create-egregore 0.19.8 → 0.19.12
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/assets/egregore-launcher.js +266 -4
- package/lib/local.js +11 -1
- package/lib/setup.js +33 -0
- package/package.json +2 -2
- package/runtime/codex/.codex/hooks/branch-guard.js +242 -34
- package/runtime/codex/.codex/skills/activity/SKILL.md +18 -6
- package/runtime/codex/.codex/skills/deep-reflect/SKILL.md +90 -55
- package/runtime/codex/.codex/skills/deploy-bridge/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/handoff/SKILL.md +6 -2
- package/runtime/codex/.codex/skills/save/SKILL.md +29 -5
- package/runtime/codex/.codex/skills/scroll/SKILL.md +54 -3
- package/runtime/codex/.codex/skills/task/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/tui-design/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/wrap/SKILL.md +4 -1
- package/runtime/codex/.codex/spec-manifest.json +19 -7
- package/runtime/codex/AGENTS.md +63 -19
- package/runtime/codex/bin/activity-data.sh +1 -1
- package/runtime/codex/bin/agent.sh +88 -25
- package/runtime/codex/bin/attendant.sh +525 -0
- package/runtime/codex/bin/capture-reconcile.sh +67 -0
- package/runtime/codex/bin/capture-run.sh +341 -0
- package/runtime/codex/bin/codex-render-spec.mjs +29 -14
- package/runtime/codex/bin/codex-session-start.sh +49 -4
- package/runtime/codex/bin/codex-skill-render.mjs +23 -4
- package/runtime/codex/bin/graph-op.sh +719 -49
- package/runtime/codex/bin/graph-wal.sh +3 -2
- package/runtime/codex/bin/handoff-run.sh +72 -0
- package/runtime/codex/bin/handoff-save-egregore.sh +51 -11
- package/runtime/codex/bin/index-handoff.sh +26 -17
- package/runtime/codex/bin/ingest-graph.sh +80 -0
- package/runtime/codex/bin/ingest-surface.html +429 -0
- package/runtime/codex/bin/ingest.py +614 -0
- package/runtime/codex/bin/ingest.sh +231 -0
- package/runtime/codex/bin/ingest_graph.py +776 -0
- package/runtime/codex/bin/ingest_surface.py +464 -0
- package/runtime/codex/bin/lib/git-sync.sh +2 -6
- package/runtime/codex/bin/lib/greeting.sh +157 -22
- package/runtime/codex/bin/lib/identity.sh +50 -29
- package/runtime/codex/bin/lib/noncode.sh +31 -0
- package/runtime/codex/bin/person.py +610 -0
- package/runtime/codex/bin/person.sh +448 -0
- package/runtime/codex/bin/session-autosave.sh +184 -0
- package/runtime/codex/bin/session-log.sh +199 -0
- package/runtime/codex/bin/settings.sh +1 -1
- package/runtime/codex/manifest.json +19 -1
- package/runtime/pi/.codex/hooks/branch-guard.js +242 -34
- package/runtime/pi/.codex/skills/activity/SKILL.md +18 -6
- package/runtime/pi/.codex/skills/deep-reflect/SKILL.md +90 -55
- package/runtime/pi/.codex/skills/deploy-bridge/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/handoff/SKILL.md +6 -2
- package/runtime/pi/.codex/skills/save/SKILL.md +29 -5
- package/runtime/pi/.codex/skills/scroll/SKILL.md +54 -3
- package/runtime/pi/.codex/skills/task/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/tui-design/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/wrap/SKILL.md +4 -1
- package/runtime/pi/.codex/spec-manifest.json +19 -7
- package/runtime/pi/.pi/APPEND_SYSTEM.md +57 -14
- package/runtime/pi/.pi/extensions/egregore.ts +98 -8
- package/runtime/pi/.pi/spec-manifest.json +2 -2
- package/runtime/pi/AGENTS.md +63 -19
- package/runtime/pi/bin/activity-data.sh +1 -1
- package/runtime/pi/bin/agent.sh +88 -25
- package/runtime/pi/bin/attendant.sh +525 -0
- package/runtime/pi/bin/capture-reconcile.sh +67 -0
- package/runtime/pi/bin/capture-run.sh +341 -0
- package/runtime/pi/bin/codex-render-spec.mjs +29 -14
- package/runtime/pi/bin/codex-session-start.sh +49 -4
- package/runtime/pi/bin/codex-skill-render.mjs +23 -4
- package/runtime/pi/bin/graph-op.sh +719 -49
- package/runtime/pi/bin/graph-wal.sh +3 -2
- package/runtime/pi/bin/handoff-run.sh +72 -0
- package/runtime/pi/bin/handoff-save-egregore.sh +51 -11
- package/runtime/pi/bin/index-handoff.sh +26 -17
- package/runtime/pi/bin/ingest-graph.sh +80 -0
- package/runtime/pi/bin/ingest-surface.html +429 -0
- package/runtime/pi/bin/ingest.py +614 -0
- package/runtime/pi/bin/ingest.sh +231 -0
- package/runtime/pi/bin/ingest_graph.py +776 -0
- package/runtime/pi/bin/ingest_surface.py +464 -0
- package/runtime/pi/bin/lib/git-sync.sh +2 -6
- package/runtime/pi/bin/lib/greeting.sh +157 -22
- package/runtime/pi/bin/lib/identity.sh +50 -29
- package/runtime/pi/bin/lib/noncode.sh +31 -0
- package/runtime/pi/bin/person.py +610 -0
- package/runtime/pi/bin/person.sh +448 -0
- package/runtime/pi/bin/pi-branch-policy.mjs +164 -4
- package/runtime/pi/bin/pi-product-workflows.mjs +31 -2
- package/runtime/pi/bin/publish-artifact.sh +14 -0
- package/runtime/pi/bin/session-autosave.sh +184 -0
- package/runtime/pi/bin/session-log.sh +199 -0
- package/runtime/pi/bin/settings.sh +274 -0
- 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
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
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
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
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(
|
|
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
|
-
|
|
363
|
+
const visible = stripQuotedSegments(command);
|
|
364
|
+
if (isBranchSetupCommand(visible)) return false;
|
|
172
365
|
|
|
173
366
|
const mutatingPatterns = [
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
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
|
-
|
|
182
|
-
|
|
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
|
|
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}).
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
`
|
|
218
|
-
"
|
|
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.
|
|
19
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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:
|
|
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.
|
|
9
|
-
|
|
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
|
-
|
|
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
|
-
|
|
14
|
-
- `focused ...`: focused topic mode.
|
|
15
|
-
- `quick ...` or `category: content`: quick mode.
|
|
17
|
+
## Modes
|
|
16
18
|
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
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
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
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
|
-
-
|
|
62
|
-
- Header:
|
|
63
|
-
- Body:
|
|
64
|
-
|
|
65
|
-
- Footer: saved/pushed state
|
|
66
|
-
|
|
67
|
-
-
|
|
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
|
-
-
|
|
72
|
-
|
|
73
|
-
-
|
|
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,
|
|
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/
|
|
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
|