crewly 1.20.152 → 1.20.153

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 (71) hide show
  1. package/config/roles/orchestrator/prompt.md +7 -0
  2. package/config/roles/team-leader/prompt.md +6 -0
  3. package/config/sops/developer/git-workflow.md +1 -0
  4. package/dist/backend/backend/src/constants.d.ts +92 -0
  5. package/dist/backend/backend/src/constants.d.ts.map +1 -1
  6. package/dist/backend/backend/src/constants.js +92 -0
  7. package/dist/backend/backend/src/constants.js.map +1 -1
  8. package/dist/backend/backend/src/controllers/worktree-janitor/worktree-janitor.routes.d.ts +29 -0
  9. package/dist/backend/backend/src/controllers/worktree-janitor/worktree-janitor.routes.d.ts.map +1 -0
  10. package/dist/backend/backend/src/controllers/worktree-janitor/worktree-janitor.routes.js +63 -0
  11. package/dist/backend/backend/src/controllers/worktree-janitor/worktree-janitor.routes.js.map +1 -0
  12. package/dist/backend/backend/src/index.d.ts +7 -0
  13. package/dist/backend/backend/src/index.d.ts.map +1 -1
  14. package/dist/backend/backend/src/index.js +114 -2
  15. package/dist/backend/backend/src/index.js.map +1 -1
  16. package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
  17. package/dist/backend/backend/src/routes/api.routes.js +3 -0
  18. package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
  19. package/dist/backend/backend/src/services/ai/prompt-modules/decision-rights.module.d.ts +18 -3
  20. package/dist/backend/backend/src/services/ai/prompt-modules/decision-rights.module.d.ts.map +1 -1
  21. package/dist/backend/backend/src/services/ai/prompt-modules/decision-rights.module.js +28 -4
  22. package/dist/backend/backend/src/services/ai/prompt-modules/decision-rights.module.js.map +1 -1
  23. package/dist/backend/backend/src/services/cloud/cloud-sync.types.d.ts +1 -1
  24. package/dist/backend/backend/src/services/cloud/cloud-sync.types.d.ts.map +1 -1
  25. package/dist/backend/backend/src/services/cloud/cloud-sync.types.js +1 -0
  26. package/dist/backend/backend/src/services/cloud/cloud-sync.types.js.map +1 -1
  27. package/dist/backend/backend/src/services/cloud/waiting-actions-inbound.service.d.ts +128 -0
  28. package/dist/backend/backend/src/services/cloud/waiting-actions-inbound.service.d.ts.map +1 -0
  29. package/dist/backend/backend/src/services/cloud/waiting-actions-inbound.service.js +279 -0
  30. package/dist/backend/backend/src/services/cloud/waiting-actions-inbound.service.js.map +1 -0
  31. package/dist/backend/backend/src/services/cloud/waiting-items-sync.service.d.ts +122 -0
  32. package/dist/backend/backend/src/services/cloud/waiting-items-sync.service.d.ts.map +1 -0
  33. package/dist/backend/backend/src/services/cloud/waiting-items-sync.service.js +261 -0
  34. package/dist/backend/backend/src/services/cloud/waiting-items-sync.service.js.map +1 -0
  35. package/dist/backend/backend/src/services/cloud/waiting-items.contract.d.ts +101 -0
  36. package/dist/backend/backend/src/services/cloud/waiting-items.contract.d.ts.map +1 -0
  37. package/dist/backend/backend/src/services/cloud/waiting-items.contract.js +113 -0
  38. package/dist/backend/backend/src/services/cloud/waiting-items.contract.js.map +1 -0
  39. package/dist/backend/backend/src/services/slack/slack-initializer.d.ts.map +1 -1
  40. package/dist/backend/backend/src/services/slack/slack-initializer.js +2 -1
  41. package/dist/backend/backend/src/services/slack/slack-initializer.js.map +1 -1
  42. package/dist/backend/backend/src/services/v3/request.service.d.ts +19 -0
  43. package/dist/backend/backend/src/services/v3/request.service.d.ts.map +1 -1
  44. package/dist/backend/backend/src/services/v3/request.service.js +36 -0
  45. package/dist/backend/backend/src/services/v3/request.service.js.map +1 -1
  46. package/dist/backend/backend/src/services/worktree/worktree-janitor.git.d.ts +131 -0
  47. package/dist/backend/backend/src/services/worktree/worktree-janitor.git.d.ts.map +1 -0
  48. package/dist/backend/backend/src/services/worktree/worktree-janitor.git.js +238 -0
  49. package/dist/backend/backend/src/services/worktree/worktree-janitor.git.js.map +1 -0
  50. package/dist/backend/backend/src/services/worktree/worktree-janitor.service.d.ts +241 -0
  51. package/dist/backend/backend/src/services/worktree/worktree-janitor.service.d.ts.map +1 -0
  52. package/dist/backend/backend/src/services/worktree/worktree-janitor.service.js +504 -0
  53. package/dist/backend/backend/src/services/worktree/worktree-janitor.service.js.map +1 -0
  54. package/dist/backend/build-info.json +2 -2
  55. package/dist/cli/backend/src/constants.d.ts +92 -0
  56. package/dist/cli/backend/src/constants.d.ts.map +1 -1
  57. package/dist/cli/backend/src/constants.js +92 -0
  58. package/dist/cli/backend/src/constants.js.map +1 -1
  59. package/dist/cli/backend/src/services/ai/prompt-modules/decision-rights.module.d.ts +18 -3
  60. package/dist/cli/backend/src/services/ai/prompt-modules/decision-rights.module.d.ts.map +1 -1
  61. package/dist/cli/backend/src/services/ai/prompt-modules/decision-rights.module.js +28 -4
  62. package/dist/cli/backend/src/services/ai/prompt-modules/decision-rights.module.js.map +1 -1
  63. package/dist/cli/backend/src/services/cloud/cloud-sync.types.d.ts +1 -1
  64. package/dist/cli/backend/src/services/cloud/cloud-sync.types.d.ts.map +1 -1
  65. package/dist/cli/backend/src/services/cloud/cloud-sync.types.js +1 -0
  66. package/dist/cli/backend/src/services/cloud/cloud-sync.types.js.map +1 -1
  67. package/dist/cli/backend/src/services/v3/request.service.d.ts +19 -0
  68. package/dist/cli/backend/src/services/v3/request.service.d.ts.map +1 -1
  69. package/dist/cli/backend/src/services/v3/request.service.js +36 -0
  70. package/dist/cli/backend/src/services/v3/request.service.js.map +1 -1
  71. package/package.json +1 -1
