@vibedx/vibekit 0.4.0 → 0.6.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
@@ -41,6 +41,16 @@ vibe new "Add user authentication"
41
41
  vibe start TKT-001
42
42
  ```
43
43
 
44
+ ### šŸ¤– Use with AI Agents (skills.sh)
45
+
46
+ Install the vibekit skill so AI coding agents (Claude Code, Cursor, Codex, etc.) know how to use it:
47
+
48
+ ```bash
49
+ npx skills add vibedx/vibekit
50
+ ```
51
+
52
+ The skill teaches agents the ticket-driven workflow — they'll create focused tickets before writing code, track work through git branches, and keep tickets as living documentation.
53
+
44
54
  ## šŸ¤” Why VibeKit?
45
55
 
46
56
  - **šŸŽÆ Vibe code with manageable smaller tasks** - Break down complex features into focused tickets
@@ -84,11 +94,13 @@ vibe get-started
84
94
  ```bash
85
95
  # Create a new ticket
86
96
  vibe new "Fix login bug"
87
- vibe new "Add dark mode" --priority high --status open
97
+ vibe new "Add dark mode" --priority high --assignee opusaku
98
+ vibe new "Quick task" --assignee mani-yadv -n # -n skips AI prompt
88
99
 
89
100
  # List all tickets (with optional filtering)
90
101
  vibe list
91
102
  vibe list --status=open
103
+ vibe list --assignee=opusaku
92
104
 
93
105
  # Close/complete a ticket
94
106
  vibe close TKT-001
@@ -98,6 +110,22 @@ vibe start TKT-001
98
110
  vibe start TKT-001 --base main --update-status
99
111
  ```
100
112
 
113
+ ### šŸ‘„ Team Management
114
+ ```bash
115
+ # List team members
116
+ vibe team
117
+
118
+ # Add a member (stored in .vibe/team.yml)
119
+ vibe team add mani-yadv --name "Mani" --github mani-yadv --slack U0ABC123 --x vernon1943 --role Founder
120
+ vibe team add opusaku --name "Opus" --github opusaku --slack U0DEF456 --role "Senior Engineer"
121
+
122
+ # Show a member's details
123
+ vibe team show mani-yadv
124
+
125
+ # Remove a member
126
+ vibe team remove old-member
127
+ ```
128
+
101
129
  ### šŸ¤– AI Integration
102
130
  ```bash
103
131
  # Connect Claude Code for ticket enhancement
