@getvibeguard/cli 1.0.0

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 (128) hide show
  1. package/README.md +166 -0
  2. package/dashboard/dist/assets/Tableau10-B-NsZVaP.js +1 -0
  3. package/dashboard/dist/assets/arc-CZ1oDkm-.js +1 -0
  4. package/dashboard/dist/assets/array-BKyUJesY.js +1 -0
  5. package/dashboard/dist/assets/blockDiagram-c4efeb88-BLITAZDl.js +118 -0
  6. package/dashboard/dist/assets/c4Diagram-c83219d4-DErNj891.js +10 -0
  7. package/dashboard/dist/assets/channel-sWBGOq9p.js +1 -0
  8. package/dashboard/dist/assets/classDiagram-beda092f-Bj8IB6RW.js +2 -0
  9. package/dashboard/dist/assets/classDiagram-v2-2358418a-BZtd2g5W.js +2 -0
  10. package/dashboard/dist/assets/clone-DtEyH3ya.js +1 -0
  11. package/dashboard/dist/assets/createText-1719965b-DkNrTk-5.js +7 -0
  12. package/dashboard/dist/assets/edges-96097737-CYxl0j3g.js +4 -0
  13. package/dashboard/dist/assets/erDiagram-0228fc6a-ClEp9Atg.js +51 -0
  14. package/dashboard/dist/assets/flowDb-c6c81e3f-ClDJCJwu.js +10 -0
  15. package/dashboard/dist/assets/flowDiagram-50d868cf-DYxwClWS.js +4 -0
  16. package/dashboard/dist/assets/flowDiagram-v2-4f6560a1-D7vEnO1T.js +1 -0
  17. package/dashboard/dist/assets/flowchart-elk-definition-6af322e1-C-wX1mNx.js +139 -0
  18. package/dashboard/dist/assets/ganttDiagram-a2739b55-DTeLavAj.js +257 -0
  19. package/dashboard/dist/assets/gitGraphDiagram-82fe8481-ClxNoYya.js +70 -0
  20. package/dashboard/dist/assets/graph-CT-F3Got.js +1 -0
  21. package/dashboard/dist/assets/index-5325376f-C-jTCYZA.js +1 -0
  22. package/dashboard/dist/assets/index-CvYvquQR.js +283 -0
  23. package/dashboard/dist/assets/index-n43poL1x.css +1 -0
  24. package/dashboard/dist/assets/infoDiagram-8eee0895-Zljudo5L.js +7 -0
  25. package/dashboard/dist/assets/init-Gi6I4Gst.js +1 -0
  26. package/dashboard/dist/assets/journeyDiagram-c64418c1-DXzTbuum.js +139 -0
  27. package/dashboard/dist/assets/katex-Cu_Erd72.js +261 -0
  28. package/dashboard/dist/assets/layout-CVO3EizT.js +1 -0
  29. package/dashboard/dist/assets/line-CIgln-0z.js +1 -0
  30. package/dashboard/dist/assets/linear-bmIUMQqg.js +1 -0
  31. package/dashboard/dist/assets/mindmap-definition-8da855dc-BDLTNZYk.js +425 -0
  32. package/dashboard/dist/assets/ordinal-Cboi1Yqb.js +1 -0
  33. package/dashboard/dist/assets/path-CbwjOpE9.js +1 -0
  34. package/dashboard/dist/assets/pieDiagram-a8764435-DtcOKNPc.js +35 -0
  35. package/dashboard/dist/assets/quadrantDiagram-1e28029f-zoSI_Ltf.js +7 -0
  36. package/dashboard/dist/assets/requirementDiagram-08caed73-TsQZ9lTB.js +52 -0
  37. package/dashboard/dist/assets/sankeyDiagram-a04cb91d-DE5ciDwD.js +8 -0
  38. package/dashboard/dist/assets/sequenceDiagram-c5b8d532-DhabPb2n.js +122 -0
  39. package/dashboard/dist/assets/stateDiagram-1ecb1508-Bg2q_YNx.js +1 -0
  40. package/dashboard/dist/assets/stateDiagram-v2-c2b004d7-Bs5iRjYB.js +1 -0
  41. package/dashboard/dist/assets/styles-b4e223ce-DchmAmav.js +160 -0
  42. package/dashboard/dist/assets/styles-ca3715f6-Bu5zjaDx.js +207 -0
  43. package/dashboard/dist/assets/styles-d45a18b0-jCaD8baR.js +116 -0
  44. package/dashboard/dist/assets/svgDrawCommon-b86b1483-BrYVGY4c.js +1 -0
  45. package/dashboard/dist/assets/timeline-definition-faaaa080--sq0bTHe.js +61 -0
  46. package/dashboard/dist/assets/xychartDiagram-f5964ef8-ByvzN0uj.js +7 -0
  47. package/dashboard/dist/index.html +22 -0
  48. package/dist/cli.d.ts +4 -0
  49. package/dist/cli.d.ts.map +1 -0
  50. package/dist/cli.js +641 -0
  51. package/dist/cli.js.map +1 -0
  52. package/dist/commands/dashboard.d.ts +2 -0
  53. package/dist/commands/dashboard.d.ts.map +1 -0
  54. package/dist/commands/dashboard.js +184 -0
  55. package/dist/commands/dashboard.js.map +1 -0
  56. package/dist/commands/init.d.ts +5 -0
  57. package/dist/commands/init.d.ts.map +1 -0
  58. package/dist/commands/init.js +501 -0
  59. package/dist/commands/init.js.map +1 -0
  60. package/dist/index.d.ts +3 -0
  61. package/dist/index.d.ts.map +1 -0
  62. package/dist/index.js +602 -0
  63. package/dist/index.js.map +1 -0
  64. package/dist/librarian/autofix.d.ts +34 -0
  65. package/dist/librarian/autofix.d.ts.map +1 -0
  66. package/dist/librarian/autofix.js +369 -0
  67. package/dist/librarian/autofix.js.map +1 -0
  68. package/dist/librarian/heartbeat.d.ts +38 -0
  69. package/dist/librarian/heartbeat.d.ts.map +1 -0
  70. package/dist/librarian/heartbeat.js +98 -0
  71. package/dist/librarian/heartbeat.js.map +1 -0
  72. package/dist/librarian/initializer.d.ts +41 -0
  73. package/dist/librarian/initializer.d.ts.map +1 -0
  74. package/dist/librarian/initializer.js +202 -0
  75. package/dist/librarian/initializer.js.map +1 -0
  76. package/dist/librarian/memory-manager.d.ts +37 -0
  77. package/dist/librarian/memory-manager.d.ts.map +1 -0
  78. package/dist/librarian/memory-manager.js +205 -0
  79. package/dist/librarian/memory-manager.js.map +1 -0
  80. package/dist/librarian/oracle.d.ts +51 -0
  81. package/dist/librarian/oracle.d.ts.map +1 -0
  82. package/dist/librarian/oracle.js +235 -0
  83. package/dist/librarian/oracle.js.map +1 -0
  84. package/dist/librarian/summarizer.d.ts +19 -0
  85. package/dist/librarian/summarizer.d.ts.map +1 -0
  86. package/dist/librarian/summarizer.js +175 -0
  87. package/dist/librarian/summarizer.js.map +1 -0
  88. package/dist/librarian/watcher.d.ts +51 -0
  89. package/dist/librarian/watcher.d.ts.map +1 -0
  90. package/dist/librarian/watcher.js +189 -0
  91. package/dist/librarian/watcher.js.map +1 -0
  92. package/dist/mcp-server.d.ts +10 -0
  93. package/dist/mcp-server.d.ts.map +1 -0
  94. package/dist/mcp-server.js +879 -0
  95. package/dist/mcp-server.js.map +1 -0
  96. package/dist/services/token-tracker.d.ts +58 -0
  97. package/dist/services/token-tracker.d.ts.map +1 -0
  98. package/dist/services/token-tracker.js +178 -0
  99. package/dist/services/token-tracker.js.map +1 -0
  100. package/dist/utils/chunker.d.ts +5 -0
  101. package/dist/utils/chunker.d.ts.map +1 -0
  102. package/dist/utils/chunker.js +121 -0
  103. package/dist/utils/chunker.js.map +1 -0
  104. package/dist/utils/config-manager.d.ts +65 -0
  105. package/dist/utils/config-manager.d.ts.map +1 -0
  106. package/dist/utils/config-manager.js +243 -0
  107. package/dist/utils/config-manager.js.map +1 -0
  108. package/dist/utils/config.d.ts +46 -0
  109. package/dist/utils/config.d.ts.map +1 -0
  110. package/dist/utils/config.js +171 -0
  111. package/dist/utils/config.js.map +1 -0
  112. package/dist/utils/diff-cleaner.d.ts +13 -0
  113. package/dist/utils/diff-cleaner.d.ts.map +1 -0
  114. package/dist/utils/diff-cleaner.js +80 -0
  115. package/dist/utils/diff-cleaner.js.map +1 -0
  116. package/dist/utils/git.d.ts +71 -0
  117. package/dist/utils/git.d.ts.map +1 -0
  118. package/dist/utils/git.js +285 -0
  119. package/dist/utils/git.js.map +1 -0
  120. package/dist/utils/llm.d.ts +30 -0
  121. package/dist/utils/llm.d.ts.map +1 -0
  122. package/dist/utils/llm.js +308 -0
  123. package/dist/utils/llm.js.map +1 -0
  124. package/dist/utils/pricing.d.ts +47 -0
  125. package/dist/utils/pricing.d.ts.map +1 -0
  126. package/dist/utils/pricing.js +171 -0
  127. package/dist/utils/pricing.js.map +1 -0
  128. package/package.json +45 -0
