@norman-else/dsh-claude 0.1.42 → 0.1.44
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/lib/client.d.ts +18 -0
- package/lib/client.js +368 -99
- package/lib/client.js.map +1 -1
- package/lib/index.mjs +399 -57
- package/lib/index.mjs.map +1 -1
- package/package.json +1 -1
package/lib/index.mjs
CHANGED
|
@@ -1408,56 +1408,108 @@ function normalizeSdkMessage(message) {
|
|
|
1408
1408
|
}
|
|
1409
1409
|
//#endregion
|
|
1410
1410
|
//#region src/model-catalog.ts
|
|
1411
|
-
/**
|
|
1412
|
-
*
|
|
1413
|
-
*
|
|
1414
|
-
*
|
|
1415
|
-
*
|
|
1416
|
-
*
|
|
1411
|
+
/** The Claude Code model lineup, read from the running CLI instead of pinned
|
|
1412
|
+
* here.
|
|
1413
|
+
*
|
|
1414
|
+
* Anthropic ships models between releases of this plugin -- Fable arrived in a
|
|
1415
|
+
* CLI update, not in one of ours -- so a table maintained here is stale the day
|
|
1416
|
+
* it is written, and a model the user can already pick in `/model` is missing
|
|
1417
|
+
* from the DSH selector until someone edits an array. The CLI answers the same
|
|
1418
|
+
* question itself: every session's initialize response carries the lineup it
|
|
1419
|
+
* would show in `/model`, already narrowed to the logged-in account's plan and
|
|
1420
|
+
* to any `availableModels` restriction the settings cascade imposes.
|
|
1421
|
+
*
|
|
1422
|
+
* What DSH persists on a session, though, must NOT be a CLI model id. DSH
|
|
1423
|
+
* stores the selector row's id verbatim and matches it back by string
|
|
1424
|
+
* equality, so a concrete id (`claude-fable-5-1[1m]`) turns into a dangling
|
|
1425
|
+
* reference the moment Anthropic bumps the version -- the session keeps
|
|
1426
|
+
* pointing at a row nothing advertises any more, and the composer falls back
|
|
1427
|
+
* to printing the raw id. The selector therefore advertises an alias this
|
|
1428
|
+
* plugin owns (`fable[1m]`), derived from the row rather than tabulated, and
|
|
1429
|
+
* the CLI id it stands for is kept beside it and used only at dispatch.
|
|
1430
|
+
*/
|
|
1431
|
+
/** A 1M-context route spells it in the id (`opus[1m]`, `claude-fable-5-1[1m]`). */
|
|
1432
|
+
const WIDE_ROUTE = /\[1m\]$/u;
|
|
1433
|
+
/** What the selector shows before the lineup is known -- the probe below failed
|
|
1434
|
+
* or has not answered yet. `default` is the only id that is valid on every
|
|
1435
|
+
* release and plan; the rest are the stable `/model` spellings Claude Code has
|
|
1436
|
+
* kept across releases, and every one of them is a spelling the CLI accepts,
|
|
1437
|
+
* so a session that persists one still dispatches. */
|
|
1417
1438
|
const SEED = [
|
|
1418
1439
|
{
|
|
1419
1440
|
id: "default",
|
|
1441
|
+
value: "default",
|
|
1420
1442
|
name: "Default (recommended)",
|
|
1421
1443
|
description: ""
|
|
1422
1444
|
},
|
|
1423
1445
|
{
|
|
1424
1446
|
id: "opus[1m]",
|
|
1447
|
+
value: "opus[1m]",
|
|
1425
1448
|
name: "Opus (1M context)",
|
|
1426
1449
|
description: "",
|
|
1427
1450
|
contextWindow: 1e6
|
|
1428
1451
|
},
|
|
1429
1452
|
{
|
|
1430
1453
|
id: "fable",
|
|
1454
|
+
value: "fable",
|
|
1431
1455
|
name: "Fable",
|
|
1432
1456
|
description: ""
|
|
1433
1457
|
},
|
|
1434
1458
|
{
|
|
1435
1459
|
id: "sonnet",
|
|
1460
|
+
value: "sonnet",
|
|
1436
1461
|
name: "Sonnet",
|
|
1437
1462
|
description: ""
|
|
1438
1463
|
},
|
|
1439
1464
|
{
|
|
1440
1465
|
id: "haiku",
|
|
1466
|
+
value: "haiku",
|
|
1441
1467
|
name: "Haiku",
|
|
1442
1468
|
description: ""
|
|
1443
1469
|
}
|
|
1444
1470
|
];
|
|
1445
|
-
/** A 1M-context route spells it in the id
|
|
1446
|
-
*
|
|
1447
|
-
*
|
|
1471
|
+
/** A 1M-context route spells it in the id, so this needs no capacity table
|
|
1472
|
+
* either. It is only a floor: the supervisor overrides it with the window the
|
|
1473
|
+
* CLI reports once a turn has run. */
|
|
1448
1474
|
function declaredContextWindow(row) {
|
|
1449
|
-
return
|
|
1475
|
+
return WIDE_ROUTE.test(row.resolvedModel ?? row.value) ? 1e6 : void 0;
|
|
1476
|
+
}
|
|
1477
|
+
/**
|
|
1478
|
+
* The selector id for one CLI row: the model's family, plus the `[1m]` marker
|
|
1479
|
+
* when the route carries one.
|
|
1480
|
+
*
|
|
1481
|
+
* Derived, never tabulated -- a family this plugin has never heard of gets its
|
|
1482
|
+
* id the same way, so a model Anthropic ships tomorrow lands in the selector
|
|
1483
|
+
* without an edit here, and a version bump (`claude-fable-5-1` ->
|
|
1484
|
+
* `claude-fable-5-2`) leaves an already-persisted selection pointing at the
|
|
1485
|
+
* same row. The family is the first non-numeric segment, which covers both
|
|
1486
|
+
* spellings Anthropic has used (`claude-fable-5-1`, `claude-3-5-sonnet-…`).
|
|
1487
|
+
*
|
|
1488
|
+
* Read off `value` alone, never the id it resolves to: `default` names a route
|
|
1489
|
+
* whose resolution moves with the account and the release, so folding the
|
|
1490
|
+
* resolved `[1m]` in would flip an already-persisted `default` to `default[1m]`
|
|
1491
|
+
* the day Anthropic repoints it.
|
|
1492
|
+
* @param value - the CLI's own id for the row.
|
|
1493
|
+
* @returns the alias to advertise.
|
|
1494
|
+
*/
|
|
1495
|
+
function claudeModelAlias(value) {
|
|
1496
|
+
const wide = WIDE_ROUTE.test(value);
|
|
1497
|
+
const bare = value.replace(WIDE_ROUTE, "").replace(/^claude-/u, "");
|
|
1498
|
+
const family = bare.split("-").find((segment) => !/^\d+$/u.test(segment)) ?? bare;
|
|
1499
|
+
return wide ? `${family}[1m]` : family;
|
|
1450
1500
|
}
|
|
1451
|
-
function projectModel(row) {
|
|
1501
|
+
function projectModel(row, id) {
|
|
1452
1502
|
const contextWindow = declaredContextWindow(row);
|
|
1453
1503
|
return {
|
|
1454
|
-
id
|
|
1504
|
+
id,
|
|
1505
|
+
value: row.value,
|
|
1455
1506
|
name: row.displayName,
|
|
1456
1507
|
description: row.description,
|
|
1457
1508
|
...contextWindow === void 0 ? {} : { contextWindow }
|
|
1458
1509
|
};
|
|
1459
1510
|
}
|
|
1460
1511
|
let latest$1;
|
|
1512
|
+
let inflight;
|
|
1461
1513
|
/**
|
|
1462
1514
|
* Learn the lineup from one session's initialize response.
|
|
1463
1515
|
* @param models - the CLI's own `/model` rows; an empty list is ignored so a
|
|
@@ -1465,20 +1517,96 @@ let latest$1;
|
|
|
1465
1517
|
*/
|
|
1466
1518
|
function recordClaudeModels(models) {
|
|
1467
1519
|
if (models.length === 0) return;
|
|
1468
|
-
|
|
1520
|
+
const taken = /* @__PURE__ */ new Set();
|
|
1521
|
+
latest$1 = models.map((row) => {
|
|
1522
|
+
const alias = claudeModelAlias(row.value);
|
|
1523
|
+
const id = taken.has(alias) ? row.value : alias;
|
|
1524
|
+
taken.add(id);
|
|
1525
|
+
return projectModel(row, id);
|
|
1526
|
+
});
|
|
1469
1527
|
}
|
|
1470
1528
|
/** The lineup to advertise: whatever the CLI last reported, else the seed. */
|
|
1471
1529
|
function latestClaudeModels() {
|
|
1472
1530
|
return latest$1 ?? SEED;
|
|
1473
1531
|
}
|
|
1532
|
+
/** A throwaway probe should not outlive a wedged CLI. */
|
|
1533
|
+
const CLAUDE_MODEL_PROBE_TIMEOUT_MS = 2e4;
|
|
1534
|
+
/**
|
|
1535
|
+
* Read the lineup from a throwaway CLI process.
|
|
1536
|
+
*
|
|
1537
|
+
* Waiting for a session to start is too late: DSH loads the model catalog once
|
|
1538
|
+
* per Host generation, at connect, and does not reload it when this plugin
|
|
1539
|
+
* later learns the real lineup. A selector left on the seed until then hands
|
|
1540
|
+
* out seed ids, which is exactly how a session ends up persisting an id the
|
|
1541
|
+
* next launch cannot resolve. This query carries no tools, no permission
|
|
1542
|
+
* bridge and no session binding: it starts, reports what `/model` would show,
|
|
1543
|
+
* and is killed -- no prompt is ever sent, so it costs no tokens.
|
|
1544
|
+
* @param executablePath - the resolved CLI, or '' to let the SDK find it.
|
|
1545
|
+
* @param factory - test seam for the SDK query.
|
|
1546
|
+
* @returns the CLI's own `/model` rows.
|
|
1547
|
+
*/
|
|
1548
|
+
async function probeClaudeModels(executablePath, factory = query) {
|
|
1549
|
+
const lifetime = new AbortController();
|
|
1550
|
+
const timer = setTimeout(() => lifetime.abort(), CLAUDE_MODEL_PROBE_TIMEOUT_MS);
|
|
1551
|
+
timer.unref?.();
|
|
1552
|
+
const query$2 = factory({
|
|
1553
|
+
prompt: (async function* () {
|
|
1554
|
+
await new Promise(() => {});
|
|
1555
|
+
})(),
|
|
1556
|
+
options: {
|
|
1557
|
+
cwd: process.cwd(),
|
|
1558
|
+
abortController: lifetime,
|
|
1559
|
+
...executablePath.length === 0 ? {} : { pathToClaudeCodeExecutable: executablePath }
|
|
1560
|
+
}
|
|
1561
|
+
});
|
|
1562
|
+
try {
|
|
1563
|
+
(async () => {
|
|
1564
|
+
for await (const _ of query$2);
|
|
1565
|
+
})().catch(() => void 0);
|
|
1566
|
+
return (await Promise.race([query$2.initializationResult(), new Promise((_resolve, reject) => {
|
|
1567
|
+
setTimeout(() => reject(/* @__PURE__ */ new Error("dsh-claude: the model lineup probe did not answer in time")), CLAUDE_MODEL_PROBE_TIMEOUT_MS).unref?.();
|
|
1568
|
+
})])).models;
|
|
1569
|
+
} finally {
|
|
1570
|
+
clearTimeout(timer);
|
|
1571
|
+
lifetime.abort();
|
|
1572
|
+
}
|
|
1573
|
+
}
|
|
1574
|
+
/**
|
|
1575
|
+
* The lineup, learning it from the CLI the first time DSH asks for the catalog.
|
|
1576
|
+
* @param probe - reads the CLI's rows; a failure leaves the seed in place and
|
|
1577
|
+
* is retried on the next catalog load.
|
|
1578
|
+
* @returns the rows to advertise, never rejecting.
|
|
1579
|
+
*/
|
|
1580
|
+
function ensureClaudeModels(probe) {
|
|
1581
|
+
if (latest$1 !== void 0) return Promise.resolve(latest$1);
|
|
1582
|
+
inflight ??= probe().then((models) => {
|
|
1583
|
+
recordClaudeModels(models);
|
|
1584
|
+
}).catch(() => void 0).then(() => {
|
|
1585
|
+
inflight = void 0;
|
|
1586
|
+
return latestClaudeModels();
|
|
1587
|
+
});
|
|
1588
|
+
return inflight;
|
|
1589
|
+
}
|
|
1474
1590
|
/**
|
|
1475
1591
|
* Look one id up in the current lineup.
|
|
1476
|
-
* @param id - the id DSH persisted on the session, which may
|
|
1592
|
+
* @param id - the id DSH persisted on the session, which may be an alias, a
|
|
1593
|
+
* concrete CLI id persisted before this plugin aliased anything, or a row the
|
|
1477
1594
|
* running CLI no longer lists.
|
|
1478
1595
|
* @returns the row, or undefined when the lineup does not cover the id.
|
|
1479
1596
|
*/
|
|
1480
1597
|
function claudeModelRow(id) {
|
|
1481
|
-
|
|
1598
|
+
const rows = latestClaudeModels();
|
|
1599
|
+
return rows.find((row) => row.id === id) ?? rows.find((row) => row.value === id) ?? rows.find((row) => row.id === claudeModelAlias(id));
|
|
1600
|
+
}
|
|
1601
|
+
/**
|
|
1602
|
+
* The spelling to hand the CLI for one selector id.
|
|
1603
|
+
* @param id - the id DSH persisted on the session.
|
|
1604
|
+
* @returns the CLI's own id, or the selector id itself when the lineup does not
|
|
1605
|
+
* cover it -- the seed vocabulary is made of spellings the CLI accepts, and a
|
|
1606
|
+
* session persisted before this plugin aliased anything already holds one.
|
|
1607
|
+
*/
|
|
1608
|
+
function claudeModelValue(id) {
|
|
1609
|
+
return claudeModelRow(id)?.value ?? id;
|
|
1482
1610
|
}
|
|
1483
1611
|
//#endregion
|
|
1484
1612
|
//#region src/plan-usage.ts
|
|
@@ -2478,7 +2606,7 @@ var ClaudeSupervisor = class {
|
|
|
2478
2606
|
resume: binding.claudeSessionId,
|
|
2479
2607
|
...forkAt === void 0 ? {} : { resumeSessionAt: forkAt }
|
|
2480
2608
|
},
|
|
2481
|
-
model,
|
|
2609
|
+
model: claudeModelValue(model),
|
|
2482
2610
|
...thinkingMode === void 0 ? {} : thinkingMode === "off" ? { thinking: { type: "disabled" } } : thinkingMode === "ultracode" ? { settings: { ultracode: true } } : { effort: thinkingMode }
|
|
2483
2611
|
};
|
|
2484
2612
|
entry.query = this.#queryFactory({
|
|
@@ -2876,20 +3004,7 @@ var ClaudeSupervisor = class {
|
|
|
2876
3004
|
...result.usage.outputTokens === void 0 ? {} : { outputTokens: result.usage.outputTokens }
|
|
2877
3005
|
};
|
|
2878
3006
|
}
|
|
2879
|
-
async #completeProgressSegment(active, result
|
|
2880
|
-
if (recordUsage && (result.usage.inputTokens !== void 0 || result.usage.outputTokens !== void 0 || result.usage.cumulativeCostUsd !== void 0)) {
|
|
2881
|
-
await this.#appendSafely(active, {
|
|
2882
|
-
kind: "usage",
|
|
2883
|
-
phase: "completed",
|
|
2884
|
-
title: "Claude usage",
|
|
2885
|
-
summary: usageSummary(result.usage),
|
|
2886
|
-
usage: this.#timedUsage(active, result.usage)
|
|
2887
|
-
});
|
|
2888
|
-
active.output.push({
|
|
2889
|
-
type: "usage",
|
|
2890
|
-
usage: this.#reportedUsage(active, result)
|
|
2891
|
-
});
|
|
2892
|
-
}
|
|
3007
|
+
async #completeProgressSegment(active, result) {
|
|
2893
3008
|
if (!active.sawTextDelta && active.text.length === 0 && result.text !== void 0) {
|
|
2894
3009
|
active.text = result.text;
|
|
2895
3010
|
active.transcriptText = result.text;
|
|
@@ -2909,6 +3024,25 @@ var ClaudeSupervisor = class {
|
|
|
2909
3024
|
this.#closeTranscriptTextSegment(active);
|
|
2910
3025
|
active.thinking = "";
|
|
2911
3026
|
}
|
|
3027
|
+
/** The turn's accounting, recorded once the turn is actually over.
|
|
3028
|
+
*
|
|
3029
|
+
* A turn that hands off to background tasks passes through the same result
|
|
3030
|
+
* handling on its way to `waiting-tasks`, and recording there drew a closing
|
|
3031
|
+
* total under a turn that was still running. */
|
|
3032
|
+
async #recordTurnUsage(active, result) {
|
|
3033
|
+
if (result.usage.inputTokens === void 0 && result.usage.outputTokens === void 0 && result.usage.cumulativeCostUsd === void 0) return;
|
|
3034
|
+
await this.#appendSafely(active, {
|
|
3035
|
+
kind: "usage",
|
|
3036
|
+
phase: "completed",
|
|
3037
|
+
title: "Claude usage",
|
|
3038
|
+
summary: usageSummary(result.usage),
|
|
3039
|
+
usage: this.#timedUsage(active, result.usage)
|
|
3040
|
+
});
|
|
3041
|
+
active.output.push({
|
|
3042
|
+
type: "usage",
|
|
3043
|
+
usage: this.#reportedUsage(active, result)
|
|
3044
|
+
});
|
|
3045
|
+
}
|
|
2912
3046
|
async #completeTurn(entry, active, result) {
|
|
2913
3047
|
if (entry.active !== active) return;
|
|
2914
3048
|
if (active.aborted) {
|
|
@@ -2930,19 +3064,6 @@ var ClaudeSupervisor = class {
|
|
|
2930
3064
|
this.#scheduleLimitReconciliation();
|
|
2931
3065
|
return;
|
|
2932
3066
|
}
|
|
2933
|
-
if (result.usage.inputTokens !== void 0 || result.usage.outputTokens !== void 0 || result.usage.cumulativeCostUsd !== void 0) {
|
|
2934
|
-
await this.#appendSafely(active, {
|
|
2935
|
-
kind: "usage",
|
|
2936
|
-
phase: "completed",
|
|
2937
|
-
title: "Claude usage",
|
|
2938
|
-
summary: usageSummary(result.usage),
|
|
2939
|
-
usage: this.#timedUsage(active, result.usage)
|
|
2940
|
-
});
|
|
2941
|
-
active.output.push({
|
|
2942
|
-
type: "usage",
|
|
2943
|
-
usage: this.#reportedUsage(active, result)
|
|
2944
|
-
});
|
|
2945
|
-
}
|
|
2946
3067
|
const unmatchedDenials = (result.permissionDenials ?? []).filter((denial) => !active.deniedToolUseIds.has(denial.toolUseId));
|
|
2947
3068
|
if (unmatchedDenials.length > 0) await this.#appendSafely(active, {
|
|
2948
3069
|
kind: "permission",
|
|
@@ -2951,6 +3072,7 @@ var ClaudeSupervisor = class {
|
|
|
2951
3072
|
summary: unmatchedDenials.map((denial) => denial.toolName).join(", ")
|
|
2952
3073
|
});
|
|
2953
3074
|
if (!result.success) {
|
|
3075
|
+
await this.#recordTurnUsage(active, result);
|
|
2954
3076
|
if (!active.sawTextDelta && active.text.length === 0 && result.text !== void 0) {
|
|
2955
3077
|
active.text = result.text;
|
|
2956
3078
|
active.transcriptText = result.text;
|
|
@@ -2989,9 +3111,10 @@ var ClaudeSupervisor = class {
|
|
|
2989
3111
|
phase: "updated",
|
|
2990
3112
|
title: "Claude Code is waiting for background tasks"
|
|
2991
3113
|
});
|
|
2992
|
-
await this.#completeProgressSegment(active, result
|
|
3114
|
+
await this.#completeProgressSegment(active, result);
|
|
2993
3115
|
return;
|
|
2994
3116
|
}
|
|
3117
|
+
await this.#recordTurnUsage(active, result);
|
|
2995
3118
|
await this.#appendSafely(active, {
|
|
2996
3119
|
kind: "status",
|
|
2997
3120
|
phase: "completed",
|
|
@@ -3532,7 +3655,10 @@ var ClaudeCodeAdapter = class extends LlmAdapter {
|
|
|
3532
3655
|
* Settings dialog, and the read is dwarfed by the process the turn spawns. */
|
|
3533
3656
|
#renderMode;
|
|
3534
3657
|
#summarizeTitle;
|
|
3535
|
-
|
|
3658
|
+
/** Reads the CLI's own `/model` rows, so the selector never has to advertise
|
|
3659
|
+
* the seed vocabulary once the CLI can answer for itself. */
|
|
3660
|
+
#probeModels;
|
|
3661
|
+
constructor(supervisor, agents, attachments, presetIdFor, drainReviewComments = () => [], renderMode = async () => DEFAULT_CLAUDE_RENDER_MODE, summarizeTitle = (request) => summarizeSessionTitle("", request), probeModels = async () => []) {
|
|
3536
3662
|
super();
|
|
3537
3663
|
this.#supervisor = supervisor;
|
|
3538
3664
|
this.#agents = agents;
|
|
@@ -3541,6 +3667,7 @@ var ClaudeCodeAdapter = class extends LlmAdapter {
|
|
|
3541
3667
|
this.#drainReviewComments = drainReviewComments;
|
|
3542
3668
|
this.#renderMode = renderMode;
|
|
3543
3669
|
this.#summarizeTitle = summarizeTitle;
|
|
3670
|
+
this.#probeModels = probeModels;
|
|
3544
3671
|
}
|
|
3545
3672
|
providerInfo(provider) {
|
|
3546
3673
|
return {
|
|
@@ -3552,7 +3679,7 @@ var ClaudeCodeAdapter = class extends LlmAdapter {
|
|
|
3552
3679
|
return NO_RETRY_POLICY;
|
|
3553
3680
|
}
|
|
3554
3681
|
async listModels(provider) {
|
|
3555
|
-
return
|
|
3682
|
+
return (await ensureClaudeModels(this.#probeModels)).map((model) => ({
|
|
3556
3683
|
provider,
|
|
3557
3684
|
id: model.id,
|
|
3558
3685
|
name: model.name,
|
|
@@ -3753,8 +3880,8 @@ var ClaudeCodeAdapter = class extends LlmAdapter {
|
|
|
3753
3880
|
if (!completed) throw new Error("dsh-claude: Claude turn stream ended without a result");
|
|
3754
3881
|
}
|
|
3755
3882
|
};
|
|
3756
|
-
function createClaudeCodeAdapter(supervisor, agents, attachments, presetIdFor, drainReviewComments = () => [], renderMode = async () => DEFAULT_CLAUDE_RENDER_MODE, summarizeTitle = (request) => summarizeSessionTitle("", request)) {
|
|
3757
|
-
return new ClaudeCodeAdapter(supervisor, agents, attachments, presetIdFor, drainReviewComments, renderMode, summarizeTitle);
|
|
3883
|
+
function createClaudeCodeAdapter(supervisor, agents, attachments, presetIdFor, drainReviewComments = () => [], renderMode = async () => DEFAULT_CLAUDE_RENDER_MODE, summarizeTitle = (request) => summarizeSessionTitle("", request), probeModels = async () => []) {
|
|
3884
|
+
return new ClaudeCodeAdapter(supervisor, agents, attachments, presetIdFor, drainReviewComments, renderMode, summarizeTitle, probeModels);
|
|
3758
3885
|
}
|
|
3759
3886
|
//#endregion
|
|
3760
3887
|
//#region src/plugin-budget.ts
|
|
@@ -4319,6 +4446,82 @@ function registerClaudeProjectionRoute(ctx, sidecar, ownsSession, commandsForSes
|
|
|
4319
4446
|
});
|
|
4320
4447
|
}
|
|
4321
4448
|
//#endregion
|
|
4449
|
+
//#region src/diff-funcname.ts
|
|
4450
|
+
/** Extensions mapped onto a funcname driver, so `@@` hunk headers name the
|
|
4451
|
+
* method a change sits in. Without one git falls back to "the last line that
|
|
4452
|
+
* starts in column 0", which in Java or Kotlin is always the class. */
|
|
4453
|
+
const DIFF_ATTRIBUTES = [
|
|
4454
|
+
"*.java diff=java",
|
|
4455
|
+
"*.kt diff=kotlin",
|
|
4456
|
+
"*.kts diff=kotlin",
|
|
4457
|
+
"*.py diff=python",
|
|
4458
|
+
"*.pyi diff=python",
|
|
4459
|
+
"*.js diff=dshweb",
|
|
4460
|
+
"*.jsx diff=dshweb",
|
|
4461
|
+
"*.mjs diff=dshweb",
|
|
4462
|
+
"*.cjs diff=dshweb",
|
|
4463
|
+
"*.ts diff=dshweb",
|
|
4464
|
+
"*.tsx diff=dshweb",
|
|
4465
|
+
"*.mts diff=dshweb",
|
|
4466
|
+
"*.cts diff=dshweb",
|
|
4467
|
+
"*.vue diff=dshweb",
|
|
4468
|
+
"*.svelte diff=dshweb",
|
|
4469
|
+
"*.css diff=css",
|
|
4470
|
+
"*.scss diff=css",
|
|
4471
|
+
"*.less diff=css",
|
|
4472
|
+
""
|
|
4473
|
+
].join("\n");
|
|
4474
|
+
/** git ships no JavaScript driver, so this is the one pattern we write ourselves.
|
|
4475
|
+
*
|
|
4476
|
+
* POSIX extended regexes, one per line, matched top-down: a leading `!` marks a
|
|
4477
|
+
* line that can never be a header, and the reported text is capture group 1 --
|
|
4478
|
+
* hence the outer parentheses around everything worth showing.
|
|
4479
|
+
*
|
|
4480
|
+
* Line 2 keeps git's own fallback (anything unindented), because a driver
|
|
4481
|
+
* replaces that fallback rather than extending it, and most of a frontend file's
|
|
4482
|
+
* declarations already live in column 0. Lines 3 and 4 add what the fallback
|
|
4483
|
+
* cannot see: nested declarations, and indented class or object methods.
|
|
4484
|
+
*
|
|
4485
|
+
* ponytail: a method line must end in `{`. Allowing `)` too would pick up every
|
|
4486
|
+
* bare `foo(bar)` statement, which reads as a header and hides the real one.
|
|
4487
|
+
*/
|
|
4488
|
+
const DSHWEB_FUNCNAME = [
|
|
4489
|
+
"!^[ ]*(if|else|for|while|do|switch|case|catch|try|finally|return|await|new|throw|typeof)[^A-Za-z0-9_$]",
|
|
4490
|
+
"^([A-Za-z_$].*)$",
|
|
4491
|
+
"^[ ]*((export[ ]+)?(default[ ]+)?(declare[ ]+)?(abstract[ ]+)?(async[ ]+)?(function|class|interface|enum|namespace|module)[ ].*)$",
|
|
4492
|
+
"^[ ]*(((public|private|protected|static|readonly|abstract|async|get|set)[ ]+)*[A-Za-z_$#][A-Za-z0-9_$]*[ ]*[:=]?[ ]*(async[ ]+)?[(<][^;]*\\{)[ ]*$"
|
|
4493
|
+
].join("\n");
|
|
4494
|
+
let attributesFile;
|
|
4495
|
+
async function writeAttributes() {
|
|
4496
|
+
const path = dshHomePath("plugins", "dsh-claude", "diff-attributes");
|
|
4497
|
+
try {
|
|
4498
|
+
await mkdir(dirname(path), { recursive: true });
|
|
4499
|
+
await writeFile(path, DIFF_ATTRIBUTES, "utf8");
|
|
4500
|
+
return path;
|
|
4501
|
+
} catch {
|
|
4502
|
+
return;
|
|
4503
|
+
}
|
|
4504
|
+
}
|
|
4505
|
+
/** `-c` overrides to place in front of a `git diff`, teaching it which funcname
|
|
4506
|
+
* driver each extension uses.
|
|
4507
|
+
*
|
|
4508
|
+
* `core.attributesFile` is the lowest-precedence attribute source, so a
|
|
4509
|
+
* repository that already declares its own `.gitattributes` still wins. Better
|
|
4510
|
+
* hunk headers are cosmetic: a failed write drops the overrides and the diff
|
|
4511
|
+
* runs exactly as before.
|
|
4512
|
+
*/
|
|
4513
|
+
async function diffFuncnameArgs() {
|
|
4514
|
+
attributesFile ??= writeAttributes();
|
|
4515
|
+
const path = await attributesFile;
|
|
4516
|
+
if (path === void 0) return [];
|
|
4517
|
+
return [
|
|
4518
|
+
"-c",
|
|
4519
|
+
`core.attributesFile=${path}`,
|
|
4520
|
+
"-c",
|
|
4521
|
+
`diff.dshweb.xfuncname=${DSHWEB_FUNCNAME}`
|
|
4522
|
+
];
|
|
4523
|
+
}
|
|
4524
|
+
//#endregion
|
|
4322
4525
|
//#region src/repository-status.ts
|
|
4323
4526
|
const MAX_OUTPUT_BYTES$4 = 65536;
|
|
4324
4527
|
const MAX_DIFF_BYTES = 262144;
|
|
@@ -4328,6 +4531,7 @@ const GIT_TIMEOUT_MS$3 = 5e3;
|
|
|
4328
4531
|
const GH_TIMEOUT_MS$1 = 8e3;
|
|
4329
4532
|
const CACHE_TTL_MS = 5e3;
|
|
4330
4533
|
const MAX_TEXT_CHARS = 1024;
|
|
4534
|
+
const MAX_CONFLICT_PATHS = 100;
|
|
4331
4535
|
function bounded(value) {
|
|
4332
4536
|
return value.trim().slice(0, MAX_TEXT_CHARS);
|
|
4333
4537
|
}
|
|
@@ -4365,6 +4569,7 @@ function parseGitStatus(output) {
|
|
|
4365
4569
|
let upstream = false;
|
|
4366
4570
|
let ahead;
|
|
4367
4571
|
let behind;
|
|
4572
|
+
const conflicts = [];
|
|
4368
4573
|
for (const line of output.split(/\r?\n/u)) {
|
|
4369
4574
|
if (line.startsWith("# branch.head ")) {
|
|
4370
4575
|
const head = bounded(line.slice(14));
|
|
@@ -4384,6 +4589,10 @@ function parseGitStatus(output) {
|
|
|
4384
4589
|
}
|
|
4385
4590
|
continue;
|
|
4386
4591
|
}
|
|
4592
|
+
if (line.startsWith("u ")) {
|
|
4593
|
+
const path = line.split(" ").slice(10).join(" ");
|
|
4594
|
+
if (path.length > 0 && conflicts.length < MAX_CONFLICT_PATHS) conflicts.push(bounded(path));
|
|
4595
|
+
}
|
|
4387
4596
|
if (line.length > 0 && !line.startsWith("# ")) dirty = true;
|
|
4388
4597
|
}
|
|
4389
4598
|
return {
|
|
@@ -4392,9 +4601,36 @@ function parseGitStatus(output) {
|
|
|
4392
4601
|
dirty,
|
|
4393
4602
|
upstream,
|
|
4394
4603
|
...ahead === void 0 ? {} : { ahead },
|
|
4395
|
-
...behind === void 0 ? {} : { behind }
|
|
4604
|
+
...behind === void 0 ? {} : { behind },
|
|
4605
|
+
...conflicts.length === 0 ? {} : { conflicts }
|
|
4396
4606
|
};
|
|
4397
4607
|
}
|
|
4608
|
+
/** Ordered so the rebase directories win: a conflicted rebase also writes
|
|
4609
|
+
* MERGE_HEAD-like state, and the two are resumed by different commands. */
|
|
4610
|
+
const OPERATION_MARKERS = [
|
|
4611
|
+
["rebase-merge", "rebase"],
|
|
4612
|
+
["rebase-apply", "rebase"],
|
|
4613
|
+
["MERGE_HEAD", "merge"],
|
|
4614
|
+
["CHERRY_PICK_HEAD", "cherry-pick"],
|
|
4615
|
+
["REVERT_HEAD", "revert"]
|
|
4616
|
+
];
|
|
4617
|
+
/** Reads the in-progress operation out of the worktree's own git dir -- linked
|
|
4618
|
+
* worktrees keep their own, so this must not be handed the common dir. */
|
|
4619
|
+
async function detectRepositoryOperation(gitDir) {
|
|
4620
|
+
for (const [marker, operation] of OPERATION_MARKERS) {
|
|
4621
|
+
const path = join(gitDir, marker);
|
|
4622
|
+
try {
|
|
4623
|
+
await stat(path);
|
|
4624
|
+
} catch {
|
|
4625
|
+
continue;
|
|
4626
|
+
}
|
|
4627
|
+
const branch = bounded(operation === "rebase" ? await readFile(join(path, "head-name"), "utf8").catch(() => "") : "").replace(/^refs\/heads\//u, "");
|
|
4628
|
+
return {
|
|
4629
|
+
operation,
|
|
4630
|
+
...branch.length === 0 || branch.includes(" ") ? {} : { branch }
|
|
4631
|
+
};
|
|
4632
|
+
}
|
|
4633
|
+
}
|
|
4398
4634
|
function parseDiffNumstat(value) {
|
|
4399
4635
|
let additions = 0;
|
|
4400
4636
|
let deletions = 0;
|
|
@@ -4601,13 +4837,15 @@ var RepositoryStatusService = class {
|
|
|
4601
4837
|
cwd: safeCwd
|
|
4602
4838
|
};
|
|
4603
4839
|
const status = parseGitStatus(statusResult.stdout);
|
|
4840
|
+
const operation = await detectRepositoryOperation(gitDir);
|
|
4841
|
+
const branch = operation?.branch ?? status.branch;
|
|
4604
4842
|
const remoteResult = await run(this.#runtime, git, [
|
|
4605
4843
|
"remote",
|
|
4606
4844
|
"get-url",
|
|
4607
4845
|
"origin"
|
|
4608
4846
|
], cwd, GIT_TIMEOUT_MS$3);
|
|
4609
4847
|
const remote = remoteResult.exitCode === 0 ? parseGitHubRemote(remoteResult.stdout) : void 0;
|
|
4610
|
-
const pullRequest =
|
|
4848
|
+
const pullRequest = branch === void 0 || remote === void 0 ? void 0 : await this.#pullRequest(cwd, remote, branch);
|
|
4611
4849
|
const diffBase = pullRequest?.baseBranch === void 0 ? "HEAD" : await this.#mergeBase(cwd, git, pullRequest.baseBranch) ?? "HEAD";
|
|
4612
4850
|
const diff = status.dirty || diffBase !== "HEAD" ? await this.#diff(cwd, git, diffBase, signal) : {
|
|
4613
4851
|
additions: 0,
|
|
@@ -4621,6 +4859,8 @@ var RepositoryStatusService = class {
|
|
|
4621
4859
|
cwd: safeCwd,
|
|
4622
4860
|
root,
|
|
4623
4861
|
...status,
|
|
4862
|
+
...branch === void 0 ? {} : { branch },
|
|
4863
|
+
...operation === void 0 ? {} : { operation: operation.operation },
|
|
4624
4864
|
worktree: normalizedPath(gitDir) !== normalizedPath(commonDir),
|
|
4625
4865
|
...remote === void 0 ? {} : { remote },
|
|
4626
4866
|
...pullRequest === void 0 ? {} : { pullRequest },
|
|
@@ -4675,6 +4915,7 @@ var RepositoryStatusService = class {
|
|
|
4675
4915
|
if (numstat.exitCode !== 0 || numstat.lossy) return void 0;
|
|
4676
4916
|
const summary = parseDiffNumstat(numstat.stdout);
|
|
4677
4917
|
const patch = await run(this.#runtime, git, [
|
|
4918
|
+
...await diffFuncnameArgs(),
|
|
4678
4919
|
"diff",
|
|
4679
4920
|
"--no-ext-diff",
|
|
4680
4921
|
"--no-color",
|
|
@@ -5496,6 +5737,10 @@ function parseRepositoryActionStatus(output) {
|
|
|
5496
5737
|
}
|
|
5497
5738
|
return [...files.values()].sort((left, right) => left.path.localeCompare(right.path));
|
|
5498
5739
|
}
|
|
5740
|
+
function conflictPaths(result) {
|
|
5741
|
+
if (result.exitCode !== 0 || result.lossy) return [];
|
|
5742
|
+
return result.stdout.split(/\r?\n/u).filter((line) => line.length > 0).slice(0, 100);
|
|
5743
|
+
}
|
|
5499
5744
|
function fallbackCommitMessage(files) {
|
|
5500
5745
|
if (files.length === 1) return `Update ${files[0]?.path ?? "repository files"}`;
|
|
5501
5746
|
return `Update ${files.length} repository files`;
|
|
@@ -5560,6 +5805,7 @@ var RepositoryActionService = class {
|
|
|
5560
5805
|
return operation;
|
|
5561
5806
|
}
|
|
5562
5807
|
async #execute(cwd, request) {
|
|
5808
|
+
if (request.action === "resolve-continue" || request.action === "resolve-abort") return this.#resolve(cwd, request.action, request.push === true);
|
|
5563
5809
|
const before = await this.#preview(cwd);
|
|
5564
5810
|
if (before.fingerprint !== request.fingerprint) throw new RepositoryActionError("repository-changed", "Repository changes have changed. Refresh the commit panel.");
|
|
5565
5811
|
if (request.action === "push") {
|
|
@@ -5622,13 +5868,12 @@ var RepositoryActionService = class {
|
|
|
5622
5868
|
`origin/${base}`
|
|
5623
5869
|
], before.root, REMOTE_TIMEOUT_MS);
|
|
5624
5870
|
if (merged.exitCode !== 0 || merged.lossy) {
|
|
5625
|
-
const
|
|
5871
|
+
const conflicts = conflictPaths(await this.#run(git, [
|
|
5626
5872
|
"diff",
|
|
5627
5873
|
"--name-only",
|
|
5628
5874
|
"--diff-filter=U",
|
|
5629
5875
|
"--"
|
|
5630
|
-
], before.root, GIT_TIMEOUT_MS$1);
|
|
5631
|
-
const conflicts = conflicted.exitCode === 0 && !conflicted.lossy ? conflicted.stdout.split(/\r?\n/u).filter((line) => line.length > 0).slice(0, 100) : [];
|
|
5876
|
+
], before.root, GIT_TIMEOUT_MS$1));
|
|
5632
5877
|
if (conflicts.length === 0) {
|
|
5633
5878
|
await this.#run(git, [method, "--abort"], before.root, GIT_TIMEOUT_MS$1).catch(() => void 0);
|
|
5634
5879
|
throw new RepositoryActionError("merge-failed", `Git could not ${method} the base branch.`);
|
|
@@ -5727,6 +5972,87 @@ var RepositoryActionService = class {
|
|
|
5727
5972
|
pullRequestUrl: url
|
|
5728
5973
|
};
|
|
5729
5974
|
}
|
|
5975
|
+
/** Finishes or discards the merge, rebase, cherry-pick or revert git is
|
|
5976
|
+
* waiting on. The operation is read from the git dir rather than taken from
|
|
5977
|
+
* the caller: `--continue` and `--abort` are only safe against the one that
|
|
5978
|
+
* is actually in progress. */
|
|
5979
|
+
async #resolve(cwd, action, push) {
|
|
5980
|
+
const git = await this.#git();
|
|
5981
|
+
const [rootValue, gitDirValue] = (await this.#mustRun(git, [
|
|
5982
|
+
"rev-parse",
|
|
5983
|
+
"--path-format=absolute",
|
|
5984
|
+
"--show-toplevel",
|
|
5985
|
+
"--absolute-git-dir"
|
|
5986
|
+
], cwd, GIT_TIMEOUT_MS$1, "not-repository", "The session directory is not a Git repository.")).stdout.split(/\r?\n/u);
|
|
5987
|
+
const root = (rootValue ?? "").trim();
|
|
5988
|
+
const gitDir = (gitDirValue ?? "").trim();
|
|
5989
|
+
if (root.length === 0 || gitDir.length === 0) throw new RepositoryActionError("repository-unavailable", "Repository state is unavailable.");
|
|
5990
|
+
const state = await detectRepositoryOperation(gitDir);
|
|
5991
|
+
if (state === void 0) throw new RepositoryActionError("no-operation", "No merge, rebase, cherry-pick or revert is in progress.");
|
|
5992
|
+
const operation = state.operation;
|
|
5993
|
+
if (action === "resolve-abort") {
|
|
5994
|
+
await this.#mustRun(git, [operation, "--abort"], root, GIT_TIMEOUT_MS$1, "abort-failed", `Git could not abort the ${operation}.`);
|
|
5995
|
+
this.#invalidate(root);
|
|
5996
|
+
return {
|
|
5997
|
+
commit: await this.#head(git, root),
|
|
5998
|
+
pushed: false
|
|
5999
|
+
};
|
|
6000
|
+
}
|
|
6001
|
+
if (conflictPaths(await this.#run(git, [
|
|
6002
|
+
"diff",
|
|
6003
|
+
"--name-only",
|
|
6004
|
+
"--diff-filter=U",
|
|
6005
|
+
"--"
|
|
6006
|
+
], root, GIT_TIMEOUT_MS$1)).length > 0) throw new RepositoryActionError("unresolved-conflicts", "Resolve and stage every conflicted file before continuing.");
|
|
6007
|
+
const continued = await this.#run(git, [
|
|
6008
|
+
"-c",
|
|
6009
|
+
"core.editor=true",
|
|
6010
|
+
operation,
|
|
6011
|
+
"--continue"
|
|
6012
|
+
], root, REMOTE_TIMEOUT_MS);
|
|
6013
|
+
this.#invalidate(root);
|
|
6014
|
+
if (continued.exitCode !== 0 || continued.lossy) {
|
|
6015
|
+
const next = conflictPaths(await this.#run(git, [
|
|
6016
|
+
"diff",
|
|
6017
|
+
"--name-only",
|
|
6018
|
+
"--diff-filter=U",
|
|
6019
|
+
"--"
|
|
6020
|
+
], root, GIT_TIMEOUT_MS$1));
|
|
6021
|
+
if (next.length > 0) return {
|
|
6022
|
+
commit: await this.#head(git, root),
|
|
6023
|
+
pushed: false,
|
|
6024
|
+
conflicts: next
|
|
6025
|
+
};
|
|
6026
|
+
const reason = continued.stderr.split(/\r?\n/u).map((line) => line.trim()).filter((line) => line.length > 0).at(-1);
|
|
6027
|
+
throw new RepositoryActionError("continue-failed", reason === void 0 || reason.length === 0 ? `Git could not continue the ${operation}.` : reason);
|
|
6028
|
+
}
|
|
6029
|
+
const head = await this.#head(git, root);
|
|
6030
|
+
if (!push) return {
|
|
6031
|
+
commit: head,
|
|
6032
|
+
pushed: false
|
|
6033
|
+
};
|
|
6034
|
+
const branch = await this.#run(git, [
|
|
6035
|
+
"symbolic-ref",
|
|
6036
|
+
"--quiet",
|
|
6037
|
+
"--short",
|
|
6038
|
+
"HEAD"
|
|
6039
|
+
], root, GIT_TIMEOUT_MS$1);
|
|
6040
|
+
if (branch.exitCode !== 0 || branch.lossy) throw new RepositoryActionError("detached-head", "The finished operation left a detached HEAD, so nothing was pushed.", head);
|
|
6041
|
+
try {
|
|
6042
|
+
await this.#push(git, root, branch.stdout.trim(), operation === "rebase");
|
|
6043
|
+
} catch (error) {
|
|
6044
|
+
throw new RepositoryActionError("push-failed", error instanceof Error ? error.message : "Git push failed.", head);
|
|
6045
|
+
}
|
|
6046
|
+
this.#invalidate(root);
|
|
6047
|
+
return {
|
|
6048
|
+
commit: head,
|
|
6049
|
+
pushed: true
|
|
6050
|
+
};
|
|
6051
|
+
}
|
|
6052
|
+
async #head(git, root) {
|
|
6053
|
+
const head = await this.#run(git, ["rev-parse", "HEAD"], root, GIT_TIMEOUT_MS$1);
|
|
6054
|
+
return head.exitCode === 0 && !head.lossy ? head.stdout.trim() : "";
|
|
6055
|
+
}
|
|
5730
6056
|
async #preview(cwd) {
|
|
5731
6057
|
const git = await this.#git();
|
|
5732
6058
|
const root = (await this.#mustRun(git, [
|
|
@@ -5734,6 +6060,7 @@ var RepositoryActionService = class {
|
|
|
5734
6060
|
"--path-format=absolute",
|
|
5735
6061
|
"--show-toplevel"
|
|
5736
6062
|
], cwd, GIT_TIMEOUT_MS$1, "not-repository", "The session directory is not a Git repository.")).stdout.trim();
|
|
6063
|
+
const funcname = await diffFuncnameArgs();
|
|
5737
6064
|
const [branchResult, headResult, statusResult, stagedPatch, unstagedPatch] = await Promise.all([
|
|
5738
6065
|
this.#run(git, [
|
|
5739
6066
|
"symbolic-ref",
|
|
@@ -5749,6 +6076,7 @@ var RepositoryActionService = class {
|
|
|
5749
6076
|
"--untracked-files=all"
|
|
5750
6077
|
], root, GIT_TIMEOUT_MS$1),
|
|
5751
6078
|
this.#run(git, [
|
|
6079
|
+
...funcname,
|
|
5752
6080
|
"diff",
|
|
5753
6081
|
"--cached",
|
|
5754
6082
|
"--no-ext-diff",
|
|
@@ -5759,6 +6087,7 @@ var RepositoryActionService = class {
|
|
|
5759
6087
|
":(exclude)**/WARP.md"
|
|
5760
6088
|
], root, GIT_TIMEOUT_MS$1, MAX_OUTPUT_BYTES$2),
|
|
5761
6089
|
this.#run(git, [
|
|
6090
|
+
...funcname,
|
|
5762
6091
|
"diff",
|
|
5763
6092
|
"--no-ext-diff",
|
|
5764
6093
|
"--no-color",
|
|
@@ -6012,7 +6341,17 @@ const ACTIONS = /* @__PURE__ */ new Set([
|
|
|
6012
6341
|
"push",
|
|
6013
6342
|
"create-pr",
|
|
6014
6343
|
"merge-pr",
|
|
6015
|
-
"update-branch"
|
|
6344
|
+
"update-branch",
|
|
6345
|
+
"resolve-continue",
|
|
6346
|
+
"resolve-abort"
|
|
6347
|
+
]);
|
|
6348
|
+
/** Actions that commit nothing of their own, so the panel sends no message. */
|
|
6349
|
+
const MESSAGELESS = /* @__PURE__ */ new Set([
|
|
6350
|
+
"push",
|
|
6351
|
+
"merge-pr",
|
|
6352
|
+
"update-branch",
|
|
6353
|
+
"resolve-continue",
|
|
6354
|
+
"resolve-abort"
|
|
6016
6355
|
]);
|
|
6017
6356
|
function record$9(value) {
|
|
6018
6357
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
@@ -6051,7 +6390,7 @@ function actionRequest(input) {
|
|
|
6051
6390
|
return {
|
|
6052
6391
|
action,
|
|
6053
6392
|
fingerprint: string$1(input, "fingerprint"),
|
|
6054
|
-
message: action
|
|
6393
|
+
message: MESSAGELESS.has(action) ? optionalString(input, "message") ?? "" : string$1(input, "message"),
|
|
6055
6394
|
includeUnstaged: input.includeUnstaged,
|
|
6056
6395
|
...optionalString(input, "prTitle") === void 0 ? {} : { prTitle: optionalString(input, "prTitle") },
|
|
6057
6396
|
...optionalString(input, "prBody") === void 0 ? {} : { prBody: optionalString(input, "prBody") },
|
|
@@ -6059,6 +6398,9 @@ function actionRequest(input) {
|
|
|
6059
6398
|
...input.draft === void 0 ? {} : typeof input.draft === "boolean" ? { draft: input.draft } : (() => {
|
|
6060
6399
|
throw new RepositoryActionError("invalid-request", "The draft field must be a boolean.");
|
|
6061
6400
|
})(),
|
|
6401
|
+
...input.push === void 0 ? {} : typeof input.push === "boolean" ? { push: input.push } : (() => {
|
|
6402
|
+
throw new RepositoryActionError("invalid-request", "The push field must be a boolean.");
|
|
6403
|
+
})(),
|
|
6062
6404
|
...input.mergeMethod === void 0 ? {} : input.mergeMethod === "merge" || input.mergeMethod === "squash" || input.mergeMethod === "rebase" ? { mergeMethod: input.mergeMethod } : (() => {
|
|
6063
6405
|
throw new RepositoryActionError("invalid-request", "The mergeMethod field is invalid.");
|
|
6064
6406
|
})()
|
|
@@ -9102,7 +9444,7 @@ async function apply(ctx, config) {
|
|
|
9102
9444
|
let resolutionError;
|
|
9103
9445
|
try {
|
|
9104
9446
|
supervisorConfig.executablePath = (await resolveClaudeExecutable(ctx.subprocess, config.executablePath === void 0 || config.executablePath.length === 0 ? void 0 : config.executablePath)).path;
|
|
9105
|
-
ctx.llm.registerAdapter([...CLAUDE_CODE_PROVIDER_IDS], createClaudeCodeAdapter(supervisor, ctx.agents, ctx.attachments, (agent) => ctx.agentPresets.composedPreset(agent.ctx), (sessionId) => reviewComments.drain(sessionId), () => readRenderMode(), (request) => summarizeSessionTitle(supervisorConfig.executablePath, request)));
|
|
9447
|
+
ctx.llm.registerAdapter([...CLAUDE_CODE_PROVIDER_IDS], createClaudeCodeAdapter(supervisor, ctx.agents, ctx.attachments, (agent) => ctx.agentPresets.composedPreset(agent.ctx), (sessionId) => reviewComments.drain(sessionId), () => readRenderMode(), (request) => summarizeSessionTitle(supervisorConfig.executablePath, request), () => probeClaudeModels(supervisorConfig.executablePath)));
|
|
9106
9448
|
ctx.effect(() => {
|
|
9107
9449
|
const mounted = /* @__PURE__ */ new Map();
|
|
9108
9450
|
const pending = /* @__PURE__ */ new Set();
|
|
@@ -9231,7 +9573,7 @@ async function apply(ctx, config) {
|
|
|
9231
9573
|
registerAskRoute(webCtx, new AskService(webCtx.subprocess, supervisorConfig.executablePath), cwdForClaudeSession, (sessionId) => {
|
|
9232
9574
|
const snapshot = supervisor.snapshots().find((item) => item.sessionId === sessionId);
|
|
9233
9575
|
return snapshot === void 0 ? void 0 : {
|
|
9234
|
-
model: snapshot.model,
|
|
9576
|
+
model: claudeModelValue(snapshot.model),
|
|
9235
9577
|
...snapshot.thinkingMode === void 0 ? {} : { thinkingMode: snapshot.thinkingMode }
|
|
9236
9578
|
};
|
|
9237
9579
|
});
|