@cubis/foundry 0.3.21 → 0.3.23

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/CHANGELOG.md ADDED
@@ -0,0 +1,28 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project are documented in this file.
4
+
5
+ ## [0.3.23] - 2026-02-26
6
+
7
+ ### Added
8
+
9
+ - Added a root `CHANGELOG.md` and npm-visible changelog section in `README.md`.
10
+ - Included `CHANGELOG.md` in published npm package files.
11
+
12
+ ## [0.3.22] - 2026-02-26
13
+
14
+ ### Added
15
+
16
+ - Added Antigravity-only default `StitchMCP` runtime setup in Gemini settings.
17
+ - Added `--stitch-api-key` and `STITCH_API_KEY` support for Stitch MCP config.
18
+ - Added managed Stitch MCP definition output in `.cbx/mcp/antigravity/stitch.json`.
19
+ - Added Stitch and Postman API key setup links and guidance in `README.md`.
20
+
21
+ ## [0.3.21] - 2026-02-26
22
+
23
+ ### Changed
24
+
25
+ - Split Postman MCP management from skill folder into managed `.cbx/mcp/<platform>/postman.json`.
26
+ - Added centralized Postman config via `cbx_config.json`.
27
+ - Added MCP scope controls (`project/workspace/global/user`) with platform-aware runtime placement.
28
+ - Kept rules/engineering artifacts in workspace scope while defaulting skills/powers install to global scope.
package/README.md CHANGED
@@ -9,6 +9,11 @@ Primary support in this release:
9
9
  - Codex
10
10
  - Copilot (VS Code Chat + Copilot CLI)
11
11
 