package/dist/cli.js ADDED
@@ -0,0 +1,641 @@
1
+ #!/usr/bin/env node
2
+ import { fileURLToPath } from 'url';
3
+ import { dirname, join } from 'path';
4
+ import { Watcher } from './librarian/watcher.js';
5
+ import { Summarizer } from './librarian/summarizer.js';
6
+ import { MemoryManager } from './librarian/memory-manager.js';
7
+ import { OracleService } from './librarian/oracle.js';
8
+ import { Heartbeat } from './librarian/heartbeat.js';
9
+ import { GitUtils } from './utils/git.js';
10
+ import { getApiKey, getModel } from './utils/config.js';
11
+ import { generateSummary } from './utils/llm.js';
12
+ import { handleDashboard } from './commands/dashboard.js';
13
+ import { handleInit } from './commands/init.js';
14
+ import * as fs from 'fs/promises';
15
+ import * as path from 'path';
16
+ import { simpleGit } from 'simple-git';
17
+ // Robust ESM path logic
18
+ const __filename = fileURLToPath(import.meta.url);
19
+ const __dirname = dirname(__filename);
20
+ // This resolves to the dashboard/dist folder at the package root
21
+ // when the CLI is running from the dist/ folder.
22
+ export const DASHBOARD_PATH = join(__dirname, '../dashboard/dist');
23
+ // Helper to resolve paths relative to the package root
24
+ // This works whether installed via npm or run via npx
25
+ export async function resolvePackagePath(...segments) {
26
+ // When installed, node_modules/@vibeguard/cli/dist/cli.js
27
+ // When run via npx, the package is in a temp directory
28
+ // We need to find the package root (where templates/ would be)
29
+ // Try to find the package root by looking for package.json
30
+ let currentDir = __dirname;
31
+ const maxDepth = 10;
32
+ let depth = 0;
33
+ while (depth < maxDepth) {
34
+ try {
35
+ const packageJsonPath = join(currentDir, 'package.json');
36
+ await fs.access(packageJsonPath);
37
+ // Found package.json, this is likely the package root
38
+ return join(currentDir, ...segments);
39
+ }
40
+ catch {
41
+ // Not found, go up one level
42
+ const parent = path.dirname(currentDir);
43
+ if (parent === currentDir) {
44
+ // Reached filesystem root
45
+ break;
46
+ }
47
+ currentDir = parent;
48
+ depth++;
49
+ }
50
+ }
51
+ // Fallback: use __dirname (dist/) and go up to package root
52
+ // dist/ -> package root
53
+ return join(path.dirname(__dirname), ...segments);
54
+ }
55
+ const COMMANDS = {
56
+ INIT: 'init',
57
+ WATCH: 'watch',
58
+ SYNC: 'sync',
59
+ CHECK: 'check',
60
+ VISUALIZE: 'visualize',
61
+ DASHBOARD: 'dashboard',
62
+ };
63
+ async function setupServices() {
64
+ // Validate API key is available (backward compatibility)
65
+ // New init command handles this via ConfigManager
66
+ try {
67
+ await getApiKey();
68
+ }
69
+ catch (error) {
70
+ // Don't exit if using new config system - let init command handle it
71
+ console.warn('Warning: API key not found. Run `vibeguard init` to configure.');
72
+ }
73
+ }
74
+ async function handleWatch() {
75
+ console.log('Starting VibeGuard Librarian watcher...\n');
76
+ await setupServices();
77
+ const repoPath = process.cwd();
78
+ const watcher = new Watcher(repoPath);
79
+ const summarizer = new Summarizer();
80
+ const memoryManager = new MemoryManager(repoPath);
81
+ const gitUtils = new GitUtils(repoPath);
82
+ // Initialize Oracle and Heartbeat
83
+ const oracle = new OracleService(repoPath);
84
+ const heartbeat = new Heartbeat(oracle);
85
+ heartbeat.start(); // Start quiet reflection monitoring
86
+ // Set up commit processing callback
87
+ watcher.onCommitDetected(async () => {
88
+ // Record activity for quiet reflection
89
+ heartbeat.recordActivity();
90
+ try {
91
+ console.log('New commit detected, processing...');
92
+ const lastProcessed = await watcher.getLastProcessedCommit();
93
+ const currentHead = await gitUtils.getHeadCommit();
94
+ if (lastProcessed === currentHead) {
95
+ // No new commit, check for unstaged changes as draft memory
96
+ const unstagedDiff = await gitUtils.getUnstagedDiff();
97
+ if (unstagedDiff && unstagedDiff.trim().length > 0) {
98
+ console.log('No new commit, but unstaged changes detected. Creating draft memory...');
99
+ // Read current memory
100
+ const currentMemory = await memoryManager.readMemory();
101
+ // Prefix with draft note
102
+ const draftDiff = `## Draft Changes (Unstaged)\n\n${unstagedDiff}`;
103
+ // Summarize and update (using flash for routine summarization)
104
+ const updatedMemory = await summarizer.summarizeDiff(draftDiff, currentMemory, 'flash');
105
+ // Write updated memory
106
+ await memoryManager.writeMemory(updatedMemory);
107
+ console.log('✅ Draft memory updated successfully');
108
+ }
109
+ return; // Already processed or no changes
110
+ }
111
+ // Get diff for ALL commits between lastProcessed and currentHead
112
+ // This handles cases where multiple commits happened quickly
113
+ let diff;
114
+ if (lastProcessed) {
115
+ // Get diff from lastProcessed to currentHead (covers all commits in between)
116
+ try {
117
+ diff = await gitUtils.getCommitRangeDiff(lastProcessed, currentHead);
118
+ }
119
+ catch (error) {
120
+ // Fallback: if range diff fails, try latest commit only
121
+ console.warn('Could not get commit range diff, falling back to latest commit:', error.message);
122
+ diff = await gitUtils.getLatestCommitDiff();
123
+ }
124
+ }
125
+ else {
126
+ // No previous commit, use latest commit diff
127
+ diff = await gitUtils.getLatestCommitDiff();
128
+ }
129
+ if (!diff || diff.trim().length === 0) {
130
+ console.log('No diff found. Skipping...');
131
+ await watcher.setLastProcessedCommit(currentHead);
132
+ return;
133
+ }
134
+ // Read current memory
135
+ const currentMemory = await memoryManager.readMemory();
136
+ // Summarize and update (using flash for routine summarization)
137
+ const updatedMemory = await summarizer.summarizeDiff(diff, currentMemory, 'flash');
138
+ // Write updated memory
139
+ await memoryManager.writeMemory(updatedMemory);
140
+ // Update last processed commit
141
+ await watcher.setLastProcessedCommit(currentHead);
142
+ console.log('✅ Memory updated successfully');
143
+ }
144
+ catch (error) {
145
+ console.error('Error processing commit:', error);
146
+ }
147
+ });
148
+ // Initialize watcher state with current HEAD if not set
149
+ const currentHead = await gitUtils.getHeadCommit();
150
+ const lastProcessed = await watcher.getLastProcessedCommit();
151
+ if (!lastProcessed) {
152
+ await watcher.setLastProcessedCommit(currentHead);
153
+ }
154
+ // Start watching
155
+ await watcher.startWatching();
156
+ // Handle graceful shutdown
157
+ process.on('SIGINT', async () => {
158
+ console.log('\nShutting down watcher...');
159
+ heartbeat.stop();
160
+ await watcher.stopWatching();
161
+ process.exit(0);
162
+ });
163
+ process.on('SIGTERM', async () => {
164
+ console.log('\nShutting down watcher...');
165
+ heartbeat.stop();
166
+ await watcher.stopWatching();
167
+ process.exit(0);
168
+ });
169
+ // Keep process alive
170
+ console.log('Watcher is running. Press Ctrl+C to stop.\n');
171
+ }
172
+ async function handleCheck() {
173
+ console.log('VibeGuard Health Check\n');
174
+ const checks = [];
175
+ // Check 1: .env file exists and contains API key
176
+ try {
177
+ const envPath = path.join(process.cwd(), '.env');
178
+ try {
179
+ const envContent = await fs.readFile(envPath, 'utf-8');
180
+ // Check for GEMINI_API_KEY with more robust parsing
181
+ const lines = envContent.split('\n');
182
+ const apiKeyLine = lines.find(line => line.trim().startsWith('GEMINI_API_KEY=') &&
183
+ !line.trim().startsWith('#'));
184
+ if (apiKeyLine) {
185
+ // Extract value (handle quoted and unquoted values)
186
+ const value = apiKeyLine.split('=')[1]?.trim().replace(/^["']|["']$/g, '');
187
+ if (value && value.length > 0) {
188
+ checks.push({ name: 'Environment (.env)', status: true, message: '.env file exists with GEMINI_API_KEY' });
189
+ }
190
+ else {
191
+ checks.push({ name: 'Environment (.env)', status: false, message: '.env file exists but GEMINI_API_KEY is empty' });
192
+ }
193
+ }
194
+ else {
195
+ checks.push({ name: 'Environment (.env)', status: false, message: '.env file exists but GEMINI_API_KEY is missing' });
196
+ }
197
+ }
198
+ catch (error) {
199
+ checks.push({ name: 'Environment (.env)', status: false, message: '.env file not found in project root' });
200
+ }
201
+ }
202
+ catch (error) {
203
+ checks.push({ name: 'Environment (.env)', status: false, message: 'Error checking .env file' });
204
+ }
205
+ // Check 2: API key is valid and active (test with minimal Gemini call)
206
+ try {
207
+ const apiKey = await getApiKey();
208
+ // Basic validation: API key should be non-empty and look valid
209
+ if (!apiKey || apiKey.trim().length === 0) {
210
+ checks.push({ name: 'Librarian Health (API Key)', status: false, message: 'API key is empty' });
211
+ }
212
+ else {
213
+ // Test with minimal call - use a neutral prompt unlikely to trigger safety filters
214
+ try {
215
+ const model = await getModel();
216
+ await generateSummary('Say hello', 'You are a helpful assistant. Respond briefly.', { maxTokens: 1000, feature: 'Librarian' });
217
+ checks.push({ name: 'Librarian Health (API Key)', status: true, message: `API key is active (tested with ${model})` });
218
+ }
219
+ catch (error) {
220
+ const errorMsg = error.message || 'Unknown error';
221
+ if (errorMsg.includes('API_KEY') || errorMsg.includes('401') || errorMsg.includes('403')) {
222
+ checks.push({ name: 'Librarian Health (API Key)', status: false, message: `API key is invalid or unauthorized: ${errorMsg}` });
223
+ }
224
+ else {
225
+ checks.push({ name: 'Librarian Health (API Key)', status: false, message: `API test failed: ${errorMsg}` });
226
+ }
227
+ }
228
+ }
229
+ }
230
+ catch (error) {
231
+ checks.push({ name: 'Librarian Health (API Key)', status: false, message: `API key not found: ${error.message || 'Unknown error'}` });
232
+ }
233
+ // Check 3: Git repository
234
+ try {
235
+ const git = simpleGit(process.cwd());
236
+ const isRepo = await git.checkIsRepo();
237
+ if (isRepo) {
238
+ checks.push({ name: 'Git Status', status: true, message: 'Current directory is a Git repository' });
239
+ }
240
+ else {
241
+ checks.push({ name: 'Git Status', status: false, message: 'Current directory is not a Git repository' });
242
+ }
243
+ }
244
+ catch (error) {
245
+ checks.push({ name: 'Git Status', status: false, message: 'Error checking Git repository status' });
246
+ }
247
+ // Check 4: PROJECT_MEMORY.md exists
248
+ try {
249
+ const memoryPath = path.join(process.cwd(), 'PROJECT_MEMORY.md');
250
+ try {
251
+ await fs.access(memoryPath);
252
+ checks.push({ name: 'PROJECT_MEMORY.md', status: true, message: 'PROJECT_MEMORY.md exists' });
253
+ }
254
+ catch (error) {
255
+ checks.push({ name: 'PROJECT_MEMORY.md', status: false, message: 'PROJECT_MEMORY.md not found in project root' });
256
+ }
257
+ }
258
+ catch (error) {
259
+ checks.push({ name: 'PROJECT_MEMORY.md', status: false, message: 'Error checking PROJECT_MEMORY.md' });
260
+ }
261
+ // Print status report
262
+ console.log('Status Report:');
263
+ console.log('─'.repeat(60));
264
+ for (const check of checks) {
265
+ const icon = check.status ? '✓' : '✗';
266
+ const colorCode = check.status ? '\x1b[32m' : '\x1b[31m'; // Green for success, red for failure
267
+ const resetCode = '\x1b[0m';
268
+ console.log(`${colorCode}${icon}${resetCode} ${check.name.padEnd(30)} ${check.message}`);
269
+ }
270
+ console.log('─'.repeat(60));
271
+ const allPassed = checks.every(c => c.status);
272
+ if (allPassed) {
273
+ console.log('\n✅ All checks passed! VibeGuard is ready to use.');
274
+ }
275
+ else {
276
+ console.log('\n⚠️ Some checks failed. Please review the issues above.');
277
+ process.exit(1);
278
+ }
279
+ }
280
+ /**
281
+ * Scan src/ directory and return file structure
282
+ */
283
+ async function scanSrcDirectory() {
284
+ const srcPath = path.join(process.cwd(), 'src');
285
+ const files = [];
286
+ async function walkDir(dir, prefix = '', depth = 0) {
287
+ if (depth > 10)
288
+ return; // Limit depth
289
+ try {
290
+ const entries = await fs.readdir(dir, { withFileTypes: true });
291
+ // Filter out common ignore patterns
292
+ const filtered = entries.filter(entry => {
293
+ const name = entry.name;
294
+ return !name.startsWith('.') &&
295
+ name !== 'node_modules' &&
296
+ name !== 'dist' &&
297
+ name !== 'build';
298
+ });
299
+ for (let i = 0; i < filtered.length; i++) {
300
+ const entry = filtered[i];
301
+ const isLast = i === filtered.length - 1;
302
+ const currentPrefix = isLast ? '└── ' : '├── ';
303
+ const nextPrefix = isLast ? ' ' : '│ ';
304
+ files.push(prefix + currentPrefix + entry.name);
305
+ if (entry.isDirectory()) {
306
+ const fullPath = path.join(dir, entry.name);
307
+ await walkDir(fullPath, prefix + nextPrefix, depth + 1);
308
+ }
309
+ }
310
+ }
311
+ catch (error) {
312
+ // Ignore permission errors
313
+ }
314
+ }
315
+ try {
316
+ await fs.access(srcPath);
317
+ await walkDir(srcPath);
318
+ }
319
+ catch (error) {
320
+ throw new Error('src/ directory not found. Make sure you are in the project root.');
321
+ }
322
+ return files.join('\n');
323
+ }
324
+ /**
325
+ * Validate Mermaid syntax - check for unclosed brackets
326
+ */
327
+ function validateMermaidSyntax(mermaidCode) {
328
+ const errors = [];
329
+ // Extract the mermaid code block content
330
+ const mermaidMatch = mermaidCode.match(/```mermaid\n([\s\S]*?)```/);
331
+ if (!mermaidMatch) {
332
+ errors.push('No mermaid code block found');
333
+ return { valid: false, errors };
334
+ }
335
+ const code = mermaidMatch[1];
336
+ // Check for balanced brackets
337
+ const bracketPairs = [
338
+ { open: '[', close: ']', name: 'square brackets' },
339
+ { open: '(', close: ')', name: 'parentheses' },
340
+ { open: '{', close: '}', name: 'curly braces' },
341
+ ];
342
+ for (const pair of bracketPairs) {
343
+ let depth = 0;
344
+ for (let i = 0; i < code.length; i++) {
345
+ if (code[i] === pair.open) {
346
+ depth++;
347
+ }
348
+ else if (code[i] === pair.close) {
349
+ depth--;
350
+ if (depth < 0) {
351
+ errors.push(`Unmatched closing ${pair.name} at position ${i}`);
352
+ return { valid: false, errors };
353
+ }
354
+ }
355
+ }
356
+ if (depth > 0) {
357
+ errors.push(`Unclosed ${pair.name}: ${depth} unclosed bracket(s)`);
358
+ }
359
+ }
360
+ // Check for balanced subgraph blocks
361
+ const subgraphOpen = (code.match(/subgraph\s+[^\s]+\s*\[/g) || []).length;
362
+ const subgraphClose = (code.match(/end\s*$/gm) || []).length;
363
+ if (subgraphOpen !== subgraphClose) {
364
+ errors.push(`Unbalanced subgraphs: ${subgraphOpen} opened, ${subgraphClose} closed`);
365
+ }
366
+ return { valid: errors.length === 0, errors };
367
+ }
368
+ async function handleVisualize() {
369
+ console.log('Generating architecture diagram...\n');
370
+ await setupServices();
371
+ const repoPath = process.cwd();
372
+ const srcPath = path.join(repoPath, 'src');
373
+ try {
374
+ // Check if src/ exists
375
+ await fs.access(srcPath);
376
+ }
377
+ catch (error) {
378
+ console.error('Error: src/ directory not found. Make sure you are in the project root.');
379
+ process.exit(1);
380
+ }
381
+ // Scan src/ directory
382
+ console.log('Scanning src/ directory...');
383
+ const fileStructure = await scanSrcDirectory();
384
+ // Read PROJECT_MEMORY.md for context
385
+ const memoryManager = new MemoryManager(repoPath);
386
+ let projectMemory = '';
387
+ try {
388
+ projectMemory = await memoryManager.readMemory();
389
+ }
390
+ catch (error) {
391
+ console.warn('Warning: Could not read PROJECT_MEMORY.md. Continuing without context...');
392
+ }
393
+ // Build prompt for Gemini
394
+ const today = new Date().toLocaleDateString();
395
+ const prompt = `You are a Senior Software Architect & Mermaid.js Specialist. Your task is to generate a valid Mermaid.js flowchart (TD - Top Down) that visually represents the project's architecture.
396
+
397
+ # PROJECT STRUCTURE
398
+ \`\`\`
399
+ ${fileStructure}
400
+ \`\`\`
401
+
402
+ ${projectMemory ? `# PROJECT CONTEXT\n\`\`\`\n${projectMemory}\n\`\`\`\n\n` : ''}# REQUIREMENTS
403
+ 1. Use Mermaid.js Flowchart syntax (flowchart TD)
404
+ 2. Structure:
405
+ - Use subgraphs to group "Internal Logic" vs "External Services"
406
+ - Nodes should represent key components (CLI, MCP Server, Watcher, Gemini API, File System, etc.)
407
+ - Edges should have descriptive text (e.g., "Sends Diffs", "Returns Summary", "Updates Memory")
408
+ 3. Color Styling (REQUIRED):
409
+ - Add a classDef section at the end of the Mermaid code (before the closing code block)
410
+ - Define three classes with the following colors:
411
+ * ExternalServices: fill:#e1f5fe (Light Blue)
412
+ * InternalLogic: fill:#e8f5e9 (Light Green)
413
+ * PersistenceFiles: fill:#fff8e1 (Soft Amber)
414
+ - Apply classes to subgraphs or specific nodes:
415
+ * Apply "ExternalServices" class to the "External Services" subgraph
416
+ * Apply "InternalLogic" class to the "Internal Logic" subgraph
417
+ * Apply "PersistenceFiles" class to nodes representing File System, Git Repository, or any persistence layer
418
+ - Example classDef syntax:
419
+ \`\`\`
420
+ classDef ExternalServices fill:#e1f5fe,stroke:#01579b,stroke-width:2px
421
+ classDef InternalLogic fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px
422
+ classDef PersistenceFiles fill:#fff8e1,stroke:#f57f17,stroke-width:2px
423
+ \`\`\`
424
+ - Apply classes using: \`class subgraphId ExternalServices\` or \`class nodeId PersistenceFiles\`
425
+ 4. Syntax Rules:
426
+ - All node IDs must be alphanumeric (use [ ] for labels with spaces)
427
+ - Avoid special characters like & or > inside labels unless escaped
428
+ - Ensure all brackets are properly closed
429
+ - All subgraphs must have matching "end" statements
430
+ 5. Output Format:
431
+ - Wrap the output in a standard Markdown code block: \`\`\`mermaid
432
+ - Include a brief H1 header "# VibeGuard Architecture Diagram" before the code block
433
+ - Add a short "Legend" section after the diagram explaining symbols and color scheme
434
+
435
+ # OUTPUT
436
+ Return ONLY the complete Markdown content for DIAGRAM.md, including:
437
+ - H1 header
438
+ - Mermaid code block with valid syntax
439
+ - Legend section
440
+
441
+ Do not include any explanations outside the Markdown format.`;
442
+ const systemPrompt = `You are an expert at creating Mermaid.js diagrams for software architecture. You always generate valid, well-structured diagrams that follow Mermaid syntax rules strictly.`;
443
+ console.log('Generating diagram with Gemini...');
444
+ try {
445
+ const diagramContent = await generateSummary(prompt, systemPrompt, {
446
+ thinkingLevel: 'pro',
447
+ maxTokens: 10000,
448
+ feature: 'Librarian',
449
+ });
450
+ // Validate Mermaid syntax
451
+ console.log('Validating Mermaid syntax...');
452
+ const validation = validateMermaidSyntax(diagramContent);
453
+ if (!validation.valid) {
454
+ console.error('❌ Mermaid syntax validation failed:');
455
+ validation.errors.forEach(error => console.error(` - ${error}`));
456
+ console.error('\nAttempting to fix syntax issues...');
457
+ // Try to fix common issues and regenerate
458
+ const fixPrompt = `The previous Mermaid diagram had syntax errors: ${validation.errors.join(', ')}.
459
+
460
+ Please regenerate the diagram with these fixes:
461
+ ${diagramContent}
462
+
463
+ CRITICAL REQUIREMENTS:
464
+ 1. Ensure all brackets are closed and all subgraphs have matching "end" statements.
465
+ 2. MUST include color styling with classDef:
466
+ - classDef ExternalServices fill:#e1f5fe,stroke:#01579b,stroke-width:2px
467
+ - classDef InternalLogic fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px
468
+ - classDef PersistenceFiles fill:#fff8e1,stroke:#f57f17,stroke-width:2px
469
+ 3. Apply classes to subgraphs: "External Services" subgraph gets ExternalServices class, "Internal Logic" subgraph gets InternalLogic class.
470
+ 4. Apply PersistenceFiles class to File System, Git Repository, or any persistence-related nodes.`;
471
+ const fixedContent = await generateSummary(fixPrompt, systemPrompt, {
472
+ thinkingLevel: 'pro',
473
+ maxTokens: 10000,
474
+ feature: 'Librarian',
475
+ });
476
+ const fixedValidation = validateMermaidSyntax(fixedContent);
477
+ if (!fixedValidation.valid) {
478
+ console.error('❌ Could not fix syntax errors. Saving anyway, but diagram may not render correctly.');
479
+ console.error('Errors:', fixedValidation.errors);
480
+ }
481
+ else {
482
+ console.log('✅ Syntax fixed successfully!');
483
+ }
484
+ // Save the fixed version
485
+ const diagramPath = path.join(repoPath, 'DIAGRAM.md');
486
+ await fs.writeFile(diagramPath, fixedContent, 'utf-8');
487
+ console.log(`\n✅ Diagram saved to DIAGRAM.md`);
488
+ }
489
+ else {
490
+ console.log('✅ Mermaid syntax is valid!');
491
+ // Save the diagram
492
+ const diagramPath = path.join(repoPath, 'DIAGRAM.md');
493
+ await fs.writeFile(diagramPath, diagramContent, 'utf-8');
494
+ console.log(`\n✅ Diagram saved to DIAGRAM.md`);
495
+ }
496
+ }
497
+ catch (error) {
498
+ console.error('Error generating diagram:', error.message || error);
499
+ process.exit(1);
500
+ }
501
+ }
502
+ async function handleSync(deep = false) {
503
+ console.log(deep ? 'Running deep sync (full history)...' : 'Running sync (latest commits)...\n');
504
+ await setupServices();
505
+ const repoPath = process.cwd();
506
+ const summarizer = new Summarizer();
507
+ const memoryManager = new MemoryManager(repoPath);
508
+ const gitUtils = new GitUtils(repoPath);
509
+ const watcher = new Watcher(repoPath);
510
+ try {
511
+ if (deep) {
512
+ // Process full history
513
+ console.log('Processing full Git history (this may take a while)...');
514
+ // Get all commits (or a large number)
515
+ const commits = await gitUtils.getLatestCommits(1000); // Process up to 1000 commits
516
+ console.log(`Processing ${commits.length} commits...`);
517
+ // Process in batches
518
+ const batchSize = 10;
519
+ let currentMemory = await memoryManager.readMemory();
520
+ for (let i = 0; i < commits.length; i += batchSize) {
521
+ const batch = commits.slice(i, i + batchSize);
522
+ console.log(`Processing batch ${Math.floor(i / batchSize) + 1}/${Math.ceil(commits.length / batchSize)}...`);
523
+ const diffs = [];
524
+ for (const commit of batch) {
525
+ try {
526
+ const diff = await gitUtils.getCommitDiff(commit.hash);
527
+ diffs.push(`## Commit: ${commit.message}\n\n${diff}`);
528
+ }
529
+ catch (error) {
530
+ console.warn(`Skipping commit ${commit.hash}:`, error);
531
+ }
532
+ }
533
+ const combinedDiff = diffs.join('\n\n---\n\n');
534
+ currentMemory = await summarizer.summarizeDiff(combinedDiff, currentMemory, 'flash');
535
+ }
536
+ await memoryManager.writeMemory(currentMemory);
537
+ const currentHead = await gitUtils.getHeadCommit();
538
+ await watcher.setLastProcessedCommit(currentHead);
539
+ console.log('✅ Deep sync complete!');
540
+ }
541
+ else {
542
+ // Process latest commit(s)
543
+ const lastProcessed = await watcher.getLastProcessedCommit();
544
+ const currentHead = await gitUtils.getHeadCommit();
545
+ if (lastProcessed === currentHead) {
546
+ // No new commit, check for unstaged changes as draft memory
547
+ const unstagedDiff = await gitUtils.getUnstagedDiff();
548
+ if (unstagedDiff && unstagedDiff.trim().length > 0) {
549
+ console.log('No new commit, but unstaged changes detected. Creating draft memory...');
550
+ // Read current memory
551
+ const currentMemory = await memoryManager.readMemory();
552
+ // Prefix with draft note
553
+ const draftDiff = `## Draft Changes (Unstaged)\n\n${unstagedDiff}`;
554
+ // Summarize and update (using flash for routine summarization)
555
+ const updatedMemory = await summarizer.summarizeDiff(draftDiff, currentMemory, 'flash');
556
+ // Write updated memory
557
+ await memoryManager.writeMemory(updatedMemory);
558
+ console.log('✅ Draft memory sync complete!');
559
+ return;
560
+ }
561
+ console.log('No new commits to process.');
562
+ return;
563
+ }
564
+ console.log('Processing latest commit...');
565
+ // Get diff for the new commit using latest commit diff method
566
+ const diff = await gitUtils.getLatestCommitDiff();
567
+ if (!diff || diff.trim().length === 0) {
568
+ console.log('No diff found for latest commit. Updating state and skipping...');
569
+ await watcher.setLastProcessedCommit(currentHead);
570
+ return;
571
+ }
572
+ const currentMemory = await memoryManager.readMemory();
573
+ const updatedMemory = await summarizer.summarizeDiff(diff, currentMemory, 'flash');
574
+ await memoryManager.writeMemory(updatedMemory);
575
+ await watcher.setLastProcessedCommit(currentHead);
576
+ console.log('✅ Sync complete!');
577
+ }
578
+ }
579
+ catch (error) {
580
+ console.error('Error during sync:', error);
581
+ process.exit(1);
582
+ }
583
+ }
584
+ async function main() {
585
+ const args = process.argv.slice(2);
586
+ const command = args[0];
587
+ if (!command) {
588
+ console.log(`
589
+ VibeGuard Librarian - Context Management Tool
590
+
591
+ Usage:
592
+ vibeguard init Initialize and create PROJECT_MEMORY.md
593
+ vibeguard watch Start watching for Git changes
594
+ vibeguard sync Process latest commit(s)
595
+ vibeguard sync --deep Process full Git history
596
+ vibeguard check Run health check (env, API key, Git, memory)
597
+ vibeguard visualize Generate architecture diagram (DIAGRAM.md)
598
+ vibeguard dashboard Start the VibeGuard dashboard server
599
+
600
+ For more information, visit: https://github.com/cdaviddav/vibeguard
601
+ `);
602
+ process.exit(0);
603
+ }
604
+ try {
605
+ switch (command) {
606
+ case COMMANDS.INIT:
607
+ await handleInit(); // Uses new interactive wizard
608
+ break;
609
+ case COMMANDS.WATCH:
610
+ await handleWatch();
611
+ break;
612
+ case COMMANDS.SYNC:
613
+ const deep = args.includes('--deep');
614
+ await handleSync(deep);
615
+ break;
616
+ case COMMANDS.CHECK:
617
+ await handleCheck();
618
+ break;
619
+ case COMMANDS.VISUALIZE:
620
+ await handleVisualize();
621
+ break;
622
+ case COMMANDS.DASHBOARD:
623
+ await handleDashboard();
624
+ break;
625
+ default:
626
+ console.error(`Unknown command: ${command}`);
627
+ console.log('Run `vibeguard` without arguments to see usage.');
628
+ process.exit(1);
629
+ }
630
+ }
631
+ catch (error) {
632
+ console.error('Error:', error.message || error);
633
+ process.exit(1);
634
+ }
635
+ }
636
+ // Run main function (this is the entry point)
637
+ main().catch(error => {
638
+ console.error('Fatal error:', error);
639
+ process.exit(1);
640
+ });
641
+ //# sourceMappingURL=cli.js.map