@stackmemoryai/stackmemory 0.5.66 → 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 (52) hide show
  1. package/README.md +3 -3
  2. package/bin/codex-sm +6 -0
  3. package/bin/opencode-sm +1 -1
  4. package/dist/src/cli/claude-sm.js +162 -25
  5. package/dist/src/cli/claude-sm.js.map +2 -2
  6. package/dist/src/cli/commands/ping.js +14 -0
  7. package/dist/src/cli/commands/ping.js.map +7 -0
  8. package/dist/src/cli/commands/ralph.js +103 -1
  9. package/dist/src/cli/commands/ralph.js.map +2 -2
  10. package/dist/src/cli/commands/retrieval.js +1 -1
  11. package/dist/src/cli/commands/retrieval.js.map +2 -2
  12. package/dist/src/cli/commands/skills.js +201 -6
  13. package/dist/src/cli/commands/skills.js.map +2 -2
  14. package/dist/src/cli/index.js +66 -27
  15. package/dist/src/cli/index.js.map +2 -2
  16. package/dist/src/core/digest/types.js +1 -1
  17. package/dist/src/core/digest/types.js.map +1 -1
  18. package/dist/src/core/extensions/provider-adapter.js +2 -5
  19. package/dist/src/core/extensions/provider-adapter.js.map +2 -2
  20. package/dist/src/core/retrieval/llm-provider.js +2 -2
  21. package/dist/src/core/retrieval/llm-provider.js.map +1 -1
  22. package/dist/src/core/retrieval/types.js +1 -1
  23. package/dist/src/core/retrieval/types.js.map +1 -1
  24. package/dist/src/features/sweep/pty-wrapper.js +15 -5
  25. package/dist/src/features/sweep/pty-wrapper.js.map +2 -2
  26. package/dist/src/features/workers/tmux-manager.js +71 -0
  27. package/dist/src/features/workers/tmux-manager.js.map +7 -0
  28. package/dist/src/features/workers/worker-registry.js +52 -0
  29. package/dist/src/features/workers/worker-registry.js.map +7 -0
  30. package/dist/src/integrations/linear/webhook-handler.js +82 -0
  31. package/dist/src/integrations/linear/webhook-handler.js.map +2 -2
  32. package/dist/src/integrations/mcp/server.js +16 -10
  33. package/dist/src/integrations/mcp/server.js.map +2 -2
  34. package/dist/src/integrations/ralph/patterns/oracle-worker-pattern.js +2 -2
  35. package/dist/src/integrations/ralph/patterns/oracle-worker-pattern.js.map +2 -2
  36. package/dist/src/orchestrators/multimodal/constants.js +1 -1
  37. package/dist/src/orchestrators/multimodal/constants.js.map +1 -1
  38. package/dist/src/orchestrators/multimodal/harness.js +28 -29
  39. package/dist/src/orchestrators/multimodal/harness.js.map +2 -2
  40. package/dist/src/orchestrators/multimodal/providers.js +35 -22
  41. package/dist/src/orchestrators/multimodal/providers.js.map +2 -2
  42. package/dist/src/skills/claude-skills.js +116 -1
  43. package/dist/src/skills/claude-skills.js.map +2 -2
  44. package/dist/src/skills/linear-task-runner.js +262 -0
  45. package/dist/src/skills/linear-task-runner.js.map +7 -0
  46. package/dist/src/skills/recursive-agent-orchestrator.js +114 -85
  47. package/dist/src/skills/recursive-agent-orchestrator.js.map +2 -2
  48. package/dist/src/skills/spec-generator-skill.js +441 -0
  49. package/dist/src/skills/spec-generator-skill.js.map +7 -0
  50. package/package.json +3 -1
  51. package/scripts/install-claude-hooks.sh +2 -2
  52. package/templates/claude-hooks/post-edit-sweep.js +7 -10
package/README.md CHANGED
@@ -103,7 +103,7 @@ Use these JSON snippets with Claude Code’s MCP “tools/call”. Responses are
103
103
 
104
104
  - Plan only (no code):
105
105
  ```json
106
- {"method":"tools/call","params":{"name":"plan_only","arguments":{"task":"Refactor config loader","plannerModel":"claude-3-5-sonnet-latest"}}}
106
+ {"method":"tools/call","params":{"name":"plan_only","arguments":{"task":"Refactor config loader","plannerModel":"claude-sonnet-4-20250514"}}}
107
107
  ```
108
108
 
109
109
  - Approval‑gated plan (phase 1):
