@codyswann/lisa 2.194.2 → 2.195.1
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/expo/copy-overwrite/tsconfig.json +8 -1
- package/expo/package-lisa/package.lisa.json +14 -13
- package/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +12 -1
- package/plugins/lisa/.codex-plugin/hooks.json +13 -0
- package/plugins/lisa/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa/hooks/cleanup-stale-worktrees.sh +81 -0
- package/plugins/lisa-agy/plugin.json +1 -1
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-agy/plugin.json +1 -1
- package/plugins/lisa-cdk-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cdk-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-copilot/.claude-plugin/plugin.json +14 -1
- package/plugins/lisa-copilot/hooks/cleanup-stale-worktrees.sh +81 -0
- package/plugins/lisa-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-cursor/hooks/cleanup-stale-worktrees.sh +81 -0
- package/plugins/lisa-cursor/hooks/hooks.json +5 -0
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-agy/plugin.json +1 -1
- package/plugins/lisa-expo-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-agy/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-harper-fabric-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-agy/plugin.json +1 -1
- package/plugins/lisa-nestjs-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-agy/plugin.json +1 -1
- package/plugins/lisa-openclaw-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-openclaw-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-agy/plugin.json +1 -1
- package/plugins/lisa-phaser-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-phaser-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-agy/plugin.json +1 -1
- package/plugins/lisa-rails-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-agy/plugin.json +1 -1
- package/plugins/lisa-typescript-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki/.codex-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-agy/plugin.json +1 -1
- package/plugins/lisa-wiki-copilot/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-wiki-cursor/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/.claude-plugin/plugin.json +158 -24
- package/plugins/src/base/hooks/cleanup-stale-worktrees.sh +81 -0
|
@@ -9,18 +9,18 @@
|
|
|
9
9
|
"maestro:test": "maestro test .maestro/flows",
|
|
10
10
|
"maestro:test:smoke": "maestro test .maestro/flows --include-tags=smoke",
|
|
11
11
|
"maestro:studio": "maestro studio",
|
|
12
|
-
"start:local": "cp .env.localhost .env.local && STAGE=local
|
|
13
|
-
"start:dev": "cp .env.development .env.local && STAGE=development
|
|
14
|
-
"start:staging": "cp .env.staging .env.local && STAGE=staging
|
|
15
|
-
"start:production": "cp .env.production .env.local && STAGE=production
|
|
16
|
-
"start:simulator:ios:local": "cp .env.localhost .env.local && STAGE=local
|
|
17
|
-
"start:simulator:ios:dev": "cp .env.development .env.local && STAGE=development
|
|
18
|
-
"start:simulator:ios:staging": "cp .env.staging .env.local && STAGE=staging
|
|
19
|
-
"start:simulator:ios:production": "cp .env.production .env.local && STAGE=production
|
|
20
|
-
"start:simulator:android:local": "cp .env.localhost .env.local && STAGE=local
|
|
21
|
-
"start:simulator:android:dev": "cp .env.development .env.local && STAGE=development
|
|
22
|
-
"start:simulator:android:staging": "cp .env.staging .env.local && STAGE=staging
|
|
23
|
-
"start:simulator:android:production": "cp .env.production .env.local && STAGE=production
|
|
12
|
+
"start:local": "cp .env.localhost .env.local && STAGE=local expo start",
|
|
13
|
+
"start:dev": "cp .env.development .env.local && STAGE=development expo start",
|
|
14
|
+
"start:staging": "cp .env.staging .env.local && STAGE=staging expo start",
|
|
15
|
+
"start:production": "cp .env.production .env.local && STAGE=production expo start",
|
|
16
|
+
"start:simulator:ios:local": "cp .env.localhost .env.local && STAGE=local expo start --ios",
|
|
17
|
+
"start:simulator:ios:dev": "cp .env.development .env.local && STAGE=development expo start --ios",
|
|
18
|
+
"start:simulator:ios:staging": "cp .env.staging .env.local && STAGE=staging expo start --ios",
|
|
19
|
+
"start:simulator:ios:production": "cp .env.production .env.local && STAGE=production expo start --ios",
|
|
20
|
+
"start:simulator:android:local": "cp .env.localhost .env.local && STAGE=local expo start --android",
|
|
21
|
+
"start:simulator:android:dev": "cp .env.development .env.local && STAGE=development expo start --android",
|
|
22
|
+
"start:simulator:android:staging": "cp .env.staging .env.local && STAGE=staging expo start --android",
|
|
23
|
+
"start:simulator:android:production": "cp .env.production .env.local && STAGE=production expo start --android",
|
|
24
24
|
"build-and-run:simulator:ios:local": "cp .env.localhost .env.local && STAGE=local expo run:ios",
|
|
25
25
|
"build-and-run:simulator:ios:dev": "cp .env.development .env.local && STAGE=development expo run:ios",
|
|
26
26
|
"build-and-run:simulator:ios:staging": "cp .env.staging .env.local && STAGE=staging expo run:ios",
|
|
@@ -52,7 +52,8 @@
|
|
|
52
52
|
"test:mutation": "node scripts/lisa-mutation.mjs",
|
|
53
53
|
"test:watch": "NODE_ENV=test jest --watch",
|
|
54
54
|
"lint": "oxlint && eslint . --quiet",
|
|
55
|
-
"lint:fix": "oxlint --fix && eslint . --fix"
|
|
55
|
+
"lint:fix": "oxlint --fix && eslint . --fix",
|
|
56
|
+
"start:atlas": "cp .env.development .env.local && STAGE=development EXPO_ATLAS=true expo start"
|
|
56
57
|
},
|
|
57
58
|
"dependencies": {
|
|
58
59
|
"@apollo/client": "^3.10.8",
|
package/package.json
CHANGED
|
@@ -95,7 +95,7 @@
|
|
|
95
95
|
"ws": ">=8.20.1"
|
|
96
96
|
},
|
|
97
97
|
"name": "@codyswann/lisa",
|
|
98
|
-
"version": "2.
|
|
98
|
+
"version": "2.195.1",
|
|
99
99
|
"description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
|
|
100
100
|
"main": "dist/index.js",
|
|
101
101
|
"exports": {
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.195.1",
|
|
4
4
|
"description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -220,6 +220,17 @@
|
|
|
220
220
|
"command": "command -v entire >/dev/null 2>&1 && entire hooks claude-code session-end || true"
|
|
221
221
|
}
|
|
222
222
|
]
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
"matcher": "",
|
|
226
|
+
"hooks": [
|
|
227
|
+
{
|
|
228
|
+
"type": "command",
|
|
229
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/cleanup-stale-worktrees.sh",
|
|
230
|
+
"async": true,
|
|
231
|
+
"timeout": 300
|
|
232
|
+
}
|
|
233
|
+
]
|
|
223
234
|
}
|
|
224
235
|
]
|
|
225
236
|
}
|
|
@@ -114,6 +114,19 @@
|
|
|
114
114
|
}
|
|
115
115
|
]
|
|
116
116
|
}
|
|
117
|
+
],
|
|
118
|
+
"SessionEnd": [
|
|
119
|
+
{
|
|
120
|
+
"matcher": "",
|
|
121
|
+
"hooks": [
|
|
122
|
+
{
|
|
123
|
+
"type": "command",
|
|
124
|
+
"command": "${PLUGIN_ROOT}/hooks/cleanup-stale-worktrees.sh",
|
|
125
|
+
"async": true,
|
|
126
|
+
"timeout": 300
|
|
127
|
+
}
|
|
128
|
+
]
|
|
129
|
+
}
|
|
117
130
|
]
|
|
118
131
|
}
|
|
119
132
|
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# This file is managed by Lisa.
|
|
3
|
+
# Do not edit directly — changes will be overwritten on the next `lisa` run.
|
|
4
|
+
#
|
|
5
|
+
# SessionEnd sweep of abandoned agent worktrees under <repo>/.claude/worktrees.
|
|
6
|
+
#
|
|
7
|
+
# Why: Claude Code's built-in cleanup (cleanupPeriodDays) only removes
|
|
8
|
+
# PRISTINE subagent worktrees — no changes, no untracked files, no unpushed
|
|
9
|
+
# commits. Real agent worktrees almost always carry untracked junk
|
|
10
|
+
# (node_modules, build output), so they survive forever and accumulate;
|
|
11
|
+
# one long-lived repo reached 415 worktrees / 823GB, which also crashes
|
|
12
|
+
# jest-haste-map's find-buffer crawl.
|
|
13
|
+
#
|
|
14
|
+
# Safety model — a worktree is removed only when ALL hold:
|
|
15
|
+
# * it lives under .claude/worktrees/ (never the primary checkout)
|
|
16
|
+
# * no modified or staged TRACKED files (real work is never deleted)
|
|
17
|
+
# * its HEAD commit is reachable from some remote ref (nothing unpushed)
|
|
18
|
+
# * its directory mtime is older than LISA_WORKTREE_MAX_AGE_DAYS (default 7)
|
|
19
|
+
# Untracked-only dirt does NOT block removal — that junk is exactly what
|
|
20
|
+
# defeats the built-in sweep. Set LISA_WORKTREE_CLEANUP=off to disable.
|
|
21
|
+
|
|
22
|
+
set -u
|
|
23
|
+
|
|
24
|
+
[ "${LISA_WORKTREE_CLEANUP:-on}" = "off" ] && exit 0
|
|
25
|
+
|
|
26
|
+
MAX_AGE_DAYS="${LISA_WORKTREE_MAX_AGE_DAYS:-7}"
|
|
27
|
+
|
|
28
|
+
repo_root="$(git rev-parse --show-toplevel 2>/dev/null)" || exit 0
|
|
29
|
+
|
|
30
|
+
# Never run the sweep from INSIDE an agent worktree — only the primary
|
|
31
|
+
# checkout owns cleanup (a worktree deleting its siblings mid-session
|
|
32
|
+
# would race other live sessions in the same repo).
|
|
33
|
+
case "$repo_root" in
|
|
34
|
+
*/.claude/worktrees/*) exit 0 ;;
|
|
35
|
+
esac
|
|
36
|
+
|
|
37
|
+
wt_root="$repo_root/.claude/worktrees"
|
|
38
|
+
[ -d "$wt_root" ] || exit 0
|
|
39
|
+
|
|
40
|
+
now=$(date +%s)
|
|
41
|
+
max_age_secs=$((MAX_AGE_DAYS * 86400))
|
|
42
|
+
removed=0
|
|
43
|
+
|
|
44
|
+
git -C "$repo_root" worktree prune 2>/dev/null
|
|
45
|
+
|
|
46
|
+
for wt in "$wt_root"/*/; do
|
|
47
|
+
wt="${wt%/}"
|
|
48
|
+
[ -d "$wt" ] || continue
|
|
49
|
+
|
|
50
|
+
# Age gate: skip anything recently touched (possibly a live session).
|
|
51
|
+
# GNU `stat -c` is tried first: GNU's `-f` means "filesystem status" (not
|
|
52
|
+
# BSD's "format"), so `stat -f %m` on Linux silently misparses instead of
|
|
53
|
+
# failing cleanly, defeating the fallback. Try the GNU form first — it
|
|
54
|
+
# fails cleanly with no stdout on BSD/macOS, letting `-f %m` take over.
|
|
55
|
+
mtime=$(stat -c %Y "$wt" 2>/dev/null || stat -f %m "$wt" 2>/dev/null) || continue
|
|
56
|
+
[ $((now - mtime)) -ge "$max_age_secs" ] || continue
|
|
57
|
+
|
|
58
|
+
if [ -e "$wt/.git" ]; then
|
|
59
|
+
# Real work gate: modified/staged tracked files survive. A failed
|
|
60
|
+
# `git status` (corrupted index, permission issue, etc.) must NOT be
|
|
61
|
+
# treated as clean — capture its exit status too and skip on failure.
|
|
62
|
+
status_output=$(git -C "$wt" status --porcelain --untracked-files=no 2>/dev/null) || continue
|
|
63
|
+
[ -z "$status_output" ] || continue
|
|
64
|
+
|
|
65
|
+
# Unpushed gate: HEAD must be reachable from a remote ref.
|
|
66
|
+
sha=$(git -C "$wt" rev-parse HEAD 2>/dev/null) || continue
|
|
67
|
+
[ -n "$(git -C "$wt" branch -r --contains "$sha" 2>/dev/null | head -1)" ] || continue
|
|
68
|
+
|
|
69
|
+
# git worktree lock (held during live agent execution) blocks removal;
|
|
70
|
+
# --force only clears untracked junk, never the gates above.
|
|
71
|
+
git -C "$repo_root" worktree remove --force "$wt" 2>/dev/null && removed=$((removed + 1))
|
|
72
|
+
else
|
|
73
|
+
# Orphan directory git no longer tracks (post-prune leftover).
|
|
74
|
+
rm -rf "$wt" && removed=$((removed + 1))
|
|
75
|
+
fi
|
|
76
|
+
done
|
|
77
|
+
|
|
78
|
+
git -C "$repo_root" worktree prune 2>/dev/null
|
|
79
|
+
|
|
80
|
+
[ "$removed" -gt 0 ] && echo "Removed $removed stale agent worktree(s) from .claude/worktrees" >&2
|
|
81
|
+
exit 0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.195.1",
|
|
4
4
|
"description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -91,6 +91,19 @@
|
|
|
91
91
|
}
|
|
92
92
|
]
|
|
93
93
|
}
|
|
94
|
+
],
|
|
95
|
+
"sessionEnd": [
|
|
96
|
+
{
|
|
97
|
+
"matcher": "",
|
|
98
|
+
"hooks": [
|
|
99
|
+
{
|
|
100
|
+
"type": "command",
|
|
101
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/cleanup-stale-worktrees.sh",
|
|
102
|
+
"async": true,
|
|
103
|
+
"timeout": 300
|
|
104
|
+
}
|
|
105
|
+
]
|
|
106
|
+
}
|
|
94
107
|
]
|
|
95
108
|
},
|
|
96
109
|
"mcpServers": {
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# This file is managed by Lisa.
|
|
3
|
+
# Do not edit directly — changes will be overwritten on the next `lisa` run.
|
|
4
|
+
#
|
|
5
|
+
# SessionEnd sweep of abandoned agent worktrees under <repo>/.claude/worktrees.
|
|
6
|
+
#
|
|
7
|
+
# Why: Claude Code's built-in cleanup (cleanupPeriodDays) only removes
|
|
8
|
+
# PRISTINE subagent worktrees — no changes, no untracked files, no unpushed
|
|
9
|
+
# commits. Real agent worktrees almost always carry untracked junk
|
|
10
|
+
# (node_modules, build output), so they survive forever and accumulate;
|
|
11
|
+
# one long-lived repo reached 415 worktrees / 823GB, which also crashes
|
|
12
|
+
# jest-haste-map's find-buffer crawl.
|
|
13
|
+
#
|
|
14
|
+
# Safety model — a worktree is removed only when ALL hold:
|
|
15
|
+
# * it lives under .claude/worktrees/ (never the primary checkout)
|
|
16
|
+
# * no modified or staged TRACKED files (real work is never deleted)
|
|
17
|
+
# * its HEAD commit is reachable from some remote ref (nothing unpushed)
|
|
18
|
+
# * its directory mtime is older than LISA_WORKTREE_MAX_AGE_DAYS (default 7)
|
|
19
|
+
# Untracked-only dirt does NOT block removal — that junk is exactly what
|
|
20
|
+
# defeats the built-in sweep. Set LISA_WORKTREE_CLEANUP=off to disable.
|
|
21
|
+
|
|
22
|
+
set -u
|
|
23
|
+
|
|
24
|
+
[ "${LISA_WORKTREE_CLEANUP:-on}" = "off" ] && exit 0
|
|
25
|
+
|
|
26
|
+
MAX_AGE_DAYS="${LISA_WORKTREE_MAX_AGE_DAYS:-7}"
|
|
27
|
+
|
|
28
|
+
repo_root="$(git rev-parse --show-toplevel 2>/dev/null)" || exit 0
|
|
29
|
+
|
|
30
|
+
# Never run the sweep from INSIDE an agent worktree — only the primary
|
|
31
|
+
# checkout owns cleanup (a worktree deleting its siblings mid-session
|
|
32
|
+
# would race other live sessions in the same repo).
|
|
33
|
+
case "$repo_root" in
|
|
34
|
+
*/.claude/worktrees/*) exit 0 ;;
|
|
35
|
+
esac
|
|
36
|
+
|
|
37
|
+
wt_root="$repo_root/.claude/worktrees"
|
|
38
|
+
[ -d "$wt_root" ] || exit 0
|
|
39
|
+
|
|
40
|
+
now=$(date +%s)
|
|
41
|
+
max_age_secs=$((MAX_AGE_DAYS * 86400))
|
|
42
|
+
removed=0
|
|
43
|
+
|
|
44
|
+
git -C "$repo_root" worktree prune 2>/dev/null
|
|
45
|
+
|
|
46
|
+
for wt in "$wt_root"/*/; do
|
|
47
|
+
wt="${wt%/}"
|
|
48
|
+
[ -d "$wt" ] || continue
|
|
49
|
+
|
|
50
|
+
# Age gate: skip anything recently touched (possibly a live session).
|
|
51
|
+
# GNU `stat -c` is tried first: GNU's `-f` means "filesystem status" (not
|
|
52
|
+
# BSD's "format"), so `stat -f %m` on Linux silently misparses instead of
|
|
53
|
+
# failing cleanly, defeating the fallback. Try the GNU form first — it
|
|
54
|
+
# fails cleanly with no stdout on BSD/macOS, letting `-f %m` take over.
|
|
55
|
+
mtime=$(stat -c %Y "$wt" 2>/dev/null || stat -f %m "$wt" 2>/dev/null) || continue
|
|
56
|
+
[ $((now - mtime)) -ge "$max_age_secs" ] || continue
|
|
57
|
+
|
|
58
|
+
if [ -e "$wt/.git" ]; then
|
|
59
|
+
# Real work gate: modified/staged tracked files survive. A failed
|
|
60
|
+
# `git status` (corrupted index, permission issue, etc.) must NOT be
|
|
61
|
+
# treated as clean — capture its exit status too and skip on failure.
|
|
62
|
+
status_output=$(git -C "$wt" status --porcelain --untracked-files=no 2>/dev/null) || continue
|
|
63
|
+
[ -z "$status_output" ] || continue
|
|
64
|
+
|
|
65
|
+
# Unpushed gate: HEAD must be reachable from a remote ref.
|
|
66
|
+
sha=$(git -C "$wt" rev-parse HEAD 2>/dev/null) || continue
|
|
67
|
+
[ -n "$(git -C "$wt" branch -r --contains "$sha" 2>/dev/null | head -1)" ] || continue
|
|
68
|
+
|
|
69
|
+
# git worktree lock (held during live agent execution) blocks removal;
|
|
70
|
+
# --force only clears untracked junk, never the gates above.
|
|
71
|
+
git -C "$repo_root" worktree remove --force "$wt" 2>/dev/null && removed=$((removed + 1))
|
|
72
|
+
else
|
|
73
|
+
# Orphan directory git no longer tracks (post-prune leftover).
|
|
74
|
+
rm -rf "$wt" && removed=$((removed + 1))
|
|
75
|
+
fi
|
|
76
|
+
done
|
|
77
|
+
|
|
78
|
+
git -C "$repo_root" worktree prune 2>/dev/null
|
|
79
|
+
|
|
80
|
+
[ "$removed" -gt 0 ] && echo "Removed $removed stale agent worktree(s) from .claude/worktrees" >&2
|
|
81
|
+
exit 0
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# This file is managed by Lisa.
|
|
3
|
+
# Do not edit directly — changes will be overwritten on the next `lisa` run.
|
|
4
|
+
#
|
|
5
|
+
# SessionEnd sweep of abandoned agent worktrees under <repo>/.claude/worktrees.
|
|
6
|
+
#
|
|
7
|
+
# Why: Claude Code's built-in cleanup (cleanupPeriodDays) only removes
|
|
8
|
+
# PRISTINE subagent worktrees — no changes, no untracked files, no unpushed
|
|
9
|
+
# commits. Real agent worktrees almost always carry untracked junk
|
|
10
|
+
# (node_modules, build output), so they survive forever and accumulate;
|
|
11
|
+
# one long-lived repo reached 415 worktrees / 823GB, which also crashes
|
|
12
|
+
# jest-haste-map's find-buffer crawl.
|
|
13
|
+
#
|
|
14
|
+
# Safety model — a worktree is removed only when ALL hold:
|
|
15
|
+
# * it lives under .claude/worktrees/ (never the primary checkout)
|
|
16
|
+
# * no modified or staged TRACKED files (real work is never deleted)
|
|
17
|
+
# * its HEAD commit is reachable from some remote ref (nothing unpushed)
|
|
18
|
+
# * its directory mtime is older than LISA_WORKTREE_MAX_AGE_DAYS (default 7)
|
|
19
|
+
# Untracked-only dirt does NOT block removal — that junk is exactly what
|
|
20
|
+
# defeats the built-in sweep. Set LISA_WORKTREE_CLEANUP=off to disable.
|
|
21
|
+
|
|
22
|
+
set -u
|
|
23
|
+
|
|
24
|
+
[ "${LISA_WORKTREE_CLEANUP:-on}" = "off" ] && exit 0
|
|
25
|
+
|
|
26
|
+
MAX_AGE_DAYS="${LISA_WORKTREE_MAX_AGE_DAYS:-7}"
|
|
27
|
+
|
|
28
|
+
repo_root="$(git rev-parse --show-toplevel 2>/dev/null)" || exit 0
|
|
29
|
+
|
|
30
|
+
# Never run the sweep from INSIDE an agent worktree — only the primary
|
|
31
|
+
# checkout owns cleanup (a worktree deleting its siblings mid-session
|
|
32
|
+
# would race other live sessions in the same repo).
|
|
33
|
+
case "$repo_root" in
|
|
34
|
+
*/.claude/worktrees/*) exit 0 ;;
|
|
35
|
+
esac
|
|
36
|
+
|
|
37
|
+
wt_root="$repo_root/.claude/worktrees"
|
|
38
|
+
[ -d "$wt_root" ] || exit 0
|
|
39
|
+
|
|
40
|
+
now=$(date +%s)
|
|
41
|
+
max_age_secs=$((MAX_AGE_DAYS * 86400))
|
|
42
|
+
removed=0
|
|
43
|
+
|
|
44
|
+
git -C "$repo_root" worktree prune 2>/dev/null
|
|
45
|
+
|
|
46
|
+
for wt in "$wt_root"/*/; do
|
|
47
|
+
wt="${wt%/}"
|
|
48
|
+
[ -d "$wt" ] || continue
|
|
49
|
+
|
|
50
|
+
# Age gate: skip anything recently touched (possibly a live session).
|
|
51
|
+
# GNU `stat -c` is tried first: GNU's `-f` means "filesystem status" (not
|
|
52
|
+
# BSD's "format"), so `stat -f %m` on Linux silently misparses instead of
|
|
53
|
+
# failing cleanly, defeating the fallback. Try the GNU form first — it
|
|
54
|
+
# fails cleanly with no stdout on BSD/macOS, letting `-f %m` take over.
|
|
55
|
+
mtime=$(stat -c %Y "$wt" 2>/dev/null || stat -f %m "$wt" 2>/dev/null) || continue
|
|
56
|
+
[ $((now - mtime)) -ge "$max_age_secs" ] || continue
|
|
57
|
+
|
|
58
|
+
if [ -e "$wt/.git" ]; then
|
|
59
|
+
# Real work gate: modified/staged tracked files survive. A failed
|
|
60
|
+
# `git status` (corrupted index, permission issue, etc.) must NOT be
|
|
61
|
+
# treated as clean — capture its exit status too and skip on failure.
|
|
62
|
+
status_output=$(git -C "$wt" status --porcelain --untracked-files=no 2>/dev/null) || continue
|
|
63
|
+
[ -z "$status_output" ] || continue
|
|
64
|
+
|
|
65
|
+
# Unpushed gate: HEAD must be reachable from a remote ref.
|
|
66
|
+
sha=$(git -C "$wt" rev-parse HEAD 2>/dev/null) || continue
|
|
67
|
+
[ -n "$(git -C "$wt" branch -r --contains "$sha" 2>/dev/null | head -1)" ] || continue
|
|
68
|
+
|
|
69
|
+
# git worktree lock (held during live agent execution) blocks removal;
|
|
70
|
+
# --force only clears untracked junk, never the gates above.
|
|
71
|
+
git -C "$repo_root" worktree remove --force "$wt" 2>/dev/null && removed=$((removed + 1))
|
|
72
|
+
else
|
|
73
|
+
# Orphan directory git no longer tracks (post-prune leftover).
|
|
74
|
+
rm -rf "$wt" && removed=$((removed + 1))
|
|
75
|
+
fi
|
|
76
|
+
done
|
|
77
|
+
|
|
78
|
+
git -C "$repo_root" worktree prune 2>/dev/null
|
|
79
|
+
|
|
80
|
+
[ "$removed" -gt 0 ] && echo "Removed $removed stale agent worktree(s) from .claude/worktrees" >&2
|
|
81
|
+
exit 0
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.195.1",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.195.1",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, across Claude and Codex.",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.195.1",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.195.1",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa-openclaw",
|
|
3
|
-
"version": "2.
|
|
3
|
+
"version": "2.195.1",
|
|
4
4
|
"description": "Connect staff roles to Telegram or Slack via OpenClaw — facilitator/specialist hub-and-spoke routing and repo-coding topics, for Claude Code and Codex",
|
|
5
5
|
"author": {
|
|
6
6
|
"name": "Cody Swann"
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "lisa",
|
|
3
3
|
"version": "1.0.0",
|
|
4
|
-
"description": "Universal governance
|
|
5
|
-
"author": {
|
|
4
|
+
"description": "Universal governance \u2014 agents, skills, commands, hooks, and rules for all projects",
|
|
5
|
+
"author": {
|
|
6
|
+
"name": "Cody Swann"
|
|
7
|
+
},
|
|
6
8
|
"hooks": {
|
|
7
9
|
"UserPromptSubmit": [
|
|
8
10
|
{
|
|
@@ -17,13 +19,19 @@
|
|
|
17
19
|
{
|
|
18
20
|
"matcher": "",
|
|
19
21
|
"hooks": [
|
|
20
|
-
{
|
|
22
|
+
{
|
|
23
|
+
"type": "command",
|
|
24
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/enforce-team-first.sh"
|
|
25
|
+
}
|
|
21
26
|
]
|
|
22
27
|
},
|
|
23
28
|
{
|
|
24
29
|
"matcher": "",
|
|
25
30
|
"hooks": [
|
|
26
|
-
{
|
|
31
|
+
{
|
|
32
|
+
"type": "command",
|
|
33
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/enforce-verification-gate.sh"
|
|
34
|
+
}
|
|
27
35
|
]
|
|
28
36
|
}
|
|
29
37
|
],
|
|
@@ -31,25 +39,37 @@
|
|
|
31
39
|
{
|
|
32
40
|
"matcher": "Task",
|
|
33
41
|
"hooks": [
|
|
34
|
-
{
|
|
42
|
+
{
|
|
43
|
+
"type": "command",
|
|
44
|
+
"command": "command -v entire >/dev/null 2>&1 && entire hooks claude-code post-task || true"
|
|
45
|
+
}
|
|
35
46
|
]
|
|
36
47
|
},
|
|
37
48
|
{
|
|
38
49
|
"matcher": "TodoWrite",
|
|
39
50
|
"hooks": [
|
|
40
|
-
{
|
|
51
|
+
{
|
|
52
|
+
"type": "command",
|
|
53
|
+
"command": "command -v entire >/dev/null 2>&1 && entire hooks claude-code post-todo || true"
|
|
54
|
+
}
|
|
41
55
|
]
|
|
42
56
|
},
|
|
43
57
|
{
|
|
44
58
|
"matcher": "Bash",
|
|
45
59
|
"hooks": [
|
|
46
|
-
{
|
|
60
|
+
{
|
|
61
|
+
"type": "command",
|
|
62
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/shell-write-nudge.sh"
|
|
63
|
+
}
|
|
47
64
|
]
|
|
48
65
|
},
|
|
49
66
|
{
|
|
50
67
|
"matcher": "TeamCreate|Agent",
|
|
51
68
|
"hooks": [
|
|
52
|
-
{
|
|
69
|
+
{
|
|
70
|
+
"type": "command",
|
|
71
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/enforce-team-first.sh"
|
|
72
|
+
}
|
|
53
73
|
]
|
|
54
74
|
}
|
|
55
75
|
],
|
|
@@ -57,47 +77,161 @@
|
|
|
57
77
|
{
|
|
58
78
|
"matcher": "Task",
|
|
59
79
|
"hooks": [
|
|
60
|
-
{
|
|
80
|
+
{
|
|
81
|
+
"type": "command",
|
|
82
|
+
"command": "command -v entire >/dev/null 2>&1 && entire hooks claude-code pre-task || true"
|
|
83
|
+
}
|
|
61
84
|
]
|
|
62
85
|
},
|
|
63
86
|
{
|
|
64
87
|
"matcher": "Bash",
|
|
65
88
|
"hooks": [
|
|
66
|
-
{
|
|
67
|
-
|
|
89
|
+
{
|
|
90
|
+
"type": "command",
|
|
91
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/block-no-verify.sh"
|
|
92
|
+
},
|
|
93
|
+
{
|
|
94
|
+
"type": "command",
|
|
95
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/parity-safety-net.sh"
|
|
96
|
+
}
|
|
68
97
|
]
|
|
69
98
|
},
|
|
70
99
|
{
|
|
71
100
|
"matcher": "",
|
|
72
101
|
"hooks": [
|
|
73
|
-
{
|
|
102
|
+
{
|
|
103
|
+
"type": "command",
|
|
104
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/enforce-team-first.sh"
|
|
105
|
+
}
|
|
74
106
|
]
|
|
75
107
|
},
|
|
76
108
|
{
|
|
77
109
|
"matcher": "",
|
|
78
110
|
"hooks": [
|
|
79
|
-
{
|
|
111
|
+
{
|
|
112
|
+
"type": "command",
|
|
113
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/enforce-verification-gate.sh"
|
|
114
|
+
}
|
|
80
115
|
]
|
|
81
116
|
}
|
|
82
117
|
],
|
|
83
118
|
"Stop": [
|
|
84
|
-
{
|
|
85
|
-
|
|
119
|
+
{
|
|
120
|
+
"matcher": "",
|
|
121
|
+
"hooks": [
|
|
122
|
+
{
|
|
123
|
+
"type": "command",
|
|
124
|
+
"command": "command -v entire >/dev/null 2>&1 && entire hooks claude-code stop || true"
|
|
125
|
+
}
|
|
126
|
+
]
|
|
127
|
+
},
|
|
128
|
+
{
|
|
129
|
+
"matcher": "",
|
|
130
|
+
"hooks": [
|
|
131
|
+
{
|
|
132
|
+
"type": "command",
|
|
133
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/enforce-verification-gate.sh"
|
|
134
|
+
}
|
|
135
|
+
]
|
|
136
|
+
}
|
|
86
137
|
],
|
|
87
138
|
"SessionStart": [
|
|
88
|
-
{
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
139
|
+
{
|
|
140
|
+
"matcher": "startup",
|
|
141
|
+
"hooks": [
|
|
142
|
+
{
|
|
143
|
+
"type": "command",
|
|
144
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/install-pkgs.sh"
|
|
145
|
+
}
|
|
146
|
+
]
|
|
147
|
+
},
|
|
148
|
+
{
|
|
149
|
+
"matcher": "",
|
|
150
|
+
"hooks": [
|
|
151
|
+
{
|
|
152
|
+
"type": "command",
|
|
153
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/inject-rules.sh"
|
|
154
|
+
}
|
|
155
|
+
]
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
"matcher": "",
|
|
159
|
+
"hooks": [
|
|
160
|
+
{
|
|
161
|
+
"type": "command",
|
|
162
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/setup-jira-cli.sh"
|
|
163
|
+
}
|
|
164
|
+
]
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
"matcher": "",
|
|
168
|
+
"hooks": [
|
|
169
|
+
{
|
|
170
|
+
"type": "command",
|
|
171
|
+
"command": "command -v entire >/dev/null 2>&1 && entire hooks claude-code session-start || true"
|
|
172
|
+
}
|
|
173
|
+
]
|
|
174
|
+
}
|
|
92
175
|
],
|
|
93
176
|
"SubagentStart": [
|
|
94
|
-
{
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
177
|
+
{
|
|
178
|
+
"matcher": "",
|
|
179
|
+
"hooks": [
|
|
180
|
+
{
|
|
181
|
+
"type": "command",
|
|
182
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/inject-rules.sh"
|
|
183
|
+
}
|
|
184
|
+
]
|
|
185
|
+
},
|
|
186
|
+
{
|
|
187
|
+
"matcher": "",
|
|
188
|
+
"hooks": [
|
|
189
|
+
{
|
|
190
|
+
"type": "command",
|
|
191
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/inject-flow-context.sh"
|
|
192
|
+
}
|
|
193
|
+
]
|
|
194
|
+
},
|
|
195
|
+
{
|
|
196
|
+
"matcher": "",
|
|
197
|
+
"hooks": [
|
|
198
|
+
{
|
|
199
|
+
"type": "command",
|
|
200
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/enforce-team-first.sh"
|
|
201
|
+
}
|
|
202
|
+
]
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
"matcher": "",
|
|
206
|
+
"hooks": [
|
|
207
|
+
{
|
|
208
|
+
"type": "command",
|
|
209
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/enforce-verification-gate.sh"
|
|
210
|
+
}
|
|
211
|
+
]
|
|
212
|
+
}
|
|
98
213
|
],
|
|
99
214
|
"SessionEnd": [
|
|
100
|
-
{
|
|
215
|
+
{
|
|
216
|
+
"matcher": "",
|
|
217
|
+
"hooks": [
|
|
218
|
+
{
|
|
219
|
+
"type": "command",
|
|
220
|
+
"command": "command -v entire >/dev/null 2>&1 && entire hooks claude-code session-end || true"
|
|
221
|
+
}
|
|
222
|
+
]
|
|
223
|
+
},
|
|
224
|
+
{
|
|
225
|
+
"matcher": "",
|
|
226
|
+
"hooks": [
|
|
227
|
+
{
|
|
228
|
+
"type": "command",
|
|
229
|
+
"command": "${CLAUDE_PLUGIN_ROOT}/hooks/cleanup-stale-worktrees.sh",
|
|
230
|
+
"async": true,
|
|
231
|
+
"timeout": 300
|
|
232
|
+
}
|
|
233
|
+
]
|
|
234
|
+
}
|
|
101
235
|
]
|
|
102
236
|
}
|
|
103
237
|
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
#!/bin/bash
|
|
2
|
+
# This file is managed by Lisa.
|
|
3
|
+
# Do not edit directly — changes will be overwritten on the next `lisa` run.
|
|
4
|
+
#
|
|
5
|
+
# SessionEnd sweep of abandoned agent worktrees under <repo>/.claude/worktrees.
|
|
6
|
+
#
|
|
7
|
+
# Why: Claude Code's built-in cleanup (cleanupPeriodDays) only removes
|
|
8
|
+
# PRISTINE subagent worktrees — no changes, no untracked files, no unpushed
|
|
9
|
+
# commits. Real agent worktrees almost always carry untracked junk
|
|
10
|
+
# (node_modules, build output), so they survive forever and accumulate;
|
|
11
|
+
# one long-lived repo reached 415 worktrees / 823GB, which also crashes
|
|
12
|
+
# jest-haste-map's find-buffer crawl.
|
|
13
|
+
#
|
|
14
|
+
# Safety model — a worktree is removed only when ALL hold:
|
|
15
|
+
# * it lives under .claude/worktrees/ (never the primary checkout)
|
|
16
|
+
# * no modified or staged TRACKED files (real work is never deleted)
|
|
17
|
+
# * its HEAD commit is reachable from some remote ref (nothing unpushed)
|
|
18
|
+
# * its directory mtime is older than LISA_WORKTREE_MAX_AGE_DAYS (default 7)
|
|
19
|
+
# Untracked-only dirt does NOT block removal — that junk is exactly what
|
|
20
|
+
# defeats the built-in sweep. Set LISA_WORKTREE_CLEANUP=off to disable.
|
|
21
|
+
|
|
22
|
+
set -u
|
|
23
|
+
|
|
24
|
+
[ "${LISA_WORKTREE_CLEANUP:-on}" = "off" ] && exit 0
|
|
25
|
+
|
|
26
|
+
MAX_AGE_DAYS="${LISA_WORKTREE_MAX_AGE_DAYS:-7}"
|
|
27
|
+
|
|
28
|
+
repo_root="$(git rev-parse --show-toplevel 2>/dev/null)" || exit 0
|
|
29
|
+
|
|
30
|
+
# Never run the sweep from INSIDE an agent worktree — only the primary
|
|
31
|
+
# checkout owns cleanup (a worktree deleting its siblings mid-session
|
|
32
|
+
# would race other live sessions in the same repo).
|
|
33
|
+
case "$repo_root" in
|
|
34
|
+
*/.claude/worktrees/*) exit 0 ;;
|
|
35
|
+
esac
|
|
36
|
+
|
|
37
|
+
wt_root="$repo_root/.claude/worktrees"
|
|
38
|
+
[ -d "$wt_root" ] || exit 0
|
|
39
|
+
|
|
40
|
+
now=$(date +%s)
|
|
41
|
+
max_age_secs=$((MAX_AGE_DAYS * 86400))
|
|
42
|
+
removed=0
|
|
43
|
+
|
|
44
|
+
git -C "$repo_root" worktree prune 2>/dev/null
|
|
45
|
+
|
|
46
|
+
for wt in "$wt_root"/*/; do
|
|
47
|
+
wt="${wt%/}"
|
|
48
|
+
[ -d "$wt" ] || continue
|
|
49
|
+
|
|
50
|
+
# Age gate: skip anything recently touched (possibly a live session).
|
|
51
|
+
# GNU `stat -c` is tried first: GNU's `-f` means "filesystem status" (not
|
|
52
|
+
# BSD's "format"), so `stat -f %m` on Linux silently misparses instead of
|
|
53
|
+
# failing cleanly, defeating the fallback. Try the GNU form first — it
|
|
54
|
+
# fails cleanly with no stdout on BSD/macOS, letting `-f %m` take over.
|
|
55
|
+
mtime=$(stat -c %Y "$wt" 2>/dev/null || stat -f %m "$wt" 2>/dev/null) || continue
|
|
56
|
+
[ $((now - mtime)) -ge "$max_age_secs" ] || continue
|
|
57
|
+
|
|
58
|
+
if [ -e "$wt/.git" ]; then
|
|
59
|
+
# Real work gate: modified/staged tracked files survive. A failed
|
|
60
|
+
# `git status` (corrupted index, permission issue, etc.) must NOT be
|
|
61
|
+
# treated as clean — capture its exit status too and skip on failure.
|
|
62
|
+
status_output=$(git -C "$wt" status --porcelain --untracked-files=no 2>/dev/null) || continue
|
|
63
|
+
[ -z "$status_output" ] || continue
|
|
64
|
+
|
|
65
|
+
# Unpushed gate: HEAD must be reachable from a remote ref.
|
|
66
|
+
sha=$(git -C "$wt" rev-parse HEAD 2>/dev/null) || continue
|
|
67
|
+
[ -n "$(git -C "$wt" branch -r --contains "$sha" 2>/dev/null | head -1)" ] || continue
|
|
68
|
+
|
|
69
|
+
# git worktree lock (held during live agent execution) blocks removal;
|
|
70
|
+
# --force only clears untracked junk, never the gates above.
|
|
71
|
+
git -C "$repo_root" worktree remove --force "$wt" 2>/dev/null && removed=$((removed + 1))
|
|
72
|
+
else
|
|
73
|
+
# Orphan directory git no longer tracks (post-prune leftover).
|
|
74
|
+
rm -rf "$wt" && removed=$((removed + 1))
|
|
75
|
+
fi
|
|
76
|
+
done
|
|
77
|
+
|
|
78
|
+
git -C "$repo_root" worktree prune 2>/dev/null
|
|
79
|
+
|
|
80
|
+
[ "$removed" -gt 0 ] && echo "Removed $removed stale agent worktree(s) from .claude/worktrees" >&2
|
|
81
|
+
exit 0
|