@@ -0,0 +1,504 @@
1
+ /**
2
+ * Worktree janitor — removes git worktrees whose work has landed.
3
+ *
4
+ * Agents create a worktree per code task (`<repo>/.claude/worktrees/<name>`,
5
+ * `/tmp/crewly-worktrees/<session>-<slug>`) and never delete it; a busy Mac
6
+ * collected 30+ of them, tens of GB. Owner decision: once the PR is merged the
7
+ * worktree goes away by itself.
8
+ *
9
+ * ## Which repos
10
+ * Every registered project path (projects store) that is inside a git repo,
11
+ * plus the Crewly package root when it is a dev checkout. Repos are
12
+ * de-duplicated by their main worktree.
13
+ *
14
+ * ## Removal rules — a worktree is removed only when ALL hold
15
+ * 1. It is not the main worktree, not bare, and not `prunable` (a missing
16
+ * directory is left to `git worktree prune`).
17
+ * 2. It is not locked (`git worktree lock`; Claude Code locks the worktree of
18
+ * a running subagent).
19
+ * 3. It is not under `<repo>/.crewly/worktrees/` — that directory belongs to
20
+ * the per-WorkItem worktree feature (#814), which does its own
21
+ * WorkItem-aware cleanup.
22
+ * 4. It is an agent worktree: its path is under `<main>/.claude/worktrees/`
23
+ * or `<tmp>/crewly-worktrees/` (`/tmp` and `os.tmpdir()`), OR its branch
24
+ * starts with an agent-only prefix (`worktree-agent-`, Claude Code's
25
+ * automatic subagent branch). Human-made worktrees such as
26
+ * `../crewly-wt-805` on `fix/...` are never touched. Deliberately narrow:
27
+ * a false "keep" costs disk, a false "remove" costs work.
28
+ * 5. Nothing is working in it: no Crewly agent session was started in it and
29
+ * no process of this user (PTY shells, runtimes, editors, test runners)
30
+ * has its cwd inside it. If the process probe fails, everything is kept.
31
+ * 6. It was last touched more than 2 hours ago (newest mtime of the
32
+ * directory and its git dir's index / HEAD / logs/HEAD).
33
+ * 7. `git status --porcelain --untracked-files=all` is empty: no tracked
34
+ * change and no untracked file. Ignored files (node_modules, dist) do not
35
+ * count. A failing status keeps it.
36
+ * 8. Its work has landed: HEAD is an ancestor of `origin/<default branch>`
37
+ * after a quiet `git fetch` (fetch errors ignored), OR — when `gh` is
38
+ * installed and authenticated — a MERGED PR has this branch as head AND
39
+ * its head commit equals the worktree HEAD (squash merges; commits made
40
+ * after the merge keep the worktree).
41
+ *
42
+ * ## Removal
43
+ * `git worktree remove <path>` without `--force`. If git refuses although
44
+ * the status is still clean (only ignored output left) and the worktree has
45
+ * no submodules, it is retried once with `--force`. Then the local branch is
46
+ * deleted (never a protected name), and `git worktree prune` runs per repo.
47
+ *
48
+ * Runs every 30 minutes (first run 10 minutes after boot). Kill switch:
49
+ * `CREWLY_WORKTREE_JANITOR=0`. Never throws; logs one summary line per run.
50
+ *
51
+ * @module services/worktree/worktree-janitor.service
52
+ */
53
+ import * as fs from 'fs';
54
+ import * as os from 'os';
55
+ import * as path from 'path';
56
+ import { WORKTREE_JANITOR_CONSTANTS } from '../../constants.js';
57
+ import { LoggerService } from '../core/logger.service.js';
58
+ import { canonicalPath, isJanitorDisabled, isPathInside, latestWorktreeMtimeMs, listProcessCwds, parsePorcelainWorktrees, runCommand, } from './worktree-janitor.git.js';
59
+ /**
60
+ * Periodic cleaner of finished agent worktrees. See the module JSDoc for the
61
+ * exact rules.
62
+ *
63
+ * @example
64
+ * ```typescript
65
+ * const janitor = WorktreeJanitorService.getInstance();
66
+ * janitor.start(); // first pass in 10 min, then every 30 min
67
+ * const plan = await janitor.plan(); // dry run: verdict per worktree
68
+ * const summary = await janitor.run(); // one real pass now
69
+ * ```
70
+ */
71
+ export class WorktreeJanitorService {
72
+ static instance = null;
73
+ opts;
74
+ firstRunTimer = null;
75
+ intervalTimer = null;
76
+ inFlight = null;
77
+ ghUsable;
78
+ lastSummary = null;
79
+ loggerInstance = null;
80
+ /**
81
+ * @param options - Injectable dependencies; defaults use the live backend
82
+ */
83
+ constructor(options = {}) {
84
+ this.opts = options;
85
+ }
86
+ /**
87
+ * Process-wide instance used by the server and the HTTP routes.
88
+ *
89
+ * @returns The singleton
90
+ */
91
+ static getInstance() {
92
+ if (!WorktreeJanitorService.instance)
93
+ WorktreeJanitorService.instance = new WorktreeJanitorService();
94
+ return WorktreeJanitorService.instance;
95
+ }
96
+ /** Reset the singleton (tests). */
97
+ static resetInstance() {
98
+ WorktreeJanitorService.instance?.stop();
99
+ WorktreeJanitorService.instance = null;
100
+ }
101
+ /**
102
+ * Whether the kill switch is on.
103
+ *
104
+ * @returns True when `CREWLY_WORKTREE_JANITOR` disables the janitor
105
+ */
106
+ isDisabled() {
107
+ return isJanitorDisabled(this.opts.env ?? process.env);
108
+ }
109
+ /**
110
+ * Schedule the periodic pass: first after FIRST_RUN_DELAY_MS, then every
111
+ * INTERVAL_MS. No-op when disabled or already started. Timers are unref'd.
112
+ *
113
+ * @returns True when scheduled
114
+ */
115
+ start() {
116
+ if (this.isDisabled() || this.firstRunTimer || this.intervalTimer)
117
+ return false;
118
+ this.firstRunTimer = setTimeout(() => {
119
+ this.firstRunTimer = null;
120
+ void this.run();
121
+ this.intervalTimer = setInterval(() => void this.run(), WORKTREE_JANITOR_CONSTANTS.INTERVAL_MS);
122
+ this.intervalTimer.unref?.();
123
+ }, WORKTREE_JANITOR_CONSTANTS.FIRST_RUN_DELAY_MS);
124
+ this.firstRunTimer.unref?.();
125
+ return true;
126
+ }
127
+ /** Cancel scheduled passes. */
128
+ stop() {
129
+ if (this.firstRunTimer)
130
+ clearTimeout(this.firstRunTimer);
131
+ if (this.intervalTimer)
132
+ clearInterval(this.intervalTimer);
133
+ this.firstRunTimer = null;
134
+ this.intervalTimer = null;
135
+ }
136
+ /**
137
+ * Summary of the last completed real pass, if any.
138
+ *
139
+ * @returns Last summary or null
140
+ */
141
+ getLastSummary() {
142
+ return this.lastSummary;
143
+ }
144
+ /**
145
+ * Dry run: the verdict for every worktree of every known repo. Removes
146
+ * nothing (it may `git fetch`).
147
+ *
148
+ * @returns Summary with per-worktree verdicts
149
+ */
150
+ async plan() {
151
+ return this.pass(true);
152
+ }
153
+ /**
154
+ * One real pass. Concurrent calls share the pass in flight. Never throws.
155
+ * Honours the kill switch.
156
+ *
157
+ * @returns Summary of what was removed and kept
158
+ */
159
+ async run() {
160
+ if (this.inFlight)
161
+ return this.inFlight;
162
+ this.inFlight = this.pass(false).finally(() => {
163
+ this.inFlight = null;
164
+ });
165
+ const summary = await this.inFlight;
166
+ if (!summary.dryRun && !summary.disabled)
167
+ this.lastSummary = summary;
168
+ return summary;
169
+ }
170
+ // ---------------------------------------------------------------------
171
+ get logger() {
172
+ if (this.opts.logger)
173
+ return this.opts.logger;
174
+ if (!this.loggerInstance)
175
+ this.loggerInstance = LoggerService.getInstance().createComponentLogger('WorktreeJanitor');
176
+ return this.loggerInstance;
177
+ }
178
+ git(args, options = {}) {
179
+ return runCommand(this.opts.gitBin ?? 'git', args, options);
180
+ }
181
+ async pass(dryRun) {
182
+ const started = Date.now();
183
+ const summary = {
184
+ startedAt: new Date(started).toISOString(),
185
+ durationMs: 0,
186
+ disabled: false,
187
+ dryRun,
188
+ repos: [],
189
+ removed: 0,
190
+ wouldRemove: 0,
191
+ kept: 0,
192
+ keptReasons: {},
193
+ worktrees: [],
194
+ };
195
+ if (!dryRun && this.isDisabled()) {
196
+ summary.disabled = true;
197
+ return summary;
198
+ }
199
+ try {
200
+ this.ghUsable = undefined;
201
+ const repos = await this.resolveRepos();
202
+ summary.repos = repos.map((r) => r.main);
203
+ const busy = await this.safeBusyCwds();
204
+ for (const repo of repos) {
205
+ let removedHere = 0;
206
+ for (const entry of repo.entries) {
207
+ const verdict = await this.evaluate(repo, entry, busy);
208
+ const outcome = { ...verdict, removed: false, forced: false, branchDeleted: false };
209
+ if (verdict.decision === 'remove' && !dryRun) {
210
+ await this.remove(repo, outcome);
211
+ if (outcome.removed)
212
+ removedHere++;
213
+ }
214
+ summary.worktrees.push(outcome);
215
+ if (outcome.removed)
216
+ summary.removed++;
217
+ else if (dryRun && verdict.decision === 'remove')
218
+ summary.wouldRemove++;
219
+ else {
220
+ summary.kept++;
221
+ const key = outcome.error ? 'remove-failed' : verdict.reason;
222
+ summary.keptReasons[key] = (summary.keptReasons[key] ?? 0) + 1;
223
+ }
224
+ }
225
+ if (!dryRun) {
226
+ const pr = await this.git(['-C', repo.main, 'worktree', 'prune']);
227
+ if (pr.code !== 0 && removedHere > 0) {
228
+ this.logger.warn('git worktree prune failed', { repo: repo.main, error: pr.stderr.trim() });
229
+ }
230
+ }
231
+ }
232
+ }
233
+ catch (err) {
234
+ this.logger.warn('Worktree janitor pass failed', { error: err instanceof Error ? err.message : String(err) });
235
+ }
236
+ summary.durationMs = Date.now() - started;
237
+ if (!dryRun) {
238
+ const reasons = Object.entries(summary.keptReasons)
239
+ .sort((a, b) => b[1] - a[1])
240
+ .map(([k, v]) => `${k}: ${v}`)
241
+ .join(', ');
242
+ this.logger.info(`Worktree janitor: removed ${summary.removed}, kept ${summary.kept}${reasons ? ` (${reasons})` : ''} across ${summary.repos.length} repo(s) in ${summary.durationMs}ms`, { removed: summary.worktrees.filter((w) => w.removed).map((w) => w.path) });
243
+ }
244
+ return summary;
245
+ }
246
+ async resolveRepos() {
247
+ const candidates = await (this.opts.listRepoPaths ?? defaultListRepoPaths)().catch(() => []);
248
+ const seen = new Map();
249
+ for (const candidate of candidates) {
250
+ if (!candidate || !fs.existsSync(candidate))
251
+ continue;
252
+ const r = await this.git(['-C', candidate, 'worktree', 'list', '--porcelain']);
253
+ if (r.code !== 0)
254
+ continue;
255
+ const entries = parsePorcelainWorktrees(r.stdout);
256
+ const main = entries.find((e) => e.isMain);
257
+ if (!main)
258
+ continue;
259
+ const key = canonicalPath(main.path);
260
+ if (seen.has(key))
261
+ continue;
262
+ seen.set(key, { main: main.path, entries, fetched: false, defaultRef: undefined });
263
+ }
264
+ return [...seen.values()];
265
+ }
266
+ async safeBusyCwds() {
267
+ try {
268
+ return await (this.opts.listBusyCwds ?? defaultListBusyCwds)();
269
+ }
270
+ catch {
271
+ return null;
272
+ }
273
+ }
274
+ isAgentWorktree(repo, entry) {
275
+ if (isPathInside(entry.path, path.join(repo.main, WORKTREE_JANITOR_CONSTANTS.AGENT_WORKTREE_DIR)))
276
+ return true;
277
+ const tmpRoots = this.opts.tmpRoots ?? ['/tmp', os.tmpdir()];
278
+ for (const root of tmpRoots) {
279
+ if (isPathInside(entry.path, path.join(root, WORKTREE_JANITOR_CONSTANTS.TMP_WORKTREE_DIR)))
280
+ return true;
281
+ }
282
+ const branch = entry.branch;
283
+ return !!branch && WORKTREE_JANITOR_CONSTANTS.AGENT_BRANCH_PREFIXES.some((p) => branch.startsWith(p));
284
+ }
285
+ /**
286
+ * Apply the removal rules to one worktree (read-only apart from fetch).
287
+ */
288
+ async evaluate(repo, entry, busy) {
289
+ const base = { repo: repo.main, path: entry.path, branch: entry.branch, head: entry.head };
290
+ const keep = (reason, detail, idleMs) => ({
291
+ ...base,
292
+ decision: 'keep',
293
+ reason,
294
+ ...(detail ? { detail } : {}),
295
+ ...(idleMs !== undefined ? { idleMs } : {}),
296
+ });
297
+ if (entry.isMain)
298
+ return keep('main-worktree');
299
+ if (entry.bare)
300
+ return keep('bare');
301
+ if (entry.prunable || !fs.existsSync(entry.path))
302
+ return keep('prunable', 'directory missing; left to git worktree prune');
303
+ if (entry.locked)
304
+ return keep('locked', entry.lockReason);
305
+ if (isPathInside(entry.path, path.join(repo.main, WORKTREE_JANITOR_CONSTANTS.MANAGED_WORKTREE_DIR))) {
306
+ return keep('managed-by-workitem-worktrees');
307
+ }
308
+ if (!this.isAgentWorktree(repo, entry))
309
+ return keep('not-agent-worktree');
310
+ if (busy === null)
311
+ return keep('cwd-probe-failed');
312
+ const hit = busy.find((b) => isPathInside(b.cwd, entry.path));
313
+ if (hit)
314
+ return keep(hit.source === 'agent-session' ? 'agent-session-inside' : 'process-inside', hit.cwd);
315
+ const gitDirR = await this.git(['-C', entry.path, 'rev-parse', '--absolute-git-dir']);
316
+ const gitDir = gitDirR.code === 0 ? gitDirR.stdout.trim() : null;
317
+ const mtime = latestWorktreeMtimeMs(entry.path, gitDir);
318
+ const now = (this.opts.now ?? Date.now)();
319
+ const idleMs = mtime === null ? undefined : Math.max(0, now - mtime);
320
+ const minIdle = this.opts.minIdleMs ?? WORKTREE_JANITOR_CONSTANTS.MIN_IDLE_MS;
321
+ if (idleMs === undefined || idleMs < minIdle)
322
+ return keep('recent', undefined, idleMs);
323
+ const status = await this.git(['-C', entry.path, 'status', '--porcelain', '--untracked-files=all']);
324
+ if (status.code !== 0)
325
+ return keep('status-failed', status.stderr.trim().slice(0, 200), idleMs);
326
+ const dirtyLines = status.stdout.split('\n').filter((l) => l.trim() !== '');
327
+ if (dirtyLines.length > 0)
328
+ return keep('dirty', `${dirtyLines.length} changed/untracked path(s)`, idleMs);
329
+ if (!entry.head)
330
+ return keep('not-merged', 'no HEAD', idleMs);
331
+ const defaultRef = await this.defaultRef(repo);
332
+ if (defaultRef) {
333
+ const anc = await this.git(['-C', repo.main, 'merge-base', '--is-ancestor', entry.head, defaultRef]);
334
+ if (anc.code === 0)
335
+ return { ...base, decision: 'remove', reason: 'merged', detail: `HEAD is in ${defaultRef}`, idleMs };
336
+ }
337
+ if (entry.branch) {
338
+ const pr = await this.mergedPrFor(repo, entry.branch, entry.head);
339
+ if (pr !== null)
340
+ return { ...base, decision: 'remove', reason: 'pr-merged', detail: `PR #${pr} merged`, idleMs };
341
+ }
342
+ return keep('not-merged', defaultRef ? `not in ${defaultRef}, no merged PR` : 'no origin default branch, no merged PR', idleMs);
343
+ }
344
+ /** `origin/<default>` after one quiet fetch per repo per pass; null when there is no origin. */
345
+ async defaultRef(repo) {
346
+ if (repo.defaultRef !== undefined)
347
+ return repo.defaultRef;
348
+ let ref = null;
349
+ const sym = await this.git(['-C', repo.main, 'symbolic-ref', '--quiet', '--short', 'refs/remotes/origin/HEAD']);
350
+ if (sym.code === 0 && sym.stdout.trim())
351
+ ref = sym.stdout.trim();
352
+ if (!ref) {
353
+ for (const name of ['main', 'master']) {
354
+ const v = await this.git(['-C', repo.main, 'rev-parse', '--verify', '--quiet', `refs/remotes/origin/${name}`]);
355
+ if (v.code === 0) {
356
+ ref = `origin/${name}`;
357
+ break;
358
+ }
359
+ }
360
+ }
361
+ if (!ref) {
362
+ const remote = await this.git(['-C', repo.main, 'remote', 'get-url', 'origin']);
363
+ if (remote.code === 0) {
364
+ // origin exists but no remote-tracking ref yet — try one fetch.
365
+ await this.git(['-C', repo.main, 'fetch', '--quiet', 'origin'], { timeoutMs: WORKTREE_JANITOR_CONSTANTS.FETCH_TIMEOUT_MS });
366
+ repo.fetched = true;
367
+ for (const name of ['main', 'master']) {
368
+ const v = await this.git(['-C', repo.main, 'rev-parse', '--verify', '--quiet', `refs/remotes/origin/${name}`]);
369
+ if (v.code === 0) {
370
+ ref = `origin/${name}`;
371
+ break;
372
+ }
373
+ }
374
+ }
375
+ }
376
+ if (ref && !repo.fetched) {
377
+ const branchName = ref.replace(/^origin\//, '');
378
+ // Errors (offline, auth) are ignored: the stale ref is still a safe lower bound.
379
+ await this.git(['-C', repo.main, 'fetch', '--quiet', 'origin', branchName], {
380
+ timeoutMs: WORKTREE_JANITOR_CONSTANTS.FETCH_TIMEOUT_MS,
381
+ });
382
+ repo.fetched = true;
383
+ }
384
+ repo.defaultRef = ref;
385
+ return ref;
386
+ }
387
+ /** Number of a MERGED PR whose head is `branch` at exactly `head`, or null. */
388
+ async mergedPrFor(repo, branch, head) {
389
+ const gh = this.opts.ghBin === undefined ? 'gh' : this.opts.ghBin;
390
+ if (!gh)
391
+ return null;
392
+ if (this.ghUsable === undefined) {
393
+ const auth = await runCommand(gh, ['auth', 'status'], { cwd: repo.main, timeoutMs: WORKTREE_JANITOR_CONSTANTS.GH_TIMEOUT_MS });
394
+ this.ghUsable = auth.code === 0;
395
+ }
396
+ if (!this.ghUsable)
397
+ return null;
398
+ const r = await runCommand(gh, ['pr', 'list', '--head', branch, '--state', 'merged', '--json', 'number,headRefOid,state', '--limit', String(WORKTREE_JANITOR_CONSTANTS.GH_PR_LIMIT)], { cwd: repo.main, timeoutMs: WORKTREE_JANITOR_CONSTANTS.GH_TIMEOUT_MS });
399
+ if (r.code !== 0)
400
+ return null;
401
+ try {
402
+ const prs = JSON.parse(r.stdout);
403
+ if (!Array.isArray(prs))
404
+ return null;
405
+ const match = prs.find((p) => p.state === 'MERGED' && p.headRefOid === head && typeof p.number === 'number');
406
+ return match ? match.number : null;
407
+ }
408
+ catch {
409
+ return null;
410
+ }
411
+ }
412
+ /** Remove one worktree whose verdict is `remove`, then its branch. Never throws. */
413
+ async remove(repo, outcome) {
414
+ let r = await this.git(['-C', repo.main, 'worktree', 'remove', outcome.path]);
415
+ if (r.code !== 0) {
416
+ // Force only when git's refusal can only be ignored output: status is
417
+ // still clean (untracked included) and there are no submodules.
418
+ const status = await this.git(['-C', outcome.path, 'status', '--porcelain', '--untracked-files=all', '--ignore-submodules=none']);
419
+ const clean = status.code === 0 && status.stdout.trim() === '';
420
+ const hasSubmodules = fs.existsSync(path.join(outcome.path, '.gitmodules'));
421
+ if (clean && !hasSubmodules) {
422
+ r = await this.git(['-C', repo.main, 'worktree', 'remove', '--force', outcome.path]);
423
+ if (r.code === 0)
424
+ outcome.forced = true;
425
+ }
426
+ }
427
+ if (r.code !== 0) {
428
+ outcome.error = (r.stderr || 'git worktree remove failed').trim().slice(0, 300);
429
+ return;
430
+ }
431
+ outcome.removed = true;
432
+ const branch = outcome.branch;
433
+ if (!branch || WORKTREE_JANITOR_CONSTANTS.PROTECTED_BRANCHES.includes(branch))
434
+ return;
435
+ if (repo.defaultRef && branch === repo.defaultRef.replace(/^origin\//, ''))
436
+ return;
437
+ // Only delete when the branch still points at the commit we verified.
438
+ const tip = await this.git(['-C', repo.main, 'rev-parse', '--verify', '--quiet', `refs/heads/${branch}`]);
439
+ if (tip.code !== 0 || tip.stdout.trim() !== outcome.head)
440
+ return;
441
+ // -D: the merge was verified against origin (or the PR), which a local
442
+ // `-d` cannot see when the local default branch is behind.
443
+ const del = await this.git(['-C', repo.main, 'branch', '-D', branch]);
444
+ outcome.branchDeleted = del.code === 0;
445
+ }
446
+ }
447
+ /**
448
+ * Default repo candidates: every registered project path, plus the running
449
+ * Crewly package root when it is a git checkout.
450
+ *
451
+ * @returns Paths (may include non-git paths; those are skipped)
452
+ */
453
+ export async function defaultListRepoPaths() {
454
+ const out = [];
455
+ try {
456
+ const { StorageService } = await import('../core/storage.service.js');
457
+ const projects = await StorageService.getInstance().getProjects();
458
+ for (const p of projects)
459
+ if (p?.path)
460
+ out.push(p.path);
461
+ }
462
+ catch {
463
+ // projects store unavailable — continue with the dev checkout only
464
+ }
465
+ try {
466
+ const { resolveRunningPackageRoot, detectInstall } = await import('../system/auto-update.utils.js');
467
+ const info = detectInstall(resolveRunningPackageRoot(process.argv[1], process.cwd()));
468
+ if (info.kind === 'dev-checkout' && info.packageRoot)
469
+ out.push(info.packageRoot);
470
+ }
471
+ catch {
472
+ // not resolvable — fine
473
+ }
474
+ return out;
475
+ }
476
+ /**
477
+ * Default busy-cwd probe: the start directory of every live Crewly session,
478
+ * plus the cwd of every process of this user (which covers the PTY shells,
479
+ * the agent runtimes under them, and anything the owner has open).
480
+ *
481
+ * @returns Busy directories, or null when the process probe failed
482
+ */
483
+ export async function defaultListBusyCwds() {
484
+ const out = [];
485
+ try {
486
+ const { getSessionBackendSync } = await import('../session/session-backend.factory.js');
487
+ const backend = getSessionBackendSync();
488
+ for (const name of backend?.listSessions() ?? []) {
489
+ const cwd = backend?.getSession(name)?.cwd;
490
+ if (cwd)
491
+ out.push({ cwd, source: 'agent-session' });
492
+ }
493
+ }
494
+ catch {
495
+ // no session backend — process probe still covers live shells
496
+ }
497
+ const procs = await listProcessCwds();
498
+ if (procs === null)
499
+ return null;
500
+ for (const cwd of procs)
501
+ out.push({ cwd, source: 'process' });
502
+ return out;
503
+ }
504
+ //# sourceMappingURL=worktree-janitor.service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"worktree-janitor.service.js","sourceRoot":"","sources":["../../../../../../backend/src/services/worktree/worktree-janitor.service.ts"],"names":[],"mappings":"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"}
@@ -1,4 +1,4 @@
1
1
  {
2
- "commit": "ed77c4ba70d09db82cb33fe5dbe9fc49e4273c8b",
3
- "builtAt": "2026-09-28T11:24:02.491Z"
2
+ "commit": "21d72a824b9714604d76101637191afd25b0f13e",
3
+ "builtAt": "2026-09-28T11:43:27.888Z"
4
4
  }
@@ -1294,6 +1294,61 @@ export declare const NOTIFY_RECONCILIATION_CONSTANTS: {
1294
1294
  /** Delay before first reconciliation run after startup (30 seconds) */
1295
1295
  readonly STARTUP_DELAY_MS: number;
1296
1296
  };
1297
+ /**
1298
+ * Constants for the worktree janitor (`WorktreeJanitorService`).
1299
+ *
1300
+ * Agents create a git worktree per code task and never delete it. The janitor
1301
+ * removes a worktree once its work has landed (merged into origin's default
1302
+ * branch, or its PR is MERGED) and nothing is using it. See the service JSDoc
1303
+ * for the full list of removal rules.
1304
+ */
1305
+ export declare const WORKTREE_JANITOR_CONSTANTS: {
1306
+ /** Env kill switch: `CREWLY_WORKTREE_JANITOR=0` (or `off`/`false`) disables the janitor. */
1307
+ readonly ENV_VAR: "CREWLY_WORKTREE_JANITOR";
1308
+ /** Values of {@link WORKTREE_JANITOR_CONSTANTS.ENV_VAR} that disable it (lower-cased). */
1309
+ readonly DISABLED_VALUES: readonly string[];
1310
+ /** Interval between automatic runs (30 minutes). */
1311
+ readonly INTERVAL_MS: number;
1312
+ /** Delay before the first automatic run after startup (10 minutes). */
1313
+ readonly FIRST_RUN_DELAY_MS: number;
1314
+ /** A worktree touched more recently than this is kept (2 hours). */
1315
+ readonly MIN_IDLE_MS: number;
1316
+ /**
1317
+ * Directory, relative to the repo's main worktree, where agent tools
1318
+ * (Claude Code subagents, Crewly agents) create per-task worktrees.
1319
+ */
1320
+ readonly AGENT_WORKTREE_DIR: ".claude/worktrees";
1321
+ /**
1322
+ * Directory name under the system temp dir (`/tmp`, `os.tmpdir()`) that
1323
+ * the developer role prompt tells agents to put worktrees in.
1324
+ */
1325
+ readonly TMP_WORKTREE_DIR: "crewly-worktrees";
1326
+ /**
1327
+ * Directory owned by the per-WorkItem worktree feature (#814), which does
1328
+ * its own WorkItem-aware cleanup. The janitor never touches it.
1329
+ */
1330
+ readonly MANAGED_WORKTREE_DIR: ".crewly/worktrees";
1331
+ /**
1332
+ * Branch-name prefixes that only agents create. A worktree elsewhere on
1333
+ * disk is eligible only when its branch starts with one of these.
1334
+ * `worktree-agent-` is Claude Code's automatic subagent branch.
1335
+ */
1336
+ readonly AGENT_BRANCH_PREFIXES: readonly string[];
1337
+ /** Branches that are never deleted, whatever the verdict. */
1338
+ readonly PROTECTED_BRANCHES: readonly string[];
1339
+ /** Timeout of one local git command (ms). */
1340
+ readonly GIT_TIMEOUT_MS: 30000;
1341
+ /** Timeout of `git fetch` (ms). */
1342
+ readonly FETCH_TIMEOUT_MS: 60000;
1343
+ /** Timeout of one `gh` call (ms). */
1344
+ readonly GH_TIMEOUT_MS: 20000;
1345
+ /** Timeout of the process-cwd probe (`lsof`) (ms). */
1346
+ readonly CWD_PROBE_TIMEOUT_MS: 20000;
1347
+ /** Max PRs `gh pr list --head <branch>` returns per branch. */
1348
+ readonly GH_PR_LIMIT: 20;
1349
+ /** Max bytes of command output kept (stdout/stderr each). */
1350
+ readonly MAX_OUTPUT_BYTES: number;
1351
+ };
1297
1352
  /**
1298
1353
  * Constants for cron next-run computation (`getNextRunTime`).
1299
1354
  */
@@ -3662,6 +3717,43 @@ export declare const CLOUD_TALK_CONSTANTS: {
3662
3717
  /** Ticket-intake origin for a Talk message (the portal). */
3663
3718
  readonly INTAKE_ORIGIN: "portal";
3664
3719
  };
3720
+ /**
3721
+ * "Waiting on you" synced to Crewly Cloud (specs/unified-conversations-cloud-store.md §F, Phase 5):
3722
+ * tickets in 待验收 are uploaded as text snapshots; the owner's accept /
3723
+ * send-back from the portal comes back as a `waiting_action` relay push.
3724
+ */
3725
+ export declare const WAITING_SYNC_CONSTANTS: {
3726
+ /** Machine → Cloud snapshot upload. */
3727
+ readonly INGEST_PATH: "/api/cloud/conversations/waiting/ingest";
3728
+ /** `GET <path>/:actionId?instanceId=` fetches an action; `POST <path>/:actionId/result` reports it. */
3729
+ readonly ACTIONS_PATH: "/api/cloud/conversations/waiting/actions";
3730
+ /** Relay message `type` Cloud pushes for an owner action. */
3731
+ readonly RELAY_MESSAGE_TYPE: "waiting_action";
3732
+ /** Capability advertised to Cloud (heartbeat + uploads) once the action handler runs. */
3733
+ readonly CAPABILITY: "waiting_actions";
3734
+ /** Gather ticket changes this long before uploading (ms). */
3735
+ readonly DEBOUNCE_MS: 2000;
3736
+ /** Recompute the set this often and upload when it changed (catches WorkItem-driven changes) (ms). */
3737
+ readonly CHECK_INTERVAL_MS: 30000;
3738
+ /** Full snapshot (Cloud drops anything not in it) at least this often (ms). */
3739
+ readonly FULL_SYNC_INTERVAL_MS: number;
3740
+ /** Backoff after a failed upload: first delay and cap (ms). */
3741
+ readonly BACKOFF_INITIAL_MS: 5000;
3742
+ readonly BACKOFF_MAX_MS: number;
3743
+ /** 404 / 503 / 400 from Cloud: try again after this long (ms). */
3744
+ readonly UNAVAILABLE_RETRY_MS: number;
3745
+ /** Timeout of one Cloud call (ms). */
3746
+ readonly REQUEST_TIMEOUT_MS: 15000;
3747
+ /** Longest title / excerpt sent (chars; Cloud truncates beyond its own limits too). */
3748
+ readonly MAX_TITLE_CHARS: 500;
3749
+ readonly MAX_EXCERPT_CHARS: 2000;
3750
+ /** Delays before retrying a failed action fetch (ms); Cloud re-pushes after 5 min anyway. */
3751
+ readonly FETCH_RETRY_DELAYS_MS: readonly [1000, 5000, 15000];
3752
+ /** Actions already carried out are remembered this long, so a re-push only re-reports (ms). */
3753
+ readonly DONE_ACTIONS_TTL_MS: number;
3754
+ /** Most remembered actions. */
3755
+ readonly DONE_ACTIONS_MAX: 500;
3756
+ };
3665
3757
  export type AgentStatus = (typeof CREWLY_CONSTANTS.AGENT_STATUSES)[keyof typeof CREWLY_CONSTANTS.AGENT_STATUSES];
3666
3758
  export type WorkingStatus = (typeof CREWLY_CONSTANTS.WORKING_STATUSES)[keyof typeof CREWLY_CONSTANTS.WORKING_STATUSES];
3667
3759
  export type RuntimeType = (typeof RUNTIME_TYPES)[keyof typeof RUNTIME_TYPES];