@xulthekl/team-flow 0.30.0 → 0.31.0

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 (40) hide show
  1. package/.claude/always/phase-guard.md +1 -1
  2. package/.claude-plugin/marketplace.json +1 -1
  3. package/.claude-plugin/plugin.json +2 -2
  4. package/.codex-plugin/plugin.json +1 -1
  5. package/.cursor-plugin/marketplace.json +1 -1
  6. package/.cursor-plugin/plugin.json +1 -1
  7. package/.github/plugin/marketplace.json +2 -2
  8. package/AGENTS.md +2 -1
  9. package/CHANGELOG.md +38 -0
  10. package/GEMINI.md +1 -1
  11. package/INSTALL.md +1 -1
  12. package/README.md +2 -2
  13. package/agents/build-executor.md +1 -0
  14. package/docs/README_en.md +1 -1
  15. package/gemini-extension.json +1 -1
  16. package/hooks/session-start +2 -2
  17. package/llms.txt +1 -1
  18. package/package.json +1 -1
  19. package/plugin.json +2 -2
  20. package/scripts/guard/checks/test-matrix-complete.mjs +67 -0
  21. package/scripts/guard/guard.mjs +5 -1
  22. package/scripts/lib/cmd-state.mjs +9 -4
  23. package/scripts/lib/hash.mjs +11 -0
  24. package/scripts/lib/state-loader.mjs +12 -1
  25. package/scripts/lib/test-matrix-export.mjs +231 -0
  26. package/scripts/lib/test-merge.mjs +540 -0
  27. package/scripts/team-flow.mjs +6 -0
  28. package/skills/build-executor/implementer-prompt.md +38 -3
  29. package/skills/code-reviewer/SKILL.md +28 -1
  30. package/skills/code-reviewer/code-reviewer-prompt.md +10 -0
  31. package/skills/contract-builder/SKILL.md +76 -0
  32. package/skills/release-archivist/SKILL.md +35 -2
  33. package/skills/spec-writer/SKILL.md +3 -1
  34. package/skills/test-strategy/SKILL.md +70 -0
  35. package/skills/test-strategy/references/adversarial-patterns.md +0 -0
  36. package/skills/test-strategy/references/complexity-grading.md +137 -0
  37. package/skills/test-strategy/references/design-methods-detail.md +183 -0
  38. package/skills/workflow-orchestrator/references/s1-path-router.md +4 -0
  39. package/skills/workflow-start/references/routing-rules.md +17 -0
  40. package/tests/lib/cmd-install-workbuddy.test.mjs +1 -1
