greprag 5.50.0 → 5.51.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.
@@ -6,10 +6,13 @@
6
6
  * agent runs it and reads the doctrine inline (the `/ui-skills` "I know kung fu"
7
7
  * pattern). docs/load-system.md.
8
8
  *
9
- * greprag load → the catalog (every entry, one line each)
9
+ * greprag load → the catalog (bundled entries + your mirrored skills)
10
10
  * greprag load <name> → that entry's full doctrine, printed to stdout
11
+ * greprag load <skill> <docpath> → a mirrored skill's companion doc
11
12
  *
12
- * Entries are bundled markdown under `skill/templates/`, shipped in the CLI
13
- * package `files` array (resolved the same way `init` + `skill` resolve bundled
14
- * assets: relative to __dirname). Zero network, zero filesystem state. */
15
- export declare function runLoad(args: string[]): void;
13
+ * Resolution order (docs/load-system.md): BUNDLED (markdown under
14
+ * `skill/templates/`, in the package `files` array zero network) the
15
+ * Tier-3 SKILL MIRROR (your own used skills, auto-shadowed per-tenant, served
16
+ * from /v1/skillmirror works on any machine/harness with the CLI + key).
17
+ * Marketplace (remote curated) comes later. */
18
+ export declare function runLoad(args: string[]): Promise<void>;
@@ -7,12 +7,15 @@
7
7
  * agent runs it and reads the doctrine inline (the `/ui-skills` "I know kung fu"
8
8
  * pattern). docs/load-system.md.
9
9
  *
10
- * greprag load → the catalog (every entry, one line each)
10
+ * greprag load → the catalog (bundled entries + your mirrored skills)
11
11
  * greprag load <name> → that entry's full doctrine, printed to stdout
12
+ * greprag load <skill> <docpath> → a mirrored skill's companion doc
12
13
  *
13
- * Entries are bundled markdown under `skill/templates/`, shipped in the CLI
14
- * package `files` array (resolved the same way `init` + `skill` resolve bundled
15
- * assets: relative to __dirname). Zero network, zero filesystem state. */
14
+ * Resolution order (docs/load-system.md): BUNDLED (markdown under
15
+ * `skill/templates/`, in the package `files` array zero network) the
16
+ * Tier-3 SKILL MIRROR (your own used skills, auto-shadowed per-tenant, served
17
+ * from /v1/skillmirror — works on any machine/harness with the CLI + key).
18
+ * Marketplace (remote curated) comes later. */
16
19
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
17
20
  if (k2 === undefined) k2 = k;
