poe-code 3.0.69 → 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 (40) hide show
  1. package/dist/cli/bootstrap.js +2 -2
  2. package/dist/cli/bootstrap.js.map +1 -1
  3. package/dist/cli/commands/configure-payload.js +3 -1
  4. package/dist/cli/commands/configure-payload.js.map +1 -1
  5. package/dist/cli/commands/login.js +8 -28
  6. package/dist/cli/commands/login.js.map +1 -1
  7. package/dist/cli/commands/spawn.js +86 -3
  8. package/dist/cli/commands/spawn.js.map +1 -1
  9. package/dist/cli/commands/usage.js +5 -2
  10. package/dist/cli/commands/usage.js.map +1 -1
  11. package/dist/cli/container.js +10 -1
  12. package/dist/cli/container.js.map +1 -1
  13. package/dist/cli/errors.d.ts +5 -0
  14. package/dist/cli/errors.js +14 -0
  15. package/dist/cli/errors.js.map +1 -1
  16. package/dist/cli/logger.js +7 -0
  17. package/dist/cli/logger.js.map +1 -1
  18. package/dist/cli/options.d.ts +5 -0
  19. package/dist/cli/options.js +102 -12
  20. package/dist/cli/options.js.map +1 -1
  21. package/dist/index.js +442 -130
  22. package/dist/index.js.map +4 -4
  23. package/dist/providers/claude-code.js +85 -1
  24. package/dist/providers/claude-code.js.map +4 -4
  25. package/dist/providers/codex.js +85 -1
  26. package/dist/providers/codex.js.map +4 -4
  27. package/dist/providers/kimi.js +85 -1
  28. package/dist/providers/kimi.js.map +4 -4
  29. package/dist/providers/opencode.js +85 -1
  30. package/dist/providers/opencode.js.map +4 -4
  31. package/dist/providers/spawn-options.d.ts +2 -1
  32. package/dist/sdk/container.js +2 -1
  33. package/dist/sdk/container.js.map +1 -1
  34. package/dist/sdk/spawn-core.d.ts +3 -1
  35. package/dist/sdk/spawn-core.js +1 -0
  36. package/dist/sdk/spawn-core.js.map +1 -1
  37. package/dist/sdk/spawn.js +5 -1
  38. package/dist/sdk/spawn.js.map +1 -1
  39. package/dist/sdk/types.d.ts +3 -1
  40. 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) {
@@ -5539,60 +5812,6 @@ var init_spawn3 = __esm({
5539
5812
  }
5540
5813
  });
5541
5814
 
5542
- // src/cli/errors.ts
5543
- var CliError, SilentError, ApiError, ValidationError, AuthenticationError, OperationCancelledError;
5544
- var init_errors = __esm({
5545
- "src/cli/errors.ts"() {
5546
- "use strict";
5547
- CliError = class extends Error {
5548
- context;
5549
- isUserError;
5550
- constructor(message, context, options) {
5551
- super(message);
5552
- this.name = this.constructor.name;
5553
- this.context = context;
5554
- this.isUserError = options?.isUserError ?? false;
5555
- if (Error.captureStackTrace) {
5556
- Error.captureStackTrace(this, this.constructor);
5557
- }
5558
- }
5559
- };
5560
- SilentError = class extends CliError {
5561
- constructor(message = "", options) {
5562
- super(message, void 0, { isUserError: options?.isUserError ?? false });
5563
- }
5564
- };
5565
- ApiError = class extends CliError {
5566
- httpStatus;
5567
- endpoint;
5568
- constructor(message, options) {
5569
- super(message, {
5570
- ...options?.context,
5571
- httpStatus: options?.httpStatus,
5572
- apiEndpoint: options?.endpoint
5573
- });
5574
- this.httpStatus = options?.httpStatus;
5575
- this.endpoint = options?.endpoint;
5576
- }
5577
- };
5578
- ValidationError = class extends CliError {
5579
- constructor(message, context) {
5580
- super(message, context, { isUserError: true });
5581
- }
5582
- };
5583
- AuthenticationError = class extends CliError {
5584
- constructor(message, context) {
5585
- super(message, context, { isUserError: true });
5586
- }
5587
- };
5588
- OperationCancelledError = class extends SilentError {
5589
- constructor(message = "Operation cancelled.") {
5590
- super(message, { isUserError: true });
5591
- }
5592
- };
5593
- }
5594
- });
5595
-
5596
5815
  // src/services/llm-client.ts