@@ -281,6 +309,7 @@ VibeKit creates a `.vibe` directory in your project root:
281
309
  ```
282
310
  šŸ“ .vibe/
283
311
  ā”œā”€ā”€ šŸ“‹ config.yml # Main configuration
312
+ ā”œā”€ā”€ šŸ‘„ team.yml # Team members (GitHub, Slack, X handles)
284
313
  ā”œā”€ā”€ šŸ“ .templates/ # Ticket templates
285
314
  │ └── šŸ“„ default.md # Default ticket template
286
315
  ā”œā”€ā”€ šŸ“ tickets/ # Your ticket files
@@ -316,10 +345,13 @@ tickets:
316
345
  - high
317
346
  - critical
318
347
 
348
+ # Team (stored in separate file for large teams)
349
+ team:
350
+ path: .vibe/team.yml
351
+
319
352
  # AI integration
320
353
  ai:
321
354
  enabled: true
322
- provider: claude-code
323
355
 
324
356
  # Git integration
325
357
  git:
package/assets/config.yml CHANGED
@@ -21,9 +21,11 @@ tickets:
21
21
  - high
22
22
  - critical
23
23
 
24
+ team:
25
+ path: .vibe/team.yml
26
+
24
27
  ai:
25
28
  enabled: false
26
- provider: "none"
27
29
 
28
30
  hooks:
29
31
  pre_commit: false
@@ -0,0 +1,22 @@
1
+ # VibeKit Team Configuration
2
+ # Add team members here. Use their ID as the assignee value in tickets.
3
+ #
4
+ # Usage:
5
+ # vibe team - List all members
6
+ # vibe team add <id> --name "Name" ... - Add a member
7
+ # vibe team remove <id> - Remove a member
8
+ # vibe new "task" --assignee <id> - Assign ticket to member
9
+
10
+ members: {}
11
+ # Example:
12
+ # mani-yadv:
13
+ # name: Mani
14
+ # github: mani-yadv
15
+ # slack: U0XXXXXXXX
16
+ # x: vernon1943
17
+ # role: Founder
18
+ # opusaku:
19
+ # name: Opus
20
+ # github: opusaku
21
+ # slack: U0XXXXXXXX
22
+ # role: Senior Engineer
package/index.js CHANGED
@@ -22,8 +22,8 @@ const __dirname = dirname(__filename);
22
22
 
23
23
  // Available commands in VibeKit
24
24
  const AVAILABLE_COMMANDS = [
25
- 'init', 'new', 'close', 'list', 'get-started',
26
- 'start', 'link', 'unlink', 'refine', 'lint', 'review'
25
+ 'init', 'new', 'close', 'list', 'get-started',
26
+ 'start', 'link', 'unlink', 'refine', 'lint', 'review', 'team'
27
27
  ];
28
28
 
29
29
  /**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vibedx/vibekit",
3
- "version": "0.4.0",
3
+ "version": "0.6.0",
4
4
  "description": "A powerful CLI tool for managing development tickets and project workflows",
5
5
  "main": "index.js",
6
6
  "type": "module",
@@ -23,7 +23,8 @@ function initCommand(args) {
23
23
  // Use real files instead of hardcoded template strings
24
24
  const templateSrc = path.join(__dirname, "../../../assets", "default.md");
25
25
  const configSrc = path.join(__dirname, "../../../assets", "config.yml");
26
-
26
+ const teamSrc = path.join(__dirname, "../../../assets", "team.yml");
27
+
27
28
  fs.mkdirSync(targetFolder, { recursive: true });
28
29
  fs.mkdirSync(path.join(targetFolder, "tickets"), { recursive: true });
29
30
  fs.mkdirSync(path.join(targetFolder, ".templates"), { recursive: true });
@@ -31,6 +32,7 @@ function initCommand(args) {
31
32
  // Copy files from assets directory instead of using hardcoded templates
32
33
  fs.copyFileSync(configSrc, path.join(targetFolder, "config.yml"));
33
34
  fs.copyFileSync(templateSrc, path.join(targetFolder, ".templates", "default.md"));
35
+ fs.copyFileSync(teamSrc, path.join(targetFolder, "team.yml"));
34
36
 
35
37
  console.log(`āœ… '${targetFolder}' initialized with config, tickets/, and .templates/default.md`);
36
38
 
@@ -162,7 +162,6 @@ async function linkCommand() {
162
162
  config.ai = {
163
163
  ...config.ai,
164
164
  enabled: true,
165
- provider: 'claude-code',
166
165
  };
167
166
 
168
167
  if (!saveConfig(config)) {
@@ -87,7 +87,12 @@ function getRequiredFrontmatter(config) {
87
87
  }
88
88
 
89
89
  const frontmatter = yaml.load(parts[1]);
90
- return Object.keys(frontmatter || {});
90
+ // Only require fields that have non-empty default values in the template
91
+ // Fields like assignee: "" and author: "" are optional
92
+ return Object.keys(frontmatter || {}).filter(key => {
93
+ const val = frontmatter[key];
94
+ return val !== '' && val !== null && val !== undefined;
95
+ });
91
96
  } catch (error) {
92
97
  // Fallback to default required fields
93
98
  return ['id', 'title', 'slug', 'status', 'priority', 'created_at', 'updated_at'];
@@ -16,7 +16,7 @@ const GIT_STATUS_CHECK_TIMEOUT = 5000;
16
16
  * @returns {boolean} True if AI is enabled
17
17
  */
18
18
  function checkAiEnabled(config) {
19
- return config && config.ai && config.ai.enabled && config.ai.provider !== 'none';
19
+ return config && config.ai && config.ai.enabled;
20
20
  }
21
21
 
22
22
  /**
@@ -39,7 +39,7 @@ function loadConfig() {
39
39
  function checkAiConfiguration() {
40
40
  const config = loadConfig();
41
41
 
42
- if (!config.ai?.enabled || config.ai?.provider === 'none') {
42
+ if (!config.ai?.enabled) {
43
43
  return {
44
44
  configured: false,
45
45
  reason: 'AI is not enabled. Run "vibe link" first.'
@@ -0,0 +1,196 @@
1
+ import fs from 'fs';
2
+ import path from 'path';
3
+ import yaml from 'js-yaml';
4
+ import { getConfig } from '../../utils/index.js';
5
+
6
+ /**
7
+ * Get path to team.yml (from config or default)
8
+ */
9
+ function getTeamPath() {
10
+ const config = getConfig();
11
+ const teamPath = config.team?.path || '.vibe/team.yml';
12
+ return path.resolve(process.cwd(), teamPath);
13
+ }
14
+
15
+ /**
16
+ * Load team data from team.yml
17
+ */
18
+ function loadTeam() {
19
+ const teamPath = getTeamPath();
20
+ try {
21
+ if (fs.existsSync(teamPath)) {
22
+ const content = fs.readFileSync(teamPath, 'utf-8');
23
+ const data = yaml.load(content) || {};
24
+ return data.members || {};
25
+ }
26
+ } catch (error) {
27
+ console.error(`āŒ Error reading team file: ${error.message}`);
28
+ }
29
+ return {};
30
+ }
31
+
32
+ /**
33
+ * Save team data to team.yml
34
+ */
35
+ function saveTeam(members) {
36
+ const teamPath = getTeamPath();
37
+ const dir = path.dirname(teamPath);
38
+ if (!fs.existsSync(dir)) {
39
+ fs.mkdirSync(dir, { recursive: true });
40
+ }
41
+ const content = yaml.dump({ members }, { lineWidth: -1 });
42
+ fs.writeFileSync(teamPath, content, 'utf-8');
43
+ }
44
+
45
+ /**
46
+ * Manage team members
47
+ * Usage:
48
+ * vibe team - List all team members
49
+ * vibe team add <id> --name "Name" --github <gh> --slack <slack>
50
+ * vibe team remove <id>
51
+ * vibe team show <id>
52
+ */
53
+ async function teamCommand(args) {
54
+ const subcommand = args[0];
55
+
56
+ if (!subcommand || subcommand === 'list') {
57
+ return listTeam();
58
+ }
59
+ if (subcommand === 'add') {
60
+ return addMember(args.slice(1));
61
+ }
62
+ if (subcommand === 'remove') {
63
+ return removeMember(args[1]);
64
+ }
65
+ if (subcommand === 'show') {
66
+ return showMember(args[1]);
67
+ }
68
+ return showMember(subcommand);
69
+ }
70
+
71
+ function listTeam() {
72
+ const team = loadTeam();
73
+ const members = Object.entries(team);
74
+
75
+ if (members.length === 0) {
76
+ console.log('\nNo team members configured.\n');
77
+ console.log('Add members with: vibe team add <id> --name "Name" --github <gh> --slack <slack-id>\n');
78
+ return;
79
+ }
80
+
81
+ console.log('\n✨ Team Members ✨\n');
82
+
83
+ const idWidth = 16;
84
+ const nameWidth = 16;
85
+ const githubWidth = 16;
86
+ const slackWidth = 14;
87
+
88
+ console.log(
89
+ `${'ID'.padEnd(idWidth)}| ${'NAME'.padEnd(nameWidth)}| ${'GITHUB'.padEnd(githubWidth)}| SLACK`
90
+ );
91
+ console.log(`${'-'.repeat(idWidth)}+${'-'.repeat(nameWidth + 1)}+${'-'.repeat(githubWidth + 1)}+${'-'.repeat(slackWidth)}`);
92
+
93
+ for (const [id, member] of members) {
94
+ if (typeof member !== 'object') continue;
95
+ console.log(
96
+ `${id.padEnd(idWidth)}| ${(member.name || '').padEnd(nameWidth)}| ${(member.github || '').padEnd(githubWidth)}| ${member.slack || ''}`
97
+ );
98
+ }
99
+
100
+ console.log(`\nFound ${members.length} member(s).\n`);
101
+ }
102
+
103
+ function addMember(args) {
104
+ if (!args[0]) {
105
+ console.error('āŒ Please provide a member ID. Usage: vibe team add <id> --name "Name" --github <gh> --slack <slack>');
106
+ process.exit(1);
107
+ }
108
+
109
+ const id = args[0];
110
+ const member = {};
111
+
112
+ for (let i = 1; i < args.length; i++) {
113
+ if (args[i] === '--name' && args[i + 1]) {
114
+ member.name = args[++i];
115
+ } else if (args[i] === '--github' && args[i + 1]) {
116
+ member.github = args[++i];
117
+ } else if (args[i] === '--slack' && args[i + 1]) {
118
+ member.slack = args[++i];
119
+ } else if (args[i] === '--x' && args[i + 1]) {
120
+ member.x = args[++i];
121
+ } else if (args[i] === '--role' && args[i + 1]) {
122
+ member.role = args[++i];
123
+ }
124
+ }
125
+
126
+ const team = loadTeam();
127
+ team[id] = { ...team[id], ...member };
128
+ saveTeam(team);
129
+
130
+ console.log(`āœ… Added team member: ${id}`);
131
+ if (member.name) console.log(` Name: ${member.name}`);
132
+ if (member.github) console.log(` GitHub: ${member.github}`);
133
+ if (member.slack) console.log(` Slack: ${member.slack}`);
134
+ if (member.x) console.log(` X: ${member.x}`);
135
+ if (member.role) console.log(` Role: ${member.role}`);
136
+ }
137
+
138
+ function removeMember(id) {
139
+ if (!id) {
140
+ console.error('āŒ Please provide a member ID. Usage: vibe team remove <id>');
141
+ process.exit(1);
142
+ }
143
+
144
+ const team = loadTeam();
145
+ if (!team[id]) {
146
+ console.error(`āŒ Member '${id}' not found.`);
147
+ process.exit(1);
148
+ }
149
+
150
+ delete team[id];
151
+ saveTeam(team);
152
+ console.log(`āœ… Removed team member: ${id}`);
153
+ }
154
+
155
+ function showMember(id) {
156
+ if (!id) return listTeam();
157
+
158
+ const team = loadTeam();
159
+ const member = team[id];
160
+
161
+ if (!member) {
162
+ console.error(`āŒ Member '${id}' not found.`);
163
+ process.exit(1);
164
+ }
165
+
166
+ console.log(`\nšŸ“‹ ${id}`);
167
+ if (member.name) console.log(` Name: ${member.name}`);
168
+ if (member.github) console.log(` GitHub: @${member.github}`);
169
+ if (member.slack) console.log(` Slack: <@${member.slack}>`);
170
+ if (member.x) console.log(` X: @${member.x}`);
171
+ if (member.role) console.log(` Role: ${member.role}`);
172
+ console.log();
173
+ }
174
+
175
+ /**
176
+ * Resolve an assignee ID to their team info.
177
+ * Exported for use by other commands and external tools.
178
+ */
179
+ export function resolveAssignee(assigneeId) {
180
+ const team = loadTeam();
181
+ return team[assigneeId] || null;
182
+ }
183
+
184
+ /**
185
+ * Get Slack mention string for an assignee.
186
+ * Returns <@SLACK_ID> if found, or the raw assignee string.
187
+ */
188
+ export function getSlackMention(assigneeId) {
189
+ const member = resolveAssignee(assigneeId);
190
+ if (member?.slack) {
191
+ return `<@${member.slack}>`;
192
+ }
193
+ return assigneeId;
194
+ }
195
+
196
+ export default teamCommand;
@@ -83,8 +83,6 @@ async function unlinkCommand() {
83
83
 
84
84
  // Show current configuration
85
85
  console.log('šŸ“ Current AI configuration:');
86
- console.log(` Provider: ${config.ai.provider === 'claude-code' ? 'Claude Code (Anthropic API)' : config.ai.provider}`);
87
- console.log(` Model: ${config.ai.model || 'Not specified'}`);
88
86
  console.log(` Status: ${config.ai.enabled ? 'Enabled' : 'Disabled'}`);
89
87
  console.log();
90
88
 
@@ -101,7 +99,6 @@ async function unlinkCommand() {
101
99
  config.ai = {
102
100
  ...config.ai,
103
101
  enabled: false,
104
- provider: 'none'
105
102
  };
106
103
 
107
104
  if (saveConfig(config)) {
@@ -111,7 +111,6 @@ export function createMockVibeProject(baseDir, options = {}) {
111
111
  },
112
112
  ai: {
113
113
  enabled: false,
114
- provider: 'none'
115
114
  }
116
115
  };
117
116
 
@@ -141,7 +140,6 @@ git:
141
140
 
142
141
  ai:
143
142
  enabled: false
144
- provider: none
145
143
  `;
146
144
 
147
145
  fs.writeFileSync(configPath, configContent, 'utf-8');
@@ -460,7 +458,6 @@ git:
460
458
 
461
459
  ai:
462
460
  enabled: false
463
- provider: none
464
461
  `;
465
462
 
466
463
  // Create mock default.md template