vibecodingmachine-cli 2026.1.29-713 → 2026.2.20-423

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 (45) hide show
  1. package/bin/vibecodingmachine.js +124 -0
  2. package/package.json +3 -2
  3. package/src/commands/agents-check.js +69 -0
  4. package/src/commands/auto-direct.js +930 -145
  5. package/src/commands/auto.js +26 -4
  6. package/src/commands/ide.js +2 -1
  7. package/src/commands/requirements.js +23 -27
  8. package/src/utils/auto-mode.js +4 -1
  9. package/src/utils/cline-js-handler.js +218 -0
  10. package/src/utils/config.js +22 -0
  11. package/src/utils/display-formatters-complete.js +229 -0
  12. package/src/utils/display-formatters-extracted.js +219 -0
  13. package/src/utils/display-formatters.js +157 -0
  14. package/src/utils/feedback-handler.js +143 -0
  15. package/src/utils/ide-detection-complete.js +126 -0
  16. package/src/utils/ide-detection-extracted.js +116 -0
  17. package/src/utils/ide-detection.js +124 -0
  18. package/src/utils/interactive-backup.js +5664 -0
  19. package/src/utils/interactive-broken.js +280 -0
  20. package/src/utils/interactive.js +31 -5534
  21. package/src/utils/provider-checker.js +410 -0
  22. package/src/utils/provider-manager.js +251 -0
  23. package/src/utils/provider-registry.js +18 -9
  24. package/src/utils/requirement-actions.js +884 -0
  25. package/src/utils/requirements-navigator.js +585 -0
  26. package/src/utils/rui-trui-adapter.js +311 -0
  27. package/src/utils/simple-trui.js +204 -0
  28. package/src/utils/status-helpers-extracted.js +125 -0
  29. package/src/utils/status-helpers.js +107 -0
  30. package/src/utils/trui-debug.js +261 -0
  31. package/src/utils/trui-feedback.js +133 -0
  32. package/src/utils/trui-nav-agents.js +119 -0
  33. package/src/utils/trui-nav-requirements.js +268 -0
  34. package/src/utils/trui-nav-settings.js +157 -0
  35. package/src/utils/trui-nav-specifications.js +139 -0
  36. package/src/utils/trui-navigation.js +303 -0
  37. package/src/utils/trui-provider-manager.js +182 -0
  38. package/src/utils/trui-quick-menu.js +365 -0
  39. package/src/utils/trui-req-actions.js +372 -0
  40. package/src/utils/trui-req-tree.js +534 -0
  41. package/src/utils/trui-specifications.js +359 -0
  42. package/src/utils/trui-text-editor.js +350 -0
  43. package/src/utils/trui-windsurf.js +336 -0
  44. package/src/utils/welcome-screen-extracted.js +135 -0
  45. package/src/utils/welcome-screen.js +134 -0
