@norman-else/dsh-claude 0.1.51 → 0.1.52
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 +120 -55
- package/LICENSE +21 -21
- package/README.md +25 -15
- 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 +0 -0
- package/lib/bin.mjs.map +1 -1
- package/lib/client.d.ts +0 -3
- package/lib/client.js +41 -185
- package/lib/client.js.map +1 -1
- package/lib/events-B498uofA.mjs.map +1 -1
- package/lib/index.mjs +248 -24
- package/lib/index.mjs.map +1 -1
- package/lib/presenters-CRFcjLSC.mjs.map +1 -1
- package/lib/preset-installer-Cj637ucf.mjs.map +1 -1
- package/lib/preset-route.mjs.map +1 -1
- package/package.json +222 -222
- package/preset/claude/agent.cordis.yml +11 -11
- package/preset/claude/preset.yml +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"events-B498uofA.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_REWIND_PATH = '/plugins/dsh-claude/rewind'\nexport const CLAUDE_PLAN_FEEDBACK_PATH = '/plugins/dsh-claude/plan/feedback'\nexport const CLAUDE_PROMPTS_PATH = '/plugins/dsh-claude/prompts'\nexport const CLAUDE_PROMPT_NAME_PATH = '/plugins/dsh-claude/prompts/name'\nexport const CLAUDE_PROMPT_REFINE_PATH = '/plugins/dsh-claude/prompts/refine'\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\n/** Whether a session that needs the user raises a desktop notification while\n * the user is looking at another session. Client-side presentation, like\n * {@link ClaudeProseMode}: nothing on the server reads it back. */\nexport type ClaudeAlertMode = 'off' | 'on'\nexport const CLAUDE_ALERT_MODES = ['off', 'on'] as const\nexport const DEFAULT_CLAUDE_ALERT_MODE: ClaudeAlertMode = 'on'\n\nexport function isClaudeAlertMode(value: unknown): value is ClaudeAlertMode {\n return value === 'off' || value === 'on'\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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"}
|
|
1
|
+
{"version":3,"file":"events-B498uofA.mjs","names":[],"sources":["../src/constants.ts","../src/events.ts"],"sourcesContent":["export const CLAUDE_CODE_PROVIDER = 'claude'\r\nexport const CLAUDE_CODE_PRESET_ID = 'claude'\r\nexport const CLAUDE_CODE_PROVIDER_IDS = [CLAUDE_CODE_PROVIDER] as const\r\nexport const CLAUDE_SESSION_BOUND_EVENT = 'claude-code/session-bound'\r\nexport const CLAUDE_ACTIVITY_EVENT = 'claude-code/activity'\r\nexport const CLAUDE_CONTEXT_USAGE_EVENT = 'claude-code/context-usage'\r\nexport const CLAUDE_TASKS_EVENT = 'claude-code/tasks'\r\n/** Claude's subagent dispatch tools; rendered as plugin-owned group cards\r\n * gathering subagent activity instead of native tool cards. */\r\nexport const TASK_TOOL_NAMES: ReadonlySet<string> = new Set(['Task', 'Agent'])\r\nexport const SDK_VERSION = '0.3.247'\r\nexport const CLAUDE_DOCTOR_PATH = '/plugins/dsh-claude/doctor'\r\nexport const CLAUDE_CLIENT_DIAGNOSTICS_PATH = '/plugins/dsh-claude/client-diagnostics'\r\nexport const CLAUDE_UPDATE_CHECK_PATH = '/plugins/dsh-claude/update/check'\r\nexport const CLAUDE_USAGE_PATH = '/plugins/dsh-claude/usage'\r\nexport const CLAUDE_UPDATE_PATH = '/plugins/dsh-claude/update'\r\nexport const CLAUDE_PROJECTION_PATH = '/plugins/dsh-claude/projection'\r\nexport const CLAUDE_GLOBAL_SETTINGS_PATH = '/plugins/dsh-claude/settings/global'\r\nexport const CLAUDE_REPOSITORY_SETUP_PATH = '/plugins/dsh-claude/repository/setup'\r\nexport const CLAUDE_REPOSITORY_ACTION_PATH = '/plugins/dsh-claude/repository/action'\r\nexport const CLAUDE_REVIEW_COMMENT_PATH = '/plugins/dsh-claude/review-comments'\r\nexport const CLAUDE_REPOSITORY_FEEDBACK_PATH = '/plugins/dsh-claude/repository/feedback'\r\nexport const CLAUDE_REPOSITORY_STATUS_PATH = '/plugins/dsh-claude/repository/status'\r\nexport const CLAUDE_REPOSITORY_FILE_PATH = '/plugins/dsh-claude/repository/file'\r\nexport const CLAUDE_JIRA_PATH = '/plugins/dsh-claude/jira'\r\nexport const CLAUDE_ASK_PATH = '/plugins/dsh-claude/ask'\r\nexport const CLAUDE_REWIND_PATH = '/plugins/dsh-claude/rewind'\r\nexport const CLAUDE_PLAN_FEEDBACK_PATH = '/plugins/dsh-claude/plan/feedback'\r\nexport const CLAUDE_PROMPTS_PATH = '/plugins/dsh-claude/prompts'\r\nexport const CLAUDE_PROMPT_NAME_PATH = '/plugins/dsh-claude/prompts/name'\r\nexport const CLAUDE_PROMPT_REFINE_PATH = '/plugins/dsh-claude/prompts/refine'\r\n\r\n/** Which renderer draws Claude's visible output.\r\n *\r\n * 'plugin' keeps the sidecar-backed transcript this package owns (interleaved\r\n * prose, grouped tool cards, activity rows). 'native' hands the same turn to\r\n * DSH's own conversation renderer: prose streams as ordinary assistant text\r\n * blocks, thinking as reasoning blocks, and root Claude tools are mirrored\r\n * into the durable `tool/call`/`tool/result` channel so the Host's tool\r\n * presentation pipeline draws them exactly like DSH-executed calls. */\r\nexport type ClaudeRenderMode = 'plugin' | 'native'\r\nexport const CLAUDE_RENDER_MODES = ['plugin', 'native'] as const\r\nexport const DEFAULT_CLAUDE_RENDER_MODE: ClaudeRenderMode = 'plugin'\r\n\r\nexport function isClaudeRenderMode(value: unknown): value is ClaudeRenderMode {\r\n return value === 'plugin' || value === 'native'\r\n}\r\n\r\n/** How this package paints the PROSE of a Claude answer.\r\n *\r\n * 'plain' is Claude's own presentation: body text in the Host's text colour,\r\n * colour reserved for code. 'enhanced' gives headings, emphasis, list markers,\r\n * quotes and links their own hues and darkens the code surface — the way a\r\n * Markdown-highlighting editor shows a document rather than the way Claude\r\n * desktop shows an answer. It is opt-in because it deliberately breaks the\r\n * parity the rest of `markdown-theme.ts` exists to hold.\r\n *\r\n * Only meaningful under {@link ClaudeRenderMode} 'plugin': the stylesheet is\r\n * scoped to markup this package renders, and 'native' turns are drawn by the\r\n * Host, where it has no reach. */\r\nexport type ClaudeProseMode = 'plain' | 'enhanced'\r\nexport const CLAUDE_PROSE_MODES = ['plain', 'enhanced'] as const\r\nexport const DEFAULT_CLAUDE_PROSE_MODE: ClaudeProseMode = 'plain'\r\n\r\nexport function isClaudeProseMode(value: unknown): value is ClaudeProseMode {\r\n return value === 'plain' || value === 'enhanced'\r\n}\r\n\r\n/** Whether a session that needs the user raises a desktop notification while\r\n * the user is looking at another session. Client-side presentation, like\r\n * {@link ClaudeProseMode}: nothing on the server reads it back. */\r\nexport type ClaudeAlertMode = 'off' | 'on'\r\nexport const CLAUDE_ALERT_MODES = ['off', 'on'] as const\r\nexport const DEFAULT_CLAUDE_ALERT_MODE: ClaudeAlertMode = 'on'\r\n\r\nexport function isClaudeAlertMode(value: unknown): value is ClaudeAlertMode {\r\n return value === 'off' || value === 'on'\r\n}\r\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.mjs
CHANGED
|
@@ -3478,13 +3478,17 @@ async function summarizeSessionTitle(executablePath, request, factory = query) {
|
|
|
3478
3478
|
abortController: lifetime,
|
|
3479
3479
|
model: SESSION_TITLE_MODEL,
|
|
3480
3480
|
allowedTools: [],
|
|
3481
|
-
settingSources: [
|
|
3481
|
+
settingSources: [
|
|
3482
|
+
"user",
|
|
3483
|
+
"project",
|
|
3484
|
+
"local"
|
|
3485
|
+
],
|
|
3482
3486
|
maxTurns: 1,
|
|
3483
3487
|
...executablePath.length === 0 ? {} : { pathToClaudeCodeExecutable: executablePath }
|
|
3484
3488
|
}
|
|
3485
3489
|
});
|
|
3486
3490
|
for await (const message of query) {
|
|
3487
|
-
if (message.type !== "result" || message.subtype !== "success") continue;
|
|
3491
|
+
if (message.type !== "result" || message.subtype !== "success" || message.is_error === true) continue;
|
|
3488
3492
|
const title = sessionTitleLine(message.result);
|
|
3489
3493
|
if (title.length > 0) return title;
|
|
3490
3494
|
break;
|
|
@@ -5214,9 +5218,8 @@ function uniqueBranchName(candidate, taken) {
|
|
|
5214
5218
|
/** Compress a composer draft into a branch slug with a throwaway Claude turn.
|
|
5215
5219
|
*
|
|
5216
5220
|
* Deliberately NOT routed through the supervisor, for the same reasons as the
|
|
5217
|
-
* plan-usage probe: there is no session to borrow yet.
|
|
5218
|
-
*
|
|
5219
|
-
* reply in the user's language") turns the answer into an unusable slug. */
|
|
5221
|
+
* plan-usage probe: there is no session to borrow yet. Settings sources match
|
|
5222
|
+
* conversation turns so the CLI can resolve settings-based authentication. */
|
|
5220
5223
|
async function summarizeBranchSlug(executablePath, intent, factory = query) {
|
|
5221
5224
|
const task = intent.trim().slice(0, MAX_INTENT_CHARS);
|
|
5222
5225
|
if (task.length === 0) return void 0;
|
|
@@ -5231,12 +5234,16 @@ async function summarizeBranchSlug(executablePath, intent, factory = query) {
|
|
|
5231
5234
|
abortController: lifetime,
|
|
5232
5235
|
model: BRANCH_SUMMARY_MODEL,
|
|
5233
5236
|
allowedTools: [],
|
|
5234
|
-
settingSources: [
|
|
5237
|
+
settingSources: [
|
|
5238
|
+
"user",
|
|
5239
|
+
"project",
|
|
5240
|
+
"local"
|
|
5241
|
+
],
|
|
5235
5242
|
maxTurns: 1,
|
|
5236
5243
|
...executablePath.length === 0 ? {} : { pathToClaudeCodeExecutable: executablePath }
|
|
5237
5244
|
}
|
|
5238
5245
|
});
|
|
5239
|
-
for await (const message of query) if (message.type === "result" && message.subtype === "success") return branchSlug(message.result);
|
|
5246
|
+
for await (const message of query) if (message.type === "result" && message.subtype === "success" && message.is_error !== true) return branchSlug(message.result);
|
|
5240
5247
|
return;
|
|
5241
5248
|
} catch {
|
|
5242
5249
|
return;
|
|
@@ -5849,9 +5856,19 @@ var RepositorySetupService = class {
|
|
|
5849
5856
|
//#region src/repository-actions.ts
|
|
5850
5857
|
const MAX_OUTPUT_BYTES$1 = 262144;
|
|
5851
5858
|
const MAX_PATCH_CHARS = 65536;
|
|
5852
|
-
const MAX_MESSAGE_CHARS =
|
|
5859
|
+
const MAX_MESSAGE_CHARS = 2048;
|
|
5853
5860
|
const MAX_PR_TEXT_CHARS = 8192;
|
|
5854
5861
|
const MAX_UNPUSHED_COMMITS = 20;
|
|
5862
|
+
/** What one generation shows the model: the whole patch when it fits, the
|
|
5863
|
+
* head of it otherwise, and the prompt says which. */
|
|
5864
|
+
const MAX_GENERATE_PATCH_CHARS = 49152;
|
|
5865
|
+
const MAX_SUBJECT_CHARS = 72;
|
|
5866
|
+
/** Past this the first line is not a subject at all; under it, a subject a
|
|
5867
|
+
* few characters over what the prompt asked for is the user's to trim. */
|
|
5868
|
+
const MAX_SUBJECT_KEPT_CHARS = 120;
|
|
5869
|
+
const MAX_PR_TITLE_CHARS = 100;
|
|
5870
|
+
const MAX_RECENT_SUBJECTS = 10;
|
|
5871
|
+
const MAX_PR_COMMITS = 50;
|
|
5855
5872
|
const GIT_TIMEOUT_MS$1 = 15e3;
|
|
5856
5873
|
const REMOTE_TIMEOUT_MS = 6e4;
|
|
5857
5874
|
const GENERATE_TIMEOUT_MS = 6e4;
|
|
@@ -5859,10 +5876,19 @@ const GENERATE_TIMEOUT_MS = 6e4;
|
|
|
5859
5876
|
* use is cost: MCP servers (which `ask` already skips for the same reason,
|
|
5860
5877
|
* and which stall for as long as an unreachable one takes to give up) and the
|
|
5861
5878
|
* user's own hooks and settings. Project settings stay: a repository's commit
|
|
5862
|
-
* conventions belong in the message.
|
|
5863
|
-
*
|
|
5879
|
+
* conventions belong in the message. The prompt itself goes in on stdin:
|
|
5880
|
+
* a 48 KB diff on argv is past what Windows lets a process be started with
|
|
5881
|
+
* (ENAMETOOLONG, and the fallback subject where a message should be).
|
|
5882
|
+
*
|
|
5883
|
+
* Sonnet rather than the session's default: describing a diff needs no
|
|
5884
|
+
* frontier model, but telling three unrelated changes apart in one diff is
|
|
5885
|
+
* where haiku starts to blur them, and sonnet costs only a second or two
|
|
5886
|
+
* more. Extended thinking is off (see {@link GENERATE_ENV}): the naming call
|
|
5887
|
+
* in prompts.ts measured it as most of a ten-second run. */
|
|
5864
5888
|
const GENERATE_ARGUMENTS = [
|
|
5865
5889
|
"-p",
|
|
5890
|
+
"--model",
|
|
5891
|
+
"sonnet",
|
|
5866
5892
|
"--strict-mcp-config",
|
|
5867
5893
|
"--mcp-config",
|
|
5868
5894
|
"{\"mcpServers\":{}}",
|
|
@@ -5873,6 +5899,8 @@ const GENERATE_ARGUMENTS = [
|
|
|
5873
5899
|
"--output-format",
|
|
5874
5900
|
"text"
|
|
5875
5901
|
];
|
|
5902
|
+
/** A CLI that stops honouring the variable is slow again, never wrong. */
|
|
5903
|
+
const GENERATE_ENV = { MAX_THINKING_TOKENS: "0" };
|
|
5876
5904
|
var RepositoryActionError = class extends Error {
|
|
5877
5905
|
code;
|
|
5878
5906
|
commit;
|
|
@@ -5932,11 +5960,82 @@ function fallbackCommitMessage(files) {
|
|
|
5932
5960
|
if (files.length === 1) return `Update ${files[0]?.path ?? "repository files"}`;
|
|
5933
5961
|
return `Update ${files.length} repository files`;
|
|
5934
5962
|
}
|
|
5935
|
-
|
|
5936
|
-
|
|
5937
|
-
|
|
5938
|
-
|
|
5939
|
-
|
|
5963
|
+
/** The model's answer as lines: fences and a trailing prose apology dropped,
|
|
5964
|
+
* carriage returns and NULs (which git and the request validator refuse)
|
|
5965
|
+
* gone, leading blank lines gone. */
|
|
5966
|
+
function answerLines(value) {
|
|
5967
|
+
const kept = value.replace(/\0/gu, "").split(/\r?\n/u).map((line) => line.trimEnd()).filter((line) => !line.trim().startsWith("```"));
|
|
5968
|
+
while (kept.length > 0 && kept[0]?.trim() === "") kept.shift();
|
|
5969
|
+
while (kept.length > 0 && kept.at(-1)?.trim() === "") kept.pop();
|
|
5970
|
+
return kept;
|
|
5971
|
+
}
|
|
5972
|
+
function unquoted(line) {
|
|
5973
|
+
return line.trim().replace(/^['"`]+|['"`]+$/gu, "").trim();
|
|
5974
|
+
}
|
|
5975
|
+
/** Subject line, then a body when the model wrote one: the subject must at
|
|
5976
|
+
* least look like one, the body is kept as the bullets the model listed, and the
|
|
5977
|
+
* whole thing is cut at a line boundary under the commit-message cap. */
|
|
5978
|
+
function normalizeCommitMessage(value, fallback) {
|
|
5979
|
+
const lines = answerLines(value);
|
|
5980
|
+
const subject = unquoted(lines[0] ?? "");
|
|
5981
|
+
if (subject.length === 0 || subject.length > MAX_SUBJECT_KEPT_CHARS) return fallback;
|
|
5982
|
+
const body = lines.slice(1);
|
|
5983
|
+
while (body.length > 0 && body[0]?.trim() === "") body.shift();
|
|
5984
|
+
const kept = [subject];
|
|
5985
|
+
if (body.length > 0) kept.push("");
|
|
5986
|
+
let previousBlank = false;
|
|
5987
|
+
for (const line of body) {
|
|
5988
|
+
const blank = line.trim() === "";
|
|
5989
|
+
if (blank && previousBlank) continue;
|
|
5990
|
+
previousBlank = blank;
|
|
5991
|
+
if ([...kept, line].join("\n").length > MAX_MESSAGE_CHARS) break;
|
|
5992
|
+
kept.push(line);
|
|
5993
|
+
}
|
|
5994
|
+
while (kept.length > 1 && kept.at(-1)?.trim() === "") kept.pop();
|
|
5995
|
+
return kept.join("\n");
|
|
5996
|
+
}
|
|
5997
|
+
/** `Title:` on its own line, then the `Summary:` / `Changes:` body the
|
|
5998
|
+
* create-pr arm insists on. Anything else is the caller's fallback. */
|
|
5999
|
+
function parsePullRequestText(value) {
|
|
6000
|
+
const lines = answerLines(value);
|
|
6001
|
+
const titleAt = lines.findIndex((line) => /^title:/iu.test(line.trim()));
|
|
6002
|
+
if (titleAt < 0) return void 0;
|
|
6003
|
+
const title = unquoted(lines[titleAt].trim().replace(/^title:/iu, ""));
|
|
6004
|
+
if (title.length === 0 || title.length > MAX_PR_TITLE_CHARS) return void 0;
|
|
6005
|
+
const summaryAt = lines.findIndex((line, index) => index > titleAt && /^summary:/iu.test(line.trim()));
|
|
6006
|
+
if (summaryAt < 0) return void 0;
|
|
6007
|
+
const body = lines.slice(summaryAt).map((line) => line.trim() === "" ? "" : line).join("\n").trim();
|
|
6008
|
+
return validPullRequestBody(body) && body.length <= MAX_PR_TEXT_CHARS ? {
|
|
6009
|
+
title,
|
|
6010
|
+
body
|
|
6011
|
+
} : void 0;
|
|
6012
|
+
}
|
|
6013
|
+
function fallbackPullRequestText(commits, files) {
|
|
6014
|
+
const title = commits[0]?.subject ?? fallbackCommitMessage(files);
|
|
6015
|
+
const changes = commits.length > 0 ? commits.map((commit) => commit.subject) : files.map((file) => `Update ${file.path}`);
|
|
6016
|
+
return {
|
|
6017
|
+
title,
|
|
6018
|
+
body: `Summary: ${title}\n\nChanges:\n${(changes.length > 0 ? changes : [title]).map((item) => `- ${item}`).join("\n")}`
|
|
6019
|
+
};
|
|
6020
|
+
}
|
|
6021
|
+
function parseBranchCommits(output) {
|
|
6022
|
+
return output.split("\0").flatMap((record) => {
|
|
6023
|
+
const [subject = "", ...rest] = record.replace(/^\r?\n/u, "").split(/\r?\n/u);
|
|
6024
|
+
if (subject.trim().length === 0) return [];
|
|
6025
|
+
return [{
|
|
6026
|
+
subject: subject.trim().slice(0, 140),
|
|
6027
|
+
body: rest.join("\n").trim()
|
|
6028
|
+
}];
|
|
6029
|
+
}).slice(0, MAX_PR_COMMITS);
|
|
6030
|
+
}
|
|
6031
|
+
function boundedPatch(patch) {
|
|
6032
|
+
return patch.length > MAX_GENERATE_PATCH_CHARS ? {
|
|
6033
|
+
text: patch.slice(0, MAX_GENERATE_PATCH_CHARS),
|
|
6034
|
+
truncated: true
|
|
6035
|
+
} : {
|
|
6036
|
+
text: patch,
|
|
6037
|
+
truncated: false
|
|
6038
|
+
};
|
|
5940
6039
|
}
|
|
5941
6040
|
function validPrUrl(value) {
|
|
5942
6041
|
try {
|
|
@@ -5973,19 +6072,126 @@ var RepositoryActionService = class {
|
|
|
5973
6072
|
const preview = await this.#preview(cwd);
|
|
5974
6073
|
if (preview.fingerprint !== fingerprint) throw new RepositoryActionError("repository-changed", "Repository changes have changed. Refresh the commit panel.");
|
|
5975
6074
|
const fallback = fallbackCommitMessage(preview.files);
|
|
6075
|
+
const git = await this.#git();
|
|
6076
|
+
const recent = await this.#run(git, [
|
|
6077
|
+
"log",
|
|
6078
|
+
"--no-merges",
|
|
6079
|
+
"--format=%s",
|
|
6080
|
+
"-n",
|
|
6081
|
+
String(MAX_RECENT_SUBJECTS),
|
|
6082
|
+
"HEAD",
|
|
6083
|
+
"--"
|
|
6084
|
+
], preview.root, GIT_TIMEOUT_MS$1);
|
|
6085
|
+
const subjects = recent.exitCode === 0 && !recent.lossy ? recent.stdout.split(/\r?\n/u).map((line) => line.trim()).filter((line) => line.length > 0) : [];
|
|
6086
|
+
const patch = boundedPatch(preview.patch);
|
|
5976
6087
|
const prompt = [
|
|
5977
|
-
"Write
|
|
5978
|
-
|
|
6088
|
+
"Write a git commit message for the changes below, in English.",
|
|
6089
|
+
`Line 1 is the subject: imperative mood, at most ${MAX_SUBJECT_CHARS} characters, saying what the change does rather than which files it touches.`,
|
|
6090
|
+
"If the diff contains more than one independent change, leave line 2 blank and then list each change on its own line starting with \"- \", one sentence each, describing what changed as the diff shows it.",
|
|
6091
|
+
"A change with a single purpose gets the subject line only.",
|
|
6092
|
+
"Describe only what the diff shows. Do not invent motivation, do not summarise the file list, and do not mention that the diff is truncated.",
|
|
6093
|
+
"Return only the message: no quotes, no markdown fences, no explanation before or after it.",
|
|
6094
|
+
...subjects.length > 0 ? [`Recent commit subjects of this repository, as a style reference only:\n${subjects.map((subject) => `- ${subject}`).join("\n")}`] : [],
|
|
5979
6095
|
`Files: ${preview.files.map((file) => file.path).join(", ")}`,
|
|
5980
|
-
|
|
6096
|
+
...patch.truncated || preview.truncated ? ["The diff below is cut short; the file list above is complete."] : [],
|
|
6097
|
+
`Diff:\n${patch.text}`
|
|
6098
|
+
].join("\n");
|
|
6099
|
+
try {
|
|
6100
|
+
const result = await this.#run(this.#claudeExecutable, GENERATE_ARGUMENTS, preview.root, GENERATE_TIMEOUT_MS, MAX_OUTPUT_BYTES$1, GENERATE_ENV, prompt);
|
|
6101
|
+
return result.exitCode === 0 && !result.lossy ? normalizeCommitMessage(result.stdout, fallback) : fallback;
|
|
6102
|
+
} catch {
|
|
6103
|
+
return fallback;
|
|
6104
|
+
}
|
|
6105
|
+
}
|
|
6106
|
+
/** Title and description for the pull request the branch would open: the
|
|
6107
|
+
* commits and diff since the base, plus whatever is still uncommitted,
|
|
6108
|
+
* since create-pr commits that first. Base is the named branch on origin,
|
|
6109
|
+
* else origin's default; with neither, the tree alone. */
|
|
6110
|
+
async generatePullRequest(cwd, fingerprint, baseBranch) {
|
|
6111
|
+
const preview = await this.#preview(cwd);
|
|
6112
|
+
if (preview.fingerprint !== fingerprint) throw new RepositoryActionError("repository-changed", "Repository changes have changed. Refresh the commit panel.");
|
|
6113
|
+
const git = await this.#git();
|
|
6114
|
+
const base = await this.#baseRef(git, preview.root, baseBranch);
|
|
6115
|
+
let commits = [];
|
|
6116
|
+
let branchPatch = "";
|
|
6117
|
+
let branchTruncated = false;
|
|
6118
|
+
if (base !== void 0) {
|
|
6119
|
+
const log = await this.#run(git, [
|
|
6120
|
+
"log",
|
|
6121
|
+
"--no-merges",
|
|
6122
|
+
"--format=%s%n%b%x00",
|
|
6123
|
+
"-n",
|
|
6124
|
+
String(51),
|
|
6125
|
+
`${base}..HEAD`,
|
|
6126
|
+
"--"
|
|
6127
|
+
], preview.root, GIT_TIMEOUT_MS$1);
|
|
6128
|
+
if (log.exitCode === 0 && !log.lossy) commits = parseBranchCommits(log.stdout);
|
|
6129
|
+
const funcname = await diffFuncnameArgs();
|
|
6130
|
+
const diff = await this.#run(git, [
|
|
6131
|
+
...funcname,
|
|
6132
|
+
"diff",
|
|
6133
|
+
"--no-ext-diff",
|
|
6134
|
+
"--no-color",
|
|
6135
|
+
"--unified=3",
|
|
6136
|
+
`${base}...HEAD`,
|
|
6137
|
+
"--",
|
|
6138
|
+
":(exclude)WARP.md",
|
|
6139
|
+
":(exclude)**/WARP.md"
|
|
6140
|
+
], preview.root, GIT_TIMEOUT_MS$1, MAX_OUTPUT_BYTES$1);
|
|
6141
|
+
if (diff.exitCode === 0) {
|
|
6142
|
+
branchPatch = diff.stdout;
|
|
6143
|
+
branchTruncated = diff.lossy;
|
|
6144
|
+
}
|
|
6145
|
+
}
|
|
6146
|
+
const fallback = fallbackPullRequestText(commits, preview.files);
|
|
6147
|
+
const patch = boundedPatch([branchPatch, preview.patch].filter((part) => part.length > 0).join("\n"));
|
|
6148
|
+
const prompt = [
|
|
6149
|
+
"Write the title and description of a GitHub pull request for the changes below, in English.",
|
|
6150
|
+
"Answer in exactly this shape and nothing else:",
|
|
6151
|
+
`Title: <imperative title, at most ${MAX_SUBJECT_CHARS} characters, saying what the pull request does>`,
|
|
6152
|
+
"Summary: <one sentence saying what the pull request achieves as a whole>",
|
|
6153
|
+
"",
|
|
6154
|
+
"Changes:",
|
|
6155
|
+
"- <one line per independent change, describing what changed in the code>",
|
|
6156
|
+
"",
|
|
6157
|
+
"List every independent change the diff contains as its own bullet. Do not merge unrelated changes into one bullet, and do not describe anything the diff does not show.",
|
|
6158
|
+
"No markdown headings, no quotes, no fences, no text before \"Title:\" or after the last bullet.",
|
|
6159
|
+
...commits.length > 0 ? [`Commits on this branch, newest first:\n${commits.map((commit) => commit.body.length > 0 ? `- ${commit.subject}\n${commit.body}` : `- ${commit.subject}`).join("\n")}`] : [],
|
|
6160
|
+
...preview.files.length > 0 ? [`Uncommitted files that will go into the same pull request: ${preview.files.map((file) => file.path).join(", ")}`] : [],
|
|
6161
|
+
...patch.truncated || branchTruncated || preview.truncated ? ["The diff below is cut short; the commit list is complete."] : [],
|
|
6162
|
+
`Diff:\n${patch.text}`
|
|
5981
6163
|
].join("\n");
|
|
5982
6164
|
try {
|
|
5983
|
-
const result = await this.#run(this.#claudeExecutable, GENERATE_ARGUMENTS
|
|
5984
|
-
return result.exitCode === 0 && !result.lossy ?
|
|
6165
|
+
const result = await this.#run(this.#claudeExecutable, GENERATE_ARGUMENTS, preview.root, GENERATE_TIMEOUT_MS, MAX_OUTPUT_BYTES$1, GENERATE_ENV, prompt);
|
|
6166
|
+
return (result.exitCode === 0 && !result.lossy ? parsePullRequestText(result.stdout) : void 0) ?? fallback;
|
|
5985
6167
|
} catch {
|
|
5986
6168
|
return fallback;
|
|
5987
6169
|
}
|
|
5988
6170
|
}
|
|
6171
|
+
/** `origin/<branch>` when the named branch exists on origin, else the branch
|
|
6172
|
+
* origin's HEAD points at. Neither on a checkout that never fetched. */
|
|
6173
|
+
async #baseRef(git, root, baseBranch) {
|
|
6174
|
+
const named = baseBranch?.trim() ?? "";
|
|
6175
|
+
if (named.length > 0) {
|
|
6176
|
+
if (/[\0\r\n\s]|\.\.|^-/u.test(named)) return void 0;
|
|
6177
|
+
const verified = await this.#run(git, [
|
|
6178
|
+
"rev-parse",
|
|
6179
|
+
"--verify",
|
|
6180
|
+
"--quiet",
|
|
6181
|
+
"--symbolic-full-name",
|
|
6182
|
+
`refs/remotes/origin/${named}`
|
|
6183
|
+
], root, GIT_TIMEOUT_MS$1);
|
|
6184
|
+
return verified.exitCode === 0 && !verified.lossy && verified.stdout.trim() === `refs/remotes/origin/${named}` ? `origin/${named}` : void 0;
|
|
6185
|
+
}
|
|
6186
|
+
const head = await this.#run(git, [
|
|
6187
|
+
"symbolic-ref",
|
|
6188
|
+
"--quiet",
|
|
6189
|
+
"--short",
|
|
6190
|
+
"refs/remotes/origin/HEAD"
|
|
6191
|
+
], root, GIT_TIMEOUT_MS$1);
|
|
6192
|
+
const ref = head.stdout.trim();
|
|
6193
|
+
return head.exitCode === 0 && !head.lossy && /^origin\/[^\s]+$/u.test(ref) ? ref : void 0;
|
|
6194
|
+
}
|
|
5989
6195
|
execute(cwd, request) {
|
|
5990
6196
|
const operation = this.#pending.then(() => this.#execute(cwd, request));
|
|
5991
6197
|
this.#pending = operation.then(() => void 0, () => void 0);
|
|
@@ -6378,18 +6584,18 @@ var RepositoryActionService = class {
|
|
|
6378
6584
|
if (result.exitCode !== 0 || result.lossy) throw new RepositoryActionError(code, message);
|
|
6379
6585
|
return result;
|
|
6380
6586
|
}
|
|
6381
|
-
#run(executable, args, cwd, timeoutMs, maxBytes = MAX_OUTPUT_BYTES$1) {
|
|
6587
|
+
#run(executable, args, cwd, timeoutMs, maxBytes = MAX_OUTPUT_BYTES$1, env = {}, stdin) {
|
|
6382
6588
|
return collect$1(this.#runtime.spawn({
|
|
6383
6589
|
argv: [executable, ...args],
|
|
6384
6590
|
cwd,
|
|
6385
6591
|
stdio: {
|
|
6386
|
-
stdin: "ignore",
|
|
6592
|
+
stdin: stdin === void 0 ? "ignore" : { data: stdin },
|
|
6387
6593
|
stdout: { maxBytes },
|
|
6388
6594
|
stderr: { maxBytes: MAX_OUTPUT_BYTES$1 }
|
|
6389
6595
|
},
|
|
6390
6596
|
graceMs: 1e3,
|
|
6391
6597
|
signal: AbortSignal.timeout(timeoutMs),
|
|
6392
|
-
env
|
|
6598
|
+
env
|
|
6393
6599
|
}));
|
|
6394
6600
|
}
|
|
6395
6601
|
};
|
|
@@ -6648,6 +6854,17 @@ function registerRepositoryActionRoute(ctx, service, cwdForSession) {
|
|
|
6648
6854
|
value: { message: await service.generateMessage(cwd, string$1(input, "fingerprint")) }
|
|
6649
6855
|
};
|
|
6650
6856
|
}
|
|
6857
|
+
if (url.pathname === `/plugins/dsh-claude/repository/action/pull-request`) {
|
|
6858
|
+
if (io.method !== "POST") return {
|
|
6859
|
+
status: 405,
|
|
6860
|
+
value: { error: "method not allowed" }
|
|
6861
|
+
};
|
|
6862
|
+
const input = await readJson$5(io);
|
|
6863
|
+
return {
|
|
6864
|
+
status: 200,
|
|
6865
|
+
value: await service.generatePullRequest(cwd, string$1(input, "fingerprint"), optionalString(input, "baseBranch"))
|
|
6866
|
+
};
|
|
6867
|
+
}
|
|
6651
6868
|
if (url.pathname === "/plugins/dsh-claude/repository/action") {
|
|
6652
6869
|
if (io.method !== "POST") return {
|
|
6653
6870
|
status: 405,
|
|
@@ -8762,6 +8979,13 @@ async function isDirectory(path) {
|
|
|
8762
8979
|
}
|
|
8763
8980
|
/** Repository roots behind the touched paths, minus the session's own, in
|
|
8764
8981
|
* first-seen order. `rootOf` answers undefined outside any repository. */
|
|
8982
|
+
/** Whether `child` lies strictly under `parent`. Compared with one separator:
|
|
8983
|
+
* on Windows git prints forward slashes where Node resolves to backslashes,
|
|
8984
|
+
* and a root can arrive in either form. */
|
|
8985
|
+
function isInside(child, parent) {
|
|
8986
|
+
const slashed = (value) => value.replaceAll("\\", "/").replace(/\/+$/u, "");
|
|
8987
|
+
return slashed(child).startsWith(`${slashed(parent)}/`);
|
|
8988
|
+
}
|
|
8765
8989
|
async function touchedRepositoryRoots(paths, sessionRoot, rootOf, max, directory = isDirectory) {
|
|
8766
8990
|
const roots = [];
|
|
8767
8991
|
const directories = /* @__PURE__ */ new Set();
|
|
@@ -8769,7 +8993,7 @@ async function touchedRepositoryRoots(paths, sessionRoot, rootOf, max, directory
|
|
|
8769
8993
|
for (const directory of directories) {
|
|
8770
8994
|
const root = await rootOf(directory);
|
|
8771
8995
|
if (root === void 0 || root === sessionRoot || roots.includes(root)) continue;
|
|
8772
|
-
if (sessionRoot
|
|
8996
|
+
if (isInside(sessionRoot, root)) continue;
|
|
8773
8997
|
roots.push(root);
|
|
8774
8998
|
if (roots.length >= max) break;
|
|
8775
8999
|
}
|