vibe-me 3.0.0 → 3.1.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 +92 -19
- package/bin/vibes.js +554 -38
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -200,9 +200,12 @@ Download [`Vibe.bat`](Vibe.bat), drop it in any project folder, and double-click
|
|
|
200
200
|
| `vibes init` | Create `.vibe/` with the full suite — 15 files + guide |
|
|
201
201
|
| `vibes docs` | Create `docs/` operational documentation — 11 files |
|
|
202
202
|
| `vibes all` | Create both `.vibe/` and `docs/` at once |
|
|
203
|
-
| `vibes check` | Validate all files
|
|
203
|
+
| `vibes check` | Validate all files — catches unfilled templates, empty sections, missing structure |
|
|
204
204
|
| `vibes status` | Quick health dashboard with filled/N-A/empty counts |
|
|
205
205
|
| `vibes reset` | Delete and recreate everything |
|
|
206
|
+
| | |
|
|
207
|
+
| `vibes hub <path>` | Set up a central hub — git inits, creates `_insights/`, saves config |
|
|
208
|
+
| `vibes export` | Export `.vibe/` + `docs/` from current project to your hub |
|
|
206
209
|
| `vibes help` | Show all commands |
|
|
207
210
|
|
|
208
211
|
---
|
|
@@ -257,10 +260,27 @@ Every line in `.vibe/` should pass this test:
|
|
|
257
260
|
|
|
258
261
|
## 🔍 Quality Validation
|
|
259
262
|
|
|
263
|
+
`vibes check` doesn't just count lines — it catches real problems:
|
|
264
|
+
|
|
260
265
|
```bash
|
|
261
266
|
vibes check
|
|
262
267
|
```
|
|
263
268
|
|
|
269
|
+
**On a freshly scaffolded project (before AI fills it out):**
|
|
270
|
+
```
|
|
271
|
+
🔍 vibes check
|
|
272
|
+
|
|
273
|
+
── .vibe/ ──
|
|
274
|
+
|
|
275
|
+
✖ purpose.md — still contains template instructions (unfilled)
|
|
276
|
+
✖ architecture.md — still contains template instructions (unfilled)
|
|
277
|
+
✖ flows.md — still contains template instructions (unfilled)
|
|
278
|
+
...
|
|
279
|
+
|
|
280
|
+
Result: 24 failed, 1 warnings, 0 passed
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
**After your AI agent fills everything out:**
|
|
264
284
|
```
|
|
265
285
|
🔍 vibes check
|
|
266
286
|
|
|
@@ -270,7 +290,7 @@ vibes check
|
|
|
270
290
|
✔ architecture.md — 86 lines
|
|
271
291
|
✔ flows.md — 144 lines
|
|
272
292
|
✔ entities.md — 135 lines
|
|
273
|
-
|
|
293
|
+
⚠ decisions.md — 91 lines (no dependency graph fields)
|
|
274
294
|
✔ state.json — 82 lines
|
|
275
295
|
✔ context.md — 28 lines
|
|
276
296
|
✔ ai.md — 45 lines
|
|
@@ -280,25 +300,78 @@ vibes check
|
|
|
280
300
|
✔ experiments.md — 19 lines
|
|
281
301
|
⊘ business.md — N/A (not applicable)
|
|
282
302
|
⊘ market.md — N/A (not applicable)
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
303
|
+
✔ risks.md — 34 lines
|
|
304
|
+
|
|
305
|
+
Result: 2 N/A, 1 warnings, 22 passed
|
|
306
|
+
```
|
|
307
|
+
|
|
308
|
+
What it catches:
|
|
309
|
+
- **Unfilled templates** — files that still have `INSTRUCTIONS FOR AI AGENT` (hard fail)
|
|
310
|
+
- **Missing structure** — `purpose.md` without a "NOT do" section, `decisions.md` without dependency graph
|
|
311
|
+
- **Empty bullets/tables** — placeholder content that wasn't filled in
|
|
312
|
+
- **Stale data** — `state.json` not updated in 30+ days
|
|
313
|
+
- **Missing emotional reality** — `users.md` without frustrations, fears, or goals
|
|
314
|
+
- **N/A files** — recognized and not penalized
|
|
315
|
+
|
|
316
|
+
---
|
|
317
|
+
|
|
318
|
+
## 🗂️ Vibes Hub — Cross-Project Sync + Intelligence
|
|
319
|
+
|
|
320
|
+
When you're running vibes across multiple projects (and multiple computers), the **hub** lets you collect, compare, and analyze them all in one place.
|
|
321
|
+
|
|
322
|
+
### One-time setup
|
|
323
|
+
|
|
324
|
+
```bash
|
|
325
|
+
vibes hub ~/Documents/VibeHub
|
|
326
|
+
```
|
|
327
|
+
|
|
328
|
+
This creates a folder with git, a README, and an `_insights/` intelligence layer — then walks you through connecting it to a private GitHub repo.
|
|
329
|
+
|
|
330
|
+
### Export from any project
|
|
331
|
+
|
|
332
|
+
```bash
|
|
333
|
+
cd your-project
|
|
334
|
+
vibes export
|
|
335
|
+
```
|
|
336
|
+
|
|
337
|
+
Copies `.vibe/` and `docs/` into your hub, organized by project name. Auto-generates an `index.md` dashboard.
|
|
338
|
+
|
|
339
|
+
### Sync across machines
|
|
340
|
+
|
|
341
|
+
Push from one machine, pull on another. Same private repo, all your projects.
|
|
342
|
+
|
|
343
|
+
### Cross-project intelligence
|
|
344
|
+
|
|
345
|
+
After exporting 3+ projects, tell your AI agent:
|
|
346
|
+
|
|
347
|
+
> *"Read `_insights/ANALYZE.md` in my Vibes Hub and follow the instructions."*
|
|
348
|
+
|
|
349
|
+
The AI reads every project's `.vibe/` files and fills out:
|
|
350
|
+
|
|
351
|
+
| File | What it finds |
|
|
352
|
+
|:---|:---|
|
|
353
|
+
| `_insights/patterns.md` | Architecture, security, and tech stack patterns across projects |
|
|
354
|
+
| `_insights/standards.md` | Universal rules that should apply everywhere |
|
|
355
|
+
| `_insights/opportunities.md` | "Omni does X well → JustLegal should adopt it" |
|
|
356
|
+
|
|
357
|
+
```
|
|
358
|
+
VibeHub/
|
|
359
|
+
├── index.md Auto-generated dashboard
|
|
360
|
+
├── _insights/
|
|
361
|
+
│ ├── ANALYZE.md Prompt for AI analysis
|
|
362
|
+
│ ├── patterns.md Cross-project patterns
|
|
363
|
+
│ ├── standards.md Universal standards
|
|
364
|
+
│ └── opportunities.md Transferable improvements
|
|
365
|
+
├── Omni/
|
|
366
|
+
│ ├── .vibe/
|
|
367
|
+
│ └── docs/
|
|
368
|
+
├── JustLegal/
|
|
369
|
+
│ ├── .vibe/
|
|
370
|
+
│ └── docs/
|
|
371
|
+
└── ...
|
|
299
372
|
```
|
|
300
373
|
|
|
301
|
-
|
|
374
|
+
One project's best practice becomes every project's standard.
|
|
302
375
|
|
|
303
376
|
---
|
|
304
377
|
|
package/bin/vibes.js
CHANGED
|
@@ -15,6 +15,7 @@ const VIBE_DIR = '.vibe';
|
|
|
15
15
|
const DOCS_DIR = 'docs';
|
|
16
16
|
const TEMPLATES_DIR = path.join(__dirname, '..', 'templates');
|
|
17
17
|
const SPEC_DIR = path.join(__dirname, '..', 'spec');
|
|
18
|
+
const HUB_CONFIG = path.join(process.env.USERPROFILE || process.env.HOME || '', '.vibes-hub');
|
|
18
19
|
|
|
19
20
|
// All .vibe/ files — the full suite, always installed
|
|
20
21
|
const VIBE_FILES = [
|
|
@@ -208,6 +209,124 @@ function cmdCheck(targetDir) {
|
|
|
208
209
|
|
|
209
210
|
let passed = 0, failed = 0, warnings = 0, na = 0;
|
|
210
211
|
|
|
212
|
+
// ─── Universal quality checks ───
|
|
213
|
+
function checkQuality(content, file) {
|
|
214
|
+
const issues = [];
|
|
215
|
+
|
|
216
|
+
// FAIL: Still has template instruction comments
|
|
217
|
+
if (content.includes('INSTRUCTIONS FOR AI AGENT')) {
|
|
218
|
+
issues.push({ level: 'fail', msg: 'still contains template instructions (unfilled)' });
|
|
219
|
+
return issues;
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// FAIL: Too short to be real content
|
|
223
|
+
const lines = content.split('\n').length;
|
|
224
|
+
if (lines < 8) {
|
|
225
|
+
issues.push({ level: 'fail', msg: `only ${lines} lines — needs real content` });
|
|
226
|
+
return issues;
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
// WARN: Has unfilled placeholder brackets like [Component Name]
|
|
230
|
+
const placeholders = content.match(/\[(?:Component|Persona|Experiment|Decision|Error|Deliverable|Feature|Issue)\s*\w*\]/gi);
|
|
231
|
+
if (placeholders && placeholders.length > 1) {
|
|
232
|
+
issues.push({ level: 'warn', msg: `${placeholders.length} unfilled placeholders (${placeholders[0]}, ...)` });
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
// WARN: Empty bullet points (lines that are just "- " or "- \n")
|
|
236
|
+
const emptyBullets = (content.match(/^- ?\s*$/gm) || []).length;
|
|
237
|
+
if (emptyBullets > 2) {
|
|
238
|
+
issues.push({ level: 'warn', msg: `${emptyBullets} empty bullet points` });
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
// WARN: Empty table rows (| | | |)
|
|
242
|
+
const emptyRows = (content.match(/^\|\s*\|\s*\|\s*\|/gm) || []).length;
|
|
243
|
+
if (emptyRows > 1) {
|
|
244
|
+
issues.push({ level: 'warn', msg: `${emptyRows} empty table rows` });
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
return issues;
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
// ─── File-specific structure checks ───
|
|
251
|
+
function checkStructure(content, file) {
|
|
252
|
+
const issues = [];
|
|
253
|
+
|
|
254
|
+
switch (file) {
|
|
255
|
+
case 'purpose.md':
|
|
256
|
+
if (!content.toLowerCase().includes('not do') && !content.toLowerCase().includes('does not'))
|
|
257
|
+
issues.push({ level: 'warn', msg: 'missing "NOT do" / scope boundary section' });
|
|
258
|
+
if (!content.toLowerCase().includes('who'))
|
|
259
|
+
issues.push({ level: 'warn', msg: 'doesn\'t mention who uses it' });
|
|
260
|
+
break;
|
|
261
|
+
|
|
262
|
+
case 'architecture.md':
|
|
263
|
+
if (!content.includes('→') && !content.includes('->') && !content.includes('connects') && !content.includes('talks to'))
|
|
264
|
+
issues.push({ level: 'warn', msg: 'no data flow described (missing → or connection language)' });
|
|
265
|
+
break;
|
|
266
|
+
|
|
267
|
+
case 'flows.md':
|
|
268
|
+
const flowHeaders = (content.match(/^##\s+/gm) || []).length;
|
|
269
|
+
if (flowHeaders < 2) issues.push({ level: 'warn', msg: `only ${flowHeaders} flow(s) — most projects have 3+` });
|
|
270
|
+
break;
|
|
271
|
+
|
|
272
|
+
case 'entities.md':
|
|
273
|
+
if (!content.toLowerCase().includes('depends') && !content.toLowerCase().includes('relationship'))
|
|
274
|
+
issues.push({ level: 'warn', msg: 'no dependency/relationship info for entities' });
|
|
275
|
+
break;
|
|
276
|
+
|
|
277
|
+
case 'decisions.md':
|
|
278
|
+
const decisionHeaders = (content.match(/^##\s+/gm) || []).length;
|
|
279
|
+
if (decisionHeaders < 2) issues.push({ level: 'warn', msg: `only ${decisionHeaders} decision(s) — aim for 3+` });
|
|
280
|
+
if (!content.includes('Depends On') && !content.includes('Threatened By') && !content.includes('depends on'))
|
|
281
|
+
issues.push({ level: 'warn', msg: 'no dependency graph fields (Depends On, Threatened By)' });
|
|
282
|
+
break;
|
|
283
|
+
|
|
284
|
+
case 'state.json':
|
|
285
|
+
try {
|
|
286
|
+
const s = JSON.parse(content);
|
|
287
|
+
if (!s.vibe_updated) issues.push({ level: 'warn', msg: 'missing vibe_updated timestamp' });
|
|
288
|
+
else {
|
|
289
|
+
const days = (Date.now() - new Date(s.vibe_updated).getTime()) / 86400000;
|
|
290
|
+
if (days > 30) issues.push({ level: 'warn', msg: `last updated ${Math.floor(days)} days ago — stale?` });
|
|
291
|
+
}
|
|
292
|
+
if (!s.project) issues.push({ level: 'warn', msg: 'missing project name' });
|
|
293
|
+
} catch {
|
|
294
|
+
issues.push({ level: 'fail', msg: 'invalid JSON' });
|
|
295
|
+
}
|
|
296
|
+
break;
|
|
297
|
+
|
|
298
|
+
case 'context.md':
|
|
299
|
+
if (!content.includes('## Current Focus'))
|
|
300
|
+
issues.push({ level: 'warn', msg: 'missing "Current Focus" section' });
|
|
301
|
+
else {
|
|
302
|
+
const focusIdx = content.indexOf('## Current Focus');
|
|
303
|
+
const nextSection = content.indexOf('##', focusIdx + 16);
|
|
304
|
+
const focusContent = content.substring(focusIdx, nextSection > 0 ? nextSection : undefined);
|
|
305
|
+
const focusBullets = (focusContent.match(/^- .{3,}/gm) || []).length;
|
|
306
|
+
if (focusBullets === 0) issues.push({ level: 'warn', msg: 'Current Focus has no entries' });
|
|
307
|
+
}
|
|
308
|
+
break;
|
|
309
|
+
|
|
310
|
+
case 'ai.md':
|
|
311
|
+
if (!content.toLowerCase().includes('never') && !content.toLowerCase().includes('don\'t') && !content.toLowerCase().includes('do not'))
|
|
312
|
+
issues.push({ level: 'warn', msg: 'no constraints defined — what should agents NOT touch?' });
|
|
313
|
+
break;
|
|
314
|
+
|
|
315
|
+
case 'product.md':
|
|
316
|
+
if (!content.toLowerCase().includes('retention') && !content.toLowerCase().includes('why') && !content.toLowerCase().includes('value'))
|
|
317
|
+
issues.push({ level: 'warn', msg: 'missing value prop or retention drivers' });
|
|
318
|
+
break;
|
|
319
|
+
|
|
320
|
+
case 'users.md':
|
|
321
|
+
if (!content.toLowerCase().includes('frustrat') && !content.toLowerCase().includes('fear') && !content.toLowerCase().includes('pain') && !content.toLowerCase().includes('goal'))
|
|
322
|
+
issues.push({ level: 'warn', msg: 'missing emotional reality (frustrations, fears, goals)' });
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
return issues;
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// ─── Run checks on .vibe/ ───
|
|
211
330
|
if (hasVibe) {
|
|
212
331
|
console.log('');
|
|
213
332
|
console.log(dim(' ── .vibe/ ──'));
|
|
@@ -220,44 +339,32 @@ function cmdCheck(targetDir) {
|
|
|
220
339
|
const content = fs.readFileSync(fp, 'utf-8').trim();
|
|
221
340
|
const lines = content.split('\n').length;
|
|
222
341
|
|
|
223
|
-
//
|
|
224
|
-
if (content.match(/^N\/A/m)
|
|
342
|
+
// N/A check
|
|
343
|
+
if (content.match(/^N\/A/m)) {
|
|
225
344
|
console.log(dim(' ⊘ ') + file + dim(' — N/A (not applicable)'));
|
|
226
345
|
na++;
|
|
227
346
|
continue;
|
|
228
347
|
}
|
|
229
348
|
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
if (
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
if (
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
if (days > 30) warn = `stale (${Math.floor(days)} days old)`;
|
|
245
|
-
}
|
|
246
|
-
} catch { console.log(red(' ✖ ') + file + ' — invalid JSON'); failed++; continue; }
|
|
247
|
-
}
|
|
248
|
-
if (file === 'context.md') {
|
|
249
|
-
if (!content.includes('## Current Focus') || content.match(/^- $/m)) warn = 'Current Focus is empty';
|
|
250
|
-
}
|
|
251
|
-
if (file === 'decisions.md' && !content.includes('Depends On') && !content.includes('Threatened By')) {
|
|
252
|
-
// Only warn if decisions exist but lack graph relationships
|
|
253
|
-
if ((content.match(/^## Why /gm) || []).length >= 2) warn = 'decisions missing relationship fields (Depends On, Threatened By)';
|
|
349
|
+
// Run quality + structure checks
|
|
350
|
+
const allIssues = [...checkQuality(content, file), ...checkStructure(content, file)];
|
|
351
|
+
const fails = allIssues.filter(i => i.level === 'fail');
|
|
352
|
+
const warns = allIssues.filter(i => i.level === 'warn');
|
|
353
|
+
|
|
354
|
+
if (fails.length > 0) {
|
|
355
|
+
console.log(red(' ✖ ') + file + ' — ' + fails[0].msg);
|
|
356
|
+
failed++;
|
|
357
|
+
} else if (warns.length > 0) {
|
|
358
|
+
console.log(yellow(' ⚠ ') + file + ` — ${lines} lines` + yellow(` (${warns.map(w => w.msg).join('; ')})`));
|
|
359
|
+
warnings++;
|
|
360
|
+
} else {
|
|
361
|
+
console.log(green(' ✔ ') + file + ` — ${lines} lines`);
|
|
362
|
+
passed++;
|
|
254
363
|
}
|
|
255
|
-
|
|
256
|
-
if (warn) { console.log(yellow(' ⚠ ') + file + ` — ${warn}`); warnings++; }
|
|
257
|
-
else { console.log(green(' ✔ ') + file + ` — ${lines} lines`); passed++; }
|
|
258
364
|
}
|
|
259
365
|
}
|
|
260
366
|
|
|
367
|
+
// ─── Run checks on docs/ ───
|
|
261
368
|
if (hasDocs) {
|
|
262
369
|
console.log('');
|
|
263
370
|
console.log(dim(' ── docs/ ──'));
|
|
@@ -266,13 +373,28 @@ function cmdCheck(targetDir) {
|
|
|
266
373
|
for (const file of DOCS_FILES) {
|
|
267
374
|
const fp = path.join(docsDir, file);
|
|
268
375
|
if (!exists(fp)) { console.log(yellow(' ⚠ ') + file + ' — missing'); warnings++; continue; }
|
|
376
|
+
|
|
269
377
|
const content = fs.readFileSync(fp, 'utf-8').trim();
|
|
270
378
|
const lines = content.split('\n').length;
|
|
271
|
-
|
|
272
|
-
|
|
379
|
+
|
|
380
|
+
const allIssues = checkQuality(content, file);
|
|
381
|
+
const fails = allIssues.filter(i => i.level === 'fail');
|
|
382
|
+
const warns = allIssues.filter(i => i.level === 'warn');
|
|
383
|
+
|
|
384
|
+
if (fails.length > 0) {
|
|
385
|
+
console.log(red(' ✖ ') + file + ' — ' + fails[0].msg);
|
|
386
|
+
failed++;
|
|
387
|
+
} else if (warns.length > 0) {
|
|
388
|
+
console.log(yellow(' ⚠ ') + file + ` — ${lines} lines` + yellow(` (${warns.map(w => w.msg).join('; ')})`));
|
|
389
|
+
warnings++;
|
|
390
|
+
} else {
|
|
391
|
+
console.log(green(' ✔ ') + file + ` — ${lines} lines`);
|
|
392
|
+
passed++;
|
|
393
|
+
}
|
|
273
394
|
}
|
|
274
395
|
}
|
|
275
396
|
|
|
397
|
+
// ─── Summary ───
|
|
276
398
|
console.log('');
|
|
277
399
|
let result = '';
|
|
278
400
|
if (failed > 0) result = red(`${failed} failed`);
|
|
@@ -361,6 +483,393 @@ function cmdReset(targetDir) {
|
|
|
361
483
|
cmdAll(targetDir);
|
|
362
484
|
}
|
|
363
485
|
|
|
486
|
+
function copyDirRecursive(src, dest) {
|
|
487
|
+
if (!exists(src)) return 0;
|
|
488
|
+
fs.mkdirSync(dest, { recursive: true });
|
|
489
|
+
let count = 0;
|
|
490
|
+
for (const item of fs.readdirSync(src)) {
|
|
491
|
+
const srcPath = path.join(src, item);
|
|
492
|
+
const destPath = path.join(dest, item);
|
|
493
|
+
const stat = fs.statSync(srcPath);
|
|
494
|
+
if (stat.isDirectory()) {
|
|
495
|
+
count += copyDirRecursive(srcPath, destPath);
|
|
496
|
+
} else {
|
|
497
|
+
if (item === 'VIBE_GUIDE.md') continue;
|
|
498
|
+
fs.writeFileSync(destPath, fs.readFileSync(srcPath));
|
|
499
|
+
count++;
|
|
500
|
+
}
|
|
501
|
+
}
|
|
502
|
+
return count;
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
function cmdExport(targetDir, hubPathArg) {
|
|
506
|
+
targetDir = path.resolve(targetDir);
|
|
507
|
+
let hubPath = hubPathArg;
|
|
508
|
+
if (!hubPath && exists(HUB_CONFIG)) {
|
|
509
|
+
hubPath = fs.readFileSync(HUB_CONFIG, 'utf-8').trim();
|
|
510
|
+
}
|
|
511
|
+
if (!hubPath) {
|
|
512
|
+
console.log(red('\n ✖ No hub path specified.'));
|
|
513
|
+
console.log('');
|
|
514
|
+
console.log(' First time? Set your hub path:');
|
|
515
|
+
console.log(cyan(' vibes hub C:\\Users\\You\\Documents\\VibeHub'));
|
|
516
|
+
console.log('');
|
|
517
|
+
console.log(' Then run:');
|
|
518
|
+
console.log(cyan(' vibes export'));
|
|
519
|
+
console.log('');
|
|
520
|
+
process.exit(1);
|
|
521
|
+
}
|
|
522
|
+
|
|
523
|
+
hubPath = path.resolve(hubPath);
|
|
524
|
+
const name = getProjectName(targetDir);
|
|
525
|
+
const projectHub = path.join(hubPath, name);
|
|
526
|
+
|
|
527
|
+
console.log('');
|
|
528
|
+
console.log(bold(' 📤 vibes export'));
|
|
529
|
+
console.log(dim(` Exporting ${cyan(name)} → ${hubPath}`));
|
|
530
|
+
console.log('');
|
|
531
|
+
|
|
532
|
+
let total = 0;
|
|
533
|
+
|
|
534
|
+
const vibeDir = path.join(targetDir, VIBE_DIR);
|
|
535
|
+
if (exists(vibeDir)) {
|
|
536
|
+
const count = copyDirRecursive(vibeDir, path.join(projectHub, VIBE_DIR));
|
|
537
|
+
console.log(green(' ✔ ') + `.vibe/ — ${count} files`);
|
|
538
|
+
total += count;
|
|
539
|
+
} else {
|
|
540
|
+
console.log(yellow(' ⚠ ') + '.vibe/ not found, skipping');
|
|
541
|
+
}
|
|
542
|
+
|
|
543
|
+
const docsDir = path.join(targetDir, DOCS_DIR);
|
|
544
|
+
if (exists(docsDir)) {
|
|
545
|
+
const count = copyDirRecursive(docsDir, path.join(projectHub, DOCS_DIR));
|
|
546
|
+
console.log(green(' ✔ ') + `docs/ — ${count} files`);
|
|
547
|
+
total += count;
|
|
548
|
+
} else {
|
|
549
|
+
console.log(yellow(' ⚠ ') + 'docs/ not found, skipping');
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
const meta = { project: name, exported_at: now(), source: targetDir };
|
|
553
|
+
fs.writeFileSync(path.join(projectHub, 'export.json'), JSON.stringify(meta, null, 2), 'utf-8');
|
|
554
|
+
total++;
|
|
555
|
+
|
|
556
|
+
console.log('');
|
|
557
|
+
console.log(green(` ✔ Exported ${total} files to ${projectHub}`));
|
|
558
|
+
|
|
559
|
+
generateHubIndex(hubPath);
|
|
560
|
+
|
|
561
|
+
console.log('');
|
|
562
|
+
console.log(dim(' Commit and push your hub repo to sync across machines:'));
|
|
563
|
+
console.log(cyan(` cd "${hubPath}" && git add -A && git commit -m "export ${name}" && git push`));
|
|
564
|
+
console.log('');
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
function generateHubIndex(hubPath) {
|
|
568
|
+
const projects = [];
|
|
569
|
+
for (const dir of fs.readdirSync(hubPath)) {
|
|
570
|
+
const dirPath = path.join(hubPath, dir);
|
|
571
|
+
if (!fs.statSync(dirPath).isDirectory()) continue;
|
|
572
|
+
if (dir.startsWith('.') || dir.startsWith('_')) continue;
|
|
573
|
+
|
|
574
|
+
const exportFile = path.join(dirPath, 'export.json');
|
|
575
|
+
const vibeDir = path.join(dirPath, VIBE_DIR);
|
|
576
|
+
const docsDir = path.join(dirPath, DOCS_DIR);
|
|
577
|
+
|
|
578
|
+
let exportedAt = 'unknown';
|
|
579
|
+
if (exists(exportFile)) {
|
|
580
|
+
try {
|
|
581
|
+
const meta = JSON.parse(fs.readFileSync(exportFile, 'utf-8'));
|
|
582
|
+
exportedAt = meta.exported_at ? meta.exported_at.split('T')[0] : 'unknown';
|
|
583
|
+
} catch {}
|
|
584
|
+
}
|
|
585
|
+
|
|
586
|
+
let vibeFilled = 0, vibeNA = 0, vibeTotal = 0;
|
|
587
|
+
if (exists(vibeDir)) {
|
|
588
|
+
for (const f of VIBE_FILES) {
|
|
589
|
+
const fp = path.join(vibeDir, f);
|
|
590
|
+
if (!exists(fp)) continue;
|
|
591
|
+
vibeTotal++;
|
|
592
|
+
const content = fs.readFileSync(fp, 'utf-8').trim();
|
|
593
|
+
if (content.match(/^N\/A/m)) vibeNA++;
|
|
594
|
+
else if (content.split('\n').length >= 10) vibeFilled++;
|
|
595
|
+
}
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
let docsFilled = 0;
|
|
599
|
+
if (exists(docsDir)) {
|
|
600
|
+
for (const f of DOCS_FILES) {
|
|
601
|
+
const fp = path.join(docsDir, f);
|
|
602
|
+
if (!exists(fp)) continue;
|
|
603
|
+
if (fs.readFileSync(fp, 'utf-8').trim().split('\n').length >= 10) docsFilled++;
|
|
604
|
+
}
|
|
605
|
+
}
|
|
606
|
+
|
|
607
|
+
let purpose = '';
|
|
608
|
+
const purposeFile = path.join(vibeDir, 'purpose.md');
|
|
609
|
+
if (exists(purposeFile)) {
|
|
610
|
+
for (const line of fs.readFileSync(purposeFile, 'utf-8').split('\n')) {
|
|
611
|
+
const t = line.trim();
|
|
612
|
+
if (t && !t.startsWith('#') && !t.startsWith('<!--') && !t.startsWith('-->') && !t.startsWith('[')) {
|
|
613
|
+
purpose = t.substring(0, 80);
|
|
614
|
+
break;
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
projects.push({ name: dir, exportedAt, vibeFilled, vibeNA, vibeTotal, docsFilled, purpose });
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
if (projects.length === 0) return;
|
|
623
|
+
|
|
624
|
+
let md = `# Vibes Hub\n\n`;
|
|
625
|
+
md += `> Auto-generated index. Last updated: ${now().split('T')[0]}\n\n`;
|
|
626
|
+
md += `| Project | .vibe/ | docs/ | Last Export | Purpose |\n`;
|
|
627
|
+
md += `|---|---|---|---|---|\n`;
|
|
628
|
+
for (const p of projects.sort((a, b) => a.name.localeCompare(b.name))) {
|
|
629
|
+
const vs = p.vibeTotal > 0 ? `${p.vibeFilled}/${p.vibeTotal}` + (p.vibeNA > 0 ? ` (${p.vibeNA} N/A)` : '') : '—';
|
|
630
|
+
const ds = p.docsFilled > 0 ? `${p.docsFilled}/${DOCS_FILES.length}` : '—';
|
|
631
|
+
md += `| [${p.name}](./${p.name}/) | ${vs} | ${ds} | ${p.exportedAt} | ${p.purpose} |\n`;
|
|
632
|
+
}
|
|
633
|
+
md += `\n---\n\n**${projects.length} projects** tracked.\n`;
|
|
634
|
+
|
|
635
|
+
fs.writeFileSync(path.join(hubPath, 'index.md'), md, 'utf-8');
|
|
636
|
+
console.log(green(' ✔ ') + 'Updated hub index.md');
|
|
637
|
+
}
|
|
638
|
+
|
|
639
|
+
function cmdHub(hubPathArg) {
|
|
640
|
+
if (!hubPathArg) {
|
|
641
|
+
if (exists(HUB_CONFIG)) {
|
|
642
|
+
const saved = fs.readFileSync(HUB_CONFIG, 'utf-8').trim();
|
|
643
|
+
console.log(`\n Hub path: ${cyan(saved)}\n`);
|
|
644
|
+
} else {
|
|
645
|
+
console.log(yellow('\n No hub configured.'));
|
|
646
|
+
console.log(dim(' Set one with: vibes hub <path>\n'));
|
|
647
|
+
}
|
|
648
|
+
return;
|
|
649
|
+
}
|
|
650
|
+
|
|
651
|
+
const resolved = path.resolve(hubPathArg);
|
|
652
|
+
const isNew = !exists(resolved) || fs.readdirSync(resolved).length === 0;
|
|
653
|
+
|
|
654
|
+
// Create directory
|
|
655
|
+
fs.mkdirSync(resolved, { recursive: true });
|
|
656
|
+
|
|
657
|
+
console.log('');
|
|
658
|
+
console.log(bold(' 🗂️ vibes hub setup'));
|
|
659
|
+
console.log('');
|
|
660
|
+
|
|
661
|
+
if (isNew) {
|
|
662
|
+
// Initialize git
|
|
663
|
+
const { execSync } = require('child_process');
|
|
664
|
+
try {
|
|
665
|
+
execSync('git init', { cwd: resolved, stdio: 'ignore' });
|
|
666
|
+
console.log(green(' ✔ ') + 'Created directory & initialized git');
|
|
667
|
+
} catch {
|
|
668
|
+
console.log(green(' ✔ ') + 'Created directory');
|
|
669
|
+
console.log(yellow(' ⚠ ') + 'Could not git init (git not found?)');
|
|
670
|
+
}
|
|
671
|
+
|
|
672
|
+
// Create README
|
|
673
|
+
const readme = [
|
|
674
|
+
'# Vibes Hub',
|
|
675
|
+
'',
|
|
676
|
+
'Central collection of `.vibe/` and `docs/` files from all my projects.',
|
|
677
|
+
'',
|
|
678
|
+
'## How this works',
|
|
679
|
+
'',
|
|
680
|
+
'1. Run `vibes export` from any project directory',
|
|
681
|
+
'2. Commit and push: `git add -A && git commit -m "update" && git push`',
|
|
682
|
+
'3. Pull on another machine: `git pull`',
|
|
683
|
+
'',
|
|
684
|
+
'## Projects',
|
|
685
|
+
'',
|
|
686
|
+
'See [index.md](index.md) for auto-generated dashboard.',
|
|
687
|
+
'',
|
|
688
|
+
'## Cross-Project Intelligence',
|
|
689
|
+
'',
|
|
690
|
+
'The `_insights/` folder is where AI analyzes patterns across all your projects.',
|
|
691
|
+
'',
|
|
692
|
+
'- **[ANALYZE.md](_insights/ANALYZE.md)** — Copy-paste prompt for your AI agent',
|
|
693
|
+
'- **[patterns.md](_insights/patterns.md)** — Architecture, security, and tech patterns',
|
|
694
|
+
'- **[standards.md](_insights/standards.md)** — Universal rules for all projects',
|
|
695
|
+
'- **[opportunities.md](_insights/opportunities.md)** — What one project can learn from another',
|
|
696
|
+
'',
|
|
697
|
+
'Export 3+ projects, then run the analysis prompt to find cross-project improvements.',
|
|
698
|
+
'',
|
|
699
|
+
].join('\n');
|
|
700
|
+
fs.writeFileSync(path.join(resolved, 'README.md'), readme, 'utf-8');
|
|
701
|
+
console.log(green(' ✔ ') + 'Created README.md');
|
|
702
|
+
|
|
703
|
+
// Create .gitignore
|
|
704
|
+
fs.writeFileSync(path.join(resolved, '.gitignore'), 'node_modules/\n.DS_Store\nThumbs.db\n', 'utf-8');
|
|
705
|
+
console.log(green(' ✔ ') + 'Created .gitignore');
|
|
706
|
+
|
|
707
|
+
// Create _insights/ intelligence layer
|
|
708
|
+
const insightsDir = path.join(resolved, '_insights');
|
|
709
|
+
fs.mkdirSync(insightsDir, { recursive: true });
|
|
710
|
+
|
|
711
|
+
// Analysis prompt — the AI reads this to know what to do
|
|
712
|
+
const analyze = [
|
|
713
|
+
'# Hub Analysis Prompt',
|
|
714
|
+
'',
|
|
715
|
+
'> Copy-paste this to your AI agent after exporting 3+ projects.',
|
|
716
|
+
'',
|
|
717
|
+
'---',
|
|
718
|
+
'',
|
|
719
|
+
'```',
|
|
720
|
+
'I have a Vibes Hub — a central collection of .vibe/ and docs/ files',
|
|
721
|
+
'from all my projects. Read every project folder in this hub and:',
|
|
722
|
+
'',
|
|
723
|
+
'1. CROSS-PROJECT PATTERNS',
|
|
724
|
+
' - What architectural patterns appear across multiple projects?',
|
|
725
|
+
' - What security approaches are used? Are they consistent?',
|
|
726
|
+
' - What tech stack choices are shared? Where do they diverge?',
|
|
727
|
+
' - What naming conventions or coding standards are consistent?',
|
|
728
|
+
'',
|
|
729
|
+
'2. BEST PRACTICES TO REPLICATE',
|
|
730
|
+
' - Which project has the best documentation quality?',
|
|
731
|
+
' - Which project solved a problem that others still have?',
|
|
732
|
+
' - What did one project do exceptionally well that could set',
|
|
733
|
+
' the standard for all projects?',
|
|
734
|
+
' - Are there security, testing, or deployment practices from',
|
|
735
|
+
' one project that others are missing?',
|
|
736
|
+
'',
|
|
737
|
+
'3. TRANSFERABLE IMPROVEMENTS',
|
|
738
|
+
' - For each project, list specific improvements it could adopt',
|
|
739
|
+
' from another project. Be concrete: "Project A could adopt',
|
|
740
|
+
' Project B\'s approach to X because Y."',
|
|
741
|
+
' - Identify shared problems that could have a shared solution.',
|
|
742
|
+
'',
|
|
743
|
+
'4. RISKS AND GAPS',
|
|
744
|
+
' - Which projects have risks that could affect other projects?',
|
|
745
|
+
' - Are there shared dependencies that create portfolio risk?',
|
|
746
|
+
' - Which projects have missing or weak documentation areas?',
|
|
747
|
+
'',
|
|
748
|
+
'5. UNIVERSAL STANDARDS',
|
|
749
|
+
' - Based on what works best across projects, propose standards',
|
|
750
|
+
' that should apply to ALL projects going forward.',
|
|
751
|
+
' - These could be file size limits, security practices, naming',
|
|
752
|
+
' conventions, documentation requirements, etc.',
|
|
753
|
+
'',
|
|
754
|
+
'Write your findings into the _insights/ folder:',
|
|
755
|
+
'- patterns.md — Cross-project patterns',
|
|
756
|
+
'- standards.md — Proposed universal standards',
|
|
757
|
+
'- opportunities.md — Specific transferable improvements',
|
|
758
|
+
'',
|
|
759
|
+
'Be specific. Reference actual project names and files.',
|
|
760
|
+
'```',
|
|
761
|
+
'',
|
|
762
|
+
].join('\n');
|
|
763
|
+
fs.writeFileSync(path.join(insightsDir, 'ANALYZE.md'), analyze, 'utf-8');
|
|
764
|
+
|
|
765
|
+
// Patterns skeleton
|
|
766
|
+
const patterns = [
|
|
767
|
+
'# Cross-Project Patterns',
|
|
768
|
+
'',
|
|
769
|
+
'> Auto-populated by AI analysis. Run the prompt in ANALYZE.md.',
|
|
770
|
+
'',
|
|
771
|
+
'## Architecture Patterns',
|
|
772
|
+
'',
|
|
773
|
+
'<!-- What architectural approaches appear across multiple projects? -->',
|
|
774
|
+
'',
|
|
775
|
+
'## Security Patterns',
|
|
776
|
+
'',
|
|
777
|
+
'<!-- How do projects handle auth, encryption, secrets? -->',
|
|
778
|
+
'',
|
|
779
|
+
'## Shared Tech Stack',
|
|
780
|
+
'',
|
|
781
|
+
'<!-- Languages, frameworks, databases used across projects -->',
|
|
782
|
+
'',
|
|
783
|
+
'| Technology | Used In | Notes |',
|
|
784
|
+
'|---|---|---|',
|
|
785
|
+
'| | | |',
|
|
786
|
+
'',
|
|
787
|
+
'## Common Anti-Patterns',
|
|
788
|
+
'',
|
|
789
|
+
'<!-- Mistakes or bad patterns that appear in multiple projects -->',
|
|
790
|
+
'',
|
|
791
|
+
].join('\n');
|
|
792
|
+
fs.writeFileSync(path.join(insightsDir, 'patterns.md'), patterns, 'utf-8');
|
|
793
|
+
|
|
794
|
+
// Standards skeleton
|
|
795
|
+
const standards = [
|
|
796
|
+
'# Universal Standards',
|
|
797
|
+
'',
|
|
798
|
+
'> Standards that should apply to ALL projects, based on what works best.',
|
|
799
|
+
'',
|
|
800
|
+
'## Code Standards',
|
|
801
|
+
'',
|
|
802
|
+
'<!-- File size limits, naming conventions, banned patterns -->',
|
|
803
|
+
'',
|
|
804
|
+
'## Documentation Standards',
|
|
805
|
+
'',
|
|
806
|
+
'<!-- What every project must document, minimum quality bar -->',
|
|
807
|
+
'',
|
|
808
|
+
'## Security Standards',
|
|
809
|
+
'',
|
|
810
|
+
'<!-- Auth patterns, encryption requirements, secrets management -->',
|
|
811
|
+
'',
|
|
812
|
+
'## AI Agent Standards',
|
|
813
|
+
'',
|
|
814
|
+
'<!-- Rules every AI agent must follow across all projects -->',
|
|
815
|
+
'',
|
|
816
|
+
].join('\n');
|
|
817
|
+
fs.writeFileSync(path.join(insightsDir, 'standards.md'), standards, 'utf-8');
|
|
818
|
+
|
|
819
|
+
// Opportunities skeleton
|
|
820
|
+
const opportunities = [
|
|
821
|
+
'# Transferable Improvements',
|
|
822
|
+
'',
|
|
823
|
+
'> Specific things one project does well that others should adopt.',
|
|
824
|
+
'',
|
|
825
|
+
'## [Source Project] → [Target Project]',
|
|
826
|
+
'',
|
|
827
|
+
'**What:** [Specific practice or solution]',
|
|
828
|
+
'',
|
|
829
|
+
'**Why:** [Why the target project would benefit]',
|
|
830
|
+
'',
|
|
831
|
+
'**How:** [What the target project needs to change]',
|
|
832
|
+
'',
|
|
833
|
+
'---',
|
|
834
|
+
'',
|
|
835
|
+
'<!-- Copy the template above for each transferable improvement. -->',
|
|
836
|
+
'',
|
|
837
|
+
].join('\n');
|
|
838
|
+
fs.writeFileSync(path.join(insightsDir, 'opportunities.md'), opportunities, 'utf-8');
|
|
839
|
+
|
|
840
|
+
console.log(green(' ✔ ') + 'Created _insights/ (analysis prompt + templates)');
|
|
841
|
+
} else {
|
|
842
|
+
console.log(green(' ✔ ') + 'Directory already exists');
|
|
843
|
+
}
|
|
844
|
+
|
|
845
|
+
// Save config
|
|
846
|
+
fs.writeFileSync(HUB_CONFIG, resolved, 'utf-8');
|
|
847
|
+
console.log(green(' ✔ ') + `Hub path saved: ${cyan(resolved)}`);
|
|
848
|
+
|
|
849
|
+
console.log('');
|
|
850
|
+
console.log(bold(' Next — connect to GitHub:'));
|
|
851
|
+
console.log('');
|
|
852
|
+
console.log(dim(' 1. Go to ') + cyan('github.com/new'));
|
|
853
|
+
console.log(dim(' Name: ') + bold('VibeHub') + dim(' (or whatever you want)'));
|
|
854
|
+
console.log(dim(' Visibility: ') + bold('Private'));
|
|
855
|
+
console.log(dim(' Click "Create repository"'));
|
|
856
|
+
console.log('');
|
|
857
|
+
console.log(dim(' 2. Run these commands:'));
|
|
858
|
+
console.log('');
|
|
859
|
+
console.log(cyan(` cd "${resolved}"`));
|
|
860
|
+
console.log(cyan(' git remote add origin git@github.com:YOUR_USERNAME/VibeHub.git'));
|
|
861
|
+
console.log(cyan(' git add -A && git commit -m "init hub" && git push -u origin main'));
|
|
862
|
+
console.log('');
|
|
863
|
+
console.log(dim(' After that, exporting from any project is just:'));
|
|
864
|
+
console.log('');
|
|
865
|
+
console.log(cyan(' vibes export'));
|
|
866
|
+
console.log(cyan(` cd "${resolved}" && git add -A && git commit -m "update" && git push`));
|
|
867
|
+
console.log('');
|
|
868
|
+
console.log(dim(' On another computer, clone the repo and run:'));
|
|
869
|
+
console.log(cyan(' vibes hub <path-to-cloned-repo>'));
|
|
870
|
+
console.log('');
|
|
871
|
+
}
|
|
872
|
+
|
|
364
873
|
function printNextSteps() {
|
|
365
874
|
console.log('');
|
|
366
875
|
console.log(bold(' What to do next:'));
|
|
@@ -381,13 +890,18 @@ function printHelp() {
|
|
|
381
890
|
console.log('');
|
|
382
891
|
console.log(' ' + bold('Usage:'));
|
|
383
892
|
console.log('');
|
|
384
|
-
console.log(' vibes init
|
|
385
|
-
console.log(' vibes docs
|
|
386
|
-
console.log(' vibes all
|
|
387
|
-
console.log(' vibes check
|
|
388
|
-
console.log(' vibes status
|
|
389
|
-
console.log(' vibes reset
|
|
390
|
-
console.log('
|
|
893
|
+
console.log(' vibes init Create .vibe/ with the full suite (15 files + guide)');
|
|
894
|
+
console.log(' vibes docs Create docs/ operational documentation (11 files)');
|
|
895
|
+
console.log(' vibes all Create both .vibe/ and docs/ at once');
|
|
896
|
+
console.log(' vibes check Validate all files for completeness');
|
|
897
|
+
console.log(' vibes status Quick health dashboard');
|
|
898
|
+
console.log(' vibes reset Delete and recreate everything');
|
|
899
|
+
console.log('');
|
|
900
|
+
console.log(' ' + bold('Hub (cross-project sync):'));
|
|
901
|
+
console.log('');
|
|
902
|
+
console.log(' vibes hub <path> Set your central hub directory (saved globally)');
|
|
903
|
+
console.log(' vibes export Export .vibe/ + docs/ to your hub');
|
|
904
|
+
console.log(' vibes export <path> Export to a specific hub path');
|
|
391
905
|
console.log('');
|
|
392
906
|
console.log(' ' + bold('.vibe/') + dim(' — Project memory'));
|
|
393
907
|
console.log(dim(' Core: purpose · architecture · flows · entities · decisions · state'));
|
|
@@ -420,6 +934,8 @@ switch (command) {
|
|
|
420
934
|
case 'check': cmdCheck(targetDir); break;
|
|
421
935
|
case 'status': cmdStatus(targetDir); break;
|
|
422
936
|
case 'reset': cmdReset(targetDir); break;
|
|
937
|
+
case 'export': cmdExport('.', args[1]); break;
|
|
938
|
+
case 'hub': cmdHub(args[1]); break;
|
|
423
939
|
case 'help': case '--help': case '-h': printHelp(); break;
|
|
424
940
|
default:
|
|
425
941
|
console.log(red(`\n Unknown command: ${command}`));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "vibe-me",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "The complete semantic layer for any project. Repository memory, product memory, business memory, AI instructions, and living context — so any human or AI can understand your project without reading the source.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"vibes",
|