@miphamai/cli 0.49.0 → 0.51.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.
Files changed (58) hide show
  1. package/bin/mipham.ts +1 -9
  2. package/package.json +1 -1
  3. package/skills/standard/github-ops.SKILL.md +1 -1
  4. package/skills/standard/web-access/references/cdp-api.md +2 -1
  5. package/skills/standard/web-access/scripts/cdp-proxy.mjs +75 -1
  6. package/skills/standard/web-access/scripts/check-deps.mjs +17 -2
  7. package/skills/standard/web-access/scripts/find-url.mjs +0 -0
  8. package/skills/standard/web-access/scripts/match-site.mjs +0 -0
  9. package/skills/standard/web-access.SKILL.md +10 -3
  10. package/src/agent/cross-session/discovery.ts +26 -1
  11. package/src/commands/project.ts +1 -1
  12. package/src/config/defaults.ts +8 -0
  13. package/src/core/auto-memory.ts +3 -3
  14. package/src/core/behavior-tasks.json +101 -0
  15. package/src/core/behavior-tasks.ts +44 -0
  16. package/src/core/constitution-loader.ts +0 -2
  17. package/src/core/context.ts +0 -1
  18. package/src/core/credential-masker/index.ts +1 -0
  19. package/src/core/credential-masker/matcher.ts +18 -7
  20. package/src/core/credential-masker/search.ts +73 -0
  21. package/src/core/credential-masker.ts +1 -0
  22. package/src/core/crsi-producer.ts +214 -0
  23. package/src/core/crsi-sandbox.ts +5 -1
  24. package/src/core/dream-engine.ts +0 -2
  25. package/src/core/eval-harness.ts +7 -0
  26. package/src/core/instructions.ts +11 -0
  27. package/src/core/memory/memory-loader.ts +3 -1
  28. package/src/core/permission-rules.ts +23 -12
  29. package/src/core/proposal-guard.ts +100 -0
  30. package/src/core/rules-loader.ts +8 -10
  31. package/src/core/task-runner-tasks.json +14 -0
  32. package/src/core/task-runner.ts +163 -0
  33. package/src/daemon/database.ts +0 -1
  34. package/src/daemon/feishu/adapter.ts +2 -1
  35. package/src/daemon/heartbeat.ts +83 -0
  36. package/src/daemon/index.ts +18 -8
  37. package/src/daemon/server.ts +78 -3
  38. package/src/daemon/session-manager.ts +5 -4
  39. package/src/daemon/telegram/adapter.ts +70 -0
  40. package/src/daemon/telegram/api.ts +45 -0
  41. package/src/daemon/telegram/env.ts +14 -0
  42. package/src/daemon/telegram/poller.ts +64 -0
  43. package/src/daemon/telegram/types.ts +10 -0
  44. package/src/i18n-core/locales/en-US.json +123 -0
  45. package/src/i18n-core/locales/zh-CN.json +123 -0
  46. package/src/index.tsx +6 -0
  47. package/src/shared/package-info.ts +4 -1
  48. package/src/shared/types.ts +2 -0
  49. package/src/skills/bundled-skill-assets.ts +5 -5
  50. package/src/skills/bundled-skills.ts +2 -2
  51. package/src/tools/agent/exit-plan.ts +1 -3
  52. package/src/tools/agent/memory.ts +2 -1
  53. package/src/tools/file/glob.ts +38 -23
  54. package/src/tools/file/grep.ts +96 -64
  55. package/src/tools/index.ts +5 -5
  56. package/src/ui/app.tsx +21 -1
  57. package/src/ui/commands.ts +269 -125
  58. package/src/ui/config-wizard.tsx +1 -1
@@ -15,12 +15,19 @@ import { runCrsiModification, approvePending, rejectPending, hasPending } from '
15
15
  import {
16
16
  produceCrsiProposal,
17
17
  produceRuleProposal,
18
+ produceProseProposal,
18
19
  selectCrsiSignal,
20
+ collectSkillFiles,
21
+ proseProposalId,
22
+ hasProposedProse,
23
+ appendProseProposal,
24
+ clearProseProposals,
19
25
  LESSONS_FILE,
20
26
  MANAGED_RULES_FILE,
21
27
  } from '../core/crsi-producer'
28
+ import { prefilterProposal } from '../core/proposal-guard'
22
29
  import { runEval, appendEvalScore } from '../core/eval-harness'
23
- import { NPM_UPDATE_COMMAND, PACKAGE_VERSION } from '../shared/index.ts'
30
+ import { NPM_UPDATE_COMMAND, PACKAGE_VERSION, COAUTHOR_TRAILER } from '../shared/index.ts'
24
31
  import { getPreference } from '../config/preferences'
25
32
  import { loadCrossSessionConfig } from '../config/loader'
26
33
  import { stripIndent } from './strip-indent.js'
@@ -818,6 +825,62 @@ const crsiProposeCmd: CommandHandler = async (ctx, args) => {
818
825
  // 非 git 目录 → 回退 cwd
819
826
  }
820
827
 
