@miphamai/cli 0.50.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 (46) hide show
  1. package/bin/mipham.ts +1 -9
  2. package/package.json +1 -1
  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/project.ts +1 -1
  10. package/src/config/defaults.ts +5 -0
  11. package/src/core/auto-memory.ts +3 -3
  12. package/src/core/constitution-loader.ts +0 -2
  13. package/src/core/context.ts +0 -1
  14. package/src/core/credential-masker/index.ts +1 -0
  15. package/src/core/credential-masker/matcher.ts +18 -7
  16. package/src/core/credential-masker/search.ts +73 -0
  17. package/src/core/credential-masker.ts +1 -0
  18. package/src/core/crsi-sandbox.ts +0 -1
  19. package/src/core/dream-engine.ts +0 -2
  20. package/src/core/memory/memory-loader.ts +3 -1
  21. package/src/core/permission-rules.ts +23 -12
  22. package/src/core/rules-loader.ts +8 -10
  23. package/src/daemon/database.ts +0 -1
  24. package/src/daemon/feishu/adapter.ts +2 -1
  25. package/src/daemon/index.ts +18 -8
  26. package/src/daemon/server.ts +53 -10
  27. package/src/daemon/session-manager.ts +5 -4
  28. package/src/daemon/telegram/adapter.ts +70 -0
  29. package/src/daemon/telegram/api.ts +45 -0
  30. package/src/daemon/telegram/env.ts +14 -0
  31. package/src/daemon/telegram/poller.ts +64 -0
  32. package/src/daemon/telegram/types.ts +10 -0
  33. package/src/i18n-core/locales/en-US.json +123 -0
  34. package/src/i18n-core/locales/zh-CN.json +123 -0
  35. package/src/index.tsx +6 -0
  36. package/src/shared/package-info.ts +1 -1
  37. package/src/shared/types.ts +2 -0
  38. package/src/skills/bundled-skill-assets.ts +5 -5
  39. package/src/skills/bundled-skills.ts +1 -1
  40. package/src/tools/agent/exit-plan.ts +1 -3
  41. package/src/tools/agent/memory.ts +2 -1
  42. package/src/tools/file/glob.ts +38 -23
  43. package/src/tools/file/grep.ts +86 -64
  44. package/src/tools/index.ts +5 -5
  45. package/src/ui/commands.ts +188 -119
  46. package/src/ui/config-wizard.tsx +1 -1
@@ -25,6 +25,8 @@ import type { DaemonStatus } from './types'
25
25
  import { logger } from './logger'
26
26
  import { parseFeishuEnv } from './feishu/env.js'
27
27
  import type { FeishuConfig } from './feishu/types.js'
28
+ import { parseTelegramEnv } from './telegram/env.js'
29
+ import type { TelegramConfig } from './telegram/types.js'
28
30
  import { loadConfig } from '../config/loader'
29
31
 
30
32
  const HOME = homedir()
@@ -40,8 +42,6 @@ const DEFAULT_PORT = 45671
40
42
  let activeServer: Server<unknown> | null = null
41
43
  let activeDb: DaemonDatabase | null = null
42
44
  let activePool: WorkerPool | null = null
43
- let activeAgentManager: AgentManager | null = null
44
- let activeMessageBus: MessageBus | null = null
45
45
  let activeScheduleManager: ScheduleManager | null = null
46
46
  let activeRateLimiter: RateLimiter | null = null
47
47
 
@@ -150,9 +150,7 @@ export async function startDaemon(): Promise<{ port: number; token: string }> {
150
150
 
151
151
  // Create agent manager and message bus (Phase 3)
152
152
  const agentManager = new AgentManager(db)
153
- activeAgentManager = agentManager
154
153
  const messageBus = new MessageBus()
155
- activeMessageBus = messageBus
156
154
 
157
155
  // Create goal manager and schedule manager (Phase 4)
158
156
  const goalManager = new GoalManager(db)
@@ -181,6 +179,21 @@ export async function startDaemon(): Promise<{ port: number; token: string }> {
181
179
  }
182
180
  }
183
181
 
