@chenguangyao/devflow-kit 0.1.43

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 (198) hide show
  1. package/CHANGELOG.md +232 -0
  2. package/LICENSE +21 -0
  3. package/README.md +539 -0
  4. package/bin/devflow.js +9 -0
  5. package/docs/RFC-001-devflow-kit.md +617 -0
  6. package/docs/RFC-002-workflow-kernel.md +134 -0
  7. package/docs/enterprise-integration-supplement.md +274 -0
  8. package/docs/internal-gitlab-setup.md +426 -0
  9. package/docs/marketplace-skills.md +231 -0
  10. package/docs/migration-from-arb.md +232 -0
  11. package/docs/tooling-overview.md +774 -0
  12. package/docs/workflow-orchestration.md +695 -0
  13. package/docs/workflow-ui-prototype.html +271 -0
  14. package/package.json +52 -0
  15. package/schemas/config.schema.json +51 -0
  16. package/schemas/delta.schema.json +22 -0
  17. package/schemas/state.schema.json +130 -0
  18. package/schemas/status-surface.schema.json +197 -0
  19. package/schemas/workflow-confirmation-surface.schema.json +70 -0
  20. package/schemas/workflow-picker.schema.json +94 -0
  21. package/scripts/postinstall.js +101 -0
  22. package/scripts/render-workflow-ui-prototype.js +271 -0
  23. package/skills/apply/SKILL.md +313 -0
  24. package/skills/apply/references/discipline-checklist.md +145 -0
  25. package/skills/apply/references/subagent-implementer-prompt.md +113 -0
  26. package/skills/apply/references/subagent-orchestration.md +150 -0
  27. package/skills/apply/references/subagent-reviewer-prompt.md +180 -0
  28. package/skills/apply/references/tdd-loop.md +287 -0
  29. package/skills/apply/references/when-plan-is-wrong.md +279 -0
  30. package/skills/apply/references/worktree-swarm.md +292 -0
  31. package/skills/archive/SKILL.md +229 -0
  32. package/skills/archive/references/conflict-resolution.md +336 -0
  33. package/skills/archive/references/knowledge-deposit.md +381 -0
  34. package/skills/archive/references/spec-merge.md +365 -0
  35. package/skills/brainstorm/SKILL.md +123 -0
  36. package/skills/brainstorm/references/proposal-template.md +244 -0
  37. package/skills/brainstorm/references/question-catalog.md +168 -0
  38. package/skills/brainstorm/references/session-template.md +184 -0
  39. package/skills/ci-fix/SKILL.md +63 -0
  40. package/skills/ci-fix/references/loop.md +25 -0
  41. package/skills/code-review/SKILL.md +279 -0
  42. package/skills/code-review/references/escalation-playbook.md +192 -0
  43. package/skills/code-review/references/language-cheatsheets/go.md +175 -0
  44. package/skills/code-review/references/language-cheatsheets/java-spring-mybatis.md +246 -0
  45. package/skills/code-review/references/language-cheatsheets/python.md +170 -0
  46. package/skills/code-review/references/language-cheatsheets/vue.md +199 -0
  47. package/skills/code-review/references/output-template.md +275 -0
  48. package/skills/code-review/references/review-checklist.md +251 -0
  49. package/skills/complexity-grading/SKILL.md +259 -0
  50. package/skills/deliver/SKILL.md +271 -0
  51. package/skills/deliver/references/delivery-modes.md +299 -0
  52. package/skills/deliver/references/notify.md +359 -0
  53. package/skills/deliver/references/pr-description.md +319 -0
  54. package/skills/dependency-upgrade/SKILL.md +57 -0
  55. package/skills/dependency-upgrade/references/risk-matrix.md +38 -0
  56. package/skills/df-orchestrator/SKILL.md +407 -0
  57. package/skills/df-orchestrator/references/complexity-grading.md +177 -0
  58. package/skills/df-orchestrator/references/escalation-matrix.md +191 -0
  59. package/skills/df-orchestrator/references/routing-rules.md +290 -0
  60. package/skills/df-orchestrator/references/workflow-state-machine.md +208 -0
  61. package/skills/frontend-quality/SKILL.md +61 -0
  62. package/skills/frontend-quality/references/checklist.md +35 -0
  63. package/skills/handoff-resume/SKILL.md +59 -0
  64. package/skills/handoff-resume/references/handoff-template.md +54 -0
  65. package/skills/plan/SKILL.md +166 -0
  66. package/skills/plan/references/task-breakdown.md +207 -0
  67. package/skills/plan/references/task-sequencing.md +143 -0
  68. package/skills/plan/references/task-template.md +248 -0
  69. package/skills/requirement-analysis/SKILL.md +499 -0
  70. package/skills/requirement-analysis/references/acceptance-criteria.md +183 -0
  71. package/skills/requirement-analysis/references/code-recon.md +151 -0
  72. package/skills/requirement-analysis/references/edge-case-catalog.md +164 -0
  73. package/skills/requirement-analysis/references/requirement-template.md +339 -0
  74. package/skills/requirement-analysis/references/scope-negotiation.md +162 -0
  75. package/skills/security-hardening/SKILL.md +60 -0
  76. package/skills/security-hardening/references/checklist.md +42 -0
  77. package/skills/tech-spec/SKILL.md +388 -0
  78. package/skills/tech-spec/references/api-contract-design.md +172 -0
  79. package/skills/tech-spec/references/decision-records.md +110 -0
  80. package/skills/tech-spec/references/design-template.md +301 -0
  81. package/skills/tech-spec/references/rollout-and-rollback.md +203 -0
  82. package/skills/tech-spec/references/spec-delta-conventions.md +250 -0
  83. package/skills/tech-spec/references/transaction-patterns.md +212 -0
  84. package/skills/test-spec/SKILL.md +219 -0
  85. package/skills/test-spec/references/coverage-strategy.md +218 -0
  86. package/skills/test-spec/references/edge-case-to-test.md +143 -0
  87. package/skills/test-spec/references/test-case-template.md +276 -0
  88. package/skills/verify/SKILL.md +232 -0
  89. package/skills/verify/references/nfr-verification.md +292 -0
  90. package/skills/verify/references/report-templates.md +510 -0
  91. package/skills/verify/references/self-test-guide.md +240 -0
  92. package/skills/verify/references/verify-rollback-map.md +247 -0
  93. package/src/cli/commands/_helpers.js +108 -0
  94. package/src/cli/commands/_submit.js +718 -0
  95. package/src/cli/commands/apply.js +198 -0
  96. package/src/cli/commands/archive.js +180 -0
  97. package/src/cli/commands/checkpoint.js +113 -0
  98. package/src/cli/commands/deliver.js +377 -0
  99. package/src/cli/commands/deploy.js +504 -0
  100. package/src/cli/commands/design.js +158 -0
  101. package/src/cli/commands/disable.js +21 -0
  102. package/src/cli/commands/doctor.js +178 -0
  103. package/src/cli/commands/enable.js +21 -0
  104. package/src/cli/commands/flow.js +645 -0
  105. package/src/cli/commands/help.js +93 -0
  106. package/src/cli/commands/ingest.js +602 -0
  107. package/src/cli/commands/init.js +341 -0
  108. package/src/cli/commands/knowledge.js +523 -0
  109. package/src/cli/commands/logs.js +43 -0
  110. package/src/cli/commands/new.js +202 -0
  111. package/src/cli/commands/plan.js +49 -0
  112. package/src/cli/commands/propose.js +27 -0
  113. package/src/cli/commands/provider.js +698 -0
  114. package/src/cli/commands/report.js +143 -0
  115. package/src/cli/commands/requirement.js +227 -0
  116. package/src/cli/commands/review.js +301 -0
  117. package/src/cli/commands/skills.js +457 -0
  118. package/src/cli/commands/status.js +925 -0
  119. package/src/cli/commands/switch.js +27 -0
  120. package/src/cli/commands/sync.js +47 -0
  121. package/src/cli/commands/test.js +366 -0
  122. package/src/cli/commands/uninstall.js +32 -0
  123. package/src/cli/commands/update.js +74 -0
  124. package/src/cli/commands/verify.js +354 -0
  125. package/src/cli/commands/worktree.js +78 -0
  126. package/src/cli/index.js +72 -0
  127. package/src/cli/parse-args.js +102 -0
  128. package/src/core/autodetect.js +271 -0
  129. package/src/core/change.js +208 -0
  130. package/src/core/checkpoint.js +217 -0
  131. package/src/core/config.js +60 -0
  132. package/src/core/delta.js +290 -0
  133. package/src/core/markers.js +59 -0
  134. package/src/core/paths.js +173 -0
  135. package/src/core/plan-tasks.js +36 -0
  136. package/src/core/project-routing.js +285 -0
  137. package/src/core/projects.js +200 -0
  138. package/src/core/state.js +200 -0
  139. package/src/core/workflow-check.js +177 -0
  140. package/src/core/workflow-init.js +34 -0
  141. package/src/core/workflow-picker.js +154 -0
  142. package/src/core/workflow-policy.js +119 -0
  143. package/src/core/workflow-suggest.js +181 -0
  144. package/src/core/workflow-verify.js +88 -0
  145. package/src/core/workflow.js +433 -0
  146. package/src/core/worktree.js +241 -0
  147. package/src/knowledge/categories.js +107 -0
  148. package/src/knowledge/classify.js +125 -0
  149. package/src/knowledge/deposit.js +414 -0
  150. package/src/knowledge/migrate.js +149 -0
  151. package/src/knowledge/mr.js +219 -0
  152. package/src/knowledge/query.js +131 -0
  153. package/src/knowledge/registry.js +151 -0
  154. package/src/knowledge/sync.js +179 -0
  155. package/src/providers/base.js +74 -0
  156. package/src/providers/drivers/api-yapi.js +78 -0
  157. package/src/providers/drivers/ci-jenkins.js +109 -0
  158. package/src/providers/drivers/intake-confluence.js +544 -0
  159. package/src/providers/drivers/kb-git.js +549 -0
  160. package/src/providers/drivers/kb-weknora.js +472 -0
  161. package/src/providers/drivers/notify-smtp.js +515 -0
  162. package/src/providers/drivers/observability-oss.js +43 -0
  163. package/src/providers/drivers/observability-sls.js +50 -0
  164. package/src/providers/lifecycle.js +135 -0
  165. package/src/providers/loader.js +132 -0
  166. package/src/providers/local.js +190 -0
  167. package/src/providers/userconfig.js +283 -0
  168. package/src/reports/aggregate.js +185 -0
  169. package/src/reports/coverage.js +163 -0
  170. package/src/reports/detect.js +143 -0
  171. package/src/reports/parse.js +236 -0
  172. package/src/templates/files/ci/github.yml +38 -0
  173. package/src/templates/files/ci/gitlab.yml +27 -0
  174. package/src/templates/files/design.md +63 -0
  175. package/src/templates/files/ide/devflow-workflow.md +58 -0
  176. package/src/templates/files/ide/project-overview-reference.md +1 -0
  177. package/src/templates/files/ide/project-overview.md +27 -0
  178. package/src/templates/files/knowledge-index.json +17 -0
  179. package/src/templates/files/knowledge.md +28 -0
  180. package/src/templates/files/meta.json +8 -0
  181. package/src/templates/files/plan.md +38 -0
  182. package/src/templates/files/proposal.md +33 -0
  183. package/src/templates/files/reports/contract-test.md +40 -0
  184. package/src/templates/files/reports/e2e-test.md +30 -0
  185. package/src/templates/files/reports/integration-test.md +36 -0
  186. package/src/templates/files/reports/joint-test.md +58 -0
  187. package/src/templates/files/reports/perf.md +24 -0
  188. package/src/templates/files/reports/regression.md +20 -0
  189. package/src/templates/files/reports/remote-test.md +55 -0
  190. package/src/templates/files/reports/self-test.md +43 -0
  191. package/src/templates/files/reports/smoke-test.md +22 -0
  192. package/src/templates/files/reports/unit-test.md +36 -0
  193. package/src/templates/files/requirement.md +51 -0
  194. package/src/templates/files/review.md +38 -0
  195. package/src/templates/files/tests.md +36 -0
  196. package/src/templates/files/verify.md +32 -0
  197. package/src/templates/index.js +21 -0
  198. package/src/utils/log.js +37 -0
