@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
|
@@ -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
|
-
|
|
175
|
+
gepaProcesses = [];
|
|
160
176
|
startGEPAWatcher() {
|
|
161
|
-
const
|
|
162
|
-
if (
|
|
163
|
-
console.log(
|
|
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
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
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
|
-
|
|
216
|
-
|
|
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
|
-
|
|
827
|
-
|
|
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)"
|