@vibe-forge/client 0.10.1 → 0.11.1

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 (133) hide show
  1. package/dist/assets/{arc-C1rWFTer.js → arc-CSepokz3.js} +1 -1
  2. package/dist/assets/{blockDiagram-c4efeb88-DlZ9x70F.js → blockDiagram-c4efeb88-D0ARcoNf.js} +1 -1
  3. package/dist/assets/{c4Diagram-c83219d4-BKKxi__y.js → c4Diagram-c83219d4-BysYF9kP.js} +1 -1
  4. package/dist/assets/channel-CeKPk6Nd.js +1 -0
  5. package/dist/assets/{classDiagram-beda092f-CVGPySZq.js → classDiagram-beda092f-BG1GhIOL.js} +1 -1
  6. package/dist/assets/{classDiagram-v2-2358418a-7kp8GVVj.js → classDiagram-v2-2358418a-Dd08uGSH.js} +1 -1
  7. package/dist/assets/clone-CrkD2PuD.js +1 -0
  8. package/dist/assets/{createText-1719965b-Dykv8kT9.js → createText-1719965b-CigPEIEn.js} +1 -1
  9. package/dist/assets/{cssMode-B59COYVW.js → cssMode-MjflyEfm.js} +1 -1
  10. package/dist/assets/{edges-96097737-CkZ1ZBro.js → edges-96097737-DuTBJJRv.js} +1 -1
  11. package/dist/assets/{erDiagram-0228fc6a-281ADcRp.js → erDiagram-0228fc6a-Cp1bL7Y7.js} +1 -1
  12. package/dist/assets/{flowDb-c6c81e3f-BQjX_flP.js → flowDb-c6c81e3f-BfKbhiq5.js} +1 -1
  13. package/dist/assets/{flowDiagram-50d868cf-DMHZTjES.js → flowDiagram-50d868cf-m7gGc3PK.js} +1 -1
  14. package/dist/assets/flowDiagram-v2-4f6560a1-4ZU4bdp1.js +1 -0
  15. package/dist/assets/{flowchart-elk-definition-6af322e1-CI3yz4z8.js → flowchart-elk-definition-6af322e1-EVeTDRRK.js} +1 -1
  16. package/dist/assets/{freemarker2-DWnWjibn.js → freemarker2-Bb3-QAIN.js} +1 -1
  17. package/dist/assets/{ganttDiagram-a2739b55-B3IING9L.js → ganttDiagram-a2739b55-DslB2U0R.js} +1 -1
  18. package/dist/assets/{gitGraphDiagram-82fe8481-CnArIr_T.js → gitGraphDiagram-82fe8481-C-KFWMXL.js} +1 -1
  19. package/dist/assets/{graph-BZ1F0Yve.js → graph-CukaUc0o.js} +1 -1
  20. package/dist/assets/{handlebars-C1QH9qTz.js → handlebars-C4le-2Y6.js} +1 -1
  21. package/dist/assets/{html-D1NkqHjC.js → html-CjNiRs5S.js} +1 -1
  22. package/dist/assets/{htmlMode-DAZCE_rA.js → htmlMode-B73_3-We.js} +1 -1
  23. package/dist/assets/{index-5325376f-Da9zSHjA.js → index-5325376f-CVISZFPw.js} +1 -1
  24. package/dist/assets/{index-C0vjF3D0.js → index-BZosmb5_.js} +336 -336
  25. package/dist/assets/index-C1oh0w9H.css +32 -0
  26. package/dist/assets/{infoDiagram-8eee0895-DYbFvRM7.js → infoDiagram-8eee0895-DoirLE1K.js} +1 -1
  27. package/dist/assets/{javascript-CoMjGRHa.js → javascript-BDjnqJFP.js} +1 -1
  28. package/dist/assets/{journeyDiagram-c64418c1-Boebox0b.js → journeyDiagram-c64418c1-Ckn-p2CM.js} +1 -1
  29. package/dist/assets/{jsonMode-D__gAvuz.js → jsonMode-C-ftOc5j.js} +1 -1
  30. package/dist/assets/{layout-CTcHNbHp.js → layout-Z7yUG7hB.js} +1 -1
  31. package/dist/assets/{line-4AwinCz2.js → line-DPG_cfAy.js} +1 -1
  32. package/dist/assets/{linear-CeSMLzJW.js → linear--GSeVfMi.js} +1 -1
  33. package/dist/assets/{liquid-DZF6egdE.js → liquid-COiLZ9py.js} +1 -1
  34. package/dist/assets/{lspLanguageFeatures-6K4lv5S2.js → lspLanguageFeatures-DGmhryFq.js} +1 -1
  35. package/dist/assets/{mdx-Cnt4ka6w.js → mdx-BpL87Gej.js} +1 -1
  36. package/dist/assets/{mermaid.core-B0yG5s4D.js → mermaid.core-Cg1CCDo6.js} +4 -4
  37. package/dist/assets/{mindmap-definition-8da855dc-KJEvXMKj.js → mindmap-definition-8da855dc-CKDof1lD.js} +1 -1
  38. package/dist/assets/{pieDiagram-a8764435-17nFAXPJ.js → pieDiagram-a8764435-DwvCaZVE.js} +1 -1
  39. package/dist/assets/{python-DA3TtjDv.js → python-63dBmWV_.js} +1 -1
  40. package/dist/assets/{quadrantDiagram-1e28029f-Dt4vubi-.js → quadrantDiagram-1e28029f-CkzYBQpy.js} +1 -1
  41. package/dist/assets/{razor-CWDJgvX_.js → razor-C50tBqEZ.js} +1 -1
  42. package/dist/assets/{requirementDiagram-08caed73-H6aDyDK-.js → requirementDiagram-08caed73-Brgdjqf4.js} +1 -1
  43. package/dist/assets/{sankeyDiagram-a04cb91d-DxsVtbjI.js → sankeyDiagram-a04cb91d-CGkYexrs.js} +1 -1
  44. package/dist/assets/{sequenceDiagram-c5b8d532-BHa148XJ.js → sequenceDiagram-c5b8d532-D0wE-_J8.js} +1 -1
  45. package/dist/assets/{stateDiagram-1ecb1508-DgwBm8LO.js → stateDiagram-1ecb1508-BYb3NCXZ.js} +1 -1
  46. package/dist/assets/{stateDiagram-v2-c2b004d7-BK7IQLVc.js → stateDiagram-v2-c2b004d7-DrPqi4Pt.js} +1 -1
  47. package/dist/assets/{styles-b4e223ce-DzW27Bc-.js → styles-b4e223ce-DD66TIO4.js} +1 -1
  48. package/dist/assets/{styles-ca3715f6-Dex2GiLT.js → styles-ca3715f6-iy02LHIV.js} +1 -1
  49. package/dist/assets/{styles-d45a18b0-B6fGtDKS.js → styles-d45a18b0-BgqAgJyW.js} +1 -1
  50. package/dist/assets/{svgDrawCommon-b86b1483-B4HYgfV5.js → svgDrawCommon-b86b1483-CDq7ugnw.js} +1 -1
  51. package/dist/assets/{timeline-definition-faaaa080--QSbWb25.js → timeline-definition-faaaa080-DzcLLjK0.js} +1 -1
  52. package/dist/assets/{tsMode-ZM7ocZCH.js → tsMode-BFRFI4ct.js} +1 -1
  53. package/dist/assets/{typescript-CKWDmBCc.js → typescript-CBZQRAPv.js} +1 -1
  54. package/dist/assets/{xml-DuEUAzPi.js → xml-BpWm6upt.js} +1 -1
  55. package/dist/assets/{xychartDiagram-f5964ef8-D09Zkv2K.js → xychartDiagram-f5964ef8-zBN8FmLQ.js} +1 -1
  56. package/dist/assets/{yaml-DL7QPRYk.js → yaml-CqbJPiIP.js} +1 -1
  57. package/dist/index.html +2 -2
  58. package/package.json +10 -10
  59. package/src/api/git.ts +78 -0
  60. package/src/api.ts +24 -0
  61. package/src/components/chat/ChatHeader.tsx +4 -0
  62. package/src/components/chat/ChatHistoryView.tsx +22 -13
  63. package/src/components/chat/git-controls/BranchSwitcherDropdown.tsx +157 -0
  64. package/src/components/chat/git-controls/ChatGitControls.scss +616 -0
  65. package/src/components/chat/git-controls/ChatGitControls.tsx +151 -0
  66. package/src/components/chat/git-controls/GitCommitModal.tsx +199 -0
  67. package/src/components/chat/git-controls/GitCommitModalParts.tsx +151 -0
  68. package/src/components/chat/git-controls/GitOperationsDropdown.tsx +123 -0
  69. package/src/components/chat/git-controls/GitPushModal.tsx +106 -0
  70. package/src/components/chat/git-controls/GitWorktreeDropdown.tsx +68 -0
  71. package/src/components/chat/git-controls/git-branch-utils.ts +88 -0
  72. package/src/components/chat/git-controls/git-commit-utils.ts +79 -0
  73. package/src/components/chat/git-controls/git-mutation-utils.ts +69 -0
  74. package/src/components/chat/git-controls/git-operation-utils.ts +98 -0
  75. package/src/components/chat/git-controls/git-worktree-utils.ts +49 -0
  76. package/src/components/chat/git-controls/use-chat-git-commit.ts +185 -0
  77. package/src/components/chat/git-controls/use-chat-git-controls.ts +200 -0
  78. package/src/components/chat/git-controls/use-chat-git-push-state.ts +19 -0
  79. package/src/components/chat/git-controls/use-chat-git-worktrees.ts +39 -0
  80. package/src/components/chat/messages/MessageStatusNotice.scss +163 -0
  81. package/src/components/chat/messages/MessageStatusNotice.tsx +48 -0
  82. package/src/components/chat/messages/build-chat-history-status-notices.ts +138 -0
  83. package/src/components/chat/sender/@components/sender-body/SenderBody.tsx +0 -24
  84. package/src/components/chat/sender/@core/build-sender-controller-result.ts +0 -6
  85. package/src/components/chat/sender/@hooks/use-sender-controller.ts +0 -2
  86. package/src/components/chat/sender/@types/sender-props.ts +0 -3
  87. package/src/components/chat/sender/Sender.scss +0 -58
  88. package/src/components/chat/sender/Sender.tsx +0 -2
  89. package/src/components/chat/tools/DefaultTool.tsx +84 -208
  90. package/src/components/chat/tools/adapter-claude/ClaudeEditDiff.tsx +30 -0
  91. package/src/components/chat/tools/adapter-claude/GenericClaudeTool.scss +128 -0
  92. package/src/components/chat/tools/adapter-claude/GenericClaudeTool.tsx +119 -0
  93. package/src/components/chat/tools/adapter-claude/claude-tool-edit-builders.ts +109 -0
  94. package/src/components/chat/tools/adapter-claude/claude-tool-field-sections.tsx +83 -0
  95. package/src/components/chat/tools/adapter-claude/claude-tool-operation-builders.ts +135 -0
  96. package/src/components/chat/tools/adapter-claude/claude-tool-presentation.ts +61 -0
  97. package/src/components/chat/tools/adapter-claude/claude-tool-shared.ts +185 -0
  98. package/src/components/chat/tools/adapter-claude/claude-tool-summary.ts +76 -0
  99. package/src/components/chat/tools/adapter-claude/claude-tool-system-builders.ts +125 -0
  100. package/src/components/chat/tools/adapter-claude/claude-tool-task-builders.ts +148 -0
  101. package/src/components/chat/tools/adapter-claude/index.ts +24 -15
  102. package/src/components/chat/tools/core/ToolCallBox.scss +362 -36
  103. package/src/components/chat/tools/core/ToolCallBox.tsx +35 -13
  104. package/src/components/chat/tools/core/ToolDiffViewer.scss +138 -0
  105. package/src/components/chat/tools/core/ToolDiffViewer.tsx +180 -0
  106. package/src/components/chat/tools/core/ToolGroup.scss +52 -74
  107. package/src/components/chat/tools/core/ToolGroup.tsx +25 -40
  108. package/src/components/chat/tools/core/ToolRenderer.tsx +3 -3
  109. package/src/components/chat/tools/core/ToolResultContent.tsx +66 -0
  110. package/src/components/chat/tools/core/ToolSummaryHeader.tsx +67 -0
  111. package/src/components/chat/tools/core/generic-tool-presentation.ts +661 -0
  112. package/src/components/chat/tools/core/tool-content-presence.ts +57 -0
  113. package/src/components/chat/tools/core/tool-display.ts +203 -0
  114. package/src/components/chat/tools/core/tool-field-sections.tsx +132 -0
  115. package/src/components/chat/tools/core/tool-result-content-utils.ts +171 -0
  116. package/src/components/chat/tools/core/tool-summary.ts +206 -0
  117. package/src/components/chat/tools/plugin-chrome-devtools/ChromeDevtoolsTool.tsx +59 -53
  118. package/src/components/chat/tools/task/GetTaskInfoTool.tsx +26 -9
  119. package/src/components/chat/tools/task/ListTasksTool.tsx +22 -9
  120. package/src/components/chat/tools/task/StartTasksTool.tsx +22 -9
  121. package/src/hooks/chat/interaction-state.ts +29 -9
  122. package/src/hooks/chat/session-view-cache.ts +80 -0
  123. package/src/hooks/chat/use-chat-scroll.ts +2 -2
  124. package/src/hooks/chat/use-chat-session-messages.ts +139 -39
  125. package/src/hooks/chat/use-chat-session.ts +2 -2
  126. package/src/resources/locales/en.json +149 -0
  127. package/src/resources/locales/zh.json +149 -0
  128. package/src/routes/ChatRoute.tsx +24 -27
  129. package/src/utils/strip-ansi.ts +26 -0
  130. package/dist/assets/channel-F1aqMANO.js +0 -1
  131. package/dist/assets/clone-B-GCuXNo.js +0 -1
  132. package/dist/assets/flowDiagram-v2-4f6560a1-C5FzdVl1.js +0 -1
  133. package/dist/assets/index-vzEbM21t.css +0 -32
