create-egregore 0.19.2 → 0.19.5
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 +3 -1
- package/bin/cli.js +4 -1
- package/lib/local.js +78 -12
- package/lib/setup.js +15 -0
- package/package.json +2 -2
- package/runtime/codex/.codex/hooks/search-hint.js +65 -0
- package/runtime/codex/.codex/hooks.json +13 -0
- package/runtime/codex/.codex/skills/deep-reflect/SKILL.md +14 -10
- package/runtime/codex/.codex/skills/minion/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/scroll/SKILL.md +168 -0
- package/runtime/codex/.codex/skills/scroll/agents/openai.yaml +4 -0
- package/runtime/codex/.codex/skills/scroll/assets/shell.html +542 -0
- package/runtime/codex/.codex/skills/search/SKILL.md +56 -0
- package/runtime/codex/.codex/skills/session-view/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/thread/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/view/SKILL.md +147 -32
- package/runtime/codex/.codex/skills/view/agents/openai.yaml +4 -0
- package/runtime/codex/.codex/skills/view/assets/compose-scaffold.html +221 -0
- package/runtime/codex/.codex/spec-manifest.json +3 -3
- package/runtime/codex/AGENTS.md +24 -3
- package/runtime/codex/bin/activity-data.sh +46 -4
- package/runtime/codex/bin/agent.sh +10 -2
- package/runtime/codex/bin/codex-install-skills.sh +1 -1
- package/runtime/codex/bin/codex-render-spec.mjs +18 -1
- package/runtime/codex/bin/codex-session-start.sh +1 -1
- package/runtime/codex/bin/codex-skill-render.mjs +13 -7
- package/runtime/codex/bin/codex-sync-skills.sh +4 -0
- package/runtime/codex/bin/graph-op.sh +56 -5
- package/runtime/codex/bin/graph.sh +36 -0
- package/runtime/codex/bin/handoff-run.sh +157 -7
- package/runtime/codex/bin/handoff-save-egregore.sh +6 -1
- package/runtime/codex/bin/index-handoff.sh +9 -3
- package/runtime/codex/bin/lib/context.sh +89 -17
- package/runtime/codex/bin/lib/dashboard-artifact.sh +1 -1
- package/runtime/codex/bin/lib/git-sync.sh +35 -9
- package/runtime/codex/bin/lib/greeting.sh +25 -3
- package/runtime/codex/bin/publish-artifact.sh +29 -1
- package/runtime/codex/manifest.json +16 -1
|
@@ -48,6 +48,8 @@ const NATIVE_CODEX_SKILLS = [
|
|
|
48
48
|
"invite",
|
|
49
49
|
"ask",
|
|
50
50
|
"save",
|
|
51
|
+
"view",
|
|
52
|
+
"scroll",
|
|
51
53
|
];
|
|
52
54
|
|
|
53
55
|
const PRIMARY_CODEX_SKILLS = [
|
|
@@ -1372,7 +1374,7 @@ function renderHeroPanel({ selected, pv, width, height, countSuffix, filter, fil
|
|
|
1372
1374
|
const skillGroups = [
|
|
1373
1375
|
`${DIM}core${RESET}: ${workflows}`,
|
|
1374
1376
|
`${DIM}team${RESET}: ${joinShort(["/handoff", "/ask", "/invite", "/quest"], Math.max(16, bodyWidth - 8))}`,
|
|
1375
|
-
`${DIM}deeper${RESET}: ${joinShort(["/announce", "/harvest", "/the-spiral", "/deep-reflect"], Math.max(16, bodyWidth - 10))}`,
|
|
1377
|
+
`${DIM}deeper${RESET}: ${joinShort(["/announce", "/harvest", "/the-spiral", "/deep-reflect", "/view", "/scroll"], Math.max(16, bodyWidth - 10))}`,
|
|
1376
1378
|
`${DIM}fallback${RESET}: ${adapterCount} adapter skills`,
|
|
1377
1379
|
];
|
|
1378
1380
|
|
package/bin/cli.js
CHANGED
|
@@ -36,6 +36,7 @@ const {
|
|
|
36
36
|
registerInstance,
|
|
37
37
|
installLauncherBinary,
|
|
38
38
|
normalizeRuntime,
|
|
39
|
+
redactToken,
|
|
39
40
|
} = require("../lib/setup");
|
|
40
41
|
const { detectContext, isWritable, isSystemDir } = require("../lib/context");
|
|
41
42
|
|
|
@@ -888,6 +889,8 @@ async function joinFlow(api, githubToken, choice, ctx) {
|
|
|
888
889
|
}
|
|
889
890
|
|
|
890
891
|
main().catch((err) => {
|
|
891
|
-
|
|
892
|
+
// Last-resort net: never let a credential reach the terminal via an
|
|
893
|
+
// unhandled error message (cloneRepo already redacts at the source).
|
|
894
|
+
ui.error(redactToken(err && err.message));
|
|
892
895
|
process.exit(1);
|
|
893
896
|
});
|
package/lib/local.js
CHANGED
|
@@ -25,6 +25,7 @@ const {
|
|
|
25
25
|
installLauncherBinary,
|
|
26
26
|
launchOrInstruct,
|
|
27
27
|
embedToken,
|
|
28
|
+
redactToken,
|
|
28
29
|
configureGitCredentials,
|
|
29
30
|
gitCloneEnv,
|
|
30
31
|
initMemoryDirs,
|
|
@@ -81,6 +82,19 @@ async function repoExists(token, owner, repo) {
|
|
|
81
82
|
return status === 200;
|
|
82
83
|
}
|
|
83
84
|
|
|
85
|
+
// Full repo metadata (or null). Used to detect `archived` when reclaiming a repo
|
|
86
|
+
// that a prior egregore delete archived rather than hard-deleted.
|
|
87
|
+
async function getRepoMeta(token, owner, repo) {
|
|
88
|
+
const { status, data } = await ghApi("GET", `/repos/${owner}/${repo}`, token);
|
|
89
|
+
return status === 200 ? data : null;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
// Unarchive a repo so pushes/invites work again. Returns true on success.
|
|
93
|
+
async function unarchiveRepo(token, owner, repo) {
|
|
94
|
+
const { status } = await ghApi("PATCH", `/repos/${owner}/${repo}`, token, { archived: false });
|
|
95
|
+
return status === 200;
|
|
96
|
+
}
|
|
97
|
+
|
|
84
98
|
async function createFromTemplate(token, owner, name, description) {
|
|
85
99
|
const { status, data } = await ghApi(
|
|
86
100
|
"POST",
|
|
@@ -342,12 +356,20 @@ function cloneRepo(url, dir, token, ui, label) {
|
|
|
342
356
|
ui.warn(`${path.basename(dir)}/ already exists — pulling latest`);
|
|
343
357
|
run("git pull", { cwd: dir });
|
|
344
358
|
} else {
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
359
|
+
try {
|
|
360
|
+
execFileSync("git", ["clone", embedToken(url, token), dir], {
|
|
361
|
+
stdio: "pipe",
|
|
362
|
+
encoding: "utf-8",
|
|
363
|
+
timeout: 60000,
|
|
364
|
+
env: gitCloneEnv(),
|
|
365
|
+
});
|
|
366
|
+
} catch (err) {
|
|
367
|
+
// execFileSync puts the full command — including the embedded token — in
|
|
368
|
+
// err.message. Rethrow a redacted error that keeps git's stderr (the
|
|
369
|
+
// useful "remote: ..." lines) but never the credential.
|
|
370
|
+
const detail = [err.message, err.stderr].filter(Boolean).join("\n");
|
|
371
|
+
throw new Error(redactToken(detail, token));
|
|
372
|
+
}
|
|
351
373
|
try {
|
|
352
374
|
run(`git remote set-url origin ${url}`, { cwd: dir });
|
|
353
375
|
} catch {}
|
|
@@ -1023,13 +1045,57 @@ async function localFounderFlow(ui, ctx = {}) {
|
|
|
1023
1045
|
if (existingConfig) {
|
|
1024
1046
|
s1.stop(`${githubOrg}/${repoName} already has Egregore set up`);
|
|
1025
1047
|
console.log("");
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1048
|
+
|
|
1049
|
+
// The repo already carries an Egregore. You're legitimately here for one
|
|
1050
|
+
// of three reasons: reclaim your own (e.g. the platform record was deleted
|
|
1051
|
+
// but the repo survives), join someone else's, or you meant a fresh one.
|
|
1052
|
+
// Reclaim reuses the repo in place — keeping its collaborators — so you
|
|
1053
|
+
// don't get forced onto a new name and re-invite everyone.
|
|
1054
|
+
const canPick = ui.canInteract && ui.canInteract();
|
|
1055
|
+
const RECLAIM = { label: "Reuse this one — reconnect and finish setup", description: "Adopt the existing repo, keep its collaborators" };
|
|
1056
|
+
const JOIN = { label: "Just join it", description: joinCommand(githubOrg, repoName) };
|
|
1057
|
+
const RENAME = { label: "Set up a different Egregore", description: "Pick a different team name" };
|
|
1058
|
+
|
|
1059
|
+
let action = "join";
|
|
1060
|
+
if (canPick) {
|
|
1061
|
+
const picked = await ui.choose(
|
|
1062
|
+
`${githubOrg}/${repoName} already exists — what do you want to do?`,
|
|
1063
|
+
[RECLAIM, JOIN, RENAME],
|
|
1064
|
+
);
|
|
1065
|
+
action = picked.label === RECLAIM.label ? "reclaim"
|
|
1066
|
+
: picked.label === RENAME.label ? "rename" : "join";
|
|
1067
|
+
}
|
|
1068
|
+
|
|
1069
|
+
if (action === "join") {
|
|
1070
|
+
ui.info("To join it:");
|
|
1071
|
+
ui.info(` ${ui.bold(joinCommand(githubOrg, repoName))}`);
|
|
1072
|
+
console.log("");
|
|
1073
|
+
ui.info("To set up a different one, choose a different team name.");
|
|
1074
|
+
process.exit(0);
|
|
1075
|
+
}
|
|
1076
|
+
if (action === "rename") {
|
|
1077
|
+
ui.info("Re-run setup and choose a different team name.");
|
|
1078
|
+
process.exit(0);
|
|
1079
|
+
}
|
|
1080
|
+
|
|
1081
|
+
// Reclaim: adopt the existing repo. If a prior delete archived it, unarchive
|
|
1082
|
+
// so pushes/invites work. Fall through — the rest of the founder flow reuses
|
|
1083
|
+
// the repo as-is and reconnects the platform record, preserving existing
|
|
1084
|
+
// collaborators (so no redundant re-invite).
|
|
1085
|
+
const meta = await getRepoMeta(githubToken, githubOrg, repoName);
|
|
1086
|
+
if (meta && meta.archived) {
|
|
1087
|
+
const s1b = ui.spinner(`Unarchiving ${githubOrg}/${repoName}...`);
|
|
1088
|
+
if (await unarchiveRepo(githubToken, githubOrg, repoName)) {
|
|
1089
|
+
s1b.stop(`Unarchived ${githubOrg}/${repoName}`);
|
|
1090
|
+
} else {
|
|
1091
|
+
s1b.fail(`Could not unarchive ${githubOrg}/${repoName} — unarchive it on GitHub, then re-run.`);
|
|
1092
|
+
process.exit(1);
|
|
1093
|
+
}
|
|
1094
|
+
}
|
|
1095
|
+
ui.success(`Reusing ${githubOrg}/${repoName} — reconnecting`);
|
|
1096
|
+
} else {
|
|
1097
|
+
s1.stop("Egregore repo exists but has no config — using it");
|
|
1031
1098
|
}
|
|
1032
|
-
s1.stop("Egregore repo exists but has no config — using it");
|
|
1033
1099
|
} else {
|
|
1034
1100
|
try {
|
|
1035
1101
|
await createFromTemplate(githubToken, githubOrg, repoName, description);
|
package/lib/setup.js
CHANGED
|
@@ -596,6 +596,20 @@ function embedToken(url, token) {
|
|
|
596
596
|
}
|
|
597
597
|
}
|
|
598
598
|
|
|
599
|
+
// Strip credentials from any text before it reaches the terminal. execFileSync
|
|
600
|
+
// bakes the full argv into its thrown error message, so an authenticated
|
|
601
|
+
// `git clone https://x-access-token:<pat>@github.com/...` leaks the token
|
|
602
|
+
// verbatim if that error is printed. Redacts the known token, any userinfo in
|
|
603
|
+
// an https URL, and bare GitHub token shapes as a backstop.
|
|
604
|
+
function redactToken(text, token) {
|
|
605
|
+
let s = text == null ? "" : String(text);
|
|
606
|
+
if (token) s = s.split(token).join("***");
|
|
607
|
+
s = s.replace(/(https:\/\/)[^@/\s]+@/g, "$1***@"); // https://user:secret@host → https://***@host
|
|
608
|
+
s = s.replace(/gh[posru]_[A-Za-z0-9_]+/g, "***"); // ghp_/gho_/ghs_/ghu_/ghr_
|
|
609
|
+
s = s.replace(/github_pat_[A-Za-z0-9_]+/g, "***");
|
|
610
|
+
return s;
|
|
611
|
+
}
|
|
612
|
+
|
|
599
613
|
function toSSH(url) {
|
|
600
614
|
const m = url.match(/https:\/\/github\.com\/(.+?)(?:\.git)?$/);
|
|
601
615
|
return m ? `git@github.com:${m[1]}.git` : null;
|
|
@@ -1255,6 +1269,7 @@ module.exports = {
|
|
|
1255
1269
|
ensurePersonOnboardingWitness,
|
|
1256
1270
|
ensureInstallerMemberRole,
|
|
1257
1271
|
embedToken,
|
|
1272
|
+
redactToken,
|
|
1258
1273
|
configureGitCredentials,
|
|
1259
1274
|
gitCloneEnv,
|
|
1260
1275
|
assistantInstallCommand,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "create-egregore",
|
|
3
|
-
"version": "0.19.
|
|
3
|
+
"version": "0.19.5",
|
|
4
4
|
"description": "Set up Egregore for your team in one command — installs the egregore launcher and your choice of Claude Code or Codex.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"bin": {
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
],
|
|
31
31
|
"repository": {
|
|
32
32
|
"type": "git",
|
|
33
|
-
"url": "git+https://github.com/
|
|
33
|
+
"url": "git+https://github.com/Curve-Labs/egregore.git"
|
|
34
34
|
},
|
|
35
35
|
"homepage": "https://github.com/egregore-labs/egregore"
|
|
36
36
|
}
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
// search-hint.js — UserPromptSubmit hook (Codex runtime).
|
|
4
|
+
//
|
|
5
|
+
// Mirror of .claude/hooks/search-hint.sh: when the user's prompt is
|
|
6
|
+
// recall-shaped ("find the handoff about…", "did we decide…"), inject a
|
|
7
|
+
// routing hint toward `bin/agent.sh search` — one ranked call over
|
|
8
|
+
// memory/ — instead of improvised ls/grep/graph exploration.
|
|
9
|
+
//
|
|
10
|
+
// Advisory only, never blocks, rate-limited once per session, exits 0.
|
|
11
|
+
// If the Codex runtime does not fire UserPromptSubmit, this file is inert.
|
|
12
|
+
|
|
13
|
+
const fs = require("node:fs");
|
|
14
|
+
const path = require("node:path");
|
|
15
|
+
const os = require("node:os");
|
|
16
|
+
|
|
17
|
+
function readStdin() {
|
|
18
|
+
try { return fs.readFileSync(0, "utf-8"); } catch { return ""; }
|
|
19
|
+
}
|
|
20
|
+
function parseJson(raw) {
|
|
21
|
+
try { return JSON.parse(raw || "{}"); } catch { return {}; }
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
const input = parseJson(readStdin());
|
|
25
|
+
const prompt = String(input.prompt || input.user_prompt || "");
|
|
26
|
+
if (!prompt) process.exit(0);
|
|
27
|
+
|
|
28
|
+
const root = process.env.CLAUDE_PROJECT_DIR || process.cwd();
|
|
29
|
+
if (!fs.existsSync(path.join(root, "bin", "search.sh"))) process.exit(0);
|
|
30
|
+
|
|
31
|
+
// Already oriented toward search.
|
|
32
|
+
if (/(\/search\b|search\.sh|\bsearch (the )?memory\b)/i.test(prompt)) process.exit(0);
|
|
33
|
+
|
|
34
|
+
const RECALL = new RegExp(
|
|
35
|
+
[
|
|
36
|
+
"\\b(find|locate|dig up|look up|pull up)\\b.*\\b(handoff|decision|notes?|docs?|meeting|harvest|quest|reflection)s?\\b",
|
|
37
|
+
"\\bdid we (decide|discuss|agree|say|talk about|cover)\\b",
|
|
38
|
+
"\\bdo we have (any )?(notes?|docs?|anything|something)( on| about)?\\b",
|
|
39
|
+
"\\bwhat did we (decide|say|agree|discuss)\\b",
|
|
40
|
+
"\\bhave we (discussed|decided|covered|talked about|looked at)\\b",
|
|
41
|
+
"\\bnotes on\\b",
|
|
42
|
+
"\\bhandoff (from|about)\\b",
|
|
43
|
+
"\\bwho worked on\\b",
|
|
44
|
+
"\\bdidn.?t (someone|we) already\\b",
|
|
45
|
+
"\\bwas there (a|any) (handoff|decision|discussion|meeting)\\b",
|
|
46
|
+
].join("|"),
|
|
47
|
+
"i"
|
|
48
|
+
);
|
|
49
|
+
if (!RECALL.test(prompt)) process.exit(0);
|
|
50
|
+
|
|
51
|
+
// Rate-limit once per session.
|
|
52
|
+
let sid = "default";
|
|
53
|
+
try { sid = fs.readFileSync(path.join(root, ".egregore-session-id"), "utf-8").trim() || "default"; } catch {}
|
|
54
|
+
const flag = path.join(os.tmpdir(), `egregore-search-hint-${sid}`);
|
|
55
|
+
if (fs.existsSync(flag)) process.exit(0);
|
|
56
|
+
try { fs.writeFileSync(flag, "1"); } catch {}
|
|
57
|
+
|
|
58
|
+
process.stdout.write(JSON.stringify({
|
|
59
|
+
hookSpecificOutput: {
|
|
60
|
+
hookEventName: "UserPromptSubmit",
|
|
61
|
+
additionalContext:
|
|
62
|
+
"Routing hint: this prompt asks for recall from org memory. FIRST action: `bin/agent.sh search \"<the concept, not the literal sentence>\"` — one ranked call over all of memory/ (hybrid keyword+semantic; graph-state annotations attach automatically in connected mode). Read the top hits and cite paths. Do NOT improvise ls/grep/graph exploration first — hand-written graph queries against invented labels (e.g. :Handoff — handoffs are :Session nodes) return empty. Full spec: the search skill.",
|
|
63
|
+
},
|
|
64
|
+
}) + "\n");
|
|
65
|
+
process.exit(0);
|
|
@@ -1,5 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"hooks": {
|
|
3
|
+
"UserPromptSubmit": [
|
|
4
|
+
{
|
|
5
|
+
"matcher": "",
|
|
6
|
+
"hooks": [
|
|
7
|
+
{
|
|
8
|
+
"type": "command",
|
|
9
|
+
"command": "node \"$(git rev-parse --show-toplevel)/.codex/hooks/search-hint.js\"",
|
|
10
|
+
"timeout": 5,
|
|
11
|
+
"statusMessage": "Checking recall routing"
|
|
12
|
+
}
|
|
13
|
+
]
|
|
14
|
+
}
|
|
15
|
+
],
|
|
3
16
|
"PreToolUse": [
|
|
4
17
|
{
|
|
5
18
|
"matcher": "Bash|apply_patch|Edit|Write",
|
|
@@ -23,28 +23,32 @@ signals outside the lens.
|
|
|
23
23
|
1. Check `egregore.json` mode first. In local mode, do a lightweight reflection
|
|
24
24
|
from memory files and do not call graph, batch graph, or notification
|
|
25
25
|
scripts.
|
|
26
|
-
2. In
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
3.
|
|
26
|
+
2. In all modes, run `bin/agent.sh search "$TOPIC" -n 6` — hybrid content
|
|
27
|
+
recall over the whole memory repo. Feed strong hits into the candidate
|
|
28
|
+
set; in local mode this is the primary context source.
|
|
29
|
+
3. In connected mode, additionally gather graph context with `bin/graph.sh`
|
|
30
|
+
and suppress raw JSON. Query recent sessions, active quests, recent
|
|
31
|
+
artifacts, decisions, the topic landscape, and the full artifact
|
|
32
|
+
landscape.
|
|
33
|
+
4. If the graph is unavailable or has fewer than about ten artifacts, fall back
|
|
30
34
|
to lightweight reflection and say why in one line.
|
|
31
|
-
|
|
35
|
+
5. Select candidate artifacts. Prefer title, type, topics, quest, author, and
|
|
32
36
|
date metadata first; fetch file contents only for the selected subset.
|
|
33
|
-
|
|
37
|
+
6. Use Codex multi-agent or subagent tooling only if it is available in the
|
|
34
38
|
current session. If not, run a single-model staged analysis:
|
|
35
39
|
- candidate selection summary
|
|
36
40
|
- evidence extraction summary
|
|
37
41
|
- signal analysis
|
|
38
42
|
- final synthesis
|
|
39
|
-
|
|
43
|
+
7. Classify signals as tension, convergence, gap, dependency, phase shift,
|
|
40
44
|
redundancy, emergence, reinforcement, or a named custom type.
|
|
41
|
-
|
|
45
|
+
8. Write a markdown artifact under `memory/knowledge/deep-reflect/` with
|
|
42
46
|
frontmatter for run id, mode, lens, author, created time, and selected
|
|
43
47
|
evidence paths.
|
|
44
|
-
|
|
48
|
+
9. In connected mode, write best-effort graph edges with `bin/graph.sh` or
|
|
45
49
|
`bin/graph-batch.sh`. Use `TENSION_WITH` for conflict signals and
|
|
46
50
|
`RELATES_TO` for other signals.
|
|
47
|
-
|
|
51
|
+
10. Save memory and repo changes with `bin/agent.sh save --message "Deep reflect: $TOPIC" --topic "$TOPIC"`.
|
|
48
52
|
|
|
49
53
|
## Output
|
|
50
54
|
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: minion
|
|
3
|
+
description: '>'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
<!-- generated-by: bin/codex-sync-skills.sh -->
|
|
7
|
+
|
|
8
|
+
# Egregore minion 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/minion/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.
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: scroll
|
|
3
|
+
description: 'Compose and tend an Egregore living paper with an explanatory face, embedded decision harvest, stable published URL, and versioned paste-back update loop. Use when the user invokes /scroll or $scroll, asks for a living paper or a spec with questions inside, presents a proposal/architecture/product/feature with open forks, or pastes a versioned scroll decisions return block.'
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Egregore Scroll
|
|
7
|
+
|
|
8
|
+
Native Codex Egregore skill. Use only inside an Egregore checkout.
|
|
9
|
+
|
|
10
|
+
A scroll is one self-contained Meridian HTML artifact with two linked views:
|
|
11
|
+
the readable paper and the open decisions living inside it. Every return block
|
|
12
|
+
updates the same artifact at the same stable id.
|
|
13
|
+
|
|
14
|
+
Use `$view` for a static document with no pending decisions, `$harvest` for
|
|
15
|
+
elicitation without a published explaining face, and a concise question for one
|
|
16
|
+
small fork. A scroll requires all three legs: face, harvest, and update loop.
|
|
17
|
+
|
|
18
|
+
## Structured UX parity
|
|
19
|
+
|
|
20
|
+
The browser artifact is the primary output. Preserve both tabs, deep links from
|
|
21
|
+
honest costs into decisions, the versioned copydock fence, stable URL, and local
|
|
22
|
+
source path. Do not substitute a prose-only summary or expose raw publish output.
|
|
23
|
+
|
|
24
|
+
## Route the invocation
|
|
25
|
+
|
|
26
|
+
- A normal request creates version 1.
|
|
27
|
+
- A pasted block matching `#<slug>-decisions:v<N>` runs the update loop.
|
|
28
|
+
- If a pasted version is stale, absorb still-applicable answers and state which
|
|
29
|
+
forks moved after that version.
|
|
30
|
+
|
|
31
|
+
Run inline with shell and filesystem tools. Do not invoke Claude Code commands
|
|
32
|
+
or Loom delegation.
|
|
33
|
+
|
|
34
|
+
## Build a new scroll
|
|
35
|
+
|
|
36
|
+
1. Read the source design, relevant memory, code, and quest context in full.
|
|
37
|
+
2. Derive a stable kebab-case slug of at most 50 characters. Create:
|
|
38
|
+
- surface id `ds-<date>-<slug>`;
|
|
39
|
+
- harvest id `harvest-<date>-<slug>`;
|
|
40
|
+
- a short unique `<prefix>.harvest` browser storage key.
|
|
41
|
+
3. Copy `.codex/skills/scroll/assets/shell.html` into `tmp/<slug>/index.html`.
|
|
42
|
+
Keep this build source in the worktree so future turns can tend it.
|
|
43
|
+
4. Inject the canonical design-system CSS at build time from
|
|
44
|
+
`packages/egregore-artifacts/lib/vendor/design-system/dist/egregore-design.css`
|
|
45
|
+
into `/*__MERIDIAN_CSS__*/`. If the packages tree is unavailable, extract the
|
|
46
|
+
same path from `egregore-artifacts@latest` rather than freezing tokens.
|
|
47
|
+
5. Fill every placeholder and replace the `__FACE__` and `__HARVEST__` slot
|
|
48
|
+
comments using the grammar documented in the shell.
|
|
49
|
+
|
|
50
|
+
### Write the face
|
|
51
|
+
|
|
52
|
+
Create a light paper, not a transcript dump:
|
|
53
|
+
|
|
54
|
+
- A paper header with kicker, title, italic subtitle, grounds, date, and
|
|
55
|
+
`Version vN — updates in place at this URL`.
|
|
56
|
+
- The setting: current reality, load-bearing verified numbers, the gap, and a
|
|
57
|
+
thesis reframe.
|
|
58
|
+
- Two to four genuinely different mental models that stress-test the design.
|
|
59
|
+
- The machine: concrete anatomy and protocol, using layers, steps, and
|
|
60
|
+
tokenized SVG figures. Mark illustrative charts `shapes, not measurements`.
|
|
61
|
+
- Consequences for product or practice.
|
|
62
|
+
- Honest costs as cards. Every cost must call `goFork('<fork-id>')` and link to
|
|
63
|
+
the harvest decision that prices it.
|
|
64
|
+
|
|
65
|
+
### Write the harvest
|
|
66
|
+
|
|
67
|
+
Create two to four bands of decision cards. Each card needs:
|
|
68
|
+
|
|
69
|
+
- a stable fork id, grounded framing, and a reference back into the face;
|
|
70
|
+
- two to four real options, each with one credible advantage and disadvantage;
|
|
71
|
+
- at most one starred lean, presented as a position to challenge;
|
|
72
|
+
- a why textarea whose note returns with the pick.
|
|
73
|
+
|
|
74
|
+
Add open prompts for material that is not a fork. Prefer questions grounded in
|
|
75
|
+
the work over meta-prompts asking what to ask next. The copydock must emit:
|
|
76
|
+
|
|
77
|
+
```text
|
|
78
|
+
#<slug>-decisions:v<N>
|
|
79
|
+
surface: <surface-id>
|
|
80
|
+
harvest: <harvest-id>
|
|
81
|
+
date: <date>
|
|
82
|
+
|
|
83
|
+
Q1 <fork-id>: <key> ("<label>") | UNDECIDED
|
|
84
|
+
note: <why, verbatim>
|
|
85
|
+
O1 <prompt-id>: <text | UNDECIDED>
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Theme and asset constraints
|
|
89
|
+
|
|
90
|
+
Choose the default theme pair from the primary reader's recorded preference
|
|
91
|
+
when available: `sovereign`/`bedrock`, `meridian`/`nocturne`, or
|
|
92
|
+
`agronomic`/`soil`. Record a newly stated preference in that person's memory
|
|
93
|
+
profile.
|
|
94
|
+
|
|
95
|
+
Keep theme state on `<html data-theme>` and restore it before paint. In authored
|
|
96
|
+
body content and SVGs, every theme-sensitive fill, stroke, background, border,
|
|
97
|
+
and text color must use `var(--token)`. Do not emit inline hex colors. Preserve
|
|
98
|
+
the shell's persisted palette picker and print/mobile behavior.
|
|
99
|
+
|
|
100
|
+
Before publishing:
|
|
101
|
+
|
|
102
|
+
1. Confirm no `__[A-Z_]*__` placeholders remain.
|
|
103
|
+
2. Check tag balance and that every `goFork()` id has one matching
|
|
104
|
+
`data-decision` card.
|
|
105
|
+
3. Grep authored content for raw hex and resolved inline palette values.
|
|
106
|
+
4. Capture both `file://...` and `file://...#harvest` views and inspect them.
|
|
107
|
+
5. Confirm headings use the intended display face rather than a system serif.
|
|
108
|
+
|
|
109
|
+
## Publish and record
|
|
110
|
+
|
|
111
|
+
Publish connected configurations to the permanent slug:
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
bash bin/publish-artifact.sh document tmp/<slug>/index.html --raw-html \
|
|
115
|
+
--id <slug> --title "<title>" --author "<author>" \
|
|
116
|
+
--description "<description>"
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
Verify the returned URL, open it, and report it. Every later version must reuse
|
|
120
|
+
the same `--id`; never mint a new URL for an update. In local mode, publishing
|
|
121
|
+
is unavailable: open the file locally and report the source path without
|
|
122
|
+
suggesting configuration changes.
|
|
123
|
+
|
|
124
|
+
Add the URL or local path, source location, version, and fence tag to the
|
|
125
|
+
relevant quest's log/artifacts tables. If no quest is relevant, keep the source
|
|
126
|
+
path as the durable record and state it.
|
|
127
|
+
|
|
128
|
+
## Update loop
|
|
129
|
+
|
|
130
|
+
On a returned fence:
|
|
131
|
+
|
|
132
|
+
1. **Absorb.** Parse Q/O lines and notes. Write durable decisions with their
|
|
133
|
+
reasoning to `memory/knowledge/decisions/`, update the underlying spec or
|
|
134
|
+
plan, and add connected HarvestTurn records when available. `UNDECIDED` is a
|
|
135
|
+
valid answer.
|
|
136
|
+
2. **Rewrite the face.** Weave settled answers and reasoning into fresh prose,
|
|
137
|
+
mark them `decided v<N> · <date>`, and update linked cost cards to accepted,
|
|
138
|
+
mitigated, or remaining residuals.
|
|
139
|
+
3. **Regenerate the harvest.** Retire answered cards. Carry an undecided fork
|
|
140
|
+
once at most; after two undecided returns, reframe it or demote it to an open
|
|
141
|
+
prompt. If its note actually resolves it, absorb the note as the decision.
|
|
142
|
+
Mint new forks from newly unlocked consequences, respondent requests, and
|
|
143
|
+
new ground truth.
|
|
144
|
+
4. **Show, then ask.** When abstract forks run dry or the respondent asks for
|
|
145
|
+
something concrete, place the draft under discussion directly in the
|
|
146
|
+
harvest view with in-place reaction prompts.
|
|
147
|
+
5. **Version.** Bump every version marker, update the copydock, and append a
|
|
148
|
+
one-line changelog with absorbed and newly opened forks.
|
|
149
|
+
6. **Republish.** Publish to the same stable id, update the quest record, open
|
|
150
|
+
the refreshed URL, and state what changed and what the new version asks.
|
|
151
|
+
|
|
152
|
+
Multiple returns against one version are separate turns. Merge compatible
|
|
153
|
+
answers; turn conflicting choices into a new explicit fork instead of silently
|
|
154
|
+
resolving them.
|
|
155
|
+
|
|
156
|
+
## Voice and telemetry
|
|
157
|
+
|
|
158
|
+
Write the face as external-grade Egregore prose: declarative, precise, grounded
|
|
159
|
+
in real numbers, and honest about costs. Put metaphor in visuals rather than
|
|
160
|
+
labels; prefer established terms over coinages.
|
|
161
|
+
|
|
162
|
+
After completion, emit telemetry best-effort:
|
|
163
|
+
|
|
164
|
+
```bash
|
|
165
|
+
bash bin/telemetry.sh emit "command" '{"command":"scroll"}' 2>/dev/null &
|
|
166
|
+
```
|
|
167
|
+
|
|
168
|
+
Never call the deprecated `egregore-handoff` CLI.
|