vibecodingmachine-cli 2025.12.6-1702 → 2025.12.22-2230

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.
@@ -1,7 +1,5 @@
1
1
  const fs = require('fs-extra');
2
- const path = require('path');
3
2
  const os = require('os');
4
- const { spawn } = require('child_process');
5
3
  const ora = require('ora');
6
4
  const chalk = require('chalk');
7
5
 
@@ -46,36 +44,47 @@ async function installKiro() {
46
44
  }
47
45
 
48
46
  try {
49
- const fetch = require('node-fetch');
50
47
  const fs = require('fs-extra');
51
48
  const path = require('path');
52
49
  const os = require('os');
53
50
  const { execSync } = require('child_process');
54
51
 
55
52
  console.log(chalk.cyan(`\n⬇️ Downloading AWS Kiro for macOS (${arch})...`));
56
- const downloadSpinner = ora('Downloading... (this may take a moment)').start();
57
-
58
- const response = await fetch(downloadUrl);
59
- if (!response.ok) {
60
- downloadSpinner.fail(`Failed to download: ${response.statusText}`);
61
- console.log(chalk.red(`\nURL: ${downloadUrl}`));
62
- throw new Error(`Download failed with status ${response.status}`);
63
- }
64
-
53
+ const cacheDir = path.join(os.homedir(), '.vibecodingmachine', 'cache');
54
+ await fs.ensureDir(cacheDir);
55
+ const cachedPath = path.join(cacheDir, 'Kiro.dmg');
65
56
  const tempDir = await fs.mkdtemp(path.join(os.tmpdir(), 'kiro-install-'));
66
57
  const dmgPath = path.join(tempDir, 'Kiro.dmg');
67
- const fileStream = fs.createWriteStream(dmgPath);
58
+ const { downloadWithProgress } = require('./download-with-progress');
68
59
 
69
- await new Promise((resolve, reject) => {
70
- response.body.pipe(fileStream);
71
- response.body.on('error', (err) => {
72
- downloadSpinner.fail('Download error');
73
- reject(err);
74
- });
75
- fileStream.on('finish', resolve);
76
- });
60
+ // Try to reuse cached DMG to avoid re-downloading
61
+ let usedCache = false;
62
+ try {
63
+ if (await fs.pathExists(cachedPath)) {
64
+ try {
65
+ const fetch = require('node-fetch');
66
+ const head = await fetch(downloadUrl, { method: 'HEAD' });
67
+ const total = Number(head.headers.get('content-length')) || 0;
68
+ const stat = await fs.stat(cachedPath);
69
+ if (total && stat.size === total) {
70
+ await fs.copy(cachedPath, dmgPath);
71
+ usedCache = true;
72
+ console.log(chalk.gray('Using cached Kiro DMG'));
73
+ }
74
+ } catch (e) {
75
+ await fs.copy(cachedPath, dmgPath);
76
+ usedCache = true;
77
+ console.log(chalk.gray('Using cached Kiro DMG (no HEAD)'));
78
+ }
79
+ }
80
+ } catch (e) {
81
+ usedCache = false;
82
+ }
77
83
 
78
- downloadSpinner.succeed('Download complete.');
84
+ if (!usedCache) {
85
+ await downloadWithProgress(downloadUrl, dmgPath, { label: 'Downloading AWS Kiro...' });
86
+ try { await fs.copy(dmgPath, cachedPath); } catch (e) { /* ignore */ }
87
+ }
79
88
 
80
89
  const installSpinner = ora('Installing AWS Kiro...').start();
81
90
 
@@ -104,8 +113,31 @@ async function installKiro() {
104
113
  installSpinner.text = 'Replacing existing installation...';
105
114
  await fs.remove(destinationPath);
106
115
  }
107
-
108
- await fs.copy(fullSourcePath, destinationPath);
116
+ // Use robust copy with progress (rsync -> ditto -> node stream)
117
+ try {
118
+ const { copyAppWithProgress } = require('./copy-with-progress');
119
+ installSpinner.stop();
120
+ const ok = await copyAppWithProgress(fullSourcePath, destinationPath, { spinner: installSpinner });
121
+ if (!ok) {
122
+ console.log('Falling back to cp -R for installation...');
123
+ installSpinner.start();
124
+ try {
125
+ execSync(`cp -R "${fullSourcePath}" "/Applications/"`, { stdio: 'inherit' });
126
+ } catch (e) {
127
+ installSpinner.text = 'Copying (fs fallback) to /Applications...';
128
+ await fs.copy(fullSourcePath, destinationPath);
129
+ }
130
+ } else {
131
+ installSpinner.start();
132
+ }
133
+ } catch (e) {
134
+ installSpinner.text = 'Copying (cp/fs fallback) to /Applications...';
135
+ try {
136
+ execSync(`cp -R "${fullSourcePath}" "/Applications/"`, { stdio: 'inherit' });
137
+ } catch (err) {
138
+ await fs.copy(fullSourcePath, destinationPath);
139
+ }
140
+ }
109
141
 
110
142
  // Unmount
111
143
  installSpinner.text = 'Cleaning up...';
@@ -76,9 +76,7 @@ function formatAutoModeHeader(status) {
76
76
  step = 'PREPARE',
77
77
  chatCount = 0,
78
78
  maxChats = null,
79
- progress = 0,
80
- repoPath = '',
81
- hostname = ''
79
+ progress = 0
82
80
  } = status;
83
81
 
84
82
  const stepColors = {
@@ -1,5 +1,3 @@
1
- const path = require('path');
2
- const os = require('os');
3
1
  const { getAutoConfig, setAutoConfig } = require('./config');
4
2
 
5
3
  const PROVIDER_DEFINITIONS = [
@@ -9,6 +7,7 @@ const PROVIDER_DEFINITIONS = [
9
7
  type: 'direct',
10
8
  category: 'llm',
11
9
  defaultModel: 'llama-3.3-70b-versatile',
10
+ fallbackModels: ['llama-3.1-70b-versatile', 'llama-3.1-8b-instant', 'mixtral-8x7b-32768'],
12
11
  configKeys: { apiKey: 'groqApiKey', model: 'groqModel' },
13
12
  estimatedSpeed: 15000
14
13
  },
@@ -17,7 +16,8 @@ const PROVIDER_DEFINITIONS = [
17
16
  name: 'Anthropic (Claude Sonnet 4)',
18
17
  type: 'direct',
19
18
  category: 'llm',
20
- defaultModel: 'claude-sonnet-4-20250514',
19
+ defaultModel: 'claude-3-5-sonnet-20241022',
20
+ fallbackModels: ['claude-3-5-haiku-20241022', 'claude-3-opus-20240229'],
21
21
  configKeys: { apiKey: 'anthropicApiKey', model: 'anthropicModel' },
22
22
  estimatedSpeed: 25000
23
23
  },
@@ -26,7 +26,8 @@ const PROVIDER_DEFINITIONS = [
26
26
  name: 'AWS Bedrock (Claude)',
27
27
  type: 'direct',
28
28
  category: 'llm',
29
- defaultModel: 'anthropic.claude-sonnet-4-v1',
29
+ defaultModel: 'anthropic.claude-3-5-sonnet-20241022-v2:0',
30
+ fallbackModels: ['anthropic.claude-3-5-haiku-20241022-v1:0', 'anthropic.claude-3-sonnet-20240229-v1:0'],
30
31
  configKeys: { region: 'awsRegion', accessKeyId: 'awsAccessKeyId', secretAccessKey: 'awsSecretAccessKey' },
31
32
  estimatedSpeed: 40000
32
33
  },
@@ -0,0 +1,300 @@
1
+ /**
2
+ * User Tracking Integration for CLI
3
+ *
4
+ * This module integrates the CLI with the enhanced user tracking system,
5
+ * automatically logging user activities and updating usage statistics.
6
+ */
7
+
8
+ const auth = require('./auth')
9
+
10
+ class CLIUserTracking {
11
+ constructor() {
12
+ this.sessionStartTime = Date.now()
13
+ this.activityBuffer = []
14
+ this.flushInterval = null
15
+
16
+ // Start periodic flush of activity data
17
+ this.startActivityFlush()
18
+ }
19
+
20
+ /**
21
+ * Track CLI command execution
22
+ */
23
+ async trackCommand(command, args = [], metadata = {}) {
24
+ try {
25
+ await auth.trackCLIActivity('command_executed', {
26
+ command,
27
+ args: args.filter(arg => !arg.includes('password') && !arg.includes('token')), // Filter sensitive data
28
+ duration: 0,
29
+ ...metadata
30
+ })
31
+ } catch (error) {
32
+ // Silently fail - don't disrupt user experience
33
+ console.debug('Failed to track command:', error.message)
34
+ }
35
+ }
36
+
37
+ /**
38
+ * Track requirement operations
39
+ */
40
+ async trackRequirementActivity(action, requirementData = {}) {
41
+ try {
42
+ await auth.trackCLIActivity('requirement_activity', {
43
+ action, // 'created', 'updated', 'completed', 'verified', etc.
44
+ requirementId: requirementData.id,
45
+ package: requirementData.package,
46
+ duration: 0,
47
+ metadata: {
48
+ hasDescription: !!requirementData.description,
49
+ descriptionLength: requirementData.description?.length || 0,
50
+ timestamp: Date.now()
51
+ }
52
+ })
53
+ } catch (error) {
54
+ console.debug('Failed to track requirement activity:', error.message)
55
+ }
56
+ }
57
+
58
+ /**
59
+ * Track auto mode sessions
60
+ */
61
+ async trackAutoModeSession(action, sessionData = {}) {
62
+ try {
63
+ await auth.trackCLIActivity('auto_mode', {
64
+ action, // 'started', 'stopped', 'iteration_completed'
65
+ duration: sessionData.duration || 0,
66
+ metadata: {
67
+ maxIterations: sessionData.maxIterations,
68
+ completedIterations: sessionData.completedIterations,
69
+ ide: sessionData.ide,
70
+ timestamp: Date.now()
71
+ }
72
+ })
73
+ } catch (error) {
74
+ console.debug('Failed to track auto mode session:', error.message)
75
+ }
76
+ }
77
+
78
+ /**
79
+ * Track IDE interactions
80
+ */
81
+ async trackIDEInteraction(ide, action, metadata = {}) {
82
+ try {
83
+ await auth.trackCLIActivity('ide_interaction', {
84
+ action, // 'opened', 'sent_message', 'received_response'
85
+ ide,
86
+ duration: metadata.duration || 0,
87
+ metadata: {
88
+ messageLength: metadata.messageLength,
89
+ responseLength: metadata.responseLength,
90
+ success: metadata.success !== false,
91
+ timestamp: Date.now()
92
+ }
93
+ })
94
+ } catch (error) {
95
+ console.debug('Failed to track IDE interaction:', error.message)
96
+ }
97
+ }
98
+
99
+ /**
100
+ * Track project operations
101
+ */
102
+ async trackProjectActivity(action, projectData = {}) {
103
+ try {
104
+ await auth.trackCLIActivity('project_activity', {
105
+ action, // 'created', 'opened', 'switched'
106
+ duration: 0,
107
+ metadata: {
108
+ projectPath: projectData.path,
109
+ hasDescription: !!projectData.description,
110
+ techStack: projectData.techStack || [],
111
+ timestamp: Date.now()
112
+ }
113
+ })
114
+ } catch (error) {
115
+ console.debug('Failed to track project activity:', error.message)
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Track sync operations
121
+ */
122
+ async trackSyncActivity(action, syncData = {}) {
123
+ try {
124
+ await auth.trackCLIActivity('sync_activity', {
125
+ action, // 'started', 'completed', 'failed'
126
+ duration: syncData.duration || 0,
127
+ metadata: {
128
+ itemsSync: syncData.itemsSync || 0,
129
+ conflicts: syncData.conflicts || 0,
130
+ success: syncData.success !== false,
131
+ timestamp: Date.now()
132
+ }
133
+ })
134
+ } catch (error) {
135
+ console.debug('Failed to track sync activity:', error.message)
136
+ }
137
+ }
138
+
139
+ /**
140
+ * Track session duration and end session
141
+ */
142
+ async endSession() {
143
+ try {
144
+ const sessionDuration = Date.now() - this.sessionStartTime
145
+
146
+ await auth.trackCLIActivity('session_ended', {
147
+ duration: Math.round(sessionDuration / 1000), // Convert to seconds
148
+ metadata: {
149
+ sessionStartTime: this.sessionStartTime,
150
+ sessionEndTime: Date.now(),
151
+ totalCommands: this.activityBuffer.length,
152
+ timestamp: Date.now()
153
+ }
154
+ })
155
+
156
+ // Stop activity flushing
157
+ if (this.flushInterval) {
158
+ clearInterval(this.flushInterval)
159
+ }
160
+ } catch (error) {
161
+ console.debug('Failed to track session end:', error.message)
162
+ }
163
+ }
164
+
165
+ /**
166
+ * Start periodic flushing of activity data
167
+ */
168
+ startActivityFlush() {
169
+ // Flush activity buffer every 5 minutes
170
+ this.flushInterval = setInterval(() => {
171
+ this.flushActivityBuffer()
172
+ }, 5 * 60 * 1000)
173
+ }
174
+
175
+ /**
176
+ * Flush buffered activity data
177
+ */
178
+ async flushActivityBuffer() {
179
+ if (this.activityBuffer.length === 0) return
180
+
181
+ try {
182
+ // Update user activity timestamp
183
+ const token = await auth.getToken()
184
+ if (token && token.id_token) {
185
+ await auth._updateUserActivity()
186
+ }
187
+
188
+ // Clear buffer after successful flush
189
+ this.activityBuffer = []
190
+ } catch (error) {
191
+ console.debug('Failed to flush activity buffer:', error.message)
192
+ }
193
+ }
194
+
195
+ /**
196
+ * Register project with enhanced tracking
197
+ */
198
+ async registerProject(projectInfo) {
199
+ try {
200
+ const token = await auth.getToken()
201
+ if (!token || !token.id_token) return null
202
+
203
+ const payload = JSON.parse(Buffer.from(token.id_token.split('.')[1], 'base64').toString())
204
+
205
+ const UserDatabase = require('vibecodingmachine-core/src/database/user-schema')
206
+ const userDb = new UserDatabase()
207
+
208
+ const userId = userDb.generateUserId(payload.email)
209
+
210
+ const project = await userDb.registerProject(userId, {
211
+ name: projectInfo.name || 'Unnamed Project',
212
+ description: projectInfo.description || '',
213
+ path: projectInfo.path || process.cwd(),
214
+ techStack: projectInfo.techStack || [],
215
+ framework: projectInfo.framework || '',
216
+ language: projectInfo.language || '',
217
+ goals: projectInfo.goals || '',
218
+ category: projectInfo.category || 'general'
219
+ })
220
+
221
+ // Track project creation
222
+ await this.trackProjectActivity('created', {
223
+ path: project.path,
224
+ description: project.description,
225
+ techStack: project.techStack
226
+ })
227
+
228
+ return project
229
+ } catch (error) {
230
+ console.debug('Failed to register project:', error.message)
231
+ return null
232
+ }
233
+ }
234
+
235
+ /**
236
+ * Check and prompt for compliance if needed
237
+ */
238
+ async checkCompliance() {
239
+ try {
240
+ const token = await auth.getToken()
241
+ if (!token || !token.id_token) return { compliant: false }
242
+
243
+ const payload = JSON.parse(Buffer.from(token.id_token.split('.')[1], 'base64').toString())
244
+
245
+ const ComplianceManager = require('vibecodingmachine-core/src/compliance/compliance-manager')
246
+ const compliance = new ComplianceManager()
247
+
248
+ const UserDatabase = require('vibecodingmachine-core/src/database/user-schema')
249
+ const userDb = new UserDatabase()
250
+ const userId = userDb.generateUserId(payload.email)
251
+
252
+ const status = await compliance.checkComplianceStatus(userId)
253
+
254
+ return {
255
+ compliant: !status.needsAcknowledgment,
256
+ termsAccepted: status.termsAccepted,
257
+ privacyAccepted: status.privacyAccepted,
258
+ requiredActions: status.requiredActions
259
+ }
260
+ } catch (error) {
261
+ console.debug('Failed to check compliance:', error.message)
262
+ return { compliant: true } // Default to compliant to not block users
263
+ }
264
+ }
265
+
266
+ /**
267
+ * Record compliance acknowledgment
268
+ */
269
+ async recordCompliance(acknowledgments) {
270
+ try {
271
+ const token = await auth.getToken()
272
+ if (!token || !token.id_token) return false
273
+
274
+ const payload = JSON.parse(Buffer.from(token.id_token.split('.')[1], 'base64').toString())
275
+
276
+ const ComplianceManager = require('vibecodingmachine-core/src/compliance/compliance-manager')
277
+ const compliance = new ComplianceManager()
278
+
279
+ const UserDatabase = require('vibecodingmachine-core/src/database/user-schema')
280
+ const userDb = new UserDatabase()
281
+ const userId = userDb.generateUserId(payload.email)
282
+
283
+ await compliance.recordAcknowledgment(userId, acknowledgments)
284
+
285
+ // Track compliance activity
286
+ await this.trackCommand('compliance_acknowledged', [], {
287
+ termsAccepted: acknowledgments.terms,
288
+ privacyAccepted: acknowledgments.privacy
289
+ })
290
+
291
+ return true
292
+ } catch (error) {
293
+ console.debug('Failed to record compliance:', error.message)
294
+ return false
295
+ }
296
+ }
297
+ }
298
+
299
+ // Export singleton instance
300
+ module.exports = new CLIUserTracking()
@@ -0,0 +1,28 @@
1
+ const fs = require('fs');
2
+ const path = require('path');
3
+
4
+ describe('Requirements Navigator regression: buildTree must be awaited', () => {
5
+ test('interactive.js does not call buildTree() without await', () => {
6
+ const interactivePath = path.join(__dirname, '../src/utils/interactive.js');
7
+ const content = fs.readFileSync(interactivePath, 'utf8');
8
+ const lines = content.split('\n');
9
+
10
+ const offending = [];
11
+
12
+ for (let i = 0; i < lines.length; i++) {
13
+ const line = lines[i];
14
+ if (!line.includes('buildTree();')) continue;
15
+
16
+ const trimmed = line.trim();
17
+
18
+ // Ignore the function definition line(s)
19
+ if (trimmed.startsWith('const buildTree') || trimmed.startsWith('function buildTree')) continue;
20
+
21
+ if (trimmed !== 'await buildTree();') {
22
+ offending.push({ lineNumber: i + 1, line: trimmed });
23
+ }
24
+ }
25
+
26
+ expect(offending).toEqual([]);
27
+ });
28
+ });