create-egregore 0.19.32 → 0.20.0

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 (173) hide show
  1. package/assets/egregore-launcher.js +118 -13
  2. package/bin/cli.js +48 -3
  3. package/lib/connect-runtime.js +4 -2
  4. package/lib/local.js +15 -1
  5. package/lib/prime-runtime.js +208 -0
  6. package/lib/setup.js +45 -3
  7. package/package.json +5 -4
  8. package/runtime/codex/.codex/skills/ingest-notion/SKILL.md +29 -0
  9. package/runtime/codex/.codex/skills/notion-connect/SKILL.md +29 -0
  10. package/runtime/codex/.codex/skills/teams-connect/SKILL.md +29 -0
  11. package/runtime/codex/.codex/spec-manifest.json +3 -3
  12. package/runtime/codex/AGENTS.md +11 -4
  13. package/runtime/codex/bin/activity-data.sh +4 -8
  14. package/runtime/codex/bin/attendant.sh +13 -0
  15. package/runtime/codex/bin/autosave.sh +27 -1
  16. package/runtime/codex/bin/codex-render-spec.mjs +6 -0
  17. package/runtime/codex/bin/codex-session-start.sh +8 -1
  18. package/runtime/codex/bin/connect-refresh.sh +33 -0
  19. package/runtime/codex/bin/graph-batch.sh +25 -6
  20. package/runtime/codex/bin/graph-op.sh +5 -4
  21. package/runtime/codex/bin/graph.sh +31 -5
  22. package/runtime/codex/bin/ingest.py +81 -5
  23. package/runtime/codex/bin/lib/context.sh +26 -1
  24. package/runtime/codex/bin/lib/greeting.sh +4 -1
  25. package/runtime/codex/bin/lib/worktree-links.sh +25 -0
  26. package/runtime/codex/manifest.json +8 -1
  27. package/runtime/pi/.codex/skills/ingest-notion/SKILL.md +29 -0
  28. package/runtime/pi/.codex/skills/notion-connect/SKILL.md +29 -0
  29. package/runtime/pi/.codex/skills/teams-connect/SKILL.md +29 -0
  30. package/runtime/pi/.codex/spec-manifest.json +3 -3
  31. package/runtime/pi/.pi/APPEND_SYSTEM.md +3 -2
  32. package/runtime/pi/.pi/extensions/egregore.ts +17 -6
  33. package/runtime/pi/.pi/spec-manifest.json +2 -2
  34. package/runtime/pi/AGENTS.md +11 -4
  35. package/runtime/pi/bin/activity-data.sh +4 -8
  36. package/runtime/pi/bin/attendant.sh +13 -0
  37. package/runtime/pi/bin/autosave.sh +27 -1
  38. package/runtime/pi/bin/codex-render-spec.mjs +6 -0
  39. package/runtime/pi/bin/codex-session-start.sh +8 -1
  40. package/runtime/pi/bin/connect-refresh.sh +33 -0
  41. package/runtime/pi/bin/graph-batch.sh +25 -6
  42. package/runtime/pi/bin/graph-op.sh +5 -4
  43. package/runtime/pi/bin/graph.sh +31 -5
  44. package/runtime/pi/bin/ingest.py +81 -5
  45. package/runtime/pi/bin/lib/context.sh +26 -1
  46. package/runtime/pi/bin/lib/greeting.sh +4 -1
  47. package/runtime/pi/bin/lib/worktree-links.sh +25 -0
  48. package/runtime/pi/bin/pi-branch-policy.mjs +138 -1
  49. package/runtime/pi/bin/pi-product-workflows.mjs +35 -7
  50. package/runtime/pi/manifest.json +8 -1
  51. package/runtime/prime/.claude/context/notification-consent.md +66 -0
  52. package/runtime/prime/.codex/config.toml +17 -0
  53. package/runtime/prime/.codex/hooks/branch-guard.js +461 -0
  54. package/runtime/prime/.codex/hooks/search-hint.js +67 -0
  55. package/runtime/prime/.codex/hooks.json +30 -0
  56. package/runtime/prime/.codex/skills/activity/SKILL.md +82 -0
  57. package/runtime/prime/.codex/skills/announce/SKILL.md +67 -0
  58. package/runtime/prime/.codex/skills/archive/SKILL.md +45 -0
  59. package/runtime/prime/.codex/skills/branch/SKILL.md +29 -0
  60. package/runtime/prime/.codex/skills/checkup/SKILL.md +45 -0
  61. package/runtime/prime/.codex/skills/commit/SKILL.md +29 -0
  62. package/runtime/prime/.codex/skills/contribute/SKILL.md +29 -0
  63. package/runtime/prime/.codex/skills/create-skill/SKILL.md +29 -0
  64. package/runtime/prime/.codex/skills/dark-mode/SKILL.md +29 -0
  65. package/runtime/prime/.codex/skills/dashboard/SKILL.md +59 -0
  66. package/runtime/prime/.codex/skills/deep-reflect/SKILL.md +109 -0
  67. package/runtime/prime/.codex/skills/delete-user/SKILL.md +29 -0
  68. package/runtime/prime/.codex/skills/emissary/SKILL.md +45 -0
  69. package/runtime/prime/.codex/skills/handoff/SKILL.md +166 -0
  70. package/runtime/prime/.codex/skills/harvest/QUESTION_PALETTE.md +76 -0
  71. package/runtime/prime/.codex/skills/harvest/SKILL.md +131 -0
  72. package/runtime/prime/.codex/skills/ingest/SKILL.md +29 -0
  73. package/runtime/prime/.codex/skills/ingest-notion/SKILL.md +29 -0
  74. package/runtime/prime/.codex/skills/ingest-user-interview/SKILL.md +45 -0
  75. package/runtime/prime/.codex/skills/invite/SKILL.md +44 -0
  76. package/runtime/prime/.codex/skills/issue/SKILL.md +45 -0
  77. package/runtime/prime/.codex/skills/me/SKILL.md +29 -0
  78. package/runtime/prime/.codex/skills/meeting/SKILL.md +45 -0
  79. package/runtime/prime/.codex/skills/note/SKILL.md +29 -0
  80. package/runtime/prime/.codex/skills/notion-connect/SKILL.md +29 -0
  81. package/runtime/prime/.codex/skills/onboarding/SKILL.md +45 -0
  82. package/runtime/prime/.codex/skills/pr/SKILL.md +29 -0
  83. package/runtime/prime/.codex/skills/pull/SKILL.md +29 -0
  84. package/runtime/prime/.codex/skills/push/SKILL.md +29 -0
  85. package/runtime/prime/.codex/skills/quest/SKILL.md +80 -0
  86. package/runtime/prime/.codex/skills/reflect/SKILL.md +45 -0
  87. package/runtime/prime/.codex/skills/review-pr/SKILL.md +45 -0
  88. package/runtime/prime/.codex/skills/save/SKILL.md +125 -0
  89. package/runtime/prime/.codex/skills/slack-connect/SKILL.md +29 -0
  90. package/runtime/prime/.codex/skills/summon/SKILL.md +45 -0
  91. package/runtime/prime/.codex/skills/sync-repos/SKILL.md +29 -0
  92. package/runtime/prime/.codex/skills/teams-connect/SKILL.md +29 -0
  93. package/runtime/prime/.codex/skills/telegram-connect/SKILL.md +29 -0
  94. package/runtime/prime/.codex/skills/telemetry/SKILL.md +29 -0
  95. package/runtime/prime/.codex/skills/test/SKILL.md +45 -0
  96. package/runtime/prime/.codex/skills/the-spiral/SKILL.md +84 -0
  97. package/runtime/prime/.codex/skills/tui-design/SKILL.md +45 -0
  98. package/runtime/prime/.codex/skills/tutorial/SKILL.md +45 -0
  99. package/runtime/prime/.codex/skills/update/SKILL.md +29 -0
  100. package/runtime/prime/.codex/skills/view/SKILL.md +156 -0
  101. package/runtime/prime/.codex/skills/view/agents/openai.yaml +4 -0
  102. package/runtime/prime/.codex/skills/view/assets/compose-scaffold.html +221 -0
  103. package/runtime/prime/.codex/skills/visual-explain/SKILL.md +45 -0
  104. package/runtime/prime/.codex/skills/wrap/SKILL.md +58 -0
  105. package/runtime/prime/.codex/spec-manifest.json +132 -0
  106. package/runtime/prime/.prime/agent/APPEND_SYSTEM.md +393 -0
  107. package/runtime/prime/.prime/agent/extensions/egregore.ts +460 -0
  108. package/runtime/prime/.prime/agent/settings.json +4 -0
  109. package/runtime/prime/.prime/agent/skills/egregore/SKILL.md +34 -0
  110. package/runtime/prime/.prime/agent/skills/egregore/pyproject.toml +13 -0
  111. package/runtime/prime/.prime/agent/skills/egregore/src/egregore/__init__.py +156 -0
  112. package/runtime/prime/.prime/agent/spec-manifest.json +12 -0
  113. package/runtime/prime/AGENTS.md +489 -0
  114. package/runtime/prime/bin/activity-data.sh +238 -0
  115. package/runtime/prime/bin/agent.sh +775 -0
  116. package/runtime/prime/bin/attendant.sh +707 -0
  117. package/runtime/prime/bin/autosave.sh +112 -0
  118. package/runtime/prime/bin/capture-reconcile.sh +69 -0
  119. package/runtime/prime/bin/capture-run.sh +341 -0
  120. package/runtime/prime/bin/codex-install-skills.sh +78 -0
  121. package/runtime/prime/bin/codex-render-spec.mjs +440 -0
  122. package/runtime/prime/bin/codex-session-start.sh +265 -0
  123. package/runtime/prime/bin/codex-skill-render.mjs +347 -0
  124. package/runtime/prime/bin/codex-sync-skills.sh +232 -0
  125. package/runtime/prime/bin/connect-refresh.sh +33 -0
  126. package/runtime/prime/bin/dashboard-data.sh +178 -0
  127. package/runtime/prime/bin/graph-batch.sh +79 -0
  128. package/runtime/prime/bin/graph-op.sh +1102 -0
  129. package/runtime/prime/bin/graph-wal.sh +229 -0
  130. package/runtime/prime/bin/graph.sh +223 -0
  131. package/runtime/prime/bin/handoff-pr-backfill.sh +86 -0
  132. package/runtime/prime/bin/handoff-run.sh +671 -0
  133. package/runtime/prime/bin/handoff-save-egregore.sh +212 -0
  134. package/runtime/prime/bin/index-handoff.sh +322 -0
  135. package/runtime/prime/bin/ingest-graph.sh +80 -0
  136. package/runtime/prime/bin/ingest-surface.html +495 -0
  137. package/runtime/prime/bin/ingest.py +756 -0
  138. package/runtime/prime/bin/ingest.sh +279 -0
  139. package/runtime/prime/bin/ingest_graph.py +856 -0
  140. package/runtime/prime/bin/ingest_surface.py +544 -0
  141. package/runtime/prime/bin/lib/artifact-id.sh +46 -0
  142. package/runtime/prime/bin/lib/config.sh +116 -0
  143. package/runtime/prime/bin/lib/context.sh +621 -0
  144. package/runtime/prime/bin/lib/dashboard-artifact.sh +242 -0
  145. package/runtime/prime/bin/lib/ensure-develop.sh +183 -0
  146. package/runtime/prime/bin/lib/git-sync.sh +406 -0
  147. package/runtime/prime/bin/lib/greeting.sh +519 -0
  148. package/runtime/prime/bin/lib/hash.sh +13 -0
  149. package/runtime/prime/bin/lib/identity.sh +213 -0
  150. package/runtime/prime/bin/lib/metrics.sh +374 -0
  151. package/runtime/prime/bin/lib/noncode.sh +31 -0
  152. package/runtime/prime/bin/lib/permissions.sh +106 -0
  153. package/runtime/prime/bin/lib/time.sh +56 -0
  154. package/runtime/prime/bin/lib/worktree-links.sh +79 -0
  155. package/runtime/prime/bin/notify.sh +636 -0
  156. package/runtime/prime/bin/person.py +630 -0
  157. package/runtime/prime/bin/person.sh +448 -0
  158. package/runtime/prime/bin/pi-branch-policy.mjs +312 -0
  159. package/runtime/prime/bin/pi-product-workflows.mjs +394 -0
  160. package/runtime/prime/bin/prime-render-spec.mjs +123 -0
  161. package/runtime/prime/bin/prime-session-start.sh +24 -0
  162. package/runtime/prime/bin/publish-artifact.sh +271 -0
  163. package/runtime/prime/bin/publish-references.sh +144 -0
  164. package/runtime/prime/bin/repo-state.sh +96 -0
  165. package/runtime/prime/bin/search.sh +240 -0
  166. package/runtime/prime/bin/session-autosave.sh +209 -0
  167. package/runtime/prime/bin/session-log.sh +199 -0
  168. package/runtime/prime/bin/settings.sh +304 -0
  169. package/runtime/prime/bin/telemetry.sh +391 -0
  170. package/runtime/prime/bin/worktree-create.sh +128 -0
  171. package/runtime/prime/bin/worktree-remove.sh +50 -0
  172. package/runtime/prime/bin/worktree.sh +198 -0
  173. package/runtime/prime/manifest.json +178 -0
