@agimon-ai/doompi-git 0.0.1-alpha.4 → 0.0.1-alpha.41
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +33 -1
- package/dist/apiContracts.cjs +387 -0
- package/dist/apiContracts.cjs.map +1 -0
- package/dist/apiContracts.d.mts +53 -0
- package/dist/apiContracts.d.mts.map +1 -0
- package/dist/apiContracts.mjs +382 -0
- package/dist/apiContracts.mjs.map +1 -0
- package/dist/extensions/pi.cjs +105 -1
- package/dist/extensions/pi.cjs.map +1 -0
- package/dist/extensions/pi.d.mts +6 -2
- package/dist/extensions/pi.d.mts.map +1 -0
- package/dist/extensions/pi.mjs +100 -1
- package/dist/extensions/pi.mjs.map +1 -0
- package/dist/extensions/server.cjs +1828 -0
- package/dist/extensions/server.cjs.map +1 -0
- package/dist/extensions/server.d.mts +5 -0
- package/dist/extensions/server.d.mts.map +1 -0
- package/dist/extensions/server.mjs +1820 -0
- package/dist/extensions/server.mjs.map +1 -0
- package/dist/extensions/web.mjs +2641 -0
- package/dist/extensions/web.mjs.map +1 -0
- package/dist/gitDependencies-CtVubYvU.mjs +22 -0
- package/dist/gitDependencies-CtVubYvU.mjs.map +1 -0
- package/dist/gitDependencies-Dqsz2HjK.cjs +33 -0
- package/dist/gitDependencies-Dqsz2HjK.cjs.map +1 -0
- package/dist/index-D79MSCee.d.mts +145 -0
- package/dist/index-D79MSCee.d.mts.map +1 -0
- package/dist/index.cjs +4 -1
- package/dist/index.d.mts +2 -5
- package/dist/index.mjs +2 -1
- package/dist/runWorktree-BE795LxL.cjs +374 -0
- package/dist/runWorktree-BE795LxL.cjs.map +1 -0
- package/dist/runWorktree-SPzJvIBt.mjs +303 -0
- package/dist/runWorktree-SPzJvIBt.mjs.map +1 -0
- package/dist/worktreeOperations-BYmVU0UV.cjs +1350 -0
- package/dist/worktreeOperations-BYmVU0UV.cjs.map +1 -0
- package/dist/worktreeOperations-BrWG2muV.mjs +1194 -0
- package/dist/worktreeOperations-BrWG2muV.mjs.map +1 -0
- package/llms.txt +4 -0
- package/package.json +56 -41
- package/src/prompts/doompi-use-git/SKILL.md +26 -3
- package/dist/_virtual/_rolldown/runtime.cjs +0 -1
- package/dist/adapters/channel/worktreeChannel.cjs +0 -2
- package/dist/adapters/channel/worktreeChannel.cjs.map +0 -1
- package/dist/adapters/channel/worktreeChannel.d.cts +0 -40
- package/dist/adapters/channel/worktreeChannel.d.cts.map +0 -1
- package/dist/adapters/channel/worktreeChannel.d.mts +0 -40
- package/dist/adapters/channel/worktreeChannel.d.mts.map +0 -1
- package/dist/adapters/channel/worktreeChannel.mjs +0 -2
- package/dist/adapters/channel/worktreeChannel.mjs.map +0 -1
- package/dist/adapters/filesystem/atomicJson.cjs +0 -2
- package/dist/adapters/filesystem/atomicJson.cjs.map +0 -1
- package/dist/adapters/filesystem/atomicJson.mjs +0 -2
- package/dist/adapters/filesystem/atomicJson.mjs.map +0 -1
- package/dist/adapters/filesystem/paths.cjs +0 -2
- package/dist/adapters/filesystem/paths.cjs.map +0 -1
- package/dist/adapters/filesystem/paths.mjs +0 -2
- package/dist/adapters/filesystem/paths.mjs.map +0 -1
- package/dist/adapters/hub/hubClient.cjs +0 -2
- package/dist/adapters/hub/hubClient.cjs.map +0 -1
- package/dist/adapters/hub/hubClient.d.cts +0 -1
- package/dist/adapters/hub/hubClient.d.mts +0 -1
- package/dist/adapters/hub/hubClient.mjs +0 -2
- package/dist/adapters/hub/hubClient.mjs.map +0 -1
- package/dist/adapters/pi/extension.cjs +0 -2
- package/dist/adapters/pi/extension.cjs.map +0 -1
- package/dist/adapters/pi/extension.d.cts +0 -9
- package/dist/adapters/pi/extension.d.cts.map +0 -1
- package/dist/adapters/pi/extension.d.mts +0 -9
- package/dist/adapters/pi/extension.d.mts.map +0 -1
- package/dist/adapters/pi/extension.mjs +0 -2
- package/dist/adapters/pi/extension.mjs.map +0 -1
- package/dist/adapters/pi/extensions/runWorktreeTool.cjs +0 -15
- package/dist/adapters/pi/extensions/runWorktreeTool.cjs.map +0 -1
- package/dist/adapters/pi/extensions/runWorktreeTool.mjs +0 -15
- package/dist/adapters/pi/extensions/runWorktreeTool.mjs.map +0 -1
- package/dist/adapters/web/worktreesChannel.cjs +0 -2
- package/dist/adapters/web/worktreesChannel.cjs.map +0 -1
- package/dist/adapters/web/worktreesChannel.d.cts +0 -21
- package/dist/adapters/web/worktreesChannel.d.cts.map +0 -1
- package/dist/adapters/web/worktreesChannel.d.mts +0 -21
- package/dist/adapters/web/worktreesChannel.d.mts.map +0 -1
- package/dist/adapters/web/worktreesChannel.mjs +0 -2
- package/dist/adapters/web/worktreesChannel.mjs.map +0 -1
- package/dist/adapters/worktree/dependencyInstall.cjs +0 -2
- package/dist/adapters/worktree/dependencyInstall.cjs.map +0 -1
- package/dist/adapters/worktree/dependencyInstall.mjs +0 -2
- package/dist/adapters/worktree/dependencyInstall.mjs.map +0 -1
- package/dist/adapters/worktree/gitCli.cjs +0 -4
- package/dist/adapters/worktree/gitCli.cjs.map +0 -1
- package/dist/adapters/worktree/gitCli.mjs +0 -4
- package/dist/adapters/worktree/gitCli.mjs.map +0 -1
- package/dist/adapters/worktree/repositoryIdentity.cjs +0 -2
- package/dist/adapters/worktree/repositoryIdentity.cjs.map +0 -1
- package/dist/adapters/worktree/repositoryIdentity.mjs +0 -2
- package/dist/adapters/worktree/repositoryIdentity.mjs.map +0 -1
- package/dist/adapters/worktree/worktreeOperations.cjs +0 -2
- package/dist/adapters/worktree/worktreeOperations.cjs.map +0 -1
- package/dist/adapters/worktree/worktreeOperations.d.cts +0 -32
- package/dist/adapters/worktree/worktreeOperations.d.cts.map +0 -1
- package/dist/adapters/worktree/worktreeOperations.d.mts +0 -32
- package/dist/adapters/worktree/worktreeOperations.d.mts.map +0 -1
- package/dist/adapters/worktree/worktreeOperations.mjs +0 -2
- package/dist/adapters/worktree/worktreeOperations.mjs.map +0 -1
- package/dist/adapters/worktree/worktreeRegistry.cjs +0 -2
- package/dist/adapters/worktree/worktreeRegistry.cjs.map +0 -1
- package/dist/adapters/worktree/worktreeRegistry.mjs +0 -2
- package/dist/adapters/worktree/worktreeRegistry.mjs.map +0 -1
- package/dist/commands/doomGitCommand.cjs +0 -2
- package/dist/commands/doomGitCommand.cjs.map +0 -1
- package/dist/commands/doomGitCommand.mjs +0 -2
- package/dist/commands/doomGitCommand.mjs.map +0 -1
- package/dist/container/index.cjs +0 -2
- package/dist/container/index.cjs.map +0 -1
- package/dist/container/index.d.cts +0 -6
- package/dist/container/index.d.cts.map +0 -1
- package/dist/container/index.d.mts +0 -6
- package/dist/container/index.d.mts.map +0 -1
- package/dist/container/index.mjs +0 -2
- package/dist/container/index.mjs.map +0 -1
- package/dist/extensions/pi.d.cts +0 -2
- package/dist/hubApi.cjs +0 -2
- package/dist/hubApi.cjs.map +0 -1
- package/dist/hubApi.d.cts +0 -6
- package/dist/hubApi.d.cts.map +0 -1
- package/dist/hubApi.d.mts +0 -6
- package/dist/hubApi.d.mts.map +0 -1
- package/dist/hubApi.mjs +0 -2
- package/dist/hubApi.mjs.map +0 -1
- package/dist/index.d.cts +0 -5
- package/dist/schemas/runWorktreeTool.cjs +0 -2
- package/dist/schemas/runWorktreeTool.cjs.map +0 -1
- package/dist/schemas/runWorktreeTool.mjs +0 -2
- package/dist/schemas/runWorktreeTool.mjs.map +0 -1
- package/dist/services/extensionService.cjs +0 -2
- package/dist/services/extensionService.cjs.map +0 -1
- package/dist/services/extensionService.d.cts +0 -8
- package/dist/services/extensionService.d.cts.map +0 -1
- package/dist/services/extensionService.d.mts +0 -8
- package/dist/services/extensionService.d.mts.map +0 -1
- package/dist/services/extensionService.mjs +0 -2
- package/dist/services/extensionService.mjs.map +0 -1
- package/dist/services/support/errors.cjs +0 -2
- package/dist/services/support/errors.cjs.map +0 -1
- package/dist/services/support/errors.mjs +0 -2
- package/dist/services/support/errors.mjs.map +0 -1
- package/dist/services/worktreeNaming.cjs +0 -2
- package/dist/services/worktreeNaming.cjs.map +0 -1
- package/dist/services/worktreeNaming.d.cts +0 -27
- package/dist/services/worktreeNaming.d.cts.map +0 -1
- package/dist/services/worktreeNaming.d.mts +0 -27
- package/dist/services/worktreeNaming.d.mts.map +0 -1
- package/dist/services/worktreeNaming.mjs +0 -2
- package/dist/services/worktreeNaming.mjs.map +0 -1
- package/dist/types/extension.d.cts +0 -17
- package/dist/types/extension.d.cts.map +0 -1
- package/dist/types/extension.d.mts +0 -17
- package/dist/types/extension.d.mts.map +0 -1
- package/dist/types/webWorktrees.cjs +0 -2
- package/dist/types/webWorktrees.cjs.map +0 -1
- package/dist/types/webWorktrees.mjs +0 -2
- package/dist/types/webWorktrees.mjs.map +0 -1
- package/dist/types/worktreeRegistry.d.cts +0 -41
- package/dist/types/worktreeRegistry.d.cts.map +0 -1
- package/dist/types/worktreeRegistry.d.mts +0 -41
- package/dist/types/worktreeRegistry.d.mts.map +0 -1
- package/dist/webHub.cjs +0 -1
- package/dist/webHub.d.cts +0 -2
- package/dist/webHub.d.mts +0 -2
- package/dist/webHub.mjs +0 -1
- package/src/exports/webClient.ts +0 -1
- package/src/types/webWorktrees.ts +0 -55
- package/src/types/worktreeRegistry.ts +0 -69
- package/src/web/components/RunWorktreeToolMessage.tsx +0 -37
- package/src/web/components/WorktreesActivitySection.tsx +0 -64
- package/src/web/components/WorktreesPanel.tsx +0 -125
- package/src/web/components/toolRenderers.ts +0 -7
- package/src/web/index.ts +0 -30
- package/src/web/stores/worktreesActivityStore.ts +0 -124
- package/src/web/tsconfig.json +0 -22
|
@@ -1,4 +0,0 @@
|
|
|
1
|
-
import{execFile as e}from"node:child_process";import t from"node:fs";const n=3e4;function r(t,n,r){return new Promise(i=>{e(`git`,[...n],{cwd:t,timeout:r,maxBuffer:1e6,env:{...process.env,LC_ALL:`C`}},(e,t,n)=>{i({code:e&&typeof e.code==`number`?e.code:+!!e,stdout:String(t),stderr:String(n)})})})}function i(e,t){return Error(`git ${e} failed (exit ${String(t.code)}, ${String(t.stderr.length)} bytes of stderr)`)}function a(e){let t=e.toLowerCase();return t.includes(`is not a working tree`)||t.includes(`cannot remove working tree`)}function o(){let e=async e=>{await r(e,[`worktree`,`prune`],15e3)};return{async addWorktree({repositoryRoot:e,path:t,branch:n,baseRef:a}){let o=await r(e,[`worktree`,`add`,`-b`,n,t,a],3e5);if(o.code!==0)throw i(`worktree add`,o)},async removeWorktree({repositoryRoot:n,path:o,force:s}){let c=await r(n,[`worktree`,`remove`,...s?[`--force`]:[],o],3e5);if(c.code!==0){if(a(c.stderr)&&!t.existsSync(o)){await e(n);return}throw i(`worktree remove`,c)}},pruneWorktrees:e,async listWorktreePaths(e){let t=await r(e,[`worktree`,`list`,`--porcelain`],n);return t.code===0?t.stdout.split(`
|
|
2
|
-
`).filter(e=>e.startsWith(`worktree `)).map(e=>e.slice(9).trim()).filter(e=>e!==``):[]},async dirtyFiles(e){let t=await r(e,[`status`,`--porcelain`],n);return t.code===0?t.stdout.split(`
|
|
3
|
-
`).map(e=>e.slice(3).trim()).filter(e=>e!==``):[]},async repositoryRoot(e){let t=await r(e,[`rev-parse`,`--show-toplevel`],n);if(t.code!==0)return;let i=t.stdout.trim();return i===``?void 0:i},async currentBranch(e){let t=await r(e,[`rev-parse`,`--abbrev-ref`,`HEAD`],n);if(t.code!==0)return;let i=t.stdout.trim();return i===``?void 0:i},async mergeBranch({repositoryRoot:e,branch:t,message:a}){let o=await r(e,[`merge`,`--no-ff`,...a===void 0?[]:[`-m`,a],t],n);if(o.code!==0)throw i(`merge`,o)}}}export{o as createWorktreeGit};
|
|
4
|
-
//# sourceMappingURL=gitCli.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"gitCli.mjs","names":[],"sources":["../../../src/adapters/worktree/gitCli.ts"],"sourcesContent":["import { execFile } from 'node:child_process';\nimport fs from 'node:fs';\nimport type { WorktreeGit } from '../../types/worktreeRegistry.ts';\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 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 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"],"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:crypto"),r=require("node:path");r=e.__toESM(r,1);function i(e){let n=r.default.join(e,`.git`),i;try{i=t.default.statSync(n)}catch{return}if(i.isDirectory())return n;let a;try{a=t.default.readFileSync(n,`utf8`).trim()}catch{return}if(!a.startsWith(`gitdir:`))return;let o=r.default.resolve(e,a.slice(7).trim());try{let e=t.default.readFileSync(r.default.join(o,`commondir`),`utf8`).trim();return r.default.resolve(o,e)}catch{return o}}function a(e){let t=r.default.basename(e).toLowerCase().replace(/[^a-z0-9]+/gu,`-`).replace(/^-+|-+$/gu,``).slice(0,32);return t===``?`repository`:t}function o(e){let t=i(e),a=t===void 0?`root:${r.default.resolve(e)}`:`git:${t}`;return(0,n.createHash)(`sha256`).update(a).digest(`hex`).slice(0,12)}function s(e){return(0,n.createHash)(`sha256`).update(e).digest(`hex`).slice(0,8)}exports.gitCommonDirectory=i,exports.repositoryId=o,exports.repositoryLabel=a,exports.shortId=s;
|
|
2
|
-
//# sourceMappingURL=repositoryIdentity.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"repositoryIdentity.cjs","names":["path","fs","createHash"],"sources":["../../../src/adapters/worktree/repositoryIdentity.ts"],"sourcesContent":["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 */\nexport function gitCommonDirectory(root: string): string | undefined {\n const marker = path.join(root, '.git');\n let stat: fs.Stats;\n try {\n stat = fs.statSync(marker);\n } catch {\n return undefined;\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"],"mappings":"uKAgBA,SAAgB,EAAmB,EAAkC,CACnE,IAAM,EAASA,EAAAA,QAAK,KAAK,EAAM,MAAM,EACjC,EACJ,GAAI,CACF,EAAOC,EAAAA,QAAG,SAAS,CAAM,CAC3B,MAAQ,CACN,MACF,CACA,GAAI,EAAK,YAAY,EAAG,OAAO,EAC/B,IAAI,EACJ,GAAI,CACF,EAAUA,EAAAA,QAAG,aAAa,EAAQ,MAAM,CAAC,CAAC,KAAK,CACjD,MAAQ,CACN,MACF,CACA,GAAI,CAAC,EAAQ,WAAW,SAAS,EAAG,OACpC,IAAM,EAASD,EAAAA,QAAK,QAAQ,EAAM,EAAQ,MAAM,CAAgB,CAAC,CAAC,KAAK,CAAC,EACxE,GAAI,CACF,IAAM,EAASC,EAAAA,QAAG,aAAaD,EAAAA,QAAK,KAAK,EAAQ,WAAW,EAAG,MAAM,CAAC,CAAC,KAAK,EAC5E,OAAOA,EAAAA,QAAK,QAAQ,EAAQ,CAAM,CACpC,MAAQ,CAEN,OAAO,CACT,CACF,CAGA,SAAgB,EAAgB,EAAsB,CACpD,IAAM,EAAQA,EAAAA,QACX,SAAS,CAAI,CAAC,CACd,YAAY,CAAC,CACb,QAAQ,eAAgB,GAAG,CAAC,CAC5B,QAAQ,YAAa,EAAE,CAAC,CACxB,MAAM,EAAG,EAAgB,EAC5B,OAAO,IAAU,GAAK,aAAe,CACvC,CAUA,SAAgB,EAAa,EAAsB,CACjD,IAAM,EAAS,EAAmB,CAAI,EAChC,EAAQ,IAAW,IAAA,GAAY,QAAQA,EAAAA,QAAK,QAAQ,CAAI,IAAM,OAAO,IAC3E,OAAA,EAAOE,EAAAA,WAAAA,CAAW,QAAQ,CAAC,CAAC,OAAO,CAAK,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,EAAG,EAAS,CAC5E,CAGA,SAAgB,EAAQ,EAAsB,CAC5C,OAAA,EAAOA,EAAAA,WAAAA,CAAW,QAAQ,CAAC,CAAC,OAAO,CAAI,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,EAAG,CAAC,CACnE"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import e from"node:fs";import{createHash as t}from"node:crypto";import n from"node:path";function r(t){let r=n.join(t,`.git`),i;try{i=e.statSync(r)}catch{return}if(i.isDirectory())return r;let a;try{a=e.readFileSync(r,`utf8`).trim()}catch{return}if(!a.startsWith(`gitdir:`))return;let o=n.resolve(t,a.slice(7).trim());try{let t=e.readFileSync(n.join(o,`commondir`),`utf8`).trim();return n.resolve(o,t)}catch{return o}}function i(e){let t=n.basename(e).toLowerCase().replace(/[^a-z0-9]+/gu,`-`).replace(/^-+|-+$/gu,``).slice(0,32);return t===``?`repository`:t}function a(e){let i=r(e),a=i===void 0?`root:${n.resolve(e)}`:`git:${i}`;return t(`sha256`).update(a).digest(`hex`).slice(0,12)}function o(e){return t(`sha256`).update(e).digest(`hex`).slice(0,8)}export{r as gitCommonDirectory,a as repositoryId,i as repositoryLabel,o as shortId};
|
|
2
|
-
//# sourceMappingURL=repositoryIdentity.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"repositoryIdentity.mjs","names":[],"sources":["../../../src/adapters/worktree/repositoryIdentity.ts"],"sourcesContent":["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 */\nexport function gitCommonDirectory(root: string): string | undefined {\n const marker = path.join(root, '.git');\n let stat: fs.Stats;\n try {\n stat = fs.statSync(marker);\n } catch {\n return undefined;\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"],"mappings":"yFAgBA,SAAgB,EAAmB,EAAkC,CACnE,IAAM,EAAS,EAAK,KAAK,EAAM,MAAM,EACjC,EACJ,GAAI,CACF,EAAO,EAAG,SAAS,CAAM,CAC3B,MAAQ,CACN,MACF,CACA,GAAI,EAAK,YAAY,EAAG,OAAO,EAC/B,IAAI,EACJ,GAAI,CACF,EAAU,EAAG,aAAa,EAAQ,MAAM,CAAC,CAAC,KAAK,CACjD,MAAQ,CACN,MACF,CACA,GAAI,CAAC,EAAQ,WAAW,SAAS,EAAG,OACpC,IAAM,EAAS,EAAK,QAAQ,EAAM,EAAQ,MAAM,CAAgB,CAAC,CAAC,KAAK,CAAC,EACxE,GAAI,CACF,IAAM,EAAS,EAAG,aAAa,EAAK,KAAK,EAAQ,WAAW,EAAG,MAAM,CAAC,CAAC,KAAK,EAC5E,OAAO,EAAK,QAAQ,EAAQ,CAAM,CACpC,MAAQ,CAEN,OAAO,CACT,CACF,CAGA,SAAgB,EAAgB,EAAsB,CACpD,IAAM,EAAQ,EACX,SAAS,CAAI,CAAC,CACd,YAAY,CAAC,CACb,QAAQ,eAAgB,GAAG,CAAC,CAC5B,QAAQ,YAAa,EAAE,CAAC,CACxB,MAAM,EAAG,EAAgB,EAC5B,OAAO,IAAU,GAAK,aAAe,CACvC,CAUA,SAAgB,EAAa,EAAsB,CACjD,IAAM,EAAS,EAAmB,CAAI,EAChC,EAAQ,IAAW,IAAA,GAAY,QAAQ,EAAK,QAAQ,CAAI,IAAM,OAAO,IAC3E,OAAO,EAAW,QAAQ,CAAC,CAAC,OAAO,CAAK,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,EAAG,EAAS,CAC5E,CAGA,SAAgB,EAAQ,EAAsB,CAC5C,OAAO,EAAW,QAAQ,CAAC,CAAC,OAAO,CAAI,CAAC,CAAC,OAAO,KAAK,CAAC,CAAC,MAAM,EAAG,CAAC,CACnE"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const e=require("../../_virtual/_rolldown/runtime.cjs"),t=require("./dependencyInstall.cjs"),n=require("../hub/hubClient.cjs"),r=require("./repositoryIdentity.cjs"),i=require("../filesystem/paths.cjs"),a=require("../channel/worktreeChannel.cjs"),o=require("./worktreeRegistry.cjs"),s=require("../../services/worktreeNaming.cjs"),c=require("../../services/support/errors.cjs");let l=require("node:fs");l=e.__toESM(l,1);let u=require("node:crypto");function d(e){let{git:d}=e,f=e.createSession??n.createWorktreeSession,p=e.install??t.installDependencies,m=e.stopSession??n.stopWorktreeSession,h=e.isSessionLive??n.sessionIsLive,g=e.registryDir??i.hubRegistryDir(),_=e.now??(()=>new Date),v={alive:e=>h(g,e.sessionId),exists:e=>l.default.existsSync(e)},y=async t=>{let n=await d.repositoryRoot(t.cwd);if(n===void 0)throw new c.DoomGitExpectedError(`not_a_repository`,`${t.cwd} is not inside a git repository.`,!1,`Run this from a repository checkout.`);let r=o.createWorktreeRegistry(i.registryFile(n,e.homeDir));return{root:n,store:r,records:r.list()}},b=(e,t)=>{let n=e.find(e=>e.id===t);if(n===void 0)throw new c.DoomGitExpectedError(`worktree_not_found`,`No worktree with id ${t}.`,!1,`Call list to see the current ids.`);return n},x=(e,t,n)=>{if(t.parentSessionId!==e.sessionId&&h(g,t.parentSessionId))throw new c.DoomGitExpectedError(`worktree_not_owned`,`Worktree ${t.id} belongs to another session.`,!1,`Ask the session that created it to ${n} it, or close that session first.`)};return{async spawn(t,a){let{root:o,store:l,records:m}=await y(t),h=s.refuseSpawn({branch:a.branch,existing:m});if(h!==void 0)throw new c.DoomGitExpectedError(`worktree_exists`,h,!1,`Pick another branch name, or close it.`);let v=a.baseRef??await d.currentBranch(o)??`HEAD`,b=r.shortId((0,u.randomUUID)()),x=s.worktreeDirectory({worktreesRoot:i.worktreesRoot(e.homeDir),repositoryLabel:r.repositoryLabel(o),repositoryId:r.repositoryId(o),branch:a.branch,shortId:b});await d.addWorktree({repositoryRoot:o,path:x,branch:a.branch,baseRef:v});let S=await p(x);if(S.kind===`failed`)throw await d.removeWorktree({repositoryRoot:o,path:x,force:!0}).catch(()=>void 0),new c.DoomGitExpectedError(`install_failed`,`${S.command} install failed in the new worktree (exit ${String(S.code??`unknown`)}).`,!0,`Run the install by hand in the repository to see what it reports, then try again.`);let C;try{C=await f({cwd:x,name:a.name??a.branch,parentSessionId:t.sessionId,registryDir:g})}catch(e){throw await d.removeWorktree({repositoryRoot:o,path:x,force:!0}).catch(()=>void 0),e instanceof n.HubUnavailableError?new c.DoomGitExpectedError(`hub_unavailable`,e.message,!0,`Start the cockpit and try again.`):e}let w={version:1,id:b,branch:a.branch,baseRef:v,path:x,repositoryRoot:o,sessionId:C,parentSessionId:t.sessionId,status:`running`,createdAt:_().toISOString()};return l.replace([...m,w]),w},async close(e,t,n){let{root:r,store:i,records:a}=await y(e),o=b(a,t);x(e,o,`close`);let u=l.default.existsSync(o.path)?await d.dirtyFiles(o.path):[],f=s.refuseClose({record:o,dirtyFiles:u,force:n});if(f!==void 0)throw new c.DoomGitExpectedError(`worktree_dirty`,f,!1,`Commit the work, or pass force: true.`);return await m(g,o.sessionId),await d.removeWorktree({repositoryRoot:r,path:o.path,force:n}),i.replace(a.filter(e=>e.id!==t)),o},async list(e){let{store:t,records:n}=await y(e),r=s.reconcile(n,v);return r.changed.length>0&&t.replace(r.records),r.records},async status(e,t){let{records:n}=await y(e),r=b(n,t);return{record:r,dirtyFiles:l.default.existsSync(r.path)?await d.dirtyFiles(r.path):[]}},async merge(e,t,n){let{root:r,records:i}=await y(e),a=b(i,t);x(e,a,`merge`);let o=await d.dirtyFiles(r);if(o.length>0)throw new c.DoomGitExpectedError(`worktree_dirty`,`The parent checkout has ${String(o.length)} uncommitted file(s): ${o.slice(0,10).join(`, `)}.`,!1,`Commit or stash the parent checkout first.`);return await d.mergeBranch({repositoryRoot:r,branch:a.branch,...n===void 0?{}:{message:n}}),a},async send(t,n,r){let{records:o}=await y(t),s=b(o,n),c=t.sessionId===s.sessionId?`parent`:`child`;a.createWorktreeChannel(i.channelRoot(s.id,e.homeDir)).send(c,r)},async messages(t,n){let{records:r}=await y(t),o=b(r,n),s=a.createWorktreeChannel(i.channelRoot(o.id,e.homeDir));s.sweep();let c=t.sessionId===o.sessionId?`child`:`parent`;return s.receive(c)},async prune(t,n){let{root:r,store:a,records:o}=await y(t),c=s.reconcile(o,v),u=await d.listWorktreePaths(r),f=new Map;for(let e of c.records)e.status===`orphaned`&&l.default.existsSync(e.path)&&f.set(e.path,await d.dirtyFiles(e.path));let p=s.planPrune({records:c.records,gitPaths:u,worktreesRoot:i.worktreesRoot(e.homeDir),dirtyByPath:f});if(n)return a.replace(c.records),p;for(let e of p.remove)await d.removeWorktree({repositoryRoot:r,path:e.path,force:!1});let m=new Set([...p.remove,...p.forget].map(e=>e.id));return a.replace(c.records.filter(e=>!m.has(e.id))),p}}}exports.createWorktreeOperations=d;
|
|
2
|
-
//# sourceMappingURL=worktreeOperations.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"worktreeOperations.cjs","names":["createWorktreeSession","installDependencies","stopWorktreeSession","sessionIsLive","hubRegistryDir","fs","DoomGitExpectedError","createWorktreeRegistry","registryFile","require","refuseSpawn","shortId","randomUUID","worktreeDirectory","worktreesRoot","repositoryLabel","repositoryId","HubUnavailableError","refuseClose","reconcile","channelRoot","createWorktreeChannel","planPrune"],"sources":["../../../src/adapters/worktree/worktreeOperations.ts"],"sourcesContent":["import fs from 'node:fs';\nimport { randomUUID } from 'node:crypto';\nimport { installDependencies } from './dependencyInstall.ts';\nimport { createWorktreeSession, HubUnavailableError, sessionIsLive, stopWorktreeSession } from '../hub/hubClient.ts';\nimport { channelRoot, hubRegistryDir, registryFile, worktreesRoot } from '../filesystem/paths.ts';\nimport { repositoryId, repositoryLabel, shortId } from './repositoryIdentity.ts';\nimport { createWorktreeChannel } from '../channel/worktreeChannel.ts';\nimport type { ChannelMessage, ChannelParty } from '../channel/worktreeChannel.ts';\nimport { createWorktreeRegistry } from './worktreeRegistry.ts';\nimport { WORKTREE_RECORD_VERSION } from '../../types/worktreeRegistry.ts';\nimport type { WorktreeGit, WorktreeRecord } from '../../types/worktreeRegistry.ts';\nimport { planPrune, reconcile, refuseClose, refuseSpawn, worktreeDirectory } from '../../services/worktreeNaming.ts';\nimport { DoomGitExpectedError } from '../../services/support/errors.ts';\n\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}\n\nexport interface WorktreeOperations {\n spawn(context: WorktreeContext, request: SpawnWorktreeRequest): 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<ChannelMessage[]>;\n}\n\nexport interface WorktreeOperationsDeps {\n git: WorktreeGit;\n /** Injected so tests never reach the network. */\n createSession?: typeof createWorktreeSession;\n /** Injected so tests never run a package manager. */\n install?: typeof installDependencies;\n stopSession?: typeof stopWorktreeSession;\n isSessionLive?: typeof sessionIsLive;\n homeDir?: string;\n registryDir?: 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 createSession = deps.createSession ?? createWorktreeSession;\n const install = deps.install ?? installDependencies;\n const stopSession = deps.stopSession ?? stopWorktreeSession;\n const isSessionLive = deps.isSessionLive ?? sessionIsLive;\n const registryDir = deps.registryDir ?? hubRegistryDir();\n const now = deps.now ?? (() => new Date());\n const probe = {\n alive: (record: WorktreeRecord) => isSessionLive(registryDir, 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 * registry file is shared with the cockpit panel and another process may\n * have written it since 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 (!isSessionLive(registryDir, 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 return {\n async spawn(context, request) {\n const { root, store, records } = await resolve(context);\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 baseRef = request.baseRef ?? (await git.currentBranch(root)) ?? 'HEAD';\n const id = shortId(randomUUID());\n const path = worktreeDirectory({\n worktreesRoot: worktreesRoot(deps.homeDir),\n repositoryLabel: repositoryLabel(root),\n repositoryId: repositoryId(root),\n branch: request.branch,\n shortId: id,\n });\n\n await git.addWorktree({ repositoryRoot: root, path, branch: request.branch, baseRef });\n\n // Before the session, not after. The session resolves this repository's\n // composition, and every workspace package path in it points at a\n // directory that exists only once dependencies are linked. Starting the\n // session first makes it fall back to the global bundle and quietly lose\n // the repository's own packages.\n const installed = await install(path);\n if (installed.kind === 'failed') {\n await git.removeWorktree({ repositoryRoot: root, path, force: true }).catch(() => undefined);\n throw new DoomGitExpectedError(\n 'install_failed',\n `${installed.command} install failed in the new worktree (exit ${String(installed.code ?? 'unknown')}).`,\n true,\n 'Run the install by hand in the repository to see what it reports, then try again.',\n );\n }\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 let sessionId: string;\n try {\n sessionId = await createSession({\n cwd: path,\n name: request.name ?? request.branch,\n parentSessionId: context.sessionId,\n registryDir,\n });\n } catch (error) {\n await git.removeWorktree({ repositoryRoot: root, path, force: true }).catch(() => undefined);\n if (error instanceof HubUnavailableError) {\n throw new DoomGitExpectedError('hub_unavailable', error.message, true, 'Start the cockpit and try again.');\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 store.replace([...records, record]);\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 stopSession(registryDir, record.sessionId);\n await git.removeWorktree({ repositoryRoot: root, path: record.path, force });\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 /**\n * Which side of the channel the caller is.\n *\n * A worktree session is the child of the record it belongs to, and the\n * parent for everything else. Deciding from the session id rather than a\n * flag means neither side can address itself by mistake.\n */\n async send(context, id, message) {\n const { records } = await resolve(context);\n const record = require(records, id);\n const party: ChannelParty = context.sessionId === record.sessionId ? 'parent' : 'child';\n createWorktreeChannel(channelRoot(record.id, deps.homeDir)).send(party, message);\n },\n\n async messages(context, id) {\n const { records } = await resolve(context);\n const record = require(records, id);\n const channel = createWorktreeChannel(channelRoot(record.id, deps.homeDir));\n channel.sweep();\n const party: ChannelParty = context.sessionId === record.sessionId ? 'child' : 'parent';\n return channel.receive(party);\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 }\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}\n"],"mappings":"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"}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import "../hub/hubClient.cjs";
|
|
2
|
-
import { ChannelMessage } from "../channel/worktreeChannel.cjs";
|
|
3
|
-
import { WorktreeRecord } from "../../types/worktreeRegistry.cjs";
|
|
4
|
-
import { planPrune } from "../../services/worktreeNaming.cjs";
|
|
5
|
-
//#region src/adapters/worktree/worktreeOperations.d.ts
|
|
6
|
-
interface WorktreeContext {
|
|
7
|
-
/** Where the calling session is working, used to find the repository. */
|
|
8
|
-
cwd: string;
|
|
9
|
-
/** The calling session, recorded as the new session's parent. */
|
|
10
|
-
sessionId: string;
|
|
11
|
-
}
|
|
12
|
-
interface SpawnWorktreeRequest {
|
|
13
|
-
branch: string;
|
|
14
|
-
baseRef?: string;
|
|
15
|
-
name?: string;
|
|
16
|
-
}
|
|
17
|
-
interface WorktreeOperations {
|
|
18
|
-
spawn(context: WorktreeContext, request: SpawnWorktreeRequest): Promise<WorktreeRecord>;
|
|
19
|
-
close(context: WorktreeContext, id: string, force: boolean): Promise<WorktreeRecord>;
|
|
20
|
-
list(context: WorktreeContext): Promise<WorktreeRecord[]>;
|
|
21
|
-
status(context: WorktreeContext, id: string): Promise<{
|
|
22
|
-
record: WorktreeRecord;
|
|
23
|
-
dirtyFiles: string[];
|
|
24
|
-
}>;
|
|
25
|
-
merge(context: WorktreeContext, id: string, message?: string): Promise<WorktreeRecord>;
|
|
26
|
-
prune(context: WorktreeContext, dryRun: boolean): Promise<ReturnType<typeof planPrune>>;
|
|
27
|
-
send(context: WorktreeContext, id: string, message: string): Promise<void>;
|
|
28
|
-
messages(context: WorktreeContext, id: string): Promise<ChannelMessage[]>;
|
|
29
|
-
}
|
|
30
|
-
//#endregion
|
|
31
|
-
export { SpawnWorktreeRequest, WorktreeContext, WorktreeOperations };
|
|
32
|
-
//# sourceMappingURL=worktreeOperations.d.cts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"worktreeOperations.d.cts","names":[],"sources":["../../../src/adapters/worktree/worktreeOperations.ts"],"mappings":";;;;;UAciB;;EAEf;;EAEA;;UAGe;EACf;EACA;EACA;;UAGe;EACf,MAAM,SAAS,iBAAiB,SAAS,uBAAuB,QAAQ;EACxE,MAAM,SAAS,iBAAiB,YAAY,iBAAiB,QAAQ;EACrE,KAAK,SAAS,kBAAkB,QAAQ;EACxC,OAAO,SAAS,iBAAiB,aAAa;IAAU,QAAQ;IAAgB;;EAChF,MAAM,SAAS,iBAAiB,YAAY,mBAAmB,QAAQ;EACvE,MAAM,SAAS,iBAAiB,kBAAkB,QAAQ,kBAAkB;EAC5E,KAAK,SAAS,iBAAiB,YAAY,kBAAkB;EAC7D,SAAS,SAAS,iBAAiB,aAAa,QAAQ"}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import "../hub/hubClient.mjs";
|
|
2
|
-
import { ChannelMessage } from "../channel/worktreeChannel.mjs";
|
|
3
|
-
import { WorktreeRecord } from "../../types/worktreeRegistry.mjs";
|
|
4
|
-
import { planPrune } from "../../services/worktreeNaming.mjs";
|
|
5
|
-
//#region src/adapters/worktree/worktreeOperations.d.ts
|
|
6
|
-
interface WorktreeContext {
|
|
7
|
-
/** Where the calling session is working, used to find the repository. */
|
|
8
|
-
cwd: string;
|
|
9
|
-
/** The calling session, recorded as the new session's parent. */
|
|
10
|
-
sessionId: string;
|
|
11
|
-
}
|
|
12
|
-
interface SpawnWorktreeRequest {
|
|
13
|
-
branch: string;
|
|
14
|
-
baseRef?: string;
|
|
15
|
-
name?: string;
|
|
16
|
-
}
|
|
17
|
-
interface WorktreeOperations {
|
|
18
|
-
spawn(context: WorktreeContext, request: SpawnWorktreeRequest): Promise<WorktreeRecord>;
|
|
19
|
-
close(context: WorktreeContext, id: string, force: boolean): Promise<WorktreeRecord>;
|
|
20
|
-
list(context: WorktreeContext): Promise<WorktreeRecord[]>;
|
|
21
|
-
status(context: WorktreeContext, id: string): Promise<{
|
|
22
|
-
record: WorktreeRecord;
|
|
23
|
-
dirtyFiles: string[];
|
|
24
|
-
}>;
|
|
25
|
-
merge(context: WorktreeContext, id: string, message?: string): Promise<WorktreeRecord>;
|
|
26
|
-
prune(context: WorktreeContext, dryRun: boolean): Promise<ReturnType<typeof planPrune>>;
|
|
27
|
-
send(context: WorktreeContext, id: string, message: string): Promise<void>;
|
|
28
|
-
messages(context: WorktreeContext, id: string): Promise<ChannelMessage[]>;
|
|
29
|
-
}
|
|
30
|
-
//#endregion
|
|
31
|
-
export { SpawnWorktreeRequest, WorktreeContext, WorktreeOperations };
|
|
32
|
-
//# sourceMappingURL=worktreeOperations.d.mts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"worktreeOperations.d.mts","names":[],"sources":["../../../src/adapters/worktree/worktreeOperations.ts"],"mappings":";;;;;UAciB;;EAEf;;EAEA;;UAGe;EACf;EACA;EACA;;UAGe;EACf,MAAM,SAAS,iBAAiB,SAAS,uBAAuB,QAAQ;EACxE,MAAM,SAAS,iBAAiB,YAAY,iBAAiB,QAAQ;EACrE,KAAK,SAAS,kBAAkB,QAAQ;EACxC,OAAO,SAAS,iBAAiB,aAAa;IAAU,QAAQ;IAAgB;;EAChF,MAAM,SAAS,iBAAiB,YAAY,mBAAmB,QAAQ;EACvE,MAAM,SAAS,iBAAiB,kBAAkB,QAAQ,kBAAkB;EAC5E,KAAK,SAAS,iBAAiB,YAAY,kBAAkB;EAC7D,SAAS,SAAS,iBAAiB,aAAa,QAAQ"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{installDependencies as e}from"./dependencyInstall.mjs";import{HubUnavailableError as t,createWorktreeSession as n,sessionIsLive as r,stopWorktreeSession as i}from"../hub/hubClient.mjs";import{repositoryId as a,repositoryLabel as o,shortId as s}from"./repositoryIdentity.mjs";import{channelRoot as c,hubRegistryDir as l,registryFile as u,worktreesRoot as d}from"../filesystem/paths.mjs";import{createWorktreeChannel as f}from"../channel/worktreeChannel.mjs";import{createWorktreeRegistry as p}from"./worktreeRegistry.mjs";import{planPrune as m,reconcile as h,refuseClose as g,refuseSpawn as _,worktreeDirectory as v}from"../../services/worktreeNaming.mjs";import{DoomGitExpectedError as y}from"../../services/support/errors.mjs";import b from"node:fs";import{randomUUID as x}from"node:crypto";function S(S){let{git:C}=S,w=S.createSession??n,T=S.install??e,E=S.stopSession??i,D=S.isSessionLive??r,O=S.registryDir??l(),k=S.now??(()=>new Date),A={alive:e=>D(O,e.sessionId),exists:e=>b.existsSync(e)},j=async e=>{let t=await C.repositoryRoot(e.cwd);if(t===void 0)throw new y(`not_a_repository`,`${e.cwd} is not inside a git repository.`,!1,`Run this from a repository checkout.`);let n=p(u(t,S.homeDir));return{root:t,store:n,records:n.list()}},M=(e,t)=>{let n=e.find(e=>e.id===t);if(n===void 0)throw new y(`worktree_not_found`,`No worktree with id ${t}.`,!1,`Call list to see the current ids.`);return n},N=(e,t,n)=>{if(t.parentSessionId!==e.sessionId&&D(O,t.parentSessionId))throw new y(`worktree_not_owned`,`Worktree ${t.id} belongs to another session.`,!1,`Ask the session that created it to ${n} it, or close that session first.`)};return{async spawn(e,n){let{root:r,store:i,records:c}=await j(e),l=_({branch:n.branch,existing:c});if(l!==void 0)throw new y(`worktree_exists`,l,!1,`Pick another branch name, or close it.`);let u=n.baseRef??await C.currentBranch(r)??`HEAD`,f=s(x()),p=v({worktreesRoot:d(S.homeDir),repositoryLabel:o(r),repositoryId:a(r),branch:n.branch,shortId:f});await C.addWorktree({repositoryRoot:r,path:p,branch:n.branch,baseRef:u});let m=await T(p);if(m.kind===`failed`)throw await C.removeWorktree({repositoryRoot:r,path:p,force:!0}).catch(()=>void 0),new y(`install_failed`,`${m.command} install failed in the new worktree (exit ${String(m.code??`unknown`)}).`,!0,`Run the install by hand in the repository to see what it reports, then try again.`);let h;try{h=await w({cwd:p,name:n.name??n.branch,parentSessionId:e.sessionId,registryDir:O})}catch(e){throw await C.removeWorktree({repositoryRoot:r,path:p,force:!0}).catch(()=>void 0),e instanceof t?new y(`hub_unavailable`,e.message,!0,`Start the cockpit and try again.`):e}let g={version:1,id:f,branch:n.branch,baseRef:u,path:p,repositoryRoot:r,sessionId:h,parentSessionId:e.sessionId,status:`running`,createdAt:k().toISOString()};return i.replace([...c,g]),g},async close(e,t,n){let{root:r,store:i,records:a}=await j(e),o=M(a,t);N(e,o,`close`);let s=b.existsSync(o.path)?await C.dirtyFiles(o.path):[],c=g({record:o,dirtyFiles:s,force:n});if(c!==void 0)throw new y(`worktree_dirty`,c,!1,`Commit the work, or pass force: true.`);return await E(O,o.sessionId),await C.removeWorktree({repositoryRoot:r,path:o.path,force:n}),i.replace(a.filter(e=>e.id!==t)),o},async list(e){let{store:t,records:n}=await j(e),r=h(n,A);return r.changed.length>0&&t.replace(r.records),r.records},async status(e,t){let{records:n}=await j(e),r=M(n,t);return{record:r,dirtyFiles:b.existsSync(r.path)?await C.dirtyFiles(r.path):[]}},async merge(e,t,n){let{root:r,records:i}=await j(e),a=M(i,t);N(e,a,`merge`);let o=await C.dirtyFiles(r);if(o.length>0)throw new y(`worktree_dirty`,`The parent checkout has ${String(o.length)} uncommitted file(s): ${o.slice(0,10).join(`, `)}.`,!1,`Commit or stash the parent checkout first.`);return await C.mergeBranch({repositoryRoot:r,branch:a.branch,...n===void 0?{}:{message:n}}),a},async send(e,t,n){let{records:r}=await j(e),i=M(r,t),a=e.sessionId===i.sessionId?`parent`:`child`;f(c(i.id,S.homeDir)).send(a,n)},async messages(e,t){let{records:n}=await j(e),r=M(n,t),i=f(c(r.id,S.homeDir));i.sweep();let a=e.sessionId===r.sessionId?`child`:`parent`;return i.receive(a)},async prune(e,t){let{root:n,store:r,records:i}=await j(e),a=h(i,A),o=await C.listWorktreePaths(n),s=new Map;for(let e of a.records)e.status===`orphaned`&&b.existsSync(e.path)&&s.set(e.path,await C.dirtyFiles(e.path));let c=m({records:a.records,gitPaths:o,worktreesRoot:d(S.homeDir),dirtyByPath:s});if(t)return r.replace(a.records),c;for(let e of c.remove)await C.removeWorktree({repositoryRoot:n,path:e.path,force:!1});let l=new Set([...c.remove,...c.forget].map(e=>e.id));return r.replace(a.records.filter(e=>!l.has(e.id))),c}}}export{S as createWorktreeOperations};
|
|
2
|
-
//# sourceMappingURL=worktreeOperations.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"worktreeOperations.mjs","names":[],"sources":["../../../src/adapters/worktree/worktreeOperations.ts"],"sourcesContent":["import fs from 'node:fs';\nimport { randomUUID } from 'node:crypto';\nimport { installDependencies } from './dependencyInstall.ts';\nimport { createWorktreeSession, HubUnavailableError, sessionIsLive, stopWorktreeSession } from '../hub/hubClient.ts';\nimport { channelRoot, hubRegistryDir, registryFile, worktreesRoot } from '../filesystem/paths.ts';\nimport { repositoryId, repositoryLabel, shortId } from './repositoryIdentity.ts';\nimport { createWorktreeChannel } from '../channel/worktreeChannel.ts';\nimport type { ChannelMessage, ChannelParty } from '../channel/worktreeChannel.ts';\nimport { createWorktreeRegistry } from './worktreeRegistry.ts';\nimport { WORKTREE_RECORD_VERSION } from '../../types/worktreeRegistry.ts';\nimport type { WorktreeGit, WorktreeRecord } from '../../types/worktreeRegistry.ts';\nimport { planPrune, reconcile, refuseClose, refuseSpawn, worktreeDirectory } from '../../services/worktreeNaming.ts';\nimport { DoomGitExpectedError } from '../../services/support/errors.ts';\n\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}\n\nexport interface WorktreeOperations {\n spawn(context: WorktreeContext, request: SpawnWorktreeRequest): 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<ChannelMessage[]>;\n}\n\nexport interface WorktreeOperationsDeps {\n git: WorktreeGit;\n /** Injected so tests never reach the network. */\n createSession?: typeof createWorktreeSession;\n /** Injected so tests never run a package manager. */\n install?: typeof installDependencies;\n stopSession?: typeof stopWorktreeSession;\n isSessionLive?: typeof sessionIsLive;\n homeDir?: string;\n registryDir?: 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 createSession = deps.createSession ?? createWorktreeSession;\n const install = deps.install ?? installDependencies;\n const stopSession = deps.stopSession ?? stopWorktreeSession;\n const isSessionLive = deps.isSessionLive ?? sessionIsLive;\n const registryDir = deps.registryDir ?? hubRegistryDir();\n const now = deps.now ?? (() => new Date());\n const probe = {\n alive: (record: WorktreeRecord) => isSessionLive(registryDir, 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 * registry file is shared with the cockpit panel and another process may\n * have written it since 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 (!isSessionLive(registryDir, 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 return {\n async spawn(context, request) {\n const { root, store, records } = await resolve(context);\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 baseRef = request.baseRef ?? (await git.currentBranch(root)) ?? 'HEAD';\n const id = shortId(randomUUID());\n const path = worktreeDirectory({\n worktreesRoot: worktreesRoot(deps.homeDir),\n repositoryLabel: repositoryLabel(root),\n repositoryId: repositoryId(root),\n branch: request.branch,\n shortId: id,\n });\n\n await git.addWorktree({ repositoryRoot: root, path, branch: request.branch, baseRef });\n\n // Before the session, not after. The session resolves this repository's\n // composition, and every workspace package path in it points at a\n // directory that exists only once dependencies are linked. Starting the\n // session first makes it fall back to the global bundle and quietly lose\n // the repository's own packages.\n const installed = await install(path);\n if (installed.kind === 'failed') {\n await git.removeWorktree({ repositoryRoot: root, path, force: true }).catch(() => undefined);\n throw new DoomGitExpectedError(\n 'install_failed',\n `${installed.command} install failed in the new worktree (exit ${String(installed.code ?? 'unknown')}).`,\n true,\n 'Run the install by hand in the repository to see what it reports, then try again.',\n );\n }\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 let sessionId: string;\n try {\n sessionId = await createSession({\n cwd: path,\n name: request.name ?? request.branch,\n parentSessionId: context.sessionId,\n registryDir,\n });\n } catch (error) {\n await git.removeWorktree({ repositoryRoot: root, path, force: true }).catch(() => undefined);\n if (error instanceof HubUnavailableError) {\n throw new DoomGitExpectedError('hub_unavailable', error.message, true, 'Start the cockpit and try again.');\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 store.replace([...records, record]);\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 stopSession(registryDir, record.sessionId);\n await git.removeWorktree({ repositoryRoot: root, path: record.path, force });\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 /**\n * Which side of the channel the caller is.\n *\n * A worktree session is the child of the record it belongs to, and the\n * parent for everything else. Deciding from the session id rather than a\n * flag means neither side can address itself by mistake.\n */\n async send(context, id, message) {\n const { records } = await resolve(context);\n const record = require(records, id);\n const party: ChannelParty = context.sessionId === record.sessionId ? 'parent' : 'child';\n createWorktreeChannel(channelRoot(record.id, deps.homeDir)).send(party, message);\n },\n\n async messages(context, id) {\n const { records } = await resolve(context);\n const record = require(records, id);\n const channel = createWorktreeChannel(channelRoot(record.id, deps.homeDir));\n channel.sweep();\n const party: ChannelParty = context.sessionId === record.sessionId ? 'child' : 'parent';\n return channel.receive(party);\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 }\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}\n"],"mappings":"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"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const e=require("../filesystem/atomicJson.cjs");function t(e){if(typeof e!=`object`||!e)return!1;let t=e;return t.version===1&&typeof t.id==`string`&&t.id!==``&&typeof t.path==`string`&&typeof t.branch==`string`&&typeof t.sessionId==`string`}function n(n){return{list(){let r=e.readJson(n);return!r||r.version!==1||!Array.isArray(r.entries)?[]:r.entries.filter(t)},replace(t){e.writeJsonAtomic(n,{version:1,entries:t})}}}exports.createWorktreeRegistry=n;
|
|
2
|
-
//# sourceMappingURL=worktreeRegistry.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"worktreeRegistry.cjs","names":["readJson"],"sources":["../../../src/adapters/worktree/worktreeRegistry.ts"],"sourcesContent":["import { readJson, writeJsonAtomic } from '../filesystem/atomicJson.ts';\nimport {\n WORKTREE_RECORD_VERSION,\n type WorktreeRecord,\n type WorktreeRegistryFile,\n type WorktreeRegistryStore,\n} from '../../types/worktreeRegistry.ts';\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 * The worktree registry for one repository, as a single JSON file.\n *\n * One file rather than a file per worktree, and read-modify-write rather than\n * merging: every write comes from a tool call in one session's turn, so there\n * is no concurrent writer to reconcile with. Choosing the simpler shape now\n * means a reader can see the whole registry in one place; if concurrent writers\n * ever appear, that is the point to revisit, not before.\n *\n * A file that does not parse is treated as empty rather than fatal. Losing\n * track of worktrees is recoverable through prune; refusing to run is not.\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 };\n}\n"],"mappings":"gDAQA,SAAS,EAAS,EAAyC,CACzD,GAAI,OAAO,GAAU,WAAY,EAAgB,MAAO,GACxD,IAAM,EAAS,EACf,OACE,EAAO,UAAA,GACP,OAAO,EAAO,IAAO,UACrB,EAAO,KAAO,IACd,OAAO,EAAO,MAAS,UACvB,OAAO,EAAO,QAAW,UACzB,OAAO,EAAO,WAAc,QAEhC,CAcA,SAAgB,EAAuB,EAAqC,CAC1E,MAAO,CACL,MAAO,CACL,IAAM,EAASA,EAAAA,SAA+B,CAAI,EAElD,MADI,CAAC,GAAU,EAAO,UAAA,GAAuC,CAAC,MAAM,QAAQ,EAAO,OAAO,EAAU,CAAC,EAC9F,EAAO,QAAQ,OAAO,CAAQ,CACvC,EACA,QAAQ,EAAS,CACf,EAAA,gBAAgB,EAAM,CAAE,QAAA,EAAkC,SAAQ,CAAgC,CACpG,CACF,CACF"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{readJson as e,writeJsonAtomic as t}from"../filesystem/atomicJson.mjs";function n(e){if(typeof e!=`object`||!e)return!1;let t=e;return t.version===1&&typeof t.id==`string`&&t.id!==``&&typeof t.path==`string`&&typeof t.branch==`string`&&typeof t.sessionId==`string`}function r(r){return{list(){let t=e(r);return!t||t.version!==1||!Array.isArray(t.entries)?[]:t.entries.filter(n)},replace(e){t(r,{version:1,entries:e})}}}export{r as createWorktreeRegistry};
|
|
2
|
-
//# sourceMappingURL=worktreeRegistry.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"worktreeRegistry.mjs","names":[],"sources":["../../../src/adapters/worktree/worktreeRegistry.ts"],"sourcesContent":["import { readJson, writeJsonAtomic } from '../filesystem/atomicJson.ts';\nimport {\n WORKTREE_RECORD_VERSION,\n type WorktreeRecord,\n type WorktreeRegistryFile,\n type WorktreeRegistryStore,\n} from '../../types/worktreeRegistry.ts';\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 * The worktree registry for one repository, as a single JSON file.\n *\n * One file rather than a file per worktree, and read-modify-write rather than\n * merging: every write comes from a tool call in one session's turn, so there\n * is no concurrent writer to reconcile with. Choosing the simpler shape now\n * means a reader can see the whole registry in one place; if concurrent writers\n * ever appear, that is the point to revisit, not before.\n *\n * A file that does not parse is treated as empty rather than fatal. Losing\n * track of worktrees is recoverable through prune; refusing to run is not.\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 };\n}\n"],"mappings":"6EAQA,SAAS,EAAS,EAAyC,CACzD,GAAI,OAAO,GAAU,WAAY,EAAgB,MAAO,GACxD,IAAM,EAAS,EACf,OACE,EAAO,UAAA,GACP,OAAO,EAAO,IAAO,UACrB,EAAO,KAAO,IACd,OAAO,EAAO,MAAS,UACvB,OAAO,EAAO,QAAW,UACzB,OAAO,EAAO,WAAc,QAEhC,CAcA,SAAgB,EAAuB,EAAqC,CAC1E,MAAO,CACL,MAAO,CACL,IAAM,EAAS,EAA+B,CAAI,EAElD,MADI,CAAC,GAAU,EAAO,UAAA,GAAuC,CAAC,MAAM,QAAQ,EAAO,OAAO,EAAU,CAAC,EAC9F,EAAO,QAAQ,OAAO,CAAQ,CACvC,EACA,QAAQ,EAAS,CACf,EAAgB,EAAM,CAAE,QAAA,EAAkC,SAAQ,CAAgC,CACpG,CACF,CACF"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const e=`doom-git`,t=`Show git worktrees owned by this session`;function n(n,r){n.registerCommand(e,{description:t,handler:async(e,t)=>{let n=await r.execute();t.hasUI&&t.ui.notify(n.message,n.level)}})}exports.COMMAND_DESCRIPTION=t,exports.COMMAND_NAME=e,exports.registerGitCommand=n;
|
|
2
|
-
//# sourceMappingURL=doomGitCommand.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"doomGitCommand.cjs","names":[],"sources":["../../src/commands/doomGitCommand.ts"],"sourcesContent":["// @scaffold-generated\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport type { GitExtensionService } from '../types/extension.ts';\n\nexport const COMMAND_NAME = 'doom-git';\nexport const COMMAND_DESCRIPTION = 'Show git worktrees owned by this session';\n\nexport function registerGitCommand(pi: Pick<ExtensionAPI, 'registerCommand'>, service: GitExtensionService): void {\n pi.registerCommand(COMMAND_NAME, {\n description: COMMAND_DESCRIPTION,\n handler: async (_args, ctx) => {\n const result = await service.execute();\n if (ctx.hasUI) ctx.ui.notify(result.message, result.level);\n },\n });\n}\n"],"mappings":"AAIA,MAAa,EAAe,WACf,EAAsB,2CAEnC,SAAgB,EAAmB,EAA2C,EAAoC,CAChH,EAAG,gBAAgB,EAAc,CAC/B,YAAa,EACb,QAAS,MAAO,EAAO,IAAQ,CAC7B,IAAM,EAAS,MAAM,EAAQ,QAAQ,EACjC,EAAI,OAAO,EAAI,GAAG,OAAO,EAAO,QAAS,EAAO,KAAK,CAC3D,CACF,CAAC,CACH"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const e=`doom-git`,t=`Show git worktrees owned by this session`;function n(n,r){n.registerCommand(e,{description:t,handler:async(e,t)=>{let n=await r.execute();t.hasUI&&t.ui.notify(n.message,n.level)}})}export{t as COMMAND_DESCRIPTION,e as COMMAND_NAME,n as registerGitCommand};
|
|
2
|
-
//# sourceMappingURL=doomGitCommand.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"doomGitCommand.mjs","names":[],"sources":["../../src/commands/doomGitCommand.ts"],"sourcesContent":["// @scaffold-generated\nimport type { ExtensionAPI } from '@earendil-works/pi-coding-agent';\nimport type { GitExtensionService } from '../types/extension.ts';\n\nexport const COMMAND_NAME = 'doom-git';\nexport const COMMAND_DESCRIPTION = 'Show git worktrees owned by this session';\n\nexport function registerGitCommand(pi: Pick<ExtensionAPI, 'registerCommand'>, service: GitExtensionService): void {\n pi.registerCommand(COMMAND_NAME, {\n description: COMMAND_DESCRIPTION,\n handler: async (_args, ctx) => {\n const result = await service.execute();\n if (ctx.hasUI) ctx.ui.notify(result.message, result.level);\n },\n });\n}\n"],"mappings":"AAIA,MAAa,EAAe,WACf,EAAsB,2CAEnC,SAAgB,EAAmB,EAA2C,EAAoC,CAChH,EAAG,gBAAgB,EAAc,CAC/B,YAAa,EACb,QAAS,MAAO,EAAO,IAAQ,CAC7B,IAAM,EAAS,MAAM,EAAQ,QAAQ,EACjC,EAAI,OAAO,EAAI,GAAG,OAAO,EAAO,QAAS,EAAO,KAAK,CAC3D,CACF,CAAC,CACH"}
|
package/dist/container/index.cjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
const e=require("../adapters/worktree/gitCli.cjs"),t=require("../adapters/worktree/worktreeOperations.cjs"),n=require("../services/extensionService.cjs");function r(r={}){return{service:r.service??new n.DefaultGitExtensionService,operations:r.operations??t.createWorktreeOperations({git:e.createWorktreeGit()})}}exports.createGitContainer=r;
|
|
2
|
-
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["DefaultGitExtensionService","createWorktreeOperations","createWorktreeGit"],"sources":["../../src/container/index.ts"],"sourcesContent":["// @scaffold-generated\nimport { createWorktreeGit } from '../adapters/worktree/gitCli.ts';\nimport { DefaultGitExtensionService } from '../services/extensionService.ts';\nimport { createWorktreeOperations } from '../adapters/worktree/worktreeOperations.ts';\nimport type { GitExtensionDependencies } from '../types/extension.ts';\n\nexport function createGitContainer(overrides: Partial<GitExtensionDependencies> = {}): GitExtensionDependencies {\n return {\n service: overrides.service ?? new DefaultGitExtensionService(),\n operations: overrides.operations ?? createWorktreeOperations({ git: createWorktreeGit() }),\n };\n}\n"],"mappings":"0JAMA,SAAgB,EAAmB,EAA+C,CAAC,EAA6B,CAC9G,MAAO,CACL,QAAS,EAAU,SAAW,IAAIA,EAAAA,2BAClC,WAAY,EAAU,YAAcC,EAAAA,yBAAyB,CAAE,IAAKC,EAAAA,kBAAkB,CAAE,CAAC,CAC3F,CACF"}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { GitExtensionDependencies } from "../types/extension.cjs";
|
|
2
|
-
//#region src/container/index.d.ts
|
|
3
|
-
declare function createGitContainer(overrides?: Partial<GitExtensionDependencies>): GitExtensionDependencies;
|
|
4
|
-
//#endregion
|
|
5
|
-
export { createGitContainer };
|
|
6
|
-
//# sourceMappingURL=index.d.cts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.cts","names":[],"sources":["../../src/container/index.ts"],"mappings":";;iBAMgB,mBAAmB,YAAW,QAAQ,4BAAiC"}
|
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
import { GitExtensionDependencies } from "../types/extension.mjs";
|
|
2
|
-
//#region src/container/index.d.ts
|
|
3
|
-
declare function createGitContainer(overrides?: Partial<GitExtensionDependencies>): GitExtensionDependencies;
|
|
4
|
-
//#endregion
|
|
5
|
-
export { createGitContainer };
|
|
6
|
-
//# sourceMappingURL=index.d.mts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.mts","names":[],"sources":["../../src/container/index.ts"],"mappings":";;iBAMgB,mBAAmB,YAAW,QAAQ,4BAAiC"}
|
package/dist/container/index.mjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{createWorktreeGit as e}from"../adapters/worktree/gitCli.mjs";import{createWorktreeOperations as t}from"../adapters/worktree/worktreeOperations.mjs";import{DefaultGitExtensionService as n}from"../services/extensionService.mjs";function r(r={}){return{service:r.service??new n,operations:r.operations??t({git:e()})}}export{r as createGitContainer};
|
|
2
|
-
//# sourceMappingURL=index.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../../src/container/index.ts"],"sourcesContent":["// @scaffold-generated\nimport { createWorktreeGit } from '../adapters/worktree/gitCli.ts';\nimport { DefaultGitExtensionService } from '../services/extensionService.ts';\nimport { createWorktreeOperations } from '../adapters/worktree/worktreeOperations.ts';\nimport type { GitExtensionDependencies } from '../types/extension.ts';\n\nexport function createGitContainer(overrides: Partial<GitExtensionDependencies> = {}): GitExtensionDependencies {\n return {\n service: overrides.service ?? new DefaultGitExtensionService(),\n operations: overrides.operations ?? createWorktreeOperations({ git: createWorktreeGit() }),\n };\n}\n"],"mappings":"yOAMA,SAAgB,EAAmB,EAA+C,CAAC,EAA6B,CAC9G,MAAO,CACL,QAAS,EAAU,SAAW,IAAI,EAClC,WAAY,EAAU,YAAc,EAAyB,CAAE,IAAK,EAAkB,CAAE,CAAC,CAC3F,CACF"}
|
package/dist/extensions/pi.d.cts
DELETED
package/dist/hubApi.cjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require("./adapters/worktree/gitCli.cjs"),t=require("./services/support/errors.cjs"),n=require("./adapters/worktree/worktreeOperations.cjs");function r(e){return Response.json({error:e},{status:400})}function i(e){return e instanceof t.DoomGitExpectedError?Response.json({error:e.message,code:e.code},{status:e.retryable?503:409}):Response.json({error:`The worktree operation failed.`},{status:500})}const a={basePath:`git`,start(t){let a=n.createWorktreeOperations({git:e.createWorktreeGit()}),o=e=>{let n=e.searchParams.get(`repositoryId`);if(n===null||n===``)return r(`A repositoryId is required.`);let i=t.resolveRepository?.(n);return i===void 0?r(`That repository is not admitted to this cockpit.`):i};return{async fetch(e){let n=new URL(e.url),s=o(n);if(s instanceof Response)return s;let c={cwd:s,sessionId:n.searchParams.get(`sessionId`)??``};try{if(e.method===`GET`&&n.pathname===`/worktrees`)return Response.json({worktrees:await a.list(c)});if(e.method===`POST`&&n.pathname===`/worktrees`){let t=await e.json();if(typeof t.branch!=`string`||t.branch.trim()===``)return r(`A branch is required.`);let n=await a.spawn(c,{branch:t.branch,...t.baseRef===void 0?{}:{baseRef:t.baseRef},...t.name===void 0?{}:{name:t.name}});return Response.json({worktree:n},{status:201})}let t=/^\/worktrees\/([^/]+)$/u.exec(n.pathname);if(e.method===`DELETE`&&t){let e=await a.close(c,decodeURIComponent(String(t[1])),n.searchParams.get(`force`)===`true`);return Response.json({worktree:e})}return Response.json({error:`No such worktree route.`},{status:404})}catch(e){return t.onNotice(`worktree request failed: ${e.name}`),i(e)}},close(){}}}};exports.api=a;
|
|
2
|
-
//# sourceMappingURL=hubApi.cjs.map
|
package/dist/hubApi.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hubApi.cjs","names":["DoomGitExpectedError","createWorktreeOperations","createWorktreeGit"],"sources":["../src/exports/hubApi.ts"],"sourcesContent":["import type { DoomApi, DoomApiContext, DoomApiHandler } from '@agimon-ai/doompi-extension-contracts/package-api';\nimport { createWorktreeGit } from '../adapters/worktree/gitCli.ts';\nimport { createWorktreeOperations } from '../adapters/worktree/worktreeOperations.ts';\nimport { DoomGitExpectedError } from '../services/support/errors.ts';\n\n/**\n * The worktree surface the cockpit panel calls.\n *\n * Hub-scoped because worktrees are a property of a repository, not of one\n * session: the panel lists and creates them whether or not the session that\n * made them is focused, and a session-scoped API would disappear with it.\n *\n * The panel names a repository by the hub's opaque id and never by path.\n * `resolveRepository` turns that into an admitted root, so a browser cannot\n * point this at a directory the cockpit was never given.\n */\nfunction badRequest(message: string): Response {\n return Response.json({ error: message }, { status: 400 });\n}\n\nfunction failed(error: unknown): Response {\n if (error instanceof DoomGitExpectedError) {\n return Response.json({ error: error.message, code: error.code }, { status: error.retryable ? 503 : 409 });\n }\n // Never the raw message: a git failure can carry a remote URL with a token.\n return Response.json({ error: 'The worktree operation failed.' }, { status: 500 });\n}\n\nexport const api: DoomApi = {\n basePath: 'git',\n start(context: DoomApiContext): DoomApiHandler {\n const operations = createWorktreeOperations({ git: createWorktreeGit() });\n\n /** The admitted root for a request, or a response explaining why not. */\n const rootOf = (url: URL): string | Response => {\n const repositoryId = url.searchParams.get('repositoryId');\n if (repositoryId === null || repositoryId === '') return badRequest('A repositoryId is required.');\n const root = context.resolveRepository?.(repositoryId);\n if (root === undefined) return badRequest('That repository is not admitted to this cockpit.');\n return root;\n };\n\n return {\n async fetch(request) {\n const url = new URL(request.url);\n const root = rootOf(url);\n if (root instanceof Response) return root;\n // The operations layer works from a cwd, and an admitted repository\n // root is one.\n const callerContext = { cwd: root, sessionId: url.searchParams.get('sessionId') ?? '' };\n\n try {\n if (request.method === 'GET' && url.pathname === '/worktrees') {\n return Response.json({ worktrees: await operations.list(callerContext) });\n }\n if (request.method === 'POST' && url.pathname === '/worktrees') {\n const body = (await request.json()) as { branch?: string; baseRef?: string; name?: string };\n if (typeof body.branch !== 'string' || body.branch.trim() === '') {\n return badRequest('A branch is required.');\n }\n const record = await operations.spawn(callerContext, {\n branch: body.branch,\n ...(body.baseRef === undefined ? {} : { baseRef: body.baseRef }),\n ...(body.name === undefined ? {} : { name: body.name }),\n });\n return Response.json({ worktree: record }, { status: 201 });\n }\n const closing = /^\\/worktrees\\/([^/]+)$/u.exec(url.pathname);\n if (request.method === 'DELETE' && closing) {\n const record = await operations.close(\n callerContext,\n decodeURIComponent(String(closing[1])),\n url.searchParams.get('force') === 'true',\n );\n return Response.json({ worktree: record });\n }\n return Response.json({ error: 'No such worktree route.' }, { status: 404 });\n } catch (error) {\n context.onNotice(`worktree request failed: ${(error as Error).name}`);\n return failed(error);\n }\n },\n close() {},\n };\n },\n};\n"],"mappings":"wNAgBA,SAAS,EAAW,EAA2B,CAC7C,OAAO,SAAS,KAAK,CAAE,MAAO,CAAQ,EAAG,CAAE,OAAQ,GAAI,CAAC,CAC1D,CAEA,SAAS,EAAO,EAA0B,CAKxC,OAJI,aAAiBA,EAAAA,qBACZ,SAAS,KAAK,CAAE,MAAO,EAAM,QAAS,KAAM,EAAM,IAAK,EAAG,CAAE,OAAQ,EAAM,UAAY,IAAM,GAAI,CAAC,EAGnG,SAAS,KAAK,CAAE,MAAO,gCAAiC,EAAG,CAAE,OAAQ,GAAI,CAAC,CACnF,CAEA,MAAa,EAAe,CAC1B,SAAU,MACV,MAAM,EAAyC,CAC7C,IAAM,EAAaC,EAAAA,yBAAyB,CAAE,IAAKC,EAAAA,kBAAkB,CAAE,CAAC,EAGlE,EAAU,GAAgC,CAC9C,IAAM,EAAe,EAAI,aAAa,IAAI,cAAc,EACxD,GAAI,IAAiB,MAAQ,IAAiB,GAAI,OAAO,EAAW,6BAA6B,EACjG,IAAM,EAAO,EAAQ,oBAAoB,CAAY,EAErD,OADI,IAAS,IAAA,GAAkB,EAAW,kDAAkD,EACrF,CACT,EAEA,MAAO,CACL,MAAM,MAAM,EAAS,CACnB,IAAM,EAAM,IAAI,IAAI,EAAQ,GAAG,EACzB,EAAO,EAAO,CAAG,EACvB,GAAI,aAAgB,SAAU,OAAO,EAGrC,IAAM,EAAgB,CAAE,IAAK,EAAM,UAAW,EAAI,aAAa,IAAI,WAAW,GAAK,EAAG,EAEtF,GAAI,CACF,GAAI,EAAQ,SAAW,OAAS,EAAI,WAAa,aAC/C,OAAO,SAAS,KAAK,CAAE,UAAW,MAAM,EAAW,KAAK,CAAa,CAAE,CAAC,EAE1E,GAAI,EAAQ,SAAW,QAAU,EAAI,WAAa,aAAc,CAC9D,IAAM,EAAQ,MAAM,EAAQ,KAAK,EACjC,GAAI,OAAO,EAAK,QAAW,UAAY,EAAK,OAAO,KAAK,IAAM,GAC5D,OAAO,EAAW,uBAAuB,EAE3C,IAAM,EAAS,MAAM,EAAW,MAAM,EAAe,CACnD,OAAQ,EAAK,OACb,GAAI,EAAK,UAAY,IAAA,GAAY,CAAC,EAAI,CAAE,QAAS,EAAK,OAAQ,EAC9D,GAAI,EAAK,OAAS,IAAA,GAAY,CAAC,EAAI,CAAE,KAAM,EAAK,IAAK,CACvD,CAAC,EACD,OAAO,SAAS,KAAK,CAAE,SAAU,CAAO,EAAG,CAAE,OAAQ,GAAI,CAAC,CAC5D,CACA,IAAM,EAAU,0BAA0B,KAAK,EAAI,QAAQ,EAC3D,GAAI,EAAQ,SAAW,UAAY,EAAS,CAC1C,IAAM,EAAS,MAAM,EAAW,MAC9B,EACA,mBAAmB,OAAO,EAAQ,EAAE,CAAC,EACrC,EAAI,aAAa,IAAI,OAAO,IAAM,MACpC,EACA,OAAO,SAAS,KAAK,CAAE,SAAU,CAAO,CAAC,CAC3C,CACA,OAAO,SAAS,KAAK,CAAE,MAAO,yBAA0B,EAAG,CAAE,OAAQ,GAAI,CAAC,CAC5E,OAAS,EAAO,CAEd,OADA,EAAQ,SAAS,4BAA6B,EAAgB,MAAM,EAC7D,EAAO,CAAK,CACrB,CACF,EACA,OAAQ,CAAC,CACX,CACF,CACF"}
|
package/dist/hubApi.d.cts
DELETED
package/dist/hubApi.d.cts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hubApi.d.cts","names":[],"sources":["../src/exports/hubApi.ts"],"mappings":";;cA4Ba,KAAK"}
|
package/dist/hubApi.d.mts
DELETED
package/dist/hubApi.d.mts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hubApi.d.mts","names":[],"sources":["../src/exports/hubApi.ts"],"mappings":";;cA4Ba,KAAK"}
|
package/dist/hubApi.mjs
DELETED
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{createWorktreeGit as e}from"./adapters/worktree/gitCli.mjs";import{DoomGitExpectedError as t}from"./services/support/errors.mjs";import{createWorktreeOperations as n}from"./adapters/worktree/worktreeOperations.mjs";function r(e){return Response.json({error:e},{status:400})}function i(e){return e instanceof t?Response.json({error:e.message,code:e.code},{status:e.retryable?503:409}):Response.json({error:`The worktree operation failed.`},{status:500})}const a={basePath:`git`,start(t){let a=n({git:e()}),o=e=>{let n=e.searchParams.get(`repositoryId`);if(n===null||n===``)return r(`A repositoryId is required.`);let i=t.resolveRepository?.(n);return i===void 0?r(`That repository is not admitted to this cockpit.`):i};return{async fetch(e){let n=new URL(e.url),s=o(n);if(s instanceof Response)return s;let c={cwd:s,sessionId:n.searchParams.get(`sessionId`)??``};try{if(e.method===`GET`&&n.pathname===`/worktrees`)return Response.json({worktrees:await a.list(c)});if(e.method===`POST`&&n.pathname===`/worktrees`){let t=await e.json();if(typeof t.branch!=`string`||t.branch.trim()===``)return r(`A branch is required.`);let n=await a.spawn(c,{branch:t.branch,...t.baseRef===void 0?{}:{baseRef:t.baseRef},...t.name===void 0?{}:{name:t.name}});return Response.json({worktree:n},{status:201})}let t=/^\/worktrees\/([^/]+)$/u.exec(n.pathname);if(e.method===`DELETE`&&t){let e=await a.close(c,decodeURIComponent(String(t[1])),n.searchParams.get(`force`)===`true`);return Response.json({worktree:e})}return Response.json({error:`No such worktree route.`},{status:404})}catch(e){return t.onNotice(`worktree request failed: ${e.name}`),i(e)}},close(){}}}};export{a as api};
|
|
2
|
-
//# sourceMappingURL=hubApi.mjs.map
|
package/dist/hubApi.mjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"hubApi.mjs","names":[],"sources":["../src/exports/hubApi.ts"],"sourcesContent":["import type { DoomApi, DoomApiContext, DoomApiHandler } from '@agimon-ai/doompi-extension-contracts/package-api';\nimport { createWorktreeGit } from '../adapters/worktree/gitCli.ts';\nimport { createWorktreeOperations } from '../adapters/worktree/worktreeOperations.ts';\nimport { DoomGitExpectedError } from '../services/support/errors.ts';\n\n/**\n * The worktree surface the cockpit panel calls.\n *\n * Hub-scoped because worktrees are a property of a repository, not of one\n * session: the panel lists and creates them whether or not the session that\n * made them is focused, and a session-scoped API would disappear with it.\n *\n * The panel names a repository by the hub's opaque id and never by path.\n * `resolveRepository` turns that into an admitted root, so a browser cannot\n * point this at a directory the cockpit was never given.\n */\nfunction badRequest(message: string): Response {\n return Response.json({ error: message }, { status: 400 });\n}\n\nfunction failed(error: unknown): Response {\n if (error instanceof DoomGitExpectedError) {\n return Response.json({ error: error.message, code: error.code }, { status: error.retryable ? 503 : 409 });\n }\n // Never the raw message: a git failure can carry a remote URL with a token.\n return Response.json({ error: 'The worktree operation failed.' }, { status: 500 });\n}\n\nexport const api: DoomApi = {\n basePath: 'git',\n start(context: DoomApiContext): DoomApiHandler {\n const operations = createWorktreeOperations({ git: createWorktreeGit() });\n\n /** The admitted root for a request, or a response explaining why not. */\n const rootOf = (url: URL): string | Response => {\n const repositoryId = url.searchParams.get('repositoryId');\n if (repositoryId === null || repositoryId === '') return badRequest('A repositoryId is required.');\n const root = context.resolveRepository?.(repositoryId);\n if (root === undefined) return badRequest('That repository is not admitted to this cockpit.');\n return root;\n };\n\n return {\n async fetch(request) {\n const url = new URL(request.url);\n const root = rootOf(url);\n if (root instanceof Response) return root;\n // The operations layer works from a cwd, and an admitted repository\n // root is one.\n const callerContext = { cwd: root, sessionId: url.searchParams.get('sessionId') ?? '' };\n\n try {\n if (request.method === 'GET' && url.pathname === '/worktrees') {\n return Response.json({ worktrees: await operations.list(callerContext) });\n }\n if (request.method === 'POST' && url.pathname === '/worktrees') {\n const body = (await request.json()) as { branch?: string; baseRef?: string; name?: string };\n if (typeof body.branch !== 'string' || body.branch.trim() === '') {\n return badRequest('A branch is required.');\n }\n const record = await operations.spawn(callerContext, {\n branch: body.branch,\n ...(body.baseRef === undefined ? {} : { baseRef: body.baseRef }),\n ...(body.name === undefined ? {} : { name: body.name }),\n });\n return Response.json({ worktree: record }, { status: 201 });\n }\n const closing = /^\\/worktrees\\/([^/]+)$/u.exec(url.pathname);\n if (request.method === 'DELETE' && closing) {\n const record = await operations.close(\n callerContext,\n decodeURIComponent(String(closing[1])),\n url.searchParams.get('force') === 'true',\n );\n return Response.json({ worktree: record });\n }\n return Response.json({ error: 'No such worktree route.' }, { status: 404 });\n } catch (error) {\n context.onNotice(`worktree request failed: ${(error as Error).name}`);\n return failed(error);\n }\n },\n close() {},\n };\n },\n};\n"],"mappings":"8NAgBA,SAAS,EAAW,EAA2B,CAC7C,OAAO,SAAS,KAAK,CAAE,MAAO,CAAQ,EAAG,CAAE,OAAQ,GAAI,CAAC,CAC1D,CAEA,SAAS,EAAO,EAA0B,CAKxC,OAJI,aAAiB,EACZ,SAAS,KAAK,CAAE,MAAO,EAAM,QAAS,KAAM,EAAM,IAAK,EAAG,CAAE,OAAQ,EAAM,UAAY,IAAM,GAAI,CAAC,EAGnG,SAAS,KAAK,CAAE,MAAO,gCAAiC,EAAG,CAAE,OAAQ,GAAI,CAAC,CACnF,CAEA,MAAa,EAAe,CAC1B,SAAU,MACV,MAAM,EAAyC,CAC7C,IAAM,EAAa,EAAyB,CAAE,IAAK,EAAkB,CAAE,CAAC,EAGlE,EAAU,GAAgC,CAC9C,IAAM,EAAe,EAAI,aAAa,IAAI,cAAc,EACxD,GAAI,IAAiB,MAAQ,IAAiB,GAAI,OAAO,EAAW,6BAA6B,EACjG,IAAM,EAAO,EAAQ,oBAAoB,CAAY,EAErD,OADI,IAAS,IAAA,GAAkB,EAAW,kDAAkD,EACrF,CACT,EAEA,MAAO,CACL,MAAM,MAAM,EAAS,CACnB,IAAM,EAAM,IAAI,IAAI,EAAQ,GAAG,EACzB,EAAO,EAAO,CAAG,EACvB,GAAI,aAAgB,SAAU,OAAO,EAGrC,IAAM,EAAgB,CAAE,IAAK,EAAM,UAAW,EAAI,aAAa,IAAI,WAAW,GAAK,EAAG,EAEtF,GAAI,CACF,GAAI,EAAQ,SAAW,OAAS,EAAI,WAAa,aAC/C,OAAO,SAAS,KAAK,CAAE,UAAW,MAAM,EAAW,KAAK,CAAa,CAAE,CAAC,EAE1E,GAAI,EAAQ,SAAW,QAAU,EAAI,WAAa,aAAc,CAC9D,IAAM,EAAQ,MAAM,EAAQ,KAAK,EACjC,GAAI,OAAO,EAAK,QAAW,UAAY,EAAK,OAAO,KAAK,IAAM,GAC5D,OAAO,EAAW,uBAAuB,EAE3C,IAAM,EAAS,MAAM,EAAW,MAAM,EAAe,CACnD,OAAQ,EAAK,OACb,GAAI,EAAK,UAAY,IAAA,GAAY,CAAC,EAAI,CAAE,QAAS,EAAK,OAAQ,EAC9D,GAAI,EAAK,OAAS,IAAA,GAAY,CAAC,EAAI,CAAE,KAAM,EAAK,IAAK,CACvD,CAAC,EACD,OAAO,SAAS,KAAK,CAAE,SAAU,CAAO,EAAG,CAAE,OAAQ,GAAI,CAAC,CAC5D,CACA,IAAM,EAAU,0BAA0B,KAAK,EAAI,QAAQ,EAC3D,GAAI,EAAQ,SAAW,UAAY,EAAS,CAC1C,IAAM,EAAS,MAAM,EAAW,MAC9B,EACA,mBAAmB,OAAO,EAAQ,EAAE,CAAC,EACrC,EAAI,aAAa,IAAI,OAAO,IAAM,MACpC,EACA,OAAO,SAAS,KAAK,CAAE,SAAU,CAAO,CAAC,CAC3C,CACA,OAAO,SAAS,KAAK,CAAE,MAAO,yBAA0B,EAAG,CAAE,OAAQ,GAAI,CAAC,CAC5E,OAAS,EAAO,CAEd,OADA,EAAQ,SAAS,4BAA6B,EAAgB,MAAM,EAC7D,EAAO,CAAK,CACrB,CACF,EACA,OAAQ,CAAC,CACX,CACF,CACF"}
|
package/dist/index.d.cts
DELETED
|
@@ -1,5 +0,0 @@
|
|
|
1
|
-
import { GitExtensionDependencies, GitExtensionResult, GitExtensionService } from "./types/extension.cjs";
|
|
2
|
-
import { activateGitExtension } from "./adapters/pi/extension.cjs";
|
|
3
|
-
import { DefaultGitExtensionService } from "./services/extensionService.cjs";
|
|
4
|
-
import { createGitContainer } from "./container/index.cjs";
|
|
5
|
-
export { DefaultGitExtensionService, type GitExtensionDependencies, type GitExtensionResult, type GitExtensionService, activateGitExtension, createGitContainer };
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
let e=require("typebox");const t={spawn_worktree:`spawn_worktree`,close_worktree:`close_worktree`,list:`list`,status:`status`,merge:`merge`,prune:`prune`,send:`send`,messages:`messages`},n={spawn_worktree:[`action`,`branch`,`baseRef`,`task`,`name`],close_worktree:[`action`,`id`,`force`],list:[`action`],status:[`action`,`id`],merge:[`action`,`id`,`message`],prune:[`action`,`dryRun`],send:[`action`,`id`,`message`],messages:[`action`,`id`]},r=e.Type.Object({action:e.Type.Literal(t.spawn_worktree),branch:e.Type.String({minLength:1}),baseRef:e.Type.Optional(e.Type.String({minLength:1})),task:e.Type.Optional(e.Type.String({minLength:1})),name:e.Type.Optional(e.Type.String({minLength:1}))},{additionalProperties:!1}),i=e.Type.Object({action:e.Type.Literal(t.close_worktree),id:e.Type.String({minLength:1}),force:e.Type.Optional(e.Type.Boolean())},{additionalProperties:!1}),a=e.Type.Object({action:e.Type.Literal(t.list)},{additionalProperties:!1}),o=e.Type.Object({action:e.Type.Literal(t.status),id:e.Type.String({minLength:1})},{additionalProperties:!1}),s=e.Type.Object({action:e.Type.Literal(t.merge),id:e.Type.String({minLength:1}),message:e.Type.Optional(e.Type.String({minLength:1}))},{additionalProperties:!1}),c=e.Type.Object({action:e.Type.Literal(t.prune),dryRun:e.Type.Optional(e.Type.Boolean())},{additionalProperties:!1}),l=e.Type.Object({action:e.Type.Literal(t.send),id:e.Type.String({minLength:1}),message:e.Type.String({minLength:1})},{additionalProperties:!1}),u=e.Type.Object({action:e.Type.Literal(t.messages),id:e.Type.String({minLength:1})},{additionalProperties:!1}),d=e.Type.Union([r,i,a,o,s,c,l,u]);exports.RunWorktreeParams=d,exports.WORKTREE_ACTIONS=t,exports.WORKTREE_ACTION_FIELDS=n;
|
|
2
|
-
//# sourceMappingURL=runWorktreeTool.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"runWorktreeTool.cjs","names":["Type"],"sources":["../../src/schemas/runWorktreeTool.ts"],"sourcesContent":["import { type Static, Type } from 'typebox';\n\n/**\n * The tool's action vocabulary.\n *\n * One tool with an action field rather than six tools, matching `subagent`:\n * the actions share a subject and are meaningless apart from it, and six\n * separate tools would spend six description budgets teaching the same nouns.\n */\nexport const WORKTREE_ACTIONS = {\n spawn_worktree: 'spawn_worktree',\n close_worktree: 'close_worktree',\n list: 'list',\n status: 'status',\n merge: 'merge',\n prune: 'prune',\n send: 'send',\n messages: 'messages',\n} as const;\n\nexport type WorktreeAction = (typeof WORKTREE_ACTIONS)[keyof typeof WORKTREE_ACTIONS];\n\n/**\n * Which fields each action accepts.\n *\n * Rejecting an unknown field is what stops a plausible-looking call from doing\n * something other than what it reads like: `close_worktree` with `branch`\n * instead of `id` would otherwise close whatever the default resolved to.\n */\nexport const WORKTREE_ACTION_FIELDS: Record<WorktreeAction, readonly string[]> = {\n spawn_worktree: ['action', 'branch', 'baseRef', 'task', 'name'],\n close_worktree: ['action', 'id', 'force'],\n list: ['action'],\n status: ['action', 'id'],\n merge: ['action', 'id', 'message'],\n prune: ['action', 'dryRun'],\n send: ['action', 'id', 'message'],\n messages: ['action', 'id'],\n};\n\nconst SpawnParams = Type.Object(\n {\n action: Type.Literal(WORKTREE_ACTIONS.spawn_worktree),\n branch: Type.String({ minLength: 1 }),\n baseRef: Type.Optional(Type.String({ minLength: 1 })),\n task: Type.Optional(Type.String({ minLength: 1 })),\n name: Type.Optional(Type.String({ minLength: 1 })),\n },\n { additionalProperties: false },\n);\n\nconst CloseParams = Type.Object(\n {\n action: Type.Literal(WORKTREE_ACTIONS.close_worktree),\n id: Type.String({ minLength: 1 }),\n force: Type.Optional(Type.Boolean()),\n },\n { additionalProperties: false },\n);\n\nconst ListParams = Type.Object({ action: Type.Literal(WORKTREE_ACTIONS.list) }, { additionalProperties: false });\n\nconst StatusParams = Type.Object(\n { action: Type.Literal(WORKTREE_ACTIONS.status), id: Type.String({ minLength: 1 }) },\n { additionalProperties: false },\n);\n\nconst MergeParams = Type.Object(\n {\n action: Type.Literal(WORKTREE_ACTIONS.merge),\n id: Type.String({ minLength: 1 }),\n message: Type.Optional(Type.String({ minLength: 1 })),\n },\n { additionalProperties: false },\n);\n\nconst PruneParams = Type.Object(\n { action: Type.Literal(WORKTREE_ACTIONS.prune), dryRun: Type.Optional(Type.Boolean()) },\n { additionalProperties: false },\n);\n\nconst SendParams = Type.Object(\n {\n action: Type.Literal(WORKTREE_ACTIONS.send),\n id: Type.String({ minLength: 1 }),\n message: Type.String({ minLength: 1 }),\n },\n { additionalProperties: false },\n);\n\nconst MessagesParams = Type.Object(\n { action: Type.Literal(WORKTREE_ACTIONS.messages), id: Type.String({ minLength: 1 }) },\n { additionalProperties: false },\n);\n\nexport const RunWorktreeParams = Type.Union([\n SpawnParams,\n CloseParams,\n ListParams,\n StatusParams,\n MergeParams,\n PruneParams,\n SendParams,\n MessagesParams,\n]);\n\nexport type RunWorktreeToolParams = Static<typeof RunWorktreeParams>;\n"],"mappings":"yBASA,MAAa,EAAmB,CAC9B,eAAgB,iBAChB,eAAgB,iBAChB,KAAM,OACN,OAAQ,SACR,MAAO,QACP,MAAO,QACP,KAAM,OACN,SAAU,UACZ,EAWa,EAAoE,CAC/E,eAAgB,CAAC,SAAU,SAAU,UAAW,OAAQ,MAAM,EAC9D,eAAgB,CAAC,SAAU,KAAM,OAAO,EACxC,KAAM,CAAC,QAAQ,EACf,OAAQ,CAAC,SAAU,IAAI,EACvB,MAAO,CAAC,SAAU,KAAM,SAAS,EACjC,MAAO,CAAC,SAAU,QAAQ,EAC1B,KAAM,CAAC,SAAU,KAAM,SAAS,EAChC,SAAU,CAAC,SAAU,IAAI,CAC3B,EAEM,EAAcA,EAAAA,KAAK,OACvB,CACE,OAAQA,EAAAA,KAAK,QAAQ,EAAiB,cAAc,EACpD,OAAQA,EAAAA,KAAK,OAAO,CAAE,UAAW,CAAE,CAAC,EACpC,QAASA,EAAAA,KAAK,SAASA,EAAAA,KAAK,OAAO,CAAE,UAAW,CAAE,CAAC,CAAC,EACpD,KAAMA,EAAAA,KAAK,SAASA,EAAAA,KAAK,OAAO,CAAE,UAAW,CAAE,CAAC,CAAC,EACjD,KAAMA,EAAAA,KAAK,SAASA,EAAAA,KAAK,OAAO,CAAE,UAAW,CAAE,CAAC,CAAC,CACnD,EACA,CAAE,qBAAsB,EAAM,CAChC,EAEM,EAAcA,EAAAA,KAAK,OACvB,CACE,OAAQA,EAAAA,KAAK,QAAQ,EAAiB,cAAc,EACpD,GAAIA,EAAAA,KAAK,OAAO,CAAE,UAAW,CAAE,CAAC,EAChC,MAAOA,EAAAA,KAAK,SAASA,EAAAA,KAAK,QAAQ,CAAC,CACrC,EACA,CAAE,qBAAsB,EAAM,CAChC,EAEM,EAAaA,EAAAA,KAAK,OAAO,CAAE,OAAQA,EAAAA,KAAK,QAAQ,EAAiB,IAAI,CAAE,EAAG,CAAE,qBAAsB,EAAM,CAAC,EAEzG,EAAeA,EAAAA,KAAK,OACxB,CAAE,OAAQA,EAAAA,KAAK,QAAQ,EAAiB,MAAM,EAAG,GAAIA,EAAAA,KAAK,OAAO,CAAE,UAAW,CAAE,CAAC,CAAE,EACnF,CAAE,qBAAsB,EAAM,CAChC,EAEM,EAAcA,EAAAA,KAAK,OACvB,CACE,OAAQA,EAAAA,KAAK,QAAQ,EAAiB,KAAK,EAC3C,GAAIA,EAAAA,KAAK,OAAO,CAAE,UAAW,CAAE,CAAC,EAChC,QAASA,EAAAA,KAAK,SAASA,EAAAA,KAAK,OAAO,CAAE,UAAW,CAAE,CAAC,CAAC,CACtD,EACA,CAAE,qBAAsB,EAAM,CAChC,EAEM,EAAcA,EAAAA,KAAK,OACvB,CAAE,OAAQA,EAAAA,KAAK,QAAQ,EAAiB,KAAK,EAAG,OAAQA,EAAAA,KAAK,SAASA,EAAAA,KAAK,QAAQ,CAAC,CAAE,EACtF,CAAE,qBAAsB,EAAM,CAChC,EAEM,EAAaA,EAAAA,KAAK,OACtB,CACE,OAAQA,EAAAA,KAAK,QAAQ,EAAiB,IAAI,EAC1C,GAAIA,EAAAA,KAAK,OAAO,CAAE,UAAW,CAAE,CAAC,EAChC,QAASA,EAAAA,KAAK,OAAO,CAAE,UAAW,CAAE,CAAC,CACvC,EACA,CAAE,qBAAsB,EAAM,CAChC,EAEM,EAAiBA,EAAAA,KAAK,OAC1B,CAAE,OAAQA,EAAAA,KAAK,QAAQ,EAAiB,QAAQ,EAAG,GAAIA,EAAAA,KAAK,OAAO,CAAE,UAAW,CAAE,CAAC,CAAE,EACrF,CAAE,qBAAsB,EAAM,CAChC,EAEa,EAAoBA,EAAAA,KAAK,MAAM,CAC1C,EACA,EACA,EACA,EACA,EACA,EACA,EACA,CACF,CAAC"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
import{Type as e}from"typebox";const t={spawn_worktree:`spawn_worktree`,close_worktree:`close_worktree`,list:`list`,status:`status`,merge:`merge`,prune:`prune`,send:`send`,messages:`messages`},n={spawn_worktree:[`action`,`branch`,`baseRef`,`task`,`name`],close_worktree:[`action`,`id`,`force`],list:[`action`],status:[`action`,`id`],merge:[`action`,`id`,`message`],prune:[`action`,`dryRun`],send:[`action`,`id`,`message`],messages:[`action`,`id`]},r=e.Object({action:e.Literal(t.spawn_worktree),branch:e.String({minLength:1}),baseRef:e.Optional(e.String({minLength:1})),task:e.Optional(e.String({minLength:1})),name:e.Optional(e.String({minLength:1}))},{additionalProperties:!1}),i=e.Object({action:e.Literal(t.close_worktree),id:e.String({minLength:1}),force:e.Optional(e.Boolean())},{additionalProperties:!1}),a=e.Object({action:e.Literal(t.list)},{additionalProperties:!1}),o=e.Object({action:e.Literal(t.status),id:e.String({minLength:1})},{additionalProperties:!1}),s=e.Object({action:e.Literal(t.merge),id:e.String({minLength:1}),message:e.Optional(e.String({minLength:1}))},{additionalProperties:!1}),c=e.Object({action:e.Literal(t.prune),dryRun:e.Optional(e.Boolean())},{additionalProperties:!1}),l=e.Object({action:e.Literal(t.send),id:e.String({minLength:1}),message:e.String({minLength:1})},{additionalProperties:!1}),u=e.Object({action:e.Literal(t.messages),id:e.String({minLength:1})},{additionalProperties:!1}),d=e.Union([r,i,a,o,s,c,l,u]);export{d as RunWorktreeParams,t as WORKTREE_ACTIONS,n as WORKTREE_ACTION_FIELDS};
|
|
2
|
-
//# sourceMappingURL=runWorktreeTool.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"runWorktreeTool.mjs","names":[],"sources":["../../src/schemas/runWorktreeTool.ts"],"sourcesContent":["import { type Static, Type } from 'typebox';\n\n/**\n * The tool's action vocabulary.\n *\n * One tool with an action field rather than six tools, matching `subagent`:\n * the actions share a subject and are meaningless apart from it, and six\n * separate tools would spend six description budgets teaching the same nouns.\n */\nexport const WORKTREE_ACTIONS = {\n spawn_worktree: 'spawn_worktree',\n close_worktree: 'close_worktree',\n list: 'list',\n status: 'status',\n merge: 'merge',\n prune: 'prune',\n send: 'send',\n messages: 'messages',\n} as const;\n\nexport type WorktreeAction = (typeof WORKTREE_ACTIONS)[keyof typeof WORKTREE_ACTIONS];\n\n/**\n * Which fields each action accepts.\n *\n * Rejecting an unknown field is what stops a plausible-looking call from doing\n * something other than what it reads like: `close_worktree` with `branch`\n * instead of `id` would otherwise close whatever the default resolved to.\n */\nexport const WORKTREE_ACTION_FIELDS: Record<WorktreeAction, readonly string[]> = {\n spawn_worktree: ['action', 'branch', 'baseRef', 'task', 'name'],\n close_worktree: ['action', 'id', 'force'],\n list: ['action'],\n status: ['action', 'id'],\n merge: ['action', 'id', 'message'],\n prune: ['action', 'dryRun'],\n send: ['action', 'id', 'message'],\n messages: ['action', 'id'],\n};\n\nconst SpawnParams = Type.Object(\n {\n action: Type.Literal(WORKTREE_ACTIONS.spawn_worktree),\n branch: Type.String({ minLength: 1 }),\n baseRef: Type.Optional(Type.String({ minLength: 1 })),\n task: Type.Optional(Type.String({ minLength: 1 })),\n name: Type.Optional(Type.String({ minLength: 1 })),\n },\n { additionalProperties: false },\n);\n\nconst CloseParams = Type.Object(\n {\n action: Type.Literal(WORKTREE_ACTIONS.close_worktree),\n id: Type.String({ minLength: 1 }),\n force: Type.Optional(Type.Boolean()),\n },\n { additionalProperties: false },\n);\n\nconst ListParams = Type.Object({ action: Type.Literal(WORKTREE_ACTIONS.list) }, { additionalProperties: false });\n\nconst StatusParams = Type.Object(\n { action: Type.Literal(WORKTREE_ACTIONS.status), id: Type.String({ minLength: 1 }) },\n { additionalProperties: false },\n);\n\nconst MergeParams = Type.Object(\n {\n action: Type.Literal(WORKTREE_ACTIONS.merge),\n id: Type.String({ minLength: 1 }),\n message: Type.Optional(Type.String({ minLength: 1 })),\n },\n { additionalProperties: false },\n);\n\nconst PruneParams = Type.Object(\n { action: Type.Literal(WORKTREE_ACTIONS.prune), dryRun: Type.Optional(Type.Boolean()) },\n { additionalProperties: false },\n);\n\nconst SendParams = Type.Object(\n {\n action: Type.Literal(WORKTREE_ACTIONS.send),\n id: Type.String({ minLength: 1 }),\n message: Type.String({ minLength: 1 }),\n },\n { additionalProperties: false },\n);\n\nconst MessagesParams = Type.Object(\n { action: Type.Literal(WORKTREE_ACTIONS.messages), id: Type.String({ minLength: 1 }) },\n { additionalProperties: false },\n);\n\nexport const RunWorktreeParams = Type.Union([\n SpawnParams,\n CloseParams,\n ListParams,\n StatusParams,\n MergeParams,\n PruneParams,\n SendParams,\n MessagesParams,\n]);\n\nexport type RunWorktreeToolParams = Static<typeof RunWorktreeParams>;\n"],"mappings":"+BASA,MAAa,EAAmB,CAC9B,eAAgB,iBAChB,eAAgB,iBAChB,KAAM,OACN,OAAQ,SACR,MAAO,QACP,MAAO,QACP,KAAM,OACN,SAAU,UACZ,EAWa,EAAoE,CAC/E,eAAgB,CAAC,SAAU,SAAU,UAAW,OAAQ,MAAM,EAC9D,eAAgB,CAAC,SAAU,KAAM,OAAO,EACxC,KAAM,CAAC,QAAQ,EACf,OAAQ,CAAC,SAAU,IAAI,EACvB,MAAO,CAAC,SAAU,KAAM,SAAS,EACjC,MAAO,CAAC,SAAU,QAAQ,EAC1B,KAAM,CAAC,SAAU,KAAM,SAAS,EAChC,SAAU,CAAC,SAAU,IAAI,CAC3B,EAEM,EAAc,EAAK,OACvB,CACE,OAAQ,EAAK,QAAQ,EAAiB,cAAc,EACpD,OAAQ,EAAK,OAAO,CAAE,UAAW,CAAE,CAAC,EACpC,QAAS,EAAK,SAAS,EAAK,OAAO,CAAE,UAAW,CAAE,CAAC,CAAC,EACpD,KAAM,EAAK,SAAS,EAAK,OAAO,CAAE,UAAW,CAAE,CAAC,CAAC,EACjD,KAAM,EAAK,SAAS,EAAK,OAAO,CAAE,UAAW,CAAE,CAAC,CAAC,CACnD,EACA,CAAE,qBAAsB,EAAM,CAChC,EAEM,EAAc,EAAK,OACvB,CACE,OAAQ,EAAK,QAAQ,EAAiB,cAAc,EACpD,GAAI,EAAK,OAAO,CAAE,UAAW,CAAE,CAAC,EAChC,MAAO,EAAK,SAAS,EAAK,QAAQ,CAAC,CACrC,EACA,CAAE,qBAAsB,EAAM,CAChC,EAEM,EAAa,EAAK,OAAO,CAAE,OAAQ,EAAK,QAAQ,EAAiB,IAAI,CAAE,EAAG,CAAE,qBAAsB,EAAM,CAAC,EAEzG,EAAe,EAAK,OACxB,CAAE,OAAQ,EAAK,QAAQ,EAAiB,MAAM,EAAG,GAAI,EAAK,OAAO,CAAE,UAAW,CAAE,CAAC,CAAE,EACnF,CAAE,qBAAsB,EAAM,CAChC,EAEM,EAAc,EAAK,OACvB,CACE,OAAQ,EAAK,QAAQ,EAAiB,KAAK,EAC3C,GAAI,EAAK,OAAO,CAAE,UAAW,CAAE,CAAC,EAChC,QAAS,EAAK,SAAS,EAAK,OAAO,CAAE,UAAW,CAAE,CAAC,CAAC,CACtD,EACA,CAAE,qBAAsB,EAAM,CAChC,EAEM,EAAc,EAAK,OACvB,CAAE,OAAQ,EAAK,QAAQ,EAAiB,KAAK,EAAG,OAAQ,EAAK,SAAS,EAAK,QAAQ,CAAC,CAAE,EACtF,CAAE,qBAAsB,EAAM,CAChC,EAEM,EAAa,EAAK,OACtB,CACE,OAAQ,EAAK,QAAQ,EAAiB,IAAI,EAC1C,GAAI,EAAK,OAAO,CAAE,UAAW,CAAE,CAAC,EAChC,QAAS,EAAK,OAAO,CAAE,UAAW,CAAE,CAAC,CACvC,EACA,CAAE,qBAAsB,EAAM,CAChC,EAEM,EAAiB,EAAK,OAC1B,CAAE,OAAQ,EAAK,QAAQ,EAAiB,QAAQ,EAAG,GAAI,EAAK,OAAO,CAAE,UAAW,CAAE,CAAC,CAAE,EACrF,CAAE,qBAAsB,EAAM,CAChC,EAEa,EAAoB,EAAK,MAAM,CAC1C,EACA,EACA,EACA,EACA,EACA,EACA,EACA,CACF,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"extensionService.cjs","names":[],"sources":["../../src/services/extensionService.ts"],"sourcesContent":["// @scaffold-generated\nimport type { GitExtensionResult, GitExtensionService } from '../types/extension.ts';\n\nexport class DefaultGitExtensionService implements GitExtensionService {\n async execute(): Promise<GitExtensionResult> {\n return { message: 'Show git worktrees owned by this session', level: 'info' };\n }\n}\n"],"mappings":"AAGA,IAAa,EAAb,KAAuE,CACrE,MAAM,SAAuC,CAC3C,MAAO,CAAE,QAAS,2CAA4C,MAAO,MAAO,CAC9E,CACF"}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { GitExtensionResult, GitExtensionService } from "../types/extension.cjs";
|
|
2
|
-
//#region src/services/extensionService.d.ts
|
|
3
|
-
declare class DefaultGitExtensionService implements GitExtensionService {
|
|
4
|
-
execute(): Promise<GitExtensionResult>;
|
|
5
|
-
}
|
|
6
|
-
//#endregion
|
|
7
|
-
export { DefaultGitExtensionService };
|
|
8
|
-
//# sourceMappingURL=extensionService.d.cts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"extensionService.d.cts","names":[],"sources":["../../src/services/extensionService.ts"],"mappings":";;cAGa,sCAAsC;EAC3C,WAAW,QAAQ"}
|
|
@@ -1,8 +0,0 @@
|
|
|
1
|
-
import { GitExtensionResult, GitExtensionService } from "../types/extension.mjs";
|
|
2
|
-
//#region src/services/extensionService.d.ts
|
|
3
|
-
declare class DefaultGitExtensionService implements GitExtensionService {
|
|
4
|
-
execute(): Promise<GitExtensionResult>;
|
|
5
|
-
}
|
|
6
|
-
//#endregion
|
|
7
|
-
export { DefaultGitExtensionService };
|
|
8
|
-
//# sourceMappingURL=extensionService.d.mts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"extensionService.d.mts","names":[],"sources":["../../src/services/extensionService.ts"],"mappings":";;cAGa,sCAAsC;EAC3C,WAAW,QAAQ"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"extensionService.mjs","names":[],"sources":["../../src/services/extensionService.ts"],"sourcesContent":["// @scaffold-generated\nimport type { GitExtensionResult, GitExtensionService } from '../types/extension.ts';\n\nexport class DefaultGitExtensionService implements GitExtensionService {\n async execute(): Promise<GitExtensionResult> {\n return { message: 'Show git worktrees owned by this session', level: 'info' };\n }\n}\n"],"mappings":"AAGA,IAAa,EAAb,KAAuE,CACrE,MAAM,SAAuC,CAC3C,MAAO,CAAE,QAAS,2CAA4C,MAAO,MAAO,CAC9E,CACF"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
var e=class extends Error{code;retryable;recovery;constructor(e,t,n,r){super(`[${e}] ${t}\nRecovery: ${r}`),this.code=e,this.retryable=n,this.recovery=r,this.name=`DoomGitExpectedError`}};function t(t,n){return new e(`invalid_request`,t,!1,n)}exports.DoomGitExpectedError=e,exports.invalidRequest=t;
|
|
2
|
-
//# sourceMappingURL=errors.cjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"errors.cjs","names":[],"sources":["../../../src/services/support/errors.ts"],"sourcesContent":["/**\n * Expected failures, kept private to this package.\n *\n * `@agimon-ai/doompi-extension-contracts` 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_dirty'\n | 'hub_unavailable'\n | 'install_failed'\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\nexport function invalidRequest(message: string, recovery: string): DoomGitExpectedError {\n return new DoomGitExpectedError('invalid_request', message, false, recovery);\n}\n"],"mappings":"AAqBA,IAAa,EAAb,cAA0C,KAAM,CAEnC,KAEA,UACA,SAJX,YACE,EACA,EACA,EACA,EACA,CAGA,MAAM,IAAI,EAAK,IAAI,EAAQ,cAAc,GAAU,EAP1C,KAAA,KAAA,EAEA,KAAA,UAAA,EACA,KAAA,SAAA,EAKT,KAAK,KAAO,sBACd,CACF,EAEA,SAAgB,EAAe,EAAiB,EAAwC,CACtF,OAAO,IAAI,EAAqB,kBAAmB,EAAS,GAAO,CAAQ,CAC7E"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
var e=class extends Error{code;retryable;recovery;constructor(e,t,n,r){super(`[${e}] ${t}\nRecovery: ${r}`),this.code=e,this.retryable=n,this.recovery=r,this.name=`DoomGitExpectedError`}};function t(t,n){return new e(`invalid_request`,t,!1,n)}export{e as DoomGitExpectedError,t as invalidRequest};
|
|
2
|
-
//# sourceMappingURL=errors.mjs.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"errors.mjs","names":[],"sources":["../../../src/services/support/errors.ts"],"sourcesContent":["/**\n * Expected failures, kept private to this package.\n *\n * `@agimon-ai/doompi-extension-contracts` 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_dirty'\n | 'hub_unavailable'\n | 'install_failed'\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\nexport function invalidRequest(message: string, recovery: string): DoomGitExpectedError {\n return new DoomGitExpectedError('invalid_request', message, false, recovery);\n}\n"],"mappings":"AAqBA,IAAa,EAAb,cAA0C,KAAM,CAEnC,KAEA,UACA,SAJX,YACE,EACA,EACA,EACA,EACA,CAGA,MAAM,IAAI,EAAK,IAAI,EAAQ,cAAc,GAAU,EAP1C,KAAA,KAAA,EAEA,KAAA,UAAA,EACA,KAAA,SAAA,EAKT,KAAK,KAAO,sBACd,CACF,EAEA,SAAgB,EAAe,EAAiB,EAAwC,CACtF,OAAO,IAAI,EAAqB,kBAAmB,EAAS,GAAO,CAAQ,CAC7E"}
|
|
@@ -1,2 +0,0 @@
|
|
|
1
|
-
function e(e){let t=e.toLowerCase().replace(/[^a-z0-9]+/gu,`-`).replace(/^-+|-+$/gu,``).slice(0,64).replace(/-+$/gu,``);return t===``?`worktree`:t}function t(t){let n=`${t.repositoryLabel}--${t.repositoryId}`;return`${t.worktreesRoot}/${n}/${e(t.branch)}--${t.shortId}`}function n(e){if(e.branch.trim()===``)return`A branch name is required.`;let t=e.existing.find(t=>t.branch===e.branch&&t.status!==`orphaned`);if(t)return`Branch ${e.branch} already has a worktree (${t.id}).`}function r(e){if(!e.record)return`No worktree with that id. Call list to see the current ids.`;if(e.dirtyFiles.length===0||e.force)return;let t=e.dirtyFiles.slice(0,10).join(`, `),n=e.dirtyFiles.length>10?`, and ${String(e.dirtyFiles.length-10)} more`:``;return`${String(e.dirtyFiles.length)} uncommitted file(s): ${t}${n}. Pass force: true to discard them.`}function i(e,t){let n=[];return{records:e.map(e=>e.status===`orphaned`||t.exists(e.path)&&t.alive(e)?e:(n.push(e.id),{...e,status:`orphaned`})),changed:n}}function a(e){let t={remove:[],forget:[],untracked:[],keptDirty:[]},n=new Set(e.records.map(e=>e.path)),r=new Set(e.gitPaths);for(let n of e.records)if(n.status===`orphaned`){if(!r.has(n.path)){t.forget.push(n);continue}if((e.dirtyByPath.get(n.path)??[]).length>0){t.keptDirty.push(n);continue}t.remove.push(n)}for(let r of e.gitPaths)r.startsWith(`${e.worktreesRoot}/`)&&!n.has(r)&&t.untracked.push(r);return t}exports.branchSlug=e,exports.planPrune=a,exports.reconcile=i,exports.refuseClose=r,exports.refuseSpawn=n,exports.worktreeDirectory=t;
|
|
2
|
-
//# sourceMappingURL=worktreeNaming.cjs.map
|