create-egregore 0.17.4 → 0.18.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 (110) hide show
  1. package/assets/egregore-launcher.js +1965 -0
  2. package/assets/egregore-launcher.sh +385 -0
  3. package/bin/cli.js +472 -81
  4. package/lib/api.js +2 -2
  5. package/lib/auth.js +255 -20
  6. package/lib/codex-runtime.js +268 -0
  7. package/lib/commands.js +31 -0
  8. package/lib/context.js +1 -1
  9. package/lib/local.js +616 -189
  10. package/lib/setup.js +822 -84
  11. package/lib/ui.js +780 -84
  12. package/package.json +11 -3
  13. package/runtime/codex/.codex/config.toml +15 -0
  14. package/runtime/codex/.codex/hooks/branch-guard.js +234 -0
  15. package/runtime/codex/.codex/hooks.json +17 -0
  16. package/runtime/codex/.codex/skills/activity/SKILL.md +70 -0
  17. package/runtime/codex/.codex/skills/add/SKILL.md +25 -0
  18. package/runtime/codex/.codex/skills/announce/SKILL.md +54 -0
  19. package/runtime/codex/.codex/skills/answer/SKILL.md +25 -0
  20. package/runtime/codex/.codex/skills/archive/SKILL.md +41 -0
  21. package/runtime/codex/.codex/skills/ask/SKILL.md +59 -0
  22. package/runtime/codex/.codex/skills/branch/SKILL.md +25 -0
  23. package/runtime/codex/.codex/skills/checkup/SKILL.md +41 -0
  24. package/runtime/codex/.codex/skills/comment/SKILL.md +25 -0
  25. package/runtime/codex/.codex/skills/commit/SKILL.md +25 -0
  26. package/runtime/codex/.codex/skills/contribute/SKILL.md +25 -0
  27. package/runtime/codex/.codex/skills/dark-mode/SKILL.md +25 -0
  28. package/runtime/codex/.codex/skills/dashboard/SKILL.md +59 -0
  29. package/runtime/codex/.codex/skills/deep-reflect/SKILL.md +70 -0
  30. package/runtime/codex/.codex/skills/delete-user/SKILL.md +25 -0
  31. package/runtime/codex/.codex/skills/edit/SKILL.md +25 -0
  32. package/runtime/codex/.codex/skills/edit-process/SKILL.md +25 -0
  33. package/runtime/codex/.codex/skills/egregore-voice/SKILL.md +25 -0
  34. package/runtime/codex/.codex/skills/emissary/SKILL.md +41 -0
  35. package/runtime/codex/.codex/skills/env/SKILL.md +25 -0
  36. package/runtime/codex/.codex/skills/handoff/SKILL.md +67 -0
  37. package/runtime/codex/.codex/skills/harvest/SKILL.md +107 -0
  38. package/runtime/codex/.codex/skills/infra/SKILL.md +41 -0
  39. package/runtime/codex/.codex/skills/ingest/SKILL.md +25 -0
  40. package/runtime/codex/.codex/skills/ingest-google/SKILL.md +25 -0
  41. package/runtime/codex/.codex/skills/ingest-user-interview/SKILL.md +41 -0
  42. package/runtime/codex/.codex/skills/invite/SKILL.md +40 -0
  43. package/runtime/codex/.codex/skills/issue/SKILL.md +41 -0
  44. package/runtime/codex/.codex/skills/me/SKILL.md +25 -0
  45. package/runtime/codex/.codex/skills/meeting/SKILL.md +41 -0
  46. package/runtime/codex/.codex/skills/note/SKILL.md +25 -0
  47. package/runtime/codex/.codex/skills/onboarding/SKILL.md +41 -0
  48. package/runtime/codex/.codex/skills/pr/SKILL.md +25 -0
  49. package/runtime/codex/.codex/skills/project/SKILL.md +41 -0
  50. package/runtime/codex/.codex/skills/pull/SKILL.md +25 -0
  51. package/runtime/codex/.codex/skills/push/SKILL.md +25 -0
  52. package/runtime/codex/.codex/skills/quest/SKILL.md +77 -0
  53. package/runtime/codex/.codex/skills/quest-suggest/SKILL.md +25 -0
  54. package/runtime/codex/.codex/skills/reflect/SKILL.md +41 -0
  55. package/runtime/codex/.codex/skills/review-pr/SKILL.md +41 -0
  56. package/runtime/codex/.codex/skills/save/SKILL.md +62 -0
  57. package/runtime/codex/.codex/skills/setup/SKILL.md +25 -0
  58. package/runtime/codex/.codex/skills/summon/SKILL.md +41 -0
  59. package/runtime/codex/.codex/skills/sync-repos/SKILL.md +25 -0
  60. package/runtime/codex/.codex/skills/telegram-connect/SKILL.md +25 -0
  61. package/runtime/codex/.codex/skills/telemetry/SKILL.md +25 -0
  62. package/runtime/codex/.codex/skills/test/SKILL.md +41 -0
  63. package/runtime/codex/.codex/skills/the-spiral/SKILL.md +84 -0
  64. package/runtime/codex/.codex/skills/todo/SKILL.md +41 -0
  65. package/runtime/codex/.codex/skills/tui-design/SKILL.md +41 -0
  66. package/runtime/codex/.codex/skills/tutorial/SKILL.md +41 -0
  67. package/runtime/codex/.codex/skills/update/SKILL.md +25 -0
  68. package/runtime/codex/.codex/skills/view/SKILL.md +41 -0
  69. package/runtime/codex/.codex/skills/visual-explain/SKILL.md +41 -0
  70. package/runtime/codex/.codex/skills/wrap/SKILL.md +55 -0
  71. package/runtime/codex/.codex/spec-manifest.json +108 -0
  72. package/runtime/codex/AGENTS.md +328 -0
  73. package/runtime/codex/bin/activity-data.sh +180 -0
  74. package/runtime/codex/bin/agent.sh +669 -0
  75. package/runtime/codex/bin/codex-install-skills.sh +78 -0
  76. package/runtime/codex/bin/codex-render-spec.mjs +308 -0
  77. package/runtime/codex/bin/codex-session-start.sh +238 -0
  78. package/runtime/codex/bin/codex-skill-render.mjs +315 -0
  79. package/runtime/codex/bin/codex-sync-skills.sh +223 -0
  80. package/runtime/codex/bin/dashboard-data.sh +178 -0
  81. package/runtime/codex/bin/graph-batch.sh +60 -0
  82. package/runtime/codex/bin/graph-op.sh +397 -0
  83. package/runtime/codex/bin/graph-wal.sh +228 -0
  84. package/runtime/codex/bin/graph.sh +141 -0
  85. package/runtime/codex/bin/handoff-pr-backfill.sh +86 -0
  86. package/runtime/codex/bin/handoff-run.sh +569 -0
  87. package/runtime/codex/bin/handoff-save-egregore.sh +131 -0
  88. package/runtime/codex/bin/index-handoff.sh +306 -0
  89. package/runtime/codex/bin/lib/artifact-id.sh +46 -0
  90. package/runtime/codex/bin/lib/config.sh +71 -0
  91. package/runtime/codex/bin/lib/context.sh +511 -0
  92. package/runtime/codex/bin/lib/dashboard-artifact.sh +142 -0
  93. package/runtime/codex/bin/lib/edit-loopback.mjs +652 -0
  94. package/runtime/codex/bin/lib/ensure-develop.sh +183 -0
  95. package/runtime/codex/bin/lib/git-sync.sh +262 -0
  96. package/runtime/codex/bin/lib/greeting.sh +432 -0
  97. package/runtime/codex/bin/lib/hash.sh +13 -0
  98. package/runtime/codex/bin/lib/identity.sh +164 -0
  99. package/runtime/codex/bin/lib/permissions.sh +106 -0
  100. package/runtime/codex/bin/lib/time.sh +56 -0
  101. package/runtime/codex/bin/lib/worktree-links.sh +54 -0
  102. package/runtime/codex/bin/notify.sh +207 -0
  103. package/runtime/codex/bin/publish-artifact.sh +187 -0
  104. package/runtime/codex/bin/publish-references.sh +144 -0
  105. package/runtime/codex/bin/repo-state.sh +99 -0
  106. package/runtime/codex/bin/telemetry.sh +350 -0
  107. package/runtime/codex/bin/worktree-create.sh +115 -0
  108. package/runtime/codex/bin/worktree-remove.sh +50 -0
  109. package/runtime/codex/bin/worktree.sh +199 -0
  110. package/runtime/codex/manifest.json +162 -0
