@boyingliu01/xp-gate 0.19.2 → 0.20.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 (223) hide show
  1. package/adapters/gate-4.sh +83 -18
  2. package/adapters/gate-8.sh +49 -5
  3. package/adapters/iac.sh +1 -1
  4. package/adapters/typescript.sh +15 -60
  5. package/bin/xp-gate.js +15 -16
  6. package/gate-10.sh +35 -2
  7. package/gate-4.sh +110 -21
  8. package/gate-8.sh +49 -5
  9. package/hooks/gate-10.sh +35 -2
  10. package/hooks/gate-4.sh +110 -21
  11. package/hooks/gate-8.sh +49 -5
  12. package/hooks/lib/jscpd-run.sh +63 -0
  13. package/hooks/lib/sprint-gate-report.sh +58 -0
  14. package/hooks/lib/test-failure.sh +152 -0
  15. package/hooks/lib/typecheck.sh +141 -0
  16. package/hooks/lib/validate-code-walkthrough.cjs +43 -5
  17. package/hooks/post-merge +100 -0
  18. package/hooks/pre-commit +401 -155
  19. package/hooks/pre-push +75 -19
  20. package/lib/__tests__/bootstrap.test.js +23 -41
  21. package/lib/__tests__/check-version.test.js +8 -1
  22. package/lib/__tests__/detect-deps.test.js +16 -0
  23. package/lib/__tests__/doctor-drift-round2.test.js +212 -0
  24. package/lib/__tests__/doctor-executed-surface.test.js +209 -0
  25. package/lib/__tests__/doctor-hook-drift.test.js +329 -0
  26. package/lib/__tests__/doctor-machine-mutation.test.js +116 -0
  27. package/lib/__tests__/doctor-module-drift.test.js +206 -0
  28. package/lib/__tests__/doctor-module-resolution-parity.test.js +314 -0
  29. package/lib/__tests__/doctor-path-equivalence.test.js +59 -0
  30. package/lib/__tests__/doctor-repair-boundary.test.js +52 -0
  31. package/lib/__tests__/doctor-tui-skill-freshness.test.js +105 -0
  32. package/lib/__tests__/doctor.test.js +155 -0
  33. package/lib/__tests__/gate-runner.test.js +19 -3
  34. package/lib/__tests__/helpers/lib-sandbox.cjs +55 -0
  35. package/lib/__tests__/init.test.js +44 -17
  36. package/lib/__tests__/install-skill-full-dir.test.js +151 -0
  37. package/lib/__tests__/install-skill.test.js +51 -132
  38. package/lib/__tests__/language-tools-detect.test.js +158 -0
  39. package/lib/__tests__/phase-transition.test.js +15 -1
  40. package/lib/__tests__/qoder-delphi-agents.test.js +285 -0
  41. package/lib/__tests__/shared-paths.test.js +20 -0
  42. package/lib/__tests__/shared-utils.test.js +69 -0
  43. package/lib/__tests__/skill-flags.test.js +48 -0
  44. package/lib/__tests__/ui-detector.test.ts +6 -2
  45. package/lib/__tests__/uninstall.test.js +20 -0
  46. package/lib/__tests__/update-hooks.test.js +12 -0
  47. package/lib/__tests__/update-skill.test.js +7 -4
  48. package/lib/detect-deps.js +3 -20
  49. package/lib/doctor-tui.js +22 -5
  50. package/lib/doctor.js +1178 -31
  51. package/lib/init.js +184 -33
  52. package/lib/install-cmd.js +7 -4
  53. package/lib/install-skill.js +38 -95
  54. package/lib/language-tools.js +83 -4
  55. package/lib/mattpocock-license-verdict.json +1 -1
  56. package/lib/shared-paths.js +5 -2
  57. package/lib/shared-utils.js +47 -3
  58. package/lib/uninstall.js +12 -0
  59. package/lib/update-hooks.js +4 -4
  60. package/lib/update-skill.js +6 -7
  61. package/mock-policy/AGENTS.md +4 -4
  62. package/mutation/AGENTS.md +4 -4
  63. package/mutation/__tests__/gate-m.test.ts +66 -14
  64. package/mutation/__tests__/mutmut-runner.test.ts +91 -42
  65. package/mutation/__tests__/test-filter-parity.test.ts +37 -0
  66. package/mutation/gate-m.ts +21 -8
  67. package/package.json +1 -1
  68. package/plugins/claude-code/.claude-plugin/plugin.json +2 -2
  69. package/plugins/claude-code/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +0 -1
  70. package/plugins/claude-code/skills/delphi-review/AGENTS.md +4 -4
  71. package/plugins/claude-code/skills/delphi-review/SKILL.md +98 -14
  72. package/plugins/claude-code/skills/delphi-review/references/code-walkthrough.md +36 -3
  73. package/plugins/claude-code/skills/delphi-review/references/requirements.md +6 -6
  74. package/plugins/claude-code/skills/sprint-flow/AGENTS.md +4 -4
  75. package/plugins/claude-code/skills/sprint-flow/references/phase-2-design.md +3 -3
  76. package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +4 -4
  77. package/plugins/dsh/README.md +169 -4
  78. package/plugins/dsh/dsh-overlay.patch.yml +67 -0
  79. package/plugins/dsh/package.json +1 -1
  80. package/plugins/dsh/scripts/prepack.cjs +24 -19
  81. package/plugins/dsh/setup-dsh.ps1 +160 -0
  82. package/plugins/dsh/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +0 -1
  83. package/plugins/dsh/skills/delphi-review/AGENTS.md +4 -4
  84. package/plugins/dsh/skills/delphi-review/SKILL.md +98 -14
  85. package/plugins/dsh/skills/delphi-review/references/code-walkthrough.md +36 -3
  86. package/plugins/dsh/skills/delphi-review/references/requirements.md +6 -6
  87. package/plugins/dsh/skills/sprint-flow/AGENTS.md +4 -4
  88. package/plugins/dsh/skills/sprint-flow/references/phase-2-design.md +3 -3
  89. package/plugins/dsh/skills/test-specification-alignment/AGENTS.md +4 -4
  90. package/plugins/dsh/src/command.test.ts +66 -2
  91. package/plugins/dsh/src/command.ts +36 -14
  92. package/plugins/dsh/src/contract.test.ts +31 -1
  93. package/plugins/dsh/src/delphi-config.test.ts +190 -0
  94. package/plugins/dsh/src/delphi-config.ts +223 -0
  95. package/plugins/dsh/src/delphi-run.test.ts +181 -0
  96. package/plugins/dsh/src/delphi-run.ts +129 -0
  97. package/plugins/dsh/src/delphi.test.ts +222 -0
  98. package/plugins/dsh/src/delphi.ts +247 -0
  99. package/plugins/dsh/src/dialect.test.ts +59 -0
  100. package/plugins/dsh/src/dialect.ts +40 -0
  101. package/plugins/dsh/src/index.ts +70 -4
  102. package/plugins/dsh/src/skills.test.ts +26 -19
  103. package/plugins/opencode/package.json +1 -1
  104. package/plugins/opencode/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +0 -1
  105. package/plugins/opencode/skills/delphi-review/AGENTS.md +4 -4
  106. package/plugins/opencode/skills/delphi-review/SKILL.md +98 -14
  107. package/plugins/opencode/skills/delphi-review/references/code-walkthrough.md +36 -3
  108. package/plugins/opencode/skills/delphi-review/references/requirements.md +6 -6
  109. package/plugins/opencode/skills/sprint-flow/AGENTS.md +4 -4
  110. package/plugins/opencode/skills/sprint-flow/references/phase-2-design.md +3 -3
  111. package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +4 -4
  112. package/plugins/qoder/agents/delphi-architecture.md +1 -1
  113. package/plugins/qoder/agents/delphi-feasibility.md +1 -1
  114. package/plugins/qoder/agents/delphi-technical.md +1 -1
  115. package/plugins/qoder/plugin.json +2 -2
  116. package/plugins/qoder/skills/admin-template-guidelines/SKILL.md +6 -797
  117. package/plugins/qoder/skills/admin-template-guidelines/references/rule-1.md +91 -0
  118. package/plugins/qoder/skills/admin-template-guidelines/references/rule-2.md +155 -0
  119. package/plugins/qoder/skills/admin-template-guidelines/references/rule-3.md +70 -0
  120. package/plugins/qoder/skills/admin-template-guidelines/references/rule-4.md +154 -0
  121. package/plugins/qoder/skills/admin-template-guidelines/references/rule-5.md +126 -0
  122. package/plugins/qoder/skills/admin-template-guidelines/references/rule-6.md +180 -0
  123. package/plugins/qoder/skills/batch-grill-me/SKILL.md +15 -0
  124. package/plugins/qoder/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +0 -1
  125. package/plugins/qoder/skills/clipboard-vision/describe-clipboard.ps1 +115 -0
  126. package/plugins/qoder/skills/clipboard-vision/describe-clipboard.sh +161 -0
  127. package/plugins/qoder/skills/delphi-review/.delphi-config.json.example +61 -28
  128. package/plugins/qoder/skills/delphi-review/AGENTS.md +4 -4
  129. package/plugins/qoder/skills/delphi-review/INSTALL.md +97 -98
  130. package/plugins/qoder/skills/delphi-review/SKILL.md +170 -63
  131. package/plugins/qoder/skills/delphi-review/hooks.md +19 -0
  132. package/plugins/qoder/skills/delphi-review/opencode.json.delphi.example +6 -10
  133. package/plugins/qoder/skills/delphi-review/references/code-walkthrough.md +36 -3
  134. package/plugins/qoder/skills/delphi-review/references/orchestrator-dispatch.md +64 -0
  135. package/plugins/qoder/skills/delphi-review/references/requirements.md +402 -0
  136. package/plugins/qoder/skills/delphi-review/references/round-templates.md +134 -0
  137. package/plugins/qoder/skills/delphi-review/scripts/verify-consensus.sh +43 -0
  138. package/plugins/qoder/skills/delphi-review/tools.md +29 -0
  139. package/plugins/qoder/skills/domain-modeling/ADR-FORMAT.md +64 -0
  140. package/plugins/qoder/skills/domain-modeling/CONTEXT-FORMAT.md +60 -0
  141. package/plugins/qoder/skills/domain-modeling/SKILL.md +74 -0
  142. package/plugins/qoder/skills/grill-with-docs/SKILL.md +7 -0
  143. package/plugins/qoder/skills/grilling/SKILL.md +12 -0
  144. package/plugins/qoder/skills/ralph-loop/SKILL.md +87 -428
  145. package/plugins/qoder/skills/ralph-loop/references/components.md +113 -0
  146. package/plugins/qoder/skills/ralph-loop/references/workflow-details.md +99 -0
  147. package/plugins/qoder/skills/sprint-flow/AGENTS.md +5 -5
  148. package/plugins/qoder/skills/sprint-flow/SKILL.md +255 -454
  149. package/plugins/qoder/skills/sprint-flow/__tests__/sprint-flow.test.cjs +457 -0
  150. package/plugins/qoder/skills/sprint-flow/hooks.md +18 -0
  151. package/plugins/qoder/skills/sprint-flow/references/components/memory.md +1 -1
  152. package/plugins/qoder/skills/sprint-flow/references/components/middleware.md +1 -1
  153. package/plugins/qoder/skills/sprint-flow/references/components/skill-invocations.md +21 -20
  154. package/plugins/qoder/skills/sprint-flow/references/components/tool-descriptions.md +22 -23
  155. package/plugins/qoder/skills/sprint-flow/references/force-levels.md +0 -1
  156. package/plugins/qoder/skills/sprint-flow/references/orchestration-rules.md +34 -27
  157. package/plugins/qoder/skills/sprint-flow/references/phase-1-prep.md +1 -1
  158. package/plugins/qoder/skills/sprint-flow/references/phase-2-design.md +160 -123
  159. package/plugins/qoder/skills/sprint-flow/references/phase-3-build.md +203 -2
  160. package/plugins/qoder/skills/sprint-flow/references/phase-4-verify.md +107 -34
  161. package/plugins/qoder/skills/sprint-flow/references/phase-5-ship.md +194 -12
  162. package/plugins/qoder/skills/sprint-flow/references/phase-6-close.md +139 -7
  163. package/plugins/qoder/skills/sprint-flow/references/phase-overview.md +486 -0
  164. package/plugins/qoder/skills/sprint-flow/scripts/verify-sprint-state.sh +38 -0
  165. package/plugins/qoder/skills/sprint-flow/tools.md +29 -0
  166. package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +4 -4
  167. package/plugins/qoder/skills/test-specification-alignment/SKILL.md +104 -601
  168. package/plugins/qoder/skills/to-issues/SKILL.md +1 -1
  169. package/principles/AGENTS.md +9 -8
  170. package/principles/__tests__/adapter-metrics.test.ts +292 -0
  171. package/principles/__tests__/analyzer.test.ts +29 -6
  172. package/principles/__tests__/baseline-prune-timeout.test.ts +71 -0
  173. package/principles/__tests__/baseline-prune.test.ts +338 -0
  174. package/principles/__tests__/boy-scout-init-baseline.test.ts +167 -0
  175. package/principles/__tests__/boy-scout-integration.test.ts +97 -6
  176. package/principles/__tests__/boy-scout.test.ts +253 -7
  177. package/principles/__tests__/config-enforcement.test.ts +469 -0
  178. package/principles/__tests__/config-git-timeout.test.ts +66 -0
  179. package/principles/__tests__/direct-execution.test.ts +187 -0
  180. package/principles/__tests__/index.test.ts +16 -3
  181. package/principles/__tests__/warnings-baseline-budgets.test.ts +74 -0
  182. package/principles/adapters/typescript.ts +613 -13
  183. package/principles/analyzer.ts +22 -0
  184. package/principles/baseline-prune.ts +217 -0
  185. package/principles/boy-scout.ts +481 -51
  186. package/principles/config.ts +362 -54
  187. package/principles/direct-execution.ts +60 -0
  188. package/principles/index.ts +40 -17
  189. package/principles/rules/__tests__/clean-code/many-exports.test.ts +16 -10
  190. package/principles/rules/clean-code/code-duplication.ts +8 -8
  191. package/principles/rules/clean-code/deep-nesting.ts +8 -8
  192. package/principles/rules/clean-code/god-class.ts +8 -8
  193. package/principles/rules/clean-code/large-file.ts +15 -11
  194. package/principles/rules/clean-code/long-function.ts +8 -8
  195. package/principles/rules/clean-code/magic-numbers.ts +10 -7
  196. package/principles/rules/clean-code/many-exports.ts +7 -7
  197. package/principles/rules/clean-code/missing-error-handling.ts +5 -5
  198. package/principles/rules/clean-code/too-many-params.ts +8 -8
  199. package/principles/rules/clean-code/unused-imports.ts +5 -5
  200. package/principles/rules/solid/dip.ts +18 -8
  201. package/principles/rules/solid/isp.ts +9 -8
  202. package/principles/rules/solid/lsp.ts +6 -6
  203. package/principles/rules/solid/ocp.ts +6 -5
  204. package/principles/rules/solid/srp.ts +7 -8
  205. package/principles/types.ts +15 -0
  206. package/scripts/__tests__/sync-package-content.test.js +28 -0
  207. package/scripts/delphi-external-review.cjs +193 -10
  208. package/scripts/sync-package-content.js +7 -4
  209. package/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +0 -1
  210. package/skills/delphi-review/AGENTS.md +4 -4
  211. package/skills/delphi-review/SKILL.md +98 -14
  212. package/skills/delphi-review/references/code-walkthrough.md +36 -3
  213. package/skills/delphi-review/references/requirements.md +6 -6
  214. package/skills/sprint-flow/AGENTS.md +4 -4
  215. package/skills/sprint-flow/references/phase-2-design.md +3 -3
  216. package/skills/test-specification-alignment/AGENTS.md +4 -4
  217. package/lib/__tests__/download-skill.test.js +0 -281
  218. package/lib/download-skill.js +0 -96
  219. package/plugins/qoder/skills/ralph-loop/references/components/memory.md +0 -55
  220. package/plugins/qoder/skills/ralph-loop/references/components/middleware.md +0 -54
  221. package/plugins/qoder/skills/ralph-loop/references/components/skill-invocations.md +0 -39
  222. package/plugins/qoder/skills/ralph-loop/references/components/system-prompt.md +0 -24
  223. package/plugins/qoder/skills/ralph-loop/references/components/tool-descriptions.md +0 -32
