cc-workspace 4.1.2 → 4.1.4

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/README.md CHANGED
@@ -27,7 +27,7 @@ cd ~/projects/my-workspace
27
27
  npx cc-workspace init . "My Project"
28
28
  ```
29
29
 
30
- This creates an `orchestrator/` directory and installs 9 skills, 3 agents, 11 hooks, and 3 rules into `~/.claude/`.
30
+ This creates an `orchestrator/` directory and installs 9 skills, 3 agents, 9 hooks, and 3 rules into `~/.claude/`.
31
31
 
32
32
  ### Configure (one time)
33
33
 
@@ -200,13 +200,13 @@ next one starts. The plan on disk is the source of truth.
200
200
 
201
201
  ---
202
202
 
203
- ## The 11 hooks
203
+ ## The 9 hooks + 1 agent-level hook
204
204
 
205
- All hooks are **non-blocking** (exit 0 + warning). No hook blocks the session.
205
+ All hooks in settings.json are **non-blocking** (exit 0 + warning). No hook blocks the session.
206
206
 
207
207
  | Hook | Event | Effect |
208
208
  |------|-------|--------|
209
- | **block-orchestrator-writes** | `PreToolUse` Write\|Edit\|MultiEdit | Blocks writes in repos, allows orchestrator/ |
209
+ | **block-orchestrator-writes** | `PreToolUse` Write\|Edit\|MultiEdit | Blocks writes in repos, allows orchestrator/. **Agent frontmatter only** (team-lead) — not in settings.json, so teammates can write freely. |
210
210
  | **validate-spawn-prompt** | `PreToolUse` Teammate | Warning if missing context (rules, UX, tasks) |
211
211
  | **session-start-context** | `SessionStart` | Injects active plans + first session detection |
212
212
  | **user-prompt-guard** | `UserPromptSubmit` | Warning if code requested in a repo |
@@ -215,7 +215,6 @@ All hooks are **non-blocking** (exit 0 + warning). No hook blocks the session.
215
215
  | **track-file-modifications** | `PostToolUse` (async) | Log of modified files |
216
216
  | **teammate-idle-check** | `TeammateIdle` | Warning if remaining tasks |
217
217
  | **task-completed-check** | `TaskCompleted` | Warning if tests failed |
218
- | **worktree-create-context** | `WorktreeCreate` | Reminder to read repo CLAUDE.md |
219
218
  | **notify-user** | `Notification` | Desktop notification |
220
219
 
221
220
  ---
@@ -340,6 +339,18 @@ Both `init` and `update` are safe to re-run:
340
339
 
341
340
  ---
342
341
 
342
+ ## Changelog v4.1.0 -> v4.1.4
343
+
344
+ | # | Fix | Detail |
345
+ |---|-----|--------|
346
+ | 1 | **Hook paths use `$CLAUDE_PROJECT_DIR`** | All hooks in settings.json resolve via `${CLAUDE_PROJECT_DIR:-.}/.claude/hooks/`. Fixes failures when subagents run from a worktree CWD. |
347
+ | 2 | **stdout/stderr fix** | `task-completed-check.sh` and `teammate-idle-check.sh`: moved messages to stderr (stdout ignored by Claude Code for these events). |
348
+ | 3 | **Removed `WorktreeCreate` hook** | stdout was interpreted as worktree path, creating ghost directories. Removed. |
349
+ | 4 | **`block-orchestrator-writes` moved to agent frontmatter** | Was in settings.json → inherited by teammates, blocking their writes in worktrees. Now only in team-lead frontmatter. |
350
+ | 5 | **`track-file-modifications` scoped** | Skips when `CLAUDE_PROJECT_DIR` is unset (teammate worktree). No more parasitic log files in worktrees. |
351
+
352
+ ---
353
+
343
354
  ## Changelog v4.0.5 -> v4.1.0
344
355
 
345
356
  | # | Feature | Detail |
package/bin/cli.js CHANGED
@@ -222,7 +222,9 @@ function generateSettings(orchDir) {
222
222
  },
223
223
  hooks: {
224
224
  PreToolUse: [
225
- withMatcher("Write|Edit|MultiEdit", "block-orchestrator-writes.sh", 5),
225
+ // block-orchestrator-writes.sh is NOT here — it's in team-lead agent
226
+ // frontmatter only. Putting it in settings.json would block teammates
227
+ // from writing in their worktrees.
226
228
  withMatcher("Teammate", "validate-spawn-prompt.sh", 5)
227
229
  ],
228
230
  SessionStart: [
@@ -246,9 +248,6 @@ function generateSettings(orchDir) {
246
248
  TaskCompleted: [
247
249
  withoutMatcher("task-completed-check.sh", 3)
248
250
  ],
249
- WorktreeCreate: [
250
- withoutMatcher("worktree-create-context.sh", 3)
251
- ],
252
251
  Notification: [
253
252
  withoutMatcher("notify-user.sh", 5)
254
253
  ]
@@ -468,12 +467,16 @@ function updateLocal() {
468
467
  // ── Hooks (always overwrite — security critical) ──
469
468
  const hooksDir = path.join(orchDir, ".claude", "hooks");
470
469
  if (fs.existsSync(hooksDir)) {
471
- generateBlockHook(hooksDir);
472
- count++;
470
+ // Clean obsolete hooks before copying new ones
471
+ const obsoleteHooks = ["block-orchestrator-writes.sh", "worktree-create-context.sh", "verify-cycle-complete.sh"];
472
+ for (const f of obsoleteHooks) {
473
+ const fp = path.join(hooksDir, f);
474
+ if (fs.existsSync(fp)) fs.unlinkSync(fp);
475
+ }
473
476
  const hooksSrc = path.join(SKILLS_DIR, "hooks");
474
477
  if (fs.existsSync(hooksSrc)) {
475
478
  for (const f of fs.readdirSync(hooksSrc)) {
476
- if (!f.endsWith(".sh") || f === "verify-cycle-complete.sh") continue;
479
+ if (!f.endsWith(".sh")) continue;
477
480
  copyFile(path.join(hooksSrc, f), path.join(hooksDir, f));
478
481
  fs.chmodSync(path.join(hooksDir, f), 0o755);
479
482
  count++;
@@ -575,12 +578,11 @@ function setupWorkspace(workspacePath, projectName) {
575
578
  // ── Hooks ──
576
579
  step("Installing hooks");
577
580
  const hooksDir = path.join(orchDir, ".claude", "hooks");
578
- generateBlockHook(hooksDir);
579
581
  const hooksSrc = path.join(SKILLS_DIR, "hooks");
580
- let hookCount = 1;
582
+ let hookCount = 0;
581
583
  if (fs.existsSync(hooksSrc)) {
582
584
  for (const f of fs.readdirSync(hooksSrc)) {
583
- if (!f.endsWith(".sh") || f === "verify-cycle-complete.sh") continue;
585
+ if (!f.endsWith(".sh")) continue;
584
586
  copyFile(path.join(hooksSrc, f), path.join(hooksDir, f));
585
587
  fs.chmodSync(path.join(hooksDir, f), 0o755);
586
588
  hookCount++;
@@ -1,7 +1,7 @@
1
1
  #!/usr/bin/env bash
2
2
  # task-completed-check.sh
3
3
  # TaskCompleted hook: reminds teammates to verify tests/dead code/constitution.
4
- # Exit 0 + stdout = inject reminder as context (non-blocking, v4.0).
4
+ # TeammateIdle/TaskCompleted use stderr for feedback (stdout is ignored).
5
5
  set -euo pipefail
6
6
 
7
7
  INPUT=$(cat)
@@ -10,12 +10,8 @@ INPUT=$(cat)
10
10
  TASK_OUTPUT=$(echo "$INPUT" | jq -r '.task_output // empty' 2>/dev/null) || true
11
11
 
12
12
  # Check for explicit failure signals across common test runners
13
- # PHPUnit: FAILURES!, ERRORS!, Tests: X, Failures: Y
14
- # pytest/vitest/jest: FAILED, failed, ✗, Error
15
- # Pest (Laravel): FAIL, Tests: X, X failed
16
- # Generic: exit code non-zero indicators, AssertionError (Python), AssertionError (various)
17
13
  if echo "$TASK_OUTPUT" | grep -qiE '(tests?\s*fail|FAIL(ED|URES?)|error.*test|test.*error|ERRORS?:|failures?:|AssertionError|exit\s*code\s*[1-9])' 2>/dev/null; then
18
- echo "[Warning] Tests appear to have failed. Verify before marking complete."
14
+ echo "[Warning] Tests appear to have failed. Verify before marking complete." >&2
19
15
  fi
20
- echo "Task completion checklist: 1) Verify tests passed 2) Check for dead code 3) Verify constitution compliance."
16
+ echo "Task completion checklist: 1) Verify tests passed 2) Check for dead code 3) Verify constitution compliance." >&2
21
17
  exit 0
@@ -20,7 +20,7 @@ if [ -d "$PROJECT_DIR/plans" ]; then
20
20
 
21
21
  if [ -n "$PENDING" ]; then
22
22
  PLAN_NAMES=$(echo "$PENDING" | xargs -I{} basename {} | tr '\n' ', ' | sed 's/,$//')
23
- echo "[Warning] Unassigned tasks remain in: $PLAN_NAMES. Consider claiming the next pending task."
23
+ echo "[Warning] Unassigned tasks remain in: $PLAN_NAMES. Consider claiming the next pending task." >&2
24
24
  exit 0
25
25
  fi
26
26
  fi
@@ -2,18 +2,24 @@
2
2
  # track-file-modifications.sh
3
3
  # PostToolUse hook (async): logs modified files for merge-prep.
4
4
  # Matcher: Write|Edit|MultiEdit
5
- # Appends to .claude/modified-files.log
5
+ # Appends to .claude/modified-files.log in orchestrator/ only.
6
+ # Skips silently if CLAUDE_PROJECT_DIR is unset (teammate worktree context).
6
7
  set -euo pipefail
7
8
 
8
9
  INPUT=$(cat)
9
- PROJECT_DIR="${CLAUDE_PROJECT_DIR:-.}"
10
+ PROJECT_DIR="${CLAUDE_PROJECT_DIR:-}"
11
+
12
+ # Skip if not in orchestrator context
13
+ if [ -z "$PROJECT_DIR" ] || [ ! -d "$PROJECT_DIR/.claude" ]; then
14
+ exit 0
15
+ fi
16
+
10
17
  LOG_FILE="$PROJECT_DIR/.claude/modified-files.log"
11
18
 
12
19
  # Extract file path from tool input
13
20
  FILE_PATH=$(echo "$INPUT" | jq -r '.tool_input.file_path // .tool_input.filePath // empty' 2>/dev/null) || true
14
21
 
15
22
  if [ -n "$FILE_PATH" ]; then
16
- mkdir -p "$(dirname "$LOG_FILE")"
17
23
  echo "$(date +%Y-%m-%dT%H:%M:%S) $FILE_PATH" >> "$LOG_FILE"
18
24
  fi
19
25
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cc-workspace",
3
- "version": "4.1.2",
3
+ "version": "4.1.4",
4
4
  "description": "Claude Code multi-workspace orchestrator — skills, hooks, agents, and templates for multi-service projects",
5
5
  "bin": {
6
6
  "cc-workspace": "./bin/cli.js"
@@ -1,22 +0,0 @@
1
- #!/usr/bin/env bash
2
- # worktree-create-context.sh
3
- # WorktreeCreate hook: logs worktree creation and reminds teammate to read repo CLAUDE.md.
4
- # Parses stdin JSON to extract the worktree path for a specific reminder.
5
- set -euo pipefail
6
-
7
- INPUT=$(cat)
8
-
9
- WORKTREE_PATH=$(echo "$INPUT" | jq -r '.worktree_path // empty' 2>/dev/null) || true
10
-
11
- if [ -n "$WORKTREE_PATH" ]; then
12
- echo "[WorktreeCreate] Worktree created at: $WORKTREE_PATH"
13
- if [ -f "$WORKTREE_PATH/CLAUDE.md" ]; then
14
- echo "Read $WORKTREE_PATH/CLAUDE.md first — follow its conventions."
15
- else
16
- echo "WARNING: No CLAUDE.md found in $WORKTREE_PATH. Check repo root or run bootstrap-repo."
17
- fi
18
- else
19
- echo "[WorktreeCreate] Worktree created. Read the CLAUDE.md in the repo root first."
20
- fi
21
-
22
- exit 0