create-egregore 0.19.7 → 0.19.11

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (92) hide show
  1. package/assets/egregore-launcher.js +188 -295
  2. package/lib/local.js +11 -1
  3. package/lib/setup.js +33 -0
  4. package/package.json +1 -1
  5. package/runtime/codex/.codex/hooks/branch-guard.js +242 -34
  6. package/runtime/codex/.codex/skills/activity/SKILL.md +18 -6
  7. package/runtime/codex/.codex/skills/deep-reflect/SKILL.md +90 -55
  8. package/runtime/codex/.codex/skills/deploy-bridge/SKILL.md +25 -0
  9. package/runtime/codex/.codex/skills/handoff/SKILL.md +6 -2
  10. package/runtime/codex/.codex/skills/save/SKILL.md +21 -3
  11. package/runtime/codex/.codex/skills/scroll/SKILL.md +54 -3
  12. package/runtime/codex/.codex/skills/task/SKILL.md +25 -0
  13. package/runtime/codex/.codex/skills/tui-design/SKILL.md +1 -1
  14. package/runtime/codex/.codex/skills/wrap/SKILL.md +4 -1
  15. package/runtime/codex/.codex/spec-manifest.json +15 -3
  16. package/runtime/codex/AGENTS.md +45 -9
  17. package/runtime/codex/bin/activity-data.sh +1 -1
  18. package/runtime/codex/bin/agent.sh +66 -23
  19. package/runtime/codex/bin/attendant.sh +525 -0
  20. package/runtime/codex/bin/capture-reconcile.sh +67 -0
  21. package/runtime/codex/bin/capture-run.sh +341 -0
  22. package/runtime/codex/bin/codex-render-spec.mjs +15 -4
  23. package/runtime/codex/bin/codex-session-start.sh +45 -3
  24. package/runtime/codex/bin/codex-skill-render.mjs +23 -4
  25. package/runtime/codex/bin/graph-op.sh +719 -49
  26. package/runtime/codex/bin/graph-wal.sh +3 -2
  27. package/runtime/codex/bin/handoff-run.sh +72 -0
  28. package/runtime/codex/bin/handoff-save-egregore.sh +51 -11
  29. package/runtime/codex/bin/index-handoff.sh +26 -17
  30. package/runtime/codex/bin/ingest-graph.sh +80 -0
  31. package/runtime/codex/bin/ingest-surface.html +429 -0
  32. package/runtime/codex/bin/ingest.py +614 -0
  33. package/runtime/codex/bin/ingest.sh +231 -0
  34. package/runtime/codex/bin/ingest_graph.py +776 -0
  35. package/runtime/codex/bin/ingest_surface.py +464 -0
  36. package/runtime/codex/bin/lib/git-sync.sh +2 -6
  37. package/runtime/codex/bin/lib/greeting.sh +157 -22
  38. package/runtime/codex/bin/lib/identity.sh +50 -29
  39. package/runtime/codex/bin/lib/noncode.sh +31 -0
  40. package/runtime/codex/bin/person.py +610 -0
  41. package/runtime/codex/bin/person.sh +448 -0
  42. package/runtime/codex/bin/session-autosave.sh +184 -0
  43. package/runtime/codex/bin/session-log.sh +199 -0
  44. package/runtime/codex/bin/settings.sh +1 -1
  45. package/runtime/codex/manifest.json +19 -1
  46. package/runtime/pi/.codex/hooks/branch-guard.js +242 -34
  47. package/runtime/pi/.codex/skills/activity/SKILL.md +18 -6
  48. package/runtime/pi/.codex/skills/deep-reflect/SKILL.md +90 -55
  49. package/runtime/pi/.codex/skills/deploy-bridge/SKILL.md +25 -0
  50. package/runtime/pi/.codex/skills/handoff/SKILL.md +6 -2
  51. package/runtime/pi/.codex/skills/save/SKILL.md +21 -3
  52. package/runtime/pi/.codex/skills/scroll/SKILL.md +54 -3
  53. package/runtime/pi/.codex/skills/task/SKILL.md +25 -0
  54. package/runtime/pi/.codex/skills/tui-design/SKILL.md +1 -1
  55. package/runtime/pi/.codex/skills/wrap/SKILL.md +4 -1
  56. package/runtime/pi/.codex/spec-manifest.json +15 -3
  57. package/runtime/pi/.pi/APPEND_SYSTEM.md +41 -5
  58. package/runtime/pi/.pi/extensions/egregore.ts +98 -8
  59. package/runtime/pi/.pi/spec-manifest.json +2 -2
  60. package/runtime/pi/AGENTS.md +45 -9
  61. package/runtime/pi/bin/activity-data.sh +1 -1
  62. package/runtime/pi/bin/agent.sh +66 -23
  63. package/runtime/pi/bin/attendant.sh +525 -0
  64. package/runtime/pi/bin/capture-reconcile.sh +67 -0
  65. package/runtime/pi/bin/capture-run.sh +341 -0
  66. package/runtime/pi/bin/codex-render-spec.mjs +15 -4
  67. package/runtime/pi/bin/codex-session-start.sh +45 -3
  68. package/runtime/pi/bin/codex-skill-render.mjs +23 -4
  69. package/runtime/pi/bin/graph-op.sh +719 -49
  70. package/runtime/pi/bin/graph-wal.sh +3 -2
  71. package/runtime/pi/bin/handoff-run.sh +72 -0
  72. package/runtime/pi/bin/handoff-save-egregore.sh +51 -11
  73. package/runtime/pi/bin/index-handoff.sh +26 -17
  74. package/runtime/pi/bin/ingest-graph.sh +80 -0
  75. package/runtime/pi/bin/ingest-surface.html +429 -0
  76. package/runtime/pi/bin/ingest.py +614 -0
  77. package/runtime/pi/bin/ingest.sh +231 -0
  78. package/runtime/pi/bin/ingest_graph.py +776 -0
  79. package/runtime/pi/bin/ingest_surface.py +464 -0
  80. package/runtime/pi/bin/lib/git-sync.sh +2 -6
  81. package/runtime/pi/bin/lib/greeting.sh +157 -22
  82. package/runtime/pi/bin/lib/identity.sh +50 -29
  83. package/runtime/pi/bin/lib/noncode.sh +31 -0
  84. package/runtime/pi/bin/person.py +610 -0
  85. package/runtime/pi/bin/person.sh +448 -0
  86. package/runtime/pi/bin/pi-branch-policy.mjs +164 -4
  87. package/runtime/pi/bin/pi-product-workflows.mjs +31 -2
  88. package/runtime/pi/bin/publish-artifact.sh +14 -0
  89. package/runtime/pi/bin/session-autosave.sh +184 -0
  90. package/runtime/pi/bin/session-log.sh +199 -0
  91. package/runtime/pi/bin/settings.sh +274 -0
  92. package/runtime/pi/manifest.json +20 -1