182
+ // Telegram remote-control adapter(env 未配置时跳过;配置则解析 session 默认 provider/model)
183
+ const telegramConfig = parseTelegramEnv()
184
+ let telegram: { config: TelegramConfig; cwd: string; provider: string; model: string } | undefined
185
+ if (telegramConfig) {
186
+ const cfg = loadConfig()
187
+ // 取第一个非 upcoming(即非「待上线」)的 provider 及其首个 model
188
+ const provider = cfg.providers.find((p) => p.status !== 'upcoming') ?? cfg.providers[0]
189
+ telegram = {
190
+ config: telegramConfig,
191
+ cwd: process.env.TELEGRAM_CWD || process.cwd(),
192
+ provider: provider?.id ?? 'anthropic',
193
+ model: provider?.models?.[0]?.id ?? 'claude-sonnet-5',
194
+ }
195
+ }
196
+
184
197
  // Start HTTP server (Bun.serve starts listening immediately)
185
198
  const server = createServer({
186
199
  db,
@@ -196,6 +209,7 @@ export async function startDaemon(): Promise<{ port: number; token: string }> {
196
209
  scheduleManager,
197
210
  rateLimiter,
198
211
  feishu,
212
+ telegram,
199
213
  })
200
214
  activeServer = server
201
215
 
@@ -250,10 +264,6 @@ export async function stopDaemon(force: boolean = false): Promise<void> {
250
264
  activeRateLimiter = null
251
265
  }
252
266
 
253
- // Clean up agent manager and message bus (Phase 3)
254
- activeAgentManager = null
255
- activeMessageBus = null
256
-
257
267
  // Close database (commits any pending WAL)
258
268
  if (activeDb) {
259
269
  activeDb.close()
@@ -26,6 +26,9 @@ import type { ToolDefinition, PermissionMode, PermissionRestrictions } from '../
26
26
  import { createFeishuAdapter } from './feishu/adapter.js'
27
27
  import { createFeishuApi } from './feishu/api.js'
28
28
  import type { FeishuConfig } from './feishu/types.js'
29
+ import { createTelegramAdapter } from './telegram/adapter.js'
30
+ import { createTelegramApi } from './telegram/api.js'
31
+ import type { TelegramConfig } from './telegram/types.js'
29
32
  import { startHeartbeat } from './heartbeat'
30
33
 
31
34
  interface ServerConfig {
@@ -42,6 +45,7 @@ interface ServerConfig {
42
45
  scheduleManager: ScheduleManager
43
46
  rateLimiter: RateLimiter
44
47
  feishu?: { config: FeishuConfig; cwd: string; provider: string; model: string }
48
+ telegram?: { config: TelegramConfig; cwd: string; provider: string; model: string }
45
49
  }
46
50
 
47
51
  interface WsData {
@@ -71,13 +75,20 @@ function resolveDaemonPermission(): PermissionMode {
71
75
 
72
76
  /**
73
77
  * Build the daemon's PermissionSystem, honoring org-level restrictions
74
- * (permissionRestrictions). setRestrictions re-clamps the env-derived mode,
75
- * so a `MIPHAM_DAEMON_PERMISSION=bypassPermissions` is downgraded when the
76
- * config forbids it — mirroring the CLI's fail-closed behavior.
78
+ * (permissionRestrictions) and user-defined allow/deny rules. setRestrictions
79
+ * re-clamps the env-derived mode, so a `MIPHAM_DAEMON_PERMISSION=bypassPermissions`
80
+ * is downgraded when the config forbids it — mirroring the CLI's fail-closed behavior.
77
81
  */
78
- export function buildDaemonPermission(restrictions?: PermissionRestrictions): PermissionSystem {
82
+ export function buildDaemonPermission(
83
+ restrictions?: PermissionRestrictions,
84
+ rules?: { allow?: string[]; deny?: string[] },
85
+ ): PermissionSystem {
79
86
  const permission = new PermissionSystem(resolveDaemonPermission())
80
87
  if (restrictions) permission.setRestrictions(restrictions)
88
+ if (rules) {
89
+ for (const rule of rules.allow ?? []) permission.allow(rule)
90
+ for (const rule of rules.deny ?? []) permission.deny(rule)
91
+ }
81
92
  return permission
82
93
  }
83
94
 
@@ -112,6 +123,7 @@ export function createServer(config: ServerConfig): Server<WsData> {
112
123
  scheduleManager,
113
124
  rateLimiter,
114
125
  feishu,
126
+ telegram,
115
127
  } = config
116
128
 
117
129
  const wsClients = new Map<string, Set<ServerWebSocket<WsData>>>()
@@ -199,7 +211,11 @@ export function createServer(config: ServerConfig): Server<WsData> {
199
211
  }
200
212
  }
201
213
 
202
- const permission = buildDaemonPermission(loadConfig(cwd).permissionRestrictions)
214
+ const daemonConfig = loadConfig(cwd)
215
+ const permission = buildDaemonPermission(
216
+ daemonConfig.permissionRestrictions,
217
+ daemonConfig.permissionRules,
218
+ )
203
219
  const engine = new QueryEngine(sharedRegistry, context, sharedTools, permission)
204
220
  engine.setSessionId(sessionId)
205
221
  engineCache.set(sessionId, engine)
@@ -208,7 +224,7 @@ export function createServer(config: ServerConfig): Server<WsData> {
208
224
 
209
225
  function getOrCreateWorker(
210
226
  sessionId: string,
211
- ws?: ServerWebSocket<WsData>,
227
+ _ws?: ServerWebSocket<WsData>,
212
228
  ): SessionWorker | null {
213
229
  try {
214
230
  const session = db.getSession(sessionId)
@@ -245,14 +261,28 @@ export function createServer(config: ServerConfig): Server<WsData> {
245
261
  })
246
262
  : undefined
247
263
 
264
+ // ── Telegram remote-control adapter(长轮询,无需 webhook 路由)──
265
+ const telegramAdapter = telegram
266
+ ? createTelegramAdapter(telegram.config, createTelegramApi(telegram.config), {
267
+ sm,
268
+ getOrCreateWorker,
269
+ rateLimiter,
270
+ cwd: telegram.cwd,
271
+ provider: telegram.provider,
272
+ model: telegram.model,
273
+ })
274
+ : undefined
275
+ telegramAdapter?.start()
276
+
248
277
  // ── 心跳式通知:定时扫 pending(goal/schedule),只通知、不自主行动 ──
278
+ const heartbeatSource = {
279
+ listGoals: () => sm.listSessions().flatMap((s) => goalManager.getGoals(s.id)),
280
+ listSchedules: () => sm.listSessions().flatMap((s) => scheduleManager.getSchedules(s.id)),
281
+ }
249
282
  if (feishu) {
250
283
  const feishuApi = createFeishuApi(feishu.config)
251
284
  startHeartbeat({
252
- source: {
253
- listGoals: () => sm.listSessions().flatMap((s) => goalManager.getGoals(s.id)),
254
- listSchedules: () => sm.listSessions().flatMap((s) => scheduleManager.getSchedules(s.id)),
255
- },
285
+ source: heartbeatSource,
256
286
  push: (message) => {
257
287
  for (const openId of feishu.config.allowedOpenIds) {
258
288
  void feishuApi.sendText(openId, message).catch(() => {
@@ -262,6 +292,19 @@ export function createServer(config: ServerConfig): Server<WsData> {
262
292
  },
263
293
  })
264
294
  }
295
+ if (telegram) {
296
+ const telegramApi = createTelegramApi(telegram.config)
297
+ startHeartbeat({
298
+ source: heartbeatSource,
299
+ push: (message) => {
300
+ for (const chatId of telegram.config.allowedChatIds) {
301
+ void telegramApi.sendText(chatId, message).catch(() => {
302
+ /* 推送失败静默,不打断心跳 */
303
+ })
304
+ }
305
+ },
306
+ })
307
+ }
265
308
 
266
309
  const server = Bun.serve<WsData>({
267
310
  port,
@@ -1,5 +1,5 @@
1
1
  import type { DaemonDatabase } from './database'
2
- import type { DaemonSession, SessionStatus } from './types'
2
+ import type { DaemonSession } from './types'
3
3
 
4
4
  type SessionCloseCallback = (sessionId: string) => void
5
5
 
@@ -23,13 +23,14 @@ export class SessionManager {
23
23
  return this.db.listSessions(status)
24
24
  }
25
25
 
26
- getOrCreateByFeishuOpenId(
27
- openId: string,
26
+ getOrCreateByExternalUser(
27
+ channel: string,
28
+ userId: string,
28
29
  cwd: string,
29
30
  provider: string,
30
31
  model: string,
31
32
  ): DaemonSession {
32
- const name = `feishu-${openId}`
33
+ const name = `${channel}-${userId}`
33
34
  const existing = this.db.listSessions().find((s) => s.name === name && s.status !== 'closed')
34
35
  if (existing) return existing
35
36
  return this.db.createSession({ name, cwd, provider, model })
@@ -0,0 +1,70 @@
1
+ import type { TelegramApi } from './api.js'
2
+ import type { TelegramConfig, TelegramMessage } from './types.js'
3
+ import type { SessionManager } from '../session-manager'
4
+ import type { SessionWorker } from '../session-worker'
5
+ import type { RateLimiter } from '../rate-limiter'
6
+ import { startTelegramPoller } from './poller.js'
7
+
8
+ export interface TelegramAdapterDeps {
9
+ sm: SessionManager
10
+ getOrCreateWorker: (sessionId: string) => SessionWorker | null
11
+ rateLimiter: RateLimiter
12
+ cwd: string
13
+ provider: string
14
+ model: string
15
+ }
16
+
17
+ export interface TelegramAdapter {
18
+ start(): () => void
19
+ handleMessage(msg: TelegramMessage): Promise<void>
20
+ isAllowed(chatId: string): boolean
21
+ }
22
+
23
+ export function createTelegramAdapter(
24
+ config: TelegramConfig,
25
+ api: TelegramApi,
26
+ deps: TelegramAdapterDeps,
27
+ ): TelegramAdapter {
28
+ const allowed = new Set(config.allowedChatIds)
29
+ let stopPoller: (() => void) | null = null
30
+
31
+ async function handleMessage(msg: TelegramMessage): Promise<void> {
32
+ try {
33
+ if (!allowed.has(msg.chatId)) return
34
+ if (!deps.rateLimiter.check(`telegram:${msg.chatId}`).allowed) return
35
+
36
+ const session = deps.sm.getOrCreateByExternalUser(
37
+ 'telegram',
38
+ msg.chatId,
39
+ deps.cwd,
40
+ deps.provider,
41
+ deps.model,
42
+ )
43
+ const worker = deps.getOrCreateWorker(session.id)
44
+ if (!worker) {
45
+ await api.sendText(msg.chatId, '(会话初始化失败,请稍后重试)')
46
+ return
47
+ }
48
+ await worker.processPrompt(msg.text)
49
+ const result = worker.getLastAssistantContent()
50
+ await api.sendText(msg.chatId, result ? result.slice(0, 4096) : '(无回复)')
51
+ } catch (err) {
52
+ console.error('[telegram] message handling failed:', err)
53
+ try {
54
+ await api.sendText(msg.chatId, '(处理失败,请稍后重试)')
55
+ } catch {
56
+ /* 忽略回送失败,不 rethrow */
57
+ }
58
+ }
59
+ }
60
+
61
+ return {
62
+ handleMessage,
63
+ isAllowed: (chatId) => allowed.has(chatId),
64
+ start() {
65
+ if (stopPoller) return stopPoller
66
+ stopPoller = startTelegramPoller(api, handleMessage)
67
+ return stopPoller
68
+ },
69
+ }
70
+ }
@@ -0,0 +1,45 @@
1
+ import type { TelegramConfig } from './types.js'
2
+
3
+ export interface TelegramApi {
4
+ getUpdates(
5
+ offset: number,
6
+ timeoutSeconds: number,
7
+ ): Promise<Array<{ update_id: number; message?: unknown }>>
8
+ sendText(chatId: string, text: string): Promise<void>
9
+ }
10
+
11
+ /** 裸 fetch 直连 Telegram Bot API,零依赖。 */
12
+ export function createTelegramApi(config: TelegramConfig): TelegramApi {
13
+ const base = `https://api.telegram.org/bot${config.botToken}`
14
+ return {
15
+ async getUpdates(offset, timeoutSeconds) {
16
+ const url =
17
+ `${base}/getUpdates?offset=${offset}&timeout=${timeoutSeconds}` +
18
+ `&limit=100&allowed_updates=%5B%22message%22%5D`
19
+ const res = await fetch(url)
20
+ if (!res.ok) throw new Error(`getUpdates ${res.status}`)
21
+ const data = (await res.json()) as {
22
+ ok: boolean
23
+ result?: Array<{ update_id: number; message?: unknown }>
24
+ error_code?: number
25
+ description?: string
26
+ }
27
+ if (!data.ok) throw new Error(`getUpdates failed: ${data.error_code ?? res.status}`)
28
+ return data.result ?? []
29
+ },
30
+ async sendText(chatId, text) {
31
+ const res = await fetch(`${base}/sendMessage`, {
32
+ method: 'POST',
33
+ headers: { 'Content-Type': 'application/json' },
34
+ body: JSON.stringify({ chat_id: chatId, text }),
35
+ })
36
+ if (!res.ok) throw new Error(`sendMessage ${res.status}`)
37
+ const data = (await res.json()) as {
38
+ ok: boolean
39
+ error_code?: number
40
+ description?: string
41
+ }
42
+ if (!data.ok) throw new Error(`sendMessage failed: ${data.error_code ?? res.status}`)
43
+ },
44
+ }
45
+ }
@@ -0,0 +1,14 @@
1
+ import type { TelegramConfig } from './types.js'
2
+
3
+ /** fail-closed:缺 botToken → null(daemon 不启用 Telegram)。 */
4
+ export function parseTelegramEnv(): TelegramConfig | null {
5
+ const botToken = process.env.TELEGRAM_BOT_TOKEN
6
+ if (!botToken) return null
7
+ return {
8
+ botToken,
9
+ allowedChatIds: (process.env.TELEGRAM_ALLOWED_CHAT_IDS || '')
10
+ .split(',')
11
+ .map((s) => s.trim())
12
+ .filter(Boolean),
13
+ }
14
+ }
@@ -0,0 +1,64 @@
1
+ import type { TelegramApi } from './api.js'
2
+ import type { TelegramMessage } from './types.js'
3
+
4
+ /** 从 update 提取文本消息;非文本/缺 message → null。chat.id 统一字符串化比对(id < 2^53;超范围 bigint 后续再引入)。 */
5
+ export function extractTextMessage(update: unknown): TelegramMessage | null {
6
+ const msg = (
7
+ update as { message?: { chat?: { id?: number | string }; message_id?: number; text?: string } }
8
+ )?.message
9
+ if (!msg?.text || msg.chat?.id == null) return null
10
+ return { chatId: String(msg.chat.id), messageId: msg.message_id ?? 0, text: msg.text }
11
+ }
12
+
13
+ /** 推进 offset:最大 update_id + 1;空列表沿用 prev。 */
14
+ export function nextOffset(updates: Array<{ update_id?: number }>, prevOffset: number): number {
15
+ if (updates.length === 0) return prevOffset
16
+ return Math.max(...updates.map((u) => u.update_id ?? prevOffset)) + 1
17
+ }
18
+
19
+ /** 指数退避,封顶 30s。 */
20
+ export function nextBackoff(currentMs: number): number {
21
+ return Math.min(currentMs * 2, 30_000)
22
+ }
23
+
24
+ /** 长轮询循环(unref 不阻退出)。返回 stop 函数。循环行为由 Task 7 集成测试覆盖。 */
25
+ export function startTelegramPoller(
26
+ api: TelegramApi,
27
+ onMessage: (msg: TelegramMessage) => Promise<void>,
28
+ opts?: { pollTimeoutSeconds?: number },
29
+ ): () => void {
30
+ let offset = 0
31
+ let backoffMs = 1000
32
+ let stopped = false
33
+ let timer: ReturnType<typeof setTimeout> | null = null
34
+
35
+ async function loop() {
36
+ if (stopped) return
37
+ try {
38
+ const updates = await api.getUpdates(offset, opts?.pollTimeoutSeconds ?? 30)
39
+ for (const u of updates) {
40
+ const msg = extractTextMessage(u)
41
+ if (msg) {
42
+ try {
43
+ await onMessage(msg)
44
+ } catch {
45
+ /* 单条失败不中断轮询 */
46
+ }
47
+ }
48
+ }
49
+ offset = nextOffset(updates, offset)
50
+ backoffMs = 1000
51
+ } catch {
52
+ backoffMs = nextBackoff(backoffMs)
53
+ }
54
+ if (stopped) return
55
+ timer = setTimeout(loop, backoffMs)
56
+ ;(timer as { unref?: () => void }).unref?.()
57
+ }
58
+
59
+ void loop()
60
+ return () => {
61
+ stopped = true
62
+ if (timer) clearTimeout(timer)
63
+ }
64
+ }
@@ -0,0 +1,10 @@
1
+ export interface TelegramConfig {
2
+ botToken: string
3
+ allowedChatIds: string[]
4
+ }
5
+
6
+ export interface TelegramMessage {
7
+ chatId: string
8
+ messageId: number
9
+ text: string
10
+ }
@@ -612,6 +612,129 @@
612
612
  },
613
613
  "schedule": {
614
614
  "failed": "Schedule check failed: {error}"
615
+ },
616
+ "crsi_critique": {
617
+ "not_init": "SelfCritique not initialized. Mipham Code >= v0.34.0 required.",
618
+ "on_title": "## 🔍 Self-Critique: ON",
619
+ "on_desc1": "The AI will now critique its own tool calls before execution.",
620
+ "on_desc2": "Safe + correct + necessary checks run before every targeted tool call.",
621
+ "on_hint": "💡 `/crsi critique off` to disable | `/crsi critique status` for current config",
622
+ "off_title": "## 🔍 Self-Critique: OFF\n\nTool calls will execute without pre-flight critique.",
623
+ "status_title": "## 🔍 Self-Critique Status",
624
+ "state": "State: **{state}**",
625
+ "model": "Model: **{model}**",
626
+ "threshold": "Threshold: **{pct}%**",
627
+ "threshold_detail": "Threshold: **{pct}%** (below this → correction or block)",
628
+ "target_tools": "Target tools: **{tools}**",
629
+ "timeout": "Timeout: **{ms}ms**",
630
+ "status_on_hint": "🔍 `/crsi critique on` — Enable self-critique",
631
+ "status_off_hint": "🔍 `/crsi critique off` — Disable self-critique",
632
+ "inspired": "*Inspired by Anthropic RLAIF — AI critiques its own actions before execution.*"
633
+ },
634
+ "crsi_interpret": {
635
+ "title": "## 🧠 CRSI Tool-Call Interpretability",
636
+ "no_sigs_for_tool": "No error signatures for tool `{tool}`. CRSI immune memory is clean for this tool.",
637
+ "no_sigs": "No active error signatures. CRSI immune memory is clean.",
638
+ "sigs_title": "### 🛡️ Error Signatures{tool}",
639
+ "sig_success": " Success: {bar} {pct}% | {occurrences}x | {strategy}",
640
+ "sig_fix": " Fix: `{fix}`",
641
+ "sigs_more": "... and {count} more signatures",
642
+ "reflection_title": "### 📊 CRSI Reflection Summary",
643
+ "reflections": "Turn reflections analyzed: **{count}**",
644
+ "usage_title": "### 💰 Token Usage",
645
+ "api_in": "API input tokens: {count}",
646
+ "api_out": "API output tokens: {count}",
647
+ "est_tokens": "Estimated tokens: {count}",
648
+ "health_title": "### 🏥 System Health",
649
+ "overall": "Overall: {bar} **{score}/100**",
650
+ "assessment": "Assessment: {assessment}",
651
+ "constitution_title": "### ⚖️ Constitution",
652
+ "principles": "Principles: **{total}** (🚫 {block} block | ⚠️ {warn} warn | 🔄 {auto} auto)",
653
+ "version": "Version: v{version}",
654
+ "empty": "_Start using tools to populate CRSI interpretability data._",
655
+ "filter_hint": "🔍 Filter by tool: `/crsi interpret <tool-name>` (e.g. `/crsi interpret Bash`)"
656
+ },
657
+ "crsi_red_team": {
658
+ "not_init": "Red-Team requires both ConstitutionLoader and PreFlightChecker to be initialized.",
659
+ "title": "## 🔴 CRSI Red-Team Report",
660
+ "score": "Overall Score: **{score}/100**",
661
+ "metric": "| Metric | Count |",
662
+ "metric_sep": "|--------|------|",
663
+ "total_attacks": "| Total attacks | {count} |",
664
+ "blocked": "| 🛡️ Correctly blocked | {count} |",
665
+ "passed_through": "| 🔴 Passed through (GAP) | {count} |",
666
+ "false_positives": "| ⚠️ False positives | {count} |",
667
+ "by_principle": "### By Principle",
668
+ "gaps_title": "### 🔴 Security Gaps (Should Have Been Blocked)",
669
+ "gap_tool": " Tool: `{tool}` → Params: `{params}`",
670
+ "blocked_title": "### 🛡️ Successfully Blocked",
671
+ "caught_by": "{principle}: {desc} → caught by **{caught}**",
672
+ "all_blocked": "🎉 **All attacks blocked!** The SIS immune system is fully operational.",
673
+ "good_coverage": "⚠️ Good coverage. Review the gaps above and add audit patterns to `ai-guardrails.yml`.",
674
+ "critical": "🔴 **Critical gaps detected.** Prioritize fixing the passed-through attacks above."
675
+ },
676
+ "dream": {
677
+ "not_init": "DreamEngine not initialized. Mipham Code >= v0.34.0 required.",
678
+ "no_history": "🌙 Auto-Dream has not run yet. Use `/dream` to trigger the first consolidation.",
679
+ "history_title": "## 🌙 Auto-Dream History",
680
+ "history_entry": "- **{time}**: {total} actions ({applied} auto-applied, {flagged} flagged for review)",
681
+ "done_title": "## 🌙 Auto-Dream Complete",
682
+ "integrate": "Memory integration: {before} → {after}",
683
+ "dedup": "- Deduplicated: {count} entries",
684
+ "contradiction": "- Contradictions: {count} (need human review)",
685
+ "merge": "- Merged: {count} groups",
686
+ "solidify": "- Fuzzy-marked: {count} entries (suggested to solidify)",
687
+ "prune": "- Pruned expired: {count} entries",
688
+ "review_hint": "⚠️ {count} items pending review. Use `/dream --aggressive` to auto-apply all.",
689
+ "clean": "✅ Memory is clean, nothing to consolidate."
690
+ },
691
+ "constitution": {
692
+ "not_init": "ConstitutionLoader not initialized. Mipham Code >= v0.34.0 required.",
693
+ "reload_title": "## ⚖️ Constitution Reloaded",
694
+ "version": "Version: **v{version}**",
695
+ "principles": "Principles: **{count}**",
696
+ "path": "Path: `{path}`",
697
+ "view_title": "## ⚖️ Mipham Constitution",
698
+ "view_summary": "Version: **v{version}** | Principles: **{count}** | Path: `{path}`",
699
+ "scope": " Scope: `{scope}`",
700
+ "tools": " Tools: {tools}",
701
+ "reload_hint": "🔧 `/constitution reload` — Reload after editing ai-guardrails.yml",
702
+ "edit_hint": "📝 Edit: `vi ~/.mipham/ai-guardrails.yml`",
703
+ "reset_hint": "🗑️ Reset: delete `~/.mipham/ai-guardrails.yml` then run `/constitution reload`",
704
+ "inspired": "*Inspired by Anthropic Constitutional AI. Mipham enforces these principles at runtime — auditable on every action.*"
705
+ },
706
+ "bug_report": {
707
+ "title": "## 🐛 Bug Report",
708
+ "hint": "> Copy the report below and paste it into your GitHub Issue.",
709
+ "env_title": "### Environment",
710
+ "mipham": "- **Mipham Code**: v{version}",
711
+ "session": "- **Session**: {id}",
712
+ "node": "- **Node**: {version}",
713
+ "platform": "- **Platform**: {platform} {arch}",
714
+ "os": "- **OS**: {os}",
715
+ "provider": "- **Provider**: {provider}",
716
+ "model": "- **Model**: {model}",
717
+ "sigs_title": "### Active Error Signatures",
718
+ "sig_entry": "- `{id}`: {pattern} ({category}, {occurrences}x, {pct}% success)",
719
+ "disabled_hooks": "### Disabled Hooks",
720
+ "hook_entry": "- `{key}`: {failures} failures, disabled at {time}",
721
+ "last_dream": "### Last Auto-Dream",
722
+ "ran_at": "- **Ran at**: {time}",
723
+ "actions": "- **Actions**: {total} ({applied} applied)",
724
+ "steps_title": "### Steps to Reproduce",
725
+ "expected": "### Expected Behavior",
726
+ "actual": "### Actual Behavior",
727
+ "generated": "🤖 Generated with Mipham Code v{version}"
728
+ },
729
+ "changelog": {
730
+ "title": "## 📋 Changelog",
731
+ "no_tags": "No version tags found.",
732
+ "current": "Current: **v{version}**",
733
+ "current_marker": " ← current",
734
+ "release_only": " _(release tag only)_",
735
+ "details_unavailable": " _(details unavailable)_",
736
+ "no_git": "Unable to read git history. Make sure you are in the Mipham Code repository.",
737
+ "full_history": "Full history: `git log --oneline`"
615
738
  }
616
739
  },
617
740
  "ui": {