poe-code 3.0.68 → 3.0.70

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 (42) hide show
  1. package/README.md +31 -19
  2. package/dist/cli/bootstrap.js +2 -2
  3. package/dist/cli/bootstrap.js.map +1 -1
  4. package/dist/cli/commands/configure-payload.js +3 -1
  5. package/dist/cli/commands/configure-payload.js.map +1 -1
  6. package/dist/cli/commands/login.js +8 -28
  7. package/dist/cli/commands/login.js.map +1 -1
  8. package/dist/cli/commands/spawn.js +86 -3
  9. package/dist/cli/commands/spawn.js.map +1 -1
  10. package/dist/cli/commands/usage.js +5 -2
  11. package/dist/cli/commands/usage.js.map +1 -1
  12. package/dist/cli/container.js +10 -1
  13. package/dist/cli/container.js.map +1 -1
  14. package/dist/cli/errors.d.ts +5 -0
  15. package/dist/cli/errors.js +14 -0
  16. package/dist/cli/errors.js.map +1 -1
  17. package/dist/cli/logger.js +7 -0
  18. package/dist/cli/logger.js.map +1 -1
  19. package/dist/cli/options.d.ts +5 -0
  20. package/dist/cli/options.js +102 -12
  21. package/dist/cli/options.js.map +1 -1
  22. package/dist/index.js +446 -129
  23. package/dist/index.js.map +4 -4
  24. package/dist/providers/claude-code.js +85 -1
  25. package/dist/providers/claude-code.js.map +4 -4
  26. package/dist/providers/codex.js +85 -1
  27. package/dist/providers/codex.js.map +4 -4
  28. package/dist/providers/kimi.js +85 -1
  29. package/dist/providers/kimi.js.map +4 -4
  30. package/dist/providers/opencode.js +85 -1
  31. package/dist/providers/opencode.js.map +4 -4
  32. package/dist/providers/spawn-options.d.ts +2 -1
  33. package/dist/sdk/container.js +2 -1
  34. package/dist/sdk/container.js.map +1 -1
  35. package/dist/sdk/spawn-core.d.ts +3 -1
  36. package/dist/sdk/spawn-core.js +1 -0
  37. package/dist/sdk/spawn-core.js.map +1 -1
  38. package/dist/sdk/spawn.d.ts +3 -0
  39. package/dist/sdk/spawn.js +22 -2
  40. package/dist/sdk/spawn.js.map +1 -1
  41. package/dist/sdk/types.d.ts +3 -1
  42. package/package.json +2 -1
package/dist/index.js CHANGED
@@ -2192,11 +2192,64 @@ var init_src2 = __esm({
2192
2192
  }
2193
2193
  });
2194
2194
 
2195
+ // packages/agent-spawn/src/configs/mcp.ts
2196
+ function toJsonMcpServers(servers) {
2197
+ const out = {};
2198
+ for (const [name, server] of Object.entries(servers)) {
2199
+ const mapped = { command: server.command };
2200
+ if (server.args && server.args.length > 0) {
2201
+ mapped.args = server.args;
2202
+ }
2203
+ if (server.env && Object.keys(server.env).length > 0) {
2204
+ mapped.env = server.env;
2205
+ }
2206
+ out[name] = mapped;
2207
+ }
2208
+ return out;
2209
+ }
2210
+ function toTomlString(value) {
2211
+ return JSON.stringify(value);
2212
+ }
2213
+ function toTomlArray(values) {
2214
+ const serialized = values.map((value) => toTomlString(value));
2215
+ return `[${serialized.join(", ")}]`;
2216
+ }
2217
+ function toTomlInlineTable(values) {
2218
+ const parts = [];
2219
+ for (const [key, value] of Object.entries(values)) {
2220
+ parts.push(`${JSON.stringify(key)}=${toTomlString(value)}`);
2221
+ }
2222
+ return `{${parts.join(", ")}}`;
2223
+ }
2224
+ function serializeJsonMcpArgs(servers) {
2225
+ return ["--mcp-config", JSON.stringify({ mcpServers: toJsonMcpServers(servers) })];
2226
+ }
2227
+ function serializeCodexMcpArgs(servers) {
2228
+ const args = [];
2229
+ for (const [name, server] of Object.entries(servers)) {
2230
+ const prefix = `mcp_servers.${name}`;
2231
+ args.push("-c", `${prefix}.command=${toTomlString(server.command)}`);
2232
+ if (server.args && server.args.length > 0) {
2233
+ args.push("-c", `${prefix}.args=${toTomlArray(server.args)}`);
2234
+ }
2235
+ if (server.env && Object.keys(server.env).length > 0) {
2236
+ args.push("-c", `${prefix}.env=${toTomlInlineTable(server.env)}`);
2237
+ }
2238
+ }
2239
+ return args;
2240
+ }
2241
+ var init_mcp = __esm({
2242
+ "packages/agent-spawn/src/configs/mcp.ts"() {
2243
+ "use strict";
2244
+ }
2245
+ });
2246
+
2195
2247
  // packages/agent-spawn/src/configs/claude-code.ts
2196
2248
  var claudeCodeSpawnConfig;
