cc-workspace 4.7.0 → 5.2.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/CHANGELOG.md +291 -0
- package/README.md +123 -41
- package/bin/cli.js +313 -134
- package/global-skills/agents/e2e-validator.md +151 -32
- package/global-skills/agents/implementer.md +80 -68
- package/global-skills/agents/reviewer.md +192 -0
- package/global-skills/agents/security-auditor.md +345 -0
- package/global-skills/agents/team-lead.md +93 -101
- package/global-skills/agents/workspace-init.md +16 -5
- package/global-skills/bootstrap-repo/SKILL.md +1 -0
- package/global-skills/cleanup/SKILL.md +35 -25
- package/global-skills/cross-service-check/SKILL.md +1 -0
- package/global-skills/cycle-retrospective/SKILL.md +6 -4
- package/global-skills/dispatch-feature/SKILL.md +225 -173
- package/global-skills/dispatch-feature/references/anti-patterns.md +52 -35
- package/global-skills/dispatch-feature/references/spawn-templates.md +140 -97
- package/global-skills/doctor/SKILL.md +124 -25
- package/global-skills/e2e-validator/references/container-strategies.md +55 -23
- package/global-skills/hooks/orphan-cleanup.sh +60 -0
- package/global-skills/hooks/permission-auto-approve.sh +61 -4
- package/global-skills/hooks/session-start-context.sh +10 -47
- package/global-skills/hooks/test_hooks.sh +242 -0
- package/global-skills/hooks/user-prompt-guard.sh +6 -6
- package/global-skills/hooks/validate-spawn-prompt.sh +40 -30
- package/global-skills/incident-debug/SKILL.md +1 -0
- package/global-skills/merge-prep/SKILL.md +1 -0
- package/global-skills/metrics/SKILL.md +139 -0
- package/global-skills/plan-review/SKILL.md +2 -1
- package/global-skills/qa-ruthless/SKILL.md +2 -0
- package/global-skills/refresh-profiles/SKILL.md +1 -0
- package/global-skills/rules/context-hygiene.md +4 -19
- package/global-skills/rules/model-routing.md +31 -18
- package/global-skills/session/SKILL.md +41 -20
- package/global-skills/templates/workspace.template.md +1 -1
- package/package.json +4 -3
|
@@ -1,25 +1,30 @@
|
|
|
1
1
|
# Container Strategies — E2E Validator Reference
|
|
2
2
|
|
|
3
|
+
## Key principle (v5)
|
|
4
|
+
|
|
5
|
+
**No worktrees.** The e2e-validator works directly on repos checked out at the correct branch
|
|
6
|
+
(merged source branch or session branch). Build contexts point to `../{repo}` directly.
|
|
7
|
+
|
|
3
8
|
## Overlay strategy (preferred — repos have docker-compose)
|
|
4
9
|
|
|
5
10
|
When repos already have `docker-compose.yml`, generate an **overlay** file that:
|
|
6
11
|
- Creates a shared e2e network
|
|
7
12
|
- Overrides env vars for test isolation
|
|
8
13
|
- Adds health checks if missing
|
|
9
|
-
-
|
|
14
|
+
- Uses `../{repo}` as build context (no /tmp/ paths)
|
|
10
15
|
|
|
11
16
|
### Template: docker-compose.e2e.yml (overlay)
|
|
12
17
|
|
|
13
18
|
```yaml
|
|
14
19
|
# E2E overlay — use with: docker compose -f ../repo/docker-compose.yml -f ./e2e/docker-compose.e2e.yml up
|
|
15
20
|
# Generated by e2e-validator agent
|
|
21
|
+
# Ports: check e2e-config.md for assigned ports — verify they are free before starting
|
|
16
22
|
|
|
17
23
|
networks:
|
|
18
24
|
e2e:
|
|
19
25
|
driver: bridge
|
|
20
26
|
|
|
21
27
|
services:
|
|
22
|
-
# Override each service to join the e2e network and use test env
|
|
23
28
|
api:
|
|
24
29
|
networks:
|
|
25
30
|
- e2e
|
|
@@ -62,26 +67,43 @@ services:
|
|
|
62
67
|
- /var/lib/postgresql/data # RAM disk for speed
|
|
63
68
|
```
|
|
64
69
|
|
|
65
|
-
###
|
|
66
|
-
|
|
67
|
-
When testing session branches, override the build context to point to worktrees:
|
|
70
|
+
### Build context — always point to repo directory
|
|
68
71
|
|
|
69
72
|
```yaml
|
|
70
73
|
services:
|
|
71
74
|
api:
|
|
72
75
|
build:
|
|
73
|
-
context:
|
|
76
|
+
context: ../api # repo at current checked-out branch (merged or session)
|
|
74
77
|
dockerfile: Dockerfile
|
|
75
78
|
frontend:
|
|
76
79
|
build:
|
|
77
|
-
context:
|
|
80
|
+
context: ../frontend
|
|
78
81
|
dockerfile: Dockerfile
|
|
79
82
|
```
|
|
80
83
|
|
|
84
|
+
**Never use /tmp/ paths.** The e2e-validator checks out the correct branch directly in the repo.
|
|
85
|
+
|
|
81
86
|
## Standalone strategy (repos have NO docker-compose)
|
|
82
87
|
|
|
83
88
|
Generate a complete `docker-compose.e2e.yml` from scratch.
|
|
84
89
|
|
|
90
|
+
### Port assignment
|
|
91
|
+
|
|
92
|
+
Assign ports in `e2e-config.md` and check they are free before starting.
|
|
93
|
+
Prefer non-standard ports to avoid conflicts with local dev servers:
|
|
94
|
+
|
|
95
|
+
| Service type | Default E2E port |
|
|
96
|
+
|---|---|
|
|
97
|
+
| PHP/Laravel API | 18000 |
|
|
98
|
+
| Node.js API | 13000 |
|
|
99
|
+
| Vue/Quasar frontend | 19000 |
|
|
100
|
+
| React/Next frontend | 13001 |
|
|
101
|
+
| PostgreSQL | 15432 |
|
|
102
|
+
| MySQL | 13306 |
|
|
103
|
+
| Redis | 16379 |
|
|
104
|
+
|
|
105
|
+
Using non-standard ports reduces conflicts with local dev environments.
|
|
106
|
+
|
|
85
107
|
### Per-stack templates
|
|
86
108
|
|
|
87
109
|
#### PHP/Laravel
|
|
@@ -89,10 +111,10 @@ Generate a complete `docker-compose.e2e.yml` from scratch.
|
|
|
89
111
|
services:
|
|
90
112
|
api:
|
|
91
113
|
build:
|
|
92
|
-
context:
|
|
114
|
+
context: ../api
|
|
93
115
|
dockerfile: Dockerfile
|
|
94
116
|
ports:
|
|
95
|
-
- "
|
|
117
|
+
- "18000:8000"
|
|
96
118
|
environment:
|
|
97
119
|
- APP_ENV=testing
|
|
98
120
|
- APP_KEY=${APP_KEY}
|
|
@@ -121,9 +143,9 @@ services:
|
|
|
121
143
|
services:
|
|
122
144
|
api:
|
|
123
145
|
build:
|
|
124
|
-
context:
|
|
146
|
+
context: ../api
|
|
125
147
|
ports:
|
|
126
|
-
- "
|
|
148
|
+
- "13000:3000"
|
|
127
149
|
environment:
|
|
128
150
|
- NODE_ENV=test
|
|
129
151
|
- DATABASE_URL=postgresql://test:test@db:5432/app_test
|
|
@@ -143,11 +165,11 @@ services:
|
|
|
143
165
|
services:
|
|
144
166
|
frontend:
|
|
145
167
|
build:
|
|
146
|
-
context:
|
|
168
|
+
context: ../frontend
|
|
147
169
|
ports:
|
|
148
|
-
- "
|
|
170
|
+
- "19000:9000"
|
|
149
171
|
environment:
|
|
150
|
-
- VITE_API_URL=http://localhost:
|
|
172
|
+
- VITE_API_URL=http://localhost:18000
|
|
151
173
|
healthcheck:
|
|
152
174
|
test: ["CMD", "curl", "-sf", "http://localhost:9000"]
|
|
153
175
|
interval: 5s
|
|
@@ -162,11 +184,11 @@ services:
|
|
|
162
184
|
services:
|
|
163
185
|
frontend:
|
|
164
186
|
build:
|
|
165
|
-
context:
|
|
187
|
+
context: ../frontend
|
|
166
188
|
ports:
|
|
167
|
-
- "
|
|
189
|
+
- "13001:3000"
|
|
168
190
|
environment:
|
|
169
|
-
- NEXT_PUBLIC_API_URL=http://localhost:
|
|
191
|
+
- NEXT_PUBLIC_API_URL=http://localhost:13000
|
|
170
192
|
healthcheck:
|
|
171
193
|
test: ["CMD", "curl", "-sf", "http://localhost:3000"]
|
|
172
194
|
interval: 5s
|
|
@@ -180,9 +202,9 @@ services:
|
|
|
180
202
|
services:
|
|
181
203
|
api:
|
|
182
204
|
build:
|
|
183
|
-
context:
|
|
205
|
+
context: ../api
|
|
184
206
|
ports:
|
|
185
|
-
- "
|
|
207
|
+
- "18000:8000"
|
|
186
208
|
environment:
|
|
187
209
|
- DATABASE_URL=postgresql://test:test@db:5432/app_test
|
|
188
210
|
- ENVIRONMENT=testing
|
|
@@ -203,9 +225,9 @@ services:
|
|
|
203
225
|
services:
|
|
204
226
|
api:
|
|
205
227
|
build:
|
|
206
|
-
context:
|
|
228
|
+
context: ../api
|
|
207
229
|
ports:
|
|
208
|
-
- "
|
|
230
|
+
- "18080:8080"
|
|
209
231
|
environment:
|
|
210
232
|
- DATABASE_URL=postgresql://test:test@db:5432/app_test
|
|
211
233
|
- ENV=test
|
|
@@ -230,6 +252,8 @@ services:
|
|
|
230
252
|
- POSTGRES_DB=app_test
|
|
231
253
|
- POSTGRES_USER=test
|
|
232
254
|
- POSTGRES_PASSWORD=test
|
|
255
|
+
ports:
|
|
256
|
+
- "15432:5432"
|
|
233
257
|
healthcheck:
|
|
234
258
|
test: ["CMD-SHELL", "pg_isready -U test"]
|
|
235
259
|
interval: 3s
|
|
@@ -249,6 +273,8 @@ services:
|
|
|
249
273
|
- MYSQL_USER=test
|
|
250
274
|
- MYSQL_PASSWORD=test
|
|
251
275
|
- MYSQL_ROOT_PASSWORD=root
|
|
276
|
+
ports:
|
|
277
|
+
- "13306:3306"
|
|
252
278
|
healthcheck:
|
|
253
279
|
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
|
|
254
280
|
interval: 3s
|
|
@@ -263,6 +289,8 @@ services:
|
|
|
263
289
|
```yaml
|
|
264
290
|
redis:
|
|
265
291
|
image: redis:7-alpine
|
|
292
|
+
ports:
|
|
293
|
+
- "16379:6379"
|
|
266
294
|
healthcheck:
|
|
267
295
|
test: ["CMD", "redis-cli", "ping"]
|
|
268
296
|
interval: 3s
|
|
@@ -277,6 +305,8 @@ services:
|
|
|
277
305
|
image: mongo:7
|
|
278
306
|
environment:
|
|
279
307
|
- MONGO_INITDB_DATABASE=app_test
|
|
308
|
+
ports:
|
|
309
|
+
- "127017:27017"
|
|
280
310
|
healthcheck:
|
|
281
311
|
test: ["CMD", "mongosh", "--eval", "db.adminCommand('ping')"]
|
|
282
312
|
interval: 3s
|
|
@@ -289,11 +319,12 @@ services:
|
|
|
289
319
|
|
|
290
320
|
## Startup sequence
|
|
291
321
|
|
|
292
|
-
Always start in dependency order:
|
|
322
|
+
Always start in dependency order with global timeout (10 minutes max):
|
|
293
323
|
1. Databases (db, redis, mongo) — wait for healthy
|
|
294
324
|
2. Backend services (api) — wait for healthy
|
|
295
325
|
3. Frontend services — wait for healthy
|
|
296
|
-
4.
|
|
326
|
+
4. Seed test data if seeder exists
|
|
327
|
+
5. Run tests
|
|
297
328
|
|
|
298
329
|
## Teardown
|
|
299
330
|
|
|
@@ -302,3 +333,4 @@ docker compose -f ./e2e/docker-compose.e2e.yml down -v --remove-orphans
|
|
|
302
333
|
```
|
|
303
334
|
|
|
304
335
|
Always use `-v` to remove test volumes and `--remove-orphans` for cleanup.
|
|
336
|
+
No worktree removal needed — branches are restored via `git checkout` only.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# orphan-cleanup.sh
|
|
3
|
+
# SessionStart hook (phase 1): cleans ONLY orphaned worktrees.
|
|
4
|
+
# v5.0: Active session worktrees (/tmp/{repo}-{session-name}/) are NEVER cleaned here.
|
|
5
|
+
# Only truly orphaned worktrees (no matching session JSON) are removed.
|
|
6
|
+
# Stdout on exit 0 is added as context visible to Claude.
|
|
7
|
+
set -euo pipefail
|
|
8
|
+
|
|
9
|
+
cat > /dev/null
|
|
10
|
+
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
|
|
11
|
+
SESSIONS_DIR="$PROJECT_DIR/.sessions"
|
|
12
|
+
ORPHAN_COUNT=0
|
|
13
|
+
|
|
14
|
+
shopt -s nullglob
|
|
15
|
+
for wt in /tmp/*-session-* /tmp/e2e-*; do
|
|
16
|
+
[ -d "$wt" ] || continue
|
|
17
|
+
|
|
18
|
+
# Check if this worktree belongs to an active session
|
|
19
|
+
IS_ACTIVE_SESSION=0
|
|
20
|
+
if [ -d "$SESSIONS_DIR" ]; then
|
|
21
|
+
for session_file in "$SESSIONS_DIR"/*.json; do
|
|
22
|
+
[ -f "$session_file" ] || continue
|
|
23
|
+
SESSION_STATUS=$(jq -r '.status // "unknown"' "$session_file" 2>/dev/null) || continue
|
|
24
|
+
[ "$SESSION_STATUS" != "active" ] && continue
|
|
25
|
+
if jq -r '.repos[].worktree_path // empty' "$session_file" 2>/dev/null | grep -qF "$wt"; then
|
|
26
|
+
IS_ACTIVE_SESSION=1
|
|
27
|
+
break
|
|
28
|
+
fi
|
|
29
|
+
done
|
|
30
|
+
fi
|
|
31
|
+
|
|
32
|
+
# Skip active session worktrees — they persist until session close
|
|
33
|
+
[ "$IS_ACTIVE_SESSION" -eq 1 ] && continue
|
|
34
|
+
|
|
35
|
+
# Check if registered with a repo
|
|
36
|
+
REPO_FOUND=0
|
|
37
|
+
PARENT_DIR="$(cd "$PROJECT_DIR/.." 2>/dev/null && pwd)" || continue
|
|
38
|
+
for repo_dir in "$PARENT_DIR"/*/; do
|
|
39
|
+
[ -d "$repo_dir/.git" ] || continue
|
|
40
|
+
if git -C "$repo_dir" worktree list 2>/dev/null | grep -q "$wt"; then
|
|
41
|
+
if [ ! -f "$wt/.git" ]; then
|
|
42
|
+
git -C "$repo_dir" worktree remove "$wt" --force 2>/dev/null && ORPHAN_COUNT=$((ORPHAN_COUNT + 1))
|
|
43
|
+
REPO_FOUND=1
|
|
44
|
+
break
|
|
45
|
+
fi
|
|
46
|
+
REPO_FOUND=1
|
|
47
|
+
break
|
|
48
|
+
fi
|
|
49
|
+
done
|
|
50
|
+
|
|
51
|
+
if [ "$REPO_FOUND" -eq 0 ] && { [ -d "$wt/.git" ] || [ -f "$wt/.git" ]; }; then
|
|
52
|
+
rm -rf "$wt" 2>/dev/null && ORPHAN_COUNT=$((ORPHAN_COUNT + 1))
|
|
53
|
+
fi
|
|
54
|
+
done
|
|
55
|
+
|
|
56
|
+
if [ "$ORPHAN_COUNT" -gt 0 ]; then
|
|
57
|
+
echo "[Cleanup] Removed $ORPHAN_COUNT orphaned worktree(s) from /tmp/ (no matching active session)."
|
|
58
|
+
fi
|
|
59
|
+
|
|
60
|
+
exit 0
|
|
@@ -1,16 +1,73 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# permission-auto-approve.sh
|
|
3
|
-
# PermissionRequest hook: auto-approves Read/Glob/Grep
|
|
3
|
+
# PermissionRequest hook: auto-approves Read/Glob/Grep and safe git/bash operations
|
|
4
|
+
# for the orchestrator (Opus) to reduce friction on exploration and git management.
|
|
4
5
|
# Uses hookSpecificOutput JSON with decision.behavior: "allow".
|
|
6
|
+
#
|
|
7
|
+
# Security: compound commands (&&, ||, ;, |, backticks, $()) are NEVER auto-approved.
|
|
8
|
+
# Only single, known-safe commands get auto-approval.
|
|
5
9
|
set -euo pipefail
|
|
6
10
|
|
|
7
11
|
INPUT=$(cat)
|
|
8
12
|
TOOL_NAME=$(echo "$INPUT" | jq -r '.tool_name // empty' 2>/dev/null) || true
|
|
9
13
|
|
|
14
|
+
ALLOW_JSON='{"hookSpecificOutput":{"hookEventName":"PermissionRequest","decision":{"behavior":"allow","toolNames":["Bash"]}}}'
|
|
15
|
+
ALLOW_READ='{"hookSpecificOutput":{"hookEventName":"PermissionRequest","decision":{"behavior":"allow","toolNames":["Read","Glob","Grep"]}}}'
|
|
16
|
+
|
|
17
|
+
# Auto-approve read-only tools
|
|
10
18
|
if echo "$TOOL_NAME" | grep -qE '^(Read|Glob|Grep)$'; then
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
19
|
+
echo "$ALLOW_READ"
|
|
20
|
+
exit 0
|
|
21
|
+
fi
|
|
22
|
+
|
|
23
|
+
# Auto-approve Bash for safe git operations and test/typecheck commands in worktrees
|
|
24
|
+
if [ "$TOOL_NAME" = "Bash" ]; then
|
|
25
|
+
COMMAND=$(echo "$INPUT" | jq -r '.tool_input.command // empty' 2>/dev/null) || true
|
|
26
|
+
|
|
27
|
+
# SECURITY: reject compound commands — never auto-approve chained operations
|
|
28
|
+
# This prevents `git branch session/x && rm -rf /` style bypasses
|
|
29
|
+
# Exception: `cd /tmp/... && <safe-test-command>` is a legitimate micro-QA pattern
|
|
30
|
+
if echo "$COMMAND" | grep -qE '(&&|\|\||[;|]|`|\$\()'; then
|
|
31
|
+
# Allow: cd /tmp/{worktree} && {test-command}
|
|
32
|
+
if echo "$COMMAND" | grep -qE '^\s*cd\s+(/private)?/tmp/[a-zA-Z][a-zA-Z0-9_-]+\s+&&\s+(npm run (typecheck|test|lint)|php artisan test|go test|pytest|npx vitest|npx jest|yarn test|pnpm test)\b'; then
|
|
33
|
+
echo "$ALLOW_JSON"
|
|
34
|
+
exit 0
|
|
35
|
+
fi
|
|
36
|
+
# All other compound commands — do not auto-approve
|
|
37
|
+
exit 0
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
# Safe git read operations (handles: git, git --no-pager, git -c key=val, git -C path)
|
|
41
|
+
if echo "$COMMAND" | grep -qE '^\s*git\s+(--no-pager\s+|(-[cC]\s+\S+\s+))*(log|status|diff|branch --list|branch --show-current|worktree list|show|rev-parse)\b'; then
|
|
42
|
+
echo "$ALLOW_JSON"
|
|
43
|
+
exit 0
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
# Safe git branch creation (no checkout) — handles -C path prefix
|
|
47
|
+
# Only session/ prefixed branches
|
|
48
|
+
if echo "$COMMAND" | grep -qE '^\s*git\s+(--no-pager\s+|(-[cC]\s+\S+\s+))*branch\s+session/'; then
|
|
49
|
+
echo "$ALLOW_JSON"
|
|
50
|
+
exit 0
|
|
51
|
+
fi
|
|
52
|
+
|
|
53
|
+
# Safe git worktree add for session worktrees in /tmp/
|
|
54
|
+
if echo "$COMMAND" | grep -qE '^\s*git\s+((-[cC]\s+\S+\s+))*worktree\s+add\s+/tmp/'; then
|
|
55
|
+
echo "$ALLOW_JSON"
|
|
56
|
+
exit 0
|
|
57
|
+
fi
|
|
58
|
+
|
|
59
|
+
# Safe typecheck/test commands in /tmp/ worktrees (micro-QA)
|
|
60
|
+
# Compound check above already rejected chained commands
|
|
61
|
+
if echo "$COMMAND" | grep -qE '^\s*cd\s+(/private)?/tmp/[a-zA-Z]'; then
|
|
62
|
+
echo "$ALLOW_JSON"
|
|
63
|
+
exit 0
|
|
64
|
+
fi
|
|
65
|
+
|
|
66
|
+
# Safe git diff for debug artifact check in /tmp/ worktrees
|
|
67
|
+
if echo "$COMMAND" | grep -qE '^\s*git\s+-C\s+(/private)?/tmp/\S+\s+diff\b'; then
|
|
68
|
+
echo "$ALLOW_JSON"
|
|
69
|
+
exit 0
|
|
70
|
+
fi
|
|
14
71
|
fi
|
|
15
72
|
|
|
16
73
|
exit 0
|
|
@@ -1,46 +1,16 @@
|
|
|
1
1
|
#!/usr/bin/env bash
|
|
2
2
|
# session-start-context.sh
|
|
3
|
-
# SessionStart hook: injects active plan context, repo status,
|
|
3
|
+
# SessionStart hook (phase 2): injects active plan context, repo status, session info.
|
|
4
|
+
# Orphan worktree cleanup is handled by orphan-cleanup.sh (separate hook).
|
|
4
5
|
# Stdout on exit 0 is added as context visible to Claude.
|
|
5
6
|
set -euo pipefail
|
|
6
7
|
|
|
7
8
|
cat > /dev/null
|
|
8
9
|
PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
|
|
10
|
+
SESSIONS_DIR="$PROJECT_DIR/.sessions"
|
|
9
11
|
OUTPUT=""
|
|
10
12
|
|
|
11
|
-
# ──
|
|
12
|
-
# Clean /tmp/ worktrees that were left behind by crashed implementers.
|
|
13
|
-
# These are safe to remove: they're temporary by design.
|
|
14
|
-
ORPHAN_COUNT=0
|
|
15
|
-
for wt in /tmp/*-session-* /tmp/e2e-* 2>/dev/null; do
|
|
16
|
-
[ -d "$wt" ] || continue
|
|
17
|
-
# Check if the worktree is registered with any repo
|
|
18
|
-
REPO_FOUND=0
|
|
19
|
-
PARENT_DIR="$(cd "$PROJECT_DIR/.." 2>/dev/null && pwd)" || continue
|
|
20
|
-
for repo_dir in "$PARENT_DIR"/*/; do
|
|
21
|
-
[ -d "$repo_dir/.git" ] || continue
|
|
22
|
-
if git -C "$repo_dir" worktree list 2>/dev/null | grep -q "$wt"; then
|
|
23
|
-
# Worktree is registered — check if it's stale (no git lock, no recent activity)
|
|
24
|
-
if [ ! -f "$wt/.git" ]; then
|
|
25
|
-
# Not a valid worktree anymore — remove registration
|
|
26
|
-
git -C "$repo_dir" worktree remove "$wt" --force 2>/dev/null && ORPHAN_COUNT=$((ORPHAN_COUNT + 1))
|
|
27
|
-
REPO_FOUND=1
|
|
28
|
-
break
|
|
29
|
-
fi
|
|
30
|
-
REPO_FOUND=1
|
|
31
|
-
break
|
|
32
|
-
fi
|
|
33
|
-
done
|
|
34
|
-
if [ "$REPO_FOUND" -eq 0 ] && [ -d "$wt/.git" ] || [ -f "$wt/.git" ]; then
|
|
35
|
-
# Unregistered worktree directory — likely a crash remnant. Remove it.
|
|
36
|
-
rm -rf "$wt" 2>/dev/null && ORPHAN_COUNT=$((ORPHAN_COUNT + 1))
|
|
37
|
-
fi
|
|
38
|
-
done
|
|
39
|
-
if [ "$ORPHAN_COUNT" -gt 0 ]; then
|
|
40
|
-
OUTPUT+="[Cleanup] Removed $ORPHAN_COUNT orphan worktree(s) from /tmp/.\n"
|
|
41
|
-
fi
|
|
42
|
-
|
|
43
|
-
# ── Active plans ─────────────────────────────────────────────
|
|
13
|
+
# ── Active plans ─────────────────────────────────────────────────────────────
|
|
44
14
|
if [ -d "$PROJECT_DIR/plans" ]; then
|
|
45
15
|
ACTIVE_PLANS=$(find "$PROJECT_DIR/plans" -name '*.md' \
|
|
46
16
|
! -name '_TEMPLATE.md' \
|
|
@@ -49,26 +19,19 @@ if [ -d "$PROJECT_DIR/plans" ]; then
|
|
|
49
19
|
| sort | tail -5)
|
|
50
20
|
|
|
51
21
|
if [ -n "$ACTIVE_PLANS" ]; then
|
|
52
|
-
FIRST_PLAN=""
|
|
53
22
|
OUTPUT+="[Session context] Active plans with pending tasks:\n"
|
|
54
23
|
while IFS= read -r plan; do
|
|
55
24
|
PLAN_NAME=$(basename "$plan")
|
|
56
|
-
[ -z "$FIRST_PLAN" ] && FIRST_PLAN="$PLAN_NAME"
|
|
57
25
|
TODO=$(grep -c '⏳' "$plan" 2>/dev/null || echo "0")
|
|
58
26
|
WIP=$(grep -c '🔄' "$plan" 2>/dev/null || echo "0")
|
|
59
27
|
DONE=$(grep -c '✅' "$plan" 2>/dev/null || echo "0")
|
|
60
28
|
OUTPUT+=" - $PLAN_NAME (⏳ $TODO pending, 🔄 $WIP in progress, ✅ $DONE done)\n"
|
|
61
29
|
done <<< "$ACTIVE_PLANS"
|
|
62
30
|
OUTPUT+="\nRead these plans to resume where you left off.\n"
|
|
63
|
-
|
|
64
|
-
if [ -n "${CLAUDE_ENV_FILE:-}" ] && [ -n "$FIRST_PLAN" ]; then
|
|
65
|
-
echo "ACTIVE_PLAN=$FIRST_PLAN" >> "$CLAUDE_ENV_FILE"
|
|
66
|
-
fi
|
|
67
31
|
fi
|
|
68
32
|
fi
|
|
69
33
|
|
|
70
|
-
# ── Active sessions
|
|
71
|
-
SESSIONS_DIR="$PROJECT_DIR/.sessions"
|
|
34
|
+
# ── Active sessions ───────────────────────────────────────────────────────────
|
|
72
35
|
if [ -d "$SESSIONS_DIR" ]; then
|
|
73
36
|
ACTIVE_SESSIONS=""
|
|
74
37
|
for session_file in "$SESSIONS_DIR"/*.json; do
|
|
@@ -81,16 +44,16 @@ if [ -d "$SESSIONS_DIR" ]; then
|
|
|
81
44
|
done
|
|
82
45
|
if [ -n "$ACTIVE_SESSIONS" ]; then
|
|
83
46
|
OUTPUT+="[Session context] Active sessions:\n$ACTIVE_SESSIONS"
|
|
84
|
-
OUTPUT+="Session branches are already
|
|
47
|
+
OUTPUT+="Session branches and worktrees are already set up. Teammates use worktree_path from session JSON.\n\n"
|
|
85
48
|
fi
|
|
86
49
|
fi
|
|
87
50
|
|
|
88
|
-
# ── Workspace check
|
|
51
|
+
# ── Workspace check ───────────────────────────────────────────────────────────
|
|
89
52
|
if [ ! -f "$PROJECT_DIR/workspace.md" ]; then
|
|
90
|
-
OUTPUT+="[WARNING] No workspace.md found. Run
|
|
53
|
+
OUTPUT+="[WARNING] No workspace.md found. Run: claude --agent workspace-init\n"
|
|
91
54
|
fi
|
|
92
55
|
|
|
93
|
-
# ── First-session detection
|
|
56
|
+
# ── First-session detection ───────────────────────────────────────────────────
|
|
94
57
|
if [ -f "$PROJECT_DIR/workspace.md" ]; then
|
|
95
58
|
if grep -q '\[UNCONFIGURED\]' "$PROJECT_DIR/workspace.md" 2>/dev/null; then
|
|
96
59
|
OUTPUT+="[FIRST SESSION] workspace.md is not yet configured. Run: claude --agent workspace-init\n"
|
|
@@ -108,7 +71,7 @@ if [ -f "$PROJECT_DIR/workspace.md" ]; then
|
|
|
108
71
|
fi
|
|
109
72
|
fi
|
|
110
73
|
|
|
111
|
-
# ── Auto-discovery: new repos
|
|
74
|
+
# ── Auto-discovery: new repos ─────────────────────────────────────────────────
|
|
112
75
|
if [ -f "$PROJECT_DIR/workspace.md" ] && ! grep -q '\[UNCONFIGURED\]' "$PROJECT_DIR/workspace.md" 2>/dev/null; then
|
|
113
76
|
PARENT_DIR="$(cd "$PROJECT_DIR/.." 2>/dev/null && pwd)"
|
|
114
77
|
NEW_REPOS=""
|