greprag 5.75.0 → 5.77.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/codex-doctor.js +1 -0
- package/dist/commands/delivery-reminder.js +5 -5
- package/dist/commands/grok-spawn.js +208 -0
- package/dist/commands/inbox-drain.js +36 -6
- package/dist/commands/inbox-primer-reminder.js +1 -1
- package/dist/commands/inbox-watch.js +14 -2
- package/dist/commands/init.js +33 -8
- package/dist/commands/load-primer-reminder.js +8 -1
- package/dist/commands/load.js +4 -0
- package/dist/commands/opencode-interrupt.js +8 -0
- package/dist/commands/os-primer-reminder.js +2 -1
- package/dist/commands/persona-reminder.js +9 -0
- package/dist/commands/persona.js +191 -0
- package/dist/commands/reminder-registry.js +2 -0
- package/dist/commands/status.js +2 -1
- package/dist/hook.js +38 -9
- package/dist/index.js +56 -11
- package/dist/opencode-plugin.bundle.js +69 -10
- package/dist/opencode-plugin.js +45 -2
- package/dist/session-id.js +13 -0
- package/package.json +1 -1
- package/skill/greprag/SKILL.md +37 -3
- package/skill/templates/chip-spawn.md +2 -0
- package/skill/templates/grok-chip-spawn.md +101 -0
|
@@ -1771,7 +1771,7 @@ function crushMessages(messages, opts) {
|
|
|
1771
1771
|
|
|
1772
1772
|
// src/commands/os-primer-reminder.ts
|
|
1773
1773
|
function buildOsPrimer(env) {
|
|
1774
|
-
const spawnEntry = env?.platform === "codex" ? "codex-chip-spawn" : env?.platform === "opencode" ? "chip-bootloader" : "chip-spawn";
|
|
1774
|
+
const spawnEntry = env?.platform === "codex" ? "codex-chip-spawn" : env?.platform === "opencode" ? "chip-bootloader" : env?.platform === "grok" ? "grok-chip-spawn" : "chip-spawn";
|
|
1775
1775
|
return [
|
|
1776
1776
|
"[grepragOS \u2014 the operating laws. Full doctrine: `greprag load os`.]",
|
|
1777
1777
|
'\u2022 Doctrine vs state: methods ship in the CLI (`greprag load`); live state lives in the repo. A skill that depends on repo state carries a "STATE \u2014 read these first" block naming exact paths.',
|
|
@@ -1818,7 +1818,7 @@ function buildInboxPrimer(env) {
|
|
|
1818
1818
|
// "ToolSearch select:Monitor" is unactionable there (neither tool exists) and an
|
|
1819
1819
|
// un-followable alarm decays into banner-blindness for the announces around it.
|
|
1820
1820
|
grok ? "[GROK INBOX \u2014 greprag is your agent-to-agent mesh. Idle wake is Grok `monitor` (NOT Claude Monitor, NOT a bash while-true). Turn-boundary drain also rides Stop additionalContext. Unarmed, idle mail waits until the next Stop or user prompt.]" : codex ? "[CODEX INBOX \u2014 greprag is your agent-to-agent mesh. Codex-to-Codex: discover tasks and their repo/workspace with `codex_app.list_threads`, then coordinate with `codex_app.send_message_to_thread`. Reaching ANY non-Codex peer (Claude Code / opencode / Grok / another tenant's agent): `greprag send` IS the live primary path \u2014 native Codex messaging cannot reach them.]" : opencode ? "[OPENCODE INBOX \u2014 greprag is your agent-to-agent mesh. Inbound delivery is AUTOMATIC here: the greprag plugin arms a relay for this session (nothing to arm or re-arm) and inbound peer messages arrive as injected turns.]" : "[\u26A0 ARM YOUR INBOX WATCHER NOW \u2014 you are UNREACHABLE until you do. Peers are live in your mesh trying to coordinate with this session, and the operator can DM you \u2014 but they reach you ONLY while armed. Unarmed, you are invisible and their messages silently pile up. The watcher can DROP ANYTIME (crash / reload / OOM) \u2014 EVERY turn you are flagged unarmed you MUST re-arm, or coordination breaks.]",
|
|
1821
|
-
grok ? `ARM (idle wake): Grok \`monitor\` tool, persistent:true, description:"greprag inbox ${env.short}", command: \`${arm}\`. \`--quiet\` is REQUIRED \u2014 Grok treats stderr as wake events; the Claude bash wrapper is PowerShell-invalid. Use full UUID / 16-hex, never 8-hex.
|
|
1821
|
+
grok ? `ARM (idle wake): Grok \`monitor\` tool, persistent:true, description:"greprag inbox ${env.short}", command: \`${arm}\`. \`--quiet\` is REQUIRED \u2014 Grok treats stderr as wake events; the Claude bash wrapper is PowerShell-invalid. Use full UUID / 16-hex, never 8-hex. A chip \u2192 \`greprag load grok-chip-spawn\` then \`greprag grok spawn\` (child arms its own watch; parent keeps ONE). A helper \u2192 spawn_subagent. Floor: Stop-hook drain injects unread mail even if you never arm. Then \`greprag inbox\`.` : codex ? "CODEX: Native Codex tools are the source of truth: `codex_app.list_threads` discovers tasks plus their repo/workspace, and `codex_app.send_message_to_thread` handles Codex-to-Codex coordination. GrepRAG inbox rows for Codex surface through turn hooks only: SessionStart `drain` and UserPromptSubmit `codex-notify`. There is no Codex startup watcher to install. If hooks do not fire, open Codex Desktop Settings -> Settings -> Hooks, trust the GrepRAG commands, start a fresh Codex session, then drain what is waiting: `greprag inbox`." : opencode ? "DELIVERY: the relay arms itself on this session's first turn and injects inbound messages as they land \u2014 treat an injected `Message from <handle> (session <8hex>):` turn as async peer mail, not the operator typing. Drain anything already waiting: `greprag inbox`." : `ARM: Monitor is a deferred tool \u2014 \`ToolSearch select:Monitor\`, then call Monitor with persistent:true, timeout_ms:3600000, description:"greprag inbox ${env.short}", command: \`${arm}\`. The \`--session ${env.short}\` is REQUIRED \u2014 a bare \`greprag inbox watch\` writes no pidfile, so the arm-check reads you unarmed forever. NEVER run it bare also because it is a foreground stream and hangs the turn. Then drain what is waiting: \`greprag inbox\`.`,
|
|
1822
1822
|
"",
|
|
1823
1823
|
// The facts + directives — a flat list (no MODEL/RULES scaffolding; the labels were
|
|
1824
1824
|
// human doc-structure, dead weight to an agent). Everything that helps the agent DECIDE
|
|
@@ -1857,6 +1857,10 @@ var OPENCODE_CHIP_SPAWN_POINTER = [
|
|
|
1857
1857
|
"[greprag chips \u2014 agent coordination is built in (messaging plumbing in the inbox primer).]",
|
|
1858
1858
|
"\u2022 Delegating a component of a plan to an isolated chip session, or `greprag fix spawn` just printed a FIX-chip mission? \u2192 run `greprag load chip-bootloader` FIRST for the method: OpenCode chips spawn natively via `greprag opencode chip goal create` + `greprag opencode chip spawn` (HTTP child session; manual paste only when the API is down). \u22652 chips at one objective \u2192 `greprag load chip-leader-opencode` BEFORE the first spawn."
|
|
1859
1859
|
].join("\n");
|
|
1860
|
+
var GROK_CHIP_SPAWN_POINTER = [
|
|
1861
|
+
"[greprag chips \u2014 agent coordination is built in (messaging plumbing in the inbox primer).]",
|
|
1862
|
+
"\u2022 About to spawn a chip? \u2192 run `greprag load grok-chip-spawn` FIRST. Vehicle is `greprag grok spawn` (cmd /k TUI bootloader). Child arms its own quiet inbox watch; parent keeps one and talks via `greprag send`. `spawn_subagent` is a helper, not a chip. \u22652 chips at one objective \u2192 `greprag load chip-leader` BEFORE the first spawn."
|
|
1863
|
+
].join("\n");
|
|
1860
1864
|
var CODEX_CHIP_SPAWN_POINTER = [
|
|
1861
1865
|
"[greprag Codex delegation \u2014 1\u20132-chip quick path with the initiator as LEAD, or a separate LEAD for larger/seamed missions.]",
|
|
1862
1866
|
"\u2022 About to delegate a Codex task? \u2192 run `greprag load codex-chip-spawn` FIRST. Use exact first-line titles `LEAD: <Mission>`, `Chip A/B/C: <Specific Purview>`, and `FIX: [type] <one friction unit>`. Chips are ordinary writable native tasks; completion is a native Codex task reply to the LEAD after committing. A `FIX:` task loads `greprag load mechanic` first."
|
|
@@ -1882,6 +1886,8 @@ var chipSpawnPointerModule = {
|
|
|
1882
1886
|
return null;
|
|
1883
1887
|
if (env.platform === "opencode")
|
|
1884
1888
|
return OPENCODE_CHIP_SPAWN_POINTER;
|
|
1889
|
+
if (env.platform === "grok")
|
|
1890
|
+
return GROK_CHIP_SPAWN_POINTER;
|
|
1885
1891
|
return CHIP_SPAWN_POINTER;
|
|
1886
1892
|
},
|
|
1887
1893
|
reminder: () => null
|
|
@@ -2432,15 +2438,15 @@ var loadoutRegistrarModule = {
|
|
|
2432
2438
|
// src/commands/delivery-reminder.ts
|
|
2433
2439
|
function coordinationLine(platform) {
|
|
2434
2440
|
if (platform === "codex") {
|
|
2435
|
-
return "Codex: at the merge/deploy gate, call codex_app.list_threads unfiltered, filter to peers in the same repo,
|
|
2441
|
+
return "Codex: at the merge/deploy gate, call codex_app.list_threads unfiltered, filter to peers in the same repo, and inspect their current summaries/status. Use codex_app.send_message_to_thread only for visible evidence of an unlanded ready commit, overlapping integration, or a concrete blocker. Peers with nothing actionable stay silent; do not solicit or send negative acknowledgements or routine completion follow-ups. Delivery owner: rename this task `DEPLOY: <existing title>` for operator visibility only; transfer the prefix on handoff; remove it before reporting delivery complete or abandoned.";
|
|
2436
2442
|
}
|
|
2437
2443
|
if (platform === "claude-code") {
|
|
2438
|
-
return "Claude Code: at the merge/deploy gate,
|
|
2444
|
+
return "Claude Code: at the merge/deploy gate, inspect peers in the same repo and send one concise delivery notice only for visible evidence of an unlanded ready commit, overlapping integration, or a concrete blocker. Peers with nothing actionable stay silent; do not solicit or send negative acknowledgements or routine completion follow-ups.";
|
|
2439
2445
|
}
|
|
2440
2446
|
if (platform === "opencode") {
|
|
2441
|
-
return "OpenCode: at the merge/deploy gate, use the GrepRAG project/session registry and send one concise
|
|
2447
|
+
return "OpenCode: at the merge/deploy gate, use the GrepRAG project/session registry and send one concise notice only for visible evidence of an unlanded ready commit, overlapping integration, or a concrete blocker. Peers with nothing actionable stay silent; do not solicit or send negative acknowledgements or routine completion follow-ups.";
|
|
2442
2448
|
}
|
|
2443
|
-
return "At the merge/deploy gate,
|
|
2449
|
+
return "At the merge/deploy gate, inspect peers in the same repo and send one concise notice only for visible evidence of an unlanded ready commit, overlapping integration, or a concrete blocker. Peers with nothing actionable stay silent; do not solicit or send negative acknowledgements or routine completion follow-ups.";
|
|
2444
2450
|
}
|
|
2445
2451
|
function buildDeliveryAnnounce(env) {
|
|
2446
2452
|
if (!env.deliveryRepo)
|
|
@@ -2450,7 +2456,7 @@ function buildDeliveryAnnounce(env) {
|
|
|
2450
2456
|
`[DELIVERY \u2014 ${project}]`,
|
|
2451
2457
|
"Commit useful, passing work.",
|
|
2452
2458
|
coordinationLine(env.platform),
|
|
2453
|
-
"Include ready peer commits and clear known blockers, then merge to the default branch and deploy immediately.
|
|
2459
|
+
"Include visible ready peer commits and clear known blockers, then merge to the default branch and deploy immediately. Silence means nothing actionable and does not block delivery.",
|
|
2454
2460
|
"Use this repo's delivery profile and verify production."
|
|
2455
2461
|
].join("\n");
|
|
2456
2462
|
}
|
|
@@ -2461,6 +2467,14 @@ var deliveryControlModule = {
|
|
|
2461
2467
|
reminder: () => null
|
|
2462
2468
|
};
|
|
2463
2469
|
|
|
2470
|
+
// src/commands/persona-reminder.ts
|
|
2471
|
+
var personaAnnounceModule = {
|
|
2472
|
+
id: "persona-announce",
|
|
2473
|
+
detect: () => ({ tier: "silent" }),
|
|
2474
|
+
announce: (env) => env.personaAnnounce || null,
|
|
2475
|
+
reminder: () => null
|
|
2476
|
+
};
|
|
2477
|
+
|
|
2464
2478
|
// src/commands/reminder-registry.ts
|
|
2465
2479
|
var REGISTRY = [
|
|
2466
2480
|
osPrimerModule,
|
|
@@ -2482,6 +2496,8 @@ var REGISTRY = [
|
|
|
2482
2496
|
// active project procedures, rendered generically from operator-owned installs
|
|
2483
2497
|
deliveryControlModule,
|
|
2484
2498
|
// repo-scoped announce-first Delivery System pilot (all harnesses)
|
|
2499
|
+
personaAnnounceModule,
|
|
2500
|
+
// tenant-scoped Persona, shared across projects/harnesses
|
|
2485
2501
|
loadoutRegistrarModule,
|
|
2486
2502
|
// equipped loadouts: one advisor announce each + per-turn keyword Match (docs/loadout.md)
|
|
2487
2503
|
setupWarningModule,
|
|
@@ -2585,12 +2601,16 @@ function buildOpenCodeEnv(params) {
|
|
|
2585
2601
|
corpusApiDocs: params.corpusApiDocs ?? [],
|
|
2586
2602
|
updateAvailable: params.updateAvailable ?? null,
|
|
2587
2603
|
procedureAnnounces: params.procedureAnnounces ?? [],
|
|
2588
|
-
mirroredSkills: params.mirroredSkills
|
|
2604
|
+
mirroredSkills: params.mirroredSkills,
|
|
2605
|
+
personaAnnounce: params.personaAnnounce ?? null
|
|
2589
2606
|
};
|
|
2590
2607
|
}
|
|
2591
2608
|
function getOpenCodeAnnounces(env) {
|
|
2592
2609
|
return [...collectAnnounces(env, opencodeRegistry), OPENCODE_QUOTA_WAITLIST_ANNOUNCE];
|
|
2593
2610
|
}
|
|
2611
|
+
function getOpenCodePersistentAnnounces(env) {
|
|
2612
|
+
return collectAnnounces(env, opencodeRegistry.filter((m) => m.id === "persona-announce"));
|
|
2613
|
+
}
|
|
2594
2614
|
function getOpenCodeReminders(env) {
|
|
2595
2615
|
return collectReminders(env, opencodeRegistry);
|
|
2596
2616
|
}
|
|
@@ -4290,6 +4310,8 @@ async function fetchRecapInbox(anchor) {
|
|
|
4290
4310
|
}
|
|
4291
4311
|
var relayArmedBySession = /* @__PURE__ */ new Set();
|
|
4292
4312
|
var announcedBySession = /* @__PURE__ */ new Set();
|
|
4313
|
+
var personaAnnounceBySession = /* @__PURE__ */ new Map();
|
|
4314
|
+
var PERSONA_SESSION_CACHE_MAX = 100;
|
|
4293
4315
|
var turnCountBySession = /* @__PURE__ */ new Map();
|
|
4294
4316
|
var procedurePromptKeyBySession = /* @__PURE__ */ new Map();
|
|
4295
4317
|
var cachedRecap = null;
|
|
@@ -4327,6 +4349,32 @@ async function fetchSkillActivation(apiKey, cwd) {
|
|
|
4327
4349
|
return void 0;
|
|
4328
4350
|
}
|
|
4329
4351
|
}
|
|
4352
|
+
async function fetchPersonaAnnounce(apiKey) {
|
|
4353
|
+
try {
|
|
4354
|
+
const response = await fetch(`${API_URL}/v1/persona`, {
|
|
4355
|
+
headers: { Authorization: `Bearer ${apiKey}` }
|
|
4356
|
+
});
|
|
4357
|
+
if (!response.ok)
|
|
4358
|
+
return null;
|
|
4359
|
+
const data = await response.json();
|
|
4360
|
+
return typeof data.announce === "string" && data.announce.length > 0 ? data.announce : null;
|
|
4361
|
+
} catch {
|
|
4362
|
+
return null;
|
|
4363
|
+
}
|
|
4364
|
+
}
|
|
4365
|
+
async function personaAnnounceForSession(sessionId, apiKey) {
|
|
4366
|
+
if (personaAnnounceBySession.has(sessionId)) {
|
|
4367
|
+
return personaAnnounceBySession.get(sessionId) ?? null;
|
|
4368
|
+
}
|
|
4369
|
+
const announce = await fetchPersonaAnnounce(apiKey);
|
|
4370
|
+
if (personaAnnounceBySession.size >= PERSONA_SESSION_CACHE_MAX) {
|
|
4371
|
+
const oldest = personaAnnounceBySession.keys().next().value;
|
|
4372
|
+
if (oldest)
|
|
4373
|
+
personaAnnounceBySession.delete(oldest);
|
|
4374
|
+
}
|
|
4375
|
+
personaAnnounceBySession.set(sessionId, announce);
|
|
4376
|
+
return announce;
|
|
4377
|
+
}
|
|
4330
4378
|
function startSessionRelay(sessionId, serverUrl) {
|
|
4331
4379
|
if (process.env.GREPRAG_OPENCODE_RELAY === "0")
|
|
4332
4380
|
return;
|
|
@@ -4661,6 +4709,7 @@ ALWAYS pass \`--from-session ${sid82}\` when you run \`greprag send\`, so recipi
|
|
|
4661
4709
|
if (sid8) {
|
|
4662
4710
|
const turnCount = (turnCountBySession.get(sid8) ?? 0) + 1;
|
|
4663
4711
|
turnCountBySession.set(sid8, turnCount);
|
|
4712
|
+
const firstAnnounce = !announcedBySession.has(sid8);
|
|
4664
4713
|
const env = buildOpenCodeEnv({
|
|
4665
4714
|
short: sid8,
|
|
4666
4715
|
turnCount,
|
|
@@ -4676,9 +4725,12 @@ ALWAYS pass \`--from-session ${sid82}\` when you run \`greprag send\`, so recipi
|
|
|
4676
4725
|
return [];
|
|
4677
4726
|
}
|
|
4678
4727
|
})(),
|
|
4679
|
-
mirroredSkills:
|
|
4728
|
+
mirroredSkills: firstAnnounce ? await fetchSkillActivation(apiKey, workingDir) : void 0,
|
|
4729
|
+
// output.system is fresh on EVERY transform call. Resolve Persona
|
|
4730
|
+
// once/session, then replay the cached shared-registry announce.
|
|
4731
|
+
personaAnnounce: await personaAnnounceForSession(sid8, apiKey)
|
|
4680
4732
|
});
|
|
4681
|
-
if (
|
|
4733
|
+
if (firstAnnounce) {
|
|
4682
4734
|
announcedBySession.add(sid8);
|
|
4683
4735
|
const announces = getOpenCodeAnnounces(env);
|
|
4684
4736
|
if (announces.length > 0) {
|
|
@@ -4687,6 +4739,13 @@ ALWAYS pass \`--from-session ${sid82}\` when you run \`greprag send\`, so recipi
|
|
|
4687
4739
|
}
|
|
4688
4740
|
dlog(`interrupt: pushed ${announces.length} announce(s) for sid=${sid8}`);
|
|
4689
4741
|
}
|
|
4742
|
+
} else {
|
|
4743
|
+
const persistent = getOpenCodePersistentAnnounces(env);
|
|
4744
|
+
for (const line of persistent)
|
|
4745
|
+
pushSystemPrompt(output, line);
|
|
4746
|
+
if (persistent.length > 0) {
|
|
4747
|
+
dlog(`interrupt: replayed ${persistent.length} persistent announce(s) for sid=${sid8}`);
|
|
4748
|
+
}
|
|
4690
4749
|
}
|
|
4691
4750
|
const reminders = getOpenCodeReminders(env);
|
|
4692
4751
|
if (reminders.length > 0) {
|
package/dist/opencode-plugin.js
CHANGED
|
@@ -548,6 +548,8 @@ async function fetchRecapInbox(anchor) {
|
|
|
548
548
|
// ============================================================================
|
|
549
549
|
const relayArmedBySession = new Set();
|
|
550
550
|
const announcedBySession = new Set();
|
|
551
|
+
const personaAnnounceBySession = new Map();
|
|
552
|
+
const PERSONA_SESSION_CACHE_MAX = 100;
|
|
551
553
|
const turnCountBySession = new Map();
|
|
552
554
|
const procedurePromptKeyBySession = new Map();
|
|
553
555
|
/** Cached recap body per projectId. opencode fires
|
|
@@ -596,6 +598,35 @@ async function fetchSkillActivation(apiKey, cwd) {
|
|
|
596
598
|
return undefined;
|
|
597
599
|
}
|
|
598
600
|
}
|
|
601
|
+
async function fetchPersonaAnnounce(apiKey) {
|
|
602
|
+
try {
|
|
603
|
+
const response = await fetch(`${API_URL}/v1/persona`, {
|
|
604
|
+
headers: { Authorization: `Bearer ${apiKey}` },
|
|
605
|
+
});
|
|
606
|
+
if (!response.ok)
|
|
607
|
+
return null;
|
|
608
|
+
const data = await response.json();
|
|
609
|
+
return typeof data.announce === 'string' && data.announce.length > 0
|
|
610
|
+
? data.announce
|
|
611
|
+
: null;
|
|
612
|
+
}
|
|
613
|
+
catch {
|
|
614
|
+
return null;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
async function personaAnnounceForSession(sessionId, apiKey) {
|
|
618
|
+
if (personaAnnounceBySession.has(sessionId)) {
|
|
619
|
+
return personaAnnounceBySession.get(sessionId) ?? null;
|
|
620
|
+
}
|
|
621
|
+
const announce = await fetchPersonaAnnounce(apiKey);
|
|
622
|
+
if (personaAnnounceBySession.size >= PERSONA_SESSION_CACHE_MAX) {
|
|
623
|
+
const oldest = personaAnnounceBySession.keys().next().value;
|
|
624
|
+
if (oldest)
|
|
625
|
+
personaAnnounceBySession.delete(oldest);
|
|
626
|
+
}
|
|
627
|
+
personaAnnounceBySession.set(sessionId, announce);
|
|
628
|
+
return announce;
|
|
629
|
+
}
|
|
599
630
|
// ============================================================================
|
|
600
631
|
// Per-session relay spawner — "anytime opencode is opened and being used"
|
|
601
632
|
// ============================================================================
|
|
@@ -1002,6 +1033,7 @@ const GrepRAGMemoryPlugin = async (ctx) => {
|
|
|
1002
1033
|
if (sid8) {
|
|
1003
1034
|
const turnCount = (turnCountBySession.get(sid8) ?? 0) + 1;
|
|
1004
1035
|
turnCountBySession.set(sid8, turnCount);
|
|
1036
|
+
const firstAnnounce = !announcedBySession.has(sid8);
|
|
1005
1037
|
// Build env — only populate what's available in the opencode context
|
|
1006
1038
|
const env = (0, opencode_interrupt_1.buildOpenCodeEnv)({
|
|
1007
1039
|
short: sid8,
|
|
@@ -1019,12 +1051,15 @@ const GrepRAGMemoryPlugin = async (ctx) => {
|
|
|
1019
1051
|
return [];
|
|
1020
1052
|
}
|
|
1021
1053
|
})(),
|
|
1022
|
-
mirroredSkills:
|
|
1054
|
+
mirroredSkills: firstAnnounce
|
|
1023
1055
|
? await fetchSkillActivation(apiKey, workingDir)
|
|
1024
1056
|
: undefined,
|
|
1057
|
+
// output.system is fresh on EVERY transform call. Resolve Persona
|
|
1058
|
+
// once/session, then replay the cached shared-registry announce.
|
|
1059
|
+
personaAnnounce: await personaAnnounceForSession(sid8, apiKey),
|
|
1025
1060
|
});
|
|
1026
1061
|
// Announces: inject on first fire per session (static primers)
|
|
1027
|
-
if (
|
|
1062
|
+
if (firstAnnounce) {
|
|
1028
1063
|
announcedBySession.add(sid8);
|
|
1029
1064
|
const announces = (0, opencode_interrupt_1.getOpenCodeAnnounces)(env);
|
|
1030
1065
|
if (announces.length > 0) {
|
|
@@ -1034,6 +1069,14 @@ const GrepRAGMemoryPlugin = async (ctx) => {
|
|
|
1034
1069
|
dlog(`interrupt: pushed ${announces.length} announce(s) for sid=${sid8}`);
|
|
1035
1070
|
}
|
|
1036
1071
|
}
|
|
1072
|
+
else {
|
|
1073
|
+
const persistent = (0, opencode_interrupt_1.getOpenCodePersistentAnnounces)(env);
|
|
1074
|
+
for (const line of persistent)
|
|
1075
|
+
pushSystemPrompt(output, line);
|
|
1076
|
+
if (persistent.length > 0) {
|
|
1077
|
+
dlog(`interrupt: replayed ${persistent.length} persistent announce(s) for sid=${sid8}`);
|
|
1078
|
+
}
|
|
1079
|
+
}
|
|
1037
1080
|
// Reminders: inject every turn (dynamic nudges)
|
|
1038
1081
|
const reminders = (0, opencode_interrupt_1.getOpenCodeReminders)(env);
|
|
1039
1082
|
if (reminders.length > 0) {
|
package/dist/session-id.js
CHANGED
|
@@ -45,6 +45,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
45
45
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
46
46
|
exports.isUuidV7 = isUuidV7;
|
|
47
47
|
exports.truncateSessionId = truncateSessionId;
|
|
48
|
+
exports.isSessionAddressTarget = isSessionAddressTarget;
|
|
48
49
|
exports.sameSessionId = sameSessionId;
|
|
49
50
|
exports.readSessionEnv = readSessionEnv;
|
|
50
51
|
exports.peerHumanTag = peerHumanTag;
|
|
@@ -83,6 +84,18 @@ function truncateSessionId(sessionId) {
|
|
|
83
84
|
return hex.slice(0, 16);
|
|
84
85
|
return hex.slice(0, 8);
|
|
85
86
|
}
|
|
87
|
+
function isSessionAddressTarget(target) {
|
|
88
|
+
if (!target)
|
|
89
|
+
return false;
|
|
90
|
+
const t = target.trim();
|
|
91
|
+
if (FULL_UUID_RE.test(t))
|
|
92
|
+
return true;
|
|
93
|
+
if (/^[0-9a-f]{8}$/i.test(t))
|
|
94
|
+
return true;
|
|
95
|
+
if (/^[0-9a-f]{16}$/i.test(t))
|
|
96
|
+
return true;
|
|
97
|
+
return false;
|
|
98
|
+
}
|
|
86
99
|
function sameSessionId(left, right) {
|
|
87
100
|
if (!left || !right)
|
|
88
101
|
return false;
|
package/package.json
CHANGED
package/skill/greprag/SKILL.md
CHANGED
|
@@ -17,7 +17,7 @@ description: |
|
|
|
17
17
|
Codex work", "isolated Codex session", or "repo collisions".
|
|
18
18
|
metadata:
|
|
19
19
|
author: travsteward
|
|
20
|
-
version: "3.
|
|
20
|
+
version: "3.11.0"
|
|
21
21
|
repository: https://github.com/travsteward/greprag
|
|
22
22
|
license: MIT
|
|
23
23
|
---
|
|
@@ -30,7 +30,7 @@ Platform setup is progressive:
|
|
|
30
30
|
- **Codex**: see `docs/setup.md § codex` after install (`greprag init --codex --tenant-id <handle>`, then Codex Desktop Settings -> Settings -> Hooks trust review).
|
|
31
31
|
- **Claude Code**: see `docs/setup.md § claude-code` for the default hook/Monitor/conventions path (`greprag init --claude` or detected `greprag init`).
|
|
32
32
|
- **OpenCode**: see `docs/setup.md § opencode` for plugin install (`greprag init --opencode`).
|
|
33
|
-
- **Grok Build**:
|
|
33
|
+
- **Grok Build TUI**: exact method below + `docs/platform-grok.md`. Setup: `greprag init --grok --tenant-id <handle>`, then `/hooks` → `r`.
|
|
34
34
|
|
|
35
35
|
For collision-safe parallel Codex implementation, read `docs/codex-chip.md`.
|
|
36
36
|
|
|
@@ -139,9 +139,42 @@ All accept `--project <name>`, `--from ISO --to ISO`, `--format markdown|json`.
|
|
|
139
139
|
|
|
140
140
|
Aliases (silent back-compat): `greprag memory briefing` → `recap` (renamed v5.16.0); `greprag odyssey ...` still works for v5.8.0-era scripts. Raw curl + `--type` value catalog: `docs/memory-advanced.md`.
|
|
141
141
|
|
|
142
|
+
## Grok Build TUI — exact method
|
|
143
|
+
|
|
144
|
+
Grok is not Claude. SessionStart stdout cannot inject. `monitor` treats **stderr as a wake**. Tool-runner PowerShell is `-NonInteractive` with redirected stdin; its Job Object **kills every child when the tool call returns**.
|
|
145
|
+
|
|
146
|
+
**Session start (every turn-0):**
|
|
147
|
+
1. Short = first **16 hex** of `$GROK_SESSION_ID` with dashes stripped (never 8 — UUIDv7 8-hex collides for ~65s).
|
|
148
|
+
2. If `~/.greprag/grok-context/<16-hex>.md` exists, Read it. Else `greprag memory recap`.
|
|
149
|
+
3. Send/reply with `--from-session $GROK_SESSION_ID` (full UUID).
|
|
150
|
+
|
|
151
|
+
**Arm inbox (once per session):** Grok tool `monitor`, `persistent: true`, `description: "greprag inbox <16-hex>"`, command:
|
|
152
|
+
|
|
153
|
+
```
|
|
154
|
+
greprag inbox watch --session $GROK_SESSION_ID --json --quiet
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
- Not Claude Monitor. Not bash `while true`. `--quiet` is required.
|
|
158
|
+
- If a greprag inbox monitor is **already listed**, do not start another. Instant exit = already armed, not a crash.
|
|
159
|
+
- Floor: Stop-hook `drain` injects unread mail even unarmed. Do not nag UNARMED every Stop.
|
|
160
|
+
|
|
161
|
+
**Chip (visible TUI):** `greprag load grok-chip-spawn` FIRST, then `greprag grok spawn`. Child is a real window + own inbox watch. **Helper:** `spawn_subagent` `background: true` (dies with this session, no mesh). Parent keeps **one** watch. Send:
|
|
162
|
+
|
|
163
|
+
```
|
|
164
|
+
greprag send "…" --to travis@greprag.com/<child-full-uuid> --from-session $GROK_SESSION_ID
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Child watch events may appear on the parent TUI — resume the child to act. Do not arm a second parent watch.
|
|
168
|
+
|
|
169
|
+
**Secrets / TTY (npm login, Read-Host, sticky consoles):**
|
|
170
|
+
- `Read-Host` throws. Do not paste secrets in chat.
|
|
171
|
+
- In-process paste: `powershell -STA -NoProfile -File $env:USERPROFILE\.claude\scripts\prompt-secret.ps1 …` (WinForms, prefix-only stdout).
|
|
172
|
+
- `Start-Process` / `cmd /c start` windows **die with the tool call** (Job Object). A console that must outlive the call: `schtasks /Create … /IT` then `schtasks /Run` (outside the job).
|
|
173
|
+
- npm publish with Windows Hello passkey: `npm login --auth-type=web` in that scheduled-task `cmd /k` window → browser **Use security key** / Hello PIN. If the page then shows a **token**, that is the CLI OTP — not the Hello PIN. `--otp` is TOTP or that page token only. Publish is a second Hello. Docs: https://docs.npmjs.com/accessing-npm-using-2fa/
|
|
174
|
+
|
|
142
175
|
## Proactive-fire rules
|
|
143
176
|
|
|
144
|
-
**Grok Build: ABOUT TO ARM INBOX? USE Grok `monitor`
|
|
177
|
+
**Grok Build: ABOUT TO ARM INBOX? USE the Grok TUI method above — `monitor` + `greprag inbox watch --session $GROK_SESSION_ID --json --quiet`.** One watch. Instant exit = armed. Full UUID / 16-hex, never 8-hex.
|
|
145
178
|
|
|
146
179
|
**Claude Code: ABOUT TO BACKGROUND A `greprag inbox watch`? USE THE `Monitor` AGENT TOOL, NOT `Bash(run_in_background: true)`.** Bash background notifies only on process completion; watchers run forever, so the agent gets zero events until it manually reads the output file. Arm it with the **exit-aware relauncher** — `while true; do greprag inbox watch --session <short> --json --owner-pid <pid>; case $? in 0|64) break;; esac; sleep 1; done` — under Monitor `persistent:true` (this is exactly what the arm hook prints; paste it verbatim). The loop is supervisor-death recovery: Monitor `persistent:true` does NOT re-run the command on exit, so without it a crashed supervisor only recovers on the next turn. The `case $? in 0|64) break` is what makes it safe — it breaks on clean/consumer-gone (0) and fatal/bad-key (64), relaunching ONLY on a crash. (A *bare* `while true; …; sleep 1; done` WITHOUT that break is the 2026-06-04 OOM orphan — don't use that form; the exit-aware version was restored 2026-06-14.) Full pattern: `docs/inbox-watch.md`.
|
|
147
180
|
|
|
@@ -165,6 +198,7 @@ Aliases (silent back-compat): `greprag memory briefing` → `recap` (renamed v5.
|
|
|
165
198
|
|
|
166
199
|
- `docs/setup.md` — codex · claude-code · opencode · auth · hooks · conventions · permissions · channels · anchor · bulk-register
|
|
167
200
|
- `docs/platforms.md` — exact platform paths for Claude Code · Codex · OpenCode · Grok Build
|
|
201
|
+
- `docs/platform-grok.md` — Grok Build TUI method (hooks, sidecar, monitor, Job Object)
|
|
168
202
|
- `docs/codex-chip.md` — Codex quick-chip, Leader, reporting, and cleanup shape
|
|
169
203
|
- `docs/per-project-flags.md` — flip `memory_capture` / `session_start_recap` / `inbox_notify`
|
|
170
204
|
- `docs/inbox.md` — `greprag send`, `greprag inbox`, address grammar, retract (internal messaging)
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
# Chip Spawn Method
|
|
2
2
|
|
|
3
|
+
**Grok Build?** Stop. Run `greprag load grok-chip-spawn`. This entry is Claude `spawn_task`.
|
|
4
|
+
|
|
3
5
|
The `pre-spawn-check` PreToolUse hook **validates** chip prompts at the spawn boundary, but **cannot inject content** — `modifiedInput` is silently dropped by the CCD harness for MCP `spawn_task` calls (see `adr/spawn-task-hook-mode.md` 2026-05-27 entry). The agent writes Block 1 + Block 2 into the prompt itself. Validator rejects with `permissionDecision: deny` and a remediation reason if Block 1 or Block 2 markers are missing.
|
|
4
6
|
|
|
5
7
|
> **Part of a multi-chip mission?** If this chip is one of ≥2 aimed at a single objective, you should already be inside a chip-leader plan — your **base branch** and **merge target** (the integration branch, *never* master) come from it. If you're not, stop and run `greprag load chip-leader` first. A lone chip targeting its own objective proceeds here directly.
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
# Grok Chip Spawn Method
|
|
2
|
+
|
|
3
|
+
The Grok chip is a **real TUI**: `greprag grok spawn` opens a `cmd /k` bootloader
|
|
4
|
+
outside the tool Job Object. The child is its own Grok session (`$GROK_SESSION_ID`),
|
|
5
|
+
arms its own quiet inbox watch, and talks on `greprag send`. Proven 2026-08-21
|
|
6
|
+
(parent ping → child pong on the mesh).
|
|
7
|
+
|
|
8
|
+
`spawn_subagent` is **not** a chip. It is a same-pager helper: no window, no
|
|
9
|
+
inbox watch, dies with the parent. Use it for a bounded look-up. Never for a
|
|
10
|
+
unit of work that must survive this session or that the operator should sit in.
|
|
11
|
+
|
|
12
|
+
> **Part of a multi-chip mission?** If this chip is one of ≥2 aimed at a single
|
|
13
|
+
> objective, stop and run `greprag load chip-leader` first. A lone chip at its
|
|
14
|
+
> own objective proceeds here.
|
|
15
|
+
|
|
16
|
+
There is no `pre-spawn-check` on Grok. You write Block 1 + task + Block 2 into
|
|
17
|
+
the prompt yourself.
|
|
18
|
+
|
|
19
|
+
## What the parent provides
|
|
20
|
+
|
|
21
|
+
- `title: "Chip: <verb-phrase>"` — or leader label `"Chip A: …"` / `"Chip B: …"`.
|
|
22
|
+
- Isolated worktree, then spawn `--cwd` **into that worktree**.
|
|
23
|
+
- Prompt file: Block 1 + task body + Block 2.
|
|
24
|
+
- Parent already has **one** quiet watch. Do not arm a second.
|
|
25
|
+
|
|
26
|
+
`<slug>` = title slugified (`Chip: Fix synthesis loop` → `fix-synthesis-loop`).
|
|
27
|
+
`<parent-uuid>` = **this** session's `$GROK_SESSION_ID` (full UUID). Short form
|
|
28
|
+
for display is **16 hex**, never 8 (UUIDv7 8-hex collides ~65s).
|
|
29
|
+
`<handle>` = operator greprag handle (from `greprag whoami`).
|
|
30
|
+
|
|
31
|
+
## Parent — worktree then spawn (PowerShell)
|
|
32
|
+
|
|
33
|
+
```powershell
|
|
34
|
+
$repo = (git rev-parse --show-toplevel)
|
|
35
|
+
$slug = "<slug>"
|
|
36
|
+
$wt = Join-Path $repo ".claude\worktrees\$slug"
|
|
37
|
+
if (-not (Test-Path $wt)) {
|
|
38
|
+
git worktree add $wt -b "chip/$slug"
|
|
39
|
+
}
|
|
40
|
+
$promptFile = Join-Path $env:TEMP "greprag-chip-$slug.md"
|
|
41
|
+
Set-Content -LiteralPath $promptFile -Value $prompt -Encoding utf8
|
|
42
|
+
greprag grok spawn --cwd $wt --title "Chip: <verb-phrase>" --prompt-file $promptFile
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
`$prompt` is Block 1 + task + Block 2 below. After spawn, wait for `IN-FLIGHT`
|
|
46
|
+
on **your** watch. No ping = the window did not boot — do not wait on results.
|
|
47
|
+
|
|
48
|
+
## Block 1 — Setup (verbatim, substitute parent uuid + handle)
|
|
49
|
+
|
|
50
|
+
The chip starts already in the worktree (`--cwd`). It substitutes
|
|
51
|
+
`<own-session-id>` from `$GROK_SESSION_ID`.
|
|
52
|
+
|
|
53
|
+
````
|
|
54
|
+
**Setup — do this FIRST:**
|
|
55
|
+
|
|
56
|
+
1. Session id is `$GROK_SESSION_ID` (full UUID). Short = first 16 hex, dashes stripped.
|
|
57
|
+
2. Arm ONE quiet watch (skip if a greprag inbox monitor is already listed):
|
|
58
|
+
|
|
59
|
+
```
|
|
60
|
+
greprag inbox watch --session $GROK_SESSION_ID --json --quiet
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Grok `monitor`, `persistent:true`. Not Claude Monitor. Instant exit = already armed.
|
|
64
|
+
|
|
65
|
+
3. Ping the parent:
|
|
66
|
+
|
|
67
|
+
```
|
|
68
|
+
greprag send "IN-FLIGHT: chip/<slug> launched — working" --to <handle>@greprag.com/<parent-uuid> --from-session $GROK_SESSION_ID
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
4. If `scripts/worktree-bootstrap.cjs` exists, run `node scripts/worktree-bootstrap.cjs`.
|
|
72
|
+
````
|
|
73
|
+
|
|
74
|
+
## Task body
|
|
75
|
+
|
|
76
|
+
The work. Isolated to this worktree / `chip/<slug>`. Default autonomous. To pause
|
|
77
|
+
for a human, say so in the body and `BLOCKED` ping the parent.
|
|
78
|
+
|
|
79
|
+
## Block 2 — Report back (verbatim)
|
|
80
|
+
|
|
81
|
+
````
|
|
82
|
+
**Block 2 — Report back via greprag inbox:**
|
|
83
|
+
|
|
84
|
+
```
|
|
85
|
+
greprag send "<status>: <commit hash> on chip/<slug> — <one-line>" --to <handle>@greprag.com/<parent-uuid> --from-session $GROK_SESSION_ID --artifact commit:<hash>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
Cleanup HARD RULE: no `git clean`, `git reset --hard`, `git worktree remove`,
|
|
89
|
+
`git checkout <other>`, raw `rm -rf` outside this worktree's tracked files.
|
|
90
|
+
````
|
|
91
|
+
|
|
92
|
+
## After spawning (parent)
|
|
93
|
+
|
|
94
|
+
Keep **one** watch. Talk with `greprag send --to <handle>@greprag.com/<child-full-uuid> --from-session $GROK_SESSION_ID`.
|
|
95
|
+
`greprag inbox watchers` lists the child once it has armed. Child watch events
|
|
96
|
+
may print on this TUI — they belong to the child; do not steal its turn.
|
|
97
|
+
|
|
98
|
+
Merge / prune: same HARD RULES as `greprag load chip-spawn` (junction guard,
|
|
99
|
+
`git worktree remove .claude/worktrees/<slug>`, `git branch -d chip/<slug>`).
|
|
100
|
+
FIX chips: `greprag fix spawn` still prints the mission; you launch it with
|
|
101
|
+
this method instead of `spawn_task`.
|