@vibe-forge/client 0.10.1 → 0.11.0

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.
Files changed (109) hide show
  1. package/dist/assets/{arc-C1rWFTer.js → arc-M4HYfcHs.js} +1 -1
  2. package/dist/assets/{blockDiagram-c4efeb88-DlZ9x70F.js → blockDiagram-c4efeb88-CUrDjrxj.js} +1 -1
  3. package/dist/assets/{c4Diagram-c83219d4-BKKxi__y.js → c4Diagram-c83219d4-BMEtqlFp.js} +1 -1
  4. package/dist/assets/channel-Cj3Cp2OJ.js +1 -0
  5. package/dist/assets/{classDiagram-beda092f-CVGPySZq.js → classDiagram-beda092f-BOmDJ0Ml.js} +1 -1
  6. package/dist/assets/{classDiagram-v2-2358418a-7kp8GVVj.js → classDiagram-v2-2358418a-BODzX2MB.js} +1 -1
  7. package/dist/assets/clone-B7Q9B1dS.js +1 -0
  8. package/dist/assets/{createText-1719965b-Dykv8kT9.js → createText-1719965b-B9Dd8zcR.js} +1 -1
  9. package/dist/assets/{cssMode-B59COYVW.js → cssMode-DLxG92Ot.js} +1 -1
  10. package/dist/assets/{edges-96097737-CkZ1ZBro.js → edges-96097737-CuZFd43m.js} +1 -1
  11. package/dist/assets/{erDiagram-0228fc6a-281ADcRp.js → erDiagram-0228fc6a-8g9lu2-Z.js} +1 -1
  12. package/dist/assets/{flowDb-c6c81e3f-BQjX_flP.js → flowDb-c6c81e3f-BlBS1tdN.js} +1 -1
  13. package/dist/assets/{flowDiagram-50d868cf-DMHZTjES.js → flowDiagram-50d868cf-u6mWflpF.js} +1 -1
  14. package/dist/assets/flowDiagram-v2-4f6560a1-G3v545eF.js +1 -0
  15. package/dist/assets/{flowchart-elk-definition-6af322e1-CI3yz4z8.js → flowchart-elk-definition-6af322e1-BDqI2NFr.js} +1 -1
  16. package/dist/assets/{freemarker2-DWnWjibn.js → freemarker2-tVtpTMPu.js} +1 -1
  17. package/dist/assets/{ganttDiagram-a2739b55-B3IING9L.js → ganttDiagram-a2739b55-CDQjx9Wu.js} +1 -1
  18. package/dist/assets/{gitGraphDiagram-82fe8481-CnArIr_T.js → gitGraphDiagram-82fe8481-DUHFKRVA.js} +1 -1
  19. package/dist/assets/{graph-BZ1F0Yve.js → graph-2HKPi5B_.js} +1 -1
  20. package/dist/assets/{handlebars-C1QH9qTz.js → handlebars-D00tgNd8.js} +1 -1
  21. package/dist/assets/{html-D1NkqHjC.js → html-B-TDzBiR.js} +1 -1
  22. package/dist/assets/{htmlMode-DAZCE_rA.js → htmlMode-ClycqSTM.js} +1 -1
  23. package/dist/assets/{index-5325376f-Da9zSHjA.js → index-5325376f-DPrJpRQ-.js} +1 -1
  24. package/dist/assets/{index-C0vjF3D0.js → index-CAHZZEoo.js} +319 -323
  25. package/dist/assets/{index-vzEbM21t.css → index-Di7lePfb.css} +1 -1
  26. package/dist/assets/{infoDiagram-8eee0895-DYbFvRM7.js → infoDiagram-8eee0895-Co5tS1I5.js} +1 -1
  27. package/dist/assets/{javascript-CoMjGRHa.js → javascript-zbkwarmb.js} +1 -1
  28. package/dist/assets/{journeyDiagram-c64418c1-Boebox0b.js → journeyDiagram-c64418c1-k_qioHgy.js} +1 -1
  29. package/dist/assets/{jsonMode-D__gAvuz.js → jsonMode-C3CSpzBF.js} +1 -1
  30. package/dist/assets/{layout-CTcHNbHp.js → layout-CjOXKxvs.js} +1 -1
  31. package/dist/assets/{line-4AwinCz2.js → line-C-XnQrKR.js} +1 -1
  32. package/dist/assets/{linear-CeSMLzJW.js → linear-C7MMERzS.js} +1 -1
  33. package/dist/assets/{liquid-DZF6egdE.js → liquid-5G37EU6K.js} +1 -1
  34. package/dist/assets/{lspLanguageFeatures-6K4lv5S2.js → lspLanguageFeatures-zaDMuhCE.js} +1 -1
  35. package/dist/assets/{mdx-Cnt4ka6w.js → mdx-Bc-LY0gi.js} +1 -1
  36. package/dist/assets/{mermaid.core-B0yG5s4D.js → mermaid.core-CechbHof.js} +4 -4
  37. package/dist/assets/{mindmap-definition-8da855dc-KJEvXMKj.js → mindmap-definition-8da855dc-ejftCDGb.js} +1 -1
  38. package/dist/assets/{pieDiagram-a8764435-17nFAXPJ.js → pieDiagram-a8764435-DY__X3Qj.js} +1 -1
  39. package/dist/assets/{python-DA3TtjDv.js → python-vK2Ff2J5.js} +1 -1
  40. package/dist/assets/{quadrantDiagram-1e28029f-Dt4vubi-.js → quadrantDiagram-1e28029f-azIZCv_2.js} +1 -1
  41. package/dist/assets/{razor-CWDJgvX_.js → razor-BipjBJKu.js} +1 -1
  42. package/dist/assets/{requirementDiagram-08caed73-H6aDyDK-.js → requirementDiagram-08caed73-C4EB0Xs2.js} +1 -1
  43. package/dist/assets/{sankeyDiagram-a04cb91d-DxsVtbjI.js → sankeyDiagram-a04cb91d-PNhR6YWu.js} +1 -1
  44. package/dist/assets/{sequenceDiagram-c5b8d532-BHa148XJ.js → sequenceDiagram-c5b8d532-4c-qV-Ri.js} +1 -1
  45. package/dist/assets/{stateDiagram-1ecb1508-DgwBm8LO.js → stateDiagram-1ecb1508-CnURumPE.js} +1 -1
  46. package/dist/assets/{stateDiagram-v2-c2b004d7-BK7IQLVc.js → stateDiagram-v2-c2b004d7-DR2qHTPg.js} +1 -1
  47. package/dist/assets/{styles-b4e223ce-DzW27Bc-.js → styles-b4e223ce-B2PWXT_i.js} +1 -1
  48. package/dist/assets/{styles-ca3715f6-Dex2GiLT.js → styles-ca3715f6-DEhgVF5H.js} +1 -1
  49. package/dist/assets/{styles-d45a18b0-B6fGtDKS.js → styles-d45a18b0-DyzccA5F.js} +1 -1
  50. package/dist/assets/{svgDrawCommon-b86b1483-B4HYgfV5.js → svgDrawCommon-b86b1483-C_1tMhxp.js} +1 -1
  51. package/dist/assets/{timeline-definition-faaaa080--QSbWb25.js → timeline-definition-faaaa080-FdaC0dQH.js} +1 -1
  52. package/dist/assets/{tsMode-ZM7ocZCH.js → tsMode-CrMC5T3_.js} +1 -1
  53. package/dist/assets/{typescript-CKWDmBCc.js → typescript-CRfPu8v7.js} +1 -1
  54. package/dist/assets/{xml-DuEUAzPi.js → xml-jlRvQfFI.js} +1 -1
  55. package/dist/assets/{xychartDiagram-f5964ef8-D09Zkv2K.js → xychartDiagram-f5964ef8-sxjv75h9.js} +1 -1
  56. package/dist/assets/{yaml-DL7QPRYk.js → yaml-B47_IHOH.js} +1 -1
  57. package/dist/index.html +2 -2
  58. package/package.json +10 -10
  59. package/src/components/chat/ChatHistoryView.tsx +22 -13
  60. package/src/components/chat/messages/MessageStatusNotice.scss +163 -0
  61. package/src/components/chat/messages/MessageStatusNotice.tsx +48 -0
  62. package/src/components/chat/messages/build-chat-history-status-notices.ts +138 -0
  63. package/src/components/chat/sender/@components/sender-body/SenderBody.tsx +0 -24
  64. package/src/components/chat/sender/@core/build-sender-controller-result.ts +0 -6
  65. package/src/components/chat/sender/@hooks/use-sender-controller.ts +0 -2
  66. package/src/components/chat/sender/@types/sender-props.ts +0 -3
  67. package/src/components/chat/sender/Sender.scss +0 -58
  68. package/src/components/chat/sender/Sender.tsx +0 -2
  69. package/src/components/chat/tools/DefaultTool.tsx +74 -207
  70. package/src/components/chat/tools/adapter-claude/ClaudeEditDiff.tsx +30 -0
  71. package/src/components/chat/tools/adapter-claude/GenericClaudeTool.scss +128 -0
  72. package/src/components/chat/tools/adapter-claude/GenericClaudeTool.tsx +133 -0
  73. package/src/components/chat/tools/adapter-claude/claude-tool-edit-builders.ts +102 -0
  74. package/src/components/chat/tools/adapter-claude/claude-tool-field-sections.tsx +168 -0
  75. package/src/components/chat/tools/adapter-claude/claude-tool-operation-builders.ts +135 -0
  76. package/src/components/chat/tools/adapter-claude/claude-tool-presentation.ts +61 -0
  77. package/src/components/chat/tools/adapter-claude/claude-tool-shared.ts +185 -0
  78. package/src/components/chat/tools/adapter-claude/claude-tool-summary.ts +76 -0
  79. package/src/components/chat/tools/adapter-claude/claude-tool-system-builders.ts +125 -0
  80. package/src/components/chat/tools/adapter-claude/claude-tool-task-builders.ts +148 -0
  81. package/src/components/chat/tools/adapter-claude/index.ts +24 -15
  82. package/src/components/chat/tools/core/ToolCallBox.scss +344 -36
  83. package/src/components/chat/tools/core/ToolCallBox.tsx +35 -13
  84. package/src/components/chat/tools/core/ToolDiffViewer.scss +138 -0
  85. package/src/components/chat/tools/core/ToolDiffViewer.tsx +180 -0
  86. package/src/components/chat/tools/core/ToolGroup.scss +52 -74
  87. package/src/components/chat/tools/core/ToolGroup.tsx +25 -40
  88. package/src/components/chat/tools/core/ToolRenderer.tsx +3 -3
  89. package/src/components/chat/tools/core/ToolResultContent.tsx +66 -0
  90. package/src/components/chat/tools/core/ToolSummaryHeader.tsx +67 -0
  91. package/src/components/chat/tools/core/tool-content-presence.ts +57 -0
  92. package/src/components/chat/tools/core/tool-display.ts +192 -0
  93. package/src/components/chat/tools/core/tool-result-content-utils.ts +171 -0
  94. package/src/components/chat/tools/core/tool-summary.ts +194 -0
  95. package/src/components/chat/tools/plugin-chrome-devtools/ChromeDevtoolsTool.tsx +66 -53
  96. package/src/components/chat/tools/task/GetTaskInfoTool.tsx +26 -9
  97. package/src/components/chat/tools/task/ListTasksTool.tsx +22 -9
  98. package/src/components/chat/tools/task/StartTasksTool.tsx +22 -9
  99. package/src/hooks/chat/interaction-state.ts +29 -9
  100. package/src/hooks/chat/use-chat-scroll.ts +2 -2
  101. package/src/hooks/chat/use-chat-session-messages.ts +24 -18
  102. package/src/hooks/chat/use-chat-session.ts +2 -2
  103. package/src/resources/locales/en.json +81 -0
  104. package/src/resources/locales/zh.json +81 -0
  105. package/src/routes/ChatRoute.tsx +24 -27
  106. package/src/utils/strip-ansi.ts +26 -0
  107. package/dist/assets/channel-F1aqMANO.js +0 -1
  108. package/dist/assets/clone-B-GCuXNo.js +0 -1
  109. package/dist/assets/flowDiagram-v2-4f6560a1-C5FzdVl1.js +0 -1
