@searls/turbocommit 0.13.0 → 0.13.2
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/lib/doctor.js +4 -1
- package/lib/harness.js +7 -1
- package/lib/install.js +29 -12
- package/lib/transcript.js +16 -1
- package/package.json +1 -1
package/lib/doctor.js
CHANGED
|
@@ -53,7 +53,10 @@ function doctorLegacy (settingsPath, cwd) {
|
|
|
53
53
|
const missing = Object.keys(HOOK_DEFS).filter(event =>
|
|
54
54
|
!hasTurbocommit((settings.hooks && settings.hooks[event]) || [])
|
|
55
55
|
)
|
|
56
|
-
|
|
56
|
+
const message = missing.length > 0
|
|
57
|
+
? `Missing hooks: ${missing.join(', ')}. Run: turbocommit uninstall && turbocommit install`
|
|
58
|
+
: 'Outdated hooks (missing the --harness flag); Claude sessions may be misparsed. Run: turbocommit install'
|
|
59
|
+
checks.push({ name: 'Hooks installed', status: 'error', message })
|
|
57
60
|
return { ok: false, checks }
|
|
58
61
|
}
|
|
59
62
|
checks.push({ name: 'Hooks installed', status: 'ok', message: `All ${Object.keys(HOOK_DEFS).length} hooks installed` })
|
package/lib/harness.js
CHANGED
|
@@ -48,8 +48,14 @@ function selectedHarnesses (harness, { create = false } = {}) {
|
|
|
48
48
|
}
|
|
49
49
|
|
|
50
50
|
function inferHarness (hookInput, forcedHarness) {
|
|
51
|
+
// The installed hooks pass an explicit --harness flag, which is the source of
|
|
52
|
+
// truth. Inference is only a fallback for legacy flagless installs.
|
|
53
|
+
//
|
|
54
|
+
// We deliberately do NOT sniff `hook_event_name`: current Claude Code hook
|
|
55
|
+
// payloads include it too, so treating its presence as a Codex signal
|
|
56
|
+
// misroutes every Claude session into the Codex transcript parser (which
|
|
57
|
+
// yields zero pairs and a "(no transcript)" commit body). Default to Claude.
|
|
51
58
|
if (forcedHarness) return forcedHarness
|
|
52
|
-
if (hookInput && hookInput.hook_event_name) return 'codex'
|
|
53
59
|
return 'claude'
|
|
54
60
|
}
|
|
55
61
|
|
package/lib/install.js
CHANGED
|
@@ -8,34 +8,34 @@ const { selectedHarnesses, claudeHome, codexHome } = require('./harness')
|
|
|
8
8
|
const HOOK_DEFS = {
|
|
9
9
|
PreToolUse: {
|
|
10
10
|
matcher: 'Write|Edit|MultiEdit|NotebookEdit|Bash|mcp__.*',
|
|
11
|
-
hooks: [{ type: 'command', command: 'turbocommit hook pre-tool-use' }]
|
|
11
|
+
hooks: [{ type: 'command', command: 'turbocommit hook pre-tool-use --harness claude' }]
|
|
12
12
|
},
|
|
13
13
|
SessionStart: {
|
|
14
|
-
hooks: [{ type: 'command', command: 'turbocommit hook session-start' }]
|
|
14
|
+
hooks: [{ type: 'command', command: 'turbocommit hook session-start --harness claude' }]
|
|
15
15
|
},
|
|
16
16
|
SessionEnd: {
|
|
17
|
-
hooks: [{ type: 'command', command: 'turbocommit hook session-end' }]
|
|
17
|
+
hooks: [{ type: 'command', command: 'turbocommit hook session-end --harness claude' }]
|
|
18
18
|
},
|
|
19
19
|
Stop: {
|
|
20
|
-
hooks: [{ type: 'command', command: 'turbocommit hook stop' }]
|
|
20
|
+
hooks: [{ type: 'command', command: 'turbocommit hook stop --harness claude' }]
|
|
21
21
|
}
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
const CODEX_HOOK_DEFS = {
|
|
25
25
|
PreToolUse: {
|
|
26
26
|
matcher: 'Write|Edit|MultiEdit|NotebookEdit|Bash|mcp__.*',
|
|
27
|
-
hooks: [{ type: 'command', command: 'turbocommit hook pre-tool-use' }]
|
|
27
|
+
hooks: [{ type: 'command', command: 'turbocommit hook pre-tool-use --harness codex' }]
|
|
28
28
|
},
|
|
29
29
|
SessionStart: {
|
|
30
30
|
matcher: 'resume|clear',
|
|
31
|
-
hooks: [{ type: 'command', command: 'turbocommit hook session-start' }]
|
|
31
|
+
hooks: [{ type: 'command', command: 'turbocommit hook session-start --harness codex' }]
|
|
32
32
|
},
|
|
33
33
|
PreCompact: {
|
|
34
34
|
matcher: 'manual|auto',
|
|
35
|
-
hooks: [{ type: 'command', command: 'turbocommit hook pre-compact' }]
|
|
35
|
+
hooks: [{ type: 'command', command: 'turbocommit hook pre-compact --harness codex' }]
|
|
36
36
|
},
|
|
37
37
|
Stop: {
|
|
38
|
-
hooks: [{ type: 'command', command: 'turbocommit hook stop' }]
|
|
38
|
+
hooks: [{ type: 'command', command: 'turbocommit hook stop --harness codex' }]
|
|
39
39
|
}
|
|
40
40
|
}
|
|
41
41
|
|
|
@@ -55,20 +55,37 @@ function hasTurbocommit (groups) {
|
|
|
55
55
|
})
|
|
56
56
|
}
|
|
57
57
|
|
|
58
|
+
/**
|
|
59
|
+
* Check whether the exact expected command is already installed for an event.
|
|
60
|
+
* Requiring an exact match (not just any turbocommit hook) means legacy
|
|
61
|
+
* flagless installs are treated as out-of-date and get rewritten with the
|
|
62
|
+
* explicit --harness flag on the next install.
|
|
63
|
+
*/
|
|
64
|
+
function hasExactHooks (groups, def) {
|
|
65
|
+
if (!Array.isArray(groups)) return false
|
|
66
|
+
const expected = def.hooks.map(h => h.command)
|
|
67
|
+
return expected.every(cmd =>
|
|
68
|
+
groups.some(g => {
|
|
69
|
+
const hooks = g && g.hooks ? g.hooks : []
|
|
70
|
+
return hooks.some(h => h.command === cmd)
|
|
71
|
+
})
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
|
|
58
75
|
/**
|
|
59
76
|
* Check if turbocommit hooks are installed across all expected events.
|
|
60
77
|
*/
|
|
61
78
|
function isFullyInstalled (settings) {
|
|
62
79
|
if (!settings || !settings.hooks) return false
|
|
63
|
-
return Object.
|
|
64
|
-
|
|
80
|
+
return Object.entries(HOOK_DEFS).every(([event, def]) =>
|
|
81
|
+
hasExactHooks(settings.hooks[event], def)
|
|
65
82
|
)
|
|
66
83
|
}
|
|
67
84
|
|
|
68
85
|
function isCodexFullyInstalled (hooksConfig) {
|
|
69
86
|
if (!hooksConfig || !hooksConfig.hooks) return false
|
|
70
|
-
return Object.
|
|
71
|
-
|
|
87
|
+
return Object.entries(CODEX_HOOK_DEFS).every(([event, def]) =>
|
|
88
|
+
hasExactHooks(hooksConfig.hooks[event], def)
|
|
72
89
|
)
|
|
73
90
|
}
|
|
74
91
|
|
package/lib/transcript.js
CHANGED
|
@@ -61,7 +61,7 @@ function parseCodexTranscript (filePath) {
|
|
|
61
61
|
if (entry.type !== 'response_item' || payload?.type !== 'message') continue
|
|
62
62
|
|
|
63
63
|
if (payload.role === 'user') {
|
|
64
|
-
const prompt = visibleText(payload.content)
|
|
64
|
+
const prompt = stripCodexInjectedContext(visibleText(payload.content))
|
|
65
65
|
if (isCodexSystemStatus(prompt)) continue
|
|
66
66
|
if (state.prompt !== null) {
|
|
67
67
|
state.pairs.push({ prompt: state.prompt, response: state.response })
|
|
@@ -94,6 +94,21 @@ function isCodexSystemStatus (text) {
|
|
|
94
94
|
return text.startsWith('== System Status ==\n') && text.includes('[automatic message added by system]')
|
|
95
95
|
}
|
|
96
96
|
|
|
97
|
+
function stripCodexInjectedContext (text) {
|
|
98
|
+
if (!isCodexInjectedContext(text)) return text
|
|
99
|
+
|
|
100
|
+
const end = '</environment_context>'
|
|
101
|
+
const endIndex = text.indexOf(end)
|
|
102
|
+
if (endIndex === -1) return text
|
|
103
|
+
return text.slice(endIndex + end.length).trim()
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function isCodexInjectedContext (text) {
|
|
107
|
+
return text.startsWith('# AGENTS.md instructions for ') &&
|
|
108
|
+
text.includes('<INSTRUCTIONS>') &&
|
|
109
|
+
text.includes('<environment_context>')
|
|
110
|
+
}
|
|
111
|
+
|
|
97
112
|
/**
|
|
98
113
|
* Format a single prompt/response pair.
|
|
99
114
|
*/
|