@stackmemoryai/stackmemory 0.5.64 → 0.5.67

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 (66) hide show
  1. package/README.md +69 -346
  2. package/bin/claude-sm +1 -1
  3. package/bin/claude-smd +1 -1
  4. package/bin/codex-sm +6 -0
  5. package/bin/codex-smd +1 -1
  6. package/bin/opencode-sm +1 -1
  7. package/dist/src/cli/claude-sm.js +162 -25
  8. package/dist/src/cli/claude-sm.js.map +2 -2
  9. package/dist/src/cli/commands/ping.js +14 -0
  10. package/dist/src/cli/commands/ping.js.map +7 -0
  11. package/dist/src/cli/commands/ralph.js +103 -1
  12. package/dist/src/cli/commands/ralph.js.map +2 -2
  13. package/dist/src/cli/commands/retrieval.js +1 -1
  14. package/dist/src/cli/commands/retrieval.js.map +2 -2
  15. package/dist/src/cli/commands/skills.js +300 -1
  16. package/dist/src/cli/commands/skills.js.map +2 -2
  17. package/dist/src/cli/index.js +362 -20
  18. package/dist/src/cli/index.js.map +2 -2
  19. package/dist/src/core/digest/types.js +1 -1
  20. package/dist/src/core/digest/types.js.map +1 -1
  21. package/dist/src/core/extensions/provider-adapter.js +2 -5
  22. package/dist/src/core/extensions/provider-adapter.js.map +2 -2
  23. package/dist/src/core/retrieval/llm-provider.js +2 -2
  24. package/dist/src/core/retrieval/llm-provider.js.map +1 -1
  25. package/dist/src/core/retrieval/types.js +1 -1
  26. package/dist/src/core/retrieval/types.js.map +1 -1
  27. package/dist/src/features/sweep/pty-wrapper.js +15 -5
  28. package/dist/src/features/sweep/pty-wrapper.js.map +2 -2
  29. package/dist/src/features/workers/tmux-manager.js +71 -0
  30. package/dist/src/features/workers/tmux-manager.js.map +7 -0
  31. package/dist/src/features/workers/worker-registry.js +52 -0
  32. package/dist/src/features/workers/worker-registry.js.map +7 -0
  33. package/dist/src/integrations/linear/webhook-handler.js +82 -0
  34. package/dist/src/integrations/linear/webhook-handler.js.map +2 -2
  35. package/dist/src/integrations/mcp/pending-utils.js +33 -0
  36. package/dist/src/integrations/mcp/pending-utils.js.map +7 -0
  37. package/dist/src/integrations/mcp/server.js +571 -1
  38. package/dist/src/integrations/mcp/server.js.map +2 -2
  39. package/dist/src/integrations/ralph/patterns/oracle-worker-pattern.js +2 -2
  40. package/dist/src/integrations/ralph/patterns/oracle-worker-pattern.js.map +2 -2
  41. package/dist/src/orchestrators/multimodal/constants.js +17 -0
  42. package/dist/src/orchestrators/multimodal/constants.js.map +7 -0
  43. package/dist/src/orchestrators/multimodal/harness.js +292 -0
  44. package/dist/src/orchestrators/multimodal/harness.js.map +7 -0
  45. package/dist/src/orchestrators/multimodal/providers.js +98 -0
  46. package/dist/src/orchestrators/multimodal/providers.js.map +7 -0
  47. package/dist/src/orchestrators/multimodal/types.js +5 -0
  48. package/dist/src/orchestrators/multimodal/types.js.map +7 -0
  49. package/dist/src/orchestrators/multimodal/utils.js +25 -0
  50. package/dist/src/orchestrators/multimodal/utils.js.map +7 -0
  51. package/dist/src/skills/claude-skills.js +116 -1
  52. package/dist/src/skills/claude-skills.js.map +2 -2
  53. package/dist/src/skills/linear-task-runner.js +262 -0
  54. package/dist/src/skills/linear-task-runner.js.map +7 -0
  55. package/dist/src/skills/recursive-agent-orchestrator.js +114 -85
  56. package/dist/src/skills/recursive-agent-orchestrator.js.map +2 -2
  57. package/dist/src/skills/spec-generator-skill.js +441 -0
  58. package/dist/src/skills/spec-generator-skill.js.map +7 -0
  59. package/package.json +14 -9
  60. package/scripts/claude-code-wrapper.sh +18 -30
  61. package/scripts/demos/ralph-integration-demo.ts +14 -13
  62. package/scripts/demos/trace-demo.ts +7 -21
  63. package/scripts/demos/trace-test.ts +20 -8
  64. package/scripts/install-claude-hooks.sh +2 -2
  65. package/scripts/verify-dist.cjs +83 -0
  66. package/templates/claude-hooks/post-edit-sweep.js +7 -10
