create-egregore 0.20.1 → 0.20.3

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.
Files changed (58) hide show
  1. package/assets/egregore-launcher.js +34 -19
  2. package/bin/cli.js +3 -63
  3. package/lib/local.js +4 -20
  4. package/lib/setup.js +12 -16
  5. package/package.json +2 -2
  6. package/runtime/codex/.codex/spec-manifest.json +3 -3
  7. package/runtime/codex/AGENTS.md +23 -9
  8. package/runtime/codex/bin/agent.sh +15 -3
  9. package/runtime/codex/bin/attendant.sh +14 -301
  10. package/runtime/codex/bin/autosave.sh +1 -27
  11. package/runtime/codex/bin/codex-render-spec.mjs +5 -3
  12. package/runtime/codex/bin/codex-session-start.sh +3 -3
  13. package/runtime/codex/bin/contribute-guard.sh +79 -0
  14. package/runtime/codex/bin/handoff-save-egregore.sh +1 -1
  15. package/runtime/codex/bin/lib/git-sync.sh +16 -0
  16. package/runtime/codex/bin/lib/greeting.sh +2 -2
  17. package/runtime/codex/bin/search.sh +46 -4
  18. package/runtime/codex/bin/session-autosave.sh +6 -1
  19. package/runtime/codex/bin/settings.sh +62 -3
  20. package/runtime/codex/bin/worktree-create.sh +4 -1
  21. package/runtime/codex/manifest.json +1 -0
  22. package/runtime/pi/.codex/spec-manifest.json +3 -3
  23. package/runtime/pi/.pi/APPEND_SYSTEM.md +23 -9
  24. package/runtime/pi/.pi/spec-manifest.json +2 -2
  25. package/runtime/pi/AGENTS.md +23 -9
  26. package/runtime/pi/bin/agent.sh +15 -3
  27. package/runtime/pi/bin/attendant.sh +14 -301
  28. package/runtime/pi/bin/autosave.sh +1 -27
  29. package/runtime/pi/bin/codex-render-spec.mjs +5 -3
  30. package/runtime/pi/bin/codex-session-start.sh +3 -3
  31. package/runtime/pi/bin/contribute-guard.sh +79 -0
  32. package/runtime/pi/bin/handoff-save-egregore.sh +1 -1
  33. package/runtime/pi/bin/lib/git-sync.sh +16 -0
  34. package/runtime/pi/bin/lib/greeting.sh +2 -2
  35. package/runtime/pi/bin/search.sh +46 -4
  36. package/runtime/pi/bin/session-autosave.sh +6 -1
  37. package/runtime/pi/bin/settings.sh +62 -3
  38. package/runtime/pi/bin/worktree-create.sh +4 -1
  39. package/runtime/pi/manifest.json +1 -0
  40. package/runtime/prime/.codex/spec-manifest.json +3 -3
  41. package/runtime/prime/.prime/agent/APPEND_SYSTEM.md +24 -10
  42. package/runtime/prime/.prime/agent/spec-manifest.json +2 -2
  43. package/runtime/prime/AGENTS.md +23 -9
  44. package/runtime/prime/bin/agent.sh +15 -3
  45. package/runtime/prime/bin/attendant.sh +14 -301
  46. package/runtime/prime/bin/autosave.sh +1 -27
  47. package/runtime/prime/bin/codex-render-spec.mjs +5 -3
  48. package/runtime/prime/bin/codex-session-start.sh +3 -3
  49. package/runtime/prime/bin/contribute-guard.sh +79 -0
  50. package/runtime/prime/bin/handoff-save-egregore.sh +1 -1
  51. package/runtime/prime/bin/lib/git-sync.sh +16 -0
  52. package/runtime/prime/bin/lib/greeting.sh +2 -2
  53. package/runtime/prime/bin/prime-render-spec.mjs +1 -1
  54. package/runtime/prime/bin/search.sh +46 -4
  55. package/runtime/prime/bin/session-autosave.sh +6 -1
  56. package/runtime/prime/bin/settings.sh +62 -3
  57. package/runtime/prime/bin/worktree-create.sh +4 -1
  58. package/runtime/prime/manifest.json +1 -0
