greprag 5.80.0 → 5.82.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 (63) hide show
  1. package/dist/capture-manifest.js +2 -1
  2. package/dist/codex-fast-hook.js +6 -0
  3. package/dist/codex-steering.js +1 -1
  4. package/dist/commands/app-model.js +0 -1
  5. package/dist/commands/arm-reminder.js +9 -7
  6. package/dist/commands/collision-check.js +7 -6
  7. package/dist/commands/corpus/client.js +13 -3
  8. package/dist/commands/delivery-reminder.js +35 -14
  9. package/dist/commands/deploy-gate.js +55 -0
  10. package/dist/commands/deploy-lock.js +100 -0
  11. package/dist/commands/deploy-record.js +145 -0
  12. package/dist/commands/deploy-verify.js +111 -0
  13. package/dist/commands/inbox-primer-reminder.js +5 -5
  14. package/dist/commands/inbox-watch.js +2 -4
  15. package/dist/commands/init.js +82 -0
  16. package/dist/commands/load.js +40 -0
  17. package/dist/commands/loadout-reminder.js +1 -1
  18. package/dist/commands/merge-guard.js +419 -0
  19. package/dist/commands/merge-lock.js +176 -0
  20. package/dist/commands/parity-reminder.js +53 -0
  21. package/dist/commands/persona-reminder.js +11 -0
  22. package/dist/commands/persona.js +50 -0
  23. package/dist/commands/procedure.js +77 -6
  24. package/dist/commands/reminder-registry.js +21 -5
  25. package/dist/commands/repodoc.js +433 -0
  26. package/dist/commands/search.js +149 -0
  27. package/dist/commands/skillgain.js +33 -25
  28. package/dist/delivery-lifecycle.js +16 -1
  29. package/dist/deploy-gate.js +355 -0
  30. package/dist/deploy-locks.js +339 -0
  31. package/dist/deploy-verify.js +209 -0
  32. package/dist/env-redaction.js +157 -0
  33. package/dist/harness-limits.js +17 -0
  34. package/dist/hook-runtime.js +11 -1
  35. package/dist/hook.js +170 -88
  36. package/dist/index.js +593 -567
  37. package/dist/inline-atom-episode.js +15 -7
  38. package/dist/inline-atom.js +8 -2
  39. package/dist/native-skill-adoption.js +11 -0
  40. package/dist/native-skill-mirror.js +8 -1
  41. package/dist/node-identity.bundle.js +1166 -0
  42. package/dist/opencode-plugin.bundle.js +307 -119
  43. package/dist/procedure-enabled.js +55 -0
  44. package/dist/procedure-runtime.js +6 -0
  45. package/dist/procedure-scope.js +190 -0
  46. package/dist/procedure-watch.js +29 -16
  47. package/dist/procedure.js +111 -5
  48. package/dist/project-anchor.js +1 -14
  49. package/dist/reminder-injector.js +11 -10
  50. package/dist/repodoc-client.js +296 -0
  51. package/dist/session-id.js +7 -8
  52. package/dist/skill-landing.js +57 -2
  53. package/dist/skill-mirror-client.js +14 -0
  54. package/dist/skill-mirror-files.js +18 -0
  55. package/package.json +2 -2
  56. package/scripts/bundle-node-identity.mjs +47 -0
  57. package/skill/templates/chip-spawn.md +7 -1
  58. package/skill/templates/delivery.md +105 -0
  59. package/skill/templates/prompt-audit.md +196 -0
  60. package/skill/templates/skill-change.md +25 -2
  61. package/dist/assistant-doctrine.js +0 -85
  62. package/dist/commands/assistant-reminder.js +0 -19
  63. package/dist/commands/assistant.js +0 -95
@@ -38,8 +38,38 @@ var __importStar = (this && this.__importStar) || (function () {
38
38
  };
39
39
  })();
40
40
  Object.defineProperty(exports, "__esModule", { value: true });
