@remixhq/claude-plugin 0.1.12 → 0.1.15

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "remix",
3
3
  "description": "Remix collaboration workflows for Claude Code",
4
- "version": "0.1.12",
4
+ "version": "0.1.15",
5
5
  "author": {
6
6
  "name": "Remix"
7
7
  },
@@ -22,10 +22,10 @@ Operating rules:
22
22
  - use `remix_collab_memory_change_step_diff` only after you have identified a specific `changeStepId`.
23
23
  8. Use raw git for historical reads only after Remix memory has narrowed the relevant change, or when the user explicitly asks for exact commit, blame, ancestry, or raw patch details.
24
24
  9. Clearly explain local mutation risk before using tools that can modify the local repo.
25
- 10. Assume the installed hook is the normal automatic recording path for completed assistant turns in a bound repo: changed turn => `remix_collab_add`, no-diff turn => `remix_collab_record_turn`.
26
- 11. Do not proactively call `remix_collab_add` or `remix_collab_record_turn` during normal work. The hook should do the automatic per-turn recording unless recovery is required.
27
- 12. Only use manual `remix_collab_add` or `remix_collab_record_turn` when the user explicitly asks for it, or when doing operational recovery, backfills, or debugging after automatic recording could not complete safely.
28
- 13. When you must choose manually, treat `remix_collab_add` or the clearer alias `remix_collab_add_change_step` as the code-diff tool, and treat `remix_collab_record_turn` or `remix_collab_record_no_diff_turn` as no-diff prompt/response history only.
25
+ 10. In a bound repo, Remix MCP tools are the required workflow layer for ordinary collaboration work.
26
+ 11. In a bound repo, exactly one final `remix_collab_finalize_turn` is required before the final user-facing response.
27
+ 12. The final recording call must use the exact user prompt and your final assistant response.
28
+ 13. Do not finish the turn before recording. Do not make additional repo mutations after the final turn-recording call unless you intend to record again.
29
29
  14. Do not duplicate core business logic in reasoning. Use the MCP tools to inspect and execute the workflow.
30
30
 
31
31
  When appropriate:
@@ -33,9 +33,10 @@ When appropriate:
33
33
  - use `remix_collab_init` to bind the current repo
34
34
  - use `remix_collab_remix` to start from an existing app lineage
35
35
  - use `remix_collab_checkout` to continue work on an existing app id without creating a fork
36
+ - when helping the user choose an app, prefer scoped or membership-oriented discovery first: use `organizationId` / `projectId` when known, or use `ownership` with `accessScope="explicit_member"` when the user means “apps I can work on”
37
+ - reserve `accessScope="all_readable"` for explicit public or broad readable discovery, not as the default work-oriented app picker
36
38
  - use memory summary/search/timeline tools before repo inspection when historical context or reasoning is needed
37
39
  - use the explicit change-step diff tool only after you already know which `changeStepId` matters
38
- - use `remix_collab_add` only when a manual changed-turn recording step is explicitly needed
39
- - use `remix_collab_record_turn` only when a manual no-diff turn recording step is explicitly needed
40
+ - use `remix_collab_finalize_turn` as the final turn recorder
40
41
  - use `remix_collab_review_queue` for reviewable merge requests, `remix_collab_my_merge_requests` for authored requests, and `remix_collab_list_app_merge_requests` for app-scoped MR flows with required `queue` set to `app_reviewable`, `app_outgoing`, or `app_related_visible`
41
42
  - use merge request tools for review and approval flows