@@ -0,0 +1,106 @@
1
+ # shellcheck shell=bash
2
+ # permissions.sh — Soft-enforced admin-only access for Egregore content.
3
+ #
4
+ # Content can be marked admin-only via frontmatter:
5
+ #
6
+ # ---
7
+ # admin: true
8
+ # ---
9
+ #
10
+ # Admins are listed in egregore.json under "admins": ["github1", "github2", ...].
11
+ #
12
+ # Two enforcement layers:
13
+ #
14
+ # - Read-side (CLI listings, /view rendering) — soft, tag-not-hide:
15
+ # admin entries appear in /activity and /dashboard listings tagged so admins
16
+ # can identify them at a glance. For non-admins the title/topic/branch are
17
+ # replaced with an opaque "[admin]" placeholder — they see admin work
18
+ # exists but the topic doesn't leak. Anyone with git access still bypasses
19
+ # by `cat`-ing the file.
20
+ #
21
+ # - Publish-side (egregore.xyz upload) — strict: publish-artifact.sh refuses
22
+ # to push admin-only files because egregore.xyz URLs are not auth-gated.
23
+ # Override with --allow-admin (set by /view after explicit user
24
+ # confirmation via AskUserQuestion).
25
+
26
+ # Get list of admin GitHub usernames (newline-separated, lowercase)
27
+ _get_admins() {
28
+ local config="${CONFIG:-${SCRIPT_DIR:-$(pwd)}/egregore.json}"
29
+ if [ -f "$config" ]; then
30
+ jq -r '.admins[]? // empty' "$config" 2>/dev/null | tr '[:upper:]' '[:lower:]'
31
+ fi
32
+ }
33
+
34
+ # Returns 0 if username is an admin, 1 otherwise. Empty username = not admin.
35
+ _is_admin() {
36
+ local username="${1:-}"
37
+ [ -z "$username" ] && return 1
38
+ local lc
39
+ lc=$(printf '%s' "$username" | tr '[:upper:]' '[:lower:]')
40
+ _get_admins | grep -qx "$lc"
41
+ }
42
+
43
+ # Returns 0 if the file's frontmatter contains `admin: true`, 1 otherwise.
44
+ # Tolerates whitespace and case in the value. Looks at the first ~60 lines
45
+ # to keep the scan bounded.
46
+ _is_admin_only_file() {
47
+ local path="$1"
48
+ [ -f "$path" ] || return 1
49
+ awk '
50
+ BEGIN { in_fm = 0; found = 0 }
51
+ NR == 1 && /^---[[:space:]]*$/ { in_fm = 1; next }
52
+ in_fm && /^---[[:space:]]*$/ { exit (found ? 0 : 1) }
53
+ in_fm && tolower($0) ~ /^admin:[[:space:]]*true[[:space:]]*$/ { found = 1 }
54
+ NR > 60 { exit (found ? 0 : 1) }
55
+ END { exit (found ? 0 : 1) }
56
+ ' "$path" 2>/dev/null
57
+ }
58
+
59
+ # Returns 0 if the file's frontmatter has the `admin:` key set at all (true or
60
+ # false), 1 otherwise. Used by the admin-content-detector hook to skip files
61
+ # whose admin status was already decided — including an explicit `admin: false`
62
+ # meaning "reviewed, public on purpose". Without this, the hook would re-prompt
63
+ # on every edit of a sensitive-keyword file the user has already cleared.
64
+ _has_admin_decision() {
65
+ local path="$1"
66
+ [ -f "$path" ] || return 1
67
+ awk '
68
+ BEGIN { in_fm = 0; found = 0 }
69
+ NR == 1 && /^---[[:space:]]*$/ { in_fm = 1; next }
70
+ in_fm && /^---[[:space:]]*$/ { exit (found ? 0 : 1) }
71
+ in_fm && tolower($0) ~ /^admin:[[:space:]]*(true|false)[[:space:]]*$/ { found = 1 }
72
+ NR > 60 { exit (found ? 0 : 1) }
73
+ END { exit (found ? 0 : 1) }
74
+ ' "$path" 2>/dev/null
75
+ }
76
+
77
+ # Resolve current user's GitHub username (lowercase). Falls back through:
78
+ # $1 → .egregore-state.json → git user.name
79
+ _current_user() {
80
+ local explicit="${1:-}"
81
+ if [ -n "$explicit" ]; then
82
+ printf '%s' "$explicit" | tr '[:upper:]' '[:lower:]'
83
+ return 0
84
+ fi
85
+ local state="${SCRIPT_DIR:-$(pwd)}/.egregore-state.json"
86
+ if [ -f "$state" ]; then
87
+ local u
88
+ u=$(jq -r '.github_username // empty' "$state" 2>/dev/null)
89
+ if [ -n "$u" ]; then
90
+ printf '%s' "$u" | tr '[:upper:]' '[:lower:]'
91
+ return 0
92
+ fi
93
+ fi
94
+ git config user.name 2>/dev/null | tr '[:upper:]' '[:lower:]'
95
+ }
96
+
97
+ # Returns 0 if the file's body contains sensitive keywords (fundraising, legal,
98
+ # personnel, strategic). Used by the admin-content-detector hook to flag files
99
+ # that probably should be admin-only but aren't yet. Whole-word match on a
100
+ # short curated list — easy to extend without retraining intuition.
101
+ _looks_sensitive() {
102
+ local path="$1"
103
+ [ -f "$path" ] || return 1
104
+ # Word-boundary regex (case-insensitive). Keep this list curated, not exhaustive.
105
+ grep -qiE '\b(fundrais(e|ing)|term[ -]?sheet|cap[ -]?table|valuation|runway|investor|VC[s]?|raise|seed[ -]?round|series[ -][a-d]|board[ -]prep|equity[ -]grant|stock[ -]option|RSU|salary|compensation|payroll|severance|PIP|NDA|attorney|legal[ -]hold|lawsuit|acquisition|merger|due[ -]diligence)\b' "$path"
106
+ }
@@ -0,0 +1,56 @@
1
+ #!/usr/bin/env bash
2
+ # Cross-platform time helpers — sourced by scripts that need time formatting.
3
+
4
+ # Convert ISO timestamp to epoch seconds (macOS + Linux compatible)
5
+ # Assumes input is UTC (Z or +00:00 suffix, or bare — treated as UTC)
6
+ _iso_to_epoch() {
7
+ local iso="$1"
8
+ local clean
9
+ clean=$(echo "$iso" | sed 's/Z$//; s/+00:00$//; s/\.[0-9]*//')
10
+ # If date-only (no T), append midnight to avoid macOS date -j filling current time
11
+ [[ "$clean" != *T* ]] && clean="${clean}T00:00:00"
12
+ # macOS → date -j, Linux → date -d
13
+ TZ=UTC date -j -f "%Y-%m-%dT%H:%M:%S" "$clean" "+%s" 2>/dev/null || \
14
+ TZ=UTC date -d "$clean" +%s 2>/dev/null || echo "0"
15
+ }
16
+
17
+ # Convert epoch to relative time string (e.g. "3h ago", "yesterday")
18
+ # Uses calendar-day comparison for multi-day ranges (not raw delta)
19
+ _epoch_to_relative() {
20
+ local epoch="$1"
21
+ local now="${2:-$(date +%s)}"
22
+ [ "$epoch" = "0" ] && echo "--" && return
23
+ local delta=$(( now - epoch ))
24
+ if [ "$delta" -lt 60 ]; then echo "just now"
25
+ elif [ "$delta" -lt 3600 ]; then echo "$(( delta / 60 ))m ago"
26
+ elif [ "$delta" -lt 86400 ]; then echo "$(( delta / 3600 ))h ago"
27
+ else
28
+ # Calendar-day based: compare local dates for accurate "yesterday" / "Xd ago"
29
+ local event_date today_date
30
+ event_date=$(date -r "$epoch" +%Y-%m-%d 2>/dev/null || \
31
+ date -d "@$epoch" +%Y-%m-%d 2>/dev/null || echo "")
32
+ today_date=$(date +%Y-%m-%d)
33
+ if [ -z "$event_date" ] || [ "$event_date" = "$today_date" ]; then echo "today"
34
+ else
35
+ local today_e event_e
36
+ today_e=$(date -j -f "%Y-%m-%d" "$today_date" +%s 2>/dev/null || \
37
+ date -d "$today_date" +%s 2>/dev/null || echo "$now")
38
+ event_e=$(date -j -f "%Y-%m-%d" "$event_date" +%s 2>/dev/null || \
39
+ date -d "$event_date" +%s 2>/dev/null || echo "$epoch")
40
+ local days=$(( (today_e - event_e) / 86400 ))
41
+ [ "$days" -lt 1 ] && days=1
42
+ if [ "$days" -eq 1 ]; then echo "yesterday"
43
+ else echo "${days}d ago"
44
+ fi
45
+ fi
46
+ fi
47
+ }
48
+
49
+ # Millisecond timer (for latency measurement)
50
+ _millis() {
51
+ if command -v gdate &>/dev/null; then
52
+ gdate +%s%3N
53
+ else
54
+ python3 -c "import time; print(int(time.time() * 1000))" 2>/dev/null || echo "0"
55
+ fi
56
+ }
@@ -0,0 +1,79 @@
1
+ # shellcheck shell=bash
2
+ # worktree-links.sh - shared-state bootstrap for git worktrees.
3
+ #
4
+ # Claude Code worktrees are normally created through bin/worktree-create.sh,
5
+ # which links memory/, .env, and local state from the main checkout. Portable
6
+ # runtimes such as Codex can land in a plain git worktree, so startup and
7
+ # agent.sh use this helper to self-heal those links before reading context.
8
+
9
+ egregore_main_project_dir() {
10
+ local project_dir="${1:?project dir required}"
11
+ if [ -f "$project_dir/.git" ]; then
12
+ local wt_gitdir
13
+ wt_gitdir=$(sed 's/^gitdir: //' "$project_dir/.git" 2>/dev/null || true)
14
+ if [ -n "$wt_gitdir" ]; then
15
+ cd "$wt_gitdir/../../.." 2>/dev/null && pwd
16
+ return
17
+ fi
18
+ fi
19
+ cd "$project_dir" 2>/dev/null && pwd
20
+ }
21
+
22
+ egregore_link_shared_state() {
23
+ local worktree_dir="${1:?worktree dir required}"
24
+ local main_dir="${2:-}"
25
+ local file src dst resolved
26
+
27
+ [ -f "$worktree_dir/.git" ] || return 0
28
+ [ -n "$main_dir" ] || main_dir="$(egregore_main_project_dir "$worktree_dir")"
29
+ [ -n "$main_dir" ] || return 0
30
+ [ "$main_dir" != "$worktree_dir" ] || return 0
31
+
32
+ for file in memory .env .egregore-state.json .egregore-session-id; do
33
+ src="$main_dir/$file"
34
+ dst="$worktree_dir/$file"
35
+
36
+ if [ "$file" = "memory" ]; then
37
+ if [ -L "$src" ] || [ -d "$src" ]; then
38
+ resolved="$(realpath "$src" 2>/dev/null || true)"
39
+ [ -n "$resolved" ] && [ -d "$resolved" ] || continue
40
+ src="$resolved"
41
+ else
42
+ continue
43
+ fi
44
+ else
45
+ [ -f "$src" ] || continue
46
+ fi
47
+
48
+ if [ -L "$dst" ]; then
49
+ ln -sfn "$src" "$dst" 2>/dev/null || true
50
+ elif [ ! -e "$dst" ]; then
51
+ ln -s "$src" "$dst" 2>/dev/null || true
52
+ fi
53
+ done
54
+ }
55
+
56
+ # Resolve the .env that governs a checkout. A worktree can be missing its .env
57
+ # symlink (created before .env existed, or by a runtime that skipped worktree
58
+ # setup) — self-heal the links, then fall back to the main checkout's .env so
59
+ # credential loading never silently comes up empty. Prints the usable path, or
60
+ # nothing when neither checkout has one. Always returns 0.
61
+ egregore_env_file() {
62
+ local project_dir="${1:?project dir required}"
63
+ if [ ! -f "$project_dir/.env" ] && [ -f "$project_dir/.git" ]; then
64
+ egregore_link_shared_state "$project_dir" 2>/dev/null || true
65
+ fi
66
+ if [ -f "$project_dir/.env" ]; then
67
+ echo "$project_dir/.env"
68
+ return 0
69
+ fi
70
+ if [ -f "$project_dir/.git" ]; then
71
+ local main_dir
72
+ main_dir="$(egregore_main_project_dir "$project_dir" 2>/dev/null || true)"
73
+ if [ -n "$main_dir" ] && [ -f "$main_dir/.env" ]; then
74
+ echo "$main_dir/.env"
75
+ return 0
76
+ fi
77
+ fi
78
+ return 0
79
+ }