@@ -0,0 +1,231 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * test-matrix-export — glaf4 test-matrix.json → team-flow test-matrix.md 格式转换
4
+ *
5
+ * v0.12 §46.1 定义,v0.31.0 实现为 CLI 子命令
6
+ * 用法:tf test-matrix-export <input.json> <output.md> [--change-id <id>] [--batch-id <id>]
7
+ *
8
+ * 功能:
9
+ * - 读取 glaf4 的 test-matrix.json
10
+ * - 保留全部 14 字段 + 包装 team_flow_context
11
+ * - 从 test_kind 派生 test_tier(unit/integration)
12
+ * - 输出 team-flow 格式的 test-matrix.md
13
+ */
14
+
15
+ import { readFileSync, writeFileSync } from 'node:fs';
16
+ import { resolve, basename } from 'node:path';
17
+
18
+ /**
19
+ * 解析 CLI 参数
20
+ */
21
+ function parseArgv(argv) {
22
+ const parsed = { _: [] };
23
+ for (let i = 0; i < argv.length; i++) {
24
+ if (argv[i] === '--change-id' && argv[i + 1]) {
25
+ parsed.changeId = argv[++i];
26
+ } else if (argv[i] === '--batch-id' && argv[i + 1]) {
27
+ parsed.batchId = argv[++i];
28
+ } else if (argv[i] === '--ledger-ref' && argv[i + 1]) {
29
+ parsed.ledgerRef = argv[++i];
30
+ } else if (!argv[i].startsWith('--')) {
31
+ parsed._.push(argv[i]);
32
+ }
33
+ }
34
+ return parsed;
35
+ }
36
+
37
+ /**
38
+ * test_kind → test_tier 映射(v0.12 §42.4)
39
+ */
40
+ const TEST_KIND_TO_TIER = {
41
+ // unit tier
42
+ pure_unit: 'unit',
43
+ mockito_unit: 'unit',
44
+ spring_assisted_unit: 'unit',
45
+ vitest_unit: 'unit',
46
+ vue_test_utils_mount: 'unit',
47
+ // integration tier
48
+ api_mockmvc_standalone: 'integration',
49
+ api_mockmvc_slice: 'integration',
50
+ api_mockmvc_boot: 'integration',
51
+ service_social: 'integration',
52
+ repository_h2: 'integration',
53
+ rabbitmq_social: 'integration',
54
+ redis_social: 'integration',
55
+ external_api_stub: 'integration',
56
+ test_infrastructure: 'integration',
57
+ };
58
+
59
+ /**
60
+ * 从 glaf4 case 派生 test_tier
61
+ */
62
+ function deriveTestTier(testKind) {
63
+ return TEST_KIND_TO_TIER[testKind] || 'unit';
64
+ }
65
+
66
+ /**
67
+ * 转义 markdown 表格单元格
68
+ */
69
+ function escapeCell(str) {
70
+ if (typeof str !== 'string') return String(str ?? '');
71
+ return str.replace(/\|/g, '\\|').replace(/\n/g, ' ');
72
+ }
73
+
74
+ /**
75
+ * 转换单个 case 为 markdown 行
76
+ */
77
+ function caseToRow(c) {
78
+ const tier = deriveTestTier(c.test_kind);
79
+ return [
80
+ c.case_id || '',
81
+ escapeCell(c.behavior || c.display_name || ''),
82
+ c.design_method || '',
83
+ escapeCell(typeof c.input === 'object' ? JSON.stringify(c.input) : (c.input || '')),
84
+ escapeCell(typeof c.expected === 'object' ? JSON.stringify(c.expected) : (c.expected || '')),
85
+ c.test_kind || '',
86
+ tier,
87
+ escapeCell(Array.isArray(c.required_stubs) ? c.required_stubs.join(', ') : (c.mock || '')),
88
+ c.work_mode || 'TDD',
89
+ c.test_file || '',
90
+ c.test_method_name || '',
91
+ c.run_command || '',
92
+ ].join(' | ');
93
+ }
94
+
95
+ /**
96
+ * 提取模块名(从 target 或 case_id 前缀)
97
+ */
98
+ function extractModuleName(matrix) {
99
+ if (matrix.target) {
100
+ // e.g., "com.example.service.OrderService" → "OrderService"
101
+ const parts = matrix.target.split('.');
102
+ return parts[parts.length - 1];
103
+ }
104
+ // fallback: from first case_id
105
+ if (matrix.cases?.[0]?.case_id) {
106
+ const id = matrix.cases[0].case_id;
107
+ const match = id.match(/^([A-Z][a-zA-Z]+)/);
108
+ if (match) return match[1];
109
+ }
110
+ return 'Unknown';
111
+ }
112
+
113
+ /**
114
+ * 转换 glaf4 matrix → team-flow markdown
115
+ */
116
+ export function convert(matrix, options = {}) {
117
+ const moduleName = extractModuleName(matrix);
118
+ const cases = matrix.cases || [];
119
+
120
+ // 统计
121
+ const unitCases = cases.filter(c => deriveTestTier(c.test_kind) === 'unit');
122
+ const integrationCases = cases.filter(c => deriveTestTier(c.test_kind) === 'integration');
123
+
124
+ // 提取 complexity
125
+ const complexity = matrix.target_complexity || {};
126
+ let complexityTier = 'medium';
127
+ if (complexity.is_trivial) complexityTier = 'trivial';
128
+ else if ((complexity.public_methods || 0) > 15 || (complexity.lines || 0) > 800 || (complexity.param_count || 0) > 6) {
129
+ complexityTier = 'complex';
130
+ }
131
+
132
+ // 提取 candidate ledger
133
+ const candidateLedger = matrix.source_candidate_ledger || [];
134
+
135
+ // 构建 markdown
136
+ const lines = [];
137
+ lines.push(`# Test Matrix — ${options.changeId || 'unknown'}`);
138
+ lines.push('');
139
+ lines.push('## Summary');
140
+ lines.push(`- Total cases: ${cases.length}`);
141
+ lines.push(`- Modules covered: 1 (${moduleName}, complexity: ${complexityTier})`);
142
+ lines.push(`- Unit cases: ${unitCases.length} (${Math.round(unitCases.length / cases.length * 100) || 0}%)`);
143
+ lines.push(`- Integration cases: ${integrationCases.length} (${Math.round(integrationCases.length / cases.length * 100) || 0}%)`);
144
+ lines.push(`- Deferred items: ${candidateLedger.filter(c => c.decision === 'deferred').length}`);
145
+ lines.push(`- Matrix revision: 1`);
146
+ lines.push('');
147
+
148
+ // Candidate Coverage Ledger
149
+ if (candidateLedger.length > 0) {
150
+ lines.push('## Candidate Coverage Ledger');
151
+ lines.push('');
152
+ lines.push('| candidate | category | decision | case_ids | reason |');
153
+ lines.push('|---|---|---|---|---|');
154
+ for (const c of candidateLedger) {
155
+ lines.push(`| ${escapeCell(c.candidate)} | ${c.category || ''} | ${c.decision} | ${escapeCell(Array.isArray(c.case_ids) ? c.case_ids.join(', ') : (c.case_ids || ''))} | ${escapeCell(c.reason || '')} |`);
156
+ }
157
+ lines.push('');
158
+ }
159
+
160
+ // Cases
161
+ lines.push('## Cases');
162
+ lines.push('');
163
+ lines.push(`### ${moduleName}`);
164
+ lines.push('');
165
+ lines.push('| case_id | behavior | design_method | input | expected | test_kind | test_tier | mock | work_mode | test_file | test_method_name | run_command |');
166
+ lines.push('|---|---|---|---|---|---|---|---|---|---|---|---|');
167
+ for (const c of cases) {
168
+ lines.push(`| ${caseToRow(c)} |`);
169
+ }
170
+ lines.push('');
171
+
172
+ // Deferred Items
173
+ const deferred = candidateLedger.filter(c => c.decision === 'deferred');
174
+ if (deferred.length > 0) {
175
+ lines.push('## Deferred Items');
176
+ lines.push('');
177
+ lines.push('| case_id | behavior | design_method | reason | deferred_since |');
178
+ lines.push('|---|---|---|---|---|');
179
+ for (const d of deferred) {
180
+ lines.push(`| ${escapeCell(Array.isArray(d.case_ids) ? d.case_ids[0] : (d.case_ids || ''))} | ${escapeCell(d.candidate)} | — | ${escapeCell(d.reason || '')} | ${new Date().toISOString().slice(0, 10)} |`);
181
+ }
182
+ lines.push('');
183
+ }
184
+
185
+ // team_flow_context footer
186
+ lines.push('---');
187
+ lines.push('');
188
+ lines.push('<!-- team_flow_context:');
189
+ lines.push(` change_id: ${options.changeId || 'unknown'}`);
190
+ if (options.batchId) lines.push(` batch_id: ${options.batchId}`);
191
+ lines.push(` closing_stage: test-merge`);
192
+ if (options.ledgerRef) lines.push(` ledger_ref: ${options.ledgerRef}`);
193
+ lines.push(` source: glaf4-tests-export`);
194
+ lines.push('-->');
195
+
196
+ return lines.join('\n') + '\n';
197
+ }
198
+
199
+ /**
200
+ * CLI entry point
201
+ */
202
+ export async function run(args) {
203
+ const argv = parseArgv(args || process.argv.slice(2));
204
+ const inputFile = argv._[0];
205
+ const outputFile = argv._[1];
206
+
207
+ if (!inputFile) {
208
+ console.error('Usage: tf test-matrix-export <input.json> <output.md> [--change-id <id>] [--batch-id <id>]');
209
+ process.exit(2);
210
+ }
211
+
212
+ if (!outputFile) {
213
+ console.error('Usage: tf test-matrix-export <input.json> <output.md>');
214
+ process.exit(2);
215
+ }
216
+
217
+ try {
218
+ const raw = readFileSync(resolve(inputFile), 'utf-8');
219
+ const matrix = JSON.parse(raw);
220
+ const md = convert(matrix, {
221
+ changeId: argv.changeId || basename(inputFile, '.json'),
222
+ batchId: argv.batchId,
223
+ ledgerRef: argv.ledgerRef,
224
+ });
225
+ writeFileSync(resolve(outputFile), md, 'utf-8');
226
+ console.log(`✅ test-matrix-export: ${inputFile} → ${outputFile}`);
227
+ } catch (err) {
228
+ console.error(`❌ test-matrix-export error: ${err.message}`);
229
+ process.exit(1);
230
+ }
231
+ }