@slope-dev/slope 1.57.2 → 1.58.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (209) hide show
  1. package/dist/cli/atomic-write.d.ts.map +1 -1
  2. package/dist/cli/atomic-write.js +12 -3
  3. package/dist/cli/atomic-write.js.map +1 -1
  4. package/dist/cli/commands/auto-card.d.ts.map +1 -1
  5. package/dist/cli/commands/auto-card.js +27 -7
  6. package/dist/cli/commands/auto-card.js.map +1 -1
  7. package/dist/cli/commands/commit-ready.d.ts.map +1 -1
  8. package/dist/cli/commands/commit-ready.js +19 -33
  9. package/dist/cli/commands/commit-ready.js.map +1 -1
  10. package/dist/cli/commands/enrich.js +4 -4
  11. package/dist/cli/commands/enrich.js.map +1 -1
  12. package/dist/cli/commands/guard.d.ts.map +1 -1
  13. package/dist/cli/commands/guard.js +2 -1
  14. package/dist/cli/commands/guard.js.map +1 -1
  15. package/dist/cli/commands/issue.d.ts +2 -0
  16. package/dist/cli/commands/issue.d.ts.map +1 -0
  17. package/dist/cli/commands/issue.js +548 -0
  18. package/dist/cli/commands/issue.js.map +1 -0
  19. package/dist/cli/commands/map.d.ts.map +1 -1
  20. package/dist/cli/commands/map.js +127 -19
  21. package/dist/cli/commands/map.js.map +1 -1
  22. package/dist/cli/commands/pr.d.ts +1 -1
  23. package/dist/cli/commands/pr.d.ts.map +1 -1
  24. package/dist/cli/commands/pr.js +19 -7
  25. package/dist/cli/commands/pr.js.map +1 -1
  26. package/dist/cli/commands/resume.d.ts +2 -0
  27. package/dist/cli/commands/resume.d.ts.map +1 -0
  28. package/dist/cli/commands/resume.js +5 -0
  29. package/dist/cli/commands/resume.js.map +1 -0
  30. package/dist/cli/commands/retro.d.ts +2 -1
  31. package/dist/cli/commands/retro.d.ts.map +1 -1
  32. package/dist/cli/commands/retro.js +243 -4
  33. package/dist/cli/commands/retro.js.map +1 -1
  34. package/dist/cli/commands/review.d.ts +1 -1
  35. package/dist/cli/commands/review.d.ts.map +1 -1
  36. package/dist/cli/commands/review.js +13 -5
  37. package/dist/cli/commands/review.js.map +1 -1
  38. package/dist/cli/commands/session.d.ts.map +1 -1
  39. package/dist/cli/commands/session.js +17 -0
  40. package/dist/cli/commands/session.js.map +1 -1
  41. package/dist/cli/commands/sprint.d.ts.map +1 -1
  42. package/dist/cli/commands/sprint.js +302 -59
  43. package/dist/cli/commands/sprint.js.map +1 -1
  44. package/dist/cli/commands/status.d.ts.map +1 -1
  45. package/dist/cli/commands/status.js +18 -9
  46. package/dist/cli/commands/status.js.map +1 -1
  47. package/dist/cli/commands/store.d.ts.map +1 -1
  48. package/dist/cli/commands/store.js +10 -0
  49. package/dist/cli/commands/store.js.map +1 -1
  50. package/dist/cli/commands/validate.d.ts.map +1 -1
  51. package/dist/cli/commands/validate.js +32 -3
  52. package/dist/cli/commands/validate.js.map +1 -1
  53. package/dist/cli/commands/version.d.ts.map +1 -1
  54. package/dist/cli/commands/version.js +15 -8
  55. package/dist/cli/commands/version.js.map +1 -1
  56. package/dist/cli/commands/worktree.d.ts.map +1 -1
  57. package/dist/cli/commands/worktree.js +9 -7
  58. package/dist/cli/commands/worktree.js.map +1 -1
  59. package/dist/cli/guards/branch-before-commit.d.ts.map +1 -1
  60. package/dist/cli/guards/branch-before-commit.js +2 -1
  61. package/dist/cli/guards/branch-before-commit.js.map +1 -1
  62. package/dist/cli/guards/claim-required.d.ts.map +1 -1
  63. package/dist/cli/guards/claim-required.js +26 -15
  64. package/dist/cli/guards/claim-required.js.map +1 -1
  65. package/dist/cli/guards/commit-nudge.d.ts.map +1 -1
  66. package/dist/cli/guards/commit-nudge.js +6 -5
  67. package/dist/cli/guards/commit-nudge.js.map +1 -1
  68. package/dist/cli/guards/compaction.d.ts.map +1 -1
  69. package/dist/cli/guards/compaction.js +6 -5
  70. package/dist/cli/guards/compaction.js.map +1 -1
  71. package/dist/cli/guards/docs.js +3 -3
  72. package/dist/cli/guards/docs.js.map +1 -1
  73. package/dist/cli/guards/explore.d.ts.map +1 -1
  74. package/dist/cli/guards/explore.js +6 -2
  75. package/dist/cli/guards/explore.js.map +1 -1
  76. package/dist/cli/guards/git-utils.js +3 -3
  77. package/dist/cli/guards/git-utils.js.map +1 -1
  78. package/dist/cli/guards/hazard.d.ts.map +1 -1
  79. package/dist/cli/guards/hazard.js +7 -2
  80. package/dist/cli/guards/hazard.js.map +1 -1
  81. package/dist/cli/guards/post-hole-enforcement.d.ts.map +1 -1
  82. package/dist/cli/guards/post-hole-enforcement.js +22 -2
  83. package/dist/cli/guards/post-hole-enforcement.js.map +1 -1
  84. package/dist/cli/guards/post-push.d.ts.map +1 -1
  85. package/dist/cli/guards/post-push.js +15 -10
  86. package/dist/cli/guards/post-push.js.map +1 -1
  87. package/dist/cli/guards/pr-review.d.ts.map +1 -1
  88. package/dist/cli/guards/pr-review.js +1 -0
  89. package/dist/cli/guards/pr-review.js.map +1 -1
  90. package/dist/cli/guards/push-nudge.d.ts.map +1 -1
  91. package/dist/cli/guards/push-nudge.js +4 -3
  92. package/dist/cli/guards/push-nudge.js.map +1 -1
  93. package/dist/cli/guards/scope-drift.js +2 -0
  94. package/dist/cli/guards/scope-drift.js.map +1 -1
  95. package/dist/cli/guards/sprint-completion.d.ts.map +1 -1
  96. package/dist/cli/guards/sprint-completion.js +222 -21
  97. package/dist/cli/guards/sprint-completion.js.map +1 -1
  98. package/dist/cli/guards/stop-check.d.ts.map +1 -1
  99. package/dist/cli/guards/stop-check.js +14 -12
  100. package/dist/cli/guards/stop-check.js.map +1 -1
  101. package/dist/cli/guards/version-check.d.ts.map +1 -1
  102. package/dist/cli/guards/version-check.js +3 -1
  103. package/dist/cli/guards/version-check.js.map +1 -1
  104. package/dist/cli/guards/workflow-step-gate.d.ts.map +1 -1
  105. package/dist/cli/guards/workflow-step-gate.js +14 -3
  106. package/dist/cli/guards/workflow-step-gate.js.map +1 -1
  107. package/dist/cli/guards/worktree-check.d.ts.map +1 -1
  108. package/dist/cli/guards/worktree-check.js +138 -8
  109. package/dist/cli/guards/worktree-check.js.map +1 -1
  110. package/dist/cli/guards/worktree-merge.d.ts.map +1 -1
  111. package/dist/cli/guards/worktree-merge.js +3 -2
  112. package/dist/cli/guards/worktree-merge.js.map +1 -1
  113. package/dist/cli/guards/worktree-reuse.d.ts.map +1 -1
  114. package/dist/cli/guards/worktree-reuse.js +41 -12
  115. package/dist/cli/guards/worktree-reuse.js.map +1 -1
  116. package/dist/cli/guards/worktree-self-remove.d.ts.map +1 -1
  117. package/dist/cli/guards/worktree-self-remove.js +3 -2
  118. package/dist/cli/guards/worktree-self-remove.js.map +1 -1
  119. package/dist/cli/index.js +24 -1
  120. package/dist/cli/index.js.map +1 -1
  121. package/dist/cli/loop/slope-executor.d.ts.map +1 -1
  122. package/dist/cli/loop/slope-executor.js +4 -2
  123. package/dist/cli/loop/slope-executor.js.map +1 -1
  124. package/dist/cli/pr-closeout.d.ts +28 -0
  125. package/dist/cli/pr-closeout.d.ts.map +1 -1
  126. package/dist/cli/pr-closeout.js +297 -8
  127. package/dist/cli/pr-closeout.js.map +1 -1
  128. package/dist/cli/pr-review-state.d.ts +15 -0
  129. package/dist/cli/pr-review-state.d.ts.map +1 -1
  130. package/dist/cli/pr-review-state.js +68 -0
  131. package/dist/cli/pr-review-state.js.map +1 -1
  132. package/dist/cli/registry.d.ts.map +1 -1
  133. package/dist/cli/registry.js +52 -0
  134. package/dist/cli/registry.js.map +1 -1
  135. package/dist/cli/source-runtime.d.ts +9 -0
  136. package/dist/cli/source-runtime.d.ts.map +1 -0
  137. package/dist/cli/source-runtime.js +50 -0
  138. package/dist/cli/source-runtime.js.map +1 -0
  139. package/dist/cli/sprint-resume.d.ts +55 -0
  140. package/dist/cli/sprint-resume.d.ts.map +1 -0
  141. package/dist/cli/sprint-resume.js +213 -0
  142. package/dist/cli/sprint-resume.js.map +1 -0
  143. package/dist/cli/template-generator.d.ts.map +1 -1
  144. package/dist/cli/template-generator.js +10 -2
  145. package/dist/cli/template-generator.js.map +1 -1
  146. package/dist/cli/workflow-resync.d.ts +37 -0
  147. package/dist/cli/workflow-resync.d.ts.map +1 -0
  148. package/dist/cli/workflow-resync.js +273 -0
  149. package/dist/cli/workflow-resync.js.map +1 -0
  150. package/dist/core/analyzers/walk.d.ts.map +1 -1
  151. package/dist/core/analyzers/walk.js +4 -1
  152. package/dist/core/analyzers/walk.js.map +1 -1
  153. package/dist/core/briefing.d.ts.map +1 -1
  154. package/dist/core/briefing.js +17 -5
  155. package/dist/core/briefing.js.map +1 -1
  156. package/dist/core/flows.d.ts.map +1 -1
  157. package/dist/core/flows.js +2 -2
  158. package/dist/core/flows.js.map +1 -1
  159. package/dist/core/formatter.d.ts.map +1 -1
  160. package/dist/core/formatter.js +3 -48
  161. package/dist/core/formatter.js.map +1 -1
  162. package/dist/core/harness.d.ts +1 -1
  163. package/dist/core/harness.d.ts.map +1 -1
  164. package/dist/core/harness.js +7 -5
  165. package/dist/core/harness.js.map +1 -1
  166. package/dist/core/imports.d.ts.map +1 -1
  167. package/dist/core/imports.js +7 -4
  168. package/dist/core/imports.js.map +1 -1
  169. package/dist/core/index.d.ts +5 -1
  170. package/dist/core/index.d.ts.map +1 -1
  171. package/dist/core/index.js +5 -1
  172. package/dist/core/index.js.map +1 -1
  173. package/dist/core/interview-engine.d.ts.map +1 -1
  174. package/dist/core/interview-engine.js +3 -1
  175. package/dist/core/interview-engine.js.map +1 -1
  176. package/dist/core/issue-scout.d.ts +70 -0
  177. package/dist/core/issue-scout.d.ts.map +1 -0
  178. package/dist/core/issue-scout.js +481 -0
  179. package/dist/core/issue-scout.js.map +1 -0
  180. package/dist/core/loader.d.ts +5 -1
  181. package/dist/core/loader.d.ts.map +1 -1
  182. package/dist/core/loader.js +102 -4
  183. package/dist/core/loader.js.map +1 -1
  184. package/dist/core/memory-types.d.ts +1 -1
  185. package/dist/core/memory-types.d.ts.map +1 -1
  186. package/dist/core/memory-validation.js +1 -1
  187. package/dist/core/memory-validation.js.map +1 -1
  188. package/dist/core/memory.d.ts.map +1 -1
  189. package/dist/core/memory.js +5 -0
  190. package/dist/core/memory.js.map +1 -1
  191. package/dist/core/prep.d.ts.map +1 -1
  192. package/dist/core/prep.js +7 -4
  193. package/dist/core/prep.js.map +1 -1
  194. package/dist/core/process.d.ts +3 -0
  195. package/dist/core/process.d.ts.map +1 -0
  196. package/dist/core/process.js +3 -0
  197. package/dist/core/process.js.map +1 -0
  198. package/dist/core/retro.d.ts +49 -0
  199. package/dist/core/retro.d.ts.map +1 -0
  200. package/dist/core/retro.js +112 -0
  201. package/dist/core/retro.js.map +1 -0
  202. package/dist/core/vision.js +5 -5
  203. package/dist/core/vision.js.map +1 -1
  204. package/dist/mcp/index.d.ts.map +1 -1
  205. package/dist/mcp/index.js +74 -33
  206. package/dist/mcp/index.js.map +1 -1
  207. package/package.json +3 -3
  208. package/templates/codex/plugins/slope/.codex-plugin/plugin.json +4 -3
  209. package/templates/codex/plugins/slope/skills/slope-retro/SKILL.md +33 -0
