@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.
Files changed (43) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +368 -0
  3. package/assets/config.yml +35 -0
  4. package/assets/default.md +47 -0
  5. package/assets/instructions/README.md +46 -0
  6. package/assets/instructions/claude.md +83 -0
  7. package/assets/instructions/codex.md +19 -0
  8. package/index.js +106 -0
  9. package/package.json +90 -0
  10. package/src/commands/close/index.js +66 -0
  11. package/src/commands/close/index.test.js +235 -0
  12. package/src/commands/get-started/index.js +138 -0
  13. package/src/commands/get-started/index.test.js +246 -0
  14. package/src/commands/init/index.js +51 -0
  15. package/src/commands/init/index.test.js +159 -0
  16. package/src/commands/link/index.js +395 -0
  17. package/src/commands/link/index.test.js +28 -0
  18. package/src/commands/lint/index.js +657 -0
  19. package/src/commands/lint/index.test.js +569 -0
  20. package/src/commands/list/index.js +131 -0
  21. package/src/commands/list/index.test.js +153 -0
  22. package/src/commands/new/index.js +305 -0
  23. package/src/commands/new/index.test.js +256 -0
  24. package/src/commands/refine/index.js +741 -0
  25. package/src/commands/refine/index.test.js +28 -0
  26. package/src/commands/review/index.js +957 -0
  27. package/src/commands/review/index.test.js +193 -0
  28. package/src/commands/start/index.js +180 -0
  29. package/src/commands/start/index.test.js +88 -0
  30. package/src/commands/unlink/index.js +123 -0
  31. package/src/commands/unlink/index.test.js +22 -0
  32. package/src/utils/arrow-select.js +233 -0
  33. package/src/utils/cli.js +489 -0
  34. package/src/utils/cli.test.js +9 -0
  35. package/src/utils/git.js +146 -0
  36. package/src/utils/git.test.js +330 -0
  37. package/src/utils/index.js +193 -0
  38. package/src/utils/index.test.js +375 -0
  39. package/src/utils/prompts.js +47 -0
  40. package/src/utils/prompts.test.js +165 -0
  41. package/src/utils/test-helpers.js +492 -0
  42. package/src/utils/ticket.js +423 -0
  43. package/src/utils/ticket.test.js +190 -0
