specdrive-cli 0.1.9 → 0.1.12

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 (74) hide show
  1. package/README.md +955 -506
  2. package/agents/00-onboarding.md +261 -0
  3. package/agents/01-constitution.md +214 -201
  4. package/agents/02-specification.md +249 -226
  5. package/agents/03-uiux.md +156 -144
  6. package/agents/04-cascade.md +151 -122
  7. package/agents/05-discover-skills.md +136 -136
  8. package/agents/06-documentation.md +158 -145
  9. package/agents/07-implementation.md +201 -169
  10. package/agents/08-performance.md +179 -165
  11. package/agents/09-review-complete.md +239 -168
  12. package/agents/10-security.md +180 -167
  13. package/agents/11-test.md +195 -0
  14. package/commands/gates.js +73 -73
  15. package/commands/manifest.json +113 -95
  16. package/commands/permissions.json +39 -0
  17. package/commands/router.js +151 -127
  18. package/commands/tools.json +19 -19
  19. package/dashboard/app.js +394 -0
  20. package/dashboard/index.html +74 -0
  21. package/dashboard/server.js +166 -0
  22. package/dashboard/style.css +157 -0
  23. package/mcp/mcp.json +31 -0
  24. package/mcp/server.js +108 -0
  25. package/package.json +35 -32
  26. package/schemas/config.schema.json +20 -0
  27. package/schemas/workflow-state.schema.json +152 -35
  28. package/scripts/anti-redundancy.js +176 -176
  29. package/scripts/audit-log.js +46 -46
  30. package/scripts/check-permission.js +87 -0
  31. package/scripts/diff-spec.js +51 -0
  32. package/scripts/diff-version.js +96 -0
  33. package/scripts/generate-adapters.js +80 -80
  34. package/scripts/generate-from-template.js +97 -70
  35. package/scripts/generate-openapi.js +76 -0
  36. package/scripts/github-team-sync.js +81 -0
  37. package/scripts/install-hooks.js +21 -0
  38. package/scripts/load-plugins.js +65 -65
  39. package/scripts/migrate-openspec.js +318 -0
  40. package/scripts/migrate-speckit.js +322 -0
  41. package/scripts/migrate.js +12 -0
  42. package/scripts/onboard.js +312 -0
  43. package/scripts/pre-commit.js +56 -20
  44. package/scripts/team.js +113 -113
  45. package/scripts/test-adapters.js +118 -118
  46. package/scripts/test-create.js +13 -13
  47. package/scripts/test-end-to-end.js +137 -137
  48. package/scripts/test-router.js +110 -110
  49. package/scripts/test-state-transitions.js +146 -146
  50. package/scripts/test-validator.js +152 -152
  51. package/scripts/validate-config.js +36 -0
  52. package/scripts/validate-governance.js +150 -130
  53. package/scripts/verify.js +525 -0
  54. package/scripts/version-new.js +202 -0
  55. package/src/index.js +1010 -400
  56. package/templates/expo/plan.json +12 -0
  57. package/templates/expo/spec.json +12 -0
  58. package/templates/expo/tasks.json +5 -0
  59. package/templates/fastapi/plan.json +12 -0
  60. package/templates/fastapi/spec.json +12 -0
  61. package/templates/fastapi/tasks.json +5 -0
  62. package/templates/generic/plan.json +12 -0
  63. package/templates/generic/spec.json +11 -0
  64. package/templates/generic/tasks.json +5 -0
  65. package/templates/nextjs/plan.json +23 -0
  66. package/templates/nextjs/spec.json +12 -0
  67. package/templates/nextjs/tasks.json +5 -0
  68. package/templates/react-node/plan.json +15 -0
  69. package/templates/react-node/spec.json +12 -0
  70. package/templates/react-node/tasks.json +5 -0
  71. package/templates/registry.json +30 -0
  72. package/templates/turborepo/plan.json +12 -0
  73. package/templates/turborepo/spec.json +12 -0
  74. package/templates/turborepo/tasks.json +5 -0
