drskill 0.1.0__tar.gz → 0.2.0__tar.gz

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.
Files changed (126) hide show
  1. {drskill-0.1.0 → drskill-0.2.0}/.gitignore +1 -0
  2. {drskill-0.1.0 → drskill-0.2.0}/PKG-INFO +47 -2
  3. {drskill-0.1.0 → drskill-0.2.0}/README.md +46 -1
  4. drskill-0.2.0/docs/superpowers/plans/2026-07-20-tier2-heuristics.md +955 -0
  5. drskill-0.2.0/docs/superpowers/plans/2026-07-20-tier3-injection.md +1812 -0
  6. drskill-0.2.0/docs/superpowers/plans/2026-07-20-triage-ux.md +1147 -0
  7. drskill-0.2.0/docs/superpowers/specs/2026-07-20-granular-verification-design.md +43 -0
  8. drskill-0.2.0/docs/superpowers/specs/2026-07-20-report-triage-design.md +87 -0
  9. drskill-0.2.0/docs/superpowers/specs/2026-07-20-review-command-design.md +73 -0
  10. drskill-0.2.0/docs/superpowers/specs/2026-07-20-tier2-heuristics-design.md +143 -0
  11. drskill-0.2.0/docs/superpowers/specs/2026-07-20-tier3-injection-design.md +113 -0
  12. drskill-0.2.0/docs/superpowers/specs/2026-07-21-explain-command-design.md +102 -0
  13. drskill-0.2.0/drskill.toml +16 -0
  14. {drskill-0.1.0 → drskill-0.2.0}/pyproject.toml +1 -1
  15. drskill-0.2.0/scripts/corpus.py +115 -0
  16. {drskill-0.1.0 → drskill-0.2.0}/src/drskill/checks/__init__.py +17 -4
  17. drskill-0.2.0/src/drskill/checks/heuristics.py +249 -0
  18. drskill-0.2.0/src/drskill/checks/injection.py +523 -0
  19. drskill-0.2.0/src/drskill/checks/shadowing.py +144 -0
  20. drskill-0.2.0/src/drskill/cli.py +432 -0
  21. {drskill-0.1.0 → drskill-0.2.0}/src/drskill/data/harnesses.toml +186 -93
  22. {drskill-0.1.0 → drskill-0.2.0}/src/drskill/harnesses.py +6 -2
  23. drskill-0.2.0/src/drskill/interactive.py +48 -0
  24. {drskill-0.1.0 → drskill-0.2.0}/src/drskill/ledger.py +40 -0
  25. {drskill-0.1.0 → drskill-0.2.0}/src/drskill/models.py +12 -0
  26. drskill-0.2.0/src/drskill/report.py +259 -0
  27. {drskill-0.1.0 → drskill-0.2.0}/src/drskill/resolution.py +65 -1
  28. drskill-0.2.0/src/drskill/state.py +42 -0
  29. drskill-0.2.0/src/drskill/text.py +113 -0
  30. {drskill-0.1.0 → drskill-0.2.0}/src/drskill/tokens.py +3 -1
  31. drskill-0.2.0/tests/conformance/cases/clean-pair/expect.toml +79 -0
  32. drskill-0.2.0/tests/conformance/cases/corpus-activation/LICENSE-NOTE.md +10 -0
  33. drskill-0.2.0/tests/conformance/cases/corpus-activation/expect.toml +20 -0
  34. drskill-0.2.0/tests/conformance/cases/corpus-activation/tree/.claude/skills/frontend-design/SKILL.md +5 -0
  35. drskill-0.2.0/tests/conformance/cases/corpus-activation/tree/.claude/skills/grok/SKILL.md +5 -0
  36. drskill-0.2.0/tests/conformance/cases/corpus-activation/tree/.claude/skills/webapp-testing/SKILL.md +5 -0
  37. drskill-0.2.0/tests/conformance/cases/corpus-injection/LICENSE-NOTE.md +10 -0
  38. drskill-0.2.0/tests/conformance/cases/corpus-injection/expect.toml +21 -0
  39. drskill-0.2.0/tests/conformance/cases/corpus-injection/tree/.claude/skills/gpu-cloud/SKILL.md +5 -0
  40. drskill-0.2.0/tests/conformance/cases/corpus-injection/tree/.claude/skills/gpu-cloud/references/troubleshooting.md +3 -0
  41. drskill-0.2.0/tests/conformance/cases/corpus-injection/tree/.claude/skills/metrics-optimizer/SKILL.md +5 -0
  42. drskill-0.2.0/tests/conformance/cases/corpus-injection/tree/.claude/skills/metrics-optimizer/scripts/deep-dive.mjs +1 -0
  43. drskill-0.2.0/tests/conformance/cases/corpus-injection/tree/.claude/skills/tool-caller/SKILL.md +5 -0
  44. drskill-0.2.0/tests/conformance/cases/corpus-injection/tree/.claude/skills/trading-client/SKILL.md +5 -0
  45. drskill-0.2.0/tests/conformance/cases/corpus-injection/tree/.claude/skills/trading-client/scripts/client.py +2 -0
  46. drskill-0.2.0/tests/conformance/cases/diverged-copies/expect.toml +7 -0
  47. drskill-0.2.0/tests/conformance/cases/diverged-copies/tree/.claude/skills/geo/SKILL.md +5 -0
  48. drskill-0.2.0/tests/conformance/cases/diverged-copies/tree/.pi/skills/geo/SKILL.md +5 -0
  49. drskill-0.2.0/tests/conformance/cases/generic-description/expect.toml +3 -0
  50. drskill-0.2.0/tests/conformance/cases/generic-description/tree/.claude/skills/vague-helper/SKILL.md +5 -0
  51. drskill-0.2.0/tests/conformance/cases/injection-hostile/expect.toml +30 -0
  52. drskill-0.2.0/tests/conformance/cases/injection-hostile/tree/.claude/skills/data-helper/SKILL.md +15 -0
  53. drskill-0.2.0/tests/conformance/cases/injection-hostile/tree/.claude/skills/data-helper/references/notes.md +1 -0
  54. drskill-0.2.0/tests/conformance/cases/injection-hostile/tree/.claude/skills/data-helper/scripts/setup.sh +3 -0
  55. drskill-0.2.0/tests/conformance/cases/missing-activation/expect.toml +7 -0
  56. drskill-0.2.0/tests/conformance/cases/missing-activation/tree/.claude/skills/formatter/SKILL.md +5 -0
  57. drskill-0.2.0/tests/conformance/cases/opposing-imperatives/expect.toml +3 -0
  58. drskill-0.2.0/tests/conformance/cases/opposing-imperatives/tree/.claude/skills/tabs-fan/SKILL.md +5 -0
  59. drskill-0.2.0/tests/conformance/cases/opposing-imperatives/tree/.claude/skills/tabs-foe/SKILL.md +5 -0
  60. drskill-0.2.0/tests/conformance/cases/overlap-pileup/expect.toml +3 -0
  61. drskill-0.2.0/tests/conformance/cases/overlap-pileup/tree/.claude/skills/doc-chapters/SKILL.md +5 -0
  62. drskill-0.2.0/tests/conformance/cases/overlap-pileup/tree/.claude/skills/doc-pages/SKILL.md +5 -0
  63. drskill-0.2.0/tests/conformance/cases/overlap-pileup/tree/.claude/skills/doc-summaries/SKILL.md +5 -0
  64. drskill-0.2.0/tests/test_checks_heuristics.py +266 -0
  65. drskill-0.2.0/tests/test_checks_injection.py +436 -0
  66. {drskill-0.1.0 → drskill-0.2.0}/tests/test_checks_shadowing.py +52 -0
  67. {drskill-0.1.0 → drskill-0.2.0}/tests/test_checks_spec.py +1 -1
  68. drskill-0.2.0/tests/test_cli_commands.py +287 -0
  69. drskill-0.2.0/tests/test_cli_review.py +160 -0
  70. {drskill-0.1.0 → drskill-0.2.0}/tests/test_cli_scan.py +35 -5
  71. {drskill-0.1.0 → drskill-0.2.0}/tests/test_harnesses.py +7 -4
  72. drskill-0.2.0/tests/test_interactive.py +44 -0
  73. {drskill-0.1.0 → drskill-0.2.0}/tests/test_ledger.py +56 -0
  74. drskill-0.2.0/tests/test_report.py +361 -0
  75. {drskill-0.1.0 → drskill-0.2.0}/tests/test_resolution.py +135 -0
  76. drskill-0.2.0/tests/test_smoke.py +34 -0
  77. drskill-0.2.0/tests/test_state.py +51 -0
  78. drskill-0.2.0/tests/test_text.py +75 -0
  79. {drskill-0.1.0 → drskill-0.2.0}/tests/test_tokens.py +5 -0
  80. {drskill-0.1.0 → drskill-0.2.0}/uv.lock +1 -1
  81. drskill-0.1.0/src/drskill/checks/shadowing.py +0 -64
  82. drskill-0.1.0/src/drskill/cli.py +0 -182
  83. drskill-0.1.0/src/drskill/report.py +0 -124
  84. drskill-0.1.0/tests/conformance/cases/clean-pair/expect.toml +0 -7
  85. drskill-0.1.0/tests/test_cli_commands.py +0 -138
  86. drskill-0.1.0/tests/test_report.py +0 -169
  87. drskill-0.1.0/tests/test_smoke.py +0 -21
  88. {drskill-0.1.0 → drskill-0.2.0}/.claude/scheduled_tasks.lock +0 -0
  89. {drskill-0.1.0 → drskill-0.2.0}/LICENSE +0 -0
  90. {drskill-0.1.0 → drskill-0.2.0}/docs/superpowers/plans/2026-07-19-drskill-v0.1.md +0 -0
  91. {drskill-0.1.0 → drskill-0.2.0}/docs/superpowers/plans/2026-07-20-detailed-scan-linked-provenance.md +0 -0
  92. {drskill-0.1.0 → drskill-0.2.0}/docs/superpowers/specs/2026-07-19-drskill-v0.1-design.md +0 -0
  93. {drskill-0.1.0 → drskill-0.2.0}/docs/superpowers/specs/2026-07-20-detailed-scan-and-linked-provenance-design.md +0 -0
  94. {drskill-0.1.0 → drskill-0.2.0}/initial_design_doc.md +0 -0
  95. {drskill-0.1.0 → drskill-0.2.0}/src/drskill/__init__.py +0 -0
  96. {drskill-0.1.0 → drskill-0.2.0}/src/drskill/checks/budget.py +0 -0
  97. {drskill-0.1.0 → drskill-0.2.0}/src/drskill/checks/duplicates.py +0 -0
  98. {drskill-0.1.0 → drskill-0.2.0}/src/drskill/checks/filesystem.py +0 -0
  99. {drskill-0.1.0 → drskill-0.2.0}/src/drskill/checks/lockfile.py +0 -0
  100. {drskill-0.1.0 → drskill-0.2.0}/src/drskill/checks/spec.py +0 -0
  101. {drskill-0.1.0 → drskill-0.2.0}/src/drskill/data/__init__.py +0 -0
  102. {drskill-0.1.0 → drskill-0.2.0}/src/drskill/discovery.py +0 -0
  103. {drskill-0.1.0 → drskill-0.2.0}/src/drskill/pipeline.py +0 -0
  104. {drskill-0.1.0 → drskill-0.2.0}/tests/__init__.py +0 -0
  105. {drskill-0.1.0 → drskill-0.2.0}/tests/conformance/cases/clean-pair/tree/.claude/skills/docx-report/SKILL.md +0 -0
  106. {drskill-0.1.0 → drskill-0.2.0}/tests/conformance/cases/clean-pair/tree/.claude/skills/git-helper/SKILL.md +0 -0
  107. {drskill-0.1.0 → drskill-0.2.0}/tests/conformance/cases/exact-dup-cross-harness/expect.toml +0 -0
  108. {drskill-0.1.0 → drskill-0.2.0}/tests/conformance/cases/exact-dup-cross-harness/tree/.claude/skills/fmt/SKILL.md +0 -0
  109. {drskill-0.1.0 → drskill-0.2.0}/tests/conformance/cases/exact-dup-cross-harness/tree/.pi/skills/fmt/SKILL.md +0 -0
  110. {drskill-0.1.0 → drskill-0.2.0}/tests/conformance/cases/near-dup-pair/expect.toml +0 -0
  111. {drskill-0.1.0 → drskill-0.2.0}/tests/conformance/cases/near-dup-pair/tree/.claude/skills/report-writer/SKILL.md +0 -0
  112. {drskill-0.1.0 → drskill-0.2.0}/tests/conformance/cases/near-dup-pair/tree/.claude/skills/status-writer/SKILL.md +0 -0
  113. {drskill-0.1.0 → drskill-0.2.0}/tests/conformance/cases/near-dup-pair/tree/drskill.toml +0 -0
  114. {drskill-0.1.0 → drskill-0.2.0}/tests/conformance/cases/over-budget/expect.toml +0 -0
  115. {drskill-0.1.0 → drskill-0.2.0}/tests/conformance/cases/over-budget/tree/.claude/skills/chatty/SKILL.md +0 -0
  116. {drskill-0.1.0 → drskill-0.2.0}/tests/conformance/cases/over-budget/tree/drskill.toml +0 -0
  117. {drskill-0.1.0 → drskill-0.2.0}/tests/conformance/cases/spec-violations/expect.toml +0 -0
  118. {drskill-0.1.0 → drskill-0.2.0}/tests/conformance/cases/spec-violations/tree/.claude/skills/no-desc/SKILL.md +0 -0
  119. {drskill-0.1.0 → drskill-0.2.0}/tests/conformance/cases/spec-violations/tree/.claude/skills/wrong-name/SKILL.md +0 -0
  120. {drskill-0.1.0 → drskill-0.2.0}/tests/conformance/test_conformance.py +0 -0
  121. {drskill-0.1.0 → drskill-0.2.0}/tests/test_checks_budget.py +0 -0
  122. {drskill-0.1.0 → drskill-0.2.0}/tests/test_checks_duplicates.py +0 -0
  123. {drskill-0.1.0 → drskill-0.2.0}/tests/test_checks_filesystem.py +0 -0
  124. {drskill-0.1.0 → drskill-0.2.0}/tests/test_checks_lockfile.py +0 -0
  125. {drskill-0.1.0 → drskill-0.2.0}/tests/test_discovery.py +0 -0
  126. {drskill-0.1.0 → drskill-0.2.0}/tests/test_models.py +0 -0
