feat: 升级KT工作流能力

This commit is contained in:
sunlei 2026-06-04 13:30:25 +08:00
parent d813dc69ab
commit ad7ee6d1b0
10 changed files with 177 additions and 29 deletions

View File

@ -12,15 +12,15 @@
- 生成任务收尾包:把状态、验证计划、可选验证执行、全局 review 和历史清理收成一份结果。
- 生成页面测试用例:内置“先写用例、可视化证据、事不过三”的测试闭环。
- 生成接口测试计划:接口改动后输出真实调用命令和统一返回结构断言。
- 生成业务链路测试计划:固化 Admin 登录、博客 CRUD、QQBot 扫码/自动回复、Web/Playground 回跳。
- 生成业务链路测试计划:固化 Admin 登录、博客 CRUD、QQBot 扫码/自动回复、更新登录 SSE、FFLogs 命令、系统日志可视化、Web/Playground 回跳。
- 生成多仓库提交/推送计划:按仓库分组、建议提交信息、列出提交和推送前检查。
- 生成远程只读健康检查和数据库同步安全向导:覆盖飞牛 NAS 服务探测、GTID、备份和行数校验。
- 生成专项组件工作流KtTable、BlogArgon、AdminAuth、QQBot、Knife4jSwagger 的防踩坑清单和验证点。
- 生成专项组件工作流KtTable、BlogArgon、AdminAuth、QQBot、FF14Plugin、NapCatLogin、SystemLog、Knife4jSwagger 的防踩坑清单和验证点。
- 生成验证进程清理计划:按项目路径和端口给出 PowerShell 检查命令,不直接杀进程。
- 清理历史产物:统一治理 `.kt-workspace` 下的测试/验证产物,按目录最近修改时间只保留最近 3 轮,模板目录永久保留。
- 检查 env 策略和变更风险:区分后端真实 env 与前端客户端 `.env*`,提醒锁文件、核心表格组件、部署链路和 Vue TSX 插槽写法等高风险改动。
- 全局 CodeReview 只读扫描:汇总全部 KT 子仓库的 Git 状态、敏感文件跟踪、冲突标记、运行时调试输出、疑似凭据字面量、根目录生成产物和当前变更风险;任何文件改动后都要跑一遍。
- 生成或写入 `TASKS.md` 改动记录:默认 `dryRun=true`,确认后再落盘。
- 全局 CodeReview 只读扫描:汇总全部 KT 子仓库的 Git 状态、敏感文件跟踪、冲突标记、运行时调试输出、疑似凭据字面量、根目录生成产物和当前变更风险;默认只对变更文件做内容扫描,避免历史误报污染上下文;任何文件改动后都要跑一遍。
- 生成或写入 `TASKS.md` 最近记录:默认 `dryRun=true`,确认后再落盘。
- 生成提交前检查清单:校验 KT commit message 约定。
## 安装
@ -74,15 +74,15 @@ pnpm run admin-login -- --url http://127.0.0.1:5999/#/auth/login
| `kt_cleanup_process_plan` | 生成验证进程清理检查命令 |
| `kt_env_policy` | 检查 env 文件现状和提交策略 |
| `kt_risk_scan` | 扫描当前或传入变更文件的偏差风险 |
| `kt_global_code_review` | 对 KT 全部子仓库做只读全局 CodeReview 扫描 |
| `kt_global_code_review` | 对 KT 全部子仓库做只读全局 CodeReview 扫描,默认仅深扫变更文件 |
| `kt_finish_task` | 生成任务收尾包,可选执行验证和历史清理 |
| `kt_commit_plan` | 生成多仓库提交计划、建议 commit message 和检查项 |
| `kt_push_plan` | 生成多仓库推送计划和远程异常提醒 |
| `kt_business_test_plan` | 生成固化业务链路测试计划 |
| `kt_business_test_plan` | 生成固化业务链路测试计划,包含 QQBot SSE、FFLogs 和系统日志 |
| `kt_remote_health_check` | 生成或执行远程只读健康检查命令 |
| `kt_db_sync_plan` | 生成数据库同步安全向导 |
| `kt_component_workflow` | 输出专项组件/链路防踩坑工作流 |
| `kt_append_task_record` | 预览或写入 `TASKS.md` 改动记录 |
| `kt_append_task_record` | 预览或写入 `TASKS.md` 最近记录 |
| `kt_commit_checklist` | 生成提交前检查清单并校验 commit message |
## 可复用脚本
@ -90,7 +90,7 @@ 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 global-review` | 对 KT 全部子仓库做只读全局 CodeReview 扫描,输出 JSON 复审报告。 |
| `pnpm run global-review` | 对 KT 全部子仓库做只读全局 CodeReview 扫描,默认仅对变更文件做内容深扫,输出 JSON 复审报告。 |
## 项目别名
@ -122,10 +122,10 @@ pnpm run admin-login -- --url http://127.0.0.1:5999/#/auth/login
- 要收尾时调用 `kt_finish_task`,默认只生成计划和 review需要执行验证时显式传 `runValidation=true`
- 文件改动完成并验证后调用 `kt_global_code_review`,或运行 `pnpm run global-review`;它只读扫描,不删除文件、不提交代码。
- 要提交或推送时先调用 `kt_commit_plan` / `kt_push_plan`,按仓库分组确认范围。
- 要测真实业务链路时调用 `kt_business_test_plan`,选择 `admin-login`、`qqbot-auto-reply` 等 flow。
- 要测真实业务链路时调用 `kt_business_test_plan`,选择 `admin-login`、`qqbot-auto-reply`、`qqbot-login-sse`、`fflogs-command`、`system-log-visualization` 等 flow。
- 远程服务排查先调用 `kt_remote_health_check`,默认只生成只读命令;需要执行时显式传 `execute=true`
- 数据库同步前调用 `kt_db_sync_plan`,先确认源库、目标库、备份库和校验点。
- 改 KtTable、BlogArgon、AdminAuth、QQBot、Knife4jSwagger 前调用 `kt_component_workflow`,先看专项禁区。
- 改 KtTable、BlogArgon、AdminAuth、QQBot、FF14Plugin、NapCatLogin、SystemLog、Knife4jSwagger 前调用 `kt_component_workflow`,先看专项禁区。
- 不确定任务边界时调用 `kt_guardrails`,先拿到“能做什么、不能做什么、怎么验证”。
- 验证前调用 `kt_suggest_verification`,避免盲跑全量构建。
- 页面测试前调用 `kt_create_page_test_case`,再执行 Playwright/浏览器测试。

View File

@ -1,6 +1,6 @@
{
"name": "@kt/mcp-kt-workflow",
"version": "0.5.0",
"version": "0.6.0",
"private": true,
"type": "module",
"description": "Reusable MCP workflow tools for the KT workspace.",

View File

@ -205,8 +205,9 @@ export function registerTools(server: McpServer): void {
server.registerTool(
'kt_global_code_review',
{
description: '对 KT 已知子仓库做只读全局 CodeReview 扫描Git 状态、tracked env/deploy/secrets、冲突标记、调试输出、疑似凭据字面量和当前变更风险。',
description: '对 KT 已知子仓库做只读全局 CodeReview 扫描Git 状态、tracked env/deploy/secrets、冲突标记、调试输出、疑似凭据字面量和当前变更风险。默认只对变更文件做内容扫描,避免历史误报污染上下文。',
inputSchema: {
contentScanMode: z.enum(['changed', 'all']).default('changed'),
includeContentScan: z.boolean().default(true),
includeRootScan: z.boolean().default(true),
maxFindingsPerProject: z.number().int().min(1).max(200).default(20),
@ -224,6 +225,7 @@ export function registerTools(server: McpServer): void {
inputSchema: {
changeType: z.enum(taskTypeValues).default('general'),
cleanupHistory: z.boolean().default(false),
contentScanMode: z.enum(['changed', 'all']).default('changed'),
includeRootScan: z.boolean().default(true),
keepHistory: z.number().int().min(0).max(50).default(3),
maxFindingsPerProject: z.number().int().min(1).max(200).default(20),
@ -274,8 +276,11 @@ export function registerTools(server: McpServer): void {
.enum([
'admin-login',
'blog-crud',
'fflogs-command',
'qqbot-account-scan',
'qqbot-auto-reply',
'qqbot-login-sse',
'system-log-visualization',
'web-playground-auth',
'all',
])
@ -319,10 +324,19 @@ export function registerTools(server: McpServer): void {
server.registerTool(
'kt_component_workflow',
{
description: '按 KT 专项组件/链路输出防踩坑工作流KtTable、BlogArgon、AdminAuth、QQBot、Knife4jSwagger。',
description: '按 KT 专项组件/链路输出防踩坑工作流KtTable、BlogArgon、AdminAuth、QQBot、FF14Plugin、NapCatLogin、SystemLog、Knife4jSwagger。',
inputSchema: {
target: z
.enum(['AdminAuth', 'BlogArgon', 'Knife4jSwagger', 'KtTable', 'QQBot'])
.enum([
'AdminAuth',
'BlogArgon',
'FF14Plugin',
'Knife4jSwagger',
'KtTable',
'NapCatLogin',
'QQBot',
'SystemLog',
])
.default('KtTable'),
},
title: 'KT Component Workflow',

View File

@ -38,13 +38,13 @@ export async function runSelfTest(): Promise<void> {
projects: ['mcp'],
}),
businessTestPlan: buildBusinessTestPlan({
flow: 'qqbot-auto-reply',
flow: 'system-log-visualization',
}),
commitPlan: await buildCommitPlan({
projects: ['mcp'],
}),
componentWorkflow: buildComponentWorkflow({
target: 'Knife4jSwagger',
target: 'SystemLog',
}),
dbSyncPlan: buildDbSyncPlan({}),
finishTask: await buildFinishTask({

View File

@ -127,6 +127,51 @@ export async function scanTaskRisk(input: RiskScanInput): Promise<Record<string,
addRisk('medium', file, '后端接口或业务逻辑改动。', '本地启动或复用服务后真实调用对应接口。');
}
if (/src\/common\/decorators\/format-date-time\.decorator\.ts|src\/.*\.(entity|dto)\.ts/.test(normalized)) {
addRisk(
'medium',
file,
'后端时间字段或 DTO/Entity 响应结构改动。',
'确认数据源经 DTO/Entity 实例返回FormatDateTime 不做递归响应遍历,时间统一 YYYY-MM-DD HH:mm:ss。',
);
}
if (/src\/common\/logger\/|src\/admin\/system-log\/|pino|loki|system-log/i.test(normalized)) {
addRisk(
'medium',
file,
'Pino/Loki 或系统日志链路改动。',
'验证 API 日志写入、/system/logs 系列接口、Admin 系统日志页面和 Loki ready/query_range。',
);
}
if (/src\/qqbot\/plugins\/(ff14Market|fflogs)\//i.test(normalized)) {
addRisk(
'medium',
file,
'FF14/FFLogs 命令插件改动。',
'确认中文查询和回显走字典映射,命令注册表同步,真实命令输出字段完整。',
);
}
if (/src\/qqbot\/(account|napcat|connection)\//i.test(normalized)) {
addRisk(
'medium',
file,
'QQBot/NapCat 登录链路改动。',
'验证新增账号和更新登录都能生成有效二维码SSE 进度可见,旧 isLogin 状态不会短路。',
);
}
if (/apps\/web-antdv-next\/src\/views\/system\/(dict|log)\//.test(normalized)) {
addRisk(
'medium',
file,
'Admin 系统管理页改动。',
'验证菜单权限、KtTable 展示、列表筛选和对应 API 响应结构。',
);
}
if (/Jenkinsfile|deploy\/|k8s\/|nginx.*\.conf/.test(normalized)) {
addRisk('medium', file, '部署链路配置改动。', '只验证配置语法或生成产物,不主动触发远程部署。');
}

View File

@ -66,6 +66,7 @@ interface ReviewFinding {
interface ReviewProjectResult {
changedFiles: string[];
contentScannedFiles: string[];
credentialHints: ReviewFinding[];
debugFindings: ReviewFinding[];
findings: ReviewFinding[];
@ -109,11 +110,16 @@ function isBenignCredentialValue(value: string): boolean {
if (!normalized) return true;
if (normalized.startsWith('$') || normalized.startsWith('${')) return true;
if (normalized.startsWith('<') || normalized.includes('example')) return true;
if (/^\*+$/.test(normalized)) return true;
return [
'false',
'hidden',
'masked',
'null',
'password',
'please-replace-me-with-your-own-key',
'redacted',
'replace-me',
'secret',
'token',
'true',
@ -138,6 +144,7 @@ function isCredentialHintAllowed(file: string, line: string): boolean {
const normalized = file.replaceAll('\\', '/');
const trimmed = line.trimStart();
if (trimmed.startsWith('<')) return true;
if (/\b(url|uri|dsn)\.password\s*=\s*['"]\*+['"]/.test(trimmed)) return true;
return /(^|\/)views\/(demos|examples)\//.test(normalized);
}
@ -274,7 +281,9 @@ function findRootGeneratedArtifacts(): string[] {
async function reviewProject(
projectKey: string,
input: Required<Pick<GlobalCodeReviewInput, 'includeContentScan' | 'maxFindingsPerProject'>>,
input: Required<
Pick<GlobalCodeReviewInput, 'contentScanMode' | 'includeContentScan' | 'maxFindingsPerProject'>
>,
): Promise<ReviewProjectResult> {
const project = resolveProject(projectKey);
const repoType = detectRepoType(project.path);
@ -284,6 +293,14 @@ async function reviewProject(
: { ok: false, stderr: '', stdout: '' };
const changedFiles = status.ok ? parseGitStatusFiles(status.stdout) : [];
const trackedFiles = repoType === 'git' ? await listGitTrackedFiles(project.path) : [];
const trackedFileSet = new Set(trackedFiles);
const contentScannedFiles =
input.contentScanMode === 'all'
? trackedFiles
: changedFiles
.map((file) => file.replaceAll('\\', '/'))
.filter((file) => shouldReadReviewFile(file))
.filter((file) => trackedFileSet.has(file) || existsSync(path.join(project.path, file)));
const trackedSensitiveFiles = trackedFiles.filter(
(file) =>
sensitiveTrackedFilePattern.test(file) &&
@ -291,7 +308,7 @@ async function reviewProject(
);
const contentFindings = collectProjectContentFindings(
project,
trackedFiles,
contentScannedFiles,
input.includeContentScan,
input.maxFindingsPerProject,
);
@ -308,6 +325,7 @@ async function reviewProject(
return {
changedFiles,
contentScannedFiles,
credentialHints: contentFindings.credentialHints,
debugFindings: contentFindings.debugFindings,
findings: [...trackedSensitiveFindings, ...contentFindings.findings],
@ -330,6 +348,7 @@ export async function buildGlobalCodeReview(
? input.projects!
: [...defaultReviewProjects];
const includeContentScan = input.includeContentScan ?? true;
const contentScanMode = input.contentScanMode ?? 'changed';
const maxFindingsPerProject = Math.max(
1,
Math.min(input.maxFindingsPerProject ?? 20, 200),
@ -337,6 +356,7 @@ export async function buildGlobalCodeReview(
const projectResults = await Promise.all(
projects.map((project) =>
reviewProject(project, {
contentScanMode,
includeContentScan,
maxFindingsPerProject,
}),
@ -377,11 +397,13 @@ export async function buildGlobalCodeReview(
notes: [
'本工具只读扫描,不会修改文件、删除文件或提交代码。',
'P1 优先处理:冲突标记、被跟踪的真实 env/部署凭据文件。',
'默认 contentScanMode=changed只对当前变更文件做 P2/P3 内容扫描,避免历史误报污染上下文;需要全仓库深扫时传 all。',
'P2 需要人工确认:疑似凭据字面量、旧部署文件、调试断点。',
'P3 为清洁度问题:运行时 console.log 等。',
],
projects: projectResults.map((item) => ({
changedFiles: item.changedFiles,
contentScannedFileCount: item.contentScannedFiles.length,
credentialHintCount: item.credentialHints.length,
debugFindingCount: item.debugFindings.length,
findingCount: item.findings.length,
@ -394,6 +416,7 @@ export async function buildGlobalCodeReview(
summary: {
changedProjectCount: projectResults.filter((item) => item.changedFiles.length > 0)
.length,
contentScanMode,
findingCount: allFindings.length,
p1: allFindings.filter((item) => item.level === 'P1').length,
p2: allFindings.filter((item) => item.level === 'P2').length,

View File

@ -38,7 +38,7 @@ export async function prepareTask(input: PrepareTaskInput): Promise<Record<strin
'按最小范围修改。',
'按 verification.commands 和 notes 做轻量验证。',
'有文件改动时更新 TASKS.md。',
'最终回复或提交前跑全局 review,并处理全部 findings。',
'最终回复或提交前跑全局 review;默认只深扫变更文件,真实 findings 先处理。',
],
pageTestCase,
project,
@ -66,21 +66,22 @@ export function buildTaskRecord(input: TaskRecordInput): string {
const date = input.date || formatDateInShanghai();
const scope = (input.scope?.length ?? 0) > 0 ? input.scope!.join('、') : '待补充';
const content = input.content || '待补充';
const testCase = input.testCase || '本次无页面测试或待补充。';
const verification = input.verification || '待补充。';
return `### ${date}${input.title}\n\n- 范围:${scope}\n- 内容:${content}\n- 页面测试用例:${testCase}\n- 验证:${verification}\n`;
const optionalTestCase = input.testCase ? `- 页面测试用例:${input.testCase}\n` : '';
return `### ${date}${input.title}\n\n- 范围:${scope}\n- 关键词:${content}\n${optionalTestCase}- 验证:${verification}\n`;
}
export function appendTaskRecord(input: TaskRecordInput): Record<string, unknown> {
const record = buildTaskRecord(input);
const tasksPath = resolveInsideRoot('TASKS.md');
const current = readFileSync(tasksPath, 'utf8');
const marker = '## 改动记录';
const marker = current.includes('## 最近记录') ? '## 最近记录' : '## 改动记录';
const markerIndex = current.indexOf(marker);
if (markerIndex === -1) {
throw new Error('TASKS.md does not contain ## 改动记录');
throw new Error('TASKS.md does not contain ## 最近记录 or ## 改动记录');
}
const insertAt = current.indexOf('\n', markerIndex);

View File

@ -102,6 +102,17 @@ export function buildBusinessTestPlan(input: BusinessTestPlanInput = {}): Record
projects: ['api', 'admin', 'blog'],
steps: ['登录 Admin', '新增分类/标签', '新增文章并绑定分类标签', '编辑后查询列表', '清理 KT_TEST_ 数据'],
},
'fflogs-command': {
assertions: [
'命令注册表和在线命令表都能看到 FFLogs 查询命令。',
'中文任务名可查询,回显使用本地化任务名和字段名。',
'最近 10 次 logs 返回颜色、输出评分、治疗评分、dps、adps、rdps、ndps、hps。',
'FF14 查价和 FFLogs 映射优先来自字典表,不在插件代码硬编码。',
],
preflight: ['API 可达', 'FFLogs client id/secret 由环境变量注入', '字典表存在 FFLogs/FF14Market 映射项', 'QQBot 测试账号在线'],
projects: ['api', 'admin'],
steps: ['确认字典映射', '执行 /logs 角色 服务器 中文高难任务', '查询命令日志', '确认失败场景返回可读中文错误'],
},
'qqbot-account-scan': {
assertions: [
'扫码创建返回 qrcode/containerName/webuiPort。',
@ -112,6 +123,17 @@ export function buildBusinessTestPlan(input: BusinessTestPlanInput = {}): Record
projects: ['api', 'admin'],
steps: ['打开账号连接页', '点击扫码新增账号', '轮询二维码状态', '确认账号回填', '测试删除联动容器'],
},
'qqbot-login-sse': {
assertions: [
'更新登录接口快速返回 pending session不被前端 HTTP 超时截断。',
'scan/events SSE 能看到重置登录态、生成二维码、等待扫码、成功或错误等步骤。',
'账号被其他终端踢下线后,更新登录不复用旧二维码或旧 isLogin 状态。',
'手动刷新二维码不超时,且不会无意义重启容器。',
],
preflight: ['API 可达', 'NapCat 容器可达', '账号处于离线或可重置状态', 'Admin QQBot 账号页面可访问'],
projects: ['api', 'admin'],
steps: ['触发更新登录', '订阅 scan/events', '观察二维码刷新', '扫码或等待过期', '核对账号状态和容器状态'],
},
'qqbot-auto-reply': {
assertions: [
'消息日志能看到 OneBot 收到的私聊/群聊/频道消息。',
@ -122,6 +144,17 @@ export function buildBusinessTestPlan(input: BusinessTestPlanInput = {}): Record
projects: ['api', 'admin'],
steps: ['创建 KT_TEST_ 自动回复规则', '发送私聊测试消息', '查询消息日志', '查询发送日志', '清理测试规则'],
},
'system-log-visualization': {
assertions: [
'API pino 日志能写入 Loki 或在 Loki 不可用时不阻塞业务。',
'/system/logs、/system/logs/summary、/system/logs/status 返回 Vben 兼容结构。',
'Admin 系统日志页面能查询、筛选、刷新,控制台无 error。',
'时间字段统一显示为 YYYY-MM-DD HH:mm:ss。',
],
preflight: ['API 可达', 'Loki /ready 返回 200 或降级状态明确', 'Admin 系统日志菜单可见', 'K8s Secret/Jenkins env 已注入 LOKI_*'],
projects: ['api', 'admin'],
steps: ['触发一条 API 日志', '查询 Loki query_range', '打开 Admin #/system/logs', '执行筛选刷新', '核对 summary/status'],
},
'web-playground-auth': {
assertions: [
'无 token 时不主动 refresh不产生登录循环。',

View File

@ -95,6 +95,7 @@ export async function buildFinishTask(input: FinishTaskInput = {}): Promise<Reco
}
const review = await buildGlobalCodeReview({
contentScanMode: input.contentScanMode ?? 'changed',
includeContentScan: true,
includeRootScan: input.includeRootScan ?? true,
maxFindingsPerProject: input.maxFindingsPerProject ?? 20,
@ -116,7 +117,7 @@ export async function buildFinishTask(input: FinishTaskInput = {}): Promise<Reco
review,
snapshots,
stopConditions: [
'review.findings 非空时先修复,再复跑 kt_global_code_review。',
'review.findings 存在真实 P1/P2 时先修复,再复跑 kt_global_code_review。',
'validationRuns 中任一 result.ok=false 时先处理失败命令。',
'发现真实 env、生产 Secret 或非本次范围文件时停止提交。',
],
@ -139,7 +140,7 @@ export async function buildCommitPlan(input: CommitPlanInput = {}): Promise<Reco
'每个独立 Git 仓库单独提交。',
'提交前先跑对应轻量验证和 kt_global_code_review。',
'后端真实 .env.development/.env.production 不提交;前端 env 可提交但只能包含客户端公开配置。',
'根目录 AGENTS/SKILLS/TASKS 不属于子仓库,最终回复里单独说明。',
'KT 根目录是本地治理仓库;如没有 remote只本地提交并在最终回复说明。',
],
includePush: input.includePush ?? false,
projects: changedSnapshots.map((snapshot) => {
@ -313,10 +314,28 @@ export function buildComponentWorkflow(input: ComponentWorkflowInput = {}): Reco
verification: ['横向滚动性能', '纵向滚动表头对齐', '列宽/行高 resize', 'summary 固定底部', '宽屏无无意义横向滚动条'],
},
QQBot: {
antiPatterns: ['把 NapCat token 写进仓库', '删除账号不清理专属容器', '白名单/黑名单只做前端过滤', '远程服务不可达时伪装成业务成功'],
antiPatterns: ['把 NapCat token 写进仓库', '删除账号不清理专属容器', '白名单/黑名单只做前端过滤', '远程服务不可达时伪装成业务成功', '更新登录时用旧 isLogin 状态短路'],
files: ['Node/kt-template-online-api/src/qqbot', 'Vue/kt-template-admin/apps/web-antdv-next/src/views/qqbot', 'ci/fnos-k8s/deploy-qqbot-runtime.ps1'],
guardrails: ['OneBot 反向 WS token 校验', '权限名单底层先走 QQ号过滤', '群/频道精确 QQ 过滤由开关控制', '扫码取消/过期/失败清理未绑定容器'],
verification: ['账号在线', '扫码新增/更新登录', '自动回复规则命中', '权限名单三类过滤', '消息日志和发送日志闭环'],
guardrails: ['OneBot 反向 WS token 校验', '权限名单底层先走 QQ号过滤', '群/频道精确 QQ 过滤由开关控制', '扫码取消/过期/失败清理未绑定容器', '更新登录走 SSE 进度,后台重置登录态后再生成二维码'],
verification: ['账号在线', '扫码新增/更新登录', 'scan/events 每步进度', '自动回复规则命中', '权限名单三类过滤', '消息日志和发送日志闭环'],
},
FF14Plugin: {
antiPatterns: ['把 boss/服务器/职业映射硬编码到插件里', 'FFLogs 英文任务名直接回显给中文用户', '外部 API 失败时吞掉错误上下文', '字典缺失时返回空白结果'],
files: ['Node/kt-template-online-api/src/qqbot/plugins/ff14Market', 'Node/kt-template-online-api/src/qqbot/plugins/fflogs', 'Node/kt-template-online-api/src/admin/dict', 'Vue/kt-template-admin/apps/web-antdv-next/src/views/system/dict'],
guardrails: ['FF14 查价和 FFLogs 映射优先走字典表维护', '中文任务名查询和回显都要经过字典', '命令注册表和在线命令列表同步验证', '保留最近 logs 的颜色/评分/dps/adps/rdps/ndps/hps 字段语义'],
verification: ['/logs 角色 服务器 高难任务中文名', '/logs 角色 服务器 最近10次', 'FF14 查价中文物品名', '字典管理页面可维护映射'],
},
NapCatLogin: {
antiPatterns: ['更新登录复用已过期二维码 URL', '前端等待长接口导致超时', '手动刷新每次重启容器', '后端拿旧 login state 判断成功'],
files: ['Node/kt-template-online-api/src/qqbot/account', 'Node/kt-template-online-api/src/qqbot/napcat', 'Vue/kt-template-admin/apps/web-antdv-next/src/views/qqbot'],
guardrails: ['创建账号和更新登录共用同一套二维码生成事实源', '更新登录先 reset runtime login state', 'SSE 返回 step/status/qrcode/error', '容器重启只在必要时触发,优先直接通信 NapCat API'],
verification: ['账号被踢下线后点击更新登录', 'scan/events 能看到重置/生成二维码/等待扫码/成功或失败', '手动刷新不超时', '过期二维码不再复用'],
},
SystemLog: {
antiPatterns: ['把真实 Loki 凭据写入仓库', '只改 Dockerfile 不校验线上 env/Secret', '日志时间格式前后端不一致', 'global-review 把历史脱敏占位符当作真实凭据输出'],
files: ['Node/kt-template-online-api/src/common/logger', 'Node/kt-template-online-api/src/admin/system-log', 'Vue/kt-template-admin/apps/web-antdv-next/src/views/system/log', 'mcp/ktWorkflow/src/tools/review.ts'],
guardrails: ['pino + Loki env 走 Jenkins 私有 env 或 K8s Secret', '后端时间字段经 DTO/Entity 装饰器统一 YYYY-MM-DD HH:mm:ss', '系统日志页面走 /system/logs 系列接口', 'review 默认只对变更文件做 P2/P3 内容扫描'],
verification: ['API typecheck/lint/test', 'Loki /ready 和 query_range', 'Admin #/system/logs 无 console error', 'global-review findings 不含已知脱敏占位符误报'],
},
} satisfies Record<string, Record<string, unknown>>;

View File

@ -128,6 +128,7 @@ export interface RiskScanInput {
}
export interface GlobalCodeReviewInput {
contentScanMode?: 'all' | 'changed';
includeContentScan?: boolean;
includeRootScan?: boolean;
maxFindingsPerProject?: number;
@ -137,6 +138,7 @@ export interface GlobalCodeReviewInput {
export interface FinishTaskInput {
changeType?: TaskType;
cleanupHistory?: boolean;
contentScanMode?: 'all' | 'changed';
includeRootScan?: boolean;
keepHistory?: number;
maxFindingsPerProject?: number;
@ -159,8 +161,11 @@ export interface PushPlanInput {
export type BusinessFlow =
| 'admin-login'
| 'blog-crud'
| 'fflogs-command'
| 'qqbot-account-scan'
| 'qqbot-auto-reply'
| 'qqbot-login-sse'
| 'system-log-visualization'
| 'web-playground-auth';
export interface BusinessTestPlanInput {
@ -184,7 +189,15 @@ export interface DbSyncPlanInput {
}
export interface ComponentWorkflowInput {
target?: 'AdminAuth' | 'BlogArgon' | 'Knife4jSwagger' | 'KtTable' | 'QQBot';
target?:
| 'AdminAuth'
| 'BlogArgon'
| 'FF14Plugin'
| 'Knife4jSwagger'
| 'KtTable'
| 'NapCatLogin'
| 'QQBot'
| 'SystemLog';
}
export interface PrepareTaskInput extends GuardrailsInput {