vibecodingmachine-core 2025.12.1-534 โ†’ 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.
@@ -0,0 +1,324 @@
1
+ const fs = require('fs-extra');
2
+ const path = require('path');
3
+
4
+ /**
5
+ * Parse requirements file and extract all sections
6
+ * This matches the CLI's parsing logic to ensure consistency
7
+ * @param {string} content - The requirements file content
8
+ * @returns {Object} Parsed requirements with sections: requirements, completed, needInformation, changelog, verified
9
+ */
10
+ function parseRequirementsFile(content) {
11
+ const lines = content.split('\n');
12
+ const requirements = [];
13
+ const completed = [];
14
+ const needInformation = [];
15
+ const changelog = [];
16
+ const verified = [];
17
+ const current = [];
18
+
19
+ let currentSection = '';
20
+ let inSection = false;
21
+
22
+ // Section detection patterns (matching CLI logic)
23
+ const sectionPatterns = {
24
+ todo: ['โณ Requirements not yet completed', 'Requirements not yet completed'],
25
+ verify: [
26
+ '๐Ÿ” TO VERIFY BY HUMAN',
27
+ 'TO VERIFY BY HUMAN',
28
+ '๐Ÿ” TO VERIFY',
29
+ 'TO VERIFY',
30
+ 'โœ… TO VERIFY',
31
+ 'โœ… Verified by AI screenshot. Needs Human to Verify and move to CHANGELOG',
32
+ 'Verified by AI screenshot. Needs Human to Verify and move to CHANGELOG',
33
+ 'Verified by AI screenshot'
34
+ ],
35
+ verified: ['๐Ÿ“ VERIFIED', 'VERIFIED'],
36
+ changelog: ['CHANGELOG'],
37
+ needInformation: ['โ“ Requirements needing', 'Requirements needing', 'needing manual feedback', 'needing information'],
38
+ current: ['๐Ÿ”จ Current In Progress Requirement', 'Current In Progress Requirement']
39
+ };
40
+
41
+ for (let i = 0; i < lines.length; i++) {
42
+ const line = lines[i];
43
+ const trimmed = line.trim();
44
+
45
+ // Check for section headers (## but not ###)
46
+ if (trimmed.startsWith('##') && !trimmed.startsWith('###')) {
47
+ // Determine which section we're entering
48
+ if (sectionPatterns.todo.some(pattern => trimmed.includes(pattern))) {
49
+ currentSection = 'todo';
50
+ inSection = true;
51
+ continue;
52
+ } else if (sectionPatterns.verify.some(pattern => {
53
+ const matches = trimmed.includes(pattern);
54
+ // Make sure it's not a VERIFIED section (without TO VERIFY)
55
+ if (matches && !trimmed.includes('๐Ÿ“ VERIFIED') && !trimmed.match(/^##\s+VERIFIED$/i) && !trimmed.includes('๐Ÿ“ VERIFIED')) {
56
+ return true;
57
+ }
58
+ return false;
59
+ })) {
60
+ currentSection = 'verify';
61
+ inSection = true;
62
+ continue;
63
+ } else if (sectionPatterns.verified.some(pattern => trimmed.includes(pattern))) {
64
+ currentSection = 'verified';
65
+ inSection = true;
66
+ continue;
67
+ } else if (sectionPatterns.changelog.some(pattern => trimmed.includes(pattern))) {
68
+ currentSection = 'changelog';
69
+ inSection = true;
70
+ continue;
71
+ } else if (sectionPatterns.needInformation.some(pattern => trimmed.includes(pattern))) {
72
+ currentSection = 'needInformation';
73
+ inSection = true;
74
+ continue;
75
+ } else if (sectionPatterns.current.some(pattern => trimmed.includes(pattern))) {
76
+ currentSection = 'current';
77
+ inSection = true;
78
+ continue;
79
+ } else {
80
+ // Different section header - exit current section
81
+ if (inSection) {
82
+ // Check if we're leaving TO VERIFY section
83
+ if (currentSection === 'verify') {
84
+ const isTodoSection = trimmed.includes('โณ Requirements not yet completed') || trimmed.includes('Requirements not yet completed');
85
+ const isVerifiedSection = trimmed === '## ๐Ÿ“ VERIFIED' || trimmed.startsWith('## ๐Ÿ“ VERIFIED');
86
+ const isRecycledSection = trimmed === '## โ™ป๏ธ RECYCLED' || trimmed.startsWith('## โ™ป๏ธ RECYCLED') ||
87
+ trimmed === '## ๐Ÿ“ฆ RECYCLED' || trimmed.startsWith('## ๐Ÿ“ฆ RECYCLED');
88
+ const isClarificationSection = trimmed.includes('## โ“ Requirements needing') || trimmed.includes('โ“ Requirements needing');
89
+
90
+ if (isVerifiedSection || isTodoSection || isRecycledSection || isClarificationSection) {
91
+ inSection = false;
92
+ currentSection = '';
93
+ }
94
+ // Otherwise continue - might be REJECTED or CHANGELOG which are not section boundaries for TO VERIFY
95
+ } else {
96
+ inSection = false;
97
+ currentSection = '';
98
+ }
99
+ }
100
+ continue;
101
+ }
102
+ }
103
+
104
+ // Parse requirements in new format (### header)
105
+ if (inSection && trimmed.startsWith('###')) {
106
+ const title = trimmed.replace(/^###\s*/, '').trim();
107
+
108
+ // Skip malformed requirements (title is just a package name, empty, or too short)
109
+ const packageNames = ['cli', 'core', 'electron-app', 'web', 'mobile', 'vscode-extension', 'sync-server'];
110
+ if (!title || title.length === 0 || packageNames.includes(title.toLowerCase())) {
111
+ continue;
112
+ }
113
+
114
+ const details = [];
115
+ let package = null;
116
+ let options = null;
117
+ let optionsType = null;
118
+
119
+ // Read package, description, and options
120
+ for (let j = i + 1; j < lines.length; j++) {
121
+ const nextLine = lines[j].trim();
122
+
123
+ // Stop if we hit another requirement or section
124
+ if (nextLine.startsWith('###') || (nextLine.startsWith('##') && !nextLine.startsWith('###'))) {
125
+ break;
126
+ }
127
+
128
+ // Check for PACKAGE line
129
+ if (nextLine.startsWith('PACKAGE:')) {
130
+ package = nextLine.replace(/^PACKAGE:\s*/, '').trim();
131
+ }
132
+ // Check for OPTIONS line (for need information requirements)
133
+ else if (nextLine.startsWith('OPTIONS:')) {
134
+ optionsType = 'checkbox'; // default
135
+ const optionsText = nextLine.replace(/^OPTIONS:\s*/, '').trim();
136
+ // Parse options (could be checkbox or radio)
137
+ if (optionsText.includes('|')) {
138
+ options = optionsText.split('|').map(opt => opt.trim()).filter(opt => opt);
139
+ } else {
140
+ options = [optionsText];
141
+ }
142
+ }
143
+ // Description line (include all lines including empty ones to preserve formatting)
144
+ else if (nextLine !== undefined) {
145
+ details.push(nextLine);
146
+ }
147
+ }
148
+
149
+ const requirement = {
150
+ title,
151
+ description: details.join('\n'),
152
+ package,
153
+ options,
154
+ optionsType,
155
+ lineIndex: i
156
+ };
157
+
158
+ // Add to appropriate array based on current section
159
+ if (currentSection === 'todo') {
160
+ requirements.push(requirement);
161
+ } else if (currentSection === 'verify') {
162
+ completed.push(requirement);
163
+ } else if (currentSection === 'needInformation') {
164
+ needInformation.push(requirement);
165
+ } else if (currentSection === 'changelog') {
166
+ changelog.push(requirement);
167
+ } else if (currentSection === 'verified') {
168
+ verified.push(requirement);
169
+ } else if (currentSection === 'current') {
170
+ current.push(requirement);
171
+ }
172
+ }
173
+
174
+ // Also support old format (lines starting with -) for backwards compatibility
175
+ if (inSection && trimmed.startsWith('- ') && !trimmed.startsWith('###')) {
176
+ const requirementText = trimmed.substring(2).trim();
177
+ if (requirementText) {
178
+ // Extract date if present (format: "2025-12-25 2:35 PM MDT - " or "2025-10-03: ")
179
+ const dateTimeMatch = requirementText.match(/^(\d{4}-\d{2}-\d{2})(?:\s+\d{1,2}:\d{2}\s+[AP]M(?:\s+[A-Z]{2,5})?)?\s*[-:]\s*/);
180
+ const date = dateTimeMatch ? dateTimeMatch[1] : null;
181
+ const title = dateTimeMatch ? requirementText.substring(dateTimeMatch[0].length).trim() : requirementText;
182
+
183
+ const requirement = {
184
+ title: title.length > 100 ? title.substring(0, 100) + '...' : title,
185
+ description: title,
186
+ date: date
187
+ };
188
+
189
+ if (currentSection === 'todo') {
190
+ requirements.push(requirement);
191
+ } else if (currentSection === 'verify' || currentSection === 'completed') {
192
+ completed.push(requirement);
193
+ } else if (currentSection === 'changelog') {
194
+ changelog.push(requirement);
195
+ }
196
+ }
197
+ }
198
+ }
199
+
200
+ // Remove duplicates based on title (keep first occurrence)
201
+ const seenTitles = new Set();
202
+ const uniqueRequirements = [];
203
+ for (const req of requirements) {
204
+ const normalizedTitle = req.title.replace(/^TRY AGAIN \(\d+(st|nd|rd|th) time\):\s*/i, '').trim();
205
+ if (!seenTitles.has(normalizedTitle)) {
206
+ seenTitles.add(normalizedTitle);
207
+ uniqueRequirements.push(req);
208
+ }
209
+ }
210
+
211
+ return {
212
+ requirements: uniqueRequirements,
213
+ completed,
214
+ needInformation,
215
+ changelog,
216
+ verified,
217
+ current
218
+ };
219
+ }
220
+
221
+ /**
222
+ * Load verified requirements from CHANGELOG.md
223
+ * @param {string} repoPath - Path to repository root
224
+ * @returns {Promise<Array>} Array of verified requirement titles
225
+ */
226
+ async function loadVerifiedFromChangelog(repoPath) {
227
+ try {
228
+ const changelogPath = path.join(repoPath, 'CHANGELOG.md');
229
+ if (!await fs.pathExists(changelogPath)) {
230
+ return [];
231
+ }
232
+
233
+ const content = await fs.readFile(changelogPath, 'utf8');
234
+ const lines = content.split('\n');
235
+ const requirements = [];
236
+ let inVerifiedSection = false;
237
+
238
+ for (const line of lines) {
239
+ const trimmed = line.trim();
240
+
241
+ // Check for Verified Requirements section
242
+ if (trimmed.includes('## Verified Requirements')) {
243
+ inVerifiedSection = true;
244
+ continue;
245
+ }
246
+
247
+ // Exit section if we hit another ## header
248
+ if (inVerifiedSection && trimmed.startsWith('##') && !trimmed.includes('Verified Requirements')) {
249
+ break;
250
+ }
251
+
252
+ // Only collect items from within the Verified Requirements section
253
+ if (inVerifiedSection && trimmed.startsWith('- ') && trimmed.length > 10) {
254
+ const title = trimmed.substring(2).trim();
255
+ // Extract date if present (format: "Title (2025-12-05)")
256
+ const dateMatch = title.match(/^(.+?)\s*\((\d{4}-\d{2}-\d{2})\)$/);
257
+ const cleanTitle = dateMatch ? dateMatch[1].trim() : title;
258
+ const date = dateMatch ? dateMatch[2] : null;
259
+
260
+ requirements.push({
261
+ title: cleanTitle,
262
+ description: cleanTitle,
263
+ date: date
264
+ });
265
+ }
266
+ }
267
+
268
+ return requirements;
269
+ } catch (error) {
270
+ // If CHANGELOG.md doesn't exist or can't be read, return empty array
271
+ return [];
272
+ }
273
+ }
274
+
275
+ /**
276
+ * Load and parse requirements from file
277
+ * @param {string} reqPath - Path to requirements file
278
+ * @param {string} repoPath - Optional path to repository root (for loading CHANGELOG.md)
279
+ * @returns {Promise<Object>} Parsed requirements
280
+ */
281
+ async function loadRequirementsFromFile(reqPath, repoPath = null) {
282
+ try {
283
+ const parsed = {
284
+ requirements: [],
285
+ completed: [],
286
+ needInformation: [],
287
+ changelog: [],
288
+ verified: [],
289
+ current: []
290
+ };
291
+
292
+ if (await fs.pathExists(reqPath)) {
293
+ const content = await fs.readFile(reqPath, 'utf8');
294
+ const fileParsed = parseRequirementsFile(content);
295
+ Object.assign(parsed, fileParsed);
296
+ }
297
+
298
+ // Also load verified from CHANGELOG.md if repoPath is provided
299
+ if (repoPath) {
300
+ const verifiedFromChangelog = await loadVerifiedFromChangelog(repoPath);
301
+ // Merge with verified from requirements file (CHANGELOG.md takes precedence)
302
+ if (verifiedFromChangelog.length > 0) {
303
+ parsed.verified = verifiedFromChangelog;
304
+ }
305
+ }
306
+
307
+ return parsed;
308
+ } catch (error) {
309
+ throw new Error(`Failed to load requirements from file: ${error.message}`);
310
+ }
311
+ }
312
+
313
+ module.exports = {
314
+ parseRequirementsFile,
315
+ loadRequirementsFromFile,
316
+ loadVerifiedFromChangelog
317
+ };
318
+
319
+
320
+
321
+
322
+
323
+
324
+
@@ -1,6 +1,7 @@
1
1
  const https = require('https');
2
2
  const fs = require('fs');
3
3
  const path = require('path');
4
+ const { shouldCheckUpdates } = require('./env-helpers');
4
5
 
5
6
  /**
6
7
  * Check for updates from npm registry
@@ -9,6 +10,9 @@ const path = require('path');
9
10
  * @returns {Promise<Object>} Update info or null if no update available
10
11
  */
11
12
  async function checkForUpdates(packageName, currentVersion) {
13
+ if (!shouldCheckUpdates()) {
14
+ return { hasUpdate: false, currentVersion };
15
+ }
12
16
  return new Promise((resolve, reject) => {
13
17
  const registryUrl = `https://registry.npmjs.org/${packageName}`;
14
18
 
@@ -80,6 +84,9 @@ function isVersionNewer(version1, version2) {
80
84
  * @returns {Promise<Object>} Update info or null if no update available
81
85
  */
82
86
  async function checkForCLIUpdates(currentVersion) {
87
+ if (!shouldCheckUpdates()) {
88
+ return { hasUpdate: false, currentVersion };
89
+ }
83
90
  return new Promise((resolve, reject) => {
84
91
  const manifestUrl = `https://d3fh7zgi8horze.cloudfront.net/downloads/version.json?t=${Date.now()}`;
85
92
 
@@ -0,0 +1,67 @@
1
+ const { fetchQuotaForAgent } = require('./src/quota-management/index.js');
2
+ const sharedAuth = require('./src/auth/shared-auth-storage');
3
+ const ProviderManager = require('./src/ide-integration/provider-manager.cjs');
4
+
5
+ async function testQuotaSystem() {
6
+ console.log('๐Ÿงช Starting Quota System Verification Test...');
7
+
8
+ // 1. Test Global Iterations Quota
9
+ console.log('\n--- Test 1: Global Iterations ---');
10
+ // Mock sharedAuth.canRunAutoMode
11
+ const originalCanRun = sharedAuth.canRunAutoMode;
12
+ sharedAuth.canRunAutoMode = async () => ({
13
+ canRun: true,
14
+ todayUsage: 4,
15
+ maxIterations: 10
16
+ });
17
+
18
+ try {
19
+ const globalQuota = await fetchQuotaForAgent('global:iterations');
20
+ console.log('Global Quota Type:', globalQuota.type);
21
+ console.log('Global Remaining:', globalQuota.remaining);
22
+ console.log('Global Limit:', globalQuota.limit);
23
+ if (globalQuota.remaining === 6 && globalQuota.type === 'global') {
24
+ console.log('โœ… Global iterations test passed');
25
+ } else {
26
+ console.log('โŒ Global iterations test failed');
27
+ }
28
+ } catch (error) {
29
+ console.error('โŒ Global iterations test error:', error);
30
+ } finally {
31
+ sharedAuth.canRunAutoMode = originalCanRun;
32
+ }
33
+
34
+ // 2. Test Local Agent (Infinite)
35
+ console.log('\n--- Test 2: Local Agent (Infinite) ---');
36
+ try {
37
+ const localQuota = await fetchQuotaForAgent('local-ollama:qwen2.5');
38
+ console.log('Local Quota Type:', localQuota.type);
39
+ console.log('Local Remaining:', localQuota.remaining);
40
+ if (localQuota.remaining === Infinity && localQuota.type === 'infinite') {
41
+ console.log('โœ… Local agent test passed');
42
+ } else {
43
+ console.log('โŒ Local agent test failed');
44
+ }
45
+ } catch (error) {
46
+ console.error('โŒ Local agent test error:', error);
47
+ }
48
+
49
+ // 3. Test Provider Rate Limit
50
+ console.log('\n--- Test 3: Provider Rate Limit ---');
51
+ try {
52
+ const providerQuota = await fetchQuotaForAgent('anthropic:claude-3');
53
+ console.log('Provider Quota Type:', providerQuota.type);
54
+ console.log('Provider Available (Remaining > 0):', providerQuota.remaining > 0);
55
+ if (providerQuota.type === 'rate-limit') {
56
+ console.log('โœ… Provider rate limit test passed');
57
+ } else {
58
+ console.log('โŒ Provider rate limit test failed');
59
+ }
60
+ } catch (error) {
61
+ console.error('โŒ Provider rate limit test error:', error);
62
+ }
63
+
64
+ console.log('\n๐Ÿ Quota System Verification Finished.');
65
+ }
66
+
67
+ testQuotaSystem().catch(console.error);
@@ -0,0 +1,66 @@
1
+ const path = require('path');
2
+ const fs = require('fs-extra');
3
+ const os = require('os');
4
+ const { getProjectRequirementStats } = require('./src/utils/requirement-helpers');
5
+
6
+ async function testStats() {
7
+ const testRepo = path.join(os.tmpdir(), 'vbc-test-stats-repo');
8
+ const hostname = os.hostname();
9
+ const vibecodingmachineDir = path.join(testRepo, '.vibecodingmachine');
10
+ const reqPath = path.join(vibecodingmachineDir, `REQUIREMENTS-${hostname}.md`);
11
+ const changelogPath = path.join(testRepo, 'CHANGELOG.md');
12
+
13
+ try {
14
+ await fs.ensureDir(vibecodingmachineDir);
15
+
16
+ const reqContent = `# Requirements
17
+ ## โณ Requirements not yet completed
18
+ ### Req 1
19
+ Details 1
20
+ ### Req 2
21
+ Details 2
22
+
23
+ ## โœ… Verified by AI screenshot
24
+ ### Req 3
25
+ Details 3
26
+ `;
27
+ await fs.writeFile(reqPath, reqContent);
28
+
29
+ const changelogContent = `# Changelog
30
+ ## Verified Requirements
31
+ - Req 4 (2025-12-18)
32
+ - Req 5 (2025-12-18)
33
+ `;
34
+ await fs.writeFile(changelogPath, changelogContent);
35
+
36
+ console.log('๐Ÿงช Testing getProjectRequirementStats...');
37
+ const stats = await getProjectRequirementStats(testRepo);
38
+
39
+ console.log('Result:', JSON.stringify(stats, null, 2));
40
+
41
+ const expected = {
42
+ todoCount: 2,
43
+ toVerifyCount: 1,
44
+ verifiedCount: 2,
45
+ total: 5
46
+ };
47
+
48
+ if (stats.todoCount === expected.todoCount &&
49
+ stats.toVerifyCount === expected.toVerifyCount &&
50
+ stats.verifiedCount === expected.verifiedCount &&
51
+ stats.total === expected.total) {
52
+ console.log('โœ… PASS: Stats match expected values');
53
+ } else {
54
+ console.error('โŒ FAIL: Stats do not match expected values');
55
+ process.exit(1);
56
+ }
57
+
58
+ } catch (error) {
59
+ console.error('โŒ Error in test:', error);
60
+ process.exit(1);
61
+ } finally {
62
+ await fs.remove(testRepo);
63
+ }
64
+ }
65
+
66
+ testStats();