@opengsd/gsd-core 1.9.0 → 1.10.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 +2 -3
- package/.opencode/plugins/gsd-core.js +8 -1
- package/agents/gsd-code-fixer.md +131 -34
- package/agents/gsd-debugger.md +12 -246
- package/agents/gsd-executor.md +7 -5
- package/agents/gsd-integration-checker.md +3 -0
- package/agents/gsd-plan-checker.md +9 -0
- package/agents/gsd-planner.md +5 -8
- package/agents/gsd-roadmapper.md +21 -3
- package/agents/gsd-verifier.md +14 -70
- package/bin/install.js +503 -341
- package/commands/gsd/mempalace-capture.md +1 -1
- package/commands/gsd/new-milestone.md +1 -1
- package/commands/gsd/plan-phase.md +1 -1
- package/gsd-core/bin/gsd-tools.cjs +607 -63
- package/gsd-core/bin/lib/active-workstream-store.cjs +25 -0
- package/gsd-core/bin/lib/agent-install-check.cjs +38 -6
- package/gsd-core/bin/lib/api-coverage.cjs +120 -0
- package/gsd-core/bin/lib/audit.cjs +89 -1
- package/gsd-core/bin/lib/broken-windows.cjs +36 -6
- package/gsd-core/bin/lib/capability-registry.cjs +96 -110
- package/gsd-core/bin/lib/capability-validator.cjs +12 -2
- package/gsd-core/bin/lib/check-command-router.cjs +43 -1
- package/gsd-core/bin/lib/command-aliases.cjs +72 -0
- package/gsd-core/bin/lib/commands.cjs +26 -25
- package/gsd-core/bin/lib/commonjs-marker.cjs +136 -0
- package/gsd-core/bin/lib/config-loader.cjs +1 -0
- package/gsd-core/bin/lib/config.cjs +12 -1
- package/gsd-core/bin/lib/context-composer.cjs +278 -0
- package/gsd-core/bin/lib/context-predicates.cjs +506 -0
- package/gsd-core/bin/lib/core-utils.cjs +91 -12
- package/gsd-core/bin/lib/docs.cjs +3 -2
- package/gsd-core/bin/lib/external-job.cjs +19 -4
- package/gsd-core/bin/lib/frontmatter.cjs +84 -12
- package/gsd-core/bin/lib/gate-predicate-evaluator.cjs +57 -6
- package/gsd-core/bin/lib/git-base-branch.cjs +58 -15
- package/gsd-core/bin/lib/graphify.cjs +142 -27
- package/gsd-core/bin/lib/gsd2-import.cjs +27 -4
- package/gsd-core/bin/lib/host-integration.cjs +13 -1
- package/gsd-core/bin/lib/init-command-router.cjs +83 -8
- package/gsd-core/bin/lib/init.cjs +1021 -57
- package/gsd-core/bin/lib/install-engine.cjs +64 -10
- package/gsd-core/bin/lib/install-profiles.cjs +27 -1
- package/gsd-core/bin/lib/installer-migration-authoring.cjs +3 -1
- package/gsd-core/bin/lib/installer-migration-report.cjs +4 -0
- package/gsd-core/bin/lib/installer-migrations/007-retire-config-root-commonjs-marker.cjs +149 -0
- package/gsd-core/bin/lib/installer-migrations/008-cursor-retire-commands-surface.cjs +55 -0
- package/gsd-core/bin/lib/installer-migrations/009-pi-retire-reserved-hooks-dir.cjs +199 -0
- package/gsd-core/bin/lib/installer-migrations.cjs +87 -1
- package/gsd-core/bin/lib/io.cjs +28 -3
- package/gsd-core/bin/lib/markdown-sectionizer.cjs +6 -0
- package/gsd-core/bin/lib/mcp-catalog.cjs +518 -0
- package/gsd-core/bin/lib/mcp-server.cjs +135 -3
- package/gsd-core/bin/lib/milestone.cjs +106 -51
- package/gsd-core/bin/lib/phase-id.cjs +63 -0
- package/gsd-core/bin/lib/phase-locator.cjs +138 -45
- package/gsd-core/bin/lib/phase.cjs +260 -25
- package/gsd-core/bin/lib/plan-dependency-graph.cjs +232 -0
- package/gsd-core/bin/lib/planning-workspace.cjs +4 -0
- package/gsd-core/bin/lib/project-root.cjs +48 -0
- package/gsd-core/bin/lib/prompt-budget.cjs +128 -165
- package/gsd-core/bin/lib/retired-artifact-cleanup.cjs +80 -0
- package/gsd-core/bin/lib/review-lane-descriptor.cjs +99 -0
- package/gsd-core/bin/lib/review-lane-runner.cjs +30 -6
- package/gsd-core/bin/lib/roadmap-command-router.cjs +42 -9
- package/gsd-core/bin/lib/roadmap-parser.cjs +100 -18
- package/gsd-core/bin/lib/roadmap.cjs +37 -7
- package/gsd-core/bin/lib/runtime-artifact-conversion.cjs +195 -62
- package/gsd-core/bin/lib/runtime-artifact-layout.cjs +15 -3
- package/gsd-core/bin/lib/runtime-homes.cjs +154 -41
- package/gsd-core/bin/lib/runtime-hooks-surface.cjs +105 -41
- package/gsd-core/bin/lib/section-manifest.cjs +209 -0
- package/gsd-core/bin/lib/shell-command-projection.cjs +113 -27
- package/gsd-core/bin/lib/smart-entry.cjs +12 -0
- package/gsd-core/bin/lib/state-transition.cjs +73 -8
- package/gsd-core/bin/lib/state.cjs +151 -62
- package/gsd-core/bin/lib/surface.cjs +12 -1
- package/gsd-core/bin/lib/uat-predicate.cjs +11 -1
- package/gsd-core/bin/lib/uat.cjs +320 -21
- package/gsd-core/bin/lib/unusable-input.cjs +9 -0
- package/gsd-core/bin/lib/verification.cjs +29 -12
- package/gsd-core/bin/lib/verify.cjs +29 -5
- package/gsd-core/bin/lib/workflow-fragments.cjs +557 -0
- package/gsd-core/bin/lib/workstream-inventory-builder.cjs +181 -18
- package/gsd-core/bin/lib/workstream-inventory.cjs +519 -27
- package/gsd-core/bin/lib/workstream.cjs +6 -0
- package/gsd-core/bin/lib/worktree-base-ref.cjs +50 -6
- package/gsd-core/bin/lib/worktree-safety.cjs +276 -118
- package/gsd-core/bin/shared/config-schema.manifest.json +2 -0
- package/gsd-core/references/artifact-types.md +10 -3
- package/gsd-core/references/autonomous-ui-design-contract.md +42 -0
- package/gsd-core/references/debugger-techniques.md +255 -0
- package/gsd-core/references/research-documentation-lookup.md +5 -3
- package/gsd-core/references/specless-probe-fallback.md +7 -6
- package/gsd-core/references/verifier-wiring-patterns.md +100 -0
- package/gsd-core/references/worktree-branch-check.md +2 -2
- package/gsd-core/templates/summary-complex.md +2 -0
- package/gsd-core/templates/summary-minimal.md +2 -0
- package/gsd-core/templates/summary-standard.md +2 -0
- package/gsd-core/templates/summary.md +2 -0
- package/gsd-core/workflows/audit-milestone.md +3 -0
- package/gsd-core/workflows/autonomous/steps/converge-banner.md +1 -0
- package/gsd-core/workflows/autonomous/steps/converge-dispatch-bg.md +11 -0
- package/gsd-core/workflows/autonomous/steps/converge-dispatch-inline.md +7 -0
- package/gsd-core/workflows/autonomous/steps/converge-fail-fast.md +21 -0
- package/gsd-core/workflows/autonomous/steps/converge-loop.md +7 -0
- package/gsd-core/workflows/autonomous.md +32 -69
- package/gsd-core/workflows/code-review/steps/dispatch-fix.md +39 -0
- package/gsd-core/workflows/code-review/steps/structural-pre-pass.md +83 -0
- package/gsd-core/workflows/code-review.md +42 -145
- package/gsd-core/workflows/complete-milestone/steps/git-tag.md +29 -0
- package/gsd-core/workflows/complete-milestone.md +23 -81
- package/gsd-core/workflows/debug.md +9 -12
- package/gsd-core/workflows/diagnose-issues.md +22 -0
- package/gsd-core/workflows/discovery-phase.md +4 -4
- package/gsd-core/workflows/discuss-phase-assumptions/steps/auto-advance-dispatch.md +15 -0
- package/gsd-core/workflows/discuss-phase-assumptions.md +5 -16
- package/gsd-core/workflows/docs-update/steps/dispatch-monorepo-packages.md +51 -0
- package/gsd-core/workflows/docs-update.md +8 -51
- package/gsd-core/workflows/execute-phase/steps/executor-isolation-dispatch.md +34 -2
- package/gsd-core/workflows/execute-phase/steps/gap-closure-artifacts.md +50 -0
- package/gsd-core/workflows/execute-phase/steps/partial-wave.md +31 -0
- package/gsd-core/workflows/execute-phase/steps/per-plan-worktree-gate.md +19 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate-run.md +42 -0
- package/gsd-core/workflows/execute-phase/steps/regression-gate.md +43 -37
- package/gsd-core/workflows/execute-phase.md +65 -137
- package/gsd-core/workflows/execute-plan.md +1 -1
- package/gsd-core/workflows/help/modes/full.md +6 -1
- package/gsd-core/workflows/ingest-docs.md +2 -1
- package/gsd-core/workflows/new-milestone/steps/project-md-milestone-write.md +16 -0
- package/gsd-core/workflows/new-milestone/steps/reset-phase-safety.md +19 -0
- package/gsd-core/workflows/new-milestone.md +21 -38
- package/gsd-core/workflows/new-project/steps/auto-mode-config.md +176 -0
- package/gsd-core/workflows/new-project/steps/auto-mode-detection.md +32 -0
- package/gsd-core/workflows/new-project/steps/codebase-map-offer.md +18 -0
- package/gsd-core/workflows/new-project.md +13 -226
- package/gsd-core/workflows/plan-phase/steps/adr-ingest-express-path.md +15 -0
- package/gsd-core/workflows/plan-phase/steps/chunked-planning-mode.md +110 -0
- package/gsd-core/workflows/plan-phase/steps/prd-express-gate.md +8 -0
- package/gsd-core/workflows/plan-phase/steps/research-only-early-exit.md +17 -0
- package/gsd-core/workflows/plan-phase/steps/research-only-modifiers.md +16 -0
- package/gsd-core/workflows/plan-phase/steps/reviews-prerequisite.md +17 -0
- package/gsd-core/workflows/plan-phase/steps/stall-detection-helpers.md +149 -0
- package/gsd-core/workflows/plan-phase.md +49 -193
- package/gsd-core/workflows/progress/steps/forensic-audit.md +125 -0
- package/gsd-core/workflows/progress/steps/mvp-display.md +18 -0
- package/gsd-core/workflows/progress.md +11 -153
- package/gsd-core/workflows/quick/steps/discussion-phase.md +124 -0
- package/gsd-core/workflows/quick/steps/plan-checker-loop.md +111 -0
- package/gsd-core/workflows/quick/steps/quick-verification.md +46 -0
- package/gsd-core/workflows/quick/steps/research-phase.md +72 -0
- package/gsd-core/workflows/quick/steps/worktree-pre-dispatch-commit.md +37 -0
- package/gsd-core/workflows/quick.md +20 -390
- package/gsd-core/workflows/resume-project.md +3 -0
- package/gsd-core/workflows/review/steps/reviewer-instances-note-1.md +4 -0
- package/gsd-core/workflows/review/steps/reviewer-instances-note-2.md +3 -0
- package/gsd-core/workflows/review.md +15 -8
- package/gsd-core/workflows/section-manifest.json +219 -0
- package/gsd-core/workflows/sketch.md +1 -1
- package/gsd-core/workflows/spec-phase.md +17 -14
- package/gsd-core/workflows/spike-wrap-up.md +20 -5
- package/gsd-core/workflows/spike.md +50 -16
- package/gsd-core/workflows/sync-skills.md +49 -11
- package/gsd-core/workflows/transition/steps/workstream-collision-check.md +17 -0
- package/gsd-core/workflows/transition.md +8 -21
- package/gsd-core/workflows/ui-phase.md +8 -7
- package/gsd-core/workflows/update/steps/channel-banner.md +7 -0
- package/gsd-core/workflows/update.md +18 -7
- package/gsd-core/workflows/verify-phase.md +4 -7
- package/gsd-core/workflows/verify-work/steps/automated-ui-verification.md +36 -0
- package/gsd-core/workflows/verify-work/steps/mvp-uat-framing.md +21 -0
- package/gsd-core/workflows/verify-work.md +8 -58
- package/hooks/dist/gsd-agent-isolation-guard.js +428 -0
- package/hooks/dist/gsd-check-update-worker.js +14 -5
- package/hooks/dist/gsd-cursor-subagent-start.js +532 -26
- package/hooks/dist/gsd-read-injection-scanner.js +7 -0
- package/hooks/dist/gsd-statusline.js +72 -6
- package/hooks/dist/gsd-worktree-path-guard.js +2 -1
- package/hooks/dist/gsd-write-guard.js +359 -0
- package/hooks/dist/lib/isolation-sentinel.js +268 -0
- package/hooks/dist/managed-hooks-registry.cjs +2 -0
- package/hooks/gsd-agent-isolation-guard.js +428 -0
- package/hooks/gsd-check-update-worker.js +14 -5
- package/hooks/gsd-cursor-subagent-start.js +532 -26
- package/hooks/gsd-read-injection-scanner.js +7 -0
- package/hooks/gsd-statusline.js +72 -6
- package/hooks/gsd-worktree-path-guard.js +2 -1
- package/hooks/gsd-write-guard.js +359 -0
- package/hooks/hooks.json +12 -0
- package/hooks/lib/isolation-sentinel.js +268 -0
- package/hooks/managed-hooks-registry.cjs +2 -0
- package/package.json +14 -5
- package/pi/gsd.cjs +57 -12
- package/scripts/build-hooks.js +9 -0
- package/scripts/changeset/lint.cjs +9 -2
- package/scripts/changeset/serialize.cjs +5 -1
- package/scripts/gen-capability-matrix.cjs +1 -1
- package/scripts/gen-context-index.cjs +448 -0
- package/scripts/gen-inventory-manifest.cjs +101 -1
- package/scripts/gen-prompt-budget-parity-corpus.cjs +645 -0
- package/scripts/gen-registry.cjs +39 -15
- package/scripts/gen-section-manifest.cjs +638 -0
- package/scripts/generate-package-identity.cjs +4 -2
- package/scripts/lint-allow-test-rule-refs.allowlist.json +17 -31
- package/scripts/lint-compiled-artifact-sync.cjs +6 -1
- package/scripts/lint-docs-command-form.cjs +195 -0
- package/scripts/lint-docs-required.cjs +9 -1
- package/scripts/lint-emitted-drift-ack.cjs +215 -20
- package/scripts/lint-example-parser-parity.cjs +395 -0
- package/scripts/lint-test-file-count.allowlist.json +27 -1
- package/scripts/mutation-matrix.cjs +13 -0
- package/scripts/prompt-injection-scan.sh +27 -6
- package/scripts/registry-schema.cjs +323 -94
- package/scripts/run-tests.cjs +3 -2
- package/scripts/validate-registry.cjs +10 -6
- package/skills/gsd-autonomous/SKILL.md +1 -1
- package/skills/gsd-execute-phase/SKILL.md +1 -1
- package/skills/gsd-mempalace-capture/SKILL.md +1 -1
- package/skills/gsd-new-milestone/SKILL.md +1 -1
- package/skills/gsd-plan-phase/SKILL.md +2 -2
- package/vscode/package.json +1 -1
- package/scripts/gen-emitted-baseline.cjs +0 -145
|
@@ -31,11 +31,14 @@ const shell_command_projection_cjs_1 = require("./shell-command-projection.cjs")
|
|
|
31
31
|
// `require('../../../package.json')`. That require ran at module load on every
|
|
32
32
|
// gsd-tools invocation (this module sits in the gsd-tools loader chain) and
|
|
33
33
|
// threw `Cannot find module '../../../package.json'` on runtimes whose root has
|
|
34
|
-
// no package.json —
|
|
35
|
-
// package.json
|
|
36
|
-
//
|
|
37
|
-
//
|
|
38
|
-
//
|
|
34
|
+
// no package.json — originally just Codex, where the installer never wrote the
|
|
35
|
+
// synthetic root package.json; since #2544 that is true of EVERY runtime, as
|
|
36
|
+
// GSD's markers moved into `hooks/` and the native plugin dir and the config
|
|
37
|
+
// root is no longer written at all — taking the entire CLI down before it did
|
|
38
|
+
// anything. And even where it used to resolve (the synthetic
|
|
39
|
+
// `{"type":"commonjs"}`), there is no `version` field, so the single consumer
|
|
40
|
+
// below already emitted `version: undefined`. Resolve lazily and defensively
|
|
41
|
+
// instead:
|
|
39
42
|
// 1. Installed trees carry <root>/gsd-core/VERSION (written by the installer);
|
|
40
43
|
// this module lives at <root>/gsd-core/bin/lib, so VERSION is two dirs up.
|
|
41
44
|
// 2. The source / npm-package tree has no gsd-core/VERSION but carries a real
|
|
@@ -365,7 +368,14 @@ function skillFrontmatterName(skillDirName) {
|
|
|
365
368
|
return skillDirName;
|
|
366
369
|
}
|
|
367
370
|
function normalizeClaudeSkillEffort(effort) {
|
|
368
|
-
|
|
371
|
+
// #3039: `max` is rejected by Anthropic models when extended thinking is
|
|
372
|
+
// disabled (400: output_config.effort 'max' is not supported when thinking
|
|
373
|
+
// is disabled). The frontmatter is static at install time and the installer
|
|
374
|
+
// cannot know whether thinking will be on or off at invocation. `high` is the
|
|
375
|
+
// maximum value that works in both states on all supported models.
|
|
376
|
+
if (effort === 'xhigh' || effort === 'max')
|
|
377
|
+
return 'high';
|
|
378
|
+
return effort;
|
|
369
379
|
}
|
|
370
380
|
/**
|
|
371
381
|
* Qwen Code skills accept an optional numeric `priority` frontmatter field.
|
|
@@ -848,8 +858,9 @@ function convertClaudeToCursorMarkdown(content) {
|
|
|
848
858
|
// Remove Claude Code-specific bug workarounds before brand replacement
|
|
849
859
|
converted = converted.replace(/\*\*Known Claude Code bug \(classifyHandoffIfNeeded\):\*\*[^\n]*\n/g, '');
|
|
850
860
|
converted = converted.replace(/- \*\*classifyHandoffIfNeeded false failure:\*\*[^\n]*\n/g, '');
|
|
851
|
-
// Replace "Claude Code" brand references with "Cursor"
|
|
852
|
-
|
|
861
|
+
// Replace "Claude Code" brand references with "Cursor" — #2284(b): skips
|
|
862
|
+
// <runtime_compatibility> comparison-table content (protected region).
|
|
863
|
+
converted = applyClaudeCodeBrandSwap(converted, 'Cursor');
|
|
853
864
|
return converted;
|
|
854
865
|
}
|
|
855
866
|
function getCursorSkillAdapterHeader(skillName) {
|
|
@@ -890,39 +901,41 @@ function convertClaudeCommandToCursorSkill(content, skillName) {
|
|
|
890
901
|
description = toSingleLine(description);
|
|
891
902
|
const shortDescription = description.length > 180 ? `${description.slice(0, 177)}...` : description;
|
|
892
903
|
const adapter = getCursorSkillAdapterHeader(skillName);
|
|
893
|
-
//
|
|
894
|
-
//
|
|
895
|
-
//
|
|
896
|
-
//
|
|
897
|
-
|
|
898
|
-
// '/' while keeping model invocation — distinct from disable-model-invocation.)
|
|
899
|
-
return `---\nname: ${yamlIdentifier(skillName)}\ndescription: ${yamlQuote(shortDescription)}\nuser-invocable: false\n---\n\n${adapter}\n\n${body.trimStart()}`;
|
|
900
|
-
}
|
|
901
|
-
/**
|
|
902
|
-
* Convert a Claude Code command to a Cursor 1.6 slash command (#785).
|
|
903
|
-
*
|
|
904
|
-
* Cursor slash commands live in `.cursor/commands/<name>.md` and are
|
|
905
|
-
* plain markdown — no YAML frontmatter, no adapter header. The filename
|
|
906
|
-
* becomes the command name (e.g. `gsd-help.md` → `/gsd-help`).
|
|
907
|
-
*
|
|
908
|
-
* Applies the same `convertClaudeToCursorMarkdown` transforms as the skill
|
|
909
|
-
* converter (tool renames, brand substitution, slash-command normalisation),
|
|
910
|
-
* then strips the YAML frontmatter block so only the prose body remains.
|
|
911
|
-
*
|
|
912
|
-
* @param {string} content raw Claude Code command markdown (may have frontmatter)
|
|
913
|
-
* @param {string} _commandName the target command name (unused; present for
|
|
914
|
-
* API symmetry with other converters so the runtime-artifact-layout stage
|
|
915
|
-
* function can call it uniformly)
|
|
916
|
-
* @returns {string} plain markdown body, no frontmatter
|
|
917
|
-
*/
|
|
918
|
-
function convertClaudeCommandToCursorCommand(content, _commandName) {
|
|
919
|
-
const converted = convertClaudeToCursorMarkdown(content);
|
|
920
|
-
const { body } = extractFrontmatterAndBody(converted);
|
|
921
|
-
return body.trimStart();
|
|
904
|
+
// Cursor skills are both slash-invocable and model-invocable. Do not emit the
|
|
905
|
+
// unsupported `user-invocable` field: it is ignored by Cursor and previously
|
|
906
|
+
// hid the real cause of duplicate entries, the parallel commands/ surface
|
|
907
|
+
// retired in #2644.
|
|
908
|
+
return `---\nname: ${yamlIdentifier(skillName)}\ndescription: ${yamlQuote(shortDescription)}\n---\n\n${adapter}\n\n${body.trimStart()}`;
|
|
922
909
|
}
|
|
923
910
|
// --- Windsurf converters ---
|
|
924
911
|
// Windsurf uses a tool set similar to Cursor.
|
|
925
912
|
// Config lives in .windsurf/ (local) and ~/.codeium/windsurf/ (global).
|
|
913
|
+
// #2931: ported from bin/install.js's local Windsurf converter copy, which had
|
|
914
|
+
// picked up the #2284(b) protected-region fix that this exported source never
|
|
915
|
+
// received. Binding bin/install.js's Windsurf family to these exports (see
|
|
916
|
+
// tests/install-runtime-artifacts.test.cjs reference-identity assertions)
|
|
917
|
+
// without this would have silently regressed live installs: `Claude Code`
|
|
918
|
+
// mentions inside a `<runtime_compatibility>` comparison table would start
|
|
919
|
+
// getting brand-swapped again. Split `content` on the protected-block regex,
|
|
920
|
+
// brand-swap only the gap text between (and around) matches, then rejoin
|
|
921
|
+
// gap+block alternately — no placeholder/sentinel token involved.
|
|
922
|
+
const RUNTIME_COMPATIBILITY_BLOCK_RE = /<runtime_compatibility>[\s\S]*?<\/runtime_compatibility>/g;
|
|
923
|
+
function applyClaudeCodeBrandSwap(content, brandName) {
|
|
924
|
+
if (!brandName)
|
|
925
|
+
return content;
|
|
926
|
+
let result = '';
|
|
927
|
+
let lastIndex = 0;
|
|
928
|
+
RUNTIME_COMPATIBILITY_BLOCK_RE.lastIndex = 0; // reset shared global-regex state before each use
|
|
929
|
+
let m;
|
|
930
|
+
while ((m = RUNTIME_COMPATIBILITY_BLOCK_RE.exec(content))) {
|
|
931
|
+
const gap = content.slice(lastIndex, m.index);
|
|
932
|
+
result += gap.replace(/\bClaude Code\b/g, brandName);
|
|
933
|
+
result += m[0]; // protected block, verbatim — never brand-swapped
|
|
934
|
+
lastIndex = m.index + m[0].length;
|
|
935
|
+
}
|
|
936
|
+
result += content.slice(lastIndex).replace(/\bClaude Code\b/g, brandName);
|
|
937
|
+
return result;
|
|
938
|
+
}
|
|
926
939
|
function convertSlashCommandsToWindsurfSkillMentions(content) {
|
|
927
940
|
// Keep leading "/" for slash commands; only normalize gsd: -> gsd-.
|
|
928
941
|
return content.replace(/gsd:/gi, 'gsd-');
|
|
@@ -953,8 +966,9 @@ function convertClaudeToWindsurfMarkdown(content) {
|
|
|
953
966
|
// Remove Claude Code-specific bug workarounds before brand replacement
|
|
954
967
|
converted = converted.replace(/\*\*Known Claude Code bug \(classifyHandoffIfNeeded\):\*\*[^\n]*\n/g, '');
|
|
955
968
|
converted = converted.replace(/- \*\*classifyHandoffIfNeeded false failure:\*\*[^\n]*\n/g, '');
|
|
956
|
-
// Replace "Claude Code" brand references with "Windsurf"
|
|
957
|
-
|
|
969
|
+
// Replace "Claude Code" brand references with "Windsurf" — #2284(b): skips
|
|
970
|
+
// <runtime_compatibility> comparison-table content (protected region).
|
|
971
|
+
converted = applyClaudeCodeBrandSwap(converted, 'Windsurf');
|
|
958
972
|
return converted;
|
|
959
973
|
}
|
|
960
974
|
function getWindsurfSkillAdapterHeader(skillName) {
|
|
@@ -993,10 +1007,46 @@ function convertClaudeCommandToWindsurfSkill(content, skillName) {
|
|
|
993
1007
|
}
|
|
994
1008
|
}
|
|
995
1009
|
description = toSingleLine(description);
|
|
996
|
-
|
|
1010
|
+
// #2931: code-point-safe truncation (see truncateWindsurfWorkflowDescription
|
|
1011
|
+
// below) — a raw UTF-16 `slice(0, 177)` can bisect a surrogate pair and
|
|
1012
|
+
// emit a lone surrogate on re-encode. Same exact bounds as before (>180
|
|
1013
|
+
// chars -> first 177 code points + '...'), just harmonized with the
|
|
1014
|
+
// sibling Windsurf workflow converter's helper instead of duplicating the
|
|
1015
|
+
// surrogate-splitting idiom here.
|
|
1016
|
+
const shortDescription = truncateWindsurfWorkflowDescription(description);
|
|
997
1017
|
const adapter = getWindsurfSkillAdapterHeader(skillName);
|
|
998
1018
|
return `---\nname: ${yamlIdentifier(skillName)}\ndescription: ${yamlQuote(shortDescription)}\n---\n\n${adapter}\n\n${body.trimStart()}`;
|
|
999
1019
|
}
|
|
1020
|
+
// #2931: cap on the Windsurf workflow's only unbounded input (the frontmatter
|
|
1021
|
+
// `description`). Shared (not just mirrored) by convertClaudeCommandToWindsurfSkill
|
|
1022
|
+
// above — both converters call truncateWindsurfWorkflowDescription below so
|
|
1023
|
+
// there is exactly one code-point-safe truncation idiom, not two.
|
|
1024
|
+
const WINDSURF_WORKFLOW_DESCRIPTION_MAX = 180;
|
|
1025
|
+
function truncateWindsurfWorkflowDescription(description) {
|
|
1026
|
+
// Multi-byte safe: slice by Unicode code points (`Array.from`), never by
|
|
1027
|
+
// raw UTF-16 index — an index-based slice can bisect a surrogate pair and
|
|
1028
|
+
// emit a lone surrogate / U+FFFD on re-encode. See #2931.
|
|
1029
|
+
const codePoints = Array.from(description);
|
|
1030
|
+
if (codePoints.length <= WINDSURF_WORKFLOW_DESCRIPTION_MAX)
|
|
1031
|
+
return description;
|
|
1032
|
+
return `${codePoints.slice(0, WINDSURF_WORKFLOW_DESCRIPTION_MAX - 3).join('')}...`;
|
|
1033
|
+
}
|
|
1034
|
+
// #2931: SEPARATE size control from the #1615 security regex below — do not
|
|
1035
|
+
// fold the two together or make either conditional on the other. The #1615
|
|
1036
|
+
// regex constrains commandName's CHARACTER CLASS but not its LENGTH, and
|
|
1037
|
+
// commandName is interpolated into the emitted template three times (the
|
|
1038
|
+
// `# <commandName>` heading, the `@.../<stem>.md` @-reference target, and the
|
|
1039
|
+
// trailing "after /<commandName>" mention) — so an unbounded commandName
|
|
1040
|
+
// reopens the byte-cap hole the removed 12000-byte throw used to close
|
|
1041
|
+
// (verified: commandName length 246 -> 900 bytes, 5000 -> 15,162 bytes,
|
|
1042
|
+
// 20000 -> 60,162 bytes — all silently over the old 12000 cap). THROW rather
|
|
1043
|
+
// than truncate: a truncated commandName would silently point the workflow's
|
|
1044
|
+
// `@~/.claude/gsd-core/commands/gsd/<stem>.md` reference at a file that does
|
|
1045
|
+
// not exist (see DEFECT.WORKFLOW-DELEGATION-TARGET-NOT-INSTALLED) — a name
|
|
1046
|
+
// too long to represent is a genuine error, not something to degrade. 128 is
|
|
1047
|
+
// deliberately generous: the longest real shipped command name is
|
|
1048
|
+
// `gsd-plan-review-convergence` at 27 characters.
|
|
1049
|
+
const WINDSURF_COMMAND_NAME_MAX = 128;
|
|
1000
1050
|
function convertClaudeCommandToWindsurfWorkflow(content, commandName) {
|
|
1001
1051
|
// #1615 security: commandName flows unsanitized into a markdown body that
|
|
1002
1052
|
// Windsurf loads as an LLM-readable workflow. Validate at entry to prevent
|
|
@@ -1005,21 +1055,43 @@ function convertClaudeCommandToWindsurfWorkflow(content, commandName) {
|
|
|
1005
1055
|
// Pattern: optional gsd- prefix + lowercase alphanumeric + dashes; rejects
|
|
1006
1056
|
// everything else. See DEFECT.PROMPT-INJECTION-SCAN-COLLISION and the
|
|
1007
1057
|
// PR #1622 security review.
|
|
1058
|
+
// #2931: this is a SECURITY control, not a size control — do not weaken,
|
|
1059
|
+
// reorder, or make it conditional on the description-truncation logic
|
|
1060
|
+
// added below. Keep the two concerns independent even though both happen
|
|
1061
|
+
// to run in this function.
|
|
1008
1062
|
if (typeof commandName !== 'string' || !/^(?:gsd-)?[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/.test(commandName)) {
|
|
1009
1063
|
const preview = typeof commandName === 'string' ? JSON.stringify(commandName.slice(0, 60)) : String(commandName);
|
|
1010
1064
|
throw new Error(`convertClaudeCommandToWindsurfWorkflow: rejected commandName ${preview}; ` +
|
|
1011
1065
|
'must match /^(?:gsd-)?[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$/ (no slashes, backslashes, spaces, dots, trailing dash, or control chars — prevents prompt injection and path-component injection into the workflow body)');
|
|
1012
1066
|
}
|
|
1067
|
+
// #2931: SEPARATE size control — see WINDSURF_COMMAND_NAME_MAX above for
|
|
1068
|
+
// why this exists and why it throws instead of truncating. Kept as an
|
|
1069
|
+
// independent check from the #1615 regex above (not folded into it, not
|
|
1070
|
+
// conditional on it).
|
|
1071
|
+
if (commandName.length > WINDSURF_COMMAND_NAME_MAX) {
|
|
1072
|
+
const preview = JSON.stringify(commandName.slice(0, 60));
|
|
1073
|
+
throw new Error(`convertClaudeCommandToWindsurfWorkflow: commandName too long (${commandName.length} chars, ` +
|
|
1074
|
+
`preview ${preview}...); max ${WINDSURF_COMMAND_NAME_MAX} chars (see WINDSURF_COMMAND_NAME_MAX)`);
|
|
1075
|
+
}
|
|
1013
1076
|
const converted = convertClaudeToWindsurfMarkdown(content);
|
|
1014
1077
|
const { frontmatter } = extractFrontmatterAndBody(converted);
|
|
1015
|
-
const
|
|
1078
|
+
const rawDescription = frontmatter ? extractFrontmatterField(frontmatter, 'description') : '';
|
|
1079
|
+
// #2931: a whitespace-only description is truthy (`description || fallback`
|
|
1080
|
+
// would keep it) but toSingleLine() collapses it to ''. Treat it as absent
|
|
1081
|
+
// so the fallback is used instead of emitting a blank line.
|
|
1082
|
+
const singleLineDescription = rawDescription ? toSingleLine(rawDescription) : '';
|
|
1083
|
+
const effectiveDescription = truncateWindsurfWorkflowDescription(singleLineDescription || `Run ${commandName}.`);
|
|
1016
1084
|
const stem = commandName.startsWith('gsd-') ? commandName.slice(4) : commandName;
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1085
|
+
// #2931: total emission size is bounded by (fixed template text) +
|
|
1086
|
+
// (3 x WINDSURF_COMMAND_NAME_MAX, one per commandName/stem interpolation
|
|
1087
|
+
// above) + (WINDSURF_WORKFLOW_DESCRIPTION_MAX Unicode code points, up to 4
|
|
1088
|
+
// UTF-8 bytes each). Both inputs are validated/truncated above — commandName
|
|
1089
|
+
// is length-capped-and-thrown by WINDSURF_COMMAND_NAME_MAX, description is
|
|
1090
|
+
// truncated by truncateWindsurfWorkflowDescription — so this bound holds by
|
|
1091
|
+
// construction, not by measurement. The 12000-byte figure itself lives in
|
|
1092
|
+
// exactly one place — the cap table in tests/helpers/emitted-caps.cjs —
|
|
1093
|
+
// this comment only justifies why the actual emitted size stays under it.
|
|
1094
|
+
return `# ${commandName}\n\n${effectiveDescription}\n\nRead and execute the GSD command at @~/.claude/gsd-core/commands/gsd/${stem}.md end-to-end. Treat the user's message after /${commandName} as the command arguments.`;
|
|
1023
1095
|
}
|
|
1024
1096
|
// --- Augment converters ---
|
|
1025
1097
|
// Augment uses a tool set similar to Cursor/Windsurf.
|
|
@@ -1047,8 +1119,9 @@ function convertClaudeToAugmentMarkdown(content) {
|
|
|
1047
1119
|
// Remove Claude Code-specific bug workarounds before brand replacement
|
|
1048
1120
|
converted = converted.replace(/\*\*Known Claude Code bug \(classifyHandoffIfNeeded\):\*\*[^\n]*\n/g, '');
|
|
1049
1121
|
converted = converted.replace(/- \*\*classifyHandoffIfNeeded false failure:\*\*[^\n]*\n/g, '');
|
|
1050
|
-
// Replace "Claude Code" brand references with "Augment"
|
|
1051
|
-
|
|
1122
|
+
// Replace "Claude Code" brand references with "Augment" — #2284(b): skips
|
|
1123
|
+
// <runtime_compatibility> comparison-table content (protected region).
|
|
1124
|
+
converted = applyClaudeCodeBrandSwap(converted, 'Augment');
|
|
1052
1125
|
return converted;
|
|
1053
1126
|
}
|
|
1054
1127
|
// #2097 (ADR-1239): command-body converters selected by descriptor
|
|
@@ -1111,10 +1184,55 @@ function convertClaudeToTraeMarkdown(content) {
|
|
|
1111
1184
|
// Replace general-purpose subagent type with Trae's equivalent "general_purpose_task"
|
|
1112
1185
|
converted = converted.replace(/subagent_type="general-purpose"/g, 'subagent_type="general_purpose_task"');
|
|
1113
1186
|
converted = converted.replace(/\$ARGUMENTS\b/g, '{{GSD_ARGS}}');
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1187
|
+
// #2658: full-path forms (with a leading dot-claude-slash prefix) MUST be
|
|
1188
|
+
// replaced before the bare Claude-instruction-file pattern and before the
|
|
1189
|
+
// generic dot-claude-slash rewrite below — otherwise the bare pattern
|
|
1190
|
+
// consumes only the instruction-filename tail, leaving that prefix stale
|
|
1191
|
+
// in place, and the generic rewrite then mutates the stale leftover too,
|
|
1192
|
+
// producing a doubled trae-prefix segment ahead of the rules path instead
|
|
1193
|
+
// of a single clean one. (Deliberately never spelling the instruction
|
|
1194
|
+
// filename as one contiguous "CLAUDE" + dot + "md" token, and never
|
|
1195
|
+
// spelling either malformed shape out as a literal contiguous string, in
|
|
1196
|
+
// ANY comment in this function: this file ships verbatim into local
|
|
1197
|
+
// `--trae` installs, where it is itself run through this same class of
|
|
1198
|
+
// find/replace — a literal instruction-filename token sitting in a
|
|
1199
|
+
// comment gets "fixed" right along with real code, and the emitted-content
|
|
1200
|
+
// regression test added alongside this fix asserts neither malformed
|
|
1201
|
+
// shape appears anywhere in the installed tree, comments included; this
|
|
1202
|
+
// bit the fix itself twice during development.) All forms converge on the
|
|
1203
|
+
// same concrete file (never a bare directory) so this stays in parity
|
|
1204
|
+
// with the `trae.js` RUNTIME_CONTENT_DISPATCH entry.
|
|
1205
|
+
converted = converted.replace(/`\.\/\.claude\/CLAUDE\.md`/g, '`.trae/rules/rules.md`');
|
|
1206
|
+
converted = converted.replace(/\.\/\.claude\/CLAUDE\.md/g, '.trae/rules/rules.md');
|
|
1207
|
+
converted = converted.replace(/`\.claude\/CLAUDE\.md`/g, '`.trae/rules/rules.md`');
|
|
1208
|
+
converted = converted.replace(/\.claude\/CLAUDE\.md/g, '.trae/rules/rules.md');
|
|
1209
|
+
// #2658 (found via the end-to-end install regression test, not the static
|
|
1210
|
+
// trace above): `copyWithPathReplacement` runs a GENERIC dot-claude-slash
|
|
1211
|
+
// -> runtime-config-dir rewrite on every .md file before calling this
|
|
1212
|
+
// converter — for `~/.claude/`, `$HOME/.claude/`, AND `./.claude/` alike —
|
|
1213
|
+
// substituting a runtime-appropriate `pathPrefix` this function is never
|
|
1214
|
+
// given and cannot itself compute (it differs per install invocation: a
|
|
1215
|
+
// relative `./.trae/` for a project-local install, an arbitrary absolute
|
|
1216
|
+
// path for a local install rooted elsewhere, `~/.trae/` for a global one).
|
|
1217
|
+
// So for source using any of those prefixed forms, the patterns above
|
|
1218
|
+
// never fire here — this converter only ever sees the ALREADY-rewritten
|
|
1219
|
+
// "<runtime-config-dir>/" + instruction-filename shape, with whatever
|
|
1220
|
+
// prefix the install actually used. The generic pattern below preserves
|
|
1221
|
+
// that prefix verbatim (via the capture group) and only fixes the
|
|
1222
|
+
// filename suffix, rather than assuming a fixed `./.trae/` shape — a
|
|
1223
|
+
// narrower fixed-prefix version of this pattern shipped first and still
|
|
1224
|
+
// left the doubled-prefix defect live for the `$HOME/.claude/` and
|
|
1225
|
+
// `~/.claude/` forms specifically (found the same way, one regression-test
|
|
1226
|
+
// run later). Scoped to a `.trae/` tail so it cannot also swallow the
|
|
1227
|
+
// unprefixed `./CLAUDE.md` form the very next pattern handles differently
|
|
1228
|
+
// (discarding the prefix entirely, not preserving it). Must run before
|
|
1229
|
+
// the bare pattern for the same consume-the-full-match-first reason.
|
|
1230
|
+
converted = converted.replace(/`([^\s`]*\.trae\/)CLAUDE\.md`/g, '`$1rules/rules.md`');
|
|
1231
|
+
converted = converted.replace(/([^\s`]*\.trae\/)CLAUDE\.md/g, '$1rules/rules.md');
|
|
1232
|
+
converted = converted.replace(/`\.\/CLAUDE\.md`/g, '`.trae/rules/rules.md`');
|
|
1233
|
+
converted = converted.replace(/\.\/CLAUDE\.md/g, '.trae/rules/rules.md');
|
|
1234
|
+
converted = converted.replace(/`CLAUDE\.md`/g, '`.trae/rules/rules.md`');
|
|
1235
|
+
converted = converted.replace(/\bCLAUDE\.md\b/g, '.trae/rules/rules.md');
|
|
1118
1236
|
converted = converted.replace(/\.claude\/skills\//g, '.trae/skills/');
|
|
1119
1237
|
converted = converted.replace(/\.\/\.claude\//g, './.trae/');
|
|
1120
1238
|
converted = converted.replace(/\.claude\//g, '.trae/');
|
|
@@ -1126,7 +1244,8 @@ function convertClaudeToTraeMarkdown(content) {
|
|
|
1126
1244
|
converted = converted.replace(/\bCLAUDE_CONFIG_DIR\b/g, 'TRAE_CONFIG_DIR');
|
|
1127
1245
|
converted = converted.replace(/\*\*Known Claude Code bug \(classifyHandoffIfNeeded\):\*\*[^\n]*\n/g, '');
|
|
1128
1246
|
converted = converted.replace(/- \*\*classifyHandoffIfNeeded false failure:\*\*[^\n]*\n/g, '');
|
|
1129
|
-
|
|
1247
|
+
// #2284(b): skips <runtime_compatibility> comparison-table content (protected region).
|
|
1248
|
+
converted = applyClaudeCodeBrandSwap(converted, 'Trae');
|
|
1130
1249
|
return converted;
|
|
1131
1250
|
}
|
|
1132
1251
|
// DEFECT.GENERATIVE-FIX: this body is mirrored in bin/install.js's
|
|
@@ -1180,7 +1299,8 @@ function convertClaudeToCodebuddyMarkdown(content) {
|
|
|
1180
1299
|
converted = converted.replace(/\.claude\//g, '.codebuddy/');
|
|
1181
1300
|
converted = converted.replace(/\*\*Known Claude Code bug \(classifyHandoffIfNeeded\):\*\*[^\n]*\n/g, '');
|
|
1182
1301
|
converted = converted.replace(/- \*\*classifyHandoffIfNeeded false failure:\*\*[^\n]*\n/g, '');
|
|
1183
|
-
|
|
1302
|
+
// #2284(b): skips <runtime_compatibility> comparison-table content (protected region).
|
|
1303
|
+
converted = applyClaudeCodeBrandSwap(converted, 'CodeBuddy');
|
|
1184
1304
|
return converted;
|
|
1185
1305
|
}
|
|
1186
1306
|
function convertClaudeCommandToCodebuddySkill(content, skillName) {
|
|
@@ -1260,7 +1380,8 @@ function convertClaudeToCliineMarkdown(content) {
|
|
|
1260
1380
|
converted = converted.replace(/\bCLAUDE_CONFIG_DIR\b/g, 'CLINE_CONFIG_DIR');
|
|
1261
1381
|
converted = converted.replace(/\*\*Known Claude Code bug \(classifyHandoffIfNeeded\):\*\*[^\n]*\n/g, '');
|
|
1262
1382
|
converted = converted.replace(/- \*\*classifyHandoffIfNeeded false failure:\*\*[^\n]*\n/g, '');
|
|
1263
|
-
|
|
1383
|
+
// #2284(b): skips <runtime_compatibility> comparison-table content (protected region).
|
|
1384
|
+
converted = applyClaudeCodeBrandSwap(converted, 'Cline');
|
|
1264
1385
|
return converted;
|
|
1265
1386
|
}
|
|
1266
1387
|
/**
|
|
@@ -1413,6 +1534,8 @@ Typed mapping (agent_type-capable schema only):
|
|
|
1413
1534
|
inherited, or unsupported values; do not invent one-off effort literals in
|
|
1414
1535
|
workflow prose.
|
|
1415
1536
|
- \`fork_context: false\` by default — GSD agents load their own context via \`<files_to_read>\` blocks
|
|
1537
|
+
- \`task_name\` — required by the collaboration schema; provide a descriptive name for each spawned task
|
|
1538
|
+
- \`fork_turns\` — optional parameter controlling turn-forking depth; coexists with \`fork_context\` (not a replacement)
|
|
1416
1539
|
- \`Task(isolation="worktree")\` / \`Agent(isolation="worktree")\` → no direct \`spawn_agent\` mapping,
|
|
1417
1540
|
but Codex declares \`dispatch.isolation: orchestrator-worktree\` (#2584). Codex
|
|
1418
1541
|
\`spawn_agent\` still does not create or bind a git worktree; instead GSD itself
|
|
@@ -1449,11 +1572,13 @@ Spawn restriction:
|
|
|
1449
1572
|
defaulting to inline execution.
|
|
1450
1573
|
|
|
1451
1574
|
Parallel fan-out:
|
|
1452
|
-
- Spawn multiple agents → collect agent IDs → \`
|
|
1575
|
+
- Spawn multiple agents → collect agent IDs → \`collaboration.wait_agent(timeout_ms=...)\` for each to complete
|
|
1576
|
+
- Do NOT use \`functions.wait(cell_id=...)\` — that is an unrelated exec-cell tool, not the collaboration wait
|
|
1453
1577
|
|
|
1454
1578
|
Result parsing:
|
|
1455
1579
|
- Look for structured markers in agent output: \`CHECKPOINT\`, \`PLAN COMPLETE\`, \`SUMMARY\`, etc.
|
|
1456
|
-
- \`close_agent(id)\` after collecting results
|
|
1580
|
+
- \`close_agent(id)\` after collecting results — but only if \`close_agent\` is visible in the current
|
|
1581
|
+
tool schema (check via \`tool_search\` first, same schema-detection gate as \`spawn_agent\` above)
|
|
1457
1582
|
</codex_skill_adapter>`;
|
|
1458
1583
|
}
|
|
1459
1584
|
function convertClaudeCommandToCodexSkill(content, skillName) {
|
|
@@ -2038,8 +2163,9 @@ function convertClaudeAgentToQwenAgent(content) {
|
|
|
2038
2163
|
let converted = content;
|
|
2039
2164
|
if (_b['CLAUDE.md'])
|
|
2040
2165
|
converted = converted.replace(/CLAUDE\.md/g, _b['CLAUDE.md']);
|
|
2166
|
+
// #2284(b): skips <runtime_compatibility> comparison-table content (protected region).
|
|
2041
2167
|
if (_b['Claude Code'])
|
|
2042
|
-
converted = converted
|
|
2168
|
+
converted = applyClaudeCodeBrandSwap(converted, _b['Claude Code']);
|
|
2043
2169
|
if (_b['.claude/'])
|
|
2044
2170
|
converted = converted.replace(/\.claude\//g, _b['.claude/']);
|
|
2045
2171
|
const { frontmatter, body } = extractFrontmatterAndBody(converted);
|
|
@@ -2342,7 +2468,8 @@ function _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal = false, a
|
|
|
2342
2468
|
const _b = _hostBehaviors(runtime).brandingRewrites;
|
|
2343
2469
|
if (_b) {
|
|
2344
2470
|
content = content.replace(/CLAUDE\.md/g, _b['CLAUDE.md']);
|
|
2345
|
-
|
|
2471
|
+
// #2284(b): skips <runtime_compatibility> comparison-table content (protected region).
|
|
2472
|
+
content = applyClaudeCodeBrandSwap(content, _b['Claude Code']);
|
|
2346
2473
|
}
|
|
2347
2474
|
content = content.replace(/~\/\.claude\//g, pathPrefix);
|
|
2348
2475
|
content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
|
|
@@ -2366,7 +2493,8 @@ function _applyRuntimeRewrites(content, runtime, pathPrefix, isGlobal = false, a
|
|
|
2366
2493
|
const _b = _hostBehaviors(runtime).brandingRewrites;
|
|
2367
2494
|
if (_b) {
|
|
2368
2495
|
content = content.replace(/CLAUDE\.md/g, _b['CLAUDE.md']);
|
|
2369
|
-
|
|
2496
|
+
// #2284(b): skips <runtime_compatibility> comparison-table content (protected region).
|
|
2497
|
+
content = applyClaudeCodeBrandSwap(content, _b['Claude Code']);
|
|
2370
2498
|
}
|
|
2371
2499
|
content = content.replace(/~\/\.claude\//g, pathPrefix);
|
|
2372
2500
|
content = content.replace(/\$HOME\/\.claude\//g, pathPrefix);
|
|
@@ -2633,10 +2761,15 @@ module.exports = {
|
|
|
2633
2761
|
buildKimiAgentArtifacts,
|
|
2634
2762
|
convertClaudeToCursorMarkdown,
|
|
2635
2763
|
convertClaudeCommandToCursorSkill,
|
|
2636
|
-
convertClaudeCommandToCursorCommand,
|
|
2637
2764
|
convertClaudeToWindsurfMarkdown,
|
|
2638
2765
|
convertClaudeCommandToWindsurfSkill,
|
|
2639
2766
|
convertClaudeCommandToWindsurfWorkflow,
|
|
2767
|
+
// #2931: single-sourced brand-swap helper (was duplicated verbatim in
|
|
2768
|
+
// bin/install.js — the exact drift class this PR exists to reduce). Used
|
|
2769
|
+
// internally by convertClaudeToWindsurfMarkdown/convertClaudeToAugmentMarkdown
|
|
2770
|
+
// above and bound from here by the remaining bin/install.js converters
|
|
2771
|
+
// (Cursor/Trae/CodeBuddy/Cline) that still brand-swap inline.
|
|
2772
|
+
applyClaudeCodeBrandSwap,
|
|
2640
2773
|
convertClaudeToAugmentMarkdown,
|
|
2641
2774
|
convertClaudeCommandToAugmentSkill,
|
|
2642
2775
|
convertClaudeToTraeMarkdown,
|
|
@@ -19,7 +19,7 @@ const node_fs_1 = __importDefault(require("node:fs"));
|
|
|
19
19
|
const node_os_1 = __importDefault(require("node:os"));
|
|
20
20
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
21
21
|
const installProfiles = require("./install-profiles.cjs");
|
|
22
|
-
const { stageSkillsForProfile,
|
|
22
|
+
const { stageSkillsForProfile, stageAgentsForRuntimeWithConverter, stageSkillsForRuntimeAsSkills, stageCommandsForRuntimeFlat, } = installProfiles;
|
|
23
23
|
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
24
24
|
const runtimeArtifactConversion = require("./runtime-artifact-conversion.cjs");
|
|
25
25
|
const conversionExports = runtimeArtifactConversion;
|
|
@@ -117,7 +117,17 @@ function agentsKind(destSubpath, prefix, configDir) {
|
|
|
117
117
|
kind: 'agents',
|
|
118
118
|
destSubpath,
|
|
119
119
|
prefix,
|
|
120
|
-
|
|
120
|
+
// #2995: a `converter: null` agents entry (claude local, zcode) previously
|
|
121
|
+
// staged via stageAgentsForProfile — a RAW byte copy that never reads content
|
|
122
|
+
// into JS, so gsd:section markers shipped verbatim. Route through the
|
|
123
|
+
// composing stager with an identity converter instead: same output as the raw
|
|
124
|
+
// copy for an unmarked agent, markers stripped for a marked one. Routing both
|
|
125
|
+
// agent kinds through the stager collapses what were five independent agent
|
|
126
|
+
// read points down to three compose call sites: this stager, bin/install.js's
|
|
127
|
+
// inline agent loop, and installCodexConfig's per-agent .toml writer. The
|
|
128
|
+
// exhaustive per-runtime sweep in tests/agent-fragments-emission.install.test.cjs
|
|
129
|
+
// is what keeps a fourth from appearing uncomposed.
|
|
130
|
+
stage: (resolved) => stageAgentsForRuntimeWithConverter(findAgentsSourceRoot(configDir), resolved, (content) => content),
|
|
121
131
|
};
|
|
122
132
|
}
|
|
123
133
|
/**
|
|
@@ -185,7 +195,9 @@ function kimiAgentsKind(destSubpath, prefix, configDir) {
|
|
|
185
195
|
prefix,
|
|
186
196
|
stage: (resolved) => {
|
|
187
197
|
const buildKimiAgentArtifacts = conversionExports['buildKimiAgentArtifacts'];
|
|
188
|
-
|
|
198
|
+
// #2995: compose at staging (identity converter) so the readFileSync below
|
|
199
|
+
// sees marker-free content — same single composing stager as agentsKind.
|
|
200
|
+
const stagedAgents = stageAgentsForRuntimeWithConverter(findAgentsSourceRoot(configDir), resolved, (content) => content);
|
|
189
201
|
const subagents = [];
|
|
190
202
|
if (node_fs_1.default.existsSync(stagedAgents)) {
|
|
191
203
|
for (const entry of node_fs_1.default.readdirSync(stagedAgents, { withFileTypes: true })) {
|