Skip to content
Open
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 10 additions & 2 deletions packages/cli/src/commands/skills.ts
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,14 @@ const SKILL_TARGETS = {
function slugify(s: string): string {
return s.toLowerCase().replace(/[^a-z0-9]+/g, '-').replace(/^-|-$/g, '').slice(0, 64) || 'my-skill';
}
function validatePrice(raw: string): number {
const trimmed = raw.trim();
if (!/^\d+$/.test(trimmed)) {
throw new Error(`Invalid --price "${raw}": must be a non-negative integer (sats). Got "${raw}".`);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Redundant value echo in error message

raw appears twice in the same message — once as "${raw}" after Invalid --price and again as Got "${raw}". The second occurrence adds no new information. If the intent is to distinguish the original input from the trimmed value, the second slot should reference trimmed; otherwise, drop the trailing Got "${raw}"..

Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!

}
return parseInt(trimmed, 10);
}

function q(s: string): string { return JSON.stringify(s); }
async function exists(path: string): Promise<boolean> { try { await access(path); return true; } catch { return false; } }
function frontmatterValue(text: string, key: string): string | undefined {
Expand Down Expand Up @@ -356,10 +364,10 @@ skillsCmd
tagline: opts.tagline,
category: opts.category,
tags: opts.tags.split(',').map(t => t.trim()).filter(Boolean).slice(0, 10),
price: Number.parseInt(opts.price, 10) || 0,
price: validatePrice(opts.price),
skillFile,
sourceUrl: opts.sourceUrl,
marketplaces: Object.fromEntries(MARKETPLACES.map(mp => [mp.id, { enabled: true, status: 'pending', command: 'command' in mp && mp.command ? mp.command({ name, title, description, tagline: opts.tagline, category: opts.category, tags: opts.tags.split(',').map(t => t.trim()).filter(Boolean).slice(0, 10), price: Number.parseInt(opts.price, 10) || 0, skillFile, sourceUrl: opts.sourceUrl, marketplaces: {} }) : undefined, note: 'note' in mp ? mp.note : undefined }])) as SkillManifest['marketplaces'],
marketplaces: Object.fromEntries(MARKETPLACES.map(mp => [mp.id, { enabled: true, status: 'pending', command: 'command' in mp && mp.command ? mp.command({ name, title, description, tagline: opts.tagline, category: opts.category, tags: opts.tags.split(',').map(t => t.trim()).filter(Boolean).slice(0, 10), price: validatePrice(opts.price), skillFile, sourceUrl: opts.sourceUrl, marketplaces: {} }) : undefined, note: 'note' in mp ? mp.note : undefined }])) as SkillManifest['marketplaces'],
Comment on lines +367 to +370
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 validatePrice called twice for the same input

validatePrice(opts.price) is invoked once for manifest.price (line 367) and again inside the marketplaces builder (line 370). While both calls return the same value and neither has side effects, calling a throwing validator twice is unexpected. Extracting the result into a const price = validatePrice(opts.price) before the manifest literal would make the intent clearer and avoid the double call.

};
await mkdir(dirname(resolve(opts.out)), { recursive: true });
await saveManifest(opts.out, manifest);
Expand Down