@boyingliu01/xp-gate 0.18.5 → 0.19.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.
- package/adapter-common.sh +27 -0
- package/adapters/gate-3.sh +0 -0
- package/adapters/gate-4.sh +0 -0
- package/adapters/gate-7.sh +0 -0
- package/adapters/gate-8.sh +0 -0
- package/adapters/gate-9.sh +0 -0
- package/adapters/powershell.sh +114 -35
- package/adapters/typescript.sh +8 -8
- package/build-integrity/__tests__/import-resolver.test.ts +22 -0
- package/build-integrity/__tests__/orchestrator.test.ts +1 -2
- package/build-integrity/gate-10.ts +7 -0
- package/gate-12-file-hygiene.sh +230 -0
- package/hooks/adapter-common.sh +27 -0
- package/hooks/gate-10.sh +105 -0
- package/hooks/gate-12-file-hygiene.sh +0 -0
- package/hooks/lib/validate-code-walkthrough.cjs +125 -0
- package/hooks/pre-commit +130 -33
- package/hooks/pre-push +15 -91
- package/lib/__tests__/check-version.test.js +67 -46
- package/lib/__tests__/doctor.test.js +7 -6
- package/lib/__tests__/gate-runner.test.js +267 -0
- package/lib/__tests__/init.test.js +32 -0
- package/lib/__tests__/npm-package.test.js +13 -0
- package/lib/__tests__/phase-transition.test.js +604 -12
- package/lib/__tests__/sprint-discovery.test.ts +0 -1
- package/lib/__tests__/sprint-status-rework.test.js +100 -1
- package/lib/__tests__/update-hooks.test.js +143 -10
- package/lib/__tests__/upgrade-exec.test.js +73 -45
- package/lib/__tests__/upgrade-skill-sync.test.js +1 -1
- package/lib/__tests__/upgrade.test.js +12 -11
- package/lib/doctor.js +2 -1
- package/lib/gate-runner.js +68 -23
- package/lib/init.js +34 -5
- package/lib/install-skill.js +1 -0
- package/lib/mattpocock-license-verdict.json +1 -1
- package/lib/phase-transition.js +203 -23
- package/lib/shared-paths.js +2 -0
- package/lib/sprint-status.js +15 -4
- package/lib/test-alignment.ts +3 -13
- package/lib/update-hooks.js +129 -15
- package/mock-policy/AGENTS.md +4 -4
- package/mock-policy/__tests__/gate-m3.test.ts +0 -5
- package/mock-policy/__tests__/scope-scanner.test.ts +8 -2
- package/mutation/AGENTS.md +3 -3
- package/mutation/__tests__/mutmut-runner.test.ts +443 -2
- package/mutation/__tests__/update-baseline.test.ts +0 -2
- package/mutation/runners/mutmut-runner.ts +67 -17
- package/package.json +9 -3
- package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
- package/plugins/claude-code/skills/clipboard-vision/SKILL.md +47 -0
- package/plugins/claude-code/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +65 -0
- package/plugins/claude-code/skills/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +185 -0
- package/plugins/claude-code/skills/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +138 -0
- package/plugins/claude-code/skills/clipboard-vision/__tests__/clipboard-vision.test.cjs +273 -0
- package/plugins/claude-code/skills/clipboard-vision/describe-clipboard.ps1 +115 -0
- package/plugins/claude-code/skills/clipboard-vision/describe-clipboard.sh +161 -0
- package/plugins/claude-code/skills/clipboard-vision/scripts/clipboard-vision.ps1 +86 -0
- package/plugins/claude-code/skills/clipboard-vision/scripts/clipboard-vision.sh +137 -0
- package/plugins/claude-code/skills/delphi-review/.delphi-config.json.example +24 -27
- package/plugins/claude-code/skills/delphi-review/AGENTS.md +12 -11
- package/plugins/claude-code/skills/delphi-review/INSTALL.md +15 -17
- package/plugins/claude-code/skills/delphi-review/SKILL.md +30 -29
- package/plugins/claude-code/skills/delphi-review/opencode.json.delphi.example +6 -10
- package/plugins/claude-code/skills/delphi-review/references/code-walkthrough.md +33 -49
- package/plugins/claude-code/skills/delphi-review/references/requirements.md +66 -54
- package/plugins/claude-code/skills/delphi-review/references/round-templates.md +7 -7
- package/plugins/claude-code/skills/sprint-flow/AGENTS.md +4 -4
- package/plugins/claude-code/skills/sprint-flow/SKILL.md +4 -4
- package/plugins/claude-code/skills/sprint-flow/references/components/skill-invocations.md +2 -2
- package/plugins/claude-code/skills/sprint-flow/references/force-levels.md +37 -35
- package/plugins/claude-code/skills/sprint-flow/references/orchestration-rules.md +1 -1
- package/plugins/claude-code/skills/sprint-flow/references/phase-1-prep.md +1 -1
- package/plugins/claude-code/skills/sprint-flow/references/phase-2-design.md +24 -14
- package/plugins/claude-code/skills/sprint-flow/references/phase-4-verify.md +6 -6
- package/plugins/claude-code/skills/sprint-flow/references/phase-overview.md +2 -2
- package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +3 -3
- package/plugins/dsh/README.md +25 -0
- package/plugins/dsh/cordis.patch.yml +7 -0
- package/plugins/dsh/package.json +70 -0
- package/plugins/dsh/scripts/prepack.cjs +82 -0
- package/plugins/dsh/skills/admin-template-guidelines/SKILL.md +113 -0
- package/plugins/dsh/skills/admin-template-guidelines/references/rule-1.md +91 -0
- package/plugins/dsh/skills/admin-template-guidelines/references/rule-2.md +155 -0
- package/plugins/dsh/skills/admin-template-guidelines/references/rule-3.md +70 -0
- package/plugins/dsh/skills/admin-template-guidelines/references/rule-4.md +154 -0
- package/plugins/dsh/skills/admin-template-guidelines/references/rule-5.md +126 -0
- package/plugins/dsh/skills/admin-template-guidelines/references/rule-6.md +180 -0
- package/plugins/dsh/skills/batch-grill-me/SKILL.md +15 -0
- package/plugins/dsh/skills/clipboard-vision/SKILL.md +47 -0
- package/plugins/dsh/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +65 -0
- package/plugins/dsh/skills/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +185 -0
- package/plugins/dsh/skills/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +138 -0
- package/plugins/dsh/skills/clipboard-vision/__tests__/clipboard-vision.test.cjs +273 -0
- package/plugins/dsh/skills/clipboard-vision/describe-clipboard.ps1 +115 -0
- package/plugins/dsh/skills/clipboard-vision/describe-clipboard.sh +161 -0
- package/plugins/dsh/skills/clipboard-vision/scripts/clipboard-vision.ps1 +86 -0
- package/plugins/dsh/skills/clipboard-vision/scripts/clipboard-vision.sh +137 -0
- package/plugins/dsh/skills/delphi-review/.delphi-config.json.example +70 -0
- package/plugins/dsh/skills/delphi-review/AGENTS.md +67 -0
- package/plugins/dsh/skills/delphi-review/INSTALL.md +150 -0
- package/plugins/dsh/skills/delphi-review/SKILL.md +559 -0
- package/plugins/dsh/skills/delphi-review/hooks.md +19 -0
- package/plugins/dsh/skills/delphi-review/opencode.json.delphi.example +52 -0
- package/plugins/dsh/skills/delphi-review/references/code-walkthrough.md +470 -0
- package/plugins/dsh/skills/delphi-review/references/orchestrator-dispatch.md +64 -0
- package/plugins/dsh/skills/delphi-review/references/requirements.md +402 -0
- package/plugins/dsh/skills/delphi-review/references/round-templates.md +134 -0
- package/plugins/dsh/skills/delphi-review/scripts/verify-consensus.sh +43 -0
- package/plugins/dsh/skills/delphi-review/tools.md +29 -0
- package/plugins/dsh/skills/domain-modeling/ADR-FORMAT.md +64 -0
- package/plugins/dsh/skills/domain-modeling/CONTEXT-FORMAT.md +60 -0
- package/plugins/dsh/skills/domain-modeling/SKILL.md +74 -0
- package/plugins/dsh/skills/grill-with-docs/SKILL.md +7 -0
- package/plugins/dsh/skills/grilling/SKILL.md +12 -0
- package/plugins/dsh/skills/improve-codebase-architecture/SKILL.md +156 -0
- package/plugins/dsh/skills/ralph-loop/SKILL.md +198 -0
- package/plugins/dsh/skills/ralph-loop/references/components.md +113 -0
- package/plugins/dsh/skills/ralph-loop/references/phase-2-build-ralph.md +89 -0
- package/plugins/dsh/skills/ralph-loop/references/workflow-details.md +99 -0
- package/plugins/dsh/skills/ralph-loop/templates/progress-log.md +36 -0
- package/plugins/dsh/skills/sprint-flow/AGENTS.md +103 -0
- package/plugins/dsh/skills/sprint-flow/SKILL.md +411 -0
- package/plugins/dsh/skills/sprint-flow/__tests__/sprint-flow.test.cjs +457 -0
- package/plugins/dsh/skills/sprint-flow/hooks.md +18 -0
- package/plugins/dsh/skills/sprint-flow/references/components/memory.md +87 -0
- package/plugins/dsh/skills/sprint-flow/references/components/middleware.md +75 -0
- package/plugins/dsh/skills/sprint-flow/references/components/skill-invocations.md +105 -0
- package/plugins/dsh/skills/sprint-flow/references/components/system-prompt.md +27 -0
- package/plugins/dsh/skills/sprint-flow/references/components/tool-descriptions.md +95 -0
- package/plugins/dsh/skills/sprint-flow/references/force-levels.md +204 -0
- package/plugins/dsh/skills/sprint-flow/references/orchestration-rules.md +236 -0
- package/plugins/dsh/skills/sprint-flow/references/phase-1-prep.md +294 -0
- package/plugins/dsh/skills/sprint-flow/references/phase-2-design.md +273 -0
- package/plugins/dsh/skills/sprint-flow/references/phase-3-build.md +348 -0
- package/plugins/dsh/skills/sprint-flow/references/phase-4-verify.md +225 -0
- package/plugins/dsh/skills/sprint-flow/references/phase-5-ship.md +222 -0
- package/plugins/dsh/skills/sprint-flow/references/phase-6-close.md +266 -0
- package/plugins/dsh/skills/sprint-flow/references/phase-overview.md +486 -0
- package/plugins/dsh/skills/sprint-flow/scripts/verify-sprint-state.sh +38 -0
- package/plugins/dsh/skills/sprint-flow/templates/auto-estimate-learning-log.md +136 -0
- package/plugins/dsh/skills/sprint-flow/templates/auto-estimate-output-template.md +110 -0
- package/plugins/dsh/skills/sprint-flow/templates/emergent-issues-template.md +120 -0
- package/plugins/dsh/skills/sprint-flow/templates/pain-document-template.md +115 -0
- package/plugins/dsh/skills/sprint-flow/templates/sprint-progress-template.md +140 -0
- package/plugins/dsh/skills/sprint-flow/templates/sprint-summary-template.md +121 -0
- package/plugins/dsh/skills/sprint-flow/tools.md +29 -0
- package/plugins/dsh/skills/test-driven-development/SKILL.md +371 -0
- package/plugins/dsh/skills/test-driven-development/testing-anti-patterns.md +299 -0
- package/plugins/dsh/skills/test-specification-alignment/AGENTS.md +56 -0
- package/plugins/dsh/skills/test-specification-alignment/SKILL.md +205 -0
- package/plugins/dsh/skills/test-specification-alignment/references/alignment-verification-algorithm.md +493 -0
- package/plugins/dsh/skills/test-specification-alignment/references/phase2-constraint-enforcement.md +431 -0
- package/plugins/dsh/skills/test-specification-alignment/references/specification-format.md +348 -0
- package/plugins/dsh/skills/to-issues/SKILL.md +277 -0
- package/plugins/dsh/src/command.test.ts +102 -0
- package/plugins/dsh/src/command.ts +75 -0
- package/plugins/dsh/src/contract.test.ts +62 -0
- package/plugins/dsh/src/gate-runner.test.ts +100 -0
- package/plugins/dsh/src/gate-runner.ts +58 -0
- package/plugins/dsh/src/index.test.ts +97 -0
- package/plugins/dsh/src/index.ts +94 -0
- package/plugins/dsh/src/skills.test.ts +58 -0
- package/plugins/dsh/tsconfig.build.json +13 -0
- package/plugins/dsh/tsconfig.json +19 -0
- package/plugins/dsh/vitest.config.ts +8 -0
- package/plugins/opencode/package.json +1 -1
- package/plugins/opencode/scripts/prepack.cjs +1 -0
- package/plugins/opencode/skills/clipboard-vision/SKILL.md +47 -0
- package/plugins/opencode/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +65 -0
- package/plugins/opencode/skills/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +185 -0
- package/plugins/opencode/skills/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +138 -0
- package/plugins/opencode/skills/clipboard-vision/__tests__/clipboard-vision.test.cjs +273 -0
- package/plugins/opencode/skills/clipboard-vision/describe-clipboard.ps1 +115 -0
- package/plugins/opencode/skills/clipboard-vision/describe-clipboard.sh +161 -0
- package/plugins/opencode/skills/clipboard-vision/scripts/clipboard-vision.ps1 +86 -0
- package/plugins/opencode/skills/clipboard-vision/scripts/clipboard-vision.sh +137 -0
- package/plugins/opencode/skills/delphi-review/.delphi-config.json.example +24 -27
- package/plugins/opencode/skills/delphi-review/AGENTS.md +12 -11
- package/plugins/opencode/skills/delphi-review/INSTALL.md +15 -17
- package/plugins/opencode/skills/delphi-review/SKILL.md +30 -29
- package/plugins/opencode/skills/delphi-review/opencode.json.delphi.example +6 -10
- package/plugins/opencode/skills/delphi-review/references/code-walkthrough.md +33 -49
- package/plugins/opencode/skills/delphi-review/references/requirements.md +66 -54
- package/plugins/opencode/skills/delphi-review/references/round-templates.md +7 -7
- package/plugins/opencode/skills/sprint-flow/AGENTS.md +4 -4
- package/plugins/opencode/skills/sprint-flow/SKILL.md +4 -4
- package/plugins/opencode/skills/sprint-flow/references/components/skill-invocations.md +2 -2
- package/plugins/opencode/skills/sprint-flow/references/force-levels.md +37 -35
- package/plugins/opencode/skills/sprint-flow/references/orchestration-rules.md +1 -1
- package/plugins/opencode/skills/sprint-flow/references/phase-1-prep.md +1 -1
- package/plugins/opencode/skills/sprint-flow/references/phase-2-design.md +24 -14
- package/plugins/opencode/skills/sprint-flow/references/phase-4-verify.md +6 -6
- package/plugins/opencode/skills/sprint-flow/references/phase-overview.md +2 -2
- package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +3 -3
- package/plugins/qoder/plugin.json +1 -1
- package/plugins/qoder/skills/clipboard-vision/SKILL.md +47 -0
- package/plugins/qoder/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +65 -0
- package/plugins/qoder/skills/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +185 -0
- package/plugins/qoder/skills/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +138 -0
- package/plugins/qoder/skills/clipboard-vision/__tests__/clipboard-vision.test.cjs +273 -0
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/SKILL.md +47 -0
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +65 -0
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +185 -0
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +138 -0
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/__tests__/clipboard-vision.test.cjs +273 -0
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/scripts/clipboard-vision.ps1 +86 -0
- package/plugins/qoder/skills/clipboard-vision/clipboard-vision/scripts/clipboard-vision.sh +137 -0
- package/plugins/qoder/skills/clipboard-vision/scripts/clipboard-vision.ps1 +86 -0
- package/plugins/qoder/skills/clipboard-vision/scripts/clipboard-vision.sh +137 -0
- package/plugins/qoder/skills/delphi-review/.delphi-config.json.example +3 -11
- package/plugins/qoder/skills/delphi-review/AGENTS.md +14 -16
- package/plugins/qoder/skills/delphi-review/INSTALL.md +12 -12
- package/plugins/qoder/skills/delphi-review/SKILL.md +58 -25
- package/plugins/qoder/skills/delphi-review/opencode.json.delphi.example +1 -1
- package/plugins/qoder/skills/delphi-review/references/code-walkthrough.md +33 -49
- package/plugins/qoder/skills/sprint-flow/AGENTS.md +4 -4
- package/plugins/qoder/skills/sprint-flow/SKILL.md +2 -2
- package/plugins/qoder/skills/sprint-flow/references/force-levels.md +38 -35
- package/plugins/qoder/skills/sprint-flow/references/phase-1-prep.md +1 -1
- package/plugins/qoder/skills/sprint-flow/references/phase-2-design.md +48 -6
- package/plugins/qoder/skills/sprint-flow/references/phase-4-verify.md +5 -6
- package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +3 -3
- package/principles/AGENTS.md +3 -3
- package/scripts/__tests__/sync-package-content.test.js +30 -0
- package/scripts/delphi-external-review.cjs +194 -63
- package/scripts/sync-package-content.js +9 -2
- package/skills/clipboard-vision/SKILL.md +47 -0
- package/skills/clipboard-vision/__tests__/clipboard-vision-distribution.test.cjs +65 -0
- package/skills/clipboard-vision/__tests__/clipboard-vision-security.test.cjs +185 -0
- package/skills/clipboard-vision/__tests__/clipboard-vision.Tests.ps1 +138 -0
- package/skills/clipboard-vision/__tests__/clipboard-vision.test.cjs +273 -0
- package/skills/clipboard-vision/describe-clipboard.ps1 +115 -0
- package/skills/clipboard-vision/describe-clipboard.sh +161 -0
- package/skills/clipboard-vision/scripts/clipboard-vision.ps1 +86 -0
- package/skills/clipboard-vision/scripts/clipboard-vision.sh +137 -0
- package/skills/delphi-review/.delphi-config.json.example +24 -27
- package/skills/delphi-review/AGENTS.md +12 -11
- package/skills/delphi-review/INSTALL.md +15 -17
- package/skills/delphi-review/SKILL.md +30 -29
- package/skills/delphi-review/opencode.json.delphi.example +6 -10
- package/skills/delphi-review/references/code-walkthrough.md +33 -49
- package/skills/delphi-review/references/requirements.md +66 -54
- package/skills/delphi-review/references/round-templates.md +7 -7
- package/skills/sprint-flow/AGENTS.md +4 -4
- package/skills/sprint-flow/SKILL.md +4 -4
- package/skills/sprint-flow/references/components/skill-invocations.md +2 -2
- package/skills/sprint-flow/references/force-levels.md +37 -35
- package/skills/sprint-flow/references/orchestration-rules.md +1 -1
- package/skills/sprint-flow/references/phase-1-prep.md +1 -1
- package/skills/sprint-flow/references/phase-2-design.md +24 -14
- package/skills/sprint-flow/references/phase-4-verify.md +6 -6
- package/skills/sprint-flow/references/phase-overview.md +2 -2
- package/skills/test-specification-alignment/AGENTS.md +3 -3
- package/sprint-gate.sh +291 -0
|
@@ -14,7 +14,22 @@ let TMP_DIR;
|
|
|
14
14
|
let cwdSpy;
|
|
15
15
|
|
|
16
16
|
function git(cmd, dir) {
|
|
17
|
-
|
|
17
|
+
const gitEnv = { ...process.env };
|
|
18
|
+
for (const name of [
|
|
19
|
+
'GIT_ALTERNATE_OBJECT_DIRECTORIES', 'GIT_CONFIG', 'GIT_CONFIG_PARAMETERS',
|
|
20
|
+
'GIT_CONFIG_COUNT', 'GIT_OBJECT_DIRECTORY', 'GIT_DIR', 'GIT_WORK_TREE',
|
|
21
|
+
'GIT_IMPLICIT_WORK_TREE', 'GIT_GRAFT_FILE', 'GIT_INDEX_FILE',
|
|
22
|
+
'GIT_NO_REPLACE_OBJECTS', 'GIT_REPLACE_REF_BASE', 'GIT_PREFIX',
|
|
23
|
+
'GIT_SHALLOW_FILE', 'GIT_COMMON_DIR',
|
|
24
|
+
]) {
|
|
25
|
+
delete gitEnv[name];
|
|
26
|
+
}
|
|
27
|
+
return execSync(`git ${cmd}`, {
|
|
28
|
+
cwd: dir || TMP_DIR,
|
|
29
|
+
env: gitEnv,
|
|
30
|
+
encoding: 'utf8',
|
|
31
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
32
|
+
});
|
|
18
33
|
}
|
|
19
34
|
|
|
20
35
|
function writeSprintState(state, dir) {
|
|
@@ -181,4 +196,88 @@ describe('sprint-status --rework-check (#369)', () => {
|
|
|
181
196
|
expect(code).toBe(0);
|
|
182
197
|
expect(output).toContain('No closed sprints within 1 day');
|
|
183
198
|
});
|
|
199
|
+
|
|
200
|
+
test('(e) --dir takes precedence over inherited Git hook repository variables', async () => {
|
|
201
|
+
const targetDir = fs.mkdtempSync(path.join(os.tmpdir(), 'rework-target-'));
|
|
202
|
+
const outerDir = fs.mkdtempSync(path.join(os.tmpdir(), 'rework-outer-'));
|
|
203
|
+
|
|
204
|
+
for (const dir of [targetDir, outerDir]) {
|
|
205
|
+
git('init', dir);
|
|
206
|
+
git('config user.email "test@test.com"', dir);
|
|
207
|
+
git('config user.name "Test"', dir);
|
|
208
|
+
}
|
|
209
|
+
makeCommit('feat: target initial', targetDir);
|
|
210
|
+
makeCommit('fix: target bug', targetDir);
|
|
211
|
+
makeCommit('fix: outer one', outerDir);
|
|
212
|
+
makeCommit('fix: outer two', outerDir);
|
|
213
|
+
makeCommit('fix: outer three', outerDir);
|
|
214
|
+
|
|
215
|
+
writeSprintState({
|
|
216
|
+
_schema_version: 1,
|
|
217
|
+
id: 'sprint-hook-env',
|
|
218
|
+
task_description: 'Hook environment sprint',
|
|
219
|
+
phase: 6,
|
|
220
|
+
status: 'completed',
|
|
221
|
+
started_at: daysAgo(9),
|
|
222
|
+
isolation: { worktree_path: null, branch: 'main' },
|
|
223
|
+
phase_history: [],
|
|
224
|
+
metrics: { completed_at: daysAgo(2), total_sprint_commits: 10 },
|
|
225
|
+
}, targetDir);
|
|
226
|
+
cwdSpy.mockReturnValue(targetDir);
|
|
227
|
+
|
|
228
|
+
const previousGitDir = process.env.GIT_DIR;
|
|
229
|
+
const previousWorkTree = process.env.GIT_WORK_TREE;
|
|
230
|
+
process.env.GIT_DIR = path.join(outerDir, '.git');
|
|
231
|
+
process.env.GIT_WORK_TREE = outerDir;
|
|
232
|
+
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
233
|
+
try {
|
|
234
|
+
const code = await handleSprintStatus([
|
|
235
|
+
'--rework-check', '--window-days', '7', '--dir', targetDir,
|
|
236
|
+
]);
|
|
237
|
+
const output = logSpy.mock.calls.map(c => c.join(' ')).join('\n');
|
|
238
|
+
|
|
239
|
+
expect(code).toBe(0);
|
|
240
|
+
expect(output).toContain('10.0%');
|
|
241
|
+
expect(output).toContain('1 fix / 10 total commits');
|
|
242
|
+
} finally {
|
|
243
|
+
logSpy.mockRestore();
|
|
244
|
+
if (previousGitDir === undefined) delete process.env.GIT_DIR;
|
|
245
|
+
else process.env.GIT_DIR = previousGitDir;
|
|
246
|
+
if (previousWorkTree === undefined) delete process.env.GIT_WORK_TREE;
|
|
247
|
+
else process.env.GIT_WORK_TREE = previousWorkTree;
|
|
248
|
+
fs.rmSync(targetDir, { recursive: true, force: true });
|
|
249
|
+
fs.rmSync(outerDir, { recursive: true, force: true });
|
|
250
|
+
}
|
|
251
|
+
});
|
|
252
|
+
|
|
253
|
+
test('(f) completed_at cannot inject shell commands into the Git log query', async () => {
|
|
254
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'rework-injection-'));
|
|
255
|
+
const markerFile = path.join(dir, 'injection-marker');
|
|
256
|
+
cwdSpy.mockReturnValue(dir);
|
|
257
|
+
git('init', dir);
|
|
258
|
+
git('config user.email "test@test.com"', dir);
|
|
259
|
+
git('config user.name "Test"', dir);
|
|
260
|
+
makeCommit('fix: target bug', dir);
|
|
261
|
+
|
|
262
|
+
writeSprintState({
|
|
263
|
+
_schema_version: 1,
|
|
264
|
+
id: 'sprint-injection',
|
|
265
|
+
phase: 6,
|
|
266
|
+
status: 'completed',
|
|
267
|
+
metrics: {
|
|
268
|
+
completed_at: `Thu, 06 Aug 2026 00:00:00 GMT ("; touch ${markerFile}; #)`,
|
|
269
|
+
total_sprint_commits: 1,
|
|
270
|
+
},
|
|
271
|
+
}, dir);
|
|
272
|
+
|
|
273
|
+
const logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
274
|
+
try {
|
|
275
|
+
const code = await handleSprintStatus(['--rework-check', '--window-days', '3650', '--dir', dir]);
|
|
276
|
+
expect(code).toBe(0);
|
|
277
|
+
expect(fs.existsSync(markerFile)).toBe(false);
|
|
278
|
+
} finally {
|
|
279
|
+
logSpy.mockRestore();
|
|
280
|
+
fs.rmSync(dir, { recursive: true, force: true });
|
|
281
|
+
}
|
|
282
|
+
});
|
|
184
283
|
});
|
|
@@ -3,9 +3,9 @@
|
|
|
3
3
|
* @intent Verify updateHooks correctly syncs hook files with proper backup, dry-run, scope, and detection behavior
|
|
4
4
|
* @covers AC-265-01, AC-265-02, AC-265-03, AC-265-04, AC-265-05
|
|
5
5
|
*/
|
|
6
|
-
const fs = require('fs');
|
|
7
|
-
const path = require('path');
|
|
8
|
-
const os = require('os');
|
|
6
|
+
const fs = require('node:fs');
|
|
7
|
+
const path = require('node:path');
|
|
8
|
+
const os = require('node:os');
|
|
9
9
|
|
|
10
10
|
describe('updateHooks', () => {
|
|
11
11
|
let tmpHome;
|
|
@@ -14,7 +14,6 @@ describe('updateHooks', () => {
|
|
|
14
14
|
let originalHome;
|
|
15
15
|
let logSpy;
|
|
16
16
|
let warnSpy;
|
|
17
|
-
let errorSpy;
|
|
18
17
|
|
|
19
18
|
beforeEach(() => {
|
|
20
19
|
originalHome = process.env.HOME;
|
|
@@ -25,7 +24,6 @@ describe('updateHooks', () => {
|
|
|
25
24
|
vi.resetModules();
|
|
26
25
|
logSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
27
26
|
warnSpy = vi.spyOn(console, 'warn').mockImplementation(() => {});
|
|
28
|
-
errorSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
29
27
|
});
|
|
30
28
|
|
|
31
29
|
afterEach(() => {
|
|
@@ -39,17 +37,26 @@ describe('updateHooks', () => {
|
|
|
39
37
|
/** Helper: create a fake package structure under tmpPackage */
|
|
40
38
|
function createPackageSource(overrides = {}) {
|
|
41
39
|
const hooksDir = path.join(tmpPackage, 'hooks');
|
|
40
|
+
const hooksLibDir = path.join(hooksDir, 'lib');
|
|
42
41
|
const adaptersDir = path.join(tmpPackage, 'adapters');
|
|
43
|
-
fs.mkdirSync(
|
|
42
|
+
fs.mkdirSync(hooksLibDir, { recursive: true });
|
|
44
43
|
fs.mkdirSync(adaptersDir, { recursive: true });
|
|
45
44
|
|
|
46
45
|
fs.writeFileSync(path.join(hooksDir, 'pre-commit'), overrides['hooks/pre-commit'] || '#!/bin/bash\necho "hook-v2"');
|
|
47
46
|
fs.writeFileSync(path.join(hooksDir, 'pre-push'), overrides['hooks/pre-push'] || '#!/bin/bash\necho "push-v2"');
|
|
47
|
+
fs.writeFileSync(path.join(hooksLibDir, 'now-ms.sh'), overrides['hooks/lib/now-ms.sh'] || 'now_ms() { echo 123; }');
|
|
48
48
|
fs.writeFileSync(path.join(tmpPackage, 'adapter-common.sh'), overrides['adapter-common.sh'] || '#!/bin/bash\necho "adapter-common-v2"');
|
|
49
49
|
fs.writeFileSync(path.join(adaptersDir, 'typescript.sh'), overrides['adapters/typescript.sh'] || '#!/bin/bash\necho "ts-v2"');
|
|
50
50
|
fs.writeFileSync(path.join(adaptersDir, 'python.sh'), overrides['adapters/python.sh'] || '#!/bin/bash\necho "py-v2"');
|
|
51
51
|
fs.writeFileSync(path.join(tmpPackage, 'gate-3.sh'), overrides['gate-3.sh'] || '#!/bin/bash\necho "gate3-v2"');
|
|
52
52
|
fs.writeFileSync(path.join(tmpPackage, 'gate-4.sh'), overrides['gate-4.sh'] || '#!/bin/bash\necho "gate4-v2"');
|
|
53
|
+
|
|
54
|
+
// Quality gate modules (principles, mutation, mock-policy, build-integrity)
|
|
55
|
+
['principles', 'build-integrity'].forEach(module => {
|
|
56
|
+
const moduleDir = path.join(tmpPackage, module);
|
|
57
|
+
fs.mkdirSync(moduleDir, { recursive: true });
|
|
58
|
+
fs.writeFileSync(path.join(moduleDir, 'index.ts'), overrides[`${module}/index.ts`] || `export const ${module} = 1;`);
|
|
59
|
+
});
|
|
53
60
|
}
|
|
54
61
|
|
|
55
62
|
/** Helper: get fresh updateHooks module */
|
|
@@ -145,16 +152,16 @@ describe('updateHooks', () => {
|
|
|
145
152
|
expect(fs.readFileSync(path.join(dest, 'gate-4.sh'), 'utf8')).toContain('gate4-v2');
|
|
146
153
|
});
|
|
147
154
|
|
|
148
|
-
it('copies
|
|
155
|
+
it('copies gate-12-file-hygiene.sh to the adapters directory', () => {
|
|
149
156
|
createPackageSource();
|
|
150
|
-
fs.writeFileSync(path.join(tmpPackage, '
|
|
157
|
+
fs.writeFileSync(path.join(tmpPackage, 'gate-12-file-hygiene.sh'), '#!/bin/bash\necho "hygiene-v1"');
|
|
151
158
|
const mod = getModule();
|
|
152
|
-
const dest = path.join(tmpProject, 'gates-
|
|
159
|
+
const dest = path.join(tmpProject, 'gates-hygiene');
|
|
153
160
|
fs.mkdirSync(dest, { recursive: true });
|
|
154
161
|
|
|
155
162
|
mod.copyGateScripts(tmpPackage, dest, false, true);
|
|
156
163
|
|
|
157
|
-
expect(fs.readFileSync(path.join(dest, '
|
|
164
|
+
expect(fs.readFileSync(path.join(dest, 'gate-12-file-hygiene.sh'), 'utf8')).toContain('hygiene-v1');
|
|
158
165
|
});
|
|
159
166
|
|
|
160
167
|
it('does not copy non-gate files', () => {
|
|
@@ -167,6 +174,7 @@ describe('updateHooks', () => {
|
|
|
167
174
|
mod.copyGateScripts(tmpPackage, dest, false, true);
|
|
168
175
|
|
|
169
176
|
expect(fs.existsSync(path.join(dest, 'some-other.sh'))).toBe(false);
|
|
177
|
+
expect(fs.existsSync(path.join(dest, 'sprint-gate.sh'))).toBe(false);
|
|
170
178
|
});
|
|
171
179
|
|
|
172
180
|
it('handles missing source directory gracefully', () => {
|
|
@@ -181,6 +189,73 @@ describe('updateHooks', () => {
|
|
|
181
189
|
});
|
|
182
190
|
});
|
|
183
191
|
|
|
192
|
+
// ===== copySprintGate tests =====
|
|
193
|
+
|
|
194
|
+
describe('copySprintGate', () => {
|
|
195
|
+
it('copies sprint-gate.sh to the hooks directory', () => {
|
|
196
|
+
createPackageSource();
|
|
197
|
+
fs.writeFileSync(path.join(tmpPackage, 'sprint-gate.sh'), '#!/bin/bash\necho "sprint-gate-v1"');
|
|
198
|
+
const mod = getModule();
|
|
199
|
+
const hooksDest = path.join(tmpProject, 'hooks-dest');
|
|
200
|
+
fs.mkdirSync(hooksDest, { recursive: true });
|
|
201
|
+
|
|
202
|
+
mod.copySprintGate(tmpPackage, hooksDest, false, true);
|
|
203
|
+
|
|
204
|
+
expect(fs.readFileSync(path.join(hooksDest, 'sprint-gate.sh'), 'utf8')).toContain('sprint-gate-v1');
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
it('skips silently when sprint-gate.sh is absent from package root', () => {
|
|
208
|
+
createPackageSource();
|
|
209
|
+
const mod = getModule();
|
|
210
|
+
const hooksDest = path.join(tmpProject, 'hooks-no-sprint');
|
|
211
|
+
fs.mkdirSync(hooksDest, { recursive: true });
|
|
212
|
+
|
|
213
|
+
mod.copySprintGate(tmpPackage, hooksDest, false, true);
|
|
214
|
+
|
|
215
|
+
expect(fs.existsSync(path.join(hooksDest, 'sprint-gate.sh'))).toBe(false);
|
|
216
|
+
});
|
|
217
|
+
});
|
|
218
|
+
|
|
219
|
+
// ===== copyModules tests =====
|
|
220
|
+
|
|
221
|
+
describe('copyModules', () => {
|
|
222
|
+
it('copies quality gate module directories to the modules destination', () => {
|
|
223
|
+
createPackageSource();
|
|
224
|
+
const mod = getModule();
|
|
225
|
+
const modulesDest = path.join(tmpProject, 'modules-dest');
|
|
226
|
+
fs.mkdirSync(modulesDest, { recursive: true });
|
|
227
|
+
|
|
228
|
+
mod.copyModules(tmpPackage, modulesDest, false, true);
|
|
229
|
+
|
|
230
|
+
expect(fs.readFileSync(path.join(modulesDest, 'principles', 'index.ts'), 'utf8')).toContain('principles');
|
|
231
|
+
expect(fs.readFileSync(path.join(modulesDest, 'build-integrity', 'index.ts'), 'utf8')).toContain('build-integrity');
|
|
232
|
+
});
|
|
233
|
+
|
|
234
|
+
it('skips modules absent from the package root', () => {
|
|
235
|
+
createPackageSource();
|
|
236
|
+
const mod = getModule();
|
|
237
|
+
const modulesDest = path.join(tmpProject, 'modules-dest-skip');
|
|
238
|
+
fs.mkdirSync(modulesDest, { recursive: true });
|
|
239
|
+
fs.rmSync(path.join(tmpPackage, 'mutation'), { recursive: true, force: true });
|
|
240
|
+
|
|
241
|
+
mod.copyModules(tmpPackage, modulesDest, false, true);
|
|
242
|
+
|
|
243
|
+
expect(fs.existsSync(path.join(modulesDest, 'mutation'))).toBe(false);
|
|
244
|
+
expect(fs.existsSync(path.join(modulesDest, 'principles'))).toBe(true);
|
|
245
|
+
});
|
|
246
|
+
|
|
247
|
+
it('does not write files in dry-run mode', () => {
|
|
248
|
+
createPackageSource();
|
|
249
|
+
const mod = getModule();
|
|
250
|
+
const modulesDest = path.join(tmpProject, 'modules-dest-dry');
|
|
251
|
+
fs.mkdirSync(modulesDest, { recursive: true });
|
|
252
|
+
|
|
253
|
+
mod.copyModules(tmpPackage, modulesDest, true, true);
|
|
254
|
+
|
|
255
|
+
expect(fs.existsSync(path.join(modulesDest, 'principles', 'index.ts'))).toBe(false);
|
|
256
|
+
});
|
|
257
|
+
});
|
|
258
|
+
|
|
184
259
|
// ===== atomicCopyFile tests =====
|
|
185
260
|
|
|
186
261
|
describe('atomicCopyFile', () => {
|
|
@@ -403,6 +478,47 @@ describe('updateHooks', () => {
|
|
|
403
478
|
mod.getPackageRoot = origGetPackageRoot;
|
|
404
479
|
});
|
|
405
480
|
|
|
481
|
+
it('copies hooks/lib/now-ms.sh during a normal update', () => {
|
|
482
|
+
createPackageSource();
|
|
483
|
+
const hooksDest = path.join(tmpProject, '.git', 'hooks');
|
|
484
|
+
fs.mkdirSync(hooksDest, { recursive: true });
|
|
485
|
+
vi.spyOn(process, 'cwd').mockReturnValue(tmpProject);
|
|
486
|
+
const mod = getModule();
|
|
487
|
+
mod.getPackageRoot = () => tmpPackage;
|
|
488
|
+
|
|
489
|
+
const result = mod.updateHooks({
|
|
490
|
+
global: false,
|
|
491
|
+
force: true,
|
|
492
|
+
dryRun: false,
|
|
493
|
+
noBackup: true,
|
|
494
|
+
scope: 'hooks',
|
|
495
|
+
});
|
|
496
|
+
|
|
497
|
+
expect(result).toBe(0);
|
|
498
|
+
expect(fs.readFileSync(path.join(hooksDest, 'lib', 'now-ms.sh'), 'utf8')).toContain('echo 123');
|
|
499
|
+
});
|
|
500
|
+
|
|
501
|
+
it('does not write hooks/lib/now-ms.sh during a dry run', () => {
|
|
502
|
+
createPackageSource();
|
|
503
|
+
const hooksDest = path.join(tmpProject, '.git', 'hooks');
|
|
504
|
+
fs.mkdirSync(hooksDest, { recursive: true });
|
|
505
|
+
vi.spyOn(process, 'cwd').mockReturnValue(tmpProject);
|
|
506
|
+
const mod = getModule();
|
|
507
|
+
mod.getPackageRoot = () => tmpPackage;
|
|
508
|
+
|
|
509
|
+
const result = mod.updateHooks({
|
|
510
|
+
global: false,
|
|
511
|
+
force: true,
|
|
512
|
+
dryRun: true,
|
|
513
|
+
noBackup: true,
|
|
514
|
+
scope: 'hooks',
|
|
515
|
+
});
|
|
516
|
+
|
|
517
|
+
expect(result).toBe(0);
|
|
518
|
+
expect(logSpy).toHaveBeenCalledWith(' would update: lib/now-ms.sh');
|
|
519
|
+
expect(fs.existsSync(path.join(hooksDest, 'lib', 'now-ms.sh'))).toBe(false);
|
|
520
|
+
});
|
|
521
|
+
|
|
406
522
|
it('returns 1 when local modifications detected and no --force', () => {
|
|
407
523
|
createPackageSource();
|
|
408
524
|
const hooksDest = path.join(tmpProject, '.git', 'hooks');
|
|
@@ -447,6 +563,8 @@ describe('updateHooks', () => {
|
|
|
447
563
|
});
|
|
448
564
|
expect(result).toBe(0);
|
|
449
565
|
expect(fs.readFileSync(path.join(hooksDest, 'pre-commit'), 'utf8')).toContain('hook-v2');
|
|
566
|
+
// scope 'all' also re-deploys quality-gate modules (repairs existing installs)
|
|
567
|
+
expect(fs.readFileSync(path.join(tmpProject, '.xp-gate', 'modules', 'build-integrity', 'index.ts'), 'utf8')).toContain('build-integrity');
|
|
450
568
|
|
|
451
569
|
mod.getPackageRoot = origGetPackageRoot;
|
|
452
570
|
});
|
|
@@ -589,4 +707,19 @@ describe('updateHooks', () => {
|
|
|
589
707
|
mod.getPackageRoot = origGetPackageRoot;
|
|
590
708
|
});
|
|
591
709
|
});
|
|
710
|
+
|
|
711
|
+
describe('warnMissingModuleDeps', () => {
|
|
712
|
+
it('warns when global module runtime deps are absent', () => {
|
|
713
|
+
const mod = getModule();
|
|
714
|
+
mod.warnMissingModuleDeps(true, false);
|
|
715
|
+
expect(warnSpy).toHaveBeenCalledWith(expect.stringContaining('setup-global'));
|
|
716
|
+
});
|
|
717
|
+
|
|
718
|
+
it('does not warn for local installs or dry-runs', () => {
|
|
719
|
+
const mod = getModule();
|
|
720
|
+
mod.warnMissingModuleDeps(false, false);
|
|
721
|
+
mod.warnMissingModuleDeps(true, true);
|
|
722
|
+
expect(warnSpy).not.toHaveBeenCalled();
|
|
723
|
+
});
|
|
724
|
+
});
|
|
592
725
|
});
|
|
@@ -13,8 +13,56 @@
|
|
|
13
13
|
*/
|
|
14
14
|
|
|
15
15
|
const { EventEmitter } = require('events');
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const os = require('os');
|
|
18
|
+
const path = require('path');
|
|
16
19
|
|
|
17
20
|
describe('upgrade.js --apply execSync path', () => {
|
|
21
|
+
let cacheDir;
|
|
22
|
+
let savedCacheDir;
|
|
23
|
+
let savedHttpsGet;
|
|
24
|
+
let savedSpawn;
|
|
25
|
+
let savedReadFileSync;
|
|
26
|
+
|
|
27
|
+
beforeEach(() => {
|
|
28
|
+
savedCacheDir = process.env.XP_GATE_CACHE_DIR;
|
|
29
|
+
savedHttpsGet = require('https').get;
|
|
30
|
+
savedSpawn = require('child_process').spawn;
|
|
31
|
+
savedReadFileSync = fs.readFileSync;
|
|
32
|
+
cacheDir = undefined;
|
|
33
|
+
cacheDir = fs.mkdtempSync(path.join(os.tmpdir(), 'upgrade-cache-'));
|
|
34
|
+
process.env.XP_GATE_CACHE_DIR = cacheDir;
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
afterEach(() => {
|
|
38
|
+
if (savedCacheDir === undefined) delete process.env.XP_GATE_CACHE_DIR;
|
|
39
|
+
else process.env.XP_GATE_CACHE_DIR = savedCacheDir;
|
|
40
|
+
require('https').get = savedHttpsGet;
|
|
41
|
+
require('child_process').spawn = savedSpawn;
|
|
42
|
+
fs.readFileSync = savedReadFileSync;
|
|
43
|
+
if (cacheDir) fs.rmSync(cacheDir, { recursive: true, force: true });
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
function installFakeHttpsGet(latestVersion) {
|
|
47
|
+
const https = require('https');
|
|
48
|
+
const saved = https.get;
|
|
49
|
+
https.get = (_url, options, callbackArg) => {
|
|
50
|
+
const callback = typeof options === 'function' ? options : callbackArg;
|
|
51
|
+
const request = new EventEmitter();
|
|
52
|
+
request.destroy = () => undefined;
|
|
53
|
+
if (!callback) return request;
|
|
54
|
+
const response = new EventEmitter();
|
|
55
|
+
response.statusCode = 200;
|
|
56
|
+
callback(response);
|
|
57
|
+
response.emit('data', JSON.stringify({ latest: latestVersion }));
|
|
58
|
+
response.emit('end');
|
|
59
|
+
return request;
|
|
60
|
+
};
|
|
61
|
+
return () => {
|
|
62
|
+
https.get = saved;
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
18
66
|
function evictCache() {
|
|
19
67
|
const resolved = require.resolve('../upgrade');
|
|
20
68
|
const cvResolved = require.resolve('../check-version');
|
|
@@ -38,39 +86,36 @@ describe('upgrade.js --apply execSync path', () => {
|
|
|
38
86
|
return ee;
|
|
39
87
|
}
|
|
40
88
|
|
|
41
|
-
function withMockedEnv(latestVersion, spawnImpl, fn) {
|
|
42
|
-
const
|
|
43
|
-
const os = require('os');
|
|
44
|
-
const cpPath = require('path').join(os.homedir(), '.xp-gate', 'version-cache.json');
|
|
89
|
+
async function withMockedEnv(latestVersion, spawnImpl, fn) {
|
|
90
|
+
const cpPath = path.join(cacheDir, 'version-cache.json');
|
|
45
91
|
if (fs.existsSync(cpPath)) {
|
|
46
92
|
try { fs.unlinkSync(cpPath); } catch { }
|
|
47
93
|
}
|
|
48
94
|
evictCache();
|
|
49
95
|
const cp = require('child_process');
|
|
50
|
-
const
|
|
51
|
-
const saved = {
|
|
96
|
+
const updateSkillPath = require.resolve('../update-skill');
|
|
97
|
+
const saved = {
|
|
98
|
+
spawn: cp.spawn,
|
|
99
|
+
updateSkillCache: require.cache[updateSkillPath],
|
|
100
|
+
};
|
|
52
101
|
cp.spawn = spawnImpl;
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
if (evt === 'data') handler(body);
|
|
61
|
-
if (evt === 'end') handler();
|
|
62
|
-
return mockRes;
|
|
63
|
-
},
|
|
64
|
-
};
|
|
65
|
-
callback(mockRes);
|
|
66
|
-
return { on: () => undefined, destroy: () => undefined };
|
|
102
|
+
require.cache[updateSkillPath] = {
|
|
103
|
+
id: updateSkillPath,
|
|
104
|
+
filename: updateSkillPath,
|
|
105
|
+
loaded: true,
|
|
106
|
+
exports: { updateSkill: vi.fn(async () => 0) },
|
|
107
|
+
children: [],
|
|
108
|
+
paths: [],
|
|
67
109
|
};
|
|
110
|
+
const restoreHttpsGet = installFakeHttpsGet(latestVersion);
|
|
68
111
|
try {
|
|
69
112
|
const m = require('../upgrade');
|
|
70
|
-
return fn(m);
|
|
113
|
+
return await fn(m);
|
|
71
114
|
} finally {
|
|
72
115
|
cp.spawn = saved.spawn;
|
|
73
|
-
|
|
116
|
+
restoreHttpsGet();
|
|
117
|
+
if (saved.updateSkillCache) require.cache[updateSkillPath] = saved.updateSkillCache;
|
|
118
|
+
else delete require.cache[updateSkillPath];
|
|
74
119
|
}
|
|
75
120
|
}
|
|
76
121
|
|
|
@@ -134,17 +179,14 @@ describe('upgrade.js --apply execSync path', () => {
|
|
|
134
179
|
}, 10000);
|
|
135
180
|
|
|
136
181
|
// ── withMockedEnv variant: also mocks getLocalVersion() to return null ──
|
|
137
|
-
function withMockedEnvNoLocal(latestVersion, spawnImpl, fn) {
|
|
138
|
-
const
|
|
139
|
-
const os = require('os');
|
|
140
|
-
const cpPath = require('path').join(os.homedir(), '.xp-gate', 'version-cache.json');
|
|
182
|
+
async function withMockedEnvNoLocal(latestVersion, spawnImpl, fn) {
|
|
183
|
+
const cpPath = path.join(cacheDir, 'version-cache.json');
|
|
141
184
|
if (fs.existsSync(cpPath)) {
|
|
142
185
|
try { fs.unlinkSync(cpPath); } catch { }
|
|
143
186
|
}
|
|
144
187
|
evictCache();
|
|
145
188
|
const cp = require('child_process');
|
|
146
|
-
const
|
|
147
|
-
const saved = { spawn: cp.spawn, httpsGet: https.get, fsReadFileSync: fs.readFileSync };
|
|
189
|
+
const saved = { spawn: cp.spawn, fsReadFileSync: fs.readFileSync };
|
|
148
190
|
cp.spawn = spawnImpl;
|
|
149
191
|
// Make getLocalVersion() return null by making fs.readFileSync throw for package.json
|
|
150
192
|
fs.readFileSync = (filePath, encoding) => {
|
|
@@ -155,27 +197,13 @@ describe('upgrade.js --apply execSync path', () => {
|
|
|
155
197
|
}
|
|
156
198
|
return saved.fsReadFileSync.call(fs, filePath, encoding);
|
|
157
199
|
};
|
|
158
|
-
const
|
|
159
|
-
https.get = (_url, _opts, cb) => {
|
|
160
|
-
const callback = typeof _opts === 'function' ? _opts : cb;
|
|
161
|
-
if (!callback) return { on: () => undefined, destroy: () => undefined };
|
|
162
|
-
const mockRes = {
|
|
163
|
-
statusCode: 200,
|
|
164
|
-
on: (evt, handler) => {
|
|
165
|
-
if (evt === 'data') handler(body);
|
|
166
|
-
if (evt === 'end') handler();
|
|
167
|
-
return mockRes;
|
|
168
|
-
},
|
|
169
|
-
};
|
|
170
|
-
callback(mockRes);
|
|
171
|
-
return { on: () => undefined, destroy: () => undefined };
|
|
172
|
-
};
|
|
200
|
+
const restoreHttpsGet = installFakeHttpsGet(latestVersion);
|
|
173
201
|
try {
|
|
174
202
|
const m = require('../upgrade');
|
|
175
|
-
return fn(m);
|
|
203
|
+
return await fn(m);
|
|
176
204
|
} finally {
|
|
177
205
|
cp.spawn = saved.spawn;
|
|
178
|
-
|
|
206
|
+
restoreHttpsGet();
|
|
179
207
|
fs.readFileSync = saved.fsReadFileSync;
|
|
180
208
|
}
|
|
181
209
|
}
|
|
@@ -57,7 +57,7 @@ describe('doctor skill version check — #332', () => {
|
|
|
57
57
|
|
|
58
58
|
// AC-332-05: diagnoseInstalledSkills compares installed skills against package-bundled skills
|
|
59
59
|
it('AC-332-05: diagnoseInstalledSkills reads config installedSkills and package skills dir', () => {
|
|
60
|
-
const source = fs.readFileSync(require.resolve('../doctor.js'), 'utf8');
|
|
60
|
+
const source = fs.readFileSync(require.resolve('../doctor-tui.js'), 'utf8');
|
|
61
61
|
expect(source).toContain('installedSkills');
|
|
62
62
|
expect(source).toContain('diagnoseInstalledSkills');
|
|
63
63
|
expect(source).toContain('skills');
|
|
@@ -18,8 +18,9 @@ function capture() {
|
|
|
18
18
|
}
|
|
19
19
|
|
|
20
20
|
describe('upgrade.js — REQ-001-02', () => {
|
|
21
|
-
let mod;
|
|
22
21
|
let r;
|
|
22
|
+
let cacheDir;
|
|
23
|
+
let savedCacheDir;
|
|
23
24
|
|
|
24
25
|
function evictLibCache() {
|
|
25
26
|
const resolved = require.resolve('../upgrade');
|
|
@@ -33,20 +34,22 @@ describe('upgrade.js — REQ-001-02', () => {
|
|
|
33
34
|
}
|
|
34
35
|
|
|
35
36
|
beforeEach(() => {
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
if (fs.existsSync(cpPath)) { try { fs.unlinkSync(cpPath); } catch { } }
|
|
37
|
+
savedCacheDir = process.env.XP_GATE_CACHE_DIR;
|
|
38
|
+
r = undefined;
|
|
39
|
+
cacheDir = undefined;
|
|
40
|
+
cacheDir = fs.mkdtempSync(path.join(require('os').tmpdir(), 'upgrade-test-cache-'));
|
|
41
|
+
process.env.XP_GATE_CACHE_DIR = cacheDir;
|
|
42
42
|
evictLibCache();
|
|
43
43
|
vi.resetModules();
|
|
44
44
|
r = capture();
|
|
45
|
-
|
|
45
|
+
require('../upgrade');
|
|
46
46
|
});
|
|
47
47
|
|
|
48
48
|
afterEach(() => {
|
|
49
|
-
r
|
|
49
|
+
r?.restore();
|
|
50
|
+
if (savedCacheDir === undefined) delete process.env.XP_GATE_CACHE_DIR;
|
|
51
|
+
else process.env.XP_GATE_CACHE_DIR = savedCacheDir;
|
|
52
|
+
if (cacheDir) fs.rmSync(cacheDir, { recursive: true, force: true });
|
|
50
53
|
});
|
|
51
54
|
|
|
52
55
|
// AC-002-01 through AC-002-06: tested in upgrade-exec.test.js (isolated, no real network)
|
|
@@ -73,5 +76,3 @@ describe('upgrade.js — REQ-001-02', () => {
|
|
|
73
76
|
});
|
|
74
77
|
});
|
|
75
78
|
});
|
|
76
|
-
|
|
77
|
-
|
package/lib/doctor.js
CHANGED
|
@@ -275,6 +275,7 @@ const EXPECTED_GATE_SCRIPTS = [
|
|
|
275
275
|
'gate-7.sh',
|
|
276
276
|
'gate-8.sh',
|
|
277
277
|
'gate-9.sh',
|
|
278
|
+
'gate-10.sh',
|
|
278
279
|
];
|
|
279
280
|
|
|
280
281
|
async function checkAdapters(checks, mode, gitDirPromise) {
|
|
@@ -713,7 +714,7 @@ function fixMissingAdapters(mode, srcDir, adaptersDir) {
|
|
|
713
714
|
|
|
714
715
|
/**
|
|
715
716
|
* Restore missing gate scripts from package root to adapters directory.
|
|
716
|
-
* Gate scripts (gate-3.sh through gate-
|
|
717
|
+
* Gate scripts (gate-3.sh through gate-10.sh) are stored in the package root,
|
|
717
718
|
* not in the adapters subdirectory.
|
|
718
719
|
*/
|
|
719
720
|
function fixMissingGateScripts(srcDir, adaptersDir) {
|