feat: 接入Obsidian上下文闭环工具
This commit is contained in:
parent
fe5fab3479
commit
1eec05f885
23
README.md
23
README.md
@ -5,6 +5,7 @@
|
|||||||
## 能力边界
|
## 能力边界
|
||||||
|
|
||||||
- 读取 KT 工作区紧凑上下文:项目清单、硬性规则、skill registry、标准 skill 包索引、历史关键词、最近任务记录和详细文档入口;需要全量文档时显式开启。
|
- 读取 KT 工作区紧凑上下文:项目清单、硬性规则、skill registry、标准 skill 包索引、历史关键词、最近任务记录和详细文档入口;需要全量文档时显式开启。
|
||||||
|
- 读取 Obsidian 索引上下文:从 `docs/obsidian` 的总入口、模块索引、文档矩阵、工作流关系和 Canvas 摘要中按模块或关键词定位文档,并返回 `codeAnchors` / `ruleAnchors` 锚定到现有项目别名、源码路径、相关规则文档和后续上下文工具。
|
||||||
- 检查子项目环境:Git/SVN、包管理器、Node 版本、env 文件、Git 状态。
|
- 检查子项目环境:Git/SVN、包管理器、Node 版本、env 文件、Git 状态。
|
||||||
- 生成防偏差工作包:把开工前检查、禁止项、风险扫描、验证计划汇总成一份可执行清单。
|
- 生成防偏差工作包:把开工前检查、禁止项、风险扫描、验证计划汇总成一份可执行清单。
|
||||||
- 生成验证建议:按后端、前端、样式、页面、部署、MCP 等变更类型给出轻量验证命令。
|
- 生成验证建议:按后端、前端、样式、页面、部署、MCP 等变更类型给出轻量验证命令。
|
||||||
@ -15,11 +16,12 @@
|
|||||||
- 生成接口测试计划:接口改动后输出真实调用命令和统一返回结构断言。
|
- 生成接口测试计划:接口改动后输出真实调用命令和统一返回结构断言。
|
||||||
- 生成业务链路测试计划:固化 Admin 登录、博客 CRUD、QQBot 扫码/自动回复、更新登录 SSE、FFLogs 命令、系统日志可视化、Web/Playground 回跳。
|
- 生成业务链路测试计划:固化 Admin 登录、博客 CRUD、QQBot 扫码/自动回复、更新登录 SSE、FFLogs 命令、系统日志可视化、Web/Playground 回跳。
|
||||||
- 生成卡点固化记录:把超时、卡进程、远程命令误写、重复失败整理成“问题点 / 稳定解法 / 后续入口 / 验证证据”,避免原样重试。
|
- 生成卡点固化记录:把超时、卡进程、远程命令误写、重复失败整理成“问题点 / 稳定解法 / 后续入口 / 验证证据”,避免原样重试。
|
||||||
|
- 生成改动文档同步计划:按变更文件自动提示需要同步的 README、API、AGENTS、docs、Obsidian、skill 和 ktWorkflow 入口。
|
||||||
- 生成多仓库提交/推送计划:按仓库分组、建议提交信息、列出提交和推送前检查。
|
- 生成多仓库提交/推送计划:按仓库分组、建议提交信息、列出提交和推送前检查。
|
||||||
- 生成远程只读健康检查和数据库同步安全向导:覆盖飞牛 NAS 服务探测、GTID、备份和行数校验。
|
- 生成远程只读健康检查和数据库同步安全向导:覆盖飞牛 NAS 服务探测、GTID、备份和行数校验。
|
||||||
- 生成专项组件工作流:KtTable、BlogArgon、AdminAuth、QQBot、FF14Plugin、NapCatLogin、SystemLog、Knife4jSwagger、FnosK8s 的防踩坑清单和验证点。
|
- 生成专项组件工作流:KtTable、BlogArgon、AdminAuth、QQBot、FF14Plugin、NapCatLogin、SystemLog、Knife4jSwagger、FnosK8s 的防踩坑清单和验证点。
|
||||||
- 生成验证进程清理计划:按项目路径和端口给出 PowerShell 检查命令,不直接杀进程。
|
- 生成验证进程清理计划:按项目路径和端口给出 PowerShell 检查命令,不直接杀进程。
|
||||||
- 清理历史产物:统一治理 `.kt-workspace` 下的测试/验证产物,按目录最近修改时间只保留最近 3 轮,模板目录永久保留。
|
- 清理历史产物:统一治理 `.kt-workspace` 下的测试/验证产物,按目录最近修改时间只保留最近 3 轮,模板目录永久保留;CLI 默认 dry-run,真实清理必须显式传 `--execute`。
|
||||||
- 检查 env 策略和变更风险:区分后端真实 env 与前端客户端 `.env*`,提醒锁文件、核心表格组件、部署链路和 Vue TSX 插槽写法等高风险改动。
|
- 检查 env 策略和变更风险:区分后端真实 env 与前端客户端 `.env*`,提醒锁文件、核心表格组件、部署链路和 Vue TSX 插槽写法等高风险改动。
|
||||||
- 全局 CodeReview 只读扫描:汇总全部 KT 子仓库的 Git 状态、敏感文件跟踪、冲突标记、运行时调试输出、疑似凭据字面量、根目录生成产物和当前变更风险;默认只对变更文件做内容扫描,并放过短中文显示标签,避免历史误报污染上下文;任何文件改动后都要跑一遍。
|
- 全局 CodeReview 只读扫描:汇总全部 KT 子仓库的 Git 状态、敏感文件跟踪、冲突标记、运行时调试输出、疑似凭据字面量、根目录生成产物和当前变更风险;默认只对变更文件做内容扫描,并放过短中文显示标签,避免历史误报污染上下文;任何文件改动后都要跑一遍。
|
||||||
- 生成或写入 `TASKS.md` 最近记录:默认 `dryRun=true`,确认后再落盘。
|
- 生成或写入 `TASKS.md` 最近记录:默认 `dryRun=true`,确认后再落盘。
|
||||||
@ -32,8 +34,12 @@ cd D:/MyFiles/KT/mcp/ktWorkflow
|
|||||||
pnpm install
|
pnpm install
|
||||||
pnpm run typecheck
|
pnpm run typecheck
|
||||||
pnpm run self-test
|
pnpm run self-test
|
||||||
|
pnpm run obsidian-context -- --module ktWorkflow
|
||||||
|
pnpm run obsidian-validate
|
||||||
|
pnpm run obsidian-sync
|
||||||
pnpm run workstream-closeout -- --title "发布闭环" --verification "Jenkins SUCCESS" --problem "无新卡点" --solution "无新增稳定解法"
|
pnpm run workstream-closeout -- --title "发布闭环" --verification "Jenkins SUCCESS" --problem "无新卡点" --solution "无新增稳定解法"
|
||||||
pnpm run cleanup-history -- --dry-run
|
pnpm run cleanup-history -- --dry-run
|
||||||
|
pnpm run cleanup-history -- --execute
|
||||||
pnpm run admin-login -- --url http://127.0.0.1:5999/#/auth/login
|
pnpm run admin-login -- --url http://127.0.0.1:5999/#/auth/login
|
||||||
```
|
```
|
||||||
|
|
||||||
@ -80,6 +86,8 @@ pnpm run admin-login -- --url http://127.0.0.1:5999/#/auth/login
|
|||||||
| `kt_global_code_review` | 对 KT 全部子仓库做只读全局 CodeReview 扫描,默认仅深扫变更文件 |
|
| `kt_global_code_review` | 对 KT 全部子仓库做只读全局 CodeReview 扫描,默认仅深扫变更文件 |
|
||||||
| `kt_finish_task` | 生成任务收尾包,可选执行验证和历史清理 |
|
| `kt_finish_task` | 生成任务收尾包,可选执行验证和历史清理 |
|
||||||
| `kt_workstream_closeout` | 生成大方向完结闭环审计,检查测试证据、问题记录、稳定解法和 ktWorkflow 升级 |
|
| `kt_workstream_closeout` | 生成大方向完结闭环审计,检查测试证据、问题记录、稳定解法和 ktWorkflow 升级 |
|
||||||
|
| `kt_workflow_loop_audit` | 审计上下文锚定、测试证据、文档同步、历史清理、问题固化、ktWorkflow 升级和全局 review |
|
||||||
|
| `kt_change_doc_sync` | 根据改动路径生成 README/API/AGENTS/docs/Obsidian/skill/ktWorkflow 文档同步清单 |
|
||||||
| `kt_commit_plan` | 生成多仓库提交计划、建议 commit message 和检查项 |
|
| `kt_commit_plan` | 生成多仓库提交计划、建议 commit message 和检查项 |
|
||||||
| `kt_push_plan` | 生成多仓库推送计划和远程异常提醒 |
|
| `kt_push_plan` | 生成多仓库推送计划和远程异常提醒 |
|
||||||
| `kt_business_test_plan` | 生成固化业务链路测试计划,包含 QQBot SSE、FFLogs 和系统日志 |
|
| `kt_business_test_plan` | 生成固化业务链路测试计划,包含 QQBot SSE、FFLogs 和系统日志 |
|
||||||
@ -87,6 +95,9 @@ pnpm run admin-login -- --url http://127.0.0.1:5999/#/auth/login
|
|||||||
| `kt_remote_health_check` | 生成或执行远程只读健康检查命令 |
|
| `kt_remote_health_check` | 生成或执行远程只读健康检查命令 |
|
||||||
| `kt_db_sync_plan` | 生成数据库同步安全向导 |
|
| `kt_db_sync_plan` | 生成数据库同步安全向导 |
|
||||||
| `kt_component_workflow` | 输出专项组件/链路防踩坑工作流,包含 FnosK8s |
|
| `kt_component_workflow` | 输出专项组件/链路防踩坑工作流,包含 FnosK8s |
|
||||||
|
| `kt_obsidian_context` | 读取 Obsidian 索引上下文,支持按 `module` / `query` 返回模块页、文档矩阵、Canvas 摘要、代码锚点和相关规则文档 |
|
||||||
|
| `kt_obsidian_validate` | 只读校验 Obsidian JSON、Canvas、Base、Wiki 链接、Markdown 相对链接和旧引用 |
|
||||||
|
| `kt_obsidian_sync` | 审计 Obsidian 工作流入口、书签、核心插件、忽略目录和校验结果 |
|
||||||
| `kt_append_task_record` | 预览或写入 `TASKS.md` 最近记录 |
|
| `kt_append_task_record` | 预览或写入 `TASKS.md` 最近记录 |
|
||||||
| `kt_commit_checklist` | 生成提交前检查清单并校验 commit message |
|
| `kt_commit_checklist` | 生成提交前检查清单并校验 commit message |
|
||||||
|
|
||||||
@ -96,6 +107,10 @@ pnpm run admin-login -- --url http://127.0.0.1:5999/#/auth/login
|
|||||||
| --- | --- |
|
| --- | --- |
|
||||||
| `pnpm run admin-login` | 使用可见 Edge 打开 Admin 登录页,填写账号密码,拖动滑块,保存登录态和截图。默认账号来自初始化数据 `admin/123456`,生产或个人账号用 `KT_ADMIN_USERNAME` / `KT_ADMIN_PASSWORD` 或 CLI 参数覆盖。 |
|
| `pnpm run admin-login` | 使用可见 Edge 打开 Admin 登录页,填写账号密码,拖动滑块,保存登录态和截图。默认账号来自初始化数据 `admin/123456`,生产或个人账号用 `KT_ADMIN_USERNAME` / `KT_ADMIN_PASSWORD` 或 CLI 参数覆盖。 |
|
||||||
| `pnpm run global-review` | 对 KT 全部子仓库做只读全局 CodeReview 扫描,默认仅对变更文件做内容深扫,输出 JSON 复审报告;确认误报时优先升级 `src/tools/review.ts`,不要把误报沉积到上下文。 |
|
| `pnpm run global-review` | 对 KT 全部子仓库做只读全局 CodeReview 扫描,默认仅对变更文件做内容深扫,输出 JSON 复审报告;确认误报时优先升级 `src/tools/review.ts`,不要把误报沉积到上下文。 |
|
||||||
|
| `pnpm run obsidian-context` | 输出 Obsidian 索引上下文;可传 `--module Admin`、`--query QQBot`、`--max-documents 10`。 |
|
||||||
|
| `pnpm run obsidian-validate` | 校验 KT Obsidian vault 结构和链接;默认 warning 不让脚本失败,需要严格模式时传 `--fail-on-warnings`。 |
|
||||||
|
| `pnpm run obsidian-sync` | 审计 Obsidian 工作流入口是否连通,并联动执行 validate。 |
|
||||||
|
| `pnpm run cleanup-history` | 预览 `.kt-workspace` 测试/验证历史产物清理;真实清理用 `pnpm run cleanup-history -- --execute`,只保留最近 3 轮并保留模板目录。 |
|
||||||
| `pnpm run workstream-closeout` | 生成大方向完结闭环审计;识别测试证据、问题记录、稳定解法和 ktWorkflow 升级缺口。 |
|
| `pnpm run workstream-closeout` | 生成大方向完结闭环审计;识别测试证据、问题记录、稳定解法和 ktWorkflow 升级缺口。 |
|
||||||
|
|
||||||
## 项目别名
|
## 项目别名
|
||||||
@ -125,10 +140,13 @@ pnpm run admin-login -- --url http://127.0.0.1:5999/#/auth/login
|
|||||||
## 使用建议
|
## 使用建议
|
||||||
|
|
||||||
- 写代码前先调用 `kt_prepare_task`;只需要单项信息时再调用 `kt_read_context` 或 `kt_inspect_project`。`kt_read_context` 默认紧凑输出,只有确实需要完整 `TASKS.md` 时再传 `includeFullDocs=true`。
|
- 写代码前先调用 `kt_prepare_task`;只需要单项信息时再调用 `kt_read_context` 或 `kt_inspect_project`。`kt_read_context` 默认紧凑输出,只有确实需要完整 `TASKS.md` 时再传 `includeFullDocs=true`。
|
||||||
|
- 需要按知识图谱定位上下文时先调用 `kt_obsidian_context`,例如 `module=Admin` 或 `query=BangDream`;Obsidian 只做文档索引,返回的 `ruleAnchors` 用来读取相关规则文档,`codeAnchors` 必须继续搭配 `kt_inspect_project`、`kt_risk_scan`、`kt_suggest_verification` 锚定真实代码上下文。
|
||||||
- 多项目联动时先调用 `kt_inspect_all_projects`。
|
- 多项目联动时先调用 `kt_inspect_all_projects`。
|
||||||
- 要收尾时调用 `kt_finish_task`,默认只生成计划和 review;需要执行验证时显式传 `runValidation=true`。
|
- 要收尾时调用 `kt_finish_task`,默认只生成计划和 review;需要执行验证时显式传 `runValidation=true`。
|
||||||
- 大方向结束前调用 `kt_workstream_closeout` 或 `pnpm run workstream-closeout`;如果识别到测试流、卡点、误报、清理、部署观测或命令模板,要先升级对应 ktWorkflow 规则再报告完成。
|
- 大方向结束前调用 `kt_workstream_closeout` 或 `pnpm run workstream-closeout`;如果识别到测试流、卡点、误报、清理、部署观测或命令模板,要先升级对应 ktWorkflow 规则再报告完成。
|
||||||
- 文件改动完成并验证后调用 `kt_global_code_review`,或运行 `pnpm run global-review`;它只读扫描,不删除文件、不提交代码。`findings` 先判定真实风险或工具误报,真实风险修业务代码,误报修 `mcp/ktWorkflow/src/tools/review.ts` 并复跑。
|
- 文件改动完成并验证后调用 `kt_global_code_review`,或运行 `pnpm run global-review`;它只读扫描,不删除文件、不提交代码。`findings` 先判定真实风险或工具误报,真实风险修业务代码,误报修 `mcp/ktWorkflow/src/tools/review.ts` 并复跑。
|
||||||
|
- 代码或配置改动后调用 `kt_change_doc_sync`,把需要同步的 README/API/AGENTS/docs/Obsidian/skill/ktWorkflow 入口补齐;无需同步时把原因写进收尾证据。
|
||||||
|
- 报告非平凡任务完成前调用 `kt_workflow_loop_audit`,确认测试证据、文档同步、历史清理、问题固化、ktWorkflow 升级和全局 review 都过门。
|
||||||
- 要提交或推送时先调用 `kt_commit_plan` / `kt_push_plan`,按仓库分组确认范围。
|
- 要提交或推送时先调用 `kt_commit_plan` / `kt_push_plan`,按仓库分组确认范围。
|
||||||
- 要测真实业务链路时调用 `kt_business_test_plan`,选择 `admin-login`、`qqbot-auto-reply`、`qqbot-login-sse`、`fflogs-command`、`system-log-visualization` 等 flow。
|
- 要测真实业务链路时调用 `kt_business_test_plan`,选择 `admin-login`、`qqbot-auto-reply`、`qqbot-login-sse`、`fflogs-command`、`system-log-visualization` 等 flow。
|
||||||
- 遇到同一命令或同一远程步骤重复卡住时调用 `kt_blocker_resolution`;第二次仍失败时先写入 `TASKS.md` 或补成脚本,再继续。
|
- 遇到同一命令或同一远程步骤重复卡住时调用 `kt_blocker_resolution`;第二次仍失败时先写入 `TASKS.md` 或补成脚本,再继续。
|
||||||
@ -141,6 +159,7 @@ pnpm run admin-login -- --url http://127.0.0.1:5999/#/auth/login
|
|||||||
- Admin 页面测试前可先执行 `pnpm run admin-login -- --url <Admin登录页>` 固化登录态,输出的 `storageState` 可作为后续 Playwright 用例前置状态。
|
- Admin 页面测试前可先执行 `pnpm run admin-login -- --url <Admin登录页>` 固化登录态,输出的 `storageState` 可作为后续 Playwright 用例前置状态。
|
||||||
- 接口改动后调用 `kt_api_test_plan`,并真实请求一次接口。
|
- 接口改动后调用 `kt_api_test_plan`,并真实请求一次接口。
|
||||||
- 验证启动过本地服务后调用 `kt_cleanup_process_plan`,清掉本次进程。
|
- 验证启动过本地服务后调用 `kt_cleanup_process_plan`,清掉本次进程。
|
||||||
- 每轮测试/验证结束后调用 `kt_cleanup_history`,或运行 `pnpm run cleanup-history -- --keep=3`,让 `.kt-workspace` 历史产物只保留最近 3 轮。
|
- 每轮测试/验证结束后先调用 `kt_cleanup_history`,或运行 `pnpm run cleanup-history -- --dry-run`;如果预览里 `deleted` 非空,确认范围后执行 `pnpm run cleanup-history -- --execute`,让 `.kt-workspace` 历史产物只保留最近 3 轮并保留模板目录。
|
||||||
|
- 更新 Obsidian 图谱、模块页、文档矩阵或 `.obsidian` 配置后运行 `kt_obsidian_validate` / `pnpm run obsidian-validate`;整理工作流入口时再跑 `kt_obsidian_sync`。
|
||||||
- 改完文件后用 `kt_append_task_record` 先 `dryRun` 预览记录,再决定是否写入。
|
- 改完文件后用 `kt_append_task_record` 先 `dryRun` 预览记录,再决定是否写入。
|
||||||
- 提交前调用 `kt_commit_checklist`,确认文件范围和提交信息。
|
- 提交前调用 `kt_commit_checklist`,确认文件范围和提交信息。
|
||||||
|
|||||||
@ -8,6 +8,9 @@
|
|||||||
"admin-login": "node --import ./node_modules/tsx/dist/loader.mjs scripts/admin-login-smoke.ts",
|
"admin-login": "node --import ./node_modules/tsx/dist/loader.mjs scripts/admin-login-smoke.ts",
|
||||||
"cleanup-history": "node --import ./node_modules/tsx/dist/loader.mjs src/server.ts --cleanup-history",
|
"cleanup-history": "node --import ./node_modules/tsx/dist/loader.mjs src/server.ts --cleanup-history",
|
||||||
"global-review": "node --import ./node_modules/tsx/dist/loader.mjs src/server.ts --global-review",
|
"global-review": "node --import ./node_modules/tsx/dist/loader.mjs src/server.ts --global-review",
|
||||||
|
"obsidian-context": "node --import ./node_modules/tsx/dist/loader.mjs src/server.ts --obsidian-context",
|
||||||
|
"obsidian-sync": "node --import ./node_modules/tsx/dist/loader.mjs src/server.ts --obsidian-sync",
|
||||||
|
"obsidian-validate": "node --import ./node_modules/tsx/dist/loader.mjs src/server.ts --obsidian-validate",
|
||||||
"self-test": "node --import ./node_modules/tsx/dist/loader.mjs src/server.ts --self-test",
|
"self-test": "node --import ./node_modules/tsx/dist/loader.mjs src/server.ts --self-test",
|
||||||
"start": "node --import ./node_modules/tsx/dist/loader.mjs src/server.ts",
|
"start": "node --import ./node_modules/tsx/dist/loader.mjs src/server.ts",
|
||||||
"typecheck": "tsc --noEmit",
|
"typecheck": "tsc --noEmit",
|
||||||
|
|||||||
@ -1,4 +1,11 @@
|
|||||||
import type { GlobalCodeReviewInput, WorkstreamCloseoutInput, WorkstreamReusablePattern } from '../types.js';
|
import type {
|
||||||
|
GlobalCodeReviewInput,
|
||||||
|
ObsidianContextInput,
|
||||||
|
ObsidianSyncInput,
|
||||||
|
ObsidianValidateInput,
|
||||||
|
WorkstreamCloseoutInput,
|
||||||
|
WorkstreamReusablePattern,
|
||||||
|
} from '../types.js';
|
||||||
import { workstreamReusablePatternValues } from './constants.js';
|
import { workstreamReusablePatternValues } from './constants.js';
|
||||||
|
|
||||||
function parseContentScanMode(argv: string[]): 'all' | 'changed' {
|
function parseContentScanMode(argv: string[]): 'all' | 'changed' {
|
||||||
@ -70,3 +77,31 @@ export function parseWorkstreamCloseoutCliArgs(argv: string[]): WorkstreamCloseo
|
|||||||
verificationEvidence: readOptionList(argv, ['--verification', '--verification-evidence']),
|
verificationEvidence: readOptionList(argv, ['--verification', '--verification-evidence']),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function parseObsidianCliArgs(argv: string[]): ObsidianValidateInput {
|
||||||
|
return {
|
||||||
|
checkLayout: !argv.includes('--no-layout'),
|
||||||
|
failOnWarnings: argv.includes('--fail-on-warnings'),
|
||||||
|
includeMarkdownLinks: !argv.includes('--no-markdown-links'),
|
||||||
|
includeStaleReferences: !argv.includes('--no-stale-references'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseObsidianSyncCliArgs(argv: string[]): ObsidianSyncInput {
|
||||||
|
return {
|
||||||
|
...parseObsidianCliArgs(argv),
|
||||||
|
includeValidation: !argv.includes('--no-validation'),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseObsidianContextCliArgs(argv: string[]): ObsidianContextInput {
|
||||||
|
const maxDocuments = Number(readOption(argv, ['--max-documents', '--maxDocuments']));
|
||||||
|
|
||||||
|
return {
|
||||||
|
includeCanvas: !argv.includes('--no-canvas'),
|
||||||
|
includeContent: !argv.includes('--no-content'),
|
||||||
|
maxDocuments: Number.isFinite(maxDocuments) ? maxDocuments : undefined,
|
||||||
|
module: readOption(argv, ['--module']),
|
||||||
|
query: readOption(argv, ['--query']),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|||||||
@ -64,6 +64,8 @@ export const registeredToolNames = [
|
|||||||
'kt_global_code_review',
|
'kt_global_code_review',
|
||||||
'kt_finish_task',
|
'kt_finish_task',
|
||||||
'kt_workstream_closeout',
|
'kt_workstream_closeout',
|
||||||
|
'kt_workflow_loop_audit',
|
||||||
|
'kt_change_doc_sync',
|
||||||
'kt_commit_plan',
|
'kt_commit_plan',
|
||||||
'kt_push_plan',
|
'kt_push_plan',
|
||||||
'kt_business_test_plan',
|
'kt_business_test_plan',
|
||||||
@ -71,6 +73,9 @@ export const registeredToolNames = [
|
|||||||
'kt_remote_health_check',
|
'kt_remote_health_check',
|
||||||
'kt_db_sync_plan',
|
'kt_db_sync_plan',
|
||||||
'kt_component_workflow',
|
'kt_component_workflow',
|
||||||
|
'kt_obsidian_context',
|
||||||
|
'kt_obsidian_validate',
|
||||||
|
'kt_obsidian_sync',
|
||||||
'kt_append_task_record',
|
'kt_append_task_record',
|
||||||
'kt_commit_checklist',
|
'kt_commit_checklist',
|
||||||
] as const;
|
] as const;
|
||||||
@ -82,4 +87,7 @@ export const defaultHistoryRoots = [
|
|||||||
'.kt-workspace/playwright-mcp',
|
'.kt-workspace/playwright-mcp',
|
||||||
'.kt-workspace/db-sync',
|
'.kt-workspace/db-sync',
|
||||||
'.kt-workspace/tmp',
|
'.kt-workspace/tmp',
|
||||||
|
'.kt-workspace/logs',
|
||||||
|
'.kt-workspace/deploy',
|
||||||
|
'.kt-workspace',
|
||||||
] as const;
|
] as const;
|
||||||
|
|||||||
@ -8,11 +8,14 @@ import { createCleanupProcessPlan, cleanupHistoryArtifacts } from './tools/clean
|
|||||||
import { buildEnvPolicy, scanTaskRisk } from './tools/envRisk.js';
|
import { buildEnvPolicy, scanTaskRisk } from './tools/envRisk.js';
|
||||||
import { buildGuardrails } from './tools/guardrails.js';
|
import { buildGuardrails } from './tools/guardrails.js';
|
||||||
import { inspectAllProjects, inspectProject } from './tools/inspect.js';
|
import { inspectAllProjects, inspectProject } from './tools/inspect.js';
|
||||||
|
import { buildWorkflowLoopAudit } from './tools/loop.js';
|
||||||
|
import { readObsidianContext, syncObsidianWorkflow, validateObsidianVault } from './tools/obsidian.js';
|
||||||
import { buildGlobalCodeReview, defaultReviewProjects } from './tools/review.js';
|
import { buildGlobalCodeReview, defaultReviewProjects } from './tools/review.js';
|
||||||
import { appendTaskRecord, createCommitChecklist, prepareTask, readWorkflowContext } from './tools/task.js';
|
import { appendTaskRecord, createCommitChecklist, prepareTask, readWorkflowContext } from './tools/task.js';
|
||||||
import { buildBusinessTestPlan, createApiTestPlan, createPageTestCase } from './tools/testing.js';
|
import { buildBusinessTestPlan, createApiTestPlan, createPageTestCase } from './tools/testing.js';
|
||||||
import { buildVerificationPlan } from './tools/verification.js';
|
import { buildVerificationPlan } from './tools/verification.js';
|
||||||
import { buildWorkstreamCloseout } from './tools/closeout.js';
|
import { buildWorkstreamCloseout } from './tools/closeout.js';
|
||||||
|
import { buildChangeDocSync } from './tools/docSync.js';
|
||||||
import { buildCommitPlan, buildComponentWorkflow, buildDbSyncPlan, buildFinishTask, buildPushPlan, buildRemoteHealthCheck } from './tools/workflow.js';
|
import { buildCommitPlan, buildComponentWorkflow, buildDbSyncPlan, buildFinishTask, buildPushPlan, buildRemoteHealthCheck } from './tools/workflow.js';
|
||||||
import { workstreamReusablePatternValues } from './core/constants.js';
|
import { workstreamReusablePatternValues } from './core/constants.js';
|
||||||
export function response(data: unknown): McpTextResponse {
|
export function response(data: unknown): McpTextResponse {
|
||||||
@ -156,7 +159,7 @@ export function registerTools(server: McpServer): void {
|
|||||||
server.registerTool(
|
server.registerTool(
|
||||||
'kt_cleanup_history',
|
'kt_cleanup_history',
|
||||||
{
|
{
|
||||||
description: '清理 KT 运行态历史产物。默认 dryRun=true,只预览;执行时按 LastWriteTime 每个 .kt-workspace 历史目录仅保留最近 3 轮,.kt-workspace/test-artifacts/_templates 永久保留。',
|
description: '清理 KT 运行态历史产物。默认 dryRun=true,只预览;执行时按 LastWriteTime 每个 .kt-workspace 历史目录仅保留最近 3 轮,template/_templates 等模板目录永久保留。',
|
||||||
inputSchema: {
|
inputSchema: {
|
||||||
dryRun: z.boolean().default(true),
|
dryRun: z.boolean().default(true),
|
||||||
keep: z.number().int().min(0).max(50).default(3),
|
keep: z.number().int().min(0).max(50).default(3),
|
||||||
@ -259,6 +262,46 @@ export function registerTools(server: McpServer): void {
|
|||||||
async (input) => response(buildWorkstreamCloseout(input)),
|
async (input) => response(buildWorkstreamCloseout(input)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
server.registerTool(
|
||||||
|
'kt_workflow_loop_audit',
|
||||||
|
{
|
||||||
|
description: '审计 KT 自动化闭环:上下文锚定、测试用例、验证证据、历史清理、问题固化、ktWorkflow 升级和全局 review 是否齐备。',
|
||||||
|
inputSchema: {
|
||||||
|
changedProjects: z.array(z.string()).default([]),
|
||||||
|
cleanupExecuted: z.boolean().default(false),
|
||||||
|
cleanupPreviewDeleted: z.number().int().min(0).default(0),
|
||||||
|
docSyncEvidence: z.array(z.string()).default([]),
|
||||||
|
failureRecords: z.array(z.string()).default([]),
|
||||||
|
ktWorkflowUpdated: z.boolean().default(false),
|
||||||
|
problemRecords: z.array(z.string()).default([]),
|
||||||
|
requireCompletion: z.boolean().default(true),
|
||||||
|
reusablePattern: z.boolean().default(false),
|
||||||
|
reviewEvidence: z.array(z.string()).default([]),
|
||||||
|
stableSolutions: z.array(z.string()).default([]),
|
||||||
|
stage: z.enum(['failure', 'finish', 'start', 'test']).default('finish'),
|
||||||
|
taskTitle: z.string().optional(),
|
||||||
|
testCases: z.array(z.string()).default([]),
|
||||||
|
verificationEvidence: z.array(z.string()).default([]),
|
||||||
|
},
|
||||||
|
title: 'KT Workflow Loop Audit',
|
||||||
|
},
|
||||||
|
async (input) => response(buildWorkflowLoopAudit(input)),
|
||||||
|
);
|
||||||
|
|
||||||
|
server.registerTool(
|
||||||
|
'kt_change_doc_sync',
|
||||||
|
{
|
||||||
|
description: '根据代码改动路径生成必须检查/同步的 README、API、AGENTS、docs、Obsidian、skill 和 ktWorkflow 文档入口。',
|
||||||
|
inputSchema: {
|
||||||
|
changedFiles: z.array(z.string()).default([]),
|
||||||
|
project: z.string().default('root'),
|
||||||
|
taskType: z.enum(taskTypeValues).optional(),
|
||||||
|
},
|
||||||
|
title: 'KT Change Doc Sync',
|
||||||
|
},
|
||||||
|
async (input) => response(await buildChangeDocSync(input)),
|
||||||
|
);
|
||||||
|
|
||||||
server.registerTool(
|
server.registerTool(
|
||||||
'kt_commit_plan',
|
'kt_commit_plan',
|
||||||
{
|
{
|
||||||
@ -387,6 +430,53 @@ export function registerTools(server: McpServer): void {
|
|||||||
async (input) => response(buildComponentWorkflow(input)),
|
async (input) => response(buildComponentWorkflow(input)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
server.registerTool(
|
||||||
|
'kt_obsidian_context',
|
||||||
|
{
|
||||||
|
description: '读取 KT Obsidian 索引上下文:总入口、模块索引、文档矩阵、工作流关系,并可按 module/query 定向返回相关模块页摘要。',
|
||||||
|
inputSchema: {
|
||||||
|
includeCanvas: z.boolean().default(true),
|
||||||
|
includeContent: z.boolean().default(true),
|
||||||
|
maxDocuments: z.number().int().min(1).max(30).default(8),
|
||||||
|
module: z.string().optional(),
|
||||||
|
query: z.string().optional(),
|
||||||
|
},
|
||||||
|
title: 'KT Obsidian Context',
|
||||||
|
},
|
||||||
|
async (input) => response(readObsidianContext(input)),
|
||||||
|
);
|
||||||
|
|
||||||
|
server.registerTool(
|
||||||
|
'kt_obsidian_validate',
|
||||||
|
{
|
||||||
|
description: '只读校验 KT Obsidian vault:JSON、Canvas、Base、Wiki 链接、Markdown 相对链接和旧引用。',
|
||||||
|
inputSchema: {
|
||||||
|
checkLayout: z.boolean().default(true),
|
||||||
|
failOnWarnings: z.boolean().default(false),
|
||||||
|
includeMarkdownLinks: z.boolean().default(true),
|
||||||
|
includeStaleReferences: z.boolean().default(true),
|
||||||
|
},
|
||||||
|
title: 'KT Obsidian Validate',
|
||||||
|
},
|
||||||
|
async (input) => response(validateObsidianVault(input)),
|
||||||
|
);
|
||||||
|
|
||||||
|
server.registerTool(
|
||||||
|
'kt_obsidian_sync',
|
||||||
|
{
|
||||||
|
description: '审计 KT Obsidian 工作流入口是否连通:必备文档、书签、核心插件、忽略目录和校验结果。',
|
||||||
|
inputSchema: {
|
||||||
|
checkLayout: z.boolean().default(true),
|
||||||
|
failOnWarnings: z.boolean().default(false),
|
||||||
|
includeMarkdownLinks: z.boolean().default(true),
|
||||||
|
includeStaleReferences: z.boolean().default(true),
|
||||||
|
includeValidation: z.boolean().default(true),
|
||||||
|
},
|
||||||
|
title: 'KT Obsidian Sync',
|
||||||
|
},
|
||||||
|
async (input) => response(syncObsidianWorkflow(input)),
|
||||||
|
);
|
||||||
|
|
||||||
server.registerTool(
|
server.registerTool(
|
||||||
'kt_append_task_record',
|
'kt_append_task_record',
|
||||||
{
|
{
|
||||||
|
|||||||
@ -4,9 +4,12 @@ import path from 'node:path';
|
|||||||
import { parseGlobalReviewCliArgs } from './core/cli.js';
|
import { parseGlobalReviewCliArgs } from './core/cli.js';
|
||||||
import { registeredToolNames } from './core/constants.js';
|
import { registeredToolNames } from './core/constants.js';
|
||||||
import { buildBlockerResolution } from './tools/blocker.js';
|
import { buildBlockerResolution } from './tools/blocker.js';
|
||||||
|
import { buildChangeDocSync } from './tools/docSync.js';
|
||||||
import { resolveInsideRoot } from './core/workspace.js';
|
import { resolveInsideRoot } from './core/workspace.js';
|
||||||
import { buildGuardrails } from './tools/guardrails.js';
|
import { buildGuardrails } from './tools/guardrails.js';
|
||||||
import { cleanupHistoryArtifacts } from './tools/cleanup.js';
|
import { readObsidianContext, syncObsidianWorkflow, validateObsidianVault } from './tools/obsidian.js';
|
||||||
|
import { cleanupHistoryArtifacts, parseCliCleanupArgs } from './tools/cleanup.js';
|
||||||
|
import { buildWorkflowLoopAudit } from './tools/loop.js';
|
||||||
import { inspectProject } from './tools/inspect.js';
|
import { inspectProject } from './tools/inspect.js';
|
||||||
import { prepareTask, readWorkflowContext } from './tools/task.js';
|
import { prepareTask, readWorkflowContext } from './tools/task.js';
|
||||||
import { buildGlobalCodeReview, isBenignCredentialReviewValue } from './tools/review.js';
|
import { buildGlobalCodeReview, isBenignCredentialReviewValue } from './tools/review.js';
|
||||||
@ -71,6 +74,84 @@ export async function runSelfTest(): Promise<void> {
|
|||||||
throw new Error('workstream closeout complete self-check failed');
|
throw new Error('workstream closeout complete self-check failed');
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const obsidianContext = readObsidianContext({
|
||||||
|
maxDocuments: 6,
|
||||||
|
module: 'ktWorkflow',
|
||||||
|
});
|
||||||
|
if ((obsidianContext.summary as { returnedDocuments?: number }).returnedDocuments === 0) {
|
||||||
|
throw new Error('obsidian context self-check failed');
|
||||||
|
}
|
||||||
|
if ((obsidianContext.summary as { codeAnchors?: number }).codeAnchors === 0) {
|
||||||
|
throw new Error('obsidian code anchor self-check failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
const obsidianValidation = validateObsidianVault({
|
||||||
|
includeStaleReferences: false,
|
||||||
|
});
|
||||||
|
if ((obsidianValidation.summary as { errors?: number }).errors !== 0) {
|
||||||
|
throw new Error('obsidian validation self-check failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
const cleanupCliParser = {
|
||||||
|
defaultDryRun: parseCliCleanupArgs(['node', 'server', '--cleanup-history']).dryRun,
|
||||||
|
executeDryRun: parseCliCleanupArgs(['node', 'server', '--cleanup-history', '--execute']).dryRun,
|
||||||
|
};
|
||||||
|
if (!cleanupCliParser.defaultDryRun || cleanupCliParser.executeDryRun) {
|
||||||
|
throw new Error('cleanup CLI dry-run parser self-check failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
const cleanupPreview = cleanupHistoryArtifacts({
|
||||||
|
dryRun: true,
|
||||||
|
keep: 3,
|
||||||
|
roots: ['.kt-workspace/test-artifacts', '.kt-workspace'],
|
||||||
|
});
|
||||||
|
const cleanupRoots = cleanupPreview.roots as Array<{
|
||||||
|
preservedNames: string[];
|
||||||
|
root: string;
|
||||||
|
}>;
|
||||||
|
const testArtifactsRoot = cleanupRoots.find((item) => item.root === '.kt-workspace/test-artifacts');
|
||||||
|
const workspaceHistoryRoot = cleanupRoots.find((item) => item.root === '.kt-workspace');
|
||||||
|
if (!testArtifactsRoot?.preservedNames.includes('_templates')) {
|
||||||
|
throw new Error('cleanup template preservation self-check failed');
|
||||||
|
}
|
||||||
|
if (!workspaceHistoryRoot?.preservedNames.includes('test-artifacts')) {
|
||||||
|
throw new Error('cleanup workspace root preservation self-check failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
const docSyncBangDream = await buildChangeDocSync({
|
||||||
|
changedFiles: ['src/qqbot/plugins/bangDream/application/bangdream-application.service.ts'],
|
||||||
|
project: 'api',
|
||||||
|
});
|
||||||
|
const docSyncRequiredDocs = docSyncBangDream.requiredDocs as Array<{ path: string }>;
|
||||||
|
if (!docSyncRequiredDocs.some((item) => item.path === 'Node/kt-template-online-api/API.md')) {
|
||||||
|
throw new Error('doc sync API self-check failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
const loopAuditComplete = buildWorkflowLoopAudit({
|
||||||
|
cleanupExecuted: true,
|
||||||
|
cleanupPreviewDeleted: 3,
|
||||||
|
docSyncEvidence: ['kt_change_doc_sync requiredDocs 已处理。'],
|
||||||
|
ktWorkflowUpdated: true,
|
||||||
|
problemRecords: ['测试历史清理流程未自动执行。'],
|
||||||
|
reusablePattern: true,
|
||||||
|
reviewEvidence: ['global-review findings=0'],
|
||||||
|
stableSolutions: ['cleanup-history 默认 dry-run,显式 --execute 后复验 deleted=0。'],
|
||||||
|
taskTitle: '自动化闭环审计',
|
||||||
|
testCases: ['cleanup-history dry-run/execute 回归测试'],
|
||||||
|
verificationEvidence: ['typecheck/self-test/cleanup dry-run 通过'],
|
||||||
|
});
|
||||||
|
if ((loopAuditComplete.canReportComplete as boolean) !== true) {
|
||||||
|
throw new Error('workflow loop complete self-check failed');
|
||||||
|
}
|
||||||
|
|
||||||
|
const loopAuditBlocked = buildWorkflowLoopAudit({
|
||||||
|
cleanupPreviewDeleted: 1,
|
||||||
|
taskTitle: '缺证据闭环',
|
||||||
|
});
|
||||||
|
if ((loopAuditBlocked.canReportComplete as boolean) !== false) {
|
||||||
|
throw new Error('workflow loop blocked self-check failed');
|
||||||
|
}
|
||||||
|
|
||||||
const data = {
|
const data = {
|
||||||
context: readWorkflowContext({ taskRecordCount: 2 }),
|
context: readWorkflowContext({ taskRecordCount: 2 }),
|
||||||
guardrails: buildGuardrails({
|
guardrails: buildGuardrails({
|
||||||
@ -78,6 +159,11 @@ export async function runSelfTest(): Promise<void> {
|
|||||||
taskType: 'mcp',
|
taskType: 'mcp',
|
||||||
userRequest: '扩展 KT 工作区 MCP 可复用能力',
|
userRequest: '扩展 KT 工作区 MCP 可复用能力',
|
||||||
}),
|
}),
|
||||||
|
cleanupCliParser,
|
||||||
|
cleanupPreview,
|
||||||
|
docSyncBangDream,
|
||||||
|
loopAuditBlocked,
|
||||||
|
loopAuditComplete,
|
||||||
historyCleanup: cleanupHistoryArtifacts({
|
historyCleanup: cleanupHistoryArtifacts({
|
||||||
dryRun: true,
|
dryRun: true,
|
||||||
keep: 3,
|
keep: 3,
|
||||||
@ -104,6 +190,11 @@ export async function runSelfTest(): Promise<void> {
|
|||||||
}),
|
}),
|
||||||
closeoutComplete,
|
closeoutComplete,
|
||||||
closeoutNeedsUpgrade,
|
closeoutNeedsUpgrade,
|
||||||
|
obsidianContext,
|
||||||
|
obsidianSync: syncObsidianWorkflow({
|
||||||
|
includeStaleReferences: false,
|
||||||
|
}),
|
||||||
|
obsidianValidation,
|
||||||
blockerResolution: buildBlockerResolution({
|
blockerResolution: buildBlockerResolution({
|
||||||
attempts: 2,
|
attempts: 2,
|
||||||
evidence: 'self-test',
|
evidence: 'self-test',
|
||||||
|
|||||||
@ -2,9 +2,16 @@
|
|||||||
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
|
||||||
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
|
||||||
|
|
||||||
import { parseGlobalReviewCliArgs, parseWorkstreamCloseoutCliArgs } from './core/cli.js';
|
import {
|
||||||
|
parseGlobalReviewCliArgs,
|
||||||
|
parseObsidianContextCliArgs,
|
||||||
|
parseObsidianCliArgs,
|
||||||
|
parseObsidianSyncCliArgs,
|
||||||
|
parseWorkstreamCloseoutCliArgs,
|
||||||
|
} from './core/cli.js';
|
||||||
import { cleanupHistoryArtifacts, parseCliCleanupArgs } from './tools/cleanup.js';
|
import { cleanupHistoryArtifacts, parseCliCleanupArgs } from './tools/cleanup.js';
|
||||||
import { buildWorkstreamCloseout } from './tools/closeout.js';
|
import { buildWorkstreamCloseout } from './tools/closeout.js';
|
||||||
|
import { readObsidianContext, syncObsidianWorkflow, validateObsidianVault } from './tools/obsidian.js';
|
||||||
import { buildGlobalCodeReview } from './tools/review.js';
|
import { buildGlobalCodeReview } from './tools/review.js';
|
||||||
import { registerTools } from './registerTools.js';
|
import { registerTools } from './registerTools.js';
|
||||||
import { runSelfTest } from './selfTest.js';
|
import { runSelfTest } from './selfTest.js';
|
||||||
@ -19,6 +26,12 @@ if (process.argv.includes('--cleanup-history')) {
|
|||||||
2,
|
2,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
} else if (process.argv.includes('--obsidian-validate')) {
|
||||||
|
console.log(JSON.stringify(validateObsidianVault(parseObsidianCliArgs(process.argv)), null, 2));
|
||||||
|
} else if (process.argv.includes('--obsidian-context')) {
|
||||||
|
console.log(JSON.stringify(readObsidianContext(parseObsidianContextCliArgs(process.argv)), null, 2));
|
||||||
|
} else if (process.argv.includes('--obsidian-sync')) {
|
||||||
|
console.log(JSON.stringify(syncObsidianWorkflow(parseObsidianSyncCliArgs(process.argv)), null, 2));
|
||||||
} else if (process.argv.includes('--self-test')) {
|
} else if (process.argv.includes('--self-test')) {
|
||||||
await runSelfTest();
|
await runSelfTest();
|
||||||
} else if (process.argv.includes('--workstream-closeout')) {
|
} else if (process.argv.includes('--workstream-closeout')) {
|
||||||
|
|||||||
@ -36,6 +36,41 @@ interface HistoryEntry {
|
|||||||
type: 'directory' | 'file';
|
type: 'directory' | 'file';
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const alwaysPreservedTemplateNames = new Set(['_template', '_templates', 'template', 'templates']);
|
||||||
|
const workspaceRootPreservedNames = new Set([
|
||||||
|
'backup',
|
||||||
|
'db-sync',
|
||||||
|
'deploy',
|
||||||
|
'external',
|
||||||
|
'logs',
|
||||||
|
'playwright-mcp',
|
||||||
|
'sources',
|
||||||
|
'test-artifacts',
|
||||||
|
'test-logs',
|
||||||
|
'tmp',
|
||||||
|
'verify',
|
||||||
|
]);
|
||||||
|
|
||||||
|
function isTemplateEntryName(name: string): boolean {
|
||||||
|
return alwaysPreservedTemplateNames.has(name.toLowerCase());
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildPreservedNames(rootRelativePath: string, childNames: string[]): Set<string> {
|
||||||
|
const preservedNames = new Set<string>();
|
||||||
|
|
||||||
|
for (const name of childNames) {
|
||||||
|
if (isTemplateEntryName(name)) preservedNames.add(name);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (rootRelativePath === '.kt-workspace') {
|
||||||
|
for (const name of workspaceRootPreservedNames) {
|
||||||
|
preservedNames.add(name);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return preservedNames;
|
||||||
|
}
|
||||||
|
|
||||||
function listHistoryEntries(rootPath: string, preservedNames: Set<string>): HistoryEntry[] {
|
function listHistoryEntries(rootPath: string, preservedNames: Set<string>): HistoryEntry[] {
|
||||||
if (!existsSync(rootPath)) return [];
|
if (!existsSync(rootPath)) return [];
|
||||||
|
|
||||||
@ -91,9 +126,8 @@ export function cleanupHistoryArtifacts(input: CleanupHistoryInput = {}): Record
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
const preservedNames = new Set(
|
const childNames = readdirSync(rootPath, { withFileTypes: true }).map((item) => item.name);
|
||||||
rootRelativePath.endsWith('test-artifacts') ? ['_templates'] : [],
|
const preservedNames = buildPreservedNames(rootRelativePath, childNames);
|
||||||
);
|
|
||||||
const entries = listHistoryEntries(rootPath, preservedNames);
|
const entries = listHistoryEntries(rootPath, preservedNames);
|
||||||
const preserved = entries.slice(0, keep);
|
const preserved = entries.slice(0, keep);
|
||||||
const deleting = entries.slice(keep);
|
const deleting = entries.slice(keep);
|
||||||
@ -127,7 +161,7 @@ export function parseCliCleanupArgs(args: string[]): CleanupHistoryInput {
|
|||||||
const rootsArg = args.find((arg) => arg.startsWith('--roots='));
|
const rootsArg = args.find((arg) => arg.startsWith('--roots='));
|
||||||
|
|
||||||
return {
|
return {
|
||||||
dryRun: args.includes('--dry-run'),
|
dryRun: !(args.includes('--execute') || args.includes('--no-dry-run')),
|
||||||
keep: keepArg ? Number.parseInt(keepArg.split('=')[1], 10) : 3,
|
keep: keepArg ? Number.parseInt(keepArg.split('=')[1], 10) : 3,
|
||||||
roots: rootsArg ? rootsArg.split('=')[1].split(',').filter(Boolean) : [...defaultHistoryRoots],
|
roots: rootsArg ? rootsArg.split('=')[1].split(',').filter(Boolean) : [...defaultHistoryRoots],
|
||||||
};
|
};
|
||||||
|
|||||||
139
src/tools/docSync.ts
Normal file
139
src/tools/docSync.ts
Normal file
@ -0,0 +1,139 @@
|
|||||||
|
import { existsSync } from 'node:fs';
|
||||||
|
import path from 'node:path';
|
||||||
|
|
||||||
|
import type { ChangeDocSyncInput } from '../types.js';
|
||||||
|
import { detectRepoType, parseGitStatusFiles } from '../core/project.js';
|
||||||
|
import { tryExecFile } from '../core/exec.js';
|
||||||
|
import { resolveProject, toPosix, workspaceRoot } from '../core/workspace.js';
|
||||||
|
|
||||||
|
interface DocSyncRule {
|
||||||
|
docs: string[];
|
||||||
|
match: RegExp;
|
||||||
|
reason: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const docSyncRules: DocSyncRule[] = [
|
||||||
|
{
|
||||||
|
docs: ['Node/kt-template-online-api/README.md', 'Node/kt-template-online-api/API.md', 'docs/obsidian/modules/KT 模块 - API Backend.md'],
|
||||||
|
match: /^(src\/|Node\/kt-template-online-api\/src\/).*(controller|service|dto|entity|module|guard|interceptor|filter)|^Node\/kt-template-online-api\/src\//,
|
||||||
|
reason: 'API 接口、DTO/Entity、模块或服务能力变更,需要同步后端 README/API 和模块索引。',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
docs: ['Node/kt-template-online-api/API.md', 'docs/qqbot-nas-runtime.md', 'docs/obsidian/modules/KT 模块 - QQBot BangDream FFLogs.md'],
|
||||||
|
match: /qqbot|bangDream|fflogs|ff14Market|napcat/i,
|
||||||
|
reason: 'QQBot/BangDream/FFLogs/NapCat 链路变更,需要同步命令、运行态和 smoke 文档。',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
docs: ['Vue/kt-template-admin/README.md', 'docs/obsidian/modules/KT 模块 - Admin.md'],
|
||||||
|
match: /Vue\/kt-template-admin\/|apps\/web-antdv-next\/src\/views\/|apps\/web-antdv-next\/src\/api\//,
|
||||||
|
reason: 'Admin 页面、API 客户端或业务视图变更,需要同步 Admin README 和模块页。',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
docs: ['Vue/kt-template-admin/README.md', 'docs/obsidian/modules/KT 模块 - KtTable.md', 'skills/kt-component-specialist/SKILL.md'],
|
||||||
|
match: /ktTable|KtTable|components\/ktTable/i,
|
||||||
|
reason: 'KtTable 组件或迁移规则变更,需要同步专项模块和 skill。',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
docs: ['Vue/kt-blog-web/README.md', 'docs/obsidian/modules/KT 模块 - Blog Web.md', 'skills/kt-blog-argon-workflow/SKILL.md'],
|
||||||
|
match: /Vue\/kt-blog-web\/|blog|argon/i,
|
||||||
|
reason: 'Blog/Argon 能力变更,需要同步博客 README、模块页和专项 skill。',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
docs: ['Plugins/fnos-k8s-dashboard-fpk/README.md', 'Plugins/fnos-k8s-dashboard-fpk/AGENTS.md', 'docs/obsidian/modules/KT 模块 - Plugins.md'],
|
||||||
|
match: /fnos-k8s-dashboard|fnosK8s|fpk|Kubernetes/i,
|
||||||
|
reason: 'fnOS K8s 插件变更,需要同步插件 README/AGENTS 和 Obsidian 模块。',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
docs: ['mcp/ktWorkflow/README.md', 'AGENTS.md', 'TASKS.md', 'docs/kt-testing-workflows.md', 'docs/obsidian/KT 工作流工程化.md'],
|
||||||
|
match: /mcp\/ktWorkflow|src\/tools\/|src\/registerTools|src\/core|workflow|cleanup|review|selfTest/i,
|
||||||
|
reason: 'ktWorkflow 或自动化规则变更,需要同步 README、根规则、测试流程和当前任务记录。',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
docs: ['AGENTS.md', 'SKILLS.md', 'docs/kt-context-semantics.md', 'docs/obsidian/modules/KT 模块 - Root Governance.md'],
|
||||||
|
match: /AGENTS\.md|SKILLS\.md|skills\/.*\/SKILL\.md|docs\/kt-context-semantics\.md/,
|
||||||
|
reason: '上下文治理或 skill 规则变更,需要同步根治理文档和 Obsidian 模块。',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
docs: ['docs/obsidian/KT 文档矩阵.md', 'docs/obsidian/KT Obsidian 工程化.md', 'docs/obsidian/KT 关系图谱.canvas'],
|
||||||
|
match: /\.obsidian|docs\/obsidian|\.canvas$|\.base$/,
|
||||||
|
reason: 'Obsidian vault、Canvas、Base 或文档矩阵变更,需要同步图谱和校验。',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
docs: ['docs/kt-testing-workflows.md', 'mcp/ktWorkflow/README.md'],
|
||||||
|
match: /\.kt-workspace|test-artifacts|test-logs|verify|playwright|smoke|Jenkinsfile|k8s\/|deploy\//i,
|
||||||
|
reason: '测试、验证、发布或运行态产物流程变更,需要同步测试流程和 ktWorkflow 文档。',
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
async function inferChangedFiles(projectKey: string): Promise<string[]> {
|
||||||
|
const project = resolveProject(projectKey);
|
||||||
|
if (detectRepoType(project.path) !== 'git') return [];
|
||||||
|
|
||||||
|
const status = await tryExecFile('git', ['status', '--short'], project.path);
|
||||||
|
if (!status.ok) return [];
|
||||||
|
|
||||||
|
return parseGitStatusFiles(status.stdout).map((file) =>
|
||||||
|
project.alias && project.alias !== 'root'
|
||||||
|
? `${project.relativePath}/${file.replaceAll('\\', '/')}`
|
||||||
|
: file.replaceAll('\\', '/'),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeChangedFile(file: string, projectKey?: string): string {
|
||||||
|
const project = resolveProject(projectKey || 'root');
|
||||||
|
const normalized = file.replaceAll('\\', '/');
|
||||||
|
if (project.alias && project.alias !== 'root' && !normalized.startsWith(`${project.relativePath}/`)) {
|
||||||
|
return `${project.relativePath}/${normalized}`;
|
||||||
|
}
|
||||||
|
return normalized;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function buildChangeDocSync(input: ChangeDocSyncInput = {}): Promise<Record<string, unknown>> {
|
||||||
|
const projectKey = input.project || 'root';
|
||||||
|
const changedFiles = (input.changedFiles?.length ?? 0) > 0
|
||||||
|
? input.changedFiles!.map((file) => normalizeChangedFile(file, projectKey))
|
||||||
|
: await inferChangedFiles(projectKey);
|
||||||
|
const recommendations = docSyncRules
|
||||||
|
.map((rule) => {
|
||||||
|
const matchedFiles = changedFiles.filter((file) => rule.match.test(file));
|
||||||
|
return matchedFiles.length > 0
|
||||||
|
? {
|
||||||
|
docs: rule.docs.map((file) => ({
|
||||||
|
exists: existsSync(path.resolve(workspaceRoot, file)),
|
||||||
|
path: file,
|
||||||
|
})),
|
||||||
|
matchedFiles,
|
||||||
|
reason: rule.reason,
|
||||||
|
}
|
||||||
|
: null;
|
||||||
|
})
|
||||||
|
.filter((item): item is NonNullable<typeof item> => Boolean(item));
|
||||||
|
const requiredDocs = Array.from(
|
||||||
|
new Set(recommendations.flatMap((item) => item.docs.map((doc) => doc.path))),
|
||||||
|
).map((file) => ({
|
||||||
|
exists: existsSync(path.resolve(workspaceRoot, file)),
|
||||||
|
path: file,
|
||||||
|
}));
|
||||||
|
|
||||||
|
return {
|
||||||
|
changedFiles,
|
||||||
|
commands: [
|
||||||
|
'pnpm --dir mcp/ktWorkflow run obsidian-context -- --query <module>',
|
||||||
|
'pnpm --dir mcp/ktWorkflow run obsidian-validate',
|
||||||
|
'pnpm --dir mcp/ktWorkflow run global-review',
|
||||||
|
],
|
||||||
|
requiredDocs,
|
||||||
|
recommendations,
|
||||||
|
summary: {
|
||||||
|
changedFileCount: changedFiles.length,
|
||||||
|
recommendationCount: recommendations.length,
|
||||||
|
requiredDocCount: requiredDocs.length,
|
||||||
|
},
|
||||||
|
verification: [
|
||||||
|
'确认 requiredDocs 中需要更新的文档已同步,未更新时在最终说明原因。',
|
||||||
|
'涉及 Obsidian 索引时运行 obsidian-validate。',
|
||||||
|
'涉及 ktWorkflow/规则/测试流程时运行 self-test 和 global-review。',
|
||||||
|
`当前项目:${toPosix(resolveProject(projectKey).relativePath)}`,
|
||||||
|
],
|
||||||
|
};
|
||||||
|
}
|
||||||
150
src/tools/loop.ts
Normal file
150
src/tools/loop.ts
Normal file
@ -0,0 +1,150 @@
|
|||||||
|
import type { WorkflowLoopAuditInput } from '../types.js';
|
||||||
|
|
||||||
|
interface LoopGate {
|
||||||
|
evidence: string[];
|
||||||
|
name: string;
|
||||||
|
required: boolean;
|
||||||
|
status: 'missing' | 'not-needed' | 'ok';
|
||||||
|
suggestion?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizeList(values?: string[]): string[] {
|
||||||
|
return (values || []).map((value) => value.trim()).filter(Boolean);
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildRequiredGate(name: string, evidence: string[], suggestion: string): LoopGate {
|
||||||
|
return {
|
||||||
|
evidence,
|
||||||
|
name,
|
||||||
|
required: true,
|
||||||
|
status: evidence.length > 0 ? 'ok' : 'missing',
|
||||||
|
suggestion: evidence.length > 0 ? undefined : suggestion,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
function buildBooleanGate(name: string, ok: boolean, evidence: string[], suggestion: string): LoopGate {
|
||||||
|
return {
|
||||||
|
evidence,
|
||||||
|
name,
|
||||||
|
required: true,
|
||||||
|
status: ok ? 'ok' : 'missing',
|
||||||
|
suggestion: ok ? undefined : suggestion,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
export function buildWorkflowLoopAudit(input: WorkflowLoopAuditInput = {}): Record<string, unknown> {
|
||||||
|
const stage = input.stage || 'finish';
|
||||||
|
const requireCompletion = input.requireCompletion !== false && stage === 'finish';
|
||||||
|
const changedProjects = normalizeList(input.changedProjects);
|
||||||
|
const testCases = normalizeList(input.testCases);
|
||||||
|
const verificationEvidence = normalizeList(input.verificationEvidence);
|
||||||
|
const failureRecords = normalizeList(input.failureRecords);
|
||||||
|
const docSyncEvidence = normalizeList(input.docSyncEvidence);
|
||||||
|
const problemRecords = normalizeList(input.problemRecords);
|
||||||
|
const stableSolutions = normalizeList(input.stableSolutions);
|
||||||
|
const reviewEvidence = normalizeList(input.reviewEvidence);
|
||||||
|
const cleanupPreviewDeleted = Math.max(0, input.cleanupPreviewDeleted ?? 0);
|
||||||
|
const cleanupOk = cleanupPreviewDeleted === 0 || input.cleanupExecuted === true;
|
||||||
|
const hasFailure = failureRecords.length > 0;
|
||||||
|
const reusablePattern = input.reusablePattern === true;
|
||||||
|
const needsSolidification = hasFailure || reusablePattern;
|
||||||
|
const ktWorkflowUpdated = input.ktWorkflowUpdated === true;
|
||||||
|
|
||||||
|
const gates: LoopGate[] = [
|
||||||
|
buildRequiredGate('task target', [input.taskTitle || ''].filter(Boolean), '写清本轮任务目标。'),
|
||||||
|
buildRequiredGate('test case before test', testCases, '测试前先生成页面/业务/接口测试用例或验证计划。'),
|
||||||
|
buildRequiredGate('verification evidence', verificationEvidence, '补充真实验证证据;只有计划不算完成。'),
|
||||||
|
buildBooleanGate(
|
||||||
|
'history cleanup',
|
||||||
|
cleanupOk,
|
||||||
|
cleanupOk
|
||||||
|
? [
|
||||||
|
cleanupPreviewDeleted === 0
|
||||||
|
? 'cleanup-history dry-run deleted=0'
|
||||||
|
: 'cleanup-history executed after stale artifacts were found',
|
||||||
|
]
|
||||||
|
: [`cleanup-history dry-run deleted=${cleanupPreviewDeleted}`],
|
||||||
|
'执行 kt_cleanup_history dryRun=false 或 pnpm run cleanup-history -- --execute,并复跑 dry-run 确认 deleted=0。',
|
||||||
|
),
|
||||||
|
buildRequiredGate('documentation sync evidence', docSyncEvidence, '运行 kt_change_doc_sync 并同步 README/API/AGENTS/docs/Obsidian/skills/ktWorkflow 中需要更新的入口;无需更新时写明原因。'),
|
||||||
|
buildRequiredGate('global review evidence', reviewEvidence, '文件改动后跑 kt_global_code_review 或 pnpm run global-review。'),
|
||||||
|
];
|
||||||
|
|
||||||
|
if (needsSolidification) {
|
||||||
|
gates.push(
|
||||||
|
buildRequiredGate('problem record', problemRecords, '写入问题点,至少包含触发条件和证据。'),
|
||||||
|
buildRequiredGate('stable solution', stableSolutions, '写入稳定解法和后续入口。'),
|
||||||
|
buildBooleanGate(
|
||||||
|
'ktWorkflow upgrade',
|
||||||
|
ktWorkflowUpdated,
|
||||||
|
ktWorkflowUpdated ? ['ktWorkflow updated or audited as not needing executable upgrade'] : [],
|
||||||
|
'可复用问题必须沉淀到 AGENTS.md、skills、docs 或 mcp/ktWorkflow;能自动化的要升级 ktWorkflow。',
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
gates.push({
|
||||||
|
evidence: ['无失败或可复用问题。'],
|
||||||
|
name: 'problem solidification',
|
||||||
|
required: false,
|
||||||
|
status: 'not-needed',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
const missingItems = requireCompletion
|
||||||
|
? gates
|
||||||
|
.filter((gate) => gate.required && gate.status === 'missing')
|
||||||
|
.map((gate) => `${gate.name}: ${gate.suggestion}`)
|
||||||
|
: [];
|
||||||
|
|
||||||
|
return {
|
||||||
|
canReportComplete: missingItems.length === 0,
|
||||||
|
commands: [
|
||||||
|
'pnpm --dir mcp/ktWorkflow run obsidian-context -- --query <keyword>',
|
||||||
|
'pnpm --dir mcp/ktWorkflow run self-test',
|
||||||
|
'pnpm --dir mcp/ktWorkflow run cleanup-history -- --dry-run',
|
||||||
|
'pnpm --dir mcp/ktWorkflow run cleanup-history -- --execute',
|
||||||
|
'pnpm --dir mcp/ktWorkflow run global-review',
|
||||||
|
'pnpm --dir mcp/ktWorkflow run workstream-closeout -- --title "<目标>" --verification "<证据>" --problem "<问题记录>" --solution "<稳定解法>"',
|
||||||
|
],
|
||||||
|
gates,
|
||||||
|
loops: {
|
||||||
|
contextLoop: [
|
||||||
|
'读取 AGENTS.md / SKILLS.md / TASKS.md。',
|
||||||
|
'用 kt_obsidian_context 获取 ruleAnchors 和 codeAnchors。',
|
||||||
|
'用 kt_inspect_project / kt_risk_scan / kt_suggest_verification 锚定代码上下文。',
|
||||||
|
],
|
||||||
|
problemSolidificationLoop: [
|
||||||
|
'失败或主动修复后,先判断是否可复用。',
|
||||||
|
'可复用则记录问题点 / 稳定解法 / 后续入口 / 验证证据。',
|
||||||
|
'能自动化的升级 mcp/ktWorkflow;规则类写 AGENTS.md;长流程写 docs;当前证据写 TASKS.md。',
|
||||||
|
'复跑 self-test / 相关脚本 / global-review。',
|
||||||
|
],
|
||||||
|
documentationSyncLoop: [
|
||||||
|
'收集 changedFiles。',
|
||||||
|
'运行 kt_change_doc_sync 生成 requiredDocs。',
|
||||||
|
'更新必要的 README/API/AGENTS/SKILLS/docs/Obsidian/skill/ktWorkflow 入口。',
|
||||||
|
'无法或无需更新时写明原因,并保留在 TASKS.md 或最终报告。',
|
||||||
|
],
|
||||||
|
testLoop: [
|
||||||
|
'测试前先写用例或验证计划。',
|
||||||
|
'按用例执行验证并保存证据到 .kt-workspace。',
|
||||||
|
'失败最多三轮,每轮必须改变一个可验证变量。',
|
||||||
|
'每轮结束运行 cleanup-history dry-run;存在待删项则执行 --execute 并复验 deleted=0。',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
missingItems,
|
||||||
|
stage,
|
||||||
|
summary: {
|
||||||
|
changedProjects,
|
||||||
|
cleanupPreviewDeleted,
|
||||||
|
docSyncEvidenceCount: docSyncEvidence.length,
|
||||||
|
failureCount: failureRecords.length,
|
||||||
|
problemRecordCount: problemRecords.length,
|
||||||
|
reusablePattern,
|
||||||
|
reviewEvidenceCount: reviewEvidence.length,
|
||||||
|
stableSolutionCount: stableSolutions.length,
|
||||||
|
testCaseCount: testCases.length,
|
||||||
|
verificationEvidenceCount: verificationEvidence.length,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
1055
src/tools/obsidian.ts
Normal file
1055
src/tools/obsidian.ts
Normal file
File diff suppressed because it is too large
Load Diff
@ -155,6 +155,26 @@ export function readWorkflowContext(input: WorkflowContextInput): Record<string,
|
|||||||
tasks: includeFullDocs ? readText('TASKS.md') : undefined,
|
tasks: includeFullDocs ? readText('TASKS.md') : undefined,
|
||||||
},
|
},
|
||||||
includeFullDocs,
|
includeFullDocs,
|
||||||
|
obsidianIndex: {
|
||||||
|
note: 'Obsidian 只做文档/MOC 索引;代码上下文仍以 kt_inspect_project、kt_risk_scan、kt_suggest_verification 等工具锚定。',
|
||||||
|
tools: [
|
||||||
|
{
|
||||||
|
input: {
|
||||||
|
maxDocuments: 8,
|
||||||
|
},
|
||||||
|
name: 'kt_obsidian_context',
|
||||||
|
purpose: '按模块或关键词定位 docs/obsidian 索引,并返回对应 codeAnchors。',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
input: {
|
||||||
|
includeGit: true,
|
||||||
|
project: '<codeAnchors.alias>',
|
||||||
|
},
|
||||||
|
name: 'kt_inspect_project',
|
||||||
|
purpose: '读取被 Obsidian 索引锚定的真实仓库状态和代码入口。',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
projects,
|
projects,
|
||||||
workspaceRoot,
|
workspaceRoot,
|
||||||
};
|
};
|
||||||
|
|||||||
@ -19,7 +19,7 @@ export function createPageTestCase(input: PageTestCaseInput): PageTestCaseResult
|
|||||||
'关闭浏览器实例',
|
'关闭浏览器实例',
|
||||||
'清理本次启动的 Node/Vite 进程',
|
'清理本次启动的 Node/Vite 进程',
|
||||||
'保存截图和 result.json',
|
'保存截图和 result.json',
|
||||||
'调用 kt_cleanup_history,仅保留最近 3 轮历史产物',
|
'先调用 kt_cleanup_history dryRun=true 预览,再执行 dryRun=false 或 pnpm run cleanup-history -- --execute,仅保留最近 3 轮历史产物并保留模板目录',
|
||||||
],
|
],
|
||||||
entryUrl: input.entryUrl || '待填写',
|
entryUrl: input.entryUrl || '待填写',
|
||||||
maxRounds: 3,
|
maxRounds: 3,
|
||||||
@ -179,6 +179,7 @@ export function buildBusinessTestPlan(input: BusinessTestPlanInput = {}): Record
|
|||||||
'业务测试先写用例,再按环境 -> 账号 -> 数据 -> 接口 -> 页面预检。',
|
'业务测试先写用例,再按环境 -> 账号 -> 数据 -> 接口 -> 页面预检。',
|
||||||
'失败必须记录复现步骤、证据和初步归因。',
|
'失败必须记录复现步骤、证据和初步归因。',
|
||||||
'同一用例最多三轮,第三轮仍失败则停止等待建议。',
|
'同一用例最多三轮,第三轮仍失败则停止等待建议。',
|
||||||
|
'每轮结束后执行 kt_cleanup_history 预览;如存在待删历史产物,确认范围后执行清理,只保留最近 3 轮并保留模板目录。',
|
||||||
],
|
],
|
||||||
selectedFlows: selected.map(([name, plan]) => ({
|
selectedFlows: selected.map(([name, plan]) => ({
|
||||||
name,
|
name,
|
||||||
|
|||||||
@ -107,6 +107,8 @@ export async function buildFinishTask(input: FinishTaskInput = {}): Promise<Reco
|
|||||||
dryRun: !(input.cleanupHistory ?? false),
|
dryRun: !(input.cleanupHistory ?? false),
|
||||||
keep: input.keepHistory ?? 3,
|
keep: input.keepHistory ?? 3,
|
||||||
});
|
});
|
||||||
|
const cleanupDeleted = Array.isArray(cleanup.deleted) ? cleanup.deleted : [];
|
||||||
|
const cleanupPendingCount = cleanup.dryRun ? cleanupDeleted.length : 0;
|
||||||
|
|
||||||
return {
|
return {
|
||||||
cleanup,
|
cleanup,
|
||||||
@ -123,7 +125,7 @@ export async function buildFinishTask(input: FinishTaskInput = {}): Promise<Reco
|
|||||||
mode: input.runValidation ? 'validation_executed' : 'plan_only',
|
mode: input.runValidation ? 'validation_executed' : 'plan_only',
|
||||||
notes: [
|
notes: [
|
||||||
'默认只生成收尾结果;runValidation=true 时才执行验证命令。',
|
'默认只生成收尾结果;runValidation=true 时才执行验证命令。',
|
||||||
'cleanupHistory=true 时才真实清理历史产物,否则仅预览。',
|
'cleanupHistory=true 时才真实清理历史产物,否则仅预览;预览中 deleted>0 说明测试/验证历史还未满足最近 3 轮规则。',
|
||||||
'提交和推送仍需用户明确要求后执行。',
|
'提交和推送仍需用户明确要求后执行。',
|
||||||
karpathyFinishNote,
|
karpathyFinishNote,
|
||||||
],
|
],
|
||||||
@ -132,12 +134,14 @@ export async function buildFinishTask(input: FinishTaskInput = {}): Promise<Reco
|
|||||||
stopConditions: [
|
stopConditions: [
|
||||||
'review.findings 存在 P1/P2 时先判定真实风险或工具误报;真实风险修代码,工具误报修 ktWorkflow,再复跑 kt_global_code_review。',
|
'review.findings 存在 P1/P2 时先判定真实风险或工具误报;真实风险修代码,工具误报修 ktWorkflow,再复跑 kt_global_code_review。',
|
||||||
'validationRuns 中任一 result.ok=false 时先处理失败命令。',
|
'validationRuns 中任一 result.ok=false 时先处理失败命令。',
|
||||||
|
'cleanup.dryRun=true 且 cleanup.deleted 非空时,先执行 kt_cleanup_history dryRun=false 或 pnpm run cleanup-history -- --execute,再报告测试/验证历史已合规。',
|
||||||
'发现真实 env、生产 Secret 或非本次范围文件时停止提交。',
|
'发现真实 env、生产 Secret 或非本次范围文件时停止提交。',
|
||||||
karpathyStopCondition,
|
karpathyStopCondition,
|
||||||
],
|
],
|
||||||
summary: {
|
summary: {
|
||||||
changedProjectCount: changedSnapshots.length,
|
changedProjectCount: changedSnapshots.length,
|
||||||
changedProjects: changedSnapshots.map((snapshot) => snapshot.project.relativePath),
|
changedProjects: changedSnapshots.map((snapshot) => snapshot.project.relativePath),
|
||||||
|
cleanupPendingCount,
|
||||||
validationRunCount: validationRuns.length,
|
validationRunCount: validationRuns.length,
|
||||||
},
|
},
|
||||||
validationRuns,
|
validationRuns,
|
||||||
|
|||||||
43
src/types.ts
43
src/types.ts
@ -262,6 +262,49 @@ export interface WorkstreamCloseoutInput {
|
|||||||
verificationEvidence?: string[];
|
verificationEvidence?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface ObsidianValidateInput {
|
||||||
|
checkLayout?: boolean;
|
||||||
|
failOnWarnings?: boolean;
|
||||||
|
includeMarkdownLinks?: boolean;
|
||||||
|
includeStaleReferences?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ObsidianSyncInput extends ObsidianValidateInput {
|
||||||
|
includeValidation?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ObsidianContextInput {
|
||||||
|
includeCanvas?: boolean;
|
||||||
|
includeContent?: boolean;
|
||||||
|
maxDocuments?: number;
|
||||||
|
module?: string;
|
||||||
|
query?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface WorkflowLoopAuditInput {
|
||||||
|
changedProjects?: string[];
|
||||||
|
cleanupExecuted?: boolean;
|
||||||
|
cleanupPreviewDeleted?: number;
|
||||||
|
docSyncEvidence?: string[];
|
||||||
|
failureRecords?: string[];
|
||||||
|
ktWorkflowUpdated?: boolean;
|
||||||
|
problemRecords?: string[];
|
||||||
|
requireCompletion?: boolean;
|
||||||
|
reusablePattern?: boolean;
|
||||||
|
reviewEvidence?: string[];
|
||||||
|
stableSolutions?: string[];
|
||||||
|
stage?: 'failure' | 'finish' | 'start' | 'test';
|
||||||
|
taskTitle?: string;
|
||||||
|
testCases?: string[];
|
||||||
|
verificationEvidence?: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ChangeDocSyncInput {
|
||||||
|
changedFiles?: string[];
|
||||||
|
project?: string;
|
||||||
|
taskType?: TaskType;
|
||||||
|
}
|
||||||
|
|
||||||
export interface VerificationPlanResult {
|
export interface VerificationPlanResult {
|
||||||
changeType: TaskType;
|
changeType: TaskType;
|
||||||
commands: string[];
|
commands: string[];
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user