@miphamai/cli 0.50.0 → 0.52.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 (57) hide show
  1. package/bin/mipham.ts +1 -9
  2. package/package.json +1 -2
  3. package/skills/standard/web-access/references/cdp-api.md +2 -1
  4. package/skills/standard/web-access/scripts/cdp-proxy.mjs +75 -1
  5. package/skills/standard/web-access/scripts/check-deps.mjs +17 -2
  6. package/skills/standard/web-access/scripts/find-url.mjs +0 -0
  7. package/skills/standard/web-access/scripts/match-site.mjs +0 -0
  8. package/skills/standard/web-access.SKILL.md +10 -3
  9. package/src/commands/keys.ts +26 -0
  10. package/src/commands/project.ts +1 -1
  11. package/src/config/credential-crypto.ts +114 -0
  12. package/src/config/defaults.ts +6 -1
  13. package/src/config/loader.ts +65 -14
  14. package/src/core/auto-memory.ts +3 -3
  15. package/src/core/constitution-loader.ts +0 -2
  16. package/src/core/context.ts +0 -1
  17. package/src/core/credential-masker/index.ts +1 -0
  18. package/src/core/credential-masker/matcher.ts +18 -7
  19. package/src/core/credential-masker/search.ts +73 -0
  20. package/src/core/credential-masker.ts +1 -0
  21. package/src/core/crsi-sandbox.ts +0 -1
  22. package/src/core/dream-engine.ts +0 -2
  23. package/src/core/instructions.ts +36 -12
  24. package/src/core/memory/memory-loader.ts +3 -1
  25. package/src/core/permission-rules.ts +23 -12
  26. package/src/core/rules-loader.ts +8 -10
  27. package/src/daemon/database.ts +0 -1
  28. package/src/daemon/feishu/adapter.ts +2 -1
  29. package/src/daemon/index.ts +18 -8
  30. package/src/daemon/server.ts +53 -10
  31. package/src/daemon/session-manager.ts +5 -4
  32. package/src/daemon/telegram/adapter.ts +70 -0
  33. package/src/daemon/telegram/api.ts +45 -0
  34. package/src/daemon/telegram/env.ts +14 -0
  35. package/src/daemon/telegram/poller.ts +64 -0
  36. package/src/daemon/telegram/types.ts +10 -0
  37. package/src/i18n-core/locales/en-US.json +123 -0
  38. package/src/i18n-core/locales/zh-CN.json +123 -0
  39. package/src/index.tsx +10 -8
  40. package/src/mcp/token-store.ts +2 -37
  41. package/src/shared/package-info.ts +1 -1
  42. package/src/shared/types.ts +5 -3
  43. package/src/skills/bin-check.ts +40 -0
  44. package/src/skills/bundled-skill-assets.ts +5 -5
  45. package/src/skills/bundled-skills.ts +1 -1
  46. package/src/skills/loader.ts +35 -10
  47. package/src/skills/sanitizer.ts +1 -2
  48. package/src/skills/seam.ts +1 -1
  49. package/src/tools/agent/exit-plan.ts +1 -3
  50. package/src/tools/agent/memory.ts +2 -1
  51. package/src/tools/agent/skill.ts +14 -0
  52. package/src/tools/file/glob.ts +38 -23
  53. package/src/tools/file/grep.ts +86 -64
  54. package/src/tools/index.ts +5 -5
  55. package/src/ui/commands.ts +191 -119
  56. package/src/ui/config-wizard.tsx +5 -2
  57. package/src/core/tokenizer.ts +0 -80
