greprag 5.54.0 → 5.56.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 (37) hide show
  1. package/dist/commands/loadout-reminder.d.ts +37 -0
  2. package/dist/commands/loadout-reminder.js +78 -0
  3. package/dist/commands/loadout-reminder.js.map +1 -0
  4. package/dist/commands/loadout.d.ts +15 -0
  5. package/dist/commands/loadout.js +288 -0
  6. package/dist/commands/loadout.js.map +1 -0
  7. package/dist/commands/opencode-interrupt.d.ts +1 -0
  8. package/dist/commands/opencode-interrupt.js +2 -0
  9. package/dist/commands/opencode-interrupt.js.map +1 -1
  10. package/dist/commands/procedure-reminder.d.ts +8 -0
  11. package/dist/commands/procedure-reminder.js +27 -0
  12. package/dist/commands/procedure-reminder.js.map +1 -0
  13. package/dist/commands/procedure.js +311 -12
  14. package/dist/commands/procedure.js.map +1 -1
  15. package/dist/commands/reminder-registry.js +4 -0
  16. package/dist/commands/reminder-registry.js.map +1 -1
  17. package/dist/commands/reminder-types.d.ts +21 -0
  18. package/dist/commands/ship-through-gate.d.ts +16 -0
  19. package/dist/commands/ship-through-gate.js +43 -0
  20. package/dist/commands/ship-through-gate.js.map +1 -0
  21. package/dist/equipped-loadouts-cache.d.ts +20 -0
  22. package/dist/equipped-loadouts-cache.js +79 -0
  23. package/dist/equipped-loadouts-cache.js.map +1 -0
  24. package/dist/hook.js +79 -1
  25. package/dist/hook.js.map +1 -1
  26. package/dist/index.js +16 -0
  27. package/dist/index.js.map +1 -1
  28. package/dist/opencode-plugin.bundle.js +231 -52
  29. package/dist/opencode-plugin.js +9 -0
  30. package/dist/opencode-plugin.js.map +1 -1
  31. package/dist/procedure-run.d.ts +51 -0
  32. package/dist/procedure-run.js +186 -0
  33. package/dist/procedure-run.js.map +1 -0
  34. package/dist/procedure.d.ts +71 -1
  35. package/dist/procedure.js +168 -6
  36. package/dist/procedure.js.map +1 -1
  37. package/package.json +1 -1
@@ -24,9 +24,9 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
24
24
 
25
25
  // src/opencode-plugin.ts
26
26
  var crypto2 = __toESM(require("crypto"));
27
- var fs2 = __toESM(require("fs"));
27
+ var fs3 = __toESM(require("fs"));
28
28
  var os = __toESM(require("os"));
29
- var path2 = __toESM(require("path"));
29
+ var path3 = __toESM(require("path"));
30
30
 
31
31
  // src/opencode-plugin-helpers.ts
32
32
  var crypto = __toESM(require("crypto"));
@@ -2160,6 +2160,64 @@ var skillMirrorAnnounceModule = {
2160
2160
  reminder: () => null
2161
2161
  };
2162
2162
 