828
+ // ── 散文提议路径:/crsi propose --prose 用 LLM 生成改 skill 散文提议 ──
829
+ if (args[0] === '--prose') {
830
+ const skillFiles = collectSkillFiles(root)
831
+ if (skillFiles.length === 0) {
832
+ return { content: '没有可改的 skill 文件。' }
833
+ }
834
+
835
+ const signal = selectCrsiSignal(insights, metaRules)
836
+ if (!signal) {
837
+ return { content: '没有足够的失败信号来生成散文提议。' }
838
+ }
839
+
840
+ const id = proseProposalId(signal)
841
+ if (hasProposedProse(id)) {
842
+ return { content: '该失败信号已生成过散文提议(幂等去重,跳过)。' }
843
+ }
844
+
845
+ const llm = ctx.engine.getLlm() ?? ctx.engine.getRegistry()
846
+ const { readFileSync } = await import('node:fs')
847
+ const { join } = await import('node:path')
848
+ const proposal = await produceProseProposal(signal, llm, skillFiles, (p) =>
849
+ readFileSync(join(root, p), 'utf-8'),
850
+ )
851
+ if (!proposal) {
852
+ return { content: '散文提议生成失败(LLM 未返回有效结果)。' }
853
+ }
854
+
855
+ const verdict = prefilterProposal({
856
+ id,
857
+ filePath: proposal.filePath,
858
+ kind: 'skill',
859
+ newContent: proposal.newContent,
860
+ })
861
+ if (!verdict.pass) {
862
+ return { content: `❌ 提议未通过预筛:${verdict.reasons.join('; ')}` }
863
+ }
864
+
865
+ const result = runCrsiModification({
866
+ description: proposal.description,
867
+ filePath: proposal.filePath,
868
+ newContent: proposal.newContent,
869
+ originalContent: proposal.originalContent,
870
+ })
871
+ if (!result.applied || result.phase === 'failed') {
872
+ return { content: `❌ 生成失败(phase: ${result.phase})。\n${result.error ?? ''}` }
873
+ }
874
+
875
+ appendProseProposal({ id, filePath: proposal.filePath, timestamp: new Date().toISOString() })
876
+
877
+ return {
878
+ content:
879
+ `✅ 已生成散文提议并跑过测试。审阅 diff:\n\n${result.diff}\n\n` +
880
+ '/crsi modify --approve 合并 | /crsi modify --reject 丢弃',
881
+ }
882
+ }
883
+
821
884
  // ── 毕业路径:/crsi propose --rule 固化受管理规则(行为) ──