@@ -3,3 +3,4 @@ __pycache__/
3
3
  *.pyc
4
4
  .venv/
5
5
  dist/
6
+ .corpus/
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: drskill
3
- Version: 0.1.0
3
+ Version: 0.2.0
4
4
  Summary: brew doctor for your AI agent's skill loadout
5
5
  License-File: LICENSE
6
6
  Requires-Python: >=3.11
@@ -44,6 +44,21 @@ Acknowledge a finding so it stops showing up until the skill's content changes:
44
44
  drskill ack near-duplicate docx-report documentation-writer
45
45
  ```
46
46
 
47
+ Walk the findings one at a time and decide each with one keypress:
48
+
49
+ ```
50
+ drskill review
51
+ ```
52
+
53
+ `review` shows each finding with its full evidence and takes single-key actions: `a` acks it, `n` acks it with a note, `f` queues its fix commands for a copy and paste block at the end, `s` skips it, and `q` quits. Acks are written as you go, so quitting midway loses nothing. `review` only runs in a real terminal. When stdin or stdout is not a TTY, or `CI` or `DRSKILL_NO_INTERACTIVE` is set, it prints one line pointing at `scan` and `ack` and exits. `scan` itself never prompts, so a script or an agent calling drskill can never get stuck at a prompt.
54
+
55
+ Print the full evidence for one finding, or for a whole check class:
56
+
57
+ ```
58
+ drskill show fe5b
59
+ drskill show injection-egress
60
+ ```
61
+
47
62
  List every harness's effective skill set with token counts:
48
63
 
49
64
  ```
