@remixhq/claude-plugin 0.1.8 → 0.1.9
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-plugin/plugin.json +1 -1
- package/.mcp.json +1 -1
- package/dist/{hook-post-collab.js → hook-post-collab.cjs} +56 -33
- package/dist/{hook-post-collab.js.map → hook-post-collab.cjs.map} +1 -1
- package/dist/{hook-pre-git.js → hook-pre-git.cjs} +39 -16
- package/dist/{hook-pre-git.js.map → hook-pre-git.cjs.map} +1 -1
- package/dist/{hook-stop-collab.js → hook-stop-collab.cjs} +395 -363
- package/dist/hook-stop-collab.cjs.map +1 -0
- package/dist/{hook-user-prompt.js → hook-user-prompt.cjs} +51 -28
- package/dist/{hook-user-prompt.js.map → hook-user-prompt.cjs.map} +1 -1
- package/dist/index.js +48 -6
- package/dist/index.js.map +1 -1
- package/dist/{mcp-server.js → mcp-server.cjs} +814 -782
- package/dist/mcp-server.cjs.map +1 -0
- package/hooks/hooks.json +4 -4
- package/package.json +2 -2
- package/dist/hook-stop-collab.js.map +0 -1
- package/dist/mcp-server.js.map +0 -1
- /package/dist/{hook-post-collab.d.ts → hook-post-collab.d.cts} +0 -0
- /package/dist/{hook-pre-git.d.ts → hook-pre-git.d.cts} +0 -0
- /package/dist/{hook-stop-collab.d.ts → hook-stop-collab.d.cts} +0 -0
- /package/dist/{hook-user-prompt.d.ts → hook-user-prompt.d.cts} +0 -0
- /package/dist/{mcp-server.d.ts → mcp-server.d.cts} +0 -0
package/.mcp.json
CHANGED
|
@@ -1,27 +1,50 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
2
25
|
|
|
3
26
|
// src/hook-state.ts
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
27
|
+
var import_promises = __toESM(require("fs/promises"), 1);
|
|
28
|
+
var import_node_os = __toESM(require("os"), 1);
|
|
29
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
30
|
+
var import_node_crypto = require("crypto");
|
|
8
31
|
function stateRoot() {
|
|
9
|
-
return
|
|
32
|
+
return import_node_path.default.join(import_node_os.default.tmpdir(), "remix-claude-plugin-hooks");
|
|
10
33
|
}
|
|
11
34
|
function statePath(sessionId) {
|
|
12
|
-
return
|
|
35
|
+
return import_node_path.default.join(stateRoot(), `${sessionId}.json`);
|
|
13
36
|
}
|
|
14
37
|
function stateLockPath(sessionId) {
|
|
15
|
-
return
|
|
38
|
+
return import_node_path.default.join(stateRoot(), `${sessionId}.lock`);
|
|
16
39
|
}
|
|
17
40
|
function stateLockMetaPath(sessionId) {
|
|
18
|
-
return
|
|
41
|
+
return import_node_path.default.join(stateLockPath(sessionId), "owner.json");
|
|
19
42
|
}
|
|
20
43
|
async function writeJsonAtomic(filePath, value) {
|
|
21
|
-
await
|
|
44
|
+
await import_promises.default.mkdir(import_node_path.default.dirname(filePath), { recursive: true });
|
|
22
45
|
const tmpPath = `${filePath}.tmp-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
23
|
-
await
|
|
24
|
-
await
|
|
46
|
+
await import_promises.default.writeFile(tmpPath, JSON.stringify(value, null, 2) + "\n", "utf8");
|
|
47
|
+
await import_promises.default.rename(tmpPath, filePath);
|
|
25
48
|
}
|
|
26
49
|
var STATE_LOCK_WAIT_MS = 2e3;
|
|
27
50
|
var STATE_LOCK_POLL_MS = 25;
|
|
@@ -31,7 +54,7 @@ async function sleep(ms) {
|
|
|
31
54
|
await new Promise((resolve) => setTimeout(resolve, ms));
|
|
32
55
|
}
|
|
33
56
|
async function readStateLockMetadata(sessionId) {
|
|
34
|
-
const raw = await
|
|
57
|
+
const raw = await import_promises.default.readFile(stateLockMetaPath(sessionId), "utf8").catch(() => null);
|
|
35
58
|
if (!raw) return null;
|
|
36
59
|
try {
|
|
37
60
|
const parsed = JSON.parse(raw);
|
|
@@ -56,13 +79,13 @@ async function tryRemoveStaleStateLock(sessionId) {
|
|
|
56
79
|
const metadata = await readStateLockMetadata(sessionId);
|
|
57
80
|
const staleByHeartbeat = metadata && Date.now() - new Date(metadata.heartbeatAt).getTime() > STATE_LOCK_STALE_MS;
|
|
58
81
|
if (staleByHeartbeat) {
|
|
59
|
-
await
|
|
82
|
+
await import_promises.default.rm(lockPath, { recursive: true, force: true }).catch(() => void 0);
|
|
60
83
|
return true;
|
|
61
84
|
}
|
|
62
85
|
if (!metadata) {
|
|
63
|
-
const lockStat = await
|
|
86
|
+
const lockStat = await import_promises.default.stat(lockPath).catch(() => null);
|
|
64
87
|
if (lockStat && Date.now() - lockStat.mtimeMs > STATE_LOCK_STALE_MS) {
|
|
65
|
-
await
|
|
88
|
+
await import_promises.default.rm(lockPath, { recursive: true, force: true }).catch(() => void 0);
|
|
66
89
|
return true;
|
|
67
90
|
}
|
|
68
91
|
}
|
|
@@ -73,8 +96,8 @@ async function acquireStateLock(sessionId) {
|
|
|
73
96
|
const deadline = Date.now() + STATE_LOCK_WAIT_MS;
|
|
74
97
|
while (true) {
|
|
75
98
|
try {
|
|
76
|
-
await
|
|
77
|
-
const ownerId = randomUUID();
|
|
99
|
+
await import_promises.default.mkdir(lockPath);
|
|
100
|
+
const ownerId = (0, import_node_crypto.randomUUID)();
|
|
78
101
|
const createdAt = (/* @__PURE__ */ new Date()).toISOString();
|
|
79
102
|
const metadata = {
|
|
80
103
|
ownerId,
|
|
@@ -98,7 +121,7 @@ async function acquireStateLock(sessionId) {
|
|
|
98
121
|
clearInterval(heartbeat);
|
|
99
122
|
const currentMetadata = await readStateLockMetadata(sessionId);
|
|
100
123
|
if (currentMetadata?.ownerId === ownerId) {
|
|
101
|
-
await
|
|
124
|
+
await import_promises.default.rm(lockPath, { recursive: true, force: true }).catch(() => void 0);
|
|
102
125
|
}
|
|
103
126
|
};
|
|
104
127
|
} catch (error) {
|
|
@@ -206,7 +229,7 @@ async function updatePendingTurnState(sessionId, updater) {
|
|
|
206
229
|
});
|
|
207
230
|
}
|
|
208
231
|
async function loadPendingTurnState(sessionId) {
|
|
209
|
-
const raw = await
|
|
232
|
+
const raw = await import_promises.default.readFile(statePath(sessionId), "utf8").catch(() => null);
|
|
210
233
|
if (!raw) return null;
|
|
211
234
|
try {
|
|
212
235
|
const parsed = JSON.parse(raw);
|
|
@@ -298,18 +321,18 @@ async function markPendingTurnConsultedMemory(sessionId) {
|
|
|
298
321
|
}
|
|
299
322
|
|
|
300
323
|
// src/hook-utils.ts
|
|
301
|
-
|
|
302
|
-
|
|
324
|
+
var import_promises3 = __toESM(require("fs/promises"), 1);
|
|
325
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
303
326
|
|
|
304
327
|
// node_modules/@remixhq/core/dist/chunk-FAZUMWBS.js
|
|
305
|
-
|
|
306
|
-
|
|
328
|
+
var import_promises2 = __toESM(require("fs/promises"), 1);
|
|
329
|
+
var import_path = __toESM(require("path"), 1);
|
|
307
330
|
function getCollabBindingPath(repoRoot) {
|
|
308
|
-
return
|
|
331
|
+
return import_path.default.join(repoRoot, ".remix", "config.json");
|
|
309
332
|
}
|
|
310
333
|
async function readCollabBinding(repoRoot) {
|
|
311
334
|
try {
|
|
312
|
-
const raw = await
|
|
335
|
+
const raw = await import_promises2.default.readFile(getCollabBindingPath(repoRoot), "utf8");
|
|
313
336
|
const parsed = JSON.parse(raw);
|
|
314
337
|
if (parsed?.schemaVersion !== 1) return null;
|
|
315
338
|
if (!parsed.projectId || !parsed.currentAppId || !parsed.upstreamAppId) return null;
|
|
@@ -398,7 +421,7 @@ function collectPathTargetsFromObject(input, keys) {
|
|
|
398
421
|
return keys.flatMap((key) => collectStringPathValue(input[key]));
|
|
399
422
|
}
|
|
400
423
|
function resolveCandidatePath(targetPath, baseDir) {
|
|
401
|
-
return
|
|
424
|
+
return import_node_path2.default.isAbsolute(targetPath) ? import_node_path2.default.normalize(targetPath) : import_node_path2.default.resolve(baseDir, targetPath);
|
|
402
425
|
}
|
|
403
426
|
function extractToolPathTargets(payload, toolName) {
|
|
404
427
|
const name = (toolName ?? extractToolName(payload) ?? "").trim().toLowerCase();
|
|
@@ -410,16 +433,16 @@ function extractToolPathTargets(payload, toolName) {
|
|
|
410
433
|
}
|
|
411
434
|
async function findBoundRepo(startPath) {
|
|
412
435
|
if (!startPath) return null;
|
|
413
|
-
let current =
|
|
414
|
-
let stats = await
|
|
436
|
+
let current = import_node_path2.default.resolve(startPath);
|
|
437
|
+
let stats = await import_promises3.default.stat(current).catch(() => null);
|
|
415
438
|
if (stats?.isFile()) {
|
|
416
|
-
current =
|
|
439
|
+
current = import_node_path2.default.dirname(current);
|
|
417
440
|
}
|
|
418
441
|
while (true) {
|
|
419
|
-
const bindingPath =
|
|
420
|
-
const bindingStats = await
|
|
442
|
+
const bindingPath = import_node_path2.default.join(current, ".remix", "config.json");
|
|
443
|
+
const bindingStats = await import_promises3.default.stat(bindingPath).catch(() => null);
|
|
421
444
|
if (bindingStats?.isFile()) return current;
|
|
422
|
-
const parent =
|
|
445
|
+
const parent = import_node_path2.default.dirname(current);
|
|
423
446
|
if (parent === current) return null;
|
|
424
447
|
current = parent;
|
|
425
448
|
}
|
|
@@ -515,4 +538,4 @@ main().catch((error) => {
|
|
|
515
538
|
`);
|
|
516
539
|
process.exitCode = 0;
|
|
517
540
|
});
|
|
518
|
-
//# sourceMappingURL=hook-post-collab.
|
|
541
|
+
//# sourceMappingURL=hook-post-collab.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/hook-state.ts","../src/hook-utils.ts","../node_modules/@remixhq/core/dist/chunk-FAZUMWBS.js","../src/hook-post-collab.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\n\nimport type { TurnIntent } from \"./history-routing.js\";\n\nexport type RepoRecordMode = \"changed_turn\" | \"no_diff_turn\";\n\nexport type TouchedRepoState = {\n repoRoot: string;\n projectId: string | null;\n currentAppId: string | null;\n upstreamAppId: string | null;\n firstTouchedAt: string;\n lastTouchedAt: string;\n lastObservedWriteAt: string | null;\n touchedBy: string[];\n hasObservedWrite: boolean;\n manuallyRecorded: boolean;\n manuallyRecordedAt: string | null;\n manuallyRecordedByTool: string | null;\n stopAttempted: boolean;\n stopRecorded: boolean;\n stopRecordedAt: string | null;\n stopRecordedMode: RepoRecordMode | null;\n recordingFailureMessage: string | null;\n recordingFailureHint: string | null;\n recordingFailedAt: string | null;\n};\n\nexport type PendingTurnState = {\n sessionId: string;\n turnId: string;\n prompt: string;\n initialCwd: string | null;\n intent: TurnIntent;\n submittedAt: string;\n consultedMemory: boolean;\n touchedRepos: Record<string, TouchedRepoState>;\n turnFailureMessage: string | null;\n turnFailureHint: string | null;\n turnFailedAt: string | null;\n};\n\nfunction stateRoot(): string {\n return path.join(os.tmpdir(), \"remix-claude-plugin-hooks\");\n}\n\nfunction statePath(sessionId: string): string {\n return path.join(stateRoot(), `${sessionId}.json`);\n}\n\nfunction stateLockPath(sessionId: string): string {\n return path.join(stateRoot(), `${sessionId}.lock`);\n}\n\nfunction stateLockMetaPath(sessionId: string): string {\n return path.join(stateLockPath(sessionId), \"owner.json\");\n}\n\nasync function writeJsonAtomic(filePath: string, value: unknown): Promise<void> {\n await fs.mkdir(path.dirname(filePath), { recursive: true });\n const tmpPath = `${filePath}.tmp-${Date.now()}-${Math.random().toString(16).slice(2)}`;\n await fs.writeFile(tmpPath, JSON.stringify(value, null, 2) + \"\\n\", \"utf8\");\n await fs.rename(tmpPath, filePath);\n}\n\nconst STATE_LOCK_WAIT_MS = 2_000;\nconst STATE_LOCK_POLL_MS = 25;\nconst STATE_LOCK_STALE_MS = 30_000;\nconst STATE_LOCK_HEARTBEAT_MS = 5_000;\n\ntype StateLockMetadata = {\n ownerId: string;\n pid: number;\n createdAt: string;\n heartbeatAt: string;\n};\n\nasync function sleep(ms: number): Promise<void> {\n await new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function readStateLockMetadata(sessionId: string): Promise<StateLockMetadata | null> {\n const raw = await fs.readFile(stateLockMetaPath(sessionId), \"utf8\").catch(() => null);\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as Partial<StateLockMetadata>;\n if (\n typeof parsed.ownerId !== \"string\" ||\n typeof parsed.pid !== \"number\" ||\n typeof parsed.createdAt !== \"string\" ||\n typeof parsed.heartbeatAt !== \"string\"\n ) {\n return null;\n }\n return {\n ownerId: parsed.ownerId,\n pid: parsed.pid,\n createdAt: parsed.createdAt,\n heartbeatAt: parsed.heartbeatAt,\n };\n } catch {\n return null;\n }\n}\n\nasync function writeStateLockMetadata(sessionId: string, metadata: StateLockMetadata): Promise<void> {\n await writeJsonAtomic(stateLockMetaPath(sessionId), metadata);\n}\n\nasync function tryRemoveStaleStateLock(sessionId: string): Promise<boolean> {\n const lockPath = stateLockPath(sessionId);\n const metadata = await readStateLockMetadata(sessionId);\n const staleByHeartbeat =\n metadata && Date.now() - new Date(metadata.heartbeatAt).getTime() > STATE_LOCK_STALE_MS;\n if (staleByHeartbeat) {\n await fs.rm(lockPath, { recursive: true, force: true }).catch(() => undefined);\n return true;\n }\n\n if (!metadata) {\n const lockStat = await fs.stat(lockPath).catch(() => null);\n if (lockStat && Date.now() - lockStat.mtimeMs > STATE_LOCK_STALE_MS) {\n await fs.rm(lockPath, { recursive: true, force: true }).catch(() => undefined);\n return true;\n }\n }\n\n return false;\n}\n\nasync function acquireStateLock(sessionId: string): Promise<() => Promise<void>> {\n const lockPath = stateLockPath(sessionId);\n const deadline = Date.now() + STATE_LOCK_WAIT_MS;\n\n while (true) {\n try {\n await fs.mkdir(lockPath);\n const ownerId = randomUUID();\n const createdAt = new Date().toISOString();\n const metadata: StateLockMetadata = {\n ownerId,\n pid: process.pid,\n createdAt,\n heartbeatAt: createdAt,\n };\n await writeStateLockMetadata(sessionId, metadata);\n let released = false;\n const heartbeat = setInterval(() => {\n if (released) return;\n void writeStateLockMetadata(sessionId, {\n ...metadata,\n heartbeatAt: new Date().toISOString(),\n }).catch(() => undefined);\n }, STATE_LOCK_HEARTBEAT_MS);\n heartbeat.unref?.();\n\n return async () => {\n if (released) return;\n released = true;\n clearInterval(heartbeat);\n const currentMetadata = await readStateLockMetadata(sessionId);\n if (currentMetadata?.ownerId === ownerId) {\n await fs.rm(lockPath, { recursive: true, force: true }).catch(() => undefined);\n }\n };\n } catch (error) {\n const code = error && typeof error === \"object\" && \"code\" in error ? (error as { code?: unknown }).code : null;\n if (code !== \"EEXIST\") {\n throw error;\n }\n\n if (await tryRemoveStaleStateLock(sessionId)) {\n continue;\n }\n\n if (Date.now() >= deadline) {\n throw new Error(`Timed out acquiring hook state lock for session ${sessionId}.`);\n }\n await sleep(STATE_LOCK_POLL_MS);\n }\n }\n}\n\nasync function withStateLock<T>(sessionId: string, fn: () => Promise<T>): Promise<T> {\n const release = await acquireStateLock(sessionId);\n try {\n return await fn();\n } finally {\n await release();\n }\n}\n\nfunction normalizeIntent(value: unknown): TurnIntent {\n return value === \"memory_first\" || value === \"collab_state\" || value === \"git_facts\" ? value : \"neutral\";\n}\n\nfunction normalizeString(value: unknown): string | null {\n return typeof value === \"string\" && value.trim() ? value.trim() : null;\n}\n\nfunction normalizeStringArray(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n return Array.from(\n new Set(\n value\n .filter((entry): entry is string => typeof entry === \"string\" && entry.trim().length > 0)\n .map((entry) => entry.trim()),\n ),\n );\n}\n\nfunction normalizeTouchedRepo(value: unknown, repoRoot: string): TouchedRepoState | null {\n if (!value || typeof value !== \"object\") return null;\n const parsed = value as Partial<TouchedRepoState>;\n const normalizedRepoRoot = normalizeString(parsed.repoRoot) ?? repoRoot.trim();\n if (!normalizedRepoRoot) return null;\n\n return {\n repoRoot: normalizedRepoRoot,\n projectId: normalizeString(parsed.projectId),\n currentAppId: normalizeString(parsed.currentAppId),\n upstreamAppId: normalizeString(parsed.upstreamAppId),\n firstTouchedAt: normalizeString(parsed.firstTouchedAt) ?? new Date().toISOString(),\n lastTouchedAt: normalizeString(parsed.lastTouchedAt) ?? new Date().toISOString(),\n lastObservedWriteAt: normalizeString(parsed.lastObservedWriteAt),\n touchedBy: normalizeStringArray(parsed.touchedBy),\n hasObservedWrite: Boolean(parsed.hasObservedWrite),\n manuallyRecorded: Boolean(parsed.manuallyRecorded),\n manuallyRecordedAt: normalizeString(parsed.manuallyRecordedAt),\n manuallyRecordedByTool: normalizeString(parsed.manuallyRecordedByTool),\n stopAttempted: Boolean(parsed.stopAttempted),\n stopRecorded: Boolean(parsed.stopRecorded),\n stopRecordedAt: normalizeString(parsed.stopRecordedAt),\n stopRecordedMode: parsed.stopRecordedMode === \"changed_turn\" || parsed.stopRecordedMode === \"no_diff_turn\" ? parsed.stopRecordedMode : null,\n recordingFailureMessage: normalizeString(parsed.recordingFailureMessage),\n recordingFailureHint: normalizeString(parsed.recordingFailureHint),\n recordingFailedAt: normalizeString(parsed.recordingFailedAt),\n };\n}\n\nfunction normalizeTouchedRepos(value: unknown): Record<string, TouchedRepoState> {\n if (!value || typeof value !== \"object\") return {};\n const entries = Object.entries(value as Record<string, unknown>)\n .map(([repoRoot, repo]) => normalizeTouchedRepo(repo, repoRoot))\n .filter((repo): repo is TouchedRepoState => repo !== null)\n .sort((a, b) => a.repoRoot.localeCompare(b.repoRoot));\n return Object.fromEntries(entries.map((repo) => [repo.repoRoot, repo]));\n}\n\nfunction createTouchedRepo(params: {\n repoRoot: string;\n projectId?: string | null;\n currentAppId?: string | null;\n upstreamAppId?: string | null;\n touchedBy?: string | null;\n hasObservedWrite?: boolean;\n}): TouchedRepoState {\n const now = new Date().toISOString();\n const touchedBy = params.touchedBy?.trim() ? [params.touchedBy.trim()] : [];\n return {\n repoRoot: params.repoRoot,\n projectId: normalizeString(params.projectId),\n currentAppId: normalizeString(params.currentAppId),\n upstreamAppId: normalizeString(params.upstreamAppId),\n firstTouchedAt: now,\n lastTouchedAt: now,\n lastObservedWriteAt: params.hasObservedWrite ? now : null,\n touchedBy,\n hasObservedWrite: Boolean(params.hasObservedWrite),\n manuallyRecorded: false,\n manuallyRecordedAt: null,\n manuallyRecordedByTool: null,\n stopAttempted: false,\n stopRecorded: false,\n stopRecordedAt: null,\n stopRecordedMode: null,\n recordingFailureMessage: null,\n recordingFailureHint: null,\n recordingFailedAt: null,\n };\n}\n\nasync function updatePendingTurnState(\n sessionId: string,\n updater: (state: PendingTurnState) => void | boolean,\n): Promise<PendingTurnState | null> {\n return withStateLock(sessionId, async () => {\n const existing = await loadPendingTurnState(sessionId);\n if (!existing) return null;\n const result = updater(existing);\n if (result === false) return existing;\n await savePendingTurnState(existing);\n return existing;\n });\n}\n\nexport async function loadPendingTurnState(sessionId: string): Promise<PendingTurnState | null> {\n const raw = await fs.readFile(statePath(sessionId), \"utf8\").catch(() => null);\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as Partial<PendingTurnState>;\n if (!parsed || typeof parsed !== \"object\") return null;\n if (typeof parsed.sessionId !== \"string\" || typeof parsed.turnId !== \"string\" || typeof parsed.prompt !== \"string\") {\n return null;\n }\n return {\n sessionId: parsed.sessionId,\n turnId: parsed.turnId,\n prompt: parsed.prompt,\n initialCwd: normalizeString(parsed.initialCwd),\n intent: normalizeIntent(parsed.intent),\n submittedAt: typeof parsed.submittedAt === \"string\" ? parsed.submittedAt : new Date().toISOString(),\n consultedMemory: Boolean(parsed.consultedMemory),\n touchedRepos: normalizeTouchedRepos(parsed.touchedRepos),\n turnFailureMessage: normalizeString(parsed.turnFailureMessage),\n turnFailureHint: normalizeString(parsed.turnFailureHint),\n turnFailedAt: normalizeString(parsed.turnFailedAt),\n };\n } catch {\n return null;\n }\n}\n\nexport async function savePendingTurnState(state: PendingTurnState): Promise<void> {\n await writeJsonAtomic(statePath(state.sessionId), state);\n}\n\nexport async function createPendingTurnState(params: {\n sessionId: string;\n prompt: string;\n initialCwd?: string | null;\n intent: TurnIntent;\n}): Promise<PendingTurnState> {\n return withStateLock(params.sessionId, async () => {\n const state: PendingTurnState = {\n sessionId: params.sessionId,\n turnId: randomUUID(),\n prompt: params.prompt,\n initialCwd: params.initialCwd?.trim() || null,\n intent: params.intent,\n submittedAt: new Date().toISOString(),\n consultedMemory: false,\n touchedRepos: {},\n turnFailureMessage: null,\n turnFailureHint: null,\n turnFailedAt: null,\n };\n await savePendingTurnState(state);\n return state;\n });\n}\n\nexport async function upsertTouchedRepo(\n sessionId: string,\n params: {\n repoRoot: string;\n projectId?: string | null;\n currentAppId?: string | null;\n upstreamAppId?: string | null;\n touchedBy?: string | null;\n hasObservedWrite?: boolean;\n },\n): Promise<TouchedRepoState | null> {\n const normalizedRepoRoot = params.repoRoot.trim();\n if (!normalizedRepoRoot) return null;\n const state = await updatePendingTurnState(sessionId, (existing) => {\n const current =\n existing.touchedRepos[normalizedRepoRoot] ??\n createTouchedRepo({\n repoRoot: normalizedRepoRoot,\n projectId: params.projectId,\n currentAppId: params.currentAppId,\n upstreamAppId: params.upstreamAppId,\n touchedBy: params.touchedBy,\n hasObservedWrite: params.hasObservedWrite,\n });\n\n current.projectId = normalizeString(params.projectId) ?? current.projectId;\n current.currentAppId = normalizeString(params.currentAppId) ?? current.currentAppId;\n current.upstreamAppId = normalizeString(params.upstreamAppId) ?? current.upstreamAppId;\n current.lastTouchedAt = new Date().toISOString();\n if (params.touchedBy?.trim() && !current.touchedBy.includes(params.touchedBy.trim())) {\n current.touchedBy = [...current.touchedBy, params.touchedBy.trim()].sort((a, b) => a.localeCompare(b));\n }\n if (params.hasObservedWrite) {\n current.hasObservedWrite = true;\n current.lastObservedWriteAt = new Date().toISOString();\n }\n existing.touchedRepos[normalizedRepoRoot] = current;\n });\n return state?.touchedRepos[normalizedRepoRoot] ?? null;\n}\n\nexport async function markTouchedRepoObservedWrite(\n sessionId: string,\n repoRoot: string,\n params?: { toolName?: string | null },\n): Promise<void> {\n await upsertTouchedRepo(sessionId, {\n repoRoot,\n touchedBy: params?.toolName ?? null,\n hasObservedWrite: true,\n });\n}\n\nexport async function markTouchedRepoManuallyRecorded(\n sessionId: string,\n repoRoot: string,\n params?: { toolName?: string | null },\n): Promise<void> {\n await updatePendingTurnState(sessionId, (existing) => {\n const normalizedRepoRoot = repoRoot.trim();\n if (!normalizedRepoRoot) return false;\n const current =\n existing.touchedRepos[normalizedRepoRoot] ??\n createTouchedRepo({\n repoRoot: normalizedRepoRoot,\n touchedBy: params?.toolName ?? null,\n hasObservedWrite: false,\n });\n current.lastTouchedAt = new Date().toISOString();\n current.manuallyRecorded = true;\n current.manuallyRecordedAt = new Date().toISOString();\n current.manuallyRecordedByTool = normalizeString(params?.toolName) ?? current.manuallyRecordedByTool;\n current.recordingFailureMessage = null;\n current.recordingFailureHint = null;\n current.recordingFailedAt = null;\n if (params?.toolName?.trim() && !current.touchedBy.includes(params.toolName.trim())) {\n current.touchedBy = [...current.touchedBy, params.toolName.trim()].sort((a, b) => a.localeCompare(b));\n }\n existing.touchedRepos[normalizedRepoRoot] = current;\n });\n}\n\nexport async function markPendingTurnConsultedMemory(sessionId: string): Promise<void> {\n await updatePendingTurnState(sessionId, (existing) => {\n if (existing.consultedMemory) return false;\n existing.consultedMemory = true;\n });\n}\n\nexport async function markTouchedRepoStopAttempted(sessionId: string, repoRoot: string): Promise<void> {\n await updatePendingTurnState(sessionId, (existing) => {\n const current = existing.touchedRepos[repoRoot];\n if (!current) return false;\n current.stopAttempted = true;\n current.lastTouchedAt = new Date().toISOString();\n });\n}\n\nexport async function markTouchedRepoStopRecorded(\n sessionId: string,\n repoRoot: string,\n params: {\n mode: RepoRecordMode;\n },\n): Promise<void> {\n await updatePendingTurnState(sessionId, (existing) => {\n const current = existing.touchedRepos[repoRoot];\n if (!current) return false;\n current.stopAttempted = true;\n current.stopRecorded = true;\n current.stopRecordedAt = new Date().toISOString();\n current.stopRecordedMode = params.mode;\n current.recordingFailureMessage = null;\n current.recordingFailureHint = null;\n current.recordingFailedAt = null;\n current.lastTouchedAt = new Date().toISOString();\n });\n}\n\nexport async function markTouchedRepoRecordingFailure(\n sessionId: string,\n repoRoot: string,\n params: {\n message: string;\n hint?: string | null;\n },\n): Promise<void> {\n await updatePendingTurnState(sessionId, (existing) => {\n const current = existing.touchedRepos[repoRoot];\n if (!current) return false;\n current.stopAttempted = true;\n current.recordingFailureMessage = params.message.trim();\n current.recordingFailureHint = params.hint?.trim() || null;\n current.recordingFailedAt = new Date().toISOString();\n current.lastTouchedAt = new Date().toISOString();\n });\n}\n\nexport async function markPendingTurnFailure(\n sessionId: string,\n params: {\n message: string;\n hint?: string | null;\n },\n): Promise<void> {\n await updatePendingTurnState(sessionId, (existing) => {\n existing.turnFailureMessage = params.message.trim();\n existing.turnFailureHint = params.hint?.trim() || null;\n existing.turnFailedAt = new Date().toISOString();\n });\n}\n\nexport async function listTouchedRepos(sessionId: string): Promise<TouchedRepoState[]> {\n const existing = await loadPendingTurnState(sessionId);\n if (!existing) return [];\n return Object.values(existing.touchedRepos).sort((a, b) => a.repoRoot.localeCompare(b.repoRoot));\n}\n\nexport async function clearPendingTurnState(sessionId: string): Promise<void> {\n await withStateLock(sessionId, async () => {\n await fs.rm(statePath(sessionId), { force: true }).catch(() => undefined);\n });\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport { readCollabBinding } from \"@remixhq/core/binding\";\n\ntype BindingSummary = {\n repoRoot: string;\n projectId: string | null;\n currentAppId: string | null;\n upstreamAppId: string | null;\n};\n\nexport async function readJsonStdin(): Promise<Record<string, unknown>> {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));\n }\n const raw = Buffer.concat(chunks).toString(\"utf8\").trim();\n if (!raw) return {};\n try {\n const parsed = JSON.parse(raw);\n return parsed && typeof parsed === \"object\" ? (parsed as Record<string, unknown>) : {};\n } catch {\n return {};\n }\n}\n\nfunction getNestedRecord(value: unknown): Record<string, unknown> | null {\n return value && typeof value === \"object\" ? (value as Record<string, unknown>) : null;\n}\n\nexport function extractToolInput(payload: Record<string, unknown>): Record<string, unknown> {\n return getNestedRecord(payload.tool_input) ?? getNestedRecord(payload.toolInput) ?? payload;\n}\n\nexport function extractToolResponse(payload: Record<string, unknown>): Record<string, unknown> | null {\n return getNestedRecord(payload.tool_response) ?? getNestedRecord(payload.toolResponse);\n}\n\nexport function extractToolName(payload: Record<string, unknown>): string | null {\n return extractString(payload, [\"tool_name\", \"toolName\"]);\n}\n\nexport function extractString(input: Record<string, unknown>, keys: string[]): string | null {\n for (const key of keys) {\n const value = input[key];\n if (typeof value === \"string\" && value.trim()) {\n return value.trim();\n }\n }\n return null;\n}\n\nexport function extractBoolean(input: Record<string, unknown>, keys: string[]): boolean | null {\n for (const key of keys) {\n const value = input[key];\n if (typeof value === \"boolean\") {\n return value;\n }\n }\n return null;\n}\n\nexport function extractToolCwd(payload: Record<string, unknown>): string | null {\n const toolInput = extractToolInput(payload);\n return extractString(toolInput, [\"cwd\"]) ?? extractString(payload, [\"cwd\"]);\n}\n\nexport function didToolSucceed(payload: Record<string, unknown>): boolean {\n const toolResponse = extractToolResponse(payload);\n const explicitSuccess = toolResponse ? extractBoolean(toolResponse, [\"success\", \"ok\"]) : null;\n if (explicitSuccess !== null) {\n return explicitSuccess;\n }\n const hookEventName = extractString(payload, [\"hook_event_name\", \"hookEventName\"]);\n return hookEventName === \"PostToolUse\";\n}\n\nfunction collectStringPathValue(value: unknown): string[] {\n if (typeof value === \"string\" && value.trim()) return [value.trim()];\n if (Array.isArray(value)) {\n return value.flatMap((entry) => collectStringPathValue(entry));\n }\n return [];\n}\n\nfunction collectPathTargetsFromObject(input: Record<string, unknown>, keys: string[]): string[] {\n return keys.flatMap((key) => collectStringPathValue(input[key]));\n}\n\nfunction resolveCandidatePath(targetPath: string, baseDir: string): string {\n return path.isAbsolute(targetPath) ? path.normalize(targetPath) : path.resolve(baseDir, targetPath);\n}\n\nexport function extractToolPathTargets(payload: Record<string, unknown>, toolName?: string | null): string[] {\n const name = (toolName ?? extractToolName(payload) ?? \"\").trim().toLowerCase();\n const toolInput = extractToolInput(payload);\n const baseDir = extractToolCwd(payload) ?? process.cwd();\n const baseKeys = [\"path\", \"paths\", \"file_path\", \"filePath\", \"target_file\", \"targetFile\", \"filename\"];\n\n const targets =\n name === \"notebookedit\"\n ? collectPathTargetsFromObject(toolInput, [\"target_notebook\", \"notebook_path\", \"notebookPath\", ...baseKeys])\n : collectPathTargetsFromObject(toolInput, baseKeys);\n\n return Array.from(new Set(targets.map((entry) => resolveCandidatePath(entry, baseDir))));\n}\n\nexport async function findBoundRepo(startPath: string | null): Promise<string | null> {\n if (!startPath) return null;\n let current = path.resolve(startPath);\n let stats = await fs.stat(current).catch(() => null);\n if (stats?.isFile()) {\n current = path.dirname(current);\n }\n\n while (true) {\n const bindingPath = path.join(current, \".remix\", \"config.json\");\n const bindingStats = await fs.stat(bindingPath).catch(() => null);\n if (bindingStats?.isFile()) return current;\n const parent = path.dirname(current);\n if (parent === current) return null;\n current = parent;\n }\n}\n\nexport async function resolveBoundRepoSummary(startPath: string | null): Promise<BindingSummary | null> {\n const repoRoot = await findBoundRepo(startPath);\n if (!repoRoot) return null;\n const binding = await readCollabBinding(repoRoot).catch(() => null);\n if (!binding) return null;\n return {\n repoRoot,\n projectId: binding.projectId,\n currentAppId: binding.currentAppId,\n upstreamAppId: binding.upstreamAppId,\n };\n}\n\nexport async function resolveTouchedBoundReposFromPaths(paths: string[]): Promise<BindingSummary[]> {\n const resolved = await Promise.all(paths.map((targetPath) => resolveBoundRepoSummary(targetPath)));\n const unique = new Map<string, BindingSummary>();\n for (const repo of resolved) {\n if (!repo) continue;\n unique.set(repo.repoRoot, repo);\n }\n return Array.from(unique.values()).sort((a, b) => a.repoRoot.localeCompare(b.repoRoot));\n}\n\nexport async function resolveBoundRepoFromToolCwd(payload: Record<string, unknown>): Promise<BindingSummary | null> {\n return resolveBoundRepoSummary(extractToolCwd(payload));\n}\n","import {\n RemixError\n} from \"./chunk-YZ34ICNN.js\";\n\n// src/infrastructure/binding/collabBindingStore.ts\nimport fs2 from \"fs/promises\";\nimport path2 from \"path\";\n\n// src/shared/fs.ts\nimport fs from \"fs/promises\";\nimport path from \"path\";\nasync function reserveDirectory(targetDir) {\n try {\n await fs.mkdir(targetDir);\n return targetDir;\n } catch (error) {\n if (error?.code === \"EEXIST\") {\n throw new RemixError(\"Output directory already exists.\", {\n exitCode: 2,\n hint: `Choose an empty destination path: ${targetDir}`\n });\n }\n throw error;\n }\n}\nasync function reserveAvailableDirPath(preferredDir) {\n const parent = path.dirname(preferredDir);\n const base = path.basename(preferredDir);\n for (let i = 1; i <= 1e3; i += 1) {\n const candidate = i === 1 ? preferredDir : path.join(parent, `${base}-${i}`);\n try {\n await fs.mkdir(candidate);\n return candidate;\n } catch (error) {\n if (error?.code === \"EEXIST\") continue;\n throw error;\n }\n }\n throw new RemixError(\"No available output directory name.\", {\n exitCode: 2,\n hint: `Tried ${base} through ${base}-1000 under ${parent}.`\n });\n}\nasync function writeJsonAtomic(filePath, value) {\n const dir = path.dirname(filePath);\n await fs.mkdir(dir, { recursive: true });\n const tmp = `${filePath}.tmp-${Date.now()}`;\n await fs.writeFile(tmp, `${JSON.stringify(value, null, 2)}\n`, \"utf8\");\n await fs.rename(tmp, filePath);\n}\n\n// src/infrastructure/binding/collabBindingStore.ts\nfunction getCollabBindingPath(repoRoot) {\n return path2.join(repoRoot, \".remix\", \"config.json\");\n}\nasync function readCollabBinding(repoRoot) {\n try {\n const raw = await fs2.readFile(getCollabBindingPath(repoRoot), \"utf8\");\n const parsed = JSON.parse(raw);\n if (parsed?.schemaVersion !== 1) return null;\n if (!parsed.projectId || !parsed.currentAppId || !parsed.upstreamAppId) return null;\n return {\n schemaVersion: 1,\n projectId: parsed.projectId,\n currentAppId: parsed.currentAppId,\n upstreamAppId: parsed.upstreamAppId,\n threadId: parsed.threadId ?? null,\n repoFingerprint: parsed.repoFingerprint ?? null,\n remoteUrl: parsed.remoteUrl ?? null,\n defaultBranch: parsed.defaultBranch ?? null,\n preferredBranch: parsed.preferredBranch ?? parsed.defaultBranch ?? null\n };\n } catch {\n return null;\n }\n}\nasync function writeCollabBinding(repoRoot, binding) {\n const filePath = getCollabBindingPath(repoRoot);\n await writeJsonAtomic(filePath, {\n schemaVersion: 1,\n ...binding\n });\n return filePath;\n}\n\nexport {\n reserveDirectory,\n reserveAvailableDirPath,\n getCollabBindingPath,\n readCollabBinding,\n writeCollabBinding\n};\n","import {\n markPendingTurnConsultedMemory,\n markTouchedRepoManuallyRecorded,\n markTouchedRepoObservedWrite,\n upsertTouchedRepo,\n} from \"./hook-state.js\";\nimport {\n didToolSucceed,\n extractToolName,\n extractToolPathTargets,\n readJsonStdin,\n resolveBoundRepoFromToolCwd,\n resolveTouchedBoundReposFromPaths,\n} from \"./hook-utils.js\";\n\nfunction isRecordingToolName(toolName: string): boolean {\n return /remix_collab_(add|add_change_step|record_turn|record_no_diff_turn)$/i.test(toolName);\n}\n\nfunction isRepoMutationToolName(toolName: string): boolean {\n return /remix_collab_(add|add_change_step|sync_apply|approve_and_sync_target|sync_upstream|reconcile_apply)$/i.test(toolName);\n}\n\nfunction isMemoryToolName(toolName: string): boolean {\n return /remix_collab_memory_(summary|search|timeline|change_step_diff)$/i.test(toolName);\n}\n\nfunction isStructuredLocalWriteToolName(toolName: string): boolean {\n return /^(Edit|MultiEdit|Write|Delete|NotebookEdit)$/i.test(toolName);\n}\n\nasync function main(): Promise<void> {\n const payload = await readJsonStdin();\n const sessionId = typeof payload.session_id === \"string\" && payload.session_id.trim() ? payload.session_id.trim() : null;\n const toolName = extractToolName(payload);\n if (!sessionId || !toolName) {\n return;\n }\n\n if (!didToolSucceed(payload)) {\n return;\n }\n\n if (isMemoryToolName(toolName)) {\n await markPendingTurnConsultedMemory(sessionId);\n }\n\n if (isRepoMutationToolName(toolName) || isRecordingToolName(toolName)) {\n const targetRepo = await resolveBoundRepoFromToolCwd(payload);\n if (targetRepo) {\n await upsertTouchedRepo(sessionId, {\n repoRoot: targetRepo.repoRoot,\n projectId: targetRepo.projectId,\n currentAppId: targetRepo.currentAppId,\n upstreamAppId: targetRepo.upstreamAppId,\n touchedBy: toolName,\n hasObservedWrite: isRepoMutationToolName(toolName),\n });\n if (isRepoMutationToolName(toolName)) {\n await markTouchedRepoObservedWrite(sessionId, targetRepo.repoRoot, { toolName });\n }\n if (isRecordingToolName(toolName)) {\n await markTouchedRepoManuallyRecorded(sessionId, targetRepo.repoRoot, { toolName });\n }\n }\n }\n\n if (isStructuredLocalWriteToolName(toolName)) {\n const touchedRepos = await resolveTouchedBoundReposFromPaths(extractToolPathTargets(payload, toolName));\n for (const repo of touchedRepos) {\n await upsertTouchedRepo(sessionId, {\n repoRoot: repo.repoRoot,\n projectId: repo.projectId,\n currentAppId: repo.currentAppId,\n upstreamAppId: repo.upstreamAppId,\n touchedBy: toolName,\n hasObservedWrite: true,\n });\n await markTouchedRepoObservedWrite(sessionId, repo.repoRoot, { toolName });\n }\n }\n}\n\nmain().catch((error) => {\n const message = error instanceof Error ? error.message : String(error);\n process.stderr.write(`${message}\\n`);\n process.exitCode = 0;\n});\n"],"mappings":"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","names":["fs","path","path","fs"]}
|
|
1
|
+
{"version":3,"sources":["../src/hook-state.ts","../src/hook-utils.ts","../node_modules/@remixhq/core/dist/chunk-FAZUMWBS.js","../src/hook-post-collab.ts"],"sourcesContent":["import fs from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\nimport { randomUUID } from \"node:crypto\";\n\nimport type { TurnIntent } from \"./history-routing.js\";\n\nexport type RepoRecordMode = \"changed_turn\" | \"no_diff_turn\";\n\nexport type TouchedRepoState = {\n repoRoot: string;\n projectId: string | null;\n currentAppId: string | null;\n upstreamAppId: string | null;\n firstTouchedAt: string;\n lastTouchedAt: string;\n lastObservedWriteAt: string | null;\n touchedBy: string[];\n hasObservedWrite: boolean;\n manuallyRecorded: boolean;\n manuallyRecordedAt: string | null;\n manuallyRecordedByTool: string | null;\n stopAttempted: boolean;\n stopRecorded: boolean;\n stopRecordedAt: string | null;\n stopRecordedMode: RepoRecordMode | null;\n recordingFailureMessage: string | null;\n recordingFailureHint: string | null;\n recordingFailedAt: string | null;\n};\n\nexport type PendingTurnState = {\n sessionId: string;\n turnId: string;\n prompt: string;\n initialCwd: string | null;\n intent: TurnIntent;\n submittedAt: string;\n consultedMemory: boolean;\n touchedRepos: Record<string, TouchedRepoState>;\n turnFailureMessage: string | null;\n turnFailureHint: string | null;\n turnFailedAt: string | null;\n};\n\nfunction stateRoot(): string {\n return path.join(os.tmpdir(), \"remix-claude-plugin-hooks\");\n}\n\nfunction statePath(sessionId: string): string {\n return path.join(stateRoot(), `${sessionId}.json`);\n}\n\nfunction stateLockPath(sessionId: string): string {\n return path.join(stateRoot(), `${sessionId}.lock`);\n}\n\nfunction stateLockMetaPath(sessionId: string): string {\n return path.join(stateLockPath(sessionId), \"owner.json\");\n}\n\nasync function writeJsonAtomic(filePath: string, value: unknown): Promise<void> {\n await fs.mkdir(path.dirname(filePath), { recursive: true });\n const tmpPath = `${filePath}.tmp-${Date.now()}-${Math.random().toString(16).slice(2)}`;\n await fs.writeFile(tmpPath, JSON.stringify(value, null, 2) + \"\\n\", \"utf8\");\n await fs.rename(tmpPath, filePath);\n}\n\nconst STATE_LOCK_WAIT_MS = 2_000;\nconst STATE_LOCK_POLL_MS = 25;\nconst STATE_LOCK_STALE_MS = 30_000;\nconst STATE_LOCK_HEARTBEAT_MS = 5_000;\n\ntype StateLockMetadata = {\n ownerId: string;\n pid: number;\n createdAt: string;\n heartbeatAt: string;\n};\n\nasync function sleep(ms: number): Promise<void> {\n await new Promise((resolve) => setTimeout(resolve, ms));\n}\n\nasync function readStateLockMetadata(sessionId: string): Promise<StateLockMetadata | null> {\n const raw = await fs.readFile(stateLockMetaPath(sessionId), \"utf8\").catch(() => null);\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as Partial<StateLockMetadata>;\n if (\n typeof parsed.ownerId !== \"string\" ||\n typeof parsed.pid !== \"number\" ||\n typeof parsed.createdAt !== \"string\" ||\n typeof parsed.heartbeatAt !== \"string\"\n ) {\n return null;\n }\n return {\n ownerId: parsed.ownerId,\n pid: parsed.pid,\n createdAt: parsed.createdAt,\n heartbeatAt: parsed.heartbeatAt,\n };\n } catch {\n return null;\n }\n}\n\nasync function writeStateLockMetadata(sessionId: string, metadata: StateLockMetadata): Promise<void> {\n await writeJsonAtomic(stateLockMetaPath(sessionId), metadata);\n}\n\nasync function tryRemoveStaleStateLock(sessionId: string): Promise<boolean> {\n const lockPath = stateLockPath(sessionId);\n const metadata = await readStateLockMetadata(sessionId);\n const staleByHeartbeat =\n metadata && Date.now() - new Date(metadata.heartbeatAt).getTime() > STATE_LOCK_STALE_MS;\n if (staleByHeartbeat) {\n await fs.rm(lockPath, { recursive: true, force: true }).catch(() => undefined);\n return true;\n }\n\n if (!metadata) {\n const lockStat = await fs.stat(lockPath).catch(() => null);\n if (lockStat && Date.now() - lockStat.mtimeMs > STATE_LOCK_STALE_MS) {\n await fs.rm(lockPath, { recursive: true, force: true }).catch(() => undefined);\n return true;\n }\n }\n\n return false;\n}\n\nasync function acquireStateLock(sessionId: string): Promise<() => Promise<void>> {\n const lockPath = stateLockPath(sessionId);\n const deadline = Date.now() + STATE_LOCK_WAIT_MS;\n\n while (true) {\n try {\n await fs.mkdir(lockPath);\n const ownerId = randomUUID();\n const createdAt = new Date().toISOString();\n const metadata: StateLockMetadata = {\n ownerId,\n pid: process.pid,\n createdAt,\n heartbeatAt: createdAt,\n };\n await writeStateLockMetadata(sessionId, metadata);\n let released = false;\n const heartbeat = setInterval(() => {\n if (released) return;\n void writeStateLockMetadata(sessionId, {\n ...metadata,\n heartbeatAt: new Date().toISOString(),\n }).catch(() => undefined);\n }, STATE_LOCK_HEARTBEAT_MS);\n heartbeat.unref?.();\n\n return async () => {\n if (released) return;\n released = true;\n clearInterval(heartbeat);\n const currentMetadata = await readStateLockMetadata(sessionId);\n if (currentMetadata?.ownerId === ownerId) {\n await fs.rm(lockPath, { recursive: true, force: true }).catch(() => undefined);\n }\n };\n } catch (error) {\n const code = error && typeof error === \"object\" && \"code\" in error ? (error as { code?: unknown }).code : null;\n if (code !== \"EEXIST\") {\n throw error;\n }\n\n if (await tryRemoveStaleStateLock(sessionId)) {\n continue;\n }\n\n if (Date.now() >= deadline) {\n throw new Error(`Timed out acquiring hook state lock for session ${sessionId}.`);\n }\n await sleep(STATE_LOCK_POLL_MS);\n }\n }\n}\n\nasync function withStateLock<T>(sessionId: string, fn: () => Promise<T>): Promise<T> {\n const release = await acquireStateLock(sessionId);\n try {\n return await fn();\n } finally {\n await release();\n }\n}\n\nfunction normalizeIntent(value: unknown): TurnIntent {\n return value === \"memory_first\" || value === \"collab_state\" || value === \"git_facts\" ? value : \"neutral\";\n}\n\nfunction normalizeString(value: unknown): string | null {\n return typeof value === \"string\" && value.trim() ? value.trim() : null;\n}\n\nfunction normalizeStringArray(value: unknown): string[] {\n if (!Array.isArray(value)) return [];\n return Array.from(\n new Set(\n value\n .filter((entry): entry is string => typeof entry === \"string\" && entry.trim().length > 0)\n .map((entry) => entry.trim()),\n ),\n );\n}\n\nfunction normalizeTouchedRepo(value: unknown, repoRoot: string): TouchedRepoState | null {\n if (!value || typeof value !== \"object\") return null;\n const parsed = value as Partial<TouchedRepoState>;\n const normalizedRepoRoot = normalizeString(parsed.repoRoot) ?? repoRoot.trim();\n if (!normalizedRepoRoot) return null;\n\n return {\n repoRoot: normalizedRepoRoot,\n projectId: normalizeString(parsed.projectId),\n currentAppId: normalizeString(parsed.currentAppId),\n upstreamAppId: normalizeString(parsed.upstreamAppId),\n firstTouchedAt: normalizeString(parsed.firstTouchedAt) ?? new Date().toISOString(),\n lastTouchedAt: normalizeString(parsed.lastTouchedAt) ?? new Date().toISOString(),\n lastObservedWriteAt: normalizeString(parsed.lastObservedWriteAt),\n touchedBy: normalizeStringArray(parsed.touchedBy),\n hasObservedWrite: Boolean(parsed.hasObservedWrite),\n manuallyRecorded: Boolean(parsed.manuallyRecorded),\n manuallyRecordedAt: normalizeString(parsed.manuallyRecordedAt),\n manuallyRecordedByTool: normalizeString(parsed.manuallyRecordedByTool),\n stopAttempted: Boolean(parsed.stopAttempted),\n stopRecorded: Boolean(parsed.stopRecorded),\n stopRecordedAt: normalizeString(parsed.stopRecordedAt),\n stopRecordedMode: parsed.stopRecordedMode === \"changed_turn\" || parsed.stopRecordedMode === \"no_diff_turn\" ? parsed.stopRecordedMode : null,\n recordingFailureMessage: normalizeString(parsed.recordingFailureMessage),\n recordingFailureHint: normalizeString(parsed.recordingFailureHint),\n recordingFailedAt: normalizeString(parsed.recordingFailedAt),\n };\n}\n\nfunction normalizeTouchedRepos(value: unknown): Record<string, TouchedRepoState> {\n if (!value || typeof value !== \"object\") return {};\n const entries = Object.entries(value as Record<string, unknown>)\n .map(([repoRoot, repo]) => normalizeTouchedRepo(repo, repoRoot))\n .filter((repo): repo is TouchedRepoState => repo !== null)\n .sort((a, b) => a.repoRoot.localeCompare(b.repoRoot));\n return Object.fromEntries(entries.map((repo) => [repo.repoRoot, repo]));\n}\n\nfunction createTouchedRepo(params: {\n repoRoot: string;\n projectId?: string | null;\n currentAppId?: string | null;\n upstreamAppId?: string | null;\n touchedBy?: string | null;\n hasObservedWrite?: boolean;\n}): TouchedRepoState {\n const now = new Date().toISOString();\n const touchedBy = params.touchedBy?.trim() ? [params.touchedBy.trim()] : [];\n return {\n repoRoot: params.repoRoot,\n projectId: normalizeString(params.projectId),\n currentAppId: normalizeString(params.currentAppId),\n upstreamAppId: normalizeString(params.upstreamAppId),\n firstTouchedAt: now,\n lastTouchedAt: now,\n lastObservedWriteAt: params.hasObservedWrite ? now : null,\n touchedBy,\n hasObservedWrite: Boolean(params.hasObservedWrite),\n manuallyRecorded: false,\n manuallyRecordedAt: null,\n manuallyRecordedByTool: null,\n stopAttempted: false,\n stopRecorded: false,\n stopRecordedAt: null,\n stopRecordedMode: null,\n recordingFailureMessage: null,\n recordingFailureHint: null,\n recordingFailedAt: null,\n };\n}\n\nasync function updatePendingTurnState(\n sessionId: string,\n updater: (state: PendingTurnState) => void | boolean,\n): Promise<PendingTurnState | null> {\n return withStateLock(sessionId, async () => {\n const existing = await loadPendingTurnState(sessionId);\n if (!existing) return null;\n const result = updater(existing);\n if (result === false) return existing;\n await savePendingTurnState(existing);\n return existing;\n });\n}\n\nexport async function loadPendingTurnState(sessionId: string): Promise<PendingTurnState | null> {\n const raw = await fs.readFile(statePath(sessionId), \"utf8\").catch(() => null);\n if (!raw) return null;\n try {\n const parsed = JSON.parse(raw) as Partial<PendingTurnState>;\n if (!parsed || typeof parsed !== \"object\") return null;\n if (typeof parsed.sessionId !== \"string\" || typeof parsed.turnId !== \"string\" || typeof parsed.prompt !== \"string\") {\n return null;\n }\n return {\n sessionId: parsed.sessionId,\n turnId: parsed.turnId,\n prompt: parsed.prompt,\n initialCwd: normalizeString(parsed.initialCwd),\n intent: normalizeIntent(parsed.intent),\n submittedAt: typeof parsed.submittedAt === \"string\" ? parsed.submittedAt : new Date().toISOString(),\n consultedMemory: Boolean(parsed.consultedMemory),\n touchedRepos: normalizeTouchedRepos(parsed.touchedRepos),\n turnFailureMessage: normalizeString(parsed.turnFailureMessage),\n turnFailureHint: normalizeString(parsed.turnFailureHint),\n turnFailedAt: normalizeString(parsed.turnFailedAt),\n };\n } catch {\n return null;\n }\n}\n\nexport async function savePendingTurnState(state: PendingTurnState): Promise<void> {\n await writeJsonAtomic(statePath(state.sessionId), state);\n}\n\nexport async function createPendingTurnState(params: {\n sessionId: string;\n prompt: string;\n initialCwd?: string | null;\n intent: TurnIntent;\n}): Promise<PendingTurnState> {\n return withStateLock(params.sessionId, async () => {\n const state: PendingTurnState = {\n sessionId: params.sessionId,\n turnId: randomUUID(),\n prompt: params.prompt,\n initialCwd: params.initialCwd?.trim() || null,\n intent: params.intent,\n submittedAt: new Date().toISOString(),\n consultedMemory: false,\n touchedRepos: {},\n turnFailureMessage: null,\n turnFailureHint: null,\n turnFailedAt: null,\n };\n await savePendingTurnState(state);\n return state;\n });\n}\n\nexport async function upsertTouchedRepo(\n sessionId: string,\n params: {\n repoRoot: string;\n projectId?: string | null;\n currentAppId?: string | null;\n upstreamAppId?: string | null;\n touchedBy?: string | null;\n hasObservedWrite?: boolean;\n },\n): Promise<TouchedRepoState | null> {\n const normalizedRepoRoot = params.repoRoot.trim();\n if (!normalizedRepoRoot) return null;\n const state = await updatePendingTurnState(sessionId, (existing) => {\n const current =\n existing.touchedRepos[normalizedRepoRoot] ??\n createTouchedRepo({\n repoRoot: normalizedRepoRoot,\n projectId: params.projectId,\n currentAppId: params.currentAppId,\n upstreamAppId: params.upstreamAppId,\n touchedBy: params.touchedBy,\n hasObservedWrite: params.hasObservedWrite,\n });\n\n current.projectId = normalizeString(params.projectId) ?? current.projectId;\n current.currentAppId = normalizeString(params.currentAppId) ?? current.currentAppId;\n current.upstreamAppId = normalizeString(params.upstreamAppId) ?? current.upstreamAppId;\n current.lastTouchedAt = new Date().toISOString();\n if (params.touchedBy?.trim() && !current.touchedBy.includes(params.touchedBy.trim())) {\n current.touchedBy = [...current.touchedBy, params.touchedBy.trim()].sort((a, b) => a.localeCompare(b));\n }\n if (params.hasObservedWrite) {\n current.hasObservedWrite = true;\n current.lastObservedWriteAt = new Date().toISOString();\n }\n existing.touchedRepos[normalizedRepoRoot] = current;\n });\n return state?.touchedRepos[normalizedRepoRoot] ?? null;\n}\n\nexport async function markTouchedRepoObservedWrite(\n sessionId: string,\n repoRoot: string,\n params?: { toolName?: string | null },\n): Promise<void> {\n await upsertTouchedRepo(sessionId, {\n repoRoot,\n touchedBy: params?.toolName ?? null,\n hasObservedWrite: true,\n });\n}\n\nexport async function markTouchedRepoManuallyRecorded(\n sessionId: string,\n repoRoot: string,\n params?: { toolName?: string | null },\n): Promise<void> {\n await updatePendingTurnState(sessionId, (existing) => {\n const normalizedRepoRoot = repoRoot.trim();\n if (!normalizedRepoRoot) return false;\n const current =\n existing.touchedRepos[normalizedRepoRoot] ??\n createTouchedRepo({\n repoRoot: normalizedRepoRoot,\n touchedBy: params?.toolName ?? null,\n hasObservedWrite: false,\n });\n current.lastTouchedAt = new Date().toISOString();\n current.manuallyRecorded = true;\n current.manuallyRecordedAt = new Date().toISOString();\n current.manuallyRecordedByTool = normalizeString(params?.toolName) ?? current.manuallyRecordedByTool;\n current.recordingFailureMessage = null;\n current.recordingFailureHint = null;\n current.recordingFailedAt = null;\n if (params?.toolName?.trim() && !current.touchedBy.includes(params.toolName.trim())) {\n current.touchedBy = [...current.touchedBy, params.toolName.trim()].sort((a, b) => a.localeCompare(b));\n }\n existing.touchedRepos[normalizedRepoRoot] = current;\n });\n}\n\nexport async function markPendingTurnConsultedMemory(sessionId: string): Promise<void> {\n await updatePendingTurnState(sessionId, (existing) => {\n if (existing.consultedMemory) return false;\n existing.consultedMemory = true;\n });\n}\n\nexport async function markTouchedRepoStopAttempted(sessionId: string, repoRoot: string): Promise<void> {\n await updatePendingTurnState(sessionId, (existing) => {\n const current = existing.touchedRepos[repoRoot];\n if (!current) return false;\n current.stopAttempted = true;\n current.lastTouchedAt = new Date().toISOString();\n });\n}\n\nexport async function markTouchedRepoStopRecorded(\n sessionId: string,\n repoRoot: string,\n params: {\n mode: RepoRecordMode;\n },\n): Promise<void> {\n await updatePendingTurnState(sessionId, (existing) => {\n const current = existing.touchedRepos[repoRoot];\n if (!current) return false;\n current.stopAttempted = true;\n current.stopRecorded = true;\n current.stopRecordedAt = new Date().toISOString();\n current.stopRecordedMode = params.mode;\n current.recordingFailureMessage = null;\n current.recordingFailureHint = null;\n current.recordingFailedAt = null;\n current.lastTouchedAt = new Date().toISOString();\n });\n}\n\nexport async function markTouchedRepoRecordingFailure(\n sessionId: string,\n repoRoot: string,\n params: {\n message: string;\n hint?: string | null;\n },\n): Promise<void> {\n await updatePendingTurnState(sessionId, (existing) => {\n const current = existing.touchedRepos[repoRoot];\n if (!current) return false;\n current.stopAttempted = true;\n current.recordingFailureMessage = params.message.trim();\n current.recordingFailureHint = params.hint?.trim() || null;\n current.recordingFailedAt = new Date().toISOString();\n current.lastTouchedAt = new Date().toISOString();\n });\n}\n\nexport async function markPendingTurnFailure(\n sessionId: string,\n params: {\n message: string;\n hint?: string | null;\n },\n): Promise<void> {\n await updatePendingTurnState(sessionId, (existing) => {\n existing.turnFailureMessage = params.message.trim();\n existing.turnFailureHint = params.hint?.trim() || null;\n existing.turnFailedAt = new Date().toISOString();\n });\n}\n\nexport async function listTouchedRepos(sessionId: string): Promise<TouchedRepoState[]> {\n const existing = await loadPendingTurnState(sessionId);\n if (!existing) return [];\n return Object.values(existing.touchedRepos).sort((a, b) => a.repoRoot.localeCompare(b.repoRoot));\n}\n\nexport async function clearPendingTurnState(sessionId: string): Promise<void> {\n await withStateLock(sessionId, async () => {\n await fs.rm(statePath(sessionId), { force: true }).catch(() => undefined);\n });\n}\n","import fs from \"node:fs/promises\";\nimport path from \"node:path\";\n\nimport { readCollabBinding } from \"@remixhq/core/binding\";\n\ntype BindingSummary = {\n repoRoot: string;\n projectId: string | null;\n currentAppId: string | null;\n upstreamAppId: string | null;\n};\n\nexport async function readJsonStdin(): Promise<Record<string, unknown>> {\n const chunks: Buffer[] = [];\n for await (const chunk of process.stdin) {\n chunks.push(Buffer.isBuffer(chunk) ? chunk : Buffer.from(String(chunk)));\n }\n const raw = Buffer.concat(chunks).toString(\"utf8\").trim();\n if (!raw) return {};\n try {\n const parsed = JSON.parse(raw);\n return parsed && typeof parsed === \"object\" ? (parsed as Record<string, unknown>) : {};\n } catch {\n return {};\n }\n}\n\nfunction getNestedRecord(value: unknown): Record<string, unknown> | null {\n return value && typeof value === \"object\" ? (value as Record<string, unknown>) : null;\n}\n\nexport function extractToolInput(payload: Record<string, unknown>): Record<string, unknown> {\n return getNestedRecord(payload.tool_input) ?? getNestedRecord(payload.toolInput) ?? payload;\n}\n\nexport function extractToolResponse(payload: Record<string, unknown>): Record<string, unknown> | null {\n return getNestedRecord(payload.tool_response) ?? getNestedRecord(payload.toolResponse);\n}\n\nexport function extractToolName(payload: Record<string, unknown>): string | null {\n return extractString(payload, [\"tool_name\", \"toolName\"]);\n}\n\nexport function extractString(input: Record<string, unknown>, keys: string[]): string | null {\n for (const key of keys) {\n const value = input[key];\n if (typeof value === \"string\" && value.trim()) {\n return value.trim();\n }\n }\n return null;\n}\n\nexport function extractBoolean(input: Record<string, unknown>, keys: string[]): boolean | null {\n for (const key of keys) {\n const value = input[key];\n if (typeof value === \"boolean\") {\n return value;\n }\n }\n return null;\n}\n\nexport function extractToolCwd(payload: Record<string, unknown>): string | null {\n const toolInput = extractToolInput(payload);\n return extractString(toolInput, [\"cwd\"]) ?? extractString(payload, [\"cwd\"]);\n}\n\nexport function didToolSucceed(payload: Record<string, unknown>): boolean {\n const toolResponse = extractToolResponse(payload);\n const explicitSuccess = toolResponse ? extractBoolean(toolResponse, [\"success\", \"ok\"]) : null;\n if (explicitSuccess !== null) {\n return explicitSuccess;\n }\n const hookEventName = extractString(payload, [\"hook_event_name\", \"hookEventName\"]);\n return hookEventName === \"PostToolUse\";\n}\n\nfunction collectStringPathValue(value: unknown): string[] {\n if (typeof value === \"string\" && value.trim()) return [value.trim()];\n if (Array.isArray(value)) {\n return value.flatMap((entry) => collectStringPathValue(entry));\n }\n return [];\n}\n\nfunction collectPathTargetsFromObject(input: Record<string, unknown>, keys: string[]): string[] {\n return keys.flatMap((key) => collectStringPathValue(input[key]));\n}\n\nfunction resolveCandidatePath(targetPath: string, baseDir: string): string {\n return path.isAbsolute(targetPath) ? path.normalize(targetPath) : path.resolve(baseDir, targetPath);\n}\n\nexport function extractToolPathTargets(payload: Record<string, unknown>, toolName?: string | null): string[] {\n const name = (toolName ?? extractToolName(payload) ?? \"\").trim().toLowerCase();\n const toolInput = extractToolInput(payload);\n const baseDir = extractToolCwd(payload) ?? process.cwd();\n const baseKeys = [\"path\", \"paths\", \"file_path\", \"filePath\", \"target_file\", \"targetFile\", \"filename\"];\n\n const targets =\n name === \"notebookedit\"\n ? collectPathTargetsFromObject(toolInput, [\"target_notebook\", \"notebook_path\", \"notebookPath\", ...baseKeys])\n : collectPathTargetsFromObject(toolInput, baseKeys);\n\n return Array.from(new Set(targets.map((entry) => resolveCandidatePath(entry, baseDir))));\n}\n\nexport async function findBoundRepo(startPath: string | null): Promise<string | null> {\n if (!startPath) return null;\n let current = path.resolve(startPath);\n let stats = await fs.stat(current).catch(() => null);\n if (stats?.isFile()) {\n current = path.dirname(current);\n }\n\n while (true) {\n const bindingPath = path.join(current, \".remix\", \"config.json\");\n const bindingStats = await fs.stat(bindingPath).catch(() => null);\n if (bindingStats?.isFile()) return current;\n const parent = path.dirname(current);\n if (parent === current) return null;\n current = parent;\n }\n}\n\nexport async function resolveBoundRepoSummary(startPath: string | null): Promise<BindingSummary | null> {\n const repoRoot = await findBoundRepo(startPath);\n if (!repoRoot) return null;\n const binding = await readCollabBinding(repoRoot).catch(() => null);\n if (!binding) return null;\n return {\n repoRoot,\n projectId: binding.projectId,\n currentAppId: binding.currentAppId,\n upstreamAppId: binding.upstreamAppId,\n };\n}\n\nexport async function resolveTouchedBoundReposFromPaths(paths: string[]): Promise<BindingSummary[]> {\n const resolved = await Promise.all(paths.map((targetPath) => resolveBoundRepoSummary(targetPath)));\n const unique = new Map<string, BindingSummary>();\n for (const repo of resolved) {\n if (!repo) continue;\n unique.set(repo.repoRoot, repo);\n }\n return Array.from(unique.values()).sort((a, b) => a.repoRoot.localeCompare(b.repoRoot));\n}\n\nexport async function resolveBoundRepoFromToolCwd(payload: Record<string, unknown>): Promise<BindingSummary | null> {\n return resolveBoundRepoSummary(extractToolCwd(payload));\n}\n","import {\n RemixError\n} from \"./chunk-YZ34ICNN.js\";\n\n// src/infrastructure/binding/collabBindingStore.ts\nimport fs2 from \"fs/promises\";\nimport path2 from \"path\";\n\n// src/shared/fs.ts\nimport fs from \"fs/promises\";\nimport path from \"path\";\nasync function reserveDirectory(targetDir) {\n try {\n await fs.mkdir(targetDir);\n return targetDir;\n } catch (error) {\n if (error?.code === \"EEXIST\") {\n throw new RemixError(\"Output directory already exists.\", {\n exitCode: 2,\n hint: `Choose an empty destination path: ${targetDir}`\n });\n }\n throw error;\n }\n}\nasync function reserveAvailableDirPath(preferredDir) {\n const parent = path.dirname(preferredDir);\n const base = path.basename(preferredDir);\n for (let i = 1; i <= 1e3; i += 1) {\n const candidate = i === 1 ? preferredDir : path.join(parent, `${base}-${i}`);\n try {\n await fs.mkdir(candidate);\n return candidate;\n } catch (error) {\n if (error?.code === \"EEXIST\") continue;\n throw error;\n }\n }\n throw new RemixError(\"No available output directory name.\", {\n exitCode: 2,\n hint: `Tried ${base} through ${base}-1000 under ${parent}.`\n });\n}\nasync function writeJsonAtomic(filePath, value) {\n const dir = path.dirname(filePath);\n await fs.mkdir(dir, { recursive: true });\n const tmp = `${filePath}.tmp-${Date.now()}`;\n await fs.writeFile(tmp, `${JSON.stringify(value, null, 2)}\n`, \"utf8\");\n await fs.rename(tmp, filePath);\n}\n\n// src/infrastructure/binding/collabBindingStore.ts\nfunction getCollabBindingPath(repoRoot) {\n return path2.join(repoRoot, \".remix\", \"config.json\");\n}\nasync function readCollabBinding(repoRoot) {\n try {\n const raw = await fs2.readFile(getCollabBindingPath(repoRoot), \"utf8\");\n const parsed = JSON.parse(raw);\n if (parsed?.schemaVersion !== 1) return null;\n if (!parsed.projectId || !parsed.currentAppId || !parsed.upstreamAppId) return null;\n return {\n schemaVersion: 1,\n projectId: parsed.projectId,\n currentAppId: parsed.currentAppId,\n upstreamAppId: parsed.upstreamAppId,\n threadId: parsed.threadId ?? null,\n repoFingerprint: parsed.repoFingerprint ?? null,\n remoteUrl: parsed.remoteUrl ?? null,\n defaultBranch: parsed.defaultBranch ?? null,\n preferredBranch: parsed.preferredBranch ?? parsed.defaultBranch ?? null\n };\n } catch {\n return null;\n }\n}\nasync function writeCollabBinding(repoRoot, binding) {\n const filePath = getCollabBindingPath(repoRoot);\n await writeJsonAtomic(filePath, {\n schemaVersion: 1,\n ...binding\n });\n return filePath;\n}\n\nexport {\n reserveDirectory,\n reserveAvailableDirPath,\n getCollabBindingPath,\n readCollabBinding,\n writeCollabBinding\n};\n","import {\n markPendingTurnConsultedMemory,\n markTouchedRepoManuallyRecorded,\n markTouchedRepoObservedWrite,\n upsertTouchedRepo,\n} from \"./hook-state.js\";\nimport {\n didToolSucceed,\n extractToolName,\n extractToolPathTargets,\n readJsonStdin,\n resolveBoundRepoFromToolCwd,\n resolveTouchedBoundReposFromPaths,\n} from \"./hook-utils.js\";\n\nfunction isRecordingToolName(toolName: string): boolean {\n return /remix_collab_(add|add_change_step|record_turn|record_no_diff_turn)$/i.test(toolName);\n}\n\nfunction isRepoMutationToolName(toolName: string): boolean {\n return /remix_collab_(add|add_change_step|sync_apply|approve_and_sync_target|sync_upstream|reconcile_apply)$/i.test(toolName);\n}\n\nfunction isMemoryToolName(toolName: string): boolean {\n return /remix_collab_memory_(summary|search|timeline|change_step_diff)$/i.test(toolName);\n}\n\nfunction isStructuredLocalWriteToolName(toolName: string): boolean {\n return /^(Edit|MultiEdit|Write|Delete|NotebookEdit)$/i.test(toolName);\n}\n\nasync function main(): Promise<void> {\n const payload = await readJsonStdin();\n const sessionId = typeof payload.session_id === \"string\" && payload.session_id.trim() ? payload.session_id.trim() : null;\n const toolName = extractToolName(payload);\n if (!sessionId || !toolName) {\n return;\n }\n\n if (!didToolSucceed(payload)) {\n return;\n }\n\n if (isMemoryToolName(toolName)) {\n await markPendingTurnConsultedMemory(sessionId);\n }\n\n if (isRepoMutationToolName(toolName) || isRecordingToolName(toolName)) {\n const targetRepo = await resolveBoundRepoFromToolCwd(payload);\n if (targetRepo) {\n await upsertTouchedRepo(sessionId, {\n repoRoot: targetRepo.repoRoot,\n projectId: targetRepo.projectId,\n currentAppId: targetRepo.currentAppId,\n upstreamAppId: targetRepo.upstreamAppId,\n touchedBy: toolName,\n hasObservedWrite: isRepoMutationToolName(toolName),\n });\n if (isRepoMutationToolName(toolName)) {\n await markTouchedRepoObservedWrite(sessionId, targetRepo.repoRoot, { toolName });\n }\n if (isRecordingToolName(toolName)) {\n await markTouchedRepoManuallyRecorded(sessionId, targetRepo.repoRoot, { toolName });\n }\n }\n }\n\n if (isStructuredLocalWriteToolName(toolName)) {\n const touchedRepos = await resolveTouchedBoundReposFromPaths(extractToolPathTargets(payload, toolName));\n for (const repo of touchedRepos) {\n await upsertTouchedRepo(sessionId, {\n repoRoot: repo.repoRoot,\n projectId: repo.projectId,\n currentAppId: repo.currentAppId,\n upstreamAppId: repo.upstreamAppId,\n touchedBy: toolName,\n hasObservedWrite: true,\n });\n await markTouchedRepoObservedWrite(sessionId, repo.repoRoot, { toolName });\n }\n }\n}\n\nmain().catch((error) => {\n const message = error instanceof Error ? error.message : String(error);\n process.stderr.write(`${message}\\n`);\n process.exitCode = 0;\n});\n"],"mappings":"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","names":["path","os","fs","import_promises","import_node_path","import_promises","path2","fs2","path","fs"]}
|
|
@@ -1,4 +1,27 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
"use strict";
|
|
3
|
+
var __create = Object.create;
|
|
4
|
+
var __defProp = Object.defineProperty;
|
|
5
|
+
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
|
|
6
|
+
var __getOwnPropNames = Object.getOwnPropertyNames;
|
|
7
|
+
var __getProtoOf = Object.getPrototypeOf;
|
|
8
|
+
var __hasOwnProp = Object.prototype.hasOwnProperty;
|
|
9
|
+
var __copyProps = (to, from, except, desc) => {
|
|
10
|
+
if (from && typeof from === "object" || typeof from === "function") {
|
|
11
|
+
for (let key of __getOwnPropNames(from))
|
|
12
|
+
if (!__hasOwnProp.call(to, key) && key !== except)
|
|
13
|
+
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
|
|
14
|
+
}
|
|
15
|
+
return to;
|
|
16
|
+
};
|
|
17
|
+
var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
|
|
18
|
+
// If the importer is in node compatibility mode or this is not an ESM
|
|
19
|
+
// file that has been converted to a CommonJS file using a Babel-
|
|
20
|
+
// compatible transform (i.e. "__esModule" has not been set), then set
|
|
21
|
+
// "default" to the CommonJS "module.exports" for node compatibility.
|
|
22
|
+
isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
|
|
23
|
+
mod
|
|
24
|
+
));
|
|
2
25
|
|
|
3
26
|
// src/history-routing.ts
|
|
4
27
|
var STRONG_MEMORY_FIRST_PATTERNS = [
|
|
@@ -43,15 +66,15 @@ function shouldPreferRemixMemory(intent) {
|
|
|
43
66
|
}
|
|
44
67
|
|
|
45
68
|
// src/hook-state.ts
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
69
|
+
var import_promises = __toESM(require("fs/promises"), 1);
|
|
70
|
+
var import_node_os = __toESM(require("os"), 1);
|
|
71
|
+
var import_node_path = __toESM(require("path"), 1);
|
|
72
|
+
var import_node_crypto = require("crypto");
|
|
50
73
|
function stateRoot() {
|
|
51
|
-
return
|
|
74
|
+
return import_node_path.default.join(import_node_os.default.tmpdir(), "remix-claude-plugin-hooks");
|
|
52
75
|
}
|
|
53
76
|
function statePath(sessionId) {
|
|
54
|
-
return
|
|
77
|
+
return import_node_path.default.join(stateRoot(), `${sessionId}.json`);
|
|
55
78
|
}
|
|
56
79
|
function normalizeIntent(value) {
|
|
57
80
|
return value === "memory_first" || value === "collab_state" || value === "git_facts" ? value : "neutral";
|
|
@@ -100,7 +123,7 @@ function normalizeTouchedRepos(value) {
|
|
|
100
123
|
return Object.fromEntries(entries.map((repo) => [repo.repoRoot, repo]));
|
|
101
124
|
}
|
|
102
125
|
async function loadPendingTurnState(sessionId) {
|
|
103
|
-
const raw = await
|
|
126
|
+
const raw = await import_promises.default.readFile(statePath(sessionId), "utf8").catch(() => null);
|
|
104
127
|
if (!raw) return null;
|
|
105
128
|
try {
|
|
106
129
|
const parsed = JSON.parse(raw);
|
|
@@ -127,8 +150,8 @@ async function loadPendingTurnState(sessionId) {
|
|
|
127
150
|
}
|
|
128
151
|
|
|
129
152
|
// src/hook-utils.ts
|
|
130
|
-
|
|
131
|
-
|
|
153
|
+
var import_promises2 = __toESM(require("fs/promises"), 1);
|
|
154
|
+
var import_node_path2 = __toESM(require("path"), 1);
|
|
132
155
|
async function readJsonStdin() {
|
|
133
156
|
const chunks = [];
|
|
134
157
|
for await (const chunk of process.stdin) {
|
|
@@ -164,16 +187,16 @@ function extractToolCwd(payload) {
|
|
|
164
187
|
}
|
|
165
188
|
async function findBoundRepo(startPath) {
|
|
166
189
|
if (!startPath) return null;
|
|
167
|
-
let current =
|
|
168
|
-
let stats = await
|
|
190
|
+
let current = import_node_path2.default.resolve(startPath);
|
|
191
|
+
let stats = await import_promises2.default.stat(current).catch(() => null);
|
|
169
192
|
if (stats?.isFile()) {
|
|
170
|
-
current =
|
|
193
|
+
current = import_node_path2.default.dirname(current);
|
|
171
194
|
}
|
|
172
195
|
while (true) {
|
|
173
|
-
const bindingPath =
|
|
174
|
-
const bindingStats = await
|
|
196
|
+
const bindingPath = import_node_path2.default.join(current, ".remix", "config.json");
|
|
197
|
+
const bindingStats = await import_promises2.default.stat(bindingPath).catch(() => null);
|
|
175
198
|
if (bindingStats?.isFile()) return current;
|
|
176
|
-
const parent =
|
|
199
|
+
const parent = import_node_path2.default.dirname(current);
|
|
177
200
|
if (parent === current) return null;
|
|
178
201
|
current = parent;
|
|
179
202
|
}
|
|
@@ -333,4 +356,4 @@ main().catch((error) => {
|
|
|
333
356
|
`);
|
|
334
357
|
process.exitCode = 0;
|
|
335
358
|
});
|
|
336
|
-
//# sourceMappingURL=hook-pre-git.
|
|
359
|
+
//# sourceMappingURL=hook-pre-git.cjs.map
|