shipwright-cli 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +926 -0
- package/claude-code/CLAUDE.md.shipwright +125 -0
- package/claude-code/hooks/notify-idle.sh +35 -0
- package/claude-code/hooks/pre-compact-save.sh +57 -0
- package/claude-code/hooks/task-completed.sh +170 -0
- package/claude-code/hooks/teammate-idle.sh +68 -0
- package/claude-code/settings.json.template +184 -0
- package/completions/_shipwright +140 -0
- package/completions/shipwright.bash +89 -0
- package/completions/shipwright.fish +107 -0
- package/docs/KNOWN-ISSUES.md +199 -0
- package/docs/TIPS.md +331 -0
- package/docs/definition-of-done.example.md +16 -0
- package/docs/patterns/README.md +139 -0
- package/docs/patterns/audit-loop.md +149 -0
- package/docs/patterns/bug-hunt.md +183 -0
- package/docs/patterns/feature-implementation.md +159 -0
- package/docs/patterns/refactoring.md +183 -0
- package/docs/patterns/research-exploration.md +144 -0
- package/docs/patterns/test-generation.md +173 -0
- package/package.json +49 -0
- package/scripts/adapters/docker-deploy.sh +50 -0
- package/scripts/adapters/fly-deploy.sh +41 -0
- package/scripts/adapters/iterm2-adapter.sh +122 -0
- package/scripts/adapters/railway-deploy.sh +34 -0
- package/scripts/adapters/tmux-adapter.sh +87 -0
- package/scripts/adapters/vercel-deploy.sh +35 -0
- package/scripts/adapters/wezterm-adapter.sh +103 -0
- package/scripts/cct +242 -0
- package/scripts/cct-cleanup.sh +172 -0
- package/scripts/cct-cost.sh +590 -0
- package/scripts/cct-daemon.sh +3189 -0
- package/scripts/cct-doctor.sh +328 -0
- package/scripts/cct-fix.sh +478 -0
- package/scripts/cct-fleet.sh +904 -0
- package/scripts/cct-init.sh +282 -0
- package/scripts/cct-logs.sh +273 -0
- package/scripts/cct-loop.sh +1332 -0
- package/scripts/cct-memory.sh +1148 -0
- package/scripts/cct-pipeline.sh +3844 -0
- package/scripts/cct-prep.sh +1352 -0
- package/scripts/cct-ps.sh +168 -0
- package/scripts/cct-reaper.sh +390 -0
- package/scripts/cct-session.sh +284 -0
- package/scripts/cct-status.sh +169 -0
- package/scripts/cct-templates.sh +242 -0
- package/scripts/cct-upgrade.sh +422 -0
- package/scripts/cct-worktree.sh +405 -0
- package/scripts/postinstall.mjs +96 -0
- package/templates/pipelines/autonomous.json +71 -0
- package/templates/pipelines/cost-aware.json +95 -0
- package/templates/pipelines/deployed.json +79 -0
- package/templates/pipelines/enterprise.json +114 -0
- package/templates/pipelines/fast.json +63 -0
- package/templates/pipelines/full.json +104 -0
- package/templates/pipelines/hotfix.json +63 -0
- package/templates/pipelines/standard.json +91 -0
- package/tmux/claude-teams-overlay.conf +109 -0
- package/tmux/templates/architecture.json +19 -0
- package/tmux/templates/bug-fix.json +24 -0
- package/tmux/templates/code-review.json +24 -0
- package/tmux/templates/devops.json +19 -0
- package/tmux/templates/documentation.json +19 -0
- package/tmux/templates/exploration.json +19 -0
- package/tmux/templates/feature-dev.json +24 -0
- package/tmux/templates/full-stack.json +24 -0
- package/tmux/templates/migration.json +24 -0
- package/tmux/templates/refactor.json +19 -0
- package/tmux/templates/security-audit.json +24 -0
- package/tmux/templates/testing.json +24 -0
- package/tmux/tmux.conf +167 -0
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
# Shipwright — Agent Instructions
|
|
2
|
+
|
|
3
|
+
This project uses [Shipwright](https://github.com/sethdford/shipwright) for autonomous Claude Code agent teams.
|
|
4
|
+
|
|
5
|
+
## Commands
|
|
6
|
+
|
|
7
|
+
| Command | Purpose |
|
|
8
|
+
|---------|---------|
|
|
9
|
+
| `shipwright pipeline start --issue <N>` | Run full delivery pipeline for an issue |
|
|
10
|
+
| `shipwright pipeline start --issue <N> --worktree` | Pipeline in isolated git worktree (parallel-safe) |
|
|
11
|
+
| `shipwright pipeline start --goal "..." --worktree=name` | Pipeline with named worktree |
|
|
12
|
+
| `shipwright session <name> --template <tpl>` | Create a team session with agent panes |
|
|
13
|
+
| `shipwright daemon start` | Watch repo for labeled issues, auto-process |
|
|
14
|
+
| `shipwright fleet start` | Orchestrate daemons across multiple repos |
|
|
15
|
+
| `shipwright fix "<goal>" --repos <paths>` | Apply the same fix across repos in parallel |
|
|
16
|
+
| `shipwright prep` | Analyze repo and generate preparation report |
|
|
17
|
+
| `shipwright loop` | Continuous improvement loop |
|
|
18
|
+
| `shipwright status` | Show team dashboard |
|
|
19
|
+
| `shipwright cost show` | Token usage and spending dashboard |
|
|
20
|
+
| `shipwright cost budget set <amount>` | Set daily budget limit |
|
|
21
|
+
| `shipwright cost remaining-budget` | Check remaining daily budget (used by auto-scaler) |
|
|
22
|
+
| `shipwright memory list` | View captured failure patterns |
|
|
23
|
+
|
|
24
|
+
## Pipeline Stages
|
|
25
|
+
|
|
26
|
+
```
|
|
27
|
+
intake → plan → design → build → test → review → compound_quality → pr → deploy → validate → monitor
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Parallel Pipelines
|
|
31
|
+
|
|
32
|
+
Use `--worktree` to run multiple pipelines concurrently on the same repo:
|
|
33
|
+
|
|
34
|
+
```bash
|
|
35
|
+
# Each runs in its own git worktree — no conflicts
|
|
36
|
+
shipwright pipeline start --issue 42 --worktree
|
|
37
|
+
shipwright pipeline start --issue 43 --worktree
|
|
38
|
+
shipwright pipeline start --goal "Refactor auth" --worktree=auth-refactor
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
The daemon uses worktrees automatically. Use `--worktree` for ad-hoc parallel runs.
|
|
42
|
+
|
|
43
|
+
## Auto-Scaling (Daemon)
|
|
44
|
+
|
|
45
|
+
The daemon can dynamically adjust worker count based on system resources:
|
|
46
|
+
|
|
47
|
+
```json
|
|
48
|
+
{
|
|
49
|
+
"auto_scale": true,
|
|
50
|
+
"auto_scale_interval": 5,
|
|
51
|
+
"max_workers": 8,
|
|
52
|
+
"min_workers": 1,
|
|
53
|
+
"worker_mem_gb": 4,
|
|
54
|
+
"estimated_cost_per_job_usd": 5.0
|
|
55
|
+
}
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Scaling factors (takes the minimum):
|
|
59
|
+
- **CPU**: 75% of cores (e.g., 8-core → max 6 workers)
|
|
60
|
+
- **Memory**: available GB / `worker_mem_gb`
|
|
61
|
+
- **Budget**: remaining daily budget / `estimated_cost_per_job_usd`
|
|
62
|
+
- **Queue**: current demand (active + queued issues)
|
|
63
|
+
|
|
64
|
+
## Fleet Worker Pool
|
|
65
|
+
|
|
66
|
+
Distribute a total worker budget across repos proportionally to demand:
|
|
67
|
+
|
|
68
|
+
```json
|
|
69
|
+
{
|
|
70
|
+
"worker_pool": {
|
|
71
|
+
"enabled": true,
|
|
72
|
+
"total_workers": 12,
|
|
73
|
+
"rebalance_interval_seconds": 120
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
When enabled, the fleet rebalancer runs in the background and redistributes workers every N seconds. Repos with more queued issues get more workers. Each repo always gets at least 1 worker.
|
|
79
|
+
|
|
80
|
+
## tmux Conventions
|
|
81
|
+
|
|
82
|
+
- Team windows are named `claude-<team-name>` (get the lambda icon in the status bar)
|
|
83
|
+
- Pane titles: `<team>-<role>` (visible in pane borders)
|
|
84
|
+
- Set pane title: `printf '\033]2;agent-name\033\\'`
|
|
85
|
+
- Prefix key: **Ctrl-a**
|
|
86
|
+
- Layouts: `prefix + M-1` (horizontal), `M-2` (vertical), `M-3` (tiled)
|
|
87
|
+
- Zoom: `prefix + G` (toggle focus on one pane)
|
|
88
|
+
- Capture output: `prefix + M-s` (current pane), `prefix + M-a` (all panes)
|
|
89
|
+
|
|
90
|
+
## Team Patterns
|
|
91
|
+
|
|
92
|
+
- Assign each agent **different files** to avoid merge conflicts
|
|
93
|
+
- Use `--worktree` for file isolation between agents
|
|
94
|
+
- Keep tasks self-contained (5-6 focused tasks per agent)
|
|
95
|
+
- Use the task list for coordination, not direct messaging
|
|
96
|
+
|
|
97
|
+
## Memory System
|
|
98
|
+
|
|
99
|
+
Failure patterns are automatically captured after each pipeline run and injected into future builds. Agents receive relevant context from previous runs — fixes, root causes, and codebase conventions — so they don't repeat mistakes.
|
|
100
|
+
|
|
101
|
+
## Pipeline Templates
|
|
102
|
+
|
|
103
|
+
| Template | Use Case |
|
|
104
|
+
|----------|----------|
|
|
105
|
+
| `fast` | Simple changes (score >= 70) — skip review |
|
|
106
|
+
| `standard` | Medium complexity — full pipeline |
|
|
107
|
+
| `full` | Complex changes — extra review cycles |
|
|
108
|
+
| `hotfix` | Urgent fixes — minimal stages |
|
|
109
|
+
| `autonomous` | Daemon-driven — all stages enabled |
|
|
110
|
+
| `cost-aware` | Budget-conscious — model routing by stage |
|
|
111
|
+
|
|
112
|
+
## Daemon Configuration
|
|
113
|
+
|
|
114
|
+
Generate with `shipwright daemon init`, then edit `.claude/daemon-config.json`:
|
|
115
|
+
|
|
116
|
+
| Field | Default | Purpose |
|
|
117
|
+
|-------|---------|---------|
|
|
118
|
+
| `max_parallel` | `2` | Static worker limit (overridden by auto_scale) |
|
|
119
|
+
| `auto_scale` | `false` | Enable resource-aware dynamic scaling |
|
|
120
|
+
| `max_workers` | `8` | Ceiling for auto-scaler |
|
|
121
|
+
| `min_workers` | `1` | Floor for auto-scaler |
|
|
122
|
+
| `self_optimize` | `false` | Auto-tune based on DORA metrics |
|
|
123
|
+
| `auto_template` | `false` | Pick pipeline template by issue complexity |
|
|
124
|
+
| `max_retries` | `2` | Retry failed pipelines with escalation |
|
|
125
|
+
| `priority_lane` | `false` | Reserve a slot for urgent/hotfix issues |
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ═══════════════════════════════════════════════════════════════════════
|
|
3
|
+
# notify-idle.sh — Desktop notification when Claude needs your attention
|
|
4
|
+
# ═══════════════════════════════════════════════════════════════════════
|
|
5
|
+
#
|
|
6
|
+
# Works on macOS (osascript) and Linux (notify-send).
|
|
7
|
+
#
|
|
8
|
+
# Install:
|
|
9
|
+
# 1. Copy this file to ~/.claude/hooks/notify-idle.sh
|
|
10
|
+
# 2. chmod +x ~/.claude/hooks/notify-idle.sh
|
|
11
|
+
# 3. Add to ~/.claude/settings.json:
|
|
12
|
+
# "hooks": {
|
|
13
|
+
# "Notification": [
|
|
14
|
+
# {
|
|
15
|
+
# "hooks": [
|
|
16
|
+
# {
|
|
17
|
+
# "type": "command",
|
|
18
|
+
# "command": "~/.claude/hooks/notify-idle.sh",
|
|
19
|
+
# "async": true
|
|
20
|
+
# }
|
|
21
|
+
# ]
|
|
22
|
+
# }
|
|
23
|
+
# ]
|
|
24
|
+
# }
|
|
25
|
+
# ═══════════════════════════════════════════════════════════════════════
|
|
26
|
+
|
|
27
|
+
set -euo pipefail
|
|
28
|
+
|
|
29
|
+
if [[ "$(uname)" == "Darwin" ]]; then
|
|
30
|
+
osascript -e 'display notification "An agent needs your attention" with title "Claude Code Teams" sound name "Ping"'
|
|
31
|
+
elif command -v notify-send &>/dev/null; then
|
|
32
|
+
notify-send "Claude Code Teams" "An agent needs your attention" --urgency=normal
|
|
33
|
+
fi
|
|
34
|
+
|
|
35
|
+
exit 0
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ═══════════════════════════════════════════════════════════════════════
|
|
3
|
+
# pre-compact-save.sh — Save important context before compaction
|
|
4
|
+
# ═══════════════════════════════════════════════════════════════════════
|
|
5
|
+
#
|
|
6
|
+
# Outputs text to stdout that gets injected into Claude's context after
|
|
7
|
+
# compaction. Helps Claude remember project state across compaction events.
|
|
8
|
+
#
|
|
9
|
+
# Install:
|
|
10
|
+
# 1. Copy this file to ~/.claude/hooks/pre-compact-save.sh
|
|
11
|
+
# 2. chmod +x ~/.claude/hooks/pre-compact-save.sh
|
|
12
|
+
# 3. Add to ~/.claude/settings.json:
|
|
13
|
+
# "hooks": {
|
|
14
|
+
# "PreCompact": [
|
|
15
|
+
# {
|
|
16
|
+
# "matcher": "auto",
|
|
17
|
+
# "hooks": [
|
|
18
|
+
# {
|
|
19
|
+
# "type": "command",
|
|
20
|
+
# "command": "~/.claude/hooks/pre-compact-save.sh",
|
|
21
|
+
# "statusMessage": "Saving context before compaction..."
|
|
22
|
+
# }
|
|
23
|
+
# ]
|
|
24
|
+
# }
|
|
25
|
+
# ]
|
|
26
|
+
# }
|
|
27
|
+
# ═══════════════════════════════════════════════════════════════════════
|
|
28
|
+
|
|
29
|
+
set -euo pipefail
|
|
30
|
+
|
|
31
|
+
INPUT=$(cat)
|
|
32
|
+
CWD=$(echo "$INPUT" | jq -r '.cwd // empty' 2>/dev/null || true)
|
|
33
|
+
|
|
34
|
+
if [[ -n "$CWD" ]] && [[ -d "$CWD" ]]; then
|
|
35
|
+
cd "$CWD"
|
|
36
|
+
fi
|
|
37
|
+
|
|
38
|
+
# Remind Claude of project context after compaction
|
|
39
|
+
echo "Post-compaction context refresh:"
|
|
40
|
+
|
|
41
|
+
# Show recent git activity
|
|
42
|
+
if git rev-parse --is-inside-work-tree &>/dev/null 2>&1; then
|
|
43
|
+
echo ""
|
|
44
|
+
echo "Recent commits:"
|
|
45
|
+
git log --oneline -5 2>/dev/null || true
|
|
46
|
+
echo ""
|
|
47
|
+
echo "Current branch: $(git branch --show-current 2>/dev/null || echo 'unknown')"
|
|
48
|
+
echo "Changed files: $(git diff --name-only 2>/dev/null | head -10 || true)"
|
|
49
|
+
fi
|
|
50
|
+
|
|
51
|
+
# Show CLAUDE.md reminder if present
|
|
52
|
+
if [[ -f "CLAUDE.md" ]]; then
|
|
53
|
+
echo ""
|
|
54
|
+
echo "Project has CLAUDE.md — re-read it for project conventions."
|
|
55
|
+
fi
|
|
56
|
+
|
|
57
|
+
exit 0
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ═══════════════════════════════════════════════════════════════════════
|
|
3
|
+
# task-completed.sh — Quality gate: block task completion if quality fails
|
|
4
|
+
# ═══════════════════════════════════════════════════════════════════════
|
|
5
|
+
#
|
|
6
|
+
# Runs when a Claude Code agent marks a task as completed. Checks lint
|
|
7
|
+
# and tests on changed files. If any check fails, exit code 2 tells
|
|
8
|
+
# Claude the task isn't really done yet.
|
|
9
|
+
#
|
|
10
|
+
# Install:
|
|
11
|
+
# 1. Copy this file to ~/.claude/hooks/task-completed.sh
|
|
12
|
+
# 2. chmod +x ~/.claude/hooks/task-completed.sh
|
|
13
|
+
# 3. Add to ~/.claude/settings.json:
|
|
14
|
+
# "hooks": {
|
|
15
|
+
# "TaskCompleted": [
|
|
16
|
+
# {
|
|
17
|
+
# "hooks": [
|
|
18
|
+
# {
|
|
19
|
+
# "type": "command",
|
|
20
|
+
# "command": "~/.claude/hooks/task-completed.sh",
|
|
21
|
+
# "timeout": 60
|
|
22
|
+
# }
|
|
23
|
+
# ]
|
|
24
|
+
# }
|
|
25
|
+
# ]
|
|
26
|
+
# }
|
|
27
|
+
#
|
|
28
|
+
# Exit codes:
|
|
29
|
+
# 0 = allow completion (all checks passed)
|
|
30
|
+
# 2 = block completion (fix issues first)
|
|
31
|
+
# ═══════════════════════════════════════════════════════════════════════
|
|
32
|
+
|
|
33
|
+
set -euo pipefail
|
|
34
|
+
|
|
35
|
+
# Read hook input (JSON on stdin) — contains session_id, cwd, etc.
|
|
36
|
+
INPUT=$(cat)
|
|
37
|
+
HOOK_CWD=$(echo "$INPUT" | jq -r '.cwd // empty' 2>/dev/null || true)
|
|
38
|
+
if [[ -n "$HOOK_CWD" ]]; then
|
|
39
|
+
cd "$HOOK_CWD"
|
|
40
|
+
fi
|
|
41
|
+
|
|
42
|
+
FAILED=0
|
|
43
|
+
|
|
44
|
+
# Find the project root (walk up from cwd looking for package.json)
|
|
45
|
+
find_project_root() {
|
|
46
|
+
local dir="$PWD"
|
|
47
|
+
while [[ "$dir" != "/" ]]; do
|
|
48
|
+
if [[ -f "$dir/package.json" ]]; then
|
|
49
|
+
echo "$dir"
|
|
50
|
+
return 0
|
|
51
|
+
fi
|
|
52
|
+
dir="$(dirname "$dir")"
|
|
53
|
+
done
|
|
54
|
+
return 1
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
PROJECT_ROOT="$(find_project_root)" || {
|
|
58
|
+
# No package.json found — not a JS/TS project, allow completion
|
|
59
|
+
exit 0
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
cd "$PROJECT_ROOT"
|
|
63
|
+
|
|
64
|
+
# ─── Step 1: Lint changed files ──────────────────────────────────────
|
|
65
|
+
|
|
66
|
+
# Get files changed relative to HEAD (staged + unstaged)
|
|
67
|
+
CHANGED_FILES=$(git diff --name-only HEAD 2>/dev/null || true)
|
|
68
|
+
if [[ -z "$CHANGED_FILES" ]]; then
|
|
69
|
+
# Also check staged files not yet committed
|
|
70
|
+
CHANGED_FILES=$(git diff --cached --name-only 2>/dev/null || true)
|
|
71
|
+
fi
|
|
72
|
+
|
|
73
|
+
# Filter to lintable files (ts, tsx, js, jsx)
|
|
74
|
+
LINT_FILES=$(echo "$CHANGED_FILES" | grep -E '\.(ts|tsx|js|jsx)$' || true)
|
|
75
|
+
|
|
76
|
+
if [[ -n "$LINT_FILES" ]]; then
|
|
77
|
+
echo "Linting $(echo "$LINT_FILES" | wc -l | tr -d ' ') changed file(s)..."
|
|
78
|
+
|
|
79
|
+
# Build file list as arguments (handle spaces in filenames)
|
|
80
|
+
LINT_ARGS=()
|
|
81
|
+
while IFS= read -r file; do
|
|
82
|
+
[[ -f "$file" ]] && LINT_ARGS+=("$file")
|
|
83
|
+
done <<< "$LINT_FILES"
|
|
84
|
+
|
|
85
|
+
if [[ ${#LINT_ARGS[@]} -gt 0 ]]; then
|
|
86
|
+
if command -v pnpm &>/dev/null && [[ -f "pnpm-lock.yaml" ]]; then
|
|
87
|
+
pnpm eslint --no-error-on-unmatched-pattern "${LINT_ARGS[@]}" 2>&1 || {
|
|
88
|
+
echo "::error::Lint errors in changed files."
|
|
89
|
+
FAILED=1
|
|
90
|
+
}
|
|
91
|
+
elif npx --no-install eslint --version &>/dev/null 2>&1; then
|
|
92
|
+
npx eslint --no-error-on-unmatched-pattern "${LINT_ARGS[@]}" 2>&1 || {
|
|
93
|
+
echo "::error::Lint errors in changed files."
|
|
94
|
+
FAILED=1
|
|
95
|
+
}
|
|
96
|
+
else
|
|
97
|
+
echo "ESLint not available — skipping lint check."
|
|
98
|
+
fi
|
|
99
|
+
fi
|
|
100
|
+
else
|
|
101
|
+
echo "No lintable files changed — skipping lint."
|
|
102
|
+
fi
|
|
103
|
+
|
|
104
|
+
# ─── Step 2: Run related tests ───────────────────────────────────────
|
|
105
|
+
|
|
106
|
+
# Find test files related to changed source files
|
|
107
|
+
TEST_FILES=()
|
|
108
|
+
while IFS= read -r file; do
|
|
109
|
+
[[ -z "$file" ]] && continue
|
|
110
|
+
|
|
111
|
+
# Skip if file is itself a test
|
|
112
|
+
if [[ "$file" =~ \.(test|spec)\.(ts|tsx|js|jsx)$ ]]; then
|
|
113
|
+
[[ -f "$file" ]] && TEST_FILES+=("$file")
|
|
114
|
+
continue
|
|
115
|
+
fi
|
|
116
|
+
|
|
117
|
+
# Look for corresponding test file
|
|
118
|
+
base="${file%.*}"
|
|
119
|
+
ext="${file##*.}"
|
|
120
|
+
for pattern in "${base}.test.${ext}" "${base}.spec.${ext}"; do
|
|
121
|
+
[[ -f "$pattern" ]] && TEST_FILES+=("$pattern")
|
|
122
|
+
done
|
|
123
|
+
|
|
124
|
+
# Also check __tests__ directory
|
|
125
|
+
dir="$(dirname "$file")"
|
|
126
|
+
name="$(basename "$file")"
|
|
127
|
+
namebase="${name%.*}"
|
|
128
|
+
for pattern in "${dir}/__tests__/${namebase}.test.${ext}" "${dir}/__tests__/${namebase}.spec.${ext}"; do
|
|
129
|
+
[[ -f "$pattern" ]] && TEST_FILES+=("$pattern")
|
|
130
|
+
done
|
|
131
|
+
done <<< "$CHANGED_FILES"
|
|
132
|
+
|
|
133
|
+
if [[ ${#TEST_FILES[@]} -gt 0 ]]; then
|
|
134
|
+
# Deduplicate
|
|
135
|
+
readarray -t TEST_FILES < <(printf '%s\n' "${TEST_FILES[@]}" | sort -u)
|
|
136
|
+
|
|
137
|
+
echo "Running ${#TEST_FILES[@]} related test file(s)..."
|
|
138
|
+
|
|
139
|
+
if command -v pnpm &>/dev/null && [[ -f "pnpm-lock.yaml" ]]; then
|
|
140
|
+
pnpm vitest run --reporter=verbose "${TEST_FILES[@]}" 2>&1 || {
|
|
141
|
+
echo "::error::Tests failed for changed files."
|
|
142
|
+
FAILED=1
|
|
143
|
+
}
|
|
144
|
+
elif npx --no-install vitest --version &>/dev/null 2>&1; then
|
|
145
|
+
npx vitest run --reporter=verbose "${TEST_FILES[@]}" 2>&1 || {
|
|
146
|
+
echo "::error::Tests failed for changed files."
|
|
147
|
+
FAILED=1
|
|
148
|
+
}
|
|
149
|
+
elif npx --no-install jest --version &>/dev/null 2>&1; then
|
|
150
|
+
npx jest --bail "${TEST_FILES[@]}" 2>&1 || {
|
|
151
|
+
echo "::error::Tests failed for changed files."
|
|
152
|
+
FAILED=1
|
|
153
|
+
}
|
|
154
|
+
else
|
|
155
|
+
echo "No test runner (vitest/jest) available — skipping tests."
|
|
156
|
+
fi
|
|
157
|
+
else
|
|
158
|
+
echo "No related test files found — skipping tests."
|
|
159
|
+
fi
|
|
160
|
+
|
|
161
|
+
# ─── Result ───────────────────────────────────────────────────────────
|
|
162
|
+
|
|
163
|
+
if [[ "$FAILED" -ne 0 ]]; then
|
|
164
|
+
echo ""
|
|
165
|
+
echo "Task completion blocked — fix the issues above first."
|
|
166
|
+
exit 2
|
|
167
|
+
fi
|
|
168
|
+
|
|
169
|
+
echo "All quality checks passed."
|
|
170
|
+
exit 0
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# ═══════════════════════════════════════════════════════════════════════
|
|
3
|
+
# teammate-idle.sh — Quality gate: block idle if typecheck fails
|
|
4
|
+
# ═══════════════════════════════════════════════════════════════════════
|
|
5
|
+
#
|
|
6
|
+
# Runs when a Claude Code teammate goes idle. If there are TypeScript
|
|
7
|
+
# errors, exit code 2 tells Claude to keep working and fix them first.
|
|
8
|
+
#
|
|
9
|
+
# Install:
|
|
10
|
+
# 1. Copy this file to ~/.claude/hooks/teammate-idle.sh
|
|
11
|
+
# 2. chmod +x ~/.claude/hooks/teammate-idle.sh
|
|
12
|
+
# 3. Add to ~/.claude/settings.json:
|
|
13
|
+
# "hooks": {
|
|
14
|
+
# "teammate-idle": {
|
|
15
|
+
# "command": "~/.claude/hooks/teammate-idle.sh",
|
|
16
|
+
# "timeout": 30000
|
|
17
|
+
# }
|
|
18
|
+
# }
|
|
19
|
+
#
|
|
20
|
+
# Exit codes:
|
|
21
|
+
# 0 = allow idle (typecheck passed)
|
|
22
|
+
# 2 = keep working (typecheck failed — fix errors first)
|
|
23
|
+
# ═══════════════════════════════════════════════════════════════════════
|
|
24
|
+
|
|
25
|
+
set -euo pipefail
|
|
26
|
+
|
|
27
|
+
# Find the project root (walk up from cwd looking for package.json)
|
|
28
|
+
find_project_root() {
|
|
29
|
+
local dir="$PWD"
|
|
30
|
+
while [[ "$dir" != "/" ]]; do
|
|
31
|
+
if [[ -f "$dir/package.json" ]]; then
|
|
32
|
+
echo "$dir"
|
|
33
|
+
return 0
|
|
34
|
+
fi
|
|
35
|
+
dir="$(dirname "$dir")"
|
|
36
|
+
done
|
|
37
|
+
return 1
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
PROJECT_ROOT="$(find_project_root)" || {
|
|
41
|
+
# No package.json found — not a TypeScript project, allow idle
|
|
42
|
+
exit 0
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
cd "$PROJECT_ROOT"
|
|
46
|
+
|
|
47
|
+
# Check if this project uses TypeScript
|
|
48
|
+
if [[ ! -f "tsconfig.json" ]]; then
|
|
49
|
+
exit 0
|
|
50
|
+
fi
|
|
51
|
+
|
|
52
|
+
# Run typecheck — try pnpm first, fall back to npx
|
|
53
|
+
if command -v pnpm &>/dev/null && [[ -f "pnpm-lock.yaml" ]]; then
|
|
54
|
+
pnpm typecheck 2>&1 || {
|
|
55
|
+
echo "::error::TypeScript errors found. Fix them before going idle."
|
|
56
|
+
exit 2
|
|
57
|
+
}
|
|
58
|
+
elif command -v npm &>/dev/null; then
|
|
59
|
+
npx tsc --noEmit 2>&1 || {
|
|
60
|
+
echo "::error::TypeScript errors found. Fix them before going idle."
|
|
61
|
+
exit 2
|
|
62
|
+
}
|
|
63
|
+
else
|
|
64
|
+
# No package manager available — skip check, allow idle
|
|
65
|
+
exit 0
|
|
66
|
+
fi
|
|
67
|
+
|
|
68
|
+
exit 0
|
|
@@ -0,0 +1,184 @@
|
|
|
1
|
+
{
|
|
2
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
3
|
+
// Claude Code Settings — Agent Teams + tmux Configuration
|
|
4
|
+
// ═══════════════════════════════════════════════════════════════════════
|
|
5
|
+
//
|
|
6
|
+
// Copy this to ~/.claude/settings.json and customize for your project.
|
|
7
|
+
//
|
|
8
|
+
// NOTE: Standard JSON does not support comments. This file uses JSONC
|
|
9
|
+
// (JSON with Comments) syntax for documentation purposes. Before using
|
|
10
|
+
// as your actual settings.json, either:
|
|
11
|
+
// 1. Use an editor that supports JSONC (VS Code, Zed, etc.)
|
|
12
|
+
// 2. Strip comments: sed '/^\s*\/\//d' settings.json.template > settings.json
|
|
13
|
+
//
|
|
14
|
+
// Docs: https://docs.anthropic.com/en/docs/claude-code/settings
|
|
15
|
+
|
|
16
|
+
// ─── Teammate Mode ────────────────────────────────────────────────────
|
|
17
|
+
// Agent teams auto-detect tmux: when you launch Claude Code inside a
|
|
18
|
+
// tmux session, teammates automatically get their own split panes.
|
|
19
|
+
// No explicit setting needed — just run inside tmux!
|
|
20
|
+
//
|
|
21
|
+
// TIP: Use `new-window` (not `split-window`) when spawning 4+ agents
|
|
22
|
+
// to avoid the tmux send-keys race condition. See KNOWN-ISSUES.md.
|
|
23
|
+
|
|
24
|
+
// ─── Hooks ─────────────────────────────────────────────────────────────
|
|
25
|
+
// Shell commands that run on lifecycle events.
|
|
26
|
+
// See claude-code/hooks/ for the scripts referenced below.
|
|
27
|
+
// Docs: https://code.claude.com/docs/en/hooks-guide
|
|
28
|
+
//
|
|
29
|
+
// Event reference:
|
|
30
|
+
// TeammateIdle — when an agent teammate is about to go idle
|
|
31
|
+
// TaskCompleted — when a task is being marked as completed
|
|
32
|
+
// Notification — when Claude needs your attention
|
|
33
|
+
// Stop — when Claude finishes responding
|
|
34
|
+
// PreCompact — before context window compaction
|
|
35
|
+
// PostToolUse — after a tool call succeeds (e.g., auto-format)
|
|
36
|
+
// SessionStart — when a session begins or resumes
|
|
37
|
+
//
|
|
38
|
+
// Exit codes: 0 = allow, 2 = block action (agent gets feedback to fix)
|
|
39
|
+
"hooks": {
|
|
40
|
+
// Quality gate: block idle if typecheck fails
|
|
41
|
+
"TeammateIdle": [
|
|
42
|
+
{
|
|
43
|
+
"hooks": [
|
|
44
|
+
{
|
|
45
|
+
"type": "command",
|
|
46
|
+
"command": "~/.claude/hooks/teammate-idle.sh",
|
|
47
|
+
"timeout": 30,
|
|
48
|
+
"statusMessage": "Running typecheck before idle..."
|
|
49
|
+
}
|
|
50
|
+
]
|
|
51
|
+
}
|
|
52
|
+
],
|
|
53
|
+
// Quality gate: block task completion if lint/tests fail
|
|
54
|
+
"TaskCompleted": [
|
|
55
|
+
{
|
|
56
|
+
"hooks": [
|
|
57
|
+
{
|
|
58
|
+
"type": "command",
|
|
59
|
+
"command": "~/.claude/hooks/task-completed.sh",
|
|
60
|
+
"timeout": 60,
|
|
61
|
+
"statusMessage": "Running quality checks..."
|
|
62
|
+
}
|
|
63
|
+
]
|
|
64
|
+
}
|
|
65
|
+
],
|
|
66
|
+
// Desktop notification when Claude needs attention
|
|
67
|
+
"Notification": [
|
|
68
|
+
{
|
|
69
|
+
"hooks": [
|
|
70
|
+
{
|
|
71
|
+
"type": "command",
|
|
72
|
+
"command": "~/.claude/hooks/notify-idle.sh",
|
|
73
|
+
"async": true
|
|
74
|
+
}
|
|
75
|
+
]
|
|
76
|
+
}
|
|
77
|
+
],
|
|
78
|
+
// Save context before compaction
|
|
79
|
+
"PreCompact": [
|
|
80
|
+
{
|
|
81
|
+
"matcher": "auto",
|
|
82
|
+
"hooks": [
|
|
83
|
+
{
|
|
84
|
+
"type": "command",
|
|
85
|
+
"command": "~/.claude/hooks/pre-compact-save.sh",
|
|
86
|
+
"statusMessage": "Saving context before compaction..."
|
|
87
|
+
}
|
|
88
|
+
]
|
|
89
|
+
}
|
|
90
|
+
],
|
|
91
|
+
// Auto-format files after Claude edits them
|
|
92
|
+
"PostToolUse": [
|
|
93
|
+
{
|
|
94
|
+
"matcher": "Edit|Write",
|
|
95
|
+
"hooks": [
|
|
96
|
+
{
|
|
97
|
+
"type": "command",
|
|
98
|
+
"command": "jq -r '.tool_input.file_path' | xargs npx prettier --write 2>/dev/null || true"
|
|
99
|
+
}
|
|
100
|
+
]
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
},
|
|
104
|
+
|
|
105
|
+
// ─── Status Line ──────────────────────────────────────────────────────
|
|
106
|
+
// Custom status line shown at the bottom of Claude Code.
|
|
107
|
+
// "type": "command" — runs a shell script and displays its stdout
|
|
108
|
+
// "type": "static" — shows fixed text
|
|
109
|
+
//
|
|
110
|
+
// Uncomment to enable:
|
|
111
|
+
// "statusLine": {
|
|
112
|
+
// "type": "command",
|
|
113
|
+
// "command": "echo \"$(git branch --show-current) | $(date +%H:%M)\""
|
|
114
|
+
// },
|
|
115
|
+
|
|
116
|
+
// ─── Plugins ──────────────────────────────────────────────────────────
|
|
117
|
+
// Enable/disable official and community plugins.
|
|
118
|
+
// Browse available plugins: claude plugins list
|
|
119
|
+
"enabledPlugins": {
|
|
120
|
+
// Code quality & review
|
|
121
|
+
"typescript-lsp@claude-plugins-official": true,
|
|
122
|
+
"feature-dev@claude-plugins-official": true,
|
|
123
|
+
"code-review@claude-plugins-official": true,
|
|
124
|
+
"pr-review-toolkit@claude-plugins-official": true,
|
|
125
|
+
"commit-commands@claude-plugins-official": true,
|
|
126
|
+
|
|
127
|
+
// Integrations — enable the ones you use
|
|
128
|
+
"github@claude-plugins-official": true,
|
|
129
|
+
"linear@claude-plugins-official": false,
|
|
130
|
+
"firebase@claude-plugins-official": false,
|
|
131
|
+
"slack@claude-plugins-official": false,
|
|
132
|
+
|
|
133
|
+
// Documentation & context
|
|
134
|
+
"context7@claude-plugins-official": true,
|
|
135
|
+
"frontend-design@claude-plugins-official": false,
|
|
136
|
+
|
|
137
|
+
// Workflow & output styles
|
|
138
|
+
"hookify@claude-plugins-official": true,
|
|
139
|
+
"learning-output-style@claude-plugins-official": false,
|
|
140
|
+
"explanatory-output-style@claude-plugins-official": false
|
|
141
|
+
},
|
|
142
|
+
|
|
143
|
+
// ─── Extended Thinking ────────────────────────────────────────────────
|
|
144
|
+
// When true, Claude thinks through complex problems step-by-step.
|
|
145
|
+
// Recommended for agent teams doing multi-file coordinated work.
|
|
146
|
+
"alwaysThinkingEnabled": true,
|
|
147
|
+
|
|
148
|
+
// ─── Environment Variables ────────────────────────────────────────────
|
|
149
|
+
// These env vars configure Claude Code's runtime behavior.
|
|
150
|
+
"env": {
|
|
151
|
+
// REQUIRED: Enable agent teams feature
|
|
152
|
+
"CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS": "1",
|
|
153
|
+
|
|
154
|
+
// Performance: max concurrent tool calls per agent (default: 3)
|
|
155
|
+
// Higher = faster parallel work, but more API usage
|
|
156
|
+
"CLAUDE_CODE_MAX_TOOL_USE_CONCURRENCY": "5",
|
|
157
|
+
|
|
158
|
+
// Auto-compact conversation when context usage hits this % (default: 80)
|
|
159
|
+
// Lower = more aggressive compaction, less risk of hitting limits
|
|
160
|
+
"CLAUDE_CODE_AUTOCOMPACT_PCT_OVERRIDE": "70",
|
|
161
|
+
|
|
162
|
+
// Use haiku for subagent tasks (cheaper + faster for simple lookups)
|
|
163
|
+
// Remove to use the same model as the parent agent
|
|
164
|
+
"CLAUDE_CODE_SUBAGENT_MODEL": "haiku",
|
|
165
|
+
|
|
166
|
+
// Include hidden files (dotfiles) in glob searches
|
|
167
|
+
"CLAUDE_CODE_GLOB_HIDDEN": "1",
|
|
168
|
+
|
|
169
|
+
// Keep bash working directory consistent across tool calls
|
|
170
|
+
"CLAUDE_CODE_BASH_MAINTAIN_PROJECT_WORKING_DIR": "1",
|
|
171
|
+
|
|
172
|
+
// Show tool use summaries in output (helpful for debugging agent behavior)
|
|
173
|
+
"CLAUDE_CODE_EMIT_TOOL_USE_SUMMARIES": "1",
|
|
174
|
+
|
|
175
|
+
// Show teammate names in messages (makes multi-agent output readable)
|
|
176
|
+
"CLAUDE_CODE_TST_NAMES_IN_MESSAGES": "1",
|
|
177
|
+
|
|
178
|
+
// Flush output eagerly (better for streaming, reduces perceived latency)
|
|
179
|
+
"CLAUDE_CODE_EAGER_FLUSH": "1",
|
|
180
|
+
|
|
181
|
+
// Enable prompt suggestions after idle
|
|
182
|
+
"CLAUDE_CODE_ENABLE_PROMPT_SUGGESTION": "1"
|
|
183
|
+
}
|
|
184
|
+
}
|