greprag 5.46.0 → 5.48.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/commands/arm-reminder.d.ts +25 -0
  2. package/dist/commands/arm-reminder.js +65 -0
  3. package/dist/commands/arm-reminder.js.map +1 -0
  4. package/dist/commands/assistant-reminder.d.ts +9 -0
  5. package/dist/commands/assistant-reminder.js +20 -0
  6. package/dist/commands/assistant-reminder.js.map +1 -0
  7. package/dist/commands/checkpoint-reminder.d.ts +14 -0
  8. package/dist/commands/checkpoint-reminder.js +50 -0
  9. package/dist/commands/checkpoint-reminder.js.map +1 -0
  10. package/dist/commands/friction-reminder.d.ts +92 -0
  11. package/dist/commands/friction-reminder.js +147 -0
  12. package/dist/commands/friction-reminder.js.map +1 -0
  13. package/dist/commands/frontdesk-reminder.d.ts +23 -0
  14. package/dist/commands/frontdesk-reminder.js +40 -0
  15. package/dist/commands/frontdesk-reminder.js.map +1 -0
  16. package/dist/commands/inbox-watch-supervisor.d.ts +25 -0
  17. package/dist/commands/inbox-watch-supervisor.js +112 -5
  18. package/dist/commands/inbox-watch-supervisor.js.map +1 -1
  19. package/dist/commands/init.js +31 -0
  20. package/dist/commands/init.js.map +1 -1
  21. package/dist/commands/memory-reflex.d.ts +115 -0
  22. package/dist/commands/memory-reflex.js +243 -0
  23. package/dist/commands/memory-reflex.js.map +1 -0
  24. package/dist/commands/reminder-registry.d.ts +24 -0
  25. package/dist/commands/reminder-registry.js +76 -0
  26. package/dist/commands/reminder-registry.js.map +1 -0
  27. package/dist/commands/reminder-types.d.ts +76 -0
  28. package/dist/commands/reminder-types.js +8 -0
  29. package/dist/commands/reminder-types.js.map +1 -0
  30. package/dist/commands/setup-reminder.d.ts +9 -0
  31. package/dist/commands/setup-reminder.js +20 -0
  32. package/dist/commands/setup-reminder.js.map +1 -0
  33. package/dist/hook.js +472 -120
  34. package/dist/hook.js.map +1 -1
  35. package/dist/opencode-plugin.js +68 -8
  36. package/dist/opencode-plugin.js.map +1 -1
  37. package/package.json +1 -1
  38. package/skill/templates/reflex-chip.md +58 -0
