crewforth 0.0.1 → 3.0.0-rc.2
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 +3805 -0
- package/LICENSE +21 -0
- package/README.md +37 -7
- package/VERSION +1 -0
- package/adopt.sh +1739 -0
- package/bin/cli.js +497 -0
- package/kit/AGENT_TEMPLATE.md +92 -0
- package/kit/CLAUDE.md +228 -0
- package/kit/README.md +107 -0
- package/kit/agents/crew-backend-expert.md +95 -0
- package/kit/agents/crew-commit-agent.md +96 -0
- package/kit/agents/crew-database-expert.md +84 -0
- package/kit/agents/crew-devops-expert.md +82 -0
- package/kit/agents/crew-frontend-expert.md +85 -0
- package/kit/agents/crew-performance-expert.md +78 -0
- package/kit/agents/crew-planner.md +60 -0
- package/kit/agents/crew-privacy-agent.md +71 -0
- package/kit/agents/crew-review-agent.md +95 -0
- package/kit/agents/crew-security-expert.md +73 -0
- package/kit/agents/crew-session-manager.md +77 -0
- package/kit/agents/crew-test-expert.md +64 -0
- package/kit/eval/doctor.sh +639 -0
- package/kit/eval/gate-report.sh +137 -0
- package/kit/eval/golden-holdout.txt +53 -0
- package/kit/eval/golden-routing.txt +199 -0
- package/kit/eval/lib/crew-env.sh +14 -0
- package/kit/eval/lib/settings-json.awk +154 -0
- package/kit/eval/lib/skill-listing.awk +43 -0
- package/kit/eval/lib/star.sh +70 -0
- package/kit/eval/parser-conformance.sh +612 -0
- package/kit/eval/preflight.sh +186 -0
- package/kit/eval/routing-eval.sh +209 -0
- package/kit/eval/scan-skill.sh +162 -0
- package/kit/eval/smoke-test.sh +6736 -0
- package/kit/eval/update-guard.sh +81 -0
- package/kit/eval/utilization.sh +130 -0
- package/kit/hooks/board-sync.sh +106 -0
- package/kit/hooks/board.sh +962 -0
- package/kit/hooks/commit-msg +49 -0
- package/kit/hooks/context-usage.sh +353 -0
- package/kit/hooks/floor-blocklist.txt +124 -0
- package/kit/hooks/guard-bash.sh +1433 -0
- package/kit/hooks/guard-commit-scan.sh +511 -0
- package/kit/hooks/guard-write.sh +513 -0
- package/kit/hooks/pre-commit +577 -0
- package/kit/hooks/route-hint.sh +185 -0
- package/kit/hooks/secret-blocklist.txt +82 -0
- package/kit/hooks/session-guard.sh +149 -0
- package/kit/hooks/session-rehydrate.sh +41 -0
- package/kit/hooks/session-stats.sh +252 -0
- package/kit/hooks/session-update-check.sh +236 -0
- package/kit/hooks/skill-trust.sh +163 -0
- package/kit/hooks/trace-blocklist.txt +53 -0
- package/kit/settings.json +132 -0
- package/kit/skills/a11y/SKILL.md +44 -0
- package/kit/skills/adr/SKILL.md +74 -0
- package/kit/skills/api-design/SKILL.md +50 -0
- package/kit/skills/automode-policy/SKILL.md +91 -0
- package/kit/skills/automode-policy/references/policy.json +13 -0
- package/kit/skills/automode-policy/scripts/apply.sh +84 -0
- package/kit/skills/automode-policy/scripts/check.sh +102 -0
- package/kit/skills/backend-architecture/SKILL.md +103 -0
- package/kit/skills/brainstorm/SKILL.md +47 -0
- package/kit/skills/ci-pipeline/SKILL.md +38 -0
- package/kit/skills/commit-message/SKILL.md +82 -0
- package/kit/skills/commit-message/references/examples.md +8 -0
- package/kit/skills/confidence-check/SKILL.md +86 -0
- package/kit/skills/crew-board/SKILL.md +55 -0
- package/kit/skills/crew-brainstorm/SKILL.md +19 -0
- package/kit/skills/crew-code-review/SKILL.md +151 -0
- package/kit/skills/crew-code-review/references/history-search.md +19 -0
- package/kit/skills/crew-code-review/references/panel-mode.md +32 -0
- package/kit/skills/crew-code-review/references/receiving.md +23 -0
- package/kit/skills/crew-doctor/SKILL.md +46 -0
- package/kit/skills/crew-gates/SKILL.md +25 -0
- package/kit/skills/crew-handoff/SKILL.md +14 -0
- package/kit/skills/crew-plan/SKILL.md +18 -0
- package/kit/skills/crew-review/SKILL.md +26 -0
- package/kit/skills/crew-ship/SKILL.md +19 -0
- package/kit/skills/crew-skill/SKILL.md +62 -0
- package/kit/skills/crew-studio/SKILL.md +129 -0
- package/kit/skills/crew-update/SKILL.md +43 -0
- package/kit/skills/db-migration/SKILL.md +120 -0
- package/kit/skills/db-migration/references/backup-restore.md +33 -0
- package/kit/skills/db-migration/references/raw-sql.md +30 -0
- package/kit/skills/db-migration/references/tool-matrix.md +35 -0
- package/kit/skills/dependency-audit/SKILL.md +61 -0
- package/kit/skills/dependency-upgrade/SKILL.md +82 -0
- package/kit/skills/deploy/SKILL.md +100 -0
- package/kit/skills/deploy/references/proxy-ssl.md +56 -0
- package/kit/skills/deploy/references/rollback.md +18 -0
- package/kit/skills/deploy/references/scripts.md +9 -0
- package/kit/skills/deploy/references/topology-a.md +128 -0
- package/kit/skills/deploy/references/topology-b.md +44 -0
- package/kit/skills/docs-writer/SKILL.md +53 -0
- package/kit/skills/docs-writer/references/coauthoring.md +34 -0
- package/kit/skills/eval-grader/SKILL.md +75 -0
- package/kit/skills/eval-grader/references/method.md +51 -0
- package/kit/skills/frontend/SKILL.md +56 -0
- package/kit/skills/frontend/references/verify-contract.md +41 -0
- package/kit/skills/frontend-design/SKILL.md +71 -0
- package/kit/skills/frontend-design/references/design-review.md +34 -0
- package/kit/skills/frontend-rn-expo/SKILL.md +25 -0
- package/kit/skills/handoff/SKILL.md +71 -0
- package/kit/skills/handoff/references/cold-reader.md +51 -0
- package/kit/skills/i18n-integrity/SKILL.md +31 -0
- package/kit/skills/incident-runbook/SKILL.md +40 -0
- package/kit/skills/incident-runbook/references/postmortem.md +16 -0
- package/kit/skills/iterate/SKILL.md +40 -0
- package/kit/skills/mcp-builder/SKILL.md +54 -0
- package/kit/skills/mcp-builder/references/building.md +71 -0
- package/kit/skills/observability/SKILL.md +50 -0
- package/kit/skills/performance/SKILL.md +38 -0
- package/kit/skills/performance/references/bottlenecks.md +10 -0
- package/kit/skills/privacy-compliance/SKILL.md +75 -0
- package/kit/skills/red-team/SKILL.md +48 -0
- package/kit/skills/reflect/SKILL.md +57 -0
- package/kit/skills/release/SKILL.md +44 -0
- package/kit/skills/security-scan/SKILL.md +99 -0
- package/kit/skills/security-scan/references/ai-agents.md +107 -0
- package/kit/skills/security-scan/references/fronts.md +99 -0
- package/kit/skills/security-scan/references/prompting.md +23 -0
- package/kit/skills/security-scan/references/reporting.md +75 -0
- package/kit/skills/security-scan/references/verify.md +131 -0
- package/kit/skills/sonarqube-check/SKILL.md +108 -0
- package/kit/skills/sonarqube-check/references/local-engines.md +32 -0
- package/kit/skills/sonarqube-check/references/stand-up-server.md +25 -0
- package/kit/skills/spec-planning/SKILL.md +92 -0
- package/kit/skills/systematic-debugging/SKILL.md +57 -0
- package/kit/skills/systematic-debugging/references/techniques.md +44 -0
- package/kit/skills/teamboard/SKILL.md +88 -0
- package/kit/skills/teamboard/references/setup.md +37 -0
- package/kit/skills/testing/SKILL.md +49 -0
- package/kit/skills/testing/references/flaky-triage.md +51 -0
- package/kit/skills/threat-model/SKILL.md +57 -0
- package/kit/skills/threat-model/references/interview.md +51 -0
- package/kit/skills/threat-model/references/schema.md +42 -0
- package/kit/skills/token-budget/SKILL.md +51 -0
- package/kit/skills/trace-scan/SKILL.md +43 -0
- package/kit/skills/worktree/SKILL.md +40 -0
- package/kit/studio/README.md +266 -0
- package/kit/studio/ensure-node.sh +471 -0
- package/kit/studio/package.json +14 -0
- package/kit/studio/server/hooks/studio-gate.sh +169 -0
- package/kit/studio/server/index.js +902 -0
- package/kit/studio/server/lib/crew-env.js +11 -0
- package/kit/studio/server/lib/fleet.js +146 -0
- package/kit/studio/server/lib/graph.js +533 -0
- package/kit/studio/server/lib/instance.js +135 -0
- package/kit/studio/server/lib/kit-telemetry.js +182 -0
- package/kit/studio/server/lib/kit.js +158 -0
- package/kit/studio/server/lib/palette.js +118 -0
- package/kit/studio/server/lib/peers.js +72 -0
- package/kit/studio/server/lib/permissions.js +150 -0
- package/kit/studio/server/lib/projects.js +294 -0
- package/kit/studio/server/lib/roster.js +191 -0
- package/kit/studio/server/lib/session.js +309 -0
- package/kit/studio/server/lib/terminal.js +91 -0
- package/kit/studio/server/lib/transcript.js +100 -0
- package/kit/studio/web/app.js +1098 -0
- package/kit/studio/web/canvas.js +1179 -0
- package/kit/studio/web/chat.js +543 -0
- package/kit/studio/web/index.html +86 -0
- package/kit/studio/web/md.js +110 -0
- package/kit/studio/web/storage-migrate.js +23 -0
- package/kit/studio/web/style.css +1453 -0
- package/package.json +46 -8
- package/start.sh +764 -0
- package/bin/crewforth.js +0 -10
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: threat-model
|
|
3
|
+
description: |
|
|
4
|
+
Scope a security audit BEFORE scanning, to cut false positives: map assets, entry points, trust boundaries
|
|
5
|
+
and 5-8 domain-specific attack classes into a parseable THREAT_MODEL.md. A threat survives a patch; a
|
|
6
|
+
vulnerability is only evidence for one. Feeds security-scan.
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Threat Model
|
|
10
|
+
|
|
11
|
+
<!-- routing-eval reads the next line; why it sits in the body: AGENT_TEMPLATE.md -->
|
|
12
|
+
Trigger phrases: "threat model", "attack surface", "scope the audit", "trust boundary"
|
|
13
|
+
|
|
14
|
+
Scope first, scan second. A security scan with no map produces noise; a threat model tells the scanner (and
|
|
15
|
+
`security-scan`) **where to look and what matters** — the single biggest lever on false positives.
|
|
16
|
+
|
|
17
|
+
The one idea to keep — the litmus test: **if patching one line of code makes an entry disappear, it was a
|
|
18
|
+
vulnerability, not a threat.** Threats survive patching (they name *what an attacker wants and the surface they
|
|
19
|
+
arrive through*); a vulnerability is only **evidence** that raises a threat's likelihood.
|
|
20
|
+
|
|
21
|
+
> **Crewforth adaptation (local, .claude/):** `crew-security-expert` runs this to scope before `security-scan`. Output
|
|
22
|
+
> `docs/THREAT_MODEL.md` is internal (§4.3). Stack-agnostic. §4 Prohibitions apply.
|
|
23
|
+
|
|
24
|
+
## When
|
|
25
|
+
- Before a first `security-scan` of a system, or when scan output is noisy / unscoped.
|
|
26
|
+
- After a significant new surface — a new API, a new integration, a new trust boundary.
|
|
27
|
+
|
|
28
|
+
## Two modes
|
|
29
|
+
- **interview** — the owner is available: ask the four questions below, one at a time.
|
|
30
|
+
- **bootstrap** — no owner: derive the model from code + past advisories, then flag what only the owner can confirm.
|
|
31
|
+
|
|
32
|
+
## Method — the four questions (Shostack), one at a time
|
|
33
|
+
Never dump a questionnaire; ask, capture into the schema, move on. Mirror the user's language.
|
|
34
|
+
1. **What are we building?** system context, assets worth protecting, entry points and trust boundaries.
|
|
35
|
+
2. **What can go wrong?** open-ended first; then, per entry point, fall back to **STRIDE**; derive **5-8
|
|
36
|
+
domain-specific attack classes** at the right granularity — "IDOR on dataset rows", "integer overflow on
|
|
37
|
+
length fields" — not "web vulnerabilities".
|
|
38
|
+
3. **What are we doing about it?** impact · residual likelihood · status · controls per threat; "accept the
|
|
39
|
+
risk, with a written reason" (`risk_accepted`) is a valid answer.
|
|
40
|
+
4. **Did we do a good job?** read the ranked table back; coverage-check that every entry point appears.
|
|
41
|
+
|
|
42
|
+
Tag every fact **`[Code-verified]`** or **`[Owner-states]`**; each owner claim that moves a score becomes an
|
|
43
|
+
open question with a "Verify by:" note.
|
|
44
|
+
|
|
45
|
+
## Output
|
|
46
|
+
Write `docs/THREAT_MODEL.md` to the parseable contract in **`references/schema.md`** (fixed sections; a threats
|
|
47
|
+
table with enumerated columns; residual-likelihood scoring; class-level mitigations). The interview flow, the
|
|
48
|
+
STRIDE table, and the bootstrap steps live in **`references/interview.md`**.
|
|
49
|
+
|
|
50
|
+
## Principles
|
|
51
|
+
- **Evidence raises likelihood; it is not the threat.** Score the *residual* likelihood after current controls;
|
|
52
|
+
a stated-but-unverified control does not lower it — it becomes an open question.
|
|
53
|
+
- **Prefer a control that survives the next bug** over a patch for the last one (class-level mitigations).
|
|
54
|
+
- **Feeds forward:** `security-scan` reads `THREAT_MODEL.md` to scope its fronts and bias severity.
|
|
55
|
+
|
|
56
|
+
## DoD
|
|
57
|
+
- `docs/THREAT_MODEL.md` exists; every entry point covered; each threat scored; provenance tagged; scope handed to `security-scan`.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Threat-model interview + bootstrap
|
|
2
|
+
|
|
3
|
+
## Interview mode — Shostack four questions, ONE at a time
|
|
4
|
+
Never present a questionnaire. Ask, capture into the schema, move on. Mirror the user's language.
|
|
5
|
+
|
|
6
|
+
### 1 · What are we building?
|
|
7
|
+
System context, assets, entry points, trust boundaries. Draw the boundary explicitly: *"untrusted input crosses
|
|
8
|
+
into trusted code HERE."* List every entry point — this becomes section 3 and the coverage checklist.
|
|
9
|
+
|
|
10
|
+
### 2 · What can go wrong?
|
|
11
|
+
Open-ended first ("what would hurt most if it broke?"). Then, per entry point with nothing yet, walk **STRIDE**:
|
|
12
|
+
|
|
13
|
+
| STRIDE | asks |
|
|
14
|
+
|---|---|
|
|
15
|
+
| **S**poofing | can someone pretend to be another principal? |
|
|
16
|
+
| **T**ampering | can data / requests be modified in transit or at rest? |
|
|
17
|
+
| **R**epudiation | can an action be denied, or does it go unlogged? |
|
|
18
|
+
| **I**nformation disclosure | can data leak across a trust boundary? |
|
|
19
|
+
| **D**enial of service | can availability be exhausted (beyond plain volumetric floods)? |
|
|
20
|
+
| **E**levation of privilege | can a lower role reach a higher-privilege action? |
|
|
21
|
+
|
|
22
|
+
Derive **5-8 domain-specific attack classes** at the right granularity — "IDOR on dataset rows", "integer
|
|
23
|
+
overflow on length fields", "tenant bleed via a missing scope check" — **not** "web vulnerabilities". Granularity
|
|
24
|
+
test: a class should name *a surface + a mechanism*, so the scanner knows exactly what to grep for.
|
|
25
|
+
|
|
26
|
+
### 3 · What are we doing about it?
|
|
27
|
+
For each threat: `impact`, residual `likelihood`, `status`, `controls`. "Accept the risk, with a written reason"
|
|
28
|
+
(`risk_accepted`) is a legitimate status — record the reason verbatim in section 6.
|
|
29
|
+
|
|
30
|
+
### 4 · Did we do a good job?
|
|
31
|
+
Read the ranked table back to the owner. **Coverage check:** every entry point from Q1 appears in ≥1 threat's
|
|
32
|
+
`surface`. Add anything missing before you emit.
|
|
33
|
+
|
|
34
|
+
**Provenance discipline:** tag every fact **`[Code-verified]`** (you read it) or **`[Owner-states]`** (they said
|
|
35
|
+
it). Every owner claim that changes a score becomes an open question in section 6 with **"Verify by: <how>."**
|
|
36
|
+
|
|
37
|
+
## Bootstrap mode — no owner available
|
|
38
|
+
1. **Map** (in parallel where the codebase is large): docs, the entry-point surface, infra / IAM, the assets,
|
|
39
|
+
and past advisories — `git log --all -i --grep=<stack keywords>`, `gh api repos/<o>/<r>/security-advisories`.
|
|
40
|
+
2. **Generalize** each past vulnerability into a *threat*: cluster by `(entry point, bug class, asset reached)`.
|
|
41
|
+
Run a **variant scan** — grep for siblings of each vulnerable pattern — to estimate how much of the surface
|
|
42
|
+
shares it (more siblings → higher likelihood; sibling locations become **scan leads, not evidence**).
|
|
43
|
+
3. **Gap-fill with STRIDE** for every entry point that still has no threat ("past vulnerabilities are biased
|
|
44
|
+
toward what has already been found"). Invariant: the final table must contain **≥1 threat with empty
|
|
45
|
+
evidence**, or the gap-fill step did not actually run.
|
|
46
|
+
4. **Flag** every scored owner-would-know claim as an open question, so a later 10-minute interview closes it.
|
|
47
|
+
|
|
48
|
+
## Handing scope to security-scan
|
|
49
|
+
`security-scan` reads `docs/THREAT_MODEL.md`: section 3 (entry points) and section 5 (attack classes) become its
|
|
50
|
+
focus areas; the threat `impact`/`likelihood` bias which findings it treats as high severity. No threat model →
|
|
51
|
+
`security-scan` falls back to its own Front 0 discovery.
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
# THREAT_MODEL.md — parseable contract
|
|
2
|
+
|
|
3
|
+
A **map, not a bug list.** Fixed sections in this order so `security-scan` and a reviewer can parse it.
|
|
4
|
+
|
|
5
|
+
## Sections
|
|
6
|
+
1. **System** — one paragraph: what it does, who uses it, where it runs.
|
|
7
|
+
2. **Assets** — what is worth protecting (data · money · availability · reputation/trust), each with a *why*.
|
|
8
|
+
3. **Entry points & trust boundaries** — every place untrusted input crosses into more-trusted code
|
|
9
|
+
(route · API · form · file upload · CLI arg · queue message · webhook · IAM/role boundary). This is the surface.
|
|
10
|
+
4. **Threats** — the ranked table (below).
|
|
11
|
+
5. **Attack classes** — the 5-8 domain-specific classes derived, each mapped to the entry points it applies to.
|
|
12
|
+
6. **Open questions** — owner claims still to verify, each with a "Verify by: <how>".
|
|
13
|
+
7. **Mitigations** — class-level controls (table below).
|
|
14
|
+
8. **Coverage** — assert every section-3 entry point appears in ≥1 threat's `surface`.
|
|
15
|
+
|
|
16
|
+
## Threats table
|
|
17
|
+
Pipe-separated columns:
|
|
18
|
+
|
|
19
|
+
`id | threat | actor | surface | asset | impact | likelihood | status | controls | evidence`
|
|
20
|
+
|
|
21
|
+
Enumerated value sets (keep parseable):
|
|
22
|
+
- **actor** ∈ `external-anon · external-auth · insider · supply-chain · operator · automated · physical`
|
|
23
|
+
- **impact** ∈ `low · moderate · high · severe · existential`
|
|
24
|
+
- **likelihood** ∈ `very_rare · rare · possible · likely · almost_certain`
|
|
25
|
+
- **status** ∈ `unmitigated · partial · mitigated · risk_accepted`
|
|
26
|
+
|
|
27
|
+
Rules:
|
|
28
|
+
- Sort by `(impact, likelihood)` descending — worst first.
|
|
29
|
+
- **`evidence` raises `likelihood`; it is not the threat.** A row may have empty evidence (a threat with no
|
|
30
|
+
found vulnerability yet is still real). Score the **residual** likelihood *after* current `controls`.
|
|
31
|
+
- `risk_accepted` requires a reason recorded verbatim in section 6.
|
|
32
|
+
|
|
33
|
+
## Scoring guide
|
|
34
|
+
| axis | how to score |
|
|
35
|
+
|---|---|
|
|
36
|
+
| **impact** | realistic worst case for *this* asset. `low`=cosmetic · `moderate`=recoverable loss · `high`=serious breach/outage · `severe`=major data/financial loss · `existential`=business-ending. |
|
|
37
|
+
| **likelihood** | *after* current controls (residual). A control the owner *states* but that is not code-verified does **not** lower it — record it as an open question instead. |
|
|
38
|
+
|
|
39
|
+
## Mitigations table
|
|
40
|
+
`mitigation | threat_ids | closes_class (yes/no) | effort`
|
|
41
|
+
|
|
42
|
+
Prefer a control that **closes a whole class** (`closes_class = yes`) over a one-line patch for the last bug.
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: token-budget
|
|
3
|
+
description: |
|
|
4
|
+
Context/token discipline: subagent isolation, output = summary, move-to-file, delegation threshold, lean skills.
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Token & Context Discipline
|
|
8
|
+
|
|
9
|
+
<!-- routing-eval reads the next line; why it sits in the body: AGENT_TEMPLATE.md -->
|
|
10
|
+
Trigger phrases: "token budget", "token cost", "context window", "context management", "context is full", "clear context", "running out of context"
|
|
11
|
+
|
|
12
|
+
A subagent exists for context management: it runs in its own window and returns **only its summary**
|
|
13
|
+
to the main thread — intermediate noise (file reads, searches, logs) never enters the main context.
|
|
14
|
+
|
|
15
|
+
**Warning — measured, not guessed.** Each subagent re-pays its **full context from scratch**: in a real
|
|
16
|
+
transcript the first turn was `cache_read=0`, every token `cache_creation` — nothing is shared with the main
|
|
17
|
+
thread's cache. A **no-op** subagent (task = "reply DONE") already cost **~10k tokens with restricted tools and
|
|
18
|
+
~16k with full tool access**; that floor is base system prompt + tool schemas, paid fresh every time. Of the
|
|
19
|
+
always-on material only the **skill listing (~2.5–3k tokens) is inherited** by a subagent — the discipline
|
|
20
|
+
(`DISCIPLINE.md`/CLAUDE.md) and the agent descriptions are **not** injected into it. So a delegation is worth it
|
|
21
|
+
for **isolation / parallelism / a clean window**, or when the isolated work would otherwise cost the main thread
|
|
22
|
+
**more than that ~10–16k floor** — never by default.
|
|
23
|
+
|
|
24
|
+
## Rules
|
|
25
|
+
1. **Output = summary.** The agent returns a short, structured summary to the main thread; it does **not** return raw logs / file dumps / long code.
|
|
26
|
+
2. **Move to a file.** Heavy output (a plan, scan report, inventory) is written to `docs/*.md`; a **summary + pointer** comes back. (local, in gitignore)
|
|
27
|
+
3. **Delegation threshold.** Noisy/heavy work (reading many files, scanning, research) → subagent. A single tool-call / small work → **main thread**. Concretely: if the isolated work won't save the main thread more than the **~10–16k fresh-context floor** a subagent costs, keep it on the main thread — delegate for isolation, not to shave a few reads.
|
|
28
|
+
4. **A RESUMED agent re-pays its GROWN context, not its floor.** Continuing an agent that already ran keeps its
|
|
29
|
+
whole history, so the next turn starts from wherever it got to. Measured across four turns of one agent:
|
|
30
|
+
167k → 187k → 207k → **211k tokens**, and that last turn was a single word of seed text. The floor in rule 3
|
|
31
|
+
is the cost of a FRESH agent; a long-running one passes it many times over. Continue it for the context it
|
|
32
|
+
holds — a running verification, a migration it is mid-way through — and open a fresh one, or stay on the main
|
|
33
|
+
thread, for a small correction that does not need any of that history.
|
|
34
|
+
5. **Built-in agents are on this budget too.** `Explore`, `general-purpose` and the rest cost exactly what a Crewforth
|
|
35
|
+
agent costs; one repo-mapping `Explore` measured 520 s and ~150k tokens. Crewforth owns no search agent, so
|
|
36
|
+
reaching for a built-in is correct — just size the request the way rule 3 sizes any other delegation.
|
|
37
|
+
6. **Least tooling.** An agent holds only the tools it needs; extras accidentally pollute the context + burn the limit.
|
|
38
|
+
7. **Lean SKILL.md.** Skills load into the main context; heavy reference goes to a separate file, only when needed.
|
|
39
|
+
8. **Targeted reading.** Instead of reading a whole file, pinpoint with Grep/Glob.
|
|
40
|
+
9. **Measure fill, don't guess.** `bash .claude/hooks/context-usage.sh` reads it from the transcript; crew-session-manager recommends continue or handoff+clear; the user runs `/clear` at a phase boundary.
|
|
41
|
+
10. **Bound what a command hands back.** All of the rules above govern the context's own footprint; none of them
|
|
42
|
+
govern what a single `Bash` call dumps into it. A `find` over a monorepo, an unfiltered log, a full test run —
|
|
43
|
+
each returns everything to the main thread whether or not any of it is read. Ask for the answer, not the
|
|
44
|
+
corpus: `grep -c` over `grep`, `| tail -20` over the whole file, `--quiet`/`--porcelain` where the tool has
|
|
45
|
+
one, and a redirect to a file plus a pointer when the output is genuinely needed later (rule 2).
|
|
46
|
+
11. **Cut what nothing reaches — with evidence, not a hunch.** Every installed skill spends its name and
|
|
47
|
+
description in EVERY session, forever. `bash .claude/eval/utilization.sh` reports which skills actually fired
|
|
48
|
+
in this project's transcripts and how many bytes the cold ones cost, which is the list `doctor.sh` §4a's
|
|
49
|
+
`skillOverrides: name-only` advice needs and never had. Read it as evidence, not a verdict: a skill that only
|
|
50
|
+
fires during an incident is doing its job by existing. `--all-projects` widens the scope; by default it reads
|
|
51
|
+
this project only and prints names and counts, never paths or content.
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: trace-scan
|
|
3
|
+
description: |
|
|
4
|
+
Trace scan (§4.1/§4.2): before a commit, scans the staged changes and the message for AI traces (co-author
|
|
5
|
+
trailers, footers, robot emoji, tool names) and vendor template names. The git hooks apply it automatically.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Trace Scan (trace-scan)
|
|
9
|
+
|
|
10
|
+
<!-- routing-eval reads the next line; why it sits in the body: AGENT_TEMPLATE.md -->
|
|
11
|
+
Trigger phrases: "scan traces", "trace scan", "AI trace", "check vendor name", "pre-commit audit"
|
|
12
|
+
|
|
13
|
+
Purpose: to bind §4.1/§4.2 to a *gate* rather than to *memory*. If the rule lives only in text,
|
|
14
|
+
a trace leaks sooner or later; this skill + the hooks stop the leak at commit time.
|
|
15
|
+
|
|
16
|
+
## When
|
|
17
|
+
- Before every commit (automatic: `pre-commit` + `commit-msg` hooks).
|
|
18
|
+
- Before crew-commit-agent proposes a message (manual verification).
|
|
19
|
+
|
|
20
|
+
## How
|
|
21
|
+
Pattern list: `./.claude/hooks/trace-blocklist.txt` (grep -iE, one pattern per line).
|
|
22
|
+
- **Defaults are high-hit:** co-author trailers, auto-generation footers, robot emoji, and AI-assistant/tool
|
|
23
|
+
brand names. Standalone words that occur too often (model/assistant) are DELIBERATELY excluded. See trace-blocklist.txt for the exact list.
|
|
24
|
+
- **Vendor name is project-specific:** ADD the name of the third-party template in use to the list (§4.2).
|
|
25
|
+
|
|
26
|
+
Manual scan (a quick look without the hook):
|
|
27
|
+
```bash
|
|
28
|
+
git diff --cached --unified=0 | grep -E '^\+' | grep -Ev '^\+\+\+' \
|
|
29
|
+
| grep -iEf .claude/hooks/trace-blocklist.txt
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## Hook setup
|
|
33
|
+
`start.sh` sets `git config core.hooksPath .claude/hooks` (if there is a git repo). The hooks live
|
|
34
|
+
under `.claude` → they are in gitignore and stay local (§4.3). To do it later for a repo:
|
|
35
|
+
```bash
|
|
36
|
+
git config core.hooksPath .claude/hooks
|
|
37
|
+
chmod +x .claude/hooks/pre-commit .claude/hooks/commit-msg
|
|
38
|
+
```
|
|
39
|
+
|
|
40
|
+
## Rules
|
|
41
|
+
- If there is a finding, the commit STOPS; the phrase is removed and the real rationale is written in the project's commit language (CLAUDE.md).
|
|
42
|
+
- Skipping with `--no-verify` only on an EXPLICIT request (§4.5); the hook is not skipped silently.
|
|
43
|
+
- On a false positive, narrow/remove the pattern — the list is set up by the project owner.
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: worktree
|
|
3
|
+
description: |
|
|
4
|
+
Isolate risky or parallel file-mutating work in a git worktree so the main tree's uncommitted changes are never
|
|
5
|
+
clobbered. For fan-out agents that edit files, throwaway experiments, or any change you may want to discard cleanly.
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Worktree Isolation
|
|
9
|
+
|
|
10
|
+
<!-- routing-eval reads the next line; why it sits in the body: AGENT_TEMPLATE.md -->
|
|
11
|
+
Trigger phrases: "worktree", "git worktree", "isolate the changes", "sandbox this work", "parallel file edits"
|
|
12
|
+
|
|
13
|
+
One rule: **never let risky or parallel work run on top of uncommitted changes in the shared tree.** A git worktree
|
|
14
|
+
gives a second working copy of the same repo on its own branch — you experiment or fan out agents there, and the main
|
|
15
|
+
tree (with your in-progress edits) is physically untouched. Throw the worktree away and nothing you cared about is lost.
|
|
16
|
+
|
|
17
|
+
> **Crewforth adaptation (local, .claude/):** this exists because verification/fan-out subagents have discarded a session's
|
|
18
|
+
> **uncommitted** work by running a destructive git command over the shared tree. Those commands (`reset --hard`,
|
|
19
|
+
> `clean -f`, `checkout -- .`, `restore`) are §4.5-gated — but the real fix is to not put them near unsaved work.
|
|
20
|
+
> The Agent tool's `isolation: "worktree"` does this automatically for parallel mutating agents; reach for it there.
|
|
21
|
+
|
|
22
|
+
## When to isolate
|
|
23
|
+
- **Fan-out that writes files** — two+ agents editing the same repo in parallel would collide; give each its own worktree.
|
|
24
|
+
- **A throwaway experiment** — you might keep it or bin it; a worktree makes "bin it" a one-liner, not a `reset --hard`.
|
|
25
|
+
- **A dirty tree you can't commit yet** — you have unsaved work but need to try something risky; isolate rather than stash-and-pray.
|
|
26
|
+
- Skip it for a single, in-place edit on a clean tree — a worktree is overhead there.
|
|
27
|
+
|
|
28
|
+
## How
|
|
29
|
+
1. **Detect** — are you already in a worktree? `[ "$(git rev-parse --git-dir)" != "$(git rev-parse --git-common-dir)" ]` is true inside one; don't nest.
|
|
30
|
+
2. **Create** — `git worktree add ../wt-<name> -b <branch>` (new branch) or `git worktree add ../wt-<name> <existing>`. It shares the object store, so it's cheap.
|
|
31
|
+
3. **Work there** — cd into it; edits, builds, and agent runs stay local to that copy.
|
|
32
|
+
4. **Fold back or discard** — keep: commit on its branch, then merge. Discard: `git worktree remove ../wt-<name>` (add `--force` only if you mean to drop its changes).
|
|
33
|
+
5. **Clean up** — `git worktree prune` removes stale entries. Never leave orphaned worktrees around.
|
|
34
|
+
|
|
35
|
+
## Invariant rules
|
|
36
|
+
1. **Isolate before risk, don't revert after** — a worktree replaces "run `checkout -- .` to undo"; the undo is `worktree remove`.
|
|
37
|
+
2. **One worktree per parallel writer** — never two agents mutating the same tree at once.
|
|
38
|
+
3. **Don't nest** — creating a worktree from inside a worktree; detect first.
|
|
39
|
+
4. **Commit or isolate before delegating file-mutating work** — a subagent must never be able to discard the main tree's unsaved changes.
|
|
40
|
+
5. **Remove what you add** — no orphaned worktrees; `remove` + `prune` when done.
|
|
@@ -0,0 +1,266 @@
|
|
|
1
|
+
# Crewforth Studio
|
|
2
|
+
|
|
3
|
+
A visual orchestration panel for Claude Code: which agent is running, what it is
|
|
4
|
+
doing right now, what it has spent, and which gates fired.
|
|
5
|
+
|
|
6
|
+
Studio **is installed into your project**, because it lives inside the payload:
|
|
7
|
+
this directory is `kit/studio/`, and `kit/` is what every
|
|
8
|
+
channel already ships. `start.sh` and `adopt.sh` copy it to `.claude/studio/`,
|
|
9
|
+
beside `agents`, `skills`, `commands`, `hooks` and `eval` — the sixth of six.
|
|
10
|
+
|
|
11
|
+
That was the other way round until a real project updated with `/crew-update`,
|
|
12
|
+
ran the documented command and got ENOENT: the project had no `package.json`,
|
|
13
|
+
and nothing had ever installed the panel into it. Excluding it from every
|
|
14
|
+
channel was a decision; a documented command that cannot work is a defect.
|
|
15
|
+
|
|
16
|
+
## Run
|
|
17
|
+
|
|
18
|
+
From any project that has Crewforth:
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
/crew-studio # the command; probes node, starts it, reports the URL
|
|
22
|
+
node .claude/studio/server/index.js --open # the same thing without the picker
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
**`node` may not be on your PATH, and that is on purpose.** When Crewforth fetched the runtime for you
|
|
26
|
+
(`ensure-node.sh --install`), it promised to touch nothing outside `~/.claude/studio-runtime` — no PATH edit,
|
|
27
|
+
no shell profile, no admin rights. Measured on a stock Windows 11 machine: after a successful install
|
|
28
|
+
`node --version` reports v24.21.0 through the full path while `command -v node` finds nothing, which is the
|
|
29
|
+
promise being kept rather than a broken install. So every `node …` line on this page means "the node Crewforth
|
|
30
|
+
can find". Ask for it instead of assuming it:
|
|
31
|
+
|
|
32
|
+
```bash
|
|
33
|
+
NODE="$(bash .claude/studio/ensure-node.sh)" # prints an absolute path, exits 1 if there is none
|
|
34
|
+
"$NODE" .claude/studio/server/index.js --open
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
`/crew-studio` does exactly this for you, which is why it is the first line above and not the second.
|
|
38
|
+
|
|
39
|
+
Other flags, same file:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
node .claude/studio/server/index.js --port 8080
|
|
43
|
+
node .claude/studio/server/index.js --selftest # offline checks, no browser
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
**Started per project, scoped to the machine.** Studio reads
|
|
47
|
+
`~/.claude/projects`, which holds every session on this machine. The count is
|
|
48
|
+
whatever that directory holds; the panel prints it rather than claiming it here,
|
|
49
|
+
because a number measured on one machine describes only that machine. So a
|
|
50
|
+
running panel already sees a kit-installed project without being started inside
|
|
51
|
+
it, and the working directory only decides which project the panel opens on.
|
|
52
|
+
Run it from wherever is convenient, or run it once and leave it up.
|
|
53
|
+
|
|
54
|
+
`--open` exists so the URL is not copied by hand: the token is generated per run
|
|
55
|
+
and the panel refuses requests without it. When a browser cannot be opened — over
|
|
56
|
+
SSH, or headless — the server says so rather than looking like it worked, and
|
|
57
|
+
`/crew-studio` reports the URL either way.
|
|
58
|
+
|
|
59
|
+
**Both editions carry the panel, in different places.** A full install has it
|
|
60
|
+
at `.claude/studio/`; the plugin edition has it at `studio/` in the plugin's own
|
|
61
|
+
root. One `/crew-studio` file serves both, and its first step tells them apart
|
|
62
|
+
by reading `${CLAUDE_PLUGIN_ROOT}/studio/server/index.js` literally: Claude Code
|
|
63
|
+
replaces that placeholder before the command is read, and only when the command
|
|
64
|
+
came from the plugin. A real absolute path is the plugin's panel; the
|
|
65
|
+
placeholder left standing means a full install. Neither path on disk means an
|
|
66
|
+
install older than the panel, which `/crew-update` brings up to date.
|
|
67
|
+
In the plugin the command is namespaced: `/crewforth:crew-studio`.
|
|
68
|
+
|
|
69
|
+
**What the plugin edition cannot show.** A plugin install puts no
|
|
70
|
+
`.claude/VERSION`, `kit.conf` or Crewforth scripts into a project. That project's row
|
|
71
|
+
in the list has no version badge, and for the project the panel was opened from, the
|
|
72
|
+
inspector's gates, stats and board tabs say "Not measured" with the reason rather
|
|
73
|
+
than zero. The gates tab can still list what the gate log observed: the plugin's
|
|
74
|
+
Bash guard records its blocks, approval prompts and pre-authorised git actions,
|
|
75
|
+
and its gate-file write guard its blocks, in `.claude/gate-log.tsv` when the
|
|
76
|
+
project has a `.claude/` directory, the file is git-ignored or the project is
|
|
77
|
+
not a repo, and `CREW_GATE_LOG` does not send the log elsewhere.
|
|
78
|
+
Everything else — the live
|
|
79
|
+
agent graph, owned sessions, the permission bridge — works the same in both
|
|
80
|
+
editions.
|
|
81
|
+
|
|
82
|
+
**The suite is not here.** It lives in `packaging/studio-test/`, beside the
|
|
83
|
+
repo's other gates, and asserts things about this *repository* — the root
|
|
84
|
+
`package.json`, the payload beside it — which an installed project does not
|
|
85
|
+
have. Keeping it under `kit/` would also have shipped 104 KB of test
|
|
86
|
+
code through all four channels only for the installer to delete it on arrival.
|
|
87
|
+
The installed diagnostic is `--selftest`, which lives in `server/index.js`.
|
|
88
|
+
|
|
89
|
+
There is no install step. Studio has **zero dependencies** — `node:http` and
|
|
90
|
+
`node:child_process` are the whole stack, matching Crewforth's own promise. If
|
|
91
|
+
`node_modules` ever appears here, something has gone wrong.
|
|
92
|
+
|
|
93
|
+
Requires Node 18+. In Crewforth's own repository `bash packaging/verify.sh studio`
|
|
94
|
+
runs the same checks CI runs; a machine without node reports a skip rather than a
|
|
95
|
+
pass, and a missing `kit/studio/` is a failure rather than a skip.
|
|
96
|
+
|
|
97
|
+
## What it does
|
|
98
|
+
|
|
99
|
+
**Watches.** The delegation graph of any session on this machine, live: one node
|
|
100
|
+
per agent, what it is doing right now, what it has spent, what it reported.
|
|
101
|
+
Nodes are draggable and their positions are remembered; a workflow run is a
|
|
102
|
+
container you can fold.
|
|
103
|
+
|
|
104
|
+
**Talks.** Sessions the panel starts are driven over stdin/stdout, so you can
|
|
105
|
+
write to one and watch its agents appear beneath it. They are given a session id
|
|
106
|
+
up front, which means their transcripts land where every other session's do and
|
|
107
|
+
the graph reads them without a special case.
|
|
108
|
+
|
|
109
|
+
**Shows every conversation, drives only its own.** Selecting any session opens
|
|
110
|
+
what was said in it, read from the transcript. A session the panel did not start
|
|
111
|
+
has no channel to write to, so that pane says so and offers the two things that
|
|
112
|
+
do work: continue it here, or open it in a real terminal.
|
|
113
|
+
|
|
114
|
+
**Asks before it acts.** Every tool call in an owned session is parked by a
|
|
115
|
+
PreToolUse hook and shown in the panel — the command itself, not just the tool's
|
|
116
|
+
name — until you allow it, allow that tool for the session, or deny it.
|
|
117
|
+
|
|
118
|
+
**Runs several at once.** Each session the panel starts is a tab that keeps its
|
|
119
|
+
own stream open whether or not it is on screen, so work continuing in the
|
|
120
|
+
background is continuing, not replayed when you look back.
|
|
121
|
+
|
|
122
|
+
**Hands a session to a real terminal.** The panel cannot drive a session it did
|
|
123
|
+
not start, so for an observed one it offers the only thing that is true: a
|
|
124
|
+
button that opens it where it can be driven. What it will run is shown before
|
|
125
|
+
anything launches.
|
|
126
|
+
|
|
127
|
+
**Reaches other machines.** Transcripts are local files and nothing enumerates
|
|
128
|
+
another machine's sessions, so a peer is another Studio.
|
|
129
|
+
|
|
130
|
+
## What it reads
|
|
131
|
+
|
|
132
|
+
| Source | What it gives |
|
|
133
|
+
|---|---|
|
|
134
|
+
| `claude agents --json` | Live sessions on this machine: name, cwd, busy/waiting/idle, what it is waiting for |
|
|
135
|
+
| `<session>.jsonl` and `<session>/subagents/` | The delegation graph, nested workflow runs included |
|
|
136
|
+
| `.claude/VERSION` per project | Which projects run an old Crewforth, against the npm dist-tags feed |
|
|
137
|
+
|
|
138
|
+
That call spawns a process, so it is cached rather than repeated. The browser
|
|
139
|
+
polls every 2 s and the server caches for 1 s, which bounds the cost at one
|
|
140
|
+
spawn per poll. What the spawn costs is a property of the machine, not of this
|
|
141
|
+
file — two machines here measured 131 ms and 164 ms medians — so `--selftest`
|
|
142
|
+
measures it wherever it runs and prints that number instead of quoting one.
|
|
143
|
+
|
|
144
|
+
More sources land in later sprints: the transcript tree (per-agent live JSONL),
|
|
145
|
+
the owned-session event stream, `gate-log.tsv`, and the team board.
|
|
146
|
+
|
|
147
|
+
## Two honesty rules
|
|
148
|
+
|
|
149
|
+
These are load-bearing, not stylistic.
|
|
150
|
+
|
|
151
|
+
**"Not measured" is never drawn as zero.** If the `claude` CLI is missing or a
|
|
152
|
+
read fails, the panel says so and names the reason. An empty list would read as
|
|
153
|
+
a quiet, healthy machine — which is the kind of lie this panel exists to stop
|
|
154
|
+
telling.
|
|
155
|
+
|
|
156
|
+
**Unknown data is never coerced into something familiar.** An unrecognised
|
|
157
|
+
status keeps a neutral ring; an unrecognised field is carried through untouched
|
|
158
|
+
rather than dropped. Claude Code's event shapes are undocumented and move with
|
|
159
|
+
the version, so the panel is built to notice that rather than to guess.
|
|
160
|
+
|
|
161
|
+
## Sessions on other machines
|
|
162
|
+
|
|
163
|
+
The fleet lists them under **Reachable elsewhere**, with the time they were
|
|
164
|
+
seen. That wording is the whole of it: this is a snapshot, not a feed.
|
|
165
|
+
|
|
166
|
+
Only a session connected to Remote Control can see peers on other machines.
|
|
167
|
+
Measured: a headless session's `ListAgents` returns the local peers where a
|
|
168
|
+
connected one returns those plus five remote, and passing `--remote-control` to
|
|
169
|
+
a headless session does not change that — the flag starts an *interactive*
|
|
170
|
+
session. There is no documented API or non-interactive command that enumerates
|
|
171
|
+
them; `/list-agents` is an in-session tool, `--cloud` refuses without a TTY, and
|
|
172
|
+
the Claude API's `/v1/sessions` belongs to Managed Agents, a different product.
|
|
173
|
+
|
|
174
|
+
So the panel does not try to be that session. It reads what one already wrote:
|
|
175
|
+
a connected session's `ListAgents` result lands in its transcript as structured
|
|
176
|
+
text, and that is where this comes from. When no session here has ever asked,
|
|
177
|
+
that is reported as not measured — which is not the same as having no peers.
|
|
178
|
+
|
|
179
|
+
Listed, not opened. Those sessions' transcripts are on those machines' disks,
|
|
180
|
+
so the panel can name them but cannot draw their graphs. For that, run Studio
|
|
181
|
+
there too:
|
|
182
|
+
|
|
183
|
+
## Other machines
|
|
184
|
+
|
|
185
|
+
```bash
|
|
186
|
+
# on the other machine
|
|
187
|
+
node .claude/studio/server/index.js
|
|
188
|
+
|
|
189
|
+
# here — forward its port, then point at the forwarded one
|
|
190
|
+
ssh -N -L 7778:127.0.0.1:7777 other-machine
|
|
191
|
+
node .claude/studio/server/index.js --peer http://127.0.0.1:7778 --name mac
|
|
192
|
+
```
|
|
193
|
+
|
|
194
|
+
Sessions, projects, graphs and agent reports from a peer appear alongside local
|
|
195
|
+
ones, tagged with the machine they came from. A peer that cannot be reached is
|
|
196
|
+
reported as unreachable, never as empty.
|
|
197
|
+
|
|
198
|
+
Nothing listens on a network interface in this arrangement: both Studios stay on
|
|
199
|
+
loopback and the tunnel does the crossing.
|
|
200
|
+
|
|
201
|
+
## What Crewforth already measured
|
|
202
|
+
|
|
203
|
+
The panel does not recompute any of it. Selecting the session node opens what
|
|
204
|
+
Crewforth's own tools say, including when they say they cannot answer:
|
|
205
|
+
|
|
206
|
+
| Tab | Source |
|
|
207
|
+
|---|---|
|
|
208
|
+
| gates | `gate-report.sh --json` for the rule inventory, `.claude/gate-log.tsv` for what fired |
|
|
209
|
+
| stats | `session-stats.sh --raw` — fifteen metrics over this transcript |
|
|
210
|
+
| board | `board.sh status` |
|
|
211
|
+
|
|
212
|
+
One distinction is carried all the way to the screen. A gate event from a
|
|
213
|
+
session the panel started arrives through `--include-hook-events` and carries
|
|
214
|
+
the moment it happened, so it is listed under **Happened**. A line in
|
|
215
|
+
`gate-log.tsv` does not — that format has no timestamp column — so those are
|
|
216
|
+
listed under **Observed**, with the reason written out. Drawing the second as
|
|
217
|
+
the first would be the first untrue thing in this panel.
|
|
218
|
+
|
|
219
|
+
`gate-report.sh` finds the hooks relative to where it runs: `./.claude/hooks`
|
|
220
|
+
in an installed project, `./hooks` otherwise. Both layouts are handed what they
|
|
221
|
+
expect rather than the script being asked to guess. Where it still cannot
|
|
222
|
+
answer — a directory with no Crewforth install, a project with no log — that is reported as
|
|
223
|
+
not measured, never as zero.
|
|
224
|
+
|
|
225
|
+
## The permission gate
|
|
226
|
+
|
|
227
|
+
A hook injected through `--settings` parks each tool call, writes it into a
|
|
228
|
+
spool, and waits for the panel's answer. Exit 0 allows, exit 2 blocks.
|
|
229
|
+
|
|
230
|
+
It answers at 45 seconds against a 90-second harness timeout, and that gap is
|
|
231
|
+
the whole design. Measured here: a hook killed at its timeout emits nothing and
|
|
232
|
+
**the tool proceeds** — `permission_denials` came back 0 and the command ran. A
|
|
233
|
+
hook the harness never has to kill fails closed instead, so a shut panel is a
|
|
234
|
+
denial rather than an opening.
|
|
235
|
+
|
|
236
|
+
The waiting costs no processes. A bounded read on a fifo held open read-write
|
|
237
|
+
blocks for its timeout and never sees EOF; the alternative, a `sleep` per poll,
|
|
238
|
+
spends seconds of fork overhead on Git Bash. Both the fifo and the shell's
|
|
239
|
+
support for fractional timeouts are probed with exactly what the loop will run —
|
|
240
|
+
bash 3.2 ignores a fractional `-t` and turns the poll into a hot spin, which is
|
|
241
|
+
how the first version passed while spinning 185,000 times in five seconds.
|
|
242
|
+
|
|
243
|
+
Interactive prompts have a limit worth stating: `AskUserQuestion`, `ExitPlanMode`
|
|
244
|
+
and `EnterPlanMode` are absent from a headless session's 78-tool list, in every
|
|
245
|
+
permission mode, while an interactive session has them. So there is no
|
|
246
|
+
structured choice to render. When a reply offers options in prose the panel
|
|
247
|
+
makes them clickable, which sends that text — a shortcut for typing it, not a
|
|
248
|
+
channel that does not exist.
|
|
249
|
+
|
|
250
|
+
## Security
|
|
251
|
+
|
|
252
|
+
The server binds to `127.0.0.1` only, and the host is deliberately not
|
|
253
|
+
configurable — it reads a developer's live sessions and can start new ones.
|
|
254
|
+
|
|
255
|
+
A token is always required for `/api/`. Supply one, or let the server generate
|
|
256
|
+
one and print it with the URL:
|
|
257
|
+
|
|
258
|
+
```bash
|
|
259
|
+
CREW_STUDIO_TOKEN=$(uuidgen) node .claude/studio/server/index.js
|
|
260
|
+
```
|
|
261
|
+
|
|
262
|
+
Anything that changes state needs, on top of the token, a header no cross-origin
|
|
263
|
+
page can attach without a preflight this server never answers. Permission modes
|
|
264
|
+
are an allow-list — `plan` (the default), `acceptEdits`, `default` — so a mode
|
|
265
|
+
that skips Crewforth's gates cannot be requested, and the panel never has to name
|
|
266
|
+
one to refuse it.
|