@@ -1022,8 +1022,6 @@ const crsiHealthCmd: CommandHandler = async (ctx) => {
1022
1022
  lines.push('', '### 🛡️ SIS 自免疫子系统', '')
1023
1023
  if (db) {
1024
1024
  const stats = db.getStats()
1025
- const healthEmoji =
1026
- stats.avgSuccessRate >= 0.8 ? '🟢' : stats.avgSuccessRate >= 0.5 ? '🟡' : '🔴'
1027
1025
 
1028
1026
  lines.push(`| 指标 | 值 |`)
1029
1027
  lines.push(`|------|----|`)
@@ -1246,7 +1244,7 @@ const crsiCritiqueCmd: CommandHandler = (ctx, args) => {
1246
1244
  const engine = ctx.engine
1247
1245
  const sc = engine.getSelfCritique?.()
1248
1246
  if (!sc) {
1249
- return { content: 'SelfCritique 未初始化。请确认 Mipham Code 版本 >= v0.34.0。' }
1247
+ return { content: t('commands.crsi_critique.not_init') }
1250
1248
  }
1251
1249
 
1252
1250
  const sub = args[0]?.toLowerCase()
@@ -1256,42 +1254,44 @@ const crsiCritiqueCmd: CommandHandler = (ctx, args) => {
1256
1254
  sc.setEnabled(true)
1257
1255
  return {
1258
1256
  content: [
1259
- '## 🔍 Self-Critique: ON',
1257
+ t('commands.crsi_critique.on_title'),
1260
1258
  '',
1261
- `Model: **${config.model || 'auto (fastest available)'}**`,
1262
- `Threshold: **${(config.threshold * 100).toFixed(0)}%**`,
1263
- `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(', ') }),
1264
1262
  '',
1265
- 'The AI will now critique its own tool calls before execution.',
1266
- 'Safe + correct + necessary checks run before every targeted tool call.',
1263
+ t('commands.crsi_critique.on_desc1'),
1264
+ t('commands.crsi_critique.on_desc2'),
1267
1265
  '',
1268
- '💡 `/crsi critique off` to disable | `/crsi critique status` for current config',
1266
+ t('commands.crsi_critique.on_hint'),
1269
1267
  ].join('\n'),
1270
1268
  }
1271
1269
  }
1272
1270
 
1273
1271
  if (sub === 'off' || sub === 'disable') {
1274
1272
  sc.setEnabled(false)
1275
- return {
1276
- content: '## 🔍 Self-Critique: OFF\n\nTool calls will execute without pre-flight critique.',
1277
- }
1273
+ return { content: t('commands.crsi_critique.off_title') }
1278
1274
  }
1279
1275
 
1280
1276
  // status (default)
1281
1277
  const lines: string[] = [
1282
- '## 🔍 Self-Critique Status',
1278
+ t('commands.crsi_critique.status_title'),
1283
1279
  '',
1284
- `State: **${config.enabled ? '🟢 Enabled' : '⚫ Disabled'}**`,
1285
- `Model: **${config.model || 'auto (fastest available)'}**`,
1286
- `Threshold: **${(config.threshold * 100).toFixed(0)}%** (below this → correction or block)`,
1287
- `Target tools: **${config.targetTools.length > 0 ? config.targetTools.join(', ') : 'all'}**`,
1288
- `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) }),
1289
1289
  '',
1290
1290
  '──',
1291
- '🔍 `/crsi critique on` — Enable self-critique',
1292
- '🔍 `/crsi critique off` — Disable self-critique',
1291
+ t('commands.crsi_critique.status_on_hint'),
1292
+ t('commands.crsi_critique.status_off_hint'),
1293
1293
  '',
1294
- '*Inspired by Anthropic RLAIF — AI critiques its own actions before execution.*',
1294
+ t('commands.crsi_critique.inspired'),
1295
1295
  ]
1296
1296
 
1297
1297
  return { content: lines.join('\n') }
@@ -1306,7 +1306,7 @@ const crsiInterpretCmd: CommandHandler = (ctx, args) => {
1306
1306
  const engine = ctx.engine
1307
1307
  const toolFilter = args[0]?.toLowerCase()
1308
1308
 
1309
- const lines: string[] = ['## 🧠 CRSI Tool-Call Interpretability', '']
1309
+ const lines: string[] = [t('commands.crsi_interpret.title'), '']
1310
1310
 
1311
1311
  // ── Error Signature Analysis ──
1312
1312
  const db = engine.getErrorSignatureDB?.()
@@ -1318,23 +1318,34 @@ const crsiInterpretCmd: CommandHandler = (ctx, args) => {
1318
1318
  if (sigs.length === 0) {
1319
1319
  lines.push(
1320
1320
  toolFilter
1321
- ? `No error signatures for tool \`${toolFilter}\`. CRSI immune memory is clean for this tool.`
1322
- : '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'),
1323
1323
  )
1324
1324
  } else {
1325
- lines.push(`### 🛡️ Error Signatures${toolFilter ? ` — \`${toolFilter}\`` : ''}`, '')
1325
+ lines.push(
1326
+ t('commands.crsi_interpret.sigs_title', {
1327
+ tool: toolFilter ? ` — \`${toolFilter}\`` : '',
1328
+ }),
1329
+ '',
1330
+ )
1326
1331
  for (const sig of sigs.slice(0, 15)) {
1327
1332
  const bar =
1328
1333
  '█'.repeat(Math.round(sig.successRate * 10)) +
1329
1334
  '░'.repeat(10 - Math.round(sig.successRate * 10))
1330
1335
  lines.push(`- **${sig.toolName}**: \`${sig.pattern.slice(0, 60)}\``)
1331
1336
  lines.push(
1332
- ` 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
+ }),
1333
1343
  )
1334
- lines.push(` Fix: \`${sig.fixAction.slice(0, 80)}\``)
1344
+ lines.push(t('commands.crsi_interpret.sig_fix', { fix: sig.fixAction.slice(0, 80) }))
1335
1345
  lines.push('')
1336
1346
  }
1337
- 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) }), '')
1338
1349
  }
