@opengsd/gsd-core 1.16.0 → 1.18.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 (211) hide show
  1. package/.claude-plugin/marketplace.json +1 -1
  2. package/.claude-plugin/plugin.json +1 -2
  3. package/.opencode/plugins/gsd-core.js +251 -1
  4. package/.opencode/plugins/gsd-statusline/tui.tsx +55 -0
  5. package/GEMINI.md +1 -1
  6. package/agents/gsd-advisor-researcher.compact.md +1 -1
  7. package/agents/gsd-advisor-researcher.md +1 -1
  8. package/agents/gsd-assumptions-analyzer.compact.md +1 -1
  9. package/agents/gsd-assumptions-analyzer.md +1 -1
  10. package/agents/gsd-code-fixer.compact.md +1 -1
  11. package/agents/gsd-code-fixer.md +1 -1
  12. package/agents/gsd-code-reviewer.compact.md +1 -1
  13. package/agents/gsd-code-reviewer.md +1 -1
  14. package/agents/gsd-codebase-mapper.compact.md +1 -1
  15. package/agents/gsd-codebase-mapper.md +1 -1
  16. package/agents/gsd-debug-session-manager.compact.md +6 -0
  17. package/agents/gsd-debug-session-manager.md +6 -0
  18. package/agents/gsd-debugger.md +1 -1
  19. package/agents/gsd-doc-verifier.compact.md +4 -4
  20. package/agents/gsd-doc-verifier.md +4 -3
  21. package/agents/gsd-doc-writer.compact.md +23 -5
  22. package/agents/gsd-doc-writer.md +19 -6
  23. package/agents/gsd-eval-auditor.compact.md +1 -1
  24. package/agents/gsd-eval-auditor.md +1 -1
  25. package/agents/gsd-executor.md +7 -3
  26. package/agents/gsd-integration-checker.compact.md +1 -1
  27. package/agents/gsd-integration-checker.md +1 -1
  28. package/agents/gsd-nyquist-auditor.compact.md +1 -1
  29. package/agents/gsd-nyquist-auditor.md +1 -1
  30. package/agents/gsd-phase-researcher.md +1 -1
  31. package/agents/gsd-plan-checker.md +2 -1
  32. package/agents/gsd-planner.md +5 -4
  33. package/agents/gsd-project-researcher.compact.md +1 -1
  34. package/agents/gsd-project-researcher.md +1 -1
  35. package/agents/gsd-research-synthesizer.compact.md +5 -5
  36. package/agents/gsd-research-synthesizer.md +5 -5
  37. package/agents/gsd-roadmapper.compact.md +1 -1
  38. package/agents/gsd-roadmapper.md +1 -1
  39. package/agents/gsd-security-auditor.compact.md +1 -1
  40. package/agents/gsd-security-auditor.md +1 -1
  41. package/agents/gsd-ui-auditor.compact.md +1 -1
  42. package/agents/gsd-ui-auditor.md +1 -1
  43. package/agents/gsd-ui-checker.compact.md +1 -1
  44. package/agents/gsd-ui-checker.md +1 -1
  45. package/agents/gsd-ui-researcher.compact.md +13 -1
  46. package/agents/gsd-ui-researcher.md +28 -1
  47. package/agents/gsd-verifier.md +1 -1
  48. package/bin/gsd-mcp-server.js +3 -1
  49. package/bin/install.js +398 -95
  50. package/commands/gsd/code-review.md +8 -0
  51. package/commands/gsd/pause-work.md +1 -1
  52. package/gsd-core/bin/gsd-tools.cjs +34 -12
  53. package/gsd-core/bin/lib/adr-parser.cjs +37 -5
  54. package/gsd-core/bin/lib/capability-ledger.cjs +25 -0
  55. package/gsd-core/bin/lib/capability-lock.cjs +29 -7
  56. package/gsd-core/bin/lib/capability-registry.cjs +138 -80
  57. package/gsd-core/bin/lib/capability-source.cjs +3 -1
  58. package/gsd-core/bin/lib/capability-state.cjs +6 -3
  59. package/gsd-core/bin/lib/capability-validator.cjs +8 -5
  60. package/gsd-core/bin/lib/capability-writer.cjs +2 -1
  61. package/gsd-core/bin/lib/commands.cjs +33 -9
  62. package/gsd-core/bin/lib/config-loader.cjs +3 -3
  63. package/gsd-core/bin/lib/decision-coverage-support.cjs +19 -0
  64. package/gsd-core/bin/lib/gate-context-drift.cjs +5 -4
  65. package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +15 -0
  66. package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +13 -0
  67. package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +23 -3
  68. package/gsd-core/bin/lib/gate-predicate.cjs +8 -12
  69. package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +60 -5
  70. package/gsd-core/bin/lib/gate-ui-safety.cjs +2 -1
  71. package/gsd-core/bin/lib/graphify.cjs +3 -2
  72. package/gsd-core/bin/lib/init.cjs +43 -16
  73. package/gsd-core/bin/lib/install-effort-resolver.cjs +2 -2
  74. package/gsd-core/bin/lib/install-engine.cjs +11 -0
  75. package/gsd-core/bin/lib/install-model-override-resolver.cjs +4 -3
  76. package/gsd-core/bin/lib/install-profiles.cjs +5 -3
  77. package/gsd-core/bin/lib/install-shadow-report.cjs +12 -0
  78. package/gsd-core/bin/lib/installer-migrations/011-opencode-mcp-no-prompts.cjs +99 -0
  79. package/gsd-core/bin/lib/installer-migrations.cjs +54 -1
  80. package/gsd-core/bin/lib/jsonc-edit.cjs +418 -0
  81. package/gsd-core/bin/lib/loop-host-contract.cjs +2 -1
  82. package/gsd-core/bin/lib/mcp-server.cjs +5 -1
  83. package/gsd-core/bin/lib/opencode-mcp-entry.cjs +39 -0
  84. package/gsd-core/bin/lib/opencode-version.cjs +486 -0
  85. package/gsd-core/bin/lib/phase.cjs +141 -11
  86. package/gsd-core/bin/lib/quick-batch-dispatch.cjs +2 -1
  87. package/gsd-core/bin/lib/report-parser.cjs +57 -1
  88. package/gsd-core/bin/lib/review-lane-invocation.cjs +40 -0
  89. package/gsd-core/bin/lib/review-reviewer-selection.cjs +20 -1
  90. package/gsd-core/bin/lib/roadmap.cjs +56 -5
  91. package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +142 -6
  92. package/gsd-core/bin/lib/runtime-homes.cjs +18 -0
  93. package/gsd-core/bin/lib/runtime-hooks-surface.cjs +58 -3
  94. package/gsd-core/bin/lib/runtime-slash.cjs +13 -0
  95. package/gsd-core/bin/lib/shell-command-projection.cjs +2 -0
  96. package/gsd-core/bin/lib/smart-entry.cjs +100 -53
  97. package/gsd-core/bin/lib/state-document.cjs +37 -3
  98. package/gsd-core/bin/lib/state-transition.cjs +98 -19
  99. package/gsd-core/bin/lib/state.cjs +544 -126
  100. package/gsd-core/bin/lib/task-command-router.cjs +2 -3
  101. package/gsd-core/bin/lib/tdd-red-evidence.cjs +5 -0
  102. package/gsd-core/bin/lib/tdd-test-files.cjs +42 -0
  103. package/gsd-core/bin/lib/update-context.cjs +18 -1
  104. package/gsd-core/bin/lib/verification.cjs +107 -17
  105. package/gsd-core/bin/lib/verify.cjs +36 -1
  106. package/gsd-core/references/agent-contracts.md +1 -1
  107. package/gsd-core/references/agent-skills-bootstrap.md +26 -7
  108. package/gsd-core/references/git-integration.md +3 -3
  109. package/gsd-core/references/planner-reversibility.md +18 -4
  110. package/gsd-core/references/planner-verify-command-grounding.md +18 -0
  111. package/gsd-core/references/tdd.md +4 -3
  112. package/gsd-core/references/verifier-phase-gates.md +8 -1
  113. package/gsd-core/workflows/add-backlog.md +10 -2
  114. package/gsd-core/workflows/add-phase.md +5 -1
  115. package/gsd-core/workflows/add-tests.md +5 -1
  116. package/gsd-core/workflows/add-todo.md +5 -1
  117. package/gsd-core/workflows/ai-integration-phase.md +5 -1
  118. package/gsd-core/workflows/audit-milestone.md +21 -9
  119. package/gsd-core/workflows/autonomous/templates/minimal-context.md +41 -0
  120. package/gsd-core/workflows/autonomous.md +52 -64
  121. package/gsd-core/workflows/check-todos.md +5 -1
  122. package/gsd-core/workflows/code-review-fix.md +5 -1
  123. package/gsd-core/workflows/code-review.md +21 -2
  124. package/gsd-core/workflows/complete-milestone/detail/elaboration.md +5 -1
  125. package/gsd-core/workflows/complete-milestone.md +25 -5
  126. package/gsd-core/workflows/debug.md +5 -1
  127. package/gsd-core/workflows/diagnose-issues.md +5 -1
  128. package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -1
  129. package/gsd-core/workflows/discuss-phase-assumptions.md +11 -3
  130. package/gsd-core/workflows/discuss-phase.md +5 -1
  131. package/gsd-core/workflows/docs-update/detail/elaboration.md +7 -6
  132. package/gsd-core/workflows/docs-update.md +27 -5
  133. package/gsd-core/workflows/edit-phase.md +5 -1
  134. package/gsd-core/workflows/eval-review.md +11 -3
  135. package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +5 -1
  136. package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +1 -1
  137. package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +12 -6
  138. package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +19 -2
  139. package/gsd-core/workflows/execute-phase/steps/regression-gate.md +18 -1
  140. package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +5 -1
  141. package/gsd-core/workflows/execute-phase.md +40 -7
  142. package/gsd-core/workflows/execute-plan.md +8 -4
  143. package/gsd-core/workflows/extract-learnings.md +6 -2
  144. package/gsd-core/workflows/forensics.md +5 -1
  145. package/gsd-core/workflows/import.md +5 -1
  146. package/gsd-core/workflows/insert-phase.md +5 -1
  147. package/gsd-core/workflows/manager.md +10 -2
  148. package/gsd-core/workflows/map-codebase.md +5 -1
  149. package/gsd-core/workflows/milestone-summary.md +10 -2
  150. package/gsd-core/workflows/new-milestone.md +13 -5
  151. package/gsd-core/workflows/new-project.md +9 -5
  152. package/gsd-core/workflows/next.md +8 -4
  153. package/gsd-core/workflows/pause-work.md +3 -3
  154. package/gsd-core/workflows/plan-phase/steps/auto-advance-check.md +70 -0
  155. package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +5 -1
  156. package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +6 -2
  157. package/gsd-core/workflows/plan-phase.md +41 -73
  158. package/gsd-core/workflows/plan-review-convergence.md +17 -5
  159. package/gsd-core/workflows/plant-seed.md +8 -4
  160. package/gsd-core/workflows/progress.md +6 -2
  161. package/gsd-core/workflows/quick-batch.md +5 -1
  162. package/gsd-core/workflows/quick.md +22 -7
  163. package/gsd-core/workflows/remove-phase.md +5 -1
  164. package/gsd-core/workflows/resume-project.md +19 -11
  165. package/gsd-core/workflows/review.md +6 -2
  166. package/gsd-core/workflows/scan.md +5 -1
  167. package/gsd-core/workflows/secure-phase.md +6 -2
  168. package/gsd-core/workflows/settings.md +3 -3
  169. package/gsd-core/workflows/ship.md +13 -10
  170. package/gsd-core/workflows/thread.md +10 -2
  171. package/gsd-core/workflows/transition.md +5 -1
  172. package/gsd-core/workflows/ui-phase.md +5 -1
  173. package/gsd-core/workflows/ui-review.md +6 -2
  174. package/gsd-core/workflows/ultraplan-phase.md +5 -1
  175. package/gsd-core/workflows/update.md +5 -1
  176. package/gsd-core/workflows/validate-phase.md +6 -2
  177. package/gsd-core/workflows/verify-work/detail/elaboration.md +1 -1
  178. package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +5 -1
  179. package/gsd-core/workflows/verify-work.md +11 -3
  180. package/hooks/dist/gsd-agent-isolation-guard.js +3 -1
  181. package/hooks/dist/gsd-check-update.js +26 -20
  182. package/hooks/dist/gsd-context-monitor.js +14 -5
  183. package/hooks/dist/gsd-cursor-subagent-start.js +2 -1
  184. package/hooks/dist/gsd-secret-read-guard.js +189 -15
  185. package/hooks/dist/gsd-statusline.js +321 -22
  186. package/hooks/dist/gsd-validate-commit.sh +27 -5
  187. package/hooks/gsd-agent-isolation-guard.js +3 -1
  188. package/hooks/gsd-check-update.js +26 -20
  189. package/hooks/gsd-context-monitor.js +14 -5
  190. package/hooks/gsd-cursor-subagent-start.js +2 -1
  191. package/hooks/gsd-secret-read-guard.js +189 -15
  192. package/hooks/gsd-statusline.js +321 -22
  193. package/hooks/gsd-validate-commit.sh +27 -5
  194. package/package.json +1 -1
  195. package/scripts/audit-workflow-script-paths.cjs +1 -1
  196. package/scripts/diagnose-validate-commit-in-tier.cjs +270 -0
  197. package/scripts/diagnose-validate-commit-phases.cjs +348 -0
  198. package/scripts/diagnose-windows-spawn-watcher.cjs +317 -0
  199. package/scripts/docs-guard-registry.cjs +2 -0
  200. package/scripts/gen-capability-registry.cjs +7 -1
  201. package/scripts/gen-loop-host-contract.cjs +4 -1
  202. package/scripts/lib/macos-conformance-tier.generated.cjs +13 -0
  203. package/scripts/lib/platform-conformance-tier.generated.cjs +26 -0
  204. package/scripts/lint-phase-arg-assignment.cjs +2 -2
  205. package/scripts/lint-workflow-shellcheck-baseline.json +16 -16
  206. package/scripts/mutation-matrix.cjs +3 -1
  207. package/scripts/release-tarball-smoke.cjs +15 -7
  208. package/scripts/xtrace-phase-timing.cjs +205 -0
  209. package/skills/gsd-code-review/SKILL.md +8 -0
  210. package/skills/gsd-pause-work/SKILL.md +1 -1
  211. package/vscode/package.json +1 -1
