conductor-harness 1.0.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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 blaesild
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,137 @@
1
+ # conductor-harness
2
+
3
+ A Claude Code harness for [Conductor](https://conductor.build) projects. Installs session hooks, memory integration, Linear workflow, and project context injection into any git repo in under a minute.
4
+
5
+ ---
6
+
7
+ ## What it does
8
+
9
+ - **SessionStart hook** — injects branch, recent commits, last session progress, and `WORKFLOW.md` context before every Claude Code session
10
+ - **Linear integration** — reads attached issues directly from Conductor's `+` button; falls back to Linear MCP for comments and history
11
+ - **Graphiti memory** — searches past decisions before starting work; writes episodes after `/done`
12
+ - **Context7 docs** — fetches up-to-date framework and package documentation before planning or implementing
13
+ - **`/start`, `/done`, `/status`** — task kickoff and closeout rituals that keep progress state and memory in sync
14
+ - **`/setup`** — analyzes your project and auto-generates `WORKFLOW.md`
15
+ - **`WORKFLOW.md`** — a project north star (what you're building, current phase, constraints) injected into every session
16
+
17
+ ---
18
+
19
+ ## Requirements
20
+
21
+ - [Claude Code](https://claude.ai/code)
22
+ - [Conductor](https://conductor.build)
23
+ - Node.js 18+
24
+ - A git repository
25
+
26
+ **Recommended MCP servers** (register once per machine):
27
+
28
+ ```bash
29
+ # Graphiti memory (Zep Cloud)
30
+ claude mcp add graphiti-memory \
31
+ --transport sse \
32
+ --url https://mcp.getzep.com/sse \
33
+ --header "Authorization: Api-Key $ZEP_API_KEY"
34
+
35
+ # Linear
36
+ claude mcp add linear -s user -- npx -y @linear/mcp-server
37
+
38
+ # Context7 (up-to-date docs)
39
+ claude mcp add context7 -s user -- npx -y @upstash/context7-mcp
40
+ ```
41
+
42
+ ---
43
+
44
+ ## Install
45
+
46
+ Run from your project root:
47
+
48
+ ```bash
49
+ npx conductor-harness
50
+ ```
51
+
52
+ The installer will:
53
+
54
+ 1. Detect your package manager (pnpm / yarn / bun / npm)
55
+ 2. Ask if the project uses Railway
56
+ 3. Copy hooks, commands, skills, and agents into `.claude/`
57
+ 4. Merge harness hooks and permissions into `.claude/settings.local.json`
58
+ 5. Write `CLAUDE.md` with orchestration principles and harness configuration
59
+ 6. Write `conductor.json` with setup, run, and archive commands
60
+ 7. Create `WORKFLOW.md` for project context
61
+ 8. Create `.harness/progress.md` for session state
62
+
63
+ Re-running is safe — `settings.local.json` is always merged, never overwritten.
64
+
65
+ ---
66
+
67
+ ## Railway projects
68
+
69
+ If your project uses Railway, add these to your `.env`:
70
+
71
+ ```env
72
+ RAILWAY_PROJECT_ID=your-project-id
73
+ RAILWAY_ENVIRONMENT_ID=your-environment-id
74
+ RAILWAY_SERVICE_ID=your-service-id
75
+ ```
76
+
77
+ The generated `conductor.json` setup command will pick them up automatically via `source .env`.
78
+
79
+ ---
80
+
81
+ ## After install
82
+
83
+ **Fill in `WORKFLOW.md`** — or let Claude generate it:
84
+
85
+ ```
86
+ /setup
87
+ ```
88
+
89
+ **Start a task:**
90
+
91
+ ```
92
+ /start LIN-123
93
+ ```
94
+
95
+ Or attach a Linear issue via Conductor's `+` button and type `/start` — the harness reads it directly without an MCP fetch.
96
+
97
+ **End a task:**
98
+
99
+ ```
100
+ /done
101
+ ```
102
+
103
+ Closes the Linear issue, writes a Graphiti memory episode, and resets progress state.
104
+
105
+ ---
106
+
107
+ ## Project structure
108
+
109
+ ```
110
+ runtime/
111
+ .claude/
112
+ CLAUDE.md.template ← Orchestration principles + harness config
113
+ hooks/
114
+ session-start.sh ← Injects context before every session
115
+ stop.sh ← Saves progress on session end
116
+ commands/
117
+ start.md ← Task kickoff ritual
118
+ done.md ← Task closeout ritual
119
+ status.md ← Current task status
120
+ setup.md ← Project analysis + WORKFLOW.md generation
121
+ skills/
122
+ linear/SKILL.md ← Linear issue read/write
123
+ memory/SKILL.md ← Graphiti memory search/write
124
+ review/SKILL.md ← Code review checklist
125
+ agents/
126
+ memory-writer.md ← Subagent for writing memory episodes
127
+ settings.json.template ← Base permissions and hook registration
128
+ WORKFLOW.md.template ← Project north star template
129
+ conductor.json.template ← Conductor setup/run/archive commands
130
+ install.sh ← The installer (called by npx)
131
+ ```
132
+
133
+ ---
134
+
135
+ ## License
136
+
137
+ MIT
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env node
2
+ const { execFileSync } = require("child_process");
3
+ const path = require("path");
4
+
5
+ const script = path.join(__dirname, "..", "runtime", "install.sh");
6
+
7
+ try {
8
+ execFileSync("bash", [script], { stdio: "inherit" });
9
+ } catch {
10
+ process.exit(1);
11
+ }
package/package.json ADDED
@@ -0,0 +1,21 @@
1
+ {
2
+ "name": "conductor-harness",
3
+ "version": "1.0.0",
4
+ "description": "Claude Code harness for Conductor projects — hooks, memory, Linear, and workflow context",
5
+ "bin": {
6
+ "conductor-harness": "./bin/conductor-harness.js"
7
+ },
8
+ "files": [
9
+ "bin",
10
+ "runtime"
11
+ ],
12
+ "keywords": ["claude-code", "conductor", "ai", "harness", "linear", "railway"],
13
+ "license": "MIT",
14
+ "repository": {
15
+ "type": "git",
16
+ "url": "https://github.com/blaesild/conductor-harness.git"
17
+ },
18
+ "engines": {
19
+ "node": ">=18"
20
+ }
21
+ }
@@ -0,0 +1,53 @@
1
+ # [Project Name]
2
+
3
+ > **Stack**: [framework] | [database] | [deployment] | [pkg manager]
4
+
5
+ ---
6
+
7
+ ## Orchestration
8
+
9
+ **Plan first**: Enter plan mode for any non-trivial task (3+ steps or architectural decisions). If something goes sideways, stop and re-plan — don't push through.
10
+
11
+ **Subagents**: Keep the main context clean — delegate to subagents for specialized domains. One task per subagent. Subagents cannot spawn subagents.
12
+
13
+ **Delegate vs handle directly**:
14
+ - Handle directly: < 3 files, known pattern, trivial change
15
+ - Delegate: specialized domain, deep implementation, debugging, review
16
+
17
+ **Verify before done**: Never mark complete without proving it works. Ask: "Would a staff engineer approve this?" Run tests, check logs, demonstrate correctness.
18
+
19
+ **Memory over lessons**: After /done, a Graphiti episode is written. Before non-trivial work, search memory for past decisions. This is the self-improvement loop.
20
+
21
+ **Docs over assumptions**: AI knowledge has a cutoff. Before using any framework, library, or package API — fetch current docs via Context7 (`mcp__claude_ai_Context7__resolve-library-id` then `mcp__claude_ai_Context7__query-docs`). Never assume an API is current. This applies in plan mode too.
22
+
23
+ ---
24
+
25
+ ## Harness
26
+
27
+ @.claude/skills/linear/SKILL.md
28
+ @.claude/skills/memory/SKILL.md
29
+ @.claude/skills/review/SKILL.md
30
+
31
+ **Linear**: If an issue is attached to the current message, read it directly — no MCP fetch needed. Use the Linear MCP tool only for additional detail (comments, history, linked issues).
32
+
33
+ **Startup**: SessionStart hook injects branch, git log, WORKFLOW.md, and last session progress. Read it before responding.
34
+
35
+ **End of task**: Run /done. Do not update Linear or write memory without it.
36
+
37
+ ---
38
+
39
+ ## MCP Servers
40
+
41
+ <!-- Add project-specific MCP servers here -->
42
+ | Server | Purpose |
43
+ |--------|---------|
44
+
45
+ ---
46
+
47
+ ## Key Docs
48
+
49
+ <!-- Add: architecture docs, golden rules, README -->
50
+
51
+ ---
52
+
53
+ **Philosophy**: Trust frameworks. Use native tools. Delegate with context. Simplify.
@@ -0,0 +1,19 @@
1
+ ---
2
+ name: memory-writer
3
+ description: Writes a structured memory episode to Graphiti for the current session. Called by /done and by the Stop hook when task is marked complete.
4
+ tools: [mcp__graphiti-memory__add_episode]
5
+ ---
6
+
7
+ You are a memory writer. Your only job is to write a structured episode to Graphiti.
8
+
9
+ Write one episode using the add_episode tool. The episode should contain:
10
+ - What was built or changed (concrete, specific)
11
+ - Key decisions made and why
12
+ - Gotchas, edge cases, or surprising discoveries
13
+ - Patterns or utilities that could be reused in other contexts
14
+
15
+ Group ID: use the project name from the current directory (lowercase, hyphens for spaces).
16
+
17
+ Episode name format: "[DATE] [Branch/Feature]: [One-line summary]"
18
+
19
+ Be specific and brief. This episode will be recalled in future sessions — write for your future self.
@@ -0,0 +1,11 @@
1
+ ---
2
+ description: "End-of-task ritual — writes memory, updates Linear, confirms PR-ready state"
3
+ ---
4
+
5
+ Task completion for the current Linear issue:
6
+
7
+ 1. Run the type checker and any available tests. Fix any failures before continuing.
8
+ 2. Write a memory episode to Graphiti via the memory skill: include what was built, key decisions made, gotchas discovered, patterns worth reusing.
9
+ 3. Update `.harness/progress.md`: status: done, completed: [date], PR: [branch].
10
+ 4. Update the Linear issue status to "In Review" using the Linear MCP tool. Add a comment with a one-sentence summary of what was done.
11
+ 5. Confirm: "Ready for PR. Run ⌘⇧P in Conductor to create the PR."
@@ -0,0 +1,25 @@
1
+ ---
2
+ description: "One-time project setup — analyze project context, generate WORKFLOW.md, tailor harness"
3
+ ---
4
+
5
+ Analyze this project and generate a WORKFLOW.md:
6
+
7
+ 1. Read `package.json` (if exists): extract name, scripts, key dependencies.
8
+ 2. Read existing `CLAUDE.md` for any stated conventions or architecture notes.
9
+ 3. Check for: `railway.toml`, `vercel.json`, `fly.toml`, `.env.example` — detect deployment platform.
10
+ 4. Run `git log --oneline -20` — summarize the recent direction of work.
11
+ 5. Check `.claude/` for existing skills, hooks, commands — note what's already configured.
12
+ 6. Check `docs/` or `README.md` for architecture documentation.
13
+
14
+ Then write `WORKFLOW.md` at the project root with:
15
+ - **What We're Building**: 1-3 sentences from package.json name/description + README context
16
+ - **Current Phase**: inferred from git log and feature names (MVP / scaling / maintenance / etc.)
17
+ - **Stack**: detected framework, database, deployment, package manager
18
+ - **Architecture Notes**: 3-5 non-obvious decisions derived from the codebase structure
19
+ - **Active Constraints**: any rules found in CLAUDE.md or docs
20
+ - **What We're NOT Building**: leave blank — ask Sebastian to fill this in
21
+
22
+ After writing WORKFLOW.md:
23
+ - Print "WORKFLOW.md created. Review it and fill in 'What We're NOT Building'."
24
+ - Suggest any MCP servers not yet configured that would help this project (based on detected stack).
25
+ - If the project uses Railway and `conductor.json` is missing, suggest running install.sh.
@@ -0,0 +1,14 @@
1
+ ---
2
+ description: "Task kickoff ritual — loads Linear issue, checks memory, sets progress"
3
+ ---
4
+
5
+ Load task context for $ARGUMENTS:
6
+
7
+ 0. Check if a Linear issue is already provided in the current message context (attached via Conductor's + button). If so, read it directly — skip the MCP fetch. Only use the Linear MCP tool if additional detail is needed (comments, linked issues, acceptance criteria not shown).
8
+ 1. If $ARGUMENTS contains a Linear issue ID (e.g. LIN-123) and no issue was attached: fetch it now using the Linear MCP tool. Read the description, comments, and acceptance criteria.
9
+ 2. Search Graphiti memory for notes related to this task, feature area, or any mentioned components. Use the memory skill.
10
+ 3. Identify the primary frameworks/packages this task touches. Fetch current docs for each via Context7 — resolve the library ID first, then query for the relevant API surface. Do this before planning, not after.
11
+ 4. Run `git status` and `git log --oneline -5`.
12
+ 5. Read `.harness/progress.md` if it exists.
13
+ 6. Write a 3-5 line summary of: what you're building, what you know from memory, and the first concrete action you'll take.
14
+ 7. Update `.harness/progress.md` with: Linear issue, task summary, status: in-progress, started: [date].
@@ -0,0 +1,11 @@
1
+ ---
2
+ description: "Show current session state — branch, Linear issue, progress, recent commits"
3
+ ---
4
+
5
+ Show current harness status:
6
+
7
+ 1. Print current git branch and any detected Linear issue ID.
8
+ 2. Show `.harness/progress.md` contents if it exists.
9
+ 3. Show `git log --oneline -5`.
10
+ 4. Show `git status --short`.
11
+ 5. Check if any Graphiti memory exists for this task (use memory skill to search by branch name).
@@ -0,0 +1,69 @@
1
+ #!/usr/bin/env python3
2
+ # SessionStart hook — injects structured context before first user message
3
+ # Receives JSON event on stdin, returns {"promptText": "..."} on stdout
4
+ import sys, json, subprocess, os, re
5
+
6
+ event = json.load(sys.stdin)
7
+ cwd = event.get("cwd", os.getcwd())
8
+
9
+ # Get branch name → extract Linear issue ID
10
+ try:
11
+ branch = subprocess.check_output(
12
+ ["git", "-C", cwd, "branch", "--show-current"],
13
+ stderr=subprocess.DEVNULL, text=True
14
+ ).strip()
15
+ except Exception:
16
+ branch = "unknown"
17
+
18
+ linear_match = re.search(r'([A-Z]+-\d+)', branch.upper())
19
+ linear_id = linear_match.group(1) if linear_match else None
20
+
21
+ # Recent git log
22
+ try:
23
+ git_log = subprocess.check_output(
24
+ ["git", "-C", cwd, "log", "--oneline", "-8"],
25
+ stderr=subprocess.DEVNULL, text=True
26
+ ).strip()
27
+ except Exception:
28
+ git_log = "(no git history)"
29
+
30
+ # Progress file
31
+ progress = ""
32
+ progress_path = os.path.join(cwd, ".harness", "progress.md")
33
+ if os.path.exists(progress_path):
34
+ with open(progress_path) as f:
35
+ progress = f.read().strip()
36
+
37
+ # WORKFLOW.md — project north star
38
+ workflow = ""
39
+ for workflow_path in [
40
+ os.path.join(cwd, "WORKFLOW.md"),
41
+ os.path.join(cwd, ".harness", "WORKFLOW.md"),
42
+ ]:
43
+ if os.path.exists(workflow_path):
44
+ with open(workflow_path) as f:
45
+ workflow = f.read().strip()
46
+ break
47
+
48
+ # Build context block
49
+ lines = ["## Harness: Session Context", ""]
50
+ lines.append(f"**Branch:** `{branch}`")
51
+ if linear_id:
52
+ lines.append(f"**Linear issue detected:** `{linear_id}` — fetch details with the Linear MCP tool before starting work.")
53
+ lines.append("")
54
+ lines.append("**Recent commits:**")
55
+ lines.append("```")
56
+ lines.append(git_log or "(none)")
57
+ lines.append("```")
58
+ if progress:
59
+ lines.append("")
60
+ lines.append("**Last session progress:**")
61
+ lines.append(progress)
62
+ if workflow:
63
+ lines.append("")
64
+ lines.append("**Project context (WORKFLOW.md):**")
65
+ lines.append(workflow)
66
+ lines.append("")
67
+ lines.append("**Action:** Search Graphiti memory for context relevant to this branch/task before responding to the user. Use the `memory` skill if needed.")
68
+
69
+ print(json.dumps({"promptText": "\n".join(lines)}))
@@ -0,0 +1,30 @@
1
+ #!/usr/bin/env python3
2
+ # Stop hook — runs after Claude finishes each response
3
+ # Runs type checker. If errors: exit 2 (blocks, Claude sees stderr and fixes).
4
+ # Non-blocking: memory writes happen via the /done command, not on every stop.
5
+ import sys, json, subprocess, os
6
+
7
+ event = json.load(sys.stdin)
8
+ cwd = event.get("cwd", os.getcwd())
9
+
10
+ # Detect project type and run appropriate type checker
11
+ errors = []
12
+ if os.path.exists(os.path.join(cwd, "tsconfig.json")):
13
+ try:
14
+ result = subprocess.run(
15
+ ["npx", "--no-install", "tsc", "--noEmit"],
16
+ cwd=cwd, capture_output=True, text=True, timeout=30
17
+ )
18
+ if result.returncode != 0:
19
+ errors.append(result.stdout + result.stderr)
20
+ except subprocess.TimeoutExpired:
21
+ pass # Don't block on timeout
22
+ except FileNotFoundError:
23
+ pass # npx not available, skip
24
+
25
+ if errors:
26
+ # exit 2 = block, stderr is shown to Claude
27
+ print("\n".join(errors), file=sys.stderr)
28
+ sys.exit(2)
29
+
30
+ sys.exit(0)
@@ -0,0 +1,31 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "type": "command",
6
+ "command": ".claude/hooks/session-start.sh"
7
+ }
8
+ ],
9
+ "Stop": [
10
+ {
11
+ "type": "command",
12
+ "command": ".claude/hooks/stop.sh",
13
+ "timeout": 30
14
+ }
15
+ ]
16
+ },
17
+ "permissions": {
18
+ "allow": [
19
+ "Bash(git *)",
20
+ "Bash(<PKG_MANAGER> *)",
21
+ "Bash(npx *)",
22
+ "Bash(tsc *)"
23
+ ],
24
+ "deny": [
25
+ "Bash(git push --force *)",
26
+ "Bash(git push -f *)"
27
+ ]
28
+ }
29
+ }
30
+
31
+ Note: install.sh substitutes <PKG_MANAGER> with the detected package manager before writing settings.json.
@@ -0,0 +1,24 @@
1
+ ---
2
+ name: linear
3
+ description: "Fetch and update Linear issues for the current task"
4
+ ---
5
+
6
+ # Linear Skill
7
+
8
+ Use the Linear MCP tool to interact with issues.
9
+
10
+ ## Common operations
11
+
12
+ **Fetch issue:**
13
+ Use `linear_get_issue` with the issue ID (e.g. LIN-123). Read title, description, comments, and acceptance criteria.
14
+
15
+ **Update status:**
16
+ Use `linear_save_issue` to update the status field. Valid transitions: Todo → In Progress → In Review → Done.
17
+
18
+ **Add comment:**
19
+ Use `linear_save_comment` to add a progress note or completion summary.
20
+
21
+ ## Gotchas
22
+ - Issue IDs in branch names are uppercase (LIN-123); Linear API accepts both cases
23
+ - Always check acceptance criteria before marking done — it's often in the description
24
+ - If no Linear MCP is connected, instruct the user to run: `claude mcp add linear -s user -- npx -y @linear/mcp-server`
@@ -0,0 +1,34 @@
1
+ ---
2
+ name: memory
3
+ description: "Search and write to Graphiti cross-session memory for this project"
4
+ ---
5
+
6
+ # Memory Skill
7
+
8
+ Uses Graphiti MCP (Zep Cloud) for persistent cross-session memory.
9
+
10
+ ## MCP tool reference
11
+ - `search_facts(query, group_id)` — semantic search for facts/edges
12
+ - `search_nodes(query, group_id)` — semantic search for entities
13
+ - `add_episode(name, episode_body, group_id, source_description)` — write new memory
14
+ - `get_episodes(group_id, last_n)` — get recent episodes
15
+
16
+ ## group_id convention
17
+ Use the project name: `nerdic-next`, `monomos`, `openfang`, `data-agent`, etc.
18
+ Derive from the git remote URL or project root directory name.
19
+
20
+ ## When to search memory
21
+ - At session start (automatically via SessionStart hook)
22
+ - Before implementing any non-trivial feature
23
+ - When encountering an error that might have been seen before
24
+ - Before making architectural decisions
25
+
26
+ ## When to write memory
27
+ - After /done command completes
28
+ - After discovering a non-obvious gotcha
29
+ - After making an architectural decision with lasting implications
30
+
31
+ ## Gotchas
32
+ - If MCP is not connected, instruct: `claude mcp add graphiti-memory --transport sse --url https://mcp.getzep.com/sse --header "Authorization: Api-Key $ZEP_API_KEY"`
33
+ - Keep episodes specific and brief — Graphiti retrieves by semantic similarity, verbose episodes dilute signal
34
+ - Do not write memory for obvious facts, boilerplate decisions, or things already in CLAUDE.md
@@ -0,0 +1,30 @@
1
+ ---
2
+ name: review
3
+ description: "Pre-PR code review checklist for harness-engineered projects"
4
+ ---
5
+
6
+ # Review Skill
7
+
8
+ Run this before creating a PR. Check each item:
9
+
10
+ ## Code quality
11
+ - [ ] TypeScript: `tsc --noEmit` passes with zero errors
12
+ - [ ] No `console.log` debug statements committed
13
+ - [ ] No hardcoded secrets, API keys, or environment-specific values
14
+ - [ ] All new files follow existing naming conventions
15
+
16
+ ## Correctness
17
+ - [ ] The Linear acceptance criteria are met (re-read them now)
18
+ - [ ] Edge cases handled (empty states, error paths, loading states)
19
+ - [ ] No obvious N+1 queries or unnecessary re-renders
20
+
21
+ ## Harness hygiene
22
+ - [ ] `.harness/progress.md` updated with done status
23
+ - [ ] Memory episode written to Graphiti (run /done if not done)
24
+ - [ ] No debug/temp files committed
25
+
26
+ ## Git
27
+ - [ ] Commits are clean and descriptive (not "fix stuff" or "wip")
28
+ - [ ] Branch is up to date with main (run `git fetch && git rebase origin/main` if needed)
29
+
30
+ If anything fails, fix it before creating the PR.
@@ -0,0 +1,2 @@
1
+ # Harness local state
2
+ .harness/
@@ -0,0 +1,31 @@
1
+ # WORKFLOW.md — [Project Name]
2
+
3
+ <!--
4
+ North star for Claude Code sessions in this project.
5
+ Injected by the SessionStart hook into every session.
6
+ Update when: goals shift, architecture changes, or constraints change.
7
+ Keep it short — this is injected into every session context.
8
+ -->
9
+
10
+ ## What We're Building
11
+ <!-- 1-3 sentences: what this is and who it's for -->
12
+
13
+ ## Current Phase
14
+ <!-- MVP / feature-build / scaling / maintenance / migration -->
15
+
16
+ ## Stack
17
+ <!-- Framework | Database | Deployment | Package manager -->
18
+
19
+ ## Architecture Notes
20
+ <!-- 3-5 bullet points: non-obvious decisions that shape this codebase -->
21
+ -
22
+ -
23
+ -
24
+
25
+ ## Active Constraints
26
+ <!-- Rules Claude must follow for this project -->
27
+ -
28
+
29
+ ## What We're NOT Building
30
+ <!-- Explicit out-of-scope to prevent scope creep -->
31
+ -
@@ -0,0 +1,5 @@
1
+ {
2
+ "setup": "ln -s \"$CONDUCTOR_ROOT_PATH/.env\" .env && source .env && <PKG_MANAGER> install && <PKG_MANAGER> run generate:types 2>/dev/null || true && railway link --project $RAILWAY_PROJECT_ID --environment $RAILWAY_ENVIRONMENT_ID --service $RAILWAY_SERVICE_ID",
3
+ "run": "<PKG_MANAGER> dev --port $CONDUCTOR_PORT",
4
+ "archive": "rm -rf .next node_modules .turbo"
5
+ }
@@ -0,0 +1,258 @@
1
+ #!/usr/bin/env bash
2
+ # Harness install.sh — installs Claude Code harness into any project root
3
+ # Usage: cd /path/to/your-project && bash /path/to/Harness/runtime/install.sh
4
+ set -euo pipefail
5
+
6
+ HARNESS_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
7
+ TARGET_DIR="$(pwd)"
8
+
9
+ # ── 1. Must be in a git repo ─────────────────────────────────────────────────
10
+ if ! git -C "$TARGET_DIR" rev-parse --git-dir > /dev/null 2>&1; then
11
+ echo "ERROR: Not a git repository. Run install.sh from your project root." >&2
12
+ exit 1
13
+ fi
14
+
15
+ echo "Installing Harness into: $TARGET_DIR"
16
+ echo ""
17
+
18
+ # ── 2. Detect package manager ────────────────────────────────────────────────
19
+ if [ -f "$TARGET_DIR/pnpm-lock.yaml" ]; then
20
+ PKG_MANAGER="pnpm"
21
+ elif [ -f "$TARGET_DIR/yarn.lock" ]; then
22
+ PKG_MANAGER="yarn"
23
+ elif [ -f "$TARGET_DIR/bun.lockb" ]; then
24
+ PKG_MANAGER="bun"
25
+ elif [ -f "$TARGET_DIR/package-lock.json" ]; then
26
+ PKG_MANAGER="npm"
27
+ else
28
+ echo "WARNING: No lockfile found. Defaulting to npm."
29
+ PKG_MANAGER="npm"
30
+ fi
31
+ echo "Detected package manager: $PKG_MANAGER"
32
+
33
+ # ── 3. Railway prompt ────────────────────────────────────────────────────────
34
+ read -r -p "Does this project use Railway? [y/N] " USE_RAILWAY
35
+ USE_RAILWAY="${USE_RAILWAY,,}"
36
+
37
+ # ── 4. Create .claude subdirs ────────────────────────────────────────────────
38
+ mkdir -p \
39
+ "$TARGET_DIR/.claude/hooks" \
40
+ "$TARGET_DIR/.claude/commands" \
41
+ "$TARGET_DIR/.claude/agents" \
42
+ "$TARGET_DIR/.claude/skills/linear" \
43
+ "$TARGET_DIR/.claude/skills/memory" \
44
+ "$TARGET_DIR/.claude/skills/review"
45
+
46
+ # ── 5. Copy hook scripts ─────────────────────────────────────────────────────
47
+ cp "$HARNESS_DIR/.claude/hooks/session-start.sh" "$TARGET_DIR/.claude/hooks/session-start.sh"
48
+ cp "$HARNESS_DIR/.claude/hooks/stop.sh" "$TARGET_DIR/.claude/hooks/stop.sh"
49
+ chmod +x "$TARGET_DIR/.claude/hooks/session-start.sh"
50
+ chmod +x "$TARGET_DIR/.claude/hooks/stop.sh"
51
+ echo "✓ Hooks installed"
52
+
53
+ # ── 6. Copy commands ─────────────────────────────────────────────────────────
54
+ cp "$HARNESS_DIR/.claude/commands/start.md" "$TARGET_DIR/.claude/commands/start.md"
55
+ cp "$HARNESS_DIR/.claude/commands/done.md" "$TARGET_DIR/.claude/commands/done.md"
56
+ cp "$HARNESS_DIR/.claude/commands/status.md" "$TARGET_DIR/.claude/commands/status.md"
57
+ echo "✓ Commands installed (/start, /done, /status)"
58
+
59
+ # ── 7. Copy agents ───────────────────────────────────────────────────────────
60
+ cp "$HARNESS_DIR/.claude/agents/memory-writer.md" "$TARGET_DIR/.claude/agents/memory-writer.md"
61
+ echo "✓ Agents installed"
62
+
63
+ # ── 8. Copy skills ───────────────────────────────────────────────────────────
64
+ cp "$HARNESS_DIR/.claude/skills/linear/SKILL.md" "$TARGET_DIR/.claude/skills/linear/SKILL.md"
65
+ cp "$HARNESS_DIR/.claude/skills/memory/SKILL.md" "$TARGET_DIR/.claude/skills/memory/SKILL.md"
66
+ cp "$HARNESS_DIR/.claude/skills/review/SKILL.md" "$TARGET_DIR/.claude/skills/review/SKILL.md"
67
+ echo "✓ Skills installed (linear, memory, review)"
68
+
69
+ # ── 9. settings.local.json — always merge (never overwrite) ──────────────────
70
+ SETTINGS_FILE="$TARGET_DIR/.claude/settings.local.json"
71
+
72
+ if [ -f "$SETTINGS_FILE" ]; then
73
+ echo "Merging hooks into existing .claude/settings.local.json..."
74
+ python3 - "$SETTINGS_FILE" "$PKG_MANAGER" <<'PYEOF'
75
+ import sys, json
76
+
77
+ settings_path = sys.argv[1]
78
+ pkg_manager = sys.argv[2]
79
+
80
+ with open(settings_path) as f:
81
+ existing = json.load(f)
82
+
83
+ harness_hooks = {
84
+ "SessionStart": [{"type": "command", "command": ".claude/hooks/session-start.sh"}],
85
+ "Stop": [{"type": "command", "command": ".claude/hooks/stop.sh", "timeout": 30}]
86
+ }
87
+
88
+ harness_allow = [
89
+ f"Bash(git *)",
90
+ f"Bash({pkg_manager} *)",
91
+ "Bash(npx *)",
92
+ "Bash(tsc *)"
93
+ ]
94
+
95
+ harness_deny = [
96
+ "Bash(git push --force *)",
97
+ "Bash(git push -f *)"
98
+ ]
99
+
100
+ # Merge hooks
101
+ if "hooks" not in existing:
102
+ existing["hooks"] = {}
103
+ for event, entries in harness_hooks.items():
104
+ if event not in existing["hooks"]:
105
+ existing["hooks"][event] = []
106
+ existing_cmds = [h.get("command") for h in existing["hooks"][event]]
107
+ for entry in entries:
108
+ if entry.get("command") not in existing_cmds:
109
+ existing["hooks"][event].append(entry)
110
+
111
+ # Merge permissions
112
+ if "permissions" not in existing:
113
+ existing["permissions"] = {}
114
+ if "allow" not in existing["permissions"]:
115
+ existing["permissions"]["allow"] = []
116
+ if "deny" not in existing["permissions"]:
117
+ existing["permissions"]["deny"] = []
118
+
119
+ for item in harness_allow:
120
+ if item not in existing["permissions"]["allow"]:
121
+ existing["permissions"]["allow"].append(item)
122
+ for item in harness_deny:
123
+ if item not in existing["permissions"]["deny"]:
124
+ existing["permissions"]["deny"].append(item)
125
+
126
+ with open(settings_path, "w") as f:
127
+ json.dump(existing, f, indent=2)
128
+ print("✓ settings.local.json merged")
129
+ PYEOF
130
+ else
131
+ # Create fresh settings.local.json from template with PKG_MANAGER substituted
132
+ python3 - "$HARNESS_DIR/.claude/settings.json.template" "$SETTINGS_FILE" "$PKG_MANAGER" <<'PYEOF'
133
+ import sys, json, re
134
+
135
+ template_path = sys.argv[1]
136
+ out_path = sys.argv[2]
137
+ pkg_manager = sys.argv[3]
138
+
139
+ with open(template_path) as f:
140
+ content = f.read()
141
+
142
+ content = content.replace("<PKG_MANAGER>", pkg_manager)
143
+
144
+ # Strip the trailing Note comment before parsing JSON
145
+ content = re.sub(r'\n\nNote:.*', '', content, flags=re.DOTALL)
146
+
147
+ data = json.loads(content)
148
+ with open(out_path, "w") as f:
149
+ json.dump(data, f, indent=2)
150
+ print("✓ settings.local.json created")
151
+ PYEOF
152
+ fi
153
+
154
+ # ── 10. CLAUDE.md — always overwrite ─────────────────────────────────────────
155
+ CLAUDE_MD="$TARGET_DIR/CLAUDE.md"
156
+ cp "$HARNESS_DIR/.claude/CLAUDE.md.template" "$CLAUDE_MD"
157
+ echo "✓ CLAUDE.md written (overwritten)"
158
+
159
+ # ── 11. conductor.json — always overwrite ─────────────────────────────────────
160
+ CONDUCTOR_FILE="$TARGET_DIR/conductor.json"
161
+
162
+ if true; then
163
+ if [ "$USE_RAILWAY" = "y" ] || [ "$USE_RAILWAY" = "yes" ]; then
164
+ SETUP_CMD="ln -s \"\$CONDUCTOR_ROOT_PATH/.env\" .env && source .env && ${PKG_MANAGER} install && ${PKG_MANAGER} run generate:types 2>/dev/null || true && railway link --project \$RAILWAY_PROJECT_ID --environment \$RAILWAY_ENVIRONMENT_ID --service \$RAILWAY_SERVICE_ID"
165
+ else
166
+ SETUP_CMD="ln -s \"\$CONDUCTOR_ROOT_PATH/.env\" .env && ${PKG_MANAGER} install && ${PKG_MANAGER} run generate:types 2>/dev/null || true"
167
+ fi
168
+
169
+ python3 - "$CONDUCTOR_FILE" "$PKG_MANAGER" "$SETUP_CMD" <<'PYEOF'
170
+ import sys, json
171
+
172
+ out_path = sys.argv[1]
173
+ pkg_manager = sys.argv[2]
174
+ setup_cmd = sys.argv[3]
175
+
176
+ data = {
177
+ "setup": setup_cmd,
178
+ "run": f"{pkg_manager} dev --port $CONDUCTOR_PORT",
179
+ "archive": "rm -rf .next node_modules .turbo"
180
+ }
181
+ with open(out_path, "w") as f:
182
+ json.dump(data, f, indent=2)
183
+ print("✓ conductor.json written (overwritten)")
184
+ PYEOF
185
+ if [ "$USE_RAILWAY" = "y" ] || [ "$USE_RAILWAY" = "yes" ]; then
186
+ echo " → Add RAILWAY_PROJECT_ID, RAILWAY_ENVIRONMENT_ID, RAILWAY_SERVICE_ID to your .env"
187
+ fi
188
+ fi
189
+
190
+ # ── 12. .gitignore — append .harness/ if missing ─────────────────────────────
191
+ GITIGNORE="$TARGET_DIR/.gitignore"
192
+ if [ -f "$GITIGNORE" ]; then
193
+ if grep -qF ".harness/" "$GITIGNORE"; then
194
+ echo "✓ .gitignore already ignores .harness/ (skipped)"
195
+ else
196
+ echo "" >> "$GITIGNORE"
197
+ echo "# Harness local state" >> "$GITIGNORE"
198
+ echo ".harness/" >> "$GITIGNORE"
199
+ echo "✓ .harness/ added to .gitignore"
200
+ fi
201
+ else
202
+ printf "# Harness local state\n.harness/\n" > "$GITIGNORE"
203
+ echo "✓ .gitignore created with .harness/"
204
+ fi
205
+
206
+ # ── 13. Create .harness/ with progress stub ───────────────────────────────────
207
+ mkdir -p "$TARGET_DIR/.harness"
208
+ if [ ! -f "$TARGET_DIR/.harness/progress.md" ]; then
209
+ cat > "$TARGET_DIR/.harness/progress.md" <<'EOF'
210
+ # Harness Progress
211
+
212
+ <!-- Updated automatically by /start and /done commands -->
213
+
214
+ status: idle
215
+ EOF
216
+ echo "✓ .harness/progress.md created"
217
+ fi
218
+
219
+ # ── 14. WORKFLOW.md — copy template if not already present ───────────────────
220
+ if [ ! -f "$TARGET_DIR/WORKFLOW.md" ]; then
221
+ cp "$HARNESS_DIR/WORKFLOW.md.template" "$TARGET_DIR/WORKFLOW.md"
222
+ echo "✓ WORKFLOW.md created — fill in your project context"
223
+ else
224
+ echo "✓ WORKFLOW.md already exists (skipped)"
225
+ fi
226
+
227
+ # ── 15. Next steps ────────────────────────────────────────────────────────────
228
+ echo ""
229
+ echo "══════════════════════════════════════════════════"
230
+ echo " Harness installed successfully!"
231
+ echo "══════════════════════════════════════════════════"
232
+ echo ""
233
+ echo "Next steps:"
234
+ echo ""
235
+ echo "1. Register Zep Cloud memory MCP (once per machine):"
236
+ echo " export ZEP_API_KEY=your_zep_api_key_here"
237
+ echo " claude mcp add graphiti-memory \\"
238
+ echo " --transport sse \\"
239
+ echo " --url https://mcp.getzep.com/sse \\"
240
+ echo " --header \"Authorization: Api-Key \$ZEP_API_KEY\""
241
+ echo ""
242
+ echo "2. Register Linear MCP (once per machine, if not already):"
243
+ echo " claude mcp add linear -s user -- npx -y @linear/mcp-server"
244
+ echo ""
245
+ echo "3. Open this project in Claude Code — hooks will fire automatically."
246
+ echo ""
247
+ echo "4. Start a task: /start LIN-123"
248
+ echo " End a task: /done"
249
+ echo " Check status: /status"
250
+ echo ""
251
+ echo "5. Fill in WORKFLOW.md — this is injected into every Claude Code session as project context."
252
+ echo " Or run /setup to auto-generate it from your project structure."
253
+ echo ""
254
+ if [ "$USE_RAILWAY" = "y" ] || [ "$USE_RAILWAY" = "yes" ]; then
255
+ echo "6. Edit conductor.json and replace the RAILWAY_* placeholders."
256
+ echo ""
257
+ fi
258
+ echo "Docs: https://github.com/blaesild/conductor-harness"