nexus-agents 2.60.0 → 2.62.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/cli.js CHANGED
@@ -16,7 +16,7 @@ import "./chunk-6QU4DJYW.js";
16
16
  import {
17
17
  setupCommandAsync,
18
18
  verifyCommand
19
- } from "./chunk-MJ3K4FYS.js";
19
+ } from "./chunk-OJBW4II4.js";
20
20
  import "./chunk-QGZBCD2A.js";
21
21
  import {
22
22
  AuthHandler,
@@ -142,7 +142,7 @@ import {
142
142
  validateCommand,
143
143
  validateWorkflow,
144
144
  wrapInMarkdownFence
145
- } from "./chunk-5JSMLCCK.js";
145
+ } from "./chunk-X5WDX7L3.js";
146
146
  import "./chunk-ED6VQWNG.js";
147
147
  import {
148
148
  resolveToken
@@ -185,6 +185,7 @@ import "./chunk-AP2FD37C.js";
185
185
  import "./chunk-BQ4YXGGQ.js";
186
186
  import {
187
187
  CustomExpertDefinitionSchema,
188
+ DATA_SUBDIRECTORIES,
188
189
  MAX_SYSTEM_PROMPT_LENGTH,
189
190
  VALID_EXPERT_DOMAINS,
190
191
  VALID_EXPERT_TIERS,
@@ -200,7 +201,7 @@ import {
200
201
  loadConfig,
201
202
  runDoctor,
202
203
  validateNexusEnv
203
- } from "./chunk-XRXUCE6K.js";
204
+ } from "./chunk-V7AFOKWC.js";
204
205
  import {
205
206
  DEFAULTS
206
207
  } from "./chunk-H43PABG4.js";
@@ -1458,14 +1459,14 @@ ${colors.dim}Session ended.${colors.reset}
1458
1459
  });
1459
1460
  });
1460
1461
  rl.prompt();
