@norman-else/dsh-claude 0.1.10 → 0.1.12
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.js +289 -15
- package/lib/client.js.map +1 -1
- package/lib/index.d.mts +13 -8
- package/lib/index.mjs +24 -25
- package/lib/index.mjs.map +1 -1
- package/lib/{presenters-C8Yx_rNY.mjs → presenters-CtgkpzZo.mjs} +10 -57
- package/lib/presenters-CtgkpzZo.mjs.map +1 -0
- package/lib/preset-route.mjs +2 -5
- package/lib/preset-route.mjs.map +1 -1
- package/package.json +4 -1
- package/lib/presenters-C8Yx_rNY.mjs.map +0 -1
package/lib/client.js
CHANGED
|
@@ -3,7 +3,7 @@ window.__ModuleLoader__.load({
|
|
|
3
3
|
factory: (require) => {
|
|
4
4
|
var module = { exports: {} };
|
|
5
5
|
module.exports;
|
|
6
|
-
var { useCallback, useEffect, useMemo, useState } = require("react");
|
|
6
|
+
var { useCallback, useEffect, useId, useMemo, useRef, useState } = require("react");
|
|
7
7
|
var { Fragment, jsx, jsxs } = require("react/jsx-runtime");
|
|
8
8
|
var { DisclosureRow, IconApiOutline14, IconThinkOutline14, StateDot } = require("@deepseek-ai/dsh-client-ui-primitives");
|
|
9
9
|
/** Claude's subagent dispatch tools; rendered as plugin-owned group cards
|
|
@@ -310,6 +310,91 @@ window.__ModuleLoader__.load({
|
|
|
310
310
|
fontWeight: 500,
|
|
311
311
|
overflowWrap: "anywhere"
|
|
312
312
|
};
|
|
313
|
+
const settingSelect = {
|
|
314
|
+
position: "relative",
|
|
315
|
+
minWidth: 0
|
|
316
|
+
};
|
|
317
|
+
const settingSelectTrigger = {
|
|
318
|
+
width: "100%",
|
|
319
|
+
minHeight: 38,
|
|
320
|
+
display: "flex",
|
|
321
|
+
alignItems: "center",
|
|
322
|
+
justifyContent: "space-between",
|
|
323
|
+
gap: 12,
|
|
324
|
+
padding: "7px 11px 7px 13px",
|
|
325
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
326
|
+
borderRadius: 10,
|
|
327
|
+
background: "var(--dsw-alias-bg-layer-2)",
|
|
328
|
+
color: "var(--dsw-alias-label-primary)",
|
|
329
|
+
boxShadow: "0 1px 2px color-mix(in srgb, var(--dsw-alias-label-primary) 5%, transparent)",
|
|
330
|
+
font: "inherit",
|
|
331
|
+
fontSize: 13,
|
|
332
|
+
lineHeight: "20px",
|
|
333
|
+
textAlign: "left",
|
|
334
|
+
cursor: "pointer",
|
|
335
|
+
transition: "border-color 120ms ease, box-shadow 120ms ease, background 120ms ease"
|
|
336
|
+
};
|
|
337
|
+
const settingSelectTriggerOpen = {
|
|
338
|
+
borderColor: "var(--dsw-static-blue-450)",
|
|
339
|
+
boxShadow: "0 0 0 3px color-mix(in srgb, var(--dsw-static-blue-450) 15%, transparent)"
|
|
340
|
+
};
|
|
341
|
+
const settingSelectValue = {
|
|
342
|
+
minWidth: 0,
|
|
343
|
+
flex: 1,
|
|
344
|
+
overflow: "hidden",
|
|
345
|
+
textOverflow: "ellipsis",
|
|
346
|
+
whiteSpace: "nowrap",
|
|
347
|
+
fontWeight: 550
|
|
348
|
+
};
|
|
349
|
+
const settingSelectChevron = {
|
|
350
|
+
flex: "none",
|
|
351
|
+
color: "var(--dsw-alias-label-tertiary)",
|
|
352
|
+
fontSize: 16,
|
|
353
|
+
lineHeight: 1,
|
|
354
|
+
transform: "translateY(-1px)",
|
|
355
|
+
transition: "transform 120ms ease"
|
|
356
|
+
};
|
|
357
|
+
const settingSelectChevronOpen = { transform: "translateY(1px) rotate(180deg)" };
|
|
358
|
+
const settingSelectMenu = {
|
|
359
|
+
position: "absolute",
|
|
360
|
+
zIndex: 20,
|
|
361
|
+
top: "calc(100% + 6px)",
|
|
362
|
+
left: 0,
|
|
363
|
+
right: 0,
|
|
364
|
+
maxHeight: 240,
|
|
365
|
+
overflowY: "auto",
|
|
366
|
+
padding: 5,
|
|
367
|
+
border: "1px solid var(--dsw-alias-border-l2)",
|
|
368
|
+
borderRadius: 11,
|
|
369
|
+
background: "var(--dsw-alias-bg-layer-1)",
|
|
370
|
+
boxShadow: "0 12px 32px color-mix(in srgb, var(--dsw-alias-label-primary) 16%, transparent), 0 2px 8px color-mix(in srgb, var(--dsw-alias-label-primary) 8%, transparent)"
|
|
371
|
+
};
|
|
372
|
+
const settingSelectOption = {
|
|
373
|
+
width: "100%",
|
|
374
|
+
minHeight: 34,
|
|
375
|
+
display: "flex",
|
|
376
|
+
alignItems: "center",
|
|
377
|
+
gap: 8,
|
|
378
|
+
padding: "6px 9px",
|
|
379
|
+
border: "none",
|
|
380
|
+
borderRadius: 7,
|
|
381
|
+
background: "transparent",
|
|
382
|
+
color: "var(--dsw-alias-label-primary)",
|
|
383
|
+
font: "inherit",
|
|
384
|
+
fontSize: 13,
|
|
385
|
+
lineHeight: "20px",
|
|
386
|
+
textAlign: "left",
|
|
387
|
+
cursor: "pointer"
|
|
388
|
+
};
|
|
389
|
+
const settingSelectOptionActive = { background: "var(--dsw-alias-bg-layer-2)" };
|
|
390
|
+
const settingSelectCheck = {
|
|
391
|
+
width: 14,
|
|
392
|
+
flex: "none",
|
|
393
|
+
color: "var(--dsw-static-blue-450)",
|
|
394
|
+
fontSize: 12,
|
|
395
|
+
fontWeight: 700,
|
|
396
|
+
textAlign: "center"
|
|
397
|
+
};
|
|
313
398
|
const settingsActions = {
|
|
314
399
|
display: "flex",
|
|
315
400
|
flexWrap: "wrap",
|
|
@@ -1033,6 +1118,120 @@ window.__ModuleLoader__.load({
|
|
|
1033
1118
|
function value(status, detail) {
|
|
1034
1119
|
return detail === void 0 ? status : `${status} · ${detail}`;
|
|
1035
1120
|
}
|
|
1121
|
+
function GlobalSettingSelect({ setting, disabled, onChange }) {
|
|
1122
|
+
const [open, setOpen] = useState(false);
|
|
1123
|
+
const [activeIndex, setActiveIndex] = useState(0);
|
|
1124
|
+
const rootRef = useRef(null);
|
|
1125
|
+
const triggerRef = useRef(null);
|
|
1126
|
+
const listboxId = useId();
|
|
1127
|
+
const selectedIndex = Math.max(0, setting.options.findIndex((option) => option.value === setting.value));
|
|
1128
|
+
const selectedOption = setting.options[selectedIndex];
|
|
1129
|
+
useEffect(() => {
|
|
1130
|
+
if (!open) return;
|
|
1131
|
+
const closeOnOutsidePointer = (event) => {
|
|
1132
|
+
if (!rootRef.current?.contains(event.target)) setOpen(false);
|
|
1133
|
+
};
|
|
1134
|
+
document.addEventListener("mousedown", closeOnOutsidePointer);
|
|
1135
|
+
return () => document.removeEventListener("mousedown", closeOnOutsidePointer);
|
|
1136
|
+
}, [open]);
|
|
1137
|
+
const openMenu = (index = selectedIndex) => {
|
|
1138
|
+
setActiveIndex(index);
|
|
1139
|
+
setOpen(true);
|
|
1140
|
+
};
|
|
1141
|
+
const choose = (index) => {
|
|
1142
|
+
const option = setting.options[index];
|
|
1143
|
+
if (option === void 0) return;
|
|
1144
|
+
setOpen(false);
|
|
1145
|
+
triggerRef.current?.focus();
|
|
1146
|
+
if (option.value !== setting.value) onChange(option.value);
|
|
1147
|
+
};
|
|
1148
|
+
const move = (offset) => {
|
|
1149
|
+
const count = setting.options.length;
|
|
1150
|
+
if (count === 0) return;
|
|
1151
|
+
setActiveIndex((current) => (current + offset + count) % count);
|
|
1152
|
+
};
|
|
1153
|
+
return /* @__PURE__ */ jsxs("div", {
|
|
1154
|
+
ref: rootRef,
|
|
1155
|
+
style: settingSelect,
|
|
1156
|
+
onBlur: (event) => {
|
|
1157
|
+
if (!event.currentTarget.contains(event.relatedTarget)) setOpen(false);
|
|
1158
|
+
},
|
|
1159
|
+
children: [/* @__PURE__ */ jsxs("button", {
|
|
1160
|
+
ref: triggerRef,
|
|
1161
|
+
type: "button",
|
|
1162
|
+
"aria-haspopup": "listbox",
|
|
1163
|
+
"aria-expanded": open,
|
|
1164
|
+
"aria-controls": open ? listboxId : void 0,
|
|
1165
|
+
"aria-activedescendant": open ? `${listboxId}-${activeIndex}` : void 0,
|
|
1166
|
+
disabled: disabled || setting.options.length === 0,
|
|
1167
|
+
style: {
|
|
1168
|
+
...settingSelectTrigger,
|
|
1169
|
+
...open ? settingSelectTriggerOpen : {}
|
|
1170
|
+
},
|
|
1171
|
+
onClick: () => {
|
|
1172
|
+
if (open) setOpen(false);
|
|
1173
|
+
else openMenu();
|
|
1174
|
+
},
|
|
1175
|
+
onKeyDown: (event) => {
|
|
1176
|
+
if (event.key === "ArrowDown" || event.key === "ArrowUp") {
|
|
1177
|
+
event.preventDefault();
|
|
1178
|
+
if (!open) openMenu(event.key === "ArrowDown" ? selectedIndex : Math.max(0, setting.options.length - 1));
|
|
1179
|
+
else move(event.key === "ArrowDown" ? 1 : -1);
|
|
1180
|
+
} else if (event.key === "Home" && open) {
|
|
1181
|
+
event.preventDefault();
|
|
1182
|
+
setActiveIndex(0);
|
|
1183
|
+
} else if (event.key === "End" && open) {
|
|
1184
|
+
event.preventDefault();
|
|
1185
|
+
setActiveIndex(Math.max(0, setting.options.length - 1));
|
|
1186
|
+
} else if ((event.key === "Enter" || event.key === " ") && open) {
|
|
1187
|
+
event.preventDefault();
|
|
1188
|
+
choose(activeIndex);
|
|
1189
|
+
} else if (event.key === "Escape" && open) {
|
|
1190
|
+
event.preventDefault();
|
|
1191
|
+
setOpen(false);
|
|
1192
|
+
}
|
|
1193
|
+
},
|
|
1194
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
1195
|
+
style: settingSelectValue,
|
|
1196
|
+
children: selectedOption?.label ?? setting.value
|
|
1197
|
+
}), /* @__PURE__ */ jsx("span", {
|
|
1198
|
+
"aria-hidden": "true",
|
|
1199
|
+
style: {
|
|
1200
|
+
...settingSelectChevron,
|
|
1201
|
+
...open ? settingSelectChevronOpen : {}
|
|
1202
|
+
},
|
|
1203
|
+
children: "⌄"
|
|
1204
|
+
})]
|
|
1205
|
+
}), open ? /* @__PURE__ */ jsx("div", {
|
|
1206
|
+
id: listboxId,
|
|
1207
|
+
role: "listbox",
|
|
1208
|
+
"aria-activedescendant": `${listboxId}-${activeIndex}`,
|
|
1209
|
+
style: settingSelectMenu,
|
|
1210
|
+
children: setting.options.map((option, index) => {
|
|
1211
|
+
const selected = option.value === setting.value;
|
|
1212
|
+
const active = index === activeIndex;
|
|
1213
|
+
return /* @__PURE__ */ jsxs("button", {
|
|
1214
|
+
id: `${listboxId}-${index}`,
|
|
1215
|
+
type: "button",
|
|
1216
|
+
role: "option",
|
|
1217
|
+
"aria-selected": selected,
|
|
1218
|
+
style: {
|
|
1219
|
+
...settingSelectOption,
|
|
1220
|
+
...active ? settingSelectOptionActive : {}
|
|
1221
|
+
},
|
|
1222
|
+
onMouseEnter: () => setActiveIndex(index),
|
|
1223
|
+
onMouseDown: (event) => event.preventDefault(),
|
|
1224
|
+
onClick: () => choose(index),
|
|
1225
|
+
children: [/* @__PURE__ */ jsx("span", {
|
|
1226
|
+
style: settingSelectCheck,
|
|
1227
|
+
"aria-hidden": "true",
|
|
1228
|
+
children: selected ? "✓" : ""
|
|
1229
|
+
}), /* @__PURE__ */ jsx("span", { children: option.label })]
|
|
1230
|
+
}, `${option.source}:${option.value}`);
|
|
1231
|
+
})
|
|
1232
|
+
}) : null]
|
|
1233
|
+
});
|
|
1234
|
+
}
|
|
1036
1235
|
function ClaudeCodeSettings({ t }) {
|
|
1037
1236
|
const [report, setReport] = useState();
|
|
1038
1237
|
const [error, setError] = useState();
|
|
@@ -1207,22 +1406,17 @@ window.__ModuleLoader__.load({
|
|
|
1207
1406
|
globalSettings === void 0 ? /* @__PURE__ */ jsx("p", {
|
|
1208
1407
|
style: notice,
|
|
1209
1408
|
children: t("globalSettingsLoading")
|
|
1210
|
-
}) : globalSettings.settings.map((setting) => /* @__PURE__ */ jsxs("
|
|
1409
|
+
}) : globalSettings.settings.map((setting) => /* @__PURE__ */ jsxs("div", {
|
|
1211
1410
|
style: diagnosticGrid,
|
|
1212
1411
|
children: [/* @__PURE__ */ jsx("span", {
|
|
1213
1412
|
style: diagnosticLabel,
|
|
1214
1413
|
children: setting.key === "outputStyle" ? t("outputStyle") : setting.key
|
|
1215
|
-
}), /* @__PURE__ */ jsx(
|
|
1216
|
-
|
|
1414
|
+
}), /* @__PURE__ */ jsx(GlobalSettingSelect, {
|
|
1415
|
+
setting,
|
|
1217
1416
|
disabled: globalSettingsBusy,
|
|
1218
|
-
onChange: (
|
|
1219
|
-
requestGlobalSettings({ [setting.key]:
|
|
1220
|
-
}
|
|
1221
|
-
style: diagnosticValue,
|
|
1222
|
-
children: setting.options.map((option) => /* @__PURE__ */ jsx("option", {
|
|
1223
|
-
value: option.value,
|
|
1224
|
-
children: option.label
|
|
1225
|
-
}, `${option.source}:${option.value}`))
|
|
1417
|
+
onChange: (nextValue) => {
|
|
1418
|
+
requestGlobalSettings({ [setting.key]: nextValue });
|
|
1419
|
+
}
|
|
1226
1420
|
})]
|
|
1227
1421
|
}, setting.key)),
|
|
1228
1422
|
globalSettings?.settings.some((setting) => setting.effect === "new-session") === true ? /* @__PURE__ */ jsx("p", {
|
|
@@ -1353,10 +1547,12 @@ window.__ModuleLoader__.load({
|
|
|
1353
1547
|
schemaVersion: 1,
|
|
1354
1548
|
revision: 0,
|
|
1355
1549
|
owned: false,
|
|
1550
|
+
commands: [],
|
|
1356
1551
|
activities: []
|
|
1357
1552
|
};
|
|
1358
1553
|
const POLL_INTERVAL_MS = 2e3;
|
|
1359
1554
|
const MAX_ACTIVITIES = 1e4;
|
|
1555
|
+
const MAX_COMMANDS = 2e3;
|
|
1360
1556
|
function record(value) {
|
|
1361
1557
|
return value !== null && typeof value === "object" && !Array.isArray(value) ? value : void 0;
|
|
1362
1558
|
}
|
|
@@ -1366,7 +1562,11 @@ window.__ModuleLoader__.load({
|
|
|
1366
1562
|
/** Validate the public route envelope before publishing it to UI components. */
|
|
1367
1563
|
function parseClaudeClientProjection(value) {
|
|
1368
1564
|
const input = record(value);
|
|
1369
|
-
if (input === void 0 || input.schemaVersion !== 1 || !nonNegativeInteger(input.revision) || typeof input.owned !== "boolean" || !Array.isArray(input.activities) || input.activities.length > MAX_ACTIVITIES) throw new Error("invalid Claude sidecar projection");
|
|
1565
|
+
if (input === void 0 || input.schemaVersion !== 1 || !nonNegativeInteger(input.revision) || typeof input.owned !== "boolean" || !Array.isArray(input.commands) || input.commands.length > MAX_COMMANDS || !Array.isArray(input.activities) || input.activities.length > MAX_ACTIVITIES) throw new Error("invalid Claude sidecar projection");
|
|
1566
|
+
for (const item of input.commands) {
|
|
1567
|
+
const command = record(item);
|
|
1568
|
+
if (command === void 0 || typeof command.publicName !== "string" || typeof command.claudeName !== "string" || typeof command.description !== "string" || command.hint !== void 0 && typeof command.hint !== "string" || typeof command.prefixed !== "boolean") throw new Error("invalid Claude command projection");
|
|
1569
|
+
}
|
|
1370
1570
|
for (const item of input.activities) {
|
|
1371
1571
|
const activity = record(item);
|
|
1372
1572
|
if (activity === void 0 || !nonNegativeInteger(activity.turn) || !nonNegativeInteger(activity.step) || !nonNegativeInteger(activity.ordinal) || typeof activity.kind !== "string") throw new Error("invalid Claude sidecar activity");
|
|
@@ -1400,7 +1600,8 @@ window.__ModuleLoader__.load({
|
|
|
1400
1600
|
});
|
|
1401
1601
|
if (!response.ok) throw new Error(`Claude projection request failed (${response.status})`);
|
|
1402
1602
|
const next = parseClaudeClientProjection(await response.json());
|
|
1403
|
-
|
|
1603
|
+
const commandCatalogChanged = JSON.stringify(next.commands) !== JSON.stringify(snapshot.commands);
|
|
1604
|
+
if (next.revision !== snapshot.revision || next.owned !== snapshot.owned || commandCatalogChanged) {
|
|
1404
1605
|
snapshot = next;
|
|
1405
1606
|
for (const listener of [...listeners]) listener();
|
|
1406
1607
|
}
|
|
@@ -1456,6 +1657,76 @@ window.__ModuleLoader__.load({
|
|
|
1456
1657
|
}
|
|
1457
1658
|
};
|
|
1458
1659
|
//#endregion
|
|
1660
|
+
//#region src/client/claude-command-source.ts
|
|
1661
|
+
const SOURCE_NAME = "Claude Code";
|
|
1662
|
+
function commandsFor(store, session) {
|
|
1663
|
+
const projection = store.source(session.sessionId).getSnapshot();
|
|
1664
|
+
return projection.owned ? projection.commands : [];
|
|
1665
|
+
}
|
|
1666
|
+
function findCommand(store, session, publicName) {
|
|
1667
|
+
return commandsFor(store, session).find((command) => command.publicName === publicName);
|
|
1668
|
+
}
|
|
1669
|
+
function claimFor(command) {
|
|
1670
|
+
return {
|
|
1671
|
+
token: `/${command.publicName} `,
|
|
1672
|
+
...command.hint === void 0 ? {} : { hint: command.hint },
|
|
1673
|
+
async submit(args, actx) {
|
|
1674
|
+
const line = `/${command.claudeName}${args.length === 0 ? "" : ` ${args}`}`;
|
|
1675
|
+
try {
|
|
1676
|
+
await actx.conversation.send(line);
|
|
1677
|
+
return { kind: "success" };
|
|
1678
|
+
} catch (error) {
|
|
1679
|
+
return {
|
|
1680
|
+
kind: "error",
|
|
1681
|
+
text: error instanceof Error ? error.message : String(error)
|
|
1682
|
+
};
|
|
1683
|
+
}
|
|
1684
|
+
}
|
|
1685
|
+
};
|
|
1686
|
+
}
|
|
1687
|
+
function pickCommand(store, pick) {
|
|
1688
|
+
const command = findCommand(store, pick.session, pick.candidate.name);
|
|
1689
|
+
if (command === void 0) return void 0;
|
|
1690
|
+
return { claim: claimFor(command) };
|
|
1691
|
+
}
|
|
1692
|
+
/** Build a client-owned slash source. It never calls command.execute, so
|
|
1693
|
+
* Claude Skill submission creates only the ordinary user-message turn. */
|
|
1694
|
+
function createClaudeCommandSource(store) {
|
|
1695
|
+
return {
|
|
1696
|
+
trigger: "/",
|
|
1697
|
+
name: SOURCE_NAME,
|
|
1698
|
+
order: 10,
|
|
1699
|
+
async candidates(session, request) {
|
|
1700
|
+
if (request.position !== "leading") return [];
|
|
1701
|
+
return commandsFor(store, session).map((command) => ({
|
|
1702
|
+
name: command.publicName,
|
|
1703
|
+
description: command.description,
|
|
1704
|
+
...command.hint === void 0 ? {} : { hint: command.hint }
|
|
1705
|
+
}));
|
|
1706
|
+
},
|
|
1707
|
+
onPick(pick) {
|
|
1708
|
+
return pickCommand(store, pick);
|
|
1709
|
+
},
|
|
1710
|
+
matchSpace(session, token) {
|
|
1711
|
+
if (!token.startsWith("/")) return void 0;
|
|
1712
|
+
const command = findCommand(store, session, token.slice(1));
|
|
1713
|
+
return command === void 0 ? void 0 : { claim: claimFor(command) };
|
|
1714
|
+
},
|
|
1715
|
+
async matchEnter(session, line) {
|
|
1716
|
+
if (!line.startsWith("/")) return void 0;
|
|
1717
|
+
const separator = line.indexOf(" ");
|
|
1718
|
+
const command = findCommand(store, session, line.slice(1, separator === -1 ? void 0 : separator));
|
|
1719
|
+
return command === void 0 ? void 0 : { claim: claimFor(command) };
|
|
1720
|
+
},
|
|
1721
|
+
lexicon(session) {
|
|
1722
|
+
return commandsFor(store, session).map((command) => command.publicName);
|
|
1723
|
+
},
|
|
1724
|
+
subscribeLexicon(session, listener) {
|
|
1725
|
+
return store.source(session.sessionId).subscribe(listener);
|
|
1726
|
+
}
|
|
1727
|
+
};
|
|
1728
|
+
}
|
|
1729
|
+
//#endregion
|
|
1459
1730
|
//#region src/client/locales.ts
|
|
1460
1731
|
const zh = {
|
|
1461
1732
|
nav: "Claude Code",
|
|
@@ -1632,7 +1903,9 @@ window.__ModuleLoader__.load({
|
|
|
1632
1903
|
"slots",
|
|
1633
1904
|
"locale",
|
|
1634
1905
|
"conversationEvents",
|
|
1635
|
-
"sessions"
|
|
1906
|
+
"sessions",
|
|
1907
|
+
"inputTriggers",
|
|
1908
|
+
"conversation"
|
|
1636
1909
|
];
|
|
1637
1910
|
function apply(ctx) {
|
|
1638
1911
|
const namespace = "settings.claude-code";
|
|
@@ -1642,6 +1915,7 @@ window.__ModuleLoader__.load({
|
|
|
1642
1915
|
}), "dsh-claude: client copy");
|
|
1643
1916
|
const t = ctx.locale.bind(namespace);
|
|
1644
1917
|
const projections = new ClaudeProjectionStore();
|
|
1918
|
+
ctx.effect(() => ctx.inputTriggers.registerSource(createClaudeCommandSource(projections)), "dsh-claude: Claude slash source");
|
|
1645
1919
|
const sessions = ctx.get("sessions");
|
|
1646
1920
|
if (sessions !== void 0) ctx.effect(() => sessions.provide({
|
|
1647
1921
|
hooks: ["claudeProjection"],
|