claude-company 0.1.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.
Files changed (62) hide show
  1. package/.claude/agents/architect.md +69 -0
  2. package/.claude/agents/auditor.md +44 -0
  3. package/.claude/agents/developer.md +86 -0
  4. package/.claude/agents/devops-engineer.md +38 -0
  5. package/.claude/agents/docs-librarian.md +36 -0
  6. package/.claude/agents/ideation-strategist.md +54 -0
  7. package/.claude/agents/product-manager.md +54 -0
  8. package/.claude/agents/qa-engineer.md +58 -0
  9. package/.claude/agents/security-reviewer.md +44 -0
  10. package/.claude/agents/tech-lead.md +80 -0
  11. package/.claude/hooks/_common.py +209 -0
  12. package/.claude/hooks/gate_stamp.py +77 -0
  13. package/.claude/hooks/gates_detect.py +387 -0
  14. package/.claude/hooks/guard_commit.py +123 -0
  15. package/.claude/hooks/guard_frozen.py +135 -0
  16. package/.claude/hooks/guard_spec.py +95 -0
  17. package/.claude/hooks/guard_tests.py +124 -0
  18. package/.claude/hooks/no_slop.py +134 -0
  19. package/.claude/hooks/session_start.py +63 -0
  20. package/.claude/hooks/stop_gate.py +59 -0
  21. package/.claude/settings.json +70 -0
  22. package/.claude/skills/autopilot/SKILL.md +65 -0
  23. package/.claude/skills/brainstorm/SKILL.md +61 -0
  24. package/.claude/skills/company-init/SKILL.md +51 -0
  25. package/.claude/skills/cr/SKILL.md +44 -0
  26. package/.claude/skills/feature/SKILL.md +42 -0
  27. package/.claude/skills/gates/SKILL.md +33 -0
  28. package/.claude/skills/onboard/SKILL.md +52 -0
  29. package/.claude/skills/orchestrator/SKILL.md +84 -0
  30. package/.claude/skills/standup/SKILL.md +38 -0
  31. package/.mcp.json +1 -0
  32. package/LICENSE +21 -0
  33. package/ORCHESTRATOR.md +191 -0
  34. package/README.md +236 -0
  35. package/bin/claude-company.js +112 -0
  36. package/company/EXTENDING.md +58 -0
  37. package/company/GATES.md +59 -0
  38. package/company/GIT.md +119 -0
  39. package/company/IDEATION.md +102 -0
  40. package/company/LOOPS.md +108 -0
  41. package/company/METHOD.md +151 -0
  42. package/company/frozen-surfaces.json +17 -0
  43. package/company/gates.config +5 -0
  44. package/company/run-gates.sh +158 -0
  45. package/company/state/DECISIONS.md +9 -0
  46. package/company/state/RESUME.md +28 -0
  47. package/company/state/STATUS.md +30 -0
  48. package/company/state/WORRIES.md +12 -0
  49. package/company/state/adherence.log +1 -0
  50. package/company/templates/BRIEF-TEMPLATE.md +69 -0
  51. package/company/templates/CR-TEMPLATE.md +28 -0
  52. package/company/templates/MODULE-TEMPLATE.md +23 -0
  53. package/company/templates/OPTIONS-TEMPLATE.md +42 -0
  54. package/company/templates/REPORT-TEMPLATE.md +35 -0
  55. package/company/templates/SPEC-TEMPLATE.md +69 -0
  56. package/docs/customizing.md +88 -0
  57. package/docs/getting-started.md +99 -0
  58. package/docs/how-it-works.md +153 -0
  59. package/install +4 -0
  60. package/install.sh +331 -0
  61. package/lib/install-tui.js +1600 -0
  62. package/package.json +47 -0