@@ -29,6 +29,48 @@ SLUG=$(jq -r '.slug // "egregore"' "$SCRIPT_DIR/egregore.json" 2>/dev/null || ec
29
29
  COLLECTION="${SLUG}-memory"
30
30
  MEM_PATH="$(readlink -f "$SCRIPT_DIR/memory" 2>/dev/null || true)"
31
31
 
32
+ # qmd 2.5.x may render --full-path results as absolute `**file:**` paths,
33
+ # while older output uses qmd:// URIs. Normalize both to the canonical
34
+ # memory/... form users can open from an Egregore checkout, and parse both
35
+ # forms when counting hits or attaching graph state.
36
+ _normalize_file_paths() {
37
+ local line absolute_prefix uri_prefix
38
+ absolute_prefix="**file:** \`$MEM_PATH/"
39
+ uri_prefix="**file:** \`qmd://${COLLECTION}/"
40
+ while IFS= read -r line || [ -n "$line" ]; do
41
+ if [[ "$line" == "$absolute_prefix"* ]]; then
42
+ printf '**file:** `memory/%s\n' "${line#"$absolute_prefix"}"
43
+ elif [[ "$line" == "$uri_prefix"* ]]; then
44
+ printf '**file:** `memory/%s\n' "${line#"$uri_prefix"}"
45
+ else
46
+ printf '%s\n' "$line"
47
+ fi
48
+ done
49
+ }
50
+
51
+ _result_paths() {
52
+ local line path canonical_prefix absolute_prefix uri_prefix
53
+ canonical_prefix="**file:** \`memory/"
54
+ absolute_prefix="**file:** \`$MEM_PATH/"
55
+ uri_prefix="**file:** \`qmd://${COLLECTION}/"
56
+ while IFS= read -r line || [ -n "$line" ]; do
57
+ path=""
58
+ if [[ "$line" == "$canonical_prefix"* ]]; then
59
+ path="${line#"$canonical_prefix"}"
60
+ elif [[ "$line" == "$absolute_prefix"* ]]; then
61
+ path="${line#"$absolute_prefix"}"
62
+ elif [[ "$line" == "$uri_prefix"* ]]; then
63
+ path="${line#"$uri_prefix"}"
64
+ elif [[ "$line" == *"qmd://${COLLECTION}/"* ]]; then
65
+ path="${line#*"qmd://${COLLECTION}/"}"
66
+ fi
67
+ path="${path%%\`*}"
68
+ case "$path" in
69
+ *.md*) printf '%s\n' "${path%%.md*}.md" ;;
70
+ esac
71
+ done | sort -u
72
+ }
73
+
32
74
  QMD_VERSION="2.5.3" # pinned — bump deliberately, never float @latest
33
75
 
