@gravirei/reis 2.3.3 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/CHANGELOG.md CHANGED
@@ -5,6 +5,81 @@ All notable changes to REIS will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
+ ## [2.4.0] - 2026-01-22
9
+
10
+ ### Added - Kanban Board & Real Execution šŸš€
11
+
12
+ **Major Feature: Persistent Kanban Board**
13
+
14
+ A visual kanban board that displays automatically on workflow commands showing real-time cycle progress.
15
+
16
+ #### Kanban Board Features
17
+
18
+ - **Persistent Display** - Shows automatically on workflow commands (plan, execute, verify, debug, cycle, progress, resume, checkpoint)
19
+ - **4-Column Layout**:
20
+ - ALL PHASES - Shows all project phases (P1 P2 P3 P4...)
21
+ - IN PROGRESS - Current phase with wave/task breakdown
22
+ - CYCLE - Planning → Execute → Verify → Debug stages with progress bars
23
+ - COMPLETED - List of completed cycles
24
+ - **Wave Execution Visualization**:
25
+ - Wave list with tree structure (Wave 1/3 āœ“, Wave 2/3 ā—‰, Wave 3/3 ā—‹)
26
+ - Task status under current wave (ā”œ 2.1 āœ“, ā”” 2.4 ā—‹)
27
+ - Parallel execution indicator (ā«“2 of 4 tasks)
28
+ - Running/Waiting task breakdown
29
+ - **Subagent Status** - Shows active subagent (planner, executor, verifier, debugger)
30
+ - **Centered Progress Bars** - `[ā– ā– ā– ā– ā– 45% ā–‘ā–‘ā–‘ā–‘ā–‘]` format
31
+ - **3 Display Styles**: full, compact, minimal
32
+ - **Idle States**:
33
+ - "No active phase" + "Run: reis cycle" prompt
34
+ - "No cycles completed" for fresh projects
35
+ - "āœ“ All phases done!" when complete
36
+
37
+ #### Kanban Command
38
+
39
+ - `reis kanban` - Show current settings
40
+ - `reis kanban enable/disable/toggle` - Control display
41
+ - `reis kanban style <full|compact|minimal>` - Change display style
42
+ - `--no-kanban` flag to hide for single command
43
+
44
+ **Major Feature: Real Subagent Execution (Priority 1)**
45
+
46
+ Transformed REIS from a "prompt printer" to an actual "execution system".
47
+
48
+ #### Subagent Invocation API
49
+
50
+ - **New Module** (`lib/utils/subagent-invoker.js`) - Core API for programmatic subagent invocation
51
+ - Classes: `SubagentDefinition`, `ExecutionContext`, `InvocationResult`, `SubagentInvoker`
52
+ - Functions: `loadSubagentDefinition()`, `listSubagents()`, `buildExecutionContext()`, `invoke()`, `invokeParallel()`
53
+ - Event-based execution with progress tracking
54
+ - Timeout and error handling
55
+
56
+ #### Cycle Orchestrator Completion
57
+
58
+ - Implemented 5 TODOs that were previously stubbed:
59
+ - Actually execute plans via `invokeSubagent('reis_executor', ...)`
60
+ - Actually run verification via `invokeSubagent('reis_verifier', ...)`
61
+ - Actually run debug analysis via `invokeSubagent('reis_debugger', ...)`
62
+ - Actually execute fix plans
63
+ - Append completion records to STATE.md
64
+
65
+ #### Command Updates
66
+
67
+ - `reis execute <phase>` - Now invokes reis_executor subagent (with `--dry-run` fallback)
68
+ - `reis verify` - Now invokes reis_verifier subagent
69
+ - `reis debug` - Now invokes reis_debugger subagent
70
+ - New options: `--dry-run`, `--verbose`, `--timeout <ms>`
71
+
72
+ ### Fixed
73
+
74
+ - `reis whats-new` - Was showing "Command coming soon", now properly wired to implementation
75
+ - `reis docs` - Was showing "Command coming soon", now properly wired to implementation
76
+
77
+ ### Changed
78
+
79
+ - Help modal updated with new command options and compact format
80
+ - Added hidden commands to help: `reis add`, `reis insert`, `reis remove`
81
+ - Updated column widths in kanban board for better readability
82
+
8
83
  ## [2.3.0] - 2026-01-22