1339
1350
  }
1340
1351
 
@@ -1343,8 +1354,8 @@ const crsiInterpretCmd: CommandHandler = (ctx, args) => {
1343
1354
  if (autoMemory) {
1344
1355
  const count = autoMemory.sessionReflectionCount ?? 0
1345
1356
  if (count > 0) {
1346
- lines.push('### 📊 CRSI Reflection Summary', '')
1347
- 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) }))
1348
1359
  lines.push('')
1349
1360
  }
1350
1361
  }
@@ -1353,10 +1364,16 @@ const crsiInterpretCmd: CommandHandler = (ctx, args) => {
1353
1364
  const usage = engine.getUsageTracker?.()
1354
1365
  if (usage) {
1355
1366
  const summary = usage.getSummary()
1356
- lines.push('### 💰 Token Usage', '')
1357
- lines.push(`API input tokens: ${summary.apiInputTokens.toLocaleString()}`)
1358
- lines.push(`API output tokens: ${summary.apiOutputTokens.toLocaleString()}`)
1359
- 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
+ )
1360
1377
  lines.push('')
1361
1378
  }
1362
1379
 
@@ -1368,9 +1385,9 @@ const crsiInterpretCmd: CommandHandler = (ctx, args) => {
1368
1385
  if (analysis.systemHealth) {
1369
1386
  const h = analysis.systemHealth
1370
1387
  const bar = '█'.repeat(Math.round(h.score / 10)) + '░'.repeat(10 - Math.round(h.score / 10))
1371
- lines.push('### 🏥 System Health', '')
1372
- lines.push(`Overall: ${bar} **${h.score}/100**`)
1373
- 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 }))
1374
1391
  if (h.components) {
1375
1392
  lines.push('')
1376
1393
  for (const [comp, score] of Object.entries(h.components)) {
@@ -1394,20 +1411,25 @@ const crsiInterpretCmd: CommandHandler = (ctx, args) => {
1394
1411
  const blocks = c.principles.filter((p) => p.enforce === 'block').length
1395
1412
  const warns = c.principles.filter((p) => p.enforce === 'warn').length
1396
1413
  const autos = c.principles.filter((p) => p.enforce === 'auto').length
1397
- lines.push('### ⚖️ Constitution', '')
1414
+ lines.push(t('commands.crsi_interpret.constitution_title'), '')
1398
1415
  lines.push(
1399
- `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
+ }),
1400
1422
  )
1401
- lines.push(`Version: v${c.version}`)
1423
+ lines.push(t('commands.crsi_interpret.version', { version: c.version }))
1402
1424
  lines.push('')
1403
1425
  }
1404
1426
 
1405
1427
  if (lines.length <= 2) {
1406
- lines.push('_Start using tools to populate CRSI interpretability data._')
1428
+ lines.push(t('commands.crsi_interpret.empty'))
1407
1429
  }
1408
1430
 
1409
1431
  lines.push('──')
1410
- lines.push('🔍 Filter by tool: `/crsi interpret <tool-name>` (e.g. `/crsi interpret Bash`)')
1432
+ lines.push(t('commands.crsi_interpret.filter_hint'))
1411
1433
 
1412
1434
  return { content: lines.join('\n') }
1413
1435
  }
@@ -1424,7 +1446,7 @@ const crsiRedTeamCmd: CommandHandler = async (ctx) => {
1424
1446
  const preflight = engine.getPreFlightChecker?.()
1425
1447
 
1426
1448
  if (!constitution || !preflight) {
1427
- return { content: 'Red-Team 需要 ConstitutionLoader 和 PreFlightChecker 均已初始化。' }
1449
+ return { content: t('commands.crsi_red_team.not_init') }
1428
1450
  }
1429
1451
 
1430
1452
  const { RedTeam: RT } = await import('../../src/core/red-team.js')
@@ -1432,21 +1454,21 @@ const crsiRedTeamCmd: CommandHandler = async (ctx) => {
1432
1454
  const report = redTeam.run(constitution, preflight)
1433
1455
 
1434
1456
  const lines: string[] = [
1435
- '## 🔴 CRSI Red-Team Report',
1457
+ t('commands.crsi_red_team.title'),
1436
1458
  '',
1437
- `Overall Score: **${report.score}/100**`,
1459
+ t('commands.crsi_red_team.score', { score: String(report.score) }),
1438
1460
  '',
1439
- `| Metric | Count |`,
1440
- `|--------|------|`,
1441
- `| Total attacks | ${report.total} |`,
1442
- `| 🛡️ Correctly blocked | ${report.blocked} |`,
1443
- `| 🔴 Passed through (GAP) | ${report.passedThrough} |`,
1444
- `| ⚠️ 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) }),
1445
1467
  '',
1446
1468
  ]
