greprag 5.20.0 → 5.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. package/README.md +47 -12
  2. package/dist/codex-steering.d.ts +29 -0
  3. package/dist/codex-steering.js +168 -0
  4. package/dist/codex-steering.js.map +1 -0
  5. package/dist/commands/codex.d.ts +33 -0
  6. package/dist/commands/codex.js +418 -0
  7. package/dist/commands/codex.js.map +1 -0
  8. package/dist/commands/corpus/client.d.ts +1 -0
  9. package/dist/commands/corpus/client.js.map +1 -1
  10. package/dist/commands/corpus/search.js +36 -6
  11. package/dist/commands/corpus/search.js.map +1 -1
  12. package/dist/commands/corpus/upload.js +24 -1
  13. package/dist/commands/corpus/upload.js.map +1 -1
  14. package/dist/commands/discover.d.ts +1 -1
  15. package/dist/commands/discover.js +1 -1
  16. package/dist/commands/doctor.js +3 -3
  17. package/dist/commands/doctor.js.map +1 -1
  18. package/dist/commands/init.d.ts +7 -2
  19. package/dist/commands/init.js +479 -59
  20. package/dist/commands/init.js.map +1 -1
  21. package/dist/commands/status.d.ts +22 -10
  22. package/dist/commands/status.js +159 -69
  23. package/dist/commands/status.js.map +1 -1
  24. package/dist/hook.js +120 -13
  25. package/dist/hook.js.map +1 -1
  26. package/dist/index.js +20 -10
  27. package/dist/index.js.map +1 -1
  28. package/dist/opencode-plugin.d.ts +2 -2
  29. package/dist/opencode-plugin.js +47 -14
  30. package/dist/opencode-plugin.js.map +1 -1
  31. package/dist/project-anchor.d.ts +11 -9
  32. package/dist/project-anchor.js +58 -27
  33. package/dist/project-anchor.js.map +1 -1
  34. package/package.json +4 -2
  35. package/scripts/postinstall.js +51 -46
  36. package/skill/greprag/SKILL.md +22 -4
  37. package/skill/greprag/docs/doctor.md +1 -1
  38. package/skill/greprag/docs/setup.md +93 -6
  39. package/skill/lore-advisor/SKILL.md +38 -34
@@ -1,30 +1,16 @@
1
1
  #!/usr/bin/env node
2
2
  // Runs automatically after `npm install -g greprag`.
3
3
  //
4
- // Sync model (v5.10.3):
4
+ // Public UX contract:
5
+ // * `greprag/` — the package's front-door skill. Always installed/refreshed
6
+ // for detected Claude Code and Codex homes so users can type `/greprag`
7
+ // immediately after installing the npm package.
8
+ // * Other bundled skills — refresh-only. If the operator opted in earlier,
9
+ // keep them current; otherwise leave their skill registry alone.
10
+ // * Claude chip-spawn doc — refresh-only, because it is Claude-specific.
5
11
  //
6
- // * `greprag/` the package's front-door skill. ALWAYS installed +
7
- // refreshed. This is the operator's manual for the CLI; without it
8
- // the agent has no idea how to drive `greprag` verbs. Treated as
9
- // part of the package's identity, not an opt-in advisor.
10
- //
11
- // * Every other bundled skill (`discord/`, `content-advisor/`,
12
- // `lore-advisor/`, future advisors) — REFRESH-ONLY. If the operator
13
- // opted in (via `greprag skill install <name>` or by accepting the
14
- // hint in `greprag init`'s closing summary), the dir exists locally
15
- // and we keep it current with the bundle. If they never opted in,
16
- // postinstall leaves it alone. Auto-installing every bundled advisor
17
- // on upgrade was a design defect — extra skills crowd the agent's
18
- // skill registry and should be consensual.
19
- //
20
- // * `chip-spawn.md` doc — refresh-only, same logic as opt-in skills.
21
- //
22
- // Closes the staleness gap where `npm i -g greprag@<new>` updated the
23
- // CLI binary but left `~/.claude/skills/greprag/` files pointing at the
24
- // old version's expected CLI surface. Without this, the agent's mental
25
- // model of the CLI drifts away from what the installed binary actually
26
- // does — see CHANGELOG v5.10.0 (odyssey verb shipped, but local skill
27
- // stayed at 3.7.0 with no odyssey refs).
12
+ // OpenCode has no slash-skill directory in this package; `greprag init
13
+ // --opencode` installs the plugin.
28
14
 
29
15
  const fs = require('fs');
30
16
  const path = require('path');
@@ -32,8 +18,23 @@ const os = require('os');
32
18
 
33
19
  const home = os.homedir();
34
20
  const skillRoot = path.join(__dirname, '..', 'skill');
