greprag 5.83.2 → 5.85.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 (35) hide show
  1. package/dist/commands/agent-doctrine-file.js +5 -128
  2. package/dist/commands/agents.js +125 -846
  3. package/dist/commands/app-model.js +1 -1
  4. package/dist/commands/delivery-reminder.js +1 -1
  5. package/dist/commands/discord.js +12 -61
  6. package/dist/commands/inbox-watch.js +0 -13
  7. package/dist/commands/init.js +17 -29
  8. package/dist/commands/load.js +15 -35
  9. package/dist/commands/parity.js +1 -1
  10. package/dist/commands/reminder-registry.js +1 -8
  11. package/dist/commands/session.js +2 -2
  12. package/dist/commands/skill-mirror-reminder.js +1 -19
  13. package/dist/commands/skill.js +236 -448
  14. package/dist/hook.js +31 -54
  15. package/dist/index.js +19 -152
  16. package/dist/native-adapter-refresh.js +15 -18
  17. package/dist/native-skill-mirror.js +11 -89
  18. package/dist/opencode-plugin.bundle.js +82 -282
  19. package/dist/reminder-injector.js +1 -1
  20. package/dist/skill-activation-manifest.js +14 -37
  21. package/dist/skill-git-ingest.js +297 -0
  22. package/dist/skill-landing.js +7 -173
  23. package/dist/skill-mirror-client.js +4 -251
  24. package/dist/skill-staleness.js +3 -6
  25. package/package.json +1 -1
  26. package/skill/commander/SKILL.md +10 -12
  27. package/skill/greprag/SKILL.md +1 -1
  28. package/skill/greprag/docs/discord-handoff.md +6 -30
  29. package/skill/mechanic/docs/skill-fix-conventions.md +1 -1
  30. package/skill/templates/chip-spawn.md +6 -2
  31. package/skill/templates/delivery.md +16 -0
  32. package/skill/templates/os.md +1 -1
  33. package/skill/templates/prompt-audit.md +26 -49
  34. package/skill/templates/skill-change.md +15 -11
  35. package/dist/commands/sms.js +0 -215
@@ -292,7 +292,7 @@ async function buildAppModel(cwd, requestedProjectId, homeDir = os.homedir()) {
292
292
  group: 'Other',
293
293
  }),
294
294
  enabled: !disabled.has(module.id),
295
- surface: module.source === 'command' ? 'Before sensitive actions' : 'Agent guidance',
295
+ surface: 'Agent guidance',
296
296
  harnesses: module.harnesses || ['claude-code', 'codex', 'opencode', 'grok'],
297
297
  })),
298
298
  },