41
+ exports.fitPersonaAnnounce = fitPersonaAnnounce;
41
42
  exports.runPersona = runPersona;
42
43
  const fs = __importStar(require("fs"));
44
+ const harness_limits_1 = require("../harness-limits");
45
+ /** Last line of defence against a silently half-delivered Persona.
46
+ *
47
+ * `validatePersonaContent` rejects an oversize persona at set time, but rows
48
+ * written before that check existed are still in the table and the column has no
49
+ * DB constraint — so a rendered announce can still arrive over the cap. The
50
+ * harness's own handling is the failure this ADR exists to end: it keeps the
51
+ * first 2KB, drops the rest, and says nothing.
52
+ *
53
+ * So the hook cuts it deliberately and SAYS SO. A session that knows its
54
+ * speaking rules are incomplete, and where to read the rest, is strictly better
55
+ * off than one silently obeying half of them — and better off than one given
56
+ * nothing, since a truncated persona still carries whatever the operator put
57
+ * first. adr: adr/persona.md */
58
+ function fitPersonaAnnounce(announce, cap = harness_limits_1.ANNOUNCE_HARNESS_CAP) {
59
+ if (Buffer.byteLength(announce, 'utf8') <= cap)
60
+ return announce;
61
+ const notice = '\n[/Persona]\n[greprag: persona TRUNCATED — the stored persona is larger than the '
62
+ + `${cap} bytes this harness inlines per hook, so the rules above are INCOMPLETE. `
63
+ + 'Read the whole thing with `greprag persona show`.]';
64
+ const room = cap - Buffer.byteLength(notice, 'utf8');
65
+ // Slicing bytes can land mid-codepoint; a trailing U+FFFD is the evidence.
66
+ let body = Buffer.from(announce, 'utf8').subarray(0, room).toString('utf8').replace(/�+$/, '');
67
+ // Prefer a whole-line cut, but never throw away most of the persona to get one.
68
+ const nl = body.lastIndexOf('\n');
69
+ if (nl > room * 0.5)
70
+ body = body.slice(0, nl);
71
+ return body + notice;
72
+ }
43
73
  function getConfig() {
44
74
  return {
45
75
  apiUrl: process.env.GREPRAG_API_URL || 'https://api.greprag.com',
@@ -161,9 +191,29 @@ async function show() {
161
191
  }
162
192
  const source = p.mode === 'template' ? `template ${p.templateId}` : 'custom';
163
193
  console.log(`Persona (${source})`);
194
+ const warning = deliveryWarning(data.announce);
195
+ if (warning)
196
+ console.log(warning);
164
197
  console.log('');
165
198
  console.log(p.content || '');
166
199
  }
200
+ /** Say plainly when what is STORED is bigger than what is DELIVERED.
201
+ *
202
+ * The server returns the rendered announce — content plus its provenance
203
+ * envelope — and that is the string the harness caps. A row written before the
204
+ * set-time limit existed can still be over it, and until now `show` printed it
205
+ * in full as if all of it reached a session. adr: adr/persona.md */
206
+ function deliveryWarning(announce) {
207
+ if (typeof announce !== 'string' || !announce)
208
+ return null;
209
+ const bytes = Buffer.byteLength(announce, 'utf8');
210
+ if (bytes <= harness_limits_1.ANNOUNCE_HARNESS_CAP)
211
+ return null;
212
+ return `⚠ OVER THE DELIVERY LIMIT — this persona renders to ${bytes} bytes, but the harness `
213
+ + `inlines only ${harness_limits_1.ANNOUNCE_HARNESS_CAP} per hook. About ${bytes - harness_limits_1.ANNOUNCE_HARNESS_CAP} bytes `
214
+ + 'never reach a session; what does arrive is marked as truncated. Shorten it with '
215
+ + '`greprag persona set`.';
216
+ }
167
217
  async function clear() {
168
218
  const { ok, status, data } = await api('DELETE', '/v1/persona');
169
219
  if (!ok)
@@ -7,6 +7,8 @@
7
7
  * show <verb> — print one procedure + effective runtime behavior
8
8
  * seed [--git] — write the git-ecosystem seed set (commit/merge/push/deploy/release)
9
9
  * register --verb V --steps "..." [--caveats "..."] [--endpoint "..."] [--trigger T ...] [--destructive]
10
+ * — non-lifecycle verbs only; a lifecycle verb is refused, never
11
+ * half-written (the seed refresh would reclaim the row on read)
10
12
  * rm <verb> — remove a procedure
11
13
  * match "<text>" — dry-run: effective prompt-boundary behavior
12
14
  *
@@ -19,7 +21,9 @@ exports.runProcedure = runProcedure;
19
21
  const procedure_1 = require("../procedure");
20
22
  const procedure_run_1 = require("../procedure-run");
21
23
  const procedure_runtime_1 = require("../procedure-runtime");
24
+ const delivery_lifecycle_1 = require("../delivery-lifecycle");
22
25
  const project_anchor_1 = require("../project-anchor");
26
+ const procedure_enabled_1 = require("../procedure-enabled");
23
27
  function getFlag(args, flag) {
24
28
  const idx = args.indexOf(flag);
25
29
  if (idx === -1 || idx + 1 >= args.length)
@@ -64,6 +68,11 @@ function runtimeState(proc, install) {
64
68
  return (0, procedure_runtime_1.procedureRuntimeState)(install.state, proc);
65
69
  }
66
70
  function printRuntimeDisposition(proc, install) {
71
+ // adr: adr/procedure-system-off.md
72
+ if (!procedure_enabled_1.PROCEDURE_SYSTEM_ENABLED) {
73
+ console.log('Inert. The Procedure System is off, so the prompt boundary injects nothing for any verb.');
74
+ return true;
75
+ }
67
76
  const state = runtimeState(proc, install);
68
77
  if (state === 'active')
69
78
  return false;
@@ -108,6 +117,17 @@ function printInstall(proc, install) {
108
117
  if (install.ratifiedAt)
109
118
  console.log(` ratified: ${install.ratifiedAt} (${install.ratifiedBy || 'operator'})`);
110
119
  }
120
+ /** Did the store honour exactly what `register` asked for? Compares only the
121
+ * register-owned fields (the store stamps `updatedAt` of its own accord).
122
+ * The invariant: the CLI never reports a write it will not honour. */
123
+ function storedMatchesRegistration(stored, asked) {
124
+ return stored.steps === asked.steps
125
+ && (stored.caveats ?? '') === (asked.caveats ?? '')
126
+ && (stored.endpoint ?? '') === (asked.endpoint ?? '')
127
+ && stored.status === asked.status
128
+ && !!stored.destructive === !!asked.destructive
129
+ && JSON.stringify(stored.triggers) === JSON.stringify(asked.triggers);
130
+ }
111
131
  function installForState(proc, current, state, settings = current.settings) {
112
132
  const now = new Date().toISOString();
113
133
  return {
@@ -121,6 +141,8 @@ function installForState(proc, current, state, settings = current.settings) {
121
141
  };
122
142
  }
123
143
  async function runProcedure(args) {
144
+ if (!procedure_enabled_1.PROCEDURE_SYSTEM_ENABLED)
145
+ console.log(procedure_enabled_1.PROCEDURE_SYSTEM_OFF_NOTE + '\n');
124
146
  const sub = args[0];
125
147
  const rest = args.slice(1);
126
148
  const { projectId, projectName } = resolveProjectId();
@@ -390,13 +412,16 @@ async function runProcedure(args) {
390
412
  // --git is the only seed set today; default to it so bare `seed` works.
391
413
  const store = (0, procedure_1.readProcedureStore)(projectId);
392
414
  let next = store;
393
- for (const p of procedure_1.GIT_SEED_SET) {
415
+ const seeds = procedure_1.GIT_SEED_SET;
416
+ for (const p of seeds) {
394
417
  next = (0, procedure_1.upsertProcedure)(next, p, /* force */ true);
395
418
  }
396
419
  (0, procedure_1.writeProcedureStore)(projectId, next);
397
- console.log(`Seeded ${procedure_1.GIT_SEED_SET.length} git-ecosystem procedures for ${projectName}:`);
398
- for (const p of procedure_1.GIT_SEED_SET)
399
- console.log(` • ${p.verb}${p.destructive ? ' (destructive — seed-only)' : ''}`);
420
+ console.log(`Seeded ${seeds.length} git-ecosystem procedures for ${projectName}:`);
421
+ for (const p of seeds) {
422
+ const note = p.destructive ? ' (destructive — seed-only)' : '';
423
+ console.log(` • ${p.verb}${note}`);
424
+ }
400
425
  console.log(`\n store: ${(0, procedure_1.procedureStorePath)(projectId)}`);
401
426
  return;
402
427
  }
@@ -407,6 +432,39 @@ async function runProcedure(args) {
407
432
  console.error('Usage: greprag procedure register --verb <v> --steps "<...>" [--caveats "<...>"] [--endpoint "<...>"] [--trigger <t> ...] [--destructive]');
408
433
  process.exit(1);
409
434
  }
435
+ // A delivery-owned verb cannot carry a recipe. Its text belongs to the
436
+ // repo's Delivery Profile document, and a copy here forks from that
437
+ // document the moment the document is corrected — with nothing to surface
438
+ // the divergence. The bundled definition reclaims the row on the next read
439
+ // either way, so refuse the write rather than confirm one that will not
440
+ // survive. adr: adr/delivery-owned-procedures.md
441
+ if ((0, delivery_lifecycle_1.isDeliveryOwnedVerb)(verb)) {
442
+ const lifecycle = (0, delivery_lifecycle_1.isDeliveryLifecycleVerb)(verb);
443
+ console.error(`Refusing to register "${verb}" — nothing was written.`);
444
+ if (lifecycle) {
445
+ console.error(` The lifecycle verbs (${delivery_lifecycle_1.DELIVERY_LIFECYCLE_VERBS.join(', ')}) are vocabulary-only during the`);
446
+ console.error(' announce-first Delivery System pilot. Procedure records shadow evidence for them and');
447
+ console.error(' never injects steps, and the bundled seed definition reclaims the row on the next read.');
448
+ }
449
+ else {
450
+ console.error(` "${verb}" is delivery-owned: its recipe is a section of this repo's Delivery Profile`);
451
+ console.error(' document, and Procedure injects a live pointer to that section rather than a copy.');
452
+ console.error(' Correcting the document is what changes what a session is told — there is no');
453
+ console.error(' registered text to keep in sync, which is exactly why this write is refused.');
454
+ }
455
+ console.error(` Put the ${verb} recipe in this repo's Delivery Profile document instead:`);
456
+ console.error(' greprag delivery resolve # names the profile + its documents');
457
+ console.error(' See docs/procedure-system.md (Seeding) and docs/delivery-profiles.md.');
458
+ process.exit(1);
459
+ }
460
+ // Every other verb: the system is off, so a registered recipe would never
461
+ // reach a session, and the CLI never reports a write it will not honour.
462
+ // adr: adr/procedure-system-off.md
463
+ if (!procedure_enabled_1.PROCEDURE_SYSTEM_ENABLED) {
464
+ console.error(`Refusing to register "${verb}" — nothing was written.`);
465
+ console.error(` ${procedure_enabled_1.PROCEDURE_SYSTEM_OFF_NOTE}`);
466
+ process.exit(1);
467
+ }
410
468
  const triggers = collectFlags(rest, '--trigger');
411
469
  const proc = {
412
470
  verb,
@@ -419,8 +477,21 @@ async function runProcedure(args) {
419
477
  };
420
478
  const store = (0, procedure_1.readProcedureStore)(projectId);
421
479
  (0, procedure_1.writeProcedureStore)(projectId, (0, procedure_1.upsertProcedure)(store, proc, /* force */ true));
480
+ // Report the STORED row, never the in-memory candidate. Echoing the
481
+ // candidate is what let a discarded write look like a success.
482
+ const after = (0, procedure_1.readProcedureStore)(projectId);
483
+ const stored = after.procedures.find(p => p.verb === verb);
484
+ if (!stored || !storedMatchesRegistration(stored, proc)) {
485
+ console.error(`Registered "${verb}" did not survive the store round-trip — the definition was NOT honoured.`);
486
+ if (stored) {
487
+ console.error(' the store now holds:');
488
+ printProcedure(stored, after);
489
+ }
490
+ console.error(` store: ${(0, procedure_1.procedureStorePath)(projectId)}`);
491
+ process.exit(1);
492
+ }
422
493
  console.log(`Registered procedure "${verb}" for ${projectName}.`);
423
- printProcedure(proc);
494
+ printProcedure(stored, after);
424
495
  return;
425
496
  }
426
497
  case 'rm': {
@@ -476,7 +547,7 @@ async function runProcedure(args) {
476
547
  console.log(' run-status <id> show latest run + evidence');
477
548
  console.log(' complete <id> <outcome> close the current run');
478
549
  console.log(' seed --git write the git-ecosystem seed set');
479
- console.log(' register --verb ... hand-roll a procedure');
550
+ console.log(' register --verb ... hand-roll a procedure (lifecycle verbs are refused — see the Delivery Profile)');
480
551
  console.log(' rm <verb> remove a procedure');
481
552
  console.log(' match "<text>" dry-run effective prompt-boundary behavior');
482
553
  return;
@@ -5,7 +5,7 @@
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.ANNOUNCE_HARNESS_CAP = exports.ANNOUNCE_INLINE_BUDGET = exports.compactReannounceModules = exports.commandModules = exports.promptModules = exports.harnessModules = exports.REGISTRY = void 0;
8
+ exports.ANNOUNCE_POINTER_RESERVE = exports.ANNOUNCE_HARNESS_CAP = exports.ANNOUNCE_INLINE_BUDGET = exports.compactReannounceModules = exports.commandModules = exports.promptModules = exports.harnessModules = exports.REGISTRY = void 0;
9
9
  exports.collectReminders = collectReminders;
10
10
  exports.bootOrder = bootOrder;
11
11
  exports.collectAnnounces = collectAnnounces;
@@ -18,7 +18,6 @@ const email_primer_reminder_1 = require("./email-primer-reminder");
18
18
  const corpus_reminder_1 = require("./corpus-reminder");
19
19
  const doc_pointer_reminder_1 = require("./doc-pointer-reminder");
20
20
  const setup_reminder_1 = require("./setup-reminder");
21
- const assistant_reminder_1 = require("./assistant-reminder");
22
21
  const memory_reflex_1 = require("./memory-reflex");
23
22
  const arm_reminder_1 = require("./arm-reminder");
24
23
  const friction_reminder_1 = require("./friction-reminder");
@@ -30,6 +29,7 @@ const procedure_reminder_1 = require("./procedure-reminder");
30
29
  const loadout_reminder_1 = require("./loadout-reminder");
31
30
  const delivery_reminder_1 = require("./delivery-reminder");
32
31
  const persona_reminder_1 = require("./persona-reminder");
32
+ const parity_reminder_1 = require("./parity-reminder");
33
33
  /** Registry order = display order. THE single agent-facing announce/reminder assembly:
34
34
  * the hook does I/O → fills ReminderEnv → collectAnnounces (SessionStart) / collectReminders
35
35
  * (per turn) render every module here in this order. A module may own either surface or both;
@@ -49,11 +49,11 @@ exports.REGISTRY = [
49
49
  procedure_reminder_1.procedureAnnounceModule, // active project procedures, rendered generically from operator-owned installs
50
50
  delivery_reminder_1.deliveryControlModule, // repo-scoped announce-first Delivery System pilot (all harnesses)
51
51
  persona_reminder_1.personaAnnounceModule, // tenant-scoped Persona, shared across projects/harnesses
52
+ parity_reminder_1.parityPendingModule, // what THIS harness still owes when another one advanced
52
53
  loadout_reminder_1.loadoutRegistrarModule, // equipped loadouts: one advisor announce each + per-turn keyword Match (docs/loadout.md)
53
54
  setup_reminder_1.setupWarningModule,
54
55
  version_reminder_1.versionUpgradeModule, // Deficiency-gated announce — silent unless a newer release exists
55
56
  enrichment_health_reminder_1.enrichmentHealthModule, // Deficiency-gated announce — silent unless the Gemini probe fails (fix c2eb8777)
56
- assistant_reminder_1.assistantDoctrineModule,
57
57
  memory_reflex_1.memoryPrimerModule,
58
58
  arm_reminder_1.watcherArmModule,
59
59
  friction_reminder_1.mechanicFrictionModule,
@@ -178,8 +178,21 @@ function collectAnnounceBlocks(env, registry = exports.REGISTRY) {
178
178
  exports.ANNOUNCE_INLINE_BUDGET = 1650;
179
179
  /** The harness's hard ceiling. Past this, output is replaced by a 2KB preview and
180
180
  * a `<persisted-output>` file reference. ANNOUNCE_INLINE_BUDGET must stay under
181
- * it with room for the trailing memory-recap pointer. */
182
- exports.ANNOUNCE_HARNESS_CAP = 2048;
181
+ * it with room for the trailing memory-recap pointer.
182
+ *
183
+ * Re-exported, not redeclared: the number now has one owner in
184
+ * `@greprag/core`'s `harness-limits`, mirrored into the CLI at
185
+ * `src/harness-limits.ts`. The server derives the Persona content limit from
186
+ * the same value, and a second literal is how `PERSONA_MAX_CHARS = 12_000`
187
+ * became a limit that governed nothing. */
188
+ var harness_limits_1 = require("../harness-limits");
189
+ Object.defineProperty(exports, "ANNOUNCE_HARNESS_CAP", { enumerable: true, get: function () { return harness_limits_1.ANNOUNCE_HARNESS_CAP; } });
190
+ /** Bytes the overflow pointer line costs, reserved before fitting so adding it can
191
+ * never be what pushes the payload over the cap. Declared here beside the budget
192
+ * because the `mustInline` guard has to charge a module the same reserve the hook
193
+ * does — a module sized against the raw budget would still be dropped in the field.
194
+ * adr: adr/announce-inline-budget.md */
195
+ exports.ANNOUNCE_POINTER_RESERVE = 220;
183
196
  /** Inline-worthiness ranking, most-keepable first. The rule: a block earns inline
184
197
  * space when it is SHORT, SESSION-SPECIFIC, and available nowhere else. A block
185
198
  * that is long, static, and already loadable on demand does not — it is exactly
@@ -191,6 +204,9 @@ exports.ANNOUNCE_HARNESS_CAP = 2048;
191
204
  * until now consumed the entire budget and starved everything behind it. */
192
205
  const ANNOUNCE_PRIORITY = [
193
206
  'persona-announce',
207
+ // Work this harness owes. Deficiency-gated, so it is silent almost always — and
208
+ // when it does fire it is the most actionable thing in the announce.
209
+ 'parity-pending',
194
210
  'setup-warning',
195
211
  'version-upgrade',
196
212
  'enrichment-health',