34
76
  _qmd() {
@@ -104,7 +146,7 @@ _enrich() {
104
146
  local results paths params out
105
147
  results=$(cat)
106
148
  echo "$results"
107
- paths=$(echo "$results" | grep -o "qmd://${COLLECTION}/[^: ]*\.md" | sed "s|qmd://${COLLECTION}/||" | sort -u)
149
+ paths=$(printf '%s\n' "$results" | _result_paths)
108
150
  [ -z "$paths" ] && return 0
109
151
  params=$(printf '%s\n' "$paths" | jq -R . | jq -sc '{paths: .}')
110
152
  out=$(bash "$SCRIPT_DIR/bin/graph.sh" query "UNWIND \$paths AS fp
@@ -167,7 +209,7 @@ cmd_query() {
167
209
  local t0 out label
168
210
  t0=$(date +%s)
169
211
  if [ "$mode" != "search" ]; then
170
- out=$(_qmd "$mode" "$q" -c "$COLLECTION" -n "$n" --format md --full-path "${all[@]}" 2>/dev/null | _clean)
212
+ out=$(_qmd "$mode" "$q" -c "$COLLECTION" -n "$n" --format md --full-path "${all[@]}" 2>/dev/null | _clean | _normalize_file_paths)
171
213
  if [ -n "$out" ] && [ "$out" != "No results found." ]; then
172
214
  [ "$mode" = "query" ] && label="hybrid" || label="semantic"
173
215
  _banner_and_emit "$label" "$t0" "$emit" "$q" "$out"
@@ -176,7 +218,7 @@ cmd_query() {
176
218
  echo "_(hybrid/semantic returned nothing — showing keyword results. If this persists, run: bash bin/search.sh reindex --embed)_"
177
219
  echo ""
178
220
  fi
179
- out=$(_qmd search "$q" -c "$COLLECTION" -n "$n" --format md --full-path "${all[@]}" | _clean)
221
+ out=$(_qmd search "$q" -c "$COLLECTION" -n "$n" --format md --full-path "${all[@]}" | _clean | _normalize_file_paths)
180
222
  _banner_and_emit "keyword" "$t0" "$emit" "$q" "$out"
181
223
  }
182
224
 
@@ -189,7 +231,7 @@ cmd_query() {
189
231
  _banner_and_emit() {
190
232
  local label="$1" t0="$2" emit="$3" q="$4" out="$5"
191
233
  local hits dur modef product surface plural=""
192
- hits=$(printf '%s\n' "$out" | grep -c '^qmd://' 2>/dev/null | tr -d ' ')
234
+ hits=$(printf '%s\n' "$out" | _result_paths | wc -l | tr -d ' ')
193
235
  dur=$(( $(date +%s) - t0 ))
194
236
  modef="$label"
195
237
  product="Egregore"
@@ -85,7 +85,12 @@ git rev-parse --git-dir >/dev/null 2>&1 || exit 0
85
85
  DIR_HASH=$( { md5sum <<<"$DIR" 2>/dev/null || md5 -q -s "$DIR" 2>/dev/null || echo default; } | cut -c1-8 )
86
86
  STAMP="/tmp/.egregore-autosave-${DIR_HASH}"
87
87
  if [ "$DEBOUNCE" -gt 0 ] && [ -f "$STAMP" ]; then
88
- LAST=$(stat -f %m "$STAMP" 2>/dev/null || stat -c %Y "$STAMP" 2>/dev/null || echo 0)
88
+ if stat -c %Y "$STAMP" >/dev/null 2>&1; then
89
+ LAST=$(stat -c %Y "$STAMP")
90
+ else
91
+ LAST=$(stat -f %m "$STAMP" 2>/dev/null || echo 0)
92
+ fi
93
+ case "$LAST" in ""|*[!0-9]*) LAST=0 ;; esac
89
94
  [ $(( $(date +%s) - LAST )) -lt "$DEBOUNCE" ] && exit 0
90
95
  fi
91
96
  touch "$STAMP" 2>/dev/null || true
@@ -3,14 +3,16 @@ set -euo pipefail
3
3
 
4
4
  # settings.sh — deterministic config verbs for an Egregore instance.
5
5
  #
6
- # Edits egregore.json atomically. No agent, no network, no session. Idempotent:
7
- # re-adding an existing item is a no-op success; removing a missing one is too.
6
+ # Edits egregore.json atomically. No agent, no session. Idempotent: re-adding
7
+ # an existing item is a no-op success; removing a missing one is too. Switching
8
+ # to staged releases is the one networked verb: it creates origin/develop first.
8
9
  # This is the deterministic core the launcher settings screen (and slash
9
10
  # commands) call — the reliability lives here, the surfaces are thin callers.
10
11
  #
11
12
  # Usage:
12
13
  # settings.sh hosting status|on|off
13
14
  # settings.sh relay status|on|off
15
+ # settings.sh workflow status|simple|staged
14
16
  # settings.sh repo list | add <name> [description] | remove <name>
15
17
  # settings.sh admin list | add <github-handle> | remove <github-handle>
16
18
  # settings.sh dump # full settings snapshot as JSON (for the launcher)
@@ -53,6 +55,7 @@ settings.sh — Egregore instance settings
53
55
 
54
56
  settings.sh hosting status|on|off
55
57
  settings.sh relay status|on|off
58
+ settings.sh workflow status|simple|staged
56
59
  settings.sh repo list | add <name> [description] | remove <name>
57
60
  settings.sh admin list | add <github-handle> | remove <github-handle>
58
61
  settings.sh people list | add <github-handle> | remove <github-handle>
@@ -114,6 +117,54 @@ cmd_relay() {
114
117
  esac
115
118
  }
116
119
 
120
+ cmd_workflow() {
121
+ local action="${1:-status}"
122
+ local base
123
+ base="$(jq -r 'if has("base_branch") then .base_branch else "develop" end' "$CONFIG" 2>/dev/null)"
124
+ case "$action" in
125
+ status)
126
+ local workflow="custom"
127
+ [ "$base" = "main" ] && workflow="simple"
128
+ [ "$base" = "develop" ] && workflow="staged"
129
+ if [ "$JSON" = 1 ]; then
130
+ jq -n --arg workflow "$workflow" --arg base_branch "$base" \
131
+ '{domain:"workflow", workflow:$workflow, base_branch:$base_branch}'
132
+ elif [ "$workflow" = "simple" ]; then
133
+ echo "Git workflow: SIMPLE — pull requests target main"
134
+ elif [ "$workflow" = "staged" ]; then
135
+ echo "Git workflow: STAGED — pull requests target develop; releases promote to main"
136
+ else
137
+ echo "Git workflow: CUSTOM — pull requests target $base"
138
+ fi
139
+ ;;
140
+ simple)
141
+ if [ "$base" != "main" ]; then
142
+ git -C "$SCRIPT_DIR" rev-parse --git-dir >/dev/null 2>&1 \
143
+ || _die "cannot switch workflow: $SCRIPT_DIR is not a Git repository" 1
144
+ git -C "$SCRIPT_DIR" ls-remote --exit-code --heads origin main >/dev/null 2>&1 \
145
+ || _die "cannot switch workflow: origin/main does not exist or could not be reached" 1
146
+ _save '.base_branch = "main"'
147
+ fi
148
+ echo "Git workflow: SIMPLE — pull requests now target main"
149
+ ;;
150
+ staged)
151
+ if [ "$base" != "develop" ]; then
152
+ git -C "$SCRIPT_DIR" rev-parse --git-dir >/dev/null 2>&1 \
153
+ || _die "cannot switch workflow: $SCRIPT_DIR is not a Git repository" 1
154
+ bash "$SCRIPT_DIR/bin/lib/ensure-develop.sh" "$SCRIPT_DIR" >/dev/null \
155
+ || _die "could not create develop; workflow is unchanged" 1
156
+ git -C "$SCRIPT_DIR" ls-remote --exit-code --heads origin develop >/dev/null 2>&1 \
157
+ || _die "could not verify origin/develop; workflow is unchanged" 1
158
+ _save '.base_branch = "develop"'
159
+ elif ! jq -e 'has("base_branch")' "$CONFIG" >/dev/null 2>&1; then
160
+ _save '.base_branch = "develop"'
161
+ fi
162
+ echo "Git workflow: STAGED — pull requests now target develop; releases promote to main"
163
+ ;;
164
+ *) _die "usage: settings.sh workflow status|simple|staged" 1 ;;
165
+ esac
166
+ }
167
+
117
168
  cmd_repo() {
118
169
  local action="${1:-list}"; shift || true
119
170
  case "$action" in
@@ -273,6 +324,13 @@ cmd_dump() {
273
324
  slug: .slug,
274
325
  hosting: (.features.publishing != false),
275
326
  public_relay: (.features.public_relay == true),
327
+ workflow: (
328
+ if ((if has("base_branch") then .base_branch else "develop" end) == "main") then "simple"
329
+ elif ((if has("base_branch") then .base_branch else "develop" end) == "develop") then "staged"
330
+ else "custom"
331
+ end
332
+ ),
333
+ base_branch: (if has("base_branch") then .base_branch else "develop" end),
276
334
  repos: [.repos[]? | if type=="object" then . else {name:.} end],
277
335
  admins: (.admins // []),
278
336
  people: $people
@@ -295,10 +353,11 @@ DOMAIN="${1:-}"; shift || true
295
353
  case "$DOMAIN" in
296
354
  hosting) cmd_hosting "$@" ;;
297
355
  relay) cmd_relay "$@" ;;
356
+ workflow) cmd_workflow "$@" ;;
298
357
  repo) cmd_repo "$@" ;;
