greprag 0.6.1 → 0.7.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.
- package/dist/commands/doctor.d.ts +15 -0
- package/dist/commands/doctor.js +297 -0
- package/dist/commands/doctor.js.map +1 -0
- package/dist/commands/init.js +85 -3
- package/dist/commands/init.js.map +1 -1
- package/dist/hook.js +51 -7
- package/dist/hook.js.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/dist/project-anchor.d.ts +48 -14
- package/dist/project-anchor.js +184 -63
- package/dist/project-anchor.js.map +1 -1
- package/package.json +1 -1
- package/skill/SKILL.md +23 -0
package/dist/project-anchor.js
CHANGED
|
@@ -44,6 +44,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
44
44
|
};
|
|
45
45
|
})();
|
|
46
46
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
47
|
+
exports.computeGitDerivedProjectId = computeGitDerivedProjectId;
|
|
47
48
|
exports.readAnchor = readAnchor;
|
|
48
49
|
exports.ensureGlobalAnchor = ensureGlobalAnchor;
|
|
49
50
|
exports.ensureAnchor = ensureAnchor;
|
|
@@ -51,6 +52,7 @@ const path = __importStar(require("path"));
|
|
|
51
52
|
const fs = __importStar(require("fs"));
|
|
52
53
|
const crypto = __importStar(require("crypto"));
|
|
53
54
|
const os = __importStar(require("os"));
|
|
55
|
+
const child_process_1 = require("child_process");
|
|
54
56
|
const ANCHOR_DIR = '.claude';
|
|
55
57
|
const ANCHOR_FILE = 'project.json';
|
|
56
58
|
/** Walk up from cwd looking for an existing .claude/project.json.
|
|
@@ -91,6 +93,41 @@ function isEphemeralCwd(cwd) {
|
|
|
91
93
|
return true; // macOS
|
|
92
94
|
return false;
|
|
93
95
|
}
|
|
96
|
+
/** Derive a stable project_id from the git repository's root commit SHA.
|
|
97
|
+
* Returns null when the cwd isn't inside a git repo or the repo has no
|
|
98
|
+
* commits yet (newly `git init`-ed, no first commit).
|
|
99
|
+
*
|
|
100
|
+
* The root commit is *permanently stable* per project — it doesn't change
|
|
101
|
+
* across clones, worktrees, machines, or directory renames, and it survives
|
|
102
|
+
* anchor-file deletion. Disjoint histories (unrelated-histories merge) are
|
|
103
|
+
* handled by sorting all root SHAs alphabetically and concatenating before
|
|
104
|
+
* hashing — same algorithm everywhere produces the same UUID. */
|
|
105
|
+
function computeGitDerivedProjectId(workingDir) {
|
|
106
|
+
try {
|
|
107
|
+
// Multiple root commits possible (history merged from disjoint roots).
|
|
108
|
+
// We sort alphabetically for determinism across machines.
|
|
109
|
+
const out = (0, child_process_1.execSync)('git rev-list --max-parents=0 HEAD', {
|
|
110
|
+
cwd: workingDir,
|
|
111
|
+
encoding: 'utf-8',
|
|
112
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
113
|
+
});
|
|
114
|
+
const roots = out.trim().split(/\s+/).filter(Boolean).sort();
|
|
115
|
+
if (roots.length === 0)
|
|
116
|
+
return null;
|
|
117
|
+
const hash = crypto.createHash('sha256').update(roots.join('\n')).digest('hex');
|
|
118
|
+
// UUID v4-shape, deterministic (matches the deterministicProjectId format).
|
|
119
|
+
return [
|
|
120
|
+
hash.slice(0, 8),
|
|
121
|
+
hash.slice(8, 12),
|
|
122
|
+
'4' + hash.slice(13, 16), // version 4 nibble
|
|
123
|
+
'8' + hash.slice(17, 20), // variant nibble
|
|
124
|
+
hash.slice(20, 32),
|
|
125
|
+
].join('-');
|
|
126
|
+
}
|
|
127
|
+
catch {
|
|
128
|
+
return null;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
94
131
|
/** Hash a working directory into a stable UUID. Used when no anchor file
|
|
95
132
|
* exists yet — keeps turns flowing without forcing init first. */
|
|
96
133
|
function deterministicProjectId(workingDir) {
|
|
@@ -105,78 +142,113 @@ function deterministicProjectId(workingDir) {
|
|
|
105
142
|
hash.slice(20, 32),
|
|
106
143
|
].join('-');
|
|
107
144
|
}
|
|
108
|
-
/**
|
|
109
|
-
*
|
|
110
|
-
|
|
145
|
+
/** Read a `.claude/project.json` and return its parsed contents. Returns null
|
|
146
|
+
* if the file is missing, unreadable, or invalid JSON. Identity fields stay
|
|
147
|
+
* optional; settings always come back with their defaults applied. */
|
|
148
|
+
function tryReadAnchorFileContents(filePath) {
|
|
111
149
|
try {
|
|
112
150
|
const raw = JSON.parse(fs.readFileSync(filePath, 'utf-8'));
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
sessionStartRecap: raw.session_start_recap !== false, // default true
|
|
126
|
-
inboxNotify,
|
|
127
|
-
};
|
|
128
|
-
}
|
|
151
|
+
const notifyRaw = raw.inbox_notify;
|
|
152
|
+
const inboxNotify = notifyRaw === 'off' || notifyRaw === 'session_start_only'
|
|
153
|
+
? notifyRaw
|
|
154
|
+
: 'every_turn';
|
|
155
|
+
return {
|
|
156
|
+
projectId: raw.project_id,
|
|
157
|
+
projectName: raw.project_name,
|
|
158
|
+
created: raw.created,
|
|
159
|
+
memoryCapture: raw.memory_capture !== false,
|
|
160
|
+
sessionStartRecap: raw.session_start_recap !== false,
|
|
161
|
+
inboxNotify,
|
|
162
|
+
};
|
|
129
163
|
}
|
|
130
164
|
catch {
|
|
131
|
-
|
|
165
|
+
return null;
|
|
132
166
|
}
|
|
133
|
-
return null;
|
|
134
167
|
}
|
|
135
168
|
/** Read the project anchor for a given cwd. Read-only.
|
|
136
169
|
*
|
|
137
|
-
*
|
|
138
|
-
* (1)
|
|
139
|
-
*
|
|
140
|
-
*
|
|
141
|
-
*
|
|
142
|
-
*
|
|
170
|
+
* 4-level cascade (v0.7+):
|
|
171
|
+
* (1) `.claude/project.json` with explicit `project_id` → file-based identity
|
|
172
|
+
* (legacy / explicit override). Settings come from the file too.
|
|
173
|
+
* (2) Git repo with at least one commit → derive UUID from the root commit
|
|
174
|
+
* SHA. Stable across clones, worktrees, machines, file deletions. If a
|
|
175
|
+
* settings-only file exists nearby, layer its settings on top.
|
|
176
|
+
* (3) Ephemeral cwd (Cowork session paths, /tmp/, etc.) + ~/.claude/project.json
|
|
177
|
+
* exists → use the global anchor.
|
|
178
|
+
* (4) Deterministic path-hash fallback. The only source that returns
|
|
179
|
+
* `initialized: false` — the hook emits a setup warning so the user
|
|
180
|
+
* knows to `git init` or `greprag init`.
|
|
143
181
|
*
|
|
144
|
-
*
|
|
145
|
-
*
|
|
146
|
-
*
|
|
182
|
+
* Settings (memoryCapture, sessionStartRecap, inboxNotify) come from a
|
|
183
|
+
* repo-level `.claude/project.json` when one exists, regardless of which
|
|
184
|
+
* identity level resolved. A file with no `project_id` carries settings
|
|
185
|
+
* only — identity flows through git derivation.
|
|
147
186
|
*
|
|
148
187
|
* // adr: adr/project-anchor.md
|
|
149
188
|
*/
|
|
150
189
|
function readAnchor(cwd) {
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
if (
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
190
|
+
const existingPath = findExistingAnchor(cwd);
|
|
191
|
+
const fileContents = existingPath ? tryReadAnchorFileContents(existingPath) : null;
|
|
192
|
+
// 1. File with explicit project_id → legacy/override identity.
|
|
193
|
+
if (existingPath && fileContents && fileContents.projectId && fileContents.projectName) {
|
|
194
|
+
return {
|
|
195
|
+
projectId: fileContents.projectId,
|
|
196
|
+
projectName: fileContents.projectName,
|
|
197
|
+
initialized: true,
|
|
198
|
+
source: 'file',
|
|
199
|
+
anchorPath: existingPath,
|
|
200
|
+
created: fileContents.created,
|
|
201
|
+
memoryCapture: fileContents.memoryCapture,
|
|
202
|
+
sessionStartRecap: fileContents.sessionStartRecap,
|
|
203
|
+
inboxNotify: fileContents.inboxNotify,
|
|
204
|
+
};
|
|
158
205
|
}
|
|
159
|
-
// 2.
|
|
160
|
-
|
|
161
|
-
|
|
206
|
+
// 2. Git-derived identity — stable per-project, no file needed.
|
|
207
|
+
const gitId = computeGitDerivedProjectId(cwd);
|
|
208
|
+
if (gitId) {
|
|
209
|
+
const root = path.resolve(cwd);
|
|
210
|
+
return {
|
|
211
|
+
projectId: gitId,
|
|
212
|
+
projectName: fileContents?.projectName || path.basename(root).toLowerCase(),
|
|
213
|
+
initialized: true,
|
|
214
|
+
source: 'git',
|
|
215
|
+
anchorPath: existingPath || path.join(root, ANCHOR_DIR, ANCHOR_FILE),
|
|
216
|
+
created: fileContents?.created,
|
|
217
|
+
memoryCapture: fileContents?.memoryCapture ?? true,
|
|
218
|
+
sessionStartRecap: fileContents?.sessionStartRecap ?? true,
|
|
219
|
+
inboxNotify: fileContents?.inboxNotify ?? 'every_turn',
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
// 3. Ephemeral cwd + global anchor — Cowork and similar agent runners.
|
|
162
223
|
if (isEphemeralCwd(cwd)) {
|
|
163
224
|
const globalAnchorPath = path.join(os.homedir(), ANCHOR_DIR, ANCHOR_FILE);
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
225
|
+
const globalContents = tryReadAnchorFileContents(globalAnchorPath);
|
|
226
|
+
if (globalContents && globalContents.projectId && globalContents.projectName) {
|
|
227
|
+
return {
|
|
228
|
+
projectId: globalContents.projectId,
|
|
229
|
+
projectName: globalContents.projectName,
|
|
230
|
+
initialized: true,
|
|
231
|
+
source: 'global',
|
|
232
|
+
anchorPath: globalAnchorPath,
|
|
233
|
+
created: globalContents.created,
|
|
234
|
+
memoryCapture: globalContents.memoryCapture,
|
|
235
|
+
sessionStartRecap: globalContents.sessionStartRecap,
|
|
236
|
+
inboxNotify: globalContents.inboxNotify,
|
|
237
|
+
};
|
|
168
238
|
}
|
|
169
239
|
}
|
|
170
|
-
//
|
|
240
|
+
// 4. Path-hash fallback — only source that flags `initialized: false`.
|
|
171
241
|
const root = path.resolve(cwd);
|
|
172
242
|
return {
|
|
173
243
|
projectId: deterministicProjectId(root),
|
|
174
|
-
projectName: path.basename(root).toLowerCase(),
|
|
244
|
+
projectName: fileContents?.projectName || path.basename(root).toLowerCase(),
|
|
175
245
|
initialized: false,
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
246
|
+
source: 'hash',
|
|
247
|
+
anchorPath: existingPath || path.join(root, ANCHOR_DIR, ANCHOR_FILE),
|
|
248
|
+
created: fileContents?.created,
|
|
249
|
+
memoryCapture: fileContents?.memoryCapture ?? true,
|
|
250
|
+
sessionStartRecap: fileContents?.sessionStartRecap ?? true,
|
|
251
|
+
inboxNotify: fileContents?.inboxNotify ?? 'every_turn',
|
|
180
252
|
};
|
|
181
253
|
}
|
|
182
254
|
/** Create (or return existing) ~/.claude/project.json.
|
|
@@ -184,11 +256,21 @@ function readAnchor(cwd) {
|
|
|
184
256
|
function ensureGlobalAnchor(projectName) {
|
|
185
257
|
const globalAnchorDir = path.join(os.homedir(), ANCHOR_DIR);
|
|
186
258
|
const globalAnchorPath = path.join(globalAnchorDir, ANCHOR_FILE);
|
|
187
|
-
// Return existing if valid.
|
|
188
259
|
if (fs.existsSync(globalAnchorPath)) {
|
|
189
|
-
const
|
|
190
|
-
if (
|
|
191
|
-
return
|
|
260
|
+
const contents = tryReadAnchorFileContents(globalAnchorPath);
|
|
261
|
+
if (contents && contents.projectId && contents.projectName) {
|
|
262
|
+
return {
|
|
263
|
+
projectId: contents.projectId,
|
|
264
|
+
projectName: contents.projectName,
|
|
265
|
+
initialized: true,
|
|
266
|
+
source: 'global',
|
|
267
|
+
anchorPath: globalAnchorPath,
|
|
268
|
+
created: contents.created,
|
|
269
|
+
memoryCapture: contents.memoryCapture,
|
|
270
|
+
sessionStartRecap: contents.sessionStartRecap,
|
|
271
|
+
inboxNotify: contents.inboxNotify,
|
|
272
|
+
};
|
|
273
|
+
}
|
|
192
274
|
}
|
|
193
275
|
if (!fs.existsSync(globalAnchorDir)) {
|
|
194
276
|
fs.mkdirSync(globalAnchorDir, { recursive: true });
|
|
@@ -203,6 +285,7 @@ function ensureGlobalAnchor(projectName) {
|
|
|
203
285
|
projectId: newAnchor.project_id,
|
|
204
286
|
projectName: newAnchor.project_name,
|
|
205
287
|
initialized: true,
|
|
288
|
+
source: 'global',
|
|
206
289
|
anchorPath: globalAnchorPath,
|
|
207
290
|
created: newAnchor.created,
|
|
208
291
|
memoryCapture: true,
|
|
@@ -210,37 +293,75 @@ function ensureGlobalAnchor(projectName) {
|
|
|
210
293
|
inboxNotify: 'every_turn',
|
|
211
294
|
};
|
|
212
295
|
}
|
|
213
|
-
/** Ensure an anchor exists at <cwd>/.claude/project.json.
|
|
214
|
-
* the
|
|
215
|
-
* Called by `greprag init`.
|
|
296
|
+
/** Ensure an anchor exists at <cwd>/.claude/project.json. Behavior depends on
|
|
297
|
+
* whether the cwd is inside a git repo with commits:
|
|
216
298
|
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
299
|
+
* - Git repo with commits: writes settings only (no `project_id` field).
|
|
300
|
+
* Identity flows through git derivation — no random UUID stored, so the
|
|
301
|
+
* file can be deleted without orphaning history. Returns the resolved
|
|
302
|
+
* anchor (source='git').
|
|
303
|
+
*
|
|
304
|
+
* - No git or no commits: writes a random `project_id` to the file (legacy
|
|
305
|
+
* behavior). User gets a stable per-path identity until they `git init`
|
|
306
|
+
* + commit, at which point they can run `greprag doctor` to migrate.
|
|
307
|
+
*
|
|
308
|
+
* Called by `greprag init`. Idempotent — if a repo-level anchor already
|
|
309
|
+
* exists, returns it unchanged.
|
|
310
|
+
*
|
|
311
|
+
* Never treats the global anchor as an existing repo anchor. */
|
|
219
312
|
function ensureAnchor(cwd, projectName) {
|
|
220
313
|
const globalAnchorPath = path.join(os.homedir(), ANCHOR_DIR, ANCHOR_FILE);
|
|
221
314
|
const existing = findExistingAnchor(cwd);
|
|
222
|
-
// Short-circuit only for a real repo anchor, not the global catch-all.
|
|
223
315
|
if (existing && existing !== globalAnchorPath) {
|
|
224
316
|
return readAnchor(cwd);
|
|
225
317
|
}
|
|
226
318
|
const root = path.resolve(cwd);
|
|
227
319
|
const anchorDir = path.join(root, ANCHOR_DIR);
|
|
228
320
|
const anchorPath = path.join(anchorDir, ANCHOR_FILE);
|
|
321
|
+
const resolvedName = (projectName || path.basename(root)).toLowerCase();
|
|
322
|
+
const created = new Date().toISOString();
|
|
229
323
|
if (!fs.existsSync(anchorDir)) {
|
|
230
324
|
fs.mkdirSync(anchorDir, { recursive: true });
|
|
231
325
|
}
|
|
326
|
+
const gitId = computeGitDerivedProjectId(cwd);
|
|
327
|
+
if (gitId) {
|
|
328
|
+
// Git repo with commits — write settings-only file, identity from git.
|
|
329
|
+
const fileBody = {
|
|
330
|
+
project_name: resolvedName,
|
|
331
|
+
created,
|
|
332
|
+
memory_capture: true,
|
|
333
|
+
session_start_recap: true,
|
|
334
|
+
inbox_notify: 'every_turn',
|
|
335
|
+
};
|
|
336
|
+
fs.writeFileSync(anchorPath, JSON.stringify(fileBody, null, 2) + '\n');
|
|
337
|
+
return {
|
|
338
|
+
projectId: gitId,
|
|
339
|
+
projectName: resolvedName,
|
|
340
|
+
initialized: true,
|
|
341
|
+
source: 'git',
|
|
342
|
+
anchorPath,
|
|
343
|
+
created,
|
|
344
|
+
memoryCapture: true,
|
|
345
|
+
sessionStartRecap: true,
|
|
346
|
+
inboxNotify: 'every_turn',
|
|
347
|
+
};
|
|
348
|
+
}
|
|
349
|
+
// No git or no commits — write a random project_id (legacy path). The
|
|
350
|
+
// hook's drift detector will surface a migration prompt once the project
|
|
351
|
+
// does get a first commit (doctor consolidates old random UUID → git UUID).
|
|
232
352
|
const newAnchor = {
|
|
233
353
|
project_id: crypto.randomUUID(),
|
|
234
|
-
project_name:
|
|
235
|
-
created
|
|
354
|
+
project_name: resolvedName,
|
|
355
|
+
created,
|
|
236
356
|
};
|
|
237
357
|
fs.writeFileSync(anchorPath, JSON.stringify(newAnchor, null, 2) + '\n');
|
|
238
358
|
return {
|
|
239
359
|
projectId: newAnchor.project_id,
|
|
240
360
|
projectName: newAnchor.project_name,
|
|
241
361
|
initialized: true,
|
|
362
|
+
source: 'file',
|
|
242
363
|
anchorPath,
|
|
243
|
-
created
|
|
364
|
+
created,
|
|
244
365
|
memoryCapture: true,
|
|
245
366
|
sessionStartRecap: true,
|
|
246
367
|
inboxNotify: 'every_turn',
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"project-anchor.js","sourceRoot":"","sources":["../src/project-anchor.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;GAUG;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
|
1
|
+
{"version":3,"file":"project-anchor.js","sourceRoot":"","sources":["../src/project-anchor.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
package/skill/SKILL.md
CHANGED
|
@@ -182,6 +182,29 @@ Address formats (all valid):
|
|
|
182
182
|
|
|
183
183
|
Auto-read semantics: any message returned by `greprag inbox` (without `--all`) is marked read in the same call. Once read, it stops appearing in notifications. TTL deletes read messages after 14 days. Use `keep` to extend before expiry.
|
|
184
184
|
|
|
185
|
+
## Step 5c — Health & repair (when memory looks wrong)
|
|
186
|
+
|
|
187
|
+
Triggers: user says "memory looks empty", "I'm missing my history", "fix greprag here", "are there orphans", "consolidate my project IDs"; or `greprag status` reports identity drift; or the session-start recap printed a "[GrepRAG memory: identity drift detected ...]" line.
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
greprag doctor
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
What it does:
|
|
194
|
+
1. Reads the current project's identity (project_id + which cascade level resolved it — file / git-derived / global / path-hash)
|
|
195
|
+
2. Computes what the git-derived UUID *would* be (if in a git repo with commits)
|
|
196
|
+
3. Queries the API for sibling project_ids under the same profile name (these are the orphans — usually from old anchor files lost to gitignore or a hash-fallback period before init)
|
|
197
|
+
4. Presents the findings and offers actions:
|
|
198
|
+
- **Migrate to git-derived UUID** (recommended when there's 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
|
|
199
|
+
- **Consolidate orphans into current UUID** — keeps the current identity but pulls orphan rows in
|
|
200
|
+
- **Dry-run** — runs the recommended action through the API with `dry_run: true` so the user sees exactly what would change before committing
|
|
201
|
+
|
|
202
|
+
Flags:
|
|
203
|
+
- `--inspect` — diagnose only, no prompts (good for "tell me what's wrong here")
|
|
204
|
+
- `--yes` / `-y` — non-interactive; picks the recommended action and runs it
|
|
205
|
+
|
|
206
|
+
Repair is fully tenant-scoped via the API — `greprag doctor` only ever touches rows under the caller's tenant, and the merge runs in a transaction so a partial failure rolls back cleanly.
|
|
207
|
+
|
|
185
208
|
## Step 6 — Briefing (when everything is configured)
|
|
186
209
|
|
|
187
210
|
```bash
|