greprag 5.75.0 → 5.76.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/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 +15 -5
- package/dist/commands/load-primer-reminder.js +8 -1
- package/dist/commands/load.js +4 -0
- package/dist/commands/os-primer-reminder.js +2 -1
- package/dist/commands/status.js +1 -1
- package/dist/hook.js +13 -7
- package/dist/index.js +45 -11
- package/dist/opencode-plugin.bundle.js +8 -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
|
@@ -0,0 +1,208 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/** Open a fresh Grok Build TUI from another agent session.
|
|
3
|
+
*
|
|
4
|
+
* Grok's tool-runner Job Object kills `Start-Process` / `cmd /c start`
|
|
5
|
+
* children when the tool call returns. The surviving launch is a scheduled
|
|
6
|
+
* task (`schtasks /Create /IT` then `/Run`) that `start`s a new `cmd /k`
|
|
7
|
+
* window running grok.exe — a visible command-prompt bootloader outside the
|
|
8
|
+
* job. adr: adr/grok-platform.md */
|
|
9
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
12
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
13
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
14
|
+
}
|
|
15
|
+
Object.defineProperty(o, k2, desc);
|
|
16
|
+
}) : (function(o, m, k, k2) {
|
|
17
|
+
if (k2 === undefined) k2 = k;
|
|
18
|
+
o[k2] = m[k];
|
|
19
|
+
}));
|
|
20
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
21
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
22
|
+
}) : function(o, v) {
|
|
23
|
+
o["default"] = v;
|
|
24
|
+
});
|
|
25
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
26
|
+
var ownKeys = function(o) {
|
|
27
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
28
|
+
var ar = [];
|
|
29
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
30
|
+
return ar;
|
|
31
|
+
};
|
|
32
|
+
return ownKeys(o);
|
|
33
|
+
};
|
|
34
|
+
return function (mod) {
|
|
35
|
+
if (mod && mod.__esModule) return mod;
|
|
36
|
+
var result = {};
|
|
37
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
38
|
+
__setModuleDefault(result, mod);
|
|
39
|
+
return result;
|
|
40
|
+
};
|
|
41
|
+
})();
|
|
42
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
43
|
+
exports.resolveGrokExe = resolveGrokExe;
|
|
44
|
+
exports.buildGrokSpawnScript = buildGrokSpawnScript;
|
|
45
|
+
exports.grokSpawnHelp = grokSpawnHelp;
|
|
46
|
+
exports.runGrokSpawn = runGrokSpawn;
|
|
47
|
+
exports.runGrok = runGrok;
|
|
48
|
+
const fs = __importStar(require("fs"));
|
|
49
|
+
const os = __importStar(require("os"));
|
|
50
|
+
const path = __importStar(require("path"));
|
|
51
|
+
const child_process_1 = require("child_process");
|
|
52
|
+
function winQuote(value) {
|
|
53
|
+
return `"${value.replace(/"/g, '""')}"`;
|
|
54
|
+
}
|
|
55
|
+
function resolveGrokExe() {
|
|
56
|
+
const home = os.homedir();
|
|
57
|
+
const named = process.platform === 'win32' ? 'grok.exe' : 'grok';
|
|
58
|
+
const pinned = path.join(home, '.grok', 'bin', named);
|
|
59
|
+
if (fs.existsSync(pinned))
|
|
60
|
+
return pinned;
|
|
61
|
+
try {
|
|
62
|
+
const out = (0, child_process_1.execFileSync)(process.platform === 'win32' ? 'where.exe' : 'which', ['grok'], {
|
|
63
|
+
encoding: 'utf8', stdio: ['ignore', 'pipe', 'ignore'],
|
|
64
|
+
});
|
|
65
|
+
const first = out.split(/\r?\n/).map(s => s.trim()).find(s => s);
|
|
66
|
+
if (first && fs.existsSync(first))
|
|
67
|
+
return first;
|
|
68
|
+
}
|
|
69
|
+
catch { /* fall through */ }
|
|
70
|
+
return named;
|
|
71
|
+
}
|
|
72
|
+
function buildGrokSpawnScript(opts) {
|
|
73
|
+
const cwd = path.resolve(opts.cwd);
|
|
74
|
+
const title = opts.title || `grok ${path.basename(cwd)}`;
|
|
75
|
+
const grok = opts.grokExe || resolveGrokExe();
|
|
76
|
+
const promptArg = opts.prompt ? ` ${winQuote(opts.prompt)}` : '';
|
|
77
|
+
// Self-relaunch: schtasks runs this hidden; `start cmd /k %~f0 --boot`
|
|
78
|
+
// opens a visible command-prompt bootloader that then execs grok.exe.
|
|
79
|
+
// Immediate task-delete used to cancel the launch — caller waits.
|
|
80
|
+
return [
|
|
81
|
+
'@echo off',
|
|
82
|
+
'if /I "%~1"=="--boot" goto boot',
|
|
83
|
+
`start ${winQuote(title)} cmd /k "%~f0" --boot`,
|
|
84
|
+
'goto :eof',
|
|
85
|
+
':boot',
|
|
86
|
+
`cd /d ${winQuote(cwd)}`,
|
|
87
|
+
`echo [greprag] booting Grok TUI in ${cwd}`,
|
|
88
|
+
`${winQuote(grok)}${promptArg}`,
|
|
89
|
+
'echo.',
|
|
90
|
+
'echo [greprag] grok exited. This window is yours.',
|
|
91
|
+
].join('\r\n') + '\r\n';
|
|
92
|
+
}
|
|
93
|
+
function grokSpawnHelp() {
|
|
94
|
+
return `greprag grok spawn — open a fresh Grok Build TUI in a new command prompt.
|
|
95
|
+
|
|
96
|
+
greprag grok spawn [--cwd <path>] [--prompt "<text>"|--prompt-file <path>] [--title <name>]
|
|
97
|
+
greprag grok spawn --dry-run
|
|
98
|
+
|
|
99
|
+
Windows: schtasks /IT starts a new \`cmd /k\` window (bootloader) that runs
|
|
100
|
+
grok.exe. That escapes the agent tool Job Object. The child is a real TUI
|
|
101
|
+
with its own $GROK_SESSION_ID; it arms its own inbox watch. This session
|
|
102
|
+
keeps one watch. Message it with:
|
|
103
|
+
greprag send "…" --to <handle>@greprag.com/<child-uuid> --from-session $GROK_SESSION_ID
|
|
104
|
+
`;
|
|
105
|
+
}
|
|
106
|
+
function sleepMs(ms) {
|
|
107
|
+
const end = Date.now() + ms;
|
|
108
|
+
while (Date.now() < end) { /* wait for Task Scheduler to start the .cmd */ }
|
|
109
|
+
}
|
|
110
|
+
function runGrokSpawn(args) {
|
|
111
|
+
if (args.includes('--help') || args.includes('-h') || args[0] === 'help') {
|
|
112
|
+
console.log(grokSpawnHelp());
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const cwd = path.resolve(getArg(args, '--cwd') || process.cwd());
|
|
116
|
+
const promptFile = getArg(args, '--prompt-file');
|
|
117
|
+
let prompt = getArg(args, '--prompt') || positionalPrompt(args);
|
|
118
|
+
if (promptFile) {
|
|
119
|
+
if (!fs.existsSync(promptFile)) {
|
|
120
|
+
console.error(`greprag grok spawn: prompt file not found: ${promptFile}`);
|
|
121
|
+
process.exit(1);
|
|
122
|
+
}
|
|
123
|
+
prompt = fs.readFileSync(promptFile, 'utf8').replace(/^\uFEFF/, '').trimEnd();
|
|
124
|
+
}
|
|
125
|
+
const title = getArg(args, '--title');
|
|
126
|
+
const dryRun = args.includes('--dry-run');
|
|
127
|
+
const grokExe = resolveGrokExe();
|
|
128
|
+
const opts = { cwd, prompt, title, grokExe, dryRun };
|
|
129
|
+
if (process.platform !== 'win32') {
|
|
130
|
+
const cmd = [grokExe, '--cwd', cwd, prompt].filter((p) => !!p);
|
|
131
|
+
if (dryRun) {
|
|
132
|
+
console.log(cmd.join(' '));
|
|
133
|
+
return;
|
|
134
|
+
}
|
|
135
|
+
console.error('greprag grok spawn: Windows-only bootloader (Job Object). Run this yourself:');
|
|
136
|
+
console.error(` ${cmd.map(a => /\s/.test(a) ? JSON.stringify(a) : a).join(' ')}`);
|
|
137
|
+
process.exitCode = 1;
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
const script = buildGrokSpawnScript(opts);
|
|
141
|
+
if (dryRun) {
|
|
142
|
+
process.stdout.write(script);
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
if (!fs.existsSync(cwd) || !fs.statSync(cwd).isDirectory()) {
|
|
146
|
+
console.error(`greprag grok spawn: cwd is not a directory: ${cwd}`);
|
|
147
|
+
process.exit(1);
|
|
148
|
+
}
|
|
149
|
+
const tn = `greprag-grok-${Date.now().toString(36)}`;
|
|
150
|
+
const file = path.join(os.tmpdir(), `${tn}.cmd`);
|
|
151
|
+
fs.writeFileSync(file, script, 'utf8');
|
|
152
|
+
try {
|
|
153
|
+
(0, child_process_1.execFileSync)('schtasks', [
|
|
154
|
+
'/Create', '/TN', tn, '/TR', file, '/SC', 'ONCE', '/ST', '23:59',
|
|
155
|
+
'/F', '/IT',
|
|
156
|
+
], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
157
|
+
(0, child_process_1.execFileSync)('schtasks', ['/Run', '/TN', tn], { stdio: ['ignore', 'pipe', 'pipe'] });
|
|
158
|
+
// /Run is async. Deleting immediately cancelled the launch (field 2026-08-21).
|
|
159
|
+
sleepMs(2500);
|
|
160
|
+
}
|
|
161
|
+
catch (err) {
|
|
162
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
163
|
+
console.error(`greprag grok spawn: schtasks failed: ${msg}`);
|
|
164
|
+
process.exit(1);
|
|
165
|
+
}
|
|
166
|
+
finally {
|
|
167
|
+
try {
|
|
168
|
+
(0, child_process_1.execFileSync)('schtasks', ['/Delete', '/TN', tn, '/F'], { stdio: 'ignore' });
|
|
169
|
+
}
|
|
170
|
+
catch { /* leftover task is harmless */ }
|
|
171
|
+
}
|
|
172
|
+
console.log(`Spawned Grok TUI (cmd /k bootloader) for ${cwd}${prompt ? ` — prompt: ${prompt}` : ''}`);
|
|
173
|
+
console.log('The new session arms its own inbox watch. This session keeps one.');
|
|
174
|
+
}
|
|
175
|
+
function runGrok(args) {
|
|
176
|
+
const sub = args[0];
|
|
177
|
+
if (!sub || sub === 'help' || sub === '--help' || sub === '-h') {
|
|
178
|
+
console.log(grokSpawnHelp());
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
if (sub === 'spawn')
|
|
182
|
+
return runGrokSpawn(args.slice(1));
|
|
183
|
+
console.error(`Unknown "grok ${sub}". Run \`greprag grok spawn --help\`.`);
|
|
184
|
+
process.exit(1);
|
|
185
|
+
}
|
|
186
|
+
function getArg(args, flag) {
|
|
187
|
+
const idx = args.indexOf(flag);
|
|
188
|
+
if (idx === -1 || idx + 1 >= args.length)
|
|
189
|
+
return undefined;
|
|
190
|
+
const value = args[idx + 1];
|
|
191
|
+
return value.startsWith('--') ? undefined : value;
|
|
192
|
+
}
|
|
193
|
+
function positionalPrompt(args) {
|
|
194
|
+
const skip = new Set(['--cwd', '--prompt', '--prompt-file', '--title']);
|
|
195
|
+
const out = [];
|
|
196
|
+
for (let i = 0; i < args.length; i++) {
|
|
197
|
+
if (skip.has(args[i])) {
|
|
198
|
+
i += 1;
|
|
199
|
+
continue;
|
|
200
|
+
}
|
|
201
|
+
if (args[i] === '--dry-run' || args[i] === '--help' || args[i] === '-h')
|
|
202
|
+
continue;
|
|
203
|
+
if (args[i].startsWith('--'))
|
|
204
|
+
continue;
|
|
205
|
+
out.push(args[i]);
|
|
206
|
+
}
|
|
207
|
+
return out.length ? out.join(' ') : undefined;
|
|
208
|
+
}
|
|
@@ -9,7 +9,13 @@
|
|
|
9
9
|
* inbox`. This closes it: on every arm (SessionStart startup|resume|compact)
|
|
10
10
|
* drain THIS session's unread, session-DIRECTED messages into the agent's
|
|
11
11
|
* context, tagged peer/human, mark them read, and advance the stored cursor so
|
|
12
|
-
* there is no double-delivery.
|
|
12
|
+
* there is no double-delivery.
|
|
13
|
+
*
|
|
14
|
+
* INJECT ONLY WHEN UNARMED. A live watcher already printed the body (Claude
|
|
15
|
+
* Monitor / Grok monitor stdout). Grok also wires drain on Stop because
|
|
16
|
+
* SessionStart stdout cannot inject — that Stop path is the unarmed floor.
|
|
17
|
+
* Passing `armed: true` still marks inbound unread + advances the cursor, but
|
|
18
|
+
* does not inject (and never claims "no live watcher"). adr: adr/monitor-resilience.md
|
|
13
19
|
*
|
|
14
20
|
* SCOPE = session-directed INBOUND only (to_session_id == me). NOT the front
|
|
15
21
|
* desk (cold opens + email — that's the human-scoped `mail` hook's job) and NOT
|
|
@@ -126,7 +132,31 @@ async function runInboxDrain(opts) {
|
|
|
126
132
|
return none;
|
|
127
133
|
// Chronological (oldest → newest) so a coordination thread reads in order.
|
|
128
134
|
inbound.sort((a, b) => tsOf(a) - tsOf(b));
|
|
129
|
-
// 3.
|
|
135
|
+
// 3. Armed watcher already delivered the body (live stdout). Mark every
|
|
136
|
+
// inbound-unread read and advance the cursor, but do not inject — the
|
|
137
|
+
// unarmed copy would lie, and Grok Stop would duplicate a live interrupt.
|
|
138
|
+
const cursor = newestId(messages);
|
|
139
|
+
if (opts.armed) {
|
|
140
|
+
if (cursor) {
|
|
141
|
+
try {
|
|
142
|
+
writeCursor(opts.session, cursor);
|
|
143
|
+
}
|
|
144
|
+
catch { /* best-effort */ }
|
|
145
|
+
}
|
|
146
|
+
const ids = inbound.map(m => m.id).filter(Boolean);
|
|
147
|
+
if (ids.length > 0) {
|
|
148
|
+
try {
|
|
149
|
+
await doFetch(`${base}/v1/inbox/read`, {
|
|
150
|
+
method: 'POST',
|
|
151
|
+
headers: { Authorization: `Bearer ${opts.apiKey}`, 'Content-Type': 'application/json' },
|
|
152
|
+
body: JSON.stringify({ ids }),
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
catch { /* mark-read is best-effort */ }
|
|
156
|
+
}
|
|
157
|
+
return { context: null, drained: inbound.length, truncated: 0, cursorAdvancedTo: cursor };
|
|
158
|
+
}
|
|
159
|
+
// 4. Cap the injected volume to the NEWEST `cap`. Older inbound-unread stay
|
|
130
160
|
// unread (NOT marked, NOT silently dropped): they remain in `greprag inbox`
|
|
131
161
|
// and the next arm re-drains them. The overflow count is stated below.
|
|
132
162
|
let drain = inbound;
|
|
@@ -135,20 +165,20 @@ async function runInboxDrain(opts) {
|
|
|
135
165
|
truncated = inbound.length - cap;
|
|
136
166
|
drain = inbound.slice(inbound.length - cap); // newest `cap`, still chronological
|
|
137
167
|
}
|
|
138
|
-
//
|
|
168
|
+
// 5. Build the injected context (peer/human tagged). Unarmed-only — this copy
|
|
169
|
+
// is true because we returned above when a live watcher was present.
|
|
139
170
|
const context = buildDrainContext(drain, truncated, opts.session);
|
|
140
|
-
//
|
|
171
|
+
// 6. Advance the poll cursor to the newest endpoint message id so the just-
|
|
141
172
|
// armed poll resumes strictly AFTER everything already in the inbox — no
|
|
142
173
|
// double-delivery. Only when there is something to drain (never disturb a
|
|
143
174
|
// healthy poll's cursor on an empty arm).
|
|
144
|
-
const cursor = newestId(messages);
|
|
145
175
|
if (cursor) {
|
|
146
176
|
try {
|
|
147
177
|
writeCursor(opts.session, cursor);
|
|
148
178
|
}
|
|
149
179
|
catch { /* best-effort */ }
|
|
150
180
|
}
|
|
151
|
-
//
|
|
181
|
+
// 7. Mark the drained (injected) ids read LAST — after inject + cursor — so a
|
|
152
182
|
// crash before this point re-drains next arm rather than losing a message.
|
|
153
183
|
// Inbound ids only, so a peer's view of my outbound is never touched.
|
|
154
184
|
// Fail-quiet: an unreachable / not-yet-deployed /read route leaves the
|
|
@@ -48,7 +48,7 @@ function buildInboxPrimer(env) {
|
|
|
48
48
|
? '[OPENCODE INBOX — 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.]'
|
|
49
49
|
: '[⚠ ARM YOUR INBOX WATCHER NOW — you are UNREACHABLE until you do. Peers are live in your mesh trying to coordinate with this session, and the operator can DM you — 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) — EVERY turn you are flagged unarmed you MUST re-arm, or coordination breaks.]',
|
|
50
50
|
grok
|
|
51
|
-
? `ARM (idle wake): Grok \`monitor\` tool, persistent:true, description:"greprag inbox ${env.short}", command: \`${arm}\`. \`--quiet\` is REQUIRED — Grok treats stderr as wake events; the Claude bash wrapper is PowerShell-invalid. Use full UUID / 16-hex, never 8-hex.
|
|
51
|
+
? `ARM (idle wake): Grok \`monitor\` tool, persistent:true, description:"greprag inbox ${env.short}", command: \`${arm}\`. \`--quiet\` is REQUIRED — Grok treats stderr as wake events; the Claude bash wrapper is PowerShell-invalid. Use full UUID / 16-hex, never 8-hex. A chip → \`greprag load grok-chip-spawn\` then \`greprag grok spawn\` (child arms its own watch; parent keeps ONE). A helper → spawn_subagent. Floor: Stop-hook drain injects unread mail even if you never arm. Then \`greprag inbox\`.`
|
|
52
52
|
: codex
|
|
53
53
|
? '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`.'
|
|
54
54
|
: opencode
|
|
@@ -50,6 +50,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
50
50
|
})();
|
|
51
51
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
52
52
|
exports.CONSUMER_GONE_EXIT_CODE = void 0;
|
|
53
|
+
exports.resolveWatchPlatform = resolveWatchPlatform;
|
|
53
54
|
exports.getConfig = getConfig;
|
|
54
55
|
exports.fmtDuration = fmtDuration;
|
|
55
56
|
exports.parseEventBlock = parseEventBlock;
|
|
@@ -59,6 +60,7 @@ exports.runInboxWatch = runInboxWatch;
|
|
|
59
60
|
const fs = __importStar(require("fs"));
|
|
60
61
|
const path = __importStar(require("path"));
|
|
61
62
|
const session_id_1 = require("../session-id");
|
|
63
|
+
const harness_1 = require("../harness");
|
|
62
64
|
const mechanic_friction_1 = require("../mechanic-friction");
|
|
63
65
|
const inbox_watch_supervisor_1 = require("./inbox-watch-supervisor");
|
|
64
66
|
const API_URL_DEFAULT = 'https://api.greprag.com';
|
|
@@ -84,6 +86,14 @@ exports.CONSUMER_GONE_EXIT_CODE = 65;
|
|
|
84
86
|
// than waiting for the next real message that may never come. Env-overridable
|
|
85
87
|
// for tests (the idle path otherwise takes the full 30s to observe).
|
|
86
88
|
const CONSUMER_PROBE_MS = Number(process.env.GREPRAG_WATCH_PROBE_MS) || 30_000;
|
|
89
|
+
const WATCH_PLATFORMS = new Set(['claude-code', 'codex', 'opencode', 'grok']);
|
|
90
|
+
/** Platform tag for this watch attach. Explicit opt wins; else infer. */
|
|
91
|
+
function resolveWatchPlatform(explicit) {
|
|
92
|
+
if (explicit && WATCH_PLATFORMS.has(explicit))
|
|
93
|
+
return explicit;
|
|
94
|
+
const h = (0, harness_1.inferCurrentHarness)();
|
|
95
|
+
return (h && WATCH_PLATFORMS.has(h)) ? h : undefined;
|
|
96
|
+
}
|
|
87
97
|
// -- Config (mirrors the loader in index.ts/discover.ts) -------------------
|
|
88
98
|
function loadEnvFile(filePath) {
|
|
89
99
|
try {
|
|
@@ -392,7 +402,7 @@ async function readStream(url, apiKey, signal, initialId, json, idleTimeoutMs, s
|
|
|
392
402
|
signal.removeEventListener('abort', onOuterAbort);
|
|
393
403
|
}
|
|
394
404
|
}
|
|
395
|
-
function buildUrl(apiUrl, project, session, since, receptionist, assistant, mechanic) {
|
|
405
|
+
function buildUrl(apiUrl, project, session, since, receptionist, assistant, mechanic, platform) {
|
|
396
406
|
const u = new URL(apiUrl.replace(/\/+$/, '') + '/v1/inbox/stream');
|
|
397
407
|
if (project)
|
|
398
408
|
u.searchParams.set('project', project);
|
|
@@ -406,6 +416,8 @@ function buildUrl(apiUrl, project, session, since, receptionist, assistant, mech
|
|
|
406
416
|
u.searchParams.set('role', 'mechanic');
|
|
407
417
|
else if (assistant)
|
|
408
418
|
u.searchParams.set('role', 'assistant');
|
|
419
|
+
if (platform)
|
|
420
|
+
u.searchParams.set('platform', platform);
|
|
409
421
|
return u.toString();
|
|
410
422
|
}
|
|
411
423
|
/** Public entry for `greprag inbox watch`. Dispatches by mode:
|
|
@@ -545,7 +557,7 @@ async function runWatchLoop(opts) {
|
|
|
545
557
|
watchErr(`${LOG_PREFIX} reconnecting (last_seen_id=${lastSeen})`, opts);
|
|
546
558
|
}
|
|
547
559
|
isFirstAttempt = false;
|
|
548
|
-
const url = buildUrl(cfg.apiUrl, opts.project, opts.session, cursor, !!opts.receptionist, !!opts.assistant, !!opts.mechanic);
|
|
560
|
+
const url = buildUrl(cfg.apiUrl, opts.project, opts.session, cursor, !!opts.receptionist, !!opts.assistant, !!opts.mechanic, resolveWatchPlatform(opts.platform));
|
|
549
561
|
try {
|
|
550
562
|
const lastId = await readStream(url, cfg.apiKey, controller.signal, cursor, !!opts.json, idleTimeoutMs, opts.session);
|
|
551
563
|
if (lastId)
|
package/dist/commands/init.js
CHANGED
|
@@ -668,9 +668,10 @@ Before other work this session:
|
|
|
668
668
|
\`greprag inbox watch --session $GROK_SESSION_ID --json --quiet\`
|
|
669
669
|
Not Claude Monitor. No bash while-true. \`--quiet\` required (stderr is a wake).
|
|
670
670
|
If a greprag inbox monitor is already listed in this session, do not start another. Instant exit = already armed, not a crash.
|
|
671
|
-
4.
|
|
671
|
+
4. Chip: \`greprag load grok-chip-spawn\` then \`greprag grok spawn\` (real TUI + inbox). Helper: \`spawn_subagent\` background=true. Child arms its own quiet watch with ITS \`$GROK_SESSION_ID\`. Parent sends:
|
|
672
672
|
\`greprag send "…" --to travis@greprag.com/<child-full-uuid> --from-session $GROK_SESSION_ID\`
|
|
673
673
|
Parent keeps ONE watch, for itself. Child watch events may appear on the parent TUI — resume the child to act. Do not arm a second parent watch.
|
|
674
|
+
5. Tool shell: stdin redirected + \`-NonInteractive\`. \`Read-Host\` throws. \`Start-Process\` windows die with the tool Job Object. Secret paste: \`~/.claude/scripts/prompt-secret.ps1\`. Sticky console: \`schtasks /Create … /IT\` then \`schtasks /Run\`. npm Hello: \`npm login --auth-type=web\` in that window; browser Use security key; page token ≠ Hello PIN.
|
|
674
675
|
`;
|
|
675
676
|
function getGrokHooksPath() {
|
|
676
677
|
const home = process.env.HOME || process.env.USERPROFILE || os.homedir();
|
|
@@ -691,6 +692,15 @@ function applyGrokHooks(config) {
|
|
|
691
692
|
const changes = [];
|
|
692
693
|
if (!config.hooks)
|
|
693
694
|
config.hooks = {};
|
|
695
|
+
// Empty matcher = every SessionStart source (startup, resume, /new, dashboard
|
|
696
|
+
// dispatch, fork). `startup|resume` missed /new and dashboard agents, so this
|
|
697
|
+
// session never got a recap sidecar. adr: adr/grok-platform.md
|
|
698
|
+
const sessionStartMatcher = '';
|
|
699
|
+
if (!config.hooks.SessionStart)
|
|
700
|
+
config.hooks.SessionStart = [];
|
|
701
|
+
const retargeted = ['recap', 'session-id', 'drain'].reduce((n, sub) => (n + retargetGrepragHookMatcher(config.hooks.SessionStart, sub, ['startup|resume'], sessionStartMatcher)), 0);
|
|
702
|
+
if (retargeted)
|
|
703
|
+
changes.push(`Retargeted Grok SessionStart matcher (${retargeted})`);
|
|
694
704
|
const add = (event, matcher, sub, timeout, label) => {
|
|
695
705
|
if (hasGrepragHook(config.hooks[event], sub)) {
|
|
696
706
|
changes.push(`Grok ${event} ${label} already configured (skipped)`);
|
|
@@ -701,9 +711,9 @@ function applyGrokHooks(config) {
|
|
|
701
711
|
config.hooks[event].push({ matcher, hooks: [grokCommand(sub, timeout)] });
|
|
702
712
|
changes.push(`Added Grok ${event} hook (${label})`);
|
|
703
713
|
};
|
|
704
|
-
add('SessionStart',
|
|
705
|
-
add('SessionStart',
|
|
706
|
-
add('SessionStart',
|
|
714
|
+
add('SessionStart', sessionStartMatcher, 'recap', 15, 'memory recap');
|
|
715
|
+
add('SessionStart', sessionStartMatcher, 'session-id', 5, 'session-id');
|
|
716
|
+
add('SessionStart', sessionStartMatcher, 'drain', 8, 'inbox drain');
|
|
707
717
|
add('UserPromptSubmit', '', 'notify', 8, 'prompt cache');
|
|
708
718
|
add('PreToolUse', 'Bash|run_terminal_command', 'crush-wrap', 5, 'crush-wrap');
|
|
709
719
|
add('PreToolUse', '*', 'guard', 5, 'guard');
|
|
@@ -797,7 +807,7 @@ async function runGrokInit(opts) {
|
|
|
797
807
|
console.log(' Reload hooks in Grok (/hooks then r) or start a fresh session.');
|
|
798
808
|
console.log(' Recap lands in ~/.greprag/grok-context/<16-hex>.md — Grok ignores SessionStart stdout.');
|
|
799
809
|
console.log(' Idle inbox: Grok monitor + `greprag inbox watch --session $GROK_SESSION_ID --json --quiet`.');
|
|
800
|
-
console.log(' Second session: spawn_subagent; child arms its own watch; parent keeps one.\n');
|
|
810
|
+
console.log(' Second session: `greprag grok spawn` (TUI) or spawn_subagent; child arms its own watch; parent keeps one.\n');
|
|
801
811
|
}
|
|
802
812
|
/** greprag init --global
|
|
803
813
|
* Creates ~/.greprag/project.json with a stable UUID.
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
* load-primer` / `chip-spawn-pointer`). Announce-only: detect → silent,
|
|
20
20
|
* reminder → null. adr: docs/load-system.md, docs/reminder-interrupt.md */
|
|
21
21
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
22
|
-
exports.codexChipSpawnPointerModule = exports.chipSpawnPointerModule = exports.loadPrimerModule = exports.CODEX_CHIP_SPAWN_POINTER = exports.OPENCODE_CHIP_SPAWN_POINTER = exports.CHIP_SPAWN_POINTER = exports.LOAD_PRIMER = void 0;
|
|
22
|
+
exports.codexChipSpawnPointerModule = exports.chipSpawnPointerModule = exports.loadPrimerModule = exports.CODEX_CHIP_SPAWN_POINTER = exports.GROK_CHIP_SPAWN_POINTER = exports.OPENCODE_CHIP_SPAWN_POINTER = exports.CHIP_SPAWN_POINTER = exports.LOAD_PRIMER = void 0;
|
|
23
23
|
/** PRIMER — teaches the loader itself. Resident (fires every SessionStart +
|
|
24
24
|
* re-teaches after compaction). Terse: it is in context every session. */
|
|
25
25
|
exports.LOAD_PRIMER = [
|
|
@@ -43,6 +43,11 @@ exports.OPENCODE_CHIP_SPAWN_POINTER = [
|
|
|
43
43
|
'[greprag chips — agent coordination is built in (messaging plumbing in the inbox primer).]',
|
|
44
44
|
'• Delegating a component of a plan to an isolated chip session, or `greprag fix spawn` just printed a FIX-chip mission? → 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). ≥2 chips at one objective → `greprag load chip-leader-opencode` BEFORE the first spawn.',
|
|
45
45
|
].join('\n');
|
|
46
|
+
/** POINTER — Grok Build TUI chip. Real window + inbox mesh, not spawn_subagent. */
|
|
47
|
+
exports.GROK_CHIP_SPAWN_POINTER = [
|
|
48
|
+
'[greprag chips — agent coordination is built in (messaging plumbing in the inbox primer).]',
|
|
49
|
+
'• About to spawn a chip? → 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. ≥2 chips at one objective → `greprag load chip-leader` BEFORE the first spawn.',
|
|
50
|
+
].join('\n');
|
|
46
51
|
/** POINTER — Codex Desktop's native chip path. This is intentionally distinct
|
|
47
52
|
* from Claude Code spawn_task: different lifecycle, runtime, and handoff. */
|
|
48
53
|
exports.CODEX_CHIP_SPAWN_POINTER = [
|
|
@@ -70,6 +75,8 @@ exports.chipSpawnPointerModule = {
|
|
|
70
75
|
return null;
|
|
71
76
|
if (env.platform === 'opencode')
|
|
72
77
|
return exports.OPENCODE_CHIP_SPAWN_POINTER;
|
|
78
|
+
if (env.platform === 'grok')
|
|
79
|
+
return exports.GROK_CHIP_SPAWN_POINTER;
|
|
73
80
|
return exports.CHIP_SPAWN_POINTER;
|
|
74
81
|
},
|
|
75
82
|
reminder: () => null,
|
package/dist/commands/load.js
CHANGED
|
@@ -73,6 +73,10 @@ const LIBRARY = {
|
|
|
73
73
|
files: ['skill/templates/codex-chip-spawn.md'],
|
|
74
74
|
purpose: 'Spawn an independent writable native Codex Desktop task with an isolated worktree and native completion/cleanup.',
|
|
75
75
|
},
|
|
76
|
+
'grok-chip-spawn': {
|
|
77
|
+
files: ['skill/templates/grok-chip-spawn.md'],
|
|
78
|
+
purpose: 'Spawn a Grok TUI chip (cmd /k bootloader + isolated worktree). Child arms its own inbox watch; parent talks via greprag send. Not spawn_subagent.',
|
|
79
|
+
},
|
|
76
80
|
'skill-change': {
|
|
77
81
|
files: ['skill/templates/skill-change.md'],
|
|
78
82
|
purpose: 'Internal bundled schema for safely updating a skill after a run: when to edit, Convention A/B shapes, and when to propose instead.',
|
|
@@ -20,7 +20,8 @@ function buildOsPrimer(env) {
|
|
|
20
20
|
// vehicle is the chip-bootloader entry: native `greprag opencode chip` spawn).
|
|
21
21
|
const spawnEntry = env?.platform === 'codex' ? 'codex-chip-spawn'
|
|
22
22
|
: env?.platform === 'opencode' ? 'chip-bootloader'
|
|
23
|
-
: 'chip-spawn'
|
|
23
|
+
: env?.platform === 'grok' ? 'grok-chip-spawn'
|
|
24
|
+
: 'chip-spawn';
|
|
24
25
|
return [
|
|
25
26
|
'[grepragOS — the operating laws. Full doctrine: `greprag load os`.]',
|
|
26
27
|
'• 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 — read these first" block naming exact paths.',
|
package/dist/commands/status.js
CHANGED
|
@@ -267,7 +267,7 @@ function buildStatus(cwd, platform = 'all') {
|
|
|
267
267
|
skill_path: grokSkillPath,
|
|
268
268
|
skill_installed: fs.existsSync(grokSkillPath),
|
|
269
269
|
hooks: grokHookStatus,
|
|
270
|
-
note: 'Grok recap is a sidecar + ~/.grok/rules (SessionStart stdout is ignored). Idle inbox: Grok monitor + quiet watch. Stop drain injects unread mail.',
|
|
270
|
+
note: 'Grok recap is a sidecar + ~/.grok/rules (SessionStart stdout is ignored). Idle inbox: Grok monitor + quiet watch. Stop drain injects unread mail only when unarmed.',
|
|
271
271
|
},
|
|
272
272
|
},
|
|
273
273
|
project: {
|
package/dist/hook.js
CHANGED
|
@@ -1334,7 +1334,7 @@ function grokSidecarHead(short, full) {
|
|
|
1334
1334
|
return (0, session_id_1.buildSessionIdContext)(short, (0, session_id_1.readIdentityAlias)())
|
|
1335
1335
|
+ `\n\nARM (Grok monitor persistent:true): \`${arm}\`\n`
|
|
1336
1336
|
+ `Send with --from-session ${full || short} (full UUID or 16-hex, never 8-hex).\n`
|
|
1337
|
-
+ '
|
|
1337
|
+
+ 'Chip: `greprag load grok-chip-spawn` then `greprag grok spawn`. Helper: spawn_subagent. Parent keeps ONE watch. Peers: `greprag inbox watchers` then `greprag send`.\n'
|
|
1338
1338
|
+ 'If a greprag inbox monitor is already listed, do not start another. Instant exit = already armed.\n\n';
|
|
1339
1339
|
}
|
|
1340
1340
|
function writeRecapOutput(text, mode, grokShort, grokFull) {
|
|
@@ -2023,7 +2023,9 @@ async function collisionCheck(input) {
|
|
|
2023
2023
|
* poll live-tails with no double-delivery. Closes the dead-window gap where a
|
|
2024
2024
|
* session-directed message that lands between watcher death and re-arm sits
|
|
2025
2025
|
* unread until a manual `greprag inbox`. Inbound-only — the front desk (cold
|
|
2026
|
-
* opens + email) stays with the human-scoped `mail` hook.
|
|
2026
|
+
* opens + email) stays with the human-scoped `mail` hook. Injects only when
|
|
2027
|
+
* unarmed (`isLocallyArmed`); a live watcher already delivered the body.
|
|
2028
|
+
* Grok also wires this on Stop as the unarmed floor. All real logic lives
|
|
2027
2029
|
* in ./commands/inbox-drain (pure + tested). Best-effort: any miss → silent,
|
|
2028
2030
|
* never blocks SessionStart. adr: adr/monitor-resilience.md */
|
|
2029
2031
|
async function drain(input) {
|
|
@@ -2034,7 +2036,10 @@ async function drain(input) {
|
|
|
2034
2036
|
const short = (0, session_id_1.truncateSessionId)(input.session_id);
|
|
2035
2037
|
if (!short)
|
|
2036
2038
|
return; // no session id → silent
|
|
2037
|
-
const result = await (0, inbox_drain_1.runInboxDrain)({
|
|
2039
|
+
const result = await (0, inbox_drain_1.runInboxDrain)({
|
|
2040
|
+
session: short, apiUrl: cfg.apiUrl, apiKey: cfg.apiKey,
|
|
2041
|
+
armed: (0, watcher_registry_1.isLocallyArmed)(short),
|
|
2042
|
+
});
|
|
2038
2043
|
if (result.context) {
|
|
2039
2044
|
(0, hook_runtime_1.writeAdditionalContext)(input.hook_event_name || 'SessionStart', result.context);
|
|
2040
2045
|
if ((0, harness_1.inferCurrentHarness)() === 'grok')
|
|
@@ -2417,10 +2422,11 @@ async function main() {
|
|
|
2417
2422
|
// best-effort state stash. docs/ingress-trigger-bridge.md
|
|
2418
2423
|
await store(input, harness === 'grok' ? 'grok' : 'claude-code');
|
|
2419
2424
|
if (harness === 'grok') {
|
|
2420
|
-
// Floor: inject unread mail. Do NOT nag UNARMED here —
|
|
2421
|
-
// fires every turn, and a pidfile-alias miss made that a
|
|
2422
|
-
// (watch already live → singleton-guard exits instantly →
|
|
2423
|
-
// again). Arm teaching is sidecar + rules.
|
|
2425
|
+
// Floor: inject unread mail only when unarmed. Do NOT nag UNARMED here —
|
|
2426
|
+
// Grok Stop fires every turn, and a pidfile-alias miss made that a
|
|
2427
|
+
// re-arm loop (watch already live → singleton-guard exits instantly →
|
|
2428
|
+
// model arms again). Arm teaching is sidecar + rules.
|
|
2429
|
+
// adr: adr/grok-platform.md
|
|
2424
2430
|
await drain(input);
|
|
2425
2431
|
}
|
|
2426
2432
|
stateUpdate(input);
|
package/dist/index.js
CHANGED
|
@@ -43,6 +43,7 @@ var __importStar = (this && this.__importStar) || (function () {
|
|
|
43
43
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
44
44
|
const path = __importStar(require("path"));
|
|
45
45
|
const fs = __importStar(require("fs"));
|
|
46
|
+
const os = __importStar(require("os"));
|
|
46
47
|
const init_1 = require("./commands/init");
|
|
47
48
|
const status_1 = require("./commands/status");
|
|
48
49
|
const doctor_1 = require("./commands/doctor");
|
|
@@ -77,6 +78,7 @@ const crush_1 = require("./commands/crush");
|
|
|
77
78
|
const crush_stats_1 = require("./commands/crush-stats");
|
|
78
79
|
const archive_1 = require("./commands/archive");
|
|
79
80
|
const inbox_watch_1 = require("./commands/inbox-watch");
|
|
81
|
+
const grok_spawn_1 = require("./commands/grok-spawn");
|
|
80
82
|
const inbox_attachments_1 = require("./inbox-attachments");
|
|
81
83
|
const discord_1 = require("./commands/discord");
|
|
82
84
|
const project_anchor_1 = require("./project-anchor");
|
|
@@ -272,6 +274,23 @@ undici error, OS kill); and it SELF-TERMINATES when its Monitor consumer's pipe
|
|
|
272
274
|
breaks (session reload/end) so it never orphans. Within-session robustness only;
|
|
273
275
|
a watcher cannot survive session reload / --resume / /compact.
|
|
274
276
|
adr: adr/monitor-resilience.md`;
|
|
277
|
+
function readProjectRegistry() {
|
|
278
|
+
try {
|
|
279
|
+
const file = path.join(os.homedir(), '.greprag', 'projects.json');
|
|
280
|
+
const parsed = JSON.parse(fs.readFileSync(file, 'utf-8'));
|
|
281
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
282
|
+
return {};
|
|
283
|
+
const out = {};
|
|
284
|
+
for (const [k, v] of Object.entries(parsed)) {
|
|
285
|
+
if (typeof v === 'string' && v)
|
|
286
|
+
out[k] = v;
|
|
287
|
+
}
|
|
288
|
+
return out;
|
|
289
|
+
}
|
|
290
|
+
catch {
|
|
291
|
+
return {};
|
|
292
|
+
}
|
|
293
|
+
}
|
|
275
294
|
/** greprag inbox [--all] [--session <id>] | inbox keep <id> | inbox delete <id> | inbox watch */
|
|
276
295
|
/** greprag desk — the machine's desk-line (reverse-RPC relay to the cloud).
|
|
277
296
|
* desk run hold the line open and answer cloud questions (long-running)
|
|
@@ -405,8 +424,13 @@ async function inbox(args) {
|
|
|
405
424
|
}
|
|
406
425
|
const res = await apiGet(`${cfg.apiUrl}/v1/inbox/watchers`, cfg.apiKey);
|
|
407
426
|
const watchers = (res.watchers || []);
|
|
427
|
+
const repos = readProjectRegistry();
|
|
428
|
+
const decorated = watchers.map(w => ({
|
|
429
|
+
...w,
|
|
430
|
+
repo: w.repo || (w.project_name && repos[w.project_name]) || null,
|
|
431
|
+
}));
|
|
408
432
|
if (json) {
|
|
409
|
-
console.log(JSON.stringify({ watchers }, null, 2));
|
|
433
|
+
console.log(JSON.stringify({ watchers: decorated }, null, 2));
|
|
410
434
|
return;
|
|
411
435
|
}
|
|
412
436
|
if (watchers.length === 0) {
|
|
@@ -415,13 +439,17 @@ async function inbox(args) {
|
|
|
415
439
|
}
|
|
416
440
|
console.log(`${watchers.length} live watcher(s):\n`);
|
|
417
441
|
// Label = project · title — both resolved from the session's memory (the
|
|
418
|
-
// auto-armed watcher carries neither on its WS tag). The
|
|
442
|
+
// auto-armed watcher carries neither on its WS tag). The session short
|
|
419
443
|
// rides in parens; orchestrator mode reads it back to address sends.
|
|
420
|
-
|
|
444
|
+
// Platform + local repo path (from ~/.greprag/projects.json) say which
|
|
445
|
+
// harness armed it and which checkout it's in.
|
|
446
|
+
for (const w of decorated) {
|
|
421
447
|
const proj = w.project_name ? w.project_name : (w.wide ? '(tenant-wide)' : '(no project)');
|
|
422
448
|
const label = w.title ? `${proj} · ${w.title}` : proj;
|
|
423
449
|
const sess = w.session_id ? ` (${w.session_id})` : '';
|
|
424
|
-
|
|
450
|
+
const plat = w.platform ? ` [${w.platform}]` : '';
|
|
451
|
+
const repo = w.repo ? ` ${w.repo}` : '';
|
|
452
|
+
console.log(` ${label}${sess}${plat}${repo}`);
|
|
425
453
|
}
|
|
426
454
|
return;
|
|
427
455
|
}
|
|
@@ -666,7 +694,7 @@ function parseFileFlag(raw) {
|
|
|
666
694
|
* is a session id; anything else is a project name. Project names that
|
|
667
695
|
* look like UUIDs are rejected at registration time so this is unambiguous.
|
|
668
696
|
* adr: adr/session-id-awareness.md, adr/address-grammar.md */
|
|
669
|
-
const SESSION_ID_PATTERN = /^([0-9a-f]{8}|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i;
|
|
697
|
+
const SESSION_ID_PATTERN = /^([0-9a-f]{8}|[0-9a-f]{16}|[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})$/i;
|
|
670
698
|
/** Parse a send address under the v0.12 grammar:
|
|
671
699
|
* <handle>@greprag.com[/<target>]
|
|
672
700
|
* where <target> is exactly one segment — a session UUID (or 8-hex short form)
|
|
@@ -719,7 +747,7 @@ function parseSendAddress(addr) {
|
|
|
719
747
|
if (!target) {
|
|
720
748
|
return { ok: false, error: `address "${addr}" has an empty target segment.` };
|
|
721
749
|
}
|
|
722
|
-
const kind = SESSION_ID_PATTERN.test(target) ? 'session' : 'project';
|
|
750
|
+
const kind = ((0, session_id_1.isSessionAddressTarget)(target) || SESSION_ID_PATTERN.test(target)) ? 'session' : 'project';
|
|
723
751
|
return { ok: true, targetKind: kind, target };
|
|
724
752
|
}
|
|
725
753
|
/** Internal (self-desk) message types — KEEP IN SYNC with
|
|
@@ -1267,6 +1295,10 @@ Commands:
|
|
|
1267
1295
|
Configure lifecycle hooks + anchor for Codex
|
|
1268
1296
|
init --grok [--api-key <key>] [--tenant-id <handle>]
|
|
1269
1297
|
Configure Grok Build hooks + skill + rules
|
|
1298
|
+
grok spawn [--cwd <path>] [--prompt "<text>"]
|
|
1299
|
+
Open a fresh Grok TUI in a new terminal
|
|
1300
|
+
(Windows schtasks bootloader — survives the
|
|
1301
|
+
tool Job Object; child arms its own watch)
|
|
1270
1302
|
init --all [--root <path>] Standard init for cwd, then bulk-register every
|
|
1271
1303
|
other git repo at depth 1 under <path> (default:
|
|
1272
1304
|
parent of repo root). Each becomes inbox-addressable.
|
|
@@ -1306,11 +1338,12 @@ Inbox (email-style messaging across tenants):
|
|
|
1306
1338
|
--session scopes to one specific session's view.
|
|
1307
1339
|
--project filters by project name.
|
|
1308
1340
|
--peek: NON-MUTATING — does not mark any message read.
|
|
1309
|
-
inbox watchers [--json] List currently-attached live
|
|
1310
|
-
|
|
1311
|
-
title (session_id)
|
|
1312
|
-
from session memory
|
|
1313
|
-
|
|
1341
|
+
inbox watchers [--json] List currently-attached live watchers
|
|
1342
|
+
(Claude / Codex / OpenCode / Grok). Each row:
|
|
1343
|
+
project · title (session_id) [platform] repo —
|
|
1344
|
+
project + nano title from session memory, repo
|
|
1345
|
+
from ~/.greprag/projects.json. Codex task
|
|
1346
|
+
discovery also uses codex_app.list_threads.
|
|
1314
1347
|
inbox watch Long-lived SSE stream — prints each message as it lands.
|
|
1315
1348
|
Self-supervising by default: a parent process
|
|
1316
1349
|
respawns the SSE loop (via CreateProcess) on any
|
|
@@ -1895,6 +1928,7 @@ async function main() {
|
|
|
1895
1928
|
case 'loadout': return (0, loadout_1.runLoadout)(subArgs); // Loadout: cross-tenant skill-bundle gifting (docs/loadout.md)
|
|
1896
1929
|
case 'assistant': return (0, assistant_1.runAssistant)(subArgs); // designate this project as the tenant's Assistant (role flag)
|
|
1897
1930
|
case 'opencode': return opencode(subArgs);
|
|
1931
|
+
case 'grok': return (0, grok_spawn_1.runGrok)(subArgs);
|
|
1898
1932
|
default:
|
|
1899
1933
|
console.error(`Unknown command: ${command}\n`);
|
|
1900
1934
|
console.log(HELP);
|
|
@@ -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
|
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`.
|