@@ -51,12 +51,15 @@ import { createRetrievalCommands } from "./commands/retrieval.js";
51
51
  import { createDiscoveryCommands } from "./commands/discovery.js";
52
52
  import { createModelCommand } from "./commands/model.js";
53
53
  import { registerSetupCommands } from "./commands/setup.js";
54
+ import { createPingCommand } from "./commands/ping.js";
55
+ import chalk from "chalk";
56
+ import * as fs from "fs";
57
+ import * as path from "path";
58
+ import { filterPending } from "../integrations/mcp/pending-utils.js";
54
59
  import { ProjectManager } from "../core/projects/project-manager.js";
55
- import Database from "better-sqlite3";
56
60
  import { join } from "path";
57
61
  import { existsSync, mkdirSync } from "fs";
58
62
  import inquirer from "inquirer";
59
- import chalk from "chalk";
60
63
  import { enableChromaDB } from "../core/config/storage-config.js";
61
64
  import { spawn } from "child_process";
62
65
  import { homedir } from "os";
@@ -79,6 +82,13 @@ function findPackageJson() {
79
82
  return { version: "0.0.0" };
80
83
  }
81
84
  const VERSION = findPackageJson().version;
85
+ async function openDatabase(dbPath) {
86
+ const { default: Database } = await import("better-sqlite3");
87
+ return new Database(dbPath);
88
+ }
89
+ function isTestEnv() {
90
+ return process.env["VITEST"] === "true" || process.env["NODE_ENV"] === "test" || process.env["STACKMEMORY_TEST_SKIP_DB"] === "1";
91
+ }
82
92
  UpdateChecker.checkForUpdates(VERSION, true).catch(() => {
83
93
  });
84
94
  async function startNotificationServices() {
@@ -183,13 +193,15 @@ program.command("init").description(
183
193
  }
184
194
  }
185
195
  const dbPath = join(dbDir, "context.db");
186
- const db = new Database(dbPath);
187
- new FrameManager(db, "cli-project");
196
+ if (!isTestEnv()) {
197
+ const db = await openDatabase(dbPath);
198
+ new FrameManager(db, "cli-project");
199
+ db.close();
200
+ }
188
201
  logger.info("StackMemory initialized successfully", { projectRoot });
189
202
  console.log(chalk.green("\n[OK] StackMemory initialized"));
190
203
  console.log(chalk.gray(` Project: ${projectRoot}`));
191
204
  console.log(chalk.gray(` Storage: SQLite (local)`));
192
- db.close();
193
205
  if (options.daemon) {
194
206
  console.log(chalk.cyan("\nInstalling background service..."));
195
207
  try {
@@ -273,6 +285,13 @@ program.command("status").description("Show current StackMemory status").option(
273
285
  );
274
286
  return;
275
287
  }
288
+ if (isTestEnv()) {
289
+ console.log("\u{1F4CA} StackMemory Status (test mode):");
290
+ console.log(" Frames: n/a");
291
+ console.log(" Events: n/a");
292
+ console.log(" Sessions: n/a");
293
+ return;
294
+ }
276
295
  await UpdateChecker.checkForUpdates(VERSION);
277
296
  await sessionManager.initialize();
278
297
  await sharedContextLayer.initialize();
@@ -301,7 +320,7 @@ program.command("status").description("Show current StackMemory status").option(
301
320
  );
302
321
  }
303
322
  }
304
- const db = new Database(dbPath);
323
+ const db = await openDatabase(dbPath);
305
324
  const frameManager = new FrameManager(db, session.projectId);
306
325
  if (options.all) {
307
326
  frameManager.setQueryMode(FrameQueryMode.ALL_ACTIVE);
@@ -490,7 +509,11 @@ program.command("context:test").description("Test context persistence by creatin
490
509
  );
491
510
  return;
492
511
  }
493
- const db = new Database(dbPath);
512
+ if (isTestEnv()) {
513
+ console.log("\u{1F4DD} [test] Skipping DB write in context:test");
514
+ return;
515
+ }
516
+ const db = await openDatabase(dbPath);
494
517
  const frameManager = new FrameManager(db, "cli-project");
