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 +153 -28
- package/bin/vibes.js +678 -37
- package/package.json +1 -1
- package/spec/VIBE_GUIDE.md +51 -0
- package/templates/state.json +2 -1
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
# ๐ vibes
|
|
4
4
|
|
|
5
|
-
###
|
|
5
|
+
### Project memory. Cross-project intelligence.<br>One command per project. One hub for all of them.
|
|
6
6
|
|
|
7
7
|
[](https://www.npmjs.com/package/vibe-me)
|
|
8
8
|
[](LICENSE)
|
|
@@ -11,16 +11,19 @@
|
|
|
11
11
|
|
|
12
12
|
<br>
|
|
13
13
|
|
|
14
|
-
**
|
|
14
|
+
**Scaffold. Fill. Validate. Sync. Get feedback.**
|
|
15
15
|
|
|
16
|
-
`vibes`
|
|
17
|
-
|
|
18
|
-
|
|
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
|
-
```
|
|
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
|
|
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
|
-
>
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
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
|
-
|
|
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
|
-
//
|
|
224
|
-
if (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
|
-
|
|
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
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
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
|
-
|
|
272
|
-
|
|
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
|
|
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(' vibes
|
|
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.
|
|
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",
|
package/spec/VIBE_GUIDE.md
CHANGED
|
@@ -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)?**
|
package/templates/state.json
CHANGED
|
@@ -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": "
|
|
11
|
+
"vibe_version": "3.2.0",
|
|
11
12
|
"vibe_updated": "TIMESTAMP"
|
|
12
13
|
}
|