2163
+ // src/commands/procedure-reminder.ts
2164
+ function buildProcedureAnnounce(lines) {
2165
+ const active = (lines ?? []).map((s) => s.trim()).filter(Boolean);
2166
+ if (!active.length)
2167
+ return null;
2168
+ return [
2169
+ "[greprag procedures \u2014 active project procedures:]",
2170
+ ...active.map((line) => `\u2022 ${line}`)
2171
+ ].join("\n");
2172
+ }
2173
+ var procedureAnnounceModule = {
2174
+ id: "procedure-announce",
2175
+ // ship-through references peer coordination taught by the inbox primer.
2176
+ dependsOn: ["inbox-primer"],
2177
+ detect: (_env) => ({ tier: "silent" }),
2178
+ announce: (env) => buildProcedureAnnounce(env.procedureAnnounces),
2179
+ reminder: () => null
2180
+ };
2181
+
2182
+ // src/commands/loadout-reminder.ts
2183
+ function matchLoadouts(promptText, equipped) {
2184
+ const prompt = (promptText || "").toLowerCase();
2185
+ if (!prompt.trim() || !equipped || equipped.length === 0)
2186
+ return [];
2187
+ return equipped.filter((l) => (l.keywords || []).some((kw) => {
2188
+ const k = kw.trim().toLowerCase();
2189
+ return k.length >= 3 && prompt.includes(k);
2190
+ }));
2191
+ }
2192
+ function buildLoadoutAnnounce(equipped) {
2193
+ const decls = (equipped || []).filter((l) => l.announceLine);
2194
+ if (decls.length === 0)
2195
+ return null;
2196
+ return [
2197
+ "[greprag loadouts \u2014 equipped skill bundles (served on demand via `greprag load`):]",
2198
+ ...decls.map((l) => ` ${l.announceLine}`)
2199
+ ].join("\n");
2200
+ }
2201
+ function buildLoadoutReminder(matched) {
2202
+ if (matched.length === 0)
2203
+ return null;
2204
+ return matched.map((l) => `\u{1F392} your equipped loadout "${l.name}" covers this \u2192 \`greprag load ${l.advisor}\` (it routes to the right expert)`).join("\n");
2205
+ }
2206
+ function loadoutDetect(env) {
2207
+ const matched = matchLoadouts(env.promptText, env.equippedLoadouts);
2208
+ if (matched.length === 0)
2209
+ return { tier: "silent" };
2210
+ return { tier: "nudge", detail: { matched } };
2211
+ }
2212
+ var loadoutRegistrarModule = {
2213
+ id: "loadout-registrar",
2214
+ // The announce says "greprag load" — depends on the load primer teaching it.
2215
+ dependsOn: ["load-primer"],
2216
+ detect: loadoutDetect,
2217
+ announce: (env) => buildLoadoutAnnounce(env.equippedLoadouts),
2218
+ reminder: (d) => buildLoadoutReminder(d.detail?.matched || [])
2219
+ };
2220
+
2163
2221
  // src/commands/reminder-registry.ts
