@agimon-ai/doompi-git 0.0.1-alpha.3 → 0.0.1-alpha.30

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.
Files changed (179) hide show
  1. package/README.md +2 -0
  2. package/dist/apiContracts.cjs +149 -0
  3. package/dist/apiContracts.cjs.map +1 -0
  4. package/dist/apiContracts.d.mts +53 -0
  5. package/dist/apiContracts.d.mts.map +1 -0
  6. package/dist/apiContracts.mjs +144 -0
  7. package/dist/apiContracts.mjs.map +1 -0
  8. package/dist/extensions/pi.cjs +109 -1
  9. package/dist/extensions/pi.cjs.map +1 -0
  10. package/dist/extensions/pi.d.mts +6 -2
  11. package/dist/extensions/pi.d.mts.map +1 -0
  12. package/dist/extensions/pi.mjs +104 -1
  13. package/dist/extensions/pi.mjs.map +1 -0
  14. package/dist/extensions/server.cjs +495 -0
  15. package/dist/extensions/server.cjs.map +1 -0
  16. package/dist/extensions/server.d.mts +5 -0
  17. package/dist/extensions/server.d.mts.map +1 -0
  18. package/dist/extensions/server.mjs +490 -0
  19. package/dist/extensions/server.mjs.map +1 -0
  20. package/dist/extensions/web.mjs +580 -0
  21. package/dist/extensions/web.mjs.map +1 -0
  22. package/dist/gitDependencies-D-EUEIkE.mjs +22 -0
  23. package/dist/gitDependencies-D-EUEIkE.mjs.map +1 -0
  24. package/dist/gitDependencies-DKJr5kLQ.cjs +33 -0
  25. package/dist/gitDependencies-DKJr5kLQ.cjs.map +1 -0
  26. package/dist/index-BQq2vblZ.d.mts +143 -0
  27. package/dist/index-BQq2vblZ.d.mts.map +1 -0
  28. package/dist/index.cjs +4 -1
  29. package/dist/index.d.mts +2 -5
  30. package/dist/index.mjs +2 -1
  31. package/dist/runWorktree-B6ikYRrG.cjs +318 -0
  32. package/dist/runWorktree-B6ikYRrG.cjs.map +1 -0
  33. package/dist/runWorktree-BL99h9oH.mjs +289 -0
  34. package/dist/runWorktree-BL99h9oH.mjs.map +1 -0
  35. package/dist/worktreeOperations-DAbs0ZTX.mjs +1054 -0
  36. package/dist/worktreeOperations-DAbs0ZTX.mjs.map +1 -0
  37. package/dist/worktreeOperations-DjO_Y5Yv.cjs +1138 -0
  38. package/dist/worktreeOperations-DjO_Y5Yv.cjs.map +1 -0
  39. package/package.json +53 -39
  40. package/src/prompts/doompi-use-git/SKILL.md +5 -2
  41. package/dist/_virtual/_rolldown/runtime.cjs +0 -1
  42. package/dist/adapters/channel/worktreeChannel.cjs +0 -2
  43. package/dist/adapters/channel/worktreeChannel.cjs.map +0 -1
  44. package/dist/adapters/channel/worktreeChannel.d.cts +0 -40
  45. package/dist/adapters/channel/worktreeChannel.d.cts.map +0 -1
  46. package/dist/adapters/channel/worktreeChannel.d.mts +0 -40
  47. package/dist/adapters/channel/worktreeChannel.d.mts.map +0 -1
  48. package/dist/adapters/channel/worktreeChannel.mjs +0 -2
  49. package/dist/adapters/channel/worktreeChannel.mjs.map +0 -1
  50. package/dist/adapters/filesystem/atomicJson.cjs +0 -2
  51. package/dist/adapters/filesystem/atomicJson.cjs.map +0 -1
  52. package/dist/adapters/filesystem/atomicJson.mjs +0 -2
  53. package/dist/adapters/filesystem/atomicJson.mjs.map +0 -1
  54. package/dist/adapters/filesystem/paths.cjs +0 -2
  55. package/dist/adapters/filesystem/paths.cjs.map +0 -1
  56. package/dist/adapters/filesystem/paths.mjs +0 -2
  57. package/dist/adapters/filesystem/paths.mjs.map +0 -1
  58. package/dist/adapters/hub/hubClient.cjs +0 -2
  59. package/dist/adapters/hub/hubClient.cjs.map +0 -1
  60. package/dist/adapters/hub/hubClient.d.cts +0 -1
  61. package/dist/adapters/hub/hubClient.d.mts +0 -1
  62. package/dist/adapters/hub/hubClient.mjs +0 -2
  63. package/dist/adapters/hub/hubClient.mjs.map +0 -1
  64. package/dist/adapters/pi/extension.cjs +0 -2
  65. package/dist/adapters/pi/extension.cjs.map +0 -1
  66. package/dist/adapters/pi/extension.d.cts +0 -9
  67. package/dist/adapters/pi/extension.d.cts.map +0 -1
  68. package/dist/adapters/pi/extension.d.mts +0 -9
  69. package/dist/adapters/pi/extension.d.mts.map +0 -1
  70. package/dist/adapters/pi/extension.mjs +0 -2
  71. package/dist/adapters/pi/extension.mjs.map +0 -1
  72. package/dist/adapters/pi/extensions/runWorktreeTool.cjs +0 -15
  73. package/dist/adapters/pi/extensions/runWorktreeTool.cjs.map +0 -1
  74. package/dist/adapters/pi/extensions/runWorktreeTool.mjs +0 -15
  75. package/dist/adapters/pi/extensions/runWorktreeTool.mjs.map +0 -1
  76. package/dist/adapters/web/worktreesChannel.cjs +0 -2
  77. package/dist/adapters/web/worktreesChannel.cjs.map +0 -1
  78. package/dist/adapters/web/worktreesChannel.d.cts +0 -21
  79. package/dist/adapters/web/worktreesChannel.d.cts.map +0 -1
  80. package/dist/adapters/web/worktreesChannel.d.mts +0 -21
  81. package/dist/adapters/web/worktreesChannel.d.mts.map +0 -1
  82. package/dist/adapters/web/worktreesChannel.mjs +0 -2
  83. package/dist/adapters/web/worktreesChannel.mjs.map +0 -1
  84. package/dist/adapters/worktree/dependencyInstall.cjs +0 -2
  85. package/dist/adapters/worktree/dependencyInstall.cjs.map +0 -1
  86. package/dist/adapters/worktree/dependencyInstall.mjs +0 -2
  87. package/dist/adapters/worktree/dependencyInstall.mjs.map +0 -1
  88. package/dist/adapters/worktree/gitCli.cjs +0 -4
  89. package/dist/adapters/worktree/gitCli.cjs.map +0 -1
  90. package/dist/adapters/worktree/gitCli.mjs +0 -4
  91. package/dist/adapters/worktree/gitCli.mjs.map +0 -1
  92. package/dist/adapters/worktree/repositoryIdentity.cjs +0 -2
  93. package/dist/adapters/worktree/repositoryIdentity.cjs.map +0 -1
  94. package/dist/adapters/worktree/repositoryIdentity.mjs +0 -2
  95. package/dist/adapters/worktree/repositoryIdentity.mjs.map +0 -1
  96. package/dist/adapters/worktree/worktreeOperations.cjs +0 -2
  97. package/dist/adapters/worktree/worktreeOperations.cjs.map +0 -1
  98. package/dist/adapters/worktree/worktreeOperations.d.cts +0 -32
  99. package/dist/adapters/worktree/worktreeOperations.d.cts.map +0 -1
  100. package/dist/adapters/worktree/worktreeOperations.d.mts +0 -32
  101. package/dist/adapters/worktree/worktreeOperations.d.mts.map +0 -1
  102. package/dist/adapters/worktree/worktreeOperations.mjs +0 -2
  103. package/dist/adapters/worktree/worktreeOperations.mjs.map +0 -1
  104. package/dist/adapters/worktree/worktreeRegistry.cjs +0 -2
  105. package/dist/adapters/worktree/worktreeRegistry.cjs.map +0 -1
  106. package/dist/adapters/worktree/worktreeRegistry.mjs +0 -2
  107. package/dist/adapters/worktree/worktreeRegistry.mjs.map +0 -1
  108. package/dist/commands/doomGitCommand.cjs +0 -2
  109. package/dist/commands/doomGitCommand.cjs.map +0 -1
  110. package/dist/commands/doomGitCommand.mjs +0 -2
  111. package/dist/commands/doomGitCommand.mjs.map +0 -1
  112. package/dist/container/index.cjs +0 -2
  113. package/dist/container/index.cjs.map +0 -1
  114. package/dist/container/index.d.cts +0 -6
  115. package/dist/container/index.d.cts.map +0 -1
  116. package/dist/container/index.d.mts +0 -6
  117. package/dist/container/index.d.mts.map +0 -1
  118. package/dist/container/index.mjs +0 -2
  119. package/dist/container/index.mjs.map +0 -1
  120. package/dist/extensions/pi.d.cts +0 -2
  121. package/dist/hubApi.cjs +0 -2
  122. package/dist/hubApi.cjs.map +0 -1
  123. package/dist/hubApi.d.cts +0 -6
  124. package/dist/hubApi.d.cts.map +0 -1
  125. package/dist/hubApi.d.mts +0 -6
  126. package/dist/hubApi.d.mts.map +0 -1
  127. package/dist/hubApi.mjs +0 -2
  128. package/dist/hubApi.mjs.map +0 -1
  129. package/dist/index.d.cts +0 -5
  130. package/dist/schemas/runWorktreeTool.cjs +0 -2
  131. package/dist/schemas/runWorktreeTool.cjs.map +0 -1
  132. package/dist/schemas/runWorktreeTool.mjs +0 -2
  133. package/dist/schemas/runWorktreeTool.mjs.map +0 -1
  134. package/dist/services/extensionService.cjs +0 -2
  135. package/dist/services/extensionService.cjs.map +0 -1
  136. package/dist/services/extensionService.d.cts +0 -8
  137. package/dist/services/extensionService.d.cts.map +0 -1
  138. package/dist/services/extensionService.d.mts +0 -8
  139. package/dist/services/extensionService.d.mts.map +0 -1
  140. package/dist/services/extensionService.mjs +0 -2
  141. package/dist/services/extensionService.mjs.map +0 -1
  142. package/dist/services/support/errors.cjs +0 -2
  143. package/dist/services/support/errors.cjs.map +0 -1
  144. package/dist/services/support/errors.mjs +0 -2
  145. package/dist/services/support/errors.mjs.map +0 -1
  146. package/dist/services/worktreeNaming.cjs +0 -2
  147. package/dist/services/worktreeNaming.cjs.map +0 -1
  148. package/dist/services/worktreeNaming.d.cts +0 -27
  149. package/dist/services/worktreeNaming.d.cts.map +0 -1
  150. package/dist/services/worktreeNaming.d.mts +0 -27
  151. package/dist/services/worktreeNaming.d.mts.map +0 -1
  152. package/dist/services/worktreeNaming.mjs +0 -2
  153. package/dist/services/worktreeNaming.mjs.map +0 -1
  154. package/dist/types/extension.d.cts +0 -17
  155. package/dist/types/extension.d.cts.map +0 -1
  156. package/dist/types/extension.d.mts +0 -17
  157. package/dist/types/extension.d.mts.map +0 -1
  158. package/dist/types/webWorktrees.cjs +0 -2
  159. package/dist/types/webWorktrees.cjs.map +0 -1
  160. package/dist/types/webWorktrees.mjs +0 -2
  161. package/dist/types/webWorktrees.mjs.map +0 -1
  162. package/dist/types/worktreeRegistry.d.cts +0 -41
  163. package/dist/types/worktreeRegistry.d.cts.map +0 -1
  164. package/dist/types/worktreeRegistry.d.mts +0 -41
  165. package/dist/types/worktreeRegistry.d.mts.map +0 -1
  166. package/dist/webHub.cjs +0 -1
  167. package/dist/webHub.d.cts +0 -2
  168. package/dist/webHub.d.mts +0 -2
  169. package/dist/webHub.mjs +0 -1
  170. package/src/exports/webClient.ts +0 -1
  171. package/src/types/webWorktrees.ts +0 -55
  172. package/src/types/worktreeRegistry.ts +0 -69
  173. package/src/web/components/RunWorktreeToolMessage.tsx +0 -37
  174. package/src/web/components/WorktreesActivitySection.tsx +0 -64
  175. package/src/web/components/WorktreesPanel.tsx +0 -125
  176. package/src/web/components/toolRenderers.ts +0 -7
  177. package/src/web/index.ts +0 -30
  178. package/src/web/stores/worktreesActivityStore.ts +0 -124
  179. package/src/web/tsconfig.json +0 -22
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worktreeOperations-DjO_Y5Yv.cjs","names":["fs","fs","path","path","fs","path","fs","createHash","os","path","fs","isDeepStrictEqual","randomUUID","path","setTimeout","resolveWorktreeSyncLocation","fs","require","randomUUID"],"sources":["../src/services/gitCli/index.ts","../src/services/compositionMirror/index.ts","../src/services/errors/index.ts","../src/services/atomicJson/index.ts","../src/services/repositoryIdentity/index.ts","../src/services/paths/index.ts","../src/services/worktreeEvents/index.ts","../src/services/worktreeNaming/index.ts","../src/services/worktreeRegistry/index.ts","../src/services/worktreeOperations/index.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport fs from 'node:fs';\n\nimport type { WorktreeGit } from '../../types/worktreeRegistry';\n\n/**\n * Git, as this package needs it.\n *\n * The timeouts are not uniform because the operations are not comparable. A\n * read answers in milliseconds; `worktree add` copies an entire checkout and\n * `worktree remove` deletes one, both bounded by the filesystem rather than by\n * git, and both minutes rather than seconds on a large repository or on\n * Windows. A single timeout would either fail honest work or hang on a wedged\n * one.\n */\nconst READ_TIMEOUT_MS = 30_000;\nconst ADD_TIMEOUT_MS = 300_000;\nconst REMOVE_TIMEOUT_MS = 300_000;\nconst PRUNE_TIMEOUT_MS = 15_000;\nconst MAX_OUTPUT_BYTES = 1_000_000;\n\ninterface GitResult {\n code: number;\n stdout: string;\n stderr: string;\n}\n\n/**\n * Runs git and reports the outcome without ever throwing on a non-zero exit.\n *\n * LC_ALL is pinned because two callers below decide what to do by matching\n * git's own wording, and a translated message would silently turn a recognised\n * condition into an unrecognised failure.\n */\nfunction git(cwd: string, args: readonly string[], timeout: number): Promise<GitResult> {\n return new Promise((resolve) => {\n execFile(\n 'git',\n [...args],\n { cwd, timeout, maxBuffer: MAX_OUTPUT_BYTES, env: { ...process.env, LC_ALL: 'C' } },\n (error, stdout, stderr) => {\n const code = error && typeof error.code === 'number' ? error.code : error ? 1 : 0;\n resolve({ code, stdout: String(stdout), stderr: String(stderr) });\n },\n );\n });\n}\n\n/**\n * The message a failed git call is allowed to surface.\n *\n * Raw stderr never travels: it can carry a remote URL with a token in it, and\n * this text ends up in a tool result the model reads and may repeat.\n */\nfunction failure(operation: string, result: GitResult): Error {\n return new Error(\n `git ${operation} failed (exit ${String(result.code)}, ${String(result.stderr.length)} bytes of stderr)`,\n );\n}\n\n/** Git's own words for \"that worktree is not there any more\". */\nfunction missingWorktree(stderr: string): boolean {\n const normalized = stderr.toLowerCase();\n return normalized.includes('is not a working tree') || normalized.includes('cannot remove working tree');\n}\n\nexport function createWorktreeGit(): WorktreeGit {\n const prune = async (repositoryRoot: string): Promise<void> => {\n await git(repositoryRoot, ['worktree', 'prune'], PRUNE_TIMEOUT_MS);\n };\n\n return {\n async addWorktree({ repositoryRoot, path, branch, baseRef }) {\n const result = await git(repositoryRoot, ['worktree', 'add', '-b', branch, path, baseRef], ADD_TIMEOUT_MS);\n if (result.code !== 0) throw failure('worktree add', result);\n },\n\n async removeWorktree({ repositoryRoot, path, force }) {\n const args = ['worktree', 'remove', ...(force ? ['--force'] : []), path];\n const result = await git(repositoryRoot, args, REMOVE_TIMEOUT_MS);\n if (result.code === 0) return;\n // A directory deleted behind git's back leaves an administrative entry\n // that makes a later `worktree add` at the same path refuse. Treating\n // \"already gone\" as success and pruning is what keeps that path reusable,\n // and it makes remove idempotent for a caller retrying after a crash.\n if (missingWorktree(result.stderr) && !fs.existsSync(path)) {\n await prune(repositoryRoot);\n return;\n }\n throw failure('worktree remove', result);\n },\n\n async deleteBranch({ repositoryRoot, branch }) {\n // -d, never -D: git refuses a branch that still holds unmerged commits,\n // and that refusal is the whole safety property. A rollback that could\n // throw away work someone committed in the worktree is worse than a\n // branch left behind.\n const result = await git(repositoryRoot, ['branch', '-d', branch], READ_TIMEOUT_MS);\n return result.code === 0;\n },\n pruneWorktrees: prune,\n\n async listWorktreePaths(repositoryRoot) {\n const result = await git(repositoryRoot, ['worktree', 'list', '--porcelain'], READ_TIMEOUT_MS);\n if (result.code !== 0) return [];\n return result.stdout\n .split('\\n')\n .filter((line) => line.startsWith('worktree '))\n .map((line) => line.slice('worktree '.length).trim())\n .filter((line) => line !== '');\n },\n\n async dirtyFiles(path) {\n const result = await git(path, ['status', '--porcelain'], READ_TIMEOUT_MS);\n if (result.code !== 0) return [];\n return result.stdout\n .split('\\n')\n .map((line) => line.slice(3).trim())\n .filter((line) => line !== '');\n },\n\n async repositoryRoot(cwd) {\n const result = await git(cwd, ['rev-parse', '--show-toplevel'], READ_TIMEOUT_MS);\n if (result.code !== 0) return undefined;\n const root = result.stdout.trim();\n return root === '' ? undefined : root;\n },\n\n async currentBranch(cwd) {\n const result = await git(cwd, ['rev-parse', '--abbrev-ref', 'HEAD'], READ_TIMEOUT_MS);\n if (result.code !== 0) return undefined;\n const branch = result.stdout.trim();\n return branch === '' ? undefined : branch;\n },\n\n async remoteBaseRef(cwd) {\n const upstream = await git(\n cwd,\n ['rev-parse', '--abbrev-ref', '--symbolic-full-name', '@{upstream}'],\n READ_TIMEOUT_MS,\n );\n if (upstream.code === 0) {\n const branch = upstream.stdout.trim();\n if (branch !== '') return branch;\n }\n const origin = await git(\n cwd,\n ['symbolic-ref', '--quiet', '--short', 'refs/remotes/origin/HEAD'],\n READ_TIMEOUT_MS,\n );\n if (origin.code === 0) {\n const branch = origin.stdout.trim();\n if (branch !== '') return branch;\n }\n const defaults = await git(cwd, ['for-each-ref', '--format=%(symref:short)', 'refs/remotes'], READ_TIMEOUT_MS);\n if (defaults.code !== 0) return undefined;\n return defaults.stdout\n .split('\\n')\n .map((line) => line.trim())\n .find((line) => line !== '');\n },\n\n async mergeBranch({ repositoryRoot, branch, message }) {\n // --no-ff so the worktree's work stays identifiable as a unit after the\n // fact. A fast-forward would erase the fact that it was ever separate,\n // which is the one thing worth keeping about a worktree.\n const args = ['merge', '--no-ff', ...(message === undefined ? [] : ['-m', message]), branch];\n const result = await git(repositoryRoot, args, READ_TIMEOUT_MS);\n if (result.code === 0) return;\n // A conflicted merge leaves the parent mid-merge, which is a state the\n // person has to resolve; aborting for them would throw away the conflict\n // markers they need to see.\n throw failure('merge', result);\n },\n };\n}\n","/**\n * Giving a new worktree the build output its parent checkout already has.\n *\n * DESIGN PATTERNS:\n * - Mirrors, never builds. Everything here is a copy or a symlink of something\n * the parent checkout already produced, so it costs seconds and cannot fail\n * the way a package manager or a compiler can.\n * - `node_modules` is linked, `dist` is copied. The module tree is gigabytes\n * and read-only in practice, so a link is right; build output is small and a\n * link would send a build run inside the worktree straight into the parent's\n * directory.\n * - Absent parent output means nothing to do. A repository with no\n * `node_modules` at its root is either not a JS project or not installed, and\n * either way this has no opinion about it, so a Rust, Go or Python worktree\n * pays nothing.\n *\n * WHY THIS EXISTS:\n * A DoomPi session composes the extensions its repository's config names, and\n * this repository names its own workspace packages by relative path. Those\n * packages declare `pi.extensions` under `dist/`, which git does not track, so\n * a fresh worktree resolves none of them and the session dies on start. The\n * parent checkout has already built exactly what the child needs.\n *\n * AVOID:\n * - Recursing into `node_modules`. The scan is bounded and skips it; without\n * that this walks a gigabyte to find nothing.\n * - Treating a mirror failure as fatal. The worktree is good either way; only\n * the session's composition is poorer for it.\n */\nimport fs from 'node:fs';\nimport path from 'node:path';\n\n/** Directories that are build output worth copying into a new worktree. */\nconst OUTPUT_DIRECTORY = 'dist';\nconst MODULES_DIRECTORY = 'node_modules';\nconst GIT_DIRECTORY = '.git';\n/**\n * How deep the scan goes.\n *\n * Four levels reaches `layers/<group>/<package>/dist` in this repository's\n * layout, and a workspace that buries a package deeper than that gets a\n * partial mirror rather than a slow one.\n */\nconst MAX_DEPTH = 4;\n\nexport type MirrorOutcome =\n | { kind: 'skipped'; reason: 'no-modules' }\n | { kind: 'mirrored'; copied: number; linked: number };\n\ninterface Found {\n /** Paths relative to the source root. */\n outputs: string[];\n modules: string[];\n}\n\nfunction scan(root: string): Found {\n const found: Found = { outputs: [], modules: [] };\n const walk = (relative: string, depth: number): void => {\n let entries: fs.Dirent[];\n try {\n entries = fs.readdirSync(path.join(root, relative), { withFileTypes: true });\n } catch {\n return;\n }\n for (const entry of entries) {\n const child = relative === '' ? entry.name : `${relative}/${entry.name}`;\n if (entry.name === GIT_DIRECTORY) continue;\n // Module trees may already be links inherited from an earlier worktree.\n if (entry.name === MODULES_DIRECTORY) {\n try {\n if (entry.isDirectory() || (entry.isSymbolicLink() && fs.statSync(path.join(root, child)).isDirectory())) {\n found.modules.push(child);\n }\n } catch {\n // A broken inherited link is not useful to the next worktree.\n }\n continue;\n }\n if (!entry.isDirectory()) continue;\n if (entry.name === OUTPUT_DIRECTORY) {\n found.outputs.push(child);\n continue;\n }\n if (depth < MAX_DEPTH) walk(child, depth + 1);\n }\n };\n walk('', 1);\n return found;\n}\n\n/**\n * Copies the parent's build output into a worktree and links its modules.\n *\n * Individual failures are counted out rather than thrown: a mirror that lands\n * partly is still better than none, and the caller has a worktree either way.\n */\nexport function mirrorComposition(source: string, target: string): MirrorOutcome {\n if (!fs.existsSync(path.join(source, MODULES_DIRECTORY))) return { kind: 'skipped', reason: 'no-modules' };\n\n const found = scan(source);\n let copied = 0;\n let linked = 0;\n\n for (const relative of found.modules) {\n const destination = path.join(target, relative);\n if (fs.existsSync(destination)) continue;\n try {\n fs.mkdirSync(path.dirname(destination), { recursive: true });\n fs.symlinkSync(fs.realpathSync(path.join(source, relative)), destination, 'dir');\n linked += 1;\n } catch {\n // A link the filesystem refuses costs this package one resolution, not\n // the worktree.\n }\n }\n\n for (const relative of found.outputs) {\n const destination = path.join(target, relative);\n if (fs.existsSync(destination)) continue;\n try {\n fs.mkdirSync(path.dirname(destination), { recursive: true });\n fs.cpSync(path.join(source, relative), destination, { recursive: true });\n copied += 1;\n } catch {\n // Same reasoning as a failed link.\n }\n }\n\n return { kind: 'mirrored', copied, linked };\n}\n","/**\n * Expected failures, kept private to this package.\n *\n * `@agimon-ai/doompi-core` does not export `invalidRequest` or\n * an expected-error base, so this mirrors doompi-team's shape rather than\n * reaching across a layer boundary for it. The duplication is deliberate and\n * small; a shared helper would be the better answer only once a third package\n * needs one.\n */\n\nexport type DoomGitErrorCode =\n | 'invalid_request'\n | 'not_a_repository'\n | 'worktree_exists'\n | 'worktree_not_found'\n | 'worktree_not_owned'\n | 'worktree_peer_unavailable'\n | 'worktree_dirty'\n | 'worktree_cleanup_failed'\n | 'hub_unavailable'\n | 'spawn_cancelled'\n | 'registry_write_failed'\n | 'registry_busy'\n | 'task_delivery_failed'\n | 'message_delivery_failed'\n | 'message_too_large'\n | 'git_failed';\n\nexport class DoomGitExpectedError extends Error {\n constructor(\n readonly code: DoomGitErrorCode,\n message: string,\n readonly retryable: boolean,\n readonly recovery: string,\n ) {\n // The recovery line rides in the message because a tool result is read as\n // text: an error the model cannot act on costs a turn to rediscover.\n super(`[${code}] ${message}\\nRecovery: ${recovery}`);\n this.name = 'DoomGitExpectedError';\n }\n}\n\n/** Raised when a worktree action needs the canonical hub but it is not injected. */\nexport class HubUnavailableError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'HubUnavailableError';\n }\n}\n\nexport function invalidRequest(message: string, recovery: string): DoomGitExpectedError {\n return new DoomGitExpectedError('invalid_request', message, false, recovery);\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\n\nconst OWNER_ONLY_FILE = 0o600;\n\n/**\n * Writes JSON so a reader never sees a half-written file.\n *\n * The registry is read by the same process that writes it, and by a later\n * process after a crash. A plain write can be interrupted between the truncate\n * and the last byte, which leaves a file that parses as nothing and loses every\n * record in it. Writing beside the target and renaming makes the swap atomic on\n * the same filesystem, so a reader sees either the old file or the new one.\n */\nexport function writeJsonAtomic(file: string, value: unknown): void {\n const directory = path.dirname(file);\n fs.mkdirSync(directory, { recursive: true });\n const temporary = path.join(directory, `.${path.basename(file)}.${String(process.pid)}.tmp`);\n fs.writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\\n`, { mode: OWNER_ONLY_FILE });\n fs.renameSync(temporary, file);\n}\n\n/**\n * Reads JSON, treating every failure as absent.\n *\n * A missing file and an unparseable one mean the same thing to every caller\n * here: there is nothing to act on. Distinguishing them would only offer a\n * choice no caller wants to make.\n */\nexport function readJson<T>(file: string): T | undefined {\n try {\n return JSON.parse(fs.readFileSync(file, 'utf8')) as T;\n } catch {\n return undefined;\n }\n}\n","import { createHash } from 'node:crypto';\nimport fs from 'node:fs';\nimport path from 'node:path';\n\nconst ID_LENGTH = 12;\nconst MAX_LABEL_LENGTH = 32;\n\n/**\n * The git directory shared by a repository and all of its worktrees.\n *\n * A normal checkout has a `.git` directory. A linked worktree has a `.git`\n * FILE pointing at `<common>/worktrees/<name>`, whose `commondir` points back\n * at the shared directory. Following that chain is what makes every worktree of\n * one repository agree on the repository's identity, which matters here because\n * the registry is per repository and must be found from inside a worktree too.\n * A session may run in a subdirectory, so the nearest enclosing checkout is used.\n */\nexport function gitCommonDirectory(directory: string): string | undefined {\n let root = path.resolve(directory);\n let marker = path.join(root, '.git');\n let stat: fs.Stats | undefined;\n for (;;) {\n try {\n stat = fs.statSync(marker);\n break;\n } catch {\n const parent = path.dirname(root);\n if (parent === root) return undefined;\n root = parent;\n marker = path.join(root, '.git');\n }\n }\n if (stat.isDirectory()) return marker;\n let pointer: string;\n try {\n pointer = fs.readFileSync(marker, 'utf8').trim();\n } catch {\n return undefined;\n }\n if (!pointer.startsWith('gitdir:')) return undefined;\n const gitDir = path.resolve(root, pointer.slice('gitdir:'.length).trim());\n try {\n const common = fs.readFileSync(path.join(gitDir, 'commondir'), 'utf8').trim();\n return path.resolve(gitDir, common);\n } catch {\n // A submodule's .git file has no commondir; its gitdir is already common.\n return gitDir;\n }\n}\n\n/** A directory name that is recognisable to a person and safe on a filesystem. */\nexport function repositoryLabel(root: string): string {\n const label = path\n .basename(root)\n .toLowerCase()\n .replace(/[^a-z0-9]+/gu, '-')\n .replace(/^-+|-+$/gu, '')\n .slice(0, MAX_LABEL_LENGTH);\n return label === '' ? 'repository' : label;\n}\n\n/**\n * A stable id for the repository a directory belongs to.\n *\n * Keyed on the common git directory rather than the given path, so the main\n * checkout and every worktree of it resolve to one id and therefore to one\n * registry. Falling back to the path keeps a non-repository directory usable\n * instead of failing, though nothing here should reach that case.\n */\nexport function repositoryId(root: string): string {\n const common = gitCommonDirectory(root);\n const token = common === undefined ? `root:${path.resolve(root)}` : `git:${common}`;\n return createHash('sha256').update(token).digest('hex').slice(0, ID_LENGTH);\n}\n\n/** A short, collision-resistant suffix for one worktree directory. */\nexport function shortId(seed: string): string {\n return createHash('sha256').update(seed).digest('hex').slice(0, 8);\n}\n","import { randomUUID } from 'node:crypto';\nimport fs from 'node:fs';\nimport os from 'node:os';\nimport path from 'node:path';\nimport { isDeepStrictEqual } from 'node:util';\n\nimport { WORKTREE_RECORD_VERSION, type WorktreeRecord } from '../../types/worktreeRegistry';\nimport { readJson, writeJsonAtomic } from '../atomicJson';\nimport { repositoryId } from '../repositoryIdentity';\n\n/**\n * Where this package keeps its own state.\n *\n * Outside the repository on purpose. A worktree directory inside the checkout\n * would be found by every glob the repository's own tooling runs, and would\n * resolve workspace imports against the parent checkout rather than the\n * worktree, so a session there would build the wrong source while appearing to\n * work.\n */\nexport function doomGitRoot(homeDir: string = os.homedir()): string {\n return path.join(homeDir, '.pi', '.doom', 'git');\n}\n\n/** The directory every worktree for every repository is created under. */\nexport function worktreesRoot(homeDir: string = os.homedir()): string {\n return path.join(doomGitRoot(homeDir), 'worktrees');\n}\n\n/** One registry file per repository, keyed by the shared git directory. */\nexport function registryFile(repositoryRoot: string, homeDir: string = os.homedir()): string {\n const id = repositoryId(repositoryRoot);\n const root = path.join(doomGitRoot(homeDir), 'registry');\n const canonical = path.join(root, id, 'worktrees.json');\n if (fs.existsSync(canonical)) return canonical;\n let names: string[];\n try {\n names = fs.readdirSync(root);\n } catch {\n return canonical;\n }\n const legacyFiles = names\n .filter((entry) => entry.endsWith(`--${id}`))\n .sort()\n .map((entry) => path.join(root, entry, 'worktrees.json'))\n .filter((file) => fs.existsSync(file));\n if (legacyFiles.length === 0) return canonical;\n const entries = new Map<string, WorktreeRecord>();\n for (const file of legacyFiles) {\n const legacy = readJson<{ version?: unknown; entries?: unknown }>(file);\n if (legacy?.version !== WORKTREE_RECORD_VERSION || !Array.isArray(legacy.entries)) continue;\n for (const value of legacy.entries) {\n if (\n typeof value !== 'object' ||\n value === null ||\n !('id' in value) ||\n typeof value.id !== 'string' ||\n value.id === ''\n )\n continue;\n const entry = value as WorktreeRecord;\n const existing = entries.get(entry.id);\n if (existing !== undefined && !isDeepStrictEqual(existing, entry)) {\n throw new Error(`Conflicting legacy worktree registry entries for '${entry.id}'.`);\n }\n entries.set(entry.id, entry);\n }\n }\n const temporary = `${canonical}.${randomUUID()}.migration`;\n writeJsonAtomic(temporary, { version: WORKTREE_RECORD_VERSION, entries: [...entries.values()] });\n try {\n // Publish only if absent. A concurrent writer may already have added records.\n fs.linkSync(temporary, canonical);\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;\n } finally {\n fs.rmSync(temporary);\n }\n return canonical;\n}\n","import type { DoomDirectEventBus } from '@agimon-ai/doompi-core/hubChannel';\n\nexport const GIT_WORKTREE_LIFECYCLE_EVENT = 'git_worktree_lifecycle';\nexport const GIT_WORKTREE_MESSAGE_EVENT = 'git_worktree_message';\nexport const WORKTREE_MESSAGE_VERSION = 1 as const;\nexport const MAX_WORKTREE_MESSAGE_BYTES = 64 * 1024;\nexport const MAX_WORKTREE_INBOX_MESSAGES = 100;\n\nexport interface WorktreeLifecycleEvent {\n readonly version: typeof WORKTREE_MESSAGE_VERSION;\n readonly repositoryRoot: string;\n}\n\nexport type WorktreeMessageParty = 'parent' | 'child';\n\nexport interface WorktreeMessage {\n readonly version: typeof WORKTREE_MESSAGE_VERSION;\n readonly worktreeId: string;\n readonly fromSessionId: string;\n readonly from: WorktreeMessageParty;\n readonly text: string;\n readonly sentAt: string;\n}\n\nexport interface WorktreeMessageInbox {\n send(targetSessionId: string, message: WorktreeMessage): void;\n receive(worktreeId: string): WorktreeMessage[];\n close(): void;\n}\n\nfunction isMessageParty(value: unknown): value is WorktreeMessageParty {\n return value === 'parent' || value === 'child';\n}\n\nfunction parseMessage(value: unknown): WorktreeMessage | undefined {\n if (typeof value !== 'object' || value === null) return undefined;\n const record = value as Partial<WorktreeMessage>;\n if (record.version !== WORKTREE_MESSAGE_VERSION) return undefined;\n if (typeof record.worktreeId !== 'string' || record.worktreeId === '') return undefined;\n if (typeof record.fromSessionId !== 'string' || record.fromSessionId === '') return undefined;\n if (!isMessageParty(record.from)) return undefined;\n if (typeof record.text !== 'string' || record.text === '') return undefined;\n if (Buffer.byteLength(record.text, 'utf8') > MAX_WORKTREE_MESSAGE_BYTES) return undefined;\n if (typeof record.sentAt !== 'string' || record.sentAt === '') return undefined;\n return record as WorktreeMessage;\n}\n\nexport function createWorktreeMessageInbox(directEvents: DoomDirectEventBus, sessionId: string): WorktreeMessageInbox {\n if (sessionId === '') throw new Error('The Git message service requires a session identity.');\n const messages = new Map<string, WorktreeMessage[]>();\n const unsubscribe = directEvents.subscribe(GIT_WORKTREE_MESSAGE_EVENT, sessionId, (payload) => {\n const message = parseMessage(payload);\n if (message === undefined) return;\n const inbox = messages.get(message.worktreeId) ?? [];\n if (inbox.length >= MAX_WORKTREE_INBOX_MESSAGES) inbox.shift();\n inbox.push(message);\n messages.set(message.worktreeId, inbox);\n });\n let closed = false;\n return {\n send(targetSessionId, message) {\n if (closed) throw new Error('The Git message service is closed.');\n directEvents.publish(GIT_WORKTREE_MESSAGE_EVENT, targetSessionId, message);\n },\n receive(worktreeId) {\n if (closed) return [];\n const inbox = messages.get(worktreeId);\n if (inbox === undefined) return [];\n messages.delete(worktreeId);\n return inbox;\n },\n close() {\n if (closed) return;\n closed = true;\n unsubscribe();\n messages.clear();\n },\n };\n}\n\nexport function isWorktreeLifecycleEvent(value: unknown): value is WorktreeLifecycleEvent {\n if (typeof value !== 'object' || value === null) return false;\n const record = value as Partial<WorktreeLifecycleEvent>;\n return (\n record.version === WORKTREE_MESSAGE_VERSION &&\n typeof record.repositoryRoot === 'string' &&\n record.repositoryRoot !== ''\n );\n}\n","/**\n * Naming and reconciliation rules for worktrees, as pure functions.\n *\n * Everything here is a function of its arguments so the interesting decisions,\n * which are the ones about destroying work, can be tested without a repository\n * or a filesystem.\n */\n\nimport type { WorktreeRecord } from '../../types/worktreeRegistry';\n\nconst MAX_SLUG_LENGTH = 64;\n\n/**\n * A branch name reduced to one safe path segment.\n *\n * Slashes are the point: `wt/fix-auth` is a perfectly good branch and a\n * terrible directory name, because the nested directory it implies is not the\n * one the worktree lives in.\n */\nexport function branchSlug(branch: string): string {\n const slug = branch\n .toLowerCase()\n .replace(/[^a-z0-9]+/gu, '-')\n .replace(/^-+|-+$/gu, '')\n .slice(0, MAX_SLUG_LENGTH)\n .replace(/-+$/gu, '');\n return slug === '' ? 'worktree' : slug;\n}\n\n/**\n * The directory for one worktree.\n *\n * The short id disambiguates two branches whose slugs collide, such as\n * `feature/x` and `feature_x`, which would otherwise want the same directory.\n */\nexport function worktreeDirectory(input: {\n worktreesRoot: string;\n repositoryLabel: string;\n repositoryId: string;\n branch: string;\n shortId: string;\n}): string {\n const repository = `${input.repositoryLabel}--${input.repositoryId}`;\n return `${input.worktreesRoot}/${repository}/${branchSlug(input.branch)}--${input.shortId}`;\n}\n\n/** Why a worktree cannot be created, or undefined when it can. */\nexport function refuseSpawn(input: { branch: string; existing: readonly WorktreeRecord[] }): string | undefined {\n if (input.branch.trim() === '') return 'A branch name is required.';\n // Git refuses a second worktree on one branch, but its message names a path\n // the caller never chose, so this says it in the caller's own terms first.\n const live = input.existing.find((record) => record.branch === input.branch && record.status !== 'orphaned');\n if (live) return `Branch ${input.branch} already has a worktree (${live.id}).`;\n return undefined;\n}\n\n/** Why a worktree cannot be closed, or undefined when it can. */\nexport function refuseClose(input: {\n record: WorktreeRecord | undefined;\n dirtyFiles: readonly string[];\n force: boolean;\n}): string | undefined {\n if (!input.record) return 'No worktree with that id. Call list to see the current ids.';\n if (input.dirtyFiles.length === 0 || input.force) return undefined;\n // Naming the files is the point: \"the tree is dirty\" sends the reader to go\n // look, and the answer is usually a file they forgot rather than one they\n // meant to keep.\n const names = input.dirtyFiles.slice(0, 10).join(', ');\n const more = input.dirtyFiles.length > 10 ? `, and ${String(input.dirtyFiles.length - 10)} more` : '';\n return `${String(input.dirtyFiles.length)} uncommitted file(s): ${names}${more}. Pass force: true to discard them.`;\n}\n\nexport interface Reconciliation {\n records: WorktreeRecord[];\n /** Ids whose status changed, so a caller can report what it noticed. */\n changed: string[];\n}\n\n/**\n * Marks records whose process or directory is gone as orphaned.\n *\n * Nothing is deleted here. A doompi-git orphan owns a directory that may hold\n * uncommitted work, so losing the record would lose the only pointer to it.\n * Deletion is always a separate, named decision.\n */\nexport function reconcile(\n records: readonly WorktreeRecord[],\n probe: { alive: (record: WorktreeRecord) => boolean; exists: (path: string) => boolean },\n): Reconciliation {\n const changed: string[] = [];\n const next = records.map((record) => {\n if (record.status === 'orphaned') return record;\n const gone = !probe.exists(record.path) || !probe.alive(record);\n if (!gone) return record;\n changed.push(record.id);\n return { ...record, status: 'orphaned' as const };\n });\n return { records: next, changed };\n}\n\nexport interface PrunePlan {\n /** Orphans whose directory git still lists, safe to remove. */\n remove: WorktreeRecord[];\n /** Orphans whose directory is already gone; drop the record only. */\n forget: WorktreeRecord[];\n /** Paths git lists under our root that we have no record of; report, never touch. */\n untracked: string[];\n /** Orphans left alone because they still hold uncommitted work. */\n keptDirty: WorktreeRecord[];\n /**\n * Branches git refused to delete because they hold commits.\n *\n * Filled by the prune itself, not by the plan: a dry run destroys nothing, so\n * it has nothing to report here and leaves it empty.\n */\n keptBranches: string[];\n}\n\n/**\n * What a prune would do, decided before anything is destroyed.\n *\n * Separating the plan from the execution is what makes dryRun honest and lets\n * the destructive half be reviewed on its own.\n */\nexport function planPrune(input: {\n records: readonly WorktreeRecord[];\n gitPaths: readonly string[];\n worktreesRoot: string;\n dirtyByPath: ReadonlyMap<string, readonly string[]>;\n}): PrunePlan {\n const plan: PrunePlan = { remove: [], forget: [], untracked: [], keptDirty: [], keptBranches: [] };\n const known = new Set(input.records.map((record) => record.path));\n const listed = new Set(input.gitPaths);\n for (const record of input.records) {\n if (record.status !== 'orphaned') continue;\n if (!listed.has(record.path)) {\n plan.forget.push(record);\n continue;\n }\n if ((input.dirtyByPath.get(record.path) ?? []).length > 0) {\n plan.keptDirty.push(record);\n continue;\n }\n plan.remove.push(record);\n }\n for (const path of input.gitPaths) {\n // Only ever inside our own root: a worktree the user made by hand is none\n // of this package's business, even when it looks abandoned.\n if (path.startsWith(`${input.worktreesRoot}/`) && !known.has(path)) plan.untracked.push(path);\n }\n return plan;\n}\n","import fs from 'node:fs';\nimport path from 'node:path';\nimport { setTimeout } from 'node:timers/promises';\n\nimport {\n WORKTREE_RECORD_VERSION,\n type WorktreeRecord,\n type WorktreeRegistryFile,\n type WorktreeRegistryStore,\n} from '../../types/worktreeRegistry';\nimport { readJson, writeJsonAtomic } from '../atomicJson';\nimport { DoomGitExpectedError } from '../errors';\n\nconst LOCK_WAIT_MS = 5000;\nconst LOCK_RETRY_MS = 25;\n\nfunction isRecord(value: unknown): value is WorktreeRecord {\n if (typeof value !== 'object' || value === null) return false;\n const record = value as Partial<WorktreeRecord>;\n return (\n record.version === WORKTREE_RECORD_VERSION &&\n typeof record.id === 'string' &&\n record.id !== '' &&\n typeof record.path === 'string' &&\n typeof record.branch === 'string' &&\n typeof record.sessionId === 'string'\n );\n}\n\n/**\n * Repository mutations hold an exclusive directory lock from the read through\n * the final commit. This works across independently loaded packages and hosts.\n * A crashed owner leaves a visible lock, never a lease another writer may steal.\n */\nexport function createWorktreeRegistry(file: string): WorktreeRegistryStore {\n return {\n list() {\n const parsed = readJson<WorktreeRegistryFile>(file);\n if (!parsed || parsed.version !== WORKTREE_RECORD_VERSION || !Array.isArray(parsed.entries)) return [];\n return parsed.entries.filter(isRecord);\n },\n replace(entries) {\n writeJsonAtomic(file, { version: WORKTREE_RECORD_VERSION, entries } satisfies WorktreeRegistryFile);\n },\n async transaction(operation, signal) {\n const lock = `${file}.lock`;\n fs.mkdirSync(path.dirname(file), { recursive: true });\n const deadline = Date.now() + LOCK_WAIT_MS;\n for (;;) {\n signal?.throwIfAborted();\n try {\n fs.mkdirSync(lock, { mode: 0o700 });\n break;\n } catch (error) {\n if ((error as NodeJS.ErrnoException).code !== 'EEXIST') throw error;\n if (Date.now() >= deadline)\n throw new DoomGitExpectedError(\n 'registry_busy',\n 'Another worktree operation holds the repository registry.',\n true,\n 'Retry after it finishes. After a host crash, inspect and remove the abandoned worktrees.json.lock directory.',\n );\n await setTimeout(LOCK_RETRY_MS, undefined, { signal });\n }\n }\n try {\n fs.writeFileSync(\n path.join(lock, 'owner.json'),\n JSON.stringify({ pid: process.pid, createdAt: new Date().toISOString() }),\n { mode: 0o600 },\n );\n signal?.throwIfAborted();\n return await operation();\n } finally {\n fs.rmSync(lock, { recursive: true });\n }\n },\n };\n}\n","import { randomUUID } from 'node:crypto';\nimport fs from 'node:fs';\n\nimport type { DoomHubSessionService } from '@agimon-ai/doompi-core/hubChannel';\nimport { removeSyncLocation, resolveWorktreeSyncLocation } from '@agimon-ai/doompi-core/syncLocation';\nimport type { DoomSessionDeliveryService } from '@agimon-ai/doompi-session';\n\nimport { WORKTREE_RECORD_VERSION } from '../../types/worktreeRegistry';\nimport type { WorktreeGit, WorktreeRecord } from '../../types/worktreeRegistry';\nimport { mirrorComposition } from '../compositionMirror';\nimport { DoomGitExpectedError, HubUnavailableError } from '../errors';\nimport { registryFile, worktreesRoot } from '../paths';\nimport { repositoryId, repositoryLabel, shortId } from '../repositoryIdentity';\nimport { MAX_WORKTREE_MESSAGE_BYTES, type WorktreeMessageInbox, type WorktreeMessageParty } from '../worktreeEvents';\nimport { planPrune, reconcile, refuseClose, refuseSpawn, worktreeDirectory } from '../worktreeNaming';\nimport { createWorktreeRegistry } from '../worktreeRegistry';\n\nconst GIT_WORKTREE_DELEGATION_KIND = 'git.worktree.delegation';\nconst GIT_WORKTREE_MESSAGE_KIND = 'git.worktree.message';\nconst GIT_WORKTREE_REPORT_KIND = 'git.worktree.report';\nexport interface WorktreeContext {\n /** Where the calling session is working, used to find the repository. */\n cwd: string;\n /** The calling session, recorded as the new session's parent. */\n sessionId: string;\n}\n\nexport interface SpawnWorktreeRequest {\n branch: string;\n baseRef?: string;\n name?: string;\n task?: string;\n /** Host-owned execution setup, exposed by the UI rather than the agent tool. */\n reservationId?: string;\n}\n\nexport interface SpawnOptions {\n /** Aborted when the caller gives up, so a spawn nobody is waiting for stops. */\n signal?: AbortSignal;\n /** Phase labels, for a caller that can show them while the work runs. */\n onProgress?: (label: string) => void;\n}\n\nexport interface WorktreeOperations {\n spawn(context: WorktreeContext, request: SpawnWorktreeRequest, options?: SpawnOptions): Promise<WorktreeRecord>;\n close(context: WorktreeContext, id: string, force: boolean): Promise<WorktreeRecord>;\n list(context: WorktreeContext): Promise<WorktreeRecord[]>;\n status(context: WorktreeContext, id: string): Promise<{ record: WorktreeRecord; dirtyFiles: string[] }>;\n merge(context: WorktreeContext, id: string, message?: string): Promise<WorktreeRecord>;\n prune(context: WorktreeContext, dryRun: boolean): Promise<ReturnType<typeof planPrune>>;\n send(context: WorktreeContext, id: string, message: string): Promise<void>;\n messages(context: WorktreeContext, id: string): Promise<import('../worktreeEvents').WorktreeMessage[]>;\n}\n\nexport interface WorktreeOperationsDeps {\n git: WorktreeGit;\n /** The canonical hub-owned session lifecycle. */\n sessionService?: DoomHubSessionService;\n /** Legacy in-memory fallback used outside the fixed Session composition. */\n messageInbox?: WorktreeMessageInbox;\n /** Reads the active Session provider when an operation executes. */\n sessionDelivery?: () => DoomSessionDeliveryService | undefined;\n /** Injected so a test never copies a real dependency tree. */\n mirror?: typeof mirrorComposition;\n /** Injected so tests can observe generated-storage cleanup without touching real sync state. */\n cleanupStorage?: (repositoryRoot: string, worktreeRoot: string) => void;\n homeDir?: string;\n /** Injected so a record's createdAt is reproducible in a test. */\n now?: () => Date;\n}\n\nexport function createWorktreeOperations(deps: WorktreeOperationsDeps): WorktreeOperations {\n const { git } = deps;\n const sessionService = deps.sessionService;\n const mirror = deps.mirror ?? mirrorComposition;\n const removeStorage =\n deps.cleanupStorage ??\n ((repositoryRoot: string, worktreeRoot: string): void => {\n removeSyncLocation(resolveWorktreeSyncLocation(repositoryRoot, worktreeRoot, deps.homeDir));\n });\n const cleanupStorage = (repositoryRoot: string, worktreeRoot: string): void => {\n try {\n removeStorage(repositoryRoot, worktreeRoot);\n } catch (error) {\n throw new DoomGitExpectedError(\n 'worktree_cleanup_failed',\n `The worktree checkout was removed, but its generated storage could not be deleted (${error instanceof Error ? error.message : String(error)}).`,\n true,\n 'Retry close or prune after fixing the storage permissions.',\n );\n }\n };\n const now = deps.now ?? (() => new Date());\n const requireSessionService = (): DoomHubSessionService => {\n if (sessionService === undefined)\n throw new HubUnavailableError('The cockpit session service is unavailable. Start the cockpit and try again.');\n return sessionService;\n };\n const requireMessageInbox = (): WorktreeMessageInbox => {\n if (deps.messageInbox === undefined)\n throw new HubUnavailableError('The Git message service is unavailable. Start the cockpit and try again.');\n return deps.messageInbox;\n };\n const readSessionDelivery = (): DoomSessionDeliveryService | undefined => deps.sessionDelivery?.();\n const requireSessionDelivery = (): DoomSessionDeliveryService => {\n const delivery = readSessionDelivery();\n if (delivery === undefined)\n throw new HubUnavailableError('The Session delivery service is unavailable. Start the cockpit and try again.');\n return delivery;\n };\n const probe = {\n alive: (record: WorktreeRecord) => requireSessionService().isLive(record.sessionId),\n exists: (path: string) => fs.existsSync(path),\n };\n\n /**\n * Everything an action needs about the repository it is acting on.\n *\n * Resolved per call rather than cached: a session's cwd is stable, but the\n * worktree registry is shared and another process may have written it since\n * the last call.\n */\n const resolve = async (context: WorktreeContext) => {\n const root = await git.repositoryRoot(context.cwd);\n if (root === undefined) {\n throw new DoomGitExpectedError(\n 'not_a_repository',\n `${context.cwd} is not inside a git repository.`,\n false,\n 'Run this from a repository checkout.',\n );\n }\n const store = createWorktreeRegistry(registryFile(root, deps.homeDir));\n return { root, store, records: store.list() };\n };\n\n const require = (records: readonly WorktreeRecord[], id: string): WorktreeRecord => {\n const record = records.find((entry) => entry.id === id);\n if (record === undefined) {\n throw new DoomGitExpectedError(\n 'worktree_not_found',\n `No worktree with id ${id}.`,\n false,\n 'Call list to see the current ids.',\n );\n }\n return record;\n };\n\n /**\n * Refuses to destroy or merge a worktree another live session is using.\n *\n * `parentSessionId` records who asked for the worktree. Two sessions in one\n * checkout share this registry, so without the check either could close the\n * other's work in progress by id.\n *\n * Once that session is gone there is no owner left to protect, and refusing\n * would strand the worktree: nothing else could ever close it.\n */\n const requireOwner = (context: WorktreeContext, record: WorktreeRecord, action: string): void => {\n if (record.parentSessionId === context.sessionId) return;\n if (!requireSessionService().isLive(record.parentSessionId)) return;\n throw new DoomGitExpectedError(\n 'worktree_not_owned',\n `Worktree ${record.id} belongs to another session.`,\n false,\n `Ask the session that created it to ${action} it, or close that session first.`,\n );\n };\n\n const peerFor = (\n context: WorktreeContext,\n record: WorktreeRecord,\n ): { sessionId: string; party: WorktreeMessageParty } => {\n if (context.sessionId === record.parentSessionId) return { sessionId: record.sessionId, party: 'parent' };\n if (context.sessionId === record.sessionId) return { sessionId: record.parentSessionId, party: 'child' };\n throw new DoomGitExpectedError(\n 'worktree_not_owned',\n `Worktree ${record.id} belongs to another session.`,\n false,\n 'Use a worktree owned by this session.',\n );\n };\n\n const requirePeerLive = (peerSessionId: string): void => {\n if (!requireSessionService().isLive(peerSessionId)) {\n throw new DoomGitExpectedError(\n 'worktree_peer_unavailable',\n 'The other worktree session is no longer live.',\n false,\n 'Start a new worktree session before sending a message.',\n );\n }\n };\n\n const operations: WorktreeOperations = {\n async spawn(context, request, options) {\n const { root, store, records } = await resolve(context);\n const reservations = request.reservationId === undefined ? undefined : requireSessionService().reservations;\n if (request.reservationId !== undefined && (!reservations || request.task !== undefined))\n throw new DoomGitExpectedError(\n 'invalid_request',\n 'Reserved worktree sessions require host setup and cannot start a task.',\n false,\n 'Create the session from its pending setup card.',\n );\n const reserved =\n request.reservationId === undefined ? undefined : reservations!.read(request.reservationId, context.sessionId);\n const existing =\n reserved === undefined ? undefined : records.find((record) => record.sessionId === reserved.sessionId);\n if (existing) {\n const expectedPath = worktreeDirectory({\n worktreesRoot: worktreesRoot(deps.homeDir),\n repositoryLabel: repositoryLabel(root),\n repositoryId: repositoryId(root),\n branch: request.branch,\n shortId: shortId(reserved!.sessionId),\n });\n if (\n existing.parentSessionId !== context.sessionId ||\n existing.repositoryRoot !== root ||\n existing.branch !== request.branch ||\n existing.path !== expectedPath ||\n existing.path !== reserved?.cwd ||\n (request.baseRef !== undefined && existing.baseRef !== request.baseRef) ||\n !fs.existsSync(existing.path) ||\n !(await git.listWorktreePaths(root)).includes(existing.path) ||\n (await git.repositoryRoot(existing.path)) !== existing.path ||\n (await git.currentBranch(existing.path)) !== request.branch\n )\n throw new DoomGitExpectedError(\n 'invalid_request',\n 'The reserved checkout no longer matches this setup.',\n false,\n 'Inspect the existing branch and checkout; they will not be replaced.',\n );\n if (!requireSessionService().isLive(existing.sessionId)) {\n const session = await requireSessionService().create({\n cwd: existing.path,\n name: request.name ?? request.branch,\n parentSessionId: context.sessionId,\n sessionProvenance: 'worktree',\n reservationId: request.reservationId,\n ...(options?.signal === undefined ? {} : { signal: options.signal }),\n });\n if (session.sessionId !== existing.sessionId)\n throw new Error('The host did not use the reserved session identity.');\n }\n await reservations!.complete(request.reservationId!, context.sessionId);\n return existing;\n }\n const refusal = refuseSpawn({ branch: request.branch, existing: records });\n if (refusal !== undefined) {\n throw new DoomGitExpectedError('worktree_exists', refusal, false, 'Pick another branch name, or close it.');\n }\n const taskDelivery = request.task === undefined ? undefined : requireSessionDelivery();\n const baseRef = request.baseRef ?? (await git.remoteBaseRef(root));\n if (baseRef === undefined) {\n throw new DoomGitExpectedError(\n 'git_failed',\n 'No remote base branch is available for this repository.',\n false,\n 'Fetch a remote branch or pass an explicit baseRef.',\n );\n }\n const id = shortId(reserved?.sessionId ?? randomUUID());\n let path = worktreeDirectory({\n worktreesRoot: worktreesRoot(deps.homeDir),\n repositoryLabel: repositoryLabel(root),\n repositoryId: repositoryId(root),\n branch: request.branch,\n shortId: id,\n });\n if (request.reservationId !== undefined) {\n path = (await reservations!.prepare(request.reservationId, context.sessionId, path)).cwd;\n }\n const recovering = reserved?.cwd !== undefined && fs.existsSync(path);\n if (\n recovering &&\n (!(await git.listWorktreePaths(root)).includes(path) || (await git.currentBranch(path)) !== request.branch)\n )\n throw new DoomGitExpectedError(\n 'invalid_request',\n 'The reserved checkout no longer matches this setup.',\n false,\n 'Inspect the existing directory; it will not be replaced.',\n );\n // The worktree and the branch are made by one command, so they are undone\n // by one too. `worktree remove` leaves the branch behind, and a branch\n // nobody asked for is what a failed spawn used to leave on the floor.\n const cancelled = (): boolean => options?.signal?.aborted === true;\n const rollback = async (): Promise<string> => {\n if (reserved) return ` The reserved checkout at ${path} was retained for recovery.`;\n await git.removeWorktree({ repositoryRoot: root, path, force: true }).catch(() => undefined);\n const deleted = await git.deleteBranch({ repositoryRoot: root, branch: request.branch }).catch(() => false);\n return deleted ? '' : ` The branch ${request.branch} has work on it and was kept.`;\n };\n\n options?.onProgress?.(`creating branch ${request.branch}\\u2026`);\n if (!recovering) await git.addWorktree({ repositoryRoot: root, path, branch: request.branch, baseRef });\n\n // Checked here rather than only inside the session call: this is the\n // point where a checkout exists that nothing has recorded yet, which is\n // exactly the state an interrupted spawn used to leave behind.\n if (cancelled()) {\n const kept = await rollback();\n throw new DoomGitExpectedError(\n 'spawn_cancelled',\n `Cancelled before the session started.${kept}`,\n false,\n reserved\n ? 'Retry setup with the same branch to recover the reserved checkout.'\n : 'The worktree was removed; run it again when you are ready.',\n );\n }\n\n // Before the session, because the session composes the extensions this\n // repository's config names and those live in build output git does not\n // track. A worktree without it starts and dies on the first package it\n // cannot resolve. Nothing here can fail the spawn: a repository with no\n // build output to mirror simply has none.\n options?.onProgress?.('mirroring build output\\u2026');\n if (!recovering) mirror(root, path);\n\n // The worktree exists from here on, so every later failure has to leave\n // it removed or recorded. An unrecorded directory on disk is the one\n // outcome with no way back: nothing would ever list it again.\n options?.onProgress?.('starting session\\u2026');\n let sessionId: string;\n try {\n const session = await requireSessionService().create({\n cwd: path,\n name: request.name ?? request.branch,\n parentSessionId: context.sessionId,\n sessionProvenance: 'worktree',\n ...(options?.signal === undefined ? {} : { signal: options.signal }),\n ...(request.reservationId === undefined ? {} : { reservationId: request.reservationId }),\n });\n sessionId = session.sessionId;\n if (reserved && sessionId !== reserved.sessionId)\n throw new Error('The host did not use the reserved session identity.');\n } catch (error) {\n const kept = await rollback();\n if (cancelled()) {\n throw new DoomGitExpectedError(\n 'spawn_cancelled',\n `Cancelled while the session was starting.${kept}`,\n false,\n reserved\n ? 'Retry setup with the same branch to recover the reserved checkout.'\n : 'The worktree was removed; run it again when you are ready.',\n );\n }\n if (error instanceof HubUnavailableError) {\n throw new DoomGitExpectedError(\n 'hub_unavailable',\n `${error.message}${kept}`,\n true,\n 'Start the cockpit and try again.',\n );\n }\n throw error;\n }\n\n const record: WorktreeRecord = {\n version: WORKTREE_RECORD_VERSION,\n id,\n branch: request.branch,\n baseRef,\n path,\n repositoryRoot: root,\n sessionId,\n parentSessionId: context.sessionId,\n status: 'running',\n createdAt: now().toISOString(),\n };\n try {\n store.replace([...records, record]);\n } catch (error) {\n // A live session with no record is the worst outcome available here:\n // it runs, and no id anything can name points at it. The session goes\n // back too, rather than being left for someone to find by hand.\n await requireSessionService()\n .close(sessionId)\n .catch(() => undefined);\n const kept = await rollback();\n throw new DoomGitExpectedError(\n 'registry_write_failed',\n `The worktree was created but the registry could not be written (${(error as Error).message}).${kept}`,\n true,\n 'Check the registry file for permissions or free space, then try again.',\n );\n }\n if (request.task !== undefined && taskDelivery !== undefined) {\n try {\n const receipt = await taskDelivery.deliver({\n recipientKey: sessionId,\n kind: GIT_WORKTREE_DELEGATION_KIND,\n prompt: request.task,\n metadata: {\n worktreeId: record.id,\n fromRole: 'parent',\n text: request.task,\n sentAt: record.createdAt,\n },\n });\n const admission = await taskDelivery.waitForAdmission(receipt.deliveryId);\n if (admission !== 'admitted') {\n throw new Error(\n admission === 'recovery_required'\n ? 'The recipient could not admit the task.'\n : 'The recipient did not confirm task admission before the timeout.',\n );\n }\n } catch (error) {\n throw new DoomGitExpectedError(\n 'task_delivery_failed',\n `Worktree ${record.id} was created, but its task admission was not confirmed (${(error as Error).message}).`,\n true,\n `Use send with worktree ${record.id} to deliver the task without recreating the worktree.`,\n );\n }\n }\n if (request.reservationId !== undefined) await reservations!.complete(request.reservationId, context.sessionId);\n return record;\n },\n\n async close(context, id, force) {\n const { root, store, records } = await resolve(context);\n const record = require(records, id);\n requireOwner(context, record, 'close');\n const dirtyFiles = fs.existsSync(record.path) ? await git.dirtyFiles(record.path) : [];\n const refusal = refuseClose({ record, dirtyFiles, force });\n if (refusal !== undefined) {\n throw new DoomGitExpectedError('worktree_dirty', refusal, false, 'Commit the work, or pass force: true.');\n }\n // The session is stopped before the directory goes. Removing a checkout\n // while a process still holds files open in it is how a half-removed\n // worktree and a stale git administrative entry are made.\n await requireSessionService().close(record.sessionId);\n await git.removeWorktree({ repositoryRoot: root, path: record.path, force });\n cleanupStorage(root, record.path);\n store.replace(records.filter((entry) => entry.id !== id));\n return record;\n },\n\n async list(context) {\n const { store, records } = await resolve(context);\n const outcome = reconcile(records, probe);\n if (outcome.changed.length > 0) store.replace(outcome.records);\n return outcome.records;\n },\n\n async status(context, id) {\n const { records } = await resolve(context);\n const record = require(records, id);\n const dirtyFiles = fs.existsSync(record.path) ? await git.dirtyFiles(record.path) : [];\n return { record, dirtyFiles };\n },\n\n async merge(context, id, message) {\n const { root, records } = await resolve(context);\n const record = require(records, id);\n requireOwner(context, record, 'merge');\n // The merge lands in the parent, so the parent is what must be clean. A\n // dirty parent would mix the person's in-flight edits into a merge commit\n // they did not intend to make.\n const parentDirty = await git.dirtyFiles(root);\n if (parentDirty.length > 0) {\n throw new DoomGitExpectedError(\n 'worktree_dirty',\n `The parent checkout has ${String(parentDirty.length)} uncommitted file(s): ${parentDirty.slice(0, 10).join(', ')}.`,\n false,\n 'Commit or stash the parent checkout first.',\n );\n }\n await git.mergeBranch({\n repositoryRoot: root,\n branch: record.branch,\n ...(message === undefined ? {} : { message }),\n });\n return record;\n },\n\n /** Sends only between the parent session and its own worktree session. */\n async send(context, id, message) {\n if (Buffer.byteLength(message, 'utf8') > MAX_WORKTREE_MESSAGE_BYTES) {\n throw new DoomGitExpectedError(\n 'message_too_large',\n `A message may not exceed ${String(MAX_WORKTREE_MESSAGE_BYTES)} bytes.`,\n false,\n 'Send a shorter message.',\n );\n }\n const { records } = await resolve(context);\n const record = require(records, id);\n const peer = peerFor(context, record);\n requirePeerLive(peer.sessionId);\n const sentAt = new Date().toISOString();\n const delivery = readSessionDelivery();\n if (delivery !== undefined) {\n const kind = peer.party === 'parent' ? GIT_WORKTREE_MESSAGE_KIND : GIT_WORKTREE_REPORT_KIND;\n const receipt = await delivery.deliver({\n recipientKey: peer.sessionId,\n kind,\n prompt:\n peer.party === 'parent'\n ? `Parent message for worktree ${record.id}:\\n\\n${message}`\n : `Report from worktree ${record.id}:\\n\\n${message}`,\n metadata: { worktreeId: record.id, fromRole: peer.party, text: message, sentAt },\n });\n const admission = await delivery.waitForAdmission(receipt.deliveryId);\n if (admission !== 'admitted') {\n throw new DoomGitExpectedError(\n 'message_delivery_failed',\n `Worktree ${record.id} persisted the message, but its session did not confirm admission.`,\n true,\n admission === 'recovery_required'\n ? 'Call messages from the recipient session to recover the persisted text.'\n : 'The message remains durable; retry after the recipient is ready.',\n );\n }\n return;\n }\n requireMessageInbox().send(peer.sessionId, {\n version: 1,\n worktreeId: record.id,\n fromSessionId: context.sessionId,\n from: peer.party,\n text: message,\n sentAt,\n });\n },\n\n async messages(context, id) {\n const { records } = await resolve(context);\n const record = require(records, id);\n const peer = peerFor(context, record);\n const delivery = readSessionDelivery();\n if (delivery === undefined) return requireMessageInbox().receive(record.id);\n const entries = delivery\n .inbox({ consumed: false, metadata: { worktreeId: record.id } })\n .filter(\n (entry) =>\n entry.senderKey === peer.sessionId &&\n (entry.state === 'admitted' || entry.state === 'recovery_required') &&\n (peer.party === 'parent'\n ? entry.kind === GIT_WORKTREE_REPORT_KIND\n : entry.kind === GIT_WORKTREE_MESSAGE_KIND || entry.kind === GIT_WORKTREE_DELEGATION_KIND),\n );\n return entries.map((entry) => {\n delivery.consume(entry.deliveryId);\n return {\n version: 1,\n worktreeId: record.id,\n fromSessionId: entry.senderKey,\n from: peer.party === 'parent' ? ('child' as const) : ('parent' as const),\n text:\n entry.state === 'recovery_required'\n ? `[Recovery required: prompt admission was not confirmed] ${entry.metadata.text ?? entry.prompt}`\n : (entry.metadata.text ?? entry.prompt),\n sentAt: entry.metadata.sentAt ?? '',\n };\n });\n },\n\n async prune(context, dryRun) {\n const { root, store, records } = await resolve(context);\n const reconciled = reconcile(records, probe);\n const gitPaths = await git.listWorktreePaths(root);\n const dirtyByPath = new Map<string, readonly string[]>();\n for (const record of reconciled.records) {\n if (record.status === 'orphaned' && fs.existsSync(record.path)) {\n dirtyByPath.set(record.path, await git.dirtyFiles(record.path));\n }\n }\n const plan = planPrune({\n records: reconciled.records,\n gitPaths,\n worktreesRoot: worktreesRoot(deps.homeDir),\n dirtyByPath,\n });\n if (dryRun) {\n store.replace(reconciled.records);\n return plan;\n }\n for (const record of plan.remove) {\n await git.removeWorktree({ repositoryRoot: root, path: record.path, force: false });\n cleanupStorage(root, record.path);\n }\n for (const record of plan.forget) cleanupStorage(root, record.path);\n // The checkout goes, so the branch it was made for goes with it. Safe\n // delete only: git keeps anything holding commits, and the plan says\n // which ones it kept rather than leaving the reader to notice later.\n for (const record of [...plan.remove, ...plan.forget]) {\n const deleted = await git.deleteBranch({ repositoryRoot: root, branch: record.branch }).catch(() => false);\n if (!deleted) plan.keptBranches.push(record.branch);\n }\n const dropped = new Set([...plan.remove, ...plan.forget].map((record) => record.id));\n store.replace(reconciled.records.filter((record) => !dropped.has(record.id)));\n return plan;\n },\n };\n const locked = async <T>(context: WorktreeContext, operation: () => Promise<T>, signal?: AbortSignal): Promise<T> => {\n try {\n const { store } = await resolve(context);\n return await store.transaction(operation, signal);\n } catch (error) {\n if (signal?.aborted && !(error instanceof DoomGitExpectedError))\n throw new DoomGitExpectedError(\n 'spawn_cancelled',\n 'The worktree operation was cancelled.',\n false,\n 'Inspect its recorded state before retrying.',\n );\n if (['ENOTDIR', 'EACCES', 'EROFS', 'ENOSPC'].includes(String((error as NodeJS.ErrnoException).code)))\n throw new DoomGitExpectedError(\n 'registry_write_failed',\n 'The worktree registry is not writable.',\n true,\n 'Check permissions and free space before retrying.',\n );\n throw error;\n }\n };\n // ponytail: serialize Git lifecycle mutations, not session execution. Keeping\n // the lock across startup avoids a second lease system for partially created trees.\n return {\n ...operations,\n spawn: (context, request, options) => {\n if (sessionService === undefined)\n return Promise.reject(\n new DoomGitExpectedError(\n 'hub_unavailable',\n 'The cockpit session service is unavailable.',\n true,\n 'Use the cockpit worktree tool after its Git server facet is available.',\n ),\n );\n return locked(context, () => operations.spawn(context, request, options), options?.signal);\n },\n close: (context, id, force) => locked(context, () => operations.close(context, id, force)),\n list: (context) => locked(context, () => operations.list(context)),\n merge: (context, id, message) => locked(context, () => operations.merge(context, id, message)),\n prune: (context, dryRun) => locked(context, () => operations.prune(context, dryRun)),\n };\n}\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,9 +1,11 @@
1
1
  {
2
2
  "name": "@agimon-ai/doompi-git",
3
- "version": "0.0.1-alpha.3",
3
+ "version": "0.0.1-alpha.30",
4
4
  "description": "Git worktree sessions for DoomPi: spawn an isolated worktree with its own session and manage it from the parent",
5
5
  "keywords": [
6
+ "ai",
6
7
  "coding-agent",
8
+ "developer-tools",
7
9
  "doompi",
8
10
  "git-worktree",
9
11
  "pi-coding-agent",
@@ -23,19 +25,16 @@
23
25
  "directory": "layers/source-control/doompi-git"
24
26
  },
25
27
  "files": [
26
- "LICENSE",
27
- "README.md",
28
28
  "dist",
29
- "llms.txt",
30
- "package.json",
31
- "src/exports/webClient.ts",
32
29
  "src/prompts",
33
- "src/types/webWorktrees.ts",
34
- "src/types/worktreeRegistry.ts",
35
- "src/web"
30
+ "llms.txt",
31
+ "README.md",
32
+ "LICENSE",
33
+ "package.json"
36
34
  ],
37
35
  "type": "module",
38
36
  "main": "./dist/index.cjs",
37
+ "module": "./dist/index.mjs",
39
38
  "types": "./dist/index.d.mts",
40
39
  "jsnext:main": "./dist/index.mjs",
41
40
  "exports": {
@@ -44,45 +43,55 @@
44
43
  "import": "./dist/index.mjs",
45
44
  "require": "./dist/index.cjs"
46
45
  },
47
- "./api/git": {
48
- "types": "./dist/hubApi.d.mts",
49
- "import": "./dist/hubApi.mjs",
50
- "require": "./dist/hubApi.cjs"
46
+ "./apiContracts": {
47
+ "types": "./dist/apiContracts.d.mts",
48
+ "import": "./dist/apiContracts.mjs",
49
+ "require": "./dist/apiContracts.cjs"
51
50
  },
52
51
  "./extensions/pi": {
53
52
  "types": "./dist/extensions/pi.d.mts",
54
53
  "import": "./dist/extensions/pi.mjs",
55
54
  "require": "./dist/extensions/pi.cjs"
56
55
  },
56
+ "./extensions/server": {
57
+ "types": "./dist/extensions/server.d.mts",
58
+ "import": "./dist/extensions/server.mjs",
59
+ "require": "./dist/extensions/server.cjs"
60
+ },
61
+ "./extensions/web": {
62
+ "import": "./dist/extensions/web.mjs"
63
+ },
57
64
  "./package.json": "./package.json"
58
65
  },
59
66
  "publishConfig": {
60
67
  "access": "public"
61
68
  },
62
69
  "dependencies": {
70
+ "@agimon-ai/doompi-core": "0.0.1-alpha.94",
71
+ "@agimon-ai/doompi-session": "0.0.1-alpha.18",
72
+ "@agimon-ai/doompi-web-components": "0.0.1-alpha.52",
63
73
  "@deepseek-ai/cordis": "4.0.2",
64
74
  "@tanstack/react-store": "0.11.1",
65
75
  "@tanstack/store": "0.11.1",
66
- "typebox": "1.3.19",
67
- "@agimon-ai/doompi-extension-contracts": "0.0.1-alpha.66",
68
- "@agimon-ai/doompi-web-components": "0.0.1-alpha.26",
69
- "@agimon-ai/doompi-web-contracts": "0.0.1-alpha.29"
76
+ "typebox": "1.3.34"
70
77
  },
71
78
  "devDependencies": {
72
- "@earendil-works/pi-agent-core": "0.85.1",
73
- "@earendil-works/pi-coding-agent": "0.85.1",
74
- "@types/node": "26.2.0",
75
- "@types/react": "19.2.18",
76
- "@vitest/coverage-v8": "5.0.0",
77
- "react": "19.2.8",
78
- "tsdown": "0.22.14",
79
+ "@agimon-ai/doompi-build": "0.0.1-alpha.17",
80
+ "@agimon-ai/vibe-lint-plugin-doom-extension": "0.0.1-alpha.83",
81
+ "@earendil-works/pi-agent-core": "0.87.1",
82
+ "@earendil-works/pi-coding-agent": "0.87.1",
83
+ "@types/node": "26.6.1",
84
+ "@types/react": "19.3.0",
85
+ "@vitest/coverage-v8": "5.0.1",
86
+ "react": "19.3.0",
87
+ "react-dom": "19.3.0",
88
+ "tsdown": "0.23.0",
79
89
  "typescript": "7.0.2",
80
- "vitest": "5.0.0",
81
- "@agimon-ai/vibe-lint-plugin-doom-extension": "0.0.1-alpha.63"
90
+ "vitest": "5.0.1"
82
91
  },
83
92
  "peerDependencies": {
84
- "@earendil-works/pi-agent-core": "0.85.1",
85
- "@earendil-works/pi-coding-agent": "0.85.1"
93
+ "@earendil-works/pi-agent-core": "0.87.1",
94
+ "@earendil-works/pi-coding-agent": "0.87.1"
86
95
  },
87
96
  "peerDependenciesMeta": {
88
97
  "@earendil-works/pi-agent-core": {
@@ -95,11 +104,17 @@
95
104
  "engines": {
96
105
  "node": ">=22.19.0"
97
106
  },
98
- "doompiApi": {
99
- "basePath": "git",
100
- "hub": {
101
- "entry": "./src/exports/hubApi.ts",
102
- "dist": "./dist/hubApi.mjs"
107
+ "doompiServer": {
108
+ "entry": "./generated/server.ts",
109
+ "dist": "./dist/extensions/server.mjs",
110
+ "scopes": [
111
+ "global",
112
+ "workspace",
113
+ "session"
114
+ ],
115
+ "contracts": {
116
+ "entry": "./src/exports/apiContracts.ts",
117
+ "dist": "./dist/apiContracts.mjs"
103
118
  }
104
119
  },
105
120
  "doompiWeb": {
@@ -107,11 +122,10 @@
107
122
  "channels": [
108
123
  "git_worktrees"
109
124
  ],
110
- "client": "./src/exports/webClient.ts",
111
- "hub": {
112
- "entry": "./src/exports/webHub.ts",
113
- "dist": "./dist/webHub.mjs"
114
- }
125
+ "client": "./dist/extensions/web.mjs",
126
+ "scopes": [
127
+ "session"
128
+ ]
115
129
  },
116
130
  "pi": {
117
131
  "extensions": [
@@ -121,7 +135,7 @@
121
135
  "scripts": {
122
136
  "build": "tsdown",
123
137
  "test": "vitest --run",
124
- "typecheck": "tsc --noEmit",
138
+ "typecheck": "tsc --noEmit && tsc --noEmit -p tsconfig.web.json",
125
139
  "lint": "oxlint . && oxfmt . --check",
126
140
  "fixcode": "oxlint . --fix && oxfmt ."
127
141
  }
@@ -17,12 +17,15 @@ the parent checkout and silently builds the wrong code.
17
17
 
18
18
  Use a worktree when work needs its own branch **and** its own checkout at the
19
19
  same time: a long refactor you want to leave half-finished, a risky migration,
20
- or two features that touch the same files. A worktree costs a full install in
21
- the new directory, so it is the wrong tool for a quick edit; just edit.
20
+ or two features that touch the same files.
22
21
 
23
22
  Do not reach for it to parallelise work that shares a branch. Two sessions on
24
23
  one branch in two checkouts will fight, and git refuses the second worktree.
25
24
 
25
+ The new checkout has no dependencies installed: `spawn_worktree` runs git and
26
+ nothing else, whatever language the repository is in. Install them in the
27
+ worktree before building or testing there.
28
+
26
29
  ## Actions
27
30
 
28
31
  ```
@@ -1 +0,0 @@
1
- var e=Object.create,t=Object.defineProperty,n=Object.getOwnPropertyDescriptor,r=Object.getOwnPropertyNames,i=Object.getPrototypeOf,a=Object.prototype.hasOwnProperty,o=(e,i,o,s)=>{if(i&&typeof i==`object`||typeof i==`function`)for(var c=r(i),l=0,u=c.length,d;l<u;l++)d=c[l],!a.call(e,d)&&d!==o&&t(e,d,{get:(e=>i[e]).bind(null,d),enumerable:!(s=n(i,d))||s.enumerable});return e},s=(n,r,s)=>(s=n==null?{}:e(i(n)),o(r||!n||!n.__esModule||!a.call(n,`default`)?t(s,`default`,{value:n,enumerable:!0}):s,n));exports.__toESM=s;
@@ -1,2 +0,0 @@
1
- const e=require("../../_virtual/_rolldown/runtime.cjs");let t=require("node:fs");t=e.__toESM(t,1);let n=require("node:crypto"),r=require("node:path");r=e.__toESM(r,1);const i=`.json`,a=`.claim-`;let o=-1,s=0;function c(e,t){return r.default.join(e,t)}function l(e){let t;try{t=JSON.parse(e)}catch{return}if(typeof t!=`object`||!t)return;let n=t;if(n.version===1&&(n.from===`parent`||n.from===`child`)&&typeof n.text==`string`&&n.text!==``&&typeof n.sentAt==`string`)return{version:1,from:n.from,text:n.text,sentAt:n.sentAt}}function u(){let e=Date.now();return e===o?s+=1:(o=e,s=0),`${String(e).padStart(14,`0`)}-${String(s).padStart(6,`0`)}-${(0,n.randomBytes)(6).toString(`hex`)}`}function d(e){return{send(n,a){if(Buffer.byteLength(a,`utf8`)>65536)throw Error(`A message may not exceed 65536 bytes.`);let o=c(e,n);t.default.mkdirSync(o,{recursive:!0,mode:448});let s={version:1,from:n===`parent`?`child`:`parent`,text:a,sentAt:new Date().toISOString()},l=u(),d=r.default.join(o,`.tmp-${l}`);t.default.writeFileSync(d,JSON.stringify(s),{mode:384}),t.default.renameSync(d,r.default.join(o,`${l}${i}`))},receive(n){let o=c(e,n),s;try{s=t.default.readdirSync(o)}catch{return[]}let u=[];for(let e of s.filter(e=>e.endsWith(i)).sort()){let n=r.default.join(o,`${a}${String(process.pid)}-${e}`);try{t.default.renameSync(r.default.join(o,e),n)}catch{continue}let i;try{i=l(t.default.readFileSync(n,`utf8`))}catch{i=void 0}i!==void 0&&(u.push(i),t.default.rmSync(n,{force:!0}))}return u},sweep(n=Date.now()){let i=0;for(let o of[`parent`,`child`]){let s=c(e,o),l;try{l=t.default.readdirSync(s)}catch{continue}for(let e of l.filter(e=>e.startsWith(a))){let a=r.default.join(s,e);try{if(n-t.default.statSync(a).mtimeMs<6e4)continue;let o=e.slice(e.indexOf(`-`,7)+1);t.default.renameSync(a,r.default.join(s,o)),i+=1}catch{}}}return i}}}exports.createWorktreeChannel=d;
2
- //# sourceMappingURL=worktreeChannel.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"worktreeChannel.cjs","names":["path","randomBytes","fs"],"sources":["../../../src/adapters/channel/worktreeChannel.ts"],"sourcesContent":["/**\n * Messages between a parent session and one worktree session.\n *\n * A two-party mirror of doompi-team's channel, kept private to this package\n * rather than imported: a layer must not depend on another layer, and two\n * parties need none of the membership, heartbeat or fanout machinery that makes\n * that module large.\n *\n * THE INVARIANTS WORTH COPYING, AND WHAT BREAKS WITHOUT THEM:\n *\n * 1. Write to a temp name and rename into place. A reader polling a directory\n * will otherwise read a file that is still being written and parse half a\n * message.\n * 2. Claim by rename before reading. Rename is atomic, so exactly one reader\n * wins a message even when a session polls twice concurrently. Reading first\n * and deleting after delivers the same message twice.\n * 3. Deliver, then delete. Deleting first loses the message permanently if the\n * delivery throws; a duplicate is recoverable, a loss is not.\n * 4. A claim that outlives its reader is swept. Otherwise a crash mid-delivery\n * strands the message in a claimed state nobody will ever look at again.\n *\n * AVOID:\n * - Treating an atomic write as a lock. It prevents a torn read, nothing else.\n * - Deleting a file that failed to parse. It may belong to a newer build, and\n * destroying it turns a version skew into data loss.\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { randomBytes } from 'node:crypto';\n\nexport const CHANNEL_MESSAGE_VERSION = 1;\n\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst PRIVATE_FILE_MODE = 0o600;\nconst MESSAGE_SUFFIX = '.json';\nconst CLAIM_PREFIX = '.claim-';\nconst MAX_MESSAGE_BYTES = 64 * 1024;\n/** A claim older than this belonged to a reader that is not coming back. */\nconst CLAIM_STALE_MS = 60_000;\nconst MESSAGE_SEQUENCE_WIDTH = 6;\nlet lastMessageTimestamp = -1;\nlet messageSequence = 0;\n\n/** Which side of the pair a message is for. */\nexport type ChannelParty = 'parent' | 'child';\n\nexport interface ChannelMessage {\n version: typeof CHANNEL_MESSAGE_VERSION;\n /** Who sent it, so a reader never has to infer it from the directory. */\n from: ChannelParty;\n text: string;\n sentAt: string;\n}\n\nexport interface WorktreeChannel {\n send(to: ChannelParty, text: string): void;\n /** Claims and returns everything waiting for a party, oldest first. */\n receive(as: ChannelParty): ChannelMessage[];\n /** Releases claims abandoned by a reader that died mid-delivery. */\n sweep(now?: number): number;\n}\n\nfunction inboxDirectory(root: string, party: ChannelParty): string {\n return path.join(root, party);\n}\n\nfunction parse(raw: string): ChannelMessage | undefined {\n let value: unknown;\n try {\n value = JSON.parse(raw);\n } catch {\n return undefined;\n }\n if (typeof value !== 'object' || value === null) return undefined;\n const record = value as Partial<ChannelMessage>;\n if (record.version !== CHANNEL_MESSAGE_VERSION) return undefined;\n if (record.from !== 'parent' && record.from !== 'child') return undefined;\n if (typeof record.text !== 'string' || record.text === '') return undefined;\n if (typeof record.sentAt !== 'string') return undefined;\n return { version: CHANNEL_MESSAGE_VERSION, from: record.from, text: record.text, sentAt: record.sentAt };\n}\n\nfunction nextMessageName(): string {\n const timestamp = Date.now();\n if (timestamp === lastMessageTimestamp) {\n messageSequence += 1;\n } else {\n lastMessageTimestamp = timestamp;\n messageSequence = 0;\n }\n return `${String(timestamp).padStart(14, '0')}-${String(messageSequence).padStart(MESSAGE_SEQUENCE_WIDTH, '0')}-${randomBytes(6).toString('hex')}`;\n}\n\n/** The channel rooted at a directory, created on demand. */\nexport function createWorktreeChannel(root: string): WorktreeChannel {\n return {\n send(to, text) {\n if (Buffer.byteLength(text, 'utf8') > MAX_MESSAGE_BYTES) {\n throw new Error(`A message may not exceed ${String(MAX_MESSAGE_BYTES)} bytes.`);\n }\n const directory = inboxDirectory(root, to);\n fs.mkdirSync(directory, { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n const message: ChannelMessage = {\n version: CHANNEL_MESSAGE_VERSION,\n from: to === 'parent' ? 'child' : 'parent',\n text,\n sentAt: new Date().toISOString(),\n };\n // A process-local sequence preserves send order when several messages share\n // a millisecond. The random suffix still prevents cross-process collisions.\n const name = nextMessageName();\n const temporary = path.join(directory, `.tmp-${name}`);\n fs.writeFileSync(temporary, JSON.stringify(message), { mode: PRIVATE_FILE_MODE });\n fs.renameSync(temporary, path.join(directory, `${name}${MESSAGE_SUFFIX}`));\n },\n\n receive(as) {\n const directory = inboxDirectory(root, as);\n let names: string[];\n try {\n names = fs.readdirSync(directory);\n } catch {\n return [];\n }\n const delivered: ChannelMessage[] = [];\n for (const name of names.filter((entry) => entry.endsWith(MESSAGE_SUFFIX)).sort()) {\n const claim = path.join(directory, `${CLAIM_PREFIX}${String(process.pid)}-${name}`);\n try {\n // Whoever wins this rename owns the message. A loser gets ENOENT and\n // simply moves on, which is why this is not wrapped in an existence\n // check: the check and the rename would not be atomic together.\n fs.renameSync(path.join(directory, name), claim);\n } catch {\n continue;\n }\n let message: ChannelMessage | undefined;\n try {\n message = parse(fs.readFileSync(claim, 'utf8'));\n } catch {\n message = undefined;\n }\n if (message === undefined) {\n // Left claimed rather than deleted. An unreadable message may come\n // from a newer build, and the sweep will surface it again rather than\n // this reader destroying something it did not understand.\n continue;\n }\n delivered.push(message);\n fs.rmSync(claim, { force: true });\n }\n return delivered;\n },\n\n sweep(now = Date.now()) {\n let released = 0;\n for (const party of ['parent', 'child'] as const) {\n const directory = inboxDirectory(root, party);\n let names: string[];\n try {\n names = fs.readdirSync(directory);\n } catch {\n continue;\n }\n for (const name of names.filter((entry) => entry.startsWith(CLAIM_PREFIX))) {\n const claimed = path.join(directory, name);\n try {\n if (now - fs.statSync(claimed).mtimeMs < CLAIM_STALE_MS) continue;\n // Returned to the inbox rather than deleted, because the message\n // was claimed but never delivered.\n const original = name.slice(name.indexOf('-', CLAIM_PREFIX.length) + 1);\n fs.renameSync(claimed, path.join(directory, original));\n released += 1;\n } catch {\n // Another sweeper got there first.\n }\n }\n }\n return released;\n },\n };\n}\n"],"mappings":"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"}
@@ -1,40 +0,0 @@
1
- //#region src/adapters/channel/worktreeChannel.d.ts
2
- /**
3
- * Messages between a parent session and one worktree session.
4
- *
5
- * A two-party mirror of doompi-team's channel, kept private to this package
6
- * rather than imported: a layer must not depend on another layer, and two
7
- * parties need none of the membership, heartbeat or fanout machinery that makes
8
- * that module large.
9
- *
10
- * THE INVARIANTS WORTH COPYING, AND WHAT BREAKS WITHOUT THEM:
11
- *
12
- * 1. Write to a temp name and rename into place. A reader polling a directory
13
- * will otherwise read a file that is still being written and parse half a
14
- * message.
15
- * 2. Claim by rename before reading. Rename is atomic, so exactly one reader
16
- * wins a message even when a session polls twice concurrently. Reading first
17
- * and deleting after delivers the same message twice.
18
- * 3. Deliver, then delete. Deleting first loses the message permanently if the
19
- * delivery throws; a duplicate is recoverable, a loss is not.
20
- * 4. A claim that outlives its reader is swept. Otherwise a crash mid-delivery
21
- * strands the message in a claimed state nobody will ever look at again.
22
- *
23
- * AVOID:
24
- * - Treating an atomic write as a lock. It prevents a torn read, nothing else.
25
- * - Deleting a file that failed to parse. It may belong to a newer build, and
26
- * destroying it turns a version skew into data loss.
27
- */
28
- declare const CHANNEL_MESSAGE_VERSION = 1;
29
- /** Which side of the pair a message is for. */
30
- type ChannelParty = 'parent' | 'child';
31
- interface ChannelMessage {
32
- version: typeof CHANNEL_MESSAGE_VERSION;
33
- /** Who sent it, so a reader never has to infer it from the directory. */
34
- from: ChannelParty;
35
- text: string;
36
- sentAt: string;
37
- }
38
- //#endregion
39
- export { CHANNEL_MESSAGE_VERSION, ChannelMessage, ChannelParty };
40
- //# sourceMappingURL=worktreeChannel.d.cts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"worktreeChannel.d.cts","names":[],"sources":["../../../src/adapters/channel/worktreeChannel.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;cA+Ba;;KAcD;UAEK;EACf,gBAAgB;;EAEhB,MAAM;EACN;EACA"}
@@ -1,40 +0,0 @@
1
- //#region src/adapters/channel/worktreeChannel.d.ts
2
- /**
3
- * Messages between a parent session and one worktree session.
4
- *
5
- * A two-party mirror of doompi-team's channel, kept private to this package
6
- * rather than imported: a layer must not depend on another layer, and two
7
- * parties need none of the membership, heartbeat or fanout machinery that makes
8
- * that module large.
9
- *
10
- * THE INVARIANTS WORTH COPYING, AND WHAT BREAKS WITHOUT THEM:
11
- *
12
- * 1. Write to a temp name and rename into place. A reader polling a directory
13
- * will otherwise read a file that is still being written and parse half a
14
- * message.
15
- * 2. Claim by rename before reading. Rename is atomic, so exactly one reader
16
- * wins a message even when a session polls twice concurrently. Reading first
17
- * and deleting after delivers the same message twice.
18
- * 3. Deliver, then delete. Deleting first loses the message permanently if the
19
- * delivery throws; a duplicate is recoverable, a loss is not.
20
- * 4. A claim that outlives its reader is swept. Otherwise a crash mid-delivery
21
- * strands the message in a claimed state nobody will ever look at again.
22
- *
23
- * AVOID:
24
- * - Treating an atomic write as a lock. It prevents a torn read, nothing else.
25
- * - Deleting a file that failed to parse. It may belong to a newer build, and
26
- * destroying it turns a version skew into data loss.
27
- */
28
- declare const CHANNEL_MESSAGE_VERSION = 1;
29
- /** Which side of the pair a message is for. */
30
- type ChannelParty = 'parent' | 'child';
31
- interface ChannelMessage {
32
- version: typeof CHANNEL_MESSAGE_VERSION;
33
- /** Who sent it, so a reader never has to infer it from the directory. */
34
- from: ChannelParty;
35
- text: string;
36
- sentAt: string;
37
- }
38
- //#endregion
39
- export { CHANNEL_MESSAGE_VERSION, ChannelMessage, ChannelParty };
40
- //# sourceMappingURL=worktreeChannel.d.mts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"worktreeChannel.d.mts","names":[],"sources":["../../../src/adapters/channel/worktreeChannel.ts"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;cA+Ba;;KAcD;UAEK;EACf,gBAAgB;;EAEhB,MAAM;EACN;EACA"}
@@ -1,2 +0,0 @@
1
- import e from"node:fs";import{randomBytes as t}from"node:crypto";import n from"node:path";const r=`.json`,i=`.claim-`;let a=-1,o=0;function s(e,t){return n.join(e,t)}function c(e){let t;try{t=JSON.parse(e)}catch{return}if(typeof t!=`object`||!t)return;let n=t;if(n.version===1&&(n.from===`parent`||n.from===`child`)&&typeof n.text==`string`&&n.text!==``&&typeof n.sentAt==`string`)return{version:1,from:n.from,text:n.text,sentAt:n.sentAt}}function l(){let e=Date.now();return e===a?o+=1:(a=e,o=0),`${String(e).padStart(14,`0`)}-${String(o).padStart(6,`0`)}-${t(6).toString(`hex`)}`}function u(t){return{send(i,a){if(Buffer.byteLength(a,`utf8`)>65536)throw Error(`A message may not exceed 65536 bytes.`);let o=s(t,i);e.mkdirSync(o,{recursive:!0,mode:448});let c={version:1,from:i===`parent`?`child`:`parent`,text:a,sentAt:new Date().toISOString()},u=l(),d=n.join(o,`.tmp-${u}`);e.writeFileSync(d,JSON.stringify(c),{mode:384}),e.renameSync(d,n.join(o,`${u}${r}`))},receive(a){let o=s(t,a),l;try{l=e.readdirSync(o)}catch{return[]}let u=[];for(let t of l.filter(e=>e.endsWith(r)).sort()){let r=n.join(o,`${i}${String(process.pid)}-${t}`);try{e.renameSync(n.join(o,t),r)}catch{continue}let a;try{a=c(e.readFileSync(r,`utf8`))}catch{a=void 0}a!==void 0&&(u.push(a),e.rmSync(r,{force:!0}))}return u},sweep(r=Date.now()){let a=0;for(let o of[`parent`,`child`]){let c=s(t,o),l;try{l=e.readdirSync(c)}catch{continue}for(let t of l.filter(e=>e.startsWith(i))){let i=n.join(c,t);try{if(r-e.statSync(i).mtimeMs<6e4)continue;let o=t.slice(t.indexOf(`-`,7)+1);e.renameSync(i,n.join(c,o)),a+=1}catch{}}}return a}}}export{u as createWorktreeChannel};
2
- //# sourceMappingURL=worktreeChannel.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"worktreeChannel.mjs","names":[],"sources":["../../../src/adapters/channel/worktreeChannel.ts"],"sourcesContent":["/**\n * Messages between a parent session and one worktree session.\n *\n * A two-party mirror of doompi-team's channel, kept private to this package\n * rather than imported: a layer must not depend on another layer, and two\n * parties need none of the membership, heartbeat or fanout machinery that makes\n * that module large.\n *\n * THE INVARIANTS WORTH COPYING, AND WHAT BREAKS WITHOUT THEM:\n *\n * 1. Write to a temp name and rename into place. A reader polling a directory\n * will otherwise read a file that is still being written and parse half a\n * message.\n * 2. Claim by rename before reading. Rename is atomic, so exactly one reader\n * wins a message even when a session polls twice concurrently. Reading first\n * and deleting after delivers the same message twice.\n * 3. Deliver, then delete. Deleting first loses the message permanently if the\n * delivery throws; a duplicate is recoverable, a loss is not.\n * 4. A claim that outlives its reader is swept. Otherwise a crash mid-delivery\n * strands the message in a claimed state nobody will ever look at again.\n *\n * AVOID:\n * - Treating an atomic write as a lock. It prevents a torn read, nothing else.\n * - Deleting a file that failed to parse. It may belong to a newer build, and\n * destroying it turns a version skew into data loss.\n */\n\nimport fs from 'node:fs';\nimport path from 'node:path';\nimport { randomBytes } from 'node:crypto';\n\nexport const CHANNEL_MESSAGE_VERSION = 1;\n\nconst PRIVATE_DIRECTORY_MODE = 0o700;\nconst PRIVATE_FILE_MODE = 0o600;\nconst MESSAGE_SUFFIX = '.json';\nconst CLAIM_PREFIX = '.claim-';\nconst MAX_MESSAGE_BYTES = 64 * 1024;\n/** A claim older than this belonged to a reader that is not coming back. */\nconst CLAIM_STALE_MS = 60_000;\nconst MESSAGE_SEQUENCE_WIDTH = 6;\nlet lastMessageTimestamp = -1;\nlet messageSequence = 0;\n\n/** Which side of the pair a message is for. */\nexport type ChannelParty = 'parent' | 'child';\n\nexport interface ChannelMessage {\n version: typeof CHANNEL_MESSAGE_VERSION;\n /** Who sent it, so a reader never has to infer it from the directory. */\n from: ChannelParty;\n text: string;\n sentAt: string;\n}\n\nexport interface WorktreeChannel {\n send(to: ChannelParty, text: string): void;\n /** Claims and returns everything waiting for a party, oldest first. */\n receive(as: ChannelParty): ChannelMessage[];\n /** Releases claims abandoned by a reader that died mid-delivery. */\n sweep(now?: number): number;\n}\n\nfunction inboxDirectory(root: string, party: ChannelParty): string {\n return path.join(root, party);\n}\n\nfunction parse(raw: string): ChannelMessage | undefined {\n let value: unknown;\n try {\n value = JSON.parse(raw);\n } catch {\n return undefined;\n }\n if (typeof value !== 'object' || value === null) return undefined;\n const record = value as Partial<ChannelMessage>;\n if (record.version !== CHANNEL_MESSAGE_VERSION) return undefined;\n if (record.from !== 'parent' && record.from !== 'child') return undefined;\n if (typeof record.text !== 'string' || record.text === '') return undefined;\n if (typeof record.sentAt !== 'string') return undefined;\n return { version: CHANNEL_MESSAGE_VERSION, from: record.from, text: record.text, sentAt: record.sentAt };\n}\n\nfunction nextMessageName(): string {\n const timestamp = Date.now();\n if (timestamp === lastMessageTimestamp) {\n messageSequence += 1;\n } else {\n lastMessageTimestamp = timestamp;\n messageSequence = 0;\n }\n return `${String(timestamp).padStart(14, '0')}-${String(messageSequence).padStart(MESSAGE_SEQUENCE_WIDTH, '0')}-${randomBytes(6).toString('hex')}`;\n}\n\n/** The channel rooted at a directory, created on demand. */\nexport function createWorktreeChannel(root: string): WorktreeChannel {\n return {\n send(to, text) {\n if (Buffer.byteLength(text, 'utf8') > MAX_MESSAGE_BYTES) {\n throw new Error(`A message may not exceed ${String(MAX_MESSAGE_BYTES)} bytes.`);\n }\n const directory = inboxDirectory(root, to);\n fs.mkdirSync(directory, { recursive: true, mode: PRIVATE_DIRECTORY_MODE });\n const message: ChannelMessage = {\n version: CHANNEL_MESSAGE_VERSION,\n from: to === 'parent' ? 'child' : 'parent',\n text,\n sentAt: new Date().toISOString(),\n };\n // A process-local sequence preserves send order when several messages share\n // a millisecond. The random suffix still prevents cross-process collisions.\n const name = nextMessageName();\n const temporary = path.join(directory, `.tmp-${name}`);\n fs.writeFileSync(temporary, JSON.stringify(message), { mode: PRIVATE_FILE_MODE });\n fs.renameSync(temporary, path.join(directory, `${name}${MESSAGE_SUFFIX}`));\n },\n\n receive(as) {\n const directory = inboxDirectory(root, as);\n let names: string[];\n try {\n names = fs.readdirSync(directory);\n } catch {\n return [];\n }\n const delivered: ChannelMessage[] = [];\n for (const name of names.filter((entry) => entry.endsWith(MESSAGE_SUFFIX)).sort()) {\n const claim = path.join(directory, `${CLAIM_PREFIX}${String(process.pid)}-${name}`);\n try {\n // Whoever wins this rename owns the message. A loser gets ENOENT and\n // simply moves on, which is why this is not wrapped in an existence\n // check: the check and the rename would not be atomic together.\n fs.renameSync(path.join(directory, name), claim);\n } catch {\n continue;\n }\n let message: ChannelMessage | undefined;\n try {\n message = parse(fs.readFileSync(claim, 'utf8'));\n } catch {\n message = undefined;\n }\n if (message === undefined) {\n // Left claimed rather than deleted. An unreadable message may come\n // from a newer build, and the sweep will surface it again rather than\n // this reader destroying something it did not understand.\n continue;\n }\n delivered.push(message);\n fs.rmSync(claim, { force: true });\n }\n return delivered;\n },\n\n sweep(now = Date.now()) {\n let released = 0;\n for (const party of ['parent', 'child'] as const) {\n const directory = inboxDirectory(root, party);\n let names: string[];\n try {\n names = fs.readdirSync(directory);\n } catch {\n continue;\n }\n for (const name of names.filter((entry) => entry.startsWith(CLAIM_PREFIX))) {\n const claimed = path.join(directory, name);\n try {\n if (now - fs.statSync(claimed).mtimeMs < CLAIM_STALE_MS) continue;\n // Returned to the inbox rather than deleted, because the message\n // was claimed but never delivered.\n const original = name.slice(name.indexOf('-', CLAIM_PREFIX.length) + 1);\n fs.renameSync(claimed, path.join(directory, original));\n released += 1;\n } catch {\n // Another sweeper got there first.\n }\n }\n }\n return released;\n },\n };\n}\n"],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- const e=require("../../_virtual/_rolldown/runtime.cjs");let t=require("node:fs");t=e.__toESM(t,1);let n=require("node:path");n=e.__toESM(n,1);function r(e,r){let i=n.default.dirname(e);t.default.mkdirSync(i,{recursive:!0});let a=n.default.join(i,`.${n.default.basename(e)}.${String(process.pid)}.tmp`);t.default.writeFileSync(a,`${JSON.stringify(r,null,2)}\n`,{mode:384}),t.default.renameSync(a,e)}function i(e){try{return JSON.parse(t.default.readFileSync(e,`utf8`))}catch{return}}exports.readJson=i,exports.writeJsonAtomic=r;
2
- //# sourceMappingURL=atomicJson.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"atomicJson.cjs","names":["path","fs"],"sources":["../../../src/adapters/filesystem/atomicJson.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nconst OWNER_ONLY_FILE = 0o600;\n\n/**\n * Writes JSON so a reader never sees a half-written file.\n *\n * The registry is read by the same process that writes it, and by a later\n * process after a crash. A plain write can be interrupted between the truncate\n * and the last byte, which leaves a file that parses as nothing and loses every\n * record in it. Writing beside the target and renaming makes the swap atomic on\n * the same filesystem, so a reader sees either the old file or the new one.\n */\nexport function writeJsonAtomic(file: string, value: unknown): void {\n const directory = path.dirname(file);\n fs.mkdirSync(directory, { recursive: true });\n const temporary = path.join(directory, `.${path.basename(file)}.${String(process.pid)}.tmp`);\n fs.writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\\n`, { mode: OWNER_ONLY_FILE });\n fs.renameSync(temporary, file);\n}\n\n/**\n * Reads JSON, treating every failure as absent.\n *\n * A missing file and an unparseable one mean the same thing to every caller\n * here: there is nothing to act on. Distinguishing them would only offer a\n * choice no caller wants to make.\n */\nexport function readJson<T>(file: string): T | undefined {\n try {\n return JSON.parse(fs.readFileSync(file, 'utf8')) as T;\n } catch {\n return undefined;\n }\n}\n"],"mappings":"8IAcA,SAAgB,EAAgB,EAAc,EAAsB,CAClE,IAAM,EAAYA,EAAAA,QAAK,QAAQ,CAAI,EACnC,EAAA,QAAG,UAAU,EAAW,CAAE,UAAW,EAAK,CAAC,EAC3C,IAAM,EAAYA,EAAAA,QAAK,KAAK,EAAW,IAAIA,EAAAA,QAAK,SAAS,CAAI,EAAE,GAAG,OAAO,QAAQ,GAAG,EAAE,KAAK,EAC3F,EAAA,QAAG,cAAc,EAAW,GAAG,KAAK,UAAU,EAAO,KAAM,CAAC,EAAE,IAAK,CAAE,KAAM,GAAgB,CAAC,EAC5F,EAAA,QAAG,WAAW,EAAW,CAAI,CAC/B,CASA,SAAgB,EAAY,EAA6B,CACvD,GAAI,CACF,OAAO,KAAK,MAAMC,EAAAA,QAAG,aAAa,EAAM,MAAM,CAAC,CACjD,MAAQ,CACN,MACF,CACF"}
@@ -1,2 +0,0 @@
1
- import e from"node:fs";import t from"node:path";function n(n,r){let i=t.dirname(n);e.mkdirSync(i,{recursive:!0});let a=t.join(i,`.${t.basename(n)}.${String(process.pid)}.tmp`);e.writeFileSync(a,`${JSON.stringify(r,null,2)}\n`,{mode:384}),e.renameSync(a,n)}function r(t){try{return JSON.parse(e.readFileSync(t,`utf8`))}catch{return}}export{r as readJson,n as writeJsonAtomic};
2
- //# sourceMappingURL=atomicJson.mjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"atomicJson.mjs","names":[],"sources":["../../../src/adapters/filesystem/atomicJson.ts"],"sourcesContent":["import fs from 'node:fs';\nimport path from 'node:path';\n\nconst OWNER_ONLY_FILE = 0o600;\n\n/**\n * Writes JSON so a reader never sees a half-written file.\n *\n * The registry is read by the same process that writes it, and by a later\n * process after a crash. A plain write can be interrupted between the truncate\n * and the last byte, which leaves a file that parses as nothing and loses every\n * record in it. Writing beside the target and renaming makes the swap atomic on\n * the same filesystem, so a reader sees either the old file or the new one.\n */\nexport function writeJsonAtomic(file: string, value: unknown): void {\n const directory = path.dirname(file);\n fs.mkdirSync(directory, { recursive: true });\n const temporary = path.join(directory, `.${path.basename(file)}.${String(process.pid)}.tmp`);\n fs.writeFileSync(temporary, `${JSON.stringify(value, null, 2)}\\n`, { mode: OWNER_ONLY_FILE });\n fs.renameSync(temporary, file);\n}\n\n/**\n * Reads JSON, treating every failure as absent.\n *\n * A missing file and an unparseable one mean the same thing to every caller\n * here: there is nothing to act on. Distinguishing them would only offer a\n * choice no caller wants to make.\n */\nexport function readJson<T>(file: string): T | undefined {\n try {\n return JSON.parse(fs.readFileSync(file, 'utf8')) as T;\n } catch {\n return undefined;\n }\n}\n"],"mappings":"gDAcA,SAAgB,EAAgB,EAAc,EAAsB,CAClE,IAAM,EAAY,EAAK,QAAQ,CAAI,EACnC,EAAG,UAAU,EAAW,CAAE,UAAW,EAAK,CAAC,EAC3C,IAAM,EAAY,EAAK,KAAK,EAAW,IAAI,EAAK,SAAS,CAAI,EAAE,GAAG,OAAO,QAAQ,GAAG,EAAE,KAAK,EAC3F,EAAG,cAAc,EAAW,GAAG,KAAK,UAAU,EAAO,KAAM,CAAC,EAAE,IAAK,CAAE,KAAM,GAAgB,CAAC,EAC5F,EAAG,WAAW,EAAW,CAAI,CAC/B,CASA,SAAgB,EAAY,EAA6B,CACvD,GAAI,CACF,OAAO,KAAK,MAAM,EAAG,aAAa,EAAM,MAAM,CAAC,CACjD,MAAQ,CACN,MACF,CACF"}
@@ -1,2 +0,0 @@
1
- const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("../worktree/repositoryIdentity.cjs");let n=require("node:path");n=e.__toESM(n,1);let r=require("@agimon-ai/doompi-web-contracts"),i=require("node:os");i=e.__toESM(i,1);function a(e=i.default.homedir()){return n.default.join(e,`.pi`,`.doom`,`git`)}function o(e=i.default.homedir()){return n.default.join(a(e),`worktrees`)}function s(e,t=i.default.homedir()){return`${a(t)}/channels/${e}`}function c(e,r=i.default.homedir()){let o=`${t.repositoryLabel(e)}--${t.repositoryId(e)}`;return n.default.join(a(r),`registry`,o,`worktrees.json`)}function l(e=process.argv,t=process.env,n=i.default.homedir()){let a=e.indexOf(`--registry-dir`),o=a>=0?e[a+1]:void 0;return(0,r.resolveRegistryDir)({...o===void 0?{}:{flagValue:o},...t[r.REGISTRY_DIR_ENV]===void 0?{}:{envValue:t[r.REGISTRY_DIR_ENV]},homeDir:n})}exports.channelRoot=s,exports.doomGitRoot=a,exports.hubRegistryDir=l,exports.registryFile=c,exports.worktreesRoot=o;
2
- //# sourceMappingURL=paths.cjs.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"paths.cjs","names":["os","path","repositoryLabel","repositoryId","resolveRegistryDir","REGISTRY_DIR_ENV"],"sources":["../../../src/adapters/filesystem/paths.ts"],"sourcesContent":["import os from 'node:os';\nimport path from 'node:path';\nimport { REGISTRY_DIR_ENV, resolveRegistryDir } from '@agimon-ai/doompi-web-contracts';\nimport { repositoryId, repositoryLabel } from '../worktree/repositoryIdentity.ts';\n\nconst REGISTRY_DIR_FLAG = '--registry-dir';\n\n/**\n * Where this package keeps its own state.\n *\n * Outside the repository on purpose. A worktree directory inside the checkout\n * would be found by every glob the repository's own tooling runs, and would\n * resolve workspace imports against the parent checkout rather than the\n * worktree, so a session there would build the wrong source while appearing to\n * work.\n */\nexport function doomGitRoot(homeDir: string = os.homedir()): string {\n return path.join(homeDir, '.pi', '.doom', 'git');\n}\n\n/** The directory every worktree for every repository is created under. */\nexport function worktreesRoot(homeDir: string = os.homedir()): string {\n return path.join(doomGitRoot(homeDir), 'worktrees');\n}\n\n/** One registry file per repository, keyed by the shared git directory. */\n/** Where a worktree's parent/child channel lives, outside the worktree itself. */\nexport function channelRoot(worktreeId: string, homeDir: string = os.homedir()): string {\n // Outside the worktree on purpose: closing a worktree deletes its directory,\n // and a channel inside it would take undelivered messages down with it.\n return `${doomGitRoot(homeDir)}/channels/${worktreeId}`;\n}\n\nexport function registryFile(repositoryRoot: string, homeDir: string = os.homedir()): string {\n const key = `${repositoryLabel(repositoryRoot)}--${repositoryId(repositoryRoot)}`;\n return path.join(doomGitRoot(homeDir), 'registry', key, 'worktrees.json');\n}\n\n/**\n * The session registry directory this process's hub is watching.\n *\n * A spawned session only appears in the rail if it registers in the same\n * directory the hub watches, and the hub passes that directory to its children\n * as `--registry-dir` while also inheriting the env var. Recovering it from\n * this process's own argv and env, in the same precedence the hub and the\n * session server use, is what makes a worktree session visible rather than\n * silently invisible.\n */\nexport function hubRegistryDir(\n argv: readonly string[] = process.argv,\n env: NodeJS.ProcessEnv = process.env,\n homeDir: string = os.homedir(),\n): string {\n const index = argv.indexOf(REGISTRY_DIR_FLAG);\n const flagValue = index >= 0 ? argv[index + 1] : undefined;\n return resolveRegistryDir({\n ...(flagValue === undefined ? {} : { flagValue }),\n ...(env[REGISTRY_DIR_ENV] === undefined ? {} : { envValue: env[REGISTRY_DIR_ENV] }),\n homeDir,\n });\n}\n"],"mappings":"2OAgBA,SAAgB,EAAY,EAAkBA,EAAAA,QAAG,QAAQ,EAAW,CAClE,OAAOC,EAAAA,QAAK,KAAK,EAAS,MAAO,QAAS,KAAK,CACjD,CAGA,SAAgB,EAAc,EAAkBD,EAAAA,QAAG,QAAQ,EAAW,CACpE,OAAOC,EAAAA,QAAK,KAAK,EAAY,CAAO,EAAG,WAAW,CACpD,CAIA,SAAgB,EAAY,EAAoB,EAAkBD,EAAAA,QAAG,QAAQ,EAAW,CAGtF,MAAO,GAAG,EAAY,CAAO,EAAE,YAAY,GAC7C,CAEA,SAAgB,EAAa,EAAwB,EAAkBA,EAAAA,QAAG,QAAQ,EAAW,CAC3F,IAAM,EAAM,GAAGE,EAAAA,gBAAgB,CAAc,EAAE,IAAIC,EAAAA,aAAa,CAAc,IAC9E,OAAOF,EAAAA,QAAK,KAAK,EAAY,CAAO,EAAG,WAAY,EAAK,gBAAgB,CAC1E,CAYA,SAAgB,EACd,EAA0B,QAAQ,KAClC,EAAyB,QAAQ,IACjC,EAAkBD,EAAAA,QAAG,QAAQ,EACrB,CACR,IAAM,EAAQ,EAAK,QAAQ,gBAAiB,EACtC,EAAY,GAAS,EAAI,EAAK,EAAQ,GAAK,IAAA,GACjD,OAAA,EAAOI,EAAAA,mBAAAA,CAAmB,CACxB,GAAI,IAAc,IAAA,GAAY,CAAC,EAAI,CAAE,WAAU,EAC/C,GAAI,EAAIC,EAAAA,oBAAsB,IAAA,GAAY,CAAC,EAAI,CAAE,SAAU,EAAIA,EAAAA,iBAAkB,EACjF,SACF,CAAC,CACH"}
@@ -1,2 +0,0 @@
1
- import{repositoryId as e,repositoryLabel as t}from"../worktree/repositoryIdentity.mjs";import n from"node:path";import{REGISTRY_DIR_ENV as r,resolveRegistryDir as i}from"@agimon-ai/doompi-web-contracts";import a from"node:os";function o(e=a.homedir()){return n.join(e,`.pi`,`.doom`,`git`)}function s(e=a.homedir()){return n.join(o(e),`worktrees`)}function c(e,t=a.homedir()){return`${o(t)}/channels/${e}`}function l(r,i=a.homedir()){let s=`${t(r)}--${e(r)}`;return n.join(o(i),`registry`,s,`worktrees.json`)}function u(e=process.argv,t=process.env,n=a.homedir()){let o=e.indexOf(`--registry-dir`),s=o>=0?e[o+1]:void 0;return i({...s===void 0?{}:{flagValue:s},...t[r]===void 0?{}:{envValue:t[r]},homeDir:n})}export{c as channelRoot,o as doomGitRoot,u as hubRegistryDir,l as registryFile,s as worktreesRoot};
2
- //# sourceMappingURL=paths.mjs.map