What:
- 新增 tools/lint-frontmatter.sh,掃一個 domain 每支 skills/*/SKILL.md 的 frontmatter,檢查分隔線成對、必要鍵齊全、未加引號的純量不含「冒號加空白」、起頭字元不是 YAML 特殊字元、加了引號的值收得起來,共五項。
- 改寫 skills/skill-check/SKILL.md 的第一組稽核,把這支腳本併進去成為第 2 步,原本的行為清單檢查、結束碼路由檢查、hook smoke 依序後移。
- 第二組留白的檢查清單項目由四項改成五項,第 3 步的合併說明、三組的完成條件、第 6 步的重驗完成條件同步改寫。
Why:
- 抓到 6 支技能的 description 是未加引號的 YAML 純量、內容含「冒號加空白」。那在 YAML 是鍵的分隔符號,整份 frontmatter 當場語法錯誤。
- Antigravity 讀到語法錯誤就靜默丟棄整支技能。磁碟上 34 支,它只認 28 支。載入器不報、CLI 不報,技能清單只是少了幾列。
- 這種缺陷唯一的發現途徑是逐檔比對磁碟數量與載入數量。人工比對 10 個 domain 每次稽核都要重做一遍,還會漏。輸入輸出固定的判定就交給程式。
How:
- 腳本用 awk 自己判定 YAML 1.2 的 plain scalar 規則,不相依 pyyaml。護欄不綁在一個不保證存在的相依上,才跑得到每一台機器。
- 單引號的跳脫是重複一次、雙引號的跳脫是反斜線,兩套規則不同,所以引號改用逐字掃描,不用正規表示式一次比對兩種。
- 結束碼分四種:0 是掃到 SKILL.md 且五項全過、1 是有不合格項目(清單走 stderr,格式 {檔案}:{鍵}:{說明})、2 是用法錯誤、3 是什麼都沒掃。
- SKILL.md 明寫退出 3 不算通過,並把「每個 domain 的 lint-frontmatter.sh 退出 0」列進第 6 步的完成條件。
Who:
屬 CLI hook 接線修正(jsc-hooks 0.3.4)在 meta 這一側的稽核工具。
15 KiB
name, description
| name | description |
|---|---|
| skill-check | Routine compliance, script, hook, flow-efficiency, and cost-efficiency audit of the whole jsc skill set with no change request in hand. Sync every domain repo from the Gitea canonical marketplace, then run three parallel groups - lint-scripts.sh plus lint-frontmatter.sh plus check-behaviors.sh plus hook smoke, the guidelines.md checklist audit, and a review of parallelism, tool extraction, repeated interaction, redundant checks, misplaced gates, and avoidable token, sub-agent, API, scan, or interaction cost. Confirm compliance fixes and optimization suggestions before applying them, re-check until accepted fixes pass, then open a PR per affected repo via jsc-git pr. Use for periodic or on-demand skill-set checks; not for applying a change request (use skillset-update) or editing one skill (use skill-update). |
skill-check — audit compliance, flow efficiency, and cost efficiency
Single source of guidelines: ../../references/guidelines.md.
Flow
-
Run
tools/sync-domains.shto sync every domain repo of the Gitea canonical marketplace. Completion condition: the script exits 0 and prints onedomain<TAB>pathline per marketplace domain — exit 0 is the only code that means every repo is present and current. Exit 3 means some repos were not updated: reconcile every path named on stderr (commit or stash the dirty tree, or fix the failing pull) and rerun; when the user confirms a dirty tree is intentional local work, record that decision and continue on the local version — never read exit 3 as current. Exit 2 means a domain could not be cloned. Exit 1 means the root could not be derived,gitea.shwas not found, or the canonical marketplace was unreadable; when stderr says the root could not be derived, setJSC_PLUGINS_ROOTto the directory that holds the domain repos and rerun, because under a plugin install the script sits in the CLI's plugin cache and its built-in guess lands there instead of the domain workspace. Resolve 2 and 1 before continuing.The three review groups of step 2 all read this synced tree, so the sync finishes first.
-
Run the three review groups over the synced repos. They are independent — every one only reads, none writes a file — so launch all three in parallel and merge their results in step 3.
Group 1 — validate scripts, frontmatter, behavior lists, and hooks.
- For every synced domain repo, run
tools/lint-scripts.sh {domain-path}. One run per domain, and the runs go in parallel — no domain's verdict depends on another's. The tool covers three checks in one pass:sh -nsyntax, executable bit, and an exit-code declaration in the file header. Route each exit code: 0 — the domain's scripts pass all three; 1 — the failing items are printed as{file}:{check}:{detail}, so report each one; 2 — usage error, the tool takes exactly one argument; 3 — nothing was scanned, because the path is missing or the domain has neithertools/norhooks/. Record exit 3 as 「無腳本可掃」; a domain with no script directory is not a failure, but exit 3 is never a pass. - For every synced domain repo, run
tools/lint-frontmatter.sh {domain-path}. One run per domain, and the runs go in parallel alongside thelint-scripts.shruns. It parses the frontmatter of everyskills/*/SKILL.mdwithout a YAML library — paired---delimiters, the requirednameanddescriptionkeys, unquoted scalars carrying a colon-space or ending in a colon, unquoted scalars opening with&,*,!,|,>,%,@or a backtick, and quoted scalars that never close. Route each exit code: 0 — every SKILL.md in that domain parses; 1 — the failures are printed on stderr as{檔案}:{鍵}:{說明}, so report every one as a compliance failure with the file and key it belongs to; 2 — usage error, the tool takes exactly one argument; 3 — nothing was scanned, because the domain path orskills/is missing, orskills/holds noSKILL.md. Record exit 3 as 「無 frontmatter 可掃」with the cause from stderr and carry it into the step 3 merge; exit 3 is never a pass. This check exists because a broken frontmatter makes Antigravity drop the whole skill with no error message at all — 34 skills on disk loaded as 28, and only a file-by-file comparison found it. - For every synced domain repo, run
tools/check-behaviors.sh {domain-path}. One run per domain, and the runs go in parallel alongside thelint-scripts.shruns — no domain's verdict depends on another's. It comparesreferences/behaviors.mdagainstskills/: section per skill, dictionary order, one table per section, five rows, no empty content cell. Route each exit code: 0 — that domain's behavior list matches; 1 — the mismatches are printed on stderr as{檔案}:{技能名}:{說明}, so report every one as a compliance failure with the skill it belongs to; 2 — usage error, the tool takes exactly one argument; 3 — nothing was checked, becausereferences/behaviors.mdis missing,skills/is missing, or noSKILL.mdwas found. Record exit 3 as 「無清單可查」with the cause from stderr and carry it into the step 3 merge; a domain with no behavior list is a compliance failure, and exit 3 is never a pass. - For every shell script directly named by a SKILL.md, confirm the skill routes every exit code the script's header declares.
lint-scripts.shproves the script exists and declares its codes; this check is the other half — that the caller branches on each of them. Report evidence asskill file:line -> script path. - When the
jsc-hooksdomain is present, runjsc-hooks/tools/wire-cli.sh smoke {cli}for every CLI reported byjsc-cli/tools/detect-clis.sh; the per-CLI smokes run in parallel. When no CLI is detected, runjsc-hooks/tools/wire-cli.sh smoke codexas the minimum hook behavior check and label it 「預設 hook smoke」 in the report. Usesmoke, notpurgeor rewiring actions, and setJSC_READONLY=1for the whole audit so a mistyped sub-command is refused in code (exit 6) instead of rewiring the machine;statusandsmokeare unaffected by that variable. Route eachsmokeexit code: 0 — the run passed its own assertions; 2 — usage error, so fix the CLI code and rerun; 4 — the smoke failed, which includes the script's own result-line count not matching what it expected. Read the count from the script'slines<TAB>{數量}output line; never write the number into this skill. The script counts its own result lines and asserts them, so a hardcoded number here goes stale the moment a hook or a decision path is added — an out-of-date count in a SKILL.md is exactly what misled the previous audit. - When a hook or script smoke fails, route it as a compliance failure with script name, exit code, output summary, and proposed fix. Do not continue to report the affected hook as compliant.
Group 2 — audit every skill of every domain against the guidelines.md audit checklist. This group MUST run as a sub agent, one sub agent per domain repo, and those sub agents run in parallel. Each sub agent reports its findings: skill, failed checklist item, evidence (file:line), proposed fix. Cover the checklist's four flow checks by name, not only the naming and language items:
- Every step number, file path and section title the skill references — inside itself and in other files — really exists (the pointer points at something).
- Every step ends in a checkable completion condition, with no vague wording.
- Every external call (script, API, other skill) states what to do on failure and routes every exit code.
- No gate the skill installs blocks the only path that lifts that gate.
Five checklist items are already decided by group 1 and must not be re-run here:
sh -non everytools/andhooks/script, script existence with the executable bit, the hook smoke, thereferences/behaviors.mdmatch, and thelint-frontmatter.shverdict. Tell each sub agent to skip those five and leave them blank; the main agent fills them in from the group 1 verdicts when merging in step 3. Re-scanning the same files in every domain sub agent buys nothing — group 1 already scanned them all, with the same tools, on the same synced tree.Group 3 — a flow and cost optimization review, kept separate from the compliance audit. Each aspect MUST run as a sub agent, and the six aspects run in parallel with each other and with groups 1 and 2:
Aspect Scope 1 Parallelism Steps that run in series today but have no data dependency and can run in parallel 2 Tool extraction SKILL.md text flows with clear inputs and outputs that should move to tools/, including hook-enforceable rules that still rely on prompts3 Repeated interaction The same user question, repository fact, wiki page, API result, or file content being collected more than once 4 Redundant checks Completion conditions or verification steps that overlap, or a later step that necessarily covers an earlier check 5 Gate timing Gates that run too early or too late, causing wasted work before a block or blocking the only path that clears the gate 6 Cost efficiency Avoidable token, sub-agent, API, file-scan, full-repo audit, or user-interaction cost that can be reduced without weakening correctness Each optimization finding reports skill, aspect, evidence (file:line), current flow step count, proposed flow step count, what time or interaction it saves, what cost it saves, current cost driver, proposed cost driver, whether correctness decreases, and which protection would be weakened if any. Cost savings may be token volume, sub-agent count, API calls, file scans, full-repo audits, or user prompts. Keep optimization findings separate from compliance failures.
Completion condition for all three groups: every domain has a
lint-scripts.shverdict, alint-frontmatter.shverdict and acheck-behaviors.shverdict, every script named by a SKILL.md has an exit-code-routing verdict, and every smoked CLI has asmokeexit code plus thelinesvalue the script printed for it; every domain has a group 2 audit result that names a verdict for all checklist items — the four flow checks included, and the five group 1 items left blank for the step 3 merge rather than re-scanned; and every one of the six aspects has returned a verdict for every domain, 「無發現」 where an aspect found nothing. - For every synced domain repo, run
-
Merge the three groups, then present compliance failures and optimization findings separately via the
jsc-ask:askdecision tree. Merging means one thing in code: fill the five skipped checklist items of every group 2 sub agent report from the matching group 1 verdicts, so each domain ends with one complete checklist and no item counted twice.- Compliance failure options: apply the proposed fix / skip / custom fix. Every option states its impact scope, for example skipping leaves the skill non-compliant until the next audit.
- Optimization options: apply / defer / custom. Any suggestion that weakens a protection must name the protection it removes and must not be applied unless the user explicitly accepts that tradeoff. Cost optimization may move, merge, cache, or narrow checks; it must not delete a compliance check only because it is expensive.
Completion condition: every domain's checklist is complete after the merge, and every compliance failure and every optimization finding has a recorded decision.
-
Apply the confirmed fixes and accepted optimizations — the file-change part MUST run as a sub agent, one sub agent per affected domain repo, and those sub agents run in parallel: each repo's files are independent. A fix that changes a skill's behavior also updates that skill's
## {name}section in the same repo'sreferences/behaviors.md, in the same pass, so the fix and the behavior list land in one PR. Then runtools/sync-skill-manifest.sh {domain-path}directly (no sub agent needed) for each affected domain repo to refresh that domain README's 「Skills 目錄」 section and bump the version in all three manifests. Route each exit code: 0 — the README block and all three manifests are synced; 1 — the domain path,skills/,README.md, theJSC-SKILLSmarkers, aSKILL.md, a manifest, or a manifestversionfield is missing, so fix the named cause on stderr and rerun; 2 — usage error, the script takes exactly one argument; any other code — the script runs underset -e, so treat it as an environment fault and stop, never as a successful sync. Completion condition: every affected repo carries the changes, the matchingreferences/behaviors.mdupdate for every fix that changed a skill's behavior, and the manifest bump. -
Sync the canonical marketplace — a required step, never optional. The canonical pair lives in
plugins/metaand every domain repo carries a byte-identical copy, so a fix that leaves the copies apart makes some repos register a stale plugin set. Runtools/sync-marketplace.sh {domain} {repo-url} {description}once with an existing entry's own current values (rewriting the same entry is idempotent); the script rewrites both canonical files and copies them into every domain repo. Route each exit code:- Exit 3 — written, but some domain repo is not present locally. Run
tools/sync-domains.sh, then rerun this step. - Exit 2 — usage error: the script takes exactly three arguments. Fix them and rerun.
- Exit 1 — the root could not be derived, python3 is missing, a canonical file was unreadable, or copies differ byte for byte. Read stderr and fix the named cause: install python3 for the second; for the root case set
JSC_PLUGINS_ROOTto the directory that holds the domain repos, because under a plugin install the script sits in the CLI's plugin cache and its built-in guess lands there. Then rerun. - Exit 0 — every copy holds identical bytes; the script verifies that itself.
Completion condition: the script exits 0 and prints the touched paths.
- Exit 3 — written, but some domain repo is not present locally. Run
-
Re-run the group 1 script, frontmatter, behavior-list, and hook validation, re-check the guidelines.md audit checklist for every touched skill, then re-run the optimization aspect that produced each accepted optimization. These three re-runs are as independent as the first pass, so run them in parallel and merge them the same way step 3 did. On any compliance failure, return to step 3: confirm and fix again, until all accepted compliance fixes pass. On an accepted optimization that does not produce the promised step reduction or cost reduction, or still weakens correctness beyond the recorded decision, return to step 3 for a new decision. Completion condition:
tools/lint-scripts.shexits 0 or 3 for every domain,tools/lint-frontmatter.shexits 0 for every domain — exit 3 is 「什麼都沒掃」 and never counts as a pass —tools/check-behaviors.shexits 0 for every domain, every hook smoke exits 0 with thelinescount the script itself asserted, all checklist items pass, and every accepted optimization has a matching verification result. -
Call
jsc-git:pronce per affected domain repo to open a Push Request. Completion condition: every affected repo has a PR URL, and all URLs are reported in one table with the format in../../references/pr-report.md.