@stackmemoryai/stackmemory 1.5.3 → 1.5.4
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/dist/src/cli/commands/orchestrate.js +7 -2
- package/dist/src/cli/commands/orchestrator.js +189 -4
- package/package.json +1 -1
- package/scripts/gepa/.before-optimize.md +89 -53
- package/scripts/gepa/generations/gen-000/baseline.md +8 -0
- package/scripts/gepa/generations/gen-001/baseline.md +8 -0
- package/scripts/gepa/generations/gen-001/variant-a.md +81 -81
- package/scripts/gepa/generations/gen-001/variant-b.md +78 -64
- package/scripts/gepa/generations/gen-001/variant-c.md +54 -74
- package/scripts/gepa/generations/gen-001/variant-d.md +79 -113
- package/scripts/gepa/results/eval-1-baseline.json +74 -74
- package/scripts/gepa/results/eval-1-variant-a.json +64 -64
- package/scripts/gepa/results/eval-1-variant-b.json +65 -65
- package/scripts/gepa/results/eval-1-variant-c.json +78 -58
- package/scripts/gepa/results/eval-1-variant-d.json +77 -57
- package/scripts/gepa/state.json +14 -14
|
@@ -346,7 +346,11 @@ function createConductorCommands() {
|
|
|
346
346
|
"--in-review <state>",
|
|
347
347
|
"State name for completed review",
|
|
348
348
|
"In Review"
|
|
349
|
-
).option("--poll <ms>", "Polling interval in milliseconds", "30000").option("--concurrency <n>", "Max concurrent agents", "3").option("--workspace-root <path>", "Workspace root directory").option("--repo <path>", "Git repo root for worktrees", process.cwd()).option("--branch <name>", "Base branch for worktrees", "main").option("--retries <n>", "Max retries per issue", "1").option("--turn-timeout <ms>", "Agent turn timeout in ms", "3600000").
|
|
349
|
+
).option("--poll <ms>", "Polling interval in milliseconds", "30000").option("--concurrency <n>", "Max concurrent agents", "3").option("--workspace-root <path>", "Workspace root directory").option("--repo <path>", "Git repo root for worktrees", process.cwd()).option("--branch <name>", "Base branch for worktrees", "main").option("--retries <n>", "Max retries per issue", "1").option("--turn-timeout <ms>", "Agent turn timeout in ms", "3600000").option(
|
|
350
|
+
"--mode <mode>",
|
|
351
|
+
'Agent mode: "cli" (claude -p, session auth) or "adapter" (JSON-RPC, API key)',
|
|
352
|
+
"cli"
|
|
353
|
+
).action(async (options) => {
|
|
350
354
|
const conductor = new Conductor({
|
|
351
355
|
teamId: options.team,
|
|
352
356
|
activeStates: options.states.split(",").map((s) => s.trim()),
|
|
@@ -358,7 +362,8 @@ function createConductorCommands() {
|
|
|
358
362
|
repoRoot: options.repo,
|
|
359
363
|
baseBranch: options.branch,
|
|
360
364
|
maxRetries: parseInt(options.retries, 10),
|
|
361
|
-
turnTimeoutMs: parseInt(options.turnTimeout, 10)
|
|
365
|
+
turnTimeoutMs: parseInt(options.turnTimeout, 10),
|
|
366
|
+
agentMode: options.mode === "adapter" ? "adapter" : "cli"
|
|
362
367
|
});
|
|
363
368
|
await conductor.start();
|
|
364
369
|
});
|
|
@@ -65,6 +65,35 @@ class TeeTransform extends Transform {
|
|
|
65
65
|
callback();
|
|
66
66
|
}
|
|
67
67
|
}
|
|
68
|
+
function inferPhaseFromStreamJson(event) {
|
|
69
|
+
if (event.type !== "assistant") return null;
|
|
70
|
+
const message = event.message;
|
|
71
|
+
const content = message?.content || [];
|
|
72
|
+
for (const block of content) {
|
|
73
|
+
if (block.type !== "tool_use") continue;
|
|
74
|
+
const toolLower = (block.name || "").toLowerCase();
|
|
75
|
+
if (toolLower.includes("read") || toolLower.includes("glob") || toolLower.includes("grep") || toolLower.includes("search")) {
|
|
76
|
+
return "reading";
|
|
77
|
+
}
|
|
78
|
+
if (toolLower.includes("todowrite") || toolLower.includes("todo")) {
|
|
79
|
+
return "planning";
|
|
80
|
+
}
|
|
81
|
+
if (toolLower.includes("edit") || toolLower.includes("write") || toolLower.includes("bash")) {
|
|
82
|
+
if (toolLower === "bash") {
|
|
83
|
+
const input = block.input;
|
|
84
|
+
const command = (input?.command ?? "").toLowerCase();
|
|
85
|
+
if (command.includes("git commit") || command.includes("git add")) {
|
|
86
|
+
return "committing";
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
return "implementing";
|
|
90
|
+
}
|
|
91
|
+
if (toolLower.includes("test")) {
|
|
92
|
+
return "testing";
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
return null;
|
|
96
|
+
}
|
|
68
97
|
function inferPhase(msg) {
|
|
69
98
|
const method = msg.method;
|
|
70
99
|
const params = msg.params;
|
|
@@ -85,7 +114,8 @@ function inferPhase(msg) {
|
|
|
85
114
|
}
|
|
86
115
|
}
|
|
87
116
|
if (method === "item/commandExecution/started") {
|
|
88
|
-
const
|
|
117
|
+
const args = params?.arguments;
|
|
118
|
+
const command = (args?.command ?? params?.command) || "";
|
|
89
119
|
if (command.includes("git commit") || command.includes("git add")) {
|
|
90
120
|
return "committing";
|
|
91
121
|
}
|
|
@@ -110,7 +140,8 @@ const DEFAULT_CONFIG = {
|
|
|
110
140
|
),
|
|
111
141
|
turnTimeoutMs: 36e5,
|
|
112
142
|
maxRetries: 1,
|
|
113
|
-
hookTimeoutMs: 6e4
|
|
143
|
+
hookTimeoutMs: 6e4,
|
|
144
|
+
agentMode: "cli"
|
|
114
145
|
};
|
|
115
146
|
class Conductor {
|
|
116
147
|
config;
|
|
@@ -642,13 +673,167 @@ class Conductor {
|
|
|
642
673
|
return identifier.replace(/[^A-Za-z0-9._-]/g, "_");
|
|
643
674
|
}
|
|
644
675
|
// ── Agent Execution ──
|
|
645
|
-
runAgent(issue, run) {
|
|
676
|
+
async runAgent(issue, run) {
|
|
677
|
+
if (this.config.agentMode === "cli") {
|
|
678
|
+
try {
|
|
679
|
+
return await this.runAgentCLI(issue, run);
|
|
680
|
+
} catch (err) {
|
|
681
|
+
const msg = err.message || "";
|
|
682
|
+
if (msg.includes("usage limits") || msg.includes("rate_limit") || msg.includes("overloaded")) {
|
|
683
|
+
logger.warn("CLI mode hit limits, falling back to adapter", {
|
|
684
|
+
identifier: issue.identifier,
|
|
685
|
+
error: msg.slice(0, 200)
|
|
686
|
+
});
|
|
687
|
+
run.toolCalls = 0;
|
|
688
|
+
run.filesModified = 0;
|
|
689
|
+
run.tokensUsed = 0;
|
|
690
|
+
run.phase = "reading";
|
|
691
|
+
return this.runAgentAdapter(issue, run);
|
|
692
|
+
}
|
|
693
|
+
throw err;
|
|
694
|
+
}
|
|
695
|
+
}
|
|
696
|
+
return this.runAgentAdapter(issue, run);
|
|
697
|
+
}
|
|
698
|
+
/**
|
|
699
|
+
* CLI mode: spawn `claude -p --output-format stream-json` directly.
|
|
700
|
+
* Uses whatever auth the environment provides (session quota, API key, etc).
|
|
701
|
+
*/
|
|
702
|
+
runAgentCLI(issue, run) {
|
|
703
|
+
return new Promise((resolve, reject) => {
|
|
704
|
+
const prompt = this.buildPrompt(issue, run.attempt);
|
|
705
|
+
const proc = spawn(
|
|
706
|
+
"claude",
|
|
707
|
+
[
|
|
708
|
+
"-p",
|
|
709
|
+
"--output-format",
|
|
710
|
+
"stream-json",
|
|
711
|
+
"--dangerously-skip-permissions",
|
|
712
|
+
prompt
|
|
713
|
+
],
|
|
714
|
+
{
|
|
715
|
+
cwd: run.workspacePath,
|
|
716
|
+
env: (() => {
|
|
717
|
+
const env = { ...process.env };
|
|
718
|
+
delete env.CLAUDECODE;
|
|
719
|
+
delete env.ANTHROPIC_API_KEY;
|
|
720
|
+
return {
|
|
721
|
+
...env,
|
|
722
|
+
SYMPHONY_WORKSPACE_DIR: run.workspacePath,
|
|
723
|
+
SYMPHONY_ISSUE_ID: issue.id,
|
|
724
|
+
SYMPHONY_ISSUE_IDENTIFIER: issue.identifier,
|
|
725
|
+
SYMPHONY_ATTEMPT: String(run.attempt)
|
|
726
|
+
};
|
|
727
|
+
})(),
|
|
728
|
+
stdio: ["pipe", "pipe", "pipe"]
|
|
729
|
+
}
|
|
730
|
+
);
|
|
731
|
+
run.process = proc;
|
|
732
|
+
const logStream = this.openAgentLogStream(issue.identifier);
|
|
733
|
+
run.logStream = logStream;
|
|
734
|
+
const tee = new TeeTransform(logStream);
|
|
735
|
+
proc.stdout.pipe(tee);
|
|
736
|
+
this.writeAgentStatus(issue.identifier, run);
|
|
737
|
+
let stderr = "";
|
|
738
|
+
let lastResultText = "";
|
|
739
|
+
const timer = setTimeout(() => {
|
|
740
|
+
logger.warn("Agent turn timeout (cli)", {
|
|
741
|
+
identifier: issue.identifier,
|
|
742
|
+
timeoutMs: this.config.turnTimeoutMs
|
|
743
|
+
});
|
|
744
|
+
proc.kill("SIGTERM");
|
|
745
|
+
reject(new Error(`Agent timeout after ${this.config.turnTimeoutMs}ms`));
|
|
746
|
+
}, this.config.turnTimeoutMs);
|
|
747
|
+
let lineBuffer = "";
|
|
748
|
+
tee.on("data", (chunk) => {
|
|
749
|
+
lineBuffer += chunk.toString();
|
|
750
|
+
const lines = lineBuffer.split("\n");
|
|
751
|
+
lineBuffer = lines.pop() || "";
|
|
752
|
+
for (const line of lines) {
|
|
753
|
+
if (!line.trim()) continue;
|
|
754
|
+
try {
|
|
755
|
+
const event = JSON.parse(line);
|
|
756
|
+
const phase = inferPhaseFromStreamJson(event);
|
|
757
|
+
if (phase) {
|
|
758
|
+
run.phase = phase;
|
|
759
|
+
}
|
|
760
|
+
if (event.type === "assistant") {
|
|
761
|
+
const message = event.message;
|
|
762
|
+
const content = message?.content || [];
|
|
763
|
+
for (const block of content) {
|
|
764
|
+
if (block.type === "tool_use") {
|
|
765
|
+
run.toolCalls++;
|
|
766
|
+
const toolLower = (block.name || "").toLowerCase();
|
|
767
|
+
if (toolLower.includes("edit") || toolLower.includes("write")) {
|
|
768
|
+
run.filesModified++;
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
if (block.type === "text" && block.text) {
|
|
772
|
+
run.tokensUsed += Math.ceil(
|
|
773
|
+
block.text.length / 4
|
|
774
|
+
);
|
|
775
|
+
}
|
|
776
|
+
}
|
|
777
|
+
}
|
|
778
|
+
if (event.type === "result" && event.result) {
|
|
779
|
+
lastResultText = typeof event.result === "string" ? event.result : JSON.stringify(event.result);
|
|
780
|
+
}
|
|
781
|
+
if (run.toolCalls % 5 === 0 || phase) {
|
|
782
|
+
this.writeAgentStatus(issue.identifier, run);
|
|
783
|
+
}
|
|
784
|
+
} catch {
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
});
|
|
788
|
+
proc.stderr.on("data", (data) => {
|
|
789
|
+
stderr += data.toString();
|
|
790
|
+
const lines = data.toString().split("\n").filter((l) => l.trim());
|
|
791
|
+
for (const line of lines) {
|
|
792
|
+
logger.debug(`[${issue.identifier}] ${line}`);
|
|
793
|
+
}
|
|
794
|
+
});
|
|
795
|
+
proc.on("error", (err) => {
|
|
796
|
+
clearTimeout(timer);
|
|
797
|
+
reject(new Error(`Failed to spawn claude: ${err.message}`));
|
|
798
|
+
});
|
|
799
|
+
proc.on("close", (code) => {
|
|
800
|
+
clearTimeout(timer);
|
|
801
|
+
run.process = null;
|
|
802
|
+
if (run.logStream && !run.logStream.destroyed) {
|
|
803
|
+
run.logStream.end();
|
|
804
|
+
}
|
|
805
|
+
this.writeAgentStatus(issue.identifier, run);
|
|
806
|
+
if (code === 0) {
|
|
807
|
+
logger.info("Agent completed (cli)", {
|
|
808
|
+
identifier: issue.identifier,
|
|
809
|
+
toolCalls: run.toolCalls,
|
|
810
|
+
resultLength: lastResultText.length
|
|
811
|
+
});
|
|
812
|
+
resolve();
|
|
813
|
+
} else {
|
|
814
|
+
reject(
|
|
815
|
+
new Error(
|
|
816
|
+
`Claude exited with code ${code}: ${stderr.slice(0, 500)}`
|
|
817
|
+
)
|
|
818
|
+
);
|
|
819
|
+
}
|
|
820
|
+
});
|
|
821
|
+
});
|
|
822
|
+
}
|
|
823
|
+
/**
|
|
824
|
+
* Adapter mode: spawn claude-app-server.cjs via JSON-RPC protocol.
|
|
825
|
+
* Uses ANTHROPIC_API_KEY for auth.
|
|
826
|
+
*/
|
|
827
|
+
runAgentAdapter(issue, run) {
|
|
646
828
|
return new Promise((resolve, reject) => {
|
|
647
829
|
const prompt = this.buildPrompt(issue, run.attempt);
|
|
830
|
+
const env = { ...process.env };
|
|
831
|
+
delete env.CLAUDECODE;
|
|
832
|
+
delete env.ANTHROPIC_API_KEY;
|
|
648
833
|
const proc = spawn("node", [this.config.appServerPath], {
|
|
649
834
|
cwd: run.workspacePath,
|
|
650
835
|
env: {
|
|
651
|
-
...
|
|
836
|
+
...env,
|
|
652
837
|
SYMPHONY_WORKSPACE_DIR: run.workspacePath,
|
|
653
838
|
SYMPHONY_ISSUE_ID: issue.id,
|
|
654
839
|
SYMPHONY_ISSUE_IDENTIFIER: issue.identifier,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackmemoryai/stackmemory",
|
|
3
|
-
"version": "1.5.
|
|
3
|
+
"version": "1.5.4",
|
|
4
4
|
"description": "Lossless, project-scoped memory for AI coding tools. Durable context across sessions with 56 MCP tools, FTS5 search, conductor orchestrator, loop/watch monitoring, snapshot capture, pre-flight overlap checks, Claude/Codex/OpenCode wrappers, Linear sync, and automatic hooks.",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=20.0.0",
|
|
@@ -21,26 +21,26 @@ docs/ # Documentation
|
|
|
21
21
|
|
|
22
22
|
## Key Files
|
|
23
23
|
|
|
24
|
-
- Entry: src/cli/index.ts
|
|
25
|
-
- MCP Server: src/integrations/mcp/server.ts
|
|
26
|
-
- Frame Manager: src/core/context/frame-manager.ts
|
|
27
|
-
- Database: src/core/database/sqlite-adapter.ts
|
|
28
|
-
|
|
29
|
-
##
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
- linear_integration.md
|
|
33
|
-
- mcp_server.md
|
|
34
|
-
- database_storage.md
|
|
35
|
-
- claude_hooks.md
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
- principles.md
|
|
39
|
-
- architecture.md
|
|
40
|
-
- SPEC.md
|
|
41
|
-
- API_REFERENCE.md
|
|
42
|
-
- DEVELOPMENT.md
|
|
43
|
-
- SETUP.md
|
|
24
|
+
- Entry: `src/cli/index.ts`
|
|
25
|
+
- MCP Server: `src/integrations/mcp/server.ts`
|
|
26
|
+
- Frame Manager: `src/core/context/frame-manager.ts`
|
|
27
|
+
- Database: `src/core/database/sqlite-adapter.ts`
|
|
28
|
+
|
|
29
|
+
## Reference Docs
|
|
30
|
+
|
|
31
|
+
`agent_docs/` (quick reference):
|
|
32
|
+
- `linear_integration.md` — Linear sync
|
|
33
|
+
- `mcp_server.md` — MCP tools
|
|
34
|
+
- `database_storage.md` — Storage
|
|
35
|
+
- `claude_hooks.md` — Hooks
|
|
36
|
+
|
|
37
|
+
`docs/` (full documentation):
|
|
38
|
+
- `principles.md` — Agent programming paradigm
|
|
39
|
+
- `architecture.md` — Extension model and browser sandbox
|
|
40
|
+
- `SPEC.md` — Technical specification
|
|
41
|
+
- `API_REFERENCE.md` — API docs
|
|
42
|
+
- `DEVELOPMENT.md` — Dev guide
|
|
43
|
+
- `SETUP.md` — Installation
|
|
44
44
|
|
|
45
45
|
## Commands
|
|
46
46
|
|
|
@@ -53,49 +53,53 @@ npm run test:run # Run tests once
|
|
|
53
53
|
npm run linear:sync # Sync with Linear
|
|
54
54
|
|
|
55
55
|
# StackMemory CLI
|
|
56
|
-
stackmemory capture
|
|
57
|
-
stackmemory restore
|
|
56
|
+
stackmemory capture # Save session state for handoff
|
|
57
|
+
stackmemory restore # Restore from captured state
|
|
58
|
+
stackmemory snapshot save # Post-run context snapshot (alias: snap)
|
|
59
|
+
stackmemory snapshot list # List recent snapshots
|
|
60
|
+
stackmemory preflight # File overlap check for parallel tasks (alias: pf)
|
|
61
|
+
stackmemory conductor start # Autonomous Linear→worktree→agent orchestrator
|
|
58
62
|
```
|
|
59
63
|
|
|
60
64
|
## Working Directory
|
|
61
65
|
|
|
62
|
-
- PRIMARY:
|
|
66
|
+
- PRIMARY: `/Users/jwu/Dev/stackmemory`
|
|
63
67
|
- ALLOWED: All subdirectories
|
|
64
|
-
- TEMP:
|
|
68
|
+
- TEMP: `/tmp` for temporary operations
|
|
65
69
|
|
|
66
70
|
## Validation (MUST DO)
|
|
67
71
|
|
|
68
|
-
|
|
69
|
-
1. `npm run lint` - fix any errors AND warnings
|
|
70
|
-
2. `npm run test:run` - verify no regressions
|
|
71
|
-
3. `npm run build` - ensure compilation
|
|
72
|
-
4. Run code to verify it works
|
|
72
|
+
Run these checks after every code change — in order:
|
|
73
73
|
|
|
74
|
-
|
|
74
|
+
1. `npm run lint` — fix all errors and warnings
|
|
75
|
+
2. `npm run test:run` — confirm no regressions
|
|
76
|
+
3. `npm run build` — confirm compilation succeeds
|
|
77
|
+
4. Execute the code and verify it works
|
|
78
|
+
|
|
79
|
+
Test coverage rules:
|
|
75
80
|
- New features require tests in `src/**/__tests__/`
|
|
76
|
-
- Maintain or improve coverage
|
|
81
|
+
- Maintain or improve coverage — no untested code paths
|
|
77
82
|
- Critical paths: context management, handoff, Linear sync
|
|
78
83
|
|
|
79
|
-
Never
|
|
84
|
+
**Never:** assume success | skip testing | use mock data as fallback
|
|
80
85
|
|
|
81
86
|
## Git Rules (CRITICAL)
|
|
82
87
|
|
|
83
|
-
- NEVER use `--no-verify` on git push or commit
|
|
84
|
-
-
|
|
85
|
-
-
|
|
86
|
-
-
|
|
87
|
-
-
|
|
88
|
-
- Branch naming: `feature/STA-XXX-description` | `fix/STA-XXX-description` | `chore/description`
|
|
88
|
+
- NEVER use `--no-verify` on `git push` or `git commit`
|
|
89
|
+
- Fix lint/test errors before pushing — never bypass pre-push hooks
|
|
90
|
+
- Run `npm run lint && npm run test:run` before every push
|
|
91
|
+
- Commit format: `type(scope): message`
|
|
92
|
+
- Branch format: `feature/STA-XXX-description` | `fix/STA-XXX-description` | `chore/description`
|
|
89
93
|
|
|
90
94
|
## Task Management
|
|
91
95
|
|
|
92
|
-
- Use TodoWrite for 3+ steps or multiple requests
|
|
93
|
-
- Keep one task in_progress at a time
|
|
94
|
-
- Update task status immediately
|
|
96
|
+
- Use TodoWrite for 3+ steps or multiple concurrent requests
|
|
97
|
+
- Keep exactly one task `in_progress` at a time
|
|
98
|
+
- Update task status immediately when complete
|
|
95
99
|
|
|
96
100
|
## Security
|
|
97
101
|
|
|
98
|
-
|
|
102
|
+
Never hardcode secrets — always use `process.env` with `dotenv/config`:
|
|
99
103
|
|
|
100
104
|
```javascript
|
|
101
105
|
import 'dotenv/config';
|
|
@@ -106,18 +110,18 @@ if (!API_KEY) {
|
|
|
106
110
|
}
|
|
107
111
|
```
|
|
108
112
|
|
|
109
|
-
|
|
110
|
-
1.
|
|
111
|
-
2.
|
|
112
|
-
3.
|
|
113
|
+
Check for API keys in this order:
|
|
114
|
+
1. `.env`
|
|
115
|
+
2. `.env.local`
|
|
116
|
+
3. `~/.zshrc`
|
|
113
117
|
4. Process environment
|
|
114
118
|
|
|
115
|
-
|
|
119
|
+
Block these secret patterns: `lin_api_*` | `lin_oauth_*` | `sk-*` | `npm_*`
|
|
116
120
|
|
|
117
121
|
## Deploy
|
|
118
122
|
|
|
119
123
|
```bash
|
|
120
|
-
# npm publish (
|
|
124
|
+
# npm publish (NPM_TOKEN from .env — no OTP required)
|
|
121
125
|
git stash -- scripts/gepa/ # stash GEPA state (dirties working tree)
|
|
122
126
|
NPM_TOKEN=$(grep '^NPM_TOKEN=' .env | cut -d= -f2) \
|
|
123
127
|
npm publish --registry https://registry.npmjs.org/ \
|
|
@@ -127,14 +131,46 @@ git stash pop # restore GEPA state
|
|
|
127
131
|
# Railway
|
|
128
132
|
railway up
|
|
129
133
|
|
|
130
|
-
# Pre-publish
|
|
134
|
+
# Pre-publish requires clean git status — always stash GEPA files first
|
|
131
135
|
```
|
|
132
136
|
|
|
137
|
+
## Task Delegation Model
|
|
138
|
+
|
|
139
|
+
Match effort to task complexity:
|
|
140
|
+
|
|
141
|
+
**AUTOMATE** — Execute immediately; lint+test is sufficient:
|
|
142
|
+
- CRUD, boilerplate, formatting, simple transforms
|
|
143
|
+
- Adding a handler following an existing switch/case pattern
|
|
144
|
+
- Config additions (env var, feature flag)
|
|
145
|
+
|
|
146
|
+
**STANDARD** — Normal workflow; lint+test+build:
|
|
147
|
+
- Feature implementation, bug fixes, refactoring
|
|
148
|
+
- New tests, documentation updates
|
|
149
|
+
- Integration wiring (adding handler to `server.ts` dispatch)
|
|
150
|
+
|
|
151
|
+
**CAREFUL** — Review approach before writing code:
|
|
152
|
+
- API/schema changes, database migrations, auth flows
|
|
153
|
+
- New integration patterns (MCP tools, webhook handlers)
|
|
154
|
+
- Changes to `frame-manager`, `sqlite-adapter`, or daemon lifecycle
|
|
155
|
+
- Anything touching error handling chains
|
|
156
|
+
|
|
157
|
+
**ARCHITECT** — Require plan mode; explore existing patterns first:
|
|
158
|
+
- New service boundaries or system integrations
|
|
159
|
+
- Performance-critical paths (FTS5 queries, search scoring)
|
|
160
|
+
- Breaking changes to MCP protocol or CLI interface
|
|
161
|
+
|
|
162
|
+
**HUMAN** — Get explicit user approval before any changes:
|
|
163
|
+
- Security-critical decisions, secret handling
|
|
164
|
+
- Irreversible operations (data migrations, schema drops)
|
|
165
|
+
- Publishing (`npm publish`, Railway deploy)
|
|
166
|
+
|
|
167
|
+
Don't over-engineer AUTOMATE tasks. Don't under-review CAREFUL ones.
|
|
168
|
+
|
|
133
169
|
## Workflow
|
|
134
170
|
|
|
135
|
-
- Check
|
|
136
|
-
- Run npm run linear:sync after
|
|
171
|
+
- Check `.env` for API keys before asking the user
|
|
172
|
+
- Run `npm run linear:sync` after completing tasks
|
|
137
173
|
- Use browser MCP for visual testing
|
|
138
|
-
-
|
|
174
|
+
- On session start: review recent commits and `stackmemory.json`
|
|
139
175
|
- Use subagents for multi-step tasks
|
|
140
|
-
- Ask 1
|
|
176
|
+
- Ask 1–3 clarifying questions for ambiguous requests (one at a time)
|
|
@@ -25,6 +25,10 @@ docs/ # Documentation
|
|
|
25
25
|
- MCP Server: src/integrations/mcp/server.ts
|
|
26
26
|
- Frame Manager: src/core/context/frame-manager.ts
|
|
27
27
|
- Database: src/core/database/sqlite-adapter.ts
|
|
28
|
+
- Snapshot: src/core/worktree/capture.ts
|
|
29
|
+
- Preflight: src/core/worktree/preflight.ts
|
|
30
|
+
- Conductor: src/cli/commands/orchestrator.ts
|
|
31
|
+
- Shared Utils: src/core/utils/{git,text,fs}.ts
|
|
28
32
|
|
|
29
33
|
## Detailed Guides
|
|
30
34
|
|
|
@@ -55,6 +59,10 @@ npm run linear:sync # Sync with Linear
|
|
|
55
59
|
# StackMemory CLI
|
|
56
60
|
stackmemory capture # Save session state for handoff
|
|
57
61
|
stackmemory restore # Restore from captured state
|
|
62
|
+
stackmemory snapshot save # Post-run context snapshot (alias: snap)
|
|
63
|
+
stackmemory snapshot list # List recent snapshots
|
|
64
|
+
stackmemory preflight # File overlap check for parallel tasks (alias: pf)
|
|
65
|
+
stackmemory conductor start # Autonomous Linear→worktree→agent orchestrator
|
|
58
66
|
```
|
|
59
67
|
|
|
60
68
|
## Working Directory
|
|
@@ -25,6 +25,10 @@ docs/ # Documentation
|
|
|
25
25
|
- MCP Server: src/integrations/mcp/server.ts
|
|
26
26
|
- Frame Manager: src/core/context/frame-manager.ts
|
|
27
27
|
- Database: src/core/database/sqlite-adapter.ts
|
|
28
|
+
- Snapshot: src/core/worktree/capture.ts
|
|
29
|
+
- Preflight: src/core/worktree/preflight.ts
|
|
30
|
+
- Conductor: src/cli/commands/orchestrator.ts
|
|
31
|
+
- Shared Utils: src/core/utils/{git,text,fs}.ts
|
|
28
32
|
|
|
29
33
|
## Detailed Guides
|
|
30
34
|
|
|
@@ -55,6 +59,10 @@ npm run linear:sync # Sync with Linear
|
|
|
55
59
|
# StackMemory CLI
|
|
56
60
|
stackmemory capture # Save session state for handoff
|
|
57
61
|
stackmemory restore # Restore from captured state
|
|
62
|
+
stackmemory snapshot save # Post-run context snapshot (alias: snap)
|
|
63
|
+
stackmemory snapshot list # List recent snapshots
|
|
64
|
+
stackmemory preflight # File overlap check for parallel tasks (alias: pf)
|
|
65
|
+
stackmemory conductor start # Autonomous Linear→worktree→agent orchestrator
|
|
58
66
|
```
|
|
59
67
|
|
|
60
68
|
## Working Directory
|