@@ -50,7 +50,7 @@ function buildDeliveryAnnounce(env) {
50
50
  `[DELIVERY — ${project}]`,
51
51
  'Commit useful, passing work. Only committed state delivers; ignore uncommitted and untracked work in every checkout.',
52
52
  coordinationLine(env.platform),
53
- 'Land every ready same-repo commit: update the default branch, rebase your worktree onto its tip, retest there, take greprag merge-lock in the canonical checkout, recheck the tip, merge --ff-only, release --landed. Tip moved? Free the lock and prepare again. Never rebase a shared default branch.',
53
+ 'Land every ready same-repo commit: update the default branch, rebase your worktree onto its tip, retest there, take greprag merge-lock in the canonical checkout, recheck the tip, merge --ff-only, release --landed. Tip moved? Free the lock and prepare again. A chip\'s branch arrives rebased by the chip: only fast-forward it; refused, free the lock and send it back. Never rebase in another session\'s worktree or a shared default branch.',
54
54
  'Deploy through this repo\'s delivery profile, from its canonical primary checkout unless the profile says otherwise. Worktrees build; they do not deploy. Then verify production serves the commit you shipped.',
55
55
  'THE GOAL ALREADY AUTHORIZES ALL OF THIS. Do not stop to ask permission to commit, merge, or deploy. Separate approval is for effects outside code delivery: destructive data, secrets, money, customer-facing messages.',
56
56
  'Why each rule, and the details: `greprag load delivery`.',
@@ -1,21 +1,21 @@
1
1
  "use strict";
2
- /** greprag discord <pair|unpair|me|handoff|unhandoff> — Discord bot CLI.
2
+ /** greprag discord <pair|unpair|me|typing> — Discord bot CLI.
3
3
  *
4
4
  * pair POSTs /v1/discord/pair with the current project anchor; prints
5
5
  * the 6-char code + DM-the-bot instructions.
6
6
  * unpair DELETEs /v1/discord/pair.
7
- * me GETs /v1/discord/me — pairing + handoff state.
8
- * handoff POSTs /v1/discord/handoff — pins DMs to (current project,
9
- * current session) for a TTL window. Bot DMs a confirmation.
10
- * unhandoff DELETEs /v1/discord/handoff.
7
+ * me GETs /v1/discord/me — pairing state.
8
+ * typing POSTs /v1/discord/typing — refreshes the typing indicator.
9
+ *
10
+ * Sessions DM the operator with `greprag send --to discord:<snowflake>`.
11
+ * Nothing reads DMs sent to the bot: the Front Desk and the handoff pin were
12
+ * retired 2026-10-06, so the bot answers each one with a fixed line.
11
13
  */
12
14
  Object.defineProperty(exports, "__esModule", { value: true });
13
15
  exports.DISCORD_HELP = void 0;
14
16
  exports.callDiscordPair = callDiscordPair;
15
17
  exports.callDiscordUnpair = callDiscordUnpair;
16
18
  exports.callDiscordMe = callDiscordMe;
17
- exports.callDiscordHandoff = callDiscordHandoff;
18
- exports.callDiscordUnhandoff = callDiscordUnhandoff;
19
19
  exports.callDiscordTyping = callDiscordTyping;
20
20
  async function callDiscordPair(deps, projectId) {
21
21
  const f = deps.fetchImpl ?? fetch;
@@ -45,30 +45,6 @@ async function callDiscordMe(deps) {
45
45
  });
46
46
  return (await res.json());
47
47
  }
