@sulhadin/orchestrator 2.0.0 → 3.0.0-beta

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 (46) hide show
  1. package/README.md +49 -74
  2. package/bin/index.js +136 -82
  3. package/package.json +1 -1
  4. package/template/.claude/agents/conductor.md +146 -0
  5. package/template/.claude/agents/reviewer.md +88 -0
  6. package/template/.claude/commands/orchestra/blueprint.md +23 -0
  7. package/template/.claude/commands/orchestra/help.md +49 -0
  8. package/template/.claude/commands/orchestra/hotfix.md +13 -0
  9. package/template/.claude/commands/orchestra/pm.md +7 -0
  10. package/template/.claude/commands/orchestra/start.md +13 -0
  11. package/template/.claude/commands/orchestra/status.md +11 -0
  12. package/template/.claude/conductor.md +146 -0
  13. package/template/.claude/rules/acceptance-check.orchestra.md +13 -0
  14. package/template/.claude/rules/code-standards.orchestra.md +15 -0
  15. package/template/.claude/rules/commit-format.orchestra.md +14 -0
  16. package/template/.claude/rules/phase-limits.orchestra.md +21 -0
  17. package/template/.claude/rules/stuck-detection.orchestra.md +25 -0
  18. package/template/.claude/rules/testing-standards.orchestra.md +10 -0
  19. package/template/.claude/rules/verification-gate.orchestra.md +24 -0
  20. package/template/.claude/skills/fullstack-infrastructure.orchestra.md +810 -0
  21. package/template/.orchestra/README.md +10 -14
  22. package/template/.orchestra/config.yml +36 -0
  23. package/template/.orchestra/knowledge.md +4 -23
  24. package/template/.orchestra/roles/adaptive.md +14 -87
  25. package/template/.orchestra/roles/architect.md +17 -407
  26. package/template/.orchestra/roles/backend-engineer.md +13 -357
  27. package/template/.orchestra/roles/frontend-engineer.md +14 -419
  28. package/template/.orchestra/roles/orchestrator.md +48 -0
  29. package/template/.orchestra/roles/product-manager.md +73 -590
  30. package/template/CLAUDE.md +39 -139
  31. package/template/.orchestra/agents/worker.md +0 -557
  32. package/template/.orchestra/roles/code-reviewer.md +0 -265
  33. package/template/.orchestra/roles/owner.md +0 -290
  34. /package/template/{.orchestra/skills/accessibility.md → .claude/skills/accessibility.orchestra.md} +0 -0
  35. /package/template/{.orchestra/skills/auth-setup.md → .claude/skills/auth-setup.orchestra.md} +0 -0
  36. /package/template/{.orchestra/skills/best-practices.md → .claude/skills/best-practices.orchestra.md} +0 -0
  37. /package/template/{.orchestra/skills/code-optimizer.md → .claude/skills/code-optimizer.orchestra.md} +0 -0
  38. /package/template/{.orchestra/skills/core-web-vitals.md → .claude/skills/core-web-vitals.orchestra.md} +0 -0
  39. /package/template/{.orchestra/skills/crud-api.md → .claude/skills/crud-api.orchestra.md} +0 -0
  40. /package/template/{.orchestra/skills/debug.md → .claude/skills/debug.orchestra.md} +0 -0
  41. /package/template/{.orchestra/skills/deployment.md → .claude/skills/deployment.orchestra.md} +0 -0
  42. /package/template/{.orchestra/skills/frontend-design.md → .claude/skills/frontend-design.orchestra.md} +0 -0
  43. /package/template/{.orchestra/skills/react-best-practices.md → .claude/skills/react-best-practices.orchestra.md} +0 -0
  44. /package/template/{.orchestra/skills/review.md → .claude/skills/review.orchestra.md} +0 -0
  45. /package/template/{.orchestra/skills/testing.md → .claude/skills/testing.orchestra.md} +0 -0
  46. /package/template/{.orchestra/skills/web-quality-audit.md → .claude/skills/web-quality-audit.orchestra.md} +0 -0
package/README.md CHANGED
@@ -1,111 +1,86 @@
1
1
  # Orchestra
2
2
 
