@cubis/foundry 0.3.74 → 0.3.76

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.
Files changed (115) hide show
  1. package/README.md +98 -76
  2. package/dist/cli/commands/register.js +1 -1
  3. package/dist/cli/commands/register.js.map +1 -1
  4. package/dist/cli/core.js +348 -216
  5. package/dist/cli/core.js.map +1 -1
  6. package/dist/cli/init/execute.js +5 -7
  7. package/dist/cli/init/execute.js.map +1 -1
  8. package/dist/cli/workflows/commands.js +2 -2
  9. package/dist/cli/workflows/commands.js.map +1 -1
  10. package/package.json +4 -3
  11. package/src/cli/commands/register.ts +1 -1
  12. package/src/cli/core.ts +429 -267
  13. package/src/cli/init/execute.ts +5 -9
  14. package/src/cli/workflows/commands.ts +2 -2
  15. package/workflows/skills/_schema/skill-platform-attributes.json +7 -0
  16. package/workflows/skills/generated/skill-audit.json +11 -2
  17. package/workflows/skills/generated/skill-catalog.json +30 -4
  18. package/workflows/skills/skills_index.json +26 -0
  19. package/workflows/skills/stitch/SKILL.md +79 -0
  20. package/workflows/skills/stitch/evals/assertions.md +45 -0
  21. package/workflows/skills/stitch/evals/evals.json +68 -0
  22. package/workflows/skills/stitch/examples/01-new-screen.md +13 -0
  23. package/workflows/skills/stitch/examples/02-update-existing-screen.md +13 -0
  24. package/workflows/skills/stitch/examples/03-mobile-handoff.md +13 -0
  25. package/workflows/skills/stitch/examples/04-prompt-enhancement.md +21 -0
  26. package/workflows/skills/stitch/examples/05-design-sync-loop.md +16 -0
  27. package/workflows/skills/stitch/references/implementation-patterns.md +20 -0
  28. package/workflows/skills/stitch/references/platform-setup.md +46 -0
  29. package/workflows/skills/stitch/references/update-diff-workflow.md +23 -0
  30. package/workflows/workflows/agent-environment-setup/generated/route-manifest.json +15 -7
  31. package/workflows/workflows/agent-environment-setup/manifest.json +5 -0
  32. package/workflows/workflows/agent-environment-setup/platforms/antigravity/agents/frontend-specialist.md +10 -2
  33. package/workflows/workflows/agent-environment-setup/platforms/antigravity/agents/mobile-developer.md +6 -2
  34. package/workflows/workflows/agent-environment-setup/platforms/antigravity/rules/GEMINI.md +1 -0
  35. package/workflows/workflows/agent-environment-setup/platforms/antigravity/skills/stitch/SKILL.md +87 -0
  36. package/workflows/workflows/agent-environment-setup/platforms/antigravity/skills/stitch/evals/assertions.md +45 -0
  37. package/workflows/workflows/agent-environment-setup/platforms/antigravity/skills/stitch/evals/evals.json +68 -0
  38. package/workflows/workflows/agent-environment-setup/platforms/antigravity/skills/stitch/examples/01-new-screen.md +13 -0
  39. package/workflows/workflows/agent-environment-setup/platforms/antigravity/skills/stitch/examples/02-update-existing-screen.md +13 -0
  40. package/workflows/workflows/agent-environment-setup/platforms/antigravity/skills/stitch/examples/03-mobile-handoff.md +13 -0
  41. package/workflows/workflows/agent-environment-setup/platforms/antigravity/skills/stitch/examples/04-prompt-enhancement.md +21 -0
  42. package/workflows/workflows/agent-environment-setup/platforms/antigravity/skills/stitch/examples/05-design-sync-loop.md +16 -0
  43. package/workflows/workflows/agent-environment-setup/platforms/antigravity/skills/stitch/references/implementation-patterns.md +20 -0
  44. package/workflows/workflows/agent-environment-setup/platforms/antigravity/skills/stitch/references/platform-setup.md +46 -0
  45. package/workflows/workflows/agent-environment-setup/platforms/antigravity/skills/stitch/references/update-diff-workflow.md +23 -0
  46. package/workflows/workflows/agent-environment-setup/platforms/antigravity/workflows/create.md +3 -2
  47. package/workflows/workflows/agent-environment-setup/platforms/antigravity/workflows/mobile.md +4 -3
  48. package/workflows/workflows/agent-environment-setup/platforms/claude/agents/frontend-specialist.md +10 -2
  49. package/workflows/workflows/agent-environment-setup/platforms/claude/agents/mobile-developer.md +6 -2
  50. package/workflows/workflows/agent-environment-setup/platforms/claude/rules/CLAUDE.md +1 -0
  51. package/workflows/workflows/agent-environment-setup/platforms/claude/skills/skills_index.json +26 -0
  52. package/workflows/workflows/agent-environment-setup/platforms/claude/skills/stitch/SKILL.md +93 -0
  53. package/workflows/workflows/agent-environment-setup/platforms/claude/skills/stitch/evals/assertions.md +45 -0
  54. package/workflows/workflows/agent-environment-setup/platforms/claude/skills/stitch/evals/evals.json +68 -0
  55. package/workflows/workflows/agent-environment-setup/platforms/claude/skills/stitch/examples/01-new-screen.md +13 -0
  56. package/workflows/workflows/agent-environment-setup/platforms/claude/skills/stitch/examples/02-update-existing-screen.md +13 -0
  57. package/workflows/workflows/agent-environment-setup/platforms/claude/skills/stitch/examples/03-mobile-handoff.md +13 -0
  58. package/workflows/workflows/agent-environment-setup/platforms/claude/skills/stitch/examples/04-prompt-enhancement.md +21 -0
  59. package/workflows/workflows/agent-environment-setup/platforms/claude/skills/stitch/examples/05-design-sync-loop.md +16 -0
  60. package/workflows/workflows/agent-environment-setup/platforms/claude/skills/stitch/references/implementation-patterns.md +20 -0
  61. package/workflows/workflows/agent-environment-setup/platforms/claude/skills/stitch/references/platform-setup.md +46 -0
  62. package/workflows/workflows/agent-environment-setup/platforms/claude/skills/stitch/references/update-diff-workflow.md +23 -0
  63. package/workflows/workflows/agent-environment-setup/platforms/claude/workflows/create.md +3 -2
  64. package/workflows/workflows/agent-environment-setup/platforms/claude/workflows/mobile.md +4 -3
  65. package/workflows/workflows/agent-environment-setup/platforms/codex/agents/frontend-specialist.md +10 -2
  66. package/workflows/workflows/agent-environment-setup/platforms/codex/agents/mobile-developer.md +6 -2
  67. package/workflows/workflows/agent-environment-setup/platforms/codex/rules/AGENTS.md +1 -0
  68. package/workflows/workflows/agent-environment-setup/platforms/codex/skills/stitch/SKILL.md +87 -0
  69. package/workflows/workflows/agent-environment-setup/platforms/codex/skills/stitch/evals/assertions.md +45 -0
  70. package/workflows/workflows/agent-environment-setup/platforms/codex/skills/stitch/evals/evals.json +68 -0
  71. package/workflows/workflows/agent-environment-setup/platforms/codex/skills/stitch/examples/01-new-screen.md +13 -0
  72. package/workflows/workflows/agent-environment-setup/platforms/codex/skills/stitch/examples/02-update-existing-screen.md +13 -0
  73. package/workflows/workflows/agent-environment-setup/platforms/codex/skills/stitch/examples/03-mobile-handoff.md +13 -0
  74. package/workflows/workflows/agent-environment-setup/platforms/codex/skills/stitch/examples/04-prompt-enhancement.md +21 -0
  75. package/workflows/workflows/agent-environment-setup/platforms/codex/skills/stitch/examples/05-design-sync-loop.md +16 -0
  76. package/workflows/workflows/agent-environment-setup/platforms/codex/skills/stitch/references/implementation-patterns.md +20 -0
  77. package/workflows/workflows/agent-environment-setup/platforms/codex/skills/stitch/references/platform-setup.md +46 -0
  78. package/workflows/workflows/agent-environment-setup/platforms/codex/skills/stitch/references/update-diff-workflow.md +23 -0
  79. package/workflows/workflows/agent-environment-setup/platforms/codex/workflows/create.md +3 -2
  80. package/workflows/workflows/agent-environment-setup/platforms/codex/workflows/mobile.md +4 -3
  81. package/workflows/workflows/agent-environment-setup/platforms/copilot/agents/frontend-specialist.md +6 -2
  82. package/workflows/workflows/agent-environment-setup/platforms/copilot/agents/mobile-developer.md +6 -2
  83. package/workflows/workflows/agent-environment-setup/platforms/copilot/rules/copilot-instructions.md +1 -0
  84. package/workflows/workflows/agent-environment-setup/platforms/copilot/skills/skills_index.json +26 -0
  85. package/workflows/workflows/agent-environment-setup/platforms/copilot/skills/stitch/SKILL.md +92 -0
  86. package/workflows/workflows/agent-environment-setup/platforms/copilot/skills/stitch/evals/assertions.md +45 -0
  87. package/workflows/workflows/agent-environment-setup/platforms/copilot/skills/stitch/evals/evals.json +68 -0
  88. package/workflows/workflows/agent-environment-setup/platforms/copilot/skills/stitch/examples/01-new-screen.md +13 -0
  89. package/workflows/workflows/agent-environment-setup/platforms/copilot/skills/stitch/examples/02-update-existing-screen.md +13 -0
  90. package/workflows/workflows/agent-environment-setup/platforms/copilot/skills/stitch/examples/03-mobile-handoff.md +13 -0
  91. package/workflows/workflows/agent-environment-setup/platforms/copilot/skills/stitch/examples/04-prompt-enhancement.md +21 -0
  92. package/workflows/workflows/agent-environment-setup/platforms/copilot/skills/stitch/examples/05-design-sync-loop.md +16 -0
  93. package/workflows/workflows/agent-environment-setup/platforms/copilot/skills/stitch/references/implementation-patterns.md +20 -0
  94. package/workflows/workflows/agent-environment-setup/platforms/copilot/skills/stitch/references/platform-setup.md +46 -0
  95. package/workflows/workflows/agent-environment-setup/platforms/copilot/skills/stitch/references/update-diff-workflow.md +23 -0
  96. package/workflows/workflows/agent-environment-setup/platforms/copilot/workflows/create.md +3 -2
  97. package/workflows/workflows/agent-environment-setup/platforms/copilot/workflows/mobile.md +4 -3
  98. package/workflows/workflows/agent-environment-setup/platforms/gemini/rules/GEMINI.md +1 -0
  99. package/workflows/workflows/agent-environment-setup/platforms/gemini/skills/stitch/SKILL.md +87 -0
  100. package/workflows/workflows/agent-environment-setup/platforms/gemini/skills/stitch/evals/assertions.md +45 -0
  101. package/workflows/workflows/agent-environment-setup/platforms/gemini/skills/stitch/evals/evals.json +68 -0
  102. package/workflows/workflows/agent-environment-setup/platforms/gemini/skills/stitch/examples/01-new-screen.md +13 -0
  103. package/workflows/workflows/agent-environment-setup/platforms/gemini/skills/stitch/examples/02-update-existing-screen.md +13 -0
  104. package/workflows/workflows/agent-environment-setup/platforms/gemini/skills/stitch/examples/03-mobile-handoff.md +13 -0
  105. package/workflows/workflows/agent-environment-setup/platforms/gemini/skills/stitch/examples/04-prompt-enhancement.md +21 -0
  106. package/workflows/workflows/agent-environment-setup/platforms/gemini/skills/stitch/examples/05-design-sync-loop.md +16 -0
  107. package/workflows/workflows/agent-environment-setup/platforms/gemini/skills/stitch/references/implementation-patterns.md +20 -0
  108. package/workflows/workflows/agent-environment-setup/platforms/gemini/skills/stitch/references/platform-setup.md +46 -0
  109. package/workflows/workflows/agent-environment-setup/platforms/gemini/skills/stitch/references/update-diff-workflow.md +23 -0
  110. package/workflows/workflows/agent-environment-setup/platforms/gemini/workflows/create.md +3 -2
  111. package/workflows/workflows/agent-environment-setup/platforms/gemini/workflows/mobile.md +4 -3
  112. package/workflows/workflows/agent-environment-setup/shared/agents/frontend-specialist.md +10 -2
  113. package/workflows/workflows/agent-environment-setup/shared/agents/mobile-developer.md +6 -2
  114. package/workflows/workflows/agent-environment-setup/shared/workflows/create.md +3 -2
  115. package/workflows/workflows/agent-environment-setup/shared/workflows/mobile.md +4 -3
