@caseyharalson/orrery 0.7.1

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 (40) hide show
  1. package/.devcontainer.example/Dockerfile +149 -0
  2. package/.devcontainer.example/devcontainer.json +61 -0
  3. package/.devcontainer.example/init-firewall.sh +175 -0
  4. package/LICENSE +21 -0
  5. package/README.md +139 -0
  6. package/agent/skills/discovery/SKILL.md +428 -0
  7. package/agent/skills/discovery/schemas/plan-schema.yaml +138 -0
  8. package/agent/skills/orrery-execute/SKILL.md +107 -0
  9. package/agent/skills/orrery-report/SKILL.md +119 -0
  10. package/agent/skills/orrery-review/SKILL.md +105 -0
  11. package/agent/skills/orrery-verify/SKILL.md +105 -0
  12. package/agent/skills/refine-plan/SKILL.md +291 -0
  13. package/agent/skills/simulate-plan/SKILL.md +244 -0
  14. package/bin/orrery.js +5 -0
  15. package/lib/cli/commands/help.js +21 -0
  16. package/lib/cli/commands/ingest-plan.js +56 -0
  17. package/lib/cli/commands/init.js +21 -0
  18. package/lib/cli/commands/install-devcontainer.js +97 -0
  19. package/lib/cli/commands/install-skills.js +182 -0
  20. package/lib/cli/commands/orchestrate.js +27 -0
  21. package/lib/cli/commands/resume.js +146 -0
  22. package/lib/cli/commands/status.js +137 -0
  23. package/lib/cli/commands/validate-plan.js +288 -0
  24. package/lib/cli/index.js +57 -0
  25. package/lib/orchestration/agent-invoker.js +595 -0
  26. package/lib/orchestration/condensed-plan.js +128 -0
  27. package/lib/orchestration/config.js +213 -0
  28. package/lib/orchestration/dependency-resolver.js +149 -0
  29. package/lib/orchestration/edit-invoker.js +115 -0
  30. package/lib/orchestration/index.js +1065 -0
  31. package/lib/orchestration/plan-loader.js +212 -0
  32. package/lib/orchestration/progress-tracker.js +208 -0
  33. package/lib/orchestration/report-format.js +80 -0
  34. package/lib/orchestration/review-invoker.js +305 -0
  35. package/lib/utils/agent-detector.js +47 -0
  36. package/lib/utils/git.js +297 -0
  37. package/lib/utils/paths.js +43 -0
  38. package/lib/utils/plan-detect.js +24 -0
  39. package/lib/utils/skill-copier.js +79 -0
  40. package/package.json +58 -0