1461
- return new Promise((resolve14) => {
1462
+ return new Promise((resolve15) => {
1462
1463
  rl.on("line", (line) => {
1463
1464
  void (async () => {
1464
1465
  try {
1465
1466
  const shouldExit = await processLine(line, session, logger17);
1466
1467
  if (shouldExit) {
1467
1468
  rl.close();
1468
- resolve14();
1469
+ resolve15();
1469
1470
  return;
1470
1471
  }
1471
1472
  } catch (error) {
@@ -1477,7 +1478,7 @@ ${colors.dim}Session ended.${colors.reset}
1477
1478
  })();
1478
1479
  });
1479
1480
  rl.on("close", () => {
1480
- resolve14();
1481
+ resolve15();
1481
1482
  });
1482
1483
  });
1483
1484
  }
@@ -12828,7 +12829,7 @@ function writeResultAndExit(result) {
12828
12829
 
12829
12830
  // src/cli/hooks/hook-router.ts
12830
12831
  async function readStdin() {
12831
- return new Promise((resolve14, reject) => {
12832
+ return new Promise((resolve15, reject) => {
12832
12833
  let data = "";
12833
12834
  process.stdin.setEncoding("utf8");
12834
12835
  process.stdin.on("readable", () => {
@@ -12839,24 +12840,24 @@ async function readStdin() {
12839
12840
  }
12840
12841
  });
12841
12842
  process.stdin.on("end", () => {
12842
- resolve14(data);
12843
+ resolve15(data);
12843
12844
  });
12844
12845
  process.stdin.on("error", (err2) => {
12845
12846
  reject(err2);
12846
12847
  });
12847
12848
  if (process.stdin.isTTY) {
12848
- resolve14("");
12849
+ resolve15("");
12849
12850
  }
12850
12851
  });
12851
12852
  }
12852
12853
  async function readStdinWithTimeout(timeoutMs = 5e3) {
12853
- return new Promise((resolve14, reject) => {
12854
+ return new Promise((resolve15, reject) => {
12854
12855
  const timeout2 = setTimeout(() => {
12855
12856
  reject(new Error(`Stdin read timeout after ${String(timeoutMs)}ms`));
12856
12857
  }, timeoutMs);
12857
12858
  readStdin().then((data) => {
12858
12859
  clearTimeout(timeout2);
12859
- resolve14(data);
12860
+ resolve15(data);
12860
12861
  }).catch((err2) => {
12861
12862
  clearTimeout(timeout2);
12862
12863
  reject(err2 instanceof Error ? err2 : new Error(String(err2)));
@@ -17653,64 +17654,383 @@ var PARSE_ARGS_CONFIG = {
17653
17654
  },
17654
17655
  source: {
17655
17656
  type: "string"
17657
+ },
17658
+ // init --portable command options (#2305)
17659
+ portable: {
17660
+ type: "boolean",
17661
+ default: false
17662
+ },
17663
+ gitignore: {
17664
+ type: "boolean",
17665
+ default: false
17666
+ },
17667
+ // init --portable --mcp-config flag (#2308)
17668
+ "mcp-config": {
17669
+ type: "boolean",
17670
+ default: false
17656
17671
  }
17657
17672
  },
17658
17673
  allowPositionals: true,
17659
17674
  strict: true
17660
17675
  };
17676
+ var VALID_COMMANDS = [
17677
+ "server",
17678
+ "help",
17679
+ "version",
17680
+ "hello",
17681
+ "config",
17682
+ "expert",
17683
+ "workflow",
17684
+ "doctor",
17685
+ "verify",
17686
+ "review",
17687
+ "routing-audit",
17688
+ "orchestrate",
17689
+ "system-review",
17690
+ "vote",
17691
+ "index",
17692
+ "research",
17693
+ "validation",
17694
+ "learning-metrics",
17695
+ "swe-bench",
17696
+ "atbench",
17697
+ "setup",
17698
+ "hooks",
17699
+ "demo",
17700
+ "sprint",
17701
+ "session",
17702
+ "evaluate",
17703
+ "issue",
17704
+ "fitness-audit",
17705
+ "release-notes",
17706
+ "release-validate",
17707
+ "release-announce",
17708
+ "scaffold",
17709
+ "visualize",
17710
+ "capabilities",
17711
+ "status",
17712
+ "memory-benchmark",
17713
+ "auth",
17714
+ "scenario",
17715
+ "warm-up",
17716
+ "e2e-eval",
17717
+ "routing-ab",
17718
+ "memory-eval",
17719
+ "health",
17720
+ "init",
17721
+ "validate",
17722
+ "registry"
17723
+ ];
17661
17724
  function isValidCommand(value) {
17662
- const validCommands = [
17663
- "server",
17664
- "help",
17665
- "version",
17666
- "hello",
17667
- "config",
17668
- "expert",
17669
- "workflow",
17670
- "doctor",
17671
- "verify",
17672
- "review",
17673
- "routing-audit",
17674
- "orchestrate",
17675
- "system-review",
17676
- "vote",
17677
- "index",
17678
- "research",
17679
- "validation",
17680
- "learning-metrics",
17681
- "swe-bench",
17682
- "atbench",
17683
- "setup",
17684
- "hooks",
17685
- "demo",
17686
- "sprint",
17687
- "session",
17688
- "evaluate",
17689
- "issue",
17690
- "fitness-audit",
17691
- "release-notes",
17692
- "release-validate",
17693
- "release-announce",
17694
- "scaffold",
17695
- "visualize",
17696
- "capabilities",
17697
- "status",
17698
- "memory-benchmark",
17699
- "auth",
17700
- "scenario",
17701
- "warm-up",
17702
- "e2e-eval",
17703
- "routing-ab",
17704
- "memory-eval",
17705
- "health",
17706
- "validate",
17707
- "registry"
17708
- ];
17709
- return validCommands.includes(value);
17725
+ return VALID_COMMANDS.includes(value);
17710
17726
  }
17711
17727
 
17712
17728
  // src/cli-commands-handlers.ts
17713
- import { existsSync as existsSync18 } from "fs";
17729
+ import { existsSync as existsSync20 } from "fs";
17730
+
17731
+ // src/cli/init-portable.ts
17732
+ import {
17733
+ existsSync as existsSync18,
17734
+ mkdirSync as mkdirSync3,
17735
+ readdirSync as readdirSync2,
17736
+ statSync as statSync3,
17737
+ appendFileSync as appendFileSync2,
17738
+ readFileSync as readFileSync11
17739
+ } from "fs";
17740
+ import { resolve as resolve11, join as join14, isAbsolute as isAbsolute2 } from "path";
17741
+
17742
+ // src/cli/mcp-config-emitter.ts
17743
+ import { existsSync as existsSync17, readFileSync as readFileSync10, writeFileSync as writeFileSync5, appendFileSync } from "fs";
17744
+ import { join as join13 } from "path";
17745
+ var MCP_CONFIG_FILENAME = ".mcp.json";
17746
+ var NEXUS_SERVER_KEY = "nexus-agents";
17747
+ function buildNexusServerEntry(dataDir) {
17748
+ return {
17749
+ command: "nexus-agents",
17750
+ args: ["--mode=server"],
17751
+ env: { NEXUS_DATA_DIR: dataDir }
17752
+ };
17753
+ }
17754
+ function entriesEqual(a, b) {
17755
+ if (a.command !== b.command) return false;
17756
+ if (a.args.length !== b.args.length) return false;
17757
+ for (let i = 0; i < a.args.length; i++) if (a.args[i] !== b.args[i]) return false;
17758
+ const aEnv = a.env ?? {};
17759
+ const bEnv = b.env ?? {};
17760
+ const aKeys = Object.keys(aEnv);
17761
+ const bKeys = Object.keys(bEnv);
17762
+ if (aKeys.length !== bKeys.length) return false;
17763
+ for (const k of aKeys) if (aEnv[k] !== bEnv[k]) return false;
17764
+ return true;
17765
+ }
17766
+ function loadExistingConfig(path23) {
17767
+ if (!existsSync17(path23)) return { ok: true, value: void 0 };
17768
+ let raw;
17769
+ try {
17770
+ raw = readFileSync10(path23, "utf-8");
17771
+ } catch (e) {
17772
+ return { ok: false, error: e instanceof Error ? e.message : String(e) };
17773
+ }
17774
+ try {
17775
+ const parsed = JSON.parse(raw);
17776
+ if (typeof parsed !== "object" || parsed === null) {
17777
+ return { ok: false, error: `${path23}: top-level JSON must be an object` };
17778
+ }
17779
+ return { ok: true, value: parsed };
17780
+ } catch (e) {
17781
+ const msg = e instanceof Error ? e.message : String(e);
17782
+ return { ok: false, error: `${path23}: invalid JSON \u2014 ${msg}` };
17783
+ }
17784
+ }
17785
+ function decideEmission(existing, desired, force) {
17786
+ if (existing === void 0) {
17787
+ return { kind: "write", nextConfig: { mcpServers: { [NEXUS_SERVER_KEY]: desired } } };
17788
+ }
17789
+ const servers = existing.mcpServers ?? {};
17790
+ const current = servers[NEXUS_SERVER_KEY];
17791
+ if (current !== void 0 && entriesEqual(current, desired)) {
17792
+ return { kind: "noop" };
17793
+ }
17794
+ if (current !== void 0 && !force) {
17795
+ return {
17796
+ kind: "refuse",
17797
+ reason: `existing ${NEXUS_SERVER_KEY} entry differs; pass --force to overwrite`
17798
+ };
17799
+ }
17800
+ const nextServers = { ...servers, [NEXUS_SERVER_KEY]: desired };
17801
+ return { kind: "write", nextConfig: { ...existing, mcpServers: nextServers } };
17802
+ }
17803
+ function autoGitignoreMcpConfig(workspaceDir, dryRun) {
17804
+ const gitDir = join13(workspaceDir, ".git");
17805
+ if (!existsSync17(gitDir)) return false;
17806
+ const gitignorePath = join13(workspaceDir, ".gitignore");
17807
+ let existing = "";
17808
+ if (existsSync17(gitignorePath)) {
17809
+ existing = readFileSync10(gitignorePath, "utf-8");
17810
+ const already = existing.split("\n").some((l) => l.trim() === MCP_CONFIG_FILENAME || l.trim() === `/${MCP_CONFIG_FILENAME}`);
17811
+ if (already) return false;
17812
+ }
17813
+ if (!dryRun) {
17814
+ const sep3 = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
17815
+ appendFileSync(gitignorePath, `${sep3}${MCP_CONFIG_FILENAME}
17816
+ `, "utf-8");
17817
+ }
17818
+ return true;
17819
+ }
17820
+ function emitMcpConfig(options) {
17821
+ const mcpConfigPath = join13(options.workspaceDir, MCP_CONFIG_FILENAME);
17822
+ const dryRun = options.dryRun === true;
17823
+ const force = options.force === true;
17824
+ const desired = buildNexusServerEntry(options.dataDir);
17825
+ const loaded = loadExistingConfig(mcpConfigPath);
17826
+ if (!loaded.ok) return makeFailure(mcpConfigPath, loaded.error);
17827
+ const decision = decideEmission(loaded.value, desired, force);
17828
+ if (decision.kind === "refuse") return makeFailure(mcpConfigPath, decision.reason);
17829
+ if (decision.kind === "noop") {
17830
+ return makeSuccess({
17831
+ mcpConfigPath,
17832
+ written: false,
17833
+ alreadyMatched: true,
17834
+ gitignoreUpdated: false
17835
+ });
17836
+ }
17837
+ if (!dryRun) {
17838
+ writeFileSync5(mcpConfigPath, JSON.stringify(decision.nextConfig, null, 2) + "\n", "utf-8");
17839
+ }
17840
+ const gitignoreUpdated = autoGitignoreMcpConfig(options.workspaceDir, dryRun);
17841
+ return makeSuccess({ mcpConfigPath, written: true, alreadyMatched: false, gitignoreUpdated });
17842
+ }
17843
+ function makeSuccess(opts) {
17844
+ return { success: true, error: null, ...opts };
17845
+ }
17846
+ function makeFailure(mcpConfigPath, error) {
17847
+ return {
17848
+ success: false,
17849
+ mcpConfigPath,
17850
+ written: false,
17851
+ alreadyMatched: false,
17852
+ gitignoreUpdated: false,
17853
+ error
17854
+ };
17855
+ }
17856
+
17857
+ // src/cli/init-portable.ts
17858
+ var DEFAULT_PORTABLE_DIRNAME = ".nexus-agents";
17859
+ var RESTRICTED_SUBDIRS = /* @__PURE__ */ new Set(["auth"]);
17860
+ function resolveTargetPath(rawPath) {
17861
+ if (rawPath === void 0 || rawPath === "") {
17862
+ return resolve11(process.cwd(), DEFAULT_PORTABLE_DIRNAME);
17863
+ }
17864
+ return isAbsolute2(rawPath) ? rawPath : resolve11(process.cwd(), rawPath);
17865
+ }
17866
+ function isNonEmpty(dir) {
17867
+ if (!existsSync18(dir)) return false;
17868
+ const stat2 = statSync3(dir);
17869
+ if (!stat2.isDirectory()) return true;
17870
+ return readdirSync2(dir).length > 0;
17871
+ }
17872
+ function ensureDir(path23, dryRun, created, alreadyExisted, mode) {
17873
+ if (existsSync18(path23)) {
17874
+ alreadyExisted.push(path23);
17875
+ return;
17876
+ }
17877
+ if (!dryRun) {
17878
+ mkdirSync3(path23, { recursive: true, ...mode !== void 0 ? { mode } : {} });
17879
+ }
17880
+ created.push(path23);
17881
+ }
17882
+ function maybeUpdateGitignore(workspaceDir, portableDirName, dryRun) {
17883
+ const gitDir = join14(workspaceDir, ".git");
17884
+ if (!existsSync18(gitDir)) return false;
17885
+ const gitignorePath = join14(workspaceDir, ".gitignore");
17886
+ const entry = `${portableDirName}/`;
17887
+ let existing = "";
17888
+ if (existsSync18(gitignorePath)) {
17889
+ existing = readFileSync11(gitignorePath, "utf-8");
17890
+ if (existing.split("\n").some((l) => l.trim() === entry || l.trim() === portableDirName)) {
17891
+ return false;
17892
+ }
17893
+ }
17894
+ if (!dryRun) {
17895
+ const sep3 = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
17896
+ appendFileSync2(gitignorePath, `${sep3}${entry}
17897
+ `, "utf-8");
17898
+ }
17899
+ return true;
17900
+ }
17901
+ function inspectTarget(target) {
17902
+ const exists = existsSync18(target);
17903
+ if (!exists) return { exists: false, nonEmpty: false, isExistingNexusDir: false };
17904
+ const nonEmpty = isNonEmpty(target);
17905
+ const stat2 = statSync3(target);
17906
+ const isExistingNexusDir = stat2.isDirectory() && existsSync18(join14(target, "audit"));
17907
+ return { exists, nonEmpty, isExistingNexusDir };
17908
+ }
17909
+ function createDataLayout(target, dryRun, created, alreadyExisted) {
17910
+ ensureDir(target, dryRun, created, alreadyExisted);
17911
+ for (const subdir of DATA_SUBDIRECTORIES) {
17912
+ const mode = RESTRICTED_SUBDIRS.has(subdir) ? 448 : void 0;
17913
+ ensureDir(join14(target, subdir), dryRun, created, alreadyExisted, mode);
17914
+ }
17915
+ }
17916
+ function makeResult(opts) {
17917
+ return {
17918
+ success: opts.success,
17919
+ absolutePath: opts.absolutePath,
17920
+ created: opts.created,
17921
+ alreadyExisted: opts.alreadyExisted,
17922
+ skipped: opts.skipped ?? false,
17923
+ gitignoreUpdated: opts.gitignoreUpdated ?? false,
17924
+ ...opts.mcpConfig !== void 0 ? { mcpConfig: opts.mcpConfig } : {},
17925
+ error: opts.error ?? null
17926
+ };
17927
+ }
17928
+ function applyGitignoreOption(target, options, dryRun) {
17929
+ if (options.gitignore !== true) return false;
17930
+ const workspaceDir = resolve11(target, "..");
17931
+ const portableName = target.slice(workspaceDir.length + 1);
17932
+ return maybeUpdateGitignore(workspaceDir, portableName, dryRun);
17933
+ }
17934
+ function applyMcpConfigOption(target, options, dryRun) {
17935
+ if (options.mcpConfig !== true) return void 0;
17936
+ const workspaceDir = resolve11(target, "..");
17937
+ return emitMcpConfig({
17938
+ workspaceDir,
17939
+ dataDir: target,
17940
+ force: options.force === true,
17941
+ dryRun
17942
+ });
17943
+ }
17944
+ function buildSuccessResult(base, flags, mcpConfig) {
17945
+ if (mcpConfig === void 0) {
17946
+ return makeResult({ ...base, ...flags, success: true });
17947
+ }
17948
+ if (mcpConfig.success) {
17949
+ return makeResult({ ...base, ...flags, success: true, mcpConfig });
17950
+ }
17951
+ return makeResult({ ...base, ...flags, success: false, mcpConfig, error: mcpConfig.error });
17952
+ }
17953
+ function initPortable(options = {}) {
17954
+ const created = [];
17955
+ const alreadyExisted = [];
17956
+ const dryRun = options.dryRun === true;
17957
+ const force = options.force === true;
17958
+ const target = resolveTargetPath(options.path);
17959
+ const base = { absolutePath: target, created, alreadyExisted };
17960
+ try {
17961
+ const state = inspectTarget(target);
17962
+ if (state.isExistingNexusDir && !force) {
17963
+ createDataLayout(target, dryRun, created, alreadyExisted);
17964
+ return buildSuccessResult(
17965
+ base,
17966
+ { skipped: true },
17967
+ applyMcpConfigOption(target, options, dryRun)
17968
+ );
17969
+ }
17970
+ if (state.nonEmpty && !state.isExistingNexusDir && !force) {
17971
+ const error = `target ${target} already exists and is not empty; pass --force to use anyway`;
17972
+ return makeResult({ ...base, success: false, error });
17973
+ }
17974
+ createDataLayout(target, dryRun, created, alreadyExisted);
17975
+ const gitignoreUpdated = applyGitignoreOption(target, options, dryRun);
17976
+ return buildSuccessResult(
17977
+ base,
17978
+ { gitignoreUpdated },
17979
+ applyMcpConfigOption(target, options, dryRun)
17980
+ );
17981
+ } catch (error) {
17982
+ const msg = error instanceof Error ? error.message : String(error);
17983
+ return makeResult({ ...base, success: false, error: msg });
17984
+ }
17985
+ }
17986
+ function renderMcpConfigLines(mcpConfig) {
17987
+ const lines = [];
17988
+ if (mcpConfig.alreadyMatched) {
17989
+ lines.push(`\u2713 .mcp.json already up to date: ${mcpConfig.mcpConfigPath}`);
17990
+ } else if (mcpConfig.written) {
17991
+ lines.push(`\u2713 Wrote MCP config: ${mcpConfig.mcpConfigPath}`);
17992
+ }
17993
+ if (mcpConfig.gitignoreUpdated) {
17994
+ lines.push(`\u2713 Added .mcp.json to .gitignore (per-machine; do not commit)`);
17995
+ }
17996
+ return lines;
17997
+ }
17998
+ function renderMcpConfigCaveat(mcpConfig) {
17999
+ if (mcpConfig?.written !== true) return [];
18000
+ return [
18001
+ "",
18002
+ "Note: .mcp.json contains an absolute path to your local data dir.",
18003
+ "It is per-machine and should NOT be committed \u2014 collaborators should",
18004
+ "run `nexus-agents init --portable --mcp-config` themselves."
18005
+ ];
18006
+ }
18007
+ function formatInitPortableMessage(result, dryRun) {
18008
+ if (!result.success) {
18009
+ return `init --portable failed: ${result.error ?? "unknown error"}
18010
+ `;
18011
+ }
18012
+ if (dryRun) {
18013
+ const lines2 = [
18014
+ `(dry-run) would create ${String(result.created.length)} entries under:`,
18015
+ ` ${result.absolutePath}`
18016
+ ];
18017
+ return lines2.join("\n") + "\n";
18018
+ }
18019
+ const lines = [];
18020
+ lines.push(
18021
+ result.skipped ? `\u2713 Already initialized: ${result.absolutePath}` : `\u2713 Created: ${result.absolutePath}`
18022
+ );
18023
+ if (result.gitignoreUpdated) lines.push(`\u2713 Added entry to .gitignore`);
18024
+ if (result.mcpConfig !== void 0) lines.push(...renderMcpConfigLines(result.mcpConfig));
18025
+ lines.push("");
18026
+ lines.push("Activate by exporting:");
18027
+ lines.push(` export NEXUS_DATA_DIR=${result.absolutePath}`);
18028
+ lines.push("");
18029
+ lines.push("Or one-off:");
18030
+ lines.push(` NEXUS_DATA_DIR=${result.absolutePath} nexus-agents <cmd>`);
18031
+ lines.push(...renderMcpConfigCaveat(result.mcpConfig));
18032
+ return lines.join("\n") + "\n";
18033
+ }
17714
18034
 
17715
18035
  // src/mcp/tools/dev-pipeline-tool.ts
17716
18036
  import { z as z9 } from "zod";
@@ -18668,7 +18988,7 @@ function logFinalEventBusStats(logger17) {
18668
18988
  // src/cli-orchestrator.ts
18669
18989
  import * as readline2 from "readline";
18670
18990
  function runOrchestratorRepl(options, logger17) {
18671
- return new Promise((resolve14) => {
18991
+ return new Promise((resolve15) => {
18672
18992
  const rl = readline2.createInterface({
18673
18993
  input: process.stdin,
18674
18994
  output: process.stdout,
@@ -18704,7 +19024,7 @@ function runOrchestratorRepl(options, logger17) {
18704
19024
  });
18705
19025
  rl.on("close", () => {
18706
19026
  logger17.info("Orchestrator REPL closed");
18707
- resolve14();
19027
+ resolve15();
18708
19028
  });
18709
19029
  });
18710
19030
  }
@@ -20523,8 +20843,8 @@ function printFirstRunHint() {
20523
20843
  const isTTY = process.stderr.isTTY;
20524
20844
  if (!isTTY) return;
20525
20845
  const dataDir = getNexusDataDir();
20526
- const hasConfig = existsSync18("./nexus-agents.yaml") || existsSync18("./nexus-agents.yml");
20527
- if (existsSync18(dataDir) || hasConfig) return;
20846
+ const hasConfig = existsSync20("./nexus-agents.yaml") || existsSync20("./nexus-agents.yml");
20847
+ if (existsSync20(dataDir) || hasConfig) return;
20528
20848
  process.stderr.write(
20529
20849
  "\n\x1B[36mnexus-agents\x1B[0m: First time? Run \x1B[1mnexus-agents setup\x1B[0m to configure.\n\n"
20530
20850
  );
@@ -20690,6 +21010,24 @@ async function handleDoctorCommand(args) {
20690
21010
  }
20691
21011
  process.exit(exitCode === 0 ? EXIT_CODES.SUCCESS : EXIT_CODES.SERVER_START_FAILED);
20692
21012
  }
21013
+ function handleInitCommand(args) {
21014
+ if (args.options.portable !== true) {
21015
+ process.stderr.write(
21016
+ "Usage: nexus-agents init --portable [path] [--force] [--dry-run] [--gitignore]\nBootstraps a workspace-local nexus-agents data directory.\n"
21017
+ );
21018
+ process.exit(EXIT_CODES.INVALID_ARGS);
21019
+ }
21020
+ const targetPath = args.positionals[1];
21021
+ const result = initPortable({
21022
+ ...targetPath !== void 0 && targetPath !== "" ? { path: targetPath } : {},
21023
+ force: args.options.force,
21024
+ dryRun: args.options.dryRun,
21025
+ gitignore: args.options.gitignore ?? false,
21026
+ mcpConfig: args.options.mcpConfig ?? false
21027
+ });
21028
+ process.stdout.write(formatInitPortableMessage(result, args.options.dryRun));
21029
+ process.exit(result.success ? EXIT_CODES.SUCCESS : EXIT_CODES.SERVER_START_FAILED);
21030
+ }
20693
21031
  async function handleSetupCommandAsync(args) {
20694
21032
  if (args.options.customApi !== void 0 && args.options.customApi !== "") {
20695
21033
  const exitCode2 = await runCustomApiSetup(args);
@@ -22163,7 +22501,7 @@ function handleStatusCommand2(args) {
22163
22501
 
22164
22502
  // src/cli/scenario-command.ts
22165
22503
  import { readdir as readdir4 } from "fs/promises";
22166
- import { join as join14, resolve as resolve13 } from "path";
22504
+ import { join as join16, resolve as resolve14 } from "path";
22167
22505
 
22168
22506
  // src/testing/e2e/scenario-runner.ts
22169
22507
  import { readFile as readFile7 } from "fs/promises";
@@ -22323,7 +22661,7 @@ function checkCircularDependencies(workflow) {
22323
22661
  var defaultStubFactory = {
22324
22662
  createAgentStub(agentType, action) {
22325
22663
  return async (inputs) => {
22326
- await new Promise((resolve14) => setTimeout(resolve14, 10));
22664
+ await new Promise((resolve15) => setTimeout(resolve15, 10));
22327
22665
  return {
22328
22666
  stepId: `${agentType}-${action}`,
22329
22667
  status: "success",
@@ -22482,7 +22820,7 @@ function createScenarioRunner(stubFactory) {
22482
22820
  }
22483
22821
 
22484
22822
  // src/cli/scenario-command.ts
22485
- var FIXTURES_DIR = resolve13(import.meta.dirname, "../testing/e2e/fixtures");
22823
+ var FIXTURES_DIR = resolve14(import.meta.dirname, "../testing/e2e/fixtures");
22486
22824
  var SCENARIO_SUFFIX = ".scenario.yaml";
22487
22825
  async function listScenarios() {
22488
22826
  try {
@@ -22533,7 +22871,7 @@ async function handleRun(args) {
22533
22871
  process.exit(EXIT_CODES.SERVER_START_FAILED);
22534
22872
  }
22535
22873
  const runner = createScenarioRunner();
22536
- const fixturePath = join14(FIXTURES_DIR, `${name}${SCENARIO_SUFFIX}`);
22874
+ const fixturePath = join16(FIXTURES_DIR, `${name}${SCENARIO_SUFFIX}`);
22537
22875
  try {
22538
22876
  const fixture = await runner.loadFixture(fixturePath);
22539
22877
  const result = await runner.run(fixture);
@@ -22796,6 +23134,8 @@ var SYNC_COMMAND_HANDLERS = {
22796
23134
  status: handleStatusCommand2,
22797
23135
  // Issue #1023: Warm-Up Command
22798
23136
  "warm-up": handleWarmUpCommand,
23137
+ // #2305: Init Portable Command
23138
+ init: handleInitCommand,
22799
23139
  "e2e-eval": handleE2EEvalCommand,
22800
23140
  "routing-ab": handleRoutingABCommand,
22801
23141
  "memory-eval": handleMemoryEvalCommand,
@@ -23242,7 +23582,15 @@ function buildOptions(values) {
23242
23582
  ...buildSweBenchOptions(values),
23243
23583
  ...buildAtbenchOptions(values),
23244
23584
  ...buildLearningMetricsOptions(values),
23245
- ...buildSetupOptions(values)
23585
+ ...buildSetupOptions(values),
23586
+ ...buildInitOptions(values)
23587
+ };
23588
+ }
23589
+ function buildInitOptions(values) {
23590
+ return {
23591
+ portable: values.portable,
23592
+ gitignore: values.gitignore,
23593
+ mcpConfig: values["mcp-config"]
23246
23594
  };
23247
23595
  }
23248
23596
  function parseCliArgs(args = process.argv.slice(2)) {