@ckelsoe/prompt-architect 3.0.0
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 +20 -0
- package/.claude-plugin/plugin.json +18 -0
- package/CHANGELOG.md +264 -0
- package/LICENSE +21 -0
- package/MIGRATION.md +66 -0
- package/README.md +1184 -0
- package/adapters/README.md +267 -0
- package/adapters/for-cursor.mdc +138 -0
- package/adapters/for-gemini-cli.md +70 -0
- package/adapters/for-github-copilot.md +141 -0
- package/adapters/for-openai-codex-cli.md +158 -0
- package/adapters/for-windsurf.md +136 -0
- package/adapters/system-prompt.md +243 -0
- package/package.json +135 -0
- package/scripts/install.js +490 -0
- package/scripts/test.js +213 -0
- package/scripts/validate-skill.js +350 -0
- package/skills/prompt-architect/SKILL.md +331 -0
- package/skills/prompt-architect/assets/templates/ape_template.txt +5 -0
- package/skills/prompt-architect/assets/templates/bab_template.txt +15 -0
- package/skills/prompt-architect/assets/templates/broke_template.txt +15 -0
- package/skills/prompt-architect/assets/templates/cai-critique-revise_template.txt +18 -0
- package/skills/prompt-architect/assets/templates/care_template.txt +16 -0
- package/skills/prompt-architect/assets/templates/chain-of-density_template.txt +46 -0
- package/skills/prompt-architect/assets/templates/chain-of-thought_template.txt +49 -0
- package/skills/prompt-architect/assets/templates/co-star_template.txt +17 -0
- package/skills/prompt-architect/assets/templates/crispe_template.txt +14 -0
- package/skills/prompt-architect/assets/templates/ctf_template.txt +15 -0
- package/skills/prompt-architect/assets/templates/devils-advocate_template.txt +25 -0
- package/skills/prompt-architect/assets/templates/hybrid_template.txt +101 -0
- package/skills/prompt-architect/assets/templates/least-to-most_template.txt +16 -0
- package/skills/prompt-architect/assets/templates/plan-and-solve_template.txt +7 -0
- package/skills/prompt-architect/assets/templates/pre-mortem_template.txt +27 -0
- package/skills/prompt-architect/assets/templates/race_template.txt +19 -0
- package/skills/prompt-architect/assets/templates/rcot_template.txt +31 -0
- package/skills/prompt-architect/assets/templates/react_template.txt +27 -0
- package/skills/prompt-architect/assets/templates/reverse-role_template.txt +11 -0
- package/skills/prompt-architect/assets/templates/rise-ie_template.txt +23 -0
- package/skills/prompt-architect/assets/templates/rise-ix_template.txt +28 -0
- package/skills/prompt-architect/assets/templates/risen_template.txt +21 -0
- package/skills/prompt-architect/assets/templates/rpef_template.txt +26 -0
- package/skills/prompt-architect/assets/templates/rtf_template.txt +13 -0
- package/skills/prompt-architect/assets/templates/self-refine_template.txt +16 -0
- package/skills/prompt-architect/assets/templates/skeleton-of-thought_template.txt +15 -0
- package/skills/prompt-architect/assets/templates/step-back_template.txt +10 -0
- package/skills/prompt-architect/assets/templates/tidd-ec_template.txt +45 -0
- package/skills/prompt-architect/assets/templates/tree-of-thought_template.txt +30 -0
- package/skills/prompt-architect/references/frameworks/ape.md +200 -0
- package/skills/prompt-architect/references/frameworks/bab.md +242 -0
- package/skills/prompt-architect/references/frameworks/broke.md +242 -0
- package/skills/prompt-architect/references/frameworks/cai-critique-revise.md +230 -0
- package/skills/prompt-architect/references/frameworks/care.md +235 -0
- package/skills/prompt-architect/references/frameworks/chain-of-density.md +479 -0
- package/skills/prompt-architect/references/frameworks/chain-of-thought.md +424 -0
- package/skills/prompt-architect/references/frameworks/co-star.md +256 -0
- package/skills/prompt-architect/references/frameworks/crispe.md +253 -0
- package/skills/prompt-architect/references/frameworks/ctf.md +207 -0
- package/skills/prompt-architect/references/frameworks/devils-advocate.md +206 -0
- package/skills/prompt-architect/references/frameworks/least-to-most.md +226 -0
- package/skills/prompt-architect/references/frameworks/plan-and-solve.md +210 -0
- package/skills/prompt-architect/references/frameworks/pre-mortem.md +197 -0
- package/skills/prompt-architect/references/frameworks/race.md +243 -0
- package/skills/prompt-architect/references/frameworks/rcot.md +203 -0
- package/skills/prompt-architect/references/frameworks/react.md +304 -0
- package/skills/prompt-architect/references/frameworks/reverse-role.md +189 -0
- package/skills/prompt-architect/references/frameworks/rise.md +555 -0
- package/skills/prompt-architect/references/frameworks/risen.md +297 -0
- package/skills/prompt-architect/references/frameworks/rpef.md +195 -0
- package/skills/prompt-architect/references/frameworks/rtf.md +358 -0
- package/skills/prompt-architect/references/frameworks/self-refine.md +205 -0
- package/skills/prompt-architect/references/frameworks/skeleton-of-thought.md +186 -0
- package/skills/prompt-architect/references/frameworks/step-back.md +206 -0
- package/skills/prompt-architect/references/frameworks/tidd-ec.md +470 -0
- package/skills/prompt-architect/references/frameworks/tree-of-thought.md +260 -0
- package/skills/prompt-architect/scripts/framework_analyzer.py +807 -0
- package/skills/prompt-architect/scripts/prompt_evaluator.py +336 -0
|
@@ -0,0 +1,490 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Prompt Architect — Multi-Agent Installer
|
|
5
|
+
*
|
|
6
|
+
* Detects installed AI agents and lets users choose where to install.
|
|
7
|
+
* Uses @clack/prompts for interactive UI, falls back to non-interactive
|
|
8
|
+
* for postinstall hooks and CI environments.
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* npx @ckelsoe/prompt-architect # Interactive multi-agent install
|
|
12
|
+
* npx @ckelsoe/prompt-architect --all # Install to all detected agents
|
|
13
|
+
* npx @ckelsoe/prompt-architect --claude # Install to Claude Code only
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
const fs = require('fs');
|
|
17
|
+
const path = require('path');
|
|
18
|
+
const os = require('os');
|
|
19
|
+
const { execSync } = require('child_process');
|
|
20
|
+
|
|
21
|
+
// Configuration
|
|
22
|
+
const SKILL_NAME = 'prompt-architect';
|
|
23
|
+
const PACKAGE_NAME = '@ckelsoe/prompt-architect';
|
|
24
|
+
const SKILL_SOURCE_DIR = 'skills';
|
|
25
|
+
|
|
26
|
+
// ─── Agent Registry ──────────────────────────────────────────────
|
|
27
|
+
|
|
28
|
+
const AGENTS = [
|
|
29
|
+
{
|
|
30
|
+
id: 'claude',
|
|
31
|
+
name: 'Claude Code',
|
|
32
|
+
detect: () => fs.existsSync(path.join(os.homedir(), '.claude')),
|
|
33
|
+
installPath: (project) => project
|
|
34
|
+
? path.join(process.cwd(), '.claude', 'skills', SKILL_NAME)
|
|
35
|
+
: path.join(os.homedir(), '.claude', 'skills', SKILL_NAME),
|
|
36
|
+
format: 'skill',
|
|
37
|
+
hint: (project) => project ? '.claude/skills/ (project)' : '~/.claude/skills/',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
id: 'gemini',
|
|
41
|
+
name: 'Gemini CLI',
|
|
42
|
+
detect: () => {
|
|
43
|
+
if (fs.existsSync(path.join(os.homedir(), '.gemini'))) return true;
|
|
44
|
+
try {
|
|
45
|
+
execSync(process.platform === 'win32' ? 'where gemini' : 'which gemini', { stdio: 'ignore' });
|
|
46
|
+
return true;
|
|
47
|
+
} catch { return false; }
|
|
48
|
+
},
|
|
49
|
+
installPath: () => path.join(os.homedir(), '.gemini', 'skills', SKILL_NAME),
|
|
50
|
+
format: 'skill',
|
|
51
|
+
hint: () => '~/.gemini/skills/',
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
id: 'agents',
|
|
55
|
+
name: 'Agent Skills (universal)',
|
|
56
|
+
detect: () => true, // Always available
|
|
57
|
+
alwaysShow: true,
|
|
58
|
+
installPath: () => path.join(os.homedir(), '.agents', 'skills', SKILL_NAME),
|
|
59
|
+
format: 'skill',
|
|
60
|
+
hint: () => '~/.agents/skills/ (30+ compatible agents)',
|
|
61
|
+
},
|
|
62
|
+
{
|
|
63
|
+
id: 'cursor',
|
|
64
|
+
name: 'Cursor',
|
|
65
|
+
detect: () => {
|
|
66
|
+
return fs.existsSync(path.join(os.homedir(), '.cursor')) ||
|
|
67
|
+
fs.existsSync(path.join(process.cwd(), '.cursor'));
|
|
68
|
+
},
|
|
69
|
+
installPath: () => path.join(process.cwd(), '.cursor', 'rules', 'prompt-architect.mdc'),
|
|
70
|
+
format: 'adapter',
|
|
71
|
+
adapterFile: 'for-cursor.mdc',
|
|
72
|
+
hint: () => '.cursor/rules/ (project)',
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
id: 'copilot',
|
|
76
|
+
name: 'GitHub Copilot',
|
|
77
|
+
detect: () => {
|
|
78
|
+
try {
|
|
79
|
+
execSync(process.platform === 'win32' ? 'where code' : 'which code', { stdio: 'ignore' });
|
|
80
|
+
return true;
|
|
81
|
+
} catch {
|
|
82
|
+
return fs.existsSync(path.join(process.cwd(), '.vscode')) ||
|
|
83
|
+
fs.existsSync(path.join(process.cwd(), '.github'));
|
|
84
|
+
}
|
|
85
|
+
},
|
|
86
|
+
installPath: () => path.join(process.cwd(), '.github', 'copilot-instructions.md'),
|
|
87
|
+
format: 'adapter',
|
|
88
|
+
adapterFile: 'for-github-copilot.md',
|
|
89
|
+
appendMode: true,
|
|
90
|
+
hint: () => '.github/copilot-instructions.md (project)',
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
id: 'windsurf',
|
|
94
|
+
name: 'Windsurf',
|
|
95
|
+
detect: () => {
|
|
96
|
+
return fs.existsSync(path.join(process.cwd(), '.windsurfrules')) ||
|
|
97
|
+
fs.existsSync(path.join(os.homedir(), '.windsurf'));
|
|
98
|
+
},
|
|
99
|
+
installPath: () => path.join(process.cwd(), '.windsurfrules'),
|
|
100
|
+
format: 'adapter',
|
|
101
|
+
adapterFile: 'for-windsurf.md',
|
|
102
|
+
appendMode: true,
|
|
103
|
+
hint: () => '.windsurfrules (project)',
|
|
104
|
+
},
|
|
105
|
+
{
|
|
106
|
+
id: 'codex',
|
|
107
|
+
name: 'OpenAI Codex CLI',
|
|
108
|
+
detect: () => {
|
|
109
|
+
try {
|
|
110
|
+
execSync(process.platform === 'win32' ? 'where codex' : 'which codex', { stdio: 'ignore' });
|
|
111
|
+
return true;
|
|
112
|
+
} catch {
|
|
113
|
+
return fs.existsSync(path.join(process.cwd(), 'AGENTS.md'));
|
|
114
|
+
}
|
|
115
|
+
},
|
|
116
|
+
installPath: () => path.join(process.cwd(), 'AGENTS.md'),
|
|
117
|
+
format: 'adapter',
|
|
118
|
+
adapterFile: 'for-openai-codex-cli.md',
|
|
119
|
+
appendMode: true,
|
|
120
|
+
hint: () => 'AGENTS.md (project)',
|
|
121
|
+
},
|
|
122
|
+
];
|
|
123
|
+
|
|
124
|
+
// ─── Source Path Resolution ──────────────────────────────────────
|
|
125
|
+
|
|
126
|
+
function findSourcePath() {
|
|
127
|
+
// Try relative to this script (development or npm package)
|
|
128
|
+
const fromScript = path.join(__dirname, '..', SKILL_SOURCE_DIR, SKILL_NAME);
|
|
129
|
+
if (fs.existsSync(fromScript)) return fromScript;
|
|
130
|
+
|
|
131
|
+
// Try local node_modules
|
|
132
|
+
const localPath = path.join(process.cwd(), 'node_modules', PACKAGE_NAME, SKILL_SOURCE_DIR, SKILL_NAME);
|
|
133
|
+
if (fs.existsSync(localPath)) return localPath;
|
|
134
|
+
|
|
135
|
+
// Try global node_modules
|
|
136
|
+
try {
|
|
137
|
+
const globalRoot = execSync('npm root -g', { encoding: 'utf8' }).trim();
|
|
138
|
+
const globalPath = path.join(globalRoot, PACKAGE_NAME, SKILL_SOURCE_DIR, SKILL_NAME);
|
|
139
|
+
if (fs.existsSync(globalPath)) return globalPath;
|
|
140
|
+
} catch { /* ignore */ }
|
|
141
|
+
|
|
142
|
+
return null;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function findAdaptersPath() {
|
|
146
|
+
const fromScript = path.join(__dirname, '..', 'adapters');
|
|
147
|
+
if (fs.existsSync(fromScript)) return fromScript;
|
|
148
|
+
|
|
149
|
+
const localPath = path.join(process.cwd(), 'node_modules', PACKAGE_NAME, 'adapters');
|
|
150
|
+
if (fs.existsSync(localPath)) return localPath;
|
|
151
|
+
|
|
152
|
+
try {
|
|
153
|
+
const globalRoot = execSync('npm root -g', { encoding: 'utf8' }).trim();
|
|
154
|
+
const globalPath = path.join(globalRoot, PACKAGE_NAME, 'adapters');
|
|
155
|
+
if (fs.existsSync(globalPath)) return globalPath;
|
|
156
|
+
} catch { /* ignore */ }
|
|
157
|
+
|
|
158
|
+
return null;
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// ─── File Operations ─────────────────────────────────────────────
|
|
162
|
+
|
|
163
|
+
function copyRecursive(src, dest) {
|
|
164
|
+
const stats = fs.statSync(src);
|
|
165
|
+
if (stats.isDirectory()) {
|
|
166
|
+
if (!fs.existsSync(dest)) fs.mkdirSync(dest, { recursive: true });
|
|
167
|
+
for (const entry of fs.readdirSync(src)) {
|
|
168
|
+
copyRecursive(path.join(src, entry), path.join(dest, entry));
|
|
169
|
+
}
|
|
170
|
+
} else {
|
|
171
|
+
fs.copyFileSync(src, dest);
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
const MARKER_START = '<!-- prompt-architect-start -->';
|
|
176
|
+
const MARKER_END = '<!-- prompt-architect-end -->';
|
|
177
|
+
|
|
178
|
+
function installSkillDir(sourcePath, destPath) {
|
|
179
|
+
const parentDir = path.dirname(destPath);
|
|
180
|
+
if (!fs.existsSync(parentDir)) fs.mkdirSync(parentDir, { recursive: true });
|
|
181
|
+
|
|
182
|
+
// Remove existing installation
|
|
183
|
+
if (fs.existsSync(destPath)) fs.rmSync(destPath, { recursive: true, force: true });
|
|
184
|
+
|
|
185
|
+
copyRecursive(sourcePath, destPath);
|
|
186
|
+
|
|
187
|
+
// Verify
|
|
188
|
+
if (!fs.existsSync(path.join(destPath, 'SKILL.md'))) {
|
|
189
|
+
throw new Error('SKILL.md not found after copy');
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
function installAdapter(adaptersPath, agent, destPath) {
|
|
194
|
+
const adapterSrc = path.join(adaptersPath, agent.adapterFile);
|
|
195
|
+
if (!fs.existsSync(adapterSrc)) {
|
|
196
|
+
throw new Error(`Adapter file not found: ${agent.adapterFile}`);
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const adapterContent = fs.readFileSync(adapterSrc, 'utf8');
|
|
200
|
+
const markedContent = `\n${MARKER_START}\n${adapterContent}\n${MARKER_END}\n`;
|
|
201
|
+
|
|
202
|
+
if (agent.appendMode && fs.existsSync(destPath)) {
|
|
203
|
+
const existing = fs.readFileSync(destPath, 'utf8');
|
|
204
|
+
// Remove old content if present
|
|
205
|
+
const cleaned = existing.replace(
|
|
206
|
+
new RegExp(`\\n?${escapeRegex(MARKER_START)}[\\s\\S]*?${escapeRegex(MARKER_END)}\\n?`),
|
|
207
|
+
''
|
|
208
|
+
);
|
|
209
|
+
fs.writeFileSync(destPath, cleaned + markedContent, 'utf8');
|
|
210
|
+
} else {
|
|
211
|
+
const parentDir = path.dirname(destPath);
|
|
212
|
+
if (!fs.existsSync(parentDir)) fs.mkdirSync(parentDir, { recursive: true });
|
|
213
|
+
fs.writeFileSync(destPath, adapterContent, 'utf8');
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
function escapeRegex(str) {
|
|
218
|
+
return str.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// ─── Version ─────────────────────────────────────────────────────
|
|
222
|
+
|
|
223
|
+
function getVersion() {
|
|
224
|
+
try {
|
|
225
|
+
const pkg = JSON.parse(fs.readFileSync(path.join(__dirname, '..', 'package.json'), 'utf8'));
|
|
226
|
+
return pkg.version || 'unknown';
|
|
227
|
+
} catch { return 'unknown'; }
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
// ─── CLI Argument Parsing ────────────────────────────────────────
|
|
231
|
+
|
|
232
|
+
function parseArgs() {
|
|
233
|
+
const args = process.argv.slice(2);
|
|
234
|
+
const flags = {
|
|
235
|
+
help: args.includes('--help') || args.includes('-h'),
|
|
236
|
+
all: args.includes('--all') || args.includes('-a'),
|
|
237
|
+
yes: args.includes('--yes') || args.includes('-y'),
|
|
238
|
+
force: args.includes('--force') || args.includes('-f'),
|
|
239
|
+
project: args.includes('--project') || args.includes('-p'),
|
|
240
|
+
// Individual agent flags
|
|
241
|
+
claude: args.includes('--claude'),
|
|
242
|
+
gemini: args.includes('--gemini'),
|
|
243
|
+
agents: args.includes('--agents'),
|
|
244
|
+
cursor: args.includes('--cursor'),
|
|
245
|
+
copilot: args.includes('--copilot'),
|
|
246
|
+
windsurf: args.includes('--windsurf'),
|
|
247
|
+
codex: args.includes('--codex'),
|
|
248
|
+
};
|
|
249
|
+
|
|
250
|
+
const specificAgents = ['claude', 'gemini', 'agents', 'cursor', 'copilot', 'windsurf', 'codex']
|
|
251
|
+
.filter(a => flags[a]);
|
|
252
|
+
|
|
253
|
+
return { flags, specificAgents };
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// ─── Help ────────────────────────────────────────────────────────
|
|
257
|
+
|
|
258
|
+
function showHelp() {
|
|
259
|
+
const version = getVersion();
|
|
260
|
+
console.log(`
|
|
261
|
+
Prompt Architect v${version} — Multi-Agent Installer
|
|
262
|
+
|
|
263
|
+
Usage:
|
|
264
|
+
npx ${PACKAGE_NAME} Interactive multi-agent install
|
|
265
|
+
npx ${PACKAGE_NAME} --all Install to all detected agents
|
|
266
|
+
npx ${PACKAGE_NAME} --claude Install to Claude Code only
|
|
267
|
+
|
|
268
|
+
Agent flags (skip interactive, install to specified targets):
|
|
269
|
+
--claude Claude Code (~/.claude/skills/)
|
|
270
|
+
--gemini Gemini CLI (~/.gemini/skills/)
|
|
271
|
+
--agents Agent Skills universal (~/.agents/skills/)
|
|
272
|
+
--cursor Cursor (.cursor/rules/)
|
|
273
|
+
--copilot GitHub Copilot (.github/)
|
|
274
|
+
--windsurf Windsurf (.windsurfrules)
|
|
275
|
+
--codex OpenAI Codex CLI (AGENTS.md)
|
|
276
|
+
|
|
277
|
+
Options:
|
|
278
|
+
-a, --all Install to all detected agents
|
|
279
|
+
-p, --project Use project-local paths where applicable
|
|
280
|
+
-f, --force Overwrite existing installations
|
|
281
|
+
-y, --yes Accept defaults (all detected agents)
|
|
282
|
+
-h, --help Show this help
|
|
283
|
+
`);
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
// ─── Non-Interactive Install ─────────────────────────────────────
|
|
287
|
+
|
|
288
|
+
function installNonInteractive(agentIds, sourcePath, adaptersPath, isProject) {
|
|
289
|
+
const version = getVersion();
|
|
290
|
+
const results = [];
|
|
291
|
+
|
|
292
|
+
for (const agent of AGENTS) {
|
|
293
|
+
if (!agentIds.includes(agent.id)) continue;
|
|
294
|
+
|
|
295
|
+
const destPath = agent.installPath(isProject);
|
|
296
|
+
try {
|
|
297
|
+
if (agent.format === 'skill') {
|
|
298
|
+
installSkillDir(sourcePath, destPath);
|
|
299
|
+
} else {
|
|
300
|
+
installAdapter(adaptersPath, agent, destPath);
|
|
301
|
+
}
|
|
302
|
+
results.push({ agent: agent.name, path: destPath, success: true });
|
|
303
|
+
} catch (err) {
|
|
304
|
+
results.push({ agent: agent.name, path: destPath, success: false, error: err.message });
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
|
|
308
|
+
// Print results
|
|
309
|
+
console.log(`\n Prompt Architect v${version}\n`);
|
|
310
|
+
for (const r of results) {
|
|
311
|
+
if (r.success) {
|
|
312
|
+
console.log(` \x1b[32m✓\x1b[0m ${r.agent} → ${r.path}`);
|
|
313
|
+
} else {
|
|
314
|
+
console.log(` \x1b[31m✗\x1b[0m ${r.agent}: ${r.error}`);
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
const succeeded = results.filter(r => r.success).length;
|
|
319
|
+
const failed = results.filter(r => !r.success).length;
|
|
320
|
+
if (succeeded > 0) {
|
|
321
|
+
console.log(`\n \x1b[32m${succeeded} installed successfully.\x1b[0m Restart your AI agent to load the skill.\n`);
|
|
322
|
+
}
|
|
323
|
+
if (failed > 0) {
|
|
324
|
+
console.log(` \x1b[31m${failed} failed.\x1b[0m\n`);
|
|
325
|
+
process.exit(1);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
// ─── Interactive Install ─────────────────────────────────────────
|
|
330
|
+
|
|
331
|
+
async function installInteractive(sourcePath, adaptersPath, isProject) {
|
|
332
|
+
const p = require('@clack/prompts');
|
|
333
|
+
const version = getVersion();
|
|
334
|
+
|
|
335
|
+
p.intro(`Prompt Architect v${version}`);
|
|
336
|
+
|
|
337
|
+
// Detect agents
|
|
338
|
+
const detected = AGENTS.filter(a => a.detect());
|
|
339
|
+
const notDetected = AGENTS.filter(a => !a.detect());
|
|
340
|
+
|
|
341
|
+
if (detected.length === 0) {
|
|
342
|
+
p.log.warn('No AI agents detected on your system.');
|
|
343
|
+
p.log.info('You can still install to any location using the flags below.');
|
|
344
|
+
showHelp();
|
|
345
|
+
p.outro('No installation performed.');
|
|
346
|
+
return;
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
const detectedNames = detected
|
|
350
|
+
.filter(a => !a.alwaysShow)
|
|
351
|
+
.map(a => a.name)
|
|
352
|
+
.join(', ');
|
|
353
|
+
if (detectedNames) {
|
|
354
|
+
p.log.info(`Detected: ${detectedNames}`);
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
// Build options — detected first (pre-selected), then others
|
|
358
|
+
const options = [];
|
|
359
|
+
|
|
360
|
+
for (const agent of detected) {
|
|
361
|
+
options.push({
|
|
362
|
+
value: agent.id,
|
|
363
|
+
label: agent.name,
|
|
364
|
+
hint: agent.hint(isProject),
|
|
365
|
+
});
|
|
366
|
+
}
|
|
367
|
+
|
|
368
|
+
for (const agent of notDetected) {
|
|
369
|
+
options.push({
|
|
370
|
+
value: agent.id,
|
|
371
|
+
label: `${agent.name} (not detected)`,
|
|
372
|
+
hint: agent.hint(isProject),
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
const initialValues = detected
|
|
377
|
+
.filter(a => !a.alwaysShow) // Don't pre-select "universal" since it's always available
|
|
378
|
+
.map(a => a.id);
|
|
379
|
+
|
|
380
|
+
const selected = await p.multiselect({
|
|
381
|
+
message: 'Select agents to install for:',
|
|
382
|
+
options,
|
|
383
|
+
initialValues,
|
|
384
|
+
required: true,
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
if (p.isCancel(selected)) {
|
|
388
|
+
p.cancel('Installation cancelled.');
|
|
389
|
+
process.exit(0);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
// Install to each selected agent
|
|
393
|
+
const s = p.spinner();
|
|
394
|
+
const results = [];
|
|
395
|
+
|
|
396
|
+
for (const agentId of selected) {
|
|
397
|
+
const agent = AGENTS.find(a => a.id === agentId);
|
|
398
|
+
if (!agent) continue;
|
|
399
|
+
|
|
400
|
+
const destPath = agent.installPath(isProject);
|
|
401
|
+
s.start(`Installing to ${agent.name}...`);
|
|
402
|
+
|
|
403
|
+
try {
|
|
404
|
+
if (agent.format === 'skill') {
|
|
405
|
+
installSkillDir(sourcePath, destPath);
|
|
406
|
+
} else {
|
|
407
|
+
installAdapter(adaptersPath, agent, destPath);
|
|
408
|
+
}
|
|
409
|
+
s.stop(`${agent.name} → ${destPath}`);
|
|
410
|
+
results.push({ agent: agent.name, success: true });
|
|
411
|
+
} catch (err) {
|
|
412
|
+
s.stop(`${agent.name}: ${err.message}`);
|
|
413
|
+
results.push({ agent: agent.name, success: false, error: err.message });
|
|
414
|
+
}
|
|
415
|
+
}
|
|
416
|
+
|
|
417
|
+
// Summary
|
|
418
|
+
const succeeded = results.filter(r => r.success).length;
|
|
419
|
+
const failed = results.filter(r => !r.success).length;
|
|
420
|
+
|
|
421
|
+
if (failed > 0) {
|
|
422
|
+
p.log.warn(`${failed} installation(s) failed.`);
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
if (succeeded > 0) {
|
|
426
|
+
p.outro(`${succeeded} agent(s) configured. Restart your AI agent to load the skill.`);
|
|
427
|
+
} else {
|
|
428
|
+
p.outro('No installations succeeded.');
|
|
429
|
+
process.exit(1);
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
433
|
+
// ─── Main ────────────────────────────────────────────────────────
|
|
434
|
+
|
|
435
|
+
async function main() {
|
|
436
|
+
const { flags, specificAgents } = parseArgs();
|
|
437
|
+
|
|
438
|
+
if (flags.help) {
|
|
439
|
+
showHelp();
|
|
440
|
+
process.exit(0);
|
|
441
|
+
}
|
|
442
|
+
|
|
443
|
+
// Find source files
|
|
444
|
+
const sourcePath = findSourcePath();
|
|
445
|
+
const adaptersPath = findAdaptersPath();
|
|
446
|
+
|
|
447
|
+
if (!sourcePath) {
|
|
448
|
+
console.error(`\n \x1b[31mError: Could not find ${SKILL_NAME} skill files.\x1b[0m`);
|
|
449
|
+
console.error(` Try reinstalling: npm install -g ${PACKAGE_NAME}\n`);
|
|
450
|
+
process.exit(1);
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
const isProject = flags.project;
|
|
454
|
+
const isInteractive = process.stdin.isTTY && process.stdout.isTTY && !process.env.CI;
|
|
455
|
+
|
|
456
|
+
// Mode 1: Specific agent flags provided
|
|
457
|
+
if (specificAgents.length > 0) {
|
|
458
|
+
installNonInteractive(specificAgents, sourcePath, adaptersPath, isProject);
|
|
459
|
+
return;
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
// Mode 2: --all flag
|
|
463
|
+
if (flags.all) {
|
|
464
|
+
const detected = AGENTS.filter(a => a.detect()).map(a => a.id);
|
|
465
|
+
installNonInteractive(detected, sourcePath, adaptersPath, isProject);
|
|
466
|
+
return;
|
|
467
|
+
}
|
|
468
|
+
|
|
469
|
+
// Mode 3: --yes flag (accept defaults = all detected, non-interactive)
|
|
470
|
+
if (flags.yes) {
|
|
471
|
+
const detected = AGENTS.filter(a => a.detect() && !a.alwaysShow).map(a => a.id);
|
|
472
|
+
installNonInteractive(detected.length > 0 ? detected : ['claude'], sourcePath, adaptersPath, isProject);
|
|
473
|
+
return;
|
|
474
|
+
}
|
|
475
|
+
|
|
476
|
+
// Mode 4: Interactive
|
|
477
|
+
if (isInteractive) {
|
|
478
|
+
await installInteractive(sourcePath, adaptersPath, isProject);
|
|
479
|
+
return;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
// Mode 5: Non-interactive fallback (postinstall, CI)
|
|
483
|
+
installNonInteractive(['claude'], sourcePath, adaptersPath, isProject);
|
|
484
|
+
console.log(` Run \x1b[36mnpx ${PACKAGE_NAME}\x1b[0m for interactive multi-agent setup.\n`);
|
|
485
|
+
}
|
|
486
|
+
|
|
487
|
+
main().catch(err => {
|
|
488
|
+
console.error(`\n \x1b[31mInstallation error: ${err.message}\x1b[0m\n`);
|
|
489
|
+
process.exit(1);
|
|
490
|
+
});
|
package/scripts/test.js
ADDED
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Test Script
|
|
5
|
+
*
|
|
6
|
+
* Runs basic tests to verify the skill package is properly configured.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const fs = require('fs');
|
|
10
|
+
const path = require('path');
|
|
11
|
+
|
|
12
|
+
const colors = {
|
|
13
|
+
reset: '\x1b[0m',
|
|
14
|
+
red: '\x1b[31m',
|
|
15
|
+
green: '\x1b[32m',
|
|
16
|
+
yellow: '\x1b[33m',
|
|
17
|
+
blue: '\x1b[34m',
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
function log(message, color = 'reset') {
|
|
21
|
+
console.log(`${colors[color]}${message}${colors.reset}`);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
log('\n🧪 Running tests for Prompt Architect skill...\n', 'blue');
|
|
25
|
+
|
|
26
|
+
let passed = 0;
|
|
27
|
+
let failed = 0;
|
|
28
|
+
|
|
29
|
+
function test(description, fn) {
|
|
30
|
+
try {
|
|
31
|
+
fn();
|
|
32
|
+
log(`✅ ${description}`, 'green');
|
|
33
|
+
passed++;
|
|
34
|
+
} catch (error) {
|
|
35
|
+
log(`❌ ${description}`, 'red');
|
|
36
|
+
log(` ${error.message}`, 'red');
|
|
37
|
+
failed++;
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// Tests
|
|
42
|
+
test('package.json exists', () => {
|
|
43
|
+
if (!fs.existsSync('package.json')) throw new Error('Not found');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
test('package.json is valid JSON', () => {
|
|
47
|
+
JSON.parse(fs.readFileSync('package.json', 'utf8'));
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
test('SKILL.md exists', () => {
|
|
51
|
+
if (!fs.existsSync('skills/prompt-architect/SKILL.md')) throw new Error('Not found');
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
test('LICENSE exists', () => {
|
|
55
|
+
if (!fs.existsSync('LICENSE')) throw new Error('Not found');
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test('README.md exists', () => {
|
|
59
|
+
if (!fs.existsSync('README.md')) throw new Error('Not found');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('CHANGELOG.md exists', () => {
|
|
63
|
+
if (!fs.existsSync('CHANGELOG.md')) throw new Error('Not found');
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
test('All framework files exist', () => {
|
|
67
|
+
const frameworks = [
|
|
68
|
+
'co-star.md',
|
|
69
|
+
'risen.md',
|
|
70
|
+
'rise.md',
|
|
71
|
+
'tidd-ec.md',
|
|
72
|
+
'rtf.md',
|
|
73
|
+
'chain-of-thought.md',
|
|
74
|
+
'chain-of-density.md',
|
|
75
|
+
];
|
|
76
|
+
frameworks.forEach(f => {
|
|
77
|
+
const fpath = path.join('skills', 'prompt-architect', 'references', 'frameworks', f);
|
|
78
|
+
if (!fs.existsSync(fpath)) {
|
|
79
|
+
throw new Error(`Framework ${f} not found`);
|
|
80
|
+
}
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('All templates exist', () => {
|
|
85
|
+
const templates = [
|
|
86
|
+
'co-star_template.txt',
|
|
87
|
+
'risen_template.txt',
|
|
88
|
+
'rise-ie_template.txt',
|
|
89
|
+
'rise-ix_template.txt',
|
|
90
|
+
'tidd-ec_template.txt',
|
|
91
|
+
'rtf_template.txt',
|
|
92
|
+
'hybrid_template.txt',
|
|
93
|
+
];
|
|
94
|
+
templates.forEach(t => {
|
|
95
|
+
const tpath = path.join('skills', 'prompt-architect', 'assets', 'templates', t);
|
|
96
|
+
if (!fs.existsSync(tpath)) {
|
|
97
|
+
throw new Error(`Template ${t} not found`);
|
|
98
|
+
}
|
|
99
|
+
});
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
test('Python scripts exist', () => {
|
|
103
|
+
const scripts = ['framework_analyzer.py', 'prompt_evaluator.py'];
|
|
104
|
+
scripts.forEach(s => {
|
|
105
|
+
const spath = path.join('skills', 'prompt-architect', 'scripts', s);
|
|
106
|
+
if (!fs.existsSync(spath)) {
|
|
107
|
+
throw new Error(`Script ${s} not found`);
|
|
108
|
+
}
|
|
109
|
+
});
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
test('package.json has required fields', () => {
|
|
113
|
+
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
|
|
114
|
+
const required = ['name', 'version', 'description', 'main', 'keywords', 'license'];
|
|
115
|
+
required.forEach(field => {
|
|
116
|
+
if (!pkg[field]) throw new Error(`Missing field: ${field}`);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test('package.json keywords include "claude-code"', () => {
|
|
121
|
+
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
|
|
122
|
+
if (!pkg.keywords || !pkg.keywords.includes('claude-code')) {
|
|
123
|
+
throw new Error('Missing "claude-code" keyword');
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
|
|
127
|
+
// Plugin manifest tests
|
|
128
|
+
test('.claude-plugin/plugin.json exists and is valid', () => {
|
|
129
|
+
const pluginPath = '.claude-plugin/plugin.json';
|
|
130
|
+
if (!fs.existsSync(pluginPath)) throw new Error('Not found');
|
|
131
|
+
const plugin = JSON.parse(fs.readFileSync(pluginPath, 'utf8'));
|
|
132
|
+
if (!plugin.name) throw new Error('Missing name');
|
|
133
|
+
if (!plugin.description) throw new Error('Missing description');
|
|
134
|
+
if (!plugin.version) throw new Error('Missing version');
|
|
135
|
+
});
|
|
136
|
+
|
|
137
|
+
test('.claude-plugin/marketplace.json exists and is valid', () => {
|
|
138
|
+
const mktPath = '.claude-plugin/marketplace.json';
|
|
139
|
+
if (!fs.existsSync(mktPath)) throw new Error('Not found');
|
|
140
|
+
const mkt = JSON.parse(fs.readFileSync(mktPath, 'utf8'));
|
|
141
|
+
if (!mkt.name) throw new Error('Missing name');
|
|
142
|
+
if (!mkt.owner) throw new Error('Missing owner');
|
|
143
|
+
if (!mkt.plugins || mkt.plugins.length === 0) throw new Error('No plugins defined');
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
test('plugin.json version matches package.json version', () => {
|
|
147
|
+
const pkg = JSON.parse(fs.readFileSync('package.json', 'utf8'));
|
|
148
|
+
const plugin = JSON.parse(fs.readFileSync('.claude-plugin/plugin.json', 'utf8'));
|
|
149
|
+
if (pkg.version !== plugin.version) {
|
|
150
|
+
throw new Error(`Version mismatch: package.json=${pkg.version}, plugin.json=${plugin.version}`);
|
|
151
|
+
}
|
|
152
|
+
});
|
|
153
|
+
|
|
154
|
+
// Adapter tests
|
|
155
|
+
test('Gemini CLI adapter exists', () => {
|
|
156
|
+
if (!fs.existsSync('adapters/for-gemini-cli.md')) throw new Error('Not found');
|
|
157
|
+
});
|
|
158
|
+
|
|
159
|
+
test('All adapter files exist', () => {
|
|
160
|
+
const adapters = [
|
|
161
|
+
'system-prompt.md',
|
|
162
|
+
'for-openai-codex-cli.md',
|
|
163
|
+
'for-cursor.mdc',
|
|
164
|
+
'for-github-copilot.md',
|
|
165
|
+
'for-windsurf.md',
|
|
166
|
+
'for-gemini-cli.md',
|
|
167
|
+
'README.md',
|
|
168
|
+
];
|
|
169
|
+
adapters.forEach(a => {
|
|
170
|
+
if (!fs.existsSync(path.join('adapters', a))) throw new Error(`Adapter ${a} not found`);
|
|
171
|
+
});
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
// Agent Skills compliance tests
|
|
175
|
+
test('SKILL.md has Agent Skills required fields', () => {
|
|
176
|
+
const content = fs.readFileSync('skills/prompt-architect/SKILL.md', 'utf8');
|
|
177
|
+
const fm = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
178
|
+
if (!fm) throw new Error('No frontmatter');
|
|
179
|
+
if (!fm[1].includes('name:')) throw new Error('Missing name');
|
|
180
|
+
if (!fm[1].includes('description:')) throw new Error('Missing description');
|
|
181
|
+
});
|
|
182
|
+
|
|
183
|
+
test('SKILL.md has optional Agent Skills fields', () => {
|
|
184
|
+
const content = fs.readFileSync('skills/prompt-architect/SKILL.md', 'utf8');
|
|
185
|
+
const fm = content.match(/^---\r?\n([\s\S]*?)\r?\n---/);
|
|
186
|
+
if (!fm) throw new Error('No frontmatter');
|
|
187
|
+
if (!fm[1].includes('license:')) throw new Error('Missing license');
|
|
188
|
+
if (!fm[1].includes('compatibility:')) throw new Error('Missing compatibility');
|
|
189
|
+
if (!fm[1].includes('metadata:')) throw new Error('Missing metadata');
|
|
190
|
+
});
|
|
191
|
+
|
|
192
|
+
test('SKILL.md name matches directory name', () => {
|
|
193
|
+
const content = fs.readFileSync('skills/prompt-architect/SKILL.md', 'utf8');
|
|
194
|
+
const nameMatch = content.match(/name:\s*([^\n\r]+)/);
|
|
195
|
+
if (!nameMatch) throw new Error('No name field');
|
|
196
|
+
if (nameMatch[1].trim() !== 'prompt-architect') {
|
|
197
|
+
throw new Error(`Name "${nameMatch[1].trim()}" does not match directory "prompt-architect"`);
|
|
198
|
+
}
|
|
199
|
+
});
|
|
200
|
+
|
|
201
|
+
// Summary
|
|
202
|
+
log('\n' + '='.repeat(50), 'blue');
|
|
203
|
+
log(`Tests completed: ${passed + failed} total`, 'blue');
|
|
204
|
+
log(`✅ Passed: ${passed}`, 'green');
|
|
205
|
+
if (failed > 0) {
|
|
206
|
+
log(`❌ Failed: ${failed}`, 'red');
|
|
207
|
+
log('='.repeat(50) + '\n', 'blue');
|
|
208
|
+
process.exit(1);
|
|
209
|
+
} else {
|
|
210
|
+
log('='.repeat(50), 'blue');
|
|
211
|
+
log('\n✨ All tests passed!\n', 'green');
|
|
212
|
+
process.exit(0);
|
|
213
|
+
}
|