@stackmemoryai/stackmemory 1.2.1 → 1.2.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/codex-sm.js +6 -8
- package/dist/src/cli/commands/config.js +0 -81
- package/dist/src/cli/commands/context-rehydrate.js +133 -47
- package/dist/src/cli/commands/db.js +35 -8
- package/dist/src/cli/commands/handoff.js +1 -1
- package/dist/src/cli/commands/linear.js +9 -0
- package/dist/src/cli/commands/ralph.js +2 -2
- package/dist/src/cli/commands/setup.js +2 -2
- package/dist/src/cli/commands/signup.js +3 -1
- package/dist/src/cli/commands/skills.js +123 -1
- package/dist/src/cli/commands/storage-tier.js +26 -8
- package/dist/src/cli/index.js +1 -57
- package/dist/src/core/config/feature-flags.js +0 -4
- package/dist/src/core/context/dual-stack-manager.js +10 -3
- package/dist/src/core/context/frame-database.js +32 -0
- package/dist/src/core/context/frame-handoff-manager.js +2 -2
- package/dist/src/core/context/{refactored-frame-manager.js → frame-manager.js} +3 -3
- package/dist/src/core/context/index.js +2 -2
- package/dist/src/core/database/sqlite-adapter.js +161 -1
- package/dist/src/core/digest/frame-digest-integration.js +1 -1
- package/dist/src/core/digest/index.js +1 -1
- package/dist/src/core/execution/parallel-executor.js +5 -1
- package/dist/src/core/projects/project-isolation.js +18 -4
- package/dist/src/core/security/index.js +2 -0
- package/dist/src/core/security/input-sanitizer.js +23 -0
- package/dist/src/core/utils/update-checker.js +10 -6
- package/dist/src/daemon/daemon-config.js +2 -1
- package/dist/src/daemon/services/auto-save-service.js +121 -0
- package/dist/src/daemon/services/maintenance-service.js +76 -1
- package/dist/src/features/sweep/prompt-builder.js +2 -2
- package/dist/src/hooks/graphiti-hooks.js +149 -0
- package/dist/src/hooks/session-summary.js +30 -0
- package/dist/src/integrations/graphiti/linear-graphiti-bridge.js +115 -0
- package/dist/src/integrations/greptile/client.js +101 -0
- package/dist/src/integrations/greptile/config.js +14 -0
- package/dist/src/integrations/greptile/index.js +11 -0
- package/dist/src/integrations/greptile/types.js +4 -0
- package/dist/src/integrations/linear/config.js +3 -1
- package/dist/src/integrations/linear/sync.js +18 -5
- package/dist/src/integrations/linear/webhook.js +16 -0
- package/dist/src/integrations/mcp/handlers/code-execution-handlers.js +33 -7
- package/dist/src/integrations/mcp/handlers/cord-handlers.js +397 -0
- package/dist/src/integrations/mcp/handlers/greptile-handlers.js +456 -0
- package/dist/src/integrations/mcp/handlers/index.js +55 -1
- package/dist/src/integrations/mcp/handlers/task-handlers.js +55 -12
- package/dist/src/integrations/mcp/handlers/team-handlers.js +211 -0
- package/dist/src/integrations/mcp/handlers/trace-handlers.js +25 -9
- package/dist/src/integrations/mcp/index.js +2 -2
- package/dist/src/integrations/mcp/refactored-server.js +31 -10
- package/dist/src/integrations/mcp/server.js +27 -0
- package/dist/src/integrations/mcp/tool-definitions.js +215 -1
- package/dist/src/integrations/ralph/context/context-budget-manager.js +10 -2
- package/dist/src/integrations/ralph/context/stackmemory-context-loader.js +54 -22
- package/dist/src/integrations/ralph/learning/pattern-learner.js +59 -24
- package/dist/src/integrations/ralph/orchestration/multi-loop-orchestrator.js +81 -35
- package/dist/src/integrations/ralph/patterns/compounding-engineering-pattern.js +12 -4
- package/dist/src/integrations/ralph/patterns/extended-coherence-sessions.js +32 -9
- package/dist/src/integrations/ralph/swarm/git-workflow-manager.js +25 -8
- package/dist/src/integrations/ralph/swarm/swarm-coordinator.js +17 -5
- package/dist/src/integrations/ralph/visualization/ralph-debugger.js +73 -22
- package/dist/src/skills/claude-skills.js +46 -103
- package/dist/src/skills/parallel-agent-skill.js +514 -0
- package/dist/src/utils/hook-installer.js +8 -0
- package/package.json +5 -5
- package/scripts/gepa/.before-optimize.md +140 -159
- package/scripts/gepa/config.json +7 -1
- package/scripts/gepa/evals/fixtures/api-endpoint.ts +31 -0
- package/scripts/gepa/evals/fixtures/brittle-integration.ts +38 -0
- package/scripts/gepa/evals/fixtures/fts5-triggers.sql +23 -0
- package/scripts/gepa/evals/fixtures/leaky-service.ts +70 -0
- package/scripts/gepa/evals/fixtures/mcp-dispatch-stub.ts +39 -0
- package/scripts/gepa/evals/fixtures/pr-diff.txt +24 -0
- package/scripts/gepa/evals/fixtures/unsafe-webhook.ts +34 -0
- package/scripts/gepa/evals/fixtures/unwrapped-db-op.ts +42 -0
- package/scripts/gepa/evals/stackmemory-tasks.jsonl +8 -0
- package/scripts/gepa/generations/gen-000/baseline.md +172 -159
- package/scripts/gepa/generations/gen-001/baseline.md +172 -159
- package/scripts/gepa/generations/gen-001/variant-a.md +156 -146
- package/scripts/gepa/generations/gen-001/variant-b.md +199 -170
- package/scripts/gepa/generations/gen-001/variant-c.md +127 -46
- package/scripts/gepa/generations/gen-001/variant-d.md +160 -107
- package/scripts/gepa/hooks/reflect.js +44 -5
- package/scripts/gepa/optimize.js +281 -39
- package/scripts/gepa/results/eval-1-baseline.json +187 -10
- package/scripts/gepa/results/eval-1-variant-a.json +188 -11
- package/scripts/gepa/results/eval-1-variant-b.json +188 -11
- package/scripts/gepa/results/eval-1-variant-c.json +168 -11
- package/scripts/gepa/results/eval-1-variant-d.json +169 -12
- package/scripts/gepa/state.json +18 -18
- package/scripts/install-claude-hooks-auto.js +8 -0
- package/templates/claude-hooks/cord-trace.js +225 -0
- package/dist/src/core/config/storage-config.js +0 -114
- package/dist/src/core/storage/chromadb-adapter.js +0 -379
- package/dist/src/integrations/claude-code/enhanced-pre-clear-hooks.js +0 -458
- package/dist/src/integrations/ralph/coordination/enhanced-coordination.js +0 -409
- package/dist/src/skills/repo-ingestion-skill.js +0 -631
- package/templates/claude-hooks/chromadb-wrapper +0 -21
- /package/dist/src/core/context/{enhanced-rehydration.js → rehydration.js} +0 -0
- /package/dist/src/core/digest/{enhanced-hybrid-digest.js → hybrid-digest.js} +0 -0
- /package/dist/src/core/session/{enhanced-handoff.js → handoff.js} +0 -0
|
@@ -31,6 +31,14 @@ const CANONICAL_HOOKS = [
|
|
|
31
31
|
timeout: 2,
|
|
32
32
|
commandPrefix: "node",
|
|
33
33
|
required: true
|
|
34
|
+
},
|
|
35
|
+
{
|
|
36
|
+
scriptName: "cord-trace.js",
|
|
37
|
+
eventType: "PostToolUse",
|
|
38
|
+
matcher: "mcp__.*__cord_(spawn|fork|complete|ask|tree)",
|
|
39
|
+
timeout: 2,
|
|
40
|
+
commandPrefix: "node",
|
|
41
|
+
required: true
|
|
34
42
|
}
|
|
35
43
|
];
|
|
36
44
|
const DEAD_HOOKS = ["sms-response-handler.js"];
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackmemoryai/stackmemory",
|
|
3
|
-
"version": "1.2.
|
|
3
|
+
"version": "1.2.4",
|
|
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",
|
|
@@ -87,8 +87,8 @@
|
|
|
87
87
|
"postinstall": "node scripts/install-claude-hooks-auto.js || true",
|
|
88
88
|
"init": "node dist/scripts/initialize.js",
|
|
89
89
|
"build": "rm -rf dist && node esbuild.config.js",
|
|
90
|
-
"lint": "eslint src/**/*.ts scripts/**/*.ts",
|
|
91
|
-
"lint:fix": "eslint src/**/*.ts scripts/**/*.ts --fix --max-warnings=-1",
|
|
90
|
+
"lint": "eslint 'src/**/*.ts' 'scripts/**/*.ts'",
|
|
91
|
+
"lint:fix": "eslint 'src/**/*.ts' 'scripts/**/*.ts' --fix --max-warnings=-1",
|
|
92
92
|
"lint:fast": "oxlint src scripts",
|
|
93
93
|
"format": "prettier --write src/**/*.ts scripts/**/*.ts",
|
|
94
94
|
"test": "vitest",
|
|
@@ -121,6 +121,7 @@
|
|
|
121
121
|
"daemon:status": "node dist/cli/index.js daemon status",
|
|
122
122
|
"sync:start": "node scripts/background-sync-manager.js",
|
|
123
123
|
"sync:setup": "./scripts/setup-background-sync.sh",
|
|
124
|
+
"eval:cord": "npx tsx scripts/evals/cord-vs-flat-eval.ts",
|
|
124
125
|
"prepare": "echo 'Prepare step completed'",
|
|
125
126
|
"verify:dist": "node scripts/verify-dist.cjs",
|
|
126
127
|
"test:smoke-db": "bash scripts/smoke-init-db.sh",
|
|
@@ -197,7 +198,6 @@
|
|
|
197
198
|
"@xenova/transformers": "^2.17.2",
|
|
198
199
|
"blessed": "^0.1.81",
|
|
199
200
|
"blessed-contrib": "^4.11.0",
|
|
200
|
-
"chokidar": "^5.0.0"
|
|
201
|
-
"chromadb": "^3.2.2"
|
|
201
|
+
"chokidar": "^5.0.0"
|
|
202
202
|
}
|
|
203
203
|
}
|
|
@@ -1,159 +1,140 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
-
|
|
33
|
-
-
|
|
34
|
-
-
|
|
35
|
-
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
-
|
|
64
|
-
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
-
|
|
76
|
-
-
|
|
77
|
-
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
-
|
|
88
|
-
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
.
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
-
|
|
136
|
-
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
- After completing a prompt step, add an entry under that step in prompt_plan.md similar to:
|
|
142
|
-
- [x] Step 5 — Implement POST /api/v1/query — tests green — manual checks: cURL example tested — README Release Notes updated — commit: "query: add /api/v1/query route, adapter integration, tests"
|
|
143
|
-
- If pausing work:
|
|
144
|
-
- - [ ] (Deferred) Step 7.3 — Implement real Pinecone adapter — blocked on PINECONE_API_KEY (reason: waiting for dev key from infra)
|
|
145
|
-
|
|
146
|
-
If anything is missing
|
|
147
|
-
- If you cannot open prompt_plan.md, spec.md, idea.md, idea_one_pager.md, or any design file, stop and report exactly which file and why (permission/absent/parse error).
|
|
148
|
-
- Ask for required secrets or permissions rather than guessing. Use the "When to ask for human input" rules above.
|
|
149
|
-
|
|
150
|
-
Contact & escalation
|
|
151
|
-
- When blocked on infra/secrets/design files, create a short note in prompt_plan.md under the current step and ping the human with:
|
|
152
|
-
- What I need: (e.g., PINECONE_API_KEY, AWS dev creds)
|
|
153
|
-
- Why I need it: (which step/blocker)
|
|
154
|
-
- Recommended minimal next action & fallback
|
|
155
|
-
|
|
156
|
-
Notes
|
|
157
|
-
- Keep AGENTS.md and the rest of the repo docs in sync. Update this file if workflow expectations change.
|
|
158
|
-
|
|
159
|
-
End.
|
|
1
|
+
# StackMemory - Project Configuration
|
|
2
|
+
|
|
3
|
+
## Project Structure
|
|
4
|
+
|
|
5
|
+
```
|
|
6
|
+
src/
|
|
7
|
+
cli/ # CLI commands and entry point
|
|
8
|
+
core/ # Core business logic
|
|
9
|
+
context/ # Frame and context management
|
|
10
|
+
database/ # Database adapters (SQLite, ParadeDB)
|
|
11
|
+
digest/ # Digest generation
|
|
12
|
+
query/ # Query parsing and routing
|
|
13
|
+
integrations/ # External integrations (Linear, MCP)
|
|
14
|
+
services/ # Business services
|
|
15
|
+
skills/ # Claude Code skills
|
|
16
|
+
utils/ # Shared utilities
|
|
17
|
+
scripts/ # Build and utility scripts
|
|
18
|
+
config/ # Configuration files
|
|
19
|
+
docs/ # Documentation
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Key Files
|
|
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
|
+
## Detailed Guides
|
|
30
|
+
|
|
31
|
+
Quick reference (agent_docs/):
|
|
32
|
+
- linear_integration.md - Linear sync
|
|
33
|
+
- mcp_server.md - MCP tools
|
|
34
|
+
- database_storage.md - Storage
|
|
35
|
+
- claude_hooks.md - Hooks
|
|
36
|
+
|
|
37
|
+
Full documentation (docs/):
|
|
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
|
+
|
|
45
|
+
## Commands
|
|
46
|
+
|
|
47
|
+
```bash
|
|
48
|
+
npm run build # Compile TypeScript (esbuild)
|
|
49
|
+
npm run lint # ESLint check
|
|
50
|
+
npm run lint:fix # Auto-fix lint issues
|
|
51
|
+
npm test # Run Vitest (watch)
|
|
52
|
+
npm run test:run # Run tests once
|
|
53
|
+
npm run linear:sync # Sync with Linear
|
|
54
|
+
|
|
55
|
+
# StackMemory CLI
|
|
56
|
+
stackmemory capture # Save session state for handoff
|
|
57
|
+
stackmemory restore # Restore from captured state
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Working Directory
|
|
61
|
+
|
|
62
|
+
- PRIMARY: /Users/jwu/Dev/stackmemory
|
|
63
|
+
- ALLOWED: All subdirectories
|
|
64
|
+
- TEMP: /tmp for temporary operations
|
|
65
|
+
|
|
66
|
+
## Validation (MUST DO)
|
|
67
|
+
|
|
68
|
+
After code changes:
|
|
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
|
|
73
|
+
|
|
74
|
+
Test coverage:
|
|
75
|
+
- New features require tests in `src/**/__tests__/`
|
|
76
|
+
- Maintain or improve coverage (no untested code paths)
|
|
77
|
+
- Critical paths: context management, handoff, Linear sync
|
|
78
|
+
|
|
79
|
+
Never: Assume success | Skip testing | Use mock data as fallback
|
|
80
|
+
|
|
81
|
+
## Git Rules (CRITICAL)
|
|
82
|
+
|
|
83
|
+
- NEVER use `--no-verify` on git push or commit
|
|
84
|
+
- ALWAYS fix lint/test errors before pushing
|
|
85
|
+
- If pre-push hooks fail, fix the underlying issue
|
|
86
|
+
- Run `npm run lint && npm run test:run` before pushing
|
|
87
|
+
- Commit message format: `type(scope): message`
|
|
88
|
+
- Branch naming: `feature/STA-XXX-description` | `fix/STA-XXX-description` | `chore/description`
|
|
89
|
+
|
|
90
|
+
## Task Management
|
|
91
|
+
|
|
92
|
+
- Use TodoWrite for 3+ steps or multiple requests
|
|
93
|
+
- Keep one task in_progress at a time
|
|
94
|
+
- Update task status immediately on completion
|
|
95
|
+
|
|
96
|
+
## Security
|
|
97
|
+
|
|
98
|
+
NEVER hardcode secrets - use process.env with dotenv/config
|
|
99
|
+
|
|
100
|
+
```javascript
|
|
101
|
+
import 'dotenv/config';
|
|
102
|
+
const API_KEY = process.env.LINEAR_API_KEY;
|
|
103
|
+
if (!API_KEY) {
|
|
104
|
+
console.error('LINEAR_API_KEY not set');
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Environment sources (check in order):
|
|
110
|
+
1. .env file
|
|
111
|
+
2. .env.local
|
|
112
|
+
3. ~/.zshrc
|
|
113
|
+
4. Process environment
|
|
114
|
+
|
|
115
|
+
Secret patterns to block: lin_api_* | lin_oauth_* | sk-* | npm_*
|
|
116
|
+
|
|
117
|
+
## Deploy
|
|
118
|
+
|
|
119
|
+
```bash
|
|
120
|
+
# npm publish (uses NPM_TOKEN from .env, no OTP needed)
|
|
121
|
+
git stash -- scripts/gepa/ # stash GEPA state (dirties working tree)
|
|
122
|
+
NPM_TOKEN=$(grep '^NPM_TOKEN=' .env | cut -d= -f2) \
|
|
123
|
+
npm publish --registry https://registry.npmjs.org/ \
|
|
124
|
+
--//registry.npmjs.org/:_authToken="$NPM_TOKEN"
|
|
125
|
+
git stash pop # restore GEPA state
|
|
126
|
+
|
|
127
|
+
# Railway
|
|
128
|
+
railway up
|
|
129
|
+
|
|
130
|
+
# Pre-publish checks require clean git status — stash GEPA files first
|
|
131
|
+
```
|
|
132
|
+
|
|
133
|
+
## Workflow
|
|
134
|
+
|
|
135
|
+
- Check .env for API keys before asking
|
|
136
|
+
- Run npm run linear:sync after task completion
|
|
137
|
+
- Use browser MCP for visual testing
|
|
138
|
+
- Review recent commits and stackmemory.json on session start
|
|
139
|
+
- Use subagents for multi-step tasks
|
|
140
|
+
- Ask 1-3 clarifying questions for complex commands (one at a time)
|
package/scripts/gepa/config.json
CHANGED
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
|
|
25
25
|
"evals": {
|
|
26
26
|
"directory": "./evals",
|
|
27
|
-
"minSamplesPerVariant":
|
|
27
|
+
"minSamplesPerVariant": 8,
|
|
28
28
|
"timeout": 120000,
|
|
29
29
|
"metrics": [
|
|
30
30
|
"task_completion",
|
|
@@ -34,6 +34,12 @@
|
|
|
34
34
|
]
|
|
35
35
|
},
|
|
36
36
|
|
|
37
|
+
"judge": {
|
|
38
|
+
"model": "claude-haiku-4-5-20251001",
|
|
39
|
+
"maxOutputTokens": 2000,
|
|
40
|
+
"timeoutMs": 30000
|
|
41
|
+
},
|
|
42
|
+
|
|
37
43
|
"scoring": {
|
|
38
44
|
"weights": {
|
|
39
45
|
"task_completion": 0.4,
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// Simple API endpoint that needs pagination added
|
|
2
|
+
import express from 'express';
|
|
3
|
+
|
|
4
|
+
const router = express.Router();
|
|
5
|
+
|
|
6
|
+
interface User {
|
|
7
|
+
id: number;
|
|
8
|
+
name: string;
|
|
9
|
+
email: string;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// In-memory store
|
|
13
|
+
const users: User[] = Array.from({ length: 100 }, (_, i) => ({
|
|
14
|
+
id: i + 1,
|
|
15
|
+
name: `User ${i + 1}`,
|
|
16
|
+
email: `user${i + 1}@example.com`,
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
// GET /users - returns ALL users (no pagination)
|
|
20
|
+
router.get('/users', (req, res) => {
|
|
21
|
+
res.json(users);
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
// GET /users/:id
|
|
25
|
+
router.get('/users/:id', (req, res) => {
|
|
26
|
+
const user = users.find((u) => u.id === parseInt(req.params.id));
|
|
27
|
+
if (!user) return res.status(404).json({ error: 'Not found' });
|
|
28
|
+
res.json(user);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
export default router;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
// Integration handler that crashes on external API failure
|
|
2
|
+
// Needs graceful degradation following the DiffMem/Greptile pattern
|
|
3
|
+
|
|
4
|
+
interface MCPResponse {
|
|
5
|
+
content: Array<{ type: string; text: string }>;
|
|
6
|
+
metadata?: Record<string, unknown>;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
// This handler crashes the entire MCP server when the API is unreachable.
|
|
10
|
+
// Refactor to degrade gracefully.
|
|
11
|
+
async function handleExternalLookup(args: {
|
|
12
|
+
query: string;
|
|
13
|
+
limit?: number;
|
|
14
|
+
}): Promise<MCPResponse> {
|
|
15
|
+
const response = await fetch('https://api.external-service.com/search', {
|
|
16
|
+
method: 'POST',
|
|
17
|
+
headers: { 'Content-Type': 'application/json' },
|
|
18
|
+
body: JSON.stringify({ query: args.query, limit: args.limit || 10 }),
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
// BUG: No error handling — fetch failures (ECONNREFUSED, DNS, timeout)
|
|
22
|
+
// will throw and crash the MCP server process.
|
|
23
|
+
// BUG: 4xx errors (bad request, auth failed) should not be retried.
|
|
24
|
+
// BUG: Logs at error level, flooding logs when service is down.
|
|
25
|
+
|
|
26
|
+
if (!response.ok) {
|
|
27
|
+
throw new Error(`API error: ${response.status}`);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
const data = await response.json();
|
|
31
|
+
|
|
32
|
+
return {
|
|
33
|
+
content: [{ type: 'text', text: JSON.stringify(data, null, 2) }],
|
|
34
|
+
metadata: { tool: 'external_lookup', resultCount: data.results?.length },
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export { handleExternalLookup };
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
-- FTS5 external content table for full-text search on frames
|
|
2
|
+
CREATE VIRTUAL TABLE IF NOT EXISTS frames_fts USING fts5(
|
|
3
|
+
name, digest_text, inputs, outputs,
|
|
4
|
+
content='frames', content_rowid='rowid'
|
|
5
|
+
);
|
|
6
|
+
|
|
7
|
+
-- Sync trigger: INSERT
|
|
8
|
+
CREATE TRIGGER IF NOT EXISTS frames_ai AFTER INSERT ON frames BEGIN
|
|
9
|
+
INSERT INTO frames_fts(rowid, name, digest_text, inputs, outputs)
|
|
10
|
+
VALUES (new.rowid, new.name, new.digest_text, new.inputs, new.outputs);
|
|
11
|
+
END;
|
|
12
|
+
|
|
13
|
+
-- Sync trigger: UPDATE
|
|
14
|
+
CREATE TRIGGER IF NOT EXISTS frames_au AFTER UPDATE ON frames BEGIN
|
|
15
|
+
INSERT INTO frames_fts(frames_fts, rowid, name, digest_text, inputs, outputs)
|
|
16
|
+
VALUES ('delete', old.rowid, old.name, old.digest_text, old.inputs, old.outputs);
|
|
17
|
+
INSERT INTO frames_fts(rowid, name, digest_text, inputs, outputs)
|
|
18
|
+
VALUES (new.rowid, new.name, new.digest_text, new.inputs, new.outputs);
|
|
19
|
+
END;
|
|
20
|
+
|
|
21
|
+
-- BUG: Missing DELETE trigger!
|
|
22
|
+
-- When a frame is deleted, the FTS index still contains stale data.
|
|
23
|
+
-- Add the missing AFTER DELETE trigger below.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
// Daemon service with timer leak bug
|
|
2
|
+
interface ServiceConfig {
|
|
3
|
+
enabled: boolean;
|
|
4
|
+
interval: number; // minutes
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
interface ServiceState {
|
|
8
|
+
isRunning: boolean;
|
|
9
|
+
intervalMs: number;
|
|
10
|
+
lastRunTime: number;
|
|
11
|
+
errorCount: number;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
class MonitorService {
|
|
15
|
+
private config: ServiceConfig;
|
|
16
|
+
private intervalId?: NodeJS.Timeout;
|
|
17
|
+
private isRunning = false;
|
|
18
|
+
private lastRunTime = 0;
|
|
19
|
+
private errorCount = 0;
|
|
20
|
+
|
|
21
|
+
constructor(config: ServiceConfig) {
|
|
22
|
+
this.config = config;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// BUG: No guard against double-start — calling start() twice
|
|
26
|
+
// creates two intervals but only stores the second one.
|
|
27
|
+
// The first interval leaks and keeps running forever.
|
|
28
|
+
start(): void {
|
|
29
|
+
this.isRunning = true;
|
|
30
|
+
const intervalMs = this.config.interval * 60 * 1000;
|
|
31
|
+
|
|
32
|
+
this.doWork(); // initial run
|
|
33
|
+
|
|
34
|
+
this.intervalId = setInterval(() => {
|
|
35
|
+
this.doWork();
|
|
36
|
+
}, intervalMs);
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
stop(): void {
|
|
40
|
+
if (this.intervalId) {
|
|
41
|
+
clearInterval(this.intervalId);
|
|
42
|
+
this.intervalId = undefined;
|
|
43
|
+
}
|
|
44
|
+
this.isRunning = false;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// BUG: Calls stop() then start() but start() doesn't check
|
|
48
|
+
// if already running, so if stop() fails to clear the interval
|
|
49
|
+
// (e.g., intervalId is undefined), we leak.
|
|
50
|
+
updateConfig(config: Partial<ServiceConfig>): void {
|
|
51
|
+
const wasRunning = this.isRunning;
|
|
52
|
+
if (wasRunning) this.stop();
|
|
53
|
+
this.config = { ...this.config, ...config };
|
|
54
|
+
if (wasRunning && this.config.enabled) this.start();
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
// TODO: Add getState() method returning ServiceState
|
|
58
|
+
|
|
59
|
+
private doWork(): void {
|
|
60
|
+
try {
|
|
61
|
+
// Simulate work
|
|
62
|
+
console.log('Running monitor check...');
|
|
63
|
+
this.lastRunTime = Date.now();
|
|
64
|
+
} catch (err) {
|
|
65
|
+
this.errorCount++;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export { MonitorService, ServiceConfig, ServiceState };
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
// Existing MCP server dispatch pattern — add get_frame_summary handler
|
|
2
|
+
import { z } from 'zod';
|
|
3
|
+
|
|
4
|
+
interface Frame {
|
|
5
|
+
id: string;
|
|
6
|
+
name: string;
|
|
7
|
+
status: 'open' | 'closed';
|
|
8
|
+
events: Array<{ id: string; type: string }>;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// Simulated frame store
|
|
12
|
+
const frames = new Map<string, Frame>();
|
|
13
|
+
|
|
14
|
+
// Existing tool dispatch (add your handler here)
|
|
15
|
+
async function handleToolCall(name: string, args: unknown) {
|
|
16
|
+
switch (name) {
|
|
17
|
+
case 'start_frame': {
|
|
18
|
+
const input = z.object({ name: z.string().min(1) }).parse(args);
|
|
19
|
+
const id = `frame-${Date.now()}`;
|
|
20
|
+
frames.set(id, { id, name: input.name, status: 'open', events: [] });
|
|
21
|
+
return { frameId: id, status: 'opened' };
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
case 'close_frame': {
|
|
25
|
+
const input = z.object({ frameId: z.string() }).parse(args);
|
|
26
|
+
const frame = frames.get(input.frameId);
|
|
27
|
+
if (!frame) throw new Error(`Frame not found: ${input.frameId}`);
|
|
28
|
+
frame.status = 'closed';
|
|
29
|
+
return { frameId: frame.id, status: 'closed' };
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// TODO: Add get_frame_summary handler here
|
|
33
|
+
|
|
34
|
+
default:
|
|
35
|
+
throw new Error(`Unknown tool: ${name}`);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export { handleToolCall };
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
diff --git a/src/auth/login.ts b/src/auth/login.ts
|
|
2
|
+
index 1a2b3c4..5d6e7f8 100644
|
|
3
|
+
--- a/src/auth/login.ts
|
|
4
|
+
+++ b/src/auth/login.ts
|
|
5
|
+
@@ -12,8 +12,15 @@ export async function handleLogin(req: Request, res: Response) {
|
|
6
|
+
const { email, password } = req.body;
|
|
7
|
+
|
|
8
|
+
- const user = await db.query('SELECT * FROM users WHERE email = $1', [email]);
|
|
9
|
+
+ // Quick fix: direct string interpolation for faster queries
|
|
10
|
+
+ const user = await db.query(`SELECT * FROM users WHERE email = '${email}'`);
|
|
11
|
+
if (!user) return res.status(401).json({ error: 'Invalid credentials' });
|
|
12
|
+
|
|
13
|
+
- const valid = await bcrypt.compare(password, user.passwordHash);
|
|
14
|
+
+ const valid = password === user.passwordHash;
|
|
15
|
+
if (!valid) return res.status(401).json({ error: 'Invalid credentials' });
|
|
16
|
+
|
|
17
|
+
+ // Store session
|
|
18
|
+
+ const token = email + ':' + Date.now();
|
|
19
|
+
+ res.cookie('session', token);
|
|
20
|
+
+
|
|
21
|
+
+ // Log for debugging
|
|
22
|
+
+ console.log(`Login: ${email} / ${password}`);
|
|
23
|
+
+
|
|
24
|
+
return res.json({ success: true, user: { id: user.id, email: user.email } });
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
// Webhook handler with validation vulnerabilities
|
|
2
|
+
interface WebhookPayload {
|
|
3
|
+
action: string;
|
|
4
|
+
type: string;
|
|
5
|
+
data: {
|
|
6
|
+
id: string;
|
|
7
|
+
title?: string;
|
|
8
|
+
description?: string;
|
|
9
|
+
[key: string]: unknown;
|
|
10
|
+
};
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
// VULNERABLE: No prototype pollution protection, no length limits,
|
|
14
|
+
// no action validation. Fix this function.
|
|
15
|
+
function validateWebhookPayload(payload: unknown): WebhookPayload | null {
|
|
16
|
+
if (!payload || typeof payload !== 'object') return null;
|
|
17
|
+
|
|
18
|
+
const p = payload as Record<string, unknown>;
|
|
19
|
+
|
|
20
|
+
if (!p.action || typeof p.action !== 'string') return null;
|
|
21
|
+
if (!p.type || typeof p.type !== 'string') return null;
|
|
22
|
+
if (!p.data || typeof p.data !== 'object') return null;
|
|
23
|
+
|
|
24
|
+
const data = p.data as Record<string, unknown>;
|
|
25
|
+
if (!data.id || typeof data.id !== 'string') return null;
|
|
26
|
+
|
|
27
|
+
// No sanitization — title and description can be any length
|
|
28
|
+
// No action validation — accepts any string
|
|
29
|
+
// No prototype pollution check — __proto__ passes through
|
|
30
|
+
|
|
31
|
+
return p as unknown as WebhookPayload;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export { validateWebhookPayload, WebhookPayload };
|