@vdkit/cli 3.0.1 → 3.0.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 (80) hide show
  1. package/README.md +1 -1
  2. package/cli.js +30 -1
  3. package/package.json +8 -8
  4. package/src/blueprints/BlueprintManifest.js +1 -1
  5. package/src/blueprints/PluginPackager.js +1 -1
  6. package/src/blueprints/retrieval/BlueprintRetrievalEngine.js +8 -6
  7. package/src/blueprints-client.js +1 -1
  8. package/src/commands/base/BaseCommand.js +1 -1
  9. package/src/commands/blueprints/BrowseCommand.js +1 -0
  10. package/src/commands/blueprints/CreateCommand.js +1 -1
  11. package/src/commands/blueprints/DeployCommand.js +65 -2
  12. package/src/commands/blueprints/RepoStatsCommand.js +6 -6
  13. package/src/commands/blueprints/SyncCommand.js +8 -4
  14. package/src/commands/community/PublishCommand.js +10 -10
  15. package/src/commands/core/ConvertCommand.js +2 -2
  16. package/src/commands/core/ImportCommand.js +1 -3
  17. package/src/commands/core/InitCommand.js +3 -3
  18. package/src/commands/core/ScanCommand.js +6 -2
  19. package/src/commands/core/StatusCommand.js +6 -2
  20. package/src/commands/core/ValidateCommand.js +1 -1
  21. package/src/commands/hub/HubGenerateCommand.js +413 -11
  22. package/src/commands/migration/SchemaMigrateCommand.js +5 -1
  23. package/src/commands/migration/UnifiedMigrateCommand.js +21 -7
  24. package/src/commands/shared/CommandContext.js +7 -3
  25. package/src/commands/team/ShareCommand.js +44 -9
  26. package/src/community/CommunityDeployer.js +109 -33
  27. package/src/hub/ConfigManager.js +3 -3
  28. package/src/hub/HubIntegration.js +3 -3
  29. package/src/hub/TelemetryManager.js +3 -3
  30. package/src/hub/VDKHubClient.js +141 -92
  31. package/src/hub/index.js +8 -8
  32. package/src/integrations/base-integration.js +3 -1
  33. package/src/integrations/claude-code-integration.js +6 -6
  34. package/src/integrations/cursor-integration.js +2 -2
  35. package/src/integrations/generic-ai-integration.js +9 -9
  36. package/src/integrations/generic-ide-integration.js +5 -5
  37. package/src/integrations/integration-manager.js +1 -1
  38. package/src/integrations/jetbrains-integration.js +3 -3
  39. package/src/integrations/vscode-variants-integration.js +2 -2
  40. package/src/integrations/windsurf-integration.js +1 -1
  41. package/src/integrations/zed-integration.js +2 -2
  42. package/src/ir/README.md +2 -2
  43. package/src/ir/generators.js +4 -5
  44. package/src/ir/index.js +1 -6
  45. package/src/ir/performance.js +3 -3
  46. package/src/mcp/McpManager.js +3 -3
  47. package/src/migration/AutoMigrator.js +46 -32
  48. package/src/migration/converters/context-converter.js +3 -3
  49. package/src/migration/converters/schema-v3-migrator.js +1 -1
  50. package/src/migration/core/MigrationBackup.js +23 -17
  51. package/src/migration/core/migration-detector.js +3 -3
  52. package/src/migration/detectors/rule-detector.js +2 -2
  53. package/src/migration/migration-manager.js +7 -6
  54. package/src/plugins/registry.js +1 -1
  55. package/src/publishing/PublishManager.js +294 -24
  56. package/src/publishing/UniversalFormatConverter.js +113 -1
  57. package/src/publishing/clients/GitHubPRClient.js +169 -27
  58. package/src/scanner/README.md +1 -1
  59. package/src/scanner/USER-GUIDE.md +1 -1
  60. package/src/scanner/core/BlueprintLoader.js +18 -7
  61. package/src/scanner/core/ClaudeCodeAdapter.js +18 -12
  62. package/src/scanner/core/CopilotAdapter.js +1 -1
  63. package/src/scanner/core/PatternDetector.js +1 -1
  64. package/src/scanner/core/PlatformConfigExtractor.js +4 -4
  65. package/src/scanner/core/RuleAdapter.js +6 -6
  66. package/src/scanner/core/RuleGenerator.js +8 -3
  67. package/src/scanner/core/TechnologyAnalyzer.js +1 -1
  68. package/src/scanner/utils/constants.js +1 -1
  69. package/src/scanner/utils/package-analyzer.js +2 -2
  70. package/src/scanner/utils/version.js +1 -1
  71. package/src/shared/ProjectContextAnalyzer.js +4 -0
  72. package/src/shared/blueprint-artifact-paths.js +32 -0
  73. package/src/shared/ide-configuration.js +8 -8
  74. package/src/shared/sync-operations.js +83 -16
  75. package/src/utils/file-system.js +17 -15
  76. package/src/utils/filename-generator.js +2 -4
  77. package/src/utils/schema-validator.js +1 -1
  78. package/src/utils/update-mcp-config.js +2 -2
  79. package/src/validation/check-duplicates.js +1 -1
  80. package/src/validation/validate-rules.js +7 -7