9
84
 
10
85
  ### Added - Decision Trees Support 🌳
package/bin/reis.js CHANGED
@@ -78,6 +78,7 @@ const configCmd = require('../lib/commands/config.js');
78
78
  const cycleCmd = require('../lib/commands/cycle.js');
79
79
  const decisionsCmd = require('../lib/commands/decisions.js');
80
80
  const treeCmd = require('../lib/commands/tree.js');
81
+ const kanbanCmd = require('../lib/commands/kanban.js');
81
82
 
82
83
  // Check for --help or -h flag before Commander parses
83
84
  if (process.argv.includes('--help') || process.argv.includes('-h')) {
@@ -95,6 +96,9 @@ program
95
96
  .description('REIS - Roadmap Execution & Implementation System')
96
97
  .usage('<command> [options]');
97
98
 
99
+ // Global option for kanban
100
+ program.option('--no-kanban', 'Hide kanban board for this command');
101
+
98
102
  // Getting Started Commands
99
103
  program
100
104
  .command('help')
@@ -130,7 +134,10 @@ program
130
134
  program
131
135
  .command('plan [phase]')
132
136
  .description('Create detailed plan for a phase')
133
- .action((phase) => planCmd({phase}));
137
+ .action((phase, options, command) => {
138
+ const globalOpts = command.parent?.opts() || {};
139
+ planCmd({ phase }, { noKanban: globalOpts.kanban === false });
140
+ });
134
141
 
135
142
  program
136
143
  .command('discuss [phase]')
@@ -150,7 +157,15 @@ program
150
157
  program
151
158
  .command('execute [phase]')
152
159
  .description('Execute a phase')
153
- .action((phase) => executeCmd({phase}));
160
+ .option('--dry-run', 'Show prompt without executing')
161
+ .option('-v, --verbose', 'Show detailed output')
162
+ .option('--no-commit', 'Skip auto-commit')
163
+ .option('--timeout <ms>', 'Execution timeout in milliseconds')
164
+ .action(async (phase, options, command) => {
165
+ const globalOpts = command.parent?.opts() || {};
166
+ const exitCode = await executeCmd({ phase }, { ...options, noKanban: globalOpts.kanban === false });
167
+ process.exit(exitCode);
168
+ });
154
169
 
155
170
  program
156
171
  .command('execute-plan <path>')
@@ -158,29 +173,37 @@ program
158
173
  .option('--wave', 'Enable wave-based execution (v2.0 feature)')
159
174
  .option('--dry-run', 'Show plan structure without executing')
160
175
  .option('--interactive', 'Step-by-step execution with prompts between waves')
161
- .action(async (path, options) => {
176
+ .action(async (planPath, options, command) => {
177
+ const globalOpts = command.parent?.opts() || {};
162
178
  await executePlanCmd({
163
- path,
179
+ path: planPath,
164
180
  wave: options.wave,
165
181
  dryRun: options.dryRun,
166
- interactive: options.interactive
182
+ interactive: options.interactive,
183
+ noKanban: globalOpts.kanban === false
167
184
  });
168
185
  });
169
186
 
170
187
  program
171
188
  .command('verify <target>')
172
189
  .description('Verify execution results against success criteria (uses reis_verifier subagent)')
190
+ .option('--dry-run', 'Show prompt without executing')
173
191
  .option('-v, --verbose', 'Show detailed verification output')
174
192
  .option('-s, --strict', 'Fail on warnings')
175
- .action(async (target, options) => {
176
- await verifyCmd(target, options);
193
+ .option('--timeout <ms>', 'Verification timeout in milliseconds')
194
+ .action(async (target, options, command) => {
195
+ const globalOpts = command.parent?.opts() || {};
196
+ await verifyCmd(target, { ...options, noKanban: globalOpts.kanban === false });
177
197
  });
178
198
 
179
199
  // Progress Commands
180
200
  program
181
201
  .command('progress')
182
202
  .description('Show current project progress')
183
- .action(() => progressCmd({}));
203
+ .action((options, command) => {
204
+ const globalOpts = command.parent?.opts() || {};
205
+ progressCmd({}, { noKanban: globalOpts.kanban === false });
206
+ });
184
207
 
185
208
  program
186
209
  .command('pause')
@@ -190,7 +213,10 @@ program
190
213
  program
191
214
  .command('resume')
192
215
  .description('Resume paused work')
193
- .action(() => resumeCmd({}));
216
+ .action((options, command) => {
217
+ const globalOpts = command.parent?.opts() || {};
218
+ resumeCmd({}, { noKanban: globalOpts.kanban === false });
219
+ });
194
220
 
195
221
  // Checkpoint Management Commands
196
222
  program
@@ -199,8 +225,9 @@ program
199
225
  .option('-c, --commit', 'Force git commit')
200
226
  .option('--no-commit', 'Skip git commit')
201
227
  .option('-m, --message <message>', 'Custom commit message')
202
- .action((subcommand, name, options) => {
203
- checkpointCmd({ subcommand, name, ...options });
228
+ .action((subcommand, name, options, command) => {
229
+ const globalOpts = command.parent?.opts() || {};
230
+ checkpointCmd({ subcommand, name, ...options, noKanban: globalOpts.kanban === false });
204
231
  });
205
232
 
206
233
  // Roadmap Management Commands
@@ -241,11 +268,14 @@ program
241
268
  program
242
269
  .command('debug [target]')
243
270
  .description('Analyze failures and generate fix plans (uses reis_debugger subagent)')
271
+ .option('--dry-run', 'Show prompt without executing')
244
272
  .option('-i, --input <path>', 'Path to DEBUG_INPUT.md or plan file')
245
273
  .option('-f, --focus <area>', 'Focus analysis on specific area')
246
274
  .option('-v, --verbose', 'Show detailed debug output')
247
- .action(async (target, options) => {
248
- await debugCmd(target, options);
275
+ .option('--timeout <ms>', 'Debug timeout in milliseconds')
276
+ .action(async (target, options, command) => {
277
+ const globalOpts = command.parent?.opts() || {};
278
+ await debugCmd(target, { ...options, noKanban: globalOpts.kanban === false });
249
279
  });
250
280
 
251
281
  program
@@ -256,8 +286,9 @@ program
256
286
  .option('--resume', 'Resume interrupted cycle')
257
287
  .option('--continue-on-fail', 'Continue even if verification fails')
258
288
  .option('-v, --verbose', 'Detailed output')
259
- .action(async (phaseOrPlan, options) => {
260
- await cycleCmd(phaseOrPlan, options);
289
+ .action(async (phaseOrPlan, options, command) => {
290
+ const globalOpts = command.parent?.opts() || {};
291
+ await cycleCmd(phaseOrPlan, { ...options, noKanban: globalOpts.kanban === false });
261
292
  });
262
293
 
263
294
  program
@@ -310,7 +341,17 @@ program
310
341
  await treeCmd(subcommand, args, options);
311
342
  });
312
343
 
344
+ program
345
+ .command('kanban [subcommand] [value]')
346
+ .description('Manage kanban board settings')
347
+ .action((subcommand, value) => {
348
+ const args = [subcommand, value].filter(Boolean);
349
+ kanbanCmd(args, {});
350
+ });
351
+
313
352
  const updateCmd = require('../lib/commands/update.js');
353
+ const whatsNewCmd = require('../lib/commands/whats-new.js');
354
+ const docsCmd = require('../lib/commands/docs.js');
314
355
 
315
356
  program
316
357
  .command('update')
@@ -323,14 +364,14 @@ program
323
364
  .command('whats-new')
324
365
  .description('Show what\'s new in latest version')
325
366
  .action(() => {
326
- console.log('Command coming soon in Phase 5-8');
367
+ whatsNewCmd({});
327
368
  });
328
369
 
329
370
  program
330
371
  .command('docs')
331
372
  .description('Open REIS documentation')
332
373
  .action(() => {
333
- console.log('Command coming soon in Phase 5-8');
374
+ docsCmd({});
334
375
  });
335
376
 
336
377
  program
@@ -6,6 +6,7 @@
6
6
  const StateManager = require('../utils/state-manager');
7
7
  const GitIntegration = require('../utils/git-integration');
8
8
  const { loadConfig } = require('../utils/config');
9
+ const { showKanbanBoard } = require('../utils/kanban-renderer');
9
10
  const fs = require('fs');
10
11
  const path = require('path');
11
12
 
@@ -301,6 +302,9 @@ async function deleteCheckpoint(name, options = {}) {
301
302
  * Main checkpoint command handler
302
303
  */
303
304
  async function checkpoint(options = {}) {
305
+ // Show kanban board (unless disabled)
306
+ showKanbanBoard({ noKanban: options?.noKanban });
307
+
304
308
  const subcommand = options.subcommand || 'list';
305
309
  const name = options.name;
306
310
 
@@ -2,6 +2,7 @@ const chalk = require('chalk');
2
2
  const { runCycle } = require('../utils/cycle-orchestrator');
3
3
  const stateManager = require('../utils/cycle-state-manager');
4
4
  const { showError, checkPlanningDir } = require('../utils/command-helpers');
5
+ const { showKanbanBoard } = require('../utils/kanban-renderer');
5
6
 
6
7
  /**
7
8
  * Cycle Command
@@ -14,6 +15,8 @@ const { showError, checkPlanningDir } = require('../utils/command-helpers');
14
15
  * @param {Object} options - Command options
15
16
  */
16
17
  async function cycle(phaseOrPlan, options = {}) {
18
+ // Show kanban board (unless disabled)
19
+ showKanbanBoard({ noKanban: options?.noKanban });
17
20
  try {
18
21
  // Show welcome banner
19
22
  showBanner(phaseOrPlan, options);
@@ -2,6 +2,7 @@ const fs = require('fs');
2
2
  const path = require('path');
3
3
  const chalk = require('chalk');
4
4
  const { showError, showSuccess, showInfo, checkPlanningDir } = require('../utils/command-helpers.js');
5
+ const { showKanbanBoard } = require('../utils/kanban-renderer');
5
6
 
6
7
  /**
7
8
  * Debug command - Analyze verification failures and generate fix plans
@@ -9,6 +10,8 @@ const { showError, showSuccess, showInfo, checkPlanningDir } = require('../utils
9
10
  * @param {Object} options - Command options
10
11
  */
11
12
  async function debugCommand(target, options = {}) {
13
+ // Show kanban board (unless disabled)
14
+ showKanbanBoard({ noKanban: options?.noKanban });
12
15
  console.log(chalk.blue('\nšŸ” REIS Debugger\n'));
13
16
 
14
17
  // Validate REIS project
@@ -58,6 +61,13 @@ async function debugCommand(target, options = {}) {
58
61
  process.exit(1);
59
62
  }
60
63
 
64
+ // Handle dry-run case
65
+ if (result.dryRun) {
66
+ console.log(chalk.yellow('\nāš ļø Dry run complete - no actual debugging performed'));
67
+ console.log(chalk.gray('Run without --dry-run to perform actual analysis'));
68
+ process.exit(0);
69
+ }
70
+
61
71
  // 7. Display results
62
72
  console.log(chalk.green('\nāœ… Analysis complete!\n'));
63
73
 
@@ -215,39 +225,92 @@ function buildDebuggerPrompt(debugInput, context, issueType, options) {
215
225
 
216
226
  /**
217
227
  * Invoke debugger subagent via Rovo Dev
228
+ * @param {string} prompt - Debug prompt
229
+ * @param {object} options - Command options
230
+ * @returns {Promise<object>} Debug results
218
231
  */
219
232
  async function invokeDebugger(prompt, options) {
220
- // TODO: Integrate with Rovo Dev API or CLI
221
- // For now, output the prompt that would be sent to the subagent
222
-
223
- console.log(chalk.yellow('āš ļø Note: Subagent invocation pending Rovo Dev integration'));
224
- console.log(chalk.gray('The debugger prompt has been generated and would be sent to reis_debugger.'));
225
- console.log();
226
- console.log(chalk.cyan('Prompt Preview:'));
227
- console.log(chalk.gray('─'.repeat(80)));
228
- console.log(prompt.substring(0, 500) + '...');
229
- console.log(chalk.gray('─'.repeat(80)));
230
- console.log();
231
-
232
- // Create debug output directory
233
233
  const debugDir = path.join(process.cwd(), '.planning', 'debug');
234
+
235
+ // Ensure debug directory exists
234
236
  if (!fs.existsSync(debugDir)) {
235
237
  fs.mkdirSync(debugDir, { recursive: true });
236
238
  }
237
-
238
- // Save prompt for manual invocation
239
- const promptPath = path.join(debugDir, 'DEBUGGER_PROMPT.txt');
240
- fs.writeFileSync(promptPath, prompt, 'utf-8');
241
- console.log(chalk.gray(`Prompt saved to: ${promptPath}`));
242
- console.log();
243
239
 
244
- return {
245
- success: true,
246
- outputs: {
247
- report: path.join(debugDir, 'DEBUG_REPORT.md'),
248
- fixPlan: path.join(debugDir, 'FIX_PLAN.md')
240
+ // Dry run mode - show prompt only
241
+ if (options.dryRun) {
242
+ console.log(chalk.yellow('--dry-run mode: Showing prompt that would be sent\n'));
243
+ console.log(chalk.gray('─'.repeat(60)));
244
+ console.log(prompt.substring(0, 2000) + '...');
245
+ console.log(chalk.gray('─'.repeat(60)));
246
+
247
+ // Save prompt for reference
248
+ const promptPath = path.join(debugDir, 'DEBUGGER_PROMPT.txt');
249
+ fs.writeFileSync(promptPath, prompt, 'utf-8');
250
+ console.log(chalk.gray(`\nPrompt saved to: ${promptPath}`));
251
+
252
+ return {
253
+ success: true,
254
+ dryRun: true,
255
+ outputs: {
256
+ report: path.join(debugDir, 'DEBUG_REPORT.md'),
257
+ fixPlan: path.join(debugDir, 'FIX_PLAN.md')
258
+ }
259
+ };
260
+ }
261
+
262
+ // Real execution mode
263
+ const { invokeSubagent, SubagentInvoker } = require('../utils/subagent-invoker');
264
+
265
+ const invoker = new SubagentInvoker({ verbose: options.verbose });
266
+
267
+ invoker.on('progress', (data) => {
268
+ console.log(chalk.gray(` ${data.message}`));
269
+ });
270
+
271
+ invoker.on('artifact', (data) => {
272
+ console.log(chalk.green(` āœ“ Generated: ${data.path}`));
273
+ });
274
+
275
+ try {
276
+ const result = await invokeSubagent('reis_debugger', {
277
+ verbose: options.verbose,
278
+ timeout: options.timeout || 300000,
279
+ additionalContext: {
280
+ debugPrompt: prompt,
281
+ focusArea: options.focus || null
282
+ }
283
+ });
284
+
285
+ // Determine output paths
286
+ let reportPath = path.join(debugDir, 'DEBUG_REPORT.md');
287
+ let fixPlanPath = path.join(debugDir, 'FIX_PLAN.md');
288
+
289
+ if (result.metadata) {
290
+ reportPath = result.metadata.reportPath || reportPath;
291
+ fixPlanPath = result.metadata.fixPlanPath || fixPlanPath;
249
292
  }
250
- };
293
+
294
+ return {
295
+ success: result.success,
296
+ outputs: {
297
+ report: reportPath,
298
+ fixPlan: fixPlanPath
299
+ },
300
+ output: result.output,
301
+ error: result.error
302
+ };
303
+
304
+ } catch (error) {
305
+ return {
306
+ success: false,
307
+ error: error,
308
+ outputs: {
309
+ report: path.join(debugDir, 'DEBUG_REPORT.md'),
310
+ fixPlan: path.join(debugDir, 'FIX_PLAN.md')
311
+ }
312
+ };
313
+ }
251
314
  }
252
315
 
253
316
  /**
@@ -7,6 +7,7 @@ const { loadConfig } = require('../utils/config');
7
7
  const { getGitStatus, commitWaveCompletion } = require('../utils/git-integration');
8
8
  const { parseDecisionTrees } = require('../utils/decision-tree-parser');
9
9
  const { renderDecisionTree } = require('../utils/visualizer');
10
+ const { showKanbanBoard } = require('../utils/kanban-renderer');
10
11
  const chalk = require('chalk');
11
12
 
12
13
  /**
@@ -16,9 +17,12 @@ const chalk = require('chalk');
16
17
  * @param {boolean} args.wave - Enable wave-based execution
17
18
  * @param {boolean} args.dryRun - Show plan without executing
18
19
  * @param {boolean} args.interactive - Step-by-step execution with prompts
20
+ * @param {boolean} args.noKanban - Hide kanban board
19
21
  * @returns {Promise<number>} Exit code
20
22
  */
21
23
  async function executePlan(args) {
24
+ // Show kanban board (unless disabled)
25
+ showKanbanBoard({ noKanban: args?.noKanban });
22
26
  // Check if .planning/ exists
23
27
  if (!checkPlanningDir()) {
24
28
  showError('Not a REIS project. Run "reis new" or "reis map" first.');
@@ -1,6 +1,18 @@
1
- const { showPrompt, showError, checkPlanningDir, validatePhaseNumber } = require('../utils/command-helpers');
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+ const chalk = require('chalk');
4
+ const { showError, checkPlanningDir, validatePhaseNumber } = require('../utils/command-helpers');
5
+ const { invokeSubagent, SubagentInvoker, buildExecutionContext } = require('../utils/subagent-invoker');
6
+ const { showKanbanBoard } = require('../utils/kanban-renderer');
2
7
 
3
- module.exports = function execute(args) {
8
+ /**
9
+ * Execute command - Runs reis_executor subagent
10
+ * @param {Object} args - Command arguments
11
+ * @param {Object} options - Command options
12
+ */
13
+ async function execute(args, options = {}) {
14
+ // Show kanban board (unless disabled)
15
+ showKanbanBoard({ noKanban: options?.noKanban });
4
16
  // Check if .planning/ exists
5
17
  if (!checkPlanningDir()) {
6
18
  showError('Not a REIS project. Run "reis new" or "reis map" first.');
@@ -14,15 +26,145 @@ module.exports = function execute(args) {
14
26
  process.exit(1);
15
27
  }
16
28
 
17
- // Validate phase is a valid positive number
18
29
  const validatedPhase = validatePhaseNumber(phase);
19
30
  if (validatedPhase === null) {
20
31
  process.exit(1);
21
32
  }
22
33
 
23
- const prompt = `Execute phase ${validatedPhase} using REIS methodology. Use the reis_executor subagent to execute all plans for this phase in parallel where possible (up to 4 simultaneous tasks). Each task gets fresh 200k context. Update .planning/STATE.md with progress.`;
34
+ // Find plan file(s) for this phase
35
+ const planPath = findPlanPath(validatedPhase);
36
+ if (!planPath) {
37
+ showError(`No plan found for phase ${validatedPhase}. Run "reis plan ${validatedPhase}" first.`);
38
+ process.exit(1);
39
+ }
40
+
41
+ console.log(chalk.blue(`\nāš™ļø REIS Executor - Phase ${validatedPhase}\n`));
42
+ console.log(chalk.gray(`Plan: ${planPath}`));
43
+
44
+ // Dry run mode - just show what would happen
45
+ if (options.dryRun) {
46
+ console.log(chalk.yellow('\n--dry-run mode: Showing prompt that would be sent\n'));
47
+ const ctx = buildExecutionContext('reis_executor', { planPath });
48
+ console.log(chalk.gray('─'.repeat(60)));
49
+ console.log(ctx.buildPrompt().substring(0, 2000) + '...');
50
+ console.log(chalk.gray('─'.repeat(60)));
51
+ return 0;
52
+ }
53
+
54
+ // Real execution mode
55
+ console.log(chalk.blue('\nšŸš€ Starting execution...\n'));
24
56
 
25
- showPrompt(prompt);
57
+ const invoker = new SubagentInvoker({ verbose: options.verbose });
26
58
 
27
- return 0;
28
- };
59
+ // Set up progress display
60
+ invoker.on('start', () => {
61
+ console.log(chalk.gray(' Initializing executor...'));
62
+ });
63
+
64
+ invoker.on('progress', (data) => {
65
+ console.log(chalk.gray(` ${data.message}`));
66
+ });
67
+
68
+ invoker.on('artifact', (data) => {
69
+ console.log(chalk.green(` āœ“ Created: ${data.path}`));
70
+ });
71
+
72
+ try {
73
+ const result = await invokeSubagent('reis_executor', {
74
+ planPath,
75
+ verbose: options.verbose,
76
+ timeout: options.timeout || 600000,
77
+ additionalContext: {
78
+ phase: validatedPhase,
79
+ autoCommit: options.commit !== false
80
+ }
81
+ });
82
+
83
+ if (result.success) {
84
+ console.log(chalk.green('\nāœ… Execution complete!\n'));
85
+
86
+ if (result.metadata?.artifacts?.length > 0) {
87
+ console.log(chalk.cyan('Artifacts created:'));
88
+ result.metadata.artifacts.forEach(a => {
89
+ console.log(chalk.gray(` - ${a}`));
90
+ });
91
+ }
92
+
93
+ console.log(chalk.blue('\nNext step: ') + chalk.cyan(`reis verify ${validatedPhase}`));
94
+ return 0;
95
+ } else {
96
+ console.log(chalk.red('\nāŒ Execution failed\n'));
97
+ if (result.error) {
98
+ console.log(chalk.red(`Error: ${result.error.message}`));
99
+ }
100
+ console.log(chalk.yellow('\nTry: reis debug to analyze the failure'));
101
+ return 1;
102
+ }
103
+ } catch (error) {
104
+ console.error(chalk.red(`\nāŒ Execution error: ${error.message}`));
105
+ if (options.verbose) {
106
+ console.error(error.stack);
107
+ }
108
+ return 1;
109
+ }
110
+ }
111
+
112
+ /**
113
+ * Find plan file for a phase
114
+ */
115
+ function findPlanPath(phase) {
116
+ const planningDir = path.join(process.cwd(), '.planning');
117
+
118
+ // Try phase-N.PLAN.md
119
+ const directPath = path.join(planningDir, `phase-${phase}.PLAN.md`);
120
+ if (fs.existsSync(directPath)) {
121
+ return directPath;
122
+ }
123
+
124
+ // Try phases/N-*/PLAN.md
125
+ const phasesDir = path.join(planningDir, 'phases');
126
+ if (fs.existsSync(phasesDir)) {
127
+ const dirs = fs.readdirSync(phasesDir).filter(d => d.startsWith(`${phase}-`));
128
+ if (dirs.length > 0) {
129
+ const phaseDir = path.join(phasesDir, dirs[0]);
130
+ const plans = fs.readdirSync(phaseDir).filter(f => f.endsWith('.PLAN.md'));
131
+ if (plans.length > 0) {
132
+ return path.join(phaseDir, plans[0]);
133
+ }
134
+ }
135
+ }
136
+
137
+ // Try N-*/PLAN.md directly in .planning
138
+ const dirs = fs.readdirSync(planningDir).filter(d => d.startsWith(`${phase}-`));
139
+ if (dirs.length > 0) {
140
+ const phaseDir = path.join(planningDir, dirs[0]);
141
+ if (fs.statSync(phaseDir).isDirectory()) {
142
+ const plans = fs.readdirSync(phaseDir).filter(f => f.endsWith('.PLAN.md'));
143
+ if (plans.length > 0) {
144
+ return path.join(phaseDir, plans[0]);
145
+ }
146
+ }
147
+ }
148
+
149
+ // Try priority-*/phase-N-* pattern (for priority folders)
150
+ const priorityDirs = fs.readdirSync(planningDir).filter(d => d.startsWith('priority-'));
151
+ for (const priorityDir of priorityDirs) {
152
+ const priorityPath = path.join(planningDir, priorityDir);
153
+ if (fs.statSync(priorityPath).isDirectory()) {
154
+ const phaseDirs = fs.readdirSync(priorityPath).filter(d =>
155
+ d.startsWith(`phase-${phase}-`) || d.startsWith(`${phase}-`)
156
+ );
157
+ if (phaseDirs.length > 0) {
158
+ const phaseDir = path.join(priorityPath, phaseDirs[0]);
159
+ const plans = fs.readdirSync(phaseDir).filter(f => f.endsWith('.PLAN.md'));
160
+ if (plans.length > 0) {
161
+ return path.join(phaseDir, plans[0]);
162
+ }
163
+ }
164
+ }
165
+ }
166
+
167
+ return null;
168
+ }
169
+
170
+ module.exports = execute;