@boyingliu01/xp-gate 0.11.4 → 0.12.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/bin/xp-gate.js +8 -0
- package/lib/__tests__/doctor.test.js +100 -1
- package/lib/__tests__/next-sprint.test.js +231 -0
- package/lib/doctor.js +61 -2
- package/lib/init.js +7 -0
- package/lib/next-sprint.js +129 -0
- package/mock-policy/AGENTS.md +3 -3
- package/mutation/AGENTS.md +3 -3
- package/package.json +1 -1
- package/plugins/claude-code/.claude-plugin/plugin.json +1 -1
- package/plugins/claude-code/skills/delphi-review/AGENTS.md +3 -3
- package/plugins/claude-code/skills/sprint-flow/AGENTS.md +3 -3
- package/plugins/claude-code/skills/test-specification-alignment/AGENTS.md +3 -3
- package/plugins/opencode/package.json +1 -1
- package/plugins/opencode/skills/delphi-review/AGENTS.md +3 -3
- package/plugins/opencode/skills/sprint-flow/AGENTS.md +3 -3
- package/plugins/opencode/skills/test-specification-alignment/AGENTS.md +3 -3
- package/plugins/qoder/plugin.json +1 -1
- package/plugins/qoder/skills/delphi-review/AGENTS.md +3 -3
- package/plugins/qoder/skills/sprint-flow/AGENTS.md +3 -3
- package/plugins/qoder/skills/test-specification-alignment/AGENTS.md +3 -3
- package/principles/AGENTS.md +3 -3
- package/skills/delphi-review/AGENTS.md +3 -3
- package/skills/sprint-flow/AGENTS.md +3 -3
- package/skills/test-specification-alignment/AGENTS.md +3 -3
package/bin/xp-gate.js
CHANGED
|
@@ -135,6 +135,14 @@ const COMMANDS = {
|
|
|
135
135
|
handleSprintStatus(subargs).then(code => process.exit(code));
|
|
136
136
|
},
|
|
137
137
|
usage: 'xp-gate sprint-status [--json] [--watch] [--dir <path>]'
|
|
138
|
+
},
|
|
139
|
+
'next-sprint': {
|
|
140
|
+
description: 'Analyze remaining open issues and plan next iteration',
|
|
141
|
+
run: subargs => {
|
|
142
|
+
const { handleNextSprint } = require('../lib/next-sprint.js');
|
|
143
|
+
handleNextSprint(subargs).then(code => process.exit(code));
|
|
144
|
+
},
|
|
145
|
+
usage: 'xp-gate next-sprint [--json] [--plan] [--dir <path>]'
|
|
138
146
|
}
|
|
139
147
|
};
|
|
140
148
|
|
|
@@ -108,6 +108,10 @@ describe('doctor', () => {
|
|
|
108
108
|
path.join(dir, 'adapters', 'python.sh'),
|
|
109
109
|
'#!/usr/bin/env bash\necho "py adapter"\n'
|
|
110
110
|
);
|
|
111
|
+
const gateScripts = ['gate-3.sh', 'gate-4.sh', 'gate-7.sh', 'gate-8.sh', 'gate-9.sh'];
|
|
112
|
+
for (const script of gateScripts) {
|
|
113
|
+
fs.writeFileSync(path.join(dir, 'adapters', script), `#!/bin/bash\n# ${script}\n`);
|
|
114
|
+
}
|
|
111
115
|
}
|
|
112
116
|
|
|
113
117
|
function tuiJsonPath() {
|
|
@@ -139,7 +143,14 @@ describe('doctor', () => {
|
|
|
139
143
|
createXpGatePreCommit(globalHooksDir());
|
|
140
144
|
createXpGatePrePush(globalHooksDir());
|
|
141
145
|
createXpGateAdapterCommon(globalAdaptersDir());
|
|
142
|
-
|
|
146
|
+
// For global mode, create adapters directly in globalAdaptersDir (not in a subdirectory)
|
|
147
|
+
fs.mkdirSync(globalAdaptersDir(), { recursive: true });
|
|
148
|
+
fs.writeFileSync(path.join(globalAdaptersDir(), 'typescript.sh'), '#!/usr/bin/env bash\necho "ts adapter"\n');
|
|
149
|
+
fs.writeFileSync(path.join(globalAdaptersDir(), 'python.sh'), '#!/usr/bin/env bash\necho "py adapter"\n');
|
|
150
|
+
const gateScripts = ['gate-3.sh', 'gate-4.sh', 'gate-7.sh', 'gate-8.sh', 'gate-9.sh'];
|
|
151
|
+
for (const script of gateScripts) {
|
|
152
|
+
fs.writeFileSync(path.join(globalAdaptersDir(), script), `#!/bin/bash\n# ${script}\n`);
|
|
153
|
+
}
|
|
143
154
|
createXpGatePreCommit(projectHooksDir());
|
|
144
155
|
createXpGatePrePush(projectHooksDir());
|
|
145
156
|
createXpGateAdapterCommon(projectGithooksDir());
|
|
@@ -805,4 +816,92 @@ describe('doctor', () => {
|
|
|
805
816
|
// Should still have exactly one entry
|
|
806
817
|
expect(tui.plugin.filter(p => p === '@boyingliu01/opencode-plugin/tui').length).toBe(1);
|
|
807
818
|
});
|
|
819
|
+
|
|
820
|
+
// === Issue #263 follow-up: Gate script detection ===
|
|
821
|
+
|
|
822
|
+
function createGateScripts(dir) {
|
|
823
|
+
fs.mkdirSync(dir, { recursive: true });
|
|
824
|
+
const gateScripts = ['gate-3.sh', 'gate-4.sh', 'gate-7.sh', 'gate-8.sh', 'gate-9.sh'];
|
|
825
|
+
for (const script of gateScripts) {
|
|
826
|
+
fs.writeFileSync(path.join(dir, script), `#!/bin/bash\n# ${script}\n`);
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
|
|
830
|
+
it('gate scripts: PASS when all expected gate scripts exist', async () => {
|
|
831
|
+
setupLocalInstall();
|
|
832
|
+
ensureTuiRegistered();
|
|
833
|
+
seedVersionCache();
|
|
834
|
+
createGateScripts(projectAdaptersDir());
|
|
835
|
+
mockExecSuccess();
|
|
836
|
+
const { doctor } = require('../doctor');
|
|
837
|
+
|
|
838
|
+
const result = await doctor([]);
|
|
839
|
+
|
|
840
|
+
expect(result).toBe(0);
|
|
841
|
+
expect(logSpy).toHaveBeenCalledWith(
|
|
842
|
+
expect.stringContaining('Gate scripts')
|
|
843
|
+
);
|
|
844
|
+
expect(logSpy).toHaveBeenCalledWith(
|
|
845
|
+
expect.stringContaining('5 gate script(s)')
|
|
846
|
+
);
|
|
847
|
+
});
|
|
848
|
+
|
|
849
|
+
it('gate scripts: FAIL when gate scripts are missing', async () => {
|
|
850
|
+
setupLocalInstall();
|
|
851
|
+
seedVersionCache();
|
|
852
|
+
// Clear adapters dir and create with only language adapters, no gate scripts
|
|
853
|
+
fs.rmSync(projectAdaptersDir(), { recursive: true, force: true });
|
|
854
|
+
fs.mkdirSync(projectAdaptersDir(), { recursive: true });
|
|
855
|
+
fs.writeFileSync(path.join(projectAdaptersDir(), 'typescript.sh'), '#!/bin/bash\n');
|
|
856
|
+
mockExecSuccess();
|
|
857
|
+
const { doctor } = require('../doctor');
|
|
858
|
+
|
|
859
|
+
const result = await doctor([]);
|
|
860
|
+
|
|
861
|
+
expect(result).toBe(1);
|
|
862
|
+
expect(logSpy).toHaveBeenCalledWith(
|
|
863
|
+
expect.stringContaining('Gate scripts')
|
|
864
|
+
);
|
|
865
|
+
expect(logSpy).toHaveBeenCalledWith(
|
|
866
|
+
expect.stringContaining('Missing')
|
|
867
|
+
);
|
|
868
|
+
});
|
|
869
|
+
|
|
870
|
+
it('--fix: restores missing gate scripts', async () => {
|
|
871
|
+
setupLocalInstall();
|
|
872
|
+
seedVersionCache();
|
|
873
|
+
// Clear adapters dir and create with only language adapters, no gate scripts
|
|
874
|
+
fs.rmSync(projectAdaptersDir(), { recursive: true, force: true });
|
|
875
|
+
fs.mkdirSync(projectAdaptersDir(), { recursive: true });
|
|
876
|
+
fs.writeFileSync(path.join(projectAdaptersDir(), 'typescript.sh'), '#!/bin/bash\n');
|
|
877
|
+
mockExecSuccess();
|
|
878
|
+
const { doctor } = require('../doctor');
|
|
879
|
+
|
|
880
|
+
const result = await doctor(['--fix']);
|
|
881
|
+
|
|
882
|
+
expect(result).toBe(0);
|
|
883
|
+
expect(fs.existsSync(path.join(projectAdaptersDir(), 'gate-3.sh'))).toBe(true);
|
|
884
|
+
expect(fs.existsSync(path.join(projectAdaptersDir(), 'gate-4.sh'))).toBe(true);
|
|
885
|
+
expect(fs.existsSync(path.join(projectAdaptersDir(), 'gate-7.sh'))).toBe(true);
|
|
886
|
+
expect(fs.existsSync(path.join(projectAdaptersDir(), 'gate-8.sh'))).toBe(true);
|
|
887
|
+
expect(fs.existsSync(path.join(projectAdaptersDir(), 'gate-9.sh'))).toBe(true);
|
|
888
|
+
expect(logSpy).toHaveBeenCalledWith(
|
|
889
|
+
expect.stringContaining('Restored')
|
|
890
|
+
);
|
|
891
|
+
});
|
|
892
|
+
|
|
893
|
+
it('--fix: idempotent for gate scripts — does not overwrite existing', async () => {
|
|
894
|
+
setupLocalInstall();
|
|
895
|
+
seedVersionCache();
|
|
896
|
+
createGateScripts(projectAdaptersDir());
|
|
897
|
+
const originalContent = fs.readFileSync(path.join(projectAdaptersDir(), 'gate-3.sh'), 'utf8');
|
|
898
|
+
mockExecSuccess();
|
|
899
|
+
const { doctor } = require('../doctor');
|
|
900
|
+
|
|
901
|
+
const result = await doctor(['--fix']);
|
|
902
|
+
|
|
903
|
+
expect(result).toBe(0);
|
|
904
|
+
const currentContent = fs.readFileSync(path.join(projectAdaptersDir(), 'gate-3.sh'), 'utf8');
|
|
905
|
+
expect(currentContent).toBe(originalContent);
|
|
906
|
+
});
|
|
808
907
|
});
|
|
@@ -0,0 +1,231 @@
|
|
|
1
|
+
const fs = require('fs');
|
|
2
|
+
const path = require('path');
|
|
3
|
+
const os = require('os');
|
|
4
|
+
|
|
5
|
+
describe('next-sprint', () => {
|
|
6
|
+
let tmpDir;
|
|
7
|
+
let consoleSpy;
|
|
8
|
+
let processExitSpy;
|
|
9
|
+
|
|
10
|
+
beforeEach(() => {
|
|
11
|
+
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'next-sprint-test-'));
|
|
12
|
+
consoleSpy = vi.spyOn(console, 'log').mockImplementation(() => {});
|
|
13
|
+
vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
14
|
+
processExitSpy = vi.spyOn(process, 'exit').mockImplementation(() => {});
|
|
15
|
+
// Clear require cache to ensure fresh module for each test
|
|
16
|
+
delete require.cache[require.resolve('../next-sprint')];
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
afterEach(() => {
|
|
20
|
+
fs.rmSync(tmpDir, { recursive: true, force: true });
|
|
21
|
+
vi.restoreAllMocks();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
function createState(overrides = {}) {
|
|
25
|
+
const stateDir = path.join(tmpDir, '.sprint-state');
|
|
26
|
+
fs.mkdirSync(stateDir, { recursive: true });
|
|
27
|
+
const state = {
|
|
28
|
+
id: 'sprint-2026-06-30-01',
|
|
29
|
+
task_description: 'Test Sprint',
|
|
30
|
+
phase: 4,
|
|
31
|
+
status: 'completed',
|
|
32
|
+
started_at: new Date().toISOString(),
|
|
33
|
+
phase_history: [
|
|
34
|
+
{
|
|
35
|
+
phase: 2,
|
|
36
|
+
phase_name: 'BUILD',
|
|
37
|
+
status: 'completed',
|
|
38
|
+
reqs: {
|
|
39
|
+
'REQ-001': { name: 'Implement login', status: 'completed' },
|
|
40
|
+
'REQ-002': { name: 'Implement logout', status: 'completed' },
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
],
|
|
44
|
+
...overrides,
|
|
45
|
+
};
|
|
46
|
+
fs.writeFileSync(path.join(stateDir, 'sprint-state.json'), JSON.stringify(state));
|
|
47
|
+
return state;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function mockGhCli(issues) {
|
|
51
|
+
const { execSync } = require('child_process');
|
|
52
|
+
vi.spyOn(require('child_process'), 'execSync').mockImplementation((cmd) => {
|
|
53
|
+
if (cmd.includes('gh issue list')) {
|
|
54
|
+
return JSON.stringify(issues);
|
|
55
|
+
}
|
|
56
|
+
return '';
|
|
57
|
+
});
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
describe('readSprintState', () => {
|
|
61
|
+
it('returns null when no sprint state exists', () => {
|
|
62
|
+
const { readSprintState } = require('../next-sprint');
|
|
63
|
+
const result = readSprintState(tmpDir);
|
|
64
|
+
expect(result).toBeNull();
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
it('returns parsed state when sprint state exists', () => {
|
|
68
|
+
const state = createState();
|
|
69
|
+
const { readSprintState } = require('../next-sprint');
|
|
70
|
+
const result = readSprintState(tmpDir);
|
|
71
|
+
expect(result).toBeDefined();
|
|
72
|
+
expect(result.id).toBe('sprint-2026-06-30-01');
|
|
73
|
+
});
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
describe('getCurrentSprintReqs', () => {
|
|
77
|
+
it('extracts requirements from sprint state', () => {
|
|
78
|
+
createState();
|
|
79
|
+
const { readSprintState, getCurrentSprintReqs } = require('../next-sprint');
|
|
80
|
+
const state = readSprintState(tmpDir);
|
|
81
|
+
const reqs = getCurrentSprintReqs(state);
|
|
82
|
+
expect(reqs).toHaveLength(2);
|
|
83
|
+
expect(reqs[0].name).toBe('Implement login');
|
|
84
|
+
expect(reqs[1].name).toBe('Implement logout');
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('returns empty array when no phase history', () => {
|
|
88
|
+
const { getCurrentSprintReqs } = require('../next-sprint');
|
|
89
|
+
const reqs = getCurrentSprintReqs({ phase_history: [] });
|
|
90
|
+
expect(reqs).toEqual([]);
|
|
91
|
+
});
|
|
92
|
+
});
|
|
93
|
+
|
|
94
|
+
describe('filterRemainingIssues', () => {
|
|
95
|
+
it('filters out issues already in sprint', () => {
|
|
96
|
+
const issues = [
|
|
97
|
+
{ number: 1, title: 'Implement login', labels: [], createdAt: '2026-06-30T00:00:00Z' },
|
|
98
|
+
{ number: 2, title: 'Implement logout', labels: [], createdAt: '2026-06-30T00:00:00Z' },
|
|
99
|
+
{ number: 3, title: 'Implement signup', labels: [], createdAt: '2026-06-30T00:00:00Z' },
|
|
100
|
+
];
|
|
101
|
+
const sprintReqs = [{ name: 'Implement login' }, { name: 'Implement logout' }];
|
|
102
|
+
const { filterRemainingIssues } = require('../next-sprint');
|
|
103
|
+
const result = filterRemainingIssues(issues, sprintReqs);
|
|
104
|
+
expect(result).toHaveLength(1);
|
|
105
|
+
expect(result[0].number).toBe(3);
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
it('returns all issues when no sprint reqs', () => {
|
|
109
|
+
const issues = [
|
|
110
|
+
{ number: 1, title: 'Implement login', labels: [], createdAt: '2026-06-30T00:00:00Z' },
|
|
111
|
+
{ number: 2, title: 'Implement logout', labels: [], createdAt: '2026-06-30T00:00:00Z' },
|
|
112
|
+
];
|
|
113
|
+
const { filterRemainingIssues } = require('../next-sprint');
|
|
114
|
+
const result = filterRemainingIssues(issues, []);
|
|
115
|
+
expect(result).toHaveLength(2);
|
|
116
|
+
});
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
describe('formatIssuesTable', () => {
|
|
120
|
+
it('formats issues as table', () => {
|
|
121
|
+
const issues = [
|
|
122
|
+
{ number: 1, title: 'Implement login', labels: [{ name: 'feature' }], createdAt: '2026-06-30T00:00:00Z' },
|
|
123
|
+
{ number: 2, title: 'Implement logout', labels: [{ name: 'bug' }], createdAt: '2026-06-30T00:00:00Z' },
|
|
124
|
+
];
|
|
125
|
+
const { formatIssuesTable } = require('../next-sprint');
|
|
126
|
+
const result = formatIssuesTable(issues);
|
|
127
|
+
expect(result).toContain('Remaining Open Issues');
|
|
128
|
+
expect(result).toContain('Implement login');
|
|
129
|
+
expect(result).toContain('feature');
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
it('handles empty issues', () => {
|
|
133
|
+
const { formatIssuesTable } = require('../next-sprint');
|
|
134
|
+
const result = formatIssuesTable([]);
|
|
135
|
+
expect(result).toBe('No remaining issues found.');
|
|
136
|
+
});
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
describe('generateSprintPlan', () => {
|
|
140
|
+
it('generates sprint plan for remaining issues', () => {
|
|
141
|
+
const issues = [
|
|
142
|
+
{ number: 1, title: 'Implement login', labels: [], createdAt: '2026-06-30T00:00:00Z' },
|
|
143
|
+
{ number: 2, title: 'Implement logout', labels: [], createdAt: '2026-06-30T00:00:00Z' },
|
|
144
|
+
];
|
|
145
|
+
const { generateSprintPlan } = require('../next-sprint');
|
|
146
|
+
const result = generateSprintPlan(issues);
|
|
147
|
+
expect(result).toContain('Suggested Next Sprint Plan');
|
|
148
|
+
expect(result).toContain('#1');
|
|
149
|
+
expect(result).toContain('sprint-flow');
|
|
150
|
+
});
|
|
151
|
+
|
|
152
|
+
it('returns null when no issues', () => {
|
|
153
|
+
const { generateSprintPlan } = require('../next-sprint');
|
|
154
|
+
const result = generateSprintPlan([]);
|
|
155
|
+
expect(result).toBeNull();
|
|
156
|
+
});
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
describe('handleNextSprint', () => {
|
|
160
|
+
it('outputs remaining issues in table format', async () => {
|
|
161
|
+
createState();
|
|
162
|
+
const { execSync } = require('child_process');
|
|
163
|
+
vi.spyOn(require('child_process'), 'execSync').mockImplementation((cmd) => {
|
|
164
|
+
if (cmd.includes('gh issue list')) {
|
|
165
|
+
return JSON.stringify([
|
|
166
|
+
{ number: 1, title: 'Implement login', labels: [{ name: 'feature' }], createdAt: '2026-06-30T00:00:00Z' },
|
|
167
|
+
{ number: 2, title: 'Implement logout', labels: [{ name: 'bug' }], createdAt: '2026-06-30T00:00:00Z' },
|
|
168
|
+
{ number: 3, title: 'Implement signup', labels: [{ name: 'enhancement' }], createdAt: '2026-06-30T00:00:00Z' },
|
|
169
|
+
]);
|
|
170
|
+
}
|
|
171
|
+
return '';
|
|
172
|
+
});
|
|
173
|
+
delete require.cache[require.resolve('../next-sprint')];
|
|
174
|
+
const { handleNextSprint } = require('../next-sprint');
|
|
175
|
+
const result = await handleNextSprint(['--dir', tmpDir]);
|
|
176
|
+
expect(result).toBe(0);
|
|
177
|
+
expect(consoleSpy).toHaveBeenCalled();
|
|
178
|
+
const output = consoleSpy.mock.calls.map(c => c[0]).join('\n');
|
|
179
|
+
expect(output).toContain('Remaining Open Issues');
|
|
180
|
+
expect(output).toContain('Implement signup');
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
it('outputs JSON when --json flag', async () => {
|
|
184
|
+
createState();
|
|
185
|
+
vi.spyOn(require('child_process'), 'execSync').mockImplementation((cmd) => {
|
|
186
|
+
if (cmd.includes('gh issue list')) {
|
|
187
|
+
return JSON.stringify([
|
|
188
|
+
{ number: 1, title: 'Implement login', labels: [], createdAt: '2026-06-30T00:00:00Z' },
|
|
189
|
+
]);
|
|
190
|
+
}
|
|
191
|
+
return '';
|
|
192
|
+
});
|
|
193
|
+
delete require.cache[require.resolve('../next-sprint')];
|
|
194
|
+
const { handleNextSprint } = require('../next-sprint');
|
|
195
|
+
const result = await handleNextSprint(['--json', '--dir', tmpDir]);
|
|
196
|
+
expect(result).toBe(0);
|
|
197
|
+
const output = consoleSpy.mock.calls.map(c => c[0]).join('\n');
|
|
198
|
+
const parsed = JSON.parse(output);
|
|
199
|
+
expect(parsed).toHaveProperty('current_sprint');
|
|
200
|
+
expect(parsed).toHaveProperty('remaining_issues');
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
it('outputs plan when --plan flag', async () => {
|
|
204
|
+
createState();
|
|
205
|
+
vi.spyOn(require('child_process'), 'execSync').mockImplementation((cmd) => {
|
|
206
|
+
if (cmd.includes('gh issue list')) {
|
|
207
|
+
return JSON.stringify([
|
|
208
|
+
{ number: 5, title: 'Implement password reset', labels: [], createdAt: '2026-06-30T00:00:00Z' },
|
|
209
|
+
]);
|
|
210
|
+
}
|
|
211
|
+
return '';
|
|
212
|
+
});
|
|
213
|
+
delete require.cache[require.resolve('../next-sprint')];
|
|
214
|
+
const { handleNextSprint } = require('../next-sprint');
|
|
215
|
+
const result = await handleNextSprint(['--plan', '--dir', tmpDir]);
|
|
216
|
+
expect(result).toBe(0);
|
|
217
|
+
const output = consoleSpy.mock.calls.map(c => c[0]).join('\n');
|
|
218
|
+
expect(output).toContain('Suggested Next Sprint Plan');
|
|
219
|
+
});
|
|
220
|
+
|
|
221
|
+
it('returns 1 when gh CLI not available', async () => {
|
|
222
|
+
vi.spyOn(require('child_process'), 'execSync').mockImplementation(() => {
|
|
223
|
+
throw new Error('gh: command not found');
|
|
224
|
+
});
|
|
225
|
+
delete require.cache[require.resolve('../next-sprint')];
|
|
226
|
+
const { handleNextSprint } = require('../next-sprint');
|
|
227
|
+
const result = await handleNextSprint(['--dir', tmpDir]);
|
|
228
|
+
expect(result).toBe(1);
|
|
229
|
+
});
|
|
230
|
+
});
|
|
231
|
+
});
|
package/lib/doctor.js
CHANGED
|
@@ -176,7 +176,20 @@ function checkSingleHook(hooksDir, name, signature, label, checks) {
|
|
|
176
176
|
return 0;
|
|
177
177
|
}
|
|
178
178
|
|
|
179
|
+
/**
|
|
180
|
+
* Expected gate scripts that should be present in the adapters directory.
|
|
181
|
+
* gate-5.sh and gate-6.sh are inline in pre-commit, so they are NOT expected here.
|
|
182
|
+
*/
|
|
183
|
+
const EXPECTED_GATE_SCRIPTS = [
|
|
184
|
+
'gate-3.sh',
|
|
185
|
+
'gate-4.sh',
|
|
186
|
+
'gate-7.sh',
|
|
187
|
+
'gate-8.sh',
|
|
188
|
+
'gate-9.sh',
|
|
189
|
+
];
|
|
190
|
+
|
|
179
191
|
function checkAdapters(checks, mode, gitDir) {
|
|
192
|
+
let issues = 0;
|
|
180
193
|
const adaptersDir = mode === 'local'
|
|
181
194
|
? path.join(path.dirname(gitDir || ''), 'githooks', 'adapters')
|
|
182
195
|
: GLOBAL_ADAPTERS_DIR;
|
|
@@ -190,8 +203,22 @@ function checkAdapters(checks, mode, gitDir) {
|
|
|
190
203
|
checks.push({ name: 'Adapters directory', status: 'FAIL', detail: 'Empty directory' });
|
|
191
204
|
return 1;
|
|
192
205
|
}
|
|
193
|
-
checks.push({ name: 'Adapters directory', status: 'PASS', detail: `${adapterFiles.length}
|
|
194
|
-
|
|
206
|
+
checks.push({ name: 'Adapters directory', status: 'PASS', detail: `${adapterFiles.length} file(s)` });
|
|
207
|
+
|
|
208
|
+
// Check for missing gate scripts (Issue #263 follow-up)
|
|
209
|
+
const missingGates = EXPECTED_GATE_SCRIPTS.filter(g => !adapterFiles.includes(g));
|
|
210
|
+
if (missingGates.length > 0) {
|
|
211
|
+
checks.push({
|
|
212
|
+
name: 'Gate scripts',
|
|
213
|
+
status: 'FAIL',
|
|
214
|
+
detail: `Missing: ${missingGates.join(', ')} — run 'xp-gate doctor --fix' to restore`
|
|
215
|
+
});
|
|
216
|
+
issues++;
|
|
217
|
+
} else {
|
|
218
|
+
checks.push({ name: 'Gate scripts', status: 'PASS', detail: `${EXPECTED_GATE_SCRIPTS.length} gate script(s)` });
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
return issues;
|
|
195
222
|
}
|
|
196
223
|
|
|
197
224
|
/**
|
|
@@ -429,6 +456,37 @@ function fixMissingAdapters(mode, srcDir, adaptersDir) {
|
|
|
429
456
|
return false;
|
|
430
457
|
}
|
|
431
458
|
|
|
459
|
+
/**
|
|
460
|
+
* Restore missing gate scripts from package root to adapters directory.
|
|
461
|
+
* Gate scripts (gate-3.sh through gate-9.sh) are stored in the package root,
|
|
462
|
+
* not in the adapters subdirectory.
|
|
463
|
+
*/
|
|
464
|
+
function fixMissingGateScripts(srcDir, adaptersDir) {
|
|
465
|
+
if (!adaptersDir || !fs.existsSync(adaptersDir)) return false;
|
|
466
|
+
|
|
467
|
+
const existingFiles = fs.readdirSync(adaptersDir);
|
|
468
|
+
const missingGates = EXPECTED_GATE_SCRIPTS.filter(g => !existingFiles.includes(g));
|
|
469
|
+
|
|
470
|
+
if (missingGates.length === 0) return false;
|
|
471
|
+
|
|
472
|
+
let restored = 0;
|
|
473
|
+
for (const gateScript of missingGates) {
|
|
474
|
+
const srcFile = path.join(srcDir, gateScript);
|
|
475
|
+
const destFile = path.join(adaptersDir, gateScript);
|
|
476
|
+
if (fs.existsSync(srcFile)) {
|
|
477
|
+
fs.copyFileSync(srcFile, destFile);
|
|
478
|
+
fs.chmodSync(destFile, 0o755);
|
|
479
|
+
restored++;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
|
|
483
|
+
if (restored > 0) {
|
|
484
|
+
console.log(` ✓ Restored ${restored} gate script(s)`);
|
|
485
|
+
return true;
|
|
486
|
+
}
|
|
487
|
+
return false;
|
|
488
|
+
}
|
|
489
|
+
|
|
432
490
|
function fixConfigMismatches(config) {
|
|
433
491
|
let fixed = false;
|
|
434
492
|
fixed = fixVersionMismatch(config, getPackageVersion()) || fixed;
|
|
@@ -481,6 +539,7 @@ function fixIssues(checks, config) {
|
|
|
481
539
|
fixed = fixHooksByMode(config, srcDir) || fixed;
|
|
482
540
|
fixed = fixGlobalHooksPath(config) || fixed;
|
|
483
541
|
fixed = fixMissingAdapters(config.mode, srcDir, getAdaptersDirByMode(config)) || fixed;
|
|
542
|
+
fixed = fixMissingGateScripts(srcDir, getAdaptersDirByMode(config)) || fixed;
|
|
484
543
|
fixed = fixTuiRegistration() || fixed;
|
|
485
544
|
fixed = printCliToolGuidance() || fixed;
|
|
486
545
|
|
package/lib/init.js
CHANGED
|
@@ -35,6 +35,13 @@ function copyAdapters(srcDir, destDir) {
|
|
|
35
35
|
}
|
|
36
36
|
});
|
|
37
37
|
}
|
|
38
|
+
// Copy gate scripts (gate-3.sh through gate-9.sh) from package root to destDir.
|
|
39
|
+
// These are sourced by pre-commit via GATE_DIR which resolves to the adapter dir.
|
|
40
|
+
fs.readdirSync(srcDir).forEach(f => {
|
|
41
|
+
if (f.startsWith('gate-') && f.endsWith('.sh')) {
|
|
42
|
+
fs.copyFileSync(path.join(srcDir, f), path.join(destDir, f));
|
|
43
|
+
}
|
|
44
|
+
});
|
|
38
45
|
}
|
|
39
46
|
|
|
40
47
|
function copyRecursive(src, dest) {
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
const childProcess = require('child_process');
|
|
2
|
+
const fs = require('fs');
|
|
3
|
+
const path = require('path');
|
|
4
|
+
|
|
5
|
+
function readSprintState(dir) {
|
|
6
|
+
try {
|
|
7
|
+
const stateFile = path.join(dir, '.sprint-state', 'sprint-state.json');
|
|
8
|
+
if (!fs.existsSync(stateFile)) return null;
|
|
9
|
+
return JSON.parse(fs.readFileSync(stateFile, 'utf8'));
|
|
10
|
+
} catch {
|
|
11
|
+
return null;
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
function getCurrentSprintReqs(state) {
|
|
16
|
+
if (!state?.phase_history) return [];
|
|
17
|
+
const reqs = [];
|
|
18
|
+
for (const phase of state.phase_history) {
|
|
19
|
+
if (phase.reqs) {
|
|
20
|
+
for (const [id, req] of Object.entries(phase.reqs)) {
|
|
21
|
+
reqs.push({ id, name: req.name, status: req.status });
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return reqs;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function fetchOpenIssues(repoRoot) {
|
|
29
|
+
try {
|
|
30
|
+
const result = childProcess.execSync('gh issue list --state open --json number,title,labels,createdAt', {
|
|
31
|
+
cwd: repoRoot,
|
|
32
|
+
encoding: 'utf8',
|
|
33
|
+
timeout: 30000,
|
|
34
|
+
});
|
|
35
|
+
return JSON.parse(result);
|
|
36
|
+
} catch (err) {
|
|
37
|
+
if (err.message.includes('gh: command not found')) {
|
|
38
|
+
console.error('Error: gh CLI not found. Install it from https://cli.github.com/');
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
if (err.message.includes('not logged in')) {
|
|
42
|
+
console.error('Error: Not logged in to GitHub. Run "gh auth login" first.');
|
|
43
|
+
return null;
|
|
44
|
+
}
|
|
45
|
+
console.error('Error fetching issues:', err.message);
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function filterRemainingIssues(issues, sprintReqs) {
|
|
51
|
+
const sprintTitles = new Set(sprintReqs.map(r => r.name?.toLowerCase()));
|
|
52
|
+
return issues.filter(issue => {
|
|
53
|
+
const titleLower = issue.title.toLowerCase();
|
|
54
|
+
return !sprintTitles.has(titleLower);
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function formatIssuesTable(issues) {
|
|
59
|
+
if (issues.length === 0) return 'No remaining issues found.';
|
|
60
|
+
const lines = ['Remaining Open Issues:', ''];
|
|
61
|
+
lines.push('┌──────┬────────────────────────────────────────────┬─────────────────────┬───────────┐');
|
|
62
|
+
lines.push('│ # │ Title │ Labels │ Created │');
|
|
63
|
+
lines.push('├──────┼────────────────────────────────────────────┼─────────────────────┼───────────┤');
|
|
64
|
+
for (const issue of issues) {
|
|
65
|
+
const num = String(issue.number).padEnd(4);
|
|
66
|
+
const title = issue.title.slice(0, 40).padEnd(40);
|
|
67
|
+
const labels = (issue.labels || []).map(l => l.name).join(', ').slice(0, 19).padEnd(19);
|
|
68
|
+
const created = issue.createdAt ? issue.createdAt.slice(0, 10) : 'N/A';
|
|
69
|
+
lines.push(`│ ${num} │ ${title} │ ${labels} │ ${created} │`);
|
|
70
|
+
}
|
|
71
|
+
lines.push('└──────┴────────────────────────────────────────────┴─────────────────────┴───────────┘');
|
|
72
|
+
lines.push(`Total: ${issues.length} remaining issues`);
|
|
73
|
+
return lines.join('\n');
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function generateSprintPlan(issues) {
|
|
77
|
+
if (issues.length === 0) return null;
|
|
78
|
+
const lines = ['Suggested Next Sprint Plan:', ''];
|
|
79
|
+
const prioritized = issues.slice(0, 5);
|
|
80
|
+
for (let i = 0; i < prioritized.length; i++) {
|
|
81
|
+
const issue = prioritized[i];
|
|
82
|
+
lines.push(`${i + 1}. #${issue.number} - ${issue.title}`);
|
|
83
|
+
}
|
|
84
|
+
if (issues.length > 5) {
|
|
85
|
+
lines.push(`... and ${issues.length - 5} more issues`);
|
|
86
|
+
}
|
|
87
|
+
lines.push('');
|
|
88
|
+
lines.push('To start a sprint, run:');
|
|
89
|
+
lines.push(` /sprint-flow "Implement #${prioritized[0].number}: ${prioritized[0].title}"`);
|
|
90
|
+
return lines.join('\n');
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
async function handleNextSprint(args = []) {
|
|
94
|
+
const jsonFlag = args.includes('--json');
|
|
95
|
+
const planFlag = args.includes('--plan');
|
|
96
|
+
const dirIdx = args.indexOf('--dir');
|
|
97
|
+
let repoRoot = process.cwd();
|
|
98
|
+
if (dirIdx >= 0 && dirIdx + 1 < args.length) {
|
|
99
|
+
repoRoot = path.resolve(args[dirIdx + 1]);
|
|
100
|
+
}
|
|
101
|
+
const state = readSprintState(repoRoot);
|
|
102
|
+
const sprintReqs = state ? getCurrentSprintReqs(state) : [];
|
|
103
|
+
const issues = fetchOpenIssues(repoRoot);
|
|
104
|
+
if (issues === null) return 1;
|
|
105
|
+
const remaining = filterRemainingIssues(issues, sprintReqs);
|
|
106
|
+
if (jsonFlag) {
|
|
107
|
+
console.log(JSON.stringify({ current_sprint: state?.id || null, sprint_reqs: sprintReqs, remaining_issues: remaining }, null, 2));
|
|
108
|
+
return 0;
|
|
109
|
+
}
|
|
110
|
+
console.log(formatIssuesTable(remaining));
|
|
111
|
+
if (planFlag) {
|
|
112
|
+
const plan = generateSprintPlan(remaining);
|
|
113
|
+
if (plan) {
|
|
114
|
+
console.log('');
|
|
115
|
+
console.log(plan);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
return 0;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
module.exports = {
|
|
122
|
+
handleNextSprint,
|
|
123
|
+
fetchOpenIssues,
|
|
124
|
+
filterRemainingIssues,
|
|
125
|
+
formatIssuesTable,
|
|
126
|
+
generateSprintPlan,
|
|
127
|
+
readSprintState,
|
|
128
|
+
getCurrentSprintReqs,
|
|
129
|
+
};
|
package/mock-policy/AGENTS.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SRC/MOCK-POLICY KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-07-01
|
|
4
|
-
**Commit:**
|
|
5
|
-
**Branch:**
|
|
6
|
-
**Version:** 0.
|
|
4
|
+
**Commit:** 99e2300
|
|
5
|
+
**Branch:** main
|
|
6
|
+
**Version:** 0.12.1.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Mock layering policy enforcement — Gate M3 of pre-push hook. Ensures integration tests use real implementations for internal dependencies, mock external dependencies, and annotate pending mocks with removal plans. Combines project scope scanning, mock decision engine, and per-file validation into a single pipeline.
|
package/mutation/AGENTS.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SRC/MUTATION KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-07-01
|
|
4
|
-
**Commit:**
|
|
5
|
-
**Branch:**
|
|
6
|
-
**Version:** 0.
|
|
4
|
+
**Commit:** 99e2300
|
|
5
|
+
**Branch:** main
|
|
6
|
+
**Version:** 0.12.1.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
**Gate M** (incremental mutation testing) + **Gate M2** helpers (test-layer detection used by `src/mock-policy/`). Pre-push quality gate. TypeScript-only; uses Stryker.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xp-gate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"displayName": "XP-Gate",
|
|
5
5
|
"description": "Extreme Programming quality gates + AI workflow skills for Claude Code. Includes 10 quality gates (Gate 0-9), Sprint Flow (11 phases), and Delphi multi-expert review (>=90% consensus).",
|
|
6
6
|
"author": {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-07-01
|
|
4
|
-
**Commit:**
|
|
5
|
-
**Branch:**
|
|
6
|
-
**Version:** 0.
|
|
4
|
+
**Commit:** 99e2300
|
|
5
|
+
**Branch:** main
|
|
6
|
+
**Version:** 0.12.1.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, 3 experts from ≥2 providers, domestic models only). Supports design + code-walkthrough modes.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/SPRINT-FLOW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-07-01
|
|
4
|
-
**Commit:**
|
|
5
|
-
**Branch:**
|
|
6
|
-
**Version:** 0.
|
|
4
|
+
**Commit:** 99e2300
|
|
5
|
+
**Branch:** main
|
|
6
|
+
**Version:** 0.12.1.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
**11-phase** development pipeline: ISOLATE → AUTO-ESTIMATE → THINK → PLAN → BUILD → REVIEW → SHIP → LAND → USER ACCEPTANCE → FEEDBACK → CLEANUP. Phase 2 default build mode is **ralph-loop** (REQ-level iteration, 40-67% token savings vs parallel). HARD-GATE in Phase 1: design must pass Delphi review (≥90% consensus) before any coding.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-07-01
|
|
4
|
-
**Commit:**
|
|
5
|
-
**Branch:**
|
|
6
|
-
**Version:** 0.
|
|
4
|
+
**Commit:** 99e2300
|
|
5
|
+
**Branch:** main
|
|
6
|
+
**Version:** 0.12.1.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-07-01
|
|
4
|
-
**Commit:**
|
|
5
|
-
**Branch:**
|
|
6
|
-
**Version:** 0.
|
|
4
|
+
**Commit:** 99e2300
|
|
5
|
+
**Branch:** main
|
|
6
|
+
**Version:** 0.12.1.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, 3 experts from ≥2 providers, domestic models only). Supports design + code-walkthrough modes.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/SPRINT-FLOW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-07-01
|
|
4
|
-
**Commit:**
|
|
5
|
-
**Branch:**
|
|
6
|
-
**Version:** 0.
|
|
4
|
+
**Commit:** 99e2300
|
|
5
|
+
**Branch:** main
|
|
6
|
+
**Version:** 0.12.1.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
**11-phase** development pipeline: ISOLATE → AUTO-ESTIMATE → THINK → PLAN → BUILD → REVIEW → SHIP → LAND → USER ACCEPTANCE → FEEDBACK → CLEANUP. Phase 2 default build mode is **ralph-loop** (REQ-level iteration, 40-67% token savings vs parallel). HARD-GATE in Phase 1: design must pass Delphi review (≥90% consensus) before any coding.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-07-01
|
|
4
|
-
**Commit:**
|
|
5
|
-
**Branch:**
|
|
6
|
-
**Version:** 0.
|
|
4
|
+
**Commit:** 99e2300
|
|
5
|
+
**Branch:** main
|
|
6
|
+
**Version:** 0.12.1.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "xp-gate",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.12.1",
|
|
4
4
|
"displayName": "XP-Gate",
|
|
5
5
|
"description": "Extreme Programming quality gates + AI workflow skills for Qoder. Includes 10 quality gates (Gate 0-9), Sprint Flow (11 phases), and Delphi multi-expert review (>=90% consensus).",
|
|
6
6
|
"author": {
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-07-01
|
|
4
|
-
**Commit:**
|
|
5
|
-
**Branch:**
|
|
6
|
-
**Version:** 0.
|
|
4
|
+
**Commit:** 99e2300
|
|
5
|
+
**Branch:** main
|
|
6
|
+
**Version:** 0.12.1.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, 3 experts from ≥2 providers, domestic models only). Supports design + code-walkthrough modes.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/SPRINT-FLOW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-07-01
|
|
4
|
-
**Commit:**
|
|
5
|
-
**Branch:**
|
|
6
|
-
**Version:** 0.
|
|
4
|
+
**Commit:** 99e2300
|
|
5
|
+
**Branch:** main
|
|
6
|
+
**Version:** 0.12.1.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
**11-phase** development pipeline: ISOLATE → AUTO-ESTIMATE → THINK → PLAN → BUILD → REVIEW → USER ACCEPTANCE → FEEDBACK → SHIP → LAND → CLEANUP. Phase 2 default build mode is **ralph-loop** (REQ-level iteration, 40-67% token savings vs parallel). HARD-GATE in Phase 1: design must pass Delphi review (≥90% consensus) before any coding.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-07-01
|
|
4
|
-
**Commit:**
|
|
5
|
-
**Branch:**
|
|
6
|
-
**Version:** 0.
|
|
4
|
+
**Commit:** 99e2300
|
|
5
|
+
**Branch:** main
|
|
6
|
+
**Version:** 0.12.1.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
|
package/principles/AGENTS.md
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# PRINCIPLES CHECKER MODULE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-07-01
|
|
4
|
-
**Commit:**
|
|
5
|
-
**Branch:**
|
|
6
|
-
**Version:** 0.
|
|
4
|
+
**Commit:** 99e2300
|
|
5
|
+
**Branch:** main
|
|
6
|
+
**Version:** 0.12.1.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Clean Code & SOLID principles checker — **Gate 4** of pre-commit. 14 rules × 9 language adapters, SARIF 2.1.0 output. Houses the **Boy Scout Rule** enforcement engine (Gate 6) and warning-baseline storage.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/DELPHI-REVIEW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-07-01
|
|
4
|
-
**Commit:**
|
|
5
|
-
**Branch:**
|
|
6
|
-
**Version:** 0.
|
|
4
|
+
**Commit:** 99e2300
|
|
5
|
+
**Branch:** main
|
|
6
|
+
**Version:** 0.12.1.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Delphi Consensus Review — multi-round anonymous expert review (≥90% threshold, 3 experts from ≥2 providers, domestic models only). Supports design + code-walkthrough modes.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/SPRINT-FLOW KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-07-01
|
|
4
|
-
**Commit:**
|
|
5
|
-
**Branch:**
|
|
6
|
-
**Version:** 0.
|
|
4
|
+
**Commit:** 99e2300
|
|
5
|
+
**Branch:** main
|
|
6
|
+
**Version:** 0.12.1.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
**11-phase** development pipeline: ISOLATE → AUTO-ESTIMATE → THINK → PLAN → BUILD → REVIEW → SHIP → LAND → USER ACCEPTANCE → FEEDBACK → CLEANUP. Phase 2 default build mode is **ralph-loop** (REQ-level iteration, 40-67% token savings vs parallel). HARD-GATE in Phase 1: design must pass Delphi review (≥90% consensus) before any coding.
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
# SKILLS/TEST-SPECIFICATION-ALIGNMENT KNOWLEDGE BASE
|
|
2
2
|
|
|
3
3
|
**Generated:** 2026-07-01
|
|
4
|
-
**Commit:**
|
|
5
|
-
**Branch:**
|
|
6
|
-
**Version:** 0.
|
|
4
|
+
**Commit:** 99e2300
|
|
5
|
+
**Branch:** main
|
|
6
|
+
**Version:** 0.12.1.0
|
|
7
7
|
|
|
8
8
|
## OVERVIEW
|
|
9
9
|
Test-Specification Alignment Engine — two-stage validation ensuring tests accurately reflect requirements and design specs.
|