48
- async function callDiscordHandoff(deps, args) {
49
- const f = deps.fetchImpl ?? fetch;
50
- const res = await f(`${deps.apiUrl}/v1/discord/handoff`, {
51
- method: 'POST',
52
- headers: {
53
- 'Authorization': `Bearer ${deps.apiKey}`,
54
- 'Content-Type': 'application/json',
55
- },
56
- body: JSON.stringify({
57
- project_id: args.projectId,
58
- session_id: args.sessionId,
59
- ttl_minutes: args.ttlMinutes,
60
- }),
61
- });
62
- return (await res.json());
63
- }
64
- async function callDiscordUnhandoff(deps) {
65
- const f = deps.fetchImpl ?? fetch;
66
- const res = await f(`${deps.apiUrl}/v1/discord/handoff`, {
67
- method: 'DELETE',
68
- headers: { 'Authorization': `Bearer ${deps.apiKey}` },
69
- });
70
- return (await res.json());
71
- }
72
48
  async function callDiscordTyping(deps) {
73
49
  const f = deps.fetchImpl ?? fetch;
74
50
  const res = await f(`${deps.apiUrl}/v1/discord/typing`, {
@@ -78,44 +54,19 @@ async function callDiscordTyping(deps) {
78
54
  return (await res.json());
79
55
  }
80
56
  exports.DISCORD_HELP = `
81
- greprag discord — Discord DM bridge to your greprag inbox.
57
+ greprag discord — let your sessions DM you on Discord.
82
58
 
83
- Pair once, then DM the bot to talk to the agents listening in your project.
84
- Handoff temporarily redirects DMs to a specific session (e.g. while you step
85
- away from the desk and want to keep the same thread alive on your phone).
59
+ Pair once; then any session can message you (see Outbound below). The bot does
60
+ not read DMs you send it: the Front Desk was retired 2026-10-06.
86
61
 
87
62
  Subcommands:
88
63
  greprag discord pair Generate a 15-min code; DM @greprag-bot
89
64
  /pair <code> to link this Discord account
90
65
  to the current project.
91
66
  greprag discord unpair Remove the pairing.
92
- greprag discord me Show current pairing + handoff state.
93
- greprag discord handoff [--ttl N] Pin DMs to (current project, current
94
- session) for N minutes (default 60,
95
- max 1440). Bot DMs you a confirmation,
96
- then this command BLOCKS and streams
97
- inbound DMs to stdout (one per line)
98
- until killed. Run it under Claude's
99
- Monitor tool: each line = one
100
- notification right back into the
101
- agent's turn. Sliding TTL: each
102
- inbound DM extends the pin by >=30 min.
103
- [--session <8-hex>] Override session id (default: CLAUDE_SESSION_ID).
104
- [--project <name>] Override project (default: cwd anchor).
105
- [--orchestrator] Tenant-wide watcher — sees inbox traffic
106
- from any project under this tenant. The
107
- DM pin still routes here, but the agent
108
- has portfolio-wide eyes. Use when this
109
- session is the user's orchestrator
110
- (/business-advisor loaded, etc.).
111
- [--json] Emit raw JSON per line (for Monitor).
112
- [--no-watch] Pin and exit — skip the inline watcher.
113
- For scripted use; loses the live DM stream.
114
- greprag discord unhandoff Clear the active handoff — DMs revert
115
- to default project.
67
+ greprag discord me Show current pairing state.
116
68
  greprag discord typing Refresh the "typing..." indicator on
117
- Discord for ~10s. Auto-fires on every
118
- inbound DM; call manually during long
69
+ Discord for ~10s. Call it during long
119
70
  agent turns so the user sees activity
120
71
  instead of silence.
121
72
 
@@ -251,19 +251,6 @@ function printMessage(msg) {
251
251
  function emitDmReplyDirective(msg, session) {
252
252
  if (!session)
253
253
  return;
254
- // Front Desk Switchboard relay: the operator reached THIS session through the
255
- // receptionist and `references.reply_to` is the rail to answer on. Fires for
256
- // ANY message_type (the relay uses plain 'message'), and takes precedence over
257
- // the legacy Discord-DM path. Without this the session falls back to the bare
258
- // sender handle (travis@greprag.com) — the reply lands in MAIN, never reaching
259
- // the operator on Discord/SMS. Live failure 2026-06-08. spec:
260
- // docs/front-desk-switchboard.md §4 (return leg).
261
- const replyTo = msg.references?.reply_to;
262
- if (replyTo) {
263
- console.log(`[greprag] Operator via front desk — answer now, even mid-task: greprag send --to ${replyTo} "…" ` +
264
- `(exact addr; bare handle is lost). If they reply inline here, don't echo to Discord/SMS.`);
265
- return;
266
- }
267
254
  if (msg.message_type !== 'discord_dm')
268
255
  return;
269
256
  const snowflake = msg.references?.discord?.snowflake ?? '<snowflake>';
@@ -74,29 +74,20 @@ async function trySyncNativeSkillMirror(apiKey, platform, cwd = process.cwd()) {
74
74
  return `Native skill mirror unavailable; existing skills preserved (${message})`;
75
75
  }
76
76
  }
77
- async function tryBootstrapAgentDoctrine(apiKey, harness) {
77
+ /** Render the Codex/OpenCode AGENTS.md files from ~/.claude/CLAUDE.md (the git
78
+ * source) and say what happened. adr: adr/skill-ownership-git.md */
79
+ function renderInstructionsSummary() {
78
80
  try {
79
- return await (0, agents_1.bootstrapAgentDoctrine)(apiKey, harness);
81
+ const results = (0, agents_1.renderGlobalInstructions)();
82
+ if (results.every(r => r.status === 'no-source'))
83
+ return 'AGENTS.md render: no ~/.claude/CLAUDE.md yet';
84
+ const written = results.filter(r => r.status === 'written').length;
85
+ return written ? `AGENTS.md render: ${written} file(s) rewritten from ~/.claude/CLAUDE.md` : 'AGENTS.md render: current';
80
86
  }
81
87
  catch (error) {
82
- const message = error instanceof Error ? error.message : String(error);
83
- console.warn(` GrepRAG Instructions materialization skipped: ${message}`);
84
- return null;
88
+ return `AGENTS.md render skipped: ${error instanceof Error ? error.message : String(error)}`;
85
89
  }
86
90
  }
87
- function doctrineBootstrapSummary(result) {
88
- if (!result)
89
- return 'GrepRAG Instructions: materialization unavailable; existing files preserved';
90
- if (result.canonicalScopes.length === 0)
91
- return 'GrepRAG Instructions: no canonical scopes adopted yet';
92
- const state = result.changedFollowers > 0
93
- ? `${result.changedFollowers} follower(s) materialized`
94
- : `${result.currentFollowers} follower(s) already current`;
95
- const blocked = result.blockedFollowers > 0
96
- ? `; ${result.blockedFollowers} existing follower(s) preserved (run greprag instructions push explicitly)`
97
- : '';
98
- return `GrepRAG Instructions (${result.canonicalScopes.join(' + ')}): ${state}${blocked}`;
99
- }
100
91
  function hasEnvPrefix(prefix) {
101
92
  return Object.keys(process.env).some(k => k.startsWith(prefix));
102
93
  }
@@ -344,13 +335,11 @@ async function runInit(opts) {
344
335
  // chip-spawn POINTER announce names the trigger + the command). One less
345
336
  // install-time file write, and it works in every project, not just where the
346
337
  // file happened to be installed.
347
- // Step 7: Materialize canonical doctrine for Claude. Once global canon
348
- // exists, it owns ~/.claude/CLAUDE.md; running the legacy direct patch after
349
- // sync would immediately diverge the materialized follower. Pre-adoption
350
- // tenants retain the old patch path unchanged.
351
- const doctrine = await tryBootstrapAgentDoctrine(apiKey, 'claude-code');
352
- changes.push(doctrineBootstrapSummary(doctrine));
353
- if (!doctrine?.canonicalScopes.includes('global')) {
338
+ // Step 7: ~/.claude/CLAUDE.md. A file committed in git is its owner's to
339
+ // edit, so init leaves it alone; an untracked one gets the greprag CLI
340
+ // section as before. adr: adr/skill-ownership-git.md
341
+ changes.push(renderInstructionsSummary());
342
+ if ((0, agents_1.committedInstructionSource)() === null) {
354
343
  const claudeMdPatched = patchGlobalClaudeMd();
355
344
  changes.push(claudeMdPatched
356
345
  ? 'Patched ~/.claude/CLAUDE.md greprag CLI section'
@@ -525,13 +514,13 @@ async function runOpenCodeInit(opts) {
525
514
  console.warn(' Plugin bundle not found (build issue) — run `npm run build` in packages/cli and retry');
526
515
  }
527
516
  envChanges.push(await trySyncNativeSkillMirror(apiKey, 'opencode'));
528
- const doctrine = await tryBootstrapAgentDoctrine(apiKey, 'opencode');
517
+ const instructionsSummary = renderInstructionsSummary();
529
518
  // Summary
530
519
  console.log('\n Setup complete!\n');
531
520
  for (const c of envChanges)
532
521
  console.log(` - ${c}`);
533
522
  console.log(` - Project anchor: ${anchor.anchorPath} (source: ${anchor.source})`);
534
- console.log(` - ${doctrineBootstrapSummary(doctrine)}`);
523
+ console.log(` - ${instructionsSummary}`);
535
524
  if (pluginInstalled)
536
525
  console.log(` - Plugin: ${pluginMain}`);
537
526
  console.log(` - Shared env: ${getGrepragEnvPath()}`);
@@ -583,8 +572,7 @@ async function runCodexInit(opts) {
583
572
  if (codexSkill)
584
573
  changes.push(`Skill: installed → ${codexSkill}`);
585
574
  changes.push(await trySyncNativeSkillMirror(apiKey, 'codex'));
586
- const doctrine = await tryBootstrapAgentDoctrine(apiKey, 'codex');
587
- changes.push(doctrineBootstrapSummary(doctrine));
575
+ changes.push(renderInstructionsSummary());
588
576
  try {
589
577
  const res = await fetch(`${API_URL}/v1/inbox/projects/register`, {
590
578
  method: 'POST',
@@ -60,7 +60,6 @@ const os = __importStar(require("os"));
60
60
  const harness_1 = require("../harness");
61
61
  const skill_activation_manifest_1 = require("../skill-activation-manifest");
62
62
  const project_anchor_1 = require("../project-anchor");
63
- const skill_landing_1 = require("../skill-landing");
64
63
  const skill_mirror_files_1 = require("../skill-mirror-files");
65
64
  const proc_1 = require("../proc");
66
65
  const skill_staleness_1 = require("../skill-staleness");
@@ -91,7 +90,7 @@ const LIBRARY = {
91
90
  },
92
91
  'skill-change': {
93
92
  files: ['skill/templates/skill-change.md'],
94
- purpose: 'Internal bundled schema for safely updating a skill after a run: when to edit, Convention A/B shapes, and when to propose instead.',
93
+ purpose: 'How to change a skill after a run: edit its git folder and commit (GrepRAG only mirrors it), Convention A/B shapes, and when to propose instead.',
95
94
  },
96
95
  mechanic: {
97
96
  files: ['skill/mechanic/SKILL.md', 'skill/mechanic/docs/skill-fix-conventions.md'],
@@ -109,10 +108,10 @@ const LIBRARY = {
109
108
  files: ['skill/templates/chip-leader-opencode.md'],
110
109
  purpose: 'Plan a MULTI-CHIP mission for opencode (integration branch, seams, merge order). Loaded from chip-bootloader when ≥2 chips are needed.',
111
110
  },
112
- // adr: adr/prompt-audit-canon.md — the audit lands through `instructions pull --push`, never a follower edit.
111
+ // adr: adr/prompt-audit-canon.md — the audit lands as an edit to ~/.claude/CLAUDE.md in git, never to a rendered AGENTS.md.
113
112
  'prompt-audit': {
114
113
  files: ['skill/templates/prompt-audit.md'],
115
- purpose: 'Audit canonical CLAUDE.md/AGENTS.md instructions for dated-model cruft (shipped rubric) and land accepted edits through `instructions pull --push`, never a follower edit.',
114
+ purpose: 'Audit CLAUDE.md instructions for dated-model cruft (shipped rubric) and land accepted edits in ~/.claude/CLAUDE.md (git); the AGENTS.md files are rendered from it.',
116
115
  },
117
116
  };
118
117
  /** Absolute path to a payload shipped in the package. */
@@ -189,7 +188,7 @@ async function printCatalog() {
189
188
  for (const name of names) {
190
189
  console.log(` ${name.padEnd(width)} ${LIBRARY[name].purpose}`);
191
190
  }
192
- console.log(` ${'instructions'.padEnd(width)} Canonical global + repo GrepRAG Instructions; CLAUDE.md/AGENTS.md are rendered followers.`);
191
+ console.log(` ${'instructions'.padEnd(width)} Read-only copy of ~/.claude/CLAUDE.md (git is the source; AGENTS.md files are rendered from it).`);
193
192
  console.log(` ${'agents'.padEnd(width)} Deprecated alias for instructions.`);
194
193
  // Tier-3 section: the operator's own skills, auto-mirrored from use.
195
194
  const data = await mirrorGet('/v1/skillmirror');
@@ -201,20 +200,18 @@ async function printCatalog() {
201
200
  // A mirrored skill with no native adapter on this harness never reaches the
202
201
  // harness's own skill list — the agent can only reach it through this
203
202
  // catalog. Mark the gap here and name the command that closes it.
204
- // A hand-kept copy does reach that list, but the harness runs the copy and
205
- // never sees canon move on, so it is marked too (with `~`).
206
203
  const platform = (0, harness_1.inferCurrentHarness)() || '';
207
204
  const native = platform
208
205
  ? (0, skill_activation_manifest_1.readNativeSkillNames)({ platform, cwd: process.cwd(), homeDir: os.homedir() })
209
206
  : new Map();
210
- const { missing, localCopies } = (0, skill_activation_manifest_1.nativeAdapterGap)(platform, skills, native);
207
+ const { missing } = (0, skill_activation_manifest_1.nativeAdapterGap)(platform, skills, native);
211
208
  const w = Math.max(...skills.map((s) => (s.skillName || '').length), 8);
212
- console.log('\nYour mirrored skills (auto-fresh from use — work on any machine/harness):\n');
209
+ console.log('\nYour mirrored skills (copied from git commits — work on any machine/harness):\n');
213
210
  for (const s of skills) {
214
211
  if (!s.skillName)
215
212
  continue;
216
213
  const scope = s.scope === 'project' && s.projectId ? ` [project:${s.projectId.slice(0, 8)}]` : '';
217
- const gap = missing.includes(s.skillName) ? ' *' : localCopies.includes(s.skillName) ? ' ~' : '';
214
+ const gap = missing.includes(s.skillName) ? ' *' : '';
218
215
  console.log(` ${(s.skillName + scope + gap).padEnd(w + 20)} ${(s.description || '').slice(0, 110)}`);
219
216
  }
220
217
  const installCommand = (0, skill_activation_manifest_1.nativeAdapterInstallCommand)(platform, missing);
@@ -223,13 +220,6 @@ async function printCatalog() {
223
220
  console.log(' in this harness\'s own skill list. Install the adapters with:\n');
224
221
  console.log(` ${installCommand}\n`);
225
222
  }
226
- const cutoverCommand = (0, skill_activation_manifest_1.localCopyCutoverCommand)(platform, localCopies);
227
- if (cutoverCommand) {
228
- console.log(`\n ~ ${localCopies.length} skill(s) run from a hand-kept copy in ${platform}, not an adapter — the harness`);
229
- console.log(' keeps running that copy when the mirrored skill changes. Replacing a copy is a per-skill');
230
- console.log(' call; its SKILL.md is backed up first:\n');
231
- console.log(` ${cutoverCommand}\n`);
232
- }
233
223
  }
234
224
  console.log('\n greprag load <name> print the full entry / mirrored skill');
235
225
  console.log(' greprag load /<name> print a user-named slash skill');
@@ -451,11 +441,13 @@ async function printMirroredSkill(name, args) {
451
441
  // adr: adr/skill-adapter-never-canon.md
452
442
  if ((0, skill_mirror_files_1.isGeneratedSkillAdapter)(file.content)) {
453
443
  const identityArg = args.skillId ? ` --skill-id ${args.skillId}` : '';
454
- console.error(`Mirrored skill "${name}" has no canonical body — the mirror is holding the generated adapter.\n` +
455
- `Refusing to serve it: the adapter is the launcher that sent you here, not the skill.\n\n` +
456
- `Restore the real body from the pre-adoption snapshot:\n` +
457
- ` greprag skill mirror snapshot list ${name}${identityArg}\n` +
458
- ` greprag skill mirror snapshot promote ${name} --name <snapshot>${identityArg} --expected-hash <current-hash>\n`);
444
+ console.error(`Mirrored skill "${name}" has no body — the mirror is holding a generated launcher.\n` +
445
+ `Refusing to serve it: the launcher is what sent you here, not the skill.\n\n` +
446
+ `The skill lives in git. Restore SKILL.md in its folder (~/.claude/skills/${name}/ in the dotclaude repo, ` +
447
+ `or <repo>/.claude/skills/${name}/), commit, then run \`greprag skill mirror sync\`. Earlier mirrored ` +
448
+ `bytes stay readable:\n` +
449
+ ` greprag skill history ${name}${identityArg}\n` +
450
+ ` greprag skill mirror snapshot restore ${name} --name <snapshot>${identityArg} --to <dir>\n`);
459
451
  process.exitCode = 1;
460
452
  return true;
461
453
  }
@@ -471,18 +463,6 @@ async function printMirroredSkill(name, args) {
471
463
  }
472
464
  process.stdout.write(file.content.endsWith('\n') ? file.content : file.content + '\n');
473
465
  if (!args.docPath) {
474
- // A mirror-managed skill's SKILL.md is a generated adapter, so the Skill
475
- // Learning Loop cannot land a fuse there without fighting the mirror for
476
- // the file. Its pending gains ride the load payload instead — this is the
477
- // moment the agent reads the skill. adr: adr/skill-fuse-delivery.md
478
- try {
479
- const pending = await mirrorGet('/v1/skillgain/pending');
480
- const gains = pending?.gains || [];
481
- const block = (0, skill_landing_1.buildLoadFuseBlock)(name, gains);
482
- if (block)
483
- process.stdout.write(block);
484
- }
485
- catch { /* a gain that fails to surface must never break the skill load */ }
486
466
  await printStateDocHints(name, file.content);
487
467
  const companions = files.map((f) => f.path).filter((p) => p !== 'SKILL.md');
488
468
  if (companions.length > 0) {
@@ -504,7 +484,7 @@ async function runLoad(args) {
504
484
  if (name === 'instructions' || name === 'agents') {
505
485
  if (await printGrepRagInstructions(args.slice(1)))
506
486
  return;
507
- console.error('No canonical GrepRAG Instructions found. Seed them with `greprag instructions adopt <CLAUDE.md|AGENTS.md>`.');
487
+ console.error('No mirrored instructions yet. `greprag instructions render` mirrors the committed ~/.claude/CLAUDE.md.');
508
488
  process.exit(1);
509
489
  }
510
490
  // An explicit --skill-id names one mirrored skill, so it outranks a bundled
@@ -254,7 +254,7 @@ async function tableCmd(args) {
254
254
  console.log(` reconciled: ${u.lastReconcile ? u.lastReconcile.slice(0, 10) : 'never'}`);
255
255
  console.log('');
256
256
  }
257
- console.log('Load-canonical GrepRAG Instructions reconcile with `greprag instructions push`; other units use a parity fix chip.');
257
+ console.log('Instruction files reconcile with `greprag instructions render`; other units use a parity fix chip.');
258
258
  }
259
259
  // ---------- Dispatch ------------------------------------------------------------
260
260
  async function runParity(args) {
@@ -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_POINTER_RESERVE = 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.harnessModules = exports.REGISTRY = void 0;
9
9
  exports.collectReminders = collectReminders;
10
10
  exports.bootOrder = bootOrder;
11
11
  exports.collectAnnounces = collectAnnounces;
@@ -66,13 +66,6 @@ exports.REGISTRY = [
66
66
  * update a parallel id list. (Root-cause repair, chip/opencode-fix-chip-system-parity.) */
67
67
  const harnessModules = (harness, registry = exports.REGISTRY) => registry.filter((m) => !m.harnesses || m.harnesses.includes(harness));
68
68
  exports.harnessModules = harnessModules;
69
- /** Split the registry by READ surface (docs/reminder-interrupt.md). UserPromptSubmit
70
- * evaluates `prompt`-source modules (the default); PreToolUse evaluates `command`-source
71
- * modules (they read env.toolCommand). Announces are always prompt-side (SessionStart). */
72
- const promptModules = (registry = exports.REGISTRY) => registry.filter((m) => (m.source ?? 'prompt') === 'prompt');
73
- exports.promptModules = promptModules;
74
- const commandModules = (registry = exports.REGISTRY) => registry.filter((m) => m.source === 'command');
75
- exports.commandModules = commandModules;
76
69
  /** PostCompact re-announce set (docs/reminder-interrupt.md). A compaction wipes the
77
70
  * SessionStart announces from context, so the announce modules re-present afterward —
78
71
  * by DEFAULT all of them; a module opts out with `reannounceOnCompact: false`. The
@@ -4,8 +4,8 @@
4
4
  * retitle <session-8hex> "<title>" — force-set a session's registry title,
5
5
  * overriding the gpt-5.4-nano auto-title. Used by the multi-chip /chip-leader
6
6
  * convention: a chip stamps its own label-aligned title ("Chip A — …") in
7
- * Block 1 so the FleetView watcher list / Discord /switch line up with the
8
- * leader's chip labels. Thin HTTP client over POST /v1/memory/title. */
7
+ * Block 1 so the FleetView watcher list lines up with the leader's chip
8
+ * labels. Thin HTTP client over POST /v1/memory/title. */
9
9
  var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
10
10
  if (k2 === undefined) k2 = k;
11
11
  var desc = Object.getOwnPropertyDescriptor(m, k);
@@ -18,28 +18,12 @@ function renderEntry(skill) {
18
18
  function buildSkillMirrorAnnounce(m, platform) {
19
19
  if (!m || m.count <= 0)
20
20
  return null;
21
- // A hand-kept copy is in the native skill list, so it is not in the trigger
22
- // list below, but the harness runs that copy and never sees canon move on.
23
- // Name each one; replacing a live copy is the user's call, skill by skill.
24
- const cutoverCommand = (0, skill_activation_manifest_1.localCopyCutoverCommand)(platform || '', m.localCopies);
25
- const copies = m.localCopies.length === 1 ? 'one mirrored skill' : `${m.localCopies.length} mirrored skills`;
26
- const copySection = cutoverCommand ? [
27
- '### Hand-Kept Copies',
28
- `This harness lists ${copies} from a hand-kept copy instead of an adapter, `
29
- + `so it keeps running that copy when the mirrored skill changes: ${m.localCopies.join(', ')}. `
30
- + 'Replacing a copy with its adapter is the user\'s call, one skill at a time (the old SKILL.md is backed up first):',
31
- '',
32
- ` ${cutoverCommand}`,
33
- '',
34
- 'Offer that command when the user asks why a skill behaves out of date, or when you needed one of them this session.',
35
- '',
36
- ] : [];
37
21
  if (m.skills.length === 0) {
38
22
  const line = '[greprag skills: slash skills are available. '
39
23
  + 'When discussing skills with the user, say use/create/update/refresh `/skill`; '
40
24
  + 'keep storage mechanics internal unless diagnosing a failure or the user asks how it works. '
41
25
  + 'Use `greprag load <skill>` to read skill instructions.]';
42
- return [line, '', ...copySection].join('\n').trimEnd();
26
+ return line;
43
27
  }
44
28
  // Mirrored skills with no native package never reach the native skill list.
45
29
  // Name the gap and the one command that closes it, or it stays invisible
@@ -64,7 +48,6 @@ function buildSkillMirrorAnnounce(m, platform) {
64
48
  'When discussing skills with the user, say use/create/update/refresh `/skill`; keep storage mechanics internal unless diagnosing a failure or the user asks how it works.',
65
49
  '',
66
50
  ...gapSection,
67
- ...copySection,
68
51
  '### Trigger Rules',
69
52
  'If the user names a skill (with or without `/`) OR the request clearly matches a skill\'s description below, '
70
53
  + 'you MUST activate that skill for the current turn. Match meaning, not exact keywords.',
@@ -90,7 +73,6 @@ function buildSkillMirrorAnnounce(m, platform) {
90
73
  }
91
74
  exports.skillMirrorAnnounceModule = {
92
75
  id: 'skill-mirror-announce',
93
- source: 'prompt',
94
76
  dependsOn: ['load-primer'], // references `greprag load` — the primer establishes it
95
77
  detect: (_env) => ({ tier: 'silent' }), // announce-only
96
78
  announce: (env) => buildSkillMirrorAnnounce(env.mirroredSkills, env.platform),