1447
1469
 
1448
1470
  // Per-principle breakdown
1449
- lines.push('### By Principle', '')
1471
+ lines.push(t('commands.crsi_red_team.by_principle'), '')
1450
1472
  for (const [pid, stats] of Object.entries(report.byPrinciple)) {
1451
1473
  const pct = stats.total > 0 ? Math.round((stats.blocked / stats.total) * 100) : 100
1452
1474
  const bar = '█'.repeat(Math.round(pct / 10)) + '░'.repeat(10 - Math.round(pct / 10))
@@ -1458,11 +1480,14 @@ const crsiRedTeamCmd: CommandHandler = async (ctx) => {
1458
1480
  // Detail: passed-through attacks
1459
1481
  const gaps = report.results.filter((r) => r.attack.shouldBlock && !r.blocked)
1460
1482
  if (gaps.length > 0) {
1461
- lines.push('### 🔴 Security Gaps (Should Have Been Blocked)', '')
1483
+ lines.push(t('commands.crsi_red_team.gaps_title'), '')
1462
1484
  for (const g of gaps) {
1463
1485
  lines.push(`- **${g.attack.principleId}**: ${g.attack.description}`)
1464
1486
  lines.push(
1465
- ` 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
+ }),
1466
1491
  )
1467
1492
  }
1468
1493
  lines.push('')
@@ -1471,21 +1496,25 @@ const crsiRedTeamCmd: CommandHandler = async (ctx) => {
1471
1496
  // Detail: caught attacks
1472
1497
  const caught = report.results.filter((r) => r.blocked)
1473
1498
  if (caught.length > 0) {
1474
- lines.push('### 🛡️ Successfully Blocked', '')
1499
+ lines.push(t('commands.crsi_red_team.blocked_title'), '')
1475
1500
  for (const c of caught) {
1476
- 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
+ )
1477
1508
  }
1478
1509
  lines.push('')
1479
1510
  }
1480
1511
 
1481
1512
  if (report.score === 100) {
1482
- lines.push('🎉 **All attacks blocked!** The SIS immune system is fully operational.')
1513
+ lines.push(t('commands.crsi_red_team.all_blocked'))
1483
1514
  } else if (report.score >= 80) {
1484
- lines.push(
1485
- '⚠️ Good coverage. Review the gaps above and add audit patterns to `ai-guardrails.yml`.',
1486
- )
1515
+ lines.push(t('commands.crsi_red_team.good_coverage'))
1487
1516
  } else {
1488
- lines.push('🔴 **Critical gaps detected.** Prioritize fixing the passed-through attacks above.')
1517
+ lines.push(t('commands.crsi_red_team.critical'))
1489
1518
  }
1490
1519
 
1491
1520
  return { content: lines.join('\n') }
@@ -1502,45 +1531,61 @@ const dreamCmd: CommandHandler = async (ctx, args) => {
1502
1531
 
1503
1532
  const engine = ctx.engine.getDreamEngine?.()
1504
1533
  if (!engine) {
1505
- return { content: 'DreamEngine 未初始化。请确认 Mipham Code 版本 >= v0.34.0。' }
1534
+ return { content: t('commands.dream.not_init') }
1506
1535
  }
1507
1536
 
1508
1537
  if (status) {
1509
1538
  const history = engine.getDreamHistory()
1510
1539
  if (history.length === 0) {
1511
- return { content: '🌙 尚未运行过 Auto-Dream。使用 `/dream` 手动触发首次梦境整合。' }
1540
+ return { content: t('commands.dream.no_history') }
1512
1541
  }
1513
- const lines: string[] = ['## 🌙 Auto-Dream 历史', '']
1542
+ const lines: string[] = [t('commands.dream.history_title'), '']
1514
1543
  for (const entry of history.slice(0, 5)) {
1515
1544
  const ts = new Date(entry.timestamp).toLocaleString('zh-CN')
1516
1545
  const total = entry.actions.length
1517
1546
  const applied = entry.actions.filter((a) => a.autoApplied).length
1518
1547
  const flagged = entry.actions.filter((a) => !a.autoApplied).length
1519
- 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
+ )
1520
1556
  }
1521
1557
  return { content: lines.join('\n') }
1522
1558
  }
1523
1559
 
1524
1560
  const report = engine.dream({ aggressive })
1525
1561
  const lines: string[] = [
1526
- '## 🌙 Auto-Dream 完成',
1562
+ t('commands.dream.done_title'),
1527
1563
  '',
1528
- `记忆整合: ${report.beforeCount} → ${report.afterCount}`,
1529
- ...(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
+ : []),
1530
1571
  ...(report.phases.contradictionsFound > 0
1531
- ? [`- 矛盾冲突: ${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) })]
1532
1576
  : []),
1533
- ...(report.phases.merged > 0 ? [`- 合并: ${report.phases.merged} 组`] : []),
1534
1577
  ...(report.phases.solidified > 0
1535
- ? [`- 模糊标记: ${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) })]
1536
1582
  : []),
1537
- ...(report.phases.pruned > 0 ? [`- 清理过期: ${report.phases.pruned} 条`] : []),
1538
1583
  '',
1539
1584
  ]
1540
1585
 
1541
1586
  const flagged = report.actions.filter((a) => !a.autoApplied)
1542
1587
  if (flagged.length > 0 && !aggressive) {
1543
- lines.push(`⚠️ ${flagged.length} 项待审查。使用 \`/dream --aggressive\` 自动应用所有操作。`)
1588
+ lines.push(t('commands.dream.review_hint', { count: String(flagged.length) }))
1544
1589
  lines.push('')
1545
1590
  for (const action of flagged) {
1546
1591
  lines.push(`- [ ] **${action.type}**: ${action.description}`)
@@ -1548,7 +1593,7 @@ const dreamCmd: CommandHandler = async (ctx, args) => {
1548
1593
  }
1549
1594
 
1550
1595
  if (report.actions.length === 0) {
1551
- lines.push('✅ 记忆干净,无需整理。')
1596
+ lines.push(t('commands.dream.clean'))
1552
1597
  }
1553
1598
 
1554
1599
  return { content: lines.join('\n') }
@@ -1563,7 +1608,7 @@ const constitutionCmd: CommandHandler = (ctx, args) => {
1563
1608
  const engine = ctx.engine
1564
1609
  const constitution = engine.getConstitutionLoader?.()
1565
1610
  if (!constitution) {
1566
- return { content: 'ConstitutionLoader 未初始化。请确认 Mipham Code 版本 >= v0.34.0。' }
1611
+ return { content: t('commands.constitution.not_init') }
1567
1612
  }
1568
1613
 
1569
1614
  const subCmd = args[0]?.toLowerCase()
@@ -1573,11 +1618,11 @@ const constitutionCmd: CommandHandler = (ctx, args) => {
1573
1618
  const c = constitution.reload()
1574
1619
  return {
1575
1620
  content: [
1576
- '## ⚖️ Constitution Reloaded',
1621
+ t('commands.constitution.reload_title'),
1577
1622
  '',
1578
- `Version: **v${c.version}**`,
1579
- `Principles: **${c.principles.length}**`,
1580
- `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() }),
1581
1626
  '',
1582
1627
  c.principles.map((p) => `- **${p.id}** [${p.enforce}]: ${p.text}`).join('\n'),
1583
1628
  ].join('\n'),
@@ -1587,9 +1632,13 @@ const constitutionCmd: CommandHandler = (ctx, args) => {
1587
1632
  // /constitution view (default)
1588
1633
  const c = constitution.load()
1589
1634
  const lines: string[] = [
1590
- '## ⚖️ Mipham Constitution',
1635
+ t('commands.constitution.view_title'),
1591
1636
  '',
1592
- `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
+ }),
1593
1642
  '',
1594
1643
  '---',
1595
1644
  '',
@@ -1601,20 +1650,18 @@ const constitutionCmd: CommandHandler = (ctx, args) => {
1601
1650
  lines.push('')
1602
1651
  lines.push(p.text)
1603
1652
  if (p.rationale) lines.push(` *${p.rationale}*`)
1604
- if (p.scope) lines.push(` Scope: \`${p.scope}\``)
1605
- 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(', ') }))
1606
1655
  lines.push('')
1607
1656
  }
1608
1657
 
1609
1658
  lines.push('---')
1610
1659
  lines.push('')
1611
- lines.push('🔧 `/constitution reload` — 重新加载(修改 ai-guardrails.yml 后使用)')
1612
- lines.push('📝 编辑: `vi ~/.mipham/ai-guardrails.yml`')
1613
- 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'))
1614
1663
  lines.push('')
1615
- lines.push(
1616
- '*Inspired by Anthropic Constitutional AI. Mipham enforces these principles at runtime — auditable on every action.*',
1617
- )
1664
+ lines.push(t('commands.constitution.inspired'))
1618
1665
 
1619
1666
  return { content: lines.join('\n') }
1620
1667
  }
@@ -1627,25 +1674,32 @@ const bugReportCmd: CommandHandler = async (ctx) => {
1627
1674
  const t = resolveT(ctx)
1628
1675
  const engine = ctx.engine
1629
1676
  const lines: string[] = [
1630
- '## 🐛 Bug Report',
1677
+ t('commands.bug_report.title'),
1631
1678
  '',
1632
- '> Copy the report below and paste it into your GitHub Issue.',
1679
+ t('commands.bug_report.hint'),
1633
1680
  '',
1634
1681
  '---',
1635
1682
  '',
1636
1683
  ]
1637
1684
 
1638
1685
  // ── Version & Runtime ──
1639
- lines.push('### Environment', '')
1640
- lines.push(`- **Mipham Code**: v${ctx.version}`)
1641
- lines.push(`- **Session**: ${ctx.sessionId}`)
1642
- lines.push(`- **Node**: ${process.version}`)
1643
- 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 }))
1644
1691
  lines.push(
1645
- `- **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
+ }),
1646
1700
  )
1647
- lines.push(`- **Provider**: ${ctx.providerId}`)
1648
- 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 }))
1649
1703
  lines.push('')
1650
1704
 
1651
1705
  // ── SIS errors ──
@@ -1654,10 +1708,16 @@ const bugReportCmd: CommandHandler = async (ctx) => {
1654
1708
  if (db) {
1655
1709
  const active = db.getActive()
1656
1710
  if (active.length > 0) {
1657
- lines.push('### Active Error Signatures', '')
1711
+ lines.push(t('commands.bug_report.sigs_title'), '')
1658
1712
  for (const sig of active.slice(0, 10)) {
1659
1713
  lines.push(
1660
- `- \`${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
+ }),
1661
1721
  )
1662
1722
  }
1663
1723
  lines.push('')
@@ -1674,10 +1734,14 @@ const bugReportCmd: CommandHandler = async (ctx) => {
1674
1734
  const health = hookEngine.getHookHealth()
1675
1735
  const disabled = health.filter((h) => h.health.disabled)
1676
1736
  if (disabled.length > 0) {
1677
- lines.push('### Disabled Hooks', '')
1737
+ lines.push(t('commands.bug_report.disabled_hooks'), '')
1678
1738
  for (const entry of disabled) {
1679
1739
  lines.push(
1680
- `- \`${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
+ }),
1681
1745
  )
1682
1746
  }
1683
1747
  lines.push('')
@@ -1694,10 +1758,15 @@ const bugReportCmd: CommandHandler = async (ctx) => {
1694
1758
  const history = dreamEngine.getDreamHistory()
1695
1759
  if (history.length > 0) {
1696
1760
  const last = history[0]!
1697
- lines.push('### Last Auto-Dream', '')
1698
- 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 }))
1699
1763
  lines.push(
1700
- `- **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
+ }),
1701
1770
  )
