@skillcap/gdh 0.6.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 +23 -5
- package/node_modules/@gdh/adapters/dist/index.d.ts +3 -2
- package/node_modules/@gdh/adapters/dist/index.d.ts.map +1 -1
- package/node_modules/@gdh/adapters/dist/index.js +158 -242
- package/node_modules/@gdh/adapters/dist/index.js.map +1 -1
- package/node_modules/@gdh/adapters/package.json +8 -8
- package/node_modules/@gdh/authoring/package.json +2 -2
- package/node_modules/@gdh/cli/dist/index.d.ts.map +1 -1
- package/node_modules/@gdh/cli/dist/index.js +52 -13
- 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/package.json +10 -10
- package/node_modules/@gdh/core/dist/index.d.ts +2 -3
- package/node_modules/@gdh/core/dist/index.d.ts.map +1 -1
- package/node_modules/@gdh/core/dist/index.js +0 -5
- package/node_modules/@gdh/core/dist/index.js.map +1 -1
- package/node_modules/@gdh/core/package.json +1 -1
- package/node_modules/@gdh/docs/package.json +2 -2
- package/node_modules/@gdh/mcp/package.json +8 -8
- package/node_modules/@gdh/observability/package.json +2 -2
- package/node_modules/@gdh/runtime/package.json +2 -2
- package/node_modules/@gdh/scan/package.json +3 -3
- package/node_modules/@gdh/verify/package.json +7 -7
- package/package.json +11 -11
|
@@ -9,7 +9,7 @@ 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,
|
|
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";
|
|
13
13
|
import { createDefaultGuidanceUnits, getGuidanceStatus, resolveGuidanceQuery, } from "@gdh/docs";
|
|
14
14
|
import { inspectGuidanceAudit } from "@gdh/observability";
|
|
15
15
|
import { inspectRuntimeBridgeSurface } from "@gdh/runtime";
|
|
@@ -65,7 +65,7 @@ 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
70
|
const execFile = promisify(execFileCallback);
|
|
71
71
|
export async function getSupportedAgentAdaptersStatus(targetPath, options = {}) {
|
|
@@ -216,7 +216,6 @@ export async function inspectProjectLifecycleCompatibility(targetPath) {
|
|
|
216
216
|
await inspectGuidanceUnitLifecycleSurface(resolvedTargetPath, projectConfig),
|
|
217
217
|
inspectMcpManifestLifecycleSurface(resolvedTargetPath, projectConfig.mcp.enabled, adapterStatus),
|
|
218
218
|
inspectCursorRuleLifecycleSurface(resolvedTargetPath, adapterStatus),
|
|
219
|
-
inspectMcpLauncherLifecycleSurface(resolvedTargetPath, projectConfig.mcp.enabled, adapterStatus),
|
|
220
219
|
inspectRuntimeBridgeLifecycleSurface(resolvedTargetPath, bridgeStatus),
|
|
221
220
|
]);
|
|
222
221
|
}
|
|
@@ -1257,34 +1256,26 @@ export function renderCursorVerifySkill(pinnedVersion) {
|
|
|
1257
1256
|
async function inspectProjectMcpSupport(targetPath, options) {
|
|
1258
1257
|
const projectConfig = await readProjectConfig(targetPath);
|
|
1259
1258
|
const enabled = resolveProjectMcpEnabled(projectConfig);
|
|
1260
|
-
const launcherContent = options.pinnedVersion === null
|
|
1261
|
-
? null
|
|
1262
|
-
: renderManagedMcpLauncher(options.pinnedVersion);
|
|
1263
1259
|
const managedMcpEntry = buildManagedMcpServerEntry({
|
|
1264
|
-
|
|
1265
|
-
integrationRootPath: options.integrationRootPath,
|
|
1266
|
-
launcherPathForConfig: path.resolve(options.integrationRootPath, MCP_LAUNCHER_RELATIVE_PATH),
|
|
1260
|
+
pinnedVersion: options.pinnedVersion ?? "latest",
|
|
1267
1261
|
});
|
|
1268
|
-
const [projectFile, cursorFile,
|
|
1262
|
+
const [projectFile, cursorFile, codexProjectContent, localPathHints] = await Promise.all([
|
|
1269
1263
|
inspectJsonFile(path.join(options.integrationRootPath, PROJECT_MCP_RELATIVE_PATH)),
|
|
1270
1264
|
inspectJsonFile(path.join(options.integrationRootPath, CURSOR_MCP_RELATIVE_PATH)),
|
|
1271
|
-
fs.readFile(path.join(options.integrationRootPath,
|
|
1265
|
+
fs.readFile(path.join(options.integrationRootPath, CODEX_PROJECT_CONFIG_RELATIVE_PATH), "utf8").catch(() => null),
|
|
1272
1266
|
readLocalPathHints(options.integrationRootPath),
|
|
1273
1267
|
]);
|
|
1274
|
-
const launcherBootstrap = await inspectLauncherBootstrap(localPathHints);
|
|
1275
1268
|
const codexConfigPath = path.join(os.homedir(), ".codex/config.toml");
|
|
1276
1269
|
const codexServerName = projectConfig === null ? null : createCodexServerName(projectConfig.projectKeySeed);
|
|
1277
1270
|
return {
|
|
1278
1271
|
enabled,
|
|
1279
1272
|
integrationRootPath: options.integrationRootPath,
|
|
1280
|
-
launcherContent,
|
|
1281
1273
|
localPathHints,
|
|
1282
|
-
launcherBootstrap,
|
|
1283
|
-
launcherFile: inspectLauncherFile(launcherSource, launcherContent, enabled, launcherBootstrap),
|
|
1284
1274
|
projectFile: inspectManagedMcpFile(projectFile, enabled, PROJECT_MCP_RELATIVE_PATH, "Claude project MCP config", managedMcpEntry),
|
|
1285
1275
|
cursorFile: inspectManagedMcpFile(cursorFile, enabled, CURSOR_MCP_RELATIVE_PATH, "Cursor project MCP config", managedMcpEntry),
|
|
1276
|
+
codexProjectFile: inspectManagedCodexProjectFile(codexProjectContent, enabled, options.pinnedVersion),
|
|
1286
1277
|
codexRegistration: options.includeUserLocal
|
|
1287
|
-
? await inspectCodexRegistration(targetPath, enabled, codexServerName, codexConfigPath, options.integrationRootPath)
|
|
1278
|
+
? await inspectCodexRegistration(targetPath, enabled, codexServerName, codexConfigPath, options.integrationRootPath, options.pinnedVersion)
|
|
1288
1279
|
: {
|
|
1289
1280
|
present: false,
|
|
1290
1281
|
state: "ready",
|
|
@@ -1361,71 +1352,52 @@ function inspectManagedMcpFile(file, enabled, relativePath, displayName, expecte
|
|
|
1361
1352
|
summary: `${displayName} includes the managed \`${GDH_MCP_SERVER_NAME}\` MCP server entry.`,
|
|
1362
1353
|
};
|
|
1363
1354
|
}
|
|
1364
|
-
function
|
|
1355
|
+
function inspectManagedCodexProjectFile(content, enabled, pinnedVersion) {
|
|
1356
|
+
const relativePath = CODEX_PROJECT_CONFIG_RELATIVE_PATH;
|
|
1365
1357
|
if (!enabled) {
|
|
1366
1358
|
return {
|
|
1367
|
-
present:
|
|
1359
|
+
present: content !== null,
|
|
1368
1360
|
state: "ready",
|
|
1369
|
-
summary: "
|
|
1361
|
+
summary: "Codex project MCP config is disabled for this target.",
|
|
1370
1362
|
};
|
|
1371
1363
|
}
|
|
1372
|
-
if (
|
|
1364
|
+
if (pinnedVersion === null) {
|
|
1373
1365
|
return {
|
|
1374
|
-
present:
|
|
1366
|
+
present: content !== null,
|
|
1375
1367
|
state: "missing",
|
|
1376
|
-
summary:
|
|
1368
|
+
summary: `Codex project MCP config cannot be validated yet: no \`gdh_version\` is pinned (run \`gdh setup\` or \`gdh migrate --apply\`).`,
|
|
1377
1369
|
};
|
|
1378
1370
|
}
|
|
1379
|
-
if (
|
|
1371
|
+
if (content === null) {
|
|
1380
1372
|
return {
|
|
1381
1373
|
present: false,
|
|
1382
1374
|
state: "missing",
|
|
1383
|
-
summary: `
|
|
1375
|
+
summary: `Codex project MCP config is missing and should define the managed \`[mcp_servers.${GDH_MCP_SERVER_NAME}]\` entry at ${relativePath}.`,
|
|
1384
1376
|
};
|
|
1385
1377
|
}
|
|
1386
|
-
|
|
1378
|
+
const existingSection = extractManagedCodexSection(content);
|
|
1379
|
+
const expectedSection = renderManagedCodexProjectSection(pinnedVersion);
|
|
1380
|
+
if (existingSection === null) {
|
|
1387
1381
|
return {
|
|
1388
1382
|
present: true,
|
|
1389
1383
|
state: "misconfigured",
|
|
1390
|
-
summary:
|
|
1384
|
+
summary: `${relativePath} exists but does not contain the managed \`[mcp_servers.${GDH_MCP_SERVER_NAME}]\` section.`,
|
|
1391
1385
|
};
|
|
1392
1386
|
}
|
|
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
|
-
}
|
|
1387
|
+
if (existingSection !== expectedSection) {
|
|
1412
1388
|
return {
|
|
1389
|
+
present: true,
|
|
1413
1390
|
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.",
|
|
1391
|
+
summary: `${relativePath} managed \`[mcp_servers.${GDH_MCP_SERVER_NAME}]\` section does not match the expected GDH shape.`,
|
|
1421
1392
|
};
|
|
1422
1393
|
}
|
|
1423
1394
|
return {
|
|
1424
|
-
|
|
1425
|
-
|
|
1395
|
+
present: true,
|
|
1396
|
+
state: "ready",
|
|
1397
|
+
summary: `Codex project MCP config includes the managed \`[mcp_servers.${GDH_MCP_SERVER_NAME}]\` entry.`,
|
|
1426
1398
|
};
|
|
1427
1399
|
}
|
|
1428
|
-
async function inspectCodexRegistration(targetPath, enabled, codexServerName, codexConfigPath, integrationRootPath) {
|
|
1400
|
+
async function inspectCodexRegistration(targetPath, enabled, codexServerName, codexConfigPath, integrationRootPath, pinnedVersion) {
|
|
1429
1401
|
if (!enabled || codexServerName === null) {
|
|
1430
1402
|
return {
|
|
1431
1403
|
present: false,
|
|
@@ -1449,16 +1421,22 @@ async function inspectCodexRegistration(targetPath, enabled, codexServerName, co
|
|
|
1449
1421
|
summary: `Codex does not yet have the expected user-local MCP registration \`${codexServerName}\` in ${codexConfigPath}.`,
|
|
1450
1422
|
};
|
|
1451
1423
|
}
|
|
1452
|
-
|
|
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
|
+
}
|
|
1453
1431
|
if (!isMatchingCodexRegistration(registration, {
|
|
1454
1432
|
targetPath,
|
|
1455
1433
|
integrationRootPath,
|
|
1456
|
-
|
|
1434
|
+
pinnedVersion,
|
|
1457
1435
|
})) {
|
|
1458
1436
|
return {
|
|
1459
1437
|
present: true,
|
|
1460
1438
|
state: "misconfigured",
|
|
1461
|
-
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.`,
|
|
1462
1440
|
};
|
|
1463
1441
|
}
|
|
1464
1442
|
return {
|
|
@@ -1648,14 +1626,14 @@ async function inspectCodexAdapter(targetPath, guidance, projectMcp, pinnedVersi
|
|
|
1648
1626
|
];
|
|
1649
1627
|
if (projectMcp.enabled) {
|
|
1650
1628
|
surfaces.push(createSurfaceStatus({
|
|
1651
|
-
kind: "
|
|
1629
|
+
kind: "mcp_file",
|
|
1652
1630
|
scope: "repo",
|
|
1653
1631
|
targetPath: projectMcp.integrationRootPath,
|
|
1654
|
-
relativePath:
|
|
1655
|
-
present: projectMcp.
|
|
1656
|
-
state: projectMcp.
|
|
1657
|
-
summary: projectMcp.
|
|
1658
|
-
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,
|
|
1659
1637
|
}));
|
|
1660
1638
|
if (options.includeUserLocal) {
|
|
1661
1639
|
surfaces.push(createSurfaceStatus({
|
|
@@ -1763,15 +1741,6 @@ async function inspectClaudeAdapter(targetPath, guidance, projectMcp, pinnedVers
|
|
|
1763
1741
|
];
|
|
1764
1742
|
if (projectMcp.enabled) {
|
|
1765
1743
|
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
1744
|
kind: "mcp_file",
|
|
1776
1745
|
scope: "repo",
|
|
1777
1746
|
targetPath: projectMcp.integrationRootPath,
|
|
@@ -1863,15 +1832,6 @@ async function inspectCursorAdapter(targetPath, guidance, projectMcp, pinnedVers
|
|
|
1863
1832
|
];
|
|
1864
1833
|
if (projectMcp.enabled) {
|
|
1865
1834
|
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
1835
|
kind: "mcp_file",
|
|
1876
1836
|
scope: "repo",
|
|
1877
1837
|
targetPath: projectMcp.integrationRootPath,
|
|
@@ -1955,17 +1915,17 @@ async function planInstallActions(targetPath, adapters, requestedAgents, options
|
|
|
1955
1915
|
const effectiveDevRepoPath = options.devRepoPath ?? resolveCurrentGdhInstall(import.meta.url).defaultDevRepoPath;
|
|
1956
1916
|
if (options.user) {
|
|
1957
1917
|
if (requestedAgents.includes("codex")) {
|
|
1958
|
-
actions.push(...planCodexUserInstallActions(targetPath, projectMcp, options.integrationRootPath));
|
|
1918
|
+
actions.push(...planCodexUserInstallActions(targetPath, projectMcp, options.integrationRootPath, options.pinnedVersion));
|
|
1959
1919
|
}
|
|
1960
1920
|
return dedupeInstallActions(actions);
|
|
1961
1921
|
}
|
|
1962
|
-
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));
|
|
1963
1923
|
for (const adapter of adapters) {
|
|
1964
1924
|
if (!requestedAgents.includes(adapter.agent)) {
|
|
1965
1925
|
continue;
|
|
1966
1926
|
}
|
|
1967
1927
|
if (adapter.agent === "codex") {
|
|
1968
|
-
actions.push(...planCodexRepoInstallActions(targetPath, adapter, options.pinnedVersion));
|
|
1928
|
+
actions.push(...planCodexRepoInstallActions(targetPath, adapter, options.pinnedVersion, projectMcp, options.integrationRootPath));
|
|
1969
1929
|
continue;
|
|
1970
1930
|
}
|
|
1971
1931
|
if (adapter.agent === "claude") {
|
|
@@ -1978,27 +1938,11 @@ async function planInstallActions(targetPath, adapters, requestedAgents, options
|
|
|
1978
1938
|
}
|
|
1979
1939
|
return dedupeInstallActions(actions);
|
|
1980
1940
|
}
|
|
1981
|
-
function planSharedRepoInstallActions(targetPath, projectMcp, agent, effectiveDevRepoPath, integrationRootPath) {
|
|
1941
|
+
function planSharedRepoInstallActions(targetPath, projectMcp, agent, effectiveDevRepoPath, integrationRootPath, pinnedVersion) {
|
|
1982
1942
|
if (!projectMcp.enabled) {
|
|
1983
1943
|
return [];
|
|
1984
1944
|
}
|
|
1985
1945
|
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
1946
|
if (projectMcp.projectFile.state !== "ready") {
|
|
2003
1947
|
actions.push(createInstallAction({
|
|
2004
1948
|
agent,
|
|
@@ -2011,11 +1955,7 @@ function planSharedRepoInstallActions(targetPath, projectMcp, agent, effectiveDe
|
|
|
2011
1955
|
summary: projectMcp.projectFile.present
|
|
2012
1956
|
? "Replace the managed `gdh` MCP entry in .mcp.json while preserving any non-GDH servers."
|
|
2013
1957
|
: "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
|
-
})),
|
|
1958
|
+
content: renderManagedMcpConfig(path.join(integrationRootPath, PROJECT_MCP_RELATIVE_PATH), buildManagedMcpServerEntry({ pinnedVersion })),
|
|
2019
1959
|
}));
|
|
2020
1960
|
}
|
|
2021
1961
|
if (projectMcp.cursorFile.state !== "ready") {
|
|
@@ -2030,11 +1970,7 @@ function planSharedRepoInstallActions(targetPath, projectMcp, agent, effectiveDe
|
|
|
2030
1970
|
summary: projectMcp.cursorFile.present
|
|
2031
1971
|
? "Replace the managed `gdh` MCP entry in .cursor/mcp.json while preserving any non-GDH servers."
|
|
2032
1972
|
: "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
|
-
})),
|
|
1973
|
+
content: renderManagedMcpConfig(path.join(integrationRootPath, CURSOR_MCP_RELATIVE_PATH), buildManagedMcpServerEntry({ pinnedVersion })),
|
|
2038
1974
|
}));
|
|
2039
1975
|
}
|
|
2040
1976
|
if (projectMcp.localPathHints.gdhDevRepoPath !== effectiveDevRepoPath) {
|
|
@@ -2104,8 +2040,8 @@ function agentLabel(agent) {
|
|
|
2104
2040
|
return "Cursor";
|
|
2105
2041
|
}
|
|
2106
2042
|
}
|
|
2107
|
-
function planCodexRepoInstallActions(targetPath, adapter, pinnedVersion) {
|
|
2108
|
-
|
|
2043
|
+
function planCodexRepoInstallActions(targetPath, adapter, pinnedVersion, projectMcp, integrationRootPath) {
|
|
2044
|
+
const actions = [
|
|
2109
2045
|
planSkillInstallAction("codex", targetPath, adapter, CODEX_ONBOARD_SKILL_RELATIVE_PATH, renderCodexOnboardSkill, "gdh-onboard", pinnedVersion),
|
|
2110
2046
|
planSkillInstallAction("codex", targetPath, adapter, CODEX_STATUS_SKILL_RELATIVE_PATH, renderCodexStatusSkill, "gdh-status", pinnedVersion),
|
|
2111
2047
|
planSkillInstallAction("codex", targetPath, adapter, CODEX_MIGRATE_SKILL_RELATIVE_PATH, renderCodexMigrateSkill, "gdh-migrate", pinnedVersion),
|
|
@@ -2115,18 +2051,42 @@ function planCodexRepoInstallActions(targetPath, adapter, pinnedVersion) {
|
|
|
2115
2051
|
planSkillInstallAction("codex", targetPath, adapter, CODEX_VERIFY_SKILL_RELATIVE_PATH, renderCodexVerifySkill, "gdh-verify", pinnedVersion),
|
|
2116
2052
|
planSkillInstallAction("codex", targetPath, adapter, CODEX_SCAN_SKILL_RELATIVE_PATH, renderCodexScanSkill, "gdh-scan", pinnedVersion),
|
|
2117
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;
|
|
2118
2074
|
}
|
|
2119
|
-
function planCodexUserInstallActions(targetPath, projectMcp, integrationRootPath) {
|
|
2075
|
+
function planCodexUserInstallActions(targetPath, projectMcp, integrationRootPath, pinnedVersion) {
|
|
2120
2076
|
if (!projectMcp.enabled || projectMcp.codexServerName === null) {
|
|
2121
2077
|
return [];
|
|
2122
2078
|
}
|
|
2123
|
-
const
|
|
2124
|
-
const
|
|
2125
|
-
|
|
2126
|
-
|
|
2127
|
-
|
|
2128
|
-
|
|
2129
|
-
|
|
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
|
+
}
|
|
2130
2090
|
const actions = [];
|
|
2131
2091
|
if (projectMcp.codexRegistration.present) {
|
|
2132
2092
|
actions.push(createInstallAction({
|
|
@@ -2138,7 +2098,7 @@ function planCodexUserInstallActions(targetPath, projectMcp, integrationRootPath
|
|
|
2138
2098
|
absolutePath: projectMcp.codexConfigPath,
|
|
2139
2099
|
state: "planned",
|
|
2140
2100
|
mode: "replace",
|
|
2141
|
-
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.`,
|
|
2142
2102
|
command: ["codex", "mcp", "remove", projectMcp.codexServerName],
|
|
2143
2103
|
}));
|
|
2144
2104
|
}
|
|
@@ -2151,15 +2111,15 @@ function planCodexUserInstallActions(targetPath, projectMcp, integrationRootPath
|
|
|
2151
2111
|
absolutePath: projectMcp.codexConfigPath,
|
|
2152
2112
|
state: "planned",
|
|
2153
2113
|
mode: projectMcp.codexRegistration.present ? "replace" : "create",
|
|
2154
|
-
summary: `Register the managed GDH
|
|
2114
|
+
summary: `Register the managed GDH MCP server with Codex as \`${projectMcp.codexServerName}\` via pinned npx invocation.`,
|
|
2155
2115
|
command: [
|
|
2156
2116
|
"codex",
|
|
2157
2117
|
"mcp",
|
|
2158
2118
|
"add",
|
|
2159
2119
|
projectMcp.codexServerName,
|
|
2160
2120
|
"--",
|
|
2161
|
-
"
|
|
2162
|
-
...
|
|
2121
|
+
"npx",
|
|
2122
|
+
...codexCommandArgs,
|
|
2163
2123
|
],
|
|
2164
2124
|
}));
|
|
2165
2125
|
return actions;
|
|
@@ -2377,64 +2337,11 @@ function resolveProjectMcpEnabled(projectConfig) {
|
|
|
2377
2337
|
function createCodexServerName(projectKeySeed) {
|
|
2378
2338
|
return `gdh-${projectKeySeed}`;
|
|
2379
2339
|
}
|
|
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
2340
|
function buildManagedMcpServerEntry(input) {
|
|
2429
2341
|
return {
|
|
2430
2342
|
type: "stdio",
|
|
2431
|
-
command: "
|
|
2432
|
-
args:
|
|
2433
|
-
targetPath: input.targetPath,
|
|
2434
|
-
integrationRootPath: input.integrationRootPath,
|
|
2435
|
-
launcherPath: input.launcherPathForConfig,
|
|
2436
|
-
useAbsoluteTargetPath: true,
|
|
2437
|
-
}),
|
|
2343
|
+
command: "npx",
|
|
2344
|
+
args: ["-y", `@skillcap/gdh@${input.pinnedVersion}`, "mcp", "serve"],
|
|
2438
2345
|
};
|
|
2439
2346
|
}
|
|
2440
2347
|
function getManagedMcpServerEntry(jsonObject) {
|
|
@@ -2475,6 +2382,64 @@ function readExistingMcpConfig(absolutePath) {
|
|
|
2475
2382
|
return {};
|
|
2476
2383
|
}
|
|
2477
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
|
+
}
|
|
2478
2443
|
function normalizeJson(value) {
|
|
2479
2444
|
if (Array.isArray(value)) {
|
|
2480
2445
|
return value.map((entry) => normalizeJson(entry));
|
|
@@ -2614,14 +2579,19 @@ async function listCodexMcpServers() {
|
|
|
2614
2579
|
}
|
|
2615
2580
|
}
|
|
2616
2581
|
function isMatchingCodexRegistration(registration, input) {
|
|
2617
|
-
const
|
|
2618
|
-
|
|
2619
|
-
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
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
|
+
}
|
|
2623
2593
|
return (registration.transport?.type === "stdio" &&
|
|
2624
|
-
registration.transport.command === "
|
|
2594
|
+
registration.transport.command === "npx" &&
|
|
2625
2595
|
JSON.stringify(registration.transport.args ?? []) === JSON.stringify(expectedArgs));
|
|
2626
2596
|
}
|
|
2627
2597
|
async function inspectProjectConfigLifecycleSurface(targetPath) {
|
|
@@ -3054,60 +3024,6 @@ function inspectCursorRuleLifecycleSurface(targetPath, adapterStatus) {
|
|
|
3054
3024
|
},
|
|
3055
3025
|
});
|
|
3056
3026
|
}
|
|
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
3027
|
function inspectRuntimeBridgeLifecycleSurface(targetPath, bridgeStatus) {
|
|
3112
3028
|
const probes = bridgeStatus.managedArtifacts.map((artifact) => createVersionProbe({
|
|
3113
3029
|
targetPath,
|