ai-vibes 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +345 -0
- package/dist/cli.js +325 -0
- package/package.json +44 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 ai-vibes
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,345 @@
|
|
|
1
|
+
# ai-vibes
|
|
2
|
+
|
|
3
|
+
> A tiny CLI that scaffolds AI steering rules for your codebase.
|
|
4
|
+
|
|
5
|
+
**The problem:** Your AI assistant writes amazing code... that looks nothing like the rest of your codebase. 🤦
|
|
6
|
+
|
|
7
|
+
**The solution:** Give your AI the vibe check it deserves. Define your team's standards once, and let every AI assistant follow them.
|
|
8
|
+
|
|
9
|
+
Think of it as `.editorconfig` but for AI behavior. Or a style guide that AIs actually read.
|
|
10
|
+
|
|
11
|
+
## Install
|
|
12
|
+
|
|
13
|
+
```bash
|
|
14
|
+
# No installation needed - use npx
|
|
15
|
+
npx ai-vibes init
|
|
16
|
+
|
|
17
|
+
# Or install globally
|
|
18
|
+
npm install -g ai-vibes
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Requirements:** Node.js 18+
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
# Interactive mode - prompts you for directory and manifest names
|
|
27
|
+
npx ai-vibes init
|
|
28
|
+
|
|
29
|
+
# Or skip the prompts with flags
|
|
30
|
+
npx ai-vibes init --dir vibes --manifest vibes.yaml
|
|
31
|
+
|
|
32
|
+
# Minimal mode - 3 essential files (recommended)
|
|
33
|
+
npx ai-vibes init --minimal
|
|
34
|
+
|
|
35
|
+
# Overwrite existing files
|
|
36
|
+
npx ai-vibes init --force
|
|
37
|
+
```
|
|
38
|
+
|
|
39
|
+
This creates:
|
|
40
|
+
|
|
41
|
+
- `vibes.yaml` - Manifest file
|
|
42
|
+
- `vibes/` - Directory with rule documents
|
|
43
|
+
|
|
44
|
+
## What You Get
|
|
45
|
+
|
|
46
|
+
### Minimal Mode (3 files)
|
|
47
|
+
|
|
48
|
+
- `security.md` - Never commit secrets, validate inputs
|
|
49
|
+
- `unit-tests.md` - Test behavior, cover edge cases
|
|
50
|
+
- `naming.md` - Consistent naming conventions
|
|
51
|
+
|
|
52
|
+
### Full Mode (11 files)
|
|
53
|
+
|
|
54
|
+
Everything in minimal, plus:
|
|
55
|
+
|
|
56
|
+
- `architecture.md` - Follow existing patterns
|
|
57
|
+
- `performance.md` - Optimization guidelines
|
|
58
|
+
- `code-review.md` - Review standards
|
|
59
|
+
- `error-handling.md` - Error patterns
|
|
60
|
+
- `documentation.md` - Doc standards
|
|
61
|
+
- `accessibility.md` - a11y guidelines
|
|
62
|
+
- `api-design.md` - RESTful conventions
|
|
63
|
+
- `git-workflow.md` - Commit standards
|
|
64
|
+
|
|
65
|
+
## Daily Usage
|
|
66
|
+
|
|
67
|
+
### Working with AI Assistants
|
|
68
|
+
|
|
69
|
+
Once set up, reference your guidelines when using AI tools:
|
|
70
|
+
|
|
71
|
+
**GitHub Copilot:**
|
|
72
|
+
|
|
73
|
+
```
|
|
74
|
+
@workspace Follow the guidelines in vibes.yaml
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
**Cursor:**
|
|
78
|
+
|
|
79
|
+
```
|
|
80
|
+
Create an API endpoint following the guidelines in vibes.yaml
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
**Claude/ChatGPT:**
|
|
84
|
+
|
|
85
|
+
```bash
|
|
86
|
+
cat vibes.yaml vibes/*.md
|
|
87
|
+
# Paste into your conversation
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Updating Guidelines
|
|
91
|
+
|
|
92
|
+
```bash
|
|
93
|
+
# Edit a rule
|
|
94
|
+
vim vibes/security.md
|
|
95
|
+
|
|
96
|
+
# Commit changes
|
|
97
|
+
git add vibes/
|
|
98
|
+
git commit -m "docs: update security guidelines for API keys"
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## CLI Options
|
|
102
|
+
|
|
103
|
+
```
|
|
104
|
+
npx ai-vibes init [options]
|
|
105
|
+
|
|
106
|
+
Options:
|
|
107
|
+
--minimal 3 essential files instead of 11
|
|
108
|
+
--force Overwrite existing files
|
|
109
|
+
--dir <name> Directory name (interactive prompt if not provided)
|
|
110
|
+
--manifest <filename> Manifest filename (interactive prompt if not provided)
|
|
111
|
+
-h, --help Display help
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
**Interactive Mode:** If you don't provide `--dir` or `--manifest`, you'll be prompted:
|
|
115
|
+
|
|
116
|
+
```
|
|
117
|
+
Directory name for rule documents (default: vibes):
|
|
118
|
+
Manifest filename (default: vibes.yaml):
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
## What It Does
|
|
122
|
+
|
|
123
|
+
✅ Creates `vibes.yaml` manifest
|
|
124
|
+
✅ Creates rule documents in Markdown
|
|
125
|
+
✅ Provides starter templates
|
|
126
|
+
✅ Gives you a place to say "No, AI, we don't do that here"
|
|
127
|
+
|
|
128
|
+
## What It Doesn't Do
|
|
129
|
+
|
|
130
|
+
❌ Does not validate rules (it's not a linter, calm down)
|
|
131
|
+
❌ Does not enforce rules (you still need code review)
|
|
132
|
+
❌ Does not integrate with AI tools directly (BYOAI - Bring Your Own AI)
|
|
133
|
+
❌ Does not make coffee ☕
|
|
134
|
+
|
|
135
|
+
ai-vibes is intentionally minimal - it scaffolds the structure, you customize the content. Think of it as the starter pack for your AI's training montage.
|
|
136
|
+
|
|
137
|
+
## How It Works
|
|
138
|
+
|
|
139
|
+
```mermaid
|
|
140
|
+
graph TD
|
|
141
|
+
A[🚀 Run: npx ai-vibes init] --> B[📄 Creates vibes.yaml manifest]
|
|
142
|
+
B --> C[📁 Creates vibes/ directory with rule files]
|
|
143
|
+
C --> D[✏️ You customize the markdown files]
|
|
144
|
+
D --> E[🤖 Share with AI assistants]
|
|
145
|
+
E --> F[✨ AI follows your team's standards]
|
|
146
|
+
|
|
147
|
+
style A fill:#4CAF50,stroke:#2E7D32,color:#fff
|
|
148
|
+
style B fill:#2196F3,stroke:#1565C0,color:#fff
|
|
149
|
+
style C fill:#2196F3,stroke:#1565C0,color:#fff
|
|
150
|
+
style D fill:#FF9800,stroke:#E65100,color:#fff
|
|
151
|
+
style E fill:#9C27B0,stroke:#6A1B9A,color:#fff
|
|
152
|
+
style F fill:#4CAF50,stroke:#2E7D32,color:#fff
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
**The magic:** The manifest (`vibes.yaml`) acts as a single source of truth that AI tools can read to discover all your coding standards in one place.
|
|
156
|
+
|
|
157
|
+
## Setup Guide
|
|
158
|
+
|
|
159
|
+
### Step-by-Step Setup
|
|
160
|
+
|
|
161
|
+
**1. Navigate to your project**
|
|
162
|
+
|
|
163
|
+
```bash
|
|
164
|
+
cd /path/to/your-project
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
**2. Run ai-vibes**
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
npx ai-vibes init
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
**3. Answer the prompts** (or press Enter for defaults)
|
|
174
|
+
|
|
175
|
+
```
|
|
176
|
+
Directory name for rule documents (default: vibes):
|
|
177
|
+
Manifest filename (default: vibes.yaml):
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
**4. Choose your mode**
|
|
181
|
+
|
|
182
|
+
- Want all 11 templates? Just press Enter
|
|
183
|
+
- Want minimal (3 files)? Run with `--minimal` flag
|
|
184
|
+
|
|
185
|
+
**5. Verify the files were created**
|
|
186
|
+
|
|
187
|
+
```bash
|
|
188
|
+
ls vibes/ # Shows your rule files
|
|
189
|
+
cat vibes.yaml # Shows your manifest
|
|
190
|
+
```
|
|
191
|
+
|
|
192
|
+
**6. Customize for your team**
|
|
193
|
+
|
|
194
|
+
Edit the generated files to match your standards:
|
|
195
|
+
|
|
196
|
+
```bash
|
|
197
|
+
# Add your security requirements
|
|
198
|
+
vim vibes/security.md
|
|
199
|
+
|
|
200
|
+
# Define your naming conventions
|
|
201
|
+
vim vibes/naming.md
|
|
202
|
+
|
|
203
|
+
# Set testing expectations
|
|
204
|
+
vim vibes/unit-tests.md
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
**7. Commit to version control**
|
|
208
|
+
|
|
209
|
+
```bash
|
|
210
|
+
git add vibes.yaml vibes/
|
|
211
|
+
git commit -m "docs: add AI coding guidelines"
|
|
212
|
+
git push
|
|
213
|
+
```
|
|
214
|
+
|
|
215
|
+
**8. Share with your AI assistant**
|
|
216
|
+
|
|
217
|
+
Now whenever you use an AI tool, reference your guidelines:
|
|
218
|
+
|
|
219
|
+
```
|
|
220
|
+
@workspace Follow the guidelines in vibes.yaml
|
|
221
|
+
```
|
|
222
|
+
|
|
223
|
+
**9. Keep it updated**
|
|
224
|
+
|
|
225
|
+
As your team's practices evolve, update the markdown files:
|
|
226
|
+
|
|
227
|
+
```bash
|
|
228
|
+
vim vibes/security.md # Add new security pattern
|
|
229
|
+
git commit -am "docs: add OAuth2 security requirements"
|
|
230
|
+
```
|
|
231
|
+
|
|
232
|
+
### What Gets Created
|
|
233
|
+
|
|
234
|
+
**Minimal Mode** (`--minimal`):
|
|
235
|
+
|
|
236
|
+
```
|
|
237
|
+
your-project/
|
|
238
|
+
├── vibes.yaml # Manifest with 3 rules
|
|
239
|
+
└── vibes/
|
|
240
|
+
├── security.md # Security guidelines
|
|
241
|
+
├── unit-tests.md # Testing standards
|
|
242
|
+
└── naming.md # Naming conventions
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
**Full Mode** (default):
|
|
246
|
+
|
|
247
|
+
```
|
|
248
|
+
your-project/
|
|
249
|
+
├── vibes.yaml # Manifest with 11 rules
|
|
250
|
+
└── vibes/
|
|
251
|
+
├── security.md # Security guidelines
|
|
252
|
+
├── unit-tests.md # Testing standards
|
|
253
|
+
├── naming.md # Naming conventions
|
|
254
|
+
├── architecture.md # Architecture patterns
|
|
255
|
+
├── performance.md # Performance guidelines
|
|
256
|
+
├── code-review.md # Review standards
|
|
257
|
+
├── error-handling.md # Error patterns
|
|
258
|
+
├── documentation.md # Documentation standards
|
|
259
|
+
├── accessibility.md # Accessibility guidelines
|
|
260
|
+
├── api-design.md # API conventions
|
|
261
|
+
└── git-workflow.md # Git standards
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Automation
|
|
265
|
+
|
|
266
|
+
Skip the prompts for CI/CD or scripts:
|
|
267
|
+
|
|
268
|
+
```bash
|
|
269
|
+
npx ai-vibes init --dir .github/ai-rules --manifest ai-rules.yaml --minimal --force
|
|
270
|
+
```
|
|
271
|
+
|
|
272
|
+
## Manifest Format
|
|
273
|
+
|
|
274
|
+
`vibes.yaml` maps rule IDs to file paths:
|
|
275
|
+
|
|
276
|
+
```yaml
|
|
277
|
+
version: 1
|
|
278
|
+
|
|
279
|
+
rules:
|
|
280
|
+
security: "vibes/security.md"
|
|
281
|
+
unit-tests: "vibes/unit-tests.md"
|
|
282
|
+
naming: "vibes/naming.md"
|
|
283
|
+
|
|
284
|
+
order:
|
|
285
|
+
- security
|
|
286
|
+
- unit-tests
|
|
287
|
+
- naming
|
|
288
|
+
|
|
289
|
+
modes:
|
|
290
|
+
codegen:
|
|
291
|
+
include: [security, unit-tests, naming]
|
|
292
|
+
review:
|
|
293
|
+
include: [security, naming]
|
|
294
|
+
```
|
|
295
|
+
|
|
296
|
+
## Examples
|
|
297
|
+
|
|
298
|
+
### Basic Setup
|
|
299
|
+
|
|
300
|
+
```bash
|
|
301
|
+
npx ai-vibes init --minimal
|
|
302
|
+
```
|
|
303
|
+
|
|
304
|
+
### Monorepo
|
|
305
|
+
|
|
306
|
+
```bash
|
|
307
|
+
cd packages/api
|
|
308
|
+
npx ai-vibes init --dir ../../.ai-rules --manifest ../../ai-rules.yaml
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
### Custom Location
|
|
312
|
+
|
|
313
|
+
```bash
|
|
314
|
+
npx ai-vibes init --dir .github/ai-guidelines
|
|
315
|
+
```
|
|
316
|
+
|
|
317
|
+
## FAQ
|
|
318
|
+
|
|
319
|
+
**Do I need to install this?**
|
|
320
|
+
No, use `npx ai-vibes init`. One command, zero commitment. Like a coding one-night stand, but productive.
|
|
321
|
+
|
|
322
|
+
**Can I customize the templates?**
|
|
323
|
+
Yes! That's literally the entire point. These are starting points, not commandments carved in stone.
|
|
324
|
+
|
|
325
|
+
**Can I add more rules?**
|
|
326
|
+
Absolutely. Got opinions about how semicolons should be used? Write a rule. Passionate about proper emoji usage in commit messages? Add it to the manifest.
|
|
327
|
+
|
|
328
|
+
**Should I commit these files?**
|
|
329
|
+
Yes! Version control means time travel, and future-you will thank past-you for documenting these decisions.
|
|
330
|
+
|
|
331
|
+
**Does this work with [my AI tool]?**
|
|
332
|
+
If your AI can read Markdown (spoiler: they all can), then yes. We're tool-agnostic. Bring your favorite AI, we'll provide the guidelines.
|
|
333
|
+
|
|
334
|
+
## Contributing
|
|
335
|
+
|
|
336
|
+
Contributions welcome! See [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
337
|
+
|
|
338
|
+
## License
|
|
339
|
+
|
|
340
|
+
MIT
|
|
341
|
+
|
|
342
|
+
---
|
|
343
|
+
|
|
344
|
+
**Repository:** https://github.com/HelpBits/ai-vibes
|
|
345
|
+
**Version:** 1.0.0
|
package/dist/cli.js
ADDED
|
@@ -0,0 +1,325 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
// src/cli.ts
|
|
4
|
+
import { Command } from "commander";
|
|
5
|
+
|
|
6
|
+
// src/init.ts
|
|
7
|
+
import { writeFile, mkdir, access } from "fs/promises";
|
|
8
|
+
import { join } from "path";
|
|
9
|
+
import { createInterface } from "readline";
|
|
10
|
+
|
|
11
|
+
// src/templates.ts
|
|
12
|
+
var security_md = '# Security\n\n## Purpose\n\nDefine security expectations for AI-generated code in this repo.\n\n## Do\n\n- **Never commit secrets** \u2013 no API keys, tokens, passwords in code or logs\n- **Validate all inputs** \u2013 especially user-provided data, query params, form fields\n- **Use environment variables** for secrets (e.g., `.env` files, not committed)\n- **Sanitize outputs** \u2013 prevent XSS, SQL injection, command injection\n- **Follow least privilege** \u2013 grant minimal necessary permissions\n- **Keep dependencies updated** \u2013 regularly audit and update packages\n- **Use secure defaults** \u2013 HTTPS, secure cookies, strong crypto\n- **Log security events** \u2013 failed auth attempts, suspicious activity (without logging sensitive data)\n\n## Don\'t\n\n- **Don\'t hardcode credentials** \u2013 ever\n- **Don\'t trust user input** \u2013 always validate and sanitize\n- **Don\'t use weak crypto** \u2013 no MD5, SHA1 for passwords; use bcrypt, argon2, or scrypt\n- **Don\'t expose stack traces** \u2013 in production error messages\n- **Don\'t add risky dependencies casually** \u2013 vet packages before installing\n- **Don\'t skip security headers** \u2013 CSP, HSTS, X-Frame-Options, etc.\n\n## Examples\n\n### \u2705 Good: Environment variables\n\n```javascript\nconst apiKey = process.env.API_KEY;\nif (!apiKey) {\n throw new Error("API_KEY not configured");\n}\n```\n\n### \u274C Bad: Hardcoded secret\n\n```javascript\nconst apiKey = "sk-1234567890abcdef"; // NEVER do this\n```\n\n### \u2705 Good: Input validation\n\n```javascript\nfunction getUser(userId) {\n if (!/^[0-9]+$/.test(userId)) {\n throw new Error("Invalid user ID");\n }\n return db.query("SELECT * FROM users WHERE id = ?", [userId]);\n}\n```\n\n### \u274C Bad: SQL injection risk\n\n```javascript\nfunction getUser(userId) {\n return db.query(`SELECT * FROM users WHERE id = ${userId}`); // vulnerable\n}\n```\n';
|
|
13
|
+
var unit_tests_md = '# Unit Tests\n\n## Purpose\n\nDefine how AI should write and maintain unit tests in this repo.\n\n## Do\n\n- **Update tests when behavior changes** \u2013 tests should reflect current expected behavior\n- **Cover edge cases** \u2013 empty inputs, null, undefined, boundary values\n- **Test error paths** \u2013 not just happy paths\n- **Use descriptive test names** \u2013 `it(\'should return 404 when user not found\')`\n- **Keep tests fast** \u2013 mock external dependencies (APIs, databases)\n- **Test one thing per test** \u2013 focused assertions\n- **Use test fixtures** \u2013 reusable test data\n- **Assert meaningful outputs** \u2013 not just "doesn\'t crash"\n\n## Don\'t\n\n- **Don\'t test implementation details** \u2013 test behavior, not private methods\n- **Don\'t write flaky tests** \u2013 avoid timeouts, race conditions\n- **Don\'t skip failing tests** \u2013 fix or remove them\n- **Don\'t copy-paste tests** \u2013 extract shared setup\n- **Don\'t mock everything** \u2013 test real logic when possible\n- **Don\'t ignore test failures** \u2013 they indicate real issues\n\n## Examples\n\n### \u2705 Good: Behavior-focused test\n\n```javascript\ndescribe("UserService", () => {\n it("should throw error when creating user with duplicate email", async () => {\n await createUser({ email: "test@example.com" });\n\n await expect(createUser({ email: "test@example.com" })).rejects.toThrow(\n "Email already exists",\n );\n });\n});\n```\n\n### \u2705 Good: Edge case coverage\n\n```javascript\ndescribe("calculateDiscount", () => {\n it("should return 0 for negative prices", () => {\n expect(calculateDiscount(-10)).toBe(0);\n });\n\n it("should return 0 for null price", () => {\n expect(calculateDiscount(null)).toBe(0);\n });\n\n it("should handle zero price", () => {\n expect(calculateDiscount(0)).toBe(0);\n });\n});\n```\n\n### \u274C Bad: Testing implementation details\n\n```javascript\nit("should call internal _parseData method", () => {\n const spy = jest.spyOn(service, "_parseData");\n service.process(data);\n expect(spy).toHaveBeenCalled(); // testing internals, not behavior\n});\n```\n';
|
|
14
|
+
var naming_md = '# Naming Conventions\n\n## Purpose\n\nDefine naming standards for consistency across the codebase.\n\n## Do\n\n- **Use descriptive names** \u2013 `getUserById` not `get`\n- **Follow language conventions** \u2013 camelCase for JS/TS, snake_case for Python\n- **Be consistent** \u2013 if the repo uses `fetch*`, don\'t add `get*` functions\n- **Use plural for collections** \u2013 `users`, `items`\n- **Use verb-noun for functions** \u2013 `createUser`, `deletePost`\n- **Use clear boolean names** \u2013 `isActive`, `hasPermission`, `canEdit`\n- **Avoid abbreviations** \u2013 unless widely known (HTTP, API, ID)\n- **Match domain language** \u2013 use terms from the product/business\n\n## Don\'t\n\n- **Don\'t be cryptic** \u2013 `a`, `tmp`, `data2`\n- **Don\'t use misleading names** \u2013 `get*` should not modify state\n- **Don\'t mix conventions** \u2013 pick one and stick with it\n- **Don\'t use generic names** \u2013 `handler`, `manager`, `helper` without context\n- **Don\'t overuse prefixes** \u2013 `myFunction`, `theVariable`\n\n## Examples\n\n### \u2705 Good: Clear function names\n\n```javascript\nfunction createUser(userData) {}\nfunction deletePost(postId) {}\nfunction isAuthenticated(user) {}\n```\n\n### \u274C Bad: Vague names\n\n```javascript\nfunction process(d) {} // what does this do?\nfunction handle() {} // handle what?\nfunction doStuff() {} // too generic\n```\n\n### \u2705 Good: Descriptive variables\n\n```javascript\nconst activeUsers = users.filter((u) => u.isActive);\nconst maxRetries = 3;\nconst apiBaseUrl = "https://api.example.com";\n```\n\n### \u274C Bad: Unclear variables\n\n```javascript\nconst arr = users.filter((u) => u.isActive); // what kind of array?\nconst num = 3; // what number?\nconst url = "https://api.example.com"; // which URL?\n```\n\n### \u2705 Good: Boolean naming\n\n```javascript\nconst isLoading = true;\nconst hasPermission = user.role === "admin";\nconst canDelete = isOwner || isAdmin;\n```\n\n### \u274C Bad: Unclear booleans\n\n```javascript\nconst loading = true; // could be a loading indicator element\nconst permission = true; // which permission?\nconst delete = true; // "delete" is ambiguous\n```\n';
|
|
15
|
+
var architecture_md = "# Architecture\n\n## Purpose\n\nDefine architectural patterns and organization principles for this repo.\n\n## Do\n\n- **Follow existing patterns** \u2013 if the repo has a structure, maintain it\n- **Put code in the right place** \u2013 controllers, services, models, utils\n- **Reuse existing utilities** \u2013 check before creating new helper functions\n- **Keep modules focused** \u2013 single responsibility principle\n- **Minimize dependencies** \u2013 between modules\n- **Document architecture decisions** \u2013 why, not just what\n- **Use dependency injection** \u2013 for testability\n- **Separate concerns** \u2013 business logic, data access, presentation\n\n## Don't\n\n- **Don't create new patterns** \u2013 without justification or discussion\n- **Don't duplicate functionality** \u2013 search for existing solutions first\n- **Don't tightly couple modules** \u2013 avoid circular dependencies\n- **Don't mix layers** \u2013 keep business logic out of controllers\n- **Don't bypass abstractions** \u2013 use the data layer, don't query directly\n- **Don't ignore folder structure** \u2013 respect the repo's organization\n\n## Examples\n\n### \u2705 Good: Layered architecture\n\n```javascript\n// controller/userController.js\nexport async function getUser(req, res) {\n const user = await userService.findById(req.params.id);\n res.json(user);\n}\n\n// service/userService.js\nexport async function findById(id) {\n return userRepository.findById(id);\n}\n\n// repository/userRepository.js\nexport async function findById(id) {\n return db.query(\"SELECT * FROM users WHERE id = ?\", [id]);\n}\n```\n\n### \u274C Bad: Mixed concerns\n\n```javascript\n// controller/userController.js\nexport async function getUser(req, res) {\n // Don't put database queries directly in controllers\n const user = await db.query(\"SELECT * FROM users WHERE id = ?\", [\n req.params.id,\n ]);\n res.json(user);\n}\n```\n\n### \u2705 Good: Reusing utilities\n\n```javascript\nimport { formatDate } from \"../utils/date\";\nimport { validateEmail } from \"../utils/validation\";\n\nconst formattedDate = formatDate(user.createdAt);\n```\n\n### \u274C Bad: Duplicating utilities\n\n```javascript\n// Creating new date formatting when one exists\nfunction myDateFormatter(date) {\n // duplicates existing formatDate utility\n}\n```\n";
|
|
16
|
+
var performance_md = "# Performance\n\n## Purpose\n\nDefine performance expectations and optimization guidelines for AI-generated code.\n\n## Do\n\n- **Measure before optimizing** \u2013 use profilers and benchmarks\n- **Use appropriate data structures** \u2013 Map for lookups, Set for uniqueness, Array for ordered data\n- **Cache expensive operations** \u2013 memoize, cache API calls, precompute when possible\n- **Avoid unnecessary re-renders** \u2013 React.memo, useMemo, useCallback where appropriate\n- **Lazy load when possible** \u2013 code splitting, dynamic imports, lazy components\n- **Optimize loops** \u2013 avoid nested loops with large datasets, use early returns\n- **Use efficient algorithms** \u2013 consider Big O complexity\n- **Debounce/throttle** \u2013 for frequent events (scroll, resize, input)\n- **Optimize images** \u2013 compress, use appropriate formats, lazy load\n- **Monitor bundle size** \u2013 keep dependencies lean\n\n## Don't\n\n- **Don't premature optimize** \u2013 optimize hot paths only after measuring\n- **Don't block the main thread** \u2013 use Web Workers for heavy computation\n- **Don't fetch in loops** \u2013 batch API calls, use Promise.all\n- **Don't ignore memory leaks** \u2013 clean up event listeners, timers, subscriptions\n- **Don't load everything upfront** \u2013 use pagination, infinite scroll, virtualization\n- **Don't ignore Core Web Vitals** \u2013 LCP, FID, CLS matter for UX\n\n## Examples\n\n### \u2705 Good: Memoization\n\n```javascript\nconst cache = new Map();\n\nfunction expensiveOperation(key) {\n if (cache.has(key)) return cache.get(key);\n\n const result = /* expensive computation */;\n cache.set(key, result);\n return result;\n}\n```\n\n### \u274C Bad: Repeated computation\n\n```javascript\nfunction expensiveOperation(key) {\n return /* expensive computation every time */;\n}\n```\n\n### \u2705 Good: Batch API calls\n\n```javascript\nconst userIds = [1, 2, 3, 4, 5];\nconst users = await fetchUsers(userIds); // Single request\n```\n\n### \u274C Bad: N+1 queries\n\n```javascript\nconst users = [];\nfor (const id of userIds) {\n users.push(await fetchUser(id)); // 5 separate requests!\n}\n```\n";
|
|
17
|
+
var code_review_md = "# Code Review\n\n## Purpose\n\nDefine standards for code reviews and what to look for when reviewing AI-generated code.\n\n## Do\n\n- **Check for security issues** \u2013 SQL injection, XSS, hardcoded secrets\n- **Verify error handling** \u2013 proper try/catch, error messages, fallbacks\n- **Review test coverage** \u2013 edge cases covered, meaningful assertions\n- **Check naming consistency** \u2013 follows repo conventions\n- **Verify documentation** \u2013 complex logic explained, JSDoc/comments where needed\n- **Look for duplication** \u2013 reusable code extracted to functions/modules\n- **Check dependencies** \u2013 necessary, up-to-date, no suspicious packages\n- **Verify accessibility** \u2013 semantic HTML, ARIA labels, keyboard navigation\n- **Check performance** \u2013 no obvious bottlenecks, efficient algorithms\n- **Review git history** \u2013 clear commit messages, logical commits\n\n## Don't\n\n- **Don't approve blindly** \u2013 actually read and understand the changes\n- **Don't nitpick style** \u2013 let linters/formatters handle it\n- **Don't block on preferences** \u2013 focus on correctness and maintainability\n- **Don't merge with unresolved comments** \u2013 address or explicitly defer\n- **Don't skip testing** \u2013 actually run the code locally\n- **Don't assume AI is always right** \u2013 LLMs make mistakes\n\n## Examples\n\n### \u2705 Good: Constructive feedback\n\n```\nThis function could throw if userId is undefined.\nConsider adding validation:\n if (!userId) throw new Error('userId required');\n```\n\n### \u274C Bad: Vague criticism\n\n```\nThis doesn't look right\n```\n\n### \u2705 Good: Security catch\n\n```\n\u26A0\uFE0F SQL injection risk here - use parameterized queries:\n db.query('SELECT * FROM users WHERE id = ?', [userId])\n```\n";
|
|
18
|
+
var error_handling_md = "# Error Handling\n\n## Purpose\n\nDefine how errors should be handled and reported in AI-generated code.\n\n## Do\n\n- **Use try/catch for async operations** \u2013 especially API calls, file I/O\n- **Throw meaningful errors** \u2013 descriptive messages, include context\n- **Use custom error classes** \u2013 for domain-specific errors\n- **Log errors properly** \u2013 include stack traces, context, but not secrets\n- **Handle errors at appropriate level** \u2013 don't catch too early\n- **Provide fallbacks** \u2013 graceful degradation when possible\n- **Validate inputs early** \u2013 fail fast with clear errors\n- **Use error boundaries** \u2013 in React/UI frameworks\n- **Return error objects** \u2013 instead of throwing in some cases (Result pattern)\n- **Document error cases** \u2013 what exceptions can be thrown\n\n## Don't\n\n- **Don't swallow errors** \u2013 empty catch blocks hide bugs\n- **Don't expose internals** \u2013 no stack traces in production to users\n- **Don't use errors for control flow** \u2013 errors are for exceptional cases\n- **Don't log secrets** \u2013 no passwords, tokens, PII in error logs\n- **Don't ignore promise rejections** \u2013 always handle with .catch or try/catch\n- **Don't use generic messages** \u2013 \"Something went wrong\" isn't helpful\n\n## Examples\n\n### \u2705 Good: Specific error with context\n\n```javascript\nasync function getUser(userId) {\n if (!userId) {\n throw new Error(\"userId is required\");\n }\n\n try {\n return await api.fetchUser(userId);\n } catch (error) {\n throw new Error(`Failed to fetch user ${userId}: ${error.message}`);\n }\n}\n```\n\n### \u274C Bad: Swallowed error\n\n```javascript\nasync function getUser(userId) {\n try {\n return await api.fetchUser(userId);\n } catch (error) {\n // Silent failure - bug is hidden!\n }\n}\n```\n\n### \u2705 Good: Error boundary (React)\n\n```javascript\nclass ErrorBoundary extends React.Component {\n state = { hasError: false };\n\n static getDerivedStateFromError(error) {\n return { hasError: true };\n }\n\n componentDidCatch(error, info) {\n logError(error, info);\n }\n\n render() {\n if (this.state.hasError) {\n return <ErrorFallback />;\n }\n return this.props.children;\n }\n}\n```\n";
|
|
19
|
+
var documentation_md = "# Documentation\n\n## Purpose\n\nDefine documentation standards for AI-generated code.\n\n## Do\n\n- **Document the \"why\" not the \"what\"** \u2013 code shows what, comments explain why\n- **Add JSDoc for public APIs** \u2013 parameters, return types, examples\n- **Document complex logic** \u2013 algorithms, business rules, non-obvious code\n- **Keep README updated** \u2013 installation, usage, examples\n- **Add inline comments** \u2013 for tricky parts, workarounds, TODOs\n- **Document breaking changes** \u2013 in CHANGELOG or commit messages\n- **Include examples** \u2013 especially for libraries and utilities\n- **Document edge cases** \u2013 what assumptions are made\n- **Link to related docs** \u2013 design docs, tickets, RFCs\n- **Keep docs close to code** \u2013 avoid docs getting stale\n\n## Don't\n\n- **Don't state the obvious** \u2013 `// increment i` is useless\n- **Don't let docs drift** \u2013 update docs when code changes\n- **Don't over-document** \u2013 clear code needs fewer comments\n- **Don't write novels** \u2013 be concise and clear\n- **Don't document implementation details** \u2013 unless necessary\n- **Don't use outdated examples** \u2013 test your examples\n\n## Examples\n\n### \u2705 Good: Explains \"why\"\n\n```javascript\n// Using setTimeout instead of setInterval because the API call\n// can take longer than 5s, which would cause requests to queue up\nsetTimeout(() => pollAPI(), 5000);\n```\n\n### \u274C Bad: States the obvious\n\n```javascript\n// Call the API\ncallAPI();\n```\n\n### \u2705 Good: JSDoc with examples\n\n```javascript\n/**\n * Formats a date for display in the UI\n * @param {Date} date - The date to format\n * @param {string} locale - The locale (e.g., 'en-US', 'es-ES')\n * @returns {string} Formatted date string\n * @example\n * formatDate(new Date(), 'en-US') // \"Jan 1, 2024\"\n */\nfunction formatDate(date, locale) {\n return new Intl.DateTimeFormat(locale).format(date);\n}\n```\n\n### \u2705 Good: Complex logic explanation\n\n```javascript\n// We're using a binary search here because the data is sorted\n// and can be very large (100k+ items). Linear search would be O(n).\n// This gives us O(log n) performance.\nfunction binarySearch(arr, target) {\n // ...\n}\n```\n";
|
|
20
|
+
var accessibility_md = '# Accessibility (a11y)\n\n## Purpose\n\nDefine accessibility standards for AI-generated UI code to ensure inclusivity.\n\n## Do\n\n- **Use semantic HTML** \u2013 `<button>`, `<nav>`, `<main>`, not just `<div>`\n- **Add ARIA labels** \u2013 when semantic HTML isn\'t enough\n- **Ensure keyboard navigation** \u2013 tab order, focus management, shortcuts\n- **Provide alt text** \u2013 for all images (empty alt="" for decorative)\n- **Use sufficient color contrast** \u2013 WCAG AA minimum (4.5:1 for text)\n- **Make clickable areas large** \u2013 44x44px minimum touch targets\n- **Support screen readers** \u2013 test with VoiceOver, NVDA, JAWS\n- **Add skip links** \u2013 "Skip to main content" for keyboard users\n- **Use labels for inputs** \u2013 properly associated with `for`/`id`\n- **Test with keyboard only** \u2013 tab, enter, escape, arrows\n\n## Don\'t\n\n- **Don\'t rely on color alone** \u2013 use icons, text, patterns too\n- **Don\'t use `<div>` for buttons** \u2013 use `<button>` or proper ARIA\n- **Don\'t auto-play media** \u2013 provide controls, respect prefers-reduced-motion\n- **Don\'t break zoom** \u2013 avoid `maximum-scale=1` in viewport\n- **Don\'t use placeholder as label** \u2013 placeholders disappear on input\n- **Don\'t forget focus styles** \u2013 never `outline: none` without alternative\n- **Don\'t hide content** \u2013 that should be available to screen readers\n\n## Examples\n\n### \u2705 Good: Semantic HTML\n\n```jsx\n<button onClick={handleClick}>Delete</button>\n```\n\n### \u274C Bad: Div as button\n\n```jsx\n<div onClick={handleClick}>Delete</div>\n```\n\n### \u2705 Good: Proper label\n\n```jsx\n<label htmlFor="email">Email</label>\n<input id="email" type="email" />\n```\n\n### \u274C Bad: Placeholder as label\n\n```jsx\n<input type="email" placeholder="Email" />\n```\n\n### \u2705 Good: Icon with label\n\n```jsx\n<button aria-label="Close dialog">\n <XIcon />\n</button>\n```\n\n### \u2705 Good: Keyboard support\n\n```javascript\nfunction handleKeyDown(e) {\n if (e.key === "Escape") {\n closeModal();\n }\n if (e.key === "Enter" || e.key === " ") {\n handleAction();\n }\n}\n```\n';
|
|
21
|
+
var api_design_md = '# API Design\n\n## Purpose\n\nDefine standards for designing APIs (REST, GraphQL, internal modules).\n\n## Do\n\n- **Use RESTful conventions** \u2013 GET (read), POST (create), PUT/PATCH (update), DELETE\n- **Version your API** \u2013 /v1/, /v2/, or header-based versioning\n- **Use plural nouns** \u2013 `/users`, `/posts`, not `/user`, `/post`\n- **Return proper HTTP status codes** \u2013 200 success, 201 created, 400 bad request, 404 not found, 500 error\n- **Include pagination** \u2013 for list endpoints (limit, offset or cursor)\n- **Validate inputs** \u2013 return 400 with clear error messages\n- **Use consistent naming** \u2013 camelCase or snake_case, pick one\n- **Include timestamps** \u2013 createdAt, updatedAt for resources\n- **Document your API** \u2013 OpenAPI/Swagger specs\n- **Handle errors gracefully** \u2013 return error objects with code and message\n\n## Don\'t\n\n- **Don\'t use verbs in URLs** \u2013 use HTTP methods instead\n- **Don\'t expose internal IDs** \u2013 use UUIDs or opaque identifiers\n- **Don\'t return different structures** \u2013 for same endpoint in different states\n- **Don\'t forget rate limiting** \u2013 protect against abuse\n- **Don\'t expose stack traces** \u2013 in production error responses\n- **Don\'t break backward compatibility** \u2013 without versioning\n- **Don\'t use GET for mutations** \u2013 GET should be idempotent\n\n## Examples\n\n### \u2705 Good: RESTful design\n\n```\nGET /v1/users # List users\nGET /v1/users/123 # Get user\nPOST /v1/users # Create user\nPATCH /v1/users/123 # Update user\nDELETE /v1/users/123 # Delete user\n```\n\n### \u274C Bad: Non-RESTful\n\n```\nGET /getUsers\nPOST /createUser\nPOST /deleteUser\n```\n\n### \u2705 Good: Error response\n\n```json\n{\n "error": {\n "code": "INVALID_EMAIL",\n "message": "Email address is not valid",\n "field": "email"\n }\n}\n```\n\n### \u274C Bad: Vague error\n\n```json\n{\n "error": "Bad request"\n}\n```\n\n### \u2705 Good: Pagination\n\n```json\n{\n "data": [...],\n "pagination": {\n "total": 1000,\n "page": 1,\n "pageSize": 20,\n "totalPages": 50\n }\n}\n```\n';
|
|
22
|
+
var git_workflow_md = "# Git Workflow\n\n## Purpose\n\nDefine Git commit and branch standards for AI-generated code changes.\n\n## Do\n\n- **Write clear commit messages** \u2013 use conventional commits format\n- **Make atomic commits** \u2013 one logical change per commit\n- **Use descriptive branch names** \u2013 feature/add-login, fix/memory-leak\n- **Keep commits small** \u2013 easier to review and revert\n- **Test before committing** \u2013 ensure code works\n- **Write commit body** \u2013 for complex changes, explain why\n- **Reference issues** \u2013 \"Fixes #123\" in commit message\n- **Squash before merge** \u2013 keep main branch clean (if team convention)\n- **Pull before push** \u2013 avoid merge conflicts\n- **Use feature branches** \u2013 never commit directly to main\n\n## Don't\n\n- **Don't commit secrets** \u2013 use .gitignore, check before push\n- **Don't commit commented code** \u2013 delete it, Git has history\n- **Don't commit build artifacts** \u2013 node_modules, dist/, .env\n- **Don't use vague messages** \u2013 \"fix bug\", \"update code\"\n- **Don't commit WIP** \u2013 to shared branches\n- **Don't rewrite public history** \u2013 no force push to main/shared branches\n- **Don't make giant commits** \u2013 breaks down to reviewable chunks\n\n## Examples\n\n### \u2705 Good: Conventional commit\n\n```\nfeat: add user authentication with JWT\n\nImplements login, logout, and token refresh endpoints.\nUses bcrypt for password hashing and validates tokens\non protected routes.\n\nFixes #45\n```\n\n### \u274C Bad: Vague message\n\n```\nupdated stuff\n```\n\n### \u2705 Good: Branch naming\n\n```\nfeature/user-authentication\nfix/login-button-spacing\nrefactor/api-client-structure\ndocs/update-readme\n```\n\n### \u274C Bad: Branch naming\n\n```\nmy-branch\ntest\nnew-stuff\n```\n\n### Conventional Commit Types\n\n- `feat:` New feature\n- `fix:` Bug fix\n- `docs:` Documentation\n- `style:` Formatting, no code change\n- `refactor:` Code restructuring\n- `test:` Adding tests\n- `chore:` Maintenance tasks\n- `perf:` Performance improvement\n\n```\n\n```\n";
|
|
23
|
+
var TEMPLATES = {
|
|
24
|
+
"security.md": security_md,
|
|
25
|
+
"unit-tests.md": unit_tests_md,
|
|
26
|
+
"naming.md": naming_md,
|
|
27
|
+
"architecture.md": architecture_md,
|
|
28
|
+
"performance.md": performance_md,
|
|
29
|
+
"code-review.md": code_review_md,
|
|
30
|
+
"error-handling.md": error_handling_md,
|
|
31
|
+
"documentation.md": documentation_md,
|
|
32
|
+
"accessibility.md": accessibility_md,
|
|
33
|
+
"api-design.md": api_design_md,
|
|
34
|
+
"git-workflow.md": git_workflow_md
|
|
35
|
+
};
|
|
36
|
+
var RULE_DEFINITIONS = [
|
|
37
|
+
{
|
|
38
|
+
"id": "security",
|
|
39
|
+
"filename": "security.md",
|
|
40
|
+
"minimal": true,
|
|
41
|
+
"modes": [
|
|
42
|
+
"codegen",
|
|
43
|
+
"review",
|
|
44
|
+
"test",
|
|
45
|
+
"docs",
|
|
46
|
+
"ops"
|
|
47
|
+
]
|
|
48
|
+
},
|
|
49
|
+
{
|
|
50
|
+
"id": "unit-tests",
|
|
51
|
+
"filename": "unit-tests.md",
|
|
52
|
+
"minimal": true,
|
|
53
|
+
"modes": [
|
|
54
|
+
"codegen",
|
|
55
|
+
"test"
|
|
56
|
+
]
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
"id": "naming",
|
|
60
|
+
"filename": "naming.md",
|
|
61
|
+
"minimal": true,
|
|
62
|
+
"modes": [
|
|
63
|
+
"codegen",
|
|
64
|
+
"review"
|
|
65
|
+
]
|
|
66
|
+
},
|
|
67
|
+
{
|
|
68
|
+
"id": "architecture",
|
|
69
|
+
"filename": "architecture.md",
|
|
70
|
+
"minimal": false,
|
|
71
|
+
"modes": [
|
|
72
|
+
"codegen",
|
|
73
|
+
"review",
|
|
74
|
+
"ops"
|
|
75
|
+
]
|
|
76
|
+
},
|
|
77
|
+
{
|
|
78
|
+
"id": "performance",
|
|
79
|
+
"filename": "performance.md",
|
|
80
|
+
"minimal": false,
|
|
81
|
+
"modes": [
|
|
82
|
+
"codegen",
|
|
83
|
+
"review"
|
|
84
|
+
]
|
|
85
|
+
},
|
|
86
|
+
{
|
|
87
|
+
"id": "code-review",
|
|
88
|
+
"filename": "code-review.md",
|
|
89
|
+
"minimal": false,
|
|
90
|
+
"modes": [
|
|
91
|
+
"review"
|
|
92
|
+
]
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"id": "error-handling",
|
|
96
|
+
"filename": "error-handling.md",
|
|
97
|
+
"minimal": false,
|
|
98
|
+
"modes": [
|
|
99
|
+
"codegen",
|
|
100
|
+
"review"
|
|
101
|
+
]
|
|
102
|
+
},
|
|
103
|
+
{
|
|
104
|
+
"id": "documentation",
|
|
105
|
+
"filename": "documentation.md",
|
|
106
|
+
"minimal": false,
|
|
107
|
+
"modes": [
|
|
108
|
+
"codegen",
|
|
109
|
+
"docs"
|
|
110
|
+
]
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"id": "accessibility",
|
|
114
|
+
"filename": "accessibility.md",
|
|
115
|
+
"minimal": false,
|
|
116
|
+
"modes": [
|
|
117
|
+
"codegen",
|
|
118
|
+
"review"
|
|
119
|
+
]
|
|
120
|
+
},
|
|
121
|
+
{
|
|
122
|
+
"id": "api-design",
|
|
123
|
+
"filename": "api-design.md",
|
|
124
|
+
"minimal": false,
|
|
125
|
+
"modes": [
|
|
126
|
+
"codegen",
|
|
127
|
+
"review",
|
|
128
|
+
"ops"
|
|
129
|
+
]
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
"id": "git-workflow",
|
|
133
|
+
"filename": "git-workflow.md",
|
|
134
|
+
"minimal": false,
|
|
135
|
+
"modes": [
|
|
136
|
+
"docs",
|
|
137
|
+
"ops"
|
|
138
|
+
]
|
|
139
|
+
}
|
|
140
|
+
];
|
|
141
|
+
function loadTemplate(filename) {
|
|
142
|
+
const template = TEMPLATES[filename];
|
|
143
|
+
if (!template) {
|
|
144
|
+
throw new Error(`Template not found: ${filename}`);
|
|
145
|
+
}
|
|
146
|
+
return template;
|
|
147
|
+
}
|
|
148
|
+
function generateManifest(options) {
|
|
149
|
+
const { dir, minimal } = options;
|
|
150
|
+
const activeRules = RULE_DEFINITIONS.filter(
|
|
151
|
+
(rule) => minimal ? rule.minimal : true
|
|
152
|
+
);
|
|
153
|
+
const rules = activeRules.map((rule) => ` ${rule.id}: "${dir}/${rule.filename}"`).join("\n");
|
|
154
|
+
const order = activeRules.map((rule) => ` - ${rule.id}`).join("\n");
|
|
155
|
+
const modeNames = ["codegen", "review", "test", "docs", "ops"];
|
|
156
|
+
const modesSection = modeNames.map((modeName) => {
|
|
157
|
+
const rulesForMode = activeRules.filter((rule) => rule.modes.includes(modeName)).map((rule) => rule.id);
|
|
158
|
+
return ` ${modeName}:
|
|
159
|
+
include: [${rulesForMode.join(", ")}]`;
|
|
160
|
+
}).join("\n");
|
|
161
|
+
return `version: 1
|
|
162
|
+
|
|
163
|
+
about:
|
|
164
|
+
name: "Repo Vibes"
|
|
165
|
+
description: "How AIs should work in this repo"
|
|
166
|
+
|
|
167
|
+
rules:
|
|
168
|
+
${rules}
|
|
169
|
+
|
|
170
|
+
order:
|
|
171
|
+
${order}
|
|
172
|
+
|
|
173
|
+
modes:
|
|
174
|
+
${modesSection}
|
|
175
|
+
`;
|
|
176
|
+
}
|
|
177
|
+
function getRuleTemplates(minimal) {
|
|
178
|
+
return RULE_DEFINITIONS.filter((rule) => minimal ? rule.minimal : true).map((rule) => ({
|
|
179
|
+
filename: rule.filename,
|
|
180
|
+
content: loadTemplate(rule.filename)
|
|
181
|
+
}));
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
// src/init.ts
|
|
185
|
+
function prompt(question, defaultValue) {
|
|
186
|
+
const rl = createInterface({
|
|
187
|
+
input: process.stdin,
|
|
188
|
+
output: process.stdout
|
|
189
|
+
});
|
|
190
|
+
return new Promise((resolve) => {
|
|
191
|
+
rl.question(`${question} (default: ${defaultValue}): `, (answer) => {
|
|
192
|
+
rl.close();
|
|
193
|
+
resolve(answer.trim() || defaultValue);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
function validateOptions(options) {
|
|
198
|
+
const { dir, manifest } = options;
|
|
199
|
+
if (!dir || dir.trim() === "") {
|
|
200
|
+
throw new Error("Directory name cannot be empty");
|
|
201
|
+
}
|
|
202
|
+
if (dir.includes("..") || dir.startsWith("/") || dir.startsWith("\\")) {
|
|
203
|
+
throw new Error("Directory name cannot contain path traversal patterns or be absolute");
|
|
204
|
+
}
|
|
205
|
+
const invalidDirChars = /[<>:"|?*\x00-\x1F]/;
|
|
206
|
+
if (invalidDirChars.test(dir)) {
|
|
207
|
+
throw new Error("Directory name contains invalid characters");
|
|
208
|
+
}
|
|
209
|
+
if (!manifest || manifest.trim() === "") {
|
|
210
|
+
throw new Error("Manifest filename cannot be empty");
|
|
211
|
+
}
|
|
212
|
+
if (!manifest.endsWith(".yaml") && !manifest.endsWith(".yml")) {
|
|
213
|
+
throw new Error("Manifest filename must end with .yaml or .yml");
|
|
214
|
+
}
|
|
215
|
+
if (manifest.includes("/") || manifest.includes("\\")) {
|
|
216
|
+
throw new Error("Manifest filename cannot contain path separators");
|
|
217
|
+
}
|
|
218
|
+
const invalidFileChars = /[<>:"|?*\x00-\x1F]/;
|
|
219
|
+
if (invalidFileChars.test(manifest)) {
|
|
220
|
+
throw new Error("Manifest filename contains invalid characters");
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
async function init(options) {
|
|
224
|
+
const { force, minimal } = options;
|
|
225
|
+
const dir = options.dir || await prompt("Directory name for rule documents", "vibes");
|
|
226
|
+
const manifest = options.manifest || await prompt("Manifest filename", "vibes.yaml");
|
|
227
|
+
validateOptions({ force, minimal, dir, manifest });
|
|
228
|
+
const cwd = process.cwd();
|
|
229
|
+
const results = [];
|
|
230
|
+
const manifestPath = join(cwd, manifest);
|
|
231
|
+
const manifestExists = await fileExists(manifestPath);
|
|
232
|
+
if (!manifestExists || force) {
|
|
233
|
+
try {
|
|
234
|
+
const manifestContent = generateManifest({ dir, minimal });
|
|
235
|
+
await writeFile(manifestPath, manifestContent, "utf-8");
|
|
236
|
+
results.push({
|
|
237
|
+
path: manifest,
|
|
238
|
+
status: manifestExists ? "overwritten" : "created"
|
|
239
|
+
});
|
|
240
|
+
} catch (error) {
|
|
241
|
+
throw new Error(`Failed to write manifest file: ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
242
|
+
}
|
|
243
|
+
} else {
|
|
244
|
+
results.push({
|
|
245
|
+
path: manifest,
|
|
246
|
+
status: "skipped"
|
|
247
|
+
});
|
|
248
|
+
}
|
|
249
|
+
try {
|
|
250
|
+
const rulesDir2 = join(cwd, dir);
|
|
251
|
+
await mkdir(rulesDir2, { recursive: true });
|
|
252
|
+
} catch (error) {
|
|
253
|
+
throw new Error(`Failed to create directory '${dir}': ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
254
|
+
}
|
|
255
|
+
const rulesDir = join(cwd, dir);
|
|
256
|
+
const templates = getRuleTemplates(minimal);
|
|
257
|
+
for (const template of templates) {
|
|
258
|
+
const filePath = join(rulesDir, template.filename);
|
|
259
|
+
const fileAlreadyExists = await fileExists(filePath);
|
|
260
|
+
if (!fileAlreadyExists || force) {
|
|
261
|
+
try {
|
|
262
|
+
await writeFile(filePath, template.content, "utf-8");
|
|
263
|
+
results.push({
|
|
264
|
+
path: join(dir, template.filename),
|
|
265
|
+
status: fileAlreadyExists ? "overwritten" : "created"
|
|
266
|
+
});
|
|
267
|
+
} catch (error) {
|
|
268
|
+
throw new Error(`Failed to write rule file '${template.filename}': ${error instanceof Error ? error.message : "Unknown error"}`);
|
|
269
|
+
}
|
|
270
|
+
} else {
|
|
271
|
+
results.push({
|
|
272
|
+
path: join(dir, template.filename),
|
|
273
|
+
status: "skipped"
|
|
274
|
+
});
|
|
275
|
+
}
|
|
276
|
+
}
|
|
277
|
+
printSummary(results);
|
|
278
|
+
if (results.some((r) => r.status === "created" || r.status === "overwritten")) {
|
|
279
|
+
console.log("\nIf this saves your team time, consider sponsoring the project:");
|
|
280
|
+
console.log("https://github.com/sponsors/ai-vibes");
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
async function fileExists(path) {
|
|
284
|
+
try {
|
|
285
|
+
await access(path);
|
|
286
|
+
return true;
|
|
287
|
+
} catch {
|
|
288
|
+
return false;
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
function printSummary(results) {
|
|
292
|
+
const created = results.filter((r) => r.status === "created");
|
|
293
|
+
const skipped = results.filter((r) => r.status === "skipped");
|
|
294
|
+
const overwritten = results.filter((r) => r.status === "overwritten");
|
|
295
|
+
if (created.length > 0) {
|
|
296
|
+
console.log("\n\u2713 Created:");
|
|
297
|
+
created.forEach((r) => console.log(` - ${r.path}`));
|
|
298
|
+
}
|
|
299
|
+
if (overwritten.length > 0) {
|
|
300
|
+
console.log("\n\u2713 Overwritten:");
|
|
301
|
+
overwritten.forEach((r) => console.log(` - ${r.path}`));
|
|
302
|
+
}
|
|
303
|
+
if (skipped.length > 0) {
|
|
304
|
+
console.log("\n\u25CB Skipped (already exists):");
|
|
305
|
+
skipped.forEach((r) => console.log(` - ${r.path}`));
|
|
306
|
+
}
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
// src/cli.ts
|
|
310
|
+
var program = new Command();
|
|
311
|
+
program.name("ai-vibes").description("Initialize a standard, repo-local AI steering manifest and rule documents").version("1.0.0");
|
|
312
|
+
program.command("init").description("Initialize vibes.yaml and starter rule documents").option("--force", "Overwrite existing files", false).option("--minimal", "Create minimal starter set (3 essential files: security, unit-tests, naming)", false).option("--dir <name>", "Directory name for rule documents").option("--manifest <filename>", "Manifest filename").action(async (options) => {
|
|
313
|
+
try {
|
|
314
|
+
await init({
|
|
315
|
+
force: options.force,
|
|
316
|
+
minimal: options.minimal,
|
|
317
|
+
dir: options.dir,
|
|
318
|
+
manifest: options.manifest
|
|
319
|
+
});
|
|
320
|
+
} catch (error) {
|
|
321
|
+
console.error("Error initializing ai-vibes:", error);
|
|
322
|
+
process.exit(1);
|
|
323
|
+
}
|
|
324
|
+
});
|
|
325
|
+
program.parse();
|
package/package.json
ADDED
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "ai-vibes",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "A tiny CLI that initializes a standard, repo-local AI steering manifest and rule documents",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"main": "dist/cli.js",
|
|
7
|
+
"bin": {
|
|
8
|
+
"ai-vibes": "dist/cli.js"
|
|
9
|
+
},
|
|
10
|
+
"files": [
|
|
11
|
+
"dist",
|
|
12
|
+
"README.md",
|
|
13
|
+
"LICENSE"
|
|
14
|
+
],
|
|
15
|
+
"scripts": {
|
|
16
|
+
"generate": "node scripts/generate-templates.js",
|
|
17
|
+
"prebuild": "npm run generate",
|
|
18
|
+
"build": "tsup src/cli.ts --format esm --clean",
|
|
19
|
+
"dev": "tsup src/cli.ts --format esm --watch",
|
|
20
|
+
"prepublishOnly": "npm run build"
|
|
21
|
+
},
|
|
22
|
+
"keywords": [
|
|
23
|
+
"ai",
|
|
24
|
+
"llm",
|
|
25
|
+
"rules",
|
|
26
|
+
"guidelines",
|
|
27
|
+
"code-quality",
|
|
28
|
+
"conventions",
|
|
29
|
+
"cli"
|
|
30
|
+
],
|
|
31
|
+
"author": "",
|
|
32
|
+
"license": "MIT",
|
|
33
|
+
"engines": {
|
|
34
|
+
"node": ">=18.0.0"
|
|
35
|
+
},
|
|
36
|
+
"dependencies": {
|
|
37
|
+
"commander": "^12.1.0"
|
|
38
|
+
},
|
|
39
|
+
"devDependencies": {
|
|
40
|
+
"@types/node": "^20.11.0",
|
|
41
|
+
"tsup": "^8.0.1",
|
|
42
|
+
"typescript": "^5.3.3"
|
|
43
|
+
}
|
|
44
|
+
}
|