create-egregore 0.19.3 → 0.19.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/egregore-launcher.js +410 -26
- package/bin/cli.js +34 -20
- package/lib/local.js +84 -11
- package/lib/pi-runtime.js +186 -0
- package/lib/setup.js +56 -11
- package/lib/ui.js +1 -1
- package/package.json +8 -6
- package/runtime/codex/.codex/hooks/search-hint.js +65 -0
- package/runtime/codex/.codex/hooks.json +13 -0
- package/runtime/codex/.codex/skills/deep-reflect/SKILL.md +14 -10
- package/runtime/codex/.codex/skills/minion/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/scroll/SKILL.md +168 -0
- package/runtime/codex/.codex/skills/scroll/agents/openai.yaml +4 -0
- package/runtime/codex/.codex/skills/scroll/assets/shell.html +542 -0
- package/runtime/codex/.codex/skills/search/SKILL.md +56 -0
- package/runtime/codex/.codex/skills/session-view/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/thread/SKILL.md +25 -0
- package/runtime/codex/.codex/skills/view/SKILL.md +147 -32
- package/runtime/codex/.codex/skills/view/agents/openai.yaml +4 -0
- package/runtime/codex/.codex/skills/view/assets/compose-scaffold.html +221 -0
- package/runtime/codex/.codex/spec-manifest.json +4 -4
- package/runtime/codex/AGENTS.md +31 -5
- package/runtime/codex/bin/activity-data.sh +46 -4
- package/runtime/codex/bin/agent.sh +10 -2
- package/runtime/codex/bin/codex-install-skills.sh +1 -1
- package/runtime/codex/bin/codex-render-spec.mjs +24 -2
- package/runtime/codex/bin/codex-session-start.sh +6 -60
- package/runtime/codex/bin/codex-skill-render.mjs +20 -12
- package/runtime/codex/bin/codex-sync-skills.sh +4 -0
- package/runtime/codex/bin/graph-op.sh +56 -5
- package/runtime/codex/bin/graph.sh +36 -0
- package/runtime/codex/bin/handoff-run.sh +157 -7
- package/runtime/codex/bin/handoff-save-egregore.sh +6 -1
- package/runtime/codex/bin/index-handoff.sh +9 -3
- package/runtime/codex/bin/lib/context.sh +97 -18
- package/runtime/codex/bin/lib/dashboard-artifact.sh +1 -1
- package/runtime/codex/bin/lib/git-sync.sh +35 -9
- package/runtime/codex/bin/lib/greeting.sh +30 -104
- package/runtime/codex/bin/lib/metrics.sh +374 -0
- package/runtime/codex/bin/publish-artifact.sh +43 -1
- package/runtime/codex/bin/settings.sh +274 -0
- package/runtime/codex/manifest.json +18 -1
- package/runtime/pi/.codex/config.toml +15 -0
- package/runtime/pi/.codex/hooks/branch-guard.js +242 -0
- package/runtime/pi/.codex/hooks/search-hint.js +65 -0
- package/runtime/pi/.codex/hooks.json +30 -0
- package/runtime/pi/.codex/skills/activity/SKILL.md +70 -0
- package/runtime/pi/.codex/skills/add/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/announce/SKILL.md +54 -0
- package/runtime/pi/.codex/skills/answer/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/archive/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/ask/SKILL.md +59 -0
- package/runtime/pi/.codex/skills/branch/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/checkup/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/commit/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/contribute/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/dark-mode/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/dashboard/SKILL.md +59 -0
- package/runtime/pi/.codex/skills/deep-reflect/SKILL.md +74 -0
- package/runtime/pi/.codex/skills/delete-user/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/egregore-voice/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/emissary/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/env/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/handoff/SKILL.md +67 -0
- package/runtime/pi/.codex/skills/harvest/SKILL.md +107 -0
- package/runtime/pi/.codex/skills/ingest/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/ingest-google/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/ingest-user-interview/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/invite/SKILL.md +40 -0
- package/runtime/pi/.codex/skills/issue/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/me/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/meeting/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/minion/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/note/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/onboarding/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/pr/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/project/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/pull/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/push/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/quest/SKILL.md +77 -0
- package/runtime/pi/.codex/skills/quest-suggest/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/reflect/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/review-pr/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/save/SKILL.md +62 -0
- package/runtime/pi/.codex/skills/scroll/SKILL.md +168 -0
- package/runtime/pi/.codex/skills/scroll/agents/openai.yaml +4 -0
- package/runtime/pi/.codex/skills/scroll/assets/shell.html +542 -0
- package/runtime/pi/.codex/skills/search/SKILL.md +56 -0
- package/runtime/pi/.codex/skills/session-view/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/setup/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/summon/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/sync-repos/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/telegram-connect/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/telemetry/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/test/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/the-spiral/SKILL.md +84 -0
- package/runtime/pi/.codex/skills/thread/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/todo/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/tui-design/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/tutorial/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/update/SKILL.md +25 -0
- package/runtime/pi/.codex/skills/view/SKILL.md +156 -0
- package/runtime/pi/.codex/skills/view/agents/openai.yaml +4 -0
- package/runtime/pi/.codex/skills/view/assets/compose-scaffold.html +221 -0
- package/runtime/pi/.codex/skills/visual-explain/SKILL.md +41 -0
- package/runtime/pi/.codex/skills/wrap/SKILL.md +55 -0
- package/runtime/pi/.codex/spec-manifest.json +114 -0
- package/runtime/pi/.pi/APPEND_SYSTEM.md +255 -0
- package/runtime/pi/.pi/extensions/egregore.ts +324 -0
- package/runtime/pi/.pi/settings.json +6 -0
- package/runtime/pi/.pi/spec-manifest.json +12 -0
- package/runtime/pi/AGENTS.md +350 -0
- package/runtime/pi/bin/activity-data.sh +242 -0
- package/runtime/pi/bin/agent.sh +677 -0
- package/runtime/pi/bin/codex-install-skills.sh +78 -0
- package/runtime/pi/bin/codex-render-spec.mjs +394 -0
- package/runtime/pi/bin/codex-session-start.sh +188 -0
- package/runtime/pi/bin/codex-skill-render.mjs +323 -0
- package/runtime/pi/bin/codex-sync-skills.sh +227 -0
- package/runtime/pi/bin/dashboard-data.sh +178 -0
- package/runtime/pi/bin/graph-batch.sh +60 -0
- package/runtime/pi/bin/graph-op.sh +431 -0
- package/runtime/pi/bin/graph-wal.sh +228 -0
- package/runtime/pi/bin/graph.sh +177 -0
- package/runtime/pi/bin/handoff-pr-backfill.sh +86 -0
- package/runtime/pi/bin/handoff-run.sh +581 -0
- package/runtime/pi/bin/handoff-save-egregore.sh +136 -0
- package/runtime/pi/bin/index-handoff.sh +312 -0
- package/runtime/pi/bin/lib/artifact-id.sh +46 -0
- package/runtime/pi/bin/lib/config.sh +71 -0
- package/runtime/pi/bin/lib/context.sh +542 -0
- package/runtime/pi/bin/lib/dashboard-artifact.sh +142 -0
- package/runtime/pi/bin/lib/ensure-develop.sh +183 -0
- package/runtime/pi/bin/lib/git-sync.sh +288 -0
- package/runtime/pi/bin/lib/greeting.sh +340 -0
- package/runtime/pi/bin/lib/hash.sh +13 -0
- package/runtime/pi/bin/lib/identity.sh +174 -0
- package/runtime/pi/bin/lib/metrics.sh +374 -0
- package/runtime/pi/bin/lib/permissions.sh +106 -0
- package/runtime/pi/bin/lib/time.sh +56 -0
- package/runtime/pi/bin/lib/worktree-links.sh +54 -0
- package/runtime/pi/bin/notify.sh +207 -0
- package/runtime/pi/bin/pi-branch-policy.mjs +15 -0
- package/runtime/pi/bin/pi-product-workflows.mjs +324 -0
- package/runtime/pi/bin/pi-render-spec.mjs +114 -0
- package/runtime/pi/bin/pi-session-start.sh +24 -0
- package/runtime/pi/bin/publish-artifact.sh +201 -0
- package/runtime/pi/bin/publish-references.sh +144 -0
- package/runtime/pi/bin/repo-state.sh +99 -0
- package/runtime/pi/bin/telemetry.sh +391 -0
- package/runtime/pi/bin/worktree-create.sh +118 -0
- package/runtime/pi/bin/worktree-remove.sh +50 -0
- package/runtime/pi/bin/worktree.sh +198 -0
- package/runtime/pi/manifest.json +176 -0
|
@@ -0,0 +1,274 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
set -euo pipefail
|
|
3
|
+
|
|
4
|
+
# settings.sh — deterministic config verbs for an Egregore instance.
|
|
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.
|
|
8
|
+
# This is the deterministic core the launcher settings screen (and slash
|
|
9
|
+
# commands) call — the reliability lives here, the surfaces are thin callers.
|
|
10
|
+
#
|
|
11
|
+
# Usage:
|
|
12
|
+
# settings.sh hosting status|on|off
|
|
13
|
+
# settings.sh repo list | add <name> [description] | remove <name>
|
|
14
|
+
# settings.sh admin list | add <github-handle> | remove <github-handle>
|
|
15
|
+
# settings.sh dump # full settings snapshot as JSON (for the launcher)
|
|
16
|
+
#
|
|
17
|
+
# Add --json to a read verb (status/list) for machine-readable output.
|
|
18
|
+
#
|
|
19
|
+
# Exit codes: 0 ok · 1 usage/validation error · 2 config not found.
|
|
20
|
+
|
|
21
|
+
SCRIPT_DIR="$(cd "$(dirname "$0")/.." && pwd)"
|
|
22
|
+
CONFIG="$SCRIPT_DIR/egregore.json"
|
|
23
|
+
|
|
24
|
+
# ── helpers ──────────────────────────────────────────────────────────────
|
|
25
|
+
|
|
26
|
+
_die() { echo "$1" >&2; exit "${2:-1}"; }
|
|
27
|
+
|
|
28
|
+
_valid_token() {
|
|
29
|
+
# non-empty, GitHub-handle / repo-name safe charset
|
|
30
|
+
[ -n "${1:-}" ] && printf '%s' "$1" | grep -qE '^[A-Za-z0-9._-]+$'
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
_save() {
|
|
34
|
+
# _save '<jq-filter>' [jq-args...] — apply filter to CONFIG, write atomically.
|
|
35
|
+
local filter="$1"; shift
|
|
36
|
+
local tmp
|
|
37
|
+
tmp="$(mktemp "$CONFIG.XXXXXX")" || _die "cannot create temp file" 1
|
|
38
|
+
if jq "$@" "$filter" "$CONFIG" > "$tmp" 2>/dev/null && [ -s "$tmp" ]; then
|
|
39
|
+
mv "$tmp" "$CONFIG"
|
|
40
|
+
else
|
|
41
|
+
rm -f "$tmp"
|
|
42
|
+
_die "failed to update $CONFIG (invalid jq filter or unwritable file)" 1
|
|
43
|
+
fi
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
_has_repo() { jq -e --arg n "$1" 'any(.repos[]?; (if type=="object" then .name else . end) == $n)' "$CONFIG" >/dev/null; }
|
|
47
|
+
_has_admin() { jq -e --arg h "$1" 'any(.admins[]?; . == $h)' "$CONFIG" >/dev/null; }
|
|
48
|
+
|
|
49
|
+
usage() {
|
|
50
|
+
cat >&2 <<'EOF'
|
|
51
|
+
settings.sh — Egregore instance settings
|
|
52
|
+
|
|
53
|
+
settings.sh hosting status|on|off
|
|
54
|
+
settings.sh repo list | add <name> [description] | remove <name>
|
|
55
|
+
settings.sh admin list | add <github-handle> | remove <github-handle>
|
|
56
|
+
settings.sh people list | add <github-handle> | remove <github-handle>
|
|
57
|
+
settings.sh dump full snapshot (JSON)
|
|
58
|
+
|
|
59
|
+
--json machine-readable output for status/list
|
|
60
|
+
EOF
|
|
61
|
+
exit "${1:-1}"
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
# ── verbs ────────────────────────────────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
cmd_hosting() {
|
|
67
|
+
local action="${1:-status}"
|
|
68
|
+
# NEVER use jq's `// "true"` here: `//` treats an explicit `false` as empty and
|
|
69
|
+
# would return the default, silently defeating the off-switch. Test `!= false`
|
|
70
|
+
# so absent/true => enabled, explicit false => disabled. `enabled` is the
|
|
71
|
+
# resolved boolean string ("true"/"false").
|
|
72
|
+
local enabled; enabled="$(jq -r '.features.publishing != false' "$CONFIG" 2>/dev/null || echo "true")"
|
|
73
|
+
case "$action" in
|
|
74
|
+
status)
|
|
75
|
+
if [ "$JSON" = 1 ]; then
|
|
76
|
+
jq -n --argjson on "$enabled" '{domain:"hosting", publishing:$on}'
|
|
77
|
+
elif [ "$enabled" = "false" ]; then echo "Hosting (egregore.xyz): OFF"
|
|
78
|
+
else echo "Hosting (egregore.xyz): ON"; fi ;;
|
|
79
|
+
on)
|
|
80
|
+
_save '.features.publishing = true'
|
|
81
|
+
echo "Hosting (egregore.xyz): ON" ;;
|
|
82
|
+
off)
|
|
83
|
+
_save '.features.publishing = false'
|
|
84
|
+
echo "Hosting (egregore.xyz): OFF — artifacts will no longer upload to egregore.xyz" ;;
|
|
85
|
+
*) _die "usage: settings.sh hosting status|on|off" 1 ;;
|
|
86
|
+
esac
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
cmd_repo() {
|
|
90
|
+
local action="${1:-list}"; shift || true
|
|
91
|
+
case "$action" in
|
|
92
|
+
list)
|
|
93
|
+
if [ "$JSON" = 1 ]; then
|
|
94
|
+
jq '[.repos[]? | if type=="object" then . else {name:.} end]' "$CONFIG"
|
|
95
|
+
else
|
|
96
|
+
jq -r '.repos[]? | if type=="object" then " " + .name + (if .description then " — " + .description else "" end) else " " + . end' "$CONFIG"
|
|
97
|
+
fi ;;
|
|
98
|
+
add)
|
|
99
|
+
local name="${1:-}"; shift || true
|
|
100
|
+
local desc="${*:-}"
|
|
101
|
+
_valid_token "$name" || _die "usage: settings.sh repo add <name> [description]" 1
|
|
102
|
+
if _has_repo "$name"; then echo "repo '$name' already managed"; return 0; fi
|
|
103
|
+
if [ -n "$desc" ]; then
|
|
104
|
+
_save '.repos = ((.repos // []) + [{name:$n, description:$d}])' --arg n "$name" --arg d "$desc"
|
|
105
|
+
else
|
|
106
|
+
_save '.repos = ((.repos // []) + [{name:$n}])' --arg n "$name"
|
|
107
|
+
fi
|
|
108
|
+
echo "added repo '$name'"
|
|
109
|
+
echo " clone it with: /sync-repos (or reopen the egregore)" ;;
|
|
110
|
+
remove)
|
|
111
|
+
local name="${1:-}"
|
|
112
|
+
_valid_token "$name" || _die "usage: settings.sh repo remove <name>" 1
|
|
113
|
+
if ! _has_repo "$name"; then echo "repo '$name' is not managed"; return 0; fi
|
|
114
|
+
_save '.repos = [.repos[]? | select((if type=="object" then .name else . end) != $n)]' --arg n "$name"
|
|
115
|
+
echo "removed repo '$name' from config"
|
|
116
|
+
echo " note: the local ../$name checkout is left untouched" ;;
|
|
117
|
+
*) _die "usage: settings.sh repo list|add|remove" 1 ;;
|
|
118
|
+
esac
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
cmd_admin() {
|
|
122
|
+
local action="${1:-list}"; shift || true
|
|
123
|
+
case "$action" in
|
|
124
|
+
list)
|
|
125
|
+
if [ "$JSON" = 1 ]; then jq '{admins:(.admins // [])}' "$CONFIG"
|
|
126
|
+
else jq -r '.admins[]? | " " + .' "$CONFIG"; fi ;;
|
|
127
|
+
add)
|
|
128
|
+
local h="${1:-}"
|
|
129
|
+
_valid_token "$h" || _die "usage: settings.sh admin add <github-handle>" 1
|
|
130
|
+
if _has_admin "$h"; then echo "'$h' is already an admin"; return 0; fi
|
|
131
|
+
_save '.admins = ((.admins // []) + [$h])' --arg h "$h"
|
|
132
|
+
echo "added admin '$h'" ;;
|
|
133
|
+
remove)
|
|
134
|
+
local h="${1:-}"
|
|
135
|
+
_valid_token "$h" || _die "usage: settings.sh admin remove <github-handle>" 1
|
|
136
|
+
if ! _has_admin "$h"; then echo "'$h' is not an admin"; return 0; fi
|
|
137
|
+
local count; count="$(jq '(.admins // []) | length' "$CONFIG")"
|
|
138
|
+
[ "$count" -le 1 ] && _die "refusing to remove the last admin ('$h') — add another admin first" 1
|
|
139
|
+
_save '.admins = [.admins[]? | select(. != $h)]' --arg h "$h"
|
|
140
|
+
echo "removed admin '$h'" ;;
|
|
141
|
+
*) _die "usage: settings.sh admin list|add|remove" 1 ;;
|
|
142
|
+
esac
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
# ── people ───────────────────────────────────────────────────────────────
|
|
146
|
+
# People live as files in memory/people/. add/remove manage that directory AND
|
|
147
|
+
# GitHub repo access (the meaningful local grant). Connected-mode extras — the
|
|
148
|
+
# invite link + Telegram (/invite) and Supabase/Neo4j teardown (/delete-user) —
|
|
149
|
+
# are pointed to, not duplicated here.
|
|
150
|
+
|
|
151
|
+
_people_dir() { echo "$SCRIPT_DIR/memory/people"; }
|
|
152
|
+
|
|
153
|
+
_people_names() {
|
|
154
|
+
local pdir; pdir="$(_people_dir)"
|
|
155
|
+
[ -d "$pdir" ] || return 0
|
|
156
|
+
ls "$pdir"/*.md 2>/dev/null | while IFS= read -r f; do basename "$f" .md; done \
|
|
157
|
+
| grep -viE '^(index|readme)$' || true
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
_people_json() {
|
|
161
|
+
local names; names="$(_people_names)"
|
|
162
|
+
if [ -z "$names" ]; then echo '[]'; return; fi
|
|
163
|
+
printf '%s\n' "$names" | jq -R . | jq -sc 'map(select(length>0))'
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
_has_person() { [ -f "$(_people_dir)/$1.md" ]; }
|
|
167
|
+
|
|
168
|
+
# add|remove a GitHub push-collaborator across the instance's repos. Best-effort:
|
|
169
|
+
# no token/org => skip with a note; per-repo failures never abort.
|
|
170
|
+
_people_github() {
|
|
171
|
+
local user="$1" op="$2" token org repos r
|
|
172
|
+
token="$(grep '^GITHUB_TOKEN=' "$SCRIPT_DIR/.env" 2>/dev/null | cut -d= -f2- || true)"
|
|
173
|
+
org="$(jq -r '.github_org // empty' "$CONFIG" 2>/dev/null)"
|
|
174
|
+
if [ -z "$token" ] || [ -z "$org" ]; then
|
|
175
|
+
echo " (no GitHub token/org — skipped repo access; run bin/github-auth.sh)" >&2
|
|
176
|
+
return 0
|
|
177
|
+
fi
|
|
178
|
+
repos="$(jq -r '[.repo_name, (.memory_repo // "" | split("/") | last | sub("\\.git$";"")), (.repos[]? | if type=="object" then .name else . end)] | map(select(. != null and . != "")) | unique | .[]' "$CONFIG" 2>/dev/null)"
|
|
179
|
+
for r in $repos; do
|
|
180
|
+
if [ "$op" = "add" ]; then
|
|
181
|
+
curl -s -o /dev/null -X PUT -H "Authorization: Bearer $token" -H "Accept: application/vnd.github+json" \
|
|
182
|
+
"https://api.github.com/repos/$org/$r/collaborators/$user" -d '{"permission":"push"}' 2>/dev/null || true
|
|
183
|
+
else
|
|
184
|
+
curl -s -o /dev/null -X DELETE -H "Authorization: Bearer $token" -H "Accept: application/vnd.github+json" \
|
|
185
|
+
"https://api.github.com/repos/$org/$r/collaborators/$user" 2>/dev/null || true
|
|
186
|
+
fi
|
|
187
|
+
done
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
# create|remove memory/people/<user>.md, committing ONLY the people dir.
|
|
191
|
+
_people_file() {
|
|
192
|
+
local user="$1" op="$2" pdir f inviter today
|
|
193
|
+
pdir="$(_people_dir)"
|
|
194
|
+
[ -d "$SCRIPT_DIR/memory" ] || { echo " (no memory dir — skipped person file)" >&2; return 0; }
|
|
195
|
+
mkdir -p "$pdir"
|
|
196
|
+
f="$pdir/$user.md"
|
|
197
|
+
if [ "$op" = "add" ]; then
|
|
198
|
+
if [ ! -f "$f" ]; then
|
|
199
|
+
inviter="$(jq -r '.display_name // .github_username // "admin"' "$SCRIPT_DIR/.egregore-state.json" 2>/dev/null || echo admin)"
|
|
200
|
+
today="$(date -u +%Y-%m-%d)"
|
|
201
|
+
printf -- '---\nname: %s\ngithub: %s\ninvited_by: %s\njoined: %s\n---\n' "$user" "$user" "$inviter" "$today" > "$f"
|
|
202
|
+
fi
|
|
203
|
+
else
|
|
204
|
+
rm -f "$f"
|
|
205
|
+
fi
|
|
206
|
+
( cd "$SCRIPT_DIR/memory" 2>/dev/null && git add -A people/ 2>/dev/null \
|
|
207
|
+
&& git commit -q -m "settings: people $op $user" 2>/dev/null \
|
|
208
|
+
&& git push -q 2>/dev/null ) || true
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
cmd_people() {
|
|
212
|
+
local action="${1:-list}"; shift || true
|
|
213
|
+
case "$action" in
|
|
214
|
+
list)
|
|
215
|
+
if [ "$JSON" = 1 ]; then _people_json
|
|
216
|
+
else
|
|
217
|
+
local names; names="$(_people_names)"
|
|
218
|
+
if [ -n "$names" ]; then printf ' %s\n' $names; else echo " (none)"; fi
|
|
219
|
+
fi ;;
|
|
220
|
+
add)
|
|
221
|
+
local h="${1:-}"
|
|
222
|
+
_valid_token "$h" || _die "usage: settings.sh people add <github-handle>" 1
|
|
223
|
+
if _has_person "$h"; then echo "'$h' is already in people"; return 0; fi
|
|
224
|
+
_people_github "$h" "add"
|
|
225
|
+
_people_file "$h" "add"
|
|
226
|
+
echo "added '$h' (repo access + person file)"
|
|
227
|
+
[ -n "$(jq -r '.api_url // empty' "$CONFIG" 2>/dev/null)" ] && echo " connected org: for the invite link + Telegram, run /invite $h" ;;
|
|
228
|
+
remove)
|
|
229
|
+
local h="${1:-}"
|
|
230
|
+
_valid_token "$h" || _die "usage: settings.sh people remove <github-handle>" 1
|
|
231
|
+
if ! _has_person "$h"; then echo "'$h' is not in people"; return 0; fi
|
|
232
|
+
_people_github "$h" "remove"
|
|
233
|
+
_people_file "$h" "remove"
|
|
234
|
+
echo "removed '$h' (repo access + person file)"
|
|
235
|
+
[ -n "$(jq -r '.api_url // empty' "$CONFIG" 2>/dev/null)" ] && echo " connected org: for full deprovision (Supabase/Neo4j), run /delete-user $h" ;;
|
|
236
|
+
*) _die "usage: settings.sh people list|add|remove" 1 ;;
|
|
237
|
+
esac
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
cmd_dump() {
|
|
241
|
+
# Always JSON — the launcher calls this once to render the whole settings screen.
|
|
242
|
+
local people; people="$(_people_json)"
|
|
243
|
+
jq --argjson people "$people" '{
|
|
244
|
+
org: .org_name,
|
|
245
|
+
slug: .slug,
|
|
246
|
+
hosting: (.features.publishing != false),
|
|
247
|
+
repos: [.repos[]? | if type=="object" then . else {name:.} end],
|
|
248
|
+
admins: (.admins // []),
|
|
249
|
+
people: $people
|
|
250
|
+
}' "$CONFIG"
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
# ── dispatch ─────────────────────────────────────────────────────────────
|
|
254
|
+
|
|
255
|
+
[ -f "$CONFIG" ] || _die "egregore.json not found at $CONFIG" 2
|
|
256
|
+
|
|
257
|
+
# Pull out --json (bash 3.2-safe array handling), leave the rest as positionals.
|
|
258
|
+
JSON=0
|
|
259
|
+
_args=()
|
|
260
|
+
for _a in "$@"; do
|
|
261
|
+
if [ "$_a" = "--json" ]; then JSON=1; else _args+=("$_a"); fi
|
|
262
|
+
done
|
|
263
|
+
set -- "${_args[@]+"${_args[@]}"}"
|
|
264
|
+
|
|
265
|
+
DOMAIN="${1:-}"; shift || true
|
|
266
|
+
case "$DOMAIN" in
|
|
267
|
+
hosting) cmd_hosting "$@" ;;
|
|
268
|
+
repo) cmd_repo "$@" ;;
|
|
269
|
+
admin) cmd_admin "$@" ;;
|
|
270
|
+
people) cmd_people "$@" ;;
|
|
271
|
+
dump) cmd_dump ;;
|
|
272
|
+
""|-h|--help|help) usage 0 ;;
|
|
273
|
+
*) _die "unknown settings domain: '$DOMAIN' (try: hosting, repo, admin, people, dump)" 1 ;;
|
|
274
|
+
esac
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"source": "egregore-runtime",
|
|
3
3
|
"policy": "public-safe subset derived from .syncignore; built by scripts/build-runtime-bundle.mjs",
|
|
4
|
-
"skillCount":
|
|
4
|
+
"skillCount": 56,
|
|
5
5
|
"skills": [
|
|
6
6
|
"activity",
|
|
7
7
|
"add",
|
|
@@ -29,6 +29,7 @@
|
|
|
29
29
|
"issue",
|
|
30
30
|
"me",
|
|
31
31
|
"meeting",
|
|
32
|
+
"minion",
|
|
32
33
|
"note",
|
|
33
34
|
"onboarding",
|
|
34
35
|
"pr",
|
|
@@ -40,6 +41,9 @@
|
|
|
40
41
|
"reflect",
|
|
41
42
|
"review-pr",
|
|
42
43
|
"save",
|
|
44
|
+
"scroll",
|
|
45
|
+
"search",
|
|
46
|
+
"session-view",
|
|
43
47
|
"setup",
|
|
44
48
|
"summon",
|
|
45
49
|
"sync-repos",
|
|
@@ -47,6 +51,7 @@
|
|
|
47
51
|
"telemetry",
|
|
48
52
|
"test",
|
|
49
53
|
"the-spiral",
|
|
54
|
+
"thread",
|
|
50
55
|
"todo",
|
|
51
56
|
"tui-design",
|
|
52
57
|
"tutorial",
|
|
@@ -60,6 +65,7 @@
|
|
|
60
65
|
".codex/config.toml",
|
|
61
66
|
".codex/hooks.json",
|
|
62
67
|
".codex/hooks/branch-guard.js",
|
|
68
|
+
".codex/hooks/search-hint.js",
|
|
63
69
|
".codex/skills/activity/SKILL.md",
|
|
64
70
|
".codex/skills/add/SKILL.md",
|
|
65
71
|
".codex/skills/announce/SKILL.md",
|
|
@@ -86,6 +92,7 @@
|
|
|
86
92
|
".codex/skills/issue/SKILL.md",
|
|
87
93
|
".codex/skills/me/SKILL.md",
|
|
88
94
|
".codex/skills/meeting/SKILL.md",
|
|
95
|
+
".codex/skills/minion/SKILL.md",
|
|
89
96
|
".codex/skills/note/SKILL.md",
|
|
90
97
|
".codex/skills/onboarding/SKILL.md",
|
|
91
98
|
".codex/skills/pr/SKILL.md",
|
|
@@ -97,6 +104,11 @@
|
|
|
97
104
|
".codex/skills/reflect/SKILL.md",
|
|
98
105
|
".codex/skills/review-pr/SKILL.md",
|
|
99
106
|
".codex/skills/save/SKILL.md",
|
|
107
|
+
".codex/skills/scroll/SKILL.md",
|
|
108
|
+
".codex/skills/scroll/agents/openai.yaml",
|
|
109
|
+
".codex/skills/scroll/assets/shell.html",
|
|
110
|
+
".codex/skills/search/SKILL.md",
|
|
111
|
+
".codex/skills/session-view/SKILL.md",
|
|
100
112
|
".codex/skills/setup/SKILL.md",
|
|
101
113
|
".codex/skills/summon/SKILL.md",
|
|
102
114
|
".codex/skills/sync-repos/SKILL.md",
|
|
@@ -104,11 +116,14 @@
|
|
|
104
116
|
".codex/skills/telemetry/SKILL.md",
|
|
105
117
|
".codex/skills/test/SKILL.md",
|
|
106
118
|
".codex/skills/the-spiral/SKILL.md",
|
|
119
|
+
".codex/skills/thread/SKILL.md",
|
|
107
120
|
".codex/skills/todo/SKILL.md",
|
|
108
121
|
".codex/skills/tui-design/SKILL.md",
|
|
109
122
|
".codex/skills/tutorial/SKILL.md",
|
|
110
123
|
".codex/skills/update/SKILL.md",
|
|
111
124
|
".codex/skills/view/SKILL.md",
|
|
125
|
+
".codex/skills/view/agents/openai.yaml",
|
|
126
|
+
".codex/skills/view/assets/compose-scaffold.html",
|
|
112
127
|
".codex/skills/visual-explain/SKILL.md",
|
|
113
128
|
".codex/skills/wrap/SKILL.md",
|
|
114
129
|
".codex/spec-manifest.json",
|
|
@@ -138,6 +153,7 @@
|
|
|
138
153
|
"bin/lib/greeting.sh",
|
|
139
154
|
"bin/lib/hash.sh",
|
|
140
155
|
"bin/lib/identity.sh",
|
|
156
|
+
"bin/lib/metrics.sh",
|
|
141
157
|
"bin/lib/permissions.sh",
|
|
142
158
|
"bin/lib/time.sh",
|
|
143
159
|
"bin/lib/worktree-links.sh",
|
|
@@ -145,6 +161,7 @@
|
|
|
145
161
|
"bin/publish-artifact.sh",
|
|
146
162
|
"bin/publish-references.sh",
|
|
147
163
|
"bin/repo-state.sh",
|
|
164
|
+
"bin/settings.sh",
|
|
148
165
|
"bin/telemetry.sh",
|
|
149
166
|
"bin/worktree-create.sh",
|
|
150
167
|
"bin/worktree-remove.sh",
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
[features]
|
|
2
|
+
hooks = true
|
|
3
|
+
|
|
4
|
+
[tui]
|
|
5
|
+
status_line = [
|
|
6
|
+
"model-with-reasoning",
|
|
7
|
+
"git-branch",
|
|
8
|
+
"current-dir",
|
|
9
|
+
"context-remaining",
|
|
10
|
+
"five-hour-limit",
|
|
11
|
+
"weekly-limit",
|
|
12
|
+
"permissions",
|
|
13
|
+
"approval-mode",
|
|
14
|
+
]
|
|
15
|
+
status_line_use_colors = true
|
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
|
|
4
|
+
const fs = require("node:fs");
|
|
5
|
+
const path = require("node:path");
|
|
6
|
+
const { spawnSync } = require("node:child_process");
|
|
7
|
+
|
|
8
|
+
function readStdin() {
|
|
9
|
+
try {
|
|
10
|
+
return fs.readFileSync(0, "utf-8");
|
|
11
|
+
} catch {
|
|
12
|
+
return "";
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
function parseJson(raw) {
|
|
17
|
+
try {
|
|
18
|
+
return JSON.parse(raw || "{}");
|
|
19
|
+
} catch {
|
|
20
|
+
return {};
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function run(cmd, args, cwd) {
|
|
25
|
+
const result = spawnSync(cmd, args, { cwd, encoding: "utf-8", stdio: ["ignore", "pipe", "pipe"] });
|
|
26
|
+
if (result.status !== 0) return "";
|
|
27
|
+
return String(result.stdout || "").trim();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function realish(filePath) {
|
|
31
|
+
const absolute = path.resolve(filePath);
|
|
32
|
+
let cursor = absolute;
|
|
33
|
+
while (!fs.existsSync(cursor)) {
|
|
34
|
+
const parent = path.dirname(cursor);
|
|
35
|
+
if (parent === cursor) return absolute;
|
|
36
|
+
cursor = parent;
|
|
37
|
+
}
|
|
38
|
+
try {
|
|
39
|
+
const resolved = fs.realpathSync(cursor);
|
|
40
|
+
return path.join(resolved, path.relative(cursor, absolute));
|
|
41
|
+
} catch {
|
|
42
|
+
return absolute;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function inside(child, parent) {
|
|
47
|
+
const rel = path.relative(parent, child);
|
|
48
|
+
return rel === "" || (!!rel && !rel.startsWith("..") && !path.isAbsolute(rel));
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
function findProjectDir(input) {
|
|
52
|
+
// Tool payload cwd is the operation location. Codex launchers may keep
|
|
53
|
+
// EGREGORE_CODEX_PROJECT_DIR pointed at the main checkout even after the
|
|
54
|
+
// model continues in a task worktree, so using the env var first can make
|
|
55
|
+
// legitimate worktree writes look like writes on protected develop.
|
|
56
|
+
const cwd = input.cwd || process.cwd() || process.env.EGREGORE_CODEX_PROJECT_DIR;
|
|
57
|
+
const top = run("git", ["-C", cwd, "rev-parse", "--show-toplevel"], cwd);
|
|
58
|
+
if (top) return realish(top);
|
|
59
|
+
|
|
60
|
+
const envDir = process.env.EGREGORE_CODEX_PROJECT_DIR || "";
|
|
61
|
+
if (envDir) {
|
|
62
|
+
const envTop = run("git", ["-C", envDir, "rev-parse", "--show-toplevel"], envDir);
|
|
63
|
+
return realish(envTop || envDir);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
return realish(cwd);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function currentBranch(projectDir) {
|
|
70
|
+
return run("git", ["-C", projectDir, "rev-parse", "--abbrev-ref", "HEAD"], projectDir);
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function currentAuthor(projectDir) {
|
|
74
|
+
try {
|
|
75
|
+
const raw = fs.readFileSync(path.join(projectDir, ".egregore-state.json"), "utf-8");
|
|
76
|
+
const state = JSON.parse(raw);
|
|
77
|
+
const value = state.handle || state.display_name || state.name || "dev";
|
|
78
|
+
return String(value).trim().toLowerCase().replace(/[^a-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "") || "dev";
|
|
79
|
+
} catch {
|
|
80
|
+
return "dev";
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function isProtectedBranch(branch) {
|
|
85
|
+
return branch === "develop" || branch === "main" || branch === "master";
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function isExemptPath(rawPath, projectDir, memoryDir) {
|
|
89
|
+
if (!rawPath) return false;
|
|
90
|
+
const absolute = path.isAbsolute(rawPath) ? rawPath : path.join(projectDir, rawPath);
|
|
91
|
+
const resolved = realish(absolute);
|
|
92
|
+
|
|
93
|
+
for (const dir of [".claude", ".codex", "memory"]) {
|
|
94
|
+
const target = realish(path.join(projectDir, dir));
|
|
95
|
+
if (inside(resolved, target)) return true;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
for (const file of [".egregore-state.json", ".env"]) {
|
|
99
|
+
if (resolved === realish(path.join(projectDir, file))) return true;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (memoryDir && inside(resolved, memoryDir)) return true;
|
|
103
|
+
|
|
104
|
+
return !inside(resolved, projectDir);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
function extractApplyPatchPaths(command) {
|
|
108
|
+
const paths = [];
|
|
109
|
+
for (const line of String(command || "").split(/\r?\n/)) {
|
|
110
|
+
const match = line.match(/^\*\*\* (?:Add|Update|Delete) File: (.+)$/) || line.match(/^\*\*\* Move to: (.+)$/);
|
|
111
|
+
if (match && match[1]) paths.push(match[1].trim());
|
|
112
|
+
}
|
|
113
|
+
return paths;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function commandTargetsOtherRepo(command, projectDir, memoryDir) {
|
|
117
|
+
const paths = [];
|
|
118
|
+
const patterns = [
|
|
119
|
+
/\bcd\s+["']?([^"';&|]+)["']?/g,
|
|
120
|
+
/\bgit\s+-C\s+["']?([^"';&|]+)["']?/g,
|
|
121
|
+
];
|
|
122
|
+
for (const pattern of patterns) {
|
|
123
|
+
let match;
|
|
124
|
+
while ((match = pattern.exec(command))) paths.push(match[1]);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
for (const raw of paths) {
|
|
128
|
+
const resolved = realish(path.isAbsolute(raw) ? raw : path.join(projectDir, raw));
|
|
129
|
+
if (memoryDir && inside(resolved, memoryDir)) return true;
|
|
130
|
+
if (!inside(resolved, projectDir)) return true;
|
|
131
|
+
}
|
|
132
|
+
return false;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
function isBranchSetupCommand(command) {
|
|
136
|
+
return (
|
|
137
|
+
/\bbin\/agent\.sh\s+branch\b/.test(command) ||
|
|
138
|
+
/\bgit\s+(checkout|switch)\s+(-b|-c)\b/.test(command) ||
|
|
139
|
+
/\bgit\s+branch\s+(dev|feature|bugfix)\//.test(command) ||
|
|
140
|
+
/\bgit\s+worktree\s+add\b/.test(command) ||
|
|
141
|
+
/\bbin\/worktree\.sh\s+setup\b/.test(command)
|
|
142
|
+
);
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function stagedOnlyFrameworkPaths(projectDir) {
|
|
146
|
+
const files = run("git", ["-C", projectDir, "diff", "--cached", "--name-only"], projectDir)
|
|
147
|
+
.split(/\r?\n/)
|
|
148
|
+
.filter(Boolean);
|
|
149
|
+
return files.length > 0 && files.every((file) => /^(bin\/|\.claude\/|\.codex\/|CLAUDE\.md$|AGENTS\.md$|skills\/)/.test(file));
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
function redirectLooksMutating(command, projectDir, memoryDir) {
|
|
153
|
+
const redirectPatterns = [
|
|
154
|
+
/(?:^|[\s;|&])(?:\d*)>>?\s*([^&\s;|]+)/g,
|
|
155
|
+
/\|\s*tee\s+(?:-a\s+)?([^&\s;|]+)/g,
|
|
156
|
+
];
|
|
157
|
+
|
|
158
|
+
for (const pattern of redirectPatterns) {
|
|
159
|
+
let match;
|
|
160
|
+
while ((match = pattern.exec(command))) {
|
|
161
|
+
const target = String(match[1] || "").replace(/^["']|["']$/g, "");
|
|
162
|
+
if (!target || target === "/dev/null" || target.startsWith("$")) continue;
|
|
163
|
+
if (!isExemptPath(target, projectDir, memoryDir)) return true;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
return false;
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function shellLooksMutating(command, projectDir, memoryDir) {
|
|
170
|
+
if (!command.trim()) return false;
|
|
171
|
+
if (isBranchSetupCommand(command)) return false;
|
|
172
|
+
|
|
173
|
+
const mutatingPatterns = [
|
|
174
|
+
/\bgit\s+(add|commit|push|rm|mv|reset|restore|checkout\s+--|clean|rebase|merge|cherry-pick)\b/,
|
|
175
|
+
/\b(apply_patch|touch|mkdir|rm|mv|cp|chmod|chown|ln)\b/,
|
|
176
|
+
/\b(sed|perl)\b[^;&|]*\s-i(\s|$)/,
|
|
177
|
+
/\b(npm|pnpm|yarn|bun)\s+(install|add|remove|update|dedupe|ci)\b/,
|
|
178
|
+
/\b(go\s+mod\s+tidy|cargo\s+(fmt|fix|update)|rustfmt|prettier\s+--write|eslint\s+--fix)\b/,
|
|
179
|
+
];
|
|
180
|
+
|
|
181
|
+
if (/\bgit\s+commit\b/.test(command) && stagedOnlyFrameworkPaths(projectDir)) return false;
|
|
182
|
+
return mutatingPatterns.some((pattern) => pattern.test(command)) || redirectLooksMutating(command, projectDir, memoryDir);
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
function deny(reason) {
|
|
186
|
+
process.stdout.write(JSON.stringify({
|
|
187
|
+
hookSpecificOutput: {
|
|
188
|
+
hookEventName: "PreToolUse",
|
|
189
|
+
permissionDecision: "deny",
|
|
190
|
+
permissionDecisionReason: reason,
|
|
191
|
+
},
|
|
192
|
+
}));
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function main() {
|
|
196
|
+
const input = parseJson(readStdin());
|
|
197
|
+
const projectDir = findProjectDir(input);
|
|
198
|
+
const branch = currentBranch(projectDir);
|
|
199
|
+
if (!isProtectedBranch(branch)) return;
|
|
200
|
+
|
|
201
|
+
// Consent bypass: the user explicitly chose to work on this protected branch.
|
|
202
|
+
// Written only after they pick "Proceed on <branch>"; cleared on session start,
|
|
203
|
+
// so consent is asked once per session, not per write.
|
|
204
|
+
try {
|
|
205
|
+
const consent = fs.readFileSync(path.join(projectDir, ".egregore-branch-consent"), "utf-8").trim();
|
|
206
|
+
if (consent === branch) return;
|
|
207
|
+
} catch {}
|
|
208
|
+
|
|
209
|
+
const toolName = input.tool_name || "";
|
|
210
|
+
const toolInput = input.tool_input || {};
|
|
211
|
+
const memoryDir = realish(path.join(projectDir, "memory"));
|
|
212
|
+
const author = currentAuthor(projectDir);
|
|
213
|
+
const reason = [
|
|
214
|
+
`Protected branch (${branch}). Ask the user how to proceed — do NOT auto-branch silently.`,
|
|
215
|
+
"Offer a choice (numbered or structured): Branch off (default) / Proceed on this branch / Cancel.",
|
|
216
|
+
` • Branch off: bin/agent.sh branch --topic "<work topic>" (prefix dev/${author}/...)`,
|
|
217
|
+
` • Proceed on ${branch}: echo '${branch}' > .egregore-branch-consent then retry`,
|
|
218
|
+
"Proceeding records consent for this branch for the rest of the session (cleared on next session start).",
|
|
219
|
+
].join("\n");
|
|
220
|
+
|
|
221
|
+
if (toolName === "apply_patch") {
|
|
222
|
+
const changedPaths = extractApplyPatchPaths(toolInput.command || "");
|
|
223
|
+
if (changedPaths.length === 0 || changedPaths.some((p) => !isExemptPath(p, projectDir, memoryDir))) {
|
|
224
|
+
deny(reason);
|
|
225
|
+
}
|
|
226
|
+
return;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
if (toolName === "Edit" || toolName === "Write") {
|
|
230
|
+
const target = toolInput.file_path || toolInput.path || "";
|
|
231
|
+
if (!target || !isExemptPath(target, projectDir, memoryDir)) deny(reason);
|
|
232
|
+
return;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
if (toolName === "Bash") {
|
|
236
|
+
const command = String(toolInput.command || "");
|
|
237
|
+
if (commandTargetsOtherRepo(command, projectDir, memoryDir)) return;
|
|
238
|
+
if (shellLooksMutating(command, projectDir, memoryDir)) deny(reason);
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
main();
|