specdrive-cli 0.1.2 → 0.1.8

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
@@ -1,3 +1,5 @@
1
+
2
+ ```markdown
1
3
  # SpecDrive
2
4
 
3
5
  **Enterprise Spec-Driven Development for AI Coding Tools**
@@ -21,6 +23,7 @@ It adds:
21
23
  - [Why SpecDrive](#why-specdrive)
22
24
  - [Prerequisites](#prerequisites)
23
25
  - [Installation](#installation)
26
+ - [Where to Run Commands](#where-to-run-commands)
24
27
  - [Quickstart](#quickstart)
25
28
  - [Core Workflow](#core-workflow)
26
29
  - [Optional Commands](#optional-commands)
@@ -30,6 +33,7 @@ It adds:
30
33
  - [Validation](#validation)
31
34
  - [CI/CD Integration](#cicd-integration)
32
35
  - [Tool Support](#tool-support)
36
+ - [Team Collaboration](#team-collaboration)
33
37
  - [Contributing](#contributing)
34
38
  - [License](#license)
35
39
 
@@ -82,7 +86,20 @@ Inside your project root, run:
82
86
  sdrive init
83
87
  ```
84
88
 
85
- This creates the `.sdrive/` folder and tool adapters.
89
+ SpecDrive will ask you to select which AI tools you use.
90
+
91
+ It then creates the `.sdrive/` folder and tool adapters for the selected tools.
92
+
93
+ ---
94
+
95
+ ## Where to Run Commands
96
+
97
+ | Command Type | Where to Run | Example |
98
+ |--------------|--------------|---------|
99
+ | Terminal command | In your terminal / shell | `sdrive init` |
100
+ | Slash command | In your AI tool's chat | `/sdrive:propose "User login"` |
101
+
102
+ **Rule:** If it starts with `sdrive`, run it in the terminal. If it starts with `/sdrive:`, run it in your AI chat.
86
103
 
87
104
  ---
88
105
 
@@ -424,6 +441,8 @@ jobs:
424
441
 
425
442
  ## Tool Support
426
443
 
444
+ SpecDrive supports 15 AI tools.
445
+
427
446
  | Tool | Adapter |
428
447
  |------|---------|
429
448
  | GitHub Copilot | `.github/copilot-instructions.md` |
@@ -431,8 +450,20 @@ jobs:
431
450
  | Claude Code | `.claude/commands/sdrive.md` |
432
451
  | Cursor | `.cursor/rules/sdrive.md` |
433
452
  | VS Code | `.vscode/sdrive-commands.json` |
453
+ | Amazon Q Developer | `.amazonq/rules/sdrive.md` |
454
+ | Gemini CLI | `.gemini/commands/sdrive.md` |
455
+ | Continue | `.continue/rules/sdrive.md` |
456
+ | Codex | `.codex/rules/sdrive.md` |
457
+ | Windsurf | `.windsurf/rules/sdrive.md` |
458
+ | Kilo Code | `.kilocode/rules/sdrive.md` |
459
+ | OpenCode | `.opencode/rules/sdrive.md` |
460
+ | Qoder | `.qoder/rules/sdrive.md` |
461
+ | Qwen Code | `.qwen/rules/sdrive.md` |
462
+ | Rovo Dev CLI | `.rovo/rules/sdrive.md` |
434
463
 
435
- ```
464
+ Adapters are generated automatically when you run `sdrive init`.
465
+
466
+ During initialization, you select which tools you use. Only selected tools get adapters.
436
467
 
437
468
  ---
438
469
 
@@ -455,6 +486,7 @@ sdrive team:update bob reviewer
455
486
  sdrive team:remove carol
456
487
  ```
457
488
 
489
+ ---
458
490
 
459
491
  ## Contributing
460
492
 
@@ -471,15 +503,4 @@ MIT
471
503
 
472
504
  ---
473
505
 
