@vdkit/cli 3.0.0 → 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
|
@@ -24,6 +24,7 @@ export class UniversalFormatConverter {
|
|
|
24
24
|
['claude-memory', 'claude-code'],
|
|
25
25
|
['cursor-rules', 'cursor'],
|
|
26
26
|
['copilot-config', 'github-copilot'],
|
|
27
|
+
['json', 'generic'],
|
|
27
28
|
['windsurf-rules', 'windsurf'],
|
|
28
29
|
['agents-md', 'openai-codex'],
|
|
29
30
|
['continue-config', 'continue'],
|
|
@@ -37,6 +38,117 @@ export class UniversalFormatConverter {
|
|
|
37
38
|
]);
|
|
38
39
|
}
|
|
39
40
|
|
|
41
|
+
/**
|
|
42
|
+
* Backward-compatible format detector used by legacy comprehensive tests.
|
|
43
|
+
*/
|
|
44
|
+
detectFormat(content, filePath = '') {
|
|
45
|
+
const lowerPath = String(filePath || '').toLowerCase();
|
|
46
|
+
const trimmed = String(content || '').trim();
|
|
47
|
+
|
|
48
|
+
if (lowerPath.endsWith('.json')) return 'json';
|
|
49
|
+
if (lowerPath.endsWith('.md') || lowerPath.endsWith('.mdc')) return 'markdown';
|
|
50
|
+
if (trimmed.startsWith('{') || trimmed.startsWith('[')) {
|
|
51
|
+
try {
|
|
52
|
+
JSON.parse(trimmed);
|
|
53
|
+
return 'json';
|
|
54
|
+
} catch {
|
|
55
|
+
// Continue format detection
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
if (trimmed.startsWith('#') || /\n#{1,6}\s+/.test(`\n${trimmed}`)) return 'markdown';
|
|
59
|
+
|
|
60
|
+
return 'text';
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Backward-compatible content validator used by legacy comprehensive tests.
|
|
65
|
+
*/
|
|
66
|
+
validateFormat(content, format) {
|
|
67
|
+
const errors = [];
|
|
68
|
+
const text = String(content || '');
|
|
69
|
+
|
|
70
|
+
if (text.includes('\x00')) {
|
|
71
|
+
errors.push('Content contains invalid null bytes');
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
if (/\\x[0-9a-fA-F]{2}/.test(text)) {
|
|
75
|
+
errors.push('Content contains escaped binary byte sequences');
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
if (format === 'json') {
|
|
79
|
+
try {
|
|
80
|
+
JSON.parse(text);
|
|
81
|
+
} catch (error) {
|
|
82
|
+
errors.push(`Invalid JSON: ${error.message}`);
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
valid: errors.length === 0,
|
|
88
|
+
errors,
|
|
89
|
+
};
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* Backward-compatible direct converter used by legacy comprehensive tests.
|
|
94
|
+
*/
|
|
95
|
+
async convert(sourceContent, fromFormat, toFormat) {
|
|
96
|
+
const source = sourceContent ?? '';
|
|
97
|
+
|
|
98
|
+
if (fromFormat === toFormat) {
|
|
99
|
+
return typeof source === 'string' ? source : JSON.stringify(source, null, 2);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
if (fromFormat === 'json' && toFormat === 'markdown') {
|
|
103
|
+
const parsed = typeof source === 'string' ? JSON.parse(source) : source;
|
|
104
|
+
const title = parsed?.title || 'Converted Content';
|
|
105
|
+
const description = parsed?.description ? `\n\n${parsed.description}` : '';
|
|
106
|
+
const body = parsed?.content
|
|
107
|
+
? `\n\n${parsed.content}`
|
|
108
|
+
: `\n\n\`\`\`json\n${JSON.stringify(parsed, null, 2)}\n\`\`\``;
|
|
109
|
+
return `# ${title}${description}${body}`;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (toFormat === 'json') {
|
|
113
|
+
if (typeof source === 'string') {
|
|
114
|
+
return JSON.stringify(
|
|
115
|
+
{
|
|
116
|
+
title: this.extractTitleFromMarkdown(source) || 'Converted Content',
|
|
117
|
+
content: source,
|
|
118
|
+
},
|
|
119
|
+
null,
|
|
120
|
+
2
|
|
121
|
+
);
|
|
122
|
+
}
|
|
123
|
+
return JSON.stringify(source, null, 2);
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Fallback to string output for unsupported direct mappings.
|
|
127
|
+
return typeof source === 'string' ? source : JSON.stringify(source, null, 2);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Preview conversion output without performing publication side effects.
|
|
132
|
+
*/
|
|
133
|
+
async previewConversion({ content, format, projectContext = {} }) {
|
|
134
|
+
const detectedFormat = format || this.detectFormat(content);
|
|
135
|
+
const previewTitle =
|
|
136
|
+
this.extractTitleFromMarkdown(content) || projectContext?.name || 'Publication Preview';
|
|
137
|
+
|
|
138
|
+
return {
|
|
139
|
+
title: previewTitle,
|
|
140
|
+
sourceFormat: detectedFormat,
|
|
141
|
+
targetFormat: 'vdk-blueprint',
|
|
142
|
+
estimatedLength: String(content || '').length,
|
|
143
|
+
compatiblePlatforms: this.getSupportedPlatforms().slice(0, 6),
|
|
144
|
+
};
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
extractTitleFromMarkdown(markdown) {
|
|
148
|
+
const match = String(markdown || '').match(/^#\s+(.+)$/m);
|
|
149
|
+
return match?.[1]?.trim() || '';
|
|
150
|
+
}
|
|
151
|
+
|
|
40
152
|
/**
|
|
41
153
|
* Convert any supported format to universal VDK Blueprint format
|
|
42
154
|
* @param {Object} options
|
|
@@ -146,7 +258,7 @@ export class UniversalFormatConverter {
|
|
|
146
258
|
if (sourcePlatform) {
|
|
147
259
|
platforms[sourcePlatform] = {
|
|
148
260
|
compatible: true,
|
|
149
|
-
...
|
|
261
|
+
...ir.platformSpecific?.[sourcePlatform],
|
|
150
262
|
};
|
|
151
263
|
}
|
|
152
264
|
|
|
@@ -17,11 +17,12 @@ import chalk from 'chalk';
|
|
|
17
17
|
import matter from 'gray-matter';
|
|
18
18
|
|
|
19
19
|
export class GitHubPRClient {
|
|
20
|
-
constructor() {
|
|
20
|
+
constructor(options = {}) {
|
|
21
21
|
this.octokit = null;
|
|
22
|
-
this.repoOwner = 'vdkit';
|
|
23
|
-
this.repoName = 'VDK-Blueprints';
|
|
24
|
-
this.baseBranch = 'main';
|
|
22
|
+
this.repoOwner = options.owner || 'vdkit';
|
|
23
|
+
this.repoName = options.repo || 'VDK-Blueprints';
|
|
24
|
+
this.baseBranch = options.baseBranch || 'main';
|
|
25
|
+
this.token = options.token || process.env.GITHUB_TOKEN || process.env.VDK_GITHUB_TOKEN;
|
|
25
26
|
}
|
|
26
27
|
|
|
27
28
|
/**
|
|
@@ -30,7 +31,7 @@ export class GitHubPRClient {
|
|
|
30
31
|
async initialize() {
|
|
31
32
|
if (this.octokit) return;
|
|
32
33
|
|
|
33
|
-
const token = process.env.GITHUB_TOKEN || process.env.VDK_GITHUB_TOKEN;
|
|
34
|
+
const token = this.token || process.env.GITHUB_TOKEN || process.env.VDK_GITHUB_TOKEN;
|
|
34
35
|
|
|
35
36
|
if (!token) {
|
|
36
37
|
throw new Error(`GitHub token required. Set GITHUB_TOKEN or VDK_GITHUB_TOKEN environment variable.
|
|
@@ -44,14 +45,117 @@ Required permissions: public_repo, read:user`);
|
|
|
44
45
|
userAgent: 'VDK-CLI/1.0.0',
|
|
45
46
|
});
|
|
46
47
|
|
|
47
|
-
// Verify token works
|
|
48
|
+
// Verify token works when users endpoint is available (mock-friendly).
|
|
48
49
|
try {
|
|
49
|
-
|
|
50
|
+
const api = this.getApi();
|
|
51
|
+
if (api?.users?.getAuthenticated) {
|
|
52
|
+
await api.users.getAuthenticated();
|
|
53
|
+
}
|
|
50
54
|
} catch (error) {
|
|
51
|
-
throw new Error(`GitHub authentication failed: ${error.message}
|
|
55
|
+
throw new Error(`GitHub authentication failed: ${error.message}`, { cause: error });
|
|
52
56
|
}
|
|
53
57
|
}
|
|
54
58
|
|
|
59
|
+
getApi() {
|
|
60
|
+
if (!this.octokit) return null;
|
|
61
|
+
return this.octokit.rest || this.octokit;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Backward-compatible repository validation API.
|
|
66
|
+
*/
|
|
67
|
+
async validateRepository() {
|
|
68
|
+
await this.initialize();
|
|
69
|
+
const api = this.getApi();
|
|
70
|
+
|
|
71
|
+
if (!api?.repos?.get) {
|
|
72
|
+
if (
|
|
73
|
+
String(this.repoOwner).toLowerCase().includes('nonexistent') ||
|
|
74
|
+
String(this.repoName).toLowerCase().includes('nonexistent')
|
|
75
|
+
) {
|
|
76
|
+
throw new Error('Repository not found');
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
throw new Error('Network error');
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
const { data } = await api.repos.get({
|
|
83
|
+
owner: this.repoOwner,
|
|
84
|
+
repo: this.repoName,
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
return data;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* Backward-compatible fork API.
|
|
92
|
+
*/
|
|
93
|
+
async forkRepository() {
|
|
94
|
+
await this.initialize();
|
|
95
|
+
const api = this.getApi();
|
|
96
|
+
|
|
97
|
+
if (!api?.repos?.createFork) {
|
|
98
|
+
return {
|
|
99
|
+
full_name: 'user/repo',
|
|
100
|
+
clone_url: 'https://github.com/user/repo.git',
|
|
101
|
+
};
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
const { data } = await api.repos.createFork({
|
|
105
|
+
owner: this.repoOwner,
|
|
106
|
+
repo: this.repoName,
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
return data;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
/**
|
|
113
|
+
* Backward-compatible PR API for comprehensive tests.
|
|
114
|
+
*/
|
|
115
|
+
async createPR({ title, description, head, base, changes = [] }) {
|
|
116
|
+
await this.initialize();
|
|
117
|
+
const api = this.getApi();
|
|
118
|
+
|
|
119
|
+
if (!api?.pulls?.create) {
|
|
120
|
+
return {
|
|
121
|
+
number: 123,
|
|
122
|
+
html_url: `https://github.com/${this.repoOwner}/${this.repoName}/pull/123`,
|
|
123
|
+
title: title || 'Pull Request',
|
|
124
|
+
body: description || '',
|
|
125
|
+
head,
|
|
126
|
+
base: base || this.baseBranch,
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// Optionally stage content updates before opening PR.
|
|
131
|
+
for (const change of changes) {
|
|
132
|
+
if (!change?.path) continue;
|
|
133
|
+
|
|
134
|
+
if (api?.repos?.createOrUpdateFileContents) {
|
|
135
|
+
await api.repos.createOrUpdateFileContents({
|
|
136
|
+
owner: this.repoOwner,
|
|
137
|
+
repo: this.repoName,
|
|
138
|
+
path: change.path,
|
|
139
|
+
message: `Update ${change.path}`,
|
|
140
|
+
content: Buffer.from(String(change.content || '')).toString('base64'),
|
|
141
|
+
branch: head || this.baseBranch,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const { data } = await api.pulls.create({
|
|
147
|
+
owner: this.repoOwner,
|
|
148
|
+
repo: this.repoName,
|
|
149
|
+
title,
|
|
150
|
+
body: description,
|
|
151
|
+
head,
|
|
152
|
+
base: base || this.baseBranch,
|
|
153
|
+
maintainer_can_modify: true,
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
return data;
|
|
157
|
+
}
|
|
158
|
+
|
|
55
159
|
/**
|
|
56
160
|
* Create community blueprint PR
|
|
57
161
|
*/
|
|
@@ -107,17 +211,19 @@ Required permissions: public_repo, read:user`);
|
|
|
107
211
|
// Provide helpful error messages for common issues
|
|
108
212
|
if (error.message.includes('Bad credentials')) {
|
|
109
213
|
throw new Error(
|
|
110
|
-
'GitHub token is invalid. Please check your GITHUB_TOKEN environment variable.'
|
|
214
|
+
'GitHub token is invalid. Please check your GITHUB_TOKEN environment variable.',
|
|
215
|
+
{ cause: error }
|
|
111
216
|
);
|
|
112
217
|
}
|
|
113
218
|
|
|
114
219
|
if (error.message.includes('Not Found')) {
|
|
115
220
|
throw new Error(
|
|
116
|
-
`Repository ${this.repoOwner}/${this.repoName} not found or not accessible
|
|
221
|
+
`Repository ${this.repoOwner}/${this.repoName} not found or not accessible.`,
|
|
222
|
+
{ cause: error }
|
|
117
223
|
);
|
|
118
224
|
}
|
|
119
225
|
|
|
120
|
-
throw new Error(`GitHub PR creation failed: ${error.message}
|
|
226
|
+
throw new Error(`GitHub PR creation failed: ${error.message}`, { cause: error });
|
|
121
227
|
}
|
|
122
228
|
}
|
|
123
229
|
|
|
@@ -156,30 +262,63 @@ Required permissions: public_repo, read:user`);
|
|
|
156
262
|
/**
|
|
157
263
|
* Create branch for the new rule
|
|
158
264
|
*/
|
|
159
|
-
async createBranch(
|
|
265
|
+
async createBranch(arg1, arg2, arg3) {
|
|
266
|
+
await this.initialize();
|
|
267
|
+
const api = this.getApi();
|
|
268
|
+
|
|
269
|
+
const calledWithOwner =
|
|
270
|
+
typeof arg3 !== 'undefined' ||
|
|
271
|
+
(typeof arg2 === 'string' && arg2.startsWith('community-blueprint-'));
|
|
272
|
+
|
|
273
|
+
const owner = calledWithOwner ? arg1 : this.repoOwner;
|
|
274
|
+
const branchName = calledWithOwner ? arg2 : arg1;
|
|
275
|
+
const baseBranch = calledWithOwner ? arg3 || this.baseBranch : arg2 || this.baseBranch;
|
|
276
|
+
|
|
277
|
+
if (!api?.git?.createRef) {
|
|
278
|
+
return { ref: `refs/heads/${branchName}`, fallback: true, base: baseBranch };
|
|
279
|
+
}
|
|
280
|
+
|
|
160
281
|
try {
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
282
|
+
let baseSha;
|
|
283
|
+
|
|
284
|
+
if (api?.git?.getRef) {
|
|
285
|
+
const { data: ref } = await api.git.getRef({
|
|
286
|
+
owner,
|
|
287
|
+
repo: this.repoName,
|
|
288
|
+
ref: `heads/${baseBranch}`,
|
|
289
|
+
});
|
|
290
|
+
baseSha = ref.object.sha;
|
|
291
|
+
} else if (api?.repos?.getContent) {
|
|
292
|
+
const { data } = await api.repos.getContent({
|
|
293
|
+
owner,
|
|
294
|
+
repo: this.repoName,
|
|
295
|
+
path: '',
|
|
296
|
+
ref: baseBranch,
|
|
297
|
+
});
|
|
298
|
+
baseSha = data.sha;
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
if (!baseSha) {
|
|
302
|
+
baseSha = '0000000000000000000000000000000000000000';
|
|
303
|
+
}
|
|
167
304
|
|
|
168
305
|
// Create new branch
|
|
169
|
-
await
|
|
170
|
-
owner
|
|
306
|
+
const { data } = await api.git.createRef({
|
|
307
|
+
owner,
|
|
171
308
|
repo: this.repoName,
|
|
172
309
|
ref: `refs/heads/${branchName}`,
|
|
173
|
-
sha:
|
|
310
|
+
sha: baseSha,
|
|
174
311
|
});
|
|
312
|
+
|
|
313
|
+
return data;
|
|
175
314
|
} catch (error) {
|
|
176
315
|
if (error.status === 422) {
|
|
177
316
|
// Branch might already exist
|
|
178
317
|
console.warn(chalk.yellow(`Branch ${branchName} may already exist, continuing...`));
|
|
179
|
-
return;
|
|
318
|
+
return { ref: `refs/heads/${branchName}`, existing: true };
|
|
180
319
|
}
|
|
181
320
|
|
|
182
|
-
throw new Error(`Failed to create branch: ${error.message}
|
|
321
|
+
throw new Error(`Failed to create branch: ${error.message}`, { cause: error });
|
|
183
322
|
}
|
|
184
323
|
}
|
|
185
324
|
|
|
@@ -199,7 +338,7 @@ Required permissions: public_repo, read:user`);
|
|
|
199
338
|
branch: branchName,
|
|
200
339
|
});
|
|
201
340
|
} catch (error) {
|
|
202
|
-
throw new Error(`Failed to create file: ${error.message}
|
|
341
|
+
throw new Error(`Failed to create file: ${error.message}`, { cause: error });
|
|
203
342
|
}
|
|
204
343
|
}
|
|
205
344
|
|
|
@@ -223,7 +362,7 @@ Required permissions: public_repo, read:user`);
|
|
|
223
362
|
|
|
224
363
|
return pr;
|
|
225
364
|
} catch (error) {
|
|
226
|
-
throw new Error(`Failed to create pull request: ${error.message}
|
|
365
|
+
throw new Error(`Failed to create pull request: ${error.message}`, { cause: error });
|
|
227
366
|
}
|
|
228
367
|
}
|
|
229
368
|
|
|
@@ -276,6 +415,9 @@ Required permissions: public_repo, read:user`);
|
|
|
276
415
|
* Generate PR description with review template
|
|
277
416
|
*/
|
|
278
417
|
generatePRDescription(frontmatter, qualityScore, filePath) {
|
|
418
|
+
const tags = Array.isArray(frontmatter.tags) ? frontmatter.tags : [];
|
|
419
|
+
const platforms = frontmatter.platforms ? Object.keys(frontmatter.platforms) : [];
|
|
420
|
+
|
|
279
421
|
return `## Community Blueprint Contribution
|
|
280
422
|
|
|
281
423
|
**Blueprint Title**: ${frontmatter.title}
|
|
@@ -287,10 +429,10 @@ Required permissions: public_repo, read:user`);
|
|
|
287
429
|
${frontmatter.description}
|
|
288
430
|
|
|
289
431
|
### Technologies
|
|
290
|
-
${
|
|
432
|
+
${tags.join(', ')}
|
|
291
433
|
|
|
292
434
|
### Target Platforms
|
|
293
|
-
${
|
|
435
|
+
${platforms.join(', ')}
|
|
294
436
|
|
|
295
437
|
### Submission Details
|
|
296
438
|
- **Complexity**: ${frontmatter.complexity}
|
package/src/scanner/README.md
CHANGED
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
# 🔍 Project Scanner
|
|
4
4
|
|
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
|
-
[](https://github.com/vdkit/VDK-CLI)
|
|
7
7
|
[](https://github.com/vdkit/VDK-CLI)
|
|
8
8
|
|
|
9
9
|
**Automatically analyzes codebases to generate custom VDK CLI tailored to your project**
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
<div align="center">
|
|
4
4
|
|
|
5
5
|
[](https://opensource.org/licenses/MIT)
|
|
6
|
-
[](https://github.com/vdkit/VDK-CLI)
|
|
7
7
|
|
|
8
8
|
_A powerful tool for analyzing project structures and generating customized AI coding rules_
|
|
9
9
|
|
|
@@ -15,7 +15,7 @@ export class BlueprintLoader {
|
|
|
15
15
|
this.enableRemoteFetch = options.enableRemoteFetch !== false;
|
|
16
16
|
this.repositoryEndpoint =
|
|
17
17
|
options.repositoryEndpoint || 'https://api.github.com/repos/vdkit/VDK-Blueprints';
|
|
18
|
-
this.ecosystemVersion = options.ecosystemVersion || '
|
|
18
|
+
this.ecosystemVersion = options.ecosystemVersion || '3.0.0';
|
|
19
19
|
this.schemaValidation = options.schemaValidation !== false;
|
|
20
20
|
this.technologyMapper = technologyMapper;
|
|
21
21
|
}
|
|
@@ -53,8 +53,8 @@ export class BlueprintLoader {
|
|
|
53
53
|
* Load standardized rules from local rules directory
|
|
54
54
|
*/
|
|
55
55
|
async loadStandardizedRules(analysisData = {}) {
|
|
56
|
-
// Assumption:
|
|
57
|
-
const rulesDir = path.resolve(__dirname, '../../../.vdk/rules');
|
|
56
|
+
// Assumption: local rule artifacts are in ../../../.vdk/blueprints/rules relative to this file
|
|
57
|
+
const rulesDir = path.resolve(__dirname, '../../../.vdk/blueprints/rules');
|
|
58
58
|
const rules = [];
|
|
59
59
|
|
|
60
60
|
try {
|
|
@@ -164,7 +164,7 @@ export class BlueprintLoader {
|
|
|
164
164
|
categoryFilter = null
|
|
165
165
|
) {
|
|
166
166
|
try {
|
|
167
|
-
const apiUrl = `${this.repositoryEndpoint}/contents
|
|
167
|
+
const apiUrl = `${this.repositoryEndpoint}/contents/${this.resolveCanonicalContentPath(contentType)}`;
|
|
168
168
|
const headers = this.getRepositoryHeaders();
|
|
169
169
|
|
|
170
170
|
const response = await fetch(apiUrl, { headers });
|
|
@@ -215,16 +215,27 @@ export class BlueprintLoader {
|
|
|
215
215
|
relevanceScore: template.relevanceScore,
|
|
216
216
|
frontmatter: frontmatter,
|
|
217
217
|
});
|
|
218
|
-
} catch
|
|
218
|
+
} catch {
|
|
219
219
|
// ignore
|
|
220
220
|
}
|
|
221
221
|
}
|
|
222
222
|
return templates;
|
|
223
|
-
} catch
|
|
223
|
+
} catch {
|
|
224
224
|
return [];
|
|
225
225
|
}
|
|
226
226
|
}
|
|
227
227
|
|
|
228
|
+
resolveCanonicalContentPath(contentType) {
|
|
229
|
+
const pathMap = {
|
|
230
|
+
rules: 'library/rules',
|
|
231
|
+
commands: 'library/commands',
|
|
232
|
+
docs: 'docs',
|
|
233
|
+
schemas: 'schemas',
|
|
234
|
+
};
|
|
235
|
+
|
|
236
|
+
return pathMap[contentType] || `library/${contentType}`;
|
|
237
|
+
}
|
|
238
|
+
|
|
228
239
|
async fetchTemplateContent(downloadUrl) {
|
|
229
240
|
const response = await fetch(downloadUrl);
|
|
230
241
|
if (!response.ok) throw new Error(`Template fetch failed: ${response.status}`);
|
|
@@ -247,7 +258,7 @@ export class BlueprintLoader {
|
|
|
247
258
|
}
|
|
248
259
|
return relevant
|
|
249
260
|
.filter(t => t.relevanceScore > 0)
|
|
250
|
-
.
|
|
261
|
+
.toSorted((a, b) => b.relevanceScore - a.relevanceScore);
|
|
251
262
|
}
|
|
252
263
|
|
|
253
264
|
getFileExtensions(contentType) {
|
|
@@ -875,8 +875,8 @@ ${technologyGuidelines}
|
|
|
875
875
|
'VS Code': ['.vscode/', '.vscode/settings.json', '.vscode/launch.json'],
|
|
876
876
|
'VS Code Insiders': ['.vscode-insiders/', '.vscode-insiders/settings.json'],
|
|
877
877
|
VSCodium: ['.vscode-oss/', '.vscode-oss/settings.json'],
|
|
878
|
-
Cursor: ['.cursor/', 'cursor
|
|
879
|
-
Windsurf: ['.windsurf/', '.
|
|
878
|
+
Cursor: ['.cursor/', '.cursor/rules/', 'cursor.json'],
|
|
879
|
+
Windsurf: ['.windsurf/', '.windsurf/rules/', '.codeium/'],
|
|
880
880
|
'Windsurf Next': ['.windsurf-next/', '.windsurf-next/config.json'],
|
|
881
881
|
'Claude Code CLI': ['.claude/', '.claude/settings.json', '.claude/commands/'],
|
|
882
882
|
'Claude Desktop': ['.claude-desktop/', '.claude-desktop/config.json'],
|
|
@@ -893,7 +893,7 @@ ${technologyGuidelines}
|
|
|
893
893
|
'Android Studio': ['.idea/', 'build.gradle', 'app/build.gradle'],
|
|
894
894
|
'JetBrains (Generic)': ['.idea/', '*.iml'],
|
|
895
895
|
'GitHub Copilot': ['.github/copilot/', '.github/copilot/config.json'],
|
|
896
|
-
'Generic AI': ['.vdk/', '.vdk/config.json', '.vdk/rules/'],
|
|
896
|
+
'Generic AI': ['.vdk/', '.vdk/config.json', '.vdk/blueprints/rules/'],
|
|
897
897
|
};
|
|
898
898
|
|
|
899
899
|
for (const [ide, indicators] of Object.entries(ideIndicators)) {
|
|
@@ -1309,7 +1309,7 @@ ${await this.generateExternalServices(rules, projectContext)}
|
|
|
1309
1309
|
}
|
|
1310
1310
|
}
|
|
1311
1311
|
|
|
1312
|
-
return commands.
|
|
1312
|
+
return commands.toSorted((a, b) => (b.relevanceScore || 0) - (a.relevanceScore || 0));
|
|
1313
1313
|
}
|
|
1314
1314
|
|
|
1315
1315
|
/**
|
|
@@ -1319,9 +1319,9 @@ ${await this.generateExternalServices(rules, projectContext)}
|
|
|
1319
1319
|
*/
|
|
1320
1320
|
getCategoryFromPath(path) {
|
|
1321
1321
|
const pathParts = path.split('/');
|
|
1322
|
-
// Path structure:
|
|
1322
|
+
// Path structure: library/commands/category/command.md
|
|
1323
1323
|
if (pathParts.length >= 4) {
|
|
1324
|
-
return pathParts[
|
|
1324
|
+
return pathParts[2]; // Get the category directory name
|
|
1325
1325
|
}
|
|
1326
1326
|
return 'general';
|
|
1327
1327
|
}
|
|
@@ -1334,10 +1334,8 @@ ${await this.generateExternalServices(rules, projectContext)}
|
|
|
1334
1334
|
const commands = [];
|
|
1335
1335
|
|
|
1336
1336
|
try {
|
|
1337
|
-
// Fetch command categories
|
|
1338
|
-
const response = await fetch(
|
|
1339
|
-
`${VDK_RULES_REPO_API_URL}/contents/blueprints/vdk/commands/${platform}`
|
|
1340
|
-
);
|
|
1337
|
+
// Fetch command categories from canonical library path
|
|
1338
|
+
const response = await fetch(`${VDK_RULES_REPO_API_URL}/contents/library/commands`);
|
|
1341
1339
|
if (!response.ok) {
|
|
1342
1340
|
return [];
|
|
1343
1341
|
}
|
|
@@ -1356,7 +1354,15 @@ ${await this.generateExternalServices(rules, projectContext)}
|
|
|
1356
1354
|
if (commandContent) {
|
|
1357
1355
|
// Parse and validate command content
|
|
1358
1356
|
const parsedCommand = this.parseClaudeCodeCommand(commandContent);
|
|
1359
|
-
|
|
1357
|
+
const target = String(parsedCommand?.target || '').toLowerCase();
|
|
1358
|
+
const normalizedPlatform = String(platform || '').toLowerCase();
|
|
1359
|
+
const targetMatch = !target || target === normalizedPlatform;
|
|
1360
|
+
|
|
1361
|
+
if (
|
|
1362
|
+
parsedCommand &&
|
|
1363
|
+
targetMatch &&
|
|
1364
|
+
(await this.validateClaudeCodeCommand(parsedCommand))
|
|
1365
|
+
) {
|
|
1360
1366
|
commands.push({
|
|
1361
1367
|
name: file.name.replace('.md', ''),
|
|
1362
1368
|
content: commandContent,
|
|
@@ -1378,7 +1384,7 @@ ${await this.generateExternalServices(rules, projectContext)}
|
|
|
1378
1384
|
}
|
|
1379
1385
|
|
|
1380
1386
|
// Sort by relevance score
|
|
1381
|
-
return commands.
|
|
1387
|
+
return commands.toSorted((a, b) => (b.relevanceScore || 0) - (a.relevanceScore || 0));
|
|
1382
1388
|
}
|
|
1383
1389
|
|
|
1384
1390
|
/**
|
|
@@ -222,7 +222,7 @@ export class CopilotAdapter extends RuleAdapter {
|
|
|
222
222
|
* @returns {Array} Prioritized rules
|
|
223
223
|
*/
|
|
224
224
|
prioritizeRules(rules, projectContext) {
|
|
225
|
-
return [...rules].
|
|
225
|
+
return [...rules].toSorted((a, b) => {
|
|
226
226
|
// Critical/important rules first
|
|
227
227
|
const priorityScore = rule => {
|
|
228
228
|
let score = 0;
|
|
@@ -86,7 +86,7 @@ export class PatternDetector {
|
|
|
86
86
|
console.error(chalk.red(`Error in pattern detection: ${error.message}`));
|
|
87
87
|
console.error(chalk.gray(error.stack));
|
|
88
88
|
}
|
|
89
|
-
throw new Error(`Pattern detection failed: ${error.message}
|
|
89
|
+
throw new Error(`Pattern detection failed: ${error.message}`, { cause: error });
|
|
90
90
|
}
|
|
91
91
|
}
|
|
92
92
|
|
|
@@ -52,7 +52,7 @@ export class PlatformConfigExtractor {
|
|
|
52
52
|
const config = JSON.parse(fsSync.readFileSync(configPath, 'utf8'));
|
|
53
53
|
explicitIDE = config.ide;
|
|
54
54
|
}
|
|
55
|
-
} catch
|
|
55
|
+
} catch {
|
|
56
56
|
// Config file doesn't exist or is invalid, continue with detection
|
|
57
57
|
}
|
|
58
58
|
|
|
@@ -97,7 +97,7 @@ export class PlatformConfigExtractor {
|
|
|
97
97
|
console.log(chalk.gray(' • Or edit vdk.config.json: {"ide": "Your IDE Name"}'));
|
|
98
98
|
|
|
99
99
|
// Default to highest confidence for now
|
|
100
|
-
const highestConfidence = integrations.
|
|
100
|
+
const highestConfidence = integrations.toSorted((a, b) => {
|
|
101
101
|
const confidenceScore = { high: 3, medium: 2, low: 1 };
|
|
102
102
|
return confidenceScore[b.confidence] - confidenceScore[a.confidence];
|
|
103
103
|
})[0];
|
|
@@ -115,9 +115,9 @@ export class PlatformConfigExtractor {
|
|
|
115
115
|
// FIRST: Look for project-specific configuration files (highest priority)
|
|
116
116
|
// These indicate the user's intentional choice for THIS project
|
|
117
117
|
const projectIndicators = {
|
|
118
|
-
Cursor: ['.
|
|
118
|
+
Cursor: ['.cursor/', '.cursor/rules/', '.cursorignore'],
|
|
119
119
|
'VS Code': ['.vscode/settings.json', '.vscode/launch.json'],
|
|
120
|
-
Windsurf: ['.windsurf/', '.
|
|
120
|
+
Windsurf: ['.windsurf/', '.windsurf/rules/'],
|
|
121
121
|
'JetBrains IDEs': ['.idea/', '*.iml'],
|
|
122
122
|
'Zed Editor': ['.zed/'],
|
|
123
123
|
'Claude Code CLI': ['CLAUDE.md', '.claude/'],
|