@cleocode/worktree 2026.4.117

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (41) hide show
  1. package/dist/compat.d.ts +126 -0
  2. package/dist/compat.d.ts.map +1 -0
  3. package/dist/compat.js +160 -0
  4. package/dist/compat.js.map +1 -0
  5. package/dist/git.d.ts +53 -0
  6. package/dist/git.d.ts.map +1 -0
  7. package/dist/git.js +90 -0
  8. package/dist/git.js.map +1 -0
  9. package/dist/index.d.ts +32 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +30 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/paths.d.ts +51 -0
  14. package/dist/paths.d.ts.map +1 -0
  15. package/dist/paths.js +66 -0
  16. package/dist/paths.js.map +1 -0
  17. package/dist/worktree-create.d.ts +36 -0
  18. package/dist/worktree-create.d.ts.map +1 -0
  19. package/dist/worktree-create.js +127 -0
  20. package/dist/worktree-create.js.map +1 -0
  21. package/dist/worktree-destroy.d.ts +30 -0
  22. package/dist/worktree-destroy.d.ts.map +1 -0
  23. package/dist/worktree-destroy.js +110 -0
  24. package/dist/worktree-destroy.js.map +1 -0
  25. package/dist/worktree-hooks.d.ts +32 -0
  26. package/dist/worktree-hooks.d.ts.map +1 -0
  27. package/dist/worktree-hooks.js +67 -0
  28. package/dist/worktree-hooks.js.map +1 -0
  29. package/dist/worktree-include.d.ts +54 -0
  30. package/dist/worktree-include.d.ts.map +1 -0
  31. package/dist/worktree-include.js +99 -0
  32. package/dist/worktree-include.js.map +1 -0
  33. package/dist/worktree-list.d.ts +40 -0
  34. package/dist/worktree-list.d.ts.map +1 -0
  35. package/dist/worktree-list.js +110 -0
  36. package/dist/worktree-list.js.map +1 -0
  37. package/dist/worktree-prune.d.ts +27 -0
  38. package/dist/worktree-prune.d.ts.map +1 -0
  39. package/dist/worktree-prune.js +91 -0
  40. package/dist/worktree-prune.js.map +1 -0
  41. package/package.json +49 -0
