@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
@@ -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)"