@skillcap/gdh 0.6.0 → 0.8.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 +23 -5
- package/node_modules/@gdh/adapters/dist/index.d.ts +6 -3
- package/node_modules/@gdh/adapters/dist/index.d.ts.map +1 -1
- package/node_modules/@gdh/adapters/dist/index.js +222 -258
- package/node_modules/@gdh/adapters/dist/index.js.map +1 -1
- package/node_modules/@gdh/adapters/dist/self-update-mechanics.d.ts.map +1 -1
- package/node_modules/@gdh/adapters/dist/self-update-mechanics.js +9 -1
- package/node_modules/@gdh/adapters/dist/self-update-mechanics.js.map +1 -1
- package/node_modules/@gdh/adapters/package.json +8 -8
- package/node_modules/@gdh/authoring/dist/prepare.d.ts.map +1 -1
- package/node_modules/@gdh/authoring/dist/prepare.js +5 -1
- package/node_modules/@gdh/authoring/dist/prepare.js.map +1 -1
- package/node_modules/@gdh/authoring/package.json +2 -2
- package/node_modules/@gdh/cli/dist/index.d.ts +35 -0
- package/node_modules/@gdh/cli/dist/index.d.ts.map +1 -1
- package/node_modules/@gdh/cli/dist/index.js +279 -150
- package/node_modules/@gdh/cli/dist/index.js.map +1 -1
- package/node_modules/@gdh/cli/dist/migrate.d.ts +5 -1
- package/node_modules/@gdh/cli/dist/migrate.d.ts.map +1 -1
- package/node_modules/@gdh/cli/dist/migrate.js +57 -3
- package/node_modules/@gdh/cli/dist/migrate.js.map +1 -1
- package/node_modules/@gdh/cli/dist/self-update.d.ts.map +1 -1
- package/node_modules/@gdh/cli/dist/self-update.js +52 -3
- package/node_modules/@gdh/cli/dist/self-update.js.map +1 -1
- package/node_modules/@gdh/cli/dist/setup.d.ts +41 -0
- package/node_modules/@gdh/cli/dist/setup.d.ts.map +1 -1
- package/node_modules/@gdh/cli/dist/setup.js +181 -26
- package/node_modules/@gdh/cli/dist/setup.js.map +1 -1
- package/node_modules/@gdh/cli/package.json +10 -10
- package/node_modules/@gdh/core/dist/index.d.ts +77 -4
- package/node_modules/@gdh/core/dist/index.d.ts.map +1 -1
- package/node_modules/@gdh/core/dist/index.js +45 -6
- package/node_modules/@gdh/core/dist/index.js.map +1 -1
- package/node_modules/@gdh/core/dist/update-cache.d.ts +12 -0
- package/node_modules/@gdh/core/dist/update-cache.d.ts.map +1 -1
- package/node_modules/@gdh/core/dist/update-cache.js +20 -0
- package/node_modules/@gdh/core/dist/update-cache.js.map +1 -1
- package/node_modules/@gdh/core/dist/update-probe.d.ts.map +1 -1
- package/node_modules/@gdh/core/dist/update-probe.js +7 -1
- package/node_modules/@gdh/core/dist/update-probe.js.map +1 -1
- package/node_modules/@gdh/core/package.json +1 -1
- package/node_modules/@gdh/docs/dist/index.d.ts +2 -0
- package/node_modules/@gdh/docs/dist/index.d.ts.map +1 -1
- package/node_modules/@gdh/docs/dist/index.js +1 -0
- package/node_modules/@gdh/docs/dist/index.js.map +1 -1
- package/node_modules/@gdh/docs/dist/recovery-hints.d.ts +43 -0
- package/node_modules/@gdh/docs/dist/recovery-hints.d.ts.map +1 -0
- package/node_modules/@gdh/docs/dist/recovery-hints.js +203 -0
- package/node_modules/@gdh/docs/dist/recovery-hints.js.map +1 -0
- 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/dist/detect-existing-onboarding.d.ts +19 -0
- package/node_modules/@gdh/scan/dist/detect-existing-onboarding.d.ts.map +1 -0
- package/node_modules/@gdh/scan/dist/detect-existing-onboarding.js +58 -0
- package/node_modules/@gdh/scan/dist/detect-existing-onboarding.js.map +1 -0
- package/node_modules/@gdh/scan/dist/index.d.ts +1 -0
- package/node_modules/@gdh/scan/dist/index.d.ts.map +1 -1
- package/node_modules/@gdh/scan/dist/index.js +1 -0
- package/node_modules/@gdh/scan/dist/index.js.map +1 -1
- package/node_modules/@gdh/scan/dist/onboard.d.ts +2 -0
- package/node_modules/@gdh/scan/dist/onboard.d.ts.map +1 -1
- package/node_modules/@gdh/scan/dist/onboard.js +12 -4
- package/node_modules/@gdh/scan/dist/onboard.js.map +1 -1
- package/node_modules/@gdh/scan/package.json +3 -3
- package/node_modules/@gdh/verify/package.json +7 -7
- package/package.json +11 -11
|
@@ -9,8 +9,8 @@ import { CLAUDE_CHECK_UPDATE_HOOK_RELATIVE_PATH, CLAUDE_CHECK_UPDATE_WORKER_RELA
|
|
|
9
9
|
import { renderClaudeCheckUpdateWorker } from "./claude-update-worker-render.js";
|
|
10
10
|
import { CLAUDE_STATUSLINE_RELATIVE_PATH, renderClaudeUpdateStatusline, } from "./claude-statusline-render.js";
|
|
11
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,
|
|
13
|
-
import { createDefaultGuidanceUnits, getGuidanceStatus, resolveGuidanceQuery, } from "@gdh/docs";
|
|
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, assertProjectLifecycleCompatibilityInvariant, definePackageBoundary, resolveCurrentGdhInstall, resolveGdhProductMetadata, } from "@gdh/core";
|
|
13
|
+
import { createDefaultGuidanceUnits, getGuidanceStatus, resolveGuidanceQuery, resolveRecoveryHints, } from "@gdh/docs";
|
|
14
14
|
import { inspectGuidanceAudit } from "@gdh/observability";
|
|
15
15
|
import { inspectRuntimeBridgeSurface } from "@gdh/runtime";
|
|
16
16
|
import { scanGodotProjectInventory } from "@gdh/scan";
|
|
@@ -65,8 +65,13 @@ export const CLAUDE_SCAN_COMMAND_RELATIVE_PATH = ".claude/commands/gdh/scan.md";
|
|
|
65
65
|
export const CODEX_SCAN_SKILL_RELATIVE_PATH = ".codex/skills/gdh-scan/SKILL.md";
|
|
66
66
|
export const CURSOR_SCAN_SKILL_RELATIVE_PATH = ".cursor/skills/gdh-scan/SKILL.md";
|
|
67
67
|
export const LOCAL_PATH_HINTS_RELATIVE_PATH = ".gdh-state/local-paths.json";
|
|
68
|
-
export const
|
|
68
|
+
export const CODEX_PROJECT_CONFIG_RELATIVE_PATH = ".codex/config.toml";
|
|
69
69
|
export const GDH_MCP_SERVER_NAME = "gdh";
|
|
70
|
+
// Phase 17 UX-01: single source of truth for the `/gdh-status` preview-header
|
|
71
|
+
// literal. The character between "dry-run)" and "not" is em-dash U+2014 (NOT
|
|
72
|
+
// two hyphens). Centralizing here prevents silent drift across the 3
|
|
73
|
+
// `/gdh-status` renderer bodies (Pitfall #5 three-adapter parity).
|
|
74
|
+
const PREVIEW_HEADER_LITERAL = "Preview (dry-run) — not applied";
|
|
70
75
|
const execFile = promisify(execFileCallback);
|
|
71
76
|
export async function getSupportedAgentAdaptersStatus(targetPath, options = {}) {
|
|
72
77
|
const includeUserLocal = options.includeUserLocal ?? true;
|
|
@@ -216,17 +221,18 @@ export async function inspectProjectLifecycleCompatibility(targetPath) {
|
|
|
216
221
|
await inspectGuidanceUnitLifecycleSurface(resolvedTargetPath, projectConfig),
|
|
217
222
|
inspectMcpManifestLifecycleSurface(resolvedTargetPath, projectConfig.mcp.enabled, adapterStatus),
|
|
218
223
|
inspectCursorRuleLifecycleSurface(resolvedTargetPath, adapterStatus),
|
|
219
|
-
inspectMcpLauncherLifecycleSurface(resolvedTargetPath, projectConfig.mcp.enabled, adapterStatus),
|
|
220
224
|
inspectRuntimeBridgeLifecycleSurface(resolvedTargetPath, bridgeStatus),
|
|
221
225
|
]);
|
|
222
226
|
}
|
|
223
227
|
export async function buildGdhStatusResult(targetPath) {
|
|
224
228
|
const context = await buildAuthoringContext(targetPath);
|
|
225
229
|
const projectLifecycle = await inspectProjectLifecycleCompatibility(targetPath);
|
|
230
|
+
const recoveryHints = resolveRecoveryHints(projectLifecycle.reasons);
|
|
226
231
|
return {
|
|
227
232
|
...context.status,
|
|
228
233
|
product: resolveGdhProductMetadata(),
|
|
229
234
|
projectLifecycle,
|
|
235
|
+
recoveryHints,
|
|
230
236
|
};
|
|
231
237
|
}
|
|
232
238
|
export async function createGsdSnapshot(targetPath, options = {}) {
|
|
@@ -471,9 +477,12 @@ export function renderClaudeStatusCommand(pinnedVersion) {
|
|
|
471
477
|
"Follow this order:",
|
|
472
478
|
"",
|
|
473
479
|
`1. Run \`npx -y @skillcap/gdh@${pinnedVersion} status\` and explain each readiness field.`,
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
480
|
+
"2. Distinguish `blockingReasons` (needs /gdh-update or manual intervention) from `coupledReasons` (fixable via /gdh-migrate).",
|
|
481
|
+
`3. Check if migration is needed with \`npx -y @skillcap/gdh@${pinnedVersion} migrate\`.`,
|
|
482
|
+
`4. ${PREVIEW_HEADER_LITERAL} — migrate output at this step describes planned actions ("would delete …") not applied ones ("deleted").`,
|
|
483
|
+
"5. If status JSON `recoveryHints` array is non-empty, surface each entry's `{command, skillName}` pair.",
|
|
484
|
+
"6. Surface any degraded or unavailable capabilities.",
|
|
485
|
+
"7. Suggest the most productive next step based on current state.",
|
|
477
486
|
"</process>",
|
|
478
487
|
"",
|
|
479
488
|
"<rules>",
|
|
@@ -512,7 +521,10 @@ export function renderCodexStatusSkill(pinnedVersion) {
|
|
|
512
521
|
"Follow this order:",
|
|
513
522
|
"",
|
|
514
523
|
`- run \`npx -y @skillcap/gdh@${pinnedVersion} status\` and explain each readiness field`,
|
|
524
|
+
"- distinguish `blockingReasons` (needs /gdh-update or manual intervention) from `coupledReasons` (fixable via /gdh-migrate)",
|
|
515
525
|
`- check if migration is needed with \`npx -y @skillcap/gdh@${pinnedVersion} migrate\``,
|
|
526
|
+
`- ${PREVIEW_HEADER_LITERAL} — migrate output describes planned actions ("would delete …") not applied ones ("deleted")`,
|
|
527
|
+
"- if status JSON `recoveryHints` array is non-empty, surface each entry's `{command, skillName}` pair",
|
|
516
528
|
"- surface any degraded or unavailable capabilities",
|
|
517
529
|
"- suggest the most productive next step based on current state",
|
|
518
530
|
"</process>",
|
|
@@ -551,7 +563,10 @@ export function renderCursorStatusSkill(pinnedVersion) {
|
|
|
551
563
|
"Follow this order:",
|
|
552
564
|
"",
|
|
553
565
|
`- run \`npx -y @skillcap/gdh@${pinnedVersion} status\` and explain each readiness field`,
|
|
566
|
+
"- distinguish `blockingReasons` (needs /gdh-update or manual intervention) from `coupledReasons` (fixable via /gdh-migrate)",
|
|
554
567
|
`- check if migration is needed with \`npx -y @skillcap/gdh@${pinnedVersion} migrate\``,
|
|
568
|
+
`- ${PREVIEW_HEADER_LITERAL} — migrate output describes planned actions ("would delete …") not applied ones ("deleted")`,
|
|
569
|
+
"- if status JSON `recoveryHints` array is non-empty, surface each entry's `{command, skillName}` pair",
|
|
555
570
|
"- surface any degraded or unavailable capabilities",
|
|
556
571
|
"- suggest the most productive next step based on current state",
|
|
557
572
|
"</process>",
|
|
@@ -695,10 +710,11 @@ export function renderClaudeMigrateCommand(pinnedVersion) {
|
|
|
695
710
|
"<process>",
|
|
696
711
|
"Follow this order:",
|
|
697
712
|
"",
|
|
698
|
-
`1. Run \`npx -y @skillcap/gdh@${pinnedVersion} migrate\` to preview pending migrations.`,
|
|
699
|
-
"2.
|
|
700
|
-
|
|
701
|
-
`4.
|
|
713
|
+
`1. Run \`npx -y @skillcap/gdh@${pinnedVersion} migrate\` to preview pending migrations (this is a dry-run; the JSON \`mode\` field will be \`"preview"\`).`,
|
|
714
|
+
"2. Inspect `compatibility.blockingReasons` vs `compatibility.coupledReasons` — if `blockingReasons` is non-empty, STOP: `migrate --apply` cannot fix them (recommend `/gdh-update` or manual intervention per each reason's recovery hint). If only `coupledReasons` is non-empty, proceed.",
|
|
715
|
+
"3. Explain what each migration step will change and why (use \"would\" verb forms — \"would delete\", \"would create\", \"would refresh\").",
|
|
716
|
+
`4. Offer to run \`npx -y @skillcap/gdh@${pinnedVersion} migrate --apply\` if the user approves — after apply, the JSON \`mode\` field becomes \`"applied"\` and verbs switch to past tense ("deleted", "created", "refreshed").`,
|
|
717
|
+
`5. After apply, run \`npx -y @skillcap/gdh@${pinnedVersion} status\` to verify the migration succeeded.`,
|
|
702
718
|
"</process>",
|
|
703
719
|
"",
|
|
704
720
|
"<rules>",
|
|
@@ -735,9 +751,10 @@ export function renderCodexMigrateSkill(pinnedVersion) {
|
|
|
735
751
|
"<process>",
|
|
736
752
|
"Follow this order:",
|
|
737
753
|
"",
|
|
738
|
-
`- run \`npx -y @skillcap/gdh@${pinnedVersion} migrate\` to preview pending migrations`,
|
|
739
|
-
"-
|
|
740
|
-
|
|
754
|
+
`- run \`npx -y @skillcap/gdh@${pinnedVersion} migrate\` to preview pending migrations (this is a dry-run; the JSON \`mode\` field will be \`"preview"\`)`,
|
|
755
|
+
"- inspect `compatibility.blockingReasons` vs `compatibility.coupledReasons` — if `blockingReasons` is non-empty, STOP: `migrate --apply` cannot fix them (recommend `/gdh-update` or manual intervention per each reason's recovery hint); if only `coupledReasons` is non-empty, proceed",
|
|
756
|
+
"- explain what each migration step will change and why (use \"would\" verb forms — \"would delete\", \"would create\", \"would refresh\")",
|
|
757
|
+
`- offer to run \`npx -y @skillcap/gdh@${pinnedVersion} migrate --apply\` if the user approves — after apply, the JSON \`mode\` field becomes \`"applied"\` and verbs switch to past tense ("deleted", "created", "refreshed")`,
|
|
741
758
|
`- after apply, run \`npx -y @skillcap/gdh@${pinnedVersion} status\` to verify the migration succeeded`,
|
|
742
759
|
"</process>",
|
|
743
760
|
"",
|
|
@@ -773,9 +790,10 @@ export function renderCursorMigrateSkill(pinnedVersion) {
|
|
|
773
790
|
"<process>",
|
|
774
791
|
"Follow this order:",
|
|
775
792
|
"",
|
|
776
|
-
`- run \`npx -y @skillcap/gdh@${pinnedVersion} migrate\` to preview pending migrations`,
|
|
777
|
-
"-
|
|
778
|
-
|
|
793
|
+
`- run \`npx -y @skillcap/gdh@${pinnedVersion} migrate\` to preview pending migrations (this is a dry-run; the JSON \`mode\` field will be \`"preview"\`)`,
|
|
794
|
+
"- inspect `compatibility.blockingReasons` vs `compatibility.coupledReasons` — if `blockingReasons` is non-empty, STOP: `migrate --apply` cannot fix them (recommend `/gdh-update` or manual intervention per each reason's recovery hint); if only `coupledReasons` is non-empty, proceed",
|
|
795
|
+
"- explain what each migration step will change and why (use \"would\" verb forms — \"would delete\", \"would create\", \"would refresh\")",
|
|
796
|
+
`- offer to run \`npx -y @skillcap/gdh@${pinnedVersion} migrate --apply\` if the user approves — after apply, the JSON \`mode\` field becomes \`"applied"\` and verbs switch to past tense ("deleted", "created", "refreshed")`,
|
|
779
797
|
`- after apply, run \`npx -y @skillcap/gdh@${pinnedVersion} status\` to verify the migration succeeded`,
|
|
780
798
|
"</process>",
|
|
781
799
|
"",
|
|
@@ -831,6 +849,7 @@ export function renderClaudeUpdateCommand(_pinnedVersion) {
|
|
|
831
849
|
"2. Surface the planned version delta and re-bake action count to the human conversationally.",
|
|
832
850
|
"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`).",
|
|
833
851
|
"4. Verify: run `npx -y @skillcap/gdh@latest verify drift` to confirm every baked surface matches the new pin.",
|
|
852
|
+
"5. Read the `pendingMigration` field in the self-update stdout JSON. When non-null, narrate the chain to the human and run `npx -y @skillcap/gdh@latest migrate --apply` as the next action; when null, confirm no migration is pending and stop.",
|
|
834
853
|
"</process>",
|
|
835
854
|
"",
|
|
836
855
|
"<rules>",
|
|
@@ -868,6 +887,7 @@ export function renderCodexUpdateSkill(_pinnedVersion) {
|
|
|
868
887
|
"2. Surface the planned version delta and re-bake action count to the human conversationally.",
|
|
869
888
|
"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`).",
|
|
870
889
|
"4. Verify: run `npx -y @skillcap/gdh@latest verify drift` to confirm every baked surface matches the new pin.",
|
|
890
|
+
"5. Read the `pendingMigration` field in the self-update stdout JSON. When non-null, narrate the chain to the human and run `npx -y @skillcap/gdh@latest migrate --apply` as the next action; when null, confirm no migration is pending and stop.",
|
|
871
891
|
"</process>",
|
|
872
892
|
"",
|
|
873
893
|
"<rules>",
|
|
@@ -903,6 +923,7 @@ export function renderCursorUpdateSkill(_pinnedVersion) {
|
|
|
903
923
|
"2. Surface the planned version delta and re-bake action count to the human conversationally.",
|
|
904
924
|
"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`).",
|
|
905
925
|
"4. Verify: run `npx -y @skillcap/gdh@latest verify drift` to confirm every baked surface matches the new pin.",
|
|
926
|
+
"5. Read the `pendingMigration` field in the self-update stdout JSON. When non-null, narrate the chain to the human and run `npx -y @skillcap/gdh@latest migrate --apply` as the next action; when null, confirm no migration is pending and stop.",
|
|
906
927
|
"</process>",
|
|
907
928
|
"",
|
|
908
929
|
"<rules>",
|
|
@@ -1257,34 +1278,26 @@ export function renderCursorVerifySkill(pinnedVersion) {
|
|
|
1257
1278
|
async function inspectProjectMcpSupport(targetPath, options) {
|
|
1258
1279
|
const projectConfig = await readProjectConfig(targetPath);
|
|
1259
1280
|
const enabled = resolveProjectMcpEnabled(projectConfig);
|
|
1260
|
-
const launcherContent = options.pinnedVersion === null
|
|
1261
|
-
? null
|
|
1262
|
-
: renderManagedMcpLauncher(options.pinnedVersion);
|
|
1263
1281
|
const managedMcpEntry = buildManagedMcpServerEntry({
|
|
1264
|
-
|
|
1265
|
-
integrationRootPath: options.integrationRootPath,
|
|
1266
|
-
launcherPathForConfig: path.resolve(options.integrationRootPath, MCP_LAUNCHER_RELATIVE_PATH),
|
|
1282
|
+
pinnedVersion: options.pinnedVersion ?? "latest",
|
|
1267
1283
|
});
|
|
1268
|
-
const [projectFile, cursorFile,
|
|
1284
|
+
const [projectFile, cursorFile, codexProjectContent, localPathHints] = await Promise.all([
|
|
1269
1285
|
inspectJsonFile(path.join(options.integrationRootPath, PROJECT_MCP_RELATIVE_PATH)),
|
|
1270
1286
|
inspectJsonFile(path.join(options.integrationRootPath, CURSOR_MCP_RELATIVE_PATH)),
|
|
1271
|
-
fs.readFile(path.join(options.integrationRootPath,
|
|
1287
|
+
fs.readFile(path.join(options.integrationRootPath, CODEX_PROJECT_CONFIG_RELATIVE_PATH), "utf8").catch(() => null),
|
|
1272
1288
|
readLocalPathHints(options.integrationRootPath),
|
|
1273
1289
|
]);
|
|
1274
|
-
const launcherBootstrap = await inspectLauncherBootstrap(localPathHints);
|
|
1275
1290
|
const codexConfigPath = path.join(os.homedir(), ".codex/config.toml");
|
|
1276
1291
|
const codexServerName = projectConfig === null ? null : createCodexServerName(projectConfig.projectKeySeed);
|
|
1277
1292
|
return {
|
|
1278
1293
|
enabled,
|
|
1279
1294
|
integrationRootPath: options.integrationRootPath,
|
|
1280
|
-
launcherContent,
|
|
1281
1295
|
localPathHints,
|
|
1282
|
-
launcherBootstrap,
|
|
1283
|
-
launcherFile: inspectLauncherFile(launcherSource, launcherContent, enabled, launcherBootstrap),
|
|
1284
1296
|
projectFile: inspectManagedMcpFile(projectFile, enabled, PROJECT_MCP_RELATIVE_PATH, "Claude project MCP config", managedMcpEntry),
|
|
1285
1297
|
cursorFile: inspectManagedMcpFile(cursorFile, enabled, CURSOR_MCP_RELATIVE_PATH, "Cursor project MCP config", managedMcpEntry),
|
|
1298
|
+
codexProjectFile: inspectManagedCodexProjectFile(codexProjectContent, enabled, options.pinnedVersion),
|
|
1286
1299
|
codexRegistration: options.includeUserLocal
|
|
1287
|
-
? await inspectCodexRegistration(targetPath, enabled, codexServerName, codexConfigPath, options.integrationRootPath)
|
|
1300
|
+
? await inspectCodexRegistration(targetPath, enabled, codexServerName, codexConfigPath, options.integrationRootPath, options.pinnedVersion)
|
|
1288
1301
|
: {
|
|
1289
1302
|
present: false,
|
|
1290
1303
|
state: "ready",
|
|
@@ -1361,71 +1374,52 @@ function inspectManagedMcpFile(file, enabled, relativePath, displayName, expecte
|
|
|
1361
1374
|
summary: `${displayName} includes the managed \`${GDH_MCP_SERVER_NAME}\` MCP server entry.`,
|
|
1362
1375
|
};
|
|
1363
1376
|
}
|
|
1364
|
-
function
|
|
1377
|
+
function inspectManagedCodexProjectFile(content, enabled, pinnedVersion) {
|
|
1378
|
+
const relativePath = CODEX_PROJECT_CONFIG_RELATIVE_PATH;
|
|
1365
1379
|
if (!enabled) {
|
|
1366
1380
|
return {
|
|
1367
|
-
present:
|
|
1381
|
+
present: content !== null,
|
|
1368
1382
|
state: "ready",
|
|
1369
|
-
summary: "
|
|
1383
|
+
summary: "Codex project MCP config is disabled for this target.",
|
|
1370
1384
|
};
|
|
1371
1385
|
}
|
|
1372
|
-
if (
|
|
1386
|
+
if (pinnedVersion === null) {
|
|
1373
1387
|
return {
|
|
1374
|
-
present:
|
|
1388
|
+
present: content !== null,
|
|
1375
1389
|
state: "missing",
|
|
1376
|
-
summary:
|
|
1390
|
+
summary: `Codex project MCP config cannot be validated yet: no \`gdh_version\` is pinned (run \`gdh setup\` or \`gdh migrate --apply\`).`,
|
|
1377
1391
|
};
|
|
1378
1392
|
}
|
|
1379
|
-
if (
|
|
1393
|
+
if (content === null) {
|
|
1380
1394
|
return {
|
|
1381
1395
|
present: false,
|
|
1382
1396
|
state: "missing",
|
|
1383
|
-
summary: `
|
|
1397
|
+
summary: `Codex project MCP config is missing and should define the managed \`[mcp_servers.${GDH_MCP_SERVER_NAME}]\` entry at ${relativePath}.`,
|
|
1384
1398
|
};
|
|
1385
1399
|
}
|
|
1386
|
-
|
|
1400
|
+
const existingSection = extractManagedCodexSection(content);
|
|
1401
|
+
const expectedSection = renderManagedCodexProjectSection(pinnedVersion);
|
|
1402
|
+
if (existingSection === null) {
|
|
1387
1403
|
return {
|
|
1388
1404
|
present: true,
|
|
1389
1405
|
state: "misconfigured",
|
|
1390
|
-
summary:
|
|
1406
|
+
summary: `${relativePath} exists but does not contain the managed \`[mcp_servers.${GDH_MCP_SERVER_NAME}]\` section.`,
|
|
1391
1407
|
};
|
|
1392
1408
|
}
|
|
1393
|
-
|
|
1394
|
-
present: true,
|
|
1395
|
-
state: bootstrap.state,
|
|
1396
|
-
summary: bootstrap.state === "ready"
|
|
1397
|
-
? "The managed GDH MCP launcher is present and can resolve the current GDH bootstrap path."
|
|
1398
|
-
: bootstrap.summary,
|
|
1399
|
-
};
|
|
1400
|
-
}
|
|
1401
|
-
async function inspectLauncherBootstrap(localPathHints) {
|
|
1402
|
-
const envDevRepoPath = process.env["GDH_DEV_REPO"]?.trim() || null;
|
|
1403
|
-
const effectiveDevRepoPath = envDevRepoPath ?? localPathHints.gdhDevRepoPath;
|
|
1404
|
-
if (effectiveDevRepoPath !== null) {
|
|
1405
|
-
const cliEntryPath = path.join(effectiveDevRepoPath, "packages/cli/dist/cli.js");
|
|
1406
|
-
if (await fileExists(cliEntryPath)) {
|
|
1407
|
-
return {
|
|
1408
|
-
state: "ready",
|
|
1409
|
-
summary: `The managed GDH MCP launcher will bootstrap from the current GDH checkout at ${effectiveDevRepoPath}.`,
|
|
1410
|
-
};
|
|
1411
|
-
}
|
|
1409
|
+
if (existingSection !== expectedSection) {
|
|
1412
1410
|
return {
|
|
1411
|
+
present: true,
|
|
1413
1412
|
state: "misconfigured",
|
|
1414
|
-
summary:
|
|
1415
|
-
};
|
|
1416
|
-
}
|
|
1417
|
-
if (await executableExistsOnPath("gdh")) {
|
|
1418
|
-
return {
|
|
1419
|
-
state: "ready",
|
|
1420
|
-
summary: "The managed GDH MCP launcher can resolve `gdh` from PATH.",
|
|
1413
|
+
summary: `${relativePath} managed \`[mcp_servers.${GDH_MCP_SERVER_NAME}]\` section does not match the expected GDH shape.`,
|
|
1421
1414
|
};
|
|
1422
1415
|
}
|
|
1423
1416
|
return {
|
|
1424
|
-
|
|
1425
|
-
|
|
1417
|
+
present: true,
|
|
1418
|
+
state: "ready",
|
|
1419
|
+
summary: `Codex project MCP config includes the managed \`[mcp_servers.${GDH_MCP_SERVER_NAME}]\` entry.`,
|
|
1426
1420
|
};
|
|
1427
1421
|
}
|
|
1428
|
-
async function inspectCodexRegistration(targetPath, enabled, codexServerName, codexConfigPath, integrationRootPath) {
|
|
1422
|
+
async function inspectCodexRegistration(targetPath, enabled, codexServerName, codexConfigPath, integrationRootPath, pinnedVersion) {
|
|
1429
1423
|
if (!enabled || codexServerName === null) {
|
|
1430
1424
|
return {
|
|
1431
1425
|
present: false,
|
|
@@ -1449,16 +1443,22 @@ async function inspectCodexRegistration(targetPath, enabled, codexServerName, co
|
|
|
1449
1443
|
summary: `Codex does not yet have the expected user-local MCP registration \`${codexServerName}\` in ${codexConfigPath}.`,
|
|
1450
1444
|
};
|
|
1451
1445
|
}
|
|
1452
|
-
|
|
1446
|
+
if (pinnedVersion === null) {
|
|
1447
|
+
return {
|
|
1448
|
+
present: true,
|
|
1449
|
+
state: "misconfigured",
|
|
1450
|
+
summary: `Codex MCP registration \`${codexServerName}\` exists but no \`gdh_version\` is pinned; cannot validate the expected npx invocation.`,
|
|
1451
|
+
};
|
|
1452
|
+
}
|
|
1453
1453
|
if (!isMatchingCodexRegistration(registration, {
|
|
1454
1454
|
targetPath,
|
|
1455
1455
|
integrationRootPath,
|
|
1456
|
-
|
|
1456
|
+
pinnedVersion,
|
|
1457
1457
|
})) {
|
|
1458
1458
|
return {
|
|
1459
1459
|
present: true,
|
|
1460
1460
|
state: "misconfigured",
|
|
1461
|
-
summary: `Codex MCP registration \`${codexServerName}\` exists but does not
|
|
1461
|
+
summary: `Codex MCP registration \`${codexServerName}\` exists but does not match the expected \`npx -y @skillcap/gdh@${pinnedVersion} mcp serve\` invocation.`,
|
|
1462
1462
|
};
|
|
1463
1463
|
}
|
|
1464
1464
|
return {
|
|
@@ -1648,14 +1648,14 @@ async function inspectCodexAdapter(targetPath, guidance, projectMcp, pinnedVersi
|
|
|
1648
1648
|
];
|
|
1649
1649
|
if (projectMcp.enabled) {
|
|
1650
1650
|
surfaces.push(createSurfaceStatus({
|
|
1651
|
-
kind: "
|
|
1651
|
+
kind: "mcp_file",
|
|
1652
1652
|
scope: "repo",
|
|
1653
1653
|
targetPath: projectMcp.integrationRootPath,
|
|
1654
|
-
relativePath:
|
|
1655
|
-
present: projectMcp.
|
|
1656
|
-
state: projectMcp.
|
|
1657
|
-
summary: projectMcp.
|
|
1658
|
-
version:
|
|
1654
|
+
relativePath: CODEX_PROJECT_CONFIG_RELATIVE_PATH,
|
|
1655
|
+
present: projectMcp.codexProjectFile.present,
|
|
1656
|
+
state: projectMcp.codexProjectFile.state,
|
|
1657
|
+
summary: projectMcp.codexProjectFile.summary,
|
|
1658
|
+
version: null,
|
|
1659
1659
|
}));
|
|
1660
1660
|
if (options.includeUserLocal) {
|
|
1661
1661
|
surfaces.push(createSurfaceStatus({
|
|
@@ -1763,15 +1763,6 @@ async function inspectClaudeAdapter(targetPath, guidance, projectMcp, pinnedVers
|
|
|
1763
1763
|
];
|
|
1764
1764
|
if (projectMcp.enabled) {
|
|
1765
1765
|
surfaces.push(createSurfaceStatus({
|
|
1766
|
-
kind: "launcher_file",
|
|
1767
|
-
scope: "repo",
|
|
1768
|
-
targetPath: projectMcp.integrationRootPath,
|
|
1769
|
-
relativePath: MCP_LAUNCHER_RELATIVE_PATH,
|
|
1770
|
-
present: projectMcp.launcherFile.present,
|
|
1771
|
-
state: projectMcp.launcherFile.state,
|
|
1772
|
-
summary: projectMcp.launcherFile.summary,
|
|
1773
|
-
version: GDH_MCP_LAUNCHER_VERSION,
|
|
1774
|
-
}), createSurfaceStatus({
|
|
1775
1766
|
kind: "mcp_file",
|
|
1776
1767
|
scope: "repo",
|
|
1777
1768
|
targetPath: projectMcp.integrationRootPath,
|
|
@@ -1863,15 +1854,6 @@ async function inspectCursorAdapter(targetPath, guidance, projectMcp, pinnedVers
|
|
|
1863
1854
|
];
|
|
1864
1855
|
if (projectMcp.enabled) {
|
|
1865
1856
|
surfaces.push(createSurfaceStatus({
|
|
1866
|
-
kind: "launcher_file",
|
|
1867
|
-
scope: "repo",
|
|
1868
|
-
targetPath: projectMcp.integrationRootPath,
|
|
1869
|
-
relativePath: MCP_LAUNCHER_RELATIVE_PATH,
|
|
1870
|
-
present: projectMcp.launcherFile.present,
|
|
1871
|
-
state: projectMcp.launcherFile.state,
|
|
1872
|
-
summary: projectMcp.launcherFile.summary,
|
|
1873
|
-
version: GDH_MCP_LAUNCHER_VERSION,
|
|
1874
|
-
}), createSurfaceStatus({
|
|
1875
1857
|
kind: "mcp_file",
|
|
1876
1858
|
scope: "repo",
|
|
1877
1859
|
targetPath: projectMcp.integrationRootPath,
|
|
@@ -1955,17 +1937,17 @@ async function planInstallActions(targetPath, adapters, requestedAgents, options
|
|
|
1955
1937
|
const effectiveDevRepoPath = options.devRepoPath ?? resolveCurrentGdhInstall(import.meta.url).defaultDevRepoPath;
|
|
1956
1938
|
if (options.user) {
|
|
1957
1939
|
if (requestedAgents.includes("codex")) {
|
|
1958
|
-
actions.push(...planCodexUserInstallActions(targetPath, projectMcp, options.integrationRootPath));
|
|
1940
|
+
actions.push(...planCodexUserInstallActions(targetPath, projectMcp, options.integrationRootPath, options.pinnedVersion));
|
|
1959
1941
|
}
|
|
1960
1942
|
return dedupeInstallActions(actions);
|
|
1961
1943
|
}
|
|
1962
|
-
actions.push(...planSharedRepoInstallActions(targetPath, projectMcp, requestedAgents[0] ?? "claude", effectiveDevRepoPath, options.integrationRootPath));
|
|
1944
|
+
actions.push(...planSharedRepoInstallActions(targetPath, projectMcp, requestedAgents[0] ?? "claude", effectiveDevRepoPath, options.integrationRootPath, options.pinnedVersion));
|
|
1963
1945
|
for (const adapter of adapters) {
|
|
1964
1946
|
if (!requestedAgents.includes(adapter.agent)) {
|
|
1965
1947
|
continue;
|
|
1966
1948
|
}
|
|
1967
1949
|
if (adapter.agent === "codex") {
|
|
1968
|
-
actions.push(...planCodexRepoInstallActions(targetPath, adapter, options.pinnedVersion));
|
|
1950
|
+
actions.push(...planCodexRepoInstallActions(targetPath, adapter, options.pinnedVersion, projectMcp, options.integrationRootPath));
|
|
1969
1951
|
continue;
|
|
1970
1952
|
}
|
|
1971
1953
|
if (adapter.agent === "claude") {
|
|
@@ -1978,27 +1960,11 @@ async function planInstallActions(targetPath, adapters, requestedAgents, options
|
|
|
1978
1960
|
}
|
|
1979
1961
|
return dedupeInstallActions(actions);
|
|
1980
1962
|
}
|
|
1981
|
-
function planSharedRepoInstallActions(targetPath, projectMcp, agent, effectiveDevRepoPath, integrationRootPath) {
|
|
1963
|
+
function planSharedRepoInstallActions(targetPath, projectMcp, agent, effectiveDevRepoPath, integrationRootPath, pinnedVersion) {
|
|
1982
1964
|
if (!projectMcp.enabled) {
|
|
1983
1965
|
return [];
|
|
1984
1966
|
}
|
|
1985
1967
|
const actions = [];
|
|
1986
|
-
if (projectMcp.launcherFile.state !== "ready") {
|
|
1987
|
-
actions.push(createInstallAction({
|
|
1988
|
-
agent,
|
|
1989
|
-
kind: "write_file",
|
|
1990
|
-
scope: "repo",
|
|
1991
|
-
targetPath: integrationRootPath,
|
|
1992
|
-
relativePath: MCP_LAUNCHER_RELATIVE_PATH,
|
|
1993
|
-
state: "planned",
|
|
1994
|
-
mode: projectMcp.launcherFile.present ? "replace" : "create",
|
|
1995
|
-
summary: projectMcp.launcherFile.present
|
|
1996
|
-
? "Replace the managed GDH MCP launcher with the current launcher content."
|
|
1997
|
-
: "Create the managed GDH MCP launcher under .gdh/bin/.",
|
|
1998
|
-
version: GDH_MCP_LAUNCHER_VERSION,
|
|
1999
|
-
content: projectMcp.launcherContent,
|
|
2000
|
-
}));
|
|
2001
|
-
}
|
|
2002
1968
|
if (projectMcp.projectFile.state !== "ready") {
|
|
2003
1969
|
actions.push(createInstallAction({
|
|
2004
1970
|
agent,
|
|
@@ -2011,11 +1977,7 @@ function planSharedRepoInstallActions(targetPath, projectMcp, agent, effectiveDe
|
|
|
2011
1977
|
summary: projectMcp.projectFile.present
|
|
2012
1978
|
? "Replace the managed `gdh` MCP entry in .mcp.json while preserving any non-GDH servers."
|
|
2013
1979
|
: "Create .mcp.json with the managed `gdh` MCP entry.",
|
|
2014
|
-
content: renderManagedMcpConfig(path.join(integrationRootPath, PROJECT_MCP_RELATIVE_PATH), buildManagedMcpServerEntry({
|
|
2015
|
-
targetPath,
|
|
2016
|
-
integrationRootPath,
|
|
2017
|
-
launcherPathForConfig: path.resolve(integrationRootPath, MCP_LAUNCHER_RELATIVE_PATH),
|
|
2018
|
-
})),
|
|
1980
|
+
content: renderManagedMcpConfig(path.join(integrationRootPath, PROJECT_MCP_RELATIVE_PATH), buildManagedMcpServerEntry({ pinnedVersion })),
|
|
2019
1981
|
}));
|
|
2020
1982
|
}
|
|
2021
1983
|
if (projectMcp.cursorFile.state !== "ready") {
|
|
@@ -2030,11 +1992,7 @@ function planSharedRepoInstallActions(targetPath, projectMcp, agent, effectiveDe
|
|
|
2030
1992
|
summary: projectMcp.cursorFile.present
|
|
2031
1993
|
? "Replace the managed `gdh` MCP entry in .cursor/mcp.json while preserving any non-GDH servers."
|
|
2032
1994
|
: "Create .cursor/mcp.json with the managed `gdh` MCP entry.",
|
|
2033
|
-
content: renderManagedMcpConfig(path.join(integrationRootPath, CURSOR_MCP_RELATIVE_PATH), buildManagedMcpServerEntry({
|
|
2034
|
-
targetPath,
|
|
2035
|
-
integrationRootPath,
|
|
2036
|
-
launcherPathForConfig: path.resolve(integrationRootPath, MCP_LAUNCHER_RELATIVE_PATH),
|
|
2037
|
-
})),
|
|
1995
|
+
content: renderManagedMcpConfig(path.join(integrationRootPath, CURSOR_MCP_RELATIVE_PATH), buildManagedMcpServerEntry({ pinnedVersion })),
|
|
2038
1996
|
}));
|
|
2039
1997
|
}
|
|
2040
1998
|
if (projectMcp.localPathHints.gdhDevRepoPath !== effectiveDevRepoPath) {
|
|
@@ -2104,8 +2062,8 @@ function agentLabel(agent) {
|
|
|
2104
2062
|
return "Cursor";
|
|
2105
2063
|
}
|
|
2106
2064
|
}
|
|
2107
|
-
function planCodexRepoInstallActions(targetPath, adapter, pinnedVersion) {
|
|
2108
|
-
|
|
2065
|
+
function planCodexRepoInstallActions(targetPath, adapter, pinnedVersion, projectMcp, integrationRootPath) {
|
|
2066
|
+
const actions = [
|
|
2109
2067
|
planSkillInstallAction("codex", targetPath, adapter, CODEX_ONBOARD_SKILL_RELATIVE_PATH, renderCodexOnboardSkill, "gdh-onboard", pinnedVersion),
|
|
2110
2068
|
planSkillInstallAction("codex", targetPath, adapter, CODEX_STATUS_SKILL_RELATIVE_PATH, renderCodexStatusSkill, "gdh-status", pinnedVersion),
|
|
2111
2069
|
planSkillInstallAction("codex", targetPath, adapter, CODEX_MIGRATE_SKILL_RELATIVE_PATH, renderCodexMigrateSkill, "gdh-migrate", pinnedVersion),
|
|
@@ -2115,18 +2073,42 @@ function planCodexRepoInstallActions(targetPath, adapter, pinnedVersion) {
|
|
|
2115
2073
|
planSkillInstallAction("codex", targetPath, adapter, CODEX_VERIFY_SKILL_RELATIVE_PATH, renderCodexVerifySkill, "gdh-verify", pinnedVersion),
|
|
2116
2074
|
planSkillInstallAction("codex", targetPath, adapter, CODEX_SCAN_SKILL_RELATIVE_PATH, renderCodexScanSkill, "gdh-scan", pinnedVersion),
|
|
2117
2075
|
];
|
|
2076
|
+
if (projectMcp.enabled && projectMcp.codexProjectFile.state !== "ready") {
|
|
2077
|
+
const absolutePath = path.join(integrationRootPath, CODEX_PROJECT_CONFIG_RELATIVE_PATH);
|
|
2078
|
+
const existingContent = fsSync.existsSync(absolutePath)
|
|
2079
|
+
? fsSync.readFileSync(absolutePath, "utf8")
|
|
2080
|
+
: null;
|
|
2081
|
+
actions.push(createInstallAction({
|
|
2082
|
+
agent: "codex",
|
|
2083
|
+
kind: "write_file",
|
|
2084
|
+
scope: "repo",
|
|
2085
|
+
targetPath: integrationRootPath,
|
|
2086
|
+
relativePath: CODEX_PROJECT_CONFIG_RELATIVE_PATH,
|
|
2087
|
+
state: "planned",
|
|
2088
|
+
mode: projectMcp.codexProjectFile.present ? "replace" : "create",
|
|
2089
|
+
summary: projectMcp.codexProjectFile.present
|
|
2090
|
+
? `Replace the managed \`[mcp_servers.${GDH_MCP_SERVER_NAME}]\` section in .codex/config.toml while preserving any non-GDH content.`
|
|
2091
|
+
: `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.`,
|
|
2092
|
+
content: renderManagedCodexProjectConfig(existingContent, pinnedVersion),
|
|
2093
|
+
}));
|
|
2094
|
+
}
|
|
2095
|
+
return actions;
|
|
2118
2096
|
}
|
|
2119
|
-
function planCodexUserInstallActions(targetPath, projectMcp, integrationRootPath) {
|
|
2097
|
+
function planCodexUserInstallActions(targetPath, projectMcp, integrationRootPath, pinnedVersion) {
|
|
2120
2098
|
if (!projectMcp.enabled || projectMcp.codexServerName === null) {
|
|
2121
2099
|
return [];
|
|
2122
2100
|
}
|
|
2123
|
-
const
|
|
2124
|
-
const
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
2101
|
+
const resolvedTargetPath = path.resolve(targetPath);
|
|
2102
|
+
const resolvedIntegrationRootPath = path.resolve(integrationRootPath);
|
|
2103
|
+
const codexCommandArgs = [
|
|
2104
|
+
"-y",
|
|
2105
|
+
`@skillcap/gdh@${pinnedVersion}`,
|
|
2106
|
+
"mcp",
|
|
2107
|
+
"serve",
|
|
2108
|
+
];
|
|
2109
|
+
if (resolvedTargetPath !== resolvedIntegrationRootPath) {
|
|
2110
|
+
codexCommandArgs.push("--target", resolvedTargetPath);
|
|
2111
|
+
}
|
|
2130
2112
|
const actions = [];
|
|
2131
2113
|
if (projectMcp.codexRegistration.present) {
|
|
2132
2114
|
actions.push(createInstallAction({
|
|
@@ -2138,7 +2120,7 @@ function planCodexUserInstallActions(targetPath, projectMcp, integrationRootPath
|
|
|
2138
2120
|
absolutePath: projectMcp.codexConfigPath,
|
|
2139
2121
|
state: "planned",
|
|
2140
2122
|
mode: "replace",
|
|
2141
|
-
summary: `Remove the existing Codex MCP registration \`${projectMcp.codexServerName}\` before re-registering it with the managed GDH
|
|
2123
|
+
summary: `Remove the existing Codex MCP registration \`${projectMcp.codexServerName}\` before re-registering it with the managed GDH npx invocation.`,
|
|
2142
2124
|
command: ["codex", "mcp", "remove", projectMcp.codexServerName],
|
|
2143
2125
|
}));
|
|
2144
2126
|
}
|
|
@@ -2151,15 +2133,15 @@ function planCodexUserInstallActions(targetPath, projectMcp, integrationRootPath
|
|
|
2151
2133
|
absolutePath: projectMcp.codexConfigPath,
|
|
2152
2134
|
state: "planned",
|
|
2153
2135
|
mode: projectMcp.codexRegistration.present ? "replace" : "create",
|
|
2154
|
-
summary: `Register the managed GDH
|
|
2136
|
+
summary: `Register the managed GDH MCP server with Codex as \`${projectMcp.codexServerName}\` via pinned npx invocation.`,
|
|
2155
2137
|
command: [
|
|
2156
2138
|
"codex",
|
|
2157
2139
|
"mcp",
|
|
2158
2140
|
"add",
|
|
2159
2141
|
projectMcp.codexServerName,
|
|
2160
2142
|
"--",
|
|
2161
|
-
"
|
|
2162
|
-
...
|
|
2143
|
+
"npx",
|
|
2144
|
+
...codexCommandArgs,
|
|
2163
2145
|
],
|
|
2164
2146
|
}));
|
|
2165
2147
|
return actions;
|
|
@@ -2377,64 +2359,11 @@ function resolveProjectMcpEnabled(projectConfig) {
|
|
|
2377
2359
|
function createCodexServerName(projectKeySeed) {
|
|
2378
2360
|
return `gdh-${projectKeySeed}`;
|
|
2379
2361
|
}
|
|
2380
|
-
export function renderManagedMcpLauncher(pinnedVersion) {
|
|
2381
|
-
return [
|
|
2382
|
-
"#!/usr/bin/env node",
|
|
2383
|
-
`// GDH MCP launcher v${GDH_MCP_LAUNCHER_VERSION}`,
|
|
2384
|
-
'import fs from "node:fs";',
|
|
2385
|
-
'import path from "node:path";',
|
|
2386
|
-
'import { spawnSync } from "node:child_process";',
|
|
2387
|
-
'import { fileURLToPath } from "node:url";',
|
|
2388
|
-
"",
|
|
2389
|
-
"const launcherPath = fileURLToPath(import.meta.url);",
|
|
2390
|
-
'const integrationRootPath = path.resolve(path.dirname(launcherPath), "../..");',
|
|
2391
|
-
'const targetOptionIndex = process.argv.indexOf("--target");',
|
|
2392
|
-
"const configuredTargetPath =",
|
|
2393
|
-
' targetOptionIndex >= 0 && typeof process.argv[targetOptionIndex + 1] === "string"',
|
|
2394
|
-
" ? process.argv[targetOptionIndex + 1]",
|
|
2395
|
-
" : null;",
|
|
2396
|
-
"const targetPath = configuredTargetPath",
|
|
2397
|
-
" ? path.resolve(integrationRootPath, configuredTargetPath)",
|
|
2398
|
-
" : integrationRootPath;",
|
|
2399
|
-
'const hintsPath = path.join(integrationRootPath, ".gdh-state", "local-paths.json");',
|
|
2400
|
-
"",
|
|
2401
|
-
"let gdhDevRepoPath = null;",
|
|
2402
|
-
"try {",
|
|
2403
|
-
' const raw = fs.readFileSync(hintsPath, "utf8");',
|
|
2404
|
-
" const parsed = JSON.parse(raw);",
|
|
2405
|
-
' if (typeof parsed.gdhDevRepoPath === "string" && parsed.gdhDevRepoPath.length > 0) {',
|
|
2406
|
-
" gdhDevRepoPath = parsed.gdhDevRepoPath;",
|
|
2407
|
-
" }",
|
|
2408
|
-
"} catch {}",
|
|
2409
|
-
"",
|
|
2410
|
-
"const effectiveDevRepoPath = process.env.GDH_DEV_REPO?.trim() || gdhDevRepoPath;",
|
|
2411
|
-
"if (effectiveDevRepoPath) {",
|
|
2412
|
-
' const devCliEntryPath = path.join(effectiveDevRepoPath, "packages/cli/dist/cli.js");',
|
|
2413
|
-
" if (fs.existsSync(devCliEntryPath)) {",
|
|
2414
|
-
' const result = spawnSync(process.execPath, [devCliEntryPath, "mcp", "serve", "--target", targetPath], { stdio: "inherit" });',
|
|
2415
|
-
" process.exit(result.status ?? 1);",
|
|
2416
|
-
" }",
|
|
2417
|
-
"}",
|
|
2418
|
-
"",
|
|
2419
|
-
`const result = spawnSync("npx", ["-y", "@skillcap/gdh@${pinnedVersion}", "mcp", "serve", "--target", targetPath], { stdio: "inherit", cwd: targetPath });`,
|
|
2420
|
-
'if (result.error && result.error.code === "ENOENT") {',
|
|
2421
|
-
` 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.");`,
|
|
2422
|
-
" process.exit(1);",
|
|
2423
|
-
"}",
|
|
2424
|
-
"process.exit(result.status ?? 1);",
|
|
2425
|
-
"",
|
|
2426
|
-
].join("\n");
|
|
2427
|
-
}
|
|
2428
2362
|
function buildManagedMcpServerEntry(input) {
|
|
2429
2363
|
return {
|
|
2430
2364
|
type: "stdio",
|
|
2431
|
-
command: "
|
|
2432
|
-
args:
|
|
2433
|
-
targetPath: input.targetPath,
|
|
2434
|
-
integrationRootPath: input.integrationRootPath,
|
|
2435
|
-
launcherPath: input.launcherPathForConfig,
|
|
2436
|
-
useAbsoluteTargetPath: true,
|
|
2437
|
-
}),
|
|
2365
|
+
command: "npx",
|
|
2366
|
+
args: ["-y", `@skillcap/gdh@${input.pinnedVersion}`, "mcp", "serve"],
|
|
2438
2367
|
};
|
|
2439
2368
|
}
|
|
2440
2369
|
function getManagedMcpServerEntry(jsonObject) {
|
|
@@ -2475,6 +2404,64 @@ function readExistingMcpConfig(absolutePath) {
|
|
|
2475
2404
|
return {};
|
|
2476
2405
|
}
|
|
2477
2406
|
}
|
|
2407
|
+
// Renders the managed [mcp_servers.gdh] section for Codex project-local
|
|
2408
|
+
// config. The section body is line-based (no TOML parser) — preserves any
|
|
2409
|
+
// other content the user has in the file via regex replacement of the
|
|
2410
|
+
// bracketed header span. Exotic forms (inline-table `mcp_servers.gdh = {...}`
|
|
2411
|
+
// or dotted-key `mcp_servers.gdh.command = "..."`) are not detected; users
|
|
2412
|
+
// with those forms may end up with a duplicate and must migrate manually.
|
|
2413
|
+
export function renderManagedCodexProjectSection(pinnedVersion) {
|
|
2414
|
+
return [
|
|
2415
|
+
"[mcp_servers.gdh]",
|
|
2416
|
+
'command = "npx"',
|
|
2417
|
+
`args = ["-y", "@skillcap/gdh@${pinnedVersion}", "mcp", "serve"]`,
|
|
2418
|
+
].join("\n");
|
|
2419
|
+
}
|
|
2420
|
+
export function renderManagedCodexProjectConfig(existingContent, pinnedVersion) {
|
|
2421
|
+
const section = renderManagedCodexProjectSection(pinnedVersion);
|
|
2422
|
+
if (existingContent === null || existingContent.trim() === "") {
|
|
2423
|
+
return `${section}\n`;
|
|
2424
|
+
}
|
|
2425
|
+
const lines = existingContent.split("\n");
|
|
2426
|
+
const sectionStart = lines.findIndex((line) => /^\[mcp_servers\.gdh\]\s*$/.test(line));
|
|
2427
|
+
if (sectionStart === -1) {
|
|
2428
|
+
const trailingNewline = existingContent.endsWith("\n") ? "" : "\n";
|
|
2429
|
+
const separator = existingContent.length > 0 ? "\n" : "";
|
|
2430
|
+
return `${existingContent}${trailingNewline}${separator}${section}\n`;
|
|
2431
|
+
}
|
|
2432
|
+
let sectionEnd = lines.length;
|
|
2433
|
+
for (let i = sectionStart + 1; i < lines.length; i++) {
|
|
2434
|
+
if (/^\[/.test(lines[i] ?? "")) {
|
|
2435
|
+
sectionEnd = i;
|
|
2436
|
+
break;
|
|
2437
|
+
}
|
|
2438
|
+
}
|
|
2439
|
+
const before = lines.slice(0, sectionStart).join("\n");
|
|
2440
|
+
const afterLines = lines.slice(sectionEnd);
|
|
2441
|
+
const after = afterLines.join("\n");
|
|
2442
|
+
const beforeJoin = before.length > 0 ? `${before}\n` : "";
|
|
2443
|
+
const afterNonEmpty = after.replace(/^\s+/, "");
|
|
2444
|
+
const afterJoin = afterNonEmpty.length > 0 ? `\n\n${afterNonEmpty}` : "\n";
|
|
2445
|
+
return `${beforeJoin}${section}${afterJoin}`;
|
|
2446
|
+
}
|
|
2447
|
+
function extractManagedCodexSection(content) {
|
|
2448
|
+
const lines = content.split("\n");
|
|
2449
|
+
const start = lines.findIndex((line) => /^\[mcp_servers\.gdh\]\s*$/.test(line));
|
|
2450
|
+
if (start === -1) {
|
|
2451
|
+
return null;
|
|
2452
|
+
}
|
|
2453
|
+
let end = lines.length;
|
|
2454
|
+
for (let i = start + 1; i < lines.length; i++) {
|
|
2455
|
+
if (/^\[/.test(lines[i] ?? "")) {
|
|
2456
|
+
end = i;
|
|
2457
|
+
break;
|
|
2458
|
+
}
|
|
2459
|
+
}
|
|
2460
|
+
return lines
|
|
2461
|
+
.slice(start, end)
|
|
2462
|
+
.join("\n")
|
|
2463
|
+
.replace(/\s+$/, "");
|
|
2464
|
+
}
|
|
2478
2465
|
function normalizeJson(value) {
|
|
2479
2466
|
if (Array.isArray(value)) {
|
|
2480
2467
|
return value.map((entry) => normalizeJson(entry));
|
|
@@ -2614,14 +2601,19 @@ async function listCodexMcpServers() {
|
|
|
2614
2601
|
}
|
|
2615
2602
|
}
|
|
2616
2603
|
function isMatchingCodexRegistration(registration, input) {
|
|
2617
|
-
const
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2604
|
+
const resolvedTargetPath = path.resolve(input.targetPath);
|
|
2605
|
+
const resolvedIntegrationRootPath = path.resolve(input.integrationRootPath);
|
|
2606
|
+
const expectedArgs = [
|
|
2607
|
+
"-y",
|
|
2608
|
+
`@skillcap/gdh@${input.pinnedVersion}`,
|
|
2609
|
+
"mcp",
|
|
2610
|
+
"serve",
|
|
2611
|
+
];
|
|
2612
|
+
if (resolvedTargetPath !== resolvedIntegrationRootPath) {
|
|
2613
|
+
expectedArgs.push("--target", resolvedTargetPath);
|
|
2614
|
+
}
|
|
2623
2615
|
return (registration.transport?.type === "stdio" &&
|
|
2624
|
-
registration.transport.command === "
|
|
2616
|
+
registration.transport.command === "npx" &&
|
|
2625
2617
|
JSON.stringify(registration.transport.args ?? []) === JSON.stringify(expectedArgs));
|
|
2626
2618
|
}
|
|
2627
2619
|
async function inspectProjectConfigLifecycleSurface(targetPath) {
|
|
@@ -3054,60 +3046,6 @@ function inspectCursorRuleLifecycleSurface(targetPath, adapterStatus) {
|
|
|
3054
3046
|
},
|
|
3055
3047
|
});
|
|
3056
3048
|
}
|
|
3057
|
-
function inspectMcpLauncherLifecycleSurface(targetPath, mcpEnabled, adapterStatus) {
|
|
3058
|
-
if (!mcpEnabled) {
|
|
3059
|
-
return createLifecycleSurfaceStatus({
|
|
3060
|
-
surface: "mcp_launcher",
|
|
3061
|
-
management: "managed",
|
|
3062
|
-
state: "compatible",
|
|
3063
|
-
summary: "Project-scoped MCP launcher is not required while MCP is disabled.",
|
|
3064
|
-
reasons: [],
|
|
3065
|
-
probes: [],
|
|
3066
|
-
action: null,
|
|
3067
|
-
});
|
|
3068
|
-
}
|
|
3069
|
-
const launcherSurface = findAdapterSurface(adapterStatus, "claude", "launcher_file") ??
|
|
3070
|
-
findAdapterSurface(adapterStatus, "cursor", "launcher_file") ??
|
|
3071
|
-
findAdapterSurface(adapterStatus, "codex", "launcher_file");
|
|
3072
|
-
const probes = launcherSurface === null
|
|
3073
|
-
? []
|
|
3074
|
-
: [
|
|
3075
|
-
createVersionProbe({
|
|
3076
|
-
targetPath,
|
|
3077
|
-
relativePath: launcherSurface.relativePath ?? MCP_LAUNCHER_RELATIVE_PATH,
|
|
3078
|
-
present: launcherSurface.present,
|
|
3079
|
-
expectedVersion: GDH_MCP_LAUNCHER_VERSION,
|
|
3080
|
-
detectedVersion: launcherSurface.present ? launcherSurface.version : null,
|
|
3081
|
-
}),
|
|
3082
|
-
];
|
|
3083
|
-
if (launcherSurface?.state === "ready") {
|
|
3084
|
-
return createLifecycleSurfaceStatus({
|
|
3085
|
-
surface: "mcp_launcher",
|
|
3086
|
-
management: "managed",
|
|
3087
|
-
state: "compatible",
|
|
3088
|
-
summary: "Managed MCP launcher file matches the current GDH version.",
|
|
3089
|
-
reasons: [],
|
|
3090
|
-
probes,
|
|
3091
|
-
action: null,
|
|
3092
|
-
});
|
|
3093
|
-
}
|
|
3094
|
-
return createLifecycleSurfaceStatus({
|
|
3095
|
-
surface: "mcp_launcher",
|
|
3096
|
-
management: "managed",
|
|
3097
|
-
state: launcherSurface?.present ? "migration_available" : "migration_needed",
|
|
3098
|
-
summary: "Managed MCP launcher file needs to be created or refreshed through the adapter install flow.",
|
|
3099
|
-
reasons: launcherSurface?.present
|
|
3100
|
-
? ["mcp_launcher_misconfigured"]
|
|
3101
|
-
: ["mcp_launcher_missing"],
|
|
3102
|
-
probes,
|
|
3103
|
-
action: {
|
|
3104
|
-
kind: "run_repair",
|
|
3105
|
-
summary: "Run GDH migrate or adapters install to refresh the managed MCP launcher file.",
|
|
3106
|
-
command: ["gdh", "adapters", "install", targetPath],
|
|
3107
|
-
autoApplicable: true,
|
|
3108
|
-
},
|
|
3109
|
-
});
|
|
3110
|
-
}
|
|
3111
3049
|
function inspectRuntimeBridgeLifecycleSurface(targetPath, bridgeStatus) {
|
|
3112
3050
|
const probes = bridgeStatus.managedArtifacts.map((artifact) => createVersionProbe({
|
|
3113
3051
|
targetPath,
|
|
@@ -3366,14 +3304,35 @@ async function inspectYamlSurfaceDirectory(input) {
|
|
|
3366
3304
|
}
|
|
3367
3305
|
function summarizeProjectLifecycleCompatibility(targetPath, surfaces) {
|
|
3368
3306
|
const state = resolveProjectLifecycleState(surfaces);
|
|
3369
|
-
const
|
|
3370
|
-
|
|
3307
|
+
const blocking = [];
|
|
3308
|
+
const coupled = [];
|
|
3309
|
+
for (const surface of surfaces) {
|
|
3310
|
+
const bucket = surface.state === "migration_blocked" || surface.state === "compatibility_degraded"
|
|
3311
|
+
? blocking
|
|
3312
|
+
: surface.state === "migration_needed" || surface.state === "migration_available"
|
|
3313
|
+
? coupled
|
|
3314
|
+
: null;
|
|
3315
|
+
if (bucket) {
|
|
3316
|
+
for (const reason of surface.reasons) {
|
|
3317
|
+
if (reason.length > 0)
|
|
3318
|
+
bucket.push(reason);
|
|
3319
|
+
}
|
|
3320
|
+
}
|
|
3321
|
+
}
|
|
3322
|
+
const blockingReasons = dedupe(blocking);
|
|
3323
|
+
const coupledReasons = dedupe(coupled.filter((r) => !blockingReasons.includes(r)));
|
|
3324
|
+
const reasons = [...blockingReasons, ...coupledReasons];
|
|
3325
|
+
const result = {
|
|
3371
3326
|
targetPath,
|
|
3372
3327
|
state,
|
|
3373
3328
|
summary: summarizeProjectLifecycleState(state, surfaces),
|
|
3374
3329
|
reasons,
|
|
3330
|
+
blockingReasons,
|
|
3331
|
+
coupledReasons,
|
|
3375
3332
|
surfaces,
|
|
3376
3333
|
};
|
|
3334
|
+
assertProjectLifecycleCompatibilityInvariant(result);
|
|
3335
|
+
return result;
|
|
3377
3336
|
}
|
|
3378
3337
|
function resolveProjectLifecycleState(surfaces) {
|
|
3379
3338
|
if (surfaces.some((surface) => surface.state === "migration_blocked")) {
|
|
@@ -3501,4 +3460,9 @@ function normalizeChangedFiles(files) {
|
|
|
3501
3460
|
export { bumpAndRebakePin, } from "./self-update-mechanics.js";
|
|
3502
3461
|
export { CLAUDE_CHECK_UPDATE_HOOK_RELATIVE_PATH } from "./claude-update-hook-render.js";
|
|
3503
3462
|
export { CLAUDE_STATUSLINE_RELATIVE_PATH } from "./claude-statusline-render.js";
|
|
3463
|
+
// Internal aggregator exposed for Wave 0 unit tests
|
|
3464
|
+
// (packages/adapters/src/lifecycle-compatibility.test.ts). Callers outside this
|
|
3465
|
+
// package should continue to use inspectProjectLifecycleCompatibility, which
|
|
3466
|
+
// wraps this function with filesystem probing and surface discovery.
|
|
3467
|
+
export { summarizeProjectLifecycleCompatibility };
|
|
3504
3468
|
//# sourceMappingURL=index.js.map
|