@ckelsoe/prompt-architect 3.0.0 → 3.1.1
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/.claude-plugin/marketplace.json +1 -1
- package/.claude-plugin/plugin.json +1 -1
- package/CHANGELOG.md +264 -264
- package/LICENSE +21 -21
- package/README.md +1064 -1184
- package/adapters/README.md +75 -267
- package/adapters/for-windsurf.md +136 -136
- package/adapters/system-prompt.md +243 -243
- package/package.json +139 -135
- package/scripts/build-skill.js +64 -0
- package/scripts/install.js +35 -56
- package/scripts/test.js +205 -213
- package/scripts/validate-skill.js +350 -350
- package/skills/prompt-architect/SKILL.md +331 -331
- package/skills/prompt-architect/assets/templates/ape_template.txt +5 -5
- package/skills/prompt-architect/assets/templates/bab_template.txt +15 -15
- package/skills/prompt-architect/assets/templates/broke_template.txt +15 -15
- package/skills/prompt-architect/assets/templates/cai-critique-revise_template.txt +18 -18
- package/skills/prompt-architect/assets/templates/care_template.txt +16 -16
- package/skills/prompt-architect/assets/templates/chain-of-density_template.txt +46 -46
- package/skills/prompt-architect/assets/templates/chain-of-thought_template.txt +49 -49
- package/skills/prompt-architect/assets/templates/co-star_template.txt +17 -17
- package/skills/prompt-architect/assets/templates/crispe_template.txt +14 -14
- package/skills/prompt-architect/assets/templates/ctf_template.txt +15 -15
- package/skills/prompt-architect/assets/templates/devils-advocate_template.txt +25 -25
- package/skills/prompt-architect/assets/templates/hybrid_template.txt +101 -101
- package/skills/prompt-architect/assets/templates/least-to-most_template.txt +16 -16
- package/skills/prompt-architect/assets/templates/plan-and-solve_template.txt +7 -7
- package/skills/prompt-architect/assets/templates/pre-mortem_template.txt +27 -27
- package/skills/prompt-architect/assets/templates/race_template.txt +19 -19
- package/skills/prompt-architect/assets/templates/rcot_template.txt +31 -31
- package/skills/prompt-architect/assets/templates/react_template.txt +27 -27
- package/skills/prompt-architect/assets/templates/reverse-role_template.txt +11 -11
- package/skills/prompt-architect/assets/templates/rise-ie_template.txt +23 -23
- package/skills/prompt-architect/assets/templates/rise-ix_template.txt +28 -28
- package/skills/prompt-architect/assets/templates/risen_template.txt +21 -21
- package/skills/prompt-architect/assets/templates/rpef_template.txt +26 -26
- package/skills/prompt-architect/assets/templates/rtf_template.txt +13 -13
- package/skills/prompt-architect/assets/templates/self-refine_template.txt +16 -16
- package/skills/prompt-architect/assets/templates/skeleton-of-thought_template.txt +15 -15
- package/skills/prompt-architect/assets/templates/step-back_template.txt +10 -10
- package/skills/prompt-architect/assets/templates/tidd-ec_template.txt +45 -45
- package/skills/prompt-architect/assets/templates/tree-of-thought_template.txt +30 -30
- package/skills/prompt-architect/references/frameworks/ape.md +200 -200
- package/skills/prompt-architect/references/frameworks/bab.md +242 -242
- package/skills/prompt-architect/references/frameworks/broke.md +242 -242
- package/skills/prompt-architect/references/frameworks/cai-critique-revise.md +230 -230
- package/skills/prompt-architect/references/frameworks/care.md +235 -235
- package/skills/prompt-architect/references/frameworks/chain-of-density.md +479 -479
- package/skills/prompt-architect/references/frameworks/chain-of-thought.md +424 -424
- package/skills/prompt-architect/references/frameworks/co-star.md +256 -256
- package/skills/prompt-architect/references/frameworks/crispe.md +253 -253
- package/skills/prompt-architect/references/frameworks/ctf.md +207 -207
- package/skills/prompt-architect/references/frameworks/devils-advocate.md +206 -206
- package/skills/prompt-architect/references/frameworks/least-to-most.md +226 -226
- package/skills/prompt-architect/references/frameworks/plan-and-solve.md +210 -210
- package/skills/prompt-architect/references/frameworks/pre-mortem.md +197 -197
- package/skills/prompt-architect/references/frameworks/race.md +243 -243
- package/skills/prompt-architect/references/frameworks/rcot.md +203 -203
- package/skills/prompt-architect/references/frameworks/react.md +304 -304
- package/skills/prompt-architect/references/frameworks/reverse-role.md +189 -189
- package/skills/prompt-architect/references/frameworks/rise.md +555 -555
- package/skills/prompt-architect/references/frameworks/risen.md +297 -297
- package/skills/prompt-architect/references/frameworks/rpef.md +195 -195
- package/skills/prompt-architect/references/frameworks/rtf.md +358 -358
- package/skills/prompt-architect/references/frameworks/self-refine.md +205 -205
- package/skills/prompt-architect/references/frameworks/skeleton-of-thought.md +186 -186
- package/skills/prompt-architect/references/frameworks/step-back.md +206 -206
- package/skills/prompt-architect/references/frameworks/tidd-ec.md +470 -470
- package/skills/prompt-architect/references/frameworks/tree-of-thought.md +260 -260
- package/skills/prompt-architect/scripts/framework_analyzer.py +807 -807
- package/skills/prompt-architect/scripts/prompt_evaluator.py +336 -336
- package/adapters/for-cursor.mdc +0 -138
- package/adapters/for-gemini-cli.md +0 -70
- package/adapters/for-github-copilot.md +0 -141
- package/adapters/for-openai-codex-cli.md +0 -158
|
@@ -1,350 +1,350 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Skill Structure Validator
|
|
5
|
-
*
|
|
6
|
-
* Validates the Claude Code skill structure before publishing to npm.
|
|
7
|
-
* Checks:
|
|
8
|
-
* - SKILL.md exists and has valid frontmatter
|
|
9
|
-
* - Required directories are present
|
|
10
|
-
* - Framework files exist
|
|
11
|
-
* - Templates exist
|
|
12
|
-
* - File sizes are reasonable
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
const fs = require('fs');
|
|
16
|
-
const path = require('path');
|
|
17
|
-
|
|
18
|
-
// Colors for console output
|
|
19
|
-
const colors = {
|
|
20
|
-
reset: '\x1b[0m',
|
|
21
|
-
red: '\x1b[31m',
|
|
22
|
-
green: '\x1b[32m',
|
|
23
|
-
yellow: '\x1b[33m',
|
|
24
|
-
blue: '\x1b[34m',
|
|
25
|
-
};
|
|
26
|
-
|
|
27
|
-
function log(message, color = 'reset') {
|
|
28
|
-
console.log(`${colors[color]}${message}${colors.reset}`);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function error(message) {
|
|
32
|
-
log(`❌ ${message}`, 'red');
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function success(message) {
|
|
36
|
-
log(`✅ ${message}`, 'green');
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
function warning(message) {
|
|
40
|
-
log(`⚠️ ${message}`, 'yellow');
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function info(message) {
|
|
44
|
-
log(`ℹ️ ${message}`, 'blue');
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
// Configuration
|
|
48
|
-
const SKILL_DIR = path.join(__dirname, '..', 'skills', 'prompt-architect');
|
|
49
|
-
const SKILL_FILE = path.join(SKILL_DIR, 'SKILL.md');
|
|
50
|
-
|
|
51
|
-
const REQUIRED_DIRS = [
|
|
52
|
-
'scripts',
|
|
53
|
-
'references/frameworks',
|
|
54
|
-
'assets/templates',
|
|
55
|
-
];
|
|
56
|
-
|
|
57
|
-
const REQUIRED_FRAMEWORKS = [
|
|
58
|
-
'co-star.md',
|
|
59
|
-
'risen.md',
|
|
60
|
-
'rise.md',
|
|
61
|
-
'tidd-ec.md',
|
|
62
|
-
'rtf.md',
|
|
63
|
-
'chain-of-thought.md',
|
|
64
|
-
'chain-of-density.md',
|
|
65
|
-
];
|
|
66
|
-
|
|
67
|
-
const REQUIRED_TEMPLATES = [
|
|
68
|
-
'co-star_template.txt',
|
|
69
|
-
'risen_template.txt',
|
|
70
|
-
'rise-ie_template.txt',
|
|
71
|
-
'rise-ix_template.txt',
|
|
72
|
-
'tidd-ec_template.txt',
|
|
73
|
-
'rtf_template.txt',
|
|
74
|
-
'hybrid_template.txt',
|
|
75
|
-
];
|
|
76
|
-
|
|
77
|
-
const REQUIRED_SCRIPTS = [
|
|
78
|
-
'framework_analyzer.py',
|
|
79
|
-
'prompt_evaluator.py',
|
|
80
|
-
];
|
|
81
|
-
|
|
82
|
-
let errorCount = 0;
|
|
83
|
-
let warningCount = 0;
|
|
84
|
-
|
|
85
|
-
// Main validation
|
|
86
|
-
function validateSkill() {
|
|
87
|
-
log('\n🔍 Validating Prompt Architect Skill Structure...\n', 'blue');
|
|
88
|
-
|
|
89
|
-
// Check skill directory exists
|
|
90
|
-
if (!fs.existsSync(SKILL_DIR)) {
|
|
91
|
-
error(`Skill directory not found: ${SKILL_DIR}`);
|
|
92
|
-
process.exit(1);
|
|
93
|
-
}
|
|
94
|
-
success('Skill directory found');
|
|
95
|
-
|
|
96
|
-
// Check SKILL.md exists
|
|
97
|
-
if (!fs.existsSync(SKILL_FILE)) {
|
|
98
|
-
error('SKILL.md not found');
|
|
99
|
-
errorCount++;
|
|
100
|
-
} else {
|
|
101
|
-
success('SKILL.md found');
|
|
102
|
-
validateSkillFile();
|
|
103
|
-
}
|
|
104
|
-
|
|
105
|
-
// Check required directories
|
|
106
|
-
REQUIRED_DIRS.forEach(dir => {
|
|
107
|
-
const fullPath = path.join(SKILL_DIR, dir);
|
|
108
|
-
if (!fs.existsSync(fullPath)) {
|
|
109
|
-
error(`Required directory missing: ${dir}`);
|
|
110
|
-
errorCount++;
|
|
111
|
-
} else {
|
|
112
|
-
success(`Directory found: ${dir}`);
|
|
113
|
-
}
|
|
114
|
-
});
|
|
115
|
-
|
|
116
|
-
// Check framework files
|
|
117
|
-
info('\nValidating framework files...');
|
|
118
|
-
REQUIRED_FRAMEWORKS.forEach(framework => {
|
|
119
|
-
const frameworkPath = path.join(SKILL_DIR, 'references', 'frameworks', framework);
|
|
120
|
-
if (!fs.existsSync(frameworkPath)) {
|
|
121
|
-
error(`Framework file missing: ${framework}`);
|
|
122
|
-
errorCount++;
|
|
123
|
-
} else {
|
|
124
|
-
const stats = fs.statSync(frameworkPath);
|
|
125
|
-
const sizeKB = (stats.size / 1024).toFixed(2);
|
|
126
|
-
success(`Framework found: ${framework} (${sizeKB} KB)`);
|
|
127
|
-
|
|
128
|
-
// Warn if framework file is too small
|
|
129
|
-
if (stats.size < 5000) {
|
|
130
|
-
warning(`Framework ${framework} seems small (< 5 KB)`);
|
|
131
|
-
warningCount++;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
});
|
|
135
|
-
|
|
136
|
-
// Check template files
|
|
137
|
-
info('\nValidating template files...');
|
|
138
|
-
REQUIRED_TEMPLATES.forEach(template => {
|
|
139
|
-
const templatePath = path.join(SKILL_DIR, 'assets', 'templates', template);
|
|
140
|
-
if (!fs.existsSync(templatePath)) {
|
|
141
|
-
error(`Template file missing: ${template}`);
|
|
142
|
-
errorCount++;
|
|
143
|
-
} else {
|
|
144
|
-
success(`Template found: ${template}`);
|
|
145
|
-
}
|
|
146
|
-
});
|
|
147
|
-
|
|
148
|
-
// Check script files
|
|
149
|
-
info('\nValidating script files...');
|
|
150
|
-
REQUIRED_SCRIPTS.forEach(script => {
|
|
151
|
-
const scriptPath = path.join(SKILL_DIR, 'scripts', script);
|
|
152
|
-
if (!fs.existsSync(scriptPath)) {
|
|
153
|
-
error(`Script file missing: ${script}`);
|
|
154
|
-
errorCount++;
|
|
155
|
-
} else {
|
|
156
|
-
success(`Script found: ${script}`);
|
|
157
|
-
}
|
|
158
|
-
});
|
|
159
|
-
|
|
160
|
-
// Check package structure
|
|
161
|
-
info('\nValidating package structure...');
|
|
162
|
-
const packageJson = path.join(__dirname, '..', 'package.json');
|
|
163
|
-
if (!fs.existsSync(packageJson)) {
|
|
164
|
-
error('package.json not found');
|
|
165
|
-
errorCount++;
|
|
166
|
-
} else {
|
|
167
|
-
success('package.json found');
|
|
168
|
-
validatePackageJson();
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
function validateSkillFile() {
|
|
174
|
-
const content = fs.readFileSync(SKILL_FILE, 'utf8');
|
|
175
|
-
|
|
176
|
-
// Check for YAML frontmatter (handle both LF and CRLF line endings)
|
|
177
|
-
const frontmatterMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
178
|
-
if (!frontmatterMatch) {
|
|
179
|
-
error('SKILL.md missing YAML frontmatter (---\\n...\\n---)');
|
|
180
|
-
errorCount++;
|
|
181
|
-
return;
|
|
182
|
-
}
|
|
183
|
-
success('YAML frontmatter found');
|
|
184
|
-
|
|
185
|
-
const frontmatter = frontmatterMatch[1];
|
|
186
|
-
|
|
187
|
-
// Check required fields
|
|
188
|
-
const requiredFields = ['name', 'description'];
|
|
189
|
-
requiredFields.forEach(field => {
|
|
190
|
-
if (!frontmatter.includes(`${field}:`)) {
|
|
191
|
-
error(`SKILL.md missing required field: ${field}`);
|
|
192
|
-
errorCount++;
|
|
193
|
-
} else {
|
|
194
|
-
success(`Required field found: ${field}`);
|
|
195
|
-
}
|
|
196
|
-
});
|
|
197
|
-
|
|
198
|
-
// Validate name format
|
|
199
|
-
const nameMatch = frontmatter.match(/name:\s*([^\n]+)/);
|
|
200
|
-
if (nameMatch) {
|
|
201
|
-
const name = nameMatch[1].trim();
|
|
202
|
-
if (!/^[a-z0-9-]+$/.test(name)) {
|
|
203
|
-
error(`Invalid skill name format: "${name}" (use lowercase, numbers, hyphens only)`);
|
|
204
|
-
errorCount++;
|
|
205
|
-
} else if (name.length > 64) {
|
|
206
|
-
error(`Skill name too long: ${name.length} characters (max 64)`);
|
|
207
|
-
errorCount++;
|
|
208
|
-
} else {
|
|
209
|
-
success(`Valid skill name: ${name}`);
|
|
210
|
-
}
|
|
211
|
-
}
|
|
212
|
-
|
|
213
|
-
// Validate description length
|
|
214
|
-
const descMatch = frontmatter.match(/description:\s*([^\n]+)/);
|
|
215
|
-
if (descMatch && descMatch[1].length > 1024) {
|
|
216
|
-
error(`Description too long: ${descMatch[1].length} characters (max 1024)`);
|
|
217
|
-
errorCount++;
|
|
218
|
-
} else if (descMatch) {
|
|
219
|
-
success(`Valid description length: ${descMatch[1].length} characters`);
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
// Check content size
|
|
223
|
-
const sizeKB = (Buffer.byteLength(content, 'utf8') / 1024).toFixed(2);
|
|
224
|
-
info(`SKILL.md size: ${sizeKB} KB`);
|
|
225
|
-
if (sizeKB > 100) {
|
|
226
|
-
warning('SKILL.md is quite large (> 100 KB). Consider splitting content.');
|
|
227
|
-
warningCount++;
|
|
228
|
-
}
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
function validatePackageJson() {
|
|
232
|
-
const packageJsonPath = path.join(__dirname, '..', 'package.json');
|
|
233
|
-
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
234
|
-
|
|
235
|
-
// Check required fields
|
|
236
|
-
const requiredFields = ['name', 'version', 'description', 'main', 'keywords', 'license'];
|
|
237
|
-
requiredFields.forEach(field => {
|
|
238
|
-
if (!packageJson[field]) {
|
|
239
|
-
error(`package.json missing required field: ${field}`);
|
|
240
|
-
errorCount++;
|
|
241
|
-
} else {
|
|
242
|
-
success(`package.json has ${field}`);
|
|
243
|
-
}
|
|
244
|
-
});
|
|
245
|
-
|
|
246
|
-
// Check keywords include claude-code
|
|
247
|
-
if (packageJson.keywords && !packageJson.keywords.includes('claude-code')) {
|
|
248
|
-
warning('package.json keywords should include "claude-code"');
|
|
249
|
-
warningCount++;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// Check files array
|
|
253
|
-
if (!packageJson.files || packageJson.files.length === 0) {
|
|
254
|
-
warning('package.json should specify "files" array for npm publish');
|
|
255
|
-
warningCount++;
|
|
256
|
-
} else {
|
|
257
|
-
success(`package.json specifies ${packageJson.files.length} file patterns`);
|
|
258
|
-
}
|
|
259
|
-
|
|
260
|
-
// Validate version format
|
|
261
|
-
const versionRegex = /^\d+\.\d+\.\d+(-[a-z0-9.]+)?$/;
|
|
262
|
-
if (!versionRegex.test(packageJson.version)) {
|
|
263
|
-
error(`Invalid version format: ${packageJson.version} (use semver: X.Y.Z)`);
|
|
264
|
-
errorCount++;
|
|
265
|
-
} else {
|
|
266
|
-
success(`Valid version: ${packageJson.version}`);
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
function validatePluginManifest() {
|
|
271
|
-
info('\nValidating Claude plugin manifest...');
|
|
272
|
-
const pluginJsonPath = path.join(__dirname, '..', '.claude-plugin', 'plugin.json');
|
|
273
|
-
const marketplacePath = path.join(__dirname, '..', '.claude-plugin', 'marketplace.json');
|
|
274
|
-
|
|
275
|
-
if (!fs.existsSync(pluginJsonPath)) {
|
|
276
|
-
error('.claude-plugin/plugin.json not found');
|
|
277
|
-
errorCount++;
|
|
278
|
-
return;
|
|
279
|
-
}
|
|
280
|
-
success('.claude-plugin/plugin.json found');
|
|
281
|
-
|
|
282
|
-
try {
|
|
283
|
-
const plugin = JSON.parse(fs.readFileSync(pluginJsonPath, 'utf8'));
|
|
284
|
-
const requiredFields = ['name', 'description', 'version'];
|
|
285
|
-
requiredFields.forEach(field => {
|
|
286
|
-
if (!plugin[field]) {
|
|
287
|
-
error(`plugin.json missing required field: ${field}`);
|
|
288
|
-
errorCount++;
|
|
289
|
-
} else {
|
|
290
|
-
success(`plugin.json has ${field}`);
|
|
291
|
-
}
|
|
292
|
-
});
|
|
293
|
-
|
|
294
|
-
// Cross-check version with package.json
|
|
295
|
-
const packageJsonPath = path.join(__dirname, '..', 'package.json');
|
|
296
|
-
if (fs.existsSync(packageJsonPath)) {
|
|
297
|
-
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
298
|
-
if (plugin.version !== pkg.version) {
|
|
299
|
-
error(`Version mismatch: plugin.json=${plugin.version}, package.json=${pkg.version}`);
|
|
300
|
-
errorCount++;
|
|
301
|
-
} else {
|
|
302
|
-
success('plugin.json version matches package.json');
|
|
303
|
-
}
|
|
304
|
-
}
|
|
305
|
-
} catch (err) {
|
|
306
|
-
error(`plugin.json parse error: ${err.message}`);
|
|
307
|
-
errorCount++;
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
if (!fs.existsSync(marketplacePath)) {
|
|
311
|
-
warning('.claude-plugin/marketplace.json not found (optional for plugins)');
|
|
312
|
-
warningCount++;
|
|
313
|
-
} else {
|
|
314
|
-
try {
|
|
315
|
-
const mkt = JSON.parse(fs.readFileSync(marketplacePath, 'utf8'));
|
|
316
|
-
if (!mkt.name || !mkt.owner || !mkt.plugins) {
|
|
317
|
-
error('marketplace.json missing required fields (name, owner, plugins)');
|
|
318
|
-
errorCount++;
|
|
319
|
-
} else {
|
|
320
|
-
success(`marketplace.json valid: ${mkt.plugins.length} plugin(s) defined`);
|
|
321
|
-
}
|
|
322
|
-
} catch (err) {
|
|
323
|
-
error(`marketplace.json parse error: ${err.message}`);
|
|
324
|
-
errorCount++;
|
|
325
|
-
}
|
|
326
|
-
}
|
|
327
|
-
}
|
|
328
|
-
|
|
329
|
-
// Run validation
|
|
330
|
-
try {
|
|
331
|
-
validateSkill();
|
|
332
|
-
validatePluginManifest();
|
|
333
|
-
|
|
334
|
-
// Final summary
|
|
335
|
-
log('\n' + '='.repeat(50), 'blue');
|
|
336
|
-
if (errorCount === 0 && warningCount === 0) {
|
|
337
|
-
success('\n✨ All validation checks passed! Ready to publish. ✨\n');
|
|
338
|
-
process.exit(0);
|
|
339
|
-
} else if (errorCount === 0) {
|
|
340
|
-
warning(`\n⚠️ Validation passed with ${warningCount} warning(s)\n`);
|
|
341
|
-
process.exit(0);
|
|
342
|
-
} else {
|
|
343
|
-
error(`\n❌ Validation failed with ${errorCount} error(s) and ${warningCount} warning(s)\n`);
|
|
344
|
-
process.exit(1);
|
|
345
|
-
}
|
|
346
|
-
} catch (err) {
|
|
347
|
-
error(`Validation error: ${err.message}`);
|
|
348
|
-
console.error(err);
|
|
349
|
-
process.exit(1);
|
|
350
|
-
}
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Skill Structure Validator
|
|
5
|
+
*
|
|
6
|
+
* Validates the Claude Code skill structure before publishing to npm.
|
|
7
|
+
* Checks:
|
|
8
|
+
* - SKILL.md exists and has valid frontmatter
|
|
9
|
+
* - Required directories are present
|
|
10
|
+
* - Framework files exist
|
|
11
|
+
* - Templates exist
|
|
12
|
+
* - File sizes are reasonable
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
const fs = require('fs');
|
|
16
|
+
const path = require('path');
|
|
17
|
+
|
|
18
|
+
// Colors for console output
|
|
19
|
+
const colors = {
|
|
20
|
+
reset: '\x1b[0m',
|
|
21
|
+
red: '\x1b[31m',
|
|
22
|
+
green: '\x1b[32m',
|
|
23
|
+
yellow: '\x1b[33m',
|
|
24
|
+
blue: '\x1b[34m',
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
function log(message, color = 'reset') {
|
|
28
|
+
console.log(`${colors[color]}${message}${colors.reset}`);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function error(message) {
|
|
32
|
+
log(`❌ ${message}`, 'red');
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
function success(message) {
|
|
36
|
+
log(`✅ ${message}`, 'green');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function warning(message) {
|
|
40
|
+
log(`⚠️ ${message}`, 'yellow');
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
function info(message) {
|
|
44
|
+
log(`ℹ️ ${message}`, 'blue');
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
// Configuration
|
|
48
|
+
const SKILL_DIR = path.join(__dirname, '..', 'skills', 'prompt-architect');
|
|
49
|
+
const SKILL_FILE = path.join(SKILL_DIR, 'SKILL.md');
|
|
50
|
+
|
|
51
|
+
const REQUIRED_DIRS = [
|
|
52
|
+
'scripts',
|
|
53
|
+
'references/frameworks',
|
|
54
|
+
'assets/templates',
|
|
55
|
+
];
|
|
56
|
+
|
|
57
|
+
const REQUIRED_FRAMEWORKS = [
|
|
58
|
+
'co-star.md',
|
|
59
|
+
'risen.md',
|
|
60
|
+
'rise.md',
|
|
61
|
+
'tidd-ec.md',
|
|
62
|
+
'rtf.md',
|
|
63
|
+
'chain-of-thought.md',
|
|
64
|
+
'chain-of-density.md',
|
|
65
|
+
];
|
|
66
|
+
|
|
67
|
+
const REQUIRED_TEMPLATES = [
|
|
68
|
+
'co-star_template.txt',
|
|
69
|
+
'risen_template.txt',
|
|
70
|
+
'rise-ie_template.txt',
|
|
71
|
+
'rise-ix_template.txt',
|
|
72
|
+
'tidd-ec_template.txt',
|
|
73
|
+
'rtf_template.txt',
|
|
74
|
+
'hybrid_template.txt',
|
|
75
|
+
];
|
|
76
|
+
|
|
77
|
+
const REQUIRED_SCRIPTS = [
|
|
78
|
+
'framework_analyzer.py',
|
|
79
|
+
'prompt_evaluator.py',
|
|
80
|
+
];
|
|
81
|
+
|
|
82
|
+
let errorCount = 0;
|
|
83
|
+
let warningCount = 0;
|
|
84
|
+
|
|
85
|
+
// Main validation
|
|
86
|
+
function validateSkill() {
|
|
87
|
+
log('\n🔍 Validating Prompt Architect Skill Structure...\n', 'blue');
|
|
88
|
+
|
|
89
|
+
// Check skill directory exists
|
|
90
|
+
if (!fs.existsSync(SKILL_DIR)) {
|
|
91
|
+
error(`Skill directory not found: ${SKILL_DIR}`);
|
|
92
|
+
process.exit(1);
|
|
93
|
+
}
|
|
94
|
+
success('Skill directory found');
|
|
95
|
+
|
|
96
|
+
// Check SKILL.md exists
|
|
97
|
+
if (!fs.existsSync(SKILL_FILE)) {
|
|
98
|
+
error('SKILL.md not found');
|
|
99
|
+
errorCount++;
|
|
100
|
+
} else {
|
|
101
|
+
success('SKILL.md found');
|
|
102
|
+
validateSkillFile();
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
// Check required directories
|
|
106
|
+
REQUIRED_DIRS.forEach(dir => {
|
|
107
|
+
const fullPath = path.join(SKILL_DIR, dir);
|
|
108
|
+
if (!fs.existsSync(fullPath)) {
|
|
109
|
+
error(`Required directory missing: ${dir}`);
|
|
110
|
+
errorCount++;
|
|
111
|
+
} else {
|
|
112
|
+
success(`Directory found: ${dir}`);
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
|
|
116
|
+
// Check framework files
|
|
117
|
+
info('\nValidating framework files...');
|
|
118
|
+
REQUIRED_FRAMEWORKS.forEach(framework => {
|
|
119
|
+
const frameworkPath = path.join(SKILL_DIR, 'references', 'frameworks', framework);
|
|
120
|
+
if (!fs.existsSync(frameworkPath)) {
|
|
121
|
+
error(`Framework file missing: ${framework}`);
|
|
122
|
+
errorCount++;
|
|
123
|
+
} else {
|
|
124
|
+
const stats = fs.statSync(frameworkPath);
|
|
125
|
+
const sizeKB = (stats.size / 1024).toFixed(2);
|
|
126
|
+
success(`Framework found: ${framework} (${sizeKB} KB)`);
|
|
127
|
+
|
|
128
|
+
// Warn if framework file is too small
|
|
129
|
+
if (stats.size < 5000) {
|
|
130
|
+
warning(`Framework ${framework} seems small (< 5 KB)`);
|
|
131
|
+
warningCount++;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
// Check template files
|
|
137
|
+
info('\nValidating template files...');
|
|
138
|
+
REQUIRED_TEMPLATES.forEach(template => {
|
|
139
|
+
const templatePath = path.join(SKILL_DIR, 'assets', 'templates', template);
|
|
140
|
+
if (!fs.existsSync(templatePath)) {
|
|
141
|
+
error(`Template file missing: ${template}`);
|
|
142
|
+
errorCount++;
|
|
143
|
+
} else {
|
|
144
|
+
success(`Template found: ${template}`);
|
|
145
|
+
}
|
|
146
|
+
});
|
|
147
|
+
|
|
148
|
+
// Check script files
|
|
149
|
+
info('\nValidating script files...');
|
|
150
|
+
REQUIRED_SCRIPTS.forEach(script => {
|
|
151
|
+
const scriptPath = path.join(SKILL_DIR, 'scripts', script);
|
|
152
|
+
if (!fs.existsSync(scriptPath)) {
|
|
153
|
+
error(`Script file missing: ${script}`);
|
|
154
|
+
errorCount++;
|
|
155
|
+
} else {
|
|
156
|
+
success(`Script found: ${script}`);
|
|
157
|
+
}
|
|
158
|
+
});
|
|
159
|
+
|
|
160
|
+
// Check package structure
|
|
161
|
+
info('\nValidating package structure...');
|
|
162
|
+
const packageJson = path.join(__dirname, '..', 'package.json');
|
|
163
|
+
if (!fs.existsSync(packageJson)) {
|
|
164
|
+
error('package.json not found');
|
|
165
|
+
errorCount++;
|
|
166
|
+
} else {
|
|
167
|
+
success('package.json found');
|
|
168
|
+
validatePackageJson();
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
function validateSkillFile() {
|
|
174
|
+
const content = fs.readFileSync(SKILL_FILE, 'utf8');
|
|
175
|
+
|
|
176
|
+
// Check for YAML frontmatter (handle both LF and CRLF line endings)
|
|
177
|
+
const frontmatterMatch = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
178
|
+
if (!frontmatterMatch) {
|
|
179
|
+
error('SKILL.md missing YAML frontmatter (---\\n...\\n---)');
|
|
180
|
+
errorCount++;
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
success('YAML frontmatter found');
|
|
184
|
+
|
|
185
|
+
const frontmatter = frontmatterMatch[1];
|
|
186
|
+
|
|
187
|
+
// Check required fields
|
|
188
|
+
const requiredFields = ['name', 'description'];
|
|
189
|
+
requiredFields.forEach(field => {
|
|
190
|
+
if (!frontmatter.includes(`${field}:`)) {
|
|
191
|
+
error(`SKILL.md missing required field: ${field}`);
|
|
192
|
+
errorCount++;
|
|
193
|
+
} else {
|
|
194
|
+
success(`Required field found: ${field}`);
|
|
195
|
+
}
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
// Validate name format
|
|
199
|
+
const nameMatch = frontmatter.match(/name:\s*([^\n]+)/);
|
|
200
|
+
if (nameMatch) {
|
|
201
|
+
const name = nameMatch[1].trim();
|
|
202
|
+
if (!/^[a-z0-9-]+$/.test(name)) {
|
|
203
|
+
error(`Invalid skill name format: "${name}" (use lowercase, numbers, hyphens only)`);
|
|
204
|
+
errorCount++;
|
|
205
|
+
} else if (name.length > 64) {
|
|
206
|
+
error(`Skill name too long: ${name.length} characters (max 64)`);
|
|
207
|
+
errorCount++;
|
|
208
|
+
} else {
|
|
209
|
+
success(`Valid skill name: ${name}`);
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
// Validate description length
|
|
214
|
+
const descMatch = frontmatter.match(/description:\s*([^\n]+)/);
|
|
215
|
+
if (descMatch && descMatch[1].length > 1024) {
|
|
216
|
+
error(`Description too long: ${descMatch[1].length} characters (max 1024)`);
|
|
217
|
+
errorCount++;
|
|
218
|
+
} else if (descMatch) {
|
|
219
|
+
success(`Valid description length: ${descMatch[1].length} characters`);
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
// Check content size
|
|
223
|
+
const sizeKB = (Buffer.byteLength(content, 'utf8') / 1024).toFixed(2);
|
|
224
|
+
info(`SKILL.md size: ${sizeKB} KB`);
|
|
225
|
+
if (sizeKB > 100) {
|
|
226
|
+
warning('SKILL.md is quite large (> 100 KB). Consider splitting content.');
|
|
227
|
+
warningCount++;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function validatePackageJson() {
|
|
232
|
+
const packageJsonPath = path.join(__dirname, '..', 'package.json');
|
|
233
|
+
const packageJson = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
234
|
+
|
|
235
|
+
// Check required fields
|
|
236
|
+
const requiredFields = ['name', 'version', 'description', 'main', 'keywords', 'license'];
|
|
237
|
+
requiredFields.forEach(field => {
|
|
238
|
+
if (!packageJson[field]) {
|
|
239
|
+
error(`package.json missing required field: ${field}`);
|
|
240
|
+
errorCount++;
|
|
241
|
+
} else {
|
|
242
|
+
success(`package.json has ${field}`);
|
|
243
|
+
}
|
|
244
|
+
});
|
|
245
|
+
|
|
246
|
+
// Check keywords include claude-code
|
|
247
|
+
if (packageJson.keywords && !packageJson.keywords.includes('claude-code')) {
|
|
248
|
+
warning('package.json keywords should include "claude-code"');
|
|
249
|
+
warningCount++;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
// Check files array
|
|
253
|
+
if (!packageJson.files || packageJson.files.length === 0) {
|
|
254
|
+
warning('package.json should specify "files" array for npm publish');
|
|
255
|
+
warningCount++;
|
|
256
|
+
} else {
|
|
257
|
+
success(`package.json specifies ${packageJson.files.length} file patterns`);
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// Validate version format
|
|
261
|
+
const versionRegex = /^\d+\.\d+\.\d+(-[a-z0-9.]+)?$/;
|
|
262
|
+
if (!versionRegex.test(packageJson.version)) {
|
|
263
|
+
error(`Invalid version format: ${packageJson.version} (use semver: X.Y.Z)`);
|
|
264
|
+
errorCount++;
|
|
265
|
+
} else {
|
|
266
|
+
success(`Valid version: ${packageJson.version}`);
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
function validatePluginManifest() {
|
|
271
|
+
info('\nValidating Claude plugin manifest...');
|
|
272
|
+
const pluginJsonPath = path.join(__dirname, '..', '.claude-plugin', 'plugin.json');
|
|
273
|
+
const marketplacePath = path.join(__dirname, '..', '.claude-plugin', 'marketplace.json');
|
|
274
|
+
|
|
275
|
+
if (!fs.existsSync(pluginJsonPath)) {
|
|
276
|
+
error('.claude-plugin/plugin.json not found');
|
|
277
|
+
errorCount++;
|
|
278
|
+
return;
|
|
279
|
+
}
|
|
280
|
+
success('.claude-plugin/plugin.json found');
|
|
281
|
+
|
|
282
|
+
try {
|
|
283
|
+
const plugin = JSON.parse(fs.readFileSync(pluginJsonPath, 'utf8'));
|
|
284
|
+
const requiredFields = ['name', 'description', 'version'];
|
|
285
|
+
requiredFields.forEach(field => {
|
|
286
|
+
if (!plugin[field]) {
|
|
287
|
+
error(`plugin.json missing required field: ${field}`);
|
|
288
|
+
errorCount++;
|
|
289
|
+
} else {
|
|
290
|
+
success(`plugin.json has ${field}`);
|
|
291
|
+
}
|
|
292
|
+
});
|
|
293
|
+
|
|
294
|
+
// Cross-check version with package.json
|
|
295
|
+
const packageJsonPath = path.join(__dirname, '..', 'package.json');
|
|
296
|
+
if (fs.existsSync(packageJsonPath)) {
|
|
297
|
+
const pkg = JSON.parse(fs.readFileSync(packageJsonPath, 'utf8'));
|
|
298
|
+
if (plugin.version !== pkg.version) {
|
|
299
|
+
error(`Version mismatch: plugin.json=${plugin.version}, package.json=${pkg.version}`);
|
|
300
|
+
errorCount++;
|
|
301
|
+
} else {
|
|
302
|
+
success('plugin.json version matches package.json');
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
} catch (err) {
|
|
306
|
+
error(`plugin.json parse error: ${err.message}`);
|
|
307
|
+
errorCount++;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (!fs.existsSync(marketplacePath)) {
|
|
311
|
+
warning('.claude-plugin/marketplace.json not found (optional for plugins)');
|
|
312
|
+
warningCount++;
|
|
313
|
+
} else {
|
|
314
|
+
try {
|
|
315
|
+
const mkt = JSON.parse(fs.readFileSync(marketplacePath, 'utf8'));
|
|
316
|
+
if (!mkt.name || !mkt.owner || !mkt.plugins) {
|
|
317
|
+
error('marketplace.json missing required fields (name, owner, plugins)');
|
|
318
|
+
errorCount++;
|
|
319
|
+
} else {
|
|
320
|
+
success(`marketplace.json valid: ${mkt.plugins.length} plugin(s) defined`);
|
|
321
|
+
}
|
|
322
|
+
} catch (err) {
|
|
323
|
+
error(`marketplace.json parse error: ${err.message}`);
|
|
324
|
+
errorCount++;
|
|
325
|
+
}
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// Run validation
|
|
330
|
+
try {
|
|
331
|
+
validateSkill();
|
|
332
|
+
validatePluginManifest();
|
|
333
|
+
|
|
334
|
+
// Final summary
|
|
335
|
+
log('\n' + '='.repeat(50), 'blue');
|
|
336
|
+
if (errorCount === 0 && warningCount === 0) {
|
|
337
|
+
success('\n✨ All validation checks passed! Ready to publish. ✨\n');
|
|
338
|
+
process.exit(0);
|
|
339
|
+
} else if (errorCount === 0) {
|
|
340
|
+
warning(`\n⚠️ Validation passed with ${warningCount} warning(s)\n`);
|
|
341
|
+
process.exit(0);
|
|
342
|
+
} else {
|
|
343
|
+
error(`\n❌ Validation failed with ${errorCount} error(s) and ${warningCount} warning(s)\n`);
|
|
344
|
+
process.exit(1);
|
|
345
|
+
}
|
|
346
|
+
} catch (err) {
|
|
347
|
+
error(`Validation error: ${err.message}`);
|
|
348
|
+
console.error(err);
|
|
349
|
+
process.exit(1);
|
|
350
|
+
}
|