diff --git a/src/api/types.ts b/src/api/types.ts index a4acc1a..fb8b0e7 100644 --- a/src/api/types.ts +++ b/src/api/types.ts @@ -418,6 +418,10 @@ export type AiChatEvent = ({ // AE-2025-04 会话级信任:同会话已批准过同类操作(同工具+同目录)自动放行,前端显示轻量 toast。 // 不写消息/不动 pending(Started/Completed 仍独立发),仅提示性事件。 type: 'AiToolAutoApproved'; id: string; tool: string; dir: string +} | { + // 技能注入反馈闭环:/ 技能 resolve 成功(ok=true)/失败(ok=false,未找到或读失败)通知, + // 前端轻 toast(成功「已注入技能《name》」/失败「技能 name 未找到」)。仅提示,不改消息流。 + type: 'AiSkillInjected'; name: string; ok: boolean } | { // path_auth 审批链阶段3b(统一审批模型):AiApprovalRequired 携带 kind 字段区分 // 'path'(F-260619-03 Phase B 路径授权挂起,走 once/session/always/deny)vs 'risk'(普通 RiskLevel 审批, @@ -772,7 +776,7 @@ export interface SkillInfo { name: string description: string argument_hint?: string - /** skill | command | plugin */ + /** skill | project | command | plugin(project 为项目级 .claude/skills 扫描来源) */ source: string /** SKILL.md 绝对路径(后端注入时读全文) */ path: string diff --git a/src/components/AiChat.vue b/src/components/AiChat.vue index 8929266..63aa65e 100644 --- a/src/components/AiChat.vue +++ b/src/components/AiChat.vue @@ -195,6 +195,8 @@ let _unlistenToolSlow: (() => void) | null = null let _unlistenAutoApproved: (() => void) | null = null // P1-e-d: 审批失败 toast unlistener(useAiApproval 审批 IPC 未送达经事件总线 emit,同 _unlistenToolSlow 模式) let _unlistenAiToast: (() => void) | null = null +// 技能注入反馈 toast unlistener(后端 AiSkillInjected 经 ai-chat-event 直达,见 onMounted) +let _unlistenSkillInjected: (() => void) | null = null // 第二批抽离: inputText/inputEl/图片输入/技能/实体联想/handleSend 已迁移至 ChatInput.vue // chatInputRef: 父调 expose(focus/startEditFocus/clearInput/hasUnsentInput/sendExamplePrompt) @@ -477,6 +479,7 @@ onBeforeUnmount(() => { if (_unlistenToolSlow) { _unlistenToolSlow(); _unlistenToolSlow = null } // B-260616-12 if (_unlistenAutoApproved) { _unlistenAutoApproved(); _unlistenAutoApproved = null } // AE-2025-04 if (_unlistenAiToast) { _unlistenAiToast(); _unlistenAiToast = null } // P1-e-d + if (_unlistenSkillInjected) { _unlistenSkillInjected(); _unlistenSkillInjected = null } // 技能注入反馈 toast // toast timer 清理由 useToast composable 的 onUnmounted 自动管理 }) @@ -520,6 +523,16 @@ onMounted(async () => { _unlistenAiToast = await listen<{ msg: string; type: 'info' | 'error' | 'warning' | 'success' }>('ai-toast', (e) => { showToast(e.payload.msg, e.payload.type) }) + // 技能注入反馈闭环(注入成功/失败轻 toast):后端 resolve_and_inject 双写 ai-chat-event, + // 此处独立 listen 过滤 AiSkillInjected(不动 useAiEvents 分发器,toast 归组件本地副作用)。 + _unlistenSkillInjected = await listen<{ name: string; ok: boolean }>('ai-chat-event', (e) => { + const p = e.payload as { type?: string; name?: string; ok?: boolean } + if (p?.type !== 'AiSkillInjected' || !p.name) return + showToast( + p.ok ? t('aiChat.skillInjectedToast', { name: p.name }) : t('aiChat.skillNotFoundToast', { name: p.name }), + p.ok ? 'success' : 'warning', + ) + }) // F-15 阶段2: 注册上下文管理事件监听器(清空/压缩生命周期事件 → toast/刷新) await initContextEventListeners() // 恢复生成中态(刷新后/分离窗口接管): diff --git a/src/composables/ai/useAiPanel.ts b/src/composables/ai/useAiPanel.ts index 8ce6a7c..05feb74 100644 --- a/src/composables/ai/useAiPanel.ts +++ b/src/composables/ai/useAiPanel.ts @@ -150,7 +150,13 @@ async function loadProviders() { } } -/** 拉取本机技能列表(用于 `/` 联想,静默失败) */ +/** 拉取本机技能列表(用于 `/` 联想,静默失败)。 + * + * 项目级技能接线点(后端+IPC 已就绪):DevFlow 无全局「当前项目」语境(projectsStore 是 + * 多项目清单,无 active root),故暂不传 projectPath(仅 home 级扫描)。后续有当前项目 + * 上下文时改调 `aiApi.listSkills(projectPath)` 即启用 `/.claude/skills` 扫描, + * 同名覆盖 home 级(source="project")。 + */ async function loadSkills() { try { state.skills = await aiApi.listSkills() diff --git a/src/i18n/en/aiChat.ts b/src/i18n/en/aiChat.ts index e6ece6c..3a15a37 100644 --- a/src/i18n/en/aiChat.ts +++ b/src/i18n/en/aiChat.ts @@ -250,6 +250,10 @@ export default { // Auto-approval toast (same-session already approved same-kind op: same tool + same dir) autoApprovedToast: 'Auto-approved: {tool}({dir})', + // ── Skill injection feedback (success/failure light toast for /skill resolve) ── + skillInjectedToast: 'Skill "{name}" injected', + skillNotFoundToast: 'Skill {name} not found', + // ── CIStatus.vue (commit CI checks status card) ── ciTitle: 'CI Checks', ciNFailed: '{n} failed', diff --git a/src/i18n/zh-CN/aiChat.ts b/src/i18n/zh-CN/aiChat.ts index 2ea3131..a6b4a00 100644 --- a/src/i18n/zh-CN/aiChat.ts +++ b/src/i18n/zh-CN/aiChat.ts @@ -251,6 +251,10 @@ export default { // 自动放行 toast(同会话已批准过同类操作:同工具+同目录,轻量 info 非审批气泡) autoApprovedToast: '已自动放行: {tool}({dir})', + // ── 技能注入反馈闭环(/ 技能 resolve 成功/失败轻 toast) ── + skillInjectedToast: '已注入技能《{name}》', + skillNotFoundToast: '技能 {name} 未找到', + // ── CIStatus.vue(代码提交 CI 检查状态卡)── ciTitle: 'CI 检查', ciNFailed: '{n} 项失败',