@@ -351,7 +351,7 @@ async function markPendingTurnConsultedMemory(sessionId) {
351
351
  // package.json
352
352
  var package_default = {
353
353
  name: "@remixhq/claude-plugin",
354
- version: "0.1.12",
354
+ version: "0.1.15",
355
355
  description: "Claude Code plugin for Remix collaboration workflows",
356
356
  homepage: "https://github.com/RemixDotOne/remix-claude-plugin",
357
357
  license: "MIT",
@@ -382,8 +382,8 @@ var package_default = {
382
382
  prepack: "npm run build"
383
383
  },
384
384
  dependencies: {
385
- "@remixhq/core": "^0.1.8",
386
- "@remixhq/mcp": "^0.1.8"
385
+ "@remixhq/core": "^0.1.10",
386
+ "@remixhq/mcp": "^0.1.10"
387
387
  },
388
388
  devDependencies: {
389
389
  "@types/node": "^25.4.0",
@@ -472,7 +472,7 @@ async function appendHookDiagnosticsEvent(params) {
472
472
  var import_promises4 = __toESM(require("fs/promises"), 1);
473
473
  var import_node_path3 = __toESM(require("path"), 1);
474
474
 
475
- // node_modules/@remixhq/core/dist/chunk-FAZUMWBS.js
475
+ // node_modules/@remixhq/core/dist/chunk-GEHSFPCD.js
476
476
  var import_promises3 = __toESM(require("fs/promises"), 1);
477
477
  var import_path = __toESM(require("path"), 1);
478
478
  function getCollabBindingPath(repoRoot) {
@@ -524,6 +524,11 @@ function extractToolInput(payload) {
524
524
  function extractToolResponse(payload) {
525
525
  return getNestedRecord(payload.tool_response) ?? getNestedRecord(payload.toolResponse);
526
526
  }
527
+ function extractToolStructuredData(payload) {
528
+ const toolResponse = extractToolResponse(payload);
529
+ const structuredContent = getNestedRecord(toolResponse?.structuredContent) ?? getNestedRecord(payload.structuredContent);
530
+ return getNestedRecord(toolResponse?.data) ?? getNestedRecord(structuredContent?.data) ?? structuredContent;
531
+ }
527
532
  function extractToolName(payload) {
528
533
  return extractString(payload, ["tool_name", "toolName"]);
529
534
  }
@@ -537,6 +542,26 @@ function normalizeHookToolName(toolName) {
537
542
  }
538
543
  return trimmed;
539
544
  }
545
+ function extractAssistantResponse(payload) {
546
+ const candidateKeys = [
547
+ "last_assistant_message",
548
+ "lastAssistantMessage",
549
+ "assistant_response",
550
+ "assistantResponse",
551
+ "assistant_message",
552
+ "assistantMessage",
553
+ "response",
554
+ "message"
555
+ ];
556
+ return extractString(payload, candidateKeys) ?? extractString(extractToolResponse(payload) ?? {}, candidateKeys) ?? extractString(extractToolStructuredData(payload) ?? {}, candidateKeys) ?? extractString(extractToolInput(payload), candidateKeys);
557
+ }
558
+ function extractFinalizeTurnMode(payload) {
559
+ const mode = extractString(extractToolStructuredData(payload) ?? {}, ["mode"]) ?? extractString(extractToolResponse(payload) ?? {}, ["mode"]) ?? extractString(payload, ["mode"]);
560
+ if (mode === "changed_turn" || mode === "no_diff_turn") {
561
+ return mode;
562
+ }
563
+ return null;
564
+ }
540
565
  function extractString(input, keys) {
541
566
  for (const key of keys) {
542
567
  const value = input[key];
@@ -672,15 +697,28 @@ function isShellToolName(toolName) {
672
697
  function isRemoteChangeRecordingToolName(toolName) {
673
698
  return /remix_collab_(add|add_change_step)$/i.test(toolName);
674
699
  }
675
- function getManualRecordingScope(toolName) {
676
- if (/remix_collab_add_change_step$/i.test(toolName)) {
677
- return "change_step";
700
+ function hasManualFullTurnPayload(payload) {
701
+ const toolInput = extractToolInput(payload);
702
+ return Boolean(extractString(toolInput, ["prompt"]) && extractAssistantResponse(payload));
703
+ }
704
+ function getManualRecordingScope(payload, toolName) {
705
+ if (/remix_collab_finalize_turn$/i.test(toolName)) {
706
+ return "full_turn";
707
+ }
708
+ if (/remix_collab_(add|add_change_step)$/i.test(toolName)) {
709
+ return hasManualFullTurnPayload(payload) ? "full_turn" : "change_step";
678
710
  }
679
- if (/remix_collab_(add|record_turn|record_no_diff_turn)$/i.test(toolName)) {
711
+ if (/remix_collab_(record_turn|record_no_diff_turn)$/i.test(toolName)) {
680
712
  return "full_turn";
681
713
  }
682
714
  return null;
683
715
  }
716
+ function didFinalizeTurnRecordRemoteChange(payload, toolName) {
717
+ if (!/remix_collab_finalize_turn$/i.test(toolName)) {
718
+ return false;
719
+ }
720
+ return extractFinalizeTurnMode(payload) === "changed_turn";
721
+ }
684
722
  function isLikelyMutatingShellCommand(command) {
685
723
  const normalized = command.trim().toLowerCase();
686
724
  if (!normalized) return false;
@@ -746,7 +784,7 @@ async function runHookPostCollab(payload) {
746
784
  return;
747
785
  }
748
786
  const toolSucceeded = didToolSucceed(payload);
749
- const remoteChangeRecordedButSyncFailed = isRemoteChangeRecordingToolName(toolName) && isRemoteChangeRecordedButLocalSyncFailed(payload);
787
+ const remoteChangeRecordedButSyncFailed = (isRemoteChangeRecordingToolName(toolName) || /remix_collab_finalize_turn$/i.test(toolName)) && isRemoteChangeRecordedButLocalSyncFailed(payload);
750
788
  await appendHookDiagnosticsEvent({
751
789
  hook: "PostToolUse",
752
790
  sessionId,
@@ -784,7 +822,7 @@ async function runHookPostCollab(payload) {
784
822
  toolName
785
823
  });
786
824
  }
787
- const manualRecordingScope = getManualRecordingScope(toolName);
825
+ const manualRecordingScope = getManualRecordingScope(payload, toolName);
788
826
  if (isRepoMutationToolName(toolName) || manualRecordingScope) {
789
827
  const targetRepo = await resolveBoundRepoFromToolCwd(payload);
790
828
  if (targetRepo) {
@@ -803,7 +841,7 @@ async function runHookPostCollab(payload) {
803
841
  await markTouchedRepoManuallyRecorded(sessionId, targetRepo.repoRoot, {
804
842
  toolName,
805
843
  scope: manualRecordingScope,
806
- remoteChangeRecorded: toolSucceeded ? isRemoteChangeRecordingToolName(toolName) : remoteChangeRecordedButSyncFailed
844
+ remoteChangeRecorded: toolSucceeded ? isRemoteChangeRecordingToolName(toolName) || didFinalizeTurnRecordRemoteChange(payload, toolName) : remoteChangeRecordedButSyncFailed
807
845
  });
808
846
  }
809
847
  await appendHookDiagnosticsEvent({
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/hook-post-collab.ts","../src/hook-diagnostics.ts","../src/hook-state.ts","../package.json","../src/metadata.ts","../src/hook-utils.ts","../node_modules/@remixhq/core/dist/chunk-FAZUMWBS.js"],"sourcesContent":["import { appendHookDiagnosticsEvent } from \"./hook-diagnostics.js\";\nimport { markPendingTurnConsultedMemory, markTouchedRepoManuallyRecorded, markTouchedRepoObservedWrite, upsertTouchedRepo } from \"./hook-state.js\";\nimport {\n didToolSucceed,\n extractBashCommand,\n extractToolName,\n extractToolPathTargets,\n isRemoteChangeRecordedButLocalSyncFailed,\n normalizeHookToolName,\n readJsonStdin,\n resolveBoundRepoFromToolCwd,\n resolveTouchedBoundReposFromPaths,\n} from \"./hook-utils.js\";\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\nfunction isStructuredLocalReadToolName(toolName: string): boolean {\n return /^Read$/i.test(toolName);\n}\n\nfunction isShellToolName(toolName: string): boolean {\n return /^Bash$/i.test(toolName);\n}\n\nfunction isRemoteChangeRecordingToolName(toolName: string): boolean {\n return /remix_collab_(add|add_change_step)$/i.test(toolName);\n}\n\nfunction getManualRecordingScope(toolName: string): \"change_step\" | \"full_turn\" | null {\n if (/remix_collab_add_change_step$/i.test(toolName)) {\n return \"change_step\";\n }\n if (/remix_collab_(add|record_turn|record_no_diff_turn)$/i.test(toolName)) {\n return \"full_turn\";\n }\n return null;\n}\n\nfunction isLikelyMutatingShellCommand(command: string): boolean {\n const normalized = command.trim().toLowerCase();\n if (!normalized) return false;\n\n const readOnlyPatterns = [\n /^pwd(?:\\s|$)/,\n /^ls(?:\\s|$)/,\n /^dir(?:\\s|$)/,\n /^tree(?:\\s|$)/,\n /^rg(?:\\s|$)/,\n /^git status(?:\\s|$)/,\n /^git diff(?:\\s|$)/,\n /^git log(?:\\s|$)/,\n /^git show(?:\\s|$)/,\n /^git branch(?:\\s|$)/,\n /^git rev-parse(?:\\s|$)/,\n /^git remote(?:\\s|$)/,\n /^git ls-files(?:\\s|$)/,\n /^cat(?:\\s|$)/,\n /^sed -n(?:\\s|$)/,\n /^head(?:\\s|$)/,\n /^tail(?:\\s|$)/,\n ];\n\n const mutatingPatterns = [\n /\\|\\s*tee(?:\\s|$)/,\n /(^|[^\\w-])>>?(?=[^&]|$)/,\n /\\bgit\\b(?:\\s+-[^\\s]+)*\\s+(add|apply|checkout|cherry-pick|clean|mv|restore|revert|rm|stash|switch)\\b/,\n /\\b(mv|cp|rm|mkdir|rmdir|touch|chmod|chown|ln|install)\\b/,\n /\\b(sed|perl|ruby)\\b.*\\s-i(?:\\s|$)/,\n /\\b(npm|pnpm|yarn|bun)\\b\\s+(install|add|remove|update|upgrade)\\b/,\n ];\n\n const segments = normalized\n .split(/&&|\\|\\||;|\\n/)\n .map((segment) => segment.trim())\n .filter(Boolean);\n\n if (mutatingPatterns.some((pattern) => pattern.test(normalized))) {\n return true;\n }\n\n if (segments.length > 0 && segments.every((segment) => readOnlyPatterns.some((pattern) => pattern.test(segment)))) {\n return false;\n }\n\n return false;\n}\n\nexport async function runHookPostCollab(payload: Record<string, unknown>): Promise<void> {\n const sessionId = typeof payload.session_id === \"string\" && payload.session_id.trim() ? payload.session_id.trim() : null;\n const toolName = normalizeHookToolName(extractToolName(payload));\n await appendHookDiagnosticsEvent({\n hook: \"PostToolUse\",\n sessionId,\n stage: \"payload_received\",\n result: \"start\",\n toolName,\n fields: {\n hasSessionId: Boolean(sessionId),\n hasToolName: Boolean(toolName),\n },\n });\n\n if (!sessionId || !toolName) {\n await appendHookDiagnosticsEvent({\n hook: \"PostToolUse\",\n sessionId,\n stage: \"payload_validation\",\n result: \"skip\",\n reason: !sessionId ? \"missing_session_id\" : \"missing_tool_name\",\n toolName,\n });\n return;\n }\n\n const toolSucceeded = didToolSucceed(payload);\n const remoteChangeRecordedButSyncFailed =\n isRemoteChangeRecordingToolName(toolName) && isRemoteChangeRecordedButLocalSyncFailed(payload);\n await appendHookDiagnosticsEvent({\n hook: \"PostToolUse\",\n sessionId,\n stage: \"tool_classified\",\n result: \"info\",\n toolName,\n fields: {\n toolSucceeded,\n remoteChangeRecordedButSyncFailed,\n isMemoryTool: isMemoryToolName(toolName),\n isRepoMutationTool: isRepoMutationToolName(toolName),\n isShellTool: isShellToolName(toolName),\n isStructuredLocalWriteTool: isStructuredLocalWriteToolName(toolName),\n isStructuredLocalReadTool: isStructuredLocalReadToolName(toolName),\n },\n });\n if (!toolSucceeded && !remoteChangeRecordedButSyncFailed) {\n await appendHookDiagnosticsEvent({\n hook: \"PostToolUse\",\n sessionId,\n stage: \"tool_result_gate\",\n result: \"skip\",\n reason: \"tool_failed_or_skipped\",\n toolName,\n });\n return;\n }\n\n if (toolSucceeded && isMemoryToolName(toolName)) {\n await markPendingTurnConsultedMemory(sessionId);\n await appendHookDiagnosticsEvent({\n hook: \"PostToolUse\",\n sessionId,\n stage: \"memory_marked_consulted\",\n result: \"success\",\n toolName,\n });\n }\n\n const manualRecordingScope = getManualRecordingScope(toolName);\n if (isRepoMutationToolName(toolName) || manualRecordingScope) {\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 (toolSucceeded && isRepoMutationToolName(toolName)) {\n await markTouchedRepoObservedWrite(sessionId, targetRepo.repoRoot, { toolName });\n }\n if (manualRecordingScope) {\n await markTouchedRepoManuallyRecorded(sessionId, targetRepo.repoRoot, {\n toolName,\n scope: manualRecordingScope,\n remoteChangeRecorded: toolSucceeded\n ? isRemoteChangeRecordingToolName(toolName)\n : remoteChangeRecordedButSyncFailed,\n });\n }\n await appendHookDiagnosticsEvent({\n hook: \"PostToolUse\",\n sessionId,\n stage: \"repo_marked_from_tool_cwd\",\n result: \"success\",\n toolName,\n repoRoot: targetRepo.repoRoot,\n fields: {\n hasObservedWrite: isRepoMutationToolName(toolName),\n manualRecordingScope,\n remoteChangeRecordedButSyncFailed,\n },\n });\n } else {\n await appendHookDiagnosticsEvent({\n hook: \"PostToolUse\",\n sessionId,\n stage: \"repo_marked_from_tool_cwd\",\n result: \"skip\",\n reason: \"repo_not_bound_from_tool_cwd\",\n toolName,\n });\n }\n }\n\n if (toolSucceeded && isShellToolName(toolName)) {\n const targetRepo = await resolveBoundRepoFromToolCwd(payload);\n if (targetRepo) {\n const bashCommand = extractBashCommand(payload);\n const hasObservedWrite = bashCommand ? isLikelyMutatingShellCommand(bashCommand) : false;\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,\n });\n if (hasObservedWrite) {\n await markTouchedRepoObservedWrite(sessionId, targetRepo.repoRoot, { toolName });\n }\n await appendHookDiagnosticsEvent({\n hook: \"PostToolUse\",\n sessionId,\n stage: \"shell_repo_marked\",\n result: \"success\",\n toolName,\n repoRoot: targetRepo.repoRoot,\n fields: {\n hasObservedWrite,\n bashCommandLength: bashCommand?.length ?? 0,\n },\n });\n } else {\n await appendHookDiagnosticsEvent({\n hook: \"PostToolUse\",\n sessionId,\n stage: \"shell_repo_marked\",\n result: \"skip\",\n reason: \"repo_not_bound_from_shell_cwd\",\n toolName,\n });\n }\n }\n\n if (toolSucceeded && (isStructuredLocalWriteToolName(toolName) || isStructuredLocalReadToolName(toolName))) {\n const touchedPaths = extractToolPathTargets(payload, toolName);\n const touchedRepos = await resolveTouchedBoundReposFromPaths(touchedPaths);\n await appendHookDiagnosticsEvent({\n hook: \"PostToolUse\",\n sessionId,\n stage: \"path_targets_resolved\",\n result: touchedRepos.length > 0 ? \"info\" : \"skip\",\n reason: touchedRepos.length > 0 ? null : \"no_bound_repos_from_paths\",\n toolName,\n fields: {\n touchedPathCount: touchedPaths.length,\n boundRepoCount: touchedRepos.length,\n },\n });\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: isStructuredLocalWriteToolName(toolName),\n });\n if (isStructuredLocalWriteToolName(toolName)) {\n await markTouchedRepoObservedWrite(sessionId, repo.repoRoot, { toolName });\n }\n await appendHookDiagnosticsEvent({\n hook: \"PostToolUse\",\n sessionId,\n stage: \"path_repo_marked\",\n result: \"success\",\n toolName,\n repoRoot: repo.repoRoot,\n fields: {\n hasObservedWrite: isStructuredLocalWriteToolName(toolName),\n },\n });\n }\n }\n\n await appendHookDiagnosticsEvent({\n hook: \"PostToolUse\",\n sessionId,\n stage: \"completed\",\n result: \"success\",\n toolName,\n });\n}\n\nasync function main(): Promise<void> {\n const payload = await readJsonStdin();\n await runHookPostCollab(payload);\n}\n\nmain().catch((error) => {\n const message = error instanceof Error ? error.message : String(error);\n void appendHookDiagnosticsEvent({\n hook: \"PostToolUse\",\n stage: \"unhandled_error\",\n result: \"error\",\n reason: \"exception\",\n message,\n });\n process.stderr.write(`${message}\\n`);\n process.exitCode = 0;\n});\n","import { createHash } from \"node:crypto\";\nimport fs from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\nimport { getPendingTurnStateRootPath, listPendingTurnStateSummaries, type PendingTurnStateSummary } from \"./hook-state.js\";\nimport { pluginMetadata } from \"./metadata.js\";\n\ntype HookDiagnosticsResult = \"start\" | \"info\" | \"skip\" | \"success\" | \"error\";\ntype HookDiagnosticsFieldValue = string | number | boolean | null;\n\nexport type HookDiagnosticsEvent = {\n ts: string;\n hook: string;\n pluginVersion: string;\n pid: number;\n sessionId: string | null;\n turnId: string | null;\n stage: string;\n result: HookDiagnosticsResult;\n reason: string | null;\n toolName: string | null;\n repoRoot: string | null;\n message: string | null;\n fields: Record<string, HookDiagnosticsFieldValue>;\n};\n\nexport type HookDiagnosticsReport = {\n logPath: string;\n stateRoot: string;\n recentEvents: HookDiagnosticsEvent[];\n pendingStates: PendingTurnStateSummary[];\n};\n\nconst DEFAULT_EVENT_LIMIT = 50;\nconst MAX_EVENT_LIMIT = 200;\nconst MAX_LOG_BYTES = 512 * 1024;\n\nfunction resolveClaudeRoot(): string {\n const configured = process.env.CLAUDE_CONFIG_DIR?.trim();\n return configured || path.join(os.homedir(), \".claude\");\n}\n\nfunction resolvePluginDataDirName(): string {\n return `${pluginMetadata.pluginId}-${pluginMetadata.pluginId}`;\n}\n\nexport function getHookDiagnosticsDirPath(): string {\n const configured = process.env.REMIX_CLAUDE_PLUGIN_HOOK_DIAGNOSTICS_DIR?.trim();\n return configured || path.join(resolveClaudeRoot(), \"plugins\", \"data\", resolvePluginDataDirName());\n}\n\nexport function getHookDiagnosticsLogPath(): string {\n return path.join(getHookDiagnosticsDirPath(), \"hooks.ndjson\");\n}\n\nfunction clampEventLimit(limit?: number): number {\n if (typeof limit !== \"number\" || !Number.isFinite(limit)) return DEFAULT_EVENT_LIMIT;\n return Math.max(1, Math.min(MAX_EVENT_LIMIT, Math.trunc(limit)));\n}\n\nfunction toFieldValue(value: unknown): HookDiagnosticsFieldValue | undefined {\n if (value === null) return null;\n if (typeof value === \"string\") return value;\n if (typeof value === \"number\" && Number.isFinite(value)) return value;\n if (typeof value === \"boolean\") return value;\n return undefined;\n}\n\nfunction normalizeFields(fields?: Record<string, unknown>): Record<string, HookDiagnosticsFieldValue> {\n if (!fields) return {};\n const normalizedEntries = Object.entries(fields)\n .map(([key, value]) => {\n const normalized = toFieldValue(value);\n return normalized === undefined ? null : ([key, normalized] as const);\n })\n .filter((entry): entry is readonly [string, HookDiagnosticsFieldValue] => entry !== null);\n return Object.fromEntries(normalizedEntries);\n}\n\nasync function rotateLogIfNeeded(logPath: string): Promise<void> {\n const stat = await fs.stat(logPath).catch(() => null);\n if (!stat || stat.size < MAX_LOG_BYTES) {\n return;\n }\n\n const rotatedPath = `${logPath}.1`;\n await fs.rm(rotatedPath, { force: true }).catch(() => undefined);\n await fs.rename(logPath, rotatedPath).catch(() => undefined);\n}\n\nexport function summarizeText(value: string | null | undefined): {\n present: boolean;\n length: number;\n sha256Prefix: string | null;\n} {\n if (typeof value !== \"string\" || !value.trim()) {\n return {\n present: false,\n length: 0,\n sha256Prefix: null,\n };\n }\n\n const trimmed = value.trim();\n return {\n present: true,\n length: trimmed.length,\n sha256Prefix: createHash(\"sha256\").update(trimmed).digest(\"hex\").slice(0, 12),\n };\n}\n\nexport async function appendHookDiagnosticsEvent(params: {\n hook: string;\n sessionId?: string | null;\n turnId?: string | null;\n stage: string;\n result: HookDiagnosticsResult;\n reason?: string | null;\n toolName?: string | null;\n repoRoot?: string | null;\n message?: string | null;\n fields?: Record<string, unknown>;\n}): Promise<void> {\n try {\n const logPath = getHookDiagnosticsLogPath();\n await fs.mkdir(path.dirname(logPath), { recursive: true });\n await rotateLogIfNeeded(logPath);\n const event: HookDiagnosticsEvent = {\n ts: new Date().toISOString(),\n hook: params.hook,\n pluginVersion: pluginMetadata.version,\n pid: process.pid,\n sessionId: params.sessionId?.trim() || null,\n turnId: params.turnId?.trim() || null,\n stage: params.stage.trim(),\n result: params.result,\n reason: params.reason?.trim() || null,\n toolName: params.toolName?.trim() || null,\n repoRoot: params.repoRoot?.trim() || null,\n message: params.message?.trim() || null,\n fields: normalizeFields(params.fields),\n };\n await fs.appendFile(logPath, `${JSON.stringify(event)}\\n`, \"utf8\");\n } catch {\n // Diagnostics are best-effort and must never break hook execution.\n }\n}\n\nasync function readEventsFromFile(filePath: string): Promise<HookDiagnosticsEvent[]> {\n const raw = await fs.readFile(filePath, \"utf8\").catch(() => null);\n if (!raw) return [];\n return raw\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter(Boolean)\n .flatMap((line) => {\n try {\n const parsed = JSON.parse(line) as HookDiagnosticsEvent;\n return parsed && typeof parsed === \"object\" ? [parsed] : [];\n } catch {\n return [];\n }\n });\n}\n\nexport async function readRecentHookDiagnosticsEvents(limit?: number): Promise<HookDiagnosticsEvent[]> {\n const eventLimit = clampEventLimit(limit);\n const logPath = getHookDiagnosticsLogPath();\n const [rotated, current] = await Promise.all([readEventsFromFile(`${logPath}.1`), readEventsFromFile(logPath)]);\n return [...rotated, ...current].slice(-eventLimit);\n}\n\nexport async function readHookDiagnosticsReport(limit?: number): Promise<HookDiagnosticsReport> {\n const [recentEvents, pendingStates] = await Promise.all([\n readRecentHookDiagnosticsEvents(limit),\n listPendingTurnStateSummaries(),\n ]);\n\n return {\n logPath: getHookDiagnosticsLogPath(),\n stateRoot: getPendingTurnStateRootPath(),\n recentEvents,\n pendingStates,\n };\n}\n","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\";\nexport type ManualRecordingScope = \"change_step\" | \"full_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 manualRecordingScope: ManualRecordingScope | null;\n manualRemoteChangeRecordedAt: 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\nexport type PendingTouchedRepoSummary = {\n repoRoot: string;\n projectId: string | null;\n currentAppId: string | null;\n upstreamAppId: string | null;\n lastTouchedAt: string;\n lastObservedWriteAt: string | null;\n touchedBy: string[];\n hasObservedWrite: boolean;\n manuallyRecorded: boolean;\n manualRecordingScope: ManualRecordingScope | null;\n stopAttempted: boolean;\n stopRecorded: boolean;\n stopRecordedMode: RepoRecordMode | null;\n recordingFailureMessage: string | null;\n recordingFailureHint: string | null;\n recordingFailedAt: string | null;\n};\n\nexport type PendingTurnStateSummary = {\n sessionId: string;\n turnId: string;\n initialCwd: string | null;\n submittedAt: string;\n consultedMemory: boolean;\n promptLength: number;\n touchedRepoCount: number;\n turnFailureMessage: string | null;\n turnFailureHint: string | null;\n turnFailedAt: string | null;\n touchedRepos: PendingTouchedRepoSummary[];\n};\n\nfunction stateRoot(): string {\n const configured = process.env.REMIX_CLAUDE_PLUGIN_HOOK_STATE_ROOT?.trim();\n return configured || 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 await fs.mkdir(stateRoot(), { recursive: true });\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 manualRecordingScope:\n parsed.manualRecordingScope === \"change_step\" || parsed.manualRecordingScope === \"full_turn\"\n ? parsed.manualRecordingScope\n : null,\n manualRemoteChangeRecordedAt: normalizeString(parsed.manualRemoteChangeRecordedAt),\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 manualRecordingScope: null,\n manualRemoteChangeRecordedAt: 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\nfunction summarizeTouchedRepo(repo: TouchedRepoState): PendingTouchedRepoSummary {\n return {\n repoRoot: repo.repoRoot,\n projectId: repo.projectId,\n currentAppId: repo.currentAppId,\n upstreamAppId: repo.upstreamAppId,\n lastTouchedAt: repo.lastTouchedAt,\n lastObservedWriteAt: repo.lastObservedWriteAt,\n touchedBy: [...repo.touchedBy],\n hasObservedWrite: repo.hasObservedWrite,\n manuallyRecorded: repo.manuallyRecorded,\n manualRecordingScope: repo.manualRecordingScope,\n stopAttempted: repo.stopAttempted,\n stopRecorded: repo.stopRecorded,\n stopRecordedMode: repo.stopRecordedMode,\n recordingFailureMessage: repo.recordingFailureMessage,\n recordingFailureHint: repo.recordingFailureHint,\n recordingFailedAt: repo.recordingFailedAt,\n };\n}\n\nfunction summarizePendingTurnState(state: PendingTurnState): PendingTurnStateSummary {\n const touchedRepos = Object.values(state.touchedRepos)\n .sort((a, b) => a.repoRoot.localeCompare(b.repoRoot))\n .map((repo) => summarizeTouchedRepo(repo));\n return {\n sessionId: state.sessionId,\n turnId: state.turnId,\n initialCwd: state.initialCwd,\n submittedAt: state.submittedAt,\n consultedMemory: state.consultedMemory,\n promptLength: state.prompt.length,\n touchedRepoCount: touchedRepos.length,\n turnFailureMessage: state.turnFailureMessage,\n turnFailureHint: state.turnFailureHint,\n turnFailedAt: state.turnFailedAt,\n touchedRepos,\n };\n}\n\nexport function getPendingTurnStateRootPath(): string {\n return stateRoot();\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; scope?: ManualRecordingScope | null; remoteChangeRecorded?: boolean },\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.manualRecordingScope = params?.scope ?? current.manualRecordingScope;\n if (params?.remoteChangeRecorded) {\n current.manualRemoteChangeRecordedAt = current.manuallyRecordedAt;\n }\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 listPendingTurnStateSummaries(): Promise<PendingTurnStateSummary[]> {\n const root = stateRoot();\n const entries = await fs.readdir(root, { withFileTypes: true }).catch(() => []);\n const sessionIds = entries\n .filter((entry) => entry.isFile() && entry.name.endsWith(\".json\"))\n .map((entry) => entry.name.replace(/\\.json$/, \"\"))\n .sort((a, b) => a.localeCompare(b));\n const states = await Promise.all(sessionIds.map((sessionId) => loadPendingTurnState(sessionId)));\n return states\n .filter((state): state is PendingTurnState => state !== null)\n .sort((a, b) => b.submittedAt.localeCompare(a.submittedAt))\n .map((state) => summarizePendingTurnState(state));\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","{\n \"name\": \"@remixhq/claude-plugin\",\n \"version\": \"0.1.12\",\n \"description\": \"Claude Code plugin for Remix collaboration workflows\",\n \"homepage\": \"https://github.com/RemixDotOne/remix-claude-plugin\",\n \"license\": \"MIT\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/RemixDotOne/remix-claude-plugin.git\"\n },\n \"type\": \"module\",\n \"engines\": {\n \"node\": \">=20\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"files\": [\n \"dist\",\n \".claude-plugin/plugin.json\",\n \".mcp.json\",\n \"skills\",\n \"hooks\",\n \"agents\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"postbuild\": \"node -e \\\"const fs=require('node:fs'); for (const p of ['dist/mcp-server.cjs','dist/hook-pre-git.cjs','dist/hook-user-prompt.cjs','dist/hook-post-collab.cjs','dist/hook-stop-collab.cjs']) fs.chmodSync(p, 0o755);\\\"\",\n \"dev\": \"tsx src/mcp-server.ts\",\n \"typecheck\": \"tsc -p tsconfig.json --noEmit\",\n \"prepack\": \"npm run build\"\n },\n \"dependencies\": {\n \"@remixhq/core\": \"^0.1.8\",\n \"@remixhq/mcp\": \"^0.1.8\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^25.4.0\",\n \"tsup\": \"^8.5.1\",\n \"tsx\": \"^4.21.0\",\n \"typescript\": \"^5.9.3\"\n }\n}\n","import pkg from \"../package.json\";\n\nexport const pluginMetadata = {\n name: pkg.name,\n version: pkg.version,\n description: pkg.description,\n pluginId: \"remix\",\n agentName: \"remix-collab\",\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 normalizeHookToolName(toolName: string | null): string | null {\n if (!toolName) return null;\n const trimmed = toolName.trim();\n if (!trimmed) return null;\n\n const remixToolIndex = trimmed.toLowerCase().indexOf(\"remix_collab_\");\n if (remixToolIndex >= 0) {\n return trimmed.slice(remixToolIndex);\n }\n\n return trimmed;\n}\n\nexport function extractAssistantResponse(payload: Record<string, unknown>): string | null {\n const candidateKeys = [\n \"last_assistant_message\",\n \"lastAssistantMessage\",\n \"assistant_response\",\n \"assistantResponse\",\n \"assistant_message\",\n \"assistantMessage\",\n \"response\",\n \"message\",\n ];\n\n return (\n extractString(payload, candidateKeys) ??\n extractString(extractToolResponse(payload) ?? {}, candidateKeys) ??\n extractString(extractToolInput(payload), candidateKeys)\n );\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 extractBashCommand(payload: Record<string, unknown>): string | null {\n const toolInput = extractToolInput(payload);\n return extractString(toolInput, [\"command\", \"cmd\", \"bash_command\", \"bashCommand\"]);\n}\n\nexport function extractToolErrorMessage(payload: Record<string, unknown>): string | null {\n const toolResponse = extractToolResponse(payload);\n const toolError = getNestedRecord(toolResponse?.error) ?? getNestedRecord(payload.error);\n return (\n extractString(toolError ?? {}, [\"message\"]) ??\n extractString(toolResponse ?? {}, [\"errorMessage\", \"message\"]) ??\n extractString(payload, [\"errorMessage\"])\n );\n}\n\nfunction extractToolStatus(payload: Record<string, unknown>): string | null {\n const toolResponse = extractToolResponse(payload);\n return (\n extractString(toolResponse ?? {}, [\"status\", \"state\"]) ??\n extractString(payload, [\"status\", \"state\"])\n );\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\n if (extractToolErrorMessage(payload)) {\n return false;\n }\n\n const status = extractToolStatus(payload)?.toLowerCase();\n if (status === \"error\" || status === \"failed\" || status === \"failure\") {\n return false;\n }\n\n const hookEventName = extractString(payload, [\"hook_event_name\", \"hookEventName\"]);\n return hookEventName === \"PostToolUse\";\n}\n\nexport function isRemoteChangeRecordedButLocalSyncFailed(payload: Record<string, unknown>): boolean {\n return extractToolErrorMessage(payload) === \"Change step succeeded remotely, but automatic local sync failed.\";\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"],"mappings":"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","names":["import_node_crypto","import_promises","import_node_os","import_node_path","path","os","fs","fs","path","os","fs","fs","path","import_promises","import_node_path","import_promises","path2","fs2","path","fs"]}
1
+ {"version":3,"sources":["../src/hook-post-collab.ts","../src/hook-diagnostics.ts","../src/hook-state.ts","../package.json","../src/metadata.ts","../src/hook-utils.ts","../node_modules/@remixhq/core/dist/chunk-GEHSFPCD.js"],"sourcesContent":["import { appendHookDiagnosticsEvent } from \"./hook-diagnostics.js\";\nimport { markPendingTurnConsultedMemory, markTouchedRepoManuallyRecorded, markTouchedRepoObservedWrite, upsertTouchedRepo } from \"./hook-state.js\";\nimport {\n didToolSucceed,\n extractAssistantResponse,\n extractBashCommand,\n extractFinalizeTurnMode,\n extractString,\n extractToolName,\n extractToolInput,\n extractToolPathTargets,\n isRemoteChangeRecordedButLocalSyncFailed,\n normalizeHookToolName,\n readJsonStdin,\n resolveBoundRepoFromToolCwd,\n resolveTouchedBoundReposFromPaths,\n} from \"./hook-utils.js\";\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\nfunction isStructuredLocalReadToolName(toolName: string): boolean {\n return /^Read$/i.test(toolName);\n}\n\nfunction isShellToolName(toolName: string): boolean {\n return /^Bash$/i.test(toolName);\n}\n\nfunction isRemoteChangeRecordingToolName(toolName: string): boolean {\n return /remix_collab_(add|add_change_step)$/i.test(toolName);\n}\n\nfunction hasManualFullTurnPayload(payload: Record<string, unknown>): boolean {\n const toolInput = extractToolInput(payload);\n return Boolean(extractString(toolInput, [\"prompt\"]) && extractAssistantResponse(payload));\n}\n\nfunction getManualRecordingScope(payload: Record<string, unknown>, toolName: string): \"change_step\" | \"full_turn\" | null {\n if (/remix_collab_finalize_turn$/i.test(toolName)) {\n return \"full_turn\";\n }\n if (/remix_collab_(add|add_change_step)$/i.test(toolName)) {\n return hasManualFullTurnPayload(payload) ? \"full_turn\" : \"change_step\";\n }\n if (/remix_collab_(record_turn|record_no_diff_turn)$/i.test(toolName)) {\n return \"full_turn\";\n }\n return null;\n}\n\nfunction didFinalizeTurnRecordRemoteChange(payload: Record<string, unknown>, toolName: string): boolean {\n if (!/remix_collab_finalize_turn$/i.test(toolName)) {\n return false;\n }\n return extractFinalizeTurnMode(payload) === \"changed_turn\";\n}\n\nfunction isLikelyMutatingShellCommand(command: string): boolean {\n const normalized = command.trim().toLowerCase();\n if (!normalized) return false;\n\n const readOnlyPatterns = [\n /^pwd(?:\\s|$)/,\n /^ls(?:\\s|$)/,\n /^dir(?:\\s|$)/,\n /^tree(?:\\s|$)/,\n /^rg(?:\\s|$)/,\n /^git status(?:\\s|$)/,\n /^git diff(?:\\s|$)/,\n /^git log(?:\\s|$)/,\n /^git show(?:\\s|$)/,\n /^git branch(?:\\s|$)/,\n /^git rev-parse(?:\\s|$)/,\n /^git remote(?:\\s|$)/,\n /^git ls-files(?:\\s|$)/,\n /^cat(?:\\s|$)/,\n /^sed -n(?:\\s|$)/,\n /^head(?:\\s|$)/,\n /^tail(?:\\s|$)/,\n ];\n\n const mutatingPatterns = [\n /\\|\\s*tee(?:\\s|$)/,\n /(^|[^\\w-])>>?(?=[^&]|$)/,\n /\\bgit\\b(?:\\s+-[^\\s]+)*\\s+(add|apply|checkout|cherry-pick|clean|mv|restore|revert|rm|stash|switch)\\b/,\n /\\b(mv|cp|rm|mkdir|rmdir|touch|chmod|chown|ln|install)\\b/,\n /\\b(sed|perl|ruby)\\b.*\\s-i(?:\\s|$)/,\n /\\b(npm|pnpm|yarn|bun)\\b\\s+(install|add|remove|update|upgrade)\\b/,\n ];\n\n const segments = normalized\n .split(/&&|\\|\\||;|\\n/)\n .map((segment) => segment.trim())\n .filter(Boolean);\n\n if (mutatingPatterns.some((pattern) => pattern.test(normalized))) {\n return true;\n }\n\n if (segments.length > 0 && segments.every((segment) => readOnlyPatterns.some((pattern) => pattern.test(segment)))) {\n return false;\n }\n\n return false;\n}\n\nexport async function runHookPostCollab(payload: Record<string, unknown>): Promise<void> {\n const sessionId = typeof payload.session_id === \"string\" && payload.session_id.trim() ? payload.session_id.trim() : null;\n const toolName = normalizeHookToolName(extractToolName(payload));\n await appendHookDiagnosticsEvent({\n hook: \"PostToolUse\",\n sessionId,\n stage: \"payload_received\",\n result: \"start\",\n toolName,\n fields: {\n hasSessionId: Boolean(sessionId),\n hasToolName: Boolean(toolName),\n },\n });\n\n if (!sessionId || !toolName) {\n await appendHookDiagnosticsEvent({\n hook: \"PostToolUse\",\n sessionId,\n stage: \"payload_validation\",\n result: \"skip\",\n reason: !sessionId ? \"missing_session_id\" : \"missing_tool_name\",\n toolName,\n });\n return;\n }\n\n const toolSucceeded = didToolSucceed(payload);\n const remoteChangeRecordedButSyncFailed =\n (isRemoteChangeRecordingToolName(toolName) || /remix_collab_finalize_turn$/i.test(toolName)) &&\n isRemoteChangeRecordedButLocalSyncFailed(payload);\n await appendHookDiagnosticsEvent({\n hook: \"PostToolUse\",\n sessionId,\n stage: \"tool_classified\",\n result: \"info\",\n toolName,\n fields: {\n toolSucceeded,\n remoteChangeRecordedButSyncFailed,\n isMemoryTool: isMemoryToolName(toolName),\n isRepoMutationTool: isRepoMutationToolName(toolName),\n isShellTool: isShellToolName(toolName),\n isStructuredLocalWriteTool: isStructuredLocalWriteToolName(toolName),\n isStructuredLocalReadTool: isStructuredLocalReadToolName(toolName),\n },\n });\n if (!toolSucceeded && !remoteChangeRecordedButSyncFailed) {\n await appendHookDiagnosticsEvent({\n hook: \"PostToolUse\",\n sessionId,\n stage: \"tool_result_gate\",\n result: \"skip\",\n reason: \"tool_failed_or_skipped\",\n toolName,\n });\n return;\n }\n\n if (toolSucceeded && isMemoryToolName(toolName)) {\n await markPendingTurnConsultedMemory(sessionId);\n await appendHookDiagnosticsEvent({\n hook: \"PostToolUse\",\n sessionId,\n stage: \"memory_marked_consulted\",\n result: \"success\",\n toolName,\n });\n }\n\n const manualRecordingScope = getManualRecordingScope(payload, toolName);\n if (isRepoMutationToolName(toolName) || manualRecordingScope) {\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 (toolSucceeded && isRepoMutationToolName(toolName)) {\n await markTouchedRepoObservedWrite(sessionId, targetRepo.repoRoot, { toolName });\n }\n if (manualRecordingScope) {\n await markTouchedRepoManuallyRecorded(sessionId, targetRepo.repoRoot, {\n toolName,\n scope: manualRecordingScope,\n remoteChangeRecorded: toolSucceeded\n ? isRemoteChangeRecordingToolName(toolName) || didFinalizeTurnRecordRemoteChange(payload, toolName)\n : remoteChangeRecordedButSyncFailed,\n });\n }\n await appendHookDiagnosticsEvent({\n hook: \"PostToolUse\",\n sessionId,\n stage: \"repo_marked_from_tool_cwd\",\n result: \"success\",\n toolName,\n repoRoot: targetRepo.repoRoot,\n fields: {\n hasObservedWrite: isRepoMutationToolName(toolName),\n manualRecordingScope,\n remoteChangeRecordedButSyncFailed,\n },\n });\n } else {\n await appendHookDiagnosticsEvent({\n hook: \"PostToolUse\",\n sessionId,\n stage: \"repo_marked_from_tool_cwd\",\n result: \"skip\",\n reason: \"repo_not_bound_from_tool_cwd\",\n toolName,\n });\n }\n }\n\n if (toolSucceeded && isShellToolName(toolName)) {\n const targetRepo = await resolveBoundRepoFromToolCwd(payload);\n if (targetRepo) {\n const bashCommand = extractBashCommand(payload);\n const hasObservedWrite = bashCommand ? isLikelyMutatingShellCommand(bashCommand) : false;\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,\n });\n if (hasObservedWrite) {\n await markTouchedRepoObservedWrite(sessionId, targetRepo.repoRoot, { toolName });\n }\n await appendHookDiagnosticsEvent({\n hook: \"PostToolUse\",\n sessionId,\n stage: \"shell_repo_marked\",\n result: \"success\",\n toolName,\n repoRoot: targetRepo.repoRoot,\n fields: {\n hasObservedWrite,\n bashCommandLength: bashCommand?.length ?? 0,\n },\n });\n } else {\n await appendHookDiagnosticsEvent({\n hook: \"PostToolUse\",\n sessionId,\n stage: \"shell_repo_marked\",\n result: \"skip\",\n reason: \"repo_not_bound_from_shell_cwd\",\n toolName,\n });\n }\n }\n\n if (toolSucceeded && (isStructuredLocalWriteToolName(toolName) || isStructuredLocalReadToolName(toolName))) {\n const touchedPaths = extractToolPathTargets(payload, toolName);\n const touchedRepos = await resolveTouchedBoundReposFromPaths(touchedPaths);\n await appendHookDiagnosticsEvent({\n hook: \"PostToolUse\",\n sessionId,\n stage: \"path_targets_resolved\",\n result: touchedRepos.length > 0 ? \"info\" : \"skip\",\n reason: touchedRepos.length > 0 ? null : \"no_bound_repos_from_paths\",\n toolName,\n fields: {\n touchedPathCount: touchedPaths.length,\n boundRepoCount: touchedRepos.length,\n },\n });\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: isStructuredLocalWriteToolName(toolName),\n });\n if (isStructuredLocalWriteToolName(toolName)) {\n await markTouchedRepoObservedWrite(sessionId, repo.repoRoot, { toolName });\n }\n await appendHookDiagnosticsEvent({\n hook: \"PostToolUse\",\n sessionId,\n stage: \"path_repo_marked\",\n result: \"success\",\n toolName,\n repoRoot: repo.repoRoot,\n fields: {\n hasObservedWrite: isStructuredLocalWriteToolName(toolName),\n },\n });\n }\n }\n\n await appendHookDiagnosticsEvent({\n hook: \"PostToolUse\",\n sessionId,\n stage: \"completed\",\n result: \"success\",\n toolName,\n });\n}\n\nasync function main(): Promise<void> {\n const payload = await readJsonStdin();\n await runHookPostCollab(payload);\n}\n\nmain().catch((error) => {\n const message = error instanceof Error ? error.message : String(error);\n void appendHookDiagnosticsEvent({\n hook: \"PostToolUse\",\n stage: \"unhandled_error\",\n result: \"error\",\n reason: \"exception\",\n message,\n });\n process.stderr.write(`${message}\\n`);\n process.exitCode = 0;\n});\n","import { createHash } from \"node:crypto\";\nimport fs from \"node:fs/promises\";\nimport os from \"node:os\";\nimport path from \"node:path\";\n\nimport { getPendingTurnStateRootPath, listPendingTurnStateSummaries, type PendingTurnStateSummary } from \"./hook-state.js\";\nimport { pluginMetadata } from \"./metadata.js\";\n\ntype HookDiagnosticsResult = \"start\" | \"info\" | \"skip\" | \"success\" | \"error\";\ntype HookDiagnosticsFieldValue = string | number | boolean | null;\n\nexport type HookDiagnosticsEvent = {\n ts: string;\n hook: string;\n pluginVersion: string;\n pid: number;\n sessionId: string | null;\n turnId: string | null;\n stage: string;\n result: HookDiagnosticsResult;\n reason: string | null;\n toolName: string | null;\n repoRoot: string | null;\n message: string | null;\n fields: Record<string, HookDiagnosticsFieldValue>;\n};\n\nexport type HookDiagnosticsReport = {\n logPath: string;\n stateRoot: string;\n recentEvents: HookDiagnosticsEvent[];\n pendingStates: PendingTurnStateSummary[];\n};\n\nconst DEFAULT_EVENT_LIMIT = 50;\nconst MAX_EVENT_LIMIT = 200;\nconst MAX_LOG_BYTES = 512 * 1024;\n\nfunction resolveClaudeRoot(): string {\n const configured = process.env.CLAUDE_CONFIG_DIR?.trim();\n return configured || path.join(os.homedir(), \".claude\");\n}\n\nfunction resolvePluginDataDirName(): string {\n return `${pluginMetadata.pluginId}-${pluginMetadata.pluginId}`;\n}\n\nexport function getHookDiagnosticsDirPath(): string {\n const configured = process.env.REMIX_CLAUDE_PLUGIN_HOOK_DIAGNOSTICS_DIR?.trim();\n return configured || path.join(resolveClaudeRoot(), \"plugins\", \"data\", resolvePluginDataDirName());\n}\n\nexport function getHookDiagnosticsLogPath(): string {\n return path.join(getHookDiagnosticsDirPath(), \"hooks.ndjson\");\n}\n\nfunction clampEventLimit(limit?: number): number {\n if (typeof limit !== \"number\" || !Number.isFinite(limit)) return DEFAULT_EVENT_LIMIT;\n return Math.max(1, Math.min(MAX_EVENT_LIMIT, Math.trunc(limit)));\n}\n\nfunction toFieldValue(value: unknown): HookDiagnosticsFieldValue | undefined {\n if (value === null) return null;\n if (typeof value === \"string\") return value;\n if (typeof value === \"number\" && Number.isFinite(value)) return value;\n if (typeof value === \"boolean\") return value;\n return undefined;\n}\n\nfunction normalizeFields(fields?: Record<string, unknown>): Record<string, HookDiagnosticsFieldValue> {\n if (!fields) return {};\n const normalizedEntries = Object.entries(fields)\n .map(([key, value]) => {\n const normalized = toFieldValue(value);\n return normalized === undefined ? null : ([key, normalized] as const);\n })\n .filter((entry): entry is readonly [string, HookDiagnosticsFieldValue] => entry !== null);\n return Object.fromEntries(normalizedEntries);\n}\n\nasync function rotateLogIfNeeded(logPath: string): Promise<void> {\n const stat = await fs.stat(logPath).catch(() => null);\n if (!stat || stat.size < MAX_LOG_BYTES) {\n return;\n }\n\n const rotatedPath = `${logPath}.1`;\n await fs.rm(rotatedPath, { force: true }).catch(() => undefined);\n await fs.rename(logPath, rotatedPath).catch(() => undefined);\n}\n\nexport function summarizeText(value: string | null | undefined): {\n present: boolean;\n length: number;\n sha256Prefix: string | null;\n} {\n if (typeof value !== \"string\" || !value.trim()) {\n return {\n present: false,\n length: 0,\n sha256Prefix: null,\n };\n }\n\n const trimmed = value.trim();\n return {\n present: true,\n length: trimmed.length,\n sha256Prefix: createHash(\"sha256\").update(trimmed).digest(\"hex\").slice(0, 12),\n };\n}\n\nexport async function appendHookDiagnosticsEvent(params: {\n hook: string;\n sessionId?: string | null;\n turnId?: string | null;\n stage: string;\n result: HookDiagnosticsResult;\n reason?: string | null;\n toolName?: string | null;\n repoRoot?: string | null;\n message?: string | null;\n fields?: Record<string, unknown>;\n}): Promise<void> {\n try {\n const logPath = getHookDiagnosticsLogPath();\n await fs.mkdir(path.dirname(logPath), { recursive: true });\n await rotateLogIfNeeded(logPath);\n const event: HookDiagnosticsEvent = {\n ts: new Date().toISOString(),\n hook: params.hook,\n pluginVersion: pluginMetadata.version,\n pid: process.pid,\n sessionId: params.sessionId?.trim() || null,\n turnId: params.turnId?.trim() || null,\n stage: params.stage.trim(),\n result: params.result,\n reason: params.reason?.trim() || null,\n toolName: params.toolName?.trim() || null,\n repoRoot: params.repoRoot?.trim() || null,\n message: params.message?.trim() || null,\n fields: normalizeFields(params.fields),\n };\n await fs.appendFile(logPath, `${JSON.stringify(event)}\\n`, \"utf8\");\n } catch {\n // Diagnostics are best-effort and must never break hook execution.\n }\n}\n\nasync function readEventsFromFile(filePath: string): Promise<HookDiagnosticsEvent[]> {\n const raw = await fs.readFile(filePath, \"utf8\").catch(() => null);\n if (!raw) return [];\n return raw\n .split(\"\\n\")\n .map((line) => line.trim())\n .filter(Boolean)\n .flatMap((line) => {\n try {\n const parsed = JSON.parse(line) as HookDiagnosticsEvent;\n return parsed && typeof parsed === \"object\" ? [parsed] : [];\n } catch {\n return [];\n }\n });\n}\n\nexport async function readRecentHookDiagnosticsEvents(limit?: number): Promise<HookDiagnosticsEvent[]> {\n const eventLimit = clampEventLimit(limit);\n const logPath = getHookDiagnosticsLogPath();\n const [rotated, current] = await Promise.all([readEventsFromFile(`${logPath}.1`), readEventsFromFile(logPath)]);\n return [...rotated, ...current].slice(-eventLimit);\n}\n\nexport async function readHookDiagnosticsReport(limit?: number): Promise<HookDiagnosticsReport> {\n const [recentEvents, pendingStates] = await Promise.all([\n readRecentHookDiagnosticsEvents(limit),\n listPendingTurnStateSummaries(),\n ]);\n\n return {\n logPath: getHookDiagnosticsLogPath(),\n stateRoot: getPendingTurnStateRootPath(),\n recentEvents,\n pendingStates,\n };\n}\n","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\";\nexport type ManualRecordingScope = \"change_step\" | \"full_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 manualRecordingScope: ManualRecordingScope | null;\n manualRemoteChangeRecordedAt: 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\nexport type PendingTouchedRepoSummary = {\n repoRoot: string;\n projectId: string | null;\n currentAppId: string | null;\n upstreamAppId: string | null;\n lastTouchedAt: string;\n lastObservedWriteAt: string | null;\n touchedBy: string[];\n hasObservedWrite: boolean;\n manuallyRecorded: boolean;\n manualRecordingScope: ManualRecordingScope | null;\n stopAttempted: boolean;\n stopRecorded: boolean;\n stopRecordedMode: RepoRecordMode | null;\n recordingFailureMessage: string | null;\n recordingFailureHint: string | null;\n recordingFailedAt: string | null;\n};\n\nexport type PendingTurnStateSummary = {\n sessionId: string;\n turnId: string;\n initialCwd: string | null;\n submittedAt: string;\n consultedMemory: boolean;\n promptLength: number;\n touchedRepoCount: number;\n turnFailureMessage: string | null;\n turnFailureHint: string | null;\n turnFailedAt: string | null;\n touchedRepos: PendingTouchedRepoSummary[];\n};\n\nfunction stateRoot(): string {\n const configured = process.env.REMIX_CLAUDE_PLUGIN_HOOK_STATE_ROOT?.trim();\n return configured || 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 await fs.mkdir(stateRoot(), { recursive: true });\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 manualRecordingScope:\n parsed.manualRecordingScope === \"change_step\" || parsed.manualRecordingScope === \"full_turn\"\n ? parsed.manualRecordingScope\n : null,\n manualRemoteChangeRecordedAt: normalizeString(parsed.manualRemoteChangeRecordedAt),\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 manualRecordingScope: null,\n manualRemoteChangeRecordedAt: 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\nfunction summarizeTouchedRepo(repo: TouchedRepoState): PendingTouchedRepoSummary {\n return {\n repoRoot: repo.repoRoot,\n projectId: repo.projectId,\n currentAppId: repo.currentAppId,\n upstreamAppId: repo.upstreamAppId,\n lastTouchedAt: repo.lastTouchedAt,\n lastObservedWriteAt: repo.lastObservedWriteAt,\n touchedBy: [...repo.touchedBy],\n hasObservedWrite: repo.hasObservedWrite,\n manuallyRecorded: repo.manuallyRecorded,\n manualRecordingScope: repo.manualRecordingScope,\n stopAttempted: repo.stopAttempted,\n stopRecorded: repo.stopRecorded,\n stopRecordedMode: repo.stopRecordedMode,\n recordingFailureMessage: repo.recordingFailureMessage,\n recordingFailureHint: repo.recordingFailureHint,\n recordingFailedAt: repo.recordingFailedAt,\n };\n}\n\nfunction summarizePendingTurnState(state: PendingTurnState): PendingTurnStateSummary {\n const touchedRepos = Object.values(state.touchedRepos)\n .sort((a, b) => a.repoRoot.localeCompare(b.repoRoot))\n .map((repo) => summarizeTouchedRepo(repo));\n return {\n sessionId: state.sessionId,\n turnId: state.turnId,\n initialCwd: state.initialCwd,\n submittedAt: state.submittedAt,\n consultedMemory: state.consultedMemory,\n promptLength: state.prompt.length,\n touchedRepoCount: touchedRepos.length,\n turnFailureMessage: state.turnFailureMessage,\n turnFailureHint: state.turnFailureHint,\n turnFailedAt: state.turnFailedAt,\n touchedRepos,\n };\n}\n\nexport function getPendingTurnStateRootPath(): string {\n return stateRoot();\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; scope?: ManualRecordingScope | null; remoteChangeRecorded?: boolean },\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.manualRecordingScope = params?.scope ?? current.manualRecordingScope;\n if (params?.remoteChangeRecorded) {\n current.manualRemoteChangeRecordedAt = current.manuallyRecordedAt;\n }\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 listPendingTurnStateSummaries(): Promise<PendingTurnStateSummary[]> {\n const root = stateRoot();\n const entries = await fs.readdir(root, { withFileTypes: true }).catch(() => []);\n const sessionIds = entries\n .filter((entry) => entry.isFile() && entry.name.endsWith(\".json\"))\n .map((entry) => entry.name.replace(/\\.json$/, \"\"))\n .sort((a, b) => a.localeCompare(b));\n const states = await Promise.all(sessionIds.map((sessionId) => loadPendingTurnState(sessionId)));\n return states\n .filter((state): state is PendingTurnState => state !== null)\n .sort((a, b) => b.submittedAt.localeCompare(a.submittedAt))\n .map((state) => summarizePendingTurnState(state));\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","{\n \"name\": \"@remixhq/claude-plugin\",\n \"version\": \"0.1.15\",\n \"description\": \"Claude Code plugin for Remix collaboration workflows\",\n \"homepage\": \"https://github.com/RemixDotOne/remix-claude-plugin\",\n \"license\": \"MIT\",\n \"repository\": {\n \"type\": \"git\",\n \"url\": \"https://github.com/RemixDotOne/remix-claude-plugin.git\"\n },\n \"type\": \"module\",\n \"engines\": {\n \"node\": \">=20\"\n },\n \"publishConfig\": {\n \"access\": \"public\"\n },\n \"files\": [\n \"dist\",\n \".claude-plugin/plugin.json\",\n \".mcp.json\",\n \"skills\",\n \"hooks\",\n \"agents\"\n ],\n \"scripts\": {\n \"build\": \"tsup\",\n \"postbuild\": \"node -e \\\"const fs=require('node:fs'); for (const p of ['dist/mcp-server.cjs','dist/hook-pre-git.cjs','dist/hook-user-prompt.cjs','dist/hook-post-collab.cjs','dist/hook-stop-collab.cjs']) fs.chmodSync(p, 0o755);\\\"\",\n \"dev\": \"tsx src/mcp-server.ts\",\n \"typecheck\": \"tsc -p tsconfig.json --noEmit\",\n \"prepack\": \"npm run build\"\n },\n \"dependencies\": {\n \"@remixhq/core\": \"^0.1.10\",\n \"@remixhq/mcp\": \"^0.1.10\"\n },\n \"devDependencies\": {\n \"@types/node\": \"^25.4.0\",\n \"tsup\": \"^8.5.1\",\n \"tsx\": \"^4.21.0\",\n \"typescript\": \"^5.9.3\"\n }\n}\n","import pkg from \"../package.json\";\n\nexport const pluginMetadata = {\n name: pkg.name,\n version: pkg.version,\n description: pkg.description,\n pluginId: \"remix\",\n agentName: \"remix-collab\",\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\nfunction extractToolStructuredData(payload: Record<string, unknown>): Record<string, unknown> | null {\n const toolResponse = extractToolResponse(payload);\n const structuredContent = getNestedRecord(toolResponse?.structuredContent) ?? getNestedRecord(payload.structuredContent);\n return (\n getNestedRecord(toolResponse?.data) ??\n getNestedRecord(structuredContent?.data) ??\n structuredContent\n );\n}\n\nexport function extractToolName(payload: Record<string, unknown>): string | null {\n return extractString(payload, [\"tool_name\", \"toolName\"]);\n}\n\nexport function normalizeHookToolName(toolName: string | null): string | null {\n if (!toolName) return null;\n const trimmed = toolName.trim();\n if (!trimmed) return null;\n\n const remixToolIndex = trimmed.toLowerCase().indexOf(\"remix_collab_\");\n if (remixToolIndex >= 0) {\n return trimmed.slice(remixToolIndex);\n }\n\n return trimmed;\n}\n\nexport function extractAssistantResponse(payload: Record<string, unknown>): string | null {\n const candidateKeys = [\n \"last_assistant_message\",\n \"lastAssistantMessage\",\n \"assistant_response\",\n \"assistantResponse\",\n \"assistant_message\",\n \"assistantMessage\",\n \"response\",\n \"message\",\n ];\n\n return (\n extractString(payload, candidateKeys) ??\n extractString(extractToolResponse(payload) ?? {}, candidateKeys) ??\n extractString(extractToolStructuredData(payload) ?? {}, candidateKeys) ??\n extractString(extractToolInput(payload), candidateKeys)\n );\n}\n\nexport function extractFinalizeTurnMode(payload: Record<string, unknown>): \"changed_turn\" | \"no_diff_turn\" | null {\n const mode =\n extractString(extractToolStructuredData(payload) ?? {}, [\"mode\"]) ??\n extractString(extractToolResponse(payload) ?? {}, [\"mode\"]) ??\n extractString(payload, [\"mode\"]);\n if (mode === \"changed_turn\" || mode === \"no_diff_turn\") {\n return mode;\n }\n return null;\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 extractBashCommand(payload: Record<string, unknown>): string | null {\n const toolInput = extractToolInput(payload);\n return extractString(toolInput, [\"command\", \"cmd\", \"bash_command\", \"bashCommand\"]);\n}\n\nexport function extractToolErrorMessage(payload: Record<string, unknown>): string | null {\n const toolResponse = extractToolResponse(payload);\n const toolError = getNestedRecord(toolResponse?.error) ?? getNestedRecord(payload.error);\n return (\n extractString(toolError ?? {}, [\"message\"]) ??\n extractString(toolResponse ?? {}, [\"errorMessage\", \"message\"]) ??\n extractString(payload, [\"errorMessage\"])\n );\n}\n\nfunction extractToolStatus(payload: Record<string, unknown>): string | null {\n const toolResponse = extractToolResponse(payload);\n return (\n extractString(toolResponse ?? {}, [\"status\", \"state\"]) ??\n extractString(payload, [\"status\", \"state\"])\n );\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\n if (extractToolErrorMessage(payload)) {\n return false;\n }\n\n const status = extractToolStatus(payload)?.toLowerCase();\n if (status === \"error\" || status === \"failed\" || status === \"failure\") {\n return false;\n }\n\n const hookEventName = extractString(payload, [\"hook_event_name\", \"hookEventName\"]);\n return hookEventName === \"PostToolUse\";\n}\n\nexport function isRemoteChangeRecordedButLocalSyncFailed(payload: Record<string, unknown>): boolean {\n return extractToolErrorMessage(payload) === \"Change step succeeded remotely, but automatic local sync failed.\";\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"],"mappings":"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","names":["import_node_crypto","import_promises","import_node_os","import_node_path","path","os","fs","fs","path","os","fs","fs","path","import_promises","import_node_path","import_promises","path2","fs2","path","fs"]}
@@ -211,7 +211,7 @@ var GIT_ADVISORIES = [
211
211
  subcommand: "commit",
212
212
  re: /\bgit\b(?:\s+-[^\s]+)*\s+commit\b/i,
213
213
  category: "mutation",
214
- message: "This repository is bound to Remix. Raw `git commit` is blocked for ordinary collaboration work because it bypasses Remix change-step recording. Let the stop hook record changed turns automatically, or use `remix_collab_add` only for explicit recovery, backfill, or manual-recording workflows.",
214
+ message: "This repository is bound to Remix. Raw `git commit` is blocked for ordinary collaboration work because it bypasses Remix turn recording. You must record the completed turn with `remix_collab_finalize_turn` before ending the response.",
215
215
  blocked: true
216
216
  },
217
217
  {