@wrongstack/cli 0.1.8 → 0.1.9

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/index.js CHANGED
@@ -1,10 +1,10 @@
1
1
  #!/usr/bin/env node
2
- import { color, DefaultLogger, DefaultModelsRegistry, Container, DefaultConfigStore, TOKENS, DefaultSecretScrubber, DefaultRetryPolicy, DefaultErrorHandler, DefaultTokenCounter, DefaultModeStore, DefaultSessionStore, DefaultMemoryStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultPermissionPolicy, HybridCompactor, ProviderRegistry, ToolRegistry, createContextManagerTool, EventBus, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, RecoveryLock, DefaultAttachmentStore, QueueStore, Context, createDefaultPipelines, AutoCompactionMiddleware, Agent, SlashCommandRegistry, loadPlugins, DefaultPathResolver, resolveWstackPaths, DefaultSecretVault, migratePlaintextSecrets, DefaultConfigLoader, InputBuilder, DefaultPluginAPI, atomicWrite, DefaultSessionReader, makeAgentSubagentRunner, Director, DefaultMultiAgentCoordinator, decryptConfigSecrets, encryptConfigSecrets } from '@wrongstack/core';
2
+ import { color, DefaultLogger, DefaultModelsRegistry, Container, DefaultConfigStore, TOKENS, DefaultSecretScrubber, DefaultRetryPolicy, DefaultErrorHandler, DefaultTokenCounter, DefaultModeStore, DefaultSessionStore, DefaultMemoryStore, DefaultSkillLoader, DefaultSystemPromptBuilder, DefaultPermissionPolicy, HybridCompactor, ProviderRegistry, ToolRegistry, createContextManagerTool, EventBus, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, RecoveryLock, DefaultAttachmentStore, QueueStore, Context, createDefaultPipelines, AutoCompactionMiddleware, Agent, SlashCommandRegistry, loadPlugins, FLEET_ROSTER, DefaultPathResolver, resolveWstackPaths, DefaultSecretVault, migratePlaintextSecrets, DefaultConfigLoader, makeDirectorSessionFactory, makeAgentSubagentRunner, Director, DefaultMultiAgentCoordinator, InputBuilder, DefaultPluginAPI, atomicWrite, DefaultSessionReader, decryptConfigSecrets, encryptConfigSecrets } from '@wrongstack/core';
3
3
  import { WebSocketServer, WebSocket } from 'ws';
4
4
  import * as fs6 from 'fs/promises';
5
5
  import { writeFileSync } from 'fs';
6
6
  import { createRequire } from 'module';
7
- import * as path5 from 'path';
7
+ import * as path6 from 'path';
8
8
  import { MCPRegistry } from '@wrongstack/mcp';
9
9
  import { buildProviderFactoriesFromRegistry, makeProviderFromConfig, capabilitiesFor } from '@wrongstack/providers';
10
10
  import { rememberTool, forgetTool } from '@wrongstack/tools';
