@sulhadin/orchestrator 3.0.0-beta → 3.0.0-beta.10

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 (32) hide show
  1. package/README.md +112 -46
  2. package/bin/build-template.js +78 -0
  3. package/bin/index.js +129 -5
  4. package/package.json +19 -5
  5. package/template/.claude/agents/conductor.md +148 -65
  6. package/template/.claude/agents/reviewer.md +32 -51
  7. package/template/.claude/commands/orchestra/adaptive.md +7 -0
  8. package/template/.claude/commands/orchestra/architect.md +7 -0
  9. package/template/.claude/commands/orchestra/backend.md +7 -0
  10. package/template/.claude/commands/orchestra/create-role.md +64 -0
  11. package/template/.claude/commands/orchestra/frontend.md +7 -0
  12. package/template/.claude/commands/orchestra/help.md +9 -9
  13. package/template/.claude/commands/orchestra/orchestrator.md +5 -0
  14. package/template/.claude/rules/acceptance-check.orchestra.md +5 -7
  15. package/template/.claude/rules/code-standards.orchestra.md +1 -1
  16. package/template/.claude/rules/phase-limits.orchestra.md +5 -16
  17. package/template/.claude/rules/role-boundaries.orchestra.md +20 -0
  18. package/template/.claude/rules/verification-gate.orchestra.md +5 -20
  19. package/template/.claude/skills/fullstack-infrastructure.orchestra.md +133 -718
  20. package/template/.orchestra/README.md +90 -112
  21. package/template/.orchestra/blueprints/README.md +4 -4
  22. package/template/.orchestra/config.yml +22 -0
  23. package/template/.orchestra/knowledge.md +78 -4
  24. package/template/.orchestra/roles/adaptive.md +7 -0
  25. package/template/.orchestra/roles/architect.md +6 -1
  26. package/template/.orchestra/roles/backend-engineer.md +6 -1
  27. package/template/.orchestra/roles/frontend-engineer.md +6 -1
  28. package/template/.orchestra/roles/orchestrator.md +5 -0
  29. package/template/.orchestra/roles/product-manager.md +37 -74
  30. package/template/CLAUDE.md +18 -59
  31. package/template/.claude/conductor.md +0 -146
  32. package/template/.orchestra/milestones/.gitkeep +0 -0
package/README.md CHANGED
@@ -1,6 +1,12 @@
1
1
  # Orchestra
2
2
 