@@ -325,22 +325,29 @@ const RUNTIME_CONFIG_FILES = Object.freeze(['settings.json', 'hooks.json', 'conf
325
325
  * embeds a launch path without registering it, which a structured
326
326
  * JSON.parse of the expected schema would miss entirely.
327
327
  *
328
- * Windows configs store paths with backslashes, which JSON/TOML doubles on
329
- * write; collapsing `\\` to `\` first makes the raw text scan work on both
330
- * platforms without parsing each config format separately (POSIX text has no
331
- * backslashes, so the collapse is a no-op there).
332
- *
333
328
  * Every writer bakes `configDir` through the same posixNormalize seam
334
329
  * (src/runtime-hooks-surface.cts) before writing it into config text, on
335
330
  * every platform — so the anchor must match that projection, not the
336
331
  * OS-native `configDir` string this function receives.
332
+ *
333
+ * The scanned text goes through that same projection (#5084): a registration
334
+ * the installer did not write — a hand-edited settings.json, another tool's
335
+ * writer — may spell its path with Windows-native backslashes, and an anchor
336
+ * and haystack in different separator forms never match, so the dangling-hook
337
+ * check would skip it silently. Scan both the backslash-unescaped projection
338
+ * (JSON and TOML basic strings) and the unchanged projection (TOML literal
339
+ * strings). Unescaping a literal UNC prefix drops a leading separator, so
340
+ * neither projection alone is sufficient. A Set combines identical paths.
337
341
  */
338
342
  function configuredEntrypointsIn(text, configDir) {
339
343
  const normalizedPrefix = shellCmdProjection.posixNormalize(configDir).replace(/\/+$/, '') + '/';
340
344
  const scriptPathRe = new RegExp(`${escapeRegex(normalizedPrefix)}[^"']{0,400}?\\.(?:js|cjs|mjs|sh|cmd|ps1)`, 'g');
341
345
  const found = new Set();
342
- for (const match of text.replace(/\\\\/g, '\\').matchAll(scriptPathRe)) {
343
- found.add(path.resolve(match[0]));
346
+ for (const candidateText of [text.replace(/\\\\/g, '\\'), text]) {
347
+ const scannedText = shellCmdProjection.posixNormalize(candidateText);
348
+ for (const match of scannedText.matchAll(scriptPathRe)) {
349
+ found.add(path.resolve(match[0]));
350
+ }
344
351
  }
345
352
  return [...found];
346
353
  }
@@ -696,6 +703,7 @@ function runSmoke({
696
703
  ...effectiveNpmEnv,
697
704
  HOME: runtimeHome,
698
705
  USERPROFILE: runtimeHome,
706
+ GSD_HOME: runtimeHome, // #5017: the defaults store root follows HOME
699
707
  // Same reason as the init check above: install.js skips its main()
700
708
  // block when GSD_TEST_MODE is set and would exit 0 writing nothing.
701
709
  GSD_TEST_MODE: '',
@@ -0,0 +1,205 @@
1
+ 'use strict';
2
+
3
+ /**
4
+ * Per-line cost attribution for a `bash -x` trace whose PS4 stamps every
5
+ * traced command with a millisecond timestamp, its line number and enclosing
6
+ * function (#5473).
7
+ *
8
+ * The stamp is `<depth marks>@@<nonce>@@<epoch-ms>@@<lineno>@@<function>@@ `, so
9
+ * one trace line looks like `++@@k3f9@@1760615775123@@271@@@@ MSG_SUFFIX=...`.
10
+ * The nonce is a per-run random token: a traced value that merely LOOKS like a
11
+ * stamp (hostile or accidental) cannot forge one without knowing it.
12
+ *
13
+ * WHAT A GAP MEANS. bash prints a trace line (and so evaluates PS4) AFTER it
14
+ * has expanded the command's words and BEFORE it runs the command. The gap
15
+ * between stamp i and stamp i+1 is therefore
16
+ *
17
+ * execution of command i + expansion of command i+1
18
+ *
19
+ * which cannot be split from the trace alone. Both attributions are reported:
20
+ * `afterMs` (the gap that FOLLOWS the line) is large for a line that EXECUTES
21
+ * slowly (an external command, a `$(...)` already expanded); `beforeMs` (the
22
+ * gap that PRECEDES it) is large for a line whose EXPANSION is slow (a
23
+ * `${var//pat/x}` or `${var:off}` over a 112KB value, whose result is only
24
+ * printed once it is computed). Neither alone is "the cost of the line".
25
+ *
26
+ * Every traced command also pays one stamp (a subshell and a perl); measure it
27
+ * with a run of no-ops (see the CLI) and read gaps below it as zero.
28
+ *
29
+ * No I/O and no clock: every function returns a value computed from its
30
+ * arguments. The CLI that runs bash is scripts/diagnose-validate-commit-phases.cjs.
31
+ */
32
+
33
+ /** PS4 to hand to `bash -x`; `timestampCommand` prints the epoch milliseconds. */
34
+ function stampPs4(timestampCommand, nonce) {
35
+ return `+@@${nonce}@@$(${timestampCommand})@@\${LINENO}@@\${FUNCNAME:-}@@ `;
36
+ }
37
+
38
+ /**
39
+ * How the stamp reaches bash. Every other route failed somewhere:
40
+ * - an exported PS4: bash 5.x does not run the command substitution in a PS4
41
+ * it imported from the environment, so it silently degrades to `+ `;
42
+ * - BASH_ENV: bash reads the system bashrc INSTEAD of it in some
43
+ * environments (it did in the gsd-test tester container);
44
+ * - `bash -c '... . "$0"'`: puts an uncontrolled path inside a shell command.
45
+ * So the script under test is run as an ordinary script that has ONE line
46
+ * inserted after its shebang, which assigns PS4 (read from a sibling file, so
47
+ * the stamp text is never quoted into shell code) and turns on xtrace. `$0`,
48
+ * `exit` and sibling files then behave exactly as in a direct run (the CLI's
49
+ * tests pin this against the real hook).
50
+ */
51
+ const TRACE_PS4_FILE = 'xtrace-ps4.txt';
52
+ const INJECTED_LINES = 1;
53
+ const INJECTED_LINE = `PS4="$(cat "\${0%/*}/${TRACE_PS4_FILE}")"; set -x`;
54
+
55
+ /**
56
+ * `scriptText` with the tracing line inserted after its shebang (or at the top
57
+ * when it has none). The script must be run by a path containing a `/`, with
58
+ * the PS4 text in TRACE_PS4_FILE beside it. Adds exactly INJECTED_LINES lines.
59
+ */
60
+ function injectTracing(scriptText) {
61
+ const text = String(scriptText);
62
+ if (!text.startsWith('#!')) return `${INJECTED_LINE}\n${text}`;
63
+ const eol = text.indexOf('\n');
64
+ if (eol === -1) return `${text}\n${INJECTED_LINE}\n`;
65
+ return `${text.slice(0, eol + 1)}${INJECTED_LINE}\n${text.slice(eol + 1)}`;
66
+ }
67
+
68
+ // What follows the depth marks and the nonce: `<epoch-ms>@@<lineno>@@<function>@@ `.
69
+ const STAMP_TAIL = /^(\d+)@@(\d+)@@([^@]*)@@ ?/;
70
+
71
+ /**
72
+ * Parse a stamped trace. Lines with no stamp are continuations of the
73
+ * previous command (a traced value that itself contains newlines) and are
74
+ * folded into it; text before the first stamp is ignored.
75
+ *
76
+ * @param {string} text
77
+ * @param {string} nonce the nonce given to stampPs4
78
+ * @returns {{ t: number, line: number, depth: number, fn: string, cmd: string }[]}
79
+ */
80
+ function parseTimedXtrace(text, nonce) {
81
+ // The nonce is matched as a literal prefix, never compiled into a pattern.
82
+ const head = `@@${nonce}@@`;
83
+ const entries = [];
84
+ const lines = String(text).split('\n');
85
+ // A stream ends with the newline of its last line; that is a terminator, not
86
+ // an empty continuation of the last command.
87
+ if (lines.length > 0 && lines[lines.length - 1] === '') lines.pop();
88
+ for (const raw of lines) {
89
+ let depth = 0;
90
+ while (raw[depth] === '+') depth++;
91
+ const tail = depth > 0 && raw.startsWith(head, depth)
92
+ ? STAMP_TAIL.exec(raw.slice(depth + head.length))
93
+ : null;
94
+ if (tail) {
95
+ entries.push({
96
+ t: Number(tail[1]),
97
+ line: Number(tail[2]),
98
+ depth,
99
+ fn: tail[3],
100
+ cmd: raw.slice(depth + head.length + tail[0].length),
101
+ });
102
+ } else if (entries.length > 0) {
103
+ entries[entries.length - 1].cmd += `\n${raw}`;
104
+ }
105
+ }
106
+ return entries;
107
+ }
108
+
109
+ /**
110
+ * Entries with their line numbers moved back by `injected` lines, so a stamp
111
+ * reports the line of the ORIGINAL script, not of the instrumented copy.
112
+ */
113
+ function shiftLines(entries, injected) {
114
+ return entries.map((e) => ({ ...e, line: e.line - injected }));
115
+ }
116
+
117
+ /**
118
+ * Attribute the gaps around each entry to its (function, line): `afterMs` is
119
+ * the gap to the NEXT stamp, `beforeMs` the gap from the PREVIOUS one (see the
120
+ * header for what each means). A clock that steps backwards counts as zero.
121
+ *
122
+ * @param {{ t: number, line: number, fn?: string, cmd: string }[]} entries
123
+ * @returns {{ costs: { fn: string, line: number, afterMs: number, beforeMs: number, maxMs: number, count: number, sample: string }[], totalMs: number }}
124
+ */
125
+ function phaseCosts(entries) {
126
+ const byKey = new Map();
127
+ const last = entries.length - 1;
128
+ for (let i = 0; i <= last; i++) {
129
+ const after = i < last ? Math.max(0, entries[i + 1].t - entries[i].t) : 0;
130
+ const before = i > 0 ? Math.max(0, entries[i].t - entries[i - 1].t) : 0;
131
+ const e = entries[i];
132
+ const fn = e.fn || '';
133
+ const key = `${fn}\u0000${e.line}`;
134
+ const acc = byKey.get(key) || { fn, line: e.line, afterMs: 0, beforeMs: 0, maxMs: 0, count: 0, sample: '' };
135
+ acc.afterMs += after;
136
+ acc.beforeMs += before;
137
+ acc.count += 1;
138
+ const peak = Math.max(after, before);
139
+ if (peak >= acc.maxMs) {
140
+ acc.maxMs = peak;
141
+ acc.sample = e.cmd;
142
+ }
143
+ byKey.set(key, acc);
144
+ }
145
+ const totalMs = entries.length > 1 ? Math.max(0, entries[last].t - entries[0].t) : 0;
146
+ return { costs: [...byKey.values()], totalMs };
147
+ }
148
+
149
+ /** A line's weight when ranking: the larger of its two attributions. */
150
+ const peakMs = (c) => Math.max(c.afterMs, c.beforeMs);
151
+
152
+ /**
153
+ * The `n` most expensive lines, costliest first; ties broken by function then
154
+ * line number so the order is deterministic.
155
+ */
156
+ function slowestLines(costs, n) {
157
+ return [...costs]
158
+ .sort((a, b) => peakMs(b) - peakMs(a) || (a.fn < b.fn ? -1 : a.fn > b.fn ? 1 : 0) || a.line - b.line)
159
+ .slice(0, n);
160
+ }
161
+
162
+ /**
163
+ * Elapsed time per run of `size` source lines of the script's top level, in
164
+ * line order, plus one entry per function. `afterMs` summed over a region is
165
+ * the time from its first stamp to the next region's, so regions roughly
166
+ * partition the run: a coarse "which phase" view that needs no knowledge of
167
+ * the script.
168
+ *
169
+ * @returns {{ label: string, afterMs: number }[]}
170
+ */
171
+ function rollupByRegion(costs, size) {
172
+ if (!Number.isInteger(size) || size < 1) throw new RangeError('region size must be a positive integer');
173
+ const regions = new Map();
174
+ for (const c of costs) {
175
+ const start = Math.floor((c.line - 1) / size) * size + 1;
176
+ const label = c.fn ? `in ${c.fn}()` : `lines ${start}-${start + size - 1}`;
177
+ const from = c.fn ? Number.MAX_SAFE_INTEGER : start; // functions sort after the top level
178
+ const acc = regions.get(label) || { label, from, afterMs: 0 };
179
+ acc.afterMs += c.afterMs;
180
+ regions.set(label, acc);
181
+ }
182
+ return [...regions.values()]
183
+ .sort((a, b) => a.from - b.from || (a.label < b.label ? -1 : a.label > b.label ? 1 : 0))
184
+ .map(({ label, afterMs }) => ({ label, afterMs }));
185
+ }
186
+
187
+ /** One-line, length-bounded rendering of a traced command for a report. */
188
+ function abbreviate(cmd, max = 100) {
189
+ const flat = String(cmd).replace(/\s+/g, ' ').trim();
190
+ return flat.length <= max ? flat : `${flat.slice(0, max)}… (${flat.length} chars)`;
191
+ }
192
+
193
+ module.exports = {
194
+ stampPs4,
195
+ TRACE_PS4_FILE,
196
+ INJECTED_LINES,
197
+ injectTracing,
198
+ parseTimedXtrace,
199
+ shiftLines,
200
+ phaseCosts,
201
+ peakMs,
202
+ slowestLines,
203
+ rollupByRegion,
204
+ abbreviate,
205
+ };
@@ -48,6 +48,14 @@ Optional flags parsed from the `<arguments>` block:
48
48
  - `--files=file1,file2,...` — Explicit file list override. Has highest precedence for file scoping per D-08. When provided, workflow skips SUMMARY.md extraction and git diff fallback entirely.
49
49
 
50
50
  Context files (CLAUDE.md, SUMMARY.md, phase state) are resolved inside the workflow via `gsd-tools query init.phase-op` and delegated to agent via `<required_reading>` blocks.
51
+
52
+ ## Outside a phase
53
+
54
+ Reviewer lanes run without a phase (`gsd_run` is defined by the runtime-launcher preamble inside workflow fences; in a plain shell call `node <install>/gsd-core/bin/gsd-tools.cjs` instead): pipe canonical repo-relative file paths (one per line) to stdin of
55
+ `gsd_run review-lane dispatch-step --repo-root <root> --depth <quick|standard|deep> --base-sha <sha> --run-dir "$(mktemp -d)" --cap-id code-review --point execute:post --explicit <slugs> --raw`.
56
+ `review-lane` flags are space-separated (`--depth quick`), never `--flag=value`. `--explicit` (omitted, nothing dispatches yet the call exits ok), `--depth`, `--base-sha` (lane reviews changes relative to it; commit them first) and stdin paths are required; `--run-dir` should be a fresh temp dir (default `.` writes the prompt file into the current directory); read each lane's output path (`results[].reviewPath`) from the JSON before removing it. `--cap-id`/`--point` select the `code-review` capability, which must be enabled (`workflow.code_review`) and active at `--point` — `execute:post`, or `execute:wave:post` when `workflow.code_review_point` selects it; otherwise nothing dispatches.
57
+ `--explicit` takes comma-separated canonical slugs, no spaces (`codex,antigravity`), not flag aliases (`antigravity`, not `agy`); slugs: first column of `gsd_run review-lane sections` (`review-lane flags` lists flags/aliases).
58
+ Each lane declares its own model key, which is not always its slug (antigravity's is `review.models.agy`); see the `review.models.*` table in `docs/CONFIGURATION.md`. Unset = lane default model.
51
59
  </context>
52
60
 
53
61
  <process>
@@ -21,7 +21,7 @@ Routes to the pause-work workflow which handles:
21
21
  - Current phase detection from recent files
22
22
  - Complete state gathering (position, completed work, remaining work, decisions, blockers)
23
23
  - Handoff file creation with all context sections
24
- - Git commit as WIP
24
+ - Git commit handoff
25
25
  - Resume instructions
26
26
  </objective>
27
27
 
@@ -2,7 +2,7 @@
2
2
  "name": "gsd-core-vscode",
3
3
  "displayName": "GSD Core",
4
4
  "description": "GSD orchestration engine embedded in VS Code (ADR-1239 IDE profile).",
5
- "version": "1.16.0",
5
+ "version": "1.18.0",
6
6
  "publisher": "opengsd",
7
7
  "engines": {
8
8
  "vscode": "^1.105.0"