claude-code-autoconfig 1.0.201 → 1.0.202
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.
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Turn OFF Pole Position tab status beeps
|
|
3
|
-
allowed-tools: Bash
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
Run exactly this command, then reply with a single confirmation line and nothing else:
|
|
7
|
-
|
|
8
|
-
```
|
|
9
|
-
rm -f ~/.claude/sounds/arcade-beeps.enabled && echo "arcade beeps DISABLED."
|
|
10
|
-
```
|
|
1
|
+
---
|
|
2
|
+
description: Turn OFF Pole Position tab status beeps
|
|
3
|
+
allowed-tools: Bash
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Run exactly this command, then reply with a single confirmation line and nothing else:
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
rm -f ~/.claude/sounds/arcade-beeps.enabled && echo "arcade beeps DISABLED."
|
|
10
|
+
```
|
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
---
|
|
2
|
-
description: Turn ON Pole Position tab status beeps (awaiting = low tone, complete = high tone)
|
|
3
|
-
allowed-tools: Bash
|
|
4
|
-
---
|
|
5
|
-
|
|
6
|
-
Run exactly this command, then reply with a single confirmation line and nothing else:
|
|
7
|
-
|
|
8
|
-
```
|
|
9
|
-
mkdir -p ~/.claude/sounds && touch ~/.claude/sounds/arcade-beeps.enabled && echo "arcade beeps ENABLED (every turn-end beeps: awaiting=low, complete=high). Disable with /disable-arcade-beeps."
|
|
10
|
-
```
|
|
1
|
+
---
|
|
2
|
+
description: Turn ON Pole Position tab status beeps (awaiting = low tone, complete = high tone)
|
|
3
|
+
allowed-tools: Bash
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Run exactly this command, then reply with a single confirmation line and nothing else:
|
|
7
|
+
|
|
8
|
+
```
|
|
9
|
+
mkdir -p ~/.claude/sounds && touch ~/.claude/sounds/arcade-beeps.enabled && echo "arcade beeps ENABLED (every turn-end beeps: awaiting=low, complete=high). Disable with /disable-arcade-beeps."
|
|
10
|
+
```
|
|
@@ -1,136 +1,136 @@
|
|
|
1
|
-
#!/usr/bin/env node
|
|
2
|
-
/**
|
|
3
|
-
* arcade-beeps — optional Pole-Position status cues for the Claude Code tab.
|
|
4
|
-
* Companion to terminal-title.js. Registered (settings.json) on Stop + Notification.
|
|
5
|
-
*
|
|
6
|
-
* OFF unless the enable flag exists: ~/.claude/sounds/arcade-beeps.enabled
|
|
7
|
-
* toggle via /enable-arcade-beeps /disable-arcade-beeps
|
|
8
|
-
* The flag is GLOBAL (homedir), so one toggle covers every project you've installed into.
|
|
9
|
-
*
|
|
10
|
-
* Mapping (matches the ◐/✻ tab glyph that terminal-title.js paints on the SAME event):
|
|
11
|
-
* Stop, turn ended on a question -> ◐ awaiting -> get-ready tick (pp3-getready-G4.wav, G4/384Hz)
|
|
12
|
-
* Stop, turn ended normally -> ✻ complete -> GO beep (pp3-go-F#5.wav, F#5/759Hz)
|
|
13
|
-
* Notification (permission_prompt) -> ◐ awaiting -> get-ready tick
|
|
14
|
-
*
|
|
15
|
-
* Tones are an extracted+smoothed take on the Pole Position race-start cue (arcade-ping
|
|
16
|
-
* envelope). LOWER tick = awaiting, HIGHER GO = complete.
|
|
17
|
-
*
|
|
18
|
-
* Assets resolve relative to THIS file (../sounds) so a per-project CCA install finds its own
|
|
19
|
-
* copy at <project>/.claude/sounds — no dependency on a global sounds dir.
|
|
20
|
-
*
|
|
21
|
-
* Playback is cross-platform and best-effort: PowerShell SoundPlayer on Windows, afplay on
|
|
22
|
-
* macOS, paplay||aplay on Linux. If none is present the hook simply stays silent — never errors.
|
|
23
|
-
*
|
|
24
|
-
* Playback BLOCKS (spawnSync) so the sound finishes inside the hook's lifetime — a detached
|
|
25
|
-
* fire-and-forget child can be killed by the hook runner's job/process-group cleanup before it
|
|
26
|
-
* plays. Blocking costs ~0.5s on turn-end but is reliable. Still fail-safe: wrapped so it never
|
|
27
|
-
* throws into the turn, and every path ends in exit(0).
|
|
28
|
-
*
|
|
29
|
-
* State detection reuses terminal-title.js's exported inspectLastResponse (lazy-required only
|
|
30
|
-
* when enabled) so sound and glyph agree. A tiny inline '?' check is the fallback.
|
|
31
|
-
*
|
|
32
|
-
* Diagnostic log (bounded ~64KB) at ~/.claude/hooks/.titles/arcade-beeps.log records every
|
|
33
|
-
* invocation + choice, so we can prove whether the hook fires on a real Stop.
|
|
34
|
-
*/
|
|
35
|
-
const fs = require('fs');
|
|
36
|
-
const os = require('os');
|
|
37
|
-
const path = require('path');
|
|
38
|
-
const { spawnSync } = require('child_process');
|
|
39
|
-
|
|
40
|
-
const ASSET_DIR = path.join(__dirname, '..', 'sounds'); // wavs ship beside the hook
|
|
41
|
-
const FLAG = path.join(os.homedir(), '.claude', 'sounds', 'arcade-beeps.enabled'); // global toggle
|
|
42
|
-
const LOG = path.join(os.homedir(), '.claude', 'hooks', '.titles', 'arcade-beeps.log');
|
|
43
|
-
const DEBUG = process.env.ARCADE_BEEPS_DEBUG === '1';
|
|
44
|
-
|
|
45
|
-
function logLine(msg) {
|
|
46
|
-
try {
|
|
47
|
-
try { if (fs.statSync(LOG).size > 64 * 1024) fs.renameSync(LOG, `${LOG}.1`); } catch (_) { /* none yet */ }
|
|
48
|
-
fs.appendFileSync(LOG, `${new Date().toISOString()} ${msg}\n`);
|
|
49
|
-
} catch (_) { /* logging must never throw */ }
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function enabled() { try { return fs.existsSync(FLAG); } catch (_) { return false; } }
|
|
53
|
-
function delay(ms) { return new Promise(r => setTimeout(r, ms)); }
|
|
54
|
-
|
|
55
|
-
// Pick a blocking audio player for the current OS. Returns [cmd, args].
|
|
56
|
-
function playerFor(wav) {
|
|
57
|
-
if (process.platform === 'win32') {
|
|
58
|
-
return ['powershell', ['-NoProfile', '-Command', `(New-Object Media.SoundPlayer '${wav}').PlaySync()`]];
|
|
59
|
-
}
|
|
60
|
-
if (process.platform === 'darwin') {
|
|
61
|
-
return ['afplay', [wav]];
|
|
62
|
-
}
|
|
63
|
-
// linux / other: prefer paplay (PulseAudio/PipeWire), fall back to aplay (ALSA)
|
|
64
|
-
return ['sh', ['-c', `paplay "${wav}" 2>/dev/null || aplay "${wav}" 2>/dev/null`]];
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
function play(wavName, why) {
|
|
68
|
-
if (DEBUG) process.stderr.write(`arcade-beeps: chose ${wavName} (${why})\n`);
|
|
69
|
-
logLine(`play ${wavName} (${why})`);
|
|
70
|
-
try {
|
|
71
|
-
const wav = path.join(ASSET_DIR, wavName);
|
|
72
|
-
if (!fs.existsSync(wav)) { logLine(`MISSING ${wav}`); return; }
|
|
73
|
-
// Block until the sound finishes so it can't be reaped with the hook process.
|
|
74
|
-
const [cmd, cmdArgs] = playerFor(wav);
|
|
75
|
-
spawnSync(cmd, cmdArgs, { stdio: 'ignore', windowsHide: true });
|
|
76
|
-
} catch (e) { logLine(`play-error ${e && e.message}`); }
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
// Fallback: does the last visible assistant text end on a question? (Mirrors terminal-title.js's regex.)
|
|
80
|
-
function endsOnQuestionInline(transcriptPath) {
|
|
81
|
-
try {
|
|
82
|
-
const lines = fs.readFileSync(transcriptPath, 'utf8').split('\n');
|
|
83
|
-
for (let i = lines.length - 1; i >= 0; i--) {
|
|
84
|
-
const l = lines[i].trim(); if (!l) continue;
|
|
85
|
-
let o; try { o = JSON.parse(l); } catch (_) { continue; }
|
|
86
|
-
if (!o || o.type !== 'assistant' || !o.message) continue;
|
|
87
|
-
const c = o.message.content; let t = '';
|
|
88
|
-
if (typeof c === 'string') t = c;
|
|
89
|
-
else if (Array.isArray(c)) t = c.filter(b => b && b.type === 'text' && typeof b.text === 'string').map(b => b.text).join('\n');
|
|
90
|
-
if (t.trim()) return /\?[\s)*_"]*(\([^()]*\)[\s.*_"]*)?$/.test(t);
|
|
91
|
-
}
|
|
92
|
-
} catch (_) { /* ignore */ }
|
|
93
|
-
return false;
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
async function main(input) {
|
|
97
|
-
let data = {};
|
|
98
|
-
try { data = JSON.parse(input); } catch (_) { /* ignore */ }
|
|
99
|
-
const event = data.hook_event_name || '?';
|
|
100
|
-
const en = enabled();
|
|
101
|
-
logLine(`invoked event=${event} enabled=${en ? 1 : 0}`);
|
|
102
|
-
if (!en) return; // opt-in only
|
|
103
|
-
|
|
104
|
-
if (event === 'Notification') { play('pp3-getready-G4.wav', 'notification'); return; }
|
|
105
|
-
if (event !== 'Stop') return;
|
|
106
|
-
|
|
107
|
-
// awaiting (question) vs complete — same signals terminal-title.js uses, so the tone matches the glyph.
|
|
108
|
-
const sid = data.session_id || '';
|
|
109
|
-
const askFile = path.join(os.homedir(), '.claude', 'hooks', '.titles', `${sid}.ask`);
|
|
110
|
-
let pending = false;
|
|
111
|
-
try { pending = fs.existsSync(askFile); } catch (_) { /* ignore */ }
|
|
112
|
-
|
|
113
|
-
if (!pending) {
|
|
114
|
-
let inspect = null;
|
|
115
|
-
try { ({ inspectLastResponse: inspect } = require('./terminal-title.js')); } catch (_) { /* fallback below */ }
|
|
116
|
-
if (inspect) {
|
|
117
|
-
let q = inspect(data.transcript_path);
|
|
118
|
-
let n = 0;
|
|
119
|
-
while (!q.ends && (q.suspectRace || !q.found) && n < 5) { await delay(120); q = inspect(data.transcript_path); n++; }
|
|
120
|
-
pending = q.ends;
|
|
121
|
-
} else {
|
|
122
|
-
pending = endsOnQuestionInline(data.transcript_path);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
play(pending ? 'pp3-getready-G4.wav' : 'pp3-go-F#5.wav', pending ? 'awaiting' : 'complete');
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
if (require.main === module) {
|
|
130
|
-
let input = '';
|
|
131
|
-
process.stdin.setEncoding('utf8');
|
|
132
|
-
process.stdin.on('data', c => (input += c));
|
|
133
|
-
process.stdin.on('end', async () => { try { await main(input); } catch (_) { /* ignore */ } process.exit(0); });
|
|
134
|
-
}
|
|
135
|
-
|
|
136
|
-
module.exports = { endsOnQuestionInline };
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* arcade-beeps — optional Pole-Position status cues for the Claude Code tab.
|
|
4
|
+
* Companion to terminal-title.js. Registered (settings.json) on Stop + Notification.
|
|
5
|
+
*
|
|
6
|
+
* OFF unless the enable flag exists: ~/.claude/sounds/arcade-beeps.enabled
|
|
7
|
+
* toggle via /enable-arcade-beeps /disable-arcade-beeps
|
|
8
|
+
* The flag is GLOBAL (homedir), so one toggle covers every project you've installed into.
|
|
9
|
+
*
|
|
10
|
+
* Mapping (matches the ◐/✻ tab glyph that terminal-title.js paints on the SAME event):
|
|
11
|
+
* Stop, turn ended on a question -> ◐ awaiting -> get-ready tick (pp3-getready-G4.wav, G4/384Hz)
|
|
12
|
+
* Stop, turn ended normally -> ✻ complete -> GO beep (pp3-go-F#5.wav, F#5/759Hz)
|
|
13
|
+
* Notification (permission_prompt) -> ◐ awaiting -> get-ready tick
|
|
14
|
+
*
|
|
15
|
+
* Tones are an extracted+smoothed take on the Pole Position race-start cue (arcade-ping
|
|
16
|
+
* envelope). LOWER tick = awaiting, HIGHER GO = complete.
|
|
17
|
+
*
|
|
18
|
+
* Assets resolve relative to THIS file (../sounds) so a per-project CCA install finds its own
|
|
19
|
+
* copy at <project>/.claude/sounds — no dependency on a global sounds dir.
|
|
20
|
+
*
|
|
21
|
+
* Playback is cross-platform and best-effort: PowerShell SoundPlayer on Windows, afplay on
|
|
22
|
+
* macOS, paplay||aplay on Linux. If none is present the hook simply stays silent — never errors.
|
|
23
|
+
*
|
|
24
|
+
* Playback BLOCKS (spawnSync) so the sound finishes inside the hook's lifetime — a detached
|
|
25
|
+
* fire-and-forget child can be killed by the hook runner's job/process-group cleanup before it
|
|
26
|
+
* plays. Blocking costs ~0.5s on turn-end but is reliable. Still fail-safe: wrapped so it never
|
|
27
|
+
* throws into the turn, and every path ends in exit(0).
|
|
28
|
+
*
|
|
29
|
+
* State detection reuses terminal-title.js's exported inspectLastResponse (lazy-required only
|
|
30
|
+
* when enabled) so sound and glyph agree. A tiny inline '?' check is the fallback.
|
|
31
|
+
*
|
|
32
|
+
* Diagnostic log (bounded ~64KB) at ~/.claude/hooks/.titles/arcade-beeps.log records every
|
|
33
|
+
* invocation + choice, so we can prove whether the hook fires on a real Stop.
|
|
34
|
+
*/
|
|
35
|
+
const fs = require('fs');
|
|
36
|
+
const os = require('os');
|
|
37
|
+
const path = require('path');
|
|
38
|
+
const { spawnSync } = require('child_process');
|
|
39
|
+
|
|
40
|
+
const ASSET_DIR = path.join(__dirname, '..', 'sounds'); // wavs ship beside the hook
|
|
41
|
+
const FLAG = path.join(os.homedir(), '.claude', 'sounds', 'arcade-beeps.enabled'); // global toggle
|
|
42
|
+
const LOG = path.join(os.homedir(), '.claude', 'hooks', '.titles', 'arcade-beeps.log');
|
|
43
|
+
const DEBUG = process.env.ARCADE_BEEPS_DEBUG === '1';
|
|
44
|
+
|
|
45
|
+
function logLine(msg) {
|
|
46
|
+
try {
|
|
47
|
+
try { if (fs.statSync(LOG).size > 64 * 1024) fs.renameSync(LOG, `${LOG}.1`); } catch (_) { /* none yet */ }
|
|
48
|
+
fs.appendFileSync(LOG, `${new Date().toISOString()} ${msg}\n`);
|
|
49
|
+
} catch (_) { /* logging must never throw */ }
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function enabled() { try { return fs.existsSync(FLAG); } catch (_) { return false; } }
|
|
53
|
+
function delay(ms) { return new Promise(r => setTimeout(r, ms)); }
|
|
54
|
+
|
|
55
|
+
// Pick a blocking audio player for the current OS. Returns [cmd, args].
|
|
56
|
+
function playerFor(wav) {
|
|
57
|
+
if (process.platform === 'win32') {
|
|
58
|
+
return ['powershell', ['-NoProfile', '-Command', `(New-Object Media.SoundPlayer '${wav}').PlaySync()`]];
|
|
59
|
+
}
|
|
60
|
+
if (process.platform === 'darwin') {
|
|
61
|
+
return ['afplay', [wav]];
|
|
62
|
+
}
|
|
63
|
+
// linux / other: prefer paplay (PulseAudio/PipeWire), fall back to aplay (ALSA)
|
|
64
|
+
return ['sh', ['-c', `paplay "${wav}" 2>/dev/null || aplay "${wav}" 2>/dev/null`]];
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function play(wavName, why) {
|
|
68
|
+
if (DEBUG) process.stderr.write(`arcade-beeps: chose ${wavName} (${why})\n`);
|
|
69
|
+
logLine(`play ${wavName} (${why})`);
|
|
70
|
+
try {
|
|
71
|
+
const wav = path.join(ASSET_DIR, wavName);
|
|
72
|
+
if (!fs.existsSync(wav)) { logLine(`MISSING ${wav}`); return; }
|
|
73
|
+
// Block until the sound finishes so it can't be reaped with the hook process.
|
|
74
|
+
const [cmd, cmdArgs] = playerFor(wav);
|
|
75
|
+
spawnSync(cmd, cmdArgs, { stdio: 'ignore', windowsHide: true });
|
|
76
|
+
} catch (e) { logLine(`play-error ${e && e.message}`); }
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Fallback: does the last visible assistant text end on a question? (Mirrors terminal-title.js's regex.)
|
|
80
|
+
function endsOnQuestionInline(transcriptPath) {
|
|
81
|
+
try {
|
|
82
|
+
const lines = fs.readFileSync(transcriptPath, 'utf8').split('\n');
|
|
83
|
+
for (let i = lines.length - 1; i >= 0; i--) {
|
|
84
|
+
const l = lines[i].trim(); if (!l) continue;
|
|
85
|
+
let o; try { o = JSON.parse(l); } catch (_) { continue; }
|
|
86
|
+
if (!o || o.type !== 'assistant' || !o.message) continue;
|
|
87
|
+
const c = o.message.content; let t = '';
|
|
88
|
+
if (typeof c === 'string') t = c;
|
|
89
|
+
else if (Array.isArray(c)) t = c.filter(b => b && b.type === 'text' && typeof b.text === 'string').map(b => b.text).join('\n');
|
|
90
|
+
if (t.trim()) return /\?[\s)*_"]*(\([^()]*\)[\s.*_"]*)?$/.test(t);
|
|
91
|
+
}
|
|
92
|
+
} catch (_) { /* ignore */ }
|
|
93
|
+
return false;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
async function main(input) {
|
|
97
|
+
let data = {};
|
|
98
|
+
try { data = JSON.parse(input); } catch (_) { /* ignore */ }
|
|
99
|
+
const event = data.hook_event_name || '?';
|
|
100
|
+
const en = enabled();
|
|
101
|
+
logLine(`invoked event=${event} enabled=${en ? 1 : 0}`);
|
|
102
|
+
if (!en) return; // opt-in only
|
|
103
|
+
|
|
104
|
+
if (event === 'Notification') { play('pp3-getready-G4.wav', 'notification'); return; }
|
|
105
|
+
if (event !== 'Stop') return;
|
|
106
|
+
|
|
107
|
+
// awaiting (question) vs complete — same signals terminal-title.js uses, so the tone matches the glyph.
|
|
108
|
+
const sid = data.session_id || '';
|
|
109
|
+
const askFile = path.join(os.homedir(), '.claude', 'hooks', '.titles', `${sid}.ask`);
|
|
110
|
+
let pending = false;
|
|
111
|
+
try { pending = fs.existsSync(askFile); } catch (_) { /* ignore */ }
|
|
112
|
+
|
|
113
|
+
if (!pending) {
|
|
114
|
+
let inspect = null;
|
|
115
|
+
try { ({ inspectLastResponse: inspect } = require('./terminal-title.js')); } catch (_) { /* fallback below */ }
|
|
116
|
+
if (inspect) {
|
|
117
|
+
let q = inspect(data.transcript_path);
|
|
118
|
+
let n = 0;
|
|
119
|
+
while (!q.ends && (q.suspectRace || !q.found) && n < 5) { await delay(120); q = inspect(data.transcript_path); n++; }
|
|
120
|
+
pending = q.ends;
|
|
121
|
+
} else {
|
|
122
|
+
pending = endsOnQuestionInline(data.transcript_path);
|
|
123
|
+
}
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
play(pending ? 'pp3-getready-G4.wav' : 'pp3-go-F#5.wav', pending ? 'awaiting' : 'complete');
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (require.main === module) {
|
|
130
|
+
let input = '';
|
|
131
|
+
process.stdin.setEncoding('utf8');
|
|
132
|
+
process.stdin.on('data', c => (input += c));
|
|
133
|
+
process.stdin.on('end', async () => { try { await main(input); } catch (_) { /* ignore */ } process.exit(0); });
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
module.exports = { endsOnQuestionInline };
|
|
@@ -95,6 +95,16 @@ async function handle(data) {
|
|
|
95
95
|
const file = path.join(dir, `${sid}.txt`);
|
|
96
96
|
logCtx = { event, sid, dir, note: '' };
|
|
97
97
|
|
|
98
|
+
// HARNESS-CONTRACT CANARY (debug-only surface): if Claude Code stops supplying a field the
|
|
99
|
+
// keying depends on, every fix above silently degrades to its fallback. Record the gap so the
|
|
100
|
+
// stale-glyph audit surfaces a platform change as one log line instead of the next mystery.
|
|
101
|
+
const degraded = [];
|
|
102
|
+
if (!process.env.CLAUDE_PROJECT_DIR) degraded.push('CLAUDE_PROJECT_DIR');
|
|
103
|
+
if (!data.cwd) degraded.push('cwd');
|
|
104
|
+
if (!sid) degraded.push('session_id');
|
|
105
|
+
if (event === 'Stop' && !data.transcript_path) degraded.push('transcript_path');
|
|
106
|
+
if (degraded.length) logCtx.contract = degraded.join(',');
|
|
107
|
+
|
|
98
108
|
if (event === 'UserPromptSubmit') {
|
|
99
109
|
// Ensure the state dir exists, but NOT the file — the model's Write tool refuses to overwrite a
|
|
100
110
|
// file it hasn't read, so a pre-created empty file would make its first title write fail.
|
|
@@ -224,9 +234,10 @@ function titleLog(glyph, title, ring) {
|
|
|
224
234
|
: glyph === GLYPH.awaiting ? 'awaiting'
|
|
225
235
|
: glyph === GLYPH.idle ? 'idle' : 'other';
|
|
226
236
|
const diag = logCtx.diag ? ` ${logCtx.diag}` : '';
|
|
237
|
+
const contract = logCtx.contract ? ` contract=degraded(${logCtx.contract})` : '';
|
|
227
238
|
const line = `${new Date().toISOString()} ${logCtx.event.padEnd(16)} `
|
|
228
239
|
+ `${name.padEnd(8)} ring=${ring ? 1 : 0} note=${(logCtx.note || '-').padEnd(8)} `
|
|
229
|
-
+ `sid=${logCtx.sid} | ${title}${diag}\n`;
|
|
240
|
+
+ `sid=${logCtx.sid} | ${title}${diag}${contract}\n`;
|
|
230
241
|
const f = path.join(logCtx.dir, '_debug.log');
|
|
231
242
|
try { if (fs.statSync(f).size > 512 * 1024) fs.renameSync(f, `${f}.1`); } catch (_) { /* none yet */ }
|
|
232
243
|
fs.appendFileSync(f, line);
|
|
@@ -381,6 +392,14 @@ const SOLICIT_STRONG = [
|
|
|
381
392
|
/\bok(?:ay)? to proceed\b/i,
|
|
382
393
|
/^green-?light\b/i,
|
|
383
394
|
/^confirm\b/i,
|
|
395
|
+
// Harvested 2026-07-08 from real _debug.log flag-turn tails — the historical "flag masked the
|
|
396
|
+
// prose violation" shapes. All are unambiguous waiting-on-you closers:
|
|
397
|
+
/^tell me\b/i,
|
|
398
|
+
/\byour call\b/i,
|
|
399
|
+
/\bready when you are\b/i,
|
|
400
|
+
/\bstanding by\b/i,
|
|
401
|
+
/\bon your go\b/i,
|
|
402
|
+
/\bwhenever you say\b/i,
|
|
384
403
|
];
|
|
385
404
|
function solicitsReply(text) {
|
|
386
405
|
if (!text) return false;
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v1.0.202
|
|
4
|
+
- feat(terminal-title): The 'awaiting your reply' tab indicator now also catches sign-off style endings like 'Ready when you are', and quietly logs a warning if a Claude Code update changes the data the hooks rely on.
|
|
5
|
+
|
|
3
6
|
## v1.0.201
|
|
4
7
|
- 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
8
|
- 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.
|
|
@@ -147,6 +150,3 @@
|
|
|
147
150
|
## v1.0.153
|
|
148
151
|
- style: left-align all table cells in docs info cards
|
|
149
152
|
|
|
150
|
-
## v1.0.152
|
|
151
|
-
- fix: warn against ! prefix workaround in inside-Claude message
|
|
152
|
-
|
package/bin/cli.js
CHANGED
|
@@ -165,7 +165,8 @@ if (process.argv.includes('--pull-updates')) {
|
|
|
165
165
|
// 2. mergeSettingsInto dedups by EXACT command string, so without this rewrite an upgrade
|
|
166
166
|
// would ADD the anchored template entry alongside the user's old relative one -> the
|
|
167
167
|
// hook runs twice per event.
|
|
168
|
-
//
|
|
168
|
+
// Any .claude/hooks/*.js relative command is rewritten — anchored resolution is identical at
|
|
169
|
+
// the project root and cd-proof everywhere else — while commands outside .claude/hooks are
|
|
169
170
|
// never touched.
|
|
170
171
|
function migrateLegacyHookCommands(userSettings) {
|
|
171
172
|
if (!userSettings || !userSettings.hooks) return;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "claude-code-autoconfig",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.202",
|
|
4
4
|
"description": "Intelligent, self-configuring setup for Claude Code. One command analyzes your project, configures Claude, and shows you what it did.",
|
|
5
5
|
"author": "ADAC 1001 <info@adac1001.com>",
|
|
6
6
|
"license": "MIT",
|
|
@@ -22,7 +22,7 @@
|
|
|
22
22
|
"cli"
|
|
23
23
|
],
|
|
24
24
|
"scripts": {
|
|
25
|
-
"test": "node test/box-alignment.test.js && node test/cli-install.test.js && node test/update-system.test.js && node test/plugin-system.test.js && node test/terminal-title.test.js && node test/update-summary.test.js && node test/changelog-gen.test.js",
|
|
25
|
+
"test": "node test/box-alignment.test.js && node test/cli-install.test.js && node test/update-system.test.js && node test/plugin-system.test.js && node test/terminal-title.test.js && node test/golden-endings.test.js && node test/live-twin-parity.test.js && node test/update-summary.test.js && node test/changelog-gen.test.js",
|
|
26
26
|
"pub": "npm whoami && npm publish",
|
|
27
27
|
"test:box": "node test/box-alignment.test.js",
|
|
28
28
|
"test:install": "node test/cli-install.test.js",
|