@@ -97,6 +112,17 @@ Without `--ci`, warnings alone exit 0. This lets you run `drskill scan` locally
97
112
  | `lockfile-drift` | warning | A skill's content hash does not match its `skills-lock.json` entry. The message attributes the likely cause, e.g. a `gh skill update` or a hand edit, and does not call it corruption. |
98
113
  | `budget-catalog-tokens` | warning | A harness's total catalog tokens exceed `[budget] catalog_tokens_max`. |
99
114
  | `budget-body-tokens` | warning | A skill's body tokens exceed `[budget] body_tokens_warn`. |
115
+ | `description-overlap` | warning | Two or more descriptions are similar enough that a router could confuse them. The finding names the cluster and the trigger phrases they share. Threshold `description_overlap`. |
116
+ | `missing-activation` | warning | A description never states when the skill should trigger, e.g. no "when", "trigger", or "if the user" phrasing. |
117
+ | `generic-description` | warning | A description has fewer distinctive words than `generic_min_distinct_tokens`, e.g. "Helps with various tasks." |
118
+ | `opposing-imperatives` | warning | Two skills give opposite orders about the same action, e.g. "Always use tabs" against "Never use tabs". Deliberately strict matching, so paraphrased conflicts are not caught. |
119
+ | `injection-unicode` | error | Skill text or a bundled file contains bidirectional control characters or zero-width characters. These can hide instructions from a human reviewer. |
120
+ | `injection-credential-read` | error | A bundled script references credential paths such as `~/.ssh`, `~/.aws`, or private key files. Reads of `.env` alone downgrade to a warning. |
121
+ | `injection-override` | warning | Skill text contains instruction-override phrasing, e.g. "ignore all previous instructions" or "without informing the user". |
122
+ | `injection-mandatory-script` | warning | The skill demands that its own bundled script runs as a required first step, e.g. "you must first run scripts/setup.sh". |
123
+ | `injection-egress` | warning | A bundled script calls the network, e.g. `curl` or `requests.post`. The finding quotes each call so you can check the destination. |
124
+ | `injection-encoded-blob` | warning | Skill text or a bundled file contains a long base64 or hex run that a reviewer cannot read. |
125
+ | `injection-remote-fetch` | warning | Skill text tells the agent to fetch remote content and act on it, e.g. `curl` piped to a shell or "download X and follow the instructions". |
100
126
 
