@skillcap/gdh 0.25.1 → 0.25.3
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 +112 -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 +298 -66
- package/node_modules/@gdh/adapters/dist/index.js.map +1 -1
- package/node_modules/@gdh/adapters/dist/skill-rendering.js +1 -1
- package/node_modules/@gdh/adapters/dist/skill-rendering.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 +2 -2
- package/node_modules/@gdh/core/dist/index.js +2 -2
- 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";
|
|
@@ -122,23 +150,23 @@ const PREVIEW_HEADER_LITERAL = "Preview (dry-run) — not applied";
|
|
|
122
150
|
const execFile = promisify(execFileCallback);
|
|
123
151
|
export async function getSupportedAgentAdaptersStatus(targetPath, options = {}) {
|
|
124
152
|
const includeUserLocal = options.includeUserLocal ?? false;
|
|
125
|
-
const
|
|
126
|
-
const guidance = await getGuidanceStatus(targetPath);
|
|
127
|
-
const pinnedVersion = await resolvePinnedVersionOrNull(targetPath);
|
|
128
|
-
const projectMcp = await inspectProjectMcpSupport(targetPath, {
|
|
153
|
+
const context = await resolveAdapterTargetContext(targetPath, options.integrationRootPath);
|
|
154
|
+
const guidance = await getGuidanceStatus(context.targetPath);
|
|
155
|
+
const pinnedVersion = await resolvePinnedVersionOrNull(context.targetPath);
|
|
156
|
+
const projectMcp = await inspectProjectMcpSupport(context.targetPath, {
|
|
129
157
|
includeUserLocal,
|
|
130
|
-
integrationRootPath,
|
|
158
|
+
integrationRootPath: context.integrationRootPath,
|
|
131
159
|
pinnedVersion,
|
|
132
160
|
});
|
|
133
161
|
const [codex, claude, cursor] = await Promise.all([
|
|
134
|
-
inspectCodexAdapter(targetPath, guidance, projectMcp, pinnedVersion, {
|
|
162
|
+
inspectCodexAdapter(context.targetPath, guidance, projectMcp, pinnedVersion, {
|
|
135
163
|
includeUserLocal,
|
|
136
164
|
}),
|
|
137
|
-
inspectClaudeAdapter(targetPath, guidance, projectMcp, pinnedVersion),
|
|
138
|
-
inspectCursorAdapter(targetPath, guidance, projectMcp, pinnedVersion),
|
|
165
|
+
inspectClaudeAdapter(context.targetPath, guidance, projectMcp, pinnedVersion),
|
|
166
|
+
inspectCursorAdapter(context.targetPath, guidance, projectMcp, pinnedVersion),
|
|
139
167
|
]);
|
|
140
168
|
return {
|
|
141
|
-
targetPath,
|
|
169
|
+
targetPath: context.targetPath,
|
|
142
170
|
guidance,
|
|
143
171
|
adapters: [codex, claude, cursor],
|
|
144
172
|
};
|
|
@@ -146,14 +174,14 @@ export async function getSupportedAgentAdaptersStatus(targetPath, options = {})
|
|
|
146
174
|
export async function installSupportedAgentAdapters(targetPath, options = {}) {
|
|
147
175
|
const dryRun = options.dryRun ?? false;
|
|
148
176
|
const requestedAgents = normalizeRequestedAgents(options.agents);
|
|
149
|
-
const
|
|
150
|
-
const status = await getSupportedAgentAdaptersStatus(targetPath, {
|
|
151
|
-
integrationRootPath,
|
|
177
|
+
const context = await resolveAdapterTargetContext(targetPath, options.integrationRootPath);
|
|
178
|
+
const status = await getSupportedAgentAdaptersStatus(context.targetPath, {
|
|
179
|
+
integrationRootPath: context.integrationRootPath,
|
|
152
180
|
});
|
|
153
|
-
const pinnedVersion = await resolvePinnedVersion(targetPath);
|
|
181
|
+
const pinnedVersion = await resolvePinnedVersion(context.targetPath);
|
|
154
182
|
if (!hasReadyVisibilityChain(status.guidance) && options.allowBootstrap !== true) {
|
|
155
183
|
return {
|
|
156
|
-
targetPath,
|
|
184
|
+
targetPath: context.targetPath,
|
|
157
185
|
dryRun,
|
|
158
186
|
state: "blocked",
|
|
159
187
|
summary: "The canonical GDH visibility chain is not ready, so agent reinforcement cannot be installed safely.",
|
|
@@ -163,10 +191,10 @@ export async function installSupportedAgentAdapters(targetPath, options = {}) {
|
|
|
163
191
|
actions: [],
|
|
164
192
|
};
|
|
165
193
|
}
|
|
166
|
-
const actions = await planInstallActions(targetPath, status.adapters, requestedAgents, {
|
|
194
|
+
const actions = await planInstallActions(context.targetPath, status.adapters, requestedAgents, {
|
|
167
195
|
user: options.user ?? false,
|
|
168
196
|
devRepoPath: options.devRepoPath ?? null,
|
|
169
|
-
integrationRootPath,
|
|
197
|
+
integrationRootPath: context.integrationRootPath,
|
|
170
198
|
pinnedVersion,
|
|
171
199
|
});
|
|
172
200
|
let appliedActions = actions;
|
|
@@ -207,7 +235,7 @@ export async function installSupportedAgentAdapters(targetPath, options = {}) {
|
|
|
207
235
|
appliedActions = newActions;
|
|
208
236
|
}
|
|
209
237
|
return {
|
|
210
|
-
targetPath,
|
|
238
|
+
targetPath: context.targetPath,
|
|
211
239
|
dryRun,
|
|
212
240
|
state: dryRun ? "planned" : "applied",
|
|
213
241
|
summary: actions.length === 0 || actions.every((action) => action.state === "unchanged")
|
|
@@ -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
|
}
|
|
@@ -1170,6 +1239,31 @@ function normalizeRequestedAgents(agents) {
|
|
|
1170
1239
|
function resolveIntegrationRootPath(targetPath, integrationRootPath) {
|
|
1171
1240
|
return path.resolve(integrationRootPath ?? targetPath);
|
|
1172
1241
|
}
|
|
1242
|
+
async function resolveAdapterTargetContext(targetPath, integrationRootPath) {
|
|
1243
|
+
const rawTargetPath = path.resolve(targetPath);
|
|
1244
|
+
const projectRoot = await resolveProjectRoot(rawTargetPath);
|
|
1245
|
+
const resolvedIntegrationRootPath = path.resolve(integrationRootPath ?? projectRoot ?? rawTargetPath);
|
|
1246
|
+
const guidance = await getGuidanceStatus(rawTargetPath).catch(() => null);
|
|
1247
|
+
if (guidance?.state === "ready") {
|
|
1248
|
+
return {
|
|
1249
|
+
targetPath: path.resolve(guidance.guidanceRootPath),
|
|
1250
|
+
integrationRootPath: path.resolve(guidance.integrationRootPath),
|
|
1251
|
+
};
|
|
1252
|
+
}
|
|
1253
|
+
const projectConfig = await readProjectConfig(resolvedIntegrationRootPath);
|
|
1254
|
+
if (projectConfig === null || projectConfig.primaryGodotProjectPath === ".") {
|
|
1255
|
+
return {
|
|
1256
|
+
targetPath: projectRoot !== null && integrationRootPath === undefined
|
|
1257
|
+
? path.resolve(projectRoot)
|
|
1258
|
+
: rawTargetPath,
|
|
1259
|
+
integrationRootPath: resolvedIntegrationRootPath,
|
|
1260
|
+
};
|
|
1261
|
+
}
|
|
1262
|
+
return {
|
|
1263
|
+
targetPath: path.resolve(resolvedIntegrationRootPath, projectConfig.primaryGodotProjectPath),
|
|
1264
|
+
integrationRootPath: resolvedIntegrationRootPath,
|
|
1265
|
+
};
|
|
1266
|
+
}
|
|
1173
1267
|
async function planInstallActions(targetPath, adapters, requestedAgents, options) {
|
|
1174
1268
|
const projectMcp = await inspectProjectMcpSupport(targetPath, {
|
|
1175
1269
|
includeUserLocal: options.user,
|
|
@@ -1198,10 +1292,15 @@ async function planInstallActions(targetPath, adapters, requestedAgents, options
|
|
|
1198
1292
|
continue;
|
|
1199
1293
|
}
|
|
1200
1294
|
if (adapter.agent === "cursor") {
|
|
1201
|
-
actions.push(...planCursorInstallActions(targetPath, adapter, options.pinnedVersion));
|
|
1295
|
+
actions.push(...planCursorInstallActions(targetPath, adapter, options.pinnedVersion, options.integrationRootPath));
|
|
1202
1296
|
}
|
|
1203
1297
|
}
|
|
1204
1298
|
actions.push(...planRetiredManagedSurfaceCleanupActions(targetPath));
|
|
1299
|
+
if (path.resolve(options.integrationRootPath) !== path.resolve(targetPath)) {
|
|
1300
|
+
actions.push(...planRetiredManagedSurfaceCleanupActions(options.integrationRootPath));
|
|
1301
|
+
actions.push(...planLegacyCodexSkillCleanupActions(options.integrationRootPath, options.pinnedVersion));
|
|
1302
|
+
actions.push(...planLegacyCursorSkillCleanupActions(options.integrationRootPath));
|
|
1303
|
+
}
|
|
1205
1304
|
return dedupeInstallActions(actions);
|
|
1206
1305
|
}
|
|
1207
1306
|
function planSharedRepoInstallActions(targetPath, projectMcp, agent, effectiveDevRepoPath, integrationRootPath, pinnedVersion) {
|
|
@@ -1209,6 +1308,8 @@ function planSharedRepoInstallActions(targetPath, projectMcp, agent, effectiveDe
|
|
|
1209
1308
|
return [];
|
|
1210
1309
|
}
|
|
1211
1310
|
const actions = [];
|
|
1311
|
+
const managedMcpEntry = buildManagedMcpServerEntry({ pinnedVersion });
|
|
1312
|
+
const nestedTarget = path.resolve(targetPath) !== path.resolve(integrationRootPath);
|
|
1212
1313
|
if (projectMcp.projectFile.state !== "ready") {
|
|
1213
1314
|
actions.push(createInstallAction({
|
|
1214
1315
|
agent,
|
|
@@ -1221,7 +1322,7 @@ function planSharedRepoInstallActions(targetPath, projectMcp, agent, effectiveDe
|
|
|
1221
1322
|
summary: projectMcp.projectFile.present
|
|
1222
1323
|
? "Replace the managed `gdh` MCP entry in .mcp.json while preserving any non-GDH servers."
|
|
1223
1324
|
: "Create .mcp.json with the managed `gdh` MCP entry.",
|
|
1224
|
-
content: renderManagedMcpConfig(path.join(integrationRootPath, PROJECT_MCP_RELATIVE_PATH),
|
|
1325
|
+
content: renderManagedMcpConfig(path.join(integrationRootPath, PROJECT_MCP_RELATIVE_PATH), managedMcpEntry),
|
|
1225
1326
|
}));
|
|
1226
1327
|
}
|
|
1227
1328
|
if (projectMcp.cursorFile.state !== "ready") {
|
|
@@ -1236,7 +1337,37 @@ function planSharedRepoInstallActions(targetPath, projectMcp, agent, effectiveDe
|
|
|
1236
1337
|
summary: projectMcp.cursorFile.present
|
|
1237
1338
|
? "Replace the managed `gdh` MCP entry in .cursor/mcp.json while preserving any non-GDH servers."
|
|
1238
1339
|
: "Create .cursor/mcp.json with the managed `gdh` MCP entry.",
|
|
1239
|
-
content: renderManagedMcpConfig(path.join(integrationRootPath, CURSOR_MCP_RELATIVE_PATH),
|
|
1340
|
+
content: renderManagedMcpConfig(path.join(integrationRootPath, CURSOR_MCP_RELATIVE_PATH), managedMcpEntry),
|
|
1341
|
+
}));
|
|
1342
|
+
}
|
|
1343
|
+
if (nestedTarget && projectMcp.targetProjectFile.state !== "ready") {
|
|
1344
|
+
actions.push(createInstallAction({
|
|
1345
|
+
agent,
|
|
1346
|
+
kind: "write_file",
|
|
1347
|
+
scope: "repo",
|
|
1348
|
+
targetPath,
|
|
1349
|
+
relativePath: PROJECT_MCP_RELATIVE_PATH,
|
|
1350
|
+
state: "planned",
|
|
1351
|
+
mode: projectMcp.targetProjectFile.present ? "replace" : "create",
|
|
1352
|
+
summary: projectMcp.targetProjectFile.present
|
|
1353
|
+
? "Replace the managed `gdh` MCP entry in target-local .mcp.json while preserving any non-GDH servers."
|
|
1354
|
+
: "Create target-local .mcp.json so Claude can discover GDH when launched from the Godot target.",
|
|
1355
|
+
content: renderManagedMcpConfig(path.join(targetPath, PROJECT_MCP_RELATIVE_PATH), managedMcpEntry),
|
|
1356
|
+
}));
|
|
1357
|
+
}
|
|
1358
|
+
if (nestedTarget && projectMcp.targetCursorFile.state !== "ready") {
|
|
1359
|
+
actions.push(createInstallAction({
|
|
1360
|
+
agent,
|
|
1361
|
+
kind: "write_file",
|
|
1362
|
+
scope: "repo",
|
|
1363
|
+
targetPath,
|
|
1364
|
+
relativePath: CURSOR_MCP_RELATIVE_PATH,
|
|
1365
|
+
state: "planned",
|
|
1366
|
+
mode: projectMcp.targetCursorFile.present ? "replace" : "create",
|
|
1367
|
+
summary: projectMcp.targetCursorFile.present
|
|
1368
|
+
? "Replace the managed `gdh` MCP entry in target-local .cursor/mcp.json while preserving any non-GDH servers."
|
|
1369
|
+
: "Create target-local .cursor/mcp.json so Cursor can discover GDH when opened at the Godot target.",
|
|
1370
|
+
content: renderManagedMcpConfig(path.join(targetPath, CURSOR_MCP_RELATIVE_PATH), managedMcpEntry),
|
|
1240
1371
|
}));
|
|
1241
1372
|
}
|
|
1242
1373
|
if (projectMcp.localPathHints.gdhDevRepoPath !== effectiveDevRepoPath) {
|
|
@@ -1296,6 +1427,35 @@ function planSkillInstallAction(agent, targetPath, adapter, relativePath, render
|
|
|
1296
1427
|
content: renderFn(pinnedVersion),
|
|
1297
1428
|
});
|
|
1298
1429
|
}
|
|
1430
|
+
function planCommandInstallAction(agent, targetPath, adapter, relativePath, renderFn, commandName, pinnedVersion) {
|
|
1431
|
+
const surface = adapter.surfaces.find((s) => s.relativePath === relativePath);
|
|
1432
|
+
if (!surface || surface.state === "ready") {
|
|
1433
|
+
return createInstallAction({
|
|
1434
|
+
agent,
|
|
1435
|
+
kind: "write_file",
|
|
1436
|
+
scope: "repo",
|
|
1437
|
+
targetPath,
|
|
1438
|
+
relativePath,
|
|
1439
|
+
state: "unchanged",
|
|
1440
|
+
mode: "unchanged",
|
|
1441
|
+
summary: `The managed ${agentLabel(agent)} \`/${commandName}\` command already matches the expected GDH content.`,
|
|
1442
|
+
content: renderFn(pinnedVersion),
|
|
1443
|
+
});
|
|
1444
|
+
}
|
|
1445
|
+
return createInstallAction({
|
|
1446
|
+
agent,
|
|
1447
|
+
kind: "write_file",
|
|
1448
|
+
scope: "repo",
|
|
1449
|
+
targetPath,
|
|
1450
|
+
relativePath,
|
|
1451
|
+
state: "planned",
|
|
1452
|
+
mode: surface.present ? "replace" : "create",
|
|
1453
|
+
summary: surface.present
|
|
1454
|
+
? `Replace the existing ${agentLabel(agent)} \`/${commandName}\` command with the managed GDH command.`
|
|
1455
|
+
: `Create the managed ${agentLabel(agent)} \`/${commandName}\` command.`,
|
|
1456
|
+
content: renderFn(pinnedVersion),
|
|
1457
|
+
});
|
|
1458
|
+
}
|
|
1299
1459
|
function planHookInstallAction(agent, targetPath, adapter, relativePath, renderFn, hookName, pinnedVersion) {
|
|
1300
1460
|
const surface = adapter.surfaces.find((entry) => entry.relativePath === relativePath);
|
|
1301
1461
|
if (!surface || surface.state === "ready") {
|
|
@@ -1369,6 +1529,15 @@ function planCodexRepoInstallActions(targetPath, adapter, pinnedVersion, project
|
|
|
1369
1529
|
content: renderManagedCodexProjectConfig(existingContent, pinnedVersion),
|
|
1370
1530
|
}));
|
|
1371
1531
|
}
|
|
1532
|
+
if (path.resolve(integrationRootPath) !== path.resolve(targetPath)) {
|
|
1533
|
+
actions.push(...planRootSymlinkActions({
|
|
1534
|
+
agent: "codex",
|
|
1535
|
+
integrationRootPath,
|
|
1536
|
+
targetPath,
|
|
1537
|
+
relativePaths: CODEX_SKILL_RELATIVE_PATHS,
|
|
1538
|
+
noun: "Codex skill",
|
|
1539
|
+
}));
|
|
1540
|
+
}
|
|
1372
1541
|
actions.push(...planLegacyCodexSkillCleanupActions(targetPath, pinnedVersion));
|
|
1373
1542
|
return actions;
|
|
1374
1543
|
}
|
|
@@ -1387,6 +1556,10 @@ function isManagedLegacyCodexSkillContent(content) {
|
|
|
1387
1556
|
return (content.includes(GDH_MANAGED_AGENT_SKILL_MARKER) ||
|
|
1388
1557
|
content.includes(LEGACY_CODEX_MANAGED_SKILL_MARKER));
|
|
1389
1558
|
}
|
|
1559
|
+
function isManagedLegacyCursorSkillContent(content) {
|
|
1560
|
+
return (content.includes(GDH_MANAGED_AGENT_SKILL_MARKER) ||
|
|
1561
|
+
content.includes(LEGACY_CURSOR_MANAGED_SKILL_MARKER));
|
|
1562
|
+
}
|
|
1390
1563
|
function isManagedLegacyClaudeCommandContent(content, skillName) {
|
|
1391
1564
|
const slashName = skillName.replace("gdh-", "gdh:");
|
|
1392
1565
|
return (content.includes(`name: ${slashName}`) &&
|
|
@@ -1471,6 +1644,7 @@ function planRetiredManagedSurfaceCleanupActions(targetPath) {
|
|
|
1471
1644
|
summary: `Remove retired managed \`${entry.skillName}\` surface. ${entry.replacementSummary}`,
|
|
1472
1645
|
}));
|
|
1473
1646
|
}
|
|
1647
|
+
actions.push(...planLegacyCursorSkillCleanupActions(targetPath));
|
|
1474
1648
|
return actions;
|
|
1475
1649
|
}
|
|
1476
1650
|
function planLegacyCodexSkillCleanupActions(targetPath, _pinnedVersion) {
|
|
@@ -1502,6 +1676,29 @@ function planLegacyCodexSkillCleanupActions(targetPath, _pinnedVersion) {
|
|
|
1502
1676
|
}
|
|
1503
1677
|
return actions;
|
|
1504
1678
|
}
|
|
1679
|
+
function planLegacyCursorSkillCleanupActions(targetPath) {
|
|
1680
|
+
const actions = [];
|
|
1681
|
+
for (const [relativePath, skillName] of LEGACY_CURSOR_SKILL_RELATIVE_PATHS) {
|
|
1682
|
+
const absolutePath = path.join(targetPath, relativePath);
|
|
1683
|
+
const content = fsSync.existsSync(absolutePath)
|
|
1684
|
+
? fsSync.readFileSync(absolutePath, "utf8")
|
|
1685
|
+
: null;
|
|
1686
|
+
if (content === null || !isManagedLegacyCursorSkillContent(content)) {
|
|
1687
|
+
continue;
|
|
1688
|
+
}
|
|
1689
|
+
actions.push(createInstallAction({
|
|
1690
|
+
agent: "cursor",
|
|
1691
|
+
kind: "remove_file",
|
|
1692
|
+
scope: "repo",
|
|
1693
|
+
targetPath,
|
|
1694
|
+
relativePath,
|
|
1695
|
+
state: "planned",
|
|
1696
|
+
mode: "delete",
|
|
1697
|
+
summary: `Remove legacy managed Cursor \`/${skillName}\` skill from .cursor/skills after installing the documented .cursor/commands location.`,
|
|
1698
|
+
}));
|
|
1699
|
+
}
|
|
1700
|
+
return actions;
|
|
1701
|
+
}
|
|
1505
1702
|
function planLegacyClaudeCommandCleanupActions(targetPath) {
|
|
1506
1703
|
const actions = [];
|
|
1507
1704
|
for (const [relativePath, skillName] of LEGACY_CLAUDE_SKILL_COMMAND_RELATIVE_PATHS) {
|
|
@@ -1705,6 +1902,22 @@ function planDirectSymlinkAction(input) {
|
|
|
1705
1902
|
expectedTarget: input.expectedTarget,
|
|
1706
1903
|
});
|
|
1707
1904
|
}
|
|
1905
|
+
function planRootSymlinkActions(input) {
|
|
1906
|
+
return input.relativePaths.map((relativePath) => {
|
|
1907
|
+
const rootLinkDirectory = path.dirname(path.join(input.integrationRootPath, relativePath));
|
|
1908
|
+
const targetFilePath = path.join(input.targetPath, relativePath);
|
|
1909
|
+
const expectedTarget = normalizePortableRelativePath(path.relative(rootLinkDirectory, targetFilePath));
|
|
1910
|
+
return planDirectSymlinkAction({
|
|
1911
|
+
agent: input.agent,
|
|
1912
|
+
targetPath: input.integrationRootPath,
|
|
1913
|
+
relativePath,
|
|
1914
|
+
expectedTarget,
|
|
1915
|
+
readySummary: `Root-launched ${input.noun} ${relativePath} already points to the target-local managed surface.`,
|
|
1916
|
+
createSummary: `Create root-launched ${input.noun} symlink ${relativePath} to the target-local managed surface.`,
|
|
1917
|
+
replaceSummary: `Replace root-launched ${input.noun} ${relativePath} with a symlink to the target-local managed surface.`,
|
|
1918
|
+
});
|
|
1919
|
+
});
|
|
1920
|
+
}
|
|
1708
1921
|
function planDirectWriteFileAction(input) {
|
|
1709
1922
|
const absolutePath = path.resolve(input.targetPath, input.relativePath);
|
|
1710
1923
|
let existingContent = null;
|
|
@@ -1758,9 +1971,10 @@ function planRootClaudeAuthoringSettingsAction(integrationRootPath) {
|
|
|
1758
1971
|
content: patchedSettings,
|
|
1759
1972
|
});
|
|
1760
1973
|
}
|
|
1761
|
-
function planCursorInstallActions(targetPath, adapter, pinnedVersion) {
|
|
1974
|
+
function planCursorInstallActions(targetPath, adapter, pinnedVersion, integrationRootPath) {
|
|
1762
1975
|
const actions = [];
|
|
1763
1976
|
const cursorRuleSurface = adapter.surfaces.find((surface) => surface.relativePath === CURSOR_RULE_RELATIVE_PATH);
|
|
1977
|
+
const targetRelativePath = path.relative(integrationRootPath, targetPath) || ".";
|
|
1764
1978
|
if (!cursorRuleSurface || cursorRuleSurface.state === "ready") {
|
|
1765
1979
|
actions.push(createInstallAction({
|
|
1766
1980
|
agent: "cursor",
|
|
@@ -1791,7 +2005,24 @@ function planCursorInstallActions(targetPath, adapter, pinnedVersion) {
|
|
|
1791
2005
|
content: renderCursorRule(),
|
|
1792
2006
|
}));
|
|
1793
2007
|
}
|
|
1794
|
-
actions.push(
|
|
2008
|
+
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));
|
|
2009
|
+
if (path.resolve(integrationRootPath) !== path.resolve(targetPath)) {
|
|
2010
|
+
actions.push(planDirectWriteFileAction({
|
|
2011
|
+
agent: "cursor",
|
|
2012
|
+
targetPath: integrationRootPath,
|
|
2013
|
+
relativePath: CURSOR_RULE_RELATIVE_PATH,
|
|
2014
|
+
content: renderCursorRule({ targetRelativePath }),
|
|
2015
|
+
readySummary: "Root-launched Cursor rule already routes to the managed Godot target.",
|
|
2016
|
+
createSummary: "Create root-launched Cursor rule that routes to the managed Godot target.",
|
|
2017
|
+
replaceSummary: "Replace root-launched Cursor rule with target-aware links to the managed Godot target.",
|
|
2018
|
+
}), ...planRootSymlinkActions({
|
|
2019
|
+
agent: "cursor",
|
|
2020
|
+
integrationRootPath,
|
|
2021
|
+
targetPath,
|
|
2022
|
+
relativePaths: CURSOR_COMMAND_RELATIVE_PATHS,
|
|
2023
|
+
noun: "Cursor command",
|
|
2024
|
+
}));
|
|
2025
|
+
}
|
|
1795
2026
|
return actions;
|
|
1796
2027
|
}
|
|
1797
2028
|
function dedupeInstallActions(actions) {
|
|
@@ -1837,7 +2068,8 @@ async function applySymlinkAction(action) {
|
|
|
1837
2068
|
if (action.absolutePath === null) {
|
|
1838
2069
|
throw new Error("Symlink install action is missing an absolute path.");
|
|
1839
2070
|
}
|
|
1840
|
-
await fs.
|
|
2071
|
+
await fs.mkdir(path.dirname(action.absolutePath), { recursive: true });
|
|
2072
|
+
await fs.rm(action.absolutePath, { recursive: true, force: true });
|
|
1841
2073
|
await fs.symlink(action.expectedTarget ?? "AGENTS.md", action.absolutePath);
|
|
1842
2074
|
}
|
|
1843
2075
|
async function applyWriteFileAction(action) {
|