3
- AI team orchestration for [Claude Code](https://docs.anthropic.com/en/docs/claude-code). Two terminals — PM plans, worker builds.
3
+ AI team orchestration for [Claude Code](https://docs.anthropic.com/en/docs/claude-code). Two terminals — PM plans, conductor builds.
4
4
 
5
5
  ## Install
6
6
 
7
7
  ```bash
8
- npx @sulhadin/orchestrator@latest
9
- ```
10
-
11
- Skip permission prompts:
12
-
13
- ```bash
14
- npx @sulhadin/orchestrator@latest --dangerously-skip-permissions
8
+ npx @sulhadin/orchestrator
15
9
  ```
16
10
 
17
11
  ## Two Terminals
18
12
 
19
- ### Terminal 1: `#pm` — Planning
13
+ ### Terminal 1: `/orchestra pm` — Planning
20
14
 
21
- PM is your strategic partner. Discuss ideas, challenge scope, create milestones. PM never writes code — only plans.
15
+ PM is your strategic partner. Discuss ideas, challenge scope, create milestones.
22
16
 
23
17
  ```
24
- You: #pm
25
- PM: "No active milestones. Ready for instructions."
18
+ You: /orchestra pm
19
+ PM: "No active milestones. What's on your mind?"
26
20
 
27
21
  You: "I want user authentication with JWT"
28
- PM: *discusses, challenges, refines scope*
29
-
30
- You: "Create the milestone"
31
- PM: *creates prd.md, grooming.md, milestone.md, phases/*
32
- "🎯 M1-user-auth ready. Run #start in another terminal."
33
-
34
- You: "Let's also plan a dashboard"
35
- PM: *plans M2 while worker executes M1*
22
+ PM: *discusses, challenges, creates milestone with phases*
36
23
  ```
37
24
 
38
- PM is always available. Plan ahead while work runs in the other terminal.
39
-
40
- ### Terminal 2: `#start` — Execution
25
+ ### Terminal 2: `/orchestra start` Execution
41
26
 
42
- Worker picks up milestones and executes them autonomously. Loops to the next when done.
27
+ Conductor picks up milestones and executes them autonomously.
43
28
 
44
29
  ```
45
- You: #start
30
+ You: /orchestra start
46
31
 
47
32
  📋 Starting M1-user-auth
48
-
49
- 🏗️ #architectRFC + grooming validation...
50
- 🏗️ #architectRFC ready
51
- 🚦 Approve RFC? yes
52
-
53
- ⚙️ #backend phase-1: DB schema + migrations...
54
- ⚙️ #backend ✅ phase-1 done (feat(db): add auth tables)
55
-
56
- ⚙️ #backend ▶ phase-2: API endpoints + tests...
57
- ⚙️ #backend ✅ phase-2 done (feat(auth): add login endpoint)
58
-
59
- 🎨 #frontend ▶ phase-3: Login UI...
60
- 🎨 #frontend ✅ phase-3 done (feat(auth): add login page)
61
-
62
- 🔍 #reviewer ▶ reviewing unpushed commits...
63
- 🔍 #reviewer ✅ approved
64
-
65
- 🚦 Push to origin? → yes
33
+ 🏗️ architect ▶ RFC... ✅ done
34
+ ⚙️ backendDB schema... ✅ done
35
+ ⚙️ backend ▶ API endpoints... done
36
+ 🎨 frontend Login UI... ✅ done
37
+ 🔍 reviewer ▶ reviewing... ✅ approved
38
+ 🚦 Push? yes
66
39
  ✅ M1-user-auth done.
67
40
 
68
41
  📋 Starting M2-dashboard...
69
42
  ```
70
43
 
71
- Close the terminal, reopen, type `#start` — it resumes from where it left off.
72
-
73
44
  ## Commands
74
45
 
75
- | Command | Where | What it does |
76
- |---------|-------|-------------|
77
- | `#pm` | Terminal 1 | Plan features, create milestones |
78
- | `#start` | Terminal 2 | Execute milestones, asks at approval gates |
79
- | `#start --auto` | Terminal 2 | Confirms once, then fully autonomous |
80
- | `#hotfix {desc}` | Any | Ultra-fast fix: implement → verify → commit → push |
81
- | `#status` | Terminal 1 | Milestone status report |
82
- | `#help` | Any | Show all commands |
83
- | `#help skills` | Any | List available skills |
84
- | `#help blueprints` | Any | List available blueprints |
85
- | `#blueprint {name}` | Terminal 1 | Generate milestones from template |
86
- | `#blueprint add` | Terminal 1 | Save current work as reusable template |
87
-
88
- Manual roles (any terminal):
89
-
90
- | Command | Role |
91
- |---------|------|
92
- | `#backend` | Backend Engineer |
93
- | `#frontend` | Frontend Engineer |
94
- | `#reviewer` | Code Reviewer |
95
- | `#architect` | Architect |
96
- | `#owner` | System maintenance |
97
- | `#adaptive` | Adaptive expert (iOS, DevOps, ML, etc.) |
46
+ | Command | What it does |
47
+ |---------|-------------|
48
+ | `/orchestra pm` | Plan features, create milestones |
49
+ | `/orchestra start` | Execute milestones (asks at approval gates) |
50
+ | `/orchestra start --auto` | Fully autonomous |
51
+ | `/orchestra hotfix {desc}` | Ultra-fast fix: implement → verify → commit → push |
52
+ | `/orchestra status` | Milestone status report |
53
+ | `/orchestra blueprint {name}` | Generate milestones from template |
54
+ | `/orchestra help` | Show all commands |
55
+
56
+ ## Architecture
57
+
58
+ ```
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
72
+ ```
98
73
 
99
74
  ## Documentation
100
75
 
101
76
  See [docs/](https://github.com/sulhadin/orchestrator/blob/main/docs/README.md) for full documentation:
102
77
 
103
- - [Getting Started](https://github.com/sulhadin/orchestrator/blob/main/docs/getting-started.md) — installation, first milestone, two-terminal model
104
- - [Commands](https://github.com/sulhadin/orchestrator/blob/main/docs/commands.md) — all commands with examples
105
- - [Roles](https://github.com/sulhadin/orchestrator/blob/main/docs/roles.md) — 7 roles, responsibilities, boundaries
106
- - [Features](https://github.com/sulhadin/orchestrator/blob/main/docs/features.md) — verification gate, fast track, parallel, hotfix, and more
107
- - [Blueprints](https://github.com/sulhadin/orchestrator/blob/main/docs/blueprints.md) — project templates, `#blueprint add`
108
- - [Skills](https://github.com/sulhadin/orchestrator/blob/main/docs/skills.md) — domain checklists, creating new skills
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)
109
84
 
110
85
  ## License
111
86
 
package/bin/index.js CHANGED
@@ -9,7 +9,9 @@ const templateDir = path.join(__dirname, "..", "template");
9
9
  const ORCHESTRA_SECTION_START = "<!-- orchestra -->";
10
10
  const ORCHESTRA_SECTION_END = "<!-- /orchestra -->";
11
11
 
12
- const USER_DIRS = ["milestones", "skills", "blueprints"];
12
+ // User directories to preserve during upgrade
13
+ const ORCHESTRA_USER_DIRS = ["milestones", "blueprints"];
14
+ const CLAUDE_USER_DIRS = ["skills", "rules"];
13
15
 
14
16
  const ALLOW_PERMISSIONS = [
15
17
  "Bash(*)",
@@ -25,10 +27,7 @@ const ALLOW_PERMISSIONS = [
25
27
 
26
28
  function parseArgs() {
27
29
  const args = process.argv.slice(2);
28
- const flags = {
29
- skipPermissions: false,
30
- help: false,
31
- };
30
+ const flags = { skipPermissions: false, help: false };
32
31
 
33
32
  for (const arg of args) {
34
33
  switch (arg) {
@@ -53,7 +52,7 @@ function showHelp() {
53
52
  console.log(" Usage: npx @sulhadin/orchestrator [options]");
54
53
  console.log("");
55
54
  console.log(" Options:");
56
- console.log(" --dangerously-skip-permissions Auto-allow all tool permissions (no y/n prompts)");
55
+ console.log(" --dangerously-skip-permissions Auto-allow all tool permissions");
57
56
  console.log(" --help, -h Show this help");
58
57
  console.log("");
59
58
  console.log(" Examples:");
@@ -66,13 +65,10 @@ function copyDirRecursive(src, dest) {
66
65
  if (!fs.existsSync(dest)) {
67
66
  fs.mkdirSync(dest, { recursive: true });
68
67
  }
69
-
70
68
  const entries = fs.readdirSync(src, { withFileTypes: true });
71
-
72
69
  for (const entry of entries) {
73
70
  const srcPath = path.join(src, entry.name);
74
71
  const destPath = path.join(dest, entry.name);
75
-
76
72
  if (entry.isDirectory()) {
77
73
  copyDirRecursive(srcPath, destPath);
78
74
  } else {
@@ -83,9 +79,7 @@ function copyDirRecursive(src, dest) {
83
79
 
84
80
  function rmDirRecursive(dir) {
85
81
  if (!fs.existsSync(dir)) return;
86
-
87
82
  const entries = fs.readdirSync(dir, { withFileTypes: true });
88
-
89
83
  for (const entry of entries) {
90
84
  const fullPath = path.join(dir, entry.name);
91
85
  if (entry.isDirectory()) {
@@ -94,21 +88,47 @@ function rmDirRecursive(dir) {
94
88
  fs.unlinkSync(fullPath);
95
89
  }
96
90
  }
97
-
98
91
  fs.rmdirSync(dir);
99
92
  }
100
93
 
101
94
  function extractOrchestraSection(content) {
102
95
  const startIdx = content.indexOf(ORCHESTRA_SECTION_START);
103
96
  if (startIdx === -1) return null;
104
-
105
97
  const endIdx = content.indexOf(ORCHESTRA_SECTION_END, startIdx);
106
- if (endIdx === -1) {
107
- return content.slice(startIdx);
108
- }
98
+ if (endIdx === -1) return content.slice(startIdx);
109
99
  return content.slice(startIdx, endIdx + ORCHESTRA_SECTION_END.length);
110
100
  }
111
101
 
102
+ /**
103
+ * Smart merge for user directories (skills, rules, blueprints):
104
+ * - .orchestra.md files = system files → always updated from template
105
+ * - other .md files = user files → preserved
106
+ */
107
+ function smartMergeDir(backupPath, restorePath, templateDirPath) {
108
+ const templateFiles = fs.existsSync(templateDirPath)
109
+ ? fs.readdirSync(templateDirPath)
110
+ : [];
111
+ const backupFiles = fs.readdirSync(backupPath).filter((f) => f !== ".gitkeep");
112
+ let restored = 0;
113
+
114
+ for (const file of backupFiles) {
115
+ // User-created files: not in template AND not .orchestra.md
116
+ const isOrchestraFile = file.endsWith(".orchestra.md");
117
+ if (!templateFiles.includes(file) && !isOrchestraFile) {
118
+ const srcFile = path.join(backupPath, file);
119
+ const destFile = path.join(restorePath, file);
120
+ if (fs.statSync(srcFile).isDirectory()) {
121
+ copyDirRecursive(srcFile, destFile);
122
+ } else {
123
+ fs.copyFileSync(srcFile, destFile);
124
+ }
125
+ restored++;
126
+ }
127
+ }
128
+
129
+ return restored;
130
+ }
131
+
112
132
  function setupPermissions() {
113
133
  const claudeDir = path.join(targetDir, ".claude");
114
134
  const settingsPath = path.join(claudeDir, "settings.local.json");
@@ -126,10 +146,7 @@ function setupPermissions() {
126
146
  }
127
147
  }
128
148
 
129
- if (!settings.permissions) {
130
- settings.permissions = {};
131
- }
132
-
149
+ if (!settings.permissions) settings.permissions = {};
133
150
  const existing = settings.permissions.allow || [];
134
151
  const merged = [...new Set([...existing, ...ALLOW_PERMISSIONS])];
135
152
  settings.permissions.allow = merged;
@@ -138,7 +155,7 @@ function setupPermissions() {
138
155
 
139
156
  const added = merged.length - existing.length;
140
157
  if (added > 0) {
141
- console.log(" [+] Permissions configured (" + added + " rules added to .claude/settings.local.json)");
158
+ console.log(" [+] Permissions configured (" + added + " rules added)");
142
159
  } else {
143
160
  console.log(" [~] Permissions already configured");
144
161
  }
@@ -146,7 +163,6 @@ function setupPermissions() {
146
163
 
147
164
  function run() {
148
165
  const flags = parseArgs();
149
-
150
166
  if (flags.help) {
151
167
  showHelp();
152
168
  return;
@@ -158,87 +174,118 @@ function run() {
158
174
  console.log("");
159
175
 
160
176
  const orchestraSrc = path.join(templateDir, ".orchestra");
177
+ const claudeSrc = path.join(templateDir, ".claude");
161
178
  const orchestraDest = path.join(targetDir, ".orchestra");
179
+ const claudeDest = path.join(targetDir, ".claude");
162
180
  const isUpgrade = fs.existsSync(orchestraDest);
163
181
 
164
182
  if (isUpgrade) {
183
+ // ── Backup user data ──
165
184
  const backups = {};
166
185
 
167
- for (const dir of USER_DIRS) {
186
+ // .orchestra/ user dirs (milestones, blueprints)
187
+ for (const dir of ORCHESTRA_USER_DIRS) {
168
188
  const dirPath = path.join(orchestraDest, dir);
169
189
  const backupPath = path.join(targetDir, ".orchestra-backup-" + dir);
190
+ if (fs.existsSync(dirPath)) {
191
+ const files = fs.readdirSync(dirPath).filter((f) => f !== ".gitkeep");
192
+ if (files.length > 0) {
193
+ copyDirRecursive(dirPath, backupPath);
194
+ backups["orchestra:" + dir] = { backupPath, type: "orchestra", dir };
195
+ console.log(" [~] Backed up .orchestra/" + dir + "/ (" + files.length + " items)");
196
+ }
197
+ }
198
+ }
170
199
 
200
+ // .claude/ user dirs (skills, rules — user may have added custom ones)
201
+ for (const dir of CLAUDE_USER_DIRS) {
202
+ const dirPath = path.join(claudeDest, dir);
203
+ const backupPath = path.join(targetDir, ".claude-backup-" + dir);
171
204
  if (fs.existsSync(dirPath)) {
172
205
  const files = fs.readdirSync(dirPath).filter((f) => f !== ".gitkeep");
173
206
  if (files.length > 0) {
174
207
  copyDirRecursive(dirPath, backupPath);
175
- backups[dir] = backupPath;
176
- console.log(" [~] Backed up " + dir + "/ (" + files.length + " items)");
208
+ backups["claude:" + dir] = { backupPath, type: "claude", dir };
209
+ console.log(" [~] Backed up .claude/" + dir + "/ (" + files.length + " items)");
177
210
  }
178
211
  }
179
212
  }
180
213
 
181
- // Backup knowledge.md (single file, not a directory)
182
- const knowledgeSrc = path.join(orchestraDest, "knowledge.md");
214
+ // knowledge.md
215
+ const knowledgePath = path.join(orchestraDest, "knowledge.md");
183
216
  const knowledgeBackup = path.join(targetDir, ".orchestra-backup-knowledge.md");
184
217
  let hasKnowledge = false;
185
- if (fs.existsSync(knowledgeSrc)) {
186
- const content = fs.readFileSync(knowledgeSrc, "utf-8");
187
- // Backup if there are any entries after the "Active Knowledge" marker
188
- const activeMarker = "<!-- New entries go here";
189
- const markerIdx = content.indexOf(activeMarker);
190
- const hasEntries = markerIdx !== -1 && content.slice(markerIdx + activeMarker.length).trim().length > 10;
191
- if (hasEntries) {
192
- fs.copyFileSync(knowledgeSrc, knowledgeBackup);
218
+ if (fs.existsSync(knowledgePath)) {
219
+ const content = fs.readFileSync(knowledgePath, "utf-8");
220
+ const marker = "<!-- New entries go here";
221
+ const idx = content.indexOf(marker);
222
+ if (idx !== -1 && content.slice(idx + marker.length).trim().length > 10) {
223
+ fs.copyFileSync(knowledgePath, knowledgeBackup);
193
224
  hasKnowledge = true;
194
- console.log(" [~] Backed up knowledge.md (has project entries)");
225
+ console.log(" [~] Backed up knowledge.md");
195
226
  }
196
227
  }
197
228
 
229
+ // config.yml (user may have customized)
230
+ const configPath = path.join(orchestraDest, "config.yml");
231
+ const configBackup = path.join(targetDir, ".orchestra-backup-config.yml");
232
+ let hasConfig = false;
233
+ if (fs.existsSync(configPath)) {
234
+ fs.copyFileSync(configPath, configBackup);
235
+ hasConfig = true;
236
+ console.log(" [~] Backed up config.yml");
237
+ }
238
+
239
+ // ── Clean install ──
198
240
  rmDirRecursive(orchestraDest);
199
241
  console.log(" [~] Removed old .orchestra/");
200
242
 
243
+ // Remove old .claude/ orchestra files (agents, commands) but keep user files
244
+ const claudeOrchestraDirs = ["agents", "commands"];
245
+ for (const dir of claudeOrchestraDirs) {
246
+ const dirPath = path.join(claudeDest, dir);
247
+ if (fs.existsSync(dirPath)) {
248
+ // Only remove orchestra files, keep user's custom agents/commands
249
+ const files = fs.readdirSync(dirPath);
250
+ for (const file of files) {
251
+ if (file === "conductor.md" || file === "reviewer.md" || file === "orchestra") {
252
+ const fullPath = path.join(dirPath, file);
253
+ if (fs.statSync(fullPath).isDirectory()) {
254
+ rmDirRecursive(fullPath);
255
+ } else {
256
+ fs.unlinkSync(fullPath);
257
+ }
258
+ }
259
+ }
260
+ }
261
+ }
262
+
201
263
  copyDirRecursive(orchestraSrc, orchestraDest);
202
- console.log(" [+] .orchestra/ installed (clean)");
264
+ console.log(" [+] .orchestra/ installed");
203
265
 
204
- for (const [dir, backupPath] of Object.entries(backups)) {
205
- const restorePath = path.join(orchestraDest, dir);
206
- const templateDirPath = path.join(orchestraSrc, dir);
266
+ if (fs.existsSync(claudeSrc)) {
267
+ copyDirRecursive(claudeSrc, claudeDest);
268
+ console.log(" [+] .claude/ orchestra files installed");
269
+ }
270
+
271
+ // ── Restore user data ──
272
+ for (const [key, { backupPath, type, dir }] of Object.entries(backups)) {
273
+ const baseDest = type === "orchestra" ? orchestraDest : claudeDest;
274
+ const restorePath = path.join(baseDest, dir);
275
+ const templateDirPath = type === "orchestra"
276
+ ? path.join(orchestraSrc, dir)
277
+ : path.join(claudeSrc, dir);
207
278
 
208
279
  if (dir === "milestones") {
209
- // Milestones are fully user-owned — restore everything
210
- if (fs.existsSync(restorePath)) {
211
- rmDirRecursive(restorePath);
212
- }
280
+ if (fs.existsSync(restorePath)) rmDirRecursive(restorePath);
213
281
  copyDirRecursive(backupPath, restorePath);
214
- console.log(" [+] Restored " + dir + "/");
282
+ console.log(" [+] Restored milestones/");
215
283
  } else {
216
- // Skills & blueprints: template files get updated, user-created files are preserved
217
- // Template files are already in place from clean install.
218
- // Only copy back files that DON'T exist in template (user-created).
219
- const templateFiles = fs.existsSync(templateDirPath)
220
- ? fs.readdirSync(templateDirPath)
221
- : [];
222
- const backupFiles = fs.readdirSync(backupPath).filter((f) => f !== ".gitkeep");
223
- let restored = 0;
224
-
225
- for (const file of backupFiles) {
226
- if (!templateFiles.includes(file)) {
227
- const srcFile = path.join(backupPath, file);
228
- const destFile = path.join(restorePath, file);
229
- if (fs.statSync(srcFile).isDirectory()) {
230
- copyDirRecursive(srcFile, destFile);
231
- } else {
232
- fs.copyFileSync(srcFile, destFile);
233
- }
234
- restored++;
235
- }
236
- }
237
-
284
+ const restored = smartMergeDir(backupPath, restorePath, templateDirPath);
238
285
  if (restored > 0) {
239
- console.log(" [+] Restored " + restored + " user-created files in " + dir + "/");
286
+ console.log(" [+] Restored " + restored + " user files in " + dir + "/");
240
287
  }
241
- console.log(" [~] Updated template files in " + dir + "/");
288
+ console.log(" [~] Updated system files in " + dir + "/");
242
289
  }
243
290
 
244
291
  rmDirRecursive(backupPath);
@@ -250,16 +297,26 @@ function run() {
250
297
  fs.unlinkSync(knowledgeBackup);
251
298
  console.log(" [+] Restored knowledge.md");
252
299
  }
300
+
301
+ // Restore config.yml (user's customizations take priority)
302
+ if (hasConfig && fs.existsSync(configBackup)) {
303
+ fs.copyFileSync(configBackup, path.join(orchestraDest, "config.yml"));
304
+ fs.unlinkSync(configBackup);
305
+ console.log(" [+] Restored config.yml (user customizations preserved)");
306
+ }
253
307
  } else {
308
+ // ── Fresh install ──
254
309
  copyDirRecursive(orchestraSrc, orchestraDest);
255
310
  console.log(" [+] .orchestra/ installed");
311
+
312
+ if (fs.existsSync(claudeSrc)) {
313
+ copyDirRecursive(claudeSrc, claudeDest);
314
+ console.log(" [+] .claude/ installed");
315
+ }
256
316
  }
257
317
 
258
- // Handle CLAUDE.md
259
- const templateClaudeMd = fs.readFileSync(
260
- path.join(templateDir, "CLAUDE.md"),
261
- "utf-8"
262
- );
318
+ // ── Handle CLAUDE.md ──
319
+ const templateClaudeMd = fs.readFileSync(path.join(templateDir, "CLAUDE.md"), "utf-8");
263
320
  const targetClaudeMdPath = path.join(targetDir, "CLAUDE.md");
264
321
 
265
322
  if (fs.existsSync(targetClaudeMdPath)) {
@@ -274,15 +331,11 @@ function run() {
274
331
  if (existingContent.includes(ORCHESTRA_SECTION_START)) {
275
332
  const existingSection = extractOrchestraSection(existingContent);
276
333
  if (existingSection) {
277
- existingContent = existingContent.replace(
278
- existingSection,
279
- orchestraSection
280
- );
334
+ existingContent = existingContent.replace(existingSection, orchestraSection);
281
335
  }
282
336
  console.log(" [~] CLAUDE.md updated (Orchestra section replaced)");
283
337
  } else {
284
- existingContent =
285
- existingContent.trimEnd() + "\n\n" + orchestraSection + "\n";
338
+ existingContent = existingContent.trimEnd() + "\n\n" + orchestraSection + "\n";
286
339
  console.log(" [+] CLAUDE.md updated (Orchestra section appended)");
287
340
  }
288
341
 
@@ -292,7 +345,7 @@ function run() {
292
345
  console.log(" [+] CLAUDE.md created");
293
346
  }
294
347
 
295
- // Handle permissions
348
+ // ── Handle permissions ──
296
349
  if (flags.skipPermissions) {
297
350
  setupPermissions();
298
351
  }
@@ -302,7 +355,8 @@ function run() {
302
355
  console.log("");
303
356
  console.log(" Next steps:");
304
357
  console.log(" 1. Open Claude Code in your project");
305
- console.log(' 2. Say "#pm" to start orchestrating');
358
+ console.log(' 2. Type "/orchestra pm" to start planning');
359
+ console.log(' 3. Type "/orchestra start" in another terminal to execute');
306
360
  console.log("");
307
361
  }
308
362
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@sulhadin/orchestrator",
3
- "version": "2.0.0",
3
+ "version": "3.0.0-beta",
4
4
  "description": "AI Team Orchestration System — multi-role coordination for Claude Code",
5
5
  "bin": {
6
6
  "orchestrator": "bin/index.js"