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 +21 -0
- package/README.md +137 -0
- package/bin/conductor-harness.js +11 -0
- package/package.json +21 -0
- package/runtime/.claude/CLAUDE.md.template +53 -0
- package/runtime/.claude/agents/memory-writer.md +19 -0
- package/runtime/.claude/commands/done.md +11 -0
- package/runtime/.claude/commands/setup.md +25 -0
- package/runtime/.claude/commands/start.md +14 -0
- package/runtime/.claude/commands/status.md +11 -0
- package/runtime/.claude/hooks/session-start.sh +69 -0
- package/runtime/.claude/hooks/stop.sh +30 -0
- package/runtime/.claude/settings.json.template +31 -0
- package/runtime/.claude/skills/linear/SKILL.md +24 -0
- package/runtime/.claude/skills/memory/SKILL.md +34 -0
- package/runtime/.claude/skills/review/SKILL.md +30 -0
- package/runtime/.gitignore.append +2 -0
- package/runtime/WORKFLOW.md.template +31 -0
- package/runtime/conductor.json.template +5 -0
- package/runtime/install.sh +258 -0
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,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"
|