feat: skill import/list — _import_skill from external path, cockpit intents
This commit is contained in:
parent
4bef8305d4
commit
8a23cb0c14
@ -92,6 +92,63 @@ def _build_skills_prompt(skills):
|
||||
return '\n'.join(lines)
|
||||
|
||||
|
||||
def _import_skill(skills_dir, source_path, role=None):
|
||||
"""Import a SKILL.md file into skills_dir/{role}/{name}/SKILL.md.
|
||||
source_path can be a file path or a directory containing SKILL.md.
|
||||
Returns (success, message, skill_name)."""
|
||||
if not skills_dir:
|
||||
return False, 'skills_dir not configured', ''
|
||||
src = os.path.abspath(source_path)
|
||||
if not os.path.exists(src):
|
||||
return False, f'路径不存在: {source_path}', ''
|
||||
# Determine skill name and content
|
||||
if os.path.isfile(src) and src.endswith('.md'):
|
||||
skill_name = os.path.splitext(os.path.basename(src))[0]
|
||||
with open(src, 'r') as f:
|
||||
content = f.read()
|
||||
elif os.path.isdir(src):
|
||||
md = os.path.join(src, 'SKILL.md')
|
||||
if not os.path.isfile(md):
|
||||
return False, f'目录中未找到 SKILL.md: {src}', ''
|
||||
skill_name = os.path.basename(src)
|
||||
with open(md, 'r') as f:
|
||||
content = f.read()
|
||||
else:
|
||||
return False, '源文件必须是 .md 文件或包含 SKILL.md 的目录', ''
|
||||
if not role:
|
||||
role = _guess_role_skill_name(skill_name)
|
||||
dest_dir = os.path.join(skills_dir, role, skill_name)
|
||||
os.makedirs(dest_dir, exist_ok=True)
|
||||
dest = os.path.join(dest_dir, 'SKILL.md')
|
||||
with open(dest, 'w') as f:
|
||||
f.write(content)
|
||||
return True, f'已导入 {role}/{skill_name}', skill_name
|
||||
|
||||
|
||||
def _guess_role_skill_name(name):
|
||||
"""Guess role from skill directory name."""
|
||||
return _guess_role(name)
|
||||
|
||||
|
||||
def _list_imported_skills(skills_dir):
|
||||
"""List all skills currently in skills_dir."""
|
||||
result = {}
|
||||
if not skills_dir or not os.path.isdir(skills_dir):
|
||||
return result
|
||||
for role_dir in os.listdir(skills_dir):
|
||||
rp = os.path.join(skills_dir, role_dir)
|
||||
if not os.path.isdir(rp):
|
||||
continue
|
||||
skills = []
|
||||
for sn in os.listdir(rp):
|
||||
sp = os.path.join(rp, sn)
|
||||
if os.path.isdir(sp) and os.path.isfile(os.path.join(sp, 'SKILL.md')):
|
||||
skills.append(sn)
|
||||
if skills:
|
||||
result[role_dir] = skills
|
||||
return result
|
||||
|
||||
|
||||
async def _load_agent_settings(sor, uid):
|
||||
"""Load user's agent settings, return defaults if not set."""
|
||||
recs = await sor.sqlExe(
|
||||
@ -315,13 +372,15 @@ INTENT_PROMPT = """你是一个开发产线意图分类器。分析用户输入
|
||||
- start_agent: 启动Agent自动执行任务
|
||||
- agent_status: 查看Agent状态和交付件
|
||||
- query: 查询当前状态
|
||||
- skill_list: 列出已导入的企业开发技能(如"查看技能""有哪些skills")
|
||||
- skill_import: 导入技能文件(如"导入技能 /path/to/skill")
|
||||
- chat: 开发相关的一般对话
|
||||
- out_of_scope: 完全无关软件开发
|
||||
|
||||
当前上下文:项目={ctx},迭代={iter}
|
||||
|
||||
返回纯JSON(不要markdown包裹):
|
||||
{"intent":"...","confidence":0.8,"project_name":"...","iteration_name":"...","title":"...","description":"...","missing_info":"..."}"""
|
||||
{"intent":"...","confidence":0.8,"project_name":"...","iteration_name":"...","title":"...","description":"...","source_path":"...","role":"...","missing_info":"..."}"""
|
||||
|
||||
|
||||
async def _classify_intent(model_info, message, ctx, history_msgs):
|
||||
@ -358,6 +417,7 @@ SCOPE_GUIDE = """我可以帮你:
|
||||
📝 提交任务 — "设计用户表结构" / "实现登录API"
|
||||
🐛 Bug管理 — "登录页报500" / "我的Bug列表"
|
||||
📊 查询 — "当前项目进度" / "有哪些迭代"
|
||||
📚 技能管理 — "导入技能 /path/to/skill" / "查看技能列表"
|
||||
请描述你的需求。"""
|
||||
|
||||
|
||||
@ -603,6 +663,35 @@ if action == 'send_message':
|
||||
for d in drecs:
|
||||
lines.append(f" · {d.title} [{d.review_status}] {d.quality_score}分")
|
||||
agent_reply = '\n'.join(lines) if len(lines) > 1 else "暂无Agent活动"
|
||||
elif intent_type == 'skill_list':
|
||||
skills_dir = ctx.get('skills_dir', '')
|
||||
if not skills_dir:
|
||||
agent_reply = "企业Skills目录未配置。请先在「组织SDLC设置」中设置 skills_dir。"
|
||||
else:
|
||||
listing = _list_imported_skills(skills_dir)
|
||||
if not listing:
|
||||
agent_reply = "暂无已导入的企业技能。\n\n技能目录结构应为:\n {skills_dir}/\n common/技能名/SKILL.md\n design/技能名/SKILL.md\n develop/技能名/SKILL.md\n test/技能名/SKILL.md\n deploy/技能名/SKILL.md\n\n导入方式:说「导入技能 /path/to/react-patterns」"
|
||||
else:
|
||||
lines = ["📚 已导入的企业技能:"]
|
||||
for role, names in sorted(listing.items()):
|
||||
lines.append(f"\n [{role}]")
|
||||
for n in sorted(names):
|
||||
lines.append(f" - {n}")
|
||||
agent_reply = '\n'.join(lines)
|
||||
elif intent_type == 'skill_import':
|
||||
skills_dir = ctx.get('skills_dir', '')
|
||||
source = intent.get('source_path', '') or message_text.split('导入技能')[-1].strip()
|
||||
if not skills_dir:
|
||||
agent_reply = "企业Skills目录未配置。请先在「组织SDLC设置」中设置 skills_dir。"
|
||||
elif not source:
|
||||
agent_reply = "请提供要导入的技能路径。例如:导入技能 /home/user/my-skill"
|
||||
else:
|
||||
role = intent.get('role', None)
|
||||
ok, msg, name = _import_skill(skills_dir, source, role)
|
||||
if ok:
|
||||
agent_reply = f"✅ {msg}"
|
||||
else:
|
||||
agent_reply = f"❌ 导入失败:{msg}"
|
||||
else:
|
||||
# chat: general conversation
|
||||
messages = await _build_context(sor, iteration_id or ctx['iteration_id'], '',
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user