greprag 5.74.10 → 5.74.11

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.
@@ -275,8 +275,12 @@ async function runShimAudit(opts) {
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 missing = s.incomplete ? ` missing ${s.missingFiles.join(', ')}` : '';
279
- const flag = s.stale ? ` ⚠ STALE${missing}` : s.incomplete ? ` ⚠ INCOMPLETE${missing}` : s.unresolvable ? ' ? unresolvable' : '';
278
+ const details = [
279
+ ...(s.incomplete ? [`missing ${s.missingFiles.join(', ')}`] : []),
280
+ ...(s.volatileForward ? ['fnm temp forward'] : []),
281
+ ];
282
+ const suffix = details.length ? ` ${details.join(', ')}` : '';
283
+ const flag = s.stale ? ` ⚠ STALE${suffix}` : s.incomplete ? ` ⚠ INCOMPLETE${suffix}` : s.volatileForward ? ` ⚠ VOLATILE${suffix}` : s.unresolvable ? ' ? unresolvable' : '';
280
284
  console.log(` ${s.name}${s.active ? '' : ' (shadowed)'}: ${s.shimPath} → ${ver}${flag}`);
281
285
  }
282
286
  if (unhealthy.length === 0) {
@@ -291,7 +295,7 @@ async function runShimAudit(opts) {
291
295
  if (!opts.yes) {
292
296
  const fixable = repairs.filter(r => r.forwardTo);
293
297
  for (const r of repairs.filter(x => !x.forwardTo)) {
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.`);
298
+ console.log(` ✗ ${r.shim.shimPath} is unhealthy and no durable current ${r.shim.name} shim exists on PATH to forward to — run \`${(0, windows_shims_1.shimInstallCommand)()}\` first.`);
295
299
  }
296
300
  if (fixable.length === 0) {
297
301
  console.log('');
@@ -5,10 +5,10 @@
5
5
  * live stress level) rather than a binary resolvable deficiency.
6
6
  *
7
7
  * A reminder-interrupt MODULE is `{ id, detect, announce, reminder }` (reminder-types.ts):
8
- * `detect(env)` reads the live state → a tier (`silent` = nothing to fix); a SessionStart
9
- * `announce` loads the schema ONCE; a per-turn `reminder` fires a thin, grounded pointer
10
- * back at it. The no-orphan invariant requires the PAIR. The container (reminder-registry.ts)
11
- * wires every module; the hook (hook.ts) assembles the env and emits — see `injectReminders`.
8
+ * `detect(env)` reads the live state → a tier (`silent` = nothing to fix); the grepragOS
9
+ * primer owns the startup schema, and this module contributes only a thin live reminder.
10
+ * The container (reminder-registry.ts) wires every module; the hook (hook.ts) assembles
11
+ * the env and emits — see `injectReminders`.
12
12
  *
13
13
  * PURE: imports only the stress constants; returns strings + a tier + a tally. No fs, no
14
14
  * network, no clock — the hook does ALL I/O. Keeping detect/tier a pure function is what
@@ -19,10 +19,10 @@ exports.frictionReminderTier = frictionReminderTier;
19
19
  exports.buildMechanicLiveNotice = buildMechanicLiveNotice;
20
20
  exports.buildFrictionReminder = buildFrictionReminder;
21
21
  exports.buildBootstrapFrictionReminder = buildBootstrapFrictionReminder;
22
- exports.buildMechanicAnnounce = buildMechanicAnnounce;
23
22
  exports.tallyFrictionFire = tallyFrictionFire;
24
23
  exports.tallyMechanicSpawn = tallyMechanicSpawn;
25
24
  const state_trigger_1 = require("./state-trigger");
25
+ // adr: adr/friction-memory-first.md
26
26
  function quoteForCommand(value) {
27
27
  return value.replace(/[\r\n]+/g, ' ').replace(/"/g, '\\"').trim();
28
28
  }
@@ -49,21 +49,22 @@ function frictionReminderTier(stress) {
49
49
  return 'nudge';
50
50
  return 'silent';
51
51
  }
52
- /** The per-turn reminder line — fires only on detected stress, points at the ONE
53
- * friction reflex (fix spawn), ends on a forced binary so it converts. null = calm.
52
+ /** The per-turn reminder line — fires only on detected stress, points first at
53
+ * Memory for a known overcome, then at fix spawn for unresolved friction.
54
+ * Ends on a forced binary so it converts. null = calm.
54
55
  * The queue-route (`mechanic friction`) and log-route (`fix log`) verbs were
55
- * RETIRED as reflexes 2026-07-12 (grepragOS): friction is fixed at the moment,
56
- * one chip per unit. */
56
+ * RETIRED as reflexes 2026-07-12 (grepragOS). */
57
57
  const FIX_SPAWN = 'greprag fix spawn --type <harness|doctrine|injection|env|code> "<one unit>"';
58
+ const MEMORY_SEARCH = 'greprag memory search "<exact error/friction + repo/tool>"';
58
59
  function buildMechanicLiveNotice(_env) {
59
- return `GrepRAG Mechanic is live. Friction reflex: \`${FIX_SPAWN}\` one fix chip per unit, spawned at the moment; then keep working. Check role only when needed: \`greprag mechanic role\`.`;
60
+ return `GrepRAG Mechanic is live. Friction order: \`${MEMORY_SEARCH}\` first; apply a clear overcome and continue. No answer, failed overcome, or repeated friction \`${FIX_SPAWN}\`. Check role only when needed: \`greprag mechanic role\`.`;
60
61
  }
61
62
  function buildFrictionReminder(tier, env) {
62
63
  switch (tier) {
63
64
  case 'nag':
64
- return `⚠ FRICTION HIGH (live signal: repetition / errors / churn) — STOP pushing through. Name the ONE unit, choose type by repair surface, and spawn now: \`${FIX_SPAWN}\` (chip fixes, verifies, checkpoints, and hands it to the mission delivery owner). No friction? say "clear" + continue.`;
65
+ return `⚠ FRICTION HIGH (live signal: repetition / errors / churn) — STOP pushing through. MEMORY FIRST: \`${MEMORY_SEARCH}\`. Clear overcome apply + continue. No answer, remembered overcome fails, or same friction repeats \`${FIX_SPAWN}\`. No friction? say "clear" + continue.`;
65
66
  case 'nudge':
66
- return `⚠ Friction detected (live signal) — repeated yourself, fought a tool, or hit a wall? Choose type by repair surface and spawn: \`${FIX_SPAWN}\`, then keep working. Otherwise say "clear" + continue.`;
67
+ return `⚠ Friction detected (live signal) — repeated yourself, fought a tool, or hit a wall? Search first: \`${MEMORY_SEARCH}\`. Apply a clear overcome + continue; no answer, failed overcome, or repeat → \`${FIX_SPAWN}\`. Otherwise say "clear" + continue.`;
67
68
  default:
68
69
  return null;
69
70
  }
@@ -74,25 +75,8 @@ function buildBootstrapFrictionReminder(env) {
74
75
  return null;
75
76
  const repeats = b.repeats && b.repeats > 1 ? ` repeated ${b.repeats}x` : '';
76
77
  const error = b.error ? ` Last error: ${b.error}` : '';
77
- return `⚠ BOOTSTRAP FRICTION detected${repeats}: setup/toolchain failure (${b.signal || 'setup'}) around \`${b.command || 'unknown command'}\`.${error} If this is not caused by the code you just edited, spawn a fix chip now: \`greprag fix spawn --type env "${bootstrapSummary(env)}"\`. The chip owns the root-cause repair, verifies it, and hands it to the mission delivery owner so future sessions stop hitting it.`;
78
- }
79
- // ---------- The fix-reflex primer (announce-only) ----------------------------
80
- /** SessionStart primer — the fix reflex, loaded ONCE. Full doctrine inline
81
- * (a PRIMER, not a pointer): it teaches WHEN friction is a spawn-now signal
82
- * and the chip's delivery-owner contract. Rewritten 2026-07-12 (grepragOS):
83
- * the queue-route (`mechanic friction`), log-route (`fix log`) and the
84
- * one-Mechanic-absorbs-all-friction model are RETIRED as reflexes — one unit
85
- * of friction ⇒ one fix chip, spawned at the moment. */
86
- function buildMechanicAnnounce() {
87
- return [
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. 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
- '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
- '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
- '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.',
93
- 'Every repair is ROOT-CAUSE — fix the pattern that makes the friction class possible, never a guard on today\'s trigger.',
94
- 'A per-turn reminder fires ONLY when live friction is DETECTED (the stress signal — repetition / errors / churn), never on a timer — act on it, or say "clear" and continue.',
95
- ].join('\n');
78
+ const summary = bootstrapSummary(env);
79
+ return `⚠ BOOTSTRAP FRICTION detected${repeats}: setup/toolchain failure (${b.signal || 'setup'}) around \`${b.command || 'unknown command'}\`.${error} MEMORY FIRST: \`greprag memory search "${summary}"\`. Apply a clear overcome + continue. No answer, remembered overcome fails, or the same failure repeats → \`greprag fix spawn --type env "${summary}"\`.`;
96
80
  }
97
81
  /** Normalize any prior-stats blob (possibly missing/partial/corrupt) into a fully
98
82
  * populated FrictionStats. Pure, total. */
@@ -136,13 +120,10 @@ function tallyMechanicSpawn(prior) {
136
120
  return s;
137
121
  }
138
122
  // ---------- Registry module --------------------------------------------------
139
- /** The active Mechanic a PRIMER-ONLY module (docs/load-system.md): one strong
140
- * SessionStart announce, no per-turn reminder (the friction nag was removed
141
- * 2026-06-22 as wallpaper). `dependsOn` the chip-spawn pointer so the chip concept
142
- * it references is already loaded — the boot sequence places it AFTER chips. */
123
+ /** The active Mechanic reminder. The grepragOS primer owns the one startup schema;
124
+ * this module is reminder-only and fires only from live stress/bootstrap state. */
143
125
  exports.mechanicFrictionModule = {
144
126
  id: 'mechanic-friction',
145
- dependsOn: ['chip-spawn-pointer'],
146
127
  // Stress fires the real friction reminder. A designated Mechanic also creates a
147
128
  // one-shot ambient route notice per source session/role epoch (hook-stamped).
148
129
  detect: (env) => {
@@ -155,12 +136,7 @@ exports.mechanicFrictionModule = {
155
136
  return { tier: 'ambient' };
156
137
  return { tier: 'silent' };
157
138
  },
158
- announce: (env = {}) => {
159
- const base = buildMechanicAnnounce();
160
- if (env.mechanicLive && !env.mechanic)
161
- return `${base}\n\n${buildMechanicLiveNotice(env)}`;
162
- return base;
163
- },
139
+ announce: () => null,
164
140
  reminder: (d, env) => {
165
141
  if (d.detail?.kind === 'bootstrap-friction')
166
142
  return buildBootstrapFrictionReminder(env);
@@ -63,7 +63,7 @@ const doc_mirror_client_1 = require("../doc-mirror-client");
63
63
  const LIBRARY = {
64
64
  os: {
65
65
  files: ['skill/templates/os.md'],
66
- purpose: 'grepragOS — the operating laws: doctrine vs state, discoverability, pull-before-derive, friction ⇒ fix spawn, teach the system not the chat.',
66
+ purpose: 'grepragOS — the operating laws: doctrine vs state, discoverability, pull-before-derive, memory-first friction recovery, unresolved friction ⇒ fix spawn, teach the system not the chat.',
67
67
  },
68
68
  'chip-spawn': {
69
69
  files: ['skill/templates/chip-spawn.md'],
@@ -29,9 +29,9 @@ function buildOpenCodeEnv(params) {
29
29
  // No opencode-native friction detector is wired yet: Claude Code's stress /
30
30
  // bootstrap-friction signals come from state-trigger.ts reading the
31
31
  // transcript in hook.ts, and opencode has no equivalent source plumbed.
32
- // 0 = calm keeps the mechanic-friction REMINDER silent while its ANNOUNCE
33
- // (the fix-spawn doctrine) still lands every session. Detector design is
34
- // parked with scored options in the chip report
32
+ // 0 = calm keeps the reminder-only mechanic-friction module silent. The
33
+ // os-primer still lands the memory-first escalation doctrine every session.
34
+ // Detector design is parked with scored options in the chip report
35
35
  // (chip/opencode-fix-chip-system-parity).
36
36
  stress: 0,
37
37
  armed: params.armed ?? false,
@@ -13,6 +13,7 @@
13
13
  Object.defineProperty(exports, "__esModule", { value: true });
14
14
  exports.osPrimerModule = void 0;
15
15
  exports.buildOsPrimer = buildOsPrimer;
16
+ // adr: adr/friction-memory-first.md
16
17
  function buildOsPrimer(env) {
17
18
  // The chip-spawn method is per-harness (Codex never sees the Claude entry —
18
19
  // the same wall the codex-chip-spawn pointer module enforces; opencode's
@@ -24,8 +25,8 @@ function buildOsPrimer(env) {
24
25
  '[grepragOS — the operating laws. Full doctrine: `greprag load os`.]',
25
26
  '• Doctrine vs state: methods ship in the CLI (`greprag load`); live state lives in the repo. A skill that depends on repo state carries a "STATE — read these first" block naming exact paths.',
26
27
  '• Discoverability: every durable artifact must be findable next session — docs auto-register, skills auto-mirror, decisions get a dated ADR/decision-log entry, everything else gets its path named in the owning skill/doc. If nothing points at it, you didn\'t finish.',
27
- '• Pull before derive: `greprag memory search` / `corpus search` / `load` BEFORE asking the operator or re-deriving what the project already knows. Env/toolchain/worktree/secret bootstrap roadblock → search Memory for the exact error and repo/tool before inventing a workaround. Named person/org/project/repo/customer/handle or unexplained proper noun → search Memory before guessing, unless fully defined in-turn.',
28
- `• Friction ⇒ fix NOW: type by durable repair surface (\`harness|doctrine|injection|env|code\`) ⇒ \`greprag fix spawn --type <type> "<unit>"\`. It PRINTS a fix-chip mission — YOU then create the visible child task with that mission as its first message (how: \`greprag load ${spawnEntry}\`, or your harness's native task tool). Repo write = isolated worktree; data-only row/diagnosis = no repo write. One unit per chip; the chip fixes, verifies, checkpoints, and hands it to the mission delivery owner. Never queue friction.`,
28
+ '• Pull before derive: `greprag memory search` / `corpus search` / `load` BEFORE asking the operator or re-deriving what the project already knows. Friction or an env/toolchain/worktree/secret bootstrap roadblock → search Memory for the exact error/friction + repo/tool before inventing a workaround. Clear overcome → apply it and continue. Named person/org/project/repo/customer/handle or unexplained proper noun → search Memory before guessing, unless fully defined in-turn.',
29
+ `• Unresolved friction ⇒ fix: only when Memory has no answer, the remembered overcome fails, or the same friction repeats. Then type by durable repair surface (\`harness|doctrine|injection|env|code\`) ⇒ \`greprag fix spawn --type <type> "<unit>"\`. It PRINTS a fix-chip mission — YOU then create the visible child task with that mission as its first message (how: \`greprag load ${spawnEntry}\`, or your harness's native task tool). Repo write = isolated worktree; data-only row/diagnosis = no repo write. One unresolved unit per chip; the chip fixes, verifies, checkpoints, and hands it to the mission delivery owner.`,
29
30
  '• Teach the system, not the chat: explained twice by the operator ⇒ it belongs in a skill / load entry / STATE block / ADR, not the conversation.',
30
31
  ].join('\n');
31
32
  }
@@ -30,10 +30,9 @@ const loadout_reminder_1 = require("./loadout-reminder");
30
30
  const delivery_reminder_1 = require("./delivery-reminder");
31
31
  /** Registry order = display order. THE single agent-facing announce/reminder assembly:
32
32
  * the hook does I/O → fills ReminderEnv → collectAnnounces (SessionStart) / collectReminders
33
- * (per turn) render every module here in this order. Order preserves the historical recap
34
- * preamble (setup-warning front-desk checkpoint → assistant-doctrine) and keeps the two
35
- * loud, action-first modules last (watcher-arm mechanic) so they read most-recent at
36
- * SessionStart. Add a module here to wire it into BOTH surfaces at once. */
33
+ * (per turn) render every module here in this order. A module may own either surface or both;
34
+ * os-primer owns startup friction doctrine and mechanic-friction is reminder-only.
35
+ * Add a module here to wire it into the appropriate surfaces at once. */
37
36
  exports.REGISTRY = [
38
37
  os_primer_reminder_1.osPrimerModule, // grepragOS constitution — the frame every other primer hangs off; boots FIRST
39
38
  inbox_primer_reminder_1.inboxPrimerModule,
@@ -327,8 +327,12 @@ function renderShims(w) {
327
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 missing = s.incomplete ? ` missing ${s.missingFiles.join(', ')}` : '';
331
- const flag = s.stale ? ` ⚠ STALE${missing}` : s.incomplete ? ` ⚠ INCOMPLETE${missing}` : s.unresolvable ? ' ? unresolvable' : '';
330
+ const details = [
331
+ ...(s.incomplete ? [`missing ${s.missingFiles.join(', ')}`] : []),
332
+ ...(s.volatileForward ? ['fnm temp forward'] : []),
333
+ ];
334
+ const suffix = details.length ? ` ${details.join(', ')}` : '';
335
+ const flag = s.stale ? ` ⚠ STALE${suffix}` : s.incomplete ? ` ⚠ INCOMPLETE${suffix}` : s.volatileForward ? ` ⚠ VOLATILE${suffix}` : s.unresolvable ? ' ? unresolvable' : '';
332
336
  lines.push(` ${s.name}${s.active ? '' : ' (shadowed)'}: ${s.shimPath} → ${ver}${flag}`);
333
337
  }
334
338
  if (w.mismatch) {
@@ -1752,8 +1752,8 @@ function buildOsPrimer(env) {
1752
1752
  "[grepragOS \u2014 the operating laws. Full doctrine: `greprag load os`.]",
1753
1753
  '\u2022 Doctrine vs state: methods ship in the CLI (`greprag load`); live state lives in the repo. A skill that depends on repo state carries a "STATE \u2014 read these first" block naming exact paths.',
1754
1754
  "\u2022 Discoverability: every durable artifact must be findable next session \u2014 docs auto-register, skills auto-mirror, decisions get a dated ADR/decision-log entry, everything else gets its path named in the owning skill/doc. If nothing points at it, you didn't finish.",
1755
- "\u2022 Pull before derive: `greprag memory search` / `corpus search` / `load` BEFORE asking the operator or re-deriving what the project already knows. Env/toolchain/worktree/secret bootstrap roadblock \u2192 search Memory for the exact error and repo/tool before inventing a workaround. Named person/org/project/repo/customer/handle or unexplained proper noun \u2192 search Memory before guessing, unless fully defined in-turn.",
1756
- `\u2022 Friction \u21D2 fix NOW: type by durable repair surface (\`harness|doctrine|injection|env|code\`) \u21D2 \`greprag fix spawn --type <type> "<unit>"\`. It PRINTS a fix-chip mission \u2014 YOU then create the visible child task with that mission as its first message (how: \`greprag load ${spawnEntry}\`, or your harness's native task tool). Repo write = isolated worktree; data-only row/diagnosis = no repo write. One unit per chip; the chip fixes, verifies, checkpoints, and hands it to the mission delivery owner. Never queue friction.`,
1755
+ "\u2022 Pull before derive: `greprag memory search` / `corpus search` / `load` BEFORE asking the operator or re-deriving what the project already knows. Friction or an env/toolchain/worktree/secret bootstrap roadblock \u2192 search Memory for the exact error/friction + repo/tool before inventing a workaround. Clear overcome \u2192 apply it and continue. Named person/org/project/repo/customer/handle or unexplained proper noun \u2192 search Memory before guessing, unless fully defined in-turn.",
1756
+ `\u2022 Unresolved friction \u21D2 fix: only when Memory has no answer, the remembered overcome fails, or the same friction repeats. Then type by durable repair surface (\`harness|doctrine|injection|env|code\`) \u21D2 \`greprag fix spawn --type <type> "<unit>"\`. It PRINTS a fix-chip mission \u2014 YOU then create the visible child task with that mission as its first message (how: \`greprag load ${spawnEntry}\`, or your harness's native task tool). Repo write = isolated worktree; data-only row/diagnosis = no repo write. One unresolved unit per chip; the chip fixes, verifies, checkpoints, and hands it to the mission delivery owner.`,
1757
1757
  "\u2022 Teach the system, not the chat: explained twice by the operator \u21D2 it belongs in a skill / load entry / STATE block / ADR, not the conversation."
1758
1758
  ].join("\n");
1759
1759
  }
@@ -2022,15 +2022,16 @@ function frictionReminderTier(stress) {
2022
2022
  return "silent";
2023
2023
  }
2024
2024
  var FIX_SPAWN = 'greprag fix spawn --type <harness|doctrine|injection|env|code> "<one unit>"';
2025
+ var MEMORY_SEARCH = 'greprag memory search "<exact error/friction + repo/tool>"';
2025
2026
  function buildMechanicLiveNotice(_env) {
2026
- return `GrepRAG Mechanic is live. Friction reflex: \`${FIX_SPAWN}\` \u2014 one fix chip per unit, spawned at the moment; then keep working. Check role only when needed: \`greprag mechanic role\`.`;
2027
+ return `GrepRAG Mechanic is live. Friction order: \`${MEMORY_SEARCH}\` first; apply a clear overcome and continue. No answer, failed overcome, or repeated friction \u2192 \`${FIX_SPAWN}\`. Check role only when needed: \`greprag mechanic role\`.`;
2027
2028
  }
2028
2029
  function buildFrictionReminder(tier, env) {
2029
2030
  switch (tier) {
2030
2031
  case "nag":
2031
- return `\u26A0 FRICTION HIGH (live signal: repetition / errors / churn) \u2014 STOP pushing through. Name the ONE unit, choose type by repair surface, and spawn now: \`${FIX_SPAWN}\` (chip fixes, verifies, checkpoints, and hands it to the mission delivery owner). No friction? say "clear" + continue.`;
2032
+ return `\u26A0 FRICTION HIGH (live signal: repetition / errors / churn) \u2014 STOP pushing through. MEMORY FIRST: \`${MEMORY_SEARCH}\`. Clear overcome \u2192 apply + continue. No answer, remembered overcome fails, or same friction repeats \u2192 \`${FIX_SPAWN}\`. No friction? say "clear" + continue.`;
2032
2033
  case "nudge":
2033
- return `\u26A0 Friction detected (live signal) \u2014 repeated yourself, fought a tool, or hit a wall? Choose type by repair surface and spawn: \`${FIX_SPAWN}\`, then keep working. Otherwise say "clear" + continue.`;
2034
+ return `\u26A0 Friction detected (live signal) \u2014 repeated yourself, fought a tool, or hit a wall? Search first: \`${MEMORY_SEARCH}\`. Apply a clear overcome + continue; no answer, failed overcome, or repeat \u2192 \`${FIX_SPAWN}\`. Otherwise say "clear" + continue.`;
2034
2035
  default:
2035
2036
  return null;
2036
2037
  }
@@ -2041,22 +2042,11 @@ function buildBootstrapFrictionReminder(env) {
2041
2042
  return null;
2042
2043
  const repeats = b.repeats && b.repeats > 1 ? ` repeated ${b.repeats}x` : "";
2043
2044
  const error = b.error ? ` Last error: ${b.error}` : "";
2044
- return `\u26A0 BOOTSTRAP FRICTION detected${repeats}: setup/toolchain failure (${b.signal || "setup"}) around \`${b.command || "unknown command"}\`.${error} If this is not caused by the code you just edited, spawn a fix chip now: \`greprag fix spawn --type env "${bootstrapSummary(env)}"\`. The chip owns the root-cause repair, verifies it, and hands it to the mission delivery owner so future sessions stop hitting it.`;
2045
- }
2046
- function buildMechanicAnnounce() {
2047
- return [
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. 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
- '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
- "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
- "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.",
2053
- "Every repair is ROOT-CAUSE \u2014 fix the pattern that makes the friction class possible, never a guard on today's trigger.",
2054
- 'A per-turn reminder fires ONLY when live friction is DETECTED (the stress signal \u2014 repetition / errors / churn), never on a timer \u2014 act on it, or say "clear" and continue.'
2055
- ].join("\n");
2045
+ const summary = bootstrapSummary(env);
2046
+ return `\u26A0 BOOTSTRAP FRICTION detected${repeats}: setup/toolchain failure (${b.signal || "setup"}) around \`${b.command || "unknown command"}\`.${error} MEMORY FIRST: \`greprag memory search "${summary}"\`. Apply a clear overcome + continue. No answer, remembered overcome fails, or the same failure repeats \u2192 \`greprag fix spawn --type env "${summary}"\`.`;
2056
2047
  }
2057
2048
  var mechanicFrictionModule = {
2058
2049
  id: "mechanic-friction",
2059
- dependsOn: ["chip-spawn-pointer"],
2060
2050
  // Stress fires the real friction reminder. A designated Mechanic also creates a
2061
2051
  // one-shot ambient route notice per source session/role epoch (hook-stamped).
2062
2052
  detect: (env) => {
@@ -2069,14 +2059,7 @@ var mechanicFrictionModule = {
2069
2059
  return { tier: "ambient" };
2070
2060
  return { tier: "silent" };
2071
2061
  },
2072
- announce: (env = {}) => {
2073
- const base = buildMechanicAnnounce();
2074
- if (env.mechanicLive && !env.mechanic)
2075
- return `${base}
2076
-
2077
- ${buildMechanicLiveNotice(env)}`;
2078
- return base;
2079
- },
2062
+ announce: () => null,
2080
2063
  reminder: (d, env) => {
2081
2064
  if (d.detail?.kind === "bootstrap-friction")
2082
2065
  return buildBootstrapFrictionReminder(env);
@@ -2558,9 +2541,9 @@ function buildOpenCodeEnv(params) {
2558
2541
  // No opencode-native friction detector is wired yet: Claude Code's stress /
2559
2542
  // bootstrap-friction signals come from state-trigger.ts reading the
2560
2543
  // transcript in hook.ts, and opencode has no equivalent source plumbed.
2561
- // 0 = calm keeps the mechanic-friction REMINDER silent while its ANNOUNCE
2562
- // (the fix-spawn doctrine) still lands every session. Detector design is
2563
- // parked with scored options in the chip report
2544
+ // 0 = calm keeps the reminder-only mechanic-friction module silent. The
2545
+ // os-primer still lands the memory-first escalation doctrine every session.
2546
+ // Detector design is parked with scored options in the chip report
2564
2547
  // (chip/opencode-fix-chip-system-parity).
2565
2548
  stress: 0,
2566
2549
  armed: params.armed ?? false,
@@ -10,7 +10,9 @@
10
10
  * silently emitted no loadout announce and the report looked like a server
11
11
  * bug. The CLI must catch that split itself: `greprag status` names every
12
12
  * shim + the version it actually executes; `greprag doctor` rewrites stale
13
- * shims to forward to a current one.
13
+ * shims to forward to a current one. Durable shims must not forward into
14
+ * fnm_multishells: those are per-shell temp directories and can disappear
15
+ * while the stable PATH entry remains.
14
16
  *
15
17
  * Pure helpers (parse/version/plan) are exported for unit tests; only
16
18
  * `auditWindowsShims`/`repairStaleShims` touch PATH + disk.
@@ -55,6 +57,7 @@ exports.missingRequiredShimFiles = missingRequiredShimFiles;
55
57
  exports.auditWindowsShims = auditWindowsShims;
56
58
  exports.shimNeedsRepair = shimNeedsRepair;
57
59
  exports.planShimRepairs = planShimRepairs;
60
+ exports.shimInstallCommand = shimInstallCommand;
58
61
  exports.repairStaleShims = repairStaleShims;
59
62
  const fs = __importStar(require("fs"));
60
63
  const path = __importStar(require("path"));
@@ -111,6 +114,9 @@ function missingRequiredShimFiles(scriptPath, name) {
111
114
  return [];
112
115
  return requiredFilesForShim(name).filter(file => !fs.existsSync(path.join(info.root, file)));
113
116
  }
117
+ function isFnmMultishellPath(p) {
118
+ return !!p && /[\\/]fnm_multishells[\\/]/i.test(path.normalize(p));
119
+ }
114
120
  /** Resolve one shim: follow up to 3 .cmd forwarding hops, then read the
115
121
  * owning package version. */
116
122
  function resolveShim(name, shimPath, active, installed) {
@@ -134,6 +140,7 @@ function resolveShim(name, shimPath, active, installed) {
134
140
  }
135
141
  const info = target ? shimPackageInfo(target) : null;
136
142
  const missingFiles = info ? requiredFilesForShim(name).filter(file => !fs.existsSync(path.join(info.root, file))) : [];
143
+ const volatileForward = !isFnmMultishellPath(shimPath) && isFnmMultishellPath(target);
137
144
  return {
138
145
  name,
139
146
  extension: path.extname(shimPath).toLowerCase() === '.ps1' ? '.ps1' : '.cmd',
@@ -143,6 +150,7 @@ function resolveShim(name, shimPath, active, installed) {
143
150
  version: info?.version ?? null,
144
151
  stale: info !== null && info.version !== installed,
145
152
  incomplete: missingFiles.length > 0,
153
+ volatileForward,
146
154
  missingFiles,
147
155
  unresolvable: target === null || info === null,
148
156
  };
@@ -199,7 +207,7 @@ function auditWindowsShims(installedVersion) {
199
207
  return out;
200
208
  }
201
209
  function shimNeedsRepair(shim) {
202
- return shim.stale || shim.incomplete;
210
+ return shim.stale || shim.incomplete || shim.volatileForward;
203
211
  }
204
212
  function canForward(from, to) {
205
213
  if (from.extension === '.ps1')
@@ -214,6 +222,7 @@ function planShimRepairs(audits) {
214
222
  && !shimNeedsRepair(a)
215
223
  && !a.unresolvable
216
224
  && a.shimPath !== shim.shimPath
225
+ && (isFnmMultishellPath(shim.shimPath) || !isFnmMultishellPath(a.shimPath))
217
226
  && canForward(shim, a));
218
227
  return { shim, forwardTo: healthy ? healthy.shimPath : null };
219
228
  });
@@ -224,8 +233,15 @@ function describeProblem(shim) {
224
233
  parts.push(`v${shim.version}`);
225
234
  if (shim.incomplete)
226
235
  parts.push(`missing ${shim.missingFiles.join(', ')}`);
236
+ if (shim.volatileForward)
237
+ parts.push('fnm temp forward');
227
238
  return parts.length ? parts.join(', ') : 'unhealthy';
228
239
  }
240
+ function shimInstallCommand() {
241
+ return process.platform === 'win32'
242
+ ? 'npm i -g greprag@latest --prefix "%APPDATA%\\npm"'
243
+ : 'npm i -g greprag@latest';
244
+ }
229
245
  function writeForwarder(shim, forwardTo) {
230
246
  if (shim.extension === '.ps1') {
231
247
  fs.writeFileSync(shim.shimPath, [
@@ -250,7 +266,7 @@ function repairStaleShims(repairs) {
250
266
  const lines = [];
251
267
  for (const r of repairs) {
252
268
  if (!r.forwardTo) {
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.`);
269
+ lines.push(`✗ ${r.shim.shimPath} is unhealthy (${describeProblem(r.shim)}) and no durable current ${r.shim.name} shim exists on PATH to forward to — run \`${shimInstallCommand()}\`, then re-run doctor.`);
254
270
  continue;
255
271
  }
256
272
  try {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "greprag",
3
- "version": "5.74.10",
3
+ "version": "5.74.11",
4
4
  "description": "GrepRAG — agent memory for Claude Code, Codex, and OpenCode.",
5
5
  "main": "dist/index.js",
6
6
  "bin": {
@@ -2,7 +2,7 @@
2
2
  name: mechanic
3
3
  description: |
4
4
  The Mechanic — keep the harness healthy. The loop is friction → fix → repair:
5
- spawn one fix chip per live friction unit, then repair so it can't recur.
5
+ recall known overcomes first; spawn one fix chip per unresolved friction unit.
6
6
  Digest the existing fix queue and audit/design notes, audit fixes for drift,
7
7
  mine episodic memory, promote project-agnostic repairs to global. One-at-a-time
8
8
  conversational review — never bulk.
@@ -19,10 +19,13 @@ license: MIT
19
19
 
20
20
  # Mechanic
21
21
 
22
- > **Fix-spawn reflex (ratified 2026-07-12, grepragOS law 4 — supersedes the
23
- > routing/queueing reflex below wherever they conflict).** Friction is fixed at
24
- > the MOMENT it happens: `greprag fix spawn --type <type> "<one unit>"` emits a
25
- > FIX chip for exactly that unit (never absorb adjacent friction). The chip's
22
+ > **Memory-first friction reflex (refined 2026-07-28, grepragOS laws 3–4 —
23
+ > supersedes the routing/queueing reflex below wherever they conflict).** Search
24
+ > Memory for the exact error/friction plus repo/tool first. Apply a clear
25
+ > remembered overcome and continue. Only when Memory has no answer, the overcome
26
+ > fails, or the same friction repeats does
27
+ > `greprag fix spawn --type <type> "<one unit>"` emit a FIX chip for that
28
+ > unresolved unit. The chip's
26
29
  > contract: identify the exact friction → make the smallest durable root-cause
27
30
  > fix → explain the friction and fix in human terms → checkpoint → hand it to
28
31
  > the mission delivery owner. A full-goal chip with no live parent becomes that
@@ -46,7 +49,7 @@ task.
46
49
 
47
50
  The Mechanic keeps the harness healthy. The loop is **friction → fix → repair**:
48
51
 
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.
52
+ - **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. Search Memory first; a clear overcome is applied inline, while unresolved friction becomes one `greprag fix spawn --type <type> "<unit>"` chip.
50
53
  - **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
54
  - **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
55
 
@@ -292,11 +295,11 @@ greprag mechanic off / on PANIC SWITCH — local file, no network; s
292
295
 
293
296
  **Legacy Mechanic role.** A `--mechanic` inbox watcher can still receive
294
297
  legacy `mechanic_friction` / `mechanic_reply` rows in addition to its own session
295
- lane, but live working sessions no longer route friction there. New friction is
296
- always Law 4: `greprag fix spawn --type <type> "<one unit>"`, one FIX chip per
297
- unit, at the moment it happens. `greprag mechanic set` only fans out a notice
298
- that teaches this fix-spawn reflex and preserves the reply rail for old mechanic
299
- threads. The Mechanic replies with `greprag mechanic reply --to <source-session>
298
+ lane, but live working sessions no longer route friction there. Laws 3–4 search
299
+ Memory first, then route each unresolved unit through
300
+ `greprag fix spawn --type <type> "<one unit>"`. `greprag mechanic set` only
301
+ fans out a notice that teaches this reflex and preserves the reply rail for old
302
+ mechanic threads. The Mechanic replies with `greprag mechanic reply --to <source-session>
300
303
  "..."` only for legacy follow-up coordination.
301
304
 
302
305
  **Born shadow → graduate.** Every repair is born `shadow`: it matches and logs would-have-fired
@@ -6,7 +6,7 @@
6
6
 
7
7
  The skill-tuning reflex injects a directive when a skill under break-in loads. The old directive said "watch your execution, emit a SELF-TUNING block, propose a fix" — vague (it pointed at the *run*, not the *skill*) and propose-only (no edit ever landed). This doc replaces that with **six locatable skill defects**, each mapped to one concrete edit. The reflex's loop is: **detect a defined friction → locate it in the skill's SKILL.md → apply the fix per the matching recipe → show the diff for one accept/revert.**
8
8
 
9
- A friction that doesn't match one of the six below is **not auto-fixable inline** spawn a doctrine fix chip for the one unit (`greprag fix spawn --type doctrine "<skill friction unit>"`) and keep working. Use `greprag fix log` only for deliberate audit/design notes, not as the live-friction reflex. Do not invent edits outside these recipes.
9
+ A friction that doesn't match one of the six below is **not auto-fixable inline**. Search Memory for the exact skill/friction first; apply a clear overcome and continue. If Memory has no answer, the overcome fails, or the friction repeats, spawn one doctrine fix chip (`greprag fix spawn --type doctrine "<skill friction unit>"`). Use `greprag fix log` only for deliberate audit/design notes. Do not invent edits outside these recipes.
10
10
 
11
11
  ## The two edit primitives (skill-optimize vocabulary)
12
12
 
@@ -52,11 +52,14 @@ project, repo, customer, handle, or unexplained proper noun, search memory for
52
52
  that entity before responding when prior context could affect the answer. Do
53
53
  not do this for generic nouns or for entities fully defined in the current turn.
54
54
 
55
- ## Law 4 Friction fix spawn, at the moment
55
+ For any env, toolchain, worktree, secret, tool, hook, command, correction, or
56
+ repetition friction, search Memory for the exact error/friction plus repo/tool.
57
+ If Memory has a clear overcome, apply it and continue.
56
58
 
57
- Friction you repeated yourself, fought a tool, got corrected twice on the
58
- same thing, rediscovered something already known, hit a setup failure that
59
- wasn't your edit is fixed at the MOMENT it happens:
59
+ ## Law 4 Unresolved friction fix spawn
60
+
61
+ Escalate only when Memory has no answer, the remembered overcome fails, or the
62
+ same friction repeats:
60
63
 
61
64
  greprag fix spawn --type <harness|doctrine|injection|env|code> "<one unit>"
62
65
 
@@ -66,9 +69,9 @@ wasn't your edit — is fixed at the MOMENT it happens:
66
69
  (Claude Code `spawn_task`, Codex `codex_app__create_thread`, opencode
67
70
  bootloader) and send the mission as its first message. Method:
68
71
  `greprag load chip-spawn`.
69
- - **One unit = one chip.** A fix chip exists for exactly one unit of
70
- friction. Adjacent friction gets its own spawn — a chip that absorbs new
71
- friction loses the context each unit needs.
72
+ - **One unresolved unit = one chip.** A fix chip exists for exactly one unit of
73
+ unresolved friction. Adjacent friction gets its own spawn — a chip that
74
+ absorbs new friction loses the context each unit needs.
72
75
  - **Type = durable repair surface.** `harness` fixes hooks/watchers/task
73
76
  dispatch/harness behavior; `doctrine` fixes `greprag load`, bundled skills,
74
77
  and rendered AGENTS/CLAUDE instructions; `injection` fixes recap, Capture,
@@ -86,10 +89,10 @@ wasn't your edit — is fixed at the MOMENT it happens:
86
89
  the operator before touching code. Rarely can a chip design the durable
87
90
  repair alone; the gate is what keeps repairs durable instead of
88
91
  workarounds.
89
- - **Never queue friction for later.** The queue-first reflex (`fix log` →
90
- periodic digestion) is retired; the moment that produced the friction
91
- holds the context the fix needs. `greprag fix log` survives only for
92
- audit trails and design-input notes that are deliberately not chips.
92
+ - **Never queue unresolved friction for later.** The queue-first reflex
93
+ (`fix log` → periodic digestion) is retired; the live context belongs with
94
+ the fix chip. `greprag fix log` survives only for audit trails and
95
+ design-input notes that are deliberately not chips.
93
96
  - Every repair is ROOT-CAUSE: fix the pattern that makes the friction class
94
97
  possible, never a guard on today's trigger.
95
98
 
@@ -98,4 +101,4 @@ wasn't your edit — is fixed at the MOMENT it happens:
98
101
  If the operator explains the same thing twice, the explanation belongs in a
99
102
  durable surface — a skill, a load entry, a STATE block, an ADR — not in the
100
103
  conversation. Hand-taught doctrine that stays in chat dies with the session;
101
- that is itself friction (Law 4 applies).
104
+ that is itself unresolved friction (Law 4 applies).