@stackmemoryai/stackmemory 0.5.66 → 0.6.0

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 (58) hide show
  1. package/README.md +139 -45
  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 +12 -5
  51. package/scripts/install-claude-hooks-auto.js +23 -9
  52. package/scripts/install-claude-hooks.sh +2 -2
  53. package/templates/claude-hooks/hooks.json +4 -2
  54. package/templates/claude-hooks/on-task-complete.js +91 -0
  55. package/templates/claude-hooks/post-edit-sweep.js +7 -10
  56. package/templates/claude-hooks/skill-eval.cjs +411 -0
  57. package/templates/claude-hooks/skill-eval.sh +31 -0
  58. package/templates/claude-hooks/skill-rules.json +274 -0
package/README.md CHANGED
@@ -14,7 +14,9 @@ StackMemory is a **production-ready memory runtime** for AI coding tools that pr
14
14
  - **Full Linear integration** with bidirectional sync
15
15
  - **Context persistence** that survives /clear operations
16
16
  - **Hierarchical frame organization** (nested call stack model)
17
- - **490 tests passing** with comprehensive coverage
17
+ - **Skills system** with `/spec` and `/linear-run` for Claude Code
18
+ - **Automatic hooks** for task tracking, Linear sync, and spec progress
19
+ - **498 tests passing** with comprehensive coverage
18
20
 
19
21
  Instead of a linear chat log, StackMemory organizes memory as a **call stack** of scoped work (frames), with intelligent LLM-driven retrieval and team collaboration features.
20
22
 
@@ -34,13 +36,13 @@ Tools forget decisions and constraints between sessions. StackMemory makes conte
34
36
 
35
37
  ## Features (at a glance)
36
38
 
37
- - MCP tools for Claude Code: 26+ tools; context on every request
38
- - Safe branches: worktree isolation with `--worktree` or `-w`
39
- - Persistent context: frames, anchors, decisions, retrieval
40
- - Optional boosts: model routing, prompt optimization (GEPA)
41
- - Integrations: Linear, Greptile, DiffMem, Browser MCP
42
-
43
- See the docs directory for deeper feature guides.
39
+ - **MCP tools** for Claude Code: 26+ tools; context on every request
40
+ - **Skills**: `/spec` (iterative spec generation), `/linear-run` (task execution via RLM)
41
+ - **Hooks**: automatic context save, task tracking, Linear sync, PROMPT_PLAN updates
42
+ - **Prompt Forge**: watches AGENTS.md and CLAUDE.md for prompt optimization (GEPA)
43
+ - **Safe branches**: worktree isolation with `--worktree` or `-w`
44
+ - **Persistent context**: frames, anchors, decisions, retrieval
45
+ - **Integrations**: Linear, Greptile, DiffMem, Browser MCP
44
46
 
45
47
  ---
46
48
 
@@ -103,7 +105,7 @@ Use these JSON snippets with Claude Code’s MCP “tools/call”. Responses are
103
105
 
104
106
  - Plan only (no code):
105
107
  ```json
106
- {"method":"tools/call","params":{"name":"plan_only","arguments":{"task":"Refactor config loader","plannerModel":"claude-3-5-sonnet-latest"}}}
108
+ {"method":"tools/call","params":{"name":"plan_only","arguments":{"task":"Refactor config loader","plannerModel":"claude-sonnet-4-20250514"}}}
107
109
  ```
108
110
 
109
111
  - Approval‑gated plan (phase 1):
