@wrongstack/cli 0.6.1 → 0.6.4

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
@@ -3,7 +3,7 @@ import * as path23 from 'path';
3
3
  import { join } from 'path';
4
4
  import * as fsp2 from 'fs/promises';
5
5
  import { readdir, readFile } from 'fs/promises';
6
- import { color, DefaultPathResolver, TOKENS, DefaultSystemPromptBuilder, makeAutonomyPromptContributor, ToolRegistry, createContextManagerTool, EventBus, SlashCommandRegistry, createDelegateTool, FLEET_ROSTER, createMcpControlTool, EternalAutonomyEngine, DefaultLogger, DefaultModelsRegistry, ProviderRegistry, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, RecoveryLock, DefaultAttachmentStore, QueueStore, Context, loadTodosCheckpoint, attachTodosCheckpoint, loadDirectorState, loadPlan, createDefaultPipelines, AutoCompactionMiddleware, estimateRequestTokens, Agent, loadPlugins, FleetManager, makeDirectorSessionFactory, Director, makeAgentSubagentRunner, NULL_FLEET_BUS, resolveWstackPaths, DefaultSecretVault, migratePlaintextSecrets, DefaultConfigLoader, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, atomicWrite, DefaultPluginAPI, AutoApprovePermissionPolicy, formatContextWindowModeList, repairToolUseAdjacency, getContextWindowMode, resolveContextWindowPolicy, formatTodosList, emptyPlan, clearPlan, savePlan, formatPlanTemplates, getPlanTemplate, addPlanItem, formatPlan, deriveTodosFromPlanItem, removePlanItem, setPlanItemStatus, SpecStore, TaskGraphStore, SpecVersioning, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, DefaultTaskStore, TaskTracker, loadGoal, goalFilePath, summarizeUsage, emptyGoal, saveGoal, buildGoalPreamble, formatGoal, InputBuilder, projectHash, defaultOrchestrator, decryptConfigSecrets, encryptConfigSecrets as encryptConfigSecrets$1, allServers as allServers$1 } from '@wrongstack/core';
6
+ import { color, DefaultPathResolver, TOKENS, DefaultSystemPromptBuilder, makeAutonomyPromptContributor, ToolRegistry, createContextManagerTool, EventBus, SlashCommandRegistry, createDelegateTool, FLEET_ROSTER, createMcpControlTool, EternalAutonomyEngine, DefaultLogger, DefaultModelsRegistry, ProviderRegistry, InMemoryMetricsSink, wireMetricsToEvents, DefaultHealthRegistry, startMetricsServer, RecoveryLock, DefaultAttachmentStore, QueueStore, Context, loadTodosCheckpoint, attachTodosCheckpoint, loadDirectorState, loadPlan, createDefaultPipelines, AutoCompactionMiddleware, estimateRequestTokens, Agent, loadPlugins, FleetManager, makeDirectorSessionFactory, Director, makeAgentSubagentRunner, NULL_FLEET_BUS, resolveWstackPaths, DefaultSecretVault, migratePlaintextSecrets, DefaultConfigLoader, DefaultSessionReader, DefaultSessionRewinder, DefaultSessionStore, atomicWrite, DefaultPluginAPI, AutoApprovePermissionPolicy, formatContextWindowModeList, repairToolUseAdjacency, getContextWindowMode, resolveContextWindowPolicy, formatTodosList, emptyPlan, clearPlan, savePlan, formatPlanTemplates, getPlanTemplate, addPlanItem, formatPlan, deriveTodosFromPlanItem, removePlanItem, setPlanItemStatus, SpecStore, TaskGraphStore, SpecVersioning, getTemplate, listTemplates, templateToMarkdown, SpecParser, renderSpecAnalysis, AISpecBuilder, DefaultTaskStore, TaskTracker, loadGoal, goalFilePath, summarizeUsage, saveGoal, emptyGoal, buildGoalPreamble, formatGoal, InputBuilder, projectHash, defaultOrchestrator, decryptConfigSecrets, encryptConfigSecrets as encryptConfigSecrets$1, allServers as allServers$1 } from '@wrongstack/core';
7
7
  import { createRequire } from 'module';
8
8
  import * as os6 from 'os';
9
9
  import os6__default from 'os';
@@ -1704,7 +1704,9 @@ var BOOLEAN_FLAGS = /* @__PURE__ */ new Set([
1704
1704
  "metrics",
1705
1705
  "webui",
1706
1706
  "no-check",
1707
- "director"
1707
+ "director",
1708
+ "no-hints",
1709
+ "hints"
1708
1710
  ]);
