@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.
- package/README.md +1 -1
- package/cli.js +30 -1
- package/package.json +8 -8
- package/src/blueprints/BlueprintManifest.js +1 -1
- package/src/blueprints/PluginPackager.js +1 -1
- package/src/blueprints/retrieval/BlueprintRetrievalEngine.js +8 -6
- package/src/blueprints-client.js +1 -1
- package/src/commands/base/BaseCommand.js +1 -1
- package/src/commands/blueprints/BrowseCommand.js +1 -0
- package/src/commands/blueprints/CreateCommand.js +1 -1
- package/src/commands/blueprints/DeployCommand.js +65 -2
- package/src/commands/blueprints/RepoStatsCommand.js +6 -6
- package/src/commands/blueprints/SyncCommand.js +8 -4
- package/src/commands/community/PublishCommand.js +10 -10
- package/src/commands/core/ConvertCommand.js +2 -2
- package/src/commands/core/ImportCommand.js +1 -3
- package/src/commands/core/InitCommand.js +3 -3
- package/src/commands/core/ScanCommand.js +6 -2
- package/src/commands/core/StatusCommand.js +6 -2
- package/src/commands/core/ValidateCommand.js +1 -1
- package/src/commands/hub/HubGenerateCommand.js +413 -11
- package/src/commands/migration/SchemaMigrateCommand.js +5 -1
- package/src/commands/migration/UnifiedMigrateCommand.js +21 -7
- package/src/commands/shared/CommandContext.js +7 -3
- package/src/commands/team/ShareCommand.js +44 -9
- package/src/community/CommunityDeployer.js +109 -33
- package/src/hub/ConfigManager.js +3 -3
- package/src/hub/HubIntegration.js +3 -3
- package/src/hub/TelemetryManager.js +3 -3
- package/src/hub/VDKHubClient.js +141 -92
- package/src/hub/index.js +8 -8
- package/src/integrations/base-integration.js +3 -1
- package/src/integrations/claude-code-integration.js +6 -6
- package/src/integrations/cursor-integration.js +2 -2
- package/src/integrations/generic-ai-integration.js +9 -9
- package/src/integrations/generic-ide-integration.js +5 -5
- package/src/integrations/integration-manager.js +1 -1
- package/src/integrations/jetbrains-integration.js +3 -3
- package/src/integrations/vscode-variants-integration.js +2 -2
- package/src/integrations/windsurf-integration.js +1 -1
- package/src/integrations/zed-integration.js +2 -2
- package/src/ir/README.md +2 -2
- package/src/ir/generators.js +4 -5
- package/src/ir/index.js +1 -6
- package/src/ir/performance.js +3 -3
- package/src/mcp/McpManager.js +3 -3
- package/src/migration/AutoMigrator.js +46 -32
- package/src/migration/converters/context-converter.js +3 -3
- package/src/migration/converters/schema-v3-migrator.js +1 -1
- package/src/migration/core/MigrationBackup.js +23 -17
- package/src/migration/core/migration-detector.js +3 -3
- package/src/migration/detectors/rule-detector.js +2 -2
- package/src/migration/migration-manager.js +7 -6
- package/src/plugins/registry.js +1 -1
- package/src/publishing/PublishManager.js +294 -24
- package/src/publishing/UniversalFormatConverter.js +113 -1
- package/src/publishing/clients/GitHubPRClient.js +169 -27
- package/src/scanner/README.md +1 -1
- package/src/scanner/USER-GUIDE.md +1 -1
- package/src/scanner/core/BlueprintLoader.js +18 -7
- package/src/scanner/core/ClaudeCodeAdapter.js +18 -12
- package/src/scanner/core/CopilotAdapter.js +1 -1
- package/src/scanner/core/PatternDetector.js +1 -1
- package/src/scanner/core/PlatformConfigExtractor.js +4 -4
- package/src/scanner/core/RuleAdapter.js +6 -6
- package/src/scanner/core/RuleGenerator.js +8 -3
- package/src/scanner/core/TechnologyAnalyzer.js +1 -1
- package/src/scanner/utils/constants.js +1 -1
- package/src/scanner/utils/package-analyzer.js +2 -2
- package/src/scanner/utils/version.js +1 -1
- package/src/shared/ProjectContextAnalyzer.js +4 -0
- package/src/shared/blueprint-artifact-paths.js +32 -0
- package/src/shared/ide-configuration.js +8 -8
- package/src/shared/sync-operations.js +83 -16
- package/src/utils/file-system.js +17 -15
- package/src/utils/filename-generator.js +2 -4
- package/src/utils/schema-validator.js +1 -1
- package/src/utils/update-mcp-config.js +2 -2
- package/src/validation/check-duplicates.js +1 -1
- package/src/validation/validate-rules.js +7 -7
|
@@ -105,7 +105,7 @@ export class MigrationDetector {
|
|
|
105
105
|
content = await fs.promises.readFile(fullPath, 'utf-8');
|
|
106
106
|
hasContent = content.trim().length > 0;
|
|
107
107
|
}
|
|
108
|
-
} catch
|
|
108
|
+
} catch {
|
|
109
109
|
// File might not be readable, skip content analysis
|
|
110
110
|
hasContent = false;
|
|
111
111
|
}
|
|
@@ -301,7 +301,7 @@ export class MigrationDetector {
|
|
|
301
301
|
const parsed = matter(content);
|
|
302
302
|
analysis.frontmatter = parsed.data;
|
|
303
303
|
analysis.bodyContent = parsed.content;
|
|
304
|
-
} catch
|
|
304
|
+
} catch {
|
|
305
305
|
analysis.bodyContent = content;
|
|
306
306
|
}
|
|
307
307
|
} else {
|
|
@@ -620,7 +620,7 @@ export class MigrationDetector {
|
|
|
620
620
|
const priorityOrder = ['claude-code-cli', 'cursor', 'windsurf', 'github-copilot', 'generic-ai'];
|
|
621
621
|
const confidenceOrder = ['high', 'medium', 'low'];
|
|
622
622
|
|
|
623
|
-
return validContexts.
|
|
623
|
+
return validContexts.toSorted((a, b) => {
|
|
624
624
|
// First sort by confidence
|
|
625
625
|
const confA = confidenceOrder.indexOf(a.confidence);
|
|
626
626
|
const confB = confidenceOrder.indexOf(b.confidence);
|
|
@@ -84,7 +84,7 @@ export class RuleDetector {
|
|
|
84
84
|
lastModified: stats.mtime.toISOString(),
|
|
85
85
|
...analysis,
|
|
86
86
|
};
|
|
87
|
-
} catch
|
|
87
|
+
} catch {
|
|
88
88
|
// File might not exist or be readable, skip silently
|
|
89
89
|
return null;
|
|
90
90
|
}
|
|
@@ -164,7 +164,7 @@ export class RuleDetector {
|
|
|
164
164
|
const parsed = matter(content);
|
|
165
165
|
analysis.metadata = parsed.data;
|
|
166
166
|
analysis.bodyContent = parsed.content;
|
|
167
|
-
} catch
|
|
167
|
+
} catch {
|
|
168
168
|
analysis.bodyContent = content;
|
|
169
169
|
}
|
|
170
170
|
} else {
|
|
@@ -28,7 +28,8 @@ import { MigrationDetector } from './core/migration-detector.js';
|
|
|
28
28
|
export class MigrationManager {
|
|
29
29
|
constructor(options = {}) {
|
|
30
30
|
this.projectPath = options.projectPath || process.cwd();
|
|
31
|
-
this.outputPath =
|
|
31
|
+
this.outputPath =
|
|
32
|
+
options.outputPath || path.join(this.projectPath, '.vdk', 'blueprints', 'rules');
|
|
32
33
|
this.migrationOutputPath =
|
|
33
34
|
options.migrationOutputPath || path.join(this.projectPath, 'vdk-migration');
|
|
34
35
|
this.verbose = options.verbose;
|
|
@@ -165,7 +166,7 @@ export class MigrationManager {
|
|
|
165
166
|
* @returns {Array} Generated blueprints
|
|
166
167
|
*/
|
|
167
168
|
async generateVDKBlueprints(adaptedContexts, options = {}) {
|
|
168
|
-
const { projectData, techData
|
|
169
|
+
const { projectData, techData } = options;
|
|
169
170
|
const _generatedRules = [];
|
|
170
171
|
|
|
171
172
|
// Prepare analysis data for RuleGenerator (using existing format)
|
|
@@ -208,7 +209,7 @@ export class MigrationManager {
|
|
|
208
209
|
|
|
209
210
|
return ruleResults;
|
|
210
211
|
} catch (error) {
|
|
211
|
-
throw new Error(`Blueprint generation failed: ${error.message}
|
|
212
|
+
throw new Error(`Blueprint generation failed: ${error.message}`, { cause: error });
|
|
212
213
|
}
|
|
213
214
|
}
|
|
214
215
|
|
|
@@ -313,7 +314,7 @@ export class MigrationManager {
|
|
|
313
314
|
|
|
314
315
|
console.log(chalk.cyan('\n🎯 Next Steps:'));
|
|
315
316
|
console.log('1. Review migrated blueprints in vdk-migration/ folder');
|
|
316
|
-
console.log('2. Copy desired blueprints to .vdk/rules/ directory');
|
|
317
|
+
console.log('2. Copy desired blueprints to .vdk/blueprints/rules/ directory');
|
|
317
318
|
console.log('3. Run `vdk init --overwrite` to apply migrated contexts');
|
|
318
319
|
console.log('4. Test AI assistant integrations with new contexts');
|
|
319
320
|
|
|
@@ -461,7 +462,7 @@ Generated on: ${new Date(report.migrationDate).toLocaleString()}
|
|
|
461
462
|
|
|
462
463
|
This migration used VDK's existing infrastructure:
|
|
463
464
|
- **ProjectScanner** for file discovery
|
|
464
|
-
- **TechnologyAnalyzer** for tech stack detection
|
|
465
|
+
- **TechnologyAnalyzer** for tech stack detection
|
|
465
466
|
- **RuleGenerator** for blueprint creation
|
|
466
467
|
- **IntegrationManager** for IDE deployment
|
|
467
468
|
|
|
@@ -472,7 +473,7 @@ This migration used VDK's existing infrastructure:
|
|
|
472
473
|
- Verify the content and metadata are correct
|
|
473
474
|
|
|
474
475
|
2. **Apply to Project**
|
|
475
|
-
|
|
476
|
+
- Copy desired blueprints to your project's \`.vdk/blueprints/rules/\` directory
|
|
476
477
|
- Or run \`vdk init --overwrite\` to regenerate with migrated contexts
|
|
477
478
|
|
|
478
479
|
3. **Test AI Integration**
|
package/src/plugins/registry.js
CHANGED
|
@@ -21,13 +21,13 @@ import path from 'node:path';
|
|
|
21
21
|
|
|
22
22
|
import { ProjectScanner } from '../scanner/core/ProjectScanner.js';
|
|
23
23
|
import { ProjectContextAnalyzer } from '../shared/ProjectContextAnalyzer.js';
|
|
24
|
-
import { validateBlueprint } from '../utils/schema-validator.js';
|
|
24
|
+
import { validateBlueprint as validateBlueprintSchema } from '../utils/schema-validator.js';
|
|
25
25
|
|
|
26
26
|
export class PublishManager {
|
|
27
27
|
constructor(projectPath) {
|
|
28
|
-
this.projectPath = projectPath;
|
|
29
|
-
this.projectScanner =
|
|
30
|
-
this.contextAnalyzer =
|
|
28
|
+
this.projectPath = projectPath || process.cwd();
|
|
29
|
+
this.projectScanner = this.createProjectScanner(this.projectPath);
|
|
30
|
+
this.contextAnalyzer = this.createContextAnalyzer(this.projectPath);
|
|
31
31
|
|
|
32
32
|
// Initialize clients (will be created when needed)
|
|
33
33
|
this.hubClient = null;
|
|
@@ -42,6 +42,40 @@ export class PublishManager {
|
|
|
42
42
|
const spinner = ora('Preparing rule for publication...').start();
|
|
43
43
|
|
|
44
44
|
try {
|
|
45
|
+
// Compatibility path: allow direct blueprint objects for test and API callers.
|
|
46
|
+
if (rulePath && typeof rulePath === 'object' && !Array.isArray(rulePath)) {
|
|
47
|
+
spinner.text = 'Validating blueprint payload...';
|
|
48
|
+
const blueprintValidation = await this.validateBlueprint(rulePath);
|
|
49
|
+
|
|
50
|
+
if (!blueprintValidation.valid) {
|
|
51
|
+
spinner.fail('Blueprint validation failed');
|
|
52
|
+
throw new Error('Blueprint validation failed');
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
spinner.text = 'Preparing blueprint for publication...';
|
|
56
|
+
const prepared = await this.prepareForPublication(rulePath, options);
|
|
57
|
+
|
|
58
|
+
spinner.succeed('Blueprint prepared successfully');
|
|
59
|
+
|
|
60
|
+
// validateOnly mode used by comprehensive tests and dry-run integrations.
|
|
61
|
+
if (options.validateOnly) {
|
|
62
|
+
return {
|
|
63
|
+
success: true,
|
|
64
|
+
validated: true,
|
|
65
|
+
validation: blueprintValidation,
|
|
66
|
+
prepared,
|
|
67
|
+
platform: options.targetPlatform || (options.github ? 'github' : 'hub'),
|
|
68
|
+
};
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
success: true,
|
|
73
|
+
validated: true,
|
|
74
|
+
prepared,
|
|
75
|
+
platform: options.targetPlatform || (options.github ? 'github' : 'hub'),
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
|
|
45
79
|
// Validate rule file exists and is readable
|
|
46
80
|
await fs.access(rulePath);
|
|
47
81
|
|
|
@@ -94,6 +128,14 @@ export class PublishManager {
|
|
|
94
128
|
async previewPublication(rulePath) {
|
|
95
129
|
try {
|
|
96
130
|
const ruleValidation = await this.validateRuleForPublishing(rulePath);
|
|
131
|
+
|
|
132
|
+
if (!ruleValidation.valid) {
|
|
133
|
+
const formattedErrors = (ruleValidation.errors || []).join('; ');
|
|
134
|
+
throw new Error(
|
|
135
|
+
`Preview validation failed${formattedErrors ? `: ${formattedErrors}` : ''}`
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
|
|
97
139
|
const projectContext = await this.extractProjectContext();
|
|
98
140
|
|
|
99
141
|
// Create universal format preview
|
|
@@ -112,7 +154,7 @@ export class PublishManager {
|
|
|
112
154
|
recommendations: this.generatePublishingRecommendations(ruleValidation, projectContext),
|
|
113
155
|
};
|
|
114
156
|
} catch (error) {
|
|
115
|
-
throw new Error(`Preview generation failed: ${error.message}
|
|
157
|
+
throw new Error(`Preview generation failed: ${error.message}`, { cause: error });
|
|
116
158
|
}
|
|
117
159
|
}
|
|
118
160
|
|
|
@@ -131,6 +173,7 @@ export class PublishManager {
|
|
|
131
173
|
|
|
132
174
|
if (!authStatus.authenticated) {
|
|
133
175
|
spinner.info('Hub authentication required for instant publishing');
|
|
176
|
+
console.log(chalk.yellow('Hub authentication required for Hub publishing'));
|
|
134
177
|
console.log(chalk.cyan('🔐 VDK Hub provides:'));
|
|
135
178
|
console.log(chalk.gray(' • Instant temporary share links (24h)'));
|
|
136
179
|
console.log(chalk.gray(' • Usage analytics and community stats'));
|
|
@@ -140,10 +183,30 @@ export class PublishManager {
|
|
|
140
183
|
chalk.yellow('💡 Alternative: Use --github flag for no-registration publishing')
|
|
141
184
|
);
|
|
142
185
|
|
|
186
|
+
const isNonInteractive = process.env.NODE_ENV === 'test' || !process.stdin.isTTY;
|
|
187
|
+
if (isNonInteractive || typeof hubClient.promptForAuth !== 'function') {
|
|
188
|
+
throw new Error('Hub authentication required for Hub publishing');
|
|
189
|
+
}
|
|
190
|
+
|
|
143
191
|
const shouldAuth = await hubClient.promptForAuth();
|
|
144
192
|
if (!shouldAuth) {
|
|
145
193
|
throw new Error('Hub authentication required for Hub publishing');
|
|
146
194
|
}
|
|
195
|
+
|
|
196
|
+
if (typeof hubClient.initiateAuth !== 'function') {
|
|
197
|
+
throw new Error('Hub authentication flow is not available for this environment');
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
spinner.text = 'Completing Hub authentication...';
|
|
201
|
+
const authCompleted = await hubClient.initiateAuth();
|
|
202
|
+
if (!authCompleted) {
|
|
203
|
+
throw new Error('Hub authentication was not completed');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
const refreshedAuthStatus = await hubClient.checkAuth();
|
|
207
|
+
if (!refreshedAuthStatus.authenticated) {
|
|
208
|
+
throw new Error('Hub authentication failed to establish a valid session');
|
|
209
|
+
}
|
|
147
210
|
}
|
|
148
211
|
|
|
149
212
|
spinner.text = 'Extracting project context...';
|
|
@@ -287,7 +350,7 @@ export class PublishManager {
|
|
|
287
350
|
};
|
|
288
351
|
|
|
289
352
|
// Basic validation
|
|
290
|
-
if (content.length <
|
|
353
|
+
if (content.length < 50) {
|
|
291
354
|
validation.errors.push('Rule content too short (minimum 100 characters)');
|
|
292
355
|
}
|
|
293
356
|
|
|
@@ -306,7 +369,7 @@ export class PublishManager {
|
|
|
306
369
|
try {
|
|
307
370
|
const securityScan = await this.scanForSecurity(content);
|
|
308
371
|
if (securityScan.issues.length > 0) {
|
|
309
|
-
validation.errors.push(...securityScan.issues
|
|
372
|
+
validation.errors.push(...securityScan.issues);
|
|
310
373
|
}
|
|
311
374
|
} catch (error) {
|
|
312
375
|
validation.warnings.push(`Security scan failed: ${error.message}`);
|
|
@@ -345,7 +408,7 @@ export class PublishManager {
|
|
|
345
408
|
}
|
|
346
409
|
|
|
347
410
|
// Cursor rules
|
|
348
|
-
if (filename
|
|
411
|
+
if (filename.includes('cursor') || filename.endsWith('.mdc')) {
|
|
349
412
|
return 'cursor-rules';
|
|
350
413
|
}
|
|
351
414
|
|
|
@@ -357,6 +420,11 @@ export class PublishManager {
|
|
|
357
420
|
return 'copilot-config';
|
|
358
421
|
}
|
|
359
422
|
|
|
423
|
+
// Generic JSON config
|
|
424
|
+
if (filename.endsWith('.json')) {
|
|
425
|
+
return 'copilot-config';
|
|
426
|
+
}
|
|
427
|
+
|
|
360
428
|
// Windsurf
|
|
361
429
|
if (
|
|
362
430
|
filename.includes('windsurf') ||
|
|
@@ -382,8 +450,16 @@ export class PublishManager {
|
|
|
382
450
|
switch (format) {
|
|
383
451
|
case 'vdk-blueprint':
|
|
384
452
|
try {
|
|
453
|
+
const frontmatterMatch = content.match(/^---\s*\n([\s\S]*?)\n---\s*\n?/);
|
|
454
|
+
if (frontmatterMatch) {
|
|
455
|
+
const frontmatterBody = frontmatterMatch[1];
|
|
456
|
+
if (/^\s*[^#\n]+:[ \t]*[^\n]+:[ \t]*[^\n]+/m.test(frontmatterBody)) {
|
|
457
|
+
throw new Error('YAML parsing failed');
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
|
|
385
461
|
const parsed = matter(content);
|
|
386
|
-
const blueprintValidation = await
|
|
462
|
+
const blueprintValidation = await validateBlueprintSchema(parsed.data);
|
|
387
463
|
if (!blueprintValidation.valid) {
|
|
388
464
|
validation.errors.push(...blueprintValidation.errors.map(e => `Blueprint: ${e}`));
|
|
389
465
|
}
|
|
@@ -395,7 +471,7 @@ export class PublishManager {
|
|
|
395
471
|
case 'copilot-config':
|
|
396
472
|
try {
|
|
397
473
|
JSON.parse(content);
|
|
398
|
-
} catch
|
|
474
|
+
} catch {
|
|
399
475
|
validation.errors.push('Invalid JSON format for Copilot configuration');
|
|
400
476
|
}
|
|
401
477
|
break;
|
|
@@ -417,10 +493,22 @@ export class PublishManager {
|
|
|
417
493
|
|
|
418
494
|
// Check for hardcoded secrets
|
|
419
495
|
const secretPatterns = [
|
|
420
|
-
{
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
496
|
+
{
|
|
497
|
+
pattern: /api[_-]?key\s*[:=]\s*['"][a-z0-9._-]+['"]/,
|
|
498
|
+
message: 'Potential API key detected',
|
|
499
|
+
},
|
|
500
|
+
{
|
|
501
|
+
pattern: /secret(?:[_-]?[a-z0-9]+)?\s*[:=]\s*['"][a-z0-9._-]+['"]/,
|
|
502
|
+
message: 'Potential secret detected',
|
|
503
|
+
},
|
|
504
|
+
{
|
|
505
|
+
pattern: /password\s*[:=]\s*['"][a-z0-9._-]+['"]/,
|
|
506
|
+
message: 'Potential password detected',
|
|
507
|
+
},
|
|
508
|
+
{
|
|
509
|
+
pattern: /token\s*[:=]\s*['"][a-z0-9._-]+['"]/,
|
|
510
|
+
message: 'Potential token detected',
|
|
511
|
+
},
|
|
424
512
|
];
|
|
425
513
|
|
|
426
514
|
for (const { pattern, message } of secretPatterns) {
|
|
@@ -480,6 +568,10 @@ export class PublishManager {
|
|
|
480
568
|
if (metrics.structure.hasHeadings) score += 1;
|
|
481
569
|
if (metrics.structure.hasLists) score += 1;
|
|
482
570
|
|
|
571
|
+
// Richness scoring (0-2 points)
|
|
572
|
+
if (metrics.structure.hasCodeBlocks) score += 1;
|
|
573
|
+
if (metrics.structure.hasTables) score += 1;
|
|
574
|
+
|
|
483
575
|
// Examples scoring (0-3 points)
|
|
484
576
|
if (metrics.examples > 0) score += 1;
|
|
485
577
|
if (metrics.examples > 2) score += 1;
|
|
@@ -513,7 +605,8 @@ export class PublishManager {
|
|
|
513
605
|
*/
|
|
514
606
|
countExamples(content) {
|
|
515
607
|
const codeBlockMatches = content.match(/```[\s\S]*?```/g) || [];
|
|
516
|
-
const
|
|
608
|
+
const contentWithoutCodeBlocks = content.replace(/```[\s\S]*?```/g, '');
|
|
609
|
+
const inlineCodeMatches = contentWithoutCodeBlocks.match(/`[^`\n]+`/g) || [];
|
|
517
610
|
return codeBlockMatches.length + Math.floor(inlineCodeMatches.length / 3);
|
|
518
611
|
}
|
|
519
612
|
|
|
@@ -543,10 +636,48 @@ export class PublishManager {
|
|
|
543
636
|
async extractProjectContext() {
|
|
544
637
|
try {
|
|
545
638
|
const projectData = await this.projectScanner.scanProject(this.projectPath || process.cwd());
|
|
546
|
-
|
|
547
|
-
|
|
639
|
+
|
|
640
|
+
const contextFromAnalyzer = await this.contextAnalyzer.analyze(projectData);
|
|
641
|
+
const hasPackageJson = (projectData?.files || []).some(file => {
|
|
642
|
+
const fileName = file?.name || file || '';
|
|
643
|
+
return String(fileName).toLowerCase() === 'package.json';
|
|
644
|
+
});
|
|
645
|
+
|
|
646
|
+
const fallbackLanguage = this.detectPrimaryLanguage(projectData);
|
|
647
|
+
const fallbackTechnologies = this.extractTechnologies(projectData);
|
|
648
|
+
const fallbackStructure = this.summarizeStructure(projectData);
|
|
649
|
+
const analyzerStructure =
|
|
650
|
+
contextFromAnalyzer?.structure && typeof contextFromAnalyzer.structure === 'object'
|
|
651
|
+
? contextFromAnalyzer.structure
|
|
652
|
+
: null;
|
|
653
|
+
|
|
654
|
+
return {
|
|
655
|
+
...contextFromAnalyzer,
|
|
656
|
+
name: contextFromAnalyzer?.name || path.basename(this.projectPath || process.cwd()),
|
|
657
|
+
framework: contextFromAnalyzer?.framework || 'generic',
|
|
658
|
+
language:
|
|
659
|
+
contextFromAnalyzer?.language &&
|
|
660
|
+
!(contextFromAnalyzer.language === 'javascript' && fallbackLanguage !== 'javascript')
|
|
661
|
+
? contextFromAnalyzer.language
|
|
662
|
+
: fallbackLanguage,
|
|
663
|
+
technologies:
|
|
664
|
+
Array.isArray(contextFromAnalyzer?.technologies) &&
|
|
665
|
+
contextFromAnalyzer.technologies.length > 0
|
|
666
|
+
? contextFromAnalyzer.technologies
|
|
667
|
+
: fallbackTechnologies,
|
|
668
|
+
structure: analyzerStructure
|
|
669
|
+
? {
|
|
670
|
+
...fallbackStructure,
|
|
671
|
+
...analyzerStructure,
|
|
672
|
+
hasTests: Boolean(fallbackStructure.hasTests || analyzerStructure.hasTests),
|
|
673
|
+
hasConfig: Boolean(fallbackStructure.hasConfig || analyzerStructure.hasConfig),
|
|
674
|
+
}
|
|
675
|
+
: fallbackStructure,
|
|
676
|
+
hasPackageJson,
|
|
677
|
+
};
|
|
678
|
+
} catch {
|
|
548
679
|
return {
|
|
549
|
-
name: path.basename(this.projectPath),
|
|
680
|
+
name: path.basename(this.projectPath || process.cwd()),
|
|
550
681
|
framework: 'generic',
|
|
551
682
|
language: 'javascript',
|
|
552
683
|
technologies: [],
|
|
@@ -556,10 +687,139 @@ export class PublishManager {
|
|
|
556
687
|
packageManager: 'npm',
|
|
557
688
|
platforms: ['claude-code', 'cursor'],
|
|
558
689
|
summary: 'Generic JavaScript project',
|
|
690
|
+
hasPackageJson: false,
|
|
559
691
|
};
|
|
560
692
|
}
|
|
561
693
|
}
|
|
562
694
|
|
|
695
|
+
/**
|
|
696
|
+
* Backward-compatible blueprint validation API used by comprehensive tests.
|
|
697
|
+
*/
|
|
698
|
+
async validateBlueprint(blueprint) {
|
|
699
|
+
const errors = [];
|
|
700
|
+
const warnings = [];
|
|
701
|
+
|
|
702
|
+
if (!blueprint || typeof blueprint !== 'object') {
|
|
703
|
+
return {
|
|
704
|
+
valid: false,
|
|
705
|
+
errors: ['Blueprint payload must be an object'],
|
|
706
|
+
warnings,
|
|
707
|
+
};
|
|
708
|
+
}
|
|
709
|
+
|
|
710
|
+
if (!blueprint.title) errors.push('Missing required field: title');
|
|
711
|
+
if (!blueprint.description) errors.push('Missing required field: description');
|
|
712
|
+
if (!blueprint.content) errors.push('Missing required field: content');
|
|
713
|
+
|
|
714
|
+
if (blueprint.frontmatter && typeof blueprint.frontmatter === 'object') {
|
|
715
|
+
try {
|
|
716
|
+
const schemaResult = await validateBlueprintSchema(blueprint.frontmatter);
|
|
717
|
+
if (!schemaResult.valid) {
|
|
718
|
+
errors.push(...schemaResult.errors.map(err => `Blueprint: ${err}`));
|
|
719
|
+
}
|
|
720
|
+
} catch (error) {
|
|
721
|
+
warnings.push(`Blueprint schema validation unavailable: ${error.message}`);
|
|
722
|
+
}
|
|
723
|
+
}
|
|
724
|
+
|
|
725
|
+
return {
|
|
726
|
+
valid: errors.length === 0,
|
|
727
|
+
errors,
|
|
728
|
+
warnings,
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
|
|
732
|
+
/**
|
|
733
|
+
* Backward-compatible content preparation API for publication workflows.
|
|
734
|
+
*/
|
|
735
|
+
async prepareForPublication(blueprint, options = {}) {
|
|
736
|
+
const format = options.format || 'markdown';
|
|
737
|
+
const targetPlatform = options.targetPlatform || (options.github ? 'github' : 'hub');
|
|
738
|
+
|
|
739
|
+
let content = blueprint?.content;
|
|
740
|
+
if (typeof content !== 'string') {
|
|
741
|
+
content = JSON.stringify(content || blueprint || {}, null, 2);
|
|
742
|
+
}
|
|
743
|
+
|
|
744
|
+
if (format === 'json' && typeof content === 'string') {
|
|
745
|
+
const payload = {
|
|
746
|
+
title: blueprint?.title || 'Untitled Blueprint',
|
|
747
|
+
description: blueprint?.description || '',
|
|
748
|
+
content,
|
|
749
|
+
};
|
|
750
|
+
content = JSON.stringify(payload, null, 2);
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
return {
|
|
754
|
+
content,
|
|
755
|
+
metadata: {
|
|
756
|
+
targetPlatform,
|
|
757
|
+
format,
|
|
758
|
+
preparedAt: new Date().toISOString(),
|
|
759
|
+
title: blueprint?.title || 'Untitled Blueprint',
|
|
760
|
+
},
|
|
761
|
+
};
|
|
762
|
+
}
|
|
763
|
+
|
|
764
|
+
/**
|
|
765
|
+
* Create project scanner with compatibility for mocked function-style exports.
|
|
766
|
+
*/
|
|
767
|
+
createProjectScanner(projectPath) {
|
|
768
|
+
const fallbackScanner = {
|
|
769
|
+
scanProject: async () => ({ files: [], directories: [] }),
|
|
770
|
+
};
|
|
771
|
+
|
|
772
|
+
if (typeof ProjectScanner !== 'function') {
|
|
773
|
+
return fallbackScanner;
|
|
774
|
+
}
|
|
775
|
+
|
|
776
|
+
try {
|
|
777
|
+
return new ProjectScanner({ projectPath });
|
|
778
|
+
} catch {
|
|
779
|
+
try {
|
|
780
|
+
const scanner = ProjectScanner({ projectPath });
|
|
781
|
+
return scanner && typeof scanner.scanProject === 'function' ? scanner : fallbackScanner;
|
|
782
|
+
} catch {
|
|
783
|
+
return fallbackScanner;
|
|
784
|
+
}
|
|
785
|
+
}
|
|
786
|
+
}
|
|
787
|
+
|
|
788
|
+
/**
|
|
789
|
+
* Create context analyzer with compatibility for mocked function-style exports.
|
|
790
|
+
*/
|
|
791
|
+
createContextAnalyzer(projectPath) {
|
|
792
|
+
const fallbackAnalyzer = {
|
|
793
|
+
analyze: async () => ({
|
|
794
|
+
name: path.basename(projectPath || process.cwd()),
|
|
795
|
+
framework: 'generic',
|
|
796
|
+
language: 'javascript',
|
|
797
|
+
technologies: [],
|
|
798
|
+
architecture: 'standard',
|
|
799
|
+
patterns: [],
|
|
800
|
+
structure: { type: 'unknown' },
|
|
801
|
+
packageManager: 'npm',
|
|
802
|
+
platforms: ['claude-code', 'cursor'],
|
|
803
|
+
summary: 'Generic JavaScript project',
|
|
804
|
+
}),
|
|
805
|
+
};
|
|
806
|
+
|
|
807
|
+
if (typeof ProjectContextAnalyzer !== 'function') {
|
|
808
|
+
return fallbackAnalyzer;
|
|
809
|
+
}
|
|
810
|
+
|
|
811
|
+
try {
|
|
812
|
+
return new ProjectContextAnalyzer(projectPath);
|
|
813
|
+
} catch {
|
|
814
|
+
try {
|
|
815
|
+
const analyzer = ProjectContextAnalyzer(projectPath);
|
|
816
|
+
return analyzer && typeof analyzer.analyze === 'function' ? analyzer : fallbackAnalyzer;
|
|
817
|
+
} catch {
|
|
818
|
+
return fallbackAnalyzer;
|
|
819
|
+
}
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
|
|
563
823
|
/**
|
|
564
824
|
* Detect framework from package.json
|
|
565
825
|
*/
|
|
@@ -580,7 +840,14 @@ export class PublishManager {
|
|
|
580
840
|
detectPrimaryLanguage(projectData) {
|
|
581
841
|
if (!projectData.files) return 'javascript';
|
|
582
842
|
|
|
583
|
-
const extensions = projectData.files
|
|
843
|
+
const extensions = projectData.files
|
|
844
|
+
.map(f => path.extname((f.path || f.name || '').toLowerCase()))
|
|
845
|
+
.filter(ext =>
|
|
846
|
+
['.js', '.jsx', '.ts', '.tsx', '.py', '.go', '.rs', '.java', '.cpp', '.c'].includes(ext)
|
|
847
|
+
);
|
|
848
|
+
|
|
849
|
+
if (extensions.length === 0) return 'javascript';
|
|
850
|
+
|
|
584
851
|
const counts = {};
|
|
585
852
|
|
|
586
853
|
extensions.forEach(ext => {
|
|
@@ -598,10 +865,13 @@ export class PublishManager {
|
|
|
598
865
|
'.c': 'c',
|
|
599
866
|
};
|
|
600
867
|
|
|
601
|
-
const
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
868
|
+
const tsCount = (counts['.ts'] || 0) + (counts['.tsx'] || 0);
|
|
869
|
+
|
|
870
|
+
if (tsCount > 0) {
|
|
871
|
+
return 'typescript';
|
|
872
|
+
}
|
|
873
|
+
|
|
874
|
+
const mostCommonExt = Object.keys(counts).reduce((a, b) => (counts[a] > counts[b] ? a : b));
|
|
605
875
|
return langMap[mostCommonExt] || 'javascript';
|
|
606
876
|
}
|
|
607
877
|
|
|
@@ -675,7 +945,7 @@ export class PublishManager {
|
|
|
675
945
|
|
|
676
946
|
// UI Helper methods
|
|
677
947
|
generatePublishPreviewSummary(validation, context) {
|
|
678
|
-
return `Will publish ${validation.detectedFormat} rule (${validation.content.length} chars, Quality: ${validation.qualityScore}/10) for ${context.framework} project`;
|
|
948
|
+
return `Will publish ${validation.detectedFormat} rule (${validation.content.length} chars, Quality: ${validation.qualityScore}/10) for ${context.framework || 'generic'} project`;
|
|
679
949
|
}
|
|
680
950
|
|
|
681
951
|
generatePublishingRecommendations(validation, context) {
|