@@ -124,20 +126,134 @@ Use these JSON snippets with Claude Code’s MCP “tools/call”. Responses are
124
126
  ```
125
127
 
126
128
  Env defaults (optional):
127
- - `STACKMEMORY_MM_PLANNER_MODEL` (e.g., `claude-3-5-sonnet-latest` or `claude-3-opus-latest`)
129
+ - `STACKMEMORY_MM_PLANNER_MODEL` (e.g., `claude-sonnet-4-20250514`)
128
130
  - `STACKMEMORY_MM_REVIEWER_MODEL` (defaults to planner if unset)
129
131
  - `STACKMEMORY_MM_IMPLEMENTER` (`codex` or `claude`)
130
132
  - `STACKMEMORY_MM_MAX_ITERS` (e.g., `2`)
131
133
 
132
134
  ---
133
135
 
134
- ## Quick Start
136
+ ## Skills System
137
+
138
+ StackMemory ships Claude Code skills that integrate directly into your workflow. Skills are invoked via `/skill-name` in Claude Code or `stackmemory skills <name>` from the CLI.
139
+
140
+ ### Spec Generator (`/spec`)
141
+
142
+ Generates iterative spec documents following a 4-doc progressive chain. Each document reads previous ones from disk for context.
143
+
144
+ ```
145
+ ONE_PAGER.md → DEV_SPEC.md → PROMPT_PLAN.md → AGENTS.md
146
+ (standalone) (reads 1) (reads 1+2) (reads 1+2+3)
147
+ ```
148
+
149
+ ```bash
150
+ # Generate specs in order
151
+ /spec one-pager "My App" # Problem, audience, core flow, MVP
152
+ /spec dev-spec # Architecture, tech stack, APIs
153
+ /spec prompt-plan # TDD stages A-G with checkboxes
154
+ /spec agents # Agent guardrails and responsibilities
155
+
156
+ # Manage progress
157
+ /spec list # Show existing specs
158
+ /spec update prompt-plan "auth" # Check off matching items
159
+ /spec validate prompt-plan # Check completion status
160
+
161
+ # CLI equivalent
162
+ stackmemory skills spec one-pager "My App"
163
+ ```
164
+
165
+ Output goes to `docs/specs/`. Use `--force` to regenerate an existing spec.
166
+
167
+ ### Linear Task Runner (`/linear-run`)
168
+
169
+ Pulls tasks from Linear, executes them via the RLM orchestrator (8 subagent types), and syncs results back.
170
+
171
+ ```bash
172
+ /linear-run next # Execute next todo task
173
+ /linear-run next --priority high # Filter by priority
174
+ /linear-run all # Execute all pending tasks
175
+ /linear-run all --dry-run # Preview without executing
176
+ /linear-run task STA-123 # Run a specific task
177
+ /linear-run preview # Show execution plan
178
+
179
+ # CLI equivalent
180
+ stackmemory ralph linear next
181
+ ```
182
+
183
+ On task completion:
184
+ 1. Marks the Linear task as `done`
185
+ 2. Auto-checks matching PROMPT_PLAN items
186
+ 3. Syncs metrics (tokens, cost, tests) back to Linear
135
187
 
136
- See above for install and minimal usage. For advanced options, see Setup.
188
+ Options: `--priority <level>`, `--tag <tag>`, `--dry-run`, `--maxConcurrent <n>`
137
189
 
138
190
  ---
139
191
 
140
- ## 2. Detailed Setup
192
+ ## Hooks (Automatic)
193
+
194
+ StackMemory installs Claude Code hooks that run automatically during your session. Hooks are non-blocking and fail silently to never interrupt your workflow.
195
+
196
+ ### Installed Hooks
197
+
198
+ | Hook | Trigger | What it does |
199
+ |------|---------|-------------|
200
+ | `on-task-complete` | Task marked done | Saves context, syncs Linear (STA-* tasks), auto-checks PROMPT_PLAN items |
201
+ | `on-startup` | Session start | Loads StackMemory context, initializes frame |
202
+ | `on-clear` | `/clear` command | Persists context before clearing |
203
+ | `skill-eval` | User prompt | Scores prompt against 28 skill patterns, recommends relevant skills |
204
+ | `tool-use-trace` | Tool invocation | Logs tool usage for context tracking |
205
+
206
+ ### Skill Evaluation
207
+
208
+ When you type a prompt, the `skill-eval` hook scores it against `skill-rules.json` (28 mapped skills with keyword, pattern, intent, and directory matching). Skills scoring above the threshold (default: 3) are recommended.
209
+
210
+ ```json
211
+ // Example: user types "generate a spec for the auth system"
212
+ // skill-eval recommends:
213
+ {
214
+ "recommendedSkills": [
215
+ { "skillName": "spec-generator", "score": 8 },
216
+ { "skillName": "frame-management", "score": 5 }
217
+ ]
218
+ }
219
+ ```
220
+
221
+ ### Hook Installation
222
+
223
+ Hooks install automatically during `npm install` (with user consent). To install or reinstall manually:
224
+
225
+ ```bash
226
+ # Automatic (prompted during npm install)
227
+ npm install -g @stackmemoryai/stackmemory
228
+
229
+ # Manual install
230
+ stackmemory hooks install
231
+
232
+ # Skip hooks (CI/non-interactive)
233
+ STACKMEMORY_AUTO_HOOKS=true npm install -g @stackmemoryai/stackmemory
234
+ ```
235
+
236
+ Hooks are stored in `~/.claude/hooks/` and configured via `~/.claude/hooks.json`.
237
+
238
+ ### PROMPT_PLAN Auto-Progress
239
+
240
+ When a task completes (via hook or `/linear-run`), StackMemory fuzzy-matches the task title against unchecked `- [ ]` items in `docs/specs/PROMPT_PLAN.md` and checks them off automatically. One item per task completion, best-effort.
241
+
242
+ ---
243
+
244
+ ## Prompt Forge (GEPA)
245
+
246
+ When launching via `claude-sm`, StackMemory watches `CLAUDE.md`, `AGENT.md`, and `AGENTS.md` for changes. On file modification, the GEPA optimizer analyzes content and suggests improvements for prompt clarity and structure. Runs as a detached background process.
247
+
248
+ ```bash
249
+ # Launch with Prompt Forge active
250
+ claude-sm
251
+
252
+ # Status shown in terminal:
253
+ # Prompt Forge: watching CLAUDE.md, AGENTS.md for optimization
254
+ ```
255
+
256
+ ---
141
257
 
142
258
  ### Install
143
259
 
@@ -180,15 +296,11 @@ stackmemory doctor
180
296
 
181
297
  Checks project initialization, database integrity, MCP config, and suggests fixes.
182
298
 
183
- ---
184
-
185
- ## 3. Advanced Setup
186
-
187
- See https://github.com/stackmemoryai/stackmemory/blob/main/docs/setup.md for advanced options (hosted mode, ChromaDB, manual MCP config, and available tools).
299
+ See [docs/setup.md](https://github.com/stackmemoryai/stackmemory/blob/main/docs/setup.md) for advanced options (hosted mode, ChromaDB, manual MCP config).
188
300
 
189
301
  ---
190
302
 
191
- ## 2. Open-Source Local Mode
303
+ ## Open-Source Local Mode
192
304
 
193
305
  ### Step 1: Clone & Build
194
306
 
@@ -285,35 +397,17 @@ StackMemory implements an intelligent two-tier storage architecture:
285
397
 
286
398
  ## Claude Code Integration
287
399
 
288
- StackMemory can automatically save context when using Claude Code, so your AI assistant has access to previous context and decisions.
289
-
290
- ### Quick Setup
400
+ StackMemory integrates with Claude Code via MCP tools, skills, and hooks. See the [Hooks](#hooks-automatic) and [Skills](#skills-system) sections above.
291
401
 
292
402
  ```bash