@@ -0,0 +1,143 @@
1
+ 'use strict';
2
+ const fs = require('fs');
3
+ const path = require('path');
4
+
5
+ /**
6
+ * Detect the test framework(s) for a given project.
7
+ *
8
+ * Returns an array of frameworks ordered by priority. Each entry:
9
+ * { framework, kind, command, coverageHint }
10
+ *
11
+ * framework: 'jest' | 'vitest' | 'mocha' | 'tap' | 'node-test' | 'go' | 'pytest' | 'unittest' | 'junit'
12
+ * kind: 'unit' (default) - we only suggest unit by default; integ/e2e usually have separate scripts
13
+ * command: the recommended invocation
14
+ * coverageHint: { format, file } - hint for the coverage parser
15
+ *
16
+ * Heuristic — light, no execution:
17
+ * - read package.json (deps, devDeps, scripts.test)
18
+ * - read go.mod
19
+ * - read pyproject.toml / setup.cfg / requirements*.txt
20
+ * - look for known config files (jest.config.*, vitest.config.*, pytest.ini)
21
+ */
22
+ function detect(root) {
23
+ const out = [];
24
+ const pkgJson = readJsonSafe(path.join(root, 'package.json'));
25
+ if (pkgJson) {
26
+ const deps = { ...(pkgJson.dependencies || {}), ...(pkgJson.devDependencies || {}) };
27
+ const scripts = pkgJson.scripts || {};
28
+ const has = (n) => deps[n] !== undefined;
29
+ const haveAny = (...names) => names.some(has);
30
+
31
+ if (has('vitest') || hasFile(root, /vitest\.config\.(c|m)?[jt]s$/)) {
32
+ out.push({
33
+ framework: 'vitest',
34
+ kind: 'unit',
35
+ command: scripts.test && /vitest/.test(scripts.test) ? 'npm test' : 'npx vitest run',
36
+ coverageHint: { format: 'json-summary', file: 'coverage/coverage-summary.json' },
37
+ });
38
+ }
39
+ if (has('jest') || hasFile(root, /jest\.config\.(c|m)?[jt]s$/)) {
40
+ out.push({
41
+ framework: 'jest',
42
+ kind: 'unit',
43
+ command: scripts.test && /jest/.test(scripts.test) ? 'npm test' : 'npx jest --ci',
44
+ coverageHint: { format: 'json-summary', file: 'coverage/coverage-summary.json' },
45
+ });
46
+ }
47
+ if (has('mocha') || hasFile(root, /\.mocharc\.(c?js|json|ya?ml)$/)) {
48
+ out.push({
49
+ framework: 'mocha',
50
+ kind: 'unit',
51
+ command: scripts.test && /mocha/.test(scripts.test) ? 'npm test' : 'npx mocha',
52
+ coverageHint: { format: 'lcov', file: 'coverage/lcov.info' },
53
+ });
54
+ }
55
+ if (has('tap') || haveAny('@tapjs/test', 'tap')) {
56
+ out.push({
57
+ framework: 'tap',
58
+ kind: 'unit',
59
+ command: scripts.test && /tap/.test(scripts.test) ? 'npm test' : 'npx tap',
60
+ coverageHint: null,
61
+ });
62
+ }
63
+ if (!out.length && scripts.test) {
64
+ // Generic node script fallback.
65
+ out.push({ framework: 'node-test', kind: 'unit', command: 'npm test', coverageHint: null });
66
+ }
67
+ }
68
+
69
+ if (fs.existsSync(path.join(root, 'go.mod'))) {
70
+ out.push({
71
+ framework: 'go',
72
+ kind: 'unit',
73
+ command: 'go test ./... -v',
74
+ coverageHint: { format: 'go-cover-out', file: 'coverage.out' },
75
+ });
76
+ }
77
+
78
+ if (
79
+ fs.existsSync(path.join(root, 'pyproject.toml')) ||
80
+ fs.existsSync(path.join(root, 'setup.cfg')) ||
81
+ fs.existsSync(path.join(root, 'pytest.ini')) ||
82
+ glob(root, /requirements.*\.txt$/).length
83
+ ) {
84
+ if (anyFileContains(root, ['pyproject.toml', 'setup.cfg', 'pytest.ini'], 'pytest') ||
85
+ glob(root, /test_.*\.py$/).length || glob(root, /.*_test\.py$/).length) {
86
+ out.push({
87
+ framework: 'pytest',
88
+ kind: 'unit',
89
+ command: 'pytest -q',
90
+ coverageHint: { format: 'cobertura', file: 'coverage.xml' },
91
+ });
92
+ } else if (glob(root, /tests?\/.*\.py$/).length) {
93
+ out.push({
94
+ framework: 'unittest',
95
+ kind: 'unit',
96
+ command: 'python -m unittest discover -v',
97
+ coverageHint: null,
98
+ });
99
+ }
100
+ }
101
+
102
+ if (fs.existsSync(path.join(root, 'pom.xml')) || fs.existsSync(path.join(root, 'build.gradle')) ||
103
+ fs.existsSync(path.join(root, 'build.gradle.kts'))) {
104
+ out.push({
105
+ framework: 'junit',
106
+ kind: 'unit',
107
+ command: fs.existsSync(path.join(root, 'pom.xml')) ? 'mvn -B test' : './gradlew test',
108
+ coverageHint: { format: 'jacoco-xml', file: 'target/site/jacoco/jacoco.xml' },
109
+ });
110
+ }
111
+
112
+ return out;
113
+ }
114
+
115
+ function readJsonSafe(file) {
116
+ try { return JSON.parse(fs.readFileSync(file, 'utf8')); } catch { return null; }
117
+ }
118
+
119
+ function hasFile(dir, re) { return glob(dir, re).length > 0; }
120
+
121
+ function glob(dir, re, max = 10) {
122
+ if (!fs.existsSync(dir)) return [];
123
+ let out = [];
124
+ try {
125
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
126
+ for (const e of entries) {
127
+ if (out.length >= max) break;
128
+ if (e.isFile() && re.test(e.name)) out.push(path.join(dir, e.name));
129
+ }
130
+ } catch { /* ignore */ }
131
+ return out;
132
+ }
133
+
134
+ function anyFileContains(root, candidates, needle) {
135
+ for (const c of candidates) {
136
+ const f = path.join(root, c);
137
+ if (!fs.existsSync(f)) continue;
138
+ try { if (fs.readFileSync(f, 'utf8').includes(needle)) return true; } catch { /* ignore */ }
139
+ }
140
+ return false;
141
+ }
142
+
143
+ module.exports = { detect };
@@ -0,0 +1,236 @@
1
+ 'use strict';
2
+ /**
3
+ * Parse stdout/stderr from various test frameworks into a uniform shape:
4
+ * { framework, total, passed, failed, skipped, duration_ms, failures: [{name, file?, message?}] }
5
+ *
6
+ * All parsers are best-effort and tolerant of missing fields. They look at
7
+ * the LAST occurrence of summary lines, since flaky retries / multiple suites
8
+ * can produce intermediate ones.
9
+ */
10
+
11
+ function parse(framework, stdout) {
12
+ const text = stdout || '';
13
+ const fn = parsers[framework];
14
+ if (!fn) return parsers.generic(text, framework);
15
+ return Object.assign({ framework, total: null, passed: null, failed: null, skipped: null, duration_ms: null, failures: [] }, fn(text));
16
+ }
17
+
18
+ const parsers = {
19
+ // -------- jest --------
20
+ // sample:
21
+ // Tests: 2 failed, 8 passed, 1 skipped, 11 total
22
+ // Time: 1.345 s
23
+ jest(text) {
24
+ const out = {};
25
+ const m = matchLast(text, /^Tests:\s+(.*?)$/m);
26
+ if (m) {
27
+ const fields = m[1];
28
+ out.failed = numField(fields, /(\d+)\s+failed/);
29
+ out.passed = numField(fields, /(\d+)\s+passed/);
30
+ out.skipped = numField(fields, /(\d+)\s+(?:skipped|todo|pending)/);
31
+ out.total = numField(fields, /(\d+)\s+total/);
32
+ }
33
+ const t = matchLast(text, /^Time:\s+([\d.]+)\s*s/m);
34
+ if (t) out.duration_ms = Math.round(parseFloat(t[1]) * 1000);
35
+ out.failures = collectFailures(text, /^\s*●\s+(.*)$/gm).slice(0, 50);
36
+ return out;
37
+ },
38
+
39
+ // -------- vitest --------
40
+ // sample:
41
+ // Test Files 1 failed (1)
42
+ // Tests 1 failed | 5 passed (6)
43
+ // Duration 234ms
44
+ vitest(text) {
45
+ const out = {};
46
+ const tests = matchLast(text, /Tests\s+([^\n]+)/);
47
+ if (tests) {
48
+ const seg = tests[1];
49
+ out.failed = numField(seg, /(\d+)\s+failed/);
50
+ out.passed = numField(seg, /(\d+)\s+passed/);
51
+ out.skipped = numField(seg, /(\d+)\s+skipped/);
52
+ const tot = seg.match(/\((\d+)\)/);
53
+ if (tot) out.total = parseInt(tot[1], 10);
54
+ else out.total = (out.failed || 0) + (out.passed || 0) + (out.skipped || 0);
55
+ }
56
+ const d = matchLast(text, /Duration\s+([\d.]+)\s*(ms|s)/);
57
+ if (d) out.duration_ms = d[2] === 's' ? Math.round(parseFloat(d[1]) * 1000) : Math.round(parseFloat(d[1]));
58
+ out.failures = collectFailures(text, /^\s*FAIL\s+(.+)$/gm).slice(0, 50);
59
+ return out;
60
+ },
61
+
62
+ // -------- mocha (spec reporter) --------
63
+ // 12 passing (45ms)
64
+ // 1 pending
65
+ // 2 failing
66
+ mocha(text) {
67
+ const out = {};
68
+ const p = matchLast(text, /(\d+)\s+passing\s*\(([\d.]+)\s*(ms|s)\)/);
69
+ if (p) {
70
+ out.passed = parseInt(p[1], 10);
71
+ out.duration_ms = p[3] === 's' ? Math.round(parseFloat(p[2]) * 1000) : Math.round(parseFloat(p[2]));
72
+ }
73
+ const f = matchLast(text, /(\d+)\s+failing/);
74
+ if (f) out.failed = parseInt(f[1], 10);
75
+ const s = matchLast(text, /(\d+)\s+pending/);
76
+ if (s) out.skipped = parseInt(s[1], 10);
77
+ out.total = sumDefined(out.passed, out.failed, out.skipped);
78
+ out.failures = collectFailures(text, /^\s*\d+\)\s+(.+)$/gm).slice(0, 50);
79
+ return out;
80
+ },
81
+
82
+ // -------- node --test (TAP-ish) --------
83
+ // # tests 10
84
+ // # pass 8
85
+ // # fail 2
86
+ // # skip 0
87
+ // # duration_ms 123.456
88
+ 'node-test'(text) {
89
+ const out = {};
90
+ const t = matchLast(text, /#\s+tests\s+(\d+)/);
91
+ const p = matchLast(text, /#\s+pass\s+(\d+)/);
92
+ const f = matchLast(text, /#\s+fail\s+(\d+)/);
93
+ const s = matchLast(text, /#\s+skip\s+(\d+)/);
94
+ const d = matchLast(text, /#\s+duration_ms\s+([\d.]+)/);
95
+ if (t) out.total = parseInt(t[1], 10);
96
+ if (p) out.passed = parseInt(p[1], 10);
97
+ if (f) out.failed = parseInt(f[1], 10);
98
+ if (s) out.skipped = parseInt(s[1], 10);
99
+ if (d) out.duration_ms = Math.round(parseFloat(d[1]));
100
+ return out;
101
+ },
102
+
103
+ // -------- tap --------
104
+ // 1..10
105
+ // ok 1 ...
106
+ // not ok 2 ...
107
+ tap(text) {
108
+ const planMatch = matchLast(text, /^\s*1\.\.(\d+)/m);
109
+ const okLines = (text.match(/^\s*ok\s+\d+/gm) || []).length;
110
+ const notOk = (text.match(/^\s*not ok\s+\d+/gm) || []).length;
111
+ return {
112
+ total: planMatch ? parseInt(planMatch[1], 10) : okLines + notOk,
113
+ passed: okLines,
114
+ failed: notOk,
115
+ skipped: (text.match(/^\s*ok\s+\d+\s+#\s+(SKIP|TODO)/gim) || []).length,
116
+ };
117
+ },
118
+
119
+ // -------- go test --------
120
+ // PASS / FAIL lines per test, then "ok package time" or "FAIL package time"
121
+ // With -v, lines like "--- PASS: TestX (0.01s)" / "--- FAIL: TestY (0.00s)"
122
+ go(text) {
123
+ const passed = (text.match(/^--- PASS:/gm) || []).length;
124
+ const failed = (text.match(/^--- FAIL:/gm) || []).length;
125
+ const skipped = (text.match(/^--- SKIP:/gm) || []).length;
126
+ const failureNames = [];
127
+ let m;
128
+ const re = /^--- FAIL:\s+(\S+)/gm;
129
+ while ((m = re.exec(text)) !== null) failureNames.push(m[1]);
130
+ return {
131
+ total: passed + failed + skipped,
132
+ passed, failed, skipped,
133
+ failures: failureNames.map((n) => ({ name: n })).slice(0, 50),
134
+ };
135
+ },
136
+
137
+ // -------- pytest --------
138
+ // ============== 2 failed, 8 passed, 1 skipped in 1.23s ==============
139
+ pytest(text) {
140
+ const out = {};
141
+ const m = matchLast(text, /=+\s*([^=]+?)\s+in\s+([\d.]+)s\s*=+/);
142
+ if (m) {
143
+ const seg = m[1];
144
+ out.failed = numField(seg, /(\d+)\s+failed/);
145
+ out.passed = numField(seg, /(\d+)\s+passed/);
146
+ out.skipped = numField(seg, /(\d+)\s+skipped/);
147
+ out.duration_ms = Math.round(parseFloat(m[2]) * 1000);
148
+ out.total = sumDefined(out.failed, out.passed, out.skipped);
149
+ }
150
+ out.failures = collectFailures(text, /^FAILED\s+(.+?)\s+-/gm).slice(0, 50);
151
+ return out;
152
+ },
153
+
154
+ // -------- python unittest --------
155
+ // Ran 10 tests in 0.123s
156
+ // FAILED (failures=2, errors=1, skipped=1)
157
+ unittest(text) {
158
+ const out = {};
159
+ const r = matchLast(text, /Ran\s+(\d+)\s+tests?\s+in\s+([\d.]+)s/);
160
+ if (r) {
161
+ out.total = parseInt(r[1], 10);
162
+ out.duration_ms = Math.round(parseFloat(r[2]) * 1000);
163
+ }
164
+ const fail = matchLast(text, /FAILED\s+\(([^)]+)\)/);
165
+ if (fail) {
166
+ out.failed = (numField(fail[1], /failures=(\d+)/) || 0) + (numField(fail[1], /errors=(\d+)/) || 0);
167
+ out.skipped = numField(fail[1], /skipped=(\d+)/) || 0;
168
+ } else if (/^OK\b/m.test(text)) {
169
+ out.failed = 0;
170
+ }
171
+ if (out.total != null) out.passed = out.total - (out.failed || 0) - (out.skipped || 0);
172
+ return out;
173
+ },
174
+
175
+ // -------- generic fallback --------
176
+ generic(text) {
177
+ // Try to extract any "X passed, Y failed" pattern.
178
+ const out = {};
179
+ out.passed = numField(text, /(\d+)\s+passed/);
180
+ out.failed = numField(text, /(\d+)\s+failed/);
181
+ out.skipped = numField(text, /(\d+)\s+skipped/);
182
+ out.total = sumDefined(out.passed, out.failed, out.skipped);
183
+ return out;
184
+ },
185
+ };
186
+
187
+ function matchLast(text, re) {
188
+ if (re.global) {
189
+ let last;
190
+ let m;
191
+ while ((m = re.exec(text)) !== null) { last = m; }
192
+ return last || null;
193
+ }
194
+ // Find all matches by repeatedly matching in remaining text.
195
+ const matches = [];
196
+ const lines = text.split('\n');
197
+ for (const line of lines) {
198
+ const m = line.match(re);
199
+ if (m) matches.push(m);
200
+ }
201
+ return matches[matches.length - 1] || null;
202
+ }
203
+
204
+ function numField(text, re) {
205
+ const m = text.match(re);
206
+ return m ? parseInt(m[1], 10) : 0;
207
+ }
208
+
209
+ function sumDefined(...nums) {
210
+ let s = 0; let any = false;
211
+ for (const n of nums) if (n != null) { s += n; any = true; }
212
+ return any ? s : null;
213
+ }
214
+
215
+ function collectFailures(text, re) {
216
+ const out = [];
217
+ let m;
218
+ while ((m = re.exec(text)) !== null) {
219
+ out.push({ name: m[1].trim() });
220
+ }
221
+ return dedupeBy(out, (x) => x.name);
222
+ }
223
+
224
+ function dedupeBy(arr, key) {
225
+ const seen = new Set();
226
+ const out = [];
227
+ for (const x of arr) {
228
+ const k = key(x);
229
+ if (seen.has(k)) continue;
230
+ seen.add(k);
231
+ out.push(x);
232
+ }
233
+ return out;
234
+ }
235
+
236
+ module.exports = { parse };
@@ -0,0 +1,38 @@
1
+ name: devflow
2
+
3
+ on:
4
+ pull_request:
5
+ paths:
6
+ - 'devflow/**'
7
+ push:
8
+ branches: [ ${defaultBranch} ]
9
+ paths:
10
+ - 'devflow/knowledge/**'
11
+
12
+ jobs:
13
+ validate:
14
+ runs-on: ubuntu-latest
15
+ steps:
16
+ - uses: actions/checkout@v4
17
+ - uses: actions/setup-node@v4
18
+ with: { node-version: '20' }
19
+ - run: npm i -g devflow-kit
20
+ - name: validate change folders
21
+ run: devflow doctor --scope=changes --json
22
+ - name: validate knowledge meta
23
+ run: devflow knowledge validate
24
+
25
+ knowledge-sync:
26
+ if: github.event_name == 'push' && github.ref == 'refs/heads/${defaultBranch}'
27
+ runs-on: ubuntu-latest
28
+ needs: validate
29
+ steps:
30
+ - uses: actions/checkout@v4
31
+ - uses: actions/setup-node@v4
32
+ with: { node-version: '20' }
33
+ - run: npm i -g devflow-kit
34
+ - name: incremental kb sync
35
+ env:
36
+ WEKNORA_TOKEN: ${{ secrets.WEKNORA_TOKEN }}
37
+ DEVFLOW_KB_DRY_RUN: ${{ vars.DEVFLOW_KB_DRY_RUN || '0' }}
38
+ run: devflow knowledge sync
@@ -0,0 +1,27 @@
1
+ stages: [ validate, sync ]
2
+
3
+ variables:
4
+ DEVFLOW_KB_DRY_RUN: "0"
5
+
6
+ devflow:validate:
7
+ stage: validate
8
+ image: node:20
9
+ script:
10
+ - npm i -g devflow-kit
11
+ - devflow doctor --scope=changes --json
12
+ - devflow knowledge validate
13
+ rules:
14
+ - changes: [ "devflow/**/*" ]
15
+
16
+ devflow:knowledge-sync:
17
+ stage: sync
18
+ image: node:20
19
+ needs: [ devflow:validate ]
20
+ variables:
21
+ WEKNORA_TOKEN: $WEKNORA_TOKEN
22
+ script:
23
+ - npm i -g devflow-kit
24
+ - devflow knowledge sync
25
+ rules:
26
+ - if: '$CI_COMMIT_BRANCH == "${defaultBranch}"'
27
+ changes: [ "devflow/knowledge/**/*" ]
@@ -0,0 +1,63 @@
1
+ ---
2
+ slug: ${slug}
3
+ phase: design
4
+ status: draft
5
+ ---
6
+
7
+ # 技术方案 — ${title}
8
+
9
+ ## 1. 方案总览
10
+
11
+ (一句话描述方案与边界)
12
+
13
+ ## 2. 架构变更
14
+
15
+ ```
16
+ (简单 ASCII 或 mermaid)
17
+ ```
18
+
19
+ ## 3. 模块/类/方法清单
20
+
21
+ | 模块 | 改动类型 | 说明 |
22
+ |------|---------|------|
23
+ | | 新建/修改/删除 | |
24
+
25
+ ## 4. 接口契约 {#contracts}
26
+
27
+ | 接口 | 方法 | 入参 | 出参 | 说明 |
28
+ |------|------|------|------|------|
29
+ | /api/xxx | POST | | | |
30
+
31
+ ### YAPI / OpenAPI 同步
32
+
33
+ - 契约文件:
34
+ - 同步命令:`devflow sync yapi --file=<openapi.json|api.md> --dry-run`
35
+
36
+ ## 5. 数据库 DDL {#ddl}
37
+
38
+ ```sql
39
+ -- 必须可重复执行(IF NOT EXISTS / IF EXISTS),禁止破坏性 DROP
40
+ ```
41
+
42
+ ## 6. 核心流程
43
+
44
+ (关键路径的伪代码或时序图)
45
+
46
+ ### 调用链一致性验证
47
+
48
+ - traceId / requestId 传递点:
49
+ - 调用方 / 回调 / redirect / query 参数:
50
+ - 日志检索:`devflow logs query --trace-id=<id>` 或 `--request-id=<id>`
51
+ - 验证命令:`devflow test contract --cmd="<contract/check command>"`
52
+
53
+ ## 7. 错误处理与回滚
54
+
55
+ ## 8. 监控与告警
56
+
57
+ ## 9. 兼容性与灰度
58
+
59
+ ## 10. 备选方案与取舍
60
+
61
+ ## 11. spec delta(可选)
62
+
63
+ > 若本方案改变了系统能力,运行 \`devflow design --with-spec\` 在 \`delta/\` 下生成对应能力的 ADDED/MODIFIED/REMOVED 文件。
@@ -0,0 +1,58 @@
1
+ > 本段由 \`devflow-kit\` 维护,请勿手工编辑。运行 \`devflow update\` 升级。
2
+
3
+ ## devflow 工作流
4
+
5
+ ### 入口路由
6
+
7
+ | 输入 | 命令 |
8
+ |------|------|
9
+ | Wiki/Confluence/Notion URL | \`devflow ingest <url>\` |
10
+ | JIRA/Linear/GitHub Issue ID | \`devflow ingest <id>\` |
11
+ | 故障告警 | \`devflow ingest incident:<id>\` |
12
+ | 纯想法(无外部输入) | \`devflow new <slug>\`(触发 brainstorm) |
13
+
14
+ ### 阶段-命令映射
15
+
16
+ | 阶段 | 命令 | 产物 |
17
+ |------|------|------|
18
+ | 立项 | \`devflow propose\` | proposal.md |
19
+ | 需求澄清 | \`devflow requirement\` | requirement.md |
20
+ | 技术设计 | \`devflow design [--with-tests] [--with-spec]\` | design.md (+ tests.md, delta/) |
21
+ | 任务计划 | \`devflow plan\` | plan.md |
22
+ | 编码 | \`devflow apply [--task N]\` | 代码 + commits |
23
+ | 审查 | \`devflow review\` | review.md(3 轮迭代) |
24
+ | 测试 | \`devflow test <unit\|integration\|e2e\|joint\|remote\|smoke\|regression\|perf>\` | reports/test-report.md#<kind> |
25
+ | 自测报告 | \`devflow report self-test\` | reports/test-report.md#self-test |
26
+ | 验证 | \`devflow verify [finalize]\` | verify.md |
27
+ | 收尾 | \`devflow deliver\` | merge / PR / keep / discard |
28
+ | 归档 | \`devflow archive\` | archive/ + spec delta 合并 |
29
+
30
+ ### ⛔ AI Agent 强制规则:devflow 命令必须用 Shell 工具执行
31
+
32
+ **当用户给出 Wiki/Confluence/Notion/JIRA 等 URL 或 ID 时:**
33
+
34
+ ```
35
+ ✅ 正确:调用 Shell/Terminal 工具 → devflow ingest "<url>"
36
+ ❌ 禁止:WebFetch 工具 / curl / browser 工具 / 让用户复制粘贴内容
37
+ ```
38
+
39
+ - `devflow ingest` 通过已配置的 provider + 认证 token 访问内网,AI 自身无法直接访问内网 URL
40
+ - 即使 WebFetch 报超时/401,**正确响应是执行 Shell 命令**,不是放弃或让用户粘贴
41
+ - 如果 `devflow ingest` 失败(exit ≠ 0),读取错误输出诊断原因,不要绕过它
42
+
43
+ **所有 `devflow *` 命令的执行方式:Shell 工具 → `cd <project_dir> && devflow <subcmd>`**
44
+
45
+ ### 硬规则
46
+
47
+ - 任何代码改动必须先 \`devflow plan\`,先写测试再写实现
48
+ - \`devflow review\` 失败 ≥ 3 轮必须升级 \`design.md\` 重新评审
49
+ - \`devflow verify finalize\` 校验所有启用的报告存在,无报告不得 \`devflow deliver\`
50
+ - \`devflow archive\` 时 spec delta 自动合并到 \`devflow/specs/\`
51
+ - 凭证不得写入任何 markdown 产物;\`devflow doctor\` 会扫描 token 嫌疑
52
+
53
+ ### 斜杠命令(Claude Code / Cursor)
54
+
55
+ - \`/df:ingest <input>\` — 等价 \`devflow ingest\`
56
+ - \`/df:design\` / \`/df:plan\` / \`/df:review\` / \`/df:verify\` / \`/df:archive\`
57
+ - \`/df:status\` — 当前 change 阶段总览
58
+ - \`/df:doctor\` — 7 维度体检
@@ -0,0 +1 @@
1
+ > 项目概览维持在 [\`${overviewSource}\`](${overviewSource}),devflow 不在此重复维护。Skills 会从 \`devflow/config.json#projectOverview.referencedFile\` 读取并加载该文件作为上下文。运行 \`devflow sync project --check\` 检查漂移。
@@ -0,0 +1,27 @@
1
+ > 本段由 \`devflow-kit\` 维护,请勿手工编辑。源:${overviewSource}。运行 \`devflow sync project\` 刷新。
2
+
3
+ ## 项目概览
4
+
5
+ ### 技术栈
6
+
7
+ - 语言 / 运行时:${stack}
8
+ - 框架:${frameworks}
9
+ - 包管理:${packageManager}
10
+
11
+ ### 模块结构
12
+
13
+ ${modules}
14
+
15
+ ### 常用命令
16
+
17
+ \`\`\`
18
+ ${commands}
19
+ \`\`\`
20
+
21
+ ### 编码规范
22
+
23
+ ${codingStyle}
24
+
25
+ ### 测试规范
26
+
27
+ ${testingStyle}
@@ -0,0 +1,17 @@
1
+ {
2
+ "$schema": "https://devflow.dev/schemas/knowledge-index.schema.json",
3
+ "version": 2,
4
+ "layout": "flat",
5
+ "categories": [
6
+ { "id": "concepts", "label": "领域概念", "examples": ["保险术语表.md", "订单状态机.md"] },
7
+ { "id": "rules", "label": "业务规则", "examples": ["合规跳转决策表.md"] },
8
+ { "id": "scenarios", "label": "业务场景", "examples": ["保险补贴申请.md"] },
9
+ { "id": "contracts", "label": "接口契约", "examples": ["订单 REST API.md", "Kafka events.md"] },
10
+ { "id": "decisions", "label": "架构决策", "examples": ["[SCRUM-001] xxx - 技术方案.md"] },
11
+ { "id": "services", "label": "服务信息", "examples": ["[order_service] 概述.md"] },
12
+ { "id": "environments", "label": "环境配置", "examples": [] },
13
+ { "id": "incidents", "label": "故障复盘", "examples": [] },
14
+ { "id": "runbooks", "label": "运维手册", "examples": [] },
15
+ { "id": "solutions", "label": "解决方案", "examples": ["[slug] 经验总结.md"] }
16
+ ]
17
+ }
@@ -0,0 +1,28 @@
1
+ ---
2
+ slug: ${slug}
3
+ phase: knowledge-query
4
+ ts: ${date}
5
+ ---
6
+
7
+ # 知识库检索结果 — ${title}
8
+
9
+ ## 查询
10
+
11
+ - 关键词:${query}
12
+ - 数据源:${sources}
13
+
14
+ ## 命中(本地)
15
+
16
+ | 文件 | 摘要 | 评分 |
17
+ |------|------|------|
18
+ | | | |
19
+
20
+ ## 命中(远端)
21
+
22
+ | 来源 | 标题 | 链接 | 评分 |
23
+ |------|------|------|------|
24
+ | | | | |
25
+
26
+ ## 与本次任务的关联
27
+
28
+ - (人工标注哪些条目影响本次设计、应在 design.md 引用)
@@ -0,0 +1,8 @@
1
+ {
2
+ "$schema": "https://devflow.dev/schemas/meta.schema.json",
3
+ "category": "${category}",
4
+ "tag_name": "${tag_name}",
5
+ "tag_id": "",
6
+ "external": {},
7
+ "files": {}
8
+ }
@@ -0,0 +1,38 @@
1
+ ---
2
+ slug: ${slug}
3
+ phase: plan
4
+ status: draft
5
+ ---
6
+
7
+ # 任务计划 — ${title}
8
+
9
+ > 原则:bite-size 任务 + TDD。每条任务可在 30~60min 内交付,先写测试再写实现。
10
+
11
+ ## 任务清单
12
+
13
+ ### T-1 (任务描述)
14
+
15
+ - 文件:\`path/to/file.ts\`
16
+ - 行为:(测试可断言的具体行为)
17
+ - 测试:
18
+ - [ ] unit:断言 X 在 Y 条件下返回 Z
19
+ - 实现:
20
+ - [ ] 先让测试失败
21
+ - [ ] 实现到测试通过
22
+ - 提交:\`feat(scope): T-1 ...\`
23
+
24
+ ### T-2
25
+
26
+ (同上)
27
+
28
+ ## 顺序与依赖
29
+
30
+ ```
31
+ T-1 → T-2 → T-3
32
+ ```
33
+
34
+ ## 检查点
35
+
36
+ - [ ] 所有任务测试通过
37
+ - [ ] \`devflow review\` 通过
38
+ - [ ] \`devflow verify\` 通过