specdrive-cli 0.1.1 → 0.1.2

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 (3) hide show
  1. package/README.md +23 -3
  2. package/package.json +1 -1
  3. package/src/index.js +107 -4
package/README.md CHANGED
@@ -327,6 +327,10 @@ Audits and manages AI skills.
327
327
  | `sdrive status` | Show feature lifecycle state |
328
328
  | `sdrive approve <gate>` | Approve a workflow gate |
329
329
  | `sdrive create <feature>` | Create feature scaffold (internal) |
330
+ | `sdrive team:add <username> <role>` | Add a new team member |
331
+ | `sdrive team:remove <username>` | Remove a team member |
332
+ | `sdrive team:list` | List all team members |
333
+ | `sdrive team:update <username> <role>` | Update a member's role |
330
334
 
331
335
  ---
332
336
 
@@ -428,13 +432,29 @@ jobs:
428
432
  | Cursor | `.cursor/rules/sdrive.md` |
429
433
  | VS Code | `.vscode/sdrive-commands.json` |
430
434
 
431
- Run the generator:
435
+ ```
436
+
437
+ ---
438
+
439
+ ## Team Collaboration
440
+
441
+ SpecDrive supports team roles.
442
+
443
+ | Role | Can approve | Can implement |
444
+ |------|-------------|---------------|
445
+ | admin | ✅ | ✅ |
446
+ | reviewer | ✅ | ❌ |
447
+ | developer | ❌ | ✅ |
448
+
449
+ Manage team:
432
450
 
433
451
  ```bash