293
- # Add alias
294
- echo 'alias claude="~/Dev/stackmemory/scripts/claude-code-wrapper.sh"' >> ~/.zshrc
295
- source ~/.zshrc
296
-
297
- # Use: claude (saves context on exit)
403
+ # Full setup (one-time)
404
+ npm install -g @stackmemoryai/stackmemory # installs hooks automatically
405
+ cd your-project && stackmemory init # init project
406
+ stackmemory setup-mcp # configure MCP
407
+ stackmemory doctor # verify everything works
298
408
  ```
299
409
 
300
- ### Integration Methods
301
-
302
- ```bash
303
- # 1. Shell wrapper (recommended)
304
- claude [--auto-sync] [--sync-interval=10]
305
-
306
- # 2. Linear auto-sync daemon
307
- ./scripts/linear-auto-sync.sh start [interval]
308
-
309
- # 3. Background daemon
310
- ./scripts/stackmemory-daemon.sh [interval] &
311
-
312
- # 4. Git hooks
313
- ./scripts/setup-git-hooks.sh
314
- ```
315
-
316
- **Features:** Auto-save on exit, Linear sync, runs only in StackMemory projects, configurable sync intervals.
410
+ Additional integration methods: shell wrapper (`claude-sm`), Linear auto-sync daemon, background daemon, git hooks. See [docs/setup.md](https://github.com/stackmemoryai/stackmemory/blob/main/docs/setup.md).
317
411
 
318
412
  ## RLM (Recursive Language Model) Orchestration
319
413
 
@@ -414,7 +508,7 @@ See https://github.com/stackmemoryai/stackmemory/blob/main/docs/roadmap.md for o
414
508
  - [Agent Instructions](./AGENTS.md) - Specific guidance for AI agents working with ML systems
415
509
 
416
510
  ### Documentation
417
-
511
+ - [Vision](./vision.md) - Product vision, principles, roadmap, metrics
418
512
  - [Product Requirements](./PRD.md) - Detailed product specifications
419
513
  - [Technical Architecture](./TECHNICAL_ARCHITECTURE.md) - System design and database schemas
420
514
  - [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)"