greprag 5.49.8 → 5.49.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/commands/collision-reminder.d.ts +14 -0
- package/dist/commands/collision-reminder.js +40 -0
- package/dist/commands/collision-reminder.js.map +1 -0
- package/dist/commands/coordinate-gate.d.ts +12 -6
- package/dist/commands/coordinate-gate.js +24 -10
- package/dist/commands/coordinate-gate.js.map +1 -1
- package/dist/commands/friction-reminder.d.ts +22 -44
- package/dist/commands/friction-reminder.js +45 -63
- package/dist/commands/friction-reminder.js.map +1 -1
- package/dist/commands/init.js +7 -14
- package/dist/commands/init.js.map +1 -1
- package/dist/commands/load-primer-reminder.d.ts +28 -0
- package/dist/commands/load-primer-reminder.js +51 -0
- package/dist/commands/load-primer-reminder.js.map +1 -0
- package/dist/commands/load.d.ts +15 -0
- package/dist/commands/load.js +112 -0
- package/dist/commands/load.js.map +1 -0
- package/dist/commands/memory-reflex.d.ts +17 -104
- package/dist/commands/memory-reflex.js +23 -215
- package/dist/commands/memory-reflex.js.map +1 -1
- package/dist/commands/reminder-registry.d.ts +12 -1
- package/dist/commands/reminder-registry.js +46 -4
- package/dist/commands/reminder-registry.js.map +1 -1
- package/dist/commands/reminder-types.d.ts +28 -0
- package/dist/commands/version-reminder.d.ts +15 -0
- package/dist/commands/version-reminder.js +34 -0
- package/dist/commands/version-reminder.js.map +1 -0
- package/dist/hook.js +111 -219
- package/dist/hook.js.map +1 -1
- package/dist/index.js +6 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/skill/templates/chip-leader.md +188 -0
- package/skill/templates/chip-spawn.md +4 -4
|
@@ -1,226 +1,34 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
/** memory-
|
|
3
|
-
*
|
|
4
|
-
*
|
|
2
|
+
/** memory-primer — the SessionStart announce that teaches greprag's episodic memory
|
|
3
|
+
* as a capability the agent operates on its OWN judgment (`greprag memory search` /
|
|
4
|
+
* `recap`). A PRIMER (full doctrine inline), nothing else.
|
|
5
5
|
*
|
|
6
|
-
*
|
|
7
|
-
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
* result is its own precision filter and the reflex never becomes wallpaper (the
|
|
16
|
-
* banner-blindness failure mode, fix `16a29a8b`). PURE module: the hook owns the search
|
|
17
|
-
* (I/O) + frames the hit via `buildMemoryInjection`, then routes the string through env —
|
|
18
|
-
* exactly the front-desk module's shape.
|
|
19
|
-
*
|
|
20
|
-
* EXCESSIVENESS is the watched risk (operator's call 2026-06-20): two filters bound it —
|
|
21
|
-
* high-precision keyword phrases (low fire rate) + the confidence gate (low inject rate) —
|
|
22
|
-
* and every fire is tallied (`MemoryReflexStats`) so /mechanic can monitor the rate and the
|
|
23
|
-
* inject/fire ratio, and auto-quiet if it turns noisy. adr: adr/memory-reflex.md */
|
|
6
|
+
* HISTORY: this module once also did per-turn AUTO-INJECTION — recall-intent keyword
|
|
7
|
+
* match → inline memory search → surface a hit. That was DROPPED 2026-06-22. Two
|
|
8
|
+
* reasons: (1) it was knowledge injection, NOT a behavioral reminder — a different,
|
|
9
|
+
* undeveloped system that was jammed into the reminder registry for lack of a slot;
|
|
10
|
+
* (2) it collided with the agent's own, better-targeted `greprag memory search` (the
|
|
11
|
+
* auto-search just threw the raw prompt at the index). Same call that retired the
|
|
12
|
+
* `[[recall:]]` self-report marker: prefer a strong primer + agent judgment over a
|
|
13
|
+
* passive auto-surface. If a real "knowledge injection" system is built later, it gets
|
|
14
|
+
* its own home — it is not a reminder. docs/reminder-interrupt.md (the landed map). */
|
|
24
15
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
25
|
-
exports.
|
|
26
|
-
exports.hasRecallIntent = hasRecallIntent;
|
|
27
|
-
exports.selectGatedHits = selectGatedHits;
|
|
28
|
-
exports.gatedHitText = gatedHitText;
|
|
29
|
-
exports.buildMemoryInjection = buildMemoryInjection;
|
|
16
|
+
exports.memoryPrimerModule = void 0;
|
|
30
17
|
exports.buildMemoryAnnounce = buildMemoryAnnounce;
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
exports.tallyMemoryUsed = tallyMemoryUsed;
|
|
34
|
-
exports.quietCacheSaysSilent = quietCacheSaysSilent;
|
|
35
|
-
// ---------- Layer 1: keyword trigger (free, same-turn) -----------------------
|
|
36
|
-
/** High-precision recall-intent phrases — MULTI-WORD by design (a single word like
|
|
37
|
-
* "previously" over-fires). Each strongly implies the agent needs recalled project
|
|
38
|
-
* context it may not hold. Tuned conservatively; the /mechanic monitor reports the
|
|
39
|
-
* false-fire rate and this list is where we'd loosen/tighten. */
|
|
40
|
-
const RECALL_PHRASES = [
|
|
41
|
-
'last time', 'we decided', 'we agreed', 'we chose', 'we discussed',
|
|
42
|
-
'we talked about', 'you said', 'you mentioned', 'you told me',
|
|
43
|
-
'remember when', 'remember that', 'what did we', 'what was our',
|
|
44
|
-
'as we discussed', 'as i mentioned', 'earlier you', 'earlier we',
|
|
45
|
-
'refresh my memory', 'catch me up', 'where did we leave',
|
|
46
|
-
'the bug we', 'that bug we', 'the plan we', 'did we ever', 'how did we',
|
|
47
|
-
];
|
|
48
|
-
const RECALL_RE = new RegExp(`\\b(${RECALL_PHRASES.join('|').replace(/ /g, '\\s+')})\\b`, 'i');
|
|
49
|
-
/** Does this prompt signal a recall need? Pure, total — the hook gates the inline search
|
|
50
|
-
* on it. The query handed to the search is the prompt itself (the search is built for
|
|
51
|
-
* natural queries; fragile noun-extraction buys nothing). */
|
|
52
|
-
function hasRecallIntent(prompt) {
|
|
53
|
-
return !!prompt && RECALL_RE.test(prompt);
|
|
54
|
-
}
|
|
55
|
-
/** Confidence gate — a hit must clear this to be injected. Below it, the reflex stays
|
|
56
|
-
* silent (the search result IS the precision filter). Reranked hits carry a calibrated
|
|
57
|
-
* `confidence` (0-1); unreranked hits fall back to a raw-score floor. */
|
|
58
|
-
exports.MEMORY_CONFIDENCE_GATE = 0.5;
|
|
59
|
-
const RAW_SCORE_FLOOR = 0.15;
|
|
60
|
-
/** Hard cap — never inject more than this many hits (context-bloat discipline; we just
|
|
61
|
-
* spent a session draining the heap). */
|
|
62
|
-
const MAX_INJECTED = 2;
|
|
63
|
-
/** Per-hit trim — keep each injected snippet tight. */
|
|
64
|
-
const HIT_TRIM = 280;
|
|
65
|
-
function passesGate(h) {
|
|
66
|
-
return typeof h.confidence === 'number'
|
|
67
|
-
? h.confidence >= exports.MEMORY_CONFIDENCE_GATE
|
|
68
|
-
: h.score >= RAW_SCORE_FLOOR;
|
|
69
|
-
}
|
|
70
|
-
/** The hits that clear the gate, capped — the SINGLE source of "what gets shown", shared by the
|
|
71
|
-
* injection framing AND the efficacy capture, so the overlap is scored against exactly what
|
|
72
|
-
* was injected. */
|
|
73
|
-
function selectGatedHits(hits) {
|
|
74
|
-
return (hits || []).filter(passesGate).slice(0, MAX_INJECTED);
|
|
75
|
-
}
|
|
76
|
-
/** Raw content of the gated hits (no framing boilerplate) — stashed at inject-time so the
|
|
77
|
-
* Stop-hook overlap scores the response against the actual memory, not the frame. */
|
|
78
|
-
function gatedHitText(hits) {
|
|
79
|
-
return selectGatedHits(hits).map(h => h.content || '').join('\n');
|
|
80
|
-
}
|
|
81
|
-
function trim(s, n) {
|
|
82
|
-
const t = (s || '').replace(/\s+/g, ' ').trim();
|
|
83
|
-
return t.length > n ? t.slice(0, n - 1) + '…' : t;
|
|
84
|
-
}
|
|
85
|
-
/** Frame the gated top hits into the inline injection block; null when nothing clears the
|
|
86
|
-
* gate (→ the module stays silent that turn). Tier is ambient (soft context, never a
|
|
87
|
-
* forced-decision directive). Tight + capped by design. */
|
|
88
|
-
function buildMemoryInjection(hits) {
|
|
89
|
-
const passing = selectGatedHits(hits);
|
|
90
|
-
if (passing.length === 0)
|
|
91
|
-
return null;
|
|
92
|
-
const lines = ['[📁 greprag memory — possibly relevant, auto-surfaced (pull more: `greprag memory search`):'];
|
|
93
|
-
for (const h of passing) {
|
|
94
|
-
const meta = [h.shape, h.createdAt ? h.createdAt.slice(0, 10) : null, h.projectName]
|
|
95
|
-
.filter(Boolean).join(', ');
|
|
96
|
-
lines.push(` · ${trim(h.content, HIT_TRIM)}${meta ? ` (${meta})` : ''}`);
|
|
97
|
-
}
|
|
98
|
-
lines.push(']');
|
|
99
|
-
return lines.join('\n');
|
|
100
|
-
}
|
|
101
|
-
// ---------- SessionStart announce (teach the capability once) -----------------
|
|
102
|
-
/** The business card, loaded once. Teaches the auto-surface AND the on-demand pull.
|
|
103
|
-
* Action-first, token-minimal, per the reminder-interrupt authoring rules. */
|
|
18
|
+
/** The business card, loaded once. Teaches the on-demand search/recap reflex —
|
|
19
|
+
* action-first, token-minimal, per the reminder-interrupt authoring rules. */
|
|
104
20
|
function buildMemoryAnnounce() {
|
|
105
21
|
return [
|
|
106
22
|
'[greprag memory — this project remembers. Every past session (decisions, bugs, what was discussed, who did what) is captured and searchable.]',
|
|
107
|
-
'
|
|
108
|
-
'On demand, any time you need more than surfaced: `greprag memory search "<topic>"` (semantic — ask in natural language), `greprag memory recap` to catch up on the session, `greprag memory recap --last N` for deeper history. Reach for it the moment you suspect this project knows something you don\'t — searching is cheap.',
|
|
23
|
+
'On demand, the moment you suspect this project knows something you don\'t: `greprag memory search "<topic>"` (semantic — ask in natural language), `greprag memory recap` to catch up on the session, `greprag memory recap --last N` for deeper history. Searching is cheap — reach for it rather than guessing or asking the operator to re-explain.',
|
|
109
24
|
].join('\n');
|
|
110
25
|
}
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
* zero cost + zero latency. */
|
|
119
|
-
const USED_MIN_TERMS = 2;
|
|
120
|
-
const MIN_TERM_LEN = 4;
|
|
121
|
-
const TERM_STOPWORDS = new Set([
|
|
122
|
-
'this', 'that', 'with', 'from', 'have', 'were', 'your', 'they', 'them', 'then', 'than',
|
|
123
|
-
'will', 'would', 'could', 'should', 'about', 'there', 'their', 'what', 'when', 'which',
|
|
124
|
-
'into', 'over', 'also', 'been', 'more', 'most', 'some', 'such', 'only', 'just', 'like',
|
|
125
|
-
'here', 'where', 'these', 'those', 'them', 'because',
|
|
126
|
-
// injection-frame boilerplate — never count these as "used":
|
|
127
|
-
'greprag', 'memory', 'relevant', 'surfaced', 'auto', 'session', 'project', 'search', 'recap', 'pull',
|
|
128
|
-
]);
|
|
129
|
-
function contentTerms(text) {
|
|
130
|
-
const out = new Set();
|
|
131
|
-
for (const raw of (text || '').toLowerCase().split(/[^a-z0-9]+/)) {
|
|
132
|
-
if (raw.length >= MIN_TERM_LEN && !TERM_STOPWORDS.has(raw))
|
|
133
|
-
out.add(raw);
|
|
134
|
-
}
|
|
135
|
-
return out;
|
|
136
|
-
}
|
|
137
|
-
/** Did the agent's response reuse distinctive content from the injection? Pure, total. */
|
|
138
|
-
function injectionWasUsed(hitText, prompt, response) {
|
|
139
|
-
const hitTerms = contentTerms(hitText);
|
|
140
|
-
if (hitTerms.size === 0)
|
|
141
|
-
return false;
|
|
142
|
-
const promptTerms = contentTerms(prompt);
|
|
143
|
-
const respTerms = contentTerms(response);
|
|
144
|
-
let used = 0;
|
|
145
|
-
for (const t of hitTerms) {
|
|
146
|
-
if (promptTerms.has(t))
|
|
147
|
-
continue; // not distinctive — already in the prompt
|
|
148
|
-
if (respTerms.has(t) && ++used >= USED_MIN_TERMS)
|
|
149
|
-
return true;
|
|
150
|
-
}
|
|
151
|
-
return false;
|
|
152
|
-
}
|
|
153
|
-
function normalizeStats(prior) {
|
|
154
|
-
const p = (prior && typeof prior === 'object') ? prior : {};
|
|
155
|
-
const bt = (p.byTrigger && typeof p.byTrigger === 'object') ? p.byTrigger : {};
|
|
156
|
-
return {
|
|
157
|
-
fires: Number.isFinite(p.fires) ? p.fires : 0,
|
|
158
|
-
injected: Number.isFinite(p.injected) ? p.injected : 0,
|
|
159
|
-
silent: Number.isFinite(p.silent) ? p.silent : 0,
|
|
160
|
-
used: Number.isFinite(p.used) ? p.used : 0,
|
|
161
|
-
byTrigger: {
|
|
162
|
-
keyword: Number.isFinite(bt.keyword) ? bt.keyword : 0,
|
|
163
|
-
flash: Number.isFinite(bt.flash) ? bt.flash : 0,
|
|
164
|
-
},
|
|
165
|
-
firstFiredAt: p.firstFiredAt,
|
|
166
|
-
lastFiredAt: p.lastFiredAt,
|
|
167
|
-
lastTurnCount: p.lastTurnCount,
|
|
168
|
-
};
|
|
169
|
-
}
|
|
170
|
-
/** Tally one fire — PURE (the hook supplies the clock). `didInject` splits the
|
|
171
|
-
* fire into injected vs silent so the monitor can read the inject/fire ratio. */
|
|
172
|
-
function tallyMemoryFire(prior, trigger, didInject, turnCount, nowIso) {
|
|
173
|
-
const s = normalizeStats(prior);
|
|
174
|
-
s.fires += 1;
|
|
175
|
-
if (didInject)
|
|
176
|
-
s.injected += 1;
|
|
177
|
-
else
|
|
178
|
-
s.silent += 1;
|
|
179
|
-
s.byTrigger[trigger] += 1;
|
|
180
|
-
s.firstFiredAt = s.firstFiredAt || nowIso;
|
|
181
|
-
s.lastFiredAt = nowIso;
|
|
182
|
-
if (Number.isFinite(turnCount))
|
|
183
|
-
s.lastTurnCount = turnCount;
|
|
184
|
-
return s;
|
|
185
|
-
}
|
|
186
|
-
/** Record that an injection was USED (the efficacy numerator) — PURE; bumped at Stop when the
|
|
187
|
-
* overlap proxy (or, later, the Flash-Lite judge) finds the response drew on the injection.
|
|
188
|
-
* Never decrements; `used ≤ injected` by construction of the call sites. */
|
|
189
|
-
function tallyMemoryUsed(prior) {
|
|
190
|
-
const s = normalizeStats(prior);
|
|
191
|
-
s.used += 1;
|
|
192
|
-
return s;
|
|
193
|
-
}
|
|
194
|
-
// ---------- Auto-quiet (server efficacy verdict, cached locally) --------------
|
|
195
|
-
/** The server (the Flash-Lite judge, `@greprag/core` memory-efficacy) owns the
|
|
196
|
-
* auto-quiet THRESHOLD and ships its verdict on the /v1/memory/query response.
|
|
197
|
-
* The hook caches that boolean and honors it with a TTL — how long to trust a
|
|
198
|
-
* cached verdict before re-probing. While quiet the reflex skips the search, so
|
|
199
|
-
* no fresh verdict arrives and the cache ages; once stale the reflex fires again
|
|
200
|
-
* to re-probe and the server re-judges. That gives auto-quiet TEETH (a real
|
|
201
|
-
* silent window) while staying self-healing — a reflex that recovers is never
|
|
202
|
-
* permanently silenced (unlike the hard, manual mechanicKilled() switch). */
|
|
203
|
-
exports.MEMORY_QUIET_CACHE_TTL_MS = 12 * 60 * 60 * 1000; // 12h
|
|
204
|
-
/** Does the locally-cached server verdict say "stay silent THIS turn"? PURE +
|
|
205
|
-
* total. Silent only when the cache is present, says quiet, and is still fresh.
|
|
206
|
-
* Fail-open everywhere else (missing / malformed / stale → NOT silent) so a bad
|
|
207
|
-
* cache can never permanently kill the reflex. */
|
|
208
|
-
function quietCacheSaysSilent(cache, nowMs) {
|
|
209
|
-
if (!cache || cache.quiet !== true || typeof cache.cachedAt !== 'string')
|
|
210
|
-
return false;
|
|
211
|
-
const t = Date.parse(cache.cachedAt);
|
|
212
|
-
if (!Number.isFinite(t))
|
|
213
|
-
return false;
|
|
214
|
-
return (nowMs - t) < exports.MEMORY_QUIET_CACHE_TTL_MS;
|
|
215
|
-
}
|
|
216
|
-
// ---------- The registry module ----------------------------------------------
|
|
217
|
-
/** memory-reflex as a reminder-interrupt module. announce teaches memory once; reminder
|
|
218
|
-
* routes the hook-supplied injection (env.memoryHit); detect gates on its presence. PURE —
|
|
219
|
-
* the hook owns the keyword gate + the search + the framing + the tally. */
|
|
220
|
-
exports.memoryReflexModule = {
|
|
221
|
-
id: 'memory-reflex',
|
|
222
|
-
detect: (env) => (env.memoryHit ? { tier: 'ambient' } : { tier: 'silent' }),
|
|
223
|
-
announce: () => buildMemoryAnnounce(),
|
|
224
|
-
reminder: (_d, env) => env.memoryHit ?? null,
|
|
26
|
+
/** memory-primer as a reminder-interrupt module: announce-only (a PRIMER). detect →
|
|
27
|
+
* silent, reminder → null. PURE. */
|
|
28
|
+
exports.memoryPrimerModule = {
|
|
29
|
+
id: 'memory-primer',
|
|
30
|
+
detect: (_env) => ({ tier: 'silent' }),
|
|
31
|
+
announce: (_env) => buildMemoryAnnounce(),
|
|
32
|
+
reminder: () => null,
|
|
225
33
|
};
|
|
226
34
|
//# sourceMappingURL=memory-reflex.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory-reflex.js","sourceRoot":"","sources":["../../src/commands/memory-reflex.ts"],"names":[],"mappings":";AAAA
|
|
1
|
+
{"version":3,"file":"memory-reflex.js","sourceRoot":"","sources":["../../src/commands/memory-reflex.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;wFAYwF;;;AAMxF,kDAKC;AAPD;+EAC+E;AAC/E,SAAgB,mBAAmB;IACjC,OAAO;QACL,+IAA+I;QAC/I,wVAAwV;KACzV,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAED;qCACqC;AACxB,QAAA,kBAAkB,GAAmB;IAChD,EAAE,EAAE,eAAe;IACnB,MAAM,EAAE,CAAC,IAAiB,EAAa,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC;IAC9D,QAAQ,EAAE,CAAC,IAAiB,EAAiB,EAAE,CAAC,mBAAmB,EAAE;IACrE,QAAQ,EAAE,GAAkB,EAAE,CAAC,IAAI;CACpC,CAAC"}
|
|
@@ -11,6 +11,11 @@ import { ReminderEnv, ReminderModule, Detection } from './reminder-types';
|
|
|
11
11
|
* loud, action-first modules last (watcher-arm → mechanic) so they read most-recent at
|
|
12
12
|
* SessionStart. Add a module here to wire it into BOTH surfaces at once. */
|
|
13
13
|
export declare const REGISTRY: ReminderModule[];
|
|
14
|
+
/** Split the registry by READ surface (docs/reminder-interrupt.md). UserPromptSubmit
|
|
15
|
+
* evaluates `prompt`-source modules (the default); PreToolUse evaluates `command`-source
|
|
16
|
+
* modules (they read env.toolCommand). Announces are always prompt-side (SessionStart). */
|
|
17
|
+
export declare const promptModules: (registry?: ReminderModule[]) => ReminderModule[];
|
|
18
|
+
export declare const commandModules: (registry?: ReminderModule[]) => ReminderModule[];
|
|
14
19
|
export interface FiredReminder {
|
|
15
20
|
id: string;
|
|
16
21
|
tier: Detection['tier'];
|
|
@@ -20,5 +25,11 @@ export interface FiredReminder {
|
|
|
20
25
|
* Stacking — a turn may carry several. A detector or reminder that throws drops that
|
|
21
26
|
* module for the turn; it never blocks the others or the turn. */
|
|
22
27
|
export declare function collectReminders(env: ReminderEnv, registry?: ReminderModule[]): FiredReminder[];
|
|
23
|
-
/**
|
|
28
|
+
/** Boot-sequence sort: a STABLE topological order over `dependsOn` (docs/load-system.md).
|
|
29
|
+
* Registry array order is the tie-breaker, so a dependency-free registry is emitted
|
|
30
|
+
* verbatim; declared deps only ever pull a module EARLIER. A missing/cyclic dep degrades
|
|
31
|
+
* gracefully to array order (never throws — a boot order is not worth crashing a turn). */
|
|
32
|
+
export declare function bootOrder(registry: ReminderModule[]): ReminderModule[];
|
|
33
|
+
/** SessionStart: every module's announce (skipping null), in BOOT-SEQUENCE order
|
|
34
|
+
* (dependsOn topo-sort, array order as tie-breaker). */
|
|
24
35
|
export declare function collectAnnounces(env: ReminderEnv, registry?: ReminderModule[]): string[];
|
|
@@ -5,10 +5,12 @@
|
|
|
5
5
|
* PURE over ReminderEnv — the hook assembles env (i/o) and emits the returned lines;
|
|
6
6
|
* a broken module never blocks a turn (fail-open per module). */
|
|
7
7
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
8
|
-
exports.REGISTRY = void 0;
|
|
8
|
+
exports.commandModules = exports.promptModules = exports.REGISTRY = void 0;
|
|
9
9
|
exports.collectReminders = collectReminders;
|
|
10
|
+
exports.bootOrder = bootOrder;
|
|
10
11
|
exports.collectAnnounces = collectAnnounces;
|
|
11
12
|
const inbox_primer_reminder_1 = require("./inbox-primer-reminder");
|
|
13
|
+
const load_primer_reminder_1 = require("./load-primer-reminder");
|
|
12
14
|
const corpus_reminder_1 = require("./corpus-reminder");
|
|
13
15
|
const setup_reminder_1 = require("./setup-reminder");
|
|
14
16
|
const frontdesk_reminder_1 = require("./frontdesk-reminder");
|
|
@@ -17,6 +19,8 @@ const assistant_reminder_1 = require("./assistant-reminder");
|
|
|
17
19
|
const memory_reflex_1 = require("./memory-reflex");
|
|
18
20
|
const arm_reminder_1 = require("./arm-reminder");
|
|
19
21
|
const friction_reminder_1 = require("./friction-reminder");
|
|
22
|
+
const collision_reminder_1 = require("./collision-reminder");
|
|
23
|
+
const version_reminder_1 = require("./version-reminder");
|
|
20
24
|
/** Registry order = display order. THE single agent-facing announce/reminder assembly:
|
|
21
25
|
* the hook does I/O → fills ReminderEnv → collectAnnounces (SessionStart) / collectReminders
|
|
22
26
|
* (per turn) render every module here in this order. Order preserves the historical recap
|
|
@@ -25,15 +29,26 @@ const friction_reminder_1 = require("./friction-reminder");
|
|
|
25
29
|
* SessionStart. Add a module here to wire it into BOTH surfaces at once. */
|
|
26
30
|
exports.REGISTRY = [
|
|
27
31
|
inbox_primer_reminder_1.inboxPrimerModule,
|
|
32
|
+
load_primer_reminder_1.loadPrimerModule,
|
|
33
|
+
load_primer_reminder_1.chipSpawnPointerModule,
|
|
28
34
|
corpus_reminder_1.corpusAnnounceModule,
|
|
29
35
|
setup_reminder_1.setupWarningModule,
|
|
36
|
+
version_reminder_1.versionUpgradeModule, // Deficiency-gated announce — silent unless a newer release exists
|
|
30
37
|
frontdesk_reminder_1.frontDeskModule,
|
|
31
38
|
checkpoint_reminder_1.checkpointModule,
|
|
32
39
|
assistant_reminder_1.assistantDoctrineModule,
|
|
33
|
-
memory_reflex_1.
|
|
40
|
+
memory_reflex_1.memoryPrimerModule,
|
|
34
41
|
arm_reminder_1.watcherArmModule,
|
|
35
42
|
friction_reminder_1.mechanicFrictionModule,
|
|
43
|
+
collision_reminder_1.collisionMatchModule, // source: 'command' — evaluated at PreToolUse, not UserPromptSubmit
|
|
36
44
|
];
|
|
45
|
+
/** Split the registry by READ surface (docs/reminder-interrupt.md). UserPromptSubmit
|
|
46
|
+
* evaluates `prompt`-source modules (the default); PreToolUse evaluates `command`-source
|
|
47
|
+
* modules (they read env.toolCommand). Announces are always prompt-side (SessionStart). */
|
|
48
|
+
const promptModules = (registry = exports.REGISTRY) => registry.filter((m) => (m.source ?? 'prompt') === 'prompt');
|
|
49
|
+
exports.promptModules = promptModules;
|
|
50
|
+
const commandModules = (registry = exports.REGISTRY) => registry.filter((m) => m.source === 'command');
|
|
51
|
+
exports.commandModules = commandModules;
|
|
37
52
|
/** Per-turn: every module's live reminder line (skipping silent), in registry order.
|
|
38
53
|
* Stacking — a turn may carry several. A detector or reminder that throws drops that
|
|
39
54
|
* module for the turn; it never blocks the others or the turn. */
|
|
@@ -61,10 +76,37 @@ function collectReminders(env, registry = exports.REGISTRY) {
|
|
|
61
76
|
}
|
|
62
77
|
return out;
|
|
63
78
|
}
|
|
64
|
-
/**
|
|
79
|
+
/** Boot-sequence sort: a STABLE topological order over `dependsOn` (docs/load-system.md).
|
|
80
|
+
* Registry array order is the tie-breaker, so a dependency-free registry is emitted
|
|
81
|
+
* verbatim; declared deps only ever pull a module EARLIER. A missing/cyclic dep degrades
|
|
82
|
+
* gracefully to array order (never throws — a boot order is not worth crashing a turn). */
|
|
83
|
+
function bootOrder(registry) {
|
|
84
|
+
const byId = new Map(registry.map((m) => [m.id, m]));
|
|
85
|
+
const ordered = [];
|
|
86
|
+
const placed = new Set();
|
|
87
|
+
const visiting = new Set();
|
|
88
|
+
const visit = (m) => {
|
|
89
|
+
if (placed.has(m.id) || visiting.has(m.id))
|
|
90
|
+
return; // placed, or a cycle → bail
|
|
91
|
+
visiting.add(m.id);
|
|
92
|
+
for (const dep of m.dependsOn ?? []) {
|
|
93
|
+
const d = byId.get(dep);
|
|
94
|
+
if (d)
|
|
95
|
+
visit(d); // unknown dep id → ignored (graceful)
|
|
96
|
+
}
|
|
97
|
+
visiting.delete(m.id);
|
|
98
|
+
placed.add(m.id);
|
|
99
|
+
ordered.push(m);
|
|
100
|
+
};
|
|
101
|
+
for (const m of registry)
|
|
102
|
+
visit(m);
|
|
103
|
+
return ordered;
|
|
104
|
+
}
|
|
105
|
+
/** SessionStart: every module's announce (skipping null), in BOOT-SEQUENCE order
|
|
106
|
+
* (dependsOn topo-sort, array order as tie-breaker). */
|
|
65
107
|
function collectAnnounces(env, registry = exports.REGISTRY) {
|
|
66
108
|
const out = [];
|
|
67
|
-
for (const m of registry) {
|
|
109
|
+
for (const m of bootOrder(registry)) {
|
|
68
110
|
let a = null;
|
|
69
111
|
try {
|
|
70
112
|
a = m.announce(env);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"reminder-registry.js","sourceRoot":"","sources":["../../src/commands/reminder-registry.ts"],"names":[],"mappings":";AAAA;;;;kEAIkE;;;
|
|
1
|
+
{"version":3,"file":"reminder-registry.js","sourceRoot":"","sources":["../../src/commands/reminder-registry.ts"],"names":[],"mappings":"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"}
|
|
@@ -40,6 +40,14 @@ export interface ReminderEnv {
|
|
|
40
40
|
/** Precomputed assistant-doctrine context — the hook owns the doctrine-file read, so
|
|
41
41
|
* this module half is a pass-through. Null for any non-assistant project. */
|
|
42
42
|
assistantDoctrine?: string | null;
|
|
43
|
+
/** Deficiency-gated announce signal: the installed greprag is behind the latest
|
|
44
|
+
* published version. Set by the hook (daily-cached npm-registry check); null/undefined
|
|
45
|
+
* when current. Drives the version-upgrade announce — "new release, upgrade with X".
|
|
46
|
+
* docs/reminder-interrupt.md (a Deficiency-gated announce, not a new type). */
|
|
47
|
+
updateAvailable?: {
|
|
48
|
+
current: string;
|
|
49
|
+
latest: string;
|
|
50
|
+
} | null;
|
|
43
51
|
/** Names of the api-docs-tagged corpus stores (the hook lists `?tag=api-docs`).
|
|
44
52
|
* Drives the corpus announce: name the on-tap reference banks so an agent
|
|
45
53
|
* searches them before answering from training data. Empty/undefined → the
|
|
@@ -52,6 +60,14 @@ export interface ReminderEnv {
|
|
|
52
60
|
* the search + the confidence gate + framing; the memory-reflex module routes the
|
|
53
61
|
* framed string as its per-turn reminder. Null = nothing cleared the gate this turn. */
|
|
54
62
|
memoryHit?: string | null;
|
|
63
|
+
/** The Bash command about to run (the `command` read surface for a Match trigger).
|
|
64
|
+
* Present only on the PreToolUse env; undefined at UserPromptSubmit. */
|
|
65
|
+
toolCommand?: string | null;
|
|
66
|
+
/** Same-repo peers live right now (the hook does the fresh watcher read off the
|
|
67
|
+
* hot path and passes the result; the collision module detects purely on it). */
|
|
68
|
+
collisionPeers?: {
|
|
69
|
+
short: string;
|
|
70
|
+
}[];
|
|
55
71
|
}
|
|
56
72
|
/** Slim open-checkpoint row (the SessionStart landmark announce, checkpoint module).
|
|
57
73
|
* Lives here — not in checkpoint-reminder.ts — because ReminderEnv references it and a
|
|
@@ -72,6 +88,18 @@ export interface Detection {
|
|
|
72
88
|
* unit-testable regression-lock. */
|
|
73
89
|
export interface ReminderModule {
|
|
74
90
|
id: string;
|
|
91
|
+
/** Which READ surface this module fires on (docs/reminder-interrupt.md — the landed
|
|
92
|
+
* map). `prompt` (default) modules run at UserPromptSubmit; `command` modules run at
|
|
93
|
+
* PreToolUse, reading `env.toolCommand`. The call site filters the registry by source
|
|
94
|
+
* so each hook event evaluates only its own modules. */
|
|
95
|
+
source?: 'prompt' | 'command';
|
|
96
|
+
/** Boot-sequence dependencies (docs/load-system.md §boot sequence): module ids whose
|
|
97
|
+
* announce MUST be emitted before this one's. A primer that references a concept an
|
|
98
|
+
* earlier primer establishes declares it here (e.g. the mechanic primer references chip
|
|
99
|
+
* vehicles → `dependsOn: ['chip-spawn-pointer']`; every pointer that says "greprag load"
|
|
100
|
+
* → `dependsOn: ['load-primer']`). collectAnnounces topologically sorts on this, so the
|
|
101
|
+
* boot order is a declared graph, not fragile array position. Omit = no dependency. */
|
|
102
|
+
dependsOn?: string[];
|
|
75
103
|
/** Read the live deficiency. The gate: `silent` ⇒ the module is quiet this turn. */
|
|
76
104
|
detect: (env: ReminderEnv) => Detection;
|
|
77
105
|
/** SessionStart schema, loaded once (teach method + why). null = nothing to announce. */
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/** version-reminder — the release-upgrade announce. A DEFICIENCY-gated announce
|
|
2
|
+
* (docs/reminder-interrupt.md): the Deficiency trigger ("a required state is missing")
|
|
3
|
+
* applied to the ANNOUNCE surface — the required state is "running the latest greprag,"
|
|
4
|
+
* the deficiency is "installed < latest published." When behind, the SessionStart announce
|
|
5
|
+
* tells the user a new release is out + how to upgrade; it auto-clears once they upgrade.
|
|
6
|
+
*
|
|
7
|
+
* This is what makes a release PROPAGATE — without it, users never learn to upgrade.
|
|
8
|
+
* NOT a new announce kind: it is a primer gated by Deficiency, exactly like setup-warning
|
|
9
|
+
* (which announces only when there's a drift gap). The hook does the I/O (the daily-cached
|
|
10
|
+
* npm-registry version check) and passes the verdict as env.updateAvailable; this module
|
|
11
|
+
* is PURE — announce-only (detect → silent, reminder → null). */
|
|
12
|
+
import { ReminderModule } from './reminder-types';
|
|
13
|
+
/** The upgrade announce — null unless the hook detected a newer published version. */
|
|
14
|
+
export declare function buildVersionAnnounce(current: string, latest: string): string;
|
|
15
|
+
export declare const versionUpgradeModule: ReminderModule;
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/** version-reminder — the release-upgrade announce. A DEFICIENCY-gated announce
|
|
3
|
+
* (docs/reminder-interrupt.md): the Deficiency trigger ("a required state is missing")
|
|
4
|
+
* applied to the ANNOUNCE surface — the required state is "running the latest greprag,"
|
|
5
|
+
* the deficiency is "installed < latest published." When behind, the SessionStart announce
|
|
6
|
+
* tells the user a new release is out + how to upgrade; it auto-clears once they upgrade.
|
|
7
|
+
*
|
|
8
|
+
* This is what makes a release PROPAGATE — without it, users never learn to upgrade.
|
|
9
|
+
* NOT a new announce kind: it is a primer gated by Deficiency, exactly like setup-warning
|
|
10
|
+
* (which announces only when there's a drift gap). The hook does the I/O (the daily-cached
|
|
11
|
+
* npm-registry version check) and passes the verdict as env.updateAvailable; this module
|
|
12
|
+
* is PURE — announce-only (detect → silent, reminder → null). */
|
|
13
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
14
|
+
exports.versionUpgradeModule = void 0;
|
|
15
|
+
exports.buildVersionAnnounce = buildVersionAnnounce;
|
|
16
|
+
/** The upgrade announce — null unless the hook detected a newer published version. */
|
|
17
|
+
function buildVersionAnnounce(current, latest) {
|
|
18
|
+
return [
|
|
19
|
+
`[greprag — a new release is out: v${latest} (you are on v${current}).]`,
|
|
20
|
+
`Upgrade: \`npm i -g greprag@latest\`. New behavior + fixes land on your next session.`,
|
|
21
|
+
].join('\n');
|
|
22
|
+
}
|
|
23
|
+
exports.versionUpgradeModule = {
|
|
24
|
+
id: 'version-upgrade',
|
|
25
|
+
detect: (_env) => ({ tier: 'silent' }), // announce-only
|
|
26
|
+
announce: (env) => {
|
|
27
|
+
const u = env.updateAvailable;
|
|
28
|
+
if (!u || !u.current || !u.latest)
|
|
29
|
+
return null;
|
|
30
|
+
return buildVersionAnnounce(u.current, u.latest);
|
|
31
|
+
},
|
|
32
|
+
reminder: () => null,
|
|
33
|
+
};
|
|
34
|
+
//# sourceMappingURL=version-reminder.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"version-reminder.js","sourceRoot":"","sources":["../../src/commands/version-reminder.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;kEAUkE;;;AAKlE,oDAKC;AAND,sFAAsF;AACtF,SAAgB,oBAAoB,CAAC,OAAe,EAAE,MAAc;IAClE,OAAO;QACL,qCAAqC,MAAM,iBAAiB,OAAO,KAAK;QACxE,uFAAuF;KACxF,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAEY,QAAA,oBAAoB,GAAmB;IAClD,EAAE,EAAE,iBAAiB;IACrB,MAAM,EAAE,CAAC,IAAiB,EAAa,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,gBAAgB;IAChF,QAAQ,EAAE,CAAC,GAAgB,EAAiB,EAAE;QAC5C,MAAM,CAAC,GAAG,GAAG,CAAC,eAAe,CAAC;QAC9B,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,OAAO,IAAI,CAAC,CAAC,CAAC,MAAM;YAAE,OAAO,IAAI,CAAC;QAC/C,OAAO,oBAAoB,CAAC,CAAC,CAAC,OAAO,EAAE,CAAC,CAAC,MAAM,CAAC,CAAC;IACnD,CAAC;IACD,QAAQ,EAAE,GAAkB,EAAE,CAAC,IAAI;CACpC,CAAC"}
|