1709
1711
  function parseArgs(argv) {
1710
1712
  const flags = {};
@@ -2590,6 +2592,84 @@ async function resolveModelSelection(answer, models, provider, _registry, render
2590
2592
  `);
2591
2593
  return { provider: provider.id, model: modelId };
2592
2594
  }
2595
+ var GROUPS = [
2596
+ {
2597
+ title: "Autonomy",
2598
+ items: [
2599
+ { key: "/goal <text>", blurb: "lock in a verifiable mission \u2014 only Esc / Ctrl+C interrupt" },
2600
+ { key: "/autonomy eternal", blurb: "sense \u2192 decide \u2192 execute \u2192 reflect loop until you stop it" },
2601
+ { key: "--eternal", blurb: "boot directly into the eternal-autonomy engine" },
2602
+ { key: "/autonomy on|suggest", blurb: "self-driving: auto-pick next step or just suggest it" }
2603
+ ]
2604
+ },
2605
+ {
2606
+ title: "Multi-agent / fleet",
2607
+ items: [
2608
+ { key: '--director "<task>"', blurb: "one-line LLM-driven fleet kickoff with 8 orchestration tools" },
2609
+ { key: "/director", blurb: "promote the current session to director mode at runtime" },
2610
+ { key: "/spawn -p <prov> -m <model> -n <name> <task>", blurb: "launch a single subagent (any provider/model)" },
2611
+ { key: "/fleet status|usage|kill|log|manifest", blurb: "inspect and control the running subagent fleet" }
2612
+ ]
2613
+ },
2614
+ {
2615
+ title: "Steering",
2616
+ items: [
2617
+ { key: "Esc (while busy)", blurb: "soft interrupt \u2014 next message carries a STEERING preamble" },
2618
+ { key: "/steer <text>", blurb: "mid-flight redirect, works when Esc is eaten by tmux" },
2619
+ { key: "Ctrl+C \xD7 1 / \xD7 2 / \xD7 3", blurb: "cancel iteration \xB7 force-exit Ink \xB7 hard exit(130)" }
2620
+ ]
2621
+ },
2622
+ {
2623
+ title: "Modes & context",
2624
+ items: [
2625
+ { key: "/mode", blurb: "switch persona: code-reviewer, debugger, architect, tester, devops, \u2026" },
2626
+ { key: "/model", blurb: "two-step provider \u2192 model picker, hot-swap at runtime" },
2627
+ { key: "/yolo on|off|toggle", blurb: "auto-approve every tool call without restart" },
2628
+ { key: "/context mode frugal|balanced|deep|archival", blurb: "pick how aggressively history is trimmed" },
2629
+ { key: "/compact", blurb: "manually compact the in-flight context window" },
2630
+ { key: "/plan show|add|start|done", blurb: "strategic roadmap, survives /resume across sessions" }
2631
+ ]
2632
+ },
2633
+ {
2634
+ title: "Daily ops",
2635
+ items: [
2636
+ { key: "@<query> / Alt+V / /image", blurb: "fuzzy file picker \xB7 paste clipboard image (TUI)" },
2637
+ { key: "/mcp \xB7 wstack mcp add <name>", blurb: "connect MCP servers (stdio / SSE / streamable-http)" },
2638
+ { key: "/plugin install|enable|disable <name>", blurb: "manage plugins (telegram, lsp, \u2026)" },
2639
+ { key: "/skill \xB7 /init \xB7 /commit", blurb: "list skills \xB7 scaffold AGENTS.md \xB7 LLM-drafted git commit" },
2640
+ { key: "/diag \xB7 /usage \xB7 wstack resume <id>", blurb: "diagnostics \xB7 token & cost totals \xB7 continue any session" }
2641
+ ]
2642
+ }
2643
+ ];
2644
+ var HINT_COUNT = GROUPS.reduce((n, g) => n + g.items.length, 0);
2645
+ function shouldSuppress(flags) {
2646
+ if (flags["no-hints"] === true) return true;
2647
+ if (flags["hints"] === false) return true;
2648
+ const env = process.env.WRONGSTACK_NO_HINTS;
2649
+ if (env && env !== "0" && env.toLowerCase() !== "false") return true;
2650
+ return false;
2651
+ }
2652
+ function printLaunchHints(renderer, flags) {
2653
+ if (shouldSuppress(flags)) return;
2654
+ const lines = [];
2655
+ lines.push("");
2656
+ lines.push(
2657
+ ` ${color.cyan("\u25C6")} ${color.bold(`WrongStack \u2014 ${HINT_COUNT} things you can do here`)}`
2658
+ );
2659
+ for (const group of GROUPS) {
2660
+ lines.push(` ${color.dim("\u2500")} ${color.cyan(group.title)}`);
2661
+ for (const item of group.items) {
2662
+ lines.push(` ${color.bold(item.key)} ${color.dim("\u2014")} ${color.dim(item.blurb)}`);
2663
+ }
2664
+ }
2665
+ lines.push("");
2666
+ lines.push(
2667
+ ` ${color.dim(`tip: hide this with ${color.bold("--no-hints")} or ${color.bold("WRONGSTACK_NO_HINTS=1")}`)}`
2668
+ );
2669
+ lines.push("");
2670
+ renderer.write(`${lines.join("\n")}
2671
+ `);
2672
+ }
2593
2673
  async function pathExists(file) {
2594
2674
  try {
2595
2675
  await fsp2.access(file);
@@ -3896,10 +3976,11 @@ function buildSpawnCommand(opts) {
3896
3976
  function buildAgentsCommand(opts) {
3897
3977
  return {
3898
3978
  name: "agents",
3899
- description: "Show status of spawned subagents.",
3900
- async run() {
3979
+ description: "Show status of spawned subagents. With an id, show live monitor view.",
3980
+ async run(args) {
3901
3981
  if (!opts.onAgents) return { message: "Multi-agent is not enabled in this session." };
3902
- return { message: opts.onAgents() };
3982
+ const subagentId = args.trim() || void 0;
3983
+ return { message: await opts.onAgents(subagentId) };
3903
3984
  }
3904
3985
  };
3905
3986
  }
@@ -4154,6 +4235,12 @@ function buildAutonomyCommand(opts) {
4154
4235
  opts.renderer.writeWarning(msg3);
4155
4236
  return { message: msg3 };
4156
4237
  }
4238
+ const isStale = goal.iterations > 0 || goal.engineState === "running";
4239
+ if (isStale) {
4240
+ const msg3 = `${color.amber("Stale goal detected.")} Previous mission has ${goal.iterations} iterations (engineState: ${goal.engineState}). Clear it first: ${color.bold("/goal clear")}, then set a new one: ${color.bold("/goal set <mission>")}.`;
4241
+ opts.renderer.writeWarning(msg3);
4242
+ return { message: msg3 };
4243
+ }
4157
4244
  if (!opts.onEternalStart) {
4158
4245
  const msg3 = "Eternal mode controller is not wired in this session.";
4159
4246
  opts.renderer.writeWarning(msg3);
@@ -4257,13 +4344,15 @@ ${color.dim(`Stored in ${goalPath} \u2014 Esc / /steer to redirect, Ctrl+C to st
4257
4344
  opts.renderer.write(msg2);
4258
4345
  return { message: msg2 };
4259
4346
  }
4347
+ const abandoned = { ...existing, goalState: "abandoned" };
4348
+ await saveGoal(goalPath, abandoned);
4260
4349
  const { unlink: unlink4 } = await import('fs/promises');
4261
4350
  try {
4262
4351
  await unlink4(goalPath);
4263
4352
  } catch {
4264
4353
  }
4265
4354
  if (opts.onEternalStop) opts.onEternalStop();
4266
- const msg = `${color.amber("Goal cleared.")} Eternal mode will stop on next cycle.`;
4355
+ const msg = `${color.amber("Goal cleared.")} Previous goal marked abandoned; eternal mode will stop.`;
4267
4356
  opts.renderer.write(msg);
4268
4357
  return { message: msg };
4269
4358
  }