101
127
  ## The ledger
102
128
 
@@ -115,6 +141,12 @@ A finding's fingerprint is a hash of the check id plus the content of every skil
115
141
 
116
142
  In global mode (`--global`), the ledger lives at `~/.drskill.toml` instead.
117
143
 
144
+ Acks are scope aware. When a finding involves only machine-level skills, e.g. a vendored skill under your home directory that has nothing to do with the current repo, `drskill ack` writes the ack to `~/.drskill.toml` and says so. Every project scan honors acks from both ledgers, so you decide once per machine instead of once per repo. When any project skill is involved, the ack goes to the project's committed `drskill.toml` as before. Two flags override the routing: `--local` forces the project ledger, and `--global-ack` forces the machine ledger.
145
+
146
+ ## Reading the report
147
+
148
+ Findings print errors first, then warnings. Inside each section the order is: findings you have not seen before, then findings on skills you installed, then findings on harness-vendored skills, which carry a `[system skill]` label. A finding you have not seen carries a `new` tag, and the summary line counts them. The memory behind the `new` tag lives in `~/.drskill/state/`, one small file per project. It only records what the report has shown you; it is not the ledger, and `--json` runs never touch it, so an agent polling drskill does not clear your markers. When a finding affects every detected harness, the harness line collapses to a count, e.g. "all 7 harnesses". Checks that flag description quality report one finding listing every offending skill, so three skills with the same problem are one entry and one ack.
149
+
118
150
  The `source` column in `list` shows where a skill came from: `skills-lock` for skills named in a project's `skills-lock.json`, `gh-skill` for skills with `gh skill` provenance in their frontmatter, and `linked` for skills that live in or link into a `.agents/skills` store. The `linked` label means an installer arranged the layout; drskill does not guess which one. `unmanaged` means a plain directory with no known manager.
