@skillcap/gdh 0.5.0 → 0.7.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/INSTALL-BUNDLE.json +1 -1
- package/README.md +66 -85
- package/node_modules/@gdh/adapters/dist/claude-settings-patch.d.ts +74 -0
- package/node_modules/@gdh/adapters/dist/claude-settings-patch.d.ts.map +1 -0
- package/node_modules/@gdh/adapters/dist/claude-settings-patch.js +158 -0
- package/node_modules/@gdh/adapters/dist/claude-settings-patch.js.map +1 -0
- package/node_modules/@gdh/adapters/dist/claude-statusline-render.d.ts +51 -0
- package/node_modules/@gdh/adapters/dist/claude-statusline-render.d.ts.map +1 -0
- package/node_modules/@gdh/adapters/dist/claude-statusline-render.js +80 -0
- package/node_modules/@gdh/adapters/dist/claude-statusline-render.js.map +1 -0
- package/node_modules/@gdh/adapters/dist/claude-update-hook-render.d.ts +35 -0
- package/node_modules/@gdh/adapters/dist/claude-update-hook-render.d.ts.map +1 -0
- package/node_modules/@gdh/adapters/dist/claude-update-hook-render.js +76 -0
- package/node_modules/@gdh/adapters/dist/claude-update-hook-render.js.map +1 -0
- package/node_modules/@gdh/adapters/dist/claude-update-worker-render.d.ts +28 -0
- package/node_modules/@gdh/adapters/dist/claude-update-worker-render.d.ts.map +1 -0
- package/node_modules/@gdh/adapters/dist/claude-update-worker-render.js +99 -0
- package/node_modules/@gdh/adapters/dist/claude-update-worker-render.js.map +1 -0
- package/node_modules/@gdh/adapters/dist/index.d.ts +12 -2
- package/node_modules/@gdh/adapters/dist/index.d.ts.map +1 -1
- package/node_modules/@gdh/adapters/dist/index.js +382 -244
- package/node_modules/@gdh/adapters/dist/index.js.map +1 -1
- package/node_modules/@gdh/adapters/dist/self-update-mechanics.d.ts +51 -0
- package/node_modules/@gdh/adapters/dist/self-update-mechanics.d.ts.map +1 -0
- package/node_modules/@gdh/adapters/dist/self-update-mechanics.js +155 -0
- package/node_modules/@gdh/adapters/dist/self-update-mechanics.js.map +1 -0
- package/node_modules/@gdh/adapters/package.json +8 -8
- package/node_modules/@gdh/authoring/dist/index.d.ts +1 -0
- package/node_modules/@gdh/authoring/dist/index.d.ts.map +1 -1
- package/node_modules/@gdh/authoring/dist/index.js +1 -0
- package/node_modules/@gdh/authoring/dist/index.js.map +1 -1
- package/node_modules/@gdh/authoring/dist/writePinnedVersion.d.ts +17 -0
- package/node_modules/@gdh/authoring/dist/writePinnedVersion.d.ts.map +1 -0
- package/node_modules/@gdh/authoring/dist/writePinnedVersion.js +50 -0
- package/node_modules/@gdh/authoring/dist/writePinnedVersion.js.map +1 -0
- package/node_modules/@gdh/authoring/package.json +5 -2
- package/node_modules/@gdh/cli/dist/index.d.ts +15 -0
- package/node_modules/@gdh/cli/dist/index.d.ts.map +1 -1
- package/node_modules/@gdh/cli/dist/index.js +119 -20
- package/node_modules/@gdh/cli/dist/index.js.map +1 -1
- package/node_modules/@gdh/cli/dist/migrate.d.ts +1 -1
- package/node_modules/@gdh/cli/dist/migrate.d.ts.map +1 -1
- package/node_modules/@gdh/cli/dist/migrate.js +44 -3
- package/node_modules/@gdh/cli/dist/migrate.js.map +1 -1
- package/node_modules/@gdh/cli/dist/self-update.d.ts +3 -0
- package/node_modules/@gdh/cli/dist/self-update.d.ts.map +1 -0
- package/node_modules/@gdh/cli/dist/self-update.js +235 -0
- package/node_modules/@gdh/cli/dist/self-update.js.map +1 -0
- package/node_modules/@gdh/cli/dist/update-banner.d.ts +42 -0
- package/node_modules/@gdh/cli/dist/update-banner.d.ts.map +1 -0
- package/node_modules/@gdh/cli/dist/update-banner.js +49 -0
- package/node_modules/@gdh/cli/dist/update-banner.js.map +1 -0
- package/node_modules/@gdh/cli/package.json +10 -10
- package/node_modules/@gdh/core/dist/dev-mode.d.ts +13 -0
- package/node_modules/@gdh/core/dist/dev-mode.d.ts.map +1 -0
- package/node_modules/@gdh/core/dist/dev-mode.js +21 -0
- package/node_modules/@gdh/core/dist/dev-mode.js.map +1 -0
- package/node_modules/@gdh/core/dist/index.d.ts +9 -4
- package/node_modules/@gdh/core/dist/index.d.ts.map +1 -1
- package/node_modules/@gdh/core/dist/index.js +8 -5
- package/node_modules/@gdh/core/dist/index.js.map +1 -1
- package/node_modules/@gdh/core/dist/update-cache.d.ts +46 -0
- package/node_modules/@gdh/core/dist/update-cache.d.ts.map +1 -0
- package/node_modules/@gdh/core/dist/update-cache.js +90 -0
- package/node_modules/@gdh/core/dist/update-cache.js.map +1 -0
- package/node_modules/@gdh/core/dist/update-probe.d.ts +102 -0
- package/node_modules/@gdh/core/dist/update-probe.d.ts.map +1 -0
- package/node_modules/@gdh/core/dist/update-probe.js +195 -0
- package/node_modules/@gdh/core/dist/update-probe.js.map +1 -0
- package/node_modules/@gdh/core/package.json +1 -1
- package/node_modules/@gdh/docs/package.json +2 -2
- package/node_modules/@gdh/mcp/dist/index.d.ts +20 -0
- package/node_modules/@gdh/mcp/dist/index.d.ts.map +1 -1
- package/node_modules/@gdh/mcp/dist/index.js +39 -2
- package/node_modules/@gdh/mcp/dist/index.js.map +1 -1
- package/node_modules/@gdh/mcp/package.json +8 -8
- package/node_modules/@gdh/observability/dist/guidance-audit.js +2 -1
- package/node_modules/@gdh/observability/dist/guidance-audit.js.map +1 -1
- 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
|
@@ -5,7 +5,11 @@ import os from "node:os";
|
|
|
5
5
|
import path from "node:path";
|
|
6
6
|
import { promisify } from "node:util";
|
|
7
7
|
import { readProjectConfig, resolvePinnedVersion, resolvePinnedVersionOrNull, resolveProjectRoot, readWorktreeState, resolveAuthoringStatus, } from "@gdh/authoring";
|
|
8
|
-
import {
|
|
8
|
+
import { CLAUDE_CHECK_UPDATE_HOOK_RELATIVE_PATH, CLAUDE_CHECK_UPDATE_WORKER_RELATIVE_PATH, renderClaudeCheckUpdateHook, } from "./claude-update-hook-render.js";
|
|
9
|
+
import { renderClaudeCheckUpdateWorker } from "./claude-update-worker-render.js";
|
|
10
|
+
import { CLAUDE_STATUSLINE_RELATIVE_PATH, renderClaudeUpdateStatusline, } from "./claude-statusline-render.js";
|
|
11
|
+
import { CLAUDE_SETTINGS_RELATIVE_PATH, patchClaudeSettingsForGdhSessionStart, patchClaudeSettingsForGdhStatusline, } from "./claude-settings-patch.js";
|
|
12
|
+
import { GDH_AGENT_CONTRACT_VERSION, GDH_CURSOR_RULE_VERSION, GDH_GUIDANCE_INDEX_VERSION, GDH_GUIDANCE_UNIT_VERSION, GDH_PROJECT_CONFIG_VERSION, GDH_RECIPE_SCHEMA_VERSION, GDH_RULES_SCHEMA_VERSION, GDH_SCENARIO_SCHEMA_VERSION, definePackageBoundary, resolveCurrentGdhInstall, resolveGdhProductMetadata, } from "@gdh/core";
|
|
9
13
|
import { createDefaultGuidanceUnits, getGuidanceStatus, resolveGuidanceQuery, } from "@gdh/docs";
|
|
10
14
|
import { inspectGuidanceAudit } from "@gdh/observability";
|
|
11
15
|
import { inspectRuntimeBridgeSurface } from "@gdh/runtime";
|
|
@@ -48,11 +52,20 @@ export const CURSOR_MIGRATE_SKILL_RELATIVE_PATH = ".cursor/skills/gdh-migrate/SK
|
|
|
48
52
|
export const CURSOR_CHECK_SKILL_RELATIVE_PATH = ".cursor/skills/gdh-check/SKILL.md";
|
|
49
53
|
export const CURSOR_PREPARE_SKILL_RELATIVE_PATH = ".cursor/skills/gdh-prepare/SKILL.md";
|
|
50
54
|
export const CURSOR_VERIFY_SKILL_RELATIVE_PATH = ".cursor/skills/gdh-verify/SKILL.md";
|
|
55
|
+
// Phase 13 SELF-01: /gdh-update skill surface path constants. The rendered
|
|
56
|
+
// bodies shell out to `npx -y @skillcap/gdh@latest self-update` (LITERAL
|
|
57
|
+
// @latest, not the pinned version — D-10) so the NEW CLI performs the update,
|
|
58
|
+
// not the (potentially pre-Phase-12) OLD pinned one. These constants are
|
|
59
|
+
// INTENTIONALLY excluded from VERIFY_DRIFT_SCANNED_FILES (D-13) because the
|
|
60
|
+
// rendered bodies are version-agnostic by design.
|
|
61
|
+
export const CLAUDE_UPDATE_COMMAND_RELATIVE_PATH = ".claude/commands/gdh/update.md";
|
|
62
|
+
export const CODEX_UPDATE_SKILL_RELATIVE_PATH = ".codex/skills/gdh-update/SKILL.md";
|
|
63
|
+
export const CURSOR_UPDATE_SKILL_RELATIVE_PATH = ".cursor/skills/gdh-update/SKILL.md";
|
|
51
64
|
export const CLAUDE_SCAN_COMMAND_RELATIVE_PATH = ".claude/commands/gdh/scan.md";
|
|
52
65
|
export const CODEX_SCAN_SKILL_RELATIVE_PATH = ".codex/skills/gdh-scan/SKILL.md";
|
|
53
66
|
export const CURSOR_SCAN_SKILL_RELATIVE_PATH = ".cursor/skills/gdh-scan/SKILL.md";
|
|
54
67
|
export const LOCAL_PATH_HINTS_RELATIVE_PATH = ".gdh-state/local-paths.json";
|
|
55
|
-
export const
|
|
68
|
+
export const CODEX_PROJECT_CONFIG_RELATIVE_PATH = ".codex/config.toml";
|
|
56
69
|
export const GDH_MCP_SERVER_NAME = "gdh";
|
|
57
70
|
const execFile = promisify(execFileCallback);
|
|
58
71
|
export async function getSupportedAgentAdaptersStatus(targetPath, options = {}) {
|
|
@@ -203,7 +216,6 @@ export async function inspectProjectLifecycleCompatibility(targetPath) {
|
|
|
203
216
|
await inspectGuidanceUnitLifecycleSurface(resolvedTargetPath, projectConfig),
|
|
204
217
|
inspectMcpManifestLifecycleSurface(resolvedTargetPath, projectConfig.mcp.enabled, adapterStatus),
|
|
205
218
|
inspectCursorRuleLifecycleSurface(resolvedTargetPath, adapterStatus),
|
|
206
|
-
inspectMcpLauncherLifecycleSurface(resolvedTargetPath, projectConfig.mcp.enabled, adapterStatus),
|
|
207
219
|
inspectRuntimeBridgeLifecycleSurface(resolvedTargetPath, bridgeStatus),
|
|
208
220
|
]);
|
|
209
221
|
}
|
|
@@ -774,6 +786,133 @@ export function renderCursorMigrateSkill(pinnedVersion) {
|
|
|
774
786
|
"",
|
|
775
787
|
].join("\n");
|
|
776
788
|
}
|
|
789
|
+
// --- gdh-update skill renders (Phase 13 SELF-01) ---
|
|
790
|
+
//
|
|
791
|
+
// D-10 invariant: every rendered body shells out to the LITERAL string
|
|
792
|
+
// `@skillcap/gdh@latest` in every npx line (dry-run, apply, verify drift).
|
|
793
|
+
// The `pinnedVersion` parameter is accepted for planSkillInstallAction
|
|
794
|
+
// signature symmetry with every other renderer, but MUST NOT be interpolated
|
|
795
|
+
// into the shellout — running the OLD pinned CLI (potentially pre-Phase-12
|
|
796
|
+
// and lacking bumpAndRebakePin entirely) to perform its own update is the
|
|
797
|
+
// failure mode Phase 13 exists to close. Check 44 in scripts/validate-docs.mjs
|
|
798
|
+
// enforces both the @latest presence AND the @${pinnedVersion} absence.
|
|
799
|
+
//
|
|
800
|
+
// D-11: preview-then-apply flow WITHOUT a confirmation gate — the human's
|
|
801
|
+
// original intent ("update GDH") IS the approval. No AskUserQuestion for
|
|
802
|
+
// Claude; no `## User questions` H2 for Codex/Cursor. Differs from /gdh-migrate
|
|
803
|
+
// which DOES gate on explicit approval.
|
|
804
|
+
//
|
|
805
|
+
// D-12: accept an optional positional version forwarded verbatim to the CLI.
|
|
806
|
+
// `/gdh-update` = latest; `/gdh-update 0.6.0` = pin that version.
|
|
807
|
+
//
|
|
808
|
+
// D-13: the three rendered skill files are EXCLUDED from VERIFY_DRIFT_SCANNED_FILES
|
|
809
|
+
// because @latest is not a semver literal — the baked-version scanner would
|
|
810
|
+
// permanently report no_baked_version. See rationale comments adjacent to the
|
|
811
|
+
// VERIFY_DRIFT_SCANNED_FILES declaration in packages/cli/src/index.ts.
|
|
812
|
+
export function renderClaudeUpdateCommand(_pinnedVersion) {
|
|
813
|
+
return [
|
|
814
|
+
"---",
|
|
815
|
+
"name: gdh:update",
|
|
816
|
+
"description: Update GDH to npm latest (bump pinned version + re-bake managed surfaces)",
|
|
817
|
+
"allowed-tools:",
|
|
818
|
+
" - Read",
|
|
819
|
+
" - Bash",
|
|
820
|
+
"---",
|
|
821
|
+
"<objective>",
|
|
822
|
+
"Update this project's GDH pinning to npm latest and re-bake every managed surface at the new pin.",
|
|
823
|
+
"Treat any positional argument (e.g., `/gdh-update 0.6.0`) as an explicit version; otherwise default to npm latest.",
|
|
824
|
+
"</objective>",
|
|
825
|
+
"",
|
|
826
|
+
"<process>",
|
|
827
|
+
"Follow this order:",
|
|
828
|
+
"",
|
|
829
|
+
"1. Preview: run `npx -y @skillcap/gdh@latest self-update [version] --dry-run` (omit `[version]` to preview against npm latest).",
|
|
830
|
+
"2. Surface the planned version delta and re-bake action count to the human conversationally.",
|
|
831
|
+
"3. Apply: run `npx -y @skillcap/gdh@latest self-update [version]` (omit `[version]` to apply against npm latest; forward whatever positional the user passed to `/gdh-update`).",
|
|
832
|
+
"4. Verify: run `npx -y @skillcap/gdh@latest verify drift` to confirm every baked surface matches the new pin.",
|
|
833
|
+
"</process>",
|
|
834
|
+
"",
|
|
835
|
+
"<rules>",
|
|
836
|
+
"- Do NOT ask the user to confirm apply — their original intent (\"update GDH\") IS the approval.",
|
|
837
|
+
"- Bake literal `@latest` in the shellout so the new CLI performs the update, not the old one.",
|
|
838
|
+
"- If `self-update` returns `rolled_back` or `blocked`, surface the failure reason and stop (do not retry).",
|
|
839
|
+
"- If `self-update` returns `skipped_dev_mode`, explain that dev-from-source mode bypasses pinning; no action needed.",
|
|
840
|
+
"</rules>",
|
|
841
|
+
"",
|
|
842
|
+
].join("\n");
|
|
843
|
+
}
|
|
844
|
+
export function renderCodexUpdateSkill(_pinnedVersion) {
|
|
845
|
+
return [
|
|
846
|
+
"---",
|
|
847
|
+
'name: "gdh-update"',
|
|
848
|
+
'description: "Update GDH to npm latest (bump pinned version + re-bake managed surfaces)"',
|
|
849
|
+
"metadata:",
|
|
850
|
+
' short-description: "Update GDH to npm latest"',
|
|
851
|
+
"---",
|
|
852
|
+
"",
|
|
853
|
+
"<codex_skill_adapter>",
|
|
854
|
+
"## Invocation",
|
|
855
|
+
"- This skill is invoked when the user says `/gdh-update` or mentions `$gdh-update`.",
|
|
856
|
+
"- Treat any positional argument (e.g., `/gdh-update 0.6.0`) as an explicit version; otherwise default to npm latest.",
|
|
857
|
+
"</codex_skill_adapter>",
|
|
858
|
+
"",
|
|
859
|
+
"<objective>",
|
|
860
|
+
"Update this project's GDH pinning to npm latest and re-bake every managed surface at the new pin.",
|
|
861
|
+
"</objective>",
|
|
862
|
+
"",
|
|
863
|
+
"<process>",
|
|
864
|
+
"Follow this order:",
|
|
865
|
+
"",
|
|
866
|
+
"1. Preview: run `npx -y @skillcap/gdh@latest self-update [version] --dry-run` (omit `[version]` to preview against npm latest).",
|
|
867
|
+
"2. Surface the planned version delta and re-bake action count to the human conversationally.",
|
|
868
|
+
"3. Apply: run `npx -y @skillcap/gdh@latest self-update [version]` (omit `[version]` to apply against npm latest; forward whatever positional the user passed to `/gdh-update`).",
|
|
869
|
+
"4. Verify: run `npx -y @skillcap/gdh@latest verify drift` to confirm every baked surface matches the new pin.",
|
|
870
|
+
"</process>",
|
|
871
|
+
"",
|
|
872
|
+
"<rules>",
|
|
873
|
+
"- Do NOT ask the user to confirm apply — their original intent (\"update GDH\") IS the approval.",
|
|
874
|
+
"- Bake literal `@latest` in the shellout so the new CLI performs the update, not the old one.",
|
|
875
|
+
"- If `self-update` returns `rolled_back` or `blocked`, surface the failure reason and stop (do not retry).",
|
|
876
|
+
"- If `self-update` returns `skipped_dev_mode`, explain that dev-from-source mode bypasses pinning; no action needed.",
|
|
877
|
+
"</rules>",
|
|
878
|
+
"",
|
|
879
|
+
].join("\n");
|
|
880
|
+
}
|
|
881
|
+
export function renderCursorUpdateSkill(_pinnedVersion) {
|
|
882
|
+
return [
|
|
883
|
+
"---",
|
|
884
|
+
"name: gdh-update",
|
|
885
|
+
'description: "Update GDH to npm latest (bump pinned version + re-bake managed surfaces)"',
|
|
886
|
+
"---",
|
|
887
|
+
"",
|
|
888
|
+
"<cursor_skill_adapter>",
|
|
889
|
+
"## Invocation",
|
|
890
|
+
"- This skill is invoked when the user says `/gdh-update` or mentions `gdh-update`.",
|
|
891
|
+
"- Treat any positional argument (e.g., `gdh-update 0.6.0`) as an explicit version; otherwise default to npm latest.",
|
|
892
|
+
"</cursor_skill_adapter>",
|
|
893
|
+
"",
|
|
894
|
+
"<objective>",
|
|
895
|
+
"Update this project's GDH pinning to npm latest and re-bake every managed surface at the new pin.",
|
|
896
|
+
"</objective>",
|
|
897
|
+
"",
|
|
898
|
+
"<process>",
|
|
899
|
+
"Follow this order:",
|
|
900
|
+
"",
|
|
901
|
+
"1. Preview: run `npx -y @skillcap/gdh@latest self-update [version] --dry-run` (omit `[version]` to preview against npm latest).",
|
|
902
|
+
"2. Surface the planned version delta and re-bake action count to the human conversationally.",
|
|
903
|
+
"3. Apply: run `npx -y @skillcap/gdh@latest self-update [version]` (omit `[version]` to apply against npm latest; forward whatever positional the user passed to `/gdh-update`).",
|
|
904
|
+
"4. Verify: run `npx -y @skillcap/gdh@latest verify drift` to confirm every baked surface matches the new pin.",
|
|
905
|
+
"</process>",
|
|
906
|
+
"",
|
|
907
|
+
"<rules>",
|
|
908
|
+
"- Do NOT ask the user to confirm apply — their original intent (\"update GDH\") IS the approval.",
|
|
909
|
+
"- Bake literal `@latest` in the shellout so the new CLI performs the update, not the old one.",
|
|
910
|
+
"- If `self-update` returns `rolled_back` or `blocked`, surface the failure reason and stop (do not retry).",
|
|
911
|
+
"- If `self-update` returns `skipped_dev_mode`, explain that dev-from-source mode bypasses pinning; no action needed.",
|
|
912
|
+
"</rules>",
|
|
913
|
+
"",
|
|
914
|
+
].join("\n");
|
|
915
|
+
}
|
|
777
916
|
// --- gdh-check skill renders ---
|
|
778
917
|
export function renderClaudeCheckCommand(pinnedVersion) {
|
|
779
918
|
return [
|
|
@@ -1117,34 +1256,26 @@ export function renderCursorVerifySkill(pinnedVersion) {
|
|
|
1117
1256
|
async function inspectProjectMcpSupport(targetPath, options) {
|
|
1118
1257
|
const projectConfig = await readProjectConfig(targetPath);
|
|
1119
1258
|
const enabled = resolveProjectMcpEnabled(projectConfig);
|
|
1120
|
-
const launcherContent = options.pinnedVersion === null
|
|
1121
|
-
? null
|
|
1122
|
-
: renderManagedMcpLauncher(options.pinnedVersion);
|
|
1123
1259
|
const managedMcpEntry = buildManagedMcpServerEntry({
|
|
1124
|
-
|
|
1125
|
-
integrationRootPath: options.integrationRootPath,
|
|
1126
|
-
launcherPathForConfig: path.resolve(options.integrationRootPath, MCP_LAUNCHER_RELATIVE_PATH),
|
|
1260
|
+
pinnedVersion: options.pinnedVersion ?? "latest",
|
|
1127
1261
|
});
|
|
1128
|
-
const [projectFile, cursorFile,
|
|
1262
|
+
const [projectFile, cursorFile, codexProjectContent, localPathHints] = await Promise.all([
|
|
1129
1263
|
inspectJsonFile(path.join(options.integrationRootPath, PROJECT_MCP_RELATIVE_PATH)),
|
|
1130
1264
|
inspectJsonFile(path.join(options.integrationRootPath, CURSOR_MCP_RELATIVE_PATH)),
|
|
1131
|
-
fs.readFile(path.join(options.integrationRootPath,
|
|
1265
|
+
fs.readFile(path.join(options.integrationRootPath, CODEX_PROJECT_CONFIG_RELATIVE_PATH), "utf8").catch(() => null),
|
|
1132
1266
|
readLocalPathHints(options.integrationRootPath),
|
|
1133
1267
|
]);
|
|
1134
|
-
const launcherBootstrap = await inspectLauncherBootstrap(localPathHints);
|
|
1135
1268
|
const codexConfigPath = path.join(os.homedir(), ".codex/config.toml");
|
|
1136
1269
|
const codexServerName = projectConfig === null ? null : createCodexServerName(projectConfig.projectKeySeed);
|
|
1137
1270
|
return {
|
|
1138
1271
|
enabled,
|
|
1139
1272
|
integrationRootPath: options.integrationRootPath,
|
|
1140
|
-
launcherContent,
|
|
1141
1273
|
localPathHints,
|
|
1142
|
-
launcherBootstrap,
|
|
1143
|
-
launcherFile: inspectLauncherFile(launcherSource, launcherContent, enabled, launcherBootstrap),
|
|
1144
1274
|
projectFile: inspectManagedMcpFile(projectFile, enabled, PROJECT_MCP_RELATIVE_PATH, "Claude project MCP config", managedMcpEntry),
|
|
1145
1275
|
cursorFile: inspectManagedMcpFile(cursorFile, enabled, CURSOR_MCP_RELATIVE_PATH, "Cursor project MCP config", managedMcpEntry),
|
|
1276
|
+
codexProjectFile: inspectManagedCodexProjectFile(codexProjectContent, enabled, options.pinnedVersion),
|
|
1146
1277
|
codexRegistration: options.includeUserLocal
|
|
1147
|
-
? await inspectCodexRegistration(targetPath, enabled, codexServerName, codexConfigPath, options.integrationRootPath)
|
|
1278
|
+
? await inspectCodexRegistration(targetPath, enabled, codexServerName, codexConfigPath, options.integrationRootPath, options.pinnedVersion)
|
|
1148
1279
|
: {
|
|
1149
1280
|
present: false,
|
|
1150
1281
|
state: "ready",
|
|
@@ -1221,71 +1352,52 @@ function inspectManagedMcpFile(file, enabled, relativePath, displayName, expecte
|
|
|
1221
1352
|
summary: `${displayName} includes the managed \`${GDH_MCP_SERVER_NAME}\` MCP server entry.`,
|
|
1222
1353
|
};
|
|
1223
1354
|
}
|
|
1224
|
-
function
|
|
1355
|
+
function inspectManagedCodexProjectFile(content, enabled, pinnedVersion) {
|
|
1356
|
+
const relativePath = CODEX_PROJECT_CONFIG_RELATIVE_PATH;
|
|
1225
1357
|
if (!enabled) {
|
|
1226
1358
|
return {
|
|
1227
|
-
present:
|
|
1359
|
+
present: content !== null,
|
|
1228
1360
|
state: "ready",
|
|
1229
|
-
summary: "
|
|
1361
|
+
summary: "Codex project MCP config is disabled for this target.",
|
|
1230
1362
|
};
|
|
1231
1363
|
}
|
|
1232
|
-
if (
|
|
1364
|
+
if (pinnedVersion === null) {
|
|
1233
1365
|
return {
|
|
1234
|
-
present:
|
|
1366
|
+
present: content !== null,
|
|
1235
1367
|
state: "missing",
|
|
1236
|
-
summary:
|
|
1368
|
+
summary: `Codex project MCP config cannot be validated yet: no \`gdh_version\` is pinned (run \`gdh setup\` or \`gdh migrate --apply\`).`,
|
|
1237
1369
|
};
|
|
1238
1370
|
}
|
|
1239
|
-
if (
|
|
1371
|
+
if (content === null) {
|
|
1240
1372
|
return {
|
|
1241
1373
|
present: false,
|
|
1242
1374
|
state: "missing",
|
|
1243
|
-
summary: `
|
|
1375
|
+
summary: `Codex project MCP config is missing and should define the managed \`[mcp_servers.${GDH_MCP_SERVER_NAME}]\` entry at ${relativePath}.`,
|
|
1244
1376
|
};
|
|
1245
1377
|
}
|
|
1246
|
-
|
|
1378
|
+
const existingSection = extractManagedCodexSection(content);
|
|
1379
|
+
const expectedSection = renderManagedCodexProjectSection(pinnedVersion);
|
|
1380
|
+
if (existingSection === null) {
|
|
1247
1381
|
return {
|
|
1248
1382
|
present: true,
|
|
1249
1383
|
state: "misconfigured",
|
|
1250
|
-
summary:
|
|
1384
|
+
summary: `${relativePath} exists but does not contain the managed \`[mcp_servers.${GDH_MCP_SERVER_NAME}]\` section.`,
|
|
1251
1385
|
};
|
|
1252
1386
|
}
|
|
1253
|
-
|
|
1254
|
-
present: true,
|
|
1255
|
-
state: bootstrap.state,
|
|
1256
|
-
summary: bootstrap.state === "ready"
|
|
1257
|
-
? "The managed GDH MCP launcher is present and can resolve the current GDH bootstrap path."
|
|
1258
|
-
: bootstrap.summary,
|
|
1259
|
-
};
|
|
1260
|
-
}
|
|
1261
|
-
async function inspectLauncherBootstrap(localPathHints) {
|
|
1262
|
-
const envDevRepoPath = process.env["GDH_DEV_REPO"]?.trim() || null;
|
|
1263
|
-
const effectiveDevRepoPath = envDevRepoPath ?? localPathHints.gdhDevRepoPath;
|
|
1264
|
-
if (effectiveDevRepoPath !== null) {
|
|
1265
|
-
const cliEntryPath = path.join(effectiveDevRepoPath, "packages/cli/dist/cli.js");
|
|
1266
|
-
if (await fileExists(cliEntryPath)) {
|
|
1267
|
-
return {
|
|
1268
|
-
state: "ready",
|
|
1269
|
-
summary: `The managed GDH MCP launcher will bootstrap from the current GDH checkout at ${effectiveDevRepoPath}.`,
|
|
1270
|
-
};
|
|
1271
|
-
}
|
|
1387
|
+
if (existingSection !== expectedSection) {
|
|
1272
1388
|
return {
|
|
1389
|
+
present: true,
|
|
1273
1390
|
state: "misconfigured",
|
|
1274
|
-
summary:
|
|
1275
|
-
};
|
|
1276
|
-
}
|
|
1277
|
-
if (await executableExistsOnPath("gdh")) {
|
|
1278
|
-
return {
|
|
1279
|
-
state: "ready",
|
|
1280
|
-
summary: "The managed GDH MCP launcher can resolve `gdh` from PATH.",
|
|
1391
|
+
summary: `${relativePath} managed \`[mcp_servers.${GDH_MCP_SERVER_NAME}]\` section does not match the expected GDH shape.`,
|
|
1281
1392
|
};
|
|
1282
1393
|
}
|
|
1283
1394
|
return {
|
|
1284
|
-
|
|
1285
|
-
|
|
1395
|
+
present: true,
|
|
1396
|
+
state: "ready",
|
|
1397
|
+
summary: `Codex project MCP config includes the managed \`[mcp_servers.${GDH_MCP_SERVER_NAME}]\` entry.`,
|
|
1286
1398
|
};
|
|
1287
1399
|
}
|
|
1288
|
-
async function inspectCodexRegistration(targetPath, enabled, codexServerName, codexConfigPath, integrationRootPath) {
|
|
1400
|
+
async function inspectCodexRegistration(targetPath, enabled, codexServerName, codexConfigPath, integrationRootPath, pinnedVersion) {
|
|
1289
1401
|
if (!enabled || codexServerName === null) {
|
|
1290
1402
|
return {
|
|
1291
1403
|
present: false,
|
|
@@ -1309,16 +1421,22 @@ async function inspectCodexRegistration(targetPath, enabled, codexServerName, co
|
|
|
1309
1421
|
summary: `Codex does not yet have the expected user-local MCP registration \`${codexServerName}\` in ${codexConfigPath}.`,
|
|
1310
1422
|
};
|
|
1311
1423
|
}
|
|
1312
|
-
|
|
1424
|
+
if (pinnedVersion === null) {
|
|
1425
|
+
return {
|
|
1426
|
+
present: true,
|
|
1427
|
+
state: "misconfigured",
|
|
1428
|
+
summary: `Codex MCP registration \`${codexServerName}\` exists but no \`gdh_version\` is pinned; cannot validate the expected npx invocation.`,
|
|
1429
|
+
};
|
|
1430
|
+
}
|
|
1313
1431
|
if (!isMatchingCodexRegistration(registration, {
|
|
1314
1432
|
targetPath,
|
|
1315
1433
|
integrationRootPath,
|
|
1316
|
-
|
|
1434
|
+
pinnedVersion,
|
|
1317
1435
|
})) {
|
|
1318
1436
|
return {
|
|
1319
1437
|
present: true,
|
|
1320
1438
|
state: "misconfigured",
|
|
1321
|
-
summary: `Codex MCP registration \`${codexServerName}\` exists but does not
|
|
1439
|
+
summary: `Codex MCP registration \`${codexServerName}\` exists but does not match the expected \`npx -y @skillcap/gdh@${pinnedVersion} mcp serve\` invocation.`,
|
|
1322
1440
|
};
|
|
1323
1441
|
}
|
|
1324
1442
|
return {
|
|
@@ -1440,6 +1558,11 @@ async function inspectCodexAdapter(targetPath, guidance, projectMcp, pinnedVersi
|
|
|
1440
1558
|
const codexSkillContent = await fs.readFile(codexSkillPath, "utf8").catch(() => null);
|
|
1441
1559
|
const codexStatusContent = await fs.readFile(path.join(targetPath, CODEX_STATUS_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
|
|
1442
1560
|
const codexMigrateContent = await fs.readFile(path.join(targetPath, CODEX_MIGRATE_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
|
|
1561
|
+
// Phase 13 Plan 13-03 deliverable — /gdh-update skill for Codex. Inspection
|
|
1562
|
+
// wiring is required so planSkillInstallAction can see the surface state;
|
|
1563
|
+
// otherwise the planner returns `unchanged` for a missing file and install
|
|
1564
|
+
// becomes a no-op (Plan 13-05 integration-test Rule 2 fix).
|
|
1565
|
+
const codexUpdateContent = await fs.readFile(path.join(targetPath, CODEX_UPDATE_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
|
|
1443
1566
|
const codexCheckContent = await fs.readFile(path.join(targetPath, CODEX_CHECK_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
|
|
1444
1567
|
const codexPrepareContent = await fs.readFile(path.join(targetPath, CODEX_PREPARE_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
|
|
1445
1568
|
const codexVerifyContent = await fs.readFile(path.join(targetPath, CODEX_VERIFY_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
|
|
@@ -1447,6 +1570,7 @@ async function inspectCodexAdapter(targetPath, guidance, projectMcp, pinnedVersi
|
|
|
1447
1570
|
const expectedCodexOnboardSkill = pinnedVersion === null ? null : renderCodexOnboardSkill(pinnedVersion);
|
|
1448
1571
|
const expectedCodexStatusSkill = pinnedVersion === null ? null : renderCodexStatusSkill(pinnedVersion);
|
|
1449
1572
|
const expectedCodexMigrateSkill = pinnedVersion === null ? null : renderCodexMigrateSkill(pinnedVersion);
|
|
1573
|
+
const expectedCodexUpdateSkill = pinnedVersion === null ? null : renderCodexUpdateSkill(pinnedVersion);
|
|
1450
1574
|
const expectedCodexCheckSkill = pinnedVersion === null ? null : renderCodexCheckSkill(pinnedVersion);
|
|
1451
1575
|
const expectedCodexPrepareSkill = pinnedVersion === null ? null : renderCodexPrepareSkill(pinnedVersion);
|
|
1452
1576
|
const expectedCodexVerifySkill = pinnedVersion === null ? null : renderCodexVerifySkill(pinnedVersion);
|
|
@@ -1494,6 +1618,7 @@ async function inspectCodexAdapter(targetPath, guidance, projectMcp, pinnedVersi
|
|
|
1494
1618
|
}),
|
|
1495
1619
|
...inspectCodexSkillSurface(targetPath, CODEX_STATUS_SKILL_RELATIVE_PATH, codexStatusContent, expectedCodexStatusSkill, "gdh-status"),
|
|
1496
1620
|
...inspectCodexSkillSurface(targetPath, CODEX_MIGRATE_SKILL_RELATIVE_PATH, codexMigrateContent, expectedCodexMigrateSkill, "gdh-migrate"),
|
|
1621
|
+
...inspectCodexSkillSurface(targetPath, CODEX_UPDATE_SKILL_RELATIVE_PATH, codexUpdateContent, expectedCodexUpdateSkill, "gdh-update"),
|
|
1497
1622
|
...inspectCodexSkillSurface(targetPath, CODEX_CHECK_SKILL_RELATIVE_PATH, codexCheckContent, expectedCodexCheckSkill, "gdh-check"),
|
|
1498
1623
|
...inspectCodexSkillSurface(targetPath, CODEX_PREPARE_SKILL_RELATIVE_PATH, codexPrepareContent, expectedCodexPrepareSkill, "gdh-prepare"),
|
|
1499
1624
|
...inspectCodexSkillSurface(targetPath, CODEX_VERIFY_SKILL_RELATIVE_PATH, codexVerifyContent, expectedCodexVerifySkill, "gdh-verify"),
|
|
@@ -1501,14 +1626,14 @@ async function inspectCodexAdapter(targetPath, guidance, projectMcp, pinnedVersi
|
|
|
1501
1626
|
];
|
|
1502
1627
|
if (projectMcp.enabled) {
|
|
1503
1628
|
surfaces.push(createSurfaceStatus({
|
|
1504
|
-
kind: "
|
|
1629
|
+
kind: "mcp_file",
|
|
1505
1630
|
scope: "repo",
|
|
1506
1631
|
targetPath: projectMcp.integrationRootPath,
|
|
1507
|
-
relativePath:
|
|
1508
|
-
present: projectMcp.
|
|
1509
|
-
state: projectMcp.
|
|
1510
|
-
summary: projectMcp.
|
|
1511
|
-
version:
|
|
1632
|
+
relativePath: CODEX_PROJECT_CONFIG_RELATIVE_PATH,
|
|
1633
|
+
present: projectMcp.codexProjectFile.present,
|
|
1634
|
+
state: projectMcp.codexProjectFile.state,
|
|
1635
|
+
summary: projectMcp.codexProjectFile.summary,
|
|
1636
|
+
version: null,
|
|
1512
1637
|
}));
|
|
1513
1638
|
if (options.includeUserLocal) {
|
|
1514
1639
|
surfaces.push(createSurfaceStatus({
|
|
@@ -1533,17 +1658,29 @@ async function inspectClaudeAdapter(targetPath, guidance, projectMcp, pinnedVers
|
|
|
1533
1658
|
const onboardCommandContent = await fs.readFile(onboardCommandPath, "utf8").catch(() => null);
|
|
1534
1659
|
const claudeStatusContent = await fs.readFile(path.join(targetPath, CLAUDE_STATUS_COMMAND_RELATIVE_PATH), "utf8").catch(() => null);
|
|
1535
1660
|
const claudeMigrateContent = await fs.readFile(path.join(targetPath, CLAUDE_MIGRATE_COMMAND_RELATIVE_PATH), "utf8").catch(() => null);
|
|
1661
|
+
// Phase 13 Plan 13-03 deliverable — /gdh-update slash command for Claude.
|
|
1662
|
+
// Inspection wiring is required so planSkillInstallAction can see the surface
|
|
1663
|
+
// state; otherwise the planner returns `unchanged` for a missing file and
|
|
1664
|
+
// install becomes a no-op (Plan 13-05 integration-test Rule 2 fix).
|
|
1665
|
+
const claudeUpdateContent = await fs.readFile(path.join(targetPath, CLAUDE_UPDATE_COMMAND_RELATIVE_PATH), "utf8").catch(() => null);
|
|
1536
1666
|
const claudeCheckContent = await fs.readFile(path.join(targetPath, CLAUDE_CHECK_COMMAND_RELATIVE_PATH), "utf8").catch(() => null);
|
|
1537
1667
|
const claudePrepareContent = await fs.readFile(path.join(targetPath, CLAUDE_PREPARE_COMMAND_RELATIVE_PATH), "utf8").catch(() => null);
|
|
1538
1668
|
const claudeVerifyContent = await fs.readFile(path.join(targetPath, CLAUDE_VERIFY_COMMAND_RELATIVE_PATH), "utf8").catch(() => null);
|
|
1539
1669
|
const claudeScanContent = await fs.readFile(path.join(targetPath, CLAUDE_SCAN_COMMAND_RELATIVE_PATH), "utf8").catch(() => null);
|
|
1670
|
+
const claudeCheckUpdateHookContent = await fs.readFile(path.join(targetPath, CLAUDE_CHECK_UPDATE_HOOK_RELATIVE_PATH), "utf8").catch(() => null);
|
|
1671
|
+
const claudeCheckUpdateWorkerContent = await fs.readFile(path.join(targetPath, CLAUDE_CHECK_UPDATE_WORKER_RELATIVE_PATH), "utf8").catch(() => null);
|
|
1672
|
+
const claudeStatuslineContent = await fs.readFile(path.join(targetPath, CLAUDE_STATUSLINE_RELATIVE_PATH), "utf8").catch(() => null);
|
|
1540
1673
|
let detectedTarget = null;
|
|
1541
1674
|
if (lstat?.isSymbolicLink()) {
|
|
1542
1675
|
detectedTarget = await fs.readlink(absolutePath).catch(() => null);
|
|
1543
1676
|
}
|
|
1544
1677
|
const expectedClaudeOnboardCommand = pinnedVersion === null ? null : renderClaudeOnboardCommand(pinnedVersion);
|
|
1678
|
+
const expectedClaudeCheckUpdateHook = pinnedVersion === null ? null : renderClaudeCheckUpdateHook(pinnedVersion);
|
|
1679
|
+
const expectedClaudeCheckUpdateWorker = pinnedVersion === null ? null : renderClaudeCheckUpdateWorker(pinnedVersion);
|
|
1680
|
+
const expectedClaudeStatusline = pinnedVersion === null ? null : renderClaudeUpdateStatusline(pinnedVersion);
|
|
1545
1681
|
const expectedClaudeStatusCommand = pinnedVersion === null ? null : renderClaudeStatusCommand(pinnedVersion);
|
|
1546
1682
|
const expectedClaudeMigrateCommand = pinnedVersion === null ? null : renderClaudeMigrateCommand(pinnedVersion);
|
|
1683
|
+
const expectedClaudeUpdateCommand = pinnedVersion === null ? null : renderClaudeUpdateCommand(pinnedVersion);
|
|
1547
1684
|
const expectedClaudeCheckCommand = pinnedVersion === null ? null : renderClaudeCheckCommand(pinnedVersion);
|
|
1548
1685
|
const expectedClaudePrepareCommand = pinnedVersion === null ? null : renderClaudePrepareCommand(pinnedVersion);
|
|
1549
1686
|
const expectedClaudeVerifyCommand = pinnedVersion === null ? null : renderClaudeVerifyCommand(pinnedVersion);
|
|
@@ -1593,22 +1730,17 @@ async function inspectClaudeAdapter(targetPath, guidance, projectMcp, pinnedVers
|
|
|
1593
1730
|
}),
|
|
1594
1731
|
...inspectClaudeCommandSurface(targetPath, CLAUDE_STATUS_COMMAND_RELATIVE_PATH, claudeStatusContent, expectedClaudeStatusCommand, "gdh-status"),
|
|
1595
1732
|
...inspectClaudeCommandSurface(targetPath, CLAUDE_MIGRATE_COMMAND_RELATIVE_PATH, claudeMigrateContent, expectedClaudeMigrateCommand, "gdh-migrate"),
|
|
1733
|
+
...inspectClaudeCommandSurface(targetPath, CLAUDE_UPDATE_COMMAND_RELATIVE_PATH, claudeUpdateContent, expectedClaudeUpdateCommand, "gdh-update"),
|
|
1596
1734
|
...inspectClaudeCommandSurface(targetPath, CLAUDE_CHECK_COMMAND_RELATIVE_PATH, claudeCheckContent, expectedClaudeCheckCommand, "gdh-check"),
|
|
1597
1735
|
...inspectClaudeCommandSurface(targetPath, CLAUDE_PREPARE_COMMAND_RELATIVE_PATH, claudePrepareContent, expectedClaudePrepareCommand, "gdh-prepare"),
|
|
1598
1736
|
...inspectClaudeCommandSurface(targetPath, CLAUDE_VERIFY_COMMAND_RELATIVE_PATH, claudeVerifyContent, expectedClaudeVerifyCommand, "gdh-verify"),
|
|
1599
1737
|
...inspectClaudeCommandSurface(targetPath, CLAUDE_SCAN_COMMAND_RELATIVE_PATH, claudeScanContent, expectedClaudeScanCommand, "gdh-scan"),
|
|
1738
|
+
...inspectClaudeCommandSurface(targetPath, CLAUDE_CHECK_UPDATE_HOOK_RELATIVE_PATH, claudeCheckUpdateHookContent, expectedClaudeCheckUpdateHook, "gdh-check-update-hook"),
|
|
1739
|
+
...inspectClaudeCommandSurface(targetPath, CLAUDE_CHECK_UPDATE_WORKER_RELATIVE_PATH, claudeCheckUpdateWorkerContent, expectedClaudeCheckUpdateWorker, "gdh-check-update-worker"),
|
|
1740
|
+
...inspectClaudeCommandSurface(targetPath, CLAUDE_STATUSLINE_RELATIVE_PATH, claudeStatuslineContent, expectedClaudeStatusline, "gdh-statusline"),
|
|
1600
1741
|
];
|
|
1601
1742
|
if (projectMcp.enabled) {
|
|
1602
1743
|
surfaces.push(createSurfaceStatus({
|
|
1603
|
-
kind: "launcher_file",
|
|
1604
|
-
scope: "repo",
|
|
1605
|
-
targetPath: projectMcp.integrationRootPath,
|
|
1606
|
-
relativePath: MCP_LAUNCHER_RELATIVE_PATH,
|
|
1607
|
-
present: projectMcp.launcherFile.present,
|
|
1608
|
-
state: projectMcp.launcherFile.state,
|
|
1609
|
-
summary: projectMcp.launcherFile.summary,
|
|
1610
|
-
version: GDH_MCP_LAUNCHER_VERSION,
|
|
1611
|
-
}), createSurfaceStatus({
|
|
1612
1744
|
kind: "mcp_file",
|
|
1613
1745
|
scope: "repo",
|
|
1614
1746
|
targetPath: projectMcp.integrationRootPath,
|
|
@@ -1630,6 +1762,11 @@ async function inspectCursorAdapter(targetPath, guidance, projectMcp, pinnedVers
|
|
|
1630
1762
|
const onboardSkillContent = await fs.readFile(onboardSkillPath, "utf8").catch(() => null);
|
|
1631
1763
|
const cursorStatusContent = await fs.readFile(path.join(targetPath, CURSOR_STATUS_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
|
|
1632
1764
|
const cursorMigrateContent = await fs.readFile(path.join(targetPath, CURSOR_MIGRATE_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
|
|
1765
|
+
// Phase 13 Plan 13-03 deliverable — /gdh-update skill for Cursor. Inspection
|
|
1766
|
+
// wiring required so planSkillInstallAction sees the surface state; otherwise
|
|
1767
|
+
// the planner returns `unchanged` for a missing file and install becomes a
|
|
1768
|
+
// no-op (Plan 13-05 integration-test Rule 2 fix).
|
|
1769
|
+
const cursorUpdateContent = await fs.readFile(path.join(targetPath, CURSOR_UPDATE_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
|
|
1633
1770
|
const cursorCheckContent = await fs.readFile(path.join(targetPath, CURSOR_CHECK_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
|
|
1634
1771
|
const cursorPrepareContent = await fs.readFile(path.join(targetPath, CURSOR_PREPARE_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
|
|
1635
1772
|
const cursorVerifyContent = await fs.readFile(path.join(targetPath, CURSOR_VERIFY_SKILL_RELATIVE_PATH), "utf8").catch(() => null);
|
|
@@ -1639,6 +1776,7 @@ async function inspectCursorAdapter(targetPath, guidance, projectMcp, pinnedVers
|
|
|
1639
1776
|
const expectedCursorOnboardSkill = pinnedVersion === null ? null : renderCursorOnboardSkill(pinnedVersion);
|
|
1640
1777
|
const expectedCursorStatusSkill = pinnedVersion === null ? null : renderCursorStatusSkill(pinnedVersion);
|
|
1641
1778
|
const expectedCursorMigrateSkill = pinnedVersion === null ? null : renderCursorMigrateSkill(pinnedVersion);
|
|
1779
|
+
const expectedCursorUpdateSkill = pinnedVersion === null ? null : renderCursorUpdateSkill(pinnedVersion);
|
|
1642
1780
|
const expectedCursorCheckSkill = pinnedVersion === null ? null : renderCursorCheckSkill(pinnedVersion);
|
|
1643
1781
|
const expectedCursorPrepareSkill = pinnedVersion === null ? null : renderCursorPrepareSkill(pinnedVersion);
|
|
1644
1782
|
const expectedCursorVerifySkill = pinnedVersion === null ? null : renderCursorVerifySkill(pinnedVersion);
|
|
@@ -1686,6 +1824,7 @@ async function inspectCursorAdapter(targetPath, guidance, projectMcp, pinnedVers
|
|
|
1686
1824
|
}),
|
|
1687
1825
|
...inspectCursorSkillSurface(targetPath, CURSOR_STATUS_SKILL_RELATIVE_PATH, cursorStatusContent, expectedCursorStatusSkill, "gdh-status"),
|
|
1688
1826
|
...inspectCursorSkillSurface(targetPath, CURSOR_MIGRATE_SKILL_RELATIVE_PATH, cursorMigrateContent, expectedCursorMigrateSkill, "gdh-migrate"),
|
|
1827
|
+
...inspectCursorSkillSurface(targetPath, CURSOR_UPDATE_SKILL_RELATIVE_PATH, cursorUpdateContent, expectedCursorUpdateSkill, "gdh-update"),
|
|
1689
1828
|
...inspectCursorSkillSurface(targetPath, CURSOR_CHECK_SKILL_RELATIVE_PATH, cursorCheckContent, expectedCursorCheckSkill, "gdh-check"),
|
|
1690
1829
|
...inspectCursorSkillSurface(targetPath, CURSOR_PREPARE_SKILL_RELATIVE_PATH, cursorPrepareContent, expectedCursorPrepareSkill, "gdh-prepare"),
|
|
1691
1830
|
...inspectCursorSkillSurface(targetPath, CURSOR_VERIFY_SKILL_RELATIVE_PATH, cursorVerifyContent, expectedCursorVerifySkill, "gdh-verify"),
|
|
@@ -1693,15 +1832,6 @@ async function inspectCursorAdapter(targetPath, guidance, projectMcp, pinnedVers
|
|
|
1693
1832
|
];
|
|
1694
1833
|
if (projectMcp.enabled) {
|
|
1695
1834
|
surfaces.push(createSurfaceStatus({
|
|
1696
|
-
kind: "launcher_file",
|
|
1697
|
-
scope: "repo",
|
|
1698
|
-
targetPath: projectMcp.integrationRootPath,
|
|
1699
|
-
relativePath: MCP_LAUNCHER_RELATIVE_PATH,
|
|
1700
|
-
present: projectMcp.launcherFile.present,
|
|
1701
|
-
state: projectMcp.launcherFile.state,
|
|
1702
|
-
summary: projectMcp.launcherFile.summary,
|
|
1703
|
-
version: GDH_MCP_LAUNCHER_VERSION,
|
|
1704
|
-
}), createSurfaceStatus({
|
|
1705
1835
|
kind: "mcp_file",
|
|
1706
1836
|
scope: "repo",
|
|
1707
1837
|
targetPath: projectMcp.integrationRootPath,
|
|
@@ -1785,17 +1915,17 @@ async function planInstallActions(targetPath, adapters, requestedAgents, options
|
|
|
1785
1915
|
const effectiveDevRepoPath = options.devRepoPath ?? resolveCurrentGdhInstall(import.meta.url).defaultDevRepoPath;
|
|
1786
1916
|
if (options.user) {
|
|
1787
1917
|
if (requestedAgents.includes("codex")) {
|
|
1788
|
-
actions.push(...planCodexUserInstallActions(targetPath, projectMcp, options.integrationRootPath));
|
|
1918
|
+
actions.push(...planCodexUserInstallActions(targetPath, projectMcp, options.integrationRootPath, options.pinnedVersion));
|
|
1789
1919
|
}
|
|
1790
1920
|
return dedupeInstallActions(actions);
|
|
1791
1921
|
}
|
|
1792
|
-
actions.push(...planSharedRepoInstallActions(targetPath, projectMcp, requestedAgents[0] ?? "claude", effectiveDevRepoPath, options.integrationRootPath));
|
|
1922
|
+
actions.push(...planSharedRepoInstallActions(targetPath, projectMcp, requestedAgents[0] ?? "claude", effectiveDevRepoPath, options.integrationRootPath, options.pinnedVersion));
|
|
1793
1923
|
for (const adapter of adapters) {
|
|
1794
1924
|
if (!requestedAgents.includes(adapter.agent)) {
|
|
1795
1925
|
continue;
|
|
1796
1926
|
}
|
|
1797
1927
|
if (adapter.agent === "codex") {
|
|
1798
|
-
actions.push(...planCodexRepoInstallActions(targetPath, adapter, options.pinnedVersion));
|
|
1928
|
+
actions.push(...planCodexRepoInstallActions(targetPath, adapter, options.pinnedVersion, projectMcp, options.integrationRootPath));
|
|
1799
1929
|
continue;
|
|
1800
1930
|
}
|
|
1801
1931
|
if (adapter.agent === "claude") {
|
|
@@ -1808,27 +1938,11 @@ async function planInstallActions(targetPath, adapters, requestedAgents, options
|
|
|
1808
1938
|
}
|
|
1809
1939
|
return dedupeInstallActions(actions);
|
|
1810
1940
|
}
|
|
1811
|
-
function planSharedRepoInstallActions(targetPath, projectMcp, agent, effectiveDevRepoPath, integrationRootPath) {
|
|
1941
|
+
function planSharedRepoInstallActions(targetPath, projectMcp, agent, effectiveDevRepoPath, integrationRootPath, pinnedVersion) {
|
|
1812
1942
|
if (!projectMcp.enabled) {
|
|
1813
1943
|
return [];
|
|
1814
1944
|
}
|
|
1815
1945
|
const actions = [];
|
|
1816
|
-
if (projectMcp.launcherFile.state !== "ready") {
|
|
1817
|
-
actions.push(createInstallAction({
|
|
1818
|
-
agent,
|
|
1819
|
-
kind: "write_file",
|
|
1820
|
-
scope: "repo",
|
|
1821
|
-
targetPath: integrationRootPath,
|
|
1822
|
-
relativePath: MCP_LAUNCHER_RELATIVE_PATH,
|
|
1823
|
-
state: "planned",
|
|
1824
|
-
mode: projectMcp.launcherFile.present ? "replace" : "create",
|
|
1825
|
-
summary: projectMcp.launcherFile.present
|
|
1826
|
-
? "Replace the managed GDH MCP launcher with the current launcher content."
|
|
1827
|
-
: "Create the managed GDH MCP launcher under .gdh/bin/.",
|
|
1828
|
-
version: GDH_MCP_LAUNCHER_VERSION,
|
|
1829
|
-
content: projectMcp.launcherContent,
|
|
1830
|
-
}));
|
|
1831
|
-
}
|
|
1832
1946
|
if (projectMcp.projectFile.state !== "ready") {
|
|
1833
1947
|
actions.push(createInstallAction({
|
|
1834
1948
|
agent,
|
|
@@ -1841,11 +1955,7 @@ function planSharedRepoInstallActions(targetPath, projectMcp, agent, effectiveDe
|
|
|
1841
1955
|
summary: projectMcp.projectFile.present
|
|
1842
1956
|
? "Replace the managed `gdh` MCP entry in .mcp.json while preserving any non-GDH servers."
|
|
1843
1957
|
: "Create .mcp.json with the managed `gdh` MCP entry.",
|
|
1844
|
-
content: renderManagedMcpConfig(path.join(integrationRootPath, PROJECT_MCP_RELATIVE_PATH), buildManagedMcpServerEntry({
|
|
1845
|
-
targetPath,
|
|
1846
|
-
integrationRootPath,
|
|
1847
|
-
launcherPathForConfig: path.resolve(integrationRootPath, MCP_LAUNCHER_RELATIVE_PATH),
|
|
1848
|
-
})),
|
|
1958
|
+
content: renderManagedMcpConfig(path.join(integrationRootPath, PROJECT_MCP_RELATIVE_PATH), buildManagedMcpServerEntry({ pinnedVersion })),
|
|
1849
1959
|
}));
|
|
1850
1960
|
}
|
|
1851
1961
|
if (projectMcp.cursorFile.state !== "ready") {
|
|
@@ -1860,11 +1970,7 @@ function planSharedRepoInstallActions(targetPath, projectMcp, agent, effectiveDe
|
|
|
1860
1970
|
summary: projectMcp.cursorFile.present
|
|
1861
1971
|
? "Replace the managed `gdh` MCP entry in .cursor/mcp.json while preserving any non-GDH servers."
|
|
1862
1972
|
: "Create .cursor/mcp.json with the managed `gdh` MCP entry.",
|
|
1863
|
-
content: renderManagedMcpConfig(path.join(integrationRootPath, CURSOR_MCP_RELATIVE_PATH), buildManagedMcpServerEntry({
|
|
1864
|
-
targetPath,
|
|
1865
|
-
integrationRootPath,
|
|
1866
|
-
launcherPathForConfig: path.resolve(integrationRootPath, MCP_LAUNCHER_RELATIVE_PATH),
|
|
1867
|
-
})),
|
|
1973
|
+
content: renderManagedMcpConfig(path.join(integrationRootPath, CURSOR_MCP_RELATIVE_PATH), buildManagedMcpServerEntry({ pinnedVersion })),
|
|
1868
1974
|
}));
|
|
1869
1975
|
}
|
|
1870
1976
|
if (projectMcp.localPathHints.gdhDevRepoPath !== effectiveDevRepoPath) {
|
|
@@ -1934,28 +2040,53 @@ function agentLabel(agent) {
|
|
|
1934
2040
|
return "Cursor";
|
|
1935
2041
|
}
|
|
1936
2042
|
}
|
|
1937
|
-
function planCodexRepoInstallActions(targetPath, adapter, pinnedVersion) {
|
|
1938
|
-
|
|
2043
|
+
function planCodexRepoInstallActions(targetPath, adapter, pinnedVersion, projectMcp, integrationRootPath) {
|
|
2044
|
+
const actions = [
|
|
1939
2045
|
planSkillInstallAction("codex", targetPath, adapter, CODEX_ONBOARD_SKILL_RELATIVE_PATH, renderCodexOnboardSkill, "gdh-onboard", pinnedVersion),
|
|
1940
2046
|
planSkillInstallAction("codex", targetPath, adapter, CODEX_STATUS_SKILL_RELATIVE_PATH, renderCodexStatusSkill, "gdh-status", pinnedVersion),
|
|
1941
2047
|
planSkillInstallAction("codex", targetPath, adapter, CODEX_MIGRATE_SKILL_RELATIVE_PATH, renderCodexMigrateSkill, "gdh-migrate", pinnedVersion),
|
|
2048
|
+
planSkillInstallAction("codex", targetPath, adapter, CODEX_UPDATE_SKILL_RELATIVE_PATH, renderCodexUpdateSkill, "gdh-update", pinnedVersion),
|
|
1942
2049
|
planSkillInstallAction("codex", targetPath, adapter, CODEX_CHECK_SKILL_RELATIVE_PATH, renderCodexCheckSkill, "gdh-check", pinnedVersion),
|
|
1943
2050
|
planSkillInstallAction("codex", targetPath, adapter, CODEX_PREPARE_SKILL_RELATIVE_PATH, renderCodexPrepareSkill, "gdh-prepare", pinnedVersion),
|
|
1944
2051
|
planSkillInstallAction("codex", targetPath, adapter, CODEX_VERIFY_SKILL_RELATIVE_PATH, renderCodexVerifySkill, "gdh-verify", pinnedVersion),
|
|
1945
2052
|
planSkillInstallAction("codex", targetPath, adapter, CODEX_SCAN_SKILL_RELATIVE_PATH, renderCodexScanSkill, "gdh-scan", pinnedVersion),
|
|
1946
2053
|
];
|
|
2054
|
+
if (projectMcp.enabled && projectMcp.codexProjectFile.state !== "ready") {
|
|
2055
|
+
const absolutePath = path.join(integrationRootPath, CODEX_PROJECT_CONFIG_RELATIVE_PATH);
|
|
2056
|
+
const existingContent = fsSync.existsSync(absolutePath)
|
|
2057
|
+
? fsSync.readFileSync(absolutePath, "utf8")
|
|
2058
|
+
: null;
|
|
2059
|
+
actions.push(createInstallAction({
|
|
2060
|
+
agent: "codex",
|
|
2061
|
+
kind: "write_file",
|
|
2062
|
+
scope: "repo",
|
|
2063
|
+
targetPath: integrationRootPath,
|
|
2064
|
+
relativePath: CODEX_PROJECT_CONFIG_RELATIVE_PATH,
|
|
2065
|
+
state: "planned",
|
|
2066
|
+
mode: projectMcp.codexProjectFile.present ? "replace" : "create",
|
|
2067
|
+
summary: projectMcp.codexProjectFile.present
|
|
2068
|
+
? `Replace the managed \`[mcp_servers.${GDH_MCP_SERVER_NAME}]\` section in .codex/config.toml while preserving any non-GDH content.`
|
|
2069
|
+
: `Create .codex/config.toml with the managed \`[mcp_servers.${GDH_MCP_SERVER_NAME}]\` entry so Codex can discover the project MCP server without a user-global registration.`,
|
|
2070
|
+
content: renderManagedCodexProjectConfig(existingContent, pinnedVersion),
|
|
2071
|
+
}));
|
|
2072
|
+
}
|
|
2073
|
+
return actions;
|
|
1947
2074
|
}
|
|
1948
|
-
function planCodexUserInstallActions(targetPath, projectMcp, integrationRootPath) {
|
|
2075
|
+
function planCodexUserInstallActions(targetPath, projectMcp, integrationRootPath, pinnedVersion) {
|
|
1949
2076
|
if (!projectMcp.enabled || projectMcp.codexServerName === null) {
|
|
1950
2077
|
return [];
|
|
1951
2078
|
}
|
|
1952
|
-
const
|
|
1953
|
-
const
|
|
1954
|
-
|
|
1955
|
-
|
|
1956
|
-
|
|
1957
|
-
|
|
1958
|
-
|
|
2079
|
+
const resolvedTargetPath = path.resolve(targetPath);
|
|
2080
|
+
const resolvedIntegrationRootPath = path.resolve(integrationRootPath);
|
|
2081
|
+
const codexCommandArgs = [
|
|
2082
|
+
"-y",
|
|
2083
|
+
`@skillcap/gdh@${pinnedVersion}`,
|
|
2084
|
+
"mcp",
|
|
2085
|
+
"serve",
|
|
2086
|
+
];
|
|
2087
|
+
if (resolvedTargetPath !== resolvedIntegrationRootPath) {
|
|
2088
|
+
codexCommandArgs.push("--target", resolvedTargetPath);
|
|
2089
|
+
}
|
|
1959
2090
|
const actions = [];
|
|
1960
2091
|
if (projectMcp.codexRegistration.present) {
|
|
1961
2092
|
actions.push(createInstallAction({
|
|
@@ -1967,7 +2098,7 @@ function planCodexUserInstallActions(targetPath, projectMcp, integrationRootPath
|
|
|
1967
2098
|
absolutePath: projectMcp.codexConfigPath,
|
|
1968
2099
|
state: "planned",
|
|
1969
2100
|
mode: "replace",
|
|
1970
|
-
summary: `Remove the existing Codex MCP registration \`${projectMcp.codexServerName}\` before re-registering it with the managed GDH
|
|
2101
|
+
summary: `Remove the existing Codex MCP registration \`${projectMcp.codexServerName}\` before re-registering it with the managed GDH npx invocation.`,
|
|
1971
2102
|
command: ["codex", "mcp", "remove", projectMcp.codexServerName],
|
|
1972
2103
|
}));
|
|
1973
2104
|
}
|
|
@@ -1980,15 +2111,15 @@ function planCodexUserInstallActions(targetPath, projectMcp, integrationRootPath
|
|
|
1980
2111
|
absolutePath: projectMcp.codexConfigPath,
|
|
1981
2112
|
state: "planned",
|
|
1982
2113
|
mode: projectMcp.codexRegistration.present ? "replace" : "create",
|
|
1983
|
-
summary: `Register the managed GDH
|
|
2114
|
+
summary: `Register the managed GDH MCP server with Codex as \`${projectMcp.codexServerName}\` via pinned npx invocation.`,
|
|
1984
2115
|
command: [
|
|
1985
2116
|
"codex",
|
|
1986
2117
|
"mcp",
|
|
1987
2118
|
"add",
|
|
1988
2119
|
projectMcp.codexServerName,
|
|
1989
2120
|
"--",
|
|
1990
|
-
"
|
|
1991
|
-
...
|
|
2121
|
+
"npx",
|
|
2122
|
+
...codexCommandArgs,
|
|
1992
2123
|
],
|
|
1993
2124
|
}));
|
|
1994
2125
|
return actions;
|
|
@@ -2024,7 +2155,55 @@ function planClaudeInstallActions(targetPath, adapter, pinnedVersion) {
|
|
|
2024
2155
|
expectedTarget: "AGENTS.md",
|
|
2025
2156
|
}));
|
|
2026
2157
|
}
|
|
2027
|
-
actions.push(planSkillInstallAction("claude", targetPath, adapter, CLAUDE_ONBOARD_COMMAND_RELATIVE_PATH, renderClaudeOnboardCommand, "gdh-onboard", pinnedVersion), planSkillInstallAction("claude", targetPath, adapter, CLAUDE_STATUS_COMMAND_RELATIVE_PATH, renderClaudeStatusCommand, "gdh-status", pinnedVersion), planSkillInstallAction("claude", targetPath, adapter, CLAUDE_MIGRATE_COMMAND_RELATIVE_PATH, renderClaudeMigrateCommand, "gdh-migrate", pinnedVersion), planSkillInstallAction("claude", targetPath, adapter, CLAUDE_CHECK_COMMAND_RELATIVE_PATH, renderClaudeCheckCommand, "gdh-check", pinnedVersion), planSkillInstallAction("claude", targetPath, adapter, CLAUDE_PREPARE_COMMAND_RELATIVE_PATH, renderClaudePrepareCommand, "gdh-prepare", pinnedVersion), planSkillInstallAction("claude", targetPath, adapter, CLAUDE_VERIFY_COMMAND_RELATIVE_PATH, renderClaudeVerifyCommand, "gdh-verify", pinnedVersion), planSkillInstallAction("claude", targetPath, adapter, CLAUDE_SCAN_COMMAND_RELATIVE_PATH, renderClaudeScanCommand, "gdh-scan", pinnedVersion)
|
|
2158
|
+
actions.push(planSkillInstallAction("claude", targetPath, adapter, CLAUDE_ONBOARD_COMMAND_RELATIVE_PATH, renderClaudeOnboardCommand, "gdh-onboard", pinnedVersion), planSkillInstallAction("claude", targetPath, adapter, CLAUDE_STATUS_COMMAND_RELATIVE_PATH, renderClaudeStatusCommand, "gdh-status", pinnedVersion), planSkillInstallAction("claude", targetPath, adapter, CLAUDE_MIGRATE_COMMAND_RELATIVE_PATH, renderClaudeMigrateCommand, "gdh-migrate", pinnedVersion), planSkillInstallAction("claude", targetPath, adapter, CLAUDE_UPDATE_COMMAND_RELATIVE_PATH, renderClaudeUpdateCommand, "gdh-update", pinnedVersion), planSkillInstallAction("claude", targetPath, adapter, CLAUDE_CHECK_COMMAND_RELATIVE_PATH, renderClaudeCheckCommand, "gdh-check", pinnedVersion), planSkillInstallAction("claude", targetPath, adapter, CLAUDE_PREPARE_COMMAND_RELATIVE_PATH, renderClaudePrepareCommand, "gdh-prepare", pinnedVersion), planSkillInstallAction("claude", targetPath, adapter, CLAUDE_VERIFY_COMMAND_RELATIVE_PATH, renderClaudeVerifyCommand, "gdh-verify", pinnedVersion), planSkillInstallAction("claude", targetPath, adapter, CLAUDE_SCAN_COMMAND_RELATIVE_PATH, renderClaudeScanCommand, "gdh-scan", pinnedVersion),
|
|
2159
|
+
// UPD-01 managed hook surfaces baked at the pinned version.
|
|
2160
|
+
planSkillInstallAction("claude", targetPath, adapter, CLAUDE_CHECK_UPDATE_HOOK_RELATIVE_PATH, renderClaudeCheckUpdateHook, "gdh-check-update-hook", pinnedVersion), planSkillInstallAction("claude", targetPath, adapter, CLAUDE_CHECK_UPDATE_WORKER_RELATIVE_PATH, renderClaudeCheckUpdateWorker, "gdh-check-update-worker", pinnedVersion),
|
|
2161
|
+
// UPD-02 managed statusline surface baked at the pinned version. The .js
|
|
2162
|
+
// file is ALWAYS baked, even when settings.json statusLine is not owned
|
|
2163
|
+
// by GDH (write-if-absent below) — users can manually point their config
|
|
2164
|
+
// at gdh-statusline.js later; the baked file is a no-op until wired.
|
|
2165
|
+
planSkillInstallAction("claude", targetPath, adapter, CLAUDE_STATUSLINE_RELATIVE_PATH, renderClaudeUpdateStatusline, "gdh-statusline", pinnedVersion));
|
|
2166
|
+
// UPD-01 + UPD-02 .claude/settings.json composite patch action.
|
|
2167
|
+
// - Plan 02 (Strategy A, planner-lock #1): patch-merge the SessionStart
|
|
2168
|
+
// hook entry by exact command-literal match, preserving siblings.
|
|
2169
|
+
// - Plan 03 (write-if-absent, planner-lock #2 / D-18): add the statusLine
|
|
2170
|
+
// entry ONLY when no existing statusLine is configured (e.g. GSD-owned
|
|
2171
|
+
// or user-owned). Silent no-op for UPD-02 on targets where another
|
|
2172
|
+
// tool already owns the slot; the universal surface is MCP _meta.
|
|
2173
|
+
//
|
|
2174
|
+
// Synchronous read via fsSync is intentional: planClaudeInstallActions is
|
|
2175
|
+
// called without `await`, and making it async would ripple to every sibling
|
|
2176
|
+
// planner (Codex, Cursor). The composed patches are idempotent and
|
|
2177
|
+
// commutative (proven in Task 1 Test J) so application order is irrelevant.
|
|
2178
|
+
let existingSettingsContent = "";
|
|
2179
|
+
try {
|
|
2180
|
+
existingSettingsContent = fsSync.readFileSync(path.join(targetPath, CLAUDE_SETTINGS_RELATIVE_PATH), "utf8");
|
|
2181
|
+
}
|
|
2182
|
+
catch {
|
|
2183
|
+
existingSettingsContent = "";
|
|
2184
|
+
}
|
|
2185
|
+
const patchedSettings = patchClaudeSettingsForGdhStatusline(patchClaudeSettingsForGdhSessionStart(existingSettingsContent));
|
|
2186
|
+
const settingsIsUnchanged = existingSettingsContent === patchedSettings;
|
|
2187
|
+
const settingsExistedOnDisk = existingSettingsContent.length > 0;
|
|
2188
|
+
actions.push(createInstallAction({
|
|
2189
|
+
agent: "claude",
|
|
2190
|
+
kind: "write_file",
|
|
2191
|
+
scope: "repo",
|
|
2192
|
+
targetPath,
|
|
2193
|
+
relativePath: CLAUDE_SETTINGS_RELATIVE_PATH,
|
|
2194
|
+
state: settingsIsUnchanged ? "unchanged" : "planned",
|
|
2195
|
+
mode: settingsIsUnchanged
|
|
2196
|
+
? "unchanged"
|
|
2197
|
+
: settingsExistedOnDisk
|
|
2198
|
+
? "replace"
|
|
2199
|
+
: "create",
|
|
2200
|
+
summary: settingsIsUnchanged
|
|
2201
|
+
? "GDH SessionStart hook + statusline already registered in .claude/settings.json."
|
|
2202
|
+
: settingsExistedOnDisk
|
|
2203
|
+
? "Register the GDH SessionStart hook and statusline (write-if-absent) in .claude/settings.json while preserving sibling content (patch-merge)."
|
|
2204
|
+
: "Create .claude/settings.json with the GDH SessionStart hook + statusline registration.",
|
|
2205
|
+
content: patchedSettings,
|
|
2206
|
+
}));
|
|
2028
2207
|
return actions;
|
|
2029
2208
|
}
|
|
2030
2209
|
function planCursorInstallActions(targetPath, adapter, pinnedVersion) {
|
|
@@ -2060,7 +2239,7 @@ function planCursorInstallActions(targetPath, adapter, pinnedVersion) {
|
|
|
2060
2239
|
content: renderCursorRule(),
|
|
2061
2240
|
}));
|
|
2062
2241
|
}
|
|
2063
|
-
actions.push(planSkillInstallAction("cursor", targetPath, adapter, CURSOR_ONBOARD_SKILL_RELATIVE_PATH, renderCursorOnboardSkill, "gdh-onboard", pinnedVersion), planSkillInstallAction("cursor", targetPath, adapter, CURSOR_STATUS_SKILL_RELATIVE_PATH, renderCursorStatusSkill, "gdh-status", pinnedVersion), planSkillInstallAction("cursor", targetPath, adapter, CURSOR_MIGRATE_SKILL_RELATIVE_PATH, renderCursorMigrateSkill, "gdh-migrate", pinnedVersion), planSkillInstallAction("cursor", targetPath, adapter, CURSOR_CHECK_SKILL_RELATIVE_PATH, renderCursorCheckSkill, "gdh-check", pinnedVersion), planSkillInstallAction("cursor", targetPath, adapter, CURSOR_PREPARE_SKILL_RELATIVE_PATH, renderCursorPrepareSkill, "gdh-prepare", pinnedVersion), planSkillInstallAction("cursor", targetPath, adapter, CURSOR_VERIFY_SKILL_RELATIVE_PATH, renderCursorVerifySkill, "gdh-verify", pinnedVersion), planSkillInstallAction("cursor", targetPath, adapter, CURSOR_SCAN_SKILL_RELATIVE_PATH, renderCursorScanSkill, "gdh-scan", pinnedVersion));
|
|
2242
|
+
actions.push(planSkillInstallAction("cursor", targetPath, adapter, CURSOR_ONBOARD_SKILL_RELATIVE_PATH, renderCursorOnboardSkill, "gdh-onboard", pinnedVersion), planSkillInstallAction("cursor", targetPath, adapter, CURSOR_STATUS_SKILL_RELATIVE_PATH, renderCursorStatusSkill, "gdh-status", pinnedVersion), planSkillInstallAction("cursor", targetPath, adapter, CURSOR_MIGRATE_SKILL_RELATIVE_PATH, renderCursorMigrateSkill, "gdh-migrate", pinnedVersion), planSkillInstallAction("cursor", targetPath, adapter, CURSOR_UPDATE_SKILL_RELATIVE_PATH, renderCursorUpdateSkill, "gdh-update", pinnedVersion), planSkillInstallAction("cursor", targetPath, adapter, CURSOR_CHECK_SKILL_RELATIVE_PATH, renderCursorCheckSkill, "gdh-check", pinnedVersion), planSkillInstallAction("cursor", targetPath, adapter, CURSOR_PREPARE_SKILL_RELATIVE_PATH, renderCursorPrepareSkill, "gdh-prepare", pinnedVersion), planSkillInstallAction("cursor", targetPath, adapter, CURSOR_VERIFY_SKILL_RELATIVE_PATH, renderCursorVerifySkill, "gdh-verify", pinnedVersion), planSkillInstallAction("cursor", targetPath, adapter, CURSOR_SCAN_SKILL_RELATIVE_PATH, renderCursorScanSkill, "gdh-scan", pinnedVersion));
|
|
2064
2243
|
return actions;
|
|
2065
2244
|
}
|
|
2066
2245
|
function dedupeInstallActions(actions) {
|
|
@@ -2158,64 +2337,11 @@ function resolveProjectMcpEnabled(projectConfig) {
|
|
|
2158
2337
|
function createCodexServerName(projectKeySeed) {
|
|
2159
2338
|
return `gdh-${projectKeySeed}`;
|
|
2160
2339
|
}
|
|
2161
|
-
export function renderManagedMcpLauncher(pinnedVersion) {
|
|
2162
|
-
return [
|
|
2163
|
-
"#!/usr/bin/env node",
|
|
2164
|
-
`// GDH MCP launcher v${GDH_MCP_LAUNCHER_VERSION}`,
|
|
2165
|
-
'import fs from "node:fs";',
|
|
2166
|
-
'import path from "node:path";',
|
|
2167
|
-
'import { spawnSync } from "node:child_process";',
|
|
2168
|
-
'import { fileURLToPath } from "node:url";',
|
|
2169
|
-
"",
|
|
2170
|
-
"const launcherPath = fileURLToPath(import.meta.url);",
|
|
2171
|
-
'const integrationRootPath = path.resolve(path.dirname(launcherPath), "../..");',
|
|
2172
|
-
'const targetOptionIndex = process.argv.indexOf("--target");',
|
|
2173
|
-
"const configuredTargetPath =",
|
|
2174
|
-
' targetOptionIndex >= 0 && typeof process.argv[targetOptionIndex + 1] === "string"',
|
|
2175
|
-
" ? process.argv[targetOptionIndex + 1]",
|
|
2176
|
-
" : null;",
|
|
2177
|
-
"const targetPath = configuredTargetPath",
|
|
2178
|
-
" ? path.resolve(integrationRootPath, configuredTargetPath)",
|
|
2179
|
-
" : integrationRootPath;",
|
|
2180
|
-
'const hintsPath = path.join(integrationRootPath, ".gdh-state", "local-paths.json");',
|
|
2181
|
-
"",
|
|
2182
|
-
"let gdhDevRepoPath = null;",
|
|
2183
|
-
"try {",
|
|
2184
|
-
' const raw = fs.readFileSync(hintsPath, "utf8");',
|
|
2185
|
-
" const parsed = JSON.parse(raw);",
|
|
2186
|
-
' if (typeof parsed.gdhDevRepoPath === "string" && parsed.gdhDevRepoPath.length > 0) {',
|
|
2187
|
-
" gdhDevRepoPath = parsed.gdhDevRepoPath;",
|
|
2188
|
-
" }",
|
|
2189
|
-
"} catch {}",
|
|
2190
|
-
"",
|
|
2191
|
-
"const effectiveDevRepoPath = process.env.GDH_DEV_REPO?.trim() || gdhDevRepoPath;",
|
|
2192
|
-
"if (effectiveDevRepoPath) {",
|
|
2193
|
-
' const devCliEntryPath = path.join(effectiveDevRepoPath, "packages/cli/dist/cli.js");',
|
|
2194
|
-
" if (fs.existsSync(devCliEntryPath)) {",
|
|
2195
|
-
' const result = spawnSync(process.execPath, [devCliEntryPath, "mcp", "serve", "--target", targetPath], { stdio: "inherit" });',
|
|
2196
|
-
" process.exit(result.status ?? 1);",
|
|
2197
|
-
" }",
|
|
2198
|
-
"}",
|
|
2199
|
-
"",
|
|
2200
|
-
`const result = spawnSync("npx", ["-y", "@skillcap/gdh@${pinnedVersion}", "mcp", "serve", "--target", targetPath], { stdio: "inherit", cwd: targetPath });`,
|
|
2201
|
-
'if (result.error && result.error.code === "ENOENT") {',
|
|
2202
|
-
` console.error("GDH MCP launcher could not launch npx for @skillcap/gdh@${pinnedVersion}. Ensure Node.js 20+ is installed (npx ships with Node), or configure the contributor dev escape hatch via the GDH_DEV_REPO env var or .gdh-state/local-paths.json gdhDevRepoPath.");`,
|
|
2203
|
-
" process.exit(1);",
|
|
2204
|
-
"}",
|
|
2205
|
-
"process.exit(result.status ?? 1);",
|
|
2206
|
-
"",
|
|
2207
|
-
].join("\n");
|
|
2208
|
-
}
|
|
2209
2340
|
function buildManagedMcpServerEntry(input) {
|
|
2210
2341
|
return {
|
|
2211
2342
|
type: "stdio",
|
|
2212
|
-
command: "
|
|
2213
|
-
args:
|
|
2214
|
-
targetPath: input.targetPath,
|
|
2215
|
-
integrationRootPath: input.integrationRootPath,
|
|
2216
|
-
launcherPath: input.launcherPathForConfig,
|
|
2217
|
-
useAbsoluteTargetPath: true,
|
|
2218
|
-
}),
|
|
2343
|
+
command: "npx",
|
|
2344
|
+
args: ["-y", `@skillcap/gdh@${input.pinnedVersion}`, "mcp", "serve"],
|
|
2219
2345
|
};
|
|
2220
2346
|
}
|
|
2221
2347
|
function getManagedMcpServerEntry(jsonObject) {
|
|
@@ -2256,6 +2382,64 @@ function readExistingMcpConfig(absolutePath) {
|
|
|
2256
2382
|
return {};
|
|
2257
2383
|
}
|
|
2258
2384
|
}
|
|
2385
|
+
// Renders the managed [mcp_servers.gdh] section for Codex project-local
|
|
2386
|
+
// config. The section body is line-based (no TOML parser) — preserves any
|
|
2387
|
+
// other content the user has in the file via regex replacement of the
|
|
2388
|
+
// bracketed header span. Exotic forms (inline-table `mcp_servers.gdh = {...}`
|
|
2389
|
+
// or dotted-key `mcp_servers.gdh.command = "..."`) are not detected; users
|
|
2390
|
+
// with those forms may end up with a duplicate and must migrate manually.
|
|
2391
|
+
export function renderManagedCodexProjectSection(pinnedVersion) {
|
|
2392
|
+
return [
|
|
2393
|
+
"[mcp_servers.gdh]",
|
|
2394
|
+
'command = "npx"',
|
|
2395
|
+
`args = ["-y", "@skillcap/gdh@${pinnedVersion}", "mcp", "serve"]`,
|
|
2396
|
+
].join("\n");
|
|
2397
|
+
}
|
|
2398
|
+
export function renderManagedCodexProjectConfig(existingContent, pinnedVersion) {
|
|
2399
|
+
const section = renderManagedCodexProjectSection(pinnedVersion);
|
|
2400
|
+
if (existingContent === null || existingContent.trim() === "") {
|
|
2401
|
+
return `${section}\n`;
|
|
2402
|
+
}
|
|
2403
|
+
const lines = existingContent.split("\n");
|
|
2404
|
+
const sectionStart = lines.findIndex((line) => /^\[mcp_servers\.gdh\]\s*$/.test(line));
|
|
2405
|
+
if (sectionStart === -1) {
|
|
2406
|
+
const trailingNewline = existingContent.endsWith("\n") ? "" : "\n";
|
|
2407
|
+
const separator = existingContent.length > 0 ? "\n" : "";
|
|
2408
|
+
return `${existingContent}${trailingNewline}${separator}${section}\n`;
|
|
2409
|
+
}
|
|
2410
|
+
let sectionEnd = lines.length;
|
|
2411
|
+
for (let i = sectionStart + 1; i < lines.length; i++) {
|
|
2412
|
+
if (/^\[/.test(lines[i] ?? "")) {
|
|
2413
|
+
sectionEnd = i;
|
|
2414
|
+
break;
|
|
2415
|
+
}
|
|
2416
|
+
}
|
|
2417
|
+
const before = lines.slice(0, sectionStart).join("\n");
|
|
2418
|
+
const afterLines = lines.slice(sectionEnd);
|
|
2419
|
+
const after = afterLines.join("\n");
|
|
2420
|
+
const beforeJoin = before.length > 0 ? `${before}\n` : "";
|
|
2421
|
+
const afterNonEmpty = after.replace(/^\s+/, "");
|
|
2422
|
+
const afterJoin = afterNonEmpty.length > 0 ? `\n\n${afterNonEmpty}` : "\n";
|
|
2423
|
+
return `${beforeJoin}${section}${afterJoin}`;
|
|
2424
|
+
}
|
|
2425
|
+
function extractManagedCodexSection(content) {
|
|
2426
|
+
const lines = content.split("\n");
|
|
2427
|
+
const start = lines.findIndex((line) => /^\[mcp_servers\.gdh\]\s*$/.test(line));
|
|
2428
|
+
if (start === -1) {
|
|
2429
|
+
return null;
|
|
2430
|
+
}
|
|
2431
|
+
let end = lines.length;
|
|
2432
|
+
for (let i = start + 1; i < lines.length; i++) {
|
|
2433
|
+
if (/^\[/.test(lines[i] ?? "")) {
|
|
2434
|
+
end = i;
|
|
2435
|
+
break;
|
|
2436
|
+
}
|
|
2437
|
+
}
|
|
2438
|
+
return lines
|
|
2439
|
+
.slice(start, end)
|
|
2440
|
+
.join("\n")
|
|
2441
|
+
.replace(/\s+$/, "");
|
|
2442
|
+
}
|
|
2259
2443
|
function normalizeJson(value) {
|
|
2260
2444
|
if (Array.isArray(value)) {
|
|
2261
2445
|
return value.map((entry) => normalizeJson(entry));
|
|
@@ -2395,14 +2579,19 @@ async function listCodexMcpServers() {
|
|
|
2395
2579
|
}
|
|
2396
2580
|
}
|
|
2397
2581
|
function isMatchingCodexRegistration(registration, input) {
|
|
2398
|
-
const
|
|
2399
|
-
|
|
2400
|
-
|
|
2401
|
-
|
|
2402
|
-
|
|
2403
|
-
|
|
2582
|
+
const resolvedTargetPath = path.resolve(input.targetPath);
|
|
2583
|
+
const resolvedIntegrationRootPath = path.resolve(input.integrationRootPath);
|
|
2584
|
+
const expectedArgs = [
|
|
2585
|
+
"-y",
|
|
2586
|
+
`@skillcap/gdh@${input.pinnedVersion}`,
|
|
2587
|
+
"mcp",
|
|
2588
|
+
"serve",
|
|
2589
|
+
];
|
|
2590
|
+
if (resolvedTargetPath !== resolvedIntegrationRootPath) {
|
|
2591
|
+
expectedArgs.push("--target", resolvedTargetPath);
|
|
2592
|
+
}
|
|
2404
2593
|
return (registration.transport?.type === "stdio" &&
|
|
2405
|
-
registration.transport.command === "
|
|
2594
|
+
registration.transport.command === "npx" &&
|
|
2406
2595
|
JSON.stringify(registration.transport.args ?? []) === JSON.stringify(expectedArgs));
|
|
2407
2596
|
}
|
|
2408
2597
|
async function inspectProjectConfigLifecycleSurface(targetPath) {
|
|
@@ -2835,60 +3024,6 @@ function inspectCursorRuleLifecycleSurface(targetPath, adapterStatus) {
|
|
|
2835
3024
|
},
|
|
2836
3025
|
});
|
|
2837
3026
|
}
|
|
2838
|
-
function inspectMcpLauncherLifecycleSurface(targetPath, mcpEnabled, adapterStatus) {
|
|
2839
|
-
if (!mcpEnabled) {
|
|
2840
|
-
return createLifecycleSurfaceStatus({
|
|
2841
|
-
surface: "mcp_launcher",
|
|
2842
|
-
management: "managed",
|
|
2843
|
-
state: "compatible",
|
|
2844
|
-
summary: "Project-scoped MCP launcher is not required while MCP is disabled.",
|
|
2845
|
-
reasons: [],
|
|
2846
|
-
probes: [],
|
|
2847
|
-
action: null,
|
|
2848
|
-
});
|
|
2849
|
-
}
|
|
2850
|
-
const launcherSurface = findAdapterSurface(adapterStatus, "claude", "launcher_file") ??
|
|
2851
|
-
findAdapterSurface(adapterStatus, "cursor", "launcher_file") ??
|
|
2852
|
-
findAdapterSurface(adapterStatus, "codex", "launcher_file");
|
|
2853
|
-
const probes = launcherSurface === null
|
|
2854
|
-
? []
|
|
2855
|
-
: [
|
|
2856
|
-
createVersionProbe({
|
|
2857
|
-
targetPath,
|
|
2858
|
-
relativePath: launcherSurface.relativePath ?? MCP_LAUNCHER_RELATIVE_PATH,
|
|
2859
|
-
present: launcherSurface.present,
|
|
2860
|
-
expectedVersion: GDH_MCP_LAUNCHER_VERSION,
|
|
2861
|
-
detectedVersion: launcherSurface.present ? launcherSurface.version : null,
|
|
2862
|
-
}),
|
|
2863
|
-
];
|
|
2864
|
-
if (launcherSurface?.state === "ready") {
|
|
2865
|
-
return createLifecycleSurfaceStatus({
|
|
2866
|
-
surface: "mcp_launcher",
|
|
2867
|
-
management: "managed",
|
|
2868
|
-
state: "compatible",
|
|
2869
|
-
summary: "Managed MCP launcher file matches the current GDH version.",
|
|
2870
|
-
reasons: [],
|
|
2871
|
-
probes,
|
|
2872
|
-
action: null,
|
|
2873
|
-
});
|
|
2874
|
-
}
|
|
2875
|
-
return createLifecycleSurfaceStatus({
|
|
2876
|
-
surface: "mcp_launcher",
|
|
2877
|
-
management: "managed",
|
|
2878
|
-
state: launcherSurface?.present ? "migration_available" : "migration_needed",
|
|
2879
|
-
summary: "Managed MCP launcher file needs to be created or refreshed through the adapter install flow.",
|
|
2880
|
-
reasons: launcherSurface?.present
|
|
2881
|
-
? ["mcp_launcher_misconfigured"]
|
|
2882
|
-
: ["mcp_launcher_missing"],
|
|
2883
|
-
probes,
|
|
2884
|
-
action: {
|
|
2885
|
-
kind: "run_repair",
|
|
2886
|
-
summary: "Run GDH migrate or adapters install to refresh the managed MCP launcher file.",
|
|
2887
|
-
command: ["gdh", "adapters", "install", targetPath],
|
|
2888
|
-
autoApplicable: true,
|
|
2889
|
-
},
|
|
2890
|
-
});
|
|
2891
|
-
}
|
|
2892
3027
|
function inspectRuntimeBridgeLifecycleSurface(targetPath, bridgeStatus) {
|
|
2893
3028
|
const probes = bridgeStatus.managedArtifacts.map((artifact) => createVersionProbe({
|
|
2894
3029
|
targetPath,
|
|
@@ -3279,4 +3414,7 @@ function deriveRepoState(context) {
|
|
|
3279
3414
|
function normalizeChangedFiles(files) {
|
|
3280
3415
|
return [...new Set(files.map((file) => file.trim()).filter((file) => file.length > 0))];
|
|
3281
3416
|
}
|
|
3417
|
+
export { bumpAndRebakePin, } from "./self-update-mechanics.js";
|
|
3418
|
+
export { CLAUDE_CHECK_UPDATE_HOOK_RELATIVE_PATH } from "./claude-update-hook-render.js";
|
|
3419
|
+
export { CLAUDE_STATUSLINE_RELATIVE_PATH } from "./claude-statusline-render.js";
|
|
3282
3420
|
//# sourceMappingURL=index.js.map
|