specdrive-cli 0.1.1 → 0.1.3

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 +33 -3
  2. package/package.json +1 -1
  3. package/src/index.js +107 -4
package/README.md CHANGED
@@ -21,6 +21,7 @@ It adds:
21
21
  - [Why SpecDrive](#why-specdrive)
22
22
  - [Prerequisites](#prerequisites)
23
23
  - [Installation](#installation)
24
+ - [Where to Run Commands](#where-to-run-commands)
24
25
  - [Quickstart](#quickstart)
25
26
  - [Core Workflow](#core-workflow)
26
27
  - [Optional Commands](#optional-commands)
@@ -85,7 +86,16 @@ sdrive init
85
86
  This creates the `.sdrive/` folder and tool adapters.
86
87
 
87
88
  ---
89
+ ## Where to Run Commands
88
90
 
91
+ | Command Type | Where to Run | Example |
92
+ |--------------|--------------|---------|
93
+ | Terminal command | In your terminal / shell | `sdrive init` |
94
+ | Slash command | In your AI tool's chat | `/sdrive:propose "User login"` |
95
+
96
+ **Rule:** If it starts with `sdrive`, run it in the terminal. If it starts with `/sdrive:`, run it in your AI chat.
97
+
98
+ ---
89
99
  ## Quickstart
90
100
 
91
101
  1. Initialize the project:
@@ -327,6 +337,10 @@ Audits and manages AI skills.
327
337
  | `sdrive status` | Show feature lifecycle state |
328
338
  | `sdrive approve <gate>` | Approve a workflow gate |
329
339
  | `sdrive create <feature>` | Create feature scaffold (internal) |
340
+ | `sdrive team:add <username> <role>` | Add a new team member |
341
+ | `sdrive team:remove <username>` | Remove a team member |
342
+ | `sdrive team:list` | List all team members |
343
+ | `sdrive team:update <username> <role>` | Update a member's role |
330
344
 
331
345
  ---
332
346
 
@@ -428,13 +442,29 @@ jobs:
428
442
  | Cursor | `.cursor/rules/sdrive.md` |
429
443
  | VS Code | `.vscode/sdrive-commands.json` |
430
444
 
431
- Run the generator:
445
+ ```
446
+
447
+ ---
448
+
449
+ ## Team Collaboration
450
+
451
+ SpecDrive supports team roles.
452
+
453
+ | Role | Can approve | Can implement |
454
+ |------|-------------|---------------|
455
+ | admin | ✅ | ✅ |
456
+ | reviewer | ✅ | ❌ |
457
+ | developer | ❌ | ✅ |
458
+
459
+ Manage team:
432
460
 
433
461
  ```bash
434
- node .sdrive/scripts/generate-adapters.js
462
+ sdrive team:add alice admin
463
+ sdrive team:list
464
+ sdrive team:update bob reviewer
465
+ sdrive team:remove carol
435
466
  ```
436
467
 
437
- ---
438
468
 
439
469
  ## Contributing
440
470
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specdrive-cli",
3
- "version": "0.1.1",
3
+ "version": "0.1.3",
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);