@@ -1,9 +1,9 @@
1
1
  # SKILLS/SPRINT-FLOW KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-09-22
4
- **Commit:** 3d1d1bf
5
- **Branch:** main
6
- **Version:** 0.19.2.0
3
+ **Generated:** 2026-10-07
4
+ **Commit:** ff55f35b
5
+ **Branch:** fix/batch-defects-20261007
6
+ **Version:** 0.20.0.0
7
7
 
8
8
  ## OVERVIEW
9
9
  **6-phase** development pipeline (v2.0 compact redesign, Issue #290): PREP → DESIGN → BUILD → VERIFY → SHIP → CLOSE. Phase 3/6 BUILD default build mode is **ralph-loop** (REQ-level iteration, 40-67% token savings vs parallel). HARD-GATE between DESIGN (2/6) and BUILD (3/6): design must pass Delphi review (≥90% consensus) before any coding.
@@ -89,9 +89,9 @@ grill 访谈达成共享理解后(或 CONTEXT.md 快速路径下读取已有
89
89
  "head_commit": "<git rev-parse HEAD>",
90
90
  "consensus_ratio": 1.0,
91
91
  "expert_verdicts": [
92
- { "role": "architecture", "verdict": "APPROVED", "result_type": "delphi_expert_result", "requested_model": "provider/model-a" },
93
- { "role": "technical", "verdict": "APPROVED", "result_type": "delphi_expert_result", "requested_model": "provider/model-b" },
94
- { "role": "feasibility", "verdict": "APPROVED", "result_type": "delphi_expert_result", "requested_model": "provider/model-c" }
92
+ { "role": "architecture", "verdict": "APPROVED", "result_type": "delphi_expert_result", "requested_model": "provider/model-a", "resolved_model": "provider/model-a", "channel": "external" },
93
+ { "role": "technical", "verdict": "APPROVED", "result_type": "delphi_expert_result", "requested_model": "provider/model-b", "resolved_model": "provider/model-b", "channel": "external" },
94
+ { "role": "feasibility", "verdict": "APPROVED", "result_type": "delphi_expert_result", "requested_model": "provider/model-c", "resolved_model": "provider/model-c", "channel": "external" }
95
95
  ],
96
96
  "rounds": 1,
97
97
  "gaps": []
@@ -1,9 +1,9 @@
1
1
  # SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
2
2
 
3
- **Generated:** 2026-09-22
4
- **Commit:** 3d1d1bf
5
- **Branch:** main
6
- **Version:** 0.19.2.0
3
+ **Generated:** 2026-10-07
4
+ **Commit:** ff55f35b
5
+ **Branch:** fix/batch-defects-20261007
6
+ **Version:** 0.20.0.0
7
7
 
8
8
  ## OVERVIEW
9
9
  Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
@@ -1,281 +0,0 @@
1
- /**
2
- * @test download-skill
3
- * @intent Verify downloadFromGitHub/downloadTarball/downloadWithRetry/downloadFile/verifyChecksum behaviors
4
- */
5
- const fs = require('fs');
6
- const path = require('path');
7
- const os = require('os');
8
- const https = require('https');
9
- const crypto = require('crypto');
10
- const { EventEmitter } = require('events');
11
- const { Writable } = require('stream');
12
-
13
- /**
14
- * Mock https.get with controllable behavior.
15
- * opts (or array of opts for sequential calls):
16
- * - statusCode (default 200)
17
- * - body (default 'data')
18
- * - errorAfter (boolean) — emit 'error' on the request instead of responding
19
- * - redirectTo (string url) — sends 302 with location header
20
- */
21
- function mockHttpsGet(optsOrList) {
22
- const list = Array.isArray(optsOrList) ? [...optsOrList] : [optsOrList];
23
- // Synchronous fake createWriteStream — the real one opens the FD asynchronously
24
- // and races against downloadFile's unlinkSync(dest) in 301/302/error branches,
25
- // causing ENOENT unhandled errors. The fake writes via writeFileSync (sync,
26
- // file present immediately) and emits 'finish' on end().
27
- vi.spyOn(fs, 'createWriteStream').mockImplementation((dest) => {
28
- let buf = '';
29
- try {
30
- fs.writeFileSync(dest, '');
31
- } catch {
32
- /* dest dir may not exist; downstream code will surface that */
33
- }
34
- const stream = new EventEmitter();
35
- stream.write = (chunk) => {
36
- buf += chunk;
37
- return true;
38
- };
39
- stream.end = () => {
40
- try {
41
- fs.writeFileSync(dest, buf);
42
- } catch {
43
- /* file may have been unlinked by source; safe to ignore */
44
- }
45
- process.nextTick(() => stream.emit('finish'));
46
- };
47
- stream.close = () => {};
48
- return stream;
49
- });
50
-
51
- vi.spyOn(https, 'get').mockImplementation((url, options, cb) => {
52
- const callback = typeof options === 'function' ? options : cb;
53
- const req = new EventEmitter();
54
- const config = list.shift() || { statusCode: 200, body: 'data' };
55
- process.nextTick(() => {
56
- if (config.errorAfter) {
57
- req.emit('error', new Error('Network error'));
58
- return;
59
- }
60
- const response = new EventEmitter();
61
- response.statusCode = config.statusCode != null ? config.statusCode : 200;
62
- response.headers = config.redirectTo ? { location: config.redirectTo } : {};
63
- response.pipe = (file) => {
64
- file.write(config.body != null ? config.body : 'data');
65
- file.end();
66
- };
67
- callback(response);
68
- });
69
- return req;
70
- });
71
- }
72
-
73
- describe('download-skill', () => {
74
- let tmpHome, originalHome;
75
-
76
- beforeEach(() => {
77
- originalHome = process.env.HOME;
78
- tmpHome = fs.mkdtempSync(path.join(os.tmpdir(), 'xpgate-dl-'));
79
- process.env.HOME = tmpHome;
80
- vi.resetModules();
81
- delete require.cache[require.resolve('../download-skill')];
82
- vi.spyOn(console, 'log').mockImplementation(() => {});
83
- vi.spyOn(console, 'error').mockImplementation(() => {});
84
- vi.spyOn(console, 'warn').mockImplementation(() => {});
85
- });
86
-
87
- afterEach(async () => {
88
- vi.useRealTimers();
89
- await new Promise((resolve) => setImmediate(resolve));
90
- process.env.HOME = originalHome;
91
- vi.restoreAllMocks();
92
- if (fs.existsSync(tmpHome)) {
93
- fs.rmSync(tmpHome, { recursive: true, force: true });
94
- }
95
- });
96
-
97
- function cacheDir() {
98
- return path.join(tmpHome, '.config', 'xp-gate', 'cache');
99
- }
100
-
101
- describe('downloadFromGitHub', () => {
102
- // Flat repo name avoids '/' becoming a subdir in cacheFile (source only mkdirs CACHE_DIR root).
103
- it('downloads file to cache dir and returns the path', async () => {
104
- mockHttpsGet({ statusCode: 200, body: '# Skill content' });
105
- const { downloadFromGitHub } = require('../download-skill');
106
-
107
- const result = await downloadFromGitHub('flatrepo', 'skills/foo/SKILL.md');
108
-
109
- expect(result).toBe(
110
- path.join(cacheDir(), 'flatrepo-skills-foo-SKILL.md.md')
111
- );
112
- expect(fs.existsSync(result)).toBe(true);
113
- expect(fs.readFileSync(result, 'utf8')).toBe('# Skill content');
114
- });
115
-
116
- it('creates cache directory if missing', async () => {
117
- mockHttpsGet({ statusCode: 200, body: 'data' });
118
- const { downloadFromGitHub } = require('../download-skill');
119
-
120
- expect(fs.existsSync(cacheDir())).toBe(false);
121
- await downloadFromGitHub('flatrepo', 'a/b');
122
- expect(fs.existsSync(cacheDir())).toBe(true);
123
- });
124
-
125
- it('uses default version=main when not specified', async () => {
126
- mockHttpsGet({ statusCode: 200, body: 'data' });
127
- const { downloadFromGitHub } = require('../download-skill');
128
- await downloadFromGitHub('flatrepo', 'path/to/file');
129
- expect(https.get).toHaveBeenCalled();
130
- const calledUrl = https.get.mock.calls[0][0];
131
- expect(calledUrl).toContain('/main/');
132
- });
133
-
134
- it('uses custom version when specified', async () => {
135
- mockHttpsGet({ statusCode: 200, body: 'data' });
136
- const { downloadFromGitHub } = require('../download-skill');
137
- await downloadFromGitHub('flatrepo', 'path/file', 'v1.2.3');
138
- expect(https.get.mock.calls[0][0]).toContain('/v1.2.3/');
139
- });
140
- });
141
-
142
- describe('downloadTarball', () => {
143
- it('downloads tarball with correct filename', async () => {
144
- mockHttpsGet({ statusCode: 200, body: 'tarball-bytes' });
145
- const { downloadTarball } = require('../download-skill');
146
-
147
- const result = await downloadTarball('boyingliu01/xp-gate');
148
-
149
- expect(result).toBe(path.join(cacheDir(), 'boyingliu01-xp-gate.tgz'));
150
- expect(fs.existsSync(result)).toBe(true);
151
- });
152
-
153
- it('uses default main version', async () => {
154
- mockHttpsGet({ statusCode: 200, body: 'data' });
155
- const { downloadTarball } = require('../download-skill');
156
- await downloadTarball('user/repo');
157
- expect(https.get.mock.calls[0][0]).toContain('/tarball/main');
158
- });
159
- });
160
-
161
- describe('downloadWithRetry', () => {
162
- it('succeeds on first try when no errors', async () => {
163
- mockHttpsGet({ statusCode: 200, body: 'ok' });
164
- const { downloadWithRetry } = require('../download-skill');
165
-
166
- const dest = path.join(tmpHome, 'out.txt');
167
- await downloadWithRetry('https://example.com/x', dest, 3);
168
-
169
- expect(https.get).toHaveBeenCalledTimes(1);
170
- expect(fs.readFileSync(dest, 'utf8')).toBe('ok');
171
- });
172
-
173
- it('retries and eventually succeeds (sleep delays stubbed)', async () => {
174
- // Stub setTimeout so sleep() resolves immediately, without patching setImmediate.
175
- vi.spyOn(global, 'setTimeout').mockImplementation((fn) => {
176
- Promise.resolve().then(fn);
177
- return 0;
178
- });
179
- mockHttpsGet([
180
- { errorAfter: true },
181
- { statusCode: 200, body: 'recovered' },
182
- ]);
183
- const { downloadWithRetry } = require('../download-skill');
184
-
185
- const dest = path.join(tmpHome, 'retry.txt');
186
- await downloadWithRetry('https://example.com/x', dest, 3);
187
-
188
- expect(fs.readFileSync(dest, 'utf8')).toBe('recovered');
189
- });
190
-
191
- it('throws original error after exhausting retries', async () => {
192
- vi.spyOn(global, 'setTimeout').mockImplementation((fn) => {
193
- Promise.resolve().then(fn);
194
- return 0;
195
- });
196
- mockHttpsGet([
197
- { errorAfter: true },
198
- { errorAfter: true },
199
- ]);
200
- const { downloadWithRetry } = require('../download-skill');
201
-
202
- const dest = path.join(tmpHome, 'fail.txt');
203
- await expect(
204
- downloadWithRetry('https://example.com/x', dest, 2)
205
- ).rejects.toThrow('Network error');
206
- });
207
- });
208
-
209
- describe('downloadFile (via downloadWithRetry retries=1)', () => {
210
- it('follows 302 redirect to new location', async () => {
211
- mockHttpsGet([
212
- { statusCode: 302, redirectTo: 'https://newlocation.example.com/final' },
213
- { statusCode: 200, body: 'redirected-content' },
214
- ]);
215
- const { downloadWithRetry } = require('../download-skill');
216
-
217
- const dest = path.join(tmpHome, 'redirect.txt');
218
- await downloadWithRetry('https://example.com/orig', dest, 1);
219
-
220
- expect(fs.readFileSync(dest, 'utf8')).toBe('redirected-content');
221
- });
222
-
223
- it('follows 301 redirect', async () => {
224
- mockHttpsGet([
225
- { statusCode: 301, redirectTo: 'https://newloc.example/final' },
226
- { statusCode: 200, body: 'permanent-redirect' },
227
- ]);
228
- const { downloadWithRetry } = require('../download-skill');
229
-
230
- const dest = path.join(tmpHome, 'r301.txt');
231
- await downloadWithRetry('https://example.com/orig', dest, 1);
232
-
233
- expect(fs.readFileSync(dest, 'utf8')).toBe('permanent-redirect');
234
- });
235
-
236
- it('rejects with HTTP error for non-2xx/3xx status', async () => {
237
- mockHttpsGet({ statusCode: 404 });
238
- const { downloadWithRetry } = require('../download-skill');
239
-
240
- const dest = path.join(tmpHome, '404.txt');
241
- await expect(downloadWithRetry('https://example.com/x', dest, 1)).rejects.toThrow(
242
- 'HTTP 404'
243
- );
244
- });
245
-
246
- it('rejects and unlinks dest on request error', async () => {
247
- mockHttpsGet({ errorAfter: true });
248
- const { downloadWithRetry } = require('../download-skill');
249
-
250
- const dest = path.join(tmpHome, 'err.txt');
251
- await expect(downloadWithRetry('https://example.com/x', dest, 1)).rejects.toThrow(
252
- 'Network error'
253
- );
254
- // The createWriteStream call will create the file, the error handler should unlink it
255
- expect(fs.existsSync(dest)).toBe(false);
256
- });
257
- });
258
-
259
- describe('verifyChecksum', () => {
260
- it('does not throw when checksum matches', () => {
261
- const file = path.join(tmpHome, 'data.txt');
262
- const content = 'hello world';
263
- fs.writeFileSync(file, content);
264
- const expected = crypto.createHash('sha256').update(content).digest('hex');
265
-
266
- const { verifyChecksum } = require('../download-skill');
267
- expect(() => verifyChecksum(file, expected)).not.toThrow();
268
- // File preserved
269
- expect(fs.existsSync(file)).toBe(true);
270
- });
271
-
272
- it('throws and deletes file on mismatch', () => {
273
- const file = path.join(tmpHome, 'bad.txt');
274
- fs.writeFileSync(file, 'real-content');
275
-
276
- const { verifyChecksum } = require('../download-skill');
277
- expect(() => verifyChecksum(file, 'wrong-hash-zzz')).toThrow(/Checksum mismatch/);
278
- expect(fs.existsSync(file)).toBe(false);
279
- });
280
- });
281
- });
@@ -1,96 +0,0 @@
1
- const https = require('https');
2
- const http = require('http');
3
- const fs = require('fs');
4
- const path = require('path');
5
- const os = require('os');
6
- const crypto = require('crypto');
7
-
8
- // Cross-platform home directory resolution
9
- const HOME = process.env.HOME || process.env.USERPROFILE || os.homedir();
10
-
11
- const CONFIG_DIR = path.join(HOME, '.config', 'xp-gate');
12
- const CACHE_DIR = path.join(CONFIG_DIR, 'cache');
13
-
14
- async function downloadFromGitHub(repo, pathInRepo, version = 'main') {
15
- const url = `https://raw.githubusercontent.com/${repo}/${version}/${pathInRepo}`;
16
- const cacheFile = path.join(CACHE_DIR, `${repo}-${pathInRepo.replace(/\//g, '-')}.md`);
17
-
18
- fs.mkdirSync(CACHE_DIR, { recursive: true });
19
-
20
- await downloadWithRetry(url, cacheFile);
21
-
22
- return cacheFile;
23
- }
24
-
25
- async function downloadWithRetry(url, dest, retries = 3) {
26
- for (let attempt = 1; attempt <= retries; attempt++) {
27
- try {
28
- await downloadFile(url, dest);
29
- return;
30
- } catch (err) {
31
- if (attempt === retries) throw err;
32
- console.log(`Retry ${attempt}/${retries}...`);
33
- await sleep(1000 * attempt);
34
- }
35
- }
36
- }
37
-
38
- async function downloadFile(url, dest) {
39
- return new Promise((resolve, reject) => {
40
- const file = fs.createWriteStream(dest);
41
-
42
- https.get(url, { timeout: 30000 }, (response) => {
43
- if (response.statusCode === 301 || response.statusCode === 302) {
44
- file.close();
45
- fs.unlinkSync(dest);
46
- downloadFile(response.headers.location, dest).then(resolve).catch(reject);
47
- return;
48
- }
49
-
50
- if (response.statusCode !== 200) {
51
- file.close();
52
- reject(new Error(`HTTP ${response.statusCode}`));
53
- return;
54
- }
55
-
56
- response.pipe(file);
57
- file.on('finish', () => {
58
- file.close();
59
- resolve();
60
- });
61
- }).on('error', (err) => {
62
- file.close();
63
- if (fs.existsSync(dest)) fs.unlinkSync(dest);
64
- reject(err);
65
- });
66
- });
67
- }
68
-
69
- async function downloadTarball(repo, version = 'main') {
70
- const tarballUrl = `https://api.github.com/repos/${repo}/tarball/${version}`;
71
- const cacheFile = path.join(CACHE_DIR, `${repo.replace('/', '-')}.tgz`);
72
-
73
- fs.mkdirSync(CACHE_DIR, { recursive: true });
74
-
75
- await downloadWithRetry(tarballUrl, cacheFile);
76
-
77
- return cacheFile;
78
- }
79
-
80
- function sleep(ms) {
81
- return new Promise(resolve => setTimeout(resolve, ms));
82
- }
83
-
84
- function verifyChecksum(file, expectedChecksum) {
85
- const hash = crypto.createHash('sha256');
86
- const content = fs.readFileSync(file);
87
- hash.update(content);
88
- const actualChecksum = hash.digest('hex');
89
-
90
- if (actualChecksum !== expectedChecksum) {
91
- fs.unlinkSync(file);
92
- throw new Error(`Checksum mismatch: expected ${expectedChecksum}, got ${actualChecksum}`);
93
- }
94
- }
95
-
96
- module.exports = { downloadFromGitHub, downloadTarball, downloadWithRetry, verifyChecksum };
@@ -1,55 +0,0 @@
1
- # memory.md — Ralph Loop 状态与记忆结构(AHE 组件分解)
2
-
3
- > **本文是 `skills/ralph-loop/SKILL.md` 中状态管理的 AHE 对齐展开。**
4
-
5
- ## 组件职责
6
-
7
- 定义 Ralph Loop 的进度持久化 Schema、learnings 分类结构。
8
-
9
- ## Progress Log Schema (`progress.log` — YAML)
10
-
11
- ```yaml
12
- req_progress:
13
- completed_count: 3
14
- total_count: 5
15
- current_req: REQ-XXX-004
16
- status: running|completed|blocked
17
- test_infra_status: generated|existing|skipped|fallback
18
-
19
- learnings:
20
- permanent:
21
- - pattern: "描述持久模式"
22
- evidence: "来源证据"
23
- contextual:
24
- - pattern: "描述当前 REQ 有效模式"
25
- expires_on: "过期条件"
26
-
27
- cost:
28
- req_number: 3
29
- tokens_used: 15000
30
- cumulative_tokens: 85000
31
- threshold: 200000
32
- ```
33
-
34
- ## Test Infra Status 字段
35
-
36
- | 值 | 含义 |
37
- |---|------|
38
- | `generated` | 本 REQ 首次生成 test-utils.ts |
39
- | `existing` | test-utils.ts 已存在且接口完整 |
40
- | `skipped` | 非首次 REQ,跳过 test-infra 检查(缓存结果)|
41
- | `fallback` | test-infra dispatch 失败,使用 inline 生成(含 warning)|
42
-
43
- ## Learnings 分类
44
-
45
- | 类型 | 生命周期 | 持久化 | 示例 |
46
- |------|---------|--------|------|
47
- | `permanent` | 跨所有 REQ | progress.log | "项目使用 ESLint strict 模式" |
48
- | `contextual` | 当前 REQ 内 | progress.log (过期标记) | "REQ 003 使用 Zod 验证 schema" |
49
-
50
- ## AHE 分类
51
-
52
- | 字段 | 值 |
53
- |------|---|
54
- | 组件类型 | Long-Term Memory |
55
- | 修改频率预期 | 低 |
@@ -1,54 +0,0 @@
1
- # middleware.md — Ralph Loop 迭代控制逻辑(AHE 组件分解)
2
-
3
- > **本文是 `skills/ralph-loop/SKILL.md` 中迭代流程的 AHE 对齐展开。**
4
-
5
- ## 组件职责
6
-
7
- 定义 Ralph Loop 的状态机:REQ 级迭代控制、熔断机制、中断续传逻辑。
8
-
9
- ## 状态机
10
-
11
- ```
12
- REQ N → 加载 learnings → 测试基础设施检查 → [infra missing?] → test_infra_dispatch
13
- │ │ │
14
- │ │ infra ready │ FAIL (max 2 retry)
15
- │ ▼ ▼
16
- │ TDD → 回归测试 ←───────────── BLOCK/fallback
17
- │ │
18
- │ PASS → REQ N+1
19
- │ FAIL → 重试 (max 3) → BLOCK → 用户决策
20
- │ ALL_REQS_COMPLETE → 结束,返回控制权给 sprint-flow orchestrator
21
- ```
22
-
23
- ## 测试基础设施状态机
24
-
25
- ```
26
- test_infra_check → [needs infra?] → test_infra_dispatch → FAIL → retry (max 2)
27
- │ │ │ │
28
- │ │ infra ready │ pass │ pass
29
- ▼ ▼ ▼ ▼
30
- in_progress test_infra_ready ─────────→ TDD → regression BLOCK/fallback
31
- ```
32
-
33
- ## 熔断机制
34
-
35
- | 触发条件 | 动作 |
36
- |---------|------|
37
- | TDD 失败 > 3 次 | BLOCK + 用户决策 |
38
- | Token 使用超阈值 | BLOCK + 用户决策 |
39
- | 回归测试失败 | 记录到 progress.log,通知 sprint-flow orchestrator |
40
- | test-infra dispatch 失败 (max 2 retry) | BLOCK 或 fallback inline 生成(记录 warning)|
41
-
42
- ## 暂停点
43
-
44
- | 暂停点 | 触发条件 | 恢复 |
45
- |--------|---------|------|
46
- | REQ 完成 | 自动暂停等待确认 | 确认后继续下一个 REQ |
47
- | 熔断 | 自动触发 | 用户决策后继续或放弃 |
48
-
49
- ## AHE 分类
50
-
51
- | 字段 | 值 |
52
- |------|---|
53
- | 组件类型 | Middleware |
54
- | 修改频率预期 | 高 |
@@ -1,39 +0,0 @@
1
- # skill-invocations.md — Ralph Loop 技能调用(AHE 组件分解)
2
-
3
- > **本文是 `skills/ralph-loop/SKILL.md` 中技能调用链的 AHE 对齐展开。**
4
-
5
- ## 组件职责
6
-
7
- 提供 Ralph Loop 在每个 REQ 执行中的技能调用链。
8
-
9
- ## 完整调用链
10
-
11
- | 步骤 | Skill | 来源 | 参数 |
12
- |------|-------|------|------|
13
- | 0 | 测试基础设施检查 | 内置 | 检查 test-utils.ts 存在性 + 接口契约(createTestApp, withTestDb)|
14
- | 1 | `test-driven-development` | superpowers | `--lang` 注入 + TDD 铁律 + Mock 边界(覆盖默认策略)|
15
- | 2 | `learn` | gstack | classification: permanent/contextual |
16
- | 3 | `requesting-code-review` | superpowers | REQ 完成后评审 |
17
-
18
- ## test-infra dispatch 节点
19
-
20
- 当 test-utils.ts 不存在或接口缺失时:
21
-
22
- ```
23
- task(category="unspecified-high", load_skills=["test-driven-development"], prompt="生成测试基础设施:createTestApp() + withTestDb()。TDD 铁律:先写测试再实现。")
24
- ```
25
-
26
- - retry max 2 次
27
- - 仍失败 → BLOCK 或 fallback inline 生成(记录 warning)
28
- - 成功后与业务代码合并为同一 commit
29
-
30
- ## 上游依赖
31
-
32
- 从 `specification.yaml` 获取 REQ 列表(由 `to-issues` skill 生成)
33
-
34
- ## AHE 分类
35
-
36
- | 字段 | 值 |
37
- |------|---|
38
- | 组件类型 | Skill Invocation Chain |
39
- | 修改频率预期 | 中 |
@@ -1,24 +0,0 @@
1
- # system-prompt.md — Ralph Loop 行为定义(AHE 组件分解)
2
-
3
- > **本文是 `skills/ralph-loop/SKILL.md` 核心原则的 AHE 对齐展开。**
4
-
5
- ## 核心职责
6
-
7
- 定义 Ralph Loop (Phase 2 BUILD 默认模式) 的行为边界:逐 REQ/切片 迭代构建,每个 REQ dispatch 独立 subagent,干净上下文,全量回归测试。
8
-
9
- ## 核心原则 (AHE 对齐)
10
-
11
- | # | 原则 | 说明 |
12
- |---|------|------|
13
- | 1 | 逐 REQ 迭代 | 一次处理一个 REQ/切片,干净上下文,避免上下文线性膨胀 |
14
- | 2 | Token 节约 | 比旧并行模式节约 40-67% token |
15
- | 3 | 全量回归 | 每个 REQ 完成后跑全量测试 |
16
- | 4 | 进度持久化 | progress.log 持久化 learnings/permanent/contextual |
17
- | 5 | 可中断续传 | 中断后可从最后完成 REQ 继续 |
18
-
19
- ## AHE 分类
20
-
21
- | 字段 | 值 |
22
- |------|---|
23
- | 组件类型 | System Prompt |
24
- | 修改频率预期 | 低 |
@@ -1,32 +0,0 @@
1
- # tool-descriptions.md — Ralph Loop 各步骤工具链(AHE 组件分解)
2
-
3
- > **本文是 `skills/ralph-loop/SKILL.md` 中构建步骤的 AHE 对齐展开。**
4
-
5
- ## 组件职责
6
-
7
- 描述 Ralph Loop 中每个 REQ 执行的具体工具链:TDD 流、回归测试、成本监控。
8
-
9
- ## 构建步骤
10
-
11
- | 步骤 | 动作 | 说明 |
12
- |------|------|------|
13
- | 1 | 加载当前 REQ | 从 specification.yaml 获取下一个 READY REQ |
14
- | 2 | 加载 learnings | 从 progress.log 加载 permanent + contextual learnings |
15
- | 3 | 测试基础设施检查 | 检查 test-utils.ts 是否存在且导出 createTestApp()、withTestDb()。不存在 → dispatch test-infra subagent,retry max 2,失败 → BLOCK/fallback |
16
- | 4 | TDD (RED→GREEN→REFACTOR) | test-driven-development skill(含 TDD 铁律 + Mock 边界注入)|
17
- | 5 | 全量回归测试 | 运行全部现有测试 |
18
- | 6 | 测试先行比率检查 (L1b) | 新增测试行数 / (新增测试 + 新增实现) ≥ 40% |
19
- | 7 | 记录 progress.log | 将 learnings 分类存储 (permanent/contextual) |
20
- | 8 | 成本检查 | token 使用量是否超阈值 |
21
- | 9 | 完成/继续 | 所有 REQ 完成 → 结束;否则 → 下一个 REQ |
22
-
23
- ## 语言特定
24
-
25
- 通过 `--lang` 注入对应 TDD skill: `springboot-tdd`, `django-tdd`, `golang-testing`
26
-
27
- ## AHE 分类
28
-
29
- | 字段 | 值 |
30
- |------|---|
31
- | 组件类型 | Tool Description |
32
- | 修改频率预期 | 高 |