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
|
@@ -0,0 +1,191 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/** greprag persona — tenant-scoped speaking instructions. adr: adr/persona.md
|
|
3
|
+
*
|
|
4
|
+
* Thin HTTP wrapper over /v1/persona. The server owns built-in template content;
|
|
5
|
+
* custom text is sent verbatim and injected without rewriting.
|
|
6
|
+
*/
|
|
7
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
8
|
+
if (k2 === undefined) k2 = k;
|
|
9
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
10
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
11
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
12
|
+
}
|
|
13
|
+
Object.defineProperty(o, k2, desc);
|
|
14
|
+
}) : (function(o, m, k, k2) {
|
|
15
|
+
if (k2 === undefined) k2 = k;
|
|
16
|
+
o[k2] = m[k];
|
|
17
|
+
}));
|
|
18
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
19
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
20
|
+
}) : function(o, v) {
|
|
21
|
+
o["default"] = v;
|
|
22
|
+
});
|
|
23
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
24
|
+
var ownKeys = function(o) {
|
|
25
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
26
|
+
var ar = [];
|
|
27
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
28
|
+
return ar;
|
|
29
|
+
};
|
|
30
|
+
return ownKeys(o);
|
|
31
|
+
};
|
|
32
|
+
return function (mod) {
|
|
33
|
+
if (mod && mod.__esModule) return mod;
|
|
34
|
+
var result = {};
|
|
35
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
36
|
+
__setModuleDefault(result, mod);
|
|
37
|
+
return result;
|
|
38
|
+
};
|
|
39
|
+
})();
|
|
40
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
41
|
+
exports.runPersona = runPersona;
|
|
42
|
+
const fs = __importStar(require("fs"));
|
|
43
|
+
function getConfig() {
|
|
44
|
+
return {
|
|
45
|
+
apiUrl: process.env.GREPRAG_API_URL || 'https://api.greprag.com',
|
|
46
|
+
apiKey: process.env.GREPRAG_API_KEY || '',
|
|
47
|
+
};
|
|
48
|
+
}
|
|
49
|
+
function getFlag(args, flag) {
|
|
50
|
+
const idx = args.indexOf(flag);
|
|
51
|
+
return idx === -1 || idx + 1 >= args.length ? undefined : args[idx + 1];
|
|
52
|
+
}
|
|
53
|
+
const HELP = `greprag persona — tenant-scoped instructions for how agents speak to you
|
|
54
|
+
|
|
55
|
+
USAGE
|
|
56
|
+
greprag persona list
|
|
57
|
+
List built-in Persona templates.
|
|
58
|
+
|
|
59
|
+
greprag persona use <template>
|
|
60
|
+
Activate a built-in template, e.g. vibe-shaper.
|
|
61
|
+
|
|
62
|
+
greprag persona set <text>
|
|
63
|
+
greprag persona set --file <path>
|
|
64
|
+
greprag persona set --stdin
|
|
65
|
+
Save and activate a custom Persona verbatim.
|
|
66
|
+
|
|
67
|
+
greprag persona show
|
|
68
|
+
Show the active Persona.
|
|
69
|
+
|
|
70
|
+
greprag persona clear
|
|
71
|
+
Remove the active Persona.`;
|
|
72
|
+
async function api(method, pathname, body) {
|
|
73
|
+
const cfg = getConfig();
|
|
74
|
+
if (!cfg.apiKey) {
|
|
75
|
+
console.error('GREPRAG_API_KEY not set — run `greprag init` first.');
|
|
76
|
+
process.exit(1);
|
|
77
|
+
}
|
|
78
|
+
const res = await fetch(`${cfg.apiUrl}${pathname}`, {
|
|
79
|
+
method,
|
|
80
|
+
headers: {
|
|
81
|
+
'Authorization': `Bearer ${cfg.apiKey}`,
|
|
82
|
+
'Content-Type': 'application/json',
|
|
83
|
+
},
|
|
84
|
+
body: body === undefined ? undefined : JSON.stringify(body),
|
|
85
|
+
});
|
|
86
|
+
let data = {};
|
|
87
|
+
try {
|
|
88
|
+
data = await res.json();
|
|
89
|
+
}
|
|
90
|
+
catch { /* non-JSON */ }
|
|
91
|
+
return { ok: res.ok, status: res.status, data };
|
|
92
|
+
}
|
|
93
|
+
function fail(data, status) {
|
|
94
|
+
console.error(`Error${status ? ` (${status})` : ''}: ${data.error || 'request failed'}`);
|
|
95
|
+
process.exit(1);
|
|
96
|
+
}
|
|
97
|
+
function readPersonaInput(args) {
|
|
98
|
+
const file = getFlag(args, '--file');
|
|
99
|
+
if (file) {
|
|
100
|
+
try {
|
|
101
|
+
return file === '-' ? fs.readFileSync(0, 'utf-8') : fs.readFileSync(file, 'utf-8');
|
|
102
|
+
}
|
|
103
|
+
catch (err) {
|
|
104
|
+
console.error(`persona set: could not read --file ${file}: ${err.message}`);
|
|
105
|
+
process.exit(1);
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
if (args.includes('--stdin')) {
|
|
109
|
+
try {
|
|
110
|
+
return fs.readFileSync(0, 'utf-8');
|
|
111
|
+
}
|
|
112
|
+
catch (err) {
|
|
113
|
+
console.error(`persona set: could not read stdin: ${err.message}`);
|
|
114
|
+
process.exit(1);
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
return args.filter((a) => a !== '--stdin').join(' ');
|
|
118
|
+
}
|
|
119
|
+
async function list() {
|
|
120
|
+
const { ok, status, data } = await api('GET', '/v1/persona/templates');
|
|
121
|
+
if (!ok)
|
|
122
|
+
fail(data, status);
|
|
123
|
+
const templates = data.templates || [];
|
|
124
|
+
if (!templates.length) {
|
|
125
|
+
console.log('No built-in Persona templates.');
|
|
126
|
+
return;
|
|
127
|
+
}
|
|
128
|
+
console.log('Built-in Persona templates:\n');
|
|
129
|
+
for (const t of templates) {
|
|
130
|
+
console.log(` ${t.id} — ${t.name}`);
|
|
131
|
+
console.log(` ${t.description}`);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
async function useTemplate(id) {
|
|
135
|
+
if (!id) {
|
|
136
|
+
console.error('Usage: greprag persona use <template>');
|
|
137
|
+
process.exit(1);
|
|
138
|
+
}
|
|
139
|
+
const { ok, status, data } = await api('PUT', '/v1/persona/template', { templateId: id });
|
|
140
|
+
if (!ok)
|
|
141
|
+
fail(data, status);
|
|
142
|
+
const p = data.persona;
|
|
143
|
+
console.log(`Persona active: ${p.templateId} (built-in template)`);
|
|
144
|
+
}
|
|
145
|
+
async function set(args) {
|
|
146
|
+
const content = readPersonaInput(args);
|
|
147
|
+
const { ok, status, data } = await api('PUT', '/v1/persona/custom', { content });
|
|
148
|
+
if (!ok)
|
|
149
|
+
fail(data, status);
|
|
150
|
+
const p = data.persona;
|
|
151
|
+
console.log(`Persona active: custom (${p.content?.length ?? content.length} chars)`);
|
|
152
|
+
}
|
|
153
|
+
async function show() {
|
|
154
|
+
const { ok, status, data } = await api('GET', '/v1/persona');
|
|
155
|
+
if (!ok)
|
|
156
|
+
fail(data, status);
|
|
157
|
+
const p = data.persona;
|
|
158
|
+
if (!p) {
|
|
159
|
+
console.log('No Persona set.');
|
|
160
|
+
return;
|
|
161
|
+
}
|
|
162
|
+
const source = p.mode === 'template' ? `template ${p.templateId}` : 'custom';
|
|
163
|
+
console.log(`Persona (${source})`);
|
|
164
|
+
console.log('');
|
|
165
|
+
console.log(p.content || '');
|
|
166
|
+
}
|
|
167
|
+
async function clear() {
|
|
168
|
+
const { ok, status, data } = await api('DELETE', '/v1/persona');
|
|
169
|
+
if (!ok)
|
|
170
|
+
fail(data, status);
|
|
171
|
+
console.log(data.removed ? 'Persona cleared.' : 'No Persona was set.');
|
|
172
|
+
}
|
|
173
|
+
async function runPersona(args) {
|
|
174
|
+
const sub = args[0];
|
|
175
|
+
const rest = args.slice(1);
|
|
176
|
+
if (!sub || sub === '--help' || sub === '-h' || sub === 'help') {
|
|
177
|
+
console.log(HELP);
|
|
178
|
+
return;
|
|
179
|
+
}
|
|
180
|
+
switch (sub) {
|
|
181
|
+
case 'list': return list();
|
|
182
|
+
case 'use': return useTemplate(rest[0]);
|
|
183
|
+
case 'set': return set(rest);
|
|
184
|
+
case 'show': return show();
|
|
185
|
+
case 'clear': return clear();
|
|
186
|
+
default:
|
|
187
|
+
console.error(`Unknown persona command: ${sub}\n`);
|
|
188
|
+
console.log(HELP);
|
|
189
|
+
process.exit(1);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
@@ -28,6 +28,7 @@ const skill_mirror_reminder_1 = require("./skill-mirror-reminder");
|
|
|
28
28
|
const procedure_reminder_1 = require("./procedure-reminder");
|
|
29
29
|
const loadout_reminder_1 = require("./loadout-reminder");
|
|
30
30
|
const delivery_reminder_1 = require("./delivery-reminder");
|
|
31
|
+
const persona_reminder_1 = require("./persona-reminder");
|
|
31
32
|
/** Registry order = display order. THE single agent-facing announce/reminder assembly:
|
|
32
33
|
* the hook does I/O → fills ReminderEnv → collectAnnounces (SessionStart) / collectReminders
|
|
33
34
|
* (per turn) render every module here in this order. A module may own either surface or both;
|
|
@@ -46,6 +47,7 @@ exports.REGISTRY = [
|
|
|
46
47
|
skill_mirror_reminder_1.skillMirrorAnnounceModule, // native-equivalent activation index for load-only mirrored skills
|
|
47
48
|
procedure_reminder_1.procedureAnnounceModule, // active project procedures, rendered generically from operator-owned installs
|
|
48
49
|
delivery_reminder_1.deliveryControlModule, // repo-scoped announce-first Delivery System pilot (all harnesses)
|
|
50
|
+
persona_reminder_1.personaAnnounceModule, // tenant-scoped Persona, shared across projects/harnesses
|
|
49
51
|
loadout_reminder_1.loadoutRegistrarModule, // equipped loadouts: one advisor announce each + per-turn keyword Match (docs/loadout.md)
|
|
50
52
|
setup_reminder_1.setupWarningModule,
|
|
51
53
|
version_reminder_1.versionUpgradeModule, // Deficiency-gated announce — silent unless a newer release exists
|
package/dist/commands/status.js
CHANGED
|
@@ -191,6 +191,7 @@ function buildStatus(cwd, platform = 'all') {
|
|
|
191
191
|
pre_tool_use_chip_guard: hasCodexHook(codexHooks.hooks?.PreToolUse, 'codex-pretooluse')
|
|
192
192
|
|| hasCodexHook(codexHooks.hooks?.PreToolUse, 'codex-chip-hook'),
|
|
193
193
|
stop_store: hasCodexHook(codexHooks.hooks?.Stop, 'codex-store'),
|
|
194
|
+
post_compact_reannounce: hasCodexHook(codexHooks.hooks?.PostCompact, 'recompact'),
|
|
194
195
|
post_compact_session_id: hasCodexHook(codexHooks.hooks?.PostCompact, 'session-id'),
|
|
195
196
|
};
|
|
196
197
|
const opencodePluginInstalled = fs.existsSync(opencodePluginPath);
|
|
@@ -267,7 +268,7 @@ function buildStatus(cwd, platform = 'all') {
|
|
|
267
268
|
skill_path: grokSkillPath,
|
|
268
269
|
skill_installed: fs.existsSync(grokSkillPath),
|
|
269
270
|
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.',
|
|
271
|
+
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
272
|
},
|
|
272
273
|
},
|
|
273
274
|
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) {
|
|
@@ -1613,11 +1613,29 @@ async function recap(input, mode = 'plain', opts = {}) {
|
|
|
1613
1613
|
return empty;
|
|
1614
1614
|
}
|
|
1615
1615
|
})();
|
|
1616
|
+
// Tenant Persona primer. The server owns both template resolution and
|
|
1617
|
+
// the provenance envelope; harnesses transport it unchanged. adr: adr/persona.md
|
|
1618
|
+
const personaPromise = (async () => {
|
|
1619
|
+
try {
|
|
1620
|
+
const res = await fetch(`${cfg.apiUrl}/v1/persona`, {
|
|
1621
|
+
headers: { 'Authorization': `Bearer ${cfg.apiKey}` },
|
|
1622
|
+
});
|
|
1623
|
+
if (!res.ok)
|
|
1624
|
+
return null;
|
|
1625
|
+
const data = await res.json();
|
|
1626
|
+
return typeof data.announce === 'string' && data.announce.length > 0
|
|
1627
|
+
? data.announce
|
|
1628
|
+
: null;
|
|
1629
|
+
}
|
|
1630
|
+
catch {
|
|
1631
|
+
return null;
|
|
1632
|
+
}
|
|
1633
|
+
})();
|
|
1616
1634
|
const updateAvailablePromise = checkForUpdate();
|
|
1617
1635
|
const bodyPromise = (!opts.compact && anchor.sessionStartRecap)
|
|
1618
1636
|
? (0, opencode_plugin_helpers_1.buildRecapBody)(cfg.apiUrl, cfg.apiKey, anchor)
|
|
1619
1637
|
: Promise.resolve('');
|
|
1620
|
-
const [, corpusApiDocs, docPointers, enrichmentDown, skillGains, mirroredSkills, loadoutState, updateAvailable, body,] = await Promise.all([
|
|
1638
|
+
const [, corpusApiDocs, docPointers, enrichmentDown, skillGains, mirroredSkills, loadoutState, personaAnnounce, updateAvailable, body,] = await Promise.all([
|
|
1621
1639
|
matchsetPromise,
|
|
1622
1640
|
corpusApiDocsPromise,
|
|
1623
1641
|
docPointersPromise,
|
|
@@ -1625,6 +1643,7 @@ async function recap(input, mode = 'plain', opts = {}) {
|
|
|
1625
1643
|
skillGainsPromise,
|
|
1626
1644
|
mirroredSkillsPromise,
|
|
1627
1645
|
loadoutStatePromise,
|
|
1646
|
+
personaPromise,
|
|
1628
1647
|
updateAvailablePromise,
|
|
1629
1648
|
bodyPromise,
|
|
1630
1649
|
]);
|
|
@@ -1704,6 +1723,7 @@ async function recap(input, mode = 'plain', opts = {}) {
|
|
|
1704
1723
|
mirroredSkills,
|
|
1705
1724
|
procedureAnnounces,
|
|
1706
1725
|
equippedLoadouts,
|
|
1726
|
+
personaAnnounce,
|
|
1707
1727
|
updateAvailable,
|
|
1708
1728
|
activeCommandDrift: activeCommandDrift(),
|
|
1709
1729
|
loadoutUpdateRequired,
|
|
@@ -2023,7 +2043,9 @@ async function collisionCheck(input) {
|
|
|
2023
2043
|
* poll live-tails with no double-delivery. Closes the dead-window gap where a
|
|
2024
2044
|
* session-directed message that lands between watcher death and re-arm sits
|
|
2025
2045
|
* unread until a manual `greprag inbox`. Inbound-only — the front desk (cold
|
|
2026
|
-
* opens + email) stays with the human-scoped `mail` hook.
|
|
2046
|
+
* opens + email) stays with the human-scoped `mail` hook. Injects only when
|
|
2047
|
+
* unarmed (`isLocallyArmed`); a live watcher already delivered the body.
|
|
2048
|
+
* Grok also wires this on Stop as the unarmed floor. All real logic lives
|
|
2027
2049
|
* in ./commands/inbox-drain (pure + tested). Best-effort: any miss → silent,
|
|
2028
2050
|
* never blocks SessionStart. adr: adr/monitor-resilience.md */
|
|
2029
2051
|
async function drain(input) {
|
|
@@ -2034,7 +2056,10 @@ async function drain(input) {
|
|
|
2034
2056
|
const short = (0, session_id_1.truncateSessionId)(input.session_id);
|
|
2035
2057
|
if (!short)
|
|
2036
2058
|
return; // no session id → silent
|
|
2037
|
-
const result = await (0, inbox_drain_1.runInboxDrain)({
|
|
2059
|
+
const result = await (0, inbox_drain_1.runInboxDrain)({
|
|
2060
|
+
session: short, apiUrl: cfg.apiUrl, apiKey: cfg.apiKey,
|
|
2061
|
+
armed: (0, watcher_registry_1.isLocallyArmed)(short),
|
|
2062
|
+
});
|
|
2038
2063
|
if (result.context) {
|
|
2039
2064
|
(0, hook_runtime_1.writeAdditionalContext)(input.hook_event_name || 'SessionStart', result.context);
|
|
2040
2065
|
if ((0, harness_1.inferCurrentHarness)() === 'grok')
|
|
@@ -2259,7 +2284,10 @@ async function main() {
|
|
|
2259
2284
|
// opt out per-module with reannounceOnCompact:false) so the doctrine the compaction
|
|
2260
2285
|
// wiped (incl. the watcher-arm announce) returns. additionalContext = the JSON form
|
|
2261
2286
|
// PostCompact injects. adr: adr/monitor-resilience.md, docs/reminder-interrupt.md
|
|
2262
|
-
await recap(input, 'additionalContext', {
|
|
2287
|
+
await recap(input, 'additionalContext', {
|
|
2288
|
+
compact: true,
|
|
2289
|
+
platform: harness === 'grok' ? 'grok' : undefined,
|
|
2290
|
+
});
|
|
2263
2291
|
}
|
|
2264
2292
|
else if (subcommand === 'codex-recap') {
|
|
2265
2293
|
await recap(input, 'additionalContext', { platform: 'codex' });
|
|
@@ -2417,10 +2445,11 @@ async function main() {
|
|
|
2417
2445
|
// best-effort state stash. docs/ingress-trigger-bridge.md
|
|
2418
2446
|
await store(input, harness === 'grok' ? 'grok' : 'claude-code');
|
|
2419
2447
|
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.
|
|
2448
|
+
// Floor: inject unread mail only when unarmed. Do NOT nag UNARMED here —
|
|
2449
|
+
// Grok Stop fires every turn, and a pidfile-alias miss made that a
|
|
2450
|
+
// re-arm loop (watch already live → singleton-guard exits instantly →
|
|
2451
|
+
// model arms again). Arm teaching is sidecar + rules.
|
|
2452
|
+
// adr: adr/grok-platform.md
|
|
2424
2453
|
await drain(input);
|
|
2425
2454
|
}
|
|
2426
2455
|
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");
|
|
@@ -65,6 +66,7 @@ const doc_1 = require("./commands/doc");
|
|
|
65
66
|
const parity_1 = require("./commands/parity");
|
|
66
67
|
const agents_1 = require("./commands/agents");
|
|
67
68
|
const loadout_1 = require("./commands/loadout");
|
|
69
|
+
const persona_1 = require("./commands/persona");
|
|
68
70
|
const assistant_1 = require("./commands/assistant");
|
|
69
71
|
const email_1 = require("./commands/email");
|
|
70
72
|
const sms_1 = require("./commands/sms");
|
|
@@ -77,6 +79,7 @@ const crush_1 = require("./commands/crush");
|
|
|
77
79
|
const crush_stats_1 = require("./commands/crush-stats");
|
|
78
80
|
const archive_1 = require("./commands/archive");
|
|
79
81
|
const inbox_watch_1 = require("./commands/inbox-watch");
|
|
82
|
+
const grok_spawn_1 = require("./commands/grok-spawn");
|
|
80
83
|
const inbox_attachments_1 = require("./inbox-attachments");
|
|
81
84
|
const discord_1 = require("./commands/discord");
|
|
82
85
|
const project_anchor_1 = require("./project-anchor");
|
|
@@ -272,6 +275,23 @@ undici error, OS kill); and it SELF-TERMINATES when its Monitor consumer's pipe
|
|
|
272
275
|
breaks (session reload/end) so it never orphans. Within-session robustness only;
|
|
273
276
|
a watcher cannot survive session reload / --resume / /compact.
|
|
274
277
|
adr: adr/monitor-resilience.md`;
|
|
278
|
+
function readProjectRegistry() {
|
|
279
|
+
try {
|
|
280
|
+
const file = path.join(os.homedir(), '.greprag', 'projects.json');
|
|
281
|
+
const parsed = JSON.parse(fs.readFileSync(file, 'utf-8'));
|
|
282
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed))
|
|
283
|
+
return {};
|
|
284
|
+
const out = {};
|
|
285
|
+
for (const [k, v] of Object.entries(parsed)) {
|
|
286
|
+
if (typeof v === 'string' && v)
|
|
287
|
+
out[k] = v;
|
|
288
|
+
}
|
|
289
|
+
return out;
|
|
290
|
+
}
|
|
291
|
+
catch {
|
|
292
|
+
return {};
|
|
293
|
+
}
|
|
294
|
+
}
|
|
275
295
|
/** greprag inbox [--all] [--session <id>] | inbox keep <id> | inbox delete <id> | inbox watch */
|
|
276
296
|
/** greprag desk — the machine's desk-line (reverse-RPC relay to the cloud).
|
|
277
297
|
* desk run hold the line open and answer cloud questions (long-running)
|
|
@@ -405,8 +425,13 @@ async function inbox(args) {
|
|
|
405
425
|
}
|
|
406
426
|
const res = await apiGet(`${cfg.apiUrl}/v1/inbox/watchers`, cfg.apiKey);
|
|
407
427
|
const watchers = (res.watchers || []);
|
|
428
|
+
const repos = readProjectRegistry();
|
|
429
|
+
const decorated = watchers.map(w => ({
|
|
430
|
+
...w,
|
|
431
|
+
repo: w.repo || (w.project_name && repos[w.project_name]) || null,
|
|
432
|
+
}));
|
|
408
433
|
if (json) {
|
|
409
|
-
console.log(JSON.stringify({ watchers }, null, 2));
|
|
434
|
+
console.log(JSON.stringify({ watchers: decorated }, null, 2));
|
|
410
435
|
return;
|
|
411
436
|
}
|
|
412
437
|
if (watchers.length === 0) {
|
|
@@ -415,13 +440,17 @@ async function inbox(args) {
|
|
|
415
440
|
}
|
|
416
441
|
console.log(`${watchers.length} live watcher(s):\n`);
|
|
417
442
|
// Label = project · title — both resolved from the session's memory (the
|
|
418
|
-
// auto-armed watcher carries neither on its WS tag). The
|
|
443
|
+
// auto-armed watcher carries neither on its WS tag). The session short
|
|
419
444
|
// rides in parens; orchestrator mode reads it back to address sends.
|
|
420
|
-
|
|
445
|
+
// Platform + local repo path (from ~/.greprag/projects.json) say which
|
|
446
|
+
// harness armed it and which checkout it's in.
|
|
447
|
+
for (const w of decorated) {
|
|
421
448
|
const proj = w.project_name ? w.project_name : (w.wide ? '(tenant-wide)' : '(no project)');
|
|
422
449
|
const label = w.title ? `${proj} · ${w.title}` : proj;
|
|
423
450
|
const sess = w.session_id ? ` (${w.session_id})` : '';
|
|
424
|
-
|
|
451
|
+
const plat = w.platform ? ` [${w.platform}]` : '';
|
|
452
|
+
const repo = w.repo ? ` ${w.repo}` : '';
|
|
453
|
+
console.log(` ${label}${sess}${plat}${repo}`);
|
|
425
454
|
}
|
|
426
455
|
return;
|
|
427
456
|
}
|
|
@@ -666,7 +695,7 @@ function parseFileFlag(raw) {
|
|
|
666
695
|
* is a session id; anything else is a project name. Project names that
|
|
667
696
|
* look like UUIDs are rejected at registration time so this is unambiguous.
|
|
668
697
|
* 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;
|
|
698
|
+
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
699
|
/** Parse a send address under the v0.12 grammar:
|
|
671
700
|
* <handle>@greprag.com[/<target>]
|
|
672
701
|
* where <target> is exactly one segment — a session UUID (or 8-hex short form)
|
|
@@ -719,7 +748,7 @@ function parseSendAddress(addr) {
|
|
|
719
748
|
if (!target) {
|
|
720
749
|
return { ok: false, error: `address "${addr}" has an empty target segment.` };
|
|
721
750
|
}
|
|
722
|
-
const kind = SESSION_ID_PATTERN.test(target) ? 'session' : 'project';
|
|
751
|
+
const kind = ((0, session_id_1.isSessionAddressTarget)(target) || SESSION_ID_PATTERN.test(target)) ? 'session' : 'project';
|
|
723
752
|
return { ok: true, targetKind: kind, target };
|
|
724
753
|
}
|
|
725
754
|
/** Internal (self-desk) message types — KEEP IN SYNC with
|
|
@@ -1267,6 +1296,10 @@ Commands:
|
|
|
1267
1296
|
Configure lifecycle hooks + anchor for Codex
|
|
1268
1297
|
init --grok [--api-key <key>] [--tenant-id <handle>]
|
|
1269
1298
|
Configure Grok Build hooks + skill + rules
|
|
1299
|
+
grok spawn [--cwd <path>] [--prompt "<text>"]
|
|
1300
|
+
Open a fresh Grok TUI in a new terminal
|
|
1301
|
+
(Windows schtasks bootloader — survives the
|
|
1302
|
+
tool Job Object; child arms its own watch)
|
|
1270
1303
|
init --all [--root <path>] Standard init for cwd, then bulk-register every
|
|
1271
1304
|
other git repo at depth 1 under <path> (default:
|
|
1272
1305
|
parent of repo root). Each becomes inbox-addressable.
|
|
@@ -1306,11 +1339,12 @@ Inbox (email-style messaging across tenants):
|
|
|
1306
1339
|
--session scopes to one specific session's view.
|
|
1307
1340
|
--project filters by project name.
|
|
1308
1341
|
--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
|
-
|
|
1342
|
+
inbox watchers [--json] List currently-attached live watchers
|
|
1343
|
+
(Claude / Codex / OpenCode / Grok). Each row:
|
|
1344
|
+
project · title (session_id) [platform] repo —
|
|
1345
|
+
project + nano title from session memory, repo
|
|
1346
|
+
from ~/.greprag/projects.json. Codex task
|
|
1347
|
+
discovery also uses codex_app.list_threads.
|
|
1314
1348
|
inbox watch Long-lived SSE stream — prints each message as it lands.
|
|
1315
1349
|
Self-supervising by default: a parent process
|
|
1316
1350
|
respawns the SSE loop (via CreateProcess) on any
|
|
@@ -1535,6 +1569,14 @@ Loadout (gift a skill bundle to another tenant — offer, never auto-install):
|
|
|
1535
1569
|
loadout unequip <name> [--delete-skills]
|
|
1536
1570
|
Stop a loadout's auto-inflate.
|
|
1537
1571
|
|
|
1572
|
+
Persona (tenant-scoped speaking instructions):
|
|
1573
|
+
persona list List built-in Persona templates.
|
|
1574
|
+
persona use <template> Activate a built-in Persona template.
|
|
1575
|
+
persona set <text>|--file <f>|--stdin
|
|
1576
|
+
Save and activate a custom Persona verbatim.
|
|
1577
|
+
persona show Show the active Persona.
|
|
1578
|
+
persona clear Remove the active Persona.
|
|
1579
|
+
|
|
1538
1580
|
GrepRAG Instructions (canonical global/repo CLAUDE.md + AGENTS.md):
|
|
1539
1581
|
instructions pull <file> [--scope global|repo] [--push]
|
|
1540
1582
|
Pull disk instructions into canon.
|
|
@@ -1606,6 +1648,7 @@ const HELP_ALL_GROUPS = [
|
|
|
1606
1648
|
['delivery', delivery_1.runDelivery],
|
|
1607
1649
|
['assistant', assistant_1.runAssistant],
|
|
1608
1650
|
['instructions', agents_1.runInstructions],
|
|
1651
|
+
['persona', persona_1.runPersona],
|
|
1609
1652
|
['doc', doc_1.runDoc],
|
|
1610
1653
|
['skill', skill_1.runSkill],
|
|
1611
1654
|
['opencode', opencode],
|
|
@@ -1893,8 +1936,10 @@ async function main() {
|
|
|
1893
1936
|
case 'instructions': return (0, agents_1.runInstructions)(subArgs); // Canonical GrepRAG Instructions + rendered followers
|
|
1894
1937
|
case 'agents': return (0, agents_1.runAgents)(subArgs); // Deprecated alias for GrepRAG Instructions
|
|
1895
1938
|
case 'loadout': return (0, loadout_1.runLoadout)(subArgs); // Loadout: cross-tenant skill-bundle gifting (docs/loadout.md)
|
|
1939
|
+
case 'persona': return (0, persona_1.runPersona)(subArgs); // tenant-scoped speaking instructions
|
|
1896
1940
|
case 'assistant': return (0, assistant_1.runAssistant)(subArgs); // designate this project as the tenant's Assistant (role flag)
|
|
1897
1941
|
case 'opencode': return opencode(subArgs);
|
|
1942
|
+
case 'grok': return (0, grok_spawn_1.runGrok)(subArgs);
|
|
1898
1943
|
default:
|
|
1899
1944
|
console.error(`Unknown command: ${command}\n`);
|
|
1900
1945
|
console.log(HELP);
|