package/index.js ADDED
@@ -0,0 +1,106 @@
1
+ #!/usr/bin/env node
2
+
3
+ /**
4
+ * VibeKit - A developer-focused ticket and task management CLI tool
5
+ *
6
+ * This is the main entry point for the VibeKit CLI application.
7
+ * It handles command routing and provides a consistent interface
8
+ * for all VibeKit operations.
9
+ *
10
+ * @fileoverview Main CLI entry point for VibeKit
11
+ * @author VibeKit Team
12
+ * @version 1.0.0
13
+ */
14
+
15
+ import path from 'path';
16
+ import { fileURLToPath } from 'url';
17
+ import { dirname } from 'path';
18
+
19
+ // ESM replacement for __dirname
20
+ const __filename = fileURLToPath(import.meta.url);
21
+ const __dirname = dirname(__filename);
22
+
23
+ // Available commands in VibeKit
24
+ const AVAILABLE_COMMANDS = [
25
+ 'init', 'new', 'close', 'list', 'get-started',
26
+ 'start', 'link', 'unlink', 'refine', 'lint', 'review'
27
+ ];
28
+
29
+ /**
30
+ * Display available commands to the user
31
+ */
32
+ function showAvailableCommands() {
33
+ console.log(`Available commands: ${AVAILABLE_COMMANDS.join(', ')}`);
34
+ }
35
+
36
+ /**
37
+ * Execute a VibeKit command
38
+ * @param {string} command - The command to execute
39
+ * @param {Array} args - Arguments to pass to the command
40
+ */
41
+ async function executeCommand(command, args) {
42
+ const commandPath = path.join(__dirname, 'src', 'commands', command, 'index.js');
43
+
44
+ try {
45
+ // Dynamic import for ESM
46
+ const commandModule = await import(commandPath);
47
+ const commandFunction = commandModule.default;
48
+
49
+ if (typeof commandFunction === 'function') {
50
+ await commandFunction(args);
51
+ } else {
52
+ console.error(`❌ Command '${command}' is not executable.`);
53
+ process.exit(1);
54
+ }
55
+ } catch (err) {
56
+ if (err.code === 'ERR_MODULE_NOT_FOUND') {
57
+ showAvailableCommands();
58
+ console.error(`❌ Command '${command}' not found.`);
59
+ } else {
60
+ console.error(`❌ Error executing command '${command}': ${err.message}`);
61
+
62
+ // Only show stack trace in debug mode or development
63
+ if (process.env.NODE_ENV === 'development' || process.env.DEBUG) {
64
+ console.error(err.stack);
65
+ }
66
+ }
67
+ process.exit(1);
68
+ }
69
+ }
70
+
71
+ /**
72
+ * Main application entry point
73
+ */
74
+ async function main() {
75
+ // Parse command line arguments
76
+ const [command, ...commandArgs] = process.argv.slice(2);
77
+
78
+ try {
79
+ // Show help if no command provided
80
+ if (!command) {
81
+ console.log('🎆 VibeKit - Developer-focused ticket management\n');
82
+ showAvailableCommands();
83
+ console.log('\nUse "vibe <command>" to get started!');
84
+ process.exit(0);
85
+ }
86
+
87
+ // Execute the requested command
88
+ await executeCommand(command, commandArgs);
89
+
90
+ } catch (err) {
91
+ console.error(`❌ Unexpected error: ${err.message}`);
92
+
93
+ // Only show stack trace in debug mode or development
94
+ if (process.env.NODE_ENV === 'development' || process.env.DEBUG) {
95
+ console.error(err.stack);
96
+ }
97
+
98
+ process.exit(1);
99
+ }
100
+ }
101
+
102
+ // Run the application
103
+ main().catch((error) => {
104
+ console.error(`❌ Fatal error: ${error.message}`);
105
+ process.exit(1);
106
+ });
package/package.json ADDED
@@ -0,0 +1,90 @@
1
+ {
2
+ "name": "@vibedx/vibekit",
3
+ "version": "0.1.0",
4
+ "description": "A powerful CLI tool for managing development tickets and project workflows",
5
+ "main": "index.js",
6
+ "type": "module",
7
+ "files": [
8
+ "index.js",
9
+ "src/",
10
+ "assets/"
11
+ ],
12
+ "bin": {
13
+ "vibe": "index.js"
14
+ },
15
+ "scripts": {
16
+ "start": "node --watch index.js",
17
+ "test": "npm run test:cleanup && NODE_OPTIONS='--experimental-vm-modules' jest --detectOpenHandles",
18
+ "test:watch": "NODE_OPTIONS='--experimental-vm-modules' jest --watch",
19
+ "test:coverage": "NODE_OPTIONS='--experimental-vm-modules' jest --coverage --detectOpenHandles",
20
+ "test:cleanup": "rm -rf __temp__",
21
+ "prepublishOnly": "echo 'Ready to publish'",
22
+ "version": "git add -A && git commit -m 'chore: version bump'",
23
+ "postversion": "git push && git push --tags"
24
+ },
25
+ "keywords": [
26
+ "cli",
27
+ "tickets",
28
+ "workflow",
29
+ "development",
30
+ "project-management"
31
+ ],
32
+ "author": "Ives van Hoorne",
33
+ "license": "MIT",
34
+ "repository": {
35
+ "type": "git",
36
+ "url": "https://github.com/vibedx/vibekit.git"
37
+ },
38
+ "bugs": {
39
+ "url": "https://github.com/vibedx/vibekit/issues"
40
+ },
41
+ "homepage": "https://github.com/vibedx/vibekit#readme",
42
+ "dependencies": {
43
+ "chalk": "^4.1.2",
44
+ "clipboardy": "^4.0.0",
45
+ "js-yaml": "^4.1.0"
46
+ },
47
+ "devDependencies": {
48
+ "@jest/globals": "^29.7.0",
49
+ "jest": "^29.7.0"
50
+ },
51
+ "jest": {
52
+ "testEnvironment": "node",
53
+ "globals": {
54
+ "__DEV__": true
55
+ },
56
+ "testMatch": [
57
+ "**/src/**/*.test.js",
58
+ "**/__tests__/**/*.test.js",
59
+ "**/tests/**/*.test.js"
60
+ ],
61
+ "collectCoverage": true,
62
+ "coverageDirectory": "coverage",
63
+ "forceExit": true,
64
+ "coverageReporters": [
65
+ "text",
66
+ "lcov",
67
+ "html"
68
+ ],
69
+ "collectCoverageFrom": [
70
+ "src/**/*.js",
71
+ "index.js",
72
+ "!src/utils/cli.test.js",
73
+ "!src/**/*.test.js",
74
+ "!**/__tests__/**",
75
+ "!**/__temp__/**",
76
+ "!**/node_modules/**"
77
+ ],
78
+ "coverageThreshold": {
79
+ "global": {
80
+ "branches": 13,
81
+ "functions": 20,
82
+ "lines": 14,
83
+ "statements": 14
84
+ }
85
+ }
86
+ },
87
+ "engines": {
88
+ "node": ">=18.0.0"
89
+ }
90
+ }
@@ -0,0 +1,66 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import yaml from 'js-yaml';
4
+ import { getTicketsDir } from '../../utils/index.js';
5
+
6
+ /**
7
+ * Mark a ticket as done
8
+ * @param {string[]} args Command arguments
9
+ */
10
+ function closeCommand(args) {
11
+ const ticketArg = args[0];
12
+
13
+ if (!ticketArg) {
14
+ console.error("❌ Please provide a ticket ID or number.");
15
+ process.exit(1);
16
+ }
17
+
18
+ const ticketFolder = getTicketsDir();
19
+
20
+ if (!fs.existsSync(ticketFolder)) {
21
+ console.error(`❌ Tickets directory not found: ${ticketFolder}`);
22
+ process.exit(1);
23
+ }
24
+
25
+ const files = fs.readdirSync(ticketFolder);
26
+ const normalizedInput = ticketArg.startsWith("TKT-")
27
+ ? ticketArg
28
+ : `TKT-${ticketArg.padStart(3, "0")}`;
29
+
30
+ let matchFound = false;
31
+
32
+ for (const file of files) {
33
+ const fullPath = path.join(ticketFolder, file);
34
+
35
+ // Skip directories
36
+ if (fs.statSync(fullPath).isDirectory()) {
37
+ continue;
38
+ }
39
+
40
+ const content = fs.readFileSync(fullPath, "utf-8");
41
+ const match = content.match(/^---\n([\s\S]*?)\n---/);
42
+
43
+ if (match) {
44
+ const frontmatter = yaml.load(match[1]);
45
+ if (
46
+ frontmatter.id === normalizedInput ||
47
+ file.includes(normalizedInput)
48
+ ) {
49
+ frontmatter.status = "done";
50
+
51
+ const updated = `---\n${yaml.dump(frontmatter)}---${content.split("---").slice(2).join("---")}`;
52
+ fs.writeFileSync(fullPath, updated, "utf-8");
53
+
54
+ console.log(`✅ Ticket ${frontmatter.id} marked as done.`);
55
+ matchFound = true;
56
+ break;
57
+ }
58
+ }
59
+ }
60
+
61
+ if (!matchFound) {
62
+ console.log(`❌ No ticket matching '${ticketArg}' found.`);
63
+ }
64
+ }
65
+
66
+ export default closeCommand;
@@ -0,0 +1,235 @@
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 closeCommand from './index.js';
13
+
14
+ describe('close command', () => {
15
+ let tempDir;
16
+ let consoleMock;
17
+ let restoreCwd;
18
+ let exitMock;
19
+
20
+ beforeEach(() => {
21
+ tempDir = createTempDir('close-test');
22
+ consoleMock = mockConsole();
23
+ restoreCwd = mockProcessCwd(tempDir);
24
+ exitMock = mockProcessExit();
25
+ });
26
+
27
+ afterEach(() => {
28
+ consoleMock.restore();
29
+ restoreCwd();
30
+ exitMock.restore();
31
+ cleanupTempDir(tempDir);
32
+ });
33
+
34
+ describe('argument validation', () => {
35
+ it('should show error when no ticket ID provided', () => {
36
+ // Act
37
+ expect(() => closeCommand([])).toThrow('process.exit(1)');
38
+
39
+ // Assert
40
+ expect(exitMock.exitCalls).toContain(1);
41
+ expect(consoleMock.logs.error).toContain('❌ Please provide a ticket ID or number.');
42
+ });
43
+ });
44
+
45
+ describe('tickets directory validation', () => {
46
+ it('should show error when tickets directory does not exist', () => {
47
+ // Act - no vibe project created
48
+ expect(() => closeCommand(['TKT-001'])).toThrow('process.exit(1)');
49
+
50
+ // Assert
51
+ expect(exitMock.exitCalls).toContain(1);
52
+ expect(consoleMock.logs.error.some(log =>
53
+ log.includes('❌ Tickets directory not found:')
54
+ )).toBe(true);
55
+ });
56
+ });
57
+
58
+ describe('ticket closing', () => {
59
+ it('should mark ticket as done when found by full ID', () => {
60
+ // Arrange
61
+ createMockVibeProject(tempDir, {
62
+ withTickets: [
63
+ {
64
+ id: 'TKT-001',
65
+ title: 'Test ticket',
66
+ status: 'open',
67
+ slug: 'test-ticket'
68
+ }
69
+ ]
70
+ });
71
+
72
+ // Act
73
+ closeCommand(['TKT-001']);
74
+
75
+ // Assert
76
+ expect(consoleMock.logs.log).toContain('✅ Ticket TKT-001 marked as done.');
77
+
78
+ // Verify file was updated
79
+ const ticketPath = path.join(tempDir, '.vibe', 'tickets', 'TKT-001-test-ticket.md');
80
+ const content = fs.readFileSync(ticketPath, 'utf-8');
81
+ expect(content).toContain('status: done');
82
+ });
83
+
84
+ it('should mark ticket as done when found by number only', () => {
85
+ // Arrange
86
+ createMockVibeProject(tempDir, {
87
+ withTickets: [
88
+ {
89
+ id: 'TKT-002',
90
+ title: 'Another ticket',
91
+ status: 'in_progress',
92
+ slug: 'another-ticket'
93
+ }
94
+ ]
95
+ });
96
+
97
+ // Act
98
+ closeCommand(['2']);
99
+
100
+ // Assert
101
+ expect(consoleMock.logs.log).toContain('✅ Ticket TKT-002 marked as done.');
102
+
103
+ // Verify file was updated
104
+ const ticketPath = path.join(tempDir, '.vibe', 'tickets', 'TKT-002-another-ticket.md');
105
+ const content = fs.readFileSync(ticketPath, 'utf-8');
106
+ expect(content).toContain('status: done');
107
+ });
108
+
109
+ it('should preserve other frontmatter fields when updating status', () => {
110
+ // Arrange
111
+ createMockVibeProject(tempDir, {
112
+ withTickets: [
113
+ {
114
+ id: 'TKT-003',
115
+ title: 'Ticket with priority',
116
+ status: 'open',
117
+ priority: 'high',
118
+ slug: 'ticket-with-priority'
119
+ }
120
+ ]
121
+ });
122
+
123
+ // Act
124
+ closeCommand(['TKT-003']);
125
+
126
+ // Assert
127
+ const ticketPath = path.join(tempDir, '.vibe', 'tickets', 'TKT-003-ticket-with-priority.md');
128
+ const content = fs.readFileSync(ticketPath, 'utf-8');
129
+ expect(content).toContain('status: done');
130
+ expect(content).toContain('priority: high');
131
+ expect(content).toContain('title: Ticket with priority');
132
+ });
133
+
134
+ it('should handle ticket not found', () => {
135
+ // Arrange
136
+ createMockVibeProject(tempDir, {
137
+ withTickets: [
138
+ {
139
+ id: 'TKT-001',
140
+ title: 'Existing ticket',
141
+ status: 'open',
142
+ slug: 'existing-ticket'
143
+ }
144
+ ]
145
+ });
146
+
147
+ // Act
148
+ closeCommand(['TKT-999']);
149
+
150
+ // Assert
151
+ expect(consoleMock.logs.log).toContain("❌ No ticket matching 'TKT-999' found.");
152
+ });
153
+
154
+ it('should handle multiple tickets and find correct one', () => {
155
+ // Arrange
156
+ createMockVibeProject(tempDir, {
157
+ withTickets: [
158
+ {
159
+ id: 'TKT-001',
160
+ title: 'First ticket',
161
+ status: 'open',
162
+ slug: 'first-ticket'
163
+ },
164
+ {
165
+ id: 'TKT-002',
166
+ title: 'Second ticket',
167
+ status: 'in_progress',
168
+ slug: 'second-ticket'
169
+ },
170
+ {
171
+ id: 'TKT-003',
172
+ title: 'Third ticket',
173
+ status: 'review',
174
+ slug: 'third-ticket'
175
+ }
176
+ ]
177
+ });
178
+
179
+ // Act
180
+ closeCommand(['2']);
181
+
182
+ // Assert
183
+ expect(consoleMock.logs.log).toContain('✅ Ticket TKT-002 marked as done.');
184
+
185
+ // Verify only the targeted ticket was updated
186
+ const ticket1Path = path.join(tempDir, '.vibe', 'tickets', 'TKT-001-first-ticket.md');
187
+ const ticket2Path = path.join(tempDir, '.vibe', 'tickets', 'TKT-002-second-ticket.md');
188
+ const ticket3Path = path.join(tempDir, '.vibe', 'tickets', 'TKT-003-third-ticket.md');
189
+
190
+ expect(fs.readFileSync(ticket1Path, 'utf-8')).toContain('status: open');
191
+ expect(fs.readFileSync(ticket2Path, 'utf-8')).toContain('status: done');
192
+ expect(fs.readFileSync(ticket3Path, 'utf-8')).toContain('status: review');
193
+ });
194
+ });
195
+
196
+ describe('error handling', () => {
197
+ it('should skip directories in tickets folder', () => {
198
+ // Arrange
199
+ const vibeProject = createMockVibeProject(tempDir);
200
+
201
+ // Create a subdirectory in tickets folder
202
+ fs.mkdirSync(path.join(vibeProject.ticketsDir, 'subdirectory'));
203
+
204
+ // Create a valid ticket
205
+ const ticketContent = `---
206
+ id: TKT-001
207
+ title: Valid ticket
208
+ status: open
209
+ ---
210
+
211
+ # Test ticket`;
212
+ fs.writeFileSync(path.join(vibeProject.ticketsDir, 'TKT-001-valid.md'), ticketContent, 'utf-8');
213
+
214
+ // Act
215
+ closeCommand(['TKT-001']);
216
+
217
+ // Assert
218
+ expect(consoleMock.logs.log).toContain('✅ Ticket TKT-001 marked as done.');
219
+ });
220
+
221
+ it('should handle files without proper frontmatter', () => {
222
+ // Arrange
223
+ const vibeProject = createMockVibeProject(tempDir);
224
+
225
+ // Create invalid file without frontmatter
226
+ fs.writeFileSync(path.join(vibeProject.ticketsDir, 'invalid.md'), 'No frontmatter here', 'utf-8');
227
+
228
+ // Act
229
+ closeCommand(['TKT-001']);
230
+
231
+ // Assert
232
+ expect(consoleMock.logs.log).toContain("❌ No ticket matching 'TKT-001' found.");
233
+ });
234
+ });
235
+ });
@@ -0,0 +1,138 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import yaml from 'js-yaml';
4
+ import { getTicketsDir, getConfig, getNextTicketId, createSlug } from '../../utils/index.js';
5
+
6
+ /**
7
+ * Helper function to create sample tickets for the onboarding experience
8
+ * @param {string} title - The title of the ticket
9
+ * @param {string} description - The description of the ticket
10
+ * @param {string} priority - The priority of the ticket (low, medium, high)
11
+ * @param {string} status - The status of the ticket (open, in_progress, review, done)
12
+ */
13
+ function createSampleTicket(title, description, priority = "medium", status = "open") {
14
+ const configPath = path.join(process.cwd(), ".vibe", "config.yml");
15
+ const templatePath = path.join(process.cwd(), ".vibe", ".templates", "default.md");
16
+
17
+ if (!fs.existsSync(configPath) || !fs.existsSync(templatePath)) {
18
+ console.error("❌ Missing config.yml or default.md template.");
19
+ return false;
20
+ }
21
+
22
+ const config = yaml.load(fs.readFileSync(configPath, "utf-8"));
23
+ const template = fs.readFileSync(templatePath, "utf-8");
24
+ const ticketDir = path.join(process.cwd(), config.tickets?.path || ".vibe/tickets");
25
+
26
+ const files = fs.readdirSync(ticketDir);
27
+ const ticketNumbers = files
28
+ .map(f => f.match(/^TKT-(\\d+)/))
29
+ .filter(Boolean)
30
+ .map(match => parseInt(match[1], 10));
31
+ const nextId = Math.max(0, ...ticketNumbers) + 1;
32
+ const paddedId = String(nextId).padStart(3, "0");
33
+ const now = new Date().toISOString();
34
+
35
+ const ticketId = `TKT-${paddedId}`;
36
+ const slug = title.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-|-$/g, "");
37
+ const filename = `${ticketId}-${slug}.md`;
38
+
39
+ // Replace template placeholders with actual values
40
+ let content = template
41
+ .replace(/{id}/g, paddedId)
42
+ .replace(/{title}/g, title)
43
+ .replace(/{date}/g, now);
44
+
45
+ // Replace priority and status in the frontmatter
46
+ content = content.replace(/^priority: .*$/m, `priority: ${priority}`);
47
+ content = content.replace(/^status: .*$/m, `status: ${status}`);
48
+
49
+ // Add description
50
+ content = content.replace(/## Description\\s*\\n\\s*\\n/m, `## Description\\n\\n${description}\\n\\n`);
51
+
52
+ // Add AI prompt for the feature request example
53
+ if (title.includes("AI Prompt")) {
54
+ content = content.replace(/## AI Prompt\\s*\\n\\s*\\n/m,
55
+ `## AI Prompt\\n\\nGenerate ideas for implementing this feature in a Node.js CLI application. Consider user experience, error handling, and performance.\\n\\n`);
56
+ }
57
+
58
+ const outputPath = path.join(ticketDir, filename);
59
+ fs.writeFileSync(outputPath, content, "utf-8");
60
+
61
+ console.log(`✅ Created sample ticket: ${filename}`);
62
+ return true;
63
+ }
64
+
65
+ /**
66
+ * Create onboarding materials for VibeKit
67
+ * @param {string[]} args Command arguments
68
+ */
69
+ function getStartedCommand(args) {
70
+ console.log("✨ Welcome to VibeKit! Setting up your onboarding experience...");
71
+
72
+ // Check if .vibe directory exists
73
+ if (!fs.existsSync(path.join(process.cwd(), ".vibe"))) {
74
+ console.error("❌ VibeKit is not initialized. Please run 'vibe init' first.");
75
+ process.exit(1);
76
+ }
77
+
78
+ // Create .vibe/README.md with instructions
79
+ const readmePath = path.join(process.cwd(), ".vibe", "README.md");
80
+ const readmeContent = `# Welcome to VibeKit
81
+
82
+ VibeKit is a CLI tool for managing tickets, project context, and AI suggestions inside your repository.
83
+
84
+ ## Getting Started
85
+
86
+ Here are the main commands you can use:
87
+
88
+ - \`vibe init\` - Initialize VibeKit in your repository
89
+ - \`vibe new "Ticket title"\` - Create a new ticket
90
+ - \`vibe list\` - List all tickets
91
+ - \`vibe close TKT-XXX\` - Mark a ticket as done
92
+ - \`vibe get-started\` - Create sample tickets and documentation
93
+
94
+ ## Ticket Structure
95
+
96
+ Each ticket is a Markdown file with YAML frontmatter containing metadata like:
97
+ - id
98
+ - title
99
+ - status
100
+ - priority
101
+ - created_at
102
+ - updated_at
103
+
104
+ The body of the ticket contains sections for Description, Acceptance Criteria, Notes, and AI prompts.
105
+
106
+ ## Next Steps
107
+
108
+ Check out the sample tickets we've created for you to see how VibeKit can be used in your workflow.
109
+ `;
110
+
111
+ fs.writeFileSync(readmePath, readmeContent, "utf-8");
112
+ console.log("✅ Created README.md with getting started instructions");
113
+
114
+ // Create sample tickets
115
+ createSampleTicket(
116
+ "Simple Task Example",
117
+ "This is a simple task ticket example showing the basic structure.",
118
+ "low"
119
+ );
120
+
121
+ createSampleTicket(
122
+ "Bug Report Example",
123
+ "This ticket demonstrates how to report and track bugs using VibeKit.",
124
+ "high",
125
+ "in_progress"
126
+ );
127
+
128
+ createSampleTicket(
129
+ "Feature Request with AI Prompt",
130
+ "This example shows how to use the AI prompt section to get assistance with implementing a feature.",
131
+ "medium"
132
+ );
133
+
134
+ console.log("✅ Created sample tickets to demonstrate VibeKit features");
135
+ console.log("\n✨ You're all set! Try running 'vibe list' to see your tickets.\n");
136
+ }
137
+
138
+ export default getStartedCommand;