@@ -1,5 +1,5 @@
1
1
  import { loadSprintState, saveSprintState, createSprintState, mutateSprintState, updateGate, updateSprintPhase, clearSprintState, isSprintComplete, pendingGates, isSprintPhase, SPRINT_PHASES, } from '../sprint-state.js';
2
- import { WorkflowEngine, loadWorkflow, resolveVariables, validateWorkflow, loadConfig, loadScorecards, parseRoadmap, castRoadmapStructure, formatSprintLabel, formatSprintNumber, parseSprintNumber } from '../../core/index.js';
2
+ import { WorkflowEngine, loadWorkflow, resolveVariables, validateWorkflow, loadConfig, parseRoadmap, castRoadmapStructure, formatSprintLabel, formatSprintNumber, parseSprintNumber } from '../../core/index.js';
3
3
  import { createHash } from 'node:crypto';
4
4
  /** Get workflow definition from execution snapshot (preferred) or disk (fallback for old executions) */
5
5
  function getDefinition(exec, cwd) {
@@ -20,9 +20,12 @@ function getDefinition(exec, cwd) {
20
20
  return { def: loadWorkflow(exec.workflow_name, cwd), drifted: false };
21
21
  }
22
22
  import { existsSync, readFileSync, readdirSync } from 'node:fs';
23
- import { join, dirname, basename } from 'node:path';
23
+ import { join, dirname, basename, isAbsolute } from 'node:path';
24
24
  import { createStore } from '../../store/index.js';
25
+ import { formatCliError } from '../error-reporter.js';
26
+ import { buildSprintResumePointer, planPortableSprintResume, writeSprintResumePointer, } from '../sprint-resume.js';
25
27
  import { blockingRoadmapIssuesForSprint, collectSiblingWorktreeReality, findWorktreeOverlaps, formatWorktreeRealitySection, loadRoadmapReality, parseTouchedPaths, } from '../pre-sprint-reality.js';
28
+ import { findStaleWorkflowExecutions, reconcileWorkflowExecutions } from '../workflow-resync.js';
26
29
  /**
27
30
  * Check completion_conditions for a step before allowing completion/skip.
28
31
  * Returns null if all conditions met, or an error message string.
@@ -385,6 +388,59 @@ function getStore(cwd) {
385
388
  const config = loadConfig(cwd);
386
389
  return createStore({ storePath: config.store_path ?? '.slope/slope.db', cwd });
387
390
  }
391
+ function roadmapTicketKeysForSprint(cwd, sprintId) {
392
+ if (!sprintId)
393
+ return [];
394
+ const sprintNumber = parseSprintNumber(sprintId);
395
+ if (sprintNumber === null)
396
+ return [];
397
+ const config = loadConfig(cwd);
398
+ if (!config.roadmapPath)
399
+ return [];
400
+ const roadmapPath = join(cwd, config.roadmapPath);
401
+ if (!existsSync(roadmapPath))
402
+ return [];
403
+ try {
404
+ const raw = JSON.parse(readFileSync(roadmapPath, 'utf8'));
405
+ const parsed = parseRoadmap(raw);
406
+ const roadmap = parsed.roadmap ?? castRoadmapStructure(raw);
407
+ const sprint = roadmap?.sprints.find(s => s.id === sprintNumber);
408
+ return sprint?.tickets.map(t => t.key).filter(Boolean) ?? [];
409
+ }
410
+ catch {
411
+ return [];
412
+ }
413
+ }
414
+ function applyWorkflowVariableDefaults(def, vars, cwd, sprintId) {
415
+ if (sprintId && !('sprint_id' in vars)) {
416
+ vars.sprint_id = sprintId;
417
+ }
418
+ const ticketsSpec = def.variables?.tickets;
419
+ if (ticketsSpec?.required && !('tickets' in vars)) {
420
+ const tickets = roadmapTicketKeysForSprint(cwd, sprintId);
421
+ if (tickets.length > 0) {
422
+ vars.tickets = tickets.join(',');
423
+ }
424
+ }
425
+ }
426
+ function positionalSprintArg(args) {
427
+ for (let i = 0; i < args.length; i++) {
428
+ const arg = args[i];
429
+ if (arg === '--var') {
430
+ i++;
431
+ continue;
432
+ }
433
+ if (!arg.startsWith('--'))
434
+ return arg;
435
+ }
436
+ return undefined;
437
+ }
438
+ function sprintIdFromRunArgs(args) {
439
+ const sprintFlag = args.find(a => a.startsWith('--sprint='));
440
+ if (sprintFlag)
441
+ return sprintFlag.slice('--sprint='.length);
442
+ return positionalSprintArg(args);
443
+ }
388
444
  const SPRINT_PHASE_ORDER = {
389
445
  planning: 0,
390
446
  reviewing: 1,
@@ -437,7 +493,7 @@ function syncSprintStateWithWorkflow(cwd, sprintId, workflowPhase) {
437
493
  });
438
494
  }
439
495
  async function runWorkflowCommand(args, cwd) {
440
- const sprintArg = args.find(a => a.startsWith('--sprint=') || !a.startsWith('--'));
496
+ const explicitSprintId = sprintIdFromRunArgs(args);
441
497
  const workflowArg = args.find(a => a.startsWith('--workflow='));
442
498
  const varArgs = [];
443
499
  for (let i = 0; i < args.length; i++) {
@@ -453,12 +509,11 @@ async function runWorkflowCommand(args, cwd) {
453
509
  console.error('Usage: slope sprint run <sprint_id> --workflow=<name> [--var key=value ...]');
454
510
  process.exit(1);
455
511
  }
456
- const sprintId = sprintArg?.startsWith('--') ? undefined : sprintArg;
457
512
  const workflowName = workflowArg.slice('--workflow='.length);
458
513
  // Parse variables
459
514
  const vars = {};
460
- if (sprintId)
461
- vars.sprint_id = sprintId;
515
+ if (explicitSprintId)
516
+ vars.sprint_id = explicitSprintId;
462
517
  for (const v of varArgs) {
463
518
  const kv = v.slice('--var='.length);
464
519
  const eq = kv.indexOf('=');
@@ -466,6 +521,7 @@ async function runWorkflowCommand(args, cwd) {
466
521
  vars[kv.slice(0, eq)] = kv.slice(eq + 1);
467
522
  }
468
523
  }
524
+ const sprintId = explicitSprintId ?? vars.sprint_id;
469
525
  // Load and validate workflow
470
526
  const def = loadWorkflow(workflowName, cwd);
471
527
  const validation = validateWorkflow(def);
@@ -476,6 +532,7 @@ async function runWorkflowCommand(args, cwd) {
476
532
  }
477
533
  process.exit(1);
478
534
  }
535
+ applyWorkflowVariableDefaults(def, vars, cwd, sprintId);
479
536
  // Resolve variables
480
537
  const resolved = resolveVariables(def, vars);
481
538
  // Start execution
@@ -513,8 +570,9 @@ async function runWorkflowCommand(args, cwd) {
513
570
  }
514
571
  async function workflowStatusCommand(args, cwd) {
515
572
  const sprintArg = args.find(a => !a.startsWith('--'));
516
- const store = getStore(cwd);
573
+ let store = null;
517
574
  try {
575
+ store = getStore(cwd);
518
576
  if (sprintArg) {
519
577
  const exec = await store.getExecutionBySprint(sprintArg);
520
578
  if (!exec) {
@@ -536,60 +594,45 @@ async function workflowStatusCommand(args, cwd) {
536
594
  }
537
595
  }
538
596
  }
539
- finally {
540
- store.close();
541
- }
542
- }
543
- function completedRoadmapSprintIds(cwd, config) {
544
- if (!config.roadmapPath)
545
- return new Set();
546
- const roadmapPath = join(cwd, config.roadmapPath);
547
- if (!existsSync(roadmapPath))
548
- return new Set();
549
- try {
550
- const raw = JSON.parse(readFileSync(roadmapPath, 'utf8'));
551
- const parsed = parseRoadmap(raw);
552
- const roadmap = parsed.roadmap ?? castRoadmapStructure(raw);
553
- if (!roadmap)
554
- return new Set();
555
- return new Set(roadmap.sprints
556
- .filter(sprint => {
557
- const status = sprint.status;
558
- return status === 'complete' || status === 'superseded';
559
- })
560
- .map(sprint => sprint.id));
561
- }
562
- catch {
563
- return new Set();
564
- }
565
- }
566
- async function findStaleWorkflowExecutions(cwd, store) {
567
- const config = loadConfig(cwd);
568
- const scorecardSprintIds = new Set(loadScorecards(config, cwd).map(card => card.sprint_number));
569
- const roadmapDoneIds = completedRoadmapSprintIds(cwd, config);
570
- const running = await store.listExecutions({ status: 'running' });
571
- const stale = [];
572
- for (const exec of running) {
573
- const sprint = sprintNumberFromId(exec.sprint_id);
574
- if (sprint === null)
575
- continue;
576
- const reasons = [];
577
- if (scorecardSprintIds.has(sprint))
578
- reasons.push('scorecard exists');
579
- if (roadmapDoneIds.has(sprint))
580
- reasons.push('roadmap complete/superseded');
581
- if (reasons.length > 0) {
582
- stale.push({ exec, reason: reasons.join(', ') });
597
+ catch (err) {
598
+ for (const line of formatCliError(err, cwd)) {
599
+ console.error(line);
583
600
  }
601
+ process.exitCode = 1;
602
+ }
603
+ finally {
604
+ store?.close();
584
605
  }
585
- return stale;
586
606
  }
587
607
  async function workflowCleanupCommand(args, cwd) {
588
608
  const action = args[0];
589
609
  const dryRun = args.includes('--dry-run');
590
610
  const staleOnly = args.includes('--stale');
611
+ const resync = action === 'resync';
612
+ if (resync) {
613
+ const store = getStore(cwd);
614
+ try {
615
+ const result = await reconcileWorkflowExecutions(cwd, store);
616
+ for (const { exec, reason } of result.paused) {
617
+ console.log(`Paused ${exec.sprint_id ?? exec.id} (${exec.workflow_name}) at ${exec.current_phase}/${exec.current_step} — ${reason}`);
618
+ }
619
+ for (const item of result.fastForwarded) {
620
+ console.log(`Fast-forwarded ${item.exec.sprint_id ?? item.exec.id} (${item.exec.workflow_name}) to ${item.phase}/${item.step} — ${item.reason}`);
621
+ }
622
+ if (result.paused.length === 0 && result.fastForwarded.length === 0) {
623
+ console.log('Workflow state already matches git/roadmap reality.');
624
+ }
625
+ else {
626
+ console.log(`\nResynced workflow state: ${result.paused.length} paused, ${result.fastForwarded.length} fast-forwarded.`);
627
+ }
628
+ }
629
+ finally {
630
+ store.close();
631
+ }
632
+ return;
633
+ }
591
634
  if (action !== 'cleanup' || !staleOnly) {
592
- console.error('Usage: slope sprint workflow cleanup --stale [--dry-run]');
635
+ console.error('Usage: slope sprint workflow cleanup --stale [--dry-run]\n slope sprint workflow resync');
593
636
  process.exit(1);
594
637
  }
595
638
  const store = getStore(cwd);
@@ -629,7 +672,184 @@ function printExecution(exec) {
629
672
  console.log(` Started: ${exec.started_at}`);
630
673
  console.log('');
631
674
  }
675
+ function shouldUsePortableResume(args) {
676
+ return args.includes('--portable')
677
+ || args.includes('--write-pointer')
678
+ || args.includes('--dry-run')
679
+ || args.some(a => a.startsWith('--from=') || a.startsWith('--sprint=') || a.startsWith('--phase=') || a.startsWith('--output='));
680
+ }
681
+ function parsePortableResumeFlags(args) {
682
+ const flags = {
683
+ portable: args.includes('--portable'),
684
+ writePointer: args.includes('--write-pointer'),
685
+ force: args.includes('--force'),
686
+ dryRun: args.includes('--dry-run'),
687
+ };
688
+ for (const arg of args) {
689
+ if (arg.startsWith('--sprint=')) {
690
+ const raw = arg.slice('--sprint='.length);
691
+ const sprint = parseSprintNumber(raw);
692
+ if (sprint)
693
+ flags.sprint = sprint;
694
+ else
695
+ flags.invalidSprint = raw;
696
+ }
697
+ else if (arg.startsWith('--phase=')) {
698
+ const phase = arg.slice('--phase='.length);
699
+ if (isSprintPhase(phase))
700
+ flags.phase = phase;
701
+ else
702
+ flags.invalidPhase = phase;
703
+ }
704
+ else if (arg.startsWith('--from=')) {
705
+ flags.from = arg.slice('--from='.length);
706
+ }
707
+ else if (arg.startsWith('--output=')) {
708
+ flags.output = arg.slice('--output='.length);
709
+ }
710
+ }
711
+ return flags;
712
+ }
713
+ async function portableResumeCommand(args, cwd) {
714
+ const flags = parsePortableResumeFlags(args);
715
+ const config = loadConfig(cwd);
716
+ if (flags.invalidSprint) {
717
+ console.error(`Error: invalid sprint "${flags.invalidSprint}". Use --sprint=N, e.g. --sprint=177.`);
718
+ process.exit(1);
719
+ return;
720
+ }
721
+ if (flags.invalidPhase) {
722
+ console.error(`Error: invalid phase "${flags.invalidPhase}". Valid phases: ${SPRINT_PHASES.join(', ')}`);
723
+ process.exit(1);
724
+ return;
725
+ }
726
+ if (flags.writePointer) {
727
+ const current = loadSprintState(cwd);
728
+ const sprint = flags.sprint ?? current?.sprint;
729
+ if (!sprint) {
730
+ console.error('Usage: slope sprint resume --write-pointer --sprint=N [--phase=<phase>] [--output=path]');
731
+ process.exit(1);
732
+ return;
733
+ }
734
+ const phase = flags.phase ?? current?.phase ?? 'implementing';
735
+ const resumeClaims = await collectResumeClaimPointers(cwd, sprint);
736
+ const pointer = buildSprintResumePointer(cwd, config, { sprint, phase, resumeClaims });
737
+ const outputPath = flags.output ? (isAbsolute(flags.output) ? flags.output : join(cwd, flags.output)) : undefined;
738
+ const written = writeSprintResumePointer(cwd, pointer, outputPath);
739
+ console.log(`Sprint resume pointer written: ${written}`);
740
+ console.log(` Sprint: ${formatSprintLabel(sprint)} (${phase})`);
741
+ console.log(` Resume claims: ${resumeClaims.length}`);
742
+ console.log(' Local runtime state excluded: slope.db, session locks, guard metrics, baselines');
743
+ return;
744
+ }
745
+ const plan = planPortableSprintResume(cwd, config, {
746
+ sprint: flags.sprint,
747
+ phase: flags.phase,
748
+ from: flags.from,
749
+ force: flags.force,
750
+ });
751
+ printPortableResumePlan(plan);
752
+ if (plan.unsafe.length > 0 && !flags.force) {
753
+ console.error('\nPortable resume refused. Rerun with --force after reviewing the unsafe condition(s).');
754
+ process.exit(1);
755
+ return;
756
+ }
757
+ const existing = loadSprintState(cwd);
758
+ if (existing && existing.sprint !== plan.sprint && !flags.force) {
759
+ console.error(`\nPortable resume refused. Local sprint-state is ${formatSprintLabel(existing.sprint)}, but resume target is ${formatSprintLabel(plan.sprint)}.`);
760
+ console.error('Run `slope sprint reset` or rerun with --force if replacing local state is intentional.');
761
+ process.exit(1);
762
+ return;
763
+ }
764
+ if (flags.dryRun) {
765
+ console.log('\nDry run: local sprint-state and claims were not changed.');
766
+ return;
767
+ }
768
+ saveSprintState(cwd, createSprintState(plan.sprint, plan.phase));
769
+ const restoredClaims = await restoreResumeClaims(cwd, plan);
770
+ console.log(`\nPortable sprint resume complete: ${formatSprintLabel(plan.sprint)} (${plan.phase}).`);
771
+ console.log(` Fresh local sprint-state written to .slope/sprint-state.json`);
772
+ console.log(` Resume claims restored: ${restoredClaims}`);
773
+ console.log(' Local DB/locks/metrics were not imported from another machine.');
774
+ }
775
+ function printPortableResumePlan(plan) {
776
+ console.log('\nPortable sprint resume plan');
777
+ console.log('='.repeat(32));
778
+ console.log(` Sprint: ${formatSprintLabel(plan.sprint)}`);
779
+ console.log(` Phase: ${plan.phase}`);
780
+ console.log(` Source: ${plan.source}${plan.pointerPath ? ` (${plan.pointerPath})` : ''}`);
781
+ if (plan.currentBranch)
782
+ console.log(` Branch: ${plan.currentBranch}`);
783
+ if (plan.headCommit)
784
+ console.log(` HEAD: ${plan.headCommit.slice(0, 12)}`);
785
+ const evidence = Object.entries(plan.evidence);
786
+ if (evidence.length > 0) {
787
+ console.log(' Evidence:');
788
+ for (const [label, value] of evidence) {
789
+ console.log(` - ${label}: ${value}`);
790
+ }
791
+ }
792
+ if (plan.resumeClaims.length > 0) {
793
+ console.log(' Resume claims:');
794
+ for (const claim of plan.resumeClaims) {
795
+ console.log(` - ${claim.id} (${claim.scope ?? 'ticket'}, ${claim.state})${claim.last_evidence ? ` via ${claim.last_evidence}` : ''}`);
796
+ }
797
+ }
798
+ if (plan.unsafe.length > 0) {
799
+ console.log(' Unsafe conditions:');
800
+ for (const item of plan.unsafe)
801
+ console.log(` - ${item}`);
802
+ }
803
+ }
804
+ async function collectResumeClaimPointers(cwd, sprint) {
805
+ const { resolveStore } = await import('../store.js');
806
+ const store = await resolveStore(cwd);
807
+ try {
808
+ const claims = await store.list(sprint);
809
+ return claims.map(claim => ({
810
+ id: claim.target,
811
+ state: 'in_progress',
812
+ scope: claim.scope,
813
+ last_evidence: claim.notes,
814
+ }));
815
+ }
816
+ finally {
817
+ store.close();
818
+ }
819
+ }
820
+ async function restoreResumeClaims(cwd, plan) {
821
+ const claims = plan.resumeClaims.filter(claim => claim.state !== 'done');
822
+ if (claims.length === 0)
823
+ return 0;
824
+ const { resolveStore } = await import('../store.js');
825
+ const store = await resolveStore(cwd);
826
+ const player = process.env.USER || 'unknown';
827
+ let restored = 0;
828
+ try {
829
+ const existing = await store.list(plan.sprint);
830
+ for (const claim of claims) {
831
+ if (existing.some(c => c.target === claim.id && c.player === player))
832
+ continue;
833
+ await store.claim({
834
+ sprint_number: plan.sprint,
835
+ player,
836
+ target: claim.id,
837
+ scope: claim.scope ?? 'ticket',
838
+ notes: claim.last_evidence ? `portable resume: ${claim.last_evidence}` : 'portable resume',
839
+ });
840
+ restored++;
841
+ }
842
+ }
843
+ finally {
844
+ store.close();
845
+ }
846
+ return restored;
847
+ }
632
848
  async function resumeCommand(args, cwd) {
849
+ if (shouldUsePortableResume(args)) {
850
+ await portableResumeCommand(args, cwd);
851
+ return;
852
+ }
633
853
  const sprintArg = args.find(a => !a.startsWith('--'));
634
854
  if (!sprintArg) {
635
855
  console.error('Usage: slope sprint resume <sprint_id>');
@@ -875,6 +1095,10 @@ async function validateSprintCommand(args, cwd) {
875
1095
  export async function sprintCommand(args) {
876
1096
  const cwd = process.cwd();
877
1097
  const sub = args[0];
1098
+ if (!sub || sub === '--help' || sub === '-h') {
1099
+ printSprintUsage();
1100
+ return;
1101
+ }
878
1102
  switch (sub) {
879
1103
  case 'start':
880
1104
  await startCommand(args.slice(1), cwd);
@@ -896,9 +1120,19 @@ export async function sprintCommand(args) {
896
1120
  case 'status':
897
1121
  await workflowStatusCommand(args.slice(1), cwd);
898
1122
  break;
899
- case 'reset':
1123
+ case 'reset': {
1124
+ const resetArgs = args.slice(1);
1125
+ if (resetArgs.includes('--help') || resetArgs.includes('-h')) {
1126
+ printSprintUsage();
1127
+ break;
1128
+ }
1129
+ if (resetArgs.length > 0) {
1130
+ printSprintUsage();
1131
+ process.exit(1);
1132
+ }
900
1133
  resetCommand(cwd);
901
1134
  break;
1135
+ }
902
1136
  case 'run':
903
1137
  await runWorkflowCommand(args.slice(1), cwd);
904
1138
  break;
@@ -921,7 +1155,13 @@ export async function sprintCommand(args) {
921
1155
  await validateSprintCommand(args.slice(1), cwd);
922
1156
  break;
923
1157
  default:
924
- console.log(`
1158
+ printSprintUsage();
1159
+ process.exit(1);
1160
+ break;
1161
+ }
1162
+ }
1163
+ function printSprintUsage() {
1164
+ console.log(`
925
1165
  slope sprint — Sprint lifecycle management
926
1166
 
927
1167
  Legacy commands:
@@ -932,6 +1172,10 @@ Legacy commands:
932
1172
  slope sprint phase <phase> Update current sprint phase
933
1173
  slope sprint gate <name> Mark a gate as complete
934
1174
  slope sprint status Show sprint state and gates
1175
+ slope sprint resume --portable [--from=path] [--force] [--dry-run]
1176
+ Reconstruct local sprint state from tracked artifacts
1177
+ slope sprint resume --write-pointer [--output=path]
1178
+ Write tracked resume pointer without syncing DB/locks
935
1179
  slope sprint reset Clear sprint state
936
1180
 
937
1181
  Workflow commands:
@@ -939,12 +1183,11 @@ Workflow commands:
939
1183
  slope sprint status [sprint_id] Show workflow execution progress
940
1184
  slope sprint resume <sprint_id> Resume a paused workflow execution
941
1185
  slope sprint pause <sprint_id> Pause a running workflow execution
1186
+ slope sprint context <sprint_id> Show current workflow step and remaining work
1187
+ slope sprint validate <sprint_id> Validate workflow, plan, scorecard, and tests
942
1188
  slope sprint workflow cleanup --stale [--dry-run] Pause stale completed/superseded executions
1189
+ slope sprint workflow resync Reconcile workflow executions with git/roadmap reality
943
1190
  slope sprint skip <id> --step=<s> --reason="..." Skip a blocking step
944
1191
  `);
945
- if (sub)
946
- process.exit(1);
947
- break;
948
- }
949
1192
  }
950
1193
  //# sourceMappingURL=sprint.js.map