@boyingliu01/xp-gate 0.19.3 → 0.20.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 (177) hide show
  1. package/adapters/gate-4.sh +83 -18
  2. package/adapters/gate-8.sh +115 -29
  3. package/adapters/iac.sh +1 -1
  4. package/adapters/powershell.sh +60 -5
  5. package/adapters/python.sh +38 -4
  6. package/adapters/typescript.sh +15 -60
  7. package/bin/xp-gate.js +15 -16
  8. package/build-integrity/__tests__/import-resolver.test.ts +59 -0
  9. package/build-integrity/gate-10.ts +17 -2
  10. package/gate-10.sh +35 -2
  11. package/gate-4.sh +110 -21
  12. package/gate-8.sh +115 -29
  13. package/hooks/gate-10.sh +35 -2
  14. package/hooks/gate-4.sh +110 -21
  15. package/hooks/gate-8.sh +115 -29
  16. package/hooks/lib/jscpd-run.sh +63 -0
  17. package/hooks/lib/sprint-gate-report.sh +58 -0
  18. package/hooks/lib/test-failure.sh +152 -0
  19. package/hooks/lib/typecheck.sh +141 -0
  20. package/hooks/lib/validate-code-walkthrough.cjs +43 -5
  21. package/hooks/post-merge +100 -0
  22. package/hooks/pre-commit +514 -184
  23. package/hooks/pre-push +75 -19
  24. package/lib/__tests__/bootstrap.test.js +23 -41
  25. package/lib/__tests__/check-version.test.js +8 -1
  26. package/lib/__tests__/detect-deps.test.js +16 -0
  27. package/lib/__tests__/doctor-drift-round2.test.js +212 -0
  28. package/lib/__tests__/doctor-executed-surface.test.js +209 -0
  29. package/lib/__tests__/doctor-hook-drift.test.js +329 -0
  30. package/lib/__tests__/doctor-machine-mutation.test.js +116 -0
  31. package/lib/__tests__/doctor-module-drift.test.js +206 -0
  32. package/lib/__tests__/doctor-module-resolution-parity.test.js +314 -0
  33. package/lib/__tests__/doctor-path-equivalence.test.js +59 -0
  34. package/lib/__tests__/doctor-repair-boundary.test.js +52 -0
  35. package/lib/__tests__/doctor-tui-skill-freshness.test.js +105 -0
  36. package/lib/__tests__/doctor.test.js +155 -0
  37. package/lib/__tests__/gate-runner.test.js +19 -3
  38. package/lib/__tests__/helpers/lib-sandbox.cjs +55 -0
  39. package/lib/__tests__/init.test.js +44 -17
  40. package/lib/__tests__/install-skill-full-dir.test.js +151 -0
  41. package/lib/__tests__/install-skill.test.js +51 -132
  42. package/lib/__tests__/language-tools-detect.test.js +158 -0
  43. package/lib/__tests__/phase-transition.test.js +15 -1
  44. package/lib/__tests__/qoder-delphi-agents.test.js +285 -0
  45. package/lib/__tests__/shared-paths.test.js +20 -0
  46. package/lib/__tests__/shared-utils.test.js +69 -0
  47. package/lib/__tests__/skill-flags.test.js +48 -0
  48. package/lib/__tests__/ui-detector.test.ts +6 -2
  49. package/lib/__tests__/uninstall.test.js +20 -0
  50. package/lib/__tests__/update-hooks.test.js +12 -0
  51. package/lib/__tests__/update-skill.test.js +7 -4
  52. package/lib/detect-deps.js +3 -20
  53. package/lib/doctor-tui.js +22 -5
  54. package/lib/doctor.js +1178 -31
  55. package/lib/init.js +184 -33
  56. package/lib/install-cmd.js +7 -4
  57. package/lib/install-skill.js +38 -95
  58. package/lib/language-tools.js +83 -4
  59. package/lib/mattpocock-license-verdict.json +1 -1
  60. package/lib/shared-paths.js +5 -2
  61. package/lib/shared-utils.js +47 -3
  62. package/lib/uninstall.js +12 -0
  63. package/lib/update-hooks.js +4 -4
  64. package/lib/update-skill.js +6 -7
  65. package/mock-policy/AGENTS.md +4 -4
  66. package/mutation/AGENTS.md +4 -4
  67. package/mutation/__tests__/gate-m.test.ts +66 -14
  68. package/mutation/__tests__/mutmut-runner.test.ts +91 -42
  69. package/mutation/__tests__/test-filter-parity.test.ts +37 -0
  70. package/mutation/gate-m.ts +21 -8
  71. package/package.json +2 -2
  72. package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
  73. package/plugins/claude-code/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +0 -1
  74. package/plugins/claude-code/skills/delphi-review/AGENTS.md +4 -4
  75. package/plugins/claude-code/skills/delphi-review/SKILL.md +98 -14
  76. package/plugins/claude-code/skills/delphi-review/references/code-walkthrough.md +36 -3
  77. package/plugins/claude-code/skills/delphi-review/references/requirements.md +6 -6
  78. package/plugins/claude-code/skills/sprint-flow/AGENTS.md +4 -4
  79. package/plugins/claude-code/skills/sprint-flow/references/phase-2-design.md +3 -3
  80. package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +4 -4
  81. package/plugins/dsh/README.md +169 -4
  82. package/plugins/dsh/dsh-overlay.patch.yml +67 -0
  83. package/plugins/dsh/package.json +1 -1
  84. package/plugins/dsh/scripts/prepack.cjs +24 -19
  85. package/plugins/dsh/setup-dsh.ps1 +160 -0
  86. package/plugins/dsh/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +0 -1
  87. package/plugins/dsh/skills/delphi-review/AGENTS.md +4 -4
  88. package/plugins/dsh/skills/delphi-review/SKILL.md +98 -14
  89. package/plugins/dsh/skills/delphi-review/references/code-walkthrough.md +36 -3
  90. package/plugins/dsh/skills/delphi-review/references/requirements.md +6 -6
  91. package/plugins/dsh/skills/sprint-flow/AGENTS.md +4 -4
  92. package/plugins/dsh/skills/sprint-flow/references/phase-2-design.md +3 -3
  93. package/plugins/dsh/skills/test-specification-alignment/AGENTS.md +4 -4
  94. package/plugins/dsh/src/command.test.ts +66 -2
  95. package/plugins/dsh/src/command.ts +36 -14
  96. package/plugins/dsh/src/contract.test.ts +31 -1
  97. package/plugins/dsh/src/delphi-config.test.ts +190 -0
  98. package/plugins/dsh/src/delphi-config.ts +223 -0
  99. package/plugins/dsh/src/delphi-run.test.ts +181 -0
  100. package/plugins/dsh/src/delphi-run.ts +129 -0
  101. package/plugins/dsh/src/delphi.test.ts +222 -0
  102. package/plugins/dsh/src/delphi.ts +247 -0
  103. package/plugins/dsh/src/dialect.test.ts +59 -0
  104. package/plugins/dsh/src/dialect.ts +40 -0
  105. package/plugins/dsh/src/index.ts +70 -4
  106. package/plugins/dsh/src/skills.test.ts +26 -19
  107. package/plugins/opencode/package.json +1 -1
  108. package/plugins/opencode/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +0 -1
  109. package/plugins/opencode/skills/delphi-review/AGENTS.md +4 -4
  110. package/plugins/opencode/skills/delphi-review/SKILL.md +98 -14
  111. package/plugins/opencode/skills/delphi-review/references/code-walkthrough.md +36 -3
  112. package/plugins/opencode/skills/delphi-review/references/requirements.md +6 -6
  113. package/plugins/opencode/skills/sprint-flow/AGENTS.md +4 -4
  114. package/plugins/opencode/skills/sprint-flow/references/phase-2-design.md +3 -3
  115. package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +4 -4
  116. package/plugins/qoder/agents/delphi-architecture.md +1 -1
  117. package/plugins/qoder/agents/delphi-feasibility.md +1 -1
  118. package/plugins/qoder/agents/delphi-technical.md +1 -1
  119. package/plugins/qoder/plugin.json +1 -1
  120. package/plugins/qoder/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +0 -1
  121. package/plugins/qoder/skills/delphi-review/AGENTS.md +4 -4
  122. package/plugins/qoder/skills/delphi-review/SKILL.md +98 -14
  123. package/plugins/qoder/skills/delphi-review/references/code-walkthrough.md +36 -3
  124. package/plugins/qoder/skills/delphi-review/references/requirements.md +6 -6
  125. package/plugins/qoder/skills/sprint-flow/AGENTS.md +4 -4
  126. package/plugins/qoder/skills/sprint-flow/references/phase-2-design.md +3 -3
  127. package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +4 -4
  128. package/principles/AGENTS.md +9 -8
  129. package/principles/__tests__/adapter-metrics.test.ts +292 -0
  130. package/principles/__tests__/analyzer.test.ts +29 -6
  131. package/principles/__tests__/baseline-prune-timeout.test.ts +71 -0
  132. package/principles/__tests__/baseline-prune.test.ts +338 -0
  133. package/principles/__tests__/boy-scout-init-baseline.test.ts +167 -0
  134. package/principles/__tests__/boy-scout-integration.test.ts +97 -6
  135. package/principles/__tests__/boy-scout.test.ts +253 -7
  136. package/principles/__tests__/config-enforcement.test.ts +469 -0
  137. package/principles/__tests__/config-git-timeout.test.ts +66 -0
  138. package/principles/__tests__/direct-execution.test.ts +187 -0
  139. package/principles/__tests__/index.test.ts +16 -3
  140. package/principles/__tests__/warnings-baseline-budgets.test.ts +74 -0
  141. package/principles/adapters/typescript.ts +613 -13
  142. package/principles/analyzer.ts +22 -0
  143. package/principles/baseline-prune.ts +217 -0
  144. package/principles/boy-scout.ts +516 -56
  145. package/principles/config.ts +362 -54
  146. package/principles/direct-execution.ts +60 -0
  147. package/principles/index.ts +40 -17
  148. package/principles/rules/__tests__/clean-code/many-exports.test.ts +16 -10
  149. package/principles/rules/clean-code/code-duplication.ts +8 -8
  150. package/principles/rules/clean-code/deep-nesting.ts +8 -8
  151. package/principles/rules/clean-code/god-class.ts +8 -8
  152. package/principles/rules/clean-code/large-file.ts +15 -11
  153. package/principles/rules/clean-code/long-function.ts +8 -8
  154. package/principles/rules/clean-code/magic-numbers.ts +10 -7
  155. package/principles/rules/clean-code/many-exports.ts +7 -7
  156. package/principles/rules/clean-code/missing-error-handling.ts +5 -5
  157. package/principles/rules/clean-code/too-many-params.ts +8 -8
  158. package/principles/rules/clean-code/unused-imports.ts +5 -5
  159. package/principles/rules/solid/dip.ts +18 -8
  160. package/principles/rules/solid/isp.ts +9 -8
  161. package/principles/rules/solid/lsp.ts +6 -6
  162. package/principles/rules/solid/ocp.ts +6 -5
  163. package/principles/rules/solid/srp.ts +7 -8
  164. package/principles/types.ts +15 -0
  165. package/scripts/__tests__/sync-package-content.test.js +28 -0
  166. package/scripts/delphi-external-review.cjs +193 -10
  167. package/scripts/sync-package-content.js +20 -10
  168. package/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +0 -1
  169. package/skills/delphi-review/AGENTS.md +4 -4
  170. package/skills/delphi-review/SKILL.md +98 -14
  171. package/skills/delphi-review/references/code-walkthrough.md +36 -3
  172. package/skills/delphi-review/references/requirements.md +6 -6
  173. package/skills/sprint-flow/AGENTS.md +4 -4
  174. package/skills/sprint-flow/references/phase-2-design.md +3 -3
  175. package/skills/test-specification-alignment/AGENTS.md +4 -4
  176. package/lib/__tests__/download-skill.test.js +0 -281
  177. package/lib/download-skill.js +0 -96