@@ -541,7 +541,7 @@ var ReadlineInputReader = class {
541
541
  history = [];
542
542
  pending = false;
543
543
  constructor(opts = {}) {
544
- this.historyFile = opts.historyFile ?? path5.join(os3.homedir(), ".wrongstack", "history");
544
+ this.historyFile = opts.historyFile ?? path6.join(os3.homedir(), ".wrongstack", "history");
545
545
  }
546
546
  async loadHistory() {
547
547
  try {
@@ -553,7 +553,7 @@ var ReadlineInputReader = class {
553
553
  }
554
554
  async saveHistory() {
555
555
  try {
556
- await fs6.mkdir(path5.dirname(this.historyFile), { recursive: true });
556
+ await fs6.mkdir(path6.dirname(this.historyFile), { recursive: true });
557
557
  await fs6.writeFile(this.historyFile, this.history.slice(-1e3).join("\n"));
558
558
  } catch {
559
559
  }
@@ -1168,8 +1168,8 @@ function initCommand(opts) {
1168
1168
  description: "Scaffold .wrongstack/AGENTS.md in the current project.",
1169
1169
  async run(args, ctx) {
1170
1170
  const force = args.trim() === "--force";
1171
- const dir = path5.join(ctx.projectRoot, ".wrongstack");
1172
- const file = path5.join(dir, "AGENTS.md");
1171
+ const dir = path6.join(ctx.projectRoot, ".wrongstack");
1172
+ const file = path6.join(dir, "AGENTS.md");
1173
1173
  try {
1174
1174
  await fs6.access(file);
1175
1175
  if (!force) {
@@ -1200,7 +1200,7 @@ No project type auto-detected. Edit the file to add build/test commands and conv
1200
1200
  async function detectProjectFacts(root) {
1201
1201
  const facts = { hints: [] };
1202
1202
  try {
1203
- const pkg = JSON.parse(await fs6.readFile(path5.join(root, "package.json"), "utf8"));
1203
+ const pkg = JSON.parse(await fs6.readFile(path6.join(root, "package.json"), "utf8"));
1204
1204
  const scripts = pkg.scripts ?? {};
1205
1205
  const pm = (pkg.packageManager ?? "npm").split("@")[0] ?? "npm";
1206
1206
  if (scripts["build"]) facts.build = `${pm} run build`;
@@ -1211,28 +1211,28 @@ async function detectProjectFacts(root) {
1211
1211
  } catch {
1212
1212
  }
1213
1213
  try {
1214
- await fs6.access(path5.join(root, "pyproject.toml"));
1214
+ await fs6.access(path6.join(root, "pyproject.toml"));
1215
1215
  facts.test ??= "pytest";
1216
1216
  facts.lint ??= "ruff check .";
1217
1217
  facts.hints.push("pyproject.toml");
1218
1218
  } catch {
1219
1219
  }
1220
1220
  try {
1221
- await fs6.access(path5.join(root, "go.mod"));
1221
+ await fs6.access(path6.join(root, "go.mod"));
1222
1222
  facts.build ??= "go build ./...";
1223
1223
  facts.test ??= "go test ./...";
1224
1224
  facts.hints.push("go.mod");
1225
1225
  } catch {
1226
1226
  }
1227
1227
  try {
1228
- await fs6.access(path5.join(root, "Cargo.toml"));
1228
+ await fs6.access(path6.join(root, "Cargo.toml"));
1229
1229
  facts.build ??= "cargo build";
1230
1230
  facts.test ??= "cargo test";
1231
1231
  facts.hints.push("Cargo.toml");
1232
1232
  } catch {
1233
1233
  }
1234
1234
  try {
1235
- await fs6.access(path5.join(root, "Makefile"));
1235
+ await fs6.access(path6.join(root, "Makefile"));
1236
1236
  facts.build ??= "make";
1237
1237
  facts.test ??= "make test";
1238
1238
  facts.hints.push("Makefile");
@@ -1808,13 +1808,13 @@ var MANIFESTS = [
1808
1808
  ];
1809
1809
  async function detectProjectKind(projectRoot) {
1810
1810
  try {
1811
- await fs6.access(path5.join(projectRoot, ".wrongstack", "AGENTS.md"));
1811
+ await fs6.access(path6.join(projectRoot, ".wrongstack", "AGENTS.md"));
1812
1812
  return "initialized";
1813
1813
  } catch {
1814
1814
  }
1815
1815
  for (const m of MANIFESTS) {
1816
1816
  try {
1817
- await fs6.access(path5.join(projectRoot, m));
1817
+ await fs6.access(path6.join(projectRoot, m));
1818
1818
  return "project";
1819
1819
  } catch {
1820
1820
  }
@@ -1822,8 +1822,8 @@ async function detectProjectKind(projectRoot) {
1822
1822
  return "empty";
1823
1823
  }
1824
1824
  async function scaffoldAgentsMd(projectRoot) {
1825
- const dir = path5.join(projectRoot, ".wrongstack");
1826
- const file = path5.join(dir, "AGENTS.md");
1825
+ const dir = path6.join(projectRoot, ".wrongstack");
1826
+ const file = path6.join(dir, "AGENTS.md");
1827
1827
  const facts = await detectProjectFacts(projectRoot);
1828
1828
  const body = renderAgentsTemplate(facts);
1829
1829
  await fs6.mkdir(dir, { recursive: true });
@@ -1836,7 +1836,7 @@ async function runProjectCheck(opts) {
1836
1836
  if (kind === "initialized") {
1837
1837
  renderer.write(
1838
1838
  `
1839
- ${color.green("\u2713")} Project initialized ${color.dim(`(${path5.join(projectRoot, ".wrongstack", "AGENTS.md")})`)}
1839
+ ${color.green("\u2713")} Project initialized ${color.dim(`(${path6.join(projectRoot, ".wrongstack", "AGENTS.md")})`)}
1840
1840
  `
1841
1841
  );
1842
1842
  return true;
@@ -2498,7 +2498,7 @@ function renderProgress2(ratio, width) {
2498
2498
  return FILLED2.repeat(capped) + EMPTY2.repeat(width - capped);
2499
2499
  }
2500
2500
  async function bootConfig(flags) {
2501
- const cwd = typeof flags["cwd"] === "string" ? path5.resolve(flags["cwd"]) : process.cwd();
2501
+ const cwd = typeof flags["cwd"] === "string" ? path6.resolve(flags["cwd"]) : process.cwd();
2502
2502
  const pathResolver = new DefaultPathResolver(cwd);
2503
2503
  const projectRoot = pathResolver.projectRoot;
2504
2504
  const userHome = os3.homedir();
@@ -2570,12 +2570,35 @@ var MultiAgentHost = class {
2570
2570
  * coordinator; the host's `coordinator` field still points at it so
2571
2571
  * the rest of the methods don't need to branch. */
2572
2572
  director;
2573
+ /** Lazily built alongside the director — produces per-subagent JSONL
2574
+ * writers under `<sessionsRoot>/<runId>/`. Null in non-director mode. */
2575
+ sessionFactory;
2573
2576
  pending = /* @__PURE__ */ new Map();
2574
2577
  results = [];
2575
2578
  opts;
2579
+ /**
2580
+ * Force the lazy build path to run *now* and return the live Director,
2581
+ * or null when director mode is off. Used by the CLI to register the
2582
+ * fleet's LLM-callable orchestration tools (spawn_subagent,
2583
+ * assign_task, await_tasks, ask_subagent, roll_up, terminate_subagent,
2584
+ * fleet_status, fleet_usage) into the leader's ToolRegistry before the
2585
+ * agent starts — without this, the leader literally cannot see the
2586
+ * orchestration tools and `--director` becomes a no-op.
2587
+ */
2588
+ async ensureDirector() {
2589
+ if (!this.opts.directorMode) return null;
2590
+ await this.ensureCoordinator();
2591
+ return this.director ?? null;
2592
+ }
2576
2593
  async ensureCoordinator() {
2577
2594
  if (this.coordinator) return this.coordinator;
2578
2595
  const config = this.deps.configStore.get();
2596
+ if (this.opts.directorMode && this.opts.sessionsRoot && !this.sessionFactory) {
2597
+ this.sessionFactory = makeDirectorSessionFactory({
2598
+ sessionsRoot: this.opts.sessionsRoot,
2599
+ directorRunId: this.opts.directorRunId
2600
+ });
2601
+ }
2579
2602
  const factory = async (subCfg) => {
2580
2603
  const events = new EventBus();
2581
2604
  const provider = await this.buildSubagentProvider(config, subCfg.provider);
@@ -2586,11 +2609,22 @@ var MultiAgentHost = class {
2586
2609
  model: subCfg.model ?? config.model,
2587
2610
  provider: subCfg.provider ?? config.provider
2588
2611
  });
2589
- const parentSession = this.deps.session;
2590
- const subSession = {
2591
- id: parentSession.id,
2592
- append: (ev) => parentSession.append({ ...ev })
2593
- };
2612
+ let subSession;
2613
+ if (this.sessionFactory) {
2614
+ const subagentName = subCfg.name ?? subCfg.id ?? `sub_${randomUUID().slice(0, 8)}`;
2615
+ subSession = await this.sessionFactory.createSubagentSession({
2616
+ subagentId: subagentName,
2617
+ provider: subCfg.provider ?? config.provider,
2618
+ model: subCfg.model ?? config.model,
2619
+ title: `subagent: ${subagentName}`
2620
+ });
2621
+ } else {
2622
+ const parentSession = this.deps.session;
2623
+ subSession = {
2624
+ id: parentSession.id,
2625
+ append: (ev) => parentSession.append({ ...ev })
2626
+ };
2627
+ }
2594
2628
  const ctx = new Context({
2595
2629
  systemPrompt: baseSystem,
2596
2630
  provider,
@@ -2628,7 +2662,8 @@ var MultiAgentHost = class {
2628
2662
  this.director = new Director({
2629
2663
  config: coordinatorConfig,
2630
2664
  runner,
2631
- manifestPath: this.opts.manifestPath
2665
+ manifestPath: this.opts.manifestPath,
2666
+ sharedScratchpadPath: this.opts.sharedScratchpadPath
2632
2667
  });
2633
2668
  this.director.on("task.completed", ({ task, result }) => {
2634
2669
  this.results.push(result);
@@ -3577,8 +3612,8 @@ async function initCmd(_args, deps) {
3577
3612
  };
3578
3613
  if (apiKey) config.apiKey = apiKey;
3579
3614
  await atomicWrite(deps.paths.globalConfig, JSON.stringify(config, null, 2));
3580
- await fs6.mkdir(path5.join(deps.projectRoot, ".wrongstack"), { recursive: true });
3581
- const agentsFile = path5.join(deps.projectRoot, ".wrongstack", "AGENTS.md");
3615
+ await fs6.mkdir(path6.join(deps.projectRoot, ".wrongstack"), { recursive: true });
3616
+ const agentsFile = path6.join(deps.projectRoot, ".wrongstack", "AGENTS.md");
3582
3617
  try {
3583
3618
  await fs6.access(agentsFile);
3584
3619
  } catch {
@@ -3986,7 +4021,7 @@ async function doctorCmd(_args, deps) {
3986
4021
  }
3987
4022
  try {
3988
4023
  await fs6.mkdir(deps.paths.projectSessions, { recursive: true });
3989
- const probe = path5.join(deps.paths.projectSessions, `.probe-${Date.now()}`);
4024
+ const probe = path6.join(deps.paths.projectSessions, `.probe-${Date.now()}`);
3990
4025
  await fs6.writeFile(probe, "");
3991
4026
  await fs6.unlink(probe);
3992
4027
  checks.push({ name: "sessions writable", status: "ok", detail: deps.paths.projectSessions });
@@ -4089,8 +4124,8 @@ async function exportCmd(args, deps) {
4089
4124
  return 1;
4090
4125
  }
4091
4126
  if (output) {
4092
- await fs6.mkdir(path5.dirname(path5.resolve(deps.cwd, output)), { recursive: true });
4093
- await fs6.writeFile(path5.resolve(deps.cwd, output), rendered, "utf8");
4127
+ await fs6.mkdir(path6.dirname(path6.resolve(deps.cwd, output)), { recursive: true });
4128
+ await fs6.writeFile(path6.resolve(deps.cwd, output), rendered, "utf8");
4094
4129
  deps.renderer.write(`Wrote ${rendered.length} bytes to ${output}
4095
4130
  `);
4096
4131
  } else {
@@ -4150,7 +4185,7 @@ async function helpCmd(_args, deps) {
4150
4185
  return 0;
4151
4186
  }
4152
4187
  async function projectsCmd(_args, deps) {
4153
- const projectsRoot = path5.join(deps.paths.globalRoot, "projects");
4188
+ const projectsRoot = path6.join(deps.paths.globalRoot, "projects");
4154
4189
  try {
4155
4190
  const entries = await fs6.readdir(projectsRoot);
4156
4191
  if (entries.length === 0) {
@@ -4160,7 +4195,7 @@ async function projectsCmd(_args, deps) {
4160
4195
  for (const hash of entries) {
4161
4196
  try {
4162
4197
  const meta = JSON.parse(
4163
- await fs6.readFile(path5.join(projectsRoot, hash, "meta.json"), "utf8")
4198
+ await fs6.readFile(path6.join(projectsRoot, hash, "meta.json"), "utf8")
4164
4199
  );
4165
4200
  deps.renderer.write(
4166
4201
  ` ${color.dim(hash)} ${color.dim(meta.lastSeen ?? "")} ${meta.root ?? "?"}
@@ -4251,7 +4286,7 @@ function resolveBundledSkillsDir() {
4251
4286
  try {
4252
4287
  const req2 = createRequire(import.meta.url);
4253
4288
  const corePkg = req2.resolve("@wrongstack/core/package.json");
4254
- return path5.join(path5.dirname(corePkg), "skills");
4289
+ return path6.join(path6.dirname(corePkg), "skills");
4255
4290
  } catch {
4256
4291
  return void 0;
4257
4292
  }
@@ -4520,7 +4555,7 @@ Try \`wstack models refresh\` once you have network access, or run with --no-fea
4520
4555
  const dumpMetrics = () => {
4521
4556
  if (!metricsSink) return;
4522
4557
  try {
4523
- const out = path5.join(wpaths.projectSessions, "metrics.json");
4558
+ const out = path6.join(wpaths.projectSessions, "metrics.json");
4524
4559
  const snap = metricsSink.snapshot();
4525
4560
  writeFileSync(out, JSON.stringify(snap, null, 2));
4526
4561
  } catch {
@@ -4677,10 +4712,10 @@ Try \`wstack models refresh\` once you have network access, or run with --no-fea
4677
4712
  }
4678
4713
  await recoveryLock.write(session.id).catch(() => void 0);
4679
4714
  const attachments = new DefaultAttachmentStore({
4680
- spoolDir: path5.join(wpaths.projectSessions, session.id, "attachments")
4715
+ spoolDir: path6.join(wpaths.projectSessions, session.id, "attachments")
4681
4716
  });
4682
4717
  const queueStore = new QueueStore({
4683
- dir: path5.join(wpaths.projectSessions, session.id)
4718
+ dir: path6.join(wpaths.projectSessions, session.id)
4684
4719
  });
4685
4720
  const tokenCounter = container.resolve(TOKENS.TokenCounter);
4686
4721
  const stats = new SessionStats(events, tokenCounter);
@@ -4889,7 +4924,10 @@ Try \`wstack models refresh\` once you have network access, or run with --no-fea
4889
4924
  }
4890
4925
  };
4891
4926
  const directorMode = flags["director"] === true;
4892
- const manifestPath = directorMode ? typeof process.env["WRONGSTACK_FLEET_MANIFEST"] === "string" ? process.env["WRONGSTACK_FLEET_MANIFEST"] : path5.join(wpaths.projectSessions, session.id, "fleet.json") : void 0;
4927
+ const fleetRoot = directorMode ? path6.join(wpaths.projectSessions, session.id) : void 0;
4928
+ const manifestPath = directorMode ? typeof process.env["WRONGSTACK_FLEET_MANIFEST"] === "string" ? process.env["WRONGSTACK_FLEET_MANIFEST"] : path6.join(fleetRoot, "fleet.json") : void 0;
4929
+ const sharedScratchpadPath = directorMode ? path6.join(fleetRoot, "shared") : void 0;
4930
+ const subagentSessionsRoot = directorMode ? path6.join(fleetRoot, "subagents") : void 0;
4893
4931
  const multiAgentHost = new MultiAgentHost({
4894
4932
  container,
4895
4933
  toolRegistry,
@@ -4901,9 +4939,27 @@ Try \`wstack models refresh\` once you have network access, or run with --no-fea
4901
4939
  tokenCounter,
4902
4940
  projectRoot,
4903
4941
  cwd
4904
- }, { directorMode, manifestPath });
4942
+ }, {
4943
+ directorMode,
4944
+ manifestPath,
4945
+ sharedScratchpadPath,
4946
+ sessionsRoot: subagentSessionsRoot,
4947
+ directorRunId: session.id
4948
+ });
4905
4949
  if (directorMode) {
4906
- renderer.writeInfo(`Director mode enabled. Fleet manifest \u2192 ${manifestPath}`);
4950
+ const director = await multiAgentHost.ensureDirector();
4951
+ if (director) {
4952
+ for (const tool of director.tools(FLEET_ROSTER)) {
4953
+ toolRegistry.register(tool);
4954
+ }
4955
+ renderer.writeInfo(`Director mode enabled. Roster: ${Object.keys(FLEET_ROSTER).join(", ")}`);
4956
+ renderer.writeInfo(` fleet root \u2192 ${fleetRoot}`);
4957
+ renderer.writeInfo(` manifest \u2192 ${manifestPath}`);
4958
+ renderer.writeInfo(` scratchpad \u2192 ${sharedScratchpadPath}`);
4959
+ renderer.writeInfo(` subagents \u2192 ${subagentSessionsRoot}`);
4960
+ } else {
4961
+ renderer.writeInfo(`Director mode enabled. Fleet manifest \u2192 ${manifestPath}`);
4962
+ }
4907
4963
  }
4908
4964
  const slashCmds = buildBuiltinSlashCommands({
4909
4965
  registry: slashRegistry,
@@ -5157,7 +5213,7 @@ Try \`wstack models refresh\` once you have network access, or run with --no-fea
5157
5213
  tokenCounter,
5158
5214
  attachments,
5159
5215
  effectiveMaxContext,
5160
- projectName: path5.basename(projectRoot) || void 0
5216
+ projectName: path6.basename(projectRoot) || void 0
5161
5217
  });
5162
5218
  await webuiPromise;
5163
5219
  } else {
@@ -5169,7 +5225,7 @@ Try \`wstack models refresh\` once you have network access, or run with --no-fea
5169
5225
  tokenCounter,
5170
5226
  attachments,
5171
5227
  effectiveMaxContext,
5172
- projectName: path5.basename(projectRoot) || void 0
5228
+ projectName: path6.basename(projectRoot) || void 0
5173
5229
  });
5174
5230
  }
5175
5231
  } finally {