@miphamai/cli 0.33.2 → 0.34.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/src/core/constitution-loader.ts +353 -0
- package/src/core/dream-engine.ts +552 -0
- package/src/core/emotion-detector.ts +257 -0
- package/src/core/engine.ts +122 -1
- package/src/core/red-team.ts +271 -0
- package/src/core/self-critique.ts +235 -0
- package/src/ui/app.tsx +22 -1
- package/src/ui/commands.ts +669 -31
package/src/ui/commands.ts
CHANGED
|
@@ -929,6 +929,553 @@ const sisCleanupCmd: CommandHandler = async (ctx) => {
|
|
|
929
929
|
return { content: lines.join('\n') }
|
|
930
930
|
}
|
|
931
931
|
|
|
932
|
+
// ═══════════════════════════════════════════════════════════════
|
|
933
|
+
// CRSI Critique — Self-Critique Hook (RLAIF)
|
|
934
|
+
// ═══════════════════════════════════════════════════════════════
|
|
935
|
+
|
|
936
|
+
const crsiCritiqueCmd: CommandHandler = (ctx, args) => {
|
|
937
|
+
const t = resolveT(ctx)
|
|
938
|
+
const engine = ctx.engine
|
|
939
|
+
const sc = engine.getSelfCritique?.()
|
|
940
|
+
if (!sc) {
|
|
941
|
+
return { content: 'SelfCritique 未初始化。请确认 Mipham Code 版本 >= v0.34.0。' }
|
|
942
|
+
}
|
|
943
|
+
|
|
944
|
+
const sub = args[0]?.toLowerCase()
|
|
945
|
+
const config = sc.getConfig()
|
|
946
|
+
|
|
947
|
+
if (sub === 'on' || sub === 'enable') {
|
|
948
|
+
sc.setEnabled(true)
|
|
949
|
+
return {
|
|
950
|
+
content: [
|
|
951
|
+
'## 🔍 Self-Critique: ON',
|
|
952
|
+
'',
|
|
953
|
+
`Model: **${config.model || 'auto (fastest available)'}**`,
|
|
954
|
+
`Threshold: **${(config.threshold * 100).toFixed(0)}%**`,
|
|
955
|
+
`Target tools: **${config.targetTools.join(', ')}**`,
|
|
956
|
+
'',
|
|
957
|
+
'The AI will now critique its own tool calls before execution.',
|
|
958
|
+
'Safe + correct + necessary checks run before every targeted tool call.',
|
|
959
|
+
'',
|
|
960
|
+
'💡 `/crsi critique off` to disable | `/crsi critique status` for current config',
|
|
961
|
+
].join('\n'),
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
|
|
965
|
+
if (sub === 'off' || sub === 'disable') {
|
|
966
|
+
sc.setEnabled(false)
|
|
967
|
+
return {
|
|
968
|
+
content: '## 🔍 Self-Critique: OFF\n\nTool calls will execute without pre-flight critique.',
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
|
|
972
|
+
// status (default)
|
|
973
|
+
const lines: string[] = [
|
|
974
|
+
'## 🔍 Self-Critique Status',
|
|
975
|
+
'',
|
|
976
|
+
`State: **${config.enabled ? '🟢 Enabled' : '⚫ Disabled'}**`,
|
|
977
|
+
`Model: **${config.model || 'auto (fastest available)'}**`,
|
|
978
|
+
`Threshold: **${(config.threshold * 100).toFixed(0)}%** (below this → correction or block)`,
|
|
979
|
+
`Target tools: **${config.targetTools.length > 0 ? config.targetTools.join(', ') : 'all'}**`,
|
|
980
|
+
`Timeout: **${config.timeoutMs}ms**`,
|
|
981
|
+
'',
|
|
982
|
+
'──',
|
|
983
|
+
'🔍 `/crsi critique on` — Enable self-critique',
|
|
984
|
+
'🔍 `/crsi critique off` — Disable self-critique',
|
|
985
|
+
'',
|
|
986
|
+
'*Inspired by Anthropic RLAIF — AI critiques its own actions before execution.*',
|
|
987
|
+
]
|
|
988
|
+
|
|
989
|
+
return { content: lines.join('\n') }
|
|
990
|
+
}
|
|
991
|
+
|
|
992
|
+
// ═══════════════════════════════════════════════════════════════
|
|
993
|
+
// CRSI Interpret — Tool-Call Behavior Dashboard
|
|
994
|
+
// ═══════════════════════════════════════════════════════════════
|
|
995
|
+
|
|
996
|
+
const crsiInterpretCmd: CommandHandler = (ctx, args) => {
|
|
997
|
+
const t = resolveT(ctx)
|
|
998
|
+
const engine = ctx.engine
|
|
999
|
+
const toolFilter = args[0]?.toLowerCase()
|
|
1000
|
+
|
|
1001
|
+
const lines: string[] = ['## 🧠 CRSI Tool-Call Interpretability', '']
|
|
1002
|
+
|
|
1003
|
+
// ── Error Signature Analysis ──
|
|
1004
|
+
const db = engine.getErrorSignatureDB?.()
|
|
1005
|
+
if (db) {
|
|
1006
|
+
const sigs = toolFilter
|
|
1007
|
+
? db.getActive().filter((s) => s.toolName.toLowerCase() === toolFilter)
|
|
1008
|
+
: db.getActive()
|
|
1009
|
+
|
|
1010
|
+
if (sigs.length === 0) {
|
|
1011
|
+
lines.push(
|
|
1012
|
+
toolFilter
|
|
1013
|
+
? `No error signatures for tool \`${toolFilter}\`. CRSI immune memory is clean for this tool.`
|
|
1014
|
+
: 'No active error signatures. CRSI immune memory is clean.',
|
|
1015
|
+
)
|
|
1016
|
+
} else {
|
|
1017
|
+
lines.push(`### 🛡️ Error Signatures${toolFilter ? ` — \`${toolFilter}\`` : ''}`, '')
|
|
1018
|
+
for (const sig of sigs.slice(0, 15)) {
|
|
1019
|
+
const bar =
|
|
1020
|
+
'█'.repeat(Math.round(sig.successRate * 10)) +
|
|
1021
|
+
'░'.repeat(10 - Math.round(sig.successRate * 10))
|
|
1022
|
+
lines.push(`- **${sig.toolName}**: \`${sig.pattern.slice(0, 60)}\``)
|
|
1023
|
+
lines.push(
|
|
1024
|
+
` Success: ${bar} ${Math.round(sig.successRate * 100)}% | ${sig.occurrences}x | ${sig.fixStrategy}`,
|
|
1025
|
+
)
|
|
1026
|
+
lines.push(` Fix: \`${sig.fixAction.slice(0, 80)}\``)
|
|
1027
|
+
lines.push('')
|
|
1028
|
+
}
|
|
1029
|
+
if (sigs.length > 15) lines.push(`... and ${sigs.length - 15} more signatures`, '')
|
|
1030
|
+
}
|
|
1031
|
+
}
|
|
1032
|
+
|
|
1033
|
+
// ── CRSI Reflection Summary ──
|
|
1034
|
+
const autoMemory = engine.getAutoMemory?.()
|
|
1035
|
+
if (autoMemory) {
|
|
1036
|
+
const count = autoMemory.sessionReflectionCount ?? 0
|
|
1037
|
+
if (count > 0) {
|
|
1038
|
+
lines.push('### 📊 CRSI Reflection Summary', '')
|
|
1039
|
+
lines.push(`Turn reflections analyzed: **${count}**`)
|
|
1040
|
+
lines.push('')
|
|
1041
|
+
}
|
|
1042
|
+
}
|
|
1043
|
+
|
|
1044
|
+
// ── Usage Tracker by Tool ──
|
|
1045
|
+
const usage = engine.getUsageTracker?.()
|
|
1046
|
+
if (usage) {
|
|
1047
|
+
const summary = usage.getSummary()
|
|
1048
|
+
lines.push('### 💰 Token Usage', '')
|
|
1049
|
+
lines.push(`API input tokens: ${summary.apiInputTokens.toLocaleString()}`)
|
|
1050
|
+
lines.push(`API output tokens: ${summary.apiOutputTokens.toLocaleString()}`)
|
|
1051
|
+
lines.push(`Estimated tokens: ${summary.estimatedTokens.toLocaleString()}`)
|
|
1052
|
+
lines.push('')
|
|
1053
|
+
}
|
|
1054
|
+
|
|
1055
|
+
// ── System Health ──
|
|
1056
|
+
const meta = engine.getMetaRuleEngine?.()
|
|
1057
|
+
if (meta) {
|
|
1058
|
+
try {
|
|
1059
|
+
const analysis = meta.analyze()
|
|
1060
|
+
if (analysis.systemHealth) {
|
|
1061
|
+
const h = analysis.systemHealth
|
|
1062
|
+
const bar = '█'.repeat(Math.round(h.score / 10)) + '░'.repeat(10 - Math.round(h.score / 10))
|
|
1063
|
+
lines.push('### 🏥 System Health', '')
|
|
1064
|
+
lines.push(`Overall: ${bar} **${h.score}/100**`)
|
|
1065
|
+
lines.push(`Assessment: ${h.assessment}`)
|
|
1066
|
+
if (h.components) {
|
|
1067
|
+
lines.push('')
|
|
1068
|
+
for (const [comp, score] of Object.entries(h.components)) {
|
|
1069
|
+
const cbar =
|
|
1070
|
+
'▮'.repeat(Math.round((score as number) / 10)) +
|
|
1071
|
+
'▯'.repeat(10 - Math.round((score as number) / 10))
|
|
1072
|
+
lines.push(` ${comp.padEnd(20)} ${cbar} ${score}/100`)
|
|
1073
|
+
}
|
|
1074
|
+
}
|
|
1075
|
+
lines.push('')
|
|
1076
|
+
}
|
|
1077
|
+
} catch {
|
|
1078
|
+
// Meta analysis unavailable
|
|
1079
|
+
}
|
|
1080
|
+
}
|
|
1081
|
+
|
|
1082
|
+
// ── Constitution Health ──
|
|
1083
|
+
const constitution = engine.getConstitutionLoader?.()
|
|
1084
|
+
if (constitution) {
|
|
1085
|
+
const c = constitution.load()
|
|
1086
|
+
const blocks = c.principles.filter((p) => p.enforce === 'block').length
|
|
1087
|
+
const warns = c.principles.filter((p) => p.enforce === 'warn').length
|
|
1088
|
+
const autos = c.principles.filter((p) => p.enforce === 'auto').length
|
|
1089
|
+
lines.push('### ⚖️ Constitution', '')
|
|
1090
|
+
lines.push(
|
|
1091
|
+
`Principles: **${c.principles.length}** (🚫 ${blocks} block | ⚠️ ${warns} warn | 🔄 ${autos} auto)`,
|
|
1092
|
+
)
|
|
1093
|
+
lines.push(`Version: v${c.version}`)
|
|
1094
|
+
lines.push('')
|
|
1095
|
+
}
|
|
1096
|
+
|
|
1097
|
+
if (lines.length <= 2) {
|
|
1098
|
+
lines.push('_Start using tools to populate CRSI interpretability data._')
|
|
1099
|
+
}
|
|
1100
|
+
|
|
1101
|
+
lines.push('──')
|
|
1102
|
+
lines.push('🔍 Filter by tool: `/crsi interpret <tool-name>` (e.g. `/crsi interpret Bash`)')
|
|
1103
|
+
|
|
1104
|
+
return { content: lines.join('\n') }
|
|
1105
|
+
}
|
|
1106
|
+
|
|
1107
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1108
|
+
// CRSI Red-Team — Adversarial Self-Testing
|
|
1109
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1110
|
+
|
|
1111
|
+
const crsiRedTeamCmd: CommandHandler = async (ctx) => {
|
|
1112
|
+
const t = resolveT(ctx)
|
|
1113
|
+
const engine = ctx.engine
|
|
1114
|
+
|
|
1115
|
+
const constitution = engine.getConstitutionLoader?.()
|
|
1116
|
+
const preflight = engine.getPreFlightChecker?.()
|
|
1117
|
+
|
|
1118
|
+
if (!constitution || !preflight) {
|
|
1119
|
+
return { content: 'Red-Team 需要 ConstitutionLoader 和 PreFlightChecker 均已初始化。' }
|
|
1120
|
+
}
|
|
1121
|
+
|
|
1122
|
+
const { RedTeam: RT } = await import('../../src/core/red-team.js')
|
|
1123
|
+
const redTeam = new RT()
|
|
1124
|
+
const report = redTeam.run(constitution, preflight)
|
|
1125
|
+
|
|
1126
|
+
const lines: string[] = [
|
|
1127
|
+
'## 🔴 CRSI Red-Team Report',
|
|
1128
|
+
'',
|
|
1129
|
+
`Overall Score: **${report.score}/100**`,
|
|
1130
|
+
'',
|
|
1131
|
+
`| Metric | Count |`,
|
|
1132
|
+
`|--------|------|`,
|
|
1133
|
+
`| Total attacks | ${report.total} |`,
|
|
1134
|
+
`| 🛡️ Correctly blocked | ${report.blocked} |`,
|
|
1135
|
+
`| 🔴 Passed through (GAP) | ${report.passedThrough} |`,
|
|
1136
|
+
`| ⚠️ False positives | ${report.falsePositives} |`,
|
|
1137
|
+
'',
|
|
1138
|
+
]
|
|
1139
|
+
|
|
1140
|
+
// Per-principle breakdown
|
|
1141
|
+
lines.push('### By Principle', '')
|
|
1142
|
+
for (const [pid, stats] of Object.entries(report.byPrinciple)) {
|
|
1143
|
+
const pct = stats.total > 0 ? Math.round((stats.blocked / stats.total) * 100) : 100
|
|
1144
|
+
const bar = '█'.repeat(Math.round(pct / 10)) + '░'.repeat(10 - Math.round(pct / 10))
|
|
1145
|
+
const icon = pct === 100 ? '✅' : pct >= 80 ? '⚠️' : '🔴'
|
|
1146
|
+
lines.push(`- ${icon} **${pid}**: ${bar} ${pct}% (${stats.blocked}/${stats.total})`)
|
|
1147
|
+
}
|
|
1148
|
+
lines.push('')
|
|
1149
|
+
|
|
1150
|
+
// Detail: passed-through attacks
|
|
1151
|
+
const gaps = report.results.filter((r) => r.attack.shouldBlock && !r.blocked)
|
|
1152
|
+
if (gaps.length > 0) {
|
|
1153
|
+
lines.push('### 🔴 Security Gaps (Should Have Been Blocked)', '')
|
|
1154
|
+
for (const g of gaps) {
|
|
1155
|
+
lines.push(`- **${g.attack.principleId}**: ${g.attack.description}`)
|
|
1156
|
+
lines.push(
|
|
1157
|
+
` Tool: \`${g.attack.toolName}\` → Params: \`${JSON.stringify(g.attack.params).slice(0, 80)}\``,
|
|
1158
|
+
)
|
|
1159
|
+
}
|
|
1160
|
+
lines.push('')
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
// Detail: caught attacks
|
|
1164
|
+
const caught = report.results.filter((r) => r.blocked)
|
|
1165
|
+
if (caught.length > 0) {
|
|
1166
|
+
lines.push('### 🛡️ Successfully Blocked', '')
|
|
1167
|
+
for (const c of caught) {
|
|
1168
|
+
lines.push(`- ${c.attack.principleId}: ${c.attack.description} → caught by **${c.caughtBy}**`)
|
|
1169
|
+
}
|
|
1170
|
+
lines.push('')
|
|
1171
|
+
}
|
|
1172
|
+
|
|
1173
|
+
if (report.score === 100) {
|
|
1174
|
+
lines.push('🎉 **All attacks blocked!** The SIS immune system is fully operational.')
|
|
1175
|
+
} else if (report.score >= 80) {
|
|
1176
|
+
lines.push(
|
|
1177
|
+
'⚠️ Good coverage. Review the gaps above and add audit patterns to `ai-guardrails.yml`.',
|
|
1178
|
+
)
|
|
1179
|
+
} else {
|
|
1180
|
+
lines.push('🔴 **Critical gaps detected.** Prioritize fixing the passed-through attacks above.')
|
|
1181
|
+
}
|
|
1182
|
+
|
|
1183
|
+
return { content: lines.join('\n') }
|
|
1184
|
+
}
|
|
1185
|
+
|
|
1186
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1187
|
+
// Auto-Dream: Background Memory Consolidation
|
|
1188
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1189
|
+
|
|
1190
|
+
const dreamCmd: CommandHandler = async (ctx, args) => {
|
|
1191
|
+
const t = resolveT(ctx)
|
|
1192
|
+
const aggressive = args.includes('--aggressive') || args.includes('-a')
|
|
1193
|
+
const status = args.includes('--status') || args.includes('-s')
|
|
1194
|
+
|
|
1195
|
+
const engine = ctx.engine.getDreamEngine?.()
|
|
1196
|
+
if (!engine) {
|
|
1197
|
+
return { content: 'DreamEngine 未初始化。请确认 Mipham Code 版本 >= v0.34.0。' }
|
|
1198
|
+
}
|
|
1199
|
+
|
|
1200
|
+
if (status) {
|
|
1201
|
+
const history = engine.getDreamHistory()
|
|
1202
|
+
if (history.length === 0) {
|
|
1203
|
+
return { content: '🌙 尚未运行过 Auto-Dream。使用 `/dream` 手动触发首次梦境整合。' }
|
|
1204
|
+
}
|
|
1205
|
+
const lines: string[] = ['## 🌙 Auto-Dream 历史', '']
|
|
1206
|
+
for (const entry of history.slice(0, 5)) {
|
|
1207
|
+
const ts = new Date(entry.timestamp).toLocaleString('zh-CN')
|
|
1208
|
+
const total = entry.actions.length
|
|
1209
|
+
const applied = entry.actions.filter((a) => a.autoApplied).length
|
|
1210
|
+
const flagged = entry.actions.filter((a) => !a.autoApplied).length
|
|
1211
|
+
lines.push(`- **${ts}**: ${total} 行动 (${applied} 自动应用, ${flagged} 标记审查)`)
|
|
1212
|
+
}
|
|
1213
|
+
return { content: lines.join('\n') }
|
|
1214
|
+
}
|
|
1215
|
+
|
|
1216
|
+
const report = engine.dream({ aggressive })
|
|
1217
|
+
const lines: string[] = [
|
|
1218
|
+
'## 🌙 Auto-Dream 完成',
|
|
1219
|
+
'',
|
|
1220
|
+
`记忆整合: ${report.beforeCount} → ${report.afterCount}`,
|
|
1221
|
+
...(report.phases.deduplicated > 0 ? [`- 去重: ${report.phases.deduplicated} 条`] : []),
|
|
1222
|
+
...(report.phases.contradictionsFound > 0
|
|
1223
|
+
? [`- 矛盾冲突: ${report.phases.contradictionsFound} 处 (需人工裁决)`]
|
|
1224
|
+
: []),
|
|
1225
|
+
...(report.phases.merged > 0 ? [`- 合并: ${report.phases.merged} 组`] : []),
|
|
1226
|
+
...(report.phases.solidified > 0
|
|
1227
|
+
? [`- 模糊标记: ${report.phases.solidified} 条 (建议固化)`]
|
|
1228
|
+
: []),
|
|
1229
|
+
...(report.phases.pruned > 0 ? [`- 清理过期: ${report.phases.pruned} 条`] : []),
|
|
1230
|
+
'',
|
|
1231
|
+
]
|
|
1232
|
+
|
|
1233
|
+
const flagged = report.actions.filter((a) => !a.autoApplied)
|
|
1234
|
+
if (flagged.length > 0 && !aggressive) {
|
|
1235
|
+
lines.push(`⚠️ ${flagged.length} 项待审查。使用 \`/dream --aggressive\` 自动应用所有操作。`)
|
|
1236
|
+
lines.push('')
|
|
1237
|
+
for (const action of flagged) {
|
|
1238
|
+
lines.push(`- [ ] **${action.type}**: ${action.description}`)
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1242
|
+
if (report.actions.length === 0) {
|
|
1243
|
+
lines.push('✅ 记忆干净,无需整理。')
|
|
1244
|
+
}
|
|
1245
|
+
|
|
1246
|
+
return { content: lines.join('\n') }
|
|
1247
|
+
}
|
|
1248
|
+
|
|
1249
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1250
|
+
// Constitution
|
|
1251
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1252
|
+
|
|
1253
|
+
const constitutionCmd: CommandHandler = (ctx, args) => {
|
|
1254
|
+
const t = resolveT(ctx)
|
|
1255
|
+
const engine = ctx.engine
|
|
1256
|
+
const constitution = engine.getConstitutionLoader?.()
|
|
1257
|
+
if (!constitution) {
|
|
1258
|
+
return { content: 'ConstitutionLoader 未初始化。请确认 Mipham Code 版本 >= v0.34.0。' }
|
|
1259
|
+
}
|
|
1260
|
+
|
|
1261
|
+
const subCmd = args[0]?.toLowerCase()
|
|
1262
|
+
|
|
1263
|
+
// /constitution reload
|
|
1264
|
+
if (subCmd === 'reload') {
|
|
1265
|
+
const c = constitution.reload()
|
|
1266
|
+
return {
|
|
1267
|
+
content: [
|
|
1268
|
+
'## ⚖️ Constitution Reloaded',
|
|
1269
|
+
'',
|
|
1270
|
+
`Version: **v${c.version}**`,
|
|
1271
|
+
`Principles: **${c.principles.length}**`,
|
|
1272
|
+
`Path: \`${constitution.getPath()}\``,
|
|
1273
|
+
'',
|
|
1274
|
+
c.principles.map((p) => `- **${p.id}** [${p.enforce}]: ${p.text}`).join('\n'),
|
|
1275
|
+
].join('\n'),
|
|
1276
|
+
}
|
|
1277
|
+
}
|
|
1278
|
+
|
|
1279
|
+
// /constitution view (default)
|
|
1280
|
+
const c = constitution.load()
|
|
1281
|
+
const lines: string[] = [
|
|
1282
|
+
'## ⚖️ Mipham Constitution',
|
|
1283
|
+
'',
|
|
1284
|
+
`Version: **v${c.version}** | Principles: **${c.principles.length}** | Path: \`${constitution.getPath()}\``,
|
|
1285
|
+
'',
|
|
1286
|
+
'---',
|
|
1287
|
+
'',
|
|
1288
|
+
]
|
|
1289
|
+
|
|
1290
|
+
for (const p of c.principles) {
|
|
1291
|
+
const icon = p.enforce === 'block' ? '🚫' : p.enforce === 'warn' ? '⚠️' : '🔄'
|
|
1292
|
+
lines.push(`### ${icon} ${p.id} [${p.enforce}]`)
|
|
1293
|
+
lines.push('')
|
|
1294
|
+
lines.push(p.text)
|
|
1295
|
+
if (p.rationale) lines.push(` *${p.rationale}*`)
|
|
1296
|
+
if (p.scope) lines.push(` Scope: \`${p.scope}\``)
|
|
1297
|
+
if (p.tools) lines.push(` Tools: ${p.tools.join(', ')}`)
|
|
1298
|
+
lines.push('')
|
|
1299
|
+
}
|
|
1300
|
+
|
|
1301
|
+
lines.push('---')
|
|
1302
|
+
lines.push('')
|
|
1303
|
+
lines.push('🔧 `/constitution reload` — 重新加载(修改 ai-guardrails.yml 后使用)')
|
|
1304
|
+
lines.push('📝 编辑: `vi ~/.mipham/ai-guardrails.yml`')
|
|
1305
|
+
lines.push('🗑️ 重置: 删除 `~/.mipham/ai-guardrails.yml` 后执行 `/constitution reload`')
|
|
1306
|
+
lines.push('')
|
|
1307
|
+
lines.push(
|
|
1308
|
+
'*Inspired by Anthropic Constitutional AI. Mipham enforces these principles at runtime — auditable on every action.*',
|
|
1309
|
+
)
|
|
1310
|
+
|
|
1311
|
+
return { content: lines.join('\n') }
|
|
1312
|
+
}
|
|
1313
|
+
|
|
1314
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1315
|
+
// Bug Report
|
|
1316
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1317
|
+
|
|
1318
|
+
const bugReportCmd: CommandHandler = async (ctx) => {
|
|
1319
|
+
const t = resolveT(ctx)
|
|
1320
|
+
const engine = ctx.engine
|
|
1321
|
+
const lines: string[] = [
|
|
1322
|
+
'## 🐛 Bug Report',
|
|
1323
|
+
'',
|
|
1324
|
+
'> Copy the report below and paste it into your GitHub Issue.',
|
|
1325
|
+
'',
|
|
1326
|
+
'---',
|
|
1327
|
+
'',
|
|
1328
|
+
]
|
|
1329
|
+
|
|
1330
|
+
// ── Version & Runtime ──
|
|
1331
|
+
lines.push('### Environment', '')
|
|
1332
|
+
lines.push(`- **Mipham Code**: v${ctx.version}`)
|
|
1333
|
+
lines.push(`- **Session**: ${ctx.sessionId}`)
|
|
1334
|
+
lines.push(`- **Node**: ${process.version}`)
|
|
1335
|
+
lines.push(`- **Platform**: ${process.platform} ${process.arch}`)
|
|
1336
|
+
lines.push(
|
|
1337
|
+
`- **OS**: ${process.platform === 'darwin' ? 'macOS' : process.platform === 'linux' ? 'Linux' : 'Windows'}`,
|
|
1338
|
+
)
|
|
1339
|
+
lines.push(`- **Provider**: ${ctx.providerId}`)
|
|
1340
|
+
lines.push(`- **Model**: ${ctx.modelId}`)
|
|
1341
|
+
lines.push('')
|
|
1342
|
+
|
|
1343
|
+
// ── SIS errors ──
|
|
1344
|
+
try {
|
|
1345
|
+
const db = engine.getErrorSignatureDB?.()
|
|
1346
|
+
if (db) {
|
|
1347
|
+
const active = db.getActive()
|
|
1348
|
+
if (active.length > 0) {
|
|
1349
|
+
lines.push('### Active Error Signatures', '')
|
|
1350
|
+
for (const sig of active.slice(0, 10)) {
|
|
1351
|
+
lines.push(
|
|
1352
|
+
`- \`${sig.id}\`: ${sig.pattern} (${sig.category}, ${sig.occurrences}x, ${(sig.successRate * 100).toFixed(0)}% success)`,
|
|
1353
|
+
)
|
|
1354
|
+
}
|
|
1355
|
+
lines.push('')
|
|
1356
|
+
}
|
|
1357
|
+
}
|
|
1358
|
+
} catch {
|
|
1359
|
+
// SIS unavailable
|
|
1360
|
+
}
|
|
1361
|
+
|
|
1362
|
+
// ── Hook health ──
|
|
1363
|
+
try {
|
|
1364
|
+
const hookEngine = engine.getHookEngine?.()
|
|
1365
|
+
if (hookEngine) {
|
|
1366
|
+
const health = hookEngine.getHookHealth()
|
|
1367
|
+
const disabled = health.filter((h) => h.health.disabled)
|
|
1368
|
+
if (disabled.length > 0) {
|
|
1369
|
+
lines.push('### Disabled Hooks', '')
|
|
1370
|
+
for (const entry of disabled) {
|
|
1371
|
+
lines.push(
|
|
1372
|
+
`- \`${entry.key}\`: ${entry.health.failures} failures, disabled at ${new Date(entry.health.disabledAt).toISOString()}`,
|
|
1373
|
+
)
|
|
1374
|
+
}
|
|
1375
|
+
lines.push('')
|
|
1376
|
+
}
|
|
1377
|
+
}
|
|
1378
|
+
} catch {
|
|
1379
|
+
// Hook health unavailable
|
|
1380
|
+
}
|
|
1381
|
+
|
|
1382
|
+
// ── Recent dream log ──
|
|
1383
|
+
try {
|
|
1384
|
+
const dreamEngine = engine.getDreamEngine?.()
|
|
1385
|
+
if (dreamEngine) {
|
|
1386
|
+
const history = dreamEngine.getDreamHistory()
|
|
1387
|
+
if (history.length > 0) {
|
|
1388
|
+
const last = history[0]!
|
|
1389
|
+
lines.push('### Last Auto-Dream', '')
|
|
1390
|
+
lines.push(`- **Ran at**: ${last.timestamp}`)
|
|
1391
|
+
lines.push(
|
|
1392
|
+
`- **Actions**: ${last.actions.length} (${last.actions.filter((a: { autoApplied: boolean }) => a.autoApplied).length} applied)`,
|
|
1393
|
+
)
|
|
1394
|
+
lines.push('')
|
|
1395
|
+
}
|
|
1396
|
+
}
|
|
1397
|
+
} catch {
|
|
1398
|
+
// Dream engine unavailable
|
|
1399
|
+
}
|
|
1400
|
+
|
|
1401
|
+
// ── Steps to reproduce (template) ──
|
|
1402
|
+
lines.push('### Steps to Reproduce', '')
|
|
1403
|
+
lines.push('1. ')
|
|
1404
|
+
lines.push('2. ')
|
|
1405
|
+
lines.push('3. ')
|
|
1406
|
+
lines.push('')
|
|
1407
|
+
lines.push('### Expected Behavior', '')
|
|
1408
|
+
lines.push('')
|
|
1409
|
+
lines.push('### Actual Behavior', '')
|
|
1410
|
+
lines.push('')
|
|
1411
|
+
lines.push('---')
|
|
1412
|
+
lines.push(`🤖 Generated with Mipham Code v${ctx.version}`)
|
|
1413
|
+
|
|
1414
|
+
return { content: lines.join('\n'), copyContent: lines.join('\n') }
|
|
1415
|
+
}
|
|
1416
|
+
|
|
1417
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1418
|
+
// Feedback
|
|
1419
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1420
|
+
// Changelog
|
|
1421
|
+
// ═══════════════════════════════════════════════════════════════
|
|
1422
|
+
|
|
1423
|
+
const changelogCmd: CommandHandler = async (ctx) => {
|
|
1424
|
+
const t = resolveT(ctx)
|
|
1425
|
+
const lines: string[] = ['## 📋 Changelog', '']
|
|
1426
|
+
|
|
1427
|
+
try {
|
|
1428
|
+
const { execSync } = await import('node:child_process')
|
|
1429
|
+
const tags = execSync('git tag --sort=-creatordate', { encoding: 'utf-8', timeout: 5000 })
|
|
1430
|
+
.trim()
|
|
1431
|
+
.split('\n')
|
|
1432
|
+
.filter(Boolean)
|
|
1433
|
+
.slice(0, 15)
|
|
1434
|
+
|
|
1435
|
+
if (tags.length === 0) {
|
|
1436
|
+
lines.push('No version tags found.')
|
|
1437
|
+
return { content: lines.join('\n') }
|
|
1438
|
+
}
|
|
1439
|
+
|
|
1440
|
+
lines.push(`Current: **v${ctx.version}**`, '')
|
|
1441
|
+
|
|
1442
|
+
for (let i = 0; i < tags.length; i++) {
|
|
1443
|
+
const tag = tags[i]!
|
|
1444
|
+
const nextTag = tags[i + 1]
|
|
1445
|
+
try {
|
|
1446
|
+
const range = nextTag ? `${nextTag}..${tag}` : tag
|
|
1447
|
+
const log = execSync(
|
|
1448
|
+
`git log --oneline --no-merges ${range} --format="%s" 2>/dev/null | head -8`,
|
|
1449
|
+
{ encoding: 'utf-8', timeout: 5000 },
|
|
1450
|
+
)
|
|
1451
|
+
.trim()
|
|
1452
|
+
.split('\n')
|
|
1453
|
+
.filter(Boolean)
|
|
1454
|
+
|
|
1455
|
+
const isCurrent = tag === `v${ctx.version}`
|
|
1456
|
+
lines.push(`### ${tag}${isCurrent ? ' ← current' : ''}`, '')
|
|
1457
|
+
if (log.length > 0) {
|
|
1458
|
+
for (const entry of log) {
|
|
1459
|
+
lines.push(`- ${entry}`)
|
|
1460
|
+
}
|
|
1461
|
+
} else {
|
|
1462
|
+
lines.push(' _(release tag only)_')
|
|
1463
|
+
}
|
|
1464
|
+
lines.push('')
|
|
1465
|
+
} catch {
|
|
1466
|
+
lines.push(`### ${tag}`, '', ' _(details unavailable)_', '')
|
|
1467
|
+
}
|
|
1468
|
+
}
|
|
1469
|
+
} catch {
|
|
1470
|
+
lines.push('Unable to read git history. Make sure you are in the Mipham Code repository.')
|
|
1471
|
+
}
|
|
1472
|
+
|
|
1473
|
+
lines.push('---')
|
|
1474
|
+
lines.push(`Full history: \`git log --oneline\``)
|
|
1475
|
+
|
|
1476
|
+
return { content: lines.join('\n') }
|
|
1477
|
+
}
|
|
1478
|
+
|
|
932
1479
|
// ═══════════════════════════════════════════════════════════════
|
|
933
1480
|
// Workflow
|
|
934
1481
|
// ═══════════════════════════════════════════════════════════════
|
|
@@ -2147,25 +2694,69 @@ const statsCmd: CommandHandler = (ctx) => {
|
|
|
2147
2694
|
const assistantMsgs = msgs.filter((m) => m.role === 'assistant').length
|
|
2148
2695
|
const systemMsgs = msgs.filter((m) => m.role === 'system').length
|
|
2149
2696
|
|
|
2150
|
-
|
|
2151
|
-
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2158
|
-
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2164
|
-
|
|
2165
|
-
|
|
2166
|
-
|
|
2167
|
-
|
|
2697
|
+
const base = [
|
|
2698
|
+
t('commands.stats.title'),
|
|
2699
|
+
'',
|
|
2700
|
+
t('commands.stats.messages', {
|
|
2701
|
+
total: String(msgs.length),
|
|
2702
|
+
user: String(userMsgs),
|
|
2703
|
+
assistant: String(assistantMsgs),
|
|
2704
|
+
system: String(systemMsgs),
|
|
2705
|
+
}),
|
|
2706
|
+
t('commands.stats.tokens', { tokens: tokens.toLocaleString() }),
|
|
2707
|
+
t('commands.stats.tools', { count: String(tools.size) }),
|
|
2708
|
+
t('commands.stats.provider', { provider: ctx.providerId }),
|
|
2709
|
+
t('commands.stats.model', { model: ctx.modelId }),
|
|
2710
|
+
t('commands.stats.permission', { permission: ctx.config.permission }),
|
|
2711
|
+
'',
|
|
2712
|
+
t('commands.stats.usage', { pct: ((tokens / 200_000) * 100).toFixed(1) }),
|
|
2713
|
+
]
|
|
2714
|
+
|
|
2715
|
+
// ── CRSI & SIS extensions ──
|
|
2716
|
+
const engine = ctx.engine
|
|
2717
|
+
const extras: string[] = []
|
|
2718
|
+
|
|
2719
|
+
try {
|
|
2720
|
+
const db = engine.getErrorSignatureDB?.()
|
|
2721
|
+
if (db) {
|
|
2722
|
+
const s = db.getStats()
|
|
2723
|
+
if (s.total > 0) {
|
|
2724
|
+
extras.push(
|
|
2725
|
+
'',
|
|
2726
|
+
'── SIS Self-Immune ──',
|
|
2727
|
+
`Signatures: ${s.total} total, ${s.active} active | Success rate: ${(s.avgSuccessRate * 100).toFixed(0)}%`,
|
|
2728
|
+
)
|
|
2729
|
+
}
|
|
2730
|
+
}
|
|
2731
|
+
} catch {
|
|
2732
|
+
/* SIS not initialized */
|
|
2168
2733
|
}
|
|
2734
|
+
|
|
2735
|
+
try {
|
|
2736
|
+
const autoMemory = engine.getAutoMemory?.()
|
|
2737
|
+
if (autoMemory) {
|
|
2738
|
+
const count = autoMemory.sessionReflectionCount ?? 0
|
|
2739
|
+
if (count > 0) extras.push(`CRSI Reflections: ${count} turn(s) analyzed`)
|
|
2740
|
+
}
|
|
2741
|
+
} catch {
|
|
2742
|
+
/* CRSI not initialized */
|
|
2743
|
+
}
|
|
2744
|
+
|
|
2745
|
+
try {
|
|
2746
|
+
const meta = engine.getMetaRuleEngine?.()
|
|
2747
|
+
if (meta) {
|
|
2748
|
+
const analysis = meta.analyze()
|
|
2749
|
+
if (analysis.systemHealth) {
|
|
2750
|
+
extras.push(
|
|
2751
|
+
`System Health: ${analysis.systemHealth.score}/100 — ${analysis.systemHealth.assessment}`,
|
|
2752
|
+
)
|
|
2753
|
+
}
|
|
2754
|
+
}
|
|
2755
|
+
} catch {
|
|
2756
|
+
/* Meta not initialized */
|
|
2757
|
+
}
|
|
2758
|
+
|
|
2759
|
+
return { content: [...base, ...extras].join('\n') }
|
|
2169
2760
|
}
|
|
2170
2761
|
|
|
2171
2762
|
const summaryCmd: CommandHandler = (ctx) => {
|
|
@@ -3157,31 +3748,57 @@ To switch providers without clearing keys, use /switch <provider> <model>.`,
|
|
|
3157
3748
|
// Phase 4 — Feedback
|
|
3158
3749
|
// ═══════════════════════════════════════════════════════════════
|
|
3159
3750
|
|
|
3160
|
-
const feedbackCmd: CommandHandler = (ctx, args) => {
|
|
3751
|
+
const feedbackCmd: CommandHandler = async (ctx, args) => {
|
|
3161
3752
|
const message = args.join(' ').trim()
|
|
3162
3753
|
|
|
3163
3754
|
const lines: string[] = ['── Feedback ──', '']
|
|
3164
3755
|
|
|
3756
|
+
// If a message was provided, save it to ~/.mipham/feedback/
|
|
3165
3757
|
if (message) {
|
|
3166
|
-
|
|
3167
|
-
|
|
3168
|
-
|
|
3169
|
-
|
|
3170
|
-
|
|
3758
|
+
try {
|
|
3759
|
+
const { writeFileSync, mkdirSync, existsSync } = await import('node:fs')
|
|
3760
|
+
const { join } = await import('node:path')
|
|
3761
|
+
const { homedir } = await import('node:os')
|
|
3762
|
+
|
|
3763
|
+
const dir = join(homedir(), '.mipham', 'feedback')
|
|
3764
|
+
if (!existsSync(dir)) mkdirSync(dir, { recursive: true })
|
|
3765
|
+
|
|
3766
|
+
const ts = new Date().toISOString().replace(/[:.]/g, '-')
|
|
3767
|
+
const file = join(dir, `feedback-${ts}.md`)
|
|
3768
|
+
const content = [
|
|
3769
|
+
'---',
|
|
3770
|
+
`timestamp: ${new Date().toISOString()}`,
|
|
3771
|
+
`version: ${ctx.version}`,
|
|
3772
|
+
`session: ${ctx.sessionId}`,
|
|
3773
|
+
`provider: ${ctx.providerId}`,
|
|
3774
|
+
`model: ${ctx.modelId}`,
|
|
3775
|
+
'---',
|
|
3776
|
+
'',
|
|
3777
|
+
message,
|
|
3778
|
+
].join('\n')
|
|
3779
|
+
writeFileSync(file, content, 'utf-8')
|
|
3780
|
+
|
|
3781
|
+
lines.push('Your feedback:')
|
|
3782
|
+
lines.push('')
|
|
3783
|
+
lines.push(' """')
|
|
3784
|
+
for (const line of message.split('\n')) {
|
|
3785
|
+
lines.push(' ' + line)
|
|
3786
|
+
}
|
|
3787
|
+
lines.push(' """')
|
|
3788
|
+
lines.push('')
|
|
3789
|
+
lines.push(`✅ Saved to \`${file}\``)
|
|
3790
|
+
lines.push('')
|
|
3791
|
+
} catch (err) {
|
|
3792
|
+
lines.push(`⚠️ Save failed: ${String(err)}`)
|
|
3793
|
+
lines.push('')
|
|
3171
3794
|
}
|
|
3172
|
-
lines.push(' """')
|
|
3173
|
-
lines.push('')
|
|
3174
|
-
lines.push('── Preview Complete ──')
|
|
3175
|
-
lines.push('')
|
|
3176
|
-
lines.push('Copy the above and submit via one of the channels below.')
|
|
3177
|
-
lines.push('')
|
|
3178
3795
|
}
|
|
3179
3796
|
|
|
3180
3797
|
lines.push('── Feedback Channels ──')
|
|
3181
3798
|
lines.push('')
|
|
3182
3799
|
lines.push(' 🐛 Bug Reports')
|
|
3183
3800
|
lines.push(' GitHub Issues: https://github.com/One-Mipham/mipham-code/issues')
|
|
3184
|
-
lines.push('
|
|
3801
|
+
lines.push(' Or run `/bug-report` to auto-generate a diagnostic report')
|
|
3185
3802
|
lines.push('')
|
|
3186
3803
|
lines.push(' 💡 Feature Requests')
|
|
3187
3804
|
lines.push(' GitHub Issues: https://github.com/One-Mipham/mipham-code/issues')
|
|
@@ -3562,6 +4179,10 @@ const commandsListCmd: CommandHandler = () => {
|
|
|
3562
4179
|
'/recap': 'Session & Identity',
|
|
3563
4180
|
'/export': 'Session & Identity',
|
|
3564
4181
|
'/doctor': 'Session & Identity',
|
|
4182
|
+
'/dream': 'Session & Identity',
|
|
4183
|
+
'/constitution': 'Session & Identity',
|
|
4184
|
+
'/bug-report': 'Session & Identity',
|
|
4185
|
+
'/changelog': 'Session & Identity',
|
|
3565
4186
|
'/resume': 'Session & Identity',
|
|
3566
4187
|
'/resume last': 'Session & Identity',
|
|
3567
4188
|
'/resume delete': 'Session & Identity',
|
|
@@ -3604,6 +4225,9 @@ const commandsListCmd: CommandHandler = () => {
|
|
|
3604
4225
|
'/crsi stats': 'Tools & Skills',
|
|
3605
4226
|
'/crsi health': 'Tools & Skills',
|
|
3606
4227
|
'/crsi meta': 'Tools & Skills',
|
|
4228
|
+
'/crsi interpret': 'Tools & Skills',
|
|
4229
|
+
'/crsi critique': 'Tools & Skills',
|
|
4230
|
+
'/crsi red-team': 'Tools & Skills',
|
|
3607
4231
|
'/sis errors': 'Tools & Skills',
|
|
3608
4232
|
'/sis stats': 'Tools & Skills',
|
|
3609
4233
|
'/sis clear': 'Tools & Skills',
|
|
@@ -3753,10 +4377,17 @@ registry.set('/crsi restore', crsiRestoreCmd)
|
|
|
3753
4377
|
registry.set('/crsi stats', crsiStatsCmd)
|
|
3754
4378
|
registry.set('/crsi health', crsiHealthCmd)
|
|
3755
4379
|
registry.set('/crsi meta', crsiMetaCmd)
|
|
4380
|
+
registry.set('/crsi interpret', crsiInterpretCmd)
|
|
4381
|
+
registry.set('/crsi critique', crsiCritiqueCmd)
|
|
4382
|
+
registry.set('/crsi red-team', crsiRedTeamCmd)
|
|
3756
4383
|
registry.set('/sis errors', sisErrorsCmd)
|
|
3757
4384
|
registry.set('/sis stats', sisStatsCmd)
|
|
3758
4385
|
registry.set('/sis clear', sisClearCmd)
|
|
3759
4386
|
registry.set('/sis cleanup', sisCleanupCmd)
|
|
4387
|
+
registry.set('/dream', dreamCmd)
|
|
4388
|
+
registry.set('/constitution', constitutionCmd)
|
|
4389
|
+
registry.set('/bug-report', bugReportCmd)
|
|
4390
|
+
registry.set('/changelog', changelogCmd)
|
|
3760
4391
|
|
|
3761
4392
|
// Workflow
|
|
3762
4393
|
registry.set('/plan', planCmd)
|
|
@@ -3877,6 +4508,10 @@ const COMMAND_DESCRIPTIONS: Record<string, string> = {
|
|
|
3877
4508
|
'/recap': 'Summarize session so far',
|
|
3878
4509
|
'/export': 'Export conversation to file',
|
|
3879
4510
|
'/doctor': 'System diagnostics',
|
|
4511
|
+
'/dream': 'Background memory consolidation',
|
|
4512
|
+
'/constitution': 'View or reload constitutional principles',
|
|
4513
|
+
'/bug-report': 'Generate diagnostic report for GitHub Issues',
|
|
4514
|
+
'/changelog': 'Recent version history',
|
|
3880
4515
|
'/resume': 'List saved sessions',
|
|
3881
4516
|
'/resume last': 'Restore the most recent saved session',
|
|
3882
4517
|
'/resume delete': 'Delete a saved session',
|
|
@@ -3917,6 +4552,9 @@ const COMMAND_DESCRIPTIONS: Record<string, string> = {
|
|
|
3917
4552
|
'/crsi stats': 'Show CRSI overall effectiveness statistics',
|
|
3918
4553
|
'/crsi health': 'CRSI + SIS unified health dashboard with scoring',
|
|
3919
4554
|
'/crsi meta': 'RSI Level 3 meta-rule analysis — rules that improve the rules',
|
|
4555
|
+
'/crsi interpret': 'Tool-call behavior dashboard — error patterns, usage, health',
|
|
4556
|
+
'/crsi critique': 'Enable/disable RLAIF self-critique on tool calls',
|
|
4557
|
+
'/crsi red-team': 'Run adversarial self-test — verify SIS blocks known attacks',
|
|
3920
4558
|
'/sis errors': 'List all active SIS immune memory signatures',
|
|
3921
4559
|
'/sis stats': 'Show SIS self-immune system aggregate statistics',
|
|
3922
4560
|
'/sis clear': 'Retire an immune memory signature by ID',
|