@norman-else/dsh-claude 0.1.35 → 0.1.37
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/INSTALL.md +55 -55
- package/LICENSE +21 -21
- package/README.md +196 -195
- package/cordis.patch.yml +12 -12
- package/legacy-preset/agent.cordis.yml +11 -11
- package/legacy-preset/preset.yml +4 -4
- package/lib/bin.mjs +1 -1
- package/lib/bin.mjs.map +1 -1
- package/lib/client.d.ts +13 -0
- package/lib/client.js +1176 -810
- package/lib/client.js.map +1 -1
- package/lib/{events-Doid-tq7.mjs → events-OhBoFNKO.mjs} +7 -2
- package/lib/events-OhBoFNKO.mjs.map +1 -0
- package/lib/index.d.mts +5 -0
- package/lib/index.mjs +256 -48
- package/lib/index.mjs.map +1 -1
- package/lib/{presenters-CXtE8qve.mjs → presenters-BBoM1Ju1.mjs} +2 -2
- package/lib/presenters-BBoM1Ju1.mjs.map +1 -0
- package/lib/{preset-installer-BMyKr5eQ.mjs → preset-installer-loenwnLS.mjs} +2 -2
- package/lib/preset-installer-loenwnLS.mjs.map +1 -0
- package/lib/preset-route.mjs +2 -2
- package/lib/preset-route.mjs.map +1 -1
- package/package.json +195 -189
- package/preset/claude/agent.cordis.yml +11 -11
- package/preset/claude/preset.yml +4 -4
- package/lib/events-Doid-tq7.mjs.map +0 -1
- package/lib/presenters-CXtE8qve.mjs.map +0 -1
- package/lib/preset-installer-BMyKr5eQ.mjs.map +0 -1
|
@@ -29,6 +29,11 @@ const DEFAULT_CLAUDE_RENDER_MODE = "plugin";
|
|
|
29
29
|
function isClaudeRenderMode(value) {
|
|
30
30
|
return value === "plugin" || value === "native";
|
|
31
31
|
}
|
|
32
|
+
const CLAUDE_PROSE_MODES = ["plain", "enhanced"];
|
|
33
|
+
const DEFAULT_CLAUDE_PROSE_MODE = "plain";
|
|
34
|
+
function isClaudeProseMode(value) {
|
|
35
|
+
return value === "plain" || value === "enhanced";
|
|
36
|
+
}
|
|
32
37
|
//#endregion
|
|
33
38
|
//#region src/events.ts
|
|
34
39
|
const SECRET_KEY = /(?:^|[_-])(password|passwd|secret|token|api[_-]?key|authorization|credential|private[_-]?key|session[_-]?key|env|environ|environment)(?:$|[_-])/i;
|
|
@@ -199,6 +204,6 @@ function latestClaudeSessionBinding(events) {
|
|
|
199
204
|
}
|
|
200
205
|
}
|
|
201
206
|
//#endregion
|
|
202
|
-
export {
|
|
207
|
+
export { CLAUDE_REWIND_PATH as A, CLAUDE_RENDER_MODES as C, CLAUDE_REPOSITORY_SETUP_PATH as D, CLAUDE_REPOSITORY_FILE_PATH as E, DEFAULT_CLAUDE_RENDER_MODE as F, SDK_VERSION as I, TASK_TOOL_NAMES as L, CLAUDE_UPDATE_PATH as M, CLAUDE_USAGE_PATH as N, CLAUDE_REPOSITORY_STATUS_PATH as O, DEFAULT_CLAUDE_PROSE_MODE as P, isClaudeProseMode as R, CLAUDE_PROSE_MODES as S, CLAUDE_REPOSITORY_FEEDBACK_PATH as T, CLAUDE_DOCTOR_PATH as _, latestClaudeTasks as a, CLAUDE_JIRA_PATH as b, normalizeTasksEvent as c, CLAUDE_ACTIVITY_EVENT as d, CLAUDE_ASK_PATH as f, CLAUDE_CODE_PROVIDER_IDS as g, CLAUDE_CODE_PROVIDER as h, latestClaudeSessionBinding as i, CLAUDE_UPDATE_CHECK_PATH as j, CLAUDE_REVIEW_COMMENT_PATH as k, redactText as l, CLAUDE_CODE_PRESET_ID as m, currentClaudeActivityCursor as n, normalizeActivity as o, CLAUDE_CLIENT_DIAGNOSTICS_PATH as p, latestClaudeContextUsage as r, normalizeContextUsage as s, boundText as t, safeDetail as u, CLAUDE_EDITOR_OPEN_PATH as v, CLAUDE_REPOSITORY_ACTION_PATH as w, CLAUDE_PROJECTION_PATH as x, CLAUDE_GLOBAL_SETTINGS_PATH as y, isClaudeRenderMode as z };
|
|
203
208
|
|
|
204
|
-
//# sourceMappingURL=events-
|
|
209
|
+
//# sourceMappingURL=events-OhBoFNKO.mjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events-OhBoFNKO.mjs","names":[],"sources":["../src/constants.ts","../src/events.ts"],"sourcesContent":["export const CLAUDE_CODE_PROVIDER = 'claude'\nexport const CLAUDE_CODE_PRESET_ID = 'claude'\nexport const CLAUDE_CODE_PROVIDER_IDS = [CLAUDE_CODE_PROVIDER] as const\nexport const CLAUDE_SESSION_BOUND_EVENT = 'claude-code/session-bound'\nexport const CLAUDE_ACTIVITY_EVENT = 'claude-code/activity'\nexport const CLAUDE_CONTEXT_USAGE_EVENT = 'claude-code/context-usage'\nexport const CLAUDE_TASKS_EVENT = 'claude-code/tasks'\n/** Claude's subagent dispatch tools; rendered as plugin-owned group cards\n * gathering subagent activity instead of native tool cards. */\nexport const TASK_TOOL_NAMES: ReadonlySet<string> = new Set(['Task', 'Agent'])\nexport const SDK_VERSION = '0.3.247'\nexport const CLAUDE_DOCTOR_PATH = '/plugins/dsh-claude/doctor'\nexport const CLAUDE_CLIENT_DIAGNOSTICS_PATH = '/plugins/dsh-claude/client-diagnostics'\nexport const CLAUDE_UPDATE_CHECK_PATH = '/plugins/dsh-claude/update/check'\nexport const CLAUDE_USAGE_PATH = '/plugins/dsh-claude/usage'\nexport const CLAUDE_UPDATE_PATH = '/plugins/dsh-claude/update'\nexport const CLAUDE_PROJECTION_PATH = '/plugins/dsh-claude/projection'\nexport const CLAUDE_GLOBAL_SETTINGS_PATH = '/plugins/dsh-claude/settings/global'\nexport const CLAUDE_REPOSITORY_SETUP_PATH = '/plugins/dsh-claude/repository/setup'\nexport const CLAUDE_REPOSITORY_ACTION_PATH = '/plugins/dsh-claude/repository/action'\nexport const CLAUDE_REVIEW_COMMENT_PATH = '/plugins/dsh-claude/review-comments'\nexport const CLAUDE_REPOSITORY_FEEDBACK_PATH = '/plugins/dsh-claude/repository/feedback'\nexport const CLAUDE_REPOSITORY_STATUS_PATH = '/plugins/dsh-claude/repository/status'\nexport const CLAUDE_REPOSITORY_FILE_PATH = '/plugins/dsh-claude/repository/file'\nexport const CLAUDE_JIRA_PATH = '/plugins/dsh-claude/jira'\nexport const CLAUDE_ASK_PATH = '/plugins/dsh-claude/ask'\nexport const CLAUDE_EDITOR_OPEN_PATH = '/plugins/dsh-claude/editor/open'\nexport const CLAUDE_REWIND_PATH = '/plugins/dsh-claude/rewind'\n\n/** Which renderer draws Claude's visible output.\n *\n * 'plugin' keeps the sidecar-backed transcript this package owns (interleaved\n * prose, grouped tool cards, activity rows). 'native' hands the same turn to\n * DSH's own conversation renderer: prose streams as ordinary assistant text\n * blocks, thinking as reasoning blocks, and root Claude tools are mirrored\n * into the durable `tool/call`/`tool/result` channel so the Host's tool\n * presentation pipeline draws them exactly like DSH-executed calls. */\nexport type ClaudeRenderMode = 'plugin' | 'native'\nexport const CLAUDE_RENDER_MODES = ['plugin', 'native'] as const\nexport const DEFAULT_CLAUDE_RENDER_MODE: ClaudeRenderMode = 'plugin'\n\nexport function isClaudeRenderMode(value: unknown): value is ClaudeRenderMode {\n return value === 'plugin' || value === 'native'\n}\n\n/** How this package paints the PROSE of a Claude answer.\n *\n * 'plain' is Claude's own presentation: body text in the Host's text colour,\n * colour reserved for code. 'enhanced' gives headings, emphasis, list markers,\n * quotes and links their own hues and darkens the code surface — the way a\n * Markdown-highlighting editor shows a document rather than the way Claude\n * desktop shows an answer. It is opt-in because it deliberately breaks the\n * parity the rest of `markdown-theme.ts` exists to hold.\n *\n * Only meaningful under {@link ClaudeRenderMode} 'plugin': the stylesheet is\n * scoped to markup this package renders, and 'native' turns are drawn by the\n * Host, where it has no reach. */\nexport type ClaudeProseMode = 'plain' | 'enhanced'\nexport const CLAUDE_PROSE_MODES = ['plain', 'enhanced'] as const\nexport const DEFAULT_CLAUDE_PROSE_MODE: ClaudeProseMode = 'plain'\n\nexport function isClaudeProseMode(value: unknown): value is ClaudeProseMode {\n return value === 'plain' || value === 'enhanced'\n}\n","import type { SessionEvent } from '@deepseek-ai/dsh-session'\nimport {\n CLAUDE_ACTIVITY_EVENT,\n CLAUDE_CONTEXT_USAGE_EVENT,\n CLAUDE_SESSION_BOUND_EVENT,\n CLAUDE_TASKS_EVENT,\n isClaudeRenderMode,\n type ClaudeRenderMode,\n} from './constants.ts'\n\nexport type ClaudeActivityKind =\n | 'text'\n | 'status'\n /** Context compaction boundary; the transcript draws it as a divider. */\n | 'compaction'\n | 'thinking'\n | 'tool-call'\n | 'tool-result'\n | 'permission'\n | 'question'\n | 'subagent'\n | 'usage'\n | 'warning'\n | 'error'\n\nexport type ClaudeActivityPhase =\n | 'started'\n | 'updated'\n | 'completed'\n | 'denied'\n | 'failed'\n\nexport interface ClaudeUsage {\n inputTokens?: number\n outputTokens?: number\n cacheReadTokens?: number\n cacheCreationTokens?: number\n cumulativeCostUsd?: number\n /** Wall time from the turn being admitted to it settling. Measured here\n * rather than derived on the client: activities carry no timestamps. */\n durationMs?: number\n /** Wall time to the first visible token of the turn. */\n ttftMs?: number\n}\n\nexport interface ClaudeSessionBoundEvent {\n claudeSessionId: string\n cliVersion?: string\n sdkVersion: string\n cwd: string\n}\n\nexport interface ClaudeActivityEvent {\n turn: number\n step: number\n ordinal: number\n kind: ClaudeActivityKind\n phase?: ClaudeActivityPhase\n /** Claude task-board identity for lifecycle activity; never a transcript path. */\n taskId?: string\n toolUseId?: string\n /** Enclosing Claude tool call for subagent-nested activity. */\n parentToolUseId?: string\n toolName?: string\n title?: string\n summary?: string\n detail?: string\n /** Redacted visible Claude prose used by the plugin-owned interleaved transcript. */\n text?: string\n isError?: boolean\n usage?: ClaudeUsage\n /** Which renderer this record was produced for. Stamped only when the Host\n * drew the step natively, so a record written before the setting existed —\n * and every record written under the plugin renderer — reads as 'plugin'.\n * It travels with the data so a step always renders the way it was\n * recorded, whatever the setting says now. */\n renderer?: ClaudeRenderMode\n}\n\nexport interface ClaudeContextUsageCategory {\n name: string\n tokens: number\n color: string\n isDeferred?: boolean\n}\n\nexport interface ClaudeContextUsageEvent {\n model: string\n totalTokens: number\n maxTokens: number\n percentage: number\n categories: readonly ClaudeContextUsageCategory[]\n}\n\nexport interface ClaudeContextUsageInput {\n model: unknown\n totalTokens: unknown\n maxTokens: unknown\n percentage: unknown\n categories: readonly {\n name?: unknown\n tokens?: unknown\n color?: unknown\n isDeferred?: unknown\n }[]\n}\n\ndeclare module '@deepseek-ai/dsh-session/types' {\n interface SessionEventMap {\n 'claude-code/session-bound': ClaudeSessionBoundEvent\n 'claude-code/activity': ClaudeActivityEvent\n 'claude-code/context-usage': ClaudeContextUsageEvent\n 'claude-code/tasks': ClaudeTasksEvent\n }\n}\n\nconst SECRET_KEY = /(?:^|[_-])(password|passwd|secret|token|api[_-]?key|authorization|credential|private[_-]?key|session[_-]?key|env|environ|environment)(?:$|[_-])/i\nconst MAX_SUMMARY_CHARS = 1_000\nconst MAX_DETAIL_CHARS = 4_000\nconst MAX_TRANSCRIPT_TEXT_CHARS = 64_000\nconst MAX_DEPTH = 6\nconst MAX_ARRAY_ITEMS = 40\nconst MAX_OBJECT_KEYS = 60\nconst REDACTED = '[REDACTED]'\nconst TRUNCATED = '…[truncated]'\nconst SECRET_ASSIGNMENT = /((?:password|passwd|secret|token|api[_-]?key|authorization|credential|private[_-]?key|session[_-]?key)\\s*(?:=|:)\\s*)(?:\"[^\"]*\"|'[^']*'|[^\\s,;&]+)/giu\nconst BEARER_TOKEN = /(\\bbearer\\s+)[A-Za-z0-9._~+/=-]+/giu\nconst PREFIXED_TOKEN = /\\b(?:sk-(?:ant-|proj-)?|xox[baprs]-|ghp_|github_pat_)[A-Za-z0-9_-]{8,}/giu\nconst JWT_TOKEN = /\\beyJ[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\.[A-Za-z0-9_-]+\\b/gu\nconst URL_USERINFO = /([a-z][a-z0-9+.-]*:\\/\\/[^:\\s/@]+:)[^@\\s/]+@/giu\nconst URL_SECRET_PARAM = /([?&](?:password|secret|token|api[_-]?key|access[_-]?token|refresh[_-]?token)=)[^&#\\s]+/giu\n\nexport function boundText(value: string, maxChars: number): string {\n if (value.length <= maxChars) return value\n return `${value.slice(0, Math.max(0, maxChars - TRUNCATED.length))}${TRUNCATED}`\n}\n\nexport function redactText(value: string, maxChars = MAX_DETAIL_CHARS): string {\n return boundText(\n value\n .replace(JWT_TOKEN, REDACTED)\n .replace(PREFIXED_TOKEN, REDACTED)\n .replace(BEARER_TOKEN, `$1${REDACTED}`)\n .replace(URL_USERINFO, `$1${REDACTED}@`)\n .replace(URL_SECRET_PARAM, `$1${REDACTED}`)\n .replace(SECRET_ASSIGNMENT, `$1${REDACTED}`),\n maxChars,\n )\n}\n\nexport function redactValue(value: unknown, depth = 0, seen = new WeakSet<object>()): unknown {\n if (depth > MAX_DEPTH) return '[max-depth]'\n if (value === null || typeof value === 'boolean' || typeof value === 'number') return value\n if (typeof value === 'string') return redactText(value)\n if (typeof value === 'bigint') return value.toString()\n if (typeof value === 'undefined') return null\n if (typeof value === 'function' || typeof value === 'symbol') return `[${typeof value}]`\n if (value instanceof Error) {\n return { name: value.name, message: redactText(value.message, MAX_SUMMARY_CHARS) }\n }\n if (typeof value !== 'object') return String(value)\n if (seen.has(value)) return '[circular]'\n seen.add(value)\n try {\n if (Array.isArray(value)) {\n const items = value.slice(0, MAX_ARRAY_ITEMS).map(item => redactValue(item, depth + 1, seen))\n if (value.length > MAX_ARRAY_ITEMS) items.push(`[${value.length - MAX_ARRAY_ITEMS} more items]`)\n return items\n }\n const result: Record<string, unknown> = {}\n const entries = Object.entries(value as Record<string, unknown>)\n for (const [key, item] of entries.slice(0, MAX_OBJECT_KEYS)) {\n result[key] = SECRET_KEY.test(key) ? REDACTED : redactValue(item, depth + 1, seen)\n }\n if (entries.length > MAX_OBJECT_KEYS) result.__truncatedKeys = entries.length - MAX_OBJECT_KEYS\n return result\n } finally {\n seen.delete(value)\n }\n}\n\nexport function safeDetail(value: unknown): string | undefined {\n if (value === undefined) return undefined\n const redacted = redactValue(value)\n const text = typeof redacted === 'string' ? redacted : JSON.stringify(redacted)\n return boundText(text, MAX_DETAIL_CHARS)\n}\n\nexport function normalizeActivity(\n activity: Omit<ClaudeActivityEvent, 'summary' | 'detail'> & {\n summary?: unknown\n detail?: unknown\n },\n): ClaudeActivityEvent {\n const normalized: ClaudeActivityEvent = {\n turn: activity.turn,\n step: activity.step,\n ordinal: activity.ordinal,\n kind: activity.kind,\n }\n if (activity.phase !== undefined) normalized.phase = activity.phase\n if (activity.taskId !== undefined) normalized.taskId = redactText(activity.taskId, 128)\n if (activity.toolUseId !== undefined) normalized.toolUseId = redactText(activity.toolUseId, 256)\n if (activity.parentToolUseId !== undefined) normalized.parentToolUseId = redactText(activity.parentToolUseId, 256)\n if (activity.toolName !== undefined) normalized.toolName = redactText(activity.toolName, 256)\n if (activity.title !== undefined) normalized.title = redactText(activity.title, MAX_SUMMARY_CHARS)\n if (activity.summary !== undefined) {\n normalized.summary = redactText(\n typeof activity.summary === 'string' ? activity.summary : safeDetail(activity.summary) ?? '',\n MAX_SUMMARY_CHARS,\n )\n }\n const detail = safeDetail(activity.detail)\n if (detail !== undefined) normalized.detail = detail\n if (activity.text !== undefined) normalized.text = redactText(activity.text, MAX_TRANSCRIPT_TEXT_CHARS)\n if (activity.isError !== undefined) normalized.isError = activity.isError\n if (activity.usage !== undefined) normalized.usage = { ...activity.usage }\n if (isClaudeRenderMode(activity.renderer)) normalized.renderer = activity.renderer\n return normalized\n}\n\nconst MAX_CONTEXT_CATEGORIES = 24\nconst FALLBACK_CONTEXT_COLOR = '#8b95a5'\nconst SAFE_CONTEXT_COLOR = /^#[0-9a-f]{3,8}$/iu\n\nfunction nonNegativeInteger(value: unknown): number {\n return typeof value === 'number' && Number.isFinite(value)\n ? Math.max(0, Math.floor(value))\n : 0\n}\n\nexport function normalizeContextUsage(input: ClaudeContextUsageInput): ClaudeContextUsageEvent {\n return {\n model: redactText(typeof input.model === 'string' ? input.model : 'unknown', 128),\n totalTokens: nonNegativeInteger(input.totalTokens),\n maxTokens: nonNegativeInteger(input.maxTokens),\n percentage: Math.min(100, nonNegativeInteger(input.percentage)),\n categories: input.categories.slice(0, MAX_CONTEXT_CATEGORIES).map(category => ({\n name: redactText(typeof category.name === 'string' ? category.name : 'Unknown', 128),\n tokens: nonNegativeInteger(category.tokens),\n color: typeof category.color === 'string' && SAFE_CONTEXT_COLOR.test(category.color)\n ? category.color\n : FALLBACK_CONTEXT_COLOR,\n ...(category.isDeferred === true ? { isDeferred: true } : {}),\n })),\n }\n}\n\nexport function latestClaudeContextUsage(\n events: readonly SessionEvent[],\n): ClaudeContextUsageEvent | undefined {\n for (let index = events.length - 1; index >= 0; index -= 1) {\n const event = events[index]\n if (event?.type === CLAUDE_CONTEXT_USAGE_EVENT) return event.data as ClaudeContextUsageEvent\n }\n return undefined\n}\n\nexport type ClaudeTaskStatus = 'running' | 'completed' | 'failed' | 'stopped' | 'killed'\n\nexport interface ClaudeTaskUsage {\n totalTokens?: number\n toolUses?: number\n durationMs?: number\n}\n\nexport interface ClaudeTaskInfo {\n taskId: string\n description: string\n status: ClaudeTaskStatus\n /** DSH turn during which this task was first observed, when known. */\n originTurn?: number\n subagentType?: string\n taskType?: string\n lastToolName?: string\n summary?: string\n usage?: ClaudeTaskUsage\n /** True while the task runs detached (background command/subagent). */\n backgrounded?: boolean\n}\n\n/** Level snapshot of one session's Claude task board, REPLACE semantics. */\nexport interface ClaudeTasksEvent {\n tasks: readonly ClaudeTaskInfo[]\n}\n\nconst MAX_TASKS_PER_SNAPSHOT = 50\nconst MAX_TASK_TEXT_CHARS = 300\n\nconst TASK_STATUSES: ReadonlySet<string> = new Set(['running', 'completed', 'failed', 'stopped', 'killed'])\n\nfunction normalizeTaskUsage(input: ClaudeTaskUsage | undefined): ClaudeTaskUsage | undefined {\n if (input === undefined) return undefined\n const usage: ClaudeTaskUsage = {}\n if (input.totalTokens !== undefined) usage.totalTokens = nonNegativeInteger(input.totalTokens)\n if (input.toolUses !== undefined) usage.toolUses = nonNegativeInteger(input.toolUses)\n if (input.durationMs !== undefined) usage.durationMs = nonNegativeInteger(input.durationMs)\n return Object.keys(usage).length === 0 ? undefined : usage\n}\n\nexport function normalizeTasksEvent(tasks: readonly ClaudeTaskInfo[]): ClaudeTasksEvent {\n return {\n tasks: tasks.slice(0, MAX_TASKS_PER_SNAPSHOT).map(task => {\n const usage = normalizeTaskUsage(task.usage)\n return {\n taskId: redactText(String(task.taskId), 128),\n description: redactText(String(task.description), MAX_TASK_TEXT_CHARS),\n status: TASK_STATUSES.has(task.status) ? task.status : 'running',\n ...(task.originTurn === undefined ? {} : { originTurn: nonNegativeInteger(task.originTurn) }),\n ...(task.subagentType === undefined ? {} : { subagentType: redactText(task.subagentType, 64) }),\n ...(task.taskType === undefined ? {} : { taskType: redactText(task.taskType, 64) }),\n ...(task.lastToolName === undefined ? {} : { lastToolName: redactText(task.lastToolName, 64) }),\n ...(task.summary === undefined ? {} : { summary: redactText(task.summary, MAX_TASK_TEXT_CHARS) }),\n ...(usage === undefined ? {} : { usage }),\n ...(task.backgrounded === true ? { backgrounded: true } : {}),\n }\n }),\n }\n}\n\nexport function latestClaudeTasks(\n events: readonly SessionEvent[],\n): ClaudeTasksEvent | undefined {\n for (let index = events.length - 1; index >= 0; index -= 1) {\n const event = events[index]\n if (event?.type === CLAUDE_TASKS_EVENT) return event.data as ClaudeTasksEvent\n }\n return undefined\n}\n\nexport type ClaudeActivityInput = Omit<\n ClaudeActivityEvent,\n 'turn' | 'step' | 'ordinal' | 'summary' | 'detail'\n> & {\n summary?: unknown\n detail?: unknown\n}\n\nexport interface ClaudeActivityCursor {\n turn: number\n step: number\n nextOrdinal: number\n}\n\n/** Derive the current DSH turn/step; activity ordinals are completed from the sidecar. */\nexport function currentClaudeActivityCursor(events: readonly SessionEvent[]): ClaudeActivityCursor {\n let turn = 0\n let step = 0\n for (const event of events) {\n if (event.type !== 'step/start') continue\n const data = event.data as { turn: number; step: number }\n turn = data.turn\n step = data.step\n }\n if (turn < 1 || step < 1) {\n throw new Error('dsh-claude: Claude activity requires an open DSH step')\n }\n return { turn, step, nextOrdinal: 0 }\n}\n\nexport function latestClaudeSessionBinding(\n events: readonly SessionEvent[],\n): ClaudeSessionBoundEvent | undefined {\n for (let index = events.length - 1; index >= 0; index -= 1) {\n const event = events[index]\n if (event?.type === CLAUDE_SESSION_BOUND_EVENT) {\n return event.data as ClaudeSessionBoundEvent\n }\n }\n return undefined\n}\n"],"mappings":"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"}
|
package/lib/index.d.mts
CHANGED
|
@@ -53,6 +53,11 @@ interface ClaudeUsage {
|
|
|
53
53
|
cacheReadTokens?: number;
|
|
54
54
|
cacheCreationTokens?: number;
|
|
55
55
|
cumulativeCostUsd?: number;
|
|
56
|
+
/** Wall time from the turn being admitted to it settling. Measured here
|
|
57
|
+
* rather than derived on the client: activities carry no timestamps. */
|
|
58
|
+
durationMs?: number;
|
|
59
|
+
/** Wall time to the first visible token of the turn. */
|
|
60
|
+
ttftMs?: number;
|
|
56
61
|
}
|
|
57
62
|
interface ClaudeSessionBoundEvent {
|
|
58
63
|
claudeSessionId: string;
|
package/lib/index.mjs
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
|
-
import { A as
|
|
2
|
-
import { a as projectClaudeCommands, i as CLAUDE_COMMANDS_SERVICE, r as dynamicPresenterDefinition, t as CLAUDE_PRESENTER_NAMES } from "./presenters-
|
|
3
|
-
import { a as resolveClaudeExecutable, n as ensureManagedPreset, o as runClaudeDoctor, t as ManagedPresetConflictError } from "./preset-installer-
|
|
1
|
+
import { A as CLAUDE_REWIND_PATH, C as CLAUDE_RENDER_MODES, D as CLAUDE_REPOSITORY_SETUP_PATH, E as CLAUDE_REPOSITORY_FILE_PATH, F as DEFAULT_CLAUDE_RENDER_MODE, L as TASK_TOOL_NAMES, M as CLAUDE_UPDATE_PATH, N as CLAUDE_USAGE_PATH, O as CLAUDE_REPOSITORY_STATUS_PATH, P as DEFAULT_CLAUDE_PROSE_MODE, R as isClaudeProseMode, S as CLAUDE_PROSE_MODES, T as CLAUDE_REPOSITORY_FEEDBACK_PATH, _ as CLAUDE_DOCTOR_PATH, a as latestClaudeTasks, b as CLAUDE_JIRA_PATH, c as normalizeTasksEvent, d as CLAUDE_ACTIVITY_EVENT, f as CLAUDE_ASK_PATH, g as CLAUDE_CODE_PROVIDER_IDS, h as CLAUDE_CODE_PROVIDER, i as latestClaudeSessionBinding, j as CLAUDE_UPDATE_CHECK_PATH, k as CLAUDE_REVIEW_COMMENT_PATH, l as redactText, m as CLAUDE_CODE_PRESET_ID, n as currentClaudeActivityCursor, o as normalizeActivity, p as CLAUDE_CLIENT_DIAGNOSTICS_PATH, r as latestClaudeContextUsage, s as normalizeContextUsage, t as boundText, u as safeDetail, v as CLAUDE_EDITOR_OPEN_PATH, w as CLAUDE_REPOSITORY_ACTION_PATH, x as CLAUDE_PROJECTION_PATH, y as CLAUDE_GLOBAL_SETTINGS_PATH, z as isClaudeRenderMode } from "./events-OhBoFNKO.mjs";
|
|
2
|
+
import { a as projectClaudeCommands, i as CLAUDE_COMMANDS_SERVICE, r as dynamicPresenterDefinition, t as CLAUDE_PRESENTER_NAMES } from "./presenters-BBoM1Ju1.mjs";
|
|
3
|
+
import { a as resolveClaudeExecutable, n as ensureManagedPreset, o as runClaudeDoctor, t as ManagedPresetConflictError } from "./preset-installer-loenwnLS.mjs";
|
|
4
4
|
import z from "@deepseek-ai/schemastery";
|
|
5
5
|
import { createHash, randomUUID } from "node:crypto";
|
|
6
|
-
import { chmod, mkdir, opendir, readFile, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
6
|
+
import { chmod, mkdir, opendir, readFile, readdir, realpath, rename, rm, stat, writeFile } from "node:fs/promises";
|
|
7
7
|
import { basename, dirname, extname, isAbsolute, join, resolve, sep } from "node:path";
|
|
8
8
|
import { dshHomePath, resolveDshHome } from "@deepseek-ai/dsh-home-paths";
|
|
9
9
|
import { homedir } from "node:os";
|
|
10
10
|
import { query } from "@anthropic-ai/claude-agent-sdk";
|
|
11
|
-
import {
|
|
11
|
+
import { LlmAdapter, ReasoningEffortId, ToolCallId, 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";
|
|
@@ -1610,9 +1610,11 @@ var ClaudeSupervisor = class {
|
|
|
1610
1610
|
transcriptTextOrdinal: void 0,
|
|
1611
1611
|
thinking: "",
|
|
1612
1612
|
requestUsage: void 0,
|
|
1613
|
+
startedAt: Date.now(),
|
|
1614
|
+
firstOutputAt: void 0,
|
|
1613
1615
|
aborted: false,
|
|
1614
1616
|
deniedToolUseIds: /* @__PURE__ */ new Set(),
|
|
1615
|
-
|
|
1617
|
+
openCalls: /* @__PURE__ */ new Map(),
|
|
1616
1618
|
...request.signal === void 0 ? {} : { signal: request.signal }
|
|
1617
1619
|
};
|
|
1618
1620
|
entry.active = active;
|
|
@@ -1898,6 +1900,7 @@ var ClaudeSupervisor = class {
|
|
|
1898
1900
|
active.text += message.text;
|
|
1899
1901
|
active.transcriptText += message.text;
|
|
1900
1902
|
await this.#upsertTranscriptText(active);
|
|
1903
|
+
active.firstOutputAt ??= Date.now();
|
|
1901
1904
|
active.output.push({
|
|
1902
1905
|
type: "text-delta",
|
|
1903
1906
|
text: message.text
|
|
@@ -1910,6 +1913,7 @@ var ClaudeSupervisor = class {
|
|
|
1910
1913
|
active.text += message.text;
|
|
1911
1914
|
active.transcriptText += message.text;
|
|
1912
1915
|
await this.#upsertTranscriptText(active);
|
|
1916
|
+
active.firstOutputAt ??= Date.now();
|
|
1913
1917
|
active.output.push({
|
|
1914
1918
|
type: "text-delta",
|
|
1915
1919
|
text: message.text
|
|
@@ -1947,7 +1951,7 @@ var ClaudeSupervisor = class {
|
|
|
1947
1951
|
detail: message.input
|
|
1948
1952
|
});
|
|
1949
1953
|
if (message.parentToolUseId === void 0) {
|
|
1950
|
-
active.
|
|
1954
|
+
active.openCalls.set(message.toolUseId, message.toolName);
|
|
1951
1955
|
if (this.#nativeRendering()) {
|
|
1952
1956
|
this.#ensureDynamicPresenter(active.agent, message.toolName);
|
|
1953
1957
|
await this.#appendNativeToolCall(active, message);
|
|
@@ -1955,6 +1959,7 @@ var ClaudeSupervisor = class {
|
|
|
1955
1959
|
}
|
|
1956
1960
|
return;
|
|
1957
1961
|
case "tool-result":
|
|
1962
|
+
active.openCalls.delete(message.toolUseId);
|
|
1958
1963
|
await this.#appendActivity(active, {
|
|
1959
1964
|
kind: message.parentToolUseId === void 0 ? "tool-result" : "subagent",
|
|
1960
1965
|
phase: message.isError ? "failed" : "completed",
|
|
@@ -2023,6 +2028,17 @@ var ClaudeSupervisor = class {
|
|
|
2023
2028
|
return;
|
|
2024
2029
|
}
|
|
2025
2030
|
}
|
|
2031
|
+
/** The turn's accounting with its wall clock attached. The transcript draws
|
|
2032
|
+
* this line itself under the plugin renderer: activities carry no
|
|
2033
|
+
* timestamps, so a duration it did not measure is a duration nobody has. */
|
|
2034
|
+
#timedUsage(active, usage) {
|
|
2035
|
+
const now = Date.now();
|
|
2036
|
+
return {
|
|
2037
|
+
...usage,
|
|
2038
|
+
durationMs: Math.max(0, now - active.startedAt),
|
|
2039
|
+
...active.firstOutputAt === void 0 ? {} : { ttftMs: Math.max(0, active.firstOutputAt - active.startedAt) }
|
|
2040
|
+
};
|
|
2041
|
+
}
|
|
2026
2042
|
#nativeRendering() {
|
|
2027
2043
|
return (this.#config.renderMode ?? "plugin") === "native";
|
|
2028
2044
|
}
|
|
@@ -2051,7 +2067,7 @@ var ClaudeSupervisor = class {
|
|
|
2051
2067
|
await active.agent.session.append("tool/call", {
|
|
2052
2068
|
turn: active.cursor.turn,
|
|
2053
2069
|
step: active.cursor.step,
|
|
2054
|
-
callId:
|
|
2070
|
+
callId: ToolCallId(message.toolUseId),
|
|
2055
2071
|
name: message.toolName,
|
|
2056
2072
|
arguments: safeDetail(message.input) ?? "{}"
|
|
2057
2073
|
});
|
|
@@ -2064,7 +2080,7 @@ var ClaudeSupervisor = class {
|
|
|
2064
2080
|
turn: active.cursor.turn,
|
|
2065
2081
|
step: active.cursor.step,
|
|
2066
2082
|
message: createToolResultMessage({
|
|
2067
|
-
callId:
|
|
2083
|
+
callId: ToolCallId(message.toolUseId),
|
|
2068
2084
|
content: [{
|
|
2069
2085
|
type: "text",
|
|
2070
2086
|
text
|
|
@@ -2214,7 +2230,7 @@ var ClaudeSupervisor = class {
|
|
|
2214
2230
|
phase: "completed",
|
|
2215
2231
|
title: "Claude usage",
|
|
2216
2232
|
summary: usageSummary(result.usage),
|
|
2217
|
-
usage: result.usage
|
|
2233
|
+
usage: this.#timedUsage(active, result.usage)
|
|
2218
2234
|
});
|
|
2219
2235
|
active.output.push({
|
|
2220
2236
|
type: "usage",
|
|
@@ -2245,6 +2261,7 @@ var ClaudeSupervisor = class {
|
|
|
2245
2261
|
if (active.aborted) {
|
|
2246
2262
|
await this.#upsertTranscriptText(active);
|
|
2247
2263
|
await this.#flushTranscript(active);
|
|
2264
|
+
await this.#settleOpenCalls(active, "Cancelled with the turn");
|
|
2248
2265
|
await this.#appendSafely(active, {
|
|
2249
2266
|
kind: "status",
|
|
2250
2267
|
phase: "failed",
|
|
@@ -2264,7 +2281,7 @@ var ClaudeSupervisor = class {
|
|
|
2264
2281
|
phase: "completed",
|
|
2265
2282
|
title: "Claude usage",
|
|
2266
2283
|
summary: usageSummary(result.usage),
|
|
2267
|
-
usage: result.usage
|
|
2284
|
+
usage: this.#timedUsage(active, result.usage)
|
|
2268
2285
|
});
|
|
2269
2286
|
active.output.push({
|
|
2270
2287
|
type: "usage",
|
|
@@ -2408,6 +2425,33 @@ var ClaudeSupervisor = class {
|
|
|
2408
2425
|
this.#interruptions.set(entry.sessionId, interruption);
|
|
2409
2426
|
return interruption;
|
|
2410
2427
|
}
|
|
2428
|
+
/** Close out the root tool calls a turn is ending without answers for.
|
|
2429
|
+
*
|
|
2430
|
+
* A tool result is the only thing that ever settles a call, and a turn that
|
|
2431
|
+
* is cancelled or disconnected produces none: the transcript would keep
|
|
2432
|
+
* drawing those calls as running for as long as the session lives, and no
|
|
2433
|
+
* later event would ever correct it. Written as the failures they are. */
|
|
2434
|
+
async #settleOpenCalls(active, summary) {
|
|
2435
|
+
const open = [...active.openCalls];
|
|
2436
|
+
active.openCalls.clear();
|
|
2437
|
+
for (const [toolUseId, toolName] of open) {
|
|
2438
|
+
await this.#appendSafely(active, {
|
|
2439
|
+
kind: "tool-result",
|
|
2440
|
+
phase: "failed",
|
|
2441
|
+
toolUseId,
|
|
2442
|
+
toolName,
|
|
2443
|
+
title: "Tool cancelled",
|
|
2444
|
+
summary,
|
|
2445
|
+
isError: true
|
|
2446
|
+
});
|
|
2447
|
+
if (this.#nativeRendering()) await this.#appendNativeToolResult(active, {
|
|
2448
|
+
kind: "tool-result",
|
|
2449
|
+
toolUseId,
|
|
2450
|
+
output: summary,
|
|
2451
|
+
isError: true
|
|
2452
|
+
});
|
|
2453
|
+
}
|
|
2454
|
+
}
|
|
2411
2455
|
async #interrupt(entry) {
|
|
2412
2456
|
const active = entry.active;
|
|
2413
2457
|
if (active === void 0 || entry.state === "interrupting") return;
|
|
@@ -2421,6 +2465,7 @@ var ClaudeSupervisor = class {
|
|
|
2421
2465
|
} catch (error) {
|
|
2422
2466
|
interruptError = error;
|
|
2423
2467
|
}
|
|
2468
|
+
await this.#settleOpenCalls(active, "Cancelled with the turn").catch(() => void 0);
|
|
2424
2469
|
try {
|
|
2425
2470
|
await this.#appendActivity(active, {
|
|
2426
2471
|
kind: "status",
|
|
@@ -2442,6 +2487,7 @@ var ClaudeSupervisor = class {
|
|
|
2442
2487
|
const unknown = active.sawActivity;
|
|
2443
2488
|
entry.state = unknown ? "outcome-unknown" : "disconnected";
|
|
2444
2489
|
const failure = unknown ? new ClaudeOutcomeUnknownError(stderr === void 0 || stderr.length === 0 ? void 0 : `Claude Code exited after activity; outcome unknown. ${stderr}`) : new Error(stderr === void 0 || stderr.length === 0 ? errorSummary(error) : stderr);
|
|
2490
|
+
await this.#settleOpenCalls(active, "Claude Code stopped before the tool answered").catch(() => void 0);
|
|
2445
2491
|
await this.#appendSafely(active, {
|
|
2446
2492
|
kind: "error",
|
|
2447
2493
|
phase: "failed",
|
|
@@ -3931,6 +3977,90 @@ var RepositoryStatusService = class {
|
|
|
3931
3977
|
}
|
|
3932
3978
|
};
|
|
3933
3979
|
//#endregion
|
|
3980
|
+
//#region src/branch-name.ts
|
|
3981
|
+
/** Name a generated worktree branch after what the user is about to ask for.
|
|
3982
|
+
*
|
|
3983
|
+
* The composer draft is the only description of the work that exists before
|
|
3984
|
+
* the session starts, and it is usually not English and never branch-safe, so
|
|
3985
|
+
* a throwaway Haiku turn compresses it into a slug. Naming is a nicety: every
|
|
3986
|
+
* failure here returns `undefined` and the caller keeps its timestamped name. */
|
|
3987
|
+
/** Cheapest model that can translate and compress a sentence. */
|
|
3988
|
+
const BRANCH_SUMMARY_MODEL = "haiku";
|
|
3989
|
+
/** A branch name is not worth blocking worktree creation on for long. */
|
|
3990
|
+
const BRANCH_SUMMARY_TIMEOUT_MS = 15e3;
|
|
3991
|
+
const MAX_INTENT_CHARS = 2e3;
|
|
3992
|
+
/** A compliant reply is one short fragment; anything longer is prose. */
|
|
3993
|
+
const MAX_REPLY_CHARS$2 = 80;
|
|
3994
|
+
/** More words than this is a sentence, not the fragment we asked for. */
|
|
3995
|
+
const MAX_SLUG_WORDS = 6;
|
|
3996
|
+
const MAX_SLUG_CHARS = 48;
|
|
3997
|
+
function branchSummaryPrompt(intent) {
|
|
3998
|
+
return [
|
|
3999
|
+
"Summarize this software task as a Git branch name fragment.",
|
|
4000
|
+
"Reply with 2-5 lowercase English words joined by hyphens and nothing else:",
|
|
4001
|
+
"no quotes, no slashes, no prefix, no punctuation, no explanation.",
|
|
4002
|
+
"Translate the task to English if it is written in another language.",
|
|
4003
|
+
"",
|
|
4004
|
+
"Task:",
|
|
4005
|
+
`"""\n${intent.replaceAll("\"\"\"", "\" \" \"")}\n"""`
|
|
4006
|
+
].join("\n");
|
|
4007
|
+
}
|
|
4008
|
+
/** Branch-safe slug for a model reply, or `undefined` when the reply is not
|
|
4009
|
+
* the fragment we asked for. Mangling a refusal or a paragraph into a slug
|
|
4010
|
+
* would produce a worse name than the timestamped fallback. */
|
|
4011
|
+
function branchSlug(reply) {
|
|
4012
|
+
const line = reply.trim();
|
|
4013
|
+
if (line.length === 0 || line.length > MAX_REPLY_CHARS$2 || /[\r\n]/u.test(line)) return void 0;
|
|
4014
|
+
const words = line.toLocaleLowerCase("en-US").replace(/[^a-z0-9]+/gu, "-").split("-").filter((word) => word.length > 0);
|
|
4015
|
+
if (words.length === 0 || words.length > MAX_SLUG_WORDS) return void 0;
|
|
4016
|
+
const slug = words.join("-").slice(0, MAX_SLUG_CHARS).replace(/-+$/u, "");
|
|
4017
|
+
return slug.length === 0 ? void 0 : slug;
|
|
4018
|
+
}
|
|
4019
|
+
/** First free name in `<candidate>`, `<candidate>-2`, `<candidate>-3`, … */
|
|
4020
|
+
function uniqueBranchName(candidate, taken) {
|
|
4021
|
+
const existing = new Set(taken);
|
|
4022
|
+
if (!existing.has(candidate)) return candidate;
|
|
4023
|
+
for (let index = 2; index < 100; index += 1) {
|
|
4024
|
+
const name = `${candidate}-${index}`;
|
|
4025
|
+
if (!existing.has(name)) return name;
|
|
4026
|
+
}
|
|
4027
|
+
return candidate;
|
|
4028
|
+
}
|
|
4029
|
+
/** Compress a composer draft into a branch slug with a throwaway Claude turn.
|
|
4030
|
+
*
|
|
4031
|
+
* Deliberately NOT routed through the supervisor, for the same reasons as the
|
|
4032
|
+
* plan-usage probe: there is no session to borrow yet. The turn is isolated
|
|
4033
|
+
* from filesystem settings as well, because a CLAUDE.md instruction ("always
|
|
4034
|
+
* reply in the user's language") turns the answer into an unusable slug. */
|
|
4035
|
+
async function summarizeBranchSlug(executablePath, intent, factory = query) {
|
|
4036
|
+
const task = intent.trim().slice(0, MAX_INTENT_CHARS);
|
|
4037
|
+
if (task.length === 0) return void 0;
|
|
4038
|
+
const lifetime = new AbortController();
|
|
4039
|
+
const timer = setTimeout(() => lifetime.abort(), BRANCH_SUMMARY_TIMEOUT_MS);
|
|
4040
|
+
timer.unref?.();
|
|
4041
|
+
try {
|
|
4042
|
+
const query = factory({
|
|
4043
|
+
prompt: branchSummaryPrompt(task),
|
|
4044
|
+
options: {
|
|
4045
|
+
cwd: process.cwd(),
|
|
4046
|
+
abortController: lifetime,
|
|
4047
|
+
model: BRANCH_SUMMARY_MODEL,
|
|
4048
|
+
allowedTools: [],
|
|
4049
|
+
settingSources: [],
|
|
4050
|
+
maxTurns: 1,
|
|
4051
|
+
...executablePath.length === 0 ? {} : { pathToClaudeCodeExecutable: executablePath }
|
|
4052
|
+
}
|
|
4053
|
+
});
|
|
4054
|
+
for await (const message of query) if (message.type === "result" && message.subtype === "success") return branchSlug(message.result);
|
|
4055
|
+
return;
|
|
4056
|
+
} catch {
|
|
4057
|
+
return;
|
|
4058
|
+
} finally {
|
|
4059
|
+
clearTimeout(timer);
|
|
4060
|
+
lifetime.abort();
|
|
4061
|
+
}
|
|
4062
|
+
}
|
|
4063
|
+
//#endregion
|
|
3934
4064
|
//#region src/repository-setup.ts
|
|
3935
4065
|
const MAX_OUTPUT_BYTES$3 = 131072;
|
|
3936
4066
|
const GIT_TIMEOUT_MS$2 = 1e4;
|
|
@@ -4008,6 +4138,7 @@ var RepositorySetupService = class {
|
|
|
4008
4138
|
#leasePath;
|
|
4009
4139
|
#worktreeRoot;
|
|
4010
4140
|
#branchPrefix;
|
|
4141
|
+
#summarizeBranch;
|
|
4011
4142
|
#cleanupGraceMs;
|
|
4012
4143
|
#gitPath;
|
|
4013
4144
|
#pending = Promise.resolve();
|
|
@@ -4016,6 +4147,7 @@ var RepositorySetupService = class {
|
|
|
4016
4147
|
this.#leasePath = options.leasePath ?? dshHomePath("plugins", "dsh-claude", "worktrees.json");
|
|
4017
4148
|
this.#worktreeRoot = options.worktreeRoot ?? dshHomePath("plugins", "dsh-claude", "worktrees");
|
|
4018
4149
|
this.#branchPrefix = options.branchPrefix ?? (async () => "claude");
|
|
4150
|
+
this.#summarizeBranch = options.summarizeBranch ?? (async () => void 0);
|
|
4019
4151
|
this.#cleanupGraceMs = options.cleanupGraceMs ?? CLEANUP_GRACE_MS;
|
|
4020
4152
|
}
|
|
4021
4153
|
async listBranches(cwd) {
|
|
@@ -4062,7 +4194,7 @@ var RepositorySetupService = class {
|
|
|
4062
4194
|
remoteBranches
|
|
4063
4195
|
};
|
|
4064
4196
|
}
|
|
4065
|
-
async setup(cwd, branchValue, useWorktree, explicitBranchName, progress = () => {}) {
|
|
4197
|
+
async setup(cwd, branchValue, useWorktree, explicitBranchName, progress = () => {}, intent) {
|
|
4066
4198
|
progress("inspecting");
|
|
4067
4199
|
const branch = safeBranch(branchValue);
|
|
4068
4200
|
const info = await this.listBranches(cwd);
|
|
@@ -4070,7 +4202,7 @@ var RepositorySetupService = class {
|
|
|
4070
4202
|
const remote = info.remoteBranches.includes(branch);
|
|
4071
4203
|
if (!local && !remote) throw new RepositorySetupError("branch-not-found", "The selected local or remote-tracking branch does not exist.");
|
|
4072
4204
|
const requestedBranch = explicitBranchName === void 0 ? void 0 : safeBranch(explicitBranchName);
|
|
4073
|
-
if (useWorktree) return this.#createWorktree(info
|
|
4205
|
+
if (useWorktree) return this.#createWorktree(info, branch, local ? `refs/heads/${branch}` : `refs/remotes/${branch}`, requestedBranch, requestedBranch !== void 0 && info.branches.includes(requestedBranch), progress, intent);
|
|
4074
4206
|
progress("switching-branch");
|
|
4075
4207
|
return !local && remote ? this.#checkoutRemote(info, branch) : this.#checkout(info, branch);
|
|
4076
4208
|
}
|
|
@@ -4153,11 +4285,17 @@ var RepositorySetupService = class {
|
|
|
4153
4285
|
});
|
|
4154
4286
|
}
|
|
4155
4287
|
/** Reconcile leases against the set of directories still referenced by a
|
|
4156
|
-
* workspace
|
|
4157
|
-
*
|
|
4158
|
-
*
|
|
4159
|
-
*
|
|
4160
|
-
|
|
4288
|
+
* workspace. Deleting a workspace is the user saying they are done with it,
|
|
4289
|
+
* so an unreferenced worktree goes even with uncommitted changes; its
|
|
4290
|
+
* sessions are archived first, through `archiveSessions`, or the Host
|
|
4291
|
+
* rebuilds the deleted workspace from their headers on the next boot.
|
|
4292
|
+
* Fresh leases are retained for a grace period so a worktree created
|
|
4293
|
+
* moments ago cannot be swept before its workspace registration lands.
|
|
4294
|
+
*
|
|
4295
|
+
* Every command runs from the repository root, never from the worktree
|
|
4296
|
+
* being removed: spawning in a directory that is already gone throws
|
|
4297
|
+
* ENOENT, which used to strand the lease forever. */
|
|
4298
|
+
cleanupOrphans(activePaths, archiveSessions) {
|
|
4161
4299
|
const active = new Set(activePaths.map(comparablePath));
|
|
4162
4300
|
return this.#serialize(async () => {
|
|
4163
4301
|
const leases = await this.#readLeases();
|
|
@@ -4172,32 +4310,19 @@ var RepositorySetupService = class {
|
|
|
4172
4310
|
continue;
|
|
4173
4311
|
}
|
|
4174
4312
|
try {
|
|
4175
|
-
|
|
4176
|
-
|
|
4177
|
-
|
|
4178
|
-
|
|
4179
|
-
|
|
4180
|
-
|
|
4181
|
-
|
|
4182
|
-
|
|
4183
|
-
|
|
4184
|
-
|
|
4313
|
+
await archiveSessions?.(item.path).catch(() => void 0);
|
|
4314
|
+
if (await pathExists(item.path)) {
|
|
4315
|
+
if ((await this.#run(git, [
|
|
4316
|
+
"worktree",
|
|
4317
|
+
"remove",
|
|
4318
|
+
"--force",
|
|
4319
|
+
"--",
|
|
4320
|
+
item.path
|
|
4321
|
+
], item.root)).exitCode !== 0) {
|
|
4322
|
+
retained.push(item);
|
|
4323
|
+
continue;
|
|
4185
4324
|
}
|
|
4186
|
-
|
|
4187
|
-
}
|
|
4188
|
-
if (status.stdout.trim().length > 0) {
|
|
4189
|
-
retained.push(item);
|
|
4190
|
-
continue;
|
|
4191
|
-
}
|
|
4192
|
-
if ((await this.#run(git, [
|
|
4193
|
-
"worktree",
|
|
4194
|
-
"remove",
|
|
4195
|
-
"--",
|
|
4196
|
-
item.path
|
|
4197
|
-
], item.root)).exitCode !== 0) {
|
|
4198
|
-
retained.push(item);
|
|
4199
|
-
continue;
|
|
4200
|
-
}
|
|
4325
|
+
} else await this.#run(git, ["worktree", "prune"], item.root).catch(() => void 0);
|
|
4201
4326
|
if (item.pluginGeneratedBranch) await this.#run(git, [
|
|
4202
4327
|
"branch",
|
|
4203
4328
|
"-D",
|
|
@@ -4211,8 +4336,38 @@ var RepositorySetupService = class {
|
|
|
4211
4336
|
}
|
|
4212
4337
|
}
|
|
4213
4338
|
if (changed) await this.#writeLeases(retained);
|
|
4339
|
+
await this.#removeUnleasedDirectories(retained, active, now);
|
|
4214
4340
|
});
|
|
4215
4341
|
}
|
|
4342
|
+
/** Remove directories under the plugin's own worktree root that no lease and
|
|
4343
|
+
* no workspace claims. A lease file lost to a crash, or a worktree whose
|
|
4344
|
+
* lease write failed, otherwise leaves a directory nothing will ever sweep.
|
|
4345
|
+
* The grace period covers the gap between `worktree add` and the lease
|
|
4346
|
+
* write, so a worktree being created right now is never taken. */
|
|
4347
|
+
async #removeUnleasedDirectories(retained, active, now) {
|
|
4348
|
+
const leased = new Set(retained.map((item) => comparablePath(item.path)));
|
|
4349
|
+
let entries;
|
|
4350
|
+
try {
|
|
4351
|
+
entries = await readdir(this.#worktreeRoot);
|
|
4352
|
+
} catch {
|
|
4353
|
+
return;
|
|
4354
|
+
}
|
|
4355
|
+
for (const entry of entries) {
|
|
4356
|
+
const path = join(this.#worktreeRoot, entry);
|
|
4357
|
+
const key = comparablePath(path);
|
|
4358
|
+
if (leased.has(key) || active.has(key)) continue;
|
|
4359
|
+
try {
|
|
4360
|
+
const info = await stat(path);
|
|
4361
|
+
if (!info.isDirectory()) continue;
|
|
4362
|
+
const created = info.birthtimeMs > 0 ? info.birthtimeMs : info.mtimeMs;
|
|
4363
|
+
if (Math.max(0, now - created) < this.#cleanupGraceMs) continue;
|
|
4364
|
+
await rm(path, {
|
|
4365
|
+
recursive: true,
|
|
4366
|
+
force: true
|
|
4367
|
+
});
|
|
4368
|
+
} catch {}
|
|
4369
|
+
}
|
|
4370
|
+
}
|
|
4216
4371
|
async #checkoutRemote(info, remoteBranch) {
|
|
4217
4372
|
const separator = remoteBranch.indexOf("/");
|
|
4218
4373
|
if (separator <= 0 || separator === remoteBranch.length - 1) throw new RepositorySetupError("invalid-branch", "The remote-tracking branch name is invalid.");
|
|
@@ -4279,13 +4434,14 @@ var RepositorySetupService = class {
|
|
|
4279
4434
|
branch
|
|
4280
4435
|
};
|
|
4281
4436
|
}
|
|
4282
|
-
async #createWorktree(
|
|
4437
|
+
async #createWorktree(info, baseBranch, baseRef, explicitBranchName, reuseExistingBranch, progress, intent) {
|
|
4438
|
+
const { root } = info;
|
|
4283
4439
|
const git = await this.#git();
|
|
4284
4440
|
progress("fetching");
|
|
4285
4441
|
await this.#fetchRemotes(git, root);
|
|
4286
4442
|
const suffix = randomUUID().slice(0, 8);
|
|
4287
4443
|
const stamp = (/* @__PURE__ */ new Date()).toISOString().replace(/[-:]/gu, "").replace(/\.\d{3}Z$/u, "Z");
|
|
4288
|
-
const branch = explicitBranchName ??
|
|
4444
|
+
const branch = explicitBranchName ?? await this.#generatedBranch(info, baseBranch, intent, stamp, suffix, progress);
|
|
4289
4445
|
const path = join(this.#worktreeRoot, `${slug(basename(root), "repository")}-${stamp}-${suffix}`);
|
|
4290
4446
|
progress("creating-worktree");
|
|
4291
4447
|
await mkdir(this.#worktreeRoot, { recursive: true });
|
|
@@ -4346,6 +4502,18 @@ var RepositorySetupService = class {
|
|
|
4346
4502
|
leaseId: item.id
|
|
4347
4503
|
};
|
|
4348
4504
|
}
|
|
4505
|
+
/** `<prefix>/<what the draft is about>`, falling back to
|
|
4506
|
+
* `<prefix>/<base branch>-<stamp>-<random>` whenever the summary is missing
|
|
4507
|
+
* or unusable. The prefix and the fallback shape are unchanged. */
|
|
4508
|
+
async #generatedBranch(info, baseBranch, intent, stamp, suffix, progress) {
|
|
4509
|
+
const prefix = safeBranch(await this.#branchPrefix());
|
|
4510
|
+
if (intent !== void 0 && intent.trim().length > 0) {
|
|
4511
|
+
progress("summarizing");
|
|
4512
|
+
const summary = await this.#summarizeBranch(intent).catch(() => void 0);
|
|
4513
|
+
if (summary !== void 0) return uniqueBranchName(`${prefix}/${summary}`, info.branches);
|
|
4514
|
+
}
|
|
4515
|
+
return `${prefix}/${slug(baseBranch, "branch")}-${stamp}-${suffix}`;
|
|
4516
|
+
}
|
|
4349
4517
|
async #repositoryRoot(git, cwd) {
|
|
4350
4518
|
const result = await this.#run(git, [
|
|
4351
4519
|
"rev-parse",
|
|
@@ -4918,7 +5086,7 @@ async function streamSetup(res, service, input) {
|
|
|
4918
5086
|
type: "progress",
|
|
4919
5087
|
stage
|
|
4920
5088
|
});
|
|
4921
|
-
})
|
|
5089
|
+
}, optionalString$1(input, "intent"))
|
|
4922
5090
|
});
|
|
4923
5091
|
} catch (error) {
|
|
4924
5092
|
const setupError = error instanceof RepositorySetupError ? error : void 0;
|
|
@@ -7258,6 +7426,32 @@ const RENDERER = {
|
|
|
7258
7426
|
else document.renderer = value;
|
|
7259
7427
|
}
|
|
7260
7428
|
};
|
|
7429
|
+
/** Whether Claude's prose gets the highlight palette. Presentation only: no
|
|
7430
|
+
* record carries it and nothing on the server reads it back, so unlike
|
|
7431
|
+
* {@link RENDERER} the switch lands the moment the Client rewrites its own
|
|
7432
|
+
* stylesheet — hence 'immediate' rather than 'next-turn'. */
|
|
7433
|
+
const PROSE = {
|
|
7434
|
+
key: "prose",
|
|
7435
|
+
kind: "select",
|
|
7436
|
+
document: "plugin",
|
|
7437
|
+
effect: "immediate",
|
|
7438
|
+
async options() {
|
|
7439
|
+
return CLAUDE_PROSE_MODES.map((value) => ({
|
|
7440
|
+
value,
|
|
7441
|
+
label: value,
|
|
7442
|
+
source: "built-in"
|
|
7443
|
+
}));
|
|
7444
|
+
},
|
|
7445
|
+
read(document) {
|
|
7446
|
+
const value = document.prose;
|
|
7447
|
+
return isClaudeProseMode(value) ? value : DEFAULT_CLAUDE_PROSE_MODE;
|
|
7448
|
+
},
|
|
7449
|
+
apply(document, value) {
|
|
7450
|
+
if (!isClaudeProseMode(value)) throw new Error("Invalid value for global setting prose");
|
|
7451
|
+
if (value === "plain") delete document.prose;
|
|
7452
|
+
else document.prose = value;
|
|
7453
|
+
}
|
|
7454
|
+
};
|
|
7261
7455
|
function isBoundedInteger(value, min, max) {
|
|
7262
7456
|
return typeof value === "number" && Number.isInteger(value) && value >= min && value <= max;
|
|
7263
7457
|
}
|
|
@@ -7282,6 +7476,7 @@ function integerSetting(key, min, max, defaultFor) {
|
|
|
7282
7476
|
const DESCRIPTORS = [
|
|
7283
7477
|
OUTPUT_STYLE,
|
|
7284
7478
|
RENDERER,
|
|
7479
|
+
PROSE,
|
|
7285
7480
|
WORKTREE_BRANCH_PREFIX,
|
|
7286
7481
|
integerSetting("maxProcesses", 1, MAX_PROCESSES_LIMIT, (limits) => limits.maxProcesses),
|
|
7287
7482
|
integerSetting("idleTimeoutMinutes", 1, MAX_IDLE_TIMEOUT_MINUTES, (limits) => Math.max(1, Math.round(limits.idleTimeoutMs / 6e4)))
|
|
@@ -7577,7 +7772,10 @@ async function apply(ctx, config) {
|
|
|
7577
7772
|
await applySettingsOverrides();
|
|
7578
7773
|
const sidecar = new ClaudeSidecarRepository();
|
|
7579
7774
|
const repositoryStatus = new RepositoryStatusService(ctx.subprocess);
|
|
7580
|
-
const repositorySetup = new RepositorySetupService(ctx.subprocess, {
|
|
7775
|
+
const repositorySetup = new RepositorySetupService(ctx.subprocess, {
|
|
7776
|
+
branchPrefix: () => readWorktreeBranchPrefix(),
|
|
7777
|
+
summarizeBranch: (intent) => summarizeBranchSlug(supervisorConfig.executablePath, intent)
|
|
7778
|
+
});
|
|
7581
7779
|
const reviewComments = new ReviewCommentStore();
|
|
7582
7780
|
const commandCatalogs = /* @__PURE__ */ new Map();
|
|
7583
7781
|
const supervisor = new ClaudeSupervisor({
|
|
@@ -7657,10 +7855,20 @@ async function apply(ctx, config) {
|
|
|
7657
7855
|
});
|
|
7658
7856
|
const injectWorkspaceRegistry = ctx.inject;
|
|
7659
7857
|
injectWorkspaceRegistry(["workspaceRegistry"], (sweepCtx) => {
|
|
7858
|
+
const archiveSessions = async (worktreePath) => {
|
|
7859
|
+
const persistence = sweepCtx.get("sessionPersistence");
|
|
7860
|
+
if (persistence === void 0) return;
|
|
7861
|
+
const target = comparablePath(worktreePath);
|
|
7862
|
+
for (const header of await persistence.list()) {
|
|
7863
|
+
if (typeof header.id !== "string" || typeof header.cwd !== "string") continue;
|
|
7864
|
+
if (comparablePath(header.cwd) !== target) continue;
|
|
7865
|
+
await sweepCtx.workspaceRegistry.archiveSession(header.id).catch(() => void 0);
|
|
7866
|
+
}
|
|
7867
|
+
};
|
|
7660
7868
|
const sweep = () => {
|
|
7661
7869
|
try {
|
|
7662
7870
|
const paths = sweepCtx.workspaceRegistry.list().map((workspace) => workspace.path);
|
|
7663
|
-
repositorySetup.cleanupOrphans(paths).catch(() => void 0);
|
|
7871
|
+
repositorySetup.cleanupOrphans(paths, archiveSessions).catch(() => void 0);
|
|
7664
7872
|
} catch {}
|
|
7665
7873
|
};
|
|
7666
7874
|
sweepCtx.effect(() => {
|