5597
5816
  function createPoeClient(options) {
5598
5817
  const httpClient = options.httpClient ?? createDefaultHttpClient();
@@ -5811,6 +6030,14 @@ function createCliContainer(dependencies) {
5811
6030
  filePath: environment.credentialsPath,
5812
6031
  apiKey: value
5813
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;
5814
6041
  }
5815
6042
  });
5816
6043
  const registry2 = createServiceRegistry();
@@ -5857,6 +6084,7 @@ var init_container2 = __esm({
5857
6084
  init_src3();
5858
6085
  await init_providers();
5859
6086
  init_poe_code_command_runner();
6087
+ init_errors();
5860
6088
  }
5861
6089
  });
5862
6090
 
@@ -6038,7 +6266,10 @@ function registerSpawnCommand(program, container, options = {}) {
6038
6266
  const extraServices = options.extraServices ?? [];
6039
6267
  const serviceList = [...spawnServices, ...extraServices];
6040
6268
  const serviceDescription = `Agent to spawn${formatServiceList(serviceList)}`;
6041
- 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(
6042
6273
  "<agent>",
6043
6274
  serviceDescription
6044
6275
  ).argument("[prompt]", "Prompt text to send (or '-' / stdin)").argument(
@@ -6047,6 +6278,7 @@ function registerSpawnCommand(program, container, options = {}) {
6047
6278
  ).action(async function(service, promptText, agentArgs = []) {
6048
6279
  const flags = resolveCommandFlags(program);
6049
6280
  const commandOptions = this.opts();
6281
+ const mcpServers = parseMcpSpawnConfig(commandOptions.mcpConfig);
6050
6282
  const cwdOverride = resolveSpawnWorkingDirectory2(
6051
6283
  container.env.cwd,
6052
6284
  commandOptions.cwd
@@ -6076,6 +6308,7 @@ function registerSpawnCommand(program, container, options = {}) {
6076
6308
  args: forwardedArgs,
6077
6309
  model: commandOptions.model,
6078
6310
  mode: commandOptions.mode,
6311
+ ...mcpServers ? { mcpServers } : {},
6079
6312
  cwd: cwdOverride
6080
6313
  });
6081
6314
  process.exitCode = result.exitCode;
@@ -6096,6 +6329,7 @@ function registerSpawnCommand(program, container, options = {}) {
6096
6329
  args: forwardedArgs,
6097
6330
  model: commandOptions.model,
6098
6331
  mode: commandOptions.mode,
6332
+ mcpServers,
6099
6333
  cwd: cwdOverride,
6100
6334
  useStdin: shouldReadFromStdin
6101
6335
  };
@@ -6141,6 +6375,7 @@ function registerSpawnCommand(program, container, options = {}) {
6141
6375
  }
6142
6376
  }
6143
6377
  try {
6378
+ assertMcpSpawnSupport(adapter.label, canonicalService, mcpServers);
6144
6379
  if (flags.dryRun) {
6145
6380
  await spawnCore(container, canonicalService, spawnOptions, {
6146
6381
  dryRun: true,
@@ -6162,7 +6397,8 @@ function registerSpawnCommand(program, container, options = {}) {
6162
6397
  args: spawnOptions.args,
6163
6398
  model: spawnOptions.model,
6164
6399
  mode: spawnOptions.mode,
6165
- cwd: spawnOptions.cwd
6400
+ cwd: spawnOptions.cwd,
6401
+ ...spawnOptions.mcpServers ? { mcpServers: spawnOptions.mcpServers } : {}
6166
6402
  });
6167
6403
  await renderAcpStream(events);
6168
6404
  const final = await result;
@@ -6214,7 +6450,10 @@ async function confirmUnconfiguredService(container, service, label, flags) {
6214
6450
  const shouldProceed = await confirm2({
6215
6451
  message: `${label} is not configured via poe. Do you want to proceed?`
6216
6452
  });
6217
- return !isCancel(shouldProceed) && shouldProceed === true;
6453
+ if (isCancel2(shouldProceed)) {
6454
+ throw new OperationCancelledError();
6455
+ }
6456
+ return shouldProceed === true;
6218
6457
  }
6219
6458
  function resolveSpawnWorkingDirectory2(baseDir, candidate) {
6220
6459
  if (!candidate || candidate.trim().length === 0) {
@@ -6225,6 +6464,94 @@ function resolveSpawnWorkingDirectory2(baseDir, candidate) {
6225
6464
  }
6226
6465
  return path10.resolve(baseDir, candidate);
6227
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
+ }
6228
6555
  var init_spawn4 = __esm({
6229
6556
  async "src/cli/commands/spawn.ts"() {
6230
6557
  "use strict";
@@ -6234,6 +6561,7 @@ var init_spawn4 = __esm({
6234
6561
  init_shared();
6235
6562
  init_spawn_core();
6236
6563
  await init_spawn3();
6564
+ init_errors();
6237
6565
  }
6238
6566
  });
6239
6567
 
@@ -6878,21 +7206,21 @@ function registerLoginCommand(program, container) {
6878
7206
  );
6879
7207
  resources.logger.intro("login");
6880
7208
  try {
6881
- const input = await resolveApiKeyInput(container, options);
6882
- const normalized = container.options.normalizeApiKey(input);
6883
7209
  const configuredServices = await loadConfiguredServices({
6884
7210
  fs: container.fs,
6885
7211
  filePath: container.env.credentialsPath
6886
7212
  });
6887
- await saveCredentials({
6888
- fs: resources.context.fs,
6889
- filePath: container.env.credentialsPath,
6890
- 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
6891
7219
  });
6892
7220
  await reconfigureServices({
6893
7221
  program,
6894
7222
  container,
6895
- apiKey: normalized,
7223
+ apiKey,
6896
7224
  configuredServices
6897
7225
  });
6898
7226
  resources.context.complete({
@@ -6911,25 +7239,6 @@ function registerLoginCommand(program, container) {
6911
7239
  }
6912
7240
  });
6913
7241
  }
6914
- async function resolveApiKeyInput(container, options) {
6915
- if (options.apiKey) {
6916
- return options.apiKey;
6917
- }
6918
- const envKey = container.env.getVariable("POE_API_KEY");
6919
- if (envKey && envKey.trim().length > 0) {
6920
- return envKey;
6921
- }
6922
- const descriptor = container.promptLibrary.loginApiKey();
6923
- const response = await container.prompts(descriptor);
6924
- const value = response[descriptor.name];
6925
- if (typeof value !== "string" || value.trim() === "") {
6926
- throw new ValidationError("POE API key is required.", {
6927
- operation: "login",
6928
- field: "apiKey"
6929
- });
6930
- }
6931
- return value;
6932
- }
6933
7242
  async function reconfigureServices(input) {
6934
7243
  const { program, container, apiKey, configuredServices } = input;
6935
7244
  const serviceNames = Object.keys(configuredServices);
@@ -6983,7 +7292,6 @@ var init_login = __esm({
6983
7292
  "use strict";
6984
7293
  init_shared();
6985
7294
  init_credentials2();
6986
- init_errors();
6987
7295
  init_mutation_events();
6988
7296
  }
6989
7297
  });
@@ -35002,8 +35310,8 @@ function registerMcpCommand(program, container) {
35002
35310
  message: "Select agent to configure:",
35003
35311
  options: supportedAgents.map((a) => ({ value: a, label: a }))
35004
35312
  });
35005
- if (isCancel(selected)) {
35006
- cancel("Operation cancelled");
35313
+ if (isCancel2(selected)) {
35314
+ cancel2("Operation cancelled");
35007
35315
  return;
35008
35316
  }
35009
35317
  agent = selected;
@@ -35104,7 +35412,7 @@ async function runMcpServer(container, options) {
35104
35412
  await runMcpServerWithTransport(outputFormatPreferences);
35105
35413
  }
35106
35414
  var DEFAULT_MCP_AGENT;
35107
- var init_mcp = __esm({
35415
+ var init_mcp2 = __esm({
35108
35416
  "src/cli/commands/mcp.ts"() {
35109
35417
  "use strict";
35110
35418
  init_src3();
@@ -35360,8 +35668,8 @@ function registerSkillCommand(program, container) {
35360
35668
  message: "Select agent to configure:",
35361
35669
  options: supportedAgents2.map((a) => ({ value: a, label: a }))
35362
35670
  });
35363
- if (isCancel(selected)) {
35364
- cancel("Operation cancelled");
35671
+ if (isCancel2(selected)) {
35672
+ cancel2("Operation cancelled");
35365
35673
  return;
35366
35674
  }
35367
35675
  agent = selected;
@@ -35393,8 +35701,8 @@ function registerSkillCommand(program, container) {
35393
35701
  { value: "local", label: "Local" }
35394
35702
  ]
35395
35703
  });
35396
- if (isCancel(selected)) {
35397
- cancel("Operation cancelled");
35704
+ if (isCancel2(selected)) {
35705
+ cancel2("Operation cancelled");
35398
35706
  return;
35399
35707
  }
35400
35708
  scope = selected;
@@ -35439,8 +35747,8 @@ function registerSkillCommand(program, container) {
35439
35747
  message: "Select agent to unconfigure:",
35440
35748
  options: supportedAgents2.map((a) => ({ value: a, label: a }))
35441
35749
  });
35442
- if (isCancel(selected)) {
35443
- cancel("Operation cancelled");
35750
+ if (isCancel2(selected)) {
35751
+ cancel2("Operation cancelled");
35444
35752
  return;
35445
35753
  }
35446
35754
  agent = selected;
@@ -35469,8 +35777,8 @@ function registerSkillCommand(program, container) {
35469
35777
  { value: "local", label: "Local" }
35470
35778
  ]
35471
35779
  });
35472
- if (isCancel(selected)) {
35473
- cancel("Operation cancelled");
35780
+ if (isCancel2(selected)) {
35781
+ cancel2("Operation cancelled");
35474
35782
  return;
35475
35783
  }
35476
35784
  scope = selected;
@@ -36563,7 +36871,7 @@ async function defaultPromptOverbake(args) {
36563
36871
  { value: "abort", label: "Abort run" }
36564
36872
  ]
36565
36873
  });
36566
- if (isCancel(value)) return "abort";
36874
+ if (isCancel2(value)) return "abort";
36567
36875
  return value;
36568
36876
  }
36569
36877
  async function buildLoop(options) {
@@ -37016,7 +37324,7 @@ async function resolvePlanPath(options) {
37016
37324
  value: candidate.path
37017
37325
  }))
37018
37326
  });
37019
- if (isCancel(selection)) {
37327
+ if (isCancel2(selection)) {
37020
37328
  return null;
37021
37329
  }
37022
37330
  if (typeof selection !== "string" || selection.trim().length === 0) {
@@ -37702,8 +38010,8 @@ function registerRalphCommand(program, container) {
37702
38010
  message: "Select agent to install Ralph for:",
37703
38011
  options: supportedAgents2.map((a) => ({ value: a, label: a }))
37704
38012
  });
37705
- if (isCancel(selected)) {
37706
- cancel("Operation cancelled");
38013
+ if (isCancel2(selected)) {
38014
+ cancel2("Operation cancelled");
37707
38015
  return;
37708
38016
  }
37709
38017
  agent2 = selected;
@@ -37731,8 +38039,8 @@ function registerRalphCommand(program, container) {
37731
38039
  { value: "global", label: "Global (user home)" }
37732
38040
  ]
37733
38041
  });
37734
- if (isCancel(selected)) {
37735
- cancel("Operation cancelled");
38042
+ if (isCancel2(selected)) {
38043
+ cancel2("Operation cancelled");
37736
38044
  return;
37737
38045
  }
37738
38046
  scope = selected;
@@ -37780,8 +38088,8 @@ function registerRalphCommand(program, container) {
37780
38088
  message: "Should the agent skip committing changes? (--no-commit)",
37781
38089
  initialValue: configuredNoCommit ?? false
37782
38090
  });
37783
- if (isCancel(answer)) {
37784
- cancel("Operation cancelled");
38091
+ if (isCancel2(answer)) {
38092
+ cancel2("Operation cancelled");
37785
38093
  return;
37786
38094
  }
37787
38095
  noCommit = Boolean(answer);
@@ -37821,8 +38129,8 @@ function registerRalphCommand(program, container) {
37821
38129
  message: "Enter path to plan file:",
37822
38130
  placeholder: ".agents/poe-code-ralph/plans/plan.yaml"
37823
38131
  });
37824
- if (isCancel(entered)) {
37825
- cancel("Operation cancelled");
38132
+ if (isCancel2(entered)) {
38133
+ cancel2("Operation cancelled");
37826
38134
  return;
37827
38135
  }
37828
38136
  planPath = entered;
@@ -38123,7 +38431,10 @@ function registerUsageCommand(program, container) {
38123
38431
  startingAfter = result.data[result.data.length - 1].query_id;
38124
38432
  if (maxPages === void 0) {
38125
38433
  const shouldContinue = await confirm2({ message: "Load more?" });
38126
- if (isCancel(shouldContinue) || !shouldContinue) {
38434
+ if (isCancel2(shouldContinue)) {
38435
+ throw new OperationCancelledError();
38436
+ }
38437
+ if (!shouldContinue) {
38127
38438
  break;
38128
38439
  }
38129
38440
  }
@@ -38452,7 +38763,7 @@ var init_package = __esm({
38452
38763
  "package.json"() {
38453
38764
  package_default = {
38454
38765
  name: "poe-code",
38455
- version: "3.0.69",
38766
+ version: "3.0.70",
38456
38767
  description: "CLI tool to configure Poe API for developer workflows.",
38457
38768
  type: "module",
38458
38769
  main: "./dist/index.js",
@@ -38493,6 +38804,7 @@ var init_package = __esm({
38493
38804
  e2e: "vitest run --config e2e/vitest.config.ts",
38494
38805
  "e2e:verbose": "E2E_VERBOSE=1 vitest run --config e2e/vitest.config.ts --reporter=verbose",
38495
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",
38496
38808
  "e2e:logs": "tsx packages/e2e-docker-test-runner/scripts/logs.ts",
38497
38809
  "e2e:logs:rotate": "tsx packages/e2e-docker-test-runner/scripts/logs.ts --rotate",
38498
38810
  "e2e:cache:clear": "rm -rf ~/.cache/poe-e2e",
@@ -38846,7 +39158,7 @@ var init_program = __esm({
38846
39158
  init_unconfigure();
38847
39159
  init_test();
38848
39160
  init_generate();
38849
- init_mcp();
39161
+ init_mcp2();
38850
39162
  init_skill();
38851
39163
  init_version2();
38852
39164
  init_ralph();
@@ -38878,8 +39190,8 @@ function createPromptRunner(adapter = {
38878
39190
  text: text3,
38879
39191
  password: password2,
38880
39192
  select: select2,
38881
- isCancel,
38882
- cancel
39193
+ isCancel: isCancel2,
39194
+ cancel: cancel2
38883
39195
  }) {
38884
39196
  const runPrompt = async (descriptor) => {
38885
39197
  const type = descriptor.type ?? "text";
@@ -38975,7 +39287,7 @@ function createCliMain(programFactory) {
38975
39287
  try {
38976
39288
  await program.parseAsync(process.argv);
38977
39289
  } catch (error2) {
38978
- if (error2 instanceof SilentError) {
39290
+ if (isSilentError(error2)) {
38979
39291
  return;
38980
39292
  }
38981
39293
  if (error2 instanceof Error) {