@searls/turbocommit 0.15.2 → 0.15.3
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 +4 -2
- package/lib/install.js +3 -3
- package/lib/session.js +2 -3
- package/lib/track.js +26 -10
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -25,8 +25,10 @@ turbocommit registers hooks with the harnesses you use:
|
|
|
25
25
|
|
|
26
26
|
- **PreToolUse** tentatively claims the paths supplied to editing tools while
|
|
27
27
|
recovery finishes, promotes the claim only when the tool may proceed, and
|
|
28
|
-
snapshots the repository before shell commands.
|
|
29
|
-
- **PostToolUse** attributes paths that became dirty during a shell command
|
|
28
|
+
snapshots the repository before shell commands and MCP tool calls.
|
|
29
|
+
- **PostToolUse** attributes paths that became dirty during a shell command or
|
|
30
|
+
MCP tool call, so files a tool rewrites without naming them (an Xcode project
|
|
31
|
+
file, for example) belong to the session that changed them.
|
|
30
32
|
Concurrent commands in one session share ownership. Commands from different
|
|
31
33
|
sessions retain hashed overlap evidence instead of claiming each other's
|
|
32
34
|
paths. Once every involved turn stops and no shell remains active in that
|
package/lib/install.js
CHANGED
|
@@ -11,11 +11,11 @@ const HOOK_DEFS = {
|
|
|
11
11
|
hooks: [{ type: 'command', command: 'turbocommit hook pre-tool-use --harness claude' }]
|
|
12
12
|
},
|
|
13
13
|
PostToolUse: {
|
|
14
|
-
matcher: 'Bash',
|
|
14
|
+
matcher: 'Bash|mcp__.*',
|
|
15
15
|
hooks: [{ type: 'command', command: 'turbocommit hook post-tool-use --harness claude' }]
|
|
16
16
|
},
|
|
17
17
|
PostToolUseFailure: {
|
|
18
|
-
matcher: 'Bash',
|
|
18
|
+
matcher: 'Bash|mcp__.*',
|
|
19
19
|
hooks: [{ type: 'command', command: 'turbocommit hook post-tool-use --harness claude' }]
|
|
20
20
|
},
|
|
21
21
|
SessionStart: {
|
|
@@ -35,7 +35,7 @@ const CODEX_HOOK_DEFS = {
|
|
|
35
35
|
hooks: [{ type: 'command', command: 'turbocommit hook pre-tool-use --harness codex' }]
|
|
36
36
|
},
|
|
37
37
|
PostToolUse: {
|
|
38
|
-
matcher: 'Bash',
|
|
38
|
+
matcher: 'Bash|mcp__.*',
|
|
39
39
|
hooks: [{ type: 'command', command: 'turbocommit hook post-tool-use --harness codex' }]
|
|
40
40
|
},
|
|
41
41
|
SessionStart: {
|
package/lib/session.js
CHANGED
|
@@ -328,11 +328,10 @@ function trackingMayDescribeModifications (file) {
|
|
|
328
328
|
} catch {
|
|
329
329
|
return true
|
|
330
330
|
}
|
|
331
|
+
const { describesModification } = require('./track')
|
|
331
332
|
return lines.some(line => {
|
|
332
333
|
try {
|
|
333
|
-
|
|
334
|
-
return entry.tool !== 'Bash' ||
|
|
335
|
-
(entry.phase === 'post' && Array.isArray(entry.files) && entry.files.length > 0)
|
|
334
|
+
return describesModification(JSON.parse(line))
|
|
336
335
|
} catch {
|
|
337
336
|
return true
|
|
338
337
|
}
|
package/lib/track.js
CHANGED
|
@@ -45,6 +45,8 @@ const FILE_PATH_KEYS = [
|
|
|
45
45
|
'notebook_path',
|
|
46
46
|
'relative_path',
|
|
47
47
|
'relativePath',
|
|
48
|
+
'sourcePath',
|
|
49
|
+
'destinationPath',
|
|
48
50
|
'uri'
|
|
49
51
|
]
|
|
50
52
|
const FILE_PATH_ARRAY_KEYS = ['file_paths', 'filePaths', 'paths', 'files']
|
|
@@ -124,6 +126,15 @@ function extractFilePaths (toolName, toolInput, cwd) {
|
|
|
124
126
|
})
|
|
125
127
|
}
|
|
126
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Bash and MCP tools change paths they never name (a shell script, an Xcode
|
|
131
|
+
* project file rewritten as a side effect), so both are attributed by
|
|
132
|
+
* snapshotting the checkout before and after the call.
|
|
133
|
+
*/
|
|
134
|
+
function snapshotsRepository (toolName) {
|
|
135
|
+
return toolName === 'Bash' || (typeof toolName === 'string' && toolName.startsWith('mcp__'))
|
|
136
|
+
}
|
|
137
|
+
|
|
127
138
|
/**
|
|
128
139
|
* PreToolUse handler. Persists ownership before waiting for overlap recovery.
|
|
129
140
|
* Returns false when the caller must deny the tool rather than let it run
|
|
@@ -158,9 +169,9 @@ function handleTrack (input, root, opts = {}) {
|
|
|
158
169
|
let pendingSnapshot = false
|
|
159
170
|
let preclaim = null
|
|
160
171
|
try {
|
|
161
|
-
if (toolName
|
|
162
|
-
entry.command = toolInput.command
|
|
163
|
-
savePendingBashSnapshot(root, sessionId, toolUseId, cwd)
|
|
172
|
+
if (snapshotsRepository(toolName)) {
|
|
173
|
+
if (toolName === 'Bash') entry.command = toolInput.command
|
|
174
|
+
savePendingBashSnapshot(root, sessionId, toolUseId, cwd, toolName)
|
|
164
175
|
pendingSnapshot = true
|
|
165
176
|
appendTracking(root, sessionId, entry)
|
|
166
177
|
} else {
|
|
@@ -210,7 +221,7 @@ function handlePostTrack (input, root) {
|
|
|
210
221
|
|
|
211
222
|
const sessionId = hookInput.sessionId || hookInput.session_id
|
|
212
223
|
const toolName = hookInput.toolName || hookInput.tool_name
|
|
213
|
-
if (!sessionId || toolName
|
|
224
|
+
if (!sessionId || !snapshotsRepository(toolName)) return
|
|
214
225
|
|
|
215
226
|
const toolUseId = hookInput.toolUseId || hookInput.tool_use_id
|
|
216
227
|
const release = acquireBashOverlapRecoveryLock(root, 5000)
|
|
@@ -239,7 +250,7 @@ function finishBashSnapshot (root, snapshot, { cwd, endedAt }) {
|
|
|
239
250
|
const candidates = changed.filter(file => !before.has(file) && !claimed.has(file))
|
|
240
251
|
const files = overlapping ? [] : candidates
|
|
241
252
|
|
|
242
|
-
const entry = { tool: 'Bash', phase: 'post', t: endedAt, cwd }
|
|
253
|
+
const entry = { tool: snapshot.toolName || 'Bash', phase: 'post', t: endedAt, cwd }
|
|
243
254
|
if (overlapping) {
|
|
244
255
|
entry.overlapping = true
|
|
245
256
|
entry.overlapEventId = recordBashOverlap(root, snapshot, overlaps, candidates, endedAt)
|
|
@@ -268,12 +279,13 @@ function bashSnapshotPath (root, sessionId, toolUseId) {
|
|
|
268
279
|
return path.join(dir, key + '.json')
|
|
269
280
|
}
|
|
270
281
|
|
|
271
|
-
function savePendingBashSnapshot (root, sessionId, toolUseId, cwd) {
|
|
282
|
+
function savePendingBashSnapshot (root, sessionId, toolUseId, cwd, toolName = 'Bash') {
|
|
272
283
|
const checkout = canonicalRoot(root)
|
|
273
284
|
writeBashSnapshot(root, sessionId, toolUseId, {
|
|
274
285
|
root: checkout,
|
|
275
286
|
cwd: cwd || checkout,
|
|
276
287
|
sessionId,
|
|
288
|
+
toolName,
|
|
277
289
|
toolUseId: toolUseId || null,
|
|
278
290
|
createdAt: Date.now(),
|
|
279
291
|
pending: true
|
|
@@ -918,12 +930,15 @@ function parseInput (input) {
|
|
|
918
930
|
/**
|
|
919
931
|
* Check whether a session has tracked any file-modifying tool calls.
|
|
920
932
|
* A Bash pre-hook alone doesn't count because shell commands may be read-only.
|
|
921
|
-
* A
|
|
933
|
+
* A snapshot post-hook counts only when it found newly dirty paths.
|
|
922
934
|
*/
|
|
923
935
|
function hasTrackedModifications (root, sessionId) {
|
|
924
|
-
return readTracking(root, sessionId).some(
|
|
925
|
-
|
|
926
|
-
|
|
936
|
+
return readTracking(root, sessionId).some(describesModification)
|
|
937
|
+
}
|
|
938
|
+
|
|
939
|
+
function describesModification (entry) {
|
|
940
|
+
if (entry.phase === 'post') return Array.isArray(entry.files) && entry.files.length > 0
|
|
941
|
+
return entry.tool !== 'Bash'
|
|
927
942
|
}
|
|
928
943
|
|
|
929
944
|
function readTracking (root, sessionId) {
|
|
@@ -986,6 +1001,7 @@ module.exports = {
|
|
|
986
1001
|
fingerprintTrackedPath,
|
|
987
1002
|
claimedPathsBySessions,
|
|
988
1003
|
hasTrackedModifications,
|
|
1004
|
+
describesModification,
|
|
989
1005
|
cleanupTracking,
|
|
990
1006
|
extractFilePath,
|
|
991
1007
|
extractFilePaths,
|