119
151
 
120
152
  ## Known limitations
@@ -125,6 +157,19 @@ Claude Code skills bundled inside plugins are not scanned yet. drskill only walk
125
157
 
126
158
  `skills-lock.json` hash verification is self-calibrating. Upstream `npx skills` computes its own content hashes, and drskill cannot always reproduce them exactly. If none of the hashes in a lockfile match what drskill computes, it will not accuse every skill of drift; instead it prints one warning saying the hashes could not be verified against that lockfile. Per-skill drift warnings only appear once drskill has confirmed, by matching at least one hash, that its hashing algorithm agrees with that lockfile's producer.
127
159
 
128
- Harness rules have three levels of confidence. Claude Code, Pi, and Gemini CLI are verified against their own docs or source. Codex, Cursor, and Copilot are best effort. Codex in particular does not shadow skills the way drskill's precedence model assumes, since Codex keeps both copies visible on a name collision instead of picking a winner, and that behavior does not fit the model yet. About 66 further harnesses are vendored from the `vercel-labs/skills` project and are also best effort; drskill detects them and reports their findings, but their search paths have not been independently confirmed against each harness's own documentation. Reports label every finding from an unverified harness "best effort" so you know how much to trust it.
160
+ Harness rules are verified in two parts, because they have two different jobs. Paths verification covers which directories a harness reads and whether it searches them recursively. Precedence verification covers which copy wins when two skills share a name. Claude Code, Pi, Gemini CLI, Codex, and Cline are verified on both. Cursor is verified on paths only, since its docs do not say which copy wins a collision. Copilot is unverified on both, since its docs do not confirm recursion and the CLI is closed source. About 65 further harnesses are vendored from the `vercel-labs/skills` project and are unverified on both.
161
+
162
+ A finding only inherits the uncertainty it actually depends on. Shadowing and double-load findings depend on precedence; every other finding depends only on paths. When a harness in a finding's list is unverified for the part that finding depends on, its name carries a `?` suffix, and the report ends with one legend line explaining it. A finding with no `?` rests entirely on verified rules.
129
163
 
