@miphamai/cli 0.16.3 → 0.16.5
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/package.json +1 -1
- package/src/ui/app.tsx +21 -30
- package/src/ui/chat.tsx +55 -2
package/package.json
CHANGED
package/src/ui/app.tsx
CHANGED
|
@@ -59,16 +59,21 @@ interface AgentProgress {
|
|
|
59
59
|
// Version is read fresh from package.json at startup via runApp prop
|
|
60
60
|
// (bypasses Bun module caching after npm update)
|
|
61
61
|
|
|
62
|
+
// Cycle order: Claude Code modes first (manual → accept edits → bypass),
|
|
63
|
+
// then Mipham-specific modes (plan → auto → dont ask).
|
|
62
64
|
const PERMISSION_MODES: PermissionMode[] = [
|
|
63
65
|
'default',
|
|
64
66
|
'acceptEdits',
|
|
67
|
+
'bypassPermissions',
|
|
65
68
|
'plan',
|
|
66
69
|
'auto',
|
|
67
70
|
'dontAsk',
|
|
68
|
-
'bypassPermissions',
|
|
69
71
|
]
|
|
72
|
+
// Labels aligned with Claude Code terminology: describe behavior, not capability.
|
|
73
|
+
// Claude Code modes: manual mode → accept edits on → bypass
|
|
74
|
+
// Mipham extends with 3 extra modes (plan, auto, dontAsk) for finer control.
|
|
70
75
|
const PERMISSION_LABELS: Record<PermissionMode, string> = {
|
|
71
|
-
default: '
|
|
76
|
+
default: 'manual mode',
|
|
72
77
|
acceptEdits: 'accept edits on',
|
|
73
78
|
plan: 'plan mode · read-only',
|
|
74
79
|
auto: 'auto mode',
|
|
@@ -348,18 +353,10 @@ export function App({
|
|
|
348
353
|
|
|
349
354
|
try {
|
|
350
355
|
for await (const chunk of engine.process(input, controller.signal)) {
|
|
351
|
-
//
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
const last = updated[updated.length - 1]
|
|
356
|
-
if (last?.role === 'assistant' && last.content === '') {
|
|
357
|
-
last.content = '💭 思考中…'
|
|
358
|
-
} else if (!last || last.role !== 'assistant' || last.content !== '💭 思考中…') {
|
|
359
|
-
updated.push({ role: 'assistant', content: '💭 思考中…' })
|
|
360
|
-
}
|
|
361
|
-
return updated
|
|
362
|
-
})
|
|
356
|
+
// Reasoning content (DeepSeek V4 thinking mode) — silently consumed,
|
|
357
|
+
// not shown to user to avoid noise.
|
|
358
|
+
if (chunk.reasoning_content) {
|
|
359
|
+
continue
|
|
363
360
|
}
|
|
364
361
|
|
|
365
362
|
if (chunk.type === 'text' && chunk.content) {
|
|
@@ -367,17 +364,10 @@ export function App({
|
|
|
367
364
|
if (isNewTurn) {
|
|
368
365
|
turnContent = chunk.content
|
|
369
366
|
isNewTurn = false
|
|
370
|
-
setMessages((prev) =>
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
if (last?.role === 'assistant' && last.content === '💭 思考中…') {
|
|
375
|
-
last.content = turnContent
|
|
376
|
-
} else {
|
|
377
|
-
updated.push({ role: 'assistant', content: turnContent })
|
|
378
|
-
}
|
|
379
|
-
return updated
|
|
380
|
-
})
|
|
367
|
+
setMessages((prev) => [
|
|
368
|
+
...prev,
|
|
369
|
+
{ role: 'assistant', content: turnContent },
|
|
370
|
+
])
|
|
381
371
|
} else {
|
|
382
372
|
turnContent += chunk.content
|
|
383
373
|
setMessages((prev) => {
|
|
@@ -425,9 +415,10 @@ export function App({
|
|
|
425
415
|
setAgentProgress(null)
|
|
426
416
|
// Sync background agents (e.g. Agent tool may have spawned them)
|
|
427
417
|
syncBgAgents()
|
|
428
|
-
//
|
|
418
|
+
// Only show tool results with meaningful content.
|
|
419
|
+
// Skip empty results, "(no matches)", and other noise.
|
|
429
420
|
const output = chunk.content ? String(chunk.content).trim() : ''
|
|
430
|
-
if (output) {
|
|
421
|
+
if (output && output !== '(no matches)' && output.length > 20) {
|
|
431
422
|
const firstLine = output.split('\n')[0]!.slice(0, 200)
|
|
432
423
|
const preview = firstLine.length < output.length ? `${firstLine}...` : firstLine
|
|
433
424
|
setMessages((prev) => [
|
|
@@ -608,7 +599,7 @@ export function App({
|
|
|
608
599
|
{/* Agent status footer — shows running background agents */}
|
|
609
600
|
<AgentFooter agents={Object.values(runningAgents)} gitBranch={gitBranch} tick={agentTick} />
|
|
610
601
|
|
|
611
|
-
{/* Status line — Claude Code style
|
|
602
|
+
{/* Status line — Claude Code style */}
|
|
612
603
|
<Box marginTop={1} flexDirection="column">
|
|
613
604
|
{goalText && (
|
|
614
605
|
<Box>
|
|
@@ -619,8 +610,8 @@ export function App({
|
|
|
619
610
|
<Text color={PERMISSION_COLORS[permissionMode]}>
|
|
620
611
|
⏵⏵ {PERMISSION_LABELS[permissionMode]} (shift+tab to cycle)
|
|
621
612
|
</Text>
|
|
622
|
-
<Text dimColor> ·
|
|
623
|
-
<Text dimColor> · ← agents</Text>
|
|
613
|
+
<Text dimColor> · esc to interrupt</Text>
|
|
614
|
+
<Text dimColor> · ← for agents</Text>
|
|
624
615
|
</Box>
|
|
625
616
|
</Box>
|
|
626
617
|
</Box>
|
package/src/ui/chat.tsx
CHANGED
|
@@ -93,8 +93,11 @@ const MessageRow = React.memo(
|
|
|
93
93
|
)
|
|
94
94
|
|
|
95
95
|
export function ChatPanel({ messages, focusMode }: ChatPanelProps) {
|
|
96
|
-
//
|
|
97
|
-
|
|
96
|
+
// Group consecutive tool calls into compact summaries to reduce visual noise.
|
|
97
|
+
// In focus mode, use the more aggressive compactForFocus.
|
|
98
|
+
const displayMessages = focusMode
|
|
99
|
+
? compactForFocus(messages)
|
|
100
|
+
: compactToolGroups(messages)
|
|
98
101
|
|
|
99
102
|
return (
|
|
100
103
|
<Box flexDirection="column" marginY={1} flexGrow={1}>
|
|
@@ -141,6 +144,56 @@ export function ChatPanel({ messages, focusMode }: ChatPanelProps) {
|
|
|
141
144
|
)
|
|
142
145
|
}
|
|
143
146
|
|
|
147
|
+
/**
|
|
148
|
+
* Group consecutive tool calls into compact one-liners for the normal view.
|
|
149
|
+
* Single tools show normally; 2+ consecutive tools get folded:
|
|
150
|
+
* ⏺ Read · Glob · Bash (3 tools)
|
|
151
|
+
*/
|
|
152
|
+
function compactToolGroups(messages: ChatMessage[]): ChatMessage[] {
|
|
153
|
+
const result: ChatMessage[] = []
|
|
154
|
+
let toolGroup: ChatMessage[] = []
|
|
155
|
+
|
|
156
|
+
const flush = () => {
|
|
157
|
+
if (toolGroup.length === 0) return
|
|
158
|
+
if (toolGroup.length === 1) {
|
|
159
|
+
result.push(toolGroup[0]!)
|
|
160
|
+
} else {
|
|
161
|
+
const names = toolGroup
|
|
162
|
+
.filter((t) => t.toolMeta?.name && !t.toolMeta.name.startsWith('tools ('))
|
|
163
|
+
.map((t) => t.toolMeta!.name)
|
|
164
|
+
const uniqueNames = [...new Set(names)]
|
|
165
|
+
result.push({
|
|
166
|
+
role: 'system',
|
|
167
|
+
content: `⏺ ${uniqueNames.join(' · ')} (${names.length} tools)`,
|
|
168
|
+
toolMeta: {
|
|
169
|
+
name: `tools (${names.length})`,
|
|
170
|
+
input: uniqueNames.join(', '),
|
|
171
|
+
collapsed: true,
|
|
172
|
+
},
|
|
173
|
+
})
|
|
174
|
+
}
|
|
175
|
+
toolGroup = []
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
for (const msg of messages) {
|
|
179
|
+
// Tool results (no name, has output) get folded into the preceding tool group
|
|
180
|
+
if (msg.toolMeta && !msg.toolMeta.name && msg.toolMeta.output) {
|
|
181
|
+
toolGroup.push(msg)
|
|
182
|
+
continue
|
|
183
|
+
}
|
|
184
|
+
// Tool calls with a name
|
|
185
|
+
if (msg.toolMeta?.name && !msg.toolMeta.name.startsWith('tools (')) {
|
|
186
|
+
toolGroup.push(msg)
|
|
187
|
+
continue
|
|
188
|
+
}
|
|
189
|
+
// Already-grouped or non-tool message
|
|
190
|
+
flush()
|
|
191
|
+
result.push(msg)
|
|
192
|
+
}
|
|
193
|
+
flush()
|
|
194
|
+
return result
|
|
195
|
+
}
|
|
196
|
+
|
|
144
197
|
/**
|
|
145
198
|
* Compact messages for focus view:
|
|
146
199
|
* - Groups consecutive tool calls into [▶ N tools] ↳ name1 ↳ name2 summary lines
|