vibe-me 3.0.0 โ†’ 3.2.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 CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  # ๐ŸŒ€ vibes
4
4
 
5
- ### Every project should contain a continuously maintained,<br>AI-readable and human-readable model of itself.
5
+ ### Project memory. Cross-project intelligence.<br>One command per project. One hub for all of them.
6
6
 
7
7
  [![npm version](https://img.shields.io/npm/v/vibe-me?style=flat-square&color=7c3aed&label=npm)](https://www.npmjs.com/package/vibe-me)
8
8
  [![license](https://img.shields.io/badge/license-MIT-10b981?style=flat-square)](LICENSE)
@@ -11,16 +11,19 @@
11
11
 
12
12
  <br>
13
13
 
14
- **One command. Full project memory. Total clarity.**
14
+ **Scaffold. Fill. Validate. Sync. Get feedback.**
15
15
 
16
- `vibes` creates a `.vibe/` semantic layer and a standardized `docs/` folder โ€”<br>
17
- so any human or AI can understand your project, your product, and your business<br>
18
- without reading the source code.
16
+ `vibes` gives every project a `.vibe/` semantic layer and `docs/` folder โ€”<br>
17
+ then connects all your projects through a **Vibes Hub** that finds patterns,<br>
18
+ surfaces improvements, and lets your best work raise the bar everywhere.
19
19
 
20
20
  <br>
21
21
 
22
- ```bash
23
- npx vibe-me all
22
+ ```
23
+ npx vibe-me all # scaffold .vibe/ + docs/ in any project
24
+ npx vibe-me check # validate quality (catches unfilled templates)
25
+ npx vibe-me export # sync to your hub
26
+ npx vibe-me insights # get cross-project feedback
24
27
  ```
25
28
 
26
29
  <br>
@@ -69,6 +72,7 @@ Not a wiki. Not a Confluence graveyard. Not a 200-page design doc nobody reads.
69
72
  - Nobody knows the pricing rationale or competitive landscape
70
73
  - User frustrations are invisible โ€” product decisions are guesswork
71
74
  - Documentation rots because nobody owns it
75
+ - Your projects are islands โ€” lessons learned in one are invisible to others
72
76
 
73
77
  </td>
74
78
  <td width="50%">
@@ -81,14 +85,15 @@ Not a wiki. Not a Confluence graveyard. Not a 200-page design doc nobody reads.
81
85
  - Business strategy, pricing, and market position are documented
82
86
  - User personas include emotional reality โ€” fears, confusion, goals
83
87
  - Documentation stays fresh because the AI updates it
88
+ - **Hub connects all your projects** โ€” patterns, standards, and improvements flow between them
84
89
 
85
90
  </td>
86
91
  </tr>
87
92
  </table>
88
93
 
89
- > **This isn't documentation. It's a digital twin of your entire venture.**<br>
94
+ > **This isn't documentation. It's a digital twin of your entire portfolio.**<br>
90
95
  > Repository memory. Product memory. Business memory. AI instructions. Living context.<br>
91
- > It's the difference between a repository and a *project that explains itself.*
96
+ > Each project explains itself โ€” and the hub makes all your projects learn from each other.
92
97
 
93
98
  ---
94
99
 
@@ -200,9 +205,13 @@ Download [`Vibe.bat`](Vibe.bat), drop it in any project folder, and double-click
200
205
  | `vibes init` | Create `.vibe/` with the full suite โ€” 15 files + guide |
201
206
  | `vibes docs` | Create `docs/` operational documentation โ€” 11 files |
202
207
  | `vibes all` | Create both `.vibe/` and `docs/` at once |
203
- | `vibes check` | Validate all files for completeness and quality |
208
+ | `vibes check` | Validate all files โ€” catches unfilled templates, empty sections, missing structure |
204
209
  | `vibes status` | Quick health dashboard with filled/N-A/empty counts |
205
210
  | `vibes reset` | Delete and recreate everything |
211
+ | | |
212
+ | `vibes hub <path>` | Set up a central hub โ€” git inits, creates `_insights/`, saves config |
213
+ | `vibes export` | Export `.vibe/` + `docs/` from current project to your hub |
214
+ | `vibes insights` | Show hub feedback for this project โ€” opportunities, standards, anti-patterns |
206
215
  | `vibes help` | Show all commands |
207
216
 
208
217
  ---
@@ -257,10 +266,27 @@ Every line in `.vibe/` should pass this test:
257
266
 
258
267
  ## ๐Ÿ” Quality Validation
259
268
 
269
+ `vibes check` doesn't just count lines โ€” it catches real problems:
270
+
260
271
  ```bash
261
272
  vibes check
262
273
  ```
263
274
 
275
+ **On a freshly scaffolded project (before AI fills it out):**
276
+ ```
277
+ ๐Ÿ” vibes check
278
+
279
+ โ”€โ”€ .vibe/ โ”€โ”€
280
+
281
+ โœ– purpose.md โ€” still contains template instructions (unfilled)
282
+ โœ– architecture.md โ€” still contains template instructions (unfilled)
283
+ โœ– flows.md โ€” still contains template instructions (unfilled)
284
+ ...
285
+
286
+ Result: 24 failed, 1 warnings, 0 passed
287
+ ```
288
+
289
+ **After your AI agent fills everything out:**
264
290
  ```
265
291
  ๐Ÿ” vibes check
266
292
 
@@ -270,7 +296,7 @@ vibes check
270
296
  โœ” architecture.md โ€” 86 lines
271
297
  โœ” flows.md โ€” 144 lines
272
298
  โœ” entities.md โ€” 135 lines
273
- โœ” decisions.md โ€” 91 lines
299
+ โš  decisions.md โ€” 91 lines (no dependency graph fields)
274
300
  โœ” state.json โ€” 82 lines
275
301
  โœ” context.md โ€” 28 lines
276
302
  โœ” ai.md โ€” 45 lines
@@ -280,25 +306,124 @@ vibes check
280
306
  โœ” experiments.md โ€” 19 lines
281
307
  โŠ˜ business.md โ€” N/A (not applicable)
282
308
  โŠ˜ market.md โ€” N/A (not applicable)
283
- โŠ˜ risks.md โ€” 34 lines
284
-
285
- โ”€โ”€ docs/ โ”€โ”€
286
-
287
- โœ” README.md โ€” 45 lines
288
- โœ” topology.md โ€” 120 lines
289
- โœ” architecture.md โ€” 89 lines
290
- โœ” api.md โ€” 200 lines
291
- โœ” issues.md โ€” 34 lines
292
- โœ” resolved.md โ€” 67 lines
293
- โœ” roadmap.md โ€” 42 lines
294
- โœ” developer_guide.md โ€” 95 lines
295
- โœ” troubleshooting.md โ€” 55 lines
296
- โœ” glossary.md โ€” 28 lines
297
-
298
- Result: 2 N/A, 23 passed โœ”
309
+ โœ” risks.md โ€” 34 lines
310
+
311
+ Result: 2 N/A, 1 warnings, 22 passed
312
+ ```
313
+
314
+ What it catches:
315
+ - **Unfilled templates** โ€” files that still have `INSTRUCTIONS FOR AI AGENT` (hard fail)
316
+ - **Missing structure** โ€” `purpose.md` without a "NOT do" section, `decisions.md` without dependency graph
317
+ - **Empty bullets/tables** โ€” placeholder content that wasn't filled in
318
+ - **Stale data** โ€” `state.json` not updated in 30+ days
319
+ - **Missing emotional reality** โ€” `users.md` without frustrations, fears, or goals
320
+ - **N/A files** โ€” recognized and not penalized
321
+
322
+ ---
323
+
324
+ ## ๐Ÿ—‚๏ธ Vibes Hub โ€” Your Projects Talking to Each Other
325
+
326
+ The hub is where the magic happens. Every project you work on โ€” across any number of computers โ€” exports its `.vibe/` and `docs/` into one central repo. An AI analyzes them all and finds patterns, anti-patterns, and improvements that flow **back** into each project.
327
+
328
+ ```
329
+ โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” vibes export โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ” vibes insights โ”Œโ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”
330
+ โ”‚ Project A โ”‚ โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ†’ โ”‚ Vibes Hub โ”‚ โ†โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ โ”‚ Project B โ”‚
331
+ โ”‚ .vibe/ โ”‚ โ”‚ _insights/ โ”‚ โ”‚ .vibe/ โ”‚
332
+ โ”‚ docs/ โ”‚ โ”‚ patterns โ”‚ "You should adopt โ”‚ docs/ โ”‚
333
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜ โ”‚ standards โ”‚ Project A's auth" โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
334
+ โ”‚ opportunitiesโ”‚
335
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
336
+ ```
337
+
338
+ ### Step 1: Set up the hub (one time)
339
+
340
+ ```bash
341
+ vibes hub ~/Documents/VibeHub
342
+ ```
343
+
344
+ Creates a folder with git, a README, and an `_insights/` intelligence layer. Connect it to a private GitHub repo so it syncs across machines.
345
+
346
+ ### Step 2: Export from each project
347
+
348
+ ```bash
349
+ cd your-project
350
+ vibes export
351
+ ```
352
+
353
+ Copies `.vibe/` and `docs/` into your hub, organized by project name. Auto-generates an `index.md` dashboard.
354
+
355
+ ### Step 3: AI analysis (after 3+ projects)
356
+
357
+ Tell your AI agent:
358
+
359
+ > *"Read `_insights/ANALYZE.md` in my Vibes Hub and follow the instructions."*
360
+
361
+ The AI reads every project's `.vibe/` files and fills out:
362
+
363
+ | File | What it finds |
364
+ |:---|:---|
365
+ | `_insights/patterns.md` | Architecture, security, and tech stack patterns across all projects |
366
+ | `_insights/standards.md` | Universal rules that should apply everywhere (file size limits, schema validation, etc.) |
367
+ | `_insights/opportunities.md` | Specific transfers โ€” "Project A does X well โ†’ Project B should adopt it because Y" |
368
+
369
+ ### Step 4: Pull feedback into any project
370
+
371
+ ```bash
372
+ cd your-project
373
+ vibes insights
374
+ ```
375
+
376
+ This reads the hub's `_insights/` and shows you **what applies to the project you're standing in**:
377
+
299
378
  ```
379
+ ๐Ÿ’ก vibes insights โ€” JustLegal
300
380
 
301
- Files marked N/A are recognized as intentionally not applicable โ€” not penalized.
381
+ โ”€โ”€ Opportunities โ”€โ”€
382
+
383
+ โšก open-season โ†’ JustLegal
384
+ Adopt EXIF metadata scrubbing for user-uploaded files.
385
+ โšก JustPolitics โ†’ JustLegal / Spark
386
+ Mobile-responsive WebKit GPU blur optimizations.
387
+
388
+ โ”€โ”€ Universal Standards โ”€โ”€
389
+
390
+ โ—† Strict File Size Limits
391
+ โ—† Structured Schema Validation
392
+ โ—† Local API Access Authentication
393
+
394
+ โ”€โ”€ Anti-Patterns โ”€โ”€
395
+
396
+ โš  Unsecured Localhost APIs
397
+
398
+ 16 insights found. Review _insights/ in your hub for full details.
399
+ ```
400
+
401
+ One project's best practice becomes every project's standard. **Your portfolio gets smarter every time you export.**
402
+
403
+ ### Multi-machine sync
404
+
405
+ The hub is a standard Git repo. Push from one machine, pull on another:
406
+
407
+ 1. **First machine:** `vibes hub ~/Documents/VibeHub` โ†’ push to private GitHub repo
408
+ 2. **Other machines:** Clone the repo โ†’ `vibes hub ~/path/to/cloned/VibeHub`
409
+ 3. Export projects on any machine โ†’ commit & push โ†’ pull on the other โ†’ `vibes insights`
410
+
411
+ ```
412
+ VibeHub/
413
+ โ”œโ”€โ”€ index.md Auto-generated dashboard
414
+ โ”œโ”€โ”€ _insights/
415
+ โ”‚ โ”œโ”€โ”€ ANALYZE.md Prompt for AI analysis
416
+ โ”‚ โ”œโ”€โ”€ patterns.md Cross-project patterns
417
+ โ”‚ โ”œโ”€โ”€ standards.md Universal standards
418
+ โ”‚ โ””โ”€โ”€ opportunities.md Transferable improvements
419
+ โ”œโ”€โ”€ ProjectA/
420
+ โ”‚ โ”œโ”€โ”€ .vibe/
421
+ โ”‚ โ””โ”€โ”€ docs/
422
+ โ”œโ”€โ”€ ProjectB/
423
+ โ”‚ โ”œโ”€โ”€ .vibe/
424
+ โ”‚ โ””โ”€โ”€ docs/
425
+ โ””โ”€โ”€ ...
426
+ ```
302
427
 
303
428
  ---
304
429
 
package/bin/vibes.js CHANGED
@@ -11,10 +11,12 @@ const path = require('path');
11
11
  // Config
12
12
  // โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€
13
13
 
14
+ const CLI_VERSION = require(path.join(__dirname, '..', 'package.json')).version;
14
15
  const VIBE_DIR = '.vibe';
15
16
  const DOCS_DIR = 'docs';
16
17
  const TEMPLATES_DIR = path.join(__dirname, '..', 'templates');
17
18
  const SPEC_DIR = path.join(__dirname, '..', 'spec');
19
+ const HUB_CONFIG = path.join(process.env.USERPROFILE || process.env.HOME || '', '.vibes-hub');
18
20
 
19
21
  // All .vibe/ files โ€” the full suite, always installed
20
22
  const VIBE_FILES = [
@@ -208,6 +210,124 @@ function cmdCheck(targetDir) {
208
210
 
209
211
  let passed = 0, failed = 0, warnings = 0, na = 0;
210
212
 
213
+ // โ”€โ”€โ”€ Universal quality checks โ”€โ”€โ”€
214
+ function checkQuality(content, file) {
215
+ const issues = [];
216
+
217
+ // FAIL: Still has template instruction comments
218
+ if (content.includes('INSTRUCTIONS FOR AI AGENT')) {
219
+ issues.push({ level: 'fail', msg: 'still contains template instructions (unfilled)' });
220
+ return issues;
221
+ }
222
+
223
+ // FAIL: Too short to be real content
224
+ const lines = content.split('\n').length;
225
+ if (lines < 8) {
226
+ issues.push({ level: 'fail', msg: `only ${lines} lines โ€” needs real content` });
227
+ return issues;
228
+ }
229
+
230
+ // WARN: Has unfilled placeholder brackets like [Component Name]
231
+ const placeholders = content.match(/\[(?:Component|Persona|Experiment|Decision|Error|Deliverable|Feature|Issue)\s*\w*\]/gi);
232
+ if (placeholders && placeholders.length > 1) {
233
+ issues.push({ level: 'warn', msg: `${placeholders.length} unfilled placeholders (${placeholders[0]}, ...)` });
234
+ }
235
+
236
+ // WARN: Empty bullet points (lines that are just "- " or "- \n")
237
+ const emptyBullets = (content.match(/^- ?\s*$/gm) || []).length;
238
+ if (emptyBullets > 2) {
239
+ issues.push({ level: 'warn', msg: `${emptyBullets} empty bullet points` });
240
+ }
241
+
242
+ // WARN: Empty table rows (| | | |)
243
+ const emptyRows = (content.match(/^\|\s*\|\s*\|\s*\|/gm) || []).length;
244
+ if (emptyRows > 1) {
245
+ issues.push({ level: 'warn', msg: `${emptyRows} empty table rows` });
246
+ }
247
+
248
+ return issues;
249
+ }
250
+
251
+ // โ”€โ”€โ”€ File-specific structure checks โ”€โ”€โ”€
252
+ function checkStructure(content, file) {
253
+ const issues = [];
254
+
255
+ switch (file) {
256
+ case 'purpose.md':
257
+ if (!content.toLowerCase().includes('not do') && !content.toLowerCase().includes('does not'))
258
+ issues.push({ level: 'warn', msg: 'missing "NOT do" / scope boundary section' });
259
+ if (!content.toLowerCase().includes('who'))
260
+ issues.push({ level: 'warn', msg: 'doesn\'t mention who uses it' });
261
+ break;
262
+
263
+ case 'architecture.md':
264
+ if (!content.includes('โ†’') && !content.includes('->') && !content.includes('connects') && !content.includes('talks to'))
265
+ issues.push({ level: 'warn', msg: 'no data flow described (missing โ†’ or connection language)' });
266
+ break;
267
+
268
+ case 'flows.md':
269
+ const flowHeaders = (content.match(/^##\s+/gm) || []).length;
270
+ if (flowHeaders < 2) issues.push({ level: 'warn', msg: `only ${flowHeaders} flow(s) โ€” most projects have 3+` });
271
+ break;
272
+
273
+ case 'entities.md':
274
+ if (!content.toLowerCase().includes('depends') && !content.toLowerCase().includes('relationship'))
275
+ issues.push({ level: 'warn', msg: 'no dependency/relationship info for entities' });
276
+ break;
277
+
278
+ case 'decisions.md':
279
+ const decisionHeaders = (content.match(/^##\s+/gm) || []).length;
280
+ if (decisionHeaders < 2) issues.push({ level: 'warn', msg: `only ${decisionHeaders} decision(s) โ€” aim for 3+` });
281
+ if (!content.includes('Depends On') && !content.includes('Threatened By') && !content.includes('depends on'))
282
+ issues.push({ level: 'warn', msg: 'no dependency graph fields (Depends On, Threatened By)' });
283
+ break;
284
+
285
+ case 'state.json':
286
+ try {
287
+ const s = JSON.parse(content);
288
+ if (!s.vibe_updated) issues.push({ level: 'warn', msg: 'missing vibe_updated timestamp' });
289
+ else {
290
+ const days = (Date.now() - new Date(s.vibe_updated).getTime()) / 86400000;
291
+ if (days > 30) issues.push({ level: 'warn', msg: `last updated ${Math.floor(days)} days ago โ€” stale?` });
292
+ }
293
+ if (!s.project) issues.push({ level: 'warn', msg: 'missing project name' });
294
+ } catch {
295
+ issues.push({ level: 'fail', msg: 'invalid JSON' });
296
+ }
297
+ break;
298
+
299
+ case 'context.md':
300
+ if (!content.includes('## Current Focus'))
301
+ issues.push({ level: 'warn', msg: 'missing "Current Focus" section' });
302
+ else {
303
+ const focusIdx = content.indexOf('## Current Focus');
304
+ const nextSection = content.indexOf('##', focusIdx + 16);
305
+ const focusContent = content.substring(focusIdx, nextSection > 0 ? nextSection : undefined);
306
+ const focusBullets = (focusContent.match(/^- .{3,}/gm) || []).length;
307
+ if (focusBullets === 0) issues.push({ level: 'warn', msg: 'Current Focus has no entries' });
308
+ }
309
+ break;
310
+
311
+ case 'ai.md':
312
+ if (!content.toLowerCase().includes('never') && !content.toLowerCase().includes('don\'t') && !content.toLowerCase().includes('do not'))
313
+ issues.push({ level: 'warn', msg: 'no constraints defined โ€” what should agents NOT touch?' });
314
+ break;
315
+
316
+ case 'product.md':
317
+ if (!content.toLowerCase().includes('retention') && !content.toLowerCase().includes('why') && !content.toLowerCase().includes('value'))
318
+ issues.push({ level: 'warn', msg: 'missing value prop or retention drivers' });
319
+ break;
320
+
321
+ case 'users.md':
322
+ if (!content.toLowerCase().includes('frustrat') && !content.toLowerCase().includes('fear') && !content.toLowerCase().includes('pain') && !content.toLowerCase().includes('goal'))
323
+ issues.push({ level: 'warn', msg: 'missing emotional reality (frustrations, fears, goals)' });
324
+ break;
325
+ }
326
+
327
+ return issues;
328
+ }
329
+
330
+ // โ”€โ”€โ”€ Run checks on .vibe/ โ”€โ”€โ”€
211
331
  if (hasVibe) {
212
332
  console.log('');
213
333
  console.log(dim(' โ”€โ”€ .vibe/ โ”€โ”€'));
@@ -220,59 +340,89 @@ function cmdCheck(targetDir) {
220
340
  const content = fs.readFileSync(fp, 'utf-8').trim();
221
341
  const lines = content.split('\n').length;
222
342
 
223
- // Check for N/A files (product/business layers marked not applicable)
224
- if (content.match(/^N\/A/m) || content.match(/^"?N\/A/m)) {
343
+ // N/A check
344
+ if (content.match(/^N\/A/m)) {
225
345
  console.log(dim(' โŠ˜ ') + file + dim(' โ€” N/A (not applicable)'));
226
346
  na++;
227
347
  continue;
228
348
  }
229
349
 
230
- if (lines < 5) { console.log(yellow(' โš  ') + file + ` โ€” looks empty (${lines} lines)`); warnings++; continue; }
350
+ // Run quality + structure checks
351
+ const allIssues = [...checkQuality(content, file), ...checkStructure(content, file)];
352
+ const fails = allIssues.filter(i => i.level === 'fail');
353
+ const warns = allIssues.filter(i => i.level === 'warn');
231
354
 
232
- // File-specific checks
233
- let warn = null;
234
- if (file === 'purpose.md' && !content.toLowerCase().includes('not do')) warn = 'missing "NOT do" section';
235
- if (file === 'decisions.md' && (content.match(/^## Why /gm) || []).length < 2) warn = 'fewer than 2 decisions';
236
- if (file === 'entities.md' && !content.includes('What depends on it')) warn = 'missing dependency fields';
237
- if (file === 'flows.md' && (content.match(/^## \d+\./gm) || []).length < 2) warn = 'fewer than 2 flows';
238
- if (file === 'state.json') {
239
- try {
240
- const s = JSON.parse(content);
241
- if (!s.vibe_updated) warn = 'missing vibe_updated';
242
- else {
243
- const days = (Date.now() - new Date(s.vibe_updated).getTime()) / 86400000;
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)';
355
+ if (fails.length > 0) {
356
+ console.log(red(' โœ– ') + file + ' โ€” ' + fails[0].msg);
357
+ failed++;
358
+ } else if (warns.length > 0) {
359
+ console.log(yellow(' โš  ') + file + ` โ€” ${lines} lines` + yellow(` (${warns.map(w => w.msg).join('; ')})`));
360
+ warnings++;
361
+ } else {
362
+ console.log(green(' โœ” ') + file + ` โ€” ${lines} lines`);
363
+ passed++;
254
364
  }
255
-
256
- if (warn) { console.log(yellow(' โš  ') + file + ` โ€” ${warn}`); warnings++; }
257
- else { console.log(green(' โœ” ') + file + ` โ€” ${lines} lines`); passed++; }
258
365
  }
259
366
  }
260
367
 
368
+ // โ”€โ”€โ”€ Run checks on docs/ โ”€โ”€โ”€
261
369
  if (hasDocs) {
262
370
  console.log('');
263
371
  console.log(dim(' โ”€โ”€ docs/ โ”€โ”€'));
264
372
  console.log('');
265
373
 
374
+ // Check for non-lowercase filenames in docs/
375
+ try {
376
+ const actualFiles = fs.readdirSync(docsDir).filter(f => f.endsWith('.md'));
377
+ for (const f of actualFiles) {
378
+ if (f !== 'README.md' && f !== f.toLowerCase()) {
379
+ console.log(yellow(' โš  ') + f + yellow(' โ€” should be lowercase (' + f.toLowerCase() + ')'));
380
+ warnings++;
381
+ }
382
+ }
383
+ } catch {}
384
+
266
385
  for (const file of DOCS_FILES) {
267
386
  const fp = path.join(docsDir, file);
268
387
  if (!exists(fp)) { console.log(yellow(' โš  ') + file + ' โ€” missing'); warnings++; continue; }
388
+
269
389
  const content = fs.readFileSync(fp, 'utf-8').trim();
270
390
  const lines = content.split('\n').length;
271
- if (lines < 5) { console.log(yellow(' โš  ') + file + ` โ€” looks empty (${lines} lines)`); warnings++; }
272
- else { console.log(green(' โœ” ') + file + ` โ€” ${lines} lines`); passed++; }
391
+
392
+ const allIssues = checkQuality(content, file);
393
+ const fails = allIssues.filter(i => i.level === 'fail');
394
+ const warns = allIssues.filter(i => i.level === 'warn');
395
+
396
+ if (fails.length > 0) {
397
+ console.log(red(' โœ– ') + file + ' โ€” ' + fails[0].msg);
398
+ failed++;
399
+ } else if (warns.length > 0) {
400
+ console.log(yellow(' โš  ') + file + ` โ€” ${lines} lines` + yellow(` (${warns.map(w => w.msg).join('; ')})`));
401
+ warnings++;
402
+ } else {
403
+ console.log(green(' โœ” ') + file + ` โ€” ${lines} lines`);
404
+ passed++;
405
+ }
273
406
  }
274
407
  }
275
408
 
409
+ // โ”€โ”€โ”€ Cross-check: vibe_version staleness โ”€โ”€โ”€
410
+ const stateFile = path.join(vibeDir, 'state.json');
411
+ if (hasVibe && exists(stateFile)) {
412
+ try {
413
+ const state = JSON.parse(fs.readFileSync(stateFile, 'utf-8'));
414
+ if (state.vibe_version && state.vibe_version !== CLI_VERSION) {
415
+ const major = (v) => (v || '').split('.')[0];
416
+ if (major(state.vibe_version) !== major(CLI_VERSION)) {
417
+ console.log('');
418
+ console.log(yellow(' โš  ') + `vibe_version in state.json is ${state.vibe_version}, CLI is ${CLI_VERSION}`);
419
+ warnings++;
420
+ }
421
+ }
422
+ } catch {}
423
+ }
424
+
425
+ // โ”€โ”€โ”€ Summary โ”€โ”€โ”€
276
426
  console.log('');
277
427
  let result = '';
278
428
  if (failed > 0) result = red(`${failed} failed`);
@@ -361,6 +511,488 @@ function cmdReset(targetDir) {
361
511
  cmdAll(targetDir);
362
512
  }
363
513
 
514
+ function copyDirRecursive(src, dest) {
515
+ if (!exists(src)) return 0;
516
+ fs.mkdirSync(dest, { recursive: true });
517
+ let count = 0;
518
+ for (const item of fs.readdirSync(src)) {
519
+ const srcPath = path.join(src, item);
520
+ const destPath = path.join(dest, item);
521
+ const stat = fs.statSync(srcPath);
522
+ if (stat.isDirectory()) {
523
+ count += copyDirRecursive(srcPath, destPath);
524
+ } else {
525
+ if (item === 'VIBE_GUIDE.md') continue;
526
+ fs.writeFileSync(destPath, fs.readFileSync(srcPath));
527
+ count++;
528
+ }
529
+ }
530
+ return count;
531
+ }
532
+
533
+ function cmdExport(targetDir, hubPathArg) {
534
+ targetDir = path.resolve(targetDir);
535
+ let hubPath = hubPathArg;
536
+ if (!hubPath && exists(HUB_CONFIG)) {
537
+ hubPath = fs.readFileSync(HUB_CONFIG, 'utf-8').trim();
538
+ }
539
+ if (!hubPath) {
540
+ console.log(red('\n โœ– No hub path specified.'));
541
+ console.log('');
542
+ console.log(' First time? Set your hub path:');
543
+ console.log(cyan(' vibes hub C:\\Users\\You\\Documents\\VibeHub'));
544
+ console.log('');
545
+ console.log(' Then run:');
546
+ console.log(cyan(' vibes export'));
547
+ console.log('');
548
+ process.exit(1);
549
+ }
550
+
551
+ hubPath = path.resolve(hubPath);
552
+ const name = getProjectName(targetDir);
553
+ const projectHub = path.join(hubPath, name);
554
+
555
+ console.log('');
556
+ console.log(bold(' ๐Ÿ“ค vibes export'));
557
+ console.log(dim(` Exporting ${cyan(name)} โ†’ ${hubPath}`));
558
+ console.log('');
559
+
560
+ let total = 0;
561
+
562
+ const vibeDir = path.join(targetDir, VIBE_DIR);
563
+ if (exists(vibeDir)) {
564
+ const count = copyDirRecursive(vibeDir, path.join(projectHub, VIBE_DIR));
565
+ console.log(green(' โœ” ') + `.vibe/ โ€” ${count} files`);
566
+ total += count;
567
+ } else {
568
+ console.log(yellow(' โš  ') + '.vibe/ not found, skipping');
569
+ }
570
+
571
+ const docsDir = path.join(targetDir, DOCS_DIR);
572
+ if (exists(docsDir)) {
573
+ const count = copyDirRecursive(docsDir, path.join(projectHub, DOCS_DIR));
574
+ console.log(green(' โœ” ') + `docs/ โ€” ${count} files`);
575
+ total += count;
576
+ } else {
577
+ console.log(yellow(' โš  ') + 'docs/ not found, skipping');
578
+ }
579
+
580
+ const meta = { project: name, exported_at: now(), source: targetDir };
581
+ fs.writeFileSync(path.join(projectHub, 'export.json'), JSON.stringify(meta, null, 2), 'utf-8');
582
+ total++;
583
+
584
+ console.log('');
585
+ console.log(green(` โœ” Exported ${total} files to ${projectHub}`));
586
+
587
+ generateHubIndex(hubPath);
588
+
589
+ console.log('');
590
+ console.log(dim(' Commit and push your hub repo to sync across machines:'));
591
+ console.log(cyan(` cd "${hubPath}" && git add -A && git commit -m "export ${name}" && git push`));
592
+ console.log('');
593
+ }
594
+
595
+ function generateHubIndex(hubPath) {
596
+ const projects = [];
597
+ for (const dir of fs.readdirSync(hubPath)) {
598
+ const dirPath = path.join(hubPath, dir);
599
+ if (!fs.statSync(dirPath).isDirectory()) continue;
600
+ if (dir.startsWith('.') || dir.startsWith('_')) continue;
601
+
602
+ const exportFile = path.join(dirPath, 'export.json');
603
+ const vibeDir = path.join(dirPath, VIBE_DIR);
604
+ const docsDir = path.join(dirPath, DOCS_DIR);
605
+
606
+ let exportedAt = 'unknown';
607
+ if (exists(exportFile)) {
608
+ try {
609
+ const meta = JSON.parse(fs.readFileSync(exportFile, 'utf-8'));
610
+ exportedAt = meta.exported_at ? meta.exported_at.split('T')[0] : 'unknown';
611
+ } catch {}
612
+ }
613
+
614
+ let vibeFilled = 0, vibeNA = 0, vibeTotal = 0;
615
+ if (exists(vibeDir)) {
616
+ for (const f of VIBE_FILES) {
617
+ const fp = path.join(vibeDir, f);
618
+ if (!exists(fp)) continue;
619
+ vibeTotal++;
620
+ const content = fs.readFileSync(fp, 'utf-8').trim();
621
+ if (content.match(/^N\/A/m)) vibeNA++;
622
+ else if (content.split('\n').length >= 10) vibeFilled++;
623
+ }
624
+ }
625
+
626
+ let docsFilled = 0;
627
+ if (exists(docsDir)) {
628
+ for (const f of DOCS_FILES) {
629
+ const fp = path.join(docsDir, f);
630
+ if (!exists(fp)) continue;
631
+ if (fs.readFileSync(fp, 'utf-8').trim().split('\n').length >= 10) docsFilled++;
632
+ }
633
+ }
634
+
635
+ let purpose = '';
636
+ const purposeFile = path.join(vibeDir, 'purpose.md');
637
+ if (exists(purposeFile)) {
638
+ for (const line of fs.readFileSync(purposeFile, 'utf-8').split('\n')) {
639
+ const t = line.trim();
640
+ if (t && !t.startsWith('#') && !t.startsWith('<!--') && !t.startsWith('-->') && !t.startsWith('[')) {
641
+ purpose = t.substring(0, 80);
642
+ break;
643
+ }
644
+ }
645
+ }
646
+
647
+ projects.push({ name: dir, exportedAt, vibeFilled, vibeNA, vibeTotal, docsFilled, purpose });
648
+ }
649
+
650
+ if (projects.length === 0) return;
651
+
652
+ let md = `# Vibes Hub\n\n`;
653
+ md += `> Auto-generated index. Last updated: ${now().split('T')[0]}\n\n`;
654
+ md += `| Project | .vibe/ | docs/ | Last Export | Purpose |\n`;
655
+ md += `|---|---|---|---|---|\n`;
656
+ for (const p of projects.sort((a, b) => a.name.localeCompare(b.name))) {
657
+ const vs = p.vibeTotal > 0 ? `${p.vibeFilled}/${p.vibeTotal}` + (p.vibeNA > 0 ? ` (${p.vibeNA} N/A)` : '') : 'โ€”';
658
+ const ds = p.docsFilled > 0 ? `${p.docsFilled}/${DOCS_FILES.length}` : 'โ€”';
659
+ md += `| [${p.name}](./${p.name}/) | ${vs} | ${ds} | ${p.exportedAt} | ${p.purpose} |\n`;
660
+ }
661
+ md += `\n---\n\n**${projects.length} projects** tracked.\n`;
662
+
663
+ fs.writeFileSync(path.join(hubPath, 'index.md'), md, 'utf-8');
664
+ console.log(green(' โœ” ') + 'Updated hub index.md');
665
+ }
666
+
667
+ function cmdHub(hubPathArg) {
668
+ if (!hubPathArg) {
669
+ if (exists(HUB_CONFIG)) {
670
+ const saved = fs.readFileSync(HUB_CONFIG, 'utf-8').trim();
671
+ console.log(`\n Hub path: ${cyan(saved)}\n`);
672
+ } else {
673
+ console.log(yellow('\n No hub configured.'));
674
+ console.log(dim(' Set one with: vibes hub <path>\n'));
675
+ }
676
+ return;
677
+ }
678
+
679
+ const resolved = path.resolve(hubPathArg);
680
+ const isNew = !exists(resolved) || fs.readdirSync(resolved).length === 0;
681
+
682
+ // Create directory
683
+ fs.mkdirSync(resolved, { recursive: true });
684
+
685
+ console.log('');
686
+ console.log(bold(' ๐Ÿ—‚๏ธ vibes hub setup'));
687
+ console.log('');
688
+
689
+ if (isNew) {
690
+ // Initialize git
691
+ const { execSync } = require('child_process');
692
+ try {
693
+ execSync('git init', { cwd: resolved, stdio: 'ignore' });
694
+ console.log(green(' โœ” ') + 'Created directory & initialized git');
695
+ } catch {
696
+ console.log(green(' โœ” ') + 'Created directory');
697
+ console.log(yellow(' โš  ') + 'Could not git init (git not found?)');
698
+ }
699
+
700
+ // Create README
701
+ const readme = [
702
+ '# Vibes Hub',
703
+ '',
704
+ 'Central collection of `.vibe/` and `docs/` files from all my projects.',
705
+ '',
706
+ '## How this works',
707
+ '',
708
+ '1. Run `vibes export` from any project directory',
709
+ '2. Commit and push: `git add -A && git commit -m "update" && git push`',
710
+ '3. Pull on another machine: `git pull`',
711
+ '',
712
+ '## Projects',
713
+ '',
714
+ 'See [index.md](index.md) for auto-generated dashboard.',
715
+ '',
716
+ '## Cross-Project Intelligence',
717
+ '',
718
+ 'The `_insights/` folder is where AI analyzes patterns across all your projects.',
719
+ '',
720
+ '- **[ANALYZE.md](_insights/ANALYZE.md)** โ€” Copy-paste prompt for your AI agent',
721
+ '- **[patterns.md](_insights/patterns.md)** โ€” Architecture, security, and tech patterns',
722
+ '- **[standards.md](_insights/standards.md)** โ€” Universal rules for all projects',
723
+ '- **[opportunities.md](_insights/opportunities.md)** โ€” What one project can learn from another',
724
+ '',
725
+ 'Export 3+ projects, then run the analysis prompt to find cross-project improvements.',
726
+ '',
727
+ ].join('\n');
728
+ fs.writeFileSync(path.join(resolved, 'README.md'), readme, 'utf-8');
729
+ console.log(green(' โœ” ') + 'Created README.md');
730
+
731
+ // Create .gitignore
732
+ fs.writeFileSync(path.join(resolved, '.gitignore'), 'node_modules/\n.DS_Store\nThumbs.db\n', 'utf-8');
733
+ console.log(green(' โœ” ') + 'Created .gitignore');
734
+
735
+ // Create _insights/ intelligence layer
736
+ const insightsDir = path.join(resolved, '_insights');
737
+ fs.mkdirSync(insightsDir, { recursive: true });
738
+
739
+ // Analysis prompt โ€” the AI reads this to know what to do
740
+ const analyze = [
741
+ '# Hub Analysis Prompt',
742
+ '',
743
+ '> Copy-paste this to your AI agent after exporting 3+ projects.',
744
+ '',
745
+ '---',
746
+ '',
747
+ '```',
748
+ 'I have a Vibes Hub โ€” a central collection of .vibe/ and docs/ files',
749
+ 'from all my projects. Read every project folder in this hub and:',
750
+ '',
751
+ '1. CROSS-PROJECT PATTERNS',
752
+ ' - What architectural patterns appear across multiple projects?',
753
+ ' - What security approaches are used? Are they consistent?',
754
+ ' - What tech stack choices are shared? Where do they diverge?',
755
+ ' - What naming conventions or coding standards are consistent?',
756
+ '',
757
+ '2. BEST PRACTICES TO REPLICATE',
758
+ ' - Which project has the best documentation quality?',
759
+ ' - Which project solved a problem that others still have?',
760
+ ' - What did one project do exceptionally well that could set',
761
+ ' the standard for all projects?',
762
+ ' - Are there security, testing, or deployment practices from',
763
+ ' one project that others are missing?',
764
+ '',
765
+ '3. TRANSFERABLE IMPROVEMENTS',
766
+ ' - For each project, list specific improvements it could adopt',
767
+ ' from another project. Be concrete: "Project A could adopt',
768
+ ' Project B\'s approach to X because Y."',
769
+ ' - Identify shared problems that could have a shared solution.',
770
+ '',
771
+ '4. RISKS AND GAPS',
772
+ ' - Which projects have risks that could affect other projects?',
773
+ ' - Are there shared dependencies that create portfolio risk?',
774
+ ' - Which projects have missing or weak documentation areas?',
775
+ '',
776
+ '5. UNIVERSAL STANDARDS',
777
+ ' - Based on what works best across projects, propose standards',
778
+ ' that should apply to ALL projects going forward.',
779
+ ' - These could be file size limits, security practices, naming',
780
+ ' conventions, documentation requirements, etc.',
781
+ '',
782
+ 'Write your findings into the _insights/ folder:',
783
+ '- patterns.md โ€” Cross-project patterns',
784
+ '- standards.md โ€” Proposed universal standards',
785
+ '- opportunities.md โ€” Specific transferable improvements',
786
+ '',
787
+ 'Be specific. Reference actual project names and files.',
788
+ '```',
789
+ '',
790
+ ].join('\n');
791
+ fs.writeFileSync(path.join(insightsDir, 'ANALYZE.md'), analyze, 'utf-8');
792
+
793
+ // Patterns skeleton
794
+ const patterns = [
795
+ '# Cross-Project Patterns',
796
+ '',
797
+ '> Auto-populated by AI analysis. Run the prompt in ANALYZE.md.',
798
+ '',
799
+ '## Architecture Patterns',
800
+ '',
801
+ '<!-- What architectural approaches appear across multiple projects? -->',
802
+ '',
803
+ '## Security Patterns',
804
+ '',
805
+ '<!-- How do projects handle auth, encryption, secrets? -->',
806
+ '',
807
+ '## Shared Tech Stack',
808
+ '',
809
+ '<!-- Languages, frameworks, databases used across projects -->',
810
+ '',
811
+ '| Technology | Used In | Notes |',
812
+ '|---|---|---|',
813
+ '| | | |',
814
+ '',
815
+ '## Common Anti-Patterns',
816
+ '',
817
+ '<!-- Mistakes or bad patterns that appear in multiple projects -->',
818
+ '',
819
+ ].join('\n');
820
+ fs.writeFileSync(path.join(insightsDir, 'patterns.md'), patterns, 'utf-8');
821
+
822
+ // Standards skeleton
823
+ const standards = [
824
+ '# Universal Standards',
825
+ '',
826
+ '> Standards that should apply to ALL projects, based on what works best.',
827
+ '',
828
+ '## Code Standards',
829
+ '',
830
+ '<!-- File size limits, naming conventions, banned patterns -->',
831
+ '',
832
+ '## Documentation Standards',
833
+ '',
834
+ '<!-- What every project must document, minimum quality bar -->',
835
+ '',
836
+ '## Security Standards',
837
+ '',
838
+ '<!-- Auth patterns, encryption requirements, secrets management -->',
839
+ '',
840
+ '## AI Agent Standards',
841
+ '',
842
+ '<!-- Rules every AI agent must follow across all projects -->',
843
+ '',
844
+ ].join('\n');
845
+ fs.writeFileSync(path.join(insightsDir, 'standards.md'), standards, 'utf-8');
846
+
847
+ // Opportunities skeleton
848
+ const opportunities = [
849
+ '# Transferable Improvements',
850
+ '',
851
+ '> Specific things one project does well that others should adopt.',
852
+ '',
853
+ '## [Source Project] โ†’ [Target Project]',
854
+ '',
855
+ '**What:** [Specific practice or solution]',
856
+ '',
857
+ '**Why:** [Why the target project would benefit]',
858
+ '',
859
+ '**How:** [What the target project needs to change]',
860
+ '',
861
+ '---',
862
+ '',
863
+ '<!-- Copy the template above for each transferable improvement. -->',
864
+ '',
865
+ ].join('\n');
866
+ fs.writeFileSync(path.join(insightsDir, 'opportunities.md'), opportunities, 'utf-8');
867
+
868
+ console.log(green(' โœ” ') + 'Created _insights/ (analysis prompt + templates)');
869
+ } else {
870
+ console.log(green(' โœ” ') + 'Directory already exists');
871
+ }
872
+
873
+ // Save config
874
+ fs.writeFileSync(HUB_CONFIG, resolved, 'utf-8');
875
+ console.log(green(' โœ” ') + `Hub path saved: ${cyan(resolved)}`);
876
+
877
+ console.log('');
878
+ console.log(bold(' Next โ€” connect to GitHub:'));
879
+ console.log('');
880
+ console.log(dim(' 1. Go to ') + cyan('github.com/new'));
881
+ console.log(dim(' Name: ') + bold('VibeHub') + dim(' (or whatever you want)'));
882
+ console.log(dim(' Visibility: ') + bold('Private'));
883
+ console.log(dim(' Click "Create repository"'));
884
+ console.log('');
885
+ console.log(dim(' 2. Run these commands:'));
886
+ console.log('');
887
+ console.log(cyan(` cd "${resolved}"`));
888
+ console.log(cyan(' git remote add origin git@github.com:YOUR_USERNAME/VibeHub.git'));
889
+ console.log(cyan(' git add -A && git commit -m "init hub" && git push -u origin main'));
890
+ console.log('');
891
+ console.log(dim(' After that, exporting from any project is just:'));
892
+ console.log('');
893
+ console.log(cyan(' vibes export'));
894
+ console.log(cyan(` cd "${resolved}" && git add -A && git commit -m "update" && git push`));
895
+ console.log('');
896
+ console.log(dim(' On another computer, clone the repo and run:'));
897
+ console.log(cyan(' vibes hub <path-to-cloned-repo>'));
898
+ console.log('');
899
+ }
900
+
901
+ function cmdInsights(targetDir) {
902
+ if (!exists(HUB_CONFIG)) {
903
+ console.log(red('\n โœ– No hub configured. Run "vibes hub <path>" first.\n'));
904
+ process.exit(1);
905
+ }
906
+
907
+ const hubPath = fs.readFileSync(HUB_CONFIG, 'utf-8').trim();
908
+ const insightsDir = path.join(hubPath, '_insights');
909
+
910
+ if (!exists(insightsDir)) {
911
+ console.log(red('\n โœ– No _insights/ folder in hub. Run the analysis prompt first.\n'));
912
+ process.exit(1);
913
+ }
914
+
915
+ const name = getProjectName(targetDir);
916
+ const nameL = name.toLowerCase();
917
+
918
+ console.log('');
919
+ console.log(bold(` ๐Ÿ’ก vibes insights โ€” ${name}`));
920
+
921
+ let totalHits = 0;
922
+
923
+ // โ”€โ”€โ”€ Scan opportunities.md โ”€โ”€โ”€
924
+ const oppFile = path.join(insightsDir, 'opportunities.md');
925
+ if (exists(oppFile)) {
926
+ const content = fs.readFileSync(oppFile, 'utf-8');
927
+ // Split by ## headers
928
+ const sections = content.split(/^## /gm).filter(s => s.trim());
929
+ const relevant = sections.filter(s => s.toLowerCase().includes(nameL));
930
+
931
+ if (relevant.length > 0) {
932
+ console.log('');
933
+ console.log(dim(' โ”€โ”€ Opportunities โ”€โ”€'));
934
+ console.log('');
935
+ for (const section of relevant) {
936
+ const title = section.split('\n')[0].trim();
937
+ const whatMatch = section.match(/\*\*What:\*\*\s*(.+)/i);
938
+ const what = whatMatch ? whatMatch[1].trim() : '';
939
+ console.log(cyan(' โšก ') + title);
940
+ if (what) console.log(dim(' ') + what);
941
+ totalHits++;
942
+ }
943
+ }
944
+ }
945
+
946
+ // โ”€โ”€โ”€ Scan standards.md โ”€โ”€โ”€
947
+ const stdFile = path.join(insightsDir, 'standards.md');
948
+ if (exists(stdFile)) {
949
+ const content = fs.readFileSync(stdFile, 'utf-8');
950
+ const bullets = content.match(/^\d+\.\s+\*\*.+\*\*/gm) || [];
951
+
952
+ if (bullets.length > 0) {
953
+ console.log('');
954
+ console.log(dim(' โ”€โ”€ Universal Standards โ”€โ”€'));
955
+ console.log('');
956
+ for (const bullet of bullets) {
957
+ const clean = bullet.replace(/^\d+\.\s+/, '').replace(/\*\*/g, '');
958
+ console.log(dim(' โ—† ') + clean);
959
+ totalHits++;
960
+ }
961
+ }
962
+ }
963
+
964
+ // โ”€โ”€โ”€ Scan patterns.md for anti-patterns mentioning this project โ”€โ”€โ”€
965
+ const patFile = path.join(insightsDir, 'patterns.md');
966
+ if (exists(patFile)) {
967
+ const content = fs.readFileSync(patFile, 'utf-8');
968
+ const antiSection = content.split('## Common Anti-Patterns')[1];
969
+ if (antiSection) {
970
+ const items = antiSection.split(/^\d+\./gm).filter(s => s.trim());
971
+ const relevant = items.filter(s => s.toLowerCase().includes(nameL));
972
+
973
+ if (relevant.length > 0) {
974
+ console.log('');
975
+ console.log(dim(' โ”€โ”€ Anti-Patterns โ”€โ”€'));
976
+ console.log('');
977
+ for (const item of relevant) {
978
+ const title = item.match(/\*\*(.+?)\*\*/)?.[1] || item.split('\n')[0].trim();
979
+ console.log(yellow(' โš  ') + title);
980
+ totalHits++;
981
+ }
982
+ }
983
+ }
984
+ }
985
+
986
+ console.log('');
987
+ if (totalHits === 0) {
988
+ console.log(dim(' No specific insights found for this project.'));
989
+ console.log(dim(' Export 3+ projects, then run the analysis prompt in _insights/ANALYZE.md.'));
990
+ } else {
991
+ console.log(` ${totalHits} insights found. Review _insights/ in your hub for full details.`);
992
+ }
993
+ console.log('');
994
+ }
995
+
364
996
  function printNextSteps() {
365
997
  console.log('');
366
998
  console.log(bold(' What to do next:'));
@@ -381,13 +1013,19 @@ function printHelp() {
381
1013
  console.log('');
382
1014
  console.log(' ' + bold('Usage:'));
383
1015
  console.log('');
384
- console.log(' vibes init Create .vibe/ with the full suite (15 files + guide)');
385
- console.log(' vibes docs Create docs/ operational documentation (11 files)');
386
- console.log(' vibes all Create both .vibe/ and docs/ at once');
387
- console.log(' vibes check Validate all files for completeness');
388
- console.log(' vibes status Quick health dashboard');
389
- console.log(' vibes reset Delete and recreate everything');
390
- console.log(' vibes help Show this help message');
1016
+ console.log(' vibes init Create .vibe/ with the full suite (15 files + guide)');
1017
+ console.log(' vibes docs Create docs/ operational documentation (11 files)');
1018
+ console.log(' vibes all Create both .vibe/ and docs/ at once');
1019
+ console.log(' vibes check Validate all files for completeness');
1020
+ console.log(' vibes status Quick health dashboard');
1021
+ console.log(' vibes reset Delete and recreate everything');
1022
+ console.log(' vibes insights Show hub feedback for this project');
1023
+ console.log('');
1024
+ console.log(' ' + bold('Hub (cross-project sync):'));
1025
+ console.log('');
1026
+ console.log(' vibes hub <path> Set your central hub directory (saved globally)');
1027
+ console.log(' vibes export Export .vibe/ + docs/ to your hub');
1028
+ console.log(' vibes export <path> Export to a specific hub path');
391
1029
  console.log('');
392
1030
  console.log(' ' + bold('.vibe/') + dim(' โ€” Project memory'));
393
1031
  console.log(dim(' Core: purpose ยท architecture ยท flows ยท entities ยท decisions ยท state'));
@@ -420,6 +1058,9 @@ switch (command) {
420
1058
  case 'check': cmdCheck(targetDir); break;
421
1059
  case 'status': cmdStatus(targetDir); break;
422
1060
  case 'reset': cmdReset(targetDir); break;
1061
+ case 'export': cmdExport('.', args[1]); break;
1062
+ case 'hub': cmdHub(args[1]); break;
1063
+ case 'insights': cmdInsights(targetDir); break;
423
1064
  case 'help': case '--help': case '-h': printHelp(); break;
424
1065
  default:
425
1066
  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.0.0",
3
+ "version": "3.2.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",
@@ -818,6 +818,57 @@ Never put API keys, passwords, internal URLs, or customer data in `.vibe/` files
818
818
 
819
819
  ---
820
820
 
821
+ ## Beyond One Project โ€” The Vibes Hub
822
+
823
+ Everything above describes how to create a `.vibe/` layer for **one** project. But the real power emerges when you connect multiple projects together.
824
+
825
+ ### The Problem
826
+
827
+ You're building 5 projects. One has great authentication. Another has a clever caching strategy. A third has a security hole that the first project already solved. But you'd never know โ€” because each project is an island.
828
+
829
+ ### The Solution: Hub + Insights
830
+
831
+ The `vibe-me` CLI includes a **hub** โ€” a central folder (synced via Git) that collects the `.vibe/` and `docs/` from all your projects. After exporting 3+ projects, an AI analyzes them all and generates cross-project intelligence:
832
+
833
+ ```bash
834
+ # One-time: create a hub
835
+ vibes hub ~/Documents/VibeHub
836
+
837
+ # From any project: export to the hub
838
+ vibes export
839
+
840
+ # From any project: see what the hub knows about you
841
+ vibes insights
842
+ ```
843
+
844
+ The AI analysis produces three files in `_insights/`:
845
+
846
+ - **patterns.md** โ€” Architecture and security patterns shared across projects
847
+ - **standards.md** โ€” Universal rules that should apply everywhere (file size limits, schema validation, token auth, etc.)
848
+ - **opportunities.md** โ€” Specific transfers: "Project A does X well โ†’ Project B should adopt it because Y"
849
+
850
+ Then `vibes insights` reads those files and shows you **only what applies to the project you're standing in** โ€” opportunities targeted at you, standards you might be missing, and anti-patterns you share with other projects.
851
+
852
+ ### Why This Matters
853
+
854
+ Without a hub, your best ideas are trapped in one project. With a hub:
855
+ - Security patterns from one project become standards for all of them
856
+ - A caching strategy you invented in one app gets recommended to another
857
+ - An anti-pattern detected in one project gets flagged everywhere it appears
858
+ - Your portfolio gets smarter every time you export
859
+
860
+ ### The Workflow
861
+
862
+ ```
863
+ Scaffold (.vibe/) โ†’ Fill out โ†’ Validate (vibes check) โ†’ Export to hub โ†’ AI analysis โ†’ vibes insights
864
+ โ†‘ โ”‚
865
+ โ””โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€ Apply feedback, improve โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”€โ”˜
866
+ ```
867
+
868
+ This is not just documentation. It's a **feedback loop across your entire portfolio**.
869
+
870
+ ---
871
+
821
872
  ## FAQ
822
873
 
823
874
  **Q: What if the project is tiny (one file, 200 lines)?**
@@ -1,4 +1,5 @@
1
1
  {
2
+ "project": "PROJECT_NAME",
2
3
  "version": "0.1.0",
3
4
  "health": "active",
4
5
  "status": "prototype",
@@ -7,6 +8,6 @@
7
8
  "known_issues": [],
8
9
  "dependencies": [],
9
10
  "constraints": [],
10
- "vibe_version": "1.0",
11
+ "vibe_version": "3.2.0",
11
12
  "vibe_updated": "TIMESTAMP"
12
13
  }