crewly 1.20.300 → 1.20.302

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 (100) hide show
  1. package/config/skills/agent/core/publish-app/SKILL.md +10 -1
  2. package/config/skills/agent/core/receive-file/SKILL.md +4 -0
  3. package/config/skills/agent/core/request-file/SKILL.md +74 -0
  4. package/config/skills/agent/core/request-file/execute.sh +89 -0
  5. package/config/skills/agent/core/request-file/execute.test.sh +77 -0
  6. package/config/skills/agent/core/slack-file/SKILL.md +1 -1
  7. package/dist/backend/backend/src/constants.d.ts +13 -4
  8. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  9. package/dist/backend/backend/src/constants.js +18 -6
  10. package/dist/backend/backend/src/constants.js.map +1 -1
  11. package/dist/backend/backend/src/controllers/apps/apps.controller.d.ts +9 -0
  12. package/dist/backend/backend/src/controllers/apps/apps.controller.d.ts.map +1 -1
  13. package/dist/backend/backend/src/controllers/apps/apps.controller.js +21 -0
  14. package/dist/backend/backend/src/controllers/apps/apps.controller.js.map +1 -1
  15. package/dist/backend/backend/src/controllers/apps/apps.routes.d.ts +2 -0
  16. package/dist/backend/backend/src/controllers/apps/apps.routes.d.ts.map +1 -1
  17. package/dist/backend/backend/src/controllers/apps/apps.routes.js +7 -1
  18. package/dist/backend/backend/src/controllers/apps/apps.routes.js.map +1 -1
  19. package/dist/backend/backend/src/index.d.ts.map +1 -1
  20. package/dist/backend/backend/src/index.js +20 -1
  21. package/dist/backend/backend/src/index.js.map +1 -1
  22. package/dist/backend/backend/src/services/agent/follow-through.service.d.ts +33 -0
  23. package/dist/backend/backend/src/services/agent/follow-through.service.d.ts.map +1 -1
  24. package/dist/backend/backend/src/services/agent/follow-through.service.js +80 -7
  25. package/dist/backend/backend/src/services/agent/follow-through.service.js.map +1 -1
  26. package/dist/backend/backend/src/services/agent/follow-through.wiring.d.ts +34 -0
  27. package/dist/backend/backend/src/services/agent/follow-through.wiring.d.ts.map +1 -1
  28. package/dist/backend/backend/src/services/agent/follow-through.wiring.js +109 -4
  29. package/dist/backend/backend/src/services/agent/follow-through.wiring.js.map +1 -1
  30. package/dist/backend/backend/src/services/agent/idle-detection.service.d.ts +9 -0
  31. package/dist/backend/backend/src/services/agent/idle-detection.service.d.ts.map +1 -1
  32. package/dist/backend/backend/src/services/agent/idle-detection.service.js +24 -1
  33. package/dist/backend/backend/src/services/agent/idle-detection.service.js.map +1 -1
  34. package/dist/backend/backend/src/services/agent/idle-work-guard.d.ts +12 -1
  35. package/dist/backend/backend/src/services/agent/idle-work-guard.d.ts.map +1 -1
  36. package/dist/backend/backend/src/services/agent/idle-work-guard.js +11 -1
  37. package/dist/backend/backend/src/services/agent/idle-work-guard.js.map +1 -1
  38. package/dist/backend/backend/src/services/agent/stated-intent.d.ts +2 -2
  39. package/dist/backend/backend/src/services/agent/stated-intent.d.ts.map +1 -1
  40. package/dist/backend/backend/src/services/agent/stated-intent.js +17 -7
  41. package/dist/backend/backend/src/services/agent/stated-intent.js.map +1 -1
  42. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
  43. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js +1 -0
  44. package/dist/backend/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
  45. package/dist/backend/backend/src/services/apps/apps.wiring.d.ts.map +1 -1
  46. package/dist/backend/backend/src/services/apps/apps.wiring.js +14 -0
  47. package/dist/backend/backend/src/services/apps/apps.wiring.js.map +1 -1
  48. package/dist/backend/backend/src/services/apps/file-request-watch.service.d.ts +88 -0
  49. package/dist/backend/backend/src/services/apps/file-request-watch.service.d.ts.map +1 -0
  50. package/dist/backend/backend/src/services/apps/file-request-watch.service.js +170 -0
  51. package/dist/backend/backend/src/services/apps/file-request-watch.service.js.map +1 -0
  52. package/dist/backend/backend/src/services/messaging/deferred-start-retry.d.ts +2 -0
  53. package/dist/backend/backend/src/services/messaging/deferred-start-retry.d.ts.map +1 -1
  54. package/dist/backend/backend/src/services/messaging/deferred-start-retry.js +1 -1
  55. package/dist/backend/backend/src/services/messaging/deferred-start-retry.js.map +1 -1
  56. package/dist/backend/backend/src/services/messaging/owner-thread-sentinel.service.d.ts +16 -0
  57. package/dist/backend/backend/src/services/messaging/owner-thread-sentinel.service.d.ts.map +1 -1
  58. package/dist/backend/backend/src/services/messaging/owner-thread-sentinel.service.js +38 -0
  59. package/dist/backend/backend/src/services/messaging/owner-thread-sentinel.service.js.map +1 -1
  60. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
  61. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js +51 -33
  62. package/dist/backend/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
  63. package/dist/backend/backend/src/types/slack.types.d.ts +8 -0
  64. package/dist/backend/backend/src/types/slack.types.d.ts.map +1 -1
  65. package/dist/backend/backend/src/types/slack.types.js.map +1 -1
  66. package/dist/backend/build-info.json +2 -2
  67. package/dist/cli/backend/src/constants.d.ts +13 -4
  68. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  69. package/dist/cli/backend/src/constants.js +18 -6
  70. package/dist/cli/backend/src/constants.js.map +1 -1
  71. package/dist/cli/backend/src/services/agent/follow-through.service.d.ts +33 -0
  72. package/dist/cli/backend/src/services/agent/follow-through.service.d.ts.map +1 -1
  73. package/dist/cli/backend/src/services/agent/follow-through.service.js +80 -7
  74. package/dist/cli/backend/src/services/agent/follow-through.service.js.map +1 -1
  75. package/dist/cli/backend/src/services/agent/stated-intent.d.ts +2 -2
  76. package/dist/cli/backend/src/services/agent/stated-intent.d.ts.map +1 -1
  77. package/dist/cli/backend/src/services/agent/stated-intent.js +17 -7
  78. package/dist/cli/backend/src/services/agent/stated-intent.js.map +1 -1
  79. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.d.ts.map +1 -1
  80. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js +1 -0
  81. package/dist/cli/backend/src/services/ai/prompt-modules/skills-reference.module.js.map +1 -1
  82. package/dist/cli/backend/src/services/messaging/owner-thread-sentinel.service.d.ts +16 -0
  83. package/dist/cli/backend/src/services/messaging/owner-thread-sentinel.service.d.ts.map +1 -1
  84. package/dist/cli/backend/src/services/messaging/owner-thread-sentinel.service.js +38 -0
  85. package/dist/cli/backend/src/services/messaging/owner-thread-sentinel.service.js.map +1 -1
  86. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.d.ts.map +1 -1
  87. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.js +51 -33
  88. package/dist/cli/backend/src/services/slack/slack-orchestrator-bridge.js.map +1 -1
  89. package/dist/cli/backend/src/services/worktree/low-disk-guard.d.ts +109 -0
  90. package/dist/cli/backend/src/services/worktree/low-disk-guard.d.ts.map +1 -0
  91. package/dist/cli/backend/src/services/worktree/low-disk-guard.js +148 -0
  92. package/dist/cli/backend/src/services/worktree/low-disk-guard.js.map +1 -0
  93. package/dist/cli/backend/src/services/worktree/worktree-janitor.git.d.ts +224 -0
  94. package/dist/cli/backend/src/services/worktree/worktree-janitor.git.d.ts.map +1 -0
  95. package/dist/cli/backend/src/services/worktree/worktree-janitor.git.js +449 -0
  96. package/dist/cli/backend/src/services/worktree/worktree-janitor.git.js.map +1 -0
  97. package/dist/cli/backend/src/types/slack.types.d.ts +8 -0
  98. package/dist/cli/backend/src/types/slack.types.d.ts.map +1 -1
  99. package/dist/cli/backend/src/types/slack.types.js.map +1 -1
  100. package/package.json +1 -1
