@slope-dev/slope 1.30.0 → 1.31.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cli/commands/guard.d.ts.map +1 -1
- package/dist/cli/commands/guard.js +16 -1
- package/dist/cli/commands/guard.js.map +1 -1
- package/dist/cli/commands/phase.d.ts +10 -0
- package/dist/cli/commands/phase.d.ts.map +1 -0
- package/dist/cli/commands/phase.js +86 -0
- package/dist/cli/commands/phase.js.map +1 -0
- package/dist/cli/guards/claim-required.d.ts +8 -0
- package/dist/cli/guards/claim-required.d.ts.map +1 -0
- package/dist/cli/guards/claim-required.js +46 -0
- package/dist/cli/guards/claim-required.js.map +1 -0
- package/dist/cli/guards/explore.d.ts.map +1 -1
- package/dist/cli/guards/explore.js +82 -7
- package/dist/cli/guards/explore.js.map +1 -1
- package/dist/cli/guards/next-action.d.ts +4 -2
- package/dist/cli/guards/next-action.d.ts.map +1 -1
- package/dist/cli/guards/next-action.js +94 -85
- package/dist/cli/guards/next-action.js.map +1 -1
- package/dist/cli/guards/phase-boundary.d.ts +7 -0
- package/dist/cli/guards/phase-boundary.d.ts.map +1 -0
- package/dist/cli/guards/phase-boundary.js +93 -0
- package/dist/cli/guards/phase-boundary.js.map +1 -0
- package/dist/cli/guards/post-push.d.ts +8 -0
- package/dist/cli/guards/post-push.d.ts.map +1 -0
- package/dist/cli/guards/post-push.js +98 -0
- package/dist/cli/guards/post-push.js.map +1 -0
- package/dist/cli/guards/pr-review.d.ts.map +1 -1
- package/dist/cli/guards/pr-review.js +14 -22
- package/dist/cli/guards/pr-review.js.map +1 -1
- package/dist/cli/guards/review-stale.d.ts +8 -0
- package/dist/cli/guards/review-stale.d.ts.map +1 -0
- package/dist/cli/guards/review-stale.js +51 -0
- package/dist/cli/guards/review-stale.js.map +1 -0
- package/dist/cli/guards/session-briefing.d.ts +8 -0
- package/dist/cli/guards/session-briefing.d.ts.map +1 -0
- package/dist/cli/guards/session-briefing.js +100 -0
- package/dist/cli/guards/session-briefing.js.map +1 -0
- package/dist/cli/guards/sprint-completion.js +33 -0
- package/dist/cli/guards/sprint-completion.js.map +1 -1
- package/dist/cli/index.js +7 -0
- package/dist/cli/index.js.map +1 -1
- package/dist/cli/phase-cleanup.d.ts +25 -0
- package/dist/cli/phase-cleanup.d.ts.map +1 -0
- package/dist/cli/phase-cleanup.js +101 -0
- package/dist/cli/phase-cleanup.js.map +1 -0
- package/dist/cli/registry.d.ts +1 -1
- package/dist/cli/registry.d.ts.map +1 -1
- package/dist/cli/registry.js +1 -1
- package/dist/cli/registry.js.map +1 -1
- package/dist/cli/session-state.d.ts +18 -0
- package/dist/cli/session-state.d.ts.map +1 -0
- package/dist/cli/session-state.js +31 -0
- package/dist/cli/session-state.js.map +1 -0
- package/dist/core/adapters/claude-code.d.ts +2 -1
- package/dist/core/adapters/claude-code.d.ts.map +1 -1
- package/dist/core/adapters/claude-code.js +45 -9
- package/dist/core/adapters/claude-code.js.map +1 -1
- package/dist/core/adapters/cursor.d.ts +2 -1
- package/dist/core/adapters/cursor.d.ts.map +1 -1
- package/dist/core/adapters/cursor.js +47 -7
- package/dist/core/adapters/cursor.js.map +1 -1
- package/dist/core/adapters/windsurf.d.ts +2 -1
- package/dist/core/adapters/windsurf.d.ts.map +1 -1
- package/dist/core/adapters/windsurf.js +29 -12
- package/dist/core/adapters/windsurf.js.map +1 -1
- package/dist/core/guard.d.ts +21 -1
- package/dist/core/guard.d.ts.map +1 -1
- package/dist/core/guard.js +38 -0
- package/dist/core/guard.js.map +1 -1
- package/dist/core/index.d.ts +1 -1
- package/dist/core/index.d.ts.map +1 -1
- package/dist/core/index.js.map +1 -1
- package/package.json +1 -1
|
@@ -25,8 +25,8 @@ export async function nextActionGuard(input, cwd) {
|
|
|
25
25
|
}
|
|
26
26
|
// Detect sprint state
|
|
27
27
|
const state = await detectSprintState(cwd, input.session_id);
|
|
28
|
-
// Build suggestion
|
|
29
|
-
const suggestion =
|
|
28
|
+
// Build structured suggestion
|
|
29
|
+
const suggestion = buildSuggestionObject(state);
|
|
30
30
|
// Write state file atomically
|
|
31
31
|
try {
|
|
32
32
|
mkdirSync(slopeDir, { recursive: true });
|
|
@@ -39,7 +39,7 @@ export async function nextActionGuard(input, cwd) {
|
|
|
39
39
|
renameSync(tmpPath, stateFile);
|
|
40
40
|
}
|
|
41
41
|
catch { /* best-effort — don't fail the guard */ }
|
|
42
|
-
return {
|
|
42
|
+
return { suggestion };
|
|
43
43
|
}
|
|
44
44
|
/** Detect current sprint state via store then filesystem fallback */
|
|
45
45
|
export async function detectSprintState(cwd, sessionId) {
|
|
@@ -171,98 +171,107 @@ function buildBetweenSprints(config, cwd, latestSprint) {
|
|
|
171
171
|
catch { /* roadmap parsing failed — proceed without context */ }
|
|
172
172
|
return { type: 'between-sprints', roadmapContext };
|
|
173
173
|
}
|
|
174
|
-
/** Build
|
|
175
|
-
export function
|
|
176
|
-
const header = 'SLOPE next-action: Before ending this session, present the user with options for what to do next.';
|
|
174
|
+
/** Build structured Suggestion from sprint state */
|
|
175
|
+
export function buildSuggestionObject(state) {
|
|
177
176
|
switch (state.type) {
|
|
178
177
|
case 'mid-sprint': {
|
|
179
178
|
const targetList = state.targets.join(', ');
|
|
180
|
-
return
|
|
181
|
-
|
|
182
|
-
'',
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
179
|
+
return {
|
|
180
|
+
id: 'next-action-mid-sprint',
|
|
181
|
+
title: 'Next Action',
|
|
182
|
+
context: `Mid-sprint — ${state.claimCount} active claim(s) for sprint ${state.sprintNumber}: ${targetList}`,
|
|
183
|
+
options: [
|
|
184
|
+
{ id: 'continue', label: 'Continue with the next ticket' },
|
|
185
|
+
{ id: 'push-break', label: 'Push and take a break', description: 'Resume later' },
|
|
186
|
+
{ id: 'end', label: 'End session', description: 'Nothing more to do right now' },
|
|
187
|
+
],
|
|
188
|
+
requiresDecision: true,
|
|
189
|
+
priority: 'normal',
|
|
190
|
+
};
|
|
192
191
|
}
|
|
193
192
|
case 'sprint-complete':
|
|
194
|
-
return
|
|
195
|
-
|
|
196
|
-
'',
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
case 'needs-review':
|
|
208
|
-
return
|
|
209
|
-
|
|
210
|
-
'',
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
'',
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
'Present these using AskUserQuestion. If the user chooses to end the session, stop without further action.',
|
|
234
|
-
].join('\n');
|
|
235
|
-
}
|
|
193
|
+
return {
|
|
194
|
+
id: 'next-action-complete',
|
|
195
|
+
title: 'Next Action',
|
|
196
|
+
context: `Sprint ${state.sprintNumber} is complete but unscored`,
|
|
197
|
+
options: [
|
|
198
|
+
{ id: 'score', label: 'Score the sprint', description: 'Run post-hole routine' },
|
|
199
|
+
{ id: 'validate', label: 'Validate scorecard', command: 'slope validate' },
|
|
200
|
+
{ id: 'distill', label: 'Distill learnings', command: 'slope distill --auto' },
|
|
201
|
+
{ id: 'end', label: 'End session', description: 'Nothing more to do right now' },
|
|
202
|
+
],
|
|
203
|
+
requiresDecision: true,
|
|
204
|
+
priority: 'normal',
|
|
205
|
+
};
|
|
206
|
+
case 'needs-review':
|
|
207
|
+
return {
|
|
208
|
+
id: 'next-action-review',
|
|
209
|
+
title: 'Next Action',
|
|
210
|
+
context: `Sprint ${state.sprintNumber} has a scorecard but no review`,
|
|
211
|
+
options: [
|
|
212
|
+
{ id: 'review', label: 'Generate sprint review', command: 'slope review' },
|
|
213
|
+
{ id: 'distill', label: 'Distill learnings', command: 'slope distill --auto' },
|
|
214
|
+
{ id: 'end', label: 'End session', description: 'Nothing more to do right now' },
|
|
215
|
+
],
|
|
216
|
+
requiresDecision: true,
|
|
217
|
+
priority: 'normal',
|
|
218
|
+
};
|
|
219
|
+
case 'needs-amend':
|
|
220
|
+
return {
|
|
221
|
+
id: 'next-action-amend',
|
|
222
|
+
title: 'Next Action',
|
|
223
|
+
context: `Sprint ${state.sprintNumber} has ${state.findingCount} review finding(s) not yet applied to scorecard`,
|
|
224
|
+
options: [
|
|
225
|
+
{ id: 'amend', label: 'Apply findings to scorecard', command: 'slope review amend' },
|
|
226
|
+
{ id: 'list', label: 'View findings first', command: 'slope review findings list' },
|
|
227
|
+
{ id: 'end', label: 'End session', description: 'Nothing more to do right now' },
|
|
228
|
+
],
|
|
229
|
+
requiresDecision: true,
|
|
230
|
+
priority: 'normal',
|
|
231
|
+
};
|
|
236
232
|
case 'testing-active':
|
|
237
|
-
return
|
|
238
|
-
|
|
239
|
-
'',
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
233
|
+
return {
|
|
234
|
+
id: 'next-action-testing',
|
|
235
|
+
title: 'Next Action',
|
|
236
|
+
context: 'Testing session active',
|
|
237
|
+
options: [
|
|
238
|
+
{ id: 'continue', label: 'Continue testing' },
|
|
239
|
+
{ id: 'end-testing', label: 'End testing session' },
|
|
240
|
+
{ id: 'end', label: 'End session', description: 'Nothing more to do right now' },
|
|
241
|
+
],
|
|
242
|
+
requiresDecision: true,
|
|
243
|
+
priority: 'normal',
|
|
244
|
+
};
|
|
249
245
|
case 'between-sprints': {
|
|
250
246
|
const contextLine = state.roadmapContext
|
|
251
|
-
? `\n${state.roadmapContext}
|
|
247
|
+
? `\n${state.roadmapContext}`
|
|
252
248
|
: '';
|
|
253
|
-
return
|
|
254
|
-
|
|
255
|
-
'',
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
249
|
+
return {
|
|
250
|
+
id: 'next-action-between',
|
|
251
|
+
title: 'Next Action',
|
|
252
|
+
context: `No active sprint${contextLine}`,
|
|
253
|
+
options: [
|
|
254
|
+
{ id: 'roadmap', label: 'Check next sprint candidates' },
|
|
255
|
+
{ id: 'start', label: 'Start a new sprint' },
|
|
256
|
+
{ id: 'briefing', label: 'Run briefing', command: 'slope briefing' },
|
|
257
|
+
{ id: 'end', label: 'End session', description: 'Nothing more to do right now' },
|
|
258
|
+
],
|
|
259
|
+
requiresDecision: true,
|
|
260
|
+
priority: 'normal',
|
|
261
|
+
};
|
|
265
262
|
}
|
|
266
263
|
}
|
|
267
264
|
}
|
|
265
|
+
/** @deprecated Use buildSuggestionObject instead. Kept for backward compatibility. */
|
|
266
|
+
export function buildSuggestions(state) {
|
|
267
|
+
const suggestion = buildSuggestionObject(state);
|
|
268
|
+
const lines = [`SLOPE ${suggestion.title}: ${suggestion.context}`, '', 'Suggested options:'];
|
|
269
|
+
for (let i = 0; i < suggestion.options.length; i++) {
|
|
270
|
+
const opt = suggestion.options[i];
|
|
271
|
+
const desc = opt.description ? ` — ${opt.description}` : '';
|
|
272
|
+
lines.push(`${i + 1}. ${opt.label}${desc}`);
|
|
273
|
+
}
|
|
274
|
+
lines.push('', 'Present these using AskUserQuestion. If the user chooses to end the session, stop without further action.');
|
|
275
|
+
return lines.join('\n');
|
|
276
|
+
}
|
|
268
277
|
//# sourceMappingURL=next-action.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"next-action.js","sourceRoot":"","sources":["../../../src/cli/guards/next-action.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,aAAa,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AACzF,OAAO,EAAE,IAAI,EAAW,MAAM,WAAW,CAAC;AAE1C,OAAO,EAAE,UAAU,EAAkB,kBAAkB,EAAE,YAAY,EAAE,sBAAsB,EAAE,MAAM,qBAAqB,CAAC;AAC3H,OAAO,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAC3C,OAAO,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAC3D,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AAWrD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,eAAe,CAAC,KAAgB,EAAE,GAAW;IACjE,kEAAkE;IAClE,MAAM,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,QAAQ,CAAC,CAAC;IACrC,MAAM,SAAS,GAAG,IAAI,CAAC,QAAQ,EAAE,uBAAuB,CAAC,CAAC;IAE1D,IAAI,KAAK,CAAC,UAAU,EAAE,CAAC;QACrB,IAAI,CAAC;YACH,IAAI,UAAU,CAAC,SAAS,CAAC,EAAE,CAAC;gBAC1B,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,SAAS,EAAE,MAAM,CAAC,CAAC,CAAC;gBACxD,IAAI,GAAG,CAAC,UAAU,KAAK,KAAK,CAAC,UAAU,EAAE,CAAC;oBACxC,OAAO,EAAE,CAAC;gBACZ,CAAC;YACH,CAAC;QACH,CAAC;QAAC,MAAM,CAAC,CAAC,oCAAoC,CAAC,CAAC;IAClD,CAAC;IAED,sBAAsB;IACtB,MAAM,KAAK,GAAG,MAAM,iBAAiB,CAAC,GAAG,EAAE,KAAK,CAAC,UAAU,CAAC,CAAC;IAE7D,
|
|
1
|
+
{"version":3,"file":"next-action.js","sourceRoot":"","sources":["../../../src/cli/guards/next-action.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { HookInput, GuardResult } from '../../core/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Phase-boundary guard: fires PreToolUse on Bash.
|
|
4
|
+
* Blocks starting a sprint in Phase N+1 if Phase N cleanup is incomplete.
|
|
5
|
+
*/
|
|
6
|
+
export declare function phaseBoundaryGuard(input: HookInput, cwd: string): Promise<GuardResult>;
|
|
7
|
+
//# sourceMappingURL=phase-boundary.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"phase-boundary.d.ts","sourceRoot":"","sources":["../../../src/cli/guards/phase-boundary.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAc,MAAM,qBAAqB,CAAC;AAU9E;;;GAGG;AACH,wBAAsB,kBAAkB,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAmF5F"}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { loadConfig, parseRoadmap } from '../../core/index.js';
|
|
4
|
+
import { isPhaseComplete, pendingPhaseGates } from '../phase-cleanup.js';
|
|
5
|
+
/** Extract phase number from name like "Phase 7 — Helmsman 3D". Falls back to array index + 1. */
|
|
6
|
+
function extractPhaseNumber(name, index) {
|
|
7
|
+
const match = name.match(/Phase\s+(\d+)/i);
|
|
8
|
+
return match ? parseInt(match[1], 10) : index + 1;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Phase-boundary guard: fires PreToolUse on Bash.
|
|
12
|
+
* Blocks starting a sprint in Phase N+1 if Phase N cleanup is incomplete.
|
|
13
|
+
*/
|
|
14
|
+
export async function phaseBoundaryGuard(input, cwd) {
|
|
15
|
+
const command = input.tool_input?.command ?? '';
|
|
16
|
+
// Only match sprint-start or claim commands
|
|
17
|
+
if (!/\bslope\s+(sprint\s+start|claim)\b/.test(command))
|
|
18
|
+
return {};
|
|
19
|
+
// Parse target sprint number from command args
|
|
20
|
+
const sprintMatch = command.match(/--sprint[=\s]+(\d+)/i) ??
|
|
21
|
+
command.match(/\bS(\d+)\b/i) ??
|
|
22
|
+
command.match(/--target[=\s]+S?(\d+)/i);
|
|
23
|
+
// If we can't determine the target sprint, allow (don't block blindly)
|
|
24
|
+
if (!sprintMatch)
|
|
25
|
+
return {};
|
|
26
|
+
const targetSprint = parseInt(sprintMatch[1], 10);
|
|
27
|
+
// Load roadmap to determine phase mapping
|
|
28
|
+
const config = loadConfig(cwd);
|
|
29
|
+
let roadmap;
|
|
30
|
+
try {
|
|
31
|
+
const roadmapPath = join(cwd, config.roadmapPath);
|
|
32
|
+
if (!existsSync(roadmapPath))
|
|
33
|
+
return {};
|
|
34
|
+
const raw = JSON.parse(readFileSync(roadmapPath, 'utf8'));
|
|
35
|
+
const result = parseRoadmap(raw);
|
|
36
|
+
roadmap = result.roadmap;
|
|
37
|
+
}
|
|
38
|
+
catch {
|
|
39
|
+
return {
|
|
40
|
+
decision: 'deny',
|
|
41
|
+
blockReason: 'SLOPE phase-boundary: Cannot determine phase — roadmap unreadable. Run `slope roadmap validate`.',
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
if (!roadmap || !roadmap.phases)
|
|
45
|
+
return {};
|
|
46
|
+
// Build phase-to-number mapping (RoadmapPhase has name + sprints[], no id)
|
|
47
|
+
const phaseNumbers = roadmap.phases.map((p, i) => extractPhaseNumber(p.name, i));
|
|
48
|
+
// Find which phase the target sprint belongs to
|
|
49
|
+
let targetPhaseIdx = -1;
|
|
50
|
+
for (let i = 0; i < roadmap.phases.length; i++) {
|
|
51
|
+
if (Array.isArray(roadmap.phases[i].sprints) && roadmap.phases[i].sprints.includes(targetSprint)) {
|
|
52
|
+
targetPhaseIdx = i;
|
|
53
|
+
break;
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
if (targetPhaseIdx < 0)
|
|
57
|
+
return {}; // Sprint not in any phase — allow
|
|
58
|
+
if (targetPhaseIdx === 0)
|
|
59
|
+
return {}; // First phase — no previous phase to check
|
|
60
|
+
const targetPhaseNum = phaseNumbers[targetPhaseIdx];
|
|
61
|
+
// Use array order (not phase number arithmetic) to find the previous phase.
|
|
62
|
+
// This correctly handles non-sequential numbering: [Phase 1, Phase 3] → check Phase 1 before Phase 3.
|
|
63
|
+
const prevPhaseIdx = targetPhaseIdx - 1;
|
|
64
|
+
const prevPhaseNum = phaseNumbers[prevPhaseIdx];
|
|
65
|
+
// Check if previous phase cleanup is complete
|
|
66
|
+
if (isPhaseComplete(cwd, prevPhaseNum))
|
|
67
|
+
return {};
|
|
68
|
+
// Previous phase cleanup incomplete — block with suggestion
|
|
69
|
+
const pending = pendingPhaseGates(cwd, prevPhaseNum);
|
|
70
|
+
const suggestion = {
|
|
71
|
+
id: 'phase-boundary',
|
|
72
|
+
title: 'Phase Boundary',
|
|
73
|
+
context: `Phase ${prevPhaseNum} cleanup is incomplete. Complete these gates before starting Sprint ${targetSprint} (Phase ${targetPhaseNum}).`,
|
|
74
|
+
options: [
|
|
75
|
+
...pending.map((gate, i) => ({
|
|
76
|
+
id: `gate-${i}`,
|
|
77
|
+
label: gate,
|
|
78
|
+
})),
|
|
79
|
+
{
|
|
80
|
+
id: 'override',
|
|
81
|
+
label: 'Mark phase complete (manual override)',
|
|
82
|
+
command: `slope phase complete ${prevPhaseNum}`,
|
|
83
|
+
},
|
|
84
|
+
],
|
|
85
|
+
requiresDecision: true,
|
|
86
|
+
priority: 'critical',
|
|
87
|
+
};
|
|
88
|
+
return {
|
|
89
|
+
decision: 'deny',
|
|
90
|
+
suggestion,
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
//# sourceMappingURL=phase-boundary.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"phase-boundary.js","sourceRoot":"","sources":["../../../src/cli/guards/phase-boundary.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { HookInput, GuardResult } from '../../core/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Post-push guard: fires PostToolUse on Bash.
|
|
4
|
+
* After a successful git push, suggests next workflow step.
|
|
5
|
+
* Context-only (non-blocking), fires once per session.
|
|
6
|
+
*/
|
|
7
|
+
export declare function postPushGuard(input: HookInput, cwd: string): Promise<GuardResult>;
|
|
8
|
+
//# sourceMappingURL=post-push.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post-push.d.ts","sourceRoot":"","sources":["../../../src/cli/guards/post-push.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAc,MAAM,qBAAqB,CAAC;AAI9E;;;;GAIG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CAyFvF"}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { loadSprintState } from '../sprint-state.js';
|
|
4
|
+
import { loadSessionState, updateSessionState } from '../session-state.js';
|
|
5
|
+
/**
|
|
6
|
+
* Post-push guard: fires PostToolUse on Bash.
|
|
7
|
+
* After a successful git push, suggests next workflow step.
|
|
8
|
+
* Context-only (non-blocking), fires once per session.
|
|
9
|
+
*/
|
|
10
|
+
export async function postPushGuard(input, cwd) {
|
|
11
|
+
const command = input.tool_input?.command ?? '';
|
|
12
|
+
// Only fire after git push commands
|
|
13
|
+
if (!/git\s+push\b/.test(command))
|
|
14
|
+
return {};
|
|
15
|
+
// Check exit code — only fire on success
|
|
16
|
+
const response = input.tool_response ?? {};
|
|
17
|
+
const exitCode = response.exit_code ?? response.exitCode;
|
|
18
|
+
if (exitCode !== 0 && exitCode !== '0' && exitCode !== undefined)
|
|
19
|
+
return {};
|
|
20
|
+
// Session dedup: fire once per session
|
|
21
|
+
const sessionId = input.session_id;
|
|
22
|
+
if (!sessionId)
|
|
23
|
+
return {};
|
|
24
|
+
const sessionState = loadSessionState(cwd);
|
|
25
|
+
if (sessionState.push_prompted_session_id === sessionId)
|
|
26
|
+
return {};
|
|
27
|
+
// Mark as prompted
|
|
28
|
+
updateSessionState(cwd, 'push_prompted_session_id', sessionId);
|
|
29
|
+
// Determine workflow context
|
|
30
|
+
const sprintState = loadSprintState(cwd);
|
|
31
|
+
let contextText;
|
|
32
|
+
let options = [];
|
|
33
|
+
if (sprintState && sprintState.phase === 'implementing') {
|
|
34
|
+
// Check how many claims remain
|
|
35
|
+
let remainingClaims = 0;
|
|
36
|
+
try {
|
|
37
|
+
const claimsPath = join(cwd, '.slope', 'claims.json');
|
|
38
|
+
if (existsSync(claimsPath)) {
|
|
39
|
+
const claims = JSON.parse(readFileSync(claimsPath, 'utf8'));
|
|
40
|
+
if (Array.isArray(claims))
|
|
41
|
+
remainingClaims = claims.length;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch { /* claims unavailable */ }
|
|
45
|
+
// Check pending gates
|
|
46
|
+
const pendingGateCount = Object.values(sprintState.gates).filter(v => !v).length;
|
|
47
|
+
if (pendingGateCount === 0) {
|
|
48
|
+
contextText = `Sprint S${sprintState.sprint} — all gates complete. Ready for PR.`;
|
|
49
|
+
options = [
|
|
50
|
+
{ id: 'create-pr', label: 'Create PR', command: 'gh pr create' },
|
|
51
|
+
{ id: 'continue', label: 'Continue working' },
|
|
52
|
+
];
|
|
53
|
+
}
|
|
54
|
+
else if (remainingClaims > 0) {
|
|
55
|
+
contextText = `Sprint S${sprintState.sprint} — ${remainingClaims} claim(s) active, ${pendingGateCount} gate(s) pending.`;
|
|
56
|
+
options = [
|
|
57
|
+
{ id: 'next-ticket', label: 'Continue with next ticket' },
|
|
58
|
+
{ id: 'run-tests', label: 'Run tests', command: 'bun test' },
|
|
59
|
+
];
|
|
60
|
+
}
|
|
61
|
+
else {
|
|
62
|
+
contextText = `Sprint S${sprintState.sprint} — all tickets done. Scoring workflow: auto-card, validate, review, PR.`;
|
|
63
|
+
options = [
|
|
64
|
+
{ id: 'auto-card', label: 'Generate scorecard', command: 'slope auto-card' },
|
|
65
|
+
{ id: 'validate', label: 'Validate scorecard', command: 'slope validate' },
|
|
66
|
+
{ id: 'review', label: 'Generate review', command: 'slope review' },
|
|
67
|
+
];
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
else if (sprintState && sprintState.phase === 'scoring') {
|
|
71
|
+
contextText = `Sprint S${sprintState.sprint} — scoring phase. Complete remaining gates.`;
|
|
72
|
+
const pending = Object.entries(sprintState.gates)
|
|
73
|
+
.filter(([, v]) => !v)
|
|
74
|
+
.map(([k]) => k);
|
|
75
|
+
options = pending.map(g => ({
|
|
76
|
+
id: `gate-${g}`,
|
|
77
|
+
label: `Complete ${g}`,
|
|
78
|
+
command: g === 'scorecard' ? 'slope validate' : g === 'review_md' ? 'slope review' : `slope sprint gate ${g}`,
|
|
79
|
+
}));
|
|
80
|
+
}
|
|
81
|
+
else {
|
|
82
|
+
contextText = 'No active sprint. Run `slope briefing` or start a new sprint.';
|
|
83
|
+
options = [
|
|
84
|
+
{ id: 'briefing', label: 'Run briefing', command: 'slope briefing' },
|
|
85
|
+
{ id: 'start-sprint', label: 'Start new sprint' },
|
|
86
|
+
];
|
|
87
|
+
}
|
|
88
|
+
const suggestion = {
|
|
89
|
+
id: 'post-push',
|
|
90
|
+
title: 'Post-Push',
|
|
91
|
+
context: contextText,
|
|
92
|
+
options,
|
|
93
|
+
requiresDecision: false,
|
|
94
|
+
priority: 'normal',
|
|
95
|
+
};
|
|
96
|
+
return { suggestion };
|
|
97
|
+
}
|
|
98
|
+
//# sourceMappingURL=post-push.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"post-push.js","sourceRoot":"","sources":["../../../src/cli/guards/post-push.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pr-review.d.ts","sourceRoot":"","sources":["../../../src/cli/guards/pr-review.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,
|
|
1
|
+
{"version":3,"file":"pr-review.d.ts","sourceRoot":"","sources":["../../../src/cli/guards/pr-review.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAc,MAAM,qBAAqB,CAAC;AAE9E;;;GAGG;AACH,wBAAsB,aAAa,CAAC,KAAK,EAAE,SAAS,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CA8BxF"}
|
|
@@ -14,28 +14,20 @@ export async function prReviewGuard(input, _cwd) {
|
|
|
14
14
|
// Extract PR URL from response
|
|
15
15
|
const urlMatch = response.match(/(https:\/\/github\.com\/[^\s]+\/pull\/\d+)/);
|
|
16
16
|
const prUrl = urlMatch ? urlMatch[1] : 'the PR';
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
'
|
|
23
|
-
'',
|
|
24
|
-
'
|
|
25
|
-
'
|
|
26
|
-
'
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
'Wait for the user\'s choice before taking any further action.',
|
|
31
|
-
'',
|
|
32
|
-
'After the review is complete, capture findings:',
|
|
33
|
-
'1. For each issue found: `slope review findings add --type=<type> --ticket=<key> --severity=<sev> --description="..."`',
|
|
34
|
-
'2. After all findings recorded: `slope review amend` to apply to scorecard',
|
|
35
|
-
'3. Run `slope distill --auto` to promote recurring patterns',
|
|
36
|
-
'',
|
|
37
|
-
'Review type to finding type mapping: architect→architect, code→code',
|
|
38
|
-
].join('\n'),
|
|
17
|
+
const suggestion = {
|
|
18
|
+
id: 'pr-review',
|
|
19
|
+
title: 'PR Review',
|
|
20
|
+
context: `A pull request was just created (${prUrl}). After the review, capture findings with \`slope review findings add\`, then \`slope review amend\` to apply to scorecard.`,
|
|
21
|
+
options: [
|
|
22
|
+
{ id: 'code', label: 'Code Review', description: 'Detailed line-by-line code review of the diff' },
|
|
23
|
+
{ id: 'architect', label: 'Architect Review', description: 'High-level architecture and design review' },
|
|
24
|
+
{ id: 'both', label: 'Both', description: 'Run code review followed by architect review' },
|
|
25
|
+
{ id: 'manual', label: 'Manual Review', description: 'User will review manually, no automated review' },
|
|
26
|
+
{ id: 'skip', label: 'Skip / Merge Now', description: 'No review needed, proceed to merge' },
|
|
27
|
+
],
|
|
28
|
+
requiresDecision: true,
|
|
29
|
+
priority: 'high',
|
|
39
30
|
};
|
|
31
|
+
return { suggestion };
|
|
40
32
|
}
|
|
41
33
|
//# sourceMappingURL=pr-review.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pr-review.js","sourceRoot":"","sources":["../../../src/cli/guards/pr-review.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAgB,EAAE,IAAY;IAChE,MAAM,OAAO,GAAI,KAAK,CAAC,UAAU,EAAE,OAAkB,IAAI,EAAE,CAAC;IAC5D,MAAM,QAAQ,GAAI,KAAK,CAAC,aAAa,EAAE,MAAiB,IAAK,KAAK,CAAC,aAAa,EAAE,MAAiB,IAAI,EAAE,CAAC;IAE1G,wCAAwC;IACxC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;QAAE,OAAO,EAAE,CAAC;IAEjD,qDAAqD;IACrD,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,CAAC;IAEjF,+BAA+B;IAC/B,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;IAC9E,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IAEhD,
|
|
1
|
+
{"version":3,"file":"pr-review.js","sourceRoot":"","sources":["../../../src/cli/guards/pr-review.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,KAAgB,EAAE,IAAY;IAChE,MAAM,OAAO,GAAI,KAAK,CAAC,UAAU,EAAE,OAAkB,IAAI,EAAE,CAAC;IAC5D,MAAM,QAAQ,GAAI,KAAK,CAAC,aAAa,EAAE,MAAiB,IAAK,KAAK,CAAC,aAAa,EAAE,MAAiB,IAAI,EAAE,CAAC;IAE1G,wCAAwC;IACxC,IAAI,CAAC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;QAAE,OAAO,EAAE,CAAC;IAEjD,qDAAqD;IACrD,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,aAAa,CAAC,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,QAAQ,CAAC;QAAE,OAAO,EAAE,CAAC;IAEjF,+BAA+B;IAC/B,MAAM,QAAQ,GAAG,QAAQ,CAAC,KAAK,CAAC,4CAA4C,CAAC,CAAC;IAC9E,MAAM,KAAK,GAAG,QAAQ,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC;IAEhD,MAAM,UAAU,GAAe;QAC7B,EAAE,EAAE,WAAW;QACf,KAAK,EAAE,WAAW;QAClB,OAAO,EAAE,oCAAoC,KAAK,8HAA8H;QAChL,OAAO,EAAE;YACP,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,aAAa,EAAE,WAAW,EAAE,+CAA+C,EAAE;YAClG,EAAE,EAAE,EAAE,WAAW,EAAE,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,2CAA2C,EAAE;YACxG,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,8CAA8C,EAAE;YAC1F,EAAE,EAAE,EAAE,QAAQ,EAAE,KAAK,EAAE,eAAe,EAAE,WAAW,EAAE,gDAAgD,EAAE;YACvG,EAAE,EAAE,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,oCAAoC,EAAE;SAC7F;QACD,gBAAgB,EAAE,IAAI;QACtB,QAAQ,EAAE,MAAM;KACjB,CAAC;IAEF,OAAO,EAAE,UAAU,EAAE,CAAC;AACxB,CAAC"}
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { HookInput, GuardResult } from '../../core/index.js';
|
|
2
|
+
/**
|
|
3
|
+
* Review-stale guard: fires on Stop.
|
|
4
|
+
* Warns if any scored sprints lack review markdown files.
|
|
5
|
+
* Non-blocking suggestion — next-action handles the hard block.
|
|
6
|
+
*/
|
|
7
|
+
export declare function reviewStaleGuard(_input: HookInput, cwd: string): Promise<GuardResult>;
|
|
8
|
+
//# sourceMappingURL=review-stale.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"review-stale.d.ts","sourceRoot":"","sources":["../../../src/cli/guards/review-stale.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,SAAS,EAAE,WAAW,EAAc,MAAM,qBAAqB,CAAC;AAG9E;;;;GAIG;AACH,wBAAsB,gBAAgB,CAAC,MAAM,EAAE,SAAS,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAAC,WAAW,CAAC,CA6C3F"}
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
import { existsSync, readdirSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { loadConfig } from '../../core/index.js';
|
|
4
|
+
/**
|
|
5
|
+
* Review-stale guard: fires on Stop.
|
|
6
|
+
* Warns if any scored sprints lack review markdown files.
|
|
7
|
+
* Non-blocking suggestion — next-action handles the hard block.
|
|
8
|
+
*/
|
|
9
|
+
export async function reviewStaleGuard(_input, cwd) {
|
|
10
|
+
const config = loadConfig(cwd);
|
|
11
|
+
const retrosDir = join(cwd, config.scorecardDir);
|
|
12
|
+
if (!existsSync(retrosDir))
|
|
13
|
+
return {};
|
|
14
|
+
// Find scorecard files and check for matching reviews
|
|
15
|
+
const missingReviews = [];
|
|
16
|
+
try {
|
|
17
|
+
const files = readdirSync(retrosDir);
|
|
18
|
+
const scorecardPattern = /^sprint-(\d+)\.json$/;
|
|
19
|
+
for (const file of files) {
|
|
20
|
+
const match = file.match(scorecardPattern);
|
|
21
|
+
if (!match)
|
|
22
|
+
continue;
|
|
23
|
+
const sprintNum = parseInt(match[1], 10);
|
|
24
|
+
const reviewPath = join(retrosDir, `sprint-${sprintNum}-review.md`);
|
|
25
|
+
if (!existsSync(reviewPath)) {
|
|
26
|
+
missingReviews.push(sprintNum);
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
catch {
|
|
31
|
+
return {}; // Can't read retros dir — skip
|
|
32
|
+
}
|
|
33
|
+
if (missingReviews.length === 0)
|
|
34
|
+
return {};
|
|
35
|
+
// Sort for consistent output
|
|
36
|
+
missingReviews.sort((a, b) => a - b);
|
|
37
|
+
const suggestion = {
|
|
38
|
+
id: 'review-stale',
|
|
39
|
+
title: 'Missing Reviews',
|
|
40
|
+
context: `${missingReviews.length} sprint(s) have scorecards but no review: ${missingReviews.map(n => `S${n}`).join(', ')}`,
|
|
41
|
+
options: missingReviews.map(n => ({
|
|
42
|
+
id: `review-${n}`,
|
|
43
|
+
label: `Generate S${n} review`,
|
|
44
|
+
command: `slope review docs/retros/sprint-${n}.json`,
|
|
45
|
+
})),
|
|
46
|
+
requiresDecision: false,
|
|
47
|
+
priority: 'normal',
|
|
48
|
+
};
|
|
49
|
+
return { suggestion };
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=review-stale.js.map
|