@miphamai/cli 0.32.2 → 0.32.4

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miphamai/cli",
3
- "version": "0.32.2",
3
+ "version": "0.32.4",
4
4
  "description": "Mipham Code — Multi-model open-core intelligent coding terminal by MiphamAI",
5
5
  "keywords": [
6
6
  "ai",
@@ -48,11 +48,25 @@ function formatTokens(n: number): string {
48
48
  /** Map tool name to Ink color for the [Tool ...] indicator. */
49
49
  function toolIndicatorColor(name: string): string {
50
50
  switch (name) {
51
- case 'Bash': case 'Read': case 'Glob': case 'Grep': return 'white'
52
- case 'Write': case 'Edit': case 'Update': return 'red'
53
- case 'Agent': case 'Task': case 'Skill': case 'Workflow': return 'magenta'
54
- case 'WebSearch': case 'WebFetch': return 'blue'
55
- default: return 'white'
51
+ case 'Bash':
52
+ case 'Read':
53
+ case 'Glob':
54
+ case 'Grep':
55
+ return 'white'
56
+ case 'Write':
57
+ case 'Edit':
58
+ case 'Update':
59
+ return 'red'
60
+ case 'Agent':
61
+ case 'Task':
62
+ case 'Skill':
63
+ case 'Workflow':
64
+ return 'magenta'
65
+ case 'WebSearch':
66
+ case 'WebFetch':
67
+ return 'blue'
68
+ default:
69
+ return 'white'
56
70
  }
57
71
  }
58
72
 
@@ -64,10 +78,22 @@ function animatedDots(tick: number): string {
64
78
 
65
79
  /** Gerund verbs for agent progress — rotated every 2 seconds for visual liveliness. */
66
80
  const GERUNDS = [
67
- 'Pondering', 'Contemplating', 'Cerebrating', 'Ruminating',
68
- 'Deliberating', 'Cogitating', 'Perambulating', 'Computing',
69
- 'Processing', 'Analyzing', 'Synthesizing', 'Orchestrating',
70
- 'Forging', 'Illuminating', 'Manifesting', 'Transmogrifying',
81
+ 'Pondering',
82
+ 'Contemplating',
83
+ 'Cerebrating',
84
+ 'Ruminating',
85
+ 'Deliberating',
86
+ 'Cogitating',
87
+ 'Perambulating',
88
+ 'Computing',
89
+ 'Processing',
90
+ 'Analyzing',
91
+ 'Synthesizing',
92
+ 'Orchestrating',
93
+ 'Forging',
94
+ 'Illuminating',
95
+ 'Manifesting',
96
+ 'Transmogrifying',
71
97
  ]
72
98
 
73
99
  /** Pick a gerund that changes every 2 seconds (driven by tick). */
@@ -89,7 +115,8 @@ export function AgentFooter({ agents, tick, activeTool, agentProgress }: AgentFo
89
115
  {hasTool && (
90
116
  <Box>
91
117
  <Text color={toolIndicatorColor(activeTool!.name)}>
92
- {' '}[{activeTool!.name} {activeTool!.detail.slice(0, 60)}{animatedDots(tick)}]
118
+ {' '}[{activeTool!.name} {activeTool!.detail.slice(0, 60)}
119
+ {animatedDots(tick)}]
93
120
  </Text>
94
121
  </Box>
95
122
  )}
package/src/ui/app.tsx CHANGED
@@ -185,11 +185,17 @@ export function App({
185
185
  const [agentTick, setAgentTick] = useState(0)
186
186
  /** Active foreground tool indicator: [Bash command...], [Update file.ts...], etc. */
187
187
  const [activeTool, setActiveTool] = useState<{ name: string; detail: string } | null>(null)
188
+ // Refs for immediate state (bypasses React batching so footer renders between rapid chunks)
189
+ const activeToolRef = useRef<{ name: string; detail: string } | null>(null)
190
+ const agentProgressRef = useRef<AgentProgress | null>(null)
188
191
 
189
- // Tick timer for agent elapsed displays (re-renders every second while agents are running)
192
+ // Tick timer for agent elapsed displays (re-renders every second while agents are running).
193
+ // Uses refs so the timer doesn't stop between state batches.
190
194
  useEffect(() => {
191
195
  const hasRunning =
192
- Object.values(runningAgents).some((a) => a.status === 'running') || agentProgress !== null || activeTool !== null
196
+ Object.values(runningAgents).some((a) => a.status === 'running') ||
197
+ agentProgressRef.current !== null ||
198
+ activeToolRef.current !== null
193
199
  if (!hasRunning) return
194
200
  const i = setInterval(() => setAgentTick((t) => t + 1), 1000)
195
201
  return () => clearInterval(i)
@@ -436,6 +442,18 @@ export function App({
436
442
  setMessages((prev) => [...prev, { role: 'user', content: input }])
437
443
  setIsLoading(true)
438
444
 
445
+ // Start agent progress indicator immediately for ALL processing
446
+ const progressStart = Date.now()
447
+ const progress: AgentProgress = {
448
+ name: '',
449
+ description: '',
450
+ startTime: progressStart,
451
+ tokensUsed: 0,
452
+ }
453
+ agentProgressRef.current = progress
454
+ setAgentProgress(progress)
455
+ setAgentTick((t) => t + 1)
456
+
439
457
  const controller = new AbortController()
440
458
  abortRef.current = controller
441
459
 
@@ -509,18 +527,24 @@ export function App({
509
527
  const isAgent = toolName === 'Agent' || toolName === 'Task'
510
528
  const detail = formatToolDetail(toolName, chunk.toolUse.input)
511
529
 
512
- // Show [ToolName detail...] activity indicator for ALL tools
513
- setActiveTool({ name: toolDisplayName(toolName), detail })
530
+ // Show [ToolName detail...] activity indicator for ALL tools (via ref for immediate render)
531
+ const toolEntry = { name: toolDisplayName(toolName), detail }
532
+ activeToolRef.current = toolEntry
533
+ setActiveTool(toolEntry)
534
+ setAgentTick((t) => t + 1)
514
535
 
515
536
  if (isAgent) {
516
- setAgentProgress({
537
+ const ap: AgentProgress = {
517
538
  name: detail || (chunk.toolUse.input.subagent_type as string) || 'General-purpose',
518
539
  description:
519
540
  (chunk.toolUse.input.description as string) ||
520
541
  (chunk.toolUse.input.prompt as string) ||
521
542
  '',
522
543
  startTime: Date.now(),
523
- })
544
+ }
545
+ agentProgressRef.current = ap
546
+ setAgentProgress(ap)
547
+ setAgentTick((t) => t + 1)
524
548
  }
525
549
 
526
550
  // Show tool call as a visible, collapsed message
@@ -544,8 +568,10 @@ export function App({
544
568
  }
545
569
 
546
570
  if (chunk.type === 'tool_result') {
547
- setAgentProgress(null)
571
+ // Clear tool indicator but keep agent progress (AI is still processing)
572
+ activeToolRef.current = null
548
573
  setActiveTool(null)
574
+ setAgentTick((t) => t + 1)
549
575
  // Sync background agents (e.g. Agent tool may have spawned them)
550
576
  syncBgAgents()
551
577
  const output = chunk.content ? String(chunk.content).trim() : ''
@@ -582,12 +608,14 @@ export function App({
582
608
  }
583
609
  }
584
610
 
585
- if (chunk.type === 'usage' && agentProgress) {
611
+ if (chunk.type === 'usage') {
586
612
  const totalTokens = (chunk.inputTokens || 0) + (chunk.outputTokens || 0)
587
- if (totalTokens > 0) {
588
- setAgentProgress((prev) =>
589
- prev ? { ...prev, tokensUsed: (prev.tokensUsed || 0) + totalTokens } : null,
590
- )
613
+ if (totalTokens > 0 && agentProgressRef.current) {
614
+ agentProgressRef.current = {
615
+ ...agentProgressRef.current,
616
+ tokensUsed: (agentProgressRef.current.tokensUsed || 0) + totalTokens,
617
+ }
618
+ setAgentProgress({ ...agentProgressRef.current })
591
619
  }
592
620
  }
593
621
 
@@ -631,6 +659,12 @@ export function App({
631
659
  flushStreamBuffer()
632
660
  setIsLoading(false)
633
661
  abortRef.current = null
662
+ // Clear all progress/tool indicators
663
+ agentProgressRef.current = null
664
+ setAgentProgress(null)
665
+ activeToolRef.current = null
666
+ setActiveTool(null)
667
+ setAgentTick((t) => t + 1)
634
668
  // Auto-save checkpoint after each AI response
635
669
  if (assistantContent) {
636
670
  engine.getContext().saveCheckpoint('post-turn')
@@ -671,14 +705,6 @@ export function App({
671
705
  if (apiKeyPrompt) {
672
706
  return (
673
707
  <Box flexDirection="column" padding={1} height="100%">
674
- {/* Header */}
675
- <Box flexDirection="column" marginBottom={1}>
676
- <Text color="#FFD700" bold>
677
- Mipham Code
678
- </Text>
679
- <Text dimColor>v{version || '0.0.0'}</Text>
680
- </Box>
681
-
682
708
  {/* Chat panel — show existing messages */}
683
709
  <ChatPanel messages={messages} focusMode={false} />
684
710
 
@@ -718,15 +744,6 @@ export function App({
718
744
 
719
745
  return (
720
746
  <Box flexDirection="column" padding={1} height="100%">
721
- {/* Header — left-aligned */}
722
- <Box flexDirection="column" marginBottom={1}>
723
- <Text color="#FFD700" bold>
724
- Mipham Code
725
- </Text>
726
- <Text dimColor>v{version || '0.0.0'}</Text>
727
- <Text dimColor>{modelId}</Text>
728
- </Box>
729
-
730
747
  {/* Workflow progress — auto-detects active workflows, renders nothing when idle */}
731
748
  <WorkflowProgress />
732
749
 
package/src/ui/chat.tsx CHANGED
@@ -79,17 +79,15 @@ const MessageRow = React.memo(
79
79
  </Box>
80
80
  ) : (
81
81
  <>
82
- <Text
83
- bold
84
- color={msg.role === 'user' ? 'green' : msg.role === 'system' ? 'yellow' : 'blue'}
85
- >
86
- {msg.role === 'user'
87
- ? `▸ ${displayCwd()}`
88
- : msg.role === 'assistant'
89
- ? t('ui.assistant.role_label')
90
- : `⚠ ${t('ui.system.role_label')}`}
91
- :
92
- </Text>
82
+ {msg.role === 'user' ? (
83
+ <Text bold color="green">
84
+ ▸ {displayCwd()}:
85
+ </Text>
86
+ ) : msg.role === 'system' ? (
87
+ <Text bold color="yellow">
88
+ ⚠ {t('ui.system.role_label')}:
89
+ </Text>
90
+ ) : null}
93
91
  <Text>{msg.content}</Text>
94
92
  </>
95
93
  )}