@@ -329,7 +329,9 @@ export class BaseIntegration {
329
329
  break;
330
330
  case 'medium':
331
331
  detection.recommendations.push(`${name} appears to be configured`);
332
- detection.recommendations.push('Consider optimizing .vdk/rules for better AI assistance');
332
+ detection.recommendations.push(
333
+ 'Consider optimizing .vdk/blueprints/rules for better AI assistance'
334
+ );
333
335
  break;
334
336
  case 'high':
335
337
  detection.recommendations.push(`${name} is actively configured and being used`);
@@ -374,7 +374,7 @@ This project uses VDK CLI for AI assistant integration and follows specific patt
374
374
  - **Framework**: ${options.framework || 'Not detected'}
375
375
 
376
376
  ### Important Conventions
377
- - All AI rules are stored in \`.vdk/rules/\` directory
377
+ - All AI rule artifacts are stored in \`.vdk/blueprints/rules/\` directory
378
378
  - Rules follow unified YAML frontmatter format
379
379
  - Project follows VDK CLI naming conventions
380
380
  - Memory persistence is enabled for context continuity
@@ -422,7 +422,7 @@ claudeCode:
422
422
  supports: false
423
423
  fileReferences:
424
424
  supports: true
425
- autoInclude: ["CLAUDE.md", ".vdk/rules/", "package.json"]
425
+ autoInclude: ["CLAUDE.md", ".vdk/blueprints/rules/", "package.json"]
426
426
 
427
427
  permissions:
428
428
  allowedTools: ["Read", "Glob", "Grep"]
@@ -456,7 +456,7 @@ Analyze the current project using VDK CLI capabilities and provide actionable re
456
456
  ### File References
457
457
  Auto-included files:
458
458
  - \`@CLAUDE.md\` - Project context and conventions
459
- - \`@.vdk/rules/\` - Current VDK rules directory
459
+ - \`@.vdk/blueprints/rules/\` - Current VDK rule artifacts directory
460
460
  - \`@package.json\` - Project dependencies
461
461
 
462
462
  ## Analysis Areas
@@ -467,7 +467,7 @@ Auto-included files:
467
467
  - Analyze technology stack and dependencies
468
468
 
469
469
  2. **Rule Status Review**
470
- - Check existing VDK rules in \`.vdk/rules/\`
470
+ - Check existing VDK rules in \`.vdk/blueprints/rules/\`
471
471
  - Validate rule format and content
472
472
  - Identify missing or outdated rules
473
473
 
@@ -516,7 +516,7 @@ claudeCode:
516
516
  supports: false
517
517
  fileReferences:
518
518
  supports: true
519
- autoInclude: ["CLAUDE.md", ".vdk/rules/", "package.json"]
519
+ autoInclude: ["CLAUDE.md", ".vdk/blueprints/rules/", "package.json"]
520
520
 
521
521
  permissions:
522
522
  allowedTools: ["Read", "Write", "Edit", "Bash(git:*)"]
@@ -604,7 +604,7 @@ claudeCode:
604
604
  supports: false
605
605
  fileReferences:
606
606
  supports: true
607
- autoInclude: ["CLAUDE.md", ".vdk/rules/", "package.json"]
607
+ autoInclude: ["CLAUDE.md", ".vdk/blueprints/rules/", "package.json"]
608
608
  bashCommands:
609
609
  supports: true
610
610
  commands: ["git status", "git log --oneline -10"]
@@ -256,7 +256,7 @@ temp/
256
256
  *.temp
257
257
 
258
258
  # VDK specific (comment out if you want AI to see these)
259
- # .vdk/rules/
259
+ # .vdk/blueprints/rules/
260
260
  # vdk.config.json
261
261
  `;
262
262
 
@@ -424,7 +424,7 @@ Reference this rule with @vdk-integration when working with VDK CLI.
424
424
 
425
425
  ## Integration Features
426
426
  - **Automatic Detection**: VDK CLI detects Cursor configuration
427
- - **Rule Generation**: Creates Cursor-compatible .vdk/rules
427
+ - **Rule Generation**: Creates Cursor-compatible .vdk/blueprints/rules
428
428
  - **MDC Format**: Proper metadata for rule activation
429
429
  - **Cross-IDE Compatibility**: Works with multiple AI assistants
430
430
 
@@ -118,7 +118,7 @@ export class GenericAIIntegration extends BaseIntegration {
118
118
  }
119
119
  }