495
518
  console.log("\u{1F4DD} Creating test context frames...");
496
519
  const rootFrame = frameManager.createFrame({
@@ -558,17 +581,22 @@ program.addCommand(createMemoryCommand());
558
581
  program.addCommand(clearCommand);
559
582
  program.addCommand(serviceCommand);
560
583
  program.addCommand(createHooksCommand());
584
+ const lazyCommands = [];
561
585
  if (isFeatureEnabled("skills")) {
562
- import("./commands/skills.js").then(
563
- ({ createSkillsCommand }) => program.addCommand(createSkillsCommand())
564
- ).catch(() => {
565
- });
586
+ lazyCommands.push(
587
+ import("./commands/skills.js").then(
588
+ ({ createSkillsCommand }) => program.addCommand(createSkillsCommand())
589
+ ).catch(() => {
590
+ })
591
+ );
566
592
  }
567
593
  if (isFeatureEnabled("ralph")) {
568
- import("./commands/ralph.js").then(
569
- ({ default: createRalphCommand }) => program.addCommand(createRalphCommand())
570
- ).catch(() => {
571
- });
594
+ lazyCommands.push(
595
+ import("./commands/ralph.js").then(
596
+ ({ default: createRalphCommand }) => program.addCommand(createRalphCommand())
597
+ ).catch(() => {
598
+ })
599
+ );
572
600
  }
573
601
  program.addCommand(createDaemonCommand());
574
602
  program.addCommand(createSweepCommand());
@@ -577,16 +605,330 @@ program.addCommand(createAPICommand());
577
605
  program.addCommand(createCleanupProcessesCommand());
578
606
  program.addCommand(createAutoBackgroundCommand());
579
607
  program.addCommand(createSettingsCommand());
608
+ program.addCommand(createPingCommand());
580
609
  if (isFeatureEnabled("whatsapp")) {
581
- import("./commands/sms-notify.js").then(
582
- ({ createSMSNotifyCommand }) => program.addCommand(createSMSNotifyCommand())
583
- ).catch(() => {
584
- });
610
+ lazyCommands.push(
611
+ import("./commands/sms-notify.js").then(
612
+ ({ createSMSNotifyCommand }) => program.addCommand(createSMSNotifyCommand())
613
+ ).catch(() => {
614
+ })
615
+ );
585
616
  }
586
617
  program.addCommand(createRetrievalCommands());
587
618
  program.addCommand(createDiscoveryCommands());
588
619
  program.addCommand(createModelCommand());
589
620
  registerSetupCommands(program);
621
+ program.command("mm-spike").description(
622
+ "Run multi-agent planning/implementation spike (planner/implementer/critic)"
623
+ ).option(
624
+ "-t, --task <desc>",
625
+ "Task description",
626
+ "Add multi-agent spike harness"
627
+ ).option(
628
+ "--planner-model <name>",
629
+ "Claude model for planning",
630
+ "claude-sonnet-4-20250514"
631
+ ).option(
632
+ "--reviewer-model <name>",
633
+ "Claude model for review",
634
+ "claude-sonnet-4-20250514"
635
+ ).option(
636
+ "--execute",
637
+ "Execute implementer (codex-sm) instead of dry-run",
638
+ false
639
+ ).option("--implementer <name>", "codex|claude", "codex").option("--max-iters <n>", "Retry loop iterations", "2").option("--audit-dir <path>", "Persist spike results to directory").option("--record-frame", "Record as real frame with anchors", false).option("--record", "Record plan & critique into StackMemory context", false).option("--json", "Emit single JSON result (UI-friendly)", false).option("--quiet", "Minimal output (default)", true).option("--verbose", "Verbose sectioned output", false).option(
640
+ "--log",
641
+ "Pretty print interaction log (planner/implementer/critic)",
642
+ false
643
+ ).action(async (opts) => {
644
+ try {
645
+ const { runSpike } = await import("../orchestrators/multimodal/harness.js");
646
+ const result = await runSpike(
647
+ {
648
+ task: opts.task,
649
+ repoPath: process.cwd()
650
+ },
651
+ {
652
+ plannerModel: opts.plannerModel,
653
+ reviewerModel: opts.reviewerModel,
654
+ implementer: opts.implementer,
655
+ maxIters: parseInt(opts.maxIters),
656
+ dryRun: !opts.execute,
657
+ auditDir: opts.auditDir,
658
+ recordFrame: Boolean(opts.recordFrame),
659
+ record: Boolean(opts.record)
660
+ }
661
+ );
662
+ if (opts.log) {
663
+ printInteractionLog(
664
+ {
665
+ task: opts.task,
666
+ plannerModel: opts.plannerModel,
667
+ reviewerModel: opts.reviewerModel,
668
+ implementer: opts.implementer,
669
+ execute: Boolean(opts.execute)
670
+ },
671
+ result
672
+ );
673
+ return;
674
+ }
675
+ if (opts.json) {
676
+ console.log(JSON.stringify(result));
677
+ return;
678
+ }
679
+ if (opts.verbose) {
680
+ console.log("\n=== Plan ===");
681
+ console.log(JSON.stringify(result.plan, null, 2));
682
+ console.log("\n=== Iterations ===");
683
+ (result.iterations || []).forEach((it, i) => {
684
+ console.log(`
685
+ [Attempt ${i + 1}] ${it.command}`);
686
+ console.log("OK:", it.ok);
687
+ console.log("Critique:", JSON.stringify(it.critique));
688
+ });
689
+ console.log("\n=== Implementation ===");
690
+ console.log(JSON.stringify(result.implementation, null, 2));
691
+ console.log("\n=== Critique ===");
692
+ console.log(JSON.stringify(result.critique, null, 2));
693
+ } else if (!opts.quiet) {
694
+ console.log(
695
+ `Plan steps: ${result.plan.steps.length}, Approved: ${result.critique.approved}`
696
+ );
697
+ }
698
+ } catch (error) {
699
+ console.error("mm-spike failed:", error.message);
700
+ process.exit(1);
701
+ }
702
+ });
703
+ program.command("build").description(
704
+ "Plan + code: planner (Claude), implementer (Codex/Claude), critic (Claude) with optional log/json output"
705
+ ).argument("[task]", "Task description (positional)").option(
706
+ "-t, --task <desc>",
707
+ "Task description (required if no positional arg)"
708
+ ).option(
709
+ "--planner-model <name>",
710
+ "Claude model for planning",
711
+ "claude-sonnet-4-20250514"
712
+ ).option(
713
+ "--reviewer-model <name>",
714
+ "Claude model for review",
715
+ "claude-sonnet-4-20250514"
716
+ ).option("--execute", "Execute implementer (default: true)", true).option("--dry-run", "Skip execution, show commands only").option("--implementer <name>", "codex|claude", "codex").option("--max-iters <n>", "Retry loop iterations", "2").option("--audit-dir <path>", "Persist spike results to directory").option("--record-frame", "Record as real frame with anchors").option("--record", "Record plan & critique into StackMemory context").option("--json", "Emit single JSON result (UI-friendly)").option("--quiet", "Minimal output").option("--verbose", "Verbose sectioned output").option("--log", "Pretty print interaction log (default: true)", true).option("-C, --cwd <path>", "Working directory for implementation").action(async (taskArg, opts) => {
717
+ try {
718
+ const task = typeof taskArg === "string" && taskArg.length > 0 ? taskArg : opts.task;
719
+ if (!task) {
720
+ console.error(
721
+ chalk.red(
722
+ "Error: Task description required. Provide as argument or --task option."
723
+ )
724
+ );
725
+ console.error(
726
+ chalk.gray(' Example: stackmemory build "Add user authentication"')
727
+ );
728
+ process.exit(1);
729
+ }
730
+ const { runSpike } = await import("../orchestrators/multimodal/harness.js");
731
+ const dryRun = opts.dryRun === true || opts.execute === false;
732
+ const findGitRoot = (startDir) => {
733
+ let dir = startDir;
734
+ while (dir !== "/") {
735
+ if (existsSync(join(dir, ".git"))) {
736
+ return dir;
737
+ }
738
+ dir = path.dirname(dir);
739
+ }
740
+ return startDir;
741
+ };
742
+ const repoPath = opts.cwd ? path.resolve(opts.cwd) : findGitRoot(process.cwd());
743
+ const result = await runSpike(
744
+ { task, repoPath },
745
+ {
746
+ plannerModel: opts.plannerModel,
747
+ reviewerModel: opts.reviewerModel,
748
+ implementer: opts.implementer,
749
+ maxIters: parseInt(opts.maxIters),
750
+ dryRun,
751
+ auditDir: opts.auditDir,
752
+ recordFrame: Boolean(opts.recordFrame),
753
+ record: Boolean(opts.record)
754
+ }
755
+ );
756
+ if (opts.log) {
757
+ printInteractionLog(
758
+ {
759
+ task,
760
+ plannerModel: opts.plannerModel,
761
+ reviewerModel: opts.reviewerModel,
762
+ implementer: opts.implementer,
763
+ execute: !dryRun
764
+ },
765
+ result
766
+ );
767
+ return;
768
+ }
769
+ if (opts.json) {
770
+ console.log(JSON.stringify(result));
771
+ return;
772
+ }
773
+ if (opts.verbose) {
774
+ console.log("\n=== Plan ===");
775
+ console.log(JSON.stringify(result.plan, null, 2));
776
+ console.log("\n=== Iterations ===");
777
+ (result.iterations || []).forEach((it, i) => {
778
+ console.log(`
779
+ [Attempt ${i + 1}] ${it.command}`);
780
+ console.log("OK:", it.ok);
781
+ console.log("Critique:", JSON.stringify(it.critique));
782
+ });
783
+ console.log("\n=== Implementation ===");
784
+ console.log(JSON.stringify(result.implementation, null, 2));
785
+ console.log("\n=== Critique ===");
786
+ console.log(JSON.stringify(result.critique, null, 2));
787
+ } else if (!opts.quiet) {
788
+ console.log(
789
+ `Plan steps: ${result.plan.steps.length}, Approved: ${result.critique.approved}`
790
+ );
791
+ }
792
+ } catch (error) {
793
+ console.error("build failed:", error.message);
794
+ process.exit(1);
795
+ }
796
+ });
797
+ function printInteractionLog(meta, result) {
798
+ const divider = chalk.gray(
799
+ "\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500"
800
+ );
801
+ console.log(chalk.cyan.bold("\nPlan & Code Session"));
802
+ console.log(`${chalk.gray("Task:")} ${meta.task}`);
803
+ console.log(`${chalk.gray("Planner:")} ${meta.plannerModel}`);
804
+ console.log(`${chalk.gray("Reviewer:")} ${meta.reviewerModel}`);
805
+ console.log(
806
+ `${chalk.gray("Implementer:")} ${meta.implementer} ${meta.execute ? chalk.green("(execute)") : chalk.yellow("(dry-run)")}`
807
+ );
808
+ console.log(divider);
809
+ if (result.plan) {
810
+ console.log(
811
+ chalk.bold("Plan Summary: "),
812
+ result.plan.summary || "(no summary)"
813
+ );
814
+ const steps = result.plan.steps.slice(0, 6);
815
+ if (steps.length) {
816
+ console.log(chalk.bold("\nSteps:"));
817
+ steps.forEach((s, idx) => {
818
+ console.log(`${chalk.gray(String(idx + 1) + ".")} ${s.title || s.id}`);
819
+ const ac = s.acceptanceCriteria || [];
820
+ if (ac.length) {
821
+ ac.slice(0, 3).forEach((c) => console.log(chalk.gray(` - ${c}`)));
822
+ if (ac.length > 3) console.log(chalk.gray(" - ..."));
823
+ }
824
+ });
825
+ }
826
+ if (result.plan.risks?.length) {
827
+ console.log(chalk.bold("\nRisks:"));
828
+ result.plan.risks.slice(0, 5).forEach((r) => console.log(chalk.gray(` - ${r}`)));
829
+ }
830
+ }
831
+ console.log(`
832
+ ${divider}`);
833
+ const iters = result.iterations || [];
834
+ if (iters.length) {
835
+ iters.forEach((it, i) => {
836
+ console.log(chalk.magenta(`Attempt ${i + 1}`));
837
+ console.log(`${chalk.gray("Command:")} ${it.command}`);
838
+ console.log(
839
+ `${chalk.gray("OK:")} ${it.ok ? chalk.green("true") : chalk.red("false")}`
840
+ );
841
+ const issues = it.critique?.issues || [];
842
+ const sugg = it.critique?.suggestions || [];
843
+ if (issues.length) {
844
+ console.log(chalk.bold("Issues:"));
845
+ issues.slice(0, 5).forEach((x) => console.log(chalk.red(` - ${x}`)));
846
+ }
847
+ if (sugg.length) {
848
+ console.log(chalk.bold("Suggestions:"));
849
+ sugg.slice(0, 5).forEach((x) => console.log(chalk.yellow(` - ${x}`)));
850
+ }
851
+ console.log(divider);
852
+ });
853
+ }
854
+ const approved = result.critique?.approved ?? false;
855
+ console.log(
856
+ `${chalk.bold("Final:")} ${approved ? chalk.green("Approved") : chalk.yellow("Needs changes")}`
857
+ );
858
+ console.log("");
859
+ }
860
+ program.command("pending:list").description(
861
+ "List pending approval-gated plans (from .stackmemory/build/pending.json)"
862
+ ).option("--task-contains <substr>", "Filter tasks containing this substring").option("--older-than-ms <number>", "Only items older than this age (ms)").option("--newer-than-ms <number>", "Only items newer than this age (ms)").option("--sort <asc|desc>", "Sort by createdAt", "desc").option("--limit <number>", "Max items to return", "20").option("--pretty", "Pretty-print JSON output", false).action(async (opts) => {
863
+ try {
864
+ const storePath = path.join(
865
+ process.cwd(),
866
+ ".stackmemory",
867
+ "build",
868
+ "pending.json"
869
+ );
870
+ let pending = {};
871
+ if (fs.existsSync(storePath)) {
872
+ try {
873
+ pending = JSON.parse(fs.readFileSync(storePath, "utf-8"));
874
+ } catch {
875
+ }
876
+ }
877
+ const items = Object.entries(pending).map(([approvalId, data]) => ({
878
+ approvalId,
879
+ task: data?.task ?? "",
880
+ createdAt: Number(data?.createdAt || 0) || null
881
+ }));
882
+ const filters = {
883
+ taskContains: opts.taskContains,
884
+ olderThanMs: opts.olderThanMs ? Number(opts.olderThanMs) : void 0,
885
+ newerThanMs: opts.newerThanMs ? Number(opts.newerThanMs) : void 0,
886
+ sort: opts.sort || void 0,
887
+ limit: opts.limit ? Number(opts.limit) : void 0
888
+ };
889
+ const out = filterPending(items, filters);
890
+ console.log(
891
+ JSON.stringify({ ok: true, pending: out }, null, opts.pretty ? 2 : 0)
892
+ );
893
+ } catch (error) {
894
+ console.error(
895
+ JSON.stringify({ ok: false, error: error.message })
896
+ );
897
+ process.exit(1);
898
+ }
899
+ });
900
+ program.command("plan").description("Generate an implementation plan (no code execution)").option("-t, --task <desc>", "Task description", "Plan a small change").option(
901
+ "--planner-model <name>",
902
+ "Claude model for planning",
903
+ "claude-sonnet-4-20250514"
904
+ ).option("--json", "Emit JSON (default)", true).option("--pretty", "Pretty-print JSON", false).option(
905
+ "--compact",
906
+ "Compact output (summary + step titles + criteria)",
907
+ false
908
+ ).action(async (opts) => {
909
+ try {
910
+ const { runPlanOnly } = await import("../orchestrators/multimodal/harness.js");
911
+ const plan = await runPlanOnly(
912
+ { task: opts.task, repoPath: process.cwd() },
913
+ { plannerModel: opts.plannerModel }
914
+ );
915
+ const typedPlan = plan;
916
+ const compacted = opts.compact ? {
917
+ summary: typedPlan?.summary,
918
+ steps: Array.isArray(typedPlan?.steps) ? typedPlan.steps.map((s) => ({
919
+ id: s.id,
920
+ title: s.title,
921
+ acceptanceCriteria: s.acceptanceCriteria
922
+ })) : [],
923
+ risks: typedPlan?.risks
924
+ } : plan;
925
+ const payload = JSON.stringify(compacted, null, opts.pretty ? 2 : 0);
926
+ console.log(payload);
927
+ } catch (error) {
928
+ console.error("plan failed:", error.message);
929
+ process.exit(1);
930
+ }
931
+ });
590
932
  program.command("dashboard").description("Display monitoring dashboard in terminal").option("-w, --watch", "Auto-refresh dashboard").option("-i, --interval <seconds>", "Refresh interval in seconds", "5").action(async (options) => {
591
933
  const { dashboardCommand } = await import("./commands/dashboard.js");
592
934
  await dashboardCommand.handler(options);
@@ -601,7 +943,7 @@ if (process.argv.length > 2) {
601
943
  }
602
944
  const isMainModule = import.meta.url === `file://${process.argv[1]}` || process.argv[1]?.endsWith("/stackmemory") || process.argv[1]?.endsWith("index.ts") || process.argv[1]?.includes("tsx");
603
945
  if (isMainModule) {
604
- program.parse();
946
+ Promise.all(lazyCommands).then(() => program.parse());
605
947
  }
606
948
  export {
607
949
  program