@staff0rd/assist 0.482.0 → 0.483.0
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/dist/commands/sessions/web/bundle.js +286 -286
- package/dist/index.js +478 -271
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -6,7 +6,7 @@ import { Command } from "commander";
|
|
|
6
6
|
// package.json
|
|
7
7
|
var package_default = {
|
|
8
8
|
name: "@staff0rd/assist",
|
|
9
|
-
version: "0.
|
|
9
|
+
version: "0.483.0",
|
|
10
10
|
type: "module",
|
|
11
11
|
main: "dist/index.js",
|
|
12
12
|
bin: {
|
|
@@ -399,7 +399,7 @@ var DEFAULT_WAKE_WORDS = ["computer"];
|
|
|
399
399
|
var DEFAULT_MODELS_DIR = "~/.assist/voice/models";
|
|
400
400
|
var DEFAULT_BACKUP_DIR = "~/.assist/backups";
|
|
401
401
|
var DEFAULT_CLONE_DIR = "~/git";
|
|
402
|
-
var
|
|
402
|
+
var assistConfigShape = {
|
|
403
403
|
backup: z2.strictObject({
|
|
404
404
|
dir: z2.string().default(DEFAULT_BACKUP_DIR)
|
|
405
405
|
}).default({ dir: DEFAULT_BACKUP_DIR }),
|
|
@@ -451,7 +451,6 @@ var assistConfigSchema = z2.strictObject({
|
|
|
451
451
|
required: z2.boolean().default(false),
|
|
452
452
|
promptJira: z2.boolean().default(false)
|
|
453
453
|
}).optional(),
|
|
454
|
-
repos: z2.record(z2.string(), z2.record(z2.string(), z2.unknown())).optional(),
|
|
455
454
|
worktree: z2.strictObject({
|
|
456
455
|
enabled: z2.boolean().default(false),
|
|
457
456
|
trunk: z2.boolean().default(false),
|
|
@@ -571,6 +570,11 @@ var assistConfigSchema = z2.strictObject({
|
|
|
571
570
|
modelsDir: DEFAULT_MODELS_DIR,
|
|
572
571
|
models: {}
|
|
573
572
|
})
|
|
573
|
+
};
|
|
574
|
+
var repoConfigSchema = z2.strictObject(assistConfigShape).partial();
|
|
575
|
+
var assistConfigSchema = z2.strictObject({
|
|
576
|
+
...assistConfigShape,
|
|
577
|
+
repos: z2.record(z2.string(), repoConfigSchema).optional()
|
|
574
578
|
});
|
|
575
579
|
|
|
576
580
|
// src/shared/loadConfigFrom.ts
|
|
@@ -6665,10 +6669,10 @@ async function prepareRun(id, resumeSessionId) {
|
|
|
6665
6669
|
import chalk39 from "chalk";
|
|
6666
6670
|
function reportDuplicateRun(itemId2, holder) {
|
|
6667
6671
|
const since = holder.timestamp ? `, started ${holder.timestamp}` : "";
|
|
6668
|
-
const
|
|
6672
|
+
const reason4 = `another session is already running it (pid ${holder.pid}${since}); close that session before running it again`;
|
|
6669
6673
|
console.log(chalk39.yellow(`Already running: ${formatItemId(itemId2)}`));
|
|
6670
|
-
console.log(chalk39.dim(`Refusing to start a second run \u2014 ${
|
|
6671
|
-
appendDaemonLog(`backlog run ${itemId2}: refused \u2014 ${
|
|
6674
|
+
console.log(chalk39.dim(`Refusing to start a second run \u2014 ${reason4}.`));
|
|
6675
|
+
appendDaemonLog(`backlog run ${itemId2}: refused \u2014 ${reason4}`);
|
|
6672
6676
|
}
|
|
6673
6677
|
|
|
6674
6678
|
// src/commands/backlog/consumePause.ts
|
|
@@ -8332,11 +8336,11 @@ import {
|
|
|
8332
8336
|
var SPAWN_TIMEOUT_MS = 1e4;
|
|
8333
8337
|
var RETRY_DELAY_MS2 = 200;
|
|
8334
8338
|
var STALE_LOCK_MS = SPAWN_TIMEOUT_MS + 5e3;
|
|
8335
|
-
async function ensureDaemonRunning(
|
|
8339
|
+
async function ensureDaemonRunning(reason4 = "unspecified") {
|
|
8336
8340
|
if (await isDaemonRunning()) return;
|
|
8337
8341
|
mkdirSync10(daemonPaths.dir, { recursive: true });
|
|
8338
8342
|
const holdsLock = acquireSpawnLock();
|
|
8339
|
-
if (holdsLock) spawnDaemon(
|
|
8343
|
+
if (holdsLock) spawnDaemon(reason4);
|
|
8340
8344
|
try {
|
|
8341
8345
|
await waitForDaemon();
|
|
8342
8346
|
} finally {
|
|
@@ -8385,12 +8389,12 @@ function releaseSpawnLock() {
|
|
|
8385
8389
|
} catch {
|
|
8386
8390
|
}
|
|
8387
8391
|
}
|
|
8388
|
-
function spawnDaemon(
|
|
8392
|
+
function spawnDaemon(reason4) {
|
|
8389
8393
|
const log = openSync2(daemonPaths.log, "a");
|
|
8390
8394
|
const child = spawn4(process.execPath, [process.argv[1], "daemon", "run"], {
|
|
8391
8395
|
detached: true,
|
|
8392
8396
|
stdio: ["ignore", log, log],
|
|
8393
|
-
env: { ...process.env, ASSIST_DAEMON_SPAWN_REASON:
|
|
8397
|
+
env: { ...process.env, ASSIST_DAEMON_SPAWN_REASON: reason4 }
|
|
8394
8398
|
});
|
|
8395
8399
|
child.unref();
|
|
8396
8400
|
}
|
|
@@ -8971,7 +8975,7 @@ async function patchSubtaskStatus(req, res, id, idx) {
|
|
|
8971
8975
|
}
|
|
8972
8976
|
|
|
8973
8977
|
// src/commands/backlog/rewindItemToPhase.ts
|
|
8974
|
-
async function rewindItemToPhase(orm, item, phaseNumber,
|
|
8978
|
+
async function rewindItemToPhase(orm, item, phaseNumber, reason4) {
|
|
8975
8979
|
const plan2 = resolveRewindPlan(item);
|
|
8976
8980
|
if (phaseNumber < 1 || phaseNumber > plan2.length) {
|
|
8977
8981
|
return {
|
|
@@ -8990,7 +8994,7 @@ async function rewindItemToPhase(orm, item, phaseNumber, reason2) {
|
|
|
8990
8994
|
await appendComment(
|
|
8991
8995
|
orm,
|
|
8992
8996
|
item.id,
|
|
8993
|
-
`Rewound to phase ${phaseNumber} (${phaseName}): ${
|
|
8997
|
+
`Rewound to phase ${phaseNumber} (${phaseName}): ${reason4}`,
|
|
8994
8998
|
{ phase: phaseNumber }
|
|
8995
8999
|
);
|
|
8996
9000
|
await updateCurrentPhase(orm, item.id, phaseNumber);
|
|
@@ -9001,11 +9005,11 @@ async function rewindItemToPhase(orm, item, phaseNumber, reason2) {
|
|
|
9001
9005
|
|
|
9002
9006
|
// src/commands/backlog/web/rewindItemPhase.ts
|
|
9003
9007
|
async function rewindItemPhase(req, res, id) {
|
|
9004
|
-
const { phase, reason:
|
|
9008
|
+
const { phase, reason: reason4 } = await parseRewindBody(req);
|
|
9005
9009
|
const result = await findItemOr404(res, id);
|
|
9006
9010
|
if (!result) return;
|
|
9007
9011
|
const { orm, item } = result;
|
|
9008
|
-
const outcome = await rewindItemToPhase(orm, item, phase,
|
|
9012
|
+
const outcome = await rewindItemToPhase(orm, item, phase, reason4);
|
|
9009
9013
|
if (!outcome.ok) {
|
|
9010
9014
|
respondJson(res, 400, { error: outcome.error });
|
|
9011
9015
|
return;
|
|
@@ -9568,7 +9572,7 @@ function readConfigEntries(cwd) {
|
|
|
9568
9572
|
value: getNestedValue(config, leaf.key),
|
|
9569
9573
|
source,
|
|
9570
9574
|
sources,
|
|
9571
|
-
...
|
|
9575
|
+
...layers.repoKey ? { repoKey: layers.repoKey } : {},
|
|
9572
9576
|
globalOnly: isGlobalOnlyConfigKey(leaf.key),
|
|
9573
9577
|
node: configEntryNode(schema2, leaf.key)
|
|
9574
9578
|
};
|
|
@@ -10402,90 +10406,6 @@ function sessionView(_req, res) {
|
|
|
10402
10406
|
respondJson(res, 200, { floatWaiting });
|
|
10403
10407
|
}
|
|
10404
10408
|
|
|
10405
|
-
// src/commands/config/setNestedValue.ts
|
|
10406
|
-
function isPlainObject2(val) {
|
|
10407
|
-
return val !== null && typeof val === "object" && !Array.isArray(val);
|
|
10408
|
-
}
|
|
10409
|
-
function isNumericKey(key) {
|
|
10410
|
-
return /^\d+$/.test(key);
|
|
10411
|
-
}
|
|
10412
|
-
function resolveKey(key) {
|
|
10413
|
-
return isNumericKey(key) ? Number.parseInt(key, 10) : key;
|
|
10414
|
-
}
|
|
10415
|
-
function getItem(container, key) {
|
|
10416
|
-
if (Array.isArray(container)) return container[key];
|
|
10417
|
-
return container[key];
|
|
10418
|
-
}
|
|
10419
|
-
function setItem(container, key, value) {
|
|
10420
|
-
if (Array.isArray(container)) container[key] = value;
|
|
10421
|
-
else container[key] = value;
|
|
10422
|
-
}
|
|
10423
|
-
function ensureArray(container, key) {
|
|
10424
|
-
const existing = getItem(container, key);
|
|
10425
|
-
const arr = Array.isArray(existing) ? [...existing] : [];
|
|
10426
|
-
setItem(container, key, arr);
|
|
10427
|
-
return arr;
|
|
10428
|
-
}
|
|
10429
|
-
function ensureObject(container, key) {
|
|
10430
|
-
const existing = getItem(container, key);
|
|
10431
|
-
const obj = isPlainObject2(existing) ? { ...existing } : {};
|
|
10432
|
-
setItem(container, key, obj);
|
|
10433
|
-
return obj;
|
|
10434
|
-
}
|
|
10435
|
-
function stepIntoNested(container, key, nextKey) {
|
|
10436
|
-
const resolved = resolveKey(key);
|
|
10437
|
-
if (nextKey !== void 0 && isNumericKey(nextKey)) {
|
|
10438
|
-
return ensureArray(container, resolved);
|
|
10439
|
-
}
|
|
10440
|
-
return ensureObject(container, resolved);
|
|
10441
|
-
}
|
|
10442
|
-
function setNestedValue(obj, path71, value) {
|
|
10443
|
-
const keys = path71.split(".");
|
|
10444
|
-
const result = { ...obj };
|
|
10445
|
-
let current = result;
|
|
10446
|
-
for (let i = 0; i < keys.length - 1; i++) {
|
|
10447
|
-
current = stepIntoNested(current, keys[i], keys[i + 1]);
|
|
10448
|
-
}
|
|
10449
|
-
setItem(current, resolveKey(keys[keys.length - 1]), value);
|
|
10450
|
-
return result;
|
|
10451
|
-
}
|
|
10452
|
-
|
|
10453
|
-
// src/commands/config/validateConfig.ts
|
|
10454
|
-
function validateConfig(updated, key) {
|
|
10455
|
-
const result = assistConfigSchema.safeParse(updated);
|
|
10456
|
-
if (result.success) return { ok: true };
|
|
10457
|
-
return {
|
|
10458
|
-
ok: false,
|
|
10459
|
-
errors: result.error.issues.map((issue) => formatIssue(issue, key))
|
|
10460
|
-
};
|
|
10461
|
-
}
|
|
10462
|
-
function formatIssue(issue, key) {
|
|
10463
|
-
const path71 = issue.path.length > 0 ? issue.path.join(".") : key;
|
|
10464
|
-
return `${path71}: ${issue.message}`;
|
|
10465
|
-
}
|
|
10466
|
-
|
|
10467
|
-
// src/commands/config/applyConfigSet.ts
|
|
10468
|
-
function applyConfigSet(key, coerced, global, cwd = process.cwd()) {
|
|
10469
|
-
if (!global && isGlobalOnlyConfigKey(key)) {
|
|
10470
|
-
return {
|
|
10471
|
-
ok: false,
|
|
10472
|
-
errors: [
|
|
10473
|
-
`"${key}" is a global-only key. Use --global to set it in ~/.assist.yml`
|
|
10474
|
-
]
|
|
10475
|
-
};
|
|
10476
|
-
}
|
|
10477
|
-
const raw = global ? loadGlobalConfigRaw() : loadProjectConfig(cwd);
|
|
10478
|
-
const updated = setNestedValue(raw, key, coerced);
|
|
10479
|
-
const validation = validateConfig(updated, key);
|
|
10480
|
-
if (!validation.ok) return validation;
|
|
10481
|
-
if (global) {
|
|
10482
|
-
saveGlobalConfig(updated);
|
|
10483
|
-
return { ok: true, target: "global" };
|
|
10484
|
-
}
|
|
10485
|
-
saveConfig(updated, cwd);
|
|
10486
|
-
return { ok: true, target: "project" };
|
|
10487
|
-
}
|
|
10488
|
-
|
|
10489
10409
|
// src/commands/config/coerceConfigScalar.ts
|
|
10490
10410
|
function coerceConfigScalar(key, raw, type) {
|
|
10491
10411
|
if (type === "boolean") return coerceBoolean(key, raw);
|
|
@@ -10534,10 +10454,10 @@ function coerceStructuredConfigValue(key, raw) {
|
|
|
10534
10454
|
return { ok: true, value: parsed.data };
|
|
10535
10455
|
return {
|
|
10536
10456
|
ok: false,
|
|
10537
|
-
error: parsed.error.issues.map((issue) =>
|
|
10457
|
+
error: parsed.error.issues.map((issue) => formatIssue(key, issue)).join("\n")
|
|
10538
10458
|
};
|
|
10539
10459
|
}
|
|
10540
|
-
function
|
|
10460
|
+
function formatIssue(key, issue) {
|
|
10541
10461
|
const path71 = formatConfigPath([
|
|
10542
10462
|
{ kind: "key", name: key },
|
|
10543
10463
|
...issue.path.map(toSegment)
|
|
@@ -10591,6 +10511,152 @@ function coerceUnion(key, raw, types) {
|
|
|
10591
10511
|
return { ok: false, error: `${key}: expected ${types.join(" or ")}` };
|
|
10592
10512
|
}
|
|
10593
10513
|
|
|
10514
|
+
// src/commands/config/setNestedValue.ts
|
|
10515
|
+
function isPlainObject2(val) {
|
|
10516
|
+
return val !== null && typeof val === "object" && !Array.isArray(val);
|
|
10517
|
+
}
|
|
10518
|
+
function isNumericKey(key) {
|
|
10519
|
+
return /^\d+$/.test(key);
|
|
10520
|
+
}
|
|
10521
|
+
function resolveKey(key) {
|
|
10522
|
+
return isNumericKey(key) ? Number.parseInt(key, 10) : key;
|
|
10523
|
+
}
|
|
10524
|
+
function getItem(container, key) {
|
|
10525
|
+
if (Array.isArray(container)) return container[key];
|
|
10526
|
+
return container[key];
|
|
10527
|
+
}
|
|
10528
|
+
function setItem(container, key, value) {
|
|
10529
|
+
if (Array.isArray(container)) container[key] = value;
|
|
10530
|
+
else container[key] = value;
|
|
10531
|
+
}
|
|
10532
|
+
function ensureArray(container, key) {
|
|
10533
|
+
const existing = getItem(container, key);
|
|
10534
|
+
const arr = Array.isArray(existing) ? [...existing] : [];
|
|
10535
|
+
setItem(container, key, arr);
|
|
10536
|
+
return arr;
|
|
10537
|
+
}
|
|
10538
|
+
function ensureObject(container, key) {
|
|
10539
|
+
const existing = getItem(container, key);
|
|
10540
|
+
const obj = isPlainObject2(existing) ? { ...existing } : {};
|
|
10541
|
+
setItem(container, key, obj);
|
|
10542
|
+
return obj;
|
|
10543
|
+
}
|
|
10544
|
+
function stepIntoNested(container, key, nextKey) {
|
|
10545
|
+
const resolved = resolveKey(key);
|
|
10546
|
+
if (nextKey !== void 0 && isNumericKey(nextKey)) {
|
|
10547
|
+
return ensureArray(container, resolved);
|
|
10548
|
+
}
|
|
10549
|
+
return ensureObject(container, resolved);
|
|
10550
|
+
}
|
|
10551
|
+
function setNestedValue(obj, path71, value) {
|
|
10552
|
+
const keys = path71.split(".");
|
|
10553
|
+
const result = { ...obj };
|
|
10554
|
+
let current = result;
|
|
10555
|
+
for (let i = 0; i < keys.length - 1; i++) {
|
|
10556
|
+
current = stepIntoNested(current, keys[i], keys[i + 1]);
|
|
10557
|
+
}
|
|
10558
|
+
setItem(current, resolveKey(keys[keys.length - 1]), value);
|
|
10559
|
+
return result;
|
|
10560
|
+
}
|
|
10561
|
+
|
|
10562
|
+
// src/commands/config/validateConfig.ts
|
|
10563
|
+
function validateConfig(updated, key, schema2 = assistConfigSchema) {
|
|
10564
|
+
const result = schema2.safeParse(updated);
|
|
10565
|
+
if (result.success) return { ok: true };
|
|
10566
|
+
return {
|
|
10567
|
+
ok: false,
|
|
10568
|
+
errors: result.error.issues.map((issue) => formatIssue2(issue, key))
|
|
10569
|
+
};
|
|
10570
|
+
}
|
|
10571
|
+
function formatIssue2(issue, key) {
|
|
10572
|
+
const path71 = issue.path.length > 0 ? issue.path.join(".") : key;
|
|
10573
|
+
return `${path71}: ${issue.message}`;
|
|
10574
|
+
}
|
|
10575
|
+
|
|
10576
|
+
// src/commands/config/applyConfigSet.ts
|
|
10577
|
+
function applyConfigSet(key, coerced, global, cwd = process.cwd()) {
|
|
10578
|
+
if (!global && isGlobalOnlyConfigKey(key)) {
|
|
10579
|
+
return {
|
|
10580
|
+
ok: false,
|
|
10581
|
+
errors: [
|
|
10582
|
+
`"${key}" is a global-only key. Use --global to set it in ~/.assist.yml`
|
|
10583
|
+
]
|
|
10584
|
+
};
|
|
10585
|
+
}
|
|
10586
|
+
const raw = global ? loadGlobalConfigRaw() : loadProjectConfig(cwd);
|
|
10587
|
+
const updated = setNestedValue(raw, key, coerced);
|
|
10588
|
+
const validation = validateConfig(updated, key);
|
|
10589
|
+
if (!validation.ok) return validation;
|
|
10590
|
+
if (global) {
|
|
10591
|
+
saveGlobalConfig(updated);
|
|
10592
|
+
return { ok: true, target: "global" };
|
|
10593
|
+
}
|
|
10594
|
+
saveConfig(updated, cwd);
|
|
10595
|
+
return { ok: true, target: "project" };
|
|
10596
|
+
}
|
|
10597
|
+
|
|
10598
|
+
// src/shared/resolveNamedRepoWriteLabel.ts
|
|
10599
|
+
var UnknownRepoConfigError = class extends Error {
|
|
10600
|
+
name = "UnknownRepoConfigError";
|
|
10601
|
+
};
|
|
10602
|
+
function resolveNamedRepoWriteLabel(globalRaw, name) {
|
|
10603
|
+
const repos2 = globalRaw.repos;
|
|
10604
|
+
const entries = repos2 && typeof repos2 === "object" && !Array.isArray(repos2) ? repos2 : {};
|
|
10605
|
+
const candidates = originKeyCandidates(name);
|
|
10606
|
+
const matches = Object.keys(entries).filter((key) => candidates.has(key));
|
|
10607
|
+
if (matches.length === 0) {
|
|
10608
|
+
throw new UnknownRepoConfigError(
|
|
10609
|
+
`No repo in ~/.assist.yml matches "${name}". Use an existing repos key (full origin, org/repo, or bare repo name).`
|
|
10610
|
+
);
|
|
10611
|
+
}
|
|
10612
|
+
if (matches.length > 1) {
|
|
10613
|
+
throw new AmbiguousRepoConfigError(
|
|
10614
|
+
`Ambiguous repos config in ~/.assist.yml: keys ${matches.map((m) => `"${m}"`).join(", ")} all match "${name}". Keep a single key per repository.`
|
|
10615
|
+
);
|
|
10616
|
+
}
|
|
10617
|
+
return matches[0];
|
|
10618
|
+
}
|
|
10619
|
+
|
|
10620
|
+
// src/commands/config/applyRepoConfigSet.ts
|
|
10621
|
+
function applyRepoConfigSet(key, coerced, repoName, cwd = process.cwd()) {
|
|
10622
|
+
if (isGlobalOnlyConfigKey(key)) {
|
|
10623
|
+
return {
|
|
10624
|
+
ok: false,
|
|
10625
|
+
errors: [
|
|
10626
|
+
`"${key}" is a global-only key. Set it in ~/.assist.yml rather than under repos:`
|
|
10627
|
+
]
|
|
10628
|
+
};
|
|
10629
|
+
}
|
|
10630
|
+
const globalRaw = loadGlobalConfigRaw();
|
|
10631
|
+
const label2 = repoName === void 0 ? resolveRepoWriteLabel(globalRaw, getCurrentOrigin(cwd)) : resolveNamedRepoWriteLabel(globalRaw, repoName);
|
|
10632
|
+
const repos2 = isPlainObject3(globalRaw.repos) ? { ...globalRaw.repos } : {};
|
|
10633
|
+
const existingBlock = isPlainObject3(repos2[label2]) ? repos2[label2] : {};
|
|
10634
|
+
const updatedBlock = setNestedValue(existingBlock, key, coerced);
|
|
10635
|
+
const validation = validateConfig(updatedBlock, key, repoConfigSchema);
|
|
10636
|
+
if (!validation.ok) return validation;
|
|
10637
|
+
repos2[label2] = updatedBlock;
|
|
10638
|
+
saveGlobalConfig({ ...globalRaw, repos: repos2 });
|
|
10639
|
+
return { ok: true, target: "repo", label: label2 };
|
|
10640
|
+
}
|
|
10641
|
+
function isPlainObject3(value) {
|
|
10642
|
+
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
10643
|
+
}
|
|
10644
|
+
|
|
10645
|
+
// src/commands/sessions/web/applyScopedConfigSet.ts
|
|
10646
|
+
function applyScopedConfigSet(key, value, cwd, scope) {
|
|
10647
|
+
if (scope === "repo") {
|
|
10648
|
+
const result2 = applyRepoConfigSet(key, value, void 0, cwd);
|
|
10649
|
+
return result2.ok ? { ok: true, payload: { target: result2.target, repoKey: result2.label } } : result2;
|
|
10650
|
+
}
|
|
10651
|
+
const result = applyConfigSet(key, value, scope === "global", cwd);
|
|
10652
|
+
return result.ok ? { ok: true, payload: { target: result.target } } : result;
|
|
10653
|
+
}
|
|
10654
|
+
|
|
10655
|
+
// src/commands/config/ConfigWriteScope.ts
|
|
10656
|
+
function isConfigWriteScope(value) {
|
|
10657
|
+
return value === "project" || value === "global" || value === "repo";
|
|
10658
|
+
}
|
|
10659
|
+
|
|
10594
10660
|
// src/commands/sessions/web/readJsonBody.ts
|
|
10595
10661
|
async function readJsonBody(req) {
|
|
10596
10662
|
const chunks = [];
|
|
@@ -10611,9 +10677,9 @@ async function parseConfigWriteRequest(req) {
|
|
|
10611
10677
|
return { ok: false, error: "Missing key" };
|
|
10612
10678
|
if (typeof cwd !== "string" || cwd === "")
|
|
10613
10679
|
return { ok: false, error: "Missing cwd" };
|
|
10614
|
-
if (scope
|
|
10615
|
-
return { ok: false, error: 'scope must be "project" or "
|
|
10616
|
-
return { ok: true, key, value: body.value, cwd,
|
|
10680
|
+
if (!isConfigWriteScope(scope))
|
|
10681
|
+
return { ok: false, error: 'scope must be "project", "global" or "repo"' };
|
|
10682
|
+
return { ok: true, key, value: body.value, cwd, scope };
|
|
10617
10683
|
}
|
|
10618
10684
|
|
|
10619
10685
|
// src/commands/sessions/web/handleConfigWrite.ts
|
|
@@ -10645,22 +10711,21 @@ function setConfig(req, res) {
|
|
|
10645
10711
|
return handleConfigWrite(req, res, (request) => {
|
|
10646
10712
|
const coerced = coerceConfigValue(request.key, request.value);
|
|
10647
10713
|
if (!coerced.ok) return { ok: false, errors: [coerced.error] };
|
|
10648
|
-
|
|
10714
|
+
return applyScopedConfigSet(
|
|
10649
10715
|
request.key,
|
|
10650
10716
|
coerced.value,
|
|
10651
|
-
request.
|
|
10652
|
-
request.
|
|
10717
|
+
request.cwd,
|
|
10718
|
+
request.scope
|
|
10653
10719
|
);
|
|
10654
|
-
return result.ok ? { ok: true, payload: { target: result.target } } : result;
|
|
10655
10720
|
});
|
|
10656
10721
|
}
|
|
10657
10722
|
|
|
10658
10723
|
// src/commands/config/unsetNestedValue.ts
|
|
10659
|
-
function
|
|
10724
|
+
function isPlainObject4(val) {
|
|
10660
10725
|
return val !== null && typeof val === "object" && !Array.isArray(val);
|
|
10661
10726
|
}
|
|
10662
10727
|
function isContainer(val) {
|
|
10663
|
-
return Array.isArray(val) ||
|
|
10728
|
+
return Array.isArray(val) || isPlainObject4(val);
|
|
10664
10729
|
}
|
|
10665
10730
|
function isNumericKey2(key) {
|
|
10666
10731
|
return /^\d+$/.test(key);
|
|
@@ -10696,7 +10761,7 @@ function withChild(container, key, child) {
|
|
|
10696
10761
|
return { ...container, [key]: child };
|
|
10697
10762
|
}
|
|
10698
10763
|
function isPrunable(container) {
|
|
10699
|
-
return
|
|
10764
|
+
return isPlainObject4(container) && Object.keys(container).length === 0;
|
|
10700
10765
|
}
|
|
10701
10766
|
function replaceOrPrune(container, key, child) {
|
|
10702
10767
|
return isPrunable(child) ? withoutKey(container, key) : withChild(container, key, child);
|
|
@@ -10755,7 +10820,18 @@ function unsetConfig(req, res) {
|
|
|
10755
10820
|
return handleConfigWrite(req, res, (request) => {
|
|
10756
10821
|
if (!isKnownConfigKey(request.key))
|
|
10757
10822
|
return { ok: false, errors: [`Unknown config key "${request.key}"`] };
|
|
10758
|
-
|
|
10823
|
+
if (request.scope === "repo")
|
|
10824
|
+
return {
|
|
10825
|
+
ok: false,
|
|
10826
|
+
errors: [
|
|
10827
|
+
"Clearing a repos override is not supported \u2014 remove the key from repos: in ~/.assist.yml"
|
|
10828
|
+
]
|
|
10829
|
+
};
|
|
10830
|
+
const result = applyConfigUnset(
|
|
10831
|
+
request.key,
|
|
10832
|
+
request.scope === "global",
|
|
10833
|
+
request.cwd
|
|
10834
|
+
);
|
|
10759
10835
|
return result.ok ? {
|
|
10760
10836
|
ok: true,
|
|
10761
10837
|
payload: { target: result.target, removed: result.removed }
|
|
@@ -11750,8 +11826,8 @@ function countCopyRows(data) {
|
|
|
11750
11826
|
|
|
11751
11827
|
// src/commands/backlog/dump/parseDump.ts
|
|
11752
11828
|
var NEWLINE = 10;
|
|
11753
|
-
function invalid(
|
|
11754
|
-
throw new Error(`Invalid dump: ${
|
|
11829
|
+
function invalid(reason4) {
|
|
11830
|
+
throw new Error(`Invalid dump: ${reason4}`);
|
|
11755
11831
|
}
|
|
11756
11832
|
function readLine(dump, start3) {
|
|
11757
11833
|
const eol = dump.indexOf(NEWLINE, start3);
|
|
@@ -13123,14 +13199,14 @@ async function unstar(id) {
|
|
|
13123
13199
|
|
|
13124
13200
|
// src/commands/backlog/wontdo/index.ts
|
|
13125
13201
|
import chalk98 from "chalk";
|
|
13126
|
-
async function wontdo(id,
|
|
13202
|
+
async function wontdo(id, reason4) {
|
|
13127
13203
|
const found = await findOneItem(id);
|
|
13128
13204
|
if (!found) return;
|
|
13129
13205
|
const { orm, item } = found;
|
|
13130
13206
|
await updateStatus(orm, item.id, "wontdo");
|
|
13131
|
-
if (
|
|
13207
|
+
if (reason4) {
|
|
13132
13208
|
const phase = item.currentPhase ?? 1;
|
|
13133
|
-
await appendComment(orm, item.id,
|
|
13209
|
+
await appendComment(orm, item.id, reason4, { phase, type: "summary" });
|
|
13134
13210
|
}
|
|
13135
13211
|
console.log(
|
|
13136
13212
|
chalk98.red(`Won't do item ${formatItemId(item.id)}: ${item.name}`)
|
|
@@ -14697,9 +14773,9 @@ function resolvePermission(toolName, parts) {
|
|
|
14697
14773
|
if (denied) return denied;
|
|
14698
14774
|
const reasons = [];
|
|
14699
14775
|
for (const part of parts) {
|
|
14700
|
-
const
|
|
14701
|
-
if (!
|
|
14702
|
-
reasons.push(
|
|
14776
|
+
const reason4 = isApprovedRead(part, toolName);
|
|
14777
|
+
if (!reason4) return void 0;
|
|
14778
|
+
reasons.push(reason4);
|
|
14703
14779
|
}
|
|
14704
14780
|
return {
|
|
14705
14781
|
permissionDecision: "allow",
|
|
@@ -14823,13 +14899,13 @@ function cliHookCheck(command, toolName = "Bash") {
|
|
|
14823
14899
|
if (reportDeny(toolName, parts)) return;
|
|
14824
14900
|
const reasons = [];
|
|
14825
14901
|
for (const part of parts) {
|
|
14826
|
-
const
|
|
14827
|
-
if (!
|
|
14902
|
+
const reason4 = isApprovedRead(part, toolName);
|
|
14903
|
+
if (!reason4) {
|
|
14828
14904
|
console.log(`not approved (unrecognised: ${part})`);
|
|
14829
14905
|
process.exitCode = 1;
|
|
14830
14906
|
return;
|
|
14831
14907
|
}
|
|
14832
|
-
reasons.push(` ${part} -> ${
|
|
14908
|
+
reasons.push(` ${part} -> ${reason4}`);
|
|
14833
14909
|
}
|
|
14834
14910
|
console.log(`approved
|
|
14835
14911
|
${reasons.join("\n")}`);
|
|
@@ -16166,28 +16242,6 @@ function exitKeyNotSet(key) {
|
|
|
16166
16242
|
// src/commands/config/configSet.ts
|
|
16167
16243
|
import chalk131 from "chalk";
|
|
16168
16244
|
|
|
16169
|
-
// src/shared/resolveNamedRepoWriteLabel.ts
|
|
16170
|
-
var UnknownRepoConfigError = class extends Error {
|
|
16171
|
-
name = "UnknownRepoConfigError";
|
|
16172
|
-
};
|
|
16173
|
-
function resolveNamedRepoWriteLabel(globalRaw, name) {
|
|
16174
|
-
const repos2 = globalRaw.repos;
|
|
16175
|
-
const entries = repos2 && typeof repos2 === "object" && !Array.isArray(repos2) ? repos2 : {};
|
|
16176
|
-
const candidates = originKeyCandidates(name);
|
|
16177
|
-
const matches = Object.keys(entries).filter((key) => candidates.has(key));
|
|
16178
|
-
if (matches.length === 0) {
|
|
16179
|
-
throw new UnknownRepoConfigError(
|
|
16180
|
-
`No repo in ~/.assist.yml matches "${name}". Use an existing repos key (full origin, org/repo, or bare repo name).`
|
|
16181
|
-
);
|
|
16182
|
-
}
|
|
16183
|
-
if (matches.length > 1) {
|
|
16184
|
-
throw new AmbiguousRepoConfigError(
|
|
16185
|
-
`Ambiguous repos config in ~/.assist.yml: keys ${matches.map((m) => `"${m}"`).join(", ")} all match "${name}". Keep a single key per repository.`
|
|
16186
|
-
);
|
|
16187
|
-
}
|
|
16188
|
-
return matches[0];
|
|
16189
|
-
}
|
|
16190
|
-
|
|
16191
16245
|
// src/commands/config/exitWithConfigErrors.ts
|
|
16192
16246
|
import chalk130 from "chalk";
|
|
16193
16247
|
function exitWithConfigErrors(errors) {
|
|
@@ -16195,23 +16249,6 @@ function exitWithConfigErrors(errors) {
|
|
|
16195
16249
|
process.exit(1);
|
|
16196
16250
|
}
|
|
16197
16251
|
|
|
16198
|
-
// src/commands/config/applyRepoConfigSet.ts
|
|
16199
|
-
function applyRepoConfigSet(key, coerced, repoName) {
|
|
16200
|
-
const globalRaw = loadGlobalConfigRaw();
|
|
16201
|
-
const label2 = repoName === void 0 ? resolveRepoWriteLabel(globalRaw, getCurrentOrigin(process.cwd())) : resolveNamedRepoWriteLabel(globalRaw, repoName);
|
|
16202
|
-
const repos2 = isPlainObject4(globalRaw.repos) ? { ...globalRaw.repos } : {};
|
|
16203
|
-
const existingBlock = isPlainObject4(repos2[label2]) ? repos2[label2] : {};
|
|
16204
|
-
const updatedBlock = setNestedValue(existingBlock, key, coerced);
|
|
16205
|
-
const validation = validateConfig(updatedBlock, key);
|
|
16206
|
-
if (!validation.ok) exitWithConfigErrors(validation.errors);
|
|
16207
|
-
repos2[label2] = updatedBlock;
|
|
16208
|
-
saveGlobalConfig({ ...globalRaw, repos: repos2 });
|
|
16209
|
-
return label2;
|
|
16210
|
-
}
|
|
16211
|
-
function isPlainObject4(value) {
|
|
16212
|
-
return value !== null && typeof value === "object" && !Array.isArray(value);
|
|
16213
|
-
}
|
|
16214
|
-
|
|
16215
16252
|
// src/commands/config/resolveRepoTarget.ts
|
|
16216
16253
|
function resolveRepoTarget(key, value, repo) {
|
|
16217
16254
|
if (typeof repo === "string") {
|
|
@@ -16237,7 +16274,7 @@ function configSet(key, value, options2 = {}) {
|
|
|
16237
16274
|
process.exit(1);
|
|
16238
16275
|
}
|
|
16239
16276
|
const coerced = coerceValue(resolved.value);
|
|
16240
|
-
const target = resolved.useRepo ? `repo: ${
|
|
16277
|
+
const target = resolved.useRepo ? `repo: ${applyRepoOrExit(resolved.key, coerced, resolved.repoName)}` : applyOrExit(resolved.key, coerced, options2.global ?? false);
|
|
16241
16278
|
console.log(
|
|
16242
16279
|
chalk131.green(`Set ${resolved.key} = ${JSON.stringify(coerced)} (${target})`)
|
|
16243
16280
|
);
|
|
@@ -16247,6 +16284,11 @@ function applyOrExit(key, coerced, global) {
|
|
|
16247
16284
|
if (!result.ok) exitWithConfigErrors(result.errors);
|
|
16248
16285
|
return result.target;
|
|
16249
16286
|
}
|
|
16287
|
+
function applyRepoOrExit(key, coerced, repoName) {
|
|
16288
|
+
const result = applyRepoConfigSet(key, coerced, repoName);
|
|
16289
|
+
if (!result.ok) exitWithConfigErrors(result.errors);
|
|
16290
|
+
return result.label;
|
|
16291
|
+
}
|
|
16250
16292
|
function coerceValue(value) {
|
|
16251
16293
|
if (value === "true") return true;
|
|
16252
16294
|
if (value === "false") return false;
|
|
@@ -17888,14 +17930,14 @@ async function editHook() {
|
|
|
17888
17930
|
const input = tryParseInput2(await readStdin());
|
|
17889
17931
|
if (!input) return;
|
|
17890
17932
|
const existing = input.tool_name === "Write" ? readExisting(input.tool_input.file_path) : void 0;
|
|
17891
|
-
const
|
|
17892
|
-
if (!
|
|
17933
|
+
const reason4 = decideOverrideGuard(input, existing) ?? decideCommentGuard(input, existing) ?? decideMigrationGuard(input, existing);
|
|
17934
|
+
if (!reason4) return;
|
|
17893
17935
|
console.log(
|
|
17894
17936
|
JSON.stringify({
|
|
17895
17937
|
hookSpecificOutput: {
|
|
17896
17938
|
hookEventName: "PreToolUse",
|
|
17897
17939
|
permissionDecision: "deny",
|
|
17898
|
-
permissionDecisionReason:
|
|
17940
|
+
permissionDecisionReason: reason4
|
|
17899
17941
|
}
|
|
17900
17942
|
})
|
|
17901
17943
|
);
|
|
@@ -18841,10 +18883,12 @@ function persistedTreeRoots() {
|
|
|
18841
18883
|
}
|
|
18842
18884
|
|
|
18843
18885
|
// src/commands/sessions/daemon/worktree/seedWorktree.ts
|
|
18844
|
-
import { execFile as execFile8 } from "child_process";
|
|
18845
18886
|
import { copyFileSync, existsSync as existsSync46, mkdirSync as mkdirSync16 } from "fs";
|
|
18846
18887
|
import { dirname as dirname24, join as join48 } from "path";
|
|
18847
18888
|
|
|
18889
|
+
// src/commands/sessions/daemon/worktree/runInstall.ts
|
|
18890
|
+
import { spawn as spawn5 } from "child_process";
|
|
18891
|
+
|
|
18848
18892
|
// src/commands/sessions/daemon/worktree/resolveInstallCommand.ts
|
|
18849
18893
|
import { existsSync as existsSync45 } from "fs";
|
|
18850
18894
|
import { join as join47 } from "path";
|
|
@@ -18861,6 +18905,90 @@ function resolveInstallCommand(repoRoot, install) {
|
|
|
18861
18905
|
return detectInstallCommand(repoRoot);
|
|
18862
18906
|
}
|
|
18863
18907
|
|
|
18908
|
+
// src/commands/sessions/daemon/worktree/stopInstall.ts
|
|
18909
|
+
import { execFile as execFile8 } from "child_process";
|
|
18910
|
+
var running2 = /* @__PURE__ */ new Map();
|
|
18911
|
+
function trackInstall(worktreePath, child) {
|
|
18912
|
+
running2.set(worktreePath, child);
|
|
18913
|
+
}
|
|
18914
|
+
function untrackInstall(worktreePath, child) {
|
|
18915
|
+
if (running2.get(worktreePath) === child) running2.delete(worktreePath);
|
|
18916
|
+
}
|
|
18917
|
+
function stopInstall(worktreePath) {
|
|
18918
|
+
const child = running2.get(worktreePath);
|
|
18919
|
+
running2.delete(worktreePath);
|
|
18920
|
+
if (!child?.pid) return;
|
|
18921
|
+
daemonLog(
|
|
18922
|
+
`worktree ${worktreePath} install still running at teardown; killing pid ${child.pid} so removal is not racing it`
|
|
18923
|
+
);
|
|
18924
|
+
killTree(child.pid);
|
|
18925
|
+
}
|
|
18926
|
+
function killTree(pid) {
|
|
18927
|
+
if (process.platform === "win32") {
|
|
18928
|
+
execFile8("taskkill", ["/T", "/F", "/PID", String(pid)], () => {
|
|
18929
|
+
});
|
|
18930
|
+
return;
|
|
18931
|
+
}
|
|
18932
|
+
try {
|
|
18933
|
+
process.kill(-pid, "SIGKILL");
|
|
18934
|
+
} catch {
|
|
18935
|
+
try {
|
|
18936
|
+
process.kill(pid, "SIGKILL");
|
|
18937
|
+
} catch {
|
|
18938
|
+
}
|
|
18939
|
+
}
|
|
18940
|
+
}
|
|
18941
|
+
|
|
18942
|
+
// src/commands/sessions/daemon/worktree/installInvocation.ts
|
|
18943
|
+
function installInvocation(worktreePath, command) {
|
|
18944
|
+
const windows = /^[A-Za-z]:[\\/]/.test(worktreePath);
|
|
18945
|
+
const shell = windows ? "cmd.exe" : process.env.SHELL ?? "bash";
|
|
18946
|
+
const args = windows ? ["/c", `cd /d ${quoteForCmd(worktreePath)} && ${command}`] : ["-l", "-c", `cd ${quoteForPosixShell(worktreePath)} && ${command}`];
|
|
18947
|
+
return [
|
|
18948
|
+
shell,
|
|
18949
|
+
args,
|
|
18950
|
+
{ cwd: worktreePath, detached: true, stdio: ["ignore", "ignore", "pipe"] }
|
|
18951
|
+
];
|
|
18952
|
+
}
|
|
18953
|
+
function quoteForCmd(value) {
|
|
18954
|
+
return `"${value.replaceAll('"', "")}"`;
|
|
18955
|
+
}
|
|
18956
|
+
function quoteForPosixShell(value) {
|
|
18957
|
+
return `'${value.replaceAll("'", `'\\''`)}'`;
|
|
18958
|
+
}
|
|
18959
|
+
|
|
18960
|
+
// src/commands/sessions/daemon/worktree/runInstall.ts
|
|
18961
|
+
var STDERR_TAIL = 2e3;
|
|
18962
|
+
function runInstall(worktreePath, clone, install, onSeeded) {
|
|
18963
|
+
const command = resolveInstallCommand(clone, install);
|
|
18964
|
+
if (!command) {
|
|
18965
|
+
onSeeded();
|
|
18966
|
+
return;
|
|
18967
|
+
}
|
|
18968
|
+
daemonLog(`worktree ${worktreePath} installing deps: ${command}`);
|
|
18969
|
+
const child = spawn5(...installInvocation(worktreePath, command));
|
|
18970
|
+
trackInstall(worktreePath, child);
|
|
18971
|
+
let stderr = "";
|
|
18972
|
+
child.stderr?.on("data", (chunk) => {
|
|
18973
|
+
stderr = (stderr + chunk.toString()).slice(-STDERR_TAIL);
|
|
18974
|
+
});
|
|
18975
|
+
let settled = false;
|
|
18976
|
+
const settle = (outcome) => {
|
|
18977
|
+
if (settled) return;
|
|
18978
|
+
settled = true;
|
|
18979
|
+
untrackInstall(worktreePath, child);
|
|
18980
|
+
daemonLog(`worktree ${worktreePath} install ${outcome}`);
|
|
18981
|
+
onSeeded();
|
|
18982
|
+
};
|
|
18983
|
+
child.on("error", (error) => settle(`failed to start: ${error.message}`));
|
|
18984
|
+
child.on(
|
|
18985
|
+
"close",
|
|
18986
|
+
(code, signal) => settle(
|
|
18987
|
+
code === 0 ? "complete" : `failed (${signal ? `signal ${signal}` : `exit ${code}`}): ${stderr.trim() || "no output"}`
|
|
18988
|
+
)
|
|
18989
|
+
);
|
|
18990
|
+
}
|
|
18991
|
+
|
|
18864
18992
|
// src/commands/sessions/daemon/worktree/seedWorktree.ts
|
|
18865
18993
|
function seedWorktree(worktreePath, clone, onSeeded = () => {
|
|
18866
18994
|
}) {
|
|
@@ -18884,29 +19012,6 @@ function copyConfigFiles(worktreePath, clone, copy) {
|
|
|
18884
19012
|
}
|
|
18885
19013
|
}
|
|
18886
19014
|
}
|
|
18887
|
-
function runInstall(worktreePath, clone, install, onSeeded) {
|
|
18888
|
-
const command = resolveInstallCommand(clone, install);
|
|
18889
|
-
if (!command) {
|
|
18890
|
-
onSeeded();
|
|
18891
|
-
return;
|
|
18892
|
-
}
|
|
18893
|
-
const windows = /^[A-Za-z]:[\\/]/.test(worktreePath);
|
|
18894
|
-
const shell = windows ? "cmd.exe" : process.env.SHELL ?? "bash";
|
|
18895
|
-
const shellArgs = windows ? ["/c", `cd /d ${quoteForCmd(worktreePath)} && ${command}`] : ["-l", "-c", `cd ${quoteForPosixShell(worktreePath)} && ${command}`];
|
|
18896
|
-
daemonLog(`worktree ${worktreePath} installing deps: ${command}`);
|
|
18897
|
-
execFile8(shell, shellArgs, { cwd: worktreePath }, (error) => {
|
|
18898
|
-
daemonLog(
|
|
18899
|
-
error ? `worktree ${worktreePath} install failed: ${error.message}` : `worktree ${worktreePath} install complete`
|
|
18900
|
-
);
|
|
18901
|
-
onSeeded();
|
|
18902
|
-
});
|
|
18903
|
-
}
|
|
18904
|
-
function quoteForPosixShell(value) {
|
|
18905
|
-
return `'${value.replaceAll("'", `'\\''`)}'`;
|
|
18906
|
-
}
|
|
18907
|
-
function quoteForCmd(value) {
|
|
18908
|
-
return `"${value.replaceAll('"', "")}"`;
|
|
18909
|
-
}
|
|
18910
19015
|
function message(error) {
|
|
18911
19016
|
return error instanceof Error ? error.message : String(error);
|
|
18912
19017
|
}
|
|
@@ -20881,16 +20986,16 @@ function reply(commentId, body) {
|
|
|
20881
20986
|
|
|
20882
20987
|
// src/commands/prs/wontfix.ts
|
|
20883
20988
|
import { execSync as execSync50 } from "child_process";
|
|
20884
|
-
function validateReason(
|
|
20885
|
-
const lowerReason =
|
|
20989
|
+
function validateReason(reason4) {
|
|
20990
|
+
const lowerReason = reason4.toLowerCase();
|
|
20886
20991
|
if (lowerReason.includes("claude") || lowerReason.includes("opus")) {
|
|
20887
20992
|
console.error('Error: Reason must not contain "claude" or "opus"');
|
|
20888
20993
|
process.exit(1);
|
|
20889
20994
|
}
|
|
20890
20995
|
}
|
|
20891
|
-
function validateShaReferences(
|
|
20996
|
+
function validateShaReferences(reason4) {
|
|
20892
20997
|
const commitLinkPattern = /\[[0-9a-f]{7}\]\(https:\/\/github\.com\/[^/]+\/[^/]+\/commit\/([0-9a-f]{40})\)/gi;
|
|
20893
|
-
const shas = Array.from(
|
|
20998
|
+
const shas = Array.from(reason4.matchAll(commitLinkPattern), (m) => m[1]);
|
|
20894
20999
|
if (shas.length === 0) {
|
|
20895
21000
|
return;
|
|
20896
21001
|
}
|
|
@@ -20909,11 +21014,11 @@ function validateShaReferences(reason2) {
|
|
|
20909
21014
|
process.exit(1);
|
|
20910
21015
|
}
|
|
20911
21016
|
}
|
|
20912
|
-
function wontfix(commentId,
|
|
20913
|
-
validateReason(
|
|
20914
|
-
validateShaReferences(
|
|
21017
|
+
function wontfix(commentId, reason4) {
|
|
21018
|
+
validateReason(reason4);
|
|
21019
|
+
validateShaReferences(reason4);
|
|
20915
21020
|
try {
|
|
20916
|
-
resolveCommentWithReply(commentId,
|
|
21021
|
+
resolveCommentWithReply(commentId, reason4);
|
|
20917
21022
|
} catch (error) {
|
|
20918
21023
|
if (isGhNotInstalled(error)) {
|
|
20919
21024
|
console.error("Error: GitHub CLI (gh) is not installed.");
|
|
@@ -21092,8 +21197,8 @@ function registerPrs(program2) {
|
|
|
21092
21197
|
prsCommand.command("fixed <comment-id> <sha>").description("Reply with commit link and resolve thread").action((commentId, sha) => {
|
|
21093
21198
|
fixed(Number.parseInt(commentId, 10), sha);
|
|
21094
21199
|
});
|
|
21095
|
-
prsCommand.command("wontfix <comment-id> <reason>").description("Reply with reason and resolve thread").action((commentId,
|
|
21096
|
-
wontfix(Number.parseInt(commentId, 10),
|
|
21200
|
+
prsCommand.command("wontfix <comment-id> <reason>").description("Reply with reason and resolve thread").action((commentId, reason4) => {
|
|
21201
|
+
wontfix(Number.parseInt(commentId, 10), reason4);
|
|
21097
21202
|
});
|
|
21098
21203
|
prsCommand.command("reply <comment-id> <body>").description("Reply to a comment thread without resolving it").action((commentId, body) => {
|
|
21099
21204
|
reply(Number.parseInt(commentId, 10), body);
|
|
@@ -21542,7 +21647,7 @@ function registerRavendb(program2) {
|
|
|
21542
21647
|
}
|
|
21543
21648
|
|
|
21544
21649
|
// src/commands/refactor/check/index.ts
|
|
21545
|
-
import { spawn as
|
|
21650
|
+
import { spawn as spawn6 } from "child_process";
|
|
21546
21651
|
import * as path36 from "path";
|
|
21547
21652
|
|
|
21548
21653
|
// src/commands/refactor/logViolations.ts
|
|
@@ -21690,7 +21795,7 @@ function getViolations(pattern2, options2 = {}, maxLines = DEFAULT_MAX_LINES) {
|
|
|
21690
21795
|
// src/commands/refactor/check/index.ts
|
|
21691
21796
|
function runScript(script, cwd) {
|
|
21692
21797
|
return new Promise((resolve20) => {
|
|
21693
|
-
const child =
|
|
21798
|
+
const child = spawn6("npm", ["run", script], {
|
|
21694
21799
|
stdio: "pipe",
|
|
21695
21800
|
shell: true,
|
|
21696
21801
|
cwd
|
|
@@ -23074,10 +23179,10 @@ function listFilesRecursive(dir) {
|
|
|
23074
23179
|
}
|
|
23075
23180
|
return results;
|
|
23076
23181
|
}
|
|
23077
|
-
function addDirectoryFileMoves(moves, childDir, newLocation,
|
|
23182
|
+
function addDirectoryFileMoves(moves, childDir, newLocation, reason4) {
|
|
23078
23183
|
for (const file of listFilesRecursive(childDir)) {
|
|
23079
23184
|
const rel = path55.relative(childDir, file);
|
|
23080
|
-
moves.push({ from: file, to: path55.join(newLocation, rel), reason:
|
|
23185
|
+
moves.push({ from: file, to: path55.join(newLocation, rel), reason: reason4 });
|
|
23081
23186
|
}
|
|
23082
23187
|
}
|
|
23083
23188
|
function resolveChildDest(parentDir, childDir) {
|
|
@@ -23088,8 +23193,8 @@ function childMoveReason(parentDir) {
|
|
|
23088
23193
|
}
|
|
23089
23194
|
function registerDirectoryMove(result, childDir, dest, parentDir) {
|
|
23090
23195
|
result.directories.push(dest);
|
|
23091
|
-
const
|
|
23092
|
-
addDirectoryFileMoves(result.moves, childDir, dest,
|
|
23196
|
+
const reason4 = childMoveReason(parentDir);
|
|
23197
|
+
addDirectoryFileMoves(result.moves, childDir, dest, reason4);
|
|
23093
23198
|
}
|
|
23094
23199
|
function planChildDirectoryMove(result, parentDir, childDir) {
|
|
23095
23200
|
const dest = resolveChildDest(parentDir, childDir);
|
|
@@ -24384,7 +24489,7 @@ function reportReviewerToolUse(name, use, spinner) {
|
|
|
24384
24489
|
}
|
|
24385
24490
|
|
|
24386
24491
|
// src/commands/review/runStreamingChild.ts
|
|
24387
|
-
import { spawn as
|
|
24492
|
+
import { spawn as spawn7 } from "child_process";
|
|
24388
24493
|
|
|
24389
24494
|
// src/commands/review/attachLineParser.ts
|
|
24390
24495
|
function flushBuffer(buffer, onLine) {
|
|
@@ -24535,7 +24640,7 @@ function writeStdinSafely(child, payload) {
|
|
|
24535
24640
|
}
|
|
24536
24641
|
}
|
|
24537
24642
|
function startChild(spec) {
|
|
24538
|
-
const child =
|
|
24643
|
+
const child = spawn7(spec.command, spec.args, {
|
|
24539
24644
|
stdio: ["pipe", "pipe", "pipe"],
|
|
24540
24645
|
shell: process.platform === "win32"
|
|
24541
24646
|
});
|
|
@@ -26192,7 +26297,7 @@ function setup() {
|
|
|
26192
26297
|
}
|
|
26193
26298
|
|
|
26194
26299
|
// src/commands/voice/start.ts
|
|
26195
|
-
import { spawn as
|
|
26300
|
+
import { spawn as spawn8 } from "child_process";
|
|
26196
26301
|
import { mkdirSync as mkdirSync23, writeFileSync as writeFileSync40 } from "fs";
|
|
26197
26302
|
import { join as join65 } from "path";
|
|
26198
26303
|
|
|
@@ -26207,11 +26312,11 @@ function buildDaemonEnv(options2) {
|
|
|
26207
26312
|
// src/commands/voice/start.ts
|
|
26208
26313
|
function spawnForeground(python, script, env) {
|
|
26209
26314
|
console.log("Starting voice daemon in foreground...");
|
|
26210
|
-
const child =
|
|
26315
|
+
const child = spawn8(python, [script], { stdio: "inherit", env });
|
|
26211
26316
|
child.on("exit", (code) => process.exit(code ?? 0));
|
|
26212
26317
|
}
|
|
26213
26318
|
function spawnBackground(python, script, env) {
|
|
26214
|
-
const child =
|
|
26319
|
+
const child = spawn8(python, [script], {
|
|
26215
26320
|
detached: true,
|
|
26216
26321
|
stdio: "ignore",
|
|
26217
26322
|
env
|
|
@@ -27012,7 +27117,7 @@ function runPreCommands(pre, cwd) {
|
|
|
27012
27117
|
}
|
|
27013
27118
|
|
|
27014
27119
|
// src/commands/run/spawnRunCommand.ts
|
|
27015
|
-
import { execFileSync as execFileSync12, spawn as
|
|
27120
|
+
import { execFileSync as execFileSync12, spawn as spawn9 } from "child_process";
|
|
27016
27121
|
import { existsSync as existsSync58 } from "fs";
|
|
27017
27122
|
import { dirname as dirname29, join as join67, resolve as resolve15 } from "path";
|
|
27018
27123
|
function resolveCommand2(command) {
|
|
@@ -27028,7 +27133,7 @@ function resolveCommand2(command) {
|
|
|
27028
27133
|
}
|
|
27029
27134
|
function spawnRunCommand(command, args, env, cwd, quiet) {
|
|
27030
27135
|
const start3 = Date.now();
|
|
27031
|
-
const child =
|
|
27136
|
+
const child = spawn9(resolveCommand2(command), args, {
|
|
27032
27137
|
stdio: quiet ? "pipe" : "inherit",
|
|
27033
27138
|
env: env ? { ...process.env, ...expandEnv(env) } : void 0,
|
|
27034
27139
|
cwd
|
|
@@ -28104,9 +28209,89 @@ function otherTreeHolders(sessions, session) {
|
|
|
28104
28209
|
}
|
|
28105
28210
|
|
|
28106
28211
|
// src/commands/sessions/daemon/worktree/reapWorktree.ts
|
|
28107
|
-
import { existsSync as
|
|
28212
|
+
import { existsSync as existsSync63 } from "fs";
|
|
28108
28213
|
import { basename as basename16 } from "path";
|
|
28109
28214
|
|
|
28215
|
+
// src/commands/sessions/daemon/worktree/deleteStrandedTree.ts
|
|
28216
|
+
import { existsSync as existsSync62 } from "fs";
|
|
28217
|
+
import { join as join73 } from "path";
|
|
28218
|
+
|
|
28219
|
+
// src/commands/sessions/daemon/worktree/deleteTreeDirectly.ts
|
|
28220
|
+
import { statSync as statSync11 } from "fs";
|
|
28221
|
+
import { rm as rm2 } from "fs/promises";
|
|
28222
|
+
import { join as join72 } from "path";
|
|
28223
|
+
async function deleteTreeDirectly(clone, worktreePath, why) {
|
|
28224
|
+
if (holdsAGitDirectoryRatherThanALink(worktreePath)) {
|
|
28225
|
+
daemonLog(
|
|
28226
|
+
`worktree ${worktreePath} not deleted directly (${why}): it is a clone of its own, not a linked worktree`
|
|
28227
|
+
);
|
|
28228
|
+
return false;
|
|
28229
|
+
}
|
|
28230
|
+
daemonLog(`worktree ${worktreePath} deleting its directory directly: ${why}`);
|
|
28231
|
+
try {
|
|
28232
|
+
await rm2(worktreePath, {
|
|
28233
|
+
recursive: true,
|
|
28234
|
+
force: true,
|
|
28235
|
+
maxRetries: 3,
|
|
28236
|
+
retryDelay: 200
|
|
28237
|
+
});
|
|
28238
|
+
} catch (error) {
|
|
28239
|
+
daemonLog(
|
|
28240
|
+
`worktree ${worktreePath} directory delete failed, left in place for the next reconcile: ${reason(error)}`
|
|
28241
|
+
);
|
|
28242
|
+
return false;
|
|
28243
|
+
}
|
|
28244
|
+
daemonLog(`worktree ${worktreePath} directory deleted directly`);
|
|
28245
|
+
await pruneBookkeeping(clone, worktreePath);
|
|
28246
|
+
return true;
|
|
28247
|
+
}
|
|
28248
|
+
function holdsAGitDirectoryRatherThanALink(worktreePath) {
|
|
28249
|
+
return statSync11(join72(worktreePath, ".git"), {
|
|
28250
|
+
throwIfNoEntry: false
|
|
28251
|
+
})?.isDirectory() === true;
|
|
28252
|
+
}
|
|
28253
|
+
async function pruneBookkeeping(clone, worktreePath) {
|
|
28254
|
+
try {
|
|
28255
|
+
await git2(clone, ["worktree", "prune"]);
|
|
28256
|
+
daemonLog(
|
|
28257
|
+
`worktree bookkeeping pruned in clone ${clone} after deleting ${worktreePath} directly`
|
|
28258
|
+
);
|
|
28259
|
+
} catch (error) {
|
|
28260
|
+
daemonLog(
|
|
28261
|
+
`worktree bookkeeping prune failed in clone ${clone} after deleting ${worktreePath}: ${reason(error)}`
|
|
28262
|
+
);
|
|
28263
|
+
}
|
|
28264
|
+
}
|
|
28265
|
+
function reason(error) {
|
|
28266
|
+
return error instanceof Error ? error.message : String(error);
|
|
28267
|
+
}
|
|
28268
|
+
|
|
28269
|
+
// src/commands/sessions/daemon/worktree/deleteStrandedTree.ts
|
|
28270
|
+
async function deleteStrandedTree(clone, worktreePath, cause) {
|
|
28271
|
+
const stranded = strandedReason(worktreePath, cause);
|
|
28272
|
+
if (!stranded) {
|
|
28273
|
+
daemonLog(
|
|
28274
|
+
`worktree ${worktreePath} left in place for the next reconcile: ${reason2(cause)}`
|
|
28275
|
+
);
|
|
28276
|
+
return false;
|
|
28277
|
+
}
|
|
28278
|
+
return await deleteTreeDirectly(
|
|
28279
|
+
clone,
|
|
28280
|
+
worktreePath,
|
|
28281
|
+
`git can no longer remove it, ${stranded}`
|
|
28282
|
+
);
|
|
28283
|
+
}
|
|
28284
|
+
function strandedReason(worktreePath, cause) {
|
|
28285
|
+
if (!existsSync62(join73(worktreePath, ".git")))
|
|
28286
|
+
return "its .git link is already gone";
|
|
28287
|
+
if (/not a working tree|not a git repository/i.test(reason2(cause)))
|
|
28288
|
+
return "git no longer recognises it as a working tree";
|
|
28289
|
+
return null;
|
|
28290
|
+
}
|
|
28291
|
+
function reason2(error) {
|
|
28292
|
+
return error instanceof Error ? error.message : String(error);
|
|
28293
|
+
}
|
|
28294
|
+
|
|
28110
28295
|
// src/commands/sessions/daemon/worktree/removeTree.ts
|
|
28111
28296
|
async function removeTree(clone, worktreePath, force) {
|
|
28112
28297
|
const remove2 = (forced) => git2(clone, [
|
|
@@ -28119,12 +28304,17 @@ async function removeTree(clone, worktreePath, force) {
|
|
|
28119
28304
|
await remove2(force);
|
|
28120
28305
|
return true;
|
|
28121
28306
|
} catch (error) {
|
|
28122
|
-
daemonLog(`worktree ${worktreePath} removal failed: ${
|
|
28307
|
+
daemonLog(`worktree ${worktreePath} removal failed: ${reason3(error)}`);
|
|
28308
|
+
if (force)
|
|
28309
|
+
return await deleteTreeDirectly(
|
|
28310
|
+
clone,
|
|
28311
|
+
worktreePath,
|
|
28312
|
+
`its work was discarded and git could not remove it: ${reason3(error)}`
|
|
28313
|
+
);
|
|
28123
28314
|
}
|
|
28124
|
-
|
|
28125
|
-
return await removeIgnoringLeftovers(remove2, worktreePath);
|
|
28315
|
+
return await removeIgnoringLeftovers(clone, remove2, worktreePath);
|
|
28126
28316
|
}
|
|
28127
|
-
async function removeIgnoringLeftovers(remove2, worktreePath) {
|
|
28317
|
+
async function removeIgnoringLeftovers(clone, remove2, worktreePath) {
|
|
28128
28318
|
try {
|
|
28129
28319
|
await remove2(true);
|
|
28130
28320
|
daemonLog(
|
|
@@ -28133,18 +28323,18 @@ async function removeIgnoringLeftovers(remove2, worktreePath) {
|
|
|
28133
28323
|
return true;
|
|
28134
28324
|
} catch (error) {
|
|
28135
28325
|
daemonLog(
|
|
28136
|
-
`worktree ${worktreePath}
|
|
28326
|
+
`worktree ${worktreePath} forced removal failed: ${reason3(error)}`
|
|
28137
28327
|
);
|
|
28138
|
-
return
|
|
28328
|
+
return await deleteStrandedTree(clone, worktreePath, error);
|
|
28139
28329
|
}
|
|
28140
28330
|
}
|
|
28141
|
-
function
|
|
28331
|
+
function reason3(error) {
|
|
28142
28332
|
return error instanceof Error ? error.message : String(error);
|
|
28143
28333
|
}
|
|
28144
28334
|
|
|
28145
28335
|
// src/commands/sessions/daemon/worktree/reapWorktree.ts
|
|
28146
28336
|
async function reapWorktree(worktreePath, force = false) {
|
|
28147
|
-
if (!
|
|
28337
|
+
if (!existsSync63(worktreePath)) {
|
|
28148
28338
|
daemonLog(`worktree ${worktreePath} already gone; skipping reap`);
|
|
28149
28339
|
return false;
|
|
28150
28340
|
}
|
|
@@ -28155,13 +28345,24 @@ async function reapWorktree(worktreePath, force = false) {
|
|
|
28155
28345
|
return false;
|
|
28156
28346
|
}
|
|
28157
28347
|
}
|
|
28158
|
-
|
|
28348
|
+
stopInstall(worktreePath);
|
|
28349
|
+
const clone = owningClone(worktreePath);
|
|
28159
28350
|
if (!await removeTree(clone, worktreePath, force)) return false;
|
|
28160
28351
|
await deleteWorktreeBranch(clone, basename16(worktreePath));
|
|
28161
28352
|
forgetWorktree(worktreePath);
|
|
28162
28353
|
daemonLog(`worktree ${worktreePath} reaped${force ? " (forced)" : ""}`);
|
|
28163
28354
|
return true;
|
|
28164
28355
|
}
|
|
28356
|
+
function owningClone(worktreePath) {
|
|
28357
|
+
const recorded = worktreeAttributionIncludingReaped(worktreePath)?.clone;
|
|
28358
|
+
if (recorded && existsSync63(recorded)) return recorded;
|
|
28359
|
+
const detected = mainWorktree(worktreePath);
|
|
28360
|
+
if (detected) return detected;
|
|
28361
|
+
daemonLog(
|
|
28362
|
+
`worktree ${worktreePath} has no resolvable clone; tearing it down on its own`
|
|
28363
|
+
);
|
|
28364
|
+
return worktreePath;
|
|
28365
|
+
}
|
|
28165
28366
|
async function deleteWorktreeBranch(clone, branch2) {
|
|
28166
28367
|
if (!listLocalBranches(clone).includes(branch2)) return;
|
|
28167
28368
|
const current = await gitOrNull2(clone, ["symbolic-ref", "--short", "HEAD"]);
|
|
@@ -28241,12 +28442,12 @@ function setStatus2(session, newStatus) {
|
|
|
28241
28442
|
}
|
|
28242
28443
|
|
|
28243
28444
|
// src/commands/sessions/daemon/worktree/watchGitState.ts
|
|
28244
|
-
import { existsSync as
|
|
28445
|
+
import { existsSync as existsSync64, watch } from "fs";
|
|
28245
28446
|
var DEBOUNCE_MS = 500;
|
|
28246
28447
|
var POLL_MS = 3e4;
|
|
28247
28448
|
function watchGitState(cwd, onChange) {
|
|
28248
28449
|
const common = gitCommonDir(cwd);
|
|
28249
|
-
if (!common || !
|
|
28450
|
+
if (!common || !existsSync64(common)) return void 0;
|
|
28250
28451
|
const watchers = [
|
|
28251
28452
|
watchGitDir(common, onChange),
|
|
28252
28453
|
pollGitState(cwd, onChange)
|
|
@@ -28300,12 +28501,12 @@ async function resolveCloseDurability(session, finalize, notify2) {
|
|
|
28300
28501
|
session.gitWatcher = void 0;
|
|
28301
28502
|
finalize();
|
|
28302
28503
|
}
|
|
28303
|
-
function holdStopped(session, tree,
|
|
28304
|
-
if (session.undurable?.reason !==
|
|
28504
|
+
function holdStopped(session, tree, reason4, finalize, notify2) {
|
|
28505
|
+
if (session.undurable?.reason !== reason4)
|
|
28305
28506
|
daemonLog(
|
|
28306
|
-
`session ${session.id} stopped; ${tree.removable ? "reap" : "close"} blocked in ${tree.path}: ${
|
|
28507
|
+
`session ${session.id} stopped; ${tree.removable ? "reap" : "close"} blocked in ${tree.path}: ${reason4}`
|
|
28307
28508
|
);
|
|
28308
|
-
session.undurable = { reason:
|
|
28509
|
+
session.undurable = { reason: reason4, removesTree: tree.removable };
|
|
28309
28510
|
session.closing = void 0;
|
|
28310
28511
|
setStatus2(session, "stopped");
|
|
28311
28512
|
if (!session.gitWatcher) {
|
|
@@ -28650,10 +28851,10 @@ function emitSessionOutput(session, clients, data) {
|
|
|
28650
28851
|
}
|
|
28651
28852
|
|
|
28652
28853
|
// src/commands/sessions/daemon/exitReason.ts
|
|
28653
|
-
import { existsSync as
|
|
28854
|
+
import { existsSync as existsSync65 } from "fs";
|
|
28654
28855
|
function exitReason(session, exitCode) {
|
|
28655
28856
|
const base = `process exited with code ${exitCode}`;
|
|
28656
|
-
if (session.cwd && !
|
|
28857
|
+
if (session.cwd && !existsSync65(session.cwd))
|
|
28657
28858
|
return `${base}: working directory ${session.cwd} no longer exists`;
|
|
28658
28859
|
return base;
|
|
28659
28860
|
}
|
|
@@ -28671,7 +28872,7 @@ function handleFailedResume(session, exitCode, onStatusChange) {
|
|
|
28671
28872
|
}
|
|
28672
28873
|
|
|
28673
28874
|
// src/commands/sessions/daemon/watchActivity.ts
|
|
28674
|
-
import { existsSync as
|
|
28875
|
+
import { existsSync as existsSync66, mkdirSync as mkdirSync26, watch as watch2 } from "fs";
|
|
28675
28876
|
import { dirname as dirname31 } from "path";
|
|
28676
28877
|
|
|
28677
28878
|
// src/commands/sessions/daemon/applyReviewPause.ts
|
|
@@ -28752,7 +28953,7 @@ function watchActivity(session, notify2, onClaudeSessionId) {
|
|
|
28752
28953
|
if (timer) clearTimeout(timer);
|
|
28753
28954
|
timer = setTimeout(read, DEBOUNCE_MS2);
|
|
28754
28955
|
});
|
|
28755
|
-
if (
|
|
28956
|
+
if (existsSync66(path71)) read();
|
|
28756
28957
|
}
|
|
28757
28958
|
function refreshActivity(session) {
|
|
28758
28959
|
if (session.commandType !== "assist" || !session.cwd) return;
|
|
@@ -29767,14 +29968,14 @@ function restoreSession(id, persisted) {
|
|
|
29767
29968
|
}
|
|
29768
29969
|
|
|
29769
29970
|
// src/commands/sessions/daemon/restoreOne.ts
|
|
29770
|
-
function restoreOne(persisted,
|
|
29971
|
+
function restoreOne(persisted, spawn13, sessions) {
|
|
29771
29972
|
try {
|
|
29772
|
-
const id =
|
|
29973
|
+
const id = spawn13((sid) => restoreSession(sid, persisted));
|
|
29773
29974
|
logUnresumable(persisted.name, id, sessions.get(id));
|
|
29774
29975
|
} catch (error) {
|
|
29775
|
-
const
|
|
29976
|
+
const reason4 = logRestoreError(persisted.name, error);
|
|
29776
29977
|
try {
|
|
29777
|
-
|
|
29978
|
+
spawn13((id) => errorSession(id, persisted, reason4));
|
|
29778
29979
|
} catch {
|
|
29779
29980
|
}
|
|
29780
29981
|
}
|
|
@@ -29786,9 +29987,9 @@ function logUnresumable(name, id, session) {
|
|
|
29786
29987
|
);
|
|
29787
29988
|
}
|
|
29788
29989
|
function logRestoreError(name, error) {
|
|
29789
|
-
const
|
|
29790
|
-
daemonLog(`failed to restore session "${name}": ${
|
|
29791
|
-
return
|
|
29990
|
+
const reason4 = error instanceof Error ? error.message : String(error);
|
|
29991
|
+
daemonLog(`failed to restore session "${name}": ${reason4}`);
|
|
29992
|
+
return reason4;
|
|
29792
29993
|
}
|
|
29793
29994
|
|
|
29794
29995
|
// src/commands/sessions/daemon/sessionLimits.ts
|
|
@@ -29809,7 +30010,7 @@ var sessionLimits = {
|
|
|
29809
30010
|
};
|
|
29810
30011
|
|
|
29811
30012
|
// src/commands/sessions/daemon/restoreAll.ts
|
|
29812
|
-
function restoreAll(
|
|
30013
|
+
function restoreAll(spawn13, sessions) {
|
|
29813
30014
|
const persisted = loadPersistedSessions();
|
|
29814
30015
|
const toRestore = persisted.slice(0, sessionLimits.maxRestore);
|
|
29815
30016
|
const skipped = persisted.length - toRestore.length;
|
|
@@ -29818,7 +30019,7 @@ function restoreAll(spawn12, sessions) {
|
|
|
29818
30019
|
`restore capped at ${sessionLimits.maxRestore}; skipping ${skipped} persisted session(s)`
|
|
29819
30020
|
);
|
|
29820
30021
|
return toRestore.map((persisted2) => {
|
|
29821
|
-
restoreOne(persisted2,
|
|
30022
|
+
restoreOne(persisted2, spawn13, sessions);
|
|
29822
30023
|
return persisted2.name;
|
|
29823
30024
|
});
|
|
29824
30025
|
}
|
|
@@ -29841,7 +30042,7 @@ function rearmStoppedSessions(sessions, notify2) {
|
|
|
29841
30042
|
}
|
|
29842
30043
|
|
|
29843
30044
|
// src/commands/sessions/daemon/worktree/reconcileWorktreesOnRestore.ts
|
|
29844
|
-
import { existsSync as
|
|
30045
|
+
import { existsSync as existsSync69 } from "fs";
|
|
29845
30046
|
import { basename as basename18 } from "path";
|
|
29846
30047
|
|
|
29847
30048
|
// src/commands/sessions/daemon/worktree/accountedTrees.ts
|
|
@@ -29897,10 +30098,10 @@ function bindResumedWorktree(session, cwd, notify2) {
|
|
|
29897
30098
|
|
|
29898
30099
|
// src/commands/sessions/daemon/worktree/describeHeldWork.ts
|
|
29899
30100
|
var MAX_ITEMS = 20;
|
|
29900
|
-
async function describeHeldWork(path71,
|
|
29901
|
-
if (
|
|
29902
|
-
if (
|
|
29903
|
-
return { summary:
|
|
30101
|
+
async function describeHeldWork(path71, reason4) {
|
|
30102
|
+
if (reason4.startsWith("uncommitted")) return await changedFiles(path71);
|
|
30103
|
+
if (reason4.startsWith("unpushed")) return await unpushedCommits(path71, reason4);
|
|
30104
|
+
return { summary: reason4, items: [] };
|
|
29904
30105
|
}
|
|
29905
30106
|
async function changedFiles(path71) {
|
|
29906
30107
|
const status3 = await gitResult(path71, ["status", "--porcelain"]);
|
|
@@ -29910,9 +30111,9 @@ async function changedFiles(path71) {
|
|
|
29910
30111
|
items: capped(lines)
|
|
29911
30112
|
};
|
|
29912
30113
|
}
|
|
29913
|
-
async function unpushedCommits(path71,
|
|
30114
|
+
async function unpushedCommits(path71, reason4) {
|
|
29914
30115
|
const log = await gitResult(path71, ["log", "--oneline", "@{upstream}..HEAD"]);
|
|
29915
|
-
if (!log.ok) return { summary:
|
|
30116
|
+
if (!log.ok) return { summary: reason4, items: [] };
|
|
29916
30117
|
const lines = nonEmptyLines(log.out);
|
|
29917
30118
|
return {
|
|
29918
30119
|
summary: `${lines.length} unpushed ${lines.length === 1 ? "commit" : "commits"}`,
|
|
@@ -29931,20 +30132,20 @@ function capped(lines) {
|
|
|
29931
30132
|
}
|
|
29932
30133
|
|
|
29933
30134
|
// src/commands/sessions/daemon/worktree/reclaimVanishedWorktrees.ts
|
|
29934
|
-
import { existsSync as
|
|
30135
|
+
import { existsSync as existsSync68 } from "fs";
|
|
29935
30136
|
async function reclaimVanishedWorktrees(clone, paths) {
|
|
29936
|
-
if (!
|
|
30137
|
+
if (!existsSync68(clone)) {
|
|
29937
30138
|
for (const { path: path71 } of paths) forgetWorktree(path71);
|
|
29938
30139
|
daemonLog(
|
|
29939
30140
|
`clone ${clone} is gone; forgot ${paths.length} worktree record(s) it owned`
|
|
29940
30141
|
);
|
|
29941
30142
|
return;
|
|
29942
30143
|
}
|
|
29943
|
-
await
|
|
30144
|
+
await pruneBookkeeping2(clone);
|
|
29944
30145
|
for (const { path: path71, branch: branch2 } of paths)
|
|
29945
30146
|
if (await reclaimBranch(clone, branch2)) forgetWorktree(path71);
|
|
29946
30147
|
}
|
|
29947
|
-
async function
|
|
30148
|
+
async function pruneBookkeeping2(clone) {
|
|
29948
30149
|
try {
|
|
29949
30150
|
await git2(clone, ["worktree", "prune"]);
|
|
29950
30151
|
daemonLog(`worktree bookkeeping pruned for clone ${clone}`);
|
|
@@ -29971,7 +30172,7 @@ async function reclaimBranch(clone, branch2) {
|
|
|
29971
30172
|
// src/commands/sessions/daemon/worktree/resurfaceOrphanedWorktree.ts
|
|
29972
30173
|
import { basename as basename17 } from "path";
|
|
29973
30174
|
function resurfaceOrphanedWorktree(sessions, spawnWith, recovered, notify2) {
|
|
29974
|
-
const { orphan, reason:
|
|
30175
|
+
const { orphan, reason: reason4, held } = recovered;
|
|
29975
30176
|
let id;
|
|
29976
30177
|
try {
|
|
29977
30178
|
id = spawnWith((sid) => orphanedSession(sid, recovered));
|
|
@@ -29982,14 +30183,14 @@ function resurfaceOrphanedWorktree(sessions, spawnWith, recovered, notify2) {
|
|
|
29982
30183
|
return;
|
|
29983
30184
|
}
|
|
29984
30185
|
daemonLog(
|
|
29985
|
-
`orphaned worktree ${orphan.path} resurfaced as stopped session ${id}: ${
|
|
30186
|
+
`orphaned worktree ${orphan.path} resurfaced as stopped session ${id}: ${reason4} (${held.summary})`
|
|
29986
30187
|
);
|
|
29987
30188
|
const session = sessions.get(id);
|
|
29988
30189
|
if (session) armStoppedSession(sessions, session, notify2);
|
|
29989
30190
|
notify2();
|
|
29990
30191
|
}
|
|
29991
30192
|
function orphanedSession(id, recovered) {
|
|
29992
|
-
const { orphan, reason:
|
|
30193
|
+
const { orphan, reason: reason4, held } = recovered;
|
|
29993
30194
|
return {
|
|
29994
30195
|
...sessionBase(id, "stopped"),
|
|
29995
30196
|
name: `recovered ${basename17(orphan.path)}`,
|
|
@@ -29998,7 +30199,7 @@ function orphanedSession(id, recovered) {
|
|
|
29998
30199
|
pty: null,
|
|
29999
30200
|
cwd: orphan.path,
|
|
30000
30201
|
worktree: { path: orphan.path, clone: orphan.clone },
|
|
30001
|
-
undurable: { reason:
|
|
30202
|
+
undurable: { reason: reason4, removesTree: true },
|
|
30002
30203
|
scrollback: recoveryNotice(recovered)
|
|
30003
30204
|
};
|
|
30004
30205
|
}
|
|
@@ -30026,7 +30227,7 @@ async function recoverOrphanedWorktrees(sessions, spawnWith, notify2) {
|
|
|
30026
30227
|
const vanished = /* @__PURE__ */ new Map();
|
|
30027
30228
|
for (const { path: path71, clone } of readWorktreeRegistry()) {
|
|
30028
30229
|
if (accounted.has(path71)) continue;
|
|
30029
|
-
if (!
|
|
30230
|
+
if (!existsSync69(path71)) {
|
|
30030
30231
|
vanished.set(clone, [
|
|
30031
30232
|
...vanished.get(clone) ?? [],
|
|
30032
30233
|
{ path: path71, branch: basename18(path71) }
|
|
@@ -30239,6 +30440,12 @@ function startHeldSession(session, sessions, clients, onStatusChange, notify2) {
|
|
|
30239
30440
|
);
|
|
30240
30441
|
return;
|
|
30241
30442
|
}
|
|
30443
|
+
if (session.closing) {
|
|
30444
|
+
daemonLog(
|
|
30445
|
+
`session ${session.id} not started after seeding: card is closing (${session.worktree?.path ?? "no worktree"})`
|
|
30446
|
+
);
|
|
30447
|
+
return;
|
|
30448
|
+
}
|
|
30242
30449
|
session.startedAt = Date.now();
|
|
30243
30450
|
if (session.status === "running") session.runningSince = session.startedAt;
|
|
30244
30451
|
session.pty = start3();
|
|
@@ -30315,7 +30522,7 @@ async function isWindowsDaemonRunning() {
|
|
|
30315
30522
|
}
|
|
30316
30523
|
|
|
30317
30524
|
// src/commands/sessions/daemon/ensureWindowsDaemonRunning.ts
|
|
30318
|
-
import { spawn as
|
|
30525
|
+
import { spawn as spawn11 } from "child_process";
|
|
30319
30526
|
|
|
30320
30527
|
// src/commands/sessions/daemon/logChildStream.ts
|
|
30321
30528
|
import { createInterface as createInterface7 } from "readline";
|
|
@@ -30340,7 +30547,7 @@ async function ensureWindowsDaemonRunning() {
|
|
|
30340
30547
|
await waitForWindowsDaemon();
|
|
30341
30548
|
}
|
|
30342
30549
|
function launchWindowsDaemon() {
|
|
30343
|
-
const child =
|
|
30550
|
+
const child = spawn11("pwsh.exe", ["-Command", "assist daemon run"], {
|
|
30344
30551
|
detached: true,
|
|
30345
30552
|
stdio: ["ignore", "pipe", "pipe"]
|
|
30346
30553
|
});
|
|
@@ -30388,13 +30595,13 @@ async function defaultConnect() {
|
|
|
30388
30595
|
}
|
|
30389
30596
|
|
|
30390
30597
|
// src/commands/sessions/daemon/hasPersistedWindowsSessions.ts
|
|
30391
|
-
import { existsSync as
|
|
30598
|
+
import { existsSync as existsSync70, readFileSync as readFileSync50 } from "fs";
|
|
30392
30599
|
import { posix } from "path";
|
|
30393
30600
|
function hasPersistedWindowsSessions() {
|
|
30394
30601
|
const sessionsFile = windowsSessionsFileFromWsl();
|
|
30395
30602
|
if (!sessionsFile) return false;
|
|
30396
30603
|
try {
|
|
30397
|
-
if (!
|
|
30604
|
+
if (!existsSync70(sessionsFile)) return false;
|
|
30398
30605
|
const data = JSON.parse(readFileSync50(sessionsFile, "utf8"));
|
|
30399
30606
|
return Array.isArray(data) && data.length > 0;
|
|
30400
30607
|
} catch (error) {
|
|
@@ -30643,7 +30850,7 @@ function handleWindowsClose(state) {
|
|
|
30643
30850
|
}
|
|
30644
30851
|
|
|
30645
30852
|
// src/commands/sessions/daemon/healWindowsDaemon.ts
|
|
30646
|
-
import { spawn as
|
|
30853
|
+
import { spawn as spawn12 } from "child_process";
|
|
30647
30854
|
var UPDATE_TIMEOUT_MS = 5 * 6e4;
|
|
30648
30855
|
var STOP_TIMEOUT_MS2 = 3e4;
|
|
30649
30856
|
async function healWindowsDaemon() {
|
|
@@ -30663,7 +30870,7 @@ async function healWindowsDaemon() {
|
|
|
30663
30870
|
}
|
|
30664
30871
|
function runOnWindowsHost(command, timeoutMs) {
|
|
30665
30872
|
return new Promise((resolve20, reject) => {
|
|
30666
|
-
const child =
|
|
30873
|
+
const child = spawn12("pwsh.exe", ["-Command", command], {
|
|
30667
30874
|
stdio: ["ignore", "pipe", "pipe"]
|
|
30668
30875
|
});
|
|
30669
30876
|
logChildStream(child.stdout, `windows ${command}`);
|
|
@@ -31285,10 +31492,10 @@ import * as net3 from "net";
|
|
|
31285
31492
|
import { createInterface as createInterface9 } from "readline";
|
|
31286
31493
|
|
|
31287
31494
|
// src/commands/sessions/daemon/creator.ts
|
|
31288
|
-
function creator(isNew,
|
|
31495
|
+
function creator(isNew, spawn13) {
|
|
31289
31496
|
return (client, m, d) => {
|
|
31290
31497
|
if (m.windowsProxy.route(client, d)) return;
|
|
31291
|
-
sendTo(client, { type: "created", sessionId:
|
|
31498
|
+
sendTo(client, { type: "created", sessionId: spawn13(m, d), isNew });
|
|
31292
31499
|
};
|
|
31293
31500
|
}
|
|
31294
31501
|
|
|
@@ -32018,13 +32225,13 @@ function buildLimitsSegment(rateLimits) {
|
|
|
32018
32225
|
}
|
|
32019
32226
|
|
|
32020
32227
|
// src/commands/readGitBranch.ts
|
|
32021
|
-
import { readFileSync as readFileSync53, statSync as
|
|
32022
|
-
import { isAbsolute as isAbsolute3, join as
|
|
32228
|
+
import { readFileSync as readFileSync53, statSync as statSync13 } from "fs";
|
|
32229
|
+
import { isAbsolute as isAbsolute3, join as join75, resolve as resolve18 } from "path";
|
|
32023
32230
|
function resolveGitDir(cwd) {
|
|
32024
|
-
const dotGit =
|
|
32231
|
+
const dotGit = join75(cwd, ".git");
|
|
32025
32232
|
let stat3;
|
|
32026
32233
|
try {
|
|
32027
|
-
stat3 =
|
|
32234
|
+
stat3 = statSync13(dotGit);
|
|
32028
32235
|
} catch {
|
|
32029
32236
|
return null;
|
|
32030
32237
|
}
|
|
@@ -32051,7 +32258,7 @@ function readGitBranch(cwd) {
|
|
|
32051
32258
|
}
|
|
32052
32259
|
let head;
|
|
32053
32260
|
try {
|
|
32054
|
-
head = readFileSync53(
|
|
32261
|
+
head = readFileSync53(join75(gitDir, "HEAD"), "utf8");
|
|
32055
32262
|
} catch {
|
|
32056
32263
|
return null;
|
|
32057
32264
|
}
|