2197
2249
  var init_claude_code2 = __esm({
2198
2250
  "packages/agent-spawn/src/configs/claude-code.ts"() {
2199
2251
  "use strict";
2252
+ init_mcp();
2200
2253
  claudeCodeSpawnConfig = {
2201
2254
  kind: "cli",
2202
2255
  agentId: "claude-code",
@@ -2211,6 +2264,7 @@ var init_claude_code2 = __esm({
2211
2264
  "stream-json",
2212
2265
  "--verbose"
2213
2266
  ],
2267
+ mcpArgs: serializeJsonMcpArgs,
2214
2268
  modes: {
2215
2269
  yolo: ["--dangerously-skip-permissions"],
2216
2270
  edit: ["--permission-mode", "acceptEdits", "--allowedTools", "Bash,Read,Write,Edit,Glob,Grep,NotebookEdit"],
@@ -2233,6 +2287,7 @@ var codexSpawnConfig;
2233
2287
  var init_codex2 = __esm({
2234
2288
  "packages/agent-spawn/src/configs/codex.ts"() {
2235
2289
  "use strict";
2290
+ init_mcp();
2236
2291
  codexSpawnConfig = {
2237
2292
  kind: "cli",
2238
2293
  agentId: "codex",
@@ -2242,6 +2297,7 @@ var init_codex2 = __esm({
2242
2297
  modelFlag: "--model",
2243
2298
  modelStripProviderPrefix: true,
2244
2299
  defaultArgs: ["--skip-git-repo-check", "--json"],
2300
+ mcpArgs: serializeCodexMcpArgs,
2245
2301
  modes: {
2246
2302
  yolo: ["-s", "danger-full-access"],
2247
2303
  edit: ["-s", "workspace-write"],
@@ -2297,6 +2353,7 @@ var kimiSpawnConfig;
2297
2353
  var init_kimi2 = __esm({
2298
2354
  "packages/agent-spawn/src/configs/kimi.ts"() {
2299
2355
  "use strict";
2356
+ init_mcp();
2300
2357
  kimiSpawnConfig = {
2301
2358
  kind: "cli",
2302
2359
  agentId: "kimi",
@@ -2307,6 +2364,7 @@ var init_kimi2 = __esm({
2307
2364
  promptFlag: "-p",
2308
2365
  modelStripProviderPrefix: true,
2309
2366
  defaultArgs: ["--print", "--output-format", "stream-json"],
2367
+ mcpArgs: serializeJsonMcpArgs,
2310
2368
  modes: {
2311
2369
  yolo: ["--yolo"],
2312
2370
  edit: [],
@@ -2333,6 +2391,20 @@ function getSpawnConfig(input) {
2333
2391
  }
2334
2392
  return lookup2.get(resolvedId);
2335
2393
  }
2394
+ function supportsMcpAtSpawn(input) {
2395
+ const config2 = getSpawnConfig(input);
2396
+ return !!config2 && config2.kind === "cli" && typeof config2.mcpArgs === "function";
2397
+ }
2398
+ function listMcpSupportedAgents() {
2399
+ const supported = [];
2400
+ for (const config2 of allSpawnConfigs) {
2401
+ if (config2.kind !== "cli" || typeof config2.mcpArgs !== "function") {
2402
+ continue;
2403
+ }
2404
+ supported.push(config2.agentId);
2405
+ }
2406
+ return supported;
2407
+ }
2336
2408
  var allSpawnConfigs, lookup2;
2337
2409
  var init_configs = __esm({
2338
2410
  "packages/agent-spawn/src/configs/index.ts"() {
@@ -2377,6 +2449,35 @@ var init_resolve_config = __esm({
2377
2449
  }
2378
2450
  });
2379
2451
 
2452
+ // packages/agent-spawn/src/mcp-args.ts
2453
+ function hasMcpServers(servers) {
2454
+ if (!servers) {
2455
+ return false;
2456
+ }
2457
+ return Object.keys(servers).length > 0;
2458
+ }
2459
+ function getMcpArgs(config2, servers) {
2460
+ if (!hasMcpServers(servers)) {
2461
+ return [];
2462
+ }
2463
+ if (!config2.mcpArgs) {
2464
+ throw new Error(formatUnsupportedMcpSpawnMessage(config2.agentId));
2465
+ }
2466
+ return config2.mcpArgs(servers);
2467
+ }
2468
+ function formatUnsupportedMcpSpawnMessage(agentId) {
2469
+ const supported = listMcpSupportedAgents();
2470
+ const supportedText = supported.length > 0 ? supported.join(", ") : "(none)";
2471
+ return `Agent "${agentId}" does not support MCP servers at spawn time.
2472
+ Agents with spawn-time MCP support: ${supportedText}`;
2473
+ }
2474
+ var init_mcp_args = __esm({
2475
+ "packages/agent-spawn/src/mcp-args.ts"() {
2476
+ "use strict";
2477
+ init_configs();
2478
+ }
2479
+ });
2480
+
2380
2481
  // packages/agent-spawn/src/model-utils.ts
2381
2482
  function stripModelNamespace(model) {
2382
2483
  const slashIndex = model.indexOf("/");
@@ -2419,6 +2520,7 @@ function buildCliArgs(config2, options, stdinMode) {
2419
2520
  args.push(config2.modelFlag, model);
2420
2521
  }
2421
2522
  args.push(...config2.defaultArgs);
2523
+ args.push(...getMcpArgs(config2, options.mcpServers));
2422
2524
  args.push(...config2.modes[options.mode ?? "yolo"]);
2423
2525
  if (options.args && options.args.length > 0) {
2424
2526
  args.push(...options.args);
@@ -2480,6 +2582,7 @@ var init_spawn = __esm({
2480
2582
  "packages/agent-spawn/src/spawn.ts"() {
2481
2583
  "use strict";
2482
2584
  init_resolve_config();
2585
+ init_mcp_args();
2483
2586
  init_model_utils();
2484
2587
  }
2485
2588
  });
@@ -2516,6 +2619,7 @@ async function spawnInteractive(agentId, options) {
2516
2619
  args.push(spawnConfig.modelFlag, model);
2517
2620
  }
2518
2621
  args.push(...interactive.defaultArgs);
2622
+ args.push(...getMcpArgs(spawnConfig, options.mcpServers));
2519
2623
  const mode = options.mode ?? "yolo";
2520
2624
  args.push(...spawnConfig.modes[mode]);
2521
2625
  if (options.args && options.args.length > 0) {
@@ -2542,6 +2646,7 @@ var init_spawn_interactive = __esm({
2542
2646
  "packages/agent-spawn/src/spawn-interactive.ts"() {
2543
2647
  "use strict";
2544
2648
  init_resolve_config();
2649
+ init_mcp_args();
2545
2650
  init_model_utils();
2546
2651
  }
2547
2652
  });
@@ -3070,7 +3175,7 @@ var init_acp = __esm({
3070
3175
 
3071
3176
  // packages/design-system/src/prompts/index.ts
3072
3177
  import * as clack from "@clack/prompts";
3073
- import { isCancel, cancel, log as log2 } from "@clack/prompts";
3178
+ import { isCancel as isCancel2, cancel as cancel2, log as log2 } from "@clack/prompts";
3074
3179
  function stripAnsi3(value) {
3075
3180
  return value.replace(/\u001b\[[0-9;]*m/g, "");
3076
3181
  }
@@ -3877,6 +3982,7 @@ function spawnStreaming(options) {
3877
3982
  args.push(spawnConfig.modelFlag, model);
3878
3983
  }
3879
3984
  args.push(...spawnConfig.defaultArgs);
3985
+ args.push(...getMcpArgs(spawnConfig, options.mcpServers));
3880
3986
  const mode = options.mode ?? "yolo";
3881
3987
  args.push(...spawnConfig.modes[mode]);
3882
3988
  if (useStdin) {
@@ -3933,6 +4039,7 @@ var init_spawn2 = __esm({
3933
4039
  init_adapters();
3934
4040
  init_line_reader();
3935
4041
  init_resolve_config();
4042
+ init_mcp_args();
3936
4043
  init_model_utils();
3937
4044
  }
3938
4045
  });
@@ -4136,6 +4243,7 @@ async function spawnCore(container, service, options, flags = { dryRun: false, v
4136
4243
  args: options.args,
4137
4244
  model: options.model,
4138
4245
  mode: options.mode,
4246
+ mcpServers: options.mcpServers,
4139
4247
  cwd: cwdOverride,
4140
4248
  useStdin: options.useStdin ?? false
4141
4249
  };
@@ -4488,6 +4596,40 @@ var init_prompts2 = __esm({
4488
4596
  function stripBracketedPaste(value) {
4489
4597
  return value.replace(/\x1b\[200~/g, "").replace(/\x1b\[201~/g, "").replace(/undefinedndefined$/, "").replace(/undefined$/, "").replace(/ndefined$/, "");
4490
4598
  }
4599
+ function isAlphanumeric(value) {
4600
+ for (let i = 0; i < value.length; i++) {
4601
+ const code = value.charCodeAt(i);
4602
+ const isDigit = code >= 48 && code <= 57;
4603
+ const isUpper = code >= 65 && code <= 90;
4604
+ const isLower = code >= 97 && code <= 122;
4605
+ if (!isDigit && !isUpper && !isLower) return false;
4606
+ }
4607
+ return value.length > 0;
4608
+ }
4609
+ function isAlphanumericWithSeparators(value) {
4610
+ for (let i = 0; i < value.length; i++) {
4611
+ const code = value.charCodeAt(i);
4612
+ const isDigit = code >= 48 && code <= 57;
4613
+ const isUpper = code >= 65 && code <= 90;
4614
+ const isLower = code >= 97 && code <= 122;
4615
+ const isHyphen = code === 45;
4616
+ const isUnderscore = code === 95;
4617
+ if (!isDigit && !isUpper && !isLower && !isHyphen && !isUnderscore)
4618
+ return false;
4619
+ }
4620
+ return value.length > 0;
4621
+ }
4622
+ function hasMinimumApiKeyLength(value) {
4623
+ return value.length >= MIN_API_KEY_LENGTH;
4624
+ }
4625
+ function isValidApiKeyFormat(key) {
4626
+ if (key.length === 0) return false;
4627
+ if (key.startsWith("sk-poe-")) {
4628
+ const hash2 = key.slice(7);
4629
+ return hasMinimumApiKeyLength(hash2) && isAlphanumeric(hash2);
4630
+ }
4631
+ return hasMinimumApiKeyLength(key) && isAlphanumericWithSeparators(key);
4632
+ }
4491
4633
  function createOptionResolvers(init) {
4492
4634
  const ensure = async (input) => {
4493
4635
  if (input.value != null) {
@@ -4511,29 +4653,77 @@ function createOptionResolvers(init) {
4511
4653
  }
4512
4654
  return trimmed;
4513
4655
  };
4656
+ const confirmKeyFormat = async (apiKey, assumeYes) => {
4657
+ if (isValidApiKeyFormat(apiKey)) return true;
4658
+ if (assumeYes) return false;
4659
+ return await init.confirm(
4660
+ "Key doesn't match expected API key format. Use it anyway?"
4661
+ );
4662
+ };
4514
4663
  const resolveApiKey = async (input) => {
4664
+ const assumeYes = input.assumeYes ?? false;
4665
+ const allowStored = input.allowStored ?? true;
4515
4666
  if (input.value != null) {
4516
- const apiKey2 = normalizeApiKey(input.value);
4667
+ const apiKey = normalizeApiKey(input.value);
4668
+ const accepted = await confirmKeyFormat(apiKey, assumeYes);
4669
+ if (!accepted) {
4670
+ throw new Error("API key rejected.");
4671
+ }
4517
4672
  if (!input.dryRun) {
4518
- await init.apiKeyStore.write(apiKey2);
4673
+ await init.apiKeyStore.write(apiKey);
4519
4674
  }
4520
- return apiKey2;
4675
+ return apiKey;
4521
4676
  }
4522
- const stored = await init.apiKeyStore.read();
4523
- if (stored) {
4524
- return normalizeApiKey(stored);
4677
+ const envValue = input.envValue;
4678
+ if (typeof envValue === "string" && envValue.trim().length > 0) {
4679
+ const useEnv = assumeYes || await init.confirm(
4680
+ "Use API key from POE_API_KEY environment variable?"
4681
+ );
4682
+ if (useEnv) {
4683
+ const apiKey = normalizeApiKey(envValue);
4684
+ const accepted = await confirmKeyFormat(apiKey, assumeYes);
4685
+ if (accepted) {
4686
+ if (!input.dryRun) {
4687
+ await init.apiKeyStore.write(apiKey);
4688
+ }
4689
+ return apiKey;
4690
+ }
4691
+ if (assumeYes) {
4692
+ throw new Error("API key rejected.");
4693
+ }
4694
+ }
4525
4695
  }
4526
- const descriptor = init.promptLibrary.loginApiKey();
4527
- const response = await init.prompts(descriptor);
4528
- const result = response[descriptor.name];
4529
- if (typeof result !== "string") {
4530
- throw new Error("POE API key is required.");
4696
+ if (allowStored) {
4697
+ const stored = await init.apiKeyStore.read();
4698
+ if (stored) {
4699
+ return normalizeApiKey(stored);
4700
+ }
4531
4701
  }
4532
- const apiKey = normalizeApiKey(result);
4533
- if (!input.dryRun) {
4534
- await init.apiKeyStore.write(apiKey);
4702
+ while (true) {
4703
+ const descriptor = init.promptLibrary.loginApiKey();
4704
+ const response = await init.prompts(descriptor);
4705
+ const result = response[descriptor.name];
4706
+ if (typeof result !== "string") {
4707
+ continue;
4708
+ }
4709
+ let apiKey;
4710
+ try {
4711
+ apiKey = normalizeApiKey(result);
4712
+ } catch (error2) {
4713
+ if (error2 instanceof Error && error2.message === "POE API key cannot be empty.") {
4714
+ continue;
4715
+ }
4716
+ throw error2;
4717
+ }
4718
+ const accepted = await confirmKeyFormat(apiKey, assumeYes);
4719
+ if (!accepted) {
4720
+ continue;
4721
+ }
4722
+ if (!input.dryRun) {
4723
+ await init.apiKeyStore.write(apiKey);
4724
+ }
4725
+ return apiKey;
4535
4726
  }
4536
- return apiKey;
4537
4727
  };
4538
4728
  const resolveModel2 = async ({
4539
4729
  value,
@@ -4593,9 +4783,78 @@ function createOptionResolvers(init) {
4593
4783
  normalizeApiKey
4594
4784
  };
4595
4785
  }
4786
+ var API_KEY_REFERENCE_LENGTH, API_KEY_MIN_LENGTH_RATIO, MIN_API_KEY_LENGTH;
4596
4787
  var init_options = __esm({
4597
4788
  "src/cli/options.ts"() {
4598
4789
  "use strict";
4790
+ API_KEY_REFERENCE_LENGTH = 43;
4791
+ API_KEY_MIN_LENGTH_RATIO = 0.8;
4792
+ MIN_API_KEY_LENGTH = Math.ceil(
4793
+ API_KEY_REFERENCE_LENGTH * API_KEY_MIN_LENGTH_RATIO
4794
+ );
4795
+ }
4796
+ });
4797
+
4798
+ // src/cli/errors.ts
4799
+ function isSilentError(error2) {
4800
+ if (error2 instanceof SilentError) {
4801
+ return true;
4802
+ }
4803
+ if (!(error2 instanceof Error)) {
4804
+ return false;
4805
+ }
4806
+ return error2.name === "SilentError" || error2.name === "OperationCancelledError";
4807
+ }
4808
+ var CliError, SilentError, ApiError, ValidationError, AuthenticationError, OperationCancelledError;
4809
+ var init_errors = __esm({
4810
+ "src/cli/errors.ts"() {
4811
+ "use strict";
4812
+ CliError = class extends Error {
4813
+ context;
4814
+ isUserError;
4815
+ constructor(message, context, options) {
4816
+ super(message);
4817
+ this.name = this.constructor.name;
4818
+ this.context = context;
4819
+ this.isUserError = options?.isUserError ?? false;
4820
+ if (Error.captureStackTrace) {
4821
+ Error.captureStackTrace(this, this.constructor);
4822
+ }
4823
+ }
4824
+ };
4825
+ SilentError = class extends CliError {
4826
+ constructor(message = "", options) {
4827
+ super(message, void 0, { isUserError: options?.isUserError ?? false });
4828
+ }
4829
+ };
4830
+ ApiError = class extends CliError {
4831
+ httpStatus;
4832
+ endpoint;
4833
+ constructor(message, options) {
4834
+ super(message, {
4835
+ ...options?.context,
4836
+ httpStatus: options?.httpStatus,
4837
+ apiEndpoint: options?.endpoint
4838
+ });
4839
+ this.httpStatus = options?.httpStatus;
4840
+ this.endpoint = options?.endpoint;
4841
+ }
4842
+ };
4843
+ ValidationError = class extends CliError {
4844
+ constructor(message, context) {
4845
+ super(message, context, { isUserError: true });
4846
+ }
4847
+ };
4848
+ AuthenticationError = class extends CliError {
4849
+ constructor(message, context) {
4850
+ super(message, context, { isUserError: true });
4851
+ }
4852
+ };
4853
+ OperationCancelledError = class extends SilentError {
4854
+ constructor(message = "Operation cancelled.") {
4855
+ super(message, { isUserError: true });
4856
+ }
4857
+ };
4599
4858
  }
4600
4859
  });
4601
4860
 
@@ -4681,6 +4940,9 @@ function createLoggerFactory(emitter, theme) {
4681
4940
  ${value}`, { symbol: symbol2 });
4682
4941
  },
4683
4942
  errorWithStack(error2, errorContext) {
4943
+ if (isSilentError(error2)) {
4944
+ return;
4945
+ }
4684
4946
  emit("error", formatMessage(error2.message));
4685
4947
  if (errorLogger) {
4686
4948
  const fullContext = {
@@ -4694,6 +4956,9 @@ function createLoggerFactory(emitter, theme) {
4694
4956
  }
4695
4957
  },
4696
4958
  logException(error2, operation, errorContext) {
4959
+ if (isSilentError(error2)) {
4960
+ return;
4961
+ }
4697
4962
  emit(
4698
4963
  "error",
4699
4964
  formatMessage(`Error during ${operation}: ${error2.message}`)
@@ -4794,6 +5059,7 @@ var init_logger2 = __esm({
4794
5059
  "src/cli/logger.ts"() {
4795
5060
  "use strict";
4796
5061
  init_src3();
5062
+ init_errors();
4797
5063
  }
4798
5064
  });
4799
5065
 
@@ -5056,7 +5322,9 @@ async function createConfigurePayload(init) {
5056
5322
  const { container, flags, options, context, adapter, logger: logger2 } = init;
5057
5323
  const apiKey = await container.options.resolveApiKey({
5058
5324
  value: options.apiKey,
5059
- dryRun: flags.dryRun
5325
+ envValue: container.env.getVariable("POE_API_KEY"),
5326
+ dryRun: flags.dryRun,
5327
+ assumeYes: flags.assumeYes
5060
5328
  });
5061
5329
  const payload = { env: context.env, apiKey };
5062
5330
  const modelPrompt = adapter.configurePrompts?.model;
@@ -5370,7 +5638,8 @@ function createSdkContainer(options) {
5370
5638
  filePath: environment.credentialsPath,
5371
5639
  apiKey: value
5372
5640
  })
5373
- }
5641
+ },
5642
+ confirm: async () => true
5374
5643
  });
5375
5644
  const registry2 = createServiceRegistry();
5376
5645
  const providers = getDefaultProviders().filter(
@@ -5466,7 +5735,8 @@ function spawn3(service, promptOrOptions, maybeOptions) {
5466
5735
  cwd: options.cwd,
5467
5736
  model: options.model,
5468
5737
  mode: options.mode,
5469
- args: options.args
5738
+ args: options.args,
5739
+ ...options.mcpServers ? { mcpServers: options.mcpServers } : {}
5470
5740
  });
5471
5741
  return {
5472
5742
  stdout: interactiveResult.stdout,
@@ -5484,6 +5754,7 @@ function spawn3(service, promptOrOptions, maybeOptions) {
5484
5754
  model: options.model,
5485
5755
  mode: options.mode,
5486
5756
  args: options.args,
5757
+ ...options.mcpServers ? { mcpServers: options.mcpServers } : {},
5487
5758
  useStdin: false
5488
5759
  });
5489
5760
  resolveEventsOnce(innerEvents);
@@ -5504,6 +5775,7 @@ function spawn3(service, promptOrOptions, maybeOptions) {
5504
5775
  model: options.model,
5505
5776
  mode: options.mode,
5506
5777
  args: options.args,
5778
+ ...options.mcpServers ? { mcpServers: options.mcpServers } : {},
5507
5779
  useStdin: false
5508
5780
  });
5509
5781
  }
@@ -5515,6 +5787,7 @@ function spawn3(service, promptOrOptions, maybeOptions) {
5515
5787
  model: options.model,
5516
5788
  mode: options.mode,
5517
5789
  args: options.args,
5790
+ ...options.mcpServers ? { mcpServers: options.mcpServers } : {},
5518
5791
  useStdin: false
5519
5792
  });
5520
5793
  } catch (error2) {
@@ -5531,59 +5804,10 @@ var init_spawn3 = __esm({
5531
5804
  init_spawn_core();
5532
5805
  await init_container();
5533
5806
  init_src4();
5534
- }
5535
- });
5536
-
5537
- // src/cli/errors.ts
5538
- var CliError, SilentError, ApiError, ValidationError, AuthenticationError, OperationCancelledError;
5539
- var init_errors = __esm({
5540
- "src/cli/errors.ts"() {
5541
- "use strict";
5542
- CliError = class extends Error {
5543
- context;
5544
- isUserError;
5545
- constructor(message, context, options) {
5546
- super(message);
5547
- this.name = this.constructor.name;
5548
- this.context = context;
5549
- this.isUserError = options?.isUserError ?? false;
5550
- if (Error.captureStackTrace) {
5551
- Error.captureStackTrace(this, this.constructor);
5552
- }
5553
- }
5554
- };
5555
- SilentError = class extends CliError {
5556
- constructor(message = "", options) {
5557
- super(message, void 0, { isUserError: options?.isUserError ?? false });
5558
- }
5559
- };
5560
- ApiError = class extends CliError {
5561
- httpStatus;
5562
- endpoint;
5563
- constructor(message, options) {
5564
- super(message, {
5565
- ...options?.context,
5566
- httpStatus: options?.httpStatus,
5567
- apiEndpoint: options?.endpoint
5568
- });
5569
- this.httpStatus = options?.httpStatus;
5570
- this.endpoint = options?.endpoint;
5571
- }
5572
- };
5573
- ValidationError = class extends CliError {
5574
- constructor(message, context) {
5575
- super(message, context, { isUserError: true });
5576
- }
5577
- };
5578
- AuthenticationError = class extends CliError {
5579
- constructor(message, context) {
5580
- super(message, context, { isUserError: true });
5581
- }
5582
- };
5583
- OperationCancelledError = class extends SilentError {
5584
- constructor(message = "Operation cancelled.") {
5585
- super(message, { isUserError: true });
5586
- }
5807
+ spawn3.pretty = async function pretty(service, promptOrOptions, maybeOptions) {
5808
+ const { events, result } = spawn3(service, promptOrOptions, maybeOptions);
5809
+ await renderAcpStream(events);
5810
+ return result;
5587
5811
  };
5588
5812
  }
5589
5813
  });
@@ -5806,6 +6030,14 @@ function createCliContainer(dependencies) {
5806
6030
  filePath: environment.credentialsPath,
5807
6031
  apiKey: value
5808
6032
  })
6033
+ },
6034
+ confirm: async (message) => {
6035
+ const result = await confirm2({ message });
6036
+ if (isCancel2(result)) {
6037
+ cancel2("Operation cancelled.");
6038
+ throw new OperationCancelledError();
6039
+ }
6040
+ return result === true;
5809
6041
  }
5810
6042
  });
5811
6043
  const registry2 = createServiceRegistry();
@@ -5852,6 +6084,7 @@ var init_container2 = __esm({
5852
6084
  init_src3();
5853
6085
  await init_providers();
5854
6086
  init_poe_code_command_runner();
6087
+ init_errors();
5855
6088
  }
5856
6089
  });
5857
6090
 
@@ -6033,7 +6266,10 @@ function registerSpawnCommand(program, container, options = {}) {
6033
6266
  const extraServices = options.extraServices ?? [];
6034
6267
  const serviceList = [...spawnServices, ...extraServices];
6035
6268
  const serviceDescription = `Agent to spawn${formatServiceList(serviceList)}`;
6036
- program.command("spawn").description("Run a single prompt through a configured agent CLI.").option("--model <model>", "Model identifier override passed to the agent CLI").option("-C, --cwd <path>", "Working directory for the agent CLI").option("--stdin", "Read the prompt from stdin").option("-i, --interactive", "Launch the agent in interactive TUI mode").option("--mode <mode>", "Permission mode: yolo | edit | read (default: yolo)").argument(
6269
+ program.command("spawn").description("Run a single prompt through a configured agent CLI.").option("--model <model>", "Model identifier override passed to the agent CLI").option("-C, --cwd <path>", "Working directory for the agent CLI").option("--stdin", "Read the prompt from stdin").option("-i, --interactive", "Launch the agent in interactive TUI mode").option("--mode <mode>", "Permission mode: yolo | edit | read (default: yolo)").option(
6270
+ "--mcp-config <json>",
6271
+ "MCP server config JSON: {name: {command, args?, env?}}"
6272
+ ).argument(
6037
6273
  "<agent>",
6038
6274
  serviceDescription
6039
6275
  ).argument("[prompt]", "Prompt text to send (or '-' / stdin)").argument(
@@ -6042,6 +6278,7 @@ function registerSpawnCommand(program, container, options = {}) {
6042
6278
  ).action(async function(service, promptText, agentArgs = []) {
6043
6279
  const flags = resolveCommandFlags(program);
6044
6280
  const commandOptions = this.opts();
6281
+ const mcpServers = parseMcpSpawnConfig(commandOptions.mcpConfig);
6045
6282
  const cwdOverride = resolveSpawnWorkingDirectory2(
6046
6283
  container.env.cwd,
6047
6284
  commandOptions.cwd
@@ -6071,6 +6308,7 @@ function registerSpawnCommand(program, container, options = {}) {
6071
6308
  args: forwardedArgs,
6072
6309
  model: commandOptions.model,
6073
6310
  mode: commandOptions.mode,
6311
+ ...mcpServers ? { mcpServers } : {},
6074
6312
  cwd: cwdOverride
6075
6313
  });
6076
6314
  process.exitCode = result.exitCode;
@@ -6091,6 +6329,7 @@ function registerSpawnCommand(program, container, options = {}) {
6091
6329
  args: forwardedArgs,
6092
6330
  model: commandOptions.model,
6093
6331
  mode: commandOptions.mode,
6332
+ mcpServers,
6094
6333
  cwd: cwdOverride,
6095
6334
  useStdin: shouldReadFromStdin
6096
6335
  };
@@ -6136,6 +6375,7 @@ function registerSpawnCommand(program, container, options = {}) {
6136
6375
  }
6137
6376
  }
6138
6377
  try {
6378
+ assertMcpSpawnSupport(adapter.label, canonicalService, mcpServers);
6139
6379
  if (flags.dryRun) {
6140
6380
  await spawnCore(container, canonicalService, spawnOptions, {
6141
6381
  dryRun: true,
@@ -6157,7 +6397,8 @@ function registerSpawnCommand(program, container, options = {}) {
6157
6397
  args: spawnOptions.args,
6158
6398
  model: spawnOptions.model,
6159
6399
  mode: spawnOptions.mode,
6160
- cwd: spawnOptions.cwd
6400
+ cwd: spawnOptions.cwd,
6401
+ ...spawnOptions.mcpServers ? { mcpServers: spawnOptions.mcpServers } : {}
6161
6402
  });
6162
6403
  await renderAcpStream(events);
6163
6404
  const final = await result;
@@ -6209,7 +6450,10 @@ async function confirmUnconfiguredService(container, service, label, flags) {
6209
6450
  const shouldProceed = await confirm2({
6210
6451
  message: `${label} is not configured via poe. Do you want to proceed?`
6211
6452
  });
6212
- return !isCancel(shouldProceed) && shouldProceed === true;
6453
+ if (isCancel2(shouldProceed)) {
6454
+ throw new OperationCancelledError();
6455
+ }
6456
+ return shouldProceed === true;
6213
6457
  }
6214
6458
  function resolveSpawnWorkingDirectory2(baseDir, candidate) {
6215
6459
  if (!candidate || candidate.trim().length === 0) {
@@ -6220,6 +6464,94 @@ function resolveSpawnWorkingDirectory2(baseDir, candidate) {
6220
6464
  }
6221
6465
  return path10.resolve(baseDir, candidate);
6222
6466
  }
6467
+ function parseMcpSpawnConfig(input) {
6468
+ if (!input) {
6469
+ return void 0;
6470
+ }
6471
+ let parsed;
6472
+ try {
6473
+ parsed = JSON.parse(input);
6474
+ } catch {
6475
+ throw new ValidationError(
6476
+ "--mcp-config must be valid JSON in this shape: {name: {command, args?, env?}}"
6477
+ );
6478
+ }
6479
+ if (!isObjectRecord(parsed)) {
6480
+ throw new ValidationError(
6481
+ "--mcp-config must be an object in this shape: {name: {command, args?, env?}}"
6482
+ );
6483
+ }
6484
+ const servers = {};
6485
+ for (const [name, value] of Object.entries(parsed)) {
6486
+ if (!isObjectRecord(value)) {
6487
+ throw new ValidationError(
6488
+ `--mcp-config entry "${name}" must be an object: {command, args?, env?}`
6489
+ );
6490
+ }
6491
+ const command = value.command;
6492
+ if (typeof command !== "string" || command.trim().length === 0) {
6493
+ throw new ValidationError(
6494
+ `--mcp-config entry "${name}" must include a non-empty string "command"`
6495
+ );
6496
+ }
6497
+ let args;
6498
+ if ("args" in value && value.args !== void 0) {
6499
+ if (!Array.isArray(value.args)) {
6500
+ throw new ValidationError(
6501
+ `--mcp-config entry "${name}".args must be an array of strings`
6502
+ );
6503
+ }
6504
+ args = [];
6505
+ for (const arg of value.args) {
6506
+ if (typeof arg !== "string") {
6507
+ throw new ValidationError(
6508
+ `--mcp-config entry "${name}".args must be an array of strings`
6509
+ );
6510
+ }
6511
+ args.push(arg);
6512
+ }
6513
+ }
6514
+ let env;
6515
+ if ("env" in value && value.env !== void 0) {
6516
+ if (!isObjectRecord(value.env)) {
6517
+ throw new ValidationError(
6518
+ `--mcp-config entry "${name}".env must be an object of string values`
6519
+ );
6520
+ }
6521
+ env = {};
6522
+ for (const [envKey, envValue] of Object.entries(value.env)) {
6523
+ if (typeof envValue !== "string") {
6524
+ throw new ValidationError(
6525
+ `--mcp-config entry "${name}".env must be an object of string values`
6526
+ );
6527
+ }
6528
+ env[envKey] = envValue;
6529
+ }
6530
+ }
6531
+ servers[name] = {
6532
+ command,
6533
+ ...args ? { args } : {},
6534
+ ...env ? { env } : {}
6535
+ };
6536
+ }
6537
+ return Object.keys(servers).length > 0 ? servers : void 0;
6538
+ }
6539
+ function assertMcpSpawnSupport(label, service, servers) {
6540
+ if (!servers || Object.keys(servers).length === 0) {
6541
+ return;
6542
+ }
6543
+ if (supportsMcpAtSpawn(service)) {
6544
+ return;
6545
+ }
6546
+ const supported = listMcpSupportedAgents();
6547
+ throw new ValidationError(
6548
+ `${label} does not support MCP servers at spawn time.
6549
+ Agents with spawn-time MCP support: ${supported.join(", ")}`
6550
+ );
6551
+ }
6552
+ function isObjectRecord(value) {
6553
+ return typeof value === "object" && value !== null && !Array.isArray(value);
6554
+ }
6223
6555
  var init_spawn4 = __esm({
6224
6556
  async "src/cli/commands/spawn.ts"() {
6225
6557
  "use strict";
@@ -6229,6 +6561,7 @@ var init_spawn4 = __esm({
6229
6561
  init_shared();
6230
6562
  init_spawn_core();
6231
6563
  await init_spawn3();
6564
+ init_errors();
6232
6565
  }
6233
6566
  });
6234
6567
 
@@ -6873,21 +7206,21 @@ function registerLoginCommand(program, container) {
6873
7206
  );
6874
7207
  resources.logger.intro("login");
6875
7208
  try {
6876
- const input = await resolveApiKeyInput(container, options);
6877
- const normalized = container.options.normalizeApiKey(input);
6878
7209
  const configuredServices = await loadConfiguredServices({
6879
7210
  fs: container.fs,
6880
7211
  filePath: container.env.credentialsPath
6881
7212
  });
6882
- await saveCredentials({
6883
- fs: resources.context.fs,
6884
- filePath: container.env.credentialsPath,
6885
- apiKey: normalized
7213
+ const apiKey = await container.options.resolveApiKey({
7214
+ value: options.apiKey,
7215
+ envValue: container.env.getVariable("POE_API_KEY"),
7216
+ dryRun: flags.dryRun,
7217
+ assumeYes: flags.assumeYes,
7218
+ allowStored: false
6886
7219
  });
6887
7220
  await reconfigureServices({
6888
7221
  program,
6889
7222
  container,
6890
- apiKey: normalized,
7223
+ apiKey,
6891
7224
  configuredServices
6892
7225
  });
6893
7226
  resources.context.complete({
@@ -6906,25 +7239,6 @@ function registerLoginCommand(program, container) {
6906
7239
  }
6907
7240
  });
6908
7241
  }
6909
- async function resolveApiKeyInput(container, options) {
6910
- if (options.apiKey) {
6911
- return options.apiKey;
6912
- }
6913
- const envKey = container.env.getVariable("POE_API_KEY");
6914
- if (envKey && envKey.trim().length > 0) {
6915
- return envKey;
6916
- }
6917
- const descriptor = container.promptLibrary.loginApiKey();
6918
- const response = await container.prompts(descriptor);
6919
- const value = response[descriptor.name];
6920
- if (typeof value !== "string" || value.trim() === "") {
6921
- throw new ValidationError("POE API key is required.", {
6922
- operation: "login",
6923
- field: "apiKey"
6924
- });
6925
- }
6926
- return value;
6927
- }
6928
7242
  async function reconfigureServices(input) {
6929
7243
  const { program, container, apiKey, configuredServices } = input;
6930
7244
  const serviceNames = Object.keys(configuredServices);
@@ -6978,7 +7292,6 @@ var init_login = __esm({
6978
7292
  "use strict";
6979
7293
  init_shared();
6980
7294
  init_credentials2();
6981
- init_errors();
6982
7295
  init_mutation_events();
6983
7296
  }
6984
7297
  });
@@ -34997,8 +35310,8 @@ function registerMcpCommand(program, container) {
34997
35310
  message: "Select agent to configure:",
34998
35311
  options: supportedAgents.map((a) => ({ value: a, label: a }))
34999
35312
  });
35000
- if (isCancel(selected)) {
35001
- cancel("Operation cancelled");
35313
+ if (isCancel2(selected)) {
35314
+ cancel2("Operation cancelled");
35002
35315
  return;
35003
35316
  }
35004
35317
  agent = selected;
@@ -35099,7 +35412,7 @@ async function runMcpServer(container, options) {
35099
35412
  await runMcpServerWithTransport(outputFormatPreferences);
35100
35413
  }
35101
35414
  var DEFAULT_MCP_AGENT;
35102
- var init_mcp = __esm({
35415
+ var init_mcp2 = __esm({
35103
35416
  "src/cli/commands/mcp.ts"() {
35104
35417
  "use strict";
35105
35418
  init_src3();
@@ -35355,8 +35668,8 @@ function registerSkillCommand(program, container) {
35355
35668
  message: "Select agent to configure:",
35356
35669
  options: supportedAgents2.map((a) => ({ value: a, label: a }))
35357
35670
  });
35358
- if (isCancel(selected)) {
35359
- cancel("Operation cancelled");
35671
+ if (isCancel2(selected)) {
35672
+ cancel2("Operation cancelled");
35360
35673
  return;
35361
35674
  }
35362
35675
  agent = selected;
@@ -35388,8 +35701,8 @@ function registerSkillCommand(program, container) {
35388
35701
  { value: "local", label: "Local" }
35389
35702
  ]
35390
35703
  });
35391
- if (isCancel(selected)) {
35392
- cancel("Operation cancelled");
35704
+ if (isCancel2(selected)) {
35705
+ cancel2("Operation cancelled");
35393
35706
  return;
35394
35707
  }
35395
35708
  scope = selected;
@@ -35434,8 +35747,8 @@ function registerSkillCommand(program, container) {
35434
35747
  message: "Select agent to unconfigure:",
35435
35748
  options: supportedAgents2.map((a) => ({ value: a, label: a }))
35436
35749
  });
35437
- if (isCancel(selected)) {
35438
- cancel("Operation cancelled");
35750
+ if (isCancel2(selected)) {
35751
+ cancel2("Operation cancelled");
35439
35752
  return;
35440
35753
  }
35441
35754
  agent = selected;
@@ -35464,8 +35777,8 @@ function registerSkillCommand(program, container) {
35464
35777
  { value: "local", label: "Local" }
35465
35778
  ]
35466
35779
  });
35467
- if (isCancel(selected)) {
35468
- cancel("Operation cancelled");
35780
+ if (isCancel2(selected)) {
35781
+ cancel2("Operation cancelled");
35469
35782
  return;
35470
35783
  }
35471
35784
  scope = selected;
@@ -36558,7 +36871,7 @@ async function defaultPromptOverbake(args) {
36558
36871
  { value: "abort", label: "Abort run" }
36559
36872
  ]
36560
36873
  });
36561
- if (isCancel(value)) return "abort";
36874
+ if (isCancel2(value)) return "abort";
36562
36875
  return value;
36563
36876
  }
36564
36877
  async function buildLoop(options) {
@@ -37011,7 +37324,7 @@ async function resolvePlanPath(options) {
37011
37324
  value: candidate.path
37012
37325
  }))
37013
37326
  });
37014
- if (isCancel(selection)) {
37327
+ if (isCancel2(selection)) {
37015
37328
  return null;
37016
37329
  }
37017
37330
  if (typeof selection !== "string" || selection.trim().length === 0) {
@@ -37697,8 +38010,8 @@ function registerRalphCommand(program, container) {
37697
38010
  message: "Select agent to install Ralph for:",
37698
38011
  options: supportedAgents2.map((a) => ({ value: a, label: a }))
37699
38012
  });
37700
- if (isCancel(selected)) {
37701
- cancel("Operation cancelled");
38013
+ if (isCancel2(selected)) {
38014
+ cancel2("Operation cancelled");
37702
38015
  return;
37703
38016
  }
37704
38017
  agent2 = selected;
@@ -37726,8 +38039,8 @@ function registerRalphCommand(program, container) {
37726
38039
  { value: "global", label: "Global (user home)" }
37727
38040
  ]
37728
38041
  });
37729
- if (isCancel(selected)) {
37730
- cancel("Operation cancelled");
38042
+ if (isCancel2(selected)) {
38043
+ cancel2("Operation cancelled");
37731
38044
  return;
37732
38045
  }
37733
38046
  scope = selected;
@@ -37775,8 +38088,8 @@ function registerRalphCommand(program, container) {
37775
38088
  message: "Should the agent skip committing changes? (--no-commit)",
37776
38089
  initialValue: configuredNoCommit ?? false
37777
38090
  });
37778
- if (isCancel(answer)) {
37779
- cancel("Operation cancelled");
38091
+ if (isCancel2(answer)) {
38092
+ cancel2("Operation cancelled");
37780
38093
  return;
37781
38094
  }
37782
38095
  noCommit = Boolean(answer);
@@ -37816,8 +38129,8 @@ function registerRalphCommand(program, container) {
37816
38129
  message: "Enter path to plan file:",
37817
38130
  placeholder: ".agents/poe-code-ralph/plans/plan.yaml"
37818
38131
  });
37819
- if (isCancel(entered)) {
37820
- cancel("Operation cancelled");
38132
+ if (isCancel2(entered)) {
38133
+ cancel2("Operation cancelled");
37821
38134
  return;
37822
38135
  }
37823
38136
  planPath = entered;
@@ -38118,7 +38431,10 @@ function registerUsageCommand(program, container) {
38118
38431
  startingAfter = result.data[result.data.length - 1].query_id;
38119
38432
  if (maxPages === void 0) {
38120
38433
  const shouldContinue = await confirm2({ message: "Load more?" });
38121
- if (isCancel(shouldContinue) || !shouldContinue) {
38434
+ if (isCancel2(shouldContinue)) {
38435
+ throw new OperationCancelledError();
38436
+ }
38437
+ if (!shouldContinue) {
38122
38438
  break;
38123
38439
  }
38124
38440
  }
@@ -38447,7 +38763,7 @@ var init_package = __esm({
38447
38763
  "package.json"() {
38448
38764
  package_default = {
38449
38765
  name: "poe-code",
38450
- version: "3.0.68",
38766
+ version: "3.0.70",
38451
38767
  description: "CLI tool to configure Poe API for developer workflows.",
38452
38768
  type: "module",
38453
38769
  main: "./dist/index.js",
@@ -38488,6 +38804,7 @@ var init_package = __esm({
38488
38804
  e2e: "vitest run --config e2e/vitest.config.ts",
38489
38805
  "e2e:verbose": "E2E_VERBOSE=1 vitest run --config e2e/vitest.config.ts --reporter=verbose",
38490
38806
  "e2e:cleanup": "tsx packages/e2e-docker-test-runner/scripts/cleanup.ts",
38807
+ "e2e:cleanup:aggressive": "tsx packages/e2e-docker-test-runner/scripts/cleanup.ts --aggressive",
38491
38808
  "e2e:logs": "tsx packages/e2e-docker-test-runner/scripts/logs.ts",
38492
38809
  "e2e:logs:rotate": "tsx packages/e2e-docker-test-runner/scripts/logs.ts --rotate",
38493
38810
  "e2e:cache:clear": "rm -rf ~/.cache/poe-e2e",
@@ -38841,7 +39158,7 @@ var init_program = __esm({
38841
39158
  init_unconfigure();
38842
39159
  init_test();
38843
39160
  init_generate();
38844
- init_mcp();
39161
+ init_mcp2();
38845
39162
  init_skill();
38846
39163
  init_version2();
38847
39164
  init_ralph();
@@ -38873,8 +39190,8 @@ function createPromptRunner(adapter = {
38873
39190
  text: text3,
38874
39191
  password: password2,
38875
39192
  select: select2,
38876
- isCancel,
38877
- cancel
39193
+ isCancel: isCancel2,
39194
+ cancel: cancel2
38878
39195
  }) {
38879
39196
  const runPrompt = async (descriptor) => {
38880
39197
  const type = descriptor.type ?? "text";
@@ -38970,7 +39287,7 @@ function createCliMain(programFactory) {
38970
39287
  try {
38971
39288
  await program.parseAsync(process.argv);
38972
39289
  } catch (error2) {
38973
- if (error2 instanceof SilentError) {
39290
+ if (isSilentError(error2)) {
38974
39291
  return;
38975
39292
  }
38976
39293
  if (error2 instanceof Error) {