greprag 5.74.4 → 5.74.5
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/delivery-reminder.js +1 -1
- package/dist/commands/doctor.js +6 -5
- package/dist/commands/friction-reminder.js +1 -1
- package/dist/commands/skill.js +12 -3
- package/dist/commands/status.js +6 -5
- package/dist/commands/version-reminder.js +13 -1
- package/dist/hook.js +21 -0
- package/dist/native-skill-mirror.js +54 -16
- package/dist/opencode-plugin.bundle.js +14 -3
- package/dist/skill-staleness.js +2 -1
- package/dist/windows-shims.js +134 -41
- package/package.json +1 -1
- package/skill/mechanic/SKILL.md +1 -1
- package/skill/templates/skill-change.md +1 -0
|
@@ -12,7 +12,7 @@ exports.buildDeliveryAnnounce = buildDeliveryAnnounce;
|
|
|
12
12
|
// adr: adr/delivery-announce-pilot.md
|
|
13
13
|
function coordinationLine(platform) {
|
|
14
14
|
if (platform === 'codex') {
|
|
15
|
-
return 'Codex coordination:
|
|
15
|
+
return 'Codex coordination: call list_threads unfiltered, filter to this repo/worktree, inspect matching tasks, then send_message_to_thread with your branch/commit/status before merge/push/deploy. Use GrepRAG only for cross-harness peers.';
|
|
16
16
|
}
|
|
17
17
|
if (platform === 'claude-code') {
|
|
18
18
|
return 'Claude Code coordination: discover peers with `greprag inbox watchers --json`, send to the exact session, and keep the Monitor armed for replies.';
|
package/dist/commands/doctor.js
CHANGED
|
@@ -271,14 +271,15 @@ async function runShimAudit(opts) {
|
|
|
271
271
|
}
|
|
272
272
|
if (shims.length === 0)
|
|
273
273
|
return;
|
|
274
|
-
const
|
|
274
|
+
const unhealthy = shims.filter(windows_shims_1.shimNeedsRepair);
|
|
275
275
|
console.log('Windows shims:');
|
|
276
276
|
for (const s of shims) {
|
|
277
277
|
const ver = s.version ? `v${s.version}` : '(version unknown)';
|
|
278
|
-
const
|
|
278
|
+
const missing = s.incomplete ? ` missing ${s.missingFiles.join(', ')}` : '';
|
|
279
|
+
const flag = s.stale ? ` ⚠ STALE${missing}` : s.incomplete ? ` ⚠ INCOMPLETE${missing}` : s.unresolvable ? ' ? unresolvable' : '';
|
|
279
280
|
console.log(` ${s.name}${s.active ? '' : ' (shadowed)'}: ${s.shimPath} → ${ver}${flag}`);
|
|
280
281
|
}
|
|
281
|
-
if (
|
|
282
|
+
if (unhealthy.length === 0) {
|
|
282
283
|
console.log('');
|
|
283
284
|
return;
|
|
284
285
|
}
|
|
@@ -290,13 +291,13 @@ async function runShimAudit(opts) {
|
|
|
290
291
|
if (!opts.yes) {
|
|
291
292
|
const fixable = repairs.filter(r => r.forwardTo);
|
|
292
293
|
for (const r of repairs.filter(x => !x.forwardTo)) {
|
|
293
|
-
console.log(` ✗ ${r.shim.shimPath} is
|
|
294
|
+
console.log(` ✗ ${r.shim.shimPath} is unhealthy and no current ${r.shim.name} shim exists on PATH to forward to — run \`npm i -g greprag@latest\` first.`);
|
|
294
295
|
}
|
|
295
296
|
if (fixable.length === 0) {
|
|
296
297
|
console.log('');
|
|
297
298
|
return;
|
|
298
299
|
}
|
|
299
|
-
const answer = (await prompt(` Rewrite ${fixable.length}
|
|
300
|
+
const answer = (await prompt(` Rewrite ${fixable.length} unhealthy shim(s) to forward to the current install? (yes/no): `)).trim().toLowerCase();
|
|
300
301
|
if (answer !== 'yes' && answer !== 'y') {
|
|
301
302
|
console.log(' Skipped.\n');
|
|
302
303
|
return;
|
|
@@ -86,7 +86,7 @@ function buildBootstrapFrictionReminder(env) {
|
|
|
86
86
|
function buildMechanicAnnounce() {
|
|
87
87
|
return [
|
|
88
88
|
'[Fix reflex — friction is fixed at the MOMENT it happens, one chip per unit (grepragOS law; full doctrine: `greprag load os`).]',
|
|
89
|
-
'WHEN friction happens — repeated yourself, fought a tool, got corrected >=2x on the same thing, rediscovered something already known, or hit a setup/toolchain failure not caused by code you just edited — spawn its fix chip NOW, then keep working. One unit = one chip; adjacent friction gets its own spawn. Never queue friction for later.',
|
|
89
|
+
'WHEN friction happens — repeated yourself, fought a tool, got corrected >=2x on the same thing, rediscovered something already known, or hit a setup/toolchain failure not caused by code you just edited — spawn its fix chip NOW, then keep working. Ordinary misunderstanding or misreading user intent is not friction unless it repeats or exposes a durable doctrine/harness/injection/env/code failure. One unit = one chip; adjacent friction gets its own spawn. Never queue friction for later.',
|
|
90
90
|
'TYPE = durable repair surface: harness=hooks/watchers/task dispatch; doctrine=greprag load/skills/AGENTS; injection=recap/Capture/doc-pointer/stateful injection; env=bootstrap/deps/scripts/worktree setup; code=product/source behavior. Use: `greprag fix spawn --type <type> "<one unit>"`.',
|
|
91
91
|
'WORKSPACE ROUTING: `fix spawn` detects usable Git history before dispatch — Git uses an isolated worktree; non-Git, unavailable Git, or no commit uses the project-local task with serialized writes. No fail-then-fallback attempt.',
|
|
92
92
|
'THE CHIP\'S CONTRACT (it self-enforces; you just spawn): identify the exact friction → make the smallest durable root-cause fix → explain and verify it → checkpoint → hand it to the mission delivery owner. With no live parent and a full-goal mission, the chip becomes delivery owner and follows the repo profile. No second lifecycle approval.',
|
package/dist/commands/skill.js
CHANGED
|
@@ -66,7 +66,7 @@ USAGE
|
|
|
66
66
|
greprag skill install <name> Install a bundled advisor skill
|
|
67
67
|
greprag skill uninstall <name> Remove a previously-installed advisor
|
|
68
68
|
greprag skill edit <name> [--skill-id <id>] [--scope project --project-id <id>]
|
|
69
|
-
|
|
69
|
+
Export/edit/apply helper; opens $EDITOR unless --no-open or Codex
|
|
70
70
|
greprag skill mirror sync [harness] Install native GrepRAG skill adapters
|
|
71
71
|
claude-code --cutover-lifecycle Cut over commit/deploy/release only
|
|
72
72
|
greprag skill mirror export <name> --to <dir>
|
|
@@ -94,6 +94,9 @@ NOTES
|
|
|
94
94
|
• Mirrored skill edits use export/apply semantics. \`skill edit\` only wraps
|
|
95
95
|
that flow: export current bytes, open an editor, show a diff summary, then
|
|
96
96
|
apply with the exported content hash.
|
|
97
|
+
• Codex agents must use a non-interactive path: \`skill mirror export\`, patch
|
|
98
|
+
files, then \`skill mirror apply\`. In Codex, \`skill edit\` skips the editor
|
|
99
|
+
and prints the guarded apply command; \`--no-open\` does the same explicitly.
|
|
97
100
|
• Source stamps are local Git facts. They update the local canonical skill
|
|
98
101
|
file, then mirror to GrepRAG when GREPRAG_API_KEY is configured.`;
|
|
99
102
|
function formatAdoptionLine(prefix, entry) {
|
|
@@ -394,6 +397,9 @@ function editorCommand() {
|
|
|
394
397
|
|| process.env.EDITOR
|
|
395
398
|
|| (process.platform === 'win32' ? 'notepad' : 'vi');
|
|
396
399
|
}
|
|
400
|
+
function codexHarnessDetected() {
|
|
401
|
+
return Boolean(process.env.CODEX_THREAD_ID || process.env.CODEX_CI || process.env.CODEX_SHELL);
|
|
402
|
+
}
|
|
397
403
|
function runEditor(target) {
|
|
398
404
|
const editor = editorCommand();
|
|
399
405
|
const result = (0, proc_1.safeSpawnSync)(editor, [target], { stdio: 'inherit', shell: true });
|
|
@@ -453,8 +459,11 @@ async function runSkillEdit(args) {
|
|
|
453
459
|
});
|
|
454
460
|
console.log(`Editing ${skill} revision ${loaded.revision} (${loaded.contentHash.slice(0, 12)})`);
|
|
455
461
|
console.log(`Work dir: ${workDir}`);
|
|
456
|
-
|
|
457
|
-
|
|
462
|
+
const skipEditor = noOpen || codexHarnessDetected();
|
|
463
|
+
if (skipEditor) {
|
|
464
|
+
console.log(noOpen
|
|
465
|
+
? 'Editor skipped.'
|
|
466
|
+
: 'Codex harness detected; editor skipped to avoid launching an interactive editor.');
|
|
458
467
|
const forward = forwardedMirrorArgs(args);
|
|
459
468
|
console.log(`Apply after editing: greprag skill mirror apply ${shellArg(workDir)} --skill ${skill}${forward ? ` ${forward}` : ''} --expected-hash ${loaded.contentHash}`);
|
|
460
469
|
return;
|
package/dist/commands/status.js
CHANGED
|
@@ -264,11 +264,11 @@ function buildWindowsShims(installed) {
|
|
|
264
264
|
shims = (0, windows_shims_1.auditWindowsShims)(installed);
|
|
265
265
|
}
|
|
266
266
|
catch { /* best-effort */ }
|
|
267
|
-
const mismatch = shims.some(
|
|
267
|
+
const mismatch = shims.some(windows_shims_1.shimNeedsRepair);
|
|
268
268
|
return {
|
|
269
269
|
shims,
|
|
270
270
|
mismatch,
|
|
271
|
-
repair: mismatch ? 'greprag doctor (rewrites stale shims to forward to the current install)' : null,
|
|
271
|
+
repair: mismatch ? 'greprag doctor (rewrites stale/incomplete PowerShell/CMD shims to forward to the current install)' : null,
|
|
272
272
|
};
|
|
273
273
|
}
|
|
274
274
|
function renderPlatform(name, p) {
|
|
@@ -324,14 +324,15 @@ function renderHuman(s, docMirror) {
|
|
|
324
324
|
function renderShims(w) {
|
|
325
325
|
if (!w || w.shims.length === 0)
|
|
326
326
|
return [];
|
|
327
|
-
const lines = ['', 'Windows shims (what each
|
|
327
|
+
const lines = ['', 'Windows shims (what each PowerShell/CMD shim on PATH actually executes):'];
|
|
328
328
|
for (const s of w.shims) {
|
|
329
329
|
const ver = s.version ? `v${s.version}` : '(version unknown)';
|
|
330
|
-
const
|
|
330
|
+
const missing = s.incomplete ? ` missing ${s.missingFiles.join(', ')}` : '';
|
|
331
|
+
const flag = s.stale ? ` ⚠ STALE${missing}` : s.incomplete ? ` ⚠ INCOMPLETE${missing}` : s.unresolvable ? ' ? unresolvable' : '';
|
|
331
332
|
lines.push(` ${s.name}${s.active ? '' : ' (shadowed)'}: ${s.shimPath} → ${ver}${flag}`);
|
|
332
333
|
}
|
|
333
334
|
if (w.mismatch) {
|
|
334
|
-
lines.push(` ⚠ A shim executes a
|
|
335
|
+
lines.push(` ⚠ A shim executes a different or incomplete GrepRAG install — hooks and shell commands may silently run old code.`);
|
|
335
336
|
lines.push(` Repair: ${w.repair}`);
|
|
336
337
|
}
|
|
337
338
|
return lines;
|
|
@@ -20,12 +20,22 @@
|
|
|
20
20
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
21
21
|
exports.versionUpgradeModule = void 0;
|
|
22
22
|
exports.buildVersionAnnounce = buildVersionAnnounce;
|
|
23
|
+
exports.buildActiveCommandDriftAnnounce = buildActiveCommandDriftAnnounce;
|
|
23
24
|
exports.buildLoadoutUpdateAnnounce = buildLoadoutUpdateAnnounce;
|
|
24
25
|
/** The upgrade announce — null unless the hook detected a newer published version. */
|
|
25
26
|
function buildVersionAnnounce(current, latest) {
|
|
26
27
|
return [
|
|
27
28
|
`[greprag — a new release is out: v${latest} (you are on v${current}).]`,
|
|
28
|
-
`Upgrade: \`npm i -g greprag@latest\`. New behavior + fixes land
|
|
29
|
+
`Upgrade: \`npm i -g greprag@latest\`, then \`greprag status\`. New behavior + fixes land once the active shell shim resolves to the new install.`,
|
|
30
|
+
].join('\n');
|
|
31
|
+
}
|
|
32
|
+
function buildActiveCommandDriftAnnounce(drift) {
|
|
33
|
+
const active = drift.activeVersion ? `v${drift.activeVersion}` : 'an unknown version';
|
|
34
|
+
const missing = drift.missingFiles.length ? ` and is missing ${drift.missingFiles.join(', ')}` : '';
|
|
35
|
+
return [
|
|
36
|
+
`[greprag — active command drift detected.]`,
|
|
37
|
+
`The startup hook is running greprag v${drift.current}, but \`greprag\` on PATH resolves to ${active} at ${drift.shimPath}${missing}.`,
|
|
38
|
+
`Run \`greprag doctor --inspect\`; if it reports stale shims, run \`greprag doctor --yes\`. If no current shim exists, run \`npm i -g greprag@latest\`.`,
|
|
29
39
|
].join('\n');
|
|
30
40
|
}
|
|
31
41
|
/** The CONTEXTUALIZED upgrade announce: a pending loadout is waiting but needs a
|
|
@@ -47,6 +57,8 @@ exports.versionUpgradeModule = {
|
|
|
47
57
|
const lo = env.loadoutUpdateRequired;
|
|
48
58
|
if (lo && lo.needed && lo.current)
|
|
49
59
|
return buildLoadoutUpdateAnnounce(lo.from, lo.needed, lo.current);
|
|
60
|
+
if (env.activeCommandDrift)
|
|
61
|
+
return buildActiveCommandDriftAnnounce(env.activeCommandDrift);
|
|
50
62
|
const u = env.updateAvailable;
|
|
51
63
|
if (!u || !u.current || !u.latest)
|
|
52
64
|
return null;
|
package/dist/hook.js
CHANGED
|
@@ -138,6 +138,7 @@ const native_skill_pending_1 = require("./native-skill-pending");
|
|
|
138
138
|
const doc_mirror_client_1 = require("./doc-mirror-client");
|
|
139
139
|
const equipped_loadouts_cache_1 = require("./equipped-loadouts-cache");
|
|
140
140
|
const codex_chip_hooks_1 = require("./codex-chip-hooks");
|
|
141
|
+
const windows_shims_1 = require("./windows-shims");
|
|
141
142
|
const MAX_FIELD_CHARS = 500_000; // safety cap per text field
|
|
142
143
|
function extractText(content) {
|
|
143
144
|
if (typeof content === 'string')
|
|
@@ -1399,6 +1400,25 @@ async function checkForUpdate() {
|
|
|
1399
1400
|
return { current, latest };
|
|
1400
1401
|
return null;
|
|
1401
1402
|
}
|
|
1403
|
+
function activeCommandDrift() {
|
|
1404
|
+
const current = installedVersion();
|
|
1405
|
+
if (!current || process.platform !== 'win32')
|
|
1406
|
+
return null;
|
|
1407
|
+
try {
|
|
1408
|
+
const active = (0, windows_shims_1.auditWindowsShims)(current).find(s => s.name === 'greprag' && s.active);
|
|
1409
|
+
if (!active || !(0, windows_shims_1.shimNeedsRepair)(active))
|
|
1410
|
+
return null;
|
|
1411
|
+
return {
|
|
1412
|
+
current,
|
|
1413
|
+
activeVersion: active.version,
|
|
1414
|
+
shimPath: active.shimPath,
|
|
1415
|
+
missingFiles: active.missingFiles,
|
|
1416
|
+
};
|
|
1417
|
+
}
|
|
1418
|
+
catch {
|
|
1419
|
+
return null;
|
|
1420
|
+
}
|
|
1421
|
+
}
|
|
1402
1422
|
/** SessionStart — fetch recent episodic activity for this project and print
|
|
1403
1423
|
* to stdout. Claude Code injects the printed text as session context. Codex
|
|
1404
1424
|
* uses the same content via `hookSpecificOutput.additionalContext`. Fires
|
|
@@ -1662,6 +1682,7 @@ async function recap(input, mode = 'plain', opts = {}) {
|
|
|
1662
1682
|
procedureAnnounces,
|
|
1663
1683
|
equippedLoadouts,
|
|
1664
1684
|
updateAvailable,
|
|
1685
|
+
activeCommandDrift: activeCommandDrift(),
|
|
1665
1686
|
loadoutUpdateRequired,
|
|
1666
1687
|
};
|
|
1667
1688
|
let announceReg = (0, reminder_injector_1.mechanicKilled)() ? reminder_registry_1.REGISTRY.filter(m => m.id !== 'mechanic-friction') : reminder_registry_1.REGISTRY;
|
|
@@ -310,6 +310,47 @@ function nativeCreationLocations(platform) {
|
|
|
310
310
|
}
|
|
311
311
|
return '`~/.claude/skills/<new-name>/SKILL.md` (global) or `<repo>/.claude/skills/<new-name>/SKILL.md` (project)';
|
|
312
312
|
}
|
|
313
|
+
function renderChangeSkillLine(params) {
|
|
314
|
+
const exportCommand = `greprag skill mirror export ${params.skillName} ${params.identityArg} --to <dir>`;
|
|
315
|
+
const applyCommand = `greprag skill mirror apply <dir> --skill ${params.skillName} ${params.editArgs} --expected-hash <exported-hash>`;
|
|
316
|
+
if (params.platform === 'codex') {
|
|
317
|
+
return `- **Change this skill:** run \`greprag load skill-change\`, then \`${exportCommand}\`; patch the exported Markdown files non-interactively; run \`${applyCommand}\`; then \`${params.syncCommand}\`; then verify with \`${params.loadCommand}\`.`;
|
|
318
|
+
}
|
|
319
|
+
return `- **Change this skill:** run \`greprag load skill-change\`, then \`greprag skill edit ${params.skillName} ${params.editArgs}\`, then \`${params.syncCommand}\`, then verify with \`${params.loadCommand}\`.`;
|
|
320
|
+
}
|
|
321
|
+
function renderEditingSection(platform, params) {
|
|
322
|
+
const exportCommand = `greprag skill mirror export ${params.skillName} ${params.identityArg} --to <dir>`;
|
|
323
|
+
const applyCommand = `greprag skill mirror apply <dir> --skill ${params.skillName} ${params.editArgs} --expected-hash <exported-hash>`;
|
|
324
|
+
if (platform === 'codex') {
|
|
325
|
+
return `## Editing
|
|
326
|
+
|
|
327
|
+
Codex agents must use the non-interactive mirror path:
|
|
328
|
+
|
|
329
|
+
\`\`\`bash
|
|
330
|
+
${exportCommand}
|
|
331
|
+
# patch the exported Markdown files with normal file edits
|
|
332
|
+
${applyCommand}
|
|
333
|
+
\`\`\`
|
|
334
|
+
|
|
335
|
+
Do not launch an interactive editor from Codex. If using \`greprag skill edit\` as an export helper, include \`--no-open\` and run the printed apply command after patching.
|
|
336
|
+
`;
|
|
337
|
+
}
|
|
338
|
+
return `## Editing
|
|
339
|
+
|
|
340
|
+
To edit this skill, run the guarded editor flow:
|
|
341
|
+
|
|
342
|
+
\`\`\`bash
|
|
343
|
+
greprag skill edit ${params.skillName} ${params.editArgs}
|
|
344
|
+
\`\`\`
|
|
345
|
+
|
|
346
|
+
Manual path:
|
|
347
|
+
|
|
348
|
+
\`\`\`bash
|
|
349
|
+
${exportCommand}
|
|
350
|
+
${applyCommand}
|
|
351
|
+
\`\`\`
|
|
352
|
+
`;
|
|
353
|
+
}
|
|
313
354
|
function renderAdapterBody(platform, skill) {
|
|
314
355
|
const skillName = skill.skillName;
|
|
315
356
|
const skillId = canonicalSkillId(skill);
|
|
@@ -324,6 +365,16 @@ function renderAdapterBody(platform, skill) {
|
|
|
324
365
|
: `- **Create another skill:** create a normal native skill at ${creationLocations}; run \`${syncCommand} --dry-run\` and confirm it says \`adopt\`; then run \`${syncCommand}\`. GrepRAG adopts it as canon and replaces the native package with an adapter.`;
|
|
325
366
|
const companions = skill.files.map(file => file.path).filter(relative => relative !== 'SKILL.md').sort();
|
|
326
367
|
const harnessArg = `--harness ${platform}`;
|
|
368
|
+
const loadCommand = `greprag load ${skillName} ${identityArg} ${harnessArg}`;
|
|
369
|
+
const changeSkillLine = renderChangeSkillLine({
|
|
370
|
+
platform,
|
|
371
|
+
skillName,
|
|
372
|
+
editArgs,
|
|
373
|
+
identityArg,
|
|
374
|
+
syncCommand,
|
|
375
|
+
loadCommand,
|
|
376
|
+
});
|
|
377
|
+
const editingSection = renderEditingSection(platform, { skillName, editArgs, identityArg });
|
|
327
378
|
const companionLines = companions.length
|
|
328
379
|
? companions.map(relative => `- \`greprag load ${skillName} ${relative} ${identityArg} ${harnessArg}\``).join('\n')
|
|
329
380
|
: '- No companion docs are mirrored into this native adapter.';
|
|
@@ -337,8 +388,8 @@ Do not add or maintain project-state/doc pointers here. Current project state be
|
|
|
337
388
|
|
|
338
389
|
Required action chains:
|
|
339
390
|
|
|
340
|
-
- **Use this skill:** run
|
|
341
|
-
|
|
391
|
+
- **Use this skill:** run \`${loadCommand}\` before acting.
|
|
392
|
+
${changeSkillLine}
|
|
342
393
|
${creationLine}
|
|
343
394
|
|
|
344
395
|
Authority: ${skillId}
|
|
@@ -346,20 +397,7 @@ Source binding: ${sourceBindingState(skill)}
|
|
|
346
397
|
Canonical content hash: ${canonicalContentHash(skill)}
|
|
347
398
|
${typeof skill.revision === 'number' ? `Mirror revision: ${skill.revision}` : 'Mirror revision: unknown'}
|
|
348
399
|
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
To edit this skill, run the guarded editor flow:
|
|
352
|
-
|
|
353
|
-
\`\`\`bash
|
|
354
|
-
greprag skill edit ${skillName} ${editArgs}
|
|
355
|
-
\`\`\`
|
|
356
|
-
|
|
357
|
-
Manual path:
|
|
358
|
-
|
|
359
|
-
\`\`\`bash
|
|
360
|
-
greprag skill mirror export ${skillName} ${identityArg} --to <dir>
|
|
361
|
-
greprag skill mirror apply <dir> --skill ${skillName} ${editArgs} --expected-hash <exported-hash>
|
|
362
|
-
\`\`\`
|
|
400
|
+
${editingSection}
|
|
363
401
|
|
|
364
402
|
Snapshot rollback:
|
|
365
403
|
|
|
@@ -2046,7 +2046,7 @@ function buildBootstrapFrictionReminder(env) {
|
|
|
2046
2046
|
function buildMechanicAnnounce() {
|
|
2047
2047
|
return [
|
|
2048
2048
|
"[Fix reflex \u2014 friction is fixed at the MOMENT it happens, one chip per unit (grepragOS law; full doctrine: `greprag load os`).]",
|
|
2049
|
-
"WHEN friction happens \u2014 repeated yourself, fought a tool, got corrected >=2x on the same thing, rediscovered something already known, or hit a setup/toolchain failure not caused by code you just edited \u2014 spawn its fix chip NOW, then keep working. One unit = one chip; adjacent friction gets its own spawn. Never queue friction for later.",
|
|
2049
|
+
"WHEN friction happens \u2014 repeated yourself, fought a tool, got corrected >=2x on the same thing, rediscovered something already known, or hit a setup/toolchain failure not caused by code you just edited \u2014 spawn its fix chip NOW, then keep working. Ordinary misunderstanding or misreading user intent is not friction unless it repeats or exposes a durable doctrine/harness/injection/env/code failure. One unit = one chip; adjacent friction gets its own spawn. Never queue friction for later.",
|
|
2050
2050
|
'TYPE = durable repair surface: harness=hooks/watchers/task dispatch; doctrine=greprag load/skills/AGENTS; injection=recap/Capture/doc-pointer/stateful injection; env=bootstrap/deps/scripts/worktree setup; code=product/source behavior. Use: `greprag fix spawn --type <type> "<one unit>"`.',
|
|
2051
2051
|
"WORKSPACE ROUTING: `fix spawn` detects usable Git history before dispatch \u2014 Git uses an isolated worktree; non-Git, unavailable Git, or no commit uses the project-local task with serialized writes. No fail-then-fallback attempt.",
|
|
2052
2052
|
"THE CHIP'S CONTRACT (it self-enforces; you just spawn): identify the exact friction \u2192 make the smallest durable root-cause fix \u2192 explain and verify it \u2192 checkpoint \u2192 hand it to the mission delivery owner. With no live parent and a full-goal mission, the chip becomes delivery owner and follows the repo profile. No second lifecycle approval.",
|
|
@@ -2230,7 +2230,16 @@ var collisionMatchModule = {
|
|
|
2230
2230
|
function buildVersionAnnounce(current, latest) {
|
|
2231
2231
|
return [
|
|
2232
2232
|
`[greprag \u2014 a new release is out: v${latest} (you are on v${current}).]`,
|
|
2233
|
-
`Upgrade: \`npm i -g greprag@latest\`. New behavior + fixes land
|
|
2233
|
+
`Upgrade: \`npm i -g greprag@latest\`, then \`greprag status\`. New behavior + fixes land once the active shell shim resolves to the new install.`
|
|
2234
|
+
].join("\n");
|
|
2235
|
+
}
|
|
2236
|
+
function buildActiveCommandDriftAnnounce(drift) {
|
|
2237
|
+
const active = drift.activeVersion ? `v${drift.activeVersion}` : "an unknown version";
|
|
2238
|
+
const missing = drift.missingFiles.length ? ` and is missing ${drift.missingFiles.join(", ")}` : "";
|
|
2239
|
+
return [
|
|
2240
|
+
`[greprag \u2014 active command drift detected.]`,
|
|
2241
|
+
`The startup hook is running greprag v${drift.current}, but \`greprag\` on PATH resolves to ${active} at ${drift.shimPath}${missing}.`,
|
|
2242
|
+
`Run \`greprag doctor --inspect\`; if it reports stale shims, run \`greprag doctor --yes\`. If no current shim exists, run \`npm i -g greprag@latest\`.`
|
|
2234
2243
|
].join("\n");
|
|
2235
2244
|
}
|
|
2236
2245
|
function buildLoadoutUpdateAnnounce(from, needed, current) {
|
|
@@ -2248,6 +2257,8 @@ var versionUpgradeModule = {
|
|
|
2248
2257
|
const lo = env.loadoutUpdateRequired;
|
|
2249
2258
|
if (lo && lo.needed && lo.current)
|
|
2250
2259
|
return buildLoadoutUpdateAnnounce(lo.from, lo.needed, lo.current);
|
|
2260
|
+
if (env.activeCommandDrift)
|
|
2261
|
+
return buildActiveCommandDriftAnnounce(env.activeCommandDrift);
|
|
2251
2262
|
const u = env.updateAvailable;
|
|
2252
2263
|
if (!u || !u.current || !u.latest)
|
|
2253
2264
|
return null;
|
|
@@ -2407,7 +2418,7 @@ var loadoutRegistrarModule = {
|
|
|
2407
2418
|
// src/commands/delivery-reminder.ts
|
|
2408
2419
|
function coordinationLine(platform) {
|
|
2409
2420
|
if (platform === "codex") {
|
|
2410
|
-
return "Codex coordination:
|
|
2421
|
+
return "Codex coordination: call list_threads unfiltered, filter to this repo/worktree, inspect matching tasks, then send_message_to_thread with your branch/commit/status before merge/push/deploy. Use GrepRAG only for cross-harness peers.";
|
|
2411
2422
|
}
|
|
2412
2423
|
if (platform === "claude-code") {
|
|
2413
2424
|
return "Claude Code coordination: discover peers with `greprag inbox watchers --json`, send to the exact session, and keep the Monitor armed for replies.";
|
package/dist/skill-staleness.js
CHANGED
|
@@ -374,7 +374,8 @@ function formatSkillNoSourceBinding(skillName) {
|
|
|
374
374
|
`/${skillName} has no source bindings.`,
|
|
375
375
|
'Authority: canonical mirrored payload exists in GrepRAG; local Codex/Claude/OpenCode SKILL.md files are generated adapter-only mirrors.',
|
|
376
376
|
'Do not edit a local native adapter as skill content.',
|
|
377
|
-
`
|
|
377
|
+
`Agent-safe edit path: \`greprag skill mirror export ${skillName} --to <dir>\`, patch the exported Markdown files non-interactively, then \`greprag skill mirror apply <dir> --skill ${skillName} --expected-hash <exported-hash>\`.`,
|
|
378
|
+
`Human editor shortcut: \`greprag skill edit ${skillName}\`; Codex auto-skips the editor, and agents may pass \`--no-open\` explicitly.`,
|
|
378
379
|
`After binding a real source repo, stamp it with \`greprag skill source stamp ${skillName} --project <p> --path <rel>\`.`,
|
|
379
380
|
].join('\n') + '\n';
|
|
380
381
|
}
|
package/dist/windows-shims.js
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/** Windows shim audit — detect (and let doctor repair) stale
|
|
3
|
-
* `greprag
|
|
4
|
-
* current CLI.
|
|
2
|
+
/** Windows shim audit — detect (and let doctor repair) stale PowerShell/CMD
|
|
3
|
+
* `greprag*` shims that resolve to a DIFFERENT install than the current CLI.
|
|
5
4
|
*
|
|
6
5
|
* WHY: `npm i -g greprag@latest` upgrades the npm-global shim, but any
|
|
7
6
|
* hand-rolled `~/bin` shim (or an old bundled-folder copy referenced by a
|
|
@@ -52,38 +51,44 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
52
51
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
53
52
|
exports.parseShimTarget = parseShimTarget;
|
|
54
53
|
exports.shimPackageVersion = shimPackageVersion;
|
|
54
|
+
exports.missingRequiredShimFiles = missingRequiredShimFiles;
|
|
55
55
|
exports.auditWindowsShims = auditWindowsShims;
|
|
56
|
+
exports.shimNeedsRepair = shimNeedsRepair;
|
|
56
57
|
exports.planShimRepairs = planShimRepairs;
|
|
57
58
|
exports.repairStaleShims = repairStaleShims;
|
|
58
59
|
const fs = __importStar(require("fs"));
|
|
59
60
|
const path = __importStar(require("path"));
|
|
60
61
|
const child_process_1 = require("child_process");
|
|
61
62
|
/** Extract the script a .cmd shim executes. Handles the npm shim shape
|
|
62
|
-
* (`"%dp0%\node_modules\greprag\dist\hook.js"`),
|
|
63
|
-
*
|
|
63
|
+
* (`"%dp0%\node_modules\greprag\dist\hook.js"`), the npm PowerShell shim
|
|
64
|
+
* shape (`"$basedir/node_modules/greprag/dist/index.js"`), absolute .js
|
|
65
|
+
* paths, and one-line shim forwarders. Pure. */
|
|
64
66
|
function parseShimTarget(cmdText, cmdDir) {
|
|
65
|
-
// Strip
|
|
66
|
-
const text = cmdText.split(/\r?\n/).filter(l => !/^\s*(REM
|
|
67
|
-
// Quoted or bare tokens ending .js
|
|
68
|
-
const tokens = [...text.matchAll(/"([^"]+\.(?:js|cmd))"|(\S+\.(?:js|cmd))\b/gi)]
|
|
67
|
+
// Strip comments — a forwarder's provenance comment may name paths.
|
|
68
|
+
const text = cmdText.split(/\r?\n/).filter(l => !/^\s*(REM|::|#)\s/i.test(l)).join('\n');
|
|
69
|
+
// Quoted or bare tokens ending .js/.cmd/.ps1 that mention greprag.
|
|
70
|
+
const tokens = [...text.matchAll(/"([^"]+\.(?:js|cmd|ps1))"|(\S+\.(?:js|cmd|ps1))\b/gi)]
|
|
69
71
|
.map(m => (m[1] || m[2] || '').trim())
|
|
70
72
|
.filter(t => /greprag/i.test(t));
|
|
71
73
|
if (tokens.length === 0)
|
|
72
74
|
return null;
|
|
73
|
-
// Prefer a .js entrypoint over a forwarded
|
|
75
|
+
// Prefer a .js entrypoint over a forwarded shim; first match otherwise.
|
|
74
76
|
const pick = tokens.find(t => /\.js$/i.test(t)) || tokens[0];
|
|
75
|
-
const expanded = pick
|
|
77
|
+
const expanded = pick
|
|
78
|
+
.replace(/%~?dp0%?\\?/gi, cmdDir + path.sep)
|
|
79
|
+
.replace(/\$\{?basedir\}?/gi, cmdDir);
|
|
76
80
|
return path.normalize(expanded);
|
|
77
81
|
}
|
|
78
|
-
/**
|
|
79
|
-
*
|
|
80
|
-
function
|
|
82
|
+
/** GrepRAG package that owns `scriptPath` — nearest package.json named
|
|
83
|
+
* `greprag` walking up (max 6 levels). Pure I/O read. */
|
|
84
|
+
function shimPackageInfo(scriptPath) {
|
|
81
85
|
let dir = path.dirname(scriptPath);
|
|
82
86
|
for (let i = 0; i < 6; i++) {
|
|
83
87
|
try {
|
|
84
88
|
const pkg = JSON.parse(fs.readFileSync(path.join(dir, 'package.json'), 'utf-8'));
|
|
85
|
-
if (pkg?.name === 'greprag' && typeof pkg.version === 'string')
|
|
86
|
-
return pkg.version;
|
|
89
|
+
if (pkg?.name === 'greprag' && typeof pkg.version === 'string') {
|
|
90
|
+
return { root: dir, version: pkg.version };
|
|
91
|
+
}
|
|
87
92
|
}
|
|
88
93
|
catch { /* keep walking */ }
|
|
89
94
|
const up = path.dirname(dir);
|
|
@@ -93,6 +98,19 @@ function shimPackageVersion(scriptPath) {
|
|
|
93
98
|
}
|
|
94
99
|
return null;
|
|
95
100
|
}
|
|
101
|
+
/** Version of the greprag package that owns `scriptPath`. Pure I/O read. */
|
|
102
|
+
function shimPackageVersion(scriptPath) {
|
|
103
|
+
return shimPackageInfo(scriptPath)?.version ?? null;
|
|
104
|
+
}
|
|
105
|
+
function requiredFilesForShim(name) {
|
|
106
|
+
return name === 'greprag' ? ['dist/commands/delivery.js'] : [];
|
|
107
|
+
}
|
|
108
|
+
function missingRequiredShimFiles(scriptPath, name) {
|
|
109
|
+
const info = shimPackageInfo(scriptPath);
|
|
110
|
+
if (!info)
|
|
111
|
+
return [];
|
|
112
|
+
return requiredFilesForShim(name).filter(file => !fs.existsSync(path.join(info.root, file)));
|
|
113
|
+
}
|
|
96
114
|
/** Resolve one shim: follow up to 3 .cmd forwarding hops, then read the
|
|
97
115
|
* owning package version. */
|
|
98
116
|
function resolveShim(name, shimPath, active, installed) {
|
|
@@ -110,59 +128,134 @@ function resolveShim(name, shimPath, active, installed) {
|
|
|
110
128
|
if (!t)
|
|
111
129
|
break;
|
|
112
130
|
target = t;
|
|
113
|
-
if (!/\.cmd$/i.test(t))
|
|
131
|
+
if (!/\.(cmd|ps1)$/i.test(t))
|
|
114
132
|
break; // reached a .js entrypoint
|
|
115
|
-
current = t; // forwarded to another
|
|
133
|
+
current = t; // forwarded to another shim — follow
|
|
116
134
|
}
|
|
117
|
-
const
|
|
135
|
+
const info = target ? shimPackageInfo(target) : null;
|
|
136
|
+
const missingFiles = info ? requiredFilesForShim(name).filter(file => !fs.existsSync(path.join(info.root, file))) : [];
|
|
118
137
|
return {
|
|
119
|
-
name,
|
|
120
|
-
|
|
121
|
-
|
|
138
|
+
name,
|
|
139
|
+
extension: path.extname(shimPath).toLowerCase() === '.ps1' ? '.ps1' : '.cmd',
|
|
140
|
+
shimPath,
|
|
141
|
+
active,
|
|
142
|
+
target,
|
|
143
|
+
version: info?.version ?? null,
|
|
144
|
+
stale: info !== null && info.version !== installed,
|
|
145
|
+
incomplete: missingFiles.length > 0,
|
|
146
|
+
missingFiles,
|
|
147
|
+
unresolvable: target === null || info === null,
|
|
122
148
|
};
|
|
123
149
|
}
|
|
124
|
-
|
|
125
|
-
|
|
150
|
+
const SHIM_NAMES = ['greprag', 'greprag-hook', 'greprag-codex-hook'];
|
|
151
|
+
const SHIM_EXTENSIONS = ['.ps1', '.cmd'];
|
|
152
|
+
function pathDirs() {
|
|
153
|
+
const seen = new Set();
|
|
154
|
+
const out = [];
|
|
155
|
+
for (const raw of (process.env.PATH || '').split(path.delimiter)) {
|
|
156
|
+
const dir = raw.trim().replace(/^"|"$/g, '');
|
|
157
|
+
if (!dir)
|
|
158
|
+
continue;
|
|
159
|
+
const key = dir.toLowerCase();
|
|
160
|
+
if (seen.has(key))
|
|
161
|
+
continue;
|
|
162
|
+
seen.add(key);
|
|
163
|
+
out.push(dir);
|
|
164
|
+
}
|
|
165
|
+
return out;
|
|
166
|
+
}
|
|
167
|
+
function shimHits(name) {
|
|
168
|
+
const out = [];
|
|
169
|
+
for (const dir of pathDirs()) {
|
|
170
|
+
for (const ext of SHIM_EXTENSIONS) {
|
|
171
|
+
const candidate = path.join(dir, `${name}${ext}`);
|
|
172
|
+
try {
|
|
173
|
+
if (fs.existsSync(candidate))
|
|
174
|
+
out.push(candidate);
|
|
175
|
+
}
|
|
176
|
+
catch { /* ignore unreadable PATH entries */ }
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
return out;
|
|
180
|
+
}
|
|
181
|
+
/** Every `greprag*` PowerShell/CMD shim on PATH, in PowerShell resolution order,
|
|
182
|
+
* with the version each actually executes. Empty off-Windows. */
|
|
126
183
|
function auditWindowsShims(installedVersion) {
|
|
127
184
|
if (process.platform !== 'win32')
|
|
128
185
|
return [];
|
|
129
186
|
const out = [];
|
|
130
|
-
for (const name of
|
|
131
|
-
let hits =
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
187
|
+
for (const name of SHIM_NAMES) {
|
|
188
|
+
let hits = shimHits(name);
|
|
189
|
+
// Fallback for unusual PATHEXT/path behavior; keeps the old where.exe path.
|
|
190
|
+
if (hits.length === 0) {
|
|
191
|
+
try {
|
|
192
|
+
hits = (0, child_process_1.execFileSync)('where.exe', [`${name}.cmd`], { encoding: 'utf-8' })
|
|
193
|
+
.split(/\r?\n/).map(s => s.trim()).filter(Boolean);
|
|
194
|
+
}
|
|
195
|
+
catch { /* not found on PATH at all — nothing to audit */ }
|
|
135
196
|
}
|
|
136
|
-
catch { /* not found on PATH at all — nothing to audit */ }
|
|
137
197
|
hits.forEach((p, i) => out.push(resolveShim(name, p, i === 0, installedVersion)));
|
|
138
198
|
}
|
|
139
199
|
return out;
|
|
140
200
|
}
|
|
141
|
-
|
|
201
|
+
function shimNeedsRepair(shim) {
|
|
202
|
+
return shim.stale || shim.incomplete;
|
|
203
|
+
}
|
|
204
|
+
function canForward(from, to) {
|
|
205
|
+
if (from.extension === '.ps1')
|
|
206
|
+
return to.extension === '.ps1' || to.extension === '.cmd';
|
|
207
|
+
return to.extension === '.cmd';
|
|
208
|
+
}
|
|
209
|
+
/** Repair plan: each stale/incomplete shim forwards to the first same-name shim on PATH
|
|
142
210
|
* that already resolves to the installed version. Pure over the audit. */
|
|
143
211
|
function planShimRepairs(audits) {
|
|
144
|
-
return audits.filter(
|
|
145
|
-
const healthy = audits.find(a => a.name === shim.name
|
|
212
|
+
return audits.filter(shimNeedsRepair).map(shim => {
|
|
213
|
+
const healthy = audits.find(a => a.name === shim.name
|
|
214
|
+
&& !shimNeedsRepair(a)
|
|
215
|
+
&& !a.unresolvable
|
|
216
|
+
&& a.shimPath !== shim.shimPath
|
|
217
|
+
&& canForward(shim, a));
|
|
146
218
|
return { shim, forwardTo: healthy ? healthy.shimPath : null };
|
|
147
219
|
});
|
|
148
220
|
}
|
|
221
|
+
function describeProblem(shim) {
|
|
222
|
+
const parts = [];
|
|
223
|
+
if (shim.stale)
|
|
224
|
+
parts.push(`v${shim.version}`);
|
|
225
|
+
if (shim.incomplete)
|
|
226
|
+
parts.push(`missing ${shim.missingFiles.join(', ')}`);
|
|
227
|
+
return parts.length ? parts.join(', ') : 'unhealthy';
|
|
228
|
+
}
|
|
229
|
+
function writeForwarder(shim, forwardTo) {
|
|
230
|
+
if (shim.extension === '.ps1') {
|
|
231
|
+
fs.writeFileSync(shim.shimPath, [
|
|
232
|
+
'#!/usr/bin/env pwsh',
|
|
233
|
+
`# greprag doctor ${new Date().toISOString().slice(0, 10)}: was ${describeProblem(shim)}; forwarded to the current install.`,
|
|
234
|
+
`& "${forwardTo}" @args`,
|
|
235
|
+
'exit $LASTEXITCODE',
|
|
236
|
+
'',
|
|
237
|
+
].join('\r\n'));
|
|
238
|
+
return;
|
|
239
|
+
}
|
|
240
|
+
fs.writeFileSync(shim.shimPath, [
|
|
241
|
+
'@ECHO off',
|
|
242
|
+
`REM greprag doctor ${new Date().toISOString().slice(0, 10)}: was ${describeProblem(shim)}; forwarded to the current install.`,
|
|
243
|
+
`call "${forwardTo}" %*`,
|
|
244
|
+
'',
|
|
245
|
+
].join('\r\n'));
|
|
246
|
+
}
|
|
149
247
|
/** Rewrite each repairable stale shim as a one-line forwarder to its healthy
|
|
150
248
|
* sibling. Returns human-readable result lines. */
|
|
151
249
|
function repairStaleShims(repairs) {
|
|
152
250
|
const lines = [];
|
|
153
251
|
for (const r of repairs) {
|
|
154
252
|
if (!r.forwardTo) {
|
|
155
|
-
lines.push(`✗ ${r.shim.shimPath} is
|
|
253
|
+
lines.push(`✗ ${r.shim.shimPath} is unhealthy (${describeProblem(r.shim)}) and no current ${r.shim.name} shim exists on PATH to forward to — run \`npm i -g greprag@latest\`, then re-run doctor.`);
|
|
156
254
|
continue;
|
|
157
255
|
}
|
|
158
256
|
try {
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
`REM greprag doctor ${new Date().toISOString().slice(0, 10)}: was a stale v${r.shim.version} shim; forwarded to the current install.`,
|
|
162
|
-
`call "${r.forwardTo}" %*`,
|
|
163
|
-
'',
|
|
164
|
-
].join('\r\n'));
|
|
165
|
-
lines.push(`✓ ${r.shim.shimPath} (was v${r.shim.version}) now forwards to ${r.forwardTo}`);
|
|
257
|
+
writeForwarder(r.shim, r.forwardTo);
|
|
258
|
+
lines.push(`✓ ${r.shim.shimPath} (was ${describeProblem(r.shim)}) now forwards to ${r.forwardTo}`);
|
|
166
259
|
}
|
|
167
260
|
catch (e) {
|
|
168
261
|
lines.push(`✗ could not rewrite ${r.shim.shimPath}: ${e.message}`);
|
package/package.json
CHANGED
package/skill/mechanic/SKILL.md
CHANGED
|
@@ -46,7 +46,7 @@ task.
|
|
|
46
46
|
|
|
47
47
|
The Mechanic keeps the harness healthy. The loop is **friction → fix → repair**:
|
|
48
48
|
|
|
49
|
-
- **friction** — a rough spot the agent or operator hit: a gotcha, a rediscovery, a repeated correction, churn, rework. Live friction becomes one `greprag fix spawn --type <type> "<unit>"` chip at the moment it happens.
|
|
49
|
+
- **friction** — a rough spot the agent or operator hit: a gotcha, a rediscovery, a repeated correction, churn, rework. Ordinary misunderstanding or misreading user intent is not friction unless it repeats or exposes a durable doctrine/harness/injection/env/code failure. Live friction becomes one `greprag fix spawn --type <type> "<unit>"` chip at the moment it happens.
|
|
50
50
|
- **fix** — the repair mission or audit note. Live fixes are owned by spawned FIX chips; the queue remains for existing backlog and deliberate design-input notes.
|
|
51
51
|
- **repair** — the fix wired in so it can't recur: a hook, a code change, or *surfacing it* (a doc line / a fact-seed / an injection). A repaired fix is done.
|
|
52
52
|
|
|
@@ -17,6 +17,7 @@ This is an internal bundled GrepRAG schema, not tenant-controlled skill content.
|
|
|
17
17
|
- Bundled GrepRAG skill: edit `packages/cli/skill/<name>/`, never the installed artifact.
|
|
18
18
|
- Mirrored/local skill: edit its canonical source, not the generated Codex/Claude/OpenCode adapter.
|
|
19
19
|
- Unknown source: stop and identify the canonical source before editing.
|
|
20
|
+
- Codex/non-interactive harness: do not launch an editor. Use `greprag skill mirror export <skill> --to <dir>`, patch the exported Markdown files with file edits, then `greprag skill mirror apply <dir> --skill <skill> --expected-hash <exported-hash>`. If using `greprag skill edit` as the export helper, pass `--no-open`.
|
|
20
21
|
|
|
21
22
|
## Skill-Refresh Chips
|
|
22
23
|
|