@mohammadhprp/system-prompt 0.13.0 → 0.13.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/bin/system-prompt.js +1 -1
- package/framework/skills/README.md +1 -0
- package/framework/skills/grilling/SKILL.md +34 -0
- package/framework/skills/grilling/examples.md +47 -0
- package/package.json +1 -1
- package/src/catalog.js +13 -0
- package/src/cli/args.js +51 -0
- package/src/cli/clack-ui.js +13 -0
- package/src/cli/index.js +47 -0
- package/src/cli/interactive.js +213 -0
- package/src/cli/plan.js +85 -0
- package/src/cli/tui-preferences.js +118 -0
- package/src/config/mcp.js +25 -0
- package/src/config/opencode.js +47 -0
- package/src/config/tui.js +92 -0
- package/src/doctor.js +23 -24
- package/src/hash.js +5 -0
- package/src/install/env.js +69 -0
- package/src/install/files.js +151 -0
- package/src/install/index.js +124 -0
- package/src/install/lock.js +159 -0
- package/src/install/merge.js +62 -0
- package/src/install/templates.js +76 -0
- package/src/item-layout.js +31 -0
- package/src/paths.js +59 -0
- package/src/ui.js +32 -0
- package/src/agent-configs.js +0 -100
- package/src/cli.js +0 -334
- package/src/installer.js +0 -672
package/src/cli.js
DELETED
|
@@ -1,334 +0,0 @@
|
|
|
1
|
-
import { intro, outro, confirm, multiselect, spinner, isCancel } from '@clack/prompts';
|
|
2
|
-
import { resolve } from 'node:path';
|
|
3
|
-
|
|
4
|
-
import { categories } from './catalog.js';
|
|
5
|
-
import { getPackageVersion, install, loadLockFile, lockToSelections } from './installer.js';
|
|
6
|
-
import { doctor } from './doctor.js';
|
|
7
|
-
|
|
8
|
-
const CATEGORY_FLAGS = new Set(Object.keys(categories));
|
|
9
|
-
|
|
10
|
-
export function parseArgs(argv) {
|
|
11
|
-
const options = {
|
|
12
|
-
targetDir: '.opencode',
|
|
13
|
-
selections: {},
|
|
14
|
-
all: false,
|
|
15
|
-
dryRun: false,
|
|
16
|
-
force: false,
|
|
17
|
-
doctor: false,
|
|
18
|
-
includeAgentsMd: true,
|
|
19
|
-
};
|
|
20
|
-
for (let index = 0; index < argv.length; index += 1) {
|
|
21
|
-
const arg = argv[index];
|
|
22
|
-
if (arg === '--all') options.all = true;
|
|
23
|
-
else if (arg === '--dry-run') options.dryRun = true;
|
|
24
|
-
else if (arg === '--force') options.force = true;
|
|
25
|
-
else if (arg === '--doctor') options.doctor = true;
|
|
26
|
-
else if (arg === '--no-agents-md') options.includeAgentsMd = false;
|
|
27
|
-
else if (arg === '--target') {
|
|
28
|
-
const value = argv[++index];
|
|
29
|
-
if (!value || value.startsWith('--')) throw new Error('Missing value for --target');
|
|
30
|
-
options.targetDir = value;
|
|
31
|
-
}
|
|
32
|
-
else if (arg.startsWith('--target=')) {
|
|
33
|
-
const value = arg.slice('--target='.length);
|
|
34
|
-
if (!value) throw new Error('Missing value for --target');
|
|
35
|
-
options.targetDir = value;
|
|
36
|
-
}
|
|
37
|
-
else if (arg.startsWith('--')) {
|
|
38
|
-
const [flag, inlineValue] = arg.slice(2).split('=', 2);
|
|
39
|
-
if (!CATEGORY_FLAGS.has(flag)) throw new Error(`Unknown option: ${arg}`);
|
|
40
|
-
const value = inlineValue ?? argv[++index];
|
|
41
|
-
if (!value) throw new Error(`Missing value for --${flag}`);
|
|
42
|
-
options.selections[flag] = value.split(',').filter(Boolean);
|
|
43
|
-
} else {
|
|
44
|
-
throw new Error(`Unknown argument: ${arg}`);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
options.nonInteractive = options.all || Object.keys(options.selections).length > 0;
|
|
48
|
-
return options;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function allSelections() {
|
|
52
|
-
return Object.fromEntries(Object.entries(categories).map(([category, config]) => [
|
|
53
|
-
category,
|
|
54
|
-
config.items.filter(item => !item.removed).map(item => item.id),
|
|
55
|
-
]));
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
function buildSummary(selections) {
|
|
59
|
-
const lines = [];
|
|
60
|
-
for (const [cat, ids] of Object.entries(selections)) {
|
|
61
|
-
if (!ids?.length) continue;
|
|
62
|
-
const catConfig = categories[cat];
|
|
63
|
-
const label = catConfig?.title || cat;
|
|
64
|
-
const names = ids.map(id => {
|
|
65
|
-
const item = catConfig?.items.find(i => i.id === id);
|
|
66
|
-
return item ? item.name : id;
|
|
67
|
-
});
|
|
68
|
-
lines.push(` ${label}: ${names.join(', ')}`);
|
|
69
|
-
}
|
|
70
|
-
return lines.join('\n');
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function computeDiff(oldLock, selections) {
|
|
74
|
-
const oldSels = lockToSelections(oldLock);
|
|
75
|
-
const added = {};
|
|
76
|
-
const removed = {};
|
|
77
|
-
const kept = {};
|
|
78
|
-
|
|
79
|
-
const allCats = [...new Set([...Object.keys(oldSels), ...Object.keys(selections)])];
|
|
80
|
-
|
|
81
|
-
for (const cat of allCats) {
|
|
82
|
-
const catConfig = categories[cat] || null;
|
|
83
|
-
const oldIds = new Set(oldSels[cat] || []);
|
|
84
|
-
const newIds = new Set(selections[cat] || []);
|
|
85
|
-
|
|
86
|
-
const addedIds = [...newIds].filter(id => !oldIds.has(id));
|
|
87
|
-
const removedIds = [...oldIds].filter(id => !newIds.has(id));
|
|
88
|
-
const keptIds = [...newIds].filter(id => oldIds.has(id));
|
|
89
|
-
|
|
90
|
-
if (addedIds.length) added[cat] = { config: catConfig, ids: addedIds };
|
|
91
|
-
if (removedIds.length) removed[cat] = { config: catConfig, ids: removedIds };
|
|
92
|
-
if (keptIds.length) kept[cat] = { config: catConfig, ids: keptIds };
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
return { added, removed, kept };
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
function formatDiff(diff) {
|
|
99
|
-
const { added, removed, kept } = diff;
|
|
100
|
-
const lines = [];
|
|
101
|
-
|
|
102
|
-
if (Object.keys(added).length) {
|
|
103
|
-
lines.push(' + Added:');
|
|
104
|
-
for (const [cat, data] of Object.entries(added)) {
|
|
105
|
-
const names = data.ids.map(id => {
|
|
106
|
-
const item = data.config?.items?.find(i => i.id === id);
|
|
107
|
-
return item?.name || id;
|
|
108
|
-
});
|
|
109
|
-
lines.push(` ${data.config?.title || cat}: ${names.join(', ')}`);
|
|
110
|
-
}
|
|
111
|
-
}
|
|
112
|
-
|
|
113
|
-
if (Object.keys(removed).length) {
|
|
114
|
-
if (lines.length) lines.push('');
|
|
115
|
-
lines.push(' - Removed:');
|
|
116
|
-
for (const [cat, data] of Object.entries(removed)) {
|
|
117
|
-
const names = data.ids.map(id => {
|
|
118
|
-
const item = data.config?.items?.find(i => i.id === id);
|
|
119
|
-
return item?.name || id;
|
|
120
|
-
});
|
|
121
|
-
lines.push(` ${data.config?.title || cat}: ${names.join(', ')}`);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
if (Object.keys(kept).length) {
|
|
126
|
-
if (lines.length) lines.push('');
|
|
127
|
-
lines.push(' ~ Unchanged:');
|
|
128
|
-
for (const [cat, data] of Object.entries(kept)) {
|
|
129
|
-
const names = data.ids.map(id => {
|
|
130
|
-
const item = data.config?.items?.find(i => i.id === id);
|
|
131
|
-
return item?.name || id;
|
|
132
|
-
});
|
|
133
|
-
lines.push(` ${data.config?.title || cat}: ${names.join(', ')}`);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
return lines.join('\n');
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
export async function main(argv = process.argv.slice(2)) {
|
|
141
|
-
const args = parseArgs(argv);
|
|
142
|
-
if (args.doctor) {
|
|
143
|
-
const healthy = await doctor(args.targetDir);
|
|
144
|
-
if (!healthy) process.exitCode = 1;
|
|
145
|
-
return healthy;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
const version = await getPackageVersion();
|
|
149
|
-
intro(`System prompt (v${version})`);
|
|
150
|
-
|
|
151
|
-
const agentType = 'opencode';
|
|
152
|
-
const targetDir = args.targetDir;
|
|
153
|
-
|
|
154
|
-
if (args.nonInteractive) {
|
|
155
|
-
const selections = args.all ? allSelections() : args.selections;
|
|
156
|
-
const absTarget = resolve(process.cwd(), targetDir);
|
|
157
|
-
const oldLock = await loadLockFile(absTarget);
|
|
158
|
-
if (!args.dryRun) console.log(`Installing selected components into ${absTarget}`);
|
|
159
|
-
await install({
|
|
160
|
-
targetDir,
|
|
161
|
-
agentType,
|
|
162
|
-
selections,
|
|
163
|
-
includeAgentsMd: args.includeAgentsMd,
|
|
164
|
-
oldSelections: oldLock ? lockToSelections(oldLock) : undefined,
|
|
165
|
-
oldLock,
|
|
166
|
-
force: args.force,
|
|
167
|
-
dryRun: args.dryRun,
|
|
168
|
-
});
|
|
169
|
-
console.log(args.dryRun ? 'Dry run complete.' : 'Installation complete.');
|
|
170
|
-
return;
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const categoryOptions = Object.entries(categories).map(([key, cat]) => {
|
|
174
|
-
const visible = cat.items.filter(i => !i.removed);
|
|
175
|
-
return {
|
|
176
|
-
value: key,
|
|
177
|
-
label: cat.title,
|
|
178
|
-
hint: `${visible.length} ${key === 'mcps' ? 'MCPs' : cat.title.toLowerCase()}`,
|
|
179
|
-
};
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
const selectedCategories = await multiselect({
|
|
183
|
-
message: 'What would you like to install?',
|
|
184
|
-
options: categoryOptions,
|
|
185
|
-
required: false,
|
|
186
|
-
});
|
|
187
|
-
if (isCancel(selectedCategories)) {
|
|
188
|
-
outro('Cancelled.');
|
|
189
|
-
process.exit(0);
|
|
190
|
-
}
|
|
191
|
-
|
|
192
|
-
if (!selectedCategories?.length) {
|
|
193
|
-
const includeAgentsMd = await confirm({
|
|
194
|
-
message: 'Generate AGENTS.md?',
|
|
195
|
-
initialValue: true,
|
|
196
|
-
});
|
|
197
|
-
if (isCancel(includeAgentsMd)) {
|
|
198
|
-
outro('Cancelled.');
|
|
199
|
-
process.exit(0);
|
|
200
|
-
}
|
|
201
|
-
|
|
202
|
-
const s = spinner();
|
|
203
|
-
s.start('Writing files...');
|
|
204
|
-
await install({
|
|
205
|
-
targetDir,
|
|
206
|
-
agentType,
|
|
207
|
-
selections: {},
|
|
208
|
-
includeAgentsMd,
|
|
209
|
-
});
|
|
210
|
-
s.stop('Done.');
|
|
211
|
-
const installed = [];
|
|
212
|
-
if (includeAgentsMd) installed.push('AGENTS.md');
|
|
213
|
-
outro(`${installed.join(' and ')} written. Open them in your project to get started.`);
|
|
214
|
-
process.exit(0);
|
|
215
|
-
}
|
|
216
|
-
|
|
217
|
-
const selections = {};
|
|
218
|
-
|
|
219
|
-
const includeAgentsMd = await confirm({
|
|
220
|
-
message: 'Generate AGENTS.md?',
|
|
221
|
-
initialValue: true,
|
|
222
|
-
});
|
|
223
|
-
if (isCancel(includeAgentsMd)) {
|
|
224
|
-
outro('Cancelled.');
|
|
225
|
-
process.exit(0);
|
|
226
|
-
}
|
|
227
|
-
|
|
228
|
-
for (const cat of selectedCategories) {
|
|
229
|
-
const catConfig = categories[cat];
|
|
230
|
-
const allIds = catConfig.items.map(i => i.id);
|
|
231
|
-
|
|
232
|
-
const visibleItems = catConfig.items.filter(i => !i.removed);
|
|
233
|
-
|
|
234
|
-
const all = await confirm({
|
|
235
|
-
message: `Install all ${catConfig.title.toLowerCase()}?`,
|
|
236
|
-
initialValue: visibleItems.length <= 12,
|
|
237
|
-
});
|
|
238
|
-
if (isCancel(all)) {
|
|
239
|
-
outro('Cancelled.');
|
|
240
|
-
process.exit(0);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
if (all) {
|
|
244
|
-
selections[cat] = visibleItems.map(i => i.id);
|
|
245
|
-
} else {
|
|
246
|
-
const picked = await multiselect({
|
|
247
|
-
message: `Which ${catConfig.title.toLowerCase()} do you want?`,
|
|
248
|
-
options: visibleItems.map(item => ({
|
|
249
|
-
value: item.id,
|
|
250
|
-
label: item.deprecated ? `${item.name} (deprecated)` : item.name,
|
|
251
|
-
hint: item.deprecated ? '⚠ Deprecated — consider alternatives' : item.description,
|
|
252
|
-
})),
|
|
253
|
-
required: true,
|
|
254
|
-
});
|
|
255
|
-
if (isCancel(picked)) {
|
|
256
|
-
outro('Cancelled.');
|
|
257
|
-
process.exit(0);
|
|
258
|
-
}
|
|
259
|
-
selections[cat] = picked;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
|
|
263
|
-
const absTarget = resolve(process.cwd(), targetDir);
|
|
264
|
-
const oldLock = await loadLockFile(absTarget);
|
|
265
|
-
|
|
266
|
-
if (oldLock) {
|
|
267
|
-
const diff = computeDiff(oldLock, selections);
|
|
268
|
-
const diffText = formatDiff(diff);
|
|
269
|
-
|
|
270
|
-
console.log('\n📦 Changes from previous installation:\n');
|
|
271
|
-
if (diffText) {
|
|
272
|
-
console.log(diffText);
|
|
273
|
-
console.log();
|
|
274
|
-
} else {
|
|
275
|
-
console.log(' No changes — same selections as before.\n');
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
const genFiles = [];
|
|
279
|
-
if (includeAgentsMd) genFiles.push('AGENTS.md');
|
|
280
|
-
genFiles.push('opencode.json', 'tui.json', '.gitignore');
|
|
281
|
-
if (selections.mcps?.length) genFiles.push('.env');
|
|
282
|
-
if (selections.memory?.length) genFiles.push('memory/');
|
|
283
|
-
|
|
284
|
-
if (genFiles.length) {
|
|
285
|
-
console.log(' Generated files:');
|
|
286
|
-
for (const f of genFiles) {
|
|
287
|
-
console.log(` 📄 ${f}`);
|
|
288
|
-
}
|
|
289
|
-
console.log();
|
|
290
|
-
}
|
|
291
|
-
} else {
|
|
292
|
-
console.log('\n📦 Summary of what will be installed:\n');
|
|
293
|
-
if (includeAgentsMd) console.log(' 📄 AGENTS.md');
|
|
294
|
-
console.log(' 📄 opencode.json');
|
|
295
|
-
console.log(' 📄 tui.json');
|
|
296
|
-
console.log(' 📄 .gitignore');
|
|
297
|
-
console.log(buildSummary(selections));
|
|
298
|
-
console.log();
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
const confirmed = await confirm({
|
|
302
|
-
message: 'Proceed with installation?',
|
|
303
|
-
initialValue: true,
|
|
304
|
-
});
|
|
305
|
-
if (isCancel(confirmed) || !confirmed) {
|
|
306
|
-
outro('Installation cancelled.');
|
|
307
|
-
process.exit(0);
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
const s = spinner();
|
|
311
|
-
s.start(oldLock ? 'Updating files...' : 'Installing files...');
|
|
312
|
-
|
|
313
|
-
const finalTarget = await install({
|
|
314
|
-
targetDir,
|
|
315
|
-
agentType,
|
|
316
|
-
selections,
|
|
317
|
-
includeAgentsMd,
|
|
318
|
-
oldSelections: oldLock ? lockToSelections(oldLock) : undefined,
|
|
319
|
-
oldLock,
|
|
320
|
-
force: args.force,
|
|
321
|
-
dryRun: args.dryRun,
|
|
322
|
-
});
|
|
323
|
-
|
|
324
|
-
s.stop('Installation complete!');
|
|
325
|
-
|
|
326
|
-
const fileCount = Object.values(selections).reduce((sum, arr) => sum + (arr?.length || 0), 0);
|
|
327
|
-
const verb = oldLock ? 'Updated' : 'Installed';
|
|
328
|
-
outro(`${verb} ${fileCount} components to ${finalTarget}
|
|
329
|
-
|
|
330
|
-
Next steps:
|
|
331
|
-
${agentType === 'opencode' ? '- Open your project in OpenCode — it will read opencode.json and AGENTS.md automatically' : '- Point your AI coding agent to AGENTS.md as the entry point'}
|
|
332
|
-
- Run /help in your agent to see available commands
|
|
333
|
-
`);
|
|
334
|
-
}
|