18
21
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -82,27 +85,95 @@ const LIBRARY = {
82
85
  function templatePath(file) {
83
86
  return path.join(__dirname, '..', '..', 'skill', 'templates', file);
84
87
  }
85
- function printCatalog() {
88
+ // ---------- Tier 3 — the skill mirror (docs/load-system.md) -------------------
89
+ const API_URL_DEFAULT = 'https://api.greprag.com';
90
+ const MIRROR_TIMEOUT_MS = 4_000;
91
+ function mirrorConfig() {
92
+ const apiKey = process.env.GREPRAG_API_KEY || '';
93
+ if (!apiKey)
94
+ return null;
95
+ return { apiUrl: process.env.GREPRAG_API_URL || API_URL_DEFAULT, apiKey };
96
+ }
97
+ /** GET a mirror endpoint with a hard timeout — the catalog must never hang a
98
+ * terminal. Null on any failure (offline, no key, 404). */
99
+ async function mirrorGet(pathname) {
100
+ const cfg = mirrorConfig();
101
+ if (!cfg)
102
+ return null;
103
+ try {
104
+ const ctrl = new AbortController();
105
+ const t = setTimeout(() => ctrl.abort(), MIRROR_TIMEOUT_MS);
106
+ const res = await fetch(`${cfg.apiUrl}${pathname}`, {
107
+ headers: { 'Authorization': `Bearer ${cfg.apiKey}` },
108
+ signal: ctrl.signal,
109
+ });
110
+ clearTimeout(t);
111
+ if (!res.ok)
112
+ return null;
113
+ return await res.json();
114
+ }
115
+ catch {
116
+ return null;
117
+ }
118
+ }
119
+ async function printCatalog() {
86
120
  const names = Object.keys(LIBRARY);
87
121
  const width = Math.max(...names.map((n) => n.length));
88
122
  console.log('\ngreprag load <name> — load doctrine on demand (the CLI is the payload, no .md files):\n');
89
123
  for (const name of names) {
90
124
  console.log(` ${name.padEnd(width)} ${LIBRARY[name].purpose}`);
91
125
  }
92
- console.log('\n greprag load <name> print the full entry');
93
- console.log(' greprag load this catalog\n');
126
+ // Tier-3 section: the operator's own skills, auto-mirrored from use.
127
+ const data = await mirrorGet('/v1/skillmirror');
128
+ const skills = data?.skills || [];
129
+ if (skills.length > 0) {
130
+ const w = Math.max(...skills.map((s) => (s.skillName || '').length), 8);
131
+ console.log('\nYour mirrored skills (auto-fresh from use — work on any machine/harness):\n');
132
+ for (const s of skills) {
133
+ if (!s.skillName)
134
+ continue;
135
+ console.log(` ${s.skillName.padEnd(w)} ${(s.description || '').slice(0, 110)}`);
136
+ }
137
+ }
138
+ console.log('\n greprag load <name> print the full entry / mirrored skill');
139
+ console.log(' greprag load <skill> <doc> a mirrored skill\'s companion doc');
140
+ console.log(' greprag load this catalog\n');
141
+ }
142
+ /** Serve a mirrored skill: SKILL.md (or a named companion doc) to stdout. */
143
+ async function printMirroredSkill(name, docPath) {
144
+ const data = await mirrorGet(`/v1/skillmirror/${encodeURIComponent(name)}`);
145
+ const files = data?.files || [];
146
+ if (files.length === 0)
147
+ return false;
148
+ const target = docPath || 'SKILL.md';
149
+ const file = files.find((f) => f.path === target);
150
+ if (!file || typeof file.content !== 'string') {
151
+ console.error(`Mirrored skill "${name}" has no file "${target}". Files: ${files.map((f) => f.path).join(', ')}`);
152
+ process.exit(1);
153
+ }
154
+ process.stdout.write(file.content.endsWith('\n') ? file.content : file.content + '\n');
155
+ if (!docPath) {
156
+ const companions = files.map((f) => f.path).filter((p) => p !== 'SKILL.md');
157
+ if (companions.length > 0) {
158
+ process.stdout.write(`\n[mirrored skill — companion docs: ${companions.join(', ')} — \`greprag load ${name} <doc>\`]\n`);
159
+ }
160
+ }
161
+ return true;
94
162
  }
95
- function runLoad(args) {
163
+ async function runLoad(args) {
96
164
  const name = args[0];
97
165
  // Bare (or an explicit list flag) → the browsable catalog.
98
166
  if (!name || name === '--list' || name === 'list') {
99
- printCatalog();
167
+ await printCatalog();
100
168
  return;
101
169
  }
102
170
  const entry = LIBRARY[name];
103
171
  if (!entry) {
172
+ // Not bundled → try the Tier-3 skill mirror before giving up.
173
+ if (await printMirroredSkill(name, args[1]))
174
+ return;
104
175
  console.error(`Unknown load entry: ${name}\n`);
105
- printCatalog();
176
+ await printCatalog();
106
177
  process.exit(1);
107
178
  }
108
179
  const file = templatePath(entry.file);
@@ -1 +1 @@
1
- {"version":3,"file":"load.js","sourceRoot":"","sources":["../../src/commands/load.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"load.js","sourceRoot":"","sources":["../../src/commands/load.ts"],"names":[],"mappings":"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"}
@@ -22,6 +22,7 @@ const collision_reminder_1 = require("./collision-reminder");
22
22
  const version_reminder_1 = require("./version-reminder");
23
23
  const enrichment_health_reminder_1 = require("./enrichment-health-reminder");
24
24
  const skill_gain_reminder_1 = require("./skill-gain-reminder");
25
+ const skill_mirror_reminder_1 = require("./skill-mirror-reminder");
25
26
  /** Registry order = display order. THE single agent-facing announce/reminder assembly:
26
27
  * the hook does I/O → fills ReminderEnv → collectAnnounces (SessionStart) / collectReminders
27
28
  * (per turn) render every module here in this order. Order preserves the historical recap
@@ -34,7 +35,8 @@ exports.REGISTRY = [
34
35
  load_primer_reminder_1.chipSpawnPointerModule,
35
36
  corpus_reminder_1.corpusAnnounceModule,
36
37
  doc_pointer_reminder_1.docPointerAnnounceModule, // announce-only "core documents" list (docs/doc-pointer-system.md)
37
- skill_gain_reminder_1.skillGainAnnounceModule, // announce-only skill-gain landings + proposals (docs/skill-learning-loop.md)
38
+ skill_gain_reminder_1.skillGainAnnounceModule, // announce-only skill-gain landings (docs/skill-learning-loop.md)
39
+ skill_mirror_reminder_1.skillMirrorAnnounceModule, // one-line Tier-3 pointer: your skills travel via greprag load (docs/load-system.md)
38
40
  setup_reminder_1.setupWarningModule,
39
41
  version_reminder_1.versionUpgradeModule, // Deficiency-gated announce — silent unless a newer release exists
40
42
  enrichment_health_reminder_1.enrichmentHealthModule, // Deficiency-gated announce — silent unless the Gemini probe fails (fix c2eb8777)
@@ -1 +1 @@
1
- {"version":3,"file":"reminder-registry.js","sourceRoot":"","sources":["../../src/commands/reminder-registry.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"reminder-registry.js","sourceRoot":"","sources":["../../src/commands/reminder-registry.ts"],"names":[],"mappings":"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"}
@@ -78,6 +78,14 @@ export interface ReminderEnv {
78
78
  oneLiner: string;
79
79
  }>;
80
80
  };
81
+ /** Tier-3 skill-mirror signal (docs/load-system.md): how many of the
82
+ * operator's skills are mirrored into the load store + the freshest few
83
+ * names. Drives the one-line "your skills travel with the harness"
84
+ * pointer. Undefined/0 → announce silent. */
85
+ mirroredSkills?: {
86
+ count: number;
87
+ names: string[];
88
+ };
81
89
  /** Auto-surfaced episodic-memory injection — the hook owns the recall-intent gate +
82
90
  * the search + the confidence gate + framing; the memory-reflex module routes the
83
91
  * framed string as its per-turn reminder. Null = nothing cleared the gate this turn. */
@@ -0,0 +1,15 @@
1
+ /** skill-mirror-announce — the Tier-3 mirror's SessionStart pointer
2
+ * (docs/load-system.md §Tier 3). A POINTER, not a list (context economy):
3
+ * one line naming the capability + the command — the catalog lives behind
4
+ * `greprag load`. This is the "where to find skills" info the operator asked
5
+ * for, and the discovery half of trigger parity until the frontrun judge
6
+ * lands (see the spec's named gaps).
7
+ *
8
+ * The hook passes env.mirroredSkills (count + a few freshest names); this
9
+ * module is PURE render. Silent when nothing is mirrored yet. */
10
+ import { ReminderModule } from './reminder-types';
11
+ export declare function buildSkillMirrorAnnounce(m: {
12
+ count: number;
13
+ names: string[];
14
+ } | undefined): string | null;
15
+ export declare const skillMirrorAnnounceModule: ReminderModule;
@@ -0,0 +1,33 @@
1
+ "use strict";
2
+ /** skill-mirror-announce — the Tier-3 mirror's SessionStart pointer
3
+ * (docs/load-system.md §Tier 3). A POINTER, not a list (context economy):
4
+ * one line naming the capability + the command — the catalog lives behind
5
+ * `greprag load`. This is the "where to find skills" info the operator asked
6
+ * for, and the discovery half of trigger parity until the frontrun judge
7
+ * lands (see the spec's named gaps).
8
+ *
9
+ * The hook passes env.mirroredSkills (count + a few freshest names); this
10
+ * module is PURE render. Silent when nothing is mirrored yet. */
11
+ Object.defineProperty(exports, "__esModule", { value: true });
12
+ exports.skillMirrorAnnounceModule = void 0;
13
+ exports.buildSkillMirrorAnnounce = buildSkillMirrorAnnounce;
14
+ function buildSkillMirrorAnnounce(m) {
15
+ if (!m || m.count <= 0)
16
+ return null;
17
+ const sample = m.names.slice(0, 4).join(', ');
18
+ const more = m.count > 4 ? ', …' : '';
19
+ return [
20
+ `[greprag skills — ${m.count} of your skills travel with the harness (auto-mirrored from use, always fresh): ${sample}${more}.]`,
21
+ 'Any machine or harness with the greprag CLI can use them — `greprag load` to browse, '
22
+ + '`greprag load <skill>` to read one inline. No ~/.claude/skills needed.',
23
+ ].join('\n');
24
+ }
25
+ exports.skillMirrorAnnounceModule = {
26
+ id: 'skill-mirror-announce',
27
+ source: 'prompt',
28
+ dependsOn: ['load-primer'], // references `greprag load` — the primer establishes it
29
+ detect: (_env) => ({ tier: 'silent' }), // announce-only
30
+ announce: (env) => buildSkillMirrorAnnounce(env.mirroredSkills),
31
+ reminder: () => null,
32
+ };
33
+ //# sourceMappingURL=skill-mirror-reminder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"skill-mirror-reminder.js","sourceRoot":"","sources":["../../src/commands/skill-mirror-reminder.ts"],"names":[],"mappings":";AAAA;;;;;;;;kEAQkE;;;AAIlE,4DAWC;AAXD,SAAgB,wBAAwB,CACtC,CAAiD;IAEjD,IAAI,CAAC,CAAC,IAAI,CAAC,CAAC,KAAK,IAAI,CAAC;QAAE,OAAO,IAAI,CAAC;IACpC,MAAM,MAAM,GAAG,CAAC,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC9C,MAAM,IAAI,GAAG,CAAC,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC;IACtC,OAAO;QACL,qBAAqB,CAAC,CAAC,KAAK,mFAAmF,MAAM,GAAG,IAAI,IAAI;QAChI,uFAAuF;cACrF,wEAAwE;KAC3E,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;AACf,CAAC;AAEY,QAAA,yBAAyB,GAAmB;IACvD,EAAE,EAAE,uBAAuB;IAC3B,MAAM,EAAE,QAAQ;IAChB,SAAS,EAAE,CAAC,aAAa,CAAC,EAAE,wDAAwD;IACpF,MAAM,EAAE,CAAC,IAAiB,EAAa,EAAE,CAAC,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,EAAE,gBAAgB;IAChF,QAAQ,EAAE,CAAC,GAAgB,EAAiB,EAAE,CAAC,wBAAwB,CAAC,GAAG,CAAC,cAAc,CAAC;IAC3F,QAAQ,EAAE,GAAkB,EAAE,CAAC,IAAI;CACpC,CAAC"}
package/dist/hook.js CHANGED
@@ -117,7 +117,13 @@ const coordinate_gate_1 = require("./commands/coordinate-gate");
117
117
  // logic lives in ./commands/inbox-drain. adr: adr/monitor-resilience.md
118
118
  const inbox_drain_1 = require("./commands/inbox-drain");
119
119
  const procedure_1 = require("./procedure");
120
+ // Procedure System LEARN leg (docs/procedure-system.md §V1 LEARN-leg build
121
+ // spec): the span watch — opened at UserPromptSubmit (procedureCheck), observed
122
+ // per turn by the Stop hook (observeProcedureTurn), closed by negative
123
+ // continuity, distilled server-side, transitions applied to the local store.
124
+ const procedure_watch_1 = require("./procedure-watch");
120
125
  const skill_landing_1 = require("./skill-landing");
126
+ const skill_mirror_client_1 = require("./skill-mirror-client");
121
127
  const API_URL_DEFAULT = 'https://api.greprag.com';
122
128
  const MAX_FIELD_CHARS = 500_000; // safety cap per text field
123
129
  // ---------- Env + config ---------------------------------------------------
@@ -806,6 +812,125 @@ function collectDocEvents(filesTouched, cwd) {
806
812
  }
807
813
  return events;
808
814
  }
815
+ // ---------- Procedure span observer (docs/procedure-system.md) --------------
816
+ //
817
+ // The LEARN leg's per-turn observer (build spec component 3). Gated on "a
818
+ // watch file exists" — zero cost otherwise. Each observed turn: build a
819
+ // compact TurnDigest, append it to the watch, ask the server the negative-
820
+ // continuity question (synchronous — a ~0.5s flash-lite call inside the Stop
821
+ // hook's existing 10s budget), then apply the pure close logic. Fail-open end
822
+ // to end: a dead judge extends the watch (offStreak untouched) and the
823
+ // max-turn watchdog reaps it.
824
+ /** Fire-and-forget the watchdog's fix-queue smell via `greprag fix log`
825
+ * (detached CLI child — the guard-refresh spawn shape). Best-effort. */
826
+ function spawnProcedureSmell(cwd, text) {
827
+ try {
828
+ const cliJs = path.join(__dirname, 'index.js');
829
+ const child = (0, proc_1.safeSpawn)(process.execPath, [cliJs, 'fix', 'log', text, '--scope', 'procedure-watch'], { cwd, detached: true, stdio: 'ignore', windowsHide: true });
830
+ child.unref();
831
+ }
832
+ catch { /* best-effort */ }
833
+ }
834
+ /** One observed turn of the active watch. Never throws (the caller also
835
+ * guards); every network call fail-open. */
836
+ async function observeProcedureTurn(cfg, anchor, turn, cwd, redaction) {
837
+ const watch = (0, procedure_watch_1.readProcedureWatch)(anchor.projectId);
838
+ if (!watch) {
839
+ // File exists but is unreadable/corrupt — drop it so it can't wedge the observer.
840
+ (0, procedure_watch_1.clearProcedureWatch)(anchor.projectId);
841
+ return;
842
+ }
843
+ // Watchdog BEFORE the judge call — the aborting turn costs nothing. Abort =
844
+ // clear the watch, log a fix-queue smell, NEVER distill (build spec comp. 4).
845
+ if ((0, procedure_watch_1.willExceedWatchdog)(watch)) {
846
+ (0, procedure_watch_1.clearProcedureWatch)(anchor.projectId);
847
+ spawnProcedureSmell(cwd, `procedure watchdog: ${watch.phase} watch on "${watch.verb}" hit ${watch.turnCount} turns `
848
+ + `without closing — endpoint never detected (docs/procedure-system.md)`);
849
+ return;
850
+ }
851
+ // Compact digest of THIS turn, scrubbed like the turn envelope (secrets
852
+ // never travel — adr/secret-scrubber.md).
853
+ const rawDigest = (0, procedure_watch_1.buildTurnDigest)({
854
+ userPrompt: turn.userPrompt,
855
+ toolCalls: turn.toolCalls,
856
+ filesTouched: turn.filesTouched,
857
+ status: turn.status,
858
+ }, watch.turnCount + 1);
859
+ const digest = {
860
+ ...rawDigest,
861
+ user: (0, secret_scrubber_1.scrubString)(rawDigest.user, redaction),
862
+ commands: rawDigest.commands.map(c => (0, secret_scrubber_1.scrubString)(c, redaction)),
863
+ };
864
+ // The negative-continuity question — synchronous, fail-open: any miss →
865
+ // live=null → offStreak untouched, the watch just extends.
866
+ let live = null;
867
+ try {
868
+ const res = await fetch(`${cfg.apiUrl}/v1/procedure/${anchor.projectId}/observe`, {
869
+ method: 'POST',
870
+ headers: { 'Authorization': `Bearer ${cfg.apiKey}`, 'Content-Type': 'application/json' },
871
+ body: JSON.stringify({
872
+ projectName: anchor.projectName,
873
+ verb: watch.verb,
874
+ phase: watch.phase,
875
+ digest,
876
+ turnCount: watch.turnCount + 1,
877
+ }),
878
+ });
879
+ if (res.ok) {
880
+ const data = await res.json();
881
+ live = typeof data.live === 'boolean' ? data.live : null;
882
+ }
883
+ }
884
+ catch { /* judge unreachable → null */ }
885
+ const { watch: next, disposition } = (0, procedure_watch_1.advanceWatch)(watch, digest, live);
886
+ if (disposition === 'watchdog') {
887
+ (0, procedure_watch_1.clearProcedureWatch)(anchor.projectId);
888
+ spawnProcedureSmell(cwd, `procedure watchdog: ${next.phase} watch on "${next.verb}" hit ${next.turnCount} turns `
889
+ + `without closing — endpoint never detected (docs/procedure-system.md)`);
890
+ return;
891
+ }
892
+ if (disposition === 'close') {
893
+ // Span = [open, lastLiveTurn] — trailing not-live turns never distill.
894
+ const span = (0, procedure_watch_1.liveSpan)(next);
895
+ if (span.length > 0) {
896
+ try {
897
+ const res = await fetch(`${cfg.apiUrl}/v1/procedure/${anchor.projectId}/distill`, {
898
+ method: 'POST',
899
+ headers: { 'Authorization': `Bearer ${cfg.apiKey}`, 'Content-Type': 'application/json' },
900
+ body: JSON.stringify({
901
+ projectName: anchor.projectName,
902
+ verb: next.verb,
903
+ phase: next.phase,
904
+ digests: span,
905
+ }),
906
+ });
907
+ if (res.ok) {
908
+ const data = await res.json();
909
+ const outcome = data.outcome === 'clean' || data.outcome === 'floundered'
910
+ ? data.outcome : null;
911
+ const distilled = data.procedure
912
+ && typeof data.procedure.steps === 'string' && data.procedure.steps
913
+ && typeof data.procedure.endpoint === 'string' && data.procedure.endpoint
914
+ ? {
915
+ steps: data.procedure.steps,
916
+ endpoint: data.procedure.endpoint,
917
+ caveats: typeof data.procedure.caveats === 'string' && data.procedure.caveats
918
+ ? data.procedure.caveats : undefined,
919
+ }
920
+ : null;
921
+ const store = (0, procedure_1.readProcedureStore)(anchor.projectId);
922
+ const { store: nextStore, action } = (0, procedure_watch_1.applyDistillTransition)(store, next, outcome, distilled, (0, procedure_watch_1.learnTriggersForVerb)(next.verb, store));
923
+ if (action !== 'discarded')
924
+ (0, procedure_1.writeProcedureStore)(anchor.projectId, nextStore);
925
+ }
926
+ }
927
+ catch { /* judge down → no write; watch discarded (transition table row 4) */ }
928
+ }
929
+ (0, procedure_watch_1.clearProcedureWatch)(anchor.projectId);
930
+ return;
931
+ }
932
+ (0, procedure_watch_1.writeProcedureWatch)(anchor.projectId, next);
933
+ }
809
934
  /** SessionStart fetch: the live enrichment-health verdict (fix c2eb8777).
810
935
  * Null = healthy OR unreachable — the outage announce only fires on a
811
936
  * POSITIVE probe failure, so an API blip never cries wolf. The worker
@@ -986,6 +1111,28 @@ async function store(input, source = 'claude-code') {
986
1111
  }
987
1112
  catch { /* best-effort — a docptr outage never fails the Stop hook */ }
988
1113
  }
1114
+ // Procedure System span observer (docs/procedure-system.md V1 LEARN leg):
1115
+ // gated on "a watch file exists" — zero cost when no watch is open. One
1116
+ // synchronous flash-lite continuity call per observed turn; close/watchdog
1117
+ // logic is pure in procedure-watch.ts. Best-effort, never throws.
1118
+ if ((0, procedure_watch_1.hasProcedureWatch)(anchor.projectId)) {
1119
+ try {
1120
+ await observeProcedureTurn(cfg, anchor, turn, cwd, redaction);
1121
+ }
1122
+ catch { /* fail-open — a procedure outage never fails the Stop hook */ }
1123
+ }
1124
+ // Skill mirror (docs/load-system.md Tier 3): a skill-load turn shadows that
1125
+ // skill's current markdown into the tenant's load store — upload ONLY on
1126
+ // change (local hash state), so an unchanged skill costs zero network.
1127
+ // Follower semantics: files stay canonical. Best-effort, never throws.
1128
+ if (provenance === 'skill-injection') {
1129
+ const mirrorSkill = (0, turn_provenance_1.provenanceHint)(provenance, turn.userPrompt);
1130
+ if (mirrorSkill) {
1131
+ await (0, skill_mirror_client_1.maybeMirrorSkill)({
1132
+ skill: mirrorSkill, cwd, apiUrl: cfg.apiUrl, apiKey: cfg.apiKey,
1133
+ });
1134
+ }
1135
+ }
989
1136
  }
990
1137
  // ---------- Main ---------------------------------------------------------
991
1138
  // ---------- Recap (SessionStart) ------------------------------------------
@@ -1312,6 +1459,23 @@ async function recap(input, mode = 'plain', opts = {}) {
1312
1459
  // gains → auto-land references into learnings docs, hold rule/scope
1313
1460
  // proposals for the announce. Best-effort; undefined → announce silent.
1314
1461
  const skillGains = await fetchAndLandSkillGains(cfg.apiUrl, cfg.apiKey, anchor, cwd);
1462
+ // Tier-3 skill-mirror pointer signal (docs/load-system.md): mirrored-skill
1463
+ // count + freshest names for the one-line announce. Best-effort.
1464
+ const mirroredSkills = await (async () => {
1465
+ try {
1466
+ const res = await fetch(`${cfg.apiUrl}/v1/skillmirror`, {
1467
+ headers: { 'Authorization': `Bearer ${cfg.apiKey}` },
1468
+ });
1469
+ if (!res.ok)
1470
+ return undefined;
1471
+ const data = await res.json();
1472
+ const names = (data.skills || []).map(s => s.skillName || '').filter(Boolean);
1473
+ return names.length > 0 ? { count: names.length, names } : undefined;
1474
+ }
1475
+ catch {
1476
+ return undefined;
1477
+ }
1478
+ })();
1315
1479
  // Assistant doctrine auto-load — fires ONLY for the flagged assistant project
1316
1480
  // (isAssistantProject), so a normal project sees zero change. The hook owns the
1317
1481
  // doctrine-file read; the assistant-doctrine module routes the text. adr: adr/assistant-role.md
@@ -1347,6 +1511,7 @@ async function recap(input, mode = 'plain', opts = {}) {
1347
1511
  docPointers,
1348
1512
  enrichmentDown,
1349
1513
  skillGains,
1514
+ mirroredSkills,
1350
1515
  updateAvailable,
1351
1516
  };
1352
1517
  let announceReg = mechanicKilled() ? reminder_registry_1.REGISTRY.filter(m => m.id !== 'mechanic-friction') : reminder_registry_1.REGISTRY;
@@ -1461,13 +1626,22 @@ function codexSubagentStart(input) {
1461
1626
  if (context)
1462
1627
  writeAdditionalContext(input.hook_event_name || 'SubagentStart', context);
1463
1628
  }
1464
- /** UserPromptSubmit — Procedure System REPLAY leg (docs/procedure-system.md).
1465
- * Match the prompt against the project's procedure store; on a hit inject the
1466
- * recipe + gate as additionalContext BEFORE the agent moves, so an operational
1467
- * verb runs cleanly instead of by trial-and-error. Pure-local (no network on
1468
- * the hot path reads the file beside the matchset cache), additive, and
1469
- * fail-open: a miss or any error never blocks the turn. The Tier-1 intent guard
1470
- * (in matchProcedure) suppresses keyword mention-not-request false-fires. */
1629
+ /** UserPromptSubmit — Procedure System trigger leg (docs/procedure-system.md
1630
+ * §V1 LEARN-leg build spec, component 2). Two paths off a Tier-1 trigger hit:
1631
+ *
1632
+ * (a) known NON-STALE procedure inject the recipe + gate BEFORE the agent
1633
+ * moves (the REPLAY inject) + open a bounded REPLAY watch.
1634
+ * (b) matched verb with NO procedure, or status=stale open an open-ended
1635
+ * LEARN watch, NO injection (there's nothing to inject; the flounder
1636
+ * about to happen is the learning material). The LEARN trigger
1637
+ * vocabulary is the Tier-1 verb list (matchLearnTrigger), independent
1638
+ * of the store — build + test ride here with no seed.
1639
+ *
1640
+ * A LEARN watch NEVER opens for a destructive verb (the seed is the canon
1641
+ * even when stale). One active watch at a time — an in-flight watch is never
1642
+ * replaced. Pure-local (no network on the hot path), additive, fail-open:
1643
+ * a miss or any error never blocks the turn. The Tier-1 intent guard (in
1644
+ * matchProcedure) suppresses keyword mention-not-request false-fires. */
1471
1645
  async function procedureCheck(input) {
1472
1646
  try {
1473
1647
  const prompt = typeof input.prompt === 'string' ? input.prompt : '';
@@ -1477,12 +1651,20 @@ async function procedureCheck(input) {
1477
1651
  if (!anchor.projectId)
1478
1652
  return;
1479
1653
  const store = (0, procedure_1.readProcedureStore)(anchor.projectId);
1480
- if (!store.procedures.length)
1654
+ const m = store.procedures.length ? (0, procedure_1.matchProcedure)(prompt, store) : null;
1655
+ if (m && m.procedure.status !== 'stale') {
1656
+ writeAdditionalContext(input.hook_event_name || 'UserPromptSubmit', (0, procedure_1.buildProcedureInjection)(m.procedure));
1657
+ (0, procedure_watch_1.openWatchIfIdle)(anchor.projectId, m.procedure.verb, 'REPLAY');
1481
1658
  return;
1482
- const m = (0, procedure_1.matchProcedure)(prompt, store);
1483
- if (!m)
1659
+ }
1660
+ // LEARN path: a stale store hit, or a Tier-1 vocabulary hit with no
1661
+ // procedure for the verb. No injection either way.
1662
+ const lm = m || (0, procedure_watch_1.matchLearnTrigger)(prompt);
1663
+ if (!lm)
1484
1664
  return;
1485
- writeAdditionalContext(input.hook_event_name || 'UserPromptSubmit', (0, procedure_1.buildProcedureInjection)(m.procedure));
1665
+ if ((0, procedure_watch_1.isDestructiveVerb)(lm.procedure.verb, store))
1666
+ return; // never LEARN a destructive verb
1667
+ (0, procedure_watch_1.openWatchIfIdle)(anchor.projectId, lm.procedure.verb, 'LEARN');
1486
1668
  }
1487
1669
  catch { /* fail-open — a procedure miss never blocks the turn */ }
1488
1670
  }