greprag 5.76.0 → 5.78.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.
@@ -719,6 +719,7 @@ function applyGrokHooks(config) {
719
719
  add('PreToolUse', '*', 'guard', 5, 'guard');
720
720
  add('Stop', '', 'store', 15, 'turn capture');
721
721
  add('Stop', '', 'drain', 8, 'inbox drain');
722
+ add('PostCompact', '', 'recompact', 15, 'Interrupt re-announce');
722
723
  add('PostCompact', '', 'session-id', 5, 'session-id');
723
724
  return changes;
724
725
  }
@@ -1411,14 +1412,27 @@ function applyCodexHooks(config) {
1411
1412
  else {
1412
1413
  changes.push('Codex Stop hook already configured (skipped)');
1413
1414
  }
1414
- const postCompactHook = {
1415
+ const postCompactAnnounceHook = {
1416
+ matcher: 'manual|auto',
1417
+ hooks: [commandHook('recompact', 10, 'Re-announcing GrepRAG primers')],
1418
+ };
1419
+ if (!hasGrepragHookWithMatcher(config.hooks.PostCompact, 'recompact', postCompactAnnounceHook.matcher)) {
1420
+ if (!config.hooks.PostCompact)
1421
+ config.hooks.PostCompact = [];
1422
+ config.hooks.PostCompact.push(postCompactAnnounceHook);
1423
+ changes.push('Added Codex PostCompact hook (Interrupt re-announce)');
1424
+ }
1425
+ else {
1426
+ changes.push('Codex PostCompact Interrupt re-announce hook already configured (skipped)');
1427
+ }
1428
+ const postCompactSessionHook = {
1415
1429
  matcher: 'manual|auto',
1416
1430
  hooks: [commandHook('session-id', 3, 'Restoring GrepRAG session id')],
1417
1431
  };
1418
- if (!hasGrepragHookWithMatcher(config.hooks.PostCompact, 'session-id', postCompactHook.matcher)) {
1432
+ if (!hasGrepragHookWithMatcher(config.hooks.PostCompact, 'session-id', postCompactSessionHook.matcher)) {
1419
1433
  if (!config.hooks.PostCompact)
1420
1434
  config.hooks.PostCompact = [];
1421
- config.hooks.PostCompact.push(postCompactHook);
1435
+ config.hooks.PostCompact.push(postCompactSessionHook);
1422
1436
  changes.push('Added Codex PostCompact hook (session-id awareness)');
1423
1437
  }
1424
1438
  else {
@@ -1432,6 +1446,7 @@ function normalizeCodexHookCommands(hooks) {
1432
1446
  const desired = {
1433
1447
  recap: { timeout: 10, statusMessage: 'Loading GrepRAG memory' },
1434
1448
  'codex-recap': { timeout: 10, statusMessage: 'Loading GrepRAG memory' },
1449
+ recompact: { timeout: 10, statusMessage: 'Re-announcing GrepRAG primers' },
1435
1450
  'session-id': { timeout: 3, statusMessage: 'Loading GrepRAG session id' },
1436
1451
  drain: { timeout: 5, statusMessage: 'Draining GrepRAG inbox' },
1437
1452
  'codex-notify': { timeout: 300, statusMessage: 'Checking GrepRAG inbox', runner: 'greprag-codex-hook' },
@@ -3,8 +3,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
3
3
  exports.OPENCODE_QUOTA_WAITLIST_ANNOUNCE = exports.opencodeRegistry = void 0;
4
4
  exports.buildOpenCodeEnv = buildOpenCodeEnv;
5
5
  exports.getOpenCodeAnnounces = getOpenCodeAnnounces;
6
+ exports.getOpenCodePersistentAnnounces = getOpenCodePersistentAnnounces;
6
7
  exports.getOpenCodeReminders = getOpenCodeReminders;
7
8
  const reminder_registry_1 = require("./reminder-registry");
9
+ const app_settings_1 = require("../app-settings");
8
10
  /** The opencode registry is DERIVED from the canon, never hand-whitelisted.
9
11
  * Every REGISTRY module ships to opencode unless it declares a `harnesses`
10
12
  * list excluding it (reminder-types.ts) — so a new module wired into
@@ -44,11 +46,18 @@ function buildOpenCodeEnv(params) {
44
46
  updateAvailable: params.updateAvailable ?? null,
45
47
  procedureAnnounces: params.procedureAnnounces ?? [],
46
48
  mirroredSkills: params.mirroredSkills,
49
+ personaAnnounce: params.personaAnnounce ?? null,
47
50
  };
48
51
  }
49
52
  function getOpenCodeAnnounces(env) {
50
- return [...(0, reminder_registry_1.collectAnnounces)(env, exports.opencodeRegistry), exports.OPENCODE_QUOTA_WAITLIST_ANNOUNCE];
53
+ return [...(0, reminder_registry_1.collectAnnounces)(env, (0, app_settings_1.filterEnabledInterrupts)(exports.opencodeRegistry)), exports.OPENCODE_QUOTA_WAITLIST_ANNOUNCE];
54
+ }
55
+ /** OpenCode rebuilds output.system for every model call. Persona is the one
56
+ * startup primer that must remain behaviorally active on later calls, so the
57
+ * plugin replays this shared-registry module from its bounded session cache. */
58
+ function getOpenCodePersistentAnnounces(env) {
59
+ return (0, reminder_registry_1.collectAnnounces)(env, (0, app_settings_1.filterEnabledInterrupts)(exports.opencodeRegistry.filter((m) => m.id === 'persona-announce')));
51
60
  }
52
61
  function getOpenCodeReminders(env) {
53
- return (0, reminder_registry_1.collectReminders)(env, exports.opencodeRegistry);
62
+ return (0, reminder_registry_1.collectReminders)(env, (0, app_settings_1.filterEnabledInterrupts)(exports.opencodeRegistry));
54
63
  }
@@ -0,0 +1,9 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.personaAnnounceModule = void 0;
4
+ exports.personaAnnounceModule = {
5
+ id: 'persona-announce',
6
+ detect: () => ({ tier: 'silent' }),
7
+ announce: (env) => env.personaAnnounce || null,
8
+ reminder: () => null,
9
+ };
@@ -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
@@ -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);
package/dist/hook.js CHANGED
@@ -83,6 +83,7 @@ const guard_1 = require("./guard");
83
83
  // auto-silences when resolved (the arm-check pattern). The hook below assembles the
84
84
  // live env (ALL I/O) and emits; the modules are pure.
85
85
  const reminder_registry_1 = require("./commands/reminder-registry");
86
+ const app_settings_1 = require("./app-settings");
86
87
  const inbox_primer_reminder_1 = require("./commands/inbox-primer-reminder");
87
88
  const skill_activation_manifest_1 = require("./skill-activation-manifest");
88
89
  const worktree_state_1 = require("./worktree-state");
@@ -1613,11 +1614,29 @@ async function recap(input, mode = 'plain', opts = {}) {
1613
1614
  return empty;
1614
1615
  }
1615
1616
  })();
1617
+ // Tenant Persona primer. The server owns both template resolution and
1618
+ // the provenance envelope; harnesses transport it unchanged. adr: adr/persona.md
1619
+ const personaPromise = (async () => {
1620
+ try {
1621
+ const res = await fetch(`${cfg.apiUrl}/v1/persona`, {
1622
+ headers: { 'Authorization': `Bearer ${cfg.apiKey}` },
1623
+ });
1624
+ if (!res.ok)
1625
+ return null;
1626
+ const data = await res.json();
1627
+ return typeof data.announce === 'string' && data.announce.length > 0
1628
+ ? data.announce
1629
+ : null;
1630
+ }
1631
+ catch {
1632
+ return null;
1633
+ }
1634
+ })();
1616
1635
  const updateAvailablePromise = checkForUpdate();
1617
1636
  const bodyPromise = (!opts.compact && anchor.sessionStartRecap)
1618
1637
  ? (0, opencode_plugin_helpers_1.buildRecapBody)(cfg.apiUrl, cfg.apiKey, anchor)
1619
1638
  : Promise.resolve('');
1620
- const [, corpusApiDocs, docPointers, enrichmentDown, skillGains, mirroredSkills, loadoutState, updateAvailable, body,] = await Promise.all([
1639
+ const [, corpusApiDocs, docPointers, enrichmentDown, skillGains, mirroredSkills, loadoutState, personaAnnounce, updateAvailable, body,] = await Promise.all([
1621
1640
  matchsetPromise,
1622
1641
  corpusApiDocsPromise,
1623
1642
  docPointersPromise,
@@ -1625,6 +1644,7 @@ async function recap(input, mode = 'plain', opts = {}) {
1625
1644
  skillGainsPromise,
1626
1645
  mirroredSkillsPromise,
1627
1646
  loadoutStatePromise,
1647
+ personaPromise,
1628
1648
  updateAvailablePromise,
1629
1649
  bodyPromise,
1630
1650
  ]);
@@ -1704,11 +1724,14 @@ async function recap(input, mode = 'plain', opts = {}) {
1704
1724
  mirroredSkills,
1705
1725
  procedureAnnounces,
1706
1726
  equippedLoadouts,
1727
+ personaAnnounce,
1707
1728
  updateAvailable,
1708
1729
  activeCommandDrift: activeCommandDrift(),
1709
1730
  loadoutUpdateRequired,
1710
1731
  };
1711
- let announceReg = (0, reminder_injector_1.mechanicKilled)() ? reminder_registry_1.REGISTRY.filter(m => m.id !== 'mechanic-friction') : reminder_registry_1.REGISTRY;
1732
+ let announceReg = (0, app_settings_1.filterEnabledInterrupts)(reminder_registry_1.REGISTRY);
1733
+ if ((0, reminder_injector_1.mechanicKilled)())
1734
+ announceReg = announceReg.filter(m => m.id !== 'mechanic-friction');
1712
1735
  if (!announceShort)
1713
1736
  announceReg = announceReg.filter(m => m.id !== 'watcher-arm');
1714
1737
  // PostCompact: a compaction wiped the once-loaded SessionStart announces from context.
@@ -2191,7 +2214,7 @@ async function coordinateGate(input) {
2191
2214
  toolCommand: typeof toolInput.command === 'string' ? toolInput.command : '',
2192
2215
  collisionPeers: peers,
2193
2216
  };
2194
- const fired = (0, reminder_registry_1.collectReminders)(env, (0, reminder_registry_1.commandModules)());
2217
+ const fired = (0, reminder_registry_1.collectReminders)(env, (0, app_settings_1.filterEnabledInterrupts)((0, reminder_registry_1.commandModules)()));
2195
2218
  if (!fired.length)
2196
2219
  return;
2197
2220
  // Effect: inject as additionalContext — advisory heads-up, NO permission pause.
@@ -2264,7 +2287,10 @@ async function main() {
2264
2287
  // opt out per-module with reannounceOnCompact:false) so the doctrine the compaction
2265
2288
  // wiped (incl. the watcher-arm announce) returns. additionalContext = the JSON form
2266
2289
  // PostCompact injects. adr: adr/monitor-resilience.md, docs/reminder-interrupt.md
2267
- await recap(input, 'additionalContext', { compact: true });
2290
+ await recap(input, 'additionalContext', {
2291
+ compact: true,
2292
+ platform: harness === 'grok' ? 'grok' : undefined,
2293
+ });
2268
2294
  }
2269
2295
  else if (subcommand === 'codex-recap') {
2270
2296
  await recap(input, 'additionalContext', { platform: 'codex' });
package/dist/index.js CHANGED
@@ -66,6 +66,7 @@ const doc_1 = require("./commands/doc");
66
66
  const parity_1 = require("./commands/parity");
67
67
  const agents_1 = require("./commands/agents");
68
68
  const loadout_1 = require("./commands/loadout");
69
+ const persona_1 = require("./commands/persona");
69
70
  const assistant_1 = require("./commands/assistant");
70
71
  const email_1 = require("./commands/email");
71
72
  const sms_1 = require("./commands/sms");
@@ -79,6 +80,7 @@ const crush_stats_1 = require("./commands/crush-stats");
79
80
  const archive_1 = require("./commands/archive");
80
81
  const inbox_watch_1 = require("./commands/inbox-watch");
81
82
  const grok_spawn_1 = require("./commands/grok-spawn");
83
+ const app_1 = require("./commands/app");
82
84
  const inbox_attachments_1 = require("./inbox-attachments");
83
85
  const discord_1 = require("./commands/discord");
84
86
  const project_anchor_1 = require("./project-anchor");
@@ -1284,6 +1286,8 @@ const HELP = `
1284
1286
  greprag — agent memory for Claude Code, Codex, OpenCode, and Grok Build
1285
1287
 
1286
1288
  Commands:
1289
+ (no command) Open the local GrepRAG app
1290
+ app | dashboard [--port N] Open the local app (use --no-open on remote machines)
1287
1291
  init [--api-key <key>] [--tenant-id <handle>]
1288
1292
  Auto-detect/ask for Codex, Claude Code, or OpenCode
1289
1293
  init --claude [--api-key <key>] [--tenant-id <handle>]
@@ -1568,6 +1572,14 @@ Loadout (gift a skill bundle to another tenant — offer, never auto-install):
1568
1572
  loadout unequip <name> [--delete-skills]
1569
1573
  Stop a loadout's auto-inflate.
1570
1574
 
1575
+ Persona (tenant-scoped speaking instructions):
1576
+ persona list List built-in Persona templates.
1577
+ persona use <template> Activate a built-in Persona template.
1578
+ persona set <text>|--file <f>|--stdin
1579
+ Save and activate a custom Persona verbatim.
1580
+ persona show Show the active Persona.
1581
+ persona clear Remove the active Persona.
1582
+
1571
1583
  GrepRAG Instructions (canonical global/repo CLAUDE.md + AGENTS.md):
1572
1584
  instructions pull <file> [--scope global|repo] [--push]
1573
1585
  Pull disk instructions into canon.
@@ -1607,6 +1619,7 @@ Other:
1607
1619
  full --help — the complete, always-current guide.
1608
1620
 
1609
1621
  Examples:
1622
+ greprag # open the local customer app
1610
1623
  greprag init # standard repo setup
1611
1624
  greprag init --global # Cowork / unanchored sessions
1612
1625
  greprag init --all # register every sibling repo for inbox
@@ -1639,6 +1652,7 @@ const HELP_ALL_GROUPS = [
1639
1652
  ['delivery', delivery_1.runDelivery],
1640
1653
  ['assistant', assistant_1.runAssistant],
1641
1654
  ['instructions', agents_1.runInstructions],
1655
+ ['persona', persona_1.runPersona],
1642
1656
  ['doc', doc_1.runDoc],
1643
1657
  ['skill', skill_1.runSkill],
1644
1658
  ['opencode', opencode],
@@ -1833,7 +1847,11 @@ async function printHelpAll() {
1833
1847
  async function main() {
1834
1848
  const args = process.argv.slice(2);
1835
1849
  const command = args[0];
1836
- if (!command || command === 'help' || command === '--help' || command === '-h') {
1850
+ if (!command) {
1851
+ await (0, app_1.runApp)([]);
1852
+ return;
1853
+ }
1854
+ if (command === 'help' || command === '--help' || command === '-h') {
1837
1855
  if (args.includes('--all')) {
1838
1856
  await printHelpAll();
1839
1857
  return;
@@ -1850,6 +1868,8 @@ async function main() {
1850
1868
  ensureEnv();
1851
1869
  const subArgs = args.slice(1);
1852
1870
  switch (command) {
1871
+ case 'app':
1872
+ case 'dashboard': return (0, app_1.runApp)(subArgs);
1853
1873
  case 'init': {
1854
1874
  if (subArgs.includes('--help') || subArgs.includes('-h') || subArgs.includes('help')) {
1855
1875
  console.log(INIT_HELP);
@@ -1871,6 +1891,7 @@ async function main() {
1871
1891
  const name = getFlag(args, '--name');
1872
1892
  const root = getFlag(args, '--root');
1873
1893
  await (0, init_1.runInit)({ apiKey, tenantId, installWatcher, global, claude, opencode, codex, grok, all, name, root });
1894
+ console.log('\n Open your local settings any time with: greprag\n');
1874
1895
  return;
1875
1896
  }
1876
1897
  case 'project-id': {
@@ -1926,6 +1947,7 @@ async function main() {
1926
1947
  case 'instructions': return (0, agents_1.runInstructions)(subArgs); // Canonical GrepRAG Instructions + rendered followers
1927
1948
  case 'agents': return (0, agents_1.runAgents)(subArgs); // Deprecated alias for GrepRAG Instructions
1928
1949
  case 'loadout': return (0, loadout_1.runLoadout)(subArgs); // Loadout: cross-tenant skill-bundle gifting (docs/loadout.md)
1950
+ case 'persona': return (0, persona_1.runPersona)(subArgs); // tenant-scoped speaking instructions
1929
1951
  case 'assistant': return (0, assistant_1.runAssistant)(subArgs); // designate this project as the tenant's Assistant (role flag)
1930
1952
  case 'opencode': return opencode(subArgs);
1931
1953
  case 'grok': return (0, grok_spawn_1.runGrok)(subArgs);