@opengsd/gsd-core 1.6.0-rc.3 → 1.7.0-rc.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (105) hide show
  1. package/.claude-plugin/plugin.json +1 -1
  2. package/agents/gsd-advisor-researcher.md +2 -0
  3. package/agents/gsd-ai-researcher.md +2 -0
  4. package/agents/gsd-assumptions-analyzer.md +2 -0
  5. package/agents/gsd-doc-classifier.md +2 -0
  6. package/agents/gsd-doc-synthesizer.md +2 -0
  7. package/agents/gsd-domain-researcher.md +2 -0
  8. package/agents/gsd-eval-auditor.md +6 -9
  9. package/agents/gsd-phase-researcher.md +2 -0
  10. package/agents/gsd-project-researcher.md +2 -0
  11. package/agents/gsd-research-synthesizer.md +2 -0
  12. package/agents/gsd-ui-researcher.md +2 -0
  13. package/agents/gsd-verifier.md +1 -0
  14. package/bin/gsd-mcp-server.js +31 -0
  15. package/bin/install.js +512 -1150
  16. package/commands/gsd/review.md +6 -0
  17. package/gemini-extension.json +1 -1
  18. package/gsd-core/bin/gsd-tools.cjs +127 -2
  19. package/gsd-core/bin/lib/adapter-declarative.cjs +35 -0
  20. package/gsd-core/bin/lib/adapter-imperative.cjs +52 -0
  21. package/gsd-core/bin/lib/assumption-delta.cjs +231 -0
  22. package/gsd-core/bin/lib/capability-lifecycle.cjs +7 -7
  23. package/gsd-core/bin/lib/capability-loader.cjs +18 -0
  24. package/gsd-core/bin/lib/capability-lock.cjs +2 -2
  25. package/gsd-core/bin/lib/capability-registry.cjs +889 -82
  26. package/gsd-core/bin/lib/capability-source.cjs +4 -4
  27. package/gsd-core/bin/lib/capability-validator.cjs +198 -0
  28. package/gsd-core/bin/lib/cli-skew-check.cjs +44 -0
  29. package/gsd-core/bin/lib/command-aliases.cjs +18 -1
  30. package/gsd-core/bin/lib/config.cjs +27 -0
  31. package/gsd-core/bin/lib/decisions.cjs +27 -0
  32. package/gsd-core/bin/lib/embedding-adapter.cjs +27 -0
  33. package/gsd-core/bin/lib/eval-command-router.cjs +21 -0
  34. package/gsd-core/bin/lib/eval.cjs +60 -0
  35. package/gsd-core/bin/lib/external-descriptor-trust.cjs +70 -0
  36. package/gsd-core/bin/lib/frontmatter.cjs +132 -13
  37. package/gsd-core/bin/lib/hook-bus.cjs +81 -0
  38. package/gsd-core/bin/lib/host-integration.cjs +408 -0
  39. package/gsd-core/bin/lib/init.cjs +132 -27
  40. package/gsd-core/bin/lib/install-engine.cjs +755 -0
  41. package/gsd-core/bin/lib/install-profiles.cjs +35 -4
  42. package/gsd-core/bin/lib/installer-migrations.cjs +1 -1
  43. package/gsd-core/bin/lib/io.cjs +1 -0
  44. package/gsd-core/bin/lib/mcp-server.cjs +194 -0
  45. package/gsd-core/bin/lib/milestone.cjs +27 -30
  46. package/gsd-core/bin/lib/model-adapter.cjs +50 -0
  47. package/gsd-core/bin/lib/phase.cjs +57 -74
  48. package/gsd-core/bin/lib/plan-scan.cjs +2 -2
  49. package/gsd-core/bin/lib/planning-workspace.cjs +1 -1
  50. package/gsd-core/bin/lib/probe-core.cjs +91 -1
  51. package/gsd-core/bin/lib/review-reviewer-selection.cjs +129 -13
  52. package/gsd-core/bin/lib/roadmap-upgrade.cjs +3 -2
  53. package/gsd-core/bin/lib/roadmap.cjs +17 -3
  54. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +65 -9
  55. package/gsd-core/bin/lib/runtime-artifact-install-plan.cjs +54 -4
  56. package/gsd-core/bin/lib/runtime-artifact-layout.cjs +5 -2
  57. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +1 -1
  58. package/gsd-core/bin/lib/runtime-name-policy.cjs +160 -30
  59. package/gsd-core/bin/lib/shell-command-projection.cjs +47 -1
  60. package/gsd-core/bin/lib/stale-bake-guard.cjs +254 -0
  61. package/gsd-core/bin/lib/state-command-router.cjs +4 -0
  62. package/gsd-core/bin/lib/state-io.cjs +55 -0
  63. package/gsd-core/bin/lib/state-transition.cjs +1588 -0
  64. package/gsd-core/bin/lib/state.cjs +340 -689
  65. package/gsd-core/bin/lib/surface.cjs +4 -1
  66. package/gsd-core/bin/lib/uat-predicate.cjs +13 -6
  67. package/gsd-core/bin/lib/verification.cjs +67 -6
  68. package/gsd-core/bin/lib/verify.cjs +8 -1
  69. package/gsd-core/bin/lib/workstream.cjs +4 -4
  70. package/gsd-core/bin/shared/config-defaults.manifest.json +3 -0
  71. package/gsd-core/bin/shared/config-schema.manifest.json +11 -1
  72. package/gsd-core/references/honest-verifier.md +105 -0
  73. package/gsd-core/references/reviewer-instances.md +99 -0
  74. package/gsd-core/references/untrusted-input-boundary.md +13 -0
  75. package/gsd-core/workflows/autonomous.md +61 -54
  76. package/gsd-core/workflows/complete-milestone.md +27 -8
  77. package/gsd-core/workflows/execute-phase.md +1 -1
  78. package/gsd-core/workflows/manager.md +30 -20
  79. package/gsd-core/workflows/new-project.md +78 -12
  80. package/gsd-core/workflows/plan-phase.md +1 -1
  81. package/gsd-core/workflows/profile-user.md +6 -2
  82. package/gsd-core/workflows/progress.md +37 -4
  83. package/gsd-core/workflows/quick.md +3 -1
  84. package/gsd-core/workflows/review.md +26 -0
  85. package/gsd-core/workflows/ship.md +3 -1
  86. package/gsd-core/workflows/spec-phase.md +3 -1
  87. package/gsd-core/workflows/thread.md +4 -4
  88. package/gsd-core/workflows/transition.md +14 -12
  89. package/gsd-core/workflows/ui-review.md +2 -6
  90. package/gsd-core/workflows/verify-phase.md +11 -4
  91. package/gsd-core/workflows/verify-work.md +44 -0
  92. package/hooks/dist/gsd-graphify-update.sh +7 -1
  93. package/hooks/dist/gsd-read-injection-scanner.js +49 -25
  94. package/hooks/gsd-graphify-update.sh +7 -1
  95. package/hooks/gsd-read-injection-scanner.js +49 -25
  96. package/hooks/hooks.json +1 -1
  97. package/package.json +4 -4
  98. package/scripts/check-alias-drift.cjs +5 -0
  99. package/scripts/ci-test-scope.cjs +38 -9
  100. package/scripts/lint-allow-test-rule-refs.allowlist.json +0 -1
  101. package/scripts/lint-regression-test-names.allowlist.json +3 -0
  102. package/scripts/lint-test-file-count.allowlist.json +19 -5
  103. package/scripts/mutation-matrix.cjs +45 -3
  104. package/scripts/prompt-injection-scan.sh +13 -0
  105. package/scripts/lint-windows-test-portability.cjs +0 -178