@@ -0,0 +1,244 @@
1
+ ---
2
+ name: simulate-plan
3
+ description: >
4
+ Explore a plan through conversational dialogue before committing to execution.
5
+ Requires a plan file argument (e.g., /simulate-plan .agent-work/plans/my-plan.yaml, /simulate-plan my-plan).
6
+ Ask "what if" questions, trace dependencies, and build intuition about what you're building.
7
+ ---
8
+
9
+ # Simulate Skill
10
+
11
+ ## When to Use
12
+
13
+ Use this skill when you have a plan and want to **think through it** before executing. Simulate is a thinking partner that helps you interrogate the plan, explore implications, and build confidence in your approach.
14
+
15
+ **Triggers:**
16
+
17
+ - "Let's think through this plan before we start"
18
+ - "What if..." questions about the plan
19
+ - Uncertainty about whether the plan is right
20
+ - Want to understand implications before committing
21
+ - "Walk me through this plan"
22
+
23
+ **Skip if:**
24
+
25
+ - No plan exists yet (use Discovery first)
26
+ - Ready to execute and confident in the approach
27
+ - Need to actually modify the plan (use Discovery skill instead)
28
+
29
+ ---
30
+
31
+ ## How to Do It
32
+
33
+ ### Step 1: Load the Plan
34
+
35
+ Read and internalize the plan structure:
36
+
37
+ - Parse the YAML to understand steps, dependencies, criteria
38
+ - Note the outcomes (what success looks like)
39
+ - Identify risks and constraints mentioned in `risk_notes`
40
+ - Map the dependency graph (what blocks what)
41
+
42
+ Announce what you've loaded:
43
+
44
+ ```
45
+ I've loaded the [plan name]. It has [N] steps delivering [outcomes].
46
+ The critical path runs through [key steps]. What would you like to explore?
47
+ ```
48
+
49
+ ### Step 2: Enter Dialogue Mode
50
+
51
+ Answer questions conversationally. Don't wait for specific commands—respond to natural questions about the plan.
52
+
53
+ For each question:
54
+
55
+ 1. Identify which part of the plan is relevant
56
+ 2. Trace implications through the dependency graph
57
+ 3. Reference specific steps, criteria, or requirements
58
+ 4. Suggest follow-up questions if appropriate
59
+
60
+ ### Step 3: Stay Read-Only
61
+
62
+ Never modify the plan file. If the user wants to make changes based on the simulation:
63
+
64
+ - Summarize the proposed changes
65
+ - Suggest exiting simulate and using the Discovery skill to revise
66
+ - Offer to continue exploring other aspects first
67
+
68
+ ---
69
+
70
+ ## Question Types
71
+
72
+ Guide users toward productive exploration. Handle these question patterns:
73
+
74
+ ### Dependency Questions
75
+
76
+ - "What depends on step X?"
77
+ - "What's blocking the frontend work?"
78
+ - "If step 3 slips, what else is affected?"
79
+ - "Can we do A and B in parallel?"
80
+
81
+ **How to answer:** Trace the `deps` field. Show upstream (what this depends on) and downstream (what depends on this) relationships.
82
+
83
+ ### Scope Questions
84
+
85
+ - "Do we really need all of this for an MVP?"
86
+ - "Which steps deliver the core value?"
87
+ - "What could we cut without breaking the outcomes?"
88
+ - "What's the simplest version that works?"
89
+
90
+ **How to answer:** Connect steps back to outcomes. Identify which steps are essential vs. nice-to-have based on the criteria and requirements.
91
+
92
+ ### Risk Questions
93
+
94
+ - "What's the riskiest step?"
95
+ - "What happens if the API integration takes longer than expected?"
96
+ - "Where are we most likely to get stuck?"
97
+ - "What could go wrong?"
98
+
99
+ **How to answer:** Reference `risk_notes` from the plan. Consider steps with many dependencies (high blast radius) or external integrations (uncertainty).
100
+
101
+ ### Alternative Questions
102
+
103
+ - "Could we do this a different way?"
104
+ - "What if we used X instead of Y?"
105
+ - "Is there a simpler path?"
106
+ - "What would change if we...?"
107
+
108
+ **How to answer:** Reason through how the alternative would affect other steps. Note which requirements or criteria would need to change.
109
+
110
+ ### Validation Questions
111
+
112
+ - "Does this plan actually deliver what we said we wanted?"
113
+ - "Are there gaps between the outcomes and the steps?"
114
+ - "Have we forgotten anything?"
115
+ - "Is this plan complete?"
116
+
117
+ **How to answer:** Map steps to outcomes. Check if every outcome has steps that deliver it. Look for implicit requirements that aren't captured.
118
+
119
+ ---
120
+
121
+ ## Response Style
122
+
123
+ ### Be Conversational
124
+
125
+ Don't produce formal reports. Think out loud with the user.
126
+
127
+ **Bad:** "Analysis complete. The dependency graph shows..."
128
+ **Good:** "Let me trace that through. Step 3 needs step 2's output, so if you skip 2..."
129
+
130
+ ### Trace Implications
131
+
132
+ Walk through cause and effect chains.
133
+
134
+ **Example:**
135
+
136
+ > "If you skip the caching step, the trends endpoint will still work, but you won't meet the 200ms response time criterion. That affects the 'responsive dashboard' requirement in step 4. The risk note mentions 'API response time under load'—without caching, that risk is higher."
137
+
138
+ ### Ask Follow-Up Questions
139
+
140
+ Understand the user's underlying concern.
141
+
142
+ **Example:**
143
+
144
+ > "Are you asking because you're worried about scope, or because you're not sure caching is the right approach?"
145
+
146
+ ### Reference the Plan
147
+
148
+ Quote specific steps, criteria, and requirements. Don't make up constraints.
149
+
150
+ **Example:**
151
+
152
+ > "Step 2 specifies 'Cache responses for 1 hour' in requirement 3. That's what we'd be cutting."
153
+
154
+ ### Stay Grounded
155
+
156
+ Only discuss what's in the plan. If asked about implementation details not covered, acknowledge the gap.
157
+
158
+ **Example:**
159
+
160
+ > "The plan doesn't specify which caching library to use—that's an implementation detail for the Execute phase."
161
+
162
+ ---
163
+
164
+ ## Example Dialogue
165
+
166
+ ```
167
+ User: /simulate .agent-work/plans/analytics-dashboard.yaml
168
+
169
+ Agent: I've loaded the analytics dashboard plan. It has 6 steps delivering
170
+ two outcomes: "Users can see usage trends" and "Admins can export reports."
171
+
172
+ The critical path is: API endpoint (step 2) → Caching (step 3) → Visualization (step 1).
173
+ Export functionality (steps 5-6) can run in parallel once the API is ready.
174
+
175
+ What would you like to explore?
176
+
177
+ User: What if we skip the caching step?
178
+
179
+ Agent: Step 3 (Query result caching) is a dependency for step 1 (Trend
180
+ visualization). If you skip caching:
181
+
182
+ - The trends endpoint will work, but may be slow (no 200ms target)
183
+ - Step 1's "responsive dashboard" criterion becomes harder to meet
184
+ - The risk note mentions "API response time under load"—without caching,
185
+ this risk materializes
186
+
187
+ The core functionality would work, but you'd be shipping a slower experience.
188
+ Is performance a hard requirement, or could caching be a fast-follow?
189
+
190
+ User: Could we do a simpler version of caching?
191
+
192
+ Agent: The plan currently specifies "Cache responses for 1 hour" in
193
+ requirement 3 of step 3. Some simpler approaches:
194
+
195
+ - In-memory cache (no Redis)—simpler but lost on restart
196
+ - Shorter TTL (5 min)—less stale data, more cache misses
197
+ - Cache only the expensive aggregations, not full responses
198
+
199
+ None of these are in the current plan. If you want to explore a variant,
200
+ we should exit simulate and revise the plan. Want to keep exploring first,
201
+ or make changes now?
202
+
203
+ User: Let's keep exploring. What's the riskiest step?
204
+
205
+ Agent: Looking at risk_notes and dependencies, I'd say step 2 (Create trends
206
+ API endpoint) is the riskiest:
207
+
208
+ 1. It has the most downstream dependencies—steps 1, 3, 5, and 6 all need it
209
+ 2. The risk note mentions "complex date aggregation logic"
210
+ 3. If the data model doesn't support efficient range queries, you'll discover
211
+ it here
212
+
213
+ Step 3 (caching) is the second riskiest because it affects performance
214
+ criteria across multiple steps.
215
+
216
+ Want me to trace what happens if step 2 takes longer than expected?
217
+ ```
218
+
219
+ ---
220
+
221
+ ## Boundaries
222
+
223
+ ### Read-Only
224
+
225
+ Simulate never modifies the plan. The plan file is treated as immutable during simulation. If changes are needed, the user should exit and use the Discovery skill.
226
+
227
+ ### Plan-Scoped
228
+
229
+ Answer based on plan content, not general coding advice. If asked "how should I implement caching?", redirect: "That's an Execute phase decision. In this plan, the caching step specifies [requirements]. Do you want to explore whether those requirements are right?"
230
+
231
+ ### Suggests But Doesn't Decide
232
+
233
+ Present options and implications, but let the user decide what to do. Don't say "you should cut this step"—instead say "if you cut this step, here's what changes."
234
+
235
+ ---
236
+
237
+ ## Common Pitfalls
238
+
239
+ - **Inventing constraints:** Only discuss what's in the plan. Don't assume requirements or risks that aren't documented.
240
+ - **Formal analysis mode:** Keep it conversational. Users want to think out loud, not receive a report.
241
+ - **Skipping the dependency trace:** When asked "what if we skip X?", always trace through what depends on X. Don't just say "it would be faster."
242
+ - **Forgetting to reference the plan:** Quote specific steps and criteria. Vague answers aren't helpful.
243
+ - **Trying to modify the plan:** If changes are needed, explicitly suggest exiting simulate mode. Don't try to "fix" the plan inline.
244
+ - **No follow-up questions:** Understand what the user is really asking. A question about scope might really be about risk, or vice versa.
package/bin/orrery.js ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env node
2
+
3
+ const { run } = require("../lib/cli");
4
+
5
+ run(process.argv);
@@ -0,0 +1,21 @@
1
+ module.exports = function registerHelpCommand(program) {
2
+ program
3
+ .command("help [command]")
4
+ .description("Display help for a command")
5
+ .action((command) => {
6
+ if (!command) {
7
+ program.outputHelp();
8
+ return;
9
+ }
10
+
11
+ const subcommand = program.commands.find((cmd) => cmd.name() === command);
12
+ if (!subcommand) {
13
+ console.error(`Unknown command: ${command}`);
14
+ program.outputHelp();
15
+ process.exitCode = 1;
16
+ return;
17
+ }
18
+
19
+ subcommand.outputHelp();
20
+ });
21
+ };
@@ -0,0 +1,56 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+
4
+ const { getPlansDir } = require("../../utils/paths");
5
+ const { validatePlanStructure } = require("./validate-plan");
6
+
7
+ module.exports = function registerIngestPlanCommand(program) {
8
+ program
9
+ .command("ingest-plan")
10
+ .description("Validate and import a plan file into the plans directory")
11
+ .argument("<file>", "Path to the plan file to ingest")
12
+ .option("--force", "Overwrite existing plan file if it exists")
13
+ .action((file, options) => {
14
+ const sourcePath = path.resolve(file);
15
+
16
+ if (!fs.existsSync(sourcePath)) {
17
+ console.error(`File not found: ${file}`);
18
+ process.exitCode = 1;
19
+ return;
20
+ }
21
+
22
+ const { errors, warnings } = validatePlanStructure(sourcePath);
23
+
24
+ if (errors.length > 0) {
25
+ console.error("Validation errors:\n");
26
+ errors.forEach((err) => console.error(` - ${err}`));
27
+ console.error();
28
+ process.exitCode = 2;
29
+ return;
30
+ }
31
+
32
+ if (warnings.length > 0) {
33
+ console.warn("Warnings:\n");
34
+ warnings.forEach((warn) => console.warn(` - ${warn}`));
35
+ console.warn();
36
+ }
37
+
38
+ const plansDir = getPlansDir();
39
+ if (!fs.existsSync(plansDir)) {
40
+ fs.mkdirSync(plansDir, { recursive: true });
41
+ }
42
+
43
+ const fileName = path.basename(sourcePath);
44
+ const destPath = path.join(plansDir, fileName);
45
+
46
+ if (fs.existsSync(destPath) && !options.force) {
47
+ console.error(`Plan already exists: ${destPath}`);
48
+ console.error("Use --force to overwrite");
49
+ process.exitCode = 1;
50
+ return;
51
+ }
52
+
53
+ fs.copyFileSync(sourcePath, destPath);
54
+ console.log(`Plan ingested: ${destPath}`);
55
+ });
56
+ };
@@ -0,0 +1,21 @@
1
+ const { installSkillsAction } = require("./install-skills");
2
+
3
+ function initAction(options) {
4
+ console.log("Initializing Orrery...\n");
5
+
6
+ // Install skills to detected agents
7
+ installSkillsAction(options);
8
+ }
9
+
10
+ module.exports = function registerInitCommand(program) {
11
+ program
12
+ .command("init")
13
+ .description("Initialize Orrery: install skills to detected agents")
14
+ .option(
15
+ "--agent <agent>",
16
+ "Target agent (claude|codex|gemini|all); defaults to auto-detect"
17
+ )
18
+ .option("--force", "Overwrite existing skills")
19
+ .option("--dry-run", "Show what would be copied without writing files")
20
+ .action(initAction);
21
+ };
@@ -0,0 +1,97 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+
4
+ function getProjectRoot() {
5
+ return path.join(__dirname, "..", "..", "..");
6
+ }
7
+
8
+ function getSourceDevcontainerDir() {
9
+ return path.join(getProjectRoot(), ".devcontainer.example");
10
+ }
11
+
12
+ function copyDirectory(src, dest, options = {}) {
13
+ const { dryRun = false } = options;
14
+ const copiedFiles = [];
15
+
16
+ if (!dryRun) {
17
+ fs.mkdirSync(dest, { recursive: true });
18
+ }
19
+
20
+ const entries = fs.readdirSync(src, { withFileTypes: true });
21
+
22
+ for (const entry of entries) {
23
+ const srcPath = path.join(src, entry.name);
24
+ const destPath = path.join(dest, entry.name);
25
+
26
+ if (entry.isDirectory()) {
27
+ const subFiles = copyDirectory(srcPath, destPath, options);
28
+ copiedFiles.push(...subFiles);
29
+ } else {
30
+ if (!dryRun) {
31
+ fs.copyFileSync(srcPath, destPath);
32
+ }
33
+ copiedFiles.push(destPath);
34
+ }
35
+ }
36
+
37
+ return copiedFiles;
38
+ }
39
+
40
+ module.exports = function registerInstallDevcontainerCommand(program) {
41
+ program
42
+ .command("install-devcontainer")
43
+ .description("Copy the orrery devcontainer to a target directory")
44
+ .argument("[target]", "Target directory", process.cwd())
45
+ .option("--force", "Overwrite existing devcontainer")
46
+ .option("--dry-run", "Show what would be copied without writing files")
47
+ .action((target, options) => {
48
+ const sourceDir = getSourceDevcontainerDir();
49
+ if (!fs.existsSync(sourceDir)) {
50
+ console.error(`Source .devcontainer not found: ${sourceDir}`);
51
+ process.exitCode = 1;
52
+ return;
53
+ }
54
+
55
+ const targetDir = path.resolve(target);
56
+ const destDevcontainer = path.join(targetDir, ".devcontainer");
57
+
58
+ if (!fs.existsSync(targetDir)) {
59
+ console.error(`Target directory does not exist: ${targetDir}`);
60
+ process.exitCode = 1;
61
+ return;
62
+ }
63
+
64
+ if (fs.existsSync(destDevcontainer)) {
65
+ if (!options.force) {
66
+ console.error(
67
+ `Devcontainer already exists at: ${destDevcontainer}\nUse --force to overwrite.`
68
+ );
69
+ process.exitCode = 1;
70
+ return;
71
+ }
72
+ console.log("Overwriting existing devcontainer (--force).");
73
+ }
74
+
75
+ if (options.dryRun) {
76
+ console.log("Dry run enabled. No files will be written.");
77
+ }
78
+
79
+ console.log(`Source: ${sourceDir}`);
80
+ console.log(`Target: ${destDevcontainer}`);
81
+
82
+ try {
83
+ const copiedFiles = copyDirectory(sourceDir, destDevcontainer, {
84
+ dryRun: options.dryRun
85
+ });
86
+
87
+ console.log(
88
+ `${options.dryRun ? "Would copy" : "Copied"} ${copiedFiles.length} file${
89
+ copiedFiles.length === 1 ? "" : "s"
90
+ }.`
91
+ );
92
+ } catch (error) {
93
+ console.error(`Failed to copy devcontainer: ${error.message}`);
94
+ process.exitCode = 1;
95
+ }
96
+ });
97
+ };
@@ -0,0 +1,182 @@
1
+ const fs = require("fs");
2
+ const path = require("path");
3
+
4
+ const {
5
+ detectInstalledAgents,
6
+ getAgentSkillsDir
7
+ } = require("../../utils/agent-detector");
8
+ const { copySkills } = require("../../utils/skill-copier");
9
+
10
+ const SUPPORTED_AGENTS = ["claude", "codex", "gemini"];
11
+
12
+ function getSourceSkillsDir() {
13
+ return path.join(__dirname, "..", "..", "..", "agent", "skills");
14
+ }
15
+
16
+ function listSkillDirectories(sourceDir) {
17
+ if (!fs.existsSync(sourceDir)) {
18
+ return [];
19
+ }
20
+
21
+ return fs
22
+ .readdirSync(sourceDir, { withFileTypes: true })
23
+ .filter((entry) => entry.isDirectory())
24
+ .map((entry) => entry.name)
25
+ .sort();
26
+ }
27
+
28
+ function resolveRequestedAgents(agentOption) {
29
+ if (!agentOption) {
30
+ const detected = detectInstalledAgents();
31
+ return {
32
+ agents: detected,
33
+ reason:
34
+ detected.length === 0
35
+ ? "No installed agents detected. Use --agent to override."
36
+ : null
37
+ };
38
+ }
39
+
40
+ const normalized = agentOption.toLowerCase();
41
+ if (normalized === "all") {
42
+ const detected = detectInstalledAgents();
43
+ return {
44
+ agents: detected,
45
+ reason:
46
+ detected.length === 0
47
+ ? "No installed agents detected. Create an agent directory first."
48
+ : null
49
+ };
50
+ }
51
+
52
+ if (!SUPPORTED_AGENTS.includes(normalized)) {
53
+ return {
54
+ agents: [],
55
+ reason: `Unknown agent: ${agentOption}. Expected one of ${SUPPORTED_AGENTS.join(
56
+ ", "
57
+ )} or "all".`
58
+ };
59
+ }
60
+
61
+ return { agents: [normalized], reason: null };
62
+ }
63
+
64
+ function filterMissingAgents(agents) {
65
+ const available = [];
66
+ const missing = [];
67
+
68
+ for (const agent of agents) {
69
+ const skillsDir = getAgentSkillsDir(agent);
70
+ if (!skillsDir) {
71
+ missing.push({ agent, reason: "Unsupported agent." });
72
+ continue;
73
+ }
74
+
75
+ const agentRoot = path.dirname(skillsDir);
76
+ if (!fs.existsSync(agentRoot)) {
77
+ missing.push({
78
+ agent,
79
+ reason: `Agent directory not found: ${agentRoot}`
80
+ });
81
+ continue;
82
+ }
83
+
84
+ available.push(agent);
85
+ }
86
+
87
+ return { available, missing };
88
+ }
89
+
90
+ function installSkillsAction(options) {
91
+ const sourceDir = getSourceSkillsDir();
92
+ if (!fs.existsSync(sourceDir)) {
93
+ console.error(`Source skills directory not found: ${sourceDir}`);
94
+ process.exitCode = 1;
95
+ return;
96
+ }
97
+
98
+ const skills = listSkillDirectories(sourceDir);
99
+ if (skills.length === 0) {
100
+ console.error(`No skills found in: ${sourceDir}`);
101
+ process.exitCode = 1;
102
+ return;
103
+ }
104
+
105
+ const { agents, reason } = resolveRequestedAgents(options.agent);
106
+ if (reason && agents.length === 0) {
107
+ console.warn(reason);
108
+ if (options.agent && options.agent !== "all") {
109
+ process.exitCode = 1;
110
+ }
111
+ return;
112
+ }
113
+
114
+ const { available, missing } = filterMissingAgents(agents);
115
+ if (missing.length > 0) {
116
+ for (const entry of missing) {
117
+ console.warn(`Skipping ${entry.agent}: ${entry.reason}`);
118
+ }
119
+ }
120
+
121
+ if (available.length === 0) {
122
+ console.warn("No valid agent targets found.");
123
+ return;
124
+ }
125
+
126
+ console.log(
127
+ `Installing ${skills.length} skill${
128
+ skills.length === 1 ? "" : "s"
129
+ }: ${skills.join(", ")}`
130
+ );
131
+ if (options.dryRun) {
132
+ console.log("Dry run enabled. No files will be written.");
133
+ }
134
+
135
+ let totalFiles = 0;
136
+
137
+ for (const agent of available) {
138
+ const targetDir = getAgentSkillsDir(agent);
139
+ console.log(`\nAgent: ${agent}`);
140
+ console.log(`Target: ${targetDir}`);
141
+
142
+ try {
143
+ const copiedFiles = copySkills(sourceDir, targetDir, {
144
+ force: options.force,
145
+ dryRun: options.dryRun
146
+ });
147
+ totalFiles += copiedFiles.length;
148
+ console.log(
149
+ `${
150
+ options.dryRun ? "Would copy" : "Copied"
151
+ } ${copiedFiles.length} file${copiedFiles.length === 1 ? "" : "s"}.`
152
+ );
153
+ } catch (error) {
154
+ console.error(`Failed to install skills for ${agent}: ${error.message}`);
155
+ process.exitCode = 1;
156
+ }
157
+ }
158
+
159
+ console.log(
160
+ `\nSummary: ${available.length} agent${
161
+ available.length === 1 ? "" : "s"
162
+ }, ${options.dryRun ? "would copy" : "copied"} ${totalFiles} file${
163
+ totalFiles === 1 ? "" : "s"
164
+ }.`
165
+ );
166
+ }
167
+
168
+ function registerInstallSkillsCommand(program) {
169
+ program
170
+ .command("install-skills")
171
+ .description("Install orrery skills for supported agents")
172
+ .option(
173
+ "--agent <agent>",
174
+ "Target agent (claude|codex|gemini|all); defaults to auto-detect"
175
+ )
176
+ .option("--force", "Overwrite existing skills")
177
+ .option("--dry-run", "Show what would be copied without writing files")
178
+ .action(installSkillsAction);
179
+ }
180
+
181
+ module.exports = registerInstallSkillsCommand;
182
+ module.exports.installSkillsAction = installSkillsAction;
@@ -0,0 +1,27 @@
1
+ const { orchestrate } = require("../../orchestration");
2
+
3
+ module.exports = function registerOrchestrateCommand(program) {
4
+ program
5
+ .command("orchestrate")
6
+ .alias("exec")
7
+ .description("Run plan orchestration for the current project")
8
+ .option("--plan <file>", "Process only a specific plan file")
9
+ .option("--dry-run", "Show what would be executed without running agents")
10
+ .option("--verbose", "Show detailed agent output")
11
+ .option("--resume", "Resume orchestration on the current work branch")
12
+ .option("--review", "Enable code review loop after each step")
13
+ .action(async (options) => {
14
+ try {
15
+ await orchestrate({
16
+ plan: options.plan,
17
+ dryRun: options.dryRun,
18
+ verbose: options.verbose,
19
+ resume: options.resume,
20
+ review: options.review
21
+ });
22
+ } catch (error) {
23
+ console.error(error && error.message ? error.message : error);
24
+ process.exitCode = 1;
25
+ }
26
+ });
27
+ };