claudemd-cli 0.9.16 → 0.9.18
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/CHANGELOG.md +32 -0
- package/bin/claudemd-lint.js +40 -2
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,38 @@ All notable changes to the `claudemd` plugin. This changelog tracks plugin artif
|
|
|
8
8
|
- **Canonical spec version source**: `spec/CLAUDE.md` top-line title (`# AI-CODING-SPEC vX.Y.Z — Core`) + `spec/CLAUDE-changelog.md` top `##` entry.
|
|
9
9
|
- **Plugin semver vs spec semver** are independent: plugin patch (0.2.0 → 0.2.1) may ship when spec is unchanged (this release); plugin minor (0.1.9 → 0.2.0) ships when spec minor updates (v0.2.0 shipped spec v6.10.0).
|
|
10
10
|
|
|
11
|
+
## [0.9.18] - 2026-05-10
|
|
12
|
+
|
|
13
|
+
**Patch — public npm CLI `bin/claudemd-lint.js` had the same argv-shape silent-fallback the slash-commands fixed in v0.9.16/v0.9.17.** Spec v6.11.7 unchanged. Surfaced exercising the published CLI as a downstream integrator would: `claudemd lint --jzon "..."` (typo) silently dropped the unknown flag and scanned the text anyway → exit reflected only the text content; `claudemd lint --json=yes "..."` silently dropped `--json` (because `args.includes('--json')` returns false for the `=` form) → human-readable text emitted on stdout when JSON was expected; `claudemd lint --file=PATH` (GNU-getopt convention) was unrecognized and exited 2 with the misleading `text required` message; `claudemd audit --include-ratiox PATH` silently dropped the typo → exit 0 even when content would deny. Fifth recurrence of argv-shape silent-fallback (v0.9.14 lint positional, v0.9.15 hook tag, v0.9.16 three slash-commands, v0.9.17 two more slash-commands, this one — the **publicly published** surface, the most-visible footgun).
|
|
14
|
+
|
|
15
|
+
### Fixed
|
|
16
|
+
|
|
17
|
+
- `[fix]` **`bin/claudemd-lint.js` `lintCmd` + `auditCmd` validate flags up-front via `validateAndExpandFlags(rawArgs, knownBools, knownValues, sub)`.** Pre-fix, both commands used `args.includes('--bool')` / `args.indexOf('--key')` which silently dropped (a) `--bool=value` (typo'd as truthy → false), (b) any unknown `--typo` flag (filtered out of positional via `startsWith('--')`), (c) `--key=value` for value flags (`indexOf` looks for the bare `--key`). Post-fix, all three exit 2 with a parser error before the existing happy-path logic runs. Bonus: `--file=PATH` (GNU-getopt `=` form) is now accepted as a sibling of `--file PATH` (Unix convention preserved). Backward-compatible — every previously-passing call shape still works.
|
|
18
|
+
|
|
19
|
+
### Added
|
|
20
|
+
|
|
21
|
+
- `[test]` **4 new cases in `tests/scripts/lint-cli.test.js`** asserting exit 2 + stderr message on the three silent-fallback shapes (`--jzon` typo, `--json=yes` bool-with-value, audit `--include-ratiox` typo) + the new accepted shape (`--file=PATH`). Test count 244 → 248. Coverage gap that allowed v0.9.17 to ship without catching this: the existing tests asserted happy paths but had no negative assertions on bug shapes.
|
|
22
|
+
|
|
23
|
+
### Why no L3 / pre-ship-review chain
|
|
24
|
+
|
|
25
|
+
`fix:` per spec §2 hard-upgrade exclusion — restores the implied contract (every flag shape either works or rejects loudly; no silent-drop). L2 ceiling. Diff: 1 source file (~30 LOC added, no API change), 1 test file (+45 LOC). Notable: this is the FIFTH consecutive patch chasing the same antipattern. The §13.2 promotion case for a `grep -rE '\.find\(.*startsWith\|args\.includes.*--' bin/ scripts/` lint gate (CI step or pre-commit) is now overwhelming. The v0.9.16 memory note already flagged this — adding a one-line `npm run lint:argv` step that fails CI on any `args.includes('--`'-shaped flag detection is the obvious next iteration.
|
|
26
|
+
|
|
27
|
+
## [0.9.17] - 2026-05-10
|
|
28
|
+
|
|
29
|
+
**Patch — two more slash-command CLIs leaked the v0.9.16 antipattern.** Spec v6.11.7 unchanged. Surfaced in the same exploratory-testing session that produced v0.9.16: `/claudemd-doctor --prune-backups 5` (space form) silently dropped the value, ran without prune, exited 0; `/claudemd-rules --days 30` (space form) silently fell back to the default 90-day window, exited 0. v0.9.16 swept `clean-residue.js` / `audit.js` / `sparkline.js` but missed `doctor.js` and `hard-rules-audit.js` carrying the same `args.find(a => a.startsWith('--key='))` pattern. Fourth recurrence of argv-shape silent-fallback (v0.9.14 lint, v0.9.15 hook tag, v0.9.16 three CLIs, this one).
|
|
30
|
+
|
|
31
|
+
### Fixed
|
|
32
|
+
|
|
33
|
+
- `[fix]` **`scripts/doctor.js` + `scripts/hard-rules-audit.js` switched to `parseStrict`.** Pre-fix, both scripts used the `args.find(a => a.startsWith('--key='))` pattern that silently dropped (a) the space-separated form `--key value`, (b) any unknown flag. Post-fix, both exit 2 with a parser error before touching state. The contract documented in `commands/claudemd-doctor.md` (`--prune-backups=N`) and the `--days=N` env-var-equivalent on `hard-rules-audit.js` is now enforced. Documented happy-path behavior unchanged.
|
|
34
|
+
|
|
35
|
+
### Added
|
|
36
|
+
|
|
37
|
+
- `[test]` **2 new cases each in `tests/scripts/doctor.test.js` + `tests/scripts/hard-rules-audit.test.js`** asserting exit 2 + stderr message on space-form and unknown-flag bug shapes via spawned CLI (240 → 244 tests).
|
|
38
|
+
|
|
39
|
+
### Why no L3 / pre-ship-review chain
|
|
40
|
+
|
|
41
|
+
`fix:` per spec §2 hard-upgrade exclusion — restores documented intent (`--key=value` per slash-command docs and v0.9.16 contract). L2 ceiling. Diff: 2 script tails refactored (~10 LOC each), 2 test files (+4 cases). Recurrence count for argv-shape silent-fallback antipattern is now 4 across 4 patches — the §13.2 promotion case for a hook-level lint or repo-wide grep gate is stronger than v0.9.16 made it.
|
|
42
|
+
|
|
11
43
|
## [0.9.16] - 2026-05-10
|
|
12
44
|
|
|
13
45
|
**Patch — three slash-command CLIs silently dropped wrong-shape arguments.** Spec v6.11.7 unchanged. Surfaced while exercising the v0.9.15 plugin from a real `/claudemd-clean-residue --apply --age-days 0` user attempt: `--age-days` value dropped (script fell back to default `1`), `--apply` ran, script exited 0 reporting "0 deleted." Same family as v0.9.14 `claudemd-cli lint <path>` silent-success — argv-shape mismatch produced indistinguishable-from-success output.
|
package/bin/claudemd-lint.js
CHANGED
|
@@ -67,7 +67,44 @@ function readPackageVersion() {
|
|
|
67
67
|
}
|
|
68
68
|
}
|
|
69
69
|
|
|
70
|
-
|
|
70
|
+
// Strict-validate flag-shaped args + normalize `--key=value` → `--key value`
|
|
71
|
+
// pairs so the existing space-form parsing below works on either shape.
|
|
72
|
+
// Catches the same antipattern the slash-command CLIs hit in v0.9.16/0.9.17:
|
|
73
|
+
// `args.includes('--json')` returns false for `--json=yes`, so the flag was
|
|
74
|
+
// silently dropped; `args.indexOf('--file')` returns -1 for `--file=PATH`,
|
|
75
|
+
// so the value was silently ignored; an unknown `--jzon` typo was silently
|
|
76
|
+
// stripped from positional and never surfaced. Each path now exits 2.
|
|
77
|
+
function validateAndExpandFlags(args, knownBools, knownValues, sub) {
|
|
78
|
+
const out = [];
|
|
79
|
+
const bools = new Set(knownBools);
|
|
80
|
+
const values = new Set(knownValues);
|
|
81
|
+
for (const a of args) {
|
|
82
|
+
if (!a.startsWith('--')) { out.push(a); continue; }
|
|
83
|
+
if (a.includes('=')) {
|
|
84
|
+
const eq = a.indexOf('=');
|
|
85
|
+
const k = a.slice(0, eq);
|
|
86
|
+
const v = a.slice(eq + 1);
|
|
87
|
+
if (bools.has(k)) {
|
|
88
|
+
process.stderr.write(`${sub}: '${k}' is a boolean flag and does not take a value (got '${a}'). Drop the '=...' suffix.\n`);
|
|
89
|
+
process.exit(2);
|
|
90
|
+
}
|
|
91
|
+
if (values.has(k)) {
|
|
92
|
+
out.push(k);
|
|
93
|
+
out.push(v);
|
|
94
|
+
continue;
|
|
95
|
+
}
|
|
96
|
+
process.stderr.write(`${sub}: unknown flag '${k}' (got '${a}').\n`);
|
|
97
|
+
process.exit(2);
|
|
98
|
+
}
|
|
99
|
+
if (bools.has(a) || values.has(a)) { out.push(a); continue; }
|
|
100
|
+
process.stderr.write(`${sub}: unknown flag '${a}'.\n`);
|
|
101
|
+
process.exit(2);
|
|
102
|
+
}
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function lintCmd(rawArgs) {
|
|
107
|
+
const args = validateAndExpandFlags(rawArgs, ['--json', '--stdin'], ['--file'], 'lint');
|
|
71
108
|
const json = args.includes('--json');
|
|
72
109
|
const stdin = args.includes('--stdin');
|
|
73
110
|
|
|
@@ -152,7 +189,8 @@ function lintCmd(args) {
|
|
|
152
189
|
process.exit(hits.length === 0 ? 0 : 1);
|
|
153
190
|
}
|
|
154
191
|
|
|
155
|
-
function auditCmd(
|
|
192
|
+
function auditCmd(rawArgs) {
|
|
193
|
+
const args = validateAndExpandFlags(rawArgs, ['--json', '--include-ratio'], [], 'audit');
|
|
156
194
|
const json = args.includes('--json');
|
|
157
195
|
const includeRatio = args.includes('--include-ratio');
|
|
158
196
|
const positional = args.filter(a => !a.startsWith('--'));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claudemd-cli",
|
|
3
|
-
"version": "0.9.
|
|
3
|
+
"version": "0.9.18",
|
|
4
4
|
"description": "Standalone CLI for §10-V banned-vocab + transcript scanning. Companion to the claudemd Claude Code plugin (github.com/sdsrss/claudemd) for use in git pre-commit hooks, GitHub Actions, and other agents.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|