@@ -0,0 +1,110 @@
1
+ /**
2
+ * Worktree listing operations for @cleocode/worktree.
3
+ *
4
+ * `listWorktrees` and `listWorktreesByProjectRoot` scan the CLEO XDG worktrees
5
+ * directory to return structured entries.
6
+ *
7
+ * @task T1161
8
+ */
9
+ import { execFileSync } from 'node:child_process';
10
+ import { existsSync, readdirSync } from 'node:fs';
11
+ import { join } from 'node:path';
12
+ import envPaths from 'env-paths';
13
+ import { computeProjectHash, resolveWorktreeRootForHash } from './paths.js';
14
+ const APP_NAME = 'cleo';
15
+ /**
16
+ * Resolve the worktree root directory for a given project hash.
17
+ *
18
+ * Exported so callers can inspect the canonical path without creating a worktree.
19
+ *
20
+ * @param projectHash - 16-char project hash.
21
+ * @param worktreeRoot - Optional explicit override for the root path.
22
+ * @returns Absolute path to the worktrees root directory for this project.
23
+ */
24
+ export function resolveWorktreeRoot(projectHash, worktreeRoot) {
25
+ return resolveWorktreeRootForHash(projectHash, worktreeRoot);
26
+ }
27
+ /**
28
+ * List all active CLEO worktrees scoped to a specific project hash.
29
+ *
30
+ * Scans the XDG worktrees filesystem directory. Entries without a valid
31
+ * worktree path on disk are omitted.
32
+ *
33
+ * @param options - Listing options including optional project hash filter.
34
+ * @returns Array of worktree entries.
35
+ */
36
+ export function listWorktrees(options = {}) {
37
+ const ep = envPaths(APP_NAME, { suffix: '' });
38
+ const dataDir = process.env['CLEO_HOME'] ?? ep.data;
39
+ const worktreesBase = join(dataDir, 'worktrees');
40
+ if (!existsSync(worktreesBase))
41
+ return [];
42
+ const entries = [];
43
+ let projectHashes;
44
+ try {
45
+ projectHashes = readdirSync(worktreesBase);
46
+ }
47
+ catch {
48
+ return [];
49
+ }
50
+ for (const hash of projectHashes) {
51
+ // Filter by project hash when provided
52
+ if (options.projectHash && hash !== options.projectHash)
53
+ continue;
54
+ const hashDir = join(worktreesBase, hash);
55
+ let taskDirs;
56
+ try {
57
+ taskDirs = readdirSync(hashDir);
58
+ }
59
+ catch {
60
+ continue;
61
+ }
62
+ for (const taskId of taskDirs) {
63
+ const worktreePath = join(hashDir, taskId);
64
+ if (!existsSync(worktreePath))
65
+ continue;
66
+ const branch = resolveWorktreeBranch(worktreePath) ?? `task/${taskId}`;
67
+ entries.push({
68
+ path: worktreePath,
69
+ branch,
70
+ taskId,
71
+ projectHash: hash,
72
+ });
73
+ }
74
+ }
75
+ return entries;
76
+ }
77
+ /**
78
+ * List all active CLEO worktrees for a specific project root.
79
+ *
80
+ * Computes the project hash from the project root, then delegates to
81
+ * {@link listWorktrees} with the computed hash as filter.
82
+ *
83
+ * @param projectRoot - Absolute path to the project root directory.
84
+ * @returns Array of worktree list entries for this project.
85
+ */
86
+ export function listWorktreesByProjectRoot(projectRoot) {
87
+ const projectHash = computeProjectHash(projectRoot);
88
+ return listWorktrees({ projectHash });
89
+ }
90
+ /**
91
+ * Resolve the current branch for a worktree directory by invoking git.
92
+ *
93
+ * Returns null if the path is not a valid git worktree or git fails.
94
+ *
95
+ * @param worktreePath - Absolute path to the worktree directory.
96
+ * @returns Branch name string, or null if not determinable.
97
+ * @internal
98
+ */
99
+ function resolveWorktreeBranch(worktreePath) {
100
+ try {
101
+ return execFileSync('git', ['-C', worktreePath, 'rev-parse', '--abbrev-ref', 'HEAD'], {
102
+ encoding: 'utf-8',
103
+ stdio: ['pipe', 'pipe', 'pipe'],
104
+ }).trim();
105
+ }
106
+ catch {
107
+ return null;
108
+ }
109
+ }
110
+ //# sourceMappingURL=worktree-list.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worktree-list.js","sourceRoot":"","sources":["../src/worktree-list.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,27 @@
1
+ /**
2
+ * Worktree prune operation for @cleocode/worktree.
3
+ *
4
+ * Removes orphaned worktrees: worktree directories whose task ID is NOT in
5
+ * the provided `preserveTaskIds` set, and optionally runs `git worktree prune`
6
+ * to clean up stale git administrative entries.
7
+ *
8
+ * Called periodically by `cleo sentient tick` via `worktree-dispatch.ts`.
9
+ *
10
+ * @task T1161
11
+ */
12
+ import type { PruneWorktreesOptions, PruneWorktreesResult } from '@cleocode/contracts';
13
+ /**
14
+ * Prune orphaned agent worktrees for a project.
15
+ *
16
+ * Algorithm:
17
+ * 1. Optionally run `git worktree prune` to clean up stale git admin entries.
18
+ * 2. Read all subdirectory entries under the project's worktree root.
19
+ * 3. For each entry NOT in `preserveTaskIds`, unlock and remove the worktree.
20
+ *
21
+ * @param options - Prune options with project root and optional preserve list.
22
+ * @returns Result listing removed paths and any errors.
23
+ *
24
+ * @task T1161
25
+ */
26
+ export declare function pruneWorktrees(options: PruneWorktreesOptions): PruneWorktreesResult;
27
+ //# sourceMappingURL=worktree-prune.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worktree-prune.d.ts","sourceRoot":"","sources":["../src/worktree-prune.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAIH,OAAO,KAAK,EAAE,qBAAqB,EAAE,oBAAoB,EAAE,MAAM,qBAAqB,CAAC;AAIvF;;;;;;;;;;;;GAYG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,qBAAqB,GAAG,oBAAoB,CA+DnF"}
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Worktree prune operation for @cleocode/worktree.
3
+ *
4
+ * Removes orphaned worktrees: worktree directories whose task ID is NOT in
5
+ * the provided `preserveTaskIds` set, and optionally runs `git worktree prune`
6
+ * to clean up stale git administrative entries.
7
+ *
8
+ * Called periodically by `cleo sentient tick` via `worktree-dispatch.ts`.
9
+ *
10
+ * @task T1161
11
+ */
12
+ import { existsSync, readdirSync, rmSync } from 'node:fs';
13
+ import { join } from 'node:path';
14
+ import { getGitRoot, gitSilent } from './git.js';
15
+ import { computeProjectHash, resolveWorktreeRootForHash } from './paths.js';
16
+ /**
17
+ * Prune orphaned agent worktrees for a project.
18
+ *
19
+ * Algorithm:
20
+ * 1. Optionally run `git worktree prune` to clean up stale git admin entries.
21
+ * 2. Read all subdirectory entries under the project's worktree root.
22
+ * 3. For each entry NOT in `preserveTaskIds`, unlock and remove the worktree.
23
+ *
24
+ * @param options - Prune options with project root and optional preserve list.
25
+ * @returns Result listing removed paths and any errors.
26
+ *
27
+ * @task T1161
28
+ */
29
+ export function pruneWorktrees(options) {
30
+ const { projectRoot, preserveTaskIds, gitPrune = true } = options;
31
+ const projectHash = computeProjectHash(projectRoot);
32
+ const worktreeRoot = resolveWorktreeRootForHash(projectHash);
33
+ const removed = [];
34
+ const errors = [];
35
+ let gitPruneRan = false;
36
+ // Step 1: Run git worktree prune if requested.
37
+ if (gitPrune) {
38
+ try {
39
+ const gitRoot = getGitRoot(projectRoot);
40
+ gitSilent(['worktree', 'prune'], gitRoot);
41
+ gitPruneRan = true;
42
+ }
43
+ catch {
44
+ // Non-fatal: project root may not be a git repo in some edge cases.
45
+ gitPruneRan = false;
46
+ }
47
+ }
48
+ // Step 2: Remove orphaned worktree directories.
49
+ if (preserveTaskIds !== undefined && existsSync(worktreeRoot)) {
50
+ let entries;
51
+ try {
52
+ entries = readdirSync(worktreeRoot);
53
+ }
54
+ catch {
55
+ return { removed: 0, removedPaths: [], errors, gitPruneRan };
56
+ }
57
+ let gitRoot;
58
+ try {
59
+ gitRoot = getGitRoot(projectRoot);
60
+ }
61
+ catch {
62
+ // If there's no git root, we can still remove dirs — just skip git cmds.
63
+ gitRoot = projectRoot;
64
+ }
65
+ for (const entry of entries) {
66
+ if (preserveTaskIds.has(entry))
67
+ continue;
68
+ const worktreePath = join(worktreeRoot, entry);
69
+ // Try git-aware removal first.
70
+ gitSilent(['worktree', 'unlock', worktreePath], gitRoot);
71
+ if (gitSilent(['worktree', 'remove', '--force', worktreePath], gitRoot)) {
72
+ removed.push(worktreePath);
73
+ }
74
+ else {
75
+ // Fall back to rmSync for directories that aren't registered worktrees.
76
+ try {
77
+ rmSync(worktreePath, { recursive: true, force: true });
78
+ removed.push(worktreePath);
79
+ }
80
+ catch (err) {
81
+ errors.push({
82
+ path: worktreePath,
83
+ reason: err instanceof Error ? err.message : String(err),
84
+ });
85
+ }
86
+ }
87
+ }
88
+ }
89
+ return { removed: removed.length, removedPaths: removed, errors, gitPruneRan };
90
+ }
91
+ //# sourceMappingURL=worktree-prune.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worktree-prune.js","sourceRoot":"","sources":["../src/worktree-prune.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EAAE,UAAU,EAAE,WAAW,EAAE,MAAM,EAAE,MAAM,SAAS,CAAC;AAC1D,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAEjC,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AACjD,OAAO,EAAE,kBAAkB,EAAE,0BAA0B,EAAE,MAAM,YAAY,CAAC;AAE5E;;;;;;;;;;;;GAYG;AACH,MAAM,UAAU,cAAc,CAAC,OAA8B;IAC3D,MAAM,EAAE,WAAW,EAAE,eAAe,EAAE,QAAQ,GAAG,IAAI,EAAE,GAAG,OAAO,CAAC;IAElE,MAAM,WAAW,GAAG,kBAAkB,CAAC,WAAW,CAAC,CAAC;IACpD,MAAM,YAAY,GAAG,0BAA0B,CAAC,WAAW,CAAC,CAAC;IAC7D,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,MAAM,MAAM,GAA4C,EAAE,CAAC;IAC3D,IAAI,WAAW,GAAG,KAAK,CAAC;IAExB,+CAA+C;IAC/C,IAAI,QAAQ,EAAE,CAAC;QACb,IAAI,CAAC;YACH,MAAM,OAAO,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;YACxC,SAAS,CAAC,CAAC,UAAU,EAAE,OAAO,CAAC,EAAE,OAAO,CAAC,CAAC;YAC1C,WAAW,GAAG,IAAI,CAAC;QACrB,CAAC;QAAC,MAAM,CAAC;YACP,oEAAoE;YACpE,WAAW,GAAG,KAAK,CAAC;QACtB,CAAC;IACH,CAAC;IAED,gDAAgD;IAChD,IAAI,eAAe,KAAK,SAAS,IAAI,UAAU,CAAC,YAAY,CAAC,EAAE,CAAC;QAC9D,IAAI,OAAiB,CAAC;QACtB,IAAI,CAAC;YACH,OAAO,GAAG,WAAW,CAAC,YAAY,CAAC,CAAC;QACtC,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,OAAO,EAAE,CAAC,EAAE,YAAY,EAAE,EAAE,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;QAC/D,CAAC;QAED,IAAI,OAAe,CAAC;QACpB,IAAI,CAAC;YACH,OAAO,GAAG,UAAU,CAAC,WAAW,CAAC,CAAC;QACpC,CAAC;QAAC,MAAM,CAAC;YACP,yEAAyE;YACzE,OAAO,GAAG,WAAW,CAAC;QACxB,CAAC;QAED,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;YAC5B,IAAI,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC;gBAAE,SAAS;YAEzC,MAAM,YAAY,GAAG,IAAI,CAAC,YAAY,EAAE,KAAK,CAAC,CAAC;YAE/C,+BAA+B;YAC/B,SAAS,CAAC,CAAC,UAAU,EAAE,QAAQ,EAAE,YAAY,CAAC,EAAE,OAAO,CAAC,CAAC;YACzD,IAAI,SAAS,CAAC,CAAC,UAAU,EAAE,QAAQ,EAAE,SAAS,EAAE,YAAY,CAAC,EAAE,OAAO,CAAC,EAAE,CAAC;gBACxE,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;YAC7B,CAAC;iBAAM,CAAC;gBACN,wEAAwE;gBACxE,IAAI,CAAC;oBACH,MAAM,CAAC,YAAY,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,CAAC,CAAC;oBACvD,OAAO,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC;gBAC7B,CAAC;gBAAC,OAAO,GAAG,EAAE,CAAC;oBACb,MAAM,CAAC,IAAI,CAAC;wBACV,IAAI,EAAE,YAAY;wBAClB,MAAM,EAAE,GAAG,YAAY,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC;qBACzD,CAAC,CAAC;gBACL,CAAC;YACH,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,CAAC;AACjF,CAAC"}
package/package.json ADDED
@@ -0,0 +1,49 @@
1
+ {
2
+ "name": "@cleocode/worktree",
3
+ "version": "2026.4.117",
4
+ "private": false,
5
+ "type": "module",
6
+ "description": "Native CLEO worktree backend SDK — createWorktree, destroyWorktree, listWorktrees, pruneWorktrees with XDG path canon and declarative hooks (T1161)",
7
+ "main": "dist/index.js",
8
+ "types": "dist/index.d.ts",
9
+ "exports": {
10
+ ".": {
11
+ "types": "./dist/index.d.ts",
12
+ "import": "./dist/index.js"
13
+ }
14
+ },
15
+ "files": [
16
+ "dist/",
17
+ "README.md"
18
+ ],
19
+ "scripts": {
20
+ "build": "tsc -b",
21
+ "test": "vitest run",
22
+ "test:watch": "vitest"
23
+ },
24
+ "keywords": [
25
+ "cleo",
26
+ "worktree",
27
+ "git",
28
+ "agent",
29
+ "sdk",
30
+ "isolation"
31
+ ],
32
+ "engines": {
33
+ "node": ">=24.0.0"
34
+ },
35
+ "author": "CLEO Code <hello@cleocode.dev>",
36
+ "license": "MIT",
37
+ "publishConfig": {
38
+ "access": "public"
39
+ },
40
+ "dependencies": {
41
+ "@cleocode/contracts": "workspace:*",
42
+ "env-paths": "^4.0.0"
43
+ },
44
+ "devDependencies": {
45
+ "@types/node": "^24.3.0",
46
+ "typescript": "^6.0.2",
47
+ "vitest": "^4.1.4"
48
+ }
49
+ }