crewly 1.20.151 → 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.
- package/config/roles/orchestrator/prompt.md +7 -0
- package/config/roles/team-leader/prompt.md +6 -0
- package/config/sops/developer/git-workflow.md +1 -0
- package/dist/backend/backend/src/constants.d.ts +92 -0
- package/dist/backend/backend/src/constants.d.ts.map +1 -1
- package/dist/backend/backend/src/constants.js +92 -0
- package/dist/backend/backend/src/constants.js.map +1 -1
- package/dist/backend/backend/src/controllers/worktree-janitor/worktree-janitor.routes.d.ts +29 -0
- package/dist/backend/backend/src/controllers/worktree-janitor/worktree-janitor.routes.d.ts.map +1 -0
- package/dist/backend/backend/src/controllers/worktree-janitor/worktree-janitor.routes.js +63 -0
- package/dist/backend/backend/src/controllers/worktree-janitor/worktree-janitor.routes.js.map +1 -0
- package/dist/backend/backend/src/index.d.ts +7 -0
- package/dist/backend/backend/src/index.d.ts.map +1 -1
- package/dist/backend/backend/src/index.js +114 -2
- package/dist/backend/backend/src/index.js.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.d.ts.map +1 -1
- package/dist/backend/backend/src/routes/api.routes.js +3 -0
- package/dist/backend/backend/src/routes/api.routes.js.map +1 -1
- package/dist/backend/backend/src/services/agent/fresh-task-conversation.service.d.ts +14 -5
- package/dist/backend/backend/src/services/agent/fresh-task-conversation.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/agent/fresh-task-conversation.service.js +41 -10
- package/dist/backend/backend/src/services/agent/fresh-task-conversation.service.js.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-modules/decision-rights.module.d.ts +18 -3
- package/dist/backend/backend/src/services/ai/prompt-modules/decision-rights.module.d.ts.map +1 -1
- package/dist/backend/backend/src/services/ai/prompt-modules/decision-rights.module.js +28 -4
- package/dist/backend/backend/src/services/ai/prompt-modules/decision-rights.module.js.map +1 -1
- package/dist/backend/backend/src/services/cloud/cloud-sync.types.d.ts +1 -1
- package/dist/backend/backend/src/services/cloud/cloud-sync.types.d.ts.map +1 -1
- package/dist/backend/backend/src/services/cloud/cloud-sync.types.js +1 -0
- package/dist/backend/backend/src/services/cloud/cloud-sync.types.js.map +1 -1
- package/dist/backend/backend/src/services/cloud/waiting-actions-inbound.service.d.ts +128 -0
- package/dist/backend/backend/src/services/cloud/waiting-actions-inbound.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/cloud/waiting-actions-inbound.service.js +279 -0
- package/dist/backend/backend/src/services/cloud/waiting-actions-inbound.service.js.map +1 -0
- package/dist/backend/backend/src/services/cloud/waiting-items-sync.service.d.ts +122 -0
- package/dist/backend/backend/src/services/cloud/waiting-items-sync.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/cloud/waiting-items-sync.service.js +261 -0
- package/dist/backend/backend/src/services/cloud/waiting-items-sync.service.js.map +1 -0
- package/dist/backend/backend/src/services/cloud/waiting-items.contract.d.ts +101 -0
- package/dist/backend/backend/src/services/cloud/waiting-items.contract.d.ts.map +1 -0
- package/dist/backend/backend/src/services/cloud/waiting-items.contract.js +113 -0
- package/dist/backend/backend/src/services/cloud/waiting-items.contract.js.map +1 -0
- package/dist/backend/backend/src/services/slack/slack-initializer.d.ts.map +1 -1
- package/dist/backend/backend/src/services/slack/slack-initializer.js +2 -1
- package/dist/backend/backend/src/services/slack/slack-initializer.js.map +1 -1
- package/dist/backend/backend/src/services/v3/request.service.d.ts +19 -0
- package/dist/backend/backend/src/services/v3/request.service.d.ts.map +1 -1
- package/dist/backend/backend/src/services/v3/request.service.js +36 -0
- package/dist/backend/backend/src/services/v3/request.service.js.map +1 -1
- package/dist/backend/backend/src/services/worktree/worktree-janitor.git.d.ts +131 -0
- package/dist/backend/backend/src/services/worktree/worktree-janitor.git.d.ts.map +1 -0
- package/dist/backend/backend/src/services/worktree/worktree-janitor.git.js +238 -0
- package/dist/backend/backend/src/services/worktree/worktree-janitor.git.js.map +1 -0
- package/dist/backend/backend/src/services/worktree/worktree-janitor.service.d.ts +241 -0
- package/dist/backend/backend/src/services/worktree/worktree-janitor.service.d.ts.map +1 -0
- package/dist/backend/backend/src/services/worktree/worktree-janitor.service.js +504 -0
- package/dist/backend/backend/src/services/worktree/worktree-janitor.service.js.map +1 -0
- package/dist/backend/build-info.json +2 -2
- package/dist/cli/backend/src/constants.d.ts +92 -0
- package/dist/cli/backend/src/constants.d.ts.map +1 -1
- package/dist/cli/backend/src/constants.js +92 -0
- package/dist/cli/backend/src/constants.js.map +1 -1
- package/dist/cli/backend/src/services/ai/prompt-modules/decision-rights.module.d.ts +18 -3
- package/dist/cli/backend/src/services/ai/prompt-modules/decision-rights.module.d.ts.map +1 -1
- package/dist/cli/backend/src/services/ai/prompt-modules/decision-rights.module.js +28 -4
- package/dist/cli/backend/src/services/ai/prompt-modules/decision-rights.module.js.map +1 -1
- package/dist/cli/backend/src/services/cloud/cloud-sync.types.d.ts +1 -1
- package/dist/cli/backend/src/services/cloud/cloud-sync.types.d.ts.map +1 -1
- package/dist/cli/backend/src/services/cloud/cloud-sync.types.js +1 -0
- package/dist/cli/backend/src/services/cloud/cloud-sync.types.js.map +1 -1
- package/dist/cli/backend/src/services/v3/request.service.d.ts +19 -0
- package/dist/cli/backend/src/services/v3/request.service.d.ts.map +1 -1
- package/dist/cli/backend/src/services/v3/request.service.js +36 -0
- package/dist/cli/backend/src/services/v3/request.service.js.map +1 -1
- 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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"}
|
|
@@ -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];
|