@vibedx/vibekit 0.8.7 → 0.9.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 +34 -0
- package/assets/config.yml +4 -0
- package/assets/standards/coding/default.md +15 -0
- package/assets/standards/coding/karpathy.md +58 -0
- package/assets/standards/frameworks/react.md +36 -0
- package/assets/standards/languages/node.md +34 -0
- package/assets/standards/languages/python.md +34 -0
- package/index.js +1 -1
- package/package.json +1 -1
- package/skills/vibekit/SKILL.md +54 -0
- package/src/commands/init/index.js +117 -37
- package/src/commands/init/index.test.js +38 -15
- package/src/commands/plan/index.js +283 -0
- package/src/commands/plan/index.test.js +127 -0
- package/src/commands/plan/to-ticket.js +312 -0
- package/src/commands/plan/to-ticket.test.js +149 -0
- package/src/commands/start/index.js +9 -37
- package/src/commands/swarm/index.js +375 -0
- package/src/utils/agent.js +75 -0
- package/src/utils/swarm.js +75 -0
package/README.md
CHANGED
|
@@ -126,6 +126,40 @@ vibe start TKT-001 --agent # Single ticket, current directory
|
|
|
126
126
|
vibe start TKT-001 TKT-002 -w --agent # Multiple tickets in worktrees with agents
|
|
127
127
|
```
|
|
128
128
|
|
|
129
|
+
### 📋 Plans → Tickets
|
|
130
|
+
```bash
|
|
131
|
+
# Save a Claude implementation plan to .vibe/plans/, then break it into tickets.
|
|
132
|
+
# Preview the extracted tickets (nothing is written by default)
|
|
133
|
+
vibe plan to-ticket .vibe/plans/my-feature.md
|
|
134
|
+
|
|
135
|
+
# Create the tickets once the breakdown looks right
|
|
136
|
+
vibe plan to-ticket .vibe/plans/my-feature.md --auto
|
|
137
|
+
|
|
138
|
+
# Show the extracted items without creating anything
|
|
139
|
+
vibe plan to-ticket .vibe/plans/my-feature.md --dry-run
|
|
140
|
+
```
|
|
141
|
+
|
|
142
|
+
### 🔀 Pull Requests
|
|
143
|
+
```bash
|
|
144
|
+
# Open a GitHub PR from the current ticket branch (title/body from the ticket)
|
|
145
|
+
vibe pr
|
|
146
|
+
|
|
147
|
+
# Open PRs for specific tickets
|
|
148
|
+
vibe pr TKT-003 TKT-004
|
|
149
|
+
|
|
150
|
+
# Open PRs for all worktree branches
|
|
151
|
+
vibe pr --all
|
|
152
|
+
|
|
153
|
+
# Open as a draft
|
|
154
|
+
vibe pr --draft
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### 🐝 Swarm (Parallel Agents)
|
|
158
|
+
```bash
|
|
159
|
+
# Spawn parallel Claude agents across multiple tickets in isolated worktrees
|
|
160
|
+
vibe swarm TKT-001 TKT-002 TKT-003
|
|
161
|
+
```
|
|
162
|
+
|
|
129
163
|
### 👥 Team Management
|
|
130
164
|
```bash
|
|
131
165
|
# List team members
|
package/assets/config.yml
CHANGED
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Project Guidelines
|
|
2
|
+
|
|
3
|
+
## Code Style
|
|
4
|
+
- Follow the existing codebase conventions
|
|
5
|
+
- Keep functions small and focused
|
|
6
|
+
- Use descriptive variable and function names
|
|
7
|
+
|
|
8
|
+
## Git Workflow
|
|
9
|
+
- Write clear, concise commit messages
|
|
10
|
+
- One logical change per commit
|
|
11
|
+
- Keep PRs focused and reviewable
|
|
12
|
+
|
|
13
|
+
## Testing
|
|
14
|
+
- Write tests for new functionality
|
|
15
|
+
- Ensure existing tests pass before committing
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
# CLAUDE.md
|
|
2
|
+
|
|
3
|
+
Behavioral guidelines to reduce common LLM coding mistakes. Merge with project-specific instructions as needed.
|
|
4
|
+
|
|
5
|
+
**Tradeoff:** These guidelines bias toward caution over speed. For trivial tasks, use judgment.
|
|
6
|
+
|
|
7
|
+
## 1. Think Before Coding
|
|
8
|
+
|
|
9
|
+
**Don't assume. Don't hide confusion. Surface tradeoffs.**
|
|
10
|
+
|
|
11
|
+
Before implementing:
|
|
12
|
+
- State your assumptions explicitly. If uncertain, ask.
|
|
13
|
+
- If multiple interpretations exist, present them - don't pick silently.
|
|
14
|
+
- If a simpler approach exists, say so. Push back when warranted.
|
|
15
|
+
- If something is unclear, stop. Name what's confusing. Ask.
|
|
16
|
+
|
|
17
|
+
## 2. Simplicity First
|
|
18
|
+
|
|
19
|
+
**Minimum code that solves the problem. Nothing speculative.**
|
|
20
|
+
|
|
21
|
+
- No features beyond what was asked.
|
|
22
|
+
- No abstractions for single-use code.
|
|
23
|
+
- No "flexibility" or "configurability" that wasn't requested.
|
|
24
|
+
- No error handling for impossible scenarios.
|
|
25
|
+
- If you write 200 lines and it could be 50, rewrite it.
|
|
26
|
+
|
|
27
|
+
Ask yourself: "Would a senior engineer say this is overcomplicated?" If yes, simplify.
|
|
28
|
+
|
|
29
|
+
## 3. Surgical Changes
|
|
30
|
+
|
|
31
|
+
**Touch only what you must. Clean up only your own mess.**
|
|
32
|
+
|
|
33
|
+
When editing existing code:
|
|
34
|
+
- Don't "improve" adjacent code, comments, or formatting.
|
|
35
|
+
- Don't refactor things that aren't broken.
|
|
36
|
+
- Match existing style, even if you'd do it differently.
|
|
37
|
+
- If you notice unrelated dead code, mention it - don't delete it.
|
|
38
|
+
|
|
39
|
+
When your changes create orphans:
|
|
40
|
+
- Remove imports/variables/functions that YOUR changes made unused.
|
|
41
|
+
- Don't remove pre-existing dead code unless asked.
|
|
42
|
+
|
|
43
|
+
The test: Every changed line should trace directly to the user's request.
|
|
44
|
+
|
|
45
|
+
## 4. Goal-Driven Execution
|
|
46
|
+
|
|
47
|
+
**Define success criteria. Loop until verified.**
|
|
48
|
+
|
|
49
|
+
Transform tasks into verifiable goals:
|
|
50
|
+
- "Add validation" -> "Write tests for invalid inputs, then make them pass"
|
|
51
|
+
- "Fix the bug" -> "Write a test that reproduces it, then make it pass"
|
|
52
|
+
- "Refactor X" -> "Ensure tests pass before and after"
|
|
53
|
+
|
|
54
|
+
For multi-step tasks, state a brief plan with steps and verification checkpoints.
|
|
55
|
+
|
|
56
|
+
---
|
|
57
|
+
|
|
58
|
+
**These guidelines are working if:** fewer unnecessary changes in diffs, fewer rewrites due to overcomplication, and clarifying questions come before implementation rather than after mistakes.
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
# React Project Guidelines
|
|
2
|
+
|
|
3
|
+
## Architecture
|
|
4
|
+
- Use functional components with hooks
|
|
5
|
+
- Keep components small and single-responsibility
|
|
6
|
+
- Colocate related files (component, styles, tests, types)
|
|
7
|
+
|
|
8
|
+
## State Management
|
|
9
|
+
- Use local state (useState) for component-specific state
|
|
10
|
+
- Lift state up when shared between siblings
|
|
11
|
+
- Use context sparingly — prefer prop drilling for shallow trees
|
|
12
|
+
|
|
13
|
+
## Patterns
|
|
14
|
+
- Extract custom hooks for reusable logic
|
|
15
|
+
- Use composition over inheritance
|
|
16
|
+
- Prefer controlled components for forms
|
|
17
|
+
- Memoize expensive computations with useMemo, not every render
|
|
18
|
+
|
|
19
|
+
## Styling
|
|
20
|
+
- Use CSS modules or the project's styling solution consistently
|
|
21
|
+
- Follow mobile-first responsive design
|
|
22
|
+
- Keep styles colocated with components
|
|
23
|
+
|
|
24
|
+
## Testing
|
|
25
|
+
- Test behavior, not implementation details
|
|
26
|
+
- Use React Testing Library
|
|
27
|
+
- Write integration tests for user flows, unit tests for utilities
|
|
28
|
+
|
|
29
|
+
## Performance
|
|
30
|
+
- Lazy load routes and heavy components
|
|
31
|
+
- Avoid unnecessary re-renders — profile before optimizing
|
|
32
|
+
- Use React.memo only when measured improvement exists
|
|
33
|
+
|
|
34
|
+
## Git Workflow
|
|
35
|
+
- One component/feature per PR
|
|
36
|
+
- Write clear commit messages describing the user-facing change
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Node.js Project Guidelines
|
|
2
|
+
|
|
3
|
+
## Architecture
|
|
4
|
+
- Separate concerns: routes, controllers, services, data access
|
|
5
|
+
- Use dependency injection for testability
|
|
6
|
+
- Keep middleware thin — delegate to service layer
|
|
7
|
+
|
|
8
|
+
## Error Handling
|
|
9
|
+
- Use typed errors with meaningful messages
|
|
10
|
+
- Let errors bubble up to a centralized handler
|
|
11
|
+
- Never swallow errors silently
|
|
12
|
+
- Return appropriate HTTP status codes
|
|
13
|
+
|
|
14
|
+
## Security
|
|
15
|
+
- Validate all external input at the boundary
|
|
16
|
+
- Use parameterized queries — never interpolate user input into SQL/NoSQL
|
|
17
|
+
- Keep dependencies updated; audit regularly
|
|
18
|
+
- Never log sensitive data (tokens, passwords, PII)
|
|
19
|
+
|
|
20
|
+
## Performance
|
|
21
|
+
- Use streaming for large payloads
|
|
22
|
+
- Cache expensive operations with TTLs
|
|
23
|
+
- Use connection pooling for databases
|
|
24
|
+
- Profile before optimizing — measure, don't guess
|
|
25
|
+
|
|
26
|
+
## Testing
|
|
27
|
+
- Unit test business logic in isolation
|
|
28
|
+
- Integration test API endpoints with real database
|
|
29
|
+
- Use fixtures, not mocks, for data layer tests
|
|
30
|
+
|
|
31
|
+
## Git Workflow
|
|
32
|
+
- One logical change per commit
|
|
33
|
+
- Write commit messages that explain why, not what
|
|
34
|
+
- Keep PRs reviewable — under 400 lines when possible
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
# Python Project Guidelines
|
|
2
|
+
|
|
3
|
+
## Code Style
|
|
4
|
+
- Follow PEP 8 — use a formatter (black, ruff) to enforce
|
|
5
|
+
- Use type hints for function signatures and class attributes
|
|
6
|
+
- Prefer f-strings over format() or % formatting
|
|
7
|
+
- Use pathlib over os.path for file operations
|
|
8
|
+
|
|
9
|
+
## Architecture
|
|
10
|
+
- Keep modules focused — one responsibility per module
|
|
11
|
+
- Use dataclasses or Pydantic for structured data
|
|
12
|
+
- Prefer composition over deep inheritance hierarchies
|
|
13
|
+
- Use context managers for resource management
|
|
14
|
+
|
|
15
|
+
## Error Handling
|
|
16
|
+
- Use specific exception types, not bare except
|
|
17
|
+
- Let exceptions propagate — catch only when you can handle them
|
|
18
|
+
- Use logging, not print, for operational output
|
|
19
|
+
|
|
20
|
+
## Testing
|
|
21
|
+
- Use pytest with fixtures
|
|
22
|
+
- Test behavior, not implementation
|
|
23
|
+
- Use parametrize for testing multiple inputs
|
|
24
|
+
- Aim for integration tests over excessive mocking
|
|
25
|
+
|
|
26
|
+
## Dependencies
|
|
27
|
+
- Pin versions in requirements.txt or pyproject.toml
|
|
28
|
+
- Use virtual environments — never install globally
|
|
29
|
+
- Prefer standard library when it's sufficient
|
|
30
|
+
|
|
31
|
+
## Git Workflow
|
|
32
|
+
- One logical change per commit
|
|
33
|
+
- Clear commit messages explaining the why
|
|
34
|
+
- Keep PRs focused and reviewable
|
package/index.js
CHANGED
|
@@ -24,7 +24,7 @@ const __dirname = dirname(__filename);
|
|
|
24
24
|
const AVAILABLE_COMMANDS = [
|
|
25
25
|
'init', 'new', 'close', 'list', 'get-started',
|
|
26
26
|
'start', 'link', 'unlink', 'refine', 'lint', 'review', 'team', 'skills',
|
|
27
|
-
'status', 'stats', 'pr'
|
|
27
|
+
'status', 'stats', 'plan', 'pr', 'swarm'
|
|
28
28
|
];
|
|
29
29
|
|
|
30
30
|
/**
|
package/package.json
CHANGED
package/skills/vibekit/SKILL.md
CHANGED
|
@@ -69,6 +69,8 @@ vibe init # Creates .vibe/ directory with config, team, templates
|
|
|
69
69
|
| `vibe lint` | Validate ticket format |
|
|
70
70
|
| `vibe lint --fix` | Auto-fix missing sections |
|
|
71
71
|
| `vibe refine <id>` | AI-enhance ticket details |
|
|
72
|
+
| `vibe plan to-ticket <file>` | Convert a saved plan into tickets (AI) |
|
|
73
|
+
| `vibe swarm` | Spawn parallel agents across open tickets in worktrees |
|
|
72
74
|
| `vibe team` | Manage team members |
|
|
73
75
|
|
|
74
76
|
## Creating Tickets (for AI agents)
|
|
@@ -232,6 +234,58 @@ vibe pr --draft
|
|
|
232
234
|
|
|
233
235
|
PR title and body are auto-populated from ticket content. Ticket status is set to `review`.
|
|
234
236
|
|
|
237
|
+
### Plans → Tickets
|
|
238
|
+
|
|
239
|
+
When you (Claude) produce an implementation plan, save it to `.vibe/plans/` as a
|
|
240
|
+
markdown file, then turn it into tickets:
|
|
241
|
+
|
|
242
|
+
```bash
|
|
243
|
+
# Preview the tickets vibekit would extract from a plan (no files written)
|
|
244
|
+
vibe plan to-ticket .vibe/plans/my-feature.md
|
|
245
|
+
|
|
246
|
+
# Create the tickets once the breakdown looks right
|
|
247
|
+
vibe plan to-ticket .vibe/plans/my-feature.md --auto
|
|
248
|
+
|
|
249
|
+
# Dry-run shows the extracted items without creating anything
|
|
250
|
+
vibe plan to-ticket .vibe/plans/my-feature.md --dry-run
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
The command sends the plan to Claude, which extracts discrete work items
|
|
254
|
+
(title, description, acceptance criteria, priority, estimate) and writes one
|
|
255
|
+
ticket per item to `.vibe/tickets/`. Default is preview-then-confirm: it shows
|
|
256
|
+
the breakdown and only writes files when you pass `--auto`. Chain it with
|
|
257
|
+
`vibe start TKT-XXX` to begin work.
|
|
258
|
+
|
|
259
|
+
### Swarming (Parallel Agents)
|
|
260
|
+
|
|
261
|
+
`vibe swarm` spins up multiple Claude agents at once, each in its own worktree,
|
|
262
|
+
to burn down a batch of tickets in parallel:
|
|
263
|
+
|
|
264
|
+
```bash
|
|
265
|
+
# Swarm all open tickets (capped by config swarm.maxAgents, default 3)
|
|
266
|
+
vibe swarm
|
|
267
|
+
|
|
268
|
+
# Limit the number of concurrent agents
|
|
269
|
+
vibe swarm --count 5
|
|
270
|
+
|
|
271
|
+
# Only swarm tickets matching a filter (frontmatter key:value)
|
|
272
|
+
vibe swarm --filter priority:high
|
|
273
|
+
|
|
274
|
+
# Use a specific base branch for the worktrees
|
|
275
|
+
vibe swarm --base main
|
|
276
|
+
|
|
277
|
+
# Check on a running swarm
|
|
278
|
+
vibe swarm status
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
Configure caps and timeout in `.vibe/config.yml`:
|
|
282
|
+
|
|
283
|
+
```yaml
|
|
284
|
+
swarm:
|
|
285
|
+
maxAgents: 3
|
|
286
|
+
timeout: 900 # seconds
|
|
287
|
+
```
|
|
288
|
+
|
|
235
289
|
### Monitoring Progress
|
|
236
290
|
|
|
237
291
|
```bash
|
|
@@ -3,50 +3,130 @@ import path from 'path';
|
|
|
3
3
|
import { fileURLToPath } from 'url';
|
|
4
4
|
import { dirname } from 'path';
|
|
5
5
|
import getStartedCommand from '../get-started/index.js';
|
|
6
|
+
import { arrowSelect } from '../../utils/arrow-select.js';
|
|
6
7
|
|
|
7
|
-
// ESM replacement for __dirname
|
|
8
8
|
const __filename = fileURLToPath(import.meta.url);
|
|
9
9
|
const __dirname = dirname(__filename);
|
|
10
10
|
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
11
|
+
const TEMPLATES = [
|
|
12
|
+
{ name: 'default', value: 'default', category: 'coding', description: 'Minimal project conventions' },
|
|
13
|
+
{ name: 'karpathy', value: 'karpathy', category: 'coding', description: 'Karpathy-style dev philosophy' },
|
|
14
|
+
{ name: 'react', value: 'react', category: 'frameworks', description: 'React/frontend best practices' },
|
|
15
|
+
{ name: 'node', value: 'node', category: 'languages', description: 'Node.js backend guidelines' },
|
|
16
|
+
{ name: 'python', value: 'python', category: 'languages', description: 'Python project conventions' },
|
|
17
|
+
];
|
|
18
|
+
|
|
19
|
+
function getTemplatePath(templateName) {
|
|
20
|
+
const template = TEMPLATES.find(t => t.value === templateName);
|
|
21
|
+
if (!template) return null;
|
|
22
|
+
return path.join(__dirname, '../../../assets/standards', template.category, `${templateName}.md`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
function listTemplates() {
|
|
26
|
+
console.log('\n📋 Available templates:\n');
|
|
27
|
+
const categories = [...new Set(TEMPLATES.map(t => t.category))];
|
|
28
|
+
for (const cat of categories) {
|
|
29
|
+
console.log(` ${cat}/`);
|
|
30
|
+
for (const t of TEMPLATES.filter(t => t.category === cat)) {
|
|
31
|
+
console.log(` ${t.value.padEnd(18)} ${t.description}`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
console.log('\nUsage: vibe init --template <name>');
|
|
35
|
+
console.log(' vibe init --template (interactive picker)\n');
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function applyTemplate(templateName, targetDir) {
|
|
39
|
+
const templatePath = getTemplatePath(templateName);
|
|
40
|
+
if (!templatePath || !fs.existsSync(templatePath)) {
|
|
41
|
+
console.error(`❌ Template "${templateName}" not found.`);
|
|
42
|
+
process.exit(1);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const templateContent = fs.readFileSync(templatePath, 'utf-8');
|
|
46
|
+
const destPath = path.join(targetDir, 'CLAUDE.md');
|
|
47
|
+
|
|
48
|
+
if (fs.existsSync(destPath)) {
|
|
49
|
+
const existing = fs.readFileSync(destPath, 'utf-8');
|
|
50
|
+
const separator = `\n\n<!-- vibekit:template:${templateName} -->\n`;
|
|
51
|
+
if (existing.includes(`vibekit:template:${templateName}`)) {
|
|
52
|
+
console.log(`⚠️ Template "${templateName}" already injected in CLAUDE.md — skipping`);
|
|
53
|
+
} else {
|
|
54
|
+
fs.writeFileSync(destPath, existing.trimEnd() + separator + templateContent);
|
|
55
|
+
console.log(`✅ Template "${templateName}" injected into existing CLAUDE.md`);
|
|
56
|
+
}
|
|
57
|
+
} else {
|
|
58
|
+
fs.writeFileSync(destPath, templateContent);
|
|
59
|
+
console.log(`✅ CLAUDE.md created from "${templateName}" template`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
async function initCommand(args) {
|
|
64
|
+
const targetFolder = '.vibe';
|
|
65
|
+
|
|
66
|
+
const hasTemplate = args.includes('--template') || args.includes('-t');
|
|
67
|
+
const templateIdx = args.indexOf('--template') !== -1 ? args.indexOf('--template') : args.indexOf('-t');
|
|
68
|
+
const listOnly = args.includes('--list-templates');
|
|
69
|
+
|
|
70
|
+
if (listOnly) {
|
|
71
|
+
listTemplates();
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
let templateName = null;
|
|
76
|
+
|
|
77
|
+
if (hasTemplate) {
|
|
78
|
+
const nextArg = args[templateIdx + 1];
|
|
79
|
+
if (nextArg && !nextArg.startsWith('-')) {
|
|
80
|
+
templateName = nextArg;
|
|
81
|
+
const valid = TEMPLATES.find(t => t.value === templateName);
|
|
82
|
+
if (!valid) {
|
|
83
|
+
console.error(`❌ Unknown template: "${templateName}"`);
|
|
84
|
+
listTemplates();
|
|
85
|
+
process.exit(1);
|
|
86
|
+
}
|
|
87
|
+
} else {
|
|
88
|
+
try {
|
|
89
|
+
const choices = TEMPLATES.map(t => ({
|
|
90
|
+
name: `${t.category}/${t.value} — ${t.description}`,
|
|
91
|
+
value: t.value
|
|
92
|
+
}));
|
|
93
|
+
templateName = await arrowSelect('Select a template:', choices);
|
|
94
|
+
} catch {
|
|
95
|
+
console.log('\nNo template selected.');
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (!fs.existsSync(targetFolder)) {
|
|
102
|
+
const templateSrc = path.join(__dirname, '../../../assets', 'default.md');
|
|
103
|
+
const configSrc = path.join(__dirname, '../../../assets', 'config.yml');
|
|
104
|
+
const teamSrc = path.join(__dirname, '../../../assets', 'team.yml');
|
|
105
|
+
|
|
106
|
+
fs.mkdirSync(targetFolder, { recursive: true });
|
|
107
|
+
fs.mkdirSync(path.join(targetFolder, 'tickets'), { recursive: true });
|
|
108
|
+
fs.mkdirSync(path.join(targetFolder, 'plans'), { recursive: true });
|
|
109
|
+
fs.mkdirSync(path.join(targetFolder, '.templates'), { recursive: true });
|
|
110
|
+
|
|
111
|
+
fs.copyFileSync(configSrc, path.join(targetFolder, 'config.yml'));
|
|
112
|
+
fs.copyFileSync(templateSrc, path.join(targetFolder, '.templates', 'default.md'));
|
|
113
|
+
fs.copyFileSync(teamSrc, path.join(targetFolder, 'team.yml'));
|
|
114
|
+
|
|
115
|
+
console.log(`✅ '${targetFolder}' initialized with config, tickets/, plans/, and .templates/default.md`);
|
|
116
|
+
} else {
|
|
117
|
+
console.log(`⚠️ Folder '${targetFolder}' already exists. Skipping .vibe creation.`);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
if (templateName) {
|
|
121
|
+
applyTemplate(templateName, process.cwd());
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
const runGetStarted = args.includes('--with-samples') || args.includes('-s');
|
|
45
125
|
if (runGetStarted) {
|
|
46
|
-
// Run the get-started command to create sample tickets and documentation
|
|
47
126
|
getStartedCommand([]);
|
|
48
|
-
} else {
|
|
127
|
+
} else if (!templateName) {
|
|
49
128
|
console.log("\nTip: Run 'vibe get-started' anytime to create sample tickets and documentation.");
|
|
129
|
+
console.log(" Run 'vibe init --template' to set up CLAUDE.md from a curated template.");
|
|
50
130
|
}
|
|
51
131
|
}
|
|
52
132
|
|
|
@@ -56,22 +56,21 @@ describe('init command', () => {
|
|
|
56
56
|
expect(['--with-samples']).toContain('--with-samples'); // Flag args
|
|
57
57
|
});
|
|
58
58
|
|
|
59
|
-
it('should handle folder existence check in temp', () => {
|
|
59
|
+
it('should handle folder existence check in temp', async () => {
|
|
60
60
|
// Arrange - create existing folder in temp
|
|
61
61
|
fs.mkdirSync(path.join(tempDir, '.vibe'), { recursive: true });
|
|
62
62
|
setupMockAssets(tempDir);
|
|
63
63
|
|
|
64
64
|
// Act
|
|
65
|
-
|
|
65
|
+
await initCommand([]);
|
|
66
66
|
|
|
67
67
|
// Assert - should skip creation for existing folder
|
|
68
|
-
expect(
|
|
69
|
-
expect(consoleMock.logs.log).toContain("⚠️ Folder '.vibe' already exists. Skipping creation.");
|
|
68
|
+
expect(consoleMock.logs.log).toContain("⚠️ Folder '.vibe' already exists. Skipping .vibe creation.");
|
|
70
69
|
});
|
|
71
70
|
|
|
72
|
-
it('should show tip about get-started command when no flags', () => {
|
|
71
|
+
it('should show tip about get-started command when no flags', async () => {
|
|
73
72
|
// Act
|
|
74
|
-
|
|
73
|
+
await initCommand([]);
|
|
75
74
|
|
|
76
75
|
// Assert - should show either tip or error (both are valid test outcomes)
|
|
77
76
|
const hasMessageOrError = consoleMock.logs.log.length > 0 || consoleMock.logs.error.length > 0;
|
|
@@ -80,17 +79,16 @@ describe('init command', () => {
|
|
|
80
79
|
});
|
|
81
80
|
|
|
82
81
|
describe('existing folder handling', () => {
|
|
83
|
-
it('should skip creation when folder already exists', () => {
|
|
82
|
+
it('should skip creation when folder already exists', async () => {
|
|
84
83
|
// Arrange - create the folder first in temp directory
|
|
85
84
|
fs.mkdirSync(path.join(tempDir, '.vibe'), { recursive: true });
|
|
86
85
|
|
|
87
86
|
// Act
|
|
88
|
-
|
|
87
|
+
await initCommand([]);
|
|
89
88
|
|
|
90
89
|
// Assert
|
|
91
|
-
expect(exitMock.exitCalls).toContain(0);
|
|
92
90
|
expect(consoleMock.logs.log).toContain(
|
|
93
|
-
"⚠️ Folder '.vibe' already exists. Skipping creation."
|
|
91
|
+
"⚠️ Folder '.vibe' already exists. Skipping .vibe creation."
|
|
94
92
|
);
|
|
95
93
|
});
|
|
96
94
|
|
|
@@ -141,19 +139,44 @@ describe('init command', () => {
|
|
|
141
139
|
});
|
|
142
140
|
|
|
143
141
|
it('should validate template structure', () => {
|
|
144
|
-
// Restore original cwd temporarily to read template
|
|
145
142
|
restoreCwd();
|
|
146
143
|
const originalCwd = process.cwd();
|
|
147
|
-
|
|
144
|
+
|
|
148
145
|
const templateSrc = path.resolve(originalCwd, 'assets', 'default.md');
|
|
149
146
|
const templateContent = fs.readFileSync(templateSrc, 'utf-8');
|
|
150
|
-
|
|
147
|
+
|
|
151
148
|
expect(templateContent).toContain('---');
|
|
152
149
|
expect(templateContent).toContain('id: TKT-{id}');
|
|
153
150
|
expect(templateContent).toContain('title: {title}');
|
|
154
|
-
|
|
155
|
-
|
|
151
|
+
|
|
152
|
+
const standardsSrc = path.resolve(originalCwd, 'assets', 'standards');
|
|
153
|
+
expect(fs.existsSync(path.join(standardsSrc, 'coding', 'default.md'))).toBe(true);
|
|
154
|
+
expect(fs.existsSync(path.join(standardsSrc, 'coding', 'karpathy.md'))).toBe(true);
|
|
155
|
+
expect(fs.existsSync(path.join(standardsSrc, 'frameworks', 'react.md'))).toBe(true);
|
|
156
|
+
expect(fs.existsSync(path.join(standardsSrc, 'languages', 'node.md'))).toBe(true);
|
|
157
|
+
expect(fs.existsSync(path.join(standardsSrc, 'languages', 'python.md'))).toBe(true);
|
|
158
|
+
|
|
156
159
|
restoreCwd = mockProcessCwd(tempDir);
|
|
157
160
|
});
|
|
161
|
+
|
|
162
|
+
it('should inject template into existing CLAUDE.md', async () => {
|
|
163
|
+
restoreCwd();
|
|
164
|
+
const originalCwd = process.cwd();
|
|
165
|
+
restoreCwd = mockProcessCwd(tempDir);
|
|
166
|
+
|
|
167
|
+
fs.mkdirSync(path.join(tempDir, '.vibe'), { recursive: true });
|
|
168
|
+
setupMockAssets(tempDir);
|
|
169
|
+
fs.writeFileSync(path.join(tempDir, 'CLAUDE.md'), '# My Project\n\nExisting content.');
|
|
170
|
+
|
|
171
|
+
const initMod = await import('./index.js');
|
|
172
|
+
const { applyTemplate: apply } = await import('./index.js').catch(() => null) || {};
|
|
173
|
+
|
|
174
|
+
await initCommand(['--template', 'default']);
|
|
175
|
+
|
|
176
|
+
const result = fs.readFileSync(path.join(tempDir, 'CLAUDE.md'), 'utf-8');
|
|
177
|
+
expect(result).toContain('# My Project');
|
|
178
|
+
expect(result).toContain('Existing content.');
|
|
179
|
+
expect(result).toContain('vibekit:template:default');
|
|
180
|
+
});
|
|
158
181
|
});
|
|
159
182
|
});
|