12
+ ## Changelog
13
+
14
+ - Latest release notes are visible here on npm and tracked in [`CHANGELOG.md`](./CHANGELOG.md).
15
+ - GitHub view: [CHANGELOG.md](https://github.com/CUBETIQ/cubis-foundry/blob/main/CHANGELOG.md)
16
+
12
17
  ## Install
13
18
 
14
19
  ```bash
@@ -26,8 +31,9 @@ Compatibility binaries are still shipped for migration:
26
31
  # 1) Install CLI
27
32
  npm install -g @cubis/foundry
28
33
 
29
- # 2) Set Postman key once (recommended: env mode)
34
+ # 2) Set API keys once (recommended: env mode)
30
35
  export POSTMAN_API_KEY="<your-postman-api-key>"
36
+ export STITCH_API_KEY="<your-stitch-api-key>" # Antigravity StitchMCP only
31
37
 
32
38
  # 3) Install workflow bundle for your platform
33
39
  cbx workflows install --platform codex --bundle agent-environment-setup --postman --yes
@@ -54,7 +60,10 @@ cbx workflows install --platform antigravity --terminal-integration --terminal-v
54
60
  cbx workflows install --platform codex --postman
55
61
  cbx workflows install --platform codex --postman --postman-workspace-id null
56
62
  cbx workflows install --platform codex --postman --postman-api-key "<key>"
63
+ cbx workflows install --platform codex --postman --mcp-scope global
64
+ cbx workflows install --platform copilot --postman --mcp-scope project
57
65
  cbx workflows install --platform antigravity --postman
66
+ cbx workflows install --platform antigravity --postman --stitch-api-key "<key>"
58
67
  cbx workflows install --platform copilot --postman
59
68
  ```
60
69
 
@@ -63,15 +72,38 @@ Install bootstrap behavior:
63
72
  - When install scope is `global` (default), skills/powers install to global paths, while workflows + agents stay in workspace (`project`) paths.
64
73
  - Rule sync + engineering artifacts (`AGENTS.md`/`GEMINI.md`/Copilot instructions, `ENGINEERING_RULES.md`, `TECH.md`) are maintained in workspace (`project`) scope.
65
74
  - Codex workflow templates are maintained in workspace `.agents/workflows` so workflow-wrapper routing remains discoverable in project rules.
66
- - Optional `--postman` bootstrap creates `postman_setting.json` and installs/configures the Postman skill/MCP for Codex, Antigravity, and Copilot.
75
+ - Optional `--postman` bootstrap creates `cbx_config.json`, stores managed MCP definitions in `.cbx/mcp/`, and installs/configures Postman MCP for Codex, Antigravity, and Copilot.
76
+ - For Antigravity only, `--postman` also installs a default `StitchMCP` entry (`stitch.googleapis.com/mcp`) using key from `--stitch-api-key` or `STITCH_API_KEY`.
77
+ - Use `--mcp-scope <project|workspace|global|user>` to choose where MCP runtime config is installed (interactive installs prompt for this when not provided).
67
78
  - Use `cbx rules init --platform <platform> --overwrite` to force-regenerate both files.
68
79
 
69
- Postman setup behavior:
70
- - `postman_setting.json` is generated in project root (or `~/.cbx/postman_setting.json` with `--scope global`).
80
+ Postman + Antigravity Stitch setup behavior:
81
+ - `cbx_config.json` is generated in workspace root (project MCP scope) or `~/.cbx/cbx_config.json` (global MCP scope).
82
+ - Managed MCP definition files are generated under `.cbx/mcp/<platform>/postman.json` (workspace scope) or `~/.cbx/mcp/<platform>/postman.json` (global scope).
71
83
  - Env-first auth is supported: when `POSTMAN_API_KEY` is set, generated settings keep `apiKey: null` and MCP config uses `Bearer ${POSTMAN_API_KEY}`.
72
84
  - Inline auth is supported with `--postman-api-key <key>`.
73
85
  - `--postman-workspace-id null` writes JSON `null` for `defaultWorkspaceId`.
74
- - In project scope, `postman_setting.json` is auto-added to `.gitignore` (no duplicate entries).
86
+ - Antigravity gets an additional managed file at `.cbx/mcp/antigravity/stitch.json` (or `~/.cbx/mcp/antigravity/stitch.json` in global MCP scope).
87
+ - Stitch key source priority for Antigravity: `--stitch-api-key` then `STITCH_API_KEY`; when unset, generated config keeps placeholder `X-Goog-Api-Key: ur stitch key`.
88
+ - In project MCP scope, `cbx_config.json` and `.cbx/mcp/` are auto-added to `.gitignore` (no duplicate entries).
89
+
90
+ Platform runtime MCP placement:
91
+ - Codex:
92
+ - Global MCP scope: `~/.codex/config.toml` via `codex mcp add`.
93
+ - Workspace MCP scope: `.vscode/mcp.json`.
94
+ - Antigravity (Gemini CLI):
95
+ - Global MCP scope: `~/.gemini/settings.json` (`mcpServers`, includes `postman` + default `StitchMCP`).
96
+ - Workspace MCP scope: `.gemini/settings.json` (`mcpServers`, includes `postman` + default `StitchMCP`).
97
+ - Copilot:
98
+ - Workspace MCP scope: `.vscode/mcp.json`.
99
+ - Global MCP scope: `~/.copilot/mcp-config.json`.
100
+
101
+ API key docs:
102
+ - Google Stitch MCP setup docs: [stitch.withgoogle.com/docs/mcp/setup](https://stitch.withgoogle.com/docs/mcp/setup)
103
+ - Google Stitch settings (create API key): [stitch.withgoogle.com/settings](https://stitch.withgoogle.com/settings)
104
+ - Google Cloud API key guidance: [Authenticate to Google and Google Cloud MCP servers](https://docs.cloud.google.com/mcp/authenticate-mcp)
105
+ - Postman API key creation: [Generate and use Postman API keys](https://learning.postman.com/docs/developer/postman-api/authentication/)
106
+ - Postman MCP setup: [Set up the Postman MCP Server](https://learning.postman.com/docs/developer/postman-api/postman-mcp-server/set-up-postman-mcp-server)
75
107
 
76
108
  `rules` manages strict engineering policy and a generated codebase tech map:
77
109
 
package/bin/cubis.js CHANGED
@@ -13,13 +13,16 @@ import {
13
13
  writeFile
14
14
  } from "node:fs/promises";
15
15
  import { createRequire } from "node:module";
16
+ import { execFile as execFileCallback } from "node:child_process";
16
17
  import os from "node:os";
17
18
  import path from "node:path";
18
19
  import process from "node:process";
20
+ import { promisify } from "node:util";
19
21
  import { fileURLToPath } from "node:url";
20
22
 
21
23
  const require = createRequire(import.meta.url);
22
24
  const { version: CLI_VERSION } = require("../package.json");
25
+ const execFile = promisify(execFileCallback);
23
26
 
24
27
  const MANAGED_BLOCK_START_RE = /<!--\s*cbx:workflows:auto:start[^>]*-->/g;
25
28
  const MANAGED_BLOCK_END_RE = /<!--\s*cbx:workflows:auto:end\s*-->/g;
@@ -125,9 +128,16 @@ const DEFAULT_TERMINAL_VERIFIER = "codex";
125
128
  const POSTMAN_API_KEY_ENV_VAR = "POSTMAN_API_KEY";
126
129
  const POSTMAN_MCP_URL = "https://mcp.postman.com/minimal";
127
130
  const POSTMAN_SKILL_ID = "postman";
131
+ const STITCH_MCP_SERVER_ID = "StitchMCP";
132
+ const STITCH_API_KEY_ENV_VAR = "STITCH_API_KEY";
133
+ const STITCH_MCP_URL = "https://stitch.googleapis.com/mcp";
134
+ const STITCH_API_KEY_PLACEHOLDER = "ur stitch key";
135
+ const CBX_CONFIG_FILENAME = "cbx_config.json";
128
136
  const POSTMAN_SETTINGS_FILENAME = "postman_setting.json";
129
137
  const POSTMAN_API_KEY_MISSING_WARNING =
130
- `Postman API key is not configured. Set ${POSTMAN_API_KEY_ENV_VAR} or update ${POSTMAN_SETTINGS_FILENAME}.`;
138
+ `Postman API key is not configured. Set ${POSTMAN_API_KEY_ENV_VAR} or update ${CBX_CONFIG_FILENAME}.`;
139
+ const STITCH_API_KEY_MISSING_WARNING =
140
+ `Google Stitch API key is not configured. Set ${STITCH_API_KEY_ENV_VAR} or update ${CBX_CONFIG_FILENAME}.`;
131
141
  const TECH_SCAN_MAX_FILES = 5000;
132
142
  const TECH_SCAN_IGNORED_DIRS = new Set([
133
143
  ".git",
@@ -279,6 +289,14 @@ function normalizeScope(value) {
279
289
  throw new Error(`Unknown scope '${value}'. Use --scope project or --scope global.`);
280
290
  }
281
291
 
292
+ function normalizeMcpScope(value, fallback = "project") {
293
+ if (!value) return fallback;
294
+ const normalized = value.trim().toLowerCase();
295
+ if (normalized === "project" || normalized === "workspace") return "project";
296
+ if (normalized === "global" || normalized === "user") return "global";
297
+ throw new Error(`Unknown MCP scope '${value}'. Use project/workspace or global/user.`);
298
+ }
299
+
282
300
  function normalizeTerminalVerifier(value) {
283
301
  if (!value) return null;
284
302
  const normalized = value.trim().toLowerCase();
@@ -2254,7 +2272,7 @@ async function writeGeneratedArtifact({ destination, content, dryRun = false })
2254
2272
  return { action: exists ? "replaced" : "installed", path: destination };
2255
2273
  }
2256
2274
 
2257
- function resolvePostmanSettingsPath({ scope, cwd = process.cwd() }) {
2275
+ function resolveLegacyPostmanSettingsPath({ scope, cwd = process.cwd() }) {
2258
2276
  if (scope === "global") {
2259
2277
  return path.join(os.homedir(), ".cbx", POSTMAN_SETTINGS_FILENAME);
2260
2278
  }
@@ -2262,20 +2280,116 @@ function resolvePostmanSettingsPath({ scope, cwd = process.cwd() }) {
2262
2280
  return path.join(workspaceRoot, POSTMAN_SETTINGS_FILENAME);
2263
2281
  }
2264
2282
 
2265
- function buildPostmanMcpConfig({ apiKey = null, mcpUrl = POSTMAN_MCP_URL }) {
2266
- const authHeader = apiKey
2267
- ? `Bearer ${apiKey}`
2268
- : `Bearer \${${POSTMAN_API_KEY_ENV_VAR}}`;
2283
+ function resolveCbxConfigPath({ scope, cwd = process.cwd() }) {
2284
+ if (scope === "global") {
2285
+ return path.join(os.homedir(), ".cbx", CBX_CONFIG_FILENAME);
2286
+ }
2287
+ const workspaceRoot = findWorkspaceRoot(cwd);
2288
+ return path.join(workspaceRoot, CBX_CONFIG_FILENAME);
2289
+ }
2290
+
2291
+ function resolveMcpRootPath({ scope, cwd = process.cwd() }) {
2292
+ if (scope === "global") {
2293
+ return path.join(os.homedir(), ".cbx", "mcp");
2294
+ }
2295
+ const workspaceRoot = findWorkspaceRoot(cwd);
2296
+ return path.join(workspaceRoot, ".cbx", "mcp");
2297
+ }
2298
+
2299
+ function resolvePostmanMcpDefinitionPath({ platform, scope, cwd = process.cwd() }) {
2300
+ return path.join(resolveMcpRootPath({ scope, cwd }), platform, `${POSTMAN_SKILL_ID}.json`);
2301
+ }
2302
+
2303
+ function resolveStitchMcpDefinitionPath({ scope, cwd = process.cwd() }) {
2304
+ return path.join(resolveMcpRootPath({ scope, cwd }), "antigravity", "stitch.json");
2305
+ }
2306
+
2307
+ function buildPostmanAuthHeader({ apiKey = null, apiKeyEnvVar = POSTMAN_API_KEY_ENV_VAR }) {
2308
+ return apiKey ? `Bearer ${apiKey}` : `Bearer \${${apiKeyEnvVar}}`;
2309
+ }
2310
+
2311
+ function buildStitchApiHeader({ apiKey = null }) {
2312
+ return `X-Goog-Api-Key: ${apiKey || STITCH_API_KEY_PLACEHOLDER}`;
2313
+ }
2314
+
2315
+ function buildPostmanMcpDefinition({
2316
+ apiKey = null,
2317
+ apiKeyEnvVar = POSTMAN_API_KEY_ENV_VAR,
2318
+ mcpUrl = POSTMAN_MCP_URL
2319
+ }) {
2269
2320
  return {
2270
- mcpServers: {
2271
- postman: {
2272
- url: mcpUrl,
2273
- headers: {
2274
- Authorization: authHeader
2275
- }
2276
- }
2277
- },
2278
- disabled: false
2321
+ schemaVersion: 1,
2322
+ server: POSTMAN_SKILL_ID,
2323
+ transport: "http",
2324
+ url: mcpUrl,
2325
+ headers: {
2326
+ Authorization: buildPostmanAuthHeader({ apiKey, apiKeyEnvVar })
2327
+ }
2328
+ };
2329
+ }
2330
+
2331
+ function buildStitchMcpDefinition({
2332
+ apiKey = null,
2333
+ mcpUrl = STITCH_MCP_URL
2334
+ }) {
2335
+ return {
2336
+ schemaVersion: 1,
2337
+ server: STITCH_MCP_SERVER_ID,
2338
+ transport: "command",
2339
+ command: "npx",
2340
+ args: [
2341
+ "-y",
2342
+ "mcp-remote",
2343
+ mcpUrl,
2344
+ "--header",
2345
+ buildStitchApiHeader({ apiKey })
2346
+ ],
2347
+ env: {}
2348
+ };
2349
+ }
2350
+
2351
+ function buildVsCodePostmanServer({
2352
+ apiKey = null,
2353
+ apiKeyEnvVar = POSTMAN_API_KEY_ENV_VAR,
2354
+ mcpUrl = POSTMAN_MCP_URL
2355
+ }) {
2356
+ return {
2357
+ type: "sse",
2358
+ url: mcpUrl,
2359
+ headers: {
2360
+ Authorization: buildPostmanAuthHeader({ apiKey, apiKeyEnvVar })
2361
+ }
2362
+ };
2363
+ }
2364
+
2365
+ function buildGeminiPostmanServer({
2366
+ apiKey = null,
2367
+ apiKeyEnvVar = POSTMAN_API_KEY_ENV_VAR,
2368
+ mcpUrl = POSTMAN_MCP_URL
2369
+ }) {
2370
+ return {
2371
+ httpUrl: mcpUrl,
2372
+ headers: {
2373
+ Authorization: buildPostmanAuthHeader({ apiKey, apiKeyEnvVar })
2374
+ }
2375
+ };
2376
+ }
2377
+
2378
+ function buildGeminiStitchServer({
2379
+ apiKey = null,
2380
+ mcpUrl = STITCH_MCP_URL
2381
+ }) {
2382
+ return {
2383
+ $typeName: "exa.cascade_plugins_pb.CascadePluginCommandTemplate",
2384
+ command: "npx",
2385
+ args: [
2386
+ "-y",
2387
+ "mcp-remote",
2388
+ mcpUrl,
2389
+ "--header",
2390
+ buildStitchApiHeader({ apiKey })
2391
+ ],
2392
+ env: {}
2279
2393
  };
2280
2394
  }
2281
2395
 
@@ -2285,12 +2399,297 @@ function getPostmanApiKeySource({ apiKey, envApiKey }) {
2285
2399
  return "unset";
2286
2400
  }
2287
2401
 
2402
+ function getStitchApiKeySource({ apiKey, envApiKey }) {
2403
+ if (apiKey) return "inline";
2404
+ if (envApiKey) return "env";
2405
+ return "unset";
2406
+ }
2407
+
2288
2408
  function normalizePostmanApiKey(value) {
2289
2409
  if (value === undefined || value === null) return null;
2290
2410
  const normalized = String(value).trim();
2291
2411
  return normalized || null;
2292
2412
  }
2293
2413
 
2414
+ function parseStoredPostmanConfig(raw) {
2415
+ if (!raw || typeof raw !== "object") return null;
2416
+ const source = raw.postman && typeof raw.postman === "object" ? raw.postman : raw;
2417
+
2418
+ const apiKey = normalizePostmanApiKey(source.apiKey);
2419
+ const apiKeyEnvVar = String(source.apiKeyEnvVar || POSTMAN_API_KEY_ENV_VAR).trim() || POSTMAN_API_KEY_ENV_VAR;
2420
+ const mcpUrl = String(source.mcpUrl || POSTMAN_MCP_URL).trim() || POSTMAN_MCP_URL;
2421
+ const defaultWorkspaceId = normalizePostmanWorkspaceId(source.defaultWorkspaceId);
2422
+
2423
+ return {
2424
+ apiKey,
2425
+ apiKeyEnvVar,
2426
+ mcpUrl,
2427
+ defaultWorkspaceId
2428
+ };
2429
+ }
2430
+
2431
+ function parseStoredStitchConfig(raw) {
2432
+ if (!raw || typeof raw !== "object") return null;
2433
+ const source = raw.stitch && typeof raw.stitch === "object" ? raw.stitch : null;
2434
+ if (!source) return null;
2435
+
2436
+ const apiKey = normalizePostmanApiKey(source.apiKey);
2437
+ const apiKeyEnvVar = String(source.apiKeyEnvVar || STITCH_API_KEY_ENV_VAR).trim() || STITCH_API_KEY_ENV_VAR;
2438
+ const mcpUrl = String(source.mcpUrl || STITCH_MCP_URL).trim() || STITCH_MCP_URL;
2439
+
2440
+ return {
2441
+ apiKey,
2442
+ apiKeyEnvVar,
2443
+ mcpUrl
2444
+ };
2445
+ }
2446
+
2447
+ async function readJsonFileIfExists(filePath) {
2448
+ if (!(await pathExists(filePath))) return { exists: false, value: null };
2449
+ try {
2450
+ const raw = await readFile(filePath, "utf8");
2451
+ return { exists: true, value: JSON.parse(raw) };
2452
+ } catch (error) {
2453
+ return { exists: true, value: null, error };
2454
+ }
2455
+ }
2456
+
2457
+ async function upsertJsonObjectFile({ targetPath, updater, dryRun = false }) {
2458
+ const exists = await pathExists(targetPath);
2459
+ const warnings = [];
2460
+ let parsed = {};
2461
+
2462
+ if (exists) {
2463
+ try {
2464
+ const raw = await readFile(targetPath, "utf8");
2465
+ const decoded = JSON.parse(raw);
2466
+ if (decoded && typeof decoded === "object" && !Array.isArray(decoded)) {
2467
+ parsed = decoded;
2468
+ } else {
2469
+ warnings.push(`Existing JSON at ${targetPath} was not an object. Resetting structure.`);
2470
+ }
2471
+ } catch {
2472
+ warnings.push(`Existing JSON at ${targetPath} could not be parsed. Resetting structure.`);
2473
+ }
2474
+ }
2475
+
2476
+ const nextValue = updater(parsed);
2477
+ const content = `${JSON.stringify(nextValue, null, 2)}\n`;
2478
+ const writeResult = await writeGeneratedArtifact({
2479
+ destination: targetPath,
2480
+ content,
2481
+ dryRun
2482
+ });
2483
+
2484
+ return {
2485
+ action: writeResult.action,
2486
+ filePath: targetPath,
2487
+ warnings
2488
+ };
2489
+ }
2490
+
2491
+ async function removeGeneratedArtifactIfExists({ targetPath, dryRun = false }) {
2492
+ const exists = await pathExists(targetPath);
2493
+ if (!exists) {
2494
+ return {
2495
+ action: "missing",
2496
+ path: targetPath
2497
+ };
2498
+ }
2499
+
2500
+ if (!dryRun) {
2501
+ await rm(targetPath, { recursive: true, force: true });
2502
+ }
2503
+
2504
+ return {
2505
+ action: dryRun ? "would-remove" : "removed",
2506
+ path: targetPath
2507
+ };
2508
+ }
2509
+
2510
+ async function applyPostmanMcpForPlatform({
2511
+ platform,
2512
+ mcpScope,
2513
+ apiKey,
2514
+ apiKeyEnvVar,
2515
+ mcpUrl,
2516
+ stitchApiKey,
2517
+ stitchMcpUrl,
2518
+ includeStitchMcp = false,
2519
+ dryRun = false,
2520
+ cwd = process.cwd()
2521
+ }) {
2522
+ const workspaceRoot = findWorkspaceRoot(cwd);
2523
+ const warnings = [];
2524
+
2525
+ if (platform === "antigravity") {
2526
+ const settingsPath =
2527
+ mcpScope === "global"
2528
+ ? path.join(os.homedir(), ".gemini", "settings.json")
2529
+ : path.join(workspaceRoot, ".gemini", "settings.json");
2530
+ const result = await upsertJsonObjectFile({
2531
+ targetPath: settingsPath,
2532
+ updater: (existing) => {
2533
+ const next = { ...existing };
2534
+ const mcpServers =
2535
+ next.mcpServers && typeof next.mcpServers === "object" && !Array.isArray(next.mcpServers)
2536
+ ? { ...next.mcpServers }
2537
+ : {};
2538
+ mcpServers[POSTMAN_SKILL_ID] = buildGeminiPostmanServer({
2539
+ apiKey,
2540
+ apiKeyEnvVar,
2541
+ mcpUrl
2542
+ });
2543
+ if (includeStitchMcp) {
2544
+ mcpServers[STITCH_MCP_SERVER_ID] = buildGeminiStitchServer({
2545
+ apiKey: stitchApiKey,
2546
+ mcpUrl: stitchMcpUrl
2547
+ });
2548
+ }
2549
+ next.mcpServers = mcpServers;
2550
+ return next;
2551
+ },
2552
+ dryRun
2553
+ });
2554
+ return {
2555
+ kind: "gemini-settings",
2556
+ scope: mcpScope,
2557
+ path: settingsPath,
2558
+ action: result.action,
2559
+ warnings: [...warnings, ...result.warnings]
2560
+ };
2561
+ }
2562
+
2563
+ if (platform === "copilot") {
2564
+ const configPath =
2565
+ mcpScope === "global"
2566
+ ? path.join(os.homedir(), ".copilot", "mcp-config.json")
2567
+ : path.join(workspaceRoot, ".vscode", "mcp.json");
2568
+ const result = await upsertJsonObjectFile({
2569
+ targetPath: configPath,
2570
+ updater: (existing) => {
2571
+ const next = { ...existing };
2572
+ const servers =
2573
+ next.servers && typeof next.servers === "object" && !Array.isArray(next.servers)
2574
+ ? { ...next.servers }
2575
+ : {};
2576
+ servers[POSTMAN_SKILL_ID] = buildVsCodePostmanServer({
2577
+ apiKey,
2578
+ apiKeyEnvVar,
2579
+ mcpUrl
2580
+ });
2581
+ next.servers = servers;
2582
+ return next;
2583
+ },
2584
+ dryRun
2585
+ });
2586
+ return {
2587
+ kind: mcpScope === "global" ? "copilot-cli-mcp" : "vscode-mcp",
2588
+ scope: mcpScope,
2589
+ path: configPath,
2590
+ action: result.action,
2591
+ warnings: [...warnings, ...result.warnings]
2592
+ };
2593
+ }
2594
+
2595
+ if (platform === "codex") {
2596
+ if (mcpScope === "project") {
2597
+ const vscodePath = path.join(workspaceRoot, ".vscode", "mcp.json");
2598
+ const result = await upsertJsonObjectFile({
2599
+ targetPath: vscodePath,
2600
+ updater: (existing) => {
2601
+ const next = { ...existing };
2602
+ const servers =
2603
+ next.servers && typeof next.servers === "object" && !Array.isArray(next.servers)
2604
+ ? { ...next.servers }
2605
+ : {};
2606
+ servers[POSTMAN_SKILL_ID] = buildVsCodePostmanServer({
2607
+ apiKey,
2608
+ apiKeyEnvVar,
2609
+ mcpUrl
2610
+ });
2611
+ next.servers = servers;
2612
+ return next;
2613
+ },
2614
+ dryRun
2615
+ });
2616
+ return {
2617
+ kind: "vscode-mcp",
2618
+ scope: mcpScope,
2619
+ path: vscodePath,
2620
+ action: result.action,
2621
+ warnings: [...warnings, ...result.warnings]
2622
+ };
2623
+ }
2624
+
2625
+ const codexConfigPath = path.join(os.homedir(), ".codex", "config.toml");
2626
+ if (dryRun) {
2627
+ return {
2628
+ kind: "codex-cli",
2629
+ scope: mcpScope,
2630
+ path: codexConfigPath,
2631
+ action: "would-patch",
2632
+ warnings
2633
+ };
2634
+ }
2635
+
2636
+ try {
2637
+ await execFile("codex", ["mcp", "remove", POSTMAN_SKILL_ID], { cwd });
2638
+ } catch {
2639
+ // Best effort. Add will still run and becomes source of truth.
2640
+ }
2641
+
2642
+ try {
2643
+ await execFile(
2644
+ "codex",
2645
+ [
2646
+ "mcp",
2647
+ "add",
2648
+ POSTMAN_SKILL_ID,
2649
+ "--url",
2650
+ mcpUrl,
2651
+ "--bearer-token-env-var",
2652
+ apiKeyEnvVar || POSTMAN_API_KEY_ENV_VAR
2653
+ ],
2654
+ { cwd }
2655
+ );
2656
+ } catch (error) {
2657
+ warnings.push(
2658
+ `Failed to register Postman MCP via Codex CLI. Ensure 'codex' is installed and rerun. (${error.message})`
2659
+ );
2660
+ return {
2661
+ kind: "codex-cli",
2662
+ scope: mcpScope,
2663
+ path: codexConfigPath,
2664
+ action: "failed",
2665
+ warnings
2666
+ };
2667
+ }
2668
+
2669
+ if (apiKey) {
2670
+ warnings.push(
2671
+ "Codex global MCP uses bearer token env vars. Inline apiKey in cbx_config.json is not directly used by Codex."
2672
+ );
2673
+ }
2674
+
2675
+ return {
2676
+ kind: "codex-cli",
2677
+ scope: mcpScope,
2678
+ path: codexConfigPath,
2679
+ action: "patched",
2680
+ warnings
2681
+ };
2682
+ }
2683
+
2684
+ return {
2685
+ kind: "unknown",
2686
+ scope: mcpScope,
2687
+ path: null,
2688
+ action: "skipped",
2689
+ warnings: [`Unsupported platform '${platform}' for Postman MCP installation.`]
2690
+ };
2691
+ }
2692
+
2294
2693
  async function ensureGitIgnoreEntry({
2295
2694
  filePath,
2296
2695
  entry,
@@ -2323,13 +2722,15 @@ async function ensureGitIgnoreEntry({
2323
2722
  }
2324
2723
 
2325
2724
  async function resolvePostmanInstallSelection({
2725
+ platform,
2326
2726
  scope,
2327
2727
  options,
2328
2728
  cwd = process.cwd()
2329
2729
  }) {
2330
2730
  const hasApiKeyOption = options.postmanApiKey !== undefined;
2331
2731
  const hasWorkspaceOption = options.postmanWorkspaceId !== undefined;
2332
- const enabled = Boolean(options.postman) || hasApiKeyOption || hasWorkspaceOption;
2732
+ const hasStitchApiKeyOption = options.stitchApiKey !== undefined;
2733
+ const enabled = Boolean(options.postman) || hasApiKeyOption || hasWorkspaceOption || hasStitchApiKeyOption;
2333
2734
  if (!enabled) return { enabled: false };
2334
2735
 
2335
2736
  const explicitApiKey = hasApiKeyOption ? String(options.postmanApiKey || "").trim() : "";
@@ -2338,7 +2739,14 @@ async function resolvePostmanInstallSelection({
2338
2739
  let defaultWorkspaceId = hasWorkspaceOption
2339
2740
  ? normalizePostmanWorkspaceId(options.postmanWorkspaceId)
2340
2741
  : null;
2742
+ const requestedMcpScope = options.mcpScope
2743
+ ? normalizeMcpScope(options.mcpScope, normalizeMcpScope(scope, "project"))
2744
+ : null;
2745
+ let mcpScope = requestedMcpScope || normalizeMcpScope(scope, "project");
2341
2746
  const warnings = [];
2747
+ const stitchEnabled = platform === "antigravity";
2748
+ let stitchApiKey = hasStitchApiKeyOption ? normalizePostmanApiKey(options.stitchApiKey) : null;
2749
+ const envStitchApiKey = normalizePostmanApiKey(process.env[STITCH_API_KEY_ENV_VAR]);
2342
2750
 
2343
2751
  const canPrompt = !options.yes && process.stdin.isTTY;
2344
2752
  if (canPrompt && !hasApiKeyOption && !apiKey && !envApiKey) {
@@ -2361,34 +2769,91 @@ async function resolvePostmanInstallSelection({
2361
2769
  defaultWorkspaceId = normalizePostmanWorkspaceId(promptedWorkspaceId);
2362
2770
  }
2363
2771
 
2772
+ if (canPrompt && stitchEnabled && !hasStitchApiKeyOption && !stitchApiKey && !envStitchApiKey) {
2773
+ const promptedStitchApiKey = String(
2774
+ await input({
2775
+ message: `Google Stitch API key (optional, leave blank to keep ${STITCH_API_KEY_ENV_VAR} env mode):`,
2776
+ default: ""
2777
+ })
2778
+ ).trim();
2779
+ if (promptedStitchApiKey) {
2780
+ stitchApiKey = promptedStitchApiKey;
2781
+ }
2782
+ }
2783
+
2784
+ if (canPrompt && !requestedMcpScope) {
2785
+ mcpScope = await select({
2786
+ message: "Install MCP config in workspace or global scope?",
2787
+ choices: [
2788
+ { name: scope === "global" ? "Global (recommended)" : "Global", value: "global" },
2789
+ { name: scope === "project" ? "Workspace (recommended)" : "Workspace", value: "project" }
2790
+ ],
2791
+ default: mcpScope
2792
+ });
2793
+ }
2794
+
2364
2795
  const apiKeySource = getPostmanApiKeySource({ apiKey, envApiKey });
2365
2796
  if (apiKeySource === "unset") {
2366
2797
  warnings.push(POSTMAN_API_KEY_MISSING_WARNING);
2367
2798
  }
2368
2799
 
2369
- const settingsPath = resolvePostmanSettingsPath({ scope, cwd });
2370
- const settings = {
2371
- apiKey: apiKey || null,
2372
- apiKeyEnvVar: POSTMAN_API_KEY_ENV_VAR,
2373
- apiKeySource,
2374
- defaultWorkspaceId: defaultWorkspaceId ?? null,
2375
- mcpUrl: POSTMAN_MCP_URL,
2800
+ const stitchApiKeySource = stitchEnabled
2801
+ ? getStitchApiKeySource({ apiKey: stitchApiKey, envApiKey: envStitchApiKey })
2802
+ : null;
2803
+ if (stitchEnabled && stitchApiKeySource === "unset") {
2804
+ warnings.push(STITCH_API_KEY_MISSING_WARNING);
2805
+ }
2806
+ if (!stitchEnabled && hasStitchApiKeyOption) {
2807
+ warnings.push("--stitch-api-key is only used for --platform antigravity.");
2808
+ }
2809
+
2810
+ const cbxConfigPath = resolveCbxConfigPath({ scope: mcpScope, cwd });
2811
+ const legacySettingsPath = resolveLegacyPostmanSettingsPath({ scope: mcpScope, cwd });
2812
+ const cbxConfig = {
2813
+ schemaVersion: 1,
2376
2814
  generatedBy: "cbx workflows install --postman",
2377
- generatedAt: new Date().toISOString()
2815
+ generatedAt: new Date().toISOString(),
2816
+ mcp: {
2817
+ scope: mcpScope,
2818
+ server: POSTMAN_SKILL_ID,
2819
+ platform
2820
+ },
2821
+ postman: {
2822
+ apiKey: apiKey || null,
2823
+ apiKeyEnvVar: POSTMAN_API_KEY_ENV_VAR,
2824
+ apiKeySource,
2825
+ defaultWorkspaceId: defaultWorkspaceId ?? null,
2826
+ mcpUrl: POSTMAN_MCP_URL
2827
+ }
2378
2828
  };
2829
+ if (stitchEnabled) {
2830
+ cbxConfig.stitch = {
2831
+ server: STITCH_MCP_SERVER_ID,
2832
+ apiKey: stitchApiKey || null,
2833
+ apiKeyEnvVar: STITCH_API_KEY_ENV_VAR,
2834
+ apiKeySource: stitchApiKeySource,
2835
+ mcpUrl: STITCH_MCP_URL
2836
+ };
2837
+ }
2379
2838
 
2380
2839
  return {
2381
2840
  enabled: true,
2382
2841
  apiKey,
2383
2842
  apiKeySource,
2843
+ stitchEnabled,
2844
+ stitchApiKey,
2845
+ stitchApiKeySource,
2384
2846
  defaultWorkspaceId: defaultWorkspaceId ?? null,
2847
+ mcpScope,
2385
2848
  warnings,
2386
- settings,
2387
- settingsPath
2849
+ cbxConfig,
2850
+ cbxConfigPath,
2851
+ legacySettingsPath
2388
2852
  };
2389
2853
  }
2390
2854
 
2391
2855
  async function configurePostmanInstallArtifacts({
2856
+ platform,
2392
2857
  scope,
2393
2858
  profilePaths,
2394
2859
  postmanSelection,
@@ -2398,34 +2863,55 @@ async function configurePostmanInstallArtifacts({
2398
2863
  }) {
2399
2864
  if (!postmanSelection?.enabled) return null;
2400
2865
 
2401
- let warnings = postmanSelection.warnings.filter((warning) => warning !== POSTMAN_API_KEY_MISSING_WARNING);
2402
- const settingsContent = `${JSON.stringify(postmanSelection.settings, null, 2)}\n`;
2403
- const settingsResult = await writeTextFile({
2404
- targetPath: postmanSelection.settingsPath,
2405
- content: settingsContent,
2866
+ let warnings = postmanSelection.warnings.filter(
2867
+ (warning) => warning !== POSTMAN_API_KEY_MISSING_WARNING && warning !== STITCH_API_KEY_MISSING_WARNING
2868
+ );
2869
+ const cbxConfigContent = `${JSON.stringify(postmanSelection.cbxConfig, null, 2)}\n`;
2870
+ const cbxConfigResult = await writeTextFile({
2871
+ targetPath: postmanSelection.cbxConfigPath,
2872
+ content: cbxConfigContent,
2406
2873
  overwrite,
2407
2874
  dryRun
2408
2875
  });
2409
2876
 
2410
- let effectiveApiKey = normalizePostmanApiKey(postmanSelection.settings.apiKey);
2877
+ let effectiveApiKey = normalizePostmanApiKey(postmanSelection.cbxConfig?.postman?.apiKey);
2878
+ let effectiveApiKeyEnvVar = String(
2879
+ postmanSelection.cbxConfig?.postman?.apiKeyEnvVar || POSTMAN_API_KEY_ENV_VAR
2880
+ ).trim();
2411
2881
  let effectiveDefaultWorkspaceId = postmanSelection.defaultWorkspaceId ?? null;
2412
- let effectiveMcpUrl = postmanSelection.settings.mcpUrl || POSTMAN_MCP_URL;
2413
-
2414
- if (settingsResult.action === "skipped" || settingsResult.action === "would-skip") {
2415
- try {
2416
- const existingSettingsRaw = await readFile(postmanSelection.settingsPath, "utf8");
2417
- const existingSettings = JSON.parse(existingSettingsRaw);
2418
- effectiveApiKey = normalizePostmanApiKey(existingSettings?.apiKey);
2419
- effectiveDefaultWorkspaceId = normalizePostmanWorkspaceId(existingSettings?.defaultWorkspaceId);
2420
- const existingMcpUrl = String(existingSettings?.mcpUrl || "").trim();
2421
- if (existingMcpUrl) {
2422
- effectiveMcpUrl = existingMcpUrl;
2423
- }
2424
- } catch {
2882
+ let effectiveMcpUrl = postmanSelection.cbxConfig?.postman?.mcpUrl || POSTMAN_MCP_URL;
2883
+ const shouldInstallStitch = Boolean(postmanSelection.stitchEnabled);
2884
+ let effectiveStitchApiKey = shouldInstallStitch
2885
+ ? normalizePostmanApiKey(postmanSelection.cbxConfig?.stitch?.apiKey)
2886
+ : null;
2887
+ let effectiveStitchMcpUrl = shouldInstallStitch
2888
+ ? postmanSelection.cbxConfig?.stitch?.mcpUrl || STITCH_MCP_URL
2889
+ : STITCH_MCP_URL;
2890
+
2891
+ if (cbxConfigResult.action === "skipped" || cbxConfigResult.action === "would-skip") {
2892
+ const existingCbxConfig = await readJsonFileIfExists(postmanSelection.cbxConfigPath);
2893
+ const existingLegacySettings = await readJsonFileIfExists(postmanSelection.legacySettingsPath);
2894
+ const storedPostmanConfig =
2895
+ parseStoredPostmanConfig(existingCbxConfig.value) || parseStoredPostmanConfig(existingLegacySettings.value);
2896
+ const storedStitchConfig =
2897
+ shouldInstallStitch &&
2898
+ (parseStoredStitchConfig(existingCbxConfig.value) || parseStoredStitchConfig(existingLegacySettings.value));
2899
+
2900
+ if (storedPostmanConfig) {
2901
+ effectiveApiKey = storedPostmanConfig.apiKey;
2902
+ effectiveApiKeyEnvVar = storedPostmanConfig.apiKeyEnvVar || POSTMAN_API_KEY_ENV_VAR;
2903
+ effectiveDefaultWorkspaceId = storedPostmanConfig.defaultWorkspaceId;
2904
+ effectiveMcpUrl = storedPostmanConfig.mcpUrl || POSTMAN_MCP_URL;
2905
+ } else {
2425
2906
  warnings.push(
2426
- `Existing ${POSTMAN_SETTINGS_FILENAME} could not be parsed. Using install-time Postman values for MCP config.`
2907
+ `Existing ${CBX_CONFIG_FILENAME} (or legacy ${POSTMAN_SETTINGS_FILENAME}) could not be parsed. Using install-time Postman values for MCP config.`
2427
2908
  );
2428
2909
  }
2910
+
2911
+ if (storedStitchConfig) {
2912
+ effectiveStitchApiKey = storedStitchConfig.apiKey;
2913
+ effectiveStitchMcpUrl = storedStitchConfig.mcpUrl || STITCH_MCP_URL;
2914
+ }
2429
2915
  }
2430
2916
 
2431
2917
  const envApiKey = normalizePostmanApiKey(process.env[POSTMAN_API_KEY_ENV_VAR]);
@@ -2436,52 +2922,112 @@ async function configurePostmanInstallArtifacts({
2436
2922
  if (effectiveApiKeySource === "unset") {
2437
2923
  warnings.push(POSTMAN_API_KEY_MISSING_WARNING);
2438
2924
  }
2925
+ const envStitchApiKey = normalizePostmanApiKey(process.env[STITCH_API_KEY_ENV_VAR]);
2926
+ const effectiveStitchApiKeySource = shouldInstallStitch
2927
+ ? getStitchApiKeySource({
2928
+ apiKey: effectiveStitchApiKey,
2929
+ envApiKey: envStitchApiKey
2930
+ })
2931
+ : null;
2932
+ if (shouldInstallStitch && effectiveStitchApiKeySource === "unset") {
2933
+ warnings.push(STITCH_API_KEY_MISSING_WARNING);
2934
+ }
2439
2935
 
2440
- let gitIgnoreResult = null;
2441
- if (scope === "project") {
2936
+ const gitIgnoreResults = [];
2937
+ if (postmanSelection.mcpScope === "project") {
2442
2938
  const workspaceRoot = findWorkspaceRoot(cwd);
2443
2939
  const gitIgnorePath = path.join(workspaceRoot, ".gitignore");
2444
- gitIgnoreResult = await ensureGitIgnoreEntry({
2940
+ const configIgnore = await ensureGitIgnoreEntry({
2445
2941
  filePath: gitIgnorePath,
2446
- entry: POSTMAN_SETTINGS_FILENAME,
2942
+ entry: CBX_CONFIG_FILENAME,
2447
2943
  dryRun
2448
2944
  });
2945
+ gitIgnoreResults.push(configIgnore);
2946
+
2947
+ const mcpIgnore = await ensureGitIgnoreEntry({
2948
+ filePath: gitIgnorePath,
2949
+ entry: ".cbx/mcp/",
2950
+ dryRun
2951
+ });
2952
+ gitIgnoreResults.push(mcpIgnore);
2449
2953
  }
2450
2954
 
2451
- const postmanSkillDir = path.join(profilePaths.skillsDir, POSTMAN_SKILL_ID);
2452
- const postmanMcpPath = path.join(postmanSkillDir, "mcp.json");
2453
- let mcpResult = null;
2454
- const postmanSkillExists = await pathExists(postmanSkillDir);
2455
- if (!dryRun && !postmanSkillExists) {
2456
- mcpResult = {
2457
- action: "missing-postman-skill",
2458
- path: postmanMcpPath
2459
- };
2460
- } else {
2461
- const mcpConfigContent = `${JSON.stringify(
2462
- buildPostmanMcpConfig({
2463
- apiKey: effectiveApiKey,
2464
- mcpUrl: effectiveMcpUrl
2955
+ const mcpDefinitionPath = resolvePostmanMcpDefinitionPath({
2956
+ platform,
2957
+ scope: postmanSelection.mcpScope,
2958
+ cwd
2959
+ });
2960
+ const mcpDefinitionContent = `${JSON.stringify(
2961
+ buildPostmanMcpDefinition({
2962
+ apiKey: effectiveApiKey,
2963
+ apiKeyEnvVar: effectiveApiKeyEnvVar,
2964
+ mcpUrl: effectiveMcpUrl
2965
+ }),
2966
+ null,
2967
+ 2
2968
+ )}\n`;
2969
+ const mcpDefinitionResult = await writeGeneratedArtifact({
2970
+ destination: mcpDefinitionPath,
2971
+ content: mcpDefinitionContent,
2972
+ dryRun
2973
+ });
2974
+ let stitchMcpDefinitionPath = null;
2975
+ let stitchMcpDefinitionResult = null;
2976
+ if (shouldInstallStitch) {
2977
+ stitchMcpDefinitionPath = resolveStitchMcpDefinitionPath({
2978
+ scope: postmanSelection.mcpScope,
2979
+ cwd
2980
+ });
2981
+ const stitchMcpDefinitionContent = `${JSON.stringify(
2982
+ buildStitchMcpDefinition({
2983
+ apiKey: effectiveStitchApiKey,
2984
+ mcpUrl: effectiveStitchMcpUrl
2465
2985
  }),
2466
2986
  null,
2467
2987
  2
2468
2988
  )}\n`;
2469
- mcpResult = await writeGeneratedArtifact({
2470
- destination: postmanMcpPath,
2471
- content: mcpConfigContent,
2989
+ stitchMcpDefinitionResult = await writeGeneratedArtifact({
2990
+ destination: stitchMcpDefinitionPath,
2991
+ content: stitchMcpDefinitionContent,
2472
2992
  dryRun
2473
2993
  });
2474
2994
  }
2475
2995
 
2996
+ const mcpRuntimeResult = await applyPostmanMcpForPlatform({
2997
+ platform,
2998
+ mcpScope: postmanSelection.mcpScope,
2999
+ apiKey: effectiveApiKey,
3000
+ apiKeyEnvVar: effectiveApiKeyEnvVar,
3001
+ mcpUrl: effectiveMcpUrl,
3002
+ stitchApiKey: effectiveStitchApiKey,
3003
+ stitchMcpUrl: effectiveStitchMcpUrl,
3004
+ includeStitchMcp: shouldInstallStitch,
3005
+ dryRun,
3006
+ cwd
3007
+ });
3008
+ warnings.push(...(mcpRuntimeResult.warnings || []));
3009
+
3010
+ const legacySkillMcpCleanup = await removeGeneratedArtifactIfExists({
3011
+ targetPath: path.join(profilePaths.skillsDir, POSTMAN_SKILL_ID, "mcp.json"),
3012
+ dryRun
3013
+ });
3014
+
2476
3015
  return {
2477
3016
  enabled: true,
3017
+ mcpScope: postmanSelection.mcpScope,
2478
3018
  apiKeySource: effectiveApiKeySource,
3019
+ stitchApiKeySource: effectiveStitchApiKeySource,
2479
3020
  defaultWorkspaceId: effectiveDefaultWorkspaceId,
2480
3021
  warnings,
2481
- settingsPath: postmanSelection.settingsPath,
2482
- settingsResult,
2483
- gitIgnoreResult,
2484
- mcpResult
3022
+ cbxConfigPath: postmanSelection.cbxConfigPath,
3023
+ cbxConfigResult,
3024
+ gitIgnoreResults,
3025
+ mcpDefinitionPath,
3026
+ mcpDefinitionResult,
3027
+ stitchMcpDefinitionPath,
3028
+ stitchMcpDefinitionResult,
3029
+ mcpRuntimeResult,
3030
+ legacySkillMcpCleanup
2485
3031
  };
2486
3032
  }
2487
3033
 
@@ -3051,18 +3597,35 @@ function printPostmanSetupSummary({ postmanSetup }) {
3051
3597
  if (!postmanSetup?.enabled) return;
3052
3598
 
3053
3599
  console.log("\nPostman setup:");
3054
- console.log(`- Settings file: ${postmanSetup.settingsResult.action} (${postmanSetup.settingsPath})`);
3055
- console.log(`- API key source: ${postmanSetup.apiKeySource}`);
3600
+ console.log(`- MCP scope: ${postmanSetup.mcpScope}`);
3601
+ console.log(`- Config file: ${postmanSetup.cbxConfigResult.action} (${postmanSetup.cbxConfigPath})`);
3602
+ console.log(`- Postman API key source: ${postmanSetup.apiKeySource}`);
3603
+ if (postmanSetup.stitchApiKeySource) {
3604
+ console.log(`- Stitch API key source: ${postmanSetup.stitchApiKeySource}`);
3605
+ }
3056
3606
  console.log(
3057
3607
  `- Default workspace ID: ${postmanSetup.defaultWorkspaceId === null ? "null" : postmanSetup.defaultWorkspaceId}`
3058
3608
  );
3059
- if (postmanSetup.gitIgnoreResult) {
3609
+ for (const ignoreResult of postmanSetup.gitIgnoreResults || []) {
3610
+ console.log(`- .gitignore (${ignoreResult.filePath}): ${ignoreResult.action}`);
3611
+ }
3612
+ console.log(
3613
+ `- Managed MCP definition (${postmanSetup.mcpDefinitionPath}): ${postmanSetup.mcpDefinitionResult.action}`
3614
+ );
3615
+ if (postmanSetup.stitchMcpDefinitionPath && postmanSetup.stitchMcpDefinitionResult) {
3060
3616
  console.log(
3061
- `- .gitignore (${postmanSetup.gitIgnoreResult.filePath}): ${postmanSetup.gitIgnoreResult.action}`
3617
+ `- Managed Stitch MCP definition (${postmanSetup.stitchMcpDefinitionPath}): ${postmanSetup.stitchMcpDefinitionResult.action}`
3062
3618
  );
3063
3619
  }
3064
- if (postmanSetup.mcpResult) {
3065
- console.log(`- Postman MCP config (${postmanSetup.mcpResult.path}): ${postmanSetup.mcpResult.action}`);
3620
+ if (postmanSetup.mcpRuntimeResult) {
3621
+ console.log(
3622
+ `- Platform MCP target (${postmanSetup.mcpRuntimeResult.path || "n/a"}): ${postmanSetup.mcpRuntimeResult.action}`
3623
+ );
3624
+ }
3625
+ if (postmanSetup.legacySkillMcpCleanup) {
3626
+ console.log(
3627
+ `- Legacy skill mcp.json cleanup (${postmanSetup.legacySkillMcpCleanup.path}): ${postmanSetup.legacySkillMcpCleanup.action}`
3628
+ );
3066
3629
  }
3067
3630
 
3068
3631
  if (postmanSetup.warnings.length > 0) {
@@ -3383,16 +3946,24 @@ function withInstallOptions(command) {
3383
3946
  .option("--overwrite", "overwrite existing files")
3384
3947
  .option(
3385
3948
  "--postman",
3386
- "optional: install Postman skill and generate postman_setting.json"
3949
+ "optional: install Postman skill and generate cbx_config.json"
3387
3950
  )
3388
3951
  .option(
3389
3952
  "--postman-api-key <key>",
3390
- "optional: set Postman API key inline for generated postman_setting.json and installed Postman MCP config"
3953
+ "optional: set Postman API key inline for generated cbx_config.json and installed Postman MCP config"
3391
3954
  )
3392
3955
  .option(
3393
3956
  "--postman-workspace-id <id|null>",
3394
3957
  "optional: set default Postman workspace ID (use 'null' for no default)"
3395
3958
  )
3959
+ .option(
3960
+ "--stitch-api-key <key>",
3961
+ "optional: Antigravity only. Set Google Stitch API key inline for StitchMCP config"
3962
+ )
3963
+ .option(
3964
+ "--mcp-scope <scope>",
3965
+ "optional: MCP config scope for --postman (project|workspace|global|user)"
3966
+ )
3396
3967
  .option(
3397
3968
  "--terminal-integration",
3398
3969
  "Antigravity only: enable terminal verification integration (prompts for verifier when interactive)"
@@ -3557,6 +4128,7 @@ async function runWorkflowInstall(options) {
3557
4128
  options
3558
4129
  });
3559
4130
  const postmanSelection = await resolvePostmanInstallSelection({
4131
+ platform,
3560
4132
  scope,
3561
4133
  options,
3562
4134
  cwd
@@ -3615,6 +4187,7 @@ async function runWorkflowInstall(options) {
3615
4187
  cwd
3616
4188
  });
3617
4189
  const postmanSetupResult = await configurePostmanInstallArtifacts({
4190
+ platform,
3618
4191
  scope,
3619
4192
  profilePaths: installResult.profilePaths,
3620
4193
  postmanSelection,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cubis/foundry",
3
- "version": "0.3.21",
3
+ "version": "0.3.23",
4
4
  "description": "Cubis Foundry CLI for workflow-first AI agent environments",
5
5
  "type": "module",
6
6
  "bin": {
@@ -11,7 +11,8 @@
11
11
  "files": [
12
12
  "bin",
13
13
  "Ai Agent Workflow",
14
- "README.md"
14
+ "README.md",
15
+ "CHANGELOG.md"
15
16
  ],
16
17
  "scripts": {
17
18
  "start": "node bin/cubis.js",