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.
- package/dist/commands/load.d.ts +8 -5
- package/dist/commands/load.js +81 -10
- package/dist/commands/load.js.map +1 -1
- package/dist/commands/reminder-registry.js +3 -1
- package/dist/commands/reminder-registry.js.map +1 -1
- package/dist/commands/reminder-types.d.ts +8 -0
- package/dist/commands/skill-mirror-reminder.d.ts +15 -0
- package/dist/commands/skill-mirror-reminder.js +33 -0
- package/dist/commands/skill-mirror-reminder.js.map +1 -0
- package/dist/hook.js +193 -11
- package/dist/hook.js.map +1 -1
- package/dist/opencode-plugin.bundle.js +25 -1
- package/dist/procedure-watch.d.ts +153 -0
- package/dist/procedure-watch.js +349 -0
- package/dist/procedure-watch.js.map +1 -0
- package/dist/skill-mirror-client.d.ts +37 -0
- package/dist/skill-mirror-client.js +172 -0
- package/dist/skill-mirror-client.js.map +1 -0
- package/package.json +1 -1
package/dist/commands/load.d.ts
CHANGED
|
@@ -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 (
|
|
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
|
-
*
|
|
13
|
-
* package `files` array
|
|
14
|
-
*
|
|
15
|
-
|
|
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>;
|
package/dist/commands/load.js
CHANGED
|
@@ -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 (
|
|
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
|
-
*
|
|
14
|
-
* package `files` array
|
|
15
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
93
|
-
|
|
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":";AAAA
|
|
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
|
|
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":";AAAA;;;;kEAIkE;;;
|
|
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
|
|
1465
|
-
*
|
|
1466
|
-
*
|
|
1467
|
-
*
|
|
1468
|
-
*
|
|
1469
|
-
*
|
|
1470
|
-
*
|
|
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
|
-
|
|
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
|
-
|
|
1483
|
-
|
|
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
|
-
|
|
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
|
}
|