@@ -0,0 +1,148 @@
1
+ /**
2
+ * Low-disk guard — pure helpers for the disk janitor's free-space check.
3
+ *
4
+ * The janitor checks free space on the volume holding CREWLY_HOME every
5
+ * 10 minutes. Below 15 GB it cleans up at once with shorter idle thresholds;
6
+ * if space is still short it tells the owner (at most once per 24 h, or once
7
+ * per 6 h and marked urgent below 5 GB) what it could not remove and why.
8
+ * This file holds the decisions and the wording; the service does the I/O.
9
+ *
10
+ * @module services/worktree/low-disk-guard
11
+ */
12
+ import * as fs from 'fs';
13
+ import { WORKTREE_JANITOR_CONSTANTS } from '../../constants.js';
14
+ import { formatBytes } from './worktree-janitor.git.js';
15
+ /**
16
+ * Free bytes on the volume holding `p`.
17
+ *
18
+ * @param p - Any path on the volume
19
+ * @param statfs - Probe (default `fs.promises.statfs`)
20
+ * @returns Free bytes for unprivileged users, or null when the probe failed
21
+ */
22
+ export async function readFreeBytes(p, statfs = defaultStatFs) {
23
+ try {
24
+ const s = await statfs(p);
25
+ return Number(s.bavail) * Number(s.bsize);
26
+ }
27
+ catch {
28
+ return null;
29
+ }
30
+ }
31
+ async function defaultStatFs(p) {
32
+ return fs.promises.statfs(p);
33
+ }
34
+ /**
35
+ * Classify free space.
36
+ *
37
+ * @param freeBytes - Free bytes
38
+ * @returns `critical` below CRITICAL_DISK_BYTES, `low` below LOW_DISK_BYTES, else `ok`
39
+ */
40
+ export function diskLevel(freeBytes) {
41
+ if (freeBytes < WORKTREE_JANITOR_CONSTANTS.CRITICAL_DISK_BYTES)
42
+ return 'critical';
43
+ if (freeBytes < WORKTREE_JANITOR_CONSTANTS.LOW_DISK_BYTES)
44
+ return 'low';
45
+ return 'ok';
46
+ }
47
+ /**
48
+ * Idle threshold to use, shortened in low-disk mode.
49
+ *
50
+ * @param normalMs - Threshold in normal mode
51
+ * @param lowDisk - Whether free space is below LOW_DISK_BYTES
52
+ * @returns `normalMs`, or `normalMs / LOW_DISK_IDLE_DIVISOR` floored at LOW_DISK_MIN_IDLE_FLOOR_MS
53
+ *
54
+ * @example
55
+ * ```typescript
56
+ * idleThreshold(24 * HOUR, true); // 12 h
57
+ * idleThreshold(2 * HOUR, true); // 2 h (floor)
58
+ * ```
59
+ */
60
+ export function idleThreshold(normalMs, lowDisk) {
61
+ if (!lowDisk)
62
+ return normalMs;
63
+ const halved = normalMs / WORKTREE_JANITOR_CONSTANTS.LOW_DISK_IDLE_DIVISOR;
64
+ return Math.max(Math.min(normalMs, WORKTREE_JANITOR_CONSTANTS.LOW_DISK_MIN_IDLE_FLOOR_MS), halved);
65
+ }
66
+ /**
67
+ * Whether to tell the owner now, and how.
68
+ *
69
+ * @param level - Current disk level (after cleanup)
70
+ * @param state - When the owner was last told
71
+ * @param now - Current time (ms)
72
+ * @returns `urgent`, `normal`, or null (nothing to send)
73
+ */
74
+ export function noticeToSend(level, state, now) {
75
+ if (level === 'ok')
76
+ return null;
77
+ if (level === 'critical') {
78
+ const last = state.lastUrgentAt;
79
+ return last === undefined || now - last >= WORKTREE_JANITOR_CONSTANTS.CRITICAL_DISK_NOTIFY_INTERVAL_MS ? 'urgent' : null;
80
+ }
81
+ const last = state.lastNoticeAt;
82
+ return last === undefined || now - last >= WORKTREE_JANITOR_CONSTANTS.LOW_DISK_NOTIFY_INTERVAL_MS ? 'normal' : null;
83
+ }
84
+ /**
85
+ * The state after a notice went out.
86
+ *
87
+ * @param state - Previous state
88
+ * @param kind - What was sent
89
+ * @param now - Send time (ms)
90
+ * @returns New state
91
+ */
92
+ export function recordNotice(state, kind, now) {
93
+ return { ...state, lastNoticeAt: now, ...(kind === 'urgent' ? { lastUrgentAt: now } : {}) };
94
+ }
95
+ const REASON_TEXT = {
96
+ dirty: 'has changes that were never committed',
97
+ 'not-merged': 'has work that is not merged yet',
98
+ unpushed: 'has commits that were never pushed',
99
+ stash: 'has stashed changes',
100
+ 'no-remote': 'is a git repo with no remote, so its history may exist nowhere else',
101
+ 'has-external-worktrees': 'has a worktree somewhere else that depends on it',
102
+ 'contains-worktree': 'holds a worktree that is still in use or not finished',
103
+ recent: 'was used recently',
104
+ locked: 'is locked by a running tool',
105
+ 'process-inside': 'a program is still open in it',
106
+ 'agent-session-inside': 'an agent is working in it',
107
+ 'cwd-probe-failed': 'could not check whether a program is using it',
108
+ 'managed-by-workitem-worktrees': 'belongs to an open task',
109
+ 'status-failed': 'git could not read it',
110
+ 'git-failed': 'git could not read it',
111
+ 'remove-failed': 'deleting it failed',
112
+ };
113
+ /**
114
+ * Plain-English reason for a kept item.
115
+ *
116
+ * @param reason - Janitor reason code
117
+ * @returns Short phrase ("has changes that were never committed")
118
+ */
119
+ export function describeReason(reason) {
120
+ return REASON_TEXT[reason] ?? `kept (${reason})`;
121
+ }
122
+ /**
123
+ * Owner notice for low disk: free space, what was freed, and the biggest
124
+ * items left with the reason, short enough for a phone.
125
+ *
126
+ * @param input - Free bytes, urgency, bytes freed this pass, kept items
127
+ * @returns Title and message
128
+ */
129
+ export function buildLowDiskNotice(input) {
130
+ const title = input.urgent ? 'URGENT: disk almost full' : 'Disk space is running low';
131
+ const lines = [];
132
+ lines.push(`Only ${formatBytes(input.freeBytes)} free on this machine.${input.urgent ? ' Crewly and the agents may stop working soon.' : ''}`);
133
+ lines.push(input.freedBytes > 0
134
+ ? `I cleaned up ${formatBytes(input.freedBytes)} of finished work, but it is not enough.`
135
+ : 'I found nothing more that is safe to delete on my own.');
136
+ const top = [...input.items].sort((a, b) => b.bytes - a.bytes).slice(0, WORKTREE_JANITOR_CONSTANTS.LOW_DISK_REPORT_ITEMS);
137
+ if (top.length > 0) {
138
+ lines.push('', 'Biggest things I left alone:');
139
+ for (const it of top)
140
+ lines.push(`• ${it.path} (${formatBytes(it.bytes)}) — ${describeReason(it.reason)}`);
141
+ lines.push('', 'Tell me which ones can go, or free up space another way.');
142
+ }
143
+ else {
144
+ lines.push('Please free up some space.');
145
+ }
146
+ return { title, message: lines.join('\n') };
147
+ }
148
+ //# sourceMappingURL=low-disk-guard.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"low-disk-guard.js","sourceRoot":"","sources":["../../../../../../backend/src/services/worktree/low-disk-guard.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,224 @@
1
+ /**
2
+ * Worktree janitor — low-level helpers.
3
+ *
4
+ * A command runner that never throws, a parser for `git worktree list
5
+ * --porcelain`, path helpers, and the process-cwd probe used to tell whether
6
+ * anything is still working inside a worktree. Kept apart from the service so
7
+ * each piece can be tested on its own.
8
+ *
9
+ * @module services/worktree/worktree-janitor.git
10
+ */
11
+ /** Result of one external command. `code` is null when it could not start or timed out. */
12
+ export interface CommandResult {
13
+ /** Exit code, or null when the process did not exit normally */
14
+ code: number | null;
15
+ /** Captured stdout */
16
+ stdout: string;
17
+ /** Captured stderr (or the spawn error message) */
18
+ stderr: string;
19
+ }
20
+ /** Options for {@link runCommand}. */
21
+ export interface RunCommandOptions {
22
+ /** Working directory */
23
+ cwd?: string;
24
+ /** Timeout in ms (default: GIT_TIMEOUT_MS) */
25
+ timeoutMs?: number;
26
+ /** Extra environment variables merged over process.env */
27
+ env?: Record<string, string>;
28
+ /** Text written to the command's stdin (then closed) */
29
+ input?: string;
30
+ }
31
+ /**
32
+ * Run a command without a shell and never throw.
33
+ *
34
+ * Git and gh are told never to prompt (no credential dialogs, no pager), so a
35
+ * periodic background job cannot hang on input, and git takes no optional
36
+ * locks (a status never rewrites the index).
37
+ *
38
+ * @param file - Executable (e.g. `git`, `gh`, or an absolute path)
39
+ * @param args - Arguments
40
+ * @param options - cwd / timeout / env
41
+ * @returns Exit code and output; `code` is null on spawn failure or timeout
42
+ *
43
+ * @example
44
+ * ```typescript
45
+ * const r = await runCommand('git', ['-C', repo, 'status', '--porcelain']);
46
+ * if (r.code === 0 && r.stdout === '') console.log('clean');
47
+ * ```
48
+ */
49
+ export declare function runCommand(file: string, args: string[], options?: RunCommandOptions): Promise<CommandResult>;
50
+ /** One entry of `git worktree list --porcelain`. */
51
+ export interface PorcelainWorktree {
52
+ /** Absolute worktree path as git reports it */
53
+ path: string;
54
+ /** HEAD commit, or null (bare) */
55
+ head: string | null;
56
+ /** Short branch name (`refs/heads/` stripped), or null when detached */
57
+ branch: string | null;
58
+ /** True for the bare entry */
59
+ bare: boolean;
60
+ /** True when HEAD is detached */
61
+ detached: boolean;
62
+ /** True when the worktree is locked (`git worktree lock`) */
63
+ locked: boolean;
64
+ /** Lock reason, when given */
65
+ lockReason?: string;
66
+ /** True when git reports the directory as gone (`prunable`) */
67
+ prunable: boolean;
68
+ /** True for the first entry — the main worktree */
69
+ isMain: boolean;
70
+ }
71
+ /**
72
+ * Parse `git worktree list --porcelain` output.
73
+ *
74
+ * Entries are separated by blank lines; the first entry is the main worktree.
75
+ *
76
+ * @param output - Raw porcelain output
77
+ * @returns Parsed entries in git's order
78
+ *
79
+ * @example
80
+ * ```typescript
81
+ * parsePorcelainWorktrees('worktree /r\nHEAD abc\nbranch refs/heads/main\n');
82
+ * // [{ path: '/r', head: 'abc', branch: 'main', isMain: true, ... }]
83
+ * ```
84
+ */
85
+ export declare function parsePorcelainWorktrees(output: string): PorcelainWorktree[];
86
+ /**
87
+ * Real path of `p`, or its resolved form when it does not exist.
88
+ *
89
+ * @param p - Path
90
+ * @returns Canonical path for comparisons
91
+ */
92
+ export declare function canonicalPath(p: string): string;
93
+ /**
94
+ * Whether `child` is `parent` or inside it (both canonicalised first).
95
+ *
96
+ * @param child - Candidate path
97
+ * @param parent - Directory
98
+ * @returns True when child === parent or child is under parent
99
+ *
100
+ * @example
101
+ * ```typescript
102
+ * isPathInside('/r/.claude/worktrees/a/src', '/r/.claude/worktrees/a'); // true
103
+ * isPathInside('/r/.claude/worktrees/ab', '/r/.claude/worktrees/a'); // false
104
+ * ```
105
+ */
106
+ export declare function isPathInside(child: string, parent: string): boolean;
107
+ /**
108
+ * Latest modification time (ms) of a worktree: the directory itself plus its
109
+ * private git dir's `index`, `HEAD` and `logs/HEAD` — every commit, checkout,
110
+ * stage or reset touches one of those. Uncommitted edits are caught by the
111
+ * dirty check instead.
112
+ *
113
+ * @param worktreePath - Worktree root
114
+ * @param gitDir - Its private git dir (`.git/worktrees/<name>`), when known
115
+ * @returns Newest mtime in ms, or null when nothing could be stat'ed
116
+ */
117
+ export declare function latestWorktreeMtimeMs(worktreePath: string, gitDir: string | null): number | null;
118
+ /**
119
+ * Current working directories of every process this user can see.
120
+ *
121
+ * macOS: `lsof -a -d cwd -u <uid> -Fn`. Linux: `/proc/<pid>/cwd`. Returns
122
+ * null when the probe cannot run, so the caller can treat "unknown" as busy.
123
+ *
124
+ * @returns Absolute cwd paths (deduplicated), or null when unavailable
125
+ */
126
+ export declare function listProcessCwds(): Promise<string[] | null>;
127
+ /**
128
+ * Whether the kill switch disables the janitor.
129
+ *
130
+ * @param env - Environment (defaults to process.env)
131
+ * @returns True when `CREWLY_WORKTREE_JANITOR` is `0`/`off`/`false`/`no`
132
+ */
133
+ export declare function isJanitorDisabled(env?: NodeJS.ProcessEnv): boolean;
134
+ /**
135
+ * Yield to the event loop every YIELD_EVERY_ENTRIES calls, so a long
136
+ * filesystem walk never holds up Slack, relay polls or PTY I/O.
137
+ */
138
+ export declare class EventLoopYielder {
139
+ private count;
140
+ /**
141
+ * Count one unit of work; every YIELD_EVERY_ENTRIES units, wait one
142
+ * `setImmediate` turn.
143
+ *
144
+ * @returns Resolves when the caller may continue
145
+ */
146
+ tick(): Promise<void>;
147
+ }
148
+ /**
149
+ * Git dir a `.git` file points to (`gitdir: <path>`), resolved against `dir`.
150
+ *
151
+ * @param dir - Directory holding the `.git` file
152
+ * @returns Absolute git dir, or null when there is no readable `.git` file
153
+ */
154
+ export declare function readGitFilePointer(dir: string): Promise<string | null>;
155
+ /**
156
+ * Main worktree of the repo a linked worktree belongs to, read from the
157
+ * worktree's `.git` file (`gitdir: <main>/.git/worktrees/<name>`). Returns
158
+ * null for anything else: a `.git` directory (a normal clone), a submodule
159
+ * (`gitdir: …/.git/modules/…`), or an unreadable file.
160
+ *
161
+ * @param dir - Directory that may be a linked worktree
162
+ * @returns Absolute path of the main worktree, or null
163
+ *
164
+ * @example
165
+ * ```typescript
166
+ * // /tmp/visa-cm-wt/.git contains "gitdir: /src/ce-core/.git/worktrees/visa-cm-wt"
167
+ * await mainRepoOfLinkedWorktree('/tmp/visa-cm-wt'); // '/src/ce-core'
168
+ * ```
169
+ */
170
+ export declare function mainRepoOfLinkedWorktree(dir: string): Promise<string | null>;
171
+ /** A git checkout found on disk. */
172
+ export interface FoundRepo {
173
+ /** Directory holding `.git` */
174
+ path: string;
175
+ /** `dir` for a `.git` directory (clone / main worktree), `file` for a `.git` file (linked worktree or submodule) */
176
+ kind: 'dir' | 'file';
177
+ }
178
+ /**
179
+ * Find git checkouts under `root` without following symlinks. A directory
180
+ * with a `.git` directory or file is a checkout and is not descended into;
181
+ * `node_modules` and `.git` are never descended into. Fully async; yields to
182
+ * the event loop every few directories.
183
+ *
184
+ * @param root - Directory to search (itself included, at depth 0)
185
+ * @param maxDepth - Deepest level searched (root = 0)
186
+ * @param maxDirs - Stop after visiting this many directories
187
+ * @returns Checkouts found, in walk order
188
+ */
189
+ export declare function findGitCheckouts(root: string, maxDepth: number, maxDirs?: number): Promise<FoundRepo[]>;
190
+ /**
191
+ * Main worktrees of every repo that owns a linked worktree somewhere under
192
+ * the given roots (e.g. `/tmp/claude-501/visa-cm-wt` → `~/src/ce-core`), so
193
+ * repos that are not registered projects still get their temp worktrees
194
+ * cleaned. Visits at most DISCOVERY_MAX_DIRS directories per root.
195
+ *
196
+ * @param roots - Temp roots to scan (missing roots are skipped)
197
+ * @returns De-duplicated main-worktree paths
198
+ */
199
+ export declare function discoverLinkedWorktreeRepos(roots: readonly string[]): Promise<string[]>;
200
+ /**
201
+ * Newest mtime (ms) of `dir` and everything within `depth` levels below it,
202
+ * using lstat (symlinks are not followed). Async; yields every few entries.
203
+ *
204
+ * @param dir - Directory
205
+ * @param depth - Levels below `dir` to include (0 = only `dir`)
206
+ * @returns Newest mtime in ms, or null when `dir` cannot be stat'ed
207
+ */
208
+ export declare function latestTreeMtimeMs(dir: string, depth: number): Promise<number | null>;
209
+ /**
210
+ * Disk usage of a path in bytes (`du -sk` in a child process, so the event
211
+ * loop is never blocked; `du` does not follow symlinks).
212
+ *
213
+ * @param p - File or directory
214
+ * @returns Bytes, or null when `du` failed or timed out
215
+ */
216
+ export declare function diskUsageBytes(p: string): Promise<number | null>;
217
+ /**
218
+ * Human-readable size (`1.8 GB`, `512 MB`).
219
+ *
220
+ * @param bytes - Size in bytes
221
+ * @returns Short string with one decimal for GB
222
+ */
223
+ export declare function formatBytes(bytes: number): string;
224
+ //# sourceMappingURL=worktree-janitor.git.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worktree-janitor.git.d.ts","sourceRoot":"","sources":["../../../../../../backend/src/services/worktree/worktree-janitor.git.ts"],"names":[],"mappings":"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"}