@@ -0,0 +1,385 @@
1
+ #!/usr/bin/env bash
2
+ # egregore-launcher-version: 0.4.9
3
+ # egregore — launch an Egregore instance in Claude Code.
4
+ # Reads ~/.egregore/instances.json for the registry of installed egregores.
5
+ #
6
+ # Usage:
7
+ # egregore pick an instance (or launch the only one)
8
+ # egregore <slug|name> launch the matching instance
9
+ # egregore ls | list list installed instances
10
+ # egregore use <slug> set the default for bare `egregore`
11
+ # egregore prune remove entries whose path is gone
12
+ # egregore rm <slug|path> remove a specific entry
13
+ # egregore help | --help
14
+ # egregore --version
15
+ #
16
+ # Installed by create-egregore. Safe to move or edit — persistent state lives
17
+ # only in ~/.egregore/instances.json and ~/.egregore/default.
18
+ #
19
+ # The first line's "egregore-launcher-version" comment is read by
20
+ # installLauncherBinary() so newer create-egregore installs can auto-upgrade
21
+ # a stale binary. Don't rename or remove it.
22
+
23
+ set -u
24
+
25
+ VERSION="0.4.9"
26
+ REGISTRY="${HOME}/.egregore/instances.json"
27
+ DEFAULT_FILE="${HOME}/.egregore/default"
28
+
29
+ # ── colors ────────────────────────────────────────────────────────
30
+ if [ -t 1 ] && [ -z "${NO_COLOR:-}" ]; then
31
+ RESET=$'\e[0m'; BOLD=$'\e[1m'; DIM=$'\e[2m'
32
+ CYAN=$'\e[36m'; GREEN=$'\e[32m'; YELLOW=$'\e[33m'; RED=$'\e[31m'
33
+ else
34
+ RESET=""; BOLD=""; DIM=""; CYAN=""; GREEN=""; YELLOW=""; RED=""
35
+ fi
36
+
37
+ die() {
38
+ printf '%segregore:%s %s\n' "$RED" "$RESET" "$*" >&2
39
+ exit 1
40
+ }
41
+
42
+ banner() {
43
+ printf '\n'
44
+ printf '%s%s ███████╗ ██████╗ ██████╗ ███████╗ ██████╗ ██████╗ ██████╗ ███████╗%s\n' "$BOLD" "$CYAN" "$RESET"
45
+ printf '%s%s ██╔════╝██╔════╝ ██╔══██╗██╔════╝██╔════╝ ██╔═══██╗██╔══██╗██╔════╝%s\n' "$BOLD" "$CYAN" "$RESET"
46
+ printf '%s%s █████╗ ██║ ███╗██████╔╝█████╗ ██║ ███╗██║ ██║██████╔╝█████╗%s\n' "$BOLD" "$CYAN" "$RESET"
47
+ printf '%s%s ██╔══╝ ██║ ██║██╔══██╗██╔══╝ ██║ ██║██║ ██║██╔══██╗██╔══╝%s\n' "$BOLD" "$CYAN" "$RESET"
48
+ printf '%s%s ███████╗╚██████╔╝██║ ██║███████╗╚██████╔╝╚██████╔╝██║ ██║███████╗%s\n' "$BOLD" "$CYAN" "$RESET"
49
+ printf '%s%s ╚══════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝ ╚═════╝ ╚═════╝ ╚═╝ ╚═╝╚══════╝%s\n' "$BOLD" "$CYAN" "$RESET"
50
+ printf '\n'
51
+ }
52
+
53
+ require_jq() {
54
+ command -v jq >/dev/null 2>&1 || die "jq is required but not installed (brew install jq)."
55
+ }
56
+
57
+ require_registry() {
58
+ [ -f "$REGISTRY" ] || die "No egregores installed yet. Run: npx -y create-egregore@latest"
59
+ }
60
+
61
+ list_instances() {
62
+ # Prints: <slug>\t<name>\t<path>
63
+ jq -r '.[] | [.slug, .name, .path] | @tsv' "$REGISTRY"
64
+ }
65
+
66
+ instance_count() {
67
+ jq 'length' "$REGISTRY"
68
+ }
69
+
70
+ resolve_instance() {
71
+ # $1 = slug or name (fuzzy-ish: exact slug first, then exact name, then case-insensitive contains)
72
+ local query="$1"
73
+ local result
74
+ result=$(jq -r --arg q "$query" '.[] | select(.slug == $q) | .path' "$REGISTRY" | head -n1)
75
+ if [ -z "$result" ]; then
76
+ result=$(jq -r --arg q "$query" '.[] | select(.name == $q) | .path' "$REGISTRY" | head -n1)
77
+ fi
78
+ if [ -z "$result" ]; then
79
+ local qlower
80
+ qlower=$(printf '%s' "$query" | tr '[:upper:]' '[:lower:]')
81
+ result=$(jq -r --arg q "$qlower" '
82
+ .[] | select((.slug | ascii_downcase | contains($q)) or (.name | ascii_downcase | contains($q))) | .path' "$REGISTRY" | head -n1)
83
+ fi
84
+ printf '%s' "$result"
85
+ }
86
+
87
+ launch() {
88
+ local target_path="$1"
89
+ local target_slug="${2:-}"
90
+ [ -d "$target_path" ] || die "Instance path does not exist: $target_path"
91
+ command -v claude >/dev/null 2>&1 || die "Claude Code is not installed. Install it from https://code.claude.com then try again."
92
+ if [ -n "$target_slug" ]; then
93
+ printf ' %s→%s launching %s%s%s ...\n\n' "$DIM" "$RESET" "$BOLD" "$target_slug" "$RESET"
94
+ fi
95
+ cd "$target_path" || die "Could not cd into $target_path"
96
+ exec claude start
97
+ }
98
+
99
+ cmd_ls() {
100
+ require_jq
101
+ require_registry
102
+ local default=""
103
+ [ -f "$DEFAULT_FILE" ] && default=$(cat "$DEFAULT_FILE" 2>/dev/null)
104
+ banner
105
+ local count
106
+ count=$(instance_count)
107
+ local plural="s"
108
+ [ "$count" -eq 1 ] && plural=""
109
+ printf ' %s%d egregore%s registered%s\n\n' "$BOLD" "$count" "$plural" "$RESET"
110
+ printf ' %s%-22s %-24s %s%s\n' "$DIM" "SLUG" "NAME" "PATH" "$RESET"
111
+ while IFS=$'\t' read -r slug name path; do
112
+ if [ "$slug" = "$default" ]; then
113
+ printf ' %s●%s %s%-22s%s %-24s %s%s%s\n' "$GREEN" "$RESET" "$BOLD" "$slug" "$RESET" "$name" "$DIM" "$path" "$RESET"
114
+ else
115
+ printf ' %-22s %-24s %s%s%s\n' "$slug" "$name" "$DIM" "$path" "$RESET"
116
+ fi
117
+ done < <(list_instances)
118
+ printf '\n'
119
+ if [ -n "$default" ]; then
120
+ printf ' %s●%s = default (bare %segregore%s launches this)\n\n' "$GREEN" "$RESET" "$BOLD" "$RESET"
121
+ fi
122
+ }
123
+
124
+ cmd_use() {
125
+ require_jq
126
+ require_registry
127
+ local slug="${1:-}"
128
+ [ -n "$slug" ] || die "Usage: egregore use <slug>"
129
+ local found
130
+ found=$(jq -r --arg q "$slug" '.[] | select(.slug == $q) | .slug' "$REGISTRY" | head -n1)
131
+ [ -n "$found" ] || die "No instance with slug '$slug'. Run: egregore ls"
132
+ mkdir -p "$(dirname "$DEFAULT_FILE")"
133
+ printf '%s\n' "$found" > "$DEFAULT_FILE"
134
+ printf ' %s✓%s default set to %s%s%s\n' "$GREEN" "$RESET" "$BOLD" "$found" "$RESET"
135
+ }
136
+
137
+ # ── registry maintenance ─────────────────────────────────────────
138
+ #
139
+ # Atomic write: filter via jq to a tmp file, then rename. Backup every
140
+ # mutation to ~/.egregore/instances.json.bak.<timestamp> so a mis-click
141
+ # is always reversible.
142
+
143
+ backup_registry() {
144
+ local ts
145
+ ts=$(date +%Y%m%d-%H%M%S)
146
+ local bak="${HOME}/.egregore/instances.json.bak.${ts}"
147
+ cp "$REGISTRY" "$bak"
148
+ printf '%s' "$bak"
149
+ }
150
+
151
+ cmd_prune() {
152
+ require_jq
153
+ require_registry
154
+ banner
155
+
156
+ # Collect stale entries (path doesn't exist on disk)
157
+ local -a stale_slugs=()
158
+ local -a stale_paths=()
159
+ while IFS=$'\t' read -r slug path; do
160
+ [ -d "$path" ] || { stale_slugs+=("$slug"); stale_paths+=("$path"); }
161
+ done < <(jq -r '.[] | [.slug, .path] | @tsv' "$REGISTRY")
162
+
163
+ local count_before count_stale
164
+ count_before=$(instance_count)
165
+ count_stale=${#stale_paths[@]}
166
+
167
+ if [ "$count_stale" -eq 0 ]; then
168
+ printf ' %s✓%s registry is clean — %d entries, all paths exist.\n\n' "$GREEN" "$RESET" "$count_before"
169
+ return 0
170
+ fi
171
+
172
+ printf ' %s⚠%s %d of %d entries have missing paths:\n\n' "$YELLOW" "$RESET" "$count_stale" "$count_before"
173
+ local i=0
174
+ while [ "$i" -lt "$count_stale" ]; do
175
+ printf ' %s✗%s %-22s %s%s%s\n' "$RED" "$RESET" "${stale_slugs[$i]}" "$DIM" "${stale_paths[$i]}" "$RESET"
176
+ i=$((i + 1))
177
+ done
178
+ printf '\n'
179
+
180
+ # Dry run short-circuit
181
+ if [ "${1:-}" = "--dry-run" ] || [ "${1:-}" = "-n" ]; then
182
+ printf ' %s(dry run — nothing removed. re-run without --dry-run to apply.)%s\n\n' "$DIM" "$RESET"
183
+ return 0
184
+ fi
185
+
186
+ printf ' %s?%s remove them? [y/N]: ' "$CYAN" "$RESET"
187
+ read -r confirm
188
+ case "$confirm" in
189
+ y|Y|yes|YES) ;;
190
+ *) printf ' %sCancelled.%s\n\n' "$DIM" "$RESET"; return 0 ;;
191
+ esac
192
+
193
+ local bak
194
+ bak=$(backup_registry)
195
+
196
+ local tmp="${REGISTRY}.tmp.$$"
197
+ # Keep only entries whose path still exists. The filesystem is the source
198
+ # of truth — no path strings in the filter itself.
199
+ jq '[.[] | select(.path as $p | ($p | @sh) as $_ | true)]' "$REGISTRY" > /dev/null 2>&1 || true
200
+ # Actual filter: stream entries, read each path, bash test, emit if exists.
201
+ # We rebuild the JSON array outside jq because jq can't check filesystem.
202
+ {
203
+ printf '['
204
+ local first=1
205
+ while IFS=$'\t' read -r slug name path; do
206
+ if [ -d "$path" ]; then
207
+ [ "$first" -eq 0 ] && printf ','
208
+ jq -n --arg slug "$slug" --arg name "$name" --arg path "$path" \
209
+ '{slug: $slug, name: $name, path: $path}' | jq -c .
210
+ first=0
211
+ fi
212
+ done < <(jq -r '.[] | [.slug, .name, .path] | @tsv' "$REGISTRY")
213
+ printf ']'
214
+ } | jq '.' > "$tmp"
215
+
216
+ mv "$tmp" "$REGISTRY"
217
+ local count_after
218
+ count_after=$(instance_count)
219
+ printf ' %s✓%s pruned %d — %d remain.\n' "$GREEN" "$RESET" "$((count_before - count_after))" "$count_after"
220
+ printf ' %sbackup:%s %s\n\n' "$DIM" "$RESET" "$bak"
221
+ }
222
+
223
+ cmd_rm() {
224
+ require_jq
225
+ require_registry
226
+ local target="${1:-}"
227
+ [ -n "$target" ] || die "Usage: egregore rm <slug-or-path>"
228
+
229
+ # Find matches: exact slug, exact path, or substring match on either
230
+ local matches
231
+ matches=$(jq -r --arg q "$target" '
232
+ .[] | select(.slug == $q or .path == $q or
233
+ (.slug | contains($q)) or (.path | contains($q)))
234
+ | [.slug, .name, .path] | @tsv' "$REGISTRY")
235
+
236
+ if [ -z "$matches" ]; then
237
+ die "No instance matching '$target'. Run: egregore ls"
238
+ fi
239
+
240
+ local match_count
241
+ match_count=$(printf '%s\n' "$matches" | wc -l | tr -d ' ')
242
+
243
+ if [ "$match_count" -gt 1 ]; then
244
+ printf '\n %s⚠%s %d instances match %s"%s"%s:\n\n' "$YELLOW" "$RESET" "$match_count" "$BOLD" "$target" "$RESET"
245
+ printf '%s\n' "$matches" | while IFS=$'\t' read -r slug name path; do
246
+ printf ' %-22s %-24s %s%s%s\n' "$slug" "$name" "$DIM" "$path" "$RESET"
247
+ done
248
+ printf '\n %sUse an exact path to pick one:%s\n' "$DIM" "$RESET"
249
+ printf ' egregore rm <full-path>\n\n'
250
+ exit 1
251
+ fi
252
+
253
+ local slug name path
254
+ IFS=$'\t' read -r slug name path <<< "$matches"
255
+ printf '\n about to remove:\n'
256
+ printf ' %-22s %-24s %s%s%s\n\n' "$slug" "$name" "$DIM" "$path" "$RESET"
257
+ printf ' %s?%s confirm? [y/N]: ' "$CYAN" "$RESET"
258
+ read -r confirm
259
+ case "$confirm" in
260
+ y|Y|yes|YES) ;;
261
+ *) printf ' %sCancelled.%s\n\n' "$DIM" "$RESET"; return 0 ;;
262
+ esac
263
+
264
+ local bak
265
+ bak=$(backup_registry)
266
+
267
+ local tmp="${REGISTRY}.tmp.$$"
268
+ jq --arg p "$path" '[.[] | select(.path != $p)]' "$REGISTRY" > "$tmp"
269
+ mv "$tmp" "$REGISTRY"
270
+
271
+ printf ' %s✓%s removed.\n' "$GREEN" "$RESET"
272
+ printf ' %sbackup:%s %s\n\n' "$DIM" "$RESET" "$bak"
273
+ }
274
+
275
+ cmd_help() {
276
+ banner
277
+ printf ' %sUSAGE%s\n' "$BOLD" "$RESET"
278
+ printf ' %segregore%s pick an instance (or launch the only one)\n' "$CYAN" "$RESET"
279
+ printf ' %segregore %s<slug|name>%s launch the matching instance\n' "$CYAN" "$DIM" "$RESET"
280
+ printf ' %segregore ls%s list installed instances\n' "$CYAN" "$RESET"
281
+ printf ' %segregore use %s<slug>%s set the default for bare `egregore`\n' "$CYAN" "$DIM" "$RESET"
282
+ printf ' %segregore prune%s remove entries whose path is gone %s(--dry-run to preview)%s\n' "$CYAN" "$RESET" "$DIM" "$RESET"
283
+ printf ' %segregore rm %s<slug-or-path>%s remove a specific entry\n' "$CYAN" "$DIM" "$RESET"
284
+ printf ' %segregore --help%s\n' "$CYAN" "$RESET"
285
+ printf ' %segregore --version%s\n' "$CYAN" "$RESET"
286
+ printf '\n %sREGISTRY%s\n' "$BOLD" "$RESET"
287
+ printf ' Instances live in ~/.egregore/instances.json (written by the installer).\n'
288
+ printf ' Default selection lives in ~/.egregore/default.\n'
289
+ printf ' Every mutation backs up to ~/.egregore/instances.json.bak.<timestamp>.\n\n'
290
+ }
291
+
292
+ # ── picker ────────────────────────────────────────────────────────
293
+ #
294
+ # Shown when the user runs bare `egregore` with multiple instances and no
295
+ # default set. Numbered list + prompt that accepts numeric or slug input.
296
+
297
+ picker() {
298
+ banner
299
+ printf ' %s◆%s pick an instance to launch:\n\n' "$CYAN" "$RESET"
300
+ local -a slug_arr=()
301
+ local -a path_arr=()
302
+ local -a name_arr=()
303
+ while IFS=$'\t' read -r slug name path; do
304
+ slug_arr+=("$slug")
305
+ path_arr+=("$path")
306
+ name_arr+=("$name")
307
+ done < <(list_instances)
308
+
309
+ local default=""
310
+ [ -f "$DEFAULT_FILE" ] && default=$(cat "$DEFAULT_FILE" 2>/dev/null)
311
+
312
+ local idx=1
313
+ for slug in "${slug_arr[@]}"; do
314
+ local marker=" "
315
+ [ "$slug" = "$default" ] && marker="${GREEN}●${RESET} "
316
+ printf ' %s%2d)%s %s %s%-22s%s %s%s%s\n' \
317
+ "$DIM" "$idx" "$RESET" "$marker" "$BOLD" "$slug" "$RESET" "$DIM" "${name_arr[$((idx-1))]}" "$RESET"
318
+ idx=$((idx + 1))
319
+ done
320
+ printf '\n'
321
+ printf ' %s?%s enter number or slug: ' "$CYAN" "$RESET"
322
+ read -r choice
323
+ [ -n "$choice" ] || die "No selection."
324
+
325
+ # Numeric choice
326
+ if printf '%s' "$choice" | grep -Eq '^[0-9]+$'; then
327
+ local chosen_idx=$((choice - 1))
328
+ if [ "$chosen_idx" -ge 0 ] && [ "$chosen_idx" -lt "${#path_arr[@]}" ]; then
329
+ launch "${path_arr[$chosen_idx]}" "${slug_arr[$chosen_idx]}"
330
+ fi
331
+ die "Invalid selection: $choice"
332
+ fi
333
+
334
+ # Non-numeric: treat as slug/name
335
+ local target
336
+ target=$(resolve_instance "$choice")
337
+ [ -n "$target" ] || die "No instance matching '$choice'."
338
+ launch "$target" "$choice"
339
+ }
340
+
341
+ # ── main ──────────────────────────────────────────────────────────
342
+
343
+ case "${1:-}" in
344
+ --help|-h|help) cmd_help; exit 0 ;;
345
+ --version|-V|version) echo "egregore $VERSION"; exit 0 ;;
346
+ ls|list) shift; cmd_ls "$@"; exit 0 ;;
347
+ use) shift; cmd_use "$@"; exit 0 ;;
348
+ prune) shift; cmd_prune "$@"; exit 0 ;;
349
+ rm|remove) shift; cmd_rm "$@"; exit 0 ;;
350
+ esac
351
+
352
+ require_jq
353
+ require_registry
354
+
355
+ count=$(instance_count)
356
+
357
+ if [ "$count" -eq 0 ]; then
358
+ die "No egregores installed yet. Run: npx -y create-egregore@latest"
359
+ fi
360
+
361
+ # Explicit instance requested — fast path, no banner
362
+ if [ $# -gt 0 ]; then
363
+ target=$(resolve_instance "$1")
364
+ [ -n "$target" ] || die "No instance matching '$1'. Run: egregore ls"
365
+ launch "$target" "$1"
366
+ fi
367
+
368
+ # No args — use default if set, or only instance, else picker
369
+ if [ -f "$DEFAULT_FILE" ]; then
370
+ default_slug=$(cat "$DEFAULT_FILE" 2>/dev/null)
371
+ if [ -n "$default_slug" ]; then
372
+ target=$(jq -r --arg q "$default_slug" '.[] | select(.slug == $q) | .path' "$REGISTRY" | head -n1)
373
+ if [ -n "$target" ]; then
374
+ launch "$target" "$default_slug"
375
+ fi
376
+ fi
377
+ fi
378
+
379
+ if [ "$count" -eq 1 ]; then
380
+ target=$(jq -r '.[0].path' "$REGISTRY")
381
+ slug=$(jq -r '.[0].slug' "$REGISTRY")
382
+ launch "$target" "$slug"
383
+ fi
384
+
385
+ picker