diff --git a/.git-blame-ignore-revs b/.git-blame-ignore-revs index 0f0723cfa..2fea1425b 100644 --- a/.git-blame-ignore-revs +++ b/.git-blame-ignore-revs @@ -18,3 +18,7 @@ bd8ac3ebd916eafbce1040cea39ff9d966bb3493 bb94c967934cf3c355a143435bc2b292a917275f 05ab77d55f9b0c073708b17a0e3c34580b92ac78 295041e71071b2fa66bbc3c8517a1697c9d6c00b + +# P2-06 纯搬迁与 include 改写(2026-09-28) +51c37dea8be09e73b823f0799cef301352fbf2ff +f095f76256a47f86a403a66fde160e8d96782039 diff --git a/CLAUDE.md b/CLAUDE.md index 6db9fa94f..699f4cacd 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -291,6 +291,8 @@ CLI `--worktree [name]` / `-w`(TUI):启动即建 worktree,name 可为 PR 引用( ## Skills, Memory, And Project Instructions +P2-06 module boundaries: generic YAML frontmatter lives in `src/utils/frontmatter.*`; TUI skill command registration lives in `src/tui/commands/skill_commands.*`; reusable command expansion lives under `src/skills/`. Provider-dependent hook payload builders live in `src/agent/hook_bridge/hook_events.*`, while `src/hooks/hook_payload.*` owns the generic startup payload and `src/hooks/hook_seeds.*` owns read-only hook seed definitions. + [src/skills/](src/skills) discovers `SKILL.md` files from configured global, project, and external skill directories. Skill metadata is read from YAML frontmatter at startup; full bodies are loaded lazily through skill invocation or the `skill_view` tool. Proactive skill discovery (`inject-skill-index-into-context`): a compact skill index (name + description + optional `whenToUse` frontmatter, grouped by category) is injected each request into the session-context system-reminder built by `build_session_context_prompt` — the same per-request, never-persisted block that carries project instructions and the memory index. Budget is 1% of the context window in chars (4 chars/token, 8000-char fallback), 250 chars per entry (UTF-8-safe truncation); over budget degrades to names-only, then tail-cut with a `(+N more — call skills_list)` marker. The static `# Skills` prompt section points at this index ("err on the side of loading"); `skills_list` remains the fallback enumerator only. Without this index the model has zero visibility into installed skills and never invokes them proactively. @@ -564,7 +566,7 @@ SidePanel 折叠 UI:`ChatView` 把 `SidePanel` 包到 `
` → `{builtins:[{name,description}][, skills:[{name,description}]]}` | InputBar 斜杠下拉的命令清单。builtins **硬编码白名单 = init + compact**(描述与 TUI `register_builtin_commands` / `register_init_command` 对齐)。**`workspace` 参数(由 `expand-webui-skill-commands` 引入)**:缺省 → 不返回 `skills` 字段(向后兼容旧客户端);提供 → handler 用 `acecode::initialize_skill_registry(tmp, *cfg, workspace_cwd)` 临时构造一个 SkillRegistry 扫该 workspace 的项目链(`.agent/skills`、`.acecode/skills` + 全局 + external_dirs),与 daemon 全局 SkillRegistry 合并(workspace local 优先,first-wins by name),按字典序输出 skills 字段。`/init` `/compact` 在 web 端选中后只插入输入框 + chip 高亮,daemon 端**不**做特殊执行(原 add-webui-slash-commands 决策);`/ args` 由下面新加的 expander 真展开。实现:`src/web/handlers/commands_handler.{hpp,cpp}`(纯函数 `build_commands_payload` + gtest case)| -| `POST /api/sessions/:id/messages`(行为扩展) | `expand-webui-skill-commands` 引入:在 `send_input` 之前调 `try_expand_skill_command(text, registry)`(`src/web/handlers/skill_command_expander.{hpp,cpp}`)。命中已知 skill 名(按 session 的 workspace cwd 临时 scan)→ text 被替换为 `build_skill_invocation_hint(meta, args)` 的**轻量提示**:`[SYSTEM: User invoked / skill] + Description + Use skill_view(name=...) to load full SKILL.md + User's request: `。**不**注入 SKILL.md body / supporting_files,LLM 第一次看到提示后主动 invoke `skill_view` tool 把 SKILL.md 拉一次进 context,后续重复同名 `/skill` 调用不再注入(避免 context 膨胀)。TUI `src/skills/skill_commands.cpp::cmd.execute` 也走同一个 `build_skill_invocation_hint`,跨端行为统一。Builtin (`/init`/`/compact`) 不在 SkillRegistry 中 → 透传走普通 user message;未知命令 (`/foobar`) 同样透传。**不**新增执行端点,**不**改 AgentLoop API | +| `POST /api/sessions/:id/messages`(行为扩展) | `expand-webui-skill-commands` 引入:在 `send_input` 之前调 `try_expand_skill_command(text, registry)`(`src/skills/skill_command_expander.{hpp,cpp}`)。命中已知 skill 名(按 session 的 workspace cwd 临时 scan)→ text 被替换为 `build_skill_invocation_hint(meta, args)` 的**轻量提示**:`[SYSTEM: User invoked / skill] + Description + Use skill_view(name=...) to load full SKILL.md + User's request: `。**不**注入 SKILL.md body / supporting_files,LLM 第一次看到提示后主动 invoke `skill_view` tool 把 SKILL.md 拉一次进 context,后续重复同名 `/skill` 调用不再注入(避免 context 膨胀)。TUI `src/tui/commands/skill_commands.cpp::cmd.execute` 也走同一个 `build_skill_invocation_hint`,跨端行为统一。Builtin (`/init`/`/compact`) 不在 SkillRegistry 中 → 透传走普通 user message;未知命令 (`/foobar`) 同样透传。**不**新增执行端点,**不**改 AgentLoop API | | `POST /api/open-in-explorer` body `{path}` → `{ok:true}` / 400 `{ok:false,error}` / 501 | webapp 兼容模式(Edge --app,无 webview bridge)右键菜单「在资源管理器中打开」的 REST 通路。门控 = `WebServerDeps.open_in_explorer` 回调是否注入(worker.cpp 仅在 `native_folder_picker_enabled` 时填,复用 `desktop::open_path_in_file_manager`:接受任意现存本地绝对普通文件或目录,不做 workspace/managed-root 边界判断)。Windows 文件用 Explorer `/select` 选中,目录保持直接打开。前端 `DesktopContextMenu::openTargetInExplorer` bridge 优先、REST 兜底 | | `GET /api/fs/roots` → `{host, os, home, roots[{path,label,drive_type,total_bytes?,free_bytes?}], quick[{kind,path}], workspaces[{hash,name,path}]}` / `GET /api/fs/list?path=&show_hidden=1` → `{path, parent, truncated, entries[{name,path,kind,hidden,size?,modified_ms?,link_target?}]}` | Web「选择文件 / 文件夹」弹窗(`add-web-path-picker`)的服务端目录浏览器:**已鉴权即可只读浏览 daemon 全盘**,不走 `/api/files` 的 workspace 白名单,噪音目录不过滤,junction 照常列出并附 `link_target`;路径只做词法归一**不 canonical**(经 `C:\Users\shao` junction 进入返回的仍是 `C:/...`,与原生对话框一致,否则同一目录会注册出两个 workspace hash);5000 条上限置 `truncated`;`ec.message()` 在中文 Windows 是 GBK,入 JSON 前必须 `ensure_utf8`,否则 dump 抛异常整个请求变裸 500。触发策略:有 Desktop bridge 走原生对话框,没有(普通浏览器 / Edge --app 兼容模式 / 远程 Web)一律 web 弹窗,不再调用 daemon 的原生 REST 对话框(那会在 daemon 所在机器桌面弹框并把请求挂死)。实现:`src/web/handlers/fs_browser_handler.{hpp,cpp}` 纯函数 + `routes_fs.cpp`;前端 `lib/pathPicker.js`(纯逻辑)+ `lib/pathPickerHost.js`(Promise 形态的单实例请求)+ `components/PathPickerModal.jsx` / `PathPickerHost.jsx`(App 顶层挂载),三处接入点 `lib/workspacePicker.js` / `lib/environmentSettings.js::pickEnvironmentPath` / `lib/desktopPreviewFilePicker.js::pickPreviewFile` | diff --git a/CMakeLists.txt b/CMakeLists.txt index c76f8d221..9019d7a43 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -182,7 +182,7 @@ set(ACECODE_TUI_DIRS ${CMAKE_SOURCE_DIR}/src/markdown) set(ACECODE_TUI_TESTABLE_SUBSETS commands/ resume/ path_reference/ markdown/ - drag_scroll.cpp text_input_ops.cpp skill_commands.cpp slash_command_usage.cpp) + drag_scroll.cpp text_input_ops.cpp slash_command_usage.cpp) set(ACECODE_TUI_SOURCES) foreach(_tui_dir IN LISTS ACECODE_TUI_DIRS) if(NOT IS_DIRECTORY "${_tui_dir}") diff --git a/docs/skills-implementation.md b/docs/skills-implementation.md index 3a1f4d3d6..7dd0c553b 100644 --- a/docs/skills-implementation.md +++ b/docs/skills-implementation.md @@ -48,7 +48,7 @@ ## 3. SKILL.md frontmatter -acecode 只识别 YAML frontmatter 的一个子集,解析器位于 `src/skills/frontmatter.{hpp,cpp}`。仅读取文件首 8KB 足以抓到 frontmatter。 +acecode 只识别 YAML frontmatter 的一个子集,解析器位于 `src/utils/frontmatter.{hpp,cpp}`。仅读取文件首 8KB 足以抓到 frontmatter。 | 字段 | 类型 | 用途 | |------|------|------| @@ -108,7 +108,7 @@ struct SkillMetadata { ## 5. 激活路径 A — 用户输入 `/` -相关代码:`src/skills/skill_commands.{hpp,cpp}`、`src/skills/skill_activation.{hpp,cpp}`、`main.cpp` 对 `CommandRegistry` 的集成。 +相关代码:`src/tui/commands/skill_commands.{hpp,cpp}`、`src/skills/skill_activation.{hpp,cpp}`、`main.cpp` 对 `CommandRegistry` 的集成。 ### 注册流程 @@ -232,11 +232,11 @@ struct SkillMetadata { | 文件 | 职责 | |------|------| | `src/skills/skill_metadata.hpp` | `SkillMetadata` 数据结构 | -| `src/skills/frontmatter.{hpp,cpp}` | 受限 YAML frontmatter 解析 | +| `src/utils/frontmatter.{hpp,cpp}` | 受限 YAML frontmatter 解析 | | `src/skills/skill_loader.{hpp,cpp}` | 单个 `SKILL.md` 目录 → `SkillMetadata`;name 归一化;平台匹配 | | `src/skills/skill_registry.{hpp,cpp}` | 多根递归扫描、去重、查询、支持文件定位 | | `src/skills/skill_activation.{hpp,cpp}` | 用户 `/` 触发时向 agent 投递的消息模板 | -| `src/skills/skill_commands.{hpp,cpp}` | 把每个 skill 注册/反注册为 slash 命令;`/skills reload` | +| `src/tui/commands/skill_commands.{hpp,cpp}` | 把每个 skill 注册/反注册为 slash 命令;`/skills reload` | | `src/tool/skills_tool.{hpp,cpp}` | `skills_list` 工具(tier-1) | | `src/tool/skill_view_tool.{hpp,cpp}` | `skill_view` 工具(tier-2/3,含路径安全校验) | | `src/prompt/system_prompt.cpp` | 系统提示词 Skills 段 | diff --git "a/docs/\347\253\236\345\223\201\350\260\203\347\240\224\346\212\245\345\221\212_\345\256\214\346\225\264\347\211\210_2026-08.md" "b/docs/\347\253\236\345\223\201\350\260\203\347\240\224\346\212\245\345\221\212_\345\256\214\346\225\264\347\211\210_2026-08.md" index 64c83fd64..69448ea13 100644 --- "a/docs/\347\253\236\345\223\201\350\260\203\347\240\224\346\212\245\345\221\212_\345\256\214\346\225\264\347\211\210_2026-08.md" +++ "b/docs/\347\253\236\345\223\201\350\260\203\347\240\224\346\212\245\345\221\212_\345\256\214\346\225\264\347\211\210_2026-08.md" @@ -56,7 +56,7 @@ | 2 | 计划模式 `/plan` + 权限模式 `/mode` | `builtin_commands.cpp:1964,1968`、`src/tool/plan_mode_tool.cpp` | | 3 | 子代理:spawn_subagent 工具 + `/tasks` 后台任务 + Web Swarm | `src/tool/spawn_subagent_tool.cpp`、`builtin_commands.cpp:2022`、`web/src/components/SwarmModeIcon.jsx` | | 4 | TodoWrite 工具 | `src/tool/todo_write_tool.cpp` | -| 5 | opencode 式 markdown 自定义命令(`$ARGUMENTS` 插值,注册为斜杠命令) | `src/commands/opencode_command.cpp`、`opencode_command_registry.cpp` | +| 5 | opencode 式 markdown 自定义命令(`$ARGUMENTS` 插值,注册为斜杠命令) | `src/skills/opencode_command.cpp`、`opencode_command_registry.cpp` | | 6 | OAuth:GitHub Copilot Device Flow + Grok Coding Plan(xAI) | `src/provider/auth/github_auth.{hpp,cpp}`、`xai_auth.{hpp,cpp}`、`web/routes/routes_models.cpp` | | 7 | 自更新 `acecode upgrade` / `update` | `src/upgrade/`(apply / manifest / package / version)、`src/tui/cli_dispatch.cpp` | | 8 | 复制助手消息(鼠标选区 OSC52 / 系统剪贴板) | `src/main.cpp` clipboard_copy_status_message 路径、`src/tui/clipboard_helpers.cpp` | diff --git a/openspec/changes/refactor20260927-restructure-src-layers/branch-inventory.md b/openspec/changes/refactor20260927-restructure-src-layers/branch-inventory.md index ea5a572f0..2be00f9c7 100644 --- a/openspec/changes/refactor20260927-restructure-src-layers/branch-inventory.md +++ b/openspec/changes/refactor20260927-restructure-src-layers/branch-inventory.md @@ -464,3 +464,9 @@ Base: `master` (`e15df9eadfaf05a170fd8b83fb26939452e2d3e9`). Read-only snapshot. P2-05 不保留旧路径转发头。合并本阶段主线前,先在各自的迁移分支上运行 `normalize_includes.py --scope src` 与 `--scope tests`,再按既有方案演练 rebase / patch。不要改写或删除原 ref。 本阶段除映射表中的文件搬迁外,还拆出了 `config/vocab/permission_mode.hpp`、`provider/models_dev_catalog_cache`、`provider/model_probe_cache`、`tool/{safe_text_write,text_file_errors}`、`tool/web_search/region_cache`、`desktop/workspace_state` 与 `tui/slash_command_usage`。涉及 `state_file`、编码报错或 models.dev 全局缓存的调用方需显式包含对应模块的新头,不能仅依靠旧的间接 include。原状态字段和模型工具名映射保持。 + +## P2-06 迁移提示(2026-09-28) + +本任务把 frontmatter 移到 utils,把可复用的 opencode / skill 命令展开器归到 skills,把技能斜杠命令注册归到 tui/commands,对应五个测试文件同步归位。旧路径不保留转发头。上表九个遗留 ref 继续按映射运行 `migrate_branch.py --apply-map` 或 include 规范化脚本,原 ref 的功能内容与去留仍按 P3-03 逐项处理。 + +hook 模型事件构造器与只读种子表属于 extract 行,不能用整文件路径替换代替职责拆分:模型事件调用方引用 `agent/hook_bridge/hook_events.hpp`,种子消费者引用 `hooks/hook_seeds.hpp`。原 `hooks/hook_payload.hpp` 只提供无模型依赖的启动事件,种子安装事务仍由 skills 的统一安装器协调。 diff --git a/openspec/changes/refactor20260927-restructure-src-layers/verification/P2-06-hooks-and-skills.md b/openspec/changes/refactor20260927-restructure-src-layers/verification/P2-06-hooks-and-skills.md new file mode 100644 index 000000000..2ca546f07 --- /dev/null +++ b/openspec/changes/refactor20260927-restructure-src-layers/verification/P2-06-hooks-and-skills.md @@ -0,0 +1,76 @@ +# P2-06 hooks 与 skills 拆分验证 + +任务认领由 PR #82 合入 master,基线 `d993ae5f00c2d4c475a04c05d5509ffbbe3b4d8e`,已包含 P2-03 / P2-04 / P2-05 的验收。工作分支为 `refactor20260927/P2-06`,复用已合入且无活动进程、无未提交改动的原 P2-03 工作区;构建使用新目录。 + +## 实现边界 + +- `51c37dea`:13 个文件的纯 `git mv`,逐项 R100,0 行增删。frontmatter 归 utils,opencode_command 与 skill_command_expander 归 skills,skill_commands 归 tui/commands;五个测试同步镜像归位。 +- `f095f762`:19 个源/测试文件的 20 处 include 路径改写;CMake 删除失效的根级 skill_commands.cpp 子集项,已登记的 commands/ 子集继续确保该实现参与 acecode_testable。 +- 模型注册源、模型加载完成和助手消息完成三个事件构造器及其私有辅助移到 agent/hook_bridge/hook_events。hooks/hook_payload 仅保留无 provider 依赖的启动前事件;调用方直接引用实际定义头,旧路径无转发壳。 +- DefaultHookSeed 与只读种子表移到 hooks/hook_seeds。注册表只依赖 hooks;skills 安装器继续协调完整种子事务并读取该表,既有版本/哈希/迁移规则保持。 +- 两类启动事件复用已有 platform 进程 ID 原语,保留原来的 int 转换。移除终端探测对 hooks 配置的无用 include。 +- 同步 CLAUDE.md、技能说明和对应测试注释中的路径;help-source 生成器未引用此次移动路径,无须改写生成内容。 + +## 当前检查 + +src / tests include 规范化第二次检查为 0 改动;映射 strict、分层已有阻断项、行数和所有权 strict 通过。分层违规从 P2-05 的 87 项降为 83 项,本任务相关 hooks→provider、hooks→skills 和 environment→hooks 反向依赖消除。其余项目仍由后续 P2-07 / P2-08 处理。 + +前端首次测试因本工作区未安装 node_modules,缺少 @babel/core 而未能启动完整套件;已执行 pnpm install --frozen-lockfile,不改锁文件。依赖齐备后的完整 `pnpm test` 与 `pnpm build` 均通过。 + +对照机械提交前后的函数文本,三个模型事件构造器除了等价的进程 ID 原语调用外保持原样;hook 种子表函数完全相同。 + +## 本机原生验证 + +固定源码 `df9ef9567bb24eabb7b6294ea16f49fd991f845d`,使用 MSVC 2022 x64 / Ninja / Release,全新目录 `build/p2-06-hooks-skills`,BUILD_TESTING 与 Desktop 均开启。acecode、acecode-desktop、acecode_unit_tests 及五个 EXCLUDE_FROM_ALL 冒烟目标全部构建成功。 + +CMake File API 为 59 个目标、3637 个元组;对照已验收 P2-05 的 59 / 3623,目标和原有源文件归属、编译参数全部相同,无删除元组。14 个新增元组仅对应 hook_events 与 hook_seeds 的源、头及生成对象。 + +- 定向用例 351 / 351 通过,0 SKIP,包括三个 SkillCommandsReload、11 个 TerminalResolver 及 hooks / skills / seeder 用例。 +- 独立 HOME/TEMP 下完整用例清单 5117,实际运行 5116,9 SKIP,0 失败,退出码 0。 +- 对照 P2-05 完整本机记录,用例名和 SKIP 集合完全一致,未通过过滤隐藏失败。 + +`migrate_branch.py --check` 已执行,总体仍为非零:documentation 79、layers 2331(含尚未进行的最终分组检查)、migration_paths 60;include_normalization、map、ownership、seed 均为 0。此报告不代表 P3 已完成;过渡布局的实际分层违规仍是上文的 83 项。九个遗留分支的迁移通知已记录在 branch-inventory.md。 + +## 四平台验证 + +[refactor-matrix 36341836305](https://github.com/tmoonlight/acecode/actions/runs/36341836305) 固定上述源码提交,Windows / Linux / macOS / Deepin 全部完成构建与采集。PR #84 合入的窗口预告随后已合并到本分支,仅改 AGENTS.md / CLAUDE.md,src、tests、CMake、scripts、assets、web 相对验证源码无差异。 + +| 平台 | targets / tuples | 用例清单 | 实际执行 / SKIP | 失败 | +|---|---:|---:|---:|---:| +| Windows x64 | 59 / 3637 | 5117 | 5110 / 10 | 4(既有基线) | +| Linux x64 | 50 / 3551 | 5037 | 5028 / 16 | 0 | +| macOS arm64 | 57 / 3696 | 5042 | 5035 / 14 | 10(既有基线) | +| Deepin x64 | 13 / 1400 | 不运行单测 | 不适用 | 构建成功 | + +CI 使用 CTest 逐用例运行,实际执行统计包含脚本测试,与本机直接运行 gtest 的计数口径不同。四个平台全部保留既有 target 和源文件归属、编译参数;对照 P2-05,三个完整平台仅增加本轮两个拆出实现的 14 个元组,Deepin 增加 6 个。三个测试平台的用例与 SKIP 集合相对 P2-05 完全相同;相对 G0 仅有 P2-05 已登记的两个 TextFileToolErrors 新增用例。 + +Windows 与 macOS 的失败名称均在 G0 中,断言或异常特征也逐项对照此前 P2-05 日志一致。保留实际失败,未放宽断言、增加过滤或重跑取绿。Windows 既有失败为: + + +- `HookRunner.ShellCommandReceivesExactEnvironmentOverride` +- `McpManagerAsync.ProjectOverridesHaveIsolatedToolDiscoveryAndDispatch` +- `dev_desktop_script_unit` +- `verify_package_python_unit` + +macOS 既有失败为: + +- `BuiltinToolRegistry.NativeBrowserToolsCanBeUnregisteredAsOneGroup` +- `BuiltinToolRegistry.RegistersSharedCoreAndPlatformBrowserTools` +- `ManagedRemoteWebProxy.AutomaticPortFallsBackWhenAdjacentPortIsBusy` +- `ManagedRemoteWebProxy.StartsRealChildAndStopsWithoutOrphaningIt` +- `RemoteWebTcpProxy.TransparentlyForwardsPersistentBidirectionalBytes` +- `SandboxPolicy.ResolveAccessPrefersDeepestEntryThenDenyWriteRead` +- `SettingsCenterRender.NarrowTopRailFollowsDeepLinkedTab` +- `StateFileTest.ClaimFlagIsGrantedExactlyOnceAcrossProcesses` +- `WebServerHttp.ModelRoutesRejectUnauthenticatedRemoteAccess` +- `WebServerHttp.RealProxyRequiresTokenAndForwardsAuthenticatedHealth` + +P2-04 曾记录的 `AgentLoopTurnSteering.InterruptStartsStructuredTurnBeforeOrdinaryQueue` 和已知 macOS SSE 时序失败,本轮均未出现。 + +三个完整平台的 provenance 源码 SHA 与固定提交一致。Deepin 的 source_revision 仍因容器 Git 归属检查为空;额外核对构建 job `108683316416` 的 checkout 命令、HEAD 提示及紧接 `git log -1 --format=%H` 的完整 SHA,均为 `df9ef9567bb24eabb7b6294ea16f49fd991f845d`,未将空 provenance 当作源码证明。 + +## 不变量与合入条件 + +本轮完整测试实际执行并通过 `AgentLoopTermination.RequestPrefixIsByteStableAcrossIterationsInATurn`、`SystemPromptTest.NonGptModelStateIsByteIdenticalToLegacyPrompt`、`HookAgentLoop.DispatchesAssistantCompletedAfterTextMessageCommit`、`HookAgentLoop.SessionStartAdditionalContextReachesNextRequestOnly`、`HookAgentLoop.PermissionResolvedPairsWithHookApprovalExactlyOnce` 与三个 `SkillCommandsReloadTest`。CMake 逐元组对照同时覆盖 Desktop 链接边界与按源编译属性。 + +P2-06 的实现、构建归属、测试清单、静态检查和已知失败复核已完成。任务复选框保持未勾选,PR #83 实际合入后立即登记验收并认领下一项。 diff --git a/src/agent/hook_bridge/hook_events.cpp b/src/agent/hook_bridge/hook_events.cpp new file mode 100644 index 000000000..da87c9e81 --- /dev/null +++ b/src/agent/hook_bridge/hook_events.cpp @@ -0,0 +1,136 @@ +#include "hook_events.hpp" + +#include "hooks/hook_config.hpp" +#include "platform/process/os_process.hpp" +#include "utils/uuid.hpp" + +#include + +namespace acecode { +namespace { + +std::string registry_source_kind_to_string(RegistrySource::Kind kind) { + switch (kind) { + case RegistrySource::Kind::Empty: return "empty"; + case RegistrySource::Kind::Bundled: return "bundled"; + case RegistrySource::Kind::UserOverride: return "user_override"; + case RegistrySource::Kind::Network: return "network"; + } + return "unknown"; +} + +nlohmann::json model_profile_to_json(const ModelProfile& profile) { + nlohmann::json j = { + {"name", profile.name}, + {"provider", profile.provider}, + {"model", profile.model}, + }; + if (!profile.base_url.empty()) j["base_url"] = profile.base_url; + if (profile.models_dev_provider_id.has_value()) { + j["models_dev_provider_id"] = *profile.models_dev_provider_id; + } + if (profile.stream_timeout_ms.has_value()) { + j["stream_timeout_ms"] = *profile.stream_timeout_ms; + } + return j; +} + +nlohmann::json assistant_tool_calls_to_json(const nlohmann::json& tool_calls) { + nlohmann::json out = nlohmann::json::array(); + if (!tool_calls.is_array()) return out; + for (const auto& item : tool_calls) { + nlohmann::json one; + if (item.contains("id") && item["id"].is_string()) { + one["id"] = item["id"].get(); + } + if (item.contains("function") && item["function"].is_object()) { + const auto& fn = item["function"]; + one["name"] = fn.value("name", std::string{}); + one["arguments"] = fn.value("arguments", std::string{}); + } + if (!one.empty()) out.push_back(std::move(one)); + } + return out; +} + +} // namespace + +nlohmann::json hook_registry_source_to_json(const RegistrySource& source) { + nlohmann::json j = { + {"kind", registry_source_kind_to_string(source.kind)}, + {"path_or_url", source.path_or_url}, + }; + if (source.seed_dir.has_value()) j["seed_dir"] = *source.seed_dir; + if (source.manifest.has_value() && source.manifest->is_object()) { + const auto& m = *source.manifest; + nlohmann::json manifest = nlohmann::json::object(); + for (const char* key : {"generated_at", "source_url", "etag", "sha256"}) { + if (m.contains(key)) manifest[key] = m[key]; + } + if (!manifest.empty()) j["manifest"] = std::move(manifest); + } + return j; +} + +nlohmann::json build_startup_models_loaded_payload( + const std::string& cwd, + const ModelProfile& active_profile, + const std::shared_ptr& provider) { + nlohmann::json active = model_profile_to_json(active_profile); + if (provider) { + active["provider"] = provider->name(); + active["model"] = provider->model(); + } + + return { + {"schema_version", 1}, + {"event", kHookEventStartupModelsLoaded}, + {"timestamp", iso_timestamp()}, + {"process", { + {"pid", static_cast(daemon::current_pid())}, + {"cwd", cwd}, + }}, + {"models", { + {"registry_source", hook_registry_source_to_json(current_registry_source())}, + {"active", std::move(active)}, + }}, + }; +} + +nlohmann::json build_assistant_message_completed_payload( + const std::string& cwd, + const std::string& session_id, + const std::string& provider_name, + const std::string& model, + const ChatMessage& assistant_message) { + const bool has_tool_calls = + assistant_message.tool_calls.is_array() && !assistant_message.tool_calls.empty(); + + nlohmann::json assistant = { + {"kind", has_tool_calls ? "tool_calls" : "text"}, + {"role", assistant_message.role}, + {"content", assistant_message.content}, + {"has_tool_calls", has_tool_calls}, + {"tool_calls", assistant_tool_calls_to_json(assistant_message.tool_calls)}, + }; + if (!assistant_message.reasoning_content.empty()) { + assistant["reasoning_content"] = assistant_message.reasoning_content; + } + + return { + {"schema_version", 1}, + {"event", kHookEventAssistantMessageCompleted}, + {"timestamp", iso_timestamp()}, + {"session", { + {"id", session_id}, + {"cwd", cwd}, + }}, + {"model", { + {"provider", provider_name}, + {"model", model}, + }}, + {"assistant", std::move(assistant)}, + }; +} + +} // namespace acecode diff --git a/src/agent/hook_bridge/hook_events.hpp b/src/agent/hook_bridge/hook_events.hpp new file mode 100644 index 000000000..555493f58 --- /dev/null +++ b/src/agent/hook_bridge/hook_events.hpp @@ -0,0 +1,30 @@ +#pragma once + +// 模型和 provider 信息转成 hook 事件载荷,由 agent 编排层负责。 + +#include "config/saved_models.hpp" +#include "llm/llm_provider.hpp" +#include "provider/models_dev_registry.hpp" + +#include +#include + +#include + +namespace acecode { + +nlohmann::json hook_registry_source_to_json(const RegistrySource& source); + +nlohmann::json build_startup_models_loaded_payload( + const std::string& cwd, + const ModelProfile& active_profile, + const std::shared_ptr& provider); + +nlohmann::json build_assistant_message_completed_payload( + const std::string& cwd, + const std::string& session_id, + const std::string& provider_name, + const std::string& model, + const ChatMessage& assistant_message); + +} // namespace acecode diff --git a/src/agent_loop.cpp b/src/agent_loop.cpp index 71b749e93..35a2e4456 100644 --- a/src/agent_loop.cpp +++ b/src/agent_loop.cpp @@ -43,7 +43,7 @@ #include "web/tool_event_payload.hpp" #include "hooks/hook_config.hpp" #include "hooks/hook_manager.hpp" -#include "hooks/hook_payload.hpp" +#include "agent/hook_bridge/hook_events.hpp" #include "permissions/interaction_mode.hpp" #include "pa/pa_context_budget.hpp" #include "pa/pa_overflow_rescue.hpp" diff --git a/src/commands/builtin_commands.cpp b/src/commands/builtin_commands.cpp index aa897573d..847f492bf 100644 --- a/src/commands/builtin_commands.cpp +++ b/src/commands/builtin_commands.cpp @@ -24,7 +24,7 @@ #include "tool/mcp_manager.hpp" #include "tool/tool_executor.hpp" #include "skills/skill_registry.hpp" -#include "skills/skill_commands.hpp" +#include "tui/commands/skill_commands.hpp" #include "session/session_manager.hpp" #include "session/session_resume_restore.hpp" #include "session/session_storage.hpp" diff --git a/src/commands/command_registry.cpp b/src/commands/command_registry.cpp index 701edc39a..a40d40c14 100644 --- a/src/commands/command_registry.cpp +++ b/src/commands/command_registry.cpp @@ -1,7 +1,7 @@ #include "command_registry.hpp" #include "opencode_command_registry.hpp" #include "skills/skill_activation.hpp" -#include "skills/skill_commands.hpp" +#include "tui/commands/skill_commands.hpp" #include "skills/skill_registry.hpp" #include "utils/logger.hpp" diff --git a/src/commands/opencode_command_registry.cpp b/src/commands/opencode_command_registry.cpp index 2ba177ded..462e738df 100644 --- a/src/commands/opencode_command_registry.cpp +++ b/src/commands/opencode_command_registry.cpp @@ -1,7 +1,7 @@ #include "opencode_command_registry.hpp" #include "command_registry.hpp" -#include "opencode_command.hpp" +#include "skills/opencode_command.hpp" #include "agent_loop.hpp" #include "tui_state.hpp" #include "utils/logger.hpp" diff --git a/src/daemon/worker.cpp b/src/daemon/worker.cpp index 88d5c485c..44c8c157f 100644 --- a/src/daemon/worker.cpp +++ b/src/daemon/worker.cpp @@ -27,7 +27,7 @@ #include "provider/provider_factory.hpp" #include "hooks/hook_config.hpp" #include "hooks/hook_manager.hpp" -#include "hooks/hook_payload.hpp" +#include "agent/hook_bridge/hook_events.hpp" #include "platform/process/process_runner.hpp" #include "loop/loop_scheduler.hpp" #include "loop/loop_store.hpp" diff --git a/src/environment/terminal_resolver.cpp b/src/environment/terminal_resolver.cpp index b0135fbb0..eb4b234f6 100644 --- a/src/environment/terminal_resolver.cpp +++ b/src/environment/terminal_resolver.cpp @@ -1,6 +1,5 @@ #include "terminal_resolver.hpp" -#include "hooks/hook_config.hpp" #include "platform/process/process_runner.hpp" #include "utils/logger.hpp" diff --git a/src/headless/headless_runner.cpp b/src/headless/headless_runner.cpp index 59bac2889..9e3c2fedb 100644 --- a/src/headless/headless_runner.cpp +++ b/src/headless/headless_runner.cpp @@ -38,7 +38,7 @@ #include "utils/paths.hpp" #include "platform/power_inhibitor.hpp" #include "utils/utf8_path.hpp" -#include "web/handlers/skill_command_expander.hpp" +#include "skills/skill_command_expander.hpp" #include diff --git a/src/hooks/hook_payload.cpp b/src/hooks/hook_payload.cpp index 92c1c7fba..9f15c6de3 100644 --- a/src/hooks/hook_payload.cpp +++ b/src/hooks/hook_payload.cpp @@ -1,78 +1,12 @@ #include "hook_payload.hpp" #include "hook_config.hpp" -#include "config/config.hpp" +#include "platform/process/os_process.hpp" +#include "utils/paths.hpp" #include "utils/utf8_path.hpp" #include "utils/uuid.hpp" -#ifdef _WIN32 -#ifndef WIN32_LEAN_AND_MEAN -#define WIN32_LEAN_AND_MEAN -#endif -#ifndef NOMINMAX -#define NOMINMAX -#endif -#include -#else -#include -#endif - namespace acecode { -namespace { - -std::string registry_source_kind_to_string(RegistrySource::Kind kind) { - switch (kind) { - case RegistrySource::Kind::Empty: return "empty"; - case RegistrySource::Kind::Bundled: return "bundled"; - case RegistrySource::Kind::UserOverride: return "user_override"; - case RegistrySource::Kind::Network: return "network"; - } - return "unknown"; -} - -int current_pid() { -#ifdef _WIN32 - return static_cast(GetCurrentProcessId()); -#else - return static_cast(getpid()); -#endif -} - -nlohmann::json model_profile_to_json(const ModelProfile& profile) { - nlohmann::json j = { - {"name", profile.name}, - {"provider", profile.provider}, - {"model", profile.model}, - }; - if (!profile.base_url.empty()) j["base_url"] = profile.base_url; - if (profile.models_dev_provider_id.has_value()) { - j["models_dev_provider_id"] = *profile.models_dev_provider_id; - } - if (profile.stream_timeout_ms.has_value()) { - j["stream_timeout_ms"] = *profile.stream_timeout_ms; - } - return j; -} - -nlohmann::json assistant_tool_calls_to_json(const nlohmann::json& tool_calls) { - nlohmann::json out = nlohmann::json::array(); - if (!tool_calls.is_array()) return out; - for (const auto& item : tool_calls) { - nlohmann::json one; - if (item.contains("id") && item["id"].is_string()) { - one["id"] = item["id"].get(); - } - if (item.contains("function") && item["function"].is_object()) { - const auto& fn = item["function"]; - one["name"] = fn.value("name", std::string{}); - one["arguments"] = fn.value("arguments", std::string{}); - } - if (!one.empty()) out.push_back(std::move(one)); - } - return out; -} - -} // namespace nlohmann::json build_startup_before_model_load_payload(const std::string& cwd) { std::string config_path = path_to_utf8(path_from_utf8(get_acecode_dir()) / "config.json"); @@ -81,7 +15,7 @@ nlohmann::json build_startup_before_model_load_payload(const std::string& cwd) { {"event", kHookEventStartupBeforeModelLoad}, {"timestamp", iso_timestamp()}, {"process", { - {"pid", current_pid()}, + {"pid", static_cast(daemon::current_pid())}, {"cwd", cwd}, }}, {"config", { @@ -90,82 +24,4 @@ nlohmann::json build_startup_before_model_load_payload(const std::string& cwd) { }; } -nlohmann::json hook_registry_source_to_json(const RegistrySource& source) { - nlohmann::json j = { - {"kind", registry_source_kind_to_string(source.kind)}, - {"path_or_url", source.path_or_url}, - }; - if (source.seed_dir.has_value()) j["seed_dir"] = *source.seed_dir; - if (source.manifest.has_value() && source.manifest->is_object()) { - const auto& m = *source.manifest; - nlohmann::json manifest = nlohmann::json::object(); - for (const char* key : {"generated_at", "source_url", "etag", "sha256"}) { - if (m.contains(key)) manifest[key] = m[key]; - } - if (!manifest.empty()) j["manifest"] = std::move(manifest); - } - return j; -} - -nlohmann::json build_startup_models_loaded_payload( - const std::string& cwd, - const ModelProfile& active_profile, - const std::shared_ptr& provider) { - nlohmann::json active = model_profile_to_json(active_profile); - if (provider) { - active["provider"] = provider->name(); - active["model"] = provider->model(); - } - - return { - {"schema_version", 1}, - {"event", kHookEventStartupModelsLoaded}, - {"timestamp", iso_timestamp()}, - {"process", { - {"pid", current_pid()}, - {"cwd", cwd}, - }}, - {"models", { - {"registry_source", hook_registry_source_to_json(current_registry_source())}, - {"active", std::move(active)}, - }}, - }; -} - -nlohmann::json build_assistant_message_completed_payload( - const std::string& cwd, - const std::string& session_id, - const std::string& provider_name, - const std::string& model, - const ChatMessage& assistant_message) { - const bool has_tool_calls = - assistant_message.tool_calls.is_array() && !assistant_message.tool_calls.empty(); - - nlohmann::json assistant = { - {"kind", has_tool_calls ? "tool_calls" : "text"}, - {"role", assistant_message.role}, - {"content", assistant_message.content}, - {"has_tool_calls", has_tool_calls}, - {"tool_calls", assistant_tool_calls_to_json(assistant_message.tool_calls)}, - }; - if (!assistant_message.reasoning_content.empty()) { - assistant["reasoning_content"] = assistant_message.reasoning_content; - } - - return { - {"schema_version", 1}, - {"event", kHookEventAssistantMessageCompleted}, - {"timestamp", iso_timestamp()}, - {"session", { - {"id", session_id}, - {"cwd", cwd}, - }}, - {"model", { - {"provider", provider_name}, - {"model", model}, - }}, - {"assistant", std::move(assistant)}, - }; -} - } // namespace acecode diff --git a/src/hooks/hook_payload.hpp b/src/hooks/hook_payload.hpp index 07c3509da..ddc0ed6eb 100644 --- a/src/hooks/hook_payload.hpp +++ b/src/hooks/hook_payload.hpp @@ -1,30 +1,11 @@ #pragma once -#include "config/saved_models.hpp" -#include "llm/llm_provider.hpp" -#include "provider/models_dev_registry.hpp" - -#include #include #include namespace acecode { -nlohmann::json hook_registry_source_to_json(const RegistrySource& source); - nlohmann::json build_startup_before_model_load_payload(const std::string& cwd); -nlohmann::json build_startup_models_loaded_payload( - const std::string& cwd, - const ModelProfile& active_profile, - const std::shared_ptr& provider); - -nlohmann::json build_assistant_message_completed_payload( - const std::string& cwd, - const std::string& session_id, - const std::string& provider_name, - const std::string& model, - const ChatMessage& assistant_message); - } // namespace acecode diff --git a/src/hooks/hook_registry.cpp b/src/hooks/hook_registry.cpp index bc0cfa6ae..71364c40e 100644 --- a/src/hooks/hook_registry.cpp +++ b/src/hooks/hook_registry.cpp @@ -1,6 +1,6 @@ #include "hook_registry.hpp" -#include "skills/default_skill_seeder.hpp" +#include "hooks/hook_seeds.hpp" #include "utils/atomic_file.hpp" #include "utils/encoding.hpp" #include "utils/paths.hpp" diff --git a/src/hooks/hook_seeds.cpp b/src/hooks/hook_seeds.cpp new file mode 100644 index 000000000..0bb1fe938 --- /dev/null +++ b/src/hooks/hook_seeds.cpp @@ -0,0 +1,20 @@ +#include "hook_seeds.hpp" + +namespace acecode { + +const std::vector& default_hook_seeds() { + static const std::vector seeds = { + {"agent-reporting", + "acecode:managed-hook/agent-reporting@2026-09-17.1", + "agent-reporting", + "90403a8d660fc4086bca9c7c979c8fb8e98321dcebc63aefd1e995addc2fab1e", + { + "daeb5ce4f3ff42d1717c9997b9627bc6daf00df9ec24643203253da2aae30644", + "b731118b927bb32a5c43083f5d3279ecd4ce3d96137b351c2d105ac3548d9f2f", + "e139bd9bc1314dfe82cd7a7c018332e7a33c42eb451c8ce1db99f58d18d6ff4e", + }}, + }; + return seeds; +} + +} // namespace acecode diff --git a/src/hooks/hook_seeds.hpp b/src/hooks/hook_seeds.hpp new file mode 100644 index 000000000..f8156baee --- /dev/null +++ b/src/hooks/hook_seeds.hpp @@ -0,0 +1,20 @@ +#pragma once + +#include +#include +#include + +namespace acecode { + +// 内置 hook 的只读种子定义,供注册表识别与统一安装事务复用。 +struct DefaultHookSeed { + std::string name; + std::string source_id; + std::filesystem::path relative_path; + std::string definition_sha256; + std::vector previous_definition_sha256s; +}; + +const std::vector& default_hook_seeds(); + +} // namespace acecode diff --git a/src/main.cpp b/src/main.cpp index eacf8e65f..41b3e93c8 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -86,11 +86,12 @@ #include "session/permission_prompter.hpp" #include "skills/skill_init.hpp" #include "skills/skill_registry.hpp" -#include "skills/skill_commands.hpp" +#include "tui/commands/skill_commands.hpp" #include "skills/default_skill_seeder.hpp" #include "commands/opencode_command_registry.hpp" #include "hooks/hook_config.hpp" #include "hooks/hook_manager.hpp" +#include "agent/hook_bridge/hook_events.hpp" #include "hooks/hook_payload.hpp" #include "memory/memory_paths.hpp" #include "memory/memory_registry.hpp" diff --git a/src/memory/memory_frontmatter.cpp b/src/memory/memory_frontmatter.cpp index 73f810fee..fb151bccb 100644 --- a/src/memory/memory_frontmatter.cpp +++ b/src/memory/memory_frontmatter.cpp @@ -1,6 +1,6 @@ #include "memory_frontmatter.hpp" -#include "skills/frontmatter.hpp" +#include "utils/frontmatter.hpp" #include "utils/encoding.hpp" #include "utils/logger.hpp" #include "utils/utf8_path.hpp" diff --git a/src/skills/default_skill_seeder.cpp b/src/skills/default_skill_seeder.cpp index e48676b64..5736aab02 100644 --- a/src/skills/default_skill_seeder.cpp +++ b/src/skills/default_skill_seeder.cpp @@ -1,5 +1,7 @@ #include "default_skill_seeder.hpp" +#include "hooks/hook_seeds.hpp" + #include "utils/atomic_file.hpp" #include "utils/encoding.hpp" #include "utils/logger.hpp" @@ -1308,21 +1310,6 @@ const std::vector& default_expert_seeds() { return seeds; } -const std::vector& default_hook_seeds() { - static const std::vector seeds = { - {"agent-reporting", - "acecode:managed-hook/agent-reporting@2026-09-17.1", - "agent-reporting", - "90403a8d660fc4086bca9c7c979c8fb8e98321dcebc63aefd1e995addc2fab1e", - { - "daeb5ce4f3ff42d1717c9997b9627bc6daf00df9ec24643203253da2aae30644", - "b731118b927bb32a5c43083f5d3279ecd4ce3d96137b351c2d105ac3548d9f2f", - "e139bd9bc1314dfe82cd7a7c018332e7a33c42eb451c8ce1db99f58d18d6ff4e", - }}, - }; - return seeds; -} - std::optional find_default_skill_seed_dir( const std::string& argv0_dir) { const std::string env = getenv_utf8("ACECODE_SEED_SKILLS_DIR"); diff --git a/src/skills/default_skill_seeder.hpp b/src/skills/default_skill_seeder.hpp index ea9f51682..4ce966ed4 100644 --- a/src/skills/default_skill_seeder.hpp +++ b/src/skills/default_skill_seeder.hpp @@ -19,14 +19,6 @@ struct DefaultExpertSeed { std::filesystem::path relative_path; }; -struct DefaultHookSeed { - std::string name; - std::string source_id; - std::filesystem::path relative_path; - std::string definition_sha256; - std::vector previous_definition_sha256s; -}; - struct DefaultSkillSeedOutcome { std::string name; std::string source_id; @@ -61,7 +53,6 @@ struct DefaultSkillSeedInstallResult { const std::vector& default_skill_seeds(); const std::vector& default_expert_seeds(); -const std::vector& default_hook_seeds(); std::optional find_default_skill_seed_dir( const std::string& argv0_dir = ""); diff --git a/src/commands/opencode_command.cpp b/src/skills/opencode_command.cpp similarity index 100% rename from src/commands/opencode_command.cpp rename to src/skills/opencode_command.cpp diff --git a/src/commands/opencode_command.hpp b/src/skills/opencode_command.hpp similarity index 100% rename from src/commands/opencode_command.hpp rename to src/skills/opencode_command.hpp diff --git a/src/web/handlers/skill_command_expander.cpp b/src/skills/skill_command_expander.cpp similarity index 100% rename from src/web/handlers/skill_command_expander.cpp rename to src/skills/skill_command_expander.cpp diff --git a/src/web/handlers/skill_command_expander.hpp b/src/skills/skill_command_expander.hpp similarity index 100% rename from src/web/handlers/skill_command_expander.hpp rename to src/skills/skill_command_expander.hpp diff --git a/src/skills/skill_loader.cpp b/src/skills/skill_loader.cpp index cbd158c86..de91846f6 100644 --- a/src/skills/skill_loader.cpp +++ b/src/skills/skill_loader.cpp @@ -1,6 +1,6 @@ #include "skill_loader.hpp" -#include "frontmatter.hpp" +#include "utils/frontmatter.hpp" #include "utils/encoding.hpp" #include "utils/logger.hpp" #include "utils/utf8_path.hpp" diff --git a/src/skills/skill_registry.cpp b/src/skills/skill_registry.cpp index 7a25b3d27..295031a96 100644 --- a/src/skills/skill_registry.cpp +++ b/src/skills/skill_registry.cpp @@ -1,6 +1,6 @@ #include "skill_registry.hpp" -#include "frontmatter.hpp" +#include "utils/frontmatter.hpp" #include "skill_loader.hpp" #include "utils/encoding.hpp" #include "utils/logger.hpp" diff --git a/src/tool/spawn_subagent_tool.cpp b/src/tool/spawn_subagent_tool.cpp index cf3308e1e..86d0fdc83 100644 --- a/src/tool/spawn_subagent_tool.cpp +++ b/src/tool/spawn_subagent_tool.cpp @@ -7,7 +7,7 @@ #include "skills/skill_registry.hpp" #include "utils/encoding.hpp" #include "utils/logger.hpp" -#include "web/handlers/skill_command_expander.hpp" +#include "skills/skill_command_expander.hpp" #include "worktree/worktree_core.hpp" #include "worktree/worktree_manager.hpp" diff --git a/src/skills/skill_commands.cpp b/src/tui/commands/skill_commands.cpp similarity index 98% rename from src/skills/skill_commands.cpp rename to src/tui/commands/skill_commands.cpp index 0ae29a59e..fa98271c0 100644 --- a/src/skills/skill_commands.cpp +++ b/src/tui/commands/skill_commands.cpp @@ -1,7 +1,7 @@ #include "skill_commands.hpp" -#include "skill_activation.hpp" -#include "skill_registry.hpp" +#include "skills/skill_activation.hpp" +#include "skills/skill_registry.hpp" #include "agent_loop.hpp" #include "commands/command_registry.hpp" #include "tui_state.hpp" diff --git a/src/skills/skill_commands.hpp b/src/tui/commands/skill_commands.hpp similarity index 100% rename from src/skills/skill_commands.hpp rename to src/tui/commands/skill_commands.hpp diff --git a/src/tui/settings/management_center.cpp b/src/tui/settings/management_center.cpp index 6bb365f70..a188d5147 100644 --- a/src/tui/settings/management_center.cpp +++ b/src/tui/settings/management_center.cpp @@ -11,7 +11,7 @@ #include "config/config_mutation.hpp" #include "hooks/hook_manager.hpp" #include "hooks/hook_registry.hpp" -#include "skills/skill_commands.hpp" +#include "tui/commands/skill_commands.hpp" #include "skills/skill_init.hpp" #include "skills/skill_registry.hpp" #include "tool/mcp_manager.hpp" diff --git a/src/skills/frontmatter.cpp b/src/utils/frontmatter.cpp similarity index 100% rename from src/skills/frontmatter.cpp rename to src/utils/frontmatter.cpp diff --git a/src/skills/frontmatter.hpp b/src/utils/frontmatter.hpp similarity index 100% rename from src/skills/frontmatter.hpp rename to src/utils/frontmatter.hpp diff --git a/src/web/handlers/commands_handler.cpp b/src/web/handlers/commands_handler.cpp index 87e954fa3..ade452288 100644 --- a/src/web/handlers/commands_handler.cpp +++ b/src/web/handlers/commands_handler.cpp @@ -1,6 +1,6 @@ #include "commands_handler.hpp" -#include "commands/opencode_command.hpp" +#include "skills/opencode_command.hpp" #include "config/config.hpp" #include "skills/skill_init.hpp" #include "skills/skill_activation.hpp" diff --git a/src/web/handlers/opencode_command_expander.cpp b/src/web/handlers/opencode_command_expander.cpp index ce42012ea..a4023af2a 100644 --- a/src/web/handlers/opencode_command_expander.cpp +++ b/src/web/handlers/opencode_command_expander.cpp @@ -1,6 +1,6 @@ #include "opencode_command_expander.hpp" -#include "commands/opencode_command.hpp" +#include "skills/opencode_command.hpp" #include "config/config.hpp" namespace acecode::web { diff --git a/src/web/server_impl.hpp b/src/web/server_impl.hpp index 2ef5162b3..f005fb990 100644 --- a/src/web/server_impl.hpp +++ b/src/web/server_impl.hpp @@ -59,7 +59,7 @@ #include "web/handlers/builtin_command_handler.hpp" #include "web/handlers/commands_handler.hpp" #include "web/handlers/opencode_command_expander.hpp" -#include "web/handlers/skill_command_expander.hpp" +#include "skills/skill_command_expander.hpp" #include "web/handlers/session_list_handler.hpp" #include "web/handlers/side_chat_handler.hpp" #include "web/handlers/skills_handler.hpp" diff --git a/tests/experts/expert_registry_test.cpp b/tests/experts/expert_registry_test.cpp index 170bf107f..d217d8b40 100644 --- a/tests/experts/expert_registry_test.cpp +++ b/tests/experts/expert_registry_test.cpp @@ -8,7 +8,7 @@ #include "session/session_storage.hpp" #include "tool/tool_executor.hpp" #include "utils/utf8_path.hpp" -#include "web/handlers/skill_command_expander.hpp" +#include "skills/skill_command_expander.hpp" #include #include diff --git a/tests/hooks/hook_registry_test.cpp b/tests/hooks/hook_registry_test.cpp index 2a28c937c..95f856a92 100644 --- a/tests/hooks/hook_registry_test.cpp +++ b/tests/hooks/hook_registry_test.cpp @@ -1,7 +1,7 @@ #include #include "hooks/hook_registry.hpp" -#include "skills/default_skill_seeder.hpp" +#include "hooks/hook_seeds.hpp" #include "test_support/repo_root.hpp" #include "utils/utf8_path.hpp" diff --git a/tests/skills/default_skill_seeder_test.cpp b/tests/skills/default_skill_seeder_test.cpp index 3a26f8ff2..cb6b529d6 100644 --- a/tests/skills/default_skill_seeder_test.cpp +++ b/tests/skills/default_skill_seeder_test.cpp @@ -1,3 +1,4 @@ +#include "hooks/hook_seeds.hpp" #include "skills/default_skill_seeder.hpp" #include "skills/skill_registry.hpp" #include "test_support/repo_root.hpp" diff --git a/tests/commands/opencode_command_test.cpp b/tests/skills/opencode_command_test.cpp similarity index 99% rename from tests/commands/opencode_command_test.cpp rename to tests/skills/opencode_command_test.cpp index 30e063429..dc78739a4 100644 --- a/tests/commands/opencode_command_test.cpp +++ b/tests/skills/opencode_command_test.cpp @@ -1,6 +1,6 @@ #include -#include "commands/opencode_command.hpp" +#include "skills/opencode_command.hpp" #include "config/config.hpp" #include diff --git a/tests/web/skill_command_expander_test.cpp b/tests/skills/skill_command_expander_test.cpp similarity index 98% rename from tests/web/skill_command_expander_test.cpp rename to tests/skills/skill_command_expander_test.cpp index 77be78d9c..9585baa0b 100644 --- a/tests/web/skill_command_expander_test.cpp +++ b/tests/skills/skill_command_expander_test.cpp @@ -1,4 +1,4 @@ -// 覆盖 src/web/handlers/skill_command_expander.cpp::try_expand_skill_command。 +// 覆盖 src/skills/skill_command_expander.cpp::try_expand_skill_command。 // // 这个函数是 daemon 端 sendInput 路径上的关键 hook — 把 `/ args` // 形式的 user message rewrite 成 Codex linked Skill mention,与 TUI 行为 @@ -19,7 +19,7 @@ #include "config/config.hpp" #include "skills/skill_init.hpp" #include "skills/skill_registry.hpp" -#include "web/handlers/skill_command_expander.hpp" +#include "skills/skill_command_expander.hpp" #include #include diff --git a/tests/skill_registry_test.cpp b/tests/skills/skill_registry_test.cpp similarity index 100% rename from tests/skill_registry_test.cpp rename to tests/skills/skill_registry_test.cpp diff --git a/tests/skills/skill_commands_reload_test.cpp b/tests/tui/commands/skill_commands_reload_test.cpp similarity index 97% rename from tests/skills/skill_commands_reload_test.cpp rename to tests/tui/commands/skill_commands_reload_test.cpp index 10b431050..f128c9509 100644 --- a/tests/skills/skill_commands_reload_test.cpp +++ b/tests/tui/commands/skill_commands_reload_test.cpp @@ -1,4 +1,4 @@ -// 覆盖 src/skills/skill_commands.cpp::reload_skill_commands 在「会话进行中磁盘上 +// 覆盖 src/tui/commands/skill_commands.cpp::reload_skill_commands 在「会话进行中磁盘上 // 新增/删除 skill」时的重绑行为。 // // 背景:用户(或 agent 用 skill-creator)在 ACECode 运行期间往扫描根里写了个新 @@ -17,7 +17,7 @@ #include "commands/command_registry.hpp" #include "config/config.hpp" -#include "skills/skill_commands.hpp" +#include "tui/commands/skill_commands.hpp" #include "skills/skill_init.hpp" #include "skills/skill_registry.hpp" diff --git a/tests/skills/frontmatter_test.cpp b/tests/utils/frontmatter_test.cpp similarity index 97% rename from tests/skills/frontmatter_test.cpp rename to tests/utils/frontmatter_test.cpp index b455fedc8..4ab58c46d 100644 --- a/tests/skills/frontmatter_test.cpp +++ b/tests/utils/frontmatter_test.cpp @@ -1,4 +1,4 @@ -// 覆盖 src/skills/frontmatter.cpp 的 YAML 块标量解析。 +// 覆盖 src/utils/frontmatter.cpp 的 YAML 块标量解析。 // // 背景 bug:claude-code 生态的 skill(pdf / docx / kimi-webbridge 等)常用 // `description: >` 或 `description: |` 写多行描述。旧解析器把 ">" / "|" @@ -8,7 +8,7 @@ #include -#include "skills/frontmatter.hpp" +#include "utils/frontmatter.hpp" namespace {