2164
2222
  var REGISTRY = [
2165
2223
  inboxPrimerModule,
@@ -2174,6 +2232,10 @@ var REGISTRY = [
2174
2232
  // announce-only skill-gain landings (docs/skill-learning-loop.md)
2175
2233
  skillMirrorAnnounceModule,
2176
2234
  // one-line Tier-3 pointer: your skills travel via greprag load (docs/load-system.md)
2235
+ procedureAnnounceModule,
2236
+ // active project procedures, rendered generically from operator-owned installs
2237
+ loadoutRegistrarModule,
2238
+ // equipped loadouts: one advisor announce each + per-turn keyword Match (docs/loadout.md)
2177
2239
  setupWarningModule,
2178
2240
  versionUpgradeModule,
2179
2241
  // Deficiency-gated announce — silent unless a newer release exists
@@ -2253,6 +2315,7 @@ var OPENCODE_MODULE_IDS = /* @__PURE__ */ new Set([
2253
2315
  "setup-warning",
2254
2316
  "version-upgrade",
2255
2317
  "memory-primer",
2318
+ "procedure-announce",
2256
2319
  "collision"
2257
2320
  ]);
2258
2321
  var opencodeRegistry = REGISTRY.filter(
@@ -2268,7 +2331,8 @@ function buildOpenCodeEnv(params) {
2268
2331
  projectName: params.projectName,
2269
2332
  setupWarning: params.setupWarning ?? null,
2270
2333
  corpusApiDocs: params.corpusApiDocs ?? [],
2271
- updateAvailable: params.updateAvailable ?? null
2334
+ updateAvailable: params.updateAvailable ?? null,
2335
+ procedureAnnounces: params.procedureAnnounces ?? []
2272
2336
  };
2273
2337
  }
2274
2338
  var OPENCODE_CHIP_BOOTLOADER_POINTER = '[greprag chips \u2014 agent coordination is built in (messaging plumbing in the inbox primer).]\n\u2022 Delegating a component of a plan to an isolated sub-session (a "chip") for focused, efficient work? \u2192 run `greprag load chip-bootloader` for the copyable bootloader template (Block 1 setup + Block 2 report-back).';
@@ -2279,15 +2343,123 @@ function getOpenCodeReminders(env) {
2279
2343
  return collectReminders(env, opencodeRegistry);
2280
2344
  }
2281
2345
 
2346
+ // src/procedure.ts
2347
+ var path2 = __toESM(require("path"));
2348
+ var fs2 = __toESM(require("fs"));
2349
+ var PROCEDURE_STORE_VERSION = "2";
2350
+ function stateDir() {
2351
+ const home = process.env.HOME || process.env.USERPROFILE || "";
2352
+ return path2.join(home, ".greprag", "state");
2353
+ }
2354
+ function procedureStorePath(projectId) {
2355
+ return path2.join(stateDir(), `procedures-${projectId}.json`);
2356
+ }
2357
+ function readProcedureStore(projectId) {
2358
+ try {
2359
+ const raw = fs2.readFileSync(procedureStorePath(projectId), "utf-8");
2360
+ const parsed = JSON.parse(raw);
2361
+ if (parsed && Array.isArray(parsed.procedures)) {
2362
+ return {
2363
+ version: parsed.version || "1",
2364
+ procedures: parsed.procedures,
2365
+ installs: Array.isArray(parsed.installs) ? parsed.installs : []
2366
+ };
2367
+ }
2368
+ } catch {
2369
+ }
2370
+ return { version: PROCEDURE_STORE_VERSION, procedures: [], installs: [] };
2371
+ }
2372
+ function procedureId(proc) {
2373
+ return proc.verb;
2374
+ }
2375
+ function explicitInstall(store, id) {
2376
+ return (store.installs ?? []).find((i) => i.procedureId === id) ?? null;
2377
+ }
2378
+ function effectiveInstall(store, proc) {
2379
+ const id = procedureId(proc);
2380
+ const found = explicitInstall(store, id);
2381
+ if (found)
2382
+ return found;
2383
+ const legacyStored = store.procedures.some((p) => procedureId(p) === id);
2384
+ return {
2385
+ procedureId: id,
2386
+ definitionVersion: proc.definitionVersion ?? 1,
2387
+ state: legacyStored ? "active" : "disabled",
2388
+ autonomy: legacyStored ? "advise" : proc.defaultInstall?.autonomy ?? "advise",
2389
+ authority: legacyStored ? "fresh" : proc.defaultInstall?.authority ?? "fresh",
2390
+ settings: defaultProcedureSettings(proc),
2391
+ updatedAt: proc.updatedAt ?? ""
2392
+ };
2393
+ }
2394
+ function defaultProcedureSettings(proc) {
2395
+ return Object.fromEntries(Object.entries(proc.settingsSchema ?? {}).map(([key, spec]) => [key, spec.default]));
2396
+ }
2397
+ var SHIP_THROUGH_PROCEDURE = {
2398
+ verb: "ship-through",
2399
+ triggers: [],
2400
+ steps: [
2401
+ "Start the run with `greprag procedure begin ship-through`, then reach a coherent diff.",
2402
+ "Confer with reachable same-project GrepRAG peers; if none are reachable, use an independent local reviewer/subagent.",
2403
+ "Incorporate valid findings, run proportional checks, then attest current-artifact review with `greprag procedure attest ship-through review --via peer|local --ref <reviewer>`.",
2404
+ "Commit/merge only the task-scoped files.",
2405
+ "Invoke only the affected target's registered deploy procedure(s).",
2406
+ "Verify production health and the changed behavior."
2407
+ ].join(" "),
2408
+ caveats: "Routine code changes only. Stop for failed checks, missing migrations/secrets, money, destructive live data, undeclared external systems, or scope expansion. Peer review informs correctness but never grants authority.",
2409
+ endpoint: "the affected production target is healthy and verified at the reviewed current commit",
2410
+ status: "seeded",
2411
+ definitionVersion: 1,
2412
+ settingsSchema: {
2413
+ review_strategy: {
2414
+ type: "string",
2415
+ default: "peer-or-local",
2416
+ enum: ["peer-or-local", "peer-required", "local"],
2417
+ riskBearing: true,
2418
+ description: "Independent review source before commit/deploy."
2419
+ }
2420
+ },
2421
+ defaultInstall: { autonomy: "execute", authority: "standing" },
2422
+ announce: {
2423
+ summary: "`ship-through` ACTIVE \u2014 change/build tasks end peer-reviewed and verified in production, not \u201Cready to deploy.\u201D Start: `greprag procedure begin ship-through`; contract: `greprag procedure show ship-through`.",
2424
+ reannounceOnCompact: true
2425
+ },
2426
+ risk: {
2427
+ maximumAutonomy: "execute",
2428
+ standingAuthorityAllowed: true,
2429
+ authorityScope: ["task-scoped commit/merge", "registered deploy procedures", "production verification"]
2430
+ }
2431
+ };
2432
+ var BUILTIN_PROCEDURES = [SHIP_THROUGH_PROCEDURE];
2433
+ function listProcedureDefinitions(store) {
2434
+ const out = [...store.procedures];
2435
+ const seen = new Set(out.map(procedureId));
2436
+ for (const proc of BUILTIN_PROCEDURES) {
2437
+ if (!seen.has(procedureId(proc)))
2438
+ out.push(proc);
2439
+ }
2440
+ return out;
2441
+ }
2442
+ function activeProcedureAnnounces(store) {
2443
+ const lines = [];
2444
+ for (const proc of listProcedureDefinitions(store)) {
2445
+ if (!proc.announce)
2446
+ continue;
2447
+ if (effectiveInstall(store, proc).state !== "active")
2448
+ continue;
2449
+ lines.push(proc.announce.summary);
2450
+ }
2451
+ return lines;
2452
+ }
2453
+
2282
2454
  // src/opencode-plugin.ts
2283
- var DEBUG_LOG_PATH = path2.join(os.homedir(), ".greprag", "opencode-plugin-debug.log");
2455
+ var DEBUG_LOG_PATH = path3.join(os.homedir(), ".greprag", "opencode-plugin-debug.log");
2284
2456
  var _debugLogReady = false;
2285
2457
  function dlogInit() {
2286
2458
  if (_debugLogReady)
2287
2459
  return;
2288
2460
  _debugLogReady = true;
2289
2461
  try {
2290
- fs2.writeFileSync(DEBUG_LOG_PATH, "");
2462
+ fs3.writeFileSync(DEBUG_LOG_PATH, "");
2291
2463
  } catch {
2292
2464
  }
2293
2465
  }
@@ -2296,7 +2468,7 @@ function dlog(msg) {
2296
2468
  const line = `[${(/* @__PURE__ */ new Date()).toISOString()}] [greprag-memory] ${msg}
2297
2469
  `;
2298
2470
  try {
2299
- fs2.appendFileSync(DEBUG_LOG_PATH, line);
2471
+ fs3.appendFileSync(DEBUG_LOG_PATH, line);
2300
2472
  } catch {
2301
2473
  }
2302
2474
  }
@@ -2304,9 +2476,9 @@ dlog(`module top reached: pid=${process.pid} argv0=${process.argv[0]} distPath=$
2304
2476
  var API_URL = "https://api.greprag.com";
2305
2477
  function loadEnvFile(filePath) {
2306
2478
  try {
2307
- if (!fs2.existsSync(filePath))
2479
+ if (!fs3.existsSync(filePath))
2308
2480
  return;
2309
- const raw = fs2.readFileSync(filePath, "utf-8");
2481
+ const raw = fs3.readFileSync(filePath, "utf-8");
2310
2482
  for (const line of raw.split(/\r?\n/)) {
2311
2483
  const trimmed = line.trim();
2312
2484
  if (!trimmed || trimmed.startsWith("#"))
@@ -2326,12 +2498,12 @@ function loadEnvFile(filePath) {
2326
2498
  }
2327
2499
  }
2328
2500
  function loadGrepragEnv() {
2329
- loadEnvFile(path2.join(HOME, ".greprag", ".env"));
2501
+ loadEnvFile(path3.join(HOME, ".greprag", ".env"));
2330
2502
  try {
2331
- const p = path2.join(HOME, ".claude", "settings.json");
2332
- if (!fs2.existsSync(p))
2503
+ const p = path3.join(HOME, ".claude", "settings.json");
2504
+ if (!fs3.existsSync(p))
2333
2505
  return;
2334
- const data = JSON.parse(fs2.readFileSync(p, "utf-8"));
2506
+ const data = JSON.parse(fs3.readFileSync(p, "utf-8"));
2335
2507
  if (data && data.env && typeof data.env === "object") {
2336
2508
  for (const [key, val] of Object.entries(data.env)) {
2337
2509
  if (!process.env[key] && typeof val === "string") {
@@ -2344,11 +2516,11 @@ function loadGrepragEnv() {
2344
2516
  }
2345
2517
  loadGrepragEnv();
2346
2518
  dlog(`env loaded: MEMORY_HOOK_ENABLED=${process.env.MEMORY_HOOK_ENABLED || "<unset>"} GREPRAG_API_KEY=${process.env.GREPRAG_API_KEY ? "set" : "<unset>"} GREPRAG_OPENCODE_CAPTURE=${process.env.GREPRAG_OPENCODE_CAPTURE || "<unset>"}`);
2347
- var WATCHER_LOCK = path2.join(HOME || os.homedir(), ".greprag", "opencode-watch.lock");
2519
+ var WATCHER_LOCK = path3.join(HOME || os.homedir(), ".greprag", "opencode-watch.lock");
2348
2520
  var WATCHER_STALE_MS = 3e4;
2349
2521
  function findGrepragBinary() {
2350
2522
  const override = process.env.GREPRAG_BIN;
2351
- if (override && fs2.existsSync(override))
2523
+ if (override && fs3.existsSync(override))
2352
2524
  return override;
2353
2525
  const binaryName = process.platform === "win32" ? "greprag.cmd" : "greprag";
2354
2526
  try {
@@ -2357,14 +2529,14 @@ function findGrepragBinary() {
2357
2529
  const candidate = line.trim();
2358
2530
  if (!candidate)
2359
2531
  continue;
2360
- if (!fs2.existsSync(candidate))
2532
+ if (!fs3.existsSync(candidate))
2361
2533
  continue;
2362
2534
  if (process.platform === "win32") {
2363
- const ext = path2.extname(candidate).toLowerCase();
2535
+ const ext = path3.extname(candidate).toLowerCase();
2364
2536
  if (ext === ".cmd" || ext === ".exe" || ext === ".bat")
2365
2537
  return candidate;
2366
2538
  const cmdSibling = candidate + ".cmd";
2367
- if (fs2.existsSync(cmdSibling))
2539
+ if (fs3.existsSync(cmdSibling))
2368
2540
  return cmdSibling;
2369
2541
  continue;
2370
2542
  }
@@ -2373,19 +2545,19 @@ function findGrepragBinary() {
2373
2545
  } catch {
2374
2546
  }
2375
2547
  const candidates = process.platform === "win32" ? [
2376
- path2.join(HOME, "AppData", "Roaming", "npm", "greprag.cmd"),
2377
- path2.join(HOME, "AppData", "Roaming", "npm", "greprag"),
2378
- path2.join(HOME, "AppData", "Local", "Yarn", "bin", "greprag.cmd"),
2379
- path2.join(HOME, "AppData", "Local", "pnpm", "bin", "greprag.cmd")
2548
+ path3.join(HOME, "AppData", "Roaming", "npm", "greprag.cmd"),
2549
+ path3.join(HOME, "AppData", "Roaming", "npm", "greprag"),
2550
+ path3.join(HOME, "AppData", "Local", "Yarn", "bin", "greprag.cmd"),
2551
+ path3.join(HOME, "AppData", "Local", "pnpm", "bin", "greprag.cmd")
2380
2552
  ] : [
2381
2553
  "/usr/local/bin/greprag",
2382
2554
  "/opt/homebrew/bin/greprag",
2383
- path2.join(HOME || "", ".local", "bin", "greprag"),
2384
- path2.join(HOME || "", ".yarn", "bin", "greprag")
2555
+ path3.join(HOME || "", ".local", "bin", "greprag"),
2556
+ path3.join(HOME || "", ".yarn", "bin", "greprag")
2385
2557
  ];
2386
2558
  for (const c of candidates) {
2387
2559
  try {
2388
- if (c && fs2.existsSync(c))
2560
+ if (c && fs3.existsSync(c))
2389
2561
  return c;
2390
2562
  } catch {
2391
2563
  }
@@ -2394,18 +2566,18 @@ function findGrepragBinary() {
2394
2566
  }
2395
2567
  function tryClaimWatcherLock() {
2396
2568
  try {
2397
- const fd = fs2.openSync(WATCHER_LOCK, "wx");
2569
+ const fd = fs3.openSync(WATCHER_LOCK, "wx");
2398
2570
  return { ok: true, fd };
2399
2571
  } catch (err) {
2400
2572
  if (err.code !== "EEXIST") {
2401
2573
  return { ok: false, reason: "lock-create-failed" };
2402
2574
  }
2403
2575
  try {
2404
- const stat = fs2.statSync(WATCHER_LOCK);
2576
+ const stat = fs3.statSync(WATCHER_LOCK);
2405
2577
  const ageMs = Date.now() - stat.mtimeMs;
2406
2578
  let pidAlive = false;
2407
2579
  try {
2408
- const pid = parseInt(fs2.readFileSync(WATCHER_LOCK, "utf-8").trim(), 10);
2580
+ const pid = parseInt(fs3.readFileSync(WATCHER_LOCK, "utf-8").trim(), 10);
2409
2581
  pidAlive = pid > 0 && isPidAlive(pid);
2410
2582
  } catch {
2411
2583
  }
@@ -2413,10 +2585,10 @@ function tryClaimWatcherLock() {
2413
2585
  return { ok: false, reason: "busy" };
2414
2586
  }
2415
2587
  try {
2416
- fs2.unlinkSync(WATCHER_LOCK);
2588
+ fs3.unlinkSync(WATCHER_LOCK);
2417
2589
  } catch {
2418
2590
  }
2419
- const fd = fs2.openSync(WATCHER_LOCK, "wx");
2591
+ const fd = fs3.openSync(WATCHER_LOCK, "wx");
2420
2592
  return { ok: true, fd };
2421
2593
  } catch (err2) {
2422
2594
  return { ok: false, reason: "lock-stale-replace-failed" };
@@ -2457,11 +2629,11 @@ function startCaptureWatcher() {
2457
2629
  return;
2458
2630
  }
2459
2631
  try {
2460
- fs2.writeSync(claim.fd, String(process.pid));
2461
- fs2.closeSync(claim.fd);
2632
+ fs3.writeSync(claim.fd, String(process.pid));
2633
+ fs3.closeSync(claim.fd);
2462
2634
  } catch {
2463
2635
  try {
2464
- fs2.unlinkSync(WATCHER_LOCK);
2636
+ fs3.unlinkSync(WATCHER_LOCK);
2465
2637
  } catch {
2466
2638
  }
2467
2639
  return;
@@ -2477,11 +2649,11 @@ function startCaptureWatcher() {
2477
2649
  const claim2 = tryClaimWatcherLock();
2478
2650
  if (claim2.ok) {
2479
2651
  try {
2480
- fs2.writeSync(claim2.fd, String(process.pid));
2481
- fs2.closeSync(claim2.fd);
2652
+ fs3.writeSync(claim2.fd, String(process.pid));
2653
+ fs3.closeSync(claim2.fd);
2482
2654
  } catch {
2483
2655
  try {
2484
- fs2.unlinkSync(WATCHER_LOCK);
2656
+ fs3.unlinkSync(WATCHER_LOCK);
2485
2657
  } catch {
2486
2658
  }
2487
2659
  return false;
@@ -2524,7 +2696,7 @@ function startCaptureWatcher() {
2524
2696
  childStartTime = Date.now();
2525
2697
  dlog(`startCaptureWatcher: spawned 'greprag opencode watch' pid=${child.pid ?? "<none>"}`);
2526
2698
  try {
2527
- fs2.writeFileSync(WATCHER_LOCK, String(child.pid));
2699
+ fs3.writeFileSync(WATCHER_LOCK, String(child.pid));
2528
2700
  } catch {
2529
2701
  }
2530
2702
  child.stderr?.on("data", (chunk) => {
@@ -2536,9 +2708,9 @@ function startCaptureWatcher() {
2536
2708
  child.on("exit", (code, signal) => {
2537
2709
  dlog(`startCaptureWatcher: child pid=${child.pid ?? "<none>"} exited code=${code} signal=${signal || "none"}`);
2538
2710
  try {
2539
- const current = fs2.readFileSync(WATCHER_LOCK, "utf-8").trim();
2711
+ const current = fs3.readFileSync(WATCHER_LOCK, "utf-8").trim();
2540
2712
  if (current === String(child.pid))
2541
- fs2.unlinkSync(WATCHER_LOCK);
2713
+ fs3.unlinkSync(WATCHER_LOCK);
2542
2714
  } catch {
2543
2715
  }
2544
2716
  currentChild = null;
@@ -2567,7 +2739,7 @@ function startCaptureWatcher() {
2567
2739
  `
2568
2740
  );
2569
2741
  try {
2570
- fs2.unlinkSync(WATCHER_LOCK);
2742
+ fs3.unlinkSync(WATCHER_LOCK);
2571
2743
  } catch {
2572
2744
  }
2573
2745
  return;
@@ -2599,7 +2771,7 @@ function startCaptureWatcher() {
2599
2771
  }, 2e3).unref();
2600
2772
  }
