@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.
- package/README.md +139 -45
- package/bin/codex-sm +6 -0
- 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 +201 -6
- package/dist/src/cli/commands/skills.js.map +2 -2
- package/dist/src/cli/index.js +66 -27
- 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/server.js +16 -10
- 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 +1 -1
- package/dist/src/orchestrators/multimodal/constants.js.map +1 -1
- package/dist/src/orchestrators/multimodal/harness.js +28 -29
- package/dist/src/orchestrators/multimodal/harness.js.map +2 -2
- package/dist/src/orchestrators/multimodal/providers.js +35 -22
- package/dist/src/orchestrators/multimodal/providers.js.map +2 -2
- 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 +12 -5
- package/scripts/install-claude-hooks-auto.js +23 -9
- package/scripts/install-claude-hooks.sh +2 -2
- package/templates/claude-hooks/hooks.json +4 -2
- package/templates/claude-hooks/on-task-complete.js +91 -0
- package/templates/claude-hooks/post-edit-sweep.js +7 -10
- package/templates/claude-hooks/skill-eval.cjs +411 -0
- package/templates/claude-hooks/skill-eval.sh +31 -0
- 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
|
-
- **
|
|
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
|
-
-
|
|
39
|
-
-
|
|
40
|
-
-
|
|
41
|
-
-
|
|
42
|
-
|
|
43
|
-
|
|
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-
|
|
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-
|
|
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
|
-
##
|
|
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
|
-
|
|
188
|
+
Options: `--priority <level>`, `--tag <tag>`, `--dry-run`, `--maxConcurrent <n>`
|
|
137
189
|
|
|
138
190
|
---
|
|
139
191
|
|
|
140
|
-
##
|
|
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
|
-
##
|
|
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
|
|
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
|
-
#
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
#
|
|
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
|
-
|
|
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
package/bin/opencode-sm
CHANGED
|
@@ -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)"
|