130
164
  Token counts are approximate. drskill counts tokens with `tiktoken`'s `o200k_base` encoding, which is a reasonable estimate but will not match every harness's actual tokenizer or catalog rendering exactly.
165
+
166
+ The seven injection checks flag surfaces; they do not verify intent. Static analysis cannot prove a skill benign or hostile, so every injection finding quotes the exact lines it judged and leaves the verdict to you. A clean scan is not a security guarantee, and a finding is not an accusation. Bundled files that are binary or larger than 1 MiB are recorded but not content scanned, and the report says so when that happens. A bundled file counts as a script when it has a script extension or a shebang line; everything else is scanned as prose, so the script-only checks (egress, credential reads) do not look inside files disguised as plain text.
167
+
168
+ The four description and instruction checks are heuristics. Their thresholds are tuned against real public skill sets to stay quiet on well-written skills, and every finding can be acknowledged, but they will miss paraphrased conflicts and will flag some judgment calls. The thresholds live in `drskill.toml`:
169
+
170
+ ```toml
171
+ [thresholds]
172
+ near_duplicate = 0.85
173
+ description_overlap = 0.6
174
+ generic_min_distinct_tokens = 2
175
+ ```
@@ -30,6 +30,21 @@ Acknowledge a finding so it stops showing up until the skill's content changes:
30
30
  drskill ack near-duplicate docx-report documentation-writer
