agentic-flow-kit 0.2.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (56) hide show
  1. package/README.md +3 -2
  2. package/modules/gates/dotnet-ca/check-architecture.sh +14 -10
  3. package/modules/hosts/trae/commands/wf-build.md +14 -14
  4. package/modules/hosts/trae/commands/wf-deploy.md +13 -13
  5. package/modules/hosts/trae/commands/wf-design.md +13 -13
  6. package/modules/hosts/trae/commands/wf-maintain.md +13 -13
  7. package/modules/hosts/trae/commands/wf-new-task.md +13 -13
  8. package/modules/hosts/trae/commands/wf-plan.md +13 -13
  9. package/modules/hosts/trae/commands/wf-review.md +13 -13
  10. package/modules/hosts/trae/commands/wf-test.md +14 -14
  11. package/modules/hosts/trae/hooks/post-edit-check.cjs +10 -0
  12. package/modules/hosts/trae/hooks/pre-shell-check.cjs +14 -4
  13. package/modules/hosts/trae/hooks/resolve-shell.cjs +30 -21
  14. package/modules/hosts/trae/rules/beautify-input-chinese.md +6 -6
  15. package/modules/hosts/trae/rules/git-commit-message.md +10 -10
  16. package/package.json +10 -2
  17. package/src/add-gate.mjs +8 -1
  18. package/src/add-host.mjs +4 -0
  19. package/src/cli.mjs +4 -3
  20. package/src/doctor.mjs +9 -2
  21. package/src/render.mjs +2 -0
  22. package/src/sync.mjs +20 -4
  23. package/src/sync.test.mjs +40 -1
  24. package/templates/AGENTS.md +2 -2
  25. package/templates/_agents/board/index.html +382 -382
  26. package/templates/_agents/board/marked.min.js +6 -6
  27. package/templates/_agents/cache/kb-index.json +1 -1
  28. package/templates/_agents/commands/build.md +1 -1
  29. package/templates/_agents/commands/design.md +1 -1
  30. package/templates/_agents/commands/maintain.md +1 -1
  31. package/templates/_agents/commands/new-task.md +73 -73
  32. package/templates/_agents/commands/plan.md +2 -2
  33. package/templates/_agents/commands/test.md +1 -1
  34. package/templates/_agents/hooks/check-pairing-incremental.sh +30 -29
  35. package/templates/_agents/hooks/commit-check.cjs +6 -1
  36. package/templates/_agents/scripts/agg-delegations.cjs +35 -18
  37. package/templates/_agents/scripts/agg-delegations.test.mjs +95 -0
  38. package/templates/_agents/scripts/check-loop.sh +6 -0
  39. package/templates/_agents/scripts/check-loop.test.sh +1042 -1042
  40. package/templates/_agents/scripts/commit-check-trigger.test.mjs +94 -0
  41. package/templates/_agents/scripts/ensure-board.ps1 +1 -1
  42. package/templates/_agents/scripts/gen-wiki-board.mjs +13 -3
  43. package/templates/_agents/scripts/gen-wiki-board.test.mjs +22 -0
  44. package/templates/_agents/scripts/gen-workflow-index.mjs +7 -2
  45. package/templates/_agents/scripts/gen-workflow-index.test.mjs +27 -0
  46. package/templates/_agents/scripts/gen-workflow-metrics.mjs +17 -3
  47. package/templates/_agents/scripts/gen-workflow-metrics.test.mjs +21 -0
  48. package/templates/_agents/scripts/kb-search.mjs +2 -1
  49. package/templates/_agents/scripts/rule-budget.sh +8 -2
  50. package/templates/_agents/scripts/verify-wiki-consistency.mjs +6 -1
  51. package/templates/_agents/scripts/workflow-board-server.mjs +245 -244
  52. package/templates/_githooks/commit-msg +4 -2
  53. package/templates/_githooks/pre-push +32 -32
  54. package/templates/workflow/README.md +2 -2
  55. package/templates/workflow/intents/_TEMPLATE.md +40 -40
  56. package/templates/workflow/specs/_TEMPLATE.md +41 -41
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  /* agg-delegations: 量化证据聚合
3
- * 读 workflow/delegations.md 两张结果表(委派结果 / 自做任务结果)+ 扫 workflow/incidents/ 按月计数,
3
+ * 读 workflow/delegations.md 两张结果表(按表头签名识别:委派表含「被委派方」列、自做表=日期+任务一句话;不依赖 ## 节标题)+ 扫 workflow/incidents/ 按月计数,
4
4
  * 输出各月指标(一次通过率 / 平均返工次数 / 主兜底占比)、并发扩容门判定、可粘贴的月度快照行。
5
5
  * 结果取值:一次通过 | 返工×N | 主兜底 | 返工待修(未闭环,不计入率)。
6
6
  * 用法:node .agents/scripts/agg-delegations.cjs [--month=YYYY-MM](默认输出全部月份 + 全量累计)
@@ -26,8 +26,7 @@ function parseTableRows(sectionText) {
26
26
  .split(/\r?\n/)
27
27
  .filter((l) => /^\|/.test(l.trim()))
28
28
  .map((l) => l.trim().replace(/^\|/, '').replace(/\|$/, '').split('|').map((c) => c.trim()))
29
- .filter((cols) => cols.length >= 2 && !/^[-: ]+$/.test(cols[1])) // 去表头分隔行
30
- .filter((cols) => !/^\s*$/.test(cols[0]) && /^\d{4}-\d{2}-\d{2}$/.test(cols[0])); // 只留数据行(首列是日期)
29
+ .filter((cols) => !/^\s*$/.test(cols[0]) && /^\d{4}-\d{2}-\d{2}$/.test(cols[0])); // 只留数据行(首列是日期;表头/分隔行随之滤除)
31
30
  }
32
31
 
33
32
  function parseResult(raw) {
@@ -40,29 +39,43 @@ function parseResult(raw) {
40
39
  return { kind: 'unknown', rework: 0 };
41
40
  }
42
41
 
43
- function readLedger() {
44
- if (!fs.existsSync(LEDGER)) fail(`找不到 ${LEDGER}`);
45
- const text = fs.readFileSync(LEDGER, 'utf8');
46
- const sections = {};
42
+ // splitTables:按表头签名识别两张结果表——节标题只服务人类阅读,表头才是数据边界
43
+ // (2026-09-24 修复:曾按 `## 委派结果` 节标题定位,节结构漂移时已有记录被静默读成「台账为空」)
44
+ function splitTables(text) {
45
+ const tables = { delegated: [], self: [] };
47
46
  let cur = null;
48
47
  for (const line of text.split(/\r?\n/)) {
49
- const h = line.match(/^## (.+)$/);
50
- if (h) cur = h[1].trim();
51
- if (cur) (sections[cur] = sections[cur] || []).push(line);
48
+ if (!/^\s*\|/.test(line)) { cur = null; continue; } // 表体边界:连续 | 行;断开即出表
49
+ const cells = line.trim().replace(/^\|/, '').replace(/\|$/, '').split('|').map((c) => c.trim());
50
+ if (cells.some((c) => c.startsWith('被委派方'))) { cur = 'delegated'; continue; } // 委派表表头
51
+ if (cells[0] === '日期' && cells.some((c) => c.startsWith('任务一句话'))) { cur = 'self'; continue; } // 自做表表头(委派表已被上一行截走)
52
+ if (cur) tables[cur].push(line); // 表体行(含分隔行——parseTableRows 会滤掉)
52
53
  }
53
- const delegated = parseTableRows((sections['委派结果'] || []).join('\n')).map((c) => ({
54
+ return tables;
55
+ }
56
+
57
+ function readLedger() {
58
+ if (!fs.existsSync(LEDGER)) fail(`找不到 ${LEDGER}`);
59
+ const text = fs.readFileSync(LEDGER, 'utf8');
60
+ const tables = splitTables(text);
61
+ const delegated = parseTableRows(tables.delegated.join('\n')).map((c) => ({
54
62
  scope: '委派',
55
63
  date: c[0],
56
64
  actor: c[1],
57
65
  result: c[3] || '',
58
66
  }));
59
- const selfDone = parseTableRows((sections['自做任务结果'] || []).join('\n')).map((c) => ({
67
+ const selfDone = parseTableRows(tables.self.join('\n')).map((c) => ({
60
68
  scope: '自做',
61
69
  date: c[0],
62
70
  actor: '主智能体',
63
71
  result: c[2] || '',
64
72
  }));
65
- return delegated.concat(selfDone);
73
+ const rows = delegated.concat(selfDone);
74
+ // 零数据行时区分「台账真空」与「结构漂移」:有日期开头的数据行却识别不到表 → fail-loud,不再静默「台账为空」
75
+ if (!rows.length && /^\s*\|\s*\d{4}-\d{2}-\d{2}\s*\|/m.test(text)) {
76
+ fail(`台账结构漂移:${LEDGER} 存在日期开头的数据行但未识别到结果表(委派表表头须含「被委派方」列,自做表表头须为「日期 | 任务一句话 | 结果 | 备注」)`);
77
+ }
78
+ return rows;
66
79
  }
67
80
 
68
81
  function countIncidents() {
@@ -79,8 +92,11 @@ function countIncidents() {
79
92
  const GATE = { minSample: 20, minPassRate: 0.9, maxAvgRework: 0.3, minDelegated: 5, maxFallbackRate: 0.1 };
80
93
 
81
94
  function metrics(rows) {
82
- const valid = rows.filter((r) => parseResult(r.result).kind !== 'pending' && parseResult(r.result).kind !== 'unknown');
83
- const pending = rows.length - valid.length;
95
+ // 未知结果(结果列拼错/漏填)单列计数,不混进「待修」——待修=未闭环,未知=记录本身有问题(2026-09-24 口径分离)
96
+ const kinds = rows.map((r) => parseResult(r.result).kind);
97
+ const valid = rows.filter((_, i) => kinds[i] !== 'pending' && kinds[i] !== 'unknown');
98
+ const pending = kinds.filter((k) => k === 'pending').length;
99
+ const unknown = kinds.filter((k) => k === 'unknown').length;
84
100
  let pass = 0, reworkSum = 0, fallback = 0;
85
101
  for (const r of valid) {
86
102
  const p = parseResult(r.result);
@@ -92,6 +108,7 @@ function metrics(rows) {
92
108
  return {
93
109
  total: valid.length,
94
110
  pending,
111
+ unknown,
95
112
  pass,
96
113
  passRate: valid.length ? pass / valid.length : null,
97
114
  reworkSum,
@@ -152,16 +169,16 @@ function main() {
152
169
  const m = metrics(byMonth[ym]);
153
170
  const g = gate(m, incidents[ym] || 0);
154
171
  console.log(`## ${ym}`);
155
- console.log(` 有效任务 ${m.total}(待修 ${m.pending})|一次通过 ${m.pass}(${fmtRate(m.passRate)})|返工总次数 ${m.reworkSum}(平均 ${fmtNum(m.avgRework)})|主兜底 ${m.fallback}/${m.delegatedValid}(${fmtRate(m.fallbackRate)})|incident ${incidents[ym] || 0} 起`);
172
+ console.log(` 有效任务 ${m.total}(待修 ${m.pending}${m.unknown ? `、未知结果 ${m.unknown}` : ''})|一次通过 ${m.pass}(${fmtRate(m.passRate)})|返工总次数 ${m.reworkSum}(平均 ${fmtNum(m.avgRework)})|主兜底 ${m.fallback}/${m.delegatedValid}(${fmtRate(m.fallbackRate)})|incident ${incidents[ym] || 0} 起`);
156
173
  for (const i of g.items) console.log(` 门${i.no} ${i.ok === null ? '[人工]' : i.ok ? '✅' : '❌'} ${i.desc}`);
157
174
  console.log(` 扩容门判定:${g.verdict}\n`);
158
- snapshotRows.push(`| ${ym} | ${m.total} | ${fmtRate(m.passRate)} | ${fmtNum(m.avgRework)} | ${fmtRate(m.fallbackRate)} | ${incidents[ym] || 0} | ${g.verdict} | 样本含待修${m.pending} |`);
175
+ snapshotRows.push(`| ${ym} | ${m.total} | ${fmtRate(m.passRate)} | ${fmtNum(m.avgRework)} | ${fmtRate(m.fallbackRate)} | ${incidents[ym] || 0} | ${g.verdict} | 样本含待修${m.pending}${m.unknown ? `、未知${m.unknown}` : ''} |`);
159
176
  }
160
177
 
161
178
  if (!argMonth && rows.length) {
162
179
  const m = metrics(rows);
163
180
  console.log(`## 全量累计`);
164
- console.log(` 有效任务 ${m.total}(待修 ${m.pending})|一次通过 ${fmtRate(m.passRate)}|平均返工 ${fmtNum(m.avgRework)}|主兜底 ${fmtRate(m.fallbackRate)}|incident 合计 ${Object.values(incidents).reduce((a, b) => a + b, 0)} 起\n`);
181
+ console.log(` 有效任务 ${m.total}(待修 ${m.pending}${m.unknown ? `、未知结果 ${m.unknown}` : ''})|一次通过 ${fmtRate(m.passRate)}|平均返工 ${fmtNum(m.avgRework)}|主兜底 ${fmtRate(m.fallbackRate)}|incident 合计 ${Object.values(incidents).reduce((a, b) => a + b, 0)} 起\n`);
165
182
  }
166
183
 
167
184
  console.log(`## 可粘贴快照行(粘到 workflow/delegations.md §月度聚合快照)`);
@@ -0,0 +1,95 @@
1
+ #!/usr/bin/env node
2
+ // agg-delegations.cjs 的 fixture 驱动测试(2026-09-24 随「台账结构漂移静默吞数据」修复引入)
3
+ // 被测脚本以 __dirname 定位台账,故复制脚本进临时根(<tmp>/.agents/scripts/)后以 cwd=<tmp> 运行。
4
+ // 断言:①节标题缺失但表头合规 → 数据照常入账(本次事故形态回归);②日期数据行在但表头不可识别 → exit 1 fail-loud;
5
+ // ③空台账(表头合规、无数据行)→ exit 0 且提示为空。
6
+ // 用法:node .agents/scripts/agg-delegations.test.mjs(在仓库任意目录执行均可)
7
+ import fs from 'node:fs';
8
+ import os from 'node:os';
9
+ import path from 'node:path';
10
+ import { spawnSync } from 'node:child_process';
11
+ import { fileURLToPath } from 'node:url';
12
+
13
+ const SCRIPT_DIR = path.dirname(fileURLToPath(import.meta.url));
14
+ const SRC = path.join(SCRIPT_DIR, 'agg-delegations.cjs');
15
+ const USAGE = '用法:node .agents/scripts/agg-delegations.test.mjs';
16
+
17
+ let pass = 0;
18
+ let fail = 0;
19
+ const check = (desc, cond, detail = '') => {
20
+ if (cond) { pass++; console.log(`PASS ${desc}`); }
21
+ else { fail++; console.log(`FAIL ${desc}${detail ? `\n${detail}` : ''}`); }
22
+ };
23
+
24
+ const mkfix = (ledgerText) => {
25
+ const root = fs.mkdtempSync(path.join(os.tmpdir(), 'agg-delegations-test-'));
26
+ fs.mkdirSync(path.join(root, '.agents', 'scripts'), { recursive: true });
27
+ fs.mkdirSync(path.join(root, 'workflow', 'incidents'), { recursive: true });
28
+ fs.writeFileSync(path.join(root, 'workflow', 'delegations.md'), ledgerText, 'utf8');
29
+ fs.copyFileSync(SRC, path.join(root, '.agents', 'scripts', 'agg-delegations.cjs'));
30
+ return root;
31
+ };
32
+ const runAgg = (root) => spawnSync(process.execPath, [path.join(root, '.agents', 'scripts', 'agg-delegations.cjs')], { cwd: root, encoding: 'utf8' });
33
+
34
+ // ---- 场景 1:无 `## ` 节标题、表头合规(2026-09-24 事故形态)→ 数据照常入账 ----
35
+ {
36
+ const root = mkfix([
37
+ '# 量化证据台账(fixture)',
38
+ '',
39
+ '| 日期 | 被委派方(模型) | 任务一句话 | 结果 | 备注 |',
40
+ '|------|----------------|------------|------|------|',
41
+ '| 2026-09-23 | general-purpose(子代理) | 示例委派任务 | 一次通过 | fixture |',
42
+ '| 2026-09-24 | executor(子代理) | 结果列拼错的示例行 | 成功 | fixture |',
43
+ '',
44
+ ].join('\n'));
45
+ const r = runAgg(root);
46
+ check('场景 1:节标题缺失 + 表头合规 → exit 0', r.status === 0, `exit=${r.status}\n${r.stdout}${r.stderr}`);
47
+ check('场景 1:数据行入账(有效任务 1,非「台账为空」)', /有效任务 1(/.test(r.stdout) && !r.stdout.includes('台账为空'), r.stdout);
48
+ check('场景 1:未知结果单列计数(不混入待修,2026-09-24 口径分离)', r.stdout.includes('待修 0、未知结果 1'), r.stdout);
49
+ fs.rmSync(root, { recursive: true, force: true });
50
+ }
51
+
52
+ // ---- 场景 2:日期数据行在但表头不可识别 → fail-loud(不再静默「台账为空」)----
53
+ {
54
+ const root = mkfix([
55
+ '# 量化证据台账(fixture)',
56
+ '',
57
+ '| when | who | what | result | note |',
58
+ '|------|-----|------|--------|------|',
59
+ '| 2026-09-23 | executor | 表头被改的行 | 一次通过 | fixture |',
60
+ '',
61
+ ].join('\n'));
62
+ const r = runAgg(root);
63
+ check('场景 2:表头不可识别 → exit 1', r.status === 1, `exit=${r.status}\n${r.stdout}${r.stderr}`);
64
+ check('场景 2:报错点名结构漂移', (r.stderr || '').includes('结构漂移'), r.stderr);
65
+ fs.rmSync(root, { recursive: true, force: true });
66
+ }
67
+
68
+ // ---- 场景 3:空台账(表头合规、无数据行)→ exit 0 且提示为空 ----
69
+ {
70
+ const root = mkfix([
71
+ '# 量化证据台账(fixture)',
72
+ '',
73
+ '## 委派结果',
74
+ '',
75
+ '| 日期 | 被委派方(模型) | 任务一句话 | 结果 | 备注 |',
76
+ '|------|----------------|------------|------|------|',
77
+ '',
78
+ '## 自做任务结果',
79
+ '',
80
+ '| 日期 | 任务一句话 | 结果 | 备注 |',
81
+ '|------|------------|------|------|',
82
+ '',
83
+ ].join('\n'));
84
+ const r = runAgg(root);
85
+ check('场景 3:空台账 → exit 0', r.status === 0, `exit=${r.status}\n${r.stdout}${r.stderr}`);
86
+ check('场景 3:提示台账为空', r.stdout.includes('台账为空'), r.stdout);
87
+ fs.rmSync(root, { recursive: true, force: true });
88
+ }
89
+
90
+ console.log(`\n合计: PASS ${pass} / FAIL ${fail}`);
91
+ if (fail) {
92
+ console.log(`\n${USAGE}`);
93
+ process.exit(1);
94
+ }
95
+ process.exit(0);
@@ -25,6 +25,8 @@
25
25
  # 上限=瘦身后实测+余量,新增须先删除或下沉——一进一出)
26
26
  # 14. 新 done 的 spec/plan 须在 git 历史里出现过 `状态: approved`(确认环节留痕,2026-09-22 papercut;
27
27
  # 生效 2026-09-23=规则发布次日,不追溯发布当日已在途文档,口径同检查 12)[warning]
28
+ # 2026-09-23 裁定演进(源自 Shipyard 回流):本检查恒为 advisory,永不升级 hard-block;确认后主智能体须立即
29
+ # docs(*) 提交 approved 留痕(根 AGENTS.md 提交约定),本警告即「遗漏提交」提醒——直跳 done 无留痕仍属流程瑕疵,仅提示
28
30
  #
29
31
  # 文档协议(2026-09-13 结构化改造,papercut #4 防复发):
30
32
  # 状态/级别/日期/发现/流程/确认结果/确认时间 等机器字段一律由文件头 YAML frontmatter(受限子集)承载:
@@ -763,6 +765,8 @@ fi
763
765
  # --- 14. 新 done 的 spec/plan 须在 git 历史里出现过 approved(确认环节留痕)[warning] ---
764
766
  # 规则生效 2026-09-22(papercut 2026-09-22,用户点名):`approved` 是确认环节的机器可见态,`done` 只在关单出现;
765
767
  # 从 draft 直跳 done 会让确认环节在文档里消失——而检查 5 视 approved/done 同权,此前无任何门禁能发现跳态。
768
+ # 2026-09-23 裁定演进(源自 Shipyard 回流):此警告恒为 advisory、永不升级 hard-block;确认后 docs(*) 提交留痕为必做
769
+ # (根 AGENTS.md 提交约定),本警告即「遗漏提交」提醒。备注含「存量确认态豁免(…)」声明者出账(口径同检查 8)。
766
770
  # 作用域:日期 ≥ 2026-09-23(生效次日,不追溯发布当日已在途的并行文档;存量同理——避免对无法诚实补正的旧件刷屏,
767
771
  # 口径同检查 12:frontmatter 日期/发现 → 文件名前缀回退)。
768
772
  # 判据:当前状态为 done 的文件,须在 git 历史中至少有一个提交版本的**行首**出现过 `状态: approved`
@@ -781,6 +785,8 @@ if git rev-parse --git-dir >/dev/null 2>&1 && git rev-parse -q --verify HEAD >/d
781
785
  2026-09-2[3-9]|2026-09-[3-9][0-9]|2026-1[0-2]-[0-3][0-9]|202[7-9]-*|20[3-9][0-9]-*|2[1-9][0-9][0-9]-*) ;;
782
786
  *) continue ;;
783
787
  esac
788
+ # 出账:备注含「存量确认态豁免(…)」声明者跳过(口径同检查 8 的存量对账豁免;留痕规则生效前的存量 done 件)
789
+ [ -n "$(grep -m1 '存量确认态豁免(' "$f" 2>/dev/null)" ] && continue
784
790
  [ -n "$(git log -1 --format=%H -G'^状态:[[:space:]]*approved' -- "$f" 2>/dev/null)" ] && continue
785
791
  warnings="$warnings
786
792
  - [WARN 确认态缺失] $base 状态已 done 但 git 历史中从未出现行首「状态: approved」——确认环节未留痕(draft 直跳 done)"