specdrive-cli 0.1.3 → 0.1.9

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**
@@ -31,6 +33,7 @@ It adds:
31
33
  - [Validation](#validation)
32
34
  - [CI/CD Integration](#cicd-integration)
33
35
  - [Tool Support](#tool-support)
36
+ - [Team Collaboration](#team-collaboration)
34
37
  - [Contributing](#contributing)
35
38
  - [License](#license)
36
39
 
@@ -83,9 +86,12 @@ Inside your project root, run:
83
86
  sdrive init
84
87
  ```
85
88
 
86
- 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.
87
92
 
88
93
  ---
94
+
89
95
  ## Where to Run Commands
90
96
 
91
97
  | Command Type | Where to Run | Example |
@@ -96,6 +102,7 @@ This creates the `.sdrive/` folder and tool adapters.
96
102
  **Rule:** If it starts with `sdrive`, run it in the terminal. If it starts with `/sdrive:`, run it in your AI chat.
97
103
 
98
104
  ---
105
+
99
106
  ## Quickstart
100
107
 
101
108
  1. Initialize the project:
@@ -434,6 +441,8 @@ jobs:
434
441
 
435
442
  ## Tool Support
436
443
 
444
+ SpecDrive supports 15 AI tools.
445
+
437
446
  | Tool | Adapter |
438
447
  |------|---------|
439
448
  | GitHub Copilot | `.github/copilot-instructions.md` |
@@ -441,8 +450,20 @@ jobs:
441
450
  | Claude Code | `.claude/commands/sdrive.md` |
442
451
  | Cursor | `.cursor/rules/sdrive.md` |
443
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` |
444
463
 
445
- ```
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.
446
467
 
447
468
  ---
448
469
 
@@ -465,6 +486,7 @@ sdrive team:update bob reviewer
465
486
  sdrive team:remove carol
466
487
  ```
467
488
 
489
+ ---
468
490
 
469
491
  ## Contributing
470
492
 
@@ -481,15 +503,4 @@ MIT
481
503
 
482
504
  ---
483
505
 
484
- This is now a complete, professional README with:
485
-
486
- - Table of contents
487
- - Installation steps
488
- - Core workflow in correct order
489
- - Detailed optional commands
490
- - Agent reference
491
- - Directory structure
492
- - Validation and CI/CD sections
493
- - Tool support
494
-
495
- 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.3",
3
+ "version": "0.1.9",
4
4
  "description": "SpecDrive enterprise spec-driven development CLI",
5
5
  "bin": {
6
6
  "sdrive": "src/index.js"
@@ -11,7 +11,8 @@
11
11
  "scripts/",
12
12
  "commands/",
13
13
  "schemas/",
14
- "agents/"
14
+ "agents/",
15
+ "README.md"
15
16
  ],
16
17
  "dependencies": {
17
18
  "commander": "^12.0.0",
@@ -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
+ }