Skip to content

fix: validate skills new --price as non-negative integer (#457)#468

Open
nikos1005 wants to merge 1 commit into
profullstack:masterfrom
nikos1005:fix/skills-validate-price
Open

fix: validate skills new --price as non-negative integer (#457)#468
nikos1005 wants to merge 1 commit into
profullstack:masterfrom
nikos1005:fix/skills-validate-price

Conversation

@nikos1005
Copy link
Copy Markdown

Summary

Fixes sh1pt skills new --price accepting invalid values.

Problem

  • Negative values (e.g. --price -5) were silently persisted to sh1pt.skill.json
  • Fractional values (e.g. --price 1.9) were silently truncated by Number.parseInt()

Fix

Added validatePrice() function that uses /^\\d+$/ regex to ensure the price is a non-negative integer string before parsing.

Fixes #457

…k#457)

Reject negative values and silently-truncated floats instead of persisting them to sh1pt.skill.json.
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps Bot commented May 29, 2026

Greptile Summary

This PR fixes skills new --price accepting negative numbers and silently-truncated decimals by introducing a validatePrice() helper that applies a /^\d+$/ regex before parsing — ensuring only non-negative integer strings are accepted.

  • validatePrice(raw) trims whitespace, tests the digit-only regex, and throws a descriptive error on any invalid input, replacing the previous Number.parseInt(opts.price, 10) || 0 calls that silently coerced bad values.
  • The validator is called twice (once for manifest.price, once inside the marketplaces builder); extracting the result to a variable before the manifest literal would clean this up.
  • The error message echoes raw twice — the trailing Got "${raw}" is redundant with the opening Invalid --price "${raw}", and could instead reference trimmed to add new information.

Confidence Score: 4/5

Safe to merge — the fix correctly prevents negative and fractional prices from being written to the manifest, and the default value of '0' passes validation without issue.

The validation logic is sound: the /^\d+$/ regex reliably rejects negatives, decimals, and non-numeric input, and parseInt on a digits-only string is always safe. The only remaining rough edges are a redundant phrase in the error message and calling the validator twice rather than capturing the result in a variable — neither affects correctness.

Only packages/cli/src/commands/skills.ts changed; the double invocation of validatePrice on lines 367 and 370 is worth a second look.

Important Files Changed

Filename Overview
packages/cli/src/commands/skills.ts Adds validatePrice() using /^\d+$/ regex to reject negatives and decimals; replaces two parseInt calls. Two minor style issues: redundant value in error message and double invocation of the validator.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["skills new --price value"] --> B["opts.price as string"]
    B --> C["validatePrice(raw)"]
    C --> D{"trimmed matches digit-only regex?"}
    D -- No --> E["throw Error: Invalid --price"]
    D -- Yes --> F["parseInt(trimmed, 10)"]
    F --> G["manifest.price = number"]
    F --> H["marketplaces builder price = number"]
    G --> I["saveManifest to sh1pt.skill.json"]
    H --> I
Loading

Reviews (1): Last reviewed commit: "fix: validate skills new --price as non-..." | Re-trigger Greptile

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!

Comment on lines +367 to +370
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'],
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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

skills new accepts invalid listing prices

1 participant