@vibedx/vibekit 0.1.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 +368 -0
- package/assets/config.yml +35 -0
- package/assets/default.md +47 -0
- package/assets/instructions/README.md +46 -0
- package/assets/instructions/claude.md +83 -0
- package/assets/instructions/codex.md +19 -0
- package/index.js +106 -0
- package/package.json +90 -0
- package/src/commands/close/index.js +66 -0
- package/src/commands/close/index.test.js +235 -0
- package/src/commands/get-started/index.js +138 -0
- package/src/commands/get-started/index.test.js +246 -0
- package/src/commands/init/index.js +51 -0
- package/src/commands/init/index.test.js +159 -0
- package/src/commands/link/index.js +395 -0
- package/src/commands/link/index.test.js +28 -0
- package/src/commands/lint/index.js +657 -0
- package/src/commands/lint/index.test.js +569 -0
- package/src/commands/list/index.js +131 -0
- package/src/commands/list/index.test.js +153 -0
- package/src/commands/new/index.js +305 -0
- package/src/commands/new/index.test.js +256 -0
- package/src/commands/refine/index.js +741 -0
- package/src/commands/refine/index.test.js +28 -0
- package/src/commands/review/index.js +957 -0
- package/src/commands/review/index.test.js +193 -0
- package/src/commands/start/index.js +180 -0
- package/src/commands/start/index.test.js +88 -0
- package/src/commands/unlink/index.js +123 -0
- package/src/commands/unlink/index.test.js +22 -0
- package/src/utils/arrow-select.js +233 -0
- package/src/utils/cli.js +489 -0
- package/src/utils/cli.test.js +9 -0
- package/src/utils/git.js +146 -0
- package/src/utils/git.test.js +330 -0
- package/src/utils/index.js +193 -0
- package/src/utils/index.test.js +375 -0
- package/src/utils/prompts.js +47 -0
- package/src/utils/prompts.test.js +165 -0
- package/src/utils/test-helpers.js +492 -0
- package/src/utils/ticket.js +423 -0
- package/src/utils/ticket.test.js +190 -0
|
@@ -0,0 +1,256 @@
|
|
|
1
|
+
import { describe, it, expect, beforeEach, afterEach } from '@jest/globals';
|
|
2
|
+
import fs from 'fs';
|
|
3
|
+
import path from 'path';
|
|
4
|
+
import {
|
|
5
|
+
createTempDir,
|
|
6
|
+
cleanupTempDir,
|
|
7
|
+
mockConsole,
|
|
8
|
+
mockProcessCwd,
|
|
9
|
+
mockProcessExit,
|
|
10
|
+
createMockVibeProject
|
|
11
|
+
} from '../../utils/test-helpers.js';
|
|
12
|
+
import newCommand from './index.js';
|
|
13
|
+
|
|
14
|
+
describe('new command', () => {
|
|
15
|
+
let tempDir;
|
|
16
|
+
let consoleMock;
|
|
17
|
+
let restoreCwd;
|
|
18
|
+
let exitMock;
|
|
19
|
+
|
|
20
|
+
beforeEach(() => {
|
|
21
|
+
// Create temp directory
|
|
22
|
+
tempDir = createTempDir('new-test');
|
|
23
|
+
|
|
24
|
+
// Mock console and process
|
|
25
|
+
consoleMock = mockConsole();
|
|
26
|
+
restoreCwd = mockProcessCwd(tempDir);
|
|
27
|
+
exitMock = mockProcessExit();
|
|
28
|
+
|
|
29
|
+
// Create a basic vibe project structure
|
|
30
|
+
createMockVibeProject(tempDir);
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
afterEach(() => {
|
|
34
|
+
// Restore mocks
|
|
35
|
+
consoleMock.restore();
|
|
36
|
+
restoreCwd();
|
|
37
|
+
exitMock.restore();
|
|
38
|
+
|
|
39
|
+
// Cleanup temp directory
|
|
40
|
+
cleanupTempDir(tempDir);
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
describe('basic ticket creation', () => {
|
|
44
|
+
it('should create a new ticket with default settings', async () => {
|
|
45
|
+
// Act
|
|
46
|
+
await newCommand(['Fix authentication bug']);
|
|
47
|
+
|
|
48
|
+
// Assert
|
|
49
|
+
const ticketsDir = path.join(tempDir, '.vibe', 'tickets');
|
|
50
|
+
const files = fs.readdirSync(ticketsDir);
|
|
51
|
+
const ticketFile = files.find(f => f.startsWith('TKT-001'));
|
|
52
|
+
|
|
53
|
+
expect(ticketFile).toBeTruthy();
|
|
54
|
+
expect(files).toHaveLength(1);
|
|
55
|
+
|
|
56
|
+
const ticketPath = path.join(ticketsDir, ticketFile);
|
|
57
|
+
const content = fs.readFileSync(ticketPath, 'utf-8');
|
|
58
|
+
expect(content).toContain('title: Fix authentication bug');
|
|
59
|
+
expect(content).toContain('priority: medium');
|
|
60
|
+
expect(content).toContain('status: open');
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('should handle multi-word titles correctly', async () => {
|
|
64
|
+
// Act
|
|
65
|
+
await newCommand(['Add dark mode toggle for user interface']);
|
|
66
|
+
|
|
67
|
+
// Assert
|
|
68
|
+
const ticketsDir = path.join(tempDir, '.vibe', 'tickets');
|
|
69
|
+
const files = fs.readdirSync(ticketsDir);
|
|
70
|
+
const ticketFile = files[0];
|
|
71
|
+
|
|
72
|
+
const content = fs.readFileSync(path.join(ticketsDir, ticketFile), 'utf-8');
|
|
73
|
+
expect(content).toContain('title: Add dark mode toggle for user interface');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
it('should log success message with ticket details', async () => {
|
|
77
|
+
// Act
|
|
78
|
+
await newCommand(['Test ticket']);
|
|
79
|
+
|
|
80
|
+
// Assert
|
|
81
|
+
const successMessages = consoleMock.logs.log.filter(log =>
|
|
82
|
+
log.includes('Created ticket:') &&
|
|
83
|
+
log.includes('TKT-001') &&
|
|
84
|
+
log.includes('priority: medium') &&
|
|
85
|
+
log.includes('status: open')
|
|
86
|
+
);
|
|
87
|
+
expect(successMessages.length).toBeGreaterThan(0);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('priority and status options', () => {
|
|
92
|
+
it('should accept custom priority', async () => {
|
|
93
|
+
// Act
|
|
94
|
+
await newCommand(['High priority task', '--priority', 'high']);
|
|
95
|
+
|
|
96
|
+
// Assert
|
|
97
|
+
const ticketsDir = path.join(tempDir, '.vibe', 'tickets');
|
|
98
|
+
const files = fs.readdirSync(ticketsDir);
|
|
99
|
+
const content = fs.readFileSync(path.join(ticketsDir, files[0]), 'utf-8');
|
|
100
|
+
expect(content).toContain('priority: high');
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('should accept custom status', async () => {
|
|
104
|
+
// Act
|
|
105
|
+
await newCommand(['In progress task', '--status', 'in_progress']);
|
|
106
|
+
|
|
107
|
+
// Assert
|
|
108
|
+
const ticketsDir = path.join(tempDir, '.vibe', 'tickets');
|
|
109
|
+
const files = fs.readdirSync(ticketsDir);
|
|
110
|
+
const content = fs.readFileSync(path.join(ticketsDir, files[0]), 'utf-8');
|
|
111
|
+
expect(content).toContain('status: in_progress');
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('should accept both priority and status', async () => {
|
|
115
|
+
// Act
|
|
116
|
+
await newCommand(['Custom task', '--priority', 'urgent', '--status', 'review']);
|
|
117
|
+
|
|
118
|
+
// Assert
|
|
119
|
+
const ticketsDir = path.join(tempDir, '.vibe', 'tickets');
|
|
120
|
+
const files = fs.readdirSync(ticketsDir);
|
|
121
|
+
const content = fs.readFileSync(path.join(ticketsDir, files[0]), 'utf-8');
|
|
122
|
+
expect(content).toContain('priority: urgent');
|
|
123
|
+
expect(content).toContain('status: review');
|
|
124
|
+
});
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
describe('error handling', () => {
|
|
128
|
+
it('should exit with error when no title provided', async () => {
|
|
129
|
+
// Act & Assert
|
|
130
|
+
await expect(async () => {
|
|
131
|
+
await newCommand([]);
|
|
132
|
+
}).rejects.toThrow('process.exit(1)');
|
|
133
|
+
|
|
134
|
+
expect(exitMock.exitCalls).toContain(1);
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
it('should exit with error when config missing', async () => {
|
|
138
|
+
// Arrange - remove config file
|
|
139
|
+
fs.unlinkSync(path.join(tempDir, '.vibe', 'config.yml'));
|
|
140
|
+
|
|
141
|
+
// Act & Assert
|
|
142
|
+
await expect(async () => {
|
|
143
|
+
await newCommand(['Test ticket']);
|
|
144
|
+
}).rejects.toThrow('process.exit(1)');
|
|
145
|
+
|
|
146
|
+
expect(exitMock.exitCalls).toContain(1);
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
it('should exit with error when template missing', async () => {
|
|
150
|
+
// Arrange - remove template file
|
|
151
|
+
fs.unlinkSync(path.join(tempDir, '.vibe', '.templates', 'default.md'));
|
|
152
|
+
|
|
153
|
+
// Act & Assert
|
|
154
|
+
await expect(async () => {
|
|
155
|
+
await newCommand(['Test ticket']);
|
|
156
|
+
}).rejects.toThrow('process.exit(1)');
|
|
157
|
+
|
|
158
|
+
expect(exitMock.exitCalls).toContain(1);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('should handle empty title gracefully', async () => {
|
|
162
|
+
// Act & Assert
|
|
163
|
+
await expect(async () => {
|
|
164
|
+
await newCommand(['']);
|
|
165
|
+
}).rejects.toThrow('process.exit(1)');
|
|
166
|
+
});
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
describe('ticket ID generation', () => {
|
|
170
|
+
it('should increment ticket ID for multiple tickets', async () => {
|
|
171
|
+
// Act - create multiple tickets
|
|
172
|
+
await newCommand(['First ticket']);
|
|
173
|
+
await newCommand(['Second ticket']);
|
|
174
|
+
await newCommand(['Third ticket']);
|
|
175
|
+
|
|
176
|
+
// Assert
|
|
177
|
+
const ticketsDir = path.join(tempDir, '.vibe', 'tickets');
|
|
178
|
+
const files = fs.readdirSync(ticketsDir).sort();
|
|
179
|
+
|
|
180
|
+
expect(files).toHaveLength(3);
|
|
181
|
+
expect(files[0]).toMatch(/^TKT-001/);
|
|
182
|
+
expect(files[1]).toMatch(/^TKT-002/);
|
|
183
|
+
expect(files[2]).toMatch(/^TKT-003/);
|
|
184
|
+
});
|
|
185
|
+
|
|
186
|
+
it('should create proper slug from title', async () => {
|
|
187
|
+
// Act
|
|
188
|
+
await newCommand(['Add User Authentication System']);
|
|
189
|
+
|
|
190
|
+
// Assert
|
|
191
|
+
const ticketsDir = path.join(tempDir, '.vibe', 'tickets');
|
|
192
|
+
const files = fs.readdirSync(ticketsDir);
|
|
193
|
+
|
|
194
|
+
expect(files[0]).toMatch(/add-user-authentication-system/);
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
describe('template processing', () => {
|
|
199
|
+
it('should replace template placeholders correctly', async () => {
|
|
200
|
+
// Act
|
|
201
|
+
await newCommand(['Template test']);
|
|
202
|
+
|
|
203
|
+
// Assert
|
|
204
|
+
const ticketsDir = path.join(tempDir, '.vibe', 'tickets');
|
|
205
|
+
const files = fs.readdirSync(ticketsDir);
|
|
206
|
+
const content = fs.readFileSync(path.join(ticketsDir, files[0]), 'utf-8');
|
|
207
|
+
|
|
208
|
+
expect(content).toContain('id: TKT-001');
|
|
209
|
+
expect(content).toContain('title: Template test');
|
|
210
|
+
expect(content).toMatch(/created_at: \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/);
|
|
211
|
+
expect(content).toMatch(/updated_at: \d{4}-\d{2}-\d{2}T\d{2}:\d{2}:\d{2}/);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it('should preserve template structure', async () => {
|
|
215
|
+
// Act
|
|
216
|
+
await newCommand(['Structure test']);
|
|
217
|
+
|
|
218
|
+
// Assert
|
|
219
|
+
const ticketsDir = path.join(tempDir, '.vibe', 'tickets');
|
|
220
|
+
const files = fs.readdirSync(ticketsDir);
|
|
221
|
+
const content = fs.readFileSync(path.join(ticketsDir, files[0]), 'utf-8');
|
|
222
|
+
|
|
223
|
+
expect(content).toContain('## Description');
|
|
224
|
+
expect(content).toContain('## Acceptance Criteria');
|
|
225
|
+
expect(content).toContain('## Code Quality');
|
|
226
|
+
});
|
|
227
|
+
});
|
|
228
|
+
|
|
229
|
+
describe('argument parsing', () => {
|
|
230
|
+
it('should handle flags and title correctly', async () => {
|
|
231
|
+
// Act
|
|
232
|
+
await newCommand(['My ticket title', '--priority', 'low', '--status', 'done']);
|
|
233
|
+
|
|
234
|
+
// Assert
|
|
235
|
+
const ticketsDir = path.join(tempDir, '.vibe', 'tickets');
|
|
236
|
+
const files = fs.readdirSync(ticketsDir);
|
|
237
|
+
const content = fs.readFileSync(path.join(ticketsDir, files[0]), 'utf-8');
|
|
238
|
+
|
|
239
|
+
expect(content).toContain('title: My ticket title');
|
|
240
|
+
expect(content).toContain('priority: low');
|
|
241
|
+
expect(content).toContain('status: done');
|
|
242
|
+
});
|
|
243
|
+
|
|
244
|
+
it('should handle title with multiple words', async () => {
|
|
245
|
+
// Act
|
|
246
|
+
await newCommand(['Fix', 'the', 'critical', 'authentication', 'bug']);
|
|
247
|
+
|
|
248
|
+
// Assert
|
|
249
|
+
const ticketsDir = path.join(tempDir, '.vibe', 'tickets');
|
|
250
|
+
const files = fs.readdirSync(ticketsDir);
|
|
251
|
+
const content = fs.readFileSync(path.join(ticketsDir, files[0]), 'utf-8');
|
|
252
|
+
|
|
253
|
+
expect(content).toContain('title: Fix the critical authentication bug');
|
|
254
|
+
});
|
|
255
|
+
});
|
|
256
|
+
});
|