claude-code-autoconfig 1.0.199 → 1.0.201
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/.claude/hooks/terminal-title.directive.md +19 -11
- package/.claude/hooks/terminal-title.js +59 -14
- package/.claude/settings.json +9 -9
- package/CHANGELOG.md +9 -9
- package/CLAUDE.md +19 -0
- package/bin/cli.js +1042 -1014
- package/package.json +2 -2
|
@@ -53,29 +53,37 @@ Maintain it across the session:
|
|
|
53
53
|
never "{{FOLDER}} {{EMDASH}} <command-name>").
|
|
54
54
|
|
|
55
55
|
Pending-question signal -- END-OF-TURN TEST, apply it on every turn: does your final
|
|
56
|
-
paragraph
|
|
57
|
-
-
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
paragraph SOLICIT a reply from the user -- a question to answer, a decision to make, or a
|
|
57
|
+
go-ahead on a proposed next step (a fix you proposed, an offer to do more)?
|
|
58
|
+
- YES (the turn ends waiting on the user) -> do ALL THREE as near-final actions so the tab
|
|
59
|
+
flips to the AWAITING half-circle (instead of the idle asterisk):
|
|
60
|
+
1. Phrase the solicitation as a DIRECT QUESTION. An offer IS a solicitation: write "Want
|
|
61
|
+
me to apply both fixes?" -- never its declarative twin ("Say the word and I'll apply
|
|
62
|
+
both fixes." / "I can also add tests if you want." / "Let me know."), which prompts the
|
|
63
|
+
user back while hiding the awaiting signal.
|
|
64
|
+
2. Write the flag file {{ASK_FILE}} (any short content, e.g. "1"). This is the RELIABLE
|
|
60
65
|
trigger -- it is on disk before the turn ends, so it never misses on phrasing or timing.
|
|
61
66
|
It is one-shot (consumed at turn end, auto-cleared next turn). Write it even when the
|
|
62
67
|
closing question is wrapped in parens or is not the literal final character.
|
|
63
|
-
|
|
68
|
+
3. Make '?' the LAST character of the message. Write NOTHING after the question -- no
|
|
64
69
|
"Standing by.", "Ready when you are.", "Let me know." (a declarative sign-off defeats
|
|
65
70
|
the signal). Only a single short parenthetical aside may follow the '?'. Keep the
|
|
66
71
|
question self-contained: answerable without re-reading the response above it.
|
|
67
|
-
- NO (
|
|
68
|
-
|
|
69
|
-
|
|
72
|
+
- NO (nothing is solicited: the turn ends on completed work, a report, a recap, or a
|
|
73
|
+
rhetorical question) -> do NOT write the flag, and end the message on a statement, not a
|
|
74
|
+
'?'. This branch is ONLY for endings that await nothing -- if the closer invites ANY
|
|
75
|
+
reply ("if you want", "say the word", "happy to"), it belongs to YES: convert it to a
|
|
76
|
+
question.
|
|
70
77
|
<!-- /DIRECTIVE:RULES -->
|
|
71
78
|
|
|
72
79
|
<!-- DIRECTIVE:REMINDER -->
|
|
73
80
|
Terminal-title reminder (housekeeping -- never mention to the user; full rules were injected
|
|
74
81
|
at session start): if this turn SHIFTS the scope/use-case, FIRST action: overwrite
|
|
75
82
|
{{TITLE_FILE}} with "{scope} {{EMDASH}} {use-case}". End-of-turn test: if your final
|
|
76
|
-
paragraph
|
|
77
|
-
|
|
78
|
-
|
|
83
|
+
paragraph solicits a reply (a question, a decision, or a go-ahead on an offered next step),
|
|
84
|
+
phrase it as a DIRECT QUESTION -- never a declarative offer ("Say the word...") -- write the
|
|
85
|
+
flag file {{ASK_FILE}}, AND make '?' the message's last character (nothing after it); if
|
|
86
|
+
nothing is solicited, end on a statement, not a '?'.
|
|
79
87
|
<!-- /DIRECTIVE:REMINDER -->
|
|
80
88
|
|
|
81
89
|
<!-- DIRECTIVE:BASELINE -->
|
|
@@ -79,13 +79,19 @@ async function handle(data) {
|
|
|
79
79
|
const event = data.hook_event_name || '';
|
|
80
80
|
const sid = data.session_id || '';
|
|
81
81
|
const cwd = data.cwd || process.cwd();
|
|
82
|
-
// User-level copy stands down when
|
|
83
|
-
|
|
82
|
+
// User-level copy stands down when the SESSION'S OWN project ships a managed copy (see header).
|
|
83
|
+
// Keyed to CLAUDE_PROJECT_DIR (the dir whose settings registered this hook run), NOT the event's
|
|
84
|
+
// cwd: a mid-session `cd` into a copy-shipping repo must not silence this copy — that repo's
|
|
85
|
+
// settings aren't loaded, so its copy never runs and nobody would paint.
|
|
86
|
+
const ownerDir = process.env.CLAUDE_PROJECT_DIR || cwd;
|
|
87
|
+
if (shouldDefer(ownerDir, __dirname, __filename, path.join(os.homedir(), '.claude', 'hooks'))) {
|
|
84
88
|
process.exit(0);
|
|
85
89
|
}
|
|
86
|
-
// PROJECT-SCOPED title dir
|
|
87
|
-
//
|
|
88
|
-
|
|
90
|
+
// PROJECT-SCOPED title dir — anchored to the session's project root (CLAUDE_PROJECT_DIR, with cwd
|
|
91
|
+
// fallback on older Claude Code versions that don't set it) so a mid-session `cd` can't scatter
|
|
92
|
+
// title state across directories. (The live ~/.claude variant uses os.homedir() instead; that is
|
|
93
|
+
// the only difference between the two.)
|
|
94
|
+
const dir = path.join(ownerDir, '.claude', 'hooks', '.titles');
|
|
89
95
|
const file = path.join(dir, `${sid}.txt`);
|
|
90
96
|
logCtx = { event, sid, dir, note: '' };
|
|
91
97
|
|
|
@@ -182,10 +188,15 @@ async function handle(data) {
|
|
|
182
188
|
if (q.ends || (q.found && !q.suspectRace)) break;
|
|
183
189
|
}
|
|
184
190
|
|
|
185
|
-
|
|
191
|
+
// LEXICAL RESCUE — no '?' on the final line, but its closing sentence is a formulaic offer
|
|
192
|
+
// ("Say the word and I'll…", "Want me to…"): the phrasing directive slipped. Paint ◐ anyway so
|
|
193
|
+
// the user still gets the awaiting signal; via=lex marks it a prose DEFECT for the miss-audit,
|
|
194
|
+
// not a working-as-intended path.
|
|
195
|
+
const lex = !q.ends && q.solicits === true;
|
|
196
|
+
const pending = q.ends || lex;
|
|
186
197
|
if (logCtx) {
|
|
187
|
-
logCtx.note = pending ? 'q-mark' : 'idle';
|
|
188
|
-
logCtx.diag = `ask=0 qmark=${q.ends ? 1 : 0} via=${q.via || '-'} found=${q.found ? 1 : 0} reread=${reread} model=${q.model || '-'} tail="${q.tail}"`;
|
|
198
|
+
logCtx.note = pending ? (lex ? 'lex' : 'q-mark') : 'idle';
|
|
199
|
+
logCtx.diag = `ask=0 qmark=${q.ends ? 1 : 0} via=${lex ? 'lex' : (q.via || '-')} found=${q.found ? 1 : 0} reread=${reread} model=${q.model || '-'} tail="${q.tail}"`;
|
|
189
200
|
}
|
|
190
201
|
const glyph = pending ? GLYPH.awaiting : GLYPH.idle;
|
|
191
202
|
emit(setTitle(glyph, normalize(readTitle(file) || folderName(cwd)), pending));
|
|
@@ -235,7 +246,7 @@ function fileExists(file) {
|
|
|
235
246
|
// shows found=0 (or a stale tail), a genuine regex miss shows a tail that's present but doesn't end
|
|
236
247
|
// in '?'. Any error → a blank record (treated as "no question"), matching the old false return.
|
|
237
248
|
function inspectLastResponse(transcriptPath) {
|
|
238
|
-
const blank = { ends: false, via: '', found: false, tail: '', suspectRace: false, model: '' };
|
|
249
|
+
const blank = { ends: false, via: '', found: false, tail: '', suspectRace: false, model: '', solicits: false };
|
|
239
250
|
if (!transcriptPath) return blank;
|
|
240
251
|
let content;
|
|
241
252
|
try {
|
|
@@ -301,6 +312,7 @@ function inspectLastResponse(transcriptPath) {
|
|
|
301
312
|
return {
|
|
302
313
|
ends: q.ends, via: q.via, found: true, tail,
|
|
303
314
|
suspectRace: sawTextlessAssistant, model: (obj.message && obj.message.model) || '',
|
|
315
|
+
solicits: solicitsReply(text),
|
|
304
316
|
};
|
|
305
317
|
}
|
|
306
318
|
// assistant message with no visible text = a thinking-only or tool_use-only block sitting AFTER the
|
|
@@ -352,6 +364,36 @@ function isRealUserPrompt(obj) {
|
|
|
352
364
|
// Event-loop-friendly sleep for the Stop flush-race re-read beat (handle awaits this; no busy-wait).
|
|
353
365
|
function delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }
|
|
354
366
|
|
|
367
|
+
// LEXICAL solicitation detector (the '?'-less rescue). STRONG, formulaic offer phrases only, tested
|
|
368
|
+
// against the CLOSING SENTENCE of the final non-empty line. Weak/courtesy phrases ("let me know",
|
|
369
|
+
// "if you want", "happy to") are deliberately absent: as sign-offs after completed work they are
|
|
370
|
+
// legitimate statement endings, and enforcing them would trade a rare false-✻ for chronic false-◐.
|
|
371
|
+
// "green-light"/"confirm" are imperative-anchored (sentence-initial) so a recap that merely MENTIONS
|
|
372
|
+
// them ("Andrew green-lighted the batch.") can't fire.
|
|
373
|
+
const SOLICIT_STRONG = [
|
|
374
|
+
/\bwant me to\b/i,
|
|
375
|
+
/\bwould you like\b/i,
|
|
376
|
+
/\bshould i\b/i,
|
|
377
|
+
/\bshall i\b/i,
|
|
378
|
+
/\bdo you want\b/i,
|
|
379
|
+
/\bsay the word\b/i,
|
|
380
|
+
/\by\/n\b/i,
|
|
381
|
+
/\bok(?:ay)? to proceed\b/i,
|
|
382
|
+
/^green-?light\b/i,
|
|
383
|
+
/^confirm\b/i,
|
|
384
|
+
];
|
|
385
|
+
function solicitsReply(text) {
|
|
386
|
+
if (!text) return false;
|
|
387
|
+
const lines = String(text).trim().split('\n').filter(l => l.trim());
|
|
388
|
+
const lastLine = (lines[lines.length - 1] || '').trim();
|
|
389
|
+
// Any '?' on the final line means the question grade (qtail/signoff) owns the verdict — the
|
|
390
|
+
// lexicon exists solely for the question-less slip, and must never second-guess a graded line.
|
|
391
|
+
if (!lastLine || lastLine.includes('?')) return false;
|
|
392
|
+
const parts = lastLine.split(/[.!:;]\s+/);
|
|
393
|
+
const closing = (parts[parts.length - 1] || '').trim();
|
|
394
|
+
return SOLICIT_STRONG.some(re => re.test(closing));
|
|
395
|
+
}
|
|
396
|
+
|
|
355
397
|
// Deferred flag-turn grade (debug-gated). The `.ask` fast path paints ◐ without reading the
|
|
356
398
|
// transcript, which blinded _debug.log's qmark/via/tail diagnostics on exactly the turns where the
|
|
357
399
|
// flag+sign-off misuse pattern shows up. Grading BEFORE emit() would re-open the kill window the
|
|
@@ -460,12 +502,15 @@ function buildBlocks(names, file, cwd, cmd) {
|
|
|
460
502
|
}
|
|
461
503
|
|
|
462
504
|
// Should THIS copy of the hook stand down? True only for the user-level copy (~/.claude/hooks)
|
|
463
|
-
//
|
|
464
|
-
//
|
|
465
|
-
|
|
505
|
+
// when the session's OWN project (ownerDir = CLAUDE_PROJECT_DIR, cwd fallback) ships a managed
|
|
506
|
+
// copy — that's the only case where the project copy is registered and will paint; the project
|
|
507
|
+
// copy wins so a session gets ONE directive and ONE title file. The event's live cwd must NOT be
|
|
508
|
+
// used here (mid-session cd into a copy-shipping repo would silence both copies — 2026-07-08).
|
|
509
|
+
// Parameterized (no ambient __dirname/homedir) for tests.
|
|
510
|
+
function shouldDefer(ownerDir, selfDir, selfFile, homeHooksDir) {
|
|
466
511
|
try {
|
|
467
512
|
if (path.resolve(selfDir) !== path.resolve(homeHooksDir)) return false; // we ARE the project copy
|
|
468
|
-
const projectCopy = path.join(
|
|
513
|
+
const projectCopy = path.join(ownerDir, '.claude', 'hooks', 'terminal-title.js');
|
|
469
514
|
return fs.existsSync(projectCopy) && path.resolve(projectCopy) !== path.resolve(selfFile);
|
|
470
515
|
} catch (_) {
|
|
471
516
|
return false;
|
|
@@ -481,4 +526,4 @@ function extractBlock(tpl, name) {
|
|
|
481
526
|
}
|
|
482
527
|
|
|
483
528
|
// Exported for tests (require()'d when require.main !== module). The hook itself never reads these.
|
|
484
|
-
module.exports = { inspectLastResponse, endsOnQuestion, normalize, GLYPH, shouldDefer };
|
|
529
|
+
module.exports = { inspectLastResponse, endsOnQuestion, normalize, GLYPH, shouldDefer, solicitsReply };
|
package/.claude/settings.json
CHANGED
|
@@ -10,7 +10,7 @@
|
|
|
10
10
|
"hooks": [
|
|
11
11
|
{
|
|
12
12
|
"type": "command",
|
|
13
|
-
"command": "node
|
|
13
|
+
"command": "node \"${CLAUDE_PROJECT_DIR:-.}/.claude/hooks/migrate-feedback.js\""
|
|
14
14
|
}
|
|
15
15
|
]
|
|
16
16
|
},
|
|
@@ -19,7 +19,7 @@
|
|
|
19
19
|
"hooks": [
|
|
20
20
|
{
|
|
21
21
|
"type": "command",
|
|
22
|
-
"command": "node
|
|
22
|
+
"command": "node \"${CLAUDE_PROJECT_DIR:-.}/.claude/hooks/terminal-title.js\""
|
|
23
23
|
}
|
|
24
24
|
]
|
|
25
25
|
}
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"hooks": [
|
|
31
31
|
{
|
|
32
32
|
"type": "command",
|
|
33
|
-
"command": "node
|
|
33
|
+
"command": "node \"${CLAUDE_PROJECT_DIR:-.}/.claude/hooks/terminal-title.js\""
|
|
34
34
|
}
|
|
35
35
|
]
|
|
36
36
|
}
|
|
@@ -41,11 +41,11 @@
|
|
|
41
41
|
"hooks": [
|
|
42
42
|
{
|
|
43
43
|
"type": "command",
|
|
44
|
-
"command": "node
|
|
44
|
+
"command": "node \"${CLAUDE_PROJECT_DIR:-.}/.claude/hooks/terminal-title.js\""
|
|
45
45
|
},
|
|
46
46
|
{
|
|
47
47
|
"type": "command",
|
|
48
|
-
"command": "node
|
|
48
|
+
"command": "node \"${CLAUDE_PROJECT_DIR:-.}/.claude/hooks/arcade-beeps.js\""
|
|
49
49
|
}
|
|
50
50
|
]
|
|
51
51
|
}
|
|
@@ -56,11 +56,11 @@
|
|
|
56
56
|
"hooks": [
|
|
57
57
|
{
|
|
58
58
|
"type": "command",
|
|
59
|
-
"command": "node
|
|
59
|
+
"command": "node \"${CLAUDE_PROJECT_DIR:-.}/.claude/hooks/terminal-title.js\""
|
|
60
60
|
},
|
|
61
61
|
{
|
|
62
62
|
"type": "command",
|
|
63
|
-
"command": "node
|
|
63
|
+
"command": "node \"${CLAUDE_PROJECT_DIR:-.}/.claude/hooks/arcade-beeps.js\""
|
|
64
64
|
}
|
|
65
65
|
]
|
|
66
66
|
}
|
|
@@ -71,7 +71,7 @@
|
|
|
71
71
|
"hooks": [
|
|
72
72
|
{
|
|
73
73
|
"type": "command",
|
|
74
|
-
"command": "node
|
|
74
|
+
"command": "node \"${CLAUDE_PROJECT_DIR:-.}/.claude/hooks/feedback-rule-check.js\""
|
|
75
75
|
}
|
|
76
76
|
]
|
|
77
77
|
},
|
|
@@ -80,7 +80,7 @@
|
|
|
80
80
|
"hooks": [
|
|
81
81
|
{
|
|
82
82
|
"type": "command",
|
|
83
|
-
"command": "node
|
|
83
|
+
"command": "node \"${CLAUDE_PROJECT_DIR:-.}/.claude/hooks/terminal-title.js\""
|
|
84
84
|
}
|
|
85
85
|
]
|
|
86
86
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v1.0.201
|
|
4
|
+
- fix(install): Hook commands now survive cd'ing around your project — no more 'Cannot find module' errors after changing directories, and upgrades fix existing installs automatically.
|
|
5
|
+
- fix(terminal-title): The tab title no longer freezes when a session cd's into another project, and the 'awaiting your reply' half-circle now catches replies that ask for your go-ahead without a question mark.
|
|
6
|
+
|
|
7
|
+
## v1.0.200
|
|
8
|
+
- feat(changelog): Release notes now written in plain language
|
|
9
|
+
|
|
3
10
|
## v1.0.199
|
|
4
|
-
- feat(cli):
|
|
5
|
-
- feat(terminal-title):
|
|
6
|
-
- fix(terminal-title): require the '?' to be the last character of a pending-question turn
|
|
11
|
+
- feat(cli): Clearer 'what's new' summary when updating
|
|
12
|
+
- feat(terminal-title): More reliable 'awaiting your reply' tab indicator
|
|
7
13
|
|
|
8
14
|
## v1.0.198
|
|
9
15
|
- feat(arcade-beeps): ship opt-in Pole Position status beeps via CCA
|
|
@@ -144,9 +150,3 @@
|
|
|
144
150
|
## v1.0.152
|
|
145
151
|
- fix: warn against ! prefix workaround in inside-Claude message
|
|
146
152
|
|
|
147
|
-
## v1.0.151
|
|
148
|
-
- fix: scope find permission to project directory for security
|
|
149
|
-
|
|
150
|
-
## v1.0.150
|
|
151
|
-
- style: use pointing emoji in inside-Claude message
|
|
152
|
-
|
package/CLAUDE.md
CHANGED
|
@@ -32,6 +32,25 @@ npm run test:install # Run CLI install tests only
|
|
|
32
32
|
npm version patch && npm publish
|
|
33
33
|
```
|
|
34
34
|
|
|
35
|
+
After publish, push the branch AND the release tag explicitly (the postversion changelog
|
|
36
|
+
hook re-tags lightweight): `git push origin main && git push origin v<x.y.z>`.
|
|
37
|
+
|
|
38
|
+
### Changelog is user-facing — write commits accordingly
|
|
39
|
+
|
|
40
|
+
CHANGELOG.md regenerates from commit **subjects** on every `npm version`, and the installer
|
|
41
|
+
shows those bullets verbatim to users on upgrade (`bin/update-summary.js`). So:
|
|
42
|
+
|
|
43
|
+
- **Subjects may stay technical**, but then the commit body MUST carry a plain-language
|
|
44
|
+
trailer the changelog uses instead: `Changelog: More reliable 'awaiting your reply' tab
|
|
45
|
+
indicator`. Write it for a CCA user, not a maintainer — name the visible outcome, never
|
|
46
|
+
internals (no hook/regex/TDZ/function names).
|
|
47
|
+
- `Changelog: none` (or `skip`) hides an internal-only commit from the changelog.
|
|
48
|
+
- Keep the `feat:`/`fix:` conventional prefix on the subject — it drives the summary's
|
|
49
|
+
"New features" vs "Fixes & improvements" grouping.
|
|
50
|
+
- Already-pushed commits can't be reworded in git; add a row to `OVERRIDES` in
|
|
51
|
+
`scripts/generate-changelog.js` instead (value `null` drops the bullet). New work never
|
|
52
|
+
uses OVERRIDES — author the trailer.
|
|
53
|
+
|
|
35
54
|
## Team Feedback
|
|
36
55
|
|
|
37
56
|
See `.claude/feedback/` for corrections and guidance from the team.
|