@sulhadin/orchestrator 1.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.
- package/LICENSE +21 -0
- package/README.md +208 -0
- package/bin/index.js +163 -0
- package/package.json +23 -0
- package/template/.orchestra/README.md +366 -0
- package/template/.orchestra/agents/worker.md +112 -0
- package/template/.orchestra/milestones/.gitkeep +0 -0
- package/template/.orchestra/roles/architect.md +394 -0
- package/template/.orchestra/roles/backend-engineer.md +355 -0
- package/template/.orchestra/roles/code-reviewer.md +264 -0
- package/template/.orchestra/roles/frontend-engineer.md +413 -0
- package/template/.orchestra/roles/owner.md +161 -0
- package/template/.orchestra/roles/product-manager.md +553 -0
- package/template/CLAUDE.md +147 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Sulhadin Öney
|
|
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,208 @@
|
|
|
1
|
+
# Orchestra
|
|
2
|
+
|
|
3
|
+
A milestone-based orchestration system for [Claude Code](https://docs.anthropic.com/en/docs/claude-code). One terminal, one PM, one worker agent — features built end-to-end without manual role switching.
|
|
4
|
+
|
|
5
|
+
## The Problem
|
|
6
|
+
|
|
7
|
+
Building a feature with Claude Code typically looks like this:
|
|
8
|
+
|
|
9
|
+
```
|
|
10
|
+
You: "Build user authentication"
|
|
11
|
+
You: *opens terminal* "@architect — design the system"
|
|
12
|
+
You: *reads RFC, approves*
|
|
13
|
+
You: *opens terminal* "@backend — implement the API"
|
|
14
|
+
You: *opens terminal* "@reviewer — review the code"
|
|
15
|
+
You: *opens terminal* "@pm — close the feature"
|
|
16
|
+
```
|
|
17
|
+
|
|
18
|
+
You become the orchestrator. You carry context between sessions, sequence work in the right order, and manage the pipeline manually. This is tedious and error-prone.
|
|
19
|
+
|
|
20
|
+
## The Solution
|
|
21
|
+
|
|
22
|
+
Orchestra turns Claude Code's PM role into an autonomous orchestrator. You describe what you want, PM handles the rest:
|
|
23
|
+
|
|
24
|
+
```
|
|
25
|
+
You: "@pm"
|
|
26
|
+
You: "I want user authentication with JWT"
|
|
27
|
+
PM: creates milestone, grooms phases, dispatches worker agent
|
|
28
|
+
@architect writes RFC → you approve
|
|
29
|
+
@backend implements phase by phase → each phase = one commit
|
|
30
|
+
@frontend builds the UI → each phase = one commit
|
|
31
|
+
@reviewer reviews unpushed commits
|
|
32
|
+
you approve → PM pushes to origin
|
|
33
|
+
milestone closed.
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
One conversation. One terminal. PM drives everything.
|
|
37
|
+
|
|
38
|
+
## How It Works
|
|
39
|
+
|
|
40
|
+
```
|
|
41
|
+
You ←→ PM (always-on orchestrator)
|
|
42
|
+
│
|
|
43
|
+
├── Creates milestone with groomed phases
|
|
44
|
+
├── Spawns one worker agent (all roles loaded)
|
|
45
|
+
│
|
|
46
|
+
├── SendMessage("@architect: write RFC") → awaits
|
|
47
|
+
├── SendMessage("@backend: phase-1") → awaits → commit
|
|
48
|
+
├── SendMessage("@backend: phase-2") → awaits → commit
|
|
49
|
+
├── SendMessage("@frontend: phase-3") → awaits → commit
|
|
50
|
+
├── SendMessage("@reviewer: review") → awaits
|
|
51
|
+
│
|
|
52
|
+
├── You approve → PM pushes to origin
|
|
53
|
+
└── Milestone closed
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
### Key Concepts
|
|
57
|
+
|
|
58
|
+
**Milestones** — Each feature is a milestone. Everything lives in one directory:
|
|
59
|
+
|
|
60
|
+
```
|
|
61
|
+
.orchestra/milestones/M1-user-auth/
|
|
62
|
+
├── prd.md PM writes (what + why)
|
|
63
|
+
├── milestone.md Status, acceptance criteria
|
|
64
|
+
├── grooming.md Discussion notes, decisions
|
|
65
|
+
├── rfc.md Architect writes (how)
|
|
66
|
+
├── architecture.md Architect writes (system design)
|
|
67
|
+
├── design.md Frontend engineer writes (UI/UX)
|
|
68
|
+
├── adrs/ Architecture Decision Records
|
|
69
|
+
└── phases/
|
|
70
|
+
├── phase-1.md backend: DB schema → commit
|
|
71
|
+
├── phase-2.md backend: API endpoints → commit
|
|
72
|
+
└── phase-3.md frontend: login UI → commit
|
|
73
|
+
```
|
|
74
|
+
|
|
75
|
+
**Single Worker Session** — PM creates one agent with all roles loaded. No warmup on role switches. Architect decisions are in context when backend implements. Backend code is in context when reviewer reviews.
|
|
76
|
+
|
|
77
|
+
**Await-Based** — PM dispatches via `SendMessage`, blocks until the worker returns. No polling, no signal files. Results flow directly through the return value.
|
|
78
|
+
|
|
79
|
+
**Git-Native Review** — Reviewer doesn't need task files. Reviews unpushed commits on the current branch via `git diff origin/branch...HEAD`.
|
|
80
|
+
|
|
81
|
+
### Roles
|
|
82
|
+
|
|
83
|
+
| Role | What it does |
|
|
84
|
+
|------|-------------|
|
|
85
|
+
| **Product Manager** | Orchestrates everything. Creates milestones, dispatches worker, drives pipeline. |
|
|
86
|
+
| **Architect** | Designs technical solutions. Writes RFCs, ADRs. |
|
|
87
|
+
| **Backend Engineer** | Implements backend code + tests. One commit per phase. |
|
|
88
|
+
| **Frontend Engineer** | Designs + implements UI. One commit per phase. |
|
|
89
|
+
| **Code Reviewer** | Reviews unpushed commits. Returns approved or changes-requested. |
|
|
90
|
+
| **Owner** | Maintains Orchestra system files (roles, rules, structure). |
|
|
91
|
+
|
|
92
|
+
### Pipeline
|
|
93
|
+
|
|
94
|
+
```
|
|
95
|
+
PM creates milestone
|
|
96
|
+
→ Architect writes RFC
|
|
97
|
+
→ [You approve RFC]
|
|
98
|
+
→ Backend phases (sequential, each → commit)
|
|
99
|
+
→ Frontend phases (sequential, each → commit)
|
|
100
|
+
→ Reviewer reviews unpushed commits
|
|
101
|
+
→ Fix cycle if needed (one round, no re-review)
|
|
102
|
+
→ [You approve push]
|
|
103
|
+
→ PM pushes to origin, closes milestone
|
|
104
|
+
```
|
|
105
|
+
|
|
106
|
+
### Approval Gates
|
|
107
|
+
|
|
108
|
+
You only need to approve twice:
|
|
109
|
+
|
|
110
|
+
1. **RFC → Implementation** — after architect writes the technical design
|
|
111
|
+
2. **Push to origin** — after reviewer approves
|
|
112
|
+
|
|
113
|
+
Everything else is automatic.
|
|
114
|
+
|
|
115
|
+
## Install
|
|
116
|
+
|
|
117
|
+
```bash
|
|
118
|
+
npx @sulhadin/orchestrator
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
This will:
|
|
122
|
+
|
|
123
|
+
- Copy `.orchestra/` directory to your project root
|
|
124
|
+
- Create `CLAUDE.md` if it doesn't exist, or append Orchestra instructions to your existing one
|
|
125
|
+
|
|
126
|
+
### What Gets Installed
|
|
127
|
+
|
|
128
|
+
```
|
|
129
|
+
your-project/
|
|
130
|
+
├── .orchestra/
|
|
131
|
+
│ ├── README.md Orchestration rules
|
|
132
|
+
│ ├── agents/worker.md Worker agent prompt
|
|
133
|
+
│ ├── roles/ 6 role definitions
|
|
134
|
+
│ ├── milestones/ Feature work (empty)
|
|
135
|
+
|
|
136
|
+
└── CLAUDE.md Orchestra instructions for Claude
|
|
137
|
+
```
|
|
138
|
+
|
|
139
|
+
## Usage
|
|
140
|
+
|
|
141
|
+
Open Claude Code in your project and say `@pm`:
|
|
142
|
+
|
|
143
|
+
```
|
|
144
|
+
You: @pm
|
|
145
|
+
PM: "No active milestones. Ready for instructions."
|
|
146
|
+
|
|
147
|
+
You: "I want to add a health check endpoint"
|
|
148
|
+
PM: *discusses scope, challenges assumptions, proposes approach*
|
|
149
|
+
|
|
150
|
+
You: "Let's build it"
|
|
151
|
+
PM: *creates milestone, grooms phases, dispatches worker*
|
|
152
|
+
*reports progress after each phase*
|
|
153
|
+
*asks for approval at gates*
|
|
154
|
+
*pushes and closes milestone*
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Commands
|
|
158
|
+
|
|
159
|
+
| Command | Description |
|
|
160
|
+
|---------|------------|
|
|
161
|
+
| `@pm` | Activate Product Manager |
|
|
162
|
+
| `@backend` | Activate Backend Engineer (manual mode) |
|
|
163
|
+
| `@frontend` | Activate Frontend Engineer (manual mode) |
|
|
164
|
+
| `@reviewer` | Activate Code Reviewer (manual mode) |
|
|
165
|
+
| `@architect` | Activate Architect (manual mode) |
|
|
166
|
+
| `@owner` | Activate Owner (system maintenance) |
|
|
167
|
+
| `status` | Pipeline status report (PM only) |
|
|
168
|
+
| `orc help` | Show all commands |
|
|
169
|
+
|
|
170
|
+
### Manual Mode
|
|
171
|
+
|
|
172
|
+
You can still use roles directly without PM orchestration:
|
|
173
|
+
|
|
174
|
+
```
|
|
175
|
+
You: @backend
|
|
176
|
+
BE: *checks milestones for pending backend phases*
|
|
177
|
+
*starts working*
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
Autonomous and manual modes work side by side.
|
|
181
|
+
|
|
182
|
+
## Engineering Standards
|
|
183
|
+
|
|
184
|
+
Orchestra enforces these through role definitions and code review:
|
|
185
|
+
|
|
186
|
+
- **SOLID, KISS, YAGNI, DRY** — enforced by reviewer
|
|
187
|
+
- **Code without tests is not done** — backend and frontend engineers write tests as part of implementation
|
|
188
|
+
- **Conventional commits** — one commit per phase (`feat`, `fix`, `refactor`, etc.)
|
|
189
|
+
- **No `any` types, no unused code, no workarounds** — zero-tolerance rules
|
|
190
|
+
- **Design before code** — frontend engineer writes design specs before implementing
|
|
191
|
+
- **Grooming before implementation** — detailed phase planning before any code is written
|
|
192
|
+
- **Current library versions** — always verify with docs, never rely on memory
|
|
193
|
+
|
|
194
|
+
## Configuration
|
|
195
|
+
|
|
196
|
+
Customize roles by editing files in `.orchestra/roles/`. Each role file defines:
|
|
197
|
+
|
|
198
|
+
- Identity and boundaries
|
|
199
|
+
- File ownership (what it can/cannot write)
|
|
200
|
+
- Workflow steps
|
|
201
|
+
- Engineering principles
|
|
202
|
+
- Review checklists (for code-reviewer)
|
|
203
|
+
|
|
204
|
+
The worker agent prompt (`.orchestra/agents/worker.md`) controls how the subagent behaves when PM dispatches it.
|
|
205
|
+
|
|
206
|
+
## License
|
|
207
|
+
|
|
208
|
+
MIT
|
package/bin/index.js
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
const fs = require("fs");
|
|
4
|
+
const path = require("path");
|
|
5
|
+
|
|
6
|
+
const targetDir = process.cwd();
|
|
7
|
+
const templateDir = path.join(__dirname, "..", "template");
|
|
8
|
+
|
|
9
|
+
const ORCHESTRA_SECTION_START = "<!-- orchestra -->";
|
|
10
|
+
const ORCHESTRA_SECTION_END = "<!-- /orchestra -->";
|
|
11
|
+
|
|
12
|
+
const USER_DIRS = ["milestones"];
|
|
13
|
+
|
|
14
|
+
function copyDirRecursive(src, dest) {
|
|
15
|
+
if (!fs.existsSync(dest)) {
|
|
16
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const entries = fs.readdirSync(src, { withFileTypes: true });
|
|
20
|
+
|
|
21
|
+
for (const entry of entries) {
|
|
22
|
+
const srcPath = path.join(src, entry.name);
|
|
23
|
+
const destPath = path.join(dest, entry.name);
|
|
24
|
+
|
|
25
|
+
if (entry.isDirectory()) {
|
|
26
|
+
copyDirRecursive(srcPath, destPath);
|
|
27
|
+
} else {
|
|
28
|
+
fs.copyFileSync(srcPath, destPath);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
function rmDirRecursive(dir) {
|
|
34
|
+
if (!fs.existsSync(dir)) return;
|
|
35
|
+
|
|
36
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true });
|
|
37
|
+
|
|
38
|
+
for (const entry of entries) {
|
|
39
|
+
const fullPath = path.join(dir, entry.name);
|
|
40
|
+
if (entry.isDirectory()) {
|
|
41
|
+
rmDirRecursive(fullPath);
|
|
42
|
+
} else {
|
|
43
|
+
fs.unlinkSync(fullPath);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
fs.rmdirSync(dir);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
function extractOrchestraSection(content) {
|
|
51
|
+
const startIdx = content.indexOf(ORCHESTRA_SECTION_START);
|
|
52
|
+
if (startIdx === -1) return null;
|
|
53
|
+
|
|
54
|
+
const endIdx = content.indexOf(ORCHESTRA_SECTION_END, startIdx);
|
|
55
|
+
if (endIdx === -1) {
|
|
56
|
+
return content.slice(startIdx);
|
|
57
|
+
}
|
|
58
|
+
return content.slice(startIdx, endIdx + ORCHESTRA_SECTION_END.length);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function run() {
|
|
62
|
+
console.log("");
|
|
63
|
+
console.log(" Orchestra — AI Team Orchestration");
|
|
64
|
+
console.log(" Installing into: " + targetDir);
|
|
65
|
+
console.log("");
|
|
66
|
+
|
|
67
|
+
const orchestraSrc = path.join(templateDir, ".orchestra");
|
|
68
|
+
const orchestraDest = path.join(targetDir, ".orchestra");
|
|
69
|
+
const isUpgrade = fs.existsSync(orchestraDest);
|
|
70
|
+
|
|
71
|
+
if (isUpgrade) {
|
|
72
|
+
// Backup user data directories
|
|
73
|
+
const backups = {};
|
|
74
|
+
|
|
75
|
+
for (const dir of USER_DIRS) {
|
|
76
|
+
const dirPath = path.join(orchestraDest, dir);
|
|
77
|
+
const backupPath = path.join(targetDir, ".orchestra-backup-" + dir);
|
|
78
|
+
|
|
79
|
+
if (fs.existsSync(dirPath)) {
|
|
80
|
+
// Check if directory has real content (not just .gitkeep)
|
|
81
|
+
const files = fs.readdirSync(dirPath).filter((f) => f !== ".gitkeep");
|
|
82
|
+
if (files.length > 0) {
|
|
83
|
+
copyDirRecursive(dirPath, backupPath);
|
|
84
|
+
backups[dir] = backupPath;
|
|
85
|
+
console.log(" [~] Backed up " + dir + "/ (" + files.length + " items)");
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// Remove old .orchestra/
|
|
91
|
+
rmDirRecursive(orchestraDest);
|
|
92
|
+
console.log(" [~] Removed old .orchestra/");
|
|
93
|
+
|
|
94
|
+
// Copy fresh .orchestra/
|
|
95
|
+
copyDirRecursive(orchestraSrc, orchestraDest);
|
|
96
|
+
console.log(" [+] .orchestra/ installed (clean)");
|
|
97
|
+
|
|
98
|
+
// Restore user data
|
|
99
|
+
for (const [dir, backupPath] of Object.entries(backups)) {
|
|
100
|
+
const restorePath = path.join(orchestraDest, dir);
|
|
101
|
+
|
|
102
|
+
// Remove the template's empty dir first
|
|
103
|
+
if (fs.existsSync(restorePath)) {
|
|
104
|
+
rmDirRecursive(restorePath);
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
copyDirRecursive(backupPath, restorePath);
|
|
108
|
+
rmDirRecursive(backupPath);
|
|
109
|
+
console.log(" [+] Restored " + dir + "/");
|
|
110
|
+
}
|
|
111
|
+
} else {
|
|
112
|
+
// Fresh install
|
|
113
|
+
copyDirRecursive(orchestraSrc, orchestraDest);
|
|
114
|
+
console.log(" [+] .orchestra/ installed");
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// Handle CLAUDE.md
|
|
118
|
+
const templateClaudeMd = fs.readFileSync(
|
|
119
|
+
path.join(templateDir, "CLAUDE.md"),
|
|
120
|
+
"utf-8"
|
|
121
|
+
);
|
|
122
|
+
const targetClaudeMdPath = path.join(targetDir, "CLAUDE.md");
|
|
123
|
+
|
|
124
|
+
if (fs.existsSync(targetClaudeMdPath)) {
|
|
125
|
+
let existingContent = fs.readFileSync(targetClaudeMdPath, "utf-8");
|
|
126
|
+
const orchestraSection = extractOrchestraSection(templateClaudeMd);
|
|
127
|
+
|
|
128
|
+
if (!orchestraSection) {
|
|
129
|
+
console.log(" [!] Could not extract Orchestra section from template");
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
if (existingContent.includes(ORCHESTRA_SECTION_START)) {
|
|
134
|
+
const existingSection = extractOrchestraSection(existingContent);
|
|
135
|
+
if (existingSection) {
|
|
136
|
+
existingContent = existingContent.replace(
|
|
137
|
+
existingSection,
|
|
138
|
+
orchestraSection
|
|
139
|
+
);
|
|
140
|
+
}
|
|
141
|
+
console.log(" [~] CLAUDE.md updated (Orchestra section replaced)");
|
|
142
|
+
} else {
|
|
143
|
+
existingContent =
|
|
144
|
+
existingContent.trimEnd() + "\n\n" + orchestraSection + "\n";
|
|
145
|
+
console.log(" [+] CLAUDE.md updated (Orchestra section appended)");
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
fs.writeFileSync(targetClaudeMdPath, existingContent);
|
|
149
|
+
} else {
|
|
150
|
+
fs.copyFileSync(path.join(templateDir, "CLAUDE.md"), targetClaudeMdPath);
|
|
151
|
+
console.log(" [+] CLAUDE.md created");
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
console.log("");
|
|
155
|
+
console.log(" Done! Orchestra is ready.");
|
|
156
|
+
console.log("");
|
|
157
|
+
console.log(" Next steps:");
|
|
158
|
+
console.log(" 1. Open Claude Code in your project");
|
|
159
|
+
console.log(' 2. Say "@pm" to start orchestrating');
|
|
160
|
+
console.log("");
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
run();
|
package/package.json
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@sulhadin/orchestrator",
|
|
3
|
+
"version": "1.1.0",
|
|
4
|
+
"description": "AI Team Orchestration System — multi-role coordination for Claude Code",
|
|
5
|
+
"bin": {
|
|
6
|
+
"orchestrator": "bin/index.js"
|
|
7
|
+
},
|
|
8
|
+
"files": [
|
|
9
|
+
"bin/",
|
|
10
|
+
"template/"
|
|
11
|
+
],
|
|
12
|
+
"keywords": [
|
|
13
|
+
"claude",
|
|
14
|
+
"orchestrator",
|
|
15
|
+
"ai",
|
|
16
|
+
"agent",
|
|
17
|
+
"multi-agent",
|
|
18
|
+
"claude-code"
|
|
19
|
+
],
|
|
20
|
+
"author": "Sulhadin Öney",
|
|
21
|
+
"license": "MIT",
|
|
22
|
+
"packageManager": "yarn@4.13.0"
|
|
23
|
+
}
|