@skillcap/gdh 0.25.1 → 0.25.2
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/INSTALL-BUNDLE.json +1 -1
- package/RELEASE-SPAN-UPDATE-CONTRACTS.json +57 -0
- package/node_modules/@gdh/adapters/dist/index.d.ts +10 -9
- package/node_modules/@gdh/adapters/dist/index.d.ts.map +1 -1
- package/node_modules/@gdh/adapters/dist/index.js +256 -49
- package/node_modules/@gdh/adapters/dist/index.js.map +1 -1
- package/node_modules/@gdh/adapters/package.json +8 -8
- package/node_modules/@gdh/authoring/package.json +2 -2
- package/node_modules/@gdh/cli/dist/index.js +7 -7
- package/node_modules/@gdh/cli/dist/index.js.map +1 -1
- package/node_modules/@gdh/cli/package.json +10 -10
- package/node_modules/@gdh/core/dist/index.d.ts +1 -1
- package/node_modules/@gdh/core/dist/index.js +1 -1
- package/node_modules/@gdh/core/dist/migrations/managed-surface-classes.d.ts +7 -7
- package/node_modules/@gdh/core/dist/migrations/managed-surface-classes.js +9 -9
- package/node_modules/@gdh/core/dist/migrations/managed-surface-classes.js.map +1 -1
- package/node_modules/@gdh/core/dist/migrations/managed-target-surface-inventory.d.ts +4 -4
- package/node_modules/@gdh/core/dist/migrations/managed-target-surface-inventory.d.ts.map +1 -1
- package/node_modules/@gdh/core/dist/migrations/managed-target-surface-inventory.js +9 -7
- package/node_modules/@gdh/core/dist/migrations/managed-target-surface-inventory.js.map +1 -1
- package/node_modules/@gdh/core/package.json +1 -1
- package/node_modules/@gdh/docs/package.json +2 -2
- package/node_modules/@gdh/mcp/package.json +8 -8
- package/node_modules/@gdh/observability/package.json +2 -2
- package/node_modules/@gdh/runtime/package.json +2 -2
- package/node_modules/@gdh/scan/package.json +3 -3
- package/node_modules/@gdh/verify/package.json +7 -7
- package/package.json +11 -11
|
@@ -54,7 +54,7 @@ export const CLAUDE_ONBOARD_SKILL_RELATIVE_PATH = ".claude/skills/gdh-onboard/SK
|
|
|
54
54
|
export const PROJECT_MCP_RELATIVE_PATH = ".mcp.json";
|
|
55
55
|
export const CURSOR_MCP_RELATIVE_PATH = ".cursor/mcp.json";
|
|
56
56
|
export const CURSOR_RULE_RELATIVE_PATH = ".cursor/rules/gdh-agent.mdc";
|
|
57
|
-
export const
|
|
57
|
+
export const CURSOR_ONBOARD_COMMAND_RELATIVE_PATH = ".cursor/commands/gdh-onboard.md";
|
|
58
58
|
export const CODEX_ONBOARD_SKILL_RELATIVE_PATH = ".agents/skills/gdh-onboard/SKILL.md";
|
|
59
59
|
export const CLAUDE_STATUS_SKILL_RELATIVE_PATH = ".claude/skills/gdh-status/SKILL.md";
|
|
60
60
|
export const CLAUDE_MIGRATE_SKILL_RELATIVE_PATH = ".claude/skills/gdh-migrate/SKILL.md";
|
|
@@ -66,11 +66,10 @@ export const CODEX_MIGRATE_SKILL_RELATIVE_PATH = ".agents/skills/gdh-migrate/SKI
|
|
|
66
66
|
export const CODEX_CHECK_SKILL_RELATIVE_PATH = ".agents/skills/gdh-check/SKILL.md";
|
|
67
67
|
export const CODEX_PREPARE_SKILL_RELATIVE_PATH = ".agents/skills/gdh-prepare/SKILL.md";
|
|
68
68
|
export const CODEX_RUN_GAME_SKILL_RELATIVE_PATH = ".agents/skills/gdh-run-game/SKILL.md";
|
|
69
|
-
export const
|
|
70
|
-
export const
|
|
71
|
-
export const
|
|
72
|
-
export const
|
|
73
|
-
export const CURSOR_RUN_GAME_SKILL_RELATIVE_PATH = ".cursor/skills/gdh-run-game/SKILL.md";
|
|
69
|
+
export const CURSOR_STATUS_COMMAND_RELATIVE_PATH = ".cursor/commands/gdh-status.md";
|
|
70
|
+
export const CURSOR_MIGRATE_COMMAND_RELATIVE_PATH = ".cursor/commands/gdh-migrate.md";
|
|
71
|
+
export const CURSOR_PREPARE_COMMAND_RELATIVE_PATH = ".cursor/commands/gdh-prepare.md";
|
|
72
|
+
export const CURSOR_RUN_GAME_COMMAND_RELATIVE_PATH = ".cursor/commands/gdh-run-game.md";
|
|
74
73
|
// Phase 13 SELF-01: /gdh-update skill surface path constants. The rendered
|
|
75
74
|
// bodies shell out to `npx -y @skillcap/gdh@latest self-update` (LITERAL
|
|
76
75
|
// @latest, not the pinned version — D-10) so the NEW CLI performs the update,
|
|
@@ -79,7 +78,7 @@ export const CURSOR_RUN_GAME_SKILL_RELATIVE_PATH = ".cursor/skills/gdh-run-game/
|
|
|
79
78
|
// rendered bodies are version-agnostic by design.
|
|
80
79
|
export const CLAUDE_UPDATE_SKILL_RELATIVE_PATH = ".claude/skills/gdh-update/SKILL.md";
|
|
81
80
|
export const CODEX_UPDATE_SKILL_RELATIVE_PATH = ".agents/skills/gdh-update/SKILL.md";
|
|
82
|
-
export const
|
|
81
|
+
export const CURSOR_UPDATE_COMMAND_RELATIVE_PATH = ".cursor/commands/gdh-update.md";
|
|
83
82
|
export const CLAUDE_SCAN_SKILL_RELATIVE_PATH = ".claude/skills/gdh-scan/SKILL.md";
|
|
84
83
|
export const CODEX_SCAN_SKILL_RELATIVE_PATH = ".agents/skills/gdh-scan/SKILL.md";
|
|
85
84
|
export const CLAUDE_ONBOARD_COMMAND_RELATIVE_PATH = CLAUDE_ONBOARD_SKILL_RELATIVE_PATH;
|
|
@@ -111,7 +110,36 @@ const LEGACY_CODEX_SKILL_RELATIVE_PATHS = [
|
|
|
111
110
|
[".codex/skills/gdh-verify/SKILL.md", "gdh-verify"],
|
|
112
111
|
[".codex/skills/gdh-scan/SKILL.md", "gdh-scan"],
|
|
113
112
|
];
|
|
114
|
-
|
|
113
|
+
const LEGACY_CURSOR_SKILL_RELATIVE_PATHS = [
|
|
114
|
+
[".cursor/skills/gdh-onboard/SKILL.md", "gdh-onboard"],
|
|
115
|
+
[".cursor/skills/gdh-status/SKILL.md", "gdh-status"],
|
|
116
|
+
[".cursor/skills/gdh-migrate/SKILL.md", "gdh-migrate"],
|
|
117
|
+
[".cursor/skills/gdh-update/SKILL.md", "gdh-update"],
|
|
118
|
+
[".cursor/skills/gdh-check/SKILL.md", "gdh-check"],
|
|
119
|
+
[".cursor/skills/gdh-prepare/SKILL.md", "gdh-prepare"],
|
|
120
|
+
[".cursor/skills/gdh-run-game/SKILL.md", "gdh-run-game"],
|
|
121
|
+
[".cursor/skills/gdh-verify/SKILL.md", "gdh-verify"],
|
|
122
|
+
[".cursor/skills/gdh-scan/SKILL.md", "gdh-scan"],
|
|
123
|
+
];
|
|
124
|
+
export const CURSOR_SCAN_COMMAND_RELATIVE_PATH = ".cursor/commands/gdh-scan.md";
|
|
125
|
+
const CODEX_SKILL_RELATIVE_PATHS = [
|
|
126
|
+
path.dirname(CODEX_ONBOARD_SKILL_RELATIVE_PATH),
|
|
127
|
+
path.dirname(CODEX_STATUS_SKILL_RELATIVE_PATH),
|
|
128
|
+
path.dirname(CODEX_MIGRATE_SKILL_RELATIVE_PATH),
|
|
129
|
+
path.dirname(CODEX_UPDATE_SKILL_RELATIVE_PATH),
|
|
130
|
+
path.dirname(CODEX_PREPARE_SKILL_RELATIVE_PATH),
|
|
131
|
+
path.dirname(CODEX_RUN_GAME_SKILL_RELATIVE_PATH),
|
|
132
|
+
path.dirname(CODEX_SCAN_SKILL_RELATIVE_PATH),
|
|
133
|
+
];
|
|
134
|
+
const CURSOR_COMMAND_RELATIVE_PATHS = [
|
|
135
|
+
CURSOR_ONBOARD_COMMAND_RELATIVE_PATH,
|
|
136
|
+
CURSOR_STATUS_COMMAND_RELATIVE_PATH,
|
|
137
|
+
CURSOR_MIGRATE_COMMAND_RELATIVE_PATH,
|
|
138
|
+
CURSOR_UPDATE_COMMAND_RELATIVE_PATH,
|
|
139
|
+
CURSOR_PREPARE_COMMAND_RELATIVE_PATH,
|
|
140
|
+
CURSOR_RUN_GAME_COMMAND_RELATIVE_PATH,
|
|
141
|
+
CURSOR_SCAN_COMMAND_RELATIVE_PATH,
|
|
142
|
+
];
|
|
115
143
|
export const LOCAL_PATH_HINTS_RELATIVE_PATH = ".gdh-state/local-paths.json";
|
|
116
144
|
export const CODEX_PROJECT_CONFIG_RELATIVE_PATH = ".codex/config.toml";
|
|
117
145
|
export const GDH_MCP_SERVER_NAME = "gdh";
|
|
@@ -375,7 +403,15 @@ export async function createGsdSnapshot(targetPath, options = {}) {
|
|
|
375
403
|
guidanceResolve,
|
|
376
404
|
};
|
|
377
405
|
}
|
|
378
|
-
|
|
406
|
+
function normalizePortableRelativePath(relativePath) {
|
|
407
|
+
const normalized = relativePath.split(path.sep).join("/");
|
|
408
|
+
return normalized === "" ? "." : normalized.replace(/\/+$/, "");
|
|
409
|
+
}
|
|
410
|
+
export function renderCursorRule(input) {
|
|
411
|
+
const targetPrefix = input?.targetRelativePath
|
|
412
|
+
? normalizePortableRelativePath(input.targetRelativePath)
|
|
413
|
+
: ".";
|
|
414
|
+
const prefixed = (relativePath) => targetPrefix === "." ? relativePath : `${targetPrefix}/${relativePath}`;
|
|
379
415
|
return [
|
|
380
416
|
"---",
|
|
381
417
|
"description: GDH visibility reinforcement",
|
|
@@ -386,8 +422,8 @@ export function renderCursorRule() {
|
|
|
386
422
|
"",
|
|
387
423
|
"This GDH-managed Godot target uses GDH.",
|
|
388
424
|
"Apply this rule only for files inside this Godot target or when the user explicitly asks for GDH work on this target.",
|
|
389
|
-
|
|
390
|
-
|
|
425
|
+
`Follow the canonical target-local entrypoint in [AGENTS.md](../../${prefixed("AGENTS.md")}), then load the canonical guidance index at [.gdh/guidance/README.md](../../${prefixed(".gdh/guidance/README.md")}) before substantive Godot work.`,
|
|
426
|
+
`For runtime bridge usage, load [.gdh/guidance/authoring-and-validation.md](../../${prefixed(".gdh/guidance/authoring-and-validation.md")}) and follow its runtime bridge section.`,
|
|
391
427
|
"Do not duplicate or override the canonical GDH guidance chain here.",
|
|
392
428
|
"",
|
|
393
429
|
].join("\n");
|
|
@@ -419,9 +455,16 @@ async function inspectProjectMcpSupport(targetPath, options) {
|
|
|
419
455
|
const managedMcpEntry = buildManagedMcpServerEntry({
|
|
420
456
|
pinnedVersion: options.pinnedVersion ?? "latest",
|
|
421
457
|
});
|
|
422
|
-
const
|
|
458
|
+
const nestedTarget = path.resolve(targetPath) !== path.resolve(options.integrationRootPath);
|
|
459
|
+
const [projectFile, cursorFile, targetProjectFile, targetCursorFile, codexProjectContent, localPathHints,] = await Promise.all([
|
|
423
460
|
inspectJsonFile(path.join(options.integrationRootPath, PROJECT_MCP_RELATIVE_PATH)),
|
|
424
461
|
inspectJsonFile(path.join(options.integrationRootPath, CURSOR_MCP_RELATIVE_PATH)),
|
|
462
|
+
nestedTarget
|
|
463
|
+
? inspectJsonFile(path.join(targetPath, PROJECT_MCP_RELATIVE_PATH))
|
|
464
|
+
: inspectJsonFile(path.join(options.integrationRootPath, PROJECT_MCP_RELATIVE_PATH)),
|
|
465
|
+
nestedTarget
|
|
466
|
+
? inspectJsonFile(path.join(targetPath, CURSOR_MCP_RELATIVE_PATH))
|
|
467
|
+
: inspectJsonFile(path.join(options.integrationRootPath, CURSOR_MCP_RELATIVE_PATH)),
|
|
425
468
|
fs
|
|
426
469
|
.readFile(path.join(options.integrationRootPath, CODEX_PROJECT_CONFIG_RELATIVE_PATH), "utf8")
|
|
427
470
|
.catch(() => null),
|
|
@@ -435,6 +478,8 @@ async function inspectProjectMcpSupport(targetPath, options) {
|
|
|
435
478
|
localPathHints,
|
|
436
479
|
projectFile: inspectManagedMcpFile(projectFile, enabled, PROJECT_MCP_RELATIVE_PATH, "Claude project MCP config", managedMcpEntry),
|
|
437
480
|
cursorFile: inspectManagedMcpFile(cursorFile, enabled, CURSOR_MCP_RELATIVE_PATH, "Cursor project MCP config", managedMcpEntry),
|
|
481
|
+
targetProjectFile: inspectManagedMcpFile(targetProjectFile, enabled, PROJECT_MCP_RELATIVE_PATH, "target-local Claude project MCP config", managedMcpEntry),
|
|
482
|
+
targetCursorFile: inspectManagedMcpFile(targetCursorFile, enabled, CURSOR_MCP_RELATIVE_PATH, "target-local Cursor project MCP config", managedMcpEntry),
|
|
438
483
|
codexProjectFile: inspectManagedCodexProjectFile(codexProjectContent, enabled, options.pinnedVersion),
|
|
439
484
|
codexRegistration: options.includeUserLocal
|
|
440
485
|
? await inspectCodexRegistration(targetPath, enabled, codexServerName, codexConfigPath, options.integrationRootPath, options.pinnedVersion)
|
|
@@ -731,34 +776,34 @@ function inspectClaudeSkillSurface(targetPath, relativePath, content, expectedCo
|
|
|
731
776
|
}),
|
|
732
777
|
];
|
|
733
778
|
}
|
|
734
|
-
function
|
|
779
|
+
function inspectCursorCommandSurface(targetPath, relativePath, content, expectedContent, commandName) {
|
|
735
780
|
if (expectedContent === null) {
|
|
736
781
|
return [
|
|
737
782
|
createSurfaceStatus({
|
|
738
|
-
kind: "
|
|
783
|
+
kind: "command_file",
|
|
739
784
|
scope: "repo",
|
|
740
785
|
targetPath,
|
|
741
786
|
relativePath,
|
|
742
787
|
present: content !== null,
|
|
743
788
|
state: "missing",
|
|
744
|
-
summary: `Cursor ${
|
|
789
|
+
summary: `Cursor ${commandName} command cannot be validated yet: no \`gdh_version\` is pinned (run \`gdh setup\` or \`gdh migrate --apply\`).`,
|
|
745
790
|
version: null,
|
|
746
791
|
}),
|
|
747
792
|
];
|
|
748
793
|
}
|
|
749
794
|
return [
|
|
750
795
|
createSurfaceStatus({
|
|
751
|
-
kind: "
|
|
796
|
+
kind: "command_file",
|
|
752
797
|
scope: "repo",
|
|
753
798
|
targetPath,
|
|
754
799
|
relativePath,
|
|
755
800
|
present: content !== null,
|
|
756
801
|
state: content === null ? "missing" : content === expectedContent ? "ready" : "misconfigured",
|
|
757
802
|
summary: content === null
|
|
758
|
-
? `Cursor \`/${
|
|
803
|
+
? `Cursor \`/${commandName}\` command is missing and should install under .cursor/commands/.`
|
|
759
804
|
: content === expectedContent
|
|
760
|
-
? `Cursor can discover the managed \`/${
|
|
761
|
-
: `Cursor \`/${
|
|
805
|
+
? `Cursor can discover the managed \`/${commandName}\` command.`
|
|
806
|
+
: `Cursor \`/${commandName}\` command exists but no longer matches the expected managed GDH command.`,
|
|
762
807
|
version: null,
|
|
763
808
|
}),
|
|
764
809
|
];
|
|
@@ -1012,35 +1057,47 @@ async function inspectClaudeAdapter(targetPath, guidance, projectMcp, pinnedVers
|
|
|
1012
1057
|
: projectMcp.projectFile.summary,
|
|
1013
1058
|
version: null,
|
|
1014
1059
|
}));
|
|
1060
|
+
if (path.resolve(projectMcp.integrationRootPath) !== path.resolve(targetPath)) {
|
|
1061
|
+
surfaces.push(createSurfaceStatus({
|
|
1062
|
+
kind: "mcp_file",
|
|
1063
|
+
scope: "repo",
|
|
1064
|
+
targetPath,
|
|
1065
|
+
relativePath: PROJECT_MCP_RELATIVE_PATH,
|
|
1066
|
+
present: projectMcp.targetProjectFile.present,
|
|
1067
|
+
state: projectMcp.targetProjectFile.state,
|
|
1068
|
+
summary: projectMcp.targetProjectFile.summary,
|
|
1069
|
+
version: null,
|
|
1070
|
+
}));
|
|
1071
|
+
}
|
|
1015
1072
|
}
|
|
1016
1073
|
return createAgentStatus("claude", guidance, surfaces);
|
|
1017
1074
|
}
|
|
1018
1075
|
async function inspectCursorAdapter(targetPath, guidance, projectMcp, pinnedVersion) {
|
|
1019
1076
|
const absolutePath = path.join(targetPath, CURSOR_RULE_RELATIVE_PATH);
|
|
1020
|
-
const
|
|
1077
|
+
const onboardCommandPath = path.join(targetPath, CURSOR_ONBOARD_COMMAND_RELATIVE_PATH);
|
|
1021
1078
|
const content = await fs.readFile(absolutePath, "utf8").catch(() => null);
|
|
1022
|
-
const
|
|
1079
|
+
const onboardCommandContent = await fs.readFile(onboardCommandPath, "utf8").catch(() => null);
|
|
1023
1080
|
const cursorStatusContent = await fs
|
|
1024
|
-
.readFile(path.join(targetPath,
|
|
1081
|
+
.readFile(path.join(targetPath, CURSOR_STATUS_COMMAND_RELATIVE_PATH), "utf8")
|
|
1025
1082
|
.catch(() => null);
|
|
1026
1083
|
const cursorMigrateContent = await fs
|
|
1027
|
-
.readFile(path.join(targetPath,
|
|
1084
|
+
.readFile(path.join(targetPath, CURSOR_MIGRATE_COMMAND_RELATIVE_PATH), "utf8")
|
|
1028
1085
|
.catch(() => null);
|
|
1029
1086
|
// Phase 13 Plan 13-03 deliverable — /gdh-update skill for Cursor. Inspection
|
|
1030
1087
|
// wiring required so planSkillInstallAction sees the surface state; otherwise
|
|
1031
1088
|
// the planner returns `unchanged` for a missing file and install becomes a
|
|
1032
1089
|
// no-op (Plan 13-05 integration-test Rule 2 fix).
|
|
1033
1090
|
const cursorUpdateContent = await fs
|
|
1034
|
-
.readFile(path.join(targetPath,
|
|
1091
|
+
.readFile(path.join(targetPath, CURSOR_UPDATE_COMMAND_RELATIVE_PATH), "utf8")
|
|
1035
1092
|
.catch(() => null);
|
|
1036
1093
|
const cursorPrepareContent = await fs
|
|
1037
|
-
.readFile(path.join(targetPath,
|
|
1094
|
+
.readFile(path.join(targetPath, CURSOR_PREPARE_COMMAND_RELATIVE_PATH), "utf8")
|
|
1038
1095
|
.catch(() => null);
|
|
1039
1096
|
const cursorRunGameContent = await fs
|
|
1040
|
-
.readFile(path.resolve(targetPath,
|
|
1097
|
+
.readFile(path.resolve(targetPath, CURSOR_RUN_GAME_COMMAND_RELATIVE_PATH), "utf8")
|
|
1041
1098
|
.catch(() => null);
|
|
1042
1099
|
const cursorScanContent = await fs
|
|
1043
|
-
.readFile(path.join(targetPath,
|
|
1100
|
+
.readFile(path.join(targetPath, CURSOR_SCAN_COMMAND_RELATIVE_PATH), "utf8")
|
|
1044
1101
|
.catch(() => null);
|
|
1045
1102
|
const expectedContent = renderCursorRule();
|
|
1046
1103
|
const version = readCursorRuleVersion(content);
|
|
@@ -1067,33 +1124,33 @@ async function inspectCursorAdapter(targetPath, guidance, projectMcp, pinnedVers
|
|
|
1067
1124
|
version,
|
|
1068
1125
|
}),
|
|
1069
1126
|
createSurfaceStatus({
|
|
1070
|
-
kind: "
|
|
1127
|
+
kind: "command_file",
|
|
1071
1128
|
scope: "repo",
|
|
1072
1129
|
targetPath,
|
|
1073
|
-
relativePath:
|
|
1074
|
-
present:
|
|
1075
|
-
state:
|
|
1130
|
+
relativePath: CURSOR_ONBOARD_COMMAND_RELATIVE_PATH,
|
|
1131
|
+
present: onboardCommandContent !== null,
|
|
1132
|
+
state: onboardCommandContent === null
|
|
1076
1133
|
? "missing"
|
|
1077
1134
|
: expectedCursorOnboardSkill === null
|
|
1078
1135
|
? "missing"
|
|
1079
|
-
:
|
|
1136
|
+
: onboardCommandContent === expectedCursorOnboardSkill
|
|
1080
1137
|
? "ready"
|
|
1081
1138
|
: "misconfigured",
|
|
1082
|
-
summary:
|
|
1083
|
-
? "Cursor onboarding handoff is missing and should install `/gdh-onboard` under .cursor/
|
|
1139
|
+
summary: onboardCommandContent === null
|
|
1140
|
+
? "Cursor onboarding handoff is missing and should install `/gdh-onboard` under .cursor/commands/."
|
|
1084
1141
|
: expectedCursorOnboardSkill === null
|
|
1085
1142
|
? "Cursor onboarding handoff cannot be validated yet: no `gdh_version` is pinned (run `gdh setup` or `gdh migrate --apply`)."
|
|
1086
|
-
:
|
|
1087
|
-
? "Cursor can discover the managed `/gdh-onboard` handoff
|
|
1088
|
-
: "Cursor onboarding handoff exists but no longer matches the expected managed GDH
|
|
1143
|
+
: onboardCommandContent === expectedCursorOnboardSkill
|
|
1144
|
+
? "Cursor can discover the managed `/gdh-onboard` handoff command."
|
|
1145
|
+
: "Cursor onboarding handoff exists but no longer matches the expected managed GDH command.",
|
|
1089
1146
|
version: null,
|
|
1090
1147
|
}),
|
|
1091
|
-
...
|
|
1092
|
-
...
|
|
1093
|
-
...
|
|
1094
|
-
...
|
|
1095
|
-
...
|
|
1096
|
-
...
|
|
1148
|
+
...inspectCursorCommandSurface(targetPath, CURSOR_STATUS_COMMAND_RELATIVE_PATH, cursorStatusContent, expectedCursorStatusSkill, "gdh-status"),
|
|
1149
|
+
...inspectCursorCommandSurface(targetPath, CURSOR_MIGRATE_COMMAND_RELATIVE_PATH, cursorMigrateContent, expectedCursorMigrateSkill, "gdh-migrate"),
|
|
1150
|
+
...inspectCursorCommandSurface(targetPath, CURSOR_UPDATE_COMMAND_RELATIVE_PATH, cursorUpdateContent, expectedCursorUpdateSkill, "gdh-update"),
|
|
1151
|
+
...inspectCursorCommandSurface(targetPath, CURSOR_PREPARE_COMMAND_RELATIVE_PATH, cursorPrepareContent, expectedCursorPrepareSkill, "gdh-prepare"),
|
|
1152
|
+
...inspectCursorCommandSurface(targetPath, CURSOR_RUN_GAME_COMMAND_RELATIVE_PATH, cursorRunGameContent, expectedCursorRunGameSkill, "gdh-run-game"),
|
|
1153
|
+
...inspectCursorCommandSurface(targetPath, CURSOR_SCAN_COMMAND_RELATIVE_PATH, cursorScanContent, expectedCursorScanSkill, "gdh-scan"),
|
|
1097
1154
|
];
|
|
1098
1155
|
if (projectMcp.enabled) {
|
|
1099
1156
|
surfaces.push(createSurfaceStatus({
|
|
@@ -1106,6 +1163,18 @@ async function inspectCursorAdapter(targetPath, guidance, projectMcp, pinnedVers
|
|
|
1106
1163
|
summary: projectMcp.cursorFile.summary,
|
|
1107
1164
|
version: null,
|
|
1108
1165
|
}));
|
|
1166
|
+
if (path.resolve(projectMcp.integrationRootPath) !== path.resolve(targetPath)) {
|
|
1167
|
+
surfaces.push(createSurfaceStatus({
|
|
1168
|
+
kind: "mcp_file",
|
|
1169
|
+
scope: "repo",
|
|
1170
|
+
targetPath,
|
|
1171
|
+
relativePath: CURSOR_MCP_RELATIVE_PATH,
|
|
1172
|
+
present: projectMcp.targetCursorFile.present,
|
|
1173
|
+
state: projectMcp.targetCursorFile.state,
|
|
1174
|
+
summary: projectMcp.targetCursorFile.summary,
|
|
1175
|
+
version: null,
|
|
1176
|
+
}));
|
|
1177
|
+
}
|
|
1109
1178
|
}
|
|
1110
1179
|
return createAgentStatus("cursor", guidance, surfaces);
|
|
1111
1180
|
}
|
|
@@ -1198,10 +1267,15 @@ async function planInstallActions(targetPath, adapters, requestedAgents, options
|
|
|
1198
1267
|
continue;
|
|
1199
1268
|
}
|
|
1200
1269
|
if (adapter.agent === "cursor") {
|
|
1201
|
-
actions.push(...planCursorInstallActions(targetPath, adapter, options.pinnedVersion));
|
|
1270
|
+
actions.push(...planCursorInstallActions(targetPath, adapter, options.pinnedVersion, options.integrationRootPath));
|
|
1202
1271
|
}
|
|
1203
1272
|
}
|
|
1204
1273
|
actions.push(...planRetiredManagedSurfaceCleanupActions(targetPath));
|
|
1274
|
+
if (path.resolve(options.integrationRootPath) !== path.resolve(targetPath)) {
|
|
1275
|
+
actions.push(...planRetiredManagedSurfaceCleanupActions(options.integrationRootPath));
|
|
1276
|
+
actions.push(...planLegacyCodexSkillCleanupActions(options.integrationRootPath, options.pinnedVersion));
|
|
1277
|
+
actions.push(...planLegacyCursorSkillCleanupActions(options.integrationRootPath));
|
|
1278
|
+
}
|
|
1205
1279
|
return dedupeInstallActions(actions);
|
|
1206
1280
|
}
|
|
1207
1281
|
function planSharedRepoInstallActions(targetPath, projectMcp, agent, effectiveDevRepoPath, integrationRootPath, pinnedVersion) {
|
|
@@ -1209,6 +1283,8 @@ function planSharedRepoInstallActions(targetPath, projectMcp, agent, effectiveDe
|
|
|
1209
1283
|
return [];
|
|
1210
1284
|
}
|
|
1211
1285
|
const actions = [];
|
|
1286
|
+
const managedMcpEntry = buildManagedMcpServerEntry({ pinnedVersion });
|
|
1287
|
+
const nestedTarget = path.resolve(targetPath) !== path.resolve(integrationRootPath);
|
|
1212
1288
|
if (projectMcp.projectFile.state !== "ready") {
|
|
1213
1289
|
actions.push(createInstallAction({
|
|
1214
1290
|
agent,
|
|
@@ -1221,7 +1297,7 @@ function planSharedRepoInstallActions(targetPath, projectMcp, agent, effectiveDe
|
|
|
1221
1297
|
summary: projectMcp.projectFile.present
|
|
1222
1298
|
? "Replace the managed `gdh` MCP entry in .mcp.json while preserving any non-GDH servers."
|
|
1223
1299
|
: "Create .mcp.json with the managed `gdh` MCP entry.",
|
|
1224
|
-
content: renderManagedMcpConfig(path.join(integrationRootPath, PROJECT_MCP_RELATIVE_PATH),
|
|
1300
|
+
content: renderManagedMcpConfig(path.join(integrationRootPath, PROJECT_MCP_RELATIVE_PATH), managedMcpEntry),
|
|
1225
1301
|
}));
|
|
1226
1302
|
}
|
|
1227
1303
|
if (projectMcp.cursorFile.state !== "ready") {
|
|
@@ -1236,7 +1312,37 @@ function planSharedRepoInstallActions(targetPath, projectMcp, agent, effectiveDe
|
|
|
1236
1312
|
summary: projectMcp.cursorFile.present
|
|
1237
1313
|
? "Replace the managed `gdh` MCP entry in .cursor/mcp.json while preserving any non-GDH servers."
|
|
1238
1314
|
: "Create .cursor/mcp.json with the managed `gdh` MCP entry.",
|
|
1239
|
-
content: renderManagedMcpConfig(path.join(integrationRootPath, CURSOR_MCP_RELATIVE_PATH),
|
|
1315
|
+
content: renderManagedMcpConfig(path.join(integrationRootPath, CURSOR_MCP_RELATIVE_PATH), managedMcpEntry),
|
|
1316
|
+
}));
|
|
1317
|
+
}
|
|
1318
|
+
if (nestedTarget && projectMcp.targetProjectFile.state !== "ready") {
|
|
1319
|
+
actions.push(createInstallAction({
|
|
1320
|
+
agent,
|
|
1321
|
+
kind: "write_file",
|
|
1322
|
+
scope: "repo",
|
|
1323
|
+
targetPath,
|
|
1324
|
+
relativePath: PROJECT_MCP_RELATIVE_PATH,
|
|
1325
|
+
state: "planned",
|
|
1326
|
+
mode: projectMcp.targetProjectFile.present ? "replace" : "create",
|
|
1327
|
+
summary: projectMcp.targetProjectFile.present
|
|
1328
|
+
? "Replace the managed `gdh` MCP entry in target-local .mcp.json while preserving any non-GDH servers."
|
|
1329
|
+
: "Create target-local .mcp.json so Claude can discover GDH when launched from the Godot target.",
|
|
1330
|
+
content: renderManagedMcpConfig(path.join(targetPath, PROJECT_MCP_RELATIVE_PATH), managedMcpEntry),
|
|
1331
|
+
}));
|
|
1332
|
+
}
|
|
1333
|
+
if (nestedTarget && projectMcp.targetCursorFile.state !== "ready") {
|
|
1334
|
+
actions.push(createInstallAction({
|
|
1335
|
+
agent,
|
|
1336
|
+
kind: "write_file",
|
|
1337
|
+
scope: "repo",
|
|
1338
|
+
targetPath,
|
|
1339
|
+
relativePath: CURSOR_MCP_RELATIVE_PATH,
|
|
1340
|
+
state: "planned",
|
|
1341
|
+
mode: projectMcp.targetCursorFile.present ? "replace" : "create",
|
|
1342
|
+
summary: projectMcp.targetCursorFile.present
|
|
1343
|
+
? "Replace the managed `gdh` MCP entry in target-local .cursor/mcp.json while preserving any non-GDH servers."
|
|
1344
|
+
: "Create target-local .cursor/mcp.json so Cursor can discover GDH when opened at the Godot target.",
|
|
1345
|
+
content: renderManagedMcpConfig(path.join(targetPath, CURSOR_MCP_RELATIVE_PATH), managedMcpEntry),
|
|
1240
1346
|
}));
|
|
1241
1347
|
}
|
|
1242
1348
|
if (projectMcp.localPathHints.gdhDevRepoPath !== effectiveDevRepoPath) {
|
|
@@ -1296,6 +1402,35 @@ function planSkillInstallAction(agent, targetPath, adapter, relativePath, render
|
|
|
1296
1402
|
content: renderFn(pinnedVersion),
|
|
1297
1403
|
});
|
|
1298
1404
|
}
|
|
1405
|
+
function planCommandInstallAction(agent, targetPath, adapter, relativePath, renderFn, commandName, pinnedVersion) {
|
|
1406
|
+
const surface = adapter.surfaces.find((s) => s.relativePath === relativePath);
|
|
1407
|
+
if (!surface || surface.state === "ready") {
|
|
1408
|
+
return createInstallAction({
|
|
1409
|
+
agent,
|
|
1410
|
+
kind: "write_file",
|
|
1411
|
+
scope: "repo",
|
|
1412
|
+
targetPath,
|
|
1413
|
+
relativePath,
|
|
1414
|
+
state: "unchanged",
|
|
1415
|
+
mode: "unchanged",
|
|
1416
|
+
summary: `The managed ${agentLabel(agent)} \`/${commandName}\` command already matches the expected GDH content.`,
|
|
1417
|
+
content: renderFn(pinnedVersion),
|
|
1418
|
+
});
|
|
1419
|
+
}
|
|
1420
|
+
return createInstallAction({
|
|
1421
|
+
agent,
|
|
1422
|
+
kind: "write_file",
|
|
1423
|
+
scope: "repo",
|
|
1424
|
+
targetPath,
|
|
1425
|
+
relativePath,
|
|
1426
|
+
state: "planned",
|
|
1427
|
+
mode: surface.present ? "replace" : "create",
|
|
1428
|
+
summary: surface.present
|
|
1429
|
+
? `Replace the existing ${agentLabel(agent)} \`/${commandName}\` command with the managed GDH command.`
|
|
1430
|
+
: `Create the managed ${agentLabel(agent)} \`/${commandName}\` command.`,
|
|
1431
|
+
content: renderFn(pinnedVersion),
|
|
1432
|
+
});
|
|
1433
|
+
}
|
|
1299
1434
|
function planHookInstallAction(agent, targetPath, adapter, relativePath, renderFn, hookName, pinnedVersion) {
|
|
1300
1435
|
const surface = adapter.surfaces.find((entry) => entry.relativePath === relativePath);
|
|
1301
1436
|
if (!surface || surface.state === "ready") {
|
|
@@ -1369,6 +1504,15 @@ function planCodexRepoInstallActions(targetPath, adapter, pinnedVersion, project
|
|
|
1369
1504
|
content: renderManagedCodexProjectConfig(existingContent, pinnedVersion),
|
|
1370
1505
|
}));
|
|
1371
1506
|
}
|
|
1507
|
+
if (path.resolve(integrationRootPath) !== path.resolve(targetPath)) {
|
|
1508
|
+
actions.push(...planRootSymlinkActions({
|
|
1509
|
+
agent: "codex",
|
|
1510
|
+
integrationRootPath,
|
|
1511
|
+
targetPath,
|
|
1512
|
+
relativePaths: CODEX_SKILL_RELATIVE_PATHS,
|
|
1513
|
+
noun: "Codex skill",
|
|
1514
|
+
}));
|
|
1515
|
+
}
|
|
1372
1516
|
actions.push(...planLegacyCodexSkillCleanupActions(targetPath, pinnedVersion));
|
|
1373
1517
|
return actions;
|
|
1374
1518
|
}
|
|
@@ -1387,6 +1531,10 @@ function isManagedLegacyCodexSkillContent(content) {
|
|
|
1387
1531
|
return (content.includes(GDH_MANAGED_AGENT_SKILL_MARKER) ||
|
|
1388
1532
|
content.includes(LEGACY_CODEX_MANAGED_SKILL_MARKER));
|
|
1389
1533
|
}
|
|
1534
|
+
function isManagedLegacyCursorSkillContent(content) {
|
|
1535
|
+
return (content.includes(GDH_MANAGED_AGENT_SKILL_MARKER) ||
|
|
1536
|
+
content.includes(LEGACY_CURSOR_MANAGED_SKILL_MARKER));
|
|
1537
|
+
}
|
|
1390
1538
|
function isManagedLegacyClaudeCommandContent(content, skillName) {
|
|
1391
1539
|
const slashName = skillName.replace("gdh-", "gdh:");
|
|
1392
1540
|
return (content.includes(`name: ${slashName}`) &&
|
|
@@ -1471,6 +1619,7 @@ function planRetiredManagedSurfaceCleanupActions(targetPath) {
|
|
|
1471
1619
|
summary: `Remove retired managed \`${entry.skillName}\` surface. ${entry.replacementSummary}`,
|
|
1472
1620
|
}));
|
|
1473
1621
|
}
|
|
1622
|
+
actions.push(...planLegacyCursorSkillCleanupActions(targetPath));
|
|
1474
1623
|
return actions;
|
|
1475
1624
|
}
|
|
1476
1625
|
function planLegacyCodexSkillCleanupActions(targetPath, _pinnedVersion) {
|
|
@@ -1502,6 +1651,29 @@ function planLegacyCodexSkillCleanupActions(targetPath, _pinnedVersion) {
|
|
|
1502
1651
|
}
|
|
1503
1652
|
return actions;
|
|
1504
1653
|
}
|
|
1654
|
+
function planLegacyCursorSkillCleanupActions(targetPath) {
|
|
1655
|
+
const actions = [];
|
|
1656
|
+
for (const [relativePath, skillName] of LEGACY_CURSOR_SKILL_RELATIVE_PATHS) {
|
|
1657
|
+
const absolutePath = path.join(targetPath, relativePath);
|
|
1658
|
+
const content = fsSync.existsSync(absolutePath)
|
|
1659
|
+
? fsSync.readFileSync(absolutePath, "utf8")
|
|
1660
|
+
: null;
|
|
1661
|
+
if (content === null || !isManagedLegacyCursorSkillContent(content)) {
|
|
1662
|
+
continue;
|
|
1663
|
+
}
|
|
1664
|
+
actions.push(createInstallAction({
|
|
1665
|
+
agent: "cursor",
|
|
1666
|
+
kind: "remove_file",
|
|
1667
|
+
scope: "repo",
|
|
1668
|
+
targetPath,
|
|
1669
|
+
relativePath,
|
|
1670
|
+
state: "planned",
|
|
1671
|
+
mode: "delete",
|
|
1672
|
+
summary: `Remove legacy managed Cursor \`/${skillName}\` skill from .cursor/skills after installing the documented .cursor/commands location.`,
|
|
1673
|
+
}));
|
|
1674
|
+
}
|
|
1675
|
+
return actions;
|
|
1676
|
+
}
|
|
1505
1677
|
function planLegacyClaudeCommandCleanupActions(targetPath) {
|
|
1506
1678
|
const actions = [];
|
|
1507
1679
|
for (const [relativePath, skillName] of LEGACY_CLAUDE_SKILL_COMMAND_RELATIVE_PATHS) {
|
|
@@ -1705,6 +1877,22 @@ function planDirectSymlinkAction(input) {
|
|
|
1705
1877
|
expectedTarget: input.expectedTarget,
|
|
1706
1878
|
});
|
|
1707
1879
|
}
|
|
1880
|
+
function planRootSymlinkActions(input) {
|
|
1881
|
+
return input.relativePaths.map((relativePath) => {
|
|
1882
|
+
const rootLinkDirectory = path.dirname(path.join(input.integrationRootPath, relativePath));
|
|
1883
|
+
const targetFilePath = path.join(input.targetPath, relativePath);
|
|
1884
|
+
const expectedTarget = normalizePortableRelativePath(path.relative(rootLinkDirectory, targetFilePath));
|
|
1885
|
+
return planDirectSymlinkAction({
|
|
1886
|
+
agent: input.agent,
|
|
1887
|
+
targetPath: input.integrationRootPath,
|
|
1888
|
+
relativePath,
|
|
1889
|
+
expectedTarget,
|
|
1890
|
+
readySummary: `Root-launched ${input.noun} ${relativePath} already points to the target-local managed surface.`,
|
|
1891
|
+
createSummary: `Create root-launched ${input.noun} symlink ${relativePath} to the target-local managed surface.`,
|
|
1892
|
+
replaceSummary: `Replace root-launched ${input.noun} ${relativePath} with a symlink to the target-local managed surface.`,
|
|
1893
|
+
});
|
|
1894
|
+
});
|
|
1895
|
+
}
|
|
1708
1896
|
function planDirectWriteFileAction(input) {
|
|
1709
1897
|
const absolutePath = path.resolve(input.targetPath, input.relativePath);
|
|
1710
1898
|
let existingContent = null;
|
|
@@ -1758,9 +1946,10 @@ function planRootClaudeAuthoringSettingsAction(integrationRootPath) {
|
|
|
1758
1946
|
content: patchedSettings,
|
|
1759
1947
|
});
|
|
1760
1948
|
}
|
|
1761
|
-
function planCursorInstallActions(targetPath, adapter, pinnedVersion) {
|
|
1949
|
+
function planCursorInstallActions(targetPath, adapter, pinnedVersion, integrationRootPath) {
|
|
1762
1950
|
const actions = [];
|
|
1763
1951
|
const cursorRuleSurface = adapter.surfaces.find((surface) => surface.relativePath === CURSOR_RULE_RELATIVE_PATH);
|
|
1952
|
+
const targetRelativePath = path.relative(integrationRootPath, targetPath) || ".";
|
|
1764
1953
|
if (!cursorRuleSurface || cursorRuleSurface.state === "ready") {
|
|
1765
1954
|
actions.push(createInstallAction({
|
|
1766
1955
|
agent: "cursor",
|
|
@@ -1791,7 +1980,24 @@ function planCursorInstallActions(targetPath, adapter, pinnedVersion) {
|
|
|
1791
1980
|
content: renderCursorRule(),
|
|
1792
1981
|
}));
|
|
1793
1982
|
}
|
|
1794
|
-
actions.push(
|
|
1983
|
+
actions.push(planCommandInstallAction("cursor", targetPath, adapter, CURSOR_ONBOARD_COMMAND_RELATIVE_PATH, renderCursorOnboardSkill, "gdh-onboard", pinnedVersion), planCommandInstallAction("cursor", targetPath, adapter, CURSOR_STATUS_COMMAND_RELATIVE_PATH, renderCursorStatusSkill, "gdh-status", pinnedVersion), planCommandInstallAction("cursor", targetPath, adapter, CURSOR_MIGRATE_COMMAND_RELATIVE_PATH, renderCursorMigrateSkill, "gdh-migrate", pinnedVersion), planCommandInstallAction("cursor", targetPath, adapter, CURSOR_UPDATE_COMMAND_RELATIVE_PATH, renderCursorUpdateSkill, "gdh-update", pinnedVersion), planCommandInstallAction("cursor", targetPath, adapter, CURSOR_PREPARE_COMMAND_RELATIVE_PATH, renderCursorPrepareSkill, "gdh-prepare", pinnedVersion), planCommandInstallAction("cursor", targetPath, adapter, CURSOR_RUN_GAME_COMMAND_RELATIVE_PATH, renderCursorRunGameSkill, "gdh-run-game", pinnedVersion), planCommandInstallAction("cursor", targetPath, adapter, CURSOR_SCAN_COMMAND_RELATIVE_PATH, renderCursorScanSkill, "gdh-scan", pinnedVersion));
|
|
1984
|
+
if (path.resolve(integrationRootPath) !== path.resolve(targetPath)) {
|
|
1985
|
+
actions.push(planDirectWriteFileAction({
|
|
1986
|
+
agent: "cursor",
|
|
1987
|
+
targetPath: integrationRootPath,
|
|
1988
|
+
relativePath: CURSOR_RULE_RELATIVE_PATH,
|
|
1989
|
+
content: renderCursorRule({ targetRelativePath }),
|
|
1990
|
+
readySummary: "Root-launched Cursor rule already routes to the managed Godot target.",
|
|
1991
|
+
createSummary: "Create root-launched Cursor rule that routes to the managed Godot target.",
|
|
1992
|
+
replaceSummary: "Replace root-launched Cursor rule with target-aware links to the managed Godot target.",
|
|
1993
|
+
}), ...planRootSymlinkActions({
|
|
1994
|
+
agent: "cursor",
|
|
1995
|
+
integrationRootPath,
|
|
1996
|
+
targetPath,
|
|
1997
|
+
relativePaths: CURSOR_COMMAND_RELATIVE_PATHS,
|
|
1998
|
+
noun: "Cursor command",
|
|
1999
|
+
}));
|
|
2000
|
+
}
|
|
1795
2001
|
return actions;
|
|
1796
2002
|
}
|
|
1797
2003
|
function dedupeInstallActions(actions) {
|
|
@@ -1837,7 +2043,8 @@ async function applySymlinkAction(action) {
|
|
|
1837
2043
|
if (action.absolutePath === null) {
|
|
1838
2044
|
throw new Error("Symlink install action is missing an absolute path.");
|
|
1839
2045
|
}
|
|
1840
|
-
await fs.
|
|
2046
|
+
await fs.mkdir(path.dirname(action.absolutePath), { recursive: true });
|
|
2047
|
+
await fs.rm(action.absolutePath, { recursive: true, force: true });
|
|
1841
2048
|
await fs.symlink(action.expectedTarget ?? "AGENTS.md", action.absolutePath);
|
|
1842
2049
|
}
|
|
1843
2050
|
async function applyWriteFileAction(action) {
|