120
120
  }
121
- } catch (_error) {
121
+ } catch {
122
122
  // Ignore file read errors
123
123
  }
124
124
  }
@@ -171,7 +171,7 @@ export class GenericAIIntegration extends BaseIntegration {
171
171
  }
172
172
  }
173
173
  }
174
- } catch (_error) {
174
+ } catch {
175
175
  // Ignore package.json read errors
176
176
  }
177
177
  }
@@ -235,7 +235,7 @@ export class GenericAIIntegration extends BaseIntegration {
235
235
  detection.confidence = 'low';
236
236
  }
237
237
  }
238
- } catch (_error) {
238
+ } catch {
239
239
  // Ignore directory read errors
240
240
  }
241
241
  }
@@ -265,7 +265,7 @@ export class GenericAIIntegration extends BaseIntegration {
265
265
  'VDK can help standardize AI tool configurations across your project'
266
266
  );
267
267
  detection.recommendations.push(
268
- 'Review .vdk/rules/ for AI assistant optimization opportunities'
268
+ 'Review .vdk/blueprints/rules/ for AI assistant optimization opportunities'
269
269
  );
270
270
  } else {
271
271
  detection.recommendations.push('Well-configured AI setup detected');
@@ -283,10 +283,10 @@ export class GenericAIIntegration extends BaseIntegration {
283
283
  getConfigPaths() {
284
284
  return {
285
285
  projectConfig: path.join(this.projectPath, '.ai'),
286
- rulesPath: path.join(this.projectPath, '.vdk', 'rules'),
286
+ rulesPath: path.join(this.projectPath, '.vdk', 'blueprints', 'rules'),
287
287
  configFile: path.join(this.projectPath, '.aiconfig.json'),
288
288
  aiConfig: path.join(this.projectPath, '.ai'),
289
- vdkRules: path.join(this.projectPath, '.vdk', 'rules'),
289
+ vdkRules: path.join(this.projectPath, '.vdk', 'blueprints', 'rules'),
290
290
  aiConfigFiles: [
291
291
  path.join(this.projectPath, '.aiconfig.json'),
292
292
  path.join(this.projectPath, 'ai-config.json'),
@@ -319,8 +319,8 @@ export class GenericAIIntegration extends BaseIntegration {
319
319
  console.log('Setting up generic AI platform integration...');
320
320
  }
321
321
 
322
- // Ensure .vdk/rules directory exists
323
- const rulesPath = path.join(this.projectPath, '.vdk', 'rules');
322
+ // Ensure .vdk/blueprints/rules directory exists
323
+ const rulesPath = path.join(this.projectPath, '.vdk', 'blueprints', 'rules');
324
324
  await this.ensureDirectory(rulesPath);
325
325
 
326
326
  // Create basic AI configuration if none exists
@@ -364,7 +364,7 @@ This configuration was automatically generated by VDK to provide basic AI assist
364
364
 
365
365
  - AI assistants can use this configuration to better understand your project structure
366
366
  - Customize this file to include project-specific patterns and conventions
367
- - Add more specific rules in the .vdk/rules/ directory as needed
367
+ - Add more specific rules in the .vdk/blueprints/rules/ directory as needed
368
368
 
369
369
  ## Generated
370
370
 
@@ -170,7 +170,7 @@ export class GenericIDEIntegration extends BaseIntegration {
170
170
  break;
171
171
  }
172
172
  }
173
- } catch (_error) {
173
+ } catch {
174
174
  // Ignore file read errors
175
175
  }
176
176
  }
@@ -295,7 +295,7 @@ export class GenericIDEIntegration extends BaseIntegration {
295
295
  if (verbose) {
296
296
  console.log('No IDEs detected - setting up generic configuration');
297
297
  }
298
- // Set up generic .vdk/rules configuration
298
+ // Set up generic .vdk/blueprints/rules configuration
299
299
  await this.setupGenericConfiguration(options);
300
300
  return true;
301
301
  }
@@ -330,11 +330,11 @@ export class GenericIDEIntegration extends BaseIntegration {
330
330
  }
331
331
 
332
332
  /**
333
- * Set up generic .vdk/rules configuration
333
+ * Set up generic .vdk/blueprints/rules configuration
334
334
  * @param {Object} options - Configuration options
335
335
  */
336
336
  async setupGenericConfiguration(options = {}) {
337
- const genericRulesPath = path.join(this.projectPath, '.vdk', 'rules');
337
+ const genericRulesPath = path.join(this.projectPath, '.vdk', 'blueprints', 'rules');
338
338
  await this.ensureDirectory(genericRulesPath);
339
339
  await this.createInitialRules(genericRulesPath, 'generic', options);
340
340
  }
@@ -391,7 +391,7 @@ export class GenericIDEIntegration extends BaseIntegration {
391
391
  }
392
392
 
393
393
  // Sort by confidence and return the highest
394
- const sorted = [...detectedIDEs].sort((a, b) => {
394
+ const sorted = [...detectedIDEs].toSorted((a, b) => {
395
395
  const confidenceOrder = { high: 3, medium: 2, low: 1, none: 0 };
396
396
  return confidenceOrder[b.confidence] - confidenceOrder[a.confidence];
397
397
  });
@@ -493,7 +493,7 @@ export class IntegrationManager {
493
493
 
494
494
  // Sort by project-specific config first, then confidence level
495
495
  const confidenceOrder = { high: 3, medium: 2, low: 1, none: 0 };
496
- const sorted = [...activeIntegrations].sort((a, b) => {
496
+ const sorted = [...activeIntegrations].toSorted((a, b) => {
497
497
  // First priority: Project-specific config beats global config, regardless of confidence
498
498
  const aHasProjectConfig = a.detection?.hasProjectSpecificConfig;
499
499
  const bHasProjectConfig = b.detection?.hasProjectSpecificConfig;
@@ -82,7 +82,7 @@ export class JetBrainsIntegration extends BaseIntegration {
82
82
  detection.confidence = 'medium';
83
83
  }
84
84
  }
85
- } catch (_error) {
85
+ } catch {
86
86
  // Process detection failed - not critical
87
87
  }
88
88
 
@@ -165,7 +165,7 @@ export class JetBrainsIntegration extends BaseIntegration {
165
165
  return path.join(cacheDir, jetbrainsVersions[0], 'mcp');
166
166
  }
167
167
  }
168
- } catch (_error) {
168
+ } catch {
169
169
  // Ignore errors in path detection
170
170
  }
171
171
 
@@ -193,7 +193,7 @@ export class JetBrainsIntegration extends BaseIntegration {
193
193
  }
194
194
 
195
195
  return [...new Set(jetbrainsProcesses)]; // Remove duplicates
196
- } catch (_error) {
196
+ } catch {
197
197
  return [];
198
198
  }
199
199
  }
@@ -147,7 +147,7 @@ class VSCodeVariantIntegration extends BaseIntegration {
147
147
  detection.confidence = 'medium';
148
148
  }
149
149
  }
150
- } catch (_error) {
150
+ } catch {
151
151
  // Process detection failed - not critical
152
152
  }
153
153
 
@@ -189,7 +189,7 @@ class VSCodeVariantIntegration extends BaseIntegration {
189
189
 
190
190
  const names = processNames[this.variant] || ['code'];
191
191
  return names.some(name => processes.includes(name));
192
- } catch (_error) {
192
+ } catch {
193
193
  return false;
194
194
  }
195
195
  }