@@ -7393,22 +7482,22 @@ function fmtDuration(ms) {
7393
7482
  const remMin = m - h * 60;
7394
7483
  return `${h}h${remMin}m`;
7395
7484
  }
7396
- function fmtTaskResultLine(r, color35) {
7485
+ function fmtTaskResultLine(r, color36) {
7397
7486
  const stats = `${r.iterations}it ${r.toolCalls}tc ${fmtDuration(r.durationMs)}`;
7398
7487
  const errMsg = typeof r.error === "string" ? r.error : r.error?.message;
7399
7488
  const errKind = typeof r.error === "object" ? r.error?.kind : void 0;
7400
7489
  const errTail = errMsg ? ` \u2014 ${errMsg.replace(/\s+/g, " ").slice(0, 80)}${errMsg.length > 80 ? "\u2026" : ""}` : "";
7401
- const errKindChip = errKind ? color35.dim(` [${errKind}]`) : "";
7402
- const errSnip = errMsg || errKind ? `${errKindChip}${color35.dim(errTail)}` : "";
7490
+ const errKindChip = errKind ? color36.dim(` [${errKind}]`) : "";
7491
+ const errSnip = errMsg || errKind ? `${errKindChip}${color36.dim(errTail)}` : "";
7403
7492
  switch (r.status) {
7404
7493
  case "success":
7405
- return { mark: color35.green("\u2713"), stats, tail: "" };
7494
+ return { mark: color36.green("\u2713"), stats, tail: "" };
7406
7495
  case "timeout":
7407
- return { mark: color35.yellow("\u23F1"), stats: `${color35.yellow("timeout")} ${stats}`, tail: errSnip };
7496
+ return { mark: color36.yellow("\u23F1"), stats: `${color36.yellow("timeout")} ${stats}`, tail: errSnip };
7408
7497
  case "stopped":
7409
- return { mark: color35.dim("\u2298"), stats: `${color35.dim("stopped")} ${stats}`, tail: errSnip };
7498
+ return { mark: color36.dim("\u2298"), stats: `${color36.dim("stopped")} ${stats}`, tail: errSnip };
7410
7499
  case "failed":
7411
- return { mark: color35.red("\u2717"), stats: `${color35.red("failed")} ${stats}`, tail: errSnip };
7500
+ return { mark: color36.red("\u2717"), stats: `${color36.red("failed")} ${stats}`, tail: errSnip };
7412
7501
  }
7413
7502
  }
7414
7503
 
@@ -7549,6 +7638,7 @@ async function boot(argv) {
7549
7638
  flags["no-tui"] = true;
7550
7639
  }
7551
7640
  if (choices.yolo !== config.yolo) config = patchConfig(config, { yolo: choices.yolo });
7641
+ printLaunchHints(renderer, flags);
7552
7642
  }
