@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 +34 -2
- package/assets/config.yml +3 -1
- package/assets/team.yml +22 -0
- package/index.js +2 -2
- package/package.json +1 -1
- package/src/commands/init/index.js +3 -1
- package/src/commands/link/index.js +0 -1
- package/src/commands/lint/index.js +6 -1
- package/src/commands/new/index.js +1 -1
- package/src/commands/refine/index.js +1 -1
- package/src/commands/team/index.js +196 -0
- package/src/commands/unlink/index.js +0 -3
- package/src/utils/test-helpers.js +0 -3
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 --
|
|
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
package/assets/team.yml
ADDED
|
@@ -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
|
@@ -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
|
|
|
@@ -87,7 +87,12 @@ function getRequiredFrontmatter(config) {
|
|
|
87
87
|
}
|
|
88
88
|
|
|
89
89
|
const frontmatter = yaml.load(parts[1]);
|
|
90
|
-
|
|
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
|
|
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
|
|
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
|