@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
@@ -0,0 +1,418 @@
1
+ "use strict";
2
+ /**
3
+ * Comment-preserving JSONC editing (issue #5334; epic #5331, Phase 2 #5338).
4
+ *
5
+ * OpenCode (1.x and 2.x) and Kilo read `opencode.jsonc`/`kilo.jsonc`, so users annotate them. The
6
+ * installer used to parse such a file and write it back with `JSON.stringify`, which dropped every
7
+ * comment, trailing comma and the user's layout. This module edits the TEXT instead: it locates the
8
+ * property GSD owns and splices only that, leaving every other byte as the user wrote it.
9
+ *
10
+ * Scope is deliberately small — set a value at a key path, remove a key path (pruning objects the
11
+ * removal emptied). It is not a general JSONC formatter. A document it cannot parse or whose root is
12
+ * not an object is reported as `null` so the caller can keep its existing behavior.
13
+ */
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.hasJsoncFeatures = hasJsoncFeatures;
16
+ exports.setJsoncValue = setJsoncValue;
17
+ exports.removeJsoncValue = removeJsoncValue;
18
+ exports.isEditableJsonc = isEditableJsonc;
19
+ exports.parseJsonc = parseJsonc;
20
+ class JsoncSyntaxError extends Error {
21
+ }
22
+ function skipTrivia(text, from) {
23
+ let i = from;
24
+ while (i < text.length) {
25
+ const c = text[i];
26
+ if (c === ' ' || c === '\t' || c === '\n' || c === '\r') {
27
+ i++;
28
+ }
29
+ else if (c === '/' && text[i + 1] === '/') {
30
+ while (i < text.length && text[i] !== '\n')
31
+ i++;
32
+ }
33
+ else if (c === '/' && text[i + 1] === '*') {
34
+ const close = text.indexOf('*/', i + 2);
35
+ if (close === -1)
36
+ throw new JsoncSyntaxError('unterminated block comment');
37
+ i = close + 2;
38
+ }
39
+ else {
40
+ break;
41
+ }
42
+ }
43
+ return i;
44
+ }
45
+ function scanString(text, from) {
46
+ let i = from + 1;
47
+ while (i < text.length) {
48
+ const c = text[i];
49
+ if (c === '\\') {
50
+ i += 2;
51
+ }
52
+ else if (c === '"') {
53
+ const raw = text.slice(from, i + 1);
54
+ return { value: JSON.parse(raw), end: i + 1 };
55
+ }
56
+ else if (c === '\n') {
57
+ throw new JsoncSyntaxError('unterminated string');
58
+ }
59
+ else {
60
+ i++;
61
+ }
62
+ }
63
+ throw new JsoncSyntaxError('unterminated string');
64
+ }
65
+ function parseValue(text, from) {
66
+ const i = skipTrivia(text, from);
67
+ const c = text[i];
68
+ if (c === '{')
69
+ return parseObject(text, i);
70
+ if (c === '[')
71
+ return parseArray(text, i);
72
+ if (c === '"') {
73
+ const s = scanString(text, i);
74
+ return { kind: 'string', start: i, end: s.end, props: [] };
75
+ }
76
+ const m = /^(?:-?\d+(?:\.\d+)?(?:[eE][+-]?\d+)?|true|false|null)/.exec(text.slice(i, i + 64));
77
+ if (!m)
78
+ throw new JsoncSyntaxError(`unexpected token at ${i}`);
79
+ return { kind: m[0] === 'true' || m[0] === 'false' || m[0] === 'null' ? 'literal' : 'number', start: i, end: i + m[0].length, props: [] };
80
+ }
81
+ function parseArray(text, from) {
82
+ let i = from + 1;
83
+ for (;;) {
84
+ i = skipTrivia(text, i);
85
+ if (text[i] === ']')
86
+ return { kind: 'array', start: from, end: i + 1, props: [] };
87
+ const v = parseValue(text, i);
88
+ i = skipTrivia(text, v.end);
89
+ if (text[i] === ',')
90
+ i++;
91
+ else if (text[i] !== ']')
92
+ throw new JsoncSyntaxError(`expected , or ] at ${i}`);
93
+ }
94
+ }
95
+ function parseObject(text, from) {
96
+ const props = [];
97
+ let i = from + 1;
98
+ for (;;) {
99
+ i = skipTrivia(text, i);
100
+ if (text[i] === '}')
101
+ return { kind: 'object', start: from, end: i + 1, props };
102
+ if (text[i] !== '"')
103
+ throw new JsoncSyntaxError(`expected key at ${i}`);
104
+ const k = scanString(text, i);
105
+ let j = skipTrivia(text, k.end);
106
+ if (text[j] !== ':')
107
+ throw new JsoncSyntaxError(`expected : at ${j}`);
108
+ const value = parseValue(text, j + 1);
109
+ const prop = { key: k.value, start: i, end: value.end, value, commaAfter: -1 };
110
+ props.push(prop);
111
+ j = skipTrivia(text, value.end);
112
+ if (text[j] === ',') {
113
+ prop.commaAfter = j;
114
+ i = j + 1;
115
+ }
116
+ else if (text[j] === '}') {
117
+ i = j;
118
+ }
119
+ else {
120
+ throw new JsoncSyntaxError(`expected , or } at ${j}`);
121
+ }
122
+ }
123
+ }
124
+ function parseRoot(text) {
125
+ try {
126
+ const bom = text.charCodeAt(0) === 0xfeff ? 1 : 0;
127
+ const i = skipTrivia(text, bom);
128
+ if (text[i] !== '{')
129
+ return null;
130
+ const root = parseObject(text, i);
131
+ if (skipTrivia(text, root.end) !== text.length)
132
+ return null;
133
+ return root;
134
+ }
135
+ catch (e) {
136
+ if (e instanceof JsoncSyntaxError || e instanceof SyntaxError)
137
+ return null;
138
+ throw e;
139
+ }
140
+ }
141
+ /** True when `text` carries JSONC-only syntax (comments or a trailing comma) that a rewrite would lose. */
142
+ function hasJsoncFeatures(text) {
143
+ let i = text.charCodeAt(0) === 0xfeff ? 1 : 0;
144
+ while (i < text.length) {
145
+ const c = text[i];
146
+ if (c === '"') {
147
+ try {
148
+ i = scanString(text, i).end;
149
+ }
150
+ catch {
151
+ return false;
152
+ }
153
+ }
154
+ else if (c === '/' && (text[i + 1] === '/' || text[i + 1] === '*')) {
155
+ return true;
156
+ }
157
+ else if (c === ',') {
158
+ const j = skipTrivia(text, i + 1);
159
+ if (text[j] === '}' || text[j] === ']')
160
+ return true;
161
+ i++;
162
+ }
163
+ else {
164
+ i++;
165
+ }
166
+ }
167
+ return false;
168
+ }
169
+ // ─── Layout helpers ──────────────────────────────────────────────────────────
170
+ function lineStart(text, pos) {
171
+ const nl = text.lastIndexOf('\n', pos - 1);
172
+ return nl === -1 ? 0 : nl + 1;
173
+ }
174
+ function indentOf(text, pos) {
175
+ const ls = lineStart(text, pos);
176
+ const lead = text.slice(ls, pos);
177
+ return /^[ \t]*$/.test(lead) ? lead : null;
178
+ }
179
+ function detectUnit(text, root) {
180
+ const first = root.props[0];
181
+ const lead = first ? indentOf(text, first.start) : null;
182
+ if (lead && lead.length > 0)
183
+ return lead;
184
+ return ' ';
185
+ }
186
+ /** Indentation for children of `node`, given the indentation of the line that opens it. */
187
+ function childIndent(text, node, openerIndent, unit) {
188
+ const first = node.props[0];
189
+ const lead = first ? indentOf(text, first.start) : null;
190
+ return lead !== null && lead.length > 0 ? lead : openerIndent + unit;
191
+ }
192
+ function serialize(value, baseIndent, unit) {
193
+ // A short array of primitives stays on one line, the way people write command arrays by hand.
194
+ if (Array.isArray(value) && value.every((v) => v === null || ['string', 'number', 'boolean'].includes(typeof v))) {
195
+ const inline = `[${value.map((v) => JSON.stringify(v)).join(', ')}]`;
196
+ if (inline.length <= 100)
197
+ return inline;
198
+ }
199
+ return JSON.stringify(value, null, unit).split('\n').join('\n' + baseIndent);
200
+ }
201
+ function nest(path, value) {
202
+ let v = value;
203
+ for (let i = path.length - 1; i >= 0; i--)
204
+ v = { [path[i]]: v };
205
+ return v;
206
+ }
207
+ function endOfSameLineTrivia(text, from) {
208
+ let i = from;
209
+ for (;;) {
210
+ while (text[i] === ' ' || text[i] === '\t')
211
+ i++;
212
+ if (text[i] === '/' && text[i + 1] === '/') {
213
+ while (i < text.length && text[i] !== '\n')
214
+ i++;
215
+ return i;
216
+ }
217
+ if (text[i] === '/' && text[i + 1] === '*') {
218
+ const close = text.indexOf('*/', i + 2);
219
+ if (close === -1)
220
+ return i;
221
+ if (text.slice(i, close).includes('\n'))
222
+ return i;
223
+ i = close + 2;
224
+ continue;
225
+ }
226
+ return i;
227
+ }
228
+ }
229
+ function splice(text, start, end, insert) {
230
+ return text.slice(0, start) + insert + text.slice(end);
231
+ }
232
+ // ─── Operations ──────────────────────────────────────────────────────────────
233
+ function insertProperty(text, node, openerIndent, unit, key, value) {
234
+ const indent = childIndent(text, node, openerIndent, unit);
235
+ const rendered = `${JSON.stringify(key)}: ${serialize(value, indent, unit)}`;
236
+ if (node.props.length === 0) {
237
+ const interior = text.slice(node.start + 1, node.end - 1);
238
+ if (interior.trim() === '') {
239
+ return splice(text, node.start + 1, node.end - 1, `\n${indent}${rendered}\n${openerIndent}`);
240
+ }
241
+ // Only comments inside: keep them, add the property after the last one.
242
+ let at = node.end - 1;
243
+ while (at > node.start + 1 && /\s/.test(text[at - 1]))
244
+ at--;
245
+ return splice(text, at, at, `\n${indent}${rendered}`);
246
+ }
247
+ const last = node.props[node.props.length - 1];
248
+ if (last.commaAfter >= 0) {
249
+ // Trailing-comma style: keep it for the new property.
250
+ const at = endOfSameLineTrivia(text, last.commaAfter + 1);
251
+ return splice(text, at, at, `\n${indent}${rendered},`);
252
+ }
253
+ const at = endOfSameLineTrivia(text, last.end);
254
+ const withProp = splice(text, at, at, `\n${indent}${rendered}`);
255
+ return splice(withProp, last.end, last.end, ',');
256
+ }
257
+ function removeProperty(text, node, index) {
258
+ const prop = node.props[index];
259
+ let end = prop.commaAfter >= 0 ? prop.commaAfter + 1 : prop.end;
260
+ end = endOfSameLineTrivia(text, end);
261
+ if (text[end] === '\r')
262
+ end++;
263
+ if (text[end] === '\n')
264
+ end++;
265
+ const lead = indentOf(text, prop.start);
266
+ const start = lead !== null ? lineStart(text, prop.start) : prop.start;
267
+ if (lead === null) {
268
+ // Sharing a line with other content: do not swallow the line break.
269
+ end = prop.commaAfter >= 0 ? prop.commaAfter + 1 : prop.end;
270
+ while (text[end] === ' ')
271
+ end++;
272
+ }
273
+ let out = splice(text, start, end, '');
274
+ // The removed property was last with no trailing comma: drop the comma that preceded it.
275
+ if (prop.commaAfter < 0 && index > 0) {
276
+ const prev = node.props[index - 1];
277
+ if (prev.commaAfter >= 0 && prev.commaAfter < start) {
278
+ out = splice(out, prev.commaAfter, prev.commaAfter + 1, '');
279
+ }
280
+ }
281
+ return out;
282
+ }
283
+ function descend(root, path) {
284
+ const chain = [root];
285
+ let node = root;
286
+ for (let i = 0; i < path.length; i++) {
287
+ const prop = node.props.find((p) => p.key === path[i]);
288
+ if (!prop)
289
+ return { chain, hit: null, depth: i };
290
+ if (i === path.length - 1)
291
+ return { chain, hit: prop, depth: i };
292
+ if (prop.value.kind !== 'object')
293
+ return { chain, hit: prop, depth: i };
294
+ node = prop.value;
295
+ chain.push(node);
296
+ }
297
+ return { chain, hit: null, depth: path.length };
298
+ }
299
+ function openerIndentFor(text, chain, depth, unit) {
300
+ // Indentation of the line holding the opener of chain[depth]; the root opens at column 0.
301
+ if (depth === 0)
302
+ return '';
303
+ const node = chain[depth];
304
+ const lead = indentOf(text, node.start);
305
+ if (lead !== null)
306
+ return lead;
307
+ // `"key": {` on a line of its own: use the key's line indentation.
308
+ const parentProps = (chain[depth - 1]).props;
309
+ const owner = parentProps.find((p) => p.value === node);
310
+ const keyLead = owner ? indentOf(text, owner.start) : null;
311
+ return keyLead ?? unit.repeat(depth);
312
+ }
313
+ /**
314
+ * Set `path` to `value`, creating missing objects. Returns the edited text, or `null` when the
315
+ * document is not an object-rooted JSONC document.
316
+ */
317
+ function setJsoncValue(text, path, value) {
318
+ const root = parseRoot(text);
319
+ if (!root || path.length === 0)
320
+ return null;
321
+ const unit = detectUnit(text, root);
322
+ const { chain, hit, depth } = descend(root, path);
323
+ const atLeaf = hit !== null && depth === path.length - 1;
324
+ if (atLeaf && (hit).value.kind !== undefined) {
325
+ const prop = hit;
326
+ const base = indentOf(text, prop.start) ?? '';
327
+ return splice(text, prop.value.start, prop.value.end, serialize(value, base, unit));
328
+ }
329
+ if (hit !== null) {
330
+ // A non-object sits where an object is needed: replace it with the nested structure.
331
+ const base = indentOf(text, hit.start) ?? '';
332
+ return splice(text, hit.value.start, hit.value.end, serialize(nest(path.slice(depth + 1), value), base, unit));
333
+ }
334
+ const node = chain[chain.length - 1];
335
+ const parentDepth = chain.length - 1;
336
+ const opener = openerIndentFor(text, chain, parentDepth, unit);
337
+ const key = path[parentDepth];
338
+ return insertProperty(text, node, opener, unit, key, nest(path.slice(parentDepth + 1), value));
339
+ }
340
+ /**
341
+ * Remove the key at `path`, then remove any ancestor object (never the root) the removal emptied.
342
+ * Returns the edited text, or `null` when the document is not an object-rooted JSONC document.
343
+ * A path that does not exist leaves the text unchanged.
344
+ */
345
+ function removeJsoncValue(text, path) {
346
+ let root = parseRoot(text);
347
+ if (!root || path.length === 0)
348
+ return null;
349
+ let current = text;
350
+ let target = path;
351
+ for (;;) {
352
+ const { chain, hit, depth } = descend(root, target);
353
+ if (hit === null || depth !== target.length - 1)
354
+ return current;
355
+ const parent = chain[chain.length - 1];
356
+ current = removeProperty(current, parent, parent.props.indexOf(hit));
357
+ root = parseRoot(current);
358
+ if (!root || target.length === 1)
359
+ return current;
360
+ target = target.slice(0, -1);
361
+ const again = descend(root, target);
362
+ const holder = again.hit;
363
+ if (holder === null || again.depth !== target.length - 1 || holder.value.kind !== 'object' || holder.value.props.length > 0) {
364
+ return current;
365
+ }
366
+ }
367
+ }
368
+ /** Parse tolerantly for callers that only need to know whether the text is edit-able. */
369
+ function isEditableJsonc(text) {
370
+ return parseRoot(text) !== null;
371
+ }
372
+ /**
373
+ * Parse JSONC (JSON with comments and trailing commas) into a value. Throws a `SyntaxError` (from
374
+ * `JSON.parse`) on malformed input, like `JSON.parse` itself — callers keep their existing
375
+ * "could not parse, do not touch the file" handling.
376
+ */
377
+ function parseJsonc(content) {
378
+ let text = content.charCodeAt(0) === 0xfeff ? content.slice(1) : content;
379
+ let stripped = '';
380
+ let inString = false;
381
+ let i = 0;
382
+ while (i < text.length) {
383
+ const c = text[i];
384
+ const next = text[i + 1];
385
+ if (inString) {
386
+ stripped += c;
387
+ if (c === '\\' && i + 1 < text.length) {
388
+ stripped += next;
389
+ i += 2;
390
+ continue;
391
+ }
392
+ if (c === '"')
393
+ inString = false;
394
+ i++;
395
+ }
396
+ else if (c === '"') {
397
+ inString = true;
398
+ stripped += c;
399
+ i++;
400
+ }
401
+ else if (c === '/' && next === '/') {
402
+ while (i < text.length && text[i] !== '\n')
403
+ i++;
404
+ }
405
+ else if (c === '/' && next === '*') {
406
+ i += 2;
407
+ while (i < text.length - 1 && !(text[i] === '*' && text[i + 1] === '/'))
408
+ i++;
409
+ i += 2;
410
+ }
411
+ else {
412
+ stripped += c;
413
+ i++;
414
+ }
415
+ }
416
+ text = stripped.replace(/,(\s*[}\]])/g, '$1');
417
+ return JSON.parse(text);
418
+ }
@@ -55,7 +55,8 @@ const LOOP_HOST_CONTRACT = [
55
55
  ],