@@ -0,0 +1,168 @@
1
+ import { Tooltip } from 'antd'
2
+ import React from 'react'
3
+
4
+ import { CodeBlock } from '#~/components/CodeBlock'
5
+ import { safeJsonStringify } from '#~/utils/safe-serialize'
6
+
7
+ import { TOOL_TOOLTIP_PROPS, getToolFieldIcon, getToolInlineValueText, getToolValueText } from '../core/tool-display'
8
+ import type { ClaudeToolField, ClaudeToolQuestion } from './claude-tool-presentation'
9
+
10
+ type Translate = (key: string, options?: Record<string, unknown>) => string
11
+
12
+ const getFieldKey = (field: ClaudeToolField, index: number) => `${field.labelKey}-${index}`
13
+
14
+ const getSectionHeader = (icon: string, label: string) => (
15
+ <div className='tool-detail-section__header'>
16
+ <Tooltip title={label} {...TOOL_TOOLTIP_PROPS}>
17
+ <span className='tool-detail-section__icon material-symbols-rounded'>{icon}</span>
18
+ </Tooltip>
19
+ </div>
20
+ )
21
+
22
+ export function ClaudeToolInlineFields({
23
+ fields,
24
+ t
25
+ }: {
26
+ fields: ClaudeToolField[]
27
+ t: Translate
28
+ }) {
29
+ if (fields.length === 0) {
30
+ return null
31
+ }
32
+
33
+ return (
34
+ <div
35
+ className='tool-inline-token-list tool-inline-token-list--standalone'
36
+ aria-label={t('chat.tools.fields.details')}
37
+ >
38
+ {fields.map((field, index) => {
39
+ const label = t(field.labelKey, { defaultValue: field.fallbackLabel })
40
+ const valueText = getToolInlineValueText(field.value)
41
+ return (
42
+ <Tooltip
43
+ key={getFieldKey(field, index)}
44
+ title={
45
+ <div className='tool-tooltip-content'>
46
+ <div className='tool-tooltip-content__title'>{label}</div>
47
+ <div className='tool-tooltip-content__value'>{getToolValueText(field.value)}</div>
48
+ </div>
49
+ }
50
+ {...TOOL_TOOLTIP_PROPS}
51
+ >
52
+ <div className='tool-inline-token'>
53
+ <span className='tool-inline-token__icon material-symbols-rounded'>
54
+ {getToolFieldIcon(field.labelKey, field.format)}
55
+ </span>
56
+ <span className='tool-inline-token__value'>{valueText}</span>
57
+ </div>
58
+ </Tooltip>
59
+ )
60
+ })}
61
+ </div>
62
+ )
63
+ }
64
+
65
+ export function renderClaudeBlockField(field: ClaudeToolField, index: number, t: Translate) {
66
+ const label = t(field.labelKey, { defaultValue: field.fallbackLabel })
67
+ const sectionHeader = getSectionHeader(getToolFieldIcon(field.labelKey, field.format), label)
68
+
69
+ if (field.format === 'text') {
70
+ return (
71
+ <div className='tool-detail-section claude-generic-tool__section' key={getFieldKey(field, index)}>
72
+ {sectionHeader}
73
+ <div className='tool-detail-section__text'>{String(field.value)}</div>
74
+ </div>
75
+ )
76
+ }
77
+
78
+ if (field.format === 'code') {
79
+ return (
80
+ <div className='tool-detail-section claude-generic-tool__section' key={getFieldKey(field, index)}>
81
+ {sectionHeader}
82
+ <CodeBlock
83
+ code={String(field.value)}
84
+ lang={field.lang ?? 'text'}
85
+ hideHeader={true}
86
+ />
87
+ </div>
88
+ )
89
+ }
90
+
91
+ if (field.format === 'list') {
92
+ const items = Array.isArray(field.value) ? field.value.map(item => String(item)) : []
93
+ return (
94
+ <div className='tool-detail-section claude-generic-tool__section' key={getFieldKey(field, index)}>
95
+ {sectionHeader}
96
+ <div className='claude-generic-tool__list'>
97
+ {items.map(listItem => (
98
+ <div className='claude-generic-tool__list-item' key={listItem}>{listItem}</div>
99
+ ))}
100
+ </div>
101
+ </div>
102
+ )
103
+ }
104
+
105
+ if (field.format === 'questions') {
106
+ const questions = Array.isArray(field.value) ? field.value as ClaudeToolQuestion[] : []
107
+ return (
108
+ <div className='tool-detail-section claude-generic-tool__section' key={getFieldKey(field, index)}>
109
+ {sectionHeader}
110
+ <div className='claude-generic-tool__questions'>
111
+ {questions.map((question, questionIndex) => (
112
+ <div
113
+ className='claude-generic-tool__question'
114
+ key={`${question.header ?? question.question}-${questionIndex}`}
115
+ >
116
+ <div className='claude-generic-tool__question-header'>
117
+ <div className='claude-generic-tool__question-title'>
118
+ {question.header ?? `${label} ${questionIndex + 1}`}
119
+ </div>
120
+ <Tooltip
121
+ title={question.multiSelect
122
+ ? t('chat.tools.multiSelect')
123
+ : t('chat.tools.singleSelect')}
124
+ {...TOOL_TOOLTIP_PROPS}
125
+ >
126
+ <span className='claude-generic-tool__question-mode material-symbols-rounded'>
127
+ {question.multiSelect ? 'checklist' : 'radio_button_checked'}
128
+ </span>
129
+ </Tooltip>
130
+ </div>
131
+ <div
132
+ className='tool-detail-section__text'
133
+ title={question.question}
134
+ >
135
+ {question.question}
136
+ </div>
137
+ {question.options.length > 0 && (
138
+ <div className='claude-generic-tool__question-options'>
139
+ {question.options.map(option => (
140
+ <div className='claude-generic-tool__question-option' key={option.label}>
141
+ <div className='claude-generic-tool__question-option-label'>{option.label}</div>
142
+ {option.description != null && option.description !== '' && (
143
+ <div className='claude-generic-tool__question-option-description'>
144
+ {option.description}
145
+ </div>
146
+ )}
147
+ </div>
148
+ ))}
149
+ </div>
150
+ )}
151
+ </div>
152
+ ))}
153
+ </div>
154
+ </div>
155
+ )
156
+ }
157
+
158
+ return (
159
+ <div className='tool-detail-section claude-generic-tool__section' key={getFieldKey(field, index)}>
160
+ {sectionHeader}
161
+ <CodeBlock
162
+ code={safeJsonStringify(field.value, 2)}
163
+ lang='json'
164
+ hideHeader={true}
165
+ />
166
+ </div>
167
+ )
168
+ }
@@ -0,0 +1,135 @@
1
+ import { asBoolean, asNumber, asString, asStringArray, pushField } from './claude-tool-shared'
2
+ import type { ClaudeToolField } from './claude-tool-shared'
3
+ import { getFileInfo, getLanguageFromPath } from './utils'
4
+
5
+ interface BuilderParams {
6
+ baseName: string
7
+ record: Record<string, unknown> | null
8
+ fields: ClaudeToolField[]
9
+ usedKeys: Set<string>
10
+ }
11
+
12
+ export function buildClaudeOperationToolPresentation(params: BuilderParams) {
13
+ const { baseName, record, fields, usedKeys } = params
14
+
15
+ if (baseName === 'Bash') {
16
+ const command = asString(record?.command)
17
+ const description = asString(record?.description) ?? asString(record?.reason) ?? asString(record?.thought)
18
+
19
+ pushField(fields, usedKeys, 'description', {
20
+ labelKey: 'chat.tools.fields.description',
21
+ fallbackLabel: 'Description',
22
+ format: 'text',
23
+ value: description
24
+ })
25
+ pushField(fields, usedKeys, 'command', {
26
+ labelKey: 'chat.tools.fields.command',
27
+ fallbackLabel: 'Command',
28
+ format: 'code',
29
+ value: command,
30
+ lang: 'bash'
31
+ })
32
+ pushField(fields, usedKeys, 'timeout', {
33
+ labelKey: 'chat.tools.fields.timeout',
34
+ fallbackLabel: 'Timeout',
35
+ format: 'inline',
36
+ value: asNumber(record?.timeout)
37
+ })
38
+ pushField(fields, usedKeys, 'run_in_background', {
39
+ labelKey: 'chat.tools.fields.runInBackground',
40
+ fallbackLabel: 'Run In Background',
41
+ format: 'inline',
42
+ value: asBoolean(record?.run_in_background) != null ? String(record?.run_in_background) : undefined
43
+ })
44
+ pushField(fields, usedKeys, 'dangerouslyDisableSandbox', {
45
+ labelKey: 'chat.tools.fields.disableSandbox',
46
+ fallbackLabel: 'Disable Sandbox',
47
+ format: 'inline',
48
+ value: asBoolean(record?.dangerouslyDisableSandbox) != null
49
+ ? String(record?.dangerouslyDisableSandbox)
50
+ : undefined
51
+ })
52
+ return { handled: true, primary: description ?? command?.split('\n')[0] }
53
+ }
54
+
55
+ if (baseName === 'Read') {
56
+ const filePath = asString(record?.file_path)
57
+ const primary = filePath != null ? getFileInfo(filePath).filePath : undefined
58
+
59
+ pushField(fields, usedKeys, 'offset', {
60
+ labelKey: 'chat.tools.fields.offset',
61
+ fallbackLabel: 'Offset',
62
+ format: 'inline',
63
+ value: asNumber(record?.offset)
64
+ })
65
+ pushField(fields, usedKeys, 'limit', {
66
+ labelKey: 'chat.tools.fields.limit',
67
+ fallbackLabel: 'Limit',
68
+ format: 'inline',
69
+ value: asNumber(record?.limit)
70
+ })
71
+ usedKeys.add('file_path')
72
+ return { handled: true, primary }
73
+ }
74
+
75
+ if (baseName === 'Write') {
76
+ const filePath = asString(record?.file_path)
77
+ const primary = filePath != null ? getFileInfo(filePath).filePath : undefined
78
+ const language = getLanguageFromPath(primary ?? '')
79
+
80
+ pushField(fields, usedKeys, 'content', {
81
+ labelKey: 'chat.tools.fields.content',
82
+ fallbackLabel: 'Content',
83
+ format: 'code',
84
+ value: asString(record?.content),
85
+ lang: language
86
+ })
87
+ usedKeys.add('file_path')
88
+ return { handled: true, primary }
89
+ }
90
+
91
+ if (baseName === 'LS') {
92
+ pushField(fields, usedKeys, 'ignore', {
93
+ labelKey: 'chat.tools.fields.ignore',
94
+ fallbackLabel: 'Ignore',
95
+ format: 'list',
96
+ value: asStringArray(record?.ignore)
97
+ })
98
+ return { handled: true, primary: asString(record?.path) ?? 'current directory' }
99
+ }
100
+
101
+ if (baseName === 'Glob') {
102
+ pushField(fields, usedKeys, 'path', {
103
+ labelKey: 'chat.tools.fields.path',
104
+ fallbackLabel: 'Path',
105
+ format: 'text',
106
+ value: asString(record?.path)
107
+ })
108
+ usedKeys.add('pattern')
109
+ return { handled: true, primary: asString(record?.pattern) ?? '*' }
110
+ }
111
+
112
+ if (baseName === 'Grep') {
113
+ pushField(fields, usedKeys, 'path', {
114
+ labelKey: 'chat.tools.fields.path',
115
+ fallbackLabel: 'Path',
116
+ format: 'text',
117
+ value: asString(record?.path)
118
+ })
119
+ pushField(fields, usedKeys, 'glob', {
120
+ labelKey: 'chat.tools.fields.glob',
121
+ fallbackLabel: 'Glob',
122
+ format: 'text',
123
+ value: asString(record?.glob)
124
+ })
125
+ pushField(fields, usedKeys, 'output_mode', {
126
+ labelKey: 'chat.tools.fields.mode',
127
+ fallbackLabel: 'Mode',
128
+ format: 'inline',
129
+ value: asString(record?.output_mode)
130
+ })
131
+ usedKeys.add('pattern')
132
+ return { handled: true, primary: asString(record?.pattern) }
133
+ }
134
+ return { handled: false }
135
+ }
@@ -0,0 +1,61 @@
1
+ import { buildClaudeEditToolPresentation } from './claude-tool-edit-builders'
2
+ import { buildClaudeOperationToolPresentation } from './claude-tool-operation-builders'
3
+ import { CLAUDE_TOOL_META, addDetailsField, isRecord } from './claude-tool-shared'
4
+ import type {
5
+ ClaudeToolField,
6
+ ClaudeToolPresentation,
7
+ ClaudeToolQuestion,
8
+ ClaudeToolQuestionOption
9
+ } from './claude-tool-shared'
10
+ import { buildClaudeSystemToolPresentation } from './claude-tool-system-builders'
11
+ import { buildClaudeTaskToolPresentation } from './claude-tool-task-builders'
12
+
13
+ export type { ClaudeToolField, ClaudeToolPresentation, ClaudeToolQuestion, ClaudeToolQuestionOption }
14
+
15
+ export const getClaudeToolBaseName = (name: string) => (
16
+ name.startsWith('adapter:claude-code:') ? name.slice('adapter:claude-code:'.length) : name
17
+ )
18
+
19
+ export const isClaudeToolName = (name: string) => (
20
+ name.startsWith('adapter:claude-code:') || name in CLAUDE_TOOL_META
21
+ )
22
+
23
+ export function buildClaudeToolPresentation(name: string, input: unknown): ClaudeToolPresentation {
24
+ const baseName = getClaudeToolBaseName(name)
25
+ const meta = CLAUDE_TOOL_META[baseName] ?? {
26
+ titleKey: 'chat.tools.unknown',
27
+ fallbackTitle: baseName,
28
+ icon: 'build'
29
+ }
30
+ const record = isRecord(input) ? input : null
31
+ const fields: ClaudeToolField[] = []
32
+ const usedKeys = new Set<string>()
33
+ const editResult = buildClaudeEditToolPresentation({ baseName, record, fields, usedKeys })
34
+ const operationResult = editResult.handled
35
+ ? { handled: false as const, primary: undefined as string | undefined }
36
+ : buildClaudeOperationToolPresentation({ baseName, record, fields, usedKeys })
37
+ const systemResult = editResult.handled || operationResult.handled
38
+ ? { handled: false as const, primary: undefined as string | undefined }
39
+ : buildClaudeSystemToolPresentation({ baseName, record, fields, usedKeys })
40
+ const taskResult = editResult.handled || operationResult.handled || systemResult.handled
41
+ ? { handled: false as const, primary: undefined as string | undefined }
42
+ : buildClaudeTaskToolPresentation({ baseName, record, fields, usedKeys })
43
+ const primary = editResult.handled
44
+ ? editResult.primary
45
+ : operationResult.handled
46
+ ? operationResult.primary
47
+ : systemResult.handled
48
+ ? systemResult.primary
49
+ : taskResult.primary
50
+
51
+ addDetailsField(fields, record, usedKeys)
52
+
53
+ return {
54
+ baseName,
55
+ titleKey: meta.titleKey,
56
+ fallbackTitle: meta.fallbackTitle,
57
+ icon: meta.icon,
58
+ primary,
59
+ fields
60
+ }
61
+ }
@@ -0,0 +1,185 @@
1
+ export type ClaudeToolFieldFormat = 'inline' | 'text' | 'code' | 'list' | 'json' | 'questions'
2
+
3
+ export interface ClaudeToolQuestionOption {
4
+ label: string
5
+ description?: string
6
+ }
7
+
8
+ export interface ClaudeToolQuestion {
9
+ header?: string
10
+ question: string
11
+ options: ClaudeToolQuestionOption[]
12
+ multiSelect: boolean
13
+ }
14
+
15
+ export interface ClaudeToolField {
16
+ labelKey: string
17
+ fallbackLabel: string
18
+ format: ClaudeToolFieldFormat
19
+ value: unknown
20
+ lang?: string
21
+ }
22
+
23
+ export interface ClaudeToolPresentation {
24
+ baseName: string
25
+ titleKey: string
26
+ fallbackTitle: string
27
+ icon: string
28
+ primary?: string
29
+ fields: ClaudeToolField[]
30
+ }
31
+
32
+ export const CLAUDE_TOOL_META: Record<string, Pick<ClaudeToolPresentation, 'titleKey' | 'fallbackTitle' | 'icon'>> = {
33
+ AskUserQuestion: { titleKey: 'chat.tools.askUserQuestion', fallbackTitle: 'Ask User Question', icon: 'help' },
34
+ Bash: { titleKey: 'chat.tools.bash', fallbackTitle: 'Bash', icon: 'terminal' },
35
+ Edit: { titleKey: 'chat.tools.editTool', fallbackTitle: 'Edit File', icon: 'edit' },
36
+ EnterPlanMode: { titleKey: 'chat.tools.enterPlanMode', fallbackTitle: 'Enter Plan Mode', icon: 'rule_settings' },
37
+ ExitPlanMode: { titleKey: 'chat.tools.exitPlanMode', fallbackTitle: 'Exit Plan Mode', icon: 'exit_to_app' },
38
+ Glob: { titleKey: 'chat.tools.globTool', fallbackTitle: 'Glob', icon: 'search' },
39
+ Grep: { titleKey: 'chat.tools.grepTool', fallbackTitle: 'Grep', icon: 'find_in_page' },
40
+ LS: { titleKey: 'chat.tools.lsTool', fallbackTitle: 'List Directory', icon: 'folder_open' },
41
+ NotebookEdit: { titleKey: 'chat.tools.notebookEdit', fallbackTitle: 'Notebook Edit', icon: 'edit_note' },
42
+ Read: { titleKey: 'chat.tools.read', fallbackTitle: 'Read File', icon: 'visibility' },
43
+ Skill: { titleKey: 'chat.tools.skill', fallbackTitle: 'Skill', icon: 'auto_awesome' },
44
+ Task: { titleKey: 'chat.tools.claudeTask', fallbackTitle: 'Claude Task', icon: 'smart_toy' },
45
+ TaskCreate: { titleKey: 'chat.tools.taskCreate', fallbackTitle: 'Create Task', icon: 'add_task' },
46
+ TaskGet: { titleKey: 'chat.tools.taskGet', fallbackTitle: 'Get Task', icon: 'info' },
47
+ TaskList: { titleKey: 'chat.tools.taskList', fallbackTitle: 'List Tasks', icon: 'list_alt' },
48
+ TaskUpdate: { titleKey: 'chat.tools.taskUpdate', fallbackTitle: 'Update Task', icon: 'edit_calendar' },
49
+ TodoWrite: { titleKey: 'chat.tools.todo', fallbackTitle: 'Task Planning', icon: 'task_alt' },
50
+ WebFetch: { titleKey: 'chat.tools.webFetch', fallbackTitle: 'Web Fetch', icon: 'language' },
51
+ WebSearch: { titleKey: 'chat.tools.webSearch', fallbackTitle: 'Web Search', icon: 'travel_explore' },
52
+ Write: { titleKey: 'chat.tools.write', fallbackTitle: 'Write File', icon: 'edit_note' }
53
+ }
54
+
55
+ export const isRecord = (value: unknown): value is Record<string, unknown> => (
56
+ value != null && typeof value === 'object' && !Array.isArray(value)
57
+ )
58
+
59
+ export const asString = (value: unknown) => (
60
+ typeof value === 'string' && value.trim() !== '' ? value : undefined
61
+ )
62
+
63
+ export const asNumber = (value: unknown) => (
64
+ typeof value === 'number' && Number.isFinite(value) ? value : undefined
65
+ )
66
+
67
+ export const asBoolean = (value: unknown) => (
68
+ typeof value === 'boolean' ? value : undefined
69
+ )
70
+
71
+ export const asStringArray = (value: unknown) => {
72
+ if (!Array.isArray(value)) {
73
+ return undefined
74
+ }
75
+
76
+ const items = value
77
+ .filter(item => typeof item === 'string' && item.trim() !== '')
78
+ .map(item => item.trim())
79
+ return items.length > 0 ? items : undefined
80
+ }
81
+
82
+ export const pushField = (
83
+ fields: ClaudeToolField[],
84
+ usedKeys: Set<string>,
85
+ key: string,
86
+ field: Omit<ClaudeToolField, 'value'> & { value: unknown }
87
+ ) => {
88
+ const { value } = field
89
+ if (value == null || value === '') {
90
+ return
91
+ }
92
+ if (Array.isArray(value) && value.length === 0) {
93
+ return
94
+ }
95
+ if (isRecord(value) && Object.keys(value).length === 0) {
96
+ return
97
+ }
98
+
99
+ usedKeys.add(key)
100
+ fields.push(field)
101
+ }
102
+
103
+ export const toQuestionList = (value: unknown): ClaudeToolQuestion[] | undefined => {
104
+ if (!Array.isArray(value)) {
105
+ return undefined
106
+ }
107
+
108
+ const questions: ClaudeToolQuestion[] = []
109
+ for (const item of value) {
110
+ if (!isRecord(item)) {
111
+ continue
112
+ }
113
+
114
+ const question = asString(item.question)
115
+ if (question == null) {
116
+ continue
117
+ }
118
+
119
+ const options: ClaudeToolQuestionOption[] = []
120
+ if (Array.isArray(item.options)) {
121
+ for (const option of item.options) {
122
+ if (!isRecord(option)) {
123
+ continue
124
+ }
125
+
126
+ const label = asString(option.label)
127
+ if (label == null) {
128
+ continue
129
+ }
130
+
131
+ options.push({
132
+ label,
133
+ description: asString(option.description)
134
+ })
135
+ }
136
+ }
137
+
138
+ questions.push({
139
+ header: asString(item.header),
140
+ question,
141
+ options,
142
+ multiSelect: item.multiSelect === true
143
+ })
144
+ }
145
+
146
+ return questions.length > 0 ? questions : undefined
147
+ }
148
+
149
+ export const getDescriptionTitle = (value: string | undefined) => (
150
+ value != null && value !== '' ? value.split('\n')[0]?.trim() || value : undefined
151
+ )
152
+
153
+ export const addDetailsField = (
154
+ fields: ClaudeToolField[],
155
+ record: Record<string, unknown> | null,
156
+ usedKeys: Set<string>
157
+ ) => {
158
+ if (record == null) {
159
+ return
160
+ }
161
+
162
+ const details = Object.fromEntries(
163
+ Object.entries(record).filter(([key, value]) => {
164
+ if (usedKeys.has(key) || value == null) {
165
+ return false
166
+ }
167
+ if (typeof value === 'string') {
168
+ return value.trim() !== ''
169
+ }
170
+ if (Array.isArray(value)) {
171
+ return value.length > 0
172
+ }
173
+ return !isRecord(value) || Object.keys(value).length > 0
174
+ })
175
+ )
176
+
177
+ if (Object.keys(details).length > 0) {
178
+ fields.push({
179
+ labelKey: 'chat.tools.fields.details',
180
+ fallbackLabel: 'Details',
181
+ format: 'json',
182
+ value: details
183
+ })
184
+ }
185
+ }
@@ -0,0 +1,76 @@
1
+ import { buildClaudeToolPresentation, getClaudeToolBaseName } from './claude-tool-presentation'
2
+ import { isRecord, toQuestionList } from './claude-tool-shared'
3
+ import { getFileInfo } from './utils'
4
+
5
+ type Translate = (key: string, options?: Record<string, unknown>) => string
6
+
7
+ const getRecord = (input: unknown) => (isRecord(input) ? input : null)
8
+
9
+ const joinSummary = (title: string, primary?: string) => (
10
+ primary != null && primary !== '' ? `${title} ${primary}` : title
11
+ )
12
+
13
+ export function getClaudeToolSummaryText(name: string, input: unknown, t: Translate) {
14
+ const baseName = getClaudeToolBaseName(name)
15
+ const record = getRecord(input)
16
+
17
+ if (baseName === 'Bash') {
18
+ const description = [record?.description, record?.reason, record?.thought]
19
+ .find(value => typeof value === 'string' && value.trim() !== '')
20
+ const command = typeof record?.command === 'string' ? record.command : ''
21
+ const commandLine = command.split('\n')[0]?.trim()
22
+ return typeof description === 'string' && description.trim() !== ''
23
+ ? description.trim()
24
+ : joinSummary(t('chat.tools.bash', { defaultValue: 'Bash' }), commandLine)
25
+ }
26
+
27
+ if (baseName === 'Read' || baseName === 'Write' || baseName === 'Edit') {
28
+ const pathValue = typeof record?.file_path === 'string' ? getFileInfo(record.file_path).filePath : undefined
29
+ const titleKey = baseName === 'Read'
30
+ ? 'chat.tools.read'
31
+ : baseName === 'Write'
32
+ ? 'chat.tools.write'
33
+ : 'chat.tools.editTool'
34
+ const fallback = baseName === 'Read' ? 'Read File' : baseName === 'Write' ? 'Write File' : 'Edit File'
35
+ return joinSummary(t(titleKey, { defaultValue: fallback }), pathValue)
36
+ }
37
+
38
+ if (baseName === 'NotebookEdit') {
39
+ const notebookPath = typeof record?.notebook_path === 'string'
40
+ ? getFileInfo(record.notebook_path).filePath
41
+ : undefined
42
+ return joinSummary(t('chat.tools.notebookEdit', { defaultValue: 'Notebook Edit' }), notebookPath)
43
+ }
44
+
45
+ if (baseName === 'LS') {
46
+ const pathValue = typeof record?.path === 'string' && record.path !== '' ? record.path : 'current directory'
47
+ return joinSummary(t('chat.tools.lsTool', { defaultValue: 'List Directory' }), pathValue)
48
+ }
49
+
50
+ if (baseName === 'Glob') {
51
+ const pattern = typeof record?.pattern === 'string' && record.pattern !== '' ? record.pattern : '*'
52
+ return joinSummary(t('chat.tools.globTool', { defaultValue: 'Glob' }), pattern)
53
+ }
54
+
55
+ if (baseName === 'Grep') {
56
+ const pattern = typeof record?.pattern === 'string' ? record.pattern : undefined
57
+ return joinSummary(t('chat.tools.grepTool', { defaultValue: 'Grep' }), pattern)
58
+ }
59
+
60
+ if (baseName === 'TodoWrite') {
61
+ const count = Array.isArray(record?.todos) ? record.todos.length : 0
62
+ return joinSummary(t('chat.tools.todo', { defaultValue: 'Task Planning' }), count > 0 ? `${count}` : undefined)
63
+ }
64
+
65
+ if (baseName === 'AskUserQuestion') {
66
+ const firstQuestion = toQuestionList(record?.questions)?.[0]
67
+ return joinSummary(
68
+ t('chat.tools.askUserQuestion', { defaultValue: 'Ask User Question' }),
69
+ firstQuestion?.header ?? firstQuestion?.question
70
+ )
71
+ }
72
+
73
+ const presentation = buildClaudeToolPresentation(name, input)
74
+ const title = t(presentation.titleKey, { defaultValue: presentation.fallbackTitle })
75
+ return joinSummary(title, presentation.primary)
76
+ }