@@ -0,0 +1,318 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+
6
+ const projectRoot = process.cwd();
7
+ const sdriveRoot = path.join(projectRoot, ".sdrive");
8
+
9
+ const openspecDir = path.join(projectRoot, "openspec");
10
+ const openspecSpecsDir = path.join(openspecDir, "specs");
11
+ const openspecChangesDir = path.join(openspecDir, "changes");
12
+ const openspecArchiveDir = path.join(openspecDir, "archive");
13
+
14
+ if (!fs.existsSync(openspecDir)) {
15
+ console.error("❌ openspec/ folder not found. Nothing to migrate.");
16
+ process.exit(1);
17
+ }
18
+
19
+ function ensureDir(dir) {
20
+ fs.mkdirSync(dir, { recursive: true });
21
+ }
22
+
23
+ function readFile(filePath) {
24
+ try {
25
+ return fs.readFileSync(filePath, "utf8");
26
+ } catch (error) {
27
+ return null;
28
+ }
29
+ }
30
+
31
+ function listDirectories(dir) {
32
+ if (!fs.existsSync(dir)) return [];
33
+ return fs
34
+ .readdirSync(dir, { withFileTypes: true })
35
+ .filter((d) => d.isDirectory())
36
+ .map((d) => d.name);
37
+ }
38
+
39
+ function slugify(name) {
40
+ return name
41
+ .toLowerCase()
42
+ .replace(/[^a-z0-9]+/g, "-")
43
+ .replace(/^-+|-+$/g, "");
44
+ }
45
+
46
+ function findSpecFile(dir) {
47
+ const candidates = ["spec.md", "proposal.md", "README.md"];
48
+ for (const candidate of candidates) {
49
+ const full = path.join(dir, candidate);
50
+ if (fs.existsSync(full)) return full;
51
+ }
52
+ return null;
53
+ }
54
+
55
+ function findTasksFile(dir) {
56
+ const candidates = ["tasks.md", "todo.md"];
57
+ for (const candidate of candidates) {
58
+ const full = path.join(dir, candidate);
59
+ if (fs.existsSync(full)) return full;
60
+ }
61
+ return null;
62
+ }
63
+
64
+ function writeSpecDriveFeature(featureName, sourceDir, lifecycle) {
65
+ const featureFolder = path.join(
66
+ sdriveRoot,
67
+ "specs",
68
+ lifecycle,
69
+ featureName,
70
+ "v1"
71
+ );
72
+ const govFolder = path.join(sdriveRoot, "governance", featureName, "v1");
73
+
74
+ ensureDir(featureFolder);
75
+ ensureDir(govFolder);
76
+
77
+ const specSource = findSpecFile(sourceDir);
78
+ const tasksSource = findTasksFile(sourceDir);
79
+
80
+ const specContent = specSource
81
+ ? readFile(specSource)
82
+ : `# ${featureName}\n\n(Migrated from OpenSpec — no source spec found)`;
83
+
84
+ const tasksContent = tasksSource
85
+ ? readFile(tasksSource)
86
+ : `# Execution Tasks: ${featureName}\n\n(Migrated from OpenSpec — no tasks found)`;
87
+
88
+ const now = new Date().toISOString();
89
+
90
+ // spec.md
91
+ fs.writeFileSync(
92
+ path.join(featureFolder, "spec.md"),
93
+ `---
94
+ title: ${featureName}
95
+ status: ${lifecycle === "completed" ? "completed" : "ongoing"}
96
+ version: v1
97
+ created: ${now}
98
+ updated: ${now}
99
+ retroactive: true
100
+ migratedFrom: openspec
101
+ ---
102
+
103
+ ${specContent}
104
+ `
105
+ );
106
+
107
+ // plan.md
108
+ fs.writeFileSync(
109
+ path.join(featureFolder, "plan.md"),
110
+ `# Technical Plan: ${featureName} (migrated)\n\nThis plan was migrated from OpenSpec. No technical plan was extracted.\n`
111
+ );
112
+
113
+ // tasks.md
114
+ fs.writeFileSync(
115
+ path.join(featureFolder, "tasks.md"),
116
+ tasksContent
117
+ );
118
+
119
+ // governance JSON
120
+ fs.writeFileSync(
121
+ path.join(govFolder, "spec.json"),
122
+ JSON.stringify(
123
+ {
124
+ feature: featureName,
125
+ title: featureName,
126
+ version: 1,
127
+ retroactive: true,
128
+ migratedFrom: "openspec",
129
+ overview: {
130
+ problemStatement: "(migrated from OpenSpec)",
131
+ proposedSolution: "(migrated from OpenSpec)",
132
+ targetAudience: "(migrated from OpenSpec)"
133
+ },
134
+ userStories: [],
135
+ functionalRequirements: []
136
+ },
137
+ null,
138
+ 2
139
+ )
140
+ );
141
+
142
+ fs.writeFileSync(
143
+ path.join(govFolder, "plan.json"),
144
+ JSON.stringify(
145
+ {
146
+ feature: featureName,
147
+ version: 1,
148
+ architecture: { approach: "(migrated)", components: [] },
149
+ dataModel: { entities: [], flowDescription: "(migrated)" }
150
+ },
151
+ null,
152
+ 2
153
+ )
154
+ );
155
+
156
+ fs.writeFileSync(
157
+ path.join(govFolder, "tasks.json"),
158
+ JSON.stringify(
159
+ { feature: featureName, version: 1, phases: [] },
160
+ null,
161
+ 2
162
+ )
163
+ );
164
+
165
+ fs.writeFileSync(
166
+ path.join(govFolder, "traceability.json"),
167
+ JSON.stringify(
168
+ { feature: featureName, version: 1, rows: [] },
169
+ null,
170
+ 2
171
+ )
172
+ );
173
+
174
+ return {
175
+ feature: featureName,
176
+ source: path.relative(projectRoot, sourceDir),
177
+ lifecycle,
178
+ spec: specSource ? path.relative(projectRoot, specSource) : null,
179
+ tasks: tasksSource ? path.relative(projectRoot, tasksSource) : null
180
+ };
181
+ }
182
+
183
+ function migrate() {
184
+ const migrated = [];
185
+
186
+ // 1. Active specs
187
+ if (fs.existsSync(openspecSpecsDir)) {
188
+ const features = listDirectories(openspecSpecsDir);
189
+ for (const feature of features) {
190
+ const featureName = slugify(feature);
191
+ const sourceDir = path.join(openspecSpecsDir, feature);
192
+ const result = writeSpecDriveFeature(featureName, sourceDir, "ongoing");
193
+ migrated.push(result);
194
+ console.log(`✅ Migrated spec: ${featureName}`);
195
+ }
196
+ }
197
+
198
+ // 2. Active changes
199
+ if (fs.existsSync(openspecChangesDir)) {
200
+ const changes = listDirectories(openspecChangesDir);
201
+ for (const change of changes) {
202
+ const featureName = slugify(change);
203
+ const sourceDir = path.join(openspecChangesDir, change);
204
+ const result = writeSpecDriveFeature(featureName, sourceDir, "ongoing");
205
+ migrated.push(result);
206
+ console.log(`✅ Migrated change: ${featureName}`);
207
+ }
208
+ }
209
+
210
+ // 3. Archive
211
+ if (fs.existsSync(openspecArchiveDir)) {
212
+ const archived = listDirectories(openspecArchiveDir);
213
+ for (const feature of archived) {
214
+ const featureName = slugify(feature);
215
+ const sourceDir = path.join(openspecArchiveDir, feature);
216
+ const result = writeSpecDriveFeature(featureName, sourceDir, "completed");
217
+ migrated.push(result);
218
+ console.log(`✅ Migrated archive: ${featureName}`);
219
+ }
220
+ }
221
+
222
+ return migrated;
223
+ }
224
+
225
+ function updateWorkflowState(migrated) {
226
+ const statePath = path.join(sdriveRoot, "workflow-state.json");
227
+
228
+ let state = { features: [] };
229
+ if (fs.existsSync(statePath)) {
230
+ state = JSON.parse(fs.readFileSync(statePath, "utf8"));
231
+ }
232
+
233
+ if (!state.onboarding) {
234
+ state.onboarding = {
235
+ mode: "brownfield",
236
+ date: new Date().toISOString(),
237
+ missing: [],
238
+ deferred: [],
239
+ retroactiveFeatures: [],
240
+ verifiedFeatures: [],
241
+ notes: ""
242
+ };
243
+ }
244
+
245
+ if (!state.onboarding.migrated) {
246
+ state.onboarding.migrated = [];
247
+ }
248
+
249
+ for (const item of migrated) {
250
+ // Add to features list if not present
251
+ const existing = state.features.find((f) => f.name === item.feature);
252
+
253
+ if (!existing) {
254
+ state.features.push({
255
+ name: item.feature,
256
+ currentVersion: "v1",
257
+ versions: [
258
+ {
259
+ id: "v1",
260
+ phase: item.lifecycle === "completed" ? "completed" : "ongoing",
261
+ currentStep: "review",
262
+ gates: {
263
+ specification: item.lifecycle === "completed" ? "approved" : "pending",
264
+ planning: item.lifecycle === "completed" ? "approved" : "pending",
265
+ implementation: item.lifecycle === "completed" ? "approved" : "pending",
266
+ review: item.lifecycle === "completed" ? "approved" : "pending"
267
+ },
268
+ history: [
269
+ {
270
+ event: "feature_migrated",
271
+ timestamp: new Date().toISOString(),
272
+ by: "onboarding"
273
+ }
274
+ ]
275
+ }
276
+ ]
277
+ });
278
+ }
279
+
280
+ // Add migration record
281
+ state.onboarding.migrated.push({
282
+ feature: item.feature,
283
+ from: "openspec",
284
+ date: new Date().toISOString(),
285
+ source: item.source
286
+ });
287
+
288
+ // Add to retroactive features
289
+ if (!state.onboarding.retroactiveFeatures.includes(item.feature)) {
290
+ state.onboarding.retroactiveFeatures.push(item.feature);
291
+ }
292
+ }
293
+
294
+ fs.writeFileSync(statePath, JSON.stringify(state, null, 2));
295
+ }
296
+
297
+ function main() {
298
+ console.log("🔍 Migrating from OpenSpec...\n");
299
+
300
+ const migrated = migrate();
301
+
302
+ if (migrated.length === 0) {
303
+ console.log("\n⚠️ No OpenSpec features found to migrate.");
304
+ process.exit(0);
305
+ }
306
+
307
+ updateWorkflowState(migrated);
308
+
309
+ console.log("");
310
+ console.log(`✅ Migration complete: ${migrated.length} features.`);
311
+ console.log("");
312
+ console.log("Original openspec/ folder was NOT deleted.");
313
+ console.log("All migrated features are marked retroactive: true.");
314
+ console.log("");
315
+ console.log("Next: run sdrive validate to confirm the new files.");
316
+ }
317
+
318
+ main();
@@ -0,0 +1,322 @@
1
+ #!/usr/bin/env node
2
+
3
+ const fs = require("fs");
4
+ const path = require("path");
5
+
6
+ const projectRoot = process.cwd();
7
+ const sdriveRoot = path.join(projectRoot, ".sdrive");
8
+
9
+ const speckitDir = path.join(projectRoot, ".specify");
10
+ const speckitSpecsDir = path.join(speckitDir, "specs");
11
+ const speckitMemoryDir = path.join(speckitDir, "memory");
12
+ const speckitConstitution = path.join(speckitMemoryDir, "constitution.md");
13
+
14
+ if (!fs.existsSync(speckitDir)) {
15
+ console.error("❌ .specify/ folder not found. Nothing to migrate.");
16
+ process.exit(1);
17
+ }
18
+
19
+ function ensureDir(dir) {
20
+ fs.mkdirSync(dir, { recursive: true });
21
+ }
22
+
23
+ function readFile(filePath) {
24
+ try {
25
+ return fs.readFileSync(filePath, "utf8");
26
+ } catch (error) {
27
+ return null;
28
+ }
29
+ }
30
+
31
+ function listDirectories(dir) {
32
+ if (!fs.existsSync(dir)) return [];
33
+ return fs
34
+ .readdirSync(dir, { withFileTypes: true })
35
+ .filter((d) => d.isDirectory())
36
+ .map((d) => d.name);
37
+ }
38
+
39
+ function stripNumberPrefix(name) {
40
+ // Spec-Kit uses 001-feature-name
41
+ return name.replace(/^[0-9]+[-_]/, "");
42
+ }
43
+
44
+ function slugify(name) {
45
+ return name
46
+ .toLowerCase()
47
+ .replace(/[^a-z0-9]+/g, "-")
48
+ .replace(/^-+|-+$/g, "");
49
+ }
50
+
51
+ function findInDir(dir, names) {
52
+ for (const name of names) {
53
+ const full = path.join(dir, name);
54
+ if (fs.existsSync(full)) return full;
55
+ }
56
+ return null;
57
+ }
58
+
59
+ function writeSpecDriveFeature(featureName, sourceDir) {
60
+ const featureFolder = path.join(
61
+ sdriveRoot,
62
+ "specs",
63
+ "ongoing",
64
+ featureName,
65
+ "v1"
66
+ );
67
+ const govFolder = path.join(sdriveRoot, "governance", featureName, "v1");
68
+
69
+ ensureDir(featureFolder);
70
+ ensureDir(govFolder);
71
+
72
+ const specSource = findInDir(sourceDir, ["spec.md", "specification.md"]);
73
+ const planSource = findInDir(sourceDir, ["plan.md", "technical-plan.md"]);
74
+ const tasksSource = findInDir(sourceDir, ["tasks.md", "todo.md"]);
75
+
76
+ const now = new Date().toISOString();
77
+
78
+ const specContent = specSource
79
+ ? readFile(specSource)
80
+ : `# ${featureName}\n\n(Migrated from Spec-Kit — no source spec found)`;
81
+
82
+ const planContent = planSource
83
+ ? readFile(planSource)
84
+ : `# Technical Plan: ${featureName} (migrated)\n\nThis plan was migrated from Spec-Kit. No technical plan was extracted.\n`;
85
+
86
+ const tasksContent = tasksSource
87
+ ? readFile(tasksSource)
88
+ : `# Execution Tasks: ${featureName}\n\n(Migrated from Spec-Kit — no tasks found)\n`;
89
+
90
+ // spec.md
91
+ fs.writeFileSync(
92
+ path.join(featureFolder, "spec.md"),
93
+ `---
94
+ title: ${featureName}
95
+ status: ongoing
96
+ version: v1
97
+ created: ${now}
98
+ updated: ${now}
99
+ retroactive: true
100
+ migratedFrom: speckit
101
+ ---
102
+
103
+ ${specContent}
104
+ `
105
+ );
106
+
107
+ // plan.md
108
+ fs.writeFileSync(path.join(featureFolder, "plan.md"), planContent);
109
+
110
+ // tasks.md
111
+ fs.writeFileSync(path.join(featureFolder, "tasks.md"), tasksContent);
112
+
113
+ // governance JSON
114
+ fs.writeFileSync(
115
+ path.join(govFolder, "spec.json"),
116
+ JSON.stringify(
117
+ {
118
+ feature: featureName,
119
+ title: featureName,
120
+ version: 1,
121
+ retroactive: true,
122
+ migratedFrom: "speckit",
123
+ overview: {
124
+ problemStatement: "(migrated from Spec-Kit)",
125
+ proposedSolution: "(migrated from Spec-Kit)",
126
+ targetAudience: "(migrated from Spec-Kit)"
127
+ },
128
+ userStories: [],
129
+ functionalRequirements: []
130
+ },
131
+ null,
132
+ 2
133
+ )
134
+ );
135
+
136
+ fs.writeFileSync(
137
+ path.join(govFolder, "plan.json"),
138
+ JSON.stringify(
139
+ {
140
+ feature: featureName,
141
+ version: 1,
142
+ architecture: { approach: "(migrated)", components: [] },
143
+ dataModel: { entities: [], flowDescription: "(migrated)" }
144
+ },
145
+ null,
146
+ 2
147
+ )
148
+ );
149
+
150
+ fs.writeFileSync(
151
+ path.join(govFolder, "tasks.json"),
152
+ JSON.stringify(
153
+ { feature: featureName, version: 1, phases: [] },
154
+ null,
155
+ 2
156
+ )
157
+ );
158
+
159
+ fs.writeFileSync(
160
+ path.join(govFolder, "traceability.json"),
161
+ JSON.stringify(
162
+ { feature: featureName, version: 1, rows: [] },
163
+ null,
164
+ 2
165
+ )
166
+ );
167
+
168
+ return {
169
+ feature: featureName,
170
+ source: path.relative(projectRoot, sourceDir),
171
+ spec: specSource ? path.relative(projectRoot, specSource) : null,
172
+ plan: planSource ? path.relative(projectRoot, planSource) : null,
173
+ tasks: tasksSource ? path.relative(projectRoot, tasksSource) : null
174
+ };
175
+ }
176
+
177
+ function migrateSpecs() {
178
+ const migrated = [];
179
+
180
+ if (!fs.existsSync(speckitSpecsDir)) {
181
+ return migrated;
182
+ }
183
+
184
+ const folders = listDirectories(speckitSpecsDir);
185
+
186
+ for (const folder of folders) {
187
+ const cleaned = stripNumberPrefix(folder);
188
+ const featureName = slugify(cleaned);
189
+ const sourceDir = path.join(speckitSpecsDir, folder);
190
+
191
+ const result = writeSpecDriveFeature(featureName, sourceDir);
192
+ migrated.push(result);
193
+ console.log(`✅ Migrated spec: ${featureName}`);
194
+ }
195
+
196
+ return migrated;
197
+ }
198
+
199
+ function migrateConstitution() {
200
+ if (!fs.existsSync(speckitConstitution)) {
201
+ return false;
202
+ }
203
+
204
+ const content = readFile(speckitConstitution);
205
+ if (!content) return false;
206
+
207
+ const targetConstitution = path.join(sdriveRoot, "constitution.md");
208
+
209
+ if (fs.existsSync(targetConstitution)) {
210
+ // Append instead of overwrite
211
+ const existing = readFile(targetConstitution) || "";
212
+ fs.writeFileSync(
213
+ targetConstitution,
214
+ `${existing}\n\n---\n\n## Migrated from Spec-Kit\n\n${content}\n`
215
+ );
216
+ console.log("✅ Appended Spec-Kit constitution to existing SpecDrive constitution.");
217
+ } else {
218
+ fs.writeFileSync(
219
+ targetConstitution,
220
+ `# Project Constitution\n\n> Migrated from Spec-Kit\n\n${content}\n`
221
+ );
222
+ console.log("✅ Created SpecDrive constitution from Spec-Kit constitution.");
223
+ }
224
+
225
+ return true;
226
+ }
227
+
228
+ function updateWorkflowState(migrated) {
229
+ const statePath = path.join(sdriveRoot, "workflow-state.json");
230
+
231
+ let state = { features: [] };
232
+ if (fs.existsSync(statePath)) {
233
+ state = JSON.parse(fs.readFileSync(statePath, "utf8"));
234
+ }
235
+
236
+ if (!state.onboarding) {
237
+ state.onboarding = {
238
+ mode: "brownfield",
239
+ date: new Date().toISOString(),
240
+ missing: [],
241
+ deferred: [],
242
+ retroactiveFeatures: [],
243
+ verifiedFeatures: [],
244
+ notes: ""
245
+ };
246
+ }
247
+
248
+ if (!state.onboarding.migrated) {
249
+ state.onboarding.migrated = [];
250
+ }
251
+
252
+ for (const item of migrated) {
253
+ const existing = state.features.find((f) => f.name === item.feature);
254
+
255
+ if (!existing) {
256
+ state.features.push({
257
+ name: item.feature,
258
+ currentVersion: "v1",
259
+ versions: [
260
+ {
261
+ id: "v1",
262
+ phase: "ongoing",
263
+ currentStep: "review",
264
+ gates: {
265
+ specification: "pending",
266
+ planning: "pending",
267
+ implementation: "pending",
268
+ review: "pending"
269
+ },
270
+ history: [
271
+ {
272
+ event: "feature_migrated",
273
+ timestamp: new Date().toISOString(),
274
+ by: "onboarding"
275
+ }
276
+ ]
277
+ }
278
+ ]
279
+ });
280
+ }
281
+
282
+ state.onboarding.migrated.push({
283
+ feature: item.feature,
284
+ from: "speckit",
285
+ date: new Date().toISOString(),
286
+ source: item.source
287
+ });
288
+
289
+ if (!state.onboarding.retroactiveFeatures.includes(item.feature)) {
290
+ state.onboarding.retroactiveFeatures.push(item.feature);
291
+ }
292
+ }
293
+
294
+ fs.writeFileSync(statePath, JSON.stringify(state, null, 2));
295
+ }
296
+
297
+ function main() {
298
+ console.log("🔍 Migrating from Spec-Kit...\n");
299
+
300
+ const migrated = migrateSpecs();
301
+ const constitutionMigrated = migrateConstitution();
302
+
303
+ if (migrated.length === 0 && !constitutionMigrated) {
304
+ console.log("\n⚠️ No Spec-Kit features or constitution found to migrate.");
305
+ process.exit(0);
306
+ }
307
+
308
+ updateWorkflowState(migrated);
309
+
310
+ console.log("");
311
+ console.log(`✅ Migration complete: ${migrated.length} features.`);
312
+ if (constitutionMigrated) {
313
+ console.log("✅ Constitution merged.");
314
+ }
315
+ console.log("");
316
+ console.log("Original .specify/ folder was NOT deleted.");
317
+ console.log("All migrated features are marked retroactive: true.");
318
+ console.log("");
319
+ console.log("Next: run sdrive validate to confirm the new files.");
320
+ }
321
+
322
+ main();
@@ -0,0 +1,12 @@
1
+ #!/usr/bin/env node
2
+
3
+ console.error("⚠️ `sdrive migrate` is deprecated.");
4
+ console.error("");
5
+ console.error("Migration now happens during Brownfield Onboarding.");
6
+ console.error("");
7
+ console.error("Run:");
8
+ console.error(" sdrive onboard");
9
+ console.error(" /sdrive:onboard");
10
+ console.error("");
11
+ console.error("The Onboarding Agent will detect OpenSpec or Spec-Kit and offer to migrate.");
12
+ process.exit(1);