@@ -0,0 +1,25 @@
1
+ /** watcherArm — the REFERENCE reminder-interrupt module (docs/reminder-interrupt.md
2
+ * §Registry spec). The working pattern the whole registry is modeled on: detect the
3
+ * watcher deficiency (unarmed) → fire while down → auto-silence when armed →
4
+ * self-heal (watcher dies → next turn re-detects). Pure over ReminderEnv; the hook
5
+ * supplies `armed` (isLocallyArmed) + `sessionUnread` (this session's own mail).
6
+ * adr: adr/session-id-awareness.md */
7
+ import { ReminderEnv, Detection, ReminderModule } from './reminder-types';
8
+ /** SessionStart announce — the heavy half, loaded once (+ re-fired on compact via the
9
+ * session-id hook). Leads with ARM NOW (not "here's how for later") so the agent arms
10
+ * immediately; states peers ARE reaching this session, and teaches the drop-anytime →
11
+ * MUST re-arm fact that licenses the per-turn reminder. Carries the deferred-Monitor
12
+ * call shape (ToolSearch select:Monitor + required description/timeout_ms) so the arm
13
+ * call succeeds unaided. Do NOT trim the ToolSearch clause — the deferral made it
14
+ * load-bearing (adr/session-id-awareness.md 2026-05-29). adr: adr/session-id-awareness.md */
15
+ export declare function buildArmAnnounce(short: string, ownerPid?: number | null, alias?: string | null, assistant?: boolean): string;
16
+ /** Detect the watcher deficiency. armed → silent (the auto-stop). Unarmed → nag when a
17
+ * peer has actually messaged this session (grounded + TRUE — nag only when
18
+ * sessionUnread>0, so it never decays into a cried-wolf banner), else nudge. */
19
+ export declare function armDetect(env: ReminderEnv): Detection;
20
+ /** Per-turn thin line. nag = a real peer message waits (true by construction); nudge =
21
+ * unarmed with no mail. The heavy how-to-arm lives in the SessionStart announce. */
22
+ export declare function buildArmReminder(d: Detection): string | null;
23
+ /** The reference module. detect = unarmed; announce = the urgent how-to; reminder = the
24
+ * thin line. Wired into the registry container (reminder-registry.ts). */
25
+ export declare const watcherArmModule: ReminderModule;
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ /** watcherArm — the REFERENCE reminder-interrupt module (docs/reminder-interrupt.md
3
+ * §Registry spec). The working pattern the whole registry is modeled on: detect the
4
+ * watcher deficiency (unarmed) → fire while down → auto-silence when armed →
5
+ * self-heal (watcher dies → next turn re-detects). Pure over ReminderEnv; the hook
6
+ * supplies `armed` (isLocallyArmed) + `sessionUnread` (this session's own mail).
7
+ * adr: adr/session-id-awareness.md */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.watcherArmModule = void 0;
10
+ exports.buildArmAnnounce = buildArmAnnounce;
11
+ exports.armDetect = armDetect;
12
+ exports.buildArmReminder = buildArmReminder;
13
+ const session_id_1 = require("../session-id");
14
+ /** SessionStart announce — the heavy half, loaded once (+ re-fired on compact via the
15
+ * session-id hook). Leads with ARM NOW (not "here's how for later") so the agent arms
16
+ * immediately; states peers ARE reaching this session, and teaches the drop-anytime →
17
+ * MUST re-arm fact that licenses the per-turn reminder. Carries the deferred-Monitor
18
+ * call shape (ToolSearch select:Monitor + required description/timeout_ms) so the arm
19
+ * call succeeds unaided. Do NOT trim the ToolSearch clause — the deferral made it
20
+ * load-bearing (adr/session-id-awareness.md 2026-05-29). adr: adr/session-id-awareness.md */
21
+ function buildArmAnnounce(short, ownerPid, alias = null, assistant = false) {
22
+ const handle = alias || '<handle>';
23
+ return (`[⚠ ARM YOUR INBOX WATCHER NOW. Peers are live in your mesh trying to coordinate `
24
+ + `with this session, and the operator can DM you — but they reach you ONLY while armed. `
25
+ + `Unarmed, you're invisible and their messages silently pile up. The watcher can DROP `
26
+ + `ANYTIME (crash / reload / OOM) — every turn you're flagged unarmed you MUST re-arm, `
27
+ + `or coordination breaks. `
28
+ + `ARM: Monitor is a deferred tool — first load its schema (ToolSearch query `
29
+ + `"select:Monitor"), then call Monitor with persistent:true, timeout_ms:3600000, `
30
+ + `description:"greprag inbox ${short}", and command: `
31
+ + `\`${(0, session_id_1.armMonitorCommand)(short, ownerPid, assistant)}\`. `
32
+ + `Then drain what's waiting: \`greprag inbox\`. `
33
+ + `Reply to you: ${handle}@greprag.com/${short}.]`);
34
+ }
35
+ /** Detect the watcher deficiency. armed → silent (the auto-stop). Unarmed → nag when a
36
+ * peer has actually messaged this session (grounded + TRUE — nag only when
37
+ * sessionUnread>0, so it never decays into a cried-wolf banner), else nudge. */
38
+ function armDetect(env) {
39
+ if (env.armed)
40
+ return { tier: 'silent' };
41
+ const unread = env.sessionUnread > 0 ? env.sessionUnread : 0;
42
+ return unread > 0
43
+ ? { tier: 'nag', detail: { unread } }
44
+ : { tier: 'nudge', detail: { unread: 0 } };
45
+ }
46
+ /** Per-turn thin line. nag = a real peer message waits (true by construction); nudge =
47
+ * unarmed with no mail. The heavy how-to-arm lives in the SessionStart announce. */
48
+ function buildArmReminder(d) {
49
+ if (d.tier === 'silent')
50
+ return null;
51
+ const unread = Number((d.detail && d.detail.unread) || 0);
52
+ if (unread > 0) {
53
+ return `⚠ A peer messaged you (${unread} waiting) & you're UNARMED — arm + answer NOW: greprag inbox.`;
54
+ }
55
+ return `⚠ Watcher down — peers can't reach you. RE-ARM now.`;
56
+ }
57
+ /** The reference module. detect = unarmed; announce = the urgent how-to; reminder = the
58
+ * thin line. Wired into the registry container (reminder-registry.ts). */
59
+ exports.watcherArmModule = {
60
+ id: 'watcher-arm',
61
+ detect: armDetect,
62
+ announce: (env) => buildArmAnnounce(env.short, env.ownerPid, env.alias ?? null, env.assistant ?? false),
63
+ reminder: (d) => buildArmReminder(d),
64
+ };
65
+ //# sourceMappingURL=arm-reminder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"arm-reminder.js","sourceRoot":"","sources":["../../src/commands/arm-reminder.ts"],"names":[],"mappings":";AAAA;;;;;uCAKuC;;;AAYvC,4CAiBC;AAKD,8BAMC;AAID,4CAOC;AAhDD,8CAAkD;AAElD;;;;;;8FAM8F;AAC9F,SAAgB,gBAAgB,CAC9B,KAAa,EAAE,QAAwB,EAAE,QAAuB,IAAI,EAAE,SAAS,GAAG,KAAK;IAEvF,MAAM,MAAM,GAAG,KAAK,IAAI,UAAU,CAAC;IACnC,OAAO,CACL,kFAAkF;UAChF,wFAAwF;UACxF,sFAAsF;UACtF,sFAAsF;UACtF,0BAA0B;UAC1B,4EAA4E;UAC5E,iFAAiF;UACjF,8BAA8B,KAAK,kBAAkB;UACrD,KAAK,IAAA,8BAAiB,EAAC,KAAK,EAAE,QAAQ,EAAE,SAAS,CAAC,MAAM;UACxD,gDAAgD;UAChD,iBAAiB,MAAM,gBAAgB,KAAK,IAAI,CACnD,CAAC;AACJ,CAAC;AAED;;iFAEiF;AACjF,SAAgB,SAAS,CAAC,GAAgB;IACxC,IAAI,GAAG,CAAC,KAAK;QAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IACzC,MAAM,MAAM,GAAG,GAAG,CAAC,aAAa,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC,CAAC,CAAC,CAAC;IAC7D,OAAO,MAAM,GAAG,CAAC;QACf,CAAC,CAAC,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,EAAE;QACrC,CAAC,CAAC,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,EAAE,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;AAC/C,CAAC;AAED;qFACqF;AACrF,SAAgB,gBAAgB,CAAC,CAAY;IAC3C,IAAI,CAAC,CAAC,IAAI,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IACrC,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,IAAK,CAAC,CAAC,MAA8B,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IACnF,IAAI,MAAM,GAAG,CAAC,EAAE,CAAC;QACf,OAAO,0BAA0B,MAAM,+DAA+D,CAAC;IACzG,CAAC;IACD,OAAO,qDAAqD,CAAC;AAC/D,CAAC;AAED;2EAC2E;AAC9D,QAAA,gBAAgB,GAAmB;IAC9C,EAAE,EAAE,aAAa;IACjB,MAAM,EAAE,SAAS;IACjB,QAAQ,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,gBAAgB,CAAC,GAAG,CAAC,KAAK,EAAE,GAAG,CAAC,QAAQ,EAAE,GAAG,CAAC,KAAK,IAAI,IAAI,EAAE,GAAG,CAAC,SAAS,IAAI,KAAK,CAAC;IACvG,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,gBAAgB,CAAC,CAAC,CAAC;CACrC,CAAC"}
@@ -0,0 +1,9 @@
1
+ /** assistant-doctrine — the flagged-assistant-project doctrine auto-load as a
2
+ * reminder-interrupt module (docs/reminder-interrupt.md §Registry spec). announce-ONLY
3
+ * + pass-through: the doctrine text is PRECOMPUTED by the hook (buildAssistantDoctrineContext
4
+ * reads doctrine files — I/O that belongs in the hook). Fires ONLY for the assistant
5
+ * project — env.assistantDoctrine is null everywhere else, so a normal project sees
6
+ * nothing. adr: adr/assistant-role.md */
7
+ import { ReminderEnv, Detection, ReminderModule } from './reminder-types';
8
+ export declare function assistantDetect(env: ReminderEnv): Detection;
9
+ export declare const assistantDoctrineModule: ReminderModule;
@@ -0,0 +1,20 @@
1
+ "use strict";
2
+ /** assistant-doctrine — the flagged-assistant-project doctrine auto-load as a
3
+ * reminder-interrupt module (docs/reminder-interrupt.md §Registry spec). announce-ONLY
4
+ * + pass-through: the doctrine text is PRECOMPUTED by the hook (buildAssistantDoctrineContext
5
+ * reads doctrine files — I/O that belongs in the hook). Fires ONLY for the assistant
6
+ * project — env.assistantDoctrine is null everywhere else, so a normal project sees
7
+ * nothing. adr: adr/assistant-role.md */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.assistantDoctrineModule = void 0;
10
+ exports.assistantDetect = assistantDetect;
11
+ function assistantDetect(env) {
12
+ return env.assistantDoctrine ? { tier: 'ambient' } : { tier: 'silent' };
13
+ }
14
+ exports.assistantDoctrineModule = {
15
+ id: 'assistant-doctrine',
16
+ detect: assistantDetect,
17
+ announce: (env) => env.assistantDoctrine ?? null,
18
+ reminder: () => null,
19
+ };
20
+ //# sourceMappingURL=assistant-reminder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"assistant-reminder.js","sourceRoot":"","sources":["../../src/commands/assistant-reminder.ts"],"names":[],"mappings":";AAAA;;;;;0CAK0C;;;AAI1C,0CAEC;AAFD,SAAgB,eAAe,CAAC,GAAgB;IAC9C,OAAO,GAAG,CAAC,iBAAiB,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AAC1E,CAAC;AAEY,QAAA,uBAAuB,GAAmB;IACrD,EAAE,EAAE,oBAAoB;IACxB,MAAM,EAAE,eAAe;IACvB,QAAQ,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,iBAAiB,IAAI,IAAI;IAChD,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI;CACrB,CAAC"}
@@ -0,0 +1,14 @@
1
+ /** checkpoint — the open-checkpoints SessionStart landmark as a reminder-interrupt
2
+ * module (docs/reminder-interrupt.md §Registry spec). announce-ONLY: checkpoints are
3
+ * operator-triggered landmarks surfaced once at session start, never per-turn traffic
4
+ * (reminder → null). `detect` gates on the hook-supplied list so the announce auto-
5
+ * silences when nothing is open. The PURE render (list → hint block) lives here — the
6
+ * hook owns only the fetch (I/O), keeping the module unit-testable. */
7
+ import { ReminderEnv, Detection, ReminderModule, OpenCheckpointSlim } from './reminder-types';
8
+ /** Format the open-checkpoints hint block: 3-most-recent rows + a single overflow line
9
+ * when there are more. Returns null on zero (the announce is then skipped). */
10
+ export declare function renderCheckpointHint(open: OpenCheckpointSlim[], projectName: string): string | null;
11
+ /** Open checkpoints present → ambient announce; none (or per-turn, where the list is
12
+ * absent) → silent. */
13
+ export declare function checkpointDetect(env: ReminderEnv): Detection;
14
+ export declare const checkpointModule: ReminderModule;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ /** checkpoint — the open-checkpoints SessionStart landmark as a reminder-interrupt
3
+ * module (docs/reminder-interrupt.md §Registry spec). announce-ONLY: checkpoints are
4
+ * operator-triggered landmarks surfaced once at session start, never per-turn traffic
5
+ * (reminder → null). `detect` gates on the hook-supplied list so the announce auto-
6
+ * silences when nothing is open. The PURE render (list → hint block) lives here — the
7
+ * hook owns only the fetch (I/O), keeping the module unit-testable. */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.checkpointModule = void 0;
10
+ exports.renderCheckpointHint = renderCheckpointHint;
11
+ exports.checkpointDetect = checkpointDetect;
12
+ /** Render a YYYY-MM-DD date in UTC for the session-start hint. */
13
+ function fmtCheckpointDate(iso) {
14
+ return iso.slice(0, 10);
15
+ }
16
+ /** Format the open-checkpoints hint block: 3-most-recent rows + a single overflow line
17
+ * when there are more. Returns null on zero (the announce is then skipped). */
18
+ function renderCheckpointHint(open, projectName) {
19
+ if (open.length === 0)
20
+ return null;
21
+ // Server returns DESC by created_at; take the 3 most recent.
22
+ const top = open.slice(0, 3);
23
+ const overflow = open.length - top.length;
24
+ const lines = [];
25
+ lines.push(`[${open.length} checkpoint${open.length === 1 ? '' : 's'} open in ${projectName}:`);
26
+ for (const cp of top) {
27
+ lines.push(` · ${cp.nodeId} ${cp.title} (since ${fmtCheckpointDate(cp.createdAt)})`);
28
+ }
29
+ if (overflow > 0) {
30
+ lines.push(` · ${overflow} more — greprag checkpoint list]`);
31
+ }
32
+ else {
33
+ lines.push(` View one: greprag checkpoint show <id>]`);
34
+ }
35
+ return lines.join('\n');
36
+ }
37
+ /** Open checkpoints present → ambient announce; none (or per-turn, where the list is
38
+ * absent) → silent. */
39
+ function checkpointDetect(env) {
40
+ return (env.openCheckpoints && env.openCheckpoints.length > 0)
41
+ ? { tier: 'ambient' }
42
+ : { tier: 'silent' };
43
+ }
44
+ exports.checkpointModule = {
45
+ id: 'checkpoint',
46
+ detect: checkpointDetect,
47
+ announce: (env) => renderCheckpointHint(env.openCheckpoints ?? [], env.projectName ?? ''),
48
+ reminder: () => null,
49
+ };
50
+ //# sourceMappingURL=checkpoint-reminder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"checkpoint-reminder.js","sourceRoot":"","sources":["../../src/commands/checkpoint-reminder.ts"],"names":[],"mappings":";AAAA;;;;;wEAKwE;;;AAWxE,oDAgBC;AAID,4CAIC;AA/BD,kEAAkE;AAClE,SAAS,iBAAiB,CAAC,GAAW;IACpC,OAAO,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;AAC1B,CAAC;AAED;gFACgF;AAChF,SAAgB,oBAAoB,CAAC,IAA0B,EAAE,WAAmB;IAClF,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IACnC,6DAA6D;IAC7D,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC;IAC7B,MAAM,QAAQ,GAAG,IAAI,CAAC,MAAM,GAAG,GAAG,CAAC,MAAM,CAAC;IAC1C,MAAM,KAAK,GAAa,EAAE,CAAC;IAC3B,KAAK,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,MAAM,cAAc,IAAI,CAAC,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,YAAY,WAAW,GAAG,CAAC,CAAC;IAChG,KAAK,MAAM,EAAE,IAAI,GAAG,EAAE,CAAC;QACrB,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,MAAM,KAAK,EAAE,CAAC,KAAK,WAAW,iBAAiB,CAAC,EAAE,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;IACzF,CAAC;IACD,IAAI,QAAQ,GAAG,CAAC,EAAE,CAAC;QACjB,KAAK,CAAC,IAAI,CAAC,OAAO,QAAQ,kCAAkC,CAAC,CAAC;IAChE,CAAC;SAAM,CAAC;QACN,KAAK,CAAC,IAAI,CAAC,0CAA0C,CAAC,CAAC;IACzD,CAAC;IACD,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AAC1B,CAAC;AAED;wBACwB;AACxB,SAAgB,gBAAgB,CAAC,GAAgB;IAC/C,OAAO,CAAC,GAAG,CAAC,eAAe,IAAI,GAAG,CAAC,eAAe,CAAC,MAAM,GAAG,CAAC,CAAC;QAC5D,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE;QACrB,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AACzB,CAAC;AAEY,QAAA,gBAAgB,GAAmB;IAC9C,EAAE,EAAE,YAAY;IAChB,MAAM,EAAE,gBAAgB;IACxB,QAAQ,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,oBAAoB,CAAC,GAAG,CAAC,eAAe,IAAI,EAAE,EAAE,GAAG,CAAC,WAAW,IAAI,EAAE,CAAC;IACzF,QAAQ,EAAE,GAAG,EAAE,CAAC,IAAI;CACrB,CAAC"}
@@ -0,0 +1,92 @@
1
+ /** friction-reminder — the active Mechanic as a reminder-interrupt registry module
2
+ * (docs/reminder-interrupt.md §Registry spec). The GRADED sibling of the watcher-arm
3
+ * reference module: its detector is stress-gated (silent at calm, escalating with the
4
+ * live stress level) rather than a binary resolvable deficiency.
5
+ *
6
+ * A reminder-interrupt MODULE is `{ id, detect, announce, reminder }` (reminder-types.ts):
7
+ * `detect(env)` reads the live state → a tier (`silent` = nothing to fix); a SessionStart
8
+ * `announce` loads the schema ONCE; a per-turn `reminder` fires a thin, grounded pointer
9
+ * back at it. The no-orphan invariant requires the PAIR. The container (reminder-registry.ts)
10
+ * wires every module; the hook (hook.ts) assembles the env and emits — see `injectReminders`.
11
+ *
12
+ * PURE: imports only the stress constants; returns strings + a tier + a tally. No fs, no
13
+ * network, no clock — the hook does ALL I/O. Keeping detect/tier a pure function is what
14
+ * makes the detect→silent transition (the auto-stop) unit-testable (the regression-lock). */
15
+ import { Tier, ReminderModule } from './reminder-types';
16
+ /** The firing-tier ladder (docs/reminder-interrupt.md §Firing tiers). `silent`
17
+ * is the off rung; the other three escalate wording intensity with the cost of
18
+ * inaction: ambient = soft pointer, nudge = imperative + reason, nag = LOUD
19
+ * forced-decision. */
20
+ export type FrictionTier = Tier;
21
+ /** Ambient (calm-state) cadence: one soft pointer every N turns. The low-rate
22
+ * trickle for a capability with no live bad-state to gate on. */
23
+ export declare const AMBIENT_EVERY_N = 5;
24
+ /** Decide the friction reminder's firing tier from the live state bag — PURE so
25
+ * the ladder is unit-testable (the regression-lock rung). DYNAMIC: urgency tracks
26
+ * the live stress level (docs/stress-trigger.md), not a hand-set knob.
27
+ *
28
+ * stress HIGH (2) → nag — LOUD forced-decision; fires while the live bad
29
+ * state holds and goes silent the instant it flips.
30
+ * stress ELEVATED (1) → nudge — imperative + reason, while elevated.
31
+ * stress CALM (0) → ambient every Nth turn (turnCount % AMBIENT_EVERY_N),
32
+ * else silent.
33
+ *
34
+ * Defensive: non-finite inputs collapse to 0 (calm / turn 0 → silent), so a
35
+ * missing or corrupt state bag can never escalate — fail-quiet by construction. */
36
+ export declare function frictionReminderTier(turnCount: number, stress: number): FrictionTier;
37
+ /** SessionStart announce — the mechanic-launch schema, loaded ONCE. The heavy
38
+ * half of the pair: it (1) TEACHES the method the per-turn reminder triggers, so
39
+ * the reminder stays a thin pointer, and (2) PRIMES adherence — the agent acts on
40
+ * a reminder for a capability it was *taught*, not a cold mid-session imperative
41
+ * (the no-orphan invariant, docs/reminder-interrupt.md). Written for an AGENT under
42
+ * token pressure per the doc's authoring rules: action-first, reason+method
43
+ * bundled, directive-framed, token-minimal. ~150 tokens; amortized over the
44
+ * session. */
45
+ export declare function buildMechanicAnnounce(): string;
46
+ /** Per-turn reminder line for a given tier — the thin pointer back at the announce
47
+ * schema; null on `silent`. Wording intensity scales with the tier (the doc):
48
+ * ambient = soft pointer, nudge = imperative + reason, nag = LOUD forced-decision.
49
+ * Every non-silent line is action-first, bundles reason+method, and ends on a
50
+ * forced binary (act → spawn / clear → continue) so it CONVERTS instead of becoming
51
+ * wallpaper (the fix-`16a29a8b` banner-blindness failure mode). Grounded to the
52
+ * live signal where there is one (nudge/nag name the stress). The act-now directive
53
+ * envelope (authoring rule #4) is applied by the hook, not here — this module owns
54
+ * the wording, the caller owns the framing. */
55
+ export declare function buildFrictionReminder(tier: FrictionTier): string | null;
56
+ /** Persisted fire-counter (docs/reminder-interrupt.md §Measurement). The
57
+ * DENOMINATOR of the adherence ratio: how many friction reminders were shown, by
58
+ * tier. The numerator (reflex chips actually spawned after a reminder) is the next
59
+ * measurement increment — see `reflexSpawns` (stamped by the spawn path when it
60
+ * lands). Read the file to compute the fire rate: `reflexSpawns / total`. */
61
+ export interface FrictionStats {
62
+ /** Total non-silent reminders shown. */
63
+ total: number;
64
+ /** Per-tier breakdown of `total`. */
65
+ byTier: {
66
+ ambient: number;
67
+ nudge: number;
68
+ nag: number;
69
+ };
70
+ /** Reflex chips observed spawned after a reminder (the adherence numerator).
71
+ * Bumped by the spawn path; reminders never touch it. */
72
+ reflexSpawns: number;
73
+ firstFiredAt?: string;
74
+ lastFiredAt?: string;
75
+ /** turnCount at the most recent fire — lets the reader compute a per-N-turn rate. */
76
+ lastTurnCount?: number;
77
+ }
78
+ /** Tally one reminder fire — PURE (the hook supplies the clock via `nowIso`, so
79
+ * this stays testable and resume-safe). A `silent` tier is NOT a fire: it returns
80
+ * the prior stats unchanged, so the denominator only ever counts shown reminders. */
81
+ export declare function tallyFrictionFire(prior: FrictionStats | null, tier: FrictionTier, turnCount: number, nowIso: string): FrictionStats;
82
+ /** Record an observed reflex-chip spawn — PURE; the adherence numerator. The spawn
83
+ * path calls this when it sees a chip preloaded from the reflex-chip template land
84
+ * after a reminder. Never decrements; idempotency is the caller's concern. */
85
+ export declare function tallyReflexSpawn(prior: FrictionStats | null): FrictionStats;
86
+ /** The active Mechanic, recast onto the detector model (docs/reminder-interrupt.md
87
+ * §Registry spec). `detect` is stress-gated — the friction signal IS the live
88
+ * deficiency; `announce` teaches the reflex-chip schema once; `reminder` is the thin
89
+ * per-turn line. Wired through the container (reminder-registry.ts). Unlike the
90
+ * watcher-arm reference module (binary, auto-silences when armed), this one is
91
+ * GRADED — silent at calm, escalating with the live stress level. */
92
+ export declare const mechanicFrictionModule: ReminderModule;
@@ -0,0 +1,147 @@
1
+ "use strict";
2
+ /** friction-reminder — the active Mechanic as a reminder-interrupt registry module
3
+ * (docs/reminder-interrupt.md §Registry spec). The GRADED sibling of the watcher-arm
4
+ * reference module: its detector is stress-gated (silent at calm, escalating with the
5
+ * live stress level) rather than a binary resolvable deficiency.
6
+ *
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`.
12
+ *
13
+ * PURE: imports only the stress constants; returns strings + a tier + a tally. No fs, no
14
+ * network, no clock — the hook does ALL I/O. Keeping detect/tier a pure function is what
15
+ * makes the detect→silent transition (the auto-stop) unit-testable (the regression-lock). */
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ exports.mechanicFrictionModule = exports.AMBIENT_EVERY_N = void 0;
18
+ exports.frictionReminderTier = frictionReminderTier;
19
+ exports.buildMechanicAnnounce = buildMechanicAnnounce;
20
+ exports.buildFrictionReminder = buildFrictionReminder;
21
+ exports.tallyFrictionFire = tallyFrictionFire;
22
+ exports.tallyReflexSpawn = tallyReflexSpawn;
23
+ const state_trigger_1 = require("./state-trigger");
24
+ /** Ambient (calm-state) cadence: one soft pointer every N turns. The low-rate
25
+ * trickle for a capability with no live bad-state to gate on. */
26
+ exports.AMBIENT_EVERY_N = 5;
27
+ /** Decide the friction reminder's firing tier from the live state bag — PURE so
28
+ * the ladder is unit-testable (the regression-lock rung). DYNAMIC: urgency tracks
29
+ * the live stress level (docs/stress-trigger.md), not a hand-set knob.
30
+ *
31
+ * stress HIGH (2) → nag — LOUD forced-decision; fires while the live bad
32
+ * state holds and goes silent the instant it flips.
33
+ * stress ELEVATED (1) → nudge — imperative + reason, while elevated.
34
+ * stress CALM (0) → ambient every Nth turn (turnCount % AMBIENT_EVERY_N),
35
+ * else silent.
36
+ *
37
+ * Defensive: non-finite inputs collapse to 0 (calm / turn 0 → silent), so a
38
+ * missing or corrupt state bag can never escalate — fail-quiet by construction. */
39
+ function frictionReminderTier(turnCount, stress) {
40
+ const s = Number.isFinite(stress) ? stress : 0;
41
+ const t = Number.isFinite(turnCount) ? turnCount : 0;
42
+ if (s >= state_trigger_1.STRESS_HIGH)
43
+ return 'nag';
44
+ if (s >= state_trigger_1.STRESS_ELEVATED)
45
+ return 'nudge';
46
+ if (t >= 1 && t % exports.AMBIENT_EVERY_N === 0)
47
+ return 'ambient';
48
+ return 'silent';
49
+ }
50
+ // ---------- Authored text (announce + per-turn reminder) ---------------------
51
+ /** The HOW string — the method every tier points at. Kept in one place so the
52
+ * announce and the per-turn line can never drift on the spawn mechanic. */
53
+ const SPAWN_METHOD = 'spawn a fire-and-forget reflex chip (spawn_task → packages/cli/skill/templates/reflex-chip.md)';
54
+ /** SessionStart announce — the mechanic-launch schema, loaded ONCE. The heavy
55
+ * half of the pair: it (1) TEACHES the method the per-turn reminder triggers, so
56
+ * the reminder stays a thin pointer, and (2) PRIMES adherence — the agent acts on
57
+ * a reminder for a capability it was *taught*, not a cold mid-session imperative
58
+ * (the no-orphan invariant, docs/reminder-interrupt.md). Written for an AGENT under
59
+ * token pressure per the doc's authoring rules: action-first, reason+method
60
+ * bundled, directive-framed, token-minimal. ~150 tokens; amortized over the
61
+ * session. */
62
+ function buildMechanicAnnounce() {
63
+ return [
64
+ '[Active Mechanic — keep the harness healthy IN-SESSION. Friction is a signal to fix now, not to defer.]',
65
+ 'WHEN you hit friction this session — repeated yourself, fought a tool, got corrected ≥2× on the same thing, or rediscovered something already known — fix the PATTERN now; do not push through.',
66
+ `HOW: ${SPAWN_METHOD}. The chip is preloaded with the mechanic tools + live \`greprag mechanic status\`, so it orients instantly, lands a Tier-0 born-shadow repair, and ends — no gate to wait at.`,
67
+ 'MODES: fix (land the repair) · log (`greprag fix log "<obs>"` when it is a mechanism for /mechanic later) · ask-human (the operator must decide).',
68
+ 'WHY now: immediate-fix beats the backlog — the moment that produced the friction still holds the context the fix needs; deferred, that context is gone.',
69
+ 'A per-turn reminder fires LOUD when live friction is detected — act on it, or say "clear" and continue.',
70
+ ].join('\n');
71
+ }
72
+ /** Per-turn reminder line for a given tier — the thin pointer back at the announce
73
+ * schema; null on `silent`. Wording intensity scales with the tier (the doc):
74
+ * ambient = soft pointer, nudge = imperative + reason, nag = LOUD forced-decision.
75
+ * Every non-silent line is action-first, bundles reason+method, and ends on a
76
+ * forced binary (act → spawn / clear → continue) so it CONVERTS instead of becoming
77
+ * wallpaper (the fix-`16a29a8b` banner-blindness failure mode). Grounded to the
78
+ * live signal where there is one (nudge/nag name the stress). The act-now directive
79
+ * envelope (authoring rule #4) is applied by the hook, not here — this module owns
80
+ * the wording, the caller owns the framing. */
81
+ function buildFrictionReminder(tier) {
82
+ switch (tier) {
83
+ case 'nag':
84
+ return `⚠ FRICTION HIGH (live signal: repetition / errors / churn) — STOP pushing through; fix the PATTERN now → ${SPAWN_METHOD}. No friction? say "clear" + continue.`;
85
+ case 'nudge':
86
+ return `⚠ Friction rising — if you have repeated yourself or fought a tool, fix it now → ${SPAWN_METHOD}. Otherwise say "clear" + continue.`;
87
+ case 'ambient':
88
+ return `💡 Hit friction since the last check (repeated yourself, fought a tool, corrected ≥2×)? → ${SPAWN_METHOD}. If not, say "clear" + continue.`;
89
+ default:
90
+ return null;
91
+ }
92
+ }
93
+ /** Normalize any prior-stats blob (possibly missing/partial/corrupt) into a fully
94
+ * populated FrictionStats. Pure, total. */
95
+ function normalizeStats(prior) {
96
+ const p = (prior && typeof prior === 'object') ? prior : {};
97
+ const bt = (p.byTier && typeof p.byTier === 'object') ? p.byTier : {};
98
+ return {
99
+ total: Number.isFinite(p.total) ? p.total : 0,
100
+ byTier: {
101
+ ambient: Number.isFinite(bt.ambient) ? bt.ambient : 0,
102
+ nudge: Number.isFinite(bt.nudge) ? bt.nudge : 0,
103
+ nag: Number.isFinite(bt.nag) ? bt.nag : 0,
104
+ },
105
+ reflexSpawns: Number.isFinite(p.reflexSpawns) ? p.reflexSpawns : 0,
106
+ firstFiredAt: p.firstFiredAt,
107
+ lastFiredAt: p.lastFiredAt,
108
+ lastTurnCount: p.lastTurnCount,
109
+ };
110
+ }
111
+ /** Tally one reminder fire — PURE (the hook supplies the clock via `nowIso`, so
112
+ * this stays testable and resume-safe). A `silent` tier is NOT a fire: it returns
113
+ * the prior stats unchanged, so the denominator only ever counts shown reminders. */
114
+ function tallyFrictionFire(prior, tier, turnCount, nowIso) {
115
+ const s = normalizeStats(prior);
116
+ if (tier === 'silent')
117
+ return s;
118
+ s.total += 1;
119
+ s.byTier[tier] += 1;
120
+ s.firstFiredAt = s.firstFiredAt || nowIso;
121
+ s.lastFiredAt = nowIso;
122
+ if (Number.isFinite(turnCount))
123
+ s.lastTurnCount = turnCount;
124
+ return s;
125
+ }
126
+ /** Record an observed reflex-chip spawn — PURE; the adherence numerator. The spawn
127
+ * path calls this when it sees a chip preloaded from the reflex-chip template land
128
+ * after a reminder. Never decrements; idempotency is the caller's concern. */
129
+ function tallyReflexSpawn(prior) {
130
+ const s = normalizeStats(prior);
131
+ s.reflexSpawns += 1;
132
+ return s;
133
+ }
134
+ // ---------- Registry module --------------------------------------------------
135
+ /** The active Mechanic, recast onto the detector model (docs/reminder-interrupt.md
136
+ * §Registry spec). `detect` is stress-gated — the friction signal IS the live
137
+ * deficiency; `announce` teaches the reflex-chip schema once; `reminder` is the thin
138
+ * per-turn line. Wired through the container (reminder-registry.ts). Unlike the
139
+ * watcher-arm reference module (binary, auto-silences when armed), this one is
140
+ * GRADED — silent at calm, escalating with the live stress level. */
141
+ exports.mechanicFrictionModule = {
142
+ id: 'mechanic-friction',
143
+ detect: (env) => ({ tier: frictionReminderTier(env.turnCount, env.stress) }),
144
+ announce: () => buildMechanicAnnounce(),
145
+ reminder: (d) => buildFrictionReminder(d.tier),
146
+ };
147
+ //# sourceMappingURL=friction-reminder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"friction-reminder.js","sourceRoot":"","sources":["../../src/commands/friction-reminder.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /** front-desk — the shared tenant front-desk mailbox as a reminder-interrupt module
2
+ * (docs/reminder-interrupt.md §Registry spec). The module that uses BOTH halves of the
3
+ * pair: a SessionStart `announce` (discreet count of cold opens + inbound email waiting
4
+ * at the shared desk) AND a per-turn `reminder` (envelope-only "you've got mail from X"
5
+ * notice). This is the "announce + reminder often utilize each other" case the registry
6
+ * was consolidated for — one module, one mailbox, two surfaces.
7
+ *
8
+ * PURE: the hook owns the count fetch (frontDeskUnread) AND the notice's network + per-
9
+ * machine dedup (frontDeskNotice), passing both via env; the module only formats the
10
+ * count and routes the precomputed notice.
11
+ *
12
+ * Privacy — front-desk is the SHARED tenant mailbox (cold opens + inbound email addressed
13
+ * to nobody's session), so its count leaks nothing private. This is NOT the v5.6.1
14
+ * project-wide eavesdrop vector that betrayed a sibling session's session-targeted DMs.
15
+ * adr: adr/address-grammar.md (2026-06-10), docs/front-desk-switchboard.md §5 */
16
+ import { ReminderEnv, Detection, ReminderModule } from './reminder-types';
17
+ /** SessionStart count hint — the discreet door (count + how to read), never content.
18
+ * Null at zero so the announce is skipped. */
19
+ export declare function buildFrontDeskAnnounce(unread: number): string | null;
20
+ /** Either surface has something → ambient (soft, never a forced-decision directive); else
21
+ * silent. SessionStart sets frontDeskUnread; per-turn sets frontDeskNotice. */
22
+ export declare function frontDeskDetect(env: ReminderEnv): Detection;
23
+ export declare const frontDeskModule: ReminderModule;
@@ -0,0 +1,40 @@
1
+ "use strict";
2
+ /** front-desk — the shared tenant front-desk mailbox as a reminder-interrupt module
3
+ * (docs/reminder-interrupt.md §Registry spec). The module that uses BOTH halves of the
4
+ * pair: a SessionStart `announce` (discreet count of cold opens + inbound email waiting
5
+ * at the shared desk) AND a per-turn `reminder` (envelope-only "you've got mail from X"
6
+ * notice). This is the "announce + reminder often utilize each other" case the registry
7
+ * was consolidated for — one module, one mailbox, two surfaces.
8
+ *
9
+ * PURE: the hook owns the count fetch (frontDeskUnread) AND the notice's network + per-
10
+ * machine dedup (frontDeskNotice), passing both via env; the module only formats the
11
+ * count and routes the precomputed notice.
12
+ *
13
+ * Privacy — front-desk is the SHARED tenant mailbox (cold opens + inbound email addressed
14
+ * to nobody's session), so its count leaks nothing private. This is NOT the v5.6.1
15
+ * project-wide eavesdrop vector that betrayed a sibling session's session-targeted DMs.
16
+ * adr: adr/address-grammar.md (2026-06-10), docs/front-desk-switchboard.md §5 */
17
+ Object.defineProperty(exports, "__esModule", { value: true });
18
+ exports.frontDeskModule = void 0;
19
+ exports.buildFrontDeskAnnounce = buildFrontDeskAnnounce;
20
+ exports.frontDeskDetect = frontDeskDetect;
21
+ /** SessionStart count hint — the discreet door (count + how to read), never content.
22
+ * Null at zero so the announce is skipped. */
23
+ function buildFrontDeskAnnounce(unread) {
24
+ if (!unread || unread <= 0)
25
+ return null;
26
+ return `[📬 ${unread} message${unread === 1 ? '' : 's'} at the front desk — \`greprag inbox --front-desk\` to read]`;
27
+ }
28
+ /** Either surface has something → ambient (soft, never a forced-decision directive); else
29
+ * silent. SessionStart sets frontDeskUnread; per-turn sets frontDeskNotice. */
30
+ function frontDeskDetect(env) {
31
+ const unread = env.frontDeskUnread ?? 0;
32
+ return (unread > 0 || env.frontDeskNotice) ? { tier: 'ambient' } : { tier: 'silent' };
33
+ }
34
+ exports.frontDeskModule = {
35
+ id: 'front-desk',
36
+ detect: frontDeskDetect,
37
+ announce: (env) => buildFrontDeskAnnounce(env.frontDeskUnread ?? 0),
38
+ reminder: (_d, env) => env.frontDeskNotice ?? null,
39
+ };
40
+ //# sourceMappingURL=frontdesk-reminder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frontdesk-reminder.js","sourceRoot":"","sources":["../../src/commands/frontdesk-reminder.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;kFAckF;;;AAMlF,wDAGC;AAID,0CAGC;AAZD;+CAC+C;AAC/C,SAAgB,sBAAsB,CAAC,MAAc;IACnD,IAAI,CAAC,MAAM,IAAI,MAAM,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACxC,OAAO,OAAO,MAAM,WAAW,MAAM,KAAK,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,GAAG,8DAA8D,CAAC;AACvH,CAAC;AAED;gFACgF;AAChF,SAAgB,eAAe,CAAC,GAAgB;IAC9C,MAAM,MAAM,GAAG,GAAG,CAAC,eAAe,IAAI,CAAC,CAAC;IACxC,OAAO,CAAC,MAAM,GAAG,CAAC,IAAI,GAAG,CAAC,eAAe,CAAC,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,SAAS,EAAE,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;AACxF,CAAC;AAEY,QAAA,eAAe,GAAmB;IAC7C,EAAE,EAAE,YAAY;IAChB,MAAM,EAAE,eAAe;IACvB,QAAQ,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,sBAAsB,CAAC,GAAG,CAAC,eAAe,IAAI,CAAC,CAAC;IACnE,QAAQ,EAAE,CAAC,EAAE,EAAE,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,eAAe,IAAI,IAAI;CACnD,CAAC"}
@@ -35,7 +35,32 @@ export declare const FATAL_EXIT_CODE = 64;
35
35
  * so it respawns; SIGINT/SIGTERM skip these and return via the
36
36
  * normal abort path. */
37
37
  export declare function installLastResortHandlers(): void;
38
+ type ChildExit = {
39
+ code: number | null;
40
+ signal: NodeJS.Signals | null;
41
+ err: Error | null;
42
+ };
43
+ /** Human-readable cause for a child death + whether it was an OS-level native
44
+ * crash. Turns "exit 3221226091" into "native crash 0xC000026B (DLL init failed —
45
+ * out of commit memory / window station gone)" so both the respawn log and the
46
+ * stand-down diagnostic are actionable instead of opaque digits. */
47
+ export declare function describeChildExit(exit: ChildExit): {
48
+ reason: string;
49
+ native: boolean;
50
+ };
51
+ /** Pure crash-loop decision (testable, mirrors capSurplusVerdict/gcVerdict).
52
+ * Given how long the just-exited child lived (`liveMs`) and the running
53
+ * fast-death `streak`, return the updated streak and whether the supervisor
54
+ * should STAND DOWN instead of respawning. A child that lived at least
55
+ * `lifetimeMs` did real work → streak resets to 0. Otherwise the streak grows
56
+ * and trips once it reaches `maxDeaths`. This is the whole breaker rule — no
57
+ * process state, just a count + a lifetime floor. */
58
+ export declare function crashLoopVerdict(liveMs: number, streak: number, lifetimeMs: number, maxDeaths: number): {
59
+ streak: number;
60
+ standDown: boolean;
61
+ };
38
62
  /** Parent supervisor. Spawns the watch loop as a Node child, forwards
39
63
  * stdio so Monitor sees each printed line as a notification verbatim,
40
64
  * and respawns on any non-clean exit. */
41
65
  export declare function runSupervisor(): Promise<void>;
66
+ export {};