@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 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
- checks.push({ name: 'Hooks installed', status: 'error', message: `Missing hooks: ${missing.join(', ')}. Run: turbocommit uninstall && turbocommit install` })
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.keys(HOOK_DEFS).every(event =>
64
- hasTurbocommit(settings.hooks[event])
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.keys(CODEX_HOOK_DEFS).every(event =>
71
- hasTurbocommit(hooksConfig.hooks[event])
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
  */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@searls/turbocommit",
3
- "version": "0.13.0",
3
+ "version": "0.13.2",
4
4
  "description": "Auto-commit after every AI coding agent turn",
5
5
  "bin": {
6
6
  "turbocommit": "./cli.js"