7553
7643
  return {
7554
7644
  config,
@@ -9789,8 +9879,41 @@ async function main(argv) {
9789
9879
  const tag = tags.length > 0 ? ` (${tags.join(" / ")})` : "";
9790
9880
  return `Spawned subagent ${subagentId}${tag} for task ${taskId}. Use /agents to track progress.`;
9791
9881
  },
9792
- onAgents: () => {
9882
+ onAgents: (subagentId) => {
9793
9883
  const s = multiAgentHost.status();
9884
+ if (subagentId) {
9885
+ const live = s.live.find((a) => a.subagentId === subagentId);
9886
+ const completed = s.completed.filter((r) => r.subagentId === subagentId);
9887
+ const pending = s.pending.filter((p) => p.subagentId === subagentId);
9888
+ if (!live && completed.length === 0 && pending.length === 0) {
9889
+ return `No subagent found with id "${subagentId}".`;
9890
+ }
9891
+ const STATUS_ICON2 = {
9892
+ running: "\u25CF",
9893
+ idle: "\u25CB",
9894
+ stopped: "\u2298"
9895
+ };
9896
+ const lines2 = [color.bold(`Agent ${subagentId.slice(0, 8)}`)];
9897
+ if (live) {
9898
+ lines2.push(` ${STATUS_ICON2[live.status] ?? "?"} status: ${live.status}`);
9899
+ if (live.task) lines2.push(` task: ${live.task}`);
9900
+ }
9901
+ for (const p of pending) {
9902
+ lines2.push(` \xB7 pending: ${p.taskId.slice(0, 8)} \u2192 ${p.description.slice(0, 60)}`);
9903
+ }
9904
+ for (const r of completed) {
9905
+ const fmt = fmtTaskResultLine(r, color);
9906
+ lines2.push(` ${fmt.mark} ${r.taskId.slice(0, 8)} ${fmt.stats}${fmt.tail}`);
9907
+ }
9908
+ if (director) {
9909
+ const snap = director.snapshot();
9910
+ const per = snap.perSubagent?.[subagentId];
9911
+ if (per?.cost) lines2.push(` cost: ${per.cost.toFixed(4)}`);
9912
+ if (per?.iterations) lines2.push(` iterations: ${per.iterations}`);
9913
+ if (per?.toolCalls) lines2.push(` toolCalls: ${per.toolCalls}`);
9914
+ }
9915
+ return lines2.join("\n");
9916
+ }
9794
9917
  const lines = [s.summary];
9795
9918
  const STATUS_ICON = {
9796
9919
  running: "\u25CF",