vibecodingmachine-cli 2026.2.26-1739 → 2026.3.9-1621
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/bin/auth/auth-compliance.js +7 -1
- package/bin/commands/agent-commands.js +150 -228
- package/bin/commands/command-aliases.js +68 -0
- package/bin/vibecodingmachine.js +1 -2
- package/package.json +2 -2
- package/src/commands/agents/list.js +71 -115
- package/src/commands/agents-check.js +16 -4
- package/src/commands/analyze-file-sizes.js +1 -1
- package/src/commands/auto-direct/auto-provider-manager.js +290 -0
- package/src/commands/auto-direct/auto-status-display.js +331 -0
- package/src/commands/auto-direct/auto-utils.js +439 -0
- package/src/commands/auto-direct/file-operations.js +110 -0
- package/src/commands/auto-direct/provider-config.js +1 -1
- package/src/commands/auto-direct/provider-manager.js +1 -1
- package/src/commands/auto-direct/status-display.js +1 -1
- package/src/commands/auto-direct/utils.js +24 -18
- package/src/commands/auto-direct-refactored.js +413 -0
- package/src/commands/auto-direct.js +594 -188
- package/src/commands/requirements/commands.js +353 -0
- package/src/commands/requirements/default-handlers.js +272 -0
- package/src/commands/requirements/disable.js +97 -0
- package/src/commands/requirements/enable.js +97 -0
- package/src/commands/requirements/utils.js +194 -0
- package/src/commands/requirements-refactored.js +60 -0
- package/src/commands/requirements.js +38 -771
- package/src/commands/specs/disable.js +96 -0
- package/src/commands/specs/enable.js +96 -0
- package/src/trui/TruiInterface.js +5 -11
- package/src/trui/agents/AgentInterface.js +24 -396
- package/src/trui/agents/handlers/CommandHandler.js +93 -0
- package/src/trui/agents/handlers/ContextManager.js +117 -0
- package/src/trui/agents/handlers/DisplayHandler.js +243 -0
- package/src/trui/agents/handlers/HelpHandler.js +51 -0
- package/src/utils/auth.js +13 -111
- package/src/utils/config.js +5 -1
- package/src/utils/interactive/requirements-navigation.js +17 -15
- package/src/utils/interactive-broken.js +2 -2
- package/src/utils/provider-checker/agent-runner.js +15 -1
- package/src/utils/provider-checker/cli-installer.js +149 -7
- package/src/utils/provider-checker/opencode-checker.js +588 -0
- package/src/utils/provider-checker/provider-validator.js +88 -3
- package/src/utils/provider-checker/time-formatter.js +3 -2
- package/src/utils/provider-manager.js +28 -20
- package/src/utils/provider-registry.js +35 -3
- package/src/utils/requirements-navigator/index.js +94 -0
- package/src/utils/requirements-navigator/input-handler.js +217 -0
- package/src/utils/requirements-navigator/section-loader.js +188 -0
- package/src/utils/requirements-navigator/tree-builder.js +105 -0
- package/src/utils/requirements-navigator/tree-renderer.js +50 -0
- package/src/utils/requirements-navigator.js +2 -583
- package/src/utils/trui-clarifications.js +188 -0
- package/src/utils/trui-feedback.js +54 -1
- package/src/utils/trui-kiro-integration.js +398 -0
- package/src/utils/trui-main-handlers.js +194 -0
- package/src/utils/trui-main-menu.js +235 -0
- package/src/utils/trui-nav-agents.js +178 -25
- package/src/utils/trui-nav-requirements.js +203 -27
- package/src/utils/trui-nav-settings.js +114 -1
- package/src/utils/trui-nav-specifications.js +44 -3
- package/src/utils/trui-navigation-backup.js +603 -0
- package/src/utils/trui-navigation.js +70 -228
- package/src/utils/trui-provider-health.js +274 -0
- package/src/utils/trui-provider-manager.js +376 -0
- package/src/utils/trui-quick-menu.js +25 -1
- package/src/utils/trui-req-actions-backup.js +507 -0
- package/src/utils/trui-req-actions.js +148 -216
- package/src/utils/trui-req-editor.js +170 -0
- package/src/utils/trui-req-file-ops.js +278 -0
- package/src/utils/trui-req-tree-old.js +719 -0
- package/src/utils/trui-req-tree.js +348 -627
- package/src/utils/trui-specifications.js +25 -7
- package/src/utils/trui-windsurf.js +231 -10
- package/src/utils/welcome-screen-extracted.js +2 -2
- package/src/utils/welcome-screen.js +2 -2
|
@@ -28,6 +28,12 @@ async function loadRequirementsData(navigation) {
|
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
|
|
31
|
+
// Track expanded requirement indices
|
|
32
|
+
const expandedRequirements = new Set();
|
|
33
|
+
|
|
34
|
+
// Track selected requirements for multi-select delete
|
|
35
|
+
const selectedRequirements = new Set();
|
|
36
|
+
|
|
31
37
|
/**
|
|
32
38
|
* Build inline accordion choice objects for the requirements section.
|
|
33
39
|
* Requirements are grouped by status with Separator headers.
|
|
@@ -38,7 +44,10 @@ function buildRequirementChoices(data) {
|
|
|
38
44
|
const requirements = (data && data.requirements) || [];
|
|
39
45
|
|
|
40
46
|
if (!requirements.length) {
|
|
41
|
-
return [
|
|
47
|
+
return [
|
|
48
|
+
{ name: chalk.yellow(' No requirements found'), value: 'noop' },
|
|
49
|
+
{ name: chalk.cyan(' [+ Add Requirement]'), value: 'add-requirement' }
|
|
50
|
+
];
|
|
42
51
|
}
|
|
43
52
|
|
|
44
53
|
const sections = {
|
|
@@ -59,15 +68,44 @@ function buildRequirementChoices(data) {
|
|
|
59
68
|
const isDisabled = req.title.startsWith('DISABLED:');
|
|
60
69
|
const cleanTitle = isDisabled ? req.title.replace(/^DISABLED:\s*/, '') : req.title;
|
|
61
70
|
const disabledIcon = isDisabled ? chalk.red('🚫') : '';
|
|
71
|
+
const isExpanded = expandedRequirements.has(idx);
|
|
72
|
+
const expandIcon = isExpanded ? '▼' : '▶';
|
|
73
|
+
const isSelected = selectedRequirements.has(idx);
|
|
74
|
+
const selectIcon = isSelected ? chalk.red('[✓]') : '[ ]';
|
|
75
|
+
|
|
62
76
|
choices.push({
|
|
63
|
-
name: ` ${disabledIcon}${icon} ${chalk.white(cleanTitle)}`,
|
|
77
|
+
name: ` ${selectIcon} ${disabledIcon}${icon} ${expandIcon} ${chalk.white(cleanTitle)}`,
|
|
64
78
|
value: `req:${idx}`,
|
|
65
79
|
});
|
|
80
|
+
|
|
81
|
+
// If expanded, add full details inline
|
|
82
|
+
if (isExpanded) {
|
|
83
|
+
const details = [];
|
|
84
|
+
if (req.description) {
|
|
85
|
+
details.push(chalk.gray(` Description: ${req.description}`));
|
|
86
|
+
}
|
|
87
|
+
if (req.acceptanceCriteria && req.acceptanceCriteria.length > 0) {
|
|
88
|
+
details.push(chalk.gray(` Acceptance Criteria:`));
|
|
89
|
+
req.acceptanceCriteria.forEach(ac => {
|
|
90
|
+
details.push(chalk.gray(` • ${ac}`));
|
|
91
|
+
});
|
|
92
|
+
}
|
|
93
|
+
if (req.dependencies && req.dependencies.length > 0) {
|
|
94
|
+
details.push(chalk.gray(` Dependencies: ${req.dependencies.join(', ')}`));
|
|
95
|
+
}
|
|
96
|
+
if (details.length > 0) {
|
|
97
|
+
details.forEach(detail => {
|
|
98
|
+
choices.push(new inquirer.Separator(detail));
|
|
99
|
+
});
|
|
100
|
+
} else {
|
|
101
|
+
choices.push(new inquirer.Separator(chalk.gray(' (no additional details)')));
|
|
102
|
+
}
|
|
103
|
+
}
|
|
66
104
|
});
|
|
67
105
|
};
|
|
68
106
|
|
|
69
107
|
addSection('📌 CURRENT', sections.current, chalk.cyan('●'));
|
|
70
|
-
addSection('⏳ TODO', sections.todo, chalk.yellow('○'));
|
|
108
|
+
addSection('⏳ TODO REQUIREMENTS', sections.todo, chalk.yellow('○'));
|
|
71
109
|
addSection('🔍 TO VERIFY', sections.verify, chalk.blue('◎'));
|
|
72
110
|
addSection('✅ VERIFIED', sections.verified, chalk.green('✓'));
|
|
73
111
|
addSection('❓ NEED INFO', sections['need-information'], chalk.red('?'));
|
|
@@ -76,7 +114,7 @@ function buildRequirementChoices(data) {
|
|
|
76
114
|
}
|
|
77
115
|
|
|
78
116
|
/**
|
|
79
|
-
* Handle selection of a requirement item —
|
|
117
|
+
* Handle selection of a requirement item — toggles inline expand/collapse
|
|
80
118
|
* @param {number} index - Index into data.requirements
|
|
81
119
|
* @param {{requirements: Array}} data
|
|
82
120
|
* @param {Object} navigation - TRUINavigation instance
|
|
@@ -87,6 +125,145 @@ async function handleRequirementSelection(index, data, navigation) {
|
|
|
87
125
|
|
|
88
126
|
const req = data.requirements[index];
|
|
89
127
|
|
|
128
|
+
// Check if currently expanded - if so, collapse and return
|
|
129
|
+
if (expandedRequirements.has(index)) {
|
|
130
|
+
expandedRequirements.delete(index);
|
|
131
|
+
return true; // Refresh to show collapsed state
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
// First press expands inline
|
|
135
|
+
expandedRequirements.add(index);
|
|
136
|
+
return true; // Refresh to show expanded state
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Handle moving a requirement up or down within its section
|
|
141
|
+
* @param {number} index - Index into data.requirements
|
|
142
|
+
* @param {{requirements: Array}} data
|
|
143
|
+
* @param {string} direction - 'up' or 'down'
|
|
144
|
+
* @returns {Promise<boolean>} true if requirements were changed
|
|
145
|
+
*/
|
|
146
|
+
async function handleRequirementMove(index, data, direction) {
|
|
147
|
+
if (!data || !data.requirements || !data.requirements[index]) return false;
|
|
148
|
+
|
|
149
|
+
const req = data.requirements[index];
|
|
150
|
+
|
|
151
|
+
try {
|
|
152
|
+
const { moveRequirement, getOrCreateRequirementsFilePath } = require('vibecodingmachine-core');
|
|
153
|
+
const reqPath = await getOrCreateRequirementsFilePath();
|
|
154
|
+
const success = await moveRequirement(reqPath, req.title, direction);
|
|
155
|
+
|
|
156
|
+
if (success) {
|
|
157
|
+
console.log(chalk.green(`\n✓ Moved "${req.title}" ${direction}`));
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
return success;
|
|
161
|
+
} catch (err) {
|
|
162
|
+
console.log(chalk.red(`Error moving requirement: ${err.message}`));
|
|
163
|
+
return false;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
/**
|
|
168
|
+
* Handle multi-select toggle for a requirement
|
|
169
|
+
* @param {number} index - Index into data.requirements
|
|
170
|
+
* @returns {boolean} true to trigger refresh
|
|
171
|
+
*/
|
|
172
|
+
function handleRequirementToggleSelect(index) {
|
|
173
|
+
if (selectedRequirements.has(index)) {
|
|
174
|
+
selectedRequirements.delete(index);
|
|
175
|
+
} else {
|
|
176
|
+
selectedRequirements.add(index);
|
|
177
|
+
}
|
|
178
|
+
return true;
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
/**
|
|
182
|
+
* Handle deleting selected requirements
|
|
183
|
+
* @param {{requirements: Array}} data
|
|
184
|
+
* @param {Object} navigation - TRUINavigation instance
|
|
185
|
+
* @returns {Promise<boolean>} true if requirements were changed
|
|
186
|
+
*/
|
|
187
|
+
async function handleRequirementDelete(data, navigation) {
|
|
188
|
+
if (!data || !data.requirements) return false;
|
|
189
|
+
|
|
190
|
+
if (selectedRequirements.size === 0) {
|
|
191
|
+
console.log(chalk.yellow('\n⚠ No requirements selected for deletion'));
|
|
192
|
+
console.log(chalk.gray('Use Space key to select requirements, then press Delete/Backspace'));
|
|
193
|
+
await navigation.promptContinue();
|
|
194
|
+
return false;
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
const requirementsToDelete = Array.from(selectedRequirements)
|
|
198
|
+
.map(idx => data.requirements[idx])
|
|
199
|
+
.filter(Boolean);
|
|
200
|
+
|
|
201
|
+
if (requirementsToDelete.length === 0) return false;
|
|
202
|
+
|
|
203
|
+
console.log(chalk.bold.red(`\n⚠ Delete ${requirementsToDelete.length} requirement(s)?\n`));
|
|
204
|
+
requirementsToDelete.forEach(req => {
|
|
205
|
+
const cleanTitle = req.title.startsWith('DISABLED:')
|
|
206
|
+
? req.title.replace(/^DISABLED:\s*/, '')
|
|
207
|
+
: req.title;
|
|
208
|
+
console.log(chalk.red(` • ${cleanTitle}`));
|
|
209
|
+
});
|
|
210
|
+
console.log('');
|
|
211
|
+
|
|
212
|
+
const { confirmDelete } = await inquirer.prompt([
|
|
213
|
+
{
|
|
214
|
+
type: 'confirm',
|
|
215
|
+
name: 'confirmDelete',
|
|
216
|
+
message: chalk.red('Are you sure you want to delete these requirements?'),
|
|
217
|
+
default: false,
|
|
218
|
+
},
|
|
219
|
+
]);
|
|
220
|
+
|
|
221
|
+
if (!confirmDelete) {
|
|
222
|
+
console.log(chalk.gray('Deletion cancelled'));
|
|
223
|
+
await navigation.promptContinue();
|
|
224
|
+
return false;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
try {
|
|
228
|
+
const { deleteRequirements, getOrCreateRequirementsFilePath } = require('vibecodingmachine-core');
|
|
229
|
+
const reqPath = await getOrCreateRequirementsFilePath();
|
|
230
|
+
const titles = requirementsToDelete.map(req => req.title);
|
|
231
|
+
|
|
232
|
+
const result = await deleteRequirements(reqPath, titles);
|
|
233
|
+
|
|
234
|
+
if (result.success) {
|
|
235
|
+
console.log(chalk.green(`\n✓ Deleted ${result.deleted} requirement(s)`));
|
|
236
|
+
if (result.errors.length > 0) {
|
|
237
|
+
result.errors.forEach(err => console.log(chalk.yellow(` ⚠ ${err}`)));
|
|
238
|
+
}
|
|
239
|
+
selectedRequirements.clear();
|
|
240
|
+
await navigation.promptContinue();
|
|
241
|
+
return true;
|
|
242
|
+
} else {
|
|
243
|
+
console.log(chalk.red(`\nError deleting requirements`));
|
|
244
|
+
result.errors.forEach(err => console.log(chalk.red(` • ${err}`)));
|
|
245
|
+
await navigation.promptContinue();
|
|
246
|
+
return false;
|
|
247
|
+
}
|
|
248
|
+
} catch (err) {
|
|
249
|
+
console.log(chalk.red(`Error deleting requirements: ${err.message}`));
|
|
250
|
+
await navigation.promptContinue();
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
/**
|
|
256
|
+
* Handle action menu for a requirement (status changes, enable/disable)
|
|
257
|
+
* @param {number} index - Index into data.requirements
|
|
258
|
+
* @param {{requirements: Array}} data
|
|
259
|
+
* @param {Object} navigation - TRUINavigation instance
|
|
260
|
+
* @returns {Promise<boolean>} true if requirements were changed
|
|
261
|
+
*/
|
|
262
|
+
async function handleRequirementAction(index, data, navigation) {
|
|
263
|
+
if (!data || !data.requirements || !data.requirements[index]) return false;
|
|
264
|
+
|
|
265
|
+
const req = data.requirements[index];
|
|
266
|
+
|
|
90
267
|
const {
|
|
91
268
|
promoteToVerified,
|
|
92
269
|
demoteFromVerifiedToTodo,
|
|
@@ -97,7 +274,7 @@ async function handleRequirementSelection(index, data, navigation) {
|
|
|
97
274
|
|
|
98
275
|
const statusLabel = {
|
|
99
276
|
current: chalk.cyan('CURRENT'),
|
|
100
|
-
todo: chalk.yellow('TODO'),
|
|
277
|
+
todo: chalk.yellow('TODO REQUIREMENTS'),
|
|
101
278
|
verify: chalk.blue('TO VERIFY'),
|
|
102
279
|
verified: chalk.green('VERIFIED'),
|
|
103
280
|
'need-information': chalk.red('NEED INFO'),
|
|
@@ -123,7 +300,7 @@ async function handleRequirementSelection(index, data, navigation) {
|
|
|
123
300
|
|
|
124
301
|
const { reqAction } = await inquirer.prompt([
|
|
125
302
|
{
|
|
126
|
-
type: '
|
|
303
|
+
type: 'rawlist',
|
|
127
304
|
name: 'reqAction',
|
|
128
305
|
message: `${cleanTitle} [${statusLabel}]`,
|
|
129
306
|
choices,
|
|
@@ -150,28 +327,22 @@ async function handleRequirementSelection(index, data, navigation) {
|
|
|
150
327
|
} else if (reqAction === 'moveToVerified') {
|
|
151
328
|
await promoteToVerified(reqPath, req.title);
|
|
152
329
|
} else if (reqAction === 'enable' || reqAction === 'disable') {
|
|
153
|
-
//
|
|
154
|
-
const
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
}
|
|
330
|
+
// Use the new enable/disable utility functions
|
|
331
|
+
const { enableRequirement, disableRequirement } = require('@vibecodingmachine/core/src/utils/requirement-enable-disable');
|
|
332
|
+
|
|
333
|
+
let result;
|
|
334
|
+
if (reqAction === 'enable') {
|
|
335
|
+
result = await enableRequirement(cleanTitle);
|
|
336
|
+
} else {
|
|
337
|
+
result = await disableRequirement(cleanTitle);
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
if (result.success) {
|
|
341
|
+
console.log(chalk.green(`\n✓ ${result.message}`));
|
|
342
|
+
console.log(chalk.gray(` Equivalent: app ${reqAction} requirement "${cleanTitle}"`));
|
|
343
|
+
} else {
|
|
344
|
+
console.log(chalk.red(`\n❌ ${result.message}`));
|
|
169
345
|
}
|
|
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
346
|
await navigation.promptContinue();
|
|
176
347
|
return true;
|
|
177
348
|
}
|
|
@@ -264,5 +435,10 @@ module.exports = {
|
|
|
264
435
|
loadRequirementsData,
|
|
265
436
|
buildRequirementChoices,
|
|
266
437
|
handleRequirementSelection,
|
|
438
|
+
handleRequirementAction,
|
|
439
|
+
handleRequirementMove,
|
|
440
|
+
handleRequirementToggleSelect,
|
|
441
|
+
handleRequirementDelete,
|
|
267
442
|
addRequirementFlow,
|
|
443
|
+
selectedRequirements,
|
|
268
444
|
};
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
|
|
7
7
|
const inquirer = require('inquirer');
|
|
8
8
|
const chalk = require('chalk');
|
|
9
|
+
const { getHostname, isComputerNameEnabled, enableComputerName, disableComputerName, setHostname } = require('vibecodingmachine-core');
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Load settings data via the RUI command resolver
|
|
@@ -75,7 +76,7 @@ async function handleSettingSelection(key, data, navigation) {
|
|
|
75
76
|
*/
|
|
76
77
|
async function showSettings() {
|
|
77
78
|
const { showQuickMenu } = require('./trui-quick-menu');
|
|
78
|
-
const { getAutoConfig, getRepoPath, getStages, setAutoConfig } = require('./config');
|
|
79
|
+
const { getAutoConfig, getRepoPath, getStages, setStages, setAutoConfig } = require('./config');
|
|
79
80
|
const { getProviderPreferences } = require('./provider-registry');
|
|
80
81
|
const inquirer = require('inquirer');
|
|
81
82
|
|
|
@@ -89,8 +90,13 @@ async function showSettings() {
|
|
|
89
90
|
const repoPath = await getRepoPath();
|
|
90
91
|
const autoConfig = await getAutoConfig();
|
|
91
92
|
const prefs = await getProviderPreferences();
|
|
93
|
+
const stages = await getStages();
|
|
92
94
|
// Find first enabled provider in order for display
|
|
93
95
|
let activeIde = autoConfig.ide || autoConfig.agent || 'cline';
|
|
96
|
+
|
|
97
|
+
// Get hostname info
|
|
98
|
+
const currentHostname = getHostname();
|
|
99
|
+
const hostnameEnabled = isComputerNameEnabled();
|
|
94
100
|
|
|
95
101
|
const items = [];
|
|
96
102
|
items.push({ type: 'setting', name: `Repo: ${chalk.white(repoPath || '(not set)')}`, value: 'setting:repo' });
|
|
@@ -98,6 +104,8 @@ async function showSettings() {
|
|
|
98
104
|
items.push({ type: 'setting', name: `Max Chats: ${chalk.white(autoConfig.maxChats || '(unlimited)')}`, value: 'setting:maxChats' });
|
|
99
105
|
items.push({ type: 'setting', name: `Never Stop: ${chalk.white(autoConfig.neverStop ? 'true' : 'false')}`, value: 'setting:neverStop' });
|
|
100
106
|
items.push({ type: 'setting', name: `Skip Disabled: ${chalk.white(autoConfig.skipDisabled ? 'true' : 'false')}`, value: 'setting:skipDisabled' });
|
|
107
|
+
items.push({ type: 'setting', name: `Hostname: ${chalk.white(currentHostname)} (${hostnameEnabled ? 'enabled' : 'disabled'})`, value: 'setting:hostname' });
|
|
108
|
+
items.push({ type: 'setting', name: `Stages: ${chalk.white(stages.join(', '))}`, value: 'setting:stages' });
|
|
101
109
|
return items;
|
|
102
110
|
};
|
|
103
111
|
|
|
@@ -143,6 +151,111 @@ async function showSettings() {
|
|
|
143
151
|
} else if (value === 'setting:skipDisabled') {
|
|
144
152
|
const autoConfig = await getAutoConfig();
|
|
145
153
|
await setAutoConfig({ skipDisabled: !autoConfig.skipDisabled });
|
|
154
|
+
} else if (value === 'setting:hostname') {
|
|
155
|
+
const hostnameEnabled = isComputerNameEnabled();
|
|
156
|
+
const currentHostname = getHostname();
|
|
157
|
+
|
|
158
|
+
const { action } = await inquirer.prompt([{
|
|
159
|
+
type: 'list',
|
|
160
|
+
name: 'action',
|
|
161
|
+
message: 'Hostname settings:',
|
|
162
|
+
choices: [
|
|
163
|
+
{ name: `Change hostname (current: ${currentHostname})`, value: 'change' },
|
|
164
|
+
{ name: hostnameEnabled ? 'Disable hostname filtering' : 'Enable hostname filtering', value: 'toggle' },
|
|
165
|
+
{ name: 'Back', value: 'back' }
|
|
166
|
+
]
|
|
167
|
+
}]);
|
|
168
|
+
|
|
169
|
+
if (action === 'change') {
|
|
170
|
+
const { newHostname } = await inquirer.prompt([{
|
|
171
|
+
type: 'input',
|
|
172
|
+
name: 'newHostname',
|
|
173
|
+
message: 'Enter new hostname:',
|
|
174
|
+
default: currentHostname,
|
|
175
|
+
validate: input => {
|
|
176
|
+
if (!input.trim()) return 'Hostname cannot be empty';
|
|
177
|
+
if (!/^[a-zA-Z0-9-]+$/.test(input.trim())) {
|
|
178
|
+
return 'Hostname can only contain letters, numbers, and hyphens';
|
|
179
|
+
}
|
|
180
|
+
return true;
|
|
181
|
+
}
|
|
182
|
+
}]);
|
|
183
|
+
|
|
184
|
+
try {
|
|
185
|
+
await setHostname(newHostname.trim());
|
|
186
|
+
console.log(chalk.green(`✅ Hostname changed to: ${newHostname}`));
|
|
187
|
+
} catch (error) {
|
|
188
|
+
console.error(chalk.red('❌ Error changing hostname:'), error.message);
|
|
189
|
+
}
|
|
190
|
+
await inquirer.prompt([{ type: 'input', name: 'c', message: chalk.gray('Press Enter...') }]);
|
|
191
|
+
} else if (action === 'toggle') {
|
|
192
|
+
try {
|
|
193
|
+
if (hostnameEnabled) {
|
|
194
|
+
await disableComputerName();
|
|
195
|
+
console.log(chalk.green('✅ Hostname filtering disabled'));
|
|
196
|
+
} else {
|
|
197
|
+
await enableComputerName();
|
|
198
|
+
console.log(chalk.green('✅ Hostname filtering enabled'));
|
|
199
|
+
}
|
|
200
|
+
} catch (error) {
|
|
201
|
+
console.error(chalk.red('❌ Error toggling hostname filtering:'), error.message);
|
|
202
|
+
}
|
|
203
|
+
await inquirer.prompt([{ type: 'input', name: 'c', message: chalk.gray('Press Enter...') }]);
|
|
204
|
+
}
|
|
205
|
+
} else if (value === 'setting:stages') {
|
|
206
|
+
const stages = await getStages();
|
|
207
|
+
|
|
208
|
+
const { action } = await inquirer.prompt([{
|
|
209
|
+
type: 'list',
|
|
210
|
+
name: 'action',
|
|
211
|
+
message: 'Stage configuration:',
|
|
212
|
+
choices: [
|
|
213
|
+
{ name: `Edit stages (current: ${stages.join(', ')})`, value: 'edit' },
|
|
214
|
+
{ name: 'Reset to default stages', value: 'reset' },
|
|
215
|
+
{ name: 'Back', value: 'back' }
|
|
216
|
+
]
|
|
217
|
+
}]);
|
|
218
|
+
|
|
219
|
+
if (action === 'edit') {
|
|
220
|
+
const { newStages } = await inquirer.prompt([{
|
|
221
|
+
type: 'input',
|
|
222
|
+
name: 'newStages',
|
|
223
|
+
message: 'Enter stages (comma-separated):',
|
|
224
|
+
default: stages.join(', '),
|
|
225
|
+
validate: input => {
|
|
226
|
+
const stageList = input.split(',').map(s => s.trim()).filter(s => s);
|
|
227
|
+
if (stageList.length === 0) return 'At least one stage is required';
|
|
228
|
+
|
|
229
|
+
// Check for duplicate stages
|
|
230
|
+
const duplicates = stageList.filter((stage, index) => stageList.indexOf(stage) !== index);
|
|
231
|
+
if (duplicates.length > 0) return `Duplicate stages: ${duplicates.join(', ')}`;
|
|
232
|
+
|
|
233
|
+
// Check for valid stage names (alphanumeric with hyphens/underscores)
|
|
234
|
+
const invalidStages = stageList.filter(stage => !/^[a-zA-Z0-9_-]+$/.test(stage));
|
|
235
|
+
if (invalidStages.length > 0) return `Invalid stage names: ${invalidStages.join(', ')}`;
|
|
236
|
+
|
|
237
|
+
return true;
|
|
238
|
+
}
|
|
239
|
+
}]);
|
|
240
|
+
|
|
241
|
+
try {
|
|
242
|
+
const stageList = newStages.split(',').map(s => s.trim()).filter(s => s);
|
|
243
|
+
await setStages(stageList);
|
|
244
|
+
console.log(chalk.green(`✅ Stages updated to: ${stageList.join(', ')}`));
|
|
245
|
+
} catch (error) {
|
|
246
|
+
console.error(chalk.red('❌ Error updating stages:'), error.message);
|
|
247
|
+
}
|
|
248
|
+
await inquirer.prompt([{ type: 'input', name: 'c', message: chalk.gray('Press Enter...') }]);
|
|
249
|
+
} else if (action === 'reset') {
|
|
250
|
+
try {
|
|
251
|
+
const { DEFAULT_STAGES } = require('./config');
|
|
252
|
+
await setStages(DEFAULT_STAGES);
|
|
253
|
+
console.log(chalk.green(`✅ Stages reset to default: ${DEFAULT_STAGES.join(', ')}`));
|
|
254
|
+
} catch (error) {
|
|
255
|
+
console.error(chalk.red('❌ Error resetting stages:'), error.message);
|
|
256
|
+
}
|
|
257
|
+
await inquirer.prompt([{ type: 'input', name: 'c', message: chalk.gray('Press Enter...') }]);
|
|
258
|
+
}
|
|
146
259
|
}
|
|
147
260
|
|
|
148
261
|
printHeader();
|
|
@@ -43,7 +43,7 @@ function buildSpecificationChoices(specs) {
|
|
|
43
43
|
}
|
|
44
44
|
|
|
45
45
|
/**
|
|
46
|
-
* Handle selection of a specification item — shows detail
|
|
46
|
+
* Handle selection of a specification item — shows detail with enable/disable options
|
|
47
47
|
* @param {string} directory - Spec directory name
|
|
48
48
|
* @param {Array} specs
|
|
49
49
|
* @param {Object} navigation - TRUINavigation instance
|
|
@@ -57,13 +57,54 @@ async function handleSpecificationSelection(directory, specs, navigation) {
|
|
|
57
57
|
return;
|
|
58
58
|
}
|
|
59
59
|
|
|
60
|
+
const { enableSpec, disableSpec, isSpecDisabled } = require('@vibecodingmachine/core/src/utils/specification-enable-disable');
|
|
61
|
+
const isDisabled = await isSpecDisabled(directory);
|
|
62
|
+
|
|
60
63
|
console.log(chalk.bold.cyan(`\n📐 ${spec.title || spec.directory}\n`));
|
|
61
64
|
console.log(chalk.white(` Directory: ${spec.directory}`));
|
|
62
65
|
console.log(chalk.white(` Path: ${spec.path}`));
|
|
66
|
+
console.log(chalk.white(` Status: ${isDisabled ? chalk.red('Disabled') : chalk.green('Enabled')}`));
|
|
63
67
|
console.log(chalk.white(` Has Plan: ${spec.hasPlan ? chalk.green('Yes') : chalk.gray('No')}`));
|
|
64
68
|
console.log(chalk.white(` Has Tasks: ${spec.hasTasks ? chalk.green('Yes') : chalk.yellow('No (TODO)')}`));
|
|
65
69
|
|
|
66
|
-
|
|
70
|
+
// Show action options
|
|
71
|
+
console.log(chalk.bold.white('\nActions:'));
|
|
72
|
+
const choices = [
|
|
73
|
+
{ name: 'Back to menu', value: 'back' }
|
|
74
|
+
];
|
|
75
|
+
|
|
76
|
+
if (isDisabled) {
|
|
77
|
+
choices.unshift({ name: chalk.green('[e] Enable specification'), value: 'enable' });
|
|
78
|
+
} else {
|
|
79
|
+
choices.unshift({ name: chalk.red('[d] Disable specification'), value: 'disable' });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const { action } = await inquirer.prompt([
|
|
83
|
+
{
|
|
84
|
+
type: 'list',
|
|
85
|
+
name: 'action',
|
|
86
|
+
message: chalk.cyan('Choose action:'),
|
|
87
|
+
choices
|
|
88
|
+
}
|
|
89
|
+
]);
|
|
90
|
+
|
|
91
|
+
if (action === 'enable') {
|
|
92
|
+
const result = await enableSpec(directory);
|
|
93
|
+
if (result.success) {
|
|
94
|
+
console.log(chalk.green(`\n✅ ${result.message}`));
|
|
95
|
+
} else {
|
|
96
|
+
console.log(chalk.red(`\n❌ ${result.message}`));
|
|
97
|
+
}
|
|
98
|
+
await navigation.promptContinue();
|
|
99
|
+
} else if (action === 'disable') {
|
|
100
|
+
const result = await disableSpec(directory);
|
|
101
|
+
if (result.success) {
|
|
102
|
+
console.log(chalk.green(`\n✅ ${result.message}`));
|
|
103
|
+
} else {
|
|
104
|
+
console.log(chalk.red(`\n❌ ${result.message}`));
|
|
105
|
+
}
|
|
106
|
+
await navigation.promptContinue();
|
|
107
|
+
}
|
|
67
108
|
}
|
|
68
109
|
|
|
69
110
|
/**
|
|
@@ -103,7 +144,7 @@ async function addSpecificationFlow(navigation) {
|
|
|
103
144
|
}
|
|
104
145
|
const specPrompt = promptLines.join('\n');
|
|
105
146
|
|
|
106
|
-
console.log(chalk.gray('\
|
|
147
|
+
console.log(chalk.gray('\nTechnical details — leave blank to skip.'));
|
|
107
148
|
const planLines = [];
|
|
108
149
|
while (true) {
|
|
109
150
|
const { line } = await inquirer.prompt([
|