@@ -43,6 +43,10 @@ const security_cjs_1 = require("./security.cjs");
43
43
  const runtime_homes_cjs_1 = require("./runtime-homes.cjs");
44
44
  // eslint-disable-next-line @typescript-eslint/no-require-imports -- frontmatter.cjs is an export= CommonJS module
45
45
  const frontmatterMod = require("./frontmatter.cjs");
46
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- verification.cjs is an export= CommonJS module
47
+ const verificationMod = require("./verification.cjs");
48
+ // eslint-disable-next-line @typescript-eslint/no-require-imports -- uat-predicate.cjs is an export= CommonJS module
49
+ const uatPredicateMod = require("./uat-predicate.cjs");
46
50
  // eslint-disable-next-line @typescript-eslint/no-require-imports -- agent-install-check.cjs is an export= CommonJS module
47
51
  const agentInstallCheck = require("./agent-install-check.cjs");
48
52
  const { checkAgentsInstalled } = agentInstallCheck;
@@ -61,6 +65,8 @@ const { pruneOrphanedWorktrees } = worktreeSafety;
61
65
  const { planningPaths, planningDir, planningRoot, findContextMdIn, } = planningWorkspace;
62
66
  const { determinePhaseStatus } = commandsMod;
63
67
  const { extractFrontmatter } = frontmatterMod;
