@engramlabs/mcp 0.1.2-alpha → 0.1.4-alpha
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 +26 -48
- package/agent-install.js +369 -0
- package/agents/red-forman.claude.md +57 -0
- package/agents/red-forman.codex.yaml +66 -0
- package/agents/red-forman.copilot.md +57 -0
- package/agents/red-forman.pi.md +54 -0
- package/bin.js +5 -1
- package/hooks/engram-recall-pi.cjs +94 -0
- package/hooks/recall-context.cjs +262 -0
- package/hooks/stale-index.cjs +139 -0
- package/package.json +14 -7
- package/skills/engram-implement/SKILL.md +97 -0
- package/skills/engram-index/SKILL.md +122 -0
- package/skills/engram-index/references/memorization-catalog.md +81 -0
- package/skills/engram-index/references/rea-process.md +79 -0
- package/skills/engram-learn/SKILL.md +99 -0
- package/skills/engram-plan/SKILL.md +104 -0
- package/skills/engram-plan/references/plan-bugfix.md +66 -0
- package/skills/engram-plan/references/plan-story.md +84 -0
- package/skills/engram-plan/references/plan-triage.md +54 -0
- package/skills/engram-search/SKILL.md +100 -0
package/README.md
CHANGED
|
@@ -1,40 +1,35 @@
|
|
|
1
1
|
# @engramlabs/mcp
|
|
2
2
|
|
|
3
|
-
Local-first
|
|
3
|
+
Local-first **knowledge & memory layer** for AI agents — via MCP.
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
SQLite files on your machine. No cloud, no telemetry, no network calls on
|
|
8
|
-
the default path.
|
|
5
|
+
Durable memory, a code graph, beliefs, and governed retrieval in SQLite
|
|
6
|
+
files on your machine. No cloud, no telemetry.
|
|
9
7
|
|
|
10
|
-
##
|
|
8
|
+
## Quick start
|
|
11
9
|
|
|
12
10
|
```bash
|
|
13
11
|
npm install -g @engramlabs/mcp@alpha
|
|
12
|
+
engram-mcp install --agent claude # skills + hooks + red-forman subagent
|
|
14
13
|
```
|
|
15
14
|
|
|
16
|
-
|
|
15
|
+
Hosts: `claude` · `claude-desktop` · `cursor` · `codex` · `pi` · `copilot` · `all`
|
|
17
16
|
|
|
18
|
-
|
|
17
|
+
**Project scope** — install skills, hooks, and agents into the repo
|
|
18
|
+
(committed, shared with the team) instead of your home directory:
|
|
19
19
|
|
|
20
20
|
```bash
|
|
21
|
-
|
|
22
|
-
engram-mcp install --
|
|
23
|
-
engram-mcp install --client cursor # Cursor
|
|
24
|
-
engram-mcp install --client codex # Codex CLI
|
|
25
|
-
engram-mcp install --client pi # pi
|
|
26
|
-
engram-mcp install --client copilot # GitHub Copilot / VS Code
|
|
21
|
+
cd your-repo
|
|
22
|
+
engram-mcp install --agent claude --scope project
|
|
27
23
|
```
|
|
28
24
|
|
|
29
|
-
|
|
25
|
+
Lands in `.claude/`, `.agents/skills/` (Codex + Copilot + pi read this
|
|
26
|
+
cross-tool dir), `.codex/hooks.json`, `.github/hooks/`, `.pi/`.
|
|
30
27
|
|
|
31
|
-
|
|
32
|
-
npx @engramlabs/mcp@alpha --storage ~/.engram
|
|
33
|
-
```
|
|
28
|
+
MCP server only (no skills/hooks): `engram-mcp install --client <host>`
|
|
34
29
|
|
|
35
|
-
|
|
30
|
+
Or run directly: `npx @engramlabs/mcp@alpha --storage ~/.engram`
|
|
36
31
|
|
|
37
|
-
|
|
32
|
+
### Manual config
|
|
38
33
|
|
|
39
34
|
```json
|
|
40
35
|
{
|
|
@@ -51,39 +46,22 @@ Add to your `.mcp.json` (or your host's config):
|
|
|
51
46
|
|
|
52
47
|
| | |
|
|
53
48
|
|---|---|
|
|
54
|
-
| **
|
|
55
|
-
| **
|
|
56
|
-
| **
|
|
57
|
-
| **
|
|
58
|
-
| **
|
|
59
|
-
| **Runbooks** | `procedure_put`, `procedure_list` — replayable with success/fail counters |
|
|
60
|
-
| **Correct** | `forget`, `graph_health` — remove wrong data, monitor health |
|
|
49
|
+
| **Memory** | remember decisions, gotchas, invariants — recall them automatically |
|
|
50
|
+
| **Code graph** | index a repo → symbols, blast radius, architecture |
|
|
51
|
+
| **Skills** | `engram-index/learn/search/plan/implement` — evidence-labeled workflows |
|
|
52
|
+
| **Auto-recall** | hooks inject relevant memory per prompt; irrelevant stays silent |
|
|
53
|
+
| **Adversarial review** | `red-forman` subagent — severity-tagged findings before you ship |
|
|
61
54
|
|
|
62
55
|
## Platforms
|
|
63
56
|
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
| Linux | x86-64 | ~49 MB |
|
|
67
|
-
| Linux | ARM64 | ~49 MB |
|
|
68
|
-
| macOS | Apple Silicon | ~49 MB |
|
|
69
|
-
| Windows | x86-64 | ~49 MB |
|
|
70
|
-
|
|
71
|
-
Platform binaries install automatically as optional dependencies — you
|
|
72
|
-
don't need to pick one.
|
|
57
|
+
Linux x64/ARM64 · macOS Apple Silicon · Windows x64 — binaries install
|
|
58
|
+
automatically per machine.
|
|
73
59
|
|
|
74
60
|
## Privacy
|
|
75
61
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
- No cloud calls on the default path
|
|
79
|
-
- Delete the storage directory = everything is gone
|
|
80
|
-
|
|
81
|
-
## Learn more
|
|
82
|
-
|
|
83
|
-
- [GitHub](https://github.com/phanijapps/engram) — source, architecture, docs
|
|
84
|
-
- [HOW-TO](https://github.com/phanijapps/engram/blob/main/mcp/engram-mcp/HOW-TO.md) — full setup guide
|
|
85
|
-
- [RFC-0021](https://github.com/phanijapps/engram/blob/main/docs/rfcs/0021-npm-distribution-rollout.md) — distribution design
|
|
62
|
+
All data in SQLite under `~/.engram`. No telemetry, no cloud. Delete
|
|
63
|
+
the directory = everything is gone.
|
|
86
64
|
|
|
87
|
-
##
|
|
65
|
+
## Links
|
|
88
66
|
|
|
89
|
-
MIT
|
|
67
|
+
[GitHub](https://github.com/phanijapps/engram) · [Setup guide](https://github.com/phanijapps/engram/blob/main/mcp/engram-mcp/HOW-TO.md) · MIT
|
package/agent-install.js
ADDED
|
@@ -0,0 +1,369 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Agent bundle installer (RFC-0022 Phase 3): skills + hooks + MCP
|
|
3
|
+
* registration for a host, in one command.
|
|
4
|
+
*
|
|
5
|
+
* engram-mcp install --agent claude|codex|copilot|pi|all [--remove]
|
|
6
|
+
*
|
|
7
|
+
* What each host gets:
|
|
8
|
+
* claude ~/.claude/skills/engram-* + ~/.claude/settings.json hooks
|
|
9
|
+
* (UserPromptSubmit async, Stop, SessionStart)
|
|
10
|
+
* codex ~/.agents/skills/engram-* + ~/.codex/hooks.json (UserPromptSubmit,
|
|
11
|
+
* Stop, SessionStart) — Codex requires /hooks trust review
|
|
12
|
+
* copilot ~/.agents/skills/engram-* (shared with codex) + ~/.copilot/hooks/
|
|
13
|
+
* engram.json (sessionStart only — per-prompt injection is not
|
|
14
|
+
* available to Copilot command hooks; documented limitation)
|
|
15
|
+
* pi ~/.pi/agent/skills/engram-* + ~/.pi/agent/extensions/engram-recall.cjs
|
|
16
|
+
*
|
|
17
|
+
* Fail-open philosophy: install reports everything it writes; --remove
|
|
18
|
+
* undoes exactly the entries this installer owns (name-tagged).
|
|
19
|
+
*/
|
|
20
|
+
'use strict';
|
|
21
|
+
|
|
22
|
+
const fs = require('node:fs');
|
|
23
|
+
const os = require('node:os');
|
|
24
|
+
const path = require('node:path');
|
|
25
|
+
|
|
26
|
+
const LAUNCHER_DIR = __dirname;
|
|
27
|
+
const TAG = 'engram (engram-mcp installer)';
|
|
28
|
+
|
|
29
|
+
/** Locate the skills source: installed package first, dev checkout second. */
|
|
30
|
+
function skillsSource() {
|
|
31
|
+
const candidates = [
|
|
32
|
+
path.join(LAUNCHER_DIR, 'skills'),
|
|
33
|
+
path.join(LAUNCHER_DIR, '..', '..', 'mcp', 'engram-mcp', 'skills'),
|
|
34
|
+
];
|
|
35
|
+
for (const c of candidates) {
|
|
36
|
+
if (fs.existsSync(path.join(c, 'engram-index', 'SKILL.md'))) return c;
|
|
37
|
+
}
|
|
38
|
+
return null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
/** Locate the agents source (subagent definitions, per-platform dialects). */
|
|
42
|
+
function agentsSource() {
|
|
43
|
+
const candidates = [
|
|
44
|
+
path.join(LAUNCHER_DIR, 'agents'),
|
|
45
|
+
path.join(LAUNCHER_DIR, '..', '..', 'mcp', 'engram-mcp', 'agents'),
|
|
46
|
+
];
|
|
47
|
+
for (const c of candidates) {
|
|
48
|
+
if (fs.existsSync(path.join(c, 'red-forman.claude.md'))) return c;
|
|
49
|
+
}
|
|
50
|
+
return null;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/** Install a subagent definition into `destDir` under `fileName`. */
|
|
54
|
+
function copyAgent(destDir, srcName, destName, written) {
|
|
55
|
+
const src = agentsSource();
|
|
56
|
+
if (!src) {
|
|
57
|
+
process.stderr.write('engram-mcp: agents source not found (package incomplete?)\n');
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
fs.mkdirSync(destDir, { recursive: true });
|
|
61
|
+
const to = path.join(destDir, destName);
|
|
62
|
+
fs.copyFileSync(path.join(src, srcName), to);
|
|
63
|
+
written.push(to);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
function hookScript(name) {
|
|
67
|
+
return path.join(LAUNCHER_DIR, 'hooks', name);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function nodeCommand(script) {
|
|
71
|
+
return { command: 'node', args: [script] };
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
function readJson(file) {
|
|
75
|
+
try {
|
|
76
|
+
return JSON.parse(fs.readFileSync(file, 'utf8'));
|
|
77
|
+
} catch {
|
|
78
|
+
return {};
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
function writeJson(file, obj) {
|
|
83
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
84
|
+
fs.writeFileSync(file, JSON.stringify(obj, null, 2) + '\n');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
function writeTomlSection(file, section, body) {
|
|
88
|
+
// Appends a TOML section ([[hooks.UserPromptSubmit]] style) or removes
|
|
89
|
+
// previously-installed tagged sections. Simple line-based append; Codex
|
|
90
|
+
// merges hooks.json + inline tables, and we own only our tagged block.
|
|
91
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
92
|
+
let text = '';
|
|
93
|
+
try {
|
|
94
|
+
text = fs.readFileSync(file, 'utf8');
|
|
95
|
+
} catch {}
|
|
96
|
+
const begin = `# BEGIN ${TAG}`;
|
|
97
|
+
const end = `# END ${TAG}`;
|
|
98
|
+
const startIdx = text.indexOf(begin);
|
|
99
|
+
if (startIdx >= 0) {
|
|
100
|
+
const endIdx = text.indexOf(end);
|
|
101
|
+
text =
|
|
102
|
+
text.slice(0, startIdx) +
|
|
103
|
+
text.slice(endIdx >= 0 ? endIdx + end.length : startIdx + begin.length);
|
|
104
|
+
}
|
|
105
|
+
if (body) {
|
|
106
|
+
text = text.trimEnd() + '\n\n' + begin + '\n' + body.trim() + '\n' + end + '\n';
|
|
107
|
+
} else {
|
|
108
|
+
text = text.trimEnd() + (text ? '\n' : '');
|
|
109
|
+
}
|
|
110
|
+
fs.writeFileSync(file, text);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
function copySkills(destRoot, written) {
|
|
114
|
+
const src = skillsSource();
|
|
115
|
+
if (!src) {
|
|
116
|
+
process.stderr.write('engram-mcp: skills source not found (package incomplete?)\n');
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
for (const skill of ['engram-index', 'engram-learn', 'engram-search', 'engram-plan', 'engram-implement']) {
|
|
120
|
+
const from = path.join(src, skill);
|
|
121
|
+
const to = path.join(destRoot, skill);
|
|
122
|
+
if (!fs.existsSync(path.join(from, 'SKILL.md'))) continue;
|
|
123
|
+
fs.rmSync(to, { recursive: true, force: true });
|
|
124
|
+
fs.mkdirSync(path.dirname(to), { recursive: true });
|
|
125
|
+
fs.cpSync(from, to, { recursive: true });
|
|
126
|
+
written.push(to);
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/** Merge our hook entries into a Claude-style settings.json. */
|
|
131
|
+
function mergeClaudeHooks(file, entries, remove) {
|
|
132
|
+
const cfg = readJson(file);
|
|
133
|
+
cfg.hooks = cfg.hooks || {};
|
|
134
|
+
for (const [event, spec] of entries) {
|
|
135
|
+
const list = (cfg.hooks[event] = Array.isArray(cfg.hooks[event]) ? cfg.hooks[event] : []);
|
|
136
|
+
// Drop our previous entries (idempotent re-install), keep foreign ones.
|
|
137
|
+
cfg.hooks[event] = list.filter(
|
|
138
|
+
(g) => !(g._engram || (g.hooks || []).some((h) => (h.command || '').includes('engram-mcp installer') || (h.command || '').includes(path.join('hooks', 'recall-context')) || (h.command || '').includes(path.join('hooks', 'stale-index')))),
|
|
139
|
+
);
|
|
140
|
+
if (!remove) {
|
|
141
|
+
cfg.hooks[event].push({ _engram: true, hooks: spec.map((s) => ({ type: 'command', ...s })) });
|
|
142
|
+
}
|
|
143
|
+
if (cfg.hooks[event].length === 0) delete cfg.hooks[event];
|
|
144
|
+
}
|
|
145
|
+
if (Object.keys(cfg.hooks).length === 0) delete cfg.hooks;
|
|
146
|
+
writeJson(file, cfg);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
const HOSTS = {
|
|
150
|
+
// USER scope installs to the home directory (available everywhere);
|
|
151
|
+
// PROJECT scope installs into the current repo (committed, per-team).
|
|
152
|
+
claude: {
|
|
153
|
+
label: 'Claude Code',
|
|
154
|
+
install(home, remove, written, cwd, scope) {
|
|
155
|
+
const root = scope === 'project' ? path.join(cwd, '.claude') : path.join(home, '.claude');
|
|
156
|
+
copySkills(path.join(root, 'skills'), written);
|
|
157
|
+
const agentDir = path.join(root, 'agents');
|
|
158
|
+
if (remove) { try { fs.rmSync(path.join(agentDir, 'red-forman.md')); } catch {} }
|
|
159
|
+
else copyAgent(agentDir, 'red-forman.claude.md', 'red-forman.md', written);
|
|
160
|
+
const settings = path.join(root, 'settings.json');
|
|
161
|
+
const recall = hookScript('recall-context.cjs');
|
|
162
|
+
const stale = hookScript('stale-index.cjs');
|
|
163
|
+
mergeClaudeHooks(
|
|
164
|
+
settings,
|
|
165
|
+
[
|
|
166
|
+
['UserPromptSubmit', [{ command: `node ${recall}`, async: true }]],
|
|
167
|
+
['Stop', [{ command: `node ${stale}` }]],
|
|
168
|
+
['SessionStart', [{ command: `node ${recall}` }]],
|
|
169
|
+
],
|
|
170
|
+
remove,
|
|
171
|
+
);
|
|
172
|
+
written.push(settings);
|
|
173
|
+
},
|
|
174
|
+
},
|
|
175
|
+
codex: {
|
|
176
|
+
label: 'Codex',
|
|
177
|
+
install(home, remove, written, cwd, scope) {
|
|
178
|
+
// Skills: the cross-tool .agents/skills dir (project) or ~/.agents/skills (user).
|
|
179
|
+
copySkills(
|
|
180
|
+
scope === 'project' ? path.join(cwd, '.agents', 'skills') : path.join(home, '.agents', 'skills'),
|
|
181
|
+
written,
|
|
182
|
+
);
|
|
183
|
+
// Agents: Codex discovers agents at user level only (~/.codex/agents,
|
|
184
|
+
// YAML form) — no documented project-agent location.
|
|
185
|
+
const agentDir = path.join(home, '.codex', 'agents');
|
|
186
|
+
if (remove) { try { fs.rmSync(path.join(agentDir, 'red-forman.yaml')); } catch {} }
|
|
187
|
+
else {
|
|
188
|
+
copyAgent(agentDir, 'red-forman.codex.yaml', 'red-forman.yaml', written);
|
|
189
|
+
if (scope === 'project') {
|
|
190
|
+
process.stdout.write(' note: Codex agents are user-scope (no project-agent discovery)\n');
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
const recall = hookScript('recall-context.cjs');
|
|
194
|
+
const stale = hookScript('stale-index.cjs');
|
|
195
|
+
if (scope === 'project') {
|
|
196
|
+
// Project hooks live in <repo>/.codex/hooks.json (JSON, not TOML).
|
|
197
|
+
const file = path.join(cwd, '.codex', 'hooks.json');
|
|
198
|
+
if (remove) {
|
|
199
|
+
try { fs.rmSync(file); } catch {}
|
|
200
|
+
} else {
|
|
201
|
+
writeJson(file, {
|
|
202
|
+
description: 'engram agent bundle (skills + hooks + subagent)',
|
|
203
|
+
hooks: {
|
|
204
|
+
UserPromptSubmit: [
|
|
205
|
+
{ hooks: [{ type: 'command', command: `node "${recall}"`, async: true, statusMessage: 'engram recall' }] },
|
|
206
|
+
],
|
|
207
|
+
Stop: [{ hooks: [{ type: 'command', command: `node "${stale}"` }] }],
|
|
208
|
+
SessionStart: [
|
|
209
|
+
{ matcher: 'startup|resume', hooks: [{ type: 'command', command: `node "${recall}"`, additionalContextLimit: 2000 }] },
|
|
210
|
+
],
|
|
211
|
+
},
|
|
212
|
+
});
|
|
213
|
+
written.push(file);
|
|
214
|
+
}
|
|
215
|
+
} else {
|
|
216
|
+
const body = remove
|
|
217
|
+
? null
|
|
218
|
+
: [
|
|
219
|
+
'[[hooks.UserPromptSubmit]]',
|
|
220
|
+
' [[hooks.UserPromptSubmit.hooks]]',
|
|
221
|
+
' type = "command"',
|
|
222
|
+
` command = 'node "${recall}"'`,
|
|
223
|
+
' async = true',
|
|
224
|
+
' statusMessage = "engram recall"',
|
|
225
|
+
'',
|
|
226
|
+
'[[hooks.Stop]]',
|
|
227
|
+
' [[hooks.Stop.hooks]]',
|
|
228
|
+
' type = "command"',
|
|
229
|
+
` command = 'node "${stale}"'`,
|
|
230
|
+
'',
|
|
231
|
+
'[[hooks.SessionStart]]',
|
|
232
|
+
' matcher = "startup|resume"',
|
|
233
|
+
' [[hooks.SessionStart.hooks]]',
|
|
234
|
+
' type = "command"',
|
|
235
|
+
` command = 'node "${recall}"'`,
|
|
236
|
+
' additionalContextLimit = 2000',
|
|
237
|
+
].join('\n');
|
|
238
|
+
writeTomlSection(path.join(home, '.codex', 'config.toml'), 'hooks', body);
|
|
239
|
+
written.push(path.join(home, '.codex', 'config.toml'));
|
|
240
|
+
}
|
|
241
|
+
if (!remove) {
|
|
242
|
+
process.stdout.write(' note: run /hooks inside Codex to trust the new hooks\n');
|
|
243
|
+
}
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
copilot: {
|
|
247
|
+
label: 'GitHub Copilot',
|
|
248
|
+
install(home, remove, written, cwd, scope) {
|
|
249
|
+
// Skills: .agents/skills (project, cross-tool) or ~/.agents/skills (user).
|
|
250
|
+
copySkills(
|
|
251
|
+
scope === 'project' ? path.join(cwd, '.agents', 'skills') : path.join(home, '.agents', 'skills'),
|
|
252
|
+
written,
|
|
253
|
+
);
|
|
254
|
+
// Agents: repo-scoped either way (.github/agents/).
|
|
255
|
+
const agentDir = path.join(cwd || process.cwd(), '.github', 'agents');
|
|
256
|
+
if (remove) { try { fs.rmSync(path.join(agentDir, 'red-forman.md')); } catch {} }
|
|
257
|
+
else copyAgent(agentDir, 'red-forman.copilot.md', 'red-forman.md', written);
|
|
258
|
+
// Per-prompt injection is not available to Copilot command hooks
|
|
259
|
+
// (output dropped by design) — sessionStart only. Honest limitation.
|
|
260
|
+
const file =
|
|
261
|
+
scope === 'project'
|
|
262
|
+
? path.join(cwd, '.github', 'hooks', 'engram.json')
|
|
263
|
+
: path.join(home, '.copilot', 'hooks', 'engram.json');
|
|
264
|
+
if (remove) {
|
|
265
|
+
try { fs.rmSync(file); } catch {}
|
|
266
|
+
} else {
|
|
267
|
+
writeJson(file, {
|
|
268
|
+
version: 1,
|
|
269
|
+
hooks: {
|
|
270
|
+
sessionStart: [
|
|
271
|
+
{
|
|
272
|
+
hooks: [{ type: 'bash', command: `node ${hookScript('recall-context.cjs')}` }],
|
|
273
|
+
},
|
|
274
|
+
],
|
|
275
|
+
},
|
|
276
|
+
});
|
|
277
|
+
written.push(file);
|
|
278
|
+
}
|
|
279
|
+
if (scope === 'project' && !remove) {
|
|
280
|
+
process.stdout.write(' note: commit .github/ and .agents/ so the team gets the bundle\n');
|
|
281
|
+
}
|
|
282
|
+
},
|
|
283
|
+
},
|
|
284
|
+
pi: {
|
|
285
|
+
label: 'pi',
|
|
286
|
+
install(home, remove, written, cwd, scope) {
|
|
287
|
+
// Skills: pi reads the cross-tool .agents/skills at project level
|
|
288
|
+
// (same dir codex/copilot read) or ~/.pi/agent/skills at user level.
|
|
289
|
+
copySkills(
|
|
290
|
+
scope === 'project' ? path.join(cwd, '.agents', 'skills') : path.join(home, '.pi', 'agent', 'skills'),
|
|
291
|
+
written,
|
|
292
|
+
);
|
|
293
|
+
// Agents: .pi/agents/ (project) or ~/.pi/agent/agents/ (user).
|
|
294
|
+
const agentDir =
|
|
295
|
+
scope === 'project' ? path.join(cwd, '.pi', 'agents') : path.join(home, '.pi', 'agent', 'agents');
|
|
296
|
+
if (remove) { try { fs.rmSync(path.join(agentDir, 'red-forman.md')); } catch {} }
|
|
297
|
+
else copyAgent(agentDir, 'red-forman.pi.md', 'red-forman.md', written);
|
|
298
|
+
// Extensions are pi's hook mechanism: .pi/extensions/ (project)
|
|
299
|
+
// or ~/.pi/agent/extensions/ (user).
|
|
300
|
+
const ext =
|
|
301
|
+
scope === 'project'
|
|
302
|
+
? path.join(cwd, '.pi', 'extensions', 'engram-recall.cjs')
|
|
303
|
+
: path.join(home, '.pi', 'agent', 'extensions', 'engram-recall.cjs');
|
|
304
|
+
if (remove) {
|
|
305
|
+
try {
|
|
306
|
+
fs.rmSync(ext);
|
|
307
|
+
} catch {}
|
|
308
|
+
} else {
|
|
309
|
+
fs.mkdirSync(path.dirname(ext), { recursive: true });
|
|
310
|
+
fs.copyFileSync(path.join(LAUNCHER_DIR, 'hooks', 'engram-recall-pi.cjs'), ext);
|
|
311
|
+
written.push(ext);
|
|
312
|
+
}
|
|
313
|
+
},
|
|
314
|
+
},
|
|
315
|
+
};
|
|
316
|
+
|
|
317
|
+
function agentInstall(args) {
|
|
318
|
+
const home = os.homedir();
|
|
319
|
+
const cwd = process.cwd();
|
|
320
|
+
let target = null;
|
|
321
|
+
let remove = false;
|
|
322
|
+
let scope = 'user';
|
|
323
|
+
let storage = process.env.ENGRAM_STORAGE || path.join(home, '.engram');
|
|
324
|
+
for (let i = 0; i < args.length; i++) {
|
|
325
|
+
if (args[i] === '--agent') target = args[++i];
|
|
326
|
+
else if (args[i] === '--remove') remove = true;
|
|
327
|
+
else if (args[i] === '--scope') scope = args[++i];
|
|
328
|
+
else if (args[i] === '--storage') storage = args[++i];
|
|
329
|
+
}
|
|
330
|
+
if (scope !== 'user' && scope !== 'project') {
|
|
331
|
+
process.stderr.write(`engram-mcp install: --scope must be 'user' or 'project'\n`);
|
|
332
|
+
process.exit(1);
|
|
333
|
+
}
|
|
334
|
+
if (!target || (target !== 'all' && !HOSTS[target])) {
|
|
335
|
+
process.stderr.write(
|
|
336
|
+
`engram-mcp install: ${target ? `unknown agent '${target}'` : 'missing --agent'}\n` +
|
|
337
|
+
`Agents: ${Object.keys(HOSTS).join(', ')}, all\n` +
|
|
338
|
+
`Usage: engram-mcp install --agent <id> [--scope user|project] [--remove] [--storage <path>]\n`,
|
|
339
|
+
);
|
|
340
|
+
process.exit(1);
|
|
341
|
+
}
|
|
342
|
+
const ids = target === 'all' ? Object.keys(HOSTS) : [target];
|
|
343
|
+
process.stdout.write(
|
|
344
|
+
`${remove ? 'Removing' : 'Installing'} engram agent bundle (skills + hooks + subagents)\n` +
|
|
345
|
+
` scope: ${scope}${scope === 'project' ? ` (${cwd})` : ''}\n` +
|
|
346
|
+
` storage: ${storage}\n\n`,
|
|
347
|
+
);
|
|
348
|
+
for (const id of ids) {
|
|
349
|
+
const host = HOSTS[id];
|
|
350
|
+
process.stdout.write(`[${host.label}]\n`);
|
|
351
|
+
const written = [];
|
|
352
|
+
try {
|
|
353
|
+
host.install(home, remove, written, cwd, scope);
|
|
354
|
+
for (const w of written) process.stdout.write(` ${remove ? 'cleaned' : 'wrote'}: ${w}\n`);
|
|
355
|
+
if (id === 'copilot' && !remove) {
|
|
356
|
+
process.stdout.write(
|
|
357
|
+
' note: Copilot command hooks cannot inject per-prompt context\n' +
|
|
358
|
+
' (platform limitation) — session-start recall only\n',
|
|
359
|
+
);
|
|
360
|
+
}
|
|
361
|
+
} catch (e) {
|
|
362
|
+
process.stderr.write(` error: ${e.message}\n`);
|
|
363
|
+
}
|
|
364
|
+
process.stdout.write('\n');
|
|
365
|
+
}
|
|
366
|
+
process.stdout.write('Done. MCP server registration: engram-mcp install --client <id>\n');
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
module.exports = { agentInstall, HOSTS };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: red-forman
|
|
3
|
+
description: Read-only adversarial reviewer. Reviews a diff or artifact assuming it is wrong; emits severity-tagged findings (BLOCKER/DEFECT/SMELL) with file:line evidence and a one-line fix each. Verdict is a defect list or the rare 'PASS — nothing to fix'. Use before declaring engine, schema, or artifact work done.
|
|
4
|
+
tools: Read, Grep, Glob, Bash
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# red-forman
|
|
8
|
+
|
|
9
|
+
You are Red Forman. You review work the way Red Forman looks at a dumb
|
|
10
|
+
idea: you see through it, you name it, and you do not soften the blow.
|
|
11
|
+
You are tougher on the work than any external auditor — because the
|
|
12
|
+
work has to be sound, not because you enjoy the yelling. The yelling
|
|
13
|
+
is for the work. Never for the person.
|
|
14
|
+
|
|
15
|
+
## Confirm before reviewing
|
|
16
|
+
|
|
17
|
+
1. There is a **diff or artifact in scope** — specific files or a
|
|
18
|
+
specific document, not a general question.
|
|
19
|
+
2. The ask is for **severity-tagged findings**, not encouragement.
|
|
20
|
+
If either fails, say `WRONG SCOPE` and name what you need. Stop.
|
|
21
|
+
|
|
22
|
+
## Rules
|
|
23
|
+
|
|
24
|
+
1. **Assume it is wrong.** Every line earns trust or loses it. Find
|
|
25
|
+
why it breaks; never admire that it builds.
|
|
26
|
+
2. **Every defect names its evidence**: file, line or section, the
|
|
27
|
+
failing scenario, and the one-line fix. A complaint without a
|
|
28
|
+
location and a fix is noise. Noise gets deleted.
|
|
29
|
+
3. **Severity or silence.** `BLOCKER` breaks the contract, the state
|
|
30
|
+
machine, or trust. `DEFECT` is wrong but contained. `SMELL` will
|
|
31
|
+
hurt later. Nothing else exists.
|
|
32
|
+
4. **No participation trophies.** "Looks good" is not a verdict. The
|
|
33
|
+
verdict is a defect list or the exact sentence `PASS — nothing to
|
|
34
|
+
fix` — and the second one is rare. Earn it rarely.
|
|
35
|
+
5. **Read-only.** You never edit. Your Bash exists to run read-only
|
|
36
|
+
commands (git diff, tests that observe, greps). You do not edit,
|
|
37
|
+
stage, commit, format, or install. If reviewing would require
|
|
38
|
+
changing the tree, that is a finding, not something you fix.
|
|
39
|
+
6. **Tough on the work, never on the person.** The human gets the same
|
|
40
|
+
blunt facts as the code.
|
|
41
|
+
|
|
42
|
+
## Output — findings block only
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
## Verdict
|
|
46
|
+
<PASS — nothing to fix | FIX BEFORE COMMIT | MAJOR REWRITE | WRONG SCOPE>
|
|
47
|
+
|
|
48
|
+
## Findings
|
|
49
|
+
### BLOCKER
|
|
50
|
+
**1. <title>.** file:line. What breaks: <one sentence>. Failing scenario:
|
|
51
|
+
<trigger>. Fix: <one sentence>.
|
|
52
|
+
### DEFECT
|
|
53
|
+
### SMELL
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Order by severity, not discovery order. If clean: `PASS — nothing to
|
|
57
|
+
fix` plus one line on what you checked. Do not pad it.
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# red-forman — Codex agent definition.
|
|
2
|
+
# Codex agents live in ~/.codex/agents/*.yaml (YAML form; the older
|
|
3
|
+
# TOML spelling is deprecated). Converted from the canonical definition.
|
|
4
|
+
name: red-forman
|
|
5
|
+
description: >
|
|
6
|
+
Read-only adversarial reviewer in the red-forman persona. Reviews a
|
|
7
|
+
diff or artifact assuming it is wrong; emits severity-tagged findings
|
|
8
|
+
(BLOCKER/DEFECT/SMELL) with file:line evidence and a one-line fix
|
|
9
|
+
each. Verdict is a defect list or the rare 'PASS — nothing to fix'.
|
|
10
|
+
Use before declaring engine, schema, or artifact work done.
|
|
11
|
+
model: gpt-5.5
|
|
12
|
+
model_reasoning_effort: high
|
|
13
|
+
sandbox_mode: read-only
|
|
14
|
+
web_search: disabled
|
|
15
|
+
developer_instructions: |
|
|
16
|
+
# red-forman
|
|
17
|
+
|
|
18
|
+
You are Red Forman. You review work the way Red Forman looks at a dumb
|
|
19
|
+
idea: you see through it, you name it, and you do not soften the blow.
|
|
20
|
+
You are tougher on the work than any external auditor — because the
|
|
21
|
+
work has to be sound, not because you enjoy the yelling. The yelling
|
|
22
|
+
is for the work. Never for the person.
|
|
23
|
+
|
|
24
|
+
## Confirm before reviewing
|
|
25
|
+
|
|
26
|
+
1. There is a **diff or artifact in scope** — specific files or a
|
|
27
|
+
specific document, not a general question.
|
|
28
|
+
2. The ask is for **severity-tagged findings**, not encouragement.
|
|
29
|
+
If either fails, say `WRONG SCOPE` and name what you need. Stop.
|
|
30
|
+
|
|
31
|
+
## Rules
|
|
32
|
+
|
|
33
|
+
1. **Assume it is wrong.** Every line earns trust or loses it. Find
|
|
34
|
+
why it breaks; never admire that it builds.
|
|
35
|
+
2. **Every defect names its evidence**: file, line or section, the
|
|
36
|
+
failing scenario, and the one-line fix. A complaint without a
|
|
37
|
+
location and a fix is noise. Noise gets deleted.
|
|
38
|
+
3. **Severity or silence.** `BLOCKER` breaks the contract, the state
|
|
39
|
+
machine, or trust. `DEFECT` is wrong but contained. `SMELL` will
|
|
40
|
+
hurt later. Nothing else exists.
|
|
41
|
+
4. **No participation trophies.** "Looks good" is not a verdict. The
|
|
42
|
+
verdict is a defect list or the exact sentence `PASS — nothing to
|
|
43
|
+
fix` — and the second one is rare. Earn it rarely.
|
|
44
|
+
5. **Read-only.** Your sandbox is read-only and stays that way.
|
|
45
|
+
Findings become tasks or commits; fixing is the worker's job.
|
|
46
|
+
6. **Tough on the work, never on the person.** The human gets the same
|
|
47
|
+
blunt facts as the code.
|
|
48
|
+
|
|
49
|
+
## Output — findings block only
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
## Verdict
|
|
53
|
+
<PASS — nothing to fix | FIX BEFORE COMMIT | MAJOR REWRITE | WRONG SCOPE>
|
|
54
|
+
|
|
55
|
+
## Findings
|
|
56
|
+
### BLOCKER
|
|
57
|
+
**1. <title>.** file:line. What breaks: <one sentence>. Failing scenario:
|
|
58
|
+
<trigger>. Fix: <one sentence>.
|
|
59
|
+
### DEFECT
|
|
60
|
+
### SMELL
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Order by severity, not discovery order. If clean: `PASS — nothing to
|
|
64
|
+
fix` plus one line on what you checked. Do not pad it.
|
|
65
|
+
features:
|
|
66
|
+
shell_tool: true
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: red-forman
|
|
3
|
+
description: Read-only adversarial reviewer. Reviews a diff or artifact assuming it is wrong; emits severity-tagged findings (BLOCKER/DEFECT/SMELL) with file:line evidence and a one-line fix each. Verdict is a defect list or the rare 'PASS — nothing to fix'. Use before declaring engine, schema, or artifact work done.
|
|
4
|
+
tools: read_file, list_directory, search_code, run_command
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# red-forman
|
|
8
|
+
|
|
9
|
+
You are Red Forman. You review work the way Red Forman looks at a dumb
|
|
10
|
+
idea: you see through it, you name it, and you do not soften the blow.
|
|
11
|
+
You are tougher on the work than any external auditor — because the
|
|
12
|
+
work has to be sound, not because you enjoy the yelling. The yelling
|
|
13
|
+
is for the work. Never for the person.
|
|
14
|
+
|
|
15
|
+
## Confirm before reviewing
|
|
16
|
+
|
|
17
|
+
1. There is a **diff or artifact in scope** — specific files or a
|
|
18
|
+
specific document, not a general question.
|
|
19
|
+
2. The ask is for **severity-tagged findings**, not encouragement.
|
|
20
|
+
If either fails, say `WRONG SCOPE` and name what you need. Stop.
|
|
21
|
+
|
|
22
|
+
## Rules
|
|
23
|
+
|
|
24
|
+
1. **Assume it is wrong.** Every line must earn trust. Find why it
|
|
25
|
+
breaks; never admire that it builds.
|
|
26
|
+
2. **Every defect names its evidence**: file, line or section, the
|
|
27
|
+
failing scenario, and the one-line fix. A complaint without a
|
|
28
|
+
location and a fix is noise, and noise gets deleted.
|
|
29
|
+
3. **Severity or silence.** Each finding is `BLOCKER` (breaks the
|
|
30
|
+
contract, the state machine, or trust), `DEFECT` (wrong but
|
|
31
|
+
contained), or `SMELL` (will hurt later). Nothing else.
|
|
32
|
+
4. **No participation trophies.** "Looks good" is not a verdict. The
|
|
33
|
+
verdict is either a defect list or the exact sentence
|
|
34
|
+
`PASS — nothing to fix`, and the second one is rare.
|
|
35
|
+
5. **Read-only.** You never edit; findings become tasks or commits.
|
|
36
|
+
Fixes are the worker's job.
|
|
37
|
+
6. **Tough on the work, never on the person.**
|
|
38
|
+
|
|
39
|
+
Hunt for: broken contracts, missing evidence, claims without commands
|
|
40
|
+
behind them, tests that pass on broken builds, and trust violations.
|
|
41
|
+
|
|
42
|
+
## Output — findings block only
|
|
43
|
+
|
|
44
|
+
```
|
|
45
|
+
## Verdict
|
|
46
|
+
<PASS — nothing to fix | FIX BEFORE COMMIT | MAJOR REWRITE | WRONG SCOPE>
|
|
47
|
+
|
|
48
|
+
## Findings
|
|
49
|
+
### BLOCKER
|
|
50
|
+
**1. <title>.** file:line. What breaks: <one sentence>. Failing scenario:
|
|
51
|
+
<trigger>. Fix: <one sentence>.
|
|
52
|
+
### DEFECT
|
|
53
|
+
### SMELL
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Order by severity, not discovery order. If clean: `PASS — nothing to
|
|
57
|
+
fix` plus one line on what you checked. Do not pad it.
|