create-egregore 0.19.8 → 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.
- package/lib/local.js +11 -1
- package/lib/setup.js +33 -0
- package/package.json +1 -1
- 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 +21 -3
- 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 +15 -3
- package/runtime/codex/AGENTS.md +45 -9
- package/runtime/codex/bin/activity-data.sh +1 -1
- package/runtime/codex/bin/agent.sh +66 -23
- 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 +15 -4
- package/runtime/codex/bin/codex-session-start.sh +45 -3
- 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 +21 -3
- 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 +15 -3
- package/runtime/pi/.pi/APPEND_SYSTEM.md +41 -5
- package/runtime/pi/.pi/extensions/egregore.ts +98 -8
- package/runtime/pi/.pi/spec-manifest.json +2 -2
- package/runtime/pi/AGENTS.md +45 -9
- package/runtime/pi/bin/activity-data.sh +1 -1
- package/runtime/pi/bin/agent.sh +66 -23
- 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 +15 -4
- package/runtime/pi/bin/codex-session-start.sh +45 -3
- 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
|
@@ -1,9 +1,10 @@
|
|
|
1
|
-
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
|
2
|
-
import { join, relative } from "node:path";
|
|
1
|
+
import { existsSync, readFileSync, readdirSync, statSync } from "node:fs";
|
|
2
|
+
import { dirname, isAbsolute, join, relative, resolve } from "node:path";
|
|
3
3
|
import { Text } from "@earendil-works/pi-tui";
|
|
4
4
|
import { Type } from "typebox";
|
|
5
|
-
import { isMutatingBash } from "../../bin/pi-branch-policy.mjs";
|
|
5
|
+
import { isMutatingBash, isRuntimeStatePath, mutatesOnlyRuntimeState } from "../../bin/pi-branch-policy.mjs";
|
|
6
6
|
import {
|
|
7
|
+
captureSessionEnd,
|
|
7
8
|
createHandoff,
|
|
8
9
|
parseViewRequest,
|
|
9
10
|
publishScroll,
|
|
@@ -47,6 +48,32 @@ function errorMessage(error) {
|
|
|
47
48
|
return error instanceof Error ? error.message : String(error);
|
|
48
49
|
}
|
|
49
50
|
|
|
51
|
+
function nearestExistingDirectory(candidate) {
|
|
52
|
+
let cursor = resolve(candidate);
|
|
53
|
+
try {
|
|
54
|
+
if (existsSync(cursor) && !statSync(cursor).isDirectory()) cursor = dirname(cursor);
|
|
55
|
+
} catch {}
|
|
56
|
+
while (!existsSync(cursor)) {
|
|
57
|
+
const parent = dirname(cursor);
|
|
58
|
+
if (parent === cursor) break;
|
|
59
|
+
cursor = parent;
|
|
60
|
+
}
|
|
61
|
+
return cursor;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
async function gitContext(pi, candidate) {
|
|
65
|
+
const probe = nearestExistingDirectory(candidate);
|
|
66
|
+
const topResult = await pi.exec("git", ["-C", probe, "rev-parse", "--show-toplevel"], { timeout: 5000 });
|
|
67
|
+
const top = String(topResult.stdout || "").trim();
|
|
68
|
+
if (!top) return null;
|
|
69
|
+
const commonResult = await pi.exec("git", ["-C", top, "rev-parse", "--git-common-dir"], { timeout: 5000 });
|
|
70
|
+
const commonRaw = String(commonResult.stdout || "").trim();
|
|
71
|
+
return {
|
|
72
|
+
top: resolve(top),
|
|
73
|
+
common: resolve(top, commonRaw || ".git"),
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
|
|
50
77
|
export default function egregore(pi) {
|
|
51
78
|
const root = process.cwd();
|
|
52
79
|
const skillsDir = join(root, ".codex", "skills");
|
|
@@ -297,6 +324,18 @@ export default function egregore(pi) {
|
|
|
297
324
|
}
|
|
298
325
|
});
|
|
299
326
|
|
|
327
|
+
pi.on("session_shutdown", async (event, ctx) => {
|
|
328
|
+
// Reload keeps the same logical session alive. Every other graceful
|
|
329
|
+
// shutdown is Pi's equivalent of Claude Code's SessionEnd hook.
|
|
330
|
+
if (event?.reason === "reload") return;
|
|
331
|
+
try {
|
|
332
|
+
await captureSessionEnd(pi, root, ctx.sessionManager.getSessionFile(), undefined);
|
|
333
|
+
} catch {
|
|
334
|
+
// Loss-prevention capture must never prevent Pi from exiting. The
|
|
335
|
+
// attendant remains the fallback for interrupted or failed shutdowns.
|
|
336
|
+
}
|
|
337
|
+
});
|
|
338
|
+
|
|
300
339
|
pi.on("agent_settled", async (_event, ctx) => {
|
|
301
340
|
if (!activeWorkflow || !ctx.hasUI) return;
|
|
302
341
|
activeWorkflow = "";
|
|
@@ -305,20 +344,71 @@ export default function egregore(pi) {
|
|
|
305
344
|
});
|
|
306
345
|
|
|
307
346
|
pi.on("tool_call", async (event, ctx) => {
|
|
308
|
-
const
|
|
309
|
-
|
|
347
|
+
const isFileWrite = event.toolName === "write" || event.toolName === "edit";
|
|
348
|
+
const command = String(event.input?.command || "");
|
|
349
|
+
const mutates = isFileWrite || (event.toolName === "bash" && isMutatingBash(command));
|
|
310
350
|
if (!mutates) return;
|
|
311
351
|
|
|
312
|
-
|
|
352
|
+
// Resolve the branch in the git context the write actually lands in.
|
|
353
|
+
// Task worktrees from `bin/agent.sh branch` live outside ctx.cwd but sit
|
|
354
|
+
// on a task branch, so checking ctx.cwd alone blocks legitimate worktree
|
|
355
|
+
// edits whenever the session checkout itself is on develop. Memory and
|
|
356
|
+
// managed repos have a different Git common dir and are outside this guard.
|
|
357
|
+
const hub = await gitContext(pi, ctx.cwd);
|
|
358
|
+
if (event.toolName === "bash" && mutatesOnlyRuntimeState(command, hub?.top || ctx.cwd)) return;
|
|
359
|
+
let operation = hub;
|
|
360
|
+
if (isFileWrite) {
|
|
361
|
+
const targetPath = String(event.input?.path || "");
|
|
362
|
+
const abs = isAbsolute(targetPath) ? targetPath : join(ctx.cwd, targetPath);
|
|
363
|
+
if (hub && isRuntimeStatePath(abs, hub.top)) return;
|
|
364
|
+
operation = await gitContext(pi, abs);
|
|
365
|
+
if (!operation && hub) {
|
|
366
|
+
const rel = relative(hub.top, abs);
|
|
367
|
+
if (rel.startsWith("..") || isAbsolute(rel)) return;
|
|
368
|
+
}
|
|
369
|
+
} else {
|
|
370
|
+
// bash: `git -C PATH` or a leading `cd DIR &&` retargets the command
|
|
371
|
+
// away from ctx.cwd — resolve the branch where it actually runs so
|
|
372
|
+
// worktree commits (git -C $WT commit) aren't misjudged by the session
|
|
373
|
+
// checkout's branch.
|
|
374
|
+
const gitC = command.match(/(?:^|[;&|]\s*)git\s+-C\s+("[^"]*"|'[^']*'|\S+)/);
|
|
375
|
+
const cdAnd = command.match(/^\s*cd\s+("[^"]*"|'[^']*'|\S+)\s*&&/);
|
|
376
|
+
const raw = (gitC && gitC[1]) || (cdAnd && cdAnd[1]);
|
|
377
|
+
if (raw) {
|
|
378
|
+
const target = raw.replace(/^['"]|['"]$/g, "");
|
|
379
|
+
const abs = isAbsolute(target) ? target : join(ctx.cwd, target);
|
|
380
|
+
operation = await gitContext(pi, abs);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
if (hub && operation && operation.common !== hub.common) return;
|
|
385
|
+
const gitDir = operation?.top || hub?.top || ctx.cwd;
|
|
386
|
+
const result = await pi.exec("git", ["-C", gitDir, "branch", "--show-current"], { timeout: 5000 });
|
|
313
387
|
const branch = String(result.stdout || "").trim();
|
|
314
388
|
if (!PROTECTED_BRANCHES.has(branch)) return;
|
|
315
389
|
|
|
316
|
-
|
|
390
|
+
// The documented escape hatch must itself be runnable: recording consent
|
|
391
|
+
// (echo 'develop' into .egregore-branch-consent) contains a redirect and
|
|
392
|
+
// would otherwise be blocked by this very gate.
|
|
393
|
+
if (event.toolName === "bash") {
|
|
394
|
+
const consentCmd = new RegExp(
|
|
395
|
+
"^\\s*echo\\s+['\"]?" + branch + "['\"]?\\s*>\\s*\\.egregore-branch-consent\\s*$"
|
|
396
|
+
);
|
|
397
|
+
if (consentCmd.test(String(event.input?.command || "").trim())) return;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
const consentFile = join(gitDir, ".egregore-branch-consent");
|
|
317
401
|
if (existsSync(consentFile) && readFileSync(consentFile, "utf8").trim() === branch) return;
|
|
318
402
|
|
|
319
403
|
return {
|
|
320
404
|
block: true,
|
|
321
|
-
reason:
|
|
405
|
+
reason: [
|
|
406
|
+
`Protected branch (${branch}). Move project work to a task branch before writing.`,
|
|
407
|
+
`If the work topic is clear, run bin/agent.sh branch --topic "<work topic>" automatically and continue in the printed worktree; do not ask for routine Git permission.`,
|
|
408
|
+
`Ask only when the topic is genuinely ambiguous.`,
|
|
409
|
+
`Only when the user explicitly requested ${branch}, record consent with: echo '${branch}' > .egregore-branch-consent`,
|
|
410
|
+
"Memory, managed-repo, and runtime-state writes should bypass this project guard.",
|
|
411
|
+
].join("\n"),
|
|
322
412
|
};
|
|
323
413
|
});
|
|
324
414
|
}
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"generatedBy": "bin/pi-render-spec.mjs",
|
|
3
3
|
"source": "AGENTS.md generated Codex spec",
|
|
4
|
-
"sourceHash": "
|
|
5
|
-
"outputHash": "
|
|
4
|
+
"sourceHash": "e0336f66e9b4",
|
|
5
|
+
"outputHash": "da44541a50fb",
|
|
6
6
|
"adaptations": [
|
|
7
7
|
"Pi project trust and APPEND_SYSTEM delivery",
|
|
8
8
|
"Pi tool_call branch gate",
|
package/runtime/pi/AGENTS.md
CHANGED
|
@@ -96,6 +96,24 @@ When a user reports broken or missing framework behavior, the first question is
|
|
|
96
96
|
|
|
97
97
|
---
|
|
98
98
|
|
|
99
|
+
## Cross-Runtime Compatibility
|
|
100
|
+
|
|
101
|
+
Framework behavior must work across Claude Code, Codex, and Pi Egregores. Put
|
|
102
|
+
shared mechanics in runtime-neutral `bin/` scripts; keep `.claude/`, `.codex/`,
|
|
103
|
+
and `.pi/` as thin adapters to those mechanics. When changing startup, session
|
|
104
|
+
lifecycle, capture, skills, or commands, trace all three runtime entry points
|
|
105
|
+
and add or update a parity test. Do not call a framework change complete based
|
|
106
|
+
on one runtime alone. Regenerate derived Codex and Pi specifications or runtime
|
|
107
|
+
bundles whenever their sources change.
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
111
|
+
## Voice
|
|
112
|
+
|
|
113
|
+
Egregore's voice is governed by `.claude/rules/voice-bedrock.md` (always loaded). Register-specific skills: `egregore-voice` (external), `product-voice` (internal UX), `character-v4` (encounters), `alpha-openers` (outreach). See voice-bedrock for the full register map.
|
|
114
|
+
|
|
115
|
+
---
|
|
116
|
+
|
|
99
117
|
## On Launch — MANDATORY FIRST ACTION
|
|
100
118
|
|
|
101
119
|
The `egregore` launcher renders the Egregore startup card (identity, team momentum, and pending work) via `bin/codex-session-start.sh` before Codex starts, and installs project skills. 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, run `bash bin/codex-session-start.sh --card`.
|
|
@@ -146,19 +164,22 @@ If on develop after two messages, create a branch immediately from whatever cont
|
|
|
146
164
|
|
|
147
165
|
### Branch-guard protocol
|
|
148
166
|
|
|
149
|
-
The `.codex/hooks/branch-guard.js` PreToolUse hook (enabled by the launcher via `--enable hooks`)
|
|
167
|
+
The `.codex/hooks/branch-guard.js` PreToolUse hook (enabled by the launcher via `--enable hooks`) protects project writes on `develop`/`main`/`master`. Its block message is operational guidance, not a reason to interrupt the user with routine Git choices:
|
|
150
168
|
|
|
151
|
-
- **Topic is
|
|
152
|
-
- **Topic is ambiguous**
|
|
169
|
+
- **Topic is clear** (the user said "fix X", "add Y feature") → run `bin/agent.sh branch --topic "<topic>"` automatically, continue in the printed worktree, and say one short sentence so the branch change is visible. Do not ask the user to approve routine branching.
|
|
170
|
+
- **Topic is genuinely ambiguous** → ask only for the topic, using compact numbered options:
|
|
153
171
|
|
|
154
172
|
```text
|
|
155
|
-
|
|
173
|
+
What should I call this work?
|
|
156
174
|
1. <suggested slug from context>
|
|
157
175
|
2. <alternative slug>
|
|
158
176
|
3. Other: (name it)
|
|
159
177
|
```
|
|
160
178
|
|
|
161
179
|
Wait for the reply, then branch.
|
|
180
|
+
- **The user explicitly requested the protected branch** → that request is consent. Record it with `echo '{branch}' > .egregore-branch-consent`, then retry. Never create the marker merely to silence the hook.
|
|
181
|
+
|
|
182
|
+
Memory, managed-repo, and runtime-state writes should bypass the project guard. If one triggers it, correct the operation target/context instead of asking for protected-branch consent.
|
|
162
183
|
|
|
163
184
|
If this Codex build does not support hooks, follow the same discipline as a standing instruction: never write or commit on develop/main/master.
|
|
164
185
|
|
|
@@ -179,7 +200,22 @@ In connected mode, infrastructure credentials (Neo4j, Telegram) live on the API
|
|
|
179
200
|
|
|
180
201
|
## Knowledge Graph
|
|
181
202
|
|
|
182
|
-
**Connected mode only.**
|
|
203
|
+
**Connected mode only.** For the following intents, the named read must be the
|
|
204
|
+
first retrieval action. Run the exact command with its safe default limit before
|
|
205
|
+
opening files, running broad grep, inspecting `graph-op.sh`, or writing Cypher:
|
|
206
|
+
|
|
207
|
+
- current work addressed to someone → `bash bin/graph-op.sh open-handoffs "<user>"`
|
|
208
|
+
- unanswered questions for someone → `bash bin/graph-op.sh pending-questions "<user>"`
|
|
209
|
+
- handoff → implementation → branch/PR/direction → `bash bin/graph-op.sh lineage "<topic>"`
|
|
210
|
+
- company/person/topic across meetings → `bash bin/graph-op.sh meeting-history "<entity-or-topic>"`
|
|
211
|
+
|
|
212
|
+
A question about calls, meetings, transcripts, demos, or how a conversation
|
|
213
|
+
evolved is always `meeting-history` intent when it names a company, person, or
|
|
214
|
+
topic—even when it does not ask for "current" or "live" state. Pass the shortest
|
|
215
|
+
discriminative entity or topic (for example, `meeting-history "42CAP"`), not the
|
|
216
|
+
whole question.
|
|
217
|
+
|
|
218
|
+
Run `catalog` only when the route is unclear. Named reads return bounded stable fields and exact canonical `evidencePath` pointers; open only the returned files needed to answer. When a read reports partial coverage, use `unprojectedPaths` rather than claiming those files are represented in the graph. Use `bin/graph.sh` for unsupported Neo4j queries and never construct curl calls directly. See DEVELOPMENT.md §1 for the schema. In local mode there is no graph; read `memory/` directly.
|
|
183
219
|
|
|
184
220
|
---
|
|
185
221
|
|
|
@@ -268,13 +304,13 @@ Invoke commands from user intent — don't wait for the slash. Each command file
|
|
|
268
304
|
**Knowledge** — `$search` `$deep-reflect` `$archive` `$note` `$add` `$meeting` `$ingest` `$scroll`
|
|
269
305
|
**Identity** — `$me` (view profile or set display name)
|
|
270
306
|
**Coordination** — `$ask` `$quest` `$issue` `$invite` `$delete-user` `$announce`
|
|
271
|
-
**Connectors** — `$telegram-connect` (Telegram group setup)
|
|
307
|
+
**Connectors** — `$telegram-connect` (Telegram group setup) `$teams-connect` (Microsoft Teams channel setup)
|
|
272
308
|
**Git** — `$branch` `$commit` `$push` `$pr` `$save` `$review-pr` `$contribute`
|
|
273
309
|
**Spirits** — `$summon` (persistent agent processes)
|
|
274
310
|
**Infra** — `$setup` `$update` `$pull` `$env` `$infra` `$sync-repos` `$release` `$checkup`
|
|
275
311
|
|
|
276
312
|
**Disambiguation:**
|
|
277
|
-
- Knowledge: `$reflect` (share-ready) · `$note` (half-baked) · `$deep-reflect` (cross-
|
|
313
|
+
- Knowledge: `$reflect` (share-ready) · `$note` (half-baked) · `$deep-reflect` (deep research over memory — questions AND cross-referencing) · `$archive` (AI patterns)
|
|
278
314
|
- Finding things — the retrieval layers: **Grep/ls** (Claude Code / Codex file ops — always available, OSS) · **`bin/graph.sh`** (indexed relevance + relationships/status — paid). Use grep proactively; the graph deepens it when connected. (Semantic `$search` is dormant — evals showed it only pays off on massive unstructured data.)
|
|
279
315
|
- Finding **something you generated** (a scroll, handoff, emissary, decision, any hosted egregore.xyz link): `bash bin/artifacts.sh find <query>`. Every generative surface already records into memory — hosted artifacts self-register into `memory/artifacts/` at publish (and the record is committed+pushed, so it's findable from *any* session, not just the one that made it); handoffs land in `memory/handoffs/`, emissaries in `memory/handoffs/outbound/`, decisions/findings/patterns in `memory/knowledge/`. The finder searches **all** of them, ranks title/topic matches above passing body mentions, tags each hit by type (`[document]`/`[handoff]`/`[emissary]`/`[decision]`), and surfaces the shareable URL. Findable by **content, not just name**, across all three layers — grep-first for OSS, folding in search + graph when available. *"bring me the artifact where I laid out our GTM plan"* → this.
|
|
280
316
|
- Artifacts with questions: `$scroll` (living paper + embedded harvest, updates in place) · `$view` (static render) · `$harvest` (elicitation, no published face)
|
|
@@ -282,8 +318,8 @@ Invoke commands from user intent — don't wait for the slash. Each command file
|
|
|
282
318
|
- Ending: `$wrap` (personal closure) · `$handoff` (notes for others) · `$save` (still working)
|
|
283
319
|
- Tasks: `$todo` (personal) · `$quest` (team exploration) · `$issue` (something broken)
|
|
284
320
|
- Questions: `$ask [person]` (async) · just ask (agent answers from context)
|
|
285
|
-
- Ingestion: `$ingest meeting` · `$ingest user-interview` · `$ingest google` · ambiguous → ask which type
|
|
286
|
-
- Connectors: `$telegram-connect` (Telegram group) · `$ingest` (bring content in)
|
|
321
|
+
- Ingestion: `$ingest <file-or-folder>` (org-scoped corpus intake) · `$ingest meeting` · `$ingest user-interview` · `$ingest google` · ambiguous → ask which type
|
|
322
|
+
- Connectors: `$telegram-connect` (Telegram group) · `$teams-connect` (MS Teams channel) · `$ingest` (bring content in)
|
|
287
323
|
- Identity: `$me` — "who am I", "call me oz"
|
|
288
324
|
- People: `$invite` (add) · `$delete-user` (remove)
|
|
289
325
|
- PRs: `$pr` (create) · `$review-pr` (review)
|
|
@@ -9,7 +9,7 @@ trap "rm -rf $TMPDIR" EXIT
|
|
|
9
9
|
|
|
10
10
|
# --- Read config ---
|
|
11
11
|
API_URL=$(jq -r '.api_url // empty' "$SCRIPT_DIR/egregore.json" 2>/dev/null)
|
|
12
|
-
API_KEY
|
|
12
|
+
API_KEY="${EGREGORE_API_KEY:-$(grep '^EGREGORE_API_KEY=' "$SCRIPT_DIR/.env" 2>/dev/null | cut -d'=' -f2-)}"
|
|
13
13
|
ORG=$(jq -r '.org_name // "Egregore"' "$SCRIPT_DIR/egregore.json" 2>/dev/null || echo "Egregore")
|
|
14
14
|
DATE=$(date '+%b %d')
|
|
15
15
|
|
package/runtime/pi/bin/agent.sh
CHANGED
|
@@ -34,7 +34,8 @@ Runtime-neutral commands:
|
|
|
34
34
|
branch --topic TEXT Create or reuse a task branch/worktree
|
|
35
35
|
save [--message TEXT] Commit and push current repo + memory changes
|
|
36
36
|
wrap --topic T --summary TEXT Write a session wrap to memory/wraps
|
|
37
|
-
handoff --from A --to B --topic T [--
|
|
37
|
+
handoff --from A --to B --topic T [--intent action|feedback|fyi]
|
|
38
|
+
[--body TEXT|--body-file PATH] [--no-push] [--no-publish] [--no-notify] [--json]
|
|
38
39
|
ask --from A --to B --topic T --question TEXT [--no-push]
|
|
39
40
|
[--harvest-id ID --harvest-session-id ID --turn N
|
|
40
41
|
--question-intent TEXT --context-mode blind|disclosed|comparative]
|
|
@@ -332,7 +333,7 @@ ensure_working_branch() {
|
|
|
332
333
|
}
|
|
333
334
|
|
|
334
335
|
cmd_save() {
|
|
335
|
-
local message="" topic="" no_pr=0 branch pushed="false" committed="false" pr_url="" existing_pr=""
|
|
336
|
+
local message="" topic="" no_pr=0 branch pushed="false" committed="false" pr_url="" existing_pr="" pr_number="" non_md="" merged="false"
|
|
336
337
|
|
|
337
338
|
while [ $# -gt 0 ]; do
|
|
338
339
|
case "$1" in
|
|
@@ -379,10 +380,31 @@ cmd_save() {
|
|
|
379
380
|
fi
|
|
380
381
|
fi
|
|
381
382
|
|
|
383
|
+
# Non-coding saves auto-merge to develop (same rule as /save and the
|
|
384
|
+
# handoff helper; policy in bin/lib/noncode.sh). `--auto` needs repo
|
|
385
|
+
# auto-merge + pending required checks; without branch protection it always
|
|
386
|
+
# errors, so fall back to a direct merge.
|
|
387
|
+
if [ -n "$pr_url" ]; then
|
|
388
|
+
# shellcheck source=bin/lib/noncode.sh
|
|
389
|
+
. "$SCRIPT_DIR/bin/lib/noncode.sh" 2>/dev/null || true
|
|
390
|
+
type noncode_blockers >/dev/null 2>&1 || noncode_blockers() {
|
|
391
|
+
git -C "${2:-.}" diff "${1:-origin/develop}...HEAD" --name-only 2>/dev/null | grep -v '\.md$' | head -1 || true
|
|
392
|
+
}
|
|
393
|
+
pr_number="${pr_url##*/}"
|
|
394
|
+
non_code="$(noncode_blockers origin/develop "$SCRIPT_DIR")"
|
|
395
|
+
if [ -z "$non_code" ] && [ -n "$pr_number" ]; then
|
|
396
|
+
if gh pr merge "$pr_number" --auto --merge >/dev/null 2>&1 \
|
|
397
|
+
|| gh pr merge "$pr_number" --merge >/dev/null 2>&1; then
|
|
398
|
+
merged="true"
|
|
399
|
+
fi
|
|
400
|
+
fi
|
|
401
|
+
fi
|
|
402
|
+
|
|
382
403
|
echo "branch: ${branch:-none}"
|
|
383
404
|
echo "commit: $committed"
|
|
384
405
|
echo "push: $pushed"
|
|
385
406
|
[ -n "$pr_url" ] && echo "pr: $pr_url"
|
|
407
|
+
[ "$merged" = "true" ] && echo "merge: non-coding, merged to develop"
|
|
386
408
|
return 0
|
|
387
409
|
}
|
|
388
410
|
|
|
@@ -413,31 +435,38 @@ cmd_wrap() {
|
|
|
413
435
|
fi
|
|
414
436
|
[ -n "$body" ] || body="$summary"
|
|
415
437
|
|
|
416
|
-
local
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
438
|
+
local tmpd result rel session_id branch
|
|
439
|
+
local capture_args=(--mode personal)
|
|
440
|
+
tmpd="$(mktemp -d -t egregore-agent-wrap-XXXXXX)"
|
|
441
|
+
trap 'rm -rf "$tmpd"' RETURN
|
|
442
|
+
session_id="$(cat "$SCRIPT_DIR/.egregore-session-id" 2>/dev/null || true)"
|
|
443
|
+
branch="$(git -C "$SCRIPT_DIR" branch --show-current 2>/dev/null || true)"
|
|
444
|
+
[ "$no_push" = "1" ] && capture_args+=(--no-push)
|
|
445
|
+
|
|
446
|
+
printf '%s\n' "$body" |
|
|
447
|
+
TMPDIR="$tmpd" bash "$SCRIPT_DIR/bin/capture-run.sh" \
|
|
448
|
+
"${capture_args[@]}" \
|
|
449
|
+
--author "$from" \
|
|
450
|
+
--topic "$topic" \
|
|
451
|
+
--summary "$summary" \
|
|
452
|
+
--session-id "$session_id" \
|
|
453
|
+
--branch "$branch" >/dev/null
|
|
454
|
+
|
|
455
|
+
result="$tmpd/capture-run-result.json"
|
|
456
|
+
rel="$(jq -r '.file // empty' "$result" 2>/dev/null || true)"
|
|
457
|
+
[ -n "$rel" ] || die "wrap was not created"
|
|
435
458
|
echo "wrap: memory/$rel"
|
|
459
|
+
|
|
460
|
+
# Session close: non-coding core-repo changes save + merge themselves
|
|
461
|
+
# (gated inside session-autosave.sh — code is never auto-committed).
|
|
462
|
+
if [ "$no_push" = "0" ]; then
|
|
463
|
+
( bash "$SCRIPT_DIR/bin/session-autosave.sh" --dir "$SCRIPT_DIR" >/dev/null 2>&1 & ) >/dev/null 2>&1
|
|
464
|
+
fi
|
|
436
465
|
}
|
|
437
466
|
|
|
438
467
|
cmd_handoff() {
|
|
439
468
|
ensure_memory
|
|
440
|
-
local from="" to="" topic="" body="" body_file="" project=""
|
|
469
|
+
local from="" to="" topic="" body="" body_file="" project="" intent="action"
|
|
441
470
|
local no_publish=0 no_notify=0 json=0
|
|
442
471
|
NO_PUSH=0
|
|
443
472
|
|
|
@@ -449,6 +478,7 @@ cmd_handoff() {
|
|
|
449
478
|
--body) body="${2:-}"; shift 2 ;;
|
|
450
479
|
--body-file) body_file="${2:-}"; shift 2 ;;
|
|
451
480
|
--project) project="${2:-}"; shift 2 ;;
|
|
481
|
+
--intent) intent="${2:-}"; shift 2 ;;
|
|
452
482
|
--no-push) NO_PUSH=1; shift ;;
|
|
453
483
|
--no-publish) no_publish=1; shift ;;
|
|
454
484
|
--no-notify) no_notify=1; shift ;;
|
|
@@ -460,6 +490,10 @@ cmd_handoff() {
|
|
|
460
490
|
[ -z "$from" ] && from="$(person_from_state)"
|
|
461
491
|
[ -n "$from" ] || die "handoff requires --from"
|
|
462
492
|
[ -n "$topic" ] || die "handoff requires --topic"
|
|
493
|
+
case "$intent" in
|
|
494
|
+
action|feedback|fyi) ;;
|
|
495
|
+
*) die "invalid --intent: $intent (action|feedback|fyi)" ;;
|
|
496
|
+
esac
|
|
463
497
|
if [ -n "$body_file" ]; then
|
|
464
498
|
[ -f "$body_file" ] || die "body file not found: $body_file"
|
|
465
499
|
body="$(cat "$body_file")"
|
|
@@ -480,6 +514,7 @@ cmd_handoff() {
|
|
|
480
514
|
[ -n "$project" ] && project_line="**Project**: $project"
|
|
481
515
|
[ -n "$to" ] && handoff_args+=(--recipient "$to")
|
|
482
516
|
[ -n "$project" ] && handoff_args+=(--project "$project")
|
|
517
|
+
handoff_args+=(--intent "$intent")
|
|
483
518
|
[ "$NO_PUSH" = "1" ] && handoff_args+=(--no-push)
|
|
484
519
|
[ "$no_publish" = "1" ] && handoff_args+=(--no-publish)
|
|
485
520
|
[ "$no_notify" = "1" ] && handoff_args+=(--no-notify)
|
|
@@ -489,10 +524,12 @@ cmd_handoff() {
|
|
|
489
524
|
printf '**Date**: %s\n' "$today"
|
|
490
525
|
printf '**Author**: %s\n' "$from"
|
|
491
526
|
[ -n "$to" ] && printf '**To**: %s\n' "$to"
|
|
527
|
+
printf '**Intent**: %s\n' "$intent"
|
|
492
528
|
[ -n "$project_line" ] && printf '%s\n' "$project_line"
|
|
493
529
|
printf '\n## Briefing\n\n%s\n\n' "$body"
|
|
494
530
|
printf '## Next Steps\n\n- Continue from this handoff using the shared Egregore memory protocol.\n'
|
|
495
|
-
} | TMPDIR="$tmpd" bash "$SCRIPT_DIR/bin/
|
|
531
|
+
} | TMPDIR="$tmpd" bash "$SCRIPT_DIR/bin/capture-run.sh" \
|
|
532
|
+
--mode addressed \
|
|
496
533
|
--author "$from" \
|
|
497
534
|
--topic "$topic" \
|
|
498
535
|
"${handoff_args[@]}" \
|
|
@@ -509,6 +546,12 @@ cmd_handoff() {
|
|
|
509
546
|
[ -s "$stdout_file" ] && sed 's/^/status: /' "$stdout_file"
|
|
510
547
|
echo "handoff: memory/$rel"
|
|
511
548
|
echo "result: $result_out"
|
|
549
|
+
|
|
550
|
+
# Parity with the Claude /handoff skill: save core-repo session work in the
|
|
551
|
+
# background (non-coding diffs auto-merge to develop, code stays in a PR).
|
|
552
|
+
if [ "$NO_PUSH" = "0" ]; then
|
|
553
|
+
( bash "$SCRIPT_DIR/bin/handoff-save-egregore.sh" "$from" "$topic" --repo-dir "$SCRIPT_DIR" >/dev/null 2>&1 & ) >/dev/null 2>&1
|
|
554
|
+
fi
|
|
512
555
|
}
|
|
513
556
|
|
|
514
557
|
cmd_ask() {
|