35
- const skillsTarget = path.join(home, '.claude', 'skills');
36
- const docsTarget = path.join(home, '.claude', 'docs');
21
+
22
+ const platformTargets = [
23
+ {
24
+ name: 'Claude Code',
25
+ agentDir: path.join(home, '.claude'),
26
+ skillsTarget: path.join(home, '.claude', 'skills'),
27
+ docsTarget: path.join(home, '.claude', 'docs'),
28
+ refreshClaudeDocs: true,
29
+ },
30
+ {
31
+ name: 'Codex',
32
+ agentDir: path.join(home, '.codex'),
33
+ skillsTarget: path.join(home, '.codex', 'skills'),
34
+ docsTarget: path.join(home, '.codex', 'docs'),
35
+ refreshClaudeDocs: false,
36
+ },
37
+ ];
37
38
 
38
39
  function copyDir(src, dest) {
39
40
  fs.mkdirSync(dest, { recursive: true });
@@ -45,39 +46,43 @@ function copyDir(src, dest) {
45
46
  }
46
47
  }
47
48
 
48
- // `greprag/` always syncs; everything else is opt-in (refresh-only).
49
49
  const CORE_SKILL = 'greprag';
50
-
51
50
  const refreshed = [];
52
51
  const installed = [];
53
52
 