31
31
  ```
32
32
 
33
+ Walk the findings one at a time and decide each with one keypress:
34
+
35
+ ```
36
+ drskill review
37
+ ```
38
+
39
+ `review` shows each finding with its full evidence and takes single-key actions: `a` acks it, `n` acks it with a note, `f` queues its fix commands for a copy and paste block at the end, `s` skips it, and `q` quits. Acks are written as you go, so quitting midway loses nothing. `review` only runs in a real terminal. When stdin or stdout is not a TTY, or `CI` or `DRSKILL_NO_INTERACTIVE` is set, it prints one line pointing at `scan` and `ack` and exits. `scan` itself never prompts, so a script or an agent calling drskill can never get stuck at a prompt.
40
+
41
+ Print the full evidence for one finding, or for a whole check class:
42
+
43
+ ```
44
+ drskill show fe5b
45
+ drskill show injection-egress
46
+ ```
47
+
33
48
  List every harness's effective skill set with token counts:
34
49
 
35
50
  ```
@@ -83,6 +98,17 @@ Without `--ci`, warnings alone exit 0. This lets you run `drskill scan` locally
83
98
  | `lockfile-drift` | warning | A skill's content hash does not match its `skills-lock.json` entry. The message attributes the likely cause, e.g. a `gh skill update` or a hand edit, and does not call it corruption. |
84
99
  | `budget-catalog-tokens` | warning | A harness's total catalog tokens exceed `[budget] catalog_tokens_max`. |
85
100
  | `budget-body-tokens` | warning | A skill's body tokens exceed `[budget] body_tokens_warn`. |
101
+ | `description-overlap` | warning | Two or more descriptions are similar enough that a router could confuse them. The finding names the cluster and the trigger phrases they share. Threshold `description_overlap`. |
102
+ | `missing-activation` | warning | A description never states when the skill should trigger, e.g. no "when", "trigger", or "if the user" phrasing. |
103
+ | `generic-description` | warning | A description has fewer distinctive words than `generic_min_distinct_tokens`, e.g. "Helps with various tasks." |
104
+ | `opposing-imperatives` | warning | Two skills give opposite orders about the same action, e.g. "Always use tabs" against "Never use tabs". Deliberately strict matching, so paraphrased conflicts are not caught. |
105
+ | `injection-unicode` | error | Skill text or a bundled file contains bidirectional control characters or zero-width characters. These can hide instructions from a human reviewer. |
106
+ | `injection-credential-read` | error | A bundled script references credential paths such as `~/.ssh`, `~/.aws`, or private key files. Reads of `.env` alone downgrade to a warning. |
107
+ | `injection-override` | warning | Skill text contains instruction-override phrasing, e.g. "ignore all previous instructions" or "without informing the user". |
108
+ | `injection-mandatory-script` | warning | The skill demands that its own bundled script runs as a required first step, e.g. "you must first run scripts/setup.sh". |
109
+ | `injection-egress` | warning | A bundled script calls the network, e.g. `curl` or `requests.post`. The finding quotes each call so you can check the destination. |
110
+ | `injection-encoded-blob` | warning | Skill text or a bundled file contains a long base64 or hex run that a reviewer cannot read. |
111
+ | `injection-remote-fetch` | warning | Skill text tells the agent to fetch remote content and act on it, e.g. `curl` piped to a shell or "download X and follow the instructions". |
86
112
 
87
113
  ## The ledger
88
114
 
@@ -101,6 +127,12 @@ A finding's fingerprint is a hash of the check id plus the content of every skil
101
127
 
102
128
  In global mode (`--global`), the ledger lives at `~/.drskill.toml` instead.
103
129
 