474
- This is now a complete, professional README with:
475
-
476
- - Table of contents
477
- - Installation steps
478
- - Core workflow in correct order
479
- - Detailed optional commands
480
- - Agent reference
481
- - Directory structure
482
- - Validation and CI/CD sections
483
- - Tool support
484
-
485
- You can now save it as `README.md`.
506
+ This README is now clean and correct.
package/commands/gates.js CHANGED
@@ -7,7 +7,7 @@ function getWorkflowState() {
7
7
  const workflowStatePath = path.join(projectRoot, ".sdrive", "workflow-state.json");
8
8
 
9
9
  if (!fs.existsSync(workflowStatePath)) {
10
- console.error("❌ workflow-state.json not found.");
10
+ console.error("❌ workflow-state.json not found. Run `sdrive init` first.");
11
11
  process.exit(1);
12
12
  }
13
13
 
@@ -19,14 +19,28 @@ function saveWorkflowState(state) {
19
19
  fs.writeFileSync(workflowStatePath, JSON.stringify(state, null, 2));
20
20
  }
21
21
 
22
- function approveGate(featureName, gate) {
22
+ function getFeature(featureName) {
23
23
  const state = getWorkflowState();
24
+ return state.features?.find((f) => f.name === featureName);
25
+ }
26
+
27
+ function gateStatus(featureName, gate) {
28
+ const feature = getFeature(featureName);
24
29
 
25
- const feature = state.features?.find((f) => f.name === featureName);
30
+ if (!feature) {
31
+ return "unknown";
32
+ }
33
+
34
+ return feature.gates?.[gate] || "pending";
35
+ }
36
+
37
+ function approveGate(featureName, gate) {
38
+ const state = getWorkflowState();
39
+ const feature = getFeature(featureName);
26
40
 
27
41
  if (!feature) {
28
42
  console.error(`❌ Feature "${featureName}" not found.`);
29
- process.exit(1);
43
+ return false;
30
44
  }
31
45
 
32
46
  if (!feature.gates) {
@@ -43,22 +57,18 @@ function approveGate(featureName, gate) {
43
57
 
44
58
  saveWorkflowState(state);
45
59
  console.log(`✅ Gate "${gate}" approved for feature "${featureName}".`);
60
+ return true;
46
61
  }
47
62
 
48
- function gateStatus(featureName, gate) {
49
- const state = getWorkflowState();
50
- const feature = state.features?.find((f) => f.name === featureName);
51
-
52
- if (!feature) {
53
- return "unknown";
54
- }
55
-
56
- return feature.gates?.[gate] || "pending";
63
+ function isGateApproved(featureName, gate) {
64
+ return gateStatus(featureName, gate) === "approved";
57
65
  }
58
66
 
59
67
  module.exports = {
60
- approveGate,
61
- gateStatus,
62
68
  getWorkflowState,
63
- saveWorkflowState
69
+ saveWorkflowState,
70
+ getFeature,
71
+ gateStatus,
72
+ approveGate,
73
+ isGateApproved
64
74
  };
@@ -3,7 +3,7 @@
3
3
  const fs = require("fs");
4
4
  const path = require("path");
5
5
  const { execSync } = require("child_process");
6
- const { getWorkflowState, saveWorkflowState } = require("./gates");
6
+ const { getWorkflowState, saveWorkflowState, isGateApproved } = require("./gates");
7
7
 
8
8
  const projectRoot = process.cwd();
9
9
 
@@ -55,44 +55,6 @@ function runValidators(validators) {
55
55
  }
56
56
  }
57
57
 
58
- function checkGate(featureName, gate) {
59
- const state = getWorkflowState();
60
- const feature = state.features?.find((f) => f.name === featureName);
61
-
62
- if (!feature) {
63
- console.error(`❌ Feature "${featureName}" not found.`);
64
- return false;
65
- }
66
-
67
- return feature.gates?.[gate] === "approved";
68
- }
69
-
70
- function approveGate(featureName, gate) {
71
- const state = getWorkflowState();
72
- const feature = state.features?.find((f) => f.name === featureName);
73
-
74
- if (!feature) {
75
- console.error(`❌ Feature "${featureName}" not found.`);
76
- return false;
77
- }
78
-
79
- if (!feature.gates) {
80
- feature.gates = {};
81
- }
82
-
83
- feature.gates[gate] = "approved";
84
-
85
- feature.history = feature.history || [];
86
- feature.history.push({
87
- event: `${gate}_approved`,
88
- timestamp: new Date().toISOString()
89
- });
90
-
91
- saveWorkflowState(state);
92
- console.log(`✅ Gate "${gate}" approved for feature "${featureName}".`);
93
- return true;
94
- }
95
-
96
58
  function toKebabCase(text) {
97
59
  return text
98
60
  .toLowerCase()
@@ -129,7 +91,6 @@ function main() {
129
91
  console.log(`📁 Agent File: ${command.agentFile}`);
130
92
  console.log(`📝 Description: ${command.description}`);
131
93
 
132
- // Gate enforcement
133
94
  if (command.gates && command.gates.length > 0) {
134
95
  if (!featureName) {
135
96
  console.error(`❌ This command requires a feature name or description.`);
@@ -140,7 +101,7 @@ function main() {
140
101
  console.log(`\n🚧 Required Gates: ${command.gates.join(", ")}`);
141
102
 
142
103
  for (const gate of command.gates) {
143
- if (!checkGate(featureName, gate)) {
104
+ if (!isGateApproved(featureName, gate)) {
144
105
  console.log(`\n⏸️ Gate "${gate}" is NOT approved for "${featureName}".`);
145
106
  console.log(` Please approve this gate before running this command.`);
146
107
  console.log(` Use: sdrive approve ${gate}\n`);
@@ -1,25 +1,20 @@
1
1
  {
2
2
  "version": "1.0.0",
3
3
  "tools": [
4
- {
5
- "name": "github-copilot",
6
- "adapterPath": ".github/copilot-instructions.md"
7
- },
8
- {
9
- "name": "cline",
10
- "adapterPath": ".clinerules/sdrive.md"
11
- },
12
- {
13
- "name": "claude-code",
14
- "adapterPath": ".claude/commands/sdrive.md"
15
- },
16
- {
17
- "name": "cursor",
18
- "adapterPath": ".cursor/rules/sdrive.md"
19
- },
20
- {
21
- "name": "vscode-native",
22
- "adapterPath": ".vscode/sdrive-commands.json"
23
- }
4
+ { "name": "github-copilot", "adapterPath": ".github/copilot-instructions.md" },
5
+ { "name": "cline", "adapterPath": ".clinerules/sdrive.md" },
6
+ { "name": "claude-code", "adapterPath": ".claude/commands/sdrive.md" },
7
+ { "name": "cursor", "adapterPath": ".cursor/rules/sdrive.md" },
8
+ { "name": "vscode-native", "adapterPath": ".vscode/sdrive-commands.json" },
9
+ { "name": "amazon-q", "adapterPath": ".amazonq/rules/sdrive.md" },
10
+ { "name": "gemini-cli", "adapterPath": ".gemini/commands/sdrive.md" },
11
+ { "name": "continue", "adapterPath": ".continue/rules/sdrive.md" },
12
+ { "name": "codex", "adapterPath": ".codex/rules/sdrive.md" },
13
+ { "name": "windsurf", "adapterPath": ".windsurf/rules/sdrive.md" },
14
+ { "name": "kilo-code", "adapterPath": ".kilocode/rules/sdrive.md" },
15
+ { "name": "opencode", "adapterPath": ".opencode/rules/sdrive.md" },
16
+ { "name": "qoder", "adapterPath": ".qoder/rules/sdrive.md" },
17
+ { "name": "qwen-code", "adapterPath": ".qwen/rules/sdrive.md" },
18
+ { "name": "rovo-dev", "adapterPath": ".rovo/rules/sdrive.md" }
24
19
  ]
25
20
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "specdrive-cli",
3
- "version": "0.1.2",
3
+ "version": "0.1.8",
4
4
  "description": "SpecDrive enterprise spec-driven development CLI",
5
5
  "bin": {
6
6
  "sdrive": "src/index.js"
@@ -1,28 +1,47 @@
1
1
  {
2
- "": "http://json-schema.org/draft-07/schema#",
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "title": "Technical Plan",
4
4
  "type": "object",
5
5
  "properties": {
6
- "feature": { "type": "string", "pattern": "^[a-z0-9-]+$" },
7
- "version": { "type": "integer", "minimum": 1 },
6
+ "feature": {
7
+ "type": "string",
8
+ "pattern": "^[a-z0-9-]+$"
9
+ },
10
+ "version": {
11
+ "type": "integer",
12
+ "minimum": 1
13
+ },
8
14
  "architecture": {
9
15
  "type": "object",
10
16
  "properties": {
11
- "approach": { "type": "string" },
17
+ "approach": {
18
+ "type": "string",
19
+ "minLength": 10
20
+ },
12
21
  "components": {
13
22
  "type": "array",
14
23
  "items": {
15
24
  "type": "object",
16
25
  "properties": {
17
26
  "name": { "type": "string" },
18
- "action": { "enum": ["create", "modify", "reuse", "delete"] },
27
+ "action": {
28
+ "enum": ["create", "modify", "reuse", "delete"]
29
+ },
19
30
  "description": { "type": "string" }
20
31
  },
21
32
  "required": ["name", "action"]
22
33
  }
34
+ },
35
+ "referencedRequirementIds": {
36
+ "type": "array",
37
+ "items": {
38
+ "type": "string",
39
+ "pattern": "^(FR|NFR)-[0-9]{3}$"
40
+ },
41
+ "uniqueItems": true
23
42
  }
24
43
  },
25
- "required": ["approach", "components"]
44
+ "required": ["approach", "components", "referencedRequirementIds"]
26
45
  },
27
46
  "dataModel": {
28
47
  "type": "object",
@@ -33,7 +52,9 @@
33
52
  "type": "object",
34
53
  "properties": {
35
54
  "name": { "type": "string" },
36
- "action": { "enum": ["create", "update", "no_change", "delete"] },
55
+ "action": {
56
+ "enum": ["create", "update", "no_change", "delete"]
57
+ },
37
58
  "fields": {
38
59
  "type": "array",
39
60
  "items": {
@@ -42,7 +63,9 @@
42
63
  "name": { "type": "string" },
43
64
  "type": { "type": "string" },
44
65
  "nullable": { "type": "boolean" },
45
- "action": { "enum": ["add", "modify", "remove", "no_change"] }
66
+ "action": {
67
+ "enum": ["add", "modify", "remove", "no_change"]
68
+ }
46
69
  },
47
70
  "required": ["name", "type", "action"]
48
71
  }
@@ -51,7 +74,10 @@
51
74
  "required": ["name", "action", "fields"]
52
75
  }
53
76
  },
54
- "flowDescription": { "type": "string" }
77
+ "flowDescription": {
78
+ "type": "string",
79
+ "minLength": 10
80
+ }
55
81
  },
56
82
  "required": ["entities", "flowDescription"]
57
83
  },
@@ -60,30 +86,82 @@
60
86
  "items": {
61
87
  "type": "object",
62
88
  "properties": {
63
- "method": { "enum": ["GET", "POST", "PUT", "DELETE", "PATCH"] },
89
+ "method": {
90
+ "enum": ["GET", "POST", "PUT", "DELETE", "PATCH"]
91
+ },
64
92
  "path": { "type": "string" },
65
- "action": { "enum": ["create", "modify", "reuse", "delete"] },
93
+ "action": {
94
+ "enum": ["create", "modify", "reuse", "delete"]
95
+ },
66
96
  "authenticationRequired": { "type": "boolean" },
67
97
  "linkedRequirements": {
68
98
  "type": "array",
69
- "items": { "type": "string", "pattern": "^(FR|NFR)-[0-9]{3}$" }
99
+ "items": {
100
+ "type": "string",
101
+ "pattern": "^(FR|NFR)-[0-9]{3}$"
102
+ }
70
103
  }
71
104
  },
72
105
  "required": ["method", "path", "action", "linkedRequirements"]
73
106
  }
74
107
  },
108
+ "securityConsiderations": {
109
+ "type": "array",
110
+ "items": {
111
+ "type": "object",
112
+ "properties": {
113
+ "requirementId": {
114
+ "type": "string",
115
+ "pattern": "^NFR-[0-9]{3}$"
116
+ },
117
+ "description": { "type": "string" }
118
+ },
119
+ "required": ["requirementId", "description"]
120
+ }
121
+ },
122
+ "performanceConsiderations": {
123
+ "type": "array",
124
+ "items": {
125
+ "type": "object",
126
+ "properties": {
127
+ "requirementId": {
128
+ "type": "string",
129
+ "pattern": "^NFR-[0-9]{3}$"
130
+ },
131
+ "strategy": { "type": "string" }
132
+ },
133
+ "required": ["requirementId", "strategy"]
134
+ }
135
+ },
75
136
  "dependencies": {
76
137
  "type": "array",
77
138
  "items": {
78
139
  "type": "object",
79
140
  "properties": {
80
141
  "name": { "type": "string" },
81
- "type": { "enum": ["library", "service", "internal", "external"] },
82
- "action": { "enum": ["add", "update", "remove", "no_change"] }
142
+ "type": {
143
+ "enum": ["library", "service", "internal", "external"]
144
+ },
145
+ "action": {
146
+ "enum": ["add", "update", "remove", "no_change"]
147
+ },
148
+ "notes": { "type": "string" }
83
149
  },
84
150
  "required": ["name", "type", "action"]
85
151
  }
152
+ },
153
+ "risks": {
154
+ "type": "array",
155
+ "items": {
156
+ "type": "object",
157
+ "properties": {
158
+ "description": { "type": "string" },
159
+ "mitigation": { "type": "string" }
160
+ },
161
+ "required": ["description", "mitigation"]
162
+ }
86
163
  }
87
164
  },
88
- "required": ["feature", "version", "architecture", "dataModel"]
89
- }
165
+ "required": ["feature", "version", "architecture", "dataModel"],
166
+ "additionalProperties": false
167
+ }
@@ -1,16 +1,45 @@
1
1
  {
2
- "": "http://json-schema.org/draft-07/schema#",
2
+ "$schema": "http://json-schema.org/draft-07/schema#",
3
3
  "title": "Feature Specification",
4
4
  "type": "object",
5
5
  "properties": {
6
- "feature": { "type": "string", "pattern": "^[a-z0-9-]+$" },
7
- "version": { "type": "integer", "minimum": 1 },
6
+ "feature": {
7
+ "type": "string",
8
+ "pattern": "^[a-z0-9-]+$",
9
+ "description": "Machine-friendly feature name (kebab-case)"
10
+ },
11
+ "title": {
12
+ "type": "string",
13
+ "minLength": 3,
14
+ "description": "Human-readable feature title"
15
+ },
16
+ "version": {
17
+ "type": "integer",
18
+ "minimum": 1
19
+ },
8
20
  "overview": {
9
21
  "type": "object",
10
22
  "properties": {
11
- "problemStatement": { "type": "string" },
12
- "proposedSolution": { "type": "string" },
13
- "targetAudience": { "type": "string" }
23
+ "problemStatement": {
24
+ "type": "string",
25
+ "minLength": 10
26
+ },
27
+ "proposedSolution": {
28
+ "type": "string",
29
+ "minLength": 10
30
+ },
31
+ "targetAudience": {
32
+ "type": "string",
33
+ "minLength": 3
34
+ },
35
+ "assumptions": {
36
+ "type": "array",
37
+ "items": { "type": "string" }
38
+ },
39
+ "outOfScope": {
40
+ "type": "array",
41
+ "items": { "type": "string" }
42
+ }
14
43
  },
15
44
  "required": ["problemStatement", "proposedSolution", "targetAudience"]
16
45
  },
@@ -20,31 +49,60 @@
20
49
  "items": {
21
50
  "type": "object",
22
51
  "properties": {
23
- "id": { "type": "string", "pattern": "^US-[0-9]{3}$" },
24
- "title": { "type": "string" },
52
+ "id": {
53
+ "type": "string",
54
+ "pattern": "^US-[0-9]{3}$"
55
+ },
56
+ "title": {
57
+ "type": "string",
58
+ "minLength": 5
59
+ },
60
+ "role": { "type": "string" },
61
+ "want": { "type": "string" },
62
+ "benefit": { "type": "string" },
25
63
  "acceptanceCriteria": {
26
64
  "type": "array",
27
65
  "minItems": 1,
28
66
  "items": {
29
67
  "type": "object",
30
68
  "properties": {
31
- "id": { "type": "string", "pattern": "^AC-[0-9]{3}$" },
32
- "text": { "type": "string" }
69
+ "id": {
70
+ "type": "string",
71
+ "pattern": "^AC-[0-9]{3}$"
72
+ },
73
+ "text": {
74
+ "type": "string",
75
+ "minLength": 10
76
+ }
33
77
  },
34
78
  "required": ["id", "text"]
35
79
  }
36
80
  }
37
81
  },
38
- "required": ["id", "title", "acceptanceCriteria"]
82
+ "required": ["id", "title", "role", "want", "benefit", "acceptanceCriteria"]
39
83
  }
40
84
  },
41
85
  "functionalRequirements": {
42
86
  "type": "array",
87
+ "minItems": 1,
43
88
  "items": {
44
89
  "type": "object",
45
90
  "properties": {
46
- "id": { "type": "string", "pattern": "^FR-[0-9]{3}$" },
47
- "text": { "type": "string" }
91
+ "id": {
92
+ "type": "string",
93
+ "pattern": "^FR-[0-9]{3}$"
94
+ },
95
+ "text": {
96
+ "type": "string",
97
+ "minLength": 10
98
+ },
99
+ "linkedAcceptanceCriteria": {
100
+ "type": "array",
101
+ "items": {
102
+ "type": "string",
103
+ "pattern": "^AC-[0-9]{3}$"
104
+ }
105
+ }
48
106
  },
49
107
  "required": ["id", "text"]
50
108
  }
@@ -54,12 +112,77 @@
54
112
  "items": {
55
113
  "type": "object",
56
114
  "properties": {
57
- "id": { "type": "string", "pattern": "^NFR-[0-9]{3}$" },
58
- "text": { "type": "string" }
115
+ "id": {
116
+ "type": "string",
117
+ "pattern": "^NFR-[0-9]{3}$"
118
+ },
119
+ "text": {
120
+ "type": "string",
121
+ "minLength": 10
122
+ },
123
+ "linkedAcceptanceCriteria": {
124
+ "type": "array",
125
+ "items": {
126
+ "type": "string",
127
+ "pattern": "^AC-[0-9]{3}$"
128
+ }
129
+ }
59
130
  },
60
131
  "required": ["id", "text"]
61
132
  }
133
+ },
134
+ "edgeCases": {
135
+ "type": "array",
136
+ "items": {
137
+ "type": "object",
138
+ "properties": {
139
+ "id": {
140
+ "type": "string",
141
+ "pattern": "^EC-[0-9]{3}$"
142
+ },
143
+ "description": { "type": "string" },
144
+ "expectedBehavior": { "type": "string" }
145
+ },
146
+ "required": ["id", "description", "expectedBehavior"]
147
+ }
148
+ },
149
+ "dependencies": {
150
+ "type": "array",
151
+ "items": {
152
+ "type": "object",
153
+ "properties": {
154
+ "name": { "type": "string" },
155
+ "type": {
156
+ "enum": ["library", "service", "internal", "external"]
157
+ },
158
+ "notes": { "type": "string" }
159
+ },
160
+ "required": ["name", "type"]
161
+ }
162
+ },
163
+ "risks": {
164
+ "type": "array",
165
+ "items": {
166
+ "type": "object",
167
+ "properties": {
168
+ "id": {
169
+ "type": "string",
170
+ "pattern": "^RISK-[0-9]{3}$"
171
+ },
172
+ "description": { "type": "string" },
173
+ "mitigation": { "type": "string" }
174
+ },
175
+ "required": ["id", "description", "mitigation"]
176
+ }
62
177
  }
63
178
  },
64
- "required": ["feature", "version", "overview", "userStories"]
65
- }
179
+ "required": [
180
+ "feature",
181
+ "title",
182
+ "version",
183
+ "overview",
184
+ "userStories",
185
+ "functionalRequirements"
186
+ ],
187
+ "additionalProperties": false
188
+ }