3
- AI team orchestration for [Claude Code](https://docs.anthropic.com/en/docs/claude-code). Two terminalsPM plans, conductor builds.
3
+ AI team orchestration for [Claude Code](https://docs.anthropic.com/en/docs/claude-code). One terminal plans, another builds from idea to production.
4
+
5
+ ## What is Orchestra?
6
+
7
+ Orchestra turns a single Claude Code session into a coordinated development team. A Product Manager plans features, a Conductor executes them — switching between specialized roles (backend, frontend, architect) automatically. Each role has strict boundaries, every commit passes verification, and the system learns from past milestones.
8
+
9
+ No infrastructure. No API keys. Just markdown files and Claude Code.
4
10
 
5
11
  ## Install
6
12
 
@@ -8,79 +14,139 @@ AI team orchestration for [Claude Code](https://docs.anthropic.com/en/docs/claud
8
14
  npx @sulhadin/orchestrator
9
15
  ```
10
16
 
11
- ## Two Terminals
12
-
13
- ### Terminal 1: `/orchestra pm` — Planning
17
+ This installs `.orchestra/` (project data + config) and `.claude/` (agents, skills, rules, commands) into your project.
14
18
 
15
- PM is your strategic partner. Discuss ideas, challenge scope, create milestones.
19
+ ## How It Works
16
20
 
17
21
  ```
18
- You: /orchestra pm
19
- PM: "No active milestones. What's on your mind?"
20
-
21
- You: "I want user authentication with JWT"
22
- PM: *discusses, challenges, creates milestone with phases*
22
+ Terminal 1 (PM): Terminal 2 (Conductor):
23
+ /orchestra pm /orchestra start
24
+ │ │
25
+ ├─ Discuss features ├─ Scan milestones
26
+ ├─ Create milestones ├─ Activate architect → RFC
27
+ ├─ Groom phases ├─ Activate backend → code + tests
28
+ │ ├─ Activate frontend → UI
29
+ │ (plan M2 while M1 runs) ├─ Call reviewer → code review
30
+ │ ├─ Push → milestone done
31
+ │ └─ Loop → next milestone
23
32
  ```
24
33
 
25
- ### Terminal 2: `/orchestra start` — Execution
26
-
27
- Conductor picks up milestones and executes them autonomously.
34
+ ## Quick Example
28
35
 
36
+ **Terminal 1:**
37
+ ```
38
+ /orchestra pm
39
+ > "I want user authentication with JWT"
40
+ PM challenges scope, creates M1-user-auth with 3 phases
29
41
  ```
30
- You: /orchestra start
31
42
 
43
+ **Terminal 2:**
44
+ ```
45
+ /orchestra start
32
46
  📋 Starting M1-user-auth
33
- 🏗️ architect RFC... ✅ done
34
- ⚙️ backend DB schema... ✅ done
35
- ⚙️ backend API endpoints... done
36
- 🎨 frontend Login UI... ✅ done
37
- 🔍 reviewer reviewing... ✅ approved
47
+ 🏗️ architect RFC ready → user approves
48
+ ⚙️ backend phase-1: DB schema → committed
49
+ ⚙️ backend phase-2: API endpoints committed
50
+ 🎨 frontend phase-3: Login UI → committed
51
+ 🔍 reviewer approved
38
52
  🚦 Push? → yes
39
- ✅ M1-user-auth done.
40
-
41
- 📋 Starting M2-dashboard...
53
+ ✅ M1-user-auth done. Checking for next milestone...
42
54
  ```
43
55
 
44
56
  ## Commands
45
57
 
58
+ ### Pipeline
46
59
  | Command | What it does |
47
60
  |---------|-------------|
48
- | `/orchestra pm` | Plan features, create milestones |
49
- | `/orchestra start` | Execute milestones (asks at approval gates) |
50
- | `/orchestra start --auto` | Fully autonomous |
61
+ | `/orchestra pm` | Open PM terminal — plan features, create milestones |
62
+ | `/orchestra start` | Execute milestones autonomously (asks at approval gates) |
63
+ | `/orchestra start --auto` | Fully autonomous — warns once, then auto-push |
51
64
  | `/orchestra hotfix {desc}` | Ultra-fast fix: implement → verify → commit → push |
52
- | `/orchestra status` | Milestone status report |
65
+ | `/orchestra status` | Milestone status report (PM only) |
53
66
  | `/orchestra blueprint {name}` | Generate milestones from template |
67
+ | `/orchestra blueprint add` | Save current work as reusable template |
68
+ | `/orchestra create-role` | Create a new role interactively (Orchestrator only) |
54
69
  | `/orchestra help` | Show all commands |
55
70
 
71
+ ### Roles
72
+ | Command | Role |
73
+ |---------|------|
74
+ | `/orchestra pm` | Product Manager — plan and orchestrate |
75
+ | `/orchestra backend` | Backend Engineer — code + tests |
76
+ | `/orchestra frontend` | Frontend Engineer — UI + design |
77
+ | `/orchestra architect` | Architect — technical design |
78
+ | `/orchestra adaptive` | Adaptive expert — any domain (iOS, DevOps, ML...) |
79
+ | `/orchestra orchestrator` | System maintenance |
80
+
56
81
  ## Architecture
57
82
 
58
83
  ```
59
- .claude/ ← Claude Code integration
60
- ├── agents/conductor.md ← Autonomous executor
61
- ├── agents/reviewer.md Independent code review
62
- ├── skills/*.orchestra.md 14 domain checklists
63
- ├── rules/*.orchestra.md Discipline rules
64
- └── commands/orchestra/ /orchestra commands
65
-
66
- .orchestra/ ← Project data + config
67
- ├── config.yml Pipeline settings (customize per stack)
68
- ├── roles/ Role identities (slim)
69
- ├── blueprints/ Project templates
70
- ├── knowledge.md ← Project knowledge log
71
- └── milestones/ Your work
84
+ .claude/ ← Claude Code integration
85
+ ├── agents/
86
+ ├── conductor.md Autonomous milestone executor
87
+ │ └── reviewer.md Independent code review
88
+ ├── skills/*.orchestra.md 14 domain checklists
89
+ ├── rules/*.orchestra.md 8 discipline rules
90
+ └── commands/orchestra/ ← /orchestra commands
91
+
92
+ .orchestra/ Project data + config
93
+ ├── config.yml Pipeline settings (customize per stack)
94
+ ├── roles/ Role identities
95
+ ├── blueprints/ ← Project templates
96
+ ├── knowledge.md Project knowledge log
97
+ └── milestones/ ← Your work
98
+ ```
99
+
100
+ ## Key Features
101
+
102
+ **Config-driven pipeline** — `.orchestra/config.yml` controls everything: verification commands (customize for Go, Python, Rust), approval gates, thresholds, parallel execution. No hardcoded assumptions.
103
+
104
+ **Three complexity levels** — PM sets per milestone:
105
+ - `quick` → Engineer → Commit → Push (trivial changes)
106
+ - `standard` → Engineer → Review → Push (typical features)
107
+ - `full` → Architect → Engineer → Review → Push (complex work)
108
+
109
+ **Verification gate** — Tests + lint must pass before every commit. Commands come from config. Fails 3 times → phase marked failed, escalated to user.
110
+
111
+ **14 built-in skills** — Domain checklists for auth, CRUD, deployment, accessibility, React, testing, debugging, and more. PM assigns to phases, conductor loads them automatically.
112
+
113
+ **Blueprints** — Start from templates instead of scratch. `saas-starter` creates 5 milestones instantly. `blueprint add` saves your work as a reusable template.
114
+
115
+ **Learning system** — `knowledge.md` accumulates decisions and lessons. 5-line retrospective after each milestone. PM reads before grooming new work. System gets smarter over time.
116
+
117
+ **Role boundaries** — Enforced via `.claude/rules/`. PM cannot write code. Engineers cannot modify system files. Orchestrator cannot write features. Boundaries checked by file path, not by words.
118
+
119
+ **Stuck detection** — Detects repeated failures, circular fixes, over-engineering. Tries different approach once, then escalates. Auto mode skips to next phase.
120
+
121
+ ## Upgrading
122
+
123
+ ```bash
124
+ npx @sulhadin/orchestrator
72
125
  ```
73
126
 
127
+ Smart merge on upgrade:
128
+
129
+ | What | On upgrade |
130
+ |------|-----------|
131
+ | System files (roles, rules, agents, commands) | Updated to latest |
132
+ | Skills (`.orchestra.md`) | Updated |
133
+ | Skills (your custom `.md`) | Preserved |
134
+ | Blueprints (template) | Updated |
135
+ | Blueprints (your custom) | Preserved |
136
+ | milestones/ | Untouched |
137
+ | knowledge.md | Preserved |
138
+ | config.yml | Preserved |
139
+
74
140
  ## Documentation
75
141
 
76
- See [docs/](https://github.com/sulhadin/orchestrator/blob/main/docs/README.md) for full documentation:
142
+ Full docs at [docs/](https://github.com/sulhadin/orchestrator/blob/main/docs/README.md):
77
143
 
78
- - [Getting Started](https://github.com/sulhadin/orchestrator/blob/main/docs/getting-started.md)
79
- - [Commands](https://github.com/sulhadin/orchestrator/blob/main/docs/commands.md)
80
- - [Roles](https://github.com/sulhadin/orchestrator/blob/main/docs/roles.md)
81
- - [Features](https://github.com/sulhadin/orchestrator/blob/main/docs/features.md)
82
- - [Blueprints](https://github.com/sulhadin/orchestrator/blob/main/docs/blueprints.md)
83
- - [Skills](https://github.com/sulhadin/orchestrator/blob/main/docs/skills.md)
144
+ - [Getting Started](https://github.com/sulhadin/orchestrator/blob/main/docs/getting-started.md) — installation, first milestone, two-terminal model
145
+ - [Commands](https://github.com/sulhadin/orchestrator/blob/main/docs/commands.md) — all commands with examples
146
+ - [Roles](https://github.com/sulhadin/orchestrator/blob/main/docs/roles.md) — 6 roles + adaptive, responsibilities, boundaries
147
+ - [Features](https://github.com/sulhadin/orchestrator/blob/main/docs/features.md) — config, verification, skills, blueprints, and more
148
+ - [Blueprints](https://github.com/sulhadin/orchestrator/blob/main/docs/blueprints.md) — project templates, customization
149
+ - [Skills](https://github.com/sulhadin/orchestrator/blob/main/docs/skills.md) — domain checklists, creating new skills
84
150
 
85
151
  ## License
86
152
 
@@ -0,0 +1,78 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+
6
+ const rootDir = process.cwd();
7
+ const templateDir = path.join(rootDir, "template");
8
+
9
+ // System files to include in the template
10
+ const SYSTEM_PATHS = [
11
+ { src: ".claude/agents", dest: ".claude/agents" },
12
+ { src: ".claude/commands/orchestra", dest: ".claude/commands/orchestra" },
13
+ { src: ".claude/rules", dest: ".claude/rules", filter: (f) => f.endsWith(".orchestra.md") },
14
+ { src: ".claude/skills", dest: ".claude/skills", filter: (f) => f.endsWith(".orchestra.md") },
15
+ { src: ".orchestra/roles", dest: ".orchestra/roles" },
16
+ { src: ".orchestra/blueprints", dest: ".orchestra/blueprints" },
17
+ { src: ".orchestra/config.yml", dest: ".orchestra/config.yml" },
18
+ { src: ".orchestra/knowledge.md", dest: ".orchestra/knowledge.md" },
19
+ { src: ".orchestra/README.md", dest: ".orchestra/README.md" },
20
+ { src: "CLAUDE.md", dest: "CLAUDE.md" }
21
+ ];
22
+
23
+ function ensureDir(dir) {
24
+ if (!fs.existsSync(dir)) {
25
+ fs.mkdirSync(dir, { recursive: true });
26
+ }
27
+ }
28
+
29
+ function copyRecursive(src, dest, filter = null) {
30
+ const fullSrc = path.join(rootDir, src);
31
+ const fullDest = path.join(templateDir, dest);
32
+
33
+ if (!fs.existsSync(fullSrc)) {
34
+ console.warn(` [!] Source not found, skipping: ${src}`);
35
+ return;
36
+ }
37
+
38
+ const stat = fs.statSync(fullSrc);
39
+
40
+ if (stat.isDirectory()) {
41
+ ensureDir(fullDest);
42
+ const entries = fs.readdirSync(fullSrc, { withFileTypes: true });
43
+
44
+ for (const entry of entries) {
45
+ if (filter && !filter(entry.name)) continue;
46
+
47
+ const entrySrc = path.join(src, entry.name);
48
+ const entryDest = path.join(dest, entry.name);
49
+
50
+ const fullEntrySrc = path.join(rootDir, entrySrc);
51
+ const fullEntryDest = path.join(templateDir, entryDest);
52
+
53
+ if (entry.isSymbolicLink()) {
54
+ const linkTarget = fs.readlinkSync(fullEntrySrc);
55
+ if (fs.existsSync(fullEntryDest)) fs.unlinkSync(fullEntryDest);
56
+ fs.symlinkSync(linkTarget, fullEntryDest);
57
+ } else if (entry.isDirectory()) {
58
+ copyRecursive(entrySrc, entryDest, filter);
59
+ } else {
60
+ fs.copyFileSync(fullEntrySrc, fullEntryDest);
61
+ }
62
+ }
63
+ } else {
64
+ ensureDir(path.dirname(fullDest));
65
+ fs.copyFileSync(fullSrc, fullDest);
66
+ }
67
+ }
68
+
69
+ console.log("\n Orchestra — Template Builder");
70
+ console.log(" Packing root system files into template/...\n");
71
+
72
+ for (const item of SYSTEM_PATHS) {
73
+ copyRecursive(item.src, item.dest, item.filter);
74
+ console.log(` [+] Packed: ${item.src}`);
75
+ }
76
+
77
+ console.log("\n Done! Template is updated and ready for release.");
78
+ console.log(" Run 'yarn build' to test the installation from this template.\n");
package/bin/index.js CHANGED
@@ -39,8 +39,8 @@ function parseArgs() {
39
39
  flags.help = true;
40
40
  break;
41
41
  default:
42
- console.error(" Unknown flag: " + arg);
43
- process.exit(1);
42
+ // Ignore files passed by lint-staged or other tools
43
+ break;
44
44
  }
45
45
  }
46
46
 
@@ -71,6 +71,10 @@ function copyDirRecursive(src, dest) {
71
71
  const destPath = path.join(dest, entry.name);
72
72
  if (entry.isDirectory()) {
73
73
  copyDirRecursive(srcPath, destPath);
74
+ } else if (entry.isSymbolicLink()) {
75
+ const linkTarget = fs.readlinkSync(srcPath);
76
+ if (fs.existsSync(destPath)) fs.unlinkSync(destPath);
77
+ fs.symlinkSync(linkTarget, destPath);
74
78
  } else {
75
79
  fs.copyFileSync(srcPath, destPath);
76
80
  }
@@ -91,6 +95,123 @@ function rmDirRecursive(dir) {
91
95
  fs.rmdirSync(dir);
92
96
  }
93
97
 
98
+ /**
99
+ * Simple YAML config merge: adds new keys from template, preserves user values.
100
+ * Works with flat and one-level nested YAML (no deep nesting needed for config.yml).
101
+ */
102
+ function mergeConfigYaml(userContent, templateContent) {
103
+ const userLines = userContent.split("\n");
104
+ const templateLines = templateContent.split("\n");
105
+
106
+ // Parse YAML into { key: value } with awareness of sections
107
+ function parseYaml(lines) {
108
+ const result = {};
109
+ let currentSection = null;
110
+ for (const line of lines) {
111
+ // Skip comments and empty lines for parsing, but we'll preserve them
112
+ if (line.trim() === "" || line.trim().startsWith("#")) continue;
113
+ // Top-level section (no indent)
114
+ const sectionMatch = line.match(/^(\w[\w_-]*):\s*$/);
115
+ if (sectionMatch) {
116
+ currentSection = sectionMatch[1];
117
+ result[currentSection] = result[currentSection] || {};
118
+ continue;
119
+ }
120
+ // Nested key (2-space indent)
121
+ const nestedMatch = line.match(/^ (\w[\w_-]*):\s*(.+)?$/);
122
+ if (nestedMatch && currentSection) {
123
+ const key = nestedMatch[1];
124
+ const value = nestedMatch[2] || "";
125
+ if (!result[currentSection]) result[currentSection] = {};
126
+ // Check if this is a sub-section (value is empty, next lines are indented more)
127
+ if (!value) {
128
+ result[currentSection][key] = result[currentSection][key] || {};
129
+ } else {
130
+ result[currentSection][key] = value;
131
+ }
132
+ continue;
133
+ }
134
+ // Deeper nested key (4-space indent)
135
+ const deepMatch = line.match(/^ (\w[\w_-]*):\s*(.+)$/);
136
+ if (deepMatch && currentSection) {
137
+ // Find parent key (last nested key without value)
138
+ const parentKeys = Object.keys(result[currentSection] || {});
139
+ const parentKey = parentKeys.reverse().find(
140
+ (k) => typeof result[currentSection][k] === "object"
141
+ );
142
+ if (parentKey) {
143
+ result[currentSection][parentKey][deepMatch[1]] = deepMatch[2];
144
+ }
145
+ }
146
+ }
147
+ return result;
148
+ }
149
+
150
+ const userParsed = parseYaml(userLines);
151
+ const templateParsed = parseYaml(templateLines);
152
+
153
+ // Build merged config: start with template (has comments + structure), fill with user values
154
+ const merged = [];
155
+ let currentSection = null;
156
+ let currentSubSection = null;
157
+
158
+ for (const line of templateLines) {
159
+ const sectionMatch = line.match(/^(\w[\w_-]*):\s*$/);
160
+ if (sectionMatch) {
161
+ currentSection = sectionMatch[1];
162
+ currentSubSection = null;
163
+ merged.push(line);
164
+ continue;
165
+ }
166
+
167
+ const nestedMatch = line.match(/^ (\w[\w_-]*):\s*(.+)?$/);
168
+ if (nestedMatch && currentSection) {
169
+ const key = nestedMatch[1];
170
+ const templateValue = nestedMatch[2] || "";
171
+
172
+ if (!templateValue) {
173
+ // Sub-section header (e.g., "models:")
174
+ currentSubSection = key;
175
+ merged.push(line);
176
+ continue;
177
+ }
178
+
179
+ // Has a value → this is a flat key, reset sub-section
180
+ currentSubSection = null;
181
+
182
+ // Check if user has this key
183
+ const userSection = userParsed[currentSection];
184
+ if (userSection && userSection[key] !== undefined && typeof userSection[key] !== "object") {
185
+ merged.push(` ${key}: ${userSection[key]}`);
186
+ continue;
187
+ }
188
+ // New key — use template value
189
+ merged.push(line);
190
+ continue;
191
+ }
192
+
193
+ const deepMatch = line.match(/^ (\w[\w_-]*):\s*(.+)$/);
194
+ if (deepMatch && currentSection && currentSubSection) {
195
+ const key = deepMatch[1];
196
+ const userSection = userParsed[currentSection];
197
+ if (userSection && typeof userSection[currentSubSection] === "object") {
198
+ const userValue = userSection[currentSubSection][key];
199
+ if (userValue !== undefined) {
200
+ merged.push(` ${key}: ${userValue}`);
201
+ continue;
202
+ }
203
+ }
204
+ // New key — use template value
205
+ merged.push(line);
206
+ continue;
207
+ }
208
+
209
+ merged.push(line);
210
+ }
211
+
212
+ return merged.join("\n");
213
+ }
214
+
94
215
  function extractOrchestraSection(content) {
95
216
  const startIdx = content.indexOf(ORCHESTRA_SECTION_START);
96
217
  if (startIdx === -1) return null;
@@ -298,11 +419,14 @@ function run() {
298
419
  console.log(" [+] Restored knowledge.md");
299
420
  }
300
421
 
301
- // Restore config.yml (user's customizations take priority)
422
+ // Merge config.yml: new template keys added, user values preserved
302
423
  if (hasConfig && fs.existsSync(configBackup)) {
303
- fs.copyFileSync(configBackup, path.join(orchestraDest, "config.yml"));
424
+ const userConfig = fs.readFileSync(configBackup, "utf-8");
425
+ const templateConfig = fs.readFileSync(path.join(orchestraDest, "config.yml"), "utf-8");
426
+ const mergedConfig = mergeConfigYaml(userConfig, templateConfig);
427
+ fs.writeFileSync(path.join(orchestraDest, "config.yml"), mergedConfig);
304
428
  fs.unlinkSync(configBackup);
305
- console.log(" [+] Restored config.yml (user customizations preserved)");
429
+ console.log(" [+] Merged config.yml (user values preserved, new keys added)");
306
430
  }
307
431
  } else {
308
432
  // ── Fresh install ──
package/package.json CHANGED
@@ -1,9 +1,18 @@
1
1
  {
2
2
  "name": "@sulhadin/orchestrator",
3
- "version": "3.0.0-beta",
3
+ "version": "3.0.0-beta.10",
4
4
  "description": "AI Team Orchestration System — multi-role coordination for Claude Code",
5
- "bin": {
6
- "orchestrator": "bin/index.js"
5
+ "bin": "bin/index.js",
6
+ "scripts": {
7
+ "test": "node --test test/**/*.test.js",
8
+ "template": "node bin/build-template.js",
9
+ "prepare": "husky"
10
+ },
11
+ "lint-staged": {
12
+ "**/*.{js,md,yml,json}": [
13
+ "yarn template",
14
+ "yarn build"
15
+ ]
7
16
  },
8
17
  "files": [
9
18
  "bin/",
@@ -15,7 +24,8 @@
15
24
  "ai",
16
25
  "agent",
17
26
  "multi-agent",
18
- "claude-code"
27
+ "claude-code",
28
+ "team"
19
29
  ],
20
30
  "repository": {
21
31
  "type": "git",
@@ -27,5 +37,9 @@
27
37
  },
28
38
  "author": "Sulhadin Öney",
29
39
  "license": "MIT",
30
- "packageManager": "yarn@4.13.0"
40
+ "packageManager": "yarn@4.13.0",
41
+ "devDependencies": {
42
+ "husky": "^9.1.7",
43
+ "lint-staged": "^16.4.0"
44
+ }
31
45
  }