@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
|
@@ -295,6 +295,61 @@ function buildKiloAgentPermissionBlock(claudeTools) {
|
|
|
295
295
|
}
|
|
296
296
|
return lines;
|
|
297
297
|
}
|
|
298
|
+
// OpenCode agent tool restrictions (#5338, ADR-5331 D4). Claude's `tools:` allowlist and
|
|
299
|
+
// `disallowedTools:` denylist become OpenCode `permission:` entries. The V1 `permission` form is the one
|
|
300
|
+
// written because OpenCode 2.x translates it automatically ("legacy agent frontmatter … is translated"),
|
|
301
|
+
// so one form serves both generations — verified: a `permission: {edit: deny, bash: deny}` agent drops the
|
|
302
|
+
// edit/write/shell tools from the model's tool list on 1.18.29 and 2.0.27. Only denials are emitted: an
|
|
303
|
+
// allowed tool keeps the user's own configured default instead of being forced to `allow`, and MCP tool
|
|
304
|
+
// entries are not restricted.
|
|
305
|
+
const claudeToOpencodePermission = {
|
|
306
|
+
Read: 'read',
|
|
307
|
+
Write: 'edit',
|
|
308
|
+
Edit: 'edit',
|
|
309
|
+
MultiEdit: 'edit',
|
|
310
|
+
NotebookEdit: 'edit',
|
|
311
|
+
Bash: 'bash',
|
|
312
|
+
Grep: 'grep',
|
|
313
|
+
Glob: 'glob',
|
|
314
|
+
WebFetch: 'webfetch',
|
|
315
|
+
WebSearch: 'websearch',
|
|
316
|
+
Task: 'task',
|
|
317
|
+
Agent: 'task',
|
|
318
|
+
Skill: 'skill',
|
|
319
|
+
SlashCommand: 'skill',
|
|
320
|
+
TodoWrite: 'todowrite',
|
|
321
|
+
};
|
|
322
|
+
const opencodeAgentPermissionOrder = ['read', 'edit', 'bash', 'grep', 'glob', 'webfetch', 'websearch', 'task', 'skill', 'todowrite'];
|
|
323
|
+
/**
|
|
324
|
+
* `permission:` frontmatter lines for an OpenCode agent, or `[]` when the agent declares no tool
|
|
325
|
+
* restriction (an agent with neither `tools` nor `disallowedTools` inherits everything, as in Claude).
|
|
326
|
+
*/
|
|
327
|
+
function buildOpencodeAgentPermissionBlock(allowedTools, disallowedTools) {
|
|
328
|
+
const allowed = new Set();
|
|
329
|
+
for (const tool of allowedTools) {
|
|
330
|
+
const mapped = claudeToOpencodePermission[tool];
|
|
331
|
+
if (mapped)
|
|
332
|
+
allowed.add(mapped);
|
|
333
|
+
}
|
|
334
|
+
const denied = new Set();
|
|
335
|
+
if (allowedTools.length > 0) {
|
|
336
|
+
for (const permission of opencodeAgentPermissionOrder) {
|
|
337
|
+
if (!allowed.has(permission))
|
|
338
|
+
denied.add(permission);
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
for (const tool of disallowedTools) {
|
|
342
|
+
const mapped = claudeToOpencodePermission[tool];
|
|
343
|
+
if (mapped)
|
|
344
|
+
denied.add(mapped);
|
|
345
|
+
}
|
|
346
|
+
const lines = [];
|
|
347
|
+
for (const permission of opencodeAgentPermissionOrder) {
|
|
348
|
+
if (denied.has(permission))
|
|
349
|
+
lines.push(` ${permission}: deny`);
|
|
350
|
+
}
|
|
351
|
+
return lines.length > 0 ? ['permission:', ...lines] : [];
|
|
352
|
+
}
|
|
298
353
|
function replaceRelativePathReference(content, fromPath, toPath) {
|
|
299
354
|
const escapedPath = (0, pattern_cjs_1.escapeRegex)(fromPath);
|
|
300
355
|
return content.replace(new RegExp(`(^|[^A-Za-z0-9_./-])${escapedPath}`, 'g'), (_, prefix) => `${prefix}${toPath}`);
|
|
@@ -1953,7 +2008,11 @@ function convertClaudeToOpencodeFrontmatter(content, { isAgent = false, modelOve
|
|
|
1953
2008
|
const newLines = [];
|
|
1954
2009
|
let inAllowedTools = false;
|
|
1955
2010
|
let inSkippedArray = false;
|
|
2011
|
+
let inAgentTools = false;
|
|
2012
|
+
let inDisallowedTools = false;
|
|
1956
2013
|
const allowedTools = [];
|
|
2014
|
+
const agentTools = [];
|
|
2015
|
+
const agentDisallowed = [];
|
|
1957
2016
|
for (const line of lines) {
|
|
1958
2017
|
const trimmed = line.trim();
|
|
1959
2018
|
// For agents: skip commented-out lines (e.g. hooks blocks)
|
|
@@ -1965,11 +2024,31 @@ function convertClaudeToOpencodeFrontmatter(content, { isAgent = false, modelOve
|
|
|
1965
2024
|
inAllowedTools = true;
|
|
1966
2025
|
continue;
|
|
1967
2026
|
}
|
|
2027
|
+
// Agents: collect the `tools:` allowlist and `disallowedTools:` denylist (block-list continuation
|
|
2028
|
+
// lines) so they can be rendered as `permission:` entries below.
|
|
2029
|
+
if (isAgent && (inAgentTools || inDisallowedTools)) {
|
|
2030
|
+
if (trimmed.startsWith('- ')) {
|
|
2031
|
+
const tool = decodeToolScalar(trimmed.substring(2));
|
|
2032
|
+
if (tool !== null)
|
|
2033
|
+
(inAgentTools ? agentTools : agentDisallowed).push(tool);
|
|
2034
|
+
continue;
|
|
2035
|
+
}
|
|
2036
|
+
if (trimmed && !trimmed.startsWith('-')) {
|
|
2037
|
+
inAgentTools = false;
|
|
2038
|
+
inDisallowedTools = false;
|
|
2039
|
+
}
|
|
2040
|
+
}
|
|
1968
2041
|
// Detect inline tools: field (comma-separated string)
|
|
1969
2042
|
if (trimmed.startsWith('tools:')) {
|
|
1970
2043
|
if (isAgent) {
|
|
1971
|
-
|
|
1972
|
-
|
|
2044
|
+
const toolsValue = trimmed.substring(6).trim();
|
|
2045
|
+
// A comment-only value (`tools: # note`) is not inline content: read the block list instead.
|
|
2046
|
+
if (toolsValue && !toolsValue.startsWith('#')) {
|
|
2047
|
+
agentTools.push(...splitToolScalars(toolsValue).map(decodeToolScalar).filter((tool) => tool !== null));
|
|
2048
|
+
}
|
|
2049
|
+
else {
|
|
2050
|
+
inAgentTools = true;
|
|
2051
|
+
}
|
|
1973
2052
|
continue;
|
|
1974
2053
|
}
|
|
1975
2054
|
const toolsValue = trimmed.substring(6).trim();
|
|
@@ -1980,8 +2059,18 @@ function convertClaudeToOpencodeFrontmatter(content, { isAgent = false, modelOve
|
|
|
1980
2059
|
}
|
|
1981
2060
|
continue;
|
|
1982
2061
|
}
|
|
1983
|
-
|
|
1984
|
-
|
|
2062
|
+
if (isAgent && trimmed.startsWith('disallowedTools:')) {
|
|
2063
|
+
const value = trimmed.substring(16).trim();
|
|
2064
|
+
if (value && !value.startsWith('#')) {
|
|
2065
|
+
agentDisallowed.push(...splitToolScalars(value).map(decodeToolScalar).filter((tool) => tool !== null));
|
|
2066
|
+
}
|
|
2067
|
+
else {
|
|
2068
|
+
inDisallowedTools = true;
|
|
2069
|
+
}
|
|
2070
|
+
continue;
|
|
2071
|
+
}
|
|
2072
|
+
// For agents: strip skills:, color:, memory:, maxTurns:, permissionMode:
|
|
2073
|
+
if (isAgent && /^(skills|color|memory|maxTurns|permissionMode):/.test(trimmed)) {
|
|
1985
2074
|
inSkippedArray = true;
|
|
1986
2075
|
continue;
|
|
1987
2076
|
}
|
|
@@ -2065,6 +2154,7 @@ function convertClaudeToOpencodeFrontmatter(content, { isAgent = false, modelOve
|
|
|
2065
2154
|
if (variant) {
|
|
2066
2155
|
newLines.push(frontmatterScalar('variant:', variant));
|
|
2067
2156
|
}
|
|
2157
|
+
newLines.push(...buildOpencodeAgentPermissionBlock(agentTools, agentDisallowed));
|
|
2068
2158
|
}
|
|
2069
2159
|
// For commands: add tools object if we had allowed-tools or tools
|
|
2070
2160
|
if (!isAgent && allowedTools.length > 0) {
|
|
@@ -2833,8 +2923,13 @@ function computePathPrefix({ isGlobal, isOpencode, isWindowsHost: _isWindowsHost
|
|
|
2833
2923
|
// See DEFECT.WINDOWS-PATH-LEAK-IN-MARKDOWN-CONTENT in CONTEXT.md.
|
|
2834
2924
|
const posixTarget = (0, shell_command_projection_cjs_1.posixNormalize)(String(resolvedTarget));
|
|
2835
2925
|
const posixHome = homeDir ? (0, shell_command_projection_cjs_1.posixNormalize)(String(homeDir)) : homeDir;
|
|
2836
|
-
|
|
2837
|
-
|
|
2926
|
+
// #5123: `startsWith` alone treats a SIBLING directory whose name begins with
|
|
2927
|
+
// the home path (`/home/u2/cfg` under `/home/u`) as under home and yields the
|
|
2928
|
+
// broken `$HOMEu2/cfg/`. Require a path boundary.
|
|
2929
|
+
const underHome = typeof posixHome === 'string' && posixHome.length > 0
|
|
2930
|
+
&& (posixTarget === posixHome || posixTarget.startsWith(posixHome.endsWith('/') ? posixHome : `${posixHome}/`));
|
|
2931
|
+
if (isGlobal && underHome && !isOpencode) {
|
|
2932
|
+
return '$HOME' + posixTarget.slice(posixHome.length).replace(/^(?!\/)(?=.)/, '/') + '/';
|
|
2838
2933
|
}
|
|
2839
2934
|
if (!isGlobal && projectRelative) {
|
|
2840
2935
|
// An explicit local config dir need not be the descriptor's conventional
|
|
@@ -2848,6 +2943,44 @@ function computePathPrefix({ isGlobal, isOpencode, isWindowsHost: _isWindowsHost
|
|
|
2848
2943
|
}
|
|
2849
2944
|
return `${posixTarget}/`;
|
|
2850
2945
|
}
|
|
2946
|
+
/**
|
|
2947
|
+
* #5123: retarget an installed artifact's `@` includes to the REAL install root.
|
|
2948
|
+
* Driven by the runtime descriptor's hostBehaviors, never by a runtime name:
|
|
2949
|
+
*
|
|
2950
|
+
* - `defaultHomeIncludeRoot` (Copilot, Antigravity): their converters hard-code
|
|
2951
|
+
* the DEFAULT home into includes, so with `--config-dir` the payload lives
|
|
2952
|
+
* elsewhere and `@~/<root>/gsd-core/…` points at nothing. `@~/<root>/` and
|
|
2953
|
+
* `@$HOME/<root>/` become `@<pathPrefix>`. A default install (pathPrefix is
|
|
2954
|
+
* already `$HOME/<root>/`) is returned unchanged, so it stays byte-identical.
|
|
2955
|
+
* - `retargetsGsdCoreProsePath` (Kimi): Kimi has no `@` include, so its prompt
|
|
2956
|
+
* converter turns `~/.claude/gsd-core` into the prose "GSD core", leaving
|
|
2957
|
+
* `@GSD core/x.md` pointing nowhere. A `GSD core/<path>` reference followed
|
|
2958
|
+
* by a path segment becomes the real `<pathPrefix>gsd-core/<path>`, with the
|
|
2959
|
+
* unsupported `@` dropped. Bare prose "GSD core" is not followed by `/`.
|
|
2960
|
+
*
|
|
2961
|
+
* `pathPrefix` is the install root as computePathPrefix derives it
|
|
2962
|
+
* (`$HOME/…/` under HOME, otherwise absolute), with a trailing slash. Pure and
|
|
2963
|
+
* idempotent: a second pass finds none of the literals it rewrites.
|
|
2964
|
+
*/
|
|
2965
|
+
function retargetInstalledIncludes(content, behaviors, pathPrefix) {
|
|
2966
|
+
if (typeof content !== 'string' || content.length === 0)
|
|
2967
|
+
return content;
|
|
2968
|
+
if (typeof pathPrefix !== 'string' || !pathPrefix.endsWith('/'))
|
|
2969
|
+
return content;
|
|
2970
|
+
if (!behaviors)
|
|
2971
|
+
return content;
|
|
2972
|
+
let result = content;
|
|
2973
|
+
if (behaviors.retargetsGsdCoreProsePath === true) {
|
|
2974
|
+
result = result.replace(/@?GSD core\/(?=[a-z])/g, `${pathPrefix}gsd-core/`);
|
|
2975
|
+
}
|
|
2976
|
+
const home = behaviors.defaultHomeIncludeRoot;
|
|
2977
|
+
if (typeof home === 'string' && home.length > 0 && pathPrefix !== `$HOME/${home}/`) {
|
|
2978
|
+
result = result
|
|
2979
|
+
.split(`@~/${home}/`).join(`@${pathPrefix}`)
|
|
2980
|
+
.split(`@$HOME/${home}/`).join(`@${pathPrefix}`);
|
|
2981
|
+
}
|
|
2982
|
+
return result;
|
|
2983
|
+
}
|
|
2851
2984
|
/** Return a safe project-relative prefix for a resolved install target. */
|
|
2852
2985
|
function projectRelativePrefixFromProjectRoot(projectRoot, resolvedTarget) {
|
|
2853
2986
|
if (typeof projectRoot !== 'string' || typeof resolvedTarget !== 'string')
|
|
@@ -3816,6 +3949,7 @@ module.exports = {
|
|
|
3816
3949
|
convertClaudeToOpencodeFrontmatter,
|
|
3817
3950
|
convertClaudeToKiloFrontmatter,
|
|
3818
3951
|
_decodeToolScalar: decodeToolScalar,
|
|
3952
|
+
_buildOpencodeAgentPermissionBlock: buildOpencodeAgentPermissionBlock,
|
|
3819
3953
|
_splitToolScalars: splitToolScalars,
|
|
3820
3954
|
readGsdCommandNames,
|
|
3821
3955
|
transformContentToHyphen,
|
|
@@ -3883,4 +4017,6 @@ module.exports = {
|
|
|
3883
4017
|
_negotiatedDispatchIsolation,
|
|
3884
4018
|
// #1521: canonical non-Claude runtime list for test files and tooling
|
|
3885
4019
|
NON_CLAUDE_RUNTIMES,
|
|
4020
|
+
// #5123: post-install include retarget (config-dir aware; Kimi prose path)
|
|
4021
|
+
retargetInstalledIncludes,
|
|
3886
4022
|
};
|
|
@@ -40,6 +40,7 @@ exports.resolveConfigHomeFromDescriptor = resolveConfigHomeFromDescriptor;
|
|
|
40
40
|
exports.resolveAntigravityGlobalDir = resolveAntigravityGlobalDir;
|
|
41
41
|
exports.detectAntigravityDirAmbiguity = detectAntigravityDirAmbiguity;
|
|
42
42
|
exports.resolveKimiGlobalDir = resolveKimiGlobalDir;
|
|
43
|
+
exports.resolveGsdHome = resolveGsdHome;
|
|
43
44
|
exports.resolveKimiHooksTomlDir = resolveKimiHooksTomlDir;
|
|
44
45
|
exports.getGlobalConfigDir = getGlobalConfigDir;
|
|
45
46
|
exports.resolveSkillsBaseFromDescriptor = resolveSkillsBaseFromDescriptor;
|
|
@@ -403,6 +404,23 @@ exports.NON_REGISTRY_CONFIG_HOME_DESCRIPTORS = [
|
|
|
403
404
|
* through resolveConfigHomeFromDescriptor and have no `kind`/`name` shape.
|
|
404
405
|
*/
|
|
405
406
|
exports.GSD_LOCATION_ENV_KEYS = ['GSD_HOME', 'GSD_AGENTS_DIR'];
|
|
407
|
+
/**
|
|
408
|
+
* #5017: the ONE rule for the root of the GSD-owned store (`$GSD_HOME/.gsd/`:
|
|
409
|
+
* defaults.json, consent.json, capability overlays) — `GSD_HOME` when set and
|
|
410
|
+
* non-blank, otherwise the home directory. The config loader, the install-time
|
|
411
|
+
* resolvers, the installer's defaults writer and capability staging all call
|
|
412
|
+
* this, so one configuration gives one answer at install time, at dispatch time
|
|
413
|
+
* and in the staged capabilities. The hooks (cold-tree, no lib import) and the
|
|
414
|
+
* settings / new-project workflows (shell) carry the same rule inline; a test
|
|
415
|
+
* pins each of them to it.
|
|
416
|
+
*
|
|
417
|
+
* `fallbackHome` lets a caller that already holds an injected home directory
|
|
418
|
+
* (the install-time resolvers' test seam) keep using it when GSD_HOME is unset.
|
|
419
|
+
* A blank `GSD_HOME` is "unset": `''` must never become a relative store path.
|
|
420
|
+
*/
|
|
421
|
+
function resolveGsdHome(fallbackHome = node_os_1.default.homedir()) {
|
|
422
|
+
return process.env['GSD_HOME'] || fallbackHome;
|
|
423
|
+
}
|
|
406
424
|
/**
|
|
407
425
|
* Resolve the directory holding the Kimi product's OWN native config.toml —
|
|
408
426
|
* the file that product itself reads for providers/models/hooks/etc, and the
|
|
@@ -992,7 +992,16 @@ function buildCodexHookWindowsShimIR(scriptAbsPath, absoluteRunnerToken) {
|
|
|
992
992
|
const scriptQuoted = JSON.stringify(targetAbs);
|
|
993
993
|
const cmdPath = scriptAbsPath.replace(/\.js$/, '.cmd');
|
|
994
994
|
const hookCommand = JSON.stringify(shellCmdProjection.posixNormalize(cmdPath));
|
|
995
|
-
|
|
995
|
+
// #4838: node is resolved when the hook fires. The install-time path dies when a
|
|
996
|
+
// version manager (WinGet) removes the versioned directory on upgrade, and a
|
|
997
|
+
// shim that only knew that path failed silently until the installer re-ran.
|
|
998
|
+
// Order: explicit GSD_NODE_PATH; the install-time path while it still exists
|
|
999
|
+
// (keeps the #3662 minimal-PATH guarantee); node.exe on PATH. `where $PATH:`
|
|
1000
|
+
// searches PATH only, never the current directory, so repo content cannot
|
|
1001
|
+
// become the runner. `%` is doubled so cmd.exe cannot expand a variable out
|
|
1002
|
+
// of an install path.
|
|
1003
|
+
const bakedBat = JSON.stringify(interpreter).slice(1, -1).replace(/%/g, '%%');
|
|
1004
|
+
const scriptBat = scriptQuoted.replace(/%/g, '%%');
|
|
996
1005
|
return {
|
|
997
1006
|
invocation: { interpreter, target: scriptAbsPath },
|
|
998
1007
|
cmdPath,
|
|
@@ -1000,7 +1009,16 @@ function buildCodexHookWindowsShimIR(scriptAbsPath, absoluteRunnerToken) {
|
|
|
1000
1009
|
eol: { cmd: '\r\n' },
|
|
1001
1010
|
passthroughArgs: true,
|
|
1002
1011
|
render: {
|
|
1003
|
-
cmd: () =>
|
|
1012
|
+
cmd: () => [
|
|
1013
|
+
'@ECHO OFF',
|
|
1014
|
+
'@SETLOCAL',
|
|
1015
|
+
'@SET "GSD_NODE="',
|
|
1016
|
+
'@IF DEFINED GSD_NODE_PATH IF EXIST "%GSD_NODE_PATH%" SET "GSD_NODE=%GSD_NODE_PATH%"',
|
|
1017
|
+
`@IF NOT DEFINED GSD_NODE IF EXIST "${bakedBat}" SET "GSD_NODE=${bakedBat}"`,
|
|
1018
|
+
'@IF NOT DEFINED GSD_NODE FOR /F "delims=" %%N IN (\'where $PATH:node.exe 2^>NUL\') DO @IF NOT DEFINED GSD_NODE SET "GSD_NODE=%%N"',
|
|
1019
|
+
`@"%GSD_NODE%" ${scriptBat} %*`,
|
|
1020
|
+
'',
|
|
1021
|
+
].join('\r\n'),
|
|
1004
1022
|
},
|
|
1005
1023
|
};
|
|
1006
1024
|
}
|
|
@@ -2279,6 +2297,35 @@ function pushHookGroup(entries, matcher, command, timeout) {
|
|
|
2279
2297
|
hook['timeout'] = timeout;
|
|
2280
2298
|
entries.push(matcher === undefined ? { hooks: [hook] } : { matcher, hooks: [hook] });
|
|
2281
2299
|
}
|
|
2300
|
+
/** #4840: the hook that reads the statusline's metrics bridge file. */
|
|
2301
|
+
const CONTEXT_MONITOR_HOOK = 'gsd-context-monitor.js';
|
|
2302
|
+
/**
|
|
2303
|
+
* #4840: remove GSD-managed registrations of `stem` from one event's groups,
|
|
2304
|
+
* leaving user-authored hooks (even in the same group) untouched. Returns
|
|
2305
|
+
* whether anything was removed. A group left with no hooks is dropped.
|
|
2306
|
+
*/
|
|
2307
|
+
function stripManagedHookRegistrations(entries, stem) {
|
|
2308
|
+
let removed = false;
|
|
2309
|
+
for (let i = entries.length - 1; i >= 0; i--) {
|
|
2310
|
+
const entry = entries[i];
|
|
2311
|
+
if (!entry || !Array.isArray(entry.hooks))
|
|
2312
|
+
continue;
|
|
2313
|
+
const kept = entry.hooks.filter((h) => {
|
|
2314
|
+
const managed = referencesHook(h, stem)
|
|
2315
|
+
&& isManagedHookCommand(h.command, { surface: 'settings-json', includeLegacyAliases: true });
|
|
2316
|
+
if (managed)
|
|
2317
|
+
removed = true;
|
|
2318
|
+
return !managed;
|
|
2319
|
+
});
|
|
2320
|
+
if (kept.length === entry.hooks.length)
|
|
2321
|
+
continue;
|
|
2322
|
+
if (kept.length === 0)
|
|
2323
|
+
entries.splice(i, 1);
|
|
2324
|
+
else
|
|
2325
|
+
entry.hooks = kept;
|
|
2326
|
+
}
|
|
2327
|
+
return removed;
|
|
2328
|
+
}
|
|
2282
2329
|
function applySettingsJsonHookTables(settings, opts, tables) {
|
|
2283
2330
|
const { runtime, postToolEvent, hookEvents, extendedHookEvents, hooksSurface, targetDir, hookOpts } = opts;
|
|
2284
2331
|
// ADR-857 phase 5f-3: extended hook events are driven by the registry
|
|
@@ -2326,6 +2373,12 @@ function applySettingsJsonHookTables(settings, opts, tables) {
|
|
|
2326
2373
|
settings.hooks[key] = [];
|
|
2327
2374
|
}
|
|
2328
2375
|
const entries = settings.hooks[key];
|
|
2376
|
+
if (row.file === CONTEXT_MONITOR_HOOK && opts.contextWarnings === false) {
|
|
2377
|
+
if (stripManagedHookRegistrations(entries, hookStem(row.file))) {
|
|
2378
|
+
console.log(` ${green}✓${reset} Removed stale context monitor hook (${_capabilityTitle(runtime)} has no context metrics writer, #4840)`);
|
|
2379
|
+
}
|
|
2380
|
+
continue;
|
|
2381
|
+
}
|
|
2329
2382
|
const command = resolveRowCommand(row, opts);
|
|
2330
2383
|
if ('build' in row.command && row.command.build === 'sh')
|
|
2331
2384
|
shellCommands[row.file] = command;
|
|
@@ -2468,7 +2521,8 @@ function buildKimiHooksTomlBlock(targetDir, opts) {
|
|
|
2468
2521
|
{ event: 'PreToolUse', command: cmd('gsd-workflow-guard.js'), matcher: 'Shell|WriteFile|StrReplaceFile', timeout: kimiBlockingGuardTimeout('gsd-workflow-guard.js') },
|
|
2469
2522
|
{ event: 'PreToolUse', command: cmd('gsd-validate-commit.sh'), matcher: 'Shell', timeout: kimiBlockingGuardTimeout('gsd-validate-commit.sh') },
|
|
2470
2523
|
// PostToolUse
|
|
2471
|
-
|
|
2524
|
+
// #4840: no metrics writer on kimi, so the warning registration is skipped (a block from an earlier install is replaced wholesale).
|
|
2525
|
+
{ event: 'PostToolUse', command: opts.contextWarnings === false ? null : cmd('gsd-context-monitor.js'), timeout: 10 },
|
|
2472
2526
|
{ event: 'PostToolUse', command: cmd('gsd-phase-boundary.sh'), matcher: 'WriteFile|StrReplaceFile', timeout: 5 },
|
|
2473
2527
|
{ event: 'PostToolUse', command: cmd('gsd-read-injection-scanner.js'), matcher: 'ReadFile', timeout: 5 },
|
|
2474
2528
|
{ event: 'PostToolUse', command: cmd('gsd-graphify-update.sh'), matcher: 'Shell', timeout: 5 },
|
|
@@ -2541,6 +2595,7 @@ function stripKimiHooksTomlBlock(content) {
|
|
|
2541
2595
|
function writeKimiHooksToml(configPath, targetDir, opts) {
|
|
2542
2596
|
const configuredEntrypoints = [];
|
|
2543
2597
|
const trackedOpts = {
|
|
2598
|
+
contextWarnings: opts.contextWarnings,
|
|
2544
2599
|
hookOpts: {
|
|
2545
2600
|
...opts.hookOpts,
|
|
2546
2601
|
configPath,
|
|
@@ -36,6 +36,7 @@ exports.resolveExplicitRuntime = resolveExplicitRuntime;
|
|
|
36
36
|
exports.resolveRuntime = resolveRuntime;
|
|
37
37
|
exports.resolveActiveRuntime = resolveActiveRuntime;
|
|
38
38
|
exports.formatGsdSlashFor = formatGsdSlashFor;
|
|
39
|
+
exports.runtimeCommand = runtimeCommand;
|
|
39
40
|
const node_fs_1 = __importDefault(require("node:fs"));
|
|
40
41
|
const node_path_1 = __importDefault(require("node:path"));
|
|
41
42
|
const runtime_name_policy_cjs_1 = require("./runtime-name-policy.cjs");
|
|
@@ -227,3 +228,15 @@ function resolveActiveRuntime(config, env = process.env) {
|
|
|
227
228
|
function formatGsdSlashFor(projectDir, commandName) {
|
|
228
229
|
return formatGsdSlash(commandName, resolveRuntime(projectDir));
|
|
229
230
|
}
|
|
231
|
+
/**
|
|
232
|
+
* #4859: the command as the project's runtime registers it, typed as a string —
|
|
233
|
+
* for runtime code that builds user-facing guidance. String literals in compiled
|
|
234
|
+
* runtime code are printed verbatim (the install-time conversion only rewrites
|
|
235
|
+
* markdown artifact bodies), so a hard-coded `/gsd:<cmd>` names a command no
|
|
236
|
+
* install route registers. Route every such reference through here:
|
|
237
|
+
* `runtimeCommand(cwd, 'plan-phase 3 --research')` → `/gsd-plan-phase 3 --research`
|
|
238
|
+
* (`$gsd-plan-phase …` on Codex). `commandName` is the bare name plus any argument tail.
|
|
239
|
+
*/
|
|
240
|
+
function runtimeCommand(projectDir, commandName) {
|
|
241
|
+
return String(formatGsdSlashFor(projectDir, commandName));
|
|
242
|
+
}
|
|
@@ -1131,6 +1131,8 @@ function _normalizeMd(content) {
|
|
|
1131
1131
|
}
|
|
1132
1132
|
function normalizeContent(filePath, content, opts = {}) {
|
|
1133
1133
|
const encoding = opts.encoding ?? 'utf-8';
|
|
1134
|
+
if (opts.verbatim === true)
|
|
1135
|
+
return { content: content ?? '', encoding };
|
|
1134
1136
|
const isMd = node_path_1.default.extname(filePath).toLowerCase() === '.md';
|
|
1135
1137
|
let normalized;
|
|
1136
1138
|
if (isMd) {
|