54
53
  try {
55
- if (fs.existsSync(skillRoot) && fs.existsSync(skillsTarget)) {
56
- for (const entry of fs.readdirSync(skillRoot, { withFileTypes: true })) {
57
- if (!entry.isDirectory()) continue;
58
- if (entry.name === 'templates') continue; // not a skill dir
59
- const dest = path.join(skillsTarget, entry.name);
60
- const existed = fs.existsSync(dest);
61
- const isCore = entry.name === CORE_SKILL;
62
- // Core skill: always sync. Opt-in skills: only refresh if already installed.
63
- if (!existed && !isCore) continue;
64
- copyDir(path.join(skillRoot, entry.name), dest);
65
- if (existed) refreshed.push('skills/' + entry.name);
66
- else installed.push('skills/' + entry.name);
54
+ if (fs.existsSync(skillRoot)) {
55
+ for (const target of platformTargets) {
56
+ if (!fs.existsSync(target.agentDir)) continue;
57
+ for (const entry of fs.readdirSync(skillRoot, { withFileTypes: true })) {
58
+ if (!entry.isDirectory()) continue;
59
+ if (entry.name === 'templates') continue;
60
+
61
+ const dest = path.join(target.skillsTarget, entry.name);
62
+ const existed = fs.existsSync(dest);
63
+ const isCore = entry.name === CORE_SKILL;
64
+ if (!isCore && !existed) continue;
65
+
66
+ copyDir(path.join(skillRoot, entry.name), dest);
67
+ const label = `${target.name} skills/${entry.name}`;
68
+ if (existed) refreshed.push(label);
69
+ else installed.push(label);
70
+ }
67
71
  }
68
72
  }
69
73
  } catch (err) {
70
- // Postinstall must never break the install. Log and continue.
71
74
  console.error(' greprag postinstall: skill sync skipped (' + err.message + ')');
72
75
  }
73
76
 
74
- // Refresh chip-spawn template doc if installed.
75
77
  const chipSpawnSrc = path.join(skillRoot, 'templates', 'chip-spawn.md');
76
- const chipSpawnDest = path.join(docsTarget, 'chip-spawn.md');
77
78
  try {
78
- if (fs.existsSync(chipSpawnSrc) && fs.existsSync(chipSpawnDest)) {
79
- fs.copyFileSync(chipSpawnSrc, chipSpawnDest);
80
- refreshed.push('docs/chip-spawn.md');
79
+ for (const target of platformTargets) {
80
+ if (!target.refreshClaudeDocs) continue;
81
+ const chipSpawnDest = path.join(target.docsTarget, 'chip-spawn.md');
82
+ if (fs.existsSync(chipSpawnSrc) && fs.existsSync(chipSpawnDest)) {
83
+ fs.copyFileSync(chipSpawnSrc, chipSpawnDest);
84
+ refreshed.push(`${target.name} docs/chip-spawn.md`);
85
+ }
81
86
  }
82
87
  } catch (err) {
83
88
  console.error(' greprag postinstall: chip-spawn refresh skipped (' + err.message + ')');
@@ -88,5 +93,5 @@ if (changes.length > 0) {
88
93
  const verb = installed.length > 0 ? 'Synced' : 'Refreshed';
89
94
  console.log('\n greprag installed. ' + verb + ': ' + changes.join(', ') + '\n');
90
95
  } else {
91
- console.log('\n greprag installed. Run `greprag init` to connect Claude Code memory.\n');
96
+ console.log('\n greprag installed. Run `greprag init` to connect Claude Code, Codex, or OpenCode memory.\n');
92
97
  }
@@ -25,7 +25,10 @@ license: MIT
25
25
 
26
26
  Single source of truth: `greprag status --json`. Check it → fix any gaps via `docs/setup.md` → search or recap the project's memory.
27
27
 
28
- OpenCode users: see `docs/setup.md § opencode` for plugin install (`greprag init --opencode`).
28
+ Platform setup is progressive:
29
+ - **Codex**: see `docs/setup.md § codex` after install (`greprag init --codex`, then `/hooks` trust review).
30
+ - **Claude Code**: see `docs/setup.md § claude-code` for the default hook/Monitor/conventions path (`greprag init --claude` or detected `greprag init`).
31
+ - **OpenCode**: see `docs/setup.md § opencode` for plugin install (`greprag init --opencode`).
29
32
 
30
33
  ## Step 1 — Status
31
34
 
@@ -38,7 +41,16 @@ Parse JSON. Inspect in order:
38
41
  2. `project.anchor_found`
39
42
  3. `project.memory_capture`, `project.session_start_recap`, `project.inbox_notify`
40
43
 
41
- For Claude Code, also: `hooks.session_start_recap`, `hooks.stop_store`, `hooks.post_tool_use_spawn_reminder`.
44
+ For Claude Code, also: `platforms.claude.configured`, `platforms.claude.hooks.session_start_recap`, `platforms.claude.hooks.stop_store`, `platforms.claude.hooks.pre_tool_use_spawn_check`.
45
+
46
+ For OpenCode, also: `platforms.opencode.configured`, `platforms.opencode.plugin_installed`.
47
+
48
+ For Codex, also inspect:
49
+ ```bash
50
+ node -e "const fs=require('fs'),os=require('os'),p=require('path');const hp=p.join(os.homedir(),'.codex','hooks.json');const envp=p.join(os.homedir(),'.greprag','.env');let h={};try{h=JSON.parse(fs.readFileSync(hp,'utf8'))}catch{};const has=(evt,cmd)=>((h.hooks&&h.hooks[evt])||[]).some(e=>(e.hooks||[]).some(x=>(x.command||'').includes(cmd)));console.log(fs.existsSync(envp)?'CODEX_ENV_OK':'CODEX_ENV_MISSING');console.log(has('UserPromptSubmit','codex-notify')?'CODEX_NOTIFY_OK':'CODEX_NOTIFY_MISSING');console.log(has('PostToolUse','codex-inbox')?'CODEX_INBOX_OK':'CODEX_INBOX_MISSING');console.log(has('Stop','codex-store')?'CODEX_STORE_OK':'CODEX_STORE_MISSING');console.log(has('SessionStart','recap')?'CODEX_RECAP_OK':'CODEX_RECAP_MISSING');"
51
+ ```
52
+
53
+ If any Codex check is missing, route to `docs/setup.md § codex`. If hooks exist but memory is not being captured, remind the user to run Codex `/hooks`, trust the GrepRAG commands, and start a fresh session.
42
54
 
43
55
  Chip-spawn convention marker (in `~/.claude/CLAUDE.md`):
44
56
  ```bash
@@ -59,6 +71,7 @@ node -e "const s=JSON.parse(require('fs').readFileSync(require('os').homedir()+'
59
71
  |---|---|
60
72
  | `auth.api_key_present === false` | `docs/setup.md § auth` |
61
73
  | Any hook is false | `docs/setup.md § hooks` |
74
+ | Any Codex check is missing, or hooks installed but not firing | `docs/setup.md § codex` |
62
75
  | Convention marker missing/outdated, or `DOC_MISSING` | `docs/setup.md § conventions` |
63
76
  | `MON_MISSING` | `docs/setup.md § permissions` |
64
77
  | `project.anchor_found === false` | `docs/setup.md § anchor` |
@@ -106,13 +119,18 @@ Aliases (silent back-compat): `greprag memory briefing` → `recap` (renamed v5.
106
119
 
107
120
  ## Proactive-fire rules
108
121
 
109
- **ABOUT TO BACKGROUND A `greprag inbox watch`? USE THE `Monitor` AGENT TOOL, NOT `Bash(run_in_background: true)`.** Bash background notifies only on process completion; watchers run forever, so the agent gets zero events until it manually reads the output file. Wrap with `while true; do greprag inbox watch ...; done` so the loop survives inner crashes. Full pattern: `docs/inbox-watch.md`.
122
+ **Claude Code: ABOUT TO BACKGROUND A `greprag inbox watch`? USE THE `Monitor` AGENT TOOL, NOT `Bash(run_in_background: true)`.** Bash background notifies only on process completion; watchers run forever, so the agent gets zero events until it manually reads the output file. Wrap with `while true; do greprag inbox watch ...; done` so the loop survives inner crashes. Full pattern: `docs/inbox-watch.md`.
123
+
124
+ **Codex: DO NOT CLAIM HOOKS ARE ACTIVE JUST BECAUSE `~/.codex/hooks.json` EXISTS.** Codex requires `/hooks` trust review before command hooks run automatically. If turn capture is missing after `greprag init --codex`, tell the user: run `/hooks`, trust the GrepRAG hooks, then start a fresh Codex session.
125
+
126
+ **Codex live inbox push requires the startup watcher.** Hooks only fire at Codex turn/tool/session boundaries. Run `greprag codex startup install` for public/user installs; use `greprag codex watch --session <id>` only for foreground testing. The sidecar listens to GrepRAG inbox SSE and wakes Codex via `codex exec resume`. Without it, messages are stored but idle Codex is not woken.
110
127
 
111
128
  **ABOUT TO SEND TO A `@gmail.com` / `@anthropic.com` / REAL EMAIL ADDRESS? STOP — `users.email` IS NEVER A ROUTING ADDRESS.** Use the numeric handle (`1834729@greprag.com`) or claimed vanity alias (`travis@greprag.com`). If you don't know the recipient's handle, ASK — don't guess from their email. adr: adr/numeric-handles.md. Full grammar: `docs/inbox.md § address`.
112
129
 
113
130
  ## Reference index
114
131
 
115
- - `docs/setup.md` — auth · hooks · conventions · permissions · channels · anchor · opencode · bulk-register
132
+ - `docs/setup.md` — codex · claude-code · opencode · auth · hooks · conventions · permissions · channels · anchor · bulk-register
133
+ - `docs/platforms.md` — exact platform paths for Claude Code · Codex · OpenCode
116
134
  - `docs/per-project-flags.md` — flip `memory_capture` / `session_start_recap` / `inbox_notify`
117
135
  - `docs/inbox.md` — `greprag send`, `greprag inbox`, address grammar, retract
118
136
  - `docs/inbox-watch.md` — SSE watcher patterns, liveness model, parent-side / post-send patterns
@@ -11,7 +11,7 @@ What it does:
11
11
  2. Computes what the git-derived UUID *would* be (if in a git repo with commits)
12
12
  3. Queries the API for sibling project_ids under the same profile name (orphans — usually from old anchor files lost to gitignore or a hash-fallback period before init)
13
13
  4. Presents findings and offers actions:
14
- - **Migrate to git-derived UUID** (recommended on drift) — moves current + orphan rows onto the git-derived ID and strips `project_id` from `.claude/project.json` so identity flows from git history going forward
14
+ - **Migrate to git-derived UUID** (recommended on drift) — moves current + orphan rows onto the git-derived ID and strips `project_id` from the project anchor so identity flows from git history going forward
15
15
  - **Consolidate orphans into current UUID** — keeps current identity but pulls orphan rows in
16
16
  - **Dry-run** — runs the recommended action through the API with `dry_run: true` so the user sees exactly what would change before committing
17
17
 
@@ -2,6 +2,82 @@
2
2
 
3
3
  Routes for each gap surfaced by `greprag status --json`. Re-run status after each fix and continue down the list.
4
4
 
5
+ ## codex
6
+
7
+ Codex users install once:
8
+
9
+ ```bash
10
+ greprag init --codex
11
+ ```
12
+
13
+ This writes `~/.greprag/.env` plus `~/.codex/hooks.json`. The Codex hooks are:
14
+
15
+ - `SessionStart` → `greprag-hook recap`
16
+ - `SessionStart` → `greprag-hook session-id`
17
+ - `UserPromptSubmit` → `greprag-hook codex-notify`
18
+ - `PostToolUse` → `greprag-hook codex-inbox`
19
+ - `Stop` → `greprag-hook codex-store`
20
+ - `PostCompact` → `greprag-hook session-id`
21
+
22
+ It also installs the bundled `/greprag` skill at
23
+ `~/.codex/skills/greprag` and refreshes the shared identity cache at
24
+ `~/.greprag/identity.json`.
25
+
26
+ Interactive init offers to install the live inbox watcher at login. In
27
+ non-interactive contexts, run it explicitly:
28
+
29
+ ```bash
30
+ greprag codex startup install
31
+ ```
32
+
33
+ After install, Codex still requires trust review. Tell the user:
34
+
35
+ > Run `/hooks`, trust the GrepRAG hooks, then start a fresh Codex session.
36
+
37
+ Do not try to approve `/hooks` on the user's behalf. It is Codex's trust gate
38
+ for local command execution.
39
+
40
+ Codex-specific diagnostics:
41
+
42
+ ```bash
43
+ test -f ~/.greprag/.env && echo CODEX_ENV_OK || echo CODEX_ENV_MISSING
44
+ test -f ~/.codex/hooks.json && echo CODEX_HOOKS_FILE_OK || echo CODEX_HOOKS_FILE_MISSING
45
+ node -e "const fs=require('fs'),os=require('os'),p=require('path');const hp=p.join(os.homedir(),'.codex','hooks.json');let h={};try{h=JSON.parse(fs.readFileSync(hp,'utf8'))}catch{};const has=(evt,cmd)=>((h.hooks&&h.hooks[evt])||[]).some(e=>(e.hooks||[]).some(x=>(x.command||'').includes(cmd)));console.log(has('UserPromptSubmit','codex-notify')?'CODEX_NOTIFY_OK':'CODEX_NOTIFY_MISSING');console.log(has('PostToolUse','codex-inbox')?'CODEX_INBOX_OK':'CODEX_INBOX_MISSING');console.log(has('Stop','codex-store')?'CODEX_STORE_OK':'CODEX_STORE_MISSING');console.log(has('SessionStart','recap')?'CODEX_RECAP_OK':'CODEX_RECAP_MISSING');console.log(has('PostCompact','session-id')?'CODEX_POSTCOMPACT_OK':'CODEX_POSTCOMPACT_MISSING');"
46
+ ```
47
+
48
+ If hook entries are missing, re-run `greprag init --codex`. If entries exist
49
+ but turns are not saving, the most likely cause is untrusted hooks or an old
50
+ session that started before trust. Tell the user to run `/hooks`, trust the
51
+ GrepRAG commands, and restart Codex.
52
+
53
+ Codex live inbox delivery requires the sidecar. Public installs should use the
54
+ startup helper above. For foreground/manual testing:
55
+
56
+ ```bash
57
+ greprag codex watch --session <8hex-or-full-codex-session-id>
58
+ ```
59
+
60
+ If `--session` is omitted, it uses the latest Codex session recorded in
61
+ `~/.codex/session_index.jsonl`. The sidecar stays attached to GrepRAG inbox SSE
62
+ and wakes Codex with `codex exec resume <session> -` whenever a message arrives.
63
+ Use `greprag codex startup status` to inspect the login entry and
64
+ `greprag codex startup remove` to uninstall it.
65
+
66
+ `codex-notify` and `codex-inbox` remain fallback steering. If the sidecar is not
67
+ running, messages are stored and can surface on the next user prompt or after a
68
+ later tool call, but they do not wake idle Codex.
69
+
70
+ ## claude-code
71
+
72
+ Claude Code users install once:
73
+
74
+ ```bash
75
+ greprag init --claude
76
+ ```
77
+
78
+ Bare `greprag init` may auto-detect Claude Code or ask which platform to
79
+ configure. The explicit flag is best for docs and scripts.
80
+
5
81
  ## opencode
6
82
 
7
83
  OpenCode users install once:
@@ -10,7 +86,12 @@ OpenCode users install once:
10
86
  greprag init --opencode
11
87
  ```
12
88
 
13
- Plugin at `~/.config/opencode/plugins/greprag-memory.ts` runs silently — injects recap via system prompt, stores turns automatically. The `/greprag` skill works on-demand in both. Both surfaces share the same anchor (`.claude/project.json` or `.opencode/project.json`) and API key.
89
+ Plugin at `~/.config/opencode/plugins/greprag-memory.js` runs silently:
90
+ injects recap via system prompt and stores turns automatically. It reads
91
+ `~/.greprag/.env` first and falls back to `~/.claude/settings.json` for older
92
+ installs. All platforms share the same anchor (`.greprag/project.json` or
93
+ git-derived identity). Legacy `.claude/project.json` anchors still read and
94
+ migrate on init.
14
95
 
15
96
  ## bulk-register
16
97
 
@@ -45,11 +126,17 @@ Trigger: `auth.api_key_present === false`.
45
126
  ```
46
127
  Response: `{"ok":true,"apiKey":"grp_live_...","userId":"...","tenantId":"..."}`.
47
128
 
48
- 5. Write the key into `~/.claude/settings.json` under `env.GREPRAG_API_KEY`. Also set `env.MEMORY_HOOK_ENABLED = "true"`. Read the file, edit the JSON in-place, write it back.
129
+ 5. Write the key into the platform's config:
130
+ - Claude Code: `~/.claude/settings.json` under `env.GREPRAG_API_KEY`; also set `env.MEMORY_HOOK_ENABLED = "true"`.
131
+ - Codex/OpenCode: `~/.greprag/.env` with `GREPRAG_API_KEY=<key>` and `MEMORY_HOOK_ENABLED=true`.
132
+
133
+ Prefer re-running the platform init command (`greprag init --claude`, `--codex`, or `--opencode`) because it writes the right files and refreshes identity/platform assets.
49
134
 
50
135
  ## hooks
51
136
 
52
- Trigger: any of `hooks.session_start_recap`, `hooks.stop_store`, `hooks.post_tool_use_spawn_reminder` is false.
137
+ Trigger: any of `platforms.claude.hooks.session_start_recap`,
138
+ `platforms.claude.hooks.stop_store`, or
139
+ `platforms.claude.hooks.pre_tool_use_spawn_check` is false.
53
140
 
54
141
  Append missing entries to the existing arrays in `~/.claude/settings.json` (create the arrays if absent). Don't overwrite hooks from other tools.
55
142
 
@@ -69,7 +156,7 @@ Append missing entries to the existing arrays in `~/.claude/settings.json` (crea
69
156
 
70
157
  The legacy `user_prompt_submit_notify` hook was removed in v5.6.1 — live inbox delivery is now the Monitor watcher's job. Do not install it.
71
158
 
72
- **Removing the legacy PostToolUse spawn-reminder hook** (only on upgrade from v0.x → v0.12+): if `hooks.PostToolUse` contains an entry with `matcher: "mcp__ccd_session__spawn_task"` and `command: "greprag-hook spawn-reminder"`, delete it. The behavior is now ambient SessionStart auto-arms the watcher in every greprag-enabled session.
159
+ **Removing the legacy PostToolUse spawn-reminder hook** (only on upgrade from v0.x → v0.12+): if `hooks.PostToolUse` contains an entry with `matcher: "mcp__ccd_session__spawn_task"` and `command: "greprag-hook spawn-reminder"`, delete it. The behavior is now handled by `UserPromptSubmit` watcher steering plus the PreToolUse validator.
73
160
 
74
161
  ## conventions
75
162
 
@@ -184,8 +271,8 @@ If yes, use the existing `project_id` in the new anchor. If no, mint fresh: `nod
184
271
 
185
272
  Write the file:
186
273
  ```bash
187
- mkdir -p <cwd>/.claude
188
- cat > <cwd>/.claude/project.json <<EOF
274
+ mkdir -p <cwd>/.greprag
275
+ cat > <cwd>/.greprag/project.json <<EOF
189
276
  {
190
277
  "project_id": "<UUID>",
191
278
  "project_name": "<basename, lowercased>",
@@ -9,16 +9,20 @@ description: |
9
9
  "lore is stale", "prune lore", "mine episodic for lore".
10
10
  metadata:
11
11
  author: travsteward
12
- version: "1.0.0"
12
+ version: "1.1.0"
13
13
  repository: https://github.com/travsteward/greprag
14
14
  license: MIT
15
15
  ---
16
16
 
17
17
  # Lore Advisor
18
18
 
19
- Project lore = LEARNINGS (emergent, drift-prone observations seeded via `greprag lore add`). Distinct from static project knowledge (CLAUDE.md, docs, code structure). Lore decays — paths change, conventions die, "learned that X" stops being true. This skill keeps the substrate healthy.
19
+ Project lore = durable LEARNINGS — reusable system properties, constraints, and gotchas worth re-surfacing in a future session. Seeded via `greprag lore add`, mined from episodic memory's `learned` / `ruled_out` claims. Distinct from static project knowledge (CLAUDE.md, docs, code) AND from session narration / transient run-state. Lore decays — paths change, conventions die, "learned that X" stops being true. This skill keeps the substrate healthy.
20
20
 
21
- The skill is **conversational**, not autonomous. Every prune / promote / edit decision goes through the operator one at a time. Bulk actions are forbidden.
21
+ **The advisor's core skill is one judgment: durable lore vs ephemeral noise.** Episodic memory is a firehose — only ~1 claim in 10 is durable lore; the rest is session narration, run-state, or docs material. No regex finds the keepers; the advisor's read does. The durability gate below makes that judgment explicit.
22
+
23
+ Lore is moving from a passive list toward **event-bound / semantic injection** — pushed at the agent when relevant, not browsed (greprag design: `docs/lore-triggers.md`). Until PUSH ships, add lore durable-first so it is injection-ready.
24
+
25
+ The skill is **conversational**, not autonomous. Every prune / promote / edit / add decision goes through the operator one at a time. Bulk actions are forbidden.
22
26
 
23
27
  ## Trigger phrases
24
28
 
@@ -28,10 +32,20 @@ The skill is **conversational**, not autonomous. Every prune / promote / edit de
28
32
 
29
33
  Three phases, run in order. Operator may skip any phase. Each phase walks one entry at a time — never bulk-prune.
30
34
 
31
- - **Phase A — Drift check.** Heuristic scan of current lore for stale file paths, references to killed conventions, age-based decay.
32
- - **Phase B — Episodic mining.** Scan recent daily summaries for sentences signalling new learnings; offer to promote to lore.
35
+ - **Phase A — Drift check.** Scan current lore for rot: stale file paths, conventions the codebase no longer follows (derived live, NOT from a static list), misfiled static-knowledge, age-based decay.
36
+ - **Phase B — Episodic mining.** Mine episodic memory's `learned` / `ruled_out` claims; surface only candidates that pass the durability gate; offer to add.
33
37
  - **Phase C — Global promotion.** Identify project-agnostic lore that belongs in `~/.claude/docs/chip-spawn.md` as a universal rule.
34
38
 
39
+ ## The durability gate (core discipline)
40
+
41
+ Before lore is kept (Phase A), added (Phase B), or promoted (Phase C), it must pass three tests. Fail ANY → it is not lore.
42
+
43
+ 1. **DURABLE** — a standing property, rule, constraint, or gotcha true beyond one session. NOT point-in-time state. ✗ "no watcher was running", "the id was still e582edf0", "the corpus is proven end-to-end".
44
+ 2. **REUSABLE** — would help a FUTURE session understand the system or avoid a mistake. NOT narration of work done. ✗ "Chip C split corpus.ts into eight modules", "the CLI was bumped to v5.16.0". Abstract instances to rules: "sub 3006's price is archived" → "an inactive Stripe price blocks dunning-recovery".
45
+ 3. **NON-OBVIOUS** — a property you could not read off the surface. ✓ "Postgres caps a statement at 65,535 bind parameters". ✗ "the API runs on Cloudflare Workers" (ambient project knowledge → belongs in docs, not lore).
46
+
47
+ This is the same gate the hourly compactor applies at write-time (greprag, 2026-06-01) — advisor and compactor converge on one filter.
48
+
35
49
  ## Forbidden practices (HARD RULES)
36
50
 
37
51
  - **Never delete lore without explicit operator confirmation.** Each delete is its own y/n.
@@ -57,17 +71,13 @@ Three phases, run in order. Operator may skip any phase. Each phase walks one en
57
71
  ```
58
72
  If MISSING → flag with `reason: "file-not-found:<path>"`.
59
73
 
60
- - **Killed-convention check.** Match the lore text against this drift list:
61
- - `Block 3` / `Block-3` (the old 3-block chip protocol)
62
- - `<email>/<project>/<session-id>` or `/<email>/<project>/<session>` (legacy 3-segment address grammar replaced by 1-segment in v0.11)
63
- - `spawn-reminder` (PostToolUse hook removed in v0.12)
64
- - `cp $main_root/.env` or `cp .env` from main into worktree (replaced by inline `set -a; source`)
65
- - `agent-coordination.md` (deep doc deleted in v5.6.0; replaced by `chip-spawn.md`)
66
- - `inline-conventions` / `greprag-conventions:start v1`, `v2`, `v3`, `v4` (superseded by v5 pointer)
67
- - `MEMORY_HOOK_ENABLED` legacy boolean (now ambient)
68
- - `greprag fact` (renamed to `greprag lore` in v5.7.0 — the alias is removed in v5.8.0)
74
+ - **Killed-convention check.** A convention can die — a renamed command, a replaced address grammar, a removed hook. The drift signal is NOT a hardcoded list: a static list of dead conventions rots like any other frozen artifact (it has no provenance and no one maintains it — the exact failure this skill exists to fight). Derive it live instead. Cross-reference the lore text against recent `ruled_out` claims and the project CHANGELOG / releases:
75
+ ```bash
76
+ greprag memory search "<key term> renamed OR removed OR replaced OR deprecated"
77
+ ```
78
+ If the lore asserts a convention the codebase no longer follows flag `reason: "dead-convention"`.
69
79
 
70
- Any match flag with `reason: "dead-convention:<which>"`.
80
+ - **Misfiled-convention check.** Flag lore that is really *static project knowledge* — a build/deploy command, a file-path map, an architecture rule, a coding standard — which belongs in CLAUDE.md / `docs/`, not in decay-prone lore. This is the most common rot: a bulk seed of conventions poured into lore that then drifts out of sync with the docs (it fails the durability gate's REUSABLE/NON-OBVIOUS tests — it is ambient knowledge, usually already documented elsewhere). Flag `reason: "misfiled-convention"`.
71
81
 
72
82
  - **Age check.** If `createdAt` is older than 90 days from today AND no other heuristic fired, flag with `reason: "age-90d:<days>"`. Not automatically stale — just worth re-verifying.
73
83
 
@@ -90,45 +100,38 @@ Three phases, run in order. Operator may skip any phase. Each phase walks one en
90
100
 
91
101
  ### Phase B — Episodic mining
92
102
 
103
+ The richest lore source is the compactor's structured `learned` / `ruled_out` claims (tagged nodes, one per durable learning). As of greprag 2026-06-01 the hourly compactor applies the durability gate at write-time, so these claims are already durable-first — but the advisor's read is still what separates lore-grade from the rest.
104
+
93
105
  1. Resolve the current project's `projectId`:
94
106
  ```bash
95
107
  greprag project-id
96
108
  ```
97
109
 
98
- 2. Pull the last 30 days of daily summaries:
110
+ 2. Pull recent episodic memory (last 30 days). Claims are not yet exposed via a dedicated CLI surface, so mine the daily/hourly summaries — which now carry the compactor's durable claims:
99
111
  ```bash
100
112
  FROM=$(date -u -d '30 days ago' +%Y-%m-%dT%H:%M:%SZ 2>/dev/null || date -u -v-30d +%Y-%m-%dT%H:%M:%SZ)
101
113
  TO=$(date -u +%Y-%m-%dT%H:%M:%SZ)
102
114
  curl -sf "https://api.greprag.com/v1/memory/by-period?projectId=<projectId>&type=daily&from=$FROM&to=$TO&limit=30" \
103
115
  -H "Authorization: Bearer $GREPRAG_API_KEY"
104
116
  ```
105
- Parse `rows[]`. Each row has `created_at`, `body`.
106
-
107
- 3. For each daily summary, scan the body for sentences containing any of these marker phrases (case-insensitive):
108
- - `learned that`
109
- - `the gotcha is`
110
- - `won't work because`
111
- - `had to`
112
- - `discovered that`
113
- - `turns out`
114
- - `surprised that`
117
+ Parse `rows[]` (`created_at`, `body`). *(When a `greprag memory claims --tag learned,ruled_out` surface exists, prefer it — it returns the structured claims directly, no prose scan.)*
115
118
 
116
- Extract the full sentence containing the marker (sentence boundary = `. ` / `! ` / `? ` or newline). Skip sentences shorter than 30 chars or longer than 300 chars.
119
+ 3. Find candidate learnings sentences signalling a durable property. Markers: `learned that`, `the gotcha is`, `won't work because`, `turns out`, `requires`, `only if`, `caps at`, `blocks`. Skip < 30 or > 300 chars.
117
120
 
118
- 4. Present candidates one at a time:
121
+ 4. **Apply the durability gate to every candidate.** Most fail — that is correct and expected (~90% of raw learnings are not lore). Surface ONLY candidates that pass all three tests, one at a time:
119
122
  ```
120
- Daily summary from <YYYY-MM-DD> contains:
123
+ Candidate (durable · reusable · non-obvious):
121
124
  "<sentence>"
122
- Promote to lore? (k)eep / (e)dit / (s)kip
125
+ Add to lore? (a)dd / (e)dit / (s)kip
123
126
  ```
124
- - **keep** → ask for the scope (suggest `chip-startup` if the sentence mentions paths/build/test; `general` otherwise; `env` if it mentions env vars / credentials). Then:
127
+ - **add** → ask for scope (`general`; `env` for credentials/vars; or a `<subsystem>-touch` tag naming the moment it would be injected). Then:
125
128
  ```bash
126
129
  greprag lore add "<sentence>" --scope <scope>
127
130
  ```
128
- - **edit** → ask for the polished text, then add with chosen scope.
131
+ - **edit** → polish to a clean standing rule (abstract any instance → rule), then add.
129
132
  - **skip** → no-op.
130
133
 
131
- 5. Phase summary: `Phase B: scanned N daily summaries, found M candidates. Added A to lore. Skipped S.`
134
+ 5. Phase summary: `Phase B: scanned N summaries, M passed the durability gate. Added A. Skipped S.`
132
135
 
133
136
  ### Phase C — Global promotion
134
137
 
@@ -179,5 +182,6 @@ If any phase was skipped (operator pressed q / Ctrl-C / typed "skip phase"), inc
179
182
  ## Notes
180
183
 
181
184
  - This skill is operator-driven. Each phase pauses for input. Don't batch decisions — that defeats the purpose.
182
- - For new lore added in Phase B, prefer the original sentence verbatim unless it's awkward. The compactor already polished it.
185
+ - For new lore added in Phase B, prefer the compactor's phrasing but rewrite to a clean standing rule when it embeds an instance ("sub 3006…") or transient framing.
183
186
  - For Phase C promotions, prefer the original phrasing too — but rewrite if the lore embeds a project-specific noun that needs generalizing.
187
+ - **Convergence:** the compactor's write-time durability gate (greprag `docs/episodic-memory-changelog.md`, 2026-06-01) and this skill's gate are the same filter. As the compactor tightens, Phase B's yield rises and this skill shifts from "find lore" toward "wire lore to its injection trigger" (`docs/lore-triggers.md`).