claudemd-cli 0.74.2 → 0.76.0
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 +24 -1
- package/package.json +1 -1
- package/scripts/lib/argv.js +45 -0
package/README.md
CHANGED
|
@@ -35,7 +35,7 @@ Then bootstrap the **current** session (skip the wait-for-next-session restart)
|
|
|
35
35
|
|
|
36
36
|
Fallback (no slash command — e.g. scripting outside CC): `node ~/.claude/plugins/cache/claudemd/claudemd/<version>/scripts/install.js`. Find `<version>` with `ls ~/.claude/plugins/cache/claudemd/claudemd/ | sort -V | tail -1`.
|
|
37
37
|
|
|
38
|
-
> ⚠️ **`~/.claude/CLAUDE.md` is shared real estate.** Claude Code reads this file as your user-global instructions across every project. If you've hand-written personal instructions there (`Always reply in 中文`, `My name is X`, etc.), install moves your existing files to `~/.claude/backup-<ISO>/` (last 5 kept automatically) before writing the spec. Since v0.5.3, install prints a `[claudemd] WARN: …` line to stderr when the existing file does not look like a claudemd spec. To bring your personal instructions back on uninstall, run `CLAUDEMD_SPEC_ACTION=restore /claudemd-uninstall`.
|
|
38
|
+
> ⚠️ **`~/.claude/CLAUDE.md` is shared real estate.** Claude Code reads this file as your user-global instructions across every project. If you've hand-written personal instructions there (`Always reply in 中文`, `My name is X`, etc.), install moves your existing files to `~/.claude/backup-<ISO>/` (last 5 kept automatically) before writing the spec. Since v0.5.3, install prints a `[claudemd] WARN: …` line to stderr when the existing file does not look like a claudemd spec — visible when you run `/claudemd-install` (or `install.js`) yourself. Since v0.75.0 the **SessionStart** bootstrap, where that stderr goes to `claudemd-bootstrap.log` instead, raises the same notice as an in-session banner naming the backup path (silence it with `DISABLE_USER_CONTENT_BANNER=1`). To bring your personal instructions back on uninstall, run `CLAUDEMD_SPEC_ACTION=restore /claudemd-uninstall`.
|
|
39
39
|
|
|
40
40
|
---
|
|
41
41
|
|
|
@@ -209,6 +209,29 @@ export DISABLE_COMPACT_REREAD_REMINDER=1 # v0.27.0+ — only the post-compacti
|
|
|
209
209
|
# source=="compact"); compact events still
|
|
210
210
|
# skip bootstrap/upgrade-banner either way.
|
|
211
211
|
|
|
212
|
+
export CLAUDEMD_FORCE_ASYNC_BOOTSTRAP=1 # v0.75.0+ — restores the pre-0.75.0 detached
|
|
213
|
+
# bootstrap on the FRESH-install path (no
|
|
214
|
+
# manifest yet), which otherwise runs install.js
|
|
215
|
+
# synchronously inside the 5s SessionStart
|
|
216
|
+
# budget so ~/.claude/CLAUDE.md is on disk
|
|
217
|
+
# before Claude Code assembles context. Set it
|
|
218
|
+
# if a slow/networked $HOME makes that wait
|
|
219
|
+
# unacceptable; the cost is that the spec first
|
|
220
|
+
# reaches the model one session later. Upgrades
|
|
221
|
+
# are detached either way.
|
|
222
|
+
|
|
223
|
+
export DISABLE_USER_CONTENT_BANNER=1 # v0.75.0+ — only the SessionStart notice
|
|
224
|
+
# that install.js moved a hand-written
|
|
225
|
+
# ~/.claude/CLAUDE.md into a backup dir.
|
|
226
|
+
# The backup and the install-time stderr
|
|
227
|
+
# WARN still happen; what stops is the
|
|
228
|
+
# in-session banner naming the backup
|
|
229
|
+
# path and the restore command. The hook
|
|
230
|
+
# still consumes its sentinel on this
|
|
231
|
+
# path, so opting out leaves no residue
|
|
232
|
+
# and cannot fire a late banner naming a
|
|
233
|
+
# since-pruned backup dir.
|
|
234
|
+
|
|
212
235
|
export DISABLE_BOOTSTRAP_FAIL_BANNER=1 # v0.50.0+ — only the SessionStart banner
|
|
213
236
|
# reporting that a PRIOR session's background
|
|
214
237
|
# install.js upgrade failed; the failure
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claudemd-cli",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.76.0",
|
|
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": {
|
package/scripts/lib/argv.js
CHANGED
|
@@ -11,6 +11,9 @@
|
|
|
11
11
|
// Caller catches `ArgvError` and exits 2 (distinct from numeric-validation
|
|
12
12
|
// exit 1) so wrappers can tell parsing-shape errors from validation errors.
|
|
13
13
|
|
|
14
|
+
import fs from 'node:fs';
|
|
15
|
+
import { fileURLToPath } from 'node:url';
|
|
16
|
+
|
|
14
17
|
export class ArgvError extends Error {
|
|
15
18
|
constructor(message) {
|
|
16
19
|
super(message);
|
|
@@ -18,6 +21,31 @@ export class ArgvError extends Error {
|
|
|
18
21
|
}
|
|
19
22
|
}
|
|
20
23
|
|
|
24
|
+
// invokedAsMain(import.meta.url) — "did node start the program at THIS file?"
|
|
25
|
+
//
|
|
26
|
+
// The obvious spelling compares `import.meta.url` against a `file://` string
|
|
27
|
+
// built from process.argv[1]. Both halves of that comparison lie:
|
|
28
|
+
// - node resolves import.meta.url through symlinks, argv[1] stays as typed,
|
|
29
|
+
// so a plugin dir reached through a link (a dotfiles ~/.claude, a
|
|
30
|
+
// `git worktree`, macOS /var -> /private/var) compares unequal;
|
|
31
|
+
// - a URL percent-encodes what a path spells literally, so one space in the
|
|
32
|
+
// path is `%20` on one side and ` ` on the other.
|
|
33
|
+
// Either way the main block does not run: the CLI exits 0 having printed
|
|
34
|
+
// nothing, which every caller reads as success. install.js failing this way
|
|
35
|
+
// returns "ok" while writing no manifest, so SessionStart re-enters the
|
|
36
|
+
// fresh-install branch forever (2026-09-05 audit P0-1).
|
|
37
|
+
//
|
|
38
|
+
// realpath BOTH sides and the two failure modes collapse into one comparison.
|
|
39
|
+
// Throwing inputs (argv[1] undefined under `node -e`, a path that no longer
|
|
40
|
+
// exists) mean "not the program" — false, never a crash.
|
|
41
|
+
export function invokedAsMain(importMetaUrl) {
|
|
42
|
+
try {
|
|
43
|
+
return fs.realpathSync(fileURLToPath(importMetaUrl)) === fs.realpathSync(process.argv[1]);
|
|
44
|
+
} catch {
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
21
49
|
// Discoverability helper: when `--help` or `-h` is the first non-empty arg
|
|
22
50
|
// (or anywhere in argv for scripts with no flags), print usage to stdout and
|
|
23
51
|
// exit 0. Caller invokes BEFORE parseStrict so unknown-arg rejection doesn't
|
|
@@ -122,6 +150,23 @@ export function parseStrict(argv, { bools = [], values = [] } = {}) {
|
|
|
122
150
|
}
|
|
123
151
|
return out;
|
|
124
152
|
}
|
|
153
|
+
// parseStrictOrExit — parseStrict with the exit contract every CLI in this repo
|
|
154
|
+
// repeated verbatim: an ArgvError is a usage error, so print the one-line reason
|
|
155
|
+
// and exit 2; anything else is a bug and keeps its stack. Seventeen main blocks
|
|
156
|
+
// spelled this out by hand (jscpd's nine largest non-test clones were all this
|
|
157
|
+
// block), which is seventeen chances for one of them to swallow the rethrow or
|
|
158
|
+
// exit with a different code than its own USAGE documents.
|
|
159
|
+
export function parseStrictOrExit(argv, spec = {}) {
|
|
160
|
+
try {
|
|
161
|
+
return parseStrict(argv, spec);
|
|
162
|
+
} catch (e) {
|
|
163
|
+
if (e instanceof ArgvError) {
|
|
164
|
+
console.error(e.message);
|
|
165
|
+
process.exit(2);
|
|
166
|
+
}
|
|
167
|
+
throw e;
|
|
168
|
+
}
|
|
169
|
+
}
|
|
125
170
|
|
|
126
171
|
// Space-form argv validator for the published `claudemd-cli` binary.
|
|
127
172
|
//
|