68
+ const { readVerificationStatus } = verificationMod;
69
+ const { evaluateUatPassed } = uatPredicateMod;
64
70
  // Unused but imported for structural parity
65
71
  void stripShippedMilestones;
66
72
  // Accept all bold/colon variants of the Requirements header (#2769)
@@ -71,6 +77,45 @@ function listPhaseSummaryFiles(phaseDir) {
71
77
  function listPhasePlanFiles(phaseDir) {
72
78
  return scanPhasePlans(phaseDir)['planFiles'];
73
79
  }
80
+ function verificationNextCommand(status, phaseNumber, slashRuntime) {
81
+ if (status === 'gaps_found') {
82
+ return `${(0, runtime_slash_cjs_1.formatGsdSlash)('plan-phase', slashRuntime)} ${phaseNumber} --gaps`;
83
+ }
84
+ if (status === 'human_needed' || status === 'stale') {
85
+ return `${(0, runtime_slash_cjs_1.formatGsdSlash)('verify-work', slashRuntime)} ${phaseNumber}`;
86
+ }
87
+ if (status === 'missing' || status === 'unknown') {
88
+ return `${(0, runtime_slash_cjs_1.formatGsdSlash)('execute-phase', slashRuntime)} ${phaseNumber}`;
89
+ }
90
+ return '';
91
+ }
92
+ function projectCompletionStatus(implementationComplete, verificationPassed) {
93
+ if (implementationComplete && verificationPassed)
94
+ return 'complete';
95
+ if (implementationComplete)
96
+ return 'executed';
97
+ return 'incomplete';
98
+ }
99
+ function buildPhaseCompletionProjection(cwd, phaseNumber, phaseDir, planCount, summaryCount, slashRuntime) {
100
+ const implementationComplete = planCount > 0 && summaryCount >= planCount;
101
+ const phaseFullDir = phaseDir ? node_path_1.default.join(cwd, phaseDir) : '';
102
+ const verificationStatus = implementationComplete
103
+ ? readVerificationStatus(phaseFullDir)
104
+ : { status: 'not_required', next_action: '', next_command: '' };
105
+ const projectedVerificationStatus = verificationStatus.status;
106
+ const projectedVerificationAction = verificationStatus.next_action;
107
+ const verificationPassed = projectedVerificationStatus === 'passed';
108
+ const phaseComplete = implementationComplete && verificationPassed;
109
+ return {
110
+ implementation_complete: implementationComplete,
111
+ verification_status: projectedVerificationStatus,
112
+ verification_passed: verificationPassed,
113
+ phase_complete: phaseComplete,
114
+ completion_status: projectCompletionStatus(implementationComplete, verificationPassed),
115
+ verification_next_action: projectedVerificationAction,
116
+ verification_next_command: verificationNextCommand(projectedVerificationStatus, phaseNumber, slashRuntime),
117
+ };
118
+ }
74
119
  function getLatestCompletedMilestone(cwd) {
75
120
  const milestonesPath = node_path_1.default.join(planningRoot(cwd), 'MILESTONES.md');
76
121
  const content = (0, shell_command_projection_cjs_1.platformReadSync)(milestonesPath);
@@ -636,6 +681,7 @@ function cmdInitVerifyWork(cwd, phase, raw) {
636
681
  error('phase required for init verify-work');
637
682
  }
638
683
  const config = loadConfig(cwd);
684
+ const _slashRuntime = (0, runtime_slash_cjs_1.resolveRuntime)(cwd);
639
685
  let phaseInfo = findPhaseInternal(cwd, phase);
640
686
  if (phaseInfo?.['archived']) {
641
687
  const roadmapPhase = getRoadmapPhaseInternal(cwd, phase);
@@ -664,15 +710,30 @@ function cmdInitVerifyWork(cwd, phase, raw) {
664
710
  };
665
711
  }
666
712
  }
713
+ const phaseDir = phaseInfo?.['directory'] || null;
714
+ const planCount = phaseInfo?.['plans']?.length || 0;
715
+ const summaryCount = phaseInfo?.['summaries']?.length || 0;
716
+ const completion = buildPhaseCompletionProjection(cwd, phaseInfo?.['phase_number'] || phase, phaseDir, planCount, summaryCount, _slashRuntime);
717
+ const uatReport = phaseDir
718
+ ? evaluateUatPassed(node_path_1.default.join(cwd, phaseDir), {
719
+ policy: { requireVerification: true },
720
+ })
721
+ : null;
667
722
  const result = {
668
723
  planner_model: resolveModelInternal(cwd, 'gsd-planner'),
669
724
  checker_model: resolveModelInternal(cwd, 'gsd-plan-checker'),
670
725
  commit_docs: config.commit_docs,
671
726
  phase_found: !!phaseInfo,
672
- phase_dir: phaseInfo?.['directory'] || null,
727
+ phase_dir: phaseDir,
673
728
  phase_number: phaseInfo?.['phase_number'] || null,
674
729
  phase_name: phaseInfo?.['phase_name'] || null,
675
730
  has_verification: phaseInfo?.['has_verification'] || false,
731
+ phase_completion: {
732
+ ...completion,
733
+ uat_passed: uatReport?.passed ?? false,
734
+ uat_blockers: uatReport?.blockers ?? [],
735
+ ready_to_transition: completion.phase_complete && (uatReport?.passed ?? false),
736
+ },
676
737
  };
677
738
  output(withProjectRoot(cwd, result), raw);
678
739
  }
