@stackmemoryai/stackmemory 1.0.0 → 1.0.1
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 +66 -270
- package/dist/src/cli/claude-sm.js +9 -0
- package/dist/src/cli/codex-sm.js +9 -0
- package/dist/src/features/sweep/pty-wrapper.js +9 -0
- package/dist/src/hooks/daemon.js +8 -0
- package/dist/src/hooks/graphiti-hooks.js +104 -0
- package/dist/src/integrations/graphiti/client.js +103 -0
- package/dist/src/integrations/graphiti/config.js +17 -0
- package/dist/src/integrations/graphiti/types.js +4 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -5,19 +5,19 @@
|
|
|
5
5
|
[](https://codecov.io/gh/stackmemoryai/stackmemory)
|
|
6
6
|
[](https://www.npmjs.com/package/@stackmemoryai/stackmemory)
|
|
7
7
|
|
|
8
|
-
Lossless, project-scoped memory for AI tools
|
|
8
|
+
Lossless, project-scoped memory for AI coding tools.
|
|
9
9
|
|
|
10
10
|
StackMemory is a **production-ready memory runtime** for AI coding tools that preserves full project context across sessions:
|
|
11
11
|
|
|
12
|
-
- **Zero-config setup**
|
|
13
|
-
- **
|
|
12
|
+
- **Zero-config setup** — `stackmemory init` just works
|
|
13
|
+
- **25 MCP tools** for Claude Code integration
|
|
14
14
|
- **Full Linear integration** with bidirectional sync
|
|
15
|
-
- **Context persistence** that survives
|
|
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
18
|
- **Automatic hooks** for task tracking, Linear sync, and spec progress
|
|
19
19
|
- **Memory monitor daemon** with automatic capture/clear on RAM pressure
|
|
20
|
-
- **
|
|
20
|
+
- **652 tests passing** with comprehensive coverage
|
|
21
21
|
|
|
22
22
|
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.
|
|
23
23
|
|
|
@@ -48,12 +48,12 @@ Tools forget decisions and constraints between sessions. StackMemory makes conte
|
|
|
48
48
|
|
|
49
49
|
---
|
|
50
50
|
|
|
51
|
-
## Features
|
|
51
|
+
## Features
|
|
52
52
|
|
|
53
|
-
- **MCP tools** for Claude Code:
|
|
53
|
+
- **MCP tools** for Claude Code: 25 tools across context, tasks, Linear, traces, and discovery
|
|
54
54
|
- **Skills**: `/spec` (iterative spec generation), `/linear-run` (task execution via RLM)
|
|
55
55
|
- **Hooks**: automatic context save, task tracking, Linear sync, PROMPT_PLAN updates
|
|
56
|
-
- **Prompt Forge**: watches
|
|
56
|
+
- **Prompt Forge**: watches CLAUDE.md and AGENTS.md for prompt optimization (GEPA)
|
|
57
57
|
- **Safe branches**: worktree isolation with `--worktree` or `-w`
|
|
58
58
|
- **Persistent context**: frames, anchors, decisions, retrieval
|
|
59
59
|
- **Integrations**: Linear, DiffMem, Browser MCP
|
|
@@ -68,22 +68,34 @@ Requirements: Node >= 20
|
|
|
68
68
|
# Install globally
|
|
69
69
|
npm install -g @stackmemoryai/stackmemory
|
|
70
70
|
|
|
71
|
-
# Initialize in your project (zero-config
|
|
71
|
+
# Initialize in your project (zero-config)
|
|
72
72
|
cd your-project
|
|
73
73
|
stackmemory init
|
|
74
74
|
|
|
75
75
|
# Configure Claude Code integration
|
|
76
76
|
stackmemory setup-mcp
|
|
77
77
|
|
|
78
|
-
#
|
|
79
|
-
stackmemory
|
|
78
|
+
# Verify everything works
|
|
79
|
+
stackmemory doctor
|
|
80
80
|
```
|
|
81
81
|
|
|
82
82
|
Restart Claude Code and StackMemory MCP tools will be available.
|
|
83
83
|
|
|
84
|
+
### Wrapper Scripts
|
|
85
|
+
|
|
86
|
+
StackMemory ships wrapper scripts that launch your coding tool with StackMemory context pre-loaded:
|
|
87
|
+
|
|
88
|
+
```bash
|
|
89
|
+
claude-sm # Claude Code with StackMemory context + Prompt Forge
|
|
90
|
+
claude-smd # Claude Code with --dangerously-skip-permissions
|
|
91
|
+
codex-sm # Codex with StackMemory context
|
|
92
|
+
codex-smd # Codex with --dangerously-skip-permissions
|
|
93
|
+
opencode-sm # OpenCode with StackMemory context
|
|
94
|
+
```
|
|
95
|
+
|
|
84
96
|
---
|
|
85
97
|
|
|
86
|
-
## Core
|
|
98
|
+
## Core Concepts
|
|
87
99
|
|
|
88
100
|
| Concept | Meaning |
|
|
89
101
|
| -------------- | ------------------------------------------------- |
|
|
@@ -94,56 +106,13 @@ Restart Claude Code and StackMemory MCP tools will be available.
|
|
|
94
106
|
| **Digest** | Structured return value when a frame closes |
|
|
95
107
|
| **Anchor** | Pinned fact (DECISION, CONSTRAINT, INTERFACE) |
|
|
96
108
|
|
|
97
|
-
Frames can span
|
|
98
|
-
|
|
99
|
-
- multiple chat turns
|
|
100
|
-
- multiple tool calls
|
|
101
|
-
- multiple sessions
|
|
102
|
-
|
|
103
|
-
---
|
|
104
|
-
|
|
105
|
-
## Hosted vs Open Source
|
|
106
|
-
|
|
107
|
-
- Hosted: cloud-backed, fast retrieval, durable, team features — works out of the box.
|
|
108
|
-
- OSS local: SQLite, offline, inspectable — intentionally behind; no sync/org features.
|
|
109
|
+
Frames can span multiple chat turns, tool calls, and sessions.
|
|
109
110
|
|
|
110
111
|
---
|
|
111
112
|
|
|
112
113
|
## How it integrates
|
|
113
114
|
|
|
114
|
-
Runs as an MCP server. Editors (e.g., Claude Code) call StackMemory on each interaction to fetch a compiled context bundle; editors don
|
|
115
|
-
|
|
116
|
-
### MCP Quick Usage
|
|
117
|
-
|
|
118
|
-
Use these JSON snippets with Claude Code’s MCP “tools/call”. Responses are returned as a single text item containing JSON.
|
|
119
|
-
|
|
120
|
-
- Plan only (no code):
|
|
121
|
-
```json
|
|
122
|
-
{"method":"tools/call","params":{"name":"plan_only","arguments":{"task":"Refactor config loader","plannerModel":"claude-sonnet-4-20250514"}}}
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
- Approval‑gated plan (phase 1):
|
|
126
|
-
```json
|
|
127
|
-
{"method":"tools/call","params":{"name":"plan_gate","arguments":{"task":"Refactor config loader","compact":true}}}
|
|
128
|
-
```
|
|
129
|
-
|
|
130
|
-
- Approve + execute (phase 2):
|
|
131
|
-
```json
|
|
132
|
-
{"method":"tools/call","params":{"name":"approve_plan","arguments":{"approvalId":"<copy from plan_gate>","implementer":"codex","execute":true,"recordFrame":true,"compact":true}}}
|
|
133
|
-
```
|
|
134
|
-
|
|
135
|
-
- Manage approvals:
|
|
136
|
-
```json
|
|
137
|
-
{"method":"tools/call","params":{"name":"pending_list","arguments":{}}}
|
|
138
|
-
{"method":"tools/call","params":{"name":"pending_show","arguments":{"approvalId":"<id>","compact":true}}}
|
|
139
|
-
{"method":"tools/call","params":{"name":"pending_clear","arguments":{"approvalId":"<id>"}}}
|
|
140
|
-
```
|
|
141
|
-
|
|
142
|
-
Env defaults (optional):
|
|
143
|
-
- `STACKMEMORY_MM_PLANNER_MODEL` (e.g., `claude-sonnet-4-20250514`)
|
|
144
|
-
- `STACKMEMORY_MM_REVIEWER_MODEL` (defaults to planner if unset)
|
|
145
|
-
- `STACKMEMORY_MM_IMPLEMENTER` (`codex` or `claude`)
|
|
146
|
-
- `STACKMEMORY_MM_MAX_ITERS` (e.g., `2`)
|
|
115
|
+
Runs as an MCP server. Editors (e.g., Claude Code) call StackMemory on each interaction to fetch a compiled context bundle; editors don't store memory themselves.
|
|
147
116
|
|
|
148
117
|
---
|
|
149
118
|
|
|
@@ -156,7 +125,7 @@ StackMemory ships Claude Code skills that integrate directly into your workflow.
|
|
|
156
125
|
Generates iterative spec documents following a 4-doc progressive chain. Each document reads previous ones from disk for context.
|
|
157
126
|
|
|
158
127
|
```
|
|
159
|
-
ONE_PAGER.md
|
|
128
|
+
ONE_PAGER.md -> DEV_SPEC.md -> PROMPT_PLAN.md -> AGENTS.md
|
|
160
129
|
(standalone) (reads 1) (reads 1+2) (reads 1+2+3)
|
|
161
130
|
```
|
|
162
131
|
|
|
@@ -217,21 +186,6 @@ StackMemory installs Claude Code hooks that run automatically during your sessio
|
|
|
217
186
|
| `skill-eval` | User prompt | Scores prompt against 28 skill patterns, recommends relevant skills |
|
|
218
187
|
| `tool-use-trace` | Tool invocation | Logs tool usage for context tracking |
|
|
219
188
|
|
|
220
|
-
### Skill Evaluation
|
|
221
|
-
|
|
222
|
-
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.
|
|
223
|
-
|
|
224
|
-
```json
|
|
225
|
-
// Example: user types "generate a spec for the auth system"
|
|
226
|
-
// skill-eval recommends:
|
|
227
|
-
{
|
|
228
|
-
"recommendedSkills": [
|
|
229
|
-
{ "skillName": "spec-generator", "score": 8 },
|
|
230
|
-
{ "skillName": "frame-management", "score": 5 }
|
|
231
|
-
]
|
|
232
|
-
}
|
|
233
|
-
```
|
|
234
|
-
|
|
235
189
|
### Hook Installation
|
|
236
190
|
|
|
237
191
|
Hooks install automatically during `npm install` (with user consent). To install or reinstall manually:
|
|
@@ -255,7 +209,7 @@ When a task completes (via hook or `/linear-run`), StackMemory fuzzy-matches the
|
|
|
255
209
|
|
|
256
210
|
---
|
|
257
211
|
|
|
258
|
-
## Memory Monitor Daemon
|
|
212
|
+
## Memory Monitor Daemon
|
|
259
213
|
|
|
260
214
|
Automatically monitors system RAM and Node.js heap usage, triggering capture/clear cycles when memory pressure exceeds thresholds. Prevents long-running sessions from degrading performance.
|
|
261
215
|
|
|
@@ -286,10 +240,6 @@ stackmemory daemon status # Show memory stats, trigger count, thresholds
|
|
|
286
240
|
stackmemory daemon stop # Stop daemon
|
|
287
241
|
```
|
|
288
242
|
|
|
289
|
-
### Hook
|
|
290
|
-
|
|
291
|
-
The memory guard hook (`.claude/hooks/memory-guard.sh`) is registered as a `user-prompt-submit` hook. When the daemon writes a signal file, the hook alerts you on the next prompt to run `/clear` and restore.
|
|
292
|
-
|
|
293
243
|
---
|
|
294
244
|
|
|
295
245
|
## Prompt Forge (GEPA)
|
|
@@ -306,48 +256,44 @@ claude-sm
|
|
|
306
256
|
|
|
307
257
|
---
|
|
308
258
|
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
```bash
|
|
312
|
-
npm install -g @stackmemoryai/stackmemory@latest
|
|
313
|
-
```
|
|
259
|
+
## RLM (Recursive Language Model) Orchestration
|
|
314
260
|
|
|
315
|
-
|
|
261
|
+
StackMemory includes an RLM system that handles complex tasks through recursive decomposition and parallel execution using Claude Code's Task tool.
|
|
316
262
|
|
|
317
|
-
###
|
|
263
|
+
### Key Features
|
|
318
264
|
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
265
|
+
- **Recursive Task Decomposition**: Breaks complex tasks into manageable subtasks
|
|
266
|
+
- **Parallel Subagent Execution**: Run multiple specialized agents concurrently
|
|
267
|
+
- **8 Specialized Agent Types**: Planning, Code, Testing, Linting, Review, Improve, Context, Publish
|
|
268
|
+
- **Multi-Stage Review**: Iterative improvement cycles with quality scoring (0-1 scale)
|
|
269
|
+
- **Automatic Test Generation**: Unit, integration, and E2E test creation
|
|
323
270
|
|
|
324
|
-
|
|
271
|
+
### Usage
|
|
325
272
|
|
|
326
|
-
For interactive setup with more options:
|
|
327
273
|
```bash
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
### Configure Claude Code
|
|
274
|
+
# Basic usage
|
|
275
|
+
stackmemory skills rlm "Your complex task description"
|
|
332
276
|
|
|
333
|
-
|
|
334
|
-
stackmemory
|
|
277
|
+
# With options
|
|
278
|
+
stackmemory skills rlm "Refactor authentication system" \
|
|
279
|
+
--max-parallel 8 \
|
|
280
|
+
--review-stages 5 \
|
|
281
|
+
--quality-threshold 0.9 \
|
|
282
|
+
--test-mode all
|
|
335
283
|
```
|
|
336
284
|
|
|
337
|
-
|
|
338
|
-
- Creates `~/.claude/stackmemory-mcp.json` MCP configuration
|
|
339
|
-
- Updates `~/.claude/config.json` with StackMemory integration
|
|
340
|
-
- Validates the configuration
|
|
341
|
-
|
|
342
|
-
### Diagnose Issues
|
|
343
|
-
|
|
344
|
-
```bash
|
|
345
|
-
stackmemory doctor
|
|
346
|
-
```
|
|
285
|
+
### Configuration Options
|
|
347
286
|
|
|
348
|
-
|
|
287
|
+
| Option | Description | Default |
|
|
288
|
+
|--------|-------------|---------|
|
|
289
|
+
| `--max-parallel` | Maximum concurrent subagents | 5 |
|
|
290
|
+
| `--max-recursion` | Maximum recursion depth | 4 |
|
|
291
|
+
| `--review-stages` | Number of review iterations | 3 |
|
|
292
|
+
| `--quality-threshold` | Target quality score (0-1) | 0.85 |
|
|
293
|
+
| `--test-mode` | Test generation mode (unit/integration/e2e/all) | all |
|
|
294
|
+
| `--verbose` | Show all recursive operations | false |
|
|
349
295
|
|
|
350
|
-
|
|
296
|
+
**Note**: RLM requires Claude Code Max plan for unlimited subagent execution.
|
|
351
297
|
|
|
352
298
|
---
|
|
353
299
|
|
|
@@ -370,8 +316,6 @@ npm run mcp:start
|
|
|
370
316
|
npm run mcp:dev
|
|
371
317
|
```
|
|
372
318
|
|
|
373
|
-
This creates `.stackmemory/` with SQLite storage.
|
|
374
|
-
|
|
375
319
|
### Step 3: Point your editor to local MCP
|
|
376
320
|
|
|
377
321
|
```json
|
|
@@ -385,183 +329,35 @@ This creates `.stackmemory/` with SQLite storage.
|
|
|
385
329
|
}
|
|
386
330
|
```
|
|
387
331
|
|
|
388
|
-
## How it works
|
|
389
|
-
|
|
390
|
-
Each interaction: ingests events → updates indices → retrieves relevant context → returns sized bundle.
|
|
391
|
-
|
|
392
|
-
---
|
|
393
|
-
|
|
394
|
-
## Example MCP response (simplified)
|
|
395
|
-
|
|
396
|
-
```json
|
|
397
|
-
{
|
|
398
|
-
"hot_stack": [
|
|
399
|
-
{ "frame": "Debug auth redirect", "constraints": [...] }
|
|
400
|
-
],
|
|
401
|
-
"anchors": [
|
|
402
|
-
{ "type": "DECISION", "text": "Use SameSite=Lax cookies" }
|
|
403
|
-
],
|
|
404
|
-
"relevant_digests": [
|
|
405
|
-
{ "frame": "Initial auth refactor", "summary": "..." }
|
|
406
|
-
],
|
|
407
|
-
"pointers": [
|
|
408
|
-
"s3://logs/auth-test-0421"
|
|
409
|
-
]
|
|
410
|
-
}
|
|
411
|
-
```
|
|
412
|
-
|
|
413
|
-
---
|
|
414
|
-
|
|
415
|
-
## Storage & limits
|
|
416
|
-
|
|
417
|
-
### Two-Tier Storage System (v0.3.15+)
|
|
418
|
-
|
|
419
|
-
StackMemory implements an intelligent two-tier storage architecture:
|
|
420
|
-
|
|
421
|
-
#### Local Storage Tiers
|
|
422
|
-
- **Young (<24h)**: Uncompressed, complete retention in memory/Redis
|
|
423
|
-
- **Mature (1-7d)**: LZ4 compression (~2.5x), selective retention
|
|
424
|
-
- **Old (7-30d)**: ZSTD compression (~3.5x), critical data only
|
|
425
|
-
|
|
426
|
-
#### Remote Storage
|
|
427
|
-
- **Archive (>30d)**: Infinite retention in S3 + TimeSeries DB
|
|
428
|
-
- **Migration**: Automatic background migration based on age, size, and importance
|
|
429
|
-
- **Offline Queue**: Persistent retry logic for failed uploads
|
|
430
|
-
|
|
431
|
-
### Free tier (hosted)
|
|
432
|
-
|
|
433
|
-
- 1 project
|
|
434
|
-
- Up to **2GB local storage**
|
|
435
|
-
- Up to **100GB retrieval egress / month**
|
|
436
|
-
|
|
437
|
-
### Paid tiers
|
|
438
|
-
|
|
439
|
-
- Per-project pricing
|
|
440
|
-
- Unlimited storage + retrieval
|
|
441
|
-
- Team sharing
|
|
442
|
-
- Org controls
|
|
443
|
-
- Custom retention policies
|
|
444
|
-
|
|
445
|
-
**No seat-based pricing.**
|
|
446
|
-
|
|
447
332
|
---
|
|
448
333
|
|
|
449
|
-
## Claude Code Integration
|
|
450
|
-
|
|
451
|
-
StackMemory integrates with Claude Code via MCP tools, skills, and hooks. See the [Hooks](#hooks-automatic) and [Skills](#skills-system) sections above.
|
|
452
|
-
|
|
453
|
-
```bash
|
|
454
|
-
# Full setup (one-time)
|
|
455
|
-
npm install -g @stackmemoryai/stackmemory # installs hooks automatically
|
|
456
|
-
cd your-project && stackmemory init # init project
|
|
457
|
-
stackmemory setup-mcp # configure MCP
|
|
458
|
-
stackmemory doctor # verify everything works
|
|
459
|
-
```
|
|
460
|
-
|
|
461
|
-
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).
|
|
462
|
-
|
|
463
|
-
## RLM (Recursive Language Model) Orchestration
|
|
464
|
-
|
|
465
|
-
StackMemory includes an advanced RLM system that enables handling arbitrarily complex tasks through recursive decomposition and parallel execution using Claude Code's Task tool.
|
|
466
|
-
|
|
467
|
-
### Key Features
|
|
468
|
-
|
|
469
|
-
- **Recursive Task Decomposition**: Breaks complex tasks into manageable subtasks
|
|
470
|
-
- **Parallel Subagent Execution**: Run multiple specialized agents concurrently
|
|
471
|
-
- **8 Specialized Agent Types**: Planning, Code, Testing, Linting, Review, Improve, Context, Publish
|
|
472
|
-
- **Multi-Stage Review**: Iterative improvement cycles with quality scoring (0-1 scale)
|
|
473
|
-
- **Automatic Test Generation**: Unit, integration, and E2E test creation
|
|
474
|
-
- **Full Transparency**: Complete execution tree visualization
|
|
475
|
-
|
|
476
|
-
### Usage
|
|
477
|
-
|
|
478
|
-
```bash
|
|
479
|
-
# Basic usage
|
|
480
|
-
stackmemory skills rlm "Your complex task description"
|
|
481
|
-
|
|
482
|
-
# With options
|
|
483
|
-
stackmemory skills rlm "Refactor authentication system" \
|
|
484
|
-
--max-parallel 8 \
|
|
485
|
-
--review-stages 5 \
|
|
486
|
-
--quality-threshold 0.9 \
|
|
487
|
-
--test-mode all
|
|
488
|
-
|
|
489
|
-
# Examples
|
|
490
|
-
stackmemory skills rlm "Generate comprehensive tests for API endpoints"
|
|
491
|
-
stackmemory skills rlm "Refactor the entire authentication system to use JWT"
|
|
492
|
-
stackmemory skills rlm "Build, test, and publish version 2.0.0"
|
|
493
|
-
```
|
|
494
|
-
|
|
495
|
-
### Configuration Options
|
|
496
|
-
|
|
497
|
-
| Option | Description | Default |
|
|
498
|
-
|--------|-------------|---------|
|
|
499
|
-
| `--max-parallel` | Maximum concurrent subagents | 5 |
|
|
500
|
-
| `--max-recursion` | Maximum recursion depth | 4 |
|
|
501
|
-
| `--review-stages` | Number of review iterations | 3 |
|
|
502
|
-
| `--quality-threshold` | Target quality score (0-1) | 0.85 |
|
|
503
|
-
| `--test-mode` | Test generation mode (unit/integration/e2e/all) | all |
|
|
504
|
-
| `--verbose` | Show all recursive operations | false |
|
|
505
|
-
|
|
506
|
-
### How It Works
|
|
507
|
-
|
|
508
|
-
1. **Task Decomposition**: Planning agent analyzes the task and creates a dependency graph
|
|
509
|
-
2. **Parallel Execution**: Independent subtasks run concurrently up to the parallel limit
|
|
510
|
-
3. **Review Cycle**: Review agents assess quality, improve agents implement fixes
|
|
511
|
-
4. **Test Generation**: Testing agents create comprehensive test suites
|
|
512
|
-
5. **Result Aggregation**: All outputs are combined into a final deliverable
|
|
513
|
-
|
|
514
|
-
**Note**: RLM requires Claude Code Max plan for unlimited subagent execution. In development mode, it uses mock responses for testing.
|
|
515
|
-
|
|
516
334
|
## Guarantees & Non-goals
|
|
517
335
|
|
|
518
336
|
**Guarantees:** Lossless storage, project isolation, survives session/model switches, inspectable local mirror.
|
|
519
337
|
|
|
520
338
|
**Non-goals:** Chat UI, vector DB replacement, tool runtime, prompt framework.
|
|
521
339
|
|
|
522
|
-
|
|
523
|
-
## CLI Commands
|
|
524
|
-
|
|
525
|
-
See https://github.com/stackmemoryai/stackmemory/blob/main/docs/cli.md for the full command reference and examples.
|
|
526
|
-
|
|
527
|
-
---
|
|
528
|
-
|
|
529
|
-
## Status
|
|
530
|
-
|
|
531
|
-
See https://github.com/stackmemoryai/stackmemory/blob/main/docs/status.md for current status.
|
|
532
|
-
|
|
533
340
|
---
|
|
534
341
|
|
|
535
|
-
##
|
|
342
|
+
## CLI Commands
|
|
536
343
|
|
|
537
|
-
See https://github.com/stackmemoryai/stackmemory/blob/main/docs/
|
|
344
|
+
See [docs/cli.md](https://github.com/stackmemoryai/stackmemory/blob/main/docs/cli.md) for the full command reference.
|
|
538
345
|
|
|
539
346
|
---
|
|
540
347
|
|
|
541
|
-
##
|
|
348
|
+
## Documentation
|
|
542
349
|
|
|
543
|
-
|
|
350
|
+
- [CLI Reference](./docs/cli.md) — Full command reference
|
|
351
|
+
- [Setup Guide](./docs/SETUP.md) — Advanced setup options
|
|
352
|
+
- [Development Guide](./docs/DEVELOPMENT.md) — Contributing and development
|
|
353
|
+
- [Architecture](./docs/architecture.md) — System design
|
|
354
|
+
- [API Reference](./docs/API_REFERENCE.md) — API documentation
|
|
355
|
+
- [Vision](./vision.md) — Product vision and principles
|
|
356
|
+
- [Status](./docs/status.md) — Current project status
|
|
357
|
+
- [Roadmap](./docs/roadmap.md) — Future plans
|
|
544
358
|
|
|
545
359
|
---
|
|
546
360
|
|
|
547
361
|
## License
|
|
548
362
|
|
|
549
363
|
Licensed under the [Business Source License 1.1](./LICENSE). You can use, modify, and self-host StackMemory freely. The one restriction: you may not offer it as a competing hosted service. The license converts to MIT after 4 years per release.
|
|
550
|
-
|
|
551
|
-
---
|
|
552
|
-
|
|
553
|
-
## Additional Resources
|
|
554
|
-
|
|
555
|
-
### ML System Design
|
|
556
|
-
|
|
557
|
-
- [ML System Insights](./ML_SYSTEM_INSIGHTS.md) - Analysis of 300+ production ML systems
|
|
558
|
-
- [Agent Instructions](./AGENTS.md) - Specific guidance for AI agents working with ML systems
|
|
559
|
-
|
|
560
|
-
### Documentation
|
|
561
|
-
- [Vision](./vision.md) - Product vision, principles, roadmap, metrics
|
|
562
|
-
- [Product Requirements](./PRD.md) - Detailed product specifications
|
|
563
|
-
- [Technical Architecture](./TECHNICAL_ARCHITECTURE.md) - System design and database schemas
|
|
564
|
-
- [Beads Integration](./BEADS_INTEGRATION.md) - Git-native memory patterns from Beads ecosystem
|
|
565
|
-
- [MCP: plan_and_code](https://github.com/stackmemoryai/stackmemory/blob/main/docs/mcp.md) - Trigger planning + coding via MCP with JSON results
|
|
566
|
-
|
|
567
|
-
---
|
|
@@ -727,6 +727,15 @@ Session completed on fallback provider: ${status.currentProvider}`
|
|
|
727
727
|
action: "session_end",
|
|
728
728
|
exitCode: code
|
|
729
729
|
});
|
|
730
|
+
if (process.env["LINEAR_API_KEY"]) {
|
|
731
|
+
try {
|
|
732
|
+
execSync("stackmemory linear sync", {
|
|
733
|
+
stdio: "ignore",
|
|
734
|
+
timeout: 1e4
|
|
735
|
+
});
|
|
736
|
+
} catch {
|
|
737
|
+
}
|
|
738
|
+
}
|
|
730
739
|
if (this.config.tracingEnabled) {
|
|
731
740
|
const summary = trace.getExecutionSummary();
|
|
732
741
|
console.log();
|
package/dist/src/cli/codex-sm.js
CHANGED
|
@@ -339,6 +339,15 @@ class CodexSM {
|
|
|
339
339
|
action: "session_end",
|
|
340
340
|
exitCode: code
|
|
341
341
|
});
|
|
342
|
+
if (process.env["LINEAR_API_KEY"]) {
|
|
343
|
+
try {
|
|
344
|
+
execSync("stackmemory linear sync", {
|
|
345
|
+
stdio: "ignore",
|
|
346
|
+
timeout: 1e4
|
|
347
|
+
});
|
|
348
|
+
} catch {
|
|
349
|
+
}
|
|
350
|
+
}
|
|
342
351
|
if (this.config.tracingEnabled) {
|
|
343
352
|
const summary = trace.getExecutionSummary();
|
|
344
353
|
console.log();
|
|
@@ -117,6 +117,15 @@ class PtyWrapper {
|
|
|
117
117
|
});
|
|
118
118
|
this.ptyProcess.onExit(({ exitCode }) => {
|
|
119
119
|
this.cleanup();
|
|
120
|
+
if (process.env["LINEAR_API_KEY"]) {
|
|
121
|
+
try {
|
|
122
|
+
execSync("stackmemory linear sync", {
|
|
123
|
+
stdio: "ignore",
|
|
124
|
+
timeout: 1e4
|
|
125
|
+
});
|
|
126
|
+
} catch {
|
|
127
|
+
}
|
|
128
|
+
}
|
|
120
129
|
process.exit(exitCode);
|
|
121
130
|
});
|
|
122
131
|
const onSignal = () => {
|
package/dist/src/hooks/daemon.js
CHANGED
|
@@ -13,6 +13,7 @@ import {
|
|
|
13
13
|
import { join, extname, relative } from "path";
|
|
14
14
|
import { spawn } from "child_process";
|
|
15
15
|
import { loadConfig } from "./config.js";
|
|
16
|
+
import { GraphitiHooks } from "./graphiti-hooks.js";
|
|
16
17
|
import {
|
|
17
18
|
hookEmitter
|
|
18
19
|
} from "./events.js";
|
|
@@ -142,6 +143,13 @@ function registerBuiltinHandlers() {
|
|
|
142
143
|
hookEmitter.registerHandler("file_change", handleFileChange);
|
|
143
144
|
hookEmitter.registerHandler("suggestion_ready", handleSuggestionReady);
|
|
144
145
|
hookEmitter.registerHandler("error", handleError);
|
|
146
|
+
try {
|
|
147
|
+
if (process.env.GRAPHITI_ENDPOINT || process.env.GRAPHITI_ENABLED === "true") {
|
|
148
|
+
const graphiti = new GraphitiHooks();
|
|
149
|
+
graphiti.register(hookEmitter);
|
|
150
|
+
}
|
|
151
|
+
} catch {
|
|
152
|
+
}
|
|
145
153
|
hookEmitter.on("*", () => {
|
|
146
154
|
state.eventsProcessed++;
|
|
147
155
|
});
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
+
import { dirname as __pathDirname } from 'path';
|
|
3
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = __pathDirname(__filename);
|
|
5
|
+
import { logger } from "../core/monitoring/logger.js";
|
|
6
|
+
import { GraphitiClient } from "../integrations/graphiti/client.js";
|
|
7
|
+
import { DEFAULT_GRAPHITI_CONFIG } from "../integrations/graphiti/config.js";
|
|
8
|
+
class GraphitiHooks {
|
|
9
|
+
client;
|
|
10
|
+
config;
|
|
11
|
+
constructor(config = {}) {
|
|
12
|
+
this.config = { ...DEFAULT_GRAPHITI_CONFIG, ...config };
|
|
13
|
+
this.client = new GraphitiClient(this.config);
|
|
14
|
+
}
|
|
15
|
+
register(emitter) {
|
|
16
|
+
if (!this.config.enabled) {
|
|
17
|
+
logger.debug("Graphiti hooks disabled");
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
emitter.registerHandler("session_start", this.onSessionStart.bind(this));
|
|
21
|
+
emitter.registerHandler("file_change", this.onFileChange.bind(this));
|
|
22
|
+
emitter.registerHandler("session_end", this.onSessionEnd.bind(this));
|
|
23
|
+
logger.info("Graphiti hooks registered", {
|
|
24
|
+
endpoint: this.config.endpoint,
|
|
25
|
+
backend: this.config.backend,
|
|
26
|
+
maxHops: this.config.maxHops
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
async onSessionStart(event) {
|
|
30
|
+
try {
|
|
31
|
+
const status = await this.client.getStatus();
|
|
32
|
+
if (!status.connected) {
|
|
33
|
+
logger.warn("Graphiti not available - operating in degraded mode");
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const episode = {
|
|
37
|
+
type: "session_start",
|
|
38
|
+
content: event.data || {},
|
|
39
|
+
timestamp: Date.now(),
|
|
40
|
+
source: "stackmemory",
|
|
41
|
+
metadata: { severity: "info" }
|
|
42
|
+
};
|
|
43
|
+
await this.client.upsertEpisode(episode);
|
|
44
|
+
} catch (error) {
|
|
45
|
+
logger.debug("Graphiti session_start failed", {
|
|
46
|
+
error: error instanceof Error ? error.message : String(error)
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
async onFileChange(event) {
|
|
51
|
+
const fileEvent = event;
|
|
52
|
+
try {
|
|
53
|
+
const episode = {
|
|
54
|
+
type: "file_change",
|
|
55
|
+
content: {
|
|
56
|
+
path: fileEvent.data.path,
|
|
57
|
+
changeType: fileEvent.data.changeType,
|
|
58
|
+
size: typeof fileEvent.data.content === "string" ? fileEvent.data.content.length : void 0
|
|
59
|
+
},
|
|
60
|
+
timestamp: Date.now(),
|
|
61
|
+
source: "stackmemory"
|
|
62
|
+
};
|
|
63
|
+
await this.client.upsertEpisode(episode);
|
|
64
|
+
} catch (error) {
|
|
65
|
+
logger.debug("Graphiti file_change episode failed", {
|
|
66
|
+
error: error instanceof Error ? error.message : String(error)
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
async onSessionEnd(event) {
|
|
71
|
+
try {
|
|
72
|
+
const episode = {
|
|
73
|
+
type: "session_end",
|
|
74
|
+
content: event.data || {},
|
|
75
|
+
timestamp: Date.now(),
|
|
76
|
+
source: "stackmemory"
|
|
77
|
+
};
|
|
78
|
+
await this.client.upsertEpisode(episode);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
logger.debug("Graphiti session_end failed", {
|
|
81
|
+
error: error instanceof Error ? error.message : String(error)
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
// Expose a simple temporal query helper for future MCP tooling
|
|
86
|
+
async buildTemporalContext(query = {}) {
|
|
87
|
+
const now = Date.now();
|
|
88
|
+
const q = {
|
|
89
|
+
query: query.query || void 0,
|
|
90
|
+
entityTypes: query.entityTypes || void 0,
|
|
91
|
+
relationTypes: query.relationTypes || void 0,
|
|
92
|
+
validFrom: query.validFrom ?? now - 1e3 * 60 * 60 * 24 * 30,
|
|
93
|
+
// 30d default
|
|
94
|
+
validTo: query.validTo ?? now,
|
|
95
|
+
maxHops: query.maxHops ?? this.config.maxHops,
|
|
96
|
+
k: query.k ?? 20,
|
|
97
|
+
rerank: query.rerank ?? true
|
|
98
|
+
};
|
|
99
|
+
return this.client.queryTemporal(q);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
export {
|
|
103
|
+
GraphitiHooks
|
|
104
|
+
};
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
+
import { dirname as __pathDirname } from 'path';
|
|
3
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = __pathDirname(__filename);
|
|
5
|
+
import { DEFAULT_GRAPHITI_CONFIG } from "./config.js";
|
|
6
|
+
class GraphitiClientError extends Error {
|
|
7
|
+
constructor(message, code, statusCode) {
|
|
8
|
+
super(message);
|
|
9
|
+
this.code = code;
|
|
10
|
+
this.statusCode = statusCode;
|
|
11
|
+
this.name = "GraphitiClientError";
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
class GraphitiClient {
|
|
15
|
+
endpoint;
|
|
16
|
+
timeout;
|
|
17
|
+
maxRetries;
|
|
18
|
+
namespace;
|
|
19
|
+
constructor(config = {}) {
|
|
20
|
+
const merged = { ...DEFAULT_GRAPHITI_CONFIG, ...config };
|
|
21
|
+
this.endpoint = merged.endpoint.replace(/\/$/, "");
|
|
22
|
+
this.timeout = merged.timeoutMs;
|
|
23
|
+
this.maxRetries = merged.maxRetries;
|
|
24
|
+
this.namespace = merged.projectNamespace || "default";
|
|
25
|
+
}
|
|
26
|
+
async request(path, options = {}) {
|
|
27
|
+
const controller = new AbortController();
|
|
28
|
+
const timeoutId = setTimeout(() => controller.abort(), this.timeout);
|
|
29
|
+
let lastError;
|
|
30
|
+
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
31
|
+
try {
|
|
32
|
+
const res = await fetch(`${this.endpoint}${path}`, {
|
|
33
|
+
...options,
|
|
34
|
+
signal: controller.signal,
|
|
35
|
+
headers: { "Content-Type": "application/json", ...options.headers || {} }
|
|
36
|
+
});
|
|
37
|
+
clearTimeout(timeoutId);
|
|
38
|
+
if (!res.ok) {
|
|
39
|
+
const msg = await res.text().catch(() => res.statusText);
|
|
40
|
+
throw new GraphitiClientError(`Request failed: ${msg}`, "HTTP_ERROR", res.status);
|
|
41
|
+
}
|
|
42
|
+
return await res.json();
|
|
43
|
+
} catch (err) {
|
|
44
|
+
lastError = err;
|
|
45
|
+
if (err instanceof GraphitiClientError) throw err;
|
|
46
|
+
if (err.name === "AbortError") {
|
|
47
|
+
throw new GraphitiClientError("Request timeout", "TIMEOUT");
|
|
48
|
+
}
|
|
49
|
+
if (attempt < this.maxRetries) {
|
|
50
|
+
await new Promise((r) => setTimeout(r, Math.pow(2, attempt) * 100));
|
|
51
|
+
continue;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
clearTimeout(timeoutId);
|
|
56
|
+
throw new GraphitiClientError(lastError?.message || "Network error", "NETWORK_ERROR");
|
|
57
|
+
}
|
|
58
|
+
// Episodes
|
|
59
|
+
async upsertEpisode(episode) {
|
|
60
|
+
const payload = { ...episode, namespace: this.namespace };
|
|
61
|
+
const res = await this.request(`/episodes`, {
|
|
62
|
+
method: "POST",
|
|
63
|
+
body: JSON.stringify(payload)
|
|
64
|
+
});
|
|
65
|
+
return res;
|
|
66
|
+
}
|
|
67
|
+
// Entities
|
|
68
|
+
async upsertEntities(entities) {
|
|
69
|
+
const payload = { entities, namespace: this.namespace };
|
|
70
|
+
return this.request(`/entities:batchUpsert`, {
|
|
71
|
+
method: "POST",
|
|
72
|
+
body: JSON.stringify(payload)
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
// Relations
|
|
76
|
+
async upsertRelations(edges) {
|
|
77
|
+
const payload = { edges, namespace: this.namespace };
|
|
78
|
+
return this.request(`/relations:batchUpsert`, {
|
|
79
|
+
method: "POST",
|
|
80
|
+
body: JSON.stringify(payload)
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
// Temporal query + hybrid retrieval
|
|
84
|
+
async queryTemporal(query) {
|
|
85
|
+
const payload = { ...query, namespace: this.namespace };
|
|
86
|
+
return this.request(`/query/temporal`, {
|
|
87
|
+
method: "POST",
|
|
88
|
+
body: JSON.stringify(payload)
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
// Health/status
|
|
92
|
+
async getStatus() {
|
|
93
|
+
try {
|
|
94
|
+
return await this.request(`/status?namespace=${encodeURIComponent(this.namespace)}`);
|
|
95
|
+
} catch {
|
|
96
|
+
return { connected: false };
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
export {
|
|
101
|
+
GraphitiClient,
|
|
102
|
+
GraphitiClientError
|
|
103
|
+
};
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { fileURLToPath as __fileURLToPath } from 'url';
|
|
2
|
+
import { dirname as __pathDirname } from 'path';
|
|
3
|
+
const __filename = __fileURLToPath(import.meta.url);
|
|
4
|
+
const __dirname = __pathDirname(__filename);
|
|
5
|
+
const DEFAULT_GRAPHITI_CONFIG = {
|
|
6
|
+
enabled: !!process.env.GRAPHITI_ENDPOINT,
|
|
7
|
+
endpoint: process.env.GRAPHITI_ENDPOINT?.replace(/\/$/, "") || "http://localhost:8080",
|
|
8
|
+
backend: process.env.GRAPHITI_BACKEND || "neo4j",
|
|
9
|
+
projectNamespace: process.env.STACKMEMORY_PROJECT_ID || "default",
|
|
10
|
+
timeoutMs: 5e3,
|
|
11
|
+
maxRetries: 2,
|
|
12
|
+
maxTokens: 1600,
|
|
13
|
+
maxHops: 2
|
|
14
|
+
};
|
|
15
|
+
export {
|
|
16
|
+
DEFAULT_GRAPHITI_CONFIG
|
|
17
|
+
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackmemoryai/stackmemory",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.1",
|
|
4
4
|
"description": "Project-scoped memory for AI coding tools. Durable context across sessions with MCP integration, frames, smart retrieval, Claude Code skills, and automatic hooks.",
|
|
5
5
|
"engines": {
|
|
6
6
|
"node": ">=20.0.0",
|