56
56
  "agentRoles": [
57
57
  "executor",
58
- "verifier"
58
+ "verifier",
59
+ "code-reviewer"
59
60
  ],
60
61
  "coreArtifacts": {
61
62
  "produces": [
@@ -234,7 +234,7 @@ function handleMessage(request, ctx = {}) {
234
234
  // subscribe/listChanged — nothing ever sends those notifications
235
235
  // (design row 2 / Hyrum's Law: advertising an unimplemented
236
236
  // notification is a lie a host would act on).
237
- capabilities: { tools: {}, resources: {}, prompts: {} },
237
+ capabilities: ctx.prompts === false ? { tools: {}, resources: {} } : { tools: {}, resources: {}, prompts: {} },
238
238
  serverInfo: { name: exports.SERVER_NAME, version: resolveServerVersion() },
239
239
  };
240
240
  break;
@@ -276,6 +276,8 @@ function handleMessage(request, ctx = {}) {
276
276
  break;
277
277
  }
278
278
  case 'prompts/list':
279
+ if (ctx.prompts === false)
280
+ return isNotification ? null : errorResponse(id, METHOD_NOT_FOUND, 'Method not found: prompts/list.');
279
281
  result = {
280
282
  prompts: [...getCatalog().prompts.values()]
281
283
  .map(wirePrompt)
@@ -283,6 +285,8 @@ function handleMessage(request, ctx = {}) {
283
285
  };
284
286
  break;
285
287
  case 'prompts/get': {
288
+ if (ctx.prompts === false)
289
+ return isNotification ? null : errorResponse(id, METHOD_NOT_FOUND, 'Method not found: prompts/get.');
286
290
  const params = (request.params && typeof request.params === 'object' ? request.params : {});
287
291
  const name = asString(params.name);
288
292
  if (!name)
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ /**
3
+ * The OpenCode MCP entry GSD's installer writes (`mcp.gsd`, or the 2.x native `mcp.servers.gsd`).
4
+ * One owner for "is this entry ours" so install, uninstall and migration 011 cannot drift apart
5
+ * (#5333, #5338; epic #5331).
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.GSD_MCP_NO_PROMPTS_FLAG = void 0;
9
+ exports.isGsdMcpEntry = isGsdMcpEntry;
10
+ exports.hasNoPromptsFlag = hasNoPromptsFlag;
11
+ exports.withNoPromptsFlag = withNoPromptsFlag;
12
+ const package_identity_cjs_1 = require("./package-identity.cjs");
13
+ /** Withholds the MCP prompt catalog: OpenCode turns MCP prompts into `<server>:<prompt>` slash commands. */
14
+ exports.GSD_MCP_NO_PROMPTS_FLAG = '--no-prompts';
15
+ /**
16
+ * True when `entry` is the MCP server entry the installer writes: a local `npx … gsd-mcp-server`
17
+ * command for this package, pinned or not. A user-edited or user-defined `gsd` entry does not match.
18
+ * Recognizes both generations' shapes (`enabled` on 1.x, `disabled` on 2.x) because it keys only on
19
+ * `type` and `command`.
20
+ */
21
+ function isGsdMcpEntry(entry) {
22
+ if (!entry || typeof entry !== 'object' || Array.isArray(entry))
23
+ return false;
24
+ const e = entry;
25
+ if (e.type !== 'local' || !Array.isArray(e.command))
26
+ return false;
27
+ const [bin, ...rest] = e.command;
28
+ if (bin !== 'npx' || !rest.includes('gsd-mcp-server'))
29
+ return false;
30
+ return rest.some((a) => typeof a === 'string' && (a === package_identity_cjs_1.PACKAGE_NAME || a.startsWith(`${package_identity_cjs_1.PACKAGE_NAME}@`)));
31
+ }
32
+ /** True when the entry already carries the no-prompts flag. */
33
+ function hasNoPromptsFlag(entry) {
34
+ return entry.command.includes(exports.GSD_MCP_NO_PROMPTS_FLAG);
35
+ }
36
+ /** The entry with `--no-prompts` appended to its command (other fields untouched). */
37
+ function withNoPromptsFlag(entry) {
38
+ return hasNoPromptsFlag(entry) ? entry : { ...entry, command: [...entry.command, exports.GSD_MCP_NO_PROMPTS_FLAG] };
39
+ }