@@ -0,0 +1,128 @@
1
+ .tool-group.claude-generic-tool {
2
+ .claude-generic-tool__content {
3
+ display: flex;
4
+ flex-direction: column;
5
+ gap: 8px;
6
+ }
7
+
8
+ .claude-generic-tool__section {
9
+ display: flex;
10
+ flex-direction: column;
11
+ gap: 6px;
12
+ min-width: 0;
13
+ }
14
+
15
+ .claude-generic-tool__status {
16
+ display: inline-flex;
17
+ align-items: center;
18
+
19
+ .material-symbols-rounded {
20
+ font-size: 16px;
21
+ }
22
+
23
+ &.is-success {
24
+ color: var(--success-color);
25
+ }
26
+
27
+ &.is-error {
28
+ color: var(--danger-color);
29
+ }
30
+ }
31
+
32
+ .claude-generic-tool__list {
33
+ display: flex;
34
+ flex-direction: column;
35
+ gap: 4px;
36
+ }
37
+
38
+ .claude-generic-tool__list-item {
39
+ padding: 4px 0 4px 10px;
40
+ border: none;
41
+ border-left: 1px solid var(--border-color);
42
+ border-radius: 0;
43
+ background: transparent;
44
+ color: var(--text-color);
45
+ font-family: var(--font-mono);
46
+ font-size: 12px;
47
+ overflow-wrap: anywhere;
48
+ }
49
+
50
+ .claude-generic-tool__questions {
51
+ display: flex;
52
+ flex-direction: column;
53
+ gap: 8px;
54
+ }
55
+
56
+ .claude-generic-tool__question {
57
+ display: flex;
58
+ flex-direction: column;
59
+ gap: 6px;
60
+ padding-left: 10px;
61
+ border-left: 1px solid var(--border-color);
62
+ }
63
+
64
+ .claude-generic-tool__question-header {
65
+ display: flex;
66
+ align-items: center;
67
+ justify-content: space-between;
68
+ gap: 8px;
69
+ }
70
+
71
+ .claude-generic-tool__question-title {
72
+ font-size: 12px;
73
+ font-weight: 500;
74
+ color: var(--text-color);
75
+ min-width: 0;
76
+ overflow: hidden;
77
+ text-overflow: ellipsis;
78
+ white-space: nowrap;
79
+ }
80
+
81
+ .claude-generic-tool__question-mode {
82
+ flex-shrink: 0;
83
+ color: var(--sub-text-color);
84
+ font-size: 15px;
85
+ cursor: help;
86
+ }
87
+
88
+ .claude-generic-tool__question-text {
89
+ color: var(--text-color);
90
+ font-size: 12px;
91
+ line-height: 1.55;
92
+ }
93
+
94
+ .claude-generic-tool__question-options {
95
+ display: flex;
96
+ flex-direction: column;
97
+ gap: 4px;
98
+ }
99
+
100
+ .claude-generic-tool__question-option {
101
+ padding: 4px 0 4px 10px;
102
+ border-radius: 0;
103
+ background: transparent;
104
+ border: none;
105
+ border-left: 1px solid var(--border-color);
106
+ display: flex;
107
+ flex-direction: column;
108
+ gap: 2px;
109
+ }
110
+
111
+ .claude-generic-tool__question-option-label {
112
+ color: var(--text-color);
113
+ font-size: 12px;
114
+ font-weight: 500;
115
+ }
116
+
117
+ .claude-generic-tool__question-option-description {
118
+ color: var(--sub-text-color);
119
+ font-size: 11px;
120
+ line-height: 1.45;
121
+ }
122
+
123
+ .claude-generic-tool__placeholder {
124
+ color: var(--sub-text-color);
125
+ font-size: 12px;
126
+ font-style: italic;
127
+ }
128
+ }
@@ -0,0 +1,119 @@
1
+ import './GenericClaudeTool.scss'
2
+
3
+ import React, { useMemo } from 'react'
4
+ import { useTranslation } from 'react-i18next'
5
+
6
+ import { ToolCallBox } from '../core/ToolCallBox'
7
+ import { ToolResultContent } from '../core/ToolResultContent'
8
+ import { ToolSummaryHeader } from '../core/ToolSummaryHeader'
9
+ import { hasMeaningfulToolValue } from '../core/tool-content-presence'
10
+ import { getToolFieldIcon, getToolInlineValueText, getToolTargetPresentation } from '../core/tool-display'
11
+ import { defineToolRender } from '../defineToolRender'
12
+ import { ClaudeEditDiff } from './ClaudeEditDiff'
13
+ import { ClaudeToolInlineFields, renderClaudeBlockField } from './claude-tool-field-sections'
14
+ import { buildClaudeToolPresentation, getClaudeToolBaseName } from './claude-tool-presentation'
15
+ import { getClaudeToolSummaryText } from './claude-tool-summary'
16
+
17
+ export const GenericClaudeTool = defineToolRender(({ item, resultItem }) => {
18
+ const { t } = useTranslation()
19
+ const view = useMemo(() => buildClaudeToolPresentation(item.name, item.input), [item.input, item.name])
20
+ const titleText = useMemo(() => t(view.titleKey, { defaultValue: view.fallbackTitle }), [
21
+ t,
22
+ view.fallbackTitle,
23
+ view.titleKey
24
+ ])
25
+ const inlineFields = view.fields.filter(field => field.format === 'inline')
26
+ const isEditTool = view.baseName === 'Edit'
27
+ const blockFields = view.fields.filter((field) => {
28
+ if (field.format === 'inline') return false
29
+ if (!isEditTool) return true
30
+ return field.labelKey !== 'chat.tools.fields.oldString' && field.labelKey !== 'chat.tools.fields.newString'
31
+ })
32
+ const hasFields = view.fields.length > 0
33
+ const hasResultDetails = resultItem != null && hasMeaningfulToolValue(resultItem.content)
34
+ const showResultDetails = hasResultDetails && (!isEditTool || resultItem?.is_error === true)
35
+ const hasDetails = hasFields || hasResultDetails
36
+ const preferMarkdown = ['WebFetch', 'WebSearch'].includes(getClaudeToolBaseName(item.name))
37
+ const editInput = isEditTool && item.input != null && typeof item.input === 'object' && !Array.isArray(item.input)
38
+ ? item.input as Record<string, unknown>
39
+ : null
40
+ const editLanguage = blockFields.find(field => field.lang != null)?.lang
41
+ const editOldValue = typeof editInput?.old_string === 'string' ? editInput.old_string : undefined
42
+ const editNewValue = typeof editInput?.new_string === 'string' ? editInput.new_string : undefined
43
+ const hasEditDiff = isEditTool && (editOldValue != null || editNewValue != null)
44
+ const editMetaItems = hasEditDiff
45
+ ? inlineFields.map((field) => {
46
+ const label = t(field.labelKey, { defaultValue: field.fallbackLabel })
47
+ const isBooleanTrue = field.value === true || field.value === 'true'
48
+ const isBooleanFalse = field.value === false || field.value === 'false'
49
+
50
+ return {
51
+ icon: getToolFieldIcon(field.labelKey, field.format),
52
+ label,
53
+ value: isBooleanTrue
54
+ ? t('chat.tools.booleanOn')
55
+ : isBooleanFalse
56
+ ? t('chat.tools.booleanOff')
57
+ : getToolInlineValueText(field.value),
58
+ tone: isBooleanTrue ? 'success' : isBooleanFalse ? 'muted' : 'default'
59
+ } as const
60
+ })
61
+ : []
62
+ const standaloneInlineFields = hasEditDiff ? [] : inlineFields
63
+ const summaryText = useMemo(() => getClaudeToolSummaryText(item.name, item.input, t), [item.input, item.name, t])
64
+ const rawTargetText = view.primary ?? (summaryText !== titleText ? summaryText : undefined)
65
+ const targetPresentation = getToolTargetPresentation(rawTargetText)
66
+ const errorMeta = resultItem?.is_error === true
67
+ ? (
68
+ <span className='claude-generic-tool__status is-error'>
69
+ <span className='material-symbols-rounded'>error</span>
70
+ </span>
71
+ )
72
+ : undefined
73
+
74
+ return (
75
+ <div className='tool-group tool-group--compact claude-generic-tool'>
76
+ <ToolCallBox
77
+ variant='inline'
78
+ defaultExpanded={false}
79
+ collapsible={hasDetails}
80
+ header={({ isExpanded, isCollapsible }) => (
81
+ <ToolSummaryHeader
82
+ icon={<span className='material-symbols-rounded'>{view.icon}</span>}
83
+ title={titleText}
84
+ target={targetPresentation.text}
85
+ targetTitle={targetPresentation.title}
86
+ targetMonospace={targetPresentation.monospace}
87
+ expanded={isExpanded}
88
+ collapsible={isCollapsible}
89
+ meta={errorMeta}
90
+ metaTitle={errorMeta == null ? undefined : t('chat.result')}
91
+ />
92
+ )}
93
+ content={hasDetails
94
+ ? (
95
+ <div className='tool-detail-sections claude-generic-tool__content'>
96
+ <ClaudeToolInlineFields fields={standaloneInlineFields} t={t} />
97
+ {hasEditDiff && (
98
+ <div className='tool-detail-section claude-generic-tool__section'>
99
+ <ClaudeEditDiff
100
+ oldValue={editOldValue}
101
+ newValue={editNewValue}
102
+ lang={editLanguage}
103
+ metaItems={editMetaItems}
104
+ />
105
+ </div>
106
+ )}
107
+ {blockFields.map((field, index) => renderClaudeBlockField(field, index, t))}
108
+ {showResultDetails && resultItem != null && (
109
+ <div className='tool-detail-section'>
110
+ <ToolResultContent content={resultItem.content} preferMarkdown={preferMarkdown} />
111
+ </div>
112
+ )}
113
+ </div>
114
+ )
115
+ : null}
116
+ />
117
+ </div>
118
+ )
119
+ })
@@ -0,0 +1,109 @@
1
+ import { asBoolean, asString, 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 buildClaudeEditToolPresentation(params: BuilderParams) {
13
+ const { baseName, record, fields, usedKeys } = params
14
+
15
+ if (baseName === 'TodoWrite') {
16
+ const todos = Array.isArray(record?.todos)
17
+ ? record.todos.flatMap((todo) => {
18
+ if (todo == null || typeof todo !== 'object') {
19
+ return []
20
+ }
21
+ const data = todo as Record<string, unknown>
22
+ const content = asString(data.content)
23
+ const status = asString(data.status)
24
+ const activeForm = asString(data.activeForm)
25
+ if (content == null) {
26
+ return []
27
+ }
28
+ const detail = status === 'in_progress' && activeForm != null && activeForm !== content
29
+ ? ` (${activeForm})`
30
+ : ''
31
+ return [`[${status ?? 'pending'}] ${content}${detail}`]
32
+ })
33
+ : undefined
34
+
35
+ pushField(fields, usedKeys, 'todos', {
36
+ labelKey: 'chat.tools.fields.todos',
37
+ fallbackLabel: 'Todos',
38
+ format: 'list',
39
+ value: todos
40
+ })
41
+ return { handled: true, primary: todos != null ? `${todos.length} todos` : undefined }
42
+ }
43
+
44
+ if (baseName === 'Edit') {
45
+ const filePath = asString(record?.file_path)
46
+ const primary = filePath != null ? getFileInfo(filePath).filePath : undefined
47
+ const language = getLanguageFromPath(primary ?? '')
48
+ const replaceAll = asBoolean(record?.replace_all)
49
+
50
+ pushField(fields, usedKeys, 'replace_all', {
51
+ labelKey: 'chat.tools.fields.replaceAll',
52
+ fallbackLabel: 'Replace All',
53
+ format: 'inline',
54
+ value: replaceAll != null ? String(replaceAll) : undefined
55
+ })
56
+ pushField(fields, usedKeys, 'old_string', {
57
+ labelKey: 'chat.tools.fields.oldString',
58
+ fallbackLabel: 'Old String',
59
+ format: 'code',
60
+ value: asString(record?.old_string),
61
+ lang: language
62
+ })
63
+ pushField(fields, usedKeys, 'new_string', {
64
+ labelKey: 'chat.tools.fields.newString',
65
+ fallbackLabel: 'New String',
66
+ format: 'code',
67
+ value: asString(record?.new_string),
68
+ lang: language
69
+ })
70
+ usedKeys.add('file_path')
71
+ return { handled: true, primary }
72
+ }
73
+
74
+ if (baseName !== 'NotebookEdit') {
75
+ return { handled: false }
76
+ }
77
+
78
+ const notebookPath = asString(record?.notebook_path)
79
+ const primary = notebookPath != null ? getFileInfo(notebookPath).filePath : undefined
80
+ const cellType = asString(record?.cell_type)
81
+
82
+ pushField(fields, usedKeys, 'cell_id', {
83
+ labelKey: 'chat.tools.fields.cellId',
84
+ fallbackLabel: 'Cell ID',
85
+ format: 'inline',
86
+ value: asString(record?.cell_id)
87
+ })
88
+ pushField(fields, usedKeys, 'cell_type', {
89
+ labelKey: 'chat.tools.fields.cellType',
90
+ fallbackLabel: 'Cell Type',
91
+ format: 'inline',
92
+ value: cellType
93
+ })
94
+ pushField(fields, usedKeys, 'edit_mode', {
95
+ labelKey: 'chat.tools.fields.editMode',
96
+ fallbackLabel: 'Edit Mode',
97
+ format: 'inline',
98
+ value: asString(record?.edit_mode)
99
+ })
100
+ pushField(fields, usedKeys, 'new_source', {
101
+ labelKey: 'chat.tools.fields.newSource',
102
+ fallbackLabel: 'New Source',
103
+ format: 'code',
104
+ value: asString(record?.new_source),
105
+ lang: cellType === 'markdown' ? 'markdown' : 'text'
106
+ })
107
+ usedKeys.add('notebook_path')
108
+ return { handled: true, primary }
109
+ }
@@ -0,0 +1,83 @@
1
+ import { Tooltip } from 'antd'
2
+ import React from 'react'
3
+
4
+ import { TOOL_TOOLTIP_PROPS, getToolFieldIcon } from '../core/tool-display'
5
+ import { renderToolBlockField } from '../core/tool-field-sections'
6
+ import type { ClaudeToolField, ClaudeToolQuestion } from './claude-tool-presentation'
7
+
8
+ type Translate = (key: string, options?: Record<string, unknown>) => string
9
+
10
+ export { ToolInlineFields as ClaudeToolInlineFields } from '../core/tool-field-sections'
11
+
12
+ const getSectionHeader = (icon: string, label: string) => (
13
+ <div className='tool-detail-section__header'>
14
+ <Tooltip title={label} {...TOOL_TOOLTIP_PROPS}>
15
+ <span className='tool-detail-section__icon material-symbols-rounded'>{icon}</span>
16
+ </Tooltip>
17
+ </div>
18
+ )
19
+
20
+ export function renderClaudeBlockField(field: ClaudeToolField, index: number, t: Translate) {
21
+ const label = t(field.labelKey, { defaultValue: field.fallbackLabel })
22
+ const sectionHeader = getSectionHeader(getToolFieldIcon(field.labelKey, field.format), label)
23
+
24
+ if (field.format === 'questions') {
25
+ const questions = Array.isArray(field.value) ? field.value as ClaudeToolQuestion[] : []
26
+ return (
27
+ <div
28
+ className='tool-detail-section claude-generic-tool__section'
29
+ key={`${field.labelKey}-${index}`}
30
+ >
31
+ {sectionHeader}
32
+ <div className='claude-generic-tool__questions'>
33
+ {questions.map((question, questionIndex) => (
34
+ <div
35
+ className='claude-generic-tool__question'
36
+ key={`${question.header ?? question.question}-${questionIndex}`}
37
+ >
38
+ <div className='claude-generic-tool__question-header'>
39
+ <div className='claude-generic-tool__question-title'>
40
+ {question.header ?? `${label} ${questionIndex + 1}`}
41
+ </div>
42
+ <Tooltip
43
+ title={question.multiSelect
44
+ ? t('chat.tools.multiSelect')
45
+ : t('chat.tools.singleSelect')}
46
+ {...TOOL_TOOLTIP_PROPS}
47
+ >
48
+ <span className='claude-generic-tool__question-mode material-symbols-rounded'>
49
+ {question.multiSelect ? 'checklist' : 'radio_button_checked'}
50
+ </span>
51
+ </Tooltip>
52
+ </div>
53
+ <div
54
+ className='tool-detail-section__text'
55
+ title={question.question}
56
+ >
57
+ {question.question}
58
+ </div>
59
+ {question.options.length > 0 && (
60
+ <div className='claude-generic-tool__question-options'>
61
+ {question.options.map(option => (
62
+ <div className='claude-generic-tool__question-option' key={option.label}>
63
+ <div className='claude-generic-tool__question-option-label'>{option.label}</div>
64
+ {option.description != null && option.description !== '' && (
65
+ <div className='claude-generic-tool__question-option-description'>
66
+ {option.description}
67
+ </div>
68
+ )}
69
+ </div>
70
+ ))}
71
+ </div>
72
+ )}
73
+ </div>
74
+ ))}
75
+ </div>
76
+ </div>
77
+ )
78
+ }
79
+
80
+ return renderToolBlockField(field, index, t, {
81
+ sectionClassName: 'tool-detail-section claude-generic-tool__section'
82
+ })
83
+ }
@@ -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
+ }