@@ -0,0 +1,268 @@
1
+ /**
2
+ * TRUI Requirements Section
3
+ * Exposes loadRequirementsData / buildRequirementChoices / handleRequirementSelection
4
+ * for inline accordion display in the main navigation menu.
5
+ * Also exports addRequirementFlow for the [+ Add Requirement] shortcut.
6
+ */
7
+
8
+ const inquirer = require('inquirer');
9
+ const chalk = require('chalk');
10
+ const fs = require('fs-extra');
11
+
12
+ /**
13
+ * Load requirements data via the RUI command resolver
14
+ * @param {Object} navigation - TRUINavigation instance
15
+ * @returns {Promise<{requirements: Array}|null>}
16
+ */
17
+ async function loadRequirementsData(navigation) {
18
+ try {
19
+ const result = navigation.resolver.resolve('list requirements');
20
+ if (!result.success) return null;
21
+
22
+ const commandResult = await result.command.execute();
23
+ if (!commandResult.success || !commandResult.data) return null;
24
+
25
+ return commandResult.data;
26
+ } catch (err) {
27
+ return null;
28
+ }
29
+ }
30
+
31
+ /**
32
+ * Build inline accordion choice objects for the requirements section.
33
+ * Requirements are grouped by status with Separator headers.
34
+ * @param {{requirements: Array}} data
35
+ * @returns {Array} inquirer choice objects (includes Separators)
36
+ */
37
+ function buildRequirementChoices(data) {
38
+ const requirements = (data && data.requirements) || [];
39
+
40
+ if (!requirements.length) {
41
+ return [{ name: chalk.gray(' (no requirements found)'), value: 'noop' }];
42
+ }
43
+
44
+ const sections = {
45
+ current: requirements.filter(r => r.status === 'current'),
46
+ todo: requirements.filter(r => r.status === 'todo'),
47
+ verify: requirements.filter(r => r.status === 'verify'),
48
+ verified: requirements.filter(r => r.status === 'verified'),
49
+ 'need-information': requirements.filter(r => r.status === 'need-information'),
50
+ };
51
+
52
+ const choices = [];
53
+
54
+ const addSection = (label, items, icon) => {
55
+ if (!items.length) return;
56
+ choices.push(new inquirer.Separator(chalk.bold(` ${label}`)));
57
+ items.forEach(req => {
58
+ const idx = requirements.indexOf(req);
59
+ const isDisabled = req.title.startsWith('DISABLED:');
60
+ const cleanTitle = isDisabled ? req.title.replace(/^DISABLED:\s*/, '') : req.title;
61
+ const disabledIcon = isDisabled ? chalk.red('🚫') : '';
62
+ choices.push({
63
+ name: ` ${disabledIcon}${icon} ${chalk.white(cleanTitle)}`,
64
+ value: `req:${idx}`,
65
+ });
66
+ });
67
+ };
68
+
69
+ addSection('šŸ“Œ CURRENT', sections.current, chalk.cyan('ā—'));
70
+ addSection('ā³ TODO', sections.todo, chalk.yellow('ā—‹'));
71
+ addSection('šŸ” TO VERIFY', sections.verify, chalk.blue('ā—Ž'));
72
+ addSection('āœ… VERIFIED', sections.verified, chalk.green('āœ“'));
73
+ addSection('ā“ NEED INFO', sections['need-information'], chalk.red('?'));
74
+
75
+ return choices;
76
+ }
77
+
78
+ /**
79
+ * Handle selection of a requirement item — shows status-change sub-prompt
80
+ * @param {number} index - Index into data.requirements
81
+ * @param {{requirements: Array}} data
82
+ * @param {Object} navigation - TRUINavigation instance
83
+ * @returns {Promise<boolean>} true if requirements were changed
84
+ */
85
+ async function handleRequirementSelection(index, data, navigation) {
86
+ if (!data || !data.requirements || !data.requirements[index]) return false;
87
+
88
+ const req = data.requirements[index];
89
+
90
+ const {
91
+ promoteToVerified,
92
+ demoteFromVerifiedToTodo,
93
+ promoteTodoToVerify,
94
+ demoteVerifyToTodo,
95
+ getOrCreateRequirementsFilePath,
96
+ } = require('vibecodingmachine-core');
97
+
98
+ const statusLabel = {
99
+ current: chalk.cyan('CURRENT'),
100
+ todo: chalk.yellow('TODO'),
101
+ verify: chalk.blue('TO VERIFY'),
102
+ verified: chalk.green('VERIFIED'),
103
+ 'need-information': chalk.red('NEED INFO'),
104
+ }[req.status] || chalk.gray(req.status || 'unknown');
105
+
106
+ const isDisabled = req.title.startsWith('DISABLED:');
107
+ const cleanTitle = isDisabled ? req.title.replace(/^DISABLED:\s*/, '') : req.title;
108
+
109
+ const choices = [
110
+ { name: 'Move to TODO', value: 'moveToTodo' },
111
+ { name: 'Move to TO VERIFY', value: 'moveToVerify' },
112
+ { name: 'Move to VERIFIED', value: 'moveToVerified' },
113
+ ];
114
+
115
+ // Add enable/disable option
116
+ if (isDisabled) {
117
+ choices.push({ name: 'Enable Requirement', value: 'enable' });
118
+ } else {
119
+ choices.push({ name: 'Disable Requirement', value: 'disable' });
120
+ }
121
+
122
+ choices.push({ name: chalk.gray('Cancel'), value: 'cancel' });
123
+
124
+ const { reqAction } = await inquirer.prompt([
125
+ {
126
+ type: 'list',
127
+ name: 'reqAction',
128
+ message: `${cleanTitle} [${statusLabel}]`,
129
+ choices,
130
+ },
131
+ ]);
132
+
133
+ if (reqAction === 'cancel') return false;
134
+
135
+ try {
136
+ const reqPath = await getOrCreateRequirementsFilePath();
137
+
138
+ if (reqAction === 'moveToTodo') {
139
+ if (req.status === 'verify') {
140
+ await demoteVerifyToTodo(reqPath, req.title);
141
+ } else if (req.status === 'verified') {
142
+ await demoteFromVerifiedToTodo(reqPath, req.title);
143
+ } else {
144
+ console.log(chalk.yellow('Cannot move to TODO from current status'));
145
+ await navigation.promptContinue();
146
+ return false;
147
+ }
148
+ } else if (reqAction === 'moveToVerify') {
149
+ await promoteTodoToVerify(reqPath, req.title);
150
+ } else if (reqAction === 'moveToVerified') {
151
+ await promoteToVerified(reqPath, req.title);
152
+ } else if (reqAction === 'enable' || reqAction === 'disable') {
153
+ // Toggle disabled status
154
+ const content = await fs.readFile(reqPath, 'utf8');
155
+ const lines = content.split('\n');
156
+
157
+ // Find the requirement line
158
+ for (let i = 0; i < lines.length; i++) {
159
+ if (lines[i].includes(req.title)) {
160
+ if (reqAction === 'enable') {
161
+ // Remove DISABLED: prefix
162
+ lines[i] = lines[i].replace(/^###\s*DISABLED:\s*/, '### ');
163
+ } else {
164
+ // Add DISABLED: prefix
165
+ lines[i] = lines[i].replace(/^###\s*/, '### DISABLED: ');
166
+ }
167
+ break;
168
+ }
169
+ }
170
+
171
+ await fs.writeFile(reqPath, lines.join('\n'), 'utf8');
172
+ const action = reqAction === 'enable' ? 'enabled' : 'disabled';
173
+ console.log(chalk.green(`\nāœ“ Requirement "${cleanTitle}" ${action}`));
174
+ console.log(chalk.gray(` Equivalent: app requirements ${cleanTitle} ${reqAction}`));
175
+ await navigation.promptContinue();
176
+ return true;
177
+ }
178
+
179
+ console.log(chalk.green(`\nāœ“ Moved "${req.title}" successfully`));
180
+ console.log(chalk.gray(` Equivalent: app requirements ${req.title} ${reqAction}`));
181
+ await navigation.promptContinue();
182
+ return true;
183
+ } catch (err) {
184
+ console.log(chalk.red(`Error: ${err.message}`));
185
+ await navigation.promptContinue();
186
+ return false;
187
+ }
188
+ }
189
+
190
+ /**
191
+ * Interactive flow to add a new requirement
192
+ * Appends a new requirement to the "Requirements not yet completed" section
193
+ * @param {Object} navigation - TRUINavigation instance
194
+ */
195
+ async function addRequirementFlow(navigation) {
196
+ console.log(chalk.bold.cyan('\nšŸ“‹ Add New Requirement\n'));
197
+ console.log(chalk.gray('Equivalent: app create requirement <name>\n'));
198
+
199
+ const { title } = await inquirer.prompt([
200
+ {
201
+ type: 'input',
202
+ name: 'title',
203
+ message: chalk.cyan('Requirement title:'),
204
+ validate: input => input.trim().length > 0 || 'Title is required',
205
+ },
206
+ ]);
207
+
208
+ const { description } = await inquirer.prompt([
209
+ {
210
+ type: 'input',
211
+ name: 'description',
212
+ message: chalk.cyan('Description (optional):'),
213
+ },
214
+ ]);
215
+
216
+ try {
217
+ const { getOrCreateRequirementsFilePath } = require('vibecodingmachine-core');
218
+ const repoPath = process.cwd();
219
+ const reqPath = await getOrCreateRequirementsFilePath(repoPath);
220
+
221
+ const content = await fs.readFile(reqPath, 'utf8');
222
+ const lines = content.split('\n');
223
+
224
+ let insertIndex = -1;
225
+ for (let i = 0; i < lines.length; i++) {
226
+ if (lines[i].trim().match(/^##\s+.*(?:Requirements not yet completed|TODO)/i)) {
227
+ insertIndex = i + 1;
228
+ for (let j = i + 1; j < lines.length; j++) {
229
+ const inner = lines[j].trim();
230
+ if (inner.startsWith('##') && !inner.startsWith('###')) {
231
+ insertIndex = j;
232
+ break;
233
+ }
234
+ if (inner.startsWith('###') || inner.startsWith('- ')) {
235
+ insertIndex = j;
236
+ break;
237
+ }
238
+ }
239
+ break;
240
+ }
241
+ }
242
+
243
+ const requirementLine = description.trim()
244
+ ? `### ${title.trim()}\n\n${description.trim()}\n`
245
+ : `### ${title.trim()}\n`;
246
+
247
+ if (insertIndex >= 0) {
248
+ lines.splice(insertIndex, 0, requirementLine);
249
+ } else {
250
+ lines.push('', '## Requirements not yet completed', '', requirementLine);
251
+ }
252
+
253
+ await fs.writeFile(reqPath, lines.join('\n'), 'utf8');
254
+ console.log(chalk.green(`\nāœ“ Requirement "${title.trim()}" added`));
255
+ console.log(chalk.gray(` Equivalent: app create requirement "${title.trim()}"`));
256
+ } catch (err) {
257
+ console.log(chalk.red(`Error adding requirement: ${err.message}`));
258
+ }
259
+
260
+ await navigation.promptContinue();
261
+ }
262
+
263
+ module.exports = {
264
+ loadRequirementsData,
265
+ buildRequirementChoices,
266
+ handleRequirementSelection,
267
+ addRequirementFlow,
268
+ };
@@ -0,0 +1,157 @@
1
+ /**
2
+ * TRUI Settings Section
3
+ * Exposes loadSettingsData / buildSettingChoices / handleSettingSelection
4
+ * for inline accordion display in the main navigation menu.
5
+ */
6
+
7
+ const inquirer = require('inquirer');
8
+ const chalk = require('chalk');
9
+
10
+ /**
11
+ * Load settings data via the RUI command resolver
12
+ * @param {Object} navigation - TRUINavigation instance
13
+ * @returns {Promise<{settings: Array}|null>}
14
+ */
15
+ async function loadSettingsData(navigation) {
16
+ try {
17
+ const result = navigation.resolver.resolve('list settings');
18
+ if (!result.success) return null;
19
+
20
+ const commandResult = await result.command.execute();
21
+ if (!commandResult.success || !commandResult.data) return null;
22
+
23
+ return commandResult.data;
24
+ } catch (err) {
25
+ return null;
26
+ }
27
+ }
28
+
29
+ /**
30
+ * Build inline accordion choice objects for the settings section
31
+ * @param {{settings: Array}} data
32
+ * @returns {Array} inquirer choice objects
33
+ */
34
+ function buildSettingChoices(data) {
35
+ const settings = (data && data.settings) || [];
36
+
37
+ if (!settings.length) {
38
+ return [{ name: chalk.gray(' (no settings found)'), value: 'noop' }];
39
+ }
40
+
41
+ return settings.map(setting => ({
42
+ name: ` ${chalk.white(setting.key)}: ${chalk.gray(String(setting.value))}`,
43
+ value: `setting:${setting.key}`,
44
+ }));
45
+ }
46
+
47
+ /**
48
+ * Handle selection of a setting item — shows key/value detail
49
+ * @param {string} key - The setting key
50
+ * @param {{settings: Array}} data
51
+ * @param {Object} navigation - TRUINavigation instance
52
+ */
53
+ async function handleSettingSelection(key, data, navigation) {
54
+ const settings = (data && data.settings) || [];
55
+ const setting = settings.find(s => s.key === key);
56
+
57
+ if (!setting) {
58
+ console.log(chalk.red(`Setting "${key}" not found`));
59
+ await navigation.promptContinue();
60
+ return;
61
+ }
62
+
63
+ console.log(chalk.bold.cyan(`\nāš™ļø ${setting.key}\n`));
64
+ console.log(chalk.white(` Value: ${setting.value}`));
65
+ if (setting.description) {
66
+ console.log(chalk.gray(` Description: ${setting.description}`));
67
+ }
68
+ console.log(chalk.gray(`\n Equivalent: app get settings ${setting.key}`));
69
+
70
+ await navigation.promptContinue();
71
+ }
72
+
73
+ /**
74
+ * Show the settings menu — a showQuickMenu loop with current config displayed.
75
+ */
76
+ async function showSettings() {
77
+ const { showQuickMenu } = require('./trui-quick-menu');
78
+ const { getAutoConfig, getRepoPath, getStages, setAutoConfig } = require('./config');
79
+ const { getProviderPreferences } = require('./provider-registry');
80
+ const inquirer = require('inquirer');
81
+
82
+ const printHeader = () => {
83
+ console.clear();
84
+ process.stdout.write(chalk.bold.cyan('āš™ļø Settings\n\n'));
85
+ process.stdout.write(chalk.gray('[↑↓ navigate Enter select ← back]\n\n'));
86
+ };
87
+
88
+ const buildItems = async () => {
89
+ const repoPath = await getRepoPath();
90
+ const autoConfig = await getAutoConfig();
91
+ const prefs = await getProviderPreferences();
92
+ // Find first enabled provider in order for display
93
+ let activeIde = autoConfig.ide || autoConfig.agent || 'cline';
94
+
95
+ const items = [];
96
+ items.push({ type: 'setting', name: `Repo: ${chalk.white(repoPath || '(not set)')}`, value: 'setting:repo' });
97
+ items.push({ type: 'setting', name: `IDE/Agent: ${chalk.white(activeIde)}`, value: 'setting:ide' });
98
+ items.push({ type: 'setting', name: `Max Chats: ${chalk.white(autoConfig.maxChats || '(unlimited)')}`, value: 'setting:maxChats' });
99
+ items.push({ type: 'setting', name: `Never Stop: ${chalk.white(autoConfig.neverStop ? 'true' : 'false')}`, value: 'setting:neverStop' });
100
+ items.push({ type: 'setting', name: `Skip Disabled: ${chalk.white(autoConfig.skipDisabled ? 'true' : 'false')}`, value: 'setting:skipDisabled' });
101
+ return items;
102
+ };
103
+
104
+ printHeader();
105
+
106
+ while (true) {
107
+ const items = await buildItems();
108
+ const result = await showQuickMenu(items, 0);
109
+ const value = result.value;
110
+
111
+ if (value === '__cancel__' || value === 'exit') break;
112
+
113
+ if (value === 'setting:ide') {
114
+ const prefs = await getProviderPreferences();
115
+ const autoConfig = await getAutoConfig();
116
+ const enabledAgents = prefs.order.filter(id => prefs.enabled[id] !== false);
117
+ if (enabledAgents.length === 0) {
118
+ console.log(chalk.yellow('\nNo enabled agents. Enable agents in Agents menu first.\n'));
119
+ await inquirer.prompt([{ type: 'input', name: 'c', message: chalk.gray('Press Enter...') }]);
120
+ } else {
121
+ const { ide } = await inquirer.prompt([{
122
+ type: 'list',
123
+ name: 'ide',
124
+ message: 'Select active IDE/Agent:',
125
+ choices: enabledAgents,
126
+ default: autoConfig.ide || autoConfig.agent,
127
+ }]);
128
+ await setAutoConfig({ ide });
129
+ }
130
+ } else if (value === 'setting:maxChats') {
131
+ const autoConfig = await getAutoConfig();
132
+ const { maxChats } = await inquirer.prompt([{
133
+ type: 'input',
134
+ name: 'maxChats',
135
+ message: 'Max chats per run (blank = unlimited):',
136
+ default: autoConfig.maxChats ? String(autoConfig.maxChats) : '',
137
+ }]);
138
+ const parsed = parseInt(maxChats, 10);
139
+ await setAutoConfig({ maxChats: isNaN(parsed) ? undefined : parsed });
140
+ } else if (value === 'setting:neverStop') {
141
+ const autoConfig = await getAutoConfig();
142
+ await setAutoConfig({ neverStop: !autoConfig.neverStop });
143
+ } else if (value === 'setting:skipDisabled') {
144
+ const autoConfig = await getAutoConfig();
145
+ await setAutoConfig({ skipDisabled: !autoConfig.skipDisabled });
146
+ }
147
+
148
+ printHeader();
149
+ }
150
+ }
151
+
152
+ module.exports = {
153
+ loadSettingsData,
154
+ buildSettingChoices,
155
+ handleSettingSelection,
156
+ showSettings,
157
+ };
@@ -0,0 +1,139 @@
1
+ /**
2
+ * TRUI Specifications Section
3
+ * Exposes loadSpecificationsData / buildSpecificationChoices / handleSpecificationSelection
4
+ * for inline accordion display in the main navigation menu.
5
+ * Also exports addSpecificationFlow for the [+ Add Specification] shortcut.
6
+ */
7
+
8
+ const inquirer = require('inquirer');
9
+ const chalk = require('chalk');
10
+
11
+ /**
12
+ * Load all specifications from the repo
13
+ * @returns {Promise<Array|null>}
14
+ */
15
+ async function loadSpecificationsData() {
16
+ try {
17
+ const { getAllSpecifications } = require('vibecodingmachine-core');
18
+ const repoPath = process.cwd();
19
+ return await getAllSpecifications(repoPath);
20
+ } catch (err) {
21
+ return null;
22
+ }
23
+ }
24
+
25
+ /**
26
+ * Build inline accordion choice objects for the specifications section
27
+ * @param {Array} specs - Array of spec objects
28
+ * @returns {Array} inquirer choice objects
29
+ */
30
+ function buildSpecificationChoices(specs) {
31
+ if (!specs || !specs.length) {
32
+ return [{ name: chalk.gray(' (no specifications found)'), value: 'noop' }];
33
+ }
34
+
35
+ return specs.map(spec => {
36
+ const todoTag = !spec.hasTasks ? chalk.yellow(' [TODO]') : chalk.green(' [done]');
37
+ const label = spec.title || spec.directory;
38
+ return {
39
+ name: ` ${chalk.white(spec.directory)} ${chalk.gray('—')} ${chalk.gray(label)}${todoTag}`,
40
+ value: `spec:${spec.directory}`,
41
+ };
42
+ });
43
+ }
44
+
45
+ /**
46
+ * Handle selection of a specification item — shows detail
47
+ * @param {string} directory - Spec directory name
48
+ * @param {Array} specs
49
+ * @param {Object} navigation - TRUINavigation instance
50
+ */
51
+ async function handleSpecificationSelection(directory, specs, navigation) {
52
+ const spec = (specs || []).find(s => s.directory === directory);
53
+
54
+ if (!spec) {
55
+ console.log(chalk.red(`Specification "${directory}" not found`));
56
+ await navigation.promptContinue();
57
+ return;
58
+ }
59
+
60
+ console.log(chalk.bold.cyan(`\nšŸ“ ${spec.title || spec.directory}\n`));
61
+ console.log(chalk.white(` Directory: ${spec.directory}`));
62
+ console.log(chalk.white(` Path: ${spec.path}`));
63
+ console.log(chalk.white(` Has Plan: ${spec.hasPlan ? chalk.green('Yes') : chalk.gray('No')}`));
64
+ console.log(chalk.white(` Has Tasks: ${spec.hasTasks ? chalk.green('Yes') : chalk.yellow('No (TODO)')}`));
65
+
66
+ await navigation.promptContinue();
67
+ }
68
+
69
+ /**
70
+ * Interactive flow to add a new specification
71
+ * @param {Object} navigation - TRUINavigation instance
72
+ */
73
+ async function addSpecificationFlow(navigation) {
74
+ console.log(chalk.bold.cyan('\nšŸ“ New Specification\n'));
75
+
76
+ const { shortName } = await inquirer.prompt([
77
+ {
78
+ type: 'input',
79
+ name: 'shortName',
80
+ message: chalk.cyan('Short name (e.g. "print-to-pdf"):'),
81
+ validate: input => {
82
+ if (!input.trim()) return 'Short name is required';
83
+ if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(input.trim())) {
84
+ return 'Must be lowercase with hyphens: /^[a-z0-9]+(-[a-z0-9]+)*$/';
85
+ }
86
+ return true;
87
+ },
88
+ },
89
+ ]);
90
+
91
+ console.log(chalk.gray('Enter specification prompt. Enter a blank line to finish.'));
92
+ const promptLines = [];
93
+ while (true) {
94
+ const { line } = await inquirer.prompt([
95
+ {
96
+ type: 'input',
97
+ name: 'line',
98
+ message: chalk.cyan(promptLines.length === 0 ? 'Prompt > ' : ' > '),
99
+ },
100
+ ]);
101
+ if (line.trim() === '' && promptLines.length > 0) break;
102
+ if (line.trim() !== '') promptLines.push(line);
103
+ }
104
+ const specPrompt = promptLines.join('\n');
105
+
106
+ console.log(chalk.gray('\nPlan prompt (optional). Enter blank line to skip.'));
107
+ const planLines = [];
108
+ while (true) {
109
+ const { line } = await inquirer.prompt([
110
+ {
111
+ type: 'input',
112
+ name: 'line',
113
+ message: chalk.cyan(planLines.length === 0 ? 'Plan > ' : ' > '),
114
+ },
115
+ ]);
116
+ if (line.trim() === '') break;
117
+ planLines.push(line);
118
+ }
119
+ const planPrompt = planLines.join('\n');
120
+
121
+ try {
122
+ const { createSpecification } = require('vibecodingmachine-core');
123
+ const repoPath = process.cwd();
124
+ const result = await createSpecification(repoPath, shortName.trim(), specPrompt, planPrompt);
125
+ console.log(chalk.green(`\nāœ… Specification created: ${result.directory}`));
126
+ console.log(chalk.gray(` Path: ${result.path}`));
127
+ } catch (err) {
128
+ console.log(chalk.red(`āŒ Error creating specification: ${err.message}`));
129
+ }
130
+
131
+ await navigation.promptContinue();
132
+ }
133
+
134
+ module.exports = {
135
+ loadSpecificationsData,
136
+ buildSpecificationChoices,
137
+ handleSpecificationSelection,
138
+ addSpecificationFlow,
139
+ };