@exreve/exk 1.0.50 → 1.0.51

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.
@@ -88,26 +88,59 @@ const CACHED_CLAUDE_PATH = (() => {
88
88
  // Promisify symlink for async use
89
89
  const symlinkAsync = promisify(fsSymlink);
90
90
  // Helper function to extract tool name from result structure
91
+ /**
92
+ * Detect tool name from the shape of tool_use_result.
93
+ *
94
+ * Uses discriminating keys from the SDK's own type definitions
95
+ * (sdk-tools.d.ts: BashOutput, GrepOutput, GlobOutput, FileReadOutput,
96
+ * FileEditOutput, FileWriteOutput, TodoWriteOutput, etc.)
97
+ */
91
98
  function extractToolName(toolResult) {
92
- if (toolResult.name)
93
- return toolResult.name;
94
- if (toolResult.type === 'text' && toolResult.file)
99
+ if (!toolResult || typeof toolResult !== 'object')
100
+ return 'unknown';
101
+ // ── Read (FileReadOutput): {type: 'text'|'image'|'notebook'|'pdf'|'parts'|'file_unchanged', file: {...}}
102
+ if (toolResult.file && typeof toolResult.file === 'object'
103
+ && ['text', 'image', 'notebook', 'pdf', 'parts', 'file_unchanged'].includes(toolResult.type)) {
95
104
  return 'Read';
96
- if (toolResult.file_path || toolResult.filePath) {
97
- // Has old_string/new_string → Edit; has content/create → Write; else Read
98
- if (toolResult.old_string !== undefined && toolResult.new_string !== undefined)
99
- return 'Edit';
100
- return (toolResult.content !== undefined || toolResult.type === 'create') ? 'Write' : 'Read';
101
105
  }
102
- // Grep: {mode, numFiles, filenames, content, numLines}
103
- if (toolResult.numFiles !== undefined && toolResult.filenames !== undefined && toolResult.mode !== undefined)
106
+ // ── Edit (FileEditOutput): {filePath, oldString, newString, structuredPatch}
107
+ if (toolResult.filePath && toolResult.oldString !== undefined && toolResult.newString !== undefined
108
+ && Array.isArray(toolResult.structuredPatch)) {
109
+ return 'Edit';
110
+ }
111
+ // ── Write (FileWriteOutput): {type: 'create'|'update', filePath, content, structuredPatch}
112
+ if (toolResult.filePath && (toolResult.type === 'create' || toolResult.type === 'update')
113
+ && toolResult.content !== undefined) {
114
+ return 'Write';
115
+ }
116
+ // ── Grep (GrepOutput): {mode, numFiles, filenames, content?, numLines?}
117
+ if (typeof toolResult.numFiles === 'number' && Array.isArray(toolResult.filenames)
118
+ && toolResult.mode !== undefined) {
104
119
  return 'Grep';
105
- // Glob: {type, pattern, files}
106
- if (toolResult.files !== undefined && toolResult.pattern !== undefined && !toolResult.stdout)
120
+ }
121
+ // ── Glob (GlobOutput): {durationMs, numFiles, filenames, truncated}
122
+ if (typeof toolResult.numFiles === 'number' && Array.isArray(toolResult.filenames)
123
+ && typeof toolResult.durationMs === 'number' && toolResult.truncated !== undefined) {
107
124
  return 'Glob';
108
- if (toolResult.stdout !== undefined || toolResult.stderr !== undefined)
125
+ }
126
+ // ── TodoWrite (TodoWriteOutput): {oldTodos, newTodos}
127
+ if (Array.isArray(toolResult.oldTodos) && Array.isArray(toolResult.newTodos)) {
128
+ return 'TodoWrite';
129
+ }
130
+ // ── Bash (BashOutput): {stdout, stderr, interrupted, ...}
131
+ if (toolResult.stdout !== undefined || toolResult.stderr !== undefined) {
109
132
  return 'Bash';
110
- // send_file tool: content is JSON with _type marker — detect before Bash fallback
133
+ }
134
+ // ── WebSearch (WebSearchOutput): {query, results}
135
+ if (typeof toolResult.query === 'string' && Array.isArray(toolResult.results)) {
136
+ return 'WebSearch';
137
+ }
138
+ // ── WebFetch (WebFetchOutput): {url, result, code, bytes}
139
+ if (typeof toolResult.url === 'string' && typeof toolResult.result === 'string'
140
+ && typeof toolResult.code === 'number') {
141
+ return 'WebFetch';
142
+ }
143
+ // ── send_file (custom MCP tool): content is JSON with _type marker
111
144
  if (toolResult.content && typeof toolResult.content === 'string' && toolResult.type === 'text') {
112
145
  try {
113
146
  const parsed = JSON.parse(toolResult.content);
@@ -118,6 +151,10 @@ function extractToolName(toolResult) {
118
151
  // SDK 0.2.x: content-only results from nested tool calls (no stdout/stderr wrapper)
119
152
  return 'Bash';
120
153
  }
154
+ // ── Agent/Task output: {agentId, content, status}
155
+ if (toolResult.agentId && Array.isArray(toolResult.content) && toolResult.status) {
156
+ return 'Task';
157
+ }
121
158
  return 'unknown';
122
159
  }
123
160
  // Look up tool name from the most recent assistant message's tool_use blocks by tool_use_id
Binary file
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@exreve/exk",
3
- "version": "1.0.50",
3
+ "version": "1.0.51",
4
4
  "description": "exk - Control Claude CLI with voice and programmable interfaces",
5
5
  "type": "module",
6
6
  "bin": {