2601
2773
  try {
2602
- fs2.unlinkSync(WATCHER_LOCK);
2774
+ fs3.unlinkSync(WATCHER_LOCK);
2603
2775
  } catch {
2604
2776
  }
2605
2777
  };
@@ -2674,11 +2846,11 @@ function startSessionRelay(sessionId, serverUrl) {
2674
2846
  return;
2675
2847
  }
2676
2848
  try {
2677
- fs2.writeSync(claim.fd, String(process.pid));
2678
- fs2.closeSync(claim.fd);
2849
+ fs3.writeSync(claim.fd, String(process.pid));
2850
+ fs3.closeSync(claim.fd);
2679
2851
  } catch {
2680
2852
  try {
2681
- fs2.unlinkSync(lockPath);
2853
+ fs3.unlinkSync(lockPath);
2682
2854
  } catch {
2683
2855
  }
2684
2856
  return;
@@ -2693,11 +2865,11 @@ function startSessionRelay(sessionId, serverUrl) {
2693
2865
  const claim2 = tryClaimRelayLock(lockPath);
2694
2866
  if (claim2.ok) {
2695
2867
  try {
2696
- fs2.writeSync(claim2.fd, String(process.pid));
2697
- fs2.closeSync(claim2.fd);
2868
+ fs3.writeSync(claim2.fd, String(process.pid));
2869
+ fs3.closeSync(claim2.fd);
2698
2870
  } catch {
2699
2871
  try {
2700
- fs2.unlinkSync(lockPath);
2872
+ fs3.unlinkSync(lockPath);
2701
2873
  } catch {
2702
2874
  }
2703
2875
  return false;
@@ -2733,7 +2905,7 @@ function startSessionRelay(sessionId, serverUrl) {
2733
2905
  childStartTime = Date.now();
2734
2906
  dlog(`startSessionRelay: spawned 'greprag opencode relay' pid=${child.pid ?? "<none>"}`);
2735
2907
  try {
2736
- fs2.writeFileSync(lockPath, String(child.pid));
2908
+ fs3.writeFileSync(lockPath, String(child.pid));
2737
2909
  } catch {
2738
2910
  }
2739
2911
  child.stderr?.on("data", (chunk) => {
@@ -2744,9 +2916,9 @@ function startSessionRelay(sessionId, serverUrl) {
2744
2916
  });
2745
2917
  child.on("exit", (code, signal) => {
2746
2918
  try {
2747
- const current = fs2.readFileSync(lockPath, "utf-8").trim();
2919
+ const current = fs3.readFileSync(lockPath, "utf-8").trim();
2748
2920
  if (current === String(child.pid))
2749
- fs2.unlinkSync(lockPath);
2921
+ fs3.unlinkSync(lockPath);
2750
2922
  } catch {
2751
2923
  }
2752
2924
  currentChild = null;
@@ -2773,7 +2945,7 @@ function startSessionRelay(sessionId, serverUrl) {
2773
2945
  `
2774
2946
  );
2775
2947
  try {
2776
- fs2.unlinkSync(lockPath);
2948
+ fs3.unlinkSync(lockPath);
2777
2949
  } catch {
2778
2950
  }
2779
2951
  return;
@@ -2805,7 +2977,7 @@ function startSessionRelay(sessionId, serverUrl) {
2805
2977
  }, 2e3).unref();
2806
2978
  }
2807
2979
  try {
2808
- fs2.unlinkSync(lockPath);
2980
+ fs3.unlinkSync(lockPath);
2809
2981
  } catch {
2810
2982
  }
2811
2983
  };
@@ -2932,7 +3104,14 @@ ALWAYS pass \`--from-session ${sid82}\` when you run \`greprag send\`, so recipi
2932
3104
  const env = buildOpenCodeEnv({
2933
3105
  short: sid8,
2934
3106
  turnCount,
2935
- projectName: anchor.projectName
3107
+ projectName: anchor.projectName,
3108
+ procedureAnnounces: (() => {
3109
+ try {
3110
+ return activeProcedureAnnounces(readProcedureStore(anchor.projectId));
3111
+ } catch {
3112
+ return [];
3113
+ }
3114
+ })()
2936
3115
  });
2937
3116
  if (!announcedBySession.has(sid8)) {
2938
3117
  announcedBySession.add(sid8);
@@ -98,6 +98,7 @@ const opencode_plugin_helpers_1 = require("./opencode-plugin-helpers");
98
98
  const proc_1 = require("./proc");
99
99
  const opencode_plugin_crush_1 = require("./opencode-plugin-crush");
100
100
  const opencode_interrupt_1 = require("./commands/opencode-interrupt");
101
+ const procedure_1 = require("./procedure");
101
102
  // ============================================================================
102
103
  // File-based debug log. opencode's plugin loader (Bun) does NOT route plugin
103
104
  // stderr into the opencode log file at ~/.local/share/opencode/log/*.log —
@@ -906,6 +907,14 @@ const GrepRAGMemoryPlugin = async (ctx) => {
906
907
  short: sid8,
907
908
  turnCount,
908
909
  projectName: anchor.projectName,
910
+ procedureAnnounces: (() => {
911
+ try {
912
+ return (0, procedure_1.activeProcedureAnnounces)((0, procedure_1.readProcedureStore)(anchor.projectId));
913
+ }
914
+ catch {
915
+ return [];
916
+ }
917
+ })(),
909
918
  });
910
919
  // Announces: inject on first fire per session (static primers)
911
920
  if (!announcedBySession.has(sid8)) {