1702
1771
  lines.push('')
1703
1772
  }
@@ -1707,17 +1776,17 @@ const bugReportCmd: CommandHandler = async (ctx) => {
1707
1776
  }
1708
1777
 
1709
1778
  // ── Steps to reproduce (template) ──
1710
- lines.push('### Steps to Reproduce', '')
1779
+ lines.push(t('commands.bug_report.steps_title'), '')
1711
1780
  lines.push('1. ')
1712
1781
  lines.push('2. ')
1713
1782
  lines.push('3. ')
1714
1783
  lines.push('')
1715
- lines.push('### Expected Behavior', '')
1784
+ lines.push(t('commands.bug_report.expected'), '')
1716
1785
  lines.push('')
1717
- lines.push('### Actual Behavior', '')
1786
+ lines.push(t('commands.bug_report.actual'), '')
1718
1787
  lines.push('')
1719
1788
  lines.push('---')
1720
- lines.push(`🤖 Generated with Mipham Code v${ctx.version}`)
1789
+ lines.push(t('commands.bug_report.generated', { version: ctx.version }))
1721
1790
 
1722
1791
  return { content: lines.join('\n'), copyContent: lines.join('\n') }
1723
1792
  }
@@ -1730,7 +1799,7 @@ const bugReportCmd: CommandHandler = async (ctx) => {
1730
1799
 
1731
1800
  const changelogCmd: CommandHandler = async (ctx) => {
1732
1801
  const t = resolveT(ctx)
1733
- const lines: string[] = ['## 📋 Changelog', '']
1802
+ const lines: string[] = [t('commands.changelog.title'), '']
1734
1803
 
1735
1804
  try {
1736
1805
  const { execSync } = await import('node:child_process')
@@ -1741,11 +1810,11 @@ const changelogCmd: CommandHandler = async (ctx) => {
1741
1810
  .slice(0, 15)
1742
1811
 
1743
1812
  if (tags.length === 0) {
1744
- lines.push('No version tags found.')
1813
+ lines.push(t('commands.changelog.no_tags'))
1745
1814
  return { content: lines.join('\n') }
1746
1815
  }
1747
1816
 
1748
- lines.push(`Current: **v${ctx.version}**`, '')
1817
+ lines.push(t('commands.changelog.current', { version: ctx.version }), '')
1749
1818
 
1750
1819
  for (let i = 0; i < tags.length; i++) {
1751
1820
  const tag = tags[i]!
@@ -1761,25 +1830,25 @@ const changelogCmd: CommandHandler = async (ctx) => {
1761
1830
  .filter(Boolean)
1762
1831
 
1763
1832
  const isCurrent = tag === `v${ctx.version}`
1764
- lines.push(`### ${tag}${isCurrent ? ' ← current' : ''}`, '')
1833
+ lines.push(`### ${tag}${isCurrent ? t('commands.changelog.current_marker') : ''}`, '')
1765
1834
  if (log.length > 0) {
1766
1835
  for (const entry of log) {
1767
1836
  lines.push(`- ${entry}`)
1768
1837
  }
1769
1838
  } else {
1770
- lines.push(' _(release tag only)_')
1839
+ lines.push(t('commands.changelog.release_only'))
1771
1840
  }
1772
1841
  lines.push('')
1773
1842
  } catch {
1774
- lines.push(`### ${tag}`, '', ' _(details unavailable)_', '')
1843
+ lines.push(`### ${tag}`, '', t('commands.changelog.details_unavailable'), '')
1775
1844
  }
1776
1845
  }
1777
1846
  } catch {
1778
- lines.push('Unable to read git history. Make sure you are in the Mipham Code repository.')
1847
+ lines.push(t('commands.changelog.no_git'))
1779
1848
  }
1780
1849
 
1781
1850
  lines.push('---')
1782
- lines.push(`Full history: \`git log --oneline\``)
1851
+ lines.push(t('commands.changelog.full_history'))
1783
1852
 
1784
1853
  return { content: lines.join('\n') }
1785
1854
  }
@@ -4596,6 +4665,7 @@ const commandsListCmd: CommandHandler = () => {
4596
4665
  '/keys': 'Account',
4597
4666
  '/keys rotate': 'Account',
4598
4667
  '/keys audit': 'Account',
4668
+ '/keys view': 'Account',
4599
4669
  '/feedback': 'Account',
4600
4670
  '/agents': 'Agents',
4601
4671
  '/bg': 'Agents',
@@ -4778,6 +4848,7 @@ registry.set('/logout', logoutCmd)
4778
4848
  registry.set('/keys', keysCmd)
4779
4849
  registry.set('/keys rotate', keysCmd)
4780
4850
  registry.set('/keys audit', keysCmd)
4851
+ registry.set('/keys view', keysCmd)
4781
4852
  registry.set('/feedback', feedbackCmd)
4782
4853
  registry.set('/agents', agentsCmd)
4783
4854
  registry.set('/bg', bgCmd)
@@ -4937,6 +5008,7 @@ const COMMAND_DESCRIPTIONS: Record<string, string> = {
4937
5008
  '/keys': 'List API key rotation status',
4938
5009
  '/keys rotate': 'Rotate an API key',
4939
5010
  '/keys audit': 'Check for expired keys',
5011
+ '/keys view': 'View a provider plaintext API key',
4940
5012
  '/feedback': 'Send feedback',
4941
5013
  '/agents': 'Agent view dashboard',
4942
5014
  '/bg': 'Run a background agent task',