@@ -1035,18 +1096,23 @@ function cmdInitManager(cwd, raw) {
1035
1096
  let hasResearch = false;
1036
1097
  let lastActivity = null;
1037
1098
  let isActive = false;
1099
+ let completion = buildPhaseCompletionProjection(cwd, phaseNum, null, planCount, summaryCount, _slashRuntime);
1038
1100
  try {
1039
1101
  const dirs = _phaseDirEntries.filter(isDirInMilestone);
1040
1102
  const dirMatch = dirs.find((d) => phaseTokenMatches(d, normalized));
1041
1103
  if (dirMatch) {
1042
1104
  const fullDir = node_path_1.default.join(phasesDir, dirMatch);
1105
+ const phaseDirRel = toPosixPath(node_path_1.default.relative(cwd, fullDir));
1043
1106
  const phaseFiles = node_fs_1.default.readdirSync(fullDir);
1044
1107
  planCount = listPhasePlanFiles(fullDir).length;
1045
1108
  summaryCount = listPhaseSummaryFiles(fullDir).length;
1046
1109
  hasContext = findContextMdIn(fullDir) !== null;
1047
1110
  hasResearch = phaseFiles.some((f) => f.endsWith('-RESEARCH.md') || f === 'RESEARCH.md');
1048
- if (summaryCount >= planCount && planCount > 0)
1111
+ completion = buildPhaseCompletionProjection(cwd, phaseNum, phaseDirRel, planCount, summaryCount, _slashRuntime);
1112
+ if (completion.phase_complete)
1049
1113
  diskStatus = 'complete';
1114
+ else if (completion.implementation_complete)
1115
+ diskStatus = 'executed';
1050
1116
  else if (summaryCount > 0)
1051
1117
  diskStatus = 'partial';
1052
1118
  else if (planCount > 0)
@@ -1079,7 +1145,7 @@ function cmdInitManager(cwd, raw) {
1079
1145
  /* intentionally empty */
1080
1146
  }
1081
1147
  const roadmapComplete = _checkboxStates.get(phaseNum) || false;
1082
- if (roadmapComplete && diskStatus !== 'complete') {
1148
+ if (roadmapComplete && completion.phase_complete && diskStatus !== 'complete') {
1083
1149
  diskStatus = 'complete';
1084
1150
  }
1085
1151
  phases.push({
@@ -1093,6 +1159,7 @@ function cmdInitManager(cwd, raw) {
1093
1159
  plan_count: planCount,
1094
1160
  summary_count: summaryCount,
1095
1161
  roadmap_complete: roadmapComplete,
1162
+ ...completion,
1096
1163
  last_activity: lastActivity,
1097
1164
  is_active: isActive,
1098
1165
  });
@@ -1107,22 +1174,42 @@ function cmdInitManager(cwd, raw) {
1107
1174
  phase['display_name'] = name;
1108
1175
  }
1109
1176
  }
1110
- const completedNums = new Set(phases.filter((p) => p['disk_status'] === 'complete').map((p) => p['number']));
1177
+ function normalizePhaseNumber(value) {
1178
+ return value
1179
+ .split('.')
1180
+ .map((part) => {
1181
+ const match = /^(\d+)([A-Z]?)$/i.exec(part);
1182
+ if (!match)
1183
+ return part;
1184
+ return `${Number(match[1])}${match[2].toUpperCase()}`;
1185
+ })
1186
+ .join('.');
1187
+ }
1188
+ const completedNums = new Set(phases
1189
+ .filter((p) => p['phase_complete'] === true)
1190
+ .map((p) => normalizePhaseNumber(p['number'])));
1191
+ const phaseMap = new Map(phases.map((p) => [normalizePhaseNumber(p['number']), p]));
1111
1192
  const _allCompletedPattern = /-\s*\[x\]\s*.*Phase\s+(\d+[A-Z]?(?:\.\d+)*)[:\s]/gi;
1112
1193
  let _allMatch;
1113
1194
  while ((_allMatch = _allCompletedPattern.exec(rawContent)) !== null) {
1114
- completedNums.add(_allMatch[1]);
1195
+ const phaseNum = normalizePhaseNumber(_allMatch[1]);
1196
+ const phase = phaseMap.get(phaseNum);
1197
+ if (!phase || phase['phase_complete'] === true) {
1198
+ completedNums.add(phaseNum);
1199
+ }
1115
1200
  }
1116
- const phaseMap = new Map(phases.map((p) => [p['number'], p]));
1117
1201
  function reaches(from, to, visited = new Set()) {
1118
- if (visited.has(from))
1202
+ const normalizedFrom = normalizePhaseNumber(from);
1203
+ const normalizedTo = normalizePhaseNumber(to);
1204
+ if (visited.has(normalizedFrom))
1119
1205
  return false;
1120
- visited.add(from);
1121
- const p = phaseMap.get(from);
1206
+ visited.add(normalizedFrom);
1207
+ const p = phaseMap.get(normalizedFrom);
1122
1208
  if (!p || !p['dep_phases'] || p['dep_phases'].length === 0)
1123
1209
  return false;
1124
- if (p['dep_phases'].includes(to))
1210
+ if (p['dep_phases'].some((dep) => normalizePhaseNumber(dep) === normalizedTo)) {
1125
1211
  return true;
1212
+ }
1126
1213
  return p['dep_phases'].some((dep) => reaches(dep, to, visited));
1127
1214
  }
1128
1215
  function hasDepRelationship(numA, numB) {
@@ -1134,8 +1221,8 @@ function cmdInitManager(cwd, raw) {
1134
1221
  phase['deps_satisfied'] = true;
1135
1222
  }
1136
1223
  else {
1137
- const depNums = phase['depends_on'].match(/\d+(?:\.\d+)*/g) || [];
1138
- phase['deps_satisfied'] = depNums.every((n) => completedNums.has(n));
1224
+ const depNums = phase['depends_on'].match(/\d+[A-Z]?(?:\.\d+)*/gi) || [];
1225
+ phase['deps_satisfied'] = depNums.every((n) => completedNums.has(normalizePhaseNumber(n)));
1139
1226
  phase['dep_phases'] = depNums;
1140
1227
  }
1141
1228
  }
@@ -1167,7 +1254,16 @@ function cmdInitManager(cwd, raw) {
1167
1254
  continue;
1168
1255
  if (/^999(?:\.|$)/.test(phase['number']))
1169
1256
  continue;
1170
- if (phase['disk_status'] === 'planned' && phase['deps_satisfied']) {
1257
+ if (phase['disk_status'] === 'executed') {
1258
+ recommendedActions.push({
1259
+ phase: phase['number'],
1260
+ phase_name: phase['name'],
1261
+ action: 'verify',
1262
+ reason: `Implementation complete; verification ${phase['verification_status']}`,
1263
+ command: phase['verification_next_command'],
1264
+ });
1265
+ }
1266
+ else if (phase['disk_status'] === 'planned' && phase['deps_satisfied']) {
1171
1267
  recommendedActions.push({
1172
1268
  phase: phase['number'],
1173
1269
  phase_name: phase['name'],
@@ -1211,7 +1307,7 @@ function cmdInitManager(cwd, raw) {
1211
1307
  return true;
1212
1308
  });
1213
1309
  const nonBacklogPhases = phases.filter((p) => !/^999(?:\.|$)/.test(p['number']));
1214
- const completedCount = nonBacklogPhases.filter((p) => p['disk_status'] === 'complete').length;
1310
+ const completedCount = nonBacklogPhases.filter((p) => p['phase_complete'] === true).length;
1215
1311
  const sanitizeFlags = (rawVal) => {
1216
1312
  const val = typeof rawVal === 'string' ? rawVal : '';
1217
1313
  if (!val)
@@ -1238,7 +1334,7 @@ function cmdInitManager(cwd, raw) {
1238
1334
  phases,
1239
1335
  phase_count: phases.length,
1240
1336
  completed_count: completedCount,
1241
- in_progress_count: phases.filter((p) => ['partial', 'planned', 'discussed', 'researched'].includes(p['disk_status'])).length,
1337
+ in_progress_count: phases.filter((p) => ['executed', 'partial', 'planned', 'discussed', 'researched'].includes(p['disk_status'])).length,
1242
1338
  recommended_actions: filteredActions,
1243
1339
  waiting_signal: waitingSignal,
1244
1340
  all_complete: completedCount === nonBacklogPhases.length && nonBacklogPhases.length > 0,
@@ -1258,6 +1354,7 @@ function cmdInitProgress(cwd, raw) {
1258
1354
  }
1259
1355
  const config = loadConfig(cwd);
1260
1356
  const milestone = getMilestoneInfo(cwd);
1357
+ const _slashRuntime = (0, runtime_slash_cjs_1.resolveRuntime)(cwd);
1261
1358
  const phasesDir = node_path_1.default.join(planningDir(cwd), 'phases');
1262
1359
  const phases = [];
1263
1360
  let currentPhase = null;
@@ -1307,24 +1404,29 @@ function cmdInitProgress(cwd, raw) {
1307
1404
  const plans = listPhasePlanFiles(phasePath);
1308
1405
  const summaries = listPhaseSummaryFiles(phasePath);
1309
1406
  const hasResearch = phaseFiles.some((f) => f.endsWith('-RESEARCH.md') || f === 'RESEARCH.md');
1310
- const status = summaries.length >= plans.length && plans.length > 0
1407
+ const phaseDirRel = toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'phases', dir)));
1408
+ const completion = buildPhaseCompletionProjection(cwd, phaseNumber, phaseDirRel, plans.length, summaries.length, _slashRuntime);
1409
+ const status = completion.phase_complete
1311
1410
  ? 'complete'
1312
- : plans.length > 0
1313
- ? 'in_progress'
1314
- : hasResearch
1315
- ? 'researched'
1316
- : 'pending';
1411
+ : completion.implementation_complete
1412
+ ? 'executed'
1413
+ : plans.length > 0
1414
+ ? 'in_progress'
1415
+ : hasResearch
1416
+ ? 'researched'
1417
+ : 'pending';
1317
1418
  const phaseInfo = {
1318
1419
  number: phaseNumber,
1319
1420
  name: phaseName,
1320
- directory: toPosixPath(node_path_1.default.relative(cwd, node_path_1.default.join(planningDir(cwd), 'phases', dir))),
1421
+ directory: phaseDirRel,
1321
1422
  status,
1322
1423
  plan_count: plans.length,
1323
1424
  summary_count: summaries.length,
1324
1425
  has_research: hasResearch,
1426
+ ...completion,
1325
1427
  };
1326
1428
  phases.push(phaseInfo);
1327
- if (!currentPhase && (status === 'in_progress' || status === 'researched')) {
1429
+ if (!currentPhase && (status === 'executed' || status === 'in_progress' || status === 'researched')) {
1328
1430
  currentPhase = phaseInfo;
1329
1431
  }
1330
1432
  if (!nextPhase && status === 'pending') {
@@ -1340,7 +1442,8 @@ function cmdInitProgress(cwd, raw) {
1340
1442
  if (!seenPhaseNums.has(stripped)) {
1341
1443
  const checkboxComplete = roadmapCheckboxStates.get(num) === true ||
1342
1444
  roadmapCheckboxStates.get(stripped) === true;
1343
- const status = checkboxComplete ? 'complete' : 'not_started';
1445
+ const completion = buildPhaseCompletionProjection(cwd, num, null, 0, 0, _slashRuntime);
1446
+ const status = 'not_started';
1344
1447
  const phaseInfo = {
1345
1448
  number: num,
1346
1449
  name: name.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, ''),
@@ -1349,9 +1452,11 @@ function cmdInitProgress(cwd, raw) {
1349
1452
  plan_count: 0,
1350
1453
  summary_count: 0,
1351
1454
  has_research: false,
1455
+ roadmap_complete: checkboxComplete,
1456
+ ...completion,
1352
1457
  };
1353
1458
  phases.push(phaseInfo);
1354
- if (!nextPhase && !currentPhase && status !== 'complete') {
1459
+ if (!nextPhase && !currentPhase && !checkboxComplete) {
1355
1460
  nextPhase = phaseInfo;
1356
1461
  }
1357
1462
  }
@@ -1373,7 +1478,7 @@ function cmdInitProgress(cwd, raw) {
1373
1478
  phases,
1374
1479
  phase_count: phases.length,
1375
1480
  completed_count: phases.filter((p) => p['status'] === 'complete').length,
1376
- in_progress_count: phases.filter((p) => p['status'] === 'in_progress').length,
1481
+ in_progress_count: phases.filter((p) => ['executed', 'in_progress'].includes(p['status'])).length,
1377
1482
  current_phase: currentPhase,
1378
1483
  next_phase: nextPhase,
1379
1484
  paused_at: pausedAt,
@@ -1651,7 +1756,7 @@ function buildAgentSkillsBlock(config, agentType, projectRoot, diagnostics) {
1651
1756
  if (entry.kind === 'directive') {
1652
1757
  return `- Load the \`${entry.name}\` skill via the Skill tool before proceeding (plugin-provided).`;
1653
1758
  }
1654
- return `- @${entry.ref}`;
1759
+ return `- @${String(entry.ref).replace(/\\/g, '/')}`;
1655
1760
  }).join('\n');
1656
1761
  return `<agent_skills>\nRead these user-configured skills:\n${lines}\n</agent_skills>`;
1657
1762
  }