@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.
- package/README.md +69 -346
- package/bin/claude-sm +1 -1
- package/bin/claude-smd +1 -1
- package/bin/codex-sm +6 -0
- package/bin/codex-smd +1 -1
- package/bin/opencode-sm +1 -1
- package/dist/src/cli/claude-sm.js +162 -25
- package/dist/src/cli/claude-sm.js.map +2 -2
- package/dist/src/cli/commands/ping.js +14 -0
- package/dist/src/cli/commands/ping.js.map +7 -0
- package/dist/src/cli/commands/ralph.js +103 -1
- package/dist/src/cli/commands/ralph.js.map +2 -2
- package/dist/src/cli/commands/retrieval.js +1 -1
- package/dist/src/cli/commands/retrieval.js.map +2 -2
- package/dist/src/cli/commands/skills.js +300 -1
- package/dist/src/cli/commands/skills.js.map +2 -2
- package/dist/src/cli/index.js +362 -20
- package/dist/src/cli/index.js.map +2 -2
- package/dist/src/core/digest/types.js +1 -1
- package/dist/src/core/digest/types.js.map +1 -1
- package/dist/src/core/extensions/provider-adapter.js +2 -5
- package/dist/src/core/extensions/provider-adapter.js.map +2 -2
- package/dist/src/core/retrieval/llm-provider.js +2 -2
- package/dist/src/core/retrieval/llm-provider.js.map +1 -1
- package/dist/src/core/retrieval/types.js +1 -1
- package/dist/src/core/retrieval/types.js.map +1 -1
- package/dist/src/features/sweep/pty-wrapper.js +15 -5
- package/dist/src/features/sweep/pty-wrapper.js.map +2 -2
- package/dist/src/features/workers/tmux-manager.js +71 -0
- package/dist/src/features/workers/tmux-manager.js.map +7 -0
- package/dist/src/features/workers/worker-registry.js +52 -0
- package/dist/src/features/workers/worker-registry.js.map +7 -0
- package/dist/src/integrations/linear/webhook-handler.js +82 -0
- package/dist/src/integrations/linear/webhook-handler.js.map +2 -2
- package/dist/src/integrations/mcp/pending-utils.js +33 -0
- package/dist/src/integrations/mcp/pending-utils.js.map +7 -0
- package/dist/src/integrations/mcp/server.js +571 -1
- package/dist/src/integrations/mcp/server.js.map +2 -2
- package/dist/src/integrations/ralph/patterns/oracle-worker-pattern.js +2 -2
- package/dist/src/integrations/ralph/patterns/oracle-worker-pattern.js.map +2 -2
- package/dist/src/orchestrators/multimodal/constants.js +17 -0
- package/dist/src/orchestrators/multimodal/constants.js.map +7 -0
- package/dist/src/orchestrators/multimodal/harness.js +292 -0
- package/dist/src/orchestrators/multimodal/harness.js.map +7 -0
- package/dist/src/orchestrators/multimodal/providers.js +98 -0
- package/dist/src/orchestrators/multimodal/providers.js.map +7 -0
- package/dist/src/orchestrators/multimodal/types.js +5 -0
- package/dist/src/orchestrators/multimodal/types.js.map +7 -0
- package/dist/src/orchestrators/multimodal/utils.js +25 -0
- package/dist/src/orchestrators/multimodal/utils.js.map +7 -0
- package/dist/src/skills/claude-skills.js +116 -1
- package/dist/src/skills/claude-skills.js.map +2 -2
- package/dist/src/skills/linear-task-runner.js +262 -0
- package/dist/src/skills/linear-task-runner.js.map +7 -0
- package/dist/src/skills/recursive-agent-orchestrator.js +114 -85
- package/dist/src/skills/recursive-agent-orchestrator.js.map +2 -2
- package/dist/src/skills/spec-generator-skill.js +441 -0
- package/dist/src/skills/spec-generator-skill.js.map +7 -0
- package/package.json +14 -9
- package/scripts/claude-code-wrapper.sh +18 -30
- package/scripts/demos/ralph-integration-demo.ts +14 -13
- package/scripts/demos/trace-demo.ts +7 -21
- package/scripts/demos/trace-test.ts +20 -8
- package/scripts/install-claude-hooks.sh +2 -2
- package/scripts/verify-dist.cjs +83 -0
- package/templates/claude-hooks/post-edit-sweep.js +7 -10
package/dist/src/cli/index.js
CHANGED
|
@@ -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
|
-
|
|
187
|
-
|
|
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 =
|
|
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
|
-
|
|
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
|
-
|
|
563
|
-
(
|
|
564
|
-
|
|
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
|
-
|
|
569
|
-
(
|
|
570
|
-
|
|
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
|
-
|
|
582
|
-
(
|
|
583
|
-
|
|
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
|