@miphamai/cli 0.28.0 → 0.29.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@miphamai/cli",
3
- "version": "0.28.0",
3
+ "version": "0.29.0",
4
4
  "description": "Mipham Code — Multi-model open-core intelligent coding terminal by MiphamAI",
5
5
  "keywords": [
6
6
  "ai",
@@ -24,58 +24,58 @@ Mipham Code 审计 Mipham Code — 这是 CRSI 递归自我改进的第一个闭
24
24
 
25
25
  ### 1. 代码质量
26
26
 
27
- | 检查项 | 方法 |
28
- |--------|------|
29
- | Dead code | Grep 搜索未被引用的 export、未使用的 import |
30
- | 不一致模式 | 对比同一目录下多个文件的代码风格/模式差异 |
31
- | 类型安全 | 搜索 `as any`、`@ts-ignore`、`unknown` 未收窄 |
32
- | 错误处理 | 搜索裸 `catch`、无 `try/catch` 的 async 调用 |
33
- | Deep nesting | 搜索嵌套超过 4 层的 if/for/switch |
27
+ | 检查项 | 方法 |
28
+ | ------------ | --------------------------------------------- |
29
+ | Dead code | Grep 搜索未被引用的 export、未使用的 import |
30
+ | 不一致模式 | 对比同一目录下多个文件的代码风格/模式差异 |
31
+ | 类型安全 | 搜索 `as any`、`@ts-ignore`、`unknown` 未收窄 |
32
+ | 错误处理 | 搜索裸 `catch`、无 `try/catch` 的 async 调用 |
33
+ | Deep nesting | 搜索嵌套超过 4 层的 if/for/switch |
34
34
 
35
35
  ### 2. 架构完整性
36
36
 
37
- | 检查项 | 方法 |
38
- |--------|------|
39
- | 循环依赖 | 分析 import 图,检测 A→B→A |
40
- | 接口契约 | 对比 `shared/types.ts` 中的类型定义与实际使用 |
41
- | 模块边界 | 检查是否有跨层级直接访问(ui/ 直接 import core/ 内部实现) |
42
- | God objects | 搜索超过 500 行的单个类/函数 |
37
+ | 检查项 | 方法 |
38
+ | ----------- | ---------------------------------------------------------- |
39
+ | 循环依赖 | 分析 import 图,检测 A→B→A |
40
+ | 接口契约 | 对比 `shared/types.ts` 中的类型定义与实际使用 |
41
+ | 模块边界 | 检查是否有跨层级直接访问(ui/ 直接 import core/ 内部实现) |
42
+ | God objects | 搜索超过 500 行的单个类/函数 |
43
43
 
44
44
  ### 3. 性能
45
45
 
46
- | 检查项 | 方法 |
47
- |--------|------|
48
- | 同步阻塞 | 搜索 `readFileSync`、`execSync` 在主线程中 |
46
+ | 检查项 | 方法 |
47
+ | ------------ | ----------------------------------------------- |
48
+ | 同步阻塞 | 搜索 `readFileSync`、`execSync` 在主线程中 |
49
49
  | 内存泄漏风险 | 搜索未清理的 setInterval、EventEmitter listener |
50
- | 渲染性能 | 检查 React memo/callback 使用是否完整 |
51
- | N+1 模式 | 搜索在循环内的 I/O 操作 |
50
+ | 渲染性能 | 检查 React memo/callback 使用是否完整 |
51
+ | N+1 模式 | 搜索在循环内的 I/O 操作 |
52
52
 
53
53
  ### 4. 安全
54
54
 
55
- | 检查项 | 方法 |
56
- |--------|------|
57
- | 硬编码凭据 | 搜索 API key、token、password 字符串 |
58
- | 路径遍历 | 搜索使用用户输入的 `join`/`resolve` 路径 |
59
- | 命令注入 | 搜索字符串拼接的 shell 命令 |
60
- | 许可合规 | 检查 package.json 中的 copyleft 依赖 |
55
+ | 检查项 | 方法 |
56
+ | ---------- | ---------------------------------------- |
57
+ | 硬编码凭据 | 搜索 API key、token、password 字符串 |
58
+ | 路径遍历 | 搜索使用用户输入的 `join`/`resolve` 路径 |
59
+ | 命令注入 | 搜索字符串拼接的 shell 命令 |
60
+ | 许可合规 | 检查 package.json 中的 copyleft 依赖 |
61
61
 
62
62
  ### 5. 测试覆盖
63
63
 
64
- | 检查项 | 方法 |
65
- |--------|------|
66
- | 未测试模块 | Glob 所有 `src/**/*.ts`,对比 `test/` 目录 |
67
- | 关键路径覆盖 | 识别 engine、permission、tools 层,检查测试 |
64
+ | 检查项 | 方法 |
65
+ | --------------- | ------------------------------------------------- |
66
+ | 未测试模块 | Glob 所有 `src/**/*.ts`,对比 `test/` 目录 |
67
+ | 关键路径覆盖 | 识别 engine、permission、tools 层,检查测试 |
68
68
  | Flaky test 风险 | 搜索 `setTimeout`、`Math.random`、Date 依赖的测试 |
69
- | 边界测试缺失 | 检查主要函数的 null/undefined/empty 参数测试 |
69
+ | 边界测试缺失 | 检查主要函数的 null/undefined/empty 参数测试 |
70
70
 
71
71
  ### 6. CRSI 集成健康
72
72
 
73
- | 检查项 | 方法 |
74
- |--------|------|
75
- | Rule 引擎状态 | 检查活跃规则数、禁用规则数、builtin vs auto-generated |
76
- | 效果追踪 | 从 EffectivenessTracker 读取规则成功率 |
77
- | 模式分析器 | 检查累积的 Agent 失败模式 |
78
- | AutoMemory 状态 | 检查复盘文件数量、CRSI 洞察统计 |
73
+ | 检查项 | 方法 |
74
+ | --------------- | ----------------------------------------------------- |
75
+ | Rule 引擎状态 | 检查活跃规则数、禁用规则数、builtin vs auto-generated |
76
+ | 效果追踪 | 从 EffectivenessTracker 读取规则成功率 |
77
+ | 模式分析器 | 检查累积的 Agent 失败模式 |
78
+ | AutoMemory 状态 | 检查复盘文件数量、CRSI 洞察统计 |
79
79
 
80
80
  ## 执行流程
81
81
 
@@ -125,14 +125,14 @@ Mipham Code 审计 Mipham Code — 这是 CRSI 递归自我改进的第一个闭
125
125
 
126
126
  ## 摘要
127
127
 
128
- | 维度 | 评分 | 发现数 | 严重 |
129
- |------|------|--------|------|
130
- | 代码质量 | 7/10 | 12 | 2 |
131
- | 架构完整性 | 8/10 | 3 | 0 |
132
- | 性能 | 7/10 | 5 | 1 |
133
- | 安全 | 8/10 | 2 | 0 |
134
- | 测试覆盖 | 7/10 | 8 | 1 |
135
- | CRSI 健康 | 9/10 | 0 | 0 |
128
+ | 维度 | 评分 | 发现数 | 严重 |
129
+ | ---------- | ---- | ------ | ---- |
130
+ | 代码质量 | 7/10 | 12 | 2 |
131
+ | 架构完整性 | 8/10 | 3 | 0 |
132
+ | 性能 | 7/10 | 5 | 1 |
133
+ | 安全 | 8/10 | 2 | 0 |
134
+ | 测试覆盖 | 7/10 | 8 | 1 |
135
+ | CRSI 健康 | 9/10 | 0 | 0 |
136
136
 
137
137
  ## 🔴 严重 (需要立即处理)
138
138
 
@@ -148,9 +148,9 @@ Mipham Code 审计 Mipham Code — 这是 CRSI 递归自我改进的第一个闭
148
148
 
149
149
  ## CRSI 规则更新
150
150
 
151
- | 规则 ID | 类型 | 状态 | 上次评估 |
152
- |---------|------|------|---------|
153
- | ... | ... | active/degraded/disabled | ... |
151
+ | 规则 ID | 类型 | 状态 | 上次评估 |
152
+ | ------- | ---- | ------------------------ | -------- |
153
+ | ... | ... | active/degraded/disabled | ... |
154
154
  ```
155
155
 
156
156
  ## 安全约束
@@ -419,10 +419,7 @@ export class AutoMemoryEngine {
419
419
  return decisions.slice(0, 5)
420
420
  }
421
421
 
422
- private extractActionItems(
423
- assistantContent: string,
424
- toolCalls: ToolCallRecord[],
425
- ): string[] {
422
+ private extractActionItems(assistantContent: string, toolCalls: ToolCallRecord[]): string[] {
426
423
  const items: string[] = []
427
424
 
428
425
  // Check for TODO markers in assistant content
@@ -594,7 +591,13 @@ export class AutoMemoryEngine {
594
591
  `| 成功率 | ${totalSuccesses + totalFailures > 0 ? Math.round((totalSuccesses / (totalSuccesses + totalFailures)) * 100) : 100}% |`,
595
592
  '',
596
593
  ...(criticalInsights.length > 0
597
- ? ['## ⚠️ 关键洞察', '', ...criticalInsights.map((i) => `- **${i.category}**: ${i.description}\n → ${i.suggestion}`)]
594
+ ? [
595
+ '## ⚠️ 关键洞察',
596
+ '',
597
+ ...criticalInsights.map(
598
+ (i) => `- **${i.category}**: ${i.description}\n → ${i.suggestion}`,
599
+ ),
600
+ ]
598
601
  : []),
599
602
  '',
600
603
  '---',
@@ -52,7 +52,8 @@ export interface CrsiModificationResult {
52
52
  /** Error message if something failed */
53
53
  error?: string
54
54
  /** Current phase of the sandbox pipeline */
55
- phase: 'pending' | 'applied' | 'testing' | 'passed' | 'failed' | 'approved' | 'merged' | 'rolled-back'
55
+ phase:
56
+ 'pending' | 'applied' | 'testing' | 'passed' | 'failed' | 'approved' | 'merged' | 'rolled-back'
56
57
  }
57
58
 
58
59
  export interface CrsiTestResult {
@@ -154,7 +155,12 @@ export class CrsiSandbox {
154
155
  */
155
156
  applyModification(mod: CrsiModification): CrsiModificationResult {
156
157
  if (!this.worktreePath) {
157
- return { modification: mod, applied: false, phase: 'pending', error: 'No worktree created. Call createWorktree() first.' }
158
+ return {
159
+ modification: mod,
160
+ applied: false,
161
+ phase: 'pending',
162
+ error: 'No worktree created. Call createWorktree() first.',
163
+ }
158
164
  }
159
165
 
160
166
  this.sessionReport.summary.total++
@@ -180,7 +186,8 @@ export class CrsiSandbox {
180
186
  try {
181
187
  const currentContent = readFileSync(targetPath, 'utf-8')
182
188
  if (currentContent !== mod.originalContent && mod.originalContent) {
183
- result.error = 'Original content mismatch — file may have been modified since the audit. Aborting.'
189
+ result.error =
190
+ 'Original content mismatch — file may have been modified since the audit. Aborting.'
184
191
  result.phase = 'failed'
185
192
  this.sessionReport.modifications.push(result)
186
193
  return result
@@ -313,9 +320,7 @@ export class CrsiSandbox {
313
320
  }
314
321
 
315
322
  // Check that all modifications passed tests
316
- const failedCount = this.sessionReport.modifications.filter(
317
- (m) => m.phase === 'failed',
318
- ).length
323
+ const failedCount = this.sessionReport.modifications.filter((m) => m.phase === 'failed').length
319
324
  if (failedCount > 0) {
320
325
  return {
321
326
  success: false,
package/src/ui/app.tsx CHANGED
@@ -44,6 +44,8 @@ export interface ToolMeta {
44
44
  input: string
45
45
  output?: string
46
46
  collapsed: boolean
47
+ /** Original tool name before Claude Code display renaming (e.g. Write→Update). */
48
+ originalName?: string
47
49
  }
48
50
 
49
51
  export interface ChatMessage {
@@ -87,7 +89,8 @@ const PERMISSION_COLORS: Record<PermissionMode, string> = {
87
89
  bypassPermissions: 'red',
88
90
  }
89
91
 
90
- /** Format a tool's input parameters into a compact one-line detail string. */
92
+ /** Format a tool's input parameters into a compact one-line detail string.
93
+ * Tool display names follow Claude Code convention: Write/Edit → Update. */
91
94
  function formatToolDetail(name: string, input: Record<string, unknown>): string {
92
95
  switch (name) {
93
96
  case 'Bash':
@@ -95,9 +98,9 @@ function formatToolDetail(name: string, input: Record<string, unknown>): string
95
98
  case 'Read':
96
99
  return (input.file_path as string) || ''
97
100
  case 'Write':
98
- return (input.file_path as string) || ''
101
+ return (input.file_path as string) || '' // displayed as "Update" in chat
99
102
  case 'Edit':
100
- return (input.file_path as string) || ''
103
+ return `${(input.file_path as string) || ''}: ${((input.old_string as string) || '').slice(0, 60)}` // displayed as "Update" in chat
101
104
  case 'Grep':
102
105
  return (input.pattern as string) || ''
103
106
  case 'Glob':
@@ -115,6 +118,12 @@ function formatToolDetail(name: string, input: Record<string, unknown>): string
115
118
  }
116
119
  }
117
120
 
121
+ /** Claude Code display name: maps Write/Edit → Update for parity. */
122
+ function toolDisplayName(name: string): string {
123
+ if (name === 'Write' || name === 'Edit') return 'Update'
124
+ return name
125
+ }
126
+
118
127
  export function App({
119
128
  engine,
120
129
  config,
@@ -462,7 +471,12 @@ export function App({
462
471
  {
463
472
  role: 'system' as const,
464
473
  content: detail,
465
- toolMeta: { name: toolName, input: detail, collapsed: true },
474
+ toolMeta: {
475
+ name: toolDisplayName(toolName),
476
+ input: detail,
477
+ collapsed: true,
478
+ originalName: toolName,
479
+ },
466
480
  },
467
481
  ])
468
482
  // Flush stream buffer before showing tool card
@@ -475,18 +489,35 @@ export function App({
475
489
  setAgentProgress(null)
476
490
  // Sync background agents (e.g. Agent tool may have spawned them)
477
491
  syncBgAgents()
478
- // Only show tool results with meaningful content.
479
- // Skip empty results, "(no matches)", and other noise.
480
492
  const output = chunk.content ? String(chunk.content).trim() : ''
481
493
  if (output && output !== '(no matches)' && output.length > 20) {
482
- const firstLine = output.split('\n')[0]!.slice(0, 200)
483
- const preview = firstLine.length < output.length ? `${firstLine}...` : firstLine
494
+ // Generate diff-style summary for file operations (Claude Code parity)
495
+ let preview: string
496
+ const isFileOp = chunk.toolUse?.name === 'Write' || chunk.toolUse?.name === 'Edit'
497
+ if (isFileOp && chunk.toolUse?.input?.file_path) {
498
+ const filePath = String(chunk.toolUse.input.file_path)
499
+ const newContent = String(
500
+ chunk.toolUse.input.content || chunk.toolUse.input.new_string || '',
501
+ )
502
+ // Count lines added/removed for Write operations
503
+ const lineCount = newContent.split('\n').length
504
+ preview = `Updated ${filePath} · ${lineCount} lines`
505
+ } else {
506
+ const firstLine = output.split('\n')[0]!.slice(0, 200)
507
+ preview = firstLine.length < output.length ? `${firstLine}...` : firstLine
508
+ }
484
509
  setMessages((prev) => [
485
510
  ...prev,
486
511
  {
487
512
  role: 'system' as const,
488
- content: ` ⎿ ${preview}`,
489
- toolMeta: { name: '', input: '', output: preview, collapsed: true },
513
+ content: preview,
514
+ toolMeta: {
515
+ name: '',
516
+ input: '',
517
+ output: preview,
518
+ collapsed: true,
519
+ originalName: chunk.toolUse?.name,
520
+ },
490
521
  },
491
522
  ])
492
523
  }
package/src/ui/chat.tsx CHANGED
@@ -28,6 +28,7 @@ function toolColor(name: string): string {
28
28
  return 'white'
29
29
  case 'Write':
30
30
  case 'Edit':
31
+ case 'Update':
31
32
  return 'red'
32
33
  case 'Agent':
33
34
  case 'Task':
@@ -59,12 +60,19 @@ const MessageRow = React.memo(
59
60
  >
60
61
  {msg.toolMeta ? (
61
62
  <Box flexDirection="column">
62
- <Text color={toolColor(msg.toolMeta.name)}>
63
- {msg.toolMeta.collapsed ? '⏺' : '⏺ ▼'} {msg.toolMeta.name}
64
- {msg.toolMeta.input ? `(${msg.toolMeta.input.slice(0, 120)})` : ''}
65
- </Text>
63
+ {/* Tool call line: ⏺ ToolName(detail) — Claude Code parity */}
64
+ {msg.toolMeta.name ? (
65
+ <Text color={toolColor(msg.toolMeta.name)}>
66
+ {msg.toolMeta.collapsed ? '⏺' : '⏺ ▼'} {msg.toolMeta.name}
67
+ {msg.toolMeta.input ? ` (${msg.toolMeta.input.slice(0, 120)})` : ''}
68
+ </Text>
69
+ ) : null}
70
+ {/* Tool result line: ⎿ summary text */}
66
71
  {msg.toolMeta.output ? (
67
- <Text dimColor> ⎿ {msg.toolMeta.output.slice(0, 300)}</Text>
72
+ <Text dimColor>
73
+ {' ⎿ '}
74
+ {msg.toolMeta.output.slice(0, 300)}
75
+ </Text>
68
76
  ) : msg.content && !msg.toolMeta.collapsed ? (
69
77
  <Text dimColor>{msg.content}</Text>
70
78
  ) : null}