create-egregore 0.19.33 → 0.20.1
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 +141 -14
- package/bin/cli.js +19 -3
- package/lib/connect-runtime.js +4 -2
- package/lib/local.js +15 -1
- package/lib/prime-runtime.js +208 -0
- package/lib/setup.js +66 -5
- package/package.json +4 -3
- package/runtime/codex/.codex/config.toml +3 -0
- package/runtime/codex/.codex/skills/archive/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/branch/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/checkup/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/commit/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/contribute/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/delete-user/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/ingest/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/ingest-corpus/SKILL.md +29 -0
- package/runtime/codex/.codex/skills/ingest-notion/SKILL.md +29 -0
- package/runtime/codex/.codex/skills/ingest-user-interview/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/issue/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/me/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/meeting/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/note/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/notion-connect/SKILL.md +29 -0
- package/runtime/codex/.codex/skills/onboarding/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/pr/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/pull/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/push/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/reflect/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/review-pr/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/save/SKILL.md +3 -1
- package/runtime/codex/.codex/skills/slack-connect/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/summon/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/sync-repos/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/teams-connect/SKILL.md +29 -0
- package/runtime/codex/.codex/skills/telegram-connect/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/telemetry/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/test/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/tutorial/SKILL.md +1 -1
- package/runtime/codex/.codex/skills/update/SKILL.md +1 -1
- package/runtime/codex/.codex/spec-manifest.json +5 -5
- package/runtime/codex/AGENTS.md +41 -35
- package/runtime/codex/bin/activity-data.sh +4 -8
- package/runtime/codex/bin/agent.sh +17 -5
- package/runtime/codex/bin/attendant.sh +13 -0
- package/runtime/codex/bin/autosave.sh +27 -1
- package/runtime/codex/bin/codex-render-spec.mjs +33 -32
- package/runtime/codex/bin/codex-session-start.sh +3 -1
- package/runtime/codex/bin/codex-sync-skills.sh +6 -1
- package/runtime/codex/bin/graph-batch.sh +25 -6
- package/runtime/codex/bin/graph-op.sh +5 -4
- package/runtime/codex/bin/graph.sh +31 -5
- package/runtime/codex/bin/handoff-pr-backfill.sh +1 -1
- package/runtime/codex/bin/handoff-run.sh +8 -3
- package/runtime/codex/bin/handoff-save-egregore.sh +19 -7
- package/runtime/codex/bin/ingest.py +341 -10
- package/runtime/codex/bin/lib/context.sh +26 -1
- package/runtime/codex/bin/lib/git-message.sh +37 -0
- package/runtime/codex/bin/lib/git-sync.sh +10 -2
- package/runtime/codex/bin/lib/greeting.sh +4 -1
- package/runtime/codex/bin/lib/worktree-links.sh +25 -0
- package/runtime/codex/bin/session-autosave.sh +7 -1
- package/runtime/codex/bin/settings.sh +1 -1
- package/runtime/codex/manifest.json +10 -1
- package/runtime/pi/.codex/config.toml +3 -0
- package/runtime/pi/.codex/skills/archive/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/branch/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/checkup/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/commit/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/contribute/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/delete-user/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/ingest/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/ingest-corpus/SKILL.md +29 -0
- package/runtime/pi/.codex/skills/ingest-notion/SKILL.md +29 -0
- package/runtime/pi/.codex/skills/ingest-user-interview/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/issue/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/me/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/meeting/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/note/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/notion-connect/SKILL.md +29 -0
- package/runtime/pi/.codex/skills/onboarding/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/pr/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/pull/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/push/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/reflect/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/review-pr/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/save/SKILL.md +3 -1
- package/runtime/pi/.codex/skills/slack-connect/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/summon/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/sync-repos/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/teams-connect/SKILL.md +29 -0
- package/runtime/pi/.codex/skills/telegram-connect/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/telemetry/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/test/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/tutorial/SKILL.md +1 -1
- package/runtime/pi/.codex/skills/update/SKILL.md +1 -1
- package/runtime/pi/.codex/spec-manifest.json +5 -5
- package/runtime/pi/.pi/APPEND_SYSTEM.md +57 -33
- package/runtime/pi/.pi/extensions/egregore.ts +17 -6
- package/runtime/pi/.pi/spec-manifest.json +2 -2
- package/runtime/pi/AGENTS.md +41 -35
- package/runtime/pi/bin/activity-data.sh +4 -8
- package/runtime/pi/bin/agent.sh +17 -5
- package/runtime/pi/bin/attendant.sh +13 -0
- package/runtime/pi/bin/autosave.sh +27 -1
- package/runtime/pi/bin/codex-render-spec.mjs +33 -32
- package/runtime/pi/bin/codex-session-start.sh +3 -1
- package/runtime/pi/bin/codex-sync-skills.sh +6 -1
- package/runtime/pi/bin/graph-batch.sh +25 -6
- package/runtime/pi/bin/graph-op.sh +5 -4
- package/runtime/pi/bin/graph.sh +31 -5
- package/runtime/pi/bin/handoff-pr-backfill.sh +1 -1
- package/runtime/pi/bin/handoff-run.sh +8 -3
- package/runtime/pi/bin/handoff-save-egregore.sh +19 -7
- package/runtime/pi/bin/ingest.py +341 -10
- package/runtime/pi/bin/lib/context.sh +26 -1
- package/runtime/pi/bin/lib/git-message.sh +37 -0
- package/runtime/pi/bin/lib/git-sync.sh +10 -2
- package/runtime/pi/bin/lib/greeting.sh +4 -1
- package/runtime/pi/bin/lib/worktree-links.sh +25 -0
- package/runtime/pi/bin/pi-branch-policy.mjs +138 -1
- package/runtime/pi/bin/pi-product-workflows.mjs +35 -7
- package/runtime/pi/bin/pi-render-spec.mjs +52 -26
- package/runtime/pi/bin/session-autosave.sh +7 -1
- package/runtime/pi/bin/settings.sh +1 -1
- package/runtime/pi/manifest.json +10 -1
- package/runtime/prime/.claude/context/notification-consent.md +66 -0
- package/runtime/prime/.codex/config.toml +20 -0
- package/runtime/prime/.codex/hooks/branch-guard.js +461 -0
- package/runtime/prime/.codex/hooks/search-hint.js +67 -0
- package/runtime/prime/.codex/hooks.json +30 -0
- package/runtime/prime/.codex/skills/activity/SKILL.md +82 -0
- package/runtime/prime/.codex/skills/announce/SKILL.md +67 -0
- package/runtime/prime/.codex/skills/archive/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/branch/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/checkup/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/commit/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/contribute/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/create-skill/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/dark-mode/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/dashboard/SKILL.md +59 -0
- package/runtime/prime/.codex/skills/deep-reflect/SKILL.md +109 -0
- package/runtime/prime/.codex/skills/delete-user/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/emissary/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/handoff/SKILL.md +166 -0
- package/runtime/prime/.codex/skills/harvest/QUESTION_PALETTE.md +76 -0
- package/runtime/prime/.codex/skills/harvest/SKILL.md +131 -0
- package/runtime/prime/.codex/skills/ingest/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/ingest-corpus/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/ingest-notion/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/ingest-user-interview/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/invite/SKILL.md +44 -0
- package/runtime/prime/.codex/skills/issue/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/me/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/meeting/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/note/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/notion-connect/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/onboarding/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/pr/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/pull/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/push/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/quest/SKILL.md +80 -0
- package/runtime/prime/.codex/skills/reflect/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/review-pr/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/save/SKILL.md +127 -0
- package/runtime/prime/.codex/skills/slack-connect/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/summon/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/sync-repos/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/teams-connect/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/telegram-connect/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/telemetry/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/test/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/the-spiral/SKILL.md +84 -0
- package/runtime/prime/.codex/skills/tui-design/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/tutorial/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/update/SKILL.md +29 -0
- package/runtime/prime/.codex/skills/view/SKILL.md +156 -0
- package/runtime/prime/.codex/skills/view/agents/openai.yaml +4 -0
- package/runtime/prime/.codex/skills/view/assets/compose-scaffold.html +221 -0
- package/runtime/prime/.codex/skills/visual-explain/SKILL.md +45 -0
- package/runtime/prime/.codex/skills/wrap/SKILL.md +58 -0
- package/runtime/prime/.codex/spec-manifest.json +132 -0
- package/runtime/prime/.prime/agent/APPEND_SYSTEM.md +416 -0
- package/runtime/prime/.prime/agent/extensions/egregore.ts +460 -0
- package/runtime/prime/.prime/agent/settings.json +4 -0
- package/runtime/prime/.prime/agent/skills/egregore/SKILL.md +34 -0
- package/runtime/prime/.prime/agent/skills/egregore/pyproject.toml +13 -0
- package/runtime/prime/.prime/agent/skills/egregore/src/egregore/__init__.py +156 -0
- package/runtime/prime/.prime/agent/spec-manifest.json +12 -0
- package/runtime/prime/AGENTS.md +488 -0
- package/runtime/prime/bin/activity-data.sh +238 -0
- package/runtime/prime/bin/agent.sh +787 -0
- package/runtime/prime/bin/attendant.sh +707 -0
- package/runtime/prime/bin/autosave.sh +112 -0
- package/runtime/prime/bin/capture-reconcile.sh +69 -0
- package/runtime/prime/bin/capture-run.sh +341 -0
- package/runtime/prime/bin/codex-install-skills.sh +78 -0
- package/runtime/prime/bin/codex-render-spec.mjs +435 -0
- package/runtime/prime/bin/codex-session-start.sh +265 -0
- package/runtime/prime/bin/codex-skill-render.mjs +347 -0
- package/runtime/prime/bin/codex-sync-skills.sh +237 -0
- package/runtime/prime/bin/connect-refresh.sh +33 -0
- package/runtime/prime/bin/dashboard-data.sh +178 -0
- package/runtime/prime/bin/graph-batch.sh +79 -0
- package/runtime/prime/bin/graph-op.sh +1102 -0
- package/runtime/prime/bin/graph-wal.sh +229 -0
- package/runtime/prime/bin/graph.sh +223 -0
- package/runtime/prime/bin/handoff-pr-backfill.sh +86 -0
- package/runtime/prime/bin/handoff-run.sh +676 -0
- package/runtime/prime/bin/handoff-save-egregore.sh +224 -0
- package/runtime/prime/bin/index-handoff.sh +322 -0
- package/runtime/prime/bin/ingest-graph.sh +80 -0
- package/runtime/prime/bin/ingest-surface.html +495 -0
- package/runtime/prime/bin/ingest.py +1011 -0
- package/runtime/prime/bin/ingest.sh +279 -0
- package/runtime/prime/bin/ingest_graph.py +856 -0
- package/runtime/prime/bin/ingest_surface.py +544 -0
- package/runtime/prime/bin/lib/artifact-id.sh +46 -0
- package/runtime/prime/bin/lib/config.sh +116 -0
- package/runtime/prime/bin/lib/context.sh +621 -0
- package/runtime/prime/bin/lib/dashboard-artifact.sh +242 -0
- package/runtime/prime/bin/lib/ensure-develop.sh +183 -0
- package/runtime/prime/bin/lib/git-message.sh +37 -0
- package/runtime/prime/bin/lib/git-sync.sh +414 -0
- package/runtime/prime/bin/lib/greeting.sh +519 -0
- package/runtime/prime/bin/lib/hash.sh +13 -0
- package/runtime/prime/bin/lib/identity.sh +213 -0
- package/runtime/prime/bin/lib/metrics.sh +374 -0
- package/runtime/prime/bin/lib/noncode.sh +31 -0
- package/runtime/prime/bin/lib/permissions.sh +106 -0
- package/runtime/prime/bin/lib/time.sh +56 -0
- package/runtime/prime/bin/lib/worktree-links.sh +79 -0
- package/runtime/prime/bin/notify.sh +636 -0
- package/runtime/prime/bin/person.py +630 -0
- package/runtime/prime/bin/person.sh +448 -0
- package/runtime/prime/bin/pi-branch-policy.mjs +312 -0
- package/runtime/prime/bin/pi-product-workflows.mjs +394 -0
- package/runtime/prime/bin/prime-render-spec.mjs +147 -0
- package/runtime/prime/bin/prime-session-start.sh +24 -0
- package/runtime/prime/bin/publish-artifact.sh +271 -0
- package/runtime/prime/bin/publish-references.sh +144 -0
- package/runtime/prime/bin/repo-state.sh +96 -0
- package/runtime/prime/bin/search.sh +240 -0
- package/runtime/prime/bin/session-autosave.sh +215 -0
- package/runtime/prime/bin/session-log.sh +199 -0
- package/runtime/prime/bin/settings.sh +304 -0
- package/runtime/prime/bin/telemetry.sh +391 -0
- package/runtime/prime/bin/worktree-create.sh +128 -0
- package/runtime/prime/bin/worktree-remove.sh +50 -0
- package/runtime/prime/bin/worktree.sh +198 -0
- package/runtime/prime/manifest.json +181 -0
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { isAbsolute, relative, resolve } from "node:path";
|
|
2
2
|
|
|
3
3
|
const MUTATING_COMMANDS = /(^|[;&|]\s*)(?:sudo\s+)?(rm|mv|cp|mkdir|rmdir|touch|chmod|chown|install|tee)\b/;
|
|
4
|
-
|
|
4
|
+
// Global options (-C <path>, -c k=v, --git-dir=…) may sit between `git` and
|
|
5
|
+
// the subcommand. The repo convention actively teaches `git -C <abs-path>`,
|
|
6
|
+
// so a classifier that only matches bare `git commit` misses the shape the
|
|
7
|
+
// model is most likely to emit.
|
|
8
|
+
const MUTATING_GIT = /(^|[;&|]\s*)(?:sudo\s+)?git\s+(?:(?:-C|-c|--git-dir|--work-tree)(?:=\S+|\s+\S+)\s+)*(add|commit|push|merge|rebase|cherry-pick|tag|rm|mv|reset|restore|clean)\b/;
|
|
5
9
|
const MUTATING_PACKAGES = /(^|[;&|]\s*)(?:sudo\s+)?(npm|pnpm|yarn|bun)\s+(install|add|remove|uninstall|update)\b/;
|
|
6
10
|
|
|
7
11
|
// A `>` only acts as a shell redirect when it sits OUTSIDE quotes. Quoted
|
|
@@ -112,6 +116,7 @@ export function isRuntimeStatePath(target, projectDir) {
|
|
|
112
116
|
rel === ".claude" || rel.startsWith(".claude/") ||
|
|
113
117
|
rel === ".codex" || rel.startsWith(".codex/") ||
|
|
114
118
|
rel === ".pi" || rel.startsWith(".pi/") ||
|
|
119
|
+
rel === ".prime" || rel.startsWith(".prime/") ||
|
|
115
120
|
rel === ".egregore-state.json" ||
|
|
116
121
|
rel === ".egregore-branch-consent" ||
|
|
117
122
|
rel === ".env";
|
|
@@ -162,6 +167,20 @@ export function mutatesOnlyRuntimeState(command, projectDir) {
|
|
|
162
167
|
return foundTarget;
|
|
163
168
|
}
|
|
164
169
|
|
|
170
|
+
// The documented escape hatch must itself be runnable, but ONLY as the whole
|
|
171
|
+
// command: an unanchored match lets `git push origin develop && echo 'develop'
|
|
172
|
+
// > .egregore-branch-consent` pass the gate in one call, and the block message
|
|
173
|
+
// teaches that exact echo string. Canonical form mirrors
|
|
174
|
+
// .codex/hooks/branch-guard.js isConsentCommand — anchored, branch escaped,
|
|
175
|
+
// optional ./ prefix — and is shared so the three runtimes cannot drift.
|
|
176
|
+
export function isConsentCommand(command, branch) {
|
|
177
|
+
if (!branch) return false;
|
|
178
|
+
const escaped = String(branch).replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
|
|
179
|
+
return new RegExp(
|
|
180
|
+
`^\\s*echo\\s+['"]?${escaped}['"]?\\s*>\\s*(?:\\./)?\\.egregore-branch-consent\\s*$`
|
|
181
|
+
).test(String(command || ""));
|
|
182
|
+
}
|
|
183
|
+
|
|
165
184
|
export function isMutatingBash(command) {
|
|
166
185
|
if (!command) return false;
|
|
167
186
|
const meaningfulRedirects = stripQuotedSegments(String(command))
|
|
@@ -173,3 +192,121 @@ export function isMutatingBash(command) {
|
|
|
173
192
|
/(^|[^<])>>?\s*[^&]/.test(meaningfulRedirects) ||
|
|
174
193
|
/\bsed\s+[^\n]*\s-i(?:\s|$)/.test(command);
|
|
175
194
|
}
|
|
195
|
+
|
|
196
|
+
// ---- IPython code classification (Prime Agent) -----------------------------
|
|
197
|
+
// Prime Agent's default tool surface is a persistent IPython kernel: file
|
|
198
|
+
// writes and shell escapes arrive as Python code, not bash tool calls. This
|
|
199
|
+
// classifier is deliberately static and therefore porous (exec/eval and
|
|
200
|
+
// string-built commands pass); the generated runtime spec carries the
|
|
201
|
+
// standing-instruction discipline as the backstop, and the adversarial test
|
|
202
|
+
// fixtures pin what this actually catches.
|
|
203
|
+
|
|
204
|
+
// Strip Python string literals so call-name detection ignores docstrings and
|
|
205
|
+
// string content. Triple quotes first, then single-line quotes; f-string
|
|
206
|
+
// prefixes fall out naturally because only the quoted part is replaced.
|
|
207
|
+
function stripPythonStrings(code) {
|
|
208
|
+
return String(code)
|
|
209
|
+
.replace(/[rbuf]{0,2}("""|''')[\s\S]*?\1/gi, '""')
|
|
210
|
+
.replace(/[rbuf]{0,2}"(?:\\.|[^"\\\n])*"/gi, '""')
|
|
211
|
+
.replace(/[rbuf]{0,2}'(?:\\.|[^'\\\n])*'/gi, "''");
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function pythonShellCommands(code) {
|
|
215
|
+
const shell = [];
|
|
216
|
+
const lines = String(code).split("\n");
|
|
217
|
+
const first = (lines[0] || "").trim();
|
|
218
|
+
if (/^%%(bash|sh|script\s+(ba)?sh)\b/.test(first)) {
|
|
219
|
+
shell.push(lines.slice(1).join("\n"));
|
|
220
|
+
return { shell, rest: "" };
|
|
221
|
+
}
|
|
222
|
+
if (/^%%writefile\b/.test(first)) {
|
|
223
|
+
const target = first.replace(/^%%writefile\s+(-a\s+)?/, "").trim();
|
|
224
|
+
return { shell: [`tee ${target || "unknown-target"}`], rest: "" };
|
|
225
|
+
}
|
|
226
|
+
const rest = [];
|
|
227
|
+
for (const line of lines) {
|
|
228
|
+
const bang = line.match(/^\s*!(.+)$/);
|
|
229
|
+
const sysMagic = line.match(/^\s*%(?:system|sx)\s+(.+)$/);
|
|
230
|
+
if (bang) shell.push(bang[1]);
|
|
231
|
+
else if (sysMagic) shell.push(sysMagic[1]);
|
|
232
|
+
else rest.push(line);
|
|
233
|
+
}
|
|
234
|
+
return { shell, rest: rest.join("\n") };
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const PY_MUTATING_CALLS =
|
|
238
|
+
/\b(?:os\.(?:remove|unlink|rename|replace|rmdir|removedirs|makedirs|mkdir|symlink|link|chmod|chown|truncate)|shutil\.(?:copy|copy2|copyfile|copytree|move|rmtree|make_archive|unpack_archive))\s*\(/;
|
|
239
|
+
const PY_WRITE_METHODS = /\.\s*(?:write_text|write_bytes|unlink|touch|mkdir|rmdir|hardlink_to|symlink_to)\s*\(/;
|
|
240
|
+
const PY_OPEN_WRITE =
|
|
241
|
+
/\bopen\s*\(\s*[^,]+,\s*(?:mode\s*=\s*)?['"](?:[wax]|r\+)[rwxab+]*['"]/;
|
|
242
|
+
const PY_OPEN_MODE_KWARG = /\bopen\s*\([^)]*mode\s*=\s*['"](?:[wax]|r\+)[rwxab+]*['"]/;
|
|
243
|
+
|
|
244
|
+
function pythonSubprocessArgv(code) {
|
|
245
|
+
const argvs = [];
|
|
246
|
+
const calls =
|
|
247
|
+
/\b(?:subprocess\.(?:run|call|check_call|check_output|Popen)|os\.system|os\.popen)\s*\(\s*(\[[^\]]*\]|[rbuf]{0,2}["'](?:\\.|[^"'\\\n])*["'])/g;
|
|
248
|
+
let match;
|
|
249
|
+
while ((match = calls.exec(String(code)))) {
|
|
250
|
+
const arg = match[1];
|
|
251
|
+
if (arg.startsWith("[")) {
|
|
252
|
+
const words = [...arg.matchAll(/["']((?:\\.|[^"'\\])*)["']/g)].map((m) => m[1]);
|
|
253
|
+
if (words.length) argvs.push(words.join(" "));
|
|
254
|
+
} else {
|
|
255
|
+
argvs.push(arg.replace(/^[rbuf]{0,2}["']/i, "").replace(/["']$/, ""));
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
return argvs;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Classify IPython cell code. Returns true when the code statically performs a
|
|
262
|
+
// repo mutation that should meet the protected-branch gate. Mutations that
|
|
263
|
+
// target only Egregore runtime state (memory/, dot-dirs, consent files) are
|
|
264
|
+
// exempt when the target is statically extractable, mirroring
|
|
265
|
+
// mutatesOnlyRuntimeState for bash.
|
|
266
|
+
export function isMutatingIpython(code, projectDir) {
|
|
267
|
+
if (!code) return false;
|
|
268
|
+
const { shell, rest } = pythonShellCommands(code);
|
|
269
|
+
for (const cmd of shell) {
|
|
270
|
+
if (isMutatingBash(cmd) && !(projectDir && mutatesOnlyRuntimeState(cmd, projectDir))) return true;
|
|
271
|
+
}
|
|
272
|
+
if (!rest) return false;
|
|
273
|
+
|
|
274
|
+
for (const argv of pythonSubprocessArgv(rest)) {
|
|
275
|
+
if (isMutatingBash(argv) && !(projectDir && mutatesOnlyRuntimeState(argv, projectDir))) return true;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
const stripped = stripPythonStrings(rest);
|
|
279
|
+
if (PY_MUTATING_CALLS.test(stripped) || PY_WRITE_METHODS.test(stripped)) {
|
|
280
|
+
// Exempt when every extractable string target is runtime state. The
|
|
281
|
+
// mutated location differs by call: copy/move/rename mutate their LAST
|
|
282
|
+
// argument (the destination); remove/rmtree/mkdir mutate their first.
|
|
283
|
+
if (projectDir) {
|
|
284
|
+
const DEST_LAST = /^(?:copy|copy2|copyfile|copytree|move|rename|replace|symlink|link)$/;
|
|
285
|
+
const targets = [...String(rest).matchAll(
|
|
286
|
+
/\b(?:os|shutil)\.(\w+)\s*\(([^)]*)\)/g
|
|
287
|
+
)].flatMap((m) => {
|
|
288
|
+
const args = [...m[2].matchAll(/["']((?:\\.|[^"'\\])*)["']/g)].map((s) => s[1]);
|
|
289
|
+
if (!args.length) return [];
|
|
290
|
+
return [DEST_LAST.test(m[1]) ? args[args.length - 1] : args[0]];
|
|
291
|
+
});
|
|
292
|
+
const pathTargets = [...String(rest).matchAll(
|
|
293
|
+
/Path\s*\(\s*[rbuf]{0,2}["']((?:\\.|[^"'\\\n])*)["']\s*\)\s*\.\s*(?:write_text|write_bytes|unlink|touch|mkdir|rmdir)/g
|
|
294
|
+
)].map((m) => m[1]);
|
|
295
|
+
const all = [...targets, ...pathTargets];
|
|
296
|
+
if (all.length && all.every((t) => isRuntimeStatePath(t, projectDir))) return false;
|
|
297
|
+
}
|
|
298
|
+
return true;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (PY_OPEN_WRITE.test(rest) || PY_OPEN_MODE_KWARG.test(rest)) {
|
|
302
|
+
if (projectDir) {
|
|
303
|
+
const openTargets = [...String(rest).matchAll(
|
|
304
|
+
/\bopen\s*\(\s*[rbuf]{0,2}["']((?:\\.|[^"'\\\n])*)["']\s*,\s*(?:mode\s*=\s*)?["'](?:[wax]|r\+)/g
|
|
305
|
+
)].map((m) => m[1]);
|
|
306
|
+
if (openTargets.length && openTargets.every((t) => isRuntimeStatePath(t, projectDir))) return false;
|
|
307
|
+
}
|
|
308
|
+
return true;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
return false;
|
|
312
|
+
}
|
|
@@ -2,6 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
import {
|
|
4
4
|
existsSync,
|
|
5
|
+
mkdirSync,
|
|
5
6
|
mkdtempSync,
|
|
6
7
|
readFileSync,
|
|
7
8
|
readdirSync,
|
|
@@ -141,12 +142,37 @@ export async function renderView(pi, root, request, signal) {
|
|
|
141
142
|
return { text, file, type: request.type };
|
|
142
143
|
}
|
|
143
144
|
|
|
144
|
-
export async function renderGreeting(pi, root, signal) {
|
|
145
|
-
const script = join(root, "bin",
|
|
146
|
-
if (!existsSync(script)) throw new Error("
|
|
145
|
+
export async function renderGreeting(pi, root, signal, startScript = "pi-session-start.sh") {
|
|
146
|
+
const script = join(root, "bin", startScript);
|
|
147
|
+
if (!existsSync(script)) throw new Error("Egregore startup renderer is missing");
|
|
147
148
|
return checkedExec(pi, "bash", [script, "--card"], { signal, timeout: 45000 }, "Egregore startup");
|
|
148
149
|
}
|
|
149
150
|
|
|
151
|
+
// Raw JSON from the most recent activity-data.sh run, so follow-up focus
|
|
152
|
+
// turns can receive the data inline instead of re-running the script inside
|
|
153
|
+
// a harness surface that may cap command time (Prime's kernel kills execs at
|
|
154
|
+
// ~5s, truncating the 10-20s data script to a partial fragment).
|
|
155
|
+
let lastActivityData = "";
|
|
156
|
+
export function lastActivityJson() {
|
|
157
|
+
return lastActivityData;
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
// Stable on-disk copy of the same data. Focus turns point the model at this
|
|
161
|
+
// path instead of an inline excerpt: a file read is one fast kernel call,
|
|
162
|
+
// never truncated, and removes any reason to re-run the data script.
|
|
163
|
+
export function activityDataPath(root) {
|
|
164
|
+
return join(root, ".egregore", "activity-data.json");
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function persistActivityData(root, json) {
|
|
168
|
+
try {
|
|
169
|
+
mkdirSync(join(root, ".egregore"), { recursive: true });
|
|
170
|
+
writeFileSync(activityDataPath(root), json);
|
|
171
|
+
} catch {
|
|
172
|
+
// Cache only — rendering proceeds from memory when the disk write fails.
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
|
|
150
176
|
export async function renderActivity(pi, root, signal, options = {}) {
|
|
151
177
|
if (options.sync !== false) {
|
|
152
178
|
await checkedExec(
|
|
@@ -177,6 +203,8 @@ export async function renderActivity(pi, root, signal, options = {}) {
|
|
|
177
203
|
{ signal, timeout: 120000 },
|
|
178
204
|
"Activity refresh",
|
|
179
205
|
);
|
|
206
|
+
lastActivityData = String(json || "");
|
|
207
|
+
persistActivityData(root, lastActivityData);
|
|
180
208
|
const temp = mkdtempSync(join(tmpdir(), "egregore-pi-activity-"));
|
|
181
209
|
const input = join(temp, "activity.json");
|
|
182
210
|
try {
|
|
@@ -276,7 +304,7 @@ export async function createHandoff(pi, root, params, signal) {
|
|
|
276
304
|
}
|
|
277
305
|
}
|
|
278
306
|
|
|
279
|
-
export async function captureSessionEnd(pi, root, sessionFile, signal) {
|
|
307
|
+
export async function captureSessionEnd(pi, root, sessionFile, signal, runtime = "pi") {
|
|
280
308
|
const sessionIdFile = join(root, ".egregore-session-id");
|
|
281
309
|
if (!existsSync(sessionIdFile)) return false;
|
|
282
310
|
const sessionId = cleanArgs(readFileSync(sessionIdFile, "utf8"));
|
|
@@ -287,7 +315,7 @@ export async function captureSessionEnd(pi, root, sessionFile, signal) {
|
|
|
287
315
|
writeFileSync(input, JSON.stringify({
|
|
288
316
|
session_id: sessionId,
|
|
289
317
|
transcript_path: cleanArgs(sessionFile),
|
|
290
|
-
runtime
|
|
318
|
+
runtime,
|
|
291
319
|
}));
|
|
292
320
|
|
|
293
321
|
try {
|
|
@@ -353,11 +381,11 @@ export async function publishScroll(pi, root, params, signal) {
|
|
|
353
381
|
};
|
|
354
382
|
}
|
|
355
383
|
|
|
356
|
-
export function workflowPrompt(name, args, extra = "") {
|
|
384
|
+
export function workflowPrompt(name, args, extra = "", spec = { runtimeName: "Pi", specPath: ".pi/APPEND_SYSTEM.md" }) {
|
|
357
385
|
const userArgs = cleanArgs(args);
|
|
358
386
|
return [
|
|
359
387
|
`Run the Egregore /${name} workflow now.`,
|
|
360
|
-
`Read .codex/skills/${name}/SKILL.md completely and adapt Codex-specific wording to
|
|
388
|
+
`Read .codex/skills/${name}/SKILL.md completely and adapt Codex-specific wording to ${spec.runtimeName} using ${spec.specPath}.`,
|
|
361
389
|
"Work quietly: do not narrate progress, expose raw command output, or repeat tool results.",
|
|
362
390
|
"Use the fewest targeted reads and commands that preserve the workflow contract.",
|
|
363
391
|
userArgs ? `User arguments: ${userArgs}` : "",
|
|
@@ -40,24 +40,57 @@ function replaceSection(markdown, heading, body) {
|
|
|
40
40
|
return `${markdown.slice(0, start)}${marker}\n\n${body}${markdown.slice(end)}`;
|
|
41
41
|
}
|
|
42
42
|
|
|
43
|
+
// Keep an inherited section and put runtime-specific framing in front of it.
|
|
44
|
+
// Command Awareness is the case that matters: the canonical category rows and
|
|
45
|
+
// disambiguation map live in CLAUDE.md, and a full replace here silently
|
|
46
|
+
// shipped a truncated copy that drifted for months. The Codex spec opens the
|
|
47
|
+
// section with its own skills-not-slash-commands paragraph, which is wrong for
|
|
48
|
+
// a runtime that does have slash commands, so that one paragraph is dropped.
|
|
49
|
+
function prependToSection(markdown, heading, body, dropLeadParagraphStartingWith) {
|
|
50
|
+
const marker = `## ${heading}`;
|
|
51
|
+
const start = markdown.indexOf(marker);
|
|
52
|
+
if (start < 0) throw new Error(`missing section in AGENTS.md: ${heading}`);
|
|
53
|
+
const rest = markdown.slice(start + marker.length);
|
|
54
|
+
const boundary = rest.search(/\n\n---\n\n## /);
|
|
55
|
+
const end = boundary < 0 ? markdown.length : start + marker.length + boundary;
|
|
56
|
+
let inherited = markdown.slice(start + marker.length, end).trim();
|
|
57
|
+
if (dropLeadParagraphStartingWith) {
|
|
58
|
+
if (!inherited.startsWith(dropLeadParagraphStartingWith)) {
|
|
59
|
+
throw new Error(`pi/prime-render-spec: "${heading}" no longer opens with "${dropLeadParagraphStartingWith}" — the Codex wording moved; update this adaptation`);
|
|
60
|
+
}
|
|
61
|
+
inherited = inherited.slice(inherited.indexOf("\n\n") + 2).trim();
|
|
62
|
+
}
|
|
63
|
+
return replaceSection(markdown, heading, `${body}\n\n${inherited}`);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const CODEX_COMMAND_LEAD = "Codex reserves leading";
|
|
67
|
+
|
|
43
68
|
function adapt(sourceSpec) {
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
.
|
|
56
|
-
.
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
69
|
+
// Anchored replacements: every `from` string MUST exist in the generated
|
|
70
|
+
// Codex spec. A missing anchor once no-opped silently (the tool_call gate
|
|
71
|
+
// wording vanished from this file for a whole PR cycle before a test caught
|
|
72
|
+
// it) — now it fails the render loudly so the anchor gets updated alongside
|
|
73
|
+
// the Codex text that moved.
|
|
74
|
+
const ANCHORED = [
|
|
75
|
+
["Egregore on Codex", "Egregore on Pi"],
|
|
76
|
+
["Codex-native", "Pi-native"],
|
|
77
|
+
["Codex sessions", "Pi sessions"],
|
|
78
|
+
["On Codex", "On Pi"],
|
|
79
|
+
["Codex has no", "Pi has no built-in"],
|
|
80
|
+
["bin/codex-session-start.sh", "bin/pi-session-start.sh"],
|
|
81
|
+
[".codex/hooks/branch-guard.js", ".pi/extensions/egregore.ts"],
|
|
82
|
+
["PreToolUse hook (launcher `--enable hooks`)", "`tool_call` gate (loaded after Pi project trust)"],
|
|
83
|
+
["If this Codex build does not support hooks", "If Pi project resources are disabled"],
|
|
84
|
+
["change permissions in `.claude/settings.json` anytime", "review or disable project resources in `.pi/` anytime"],
|
|
85
|
+
];
|
|
86
|
+
let text = sourceSpec;
|
|
87
|
+
for (const [from, to] of ANCHORED) {
|
|
88
|
+
if (!text.includes(from)) {
|
|
89
|
+
throw new Error(`pi-render-spec: anchor not found in generated Codex spec: "${from}" — the Codex wording moved; update this replacement`);
|
|
90
|
+
}
|
|
91
|
+
text = text.replaceAll(from, to);
|
|
92
|
+
}
|
|
93
|
+
text = text
|
|
61
94
|
.replace(/`\$([a-z][a-z0-9-]*)/g, "`/$1")
|
|
62
95
|
.replace(/\$([a-z][a-z0-9-]*)/g, "/$1");
|
|
63
96
|
|
|
@@ -67,16 +100,9 @@ This appendix is generated from the reviewed shell-runtime translation of \`CLAU
|
|
|
67
100
|
|
|
68
101
|
text = replaceSection(text, "On Launch — MANDATORY FIRST ACTION", `The project-local Pi extension renders the Egregore startup card (identity, handoffs, team activity) inside Pi via \`bin/pi-session-start.sh\`. This works for direct \`pi\` launches and launcher-managed sessions. Do not rerun startup checks and do not narrate startup. The card ends with **"What are you working on?"** — that question is already on screen; treat the user's first message as the answer to it. To re-show the card outside Pi, run \`bash bin/pi-session-start.sh --card\`.`);
|
|
69
102
|
|
|
70
|
-
text =
|
|
71
|
-
|
|
72
|
-
Invoke commands from user intent — don't wait for the slash. Read the selected skill's \`SKILL.md\` completely before acting. Codex wording inside the shared portable skills means the shell-capable runtime adapter; in Pi, run referenced \`bin/\` scripts directly, use ordinary Pi shell/network access, ignore Codex approval-channel wording, and render compact numbered questions when no Pi UI question surface is available.
|
|
103
|
+
text = prependToSection(text, "Command Awareness", `Egregore workflows are available as Pi slash commands such as \`/activity\`, \`/handoff\`, \`/save\`, and \`/view\`. They are registered from the shared skill inventory by \`.pi/extensions/egregore.ts\`. Pi also exposes the underlying Agent Skills as \`/skill:name\`, and natural-language requests work normally. \`/save\` is the user-facing abstraction for committing, pushing, opening or reusing pull requests, and syncing memory — never make users manage the git workflow by hand.
|
|
73
104
|
|
|
74
|
-
|
|
75
|
-
**Knowledge** — \`/search\` \`/deep-reflect\` \`/archive\` \`/note\` \`/add\` \`/meeting\` \`/ingest\` \`/scroll\`
|
|
76
|
-
**Identity** — \`/me\`
|
|
77
|
-
**Coordination** — \`/ask\` \`/quest\` \`/issue\` \`/invite\` \`/delete-user\` \`/announce\`
|
|
78
|
-
**Git** — \`/branch\` \`/commit\` \`/push\` \`/pr\` \`/save\` \`/review-pr\` \`/contribute\`
|
|
79
|
-
**Infra** — \`/setup\` \`/update\` \`/pull\` \`/env\` \`/infra\` \`/sync-repos\` \`/release\` \`/checkup\``);
|
|
105
|
+
Read the selected skill's \`SKILL.md\` completely before acting. Codex wording inside the shared portable skills means the shell-capable runtime adapter; in Pi, run referenced \`bin/\` scripts directly, use ordinary Pi shell/network access, ignore Codex approval-channel wording, and render compact numbered questions when no Pi UI question surface is available.`, CODEX_COMMAND_LEAD);
|
|
80
106
|
|
|
81
107
|
return `# Egregore on Pi\n\nThis file is generated by \`bin/pi-render-spec.mjs\`; do not edit it by hand.\n\n${text}\n`;
|
|
82
108
|
}
|
|
@@ -29,6 +29,12 @@
|
|
|
29
29
|
|
|
30
30
|
set -uo pipefail
|
|
31
31
|
|
|
32
|
+
# shellcheck source=bin/lib/git-message.sh
|
|
33
|
+
. "$(cd "$(dirname "$0")" && pwd)/lib/git-message.sh" 2>/dev/null || true
|
|
34
|
+
type egregore_commit >/dev/null 2>&1 || egregore_commit() {
|
|
35
|
+
local gd="$1" m="$3"; shift 3; git -C "$gd" commit -m "$m" "$@"
|
|
36
|
+
}
|
|
37
|
+
|
|
32
38
|
DIR=""
|
|
33
39
|
AUTHOR=""
|
|
34
40
|
DEBOUNCE=0
|
|
@@ -118,7 +124,7 @@ fi
|
|
|
118
124
|
if git -C "$DIR/memory" rev-parse --git-dir >/dev/null 2>&1; then
|
|
119
125
|
if [ -n "$(git -C "$DIR/memory" status --porcelain 2>/dev/null)" ]; then
|
|
120
126
|
git -C "$DIR/memory" add -A >/dev/null 2>&1 || true
|
|
121
|
-
|
|
127
|
+
egregore_commit "$DIR/memory" "$DIR" "chore(autosave): save session work" --quiet 2>/dev/null || true
|
|
122
128
|
fi
|
|
123
129
|
if [ -n "$(git -C "$DIR/memory" log origin/main..HEAD --oneline 2>/dev/null)" ]; then
|
|
124
130
|
for _try in 1 2 3; do
|
|
@@ -232,7 +232,7 @@ _people_file() {
|
|
|
232
232
|
rm -f "$f"
|
|
233
233
|
fi
|
|
234
234
|
( cd "$SCRIPT_DIR/memory" 2>/dev/null && git add -A people/ 2>/dev/null \
|
|
235
|
-
&& git commit -q -m "settings:
|
|
235
|
+
&& git commit -q -m "chore(settings): $op $user in people" 2>/dev/null \
|
|
236
236
|
&& git push -q 2>/dev/null ) || true
|
|
237
237
|
}
|
|
238
238
|
|
package/runtime/pi/manifest.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"source": "egregore-pi-meta-harness",
|
|
3
3
|
"policy": "Pi-native lifecycle layer over the available OSS Codex skill corpus",
|
|
4
|
-
"skillCount":
|
|
4
|
+
"skillCount": 47,
|
|
5
5
|
"skills": [
|
|
6
6
|
"activity",
|
|
7
7
|
"announce",
|
|
@@ -19,12 +19,15 @@
|
|
|
19
19
|
"handoff",
|
|
20
20
|
"harvest",
|
|
21
21
|
"ingest",
|
|
22
|
+
"ingest-corpus",
|
|
23
|
+
"ingest-notion",
|
|
22
24
|
"ingest-user-interview",
|
|
23
25
|
"invite",
|
|
24
26
|
"issue",
|
|
25
27
|
"me",
|
|
26
28
|
"meeting",
|
|
27
29
|
"note",
|
|
30
|
+
"notion-connect",
|
|
28
31
|
"onboarding",
|
|
29
32
|
"pr",
|
|
30
33
|
"pull",
|
|
@@ -36,6 +39,7 @@
|
|
|
36
39
|
"slack-connect",
|
|
37
40
|
"summon",
|
|
38
41
|
"sync-repos",
|
|
42
|
+
"teams-connect",
|
|
39
43
|
"telegram-connect",
|
|
40
44
|
"telemetry",
|
|
41
45
|
"test",
|
|
@@ -70,6 +74,8 @@
|
|
|
70
74
|
".codex/skills/handoff/SKILL.md",
|
|
71
75
|
".codex/skills/harvest/QUESTION_PALETTE.md",
|
|
72
76
|
".codex/skills/harvest/SKILL.md",
|
|
77
|
+
".codex/skills/ingest-corpus/SKILL.md",
|
|
78
|
+
".codex/skills/ingest-notion/SKILL.md",
|
|
73
79
|
".codex/skills/ingest-user-interview/SKILL.md",
|
|
74
80
|
".codex/skills/ingest/SKILL.md",
|
|
75
81
|
".codex/skills/invite/SKILL.md",
|
|
@@ -77,6 +83,7 @@
|
|
|
77
83
|
".codex/skills/me/SKILL.md",
|
|
78
84
|
".codex/skills/meeting/SKILL.md",
|
|
79
85
|
".codex/skills/note/SKILL.md",
|
|
86
|
+
".codex/skills/notion-connect/SKILL.md",
|
|
80
87
|
".codex/skills/onboarding/SKILL.md",
|
|
81
88
|
".codex/skills/pr/SKILL.md",
|
|
82
89
|
".codex/skills/pull/SKILL.md",
|
|
@@ -88,6 +95,7 @@
|
|
|
88
95
|
".codex/skills/slack-connect/SKILL.md",
|
|
89
96
|
".codex/skills/summon/SKILL.md",
|
|
90
97
|
".codex/skills/sync-repos/SKILL.md",
|
|
98
|
+
".codex/skills/teams-connect/SKILL.md",
|
|
91
99
|
".codex/skills/telegram-connect/SKILL.md",
|
|
92
100
|
".codex/skills/telemetry/SKILL.md",
|
|
93
101
|
".codex/skills/test/SKILL.md",
|
|
@@ -138,6 +146,7 @@
|
|
|
138
146
|
"bin/lib/context.sh",
|
|
139
147
|
"bin/lib/dashboard-artifact.sh",
|
|
140
148
|
"bin/lib/ensure-develop.sh",
|
|
149
|
+
"bin/lib/git-message.sh",
|
|
141
150
|
"bin/lib/git-sync.sh",
|
|
142
151
|
"bin/lib/greeting.sh",
|
|
143
152
|
"bin/lib/hash.sh",
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
# External notification consent
|
|
2
|
+
|
|
3
|
+
This protocol applies to every Telegram, Teams, or other external notification
|
|
4
|
+
in every harness. It applies even when the user asked to announce, notify,
|
|
5
|
+
handoff, invite, or message someone, and even when the harness is running with
|
|
6
|
+
broad tool permissions.
|
|
7
|
+
|
|
8
|
+
## Invariant
|
|
9
|
+
|
|
10
|
+
Never dispatch an external notification until the human has approved that one
|
|
11
|
+
exact delivery in a dedicated checkpoint. The checkpoint must show:
|
|
12
|
+
|
|
13
|
+
- the organization;
|
|
14
|
+
- the final recipient or group;
|
|
15
|
+
- every channel that will receive it;
|
|
16
|
+
- the exact final message, including links.
|
|
17
|
+
|
|
18
|
+
A workflow request, plan approval, batch approval, prior approval, broad
|
|
19
|
+
permission mode, or approval of a related action is not notification consent.
|
|
20
|
+
There is no standing "always allow" choice. One approval authorizes one attempt
|
|
21
|
+
to send one immutable message.
|
|
22
|
+
|
|
23
|
+
## Required flow
|
|
24
|
+
|
|
25
|
+
1. Finish the message first. Publish any artifact and insert its real URL
|
|
26
|
+
before planning; never change the message after preview.
|
|
27
|
+
2. Resolve the exact delivery without sending:
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
PLAN_JSON=$(bash bin/notify.sh plan send "$RECIPIENT" "$MESSAGE")
|
|
31
|
+
# or
|
|
32
|
+
PLAN_JSON=$(bash bin/notify.sh plan group "$MESSAGE")
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
3. Read `plan_id` and `digest` from `PLAN_JSON`. Show the organization,
|
|
36
|
+
recipient, all `channels`/`deliveries`, and exact `message` in a dedicated
|
|
37
|
+
Send / Edit / Cancel question. Use the harness's structured question UI
|
|
38
|
+
when available. Otherwise show the same fields in plain text and stop for
|
|
39
|
+
the user's answer.
|
|
40
|
+
4. Only if the user selects Send for that exact preview:
|
|
41
|
+
|
|
42
|
+
```bash
|
|
43
|
+
APPROVAL_JSON=$(bash bin/notify.sh approve \
|
|
44
|
+
"$PLAN_ID" "$DIGEST" APPROVE_EXACT_NOTIFICATION)
|
|
45
|
+
APPROVAL_TOKEN=$(printf '%s' "$APPROVAL_JSON" | jq -r '.approval_token')
|
|
46
|
+
bash bin/notify.sh dispatch "$PLAN_ID" "$APPROVAL_TOKEN"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
5. If the user chooses Edit, cancel the old plan, edit, create a new plan, and
|
|
50
|
+
preview again. If they choose Cancel, cancel it and do not send. If the plan
|
|
51
|
+
expires or dispatch fails, create a new plan and obtain new consent; never
|
|
52
|
+
retry from the old approval.
|
|
53
|
+
|
|
54
|
+
## Hard rules
|
|
55
|
+
|
|
56
|
+
- Never call notification API endpoints directly. Use `bin/notify.sh`.
|
|
57
|
+
- Never use legacy `notify.sh send` or `notify.sh group`; they only create a
|
|
58
|
+
proposal and intentionally cannot dispatch.
|
|
59
|
+
- Never substitute a group when a direct message cannot be delivered.
|
|
60
|
+
- Never add a channel after preview.
|
|
61
|
+
- Never approve or dispatch from a detached/background process, hook, cron
|
|
62
|
+
job, batch execution, or unattended agent. Those paths may create a plan and
|
|
63
|
+
report that human approval is pending.
|
|
64
|
+
- Multiple direct recipients require a separate preview and approval for each
|
|
65
|
+
exact message. A group fanout may use one approval only when every receiving
|
|
66
|
+
channel is listed in the preview.
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
[features]
|
|
2
|
+
hooks = true
|
|
3
|
+
|
|
4
|
+
[mcp_servers.notion]
|
|
5
|
+
url = "https://mcp.notion.com/mcp"
|
|
6
|
+
|
|
7
|
+
[tui]
|
|
8
|
+
# Codex currently accepts built-in footer item IDs only. Egregore's dynamic
|
|
9
|
+
# ◆ CONNECTED / ◇ LOCAL identity is rendered by bin/codex-session-start.sh.
|
|
10
|
+
status_line = [
|
|
11
|
+
"model-with-reasoning",
|
|
12
|
+
"git-branch",
|
|
13
|
+
"current-dir",
|
|
14
|
+
"context-remaining",
|
|
15
|
+
"five-hour-limit",
|
|
16
|
+
"weekly-limit",
|
|
17
|
+
"permissions",
|
|
18
|
+
"approval-mode",
|
|
19
|
+
]
|
|
20
|
+
status_line_use_colors = true
|