130
+ Acks are scope aware. When a finding involves only machine-level skills, e.g. a vendored skill under your home directory that has nothing to do with the current repo, `drskill ack` writes the ack to `~/.drskill.toml` and says so. Every project scan honors acks from both ledgers, so you decide once per machine instead of once per repo. When any project skill is involved, the ack goes to the project's committed `drskill.toml` as before. Two flags override the routing: `--local` forces the project ledger, and `--global-ack` forces the machine ledger.
131
+
132
+ ## Reading the report
133
+
134
+ Findings print errors first, then warnings. Inside each section the order is: findings you have not seen before, then findings on skills you installed, then findings on harness-vendored skills, which carry a `[system skill]` label. A finding you have not seen carries a `new` tag, and the summary line counts them. The memory behind the `new` tag lives in `~/.drskill/state/`, one small file per project. It only records what the report has shown you; it is not the ledger, and `--json` runs never touch it, so an agent polling drskill does not clear your markers. When a finding affects every detected harness, the harness line collapses to a count, e.g. "all 7 harnesses". Checks that flag description quality report one finding listing every offending skill, so three skills with the same problem are one entry and one ack.
135
+
104
136
  The `source` column in `list` shows where a skill came from: `skills-lock` for skills named in a project's `skills-lock.json`, `gh-skill` for skills with `gh skill` provenance in their frontmatter, and `linked` for skills that live in or link into a `.agents/skills` store. The `linked` label means an installer arranged the layout; drskill does not guess which one. `unmanaged` means a plain directory with no known manager.
105
137
 
106
138
  ## Known limitations
@@ -111,6 +143,19 @@ Claude Code skills bundled inside plugins are not scanned yet. drskill only walk
111
143
 
112
144
  `skills-lock.json` hash verification is self-calibrating. Upstream `npx skills` computes its own content hashes, and drskill cannot always reproduce them exactly. If none of the hashes in a lockfile match what drskill computes, it will not accuse every skill of drift; instead it prints one warning saying the hashes could not be verified against that lockfile. Per-skill drift warnings only appear once drskill has confirmed, by matching at least one hash, that its hashing algorithm agrees with that lockfile's producer.
113
145
 
114
- Harness rules have three levels of confidence. Claude Code, Pi, and Gemini CLI are verified against their own docs or source. Codex, Cursor, and Copilot are best effort. Codex in particular does not shadow skills the way drskill's precedence model assumes, since Codex keeps both copies visible on a name collision instead of picking a winner, and that behavior does not fit the model yet. About 66 further harnesses are vendored from the `vercel-labs/skills` project and are also best effort; drskill detects them and reports their findings, but their search paths have not been independently confirmed against each harness's own documentation. Reports label every finding from an unverified harness "best effort" so you know how much to trust it.
146
+ Harness rules are verified in two parts, because they have two different jobs. Paths verification covers which directories a harness reads and whether it searches them recursively. Precedence verification covers which copy wins when two skills share a name. Claude Code, Pi, Gemini CLI, Codex, and Cline are verified on both. Cursor is verified on paths only, since its docs do not say which copy wins a collision. Copilot is unverified on both, since its docs do not confirm recursion and the CLI is closed source. About 65 further harnesses are vendored from the `vercel-labs/skills` project and are unverified on both.
147
+
148
+ A finding only inherits the uncertainty it actually depends on. Shadowing and double-load findings depend on precedence; every other finding depends only on paths. When a harness in a finding's list is unverified for the part that finding depends on, its name carries a `?` suffix, and the report ends with one legend line explaining it. A finding with no `?` rests entirely on verified rules.
115
149
 
116
150
  Token counts are approximate. drskill counts tokens with `tiktoken`'s `o200k_base` encoding, which is a reasonable estimate but will not match every harness's actual tokenizer or catalog rendering exactly.
151
+
152
+ The seven injection checks flag surfaces; they do not verify intent. Static analysis cannot prove a skill benign or hostile, so every injection finding quotes the exact lines it judged and leaves the verdict to you. A clean scan is not a security guarantee, and a finding is not an accusation. Bundled files that are binary or larger than 1 MiB are recorded but not content scanned, and the report says so when that happens. A bundled file counts as a script when it has a script extension or a shebang line; everything else is scanned as prose, so the script-only checks (egress, credential reads) do not look inside files disguised as plain text.
153
+
154
+ The four description and instruction checks are heuristics. Their thresholds are tuned against real public skill sets to stay quiet on well-written skills, and every finding can be acknowledged, but they will miss paraphrased conflicts and will flag some judgment calls. The thresholds live in `drskill.toml`:
155
+
156
+ ```toml
157
+ [thresholds]
158
+ near_duplicate = 0.85
159
+ description_overlap = 0.6
160
+ generic_min_distinct_tokens = 2
161
+ ```