299
358
  admin) cmd_admin "$@" ;;
300
359
  people) cmd_people "$@" ;;
301
360
  dump) cmd_dump ;;
302
361
  ""|-h|--help|help) usage 0 ;;
303
- *) _die "unknown settings domain: '$DOMAIN' (try: hosting, relay, repo, admin, people, dump)" 1 ;;
362
+ *) _die "unknown settings domain: '$DOMAIN' (try: hosting, relay, workflow, repo, admin, people, dump)" 1 ;;
304
363
  esac
@@ -48,7 +48,10 @@ git -C "$REPO_ROOT" fetch origin "$BASE_BRANCH" --quiet 2>/dev/null || true
48
48
 
49
49
  # --- Create branch (idempotent — skip if exists) ---
50
50
  if ! git -C "$REPO_ROOT" show-ref --verify --quiet "refs/heads/$BRANCH" 2>/dev/null; then
51
- git -C "$REPO_ROOT" branch "$BRANCH" "origin/$BASE_BRANCH" >/dev/null 2>&1 || {
51
+ # A task branch starts at the integration branch but must never track it.
52
+ # Otherwise `git push` can target origin/$BASE_BRANCH (especially when the
53
+ # user's push.default is `upstream`) before the first explicit task push.
54
+ git -C "$REPO_ROOT" branch --no-track "$BRANCH" "origin/$BASE_BRANCH" >/dev/null 2>&1 || {
52
55
  echo "Error: failed to create branch $BRANCH" >&2
53
56
  exit 1
54
57
  }
@@ -123,6 +123,7 @@
123
123
  "bin/codex-skill-render.mjs",
124
124
  "bin/codex-sync-skills.sh",
125
125
  "bin/connect-refresh.sh",
126
+ "bin/contribute-guard.sh",
126
127
  "bin/dashboard-data.sh",
127
128
  "bin/graph-batch.sh",
128
129
  "bin/graph-op.sh",
@@ -11,7 +11,7 @@
11
11
  {
12
12
  "section": "Identity & Upstream",
13
13
  "action": "keep",
14
- "sourceHash": "ddcbc0083aff",
14
+ "sourceHash": "86a2c62ce498",
15
15
  "note": "Kept; /update, /contribute, /save tokens rendered as $skill tokens."
16
16
  },
17
17
  {
@@ -35,7 +35,7 @@
35
35
  {
36
36
  "section": "After Greeting — BRANCH ON FIRST RESPONSE",
37
37
  "action": "replace",
38
- "sourceHash": "b13b81aea8f3",
38
+ "sourceHash": "3bac01c0a00a",
39
39
  "note": "EnterWorktree -> bin/agent.sh branch with git checkout fallback; AskUserQuestion -> numbered options in plain text; branch-guard mapped to .codex/hooks/branch-guard.js with instruction fallback; plan-mode note dropped (no plan mode on Codex); /branch + /onboarding -> skill tokens. Handoff claiming and repoState auto-checkout bash kept verbatim (pure git); surrounding prose compressed 2026-08 for the 32 KiB budget, every rule retained."
40
40
  },
41
41
  {
@@ -89,7 +89,7 @@
89
89
  {
90
90
  "section": "Git Workflow",
91
91
  "action": "keep",
92
- "sourceHash": "23df84b5fe80",
92
+ "sourceHash": "40735c0c09ff",
93
93
  "note": "Kept; slash tokens rendered as $skill tokens. Branch diagram unchanged (code fence)."
94
94
  },
95
95
  {
@@ -12,11 +12,20 @@ This appendix is generated from the reviewed shell-runtime translation of `CLAUD
12
12
 
13
13
  ## Identity & Upstream
14
14
 
15
- This is an Egregore instance — a downstream fork of the upstream framework at `egregore-labs/egregore`. The framework (`bin/`, `.claude/skills/`, `.claude/hooks/`, `.claude/context/`, `.claude/agents/`, `.pi/`, `loom/`, `CLAUDE.md`, `skills/`) is synced from upstream on every session start and via `/update`. It is not authored in this repo.
15
+ Framework direction comes only from `egregore.json.upstream_url`:
16
+
17
+ - **Source** — literal `"none"`: framework changes are authored here.
18
+ `/update` and `/contribute` stop; use `/save`. Public OSS is downstream and
19
+ receives changes only through this repository's release flow.
20
+ - **Downstream** — a repository URL, or an absent key defaulting to
21
+ `egregore-labs/egregore`: framework paths are synced and not authored here.
22
+
23
+ Never infer direction from a repository name or Git remote.
16
24
 
17
25
  **Where changes belong:**
18
- - **Framework is outdated or a skill is missing:** run `/update` first. It pulls the latest from upstream.
19
- - **Framework behavior needs changing or has a bug:** `/contribute` opens a PR against `egregore-labs/egregore`, or file a GitHub issue on that repo. Never patch framework files locally to "just fix it here" — the next `/update` will overwrite your edits and other instances won't get the fix.
26
+ - **Downstream framework is stale:** run `/update`.
27
+ - **Downstream framework needs a fix:** use `/contribute` or file an upstream issue; local patches are overwritten by `/update`.
28
+ - **Source framework needs a fix:** use `/save` into its configured integration and release flow; never `/contribute`.
20
29
  - **Org-level work** (memory, org-specific knowledge, managed repos, `egregore.json`): `/save` to this repo.
21
30
 
22
31
  When a user reports broken or missing framework behavior, the first question is "when did you last `/update`?"
@@ -59,7 +68,7 @@ The integration branch is `develop` unless top-level `egregore.json.base_branch`
59
68
 
60
69
  **Fallback:** If `bin/agent.sh branch` fails, resolve `{base}` with
61
70
  `_get_base_branch`, then use
62
- `git checkout -b dev/{author}/{slug} origin/{base}`.
71
+ `git checkout --no-track -b dev/{author}/{slug} origin/{base}`.
63
72
 
64
73
  4. Update graph (fire-and-forget): `bash bin/graph-op.sh set-topic "$(cat .egregore-session-id 2>/dev/null)" "topic from slug" "dev/author/slug" 2>/dev/null &`
65
74
 
@@ -125,9 +134,11 @@ The `.pi/extensions/egregore.ts` `tool_call` gate (loaded after Pi project trust
125
134
  Wait for the reply, then branch.
126
135
  - **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.
127
136
 
128
- Memory, managed-repo, and runtime-state writes bypass the project guard — if one triggers it, correct the target/context instead of asking for consent.
137
+ If Pi project resources are disabled, keep the same rule.
129
138
 
130
- If Pi project resources are disabled, follow the same discipline as a standing instruction: never write or commit on the configured base, develop, main, or master.
139
+ **History:** Do not merge, cherry-pick, or rebase another task merely to read
140
+ it; use `gh pr view/diff`, `git show`, or a temporary worktree. Integrate
141
+ only for dependencies. Deletion needs no rewrite unless purging secrets.
131
142
 
132
143
  ### Onboarding exception
133
144
 
@@ -275,11 +286,14 @@ branch; `base_branch: "main"` is single-branch mode. Users never interact with
275
286
  git directly.
276
287
 
277
288
  ```
278
- main ← stable (/release)
279
- develop ← integration (PRs land here)
280
- dev/{author}/{topic-slug} | feature/{slug} | bugfix/{slug}
289
+ main ← selected `/release` candidates
290
+ develop ← CL integration (available internally)
291
+ dev/{author}/{topic-slug} | feature/{slug} | bugfix/{slug}
281
292
  ```
282
293
 
294
+ - **Develop is not release**: merged PRs stay inside CL.
295
+ - **`/release` is selective**: the Release Desk queues exact PR SHAs into a candidate from main. Never merge all of develop.
296
+ - **OSS is separate**: `/sync-public` reviews delivery after private main.
283
297
  - **On launch**: syncs the configured base branch + memory. Does NOT create a branch.
284
298
  - **Branch creation**: MANDATORY on first work-related message (see above).
285
299
  - **Resuming**: rebase onto the configured base branch and continue.
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "generatedBy": "bin/pi-render-spec.mjs",
3
3
  "source": "AGENTS.md generated Codex spec",
4
- "sourceHash": "535e5e6f8cce",
5
- "outputHash": "f76462a38e8d",
4
+ "sourceHash": "b0b4451961ef",
5
+ "outputHash": "52773e6edfc8",
6
6
  "adaptations": [
7
7
  "Pi project trust and APPEND_SYSTEM delivery",
8
8
  "Pi tool_call branch gate",
@@ -92,11 +92,20 @@ This block is the Codex-native Egregore behavioral spec, rendered from CLAUDE.md
92
92
 
93
93
  ## Identity & Upstream
94
94
 
95
- This is an Egregore instance — a downstream fork of the upstream framework at `egregore-labs/egregore`. The framework (`bin/`, `.claude/skills/`, `.claude/hooks/`, `.claude/context/`, `.claude/agents/`, `.pi/`, `loom/`, `CLAUDE.md`, `skills/`) is synced from upstream on every session start and via `$update`. It is not authored in this repo.
95
+ Framework direction comes only from `egregore.json.upstream_url`:
96
+
97
+ - **Source** — literal `"none"`: framework changes are authored here.
98
+ `$update` and `$contribute` stop; use `$save`. Public OSS is downstream and
99
+ receives changes only through this repository's release flow.
100
+ - **Downstream** — a repository URL, or an absent key defaulting to
101
+ `egregore-labs/egregore`: framework paths are synced and not authored here.
102
+
103
+ Never infer direction from a repository name or Git remote.
96
104
 
97
105
  **Where changes belong:**
98
- - **Framework is outdated or a skill is missing:** run `$update` first. It pulls the latest from upstream.
99
- - **Framework behavior needs changing or has a bug:** `$contribute` opens a PR against `egregore-labs/egregore`, or file a GitHub issue on that repo. Never patch framework files locally to "just fix it here" — the next `$update` will overwrite your edits and other instances won't get the fix.
106
+ - **Downstream framework is stale:** run `$update`.
107
+ - **Downstream framework needs a fix:** use `$contribute` or file an upstream issue; local patches are overwritten by `$update`.
108
+ - **Source framework needs a fix:** use `$save` into its configured integration and release flow; never `$contribute`.
100
109
  - **Org-level work** (memory, org-specific knowledge, managed repos, `egregore.json`): `$save` to this repo.
101
110
 
102
111
  When a user reports broken or missing framework behavior, the first question is "when did you last `$update`?"
@@ -139,7 +148,7 @@ The integration branch is `develop` unless top-level `egregore.json.base_branch`
139
148
 
140
149
  **Fallback:** If `bin/agent.sh branch` fails, resolve `{base}` with
141
150
  `_get_base_branch`, then use
142
- `git checkout -b dev/{author}/{slug} origin/{base}`.
151
+ `git checkout --no-track -b dev/{author}/{slug} origin/{base}`.
143
152
 
144
153
  4. Update graph (fire-and-forget): `bash bin/graph-op.sh set-topic "$(cat .egregore-session-id 2>/dev/null)" "topic from slug" "dev/author/slug" 2>/dev/null &`
145
154
 
@@ -205,9 +214,11 @@ The `.codex/hooks/branch-guard.js` PreToolUse hook (launcher `--enable hooks`) p
205
214
  Wait for the reply, then branch.
206
215
  - **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.
207
216
 
208
- Memory, managed-repo, and runtime-state writes bypass the project guard — if one triggers it, correct the target/context instead of asking for consent.
217
+ If this Codex build does not support hooks, keep the same rule.
209
218
 
210
- If this Codex build does not support hooks, follow the same discipline as a standing instruction: never write or commit on the configured base, develop, main, or master.
219
+ **History:** Do not merge, cherry-pick, or rebase another task merely to read
220
+ it; use `gh pr view/diff`, `git show`, or a temporary worktree. Integrate
221
+ only for dependencies. Deletion needs no rewrite unless purging secrets.
211
222
 
212
223
  ### Onboarding exception
213
224
 
@@ -355,11 +366,14 @@ branch; `base_branch: "main"` is single-branch mode. Users never interact with
355
366
  git directly.
356
367
 
357
368
  ```
358
- main ← stable (/release)
359
- develop ← integration (PRs land here)
360
- dev/{author}/{topic-slug} | feature/{slug} | bugfix/{slug}
369
+ main ← selected `/release` candidates
370
+ develop ← CL integration (available internally)
371
+ dev/{author}/{topic-slug} | feature/{slug} | bugfix/{slug}
361
372
  ```
362
373
 
374
+ - **Develop is not release**: merged PRs stay inside CL.
375
+ - **`$release` is selective**: the Release Desk queues exact PR SHAs into a candidate from main. Never merge all of develop.
376
+ - **OSS is separate**: `$sync-public` reviews delivery after private main.
363
377
  - **On launch**: syncs the configured base branch + memory. Does NOT create a branch.
364
378
  - **Branch creation**: MANDATORY on first work-related message (see above).
365
379
  - **Resuming**: rebase onto the configured base branch and continue.
@@ -187,6 +187,15 @@ base_ref() {
187
187
  fi
188
188
  }
189
189
 
190
+ clear_base_upstream() {
191
+ local repo="$1" branch="$2" base="$3" upstream=""
192
+ case "$branch" in dev/*|feature/*|bugfix/*) ;; *) return 0 ;; esac
193
+ upstream="$(git -C "$repo" rev-parse --abbrev-ref "${branch}@{upstream}" 2>/dev/null || true)"
194
+ if [ "$upstream" = "$base" ]; then
195
+ git -C "$repo" branch --unset-upstream "$branch" >/dev/null 2>&1 || true
196
+ fi
197
+ }
198
+
190
199
  cmd_protocol() {
191
200
  cat <<'EOF'
192
201
  Egregore portable agent protocol
@@ -306,8 +315,9 @@ cmd_branch() {
306
315
  if git -C "$SCRIPT_DIR" show-ref --verify --quiet "refs/heads/$branch" 2>/dev/null; then
307
316
  git -C "$SCRIPT_DIR" checkout "$branch" --quiet
308
317
  else
309
- git -C "$SCRIPT_DIR" checkout -b "$branch" "$base" --quiet
318
+ git -C "$SCRIPT_DIR" checkout --no-track -b "$branch" "$base" --quiet
310
319
  fi
320
+ clear_base_upstream "$SCRIPT_DIR" "$branch" "$base"
311
321
  egregore_link_shared_state "$SCRIPT_DIR" "$MAIN_PROJECT_DIR" >/dev/null 2>/dev/null || true
312
322
  echo "branch: $branch"
313
323
  echo "worktree: $SCRIPT_DIR"
@@ -323,8 +333,9 @@ cmd_branch() {
323
333
  fi
324
334
 
325
335
  if ! git -C "$MAIN_PROJECT_DIR" show-ref --verify --quiet "refs/heads/$branch" 2>/dev/null; then
326
- git -C "$MAIN_PROJECT_DIR" branch "$branch" "$base" >/dev/null 2>&1 || die "failed to create branch $branch"
336
+ git -C "$MAIN_PROJECT_DIR" branch --no-track "$branch" "$base" >/dev/null 2>&1 || die "failed to create branch $branch"
327
337
  fi
338
+ clear_base_upstream "$MAIN_PROJECT_DIR" "$branch" "$base"
328
339
 
329
340
  wt_path="$MAIN_PROJECT_DIR/.claude/worktrees/$slug"
330
341
  if [ -e "$wt_path" ]; then
@@ -356,8 +367,9 @@ ensure_working_branch() {
356
367
  if git -C "$SCRIPT_DIR" show-ref --verify --quiet "refs/heads/$branch" 2>/dev/null; then
357
368
  git -C "$SCRIPT_DIR" checkout "$branch" --quiet || die "failed to checkout $branch"
358
369
  else
359
- git -C "$SCRIPT_DIR" checkout -b "$branch" "$base" --quiet || die "failed to create $branch"
370
+ git -C "$SCRIPT_DIR" checkout --no-track -b "$branch" "$base" --quiet || die "failed to create $branch"
360
371
  fi
372
+ clear_base_upstream "$SCRIPT_DIR" "$branch" "$base"
361
373
  echo "$branch"
362
374
  }
363
375