create-egregore 0.19.29 → 0.19.31

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.
@@ -1,5 +1,5 @@
1
1
  #!/usr/bin/env node
2
- // egregore-launcher-version: 0.19.29
2
+ // egregore-launcher-version: 0.19.31
3
3
  //
4
4
  // egregore — launch and browse Egregore instances from your terminal.
5
5
  // Installed by create-egregore. Reads ~/.egregore/instances.json.
@@ -33,7 +33,7 @@ const crypto = require("node:crypto");
33
33
  const { spawnSync, spawn } = require("node:child_process");
34
34
  const readline = require("node:readline");
35
35
 
36
- const VERSION = "0.19.29";
36
+ const VERSION = "0.19.31";
37
37
  const REGISTRY = path.join(os.homedir(), ".egregore", "instances.json");
38
38
  const DEFAULT_FILE = path.join(os.homedir(), ".egregore", "default");
39
39
  const UPDATE_CACHE_FILE = path.join(os.homedir(), ".egregore", "update-check.json");
@@ -317,8 +317,12 @@ function writeDefault(slug) {
317
317
  }
318
318
 
319
319
  function resolveBySlug(registry, query) {
320
- // Exact slug → exact name → case-insensitive substring (slug ∪ name)
321
- let hit = registry.find((e) => e.slug === query);
320
+ // Exact path → exact slug → exact name → case-insensitive substring (slug ∪ name).
321
+ // Path first: slugs are repo dir names and can collide across orgs; the
322
+ // installer targets the instance it just created by absolute path.
323
+ let hit = registry.find((e) => e.path === query);
324
+ if (hit) return hit;
325
+ hit = registry.find((e) => e.slug === query);
322
326
  if (hit) return hit;
323
327
  hit = registry.find((e) => e.name === query);
324
328
  if (hit) return hit;
@@ -1407,9 +1411,11 @@ function backfillSummaryLines(summary) {
1407
1411
  }
1408
1412
 
1409
1413
  async function cmdConnect(args) {
1414
+ const fresh = args.includes("--fresh");
1415
+ const positional = args.filter((a) => !String(a).startsWith("--"));
1410
1416
  const registry = readRegistry();
1411
1417
  if (registry.length === 0) die("no egregores installed yet.\n run: npx -y create-egregore@latest");
1412
- const inst = resolveInviteInstance(registry, args[0] || "");
1418
+ const inst = resolveInviteInstance(registry, positional[0] || "");
1413
1419
  if (!inst) {
1414
1420
  die("multiple egregores installed; choose one:\n egregore connect <slug>\n run: egregore ls");
1415
1421
  }
@@ -1466,6 +1472,19 @@ async function cmdConnect(args) {
1466
1472
  !intentId ||
1467
1473
  !resumable
1468
1474
  ) {
1475
+ // Local installs may have skipped platform registration (DO_NOT_TRACK
1476
+ // gates it as telemetry). Connect requires it functionally, and choosing
1477
+ // Connect is the consent — register on demand. Idempotent for members of
1478
+ // already-registered orgs; a 409 (registered by someone else) falls
1479
+ // through to the billing-actor check on intent creation.
1480
+ const registration = await connectedApiRequest(githubToken, "POST", "/api/org/register", {
1481
+ org_name: cfg.org_name || inst.slug,
1482
+ github_org: cfg.github_org || "",
1483
+ slug: cfg.slug || inst.slug,
1484
+ });
1485
+ if (registration.status !== 200 && registration.status !== 409) {
1486
+ die(`could not register this Egregore with the platform: ${connectError(registration, `HTTP ${registration.status}`)}`);
1487
+ }
1469
1488
  console.log(` [1/5] ${DIM}Creating connection intent…${RESET}`);
1470
1489
  const checkout = await connectedApiRequest(
1471
1490
  githubToken,
@@ -1479,6 +1498,7 @@ async function cmdConnect(args) {
1479
1498
  mode: cfg.mode || "local",
1480
1499
  repo_name: cfg.repo_name || "",
1481
1500
  managed_repo_count: Array.isArray(cfg.repos) ? cfg.repos.length : 0,
1501
+ fresh,
1482
1502
  },
1483
1503
  },
1484
1504
  { "X-Idempotency-Key": `cli:${inst.slug}:${crypto.randomUUID()}` },
@@ -1570,24 +1590,30 @@ async function cmdConnect(args) {
1570
1590
  });
1571
1591
  console.log(` [4/5] ${GREEN}✓${RESET} Connected credential and graph node`);
1572
1592
 
1573
- const syncScript = path.join(inst.path, "bin", "sync-graph.sh");
1574
- if (!fs.existsSync(syncScript)) {
1575
- die("bin/sync-graph.sh is missing. Update the installed Egregore version, then run egregore connect again.");
1576
- }
1577
- console.log(` [5/5] ${CYAN}◦${RESET} Projecting existing memory into the graph…`);
1578
- console.log(` ${DIM}Scanning sessions, artifacts, quests, and ingest history${RESET}`);
1579
- const sync = spawnSync("bash", [syncScript], {
1580
- cwd: inst.path,
1581
- encoding: "utf-8",
1582
- stdio: "pipe",
1583
- timeout: 10 * 60 * 1000,
1584
- });
1585
- if (sync.status !== 0) {
1586
- const detail = (sync.stderr || sync.stdout || "graph replay failed").trim();
1587
- die(`Connected is configured, but graph replay did not finish.\n ${detail}\n Run egregore connect ${inst.slug} to retry; existing data is unchanged.`);
1588
- }
1589
1593
  let backfillSummary = { replayed: true };
1590
- try { backfillSummary = JSON.parse((sync.stdout || "").trim()); } catch {}
1594
+ if (fresh) {
1595
+ // Fresh install — no existing memory to project; skip the replay entirely.
1596
+ console.log(` [5/5] ${GREEN}✓${RESET} Fresh instance — nothing to project yet`);
1597
+ backfillSummary = { fresh: true, replayed: false };
1598
+ } else {
1599
+ const syncScript = path.join(inst.path, "bin", "sync-graph.sh");
1600
+ if (!fs.existsSync(syncScript)) {
1601
+ die("bin/sync-graph.sh is missing. Update the installed Egregore version, then run egregore connect again.");
1602
+ }
1603
+ console.log(` [5/5] ${CYAN}◦${RESET} Projecting existing memory into the graph…`);
1604
+ console.log(` ${DIM}Scanning sessions, artifacts, quests, and ingest history${RESET}`);
1605
+ const sync = spawnSync("bash", [syncScript], {
1606
+ cwd: inst.path,
1607
+ encoding: "utf-8",
1608
+ stdio: "pipe",
1609
+ timeout: 10 * 60 * 1000,
1610
+ });
1611
+ if (sync.status !== 0) {
1612
+ const detail = (sync.stderr || sync.stdout || "graph replay failed").trim();
1613
+ die(`Connected is configured, but graph replay did not finish.\n ${detail}\n Run egregore connect ${inst.slug} to retry; existing data is unchanged.`);
1614
+ }
1615
+ try { backfillSummary = JSON.parse((sync.stdout || "").trim()); } catch {}
1616
+ }
1591
1617
 
1592
1618
  let complete;
1593
1619
  for (let attempt = 0; attempt < 3; attempt += 1) {
@@ -1608,9 +1634,11 @@ async function cmdConnect(args) {
1608
1634
  connected_at: new Date().toISOString(),
1609
1635
  backfill_summary: backfillSummary,
1610
1636
  });
1611
- console.log(` [5/5] ${GREEN}✓${RESET} Existing memory projected into the graph`);
1612
- for (const line of backfillSummaryLines(backfillSummary)) {
1613
- console.log(` ${DIM}${line}${RESET}`);
1637
+ if (!fresh) {
1638
+ console.log(` [5/5] ${GREEN}✓${RESET} Existing memory projected into the graph`);
1639
+ for (const line of backfillSummaryLines(backfillSummary)) {
1640
+ console.log(` ${DIM}${line}${RESET}`);
1641
+ }
1614
1642
  }
1615
1643
  const runtimeRefresh = spawnSync(
1616
1644
  "npx",
package/lib/local.js CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  const fs = require("node:fs");
7
7
  const path = require("node:path");
8
- const { execFileSync } = require("node:child_process");
8
+ const { execFileSync, spawnSync } = require("node:child_process");
9
9
  const {
10
10
  GH_INSTALL_URL,
11
11
  ghCliExists,
@@ -1612,9 +1612,43 @@ async function localFounderFlow(ui, ctx = {}) {
1612
1612
  ]);
1613
1613
  }
1614
1614
 
1615
+ await offerConnectUpgrade(egregoreDir, ui, ctx);
1615
1616
  await launchOrInstruct(egregoreDir, alias, ui, ctx);
1616
1617
  }
1617
1618
 
1619
+ // ── End-of-setup Connect choice ────────────────────────────────────
1620
+ //
1621
+ // Same activation the launcher's `c` key runs — delegated to the launcher so
1622
+ // there is exactly one connect flow. `--fresh` skips graph backfill: a brand
1623
+ // new instance has no memory to project. Declining changes nothing; the
1624
+ // launcher offers connect anytime.
1625
+
1626
+ const LAUNCHER_JS = path.join(__dirname, "..", "assets", "egregore-launcher.js");
1627
+
1628
+ async function offerConnectUpgrade(egregoreDir, ui, ctx = {}) {
1629
+ if (ctx.noAutolaunch || !ui.canInteract()) return;
1630
+ console.log("");
1631
+ const choice = await ui.choose("How should this Egregore run?", [
1632
+ {
1633
+ label: "Local",
1634
+ description: "free · Markdown + Git · connect later anytime with egregore connect",
1635
+ value: "local",
1636
+ },
1637
+ {
1638
+ label: "Egregore Connect",
1639
+ description: "€500/month · shared graph + live notifications · Stripe checkout in your browser",
1640
+ value: "connect",
1641
+ },
1642
+ ]);
1643
+ if (!choice || choice.value !== "connect") return;
1644
+ const result = spawnSync(process.execPath, [LAUNCHER_JS, "connect", egregoreDir, "--fresh"], {
1645
+ stdio: "inherit",
1646
+ });
1647
+ if (result.status !== 0) {
1648
+ ui.warn("Connect didn't finish — your Egregore stays local. Run `egregore connect` to pick it back up.");
1649
+ }
1650
+ }
1651
+
1618
1652
  // ── Flow 2: Join (local mode) ──────────────────────────────────────
1619
1653
 
1620
1654
  // Accepts: "org", "org/repo", "https://github.com/org/repo(.git)",
@@ -1817,4 +1851,4 @@ async function localJoinFlow(orgArg, ui, ctx = {}) {
1817
1851
  }
1818
1852
  }
1819
1853
 
1820
- module.exports = { localFounderFlow, localJoinFlow, parseJoinTarget, addCollaborator, getUser, repoExists, createRepo, waitForRepo, retryAsync, ghFallbackNote };
1854
+ module.exports = { localFounderFlow, localJoinFlow, parseJoinTarget, addCollaborator, getUser, repoExists, createRepo, waitForRepo, retryAsync, ghFallbackNote, offerConnectUpgrade };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "create-egregore",
3
- "version": "0.19.29",
3
+ "version": "0.19.31",
4
4
  "description": "Set up Egregore for your team in one command — installs the egregore launcher and your choice of Claude Code, Codex, or Pi.",
5
5
  "license": "MIT",
6
6
  "bin": {
@@ -12,7 +12,7 @@
12
12
  "scripts": {
13
13
  "build:runtime": "node ../../bin/capability-distribution.mjs generate-skill-view --root ../.. && node scripts/build-runtime-bundle.mjs && node scripts/build-pi-runtime-bundle.mjs && node ../../api/scripts/build-connect-runtime-pack.mjs",
14
14
  "build:pi-runtime": "node scripts/build-pi-runtime-bundle.mjs",
15
- "test": "node ../../bin/capability-distribution.mjs validate --root ../.. && node scripts/build-runtime-bundle.mjs --check && node scripts/build-pi-runtime-bundle.mjs --check && node ../../api/scripts/build-connect-runtime-pack.mjs --check && node --test test/capability-distribution.test.js test/connect-runtime.test.js test/context.test.js test/path-lookup.test.js test/nixos-portability.test.js test/parse-join-target.test.js test/skip-repos.test.js test/install-origin-guard.test.js test/onboarding-shortcircuit.test.js test/welcome-screen.test.js test/runtime-selection.test.js test/command-generation.test.js test/auth-gh.test.js test/gauntlet-fixes.test.js test/codex-runtime.test.js test/pi-runtime.test.js test/pi-product-workflows.test.js test/installer-runtime-source.test.js test/scroll-repro.test.js test/launcher-refocus-redraw.test.js test/multiselect-remote-search.test.js test/back-navigation.test.js test/manifest-form.test.js test/launcher-delete.test.js test/launcher-connect.test.js test/launcher-connected-status.test.js test/launcher-permissions.test.js test/launcher-update.test.js test/connected-config.test.js",
15
+ "test": "node ../../bin/capability-distribution.mjs validate --root ../.. && node scripts/build-runtime-bundle.mjs --check && node scripts/build-pi-runtime-bundle.mjs --check && node ../../api/scripts/build-connect-runtime-pack.mjs --check && node --test test/capability-distribution.test.js test/connect-runtime.test.js test/context.test.js test/path-lookup.test.js test/nixos-portability.test.js test/parse-join-target.test.js test/skip-repos.test.js test/install-origin-guard.test.js test/onboarding-shortcircuit.test.js test/welcome-screen.test.js test/runtime-selection.test.js test/command-generation.test.js test/auth-gh.test.js test/gauntlet-fixes.test.js test/codex-runtime.test.js test/pi-runtime.test.js test/pi-product-workflows.test.js test/installer-runtime-source.test.js test/scroll-repro.test.js test/launcher-refocus-redraw.test.js test/multiselect-remote-search.test.js test/back-navigation.test.js test/manifest-form.test.js test/launcher-delete.test.js test/launcher-connect.test.js test/installer-connect-choice.test.js test/launcher-connected-status.test.js test/launcher-permissions.test.js test/launcher-update.test.js test/connected-config.test.js",
16
16
  "test:e2e": "node --test test/e2e-local.test.js",
17
17
  "test:all": "node scripts/build-runtime-bundle.mjs --check && node scripts/build-pi-runtime-bundle.mjs --check && node --test test/"
18
18
  },
@@ -0,0 +1,29 @@
1
+ ---
2
+ name: create-skill
3
+ description: 'Create a skill your org owns — scaffolded in this Egregore, protected from framework updates, shared with teammates via /save.'
4
+ ---
5
+
6
+ <!-- generated-by: bin/codex-sync-skills.sh -->
7
+
8
+ # Egregore create-skill 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/create-skill/SKILL.md` for the workflow details.
19
+ 2. Run the referenced `bin/` scripts directly from Codex.
20
+ 3. Treat graph and publish steps as best-effort unless that workflow explicitly
21
+ says they are required.
22
+ 4. For every external notification, follow
23
+ `.claude/context/notification-consent.md`: plan without sending, then show
24
+ a separate exact Send / Edit / Cancel checkpoint. Never infer notification
25
+ consent from the workflow request or a batch approval.
26
+ 5. Keep local-mode behavior filesystem-first and avoid graph or notification
27
+ calls when `egregore.json` declares `"mode": "local"`.
28
+ 6. Never call the deprecated `egregore-handoff` CLI for Egregore project
29
+ handoffs.
@@ -29,7 +29,7 @@
29
29
  {
30
30
  "section": "On Launch — MANDATORY FIRST ACTION",
31
31
  "action": "replace",
32
- "sourceHash": "3def4421213a",
32
+ "sourceHash": "233d160f9ca8",
33
33
  "note": "Claude SessionStart hook does not run on Codex; the egregore launcher renders the card via bin/codex-session-start.sh before the session. Greeting-replay instruction dropped (card is already on screen)."
34
34
  },
35
35
  {
@@ -41,7 +41,7 @@
41
41
  {
42
42
  "section": "Config Files",
43
43
  "action": "keep",
44
- "sourceHash": "8d7f1bd0f2d9",
44
+ "sourceHash": "4ca75fe428ca",
45
45
  "note": "Kept verbatim — shell facts, runtime-neutral."
46
46
  },
47
47
  {
@@ -101,7 +101,7 @@
101
101
  {
102
102
  "section": "Command Awareness",
103
103
  "action": "keep",
104
- "sourceHash": "d6fe47a75c00",
104
+ "sourceHash": "905ad739e18c",
105
105
  "note": "Prepended the Codex command surface (skill tokens, native vs adapter split, $save abstraction); disambiguation map kept with /x -> $x token rendering. Tokens with no Codex skill (e.g. /loop, a Claude Code harness feature) are left as-is."
106
106
  },
107
107
  {
@@ -216,7 +216,7 @@ If the startup card output contains `onboarding_needed`, invoke the `$onboarding
216
216
 
217
217
  ## Config Files
218
218
 
219
- - **`egregore.json`** — committed. Non-secret org config: `org_name`, `github_org`, `memory_repo`, `slug`, `mode`. `api_url` is connected-mode only. Optional `boundary { posture, read[], locked }` sets the org's isolation posture (see Environment Isolation). **Never put secrets here.**
219
+ - **`egregore.json`** — committed. Non-secret org config: `org_name`, `github_org`, `memory_repo`, `slug`, `mode`. `api_url` is connected-mode only. Optional `boundary { posture, read[], locked }` sets the org's isolation posture (see Environment Isolation). Optional `owned_skills[]` names org-authored skills in `.claude/skills/` that `$update` must never overwrite (created via `$create-skill`). **Never put secrets here.**
220
220
  - **`.env`** — gitignored. Personal secrets. Local mode: `GITHUB_TOKEN` only. Connected mode: `GITHUB_TOKEN` + `EGREGORE_API_KEY`. **Never use `source .env`** — use `grep '^KEY=' .env | cut -d'=' -f2-`.
221
221
 
222
222
  In connected mode, infrastructure credentials (Neo4j, Telegram) live on the API server only — `bin/graph.sh` and `bin/notify.sh` route through the API gateway.
@@ -395,9 +395,10 @@ Invoke commands from user intent — don't wait for the slash. Each command file
395
395
  **Knowledge** — `$search` `$deep-reflect` `$archive` `$note` `$add` `$meeting` `$ingest` `$scroll` `$mock` `$audit`
396
396
  **Identity** — `$me` (view profile or set display name)
397
397
  **Coordination** — `$ask` `$quest` `$issue` `$invite` `$delete-user` `$announce`
398
- **Connectors** — `$telegram-connect` (Telegram group setup) `$teams-connect` (Microsoft Teams channel setup)
398
+ **Connectors** — `$notion-connect` (Notion workspace) `$telegram-connect` (Telegram group setup) `$teams-connect` (Microsoft Teams channel setup)
399
399
  **Git** — `$branch` `$commit` `$push` `$pr` `$save` `$review-pr` `$contribute`
400
400
  **Spirits** — `$summon` (persistent agent processes)
401
+ **Skills** — `$create-skill` (org-owned skill: scaffold, protect from updates, share)
401
402
  **Infra** — `$setup` `$update` `$pull` `$env` `$infra` `$sync-repos` `$release` `$checkup`
402
403
 
403
404
  **Disambiguation:**
@@ -410,11 +411,12 @@ Invoke commands from user intent — don't wait for the slash. Each command file
410
411
  - Tasks: `$todo` (personal) · `$quest` (team exploration) · `$issue` (something broken)
411
412
  - Questions: `$ask [person]` (async) · just ask (agent answers from context)
412
413
  - Ingestion: `$ingest <file-or-folder>` (org-scoped corpus intake) · `$ingest meeting` · `$ingest user-interview` · `$ingest google` · ambiguous → ask which type
413
- - Connectors: `$telegram-connect` (Telegram group) · `$teams-connect` (MS Teams channel) · `$ingest` (bring content in)
414
+ - Connectors: `$notion-connect` (Notion workspace) · `$telegram-connect` (Telegram group) · `$teams-connect` (MS Teams channel) · `$ingest` (bring content in) · `$ingest notion` (promote Notion docs)
414
415
  - Identity: `$me` — "who am I", "call me oz"
415
416
  - People: `$invite` (add) · `$delete-user` (remove)
416
417
  - PRs: `$pr` (create) · `$review-pr` (review)
417
418
  - Contributing: `$contribute` (upstream framework) · `$save` (org repo) · `$issue` (report bug)
419
+ - Skills: `$create-skill` (new org-owned skill) · `$contribute` (change a framework skill for everyone)
418
420
  - Agents: `$summon` (design through questions) · `/loop` (quick recurring schedule)
419
421
  - Announcements: `$announce` (broadcast to group) · `$handoff` (structured to a person) · `bin/notify.sh send` (DM one person)
420
422
 
@@ -15,7 +15,7 @@ ROOT = Path(__file__).resolve().parent.parent
15
15
  CORPUS_SCHEMA = "egregore-ingest-corpus/v1"
16
16
  KNOWLEDGE_SCHEMA = "egregore-knowledge-projection/v1"
17
17
  PLAN_SCHEMA = "egregore-ingest-graph-plan/v2"
18
- SOURCE_KINDS = {"meeting", "interview", "google", "corpus", "document"}
18
+ SOURCE_KINDS = {"meeting", "interview", "google", "notion", "corpus", "document"}
19
19
  SOURCE_RELATIONS = {"INVOLVES", "CONDUCTED_BY"}
20
20
  ARTIFACT_RELATIONS = {"RELATES_TO", "SUPERSEDES"}
21
21
  ARTIFACT_LABELS = {
@@ -438,6 +438,8 @@ def source_shape(kind: str) -> tuple[str, str]:
438
438
  return "Interview:KnowledgeSource", "FROM_INTERVIEW"
439
439
  if kind == "google":
440
440
  return "Artifact:KnowledgeSource", "DERIVED_FROM"
441
+ if kind == "notion":
442
+ return "Artifact:KnowledgeSource", "DERIVED_FROM"
441
443
  if kind == "corpus":
442
444
  return "IngestSource:KnowledgeSource", "DERIVED_FROM"
443
445
  return "IngestDocument:Document:KnowledgeSource", "DERIVED_FROM"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "source": "egregore-runtime",
3
3
  "policy": "available OSS components selected by capability-distribution.json; Connect is an authenticated additive overlay",
4
- "skillCount": 41,
4
+ "skillCount": 42,
5
5
  "skills": [
6
6
  "activity",
7
7
  "announce",
@@ -10,6 +10,7 @@
10
10
  "checkup",
11
11
  "commit",
12
12
  "contribute",
13
+ "create-skill",
13
14
  "dark-mode",
14
15
  "dashboard",
15
16
  "deep-reflect",
@@ -60,6 +61,7 @@
60
61
  ".codex/skills/checkup/SKILL.md",
61
62
  ".codex/skills/commit/SKILL.md",
62
63
  ".codex/skills/contribute/SKILL.md",
64
+ ".codex/skills/create-skill/SKILL.md",
63
65
  ".codex/skills/dark-mode/SKILL.md",
64
66
  ".codex/skills/dashboard/SKILL.md",
65
67
  ".codex/skills/deep-reflect/SKILL.md",
@@ -0,0 +1,29 @@
1
+ ---
2
+ name: create-skill
3
+ description: 'Create a skill your org owns — scaffolded in this Egregore, protected from framework updates, shared with teammates via /save.'
4
+ ---
5
+
6
+ <!-- generated-by: bin/codex-sync-skills.sh -->
7
+
8
+ # Egregore create-skill 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/create-skill/SKILL.md` for the workflow details.
19
+ 2. Run the referenced `bin/` scripts directly from Codex.
20
+ 3. Treat graph and publish steps as best-effort unless that workflow explicitly
21
+ says they are required.
22
+ 4. For every external notification, follow
23
+ `.claude/context/notification-consent.md`: plan without sending, then show
24
+ a separate exact Send / Edit / Cancel checkpoint. Never infer notification
25
+ consent from the workflow request or a batch approval.
26
+ 5. Keep local-mode behavior filesystem-first and avoid graph or notification
27
+ calls when `egregore.json` declares `"mode": "local"`.
28
+ 6. Never call the deprecated `egregore-handoff` CLI for Egregore project
29
+ handoffs.
@@ -29,7 +29,7 @@
29
29
  {
30
30
  "section": "On Launch — MANDATORY FIRST ACTION",
31
31
  "action": "replace",
32
- "sourceHash": "3def4421213a",
32
+ "sourceHash": "233d160f9ca8",
33
33
  "note": "Claude SessionStart hook does not run on Codex; the egregore launcher renders the card via bin/codex-session-start.sh before the session. Greeting-replay instruction dropped (card is already on screen)."
34
34
  },
35
35
  {
@@ -41,7 +41,7 @@
41
41
  {
42
42
  "section": "Config Files",
43
43
  "action": "keep",
44
- "sourceHash": "8d7f1bd0f2d9",
44
+ "sourceHash": "4ca75fe428ca",
45
45
  "note": "Kept verbatim — shell facts, runtime-neutral."
46
46
  },
47
47
  {
@@ -101,7 +101,7 @@
101
101
  {
102
102
  "section": "Command Awareness",
103
103
  "action": "keep",
104
- "sourceHash": "d6fe47a75c00",
104
+ "sourceHash": "905ad739e18c",
105
105
  "note": "Prepended the Codex command surface (skill tokens, native vs adapter split, $save abstraction); disambiguation map kept with /x -> $x token rendering. Tokens with no Codex skill (e.g. /loop, a Claude Code harness feature) are left as-is."
106
106
  },
107
107
  {
@@ -142,7 +142,7 @@ If the startup card output contains `onboarding_needed`, invoke the `/onboarding
142
142
 
143
143
  ## Config Files
144
144
 
145
- - **`egregore.json`** — committed. Non-secret org config: `org_name`, `github_org`, `memory_repo`, `slug`, `mode`. `api_url` is connected-mode only. Optional `boundary { posture, read[], locked }` sets the org's isolation posture (see Environment Isolation). **Never put secrets here.**
145
+ - **`egregore.json`** — committed. Non-secret org config: `org_name`, `github_org`, `memory_repo`, `slug`, `mode`. `api_url` is connected-mode only. Optional `boundary { posture, read[], locked }` sets the org's isolation posture (see Environment Isolation). Optional `owned_skills[]` names org-authored skills in `.claude/skills/` that `/update` must never overwrite (created via `/create-skill`). **Never put secrets here.**
146
146
  - **`.env`** — gitignored. Personal secrets. Local mode: `GITHUB_TOKEN` only. Connected mode: `GITHUB_TOKEN` + `EGREGORE_API_KEY`. **Never use `source .env`** — use `grep '^KEY=' .env | cut -d'=' -f2-`.
147
147
 
148
148
  In connected mode, infrastructure credentials (Neo4j, Telegram) live on the API server only — `bin/graph.sh` and `bin/notify.sh` route through the API gateway.
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "generatedBy": "bin/pi-render-spec.mjs",
3
3
  "source": "AGENTS.md generated Codex spec",
4
- "sourceHash": "6247d8070669",
5
- "outputHash": "117a6f3cafc3",
4
+ "sourceHash": "02c4d2d275db",
5
+ "outputHash": "dc9d961b8ce2",
6
6
  "adaptations": [
7
7
  "Pi project trust and APPEND_SYSTEM delivery",
8
8
  "Pi tool_call branch gate",
@@ -216,7 +216,7 @@ If the startup card output contains `onboarding_needed`, invoke the `$onboarding
216
216
 
217
217
  ## Config Files
218
218
 
219
- - **`egregore.json`** — committed. Non-secret org config: `org_name`, `github_org`, `memory_repo`, `slug`, `mode`. `api_url` is connected-mode only. Optional `boundary { posture, read[], locked }` sets the org's isolation posture (see Environment Isolation). **Never put secrets here.**
219
+ - **`egregore.json`** — committed. Non-secret org config: `org_name`, `github_org`, `memory_repo`, `slug`, `mode`. `api_url` is connected-mode only. Optional `boundary { posture, read[], locked }` sets the org's isolation posture (see Environment Isolation). Optional `owned_skills[]` names org-authored skills in `.claude/skills/` that `$update` must never overwrite (created via `$create-skill`). **Never put secrets here.**
220
220
  - **`.env`** — gitignored. Personal secrets. Local mode: `GITHUB_TOKEN` only. Connected mode: `GITHUB_TOKEN` + `EGREGORE_API_KEY`. **Never use `source .env`** — use `grep '^KEY=' .env | cut -d'=' -f2-`.
221
221
 
222
222
  In connected mode, infrastructure credentials (Neo4j, Telegram) live on the API server only — `bin/graph.sh` and `bin/notify.sh` route through the API gateway.
@@ -395,9 +395,10 @@ Invoke commands from user intent — don't wait for the slash. Each command file
395
395
  **Knowledge** — `$search` `$deep-reflect` `$archive` `$note` `$add` `$meeting` `$ingest` `$scroll` `$mock` `$audit`
396
396
  **Identity** — `$me` (view profile or set display name)
397
397
  **Coordination** — `$ask` `$quest` `$issue` `$invite` `$delete-user` `$announce`
398
- **Connectors** — `$telegram-connect` (Telegram group setup) `$teams-connect` (Microsoft Teams channel setup)
398
+ **Connectors** — `$notion-connect` (Notion workspace) `$telegram-connect` (Telegram group setup) `$teams-connect` (Microsoft Teams channel setup)
399
399
  **Git** — `$branch` `$commit` `$push` `$pr` `$save` `$review-pr` `$contribute`
400
400
  **Spirits** — `$summon` (persistent agent processes)
401
+ **Skills** — `$create-skill` (org-owned skill: scaffold, protect from updates, share)
401
402
  **Infra** — `$setup` `$update` `$pull` `$env` `$infra` `$sync-repos` `$release` `$checkup`
402
403
 
403
404
  **Disambiguation:**
@@ -410,11 +411,12 @@ Invoke commands from user intent — don't wait for the slash. Each command file
410
411
  - Tasks: `$todo` (personal) · `$quest` (team exploration) · `$issue` (something broken)
411
412
  - Questions: `$ask [person]` (async) · just ask (agent answers from context)
412
413
  - Ingestion: `$ingest <file-or-folder>` (org-scoped corpus intake) · `$ingest meeting` · `$ingest user-interview` · `$ingest google` · ambiguous → ask which type
413
- - Connectors: `$telegram-connect` (Telegram group) · `$teams-connect` (MS Teams channel) · `$ingest` (bring content in)
414
+ - Connectors: `$notion-connect` (Notion workspace) · `$telegram-connect` (Telegram group) · `$teams-connect` (MS Teams channel) · `$ingest` (bring content in) · `$ingest notion` (promote Notion docs)
414
415
  - Identity: `$me` — "who am I", "call me oz"
415
416
  - People: `$invite` (add) · `$delete-user` (remove)
416
417
  - PRs: `$pr` (create) · `$review-pr` (review)
417
418
  - Contributing: `$contribute` (upstream framework) · `$save` (org repo) · `$issue` (report bug)
419
+ - Skills: `$create-skill` (new org-owned skill) · `$contribute` (change a framework skill for everyone)
418
420
  - Agents: `$summon` (design through questions) · `/loop` (quick recurring schedule)
419
421
  - Announcements: `$announce` (broadcast to group) · `$handoff` (structured to a person) · `bin/notify.sh send` (DM one person)
420
422
 
@@ -15,7 +15,7 @@ ROOT = Path(__file__).resolve().parent.parent
15
15
  CORPUS_SCHEMA = "egregore-ingest-corpus/v1"
16
16
  KNOWLEDGE_SCHEMA = "egregore-knowledge-projection/v1"
17
17
  PLAN_SCHEMA = "egregore-ingest-graph-plan/v2"
18
- SOURCE_KINDS = {"meeting", "interview", "google", "corpus", "document"}
18
+ SOURCE_KINDS = {"meeting", "interview", "google", "notion", "corpus", "document"}
19
19
  SOURCE_RELATIONS = {"INVOLVES", "CONDUCTED_BY"}
20
20
  ARTIFACT_RELATIONS = {"RELATES_TO", "SUPERSEDES"}
21
21
  ARTIFACT_LABELS = {
@@ -438,6 +438,8 @@ def source_shape(kind: str) -> tuple[str, str]:
438
438
  return "Interview:KnowledgeSource", "FROM_INTERVIEW"
439
439
  if kind == "google":
440
440
  return "Artifact:KnowledgeSource", "DERIVED_FROM"
441
+ if kind == "notion":
442
+ return "Artifact:KnowledgeSource", "DERIVED_FROM"
441
443
  if kind == "corpus":
442
444
  return "IngestSource:KnowledgeSource", "DERIVED_FROM"
443
445
  return "IngestDocument:Document:KnowledgeSource", "DERIVED_FROM"
@@ -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": 41,
4
+ "skillCount": 42,
5
5
  "skills": [
6
6
  "activity",
7
7
  "announce",
@@ -10,6 +10,7 @@
10
10
  "checkup",
11
11
  "commit",
12
12
  "contribute",
13
+ "create-skill",
13
14
  "dark-mode",
14
15
  "dashboard",
15
16
  "deep-reflect",
@@ -59,6 +60,7 @@
59
60
  ".codex/skills/checkup/SKILL.md",
60
61
  ".codex/skills/commit/SKILL.md",
61
62
  ".codex/skills/contribute/SKILL.md",
63
+ ".codex/skills/create-skill/SKILL.md",
62
64
  ".codex/skills/dark-mode/SKILL.md",
63
65
  ".codex/skills/dashboard/SKILL.md",
64
66
  ".codex/skills/deep-reflect/SKILL.md",