@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/index.d.mts CHANGED
@@ -3,28 +3,33 @@ import { EffortLevel, Options, Query, SDKControlGetContextUsageResponse, SDKUser
3
3
  import { SubprocessRuntime } from "@deepseek-ai/dsh-subprocess";
4
4
  import { Context } from "@deepseek-ai/cordis";
5
5
  import { Agent } from "@deepseek-ai/dsh-agent";
6
- import { CommandDefinition, CommandDescriptor } from "@deepseek-ai/dsh-commands";
6
+ import { CommandDescriptor } from "@deepseek-ai/dsh-commands";
7
7
  import { SessionEvent } from "@deepseek-ai/dsh-session";
8
8
  import { ApprovalService } from "@deepseek-ai/dsh-user-approval";
9
9
  import { UserQuestionService } from "@deepseek-ai/dsh-user-questions";
10
10
  //#region src/command-bridge.d.ts
11
11
  /**
12
- * Agent-scope command service contract. The commands service is unreachable
12
+ * Agent-scope command-directory contract. The commands service is unreachable
13
13
  * from the host plugin's view of `agent.ctx` ("without inject"), but the
14
- * preset route plugin runs INSIDE the agent's preset composition with
15
- * `commands` injected. Registrations made through the provided service land
16
- * in the agent's scope layer, which the command registry inherits into
17
- * `list(agent)` for exactly that agent — invisible to every other session.
14
+ * preset route plugin runs inside the agent's preset composition with
15
+ * `commands` injected. The Host reads only the exact agent's effective names
16
+ * for collision checks; Claude catalog entries are never registered there.
18
17
  */
19
18
  interface ClaudeAgentCommandService {
20
19
  list(agent: unknown): readonly Pick<CommandDescriptor, 'name'>[];
21
- register(definition: CommandDefinition): () => void;
22
20
  }
23
21
  declare module '@deepseek-ai/cordis' {
24
22
  interface Context {
25
23
  claudeCommands?: ClaudeAgentCommandService;
26
24
  }
27
25
  }
26
+ interface ClaudeCommandView {
27
+ publicName: string;
28
+ claudeName: string;
29
+ description: string;
30
+ hint?: string;
31
+ prefixed: boolean;
32
+ }
28
33
  //#endregion
29
34
  //#region src/events.d.ts
30
35
  type ClaudeActivityKind = 'status' | 'thinking' | 'tool-call' | 'tool-result' | 'permission' | 'question' | 'subagent' | 'usage' | 'warning' | 'error';
@@ -232,7 +237,7 @@ interface Config {
232
237
  maxProcesses?: number;
233
238
  }
234
239
  declare const Config: z<Config>;
235
- declare function mountClaudeMetadata(ctx: Context, supervisor: ClaudeSupervisor, agent: Agent, model: string, sidecar: ClaudeSidecarRepository, resolveCommands?: () => ClaudeAgentCommandService | undefined): (() => Promise<void>) | undefined;
240
+ declare function mountClaudeMetadata(ctx: Context, supervisor: ClaudeSupervisor, agent: Agent, model: string, sidecar: ClaudeSidecarRepository, publishCommands?: (commands: readonly ClaudeCommandView[]) => void, resolveCommands?: () => ClaudeAgentCommandService | undefined): (() => Promise<void>) | undefined;
236
241
  declare function installManagedPresetCompatibility(logger: Pick<Context['logger'], 'warn'>, install?: typeof ensureManagedPreset): Promise<'installed' | 'unchanged' | 'conflict'>;
237
242
  declare function apply(ctx: Context, config: Config): Promise<void>;
238
243
  //#endregion
package/lib/index.mjs CHANGED
@@ -1,14 +1,14 @@
1
1
  import { _ as CLAUDE_PROJECTION_PATH, a as latestClaudeTasks, c as normalizeTasksEvent, d as CLAUDE_ACTIVITY_EVENT, f as CLAUDE_CODE_PRESET_ID, g as CLAUDE_GLOBAL_SETTINGS_PATH, h as CLAUDE_DOCTOR_PATH, i as latestClaudeSessionBinding, l as redactText, m as CLAUDE_CODE_PROVIDER_IDS, n as currentClaudeActivityCursor, o as normalizeActivity, p as CLAUDE_CODE_PROVIDER, r as latestClaudeContextUsage, s as normalizeContextUsage, t as boundText, u as safeDetail, v as CLAUDE_UPDATE_CHECK_PATH, x as TASK_TOOL_NAMES, y as CLAUDE_UPDATE_PATH } from "./events-6xTApIQw.mjs";
2
- import { a as ClaudeCommandBridge, i as CLAUDE_COMMANDS_SERVICE, r as dynamicPresenterDefinition, t as CLAUDE_PRESENTER_NAMES } from "./presenters-C8Yx_rNY.mjs";
2
+ import { a as projectClaudeCommands, i as CLAUDE_COMMANDS_SERVICE, r as dynamicPresenterDefinition, t as CLAUDE_PRESENTER_NAMES } from "./presenters-CtgkpzZo.mjs";
3
3
  import { a as resolveClaudeExecutable, n as ensureManagedPreset, o as runClaudeDoctor, t as ManagedPresetConflictError } from "./preset-installer-B0NrA4Hi.mjs";
4
4
  import z from "@deepseek-ai/schemastery";
5
- import { CallId, LlmAdapter, ReasoningEffortId, createToolResultMessage, createUserMessage } from "@deepseek-ai/dsh-llm";
6
5
  import { randomUUID } from "node:crypto";
7
6
  import { chmod, mkdir, opendir, readFile, realpath, rename, rm, writeFile } from "node:fs/promises";
8
7
  import { dirname, extname, join, resolve } from "node:path";
9
8
  import { dshHomePath, resolveDshHome } from "@deepseek-ai/dsh-home-paths";
10
9
  import { homedir } from "node:os";
11
10
  import { query } from "@anthropic-ai/claude-agent-sdk";
11
+ import { CallId, LlmAdapter, ReasoningEffortId, createToolResultMessage } from "@deepseek-ai/dsh-llm";
12
12
  import { EventEmitter } from "node:events";
13
13
  import { DSH_ENV_PREFIX, SENSITIVE_ENV_PATTERN } from "@deepseek-ai/dsh-subprocess";
14
14
  import { fileURLToPath } from "node:url";
@@ -1345,6 +1345,7 @@ var ClaudeSupervisor = class {
1345
1345
  }
1346
1346
  async #handleMessage(entry, message) {
1347
1347
  if (message.kind === "init") {
1348
+ const firstInitialization = !entry.initialized;
1348
1349
  if (entry.expectedResume !== void 0 && message.sessionId !== entry.expectedResume) throw new ClaudeProtocolError(`Claude Code resumed unexpected session ${message.sessionId}; expected ${entry.expectedResume}`);
1349
1350
  if (message.cwd !== entry.cwd) throw new ClaudeProtocolError(`Claude Code initialized in unexpected cwd ${message.cwd}; expected ${entry.cwd}`);
1350
1351
  if (entry.initTimer !== void 0) {
@@ -1355,7 +1356,7 @@ var ClaudeSupervisor = class {
1355
1356
  entry.claudeSessionId = message.sessionId;
1356
1357
  entry.state = entry.active === void 0 ? "idle" : "running";
1357
1358
  for (const waiter of entry.initWaiters.splice(0)) waiter(void 0);
1358
- if (entry.tasks.size > 0) {
1359
+ if (firstInitialization && entry.tasks.size > 0) {
1359
1360
  entry.tasks.clear();
1360
1361
  await this.#flushTasksSnapshot(entry);
1361
1362
  }
@@ -2258,7 +2259,7 @@ function sessionIdFromUrl(rawUrl) {
2258
2259
  }
2259
2260
  }
2260
2261
  /** Register the browser-readable, credential-free sidecar projection endpoint. */
2261
- function registerClaudeProjectionRoute(ctx, sidecar, ownsSession) {
2262
+ function registerClaudeProjectionRoute(ctx, sidecar, ownsSession, commandsForSession = () => []) {
2262
2263
  ctx.effect(() => ctx.webServer.register({
2263
2264
  kind: "prefix",
2264
2265
  path: CLAUDE_PROJECTION_PATH,
@@ -2273,6 +2274,7 @@ function registerClaudeProjectionRoute(ctx, sidecar, ownsSession) {
2273
2274
  schemaVersion: projection.schemaVersion,
2274
2275
  revision: projection.revision,
2275
2276
  owned: ownsSession(sessionId),
2277
+ commands: commandsForSession(sessionId),
2276
2278
  activities: projection.activities,
2277
2279
  ...projection.contextUsage === void 0 ? {} : { contextUsage: projection.contextUsage },
2278
2280
  ...projection.tasks === void 0 ? {} : { tasks: projection.tasks }
@@ -2504,14 +2506,15 @@ async function updatePlugin(deps = {}) {
2504
2506
  throw new Error(`DSH plugin update failed (${outcome.exitCode ?? outcome.signal ?? "unknown exit"}): ${safeMessage(detail)}`);
2505
2507
  }
2506
2508
  await verifyInstalledVersion(installation, latest);
2509
+ if (deps.requestRestart !== void 0) setTimeout(() => deps.requestRestart?.(), 100).unref?.();
2507
2510
  return {
2508
2511
  currentVersion: latest,
2509
2512
  latestVersion: latest,
2510
2513
  source: "registry",
2511
2514
  state: "current",
2512
2515
  canUpdate: false,
2513
- restartRequired: true,
2514
- message: "Update installed; restart DSH Desktop to load it"
2516
+ restartRequired: deps.requestRestart === void 0,
2517
+ message: deps.requestRestart === void 0 ? "Update installed; restart DSH Desktop to load it" : "Update installed; DSH Desktop is restarting to load it"
2515
2518
  };
2516
2519
  }
2517
2520
  function registerClaudeUpdateRoutes(ctx, runtime, deps = {}) {
@@ -2754,7 +2757,7 @@ const CATALOG_RETRY_MS = 5e3;
2754
2757
  const SCOPE_RETRY_MS = 500;
2755
2758
  const MAX_CATALOG_RETRIES = 3;
2756
2759
  const MAX_SCOPE_RETRIES = 24;
2757
- function mountClaudeMetadata(ctx, supervisor, agent, model, sidecar, resolveCommands = () => ctx.agentPresets.serviceFor(agent, CLAUDE_COMMANDS_SERVICE)) {
2760
+ function mountClaudeMetadata(ctx, supervisor, agent, model, sidecar, publishCommands = () => {}, resolveCommands = () => ctx.agentPresets.serviceFor(agent, CLAUDE_COMMANDS_SERVICE)) {
2758
2761
  if (ctx.agentPresets.composedPreset(agent.ctx) !== "claude") return void 0;
2759
2762
  let stopped = false;
2760
2763
  let pending = Promise.resolve();
@@ -2765,19 +2768,7 @@ function mountClaudeMetadata(ctx, supervisor, agent, model, sidecar, resolveComm
2765
2768
  commandScope = scoped;
2766
2769
  return scoped;
2767
2770
  };
2768
- const bridge = new ClaudeCommandBridge({
2769
- list: () => scopedCommands().list(agent),
2770
- register: (definition) => scopedCommands().register(definition),
2771
- forward: (line) => {
2772
- agent.followup(createUserMessage({
2773
- content: [{
2774
- type: "text",
2775
- text: line
2776
- }],
2777
- source: { kind: "user" }
2778
- }));
2779
- }
2780
- });
2771
+ const commandTarget = { list: () => scopedCommands().list(agent) };
2781
2772
  const warn = (area, error) => {
2782
2773
  ctx.logger.warn(`dsh-claude: ${area} refresh failed for ${String(agent.id)}: ${error instanceof Error ? error.message : String(error)}`);
2783
2774
  };
@@ -2819,7 +2810,9 @@ function mountClaudeMetadata(ctx, supervisor, agent, model, sidecar, resolveComm
2819
2810
  }
2820
2811
  if (stopped || catalog === void 0) return;
2821
2812
  try {
2822
- diagnostic.registered = bridge.refresh(catalog).map((view) => view.publicName);
2813
+ const commands = projectClaudeCommands(catalog, commandTarget);
2814
+ publishCommands(commands);
2815
+ diagnostic.registered = commands.map((view) => view.publicName);
2823
2816
  if (!stopped) scopeRetries = 0;
2824
2817
  } catch (error) {
2825
2818
  diagnostic.lastError = error instanceof Error ? error.message : String(error);
@@ -2845,10 +2838,10 @@ function mountClaudeMetadata(ctx, supervisor, agent, model, sidecar, resolveComm
2845
2838
  refresh();
2846
2839
  return async () => {
2847
2840
  stopped = true;
2841
+ publishCommands([]);
2848
2842
  if (retryTimer !== void 0) clearTimeout(retryTimer);
2849
2843
  stopStatus();
2850
2844
  await pending;
2851
- bridge.dispose();
2852
2845
  };
2853
2846
  }, "dsh-claude: agent metadata bridge");
2854
2847
  }
@@ -2870,6 +2863,7 @@ async function apply(ctx, config) {
2870
2863
  maxProcesses: config.maxProcesses ?? 4
2871
2864
  };
2872
2865
  const sidecar = new ClaudeSidecarRepository();
2866
+ const commandCatalogs = /* @__PURE__ */ new Map();
2873
2867
  const supervisor = new ClaudeSupervisor({
2874
2868
  runtime: ctx.subprocess,
2875
2869
  approval: ctx.approval,
@@ -2889,7 +2883,11 @@ async function apply(ctx, config) {
2889
2883
  const MOUNT_RETRY_LIMIT = 50;
2890
2884
  const mount = (agent) => {
2891
2885
  if (mounted.has(agent)) return;
2892
- const dispose = mountClaudeMetadata(ctx, supervisor, agent, supervisorConfig.defaultModel, sidecar);
2886
+ const sessionId = agent.id;
2887
+ const dispose = mountClaudeMetadata(ctx, supervisor, agent, supervisorConfig.defaultModel, sidecar, (commands) => {
2888
+ if (commands.length === 0) commandCatalogs.delete(sessionId);
2889
+ else commandCatalogs.set(sessionId, commands);
2890
+ });
2893
2891
  if (dispose !== void 0) mounted.set(agent, dispose);
2894
2892
  pending.delete(agent);
2895
2893
  };
@@ -2943,12 +2941,13 @@ async function apply(ctx, config) {
2943
2941
  ctx.effect(() => () => supervisor.dispose(), "dsh-claude: process supervisor");
2944
2942
  ctx.inject(["webServer"], (webCtx) => {
2945
2943
  registerClaudeDoctorRoutes(webCtx, webCtx.subprocess, supervisor, supervisorConfig, resolutionError);
2946
- registerClaudeUpdateRoutes(webCtx, webCtx.subprocess);
2944
+ const desktopActions = webCtx.get("desktopActions");
2945
+ registerClaudeUpdateRoutes(webCtx, webCtx.subprocess, { ...typeof desktopActions?.requestRestart === "function" ? { requestRestart: desktopActions.requestRestart.bind(desktopActions) } : {} });
2947
2946
  registerClaudeGlobalSettingsRoute(webCtx);
2948
2947
  registerClaudeProjectionRoute(webCtx, sidecar, (sessionId) => {
2949
2948
  const agent = webCtx.agents.get(sessionId);
2950
2949
  return agent !== void 0 && webCtx.agentPresets.composedPreset(agent.ctx) === "claude";
2951
- });
2950
+ }, (sessionId) => commandCatalogs.get(sessionId) ?? []);
2952
2951
  });
2953
2952
  }
2954
2953
  //#endregion