@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 +1 -1
- package/skills/mipham/self-audit.mipham-skill.md +46 -46
- package/src/core/auto-memory.ts +8 -5
- package/src/core/crsi-sandbox.ts +11 -6
- package/src/ui/app.tsx +41 -10
- package/src/ui/chat.tsx +13 -5
package/package.json
CHANGED
|
@@ -24,58 +24,58 @@ Mipham Code 审计 Mipham Code — 这是 CRSI 递归自我改进的第一个闭
|
|
|
24
24
|
|
|
25
25
|
### 1. 代码质量
|
|
26
26
|
|
|
27
|
-
| 检查项
|
|
28
|
-
|
|
29
|
-
| Dead code
|
|
30
|
-
| 不一致模式
|
|
31
|
-
| 类型安全
|
|
32
|
-
| 错误处理
|
|
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
|
-
| 循环依赖
|
|
40
|
-
| 接口契约
|
|
41
|
-
| 模块边界
|
|
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
|
-
| 同步阻塞
|
|
46
|
+
| 检查项 | 方法 |
|
|
47
|
+
| ------------ | ----------------------------------------------- |
|
|
48
|
+
| 同步阻塞 | 搜索 `readFileSync`、`execSync` 在主线程中 |
|
|
49
49
|
| 内存泄漏风险 | 搜索未清理的 setInterval、EventEmitter listener |
|
|
50
|
-
| 渲染性能
|
|
51
|
-
| N+1 模式
|
|
50
|
+
| 渲染性能 | 检查 React memo/callback 使用是否完整 |
|
|
51
|
+
| N+1 模式 | 搜索在循环内的 I/O 操作 |
|
|
52
52
|
|
|
53
53
|
### 4. 安全
|
|
54
54
|
|
|
55
|
-
| 检查项
|
|
56
|
-
|
|
57
|
-
| 硬编码凭据 | 搜索 API key、token、password 字符串
|
|
58
|
-
| 路径遍历
|
|
59
|
-
| 命令注入
|
|
60
|
-
| 许可合规
|
|
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
|
-
| 未测试模块
|
|
67
|
-
| 关键路径覆盖
|
|
64
|
+
| 检查项 | 方法 |
|
|
65
|
+
| --------------- | ------------------------------------------------- |
|
|
66
|
+
| 未测试模块 | Glob 所有 `src/**/*.ts`,对比 `test/` 目录 |
|
|
67
|
+
| 关键路径覆盖 | 识别 engine、permission、tools 层,检查测试 |
|
|
68
68
|
| Flaky test 风险 | 搜索 `setTimeout`、`Math.random`、Date 依赖的测试 |
|
|
69
|
-
| 边界测试缺失
|
|
69
|
+
| 边界测试缺失 | 检查主要函数的 null/undefined/empty 参数测试 |
|
|
70
70
|
|
|
71
71
|
### 6. CRSI 集成健康
|
|
72
72
|
|
|
73
|
-
| 检查项
|
|
74
|
-
|
|
75
|
-
| Rule 引擎状态
|
|
76
|
-
| 效果追踪
|
|
77
|
-
| 模式分析器
|
|
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
|
-
| 代码质量
|
|
131
|
-
| 架构完整性 | 8/10 | 3
|
|
132
|
-
| 性能
|
|
133
|
-
| 安全
|
|
134
|
-
| 测试覆盖
|
|
135
|
-
| CRSI 健康
|
|
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
|
-
| ...
|
|
151
|
+
| 规则 ID | 类型 | 状态 | 上次评估 |
|
|
152
|
+
| ------- | ---- | ------------------------ | -------- |
|
|
153
|
+
| ... | ... | active/degraded/disabled | ... |
|
|
154
154
|
```
|
|
155
155
|
|
|
156
156
|
## 安全约束
|
package/src/core/auto-memory.ts
CHANGED
|
@@ -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
|
-
? [
|
|
594
|
+
? [
|
|
595
|
+
'## ⚠️ 关键洞察',
|
|
596
|
+
'',
|
|
597
|
+
...criticalInsights.map(
|
|
598
|
+
(i) => `- **${i.category}**: ${i.description}\n → ${i.suggestion}`,
|
|
599
|
+
),
|
|
600
|
+
]
|
|
598
601
|
: []),
|
|
599
602
|
'',
|
|
600
603
|
'---',
|
package/src/core/crsi-sandbox.ts
CHANGED
|
@@ -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:
|
|
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 {
|
|
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 =
|
|
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: {
|
|
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
|
-
|
|
483
|
-
|
|
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:
|
|
489
|
-
toolMeta: {
|
|
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
|
-
|
|
63
|
-
|
|
64
|
-
{
|
|
65
|
-
|
|
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>
|
|
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}
|