@clipboard-health/groundcrew 2.1.0 → 2.1.1
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.
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"boardSource.d.ts","sourceRoot":"","sources":["../../src/lib/boardSource.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAA6C,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAM7F,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B;AAED,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,0FAA0F;IAC1F,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,0FAA0F;IAC1F,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,IAAI,eAAe,CAExE;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,KAAK,EAAE,CAAC;CACjB;AAED,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,YAAmB,UAAU,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,EAMjF;CACF;AAED,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,8DAA8D;IAC9D,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;CAC9B;AAED,UAAU,eAAe;IACvB,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,YAAY,CAAC;CACtB;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,eAAe,GAAG,WAAW,CAUpE;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAEhF;
|
|
1
|
+
{"version":3,"file":"boardSource.d.ts","sourceRoot":"","sources":["../../src/lib/boardSource.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,aAAa,CAAC;AAEhD,OAAO,EAA6C,KAAK,cAAc,EAAE,MAAM,aAAa,CAAC;AAM7F,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAC;IACX,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,GAAG,SAAS,CAAC;CAC5B;AAED,MAAM,WAAW,KAAK;IACpB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,0FAA0F;IAC1F,UAAU,EAAE,MAAM,GAAG,SAAS,CAAC;IAC/B,0FAA0F;IAC1F,KAAK,EAAE,MAAM,GAAG,SAAS,CAAC;IAC1B,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,EAAE,CAAC;IACpB,eAAe,EAAE,OAAO,CAAC;CAC1B;AAED;;;;GAIG;AACH,MAAM,MAAM,eAAe,GAAG,KAAK,GAAG;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,UAAU,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,KAAK,GAAG,KAAK,IAAI,eAAe,CAExE;AAED,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,KAAK,EAAE,CAAC;CACjB;AAED,qBAAa,yBAA0B,SAAQ,KAAK;IAClD,YAAmB,UAAU,EAAE;QAAE,MAAM,EAAE,MAAM,CAAC;QAAC,YAAY,EAAE,SAAS,MAAM,EAAE,CAAA;KAAE,EAMjF;CACF;AAED,MAAM,WAAW,WAAW;IAC1B;;;OAGG;IACH,MAAM,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACxB,8DAA8D;IAC9D,KAAK,IAAI,OAAO,CAAC,UAAU,CAAC,CAAC;CAC9B;AAED,UAAU,eAAe;IACvB,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,YAAY,CAAC;CACtB;AAED,wBAAgB,iBAAiB,CAAC,IAAI,EAAE,eAAe,GAAG,WAAW,CAUpE;AAED,wBAAgB,gBAAgB,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,cAAc,GAAG,OAAO,CAEhF;AAmMD,UAAU,aAAa;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,EAAE,MAAM,CAAC;IACpB,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;CAChB;AAID;;;;GAIG;AACH,wBAAsB,kBAAkB,CAAC,UAAU,EAAE;IACnD,MAAM,EAAE,YAAY,CAAC;IACrB,MAAM,EAAE,cAAc,CAAC;IACvB,MAAM,EAAE,MAAM,CAAC;CAChB,GAAG,OAAO,CAAC,aAAa,CAAC,CAmDzB"}
|
package/dist/lib/boardSource.js
CHANGED
|
@@ -163,7 +163,11 @@ function escapeRegex(value) {
|
|
|
163
163
|
// must beat `api` when both are configured. `\b` treats `-` as a word
|
|
164
164
|
// boundary, so without this ordering `api` would win on `api-admin`.
|
|
165
165
|
function buildRepositoryRegex(config) {
|
|
166
|
-
const
|
|
166
|
+
const candidates = config.workspace.knownRepositories.flatMap((repo) => {
|
|
167
|
+
const slashIndex = repo.indexOf("/");
|
|
168
|
+
return slashIndex === -1 ? [repo] : [repo, repo.slice(slashIndex + 1)];
|
|
169
|
+
});
|
|
170
|
+
const alternation = candidates
|
|
167
171
|
.toSorted((a, b) => b.length - a.length)
|
|
168
172
|
.map(escapeRegex)
|
|
169
173
|
.join("|");
|
|
@@ -223,14 +227,29 @@ function parseRepository(arguments_) {
|
|
|
223
227
|
repositories: config.workspace.knownRepositories,
|
|
224
228
|
});
|
|
225
229
|
}
|
|
226
|
-
const
|
|
227
|
-
if (
|
|
230
|
+
const matched = repositoryRegex.exec(description)?.[1];
|
|
231
|
+
if (matched === undefined) {
|
|
228
232
|
throw new RepositoryResolutionError({
|
|
229
233
|
ticket,
|
|
230
234
|
repositories: config.workspace.knownRepositories,
|
|
231
235
|
});
|
|
232
236
|
}
|
|
233
|
-
|
|
237
|
+
// Resolve the match to a known repo. The regex may capture a bare repo name
|
|
238
|
+
// (no org prefix) when only that appears in the description; the filter
|
|
239
|
+
// handles both full "owner/repo" and bare "repo" matches. Reject if
|
|
240
|
+
// ambiguous (same bare name under multiple orgs).
|
|
241
|
+
const candidates = config.workspace.knownRepositories.filter((r) => r === matched || r.endsWith(`/${matched}`));
|
|
242
|
+
if (candidates.length !== 1) {
|
|
243
|
+
throw new RepositoryResolutionError({
|
|
244
|
+
ticket,
|
|
245
|
+
repositories: config.workspace.knownRepositories,
|
|
246
|
+
});
|
|
247
|
+
}
|
|
248
|
+
/* v8 ignore next 3 @preserve -- candidates.length===1 guarantees [0] is defined */
|
|
249
|
+
if (candidates[0] === undefined) {
|
|
250
|
+
throw new Error("unreachable");
|
|
251
|
+
}
|
|
252
|
+
return candidates[0];
|
|
234
253
|
}
|
|
235
254
|
function parseAgentLabels(labels, config) {
|
|
236
255
|
const agentLabels = labels.filter((label) => label.name.startsWith(AGENT_LABEL_PREFIX));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"worktrees.d.ts","sourceRoot":"","sources":["../../src/lib/worktrees.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,OAAO,EAAE,KAAK,cAAc,EAAc,MAAM,iBAAiB,CAAC;AAIlE,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC;AAElC,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,YAAY,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB;
|
|
1
|
+
{"version":3,"file":"worktrees.d.ts","sourceRoot":"","sources":["../../src/lib/worktrees.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAOH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAElD,OAAO,EAAE,KAAK,cAAc,EAAc,MAAM,iBAAiB,CAAC;AAIlE,MAAM,MAAM,YAAY,GAAG,MAAM,CAAC;AAElC,MAAM,WAAW,aAAa;IAC5B,UAAU,EAAE,MAAM,CAAC;IACnB,sDAAsD;IACtD,MAAM,EAAE,MAAM,CAAC;IACf,+CAA+C;IAC/C,UAAU,EAAE,MAAM,CAAC;IACnB,GAAG,EAAE,MAAM,CAAC;IACZ,IAAI,EAAE,YAAY,CAAC;CACpB;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,MAAM,EAAE,MAAM,CAAC;CAChB;AAoSD,iBAAS,IAAI,CAAC,MAAM,EAAE,cAAc,GAAG,aAAa,EAAE,CAErD;AAED,iBAAS,YAAY,CAAC,MAAM,EAAE,cAAc,EAAE,MAAM,EAAE,MAAM,GAAG,aAAa,EAAE,CAE7E;AAED,iBAAe,MAAM,CACnB,MAAM,EAAE,cAAc,EACtB,IAAI,EAAE,YAAY,EAClB,MAAM,CAAC,EAAE,WAAW,GACnB,OAAO,CAAC,aAAa,CAAC,CAUxB;AAED,iBAAe,MAAM,CACnB,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,aAAa,EACpB,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,GAClD,OAAO,CAAC,IAAI,CAAC,CAKf;AAED,MAAM,MAAM,YAAY,GAAG,iBAAiB,GAAG,iBAAiB,CAAC;AAEjE,MAAM,WAAW,eAAe;IAC9B,KAAK,EAAE,aAAa,CAAC;IACrB,IAAI,EAAE,YAAY,CAAC;IACnB,KAAK,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,WAAW,cAAc;IAC7B,+DAA+D;IAC/D,MAAM,EAAE,MAAM,EAAE,CAAC;IACjB,sCAAsC;IACtC,OAAO,EAAE,aAAa,EAAE,CAAC;IACzB,wDAAwD;IACxD,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,cAAc,EAAE,cAAc,CAAC;CAChC;AAKD,iBAAe,QAAQ,CACrB,MAAM,EAAE,cAAc,EACtB,OAAO,EAAE,SAAS,aAAa,EAAE,EACjC,OAAO,CAAC,EAAE;IAAE,KAAK,CAAC,EAAE,OAAO,CAAC;IAAC,MAAM,CAAC,EAAE,WAAW,CAAA;CAAE,GAClD,OAAO,CAAC,cAAc,CAAC,CAkDzB;AAED,eAAO,MAAM,SAAS;;;;;;CAMrB,CAAC"}
|
package/dist/lib/worktrees.js
CHANGED
|
@@ -159,7 +159,28 @@ async function removeWorktree(config, entry, options) {
|
|
|
159
159
|
removeArguments.push("--force");
|
|
160
160
|
}
|
|
161
161
|
removeArguments.push(entry.dir);
|
|
162
|
-
|
|
162
|
+
try {
|
|
163
|
+
await runCommandAsync("git", removeArguments, longRunningCommandOptions(options.signal));
|
|
164
|
+
}
|
|
165
|
+
catch (error) {
|
|
166
|
+
// git's `fatal: ... use --force to delete it` line goes to inherited
|
|
167
|
+
// stderr, so the captured error is just "Exit status: 128". Probe the
|
|
168
|
+
// worktree ourselves so the failure message explains the condition
|
|
169
|
+
// (modified/untracked files) and points at `crew cleanup --force`.
|
|
170
|
+
if (options.force || options.signal?.aborted === true) {
|
|
171
|
+
throw error;
|
|
172
|
+
}
|
|
173
|
+
const dirtiness = await probeWorktreeDirtiness(entry.dir, options.signal);
|
|
174
|
+
if (dirtiness.kind !== "dirty") {
|
|
175
|
+
throw error;
|
|
176
|
+
}
|
|
177
|
+
throw new Error(describeDirtyWorktree({
|
|
178
|
+
ticket: entry.ticket,
|
|
179
|
+
dir: entry.dir,
|
|
180
|
+
modified: dirtiness.modified,
|
|
181
|
+
untracked: dirtiness.untracked,
|
|
182
|
+
}), { cause: error });
|
|
183
|
+
}
|
|
163
184
|
}
|
|
164
185
|
else {
|
|
165
186
|
log(`Worktree directory ${entry.dir} not found, pruning stale refs...`);
|
|
@@ -172,6 +193,45 @@ async function removeWorktree(config, entry, options) {
|
|
|
172
193
|
...signalProperty(options.signal),
|
|
173
194
|
});
|
|
174
195
|
}
|
|
196
|
+
async function probeWorktreeDirtiness(worktreeDir, signal) {
|
|
197
|
+
let output;
|
|
198
|
+
try {
|
|
199
|
+
output = await runCommandAsync("git", ["-C", worktreeDir, "status", "--porcelain"], signalProperty(signal));
|
|
200
|
+
}
|
|
201
|
+
catch {
|
|
202
|
+
return { kind: "unknown" };
|
|
203
|
+
}
|
|
204
|
+
let modified = 0;
|
|
205
|
+
let untracked = 0;
|
|
206
|
+
for (const line of output.split("\n")) {
|
|
207
|
+
if (line.length === 0) {
|
|
208
|
+
continue;
|
|
209
|
+
}
|
|
210
|
+
if (line.startsWith("??")) {
|
|
211
|
+
untracked += 1;
|
|
212
|
+
}
|
|
213
|
+
else {
|
|
214
|
+
modified += 1;
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
if (modified === 0 && untracked === 0) {
|
|
218
|
+
return { kind: "clean" };
|
|
219
|
+
}
|
|
220
|
+
return { kind: "dirty", modified, untracked };
|
|
221
|
+
}
|
|
222
|
+
function describeDirtyWorktree(arguments_) {
|
|
223
|
+
const { ticket, dir, modified, untracked } = arguments_;
|
|
224
|
+
const parts = [];
|
|
225
|
+
if (modified > 0) {
|
|
226
|
+
parts.push(`${modified} modified file${modified === 1 ? "" : "s"}`);
|
|
227
|
+
}
|
|
228
|
+
if (untracked > 0) {
|
|
229
|
+
parts.push(`${untracked} untracked file${untracked === 1 ? "" : "s"}`);
|
|
230
|
+
}
|
|
231
|
+
const summary = parts.join(" and ");
|
|
232
|
+
const pronoun = modified + untracked === 1 ? "it" : "them";
|
|
233
|
+
return `worktree has ${summary}. Run \`crew cleanup --force ${ticket}\` to discard ${pronoun}, or commit/stash in ${dir} first.`;
|
|
234
|
+
}
|
|
175
235
|
function list(config) {
|
|
176
236
|
return listWorktrees(config);
|
|
177
237
|
}
|
package/package.json
CHANGED