@@ -289,7 +289,7 @@ export class WindsurfContextIntegration extends BaseIntegration {
289
289
  },
290
290
  projectAwareness: {
291
291
  enabled: true,
292
- includeFiles: ['README.md', 'package.json', '*.config.*', '.vdk/rules/**'],
292
+ includeFiles: ['README.md', 'package.json', '*.config.*', '.vdk/blueprints/rules/**'],
293
293
  excludePatterns: ['node_modules/**', 'dist/**', '*.log', '.git/**'],
294
294
  },
295
295
  };
@@ -53,7 +53,7 @@ export class ZedIntegration extends BaseIntegration {
53
53
  indicators.push('Zed process is currently running');
54
54
  if (confidence === 'none') confidence = 'medium';
55
55
  }
56
- } catch (_error) {
56
+ } catch {
57
57
  // Process detection failed - not critical
58
58
  }
59
59
 
@@ -118,7 +118,7 @@ export class ZedIntegration extends BaseIntegration {
118
118
  return (
119
119
  processes.toLowerCase().includes('zed') || processes.toLowerCase().includes('zed-editor')
120
120
  );
121
- } catch (_error) {
121
+ } catch {
122
122
  return false;
123
123
  }
124
124
  }
package/src/ir/README.md CHANGED
@@ -26,7 +26,7 @@ VDK-CLI/src/ir/
26
26
  ### Base Platforms (index.js + generators.js)