@@ -124,7 +124,7 @@ Use these JSON snippets with Claude Code’s MCP “tools/call”. Responses are
124
124
  ```
125
125
 
126
126
  Env defaults (optional):
127
- - `STACKMEMORY_MM_PLANNER_MODEL` (e.g., `claude-3-5-sonnet-latest` or `claude-3-opus-latest`)
127
+ - `STACKMEMORY_MM_PLANNER_MODEL` (e.g., `claude-sonnet-4-20250514`)
128
128
  - `STACKMEMORY_MM_REVIEWER_MODEL` (defaults to planner if unset)
129
129
  - `STACKMEMORY_MM_IMPLEMENTER` (`codex` or `claude`)
130
130
  - `STACKMEMORY_MM_MAX_ITERS` (e.g., `2`)
@@ -414,7 +414,7 @@ See https://github.com/stackmemoryai/stackmemory/blob/main/docs/roadmap.md for o
414
414
  - [Agent Instructions](./AGENTS.md) - Specific guidance for AI agents working with ML systems
415
415
 
416
416
  ### Documentation
417
-
417
+ - [Vision](./vision.md) - Product vision, principles, roadmap, metrics
418
418
  - [Product Requirements](./PRD.md) - Detailed product specifications
419
419
  - [Technical Architecture](./TECHNICAL_ARCHITECTURE.md) - System design and database schemas
420
420
  - [Beads Integration](./BEADS_INTEGRATION.md) - Git-native memory patterns from Beads ecosystem
package/bin/codex-sm ADDED
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Codex-SM CLI Launcher (ESM)
4
+ * Delegates to built CLI in dist without requiring tsx.
5
+ */
6
+ import('../dist/src/cli/codex-sm.js');
package/bin/opencode-sm CHANGED
@@ -3,4 +3,4 @@
3
3
  * OpenCode-SM CLI Launcher (ESM)
4
4
  * Delegates to built CLI in dist without requiring tsx.
5
5
  */
6
- import('../dist/cli/opencode-sm.js');
6
+ import('../dist/src/cli/opencode-sm.js');
@@ -27,6 +27,22 @@ import {
27
27
  } from "../core/models/model-router.js";
28
28
  import { launchWrapper } from "../features/sweep/pty-wrapper.js";
29
29
  import { FallbackMonitor } from "../core/models/fallback-monitor.js";
30
+ import {
31
+ ensureWorkerStateDir,
32
+ saveRegistry,
33
+ loadRegistry,
34
+ clearRegistry
35
+ } from "../features/workers/worker-registry.js";
36
+ import {
37
+ isTmuxAvailable,
38
+ createTmuxSession,
39
+ sendToPane,
40
+ killTmuxSession,
41
+ attachToSession,
42
+ listPanes,
43
+ sendCtrlC,
44
+ sessionExists
45
+ } from "../features/workers/tmux-manager.js";
30
46
  const DEFAULT_SM_CONFIG = {
31
47
  defaultWorktree: false,
32
48
  defaultSandbox: false,
@@ -156,11 +172,15 @@ class ClaudeSM {
156
172
  }
157
173
  return null;
158
174
  }
159
- gepaProcess = null;
175
+ gepaProcesses = [];
160
176
  startGEPAWatcher() {
161
- const claudeMdPath = fs.existsSync(path.join(process.cwd(), "CLAUDE.md")) ? path.join(process.cwd(), "CLAUDE.md") : null;
162
- if (!claudeMdPath) {
163
- console.log(chalk.gray(" Prompt Forge: disabled (no CLAUDE.md found)"));
177
+ const watchFiles = ["CLAUDE.md", "AGENT.md", "AGENTS.md"].map((f) => path.join(process.cwd(), f)).filter((p) => fs.existsSync(p));
178
+ if (watchFiles.length === 0) {
179
+ console.log(
180
+ chalk.gray(
181
+ " Prompt Forge: disabled (no CLAUDE.md, AGENT.md, or AGENTS.md found)"
182
+ )
183
+ );
164
184
  return;
165
185
  }
166
186
  const gepaPaths = [
@@ -193,29 +213,31 @@ class ClaudeSM {
193
213
  console.log(chalk.gray(" Prompt Forge: disabled (scripts not found)"));
194
214
  return;
195
215
  }
196
- this.gepaProcess = spawn("node", [gepaScript, "watch", claudeMdPath], {
197
- detached: true,
198
- stdio: ["ignore", "pipe", "pipe"],
199
- env: { ...process.env, GEPA_SILENT: "1" }
200
- });
201
- this.gepaProcess.unref();
202
- this.gepaProcess.stdout?.on("data", (data) => {
203
- const output = data.toString().trim();
204
- if (output && !output.includes("Watching")) {
205
- console.log(chalk.magenta(`[GEPA] ${output}`));
206
- }
207
- });
216
+ for (const filePath of watchFiles) {
217
+ const gepaProcess = spawn("node", [gepaScript, "watch", filePath], {
218
+ detached: true,
219
+ stdio: ["ignore", "pipe", "pipe"],
220
+ env: { ...process.env, GEPA_SILENT: "1" }
221
+ });
222
+ gepaProcess.unref();
223
+ this.gepaProcesses.push(gepaProcess);
224
+ gepaProcess.stdout?.on("data", (data) => {
225
+ const output = data.toString().trim();
226
+ if (output && !output.includes("Watching")) {
227
+ console.log(chalk.magenta(`[GEPA] ${output}`));
228
+ }
229
+ });
230
+ }
231
+ const fileNames = watchFiles.map((f) => path.basename(f)).join(", ");
208
232
  console.log(
209
- chalk.cyan(
210
- ` Prompt Forge: watching ${path.basename(claudeMdPath)} for optimization`
211
- )
233
+ chalk.cyan(` Prompt Forge: watching ${fileNames} for optimization`)
212
234
  );
213
235
  }
214
236
  stopGEPAWatcher() {
215
- if (this.gepaProcess) {
216
- this.gepaProcess.kill("SIGTERM");
217
- this.gepaProcess = null;
237
+ for (const proc of this.gepaProcesses) {
238
+ proc.kill("SIGTERM");
218
239
  }
240
+ this.gepaProcesses = [];
219
241
  }
220
242
  ensureGreptileMcp() {
221
243
  const apiKey = process.env["GREPTILE_API_KEY"];
@@ -822,11 +844,17 @@ class ClaudeSM {
822
844
  claudeBin: claudeBin2,
823
845
  claudeArgs
824
846
  });
847
+ return;
825
848
  } catch (error) {
826
- console.error(chalk.red(error.message));
827
- process.exit(1);
849
+ const msg = error.message || "Unknown PTY error";
850
+ console.error(chalk.yellow(`[Sweep disabled] ${msg}`));
851
+ console.log(
852
+ chalk.gray(
853
+ "Falling back to direct Claude launch (no prediction bar)..."
854
+ )
855
+ );
856
+ this.config.useSweep = false;
828
857
  }
829
- return;
830
858
  }
831
859
  console.log(chalk.gray("Starting Claude..."));
832
860
  console.log(chalk.gray("\u2500".repeat(42)));
@@ -1224,6 +1252,115 @@ GREPTILE_API_KEY=${key.trim()}
1224
1252
  console.log(chalk.gray(`
