@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.
- package/README.md +166 -0
- package/dashboard/dist/assets/Tableau10-B-NsZVaP.js +1 -0
- package/dashboard/dist/assets/arc-CZ1oDkm-.js +1 -0
- package/dashboard/dist/assets/array-BKyUJesY.js +1 -0
- package/dashboard/dist/assets/blockDiagram-c4efeb88-BLITAZDl.js +118 -0
- package/dashboard/dist/assets/c4Diagram-c83219d4-DErNj891.js +10 -0
- package/dashboard/dist/assets/channel-sWBGOq9p.js +1 -0
- package/dashboard/dist/assets/classDiagram-beda092f-Bj8IB6RW.js +2 -0
- package/dashboard/dist/assets/classDiagram-v2-2358418a-BZtd2g5W.js +2 -0
- package/dashboard/dist/assets/clone-DtEyH3ya.js +1 -0
- package/dashboard/dist/assets/createText-1719965b-DkNrTk-5.js +7 -0
- package/dashboard/dist/assets/edges-96097737-CYxl0j3g.js +4 -0
- package/dashboard/dist/assets/erDiagram-0228fc6a-ClEp9Atg.js +51 -0
- package/dashboard/dist/assets/flowDb-c6c81e3f-ClDJCJwu.js +10 -0
- package/dashboard/dist/assets/flowDiagram-50d868cf-DYxwClWS.js +4 -0
- package/dashboard/dist/assets/flowDiagram-v2-4f6560a1-D7vEnO1T.js +1 -0
- package/dashboard/dist/assets/flowchart-elk-definition-6af322e1-C-wX1mNx.js +139 -0
- package/dashboard/dist/assets/ganttDiagram-a2739b55-DTeLavAj.js +257 -0
- package/dashboard/dist/assets/gitGraphDiagram-82fe8481-ClxNoYya.js +70 -0
- package/dashboard/dist/assets/graph-CT-F3Got.js +1 -0
- package/dashboard/dist/assets/index-5325376f-C-jTCYZA.js +1 -0
- package/dashboard/dist/assets/index-CvYvquQR.js +283 -0
- package/dashboard/dist/assets/index-n43poL1x.css +1 -0
- package/dashboard/dist/assets/infoDiagram-8eee0895-Zljudo5L.js +7 -0
- package/dashboard/dist/assets/init-Gi6I4Gst.js +1 -0
- package/dashboard/dist/assets/journeyDiagram-c64418c1-DXzTbuum.js +139 -0
- package/dashboard/dist/assets/katex-Cu_Erd72.js +261 -0
- package/dashboard/dist/assets/layout-CVO3EizT.js +1 -0
- package/dashboard/dist/assets/line-CIgln-0z.js +1 -0
- package/dashboard/dist/assets/linear-bmIUMQqg.js +1 -0
- package/dashboard/dist/assets/mindmap-definition-8da855dc-BDLTNZYk.js +425 -0
- package/dashboard/dist/assets/ordinal-Cboi1Yqb.js +1 -0
- package/dashboard/dist/assets/path-CbwjOpE9.js +1 -0
- package/dashboard/dist/assets/pieDiagram-a8764435-DtcOKNPc.js +35 -0
- package/dashboard/dist/assets/quadrantDiagram-1e28029f-zoSI_Ltf.js +7 -0
- package/dashboard/dist/assets/requirementDiagram-08caed73-TsQZ9lTB.js +52 -0
- package/dashboard/dist/assets/sankeyDiagram-a04cb91d-DE5ciDwD.js +8 -0
- package/dashboard/dist/assets/sequenceDiagram-c5b8d532-DhabPb2n.js +122 -0
- package/dashboard/dist/assets/stateDiagram-1ecb1508-Bg2q_YNx.js +1 -0
- package/dashboard/dist/assets/stateDiagram-v2-c2b004d7-Bs5iRjYB.js +1 -0
- package/dashboard/dist/assets/styles-b4e223ce-DchmAmav.js +160 -0
- package/dashboard/dist/assets/styles-ca3715f6-Bu5zjaDx.js +207 -0
- package/dashboard/dist/assets/styles-d45a18b0-jCaD8baR.js +116 -0
- package/dashboard/dist/assets/svgDrawCommon-b86b1483-BrYVGY4c.js +1 -0
- package/dashboard/dist/assets/timeline-definition-faaaa080--sq0bTHe.js +61 -0
- package/dashboard/dist/assets/xychartDiagram-f5964ef8-ByvzN0uj.js +7 -0
- package/dashboard/dist/index.html +22 -0
- package/dist/cli.d.ts +4 -0
- package/dist/cli.d.ts.map +1 -0
- package/dist/cli.js +641 -0
- package/dist/cli.js.map +1 -0
- package/dist/commands/dashboard.d.ts +2 -0
- package/dist/commands/dashboard.d.ts.map +1 -0
- package/dist/commands/dashboard.js +184 -0
- package/dist/commands/dashboard.js.map +1 -0
- package/dist/commands/init.d.ts +5 -0
- package/dist/commands/init.d.ts.map +1 -0
- package/dist/commands/init.js +501 -0
- package/dist/commands/init.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +602 -0
- package/dist/index.js.map +1 -0
- package/dist/librarian/autofix.d.ts +34 -0
- package/dist/librarian/autofix.d.ts.map +1 -0
- package/dist/librarian/autofix.js +369 -0
- package/dist/librarian/autofix.js.map +1 -0
- package/dist/librarian/heartbeat.d.ts +38 -0
- package/dist/librarian/heartbeat.d.ts.map +1 -0
- package/dist/librarian/heartbeat.js +98 -0
- package/dist/librarian/heartbeat.js.map +1 -0
- package/dist/librarian/initializer.d.ts +41 -0
- package/dist/librarian/initializer.d.ts.map +1 -0
- package/dist/librarian/initializer.js +202 -0
- package/dist/librarian/initializer.js.map +1 -0
- package/dist/librarian/memory-manager.d.ts +37 -0
- package/dist/librarian/memory-manager.d.ts.map +1 -0
- package/dist/librarian/memory-manager.js +205 -0
- package/dist/librarian/memory-manager.js.map +1 -0
- package/dist/librarian/oracle.d.ts +51 -0
- package/dist/librarian/oracle.d.ts.map +1 -0
- package/dist/librarian/oracle.js +235 -0
- package/dist/librarian/oracle.js.map +1 -0
- package/dist/librarian/summarizer.d.ts +19 -0
- package/dist/librarian/summarizer.d.ts.map +1 -0
- package/dist/librarian/summarizer.js +175 -0
- package/dist/librarian/summarizer.js.map +1 -0
- package/dist/librarian/watcher.d.ts +51 -0
- package/dist/librarian/watcher.d.ts.map +1 -0
- package/dist/librarian/watcher.js +189 -0
- package/dist/librarian/watcher.js.map +1 -0
- package/dist/mcp-server.d.ts +10 -0
- package/dist/mcp-server.d.ts.map +1 -0
- package/dist/mcp-server.js +879 -0
- package/dist/mcp-server.js.map +1 -0
- package/dist/services/token-tracker.d.ts +58 -0
- package/dist/services/token-tracker.d.ts.map +1 -0
- package/dist/services/token-tracker.js +178 -0
- package/dist/services/token-tracker.js.map +1 -0
- package/dist/utils/chunker.d.ts +5 -0
- package/dist/utils/chunker.d.ts.map +1 -0
- package/dist/utils/chunker.js +121 -0
- package/dist/utils/chunker.js.map +1 -0
- package/dist/utils/config-manager.d.ts +65 -0
- package/dist/utils/config-manager.d.ts.map +1 -0
- package/dist/utils/config-manager.js +243 -0
- package/dist/utils/config-manager.js.map +1 -0
- package/dist/utils/config.d.ts +46 -0
- package/dist/utils/config.d.ts.map +1 -0
- package/dist/utils/config.js +171 -0
- package/dist/utils/config.js.map +1 -0
- package/dist/utils/diff-cleaner.d.ts +13 -0
- package/dist/utils/diff-cleaner.d.ts.map +1 -0
- package/dist/utils/diff-cleaner.js +80 -0
- package/dist/utils/diff-cleaner.js.map +1 -0
- package/dist/utils/git.d.ts +71 -0
- package/dist/utils/git.d.ts.map +1 -0
- package/dist/utils/git.js +285 -0
- package/dist/utils/git.js.map +1 -0
- package/dist/utils/llm.d.ts +30 -0
- package/dist/utils/llm.d.ts.map +1 -0
- package/dist/utils/llm.js +308 -0
- package/dist/utils/llm.js.map +1 -0
- package/dist/utils/pricing.d.ts +47 -0
- package/dist/utils/pricing.d.ts.map +1 -0
- package/dist/utils/pricing.js +171 -0
- package/dist/utils/pricing.js.map +1 -0
- 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
|