27
27
 
28
28
  - **Claude Code** - Full support (.claude/, CLAUDE.md, agents, rules, commands, skills)
29
- - **Cursor** - MDC format, .cursorrules, glob patterns, activation modes
29
+ - **Cursor** - MDC format in `.cursor/rules/*.mdc`, glob patterns, activation modes
30
30
  - **GitHub Copilot** - 3000 char limit with smart truncation
31
31
  - **Windsurf** - Rules and workflows
32
32
 
@@ -273,7 +273,7 @@ const { lossInfo } = irToCopilot(ir);
273
273
  - āœ… MDC (Markdown Component) format
274
274
  - āœ… Glob patterns for auto-attachment
275
275
  - āœ… Activation modes: always, auto-attached, agent-requested, manual
276
- - āœ… .cursorrules and .cursor/rules/\*.mdc
276
+ - āœ… `.cursor/rules/*.mdc`
277
277
  - āŒ No agents
278
278
  - āŒ No settings
279
279
 
@@ -104,11 +104,10 @@ export function irToClaude(ir, _options = {}) {
104
104
  * @param {IntermediateRepresentation} ir - IR to convert
105
105
  * @param {Object} [options] - Generation options
106
106
  * @param {boolean} [options.useMDC=true] - Use MDC format for .cursor/rules/
107
- * @param {boolean} [options.singleFile=false] - Generate single .cursorrules file
108
107
  * @returns {{content: string, filePath: string, lossInfo: Array}}
109
108
  */
110
109
  export function irToCursor(ir, options = {}) {
111
- const { useMDC = true, singleFile = false } = options;
110
+ const { useMDC = true } = options;
112
111
  const lossInfo = trackConversionLoss(ir, ir, 'cursor');
113
112
 
114
113
  // Track agent conversion (Cursor doesn't support agents)
@@ -122,9 +121,9 @@ export function irToCursor(ir, options = {}) {
122
121
  }
123
122
 
124
123
  let content = '';
125
- const filePath = singleFile ? '.cursorrules' : `.cursor/rules/${sanitizeFileName(ir.name)}.mdc`;
124
+ const filePath = `.cursor/rules/${sanitizeFileName(ir.name)}.mdc`;
126
125
 
127
- if (useMDC && !singleFile) {
126
+ if (useMDC) {
128
127
  // Generate MDC format
129
128
  const frontmatter = {};
130
129
  if (ir.description) frontmatter.description = ir.description;
@@ -240,7 +239,7 @@ function priorityTruncate(content, sections, maxLength) {
240
239
  }
241
240
 
242
241
  // Sort sections by priority
243
- const sortedSections = [...sections].sort((a, b) => {
242
+ const sortedSections = [...sections].toSorted((a, b) => {
244
243
  const aTitle = a.title?.toLowerCase() || '';
245
244
  const bTitle = b.title?.toLowerCase() || '';
246
245
 
package/src/ir/index.js CHANGED
@@ -416,12 +416,7 @@ export function detectPlatformFromPath(filePath) {
416
416
 
417
417
  if (normalizedPath.includes('.claude') || normalizedPath.includes('claude.md'))
418
418
  return 'claude-code';
419
- if (
420
- normalizedPath.includes('.cursor') ||
421
- normalizedPath.includes('.cursorrules') ||
422
- normalizedPath.endsWith('.mdc')
423
- )
424
- return 'cursor';
419
+ if (normalizedPath.includes('.cursor') || normalizedPath.endsWith('.mdc')) return 'cursor';
425
420
  if (normalizedPath.includes('copilot') || normalizedPath.includes('.github/copilot'))
426
421
  return 'github-copilot';
427
422
  if (normalizedPath.includes('.windsurf')) return 'windsurf';
@@ -99,7 +99,7 @@ function getFileHash(filePath) {
99
99
  try {
100
100
  const content = fs.readFileSync(filePath, 'utf8');
101
101
  return crypto.createHash('sha256').update(content).digest('hex').substring(0, 16);
102
- } catch (_error) {
102
+ } catch {
103
103
  return null;
104
104
  }
105
105
  }
@@ -119,7 +119,7 @@ function getCachedFileStats(filePath) {
119
119
  if (stats.mtimeMs === cached.mtime && stats.size === cached.size) {
120
120
  return cached;
121
121
  }
122
- } catch (_error) {
122
+ } catch {
123
123
  // File no longer exists or error reading
124
124
  fileStatsCache.delete(filePath);
125
125
  return null;
@@ -141,7 +141,7 @@ function getCachedFileStats(filePath) {
141
141
 
142
142
  fileStatsCache.set(filePath, fileStats);
143
143
  return fileStats;
144
- } catch (_error) {
144
+ } catch {
145
145
  return null;
146
146
  }
147
147
  }
@@ -35,7 +35,7 @@ export class McpManager {
35
35
  if (config.mcpServers) {
36
36
  this.configs.set(loc, config.mcpServers);
37
37
  }
38
- } catch (_err) {
38
+ } catch {
39
39
  // Ignore missing files
40
40
  }
41
41
  }
@@ -82,7 +82,7 @@ export class McpManager {
82
82
  // Check common paths if none found
83
83
  if (ruleDirectories.length === 0) {
84
84
  const commonPaths = [
85
- '.vdk/rules',
85
+ '.vdk/blueprints/rules',
86
86
  '.vscode/ai-rules',
87
87
  '.cursor/rules',
88
88
  '.claude/rules',
@@ -101,7 +101,7 @@ export class McpManager {
101
101
 
102
102
  // Create default if forced
103
103
  if (ruleDirectories.length === 0 && force) {
104
- const defaultPath = path.join(projectPath, '.vdk/rules');
104
+ const defaultPath = path.join(projectPath, '.vdk/blueprints/rules');
105
105
  if (!existsSync(defaultPath)) {
106
106
  await fs.mkdir(defaultPath, { recursive: true });
107
107
  }
@@ -22,7 +22,8 @@ import { MigrationBackup } from './core/MigrationBackup.js';
22
22
  export class AutoMigrator {
23
23
  constructor(projectPath) {
24
24
  this.projectPath = projectPath;
25
- this.importPath = path.join(projectPath, '.vdk', 'import');
25
+ this.importPath = path.join(projectPath, '.vdk', 'migrate');
26
+ this.legacyImportPath = path.join(projectPath, '.vdk', 'import');
26
27
  this.projectScanner = new ProjectScanner({ projectPath: projectPath });
27
28
  this.technologyAnalyzer = new TechnologyAnalyzer({ verbose: false });
28
29
  this.patternDetector = new PatternDetector({ verbose: false });
@@ -46,11 +47,11 @@ export class AutoMigrator {
46
47
 
47
48
  try {
48
49
  // 1. Scan import directory for old rules
49
- spinner.text = 'Scanning .vdk/import/ for AI rules...';
50
+ spinner.text = 'Scanning .vdk/migrate/ for AI rules...';
50
51
  const detectedRules = await this.detectImportedRules();
51
52
 
52
53
  if (detectedRules.length === 0) {
53
- spinner.info('No rules found in .vdk/import/');
54
+ spinner.info('No rules found in .vdk/migrate/ (legacy: .vdk/import/)');
54
55
  this.showImportInstructions();
55
56
  return { success: false, reason: 'no_rules_found' };
56
57
  }
@@ -160,28 +161,34 @@ export class AutoMigrator {
160
161
  */
161
162
  async detectImportedRules() {
162
163
  const rules = [];
164
+ const seenFiles = new Set();
165
+ const candidatePaths = [this.importPath, this.legacyImportPath];
163
166
 
164
- try {
165
- await fs.access(this.importPath);
166
- } catch {
167
- // Import directory doesn't exist
168
- return rules;
169
- }
167
+ for (const candidatePath of candidatePaths) {
168
+ try {
169
+ await fs.access(candidatePath);
170
+ } catch {
171
+ continue;
172
+ }
170
173
 
171
- try {
172
- const importFiles = await fs.readdir(this.importPath, { withFileTypes: true });
173
-
174
- for (const entry of importFiles) {
175
- if (entry.isFile()) {
176
- const filePath = path.join(this.importPath, entry.name);
177
- const detectedRule = await this.detectRuleType(filePath);
178
- if (detectedRule) {
179
- rules.push(detectedRule);
174
+ try {
175
+ const importFiles = await fs.readdir(candidatePath, { withFileTypes: true });
176
+
177
+ for (const entry of importFiles) {
178
+ if (entry.isFile()) {
179
+ const filePath = path.join(candidatePath, entry.name);
180
+ if (seenFiles.has(filePath)) continue;
181
+
182
+ const detectedRule = await this.detectRuleType(filePath);
183
+ if (detectedRule) {
184
+ rules.push(detectedRule);
185
+ seenFiles.add(filePath);
186
+ }
180
187
  }
181
188
  }
189
+ } catch (error) {
190
+ console.warn(chalk.yellow(`Warning: Could not read import directory: ${error.message}`));
182
191
  }
183
- } catch (error) {
184
- console.warn(chalk.yellow(`Warning: Could not read import directory: ${error.message}`));
185
192
  }
186
193
 
187
194
  return rules;
@@ -597,14 +604,20 @@ export class AutoMigrator {
597
604
  * Clean up the import directory after successful migration
598
605
  */
599
606
  async cleanImportDirectory() {
600
- try {
601
- const files = await fs.readdir(this.importPath);
602
- for (const file of files) {
603
- await fs.unlink(path.join(this.importPath, file));
607
+ const candidatePaths = [this.importPath, this.legacyImportPath];
608
+
609
+ for (const candidatePath of candidatePaths) {
610
+ try {
611
+ const files = await fs.readdir(candidatePath);
612
+ for (const file of files) {
613
+ await fs.unlink(path.join(candidatePath, file));
614
+ }
615
+ await fs.rmdir(candidatePath);
616
+ } catch (error) {
617
+ if (error?.code !== 'ENOENT') {
618
+ console.warn(chalk.yellow(`Warning: Could not clean import directory: ${error.message}`));
619
+ }
604
620
  }
605
- await fs.rmdir(this.importPath);
606
- } catch (error) {
607
- console.warn(chalk.yellow(`Warning: Could not clean import directory: ${error.message}`));
608
621
  }
609
622
  }
610
623
 
@@ -702,12 +715,13 @@ export class AutoMigrator {
702
715
  showImportInstructions() {
703
716
  console.log(chalk.cyan('\nšŸ“ To migrate existing AI rules:'));
704
717
  console.log(chalk.gray('1. Create the import directory:'));
705
- console.log(chalk.gray(' mkdir -p .vdk/import'));
718
+ console.log(chalk.gray(' mkdir -p .vdk/migrate'));
706
719
  console.log(chalk.gray('\n2. Copy your existing rule files:'));
707
- console.log(chalk.gray(' cp .cursorrules .vdk/import/'));
708
- console.log(chalk.gray(' cp .claude/memory.md .vdk/import/'));
709
- console.log(chalk.gray(' cp .github/copilot-instructions.json .vdk/import/'));
710
- console.log(chalk.gray(' cp .windsurf/rules.xml .vdk/import/'));
720
+ console.log(chalk.gray(' cp .cursorrules .vdk/migrate/'));
721
+ console.log(chalk.gray(' cp .claude/memory.md .vdk/migrate/'));
722
+ console.log(chalk.gray(' cp .github/copilot-instructions.json .vdk/migrate/'));
723
+ console.log(chalk.gray(' cp .windsurf/rules.xml .vdk/migrate/'));
724
+ console.log(chalk.gray('\n # Legacy path .vdk/import/ is still supported during transition'));
711
725
  console.log(chalk.gray('\n3. Run migration:'));
712
726
  console.log(chalk.gray(' vdk migrate'));
713
727
  }
@@ -27,7 +27,7 @@ export class ContextConverter {
27
27
  * @returns {Object} VDK blueprint or command
28
28
  */
29
29
  async convertClaudeCode(context) {
30
- const { fileName, bodyContent, metadata, claudeSpecific } = context;
30
+ const { fileName, bodyContent, claudeSpecific } = context;
31
31
 
32
32
  // Determine if it's a command or blueprint
33
33
  const isCommand = claudeSpecific?.hasSlashCommands || fileName.includes('command');
@@ -105,7 +105,7 @@ export class ContextConverter {
105
105
  * @returns {Object} VDK blueprint
106
106
  */
107
107
  async convertGitHubCopilot(context) {
108
- const { bodyContent, copilotSpecific } = context;
108
+ const { copilotSpecific } = context;
109
109
 
110
110
  const reviewType = copilotSpecific?.hasSecurityRules
111
111
  ? 'security'
@@ -290,7 +290,7 @@ export class ContextConverter {
290
290
  * @returns {Object} Memory blueprint
291
291
  */
292
292
  convertToMemoryBlueprint(context) {
293
- const { bodyContent, sections } = context;
293
+ const {} = context;
294
294
 
295
295
  return this.convertToBlueprint(context, {
296
296
  category: 'core',
@@ -68,7 +68,7 @@ export class SchemaV3Migrator {
68
68
  }
69
69
  }
70
70
  } catch (error) {
71
- throw new Error(`v3 migration failed: ${error.message}`);
71
+ throw new Error(`v3 migration failed: ${error.message}`, { cause: error });
72
72
  }
73
73
 
74
74
  return results;
@@ -75,7 +75,7 @@ export class MigrationBackup {
75
75
 
76
76
  return this.migrationId;
77
77
  } catch (error) {
78
- throw new Error(`Failed to create migration backup: ${error.message}`);
78
+ throw new Error(`Failed to create migration backup: ${error.message}`, { cause: error });
79
79
  }
80
80
  }
81
81
 
@@ -137,7 +137,7 @@ export class MigrationBackup {
137
137
 
138
138
  return { success: true, restoredCount, backupId };
139
139
  } catch (error) {
140
- throw new Error(`Rollback failed: ${error.message}`);
140
+ throw new Error(`Rollback failed: ${error.message}`, { cause: error });
141
141
  }
142
142
  }
143
143
 
@@ -175,7 +175,7 @@ export class MigrationBackup {
175
175
  path: path.join(this.backupDir, entry.name),
176
176
  });
177
177
  }
178
- } catch (_error) {
178
+ } catch {
179
179
  // Skip invalid backup entries
180
180
  console.warn(chalk.yellow(`Warning: Invalid backup metadata for ${entry.name}`));
181
181
  }
@@ -183,7 +183,7 @@ export class MigrationBackup {
183
183
  }
184
184
 
185
185
  // Sort by timestamp (newest first)
186
- return backups.sort((a, b) => new Date(b.timestamp) - new Date(a.timestamp));
186
+ return backups.toSorted((a, b) => new Date(b.timestamp) - new Date(a.timestamp));
187
187
  } catch (error) {
188
188
  console.warn(chalk.yellow(`Warning: Failed to list backups: ${error.message}`));
189
189
  return [];
@@ -263,7 +263,7 @@ export class MigrationBackup {
263
263
  }
264
264
 
265
265
  return true;
266
- } catch (_error) {
266
+ } catch {
267
267
  return false;
268
268
  }
269
269
  }
@@ -286,24 +286,30 @@ export class MigrationBackup {
286
286
  });
287
287
  }
288
288
 
289
- // VDK rules directory
290
- const rulesDir = path.join(this.projectPath, '.vdk', 'rules');
289
+ // VDK rule artifacts directory
290
+ const rulesDir = path.join(this.projectPath, '.vdk', 'blueprints', 'rules');
291
291
  if (await this.fileExists(rulesDir)) {
292
292
  targets.push({
293
293
  type: 'directory',
294
294
  path: rulesDir,
295
- relativePath: '.vdk/rules',
295
+ relativePath: '.vdk/blueprints/rules',
296
296
  });
297
297
  }
298
298
 
299
- // VDK import directory (if it contains user data)
300
- const importDir = path.join(this.projectPath, '.vdk', 'import');
301
- if (await this.fileExists(importDir)) {
302
- targets.push({
303
- type: 'directory',
304
- path: importDir,
305
- relativePath: '.vdk/import',
306
- });
299
+ // VDK migration staging directories (primary + legacy)
300
+ const migrationDirs = [
301
+ { path: path.join(this.projectPath, '.vdk', 'migrate'), relativePath: '.vdk/migrate' },
302
+ { path: path.join(this.projectPath, '.vdk', 'import'), relativePath: '.vdk/import' },
303
+ ];
304
+
305
+ for (const migrationDir of migrationDirs) {
306
+ if (await this.fileExists(migrationDir.path)) {
307
+ targets.push({
308
+ type: 'directory',
309
+ path: migrationDir.path,
310
+ relativePath: migrationDir.relativePath,
311
+ });
312
+ }
307
313
  }
308
314
 
309
315
  // IDE-specific configuration files that VDK might modify
@@ -498,7 +504,7 @@ export class MigrationBackup {
498
504
  * @private
499
505
  */
500
506
  async removeCurrentVDKFiles(options = {}) {
501
- const filesToRemove = ['vdk.config.json', '.vdk/rules'];
507
+ const filesToRemove = ['vdk.config.json', '.vdk/blueprints/rules'];
502
508
 
503
509
  for (const relativePath of filesToRemove) {
504
510
  const fullPath = path.join(this.projectPath, relativePath);