@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.
- package/README.md +112 -46
- package/bin/build-template.js +78 -0
- package/bin/index.js +129 -5
- package/package.json +19 -5
- package/template/.claude/agents/conductor.md +148 -65
- package/template/.claude/agents/reviewer.md +32 -51
- package/template/.claude/commands/orchestra/adaptive.md +7 -0
- package/template/.claude/commands/orchestra/architect.md +7 -0
- package/template/.claude/commands/orchestra/backend.md +7 -0
- package/template/.claude/commands/orchestra/create-role.md +64 -0
- package/template/.claude/commands/orchestra/frontend.md +7 -0
- package/template/.claude/commands/orchestra/help.md +9 -9
- package/template/.claude/commands/orchestra/orchestrator.md +5 -0
- package/template/.claude/rules/acceptance-check.orchestra.md +5 -7
- package/template/.claude/rules/code-standards.orchestra.md +1 -1
- package/template/.claude/rules/phase-limits.orchestra.md +5 -16
- package/template/.claude/rules/role-boundaries.orchestra.md +20 -0
- package/template/.claude/rules/verification-gate.orchestra.md +5 -20
- package/template/.claude/skills/fullstack-infrastructure.orchestra.md +133 -718
- package/template/.orchestra/README.md +90 -112
- package/template/.orchestra/blueprints/README.md +4 -4
- package/template/.orchestra/config.yml +22 -0
- package/template/.orchestra/knowledge.md +78 -4
- package/template/.orchestra/roles/adaptive.md +7 -0
- package/template/.orchestra/roles/architect.md +6 -1
- package/template/.orchestra/roles/backend-engineer.md +6 -1
- package/template/.orchestra/roles/frontend-engineer.md +6 -1
- package/template/.orchestra/roles/orchestrator.md +5 -0
- package/template/.orchestra/roles/product-manager.md +37 -74
- package/template/CLAUDE.md +18 -59
- package/template/.claude/conductor.md +0 -146
- 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).
|
|
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
|
-
|
|
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
|
-
|
|
19
|
+
## How It Works
|
|
16
20
|
|
|
17
21
|
```
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
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
|
-
|
|
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
|
|
34
|
-
⚙️ backend
|
|
35
|
-
⚙️ backend
|
|
36
|
-
🎨 frontend
|
|
37
|
-
🔍 reviewer
|
|
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` |
|
|
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/
|
|
60
|
-
├── agents/
|
|
61
|
-
├──
|
|
62
|
-
|
|
63
|
-
├──
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
├──
|
|
69
|
-
├──
|
|
70
|
-
├──
|
|
71
|
-
|
|
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
|
-
|
|
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
|
-
|
|
43
|
-
|
|
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
|
-
//
|
|
422
|
+
// Merge config.yml: new template keys added, user values preserved
|
|
302
423
|
if (hasConfig && fs.existsSync(configBackup)) {
|
|
303
|
-
fs.
|
|
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(" [+]
|
|
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
|
-
|
|
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
|
}
|