package/dist/cli/core.js CHANGED
@@ -3783,8 +3783,8 @@ async function persistManagedCredentialsEnv({ envVarNames, dryRun = false }) {
3783
3783
  function resolvePostmanMcpDefinitionPath({ platform, scope, cwd = process.cwd(), }) {
3784
3784
  return path.join(resolveMcpRootPath({ scope, cwd }), platform, `${POSTMAN_SKILL_ID}.json`);
3785
3785
  }
3786
- function resolveStitchMcpDefinitionPath({ scope, cwd = process.cwd() }) {
3787
- return path.join(resolveMcpRootPath({ scope, cwd }), "antigravity", "stitch.json");
3786
+ function resolveStitchMcpDefinitionPath({ platform, scope, cwd = process.cwd(), }) {
3787
+ return path.join(resolveMcpRootPath({ scope, cwd }), platform, "stitch.json");
3788
3788
  }
3789
3789
  function buildPostmanAuthHeader({ apiKeyEnvVar = POSTMAN_API_KEY_ENV_VAR, apiKey = null, }) {
3790
3790
  const normalizedApiKey = normalizePostmanApiKey(apiKey);
@@ -4347,13 +4347,16 @@ async function removeGeneratedArtifactIfExists({ targetPath, dryRun = false }) {
4347
4347
  path: targetPath,
4348
4348
  };
4349
4349
  }
4350
- async function applyPostmanMcpForPlatform({ platform, mcpScope, apiKeyEnvVar, mcpUrl, includePostmanMcp = true, stitchApiKeyEnvVar, stitchMcpUrl, includeStitchMcp = false, includeFoundryMcp = true, includePlaywrightMcp = false, foundryRuntime = "local", dryRun = false, cwd = process.cwd(), }) {
4350
+ async function applyPostmanMcpForPlatform({ platform, mcpScope, includePostmanMcp = false, includeStitchMcp = false, includeFoundryMcp = true, includePlaywrightMcp = false, foundryRuntime = "local", dryRun = false, cwd = process.cwd(), }) {
4351
4351
  const workspaceRoot = findWorkspaceRoot(cwd);
4352
4352
  const warnings = [];
4353
4353
  const foundryScope = mcpScope === "global" ? "global" : "project";
4354
4354
  const normalizedFoundryRuntime = normalizeMcpRuntime(foundryRuntime, "local");
4355
- const resolvedPostmanApiKey = normalizePostmanApiKey(process.env[apiKeyEnvVar || POSTMAN_API_KEY_ENV_VAR]);
4356
- const resolvedStitchApiKey = normalizePostmanApiKey(process.env[stitchApiKeyEnvVar || STITCH_API_KEY_ENV_VAR]);
4355
+ const cleanupLegacyServers = (servers) => {
4356
+ delete servers[POSTMAN_SKILL_ID];
4357
+ delete servers[STITCH_MCP_SERVER_ID];
4358
+ return servers;
4359
+ };
4357
4360
  let foundryDockerPort = DEFAULT_MCP_DOCKER_HOST_PORT;
4358
4361
  if (includeFoundryMcp && normalizedFoundryRuntime === "docker") {
4359
4362
  const runningPort = await resolveDockerContainerHostPort({
@@ -4381,13 +4384,7 @@ async function applyPostmanMcpForPlatform({ platform, mcpScope, apiKeyEnvVar, mc
4381
4384
  !Array.isArray(next.mcpServers)
4382
4385
  ? { ...next.mcpServers }
4383
4386
  : {};
4384
- if (includePostmanMcp) {
4385
- mcpServers[POSTMAN_SKILL_ID] = buildGeminiPostmanServer({
4386
- apiKeyEnvVar,
4387
- apiKey: resolvedPostmanApiKey,
4388
- mcpUrl,
4389
- });
4390
- }
4387
+ cleanupLegacyServers(mcpServers);
4391
4388
  if (includeFoundryMcp) {
4392
4389
  mcpServers[FOUNDRY_MCP_SERVER_ID] = buildGeminiFoundryServer({
4393
4390
  scope: foundryScope,
@@ -4398,13 +4395,6 @@ async function applyPostmanMcpForPlatform({ platform, mcpScope, apiKeyEnvVar, mc
4398
4395
  else {
4399
4396
  delete mcpServers[FOUNDRY_MCP_SERVER_ID];
4400
4397
  }
4401
- if (includeStitchMcp) {
4402
- mcpServers[STITCH_MCP_SERVER_ID] = buildGeminiStitchServer({
4403
- apiKeyEnvVar: stitchApiKeyEnvVar,
4404
- apiKey: resolvedStitchApiKey,
4405
- mcpUrl: stitchMcpUrl,
4406
- });
4407
- }
4408
4398
  if (includePlaywrightMcp) {
4409
4399
  mcpServers[PLAYWRIGHT_MCP_SERVER_ID] = buildGeminiPlaywrightServer();
4410
4400
  }
@@ -4435,13 +4425,7 @@ async function applyPostmanMcpForPlatform({ platform, mcpScope, apiKeyEnvVar, mc
4435
4425
  !Array.isArray(next.mcpServers)
4436
4426
  ? { ...next.mcpServers }
4437
4427
  : {};
4438
- if (includePostmanMcp) {
4439
- mcpServers[POSTMAN_SKILL_ID] = buildCopilotCliPostmanServer({
4440
- apiKeyEnvVar,
4441
- apiKey: resolvedPostmanApiKey,
4442
- mcpUrl,
4443
- });
4444
- }
4428
+ cleanupLegacyServers(mcpServers);
4445
4429
  if (includeFoundryMcp) {
4446
4430
  mcpServers[FOUNDRY_MCP_SERVER_ID] = buildCopilotCliFoundryServer({
4447
4431
  scope: foundryScope,
@@ -4464,13 +4448,7 @@ async function applyPostmanMcpForPlatform({ platform, mcpScope, apiKeyEnvVar, mc
4464
4448
  !Array.isArray(next.servers)
4465
4449
  ? { ...next.servers }
4466
4450
  : {};
4467
- if (includePostmanMcp) {
4468
- servers[POSTMAN_SKILL_ID] = buildVsCodePostmanServer({
4469
- apiKeyEnvVar,
4470
- apiKey: resolvedPostmanApiKey,
4471
- mcpUrl,
4472
- });
4473
- }
4451
+ cleanupLegacyServers(servers);
4474
4452
  if (includeFoundryMcp) {
4475
4453
  servers[FOUNDRY_MCP_SERVER_ID] = buildVsCodeFoundryServer({
4476
4454
  scope: foundryScope,
@@ -4517,13 +4495,7 @@ async function applyPostmanMcpForPlatform({ platform, mcpScope, apiKeyEnvVar, mc
4517
4495
  !Array.isArray(next.servers)
4518
4496
  ? { ...next.servers }
4519
4497
  : {};
4520
- if (includePostmanMcp) {
4521
- servers[POSTMAN_SKILL_ID] = buildVsCodePostmanServer({
4522
- apiKeyEnvVar,
4523
- apiKey: resolvedPostmanApiKey,
4524
- mcpUrl,
4525
- });
4526
- }
4498
+ cleanupLegacyServers(servers);
4527
4499
  if (includeFoundryMcp) {
4528
4500
  servers[FOUNDRY_MCP_SERVER_ID] = buildVsCodeFoundryServer({
4529
4501
  scope: foundryScope,
@@ -4566,6 +4538,12 @@ async function applyPostmanMcpForPlatform({ platform, mcpScope, apiKeyEnvVar, mc
4566
4538
  catch {
4567
4539
  // Best effort. Add will still run and becomes source of truth.
4568
4540
  }
4541
+ try {
4542
+ await execFile("codex", ["mcp", "remove", STITCH_MCP_SERVER_ID], { cwd });
4543
+ }
4544
+ catch {
4545
+ // Best effort. Add will still run and becomes source of truth.
4546
+ }
4569
4547
  try {
4570
4548
  await execFile("codex", ["mcp", "remove", FOUNDRY_MCP_SERVER_ID], {
4571
4549
  cwd,
@@ -4574,41 +4552,13 @@ async function applyPostmanMcpForPlatform({ platform, mcpScope, apiKeyEnvVar, mc
4574
4552
  catch {
4575
4553
  // Best effort. Add will still run and becomes source of truth.
4576
4554
  }
4577
- if (includePostmanMcp) {
4578
- try {
4579
- await execFile("codex", [
4580
- "mcp",
4581
- "add",
4582
- POSTMAN_SKILL_ID,
4583
- "--url",
4584
- mcpUrl,
4585
- "--bearer-token-env-var",
4586
- apiKeyEnvVar || POSTMAN_API_KEY_ENV_VAR,
4587
- ], { cwd });
4588
- const postmanToken = normalizePostmanApiKey(process.env[apiKeyEnvVar || POSTMAN_API_KEY_ENV_VAR]);
4589
- const postmanPatch = await patchCodexPostmanHttpHeaders({
4590
- configPath: codexConfigPath,
4591
- mcpUrl,
4592
- bearerToken: postmanToken,
4593
- dryRun: false,
4594
- });
4595
- if (postmanPatch.action === "patched") {
4596
- warnings.push("Codex Postman MCP config patched to static Authorization header for startup reliability.");
4597
- }
4598
- if (postmanPatch.warnings?.length) {
4599
- warnings.push(...postmanPatch.warnings);
4600
- }
4601
- }
4602
- catch (error) {
4603
- warnings.push(`Failed to register Postman MCP via Codex CLI. Ensure 'codex' is installed and rerun. (${error.message})`);
4604
- return {
4605
- kind: "codex-cli",
4606
- scope: mcpScope,
4607
- path: codexConfigPath,
4608
- action: "failed",
4609
- warnings,
4610
- };
4611
- }
4555
+ try {
4556
+ await execFile("codex", ["mcp", "remove", PLAYWRIGHT_MCP_SERVER_ID], {
4557
+ cwd,
4558
+ });
4559
+ }
4560
+ catch {
4561
+ // Best effort. Add will still run and becomes source of truth.
4612
4562
  }
4613
4563
  if (includeFoundryMcp) {
4614
4564
  try {
@@ -4636,6 +4586,14 @@ async function applyPostmanMcpForPlatform({ platform, mcpScope, apiKeyEnvVar, mc
4636
4586
  warnings.push(`Failed to register ${FOUNDRY_MCP_SERVER_ID} MCP via Codex CLI. Ensure 'cbx' and 'codex' are installed and rerun. (${error.message})`);
4637
4587
  }
4638
4588
  }
4589
+ if (includePlaywrightMcp) {
4590
+ try {
4591
+ await execFile("codex", ["mcp", "add", PLAYWRIGHT_MCP_SERVER_ID, "--url", PLAYWRIGHT_MCP_URL], { cwd });
4592
+ }
4593
+ catch (error) {
4594
+ warnings.push(`Failed to register ${PLAYWRIGHT_MCP_SERVER_ID} MCP via Codex CLI. Ensure 'codex' is installed and rerun. (${error.message})`);
4595
+ }
4596
+ }
4639
4597
  return {
4640
4598
  kind: "codex-cli",
4641
4599
  scope: mcpScope,
@@ -4657,6 +4615,7 @@ async function applyPostmanMcpForPlatform({ platform, mcpScope, apiKeyEnvVar, mc
4657
4615
  !Array.isArray(next.mcpServers)
4658
4616
  ? { ...next.mcpServers }
4659
4617
  : {};
4618
+ cleanupLegacyServers(mcpServers);
4660
4619
  if (includeFoundryMcp) {
4661
4620
  if (normalizedFoundryRuntime === "docker") {
4662
4621
  mcpServers[FOUNDRY_MCP_SERVER_ID] = {
@@ -4700,7 +4659,7 @@ async function applyPostmanMcpForPlatform({ platform, mcpScope, apiKeyEnvVar, mc
4700
4659
  path: null,
4701
4660
  action: "skipped",
4702
4661
  warnings: [
4703
- `Unsupported platform '${platform}' for Postman MCP installation.`,
4662
+ `Unsupported platform '${platform}' for Foundry MCP installation.`,
4704
4663
  ],
4705
4664
  };
4706
4665
  }
@@ -4735,11 +4694,24 @@ async function resolvePostmanInstallSelection({ platform, scope, options, cwd =
4735
4694
  throw new Error("Inline API keys are no longer allowed. Use environment variables and --env-var profiles (for example POSTMAN_API_KEY_* and STITCH_API_KEY_*).");
4736
4695
  }
4737
4696
  const stitchRequested = Boolean(options.stitch);
4697
+ const playwrightRequested = Boolean(options.playwright);
4738
4698
  const postmanRequested = Boolean(options.postman) ||
4739
4699
  hasWorkspaceOption ||
4740
4700
  options.postmanMode !== undefined;
4741
- const foundryOnlyRequested = options.foundryMcp === true && !postmanRequested && !stitchRequested;
4742
- const enabled = postmanRequested || stitchRequested || foundryOnlyRequested;
4701
+ const stitchEnabled = stitchRequested;
4702
+ const gatewayRequested = postmanRequested || stitchEnabled;
4703
+ const foundryMcpRequested = options.foundryMcp === true;
4704
+ const foundryMcpEnabled = options.foundryMcp === false && !gatewayRequested
4705
+ ? false
4706
+ : foundryMcpRequested || gatewayRequested;
4707
+ const foundryOnlyRequested = foundryMcpRequested &&
4708
+ !postmanRequested &&
4709
+ !stitchRequested &&
4710
+ !playwrightRequested;
4711
+ const enabled = postmanRequested ||
4712
+ stitchRequested ||
4713
+ playwrightRequested ||
4714
+ foundryOnlyRequested;
4743
4715
  if (!enabled)
4744
4716
  return { enabled: false };
4745
4717
  const requestedPostmanMode = postmanRequested
@@ -4758,20 +4730,19 @@ async function resolvePostmanInstallSelection({ platform, scope, options, cwd =
4758
4730
  : null;
4759
4731
  let mcpScope = requestedMcpScope?.scope || "project";
4760
4732
  const warnings = [];
4733
+ if (options.foundryMcp === false && gatewayRequested) {
4734
+ warnings.push("Ignoring --no-foundry-mcp because Postman/Stitch now route through the Cubis Foundry MCP gateway.");
4735
+ }
4761
4736
  if (requestedMcpScope?.warning) {
4762
4737
  warnings.push(requestedMcpScope.warning);
4763
4738
  }
4764
- const stitchEnabled = stitchRequested ||
4765
- (platform === "antigravity" &&
4766
- options.stitchDefaultForAntigravity !== false);
4767
4739
  const envStitchApiKey = normalizePostmanApiKey(process.env[STITCH_API_KEY_ENV_VAR]);
4768
- const requestedRuntime = normalizeMcpRuntime(options.mcpRuntime, DEFAULT_MCP_RUNTIME);
4740
+ const requestedRuntime = normalizeMcpRuntime(options.mcpRuntime, foundryMcpEnabled ? DEFAULT_MCP_RUNTIME : "local");
4769
4741
  const requestedFallback = normalizeMcpFallback(options.mcpFallback, DEFAULT_MCP_FALLBACK);
4770
4742
  const requestedImage = normalizePostmanApiKey(options.mcpImage) || DEFAULT_MCP_DOCKER_IMAGE;
4771
4743
  const requestedUpdatePolicy = normalizeMcpUpdatePolicy(options.mcpUpdatePolicy, DEFAULT_MCP_UPDATE_POLICY);
4772
4744
  const mcpBuildLocal = Boolean(options.mcpBuildLocal);
4773
- const mcpToolSync = options.mcpToolSync !== false;
4774
- const foundryMcpEnabled = options.foundryMcp !== false;
4745
+ const mcpToolSync = options.mcpToolSync !== false && (postmanRequested || stitchEnabled);
4775
4746
  const canPrompt = !options.yes && !options.dryRun && !process.env.CI && process.stdin.isTTY;
4776
4747
  if (postmanRequested && canPrompt && !hasWorkspaceOption) {
4777
4748
  const workspaceSelection = await promptPostmanWorkspaceSelection({
@@ -4782,10 +4753,10 @@ async function resolvePostmanInstallSelection({ platform, scope, options, cwd =
4782
4753
  warnings.push(...workspaceSelection.warnings);
4783
4754
  workspaceSelectionSource = "interactive";
4784
4755
  }
4785
- let effectiveRuntime = requestedRuntime;
4786
- let runtimeSkipped = false;
4787
- let dockerImageAction = "not-requested";
4788
- if (requestedRuntime === "docker") {
4756
+ let effectiveRuntime = foundryMcpEnabled ? requestedRuntime : null;
4757
+ let runtimeSkipped = !foundryMcpEnabled;
4758
+ let dockerImageAction = foundryMcpEnabled ? "not-requested" : "not-needed";
4759
+ if (foundryMcpEnabled && requestedRuntime === "docker") {
4789
4760
  const dockerAvailable = await checkDockerAvailable({ cwd });
4790
4761
  if (!dockerAvailable) {
4791
4762
  if (requestedFallback === "fail") {
@@ -4852,10 +4823,10 @@ async function resolvePostmanInstallSelection({ platform, scope, options, cwd =
4852
4823
  generatedAt: new Date().toISOString(),
4853
4824
  mcp: {
4854
4825
  scope: mcpScope,
4855
- server: postmanRequested
4856
- ? POSTMAN_SKILL_ID
4857
- : stitchEnabled
4858
- ? STITCH_MCP_SERVER_ID
4826
+ server: foundryMcpEnabled || gatewayRequested
4827
+ ? FOUNDRY_MCP_SERVER_ID
4828
+ : playwrightRequested
4829
+ ? PLAYWRIGHT_MCP_SERVER_ID
4859
4830
  : FOUNDRY_MCP_SERVER_ID,
4860
4831
  platform,
4861
4832
  runtime: requestedRuntime,
@@ -4896,11 +4867,19 @@ async function resolvePostmanInstallSelection({ platform, scope, options, cwd =
4896
4867
  mcpUrl: STITCH_MCP_URL,
4897
4868
  };
4898
4869
  }
4870
+ if (playwrightRequested) {
4871
+ cbxConfig.playwright = {
4872
+ enabled: true,
4873
+ server: PLAYWRIGHT_MCP_SERVER_ID,
4874
+ mcpUrl: PLAYWRIGHT_MCP_URL,
4875
+ };
4876
+ }
4899
4877
  return {
4900
4878
  enabled: true,
4901
4879
  postmanEnabled: postmanRequested,
4902
4880
  apiKeySource,
4903
4881
  stitchEnabled,
4882
+ playwrightEnabled: playwrightRequested,
4904
4883
  stitchApiKeySource,
4905
4884
  mcpRuntime: requestedRuntime,
4906
4885
  effectiveMcpRuntime: runtimeSkipped ? null : effectiveRuntime,
@@ -4921,7 +4900,7 @@ async function resolvePostmanInstallSelection({ platform, scope, options, cwd =
4921
4900
  cbxConfigPath,
4922
4901
  };
4923
4902
  }
4924
- async function configurePostmanInstallArtifacts({ platform, scope, profilePaths, postmanSelection, overwrite = false, dryRun = false, cwd = process.cwd(), }) {
4903
+ async function configurePostmanInstallArtifacts({ platform, scope, profilePaths, postmanSelection, overwrite = false, persistCredentials = true, dryRun = false, cwd = process.cwd(), }) {
4925
4904
  if (!postmanSelection?.enabled)
4926
4905
  return null;
4927
4906
  const shouldInstallPostman = Boolean(postmanSelection.postmanEnabled);
@@ -5041,42 +5020,26 @@ async function configurePostmanInstallArtifacts({ platform, scope, profilePaths,
5041
5020
  });
5042
5021
  gitIgnoreResults.push(mcpIgnore);
5043
5022
  }
5044
- let mcpDefinitionPath = null;
5045
- let mcpDefinitionResult = null;
5023
+ const legacyDefinitionCleanupResults = [];
5046
5024
  if (shouldInstallPostman) {
5047
- mcpDefinitionPath = resolvePostmanMcpDefinitionPath({
5048
- platform,
5049
- scope: postmanSelection.mcpScope,
5050
- cwd,
5051
- });
5052
- const mcpDefinitionContent = `${JSON.stringify(buildPostmanMcpDefinition({
5053
- apiKeyEnvVar: effectiveApiKeyEnvVar,
5054
- apiKey: envApiKey,
5055
- mcpUrl: effectiveMcpUrl,
5056
- }), null, 2)}\n`;
5057
- mcpDefinitionResult = await writeGeneratedArtifact({
5058
- destination: mcpDefinitionPath,
5059
- content: mcpDefinitionContent,
5025
+ legacyDefinitionCleanupResults.push(await removeGeneratedArtifactIfExists({
5026
+ targetPath: resolvePostmanMcpDefinitionPath({
5027
+ platform,
5028
+ scope: postmanSelection.mcpScope,
5029
+ cwd,
5030
+ }),
5060
5031
  dryRun,
5061
- });
5032
+ }));
5062
5033
  }
5063
- let stitchMcpDefinitionPath = null;
5064
- let stitchMcpDefinitionResult = null;
5065
5034
  if (shouldInstallStitch) {
5066
- stitchMcpDefinitionPath = resolveStitchMcpDefinitionPath({
5067
- scope: postmanSelection.mcpScope,
5068
- cwd,
5069
- });
5070
- const stitchMcpDefinitionContent = `${JSON.stringify(buildStitchMcpDefinition({
5071
- apiKeyEnvVar: effectiveStitchApiKeyEnvVar,
5072
- apiKey: envStitchApiKey,
5073
- mcpUrl: effectiveStitchMcpUrl,
5074
- }), null, 2)}\n`;
5075
- stitchMcpDefinitionResult = await writeGeneratedArtifact({
5076
- destination: stitchMcpDefinitionPath,
5077
- content: stitchMcpDefinitionContent,
5035
+ legacyDefinitionCleanupResults.push(await removeGeneratedArtifactIfExists({
5036
+ targetPath: resolveStitchMcpDefinitionPath({
5037
+ platform,
5038
+ scope: postmanSelection.mcpScope,
5039
+ cwd,
5040
+ }),
5078
5041
  dryRun,
5079
- });
5042
+ }));
5080
5043
  }
5081
5044
  const mcpRuntimeResult = postmanSelection.runtimeSkipped
5082
5045
  ? {
@@ -5131,6 +5094,21 @@ async function configurePostmanInstallArtifacts({ platform, scope, profilePaths,
5131
5094
  dryRun,
5132
5095
  })
5133
5096
  : null;
5097
+ const credentialEnvVarNames = [];
5098
+ if (persistCredentials && shouldInstallPostman && effectiveApiKeySource === "env") {
5099
+ credentialEnvVarNames.push(effectiveApiKeyEnvVar || POSTMAN_API_KEY_ENV_VAR);
5100
+ }
5101
+ if (persistCredentials &&
5102
+ shouldInstallStitch &&
5103
+ effectiveStitchApiKeySource === "env") {
5104
+ credentialEnvVarNames.push(effectiveStitchApiKeyEnvVar || STITCH_API_KEY_ENV_VAR);
5105
+ }
5106
+ const persistedCredentials = credentialEnvVarNames.length > 0
5107
+ ? await persistManagedCredentialsEnv({
5108
+ envVarNames: [...new Set(credentialEnvVarNames)],
5109
+ dryRun,
5110
+ })
5111
+ : null;
5134
5112
  return {
5135
5113
  enabled: true,
5136
5114
  mcpScope: postmanSelection.mcpScope,
@@ -5144,10 +5122,14 @@ async function configurePostmanInstallArtifacts({ platform, scope, profilePaths,
5144
5122
  mcpToolSync: postmanSelection.mcpToolSync,
5145
5123
  foundryMcpEnabled: postmanSelection.foundryMcpEnabled,
5146
5124
  postmanEnabled: shouldInstallPostman,
5125
+ playwrightEnabled: Boolean(postmanSelection.playwrightEnabled),
5147
5126
  postmanMode: shouldInstallPostman && effectiveMcpUrl
5148
5127
  ? resolvePostmanModeFromUrl(effectiveMcpUrl, DEFAULT_POSTMAN_INSTALL_MODE)
5149
5128
  : null,
5150
5129
  postmanMcpUrl: shouldInstallPostman ? effectiveMcpUrl : null,
5130
+ playwrightMcpUrl: postmanSelection.playwrightEnabled
5131
+ ? PLAYWRIGHT_MCP_URL
5132
+ : null,
5151
5133
  apiKeySource: effectiveApiKeySource,
5152
5134
  stitchApiKeySource: effectiveStitchApiKeySource,
5153
5135
  defaultWorkspaceId: effectiveDefaultWorkspaceId,
@@ -5155,13 +5137,11 @@ async function configurePostmanInstallArtifacts({ platform, scope, profilePaths,
5155
5137
  cbxConfigPath: postmanSelection.cbxConfigPath,
5156
5138
  cbxConfigResult,
5157
5139
  gitIgnoreResults,
5158
- mcpDefinitionPath,
5159
- mcpDefinitionResult,
5160
- stitchMcpDefinitionPath,
5161
- stitchMcpDefinitionResult,
5140
+ legacyDefinitionCleanupResults,
5162
5141
  mcpRuntimeResult,
5163
5142
  mcpCatalogSyncResults,
5164
5143
  legacySkillMcpCleanup,
5144
+ persistedCredentials,
5165
5145
  };
5166
5146
  }
5167
5147
  function resolveMcpScopeFromConfigDocument(configValue, fallbackScope) {
@@ -5179,50 +5159,50 @@ function resolveMcpScopeFromConfigDocument(configValue, fallbackScope) {
5179
5159
  }
5180
5160
  async function applyPostmanConfigArtifacts({ platform, mcpScope, configValue, dryRun = false, cwd = process.cwd(), }) {
5181
5161
  const warnings = [];
5182
- const postmanState = ensureCredentialServiceState(configValue, "postman");
5162
+ const storedPostmanState = parseStoredPostmanConfig(configValue);
5163
+ const postmanEnabled = Boolean(storedPostmanState);
5164
+ const postmanState = storedPostmanState ||
5165
+ parseStoredCredentialServiceConfig({ service: "postman", rawService: {} });
5183
5166
  const stitchState = parseStoredStitchConfig(configValue);
5184
- const postmanApiKeyEnvVar = normalizePostmanApiKey(postmanState.apiKeyEnvVar) ||
5185
- POSTMAN_API_KEY_ENV_VAR;
5186
- const postmanMcpUrl = postmanState.mcpUrl || POSTMAN_MCP_URL;
5167
+ const postmanApiKeyEnvVar = postmanEnabled
5168
+ ? normalizePostmanApiKey(postmanState.apiKeyEnvVar) ||
5169
+ POSTMAN_API_KEY_ENV_VAR
5170
+ : POSTMAN_API_KEY_ENV_VAR;
5171
+ const postmanMcpUrl = postmanEnabled
5172
+ ? postmanState.mcpUrl || POSTMAN_MCP_URL
5173
+ : POSTMAN_MCP_URL;
5187
5174
  const stitchEnabled = Boolean(stitchState);
5188
- const playwrightEnabled = Boolean(configValue?.playwright);
5175
+ const playwrightConfig = configValue?.playwright &&
5176
+ typeof configValue.playwright === "object" &&
5177
+ !Array.isArray(configValue.playwright)
5178
+ ? configValue.playwright
5179
+ : null;
5180
+ const playwrightEnabled = Boolean(playwrightConfig?.enabled ?? configValue?.playwright);
5181
+ const playwrightMcpUrl = String(playwrightConfig?.mcpUrl || PLAYWRIGHT_MCP_URL).trim() ||
5182
+ PLAYWRIGHT_MCP_URL;
5189
5183
  const stitchApiKeyEnvVar = normalizePostmanApiKey(stitchState?.apiKeyEnvVar) || STITCH_API_KEY_ENV_VAR;
5190
5184
  const stitchMcpUrl = stitchState?.mcpUrl || STITCH_MCP_URL;
5191
5185
  const foundryRuntime = normalizeMcpRuntime(configValue?.mcp?.effectiveRuntime || configValue?.mcp?.runtime, "local");
5192
- const resolvedPostmanApiKey = normalizePostmanApiKey(process.env[postmanApiKeyEnvVar]);
5193
- const resolvedStitchApiKey = normalizePostmanApiKey(process.env[stitchApiKeyEnvVar]);
5194
- const mcpDefinitionPath = resolvePostmanMcpDefinitionPath({
5195
- platform,
5196
- scope: mcpScope,
5197
- cwd,
5198
- });
5199
- const mcpDefinitionContent = `${JSON.stringify(buildPostmanMcpDefinition({
5200
- apiKeyEnvVar: postmanApiKeyEnvVar,
5201
- apiKey: resolvedPostmanApiKey,
5202
- mcpUrl: postmanMcpUrl,
5203
- }), null, 2)}\n`;
5204
- const mcpDefinitionResult = await writeGeneratedArtifact({
5205
- destination: mcpDefinitionPath,
5206
- content: mcpDefinitionContent,
5207
- dryRun,
5208
- });
5209
- let stitchMcpDefinitionPath = null;
5210
- let stitchMcpDefinitionResult = null;
5186
+ const legacyDefinitionCleanupResults = [];
5187
+ if (postmanEnabled && platform) {
5188
+ legacyDefinitionCleanupResults.push(await removeGeneratedArtifactIfExists({
5189
+ targetPath: resolvePostmanMcpDefinitionPath({
5190
+ platform,
5191
+ scope: mcpScope,
5192
+ cwd,
5193
+ }),
5194
+ dryRun,
5195
+ }));
5196
+ }
5211
5197
  if (stitchEnabled) {
5212
- stitchMcpDefinitionPath = resolveStitchMcpDefinitionPath({
5213
- scope: mcpScope,
5214
- cwd,
5215
- });
5216
- const stitchMcpDefinitionContent = `${JSON.stringify(buildStitchMcpDefinition({
5217
- apiKeyEnvVar: stitchApiKeyEnvVar,
5218
- apiKey: resolvedStitchApiKey,
5219
- mcpUrl: stitchMcpUrl,
5220
- }), null, 2)}\n`;
5221
- stitchMcpDefinitionResult = await writeGeneratedArtifact({
5222
- destination: stitchMcpDefinitionPath,
5223
- content: stitchMcpDefinitionContent,
5198
+ legacyDefinitionCleanupResults.push(await removeGeneratedArtifactIfExists({
5199
+ targetPath: resolveStitchMcpDefinitionPath({
5200
+ platform,
5201
+ scope: mcpScope,
5202
+ cwd,
5203
+ }),
5224
5204
  dryRun,
5225
- });
5205
+ }));
5226
5206
  }
5227
5207
  let mcpRuntimeResult = null;
5228
5208
  if (!platform) {
@@ -5234,6 +5214,7 @@ async function applyPostmanConfigArtifacts({ platform, mcpScope, configValue, dr
5234
5214
  mcpScope,
5235
5215
  apiKeyEnvVar: postmanApiKeyEnvVar,
5236
5216
  mcpUrl: postmanMcpUrl,
5217
+ includePostmanMcp: postmanEnabled,
5237
5218
  stitchApiKeyEnvVar,
5238
5219
  stitchMcpUrl,
5239
5220
  includeStitchMcp: stitchEnabled,
@@ -5246,10 +5227,10 @@ async function applyPostmanConfigArtifacts({ platform, mcpScope, configValue, dr
5246
5227
  warnings.push(...(mcpRuntimeResult.warnings || []));
5247
5228
  }
5248
5229
  return {
5249
- mcpDefinitionPath,
5250
- mcpDefinitionResult,
5251
- stitchMcpDefinitionPath,
5252
- stitchMcpDefinitionResult,
5230
+ postmanEnabled,
5231
+ playwrightEnabled,
5232
+ playwrightMcpUrl: playwrightEnabled ? playwrightMcpUrl : null,
5233
+ legacyDefinitionCleanupResults,
5253
5234
  mcpRuntimeResult,
5254
5235
  warnings,
5255
5236
  };
@@ -5989,11 +5970,14 @@ function printPostmanSetupSummary({ postmanSetup }) {
5989
5970
  return;
5990
5971
  console.log("\nMCP setup:");
5991
5972
  console.log(`- MCP scope: ${postmanSetup.mcpScope}`);
5973
+ if (postmanSetup.playwrightEnabled) {
5974
+ console.log(`- Playwright MCP: enabled (${postmanSetup.playwrightMcpUrl || PLAYWRIGHT_MCP_URL})`);
5975
+ }
5992
5976
  if (postmanSetup.postmanEnabled && postmanSetup.postmanMode) {
5993
5977
  console.log(`- Postman mode: ${postmanSetup.postmanMode}`);
5994
5978
  }
5995
5979
  if (postmanSetup.postmanEnabled && postmanSetup.postmanMcpUrl) {
5996
- console.log(`- Postman MCP URL: ${postmanSetup.postmanMcpUrl}`);
5980
+ console.log(`- Postman upstream MCP URL: ${postmanSetup.postmanMcpUrl}`);
5997
5981
  }
5998
5982
  console.log(`- Config file: ${postmanSetup.cbxConfigResult.action} (${postmanSetup.cbxConfigPath})`);
5999
5983
  console.log(`- MCP runtime (requested): ${postmanSetup.mcpRuntime}`);
@@ -6004,7 +5988,7 @@ function printPostmanSetupSummary({ postmanSetup }) {
6004
5988
  console.log(`- MCP build local: ${postmanSetup.mcpBuildLocal ? "yes" : "no"}`);
6005
5989
  console.log(`- MCP image prepare: ${postmanSetup.dockerImageAction}`);
6006
5990
  console.log(`- MCP tool sync: ${postmanSetup.mcpToolSync ? "enabled" : "disabled"}`);
6007
- console.log(`- Foundry MCP side-by-side: ${postmanSetup.foundryMcpEnabled ? (postmanSetup.effectiveMcpRuntime === "docker" ? "enabled (docker endpoint)" : "enabled (cbx mcp serve)") : "disabled"}`);
5991
+ console.log(`- Foundry MCP gateway: ${postmanSetup.foundryMcpEnabled ? (postmanSetup.effectiveMcpRuntime === "docker" ? "enabled (docker endpoint)" : "enabled (cbx mcp serve)") : "disabled"}`);
6008
5992
  if (postmanSetup.postmanEnabled) {
6009
5993
  console.log(`- Postman API key source: ${postmanSetup.apiKeySource}`);
6010
5994
  }
@@ -6017,12 +6001,8 @@ function printPostmanSetupSummary({ postmanSetup }) {
6017
6001
  for (const ignoreResult of postmanSetup.gitIgnoreResults || []) {
6018
6002
  console.log(`- .gitignore (${ignoreResult.filePath}): ${ignoreResult.action}`);
6019
6003
  }
6020
- if (postmanSetup.mcpDefinitionPath && postmanSetup.mcpDefinitionResult) {
6021
- console.log(`- Managed MCP definition (${postmanSetup.mcpDefinitionPath}): ${postmanSetup.mcpDefinitionResult.action}`);
6022
- }
6023
- if (postmanSetup.stitchMcpDefinitionPath &&
6024
- postmanSetup.stitchMcpDefinitionResult) {
6025
- console.log(`- Managed Stitch MCP definition (${postmanSetup.stitchMcpDefinitionPath}): ${postmanSetup.stitchMcpDefinitionResult.action}`);
6004
+ for (const cleanupResult of postmanSetup.legacyDefinitionCleanupResults || []) {
6005
+ console.log(`- Legacy direct MCP cleanup (${cleanupResult.path}): ${cleanupResult.action}`);
6026
6006
  }
6027
6007
  if (postmanSetup.mcpRuntimeResult) {
6028
6008
  console.log(`- Platform MCP target (${postmanSetup.mcpRuntimeResult.path || "n/a"}): ${postmanSetup.mcpRuntimeResult.action}`);
@@ -6037,6 +6017,13 @@ function printPostmanSetupSummary({ postmanSetup }) {
6037
6017
  }
6038
6018
  }
6039
6019
  }
6020
+ if (postmanSetup.persistedCredentials) {
6021
+ console.log(`- Credential vault (${postmanSetup.persistedCredentials.envPath}): ${postmanSetup.persistedCredentials.action}`);
6022
+ console.log(`- Credential vars: ${postmanSetup.persistedCredentials.persisted.length > 0 ? postmanSetup.persistedCredentials.persisted.join(", ") : "(none)"}`);
6023
+ if (postmanSetup.persistedCredentials.missing.length > 0) {
6024
+ console.log(`- Missing credential vars: ${postmanSetup.persistedCredentials.missing.join(", ")}`);
6025
+ }
6026
+ }
6040
6027
  if (postmanSetup.legacySkillMcpCleanup) {
6041
6028
  console.log(`- Legacy skill mcp.json cleanup (${postmanSetup.legacySkillMcpCleanup.path}): ${postmanSetup.legacySkillMcpCleanup.action}`);
6042
6029
  }
@@ -6328,9 +6315,10 @@ function withInstallOptions(command) {
6328
6315
  .option("--scope <scope>", "install scope: project (global is accepted but coerced to project)", "project")
6329
6316
  .option("-b, --bundle <bundle>", "bundle id (default: agent-environment-setup)")
6330
6317
  .option("--overwrite", "overwrite existing files")
6331
- .option("--postman", "optional: install Postman skill and generate cbx_config.json")
6318
+ .option("--postman", "optional: configure Postman profiles and gateway-backed Foundry MCP wiring")
6332
6319
  .option("--postman-mode <mode>", "Postman MCP mode for --postman: minimal|code|full (default: full)")
6333
- .option("--stitch", "optional: include Stitch MCP profile/config alongside Postman")
6320
+ .option("--stitch", "optional: configure Stitch profiles and gateway-backed Foundry MCP wiring")
6321
+ .option("--playwright", "optional: include Playwright MCP server wiring")
6334
6322
  .option("--postman-api-key <key>", "deprecated: inline key mode is disabled. Use env vars + profiles.")
6335
6323
  .option("--postman-workspace-id <id|null>", "optional: set default Postman workspace ID (use 'null' for no default)")
6336
6324
  .option("--stitch-api-key <key>", "deprecated: inline key mode is disabled. Use env vars + profiles.")
@@ -6342,10 +6330,10 @@ function withInstallOptions(command) {
6342
6330
  .option("--mcp-build-local", "build MCP Docker image from local package mcp/ directory instead of pulling")
6343
6331
  .option("--mcp-tool-sync", "enable automatic MCP tool catalog sync (default: enabled)")
6344
6332
  .option("--no-mcp-tool-sync", "disable automatic MCP tool catalog sync")
6345
- .option("--no-foundry-mcp", "disable side-by-side cubis-foundry MCP registration during --postman setup")
6333
+ .option("--no-foundry-mcp", "deprecated: Postman/Stitch always use Cubis Foundry MCP gateway wiring")
6346
6334
  .option("--terminal-integration", "Antigravity only: enable terminal verification integration (prompts for verifier when interactive)")
6347
6335
  .option("--terminal-verifier <provider>", "Antigravity only: verifier provider (codex|gemini). Implies --terminal-integration.")
6348
- .option("--skill-profile <profile>", "skill install profile: core|web-backend|full (default: core)", DEFAULT_SKILL_PROFILE)
6336
+ .option("--skill-profile <profile>", "skill install profile: core|web-backend|full", DEFAULT_SKILL_PROFILE)
6349
6337
  .option("--all-skills", "alias for --skill-profile full")
6350
6338
  .option("--target <path>", "install into target project directory instead of cwd")
6351
6339
  .option("--link", "create symlinks instead of copies (edits in source are instantly reflected)")
@@ -6618,6 +6606,7 @@ async function performWorkflowInstall(options, { postmanSelectionOverride = null
6618
6606
  profilePaths: installResult.profilePaths,
6619
6607
  postmanSelection,
6620
6608
  overwrite: Boolean(options.overwrite),
6609
+ persistCredentials: !options.initWizardMode,
6621
6610
  dryRun,
6622
6611
  cwd,
6623
6612
  });
@@ -7370,14 +7359,16 @@ async function runWorkflowRemoveAll(options) {
7370
7359
  dryRun,
7371
7360
  records: removedRecords,
7372
7361
  });
7373
- if (platform === "antigravity") {
7374
- await removePathRecord({
7375
- targetPath: resolveStitchMcpDefinitionPath({ scope, cwd }),
7376
- category: `${platform}/${scope}/stitch-mcp-definition`,
7377
- dryRun,
7378
- records: removedRecords,
7379
- });
7380
- }
7362
+ await removePathRecord({
7363
+ targetPath: resolveStitchMcpDefinitionPath({
7364
+ platform,
7365
+ scope,
7366
+ cwd,
7367
+ }),
7368
+ category: `${platform}/${scope}/stitch-mcp-definition`,
7369
+ dryRun,
7370
+ records: removedRecords,
7371
+ });
7381
7372
  const runtimeResults = await removePlatformMcpRuntimeTargets({
7382
7373
  platform,
7383
7374
  scope,
@@ -7651,7 +7642,7 @@ function prepareConfigDocument(existingValue, { scope, generatedBy }) {
7651
7642
  next.mcp = {};
7652
7643
  next.mcp.scope = scope;
7653
7644
  if (!next.mcp.server)
7654
- next.mcp.server = POSTMAN_SKILL_ID;
7645
+ next.mcp.server = FOUNDRY_MCP_SERVER_ID;
7655
7646
  return next;
7656
7647
  }
7657
7648
  function ensureCredentialServiceState(configValue, service) {
@@ -7880,26 +7871,127 @@ function migrateInlineCredentialsInConfig(configValue) {
7880
7871
  changed: JSON.stringify(next) !== JSON.stringify(configValue || {}),
7881
7872
  };
7882
7873
  }
7883
- async function collectInlineHeaderFindings({ scope, cwd = process.cwd() }) {
7874
+ function resolveCredentialLeakScanTargets({ scope, cwd = process.cwd() }) {
7875
+ const workspaceRoot = findWorkspaceRoot(cwd);
7876
+ const targets = new Set([
7877
+ resolveLegacyPostmanConfigPath({ scope, cwd }),
7878
+ scope === "global"
7879
+ ? path.join(os.homedir(), ".gemini", "settings.json")
7880
+ : path.join(workspaceRoot, ".gemini", "settings.json"),
7881
+ scope === "global"
7882
+ ? path.join(os.homedir(), ".claude", "mcp.json")
7883
+ : path.join(workspaceRoot, ".mcp.json"),
7884
+ scope === "global"
7885
+ ? path.join(os.homedir(), ".copilot", "mcp-config.json")
7886
+ : path.join(workspaceRoot, ".vscode", "mcp.json"),
7887
+ ]);
7888
+ if (scope === "global") {
7889
+ targets.add(path.join(os.homedir(), ".codex", "config.toml"));
7890
+ }
7891
+ for (const platform of Object.keys(WORKFLOW_PROFILES)) {
7892
+ targets.add(resolvePostmanMcpDefinitionPath({ platform, scope, cwd }));
7893
+ targets.add(resolveStitchMcpDefinitionPath({ platform, scope, cwd }));
7894
+ }
7895
+ return [...targets];
7896
+ }
7897
+ function collectCredentialLeakMatches(raw) {
7898
+ const matches = [];
7899
+ const patterns = [
7900
+ {
7901
+ id: "inline-apiKey-field",
7902
+ pattern: /"apiKey"\s*:\s*"(?!\$\{)[^"]+/i,
7903
+ },
7904
+ {
7905
+ id: "inline-bearer-header-json",
7906
+ pattern: /"Authorization"\s*:\s*"Bearer\s+(?!\$\{)[^"]+/i,
7907
+ },
7908
+ {
7909
+ id: "inline-bearer-header-toml",
7910
+ pattern: /http_headers\s*=\s*\{[^}]*Authorization\s*=\s*"Bearer\s+(?!\$\{)[^"]+/is,
7911
+ },
7912
+ {
7913
+ id: "inline-stitch-header-arg",
7914
+ pattern: /X-Goog-Api-Key:(?!\s*\$\{[A-Za-z_][A-Za-z0-9_]*\})\s*[^"\n]+/i,
7915
+ },
7916
+ {
7917
+ id: "inline-stitch-header-json",
7918
+ pattern: /"X-Goog-Api-Key"\s*:\s*"(?!\$\{)[^"]+/i,
7919
+ },
7920
+ ];
7921
+ for (const { id, pattern } of patterns) {
7922
+ if (pattern.test(raw)) {
7923
+ matches.push(id);
7924
+ }
7925
+ }
7926
+ return matches;
7927
+ }
7928
+ async function collectCredentialLeakFindings({ scope, cwd = process.cwd() }) {
7884
7929
  const findings = [];
7885
- const stitchDefinitionPath = resolveStitchMcpDefinitionPath({ scope, cwd });
7886
- const geminiSettingsPath = scope === "global"
7887
- ? path.join(os.homedir(), ".gemini", "settings.json")
7888
- : path.join(findWorkspaceRoot(cwd), ".gemini", "settings.json");
7889
- const scanFile = async (filePath) => {
7930
+ for (const filePath of resolveCredentialLeakScanTargets({ scope, cwd })) {
7890
7931
  if (!(await pathExists(filePath)))
7891
- return;
7932
+ continue;
7892
7933
  const raw = await readFile(filePath, "utf8");
7893
- const unsafeStitchHeader = /X-Goog-Api-Key:(?!\s*\$\{[A-Za-z_][A-Za-z0-9_]*\})\s*[^"\n]+/i;
7894
- const unsafeBearerHeader = /"Authorization"\s*:\s*"Bearer\s+(?!\$\{)[^"]+/i;
7895
- if (unsafeStitchHeader.test(raw) || unsafeBearerHeader.test(raw)) {
7896
- findings.push(filePath);
7934
+ const matches = collectCredentialLeakMatches(raw);
7935
+ if (matches.length > 0) {
7936
+ findings.push({ filePath, matches });
7897
7937
  }
7898
- };
7899
- await scanFile(stitchDefinitionPath);
7900
- await scanFile(geminiSettingsPath);
7938
+ }
7901
7939
  return findings;
7902
7940
  }
7941
+ async function cleanupLegacyDirectCredentialArtifacts({ scope, dryRun = false, cwd = process.cwd(), }) {
7942
+ const workspaceRoot = findWorkspaceRoot(cwd);
7943
+ const cleanupResults = [];
7944
+ const legacyServerIds = [POSTMAN_SKILL_ID, STITCH_MCP_SERVER_ID];
7945
+ cleanupResults.push(await removeMcpRuntimeEntriesJson({
7946
+ filePath: scope === "global"
7947
+ ? path.join(os.homedir(), ".gemini", "settings.json")
7948
+ : path.join(workspaceRoot, ".gemini", "settings.json"),
7949
+ keyName: "mcpServers",
7950
+ serverIds: legacyServerIds,
7951
+ dryRun,
7952
+ }));
7953
+ cleanupResults.push(await removeMcpRuntimeEntriesJson({
7954
+ filePath: scope === "global"
7955
+ ? path.join(os.homedir(), ".claude", "mcp.json")
7956
+ : path.join(workspaceRoot, ".mcp.json"),
7957
+ keyName: "mcpServers",
7958
+ serverIds: legacyServerIds,
7959
+ dryRun,
7960
+ }));
7961
+ if (scope === "global") {
7962
+ cleanupResults.push(await removeMcpRuntimeEntriesJson({
7963
+ filePath: path.join(os.homedir(), ".copilot", "mcp-config.json"),
7964
+ keyName: "mcpServers",
7965
+ serverIds: legacyServerIds,
7966
+ dryRun,
7967
+ }));
7968
+ cleanupResults.push(await removeMcpRuntimeEntriesCodexToml({
7969
+ filePath: path.join(os.homedir(), ".codex", "config.toml"),
7970
+ serverIds: legacyServerIds,
7971
+ dryRun,
7972
+ cwd,
7973
+ }));
7974
+ }
7975
+ else {
7976
+ cleanupResults.push(await removeMcpRuntimeEntriesJson({
7977
+ filePath: path.join(workspaceRoot, ".vscode", "mcp.json"),
7978
+ keyName: "servers",
7979
+ serverIds: legacyServerIds,
7980
+ dryRun,
7981
+ }));
7982
+ }
7983
+ for (const platform of Object.keys(WORKFLOW_PROFILES)) {
7984
+ cleanupResults.push(await removeGeneratedArtifactIfExists({
7985
+ targetPath: resolvePostmanMcpDefinitionPath({ platform, scope, cwd }),
7986
+ dryRun,
7987
+ }));
7988
+ cleanupResults.push(await removeGeneratedArtifactIfExists({
7989
+ targetPath: resolveStitchMcpDefinitionPath({ platform, scope, cwd }),
7990
+ dryRun,
7991
+ }));
7992
+ }
7993
+ return cleanupResults.filter((item) => item.action !== "missing" && item.action !== "unchanged");
7994
+ }
7903
7995
  async function runWorkflowConfigKeysList(options) {
7904
7996
  try {
7905
7997
  const opts = resolveActionOptions(options);
@@ -8126,6 +8218,7 @@ async function runWorkflowConfigKeysMigrateInline(options) {
8126
8218
  const scopeArg = readCliOptionFromArgv("--scope");
8127
8219
  const scope = normalizeMcpScope(scopeArg ?? opts.scope, "global");
8128
8220
  const dryRun = hasCliFlag("--dry-run") || Boolean(opts.dryRun);
8221
+ await loadManagedCredentialsEnv();
8129
8222
  const { configPath, existing, existingValue } = await loadConfigForScope({
8130
8223
  scope,
8131
8224
  cwd,
@@ -8140,6 +8233,21 @@ async function runWorkflowConfigKeysMigrateInline(options) {
8140
8233
  existingExists: existing.exists,
8141
8234
  dryRun,
8142
8235
  });
8236
+ const cleanupResults = await cleanupLegacyDirectCredentialArtifacts({
8237
+ scope,
8238
+ dryRun,
8239
+ cwd,
8240
+ });
8241
+ const platform = normalizePlatform(result.next?.mcp?.platform);
8242
+ const secureArtifacts = platform && WORKFLOW_PROFILES[platform]
8243
+ ? await applyPostmanConfigArtifacts({
8244
+ platform,
8245
+ mcpScope: resolveMcpScopeFromConfigDocument(result.next, scope),
8246
+ configValue: result.next,
8247
+ dryRun,
8248
+ cwd,
8249
+ })
8250
+ : null;
8143
8251
  console.log(`Config file: ${configPath}`);
8144
8252
  console.log(`Action: ${action}`);
8145
8253
  console.log(`Inline key fields found: ${result.findings.length}`);
@@ -8154,6 +8262,19 @@ async function runWorkflowConfigKeysMigrateInline(options) {
8154
8262
  console.log(`- ${envVar}`);
8155
8263
  }
8156
8264
  }
8265
+ console.log(`Legacy direct MCP cleanup actions: ${cleanupResults.length}`);
8266
+ for (const cleanup of cleanupResults) {
8267
+ console.log(`- ${cleanup.action} ${cleanup.path}`);
8268
+ }
8269
+ if (secureArtifacts?.mcpRuntimeResult) {
8270
+ console.log(`Secure platform MCP target: ${secureArtifacts.mcpRuntimeResult.action} (${secureArtifacts.mcpRuntimeResult.path || "n/a"})`);
8271
+ }
8272
+ for (const cleanup of secureArtifacts?.legacyDefinitionCleanupResults || []) {
8273
+ console.log(`- ${cleanup.action} ${cleanup.path}`);
8274
+ }
8275
+ for (const warning of secureArtifacts?.warnings || []) {
8276
+ console.log(`Warning: ${warning}`);
8277
+ }
8157
8278
  }
8158
8279
  catch (error) {
8159
8280
  if (error?.name === "ExitPromptError") {
@@ -8170,6 +8291,7 @@ async function runWorkflowConfigKeysDoctor(options) {
8170
8291
  const cwd = process.cwd();
8171
8292
  const scopeArg = readCliOptionFromArgv("--scope");
8172
8293
  const scope = normalizeMcpScope(scopeArg ?? opts.scope, "global");
8294
+ await loadManagedCredentialsEnv();
8173
8295
  const { configPath, existing, existingValue } = await loadConfigForScope({
8174
8296
  scope,
8175
8297
  cwd,
@@ -8180,15 +8302,15 @@ async function runWorkflowConfigKeysDoctor(options) {
8180
8302
  return;
8181
8303
  }
8182
8304
  const configFindings = collectInlineCredentialFindings(existingValue);
8183
- const artifactFindings = await collectInlineHeaderFindings({ scope, cwd });
8305
+ const artifactFindings = await collectCredentialLeakFindings({ scope, cwd });
8184
8306
  const migrationPreview = migrateInlineCredentialsInConfig(existingValue);
8185
8307
  console.log(`Inline key findings: ${configFindings.length}`);
8186
8308
  for (const finding of configFindings) {
8187
8309
  console.log(`- ${finding.path}`);
8188
8310
  }
8189
- console.log(`Unsafe header findings: ${artifactFindings.length}`);
8190
- for (const filePath of artifactFindings) {
8191
- console.log(`- ${filePath}`);
8311
+ console.log(`Credential leak findings: ${artifactFindings.length}`);
8312
+ for (const finding of artifactFindings) {
8313
+ console.log(`- ${finding.filePath} [${finding.matches.join(", ")}]`);
8192
8314
  }
8193
8315
  if (migrationPreview.requiredEnvVars.length > 0) {
8194
8316
  console.log("Expected env vars:");
@@ -8202,7 +8324,7 @@ async function runWorkflowConfigKeysDoctor(options) {
8202
8324
  else {
8203
8325
  console.log("Doctor result: issues detected. Run `cbx workflows config keys migrate-inline --scope " +
8204
8326
  scope +
8205
- "` and reinstall with `--overwrite`.");
8327
+ "` to scrub keys and reapply secure Foundry MCP wiring.");
8206
8328
  }
8207
8329
  }
8208
8330
  catch (error) {
@@ -8392,6 +8514,7 @@ async function runWorkflowConfig(options) {
8392
8514
  if (!next.mcp || typeof next.mcp !== "object" || Array.isArray(next.mcp)) {
8393
8515
  next.mcp = {};
8394
8516
  }
8517
+ next.mcp.server = FOUNDRY_MCP_SERVER_ID;
8395
8518
  if (hasMcpRuntimeOption) {
8396
8519
  next.mcp.runtime = mcpRuntime;
8397
8520
  next.mcp.effectiveRuntime = mcpRuntime;
@@ -8455,10 +8578,9 @@ async function runWorkflowConfig(options) {
8455
8578
  console.log(`postman.mode: ${effectivePostmanMode}`);
8456
8579
  console.log(`postman.mcpUrl: ${effectivePostmanState.mcpUrl}`);
8457
8580
  if (postmanArtifacts) {
8458
- console.log(`postman.definition: ${postmanArtifacts.mcpDefinitionResult.action} (${postmanArtifacts.mcpDefinitionPath})`);
8459
- if (postmanArtifacts.stitchMcpDefinitionPath &&
8460
- postmanArtifacts.stitchMcpDefinitionResult) {
8461
- console.log(`stitch.definition: ${postmanArtifacts.stitchMcpDefinitionResult.action} (${postmanArtifacts.stitchMcpDefinitionPath})`);
8581
+ for (const cleanupResult of postmanArtifacts.legacyDefinitionCleanupResults ||
8582
+ []) {
8583
+ console.log(`legacy.definition.cleanup: ${cleanupResult.action} (${cleanupResult.path})`);
8462
8584
  }
8463
8585
  if (postmanArtifacts.mcpRuntimeResult) {
8464
8586
  console.log(`platform.mcp.target: ${postmanArtifacts.mcpRuntimeResult.action} (${postmanArtifacts.mcpRuntimeResult.path || "n/a"})`);
@@ -9554,7 +9676,12 @@ function normalizeInitPlatforms(value) {
9554
9676
  return normalized;
9555
9677
  }
9556
9678
  function normalizeInitMcpSelections(value) {
9557
- const allowed = new Set(["cubis-foundry", "postman", "stitch"]);
9679
+ const allowed = new Set([
9680
+ "cubis-foundry",
9681
+ "postman",
9682
+ "stitch",
9683
+ "playwright",
9684
+ ]);
9558
9685
  const items = Array.isArray(value) ? value : parseCsvOption(value);
9559
9686
  const normalized = [];
9560
9687
  for (const item of items) {
@@ -9653,7 +9780,7 @@ async function runInitWizard(options) {
9653
9780
  if (selections.platforms.length === 0) {
9654
9781
  throw new Error("No platforms selected.");
9655
9782
  }
9656
- const runtimeSelectableMcp = selections.selectedMcps.length > 0;
9783
+ const runtimeSelectableMcp = selections.selectedMcps.some((item) => item !== "playwright");
9657
9784
  if (runtimeSelectableMcp && isInteractive) {
9658
9785
  const runtimeSelection = await promptInitMcpRuntime({
9659
9786
  defaultRuntime: selections.mcpRuntime,
@@ -9791,9 +9918,14 @@ async function runInitWizard(options) {
9791
9918
  }
9792
9919
  }
9793
9920
  if (emitJson) {
9921
+ const sanitizedSelections = {
9922
+ ...selections,
9923
+ postmanApiKey: selections.postmanApiKey ? "***REDACTED***" : null,
9924
+ stitchApiKey: selections.stitchApiKey ? "***REDACTED***" : null,
9925
+ };
9794
9926
  console.log(JSON.stringify({
9795
9927
  dryRun,
9796
- selections,
9928
+ selections: sanitizedSelections,
9797
9929
  results,
9798
9930
  persistedCredentials,
9799
9931
  }, null, 2));