@vibedx/vibekit 0.3.1 ā 0.5.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 +24 -2
- package/assets/config.yml +3 -1
- package/assets/default.md +2 -0
- 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/list/index.js +68 -31
- package/src/commands/new/index.js +30 -15
- 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
|
@@ -84,11 +84,13 @@ vibe get-started
|
|
|
84
84
|
```bash
|
|
85
85
|
# Create a new ticket
|
|
86
86
|
vibe new "Fix login bug"
|
|
87
|
-
vibe new "Add dark mode" --priority high --
|
|
87
|
+
vibe new "Add dark mode" --priority high --assignee opusaku
|
|
88
|
+
vibe new "Quick task" --assignee mani-yadv -n # -n skips AI prompt
|
|
88
89
|
|
|
89
90
|
# List all tickets (with optional filtering)
|
|
90
91
|
vibe list
|
|
91
92
|
vibe list --status=open
|
|
93
|
+
vibe list --assignee=opusaku
|
|
92
94
|
|
|
93
95
|
# Close/complete a ticket
|
|
94
96
|
vibe close TKT-001
|
|
@@ -98,6 +100,22 @@ vibe start TKT-001
|
|
|
98
100
|
vibe start TKT-001 --base main --update-status
|
|
99
101
|
```
|
|
100
102
|
|
|
103
|
+
### š„ Team Management
|
|
104
|
+
```bash
|
|
105
|
+
# List team members
|
|
106
|
+
vibe team
|
|
107
|
+
|
|
108
|
+
# Add a member (stored in .vibe/team.yml)
|
|
109
|
+
vibe team add mani-yadv --name "Mani" --github mani-yadv --slack U0ABC123 --x vernon1943 --role Founder
|
|
110
|
+
vibe team add opusaku --name "Opus" --github opusaku --slack U0DEF456 --role "Senior Engineer"
|
|
111
|
+
|
|
112
|
+
# Show a member's details
|
|
113
|
+
vibe team show mani-yadv
|
|
114
|
+
|
|
115
|
+
# Remove a member
|
|
116
|
+
vibe team remove old-member
|
|
117
|
+
```
|
|
118
|
+
|
|
101
119
|
### š¤ AI Integration
|
|
102
120
|
```bash
|
|
103
121
|
# Connect Claude Code for ticket enhancement
|
|
@@ -281,6 +299,7 @@ VibeKit creates a `.vibe` directory in your project root:
|
|
|
281
299
|
```
|
|
282
300
|
š .vibe/
|
|
283
301
|
āāā š config.yml # Main configuration
|
|
302
|
+
āāā š„ team.yml # Team members (GitHub, Slack, X handles)
|
|
284
303
|
āāā š .templates/ # Ticket templates
|
|
285
304
|
ā āāā š default.md # Default ticket template
|
|
286
305
|
āāā š tickets/ # Your ticket files
|
|
@@ -316,10 +335,13 @@ tickets:
|
|
|
316
335
|
- high
|
|
317
336
|
- critical
|
|
318
337
|
|
|
338
|
+
# Team (stored in separate file for large teams)
|
|
339
|
+
team:
|
|
340
|
+
path: .vibe/team.yml
|
|
341
|
+
|
|
319
342
|
# AI integration
|
|
320
343
|
ai:
|
|
321
344
|
enabled: true
|
|
322
|
-
provider: claude-code
|
|
323
345
|
|
|
324
346
|
# Git integration
|
|
325
347
|
git:
|
package/assets/config.yml
CHANGED
package/assets/default.md
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
|
|
|
@@ -10,10 +10,13 @@ import { getTicketsDir } from '../../utils/index.js';
|
|
|
10
10
|
function listCommand(args) {
|
|
11
11
|
// Parse arguments for filtering
|
|
12
12
|
let statusFilter = null;
|
|
13
|
-
|
|
13
|
+
let assigneeFilter = null;
|
|
14
|
+
|
|
14
15
|
for (let i = 0; i < args.length; i++) {
|
|
15
16
|
if (args[i].startsWith("--status=")) {
|
|
16
17
|
statusFilter = args[i].split("=")[1];
|
|
18
|
+
} else if (args[i].startsWith("--assignee=") || args[i].startsWith("--owner=")) {
|
|
19
|
+
assigneeFilter = args[i].split("=")[1];
|
|
17
20
|
}
|
|
18
21
|
}
|
|
19
22
|
|
|
@@ -49,6 +52,8 @@ function listCommand(args) {
|
|
|
49
52
|
title: frontmatter.title || "Untitled",
|
|
50
53
|
status: frontmatter.status || "unknown",
|
|
51
54
|
priority: frontmatter.priority || "medium",
|
|
55
|
+
assignee: frontmatter.assignee || frontmatter.owner || "",
|
|
56
|
+
author: frontmatter.author || "",
|
|
52
57
|
file
|
|
53
58
|
});
|
|
54
59
|
}
|
|
@@ -57,10 +62,16 @@ function listCommand(args) {
|
|
|
57
62
|
}
|
|
58
63
|
}
|
|
59
64
|
|
|
60
|
-
// Filter tickets
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
65
|
+
// Filter tickets
|
|
66
|
+
let filteredTickets = tickets;
|
|
67
|
+
if (statusFilter) {
|
|
68
|
+
filteredTickets = filteredTickets.filter(ticket => ticket.status === statusFilter);
|
|
69
|
+
}
|
|
70
|
+
if (assigneeFilter) {
|
|
71
|
+
filteredTickets = filteredTickets.filter(ticket =>
|
|
72
|
+
ticket.assignee.toLowerCase() === assigneeFilter.toLowerCase()
|
|
73
|
+
);
|
|
74
|
+
}
|
|
64
75
|
|
|
65
76
|
// Sort tickets by ID
|
|
66
77
|
filteredTickets.sort((a, b) => {
|
|
@@ -79,20 +90,27 @@ function listCommand(args) {
|
|
|
79
90
|
// Display tickets in a formatted table
|
|
80
91
|
console.log("\n⨠VibeKit Tickets āØ\n");
|
|
81
92
|
|
|
82
|
-
// Calculate column widths
|
|
83
|
-
const idWidth = 10;
|
|
84
|
-
const statusWidth = 15;
|
|
85
|
-
const
|
|
86
|
-
|
|
93
|
+
// Calculate column widths
|
|
94
|
+
const idWidth = 10;
|
|
95
|
+
const statusWidth = 15;
|
|
96
|
+
const assigneeWidth = 14;
|
|
97
|
+
const titleWidth = 40;
|
|
98
|
+
|
|
99
|
+
// Check if any tickets have assignees
|
|
100
|
+
const hasAssignees = filteredTickets.some(t => t.assignee);
|
|
101
|
+
|
|
87
102
|
// Print header
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
"STATUS".padEnd(statusWidth)
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
103
|
+
if (hasAssignees) {
|
|
104
|
+
console.log(
|
|
105
|
+
`${"ID".padEnd(idWidth)}${"|"} ${"STATUS".padEnd(statusWidth)}${"|"} ${"ASSIGNEE".padEnd(assigneeWidth)}${"|"} TITLE`
|
|
106
|
+
);
|
|
107
|
+
console.log(`${'-'.repeat(idWidth)}+${'-'.repeat(statusWidth + 2)}+${'-'.repeat(assigneeWidth + 2)}+${'-'.repeat(titleWidth)}`);
|
|
108
|
+
} else {
|
|
109
|
+
console.log(
|
|
110
|
+
`${"ID".padEnd(idWidth)}${"|"} ${"STATUS".padEnd(statusWidth)}${"|"} TITLE`
|
|
111
|
+
);
|
|
112
|
+
console.log(`${'-'.repeat(idWidth)}+${'-'.repeat(statusWidth + 2)}+${'-'.repeat(titleWidth)}`);
|
|
113
|
+
}
|
|
96
114
|
|
|
97
115
|
for (const ticket of filteredTickets) {
|
|
98
116
|
let statusColor = "";
|
|
@@ -112,20 +130,39 @@ function listCommand(args) {
|
|
|
112
130
|
statusColor = "\x1b[0m"; // Default
|
|
113
131
|
}
|
|
114
132
|
|
|
115
|
-
// Format each row
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
133
|
+
// Format each row
|
|
134
|
+
const truncatedTitle = ticket.title.length > titleWidth - 3
|
|
135
|
+
? ticket.title.substring(0, titleWidth - 3) + "..."
|
|
136
|
+
: ticket.title;
|
|
137
|
+
|
|
138
|
+
if (hasAssignees) {
|
|
139
|
+
console.log(
|
|
140
|
+
`${ticket.id.padEnd(idWidth)}${"|"} ${
|
|
141
|
+
statusColor + ticket.status.padEnd(statusWidth - 1) + "\x1b[0m"
|
|
142
|
+
}${"|"} ${
|
|
143
|
+
(ticket.assignee || "").padEnd(assigneeWidth - 1)
|
|
144
|
+
}${"|"} ${truncatedTitle}`
|
|
145
|
+
);
|
|
146
|
+
} else {
|
|
147
|
+
console.log(
|
|
148
|
+
`${ticket.id.padEnd(idWidth)}${"|"} ${
|
|
149
|
+
statusColor + ticket.status.padEnd(statusWidth - 1) + "\x1b[0m"
|
|
150
|
+
}${"|"} ${truncatedTitle}`
|
|
151
|
+
);
|
|
152
|
+
}
|
|
125
153
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
154
|
+
|
|
155
|
+
if (hasAssignees) {
|
|
156
|
+
console.log(`${'-'.repeat(idWidth)}+${'-'.repeat(statusWidth + 2)}+${'-'.repeat(assigneeWidth + 2)}+${'-'.repeat(titleWidth)}`);
|
|
157
|
+
} else {
|
|
158
|
+
console.log(`${'-'.repeat(idWidth)}+${'-'.repeat(statusWidth + 2)}+${'-'.repeat(titleWidth)}`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
const filters = [
|
|
162
|
+
statusFilter ? `status: ${statusFilter}` : '',
|
|
163
|
+
assigneeFilter ? `assignee: ${assigneeFilter}` : '',
|
|
164
|
+
].filter(Boolean).join(', ');
|
|
165
|
+
console.log(`Found ${filteredTickets.length} ticket(s)${filters ? ` (${filters})` : ''}.\n`);
|
|
129
166
|
}
|
|
130
167
|
|
|
131
168
|
export default listCommand;
|
|
@@ -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
|
/**
|
|
@@ -32,28 +32,39 @@ function parseArguments(args) {
|
|
|
32
32
|
let titleParts = [];
|
|
33
33
|
let priority = DEFAULT_PRIORITY;
|
|
34
34
|
let status = DEFAULT_STATUS;
|
|
35
|
-
|
|
35
|
+
let assignee = '';
|
|
36
|
+
let author = '';
|
|
37
|
+
let noInteractive = false;
|
|
38
|
+
|
|
36
39
|
for (let i = 0; i < args.length; i++) {
|
|
37
40
|
const arg = args[i];
|
|
38
|
-
|
|
41
|
+
|
|
39
42
|
if (arg === '--priority' && i + 1 < args.length) {
|
|
40
43
|
priority = args[i + 1];
|
|
41
|
-
i++;
|
|
44
|
+
i++;
|
|
42
45
|
} else if (arg === '--status' && i + 1 < args.length) {
|
|
43
46
|
status = args[i + 1];
|
|
44
|
-
i++;
|
|
47
|
+
i++;
|
|
48
|
+
} else if ((arg === '--assignee' || arg === '--owner') && i + 1 < args.length) {
|
|
49
|
+
assignee = args[i + 1];
|
|
50
|
+
i++;
|
|
51
|
+
} else if (arg === '--author' && i + 1 < args.length) {
|
|
52
|
+
author = args[i + 1];
|
|
53
|
+
i++;
|
|
54
|
+
} else if (arg === '--no-interactive' || arg === '-n') {
|
|
55
|
+
noInteractive = true;
|
|
45
56
|
} else if (!arg.startsWith('--')) {
|
|
46
57
|
titleParts.push(arg);
|
|
47
58
|
}
|
|
48
59
|
}
|
|
49
|
-
|
|
60
|
+
|
|
50
61
|
const title = titleParts.join(' ').trim();
|
|
51
|
-
|
|
62
|
+
|
|
52
63
|
if (!title) {
|
|
53
64
|
throw new Error('Please provide a title for the new ticket.');
|
|
54
65
|
}
|
|
55
|
-
|
|
56
|
-
return { title, priority, status };
|
|
66
|
+
|
|
67
|
+
return { title, priority, status, assignee, author, noInteractive };
|
|
57
68
|
}
|
|
58
69
|
|
|
59
70
|
/**
|
|
@@ -93,16 +104,18 @@ function createTicketContent(template, ticketData) {
|
|
|
93
104
|
throw new Error('Template must be a string');
|
|
94
105
|
}
|
|
95
106
|
|
|
96
|
-
const { ticketId, title, slug, priority, status, timestamp } = ticketData;
|
|
107
|
+
const { ticketId, title, slug, priority, status, assignee, author, timestamp } = ticketData;
|
|
97
108
|
const paddedId = ticketId.replace('TKT-', '');
|
|
98
|
-
|
|
109
|
+
|
|
99
110
|
return template
|
|
100
111
|
.replace(/{id}/g, paddedId)
|
|
101
112
|
.replace(/{title}/g, title)
|
|
102
113
|
.replace(/{slug}/g, slug)
|
|
103
114
|
.replace(/{date}/g, timestamp)
|
|
104
115
|
.replace(/^priority: .*$/m, `priority: ${priority}`)
|
|
105
|
-
.replace(/^status: .*$/m, `status: ${status}`)
|
|
116
|
+
.replace(/^status: .*$/m, `status: ${status}`)
|
|
117
|
+
.replace(/^assignee: .*$/m, `assignee: "${assignee || ''}"`)
|
|
118
|
+
.replace(/^author: .*$/m, `author: "${author || ''}"`);
|
|
106
119
|
}
|
|
107
120
|
|
|
108
121
|
/**
|
|
@@ -193,7 +206,7 @@ async function handleFileRename(originalPath, ticketDir) {
|
|
|
193
206
|
async function newCommand(args) {
|
|
194
207
|
try {
|
|
195
208
|
// Parse and validate arguments
|
|
196
|
-
const { title, priority, status } = parseArguments(args);
|
|
209
|
+
const { title, priority, status, assignee, author, noInteractive } = parseArguments(args);
|
|
197
210
|
|
|
198
211
|
// Check required files and paths
|
|
199
212
|
const configPath = path.join(process.cwd(), '.vibe', 'config.yml');
|
|
@@ -236,6 +249,8 @@ async function newCommand(args) {
|
|
|
236
249
|
slug: ticketSlug,
|
|
237
250
|
priority: validatedOptions.priority,
|
|
238
251
|
status: validatedOptions.status,
|
|
252
|
+
assignee,
|
|
253
|
+
author,
|
|
239
254
|
timestamp
|
|
240
255
|
};
|
|
241
256
|
|
|
@@ -254,8 +269,8 @@ async function newCommand(args) {
|
|
|
254
269
|
|
|
255
270
|
logger.success(`Created ticket: ${filename} (priority: ${ticketData.priority}, status: ${ticketData.status})`);
|
|
256
271
|
|
|
257
|
-
// Offer AI enhancement if available
|
|
258
|
-
if (checkAiEnabled(config)) {
|
|
272
|
+
// Offer AI enhancement if available (skip in non-interactive mode)
|
|
273
|
+
if (checkAiEnabled(config) && !noInteractive) {
|
|
259
274
|
await offerAiEnhancement(ticketId, outputPath, ticketDir);
|
|
260
275
|
}
|
|
261
276
|
|
|
@@ -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
|