@@ -0,0 +1,153 @@
1
+ # How it works
2
+
3
+ This page explains the method behind claude-company: why it is shaped like a company, what the gates check, and how the pieces keep each other honest. Read it when you want to understand the system well enough to explain it to someone else.
4
+
5
+ <p>
6
+ <a href="#the-core-idea">Core Idea</a> &bull;
7
+ <a href="#the-pipeline">Pipeline</a> &bull;
8
+ <a href="#the-paperwork">Paperwork</a> &bull;
9
+ <a href="#the-gates">Gates</a> &bull;
10
+ <a href="#protected-files">Protected Files</a> &bull;
11
+ <a href="#what-the-owner-keeps">Owner</a>
12
+ </p>
13
+
14
+ ## The core idea
15
+
16
+ AI agents are capable builders and unreliable narrators. Left alone, an agent under pressure will report failing work as done, widen its own scope, and edit tests until they pass. Process documents help, but an instruction is something the model can skip.
17
+
18
+ claude-company treats that as an engineering problem, not a prompting problem. The rules that matter are enforced by hooks: scripts that run before every file edit and shell command, and block the ones that break the rules. The prose explains the why; the hook supplies the no.
19
+
20
+ | Layer | What lives there | Binding? |
21
+ |---|---|---|
22
+ | Canon (`company/*.md`, `CLAUDE.md`) | The method, the conventions, the why | Advisory: agents read and follow it |
23
+ | Roles (`.claude/agents/`) | Who does what, and what each role may touch | Structural: tool access is restricted per role |
24
+ | Hooks (`.claude/hooks/`) | The rules that must hold | **Mechanical: the action is blocked** |
25
+
26
+ ## The pipeline
27
+
28
+ A feature moves through separated roles, each checking the one before:
29
+
30
+ ```mermaid
31
+ sequenceDiagram
32
+ actor You
33
+ participant CEO
34
+ participant PM as product-manager
35
+ participant TL as tech-lead
36
+ participant Dev as developers (parallel)
37
+ participant QA as qa-engineer
38
+
39
+ You->>CEO: build me X
40
+ CEO->>PM: dispatch: write the spec
41
+ PM-->>CEO: spec: options considered, FR ids, fallbacks
42
+ CEO->>TL: sealed brief, isolated worktree
43
+ TL->>Dev: task orders on disjoint directories
44
+ Dev-->>TL: code + evidence reports
45
+ Note over TL: fills the gaps between pieces
46
+ TL->>QA: drive the running app
47
+ QA-->>TL: screenshots: loaded/empty/error/after-action
48
+ TL-->>CEO: evidence report + gate ladder
49
+ Note over CEO: reruns gates, checks diffs,<br>judges screenshots itself
50
+ CEO-->>You: delivery + evidence + your decisions
51
+ ```
52
+
53
+ ### Why a company shape
54
+
55
+ The structure copies what makes real engineering organizations work: separated roles with separated incentives.
56
+
57
+ - **Thinkers and builders are different agents.** The product manager and architect explore options and write plans. Developers build. A builder never quietly redefines the plan, because it never owns the plan.
58
+ - **Producers never grade their own work.** Each layer catches what the layer below was too close to see: developers report, leads verify, QA captures, the CEO judges, an auditor rechecks the big merges.
59
+ - **Everyone owns their own directories.** Work orders name the exact directories an agent may touch. Two agents never share one directory, so parallel work cannot collide.
60
+
61
+ The hierarchy stays shallow on purpose: the CEO, then tech leads, then their developers and QA. Deeper pyramids add token cost and lose information at every handoff.
62
+
63
+ ## The paperwork
64
+
65
+ The company runs on a few typed documents rather than long conversations:
66
+
67
+ | Artifact | Written by | Read by | What it carries |
68
+ |---|---|---|---|
69
+ | Options memo | strategists + CEO | you | Numbered ideas with reasoning, scored recommendation, the strongest rejected option |
70
+ | Spec | product-manager | CEO | Requirements with stable ids (FR-01, FR-02), acceptance criteria, options considered |
71
+ | Brief | CEO | one builder | Mission, owned directories, definition of done, a decided fallback per ambiguity |
72
+ | Report | every agent | its dispatcher | Facts only: the diff, gate output, screenshots, deviations |
73
+
74
+ Two design choices matter most:
75
+
76
+ - **Builders read the brief, never the spec.** The spec is rich and human-facing; the brief is the lean slice derived from it. The builder's context stays small and its instructions stay exact.
77
+ - **Ambiguity is handled once, in writing.** Every open question gets one decided fallback, so ten parallel agents make the same assumption instead of ten different ones. Questions only a human should answer wait in `company/state/DECISIONS.md` while the build proceeds on the fallback.
78
+
79
+ ## The gates
80
+
81
+ A gate is a command that must exit successfully: your test suite, your linter, your build. Gates live in `company/gates.config`, and `company/run-gates.sh` runs the ladder and stamps the result with a fingerprint of your working tree.
82
+
83
+ The stamp is what gives gates teeth:
84
+
85
+ ```mermaid
86
+ stateDiagram-v2
87
+ direction LR
88
+ [*] --> Unstamped
89
+ Unstamped --> Green: run gates, all pass
90
+ Unstamped --> Red: any gate fails
91
+ Red --> Green: fix the cause, rerun
92
+ Green --> Stale: any tracked file changes
93
+ Stale --> Green: rerun gates
94
+ Green --> [*]: git commit allowed
95
+ note right of Red
96
+ commit hook blocks
97
+ Red, Stale, Unstamped
98
+ end note
99
+ ```
100
+
101
+ Change one file after the gates ran and the stamp goes stale, so "it passed earlier" stops counting. Nobody, including the CEO, can commit past a red or stale stamp.
102
+
103
+ <details>
104
+ <summary><b>Two habits that keep gates meaningful</b></summary>
105
+ <br>
106
+
107
+ - **Test the negative space.** Where a table lists allowed actions, generate the complement and assert every non-listed action is rejected. Positive-only tests pass while a system silently allows everything.
108
+ - **Never trust a worktree's numbers.** Agents build in isolated git worktrees, where stale artifacts can mask integration failures. Verification reruns gates on the integrated result.
109
+
110
+ </details>
111
+
112
+ ## Protected files
113
+
114
+ Some files hold the whole system up: database migrations that already shipped, the schema, lockfiles, anything with exactly one legitimate writer. These are listed in `company/frozen-surfaces.json`, and the hook blocks every edit to them.
115
+
116
+ When an agent genuinely needs a protected file changed, it files a change request (CR) instead:
117
+
118
+ ```mermaid
119
+ flowchart LR
120
+ A[Agent needs a frozen<br>file changed] --> B[STOP: file a CR<br>what, why, exact diff,<br>blast radius]
121
+ B --> C{CEO arbitrates}
122
+ C -->|approve| D[CEO applies it in a<br>dedicated gated commit]
123
+ C -->|reject| E[Agent uses the CR's<br>stated workaround]
124
+ D --> F[Affected agents rebase]
125
+ ```
126
+
127
+ The paperwork is the point: every change to a shared surface becomes visible and reviewed instead of silent.
128
+
129
+ ## What the owner keeps
130
+
131
+ The escalation list is short and absolute. No agent decides:
132
+
133
+ | Decision | Why it is yours |
134
+ |---|---|
135
+ | Production deploys and migrations | Merge is integration; shipping is a button only you press |
136
+ | Anything involving money | Pricing, billing behavior, ledgers |
137
+ | Weakening a protection | The rules exist because someone will want around them |
138
+ | Scope beyond a brief | Growth is a decision, not a drift |
139
+ | Business policy | Agents run on tagged fallbacks until you answer |
140
+
141
+ One more rule catches design problems early: a gate that fails twice on the same cause stops the work and surfaces to you, because repeated failure means the plan is wrong, not the agent.
142
+
143
+ ## Watching it enforce
144
+
145
+ Every hook block and every hotfix bypass appends one line to `company/state/adherence.log`:
146
+
147
+ ```text
148
+ 2026-07-06T14:51:57Z | guard_frozen | BLOCK | .env | always-frozen: .env
149
+ 2026-07-06T14:51:58Z | guard_commit | BLOCK | git commit | gates.status is stale
150
+ 2026-07-06T14:52:14Z | guard_commit | BYPASS | git commit | hotfix mode
151
+ ```
152
+
153
+ The log is the difference between a system that claims discipline and one that demonstrates it. Repeated blocks on the same agent or file are a signal worth reading: the work order was vague, or the design is fighting the rules.
package/install ADDED
@@ -0,0 +1,4 @@
1
+ #!/bin/sh
2
+ # claude-company installer shim - execs the Node CLI's install subcommand.
3
+ dir=$(CDPATH= cd -- "$(dirname -- "$0")" && pwd)
4
+ exec node "$dir/bin/claude-company.js" install "$@"
package/install.sh ADDED
@@ -0,0 +1,331 @@
1
+ #!/usr/bin/env bash
2
+ # install.sh - drop-in installer for claude-company, a hierarchical SDLC
3
+ # system for Claude Code. Copies the company system into a target project,
4
+ # idempotently and non-destructively (user state and config always win).
5
+ #
6
+ # Usage: bash install.sh /path/to/target-project
7
+ #
8
+ # Works on macOS bash 3.2 (no associative arrays, no readarray).
9
+ set -euo pipefail
10
+
11
+ # --- colors ---------------------------------------------------------------
12
+ if [ -t 1 ]; then
13
+ C_RED=$'\033[31m'; C_GREEN=$'\033[32m'; C_YELLOW=$'\033[33m'
14
+ C_BLUE=$'\033[34m'; C_BOLD=$'\033[1m'; C_RESET=$'\033[0m'
15
+ else
16
+ C_RED=''; C_GREEN=''; C_YELLOW=''; C_BLUE=''; C_BOLD=''; C_RESET=''
17
+ fi
18
+ info() { printf '%s\n' "${C_BLUE}${C_BOLD}==>${C_RESET} $*"; }
19
+ ok() { printf '%s\n' " ${C_GREEN}ok${C_RESET} $*"; }
20
+ skip() { printf '%s\n' " ${C_YELLOW}keep${C_RESET} $*"; }
21
+ warn() { printf '%s\n' "${C_YELLOW}warning:${C_RESET} $*" >&2; }
22
+ die() { printf '%s\n' "${C_RED}error:${C_RESET} $*" >&2; exit 1; }
23
+
24
+ usage() {
25
+ cat >&2 <<USAGE
26
+ Usage: bash install.sh /path/to/target-project
27
+
28
+ Installs the claude-company SDLC system into an existing project directory.
29
+ The target directory must already exist.
30
+ USAGE
31
+ exit 2
32
+ }
33
+
34
+ # --- resolve source (this script's repo), independent of cwd --------------
35
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
36
+ SRC="$SCRIPT_DIR"
37
+
38
+ # --- validate arguments ---------------------------------------------------
39
+ [ "$#" -ge 1 ] || usage
40
+ TARGET_ARG="$1"
41
+ [ -n "$TARGET_ARG" ] || usage
42
+ [ -d "$TARGET_ARG" ] || die "target directory does not exist: $TARGET_ARG"
43
+ TARGET="$(cd "$TARGET_ARG" && pwd)"
44
+
45
+ # --- refuse to install into the claude-company repo itself ----------------
46
+ if [ "$TARGET" = "$SRC" ]; then
47
+ die "refusing to install into the claude-company repo itself ($SRC)"
48
+ fi
49
+
50
+ info "claude-company installer"
51
+ echo " source: $SRC"
52
+ echo " target: $TARGET"
53
+ echo
54
+
55
+ # --- copy helpers ---------------------------------------------------------
56
+
57
+ # Overwrite a single file (ours - update in place).
58
+ copy_overwrite() {
59
+ local src="$1" dst="$2"
60
+ if [ ! -e "$src" ]; then warn "source missing, skipped: ${src#$SRC/}"; return 0; fi
61
+ mkdir -p "$(dirname "$dst")"
62
+ cp "$src" "$dst"
63
+ ok "${dst#$TARGET/}"
64
+ }
65
+
66
+ # Copy a single file only if the destination is absent (user wins).
67
+ copy_if_absent() {
68
+ local src="$1" dst="$2"
69
+ if [ ! -e "$src" ]; then warn "source missing, skipped: ${src#$SRC/}"; return 0; fi
70
+ if [ -e "$dst" ]; then skip "${dst#$TARGET/} (kept existing)"; return 0; fi
71
+ mkdir -p "$(dirname "$dst")"
72
+ cp "$src" "$dst"
73
+ ok "${dst#$TARGET/}"
74
+ }
75
+
76
+ # Walk a directory and overwrite every file at the target (ours in place).
77
+ # Skips python bytecode caches so we never ship __pycache__/*.pyc artifacts.
78
+ copy_tree_overwrite() {
79
+ local src="$1" dst="$2" f rel
80
+ if [ ! -d "$src" ]; then warn "source dir missing, skipped: ${src#$SRC/}/"; return 0; fi
81
+ find "$src" -type f -not -path '*/__pycache__/*' -not -name '*.pyc' -print | while IFS= read -r f; do
82
+ rel="${f#$src/}"
83
+ mkdir -p "$dst/$(dirname "$rel")"
84
+ cp "$f" "$dst/$rel"
85
+ done
86
+ ok "${dst#$TARGET/}/ (updated)"
87
+ }
88
+
89
+ # Walk a directory and copy each file only if absent at target (user wins).
90
+ copy_tree_if_absent() {
91
+ local src="$1" dst="$2" f rel
92
+ if [ ! -d "$src" ]; then return 0; fi
93
+ find "$src" -type f -not -path '*/__pycache__/*' -not -name '*.pyc' -print | while IFS= read -r f; do
94
+ rel="${f#$src/}"
95
+ if [ ! -e "$dst/$rel" ]; then
96
+ mkdir -p "$dst/$(dirname "$rel")"
97
+ cp "$f" "$dst/$rel"
98
+ fi
99
+ done
100
+ }
101
+
102
+ # --- 1. agents, hooks, skills (ours - update in place) --------------------
103
+ info "Installing agents, hooks, and skills"
104
+ copy_tree_overwrite "$SRC/.claude/agents" "$TARGET/.claude/agents"
105
+ copy_tree_overwrite "$SRC/.claude/hooks" "$TARGET/.claude/hooks"
106
+ copy_tree_overwrite "$SRC/.claude/skills" "$TARGET/.claude/skills"
107
+
108
+ # --- 2. canon docs and orchestrator (ours - update in place) --------------
109
+ info "Installing canon docs"
110
+ copy_overwrite "$SRC/ORCHESTRATOR.md" "$TARGET/ORCHESTRATOR.md"
111
+ copy_overwrite "$SRC/company/METHOD.md" "$TARGET/company/METHOD.md"
112
+ copy_overwrite "$SRC/company/GATES.md" "$TARGET/company/GATES.md"
113
+ copy_overwrite "$SRC/company/EXTENDING.md" "$TARGET/company/EXTENDING.md"
114
+ copy_overwrite "$SRC/company/IDEATION.md" "$TARGET/company/IDEATION.md"
115
+ copy_overwrite "$SRC/company/GIT.md" "$TARGET/company/GIT.md"
116
+ copy_overwrite "$SRC/company/LOOPS.md" "$TARGET/company/LOOPS.md"
117
+ copy_overwrite "$SRC/company/run-gates.sh" "$TARGET/company/run-gates.sh"
118
+ [ -f "$TARGET/company/run-gates.sh" ] && chmod +x "$TARGET/company/run-gates.sh"
119
+ copy_tree_overwrite "$SRC/company/templates" "$TARGET/company/templates"
120
+
121
+ # --- 3. user config and state (user wins if present) ----------------------
122
+ info "Installing project config and state (existing files preserved)"
123
+ copy_if_absent "$SRC/company/gates.config" "$TARGET/company/gates.config"
124
+ copy_if_absent "$SRC/company/frozen-surfaces.json" "$TARGET/company/frozen-surfaces.json"
125
+
126
+ # work directories - preserve any existing content
127
+ mkdir -p "$TARGET/company/specs" "$TARGET/company/briefs" "$TARGET/company/change-requests"
128
+ copy_tree_if_absent "$SRC/company/specs" "$TARGET/company/specs"
129
+ copy_tree_if_absent "$SRC/company/briefs" "$TARGET/company/briefs"
130
+ copy_tree_if_absent "$SRC/company/change-requests" "$TARGET/company/change-requests"
131
+
132
+ # --- 4. scaffold state stubs (only if absent) -----------------------------
133
+ info "Scaffolding company/state"
134
+ mkdir -p "$TARGET/company/state"
135
+ scaffold_stub() {
136
+ local dst="$1" header="$2"
137
+ if [ -e "$dst" ]; then skip "${dst#$TARGET/} (kept existing)"; return 0; fi
138
+ printf '%s\n' "$header" > "$dst"
139
+ ok "${dst#$TARGET/}"
140
+ }
141
+ scaffold_stub "$TARGET/company/state/STATUS.md" \
142
+ "# STATUS - maintained by the orchestrator. Red stays red until proven green."
143
+ scaffold_stub "$TARGET/company/state/RESUME.md" \
144
+ "# RESUME - maintained by the orchestrator. Where we are and what happens next."
145
+ scaffold_stub "$TARGET/company/state/WORRIES.md" \
146
+ "# WORRIES - maintained by the orchestrator. Open risks and unknowns, worst first."
147
+ scaffold_stub "$TARGET/company/state/DECISIONS.md" \
148
+ "# DECISIONS - maintained by the orchestrator. Durable choices and their rationale."
149
+ if [ ! -e "$TARGET/company/state/adherence.log" ]; then
150
+ touch "$TARGET/company/state/adherence.log"
151
+ ok "company/state/adherence.log"
152
+ else
153
+ skip "company/state/adherence.log (kept existing)"
154
+ fi
155
+
156
+ # --- 5. settings.json (copy or deep-merge) --------------------------------
157
+ # Always route through the merger so the on-disk formatting is identical
158
+ # whether we started from nothing or an existing file (keeps re-runs a no-op).
159
+ info "Installing .claude/settings.json"
160
+ SETTINGS_SRC="$SRC/.claude/settings.json"
161
+ SETTINGS_DST="$TARGET/.claude/settings.json"
162
+ if [ ! -f "$SETTINGS_SRC" ]; then
163
+ warn "source .claude/settings.json missing - skipped"
164
+ else
165
+ [ -f "$SETTINGS_DST" ] && SETTINGS_VERB="merged" || SETTINGS_VERB="copied"
166
+ mkdir -p "$TARGET/.claude"
167
+ python3 - "$SETTINGS_SRC" "$SETTINGS_DST" <<'PY'
168
+ import json, sys
169
+
170
+ src_path, dst_path = sys.argv[1], sys.argv[2]
171
+ with open(src_path) as f:
172
+ ours = json.load(f)
173
+ try:
174
+ with open(dst_path) as f:
175
+ theirs = json.load(f)
176
+ except (FileNotFoundError, ValueError):
177
+ theirs = {}
178
+
179
+ # --- merge hooks: append our command entries unless already present -------
180
+ def commands_in_event(groups):
181
+ cmds = set()
182
+ for g in groups or []:
183
+ for h in (g.get("hooks") or []):
184
+ c = h.get("command")
185
+ if c is not None:
186
+ cmds.add(c)
187
+ return cmds
188
+
189
+ our_hooks = ours.get("hooks") or {}
190
+ their_hooks = theirs.setdefault("hooks", {}) if isinstance(theirs.get("hooks", {}), dict) else {}
191
+ if not isinstance(theirs.get("hooks"), dict):
192
+ theirs["hooks"] = {}
193
+ their_hooks = theirs["hooks"]
194
+
195
+ for event, our_groups in our_hooks.items():
196
+ existing_groups = their_hooks.get(event)
197
+ if not isinstance(existing_groups, list):
198
+ existing_groups = []
199
+ their_hooks[event] = existing_groups
200
+ have = commands_in_event(existing_groups)
201
+ for g in (our_groups or []):
202
+ new_hooks = []
203
+ for h in (g.get("hooks") or []):
204
+ c = h.get("command")
205
+ if c is not None and c in have:
206
+ continue # identical command already present
207
+ new_hooks.append(h)
208
+ if c is not None:
209
+ have.add(c)
210
+ if new_hooks:
211
+ ng = dict(g)
212
+ ng["hooks"] = new_hooks
213
+ existing_groups.append(ng)
214
+
215
+ # --- merge permissions.deny: union, preserving user's ---------------------
216
+ our_perms = ours.get("permissions") or {}
217
+ our_deny = our_perms.get("deny") or []
218
+ if our_deny:
219
+ their_perms = theirs.get("permissions")
220
+ if not isinstance(their_perms, dict):
221
+ their_perms = {}
222
+ theirs["permissions"] = their_perms
223
+ their_deny = their_perms.get("deny")
224
+ if not isinstance(their_deny, list):
225
+ their_deny = []
226
+ their_perms["deny"] = their_deny
227
+ for entry in our_deny:
228
+ if entry not in their_deny:
229
+ their_deny.append(entry)
230
+
231
+ with open(dst_path, "w") as f:
232
+ json.dump(theirs, f, indent=2, sort_keys=False)
233
+ f.write("\n")
234
+ PY
235
+ ok ".claude/settings.json ($SETTINGS_VERB)"
236
+ fi
237
+
238
+ # --- 6. .mcp.json (copy or merge the playwright server) -------------------
239
+ info "Installing .mcp.json"
240
+ MCP_SRC="$SRC/.mcp.json"
241
+ MCP_DST="$TARGET/.mcp.json"
242
+ if [ ! -f "$MCP_SRC" ]; then
243
+ warn "source .mcp.json missing - skipped"
244
+ else
245
+ [ -f "$MCP_DST" ] && MCP_VERB="merged" || MCP_VERB="copied"
246
+ python3 - "$MCP_SRC" "$MCP_DST" <<'PY'
247
+ import json, sys
248
+ src_path, dst_path = sys.argv[1], sys.argv[2]
249
+ with open(src_path) as f:
250
+ ours = json.load(f)
251
+ try:
252
+ with open(dst_path) as f:
253
+ theirs = json.load(f)
254
+ except (FileNotFoundError, ValueError):
255
+ theirs = {}
256
+ our_servers = ours.get("mcpServers") or {}
257
+ their_servers = theirs.get("mcpServers")
258
+ if not isinstance(their_servers, dict):
259
+ their_servers = {}
260
+ theirs["mcpServers"] = their_servers
261
+ for name, cfg in our_servers.items():
262
+ if name not in their_servers: # never overwrite a user's existing server
263
+ their_servers[name] = cfg
264
+ with open(dst_path, "w") as f:
265
+ json.dump(theirs, f, indent=2, sort_keys=False)
266
+ f.write("\n")
267
+ PY
268
+ ok ".mcp.json ($MCP_VERB)"
269
+ fi
270
+
271
+ # --- 7. CLAUDE.md marked block (append or replace) ------------------------
272
+ info "Updating CLAUDE.md"
273
+ read -r -d '' CC_BLOCK <<'BLOCK' || true
274
+ <!-- claude-company:begin -->
275
+ ## claude-company
276
+
277
+ This project runs **claude-company**, a hierarchical SDLC system for Claude Code.
278
+
279
+ - Main sessions act as CEO: drive the project through `/orchestrator` and `ORCHESTRATOR.md`.
280
+ - Subagents obey their brief plus `company/METHOD.md` - the brief is the contract.
281
+ - Gates are the definition of done and are hook-enforced. Red stays red until proven green.
282
+ - Run gates with `company/run-gates.sh`; configure them in `company/gates.config`.
283
+ - Frozen surfaces (`company/frozen-surfaces.json`) change only via a change request in `company/change-requests/`.
284
+ - Project state lives in `company/state/` (STATUS, RESUME, WORRIES, DECISIONS).
285
+
286
+ New to this project? Run `/company-init`. Adopting an existing codebase? Run `/onboard`. Then `/orchestrator`.
287
+ <!-- claude-company:end -->
288
+ BLOCK
289
+
290
+ CC_BLOCK="$CC_BLOCK" python3 - "$TARGET/CLAUDE.md" <<'PY'
291
+ import os, re, sys
292
+ path = sys.argv[1]
293
+ block = os.environ["CC_BLOCK"].rstrip("\n")
294
+ begin = "<!-- claude-company:begin -->"
295
+ end = "<!-- claude-company:end -->"
296
+ try:
297
+ with open(path) as f:
298
+ content = f.read()
299
+ except FileNotFoundError:
300
+ content = ""
301
+ if begin in content and end in content:
302
+ pattern = re.compile(re.escape(begin) + ".*?" + re.escape(end), re.DOTALL)
303
+ content = pattern.sub(lambda m: block, content)
304
+ else:
305
+ if content and not content.endswith("\n"):
306
+ content += "\n"
307
+ if content:
308
+ content += "\n"
309
+ content += block + "\n"
310
+ with open(path, "w") as f:
311
+ f.write(content)
312
+ PY
313
+ ok "CLAUDE.md (claude-company block up to date)"
314
+
315
+ # --- epilogue -------------------------------------------------------------
316
+ echo
317
+ info "claude-company installed"
318
+ cat <<EPILOGUE
319
+
320
+ Next steps:
321
+ 1. cd "$TARGET"
322
+ 2. claude
323
+ 3. In Claude Code, run one of:
324
+ /company-init (new project - scaffold specs and gates)
325
+ /onboard (existing codebase - map what is already there)
326
+ 4. Then start driving the work:
327
+ /orchestrator
328
+
329
+ Configure your gates in company/gates.config, then verify with:
330
+ bash company/run-gates.sh
331
+ EPILOGUE