434
- node .sdrive/scripts/generate-adapters.js
452
+ sdrive team:add alice admin
453
+ sdrive team:list
454
+ sdrive team:update bob reviewer
455
+ sdrive team:remove carol
435
456
  ```
436
457
 
437
- ---
438
458
 
439
459
  ## Contributing
440
460
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specdrive-cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.2",
4
4
  "description": "SpecDrive enterprise spec-driven development CLI",
5
5
  "bin": {
6
6
  "sdrive": "src/index.js"
package/src/index.js CHANGED
@@ -20,7 +20,6 @@ program
20
20
  const projectRoot = process.cwd();
21
21
  const sdriveRoot = path.join(projectRoot, ".sdrive");
22
22
 
23
- // Create .sdrive directory structure
24
23
  const dirs = [
25
24
  path.join(sdriveRoot, "agents"),
26
25
  path.join(sdriveRoot, "commands"),
@@ -44,21 +43,18 @@ program
44
43
  console.log(` Created: ${path.relative(projectRoot, dir)}`);
45
44
  });
46
45
 
47
- // Create workflow-state.json if missing
48
46
  const workflowStatePath = path.join(sdriveRoot, "workflow-state.json");
49
47
  if (!fs.existsSync(workflowStatePath)) {
50
48
  fs.writeFileSync(workflowStatePath, JSON.stringify({ features: [] }, null, 2));
51
49
  console.log(" Created: .sdrive/workflow-state.json");
52
50
  }
53
51
 
54
- // Create constitution.md if missing
55
52
  const constitutionPath = path.join(sdriveRoot, "constitution.md");
56
53
  if (!fs.existsSync(constitutionPath)) {
57
54
  fs.writeFileSync(constitutionPath, "# SpecDrive Constitution\n\nInitialized by `sdrive init`.\n");
58
55
  console.log(" Created: .sdrive/constitution.md");
59
56
  }
60
57
 
61
- // Generate tool adapters
62
58
  const generatorPath = path.join(sdriveRoot, "scripts", "generate-adapters.js");
63
59
  if (fs.existsSync(generatorPath)) {
64
60
  try {
@@ -232,4 +228,111 @@ program
232
228
  console.log(`✅ Gate "${gate}" approved for feature "${feature.name}".`);
233
229
  });
234
230
 
231
+ // ---------- TEAM COMMANDS ----------
232
+
233
+ program
234
+ .command("team:add <username> <role>")
235
+ .description("Add a new team member")
236
+ .action((username, role) => {
237
+ const teamPath = path.join(process.cwd(), ".sdrive", "team.json");
238
+
239
+ if (!fs.existsSync(teamPath)) {
240
+ console.error("❌ team.json not found. Run `sdrive init` first.");
241
+ process.exit(1);
242
+ }
243
+
244
+ const team = JSON.parse(fs.readFileSync(teamPath, "utf8"));
245
+
246
+ if (!team.members) {
247
+ team.members = [];
248
+ }
249
+
250
+ const existing = team.members.find((m) => m.username === username);
251
+ if (existing) {
252
+ console.error(`❌ User "${username}" already exists. Use team:update to change role.`);
253
+ process.exit(1);
254
+ }
255
+
256
+ team.members.push({ username, role });
257
+ fs.writeFileSync(teamPath, JSON.stringify(team, null, 2));
258
+
259
+ console.log(`✅ Added ${username} as ${role}.`);
260
+ });
261
+
262
+ program
263
+ .command("team:remove <username>")
264
+ .description("Remove a team member")
265
+ .action((username) => {
266
+ const teamPath = path.join(process.cwd(), ".sdrive", "team.json");
267
+
268
+ if (!fs.existsSync(teamPath)) {
269
+ console.error("❌ team.json not found. Run `sdrive init` first.");
270
+ process.exit(1);
271
+ }
272
+
273
+ const team = JSON.parse(fs.readFileSync(teamPath, "utf8"));
274
+
275
+ const before = team.members?.length || 0;
276
+ team.members = (team.members || []).filter((m) => m.username !== username);
277
+ const after = team.members.length;
278
+
279
+ if (before === after) {
280
+ console.error(`❌ User "${username}" not found.`);
281
+ process.exit(1);
282
+ }
283
+
284
+ fs.writeFileSync(teamPath, JSON.stringify(team, null, 2));
285
+ console.log(`✅ Removed ${username}.`);
286
+ });
287
+
288
+ program
289
+ .command("team:list")
290
+ .description("List all team members")
291
+ .action(() => {
292
+ const teamPath = path.join(process.cwd(), ".sdrive", "team.json");
293
+
294
+ if (!fs.existsSync(teamPath)) {
295
+ console.error("❌ team.json not found. Run `sdrive init` first.");
296
+ process.exit(1);
297
+ }
298
+
299
+ const team = JSON.parse(fs.readFileSync(teamPath, "utf8"));
300
+
301
+ if (!team.members || team.members.length === 0) {
302
+ console.log("No team members found.");
303
+ return;
304
+ }
305
+
306
+ console.log("\nSpecDrive Team Members\n");
307
+ team.members.forEach((m) => {
308
+ console.log(`👤 ${m.username} — ${m.role}`);
309
+ });
310
+ });
311
+
312
+ program
313
+ .command("team:update <username> <role>")
314
+ .description("Update a team member's role")
315
+ .action((username, role) => {
316
+ const teamPath = path.join(process.cwd(), ".sdrive", "team.json");
317
+
318
+ if (!fs.existsSync(teamPath)) {
319
+ console.error("❌ team.json not found. Run `sdrive init` first.");
320
+ process.exit(1);
321
+ }
322
+
323
+ const team = JSON.parse(fs.readFileSync(teamPath, "utf8"));
324
+
325
+ const member = team.members?.find((m) => m.username === username);
326
+
327
+ if (!member) {
328
+ console.error(`❌ User "${username}" not found.`);
329
+ process.exit(1);
330
+ }
331
+
332
+ member.role = role;
333
+ fs.writeFileSync(teamPath, JSON.stringify(team, null, 2));
334
+
335
+ console.log(`✅ Updated ${username} to ${role}.`);
336
+ });
337
+
235
338
  program.parse(process.argv);