@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.
- package/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -2
- package/.opencode/plugins/gsd-core.js +251 -1
- package/.opencode/plugins/gsd-statusline/tui.tsx +55 -0
- package/GEMINI.md +1 -1
- package/agents/gsd-advisor-researcher.compact.md +1 -1
- package/agents/gsd-advisor-researcher.md +1 -1
- package/agents/gsd-assumptions-analyzer.compact.md +1 -1
- package/agents/gsd-assumptions-analyzer.md +1 -1
- package/agents/gsd-code-fixer.compact.md +1 -1
- package/agents/gsd-code-fixer.md +1 -1
- package/agents/gsd-code-reviewer.compact.md +1 -1
- package/agents/gsd-code-reviewer.md +1 -1
- package/agents/gsd-codebase-mapper.compact.md +1 -1
- package/agents/gsd-codebase-mapper.md +1 -1
- package/agents/gsd-debug-session-manager.compact.md +6 -0
- package/agents/gsd-debug-session-manager.md +6 -0
- package/agents/gsd-debugger.md +1 -1
- package/agents/gsd-doc-verifier.compact.md +4 -4
- package/agents/gsd-doc-verifier.md +4 -3
- package/agents/gsd-doc-writer.compact.md +23 -5
- package/agents/gsd-doc-writer.md +19 -6
- package/agents/gsd-eval-auditor.compact.md +1 -1
- package/agents/gsd-eval-auditor.md +1 -1
- package/agents/gsd-executor.md +7 -3
- package/agents/gsd-integration-checker.compact.md +1 -1
- package/agents/gsd-integration-checker.md +1 -1
- package/agents/gsd-nyquist-auditor.compact.md +1 -1
- package/agents/gsd-nyquist-auditor.md +1 -1
- package/agents/gsd-phase-researcher.md +1 -1
- package/agents/gsd-plan-checker.md +2 -1
- package/agents/gsd-planner.md +5 -4
- package/agents/gsd-project-researcher.compact.md +1 -1
- package/agents/gsd-project-researcher.md +1 -1
- package/agents/gsd-research-synthesizer.compact.md +5 -5
- package/agents/gsd-research-synthesizer.md +5 -5
- package/agents/gsd-roadmapper.compact.md +1 -1
- package/agents/gsd-roadmapper.md +1 -1
- package/agents/gsd-security-auditor.compact.md +1 -1
- package/agents/gsd-security-auditor.md +1 -1
- package/agents/gsd-ui-auditor.compact.md +1 -1
- package/agents/gsd-ui-auditor.md +1 -1
- package/agents/gsd-ui-checker.compact.md +1 -1
- package/agents/gsd-ui-checker.md +1 -1
- package/agents/gsd-ui-researcher.compact.md +13 -1
- package/agents/gsd-ui-researcher.md +28 -1
- package/agents/gsd-verifier.md +1 -1
- package/bin/gsd-mcp-server.js +3 -1
- package/bin/install.js +398 -95
- package/commands/gsd/code-review.md +8 -0
- package/commands/gsd/pause-work.md +1 -1
- package/gsd-core/bin/gsd-tools.cjs +34 -12
- package/gsd-core/bin/lib/adr-parser.cjs +37 -5
- package/gsd-core/bin/lib/capability-ledger.cjs +25 -0
- package/gsd-core/bin/lib/capability-lock.cjs +29 -7
- package/gsd-core/bin/lib/capability-registry.cjs +138 -80
- package/gsd-core/bin/lib/capability-source.cjs +3 -1
- package/gsd-core/bin/lib/capability-state.cjs +6 -3
- package/gsd-core/bin/lib/capability-validator.cjs +8 -5
- package/gsd-core/bin/lib/capability-writer.cjs +2 -1
- package/gsd-core/bin/lib/commands.cjs +33 -9
- package/gsd-core/bin/lib/config-loader.cjs +3 -3
- package/gsd-core/bin/lib/decision-coverage-support.cjs +19 -0
- package/gsd-core/bin/lib/gate-context-drift.cjs +5 -4
- package/gsd-core/bin/lib/gate-decision-coverage-plan.cjs +15 -0
- package/gsd-core/bin/lib/gate-decision-coverage-verify.cjs +13 -0
- package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +23 -3
- package/gsd-core/bin/lib/gate-predicate.cjs +8 -12
- package/gsd-core/bin/lib/gate-tdd-review-checkpoint.cjs +60 -5
- package/gsd-core/bin/lib/gate-ui-safety.cjs +2 -1
- package/gsd-core/bin/lib/graphify.cjs +3 -2
- package/gsd-core/bin/lib/init.cjs +43 -16
- package/gsd-core/bin/lib/install-effort-resolver.cjs +2 -2
- package/gsd-core/bin/lib/install-engine.cjs +11 -0
- package/gsd-core/bin/lib/install-model-override-resolver.cjs +4 -3
- package/gsd-core/bin/lib/install-profiles.cjs +5 -3
- package/gsd-core/bin/lib/install-shadow-report.cjs +12 -0
- package/gsd-core/bin/lib/installer-migrations/011-opencode-mcp-no-prompts.cjs +99 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +54 -1
- package/gsd-core/bin/lib/jsonc-edit.cjs +418 -0
- package/gsd-core/bin/lib/loop-host-contract.cjs +2 -1
- package/gsd-core/bin/lib/mcp-server.cjs +5 -1
- package/gsd-core/bin/lib/opencode-mcp-entry.cjs +39 -0
- package/gsd-core/bin/lib/opencode-version.cjs +486 -0
- package/gsd-core/bin/lib/phase.cjs +141 -11
- package/gsd-core/bin/lib/quick-batch-dispatch.cjs +2 -1
- package/gsd-core/bin/lib/report-parser.cjs +57 -1
- package/gsd-core/bin/lib/review-lane-invocation.cjs +40 -0
- package/gsd-core/bin/lib/review-reviewer-selection.cjs +20 -1
- package/gsd-core/bin/lib/roadmap.cjs +56 -5
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +142 -6
- package/gsd-core/bin/lib/runtime-homes.cjs +18 -0
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +58 -3
- package/gsd-core/bin/lib/runtime-slash.cjs +13 -0
- package/gsd-core/bin/lib/shell-command-projection.cjs +2 -0
- package/gsd-core/bin/lib/smart-entry.cjs +100 -53
- package/gsd-core/bin/lib/state-document.cjs +37 -3
- package/gsd-core/bin/lib/state-transition.cjs +98 -19
- package/gsd-core/bin/lib/state.cjs +544 -126
- package/gsd-core/bin/lib/task-command-router.cjs +2 -3
- package/gsd-core/bin/lib/tdd-red-evidence.cjs +5 -0
- package/gsd-core/bin/lib/tdd-test-files.cjs +42 -0
- package/gsd-core/bin/lib/update-context.cjs +18 -1
- package/gsd-core/bin/lib/verification.cjs +107 -17
- package/gsd-core/bin/lib/verify.cjs +36 -1
- package/gsd-core/references/agent-contracts.md +1 -1
- package/gsd-core/references/agent-skills-bootstrap.md +26 -7
- package/gsd-core/references/git-integration.md +3 -3
- package/gsd-core/references/planner-reversibility.md +18 -4
- package/gsd-core/references/planner-verify-command-grounding.md +18 -0
- package/gsd-core/references/tdd.md +4 -3
- package/gsd-core/references/verifier-phase-gates.md +8 -1
- package/gsd-core/workflows/add-backlog.md +10 -2
- package/gsd-core/workflows/add-phase.md +5 -1
- package/gsd-core/workflows/add-tests.md +5 -1
- package/gsd-core/workflows/add-todo.md +5 -1
- package/gsd-core/workflows/ai-integration-phase.md +5 -1
- package/gsd-core/workflows/audit-milestone.md +21 -9
- package/gsd-core/workflows/autonomous/templates/minimal-context.md +41 -0
- package/gsd-core/workflows/autonomous.md +52 -64
- package/gsd-core/workflows/check-todos.md +5 -1
- package/gsd-core/workflows/code-review-fix.md +5 -1
- package/gsd-core/workflows/code-review.md +21 -2
- package/gsd-core/workflows/complete-milestone/detail/elaboration.md +5 -1
- package/gsd-core/workflows/complete-milestone.md +25 -5
- package/gsd-core/workflows/debug.md +5 -1
- package/gsd-core/workflows/diagnose-issues.md +5 -1
- package/gsd-core/workflows/discuss-phase/modes/chain.md +5 -1
- package/gsd-core/workflows/discuss-phase-assumptions.md +11 -3
- package/gsd-core/workflows/discuss-phase.md +5 -1
- package/gsd-core/workflows/docs-update/detail/elaboration.md +7 -6
- package/gsd-core/workflows/docs-update.md +27 -5
- package/gsd-core/workflows/edit-phase.md +5 -1
- package/gsd-core/workflows/eval-review.md +11 -3
- package/gsd-core/workflows/execute-phase/steps/codebase-drift-gate.md +5 -1
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +1 -1
- package/gsd-core/workflows/execute-phase/steps/post-merge-gate.md +12 -6
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +19 -2
- package/gsd-core/workflows/execute-phase/steps/regression-gate.md +18 -1
- package/gsd-core/workflows/execute-phase/steps/verify-phase-goal.md +5 -1
- package/gsd-core/workflows/execute-phase.md +40 -7
- package/gsd-core/workflows/execute-plan.md +8 -4
- package/gsd-core/workflows/extract-learnings.md +6 -2
- package/gsd-core/workflows/forensics.md +5 -1
- package/gsd-core/workflows/import.md +5 -1
- package/gsd-core/workflows/insert-phase.md +5 -1
- package/gsd-core/workflows/manager.md +10 -2
- package/gsd-core/workflows/map-codebase.md +5 -1
- package/gsd-core/workflows/milestone-summary.md +10 -2
- package/gsd-core/workflows/new-milestone.md +13 -5
- package/gsd-core/workflows/new-project.md +9 -5
- package/gsd-core/workflows/next.md +8 -4
- package/gsd-core/workflows/pause-work.md +3 -3
- package/gsd-core/workflows/plan-phase/steps/auto-advance-check.md +70 -0
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +5 -1
- package/gsd-core/workflows/plan-phase/steps/closed-phase-gate.md +6 -2
- package/gsd-core/workflows/plan-phase.md +41 -73
- package/gsd-core/workflows/plan-review-convergence.md +17 -5
- package/gsd-core/workflows/plant-seed.md +8 -4
- package/gsd-core/workflows/progress.md +6 -2
- package/gsd-core/workflows/quick-batch.md +5 -1
- package/gsd-core/workflows/quick.md +22 -7
- package/gsd-core/workflows/remove-phase.md +5 -1
- package/gsd-core/workflows/resume-project.md +19 -11
- package/gsd-core/workflows/review.md +6 -2
- package/gsd-core/workflows/scan.md +5 -1
- package/gsd-core/workflows/secure-phase.md +6 -2
- package/gsd-core/workflows/settings.md +3 -3
- package/gsd-core/workflows/ship.md +13 -10
- package/gsd-core/workflows/thread.md +10 -2
- package/gsd-core/workflows/transition.md +5 -1
- package/gsd-core/workflows/ui-phase.md +5 -1
- package/gsd-core/workflows/ui-review.md +6 -2
- package/gsd-core/workflows/ultraplan-phase.md +5 -1
- package/gsd-core/workflows/update.md +5 -1
- package/gsd-core/workflows/validate-phase.md +6 -2
- package/gsd-core/workflows/verify-work/detail/elaboration.md +1 -1
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +5 -1
- package/gsd-core/workflows/verify-work.md +11 -3
- package/hooks/dist/gsd-agent-isolation-guard.js +3 -1
- package/hooks/dist/gsd-check-update.js +26 -20
- package/hooks/dist/gsd-context-monitor.js +14 -5
- package/hooks/dist/gsd-cursor-subagent-start.js +2 -1
- package/hooks/dist/gsd-secret-read-guard.js +189 -15
- package/hooks/dist/gsd-statusline.js +321 -22
- package/hooks/dist/gsd-validate-commit.sh +27 -5
- package/hooks/gsd-agent-isolation-guard.js +3 -1
- package/hooks/gsd-check-update.js +26 -20
- package/hooks/gsd-context-monitor.js +14 -5
- package/hooks/gsd-cursor-subagent-start.js +2 -1
- package/hooks/gsd-secret-read-guard.js +189 -15
- package/hooks/gsd-statusline.js +321 -22
- package/hooks/gsd-validate-commit.sh +27 -5
- package/package.json +1 -1
- package/scripts/audit-workflow-script-paths.cjs +1 -1
- package/scripts/diagnose-validate-commit-in-tier.cjs +270 -0
- package/scripts/diagnose-validate-commit-phases.cjs +348 -0
- package/scripts/diagnose-windows-spawn-watcher.cjs +317 -0
- package/scripts/docs-guard-registry.cjs +2 -0
- package/scripts/gen-capability-registry.cjs +7 -1
- package/scripts/gen-loop-host-contract.cjs +4 -1
- package/scripts/lib/macos-conformance-tier.generated.cjs +13 -0
- package/scripts/lib/platform-conformance-tier.generated.cjs +26 -0
- package/scripts/lint-phase-arg-assignment.cjs +2 -2
- package/scripts/lint-workflow-shellcheck-baseline.json +16 -16
- package/scripts/mutation-matrix.cjs +3 -1
- package/scripts/release-tarball-smoke.cjs +15 -7
- package/scripts/xtrace-phase-timing.cjs +205 -0
- package/skills/gsd-code-review/SKILL.md +8 -0
- package/skills/gsd-pause-work/SKILL.md +1 -1
- package/vscode/package.json +1 -1
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "gsd-core",
|
|
3
3
|
"displayName": "GSD Core",
|
|
4
|
-
"version": "1.
|
|
4
|
+
"version": "1.18.0",
|
|
5
5
|
"description": "GSD Core is a meta-prompting, context engineering, and spec-driven development system for AI coding agents.",
|
|
6
6
|
"author": {
|
|
7
7
|
"name": "open-gsd",
|
|
@@ -18,6 +18,5 @@
|
|
|
18
18
|
"claude-code",
|
|
19
19
|
"gsd"
|
|
20
20
|
],
|
|
21
|
-
"commands": "./commands/gsd/",
|
|
22
21
|
"skills": "./skills/"
|
|
23
22
|
}
|
|
@@ -146,6 +146,11 @@ const TOOL_NAME_MAP = {
|
|
|
146
146
|
websearch: "WebSearch",
|
|
147
147
|
task: "Task",
|
|
148
148
|
subagent: "Task",
|
|
149
|
+
// >>> gsd:opencode-v2
|
|
150
|
+
// OpenCode 2.x tool ids (ADR-5331 E4): `shell` replaces `bash`, `patch` replaces `apply_patch`.
|
|
151
|
+
shell: "Bash",
|
|
152
|
+
patch: "MultiEdit",
|
|
153
|
+
// <<< gsd:opencode-v2
|
|
149
154
|
};
|
|
150
155
|
|
|
151
156
|
function mapToolName(tool) {
|
|
@@ -239,6 +244,18 @@ function gitProbingGuardTimeoutMs() {
|
|
|
239
244
|
return GIT_PROBING_GUARD_FALLBACK_TIMEOUT_MS;
|
|
240
245
|
}
|
|
241
246
|
|
|
247
|
+
// Hooks are Node scripts. `process.execPath` is only a JS runtime when the host is Node or Bun
|
|
248
|
+
// itself; inside OpenCode's single-file binary (1.x and 2.x) spawning it with a .js path boots
|
|
249
|
+
// OpenCode instead and exits 0, silently disabling every guard (#4849). So: an explicit
|
|
250
|
+
// GSD_HOOK_RUNTIME wins, then a Node/Bun host's own executable, else `node` from PATH.
|
|
251
|
+
let warnedMissingRuntime = false;
|
|
252
|
+
function hookRuntime() {
|
|
253
|
+
const fromEnv = process.env.GSD_HOOK_RUNTIME;
|
|
254
|
+
if (typeof fromEnv === "string" && fromEnv.length > 0) return fromEnv;
|
|
255
|
+
const base = path.basename(process.execPath).toLowerCase().replace(/\.exe$/, "");
|
|
256
|
+
return base === "node" || base === "bun" ? process.execPath : "node";
|
|
257
|
+
}
|
|
258
|
+
|
|
242
259
|
function runHook(hookFile, payload, opts = {}) {
|
|
243
260
|
const hookPath = path.join(HOOKS_DIR, hookFile);
|
|
244
261
|
if (!fs.existsSync(hookPath)) {
|
|
@@ -261,7 +278,7 @@ function runHook(hookFile, payload, opts = {}) {
|
|
|
261
278
|
(GIT_PROBING_GUARDS.has(hookFile) ? gitProbingGuardTimeoutMs() : 8000);
|
|
262
279
|
let result;
|
|
263
280
|
try {
|
|
264
|
-
result = spawnSync(
|
|
281
|
+
result = spawnSync(hookRuntime(), [hookPath], {
|
|
265
282
|
input: JSON.stringify(payload),
|
|
266
283
|
encoding: "utf8",
|
|
267
284
|
timeout,
|
|
@@ -273,6 +290,15 @@ function runHook(hookFile, payload, opts = {}) {
|
|
|
273
290
|
return { stdout: "", exitCode: 0, timedOut: false };
|
|
274
291
|
}
|
|
275
292
|
|
|
293
|
+
if (result.error && !warnedMissingRuntime) {
|
|
294
|
+
// The runtime could not be started: the guard did not run. Fail open (adapter contract) but
|
|
295
|
+
// never silently (#4849).
|
|
296
|
+
warnedMissingRuntime = true;
|
|
297
|
+
console.error(
|
|
298
|
+
`[gsd-core] cannot start hook runtime '${hookRuntime()}' (${result.error.code || result.error.message}) — ` +
|
|
299
|
+
"GSD hooks are NOT enforced. Install Node.js or set GSD_HOOK_RUNTIME to a node/bun executable.",
|
|
300
|
+
);
|
|
301
|
+
}
|
|
276
302
|
const stdout = (result.stdout || "").trim();
|
|
277
303
|
const exitCode = result.status == null ? 0 : result.status;
|
|
278
304
|
return { stdout, exitCode, timedOut: result.signal === "SIGTERM" };
|
|
@@ -358,9 +384,81 @@ function handleHookResult(hookResult, output) {
|
|
|
358
384
|
}
|
|
359
385
|
// Best-effort visibility when metadata isn't surfaced to the model
|
|
360
386
|
console.error(advisory);
|
|
387
|
+
// >>> gsd:opencode-parity
|
|
388
|
+
deliverAdvisory(advisory, output);
|
|
389
|
+
// <<< gsd:opencode-parity
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
// >>> gsd:opencode-parity (ADR-5331 Phase 3; OpenCode-only — the Kilo mirror omits this block)
|
|
393
|
+
// The four opt-in community hooks are bash scripts. Their gates (hooks.community, graphify.*) are
|
|
394
|
+
// re-checked here in-process so a project that has not opted in never pays a bash spawn per tool call.
|
|
395
|
+
function readProjectConfig(cwd) {
|
|
396
|
+
try {
|
|
397
|
+
return JSON.parse(fs.readFileSync(path.join(cwd, ".planning", "config.json"), "utf8"));
|
|
398
|
+
} catch {
|
|
399
|
+
return null;
|
|
361
400
|
}
|
|
362
401
|
}
|
|
363
402
|
|
|
403
|
+
function communityHooksEnabled(cwd) {
|
|
404
|
+
const config = readProjectConfig(cwd);
|
|
405
|
+
return !!config && config.hooks && config.hooks.community === true;
|
|
406
|
+
}
|
|
407
|
+
|
|
408
|
+
function graphifyAutoUpdateEnabled(cwd) {
|
|
409
|
+
const config = readProjectConfig(cwd);
|
|
410
|
+
return !!config && !process.env.CI && !!config.graphify &&
|
|
411
|
+
config.graphify.enabled === true && config.graphify.auto_update === true;
|
|
412
|
+
}
|
|
413
|
+
|
|
414
|
+
/**
|
|
415
|
+
* Run a GSD `.sh` hook under bash with the hook payload on stdin. Fails open: a missing script or no
|
|
416
|
+
* bash on PATH (a Windows host without Git Bash) is the same as the hook not being installed.
|
|
417
|
+
*/
|
|
418
|
+
function runShellHook(hookFile, payload, opts = {}) {
|
|
419
|
+
const hookPath = path.join(HOOKS_DIR, hookFile);
|
|
420
|
+
if (!fs.existsSync(hookPath)) return { stdout: "", exitCode: 0, timedOut: false };
|
|
421
|
+
let result;
|
|
422
|
+
try {
|
|
423
|
+
result = spawnSync(process.env.GSD_HOOK_SHELL || "bash", [hookPath], {
|
|
424
|
+
input: JSON.stringify(payload),
|
|
425
|
+
encoding: "utf8",
|
|
426
|
+
timeout: opts.timeout ?? 15000,
|
|
427
|
+
cwd: opts.cwd || currentCwd,
|
|
428
|
+
windowsHide: true,
|
|
429
|
+
});
|
|
430
|
+
} catch {
|
|
431
|
+
return { stdout: "", exitCode: 0, timedOut: false };
|
|
432
|
+
}
|
|
433
|
+
if (result.error) return { stdout: "", exitCode: 0, timedOut: false };
|
|
434
|
+
return {
|
|
435
|
+
stdout: (result.stdout || "").trim(),
|
|
436
|
+
exitCode: result.status == null ? 0 : result.status,
|
|
437
|
+
timedOut: result.signal === "SIGTERM",
|
|
438
|
+
};
|
|
439
|
+
}
|
|
440
|
+
|
|
441
|
+
// The SessionStart orientation text, produced once per session and handed to the model with its next request.
|
|
442
|
+
let pendingSessionContext = null;
|
|
443
|
+
|
|
444
|
+
// Advisories (read-before-edit, injection findings, phase-boundary, context warnings) were logged to stderr
|
|
445
|
+
// only — OpenCode does not show a plugin's stderr or metadata to the model. They are appended to the tool's
|
|
446
|
+
// own output instead, which is the one channel the model reads on both generations. An advisory raised before
|
|
447
|
+
// the tool runs waits here for that tool's result.
|
|
448
|
+
const pendingAdvisories = [];
|
|
449
|
+
function deliverAdvisory(text, output) {
|
|
450
|
+
if (output && typeof output.output === "string") {
|
|
451
|
+
output.output += "\n\n[GSD advisory] " + text;
|
|
452
|
+
return;
|
|
453
|
+
}
|
|
454
|
+
if (!pendingAdvisories.includes(text) && pendingAdvisories.length < 5) pendingAdvisories.push(text);
|
|
455
|
+
}
|
|
456
|
+
function flushPendingAdvisories(output) {
|
|
457
|
+
if (!output || typeof output.output !== "string") return;
|
|
458
|
+
while (pendingAdvisories.length > 0) output.output += "\n\n[GSD advisory] " + pendingAdvisories.shift();
|
|
459
|
+
}
|
|
460
|
+
// <<< gsd:opencode-parity
|
|
461
|
+
|
|
364
462
|
// ---------------------------------------------------------------------------
|
|
365
463
|
// Frontmatter helpers (for config registration)
|
|
366
464
|
// ---------------------------------------------------------------------------
|
|
@@ -667,6 +765,13 @@ const GsdCorePlugin = async ({ directory } = {}) => {
|
|
|
667
765
|
const r = runHook("gsd-secret-read-guard.js", prePayload());
|
|
668
766
|
handleHookResult(r, output);
|
|
669
767
|
}
|
|
768
|
+
// >>> gsd:opencode-parity
|
|
769
|
+
// 7. gsd-validate-commit.sh — Conventional Commits gate (opt-in: hooks.community)
|
|
770
|
+
if (claudeTool === "Bash" && communityHooksEnabled(cwd)) {
|
|
771
|
+
const r = runShellHook("gsd-validate-commit.sh", prePayload());
|
|
772
|
+
handleHookResult(r, output);
|
|
773
|
+
}
|
|
774
|
+
// <<< gsd:opencode-parity
|
|
670
775
|
},
|
|
671
776
|
|
|
672
777
|
// ── tool.execute.after — PostToolUse hooks ─────────────────────────
|
|
@@ -690,6 +795,25 @@ const GsdCorePlugin = async ({ directory } = {}) => {
|
|
|
690
795
|
: String(output.output);
|
|
691
796
|
output.output = rewriteContent(content);
|
|
692
797
|
}
|
|
798
|
+
// >>> gsd:opencode-parity
|
|
799
|
+
flushPendingAdvisories(output);
|
|
800
|
+
// gsd-phase-boundary.sh — reminder when .planning/ files change outside the workflow (opt-in)
|
|
801
|
+
// gsd-graphify-update.sh — rebuild the knowledge graph after a HEAD-advancing git op (opt-in)
|
|
802
|
+
{
|
|
803
|
+
const postPayload = {
|
|
804
|
+
hook_event_name: "PostToolUse",
|
|
805
|
+
tool_name: claudeTool,
|
|
806
|
+
tool_input: toolInput,
|
|
807
|
+
cwd,
|
|
808
|
+
};
|
|
809
|
+
if ((claudeTool === "Write" || claudeTool === "Edit") && communityHooksEnabled(cwd)) {
|
|
810
|
+
handleHookResult(runShellHook("gsd-phase-boundary.sh", postPayload), output);
|
|
811
|
+
}
|
|
812
|
+
if (claudeTool === "Bash" && graphifyAutoUpdateEnabled(cwd)) {
|
|
813
|
+
handleHookResult(runShellHook("gsd-graphify-update.sh", postPayload, { timeout: 8000 }), output);
|
|
814
|
+
}
|
|
815
|
+
}
|
|
816
|
+
// <<< gsd:opencode-parity
|
|
693
817
|
|
|
694
818
|
// gsd-read-injection-scanner.js — scan Read/WebFetch/WebSearch results
|
|
695
819
|
if (
|
|
@@ -727,6 +851,16 @@ const GsdCorePlugin = async ({ directory } = {}) => {
|
|
|
727
851
|
handleHookResult(r, output);
|
|
728
852
|
}
|
|
729
853
|
},
|
|
854
|
+
// >>> gsd:opencode-parity
|
|
855
|
+
// ── experimental.chat.system.transform — SessionStart orientation (gsd-session-state.sh) ──
|
|
856
|
+
"experimental.chat.system.transform": async (_input, output) => {
|
|
857
|
+
if (!pendingSessionContext) return;
|
|
858
|
+
const text = pendingSessionContext;
|
|
859
|
+
pendingSessionContext = null;
|
|
860
|
+
output.system = output.system || [];
|
|
861
|
+
output.system.push(text);
|
|
862
|
+
},
|
|
863
|
+
// <<< gsd:opencode-parity
|
|
730
864
|
|
|
731
865
|
// ── experimental.session.compacting — PreCompact ───────────────────
|
|
732
866
|
"experimental.session.compacting": async (_input, output) => {
|
|
@@ -770,6 +904,23 @@ const GsdCorePlugin = async ({ directory } = {}) => {
|
|
|
770
904
|
session_id: currentSessionId,
|
|
771
905
|
cwd: currentCwd,
|
|
772
906
|
});
|
|
907
|
+
// >>> gsd:opencode-parity
|
|
908
|
+
// gsd-session-state.sh — project state orientation for the session (opt-in: hooks.community)
|
|
909
|
+
if (communityHooksEnabled(currentCwd)) {
|
|
910
|
+
const r = runShellHook("gsd-session-state.sh", {
|
|
911
|
+
hook_event_name: "SessionStart",
|
|
912
|
+
session_id: currentSessionId,
|
|
913
|
+
cwd: currentCwd,
|
|
914
|
+
});
|
|
915
|
+
try {
|
|
916
|
+
const parsed = JSON.parse(r.stdout);
|
|
917
|
+
const text = parsed && parsed.hookSpecificOutput && parsed.hookSpecificOutput.additionalContext;
|
|
918
|
+
pendingSessionContext = typeof text === "string" && text ? text : null;
|
|
919
|
+
} catch {
|
|
920
|
+
pendingSessionContext = null;
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
// <<< gsd:opencode-parity
|
|
773
924
|
return;
|
|
774
925
|
}
|
|
775
926
|
|
|
@@ -864,7 +1015,106 @@ GsdCorePlugin._internals = {
|
|
|
864
1015
|
GsdCorePlugin,
|
|
865
1016
|
};
|
|
866
1017
|
|
|
1018
|
+
// >>> gsd:opencode-v2 (ADR-5331 D2; OpenCode 2.x entrypoint — the Kilo mirror omits this block)
|
|
1019
|
+
// OpenCode 2.x calls `setup(ctx)` with a `ctx` exposing `ctx.tool.hook(...)`; 1.x loads `server`
|
|
1020
|
+
// and never calls `setup` with such a ctx. `setup` therefore returns at once unless it is
|
|
1021
|
+
// handed a 2.x ctx. The 1.x hook handlers are reused through thin adapters so both generations
|
|
1022
|
+
// run the same guards.
|
|
1023
|
+
function v2ResultText(result) {
|
|
1024
|
+
const first = result && Array.isArray(result.content) ? result.content[0] : null;
|
|
1025
|
+
return first && typeof first.text === "string" ? first.text : null;
|
|
1026
|
+
}
|
|
1027
|
+
|
|
1028
|
+
async function setupV2(ctx) {
|
|
1029
|
+
if (!(ctx && ctx.tool && typeof ctx.tool.hook === "function")) return undefined;
|
|
1030
|
+
const hooks = await GsdCorePlugin({ directory: ctx.directory });
|
|
1031
|
+
// A thrown error in a 2.x before-hook blocks the tool (verified on 2.0.27), as in 1.x.
|
|
1032
|
+
await ctx.tool.hook("execute.before", async (e) => {
|
|
1033
|
+
await hooks["tool.execute.before"]({ tool: e.tool }, { args: e.input || {} });
|
|
1034
|
+
});
|
|
1035
|
+
await ctx.tool.hook("execute.after", async (e) => {
|
|
1036
|
+
if (e.status !== "completed") return;
|
|
1037
|
+
const text = v2ResultText(e.result);
|
|
1038
|
+
const out = { output: text, metadata: {} };
|
|
1039
|
+
await hooks["tool.execute.after"]({ tool: e.tool, args: e.input || {} }, out);
|
|
1040
|
+
if (text !== null && typeof out.output === "string" && out.output !== text) {
|
|
1041
|
+
e.result.content[0].text = out.output;
|
|
1042
|
+
}
|
|
1043
|
+
// 2.x emits no file.edited event (observed on 2.0.27), so derive it from the edit tools: the
|
|
1044
|
+
// only consumer is the .planning/config.json reload hook.
|
|
1045
|
+
const claudeTool = mapToolName(e.tool);
|
|
1046
|
+
if (claudeTool === "Write" || claudeTool === "Edit" || claudeTool === "MultiEdit") {
|
|
1047
|
+
const edited = mapToolInput(e.input || {}).file_path;
|
|
1048
|
+
if (typeof edited === "string" && edited.endsWith("config.json")) {
|
|
1049
|
+
await hooks.event({
|
|
1050
|
+
event: { type: "file.edited", properties: { file: edited, cwd: path.dirname(path.dirname(edited)) } },
|
|
1051
|
+
});
|
|
1052
|
+
}
|
|
1053
|
+
}
|
|
1054
|
+
});
|
|
1055
|
+
if (ctx.session && typeof ctx.session.hook === "function") {
|
|
1056
|
+
// 2.x compaction event: { sessionID, system: [{ type: "text", text }], messages, … }; items added
|
|
1057
|
+
// to `system` reach the summarizer (observed on 2.0.27).
|
|
1058
|
+
// gsd:opencode-parity: SessionStart orientation reaches the model through the 2.x context hook.
|
|
1059
|
+
await ctx.session.hook("context", async (e) => {
|
|
1060
|
+
if (!e || !Array.isArray(e.system)) return;
|
|
1061
|
+
const out = { system: [] };
|
|
1062
|
+
await hooks["experimental.chat.system.transform"]({}, out);
|
|
1063
|
+
for (const line of out.system) e.system.push({ type: "text", text: String(line) });
|
|
1064
|
+
});
|
|
1065
|
+
await ctx.session.hook("compaction", async (e) => {
|
|
1066
|
+
if (!e || !Array.isArray(e.system)) return;
|
|
1067
|
+
const out = { context: [] };
|
|
1068
|
+
await hooks["experimental.session.compacting"]({}, out);
|
|
1069
|
+
for (const line of out.context) e.system.push({ type: "text", text: String(line) });
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1072
|
+
if (ctx.shell && typeof ctx.shell.hook === "function") {
|
|
1073
|
+
await ctx.shell.hook("create.before", async (e) => {
|
|
1074
|
+
if (e && e.env && typeof e.env === "object") e.env.GSD_DIR = GSD_CORE;
|
|
1075
|
+
});
|
|
1076
|
+
}
|
|
1077
|
+
let stop = null;
|
|
1078
|
+
const seenSessions = new Set();
|
|
1079
|
+
if (ctx.event && typeof ctx.event.subscribe === "function") {
|
|
1080
|
+
const ac = new AbortController();
|
|
1081
|
+
stop = () => ac.abort();
|
|
1082
|
+
(async () => {
|
|
1083
|
+
for await (const ev of ctx.event.subscribe({ signal: ac.signal })) {
|
|
1084
|
+
// 2.0.27 shape (observed): { type, location: { directory }, data: { sessionID, … } }.
|
|
1085
|
+
const d = (ev && ev.data) || {};
|
|
1086
|
+
const dir = (ev && ev.location && ev.location.directory) || (d.location && d.location.directory);
|
|
1087
|
+
try {
|
|
1088
|
+
// A 2.x `run` never emits `session.created` (observed on 2.0.27: the first event of a session is
|
|
1089
|
+
// `session.inbox.enqueued`). The first event that names a session is its SessionStart.
|
|
1090
|
+
if (ev.type !== "session.created" && typeof d.sessionID === "string" && !seenSessions.has(d.sessionID)) {
|
|
1091
|
+
seenSessions.add(d.sessionID);
|
|
1092
|
+
await hooks.event({
|
|
1093
|
+
event: { type: "session.created", properties: { info: { id: d.sessionID, directory: dir } }, sessionID: d.sessionID },
|
|
1094
|
+
});
|
|
1095
|
+
} else if (ev.type === "session.created" && typeof d.sessionID === "string") {
|
|
1096
|
+
seenSessions.add(d.sessionID);
|
|
1097
|
+
}
|
|
1098
|
+
await hooks.event({
|
|
1099
|
+
event: {
|
|
1100
|
+
type: ev.type,
|
|
1101
|
+
properties: { info: { id: d.sessionID, directory: dir }, file: d.file, cwd: dir },
|
|
1102
|
+
sessionID: d.sessionID,
|
|
1103
|
+
},
|
|
1104
|
+
});
|
|
1105
|
+
} catch {
|
|
1106
|
+
// one bad event must not end the subscription
|
|
1107
|
+
}
|
|
1108
|
+
}
|
|
1109
|
+
})().catch(() => {});
|
|
1110
|
+
}
|
|
1111
|
+
return stop || undefined;
|
|
1112
|
+
}
|
|
1113
|
+
// <<< gsd:opencode-v2
|
|
867
1114
|
const gsdCorePluginExport = { server: GsdCorePlugin };
|
|
1115
|
+
// >>> gsd:opencode-v2
|
|
1116
|
+
Object.defineProperty(gsdCorePluginExport, "setup", { value: setupV2, enumerable: false });
|
|
1117
|
+
// <<< gsd:opencode-v2
|
|
868
1118
|
Object.defineProperty(gsdCorePluginExport, "id", {
|
|
869
1119
|
value: "gsd-core",
|
|
870
1120
|
enumerable: false,
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
// GSD statusline and update banner for the OpenCode 2.x terminal client (ADR-5331 Phase 3).
|
|
2
|
+
//
|
|
3
|
+
// A CLI plugin (https://opencode.ai/v2/docs/build/plugins/cli): OpenCode 2.x loads `tui.tsx` from a
|
|
4
|
+
// directory under plugins/ and renders the slots registered here. The line itself comes from GSD's own
|
|
5
|
+
// hooks/gsd-statusline.js — the script Claude Code runs — so state, progress, git and update-available
|
|
6
|
+
// output stay one implementation. OpenCode 1.x never loads this directory (it scans plugins/*.js|ts only).
|
|
7
|
+
import { Plugin } from "@opencode/plugin/tui"
|
|
8
|
+
import { execFile } from "node:child_process"
|
|
9
|
+
import { existsSync } from "node:fs"
|
|
10
|
+
import path from "node:path"
|
|
11
|
+
import { fileURLToPath } from "node:url"
|
|
12
|
+
|
|
13
|
+
const REFRESH_MS = 10_000
|
|
14
|
+
// Strip terminal colour codes: the host styles its own footer.
|
|
15
|
+
const ANSI = /\u001b\[[0-9;?]*[ -/]*[@-~]/g
|
|
16
|
+
|
|
17
|
+
function hooksDir() {
|
|
18
|
+
// <config>/plugins/gsd-statusline/tui.tsx → <config>/hooks
|
|
19
|
+
return path.resolve(path.dirname(fileURLToPath(import.meta.url)), "..", "..", "hooks")
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function runtime() {
|
|
23
|
+
return process.env.GSD_HOOK_RUNTIME || "node"
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
function render(directory: string, model: string, done: (line: string) => void) {
|
|
27
|
+
const script = path.join(hooksDir(), "gsd-statusline.js")
|
|
28
|
+
// Only inside a GSD project, and only when the script is installed.
|
|
29
|
+
if (!existsSync(script) || !existsSync(path.join(directory, ".planning"))) return done("")
|
|
30
|
+
const child = execFile(runtime(), [script], { cwd: directory, timeout: 4000, windowsHide: true }, (error, stdout) => {
|
|
31
|
+
done(error ? "" : String(stdout).replace(ANSI, "").split("\n")[0].trim())
|
|
32
|
+
})
|
|
33
|
+
child.stdin?.end(JSON.stringify({ model: { display_name: model }, workspace: { current_dir: directory } }))
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export default Plugin.define({
|
|
37
|
+
id: "gsd.statusline",
|
|
38
|
+
setup(context) {
|
|
39
|
+
const directory = context.location?.directory ?? context.data.location.default().directory
|
|
40
|
+
const [state, update] = context.storage.memory("line", { initial: { text: "" } })
|
|
41
|
+
const refresh = () => {
|
|
42
|
+
const selected = context.ui.model.current()
|
|
43
|
+
render(directory, selected?.modelID || "OpenCode", (text) => update((draft: { text: string }) => { draft.text = text }))
|
|
44
|
+
}
|
|
45
|
+
refresh()
|
|
46
|
+
const timer = setInterval(refresh, REFRESH_MS)
|
|
47
|
+
const stopHome = context.ui.slot({ append: "home.footer.status", render: () => <text>{state.text}</text> })
|
|
48
|
+
const stopPrompt = context.ui.slot({ append: "prompt.footer.status", render: () => <text>{state.text}</text> })
|
|
49
|
+
return () => {
|
|
50
|
+
clearInterval(timer)
|
|
51
|
+
stopHome?.()
|
|
52
|
+
stopPrompt?.()
|
|
53
|
+
}
|
|
54
|
+
},
|
|
55
|
+
})
|
package/GEMINI.md
CHANGED
|
@@ -25,7 +25,7 @@ files rather than in the conversation.
|
|
|
25
25
|
> run the dedicated installer:
|
|
26
26
|
>
|
|
27
27
|
> ```bash
|
|
28
|
-
> npx gsd-core --antigravity --global
|
|
28
|
+
> npx @opengsd/gsd-core@latest --antigravity --global
|
|
29
29
|
> ```
|
|
30
30
|
>
|
|
31
31
|
> The commands below are available only once that installer has run.
|
|
@@ -15,7 +15,7 @@ grounded in project context.
|
|
|
15
15
|
|
|
16
16
|
@~/.claude/gsd-core/references/untrusted-input-boundary.md
|
|
17
17
|
|
|
18
|
-
**agent_skills:** self-load per @~/.claude/gsd-core/references/agent-skills-bootstrap.md
|
|
18
|
+
**agent_skills:** self-load per @~/.claude/gsd-core/references/agent-skills-bootstrap.md (agent type: `gsd-advisor-researcher`)
|
|
19
19
|
|
|
20
20
|
<documentation_lookup>
|
|
21
21
|
@~/.claude/gsd-core/references/research-documentation-lookup.md
|
|
@@ -19,7 +19,7 @@ Spawned by `discuss-phase` via `Task()`. You do NOT present output directly to t
|
|
|
19
19
|
|
|
20
20
|
@~/.claude/gsd-core/references/untrusted-input-boundary.md
|
|
21
21
|
|
|
22
|
-
**agent_skills:** self-load per @~/.claude/gsd-core/references/agent-skills-bootstrap.md
|
|
22
|
+
**agent_skills:** self-load per @~/.claude/gsd-core/references/agent-skills-bootstrap.md (agent type: `gsd-advisor-researcher`)
|
|
23
23
|
|
|
24
24
|
<documentation_lookup>
|
|
25
25
|
@~/.claude/gsd-core/references/research-documentation-lookup.md
|
|
@@ -11,7 +11,7 @@ GSD assumptions analyzer. Deeply analyze the codebase for ONE phase; produce str
|
|
|
11
11
|
|
|
12
12
|
@~/.claude/gsd-core/references/untrusted-input-boundary.md
|
|
13
13
|
|
|
14
|
-
**agent_skills:** self-load per @~/.claude/gsd-core/references/agent-skills-bootstrap.md
|
|
14
|
+
**agent_skills:** self-load per @~/.claude/gsd-core/references/agent-skills-bootstrap.md (agent type: `gsd-assumptions-analyzer`)
|
|
15
15
|
|
|
16
16
|
<input>
|
|
17
17
|
Via prompt: `<phase>` (number/name), `<phase_goal>` (ROADMAP.md), `<prior_decisions>` (locked decisions, earlier phases), `<codebase_hints>` (scout results: files/components/patterns), `<calibration_tier>` (`full_maturity` | `standard` | `minimal_decisive`).
|
|
@@ -20,7 +20,7 @@ Spawned by `discuss-phase-assumptions` via `Task()`. You do NOT present output d
|
|
|
20
20
|
|
|
21
21
|
@~/.claude/gsd-core/references/untrusted-input-boundary.md
|
|
22
22
|
|
|
23
|
-
**agent_skills:** self-load per @~/.claude/gsd-core/references/agent-skills-bootstrap.md
|
|
23
|
+
**agent_skills:** self-load per @~/.claude/gsd-core/references/agent-skills-bootstrap.md (agent type: `gsd-assumptions-analyzer`)
|
|
24
24
|
|
|
25
25
|
<input>
|
|
26
26
|
Agent receives via prompt:
|
|
@@ -21,7 +21,7 @@ Job: read REVIEW.md findings, fix source code intelligently (not blind applicati
|
|
|
21
21
|
Before fixing code: **Project instructions** — read `./CLAUDE.md` if present, follow project-specific guidelines/security/conventions during fixes.
|
|
22
22
|
|
|
23
23
|
**Project skills:** check `.claude/skills/` or `.agents/skills/`.
|
|
24
|
-
**agent_skills:** self-load per @~/.claude/gsd-core/references/agent-skills-bootstrap.md
|
|
24
|
+
**agent_skills:** self-load per @~/.claude/gsd-core/references/agent-skills-bootstrap.md (agent type: `gsd-code-fixer`)
|
|
25
25
|
1. List available skills 2. Read `SKILL.md` for each (~130 lines) 3. Load specific `rules/*.md` as needed 4. Do NOT load full `AGENTS.md` (100KB+) 5. Follow skill rules relevant to your fix tasks.
|
|
26
26
|
</project_context>
|
|
27
27
|
|
package/agents/gsd-code-fixer.md
CHANGED
|
@@ -25,7 +25,7 @@ Before fixing code, discover project context:
|
|
|
25
25
|
|
|
26
26
|
**Project skills:** Check `.claude/skills/` or `.agents/skills/` directory if either exists:
|
|
27
27
|
|
|
28
|
-
**agent_skills:** self-load per @~/.claude/gsd-core/references/agent-skills-bootstrap.md
|
|
28
|
+
**agent_skills:** self-load per @~/.claude/gsd-core/references/agent-skills-bootstrap.md (agent type: `gsd-code-fixer`)
|
|
29
29
|
1. List available skills (subdirectories)
|
|
30
30
|
2. Read `SKILL.md` for each skill (lightweight index ~130 lines)
|
|
31
31
|
3. Load specific `rules/*.md` files as needed during implementation
|
|
@@ -38,7 +38,7 @@ Read `./CLAUDE.md` if present — follow project guidelines, security requiremen
|
|
|
38
38
|
|
|
39
39
|
**Project skills:** check `.claude/skills/` or `.agents/skills/`: list skill subdirectories, read each `SKILL.md` (lightweight index ~130 lines), load specific `rules/*.md` as needed. Do NOT load full `AGENTS.md` files (100KB+ context cost). Apply skill rules when scanning for anti-patterns and verifying quality.
|
|
40
40
|
|
|
41
|
-
**agent_skills:** self-load per @~/.claude/gsd-core/references/agent-skills-bootstrap.md
|
|
41
|
+
**agent_skills:** self-load per @~/.claude/gsd-core/references/agent-skills-bootstrap.md (agent type: `gsd-code-reviewer`)
|
|
42
42
|
</project_context>
|
|
43
43
|
|
|
44
44
|
<review_scope>
|
|
@@ -41,7 +41,7 @@ Before reviewing, discover project context:
|
|
|
41
41
|
|
|
42
42
|
**Project skills:** Check `.claude/skills/` or `.agents/skills/` directory if either exists:
|
|
43
43
|
|
|
44
|
-
**agent_skills:** self-load per @~/.claude/gsd-core/references/agent-skills-bootstrap.md
|
|
44
|
+
**agent_skills:** self-load per @~/.claude/gsd-core/references/agent-skills-bootstrap.md (agent type: `gsd-code-reviewer`)
|
|
45
45
|
1. List available skills (subdirectories)
|
|
46
46
|
2. Read `SKILL.md` for each skill (lightweight index ~130 lines)
|
|
47
47
|
3. Load specific `rules/*.md` files as needed during review
|
|
@@ -27,7 +27,7 @@ Explore thoroughly, then write document(s) directly. Return confirmation only.
|
|
|
27
27
|
|
|
28
28
|
**Project skills:** check `.claude/skills/` or `.agents/skills/` if either exists.
|
|
29
29
|
|
|
30
|
-
**agent_skills:** self-load per @~/.claude/gsd-core/references/agent-skills-bootstrap.md — list skill subdirs, read each `SKILL.md` (~130-line index), load `rules/*.md` as needed. NEVER load full `AGENTS.md` (100KB+ cost). Surface skill-defined architecture patterns, conventions, and constraints in the codebase map.
|
|
30
|
+
**agent_skills:** self-load per @~/.claude/gsd-core/references/agent-skills-bootstrap.md (agent type: `gsd-codebase-mapper`) — list skill subdirs, read each `SKILL.md` (~130-line index), load `rules/*.md` as needed. NEVER load full `AGENTS.md` (100KB+ cost). Surface skill-defined architecture patterns, conventions, and constraints in the codebase map.
|
|
31
31
|
|
|
32
32
|
<why_this_matters>
|
|
33
33
|
Downstream: `/gsd:plan-phase` loads docs by phase type (UI/frontend→CONVENTIONS+STRUCTURE; API/backend→ARCHITECTURE+CONVENTIONS; database/schema→ARCHITECTURE+STACK; testing→TESTING+CONVENTIONS; integration→INTEGRATIONS+STACK; refactor→CONCERNS+ARCHITECTURE; setup/config→STACK+STRUCTURE). `/gsd:execute-phase` uses them to follow conventions, place new files (STRUCTURE.md), match test patterns (TESTING.md), avoid adding debt (CONCERNS.md).
|
|
@@ -30,7 +30,7 @@ If the prompt contains a `<required_reading>` block, you MUST use the `Read` too
|
|
|
30
30
|
|
|
31
31
|
**Project skills:** Check `.claude/skills/` or `.agents/skills/` directory if either exists:
|
|
32
32
|
|
|
33
|
-
**agent_skills:** self-load per @~/.claude/gsd-core/references/agent-skills-bootstrap.md
|
|
33
|
+
**agent_skills:** self-load per @~/.claude/gsd-core/references/agent-skills-bootstrap.md (agent type: `gsd-codebase-mapper`)
|
|
34
34
|
1. List available skills (subdirectories)
|
|
35
35
|
2. Read `SKILL.md` for each skill (lightweight index ~130 lines)
|
|
36
36
|
3. Load specific `rules/*.md` files as needed during implementation
|
|
@@ -111,6 +111,12 @@ only "spawn continuation agent" without naming a format, so they inherit this ru
|
|
|
111
111
|
a flag written at each one — which is exactly why Step 2 must remain the only `Agent()` spawn
|
|
112
112
|
literal in this file.
|
|
113
113
|
|
|
114
|
+
**Never spawn the debugger through Bash — #5214.** The only way to start `gsd-debugger` is the Step 2
|
|
115
|
+
`Agent()` call above. A `Bash` command such as `claude --agent gsd-debugger …`, with or without
|
|
116
|
+
`run_in_background`, starts a detached process that returns no structured header, so the checkpoint
|
|
117
|
+
never advances. The Bash guard hook denies it (`debugger-cli-spawn`). If the `Agent` call itself fails,
|
|
118
|
+
stop and return that error to the orchestrator; do not improvise a shell spawn.
|
|
119
|
+
|
|
114
120
|
Resolve the debugger model before spawning (canonical `gsd_run` preamble — established once here, the single definition this agent carries):
|
|
115
121
|
```bash
|
|
116
122
|
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; _gsd_at() { for _p; do if [ -f "$_p" ]; then GSD_TOOLS="$_p"; return 0; fi; done; return 1; }; _gsd_id_ok() { case "$("$1" runtime-identity --raw 2>/dev/null || true)" in '{"packageName":"@opengsd/gsd-core"'*'}') return 0;; *) return 1;; esac; }; _gsd_homes() { set -- "${CLAUDE_CONFIG_DIR:-$HOME/.claude}" "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}" "$HOME/.gemini/antigravity-ide" "$HOME/.gemini/antigravity-cli" "${AUGMENT_CONFIG_DIR:-$HOME/.augment}" "${CLINE_CONFIG_DIR:-$HOME/.cline}" "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}" "${CODEX_HOME:-$HOME/.codex}" "${COPILOT_CONFIG_DIR:-${COPILOT_HOME:-$HOME/.copilot}}" "${CURSOR_CONFIG_DIR:-$HOME/.cursor}" "${HERMES_HOME:-$HOME/.hermes}" "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}" "${KIMI_CONFIG_DIR:-$HOME/.config/agents}" "$HOME/.agents" "${KIMI_CODE_HOME:-$HOME/.kimi-code}" "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}" "${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}" "${QWEN_CONFIG_DIR:-$HOME/.qwen}" "${TRAE_CONFIG_DIR:-$HOME/.trae}" "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}" "${ZCODE_CONFIG_DIR:-$HOME/.zcode}" "${GROK_AGENTS_HOME:-$HOME/.agents}"; for _h; do _gsd_at "$_h/gsd-core/bin/${_GSD_SHIM_NAME}" && return 0; done; return 1; }; if _gsd_at "${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}" "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif _gsd_homes; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif unset -f gsd_run; _G="$(command -v gsd_run)"; [ -n "$_G" ] && _gsd_id_ok "$_G"; then GSD_TOOLS="$_G"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and no identity-proving gsd_run is on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; GSD_IDENTITY_STATUS=unverified; _gsd_id_ok gsd_run && GSD_IDENTITY_STATUS=ok; export GSD_IDENTITY_STATUS; [ "$GSD_IDENTITY_STATUS" = ok ] || echo "WARNING: \"$GSD_TOOLS\" did not prove it is @opengsd/gsd-core - it is either a different package or an @opengsd/gsd-core older than the runtime-identity verb. See docs/how-to/diagnose-a-foreign-gsd-tools.md" >&2; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
|
|
@@ -117,6 +117,12 @@ only "spawn continuation agent" without naming a format, so they inherit this ru
|
|
|
117
117
|
a flag written at each one — which is exactly why Step 2 must remain the only `Agent()` spawn
|
|
118
118
|
literal in this file.
|
|
119
119
|
|
|
120
|
+
**Never spawn the debugger through Bash — #5214.** The only way to start `gsd-debugger` is the Step 2
|
|
121
|
+
`Agent()` call above. A `Bash` command such as `claude --agent gsd-debugger …`, with or without
|
|
122
|
+
`run_in_background`, starts a detached process that returns no structured header, so the checkpoint
|
|
123
|
+
never advances. The Bash guard hook denies it (`debugger-cli-spawn`). If the `Agent` call itself fails,
|
|
124
|
+
stop and return that error to the orchestrator; do not improvise a shell spawn.
|
|
125
|
+
|
|
120
126
|
Resolve the debugger model before spawning:
|
|
121
127
|
```bash
|
|
122
128
|
_GSD_SHIM_NAME="gsd-tools.cjs"; _GSD_RUNTIME_ROOT="${RUNTIME_DIR:-$(git rev-parse --show-toplevel 2>/dev/null || pwd)}"; GSD_TOOLS="${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}"; _gsd_at() { for _p; do if [ -f "$_p" ]; then GSD_TOOLS="$_p"; return 0; fi; done; return 1; }; _gsd_id_ok() { case "$("$1" runtime-identity --raw 2>/dev/null || true)" in '{"packageName":"@opengsd/gsd-core"'*'}') return 0;; *) return 1;; esac; }; _gsd_homes() { set -- "${CLAUDE_CONFIG_DIR:-$HOME/.claude}" "${ANTIGRAVITY_CONFIG_DIR:-$HOME/.gemini/antigravity}" "$HOME/.gemini/antigravity-ide" "$HOME/.gemini/antigravity-cli" "${AUGMENT_CONFIG_DIR:-$HOME/.augment}" "${CLINE_CONFIG_DIR:-$HOME/.cline}" "${CODEBUDDY_CONFIG_DIR:-$HOME/.codebuddy}" "${CODEX_HOME:-$HOME/.codex}" "${COPILOT_CONFIG_DIR:-${COPILOT_HOME:-$HOME/.copilot}}" "${CURSOR_CONFIG_DIR:-$HOME/.cursor}" "${HERMES_HOME:-$HOME/.hermes}" "${KILO_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/kilo}" "${KIMI_CONFIG_DIR:-$HOME/.config/agents}" "$HOME/.agents" "${KIMI_CODE_HOME:-$HOME/.kimi-code}" "${OPENCODE_CONFIG_DIR:-${XDG_CONFIG_HOME:-$HOME/.config}/opencode}" "${PI_CODING_AGENT_DIR:-$HOME/.pi/agent}" "${QWEN_CONFIG_DIR:-$HOME/.qwen}" "${TRAE_CONFIG_DIR:-$HOME/.trae}" "${WINDSURF_CONFIG_DIR:-$HOME/.codeium/windsurf}" "${ZCODE_CONFIG_DIR:-$HOME/.zcode}" "${GROK_AGENTS_HOME:-$HOME/.agents}"; for _h; do _gsd_at "$_h/gsd-core/bin/${_GSD_SHIM_NAME}" && return 0; done; return 1; }; if _gsd_at "${_GSD_RUNTIME_ROOT}/gsd-core/bin/${_GSD_SHIM_NAME}" "${_GSD_RUNTIME_ROOT}/.claude/gsd-core/bin/${_GSD_SHIM_NAME}" "${_GSD_RUNTIME_ROOT}/.codex/gsd-core/bin/${_GSD_SHIM_NAME}"; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif _gsd_homes; then gsd_run() { node "$GSD_TOOLS" "$@"; }; elif unset -f gsd_run; _G="$(command -v gsd_run)"; [ -n "$_G" ] && _gsd_id_ok "$_G"; then GSD_TOOLS="$_G"; gsd_run() { "$GSD_TOOLS" "$@"; }; else echo "ERROR: gsd-tools.cjs not found at $GSD_TOOLS and no identity-proving gsd_run is on PATH. Run: npx -y @opengsd/gsd-core@latest --claude --local" >&2; exit 1; fi; GSD_IDENTITY_STATUS=unverified; _gsd_id_ok gsd_run && GSD_IDENTITY_STATUS=ok; export GSD_IDENTITY_STATUS; [ "$GSD_IDENTITY_STATUS" = ok ] || echo "WARNING: \"$GSD_TOOLS\" did not prove it is @opengsd/gsd-core - it is either a different package or an @opengsd/gsd-core older than the runtime-identity verb. See docs/how-to/diagnose-a-foreign-gsd-tools.md" >&2; if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "${GSD_TOOLS:-}" ]; then printf "export PATH='%s':\"\$PATH\"\n" "${GSD_TOOLS%/*}" >> "$CLAUDE_ENV_FILE" 2>/dev/null || true; fi
|
package/agents/gsd-debugger.md
CHANGED
|
@@ -40,7 +40,7 @@ Your job: Find the root cause through hypothesis testing, maintain debug file st
|
|
|
40
40
|
- Load `rules/*.md` as needed during **investigation and fix**.
|
|
41
41
|
- Follow skill rules relevant to the bug being investigated and the fix being applied.
|
|
42
42
|
|
|
43
|
-
**agent_skills:** self-load per @~/.claude/gsd-core/references/agent-skills-bootstrap.md
|
|
43
|
+
**agent_skills:** self-load per @~/.claude/gsd-core/references/agent-skills-bootstrap.md (agent type: `gsd-debugger`)
|
|
44
44
|
|
|
45
45
|
<philosophy>
|
|
46
46
|
|
|
@@ -14,7 +14,7 @@ color: orange
|
|
|
14
14
|
<role>
|
|
15
15
|
A documentation file has been submitted for factual verification against the live codebase. Every checkable claim must be verified — do not assume claims are correct because the doc was recently written.
|
|
16
16
|
|
|
17
|
-
Spawned by the `/gsd:docs-update` workflow. Each spawn receives a `<verify_assignment>` XML block: `doc_path` (path to the doc file, relative to project_root) and `
|
|
17
|
+
Spawned by the `/gsd:docs-update` workflow. Each spawn receives a `<verify_assignment>` XML block: `doc_path` (path to the doc file, relative to project_root), `project_root` (absolute path) and `result_path` (absolute path of the JSON file to write, always under `project_root`, named by the doc's full relative path).
|
|
18
18
|
|
|
19
19
|
Extract checkable claims from the doc, verify each against the codebase using filesystem tools only, then write a structured JSON result file. Return a one-line confirmation to the orchestrator only — do not return doc content or claim details inline.
|
|
20
20
|
|
|
@@ -90,7 +90,7 @@ Follow in order:
|
|
|
90
90
|
|
|
91
91
|
**Step 5: Aggregate results.** Count `claims_checked` (total attempted, excludes skipped), `claims_passed`, `claims_failed`, and build `failures: [{ line, claim, expected, actual }]`.
|
|
92
92
|
|
|
93
|
-
**Step 6: Write result JSON.** Create
|
|
93
|
+
**Step 6: Write result JSON.** Create the parent directory of `result_path` if needed and write to exactly `result_path` (from the assignment — never a path you derive, never relative to your working directory), using the exact shape in `<output_format>`.
|
|
94
94
|
</verification_process>
|
|
95
95
|
|
|
96
96
|
<output_format>
|
|
@@ -115,7 +115,7 @@ Verification complete for {doc_path}: {claims_passed}/{claims_checked} claims pa
|
|
|
115
115
|
```
|
|
116
116
|
If `claims_failed > 0`, append:
|
|
117
117
|
```
|
|
118
|
-
{claims_failed} failure(s) written to
|
|
118
|
+
{claims_failed} failure(s) written to {result_path}
|
|
119
119
|
```
|
|
120
120
|
</output_format>
|
|
121
121
|
|
|
@@ -134,7 +134,7 @@ If `claims_failed > 0`, append:
|
|
|
134
134
|
- [ ] All five claim categories extracted line-by-line
|
|
135
135
|
- [ ] Skip rules applied during extraction
|
|
136
136
|
- [ ] Each claim verified using filesystem tools only
|
|
137
|
-
- [ ] Result JSON written to
|
|
137
|
+
- [ ] Result JSON written to the assigned `result_path`
|
|
138
138
|
- [ ] Confirmation returned to orchestrator
|
|
139
139
|
- [ ] `claims_failed` equals `failures.length`
|
|
140
140
|
- [ ] No modifications made to any doc file
|
|
@@ -17,6 +17,7 @@ A documentation file has been submitted for factual verification against the liv
|
|
|
17
17
|
Spawned by the `/gsd:docs-update` workflow. Each spawn receives a `<verify_assignment>` XML block containing:
|
|
18
18
|
- `doc_path`: path to the doc file to verify (relative to project_root)
|
|
19
19
|
- `project_root`: absolute path to project root
|
|
20
|
+
- `result_path`: absolute path of the JSON file to write (always under `project_root`, named by the doc's full relative path so same-named docs never collide)
|
|
20
21
|
|
|
21
22
|
Extract checkable claims from the doc, verify each against the codebase using filesystem tools only, then write a structured JSON result file. Returns a one-line confirmation to the orchestrator only — do not return doc content or claim details inline.
|
|
22
23
|
|
|
@@ -143,7 +144,7 @@ Count:
|
|
|
143
144
|
- `failures`: array of `{ line, claim, expected, actual }` objects for each failure
|
|
144
145
|
|
|
145
146
|
**Step 6: Write result JSON**
|
|
146
|
-
Create
|
|
147
|
+
Create the parent directory of `result_path` if it does not exist, then write the result to exactly `result_path` — do not derive a path of your own, and do not write relative to your working directory, which is not guaranteed to be `project_root`.
|
|
147
148
|
|
|
148
149
|
Use the exact JSON shape from `<output_format>`.
|
|
149
150
|
</verification_process>
|
|
@@ -190,7 +191,7 @@ Verification complete for {doc_path}: {claims_passed}/{claims_checked} claims pa
|
|
|
190
191
|
If `claims_failed > 0`, append:
|
|
191
192
|
|
|
192
193
|
```
|
|
193
|
-
{claims_failed} failure(s) written to
|
|
194
|
+
{claims_failed} failure(s) written to {result_path}
|
|
194
195
|
```
|
|
195
196
|
</output_format>
|
|
196
197
|
|
|
@@ -209,7 +210,7 @@ If `claims_failed > 0`, append:
|
|
|
209
210
|
- [ ] All five claim categories extracted line-by-line
|
|
210
211
|
- [ ] Skip rules applied during extraction
|
|
211
212
|
- [ ] Each claim verified using filesystem tools only
|
|
212
|
-
- [ ] Result JSON written to
|
|
213
|
+
- [ ] Result JSON written to the assigned `result_path`
|
|
213
214
|
- [ ] Confirmation returned to orchestrator
|
|
214
215
|
- [ ] `claims_failed` equals `failures.length`
|
|
215
216
|
- [ ] No modifications made to any doc file
|