@miphamai/cli 0.32.2 → 0.32.3

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.3",
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,13 @@ 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 = { name: '', description: '', startTime: progressStart, tokensUsed: 0 }
448
+ agentProgressRef.current = progress
449
+ setAgentProgress(progress)
450
+ setAgentTick((t) => t + 1)
451
+
439
452
  const controller = new AbortController()
440
453
  abortRef.current = controller
441
454
 
@@ -509,18 +522,24 @@ export function App({
509
522
  const isAgent = toolName === 'Agent' || toolName === 'Task'
510
523
  const detail = formatToolDetail(toolName, chunk.toolUse.input)
511
524
 
512
- // Show [ToolName detail...] activity indicator for ALL tools
513
- setActiveTool({ name: toolDisplayName(toolName), detail })
525
+ // Show [ToolName detail...] activity indicator for ALL tools (via ref for immediate render)
526
+ const toolEntry = { name: toolDisplayName(toolName), detail }
527
+ activeToolRef.current = toolEntry
528
+ setActiveTool(toolEntry)
529
+ setAgentTick((t) => t + 1)
514
530
 
515
531
  if (isAgent) {
516
- setAgentProgress({
532
+ const ap: AgentProgress = {
517
533
  name: detail || (chunk.toolUse.input.subagent_type as string) || 'General-purpose',
518
534
  description:
519
535
  (chunk.toolUse.input.description as string) ||
520
536
  (chunk.toolUse.input.prompt as string) ||
521
537
  '',
522
538
  startTime: Date.now(),
523
- })
539
+ }
540
+ agentProgressRef.current = ap
541
+ setAgentProgress(ap)
542
+ setAgentTick((t) => t + 1)
524
543
  }
525
544
 
526
545
  // Show tool call as a visible, collapsed message
@@ -544,8 +563,10 @@ export function App({
544
563
  }
545
564
 
546
565
  if (chunk.type === 'tool_result') {
547
- setAgentProgress(null)
566
+ // Clear tool indicator but keep agent progress (AI is still processing)
567
+ activeToolRef.current = null
548
568
  setActiveTool(null)
569
+ setAgentTick((t) => t + 1)
549
570
  // Sync background agents (e.g. Agent tool may have spawned them)
550
571
  syncBgAgents()
551
572
  const output = chunk.content ? String(chunk.content).trim() : ''
@@ -582,12 +603,14 @@ export function App({
582
603
  }
583
604
  }
584
605
 
585
- if (chunk.type === 'usage' && agentProgress) {
606
+ if (chunk.type === 'usage') {
586
607
  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
- )
608
+ if (totalTokens > 0 && agentProgressRef.current) {
609
+ agentProgressRef.current = {
610
+ ...agentProgressRef.current,
611
+ tokensUsed: (agentProgressRef.current.tokensUsed || 0) + totalTokens,
612
+ }
613
+ setAgentProgress({ ...agentProgressRef.current })
591
614
  }
592
615
  }
593
616
 
@@ -631,6 +654,12 @@ export function App({
631
654
  flushStreamBuffer()
632
655
  setIsLoading(false)
633
656
  abortRef.current = null
657
+ // Clear all progress/tool indicators
658
+ agentProgressRef.current = null
659
+ setAgentProgress(null)
660
+ activeToolRef.current = null
661
+ setActiveTool(null)
662
+ setAgentTick((t) => t + 1)
634
663
  // Auto-save checkpoint after each AI response
635
664
  if (assistantContent) {
636
665
  engine.getContext().saveCheckpoint('post-turn')