1225
1253
  Saved to ${getConfigPath()}`));
1226
1254
  });
1255
+ program.command("spawn <count>").description("Spawn N parallel Claude workers in tmux panes").option("-t, --task <desc>", "Task description for each worker").option("--worktree", "Create isolated git worktrees per worker").option("--no-sweep", "Disable Sweep predictions").option("--no-attach", "Do not attach to tmux session after creation").action(async (countStr, opts) => {
1256
+ const count = parseInt(countStr, 10);
1257
+ if (isNaN(count) || count < 1 || count > 8) {
1258
+ console.error(chalk.red("Worker count must be between 1 and 8"));
1259
+ process.exit(1);
1260
+ }
1261
+ if (!isTmuxAvailable()) {
1262
+ console.error(chalk.red("tmux is required for parallel workers."));
1263
+ console.log(chalk.gray("Install with: brew install tmux"));
1264
+ process.exit(1);
1265
+ }
1266
+ const sessionId = uuidv4().substring(0, 8);
1267
+ const sessionName = `claude-sm-${sessionId}`;
1268
+ console.log(
1269
+ chalk.blue(`Spawning ${count} workers in tmux session: ${sessionName}`)
1270
+ );
1271
+ createTmuxSession(sessionName, count);
1272
+ const workers = [];
1273
+ const panes = listPanes(sessionName);
1274
+ for (let i = 0; i < count; i++) {
1275
+ const workerId = `w${i}-${uuidv4().substring(0, 6)}`;
1276
+ const stateDir = ensureWorkerStateDir(workerId);
1277
+ const pane = panes[i] || String(i);
1278
+ const parts = ["claude-sm"];
1279
+ if (opts["sweep"] === false) parts.push("--no-sweep");
1280
+ if (opts["worktree"]) parts.push("--worktree");
1281
+ if (opts["task"]) parts.push("--task", `"${opts["task"]}"`);
1282
+ const cmd = parts.join(" ");
1283
+ sendToPane(
1284
+ sessionName,
1285
+ pane,
1286
+ `export SWEEP_INSTANCE_ID=${workerId} SWEEP_STATE_DIR=${stateDir} && ${cmd}`
1287
+ );
1288
+ workers.push({
1289
+ id: workerId,
1290
+ pane,
1291
+ cwd: process.cwd(),
1292
+ startedAt: (/* @__PURE__ */ new Date()).toISOString(),
1293
+ stateDir,
1294
+ task: opts["task"]
1295
+ });
1296
+ console.log(
1297
+ chalk.gray(
1298
+ ` Worker ${i}: ${workerId} (pane ${pane}, state: ${stateDir})`
1299
+ )
1300
+ );
1301
+ }
1302
+ const session = {
1303
+ sessionName,
1304
+ workers,
1305
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
1306
+ };
1307
+ saveRegistry(session);
1308
+ console.log(chalk.green(`
1309
+ Registry saved (${workers.length} workers)`));
1310
+ if (opts["attach"] !== false) {
1311
+ console.log(chalk.gray("Attaching to tmux session..."));
1312
+ attachToSession(sessionName);
1313
+ } else {
1314
+ console.log(
1315
+ chalk.gray(`Attach later with: tmux attach -t ${sessionName}`)
1316
+ );
1317
+ }
1318
+ });
1319
+ const workersCmd = program.command("workers").description("List active workers (default) or manage them");
1320
+ workersCmd.command("list", { isDefault: true }).description("List active workers").action(() => {
1321
+ const session = loadRegistry();
1322
+ if (!session) {
1323
+ console.log(chalk.gray("No active worker session."));
1324
+ return;
1325
+ }
1326
+ const alive = sessionExists(session.sessionName);
1327
+ const status = alive ? chalk.green("ACTIVE") : chalk.red("DEAD");
1328
+ console.log(chalk.blue(`Session: ${session.sessionName} [${status}]`));
1329
+ console.log(chalk.gray(`Created: ${session.createdAt}`));
1330
+ console.log();
1331
+ for (const w of session.workers) {
1332
+ const taskLabel = w.task ? ` task="${w.task}"` : "";
1333
+ console.log(` ${chalk.cyan(w.id)} pane=${w.pane}${taskLabel}`);
1334
+ console.log(chalk.gray(` state: ${w.stateDir}`));
1335
+ }
1336
+ });
1337
+ workersCmd.command("kill [id]").description("Kill entire session or send Ctrl-C to a specific worker").action((id) => {
1338
+ const session = loadRegistry();
1339
+ if (!session) {
1340
+ console.log(chalk.gray("No active worker session."));
1341
+ return;
1342
+ }
1343
+ if (id) {
1344
+ const worker = session.workers.find((w) => w.id === id);
1345
+ if (!worker) {
1346
+ console.error(chalk.red(`Worker ${id} not found.`));
1347
+ process.exit(1);
1348
+ }
1349
+ sendCtrlC(session.sessionName, worker.pane);
1350
+ console.log(
1351
+ chalk.yellow(`Sent Ctrl-C to worker ${id} (pane ${worker.pane})`)
1352
+ );
1353
+ } else {
1354
+ if (sessionExists(session.sessionName)) {
1355
+ killTmuxSession(session.sessionName);
1356
+ console.log(
1357
+ chalk.yellow(`Killed tmux session: ${session.sessionName}`)
1358
+ );
1359
+ }
1360
+ clearRegistry();
1361
+ console.log(chalk.gray("Registry cleared."));
1362
+ }
1363
+ });
1227
1364
  program.option("-w, --worktree", "Create isolated worktree for this instance").option("-W, --no-worktree", "Disable worktree (override default)").option("-r, --remote", "Enable remote mode (WhatsApp for all questions)").option("--no-remote", "Disable remote mode (override default)").option("-n, --notify-done", "Send WhatsApp notification when session ends").option("--no-notify-done", "Disable notification when session ends").option(
1228
1365
  "--whatsapp",
1229
1366
  "Enable WhatsApp mode (auto-start webhook + ngrok + notifications)"