feat: 接入NapCat自动化MCP工具和CLI
This commit is contained in:
parent
c16a867c60
commit
b767732fd3
14
README.md
14
README.md
@ -16,6 +16,7 @@
|
|||||||
- 生成接口测试计划:接口改动后输出真实调用命令和统一返回结构断言。
|
- 生成接口测试计划:接口改动后输出真实调用命令和统一返回结构断言。
|
||||||
- 生成业务链路测试计划:固化 Admin 登录、博客 CRUD、QQBot 扫码/自动回复、更新登录 SSE、FFLogs 命令、系统日志可视化、Web/Playground 回跳。
|
- 生成业务链路测试计划:固化 Admin 登录、博客 CRUD、QQBot 扫码/自动回复、更新登录 SSE、FFLogs 命令、系统日志可视化、Web/Playground 回跳。
|
||||||
- 静态检查 NapCat 设备身份护栏:确认 API 仍保留 QQNT 可见 hostname、实体 OUI 风格 MAC、QQNT `machine-info` 写入、持久化 runtime dir 和 `DB_TIMEZONE` 默认值。
|
- 静态检查 NapCat 设备身份护栏:确认 API 仍保留 QQNT 可见 hostname、实体 OUI 风格 MAC、QQNT `machine-info` 写入、持久化 runtime dir 和 `DB_TIMEZONE` 默认值。
|
||||||
|
- 生成 NapCatQQ 上游同步与 runtime 发布自动化入口:MCP/CLI 默认 dry-run,覆盖 upstream audit、sync candidate review、runtime readiness、remote dev handoff 和 NAS Codex bootstrap plan;除 upstream audit 在显式 `execute=true` 且 `useCodex=true` 时可运行 Codex 外,其余 Task 5 入口只返回 prompt/context/只读命令骨架。
|
||||||
- 生成卡点固化记录:把超时、卡进程、远程命令误写、重复失败整理成“问题点 / 稳定解法 / 后续入口 / 验证证据”,避免原样重试。
|
- 生成卡点固化记录:把超时、卡进程、远程命令误写、重复失败整理成“问题点 / 稳定解法 / 后续入口 / 验证证据”,避免原样重试。
|
||||||
- 生成改动文档同步计划:按变更文件自动提示需要同步的 README、API、AGENTS、docs、Obsidian、skill 和 ktWorkflow 入口。
|
- 生成改动文档同步计划:按变更文件自动提示需要同步的 README、API、AGENTS、docs、Obsidian、skill 和 ktWorkflow 入口。
|
||||||
- 生成多仓库提交/推送计划:按仓库分组、建议提交信息、列出提交和推送前检查。
|
- 生成多仓库提交/推送计划:按仓库分组、建议提交信息、列出提交和推送前检查。
|
||||||
@ -40,6 +41,10 @@ pnpm run obsidian-context -- --module ktWorkflow
|
|||||||
pnpm run obsidian-validate
|
pnpm run obsidian-validate
|
||||||
pnpm run obsidian-sync
|
pnpm run obsidian-sync
|
||||||
pnpm run workstream-closeout -- --title "发布闭环" --verification "Jenkins SUCCESS" --doc-sync "无需文档更新" --cleanup "cleanup-history dry-run deleted=0" --cleanup-final-deleted 0 --review "global-review findings=0" --superpowers-review "Superpowers reviewer completed; no Critical/Important findings" --problem "无新卡点" --solution "无新增稳定解法"
|
pnpm run workstream-closeout -- --title "发布闭环" --verification "Jenkins SUCCESS" --doc-sync "无需文档更新" --cleanup "cleanup-history dry-run deleted=0" --cleanup-final-deleted 0 --review "global-review findings=0" --superpowers-review "Superpowers reviewer completed; no Critical/Important findings" --problem "无新卡点" --solution "无新增稳定解法"
|
||||||
|
pnpm run napcat-upstream-audit -- --artifact-root .kt-workspace/test-artifacts/napcat-upstream-sync
|
||||||
|
pnpm run napcat-sync-candidate-review
|
||||||
|
pnpm run napcat-runtime-release-readiness
|
||||||
|
pnpm run nas-codex-bootstrap
|
||||||
pnpm run napcat-device-profile-check
|
pnpm run napcat-device-profile-check
|
||||||
pnpm run cleanup-history -- --dry-run
|
pnpm run cleanup-history -- --dry-run
|
||||||
pnpm run cleanup-history -- --execute
|
pnpm run cleanup-history -- --execute
|
||||||
@ -95,6 +100,11 @@ pnpm run admin-login -- --url http://127.0.0.1:5999/#/auth/login
|
|||||||
| `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 和系统日志 |
|
||||||
|
| `kt_napcat_upstream_audit` | 生成或执行 NapCatQQ upstream release 审计,默认 dry-run,显式 `execute/useCodex` 后才运行 Codex |
|
||||||
|
| `kt_napcat_sync_candidate_review` | 生成 NapCat sync candidate review 的 prompt/context/只读命令骨架,Task 5 不执行 Codex 或 Git 写操作 |
|
||||||
|
| `kt_napcat_runtime_release_readiness` | 生成 NapCat runtime release readiness 的 prompt/context/只读检查骨架,不自动构建或发布 |
|
||||||
|
| `kt_napcat_remote_dev_handoff` | 生成 NapCat 自动化远程开发 handoff 的 prompt/context 骨架,不创建线程或同步仓库 |
|
||||||
|
| `kt_nas_codex_bootstrap_plan` | 生成可信 NAS Codex bootstrap dry-run plan,Task 5 仅输出只读检查和占位计划 |
|
||||||
| `kt_napcat_device_profile_check` | 静态检查 API NapCat 设备身份护栏,覆盖 hostname、MAC、`machine-info`、runtime dir 和 DB timezone |
|
| `kt_napcat_device_profile_check` | 静态检查 API NapCat 设备身份护栏,覆盖 hostname、MAC、`machine-info`、runtime dir 和 DB timezone |
|
||||||
| `kt_blocker_resolution` | 生成或写入卡点固化记录,提醒停止原样重试 |
|
| `kt_blocker_resolution` | 生成或写入卡点固化记录,提醒停止原样重试 |
|
||||||
| `kt_remote_health_check` | 生成或执行远程只读健康检查命令 |
|
| `kt_remote_health_check` | 生成或执行远程只读健康检查命令 |
|
||||||
@ -113,6 +123,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 复审报告,并校验 `TASKS.md` 最近记录只保留范围、关键词、验证字段、NapCat `latest` 漂移、QQBot 重型服务单测导入风险、QQBot 插件 registry/controller smoke 未 mock 具体插件实现风险、OneBot/QQ 登录态边界、NapCat 密码登录验证码 wait 阶段、日志窗口锚点、验证码状态早于日志 URL 的短轮询、验证码 pending 被状态轮询误清理、API Pod SSH 日志读取超时及清理前 pending 回归,以及普通处理中状态误吃旧验证码日志回归;明显测试占位 token 不报凭据风险,确认误报时优先升级 `src/tools/review.ts`,不要把误报沉积到上下文。 |
|
| `pnpm run global-review` | 对 KT 全部子仓库做只读全局 CodeReview 扫描,默认仅对变更文件做内容深扫,输出 JSON 复审报告,并校验 `TASKS.md` 最近记录只保留范围、关键词、验证字段、NapCat `latest` 漂移、QQBot 重型服务单测导入风险、QQBot 插件 registry/controller smoke 未 mock 具体插件实现风险、OneBot/QQ 登录态边界、NapCat 密码登录验证码 wait 阶段、日志窗口锚点、验证码状态早于日志 URL 的短轮询、验证码 pending 被状态轮询误清理、API Pod SSH 日志读取超时及清理前 pending 回归,以及普通处理中状态误吃旧验证码日志回归;明显测试占位 token 不报凭据风险,确认误报时优先升级 `src/tools/review.ts`,不要把误报沉积到上下文。 |
|
||||||
|
| `pnpm run napcat-upstream-audit` | 默认 dry-run 输出 NapCatQQ 上游 release 审计分类、只读 Git 命令和 artifact 路径;显式 `--execute --use-codex` 才运行 Codex。 |
|
||||||
|
| `pnpm run napcat-sync-candidate-review` | 输出 sync candidate review 的 prompt/context/只读命令骨架,不执行 Codex 或 Git 写操作。 |
|
||||||
|
| `pnpm run napcat-runtime-release-readiness` | 输出 runtime release readiness 的 prompt/context/只读检查骨架,不构建镜像、不调用 kubectl、不发布。 |
|
||||||
|
| `pnpm run nas-codex-bootstrap` | 输出可信 NAS Codex bootstrap dry-run plan;真实安装、同步和 systemd 接入留给后续实现。 |
|
||||||
| `pnpm run napcat-device-profile-check` | 静态检查 API NapCat 设备身份护栏,防止回退到未知设备风险配置。 |
|
| `pnpm run napcat-device-profile-check` | 静态检查 API NapCat 设备身份护栏,防止回退到未知设备风险配置。 |
|
||||||
| `pnpm run deploy-observation` | 默认 dry-run 输出只读 NAS 观测命令;传 `--execute` 时执行 Jenkins/K8s/health/smoke 观测并写入 `.kt-workspace/test-artifacts/deploy-observation`。 |
|
| `pnpm run deploy-observation` | 默认 dry-run 输出只读 NAS 观测命令;传 `--execute` 时执行 Jenkins/K8s/health/smoke 观测并写入 `.kt-workspace/test-artifacts/deploy-observation`。 |
|
||||||
| `pnpm run obsidian-context` | 输出 Obsidian 索引上下文;可传 `--module Admin`、`--query QQBot`、`--max-documents 10`。 |
|
| `pnpm run obsidian-context` | 输出 Obsidian 索引上下文;可传 `--module Admin`、`--query QQBot`、`--max-documents 10`。 |
|
||||||
|
|||||||
@ -10,6 +10,10 @@
|
|||||||
"deploy-observation": "node --import ./node_modules/tsx/dist/loader.mjs src/server.ts --deploy-observation",
|
"deploy-observation": "node --import ./node_modules/tsx/dist/loader.mjs src/server.ts --deploy-observation",
|
||||||
"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",
|
||||||
"napcat-device-profile-check": "node --import ./node_modules/tsx/dist/loader.mjs src/server.ts --napcat-device-profile-check",
|
"napcat-device-profile-check": "node --import ./node_modules/tsx/dist/loader.mjs src/server.ts --napcat-device-profile-check",
|
||||||
|
"napcat-runtime-release-readiness": "node --import ./node_modules/tsx/dist/loader.mjs src/server.ts --napcat-runtime-release-readiness",
|
||||||
|
"napcat-sync-candidate-review": "node --import ./node_modules/tsx/dist/loader.mjs src/server.ts --napcat-sync-candidate-review",
|
||||||
|
"napcat-upstream-audit": "node --import ./node_modules/tsx/dist/loader.mjs src/server.ts --napcat-upstream-audit",
|
||||||
|
"nas-codex-bootstrap": "node --import ./node_modules/tsx/dist/loader.mjs src/server.ts --nas-codex-bootstrap",
|
||||||
"obsidian-context": "node --import ./node_modules/tsx/dist/loader.mjs src/server.ts --obsidian-context",
|
"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-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",
|
"obsidian-validate": "node --import ./node_modules/tsx/dist/loader.mjs src/server.ts --obsidian-validate",
|
||||||
|
|||||||
144
src/core/cli.ts
144
src/core/cli.ts
@ -1,6 +1,10 @@
|
|||||||
import type {
|
import type {
|
||||||
DeployObservationInput,
|
DeployObservationInput,
|
||||||
GlobalCodeReviewInput,
|
GlobalCodeReviewInput,
|
||||||
|
NapcatRemoteDevHandoffInput,
|
||||||
|
NapcatRuntimeReleaseReadinessInput,
|
||||||
|
NapcatSyncCandidateReviewInput,
|
||||||
|
NasCodexBootstrapPlanInput,
|
||||||
ObsidianContextInput,
|
ObsidianContextInput,
|
||||||
ObsidianSyncInput,
|
ObsidianSyncInput,
|
||||||
ObsidianValidateInput,
|
ObsidianValidateInput,
|
||||||
@ -8,6 +12,7 @@ import type {
|
|||||||
WorkstreamReusablePattern,
|
WorkstreamReusablePattern,
|
||||||
} from '../types.js';
|
} from '../types.js';
|
||||||
import { workstreamReusablePatternValues } from './constants.js';
|
import { workstreamReusablePatternValues } from './constants.js';
|
||||||
|
import type { NapcatUpstreamAuditInput } from '../tools/napcatAutomation.types.js';
|
||||||
|
|
||||||
function parseContentScanMode(argv: string[]): 'all' | 'changed' {
|
function parseContentScanMode(argv: string[]): 'all' | 'changed' {
|
||||||
if (argv.includes('--content-scan-all') || argv.includes('--contentScanMode=all')) {
|
if (argv.includes('--content-scan-all') || argv.includes('--contentScanMode=all')) {
|
||||||
@ -62,6 +67,16 @@ function readOptionList(argv: string[], names: string[]): string[] {
|
|||||||
.filter(Boolean);
|
.filter(Boolean);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Checks whether any supported boolean CLI flag is present in the raw argv list.
|
||||||
|
* @param argv - Full process argv including node, script, mode flag, and user options.
|
||||||
|
* @param names - Flag names to accept in dashed or camelCase form.
|
||||||
|
* @returns True when the caller provided one of the named flags.
|
||||||
|
*/
|
||||||
|
function hasFlag(argv: string[], names: string[]): boolean {
|
||||||
|
return names.some((name) => argv.includes(name));
|
||||||
|
}
|
||||||
|
|
||||||
export function parseWorkstreamCloseoutCliArgs(argv: string[]): WorkstreamCloseoutInput {
|
export function parseWorkstreamCloseoutCliArgs(argv: string[]): WorkstreamCloseoutInput {
|
||||||
const patternSet = new Set<string>(workstreamReusablePatternValues);
|
const patternSet = new Set<string>(workstreamReusablePatternValues);
|
||||||
const patterns = readOptionList(argv, ['--pattern', '--reusable-pattern'])
|
const patterns = readOptionList(argv, ['--pattern', '--reusable-pattern'])
|
||||||
@ -123,6 +138,135 @@ export function parseDeployObservationCliArgs(argv: string[]): DeployObservation
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the NapCat upstream audit CLI mode into the deterministic audit builder input.
|
||||||
|
* @param argv - Full process argv from `src/server.ts`; `--execute` and `--use-codex` must both be explicit before Codex can run.
|
||||||
|
* @returns Upstream audit input with dry-run defaults for all optional metadata lists.
|
||||||
|
*/
|
||||||
|
export function parseNapcatUpstreamAuditCliArgs(
|
||||||
|
argv: string[],
|
||||||
|
): NapcatUpstreamAuditInput {
|
||||||
|
return {
|
||||||
|
artifactRoot: readOption(argv, ['--artifact-root', '--artifactRoot']),
|
||||||
|
codexHome: readOption(argv, ['--codex-home', '--codexHome']),
|
||||||
|
createCandidateBranch: hasFlag(argv, ['--create-candidate-branch', '--createCandidateBranch']),
|
||||||
|
dryMergeConflict: hasFlag(argv, ['--dry-merge-conflict', '--dryMergeConflict']),
|
||||||
|
execute: hasFlag(argv, ['--execute']),
|
||||||
|
forkBranch: readOption(argv, ['--fork-branch', '--forkBranch']),
|
||||||
|
forkPatchFiles: readOptionList(argv, [
|
||||||
|
'--fork-patch-file',
|
||||||
|
'--fork-patch-files',
|
||||||
|
'--forkPatchFile',
|
||||||
|
'--forkPatchFiles',
|
||||||
|
]),
|
||||||
|
forkRepo: readOption(argv, ['--fork-repo', '--forkRepo']),
|
||||||
|
lastAcceptedUpstreamBase: readOption(argv, [
|
||||||
|
'--last-accepted-upstream-base',
|
||||||
|
'--lastAcceptedUpstreamBase',
|
||||||
|
]),
|
||||||
|
outputSchemaPath: readOption(argv, ['--output-schema', '--outputSchemaPath']),
|
||||||
|
upstreamChangedFiles: readOptionList(argv, [
|
||||||
|
'--upstream-changed-file',
|
||||||
|
'--upstream-changed-files',
|
||||||
|
'--upstreamChangedFile',
|
||||||
|
'--upstreamChangedFiles',
|
||||||
|
]),
|
||||||
|
upstreamReleaseTag: readOption(argv, [
|
||||||
|
'--upstream-release-tag',
|
||||||
|
'--upstream-release-ref',
|
||||||
|
'--upstreamReleaseTag',
|
||||||
|
]),
|
||||||
|
upstreamRepo: readOption(argv, ['--upstream-repo', '--upstreamRepo']),
|
||||||
|
useCodex: hasFlag(argv, ['--use-codex', '--useCodex']),
|
||||||
|
workspaceRoot: readOption(argv, ['--workspace-root', '--workspaceRoot']),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses common dry-run NapCat prompt tool options shared by candidate review, readiness, and handoff modes.
|
||||||
|
* @param argv - Full process argv from a NapCat automation CLI script.
|
||||||
|
* @returns Prompt skeleton input with `execute` false unless explicitly requested by the caller.
|
||||||
|
*/
|
||||||
|
function parseNapcatPromptToolCliArgs(argv: string[]): NapcatSyncCandidateReviewInput {
|
||||||
|
return {
|
||||||
|
artifactRoot: readOption(argv, ['--artifact-root', '--artifactRoot']),
|
||||||
|
candidateBranch: readOption(argv, ['--candidate-branch', '--candidateBranch']),
|
||||||
|
execute: hasFlag(argv, ['--execute']),
|
||||||
|
forkBranch: readOption(argv, ['--fork-branch', '--forkBranch']),
|
||||||
|
forkRepo: readOption(argv, ['--fork-repo', '--forkRepo']),
|
||||||
|
lastAcceptedUpstreamBase: readOption(argv, [
|
||||||
|
'--last-accepted-upstream-base',
|
||||||
|
'--lastAcceptedUpstreamBase',
|
||||||
|
]),
|
||||||
|
upstreamReleaseTag: readOption(argv, [
|
||||||
|
'--upstream-release-tag',
|
||||||
|
'--upstream-release-ref',
|
||||||
|
'--upstreamReleaseTag',
|
||||||
|
]),
|
||||||
|
upstreamRepo: readOption(argv, ['--upstream-repo', '--upstreamRepo']),
|
||||||
|
useCodex: hasFlag(argv, ['--use-codex', '--useCodex']),
|
||||||
|
workspaceRoot: readOption(argv, ['--workspace-root', '--workspaceRoot']),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the NapCat sync-candidate review CLI mode.
|
||||||
|
* @param argv - Full process argv from `pnpm run napcat-sync-candidate-review`.
|
||||||
|
* @returns Dry-run prompt skeleton input for human/Codex candidate review handoff.
|
||||||
|
*/
|
||||||
|
export function parseNapcatSyncCandidateReviewCliArgs(
|
||||||
|
argv: string[],
|
||||||
|
): NapcatSyncCandidateReviewInput {
|
||||||
|
return parseNapcatPromptToolCliArgs(argv);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the NapCat runtime release readiness CLI mode.
|
||||||
|
* @param argv - Full process argv from `pnpm run napcat-runtime-release-readiness`.
|
||||||
|
* @returns Dry-run prompt skeleton input with image/profile hints and no default mutation.
|
||||||
|
*/
|
||||||
|
export function parseNapcatRuntimeReleaseReadinessCliArgs(
|
||||||
|
argv: string[],
|
||||||
|
): NapcatRuntimeReleaseReadinessInput {
|
||||||
|
return {
|
||||||
|
...parseNapcatPromptToolCliArgs(argv),
|
||||||
|
apiImageTag: readOption(argv, ['--api-image-tag', '--apiImageTag']),
|
||||||
|
napcatImageTag: readOption(argv, ['--napcat-image-tag', '--napcatImageTag']),
|
||||||
|
profile: readOption(argv, ['--profile']),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the NapCat remote development handoff CLI mode.
|
||||||
|
* @param argv - Full process argv from `src/server.ts --napcat-remote-dev-handoff`.
|
||||||
|
* @returns Dry-run prompt skeleton input for a remote workspace handoff packet.
|
||||||
|
*/
|
||||||
|
export function parseNapcatRemoteDevHandoffCliArgs(
|
||||||
|
argv: string[],
|
||||||
|
): NapcatRemoteDevHandoffInput {
|
||||||
|
return {
|
||||||
|
...parseNapcatPromptToolCliArgs(argv),
|
||||||
|
targetHost: readOption(argv, ['--target-host', '--targetHost']),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parses the NAS Codex bootstrap planning CLI mode.
|
||||||
|
* @param argv - Full process argv from `pnpm run nas-codex-bootstrap`.
|
||||||
|
* @returns Dry-run bootstrap plan input; `execute` is false unless the caller explicitly opts in.
|
||||||
|
*/
|
||||||
|
export function parseNasCodexBootstrapCliArgs(
|
||||||
|
argv: string[],
|
||||||
|
): NasCodexBootstrapPlanInput {
|
||||||
|
return {
|
||||||
|
artifactRoot: readOption(argv, ['--artifact-root', '--artifactRoot']),
|
||||||
|
codexHome: readOption(argv, ['--codex-home', '--codexHome']),
|
||||||
|
execute: hasFlag(argv, ['--execute']),
|
||||||
|
serviceName: readOption(argv, ['--service-name', '--serviceName']),
|
||||||
|
workspaceRoot: readOption(argv, ['--workspace-root', '--workspaceRoot']),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export function parseObsidianCliArgs(argv: string[]): ObsidianValidateInput {
|
export function parseObsidianCliArgs(argv: string[]): ObsidianValidateInput {
|
||||||
return {
|
return {
|
||||||
checkLayout: !argv.includes('--no-layout'),
|
checkLayout: !argv.includes('--no-layout'),
|
||||||
|
|||||||
@ -69,6 +69,11 @@ export const registeredToolNames = [
|
|||||||
'kt_commit_plan',
|
'kt_commit_plan',
|
||||||
'kt_push_plan',
|
'kt_push_plan',
|
||||||
'kt_business_test_plan',
|
'kt_business_test_plan',
|
||||||
|
'kt_napcat_upstream_audit',
|
||||||
|
'kt_napcat_sync_candidate_review',
|
||||||
|
'kt_napcat_runtime_release_readiness',
|
||||||
|
'kt_napcat_remote_dev_handoff',
|
||||||
|
'kt_nas_codex_bootstrap_plan',
|
||||||
'kt_napcat_device_profile_check',
|
'kt_napcat_device_profile_check',
|
||||||
'kt_blocker_resolution',
|
'kt_blocker_resolution',
|
||||||
'kt_remote_health_check',
|
'kt_remote_health_check',
|
||||||
|
|||||||
@ -22,6 +22,13 @@ 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 { buildChangeDocSync } from './tools/docSync.js';
|
||||||
import { buildDeployObservation } from './tools/deployObservation.js';
|
import { buildDeployObservation } from './tools/deployObservation.js';
|
||||||
|
import {
|
||||||
|
buildNapcatRemoteDevHandoff,
|
||||||
|
buildNapcatRuntimeReleaseReadiness,
|
||||||
|
buildNapcatSyncCandidateReview,
|
||||||
|
buildNapcatUpstreamAudit,
|
||||||
|
buildNasCodexBootstrapPlan,
|
||||||
|
} from './tools/napcatAutomation.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 {
|
||||||
@ -35,6 +42,11 @@ export function response(data: unknown): McpTextResponse {
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Registers every ktWorkflow MCP tool against the provided MCP server instance.
|
||||||
|
* @param server - MCP SDK server that owns tool names, schemas, descriptions, and async handlers.
|
||||||
|
* @returns Nothing; handlers are attached to `server` for later stdio transport use.
|
||||||
|
*/
|
||||||
export function registerTools(server: McpServer): void {
|
export function registerTools(server: McpServer): void {
|
||||||
server.registerTool(
|
server.registerTool(
|
||||||
'kt_read_context',
|
'kt_read_context',
|
||||||
@ -370,6 +382,128 @@ export function registerTools(server: McpServer): void {
|
|||||||
async (input) => response(buildBusinessTestPlan(input)),
|
async (input) => response(buildBusinessTestPlan(input)),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
server.registerTool(
|
||||||
|
'kt_napcat_upstream_audit',
|
||||||
|
{
|
||||||
|
description:
|
||||||
|
'生成或执行 NapCatQQ 上游 release 审计:默认 dry-run,只读输出 hot zone、fork patch overlap、dry merge 和 Codex 审计入口;execute/useCodex 均需显式开启才会运行 Codex。',
|
||||||
|
inputSchema: {
|
||||||
|
artifactRoot: z
|
||||||
|
.string()
|
||||||
|
.default('.kt-workspace/test-artifacts/napcat-upstream-audit'),
|
||||||
|
codexHome: z.string().optional(),
|
||||||
|
createCandidateBranch: z.boolean().default(false),
|
||||||
|
dryMergeConflict: z.boolean().default(false),
|
||||||
|
execute: z.boolean().default(false),
|
||||||
|
forkBranch: z.string().default('HEAD'),
|
||||||
|
forkPatchFiles: z.array(z.string()).default([]),
|
||||||
|
forkRepo: z.string().default('KTeam/NapCatQQ'),
|
||||||
|
lastAcceptedUpstreamBase: z.string().default('origin/main'),
|
||||||
|
outputSchemaPath: z.string().optional(),
|
||||||
|
upstreamChangedFiles: z.array(z.string()).default([]),
|
||||||
|
upstreamReleaseTag: z.string().default('upstream/main'),
|
||||||
|
upstreamRepo: z.string().default('NapNeko/NapCatQQ'),
|
||||||
|
useCodex: z.boolean().default(false),
|
||||||
|
workspaceRoot: z.string().default('/vol1/docker/kt-codex/workspace/KT'),
|
||||||
|
},
|
||||||
|
title: 'KT NapCat Upstream Audit',
|
||||||
|
},
|
||||||
|
async (input) => response(buildNapcatUpstreamAudit(input)),
|
||||||
|
);
|
||||||
|
|
||||||
|
server.registerTool(
|
||||||
|
'kt_napcat_sync_candidate_review',
|
||||||
|
{
|
||||||
|
description:
|
||||||
|
'生成 NapCatQQ sync candidate 人工/Codex 复审的 dry-run prompt、上下文和只读命令骨架;Task 5 不执行 Codex 或 Git 写操作。',
|
||||||
|
inputSchema: {
|
||||||
|
artifactRoot: z
|
||||||
|
.string()
|
||||||
|
.default('.kt-workspace/test-artifacts/napcat-automation/sync-candidate-review'),
|
||||||
|
candidateBranch: z.string().default('napcat-upstream-sync/candidate'),
|
||||||
|
execute: z.boolean().default(false),
|
||||||
|
forkBranch: z.string().default('dev'),
|
||||||
|
forkRepo: z.string().default('KTeam/NapCatQQ'),
|
||||||
|
lastAcceptedUpstreamBase: z.string().default('origin/main'),
|
||||||
|
upstreamReleaseTag: z.string().default('upstream/main'),
|
||||||
|
upstreamRepo: z.string().default('NapNeko/NapCatQQ'),
|
||||||
|
useCodex: z.boolean().default(false),
|
||||||
|
workspaceRoot: z.string().default('/vol1/docker/kt-codex/workspace/KT'),
|
||||||
|
},
|
||||||
|
title: 'KT NapCat Sync Candidate Review',
|
||||||
|
},
|
||||||
|
async (input) => response(buildNapcatSyncCandidateReview(input)),
|
||||||
|
);
|
||||||
|
|
||||||
|
server.registerTool(
|
||||||
|
'kt_napcat_runtime_release_readiness',
|
||||||
|
{
|
||||||
|
description:
|
||||||
|
'生成 NapCat runtime release readiness dry-run prompt、上下文和只读检查骨架;不自动构建镜像、不调用 kubectl、不发布。',
|
||||||
|
inputSchema: {
|
||||||
|
apiImageTag: z.string().default('main-<jenkins-build>'),
|
||||||
|
artifactRoot: z
|
||||||
|
.string()
|
||||||
|
.default('.kt-workspace/test-artifacts/napcat-automation/runtime-release-readiness'),
|
||||||
|
candidateBranch: z.string().default('napcat-upstream-sync/candidate'),
|
||||||
|
execute: z.boolean().default(false),
|
||||||
|
forkBranch: z.string().default('dev'),
|
||||||
|
forkRepo: z.string().default('KTeam/NapCatQQ'),
|
||||||
|
napcatImageTag: z.string().default('kt-napcat-desktop-cn:desktop-cn'),
|
||||||
|
profile: z.string().default('desktop-cn'),
|
||||||
|
upstreamReleaseTag: z.string().default('upstream/main'),
|
||||||
|
upstreamRepo: z.string().default('NapNeko/NapCatQQ'),
|
||||||
|
useCodex: z.boolean().default(false),
|
||||||
|
workspaceRoot: z.string().default('/vol1/docker/kt-codex/workspace/KT'),
|
||||||
|
},
|
||||||
|
title: 'KT NapCat Runtime Release Readiness',
|
||||||
|
},
|
||||||
|
async (input) => response(buildNapcatRuntimeReleaseReadiness(input)),
|
||||||
|
);
|
||||||
|
|
||||||
|
server.registerTool(
|
||||||
|
'kt_napcat_remote_dev_handoff',
|
||||||
|
{
|
||||||
|
description:
|
||||||
|
'生成 NapCat 自动化远程开发 handoff 的 dry-run prompt 和上下文骨架;不创建线程、不执行 Codex、不同步仓库。',
|
||||||
|
inputSchema: {
|
||||||
|
artifactRoot: z
|
||||||
|
.string()
|
||||||
|
.default('.kt-workspace/test-artifacts/napcat-automation/remote-dev-handoff'),
|
||||||
|
candidateBranch: z.string().default('napcat-upstream-sync/candidate'),
|
||||||
|
execute: z.boolean().default(false),
|
||||||
|
forkBranch: z.string().default('dev'),
|
||||||
|
forkRepo: z.string().default('KTeam/NapCatQQ'),
|
||||||
|
targetHost: z.string().default('nas'),
|
||||||
|
upstreamReleaseTag: z.string().default('upstream/main'),
|
||||||
|
upstreamRepo: z.string().default('NapNeko/NapCatQQ'),
|
||||||
|
useCodex: z.boolean().default(false),
|
||||||
|
workspaceRoot: z.string().default('/vol1/docker/kt-codex/workspace/KT'),
|
||||||
|
},
|
||||||
|
title: 'KT NapCat Remote Dev Handoff',
|
||||||
|
},
|
||||||
|
async (input) => response(buildNapcatRemoteDevHandoff(input)),
|
||||||
|
);
|
||||||
|
|
||||||
|
server.registerTool(
|
||||||
|
'kt_nas_codex_bootstrap_plan',
|
||||||
|
{
|
||||||
|
description:
|
||||||
|
'生成可信 NAS Codex 自动化环境 bootstrap 的 dry-run plan;Task 5 仅输出只读检查和占位计划,不安装、不同步、不写 systemd。',
|
||||||
|
inputSchema: {
|
||||||
|
artifactRoot: z
|
||||||
|
.string()
|
||||||
|
.default('.kt-workspace/test-artifacts/napcat-automation/nas-codex-bootstrap'),
|
||||||
|
codexHome: z.string().default('/vol1/docker/kt-codex/home/.codex'),
|
||||||
|
execute: z.boolean().default(false),
|
||||||
|
serviceName: z.string().default('kt-codex-automation'),
|
||||||
|
workspaceRoot: z.string().default('/vol1/docker/kt-codex/workspace/KT'),
|
||||||
|
},
|
||||||
|
title: 'KT NAS Codex Bootstrap Plan',
|
||||||
|
},
|
||||||
|
async (input) => response(buildNasCodexBootstrapPlan(input)),
|
||||||
|
);
|
||||||
|
|
||||||
server.registerTool(
|
server.registerTool(
|
||||||
'kt_napcat_device_profile_check',
|
'kt_napcat_device_profile_check',
|
||||||
{
|
{
|
||||||
|
|||||||
@ -2,8 +2,12 @@ import { existsSync, mkdirSync, readFileSync, rmSync, writeFileSync } from "node
|
|||||||
import path from "node:path";
|
import path from "node:path";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
parseNasCodexBootstrapCliArgs,
|
||||||
parseDeployObservationCliArgs,
|
parseDeployObservationCliArgs,
|
||||||
parseGlobalReviewCliArgs,
|
parseGlobalReviewCliArgs,
|
||||||
|
parseNapcatRuntimeReleaseReadinessCliArgs,
|
||||||
|
parseNapcatSyncCandidateReviewCliArgs,
|
||||||
|
parseNapcatUpstreamAuditCliArgs,
|
||||||
parseWorkstreamCloseoutCliArgs,
|
parseWorkstreamCloseoutCliArgs,
|
||||||
} from "./core/cli.js";
|
} from "./core/cli.js";
|
||||||
import { registeredToolNames } from "./core/constants.js";
|
import { registeredToolNames } from "./core/constants.js";
|
||||||
@ -96,6 +100,51 @@ export async function runSelfTest(): Promise<void> {
|
|||||||
if (!napcatAutomationReasonCodes.includes("HOT_ZONE_CHANGED")) {
|
if (!napcatAutomationReasonCodes.includes("HOT_ZONE_CHANGED")) {
|
||||||
throw new Error("NapCat reason-code list self-check failed");
|
throw new Error("NapCat reason-code list self-check failed");
|
||||||
}
|
}
|
||||||
|
const napcatCli = parseNapcatUpstreamAuditCliArgs([
|
||||||
|
"node",
|
||||||
|
"server",
|
||||||
|
"--napcat-upstream-audit",
|
||||||
|
"--execute",
|
||||||
|
"--use-codex",
|
||||||
|
"--artifact-root",
|
||||||
|
"/vol1/docker/kt-codex/artifacts/napcat-upstream-sync",
|
||||||
|
]);
|
||||||
|
if (!napcatCli.execute || !napcatCli.useCodex) {
|
||||||
|
throw new Error("NapCat upstream audit CLI parser self-check failed");
|
||||||
|
}
|
||||||
|
const napcatCandidateCli = parseNapcatSyncCandidateReviewCliArgs([
|
||||||
|
"node",
|
||||||
|
"server",
|
||||||
|
"--napcat-sync-candidate-review",
|
||||||
|
]);
|
||||||
|
const napcatRuntimeCli = parseNapcatRuntimeReleaseReadinessCliArgs([
|
||||||
|
"node",
|
||||||
|
"server",
|
||||||
|
"--napcat-runtime-release-readiness",
|
||||||
|
]);
|
||||||
|
const nasBootstrapCli = parseNasCodexBootstrapCliArgs([
|
||||||
|
"node",
|
||||||
|
"server",
|
||||||
|
"--nas-codex-bootstrap",
|
||||||
|
]);
|
||||||
|
if (
|
||||||
|
napcatCandidateCli.execute ||
|
||||||
|
napcatRuntimeCli.execute ||
|
||||||
|
nasBootstrapCli.execute
|
||||||
|
) {
|
||||||
|
throw new Error("NapCat auxiliary CLI dry-run default self-check failed");
|
||||||
|
}
|
||||||
|
for (const toolName of [
|
||||||
|
"kt_napcat_upstream_audit",
|
||||||
|
"kt_napcat_sync_candidate_review",
|
||||||
|
"kt_napcat_runtime_release_readiness",
|
||||||
|
"kt_napcat_remote_dev_handoff",
|
||||||
|
"kt_nas_codex_bootstrap_plan",
|
||||||
|
] as const) {
|
||||||
|
if (!registeredToolNames.includes(toolName)) {
|
||||||
|
throw new Error(`NapCat MCP tool registry self-check failed: ${toolName}`);
|
||||||
|
}
|
||||||
|
}
|
||||||
if (parseNapcatAutomationClassification("manual-review") !== "manual-review") {
|
if (parseNapcatAutomationClassification("manual-review") !== "manual-review") {
|
||||||
throw new Error("NapCat classification parser self-check failed");
|
throw new Error("NapCat classification parser self-check failed");
|
||||||
}
|
}
|
||||||
|
|||||||
@ -3,8 +3,13 @@ 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 {
|
import {
|
||||||
|
parseNasCodexBootstrapCliArgs,
|
||||||
parseDeployObservationCliArgs,
|
parseDeployObservationCliArgs,
|
||||||
parseGlobalReviewCliArgs,
|
parseGlobalReviewCliArgs,
|
||||||
|
parseNapcatRemoteDevHandoffCliArgs,
|
||||||
|
parseNapcatRuntimeReleaseReadinessCliArgs,
|
||||||
|
parseNapcatSyncCandidateReviewCliArgs,
|
||||||
|
parseNapcatUpstreamAuditCliArgs,
|
||||||
parseObsidianContextCliArgs,
|
parseObsidianContextCliArgs,
|
||||||
parseObsidianCliArgs,
|
parseObsidianCliArgs,
|
||||||
parseObsidianSyncCliArgs,
|
parseObsidianSyncCliArgs,
|
||||||
@ -18,6 +23,13 @@ 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';
|
||||||
import { buildNapcatDeviceProfileCheck } from './tools/testing.js';
|
import { buildNapcatDeviceProfileCheck } from './tools/testing.js';
|
||||||
|
import {
|
||||||
|
buildNapcatRemoteDevHandoff,
|
||||||
|
buildNapcatRuntimeReleaseReadiness,
|
||||||
|
buildNapcatSyncCandidateReview,
|
||||||
|
buildNapcatUpstreamAudit,
|
||||||
|
buildNasCodexBootstrapPlan,
|
||||||
|
} from './tools/napcatAutomation.js';
|
||||||
|
|
||||||
if (process.argv.includes('--cleanup-history')) {
|
if (process.argv.includes('--cleanup-history')) {
|
||||||
console.log(JSON.stringify(cleanupHistoryArtifacts(parseCliCleanupArgs(process.argv)), null, 2));
|
console.log(JSON.stringify(cleanupHistoryArtifacts(parseCliCleanupArgs(process.argv)), null, 2));
|
||||||
@ -43,6 +55,38 @@ if (process.argv.includes('--cleanup-history')) {
|
|||||||
2,
|
2,
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
|
} else if (process.argv.includes('--napcat-upstream-audit')) {
|
||||||
|
console.log(
|
||||||
|
JSON.stringify(buildNapcatUpstreamAudit(parseNapcatUpstreamAuditCliArgs(process.argv)), null, 2),
|
||||||
|
);
|
||||||
|
} else if (process.argv.includes('--napcat-sync-candidate-review')) {
|
||||||
|
console.log(
|
||||||
|
JSON.stringify(
|
||||||
|
buildNapcatSyncCandidateReview(parseNapcatSyncCandidateReviewCliArgs(process.argv)),
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (process.argv.includes('--napcat-runtime-release-readiness')) {
|
||||||
|
console.log(
|
||||||
|
JSON.stringify(
|
||||||
|
buildNapcatRuntimeReleaseReadiness(parseNapcatRuntimeReleaseReadinessCliArgs(process.argv)),
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (process.argv.includes('--napcat-remote-dev-handoff')) {
|
||||||
|
console.log(
|
||||||
|
JSON.stringify(
|
||||||
|
buildNapcatRemoteDevHandoff(parseNapcatRemoteDevHandoffCliArgs(process.argv)),
|
||||||
|
null,
|
||||||
|
2,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
} else if (process.argv.includes('--nas-codex-bootstrap')) {
|
||||||
|
console.log(
|
||||||
|
JSON.stringify(buildNasCodexBootstrapPlan(parseNasCodexBootstrapCliArgs(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')) {
|
||||||
|
|||||||
@ -3,8 +3,18 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'node:fs';
|
|||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
|
|
||||||
import { upstreamAuditOutputSchema } from './napcatAutomation.types.js';
|
import { upstreamAuditOutputSchema } from './napcatAutomation.types.js';
|
||||||
import { loadNapcatAutomationPrompt } from './napcatAutomation.prompts.js';
|
import {
|
||||||
|
loadNapcatAutomationPrompt,
|
||||||
|
type NapcatAutomationPromptName,
|
||||||
|
} from './napcatAutomation.prompts.js';
|
||||||
import { workspaceRoot } from '../core/workspace.js';
|
import { workspaceRoot } from '../core/workspace.js';
|
||||||
|
import type {
|
||||||
|
NapcatPromptToolInput,
|
||||||
|
NapcatRemoteDevHandoffInput,
|
||||||
|
NapcatRuntimeReleaseReadinessInput,
|
||||||
|
NapcatSyncCandidateReviewInput,
|
||||||
|
NasCodexBootstrapPlanInput,
|
||||||
|
} from '../types.js';
|
||||||
import type {
|
import type {
|
||||||
NapcatAuditClassificationInput,
|
NapcatAuditClassificationInput,
|
||||||
NapcatAuditClassificationResult,
|
NapcatAuditClassificationResult,
|
||||||
@ -88,6 +98,279 @@ const hotZoneMatchers: Array<{ pattern: string; regex: RegExp }> = [
|
|||||||
|
|
||||||
const napcatNasCodexArtifactRoot = '/vol1/docker/kt-codex/artifacts';
|
const napcatNasCodexArtifactRoot = '/vol1/docker/kt-codex/artifacts';
|
||||||
const napcatLocalArtifactRoot = '.kt-workspace/test-artifacts';
|
const napcatLocalArtifactRoot = '.kt-workspace/test-artifacts';
|
||||||
|
const napcatAutomationSkeletonArtifactRoot =
|
||||||
|
'.kt-workspace/test-artifacts/napcat-automation';
|
||||||
|
const defaultNapcatForkRepo = 'KTeam/NapCatQQ';
|
||||||
|
const defaultNapcatUpstreamRepo = 'NapNeko/NapCatQQ';
|
||||||
|
const defaultNapcatCandidateBranch = 'napcat-upstream-sync/candidate';
|
||||||
|
const defaultNapcatWorkspaceRoot = '/vol1/docker/kt-codex/workspace/KT';
|
||||||
|
|
||||||
|
interface NapcatPromptSkeletonConfig {
|
||||||
|
artifactSlug: string;
|
||||||
|
commands: NapcatAuditCommand[];
|
||||||
|
context: Record<string, unknown>;
|
||||||
|
input: NapcatPromptToolInput;
|
||||||
|
notes: string[];
|
||||||
|
promptName: NapcatAutomationPromptName;
|
||||||
|
title: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Resolves the artifact root shown by dry-run NapCat prompt skeleton tools.
|
||||||
|
* @param input - Prompt tool input carrying an optional artifactRoot override.
|
||||||
|
* @param artifactSlug - Stable child directory name for the specific automation tool.
|
||||||
|
* @returns The caller-provided artifact root, or the ktWorkflow test-artifact default for this tool.
|
||||||
|
*/
|
||||||
|
function resolveNapcatSkeletonArtifactRoot(
|
||||||
|
input: NapcatPromptToolInput,
|
||||||
|
artifactSlug: string,
|
||||||
|
): string {
|
||||||
|
return (
|
||||||
|
input.artifactRoot?.trim() ||
|
||||||
|
`${napcatAutomationSkeletonArtifactRoot}/${artifactSlug}`
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a deterministic dry-run prompt skeleton for Task 5 NapCat automation tools that are not allowed to mutate state yet.
|
||||||
|
* @param config - Prompt name, context payload, read-only command hints, and caller execution intent.
|
||||||
|
* @returns A structured prompt/context/command package that never runs Codex or mutating release commands.
|
||||||
|
*/
|
||||||
|
function buildNapcatPromptSkeleton(config: NapcatPromptSkeletonConfig) {
|
||||||
|
const artifactRoot = resolveNapcatSkeletonArtifactRoot(
|
||||||
|
config.input,
|
||||||
|
config.artifactSlug,
|
||||||
|
);
|
||||||
|
const requestedExecute = config.input.execute === true;
|
||||||
|
|
||||||
|
return {
|
||||||
|
artifacts: [
|
||||||
|
{
|
||||||
|
path: joinNapcatArtifactPath(
|
||||||
|
artifactRoot,
|
||||||
|
`${config.artifactSlug}-prompt.md`,
|
||||||
|
),
|
||||||
|
type: 'markdown',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: joinNapcatArtifactPath(
|
||||||
|
artifactRoot,
|
||||||
|
`${config.artifactSlug}-context.json`,
|
||||||
|
),
|
||||||
|
type: 'json',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
commands: config.commands,
|
||||||
|
context: config.context,
|
||||||
|
execute: false,
|
||||||
|
notes: [
|
||||||
|
'Task 5 dry-run skeleton only; no Codex, git merge, git push, docker build, kubectl, or deploy command is executed.',
|
||||||
|
requestedExecute
|
||||||
|
? 'execute=true was requested, but this Task 5 skeleton intentionally remains non-mutating until the later implementation task.'
|
||||||
|
: 'execute defaults to false.',
|
||||||
|
...config.notes,
|
||||||
|
],
|
||||||
|
prompt: {
|
||||||
|
name: config.promptName,
|
||||||
|
text: loadNapcatAutomationPrompt(config.promptName).trim(),
|
||||||
|
},
|
||||||
|
requestedExecute,
|
||||||
|
summary: `${config.title} dry-run prompt skeleton generated.`,
|
||||||
|
useCodex: config.input.useCodex === true,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a dry-run prompt skeleton for reviewing a NapCat upstream sync candidate branch.
|
||||||
|
* @param input - Candidate branch, fork/upstream refs, artifact root, and execution intent; execution is never performed in Task 5.
|
||||||
|
* @returns Prompt, context, artifacts, and read-only command hints for candidate review.
|
||||||
|
*/
|
||||||
|
export function buildNapcatSyncCandidateReview(
|
||||||
|
input: NapcatSyncCandidateReviewInput,
|
||||||
|
) {
|
||||||
|
const context = {
|
||||||
|
candidateBranch: input.candidateBranch ?? defaultNapcatCandidateBranch,
|
||||||
|
forkBranch: input.forkBranch ?? 'dev',
|
||||||
|
forkRepo: input.forkRepo ?? defaultNapcatForkRepo,
|
||||||
|
lastAcceptedUpstreamBase:
|
||||||
|
input.lastAcceptedUpstreamBase ?? 'LAST_ACCEPTED_UPSTREAM_BASE',
|
||||||
|
upstreamReleaseTag: input.upstreamReleaseTag ?? 'UPSTREAM_RELEASE_REF',
|
||||||
|
upstreamRepo: input.upstreamRepo ?? defaultNapcatUpstreamRepo,
|
||||||
|
workspaceRoot: input.workspaceRoot ?? defaultNapcatWorkspaceRoot,
|
||||||
|
};
|
||||||
|
|
||||||
|
return buildNapcatPromptSkeleton({
|
||||||
|
artifactSlug: 'sync-candidate-review',
|
||||||
|
commands: [
|
||||||
|
{
|
||||||
|
command: 'git status --short --branch',
|
||||||
|
description: 'Confirm the candidate worktree state before review.',
|
||||||
|
readOnly: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command:
|
||||||
|
'git log --oneline --decorate -n 20 "${CANDIDATE_BRANCH:-napcat-upstream-sync/candidate}"',
|
||||||
|
description: 'Inspect recent candidate commits without changing refs.',
|
||||||
|
readOnly: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command:
|
||||||
|
'git diff --stat "${LAST_ACCEPTED_UPSTREAM_BASE:-origin/main}..${CANDIDATE_BRANCH:-napcat-upstream-sync/candidate}"',
|
||||||
|
description: 'Summarize candidate file churn for human review.',
|
||||||
|
readOnly: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
context,
|
||||||
|
input,
|
||||||
|
notes: [
|
||||||
|
'Use this skeleton after upstream audit classifies a candidate as safe enough for human review.',
|
||||||
|
],
|
||||||
|
promptName: 'sync-candidate-review',
|
||||||
|
title: 'NapCat sync candidate review',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a dry-run prompt skeleton for checking NapCat runtime release readiness.
|
||||||
|
* @param input - Image/profile hints and repo refs used to prepare readiness context; execution is never performed in Task 5.
|
||||||
|
* @returns Prompt, context, artifacts, and bounded read-only command hints for readiness review.
|
||||||
|
*/
|
||||||
|
export function buildNapcatRuntimeReleaseReadiness(
|
||||||
|
input: NapcatRuntimeReleaseReadinessInput,
|
||||||
|
) {
|
||||||
|
const context = {
|
||||||
|
apiImageTag: input.apiImageTag ?? 'main-<jenkins-build>',
|
||||||
|
candidateBranch: input.candidateBranch ?? defaultNapcatCandidateBranch,
|
||||||
|
forkRepo: input.forkRepo ?? defaultNapcatForkRepo,
|
||||||
|
napcatImageTag: input.napcatImageTag ?? 'kt-napcat-desktop-cn:<profile>',
|
||||||
|
profile: input.profile ?? 'desktop-cn',
|
||||||
|
upstreamReleaseTag: input.upstreamReleaseTag ?? 'UPSTREAM_RELEASE_REF',
|
||||||
|
workspaceRoot: input.workspaceRoot ?? defaultNapcatWorkspaceRoot,
|
||||||
|
};
|
||||||
|
|
||||||
|
return buildNapcatPromptSkeleton({
|
||||||
|
artifactSlug: 'runtime-release-readiness',
|
||||||
|
commands: [
|
||||||
|
{
|
||||||
|
command: 'git status --short --branch',
|
||||||
|
description: 'Confirm local release metadata is not mixed with unrelated changes.',
|
||||||
|
readOnly: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command:
|
||||||
|
'docker image inspect "${NAPCAT_IMAGE_TAG:-kt-napcat-desktop-cn:desktop-cn}"',
|
||||||
|
description: 'Read the NAS-local NapCat image metadata without pulling or building.',
|
||||||
|
readOnly: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command:
|
||||||
|
'curl -fsS --max-time 8 "${API_HEALTH_URL:-http://127.0.0.1:48085/health/runtime}"',
|
||||||
|
description: 'Read the API runtime health endpoint before any release promotion.',
|
||||||
|
readOnly: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
context,
|
||||||
|
input,
|
||||||
|
notes: [
|
||||||
|
'Deployment and Kubernetes evidence must be collected by the existing deploy-observation workflow after explicit human approval.',
|
||||||
|
],
|
||||||
|
promptName: 'runtime-release-readiness',
|
||||||
|
title: 'NapCat runtime release readiness',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a dry-run prompt skeleton for handing NapCat automation work to a trusted remote Codex workspace.
|
||||||
|
* @param input - Fork/ref hints, remote target label, and artifact location for the handoff packet.
|
||||||
|
* @returns Prompt, context, artifacts, and read-only local command hints for a remote handoff.
|
||||||
|
*/
|
||||||
|
export function buildNapcatRemoteDevHandoff(
|
||||||
|
input: NapcatRemoteDevHandoffInput,
|
||||||
|
) {
|
||||||
|
const context = {
|
||||||
|
candidateBranch: input.candidateBranch ?? defaultNapcatCandidateBranch,
|
||||||
|
forkRepo: input.forkRepo ?? defaultNapcatForkRepo,
|
||||||
|
targetHost: input.targetHost ?? 'nas',
|
||||||
|
upstreamReleaseTag: input.upstreamReleaseTag ?? 'UPSTREAM_RELEASE_REF',
|
||||||
|
workspaceRoot: input.workspaceRoot ?? defaultNapcatWorkspaceRoot,
|
||||||
|
};
|
||||||
|
|
||||||
|
return buildNapcatPromptSkeleton({
|
||||||
|
artifactSlug: 'remote-dev-handoff',
|
||||||
|
commands: [
|
||||||
|
{
|
||||||
|
command: 'git rev-parse HEAD',
|
||||||
|
description: 'Capture the local ktWorkflow commit used to prepare the handoff.',
|
||||||
|
readOnly: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command: 'git status --short --branch',
|
||||||
|
description: 'Confirm no unrelated local state enters the handoff packet.',
|
||||||
|
readOnly: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
context,
|
||||||
|
input,
|
||||||
|
notes: [
|
||||||
|
'The returned context is for a future remote session only; this tool does not spawn a subagent or run Codex.',
|
||||||
|
],
|
||||||
|
promptName: 'remote-dev-handoff',
|
||||||
|
title: 'NapCat remote dev handoff',
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Builds a non-mutating NAS Codex bootstrap plan placeholder for later automation tasks.
|
||||||
|
* @param input - Trusted NAS workspace/Codex-home hints and optional service name; execution is intentionally not implemented in Task 5.
|
||||||
|
* @returns A dry-run bootstrap checklist and read-only verification commands.
|
||||||
|
*/
|
||||||
|
export function buildNasCodexBootstrapPlan(input: NasCodexBootstrapPlanInput) {
|
||||||
|
const artifactRoot =
|
||||||
|
input.artifactRoot?.trim() ||
|
||||||
|
`${napcatAutomationSkeletonArtifactRoot}/nas-codex-bootstrap`;
|
||||||
|
const context = {
|
||||||
|
codexHome: input.codexHome ?? '/vol1/docker/kt-codex/home/.codex',
|
||||||
|
serviceName: input.serviceName ?? 'kt-codex-automation',
|
||||||
|
workspaceRoot: input.workspaceRoot ?? defaultNapcatWorkspaceRoot,
|
||||||
|
};
|
||||||
|
const requestedExecute = input.execute === true;
|
||||||
|
|
||||||
|
return {
|
||||||
|
artifacts: [
|
||||||
|
{
|
||||||
|
path: joinNapcatArtifactPath(artifactRoot, 'nas-codex-bootstrap-plan.md'),
|
||||||
|
type: 'markdown',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: joinNapcatArtifactPath(artifactRoot, 'nas-codex-bootstrap-context.json'),
|
||||||
|
type: 'json',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
commands: [
|
||||||
|
{
|
||||||
|
command: 'ssh nas "command -v codex && codex --version"',
|
||||||
|
description: 'Read the trusted NAS Codex CLI version without installing or changing files.',
|
||||||
|
readOnly: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
command:
|
||||||
|
'ssh nas "test -d /vol1/docker/kt-codex/workspace/KT && git -C /vol1/docker/kt-codex/workspace/KT status --short --branch"',
|
||||||
|
description: 'Read the trusted NAS KT workspace state without syncing or committing.',
|
||||||
|
readOnly: true,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
context,
|
||||||
|
execute: false,
|
||||||
|
notes: [
|
||||||
|
'Task 5 only exposes a dry-run bootstrap plan. Task 6 must implement real NAS bootstrap logic before any mutation is allowed.',
|
||||||
|
requestedExecute
|
||||||
|
? 'execute=true was requested, but no bootstrap mutation is performed by this placeholder.'
|
||||||
|
: 'execute defaults to false.',
|
||||||
|
],
|
||||||
|
requestedExecute,
|
||||||
|
summary: 'NAS Codex bootstrap dry-run plan generated.',
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Quotes a POSIX shell path argument for NAS-side Codex automation commands.
|
* Quotes a POSIX shell path argument for NAS-side Codex automation commands.
|
||||||
|
|||||||
34
src/types.ts
34
src/types.ts
@ -189,6 +189,40 @@ export interface NapcatDeviceProfileCheckInput {
|
|||||||
project?: string;
|
project?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface NapcatPromptToolInput {
|
||||||
|
artifactRoot?: string;
|
||||||
|
candidateBranch?: string;
|
||||||
|
execute?: boolean;
|
||||||
|
forkBranch?: string;
|
||||||
|
forkRepo?: string;
|
||||||
|
upstreamReleaseTag?: string;
|
||||||
|
upstreamRepo?: string;
|
||||||
|
useCodex?: boolean;
|
||||||
|
workspaceRoot?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NapcatSyncCandidateReviewInput extends NapcatPromptToolInput {
|
||||||
|
lastAcceptedUpstreamBase?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NapcatRuntimeReleaseReadinessInput extends NapcatPromptToolInput {
|
||||||
|
apiImageTag?: string;
|
||||||
|
napcatImageTag?: string;
|
||||||
|
profile?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NapcatRemoteDevHandoffInput extends NapcatPromptToolInput {
|
||||||
|
targetHost?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface NasCodexBootstrapPlanInput {
|
||||||
|
artifactRoot?: string;
|
||||||
|
codexHome?: string;
|
||||||
|
execute?: boolean;
|
||||||
|
serviceName?: string;
|
||||||
|
workspaceRoot?: string;
|
||||||
|
}
|
||||||
|
|
||||||
export interface BlockerResolutionInput {
|
export interface BlockerResolutionInput {
|
||||||
attempts?: number;
|
attempts?: number;
|
||||||
command?: string;
|
command?: string;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user