@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 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
@@ -27,6 +27,10 @@ team:
27
27
  agent:
28
28
  timeout: 900
29
29
 
30
+ swarm:
31
+ maxAgents: 3
32
+ timeout: 900
33
+
30
34
  ai:
31
35
  enabled: false
32
36
 
@@ -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
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibedx/vibekit",
3
- "version": "0.8.7",
3
+ "version": "0.9.0",
4
4
  "description": "A powerful CLI tool for managing development tickets and project workflows",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -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
- * Initialize a new VibeKit project
13
- * @param {string[]} args Command arguments
14
- */
15
- function initCommand(args) {
16
- const targetFolder = ".vibe";
17
-
18
- if (fs.existsSync(targetFolder)) {
19
- console.log(`⚠️ Folder '${targetFolder}' already exists. Skipping creation.`);
20
- process.exit(0);
21
- }
22
-
23
- // Use real files instead of hardcoded template strings
24
- const templateSrc = path.join(__dirname, "../../../assets", "default.md");
25
- const configSrc = path.join(__dirname, "../../../assets", "config.yml");
26
- const teamSrc = path.join(__dirname, "../../../assets", "team.yml");
27
-
28
- fs.mkdirSync(targetFolder, { recursive: true });
29
- fs.mkdirSync(path.join(targetFolder, "tickets"), { recursive: true });
30
- fs.mkdirSync(path.join(targetFolder, ".templates"), { recursive: true });
31
-
32
- // Copy files from assets directory instead of using hardcoded templates
33
- fs.copyFileSync(configSrc, path.join(targetFolder, "config.yml"));
34
- fs.copyFileSync(templateSrc, path.join(targetFolder, ".templates", "default.md"));
35
- fs.copyFileSync(teamSrc, path.join(targetFolder, "team.yml"));
36
-
37
- console.log(`✅ '${targetFolder}' initialized with config, tickets/, and .templates/default.md`);
38
-
39
- // Ask if the user wants to run get-started
40
- console.log("\nWould you like to create sample tickets and documentation? (y/n)");
41
-
42
- // Since we can't get user input directly in this environment, we'll check for a flag
43
- const runGetStarted = args.includes("--with-samples") || args.includes("-s");
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
- expect(() => initCommand([])).toThrow('process.exit(0)');
65
+ await initCommand([]);
66
66
 
67
67
  // Assert - should skip creation for existing folder
68
- expect(exitMock.exitCalls).toContain(0);
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
- expect(() => initCommand([])).toThrow();
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
- expect(() => initCommand([])).toThrow('process.exit(0)');
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
- // Restore mock
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
  });