greprag 5.74.4 → 5.74.6

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.
@@ -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: discover local peers with list_threads; inspect/message them with native thread tools, and use GrepRAG for cross-harness peers.';
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.';
@@ -271,14 +271,15 @@ async function runShimAudit(opts) {
271
271
  }
272
272
  if (shims.length === 0)
273
273
  return;
274
- const staleOnes = shims.filter(s => s.stale);
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 flag = s.stale ? ' ⚠ STALE' : s.unresolvable ? ' ? unresolvable' : '';
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 (staleOnes.length === 0) {
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 stale and no current ${r.shim.name}.cmd exists on PATH to forward to — run \`npm i -g greprag@latest\` first.`);
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} stale shim(s) to forward to the current install? (yes/no): `)).trim().toLowerCase();
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.',
@@ -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
- Edit canonical mirror via $EDITOR + hash guard
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
- if (noOpen) {
457
- console.log('Editor skipped.');
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;
@@ -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(s => s.stale);
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 .cmd on PATH actually executes):'];
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 flag = s.stale ? ' ⚠ STALE' : s.unresolvable ? ' ? unresolvable' : '';
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 DIFFERENT version than this CLI — hooks may silently run old code.`);
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 on your next session.`,
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 \`greprag load ${skillName} ${identityArg} ${harnessArg}\` before acting.
341
- - **Change this skill:** run \`greprag load skill-change\`, then \`greprag skill edit ${skillName} ${editArgs}\`, then \`${syncCommand}\`, then verify with \`greprag load ${skillName} ${identityArg} ${harnessArg}\`.
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
- ## Editing
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 on your next session.`
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: discover local peers with list_threads; inspect/message them with native thread tools, and use GrepRAG for cross-harness peers.";
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.";
@@ -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
- `Safe edit path: \`greprag skill edit ${skillName}\` (wraps export -> editor -> hash-checked apply). Manual path: \`greprag skill mirror export ${skillName} --to <dir>\`, edit, then apply with the exported hash.`,
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
  }
@@ -1,7 +1,6 @@
1
1
  "use strict";
2
- /** Windows shim audit — detect (and let doctor repair) stale `greprag.cmd` /
3
- * `greprag-hook.cmd` shims that resolve to a DIFFERENT install than the
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"`), absolute .js paths, and
63
- * one-line `call "<other>.cmd"` forwarders. Pure. */
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 REM comment lines — a forwarder's provenance comment may name paths.
66
- const text = cmdText.split(/\r?\n/).filter(l => !/^\s*(REM|::)\s/i.test(l)).join('\n');
67
- // Quoted or bare tokens ending .js or .cmd that mention greprag.
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 .cmd; first match otherwise.
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.replace(/%~?dp0%?\\?/gi, cmdDir + path.sep);
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
- /** Version of the greprag package that owns `scriptPath` — nearest
79
- * package.json named `greprag` walking up (max 6 levels). Pure I/O read. */
80
- function shimPackageVersion(scriptPath) {
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 .cmd — follow
133
+ current = t; // forwarded to another shim — follow
116
134
  }
117
- const version = target ? shimPackageVersion(target) : null;
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, shimPath, active, target, version,
120
- stale: version !== null && version !== installed,
121
- unresolvable: target === null || version === null,
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
- /** Every `greprag` / `greprag-hook` .cmd on PATH, in PATH order, with the
125
- * version each actually executes. Empty off-Windows. */
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 ['greprag', 'greprag-hook']) {
131
- let hits = [];
132
- try {
133
- hits = (0, child_process_1.execFileSync)('where.exe', [`${name}.cmd`], { encoding: 'utf-8' })
134
- .split(/\r?\n/).map(s => s.trim()).filter(Boolean);
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
- /** Repair plan: each stale shim forwards to the first same-name shim on PATH
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(a => a.stale).map(shim => {
145
- const healthy = audits.find(a => a.name === shim.name && !a.stale && !a.unresolvable && a.shimPath !== shim.shimPath);
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 stale (v${r.shim.version}) and no current-version ${r.shim.name}.cmd exists on PATH to forward to — run \`npm i -g greprag@latest\`, then re-run doctor.`);
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
- fs.writeFileSync(r.shim.shimPath, [
160
- '@ECHO off',
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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "greprag",
3
- "version": "5.74.4",
3
+ "version": "5.74.6",
4
4
  "description": "GrepRAG — agent memory for Claude Code, Codex, and OpenCode.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -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
 
@@ -41,6 +41,15 @@ verifies the active goal names this mission before fanout. That native goal
41
41
  tracks the LEAD mission; it is not a GrepRAG lease, durable manifest, or child
42
42
  permission gate.
43
43
 
44
+ LEAD is an orchestrator, not an implementation chip. After `create_goal` /
45
+ `get_goal`, the LEAD's next required action is creating the listed
46
+ `Chip A/B/C: <Specific Purview>` visible Codex tasks/worktrees. Before all
47
+ required children exist, LEAD must not implement the phase, edit application,
48
+ worker, or product source files, or treat Chip A/B/C as sections of its own work. If
49
+ child creation fails, LEAD stops and reports `BLOCKED`; it must not continue
50
+ solo. Allowed LEAD edits are mission/runtime docs, integration reconciliation
51
+ after chip reports, and tiny mechanical conflict fixes during merge.
52
+
44
53
  ## Quick versus Leader
45
54
 
46
55
  - **Quick:** the current task renames itself `LEAD: <Mission>` and directly
@@ -87,7 +96,8 @@ ordering in the brief rather than relying on hidden coordination metadata.
87
96
  blocker, explicit LEAD decision request, or operator status request.
88
97
  4. In the dedicated LEAD, create the native top-level mission goal with
89
98
  `create_goal`, then verify it with `get_goal`.
90
- 5. From the LEAD, spawn each `Chip A/B/C: <Specific Purview>` with its own
99
+ 5. Next, before any implementation edit, spawn each
100
+ `Chip A/B/C: <Specific Purview>` with its own
91
101
  visible worktree and the mission context it needs.
92
102
  A `FIX: [type] <one friction unit>` child must make `greprag load mechanic` its
93
103
  first Setup action before diagnosis or edits.
@@ -116,7 +116,14 @@ exists, run `node scripts/ensure-npm-deps.cjs`; if
116
116
  another checkout.
117
117
  If the exact first-line title is `LEAD: <Mission>`, create a native top-level
118
118
  mission goal with `create_goal`, then run `get_goal` and verify the active goal
119
- names this mission before any child fanout.
119
+ names this mission before any child fanout. Then create the listed
120
+ `Chip A/B/C: <Specific Purview>` visible Codex tasks/worktrees before any
121
+ implementation edit. LEAD is an orchestrator, not an implementation chip: do
122
+ not implement the phase, edit app/worker/product source files, or treat
123
+ Chip A/B/C as sections of your own work before all required children exist. If
124
+ child creation fails, stop and report `BLOCKED`; do not continue solo. Allowed
125
+ LEAD edits are mission/runtime docs, integration reconciliation after chip
126
+ reports, and tiny mechanical conflict fixes during merge.
120
127
  If the exact first-line title starts with `FIX:`, run
121
128
  `greprag load mechanic` before diagnosis or edits.
122
129
  If the exact first-line title is `ADVISOR: <Purview>`, report consultation to
@@ -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