skill-check 0.1.0 → 0.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/README.md CHANGED
@@ -5,11 +5,12 @@ Linter for agent skill files — validates SKILL.md files against the spec with
5
5
  ![skill-check demo](docs/assets/skill-check-demo.gif)
6
6
 
7
7
  Regenerate with `pnpm run demo:readme` (source: `scripts/readme-demo.tape`).
8
+ The demo runs `npx skill-check .` from repo root and auto-detects skills in tool directories such as `.agents`.
8
9
 
9
10
  ## Install
10
11
 
11
12
  ```bash
12
- npx skill-check check .
13
+ npx skill-check .
13
14
  ```
14
15
 
15
16
  Global install via curl:
@@ -29,6 +30,7 @@ brew install skill-check
29
30
 
30
31
  | Command | Description |
31
32
  |---|---|
33
+ | `skill-check [path]` | Shorthand for `skill-check check [path]` |
32
34
  | `skill-check check [path]` | Run validation (and optional security scan) |
33
35
  | `skill-check new <name>` | Scaffold a new skill directory with SKILL.md template |
34
36
  | `skill-check watch [path]` | Watch for changes and re-run validation on save |
@@ -64,7 +66,7 @@ brew install skill-check
64
66
 
65
67
  **HTML reports** are written to `skill-check-report.html` (or `output.reportPath`). In an interactive terminal the report opens in your browser automatically; use `--no-open` to skip.
66
68
 
67
- **View locally:** `npx skill-check check . --format html` or open the file directly: `open skill-check-report.html` (macOS).
69
+ **View locally:** `npx skill-check . --format html` or open the file directly: `open skill-check-report.html` (macOS).
68
70
 
69
71
  The `text` formatter includes quality score bars per skill, colorized severity badges, and boxed summaries.
70
72
  An ASCII CLI banner is shown in interactive text mode; set `SKILL_CHECK_NO_BANNER=1` to disable it.
@@ -131,18 +133,18 @@ npx skill-check init --interactive
131
133
  `skill-check` can validate repos or direct skills directories:
132
134
 
133
135
  ```bash
134
- npx skill-check check /path/to/repo
136
+ npx skill-check /path/to/repo
135
137
  npx skill-check check ~/.claude/skills
136
138
  ```
137
139
 
138
140
  `check` runs the security scan by default.
139
- If dependencies are missing, `skill-check` asks before installing in interactive terminals.
140
- In non-interactive/CI environments, use `--allow-installs` to permit automatic installs.
141
+ If dependencies are missing, `skill-check` automatically installs scanner dependencies by default.
142
+ Use `--no-installs` to hard-block automatic installs.
141
143
 
142
144
  Run security scan without UV by forcing `pipx`:
143
145
 
144
146
  ```bash
145
- npx skill-check security-scan . --security-scan-runner pipx --allow-installs
147
+ npx skill-check security-scan . --security-scan-runner pipx
146
148
  ```
147
149
 
148
150
  Run validation + security scan in one pipeline step with explicit runner:
@@ -346,7 +348,7 @@ All rules emit actionable `suggestion` text to guide fixes.
346
348
  Releases are automated with [semantic-release](https://github.com/semantic-release/semantic-release). Pushing to `main` (after CI passes) runs the release workflow: commits are analyzed for [Conventional Commits](https://www.conventionalcommits.org/) (`fix:`, `feat:`, `BREAKING CHANGE:`), the version is bumped, `CHANGELOG.md` is updated, the package is published to npm, and a GitHub release is created.
347
349
 
348
350
  - **Commit messages** are validated locally by [commitlint](https://commitlint.js.org/) (enforced by the `commit-msg` hook). Use `fix:`, `feat:`, `docs:`, `chore:`, etc.
349
- - **Secrets:** In GitHub, set the `NPM_TOKEN` repository secret (npm automation token with publish scope) so the workflow can publish to npm. `GITHUB_TOKEN` is provided automatically.
351
+ - **npm auth:** Use [npm Trusted Publishing (OIDC)](https://docs.npmjs.com/trusted-publishers) so you don’t need `NPM_TOKEN`. On [npmjs.com](https://www.npmjs.com/) go to the **skill-check** package → **Settings** → **Trusted publishing** → add a GitHub Actions publisher with workflow filename **`publish.yml`** (exact name, including extension). Then the workflow can publish without any npm token. Alternatively, set the `NPM_TOKEN` repository secret for token-based publish.
350
352
 
351
353
  To simulate a release locally (without publishing): `pnpm run release:dry-run`. It will fail `verifyConditions` without `NPM_TOKEN` and `GITHUB_TOKEN`; in CI both are set.
352
354
 
package/dist/cli/main.js CHANGED
@@ -26,6 +26,18 @@ const defaultIO = {
26
26
  stdout: (text) => process.stdout.write(text),
27
27
  stderr: (text) => process.stderr.write(text),
28
28
  };
29
+ const ROOT_COMMANDS = new Set([
30
+ 'check',
31
+ 'report',
32
+ 'security-scan',
33
+ 'rules',
34
+ 'new',
35
+ 'diff',
36
+ 'watch',
37
+ 'init',
38
+ 'help',
39
+ 'version',
40
+ ]);
29
41
  function collectList(value, previous) {
30
42
  const parsed = value
31
43
  .split(',')
@@ -89,6 +101,16 @@ function parseCommaSeparated(value) {
89
101
  .map((entry) => entry.trim())
90
102
  .filter(Boolean);
91
103
  }
104
+ function normalizeRootCommandArgs(argv) {
105
+ if (argv.length === 0) {
106
+ return argv;
107
+ }
108
+ const [first] = argv;
109
+ if (!first || first.startsWith('-') || ROOT_COMMANDS.has(first)) {
110
+ return argv;
111
+ }
112
+ return ['check', ...argv];
113
+ }
92
114
  function normalizeCheckCommandOptions(raw) {
93
115
  return {
94
116
  fix: raw.fix === true,
@@ -190,7 +212,7 @@ function normalizeAgentScanOptions(raw) {
190
212
  mode,
191
213
  paths: selectedPaths,
192
214
  skills: selectedSkills,
193
- installPolicy: denyInstalls ? 'deny' : allowInstalls ? 'allow' : 'ask',
215
+ installPolicy: denyInstalls ? 'deny' : 'allow',
194
216
  };
195
217
  }
196
218
  function shouldUseInteractiveUi(io) {
@@ -236,34 +258,13 @@ function renderAutoFixSummary(summary) {
236
258
  }
237
259
  return `${lines.join('\n')}\n\n`;
238
260
  }
239
- function formatInvocation(invocation) {
240
- return [invocation.command, ...invocation.args]
241
- .map((part) => (part.includes(' ') ? `"${part}"` : part))
242
- .join(' ');
243
- }
244
- async function ensureSecurityScanInstallConsent(scanOptions, invocation, io, format) {
261
+ async function ensureSecurityScanInstallConsent(scanOptions, invocation) {
245
262
  const mayInstallDependency = invocation.command !== 'mcp-scan' && !isCommandAvailable('mcp-scan');
246
263
  if (!mayInstallDependency)
247
264
  return;
248
265
  if (scanOptions.installPolicy === 'allow')
249
266
  return;
250
- const guidance = 'Re-run with --allow-installs, install mcp-scan manually, or skip scan with --no-security-scan.';
251
- if (scanOptions.installPolicy === 'deny') {
252
- throw new CliError(`Security scan may install dependencies via ${invocation.command}. ${guidance}`, 2);
253
- }
254
- const interactivePromptAllowed = shouldUseInteractiveUi(io) &&
255
- format === 'text' &&
256
- !isTruthyFlag(process.env.CI);
257
- if (!interactivePromptAllowed) {
258
- throw new CliError(`Security scan may install dependencies via ${invocation.command} but interactive approval is unavailable. ${guidance}`, 2);
259
- }
260
- const accepted = await confirm({
261
- message: `Security scan may install "mcp-scan" with: ${formatInvocation(invocation)}. Continue?`,
262
- initialValue: false,
263
- });
264
- if (isCancel(accepted) || !accepted) {
265
- throw new CliError('Security scan install was declined. Re-run with --no-security-scan or --allow-installs.', 2);
266
- }
267
+ throw new CliError(`Security scan may install dependencies via ${invocation.command}. Re-run without --no-installs, install mcp-scan manually, or skip scan with --no-security-scan.`, 2);
267
268
  }
268
269
  async function runValidationPipeline(cwd, config, interactiveUi) {
269
270
  const useTaskUi = interactiveUi && config.output.format === 'text';
@@ -299,7 +300,7 @@ async function runAgentScanWithFeedback(scanOptions, target, io, format) {
299
300
  if (format === 'text') {
300
301
  io.stdout(`${pc.dim('Security scan engine:')} ${pc.bold('agent-scan (mcp-scan)')} ${pc.dim('via')} ${pc.cyan(invocation.command)}\n`);
301
302
  }
302
- await ensureSecurityScanInstallConsent(scanOptions, invocation, io, format);
303
+ await ensureSecurityScanInstallConsent(scanOptions, invocation);
303
304
  if (useInteractiveFeedback) {
304
305
  ora().info('Running security scan...');
305
306
  }
@@ -696,7 +697,8 @@ export async function runCli(argv, io = defaultIO) {
696
697
  finalExitCode = 0;
697
698
  });
698
699
  try {
699
- await program.parseAsync(argv, { from: 'user' });
700
+ const normalizedArgv = normalizeRootCommandArgs(argv);
701
+ await program.parseAsync(normalizedArgv, { from: 'user' });
700
702
  return finalExitCode;
701
703
  }
702
704
  catch (error) {
@@ -6,6 +6,7 @@ export const DEFAULT_EXCLUDE = [
6
6
  '**/build/**',
7
7
  '**/.next/**',
8
8
  '**/coverage/**',
9
+ '**/fixtures/**',
9
10
  ];
10
11
  export const DEFAULT_LIMITS = {
11
12
  maxDescriptionChars: 1024,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "skill-check",
3
- "version": "0.1.0",
3
+ "version": "0.1.1",
4
4
  "description": "Linter for agent skill files",
5
5
  "type": "module",
6
6
  "private": false,