@@ -0,0 +1,217 @@
1
+ /**
2
+ * Stale-entry pruning for `.warnings-baseline.json` (#452 REQ-3).
3
+ *
4
+ * `classifyFiles` in boy-scout.ts has always parsed the git `D` status into a
5
+ * `deleted` list, but nothing consumed it, so the baseline accumulated entries
6
+ * for files that no longer exist. A stale entry is not merely untidy: if a path
7
+ * is later re-created it inherits the old allowance, and the Boy Scout gate then
8
+ * compares against a phantom history.
9
+ *
10
+ * Lives in its own module because boy-scout.ts is already at the 10-export
11
+ * clean-code.many-exports limit.
12
+ */
13
+
14
+ import { execFileSync } from 'child_process';
15
+ import { existsSync } from 'fs';
16
+ import { resolve } from 'path';
17
+
18
+ /**
19
+ * Ceiling on any single git call. Generous enough that a healthy `git ls-files`
20
+ * over a very large index never trips it, short enough that a wedged git degrades
21
+ * to "keep the entry" rather than hanging the hook this runs inside.
22
+ */
23
+ const GIT_TIMEOUT_MS = 30_000;
24
+
25
+ /** Outcome of a baseline prune: which entries survived, and which were dropped. */
26
+ export interface PruneResult {
27
+ /** Baseline keys confirmed to still exist (or that we could not disprove). */
28
+ kept: string[];
29
+ /** Baseline keys safe to drop: absent from disk AND no longer tracked by git. */
30
+ removed: string[];
31
+ /** Set when the baseline could not be parsed; nothing is removed in that case. */
32
+ error?: string;
33
+ }
34
+
35
+ /** How the project root's filesystem resolves path names. */
36
+ export interface PruneOptions {
37
+ /**
38
+ * Whether `A.ts` and `a.ts` name the same file at this root.
39
+ *
40
+ * Defaults to platform detection. Tests pass it explicitly so both branches are
41
+ * exercisable from a single checkout.
42
+ */
43
+ caseInsensitivePaths?: boolean;
44
+ }
45
+
46
+ /**
47
+ * Whether the host filesystem folds case by default.
48
+ *
49
+ * Windows always does; a default APFS/_HFS+ volume on macOS does. A deliberately
50
+ * case-sensitive macOS volume is misread as folding here, and the consequence is
51
+ * the retain direction (an entry is kept rather than dropped) -- the fail-safe side
52
+ * of this comparison.
53
+ */
54
+ function filesystemFoldsCase(): boolean {
55
+ return process.platform === 'win32' || process.platform === 'darwin';
56
+ }
57
+
58
+ /**
59
+ * Identify baseline entries whose file no longer exists.
60
+ *
61
+ * REMOVAL IS DOUBLE-CONFIRMED, on purpose. Both signals are wrong on their own:
62
+ * - `existsSync` alone: a sparse checkout, a mid-flight rebase, a symlinked
63
+ * worktree, or a case-insensitive filesystem all make a live tracked file
64
+ * look absent.
65
+ * - `git ls-files` alone: a file can be present but deliberately untracked.
66
+ * So an entry is dropped only when the path is missing from disk AND git reports
67
+ * it untracked. If git cannot be consulted at all, everything is kept -- failing
68
+ * closed retains a legitimate allowance, whereas failing open would discard one.
69
+ *
70
+ * Pure with respect to `baselineJson`: the caller decides when to persist.
71
+ */
72
+ export function pruneBaselineEntries(
73
+ baselineJson: string,
74
+ projectRoot: string,
75
+ options: PruneOptions = {},
76
+ ): PruneResult {
77
+ const files = parseBaselineFiles(baselineJson);
78
+ if (typeof files === 'string') {
79
+ return { kept: [], removed: [], error: files };
80
+ }
81
+
82
+ const caseInsensitivePaths = options.caseInsensitivePaths ?? filesystemFoldsCase();
83
+ const tracked = listTrackedFiles(projectRoot, caseInsensitivePaths);
84
+ const kept: string[] = [];
85
+ const removed: string[] = [];
86
+
87
+ if (tracked === null) {
88
+ // Nothing can be proven stale without the index, so every entry is kept --
89
+ // but that must be said out loud. Reported as "no stale entries" it is
90
+ // indistinguishable from a successful prune, which is the same silent no-op
91
+ // this module was introduced to remove.
92
+ return {
93
+ kept: Object.keys(files),
94
+ removed,
95
+ error: 'could not consult the git index, so no entry could be proven stale',
96
+ };
97
+ }
98
+
99
+ for (const key of Object.keys(files)) {
100
+ if (isEntryStale(key, projectRoot, tracked, caseInsensitivePaths)) {
101
+ removed.push(key);
102
+ } else {
103
+ kept.push(key);
104
+ }
105
+ }
106
+
107
+ return { kept, removed };
108
+ }
109
+
110
+ /**
111
+ * Extract the path -> entry map, or a string describing why it is unusable.
112
+ *
113
+ * The on-disk format is a FLAT map (`{"src/a.ts": {totalWarnings, lastAnalyzed}}`),
114
+ * not a `{files: {...}}` wrapper. A `files` wrapper is still accepted for forwards
115
+ * compatibility, but the flat form is what `saveBaseline` actually writes.
116
+ */
117
+ function parseBaselineFiles(baselineJson: string): Record<string, unknown> | string {
118
+ let parsed: unknown;
119
+ try {
120
+ parsed = JSON.parse(baselineJson);
121
+ } catch (error) {
122
+ const detail = error instanceof Error ? error.message : String(error);
123
+ // Fail safe: an unparsable baseline must never be reported as "all stale",
124
+ // which is exactly how the reporter's entries disappeared.
125
+ return `could not parse baseline: ${detail}`;
126
+ }
127
+
128
+ if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
129
+ return 'baseline is not a JSON object';
130
+ }
131
+
132
+ const record = parsed as Record<string, unknown>;
133
+ const nested = record.files;
134
+ if (nested !== null && typeof nested === 'object' && !Array.isArray(nested)) {
135
+ return nested as Record<string, unknown>;
136
+ }
137
+
138
+ // Keep only object-valued keys: scalars at the top level are metadata, not
139
+ // path entries, and a path whose entry is a bare number is the malformed shape
140
+ // that assertValidEntry rejects elsewhere (#455).
141
+ const entries: Record<string, unknown> = {};
142
+ for (const [key, value] of Object.entries(record)) {
143
+ if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
144
+ entries[key] = value;
145
+ }
146
+ }
147
+ return entries;
148
+ }
149
+
150
+ /**
151
+ * Whether a baseline key is safe to drop.
152
+ *
153
+ * Callers must have confirmed the index is readable; an unavailable git is handled
154
+ * by the caller, which reports it rather than deciding silently here.
155
+ */
156
+ function isEntryStale(
157
+ key: string,
158
+ projectRoot: string,
159
+ tracked: Set<string>,
160
+ caseInsensitivePaths: boolean,
161
+ ): boolean {
162
+ if (tracked.has(normalizeBaselinePath(key, caseInsensitivePaths))) return false;
163
+ try {
164
+ return !existsSync(resolve(projectRoot, key));
165
+ } catch {
166
+ // If the filesystem cannot answer, keep the entry.
167
+ return false;
168
+ }
169
+ }
170
+
171
+ /**
172
+ * Canonical form for comparing a baseline key with a git index path.
173
+ *
174
+ * The git index is authoritative for casing, so the default comparison is exact
175
+ * after separator normalization. Case folding is applied only when the filesystem
176
+ * at this root folds case, because that is the one setting where `Src/A.ts` in the
177
+ * index and a baseline key of `src/A.ts` name the same file. On a case-sensitive
178
+ * filesystem folding both sides conflated two distinct files, so a key matching
179
+ * neither spelling exactly still found a "tracked" hit and the phantom allowance
180
+ * was retained forever -- the failure mode this module was introduced to remove.
181
+ */
182
+ function normalizeBaselinePath(value: string, caseInsensitivePaths: boolean): string {
183
+ const separators = value.replace(/\\/g, '/');
184
+ return caseInsensitivePaths ? separators.toLowerCase() : separators;
185
+ }
186
+
187
+ /** Tracked paths per the git index, or null when git cannot be consulted. */
188
+ function listTrackedFiles(projectRoot: string, caseInsensitivePaths: boolean): Set<string> | null {
189
+ try {
190
+ const out = execFileSync('git', ['ls-files'], {
191
+ cwd: projectRoot,
192
+ encoding: 'utf-8',
193
+ stdio: ['ignore', 'pipe', 'ignore'],
194
+ // `execFileSync` defaults to a 1 MiB buffer. A repo with a few tens of
195
+ // thousands of tracked paths exceeds that, and the throw would be caught
196
+ // below and reported as "git unavailable" -- silently disabling pruning at
197
+ // exactly the scale where a stale baseline is most likely. Raised well past
198
+ // any realistic index size; the failure mode we care about is correctness,
199
+ // not memory.
200
+ maxBuffer: 256 * 1024 * 1024,
201
+ // Bound the wait. This runs inside a pre-commit hook, so an unbounded call
202
+ // turns a wedged git (locked index, stalled credential prompt, network-backed
203
+ // filesystem) into a commit that never returns -- strictly worse than the
204
+ // silent no-op the maxBuffer above fixed. On expiry the catch below reports
205
+ // "git unavailable" and every entry is kept, which fails closed.
206
+ timeout: GIT_TIMEOUT_MS,
207
+ });
208
+ const set = new Set<string>();
209
+ for (const line of out.split('\n')) {
210
+ const trimmed = line.trim();
211
+ if (trimmed) set.add(normalizeBaselinePath(trimmed, caseInsensitivePaths));
212
+ }
213
+ return set;
214
+ } catch {
215
+ return null;
216
+ }
217
+ }