822
885
  if (args[0] === '--rule') {
823
886
  const signal = selectCrsiSignal(insights, metaRules)
@@ -900,6 +963,14 @@ const crsiEvalCmd: CommandHandler = async () => {
900
963
  return { content: lines.join('\n') }
901
964
  }
902
965
 
966
+ const crsiProseClearCmd: CommandHandler = () => {
967
+ const count = clearProseProposals()
968
+ if (count === 0) {
969
+ return { content: '散文提议 ledger 为空(无可清除记录)。' }
970
+ }
971
+ return { content: `已清空散文提议 ledger(移除 ${count} 条记录)。` }
972
+ }
973
+
903
974
  const crsiHealthCmd: CommandHandler = async (ctx) => {
904
975
  const engine = ctx.engine.getRuleEngine()
905
976
  const tracker = ctx.engine.getEffectivenessTracker()
@@ -951,8 +1022,6 @@ const crsiHealthCmd: CommandHandler = async (ctx) => {
951
1022
  lines.push('', '### 🛡️ SIS 自免疫子系统', '')
952
1023
  if (db) {
953
1024
  const stats = db.getStats()
954
- const healthEmoji =
955
- stats.avgSuccessRate >= 0.8 ? '🟢' : stats.avgSuccessRate >= 0.5 ? '🟡' : '🔴'
956
1025
 
957
1026
  lines.push(`| 指标 | 值 |`)
958
1027
  lines.push(`|------|----|`)
@@ -1175,7 +1244,7 @@ const crsiCritiqueCmd: CommandHandler = (ctx, args) => {
1175
1244
  const engine = ctx.engine
1176
1245
  const sc = engine.getSelfCritique?.()
1177
1246
  if (!sc) {
1178
- return { content: 'SelfCritique 未初始化。请确认 Mipham Code 版本 >= v0.34.0。' }
1247
+ return { content: t('commands.crsi_critique.not_init') }
1179
1248
  }
1180
1249
 
1181
1250
  const sub = args[0]?.toLowerCase()
@@ -1185,42 +1254,44 @@ const crsiCritiqueCmd: CommandHandler = (ctx, args) => {
1185
1254
  sc.setEnabled(true)
1186
1255
  return {
1187
1256
  content: [
1188
- '## 🔍 Self-Critique: ON',
1257
+ t('commands.crsi_critique.on_title'),
1189
1258
  '',
1190
- `Model: **${config.model || 'auto (fastest available)'}**`,
1191
- `Threshold: **${(config.threshold * 100).toFixed(0)}%**`,
1192
- `Target tools: **${config.targetTools.join(', ')}**`,
1259
+ t('commands.crsi_critique.model', { model: config.model || 'auto (fastest available)' }),
1260
+ t('commands.crsi_critique.threshold', { pct: (config.threshold * 100).toFixed(0) }),
1261
+ t('commands.crsi_critique.target_tools', { tools: config.targetTools.join(', ') }),
1193
1262
  '',
1194
- 'The AI will now critique its own tool calls before execution.',
1195
- 'Safe + correct + necessary checks run before every targeted tool call.',
1263
+ t('commands.crsi_critique.on_desc1'),
1264
+ t('commands.crsi_critique.on_desc2'),
1196
1265
  '',
1197
- '💡 `/crsi critique off` to disable | `/crsi critique status` for current config',
1266
+ t('commands.crsi_critique.on_hint'),
1198
1267
  ].join('\n'),
1199
1268
  }
1200
1269
  }
1201
1270
 
1202
1271
  if (sub === 'off' || sub === 'disable') {
1203
1272
  sc.setEnabled(false)
1204
- return {
1205
- content: '## 🔍 Self-Critique: OFF\n\nTool calls will execute without pre-flight critique.',
1206
- }
1273
+ return { content: t('commands.crsi_critique.off_title') }
1207
1274
  }
1208
1275
 
1209
1276
  // status (default)
1210
1277
  const lines: string[] = [
1211
- '## 🔍 Self-Critique Status',
1278
+ t('commands.crsi_critique.status_title'),
1212
1279
  '',
1213
- `State: **${config.enabled ? '🟢 Enabled' : '⚫ Disabled'}**`,
1214
- `Model: **${config.model || 'auto (fastest available)'}**`,
1215
- `Threshold: **${(config.threshold * 100).toFixed(0)}%** (below this → correction or block)`,
1216
- `Target tools: **${config.targetTools.length > 0 ? config.targetTools.join(', ') : 'all'}**`,
1217
- `Timeout: **${config.timeoutMs}ms**`,
1280
+ t('commands.crsi_critique.state', {
1281
+ state: config.enabled ? '🟢 Enabled' : '⚫ Disabled',
1282
+ }),
1283
+ t('commands.crsi_critique.model', { model: config.model || 'auto (fastest available)' }),
1284
+ t('commands.crsi_critique.threshold_detail', { pct: (config.threshold * 100).toFixed(0) }),
1285
+ t('commands.crsi_critique.target_tools', {
1286
+ tools: config.targetTools.length > 0 ? config.targetTools.join(', ') : 'all',
1287
+ }),
1288
+ t('commands.crsi_critique.timeout', { ms: String(config.timeoutMs) }),
1218
1289
  '',
1219
1290
  '──',
1220
- '🔍 `/crsi critique on` — Enable self-critique',
1221
- '🔍 `/crsi critique off` — Disable self-critique',
1291
+ t('commands.crsi_critique.status_on_hint'),
1292
+ t('commands.crsi_critique.status_off_hint'),
1222
1293
  '',
1223
- '*Inspired by Anthropic RLAIF — AI critiques its own actions before execution.*',
1294
+ t('commands.crsi_critique.inspired'),
1224
1295
  ]
1225
1296
 
1226
1297
  return { content: lines.join('\n') }
@@ -1235,7 +1306,7 @@ const crsiInterpretCmd: CommandHandler = (ctx, args) => {
1235
1306
  const engine = ctx.engine
1236
1307
  const toolFilter = args[0]?.toLowerCase()
1237
1308
 
1238
- const lines: string[] = ['## 🧠 CRSI Tool-Call Interpretability', '']
1309
+ const lines: string[] = [t('commands.crsi_interpret.title'), '']
1239
1310
 
1240
1311
  // ── Error Signature Analysis ──
1241
1312
  const db = engine.getErrorSignatureDB?.()
@@ -1247,23 +1318,34 @@ const crsiInterpretCmd: CommandHandler = (ctx, args) => {
1247
1318
  if (sigs.length === 0) {
1248
1319
  lines.push(
1249
1320
  toolFilter
1250
- ? `No error signatures for tool \`${toolFilter}\`. CRSI immune memory is clean for this tool.`
1251
- : 'No active error signatures. CRSI immune memory is clean.',
1321
+ ? t('commands.crsi_interpret.no_sigs_for_tool', { tool: toolFilter })
1322
+ : t('commands.crsi_interpret.no_sigs'),
1252
1323
  )
1253
1324
  } else {
1254
- lines.push(`### 🛡️ Error Signatures${toolFilter ? ` — \`${toolFilter}\`` : ''}`, '')
1325
+ lines.push(
1326
+ t('commands.crsi_interpret.sigs_title', {
1327
+ tool: toolFilter ? ` — \`${toolFilter}\`` : '',
1328
+ }),
1329
+ '',
1330
+ )
1255
1331
  for (const sig of sigs.slice(0, 15)) {
1256
1332
  const bar =
1257
1333
  '█'.repeat(Math.round(sig.successRate * 10)) +
1258
1334
  '░'.repeat(10 - Math.round(sig.successRate * 10))
1259
1335
  lines.push(`- **${sig.toolName}**: \`${sig.pattern.slice(0, 60)}\``)
1260
1336
  lines.push(
1261
- ` Success: ${bar} ${Math.round(sig.successRate * 100)}% | ${sig.occurrences}x | ${sig.fixStrategy}`,
1337
+ t('commands.crsi_interpret.sig_success', {
1338
+ bar,
1339
+ pct: String(Math.round(sig.successRate * 100)),
1340
+ occurrences: String(sig.occurrences),
1341
+ strategy: sig.fixStrategy,
1342
+ }),
1262
1343
  )
1263
- lines.push(` Fix: \`${sig.fixAction.slice(0, 80)}\``)
1344
+ lines.push(t('commands.crsi_interpret.sig_fix', { fix: sig.fixAction.slice(0, 80) }))
1264
1345
  lines.push('')
1265
1346
  }
1266
- if (sigs.length > 15) lines.push(`... and ${sigs.length - 15} more signatures`, '')
1347
+ if (sigs.length > 15)
1348
+ lines.push(t('commands.crsi_interpret.sigs_more', { count: String(sigs.length - 15) }), '')
1267
1349
  }
1268
1350
  }
1269
1351
 
@@ -1272,8 +1354,8 @@ const crsiInterpretCmd: CommandHandler = (ctx, args) => {
1272
1354
  if (autoMemory) {
1273
1355
  const count = autoMemory.sessionReflectionCount ?? 0
1274
1356
  if (count > 0) {
1275
- lines.push('### 📊 CRSI Reflection Summary', '')
1276
- lines.push(`Turn reflections analyzed: **${count}**`)
1357
+ lines.push(t('commands.crsi_interpret.reflection_title'), '')
1358
+ lines.push(t('commands.crsi_interpret.reflections', { count: String(count) }))
1277
1359
  lines.push('')
1278
1360
  }
1279
1361
  }
@@ -1282,10 +1364,16 @@ const crsiInterpretCmd: CommandHandler = (ctx, args) => {
1282
1364
  const usage = engine.getUsageTracker?.()
1283
1365
  if (usage) {
1284
1366
  const summary = usage.getSummary()
1285
- lines.push('### 💰 Token Usage', '')
1286
- lines.push(`API input tokens: ${summary.apiInputTokens.toLocaleString()}`)
1287
- lines.push(`API output tokens: ${summary.apiOutputTokens.toLocaleString()}`)
1288
- lines.push(`Estimated tokens: ${summary.estimatedTokens.toLocaleString()}`)
1367
+ lines.push(t('commands.crsi_interpret.usage_title'), '')
1368
+ lines.push(
1369
+ t('commands.crsi_interpret.api_in', { count: summary.apiInputTokens.toLocaleString() }),
1370
+ )
1371
+ lines.push(
1372
+ t('commands.crsi_interpret.api_out', { count: summary.apiOutputTokens.toLocaleString() }),
1373
+ )
1374
+ lines.push(
1375
+ t('commands.crsi_interpret.est_tokens', { count: summary.estimatedTokens.toLocaleString() }),
1376
+ )
1289
1377
  lines.push('')
1290
1378
  }
1291
1379
 
@@ -1297,9 +1385,9 @@ const crsiInterpretCmd: CommandHandler = (ctx, args) => {
1297
1385
  if (analysis.systemHealth) {
1298
1386
  const h = analysis.systemHealth
1299
1387
  const bar = '█'.repeat(Math.round(h.score / 10)) + '░'.repeat(10 - Math.round(h.score / 10))
1300
- lines.push('### 🏥 System Health', '')
1301
- lines.push(`Overall: ${bar} **${h.score}/100**`)
1302
- lines.push(`Assessment: ${h.assessment}`)
1388
+ lines.push(t('commands.crsi_interpret.health_title'), '')
1389
+ lines.push(t('commands.crsi_interpret.overall', { bar, score: String(h.score) }))
1390
+ lines.push(t('commands.crsi_interpret.assessment', { assessment: h.assessment }))
1303
1391
  if (h.components) {
1304
1392
  lines.push('')
1305
1393
  for (const [comp, score] of Object.entries(h.components)) {
@@ -1323,20 +1411,25 @@ const crsiInterpretCmd: CommandHandler = (ctx, args) => {
1323
1411
  const blocks = c.principles.filter((p) => p.enforce === 'block').length
1324
1412
  const warns = c.principles.filter((p) => p.enforce === 'warn').length
1325
1413
  const autos = c.principles.filter((p) => p.enforce === 'auto').length
1326
- lines.push('### ⚖️ Constitution', '')
1414
+ lines.push(t('commands.crsi_interpret.constitution_title'), '')
1327
1415
  lines.push(
1328
- `Principles: **${c.principles.length}** (🚫 ${blocks} block | ⚠️ ${warns} warn | 🔄 ${autos} auto)`,
1416
+ t('commands.crsi_interpret.principles', {
1417
+ total: String(c.principles.length),
1418
+ block: String(blocks),
1419
+ warn: String(warns),
1420
+ auto: String(autos),
1421
+ }),
1329
1422
  )
1330
- lines.push(`Version: v${c.version}`)
1423
+ lines.push(t('commands.crsi_interpret.version', { version: c.version }))
1331
1424
  lines.push('')
1332
1425
  }
1333
1426
 
1334
1427
  if (lines.length <= 2) {
1335
- lines.push('_Start using tools to populate CRSI interpretability data._')
1428
+ lines.push(t('commands.crsi_interpret.empty'))
1336
1429
  }
1337
1430
 
1338
1431
  lines.push('──')
1339
- lines.push('🔍 Filter by tool: `/crsi interpret <tool-name>` (e.g. `/crsi interpret Bash`)')
1432
+ lines.push(t('commands.crsi_interpret.filter_hint'))
1340
1433
 
1341
1434
  return { content: lines.join('\n') }
1342
1435
  }
@@ -1353,7 +1446,7 @@ const crsiRedTeamCmd: CommandHandler = async (ctx) => {
1353
1446
  const preflight = engine.getPreFlightChecker?.()
1354
1447
 
1355
1448
  if (!constitution || !preflight) {
1356
- return { content: 'Red-Team 需要 ConstitutionLoader 和 PreFlightChecker 均已初始化。' }
1449
+ return { content: t('commands.crsi_red_team.not_init') }
1357
1450
  }
1358
1451
 
1359
1452
  const { RedTeam: RT } = await import('../../src/core/red-team.js')
@@ -1361,21 +1454,21 @@ const crsiRedTeamCmd: CommandHandler = async (ctx) => {
1361
1454
  const report = redTeam.run(constitution, preflight)
1362
1455
 
1363
1456
  const lines: string[] = [
1364
- '## 🔴 CRSI Red-Team Report',
1457
+ t('commands.crsi_red_team.title'),
1365
1458
  '',
1366
- `Overall Score: **${report.score}/100**`,
1459
+ t('commands.crsi_red_team.score', { score: String(report.score) }),
1367
1460
  '',
1368
- `| Metric | Count |`,
1369
- `|--------|------|`,
1370
- `| Total attacks | ${report.total} |`,
1371
- `| 🛡️ Correctly blocked | ${report.blocked} |`,
1372
- `| 🔴 Passed through (GAP) | ${report.passedThrough} |`,
1373
- `| ⚠️ False positives | ${report.falsePositives} |`,
1461
+ t('commands.crsi_red_team.metric'),
1462
+ t('commands.crsi_red_team.metric_sep'),
1463
+ t('commands.crsi_red_team.total_attacks', { count: String(report.total) }),
1464
+ t('commands.crsi_red_team.blocked', { count: String(report.blocked) }),
1465
+ t('commands.crsi_red_team.passed_through', { count: String(report.passedThrough) }),
1466
+ t('commands.crsi_red_team.false_positives', { count: String(report.falsePositives) }),
1374
1467
  '',
1375
1468
  ]
1376
1469
 
1377
1470
  // Per-principle breakdown
1378
- lines.push('### By Principle', '')
1471
+ lines.push(t('commands.crsi_red_team.by_principle'), '')
1379
1472
  for (const [pid, stats] of Object.entries(report.byPrinciple)) {
1380
1473
  const pct = stats.total > 0 ? Math.round((stats.blocked / stats.total) * 100) : 100
1381
1474
  const bar = '█'.repeat(Math.round(pct / 10)) + '░'.repeat(10 - Math.round(pct / 10))
@@ -1387,11 +1480,14 @@ const crsiRedTeamCmd: CommandHandler = async (ctx) => {
1387
1480
  // Detail: passed-through attacks
1388
1481
  const gaps = report.results.filter((r) => r.attack.shouldBlock && !r.blocked)
1389
1482
  if (gaps.length > 0) {
1390
- lines.push('### 🔴 Security Gaps (Should Have Been Blocked)', '')
1483
+ lines.push(t('commands.crsi_red_team.gaps_title'), '')
1391
1484
  for (const g of gaps) {
1392
1485
  lines.push(`- **${g.attack.principleId}**: ${g.attack.description}`)
1393
1486
  lines.push(
1394
- ` Tool: \`${g.attack.toolName}\` → Params: \`${JSON.stringify(g.attack.params).slice(0, 80)}\``,
1487
+ t('commands.crsi_red_team.gap_tool', {
1488
+ tool: g.attack.toolName,
1489
+ params: JSON.stringify(g.attack.params).slice(0, 80),
1490
+ }),
1395
1491
  )
1396
1492
  }
1397
1493
  lines.push('')
@@ -1400,21 +1496,25 @@ const crsiRedTeamCmd: CommandHandler = async (ctx) => {
1400
1496
  // Detail: caught attacks
1401
1497
  const caught = report.results.filter((r) => r.blocked)
1402
1498
  if (caught.length > 0) {
1403
- lines.push('### 🛡️ Successfully Blocked', '')
1499
+ lines.push(t('commands.crsi_red_team.blocked_title'), '')
1404
1500
  for (const c of caught) {
1405
- lines.push(`- ${c.attack.principleId}: ${c.attack.description} → caught by **${c.caughtBy}**`)
1501
+ lines.push(
1502
+ t('commands.crsi_red_team.caught_by', {
1503
+ principle: c.attack.principleId,
1504
+ desc: c.attack.description,
1505
+ caught: c.caughtBy ?? 'unknown',
1506
+ }),
1507
+ )
1406
1508
  }
1407
1509
  lines.push('')
1408
1510
  }
1409
1511
 
1410
1512
  if (report.score === 100) {
1411
- lines.push('🎉 **All attacks blocked!** The SIS immune system is fully operational.')
1513
+ lines.push(t('commands.crsi_red_team.all_blocked'))
1412
1514
  } else if (report.score >= 80) {
1413
- lines.push(
1414
- '⚠️ Good coverage. Review the gaps above and add audit patterns to `ai-guardrails.yml`.',
1415
- )
1515
+ lines.push(t('commands.crsi_red_team.good_coverage'))
1416
1516
  } else {
1417
- lines.push('🔴 **Critical gaps detected.** Prioritize fixing the passed-through attacks above.')
1517
+ lines.push(t('commands.crsi_red_team.critical'))
1418
1518
  }
1419
1519
 
1420
1520
  return { content: lines.join('\n') }
@@ -1431,45 +1531,61 @@ const dreamCmd: CommandHandler = async (ctx, args) => {
1431
1531
 
1432
1532
  const engine = ctx.engine.getDreamEngine?.()
1433
1533
  if (!engine) {
1434
- return { content: 'DreamEngine 未初始化。请确认 Mipham Code 版本 >= v0.34.0。' }
1534
+ return { content: t('commands.dream.not_init') }
1435
1535
  }
1436
1536
 
1437
1537
  if (status) {
1438
1538
  const history = engine.getDreamHistory()
1439
1539
  if (history.length === 0) {
1440
- return { content: '🌙 尚未运行过 Auto-Dream。使用 `/dream` 手动触发首次梦境整合。' }
1540
+ return { content: t('commands.dream.no_history') }
1441
1541
  }
1442
- const lines: string[] = ['## 🌙 Auto-Dream 历史', '']
1542
+ const lines: string[] = [t('commands.dream.history_title'), '']
1443
1543
  for (const entry of history.slice(0, 5)) {
1444
1544
  const ts = new Date(entry.timestamp).toLocaleString('zh-CN')
1445
1545
  const total = entry.actions.length
1446
1546
  const applied = entry.actions.filter((a) => a.autoApplied).length
1447
1547
  const flagged = entry.actions.filter((a) => !a.autoApplied).length
1448
- lines.push(`- **${ts}**: ${total} 行动 (${applied} 自动应用, ${flagged} 标记审查)`)
1548
+ lines.push(
1549
+ t('commands.dream.history_entry', {
1550
+ time: ts,
1551
+ total: String(total),
1552
+ applied: String(applied),
1553
+ flagged: String(flagged),
1554
+ }),
1555
+ )
1449
1556
  }
1450
1557
  return { content: lines.join('\n') }
1451
1558
  }
1452
1559
 
1453
1560
  const report = engine.dream({ aggressive })
1454
1561
  const lines: string[] = [
1455
- '## 🌙 Auto-Dream 完成',
1562
+ t('commands.dream.done_title'),
1456
1563
  '',
1457
- `记忆整合: ${report.beforeCount} → ${report.afterCount}`,
1458
- ...(report.phases.deduplicated > 0 ? [`- 去重: ${report.phases.deduplicated} 条`] : []),
1564
+ t('commands.dream.integrate', {
1565
+ before: String(report.beforeCount),
1566
+ after: String(report.afterCount),
1567
+ }),
1568
+ ...(report.phases.deduplicated > 0
1569
+ ? [t('commands.dream.dedup', { count: String(report.phases.deduplicated) })]
1570
+ : []),
1459
1571
  ...(report.phases.contradictionsFound > 0
1460
- ? [`- 矛盾冲突: ${report.phases.contradictionsFound} 处 (需人工裁决)`]
1572
+ ? [t('commands.dream.contradiction', { count: String(report.phases.contradictionsFound) })]
1573
+ : []),
1574
+ ...(report.phases.merged > 0
1575
+ ? [t('commands.dream.merge', { count: String(report.phases.merged) })]
1461
1576
  : []),
1462
- ...(report.phases.merged > 0 ? [`- 合并: ${report.phases.merged} 组`] : []),
1463
1577
  ...(report.phases.solidified > 0
1464
- ? [`- 模糊标记: ${report.phases.solidified} 条 (建议固化)`]
1578
+ ? [t('commands.dream.solidify', { count: String(report.phases.solidified) })]
1579
+ : []),
1580
+ ...(report.phases.pruned > 0
1581
+ ? [t('commands.dream.prune', { count: String(report.phases.pruned) })]
1465
1582
  : []),
1466
- ...(report.phases.pruned > 0 ? [`- 清理过期: ${report.phases.pruned} 条`] : []),
1467
1583
  '',
1468
1584
  ]
1469
1585
 
1470
1586
  const flagged = report.actions.filter((a) => !a.autoApplied)
1471
1587
  if (flagged.length > 0 && !aggressive) {
1472
- lines.push(`⚠️ ${flagged.length} 项待审查。使用 \`/dream --aggressive\` 自动应用所有操作。`)
1588
+ lines.push(t('commands.dream.review_hint', { count: String(flagged.length) }))
1473
1589
  lines.push('')
1474
1590
  for (const action of flagged) {
1475
1591
  lines.push(`- [ ] **${action.type}**: ${action.description}`)
@@ -1477,7 +1593,7 @@ const dreamCmd: CommandHandler = async (ctx, args) => {
1477
1593
  }
1478
1594
 
1479
1595
  if (report.actions.length === 0) {
1480
- lines.push('✅ 记忆干净,无需整理。')
1596
+ lines.push(t('commands.dream.clean'))
1481
1597
  }
1482
1598
 
1483
1599
  return { content: lines.join('\n') }
@@ -1492,7 +1608,7 @@ const constitutionCmd: CommandHandler = (ctx, args) => {
1492
1608
  const engine = ctx.engine
1493
1609
  const constitution = engine.getConstitutionLoader?.()
1494
1610
  if (!constitution) {
1495
- return { content: 'ConstitutionLoader 未初始化。请确认 Mipham Code 版本 >= v0.34.0。' }
1611
+ return { content: t('commands.constitution.not_init') }
1496
1612
  }
1497
1613
 
1498
1614
  const subCmd = args[0]?.toLowerCase()
@@ -1502,11 +1618,11 @@ const constitutionCmd: CommandHandler = (ctx, args) => {
1502
1618
  const c = constitution.reload()
1503
1619
  return {
1504
1620
  content: [
1505
- '## ⚖️ Constitution Reloaded',
1621
+ t('commands.constitution.reload_title'),
1506
1622
  '',
1507
- `Version: **v${c.version}**`,
1508
- `Principles: **${c.principles.length}**`,
1509
- `Path: \`${constitution.getPath()}\``,
1623
+ t('commands.constitution.version', { version: c.version }),
1624
+ t('commands.constitution.principles', { count: String(c.principles.length) }),
1625
+ t('commands.constitution.path', { path: constitution.getPath() }),
1510
1626
  '',
1511
1627
  c.principles.map((p) => `- **${p.id}** [${p.enforce}]: ${p.text}`).join('\n'),
1512
1628
  ].join('\n'),
@@ -1516,9 +1632,13 @@ const constitutionCmd: CommandHandler = (ctx, args) => {
1516
1632
  // /constitution view (default)
1517
1633
  const c = constitution.load()
1518
1634
  const lines: string[] = [
1519
- '## ⚖️ Mipham Constitution',
1635
+ t('commands.constitution.view_title'),
1520
1636
  '',
1521
- `Version: **v${c.version}** | Principles: **${c.principles.length}** | Path: \`${constitution.getPath()}\``,
1637
+ t('commands.constitution.view_summary', {
1638
+ version: c.version,
1639
+ count: String(c.principles.length),
1640
+ path: constitution.getPath(),
1641
+ }),
1522
1642
  '',
1523
1643
  '---',
1524
1644
  '',
@@ -1530,20 +1650,18 @@ const constitutionCmd: CommandHandler = (ctx, args) => {
1530
1650
  lines.push('')
1531
1651
  lines.push(p.text)
1532
1652
  if (p.rationale) lines.push(` *${p.rationale}*`)
1533
- if (p.scope) lines.push(` Scope: \`${p.scope}\``)
1534
- if (p.tools) lines.push(` Tools: ${p.tools.join(', ')}`)
1653
+ if (p.scope) lines.push(t('commands.constitution.scope', { scope: p.scope }))
1654
+ if (p.tools) lines.push(t('commands.constitution.tools', { tools: p.tools.join(', ') }))
1535
1655
  lines.push('')
1536
1656
  }
1537
1657
 
1538
1658
  lines.push('---')
1539
1659
  lines.push('')
1540
- lines.push('🔧 `/constitution reload` — 重新加载(修改 ai-guardrails.yml 后使用)')
1541
- lines.push('📝 编辑: `vi ~/.mipham/ai-guardrails.yml`')
1542
- lines.push('🗑️ 重置: 删除 `~/.mipham/ai-guardrails.yml` 后执行 `/constitution reload`')
1660
+ lines.push(t('commands.constitution.reload_hint'))
1661
+ lines.push(t('commands.constitution.edit_hint'))
1662
+ lines.push(t('commands.constitution.reset_hint'))
1543
1663
  lines.push('')
1544
- lines.push(
1545
- '*Inspired by Anthropic Constitutional AI. Mipham enforces these principles at runtime — auditable on every action.*',
1546
- )
1664
+ lines.push(t('commands.constitution.inspired'))
1547
1665
 
1548
1666
  return { content: lines.join('\n') }
1549
1667
  }
@@ -1556,25 +1674,32 @@ const bugReportCmd: CommandHandler = async (ctx) => {
1556
1674
  const t = resolveT(ctx)
1557
1675
  const engine = ctx.engine
1558
1676
  const lines: string[] = [
1559
- '## 🐛 Bug Report',
1677
+ t('commands.bug_report.title'),
1560
1678
  '',
1561
- '> Copy the report below and paste it into your GitHub Issue.',
1679
+ t('commands.bug_report.hint'),
1562
1680
  '',
1563
1681
  '---',
1564
1682
  '',
1565
1683
  ]
1566
1684
 
1567
1685
  // ── Version & Runtime ──
1568
- lines.push('### Environment', '')
1569
- lines.push(`- **Mipham Code**: v${ctx.version}`)
1570
- lines.push(`- **Session**: ${ctx.sessionId}`)
1571
- lines.push(`- **Node**: ${process.version}`)
1572
- lines.push(`- **Platform**: ${process.platform} ${process.arch}`)
1686
+ lines.push(t('commands.bug_report.env_title'), '')
1687
+ lines.push(t('commands.bug_report.mipham', { version: ctx.version }))
1688
+ lines.push(t('commands.bug_report.session', { id: ctx.sessionId }))
1689
+ lines.push(t('commands.bug_report.node', { version: process.version }))
1690
+ lines.push(t('commands.bug_report.platform', { platform: process.platform, arch: process.arch }))
1573
1691
  lines.push(
1574
- `- **OS**: ${process.platform === 'darwin' ? 'macOS' : process.platform === 'linux' ? 'Linux' : 'Windows'}`,
1692
+ t('commands.bug_report.os', {
1693
+ os:
1694
+ process.platform === 'darwin'
1695
+ ? 'macOS'
1696
+ : process.platform === 'linux'
1697
+ ? 'Linux'
1698
+ : 'Windows',
1699
+ }),
1575
1700
  )
1576
- lines.push(`- **Provider**: ${ctx.providerId}`)
1577
- lines.push(`- **Model**: ${ctx.modelId}`)
1701
+ lines.push(t('commands.bug_report.provider', { provider: ctx.providerId }))
1702
+ lines.push(t('commands.bug_report.model', { model: ctx.modelId }))
1578
1703
  lines.push('')
1579
1704
 
1580
1705
  // ── SIS errors ──
@@ -1583,10 +1708,16 @@ const bugReportCmd: CommandHandler = async (ctx) => {
1583
1708
  if (db) {
1584
1709
  const active = db.getActive()
1585
1710
  if (active.length > 0) {
1586
- lines.push('### Active Error Signatures', '')
1711
+ lines.push(t('commands.bug_report.sigs_title'), '')
1587
1712
  for (const sig of active.slice(0, 10)) {
1588
1713
  lines.push(
1589
- `- \`${sig.id}\`: ${sig.pattern} (${sig.category}, ${sig.occurrences}x, ${(sig.successRate * 100).toFixed(0)}% success)`,
1714
+ t('commands.bug_report.sig_entry', {
1715
+ id: sig.id,
1716
+ pattern: sig.pattern,
1717
+ category: sig.category,
1718
+ occurrences: String(sig.occurrences),
1719
+ pct: (sig.successRate * 100).toFixed(0),
1720
+ }),
1590
1721
  )
1591
1722
  }
1592
1723
  lines.push('')
@@ -1603,10 +1734,14 @@ const bugReportCmd: CommandHandler = async (ctx) => {
1603
1734
  const health = hookEngine.getHookHealth()
1604
1735
  const disabled = health.filter((h) => h.health.disabled)
1605
1736
  if (disabled.length > 0) {
1606
- lines.push('### Disabled Hooks', '')
1737
+ lines.push(t('commands.bug_report.disabled_hooks'), '')
1607
1738
  for (const entry of disabled) {
1608
1739
  lines.push(
1609
- `- \`${entry.key}\`: ${entry.health.failures} failures, disabled at ${new Date(entry.health.disabledAt).toISOString()}`,
1740
+ t('commands.bug_report.hook_entry', {
1741
+ key: entry.key,
1742
+ failures: String(entry.health.failures),
1743
+ time: new Date(entry.health.disabledAt).toISOString(),
1744
+ }),
1610
1745
  )
1611
1746
  }
1612
1747
  lines.push('')
@@ -1623,10 +1758,15 @@ const bugReportCmd: CommandHandler = async (ctx) => {
1623
1758
  const history = dreamEngine.getDreamHistory()
1624
1759
  if (history.length > 0) {
1625
1760
  const last = history[0]!
1626
- lines.push('### Last Auto-Dream', '')
1627
- lines.push(`- **Ran at**: ${last.timestamp}`)
1761
+ lines.push(t('commands.bug_report.last_dream'), '')
1762
+ lines.push(t('commands.bug_report.ran_at', { time: last.timestamp }))
1628
1763
  lines.push(
1629
- `- **Actions**: ${last.actions.length} (${last.actions.filter((a: { autoApplied: boolean }) => a.autoApplied).length} applied)`,
1764
+ t('commands.bug_report.actions', {
1765
+ total: String(last.actions.length),
1766
+ applied: String(
1767
+ last.actions.filter((a: { autoApplied: boolean }) => a.autoApplied).length,
1768
+ ),
1769
+ }),
1630
1770
  )
1631
1771
  lines.push('')
1632
1772
  }
@@ -1636,17 +1776,17 @@ const bugReportCmd: CommandHandler = async (ctx) => {
1636
1776
  }
1637
1777
 
1638
1778
  // ── Steps to reproduce (template) ──
1639
- lines.push('### Steps to Reproduce', '')
1779
+ lines.push(t('commands.bug_report.steps_title'), '')
1640
1780
  lines.push('1. ')
1641
1781
  lines.push('2. ')
1642
1782
  lines.push('3. ')
1643
1783
  lines.push('')
1644
- lines.push('### Expected Behavior', '')
1784
+ lines.push(t('commands.bug_report.expected'), '')
1645
1785
  lines.push('')
1646
- lines.push('### Actual Behavior', '')
1786
+ lines.push(t('commands.bug_report.actual'), '')
1647
1787
  lines.push('')
1648
1788
  lines.push('---')
1649
- lines.push(`🤖 Generated with Mipham Code v${ctx.version}`)
1789
+ lines.push(t('commands.bug_report.generated', { version: ctx.version }))
1650
1790
 
1651
1791
  return { content: lines.join('\n'), copyContent: lines.join('\n') }
1652
1792
  }
@@ -1659,7 +1799,7 @@ const bugReportCmd: CommandHandler = async (ctx) => {
1659
1799
 
1660
1800
  const changelogCmd: CommandHandler = async (ctx) => {
1661
1801
  const t = resolveT(ctx)
1662
- const lines: string[] = ['## 📋 Changelog', '']
1802
+ const lines: string[] = [t('commands.changelog.title'), '']
1663
1803
 
1664
1804
  try {
1665
1805
  const { execSync } = await import('node:child_process')
@@ -1670,11 +1810,11 @@ const changelogCmd: CommandHandler = async (ctx) => {
1670
1810
  .slice(0, 15)
1671
1811
 
1672
1812
  if (tags.length === 0) {
1673
- lines.push('No version tags found.')
1813
+ lines.push(t('commands.changelog.no_tags'))
1674
1814
  return { content: lines.join('\n') }
1675
1815
  }
1676
1816
 
1677
- lines.push(`Current: **v${ctx.version}**`, '')
1817
+ lines.push(t('commands.changelog.current', { version: ctx.version }), '')
1678
1818
 
1679
1819
  for (let i = 0; i < tags.length; i++) {
1680
1820
  const tag = tags[i]!
@@ -1690,25 +1830,25 @@ const changelogCmd: CommandHandler = async (ctx) => {
1690
1830
  .filter(Boolean)
1691
1831
 
1692
1832
  const isCurrent = tag === `v${ctx.version}`
1693
- lines.push(`### ${tag}${isCurrent ? ' ← current' : ''}`, '')
1833
+ lines.push(`### ${tag}${isCurrent ? t('commands.changelog.current_marker') : ''}`, '')
1694
1834
  if (log.length > 0) {
1695
1835
  for (const entry of log) {
1696
1836
  lines.push(`- ${entry}`)
1697
1837
  }
1698
1838
  } else {
1699
- lines.push(' _(release tag only)_')
1839
+ lines.push(t('commands.changelog.release_only'))
1700
1840
  }
1701
1841
  lines.push('')
1702
1842
  } catch {
1703
- lines.push(`### ${tag}`, '', ' _(details unavailable)_', '')
1843
+ lines.push(`### ${tag}`, '', t('commands.changelog.details_unavailable'), '')
1704
1844
  }
1705
1845
  }
1706
1846
  } catch {
1707
- lines.push('Unable to read git history. Make sure you are in the Mipham Code repository.')
1847
+ lines.push(t('commands.changelog.no_git'))
1708
1848
  }
1709
1849
 
1710
1850
  lines.push('---')
1711
- lines.push(`Full history: \`git log --oneline\``)
1851
+ lines.push(t('commands.changelog.full_history'))
1712
1852
 
1713
1853
  return { content: lines.join('\n') }
1714
1854
  }
@@ -4285,10 +4425,10 @@ const forkCmd: CommandHandler = async (ctx, args) => {
4285
4425
  ex(`git -C ${wtPath} add -A`, { stdio: 'ignore', timeout: 10_000 })
4286
4426
  const st = ex(`git -C ${wtPath} status --porcelain`, { encoding: 'utf-8', timeout: 10_000 })
4287
4427
  if (st.trim()) {
4288
- ex(
4289
- `git -C ${wtPath} commit -m "feat: ${prompt.slice(0, 60)}\n\nCo-Authored-By: Claude <noreply@anthropic.com>"`,
4290
- { stdio: 'ignore', timeout: 10_000 },
4291
- )
4428
+ ex(`git -C ${wtPath} commit -m "feat: ${prompt.slice(0, 60)}\n\n${COAUTHOR_TRAILER}"`, {
4429
+ stdio: 'ignore',
4430
+ timeout: 10_000,
4431
+ })
4292
4432
  ex(`git push origin ${branch}`, { stdio: 'ignore', timeout: 30_000 })
4293
4433
  }
4294
4434
  } catch {
@@ -4485,6 +4625,7 @@ const commandsListCmd: CommandHandler = () => {
4485
4625
  '/crsi inventory': 'Tools & Skills',
4486
4626
  '/crsi modify': 'Tools & Skills',
4487
4627
  '/crsi propose': 'Tools & Skills',
4628
+ '/crsi prose-clear': 'Tools & Skills',
4488
4629
  '/crsi eval': 'Tools & Skills',
4489
4630
  '/crsi meta': 'Tools & Skills',
4490
4631
  '/crsi interpret': 'Tools & Skills',
@@ -4641,6 +4782,7 @@ registry.set('/crsi health', crsiHealthCmd)
4641
4782
  registry.set('/crsi inventory', crsiInventoryCmd)
4642
4783
  registry.set('/crsi modify', crsiModifyCmd)
4643
4784
  registry.set('/crsi propose', crsiProposeCmd)
4785
+ registry.set('/crsi prose-clear', crsiProseClearCmd)
4644
4786
  registry.set('/crsi eval', crsiEvalCmd)
4645
4787
  registry.set('/crsi meta', crsiMetaCmd)
4646
4788
  registry.set('/crsi interpret', crsiInterpretCmd)
@@ -4819,7 +4961,9 @@ const COMMAND_DESCRIPTIONS: Record<string, string> = {
4819
4961
  '/crsi health': 'CRSI + SIS unified health dashboard with scoring',
4820
4962
  '/crsi inventory': 'Live capability self-report — CRSI/SIS/constitution state',
4821
4963
  '/crsi modify': 'Run a code self-modification through the sandbox (worktree → tests → approve)',
4822
- '/crsi propose': '固化 CRSI 失败信号(默认教训 / --rule 受管理规则),沙箱 + 人批准门控',
4964
+ '/crsi propose':
4965
+ '固化 CRSI 失败信号(默认教训 / --rule 受管理规则 / --prose 改 skill 散文),沙箱 + 人批准门控',
4966
+ '/crsi prose-clear': '清空散文提议去重 ledger(~/.mipham/crsi/prose-proposals.jsonl)',
4823
4967
  '/crsi eval': 'Run the ground-truth CRSI eval harness and record the score',
4824
4968
  '/crsi meta': 'RSI Level 3 meta-rule analysis — rules that improve the rules',
4825
4969
  '/crsi interpret': 'Tool-call behavior dashboard — error patterns, usage, health',