Repository navigation
Conversation
catalog check reads the scripts a skill bundles, token-free and without running them: - script-crlf (error): CRLF line endings on a shell script or on a file with a #! line. - script-syntax (error): a .py that ast.parse rejects; a shell script that bash -n rejects, only when bash is on PATH and the shebang (if any) names bash, sh, dash or ksh. - script-lint (note): ruff and shellcheck counts, only when they are on PATH; one note. The loader's vendored and template trees are not scanned, a skill at the checkout root has no script bundle, and a tool that is missing or cannot start adds nothing. Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
There was a problem hiding this comment.
🟡 Changes recommended
Bundled-script checks have unresolved coverage gaps, valid Bash shebangs can trigger false syntax errors, and null-code Ruff parse errors are double-counted as lint findings.
4 open issues
Bundled scripts are skipped when reviewing a skill through the public API · New Interpreter detection misses env options with operands and assignments · New Bash syntax check rejects valid scripts using supported extglob options · New Null-code Ruff parse errors are double-counted as script lint · New
What changed in this PR
5 new findings: 4 must-fix and 1 lower-priority documentation correction.
This PR extends catalog validation to check scripts bundled with skills without executing or importing them, supporting broader corpus scanning.
Changes:
- Adds CRLF and syntax checks, plus optional aggregated ruff and shellcheck results.
- Integrates script findings into skill reviews, with checkout-root and excluded-directory handling.
- Adds regression tests, user documentation, and a changelog entry.
| File | Description |
|---|---|
dokimi/tests/test_catalog_validate_scripts.py |
Tests script checks, tool failures, and bundle boundaries. |
dokimi/dokimi/external/validate.py |
Includes script findings in skill reviews. |
dokimi/dokimi/external/script_checks.py |
Implements script discovery, syntax checks, and optional lint summaries. |
dokimi/docs/external-content.md |
Documents script checks and exclusions. |
dokimi/changelog.d/20261106_120000_napetrov_skill-script-checks.md |
Announces bundled-script validation. |
Review effort: Balanced
| items = _json_list(done) | ||
| if items is None: | ||
| return None | ||
| codes = [str(item.get("code") or "syntax") for item in items if isinstance(item, dict)] |
There was a problem hiding this comment.
Not reproduced: ruff 0.15.12 and 0.17.2 both report code invalid-syntax for def f(:, with no null code. The syntax filter already catches it. If you have a ruff version where the code is null, send it and I will add it.
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## main #455 +/- ##
========================================
Coverage 95.09% 95.10%
========================================
Files 372 373 +1
Lines 49245 49404 +159
Branches 8467 8496 +29
========================================
+ Hits 46828 46984 +156
- Misses 1532 1534 +2
- Partials 885 886 +1
🚀 New features to boost your workflow:
|
antonwolfy
left a comment
There was a problem hiding this comment.
Review at 12a042d. Three bugs and one test gap. The CRLF and syntax logic, the containment of links, the loader skip set and the no-environment runs are sound, but the checkout-root guard, the shebang handling and the bash -n input each give a wrong verdict on a plain input.
What I ran, on a Linux review host at 12a042d:
make ci-local: 32 passed, 0 failed, 12 skipped (the skips need docker, a matrix or an org-level tool). 16,752 tests passed in the main run.- Reproductions for the code findings, with the LLM transport mocked.
- 51 hand-made mutants of
script_checks.pyagainst the new tests: 31 killed, 20 survived.
Findings not anchored inline:
- The shebang mapping and the
bash -ntimeout are not pinned by any test. Mutation check: removingshordashfromBASH_FAMILY, removingdone.timed_outin_shell_problem, and dropping.lower()on the suffix (soRUN.SHandTool.PYare skipped) all pass the full file of 23 tests. The Pythonscript-syntaxerror level is not asserted either: downgrading it to a warning passes. These lines decide whethercatalog checkreports an error. Add one test per line: a#!/bin/shscript with an unterminatedif, a bash reply withtimed_out=Truethat gives no finding, an uppercase.SHand.PY, andreview.statefor the Python case.
Bot points I checked that no longer apply at this head (1)
- Copilot's point on null-code ruff diagnostics does not apply: ruff 0.17.2 reports a parse error as
"code": "invalid-syntax", whichSYNTAX_CODESalready filters, sodef f(:adds no lint count.
review_skill_dir takes the skill directory as a synthetic scan root, so the checkout-root guard in script_findings skipped every script of a skill reviewed alone. review_skills takes root_is_checkout, which the per-skill entry point sets to False. Thread: PRRT_kwDOVBg7g860zx1a Thread: PRRT_kwDOVBg7g8601_r5 Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
_runs_under_bash dropped only the words that start with "-", so the operand of -u or -C and a NAME=value word stayed in front of the interpreter, and "#!/usr/bin/env -S -u BASH_ENV bash" hid a bash script from bash -n. The command is now found the way env reads it. Thread: PRRT_kwDOVBg7g860zx1l Thread: PRRT_kwDOVBg7g8601_r7 Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
bash -n ran with no options, so "#!/usr/bin/env -S bash -O extglob" with "case x in @(x|y)) ..." was reported as a syntax error. The shebang's -O extglob is now given to bash -n. A shebang argument that changes parsing and that this check cannot reproduce leaves the script unjudged, instead of judging it by the wrong rules. Thread: PRRT_kwDOVBg7g860zx1t Thread: PRRT_kwDOVBg7g8601_r9 Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
…ase and the Python error level Four mutants of script_checks.py each passed the suite: dropping sh from the bash family, dropping the bash -n timeout, not lowering the suffix, and reporting a Python syntax error as a warning. Each now fails a test. Review: PRR_kwDOVBg7g88AAAABWm8I3A Comment: IC_kwDOVBg7g88AAAABj5wqqw Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
The docs now say which shebang forms are looked through (env options and NAME=value words), that -O extglob reaches bash -n, and that another parsing argument leaves the script unchecked. Review: PRR_kwDOVBg7g88AAAABWlvvuQ Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>
The shebang "-o pipefail" branch of _shell_options had no test. It is now pinned, next to the -eu flag case. Comment: IC_kwDOVBg7g88AAAABj5wqqw Signed-off-by: Nikolay Petrov <nikolay.a.petrov@intel.com>

Refs #413.
Summary
catalog checknow checks the scripts a skill bundles, without running or importing them:script-crlf(error): CRLF line endings on a shell script or on a file with a#!line.script-syntax(error): a.pythatast.parserejects; a shell script thatbash -nrejects (only when bash is on PATH, and the shebang is absent or names bash, sh, dash or ksh).script-lint(note): ruff and shellcheck counts, only when those tools are on PATH; one note.A skill at the checkout root has no script bundle. A missing tool, or one that cannot start, adds nothing.
Evidence
tests/test_catalog_validate_scripts.py. The defect tests fail on the commits before their fixes (4 of 5 first-pass tests, 2 of 2 second-pass tests); the env-bash guard passes by design.make ci-localon 12a042d: 16813 passed, 1 failed:test_process.py::test_sighup_kills_the_live_groups_too, a load flake that passes alone.Merge Danger
catalog evalreport statusfailand exit 1. Probe: a CRLFrun.shgives pass on base and fail on head. Routing also leaves such a skill out of the field as a spec error. This is intended for error codes, but it changes eval results for any catalog with a broken bundled script. Please check it against the eval gate before merge.#!Python or shell line is read for CRLF but not parsed;.shis judged as bash, so a zsh-only script gets an error;bash -nis weaker for sh, dash and ksh shebangs;vendor/is not in the loader's skip set, so it is still scanned;#!files.🤖 Generated with Claude Code