vibecodingmachine-cli 2025.12.1-534 → 2025.12.6-1702

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.
@@ -0,0 +1,306 @@
1
+ const chalk = require('chalk');
2
+ const Table = require('cli-table3');
3
+ const SyncEngine = require('vibecodingmachine-core/src/sync/sync-engine');
4
+
5
+ /**
6
+ * Display all registered computers with their status
7
+ */
8
+ async function listComputers(options = {}) {
9
+ const syncEngine = new SyncEngine();
10
+
11
+ try {
12
+ await syncEngine.initialize();
13
+
14
+ // Fetch all computers from DynamoDB
15
+ const computers = await fetchAllComputers(syncEngine);
16
+
17
+ if (computers.length === 0) {
18
+ console.log(chalk.yellow('\nNo computers registered yet.\n'));
19
+ console.log(chalk.gray('Computers will appear here once they sync for the first time.\n'));
20
+ return;
21
+ }
22
+
23
+ // Filter by focus area if specified
24
+ let filteredComputers = computers;
25
+ if (options.focus) {
26
+ filteredComputers = computers.filter(c =>
27
+ c.focusArea && c.focusArea.toLowerCase().includes(options.focus.toLowerCase())
28
+ );
29
+ }
30
+
31
+ // Filter by status if specified
32
+ if (options.status) {
33
+ filteredComputers = filteredComputers.filter(c =>
34
+ c.status && c.status.toLowerCase() === options.status.toLowerCase()
35
+ );
36
+ }
37
+
38
+ // Create table
39
+ const table = new Table({
40
+ head: [
41
+ chalk.cyan('Computer'),
42
+ chalk.cyan('Focus Area'),
43
+ chalk.cyan('Status'),
44
+ chalk.cyan('Current Requirement'),
45
+ chalk.cyan('Progress'),
46
+ chalk.cyan('Last Sync')
47
+ ],
48
+ colWidths: [25, 30, 12, 35, 10, 15]
49
+ });
50
+
51
+ // Add rows
52
+ for (const computer of filteredComputers) {
53
+ const statusIcon = getStatusIcon(computer.status);
54
+ const statusText = `${statusIcon} ${computer.status || 'unknown'}`;
55
+ const progress = computer.progress ? `${computer.progress}%` : '-';
56
+ const lastSync = computer.lastSyncTime
57
+ ? formatTimeSince(computer.lastSyncTime)
58
+ : 'never';
59
+
60
+ table.push([
61
+ computer.hostname || computer.computerId,
62
+ computer.focusArea || '-',
63
+ statusText,
64
+ truncate(computer.currentRequirement || '-', 33),
65
+ progress,
66
+ lastSync
67
+ ]);
68
+ }
69
+
70
+ console.log('\n' + table.toString() + '\n');
71
+
72
+ // Show summary
73
+ const activeCount = computers.filter(c => c.status === 'active').length;
74
+ const idleCount = computers.filter(c => c.status === 'idle').length;
75
+ const errorCount = computers.filter(c => c.status === 'error').length;
76
+
77
+ console.log(chalk.gray(`Total: ${computers.length} computers`));
78
+ console.log(chalk.green(` Active: ${activeCount}`));
79
+ console.log(chalk.yellow(` Idle: ${idleCount}`));
80
+ if (errorCount > 0) {
81
+ console.log(chalk.red(` Error: ${errorCount}`));
82
+ }
83
+ console.log('');
84
+
85
+ } catch (error) {
86
+ console.error(chalk.red('\n✗ Failed to fetch computers:'), error.message);
87
+ throw error;
88
+ } finally {
89
+ syncEngine.stop();
90
+ }
91
+ }
92
+
93
+ /**
94
+ * Show detailed status of a specific computer
95
+ */
96
+ async function showComputerStatus(computerId, options = {}) {
97
+ const syncEngine = new SyncEngine();
98
+
99
+ try {
100
+ await syncEngine.initialize();
101
+
102
+ const computer = await fetchComputer(syncEngine, computerId);
103
+
104
+ if (!computer) {
105
+ console.log(chalk.red(`\n✗ Computer '${computerId}' not found.\n`));
106
+ return;
107
+ }
108
+
109
+ // Display computer details
110
+ console.log('\n' + chalk.bold.cyan('Computer Details'));
111
+ console.log(chalk.gray('─'.repeat(60)));
112
+ console.log(chalk.white('Hostname: ') + (computer.hostname || computer.computerId));
113
+ console.log(chalk.white('Computer ID: ') + computer.computerId);
114
+ console.log(chalk.white('Focus Area: ') + (computer.focusArea || '-'));
115
+ console.log(chalk.white('OS: ') + (computer.os || '-'));
116
+ console.log(chalk.white('Status: ') + getStatusIcon(computer.status) + ' ' + (computer.status || 'unknown'));
117
+ console.log(chalk.white('Last Sync: ') + (computer.lastSyncTime ? formatTimeSince(computer.lastSyncTime) : 'never'));
118
+ console.log('');
119
+
120
+ // Current work
121
+ console.log(chalk.bold.cyan('Current Work'));
122
+ console.log(chalk.gray('─'.repeat(60)));
123
+ console.log(chalk.white('Requirement: ') + (computer.currentRequirement || 'None'));
124
+ console.log(chalk.white('Progress: ') + (computer.progress ? `${computer.progress}%` : '-'));
125
+ console.log(chalk.white('Time Active: ') + (computer.timeActive || '-'));
126
+ console.log(chalk.white('IDE: ') + (computer.ide || '-'));
127
+ console.log('');
128
+
129
+ // Statistics
130
+ if (computer.stats) {
131
+ console.log(chalk.bold.cyan('Statistics'));
132
+ console.log(chalk.gray('─'.repeat(60)));
133
+ console.log(chalk.white('Total Requirements: ') + (computer.stats.total || 0));
134
+ console.log(chalk.white('Completed: ') + chalk.green(computer.stats.completed || 0));
135
+ console.log(chalk.white('In Progress: ') + chalk.yellow(computer.stats.inProgress || 0));
136
+ console.log(chalk.white('To Verify: ') + chalk.blue(computer.stats.toVerify || 0));
137
+ console.log(chalk.white('Completion Rate: ') + (computer.stats.completionRate || '0%'));
138
+ console.log('');
139
+ }
140
+
141
+ } catch (error) {
142
+ console.error(chalk.red('\n✗ Failed to fetch computer status:'), error.message);
143
+ throw error;
144
+ } finally {
145
+ syncEngine.stop();
146
+ }
147
+ }
148
+
149
+ /**
150
+ * Register current computer with focus area
151
+ */
152
+ async function registerComputer(focusArea, options = {}) {
153
+ const syncEngine = new SyncEngine();
154
+
155
+ try {
156
+ await syncEngine.initialize();
157
+
158
+ const os = require('os');
159
+ const computerData = {
160
+ computerId: syncEngine.computerId,
161
+ hostname: os.hostname(),
162
+ focusArea: focusArea,
163
+ os: `${os.platform()} ${os.release()}`,
164
+ registeredAt: Date.now(),
165
+ lastSyncTime: Date.now(),
166
+ status: 'idle'
167
+ };
168
+
169
+ await saveComputer(syncEngine, computerData);
170
+
171
+ console.log(chalk.green('\n✓ Computer registered successfully!\n'));
172
+ console.log(chalk.white('Computer ID: ') + computerData.computerId);
173
+ console.log(chalk.white('Hostname: ') + computerData.hostname);
174
+ console.log(chalk.white('Focus Area: ') + computerData.focusArea);
175
+ console.log('');
176
+
177
+ } catch (error) {
178
+ console.error(chalk.red('\n✗ Failed to register computer:'), error.message);
179
+ throw error;
180
+ } finally {
181
+ syncEngine.stop();
182
+ }
183
+ }
184
+
185
+ /**
186
+ * Update focus area for current computer
187
+ */
188
+ async function updateFocus(newFocusArea, options = {}) {
189
+ const syncEngine = new SyncEngine();
190
+
191
+ try {
192
+ await syncEngine.initialize();
193
+
194
+ const computer = await fetchComputer(syncEngine, syncEngine.computerId);
195
+
196
+ if (!computer) {
197
+ console.log(chalk.yellow('\n⚠ Computer not registered. Registering now...\n'));
198
+ await registerComputer(newFocusArea, options);
199
+ return;
200
+ }
201
+
202
+ computer.focusArea = newFocusArea;
203
+ computer.lastSyncTime = Date.now();
204
+
205
+ await saveComputer(syncEngine, computer);
206
+
207
+ console.log(chalk.green('\n✓ Focus area updated!\n'));
208
+ console.log(chalk.white('Computer: ') + computer.hostname);
209
+ console.log(chalk.white('New Focus: ') + newFocusArea);
210
+ console.log('');
211
+
212
+ } catch (error) {
213
+ console.error(chalk.red('\n✗ Failed to update focus area:'), error.message);
214
+ throw error;
215
+ } finally {
216
+ syncEngine.stop();
217
+ }
218
+ }
219
+
220
+ // Helper functions
221
+
222
+ function getStatusIcon(status) {
223
+ switch (status) {
224
+ case 'active': return chalk.green('●');
225
+ case 'idle': return chalk.yellow('●');
226
+ case 'error': return chalk.red('●');
227
+ case 'offline': return chalk.gray('●');
228
+ default: return chalk.gray('○');
229
+ }
230
+ }
231
+
232
+ function formatTimeSince(timestamp) {
233
+ const now = Date.now();
234
+ const diff = now - timestamp;
235
+
236
+ const seconds = Math.floor(diff / 1000);
237
+ const minutes = Math.floor(seconds / 60);
238
+ const hours = Math.floor(minutes / 60);
239
+ const days = Math.floor(hours / 24);
240
+
241
+ if (days > 0) return `${days}d ago`;
242
+ if (hours > 0) return `${hours}h ago`;
243
+ if (minutes > 0) return `${minutes}m ago`;
244
+ return `${seconds}s ago`;
245
+ }
246
+
247
+ function truncate(str, maxLength) {
248
+ if (str.length <= maxLength) return str;
249
+ return str.substring(0, maxLength - 3) + '...';
250
+ }
251
+
252
+ async function fetchAllComputers(syncEngine) {
253
+ const { ScanCommand } = require('@aws-sdk/lib-dynamodb');
254
+
255
+ const tableName = 'vibecodingmachine-computers';
256
+
257
+ try {
258
+ const command = new ScanCommand({
259
+ TableName: tableName
260
+ });
261
+
262
+ const response = await syncEngine.dynamoClient.send(command);
263
+ return response.Items || [];
264
+ } catch (error) {
265
+ // Table might not exist yet
266
+ return [];
267
+ }
268
+ }
269
+
270
+ async function fetchComputer(syncEngine, computerId) {
271
+ const { GetCommand } = require('@aws-sdk/lib-dynamodb');
272
+
273
+ const tableName = 'vibecodingmachine-computers';
274
+
275
+ try {
276
+ const command = new GetCommand({
277
+ TableName: tableName,
278
+ Key: { computerId }
279
+ });
280
+
281
+ const response = await syncEngine.dynamoClient.send(command);
282
+ return response.Item;
283
+ } catch (error) {
284
+ return null;
285
+ }
286
+ }
287
+
288
+ async function saveComputer(syncEngine, computerData) {
289
+ const { PutCommand } = require('@aws-sdk/lib-dynamodb');
290
+
291
+ const tableName = 'vibecodingmachine-computers';
292
+
293
+ const command = new PutCommand({
294
+ TableName: tableName,
295
+ Item: computerData
296
+ });
297
+
298
+ await syncEngine.dynamoClient.send(command);
299
+ }
300
+
301
+ module.exports = {
302
+ listComputers,
303
+ showComputerStatus,
304
+ registerComputer,
305
+ updateFocus
306
+ };
@@ -0,0 +1,304 @@
1
+ const chalk = require('chalk');
2
+ const fs = require('fs-extra');
3
+ const path = require('path');
4
+ const inquirer = require('inquirer');
5
+
6
+ /**
7
+ * List requirements for a specific computer
8
+ */
9
+ async function listRemoteRequirements(computerId, options = {}) {
10
+ try {
11
+ const repoPath = process.cwd();
12
+ const reqFilename = `REQUIREMENTS-${computerId}.md`;
13
+ const reqPath = path.join(repoPath, '.vibecodingmachine', reqFilename);
14
+
15
+ if (!await fs.pathExists(reqPath)) {
16
+ console.log(chalk.yellow(`\n⚠ Requirements file not found for ${computerId}\n`));
17
+ console.log(chalk.gray(`Expected: ${reqPath}\n`));
18
+ return;
19
+ }
20
+
21
+ const content = await fs.readFile(reqPath, 'utf8');
22
+ const sections = parseRequirements(content);
23
+
24
+ console.log(chalk.bold.cyan(`\n📋 Requirements for ${computerId}\n`));
25
+
26
+ // Show counts
27
+ console.log(chalk.white('Summary:'));
28
+ console.log(chalk.yellow(` TODO: ${sections.todo.length}`));
29
+ console.log(chalk.cyan(` TO VERIFY: ${sections.toVerify.length}`));
30
+ console.log(chalk.green(` VERIFIED: ${sections.verified.length}`));
31
+ console.log('');
32
+
33
+ // Show TODO requirements
34
+ if (sections.todo.length > 0) {
35
+ console.log(chalk.bold.yellow('⏳ TODO Requirements:\n'));
36
+ sections.todo.forEach((req, idx) => {
37
+ console.log(chalk.yellow(`${idx + 1}. `) + req.title);
38
+ if (req.description) {
39
+ console.log(chalk.gray(' ' + req.description.substring(0, 100) + '...'));
40
+ }
41
+ });
42
+ console.log('');
43
+ }
44
+
45
+ // Show TO VERIFY requirements
46
+ if (sections.toVerify.length > 0) {
47
+ console.log(chalk.bold.cyan('🔍 TO VERIFY Requirements:\n'));
48
+ sections.toVerify.forEach((req, idx) => {
49
+ console.log(chalk.cyan(`${idx + 1}. `) + req.title);
50
+ });
51
+ console.log('');
52
+ }
53
+
54
+ } catch (error) {
55
+ console.error(chalk.red('\n✗ Failed to list requirements:'), error.message);
56
+ throw error;
57
+ }
58
+ }
59
+
60
+ /**
61
+ * Add a requirement to another computer's requirements file
62
+ */
63
+ async function addRemoteRequirement(computerId, requirementText, options = {}) {
64
+ try {
65
+ const repoPath = process.cwd();
66
+ const reqFilename = `REQUIREMENTS-${computerId}.md`;
67
+ const reqPath = path.join(repoPath, '.vibecodingmachine', reqFilename);
68
+
69
+ if (!await fs.pathExists(reqPath)) {
70
+ console.log(chalk.yellow(`\n⚠ Requirements file not found for ${computerId}\n`));
71
+ console.log(chalk.gray('Creating new requirements file...\n'));
72
+
73
+ // Create basic requirements file structure
74
+ const template = `# 🤖 Vibe Coding Machine – Requirements File
75
+
76
+ ## 💻 Computer Information
77
+ - **Hostname**: ${computerId}
78
+ - **Type**: Unknown
79
+ - **OS**: Unknown
80
+
81
+ ## 🎯 Focus
82
+ **General Development**
83
+
84
+ ---
85
+
86
+ ## RESPONSE FROM LAST CHAT
87
+
88
+
89
+ ## 🚦 Current Status
90
+ DONE
91
+
92
+ ## ⏳ Requirements not yet completed
93
+
94
+ ## 🔍 TO VERIFY BY HUMAN
95
+
96
+ ## 📝 VERIFIED
97
+
98
+ ## ❓ Requirements needing manual feedback
99
+
100
+ ## REJECTED by Human
101
+
102
+ ## ✅ Verified by AI screenshot. Needs Human to Verify and move to CHANGELOG
103
+
104
+ ## ♻️ Recycled
105
+
106
+ ## CHANGELOG
107
+ `;
108
+ await fs.writeFile(reqPath, template, 'utf8');
109
+ }
110
+
111
+ // Read current content
112
+ let content = await fs.readFile(reqPath, 'utf8');
113
+
114
+ // Find the TODO section
115
+ const todoMarker = '## ⏳ Requirements not yet completed';
116
+ const todoIndex = content.indexOf(todoMarker);
117
+
118
+ if (todoIndex === -1) {
119
+ console.log(chalk.red('\n✗ Could not find TODO section in requirements file\n'));
120
+ return;
121
+ }
122
+
123
+ // Find the next section after TODO
124
+ const nextSectionIndex = content.indexOf('\n## ', todoIndex + todoMarker.length);
125
+
126
+ // Insert the new requirement
127
+ const newRequirement = `\n### ${requirementText}\n`;
128
+
129
+ if (nextSectionIndex === -1) {
130
+ // No next section, append at end
131
+ content = content.substring(0, todoIndex + todoMarker.length) + newRequirement + content.substring(todoIndex + todoMarker.length);
132
+ } else {
133
+ // Insert before next section
134
+ content = content.substring(0, nextSectionIndex) + newRequirement + content.substring(nextSectionIndex);
135
+ }
136
+
137
+ // Write back
138
+ await fs.writeFile(reqPath, content, 'utf8');
139
+
140
+ console.log(chalk.green(`\n✓ Added requirement to ${computerId}\n`));
141
+ console.log(chalk.white('Requirement: ') + requirementText);
142
+ console.log('');
143
+
144
+ } catch (error) {
145
+ console.error(chalk.red('\n✗ Failed to add requirement:'), error.message);
146
+ throw error;
147
+ }
148
+ }
149
+
150
+ /**
151
+ * Interactive menu for managing another computer's requirements
152
+ */
153
+ async function manageRemoteRequirements(computerId) {
154
+ const choices = [
155
+ { name: '📋 View Requirements', value: 'view' },
156
+ { name: '➕ Add Requirement', value: 'add' },
157
+ { name: '📝 Edit Requirements File', value: 'edit' },
158
+ { name: chalk.gray('← Back'), value: 'back' }
159
+ ];
160
+
161
+ const { action } = await inquirer.prompt([
162
+ {
163
+ type: 'list',
164
+ name: 'action',
165
+ message: `Manage requirements for ${computerId}:`,
166
+ choices: choices
167
+ }
168
+ ]);
169
+
170
+ switch (action) {
171
+ case 'view':
172
+ await listRemoteRequirements(computerId);
173
+ console.log(chalk.gray('Press Enter to continue...'));
174
+ await new Promise(resolve => {
175
+ const readline = require('readline');
176
+ const rl = readline.createInterface({
177
+ input: process.stdin,
178
+ output: process.stdout
179
+ });
180
+ rl.question('', () => {
181
+ rl.close();
182
+ resolve();
183
+ });
184
+ });
185
+ await manageRemoteRequirements(computerId);
186
+ break;
187
+
188
+ case 'add':
189
+ const { requirement } = await inquirer.prompt([
190
+ {
191
+ type: 'input',
192
+ name: 'requirement',
193
+ message: 'Enter requirement title:'
194
+ }
195
+ ]);
196
+
197
+ if (requirement) {
198
+ await addRemoteRequirement(computerId, requirement);
199
+ console.log(chalk.gray('Press Enter to continue...'));
200
+ await new Promise(resolve => {
201
+ const readline = require('readline');
202
+ const rl = readline.createInterface({
203
+ input: process.stdin,
204
+ output: process.stdout
205
+ });
206
+ rl.question('', () => {
207
+ rl.close();
208
+ resolve();
209
+ });
210
+ });
211
+ }
212
+ await manageRemoteRequirements(computerId);
213
+ break;
214
+
215
+ case 'edit':
216
+ const repoPath = process.cwd();
217
+ const reqFilename = `REQUIREMENTS-${computerId}.md`;
218
+ const reqPath = path.join(repoPath, '.vibecodingmachine', reqFilename);
219
+
220
+ if (await fs.pathExists(reqPath)) {
221
+ const { spawn } = require('child_process');
222
+ const editor = process.env.EDITOR || 'nano';
223
+
224
+ console.log(chalk.cyan(`\nOpening ${reqFilename} in ${editor}...\n`));
225
+
226
+ const child = spawn(editor, [reqPath], {
227
+ stdio: 'inherit'
228
+ });
229
+
230
+ await new Promise((resolve) => {
231
+ child.on('exit', resolve);
232
+ });
233
+
234
+ console.log(chalk.green('\n✓ File saved\n'));
235
+ } else {
236
+ console.log(chalk.yellow(`\n⚠ Requirements file not found\n`));
237
+ }
238
+ await manageRemoteRequirements(computerId);
239
+ break;
240
+
241
+ case 'back':
242
+ break;
243
+ }
244
+ }
245
+
246
+ /**
247
+ * Parse requirements from markdown content
248
+ */
249
+ function parseRequirements(content) {
250
+ const sections = {
251
+ todo: [],
252
+ toVerify: [],
253
+ verified: []
254
+ };
255
+
256
+ const lines = content.split('\n');
257
+ let currentSection = null;
258
+ let currentRequirement = null;
259
+
260
+ for (let i = 0; i < lines.length; i++) {
261
+ const line = lines[i];
262
+
263
+ // Detect sections
264
+ if (line.includes('## ⏳ Requirements not yet completed')) {
265
+ currentSection = 'todo';
266
+ continue;
267
+ } else if (line.includes('## 🔍 TO VERIFY')) {
268
+ currentSection = 'toVerify';
269
+ continue;
270
+ } else if (line.includes('## 📝 VERIFIED')) {
271
+ currentSection = 'verified';
272
+ continue;
273
+ } else if (line.startsWith('## ') && !line.startsWith('###')) {
274
+ currentSection = null;
275
+ continue;
276
+ }
277
+
278
+ // Parse requirements
279
+ if (currentSection && line.startsWith('### ')) {
280
+ if (currentRequirement) {
281
+ sections[currentSection].push(currentRequirement);
282
+ }
283
+ currentRequirement = {
284
+ title: line.replace(/^###\s*/, '').trim(),
285
+ description: ''
286
+ };
287
+ } else if (currentRequirement && line.trim() && !line.startsWith('#')) {
288
+ currentRequirement.description += line + '\n';
289
+ }
290
+ }
291
+
292
+ // Add last requirement
293
+ if (currentRequirement && currentSection) {
294
+ sections[currentSection].push(currentRequirement);
295
+ }
296
+
297
+ return sections;
298
+ }
299
+
300
+ module.exports = {
301
+ listRemoteRequirements,
302
+ addRemoteRequirement,
303
+ manageRemoteRequirements
304
+ };