@@ -48,22 +48,86 @@ function inside(child, parent) {
48
48
  return rel === "" || (!!rel && !rel.startsWith("..") && !path.isAbsolute(rel));
49
49
  }
50
50
 
51
- function findProjectDir(input) {
52
- // Tool payload cwd is the operation location. Codex launchers may keep
53
- // EGREGORE_CODEX_PROJECT_DIR pointed at the main checkout even after the
54
- // model continues in a task worktree, so using the env var first can make
55
- // legitimate worktree writes look like writes on protected develop.
56
- const cwd = input.cwd || process.cwd() || process.env.EGREGORE_CODEX_PROJECT_DIR;
57
- const top = run("git", ["-C", cwd, "rev-parse", "--show-toplevel"], cwd);
58
- if (top) return realish(top);
51
+ function nearestExistingDirectory(candidate) {
52
+ let cursor = path.resolve(candidate);
53
+ try {
54
+ if (fs.existsSync(cursor) && !fs.statSync(cursor).isDirectory()) cursor = path.dirname(cursor);
55
+ } catch {}
56
+ while (!fs.existsSync(cursor)) {
57
+ const parent = path.dirname(cursor);
58
+ if (parent === cursor) break;
59
+ cursor = parent;
60
+ }
61
+ return cursor;
62
+ }
63
+
64
+ function gitContext(candidate) {
65
+ if (!candidate) return null;
66
+ const probe = nearestExistingDirectory(candidate);
67
+ const top = run("git", ["-C", probe, "rev-parse", "--show-toplevel"], probe);
68
+ if (!top) return null;
69
+ const commonRaw = run("git", ["-C", top, "rev-parse", "--git-common-dir"], top);
70
+ const common = commonRaw
71
+ ? realish(path.isAbsolute(commonRaw) ? commonRaw : path.join(top, commonRaw))
72
+ : realish(path.join(top, ".git"));
73
+ return { top: realish(top), common };
74
+ }
75
+
76
+ function shellRetarget(command) {
77
+ // Only retarget a single operation. Mixed command chains are evaluated from
78
+ // the hub context so one external `git -C` cannot mask a later project write.
79
+ if (shellSegments(command).length !== 1) return "";
80
+ const gitC = String(command || "").match(/(?:^|[;&|]\s*)git\s+-C\s+("[^"]*"|'[^']*'|[^\s;&|]+)/);
81
+ const cdAnd = String(command || "").match(/^\s*cd\s+("[^"]*"|'[^']*'|[^\s;&|]+)\s*&&/);
82
+ const raw = (gitC && gitC[1]) || (cdAnd && cdAnd[1]) || "";
83
+ const target = raw.replace(/^['"]|['"]$/g, "");
84
+ return /[$`*?]/.test(target) ? "" : target;
85
+ }
86
+
87
+ function operationCandidate(input, baseDir) {
88
+ const toolName = input.tool_name || "";
89
+ const toolInput = input.tool_input || {};
90
+ const explicitWorkdir = toolInput.workdir || toolInput.cwd || "";
91
+ let candidate = explicitWorkdir
92
+ ? (path.isAbsolute(explicitWorkdir) ? explicitWorkdir : path.join(baseDir, explicitWorkdir))
93
+ : baseDir;
59
94
 
60
- const envDir = process.env.EGREGORE_CODEX_PROJECT_DIR || "";
61
- if (envDir) {
62
- const envTop = run("git", ["-C", envDir, "rev-parse", "--show-toplevel"], envDir);
63
- return realish(envTop || envDir);
95
+ if (toolName === "Edit" || toolName === "Write") {
96
+ const target = toolInput.file_path || toolInput.path || "";
97
+ if (target) candidate = path.isAbsolute(target) ? target : path.join(candidate, target);
98
+ } else if (toolName === "Bash") {
99
+ const target = shellRetarget(toolInput.command || "");
100
+ if (target) candidate = path.isAbsolute(target) ? target : path.join(candidate, target);
101
+ }
102
+
103
+ return candidate;
104
+ }
105
+
106
+ function findProjectContext(input) {
107
+ // The launcher keeps EGREGORE_CODEX_PROJECT_DIR on the hub checkout while
108
+ // individual tool calls can target a task worktree, memory, or a managed
109
+ // repo. Compare Git common dirs: a task worktree shares the hub common dir
110
+ // and must use its own branch; memory/managed repos are separate and exempt.
111
+ const fallback = input.cwd || process.cwd();
112
+ const hubSeed = process.env.EGREGORE_CODEX_PROJECT_DIR || fallback;
113
+ const hub = gitContext(hubSeed);
114
+ const baseDir = input.cwd || hub?.top || fallback;
115
+ const candidate = operationCandidate(input, baseDir);
116
+ const operation = gitContext(candidate);
117
+
118
+ if (!hub) return { projectDir: operation?.top || realish(baseDir), external: false };
119
+ if (operation && operation.common === hub.common) {
120
+ return { projectDir: operation.top, external: false };
121
+ }
122
+ if (operation && operation.common !== hub.common) {
123
+ return { projectDir: hub.top, external: true };
64
124
  }
65
125
 
66
- return realish(cwd);
126
+ const resolvedCandidate = realish(candidate);
127
+ if (!inside(resolvedCandidate, hub.top)) {
128
+ return { projectDir: hub.top, external: true };
129
+ }
130
+ return { projectDir: hub.top, external: false };
67
131
  }
68
132
 
69
133
  function currentBranch(projectDir) {
@@ -95,7 +159,7 @@ function isExemptPath(rawPath, projectDir, memoryDir) {
95
159
  if (inside(resolved, target)) return true;
96
160
  }
97
161
 
98
- for (const file of [".egregore-state.json", ".env"]) {
162
+ for (const file of [".egregore-state.json", ".egregore-branch-consent", ".env"]) {
99
163
  if (resolved === realish(path.join(projectDir, file))) return true;
100
164
  }
101
165
 
@@ -133,12 +197,13 @@ function commandTargetsOtherRepo(command, projectDir, memoryDir) {
133
197
  }
134
198
 
135
199
  function isBranchSetupCommand(command) {
200
+ if (shellSegments(command).length !== 1) return false;
136
201
  return (
137
- /\bbin\/agent\.sh\s+branch\b/.test(command) ||
138
- /\bgit\s+(checkout|switch)\s+(-b|-c)\b/.test(command) ||
139
- /\bgit\s+branch\s+(dev|feature|bugfix)\//.test(command) ||
140
- /\bgit\s+worktree\s+add\b/.test(command) ||
141
- /\bbin\/worktree\.sh\s+setup\b/.test(command)
202
+ /^\s*bin\/agent\.sh\s+branch\b/.test(command) ||
203
+ /^\s*git\s+(checkout|switch)\s+(-b|-c)\b/.test(command) ||
204
+ /^\s*git\s+branch\s+(dev|feature|bugfix)\//.test(command) ||
205
+ /^\s*git\s+worktree\s+add\b/.test(command) ||
206
+ /^\s*bin\/worktree\.sh\s+setup\b/.test(command)
142
207
  );
143
208
  }
144
209
 
@@ -149,7 +214,134 @@ function stagedOnlyFrameworkPaths(projectDir) {
149
214
  return files.length > 0 && files.every((file) => /^(bin\/|\.claude\/|\.codex\/|CLAUDE\.md$|AGENTS\.md$|skills\/)/.test(file));
150
215
  }
151
216
 
217
+ function stripQuotedSegments(command) {
218
+ let out = "";
219
+ let quote = null;
220
+ for (let i = 0; i < String(command || "").length; i++) {
221
+ const ch = command[i];
222
+ if (quote) {
223
+ if (quote === '"' && ch === "\\") i++;
224
+ else if (ch === quote) quote = null;
225
+ continue;
226
+ }
227
+ if (ch === "'" || ch === '"') {
228
+ quote = ch;
229
+ out += "x";
230
+ continue;
231
+ }
232
+ out += ch;
233
+ }
234
+ return out;
235
+ }
236
+
237
+ function maskQuotedOperators(command) {
238
+ let out = "";
239
+ let quote = null;
240
+ for (let i = 0; i < String(command || "").length; i++) {
241
+ const ch = command[i];
242
+ if (quote) {
243
+ if (quote === '"' && ch === "\\") {
244
+ out += ch;
245
+ if (i + 1 < command.length) out += command[++i];
246
+ continue;
247
+ }
248
+ if (ch === quote) quote = null;
249
+ out += /[><|;&]/.test(ch) ? " " : ch;
250
+ continue;
251
+ }
252
+ if (ch === "'" || ch === '"') quote = ch;
253
+ out += ch;
254
+ }
255
+ return out;
256
+ }
257
+
258
+ function shellSegments(command) {
259
+ const segments = [];
260
+ let current = "";
261
+ let quote = null;
262
+ for (let i = 0; i < String(command || "").length; i++) {
263
+ const ch = command[i];
264
+ if (quote) {
265
+ current += ch;
266
+ if (quote === '"' && ch === "\\" && i + 1 < command.length) current += command[++i];
267
+ else if (ch === quote) quote = null;
268
+ continue;
269
+ }
270
+ if (ch === "'" || ch === '"') {
271
+ quote = ch;
272
+ current += ch;
273
+ continue;
274
+ }
275
+ if (ch === ";" || ch === "|" || ch === "&" || ch === "\n") {
276
+ if (current.trim()) segments.push(current.trim());
277
+ current = "";
278
+ continue;
279
+ }
280
+ current += ch;
281
+ }
282
+ if (current.trim()) segments.push(current.trim());
283
+ return segments;
284
+ }
285
+
286
+ function shellWords(segment) {
287
+ const words = [];
288
+ let current = "";
289
+ let quote = null;
290
+ for (let i = 0; i < String(segment || "").length; i++) {
291
+ const ch = segment[i];
292
+ if (quote) {
293
+ if (quote === '"' && ch === "\\" && i + 1 < segment.length) current += segment[++i];
294
+ else if (ch === quote) quote = null;
295
+ else current += ch;
296
+ continue;
297
+ }
298
+ if (ch === "'" || ch === '"') {
299
+ quote = ch;
300
+ continue;
301
+ }
302
+ if (/\s/.test(ch)) {
303
+ if (current) words.push(current);
304
+ current = "";
305
+ continue;
306
+ }
307
+ if (ch === "\\") {
308
+ if (i + 1 < segment.length) current += segment[++i];
309
+ continue;
310
+ }
311
+ current += ch;
312
+ }
313
+ if (current) words.push(current);
314
+ return words;
315
+ }
316
+
317
+ function filesystemMutationStatus(command, projectDir, memoryDir) {
318
+ const commands = new Set(["rm", "mv", "cp", "mkdir", "rmdir", "touch", "chmod", "chown", "ln", "tee"]);
319
+ let unsafe = false;
320
+
321
+ for (const segment of shellSegments(command)) {
322
+ const words = shellWords(segment);
323
+ while (words.length && /^[A-Za-z_][A-Za-z0-9_]*=/.test(words[0])) words.shift();
324
+ while (words[0] === "sudo" || words[0] === "command" || words[0] === "env") words.shift();
325
+ if (!words.length) continue;
326
+
327
+ const name = path.basename(words.shift());
328
+ if (!commands.has(name)) continue;
329
+
330
+ const args = words.filter((word) => !word.startsWith("-"));
331
+ let targets = args;
332
+ if (name === "cp" || name === "ln") targets = args.length ? [args[args.length - 1]] : [];
333
+ if (name === "chmod" || name === "chown") targets = args.slice(1);
334
+
335
+ if (!targets.length || targets.some((target) =>
336
+ /[$`*?{}]/.test(target) || !isExemptPath(target, projectDir, memoryDir)
337
+ )) unsafe = true;
338
+ }
339
+
340
+ return { unsafe };
341
+ }
342
+
152
343
  function redirectLooksMutating(command, projectDir, memoryDir) {
344
+ const visible = maskQuotedOperators(command);
153
345
  const redirectPatterns = [
154
346
  /(?:^|[\s;|&])(?:\d*)>>?\s*([^&\s;|]+)/g,
155
347
  /\|\s*tee\s+(?:-a\s+)?([^&\s;|]+)/g,
@@ -157,7 +349,7 @@ function redirectLooksMutating(command, projectDir, memoryDir) {
157
349
 
158
350
  for (const pattern of redirectPatterns) {
159
351
  let match;
160
- while ((match = pattern.exec(command))) {
352
+ while ((match = pattern.exec(visible))) {
161
353
  const target = String(match[1] || "").replace(/^["']|["']$/g, "");
162
354
  if (!target || target === "/dev/null" || target.startsWith("$")) continue;
163
355
  if (!isExemptPath(target, projectDir, memoryDir)) return true;
@@ -168,18 +360,31 @@ function redirectLooksMutating(command, projectDir, memoryDir) {
168
360
 
169
361
  function shellLooksMutating(command, projectDir, memoryDir) {
170
362
  if (!command.trim()) return false;
171
- if (isBranchSetupCommand(command)) return false;
363
+ const visible = stripQuotedSegments(command);
364
+ if (isBranchSetupCommand(visible)) return false;
172
365
 
173
366
  const mutatingPatterns = [
174
- /\bgit\s+(add|commit|push|rm|mv|reset|restore|checkout\s+--|clean|rebase|merge|cherry-pick)\b/,
175
- /\b(apply_patch|touch|mkdir|rm|mv|cp|chmod|chown|ln)\b/,
176
- /\b(sed|perl)\b[^;&|]*\s-i(\s|$)/,
177
- /\b(npm|pnpm|yarn|bun)\s+(install|add|remove|update|dedupe|ci)\b/,
178
- /\b(go\s+mod\s+tidy|cargo\s+(fmt|fix|update)|rustfmt|prettier\s+--write|eslint\s+--fix)\b/,
367
+ /(?:^|[;&|(\n]\s*)(?:sudo\s+)?git(?:\s+-C\s+\S+)?\s+(add|commit|push|rm|mv|reset|restore|checkout\s+--|clean|rebase|merge|cherry-pick)\b/,
368
+ /(?:^|[;&|(\n]\s*)(?:sudo\s+)?apply_patch\b/,
369
+ /(?:^|[;&|(\n]\s*)(?:sudo\s+)?(sed|perl)\b[^;&|]*\s-i(\s|$)/,
370
+ /(?:^|[;&|(\n]\s*)(?:sudo\s+)?(npm|pnpm|yarn|bun)\s+(install|add|remove|update|dedupe|ci)\b/,
371
+ /(?:^|[;&|(\n]\s*)(?:sudo\s+)?(go\s+mod\s+tidy|cargo\s+(fmt|fix|update)|rustfmt|prettier\s+--write|eslint\s+--fix)\b/,
179
372
  ];
180
373
 
181
- if (/\bgit\s+commit\b/.test(command) && stagedOnlyFrameworkPaths(projectDir)) return false;
182
- return mutatingPatterns.some((pattern) => pattern.test(command)) || redirectLooksMutating(command, projectDir, memoryDir);
374
+ const gitCommit = /(?:^|[;&|(\n]\s*)(?:sudo\s+)?git(?:\s+-C\s+\S+)?\s+commit\b/.test(visible);
375
+ if (gitCommit && stagedOnlyFrameworkPaths(projectDir)) return false;
376
+
377
+ const filesystem = filesystemMutationStatus(command, projectDir, memoryDir);
378
+ return mutatingPatterns.some((pattern) => pattern.test(visible)) ||
379
+ filesystem.unsafe ||
380
+ redirectLooksMutating(command, projectDir, memoryDir);
381
+ }
382
+
383
+ function isConsentCommand(command, branch) {
384
+ const escaped = branch.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
385
+ return new RegExp(
386
+ `^\\s*echo\\s+['"]?${escaped}['"]?\\s*>\\s*(?:\\./)?\\.egregore-branch-consent\\s*$`
387
+ ).test(String(command || ""));
183
388
  }
184
389
 
185
390
  function deny(reason) {
@@ -194,7 +399,9 @@ function deny(reason) {
194
399
 
195
400
  function main() {
196
401
  const input = parseJson(readStdin());
197
- const projectDir = findProjectDir(input);
402
+ const context = findProjectContext(input);
403
+ if (context.external) return;
404
+ const projectDir = context.projectDir;
198
405
  const branch = currentBranch(projectDir);
199
406
  if (!isProtectedBranch(branch)) return;
200
407
 
@@ -211,11 +418,11 @@ function main() {
211
418
  const memoryDir = realish(path.join(projectDir, "memory"));
212
419
  const author = currentAuthor(projectDir);
213
420
  const reason = [
214
- `Protected branch (${branch}). Ask the user how to proceed — do NOT auto-branch silently.`,
215
- "Offer a choice (numbered or structured): Branch off (default) / Proceed on this branch / Cancel.",
216
- ` • Branch off: bin/agent.sh branch --topic "<work topic>" (prefix dev/${author}/...)`,
217
- ` • Proceed on ${branch}: echo '${branch}' > .egregore-branch-consent then retry`,
218
- "Proceeding records consent for this branch for the rest of the session (cleared on next session start).",
421
+ `Protected branch (${branch}). Move project work to a task branch before writing.`,
422
+ `If the work topic is clear, run bin/agent.sh branch --topic "<work topic>" automatically (prefix dev/${author}/...), continue in the printed worktree, and briefly tell the user.`,
423
+ "Ask only when the work topic is genuinely ambiguous; ask for the topic, not for routine Git permission.",
424
+ `Only when the user explicitly requested writing on ${branch}, record consent with: echo '${branch}' > .egregore-branch-consent`,
425
+ "Memory, framework-runtime, and managed-repo writes are exempt and should not trigger this guard.",
219
426
  ].join("\n");
220
427
 
221
428
  if (toolName === "apply_patch") {
@@ -234,6 +441,7 @@ function main() {
234
441
 
235
442
  if (toolName === "Bash") {
236
443
  const command = String(toolInput.command || "");
444
+ if (isConsentCommand(command, branch)) return;
237
445
  if (commandTargetsOtherRepo(command, projectDir, memoryDir)) return;
238
446
  if (shellLooksMutating(command, projectDir, memoryDir)) deny(reason);
239
447
  }
@@ -15,15 +15,27 @@ remain enough to render a useful answer.
15
15
 
16
16
  1. Verify `bin/agent.sh` exists. If it does not, say this is not an Egregore
17
17
  checkout.
18
- 2. Run `bin/agent.sh sync`.
19
- 3. Resolve the requested handle:
18
+ 2. If the request is `activity done N`, `activity expire N`, or
19
+ `activity reopen N`, fetch `bin/activity-data.sh` to a temporary JSON file,
20
+ map `N` to the corresponding `handoffs_to_me[N-1].sessionId`, and run:
21
+
22
+ ```bash
23
+ bash bin/activity-action.sh <done|expire|reopen> "<session-id>"
24
+ ```
25
+
26
+ Report the resulting topic and state. Never accept a raw session ID from the
27
+ visible command when a numbered activity item is available; the number keeps
28
+ the mutation tied to what the user just saw. In local mode, preserve the
29
+ script's explicit unavailable result.
30
+ 3. Run `bin/agent.sh sync`.
31
+ 4. Resolve the requested handle:
20
32
  - If the user named a person, match it against `memory/people/*.md`.
21
33
  - Otherwise use `.egregore-state.json` `github_username`, then git user
22
34
  fallback.
23
- 4. Run `bash bin/activity-data.sh [handle]` with network escalation and capture
35
+ 5. Run `bash bin/activity-data.sh [handle]` with network escalation and capture
24
36
  stdout as JSON. This command performs the graph API call internally; a
25
37
  normal sandboxed Codex shell can make a healthy graph look unreachable.
26
- 5. Classify the graph status:
38
+ 6. Classify the graph status:
27
39
 
28
40
  ```bash
29
41
  node bin/codex-skill-render.mjs classify-graph --mode connected
@@ -37,7 +49,7 @@ say Codex graph network access was not granted; do not claim the graph itself
37
49
  was unreachable. Do not retry for `missing_config`, `auth_error`, or
38
50
  `server_error`; render the clear reason instead.
39
51
 
40
- 6. Render the card with:
52
+ 7. Render the card with:
41
53
 
42
54
  ```bash
43
55
  node bin/codex-skill-render.mjs activity-card <json-file>
@@ -47,7 +59,7 @@ Paste the rendered card exactly in the visible response, preferably in a
47
59
  `text` fenced block. Do not paraphrase the card into bullets unless the user
48
60
  asks for a summary.
49
61
 
50
- 7. Offer one compact focus prompt:
62
+ 8. Offer one compact focus prompt:
51
63
  - `Handoffs` - open pending handoffs.
52
64
  - `Questions` - answer or inspect pending questions.
53
65
  - `Recent work` - inspect sessions and wraps.
@@ -1,74 +1,109 @@
1
1
  ---
2
2
  name: deep-reflect
3
- description: Cross-reference an insight against the Egregore knowledge base using Codex-native staged analysis when the user invokes /deep-reflect or $deep-reflect.
3
+ description: Deep research over the Egregore org memory — multi-hop, evidence-verified synthesis of what the org collectively knows — when the user invokes /deep-reflect or $deep-reflect.
4
4
  ---
5
5
 
6
- # Egregore Deep Reflect
6
+ # Egregore Deep Reflect (v4)
7
7
 
8
- Native Codex Egregore skill. Use this for evidence-based analysis of how a new
9
- insight relates to the existing knowledge base.
8
+ Native Codex Egregore skill. Ask the org's memory a question and return a
9
+ verified, cited synthesis of what it collectively knows — including what it
10
+ doesn't. `/reflect` captures user thought; `/deep-reflect` researches what the
11
+ org already knows.
10
12
 
11
- ## Modes
13
+ Full contracts live in `.claude/skills/deep-reflect/` (`SKILL.md` entry,
14
+ `RESEARCH.md` engine, `ROLES.md` agent roles, `REPORT.md` output). Follow them;
15
+ this file is the Codex-runtime adapter.
12
16
 
13
- - empty request: deep mode.
14
- - `focused ...`: focused topic mode.
15
- - `quick ...` or `category: content`: quick mode.
17
+ ## Modes
16
18
 
17
- Detect directed questions when the user asks how, why, what something means, or
18
- about a relationship. Preserve the user's lens while still surfacing important
19
- signals outside the lens.
19
+ - Question (default): interrogative or bare-topic arguments → "what does the
20
+ org know about X?"
21
+ - Cross-ref: "cross-reference this" / a declarative insight → "how does this
22
+ insight sit against existing memory?"
23
+ - Depth: `--brief` (1 hop wave), standard (≤3), `--deep` (≤5). Default is
24
+ dynamic from the corpus probe.
20
25
 
21
26
  ## Flow
22
27
 
23
- 1. Check `egregore.json` mode first. In local mode, do a lightweight reflection
24
- from memory files and do not call graph, batch graph, or notification
25
- scripts.
26
- 2. In all modes, run `bin/agent.sh search "$TOPIC" -n 6` — hybrid content
27
- recall over the whole memory repo. Feed strong hits into the candidate
28
- set; in local mode this is the primary context source.
29
- 3. In connected mode, additionally gather graph context with `bin/graph.sh`
30
- and suppress raw JSON. Query recent sessions, active quests, recent
31
- artifacts, decisions, the topic landscape, and the full artifact
32
- landscape.
33
- 4. If the graph is unavailable or has fewer than about ten artifacts, fall back
34
- to lightweight reflection and say why in one line.
35
- 5. Select candidate artifacts. Prefer title, type, topics, quest, author, and
36
- date metadata first; fetch file contents only for the selected subset.
37
- 6. Use Codex multi-agent or subagent tooling only if it is available in the
38
- current session. If not, run a single-model staged analysis:
39
- - candidate selection summary
40
- - evidence extraction summary
41
- - signal analysis
42
- - final synthesis
43
- 7. Classify signals as tension, convergence, gap, dependency, phase shift,
44
- redundancy, emergence, reinforcement, or a named custom type.
45
- 8. Write a markdown artifact under `memory/knowledge/deep-reflect/` with
46
- frontmatter for run id, mode, lens, author, created time, and selected
47
- evidence paths.
48
- 9. In connected mode, write best-effort graph edges with `bin/graph.sh` or
49
- `bin/graph-batch.sh`. Use `TENSION_WITH` for conflict signals and
50
- `RELATES_TO` for other signals.
51
- 10. Save memory and repo changes with `bin/agent.sh save --message "Deep reflect: $TOPIC" --topic "$TOPIC"`.
28
+ 1. Check `egregore.json` mode FIRST. Local mode: full engine over files; never
29
+ call graph, batch-graph, or notification scripts; zero graph vocabulary in
30
+ output. Connected: graph adds hop rails and writeback, every graph call
31
+ non-fatal (`2>/dev/null || true`).
32
+ 2. FRAME: corpus probe — 2× `bin/agent.sh search "$TOPIC" --fast -n 20` with
33
+ different wordings + `bin/artifacts.sh find "$TOPIC"` +
34
+ `grep -ril "$TOPIC" memory/knowledge/ memory/handoffs/ memory/artifacts/`.
35
+ N = the union of unique paths; set the wave budget from N per
36
+ `RESEARCH.md` §Budget; decompose the question into 2-4 sub-questions;
37
+ print a plan line (`Researching: … · ~N relevant docs · budget M waves ·
38
+ est ~X min`). Under ~5 relevant docs: say memory is too thin and offer
39
+ `/reflect` or `/harvest` instead of faking research. If search errors or
40
+ returns nothing while grep hits, rely on grep and note "recall degraded".
41
+ 3. SEED: check `memory/knowledge/research/` for prior runs on the topic —
42
+ their cited artifacts (frontmatter `artifacts_cited`, older reports use
43
+ `artifacts_consulted`; resolve filename stems with `find`) are pre-warmed
44
+ leads, and their `null_regions` gaps get re-audited. Build a frontier of
45
+ ≤12 docs from search probes, frontmatter topic/quest greps, and index
46
+ ledgers.
47
+ 4. HOP WAVES: the Codex runtime has no Workflow tool — run the foreground
48
+ staged path from `ROLES.md`: per wave, read 8-12 docs (subagents if the
49
+ session offers them, otherwise staged single-model passes), extract
50
+ claims-with-verbatim-excerpts and typed leads (doc links, quests, people,
51
+ sessions, topic terms, named absences). Dedup against the seen-set, score
52
+ leads, hop until saturation per `RESEARCH.md` (coverage on every
53
+ sub-question plus low novelty or zero yield; or budget).
54
+ 5. PATTERN: two de-correlated passes. Without subagents true blindness is
55
+ impossible — degrade deliberately: run the TENSION pass FIRST (grouped by
56
+ topic), then the convergence pass (chronological), so tension-finding is
57
+ never anchored by an existing convergence story. Disagreement between
58
+ passes is a finding, never averaged away.
59
+ 6. VERIFY: for every claim, whitespace-normalize the excerpt and re-find it in
60
+ the cited file (miss = miscite = kill; supported paraphrase = correct the
61
+ excerpt). The mechanical re-check survives self-verification — it is a
62
+ grep, not a judgment. Re-attack every gap claim with one extra probe; a
63
+ found doc refutes the gap and joins the ledger. Evidence older than ~90
64
+ days gets one `--fast` newer-artifact probe. Report the kill count. When
65
+ the verifying context is the same one that made the claims, mark the
66
+ report's provenance `verification: self-checked`.
67
+ 7. SYNTHESIZE + PRESENT: stance, not summary — answer first, then ◆ primary /
68
+ ◇ secondary findings with `memory/...` path citations, tensions, and "what
69
+ memory doesn't hold" (gaps + the probes that prove them). Confidence is
70
+ derived from evidence structure (verified + ≥3 independent sources = high;
71
+ single-source always labeled). Register: analyst, not critic.
72
+ 8. CAPTURE (user-gated): Save (default) / Edit first / File follow-ups /
73
+ Skip. On Save write `memory/knowledge/research/YYYY-MM-DD-author-slug.md`
74
+ with frontmatter (question, mode, stop_reason, waves, docs_read,
75
+ claims_killed, builds_on, artifacts_consulted, artifacts_cited,
76
+ null_regions), then connected-mode best-effort graph writes per the main
77
+ `REPORT.md` §Writeback (MERGE with filePath — do NOT use
78
+ `graph-op.sh register-artifact`; `RELATES_TO`/`TENSION_WITH` for verified
79
+ cross-ref claims only), then
80
+ `bin/agent.sh save --message "Deep research: $TOPIC" --topic "$TOPIC"`.
81
+ On Skip: nothing is written except the local run ledger under
82
+ `.egregore/research-runs/`. Render the TUI box AFTER the gate, reflecting
83
+ the actual outcome.
52
84
 
53
85
  ## Output
54
86
 
55
- Lead with the strongest finding, then list primary signals, evidence paths, and
56
- recommended next action. Keep secondary and ambient signals short.
57
-
58
- Structured UX parity is required. Then render the Egregore deep-reflect
59
- confirmation TUI:
87
+ Render the Egregore deep-reflect confirmation TUI after the capture gate:
60
88
 
61
- - Use a 72-column outer box with standard top/separator/content/bottom lines.
62
- - Header: `DEEP REFLECT`, author, and date.
63
- - Body: lens/topic, strongest finding, signal counts by type, selected evidence
64
- count, artifact path, and recommended next action.
65
- - Footer: saved/pushed state. In connected mode include graph-link state when
66
- available; in local mode omit graph language.
67
- - Do not show raw graph JSON or replace the confirmation box with plain prose.
89
+ - 72-column outer box, standard top/separator/content/bottom lines only.
90
+ - Header: `◈ DEEP REFLECT`, author, date.
91
+ - Body: question, ◆/◇ finding counts, confidence, waves · docs read · cited ·
92
+ claims cut in verification.
93
+ - Footer: actual saved/pushed state; say "graphed" only if the graph write
94
+ succeeded; in local mode omit graph language entirely.
95
+ - Structured UX parity is required: preserve the rendered deep-reflect TUI
96
+ box, no preamble, no prose-only replacement, and no raw search or graph
97
+ output.
98
+ - Never show raw graph JSON; never replace the box with prose.
68
99
 
69
100
  ## Rules
70
101
 
71
- - Never show raw graph JSON.
72
- - Do not claim evidence you did not inspect.
73
- - Keep intermediate summaries explicit so the user can audit the reasoning.
102
+ - Search budget: at most ONE non-`--fast` search probe per run (seed only);
103
+ `--fast` everywhere else. Count result blocks, never the banner hit count.
104
+ - No claim without a verbatim excerpt that survives re-finding in the file.
105
+ - Absence is a first-class finding — cite the probes that prove it.
106
+ - Don't manufacture significance: "memory holds nothing structural on this"
107
+ is a valid report.
108
+ - At most ONE clarifying question before the run, plus the capture gate.
74
109
  - Do not use Claude Code commands.
@@ -0,0 +1,25 @@
1
+ ---
2
+ name: deploy-bridge
3
+ description: 'Deploy the Telegram bridge (egregore_bridge.py) to the VPS agent box and verify it comes back healthy. Org-internal — the bridge does NOT auto-deploy from develop.'
4
+ ---
5
+
6
+ <!-- generated-by: bin/codex-sync-skills.sh -->
7
+
8
+ # Egregore deploy-bridge Adapter
9
+
10
+ This is fallback coverage for a long-tail Egregore workflow that has not been
11
+ ported to a hand-written Codex-native skill yet.
12
+
13
+ Use the project shell and filesystem directly. Do not invoke Claude Code
14
+ commands. Translate interactive choices to structured Codex question tooling
15
+ when it is available; otherwise render compact numbered choices with an
16
+ `Other:` option and wait for the user.
17
+
18
+ 1. Read `.claude/skills/deploy-bridge/SKILL.md` for the workflow details.
19
+ 2. Run the referenced `bin/` scripts directly from Codex.
20
+ 3. Treat graph, publish, and notify steps as best-effort unless that workflow
21
+ explicitly says they are required.
22
+ 4. Keep local-mode behavior filesystem-first and avoid graph or notification
23
+ calls when `egregore.json` declares `"mode": "local"`.
24
+ 5. Never call the deprecated `egregore-handoff` CLI for Egregore project
25
+ handoffs.
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: handoff
3
- description: Create an internal Egregore team handoff when the user invokes /handoff or $handoff, says they are done, asks to hand off work, or wants to pass context to a teammate or future self.
3
+ description: Create an internal Egregore team handoff when the user invokes /handoff or $handoff, asks to hand off work, or wants to pass context to a teammate or future self.
4
4
  ---
5
5
 
6
6
  # Egregore Handoff
@@ -11,6 +11,10 @@ the Egregore scripts. External runnable packets are emissaries; if the user
11
11
  asks for one or provides an `egregore.xyz/emissary/e/` link, use the emissary
12
12
  tooling instead.
13
13
 
14
+ `/handoff` is pure addressed capture. Do not triage incoming handoffs here;
15
+ route “show/close my handoffs” to `$activity`. Personal session closure without
16
+ an addressed recipient belongs to `$wrap`.
17
+
14
18
  ## Flow
15
19
 
16
20
  1. Run `bin/agent.sh sync`.
@@ -29,7 +33,7 @@ tooling instead.
29
33
 
30
34
  ```bash
31
35
  TMPDIR="$(mktemp -d -t codex-handoff-XXXXXX)"
32
- TMPDIR="$TMPDIR" bash bin/handoff-run.sh --author "$AUTHOR" --topic "$TOPIC" ${RECIPIENT_ARG} < "$BODY_FILE"
36
+ TMPDIR="$TMPDIR" bash bin/capture-run.sh --mode addressed --author "$AUTHOR" --topic "$TOPIC" ${RECIPIENT_ARG} < "$BODY_FILE"
33
37
  ```
34
38
 
35
39
  Use `--recipient "$RECIPIENT"` only when a clear teammate or future-self