codemini-cli 0.4.1 → 0.4.2
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/OPERATIONS.md +4 -2
- package/README.md +83 -5
- package/deployment.md +14 -7
- package/package.json +1 -2
- package/src/cli.js +1 -1
- package/src/commands/skill.js +145 -53
- package/src/core/agent-loop.js +1 -206
- package/src/core/chat-runtime.js +306 -53
- package/src/core/command-loader.js +12 -5
- package/src/core/context-compact.js +2 -1
- package/src/core/dream-audit.js +12 -0
- package/src/core/dream-consolidate.js +131 -59
- package/src/core/dream-evaluator.js +86 -0
- package/src/core/fff-adapter.js +1 -1
- package/src/core/memory-store.js +145 -10
- package/src/core/reflect-skill.js +178 -0
- package/src/core/tool-result-store.js +206 -0
- package/src/core/tools.js +126 -30
- package/src/tui/chat-app.js +247 -27
- package/src/core/provider/anthropic.sdk-backup.js +0 -439
- package/src/core/provider/openai-compatible.sdk-backup.js +0 -412
package/src/tui/chat-app.js
CHANGED
|
@@ -142,6 +142,7 @@ const TUI_COPY = {
|
|
|
142
142
|
'🧩 用 /mode plan 切换到规划模式,让 AI 先出方案再动手。',
|
|
143
143
|
'🆕 /new 可以新建一个干净的会话,重新开始工作。',
|
|
144
144
|
'🧠 /memory 查看和管理 AI 的持久记忆,帮助它更好地理解你的偏好。',
|
|
145
|
+
'🌐 web_fetch 默认轻量读取网页;如需更好读取 JS 渲染页面,可运行 npm install -g playwright && playwright install chromium。',
|
|
145
146
|
'💤 CodeMini 会自动"做梦"休息,整理错误信息并自我优化,越用越聪明~'
|
|
146
147
|
],
|
|
147
148
|
toolSummaryExpanded: '工具摘要:已展开',
|
|
@@ -314,6 +315,18 @@ const TUI_COPY = {
|
|
|
314
315
|
inputLocked: '计划审批进行中,请在审批框输入 /yes、/edit 或 /reject',
|
|
315
316
|
answerLabel: '审批输入',
|
|
316
317
|
answerPlaceholder: '/yes | /edit <反馈> | /reject'
|
|
318
|
+
},
|
|
319
|
+
reflectApproval: {
|
|
320
|
+
title: '审阅 Reflect 技能草稿?',
|
|
321
|
+
scopeLabel: '范围',
|
|
322
|
+
nameLabel: '名称',
|
|
323
|
+
targetLabel: '目标',
|
|
324
|
+
prompt: '输入 /yes 写入,输入 /edit <反馈> 修改,输入 /no 丢弃。',
|
|
325
|
+
invalidAnswer: '请输入 /yes、/edit <反馈> 或 /no。',
|
|
326
|
+
missingFeedback: '请在 /edit 后提供反馈内容。',
|
|
327
|
+
inputLocked: 'Reflect 审阅进行中,请在审阅框输入 /yes、/edit 或 /no',
|
|
328
|
+
answerLabel: '审阅输入',
|
|
329
|
+
answerPlaceholder: '/yes | /edit <反馈> | /no'
|
|
317
330
|
}
|
|
318
331
|
},
|
|
319
332
|
en: {
|
|
@@ -354,6 +367,7 @@ const TUI_COPY = {
|
|
|
354
367
|
'🧩 Use /mode plan to switch to planning mode — AI proposes a plan before coding.',
|
|
355
368
|
'🆕 /new starts a fresh session to begin a clean slate.',
|
|
356
369
|
'🧠 /memory lets you view and manage the AI\'s persistent memory for better personalization.',
|
|
370
|
+
'🌐 web_fetch uses a lightweight reader by default. For better JS-rendered pages: npm install -g playwright && playwright install chromium.',
|
|
357
371
|
'💤 CodeMini auto-"dreams" to rest, consolidate errors, and self-optimize — it gets smarter over time~'
|
|
358
372
|
],
|
|
359
373
|
toolSummaryExpanded: 'Tool summary: expanded',
|
|
@@ -526,6 +540,18 @@ const TUI_COPY = {
|
|
|
526
540
|
inputLocked: 'Plan approval is active; type /yes, /edit <feedback>, or /reject',
|
|
527
541
|
answerLabel: 'Approval input',
|
|
528
542
|
answerPlaceholder: '/yes | /edit <feedback> | /reject'
|
|
543
|
+
},
|
|
544
|
+
reflectApproval: {
|
|
545
|
+
title: 'Review this reflected skill draft?',
|
|
546
|
+
scopeLabel: 'Scope',
|
|
547
|
+
nameLabel: 'Name',
|
|
548
|
+
targetLabel: 'Target',
|
|
549
|
+
prompt: 'Type /yes to write, /edit <feedback> to revise, or /no to discard.',
|
|
550
|
+
invalidAnswer: 'Please enter /yes, /edit <feedback>, or /no.',
|
|
551
|
+
missingFeedback: 'Please provide feedback after /edit.',
|
|
552
|
+
inputLocked: 'Reflect review is active; type /yes, /edit <feedback>, or /no',
|
|
553
|
+
answerLabel: 'Review input',
|
|
554
|
+
answerPlaceholder: '/yes | /edit <feedback> | /no'
|
|
529
555
|
}
|
|
530
556
|
}
|
|
531
557
|
};
|
|
@@ -539,13 +565,24 @@ function getCopy(language) {
|
|
|
539
565
|
}
|
|
540
566
|
|
|
541
567
|
function messageLabel(label, copy) {
|
|
542
|
-
return copy
|
|
568
|
+
return copy?.roleLabels?.[label] || String(label || '').toUpperCase();
|
|
543
569
|
}
|
|
544
570
|
|
|
545
571
|
function roleStyle(label) {
|
|
546
572
|
return ROLE_STYLES[label] || ROLE_STYLES.system;
|
|
547
573
|
}
|
|
548
574
|
|
|
575
|
+
const PLAN_AGENT_ROLES = new Set(['planner', 'coder', 'reviewer', 'tester', 'summarizer']);
|
|
576
|
+
|
|
577
|
+
function normalizePlanAgentRole(role) {
|
|
578
|
+
const roleKey = String(role || '').trim().toLowerCase();
|
|
579
|
+
return PLAN_AGENT_ROLES.has(roleKey) ? roleKey : 'coder';
|
|
580
|
+
}
|
|
581
|
+
|
|
582
|
+
export function formatPlanAgentLabel(role, copy) {
|
|
583
|
+
return messageLabel(normalizePlanAgentRole(role), copy);
|
|
584
|
+
}
|
|
585
|
+
|
|
549
586
|
function StatusPill({ label, value, color = 'cyanBright', textColor = 'black' }) {
|
|
550
587
|
return h(
|
|
551
588
|
Box,
|
|
@@ -892,7 +929,7 @@ function textFromSessionContent(content) {
|
|
|
892
929
|
return sanitizeRenderableText(String(content || ''));
|
|
893
930
|
}
|
|
894
931
|
|
|
895
|
-
function buildUiMessagesFromSessionHistory(sessionMessages, nextId) {
|
|
932
|
+
export function buildUiMessagesFromSessionHistory(sessionMessages, nextId) {
|
|
896
933
|
const source = Array.isArray(sessionMessages) ? sessionMessages : [];
|
|
897
934
|
const out = [];
|
|
898
935
|
|
|
@@ -901,7 +938,7 @@ function buildUiMessagesFromSessionHistory(sessionMessages, nextId) {
|
|
|
901
938
|
if (message.role === 'tool') continue;
|
|
902
939
|
|
|
903
940
|
const text = textFromSessionContent(message.content);
|
|
904
|
-
if (!text.trim()
|
|
941
|
+
if (!text.trim()) continue;
|
|
905
942
|
|
|
906
943
|
if (message.role === 'user') {
|
|
907
944
|
out.push({ id: nextId(), label: 'you', text, color: 'blueBright' });
|
|
@@ -1109,6 +1146,25 @@ export function parsePlanApprovalAnswer(value) {
|
|
|
1109
1146
|
return { action: 'invalid', command: '' };
|
|
1110
1147
|
}
|
|
1111
1148
|
|
|
1149
|
+
export function parseReflectApprovalAnswer(value) {
|
|
1150
|
+
const raw = String(value || '').trim();
|
|
1151
|
+
if (!raw) return { action: 'empty', command: '' };
|
|
1152
|
+
const normalized = raw.toLowerCase();
|
|
1153
|
+
if (normalized === '/yes' || normalized === 'yes') {
|
|
1154
|
+
return { action: 'approve', command: '/yes' };
|
|
1155
|
+
}
|
|
1156
|
+
if (normalized === '/no' || normalized === 'no') {
|
|
1157
|
+
return { action: 'reject', command: '/no' };
|
|
1158
|
+
}
|
|
1159
|
+
const editMatch = raw.match(/^\/?edit(?:\s+(.+))?$/i);
|
|
1160
|
+
if (editMatch) {
|
|
1161
|
+
const feedback = String(editMatch[1] || '').trim();
|
|
1162
|
+
if (!feedback) return { action: 'missing_feedback', command: '' };
|
|
1163
|
+
return { action: 'edit', feedback, command: `/edit ${feedback}` };
|
|
1164
|
+
}
|
|
1165
|
+
return { action: 'invalid', command: '' };
|
|
1166
|
+
}
|
|
1167
|
+
|
|
1112
1168
|
export function parsePendingPlanApprovalMessage(text = '') {
|
|
1113
1169
|
const raw = String(text || '');
|
|
1114
1170
|
if (!/^Plan approval is still pending\./i.test(raw.trim())) return null;
|
|
@@ -1122,6 +1178,21 @@ export function parsePendingPlanApprovalMessage(text = '') {
|
|
|
1122
1178
|
return out;
|
|
1123
1179
|
}
|
|
1124
1180
|
|
|
1181
|
+
export function parsePendingReflectSkillMessage(text = '') {
|
|
1182
|
+
const raw = String(text || '');
|
|
1183
|
+
if (!/\bReflect skill draft pending\./i.test(raw)) return null;
|
|
1184
|
+
const lines = raw.split(/\r?\n/);
|
|
1185
|
+
const out = { scope: '', name: '', confidence: '', targetPath: '' };
|
|
1186
|
+
for (const line of lines) {
|
|
1187
|
+
const trimmed = line.trim();
|
|
1188
|
+
if (trimmed.startsWith('Scope: ')) out.scope = trimmed.slice('Scope: '.length).trim();
|
|
1189
|
+
else if (/^\[\d+\]\s+/.test(trimmed) && !out.name) out.name = trimmed.replace(/^\[\d+\]\s+/, '').trim();
|
|
1190
|
+
else if (trimmed.startsWith('Confidence: ')) out.confidence = trimmed.slice('Confidence: '.length).trim();
|
|
1191
|
+
else if (trimmed.startsWith('Target: ')) out.targetPath = trimmed.slice('Target: '.length).trim();
|
|
1192
|
+
}
|
|
1193
|
+
return out;
|
|
1194
|
+
}
|
|
1195
|
+
|
|
1125
1196
|
export function formatDeleteApprovalLines(copy, request) {
|
|
1126
1197
|
const details = normalizeDeleteApprovalRequest(request);
|
|
1127
1198
|
if (!details) return [];
|
|
@@ -1141,6 +1212,17 @@ export function formatPlanApprovalLines(copy, request) {
|
|
|
1141
1212
|
return [String(copy?.planApproval?.title || '').trim()].filter(Boolean);
|
|
1142
1213
|
}
|
|
1143
1214
|
|
|
1215
|
+
export function formatReflectApprovalLines(copy, request) {
|
|
1216
|
+
if (!request) return [];
|
|
1217
|
+
const c = copy?.reflectApproval || {};
|
|
1218
|
+
const lines = [String(c.title || '').trim()];
|
|
1219
|
+
if (request.scope) lines.push(`${c.scopeLabel || 'Scope'}: ${request.scope}`);
|
|
1220
|
+
if (request.name) lines.push(`${c.nameLabel || 'Name'}: ${request.name}`);
|
|
1221
|
+
if (request.targetPath) lines.push(`${c.targetLabel || 'Target'}: ${request.targetPath}`);
|
|
1222
|
+
if (c.prompt) lines.push(c.prompt);
|
|
1223
|
+
return lines.filter(Boolean);
|
|
1224
|
+
}
|
|
1225
|
+
|
|
1144
1226
|
function getActivityDisplayParts(activity) {
|
|
1145
1227
|
if (isCodeGenerationActivityName(activity?.name)) {
|
|
1146
1228
|
return {
|
|
@@ -1419,10 +1501,9 @@ function PlanStrip({ planState, copy }) {
|
|
|
1419
1501
|
Box,
|
|
1420
1502
|
{ flexDirection: 'column' },
|
|
1421
1503
|
...planState.steps.map((step, idx) => {
|
|
1422
|
-
const
|
|
1423
|
-
const normalizedRole = ['planner', 'coder', 'reviewer', 'tester', 'summarizer'].includes(roleKey) ? roleKey : 'coder';
|
|
1504
|
+
const normalizedRole = normalizePlanAgentRole(step.role);
|
|
1424
1505
|
const stepTheme = roleStyle(normalizedRole);
|
|
1425
|
-
const roleTag =
|
|
1506
|
+
const roleTag = formatPlanAgentLabel(normalizedRole, copy);
|
|
1426
1507
|
const stepDone = step.status === 'done' || isDone;
|
|
1427
1508
|
const stepFailed = step.status === 'failed';
|
|
1428
1509
|
const marker = stepFailed ? '✗' : stepDone ? '✓' : '·';
|
|
@@ -1431,8 +1512,8 @@ function PlanStrip({ planState, copy }) {
|
|
|
1431
1512
|
Box,
|
|
1432
1513
|
{ key: `plan-step-${idx}` },
|
|
1433
1514
|
h(Text, { color: markerColor }, `${marker} `),
|
|
1434
|
-
|
|
1435
|
-
|
|
1515
|
+
h(Text, { color: stepTheme.badgeText, backgroundColor: stepTheme.badgeBg }, ` ${roleTag} `),
|
|
1516
|
+
h(Text, { color: 'gray' }, ' '),
|
|
1436
1517
|
h(Text, { color: stepDone && !stepFailed ? 'gray' : 'white' }, `${step.index}. ${step.title}`)
|
|
1437
1518
|
);
|
|
1438
1519
|
}
|
|
@@ -1519,6 +1600,30 @@ function renderTextLine(msg, line, idx, color) {
|
|
|
1519
1600
|
);
|
|
1520
1601
|
}
|
|
1521
1602
|
|
|
1603
|
+
function historyListLineColor(line, fallbackColor) {
|
|
1604
|
+
const raw = String(line || '');
|
|
1605
|
+
const trimmed = raw.trim();
|
|
1606
|
+
if (!trimmed) return fallbackColor;
|
|
1607
|
+
if (/^Current session\s+/i.test(trimmed) || /^Recent sessions$/i.test(trimmed) || /^\d+\.\s+\S+/.test(trimmed)) {
|
|
1608
|
+
return 'cyanBright';
|
|
1609
|
+
}
|
|
1610
|
+
if (/^Messages\s+\d+$/i.test(trimmed) || /^\d+\s+msgs?\s+\|\s+updated\b/i.test(trimmed) || /^Tip:/i.test(trimmed)) {
|
|
1611
|
+
return 'gray';
|
|
1612
|
+
}
|
|
1613
|
+
if (/^resume:\s+\/history resume\b/i.test(trimmed)) {
|
|
1614
|
+
return 'blueBright';
|
|
1615
|
+
}
|
|
1616
|
+
return 'white';
|
|
1617
|
+
}
|
|
1618
|
+
|
|
1619
|
+
function isHistoryListMessage(msg) {
|
|
1620
|
+
const text = String(msg?.text || '');
|
|
1621
|
+
return msg?.label === 'system' &&
|
|
1622
|
+
/^Current session\s+/m.test(text) &&
|
|
1623
|
+
/^Recent sessions$/m.test(text) &&
|
|
1624
|
+
/resume:\s+\/history resume\b/m.test(text);
|
|
1625
|
+
}
|
|
1626
|
+
|
|
1522
1627
|
export function parseAutoPlanSummaryMessage(text) {
|
|
1523
1628
|
const raw = String(text || '').trim();
|
|
1524
1629
|
if (!/^Auto plan finished\b/i.test(raw)) return null;
|
|
@@ -1963,7 +2068,7 @@ function PlanSummaryBubble({ msg, copy }) {
|
|
|
1963
2068
|
Box,
|
|
1964
2069
|
{ marginBottom: planSteps.length > 0 || summary.approval || metaItems.length > 0 || summary.warningSteps || summary.failedSteps || shortFile ? 1 : 0, flexDirection: 'column' },
|
|
1965
2070
|
h(Text, { color: 'cyanBright' }, labels.plan),
|
|
1966
|
-
h(Text, { color: '
|
|
2071
|
+
h(Text, { color: 'white' }, summary.planSummary)
|
|
1967
2072
|
)
|
|
1968
2073
|
: null,
|
|
1969
2074
|
planSteps.length > 0
|
|
@@ -1972,12 +2077,20 @@ function PlanSummaryBubble({ msg, copy }) {
|
|
|
1972
2077
|
{ marginBottom: summary.approval || metaItems.length > 0 || summary.warningSteps || summary.failedSteps || shortFile ? 1 : 0, flexDirection: 'column' },
|
|
1973
2078
|
h(Text, { color: 'cyanBright' }, labels.steps),
|
|
1974
2079
|
...planSteps.flatMap((step, idx) => {
|
|
1975
|
-
const
|
|
2080
|
+
const roleKey = normalizePlanAgentRole(step?.role);
|
|
2081
|
+
const stepTheme = roleStyle(roleKey);
|
|
2082
|
+
const roleTag = formatPlanAgentLabel(roleKey, copy);
|
|
1976
2083
|
const titleText = String(step?.title || '-').trim() || '-';
|
|
1977
2084
|
const taskText = String(step?.task || '').trim();
|
|
1978
|
-
const titleRow = h(
|
|
2085
|
+
const titleRow = h(
|
|
2086
|
+
Text,
|
|
2087
|
+
{ key: `plan-step-title-${idx}`, color: 'white' },
|
|
2088
|
+
`${idx + 1}. `,
|
|
2089
|
+
h(Text, { color: stepTheme.badgeText, backgroundColor: stepTheme.badgeBg }, ` ${roleTag} `),
|
|
2090
|
+
` ${titleText}`
|
|
2091
|
+
);
|
|
1979
2092
|
if (!taskText) return [titleRow];
|
|
1980
|
-
const taskRow = h(Text, { key: `plan-step-task-${idx}`, color: '
|
|
2093
|
+
const taskRow = h(Text, { key: `plan-step-task-${idx}`, color: 'white' }, ` - task: ${taskText}`);
|
|
1981
2094
|
return [titleRow, taskRow];
|
|
1982
2095
|
})
|
|
1983
2096
|
)
|
|
@@ -1987,7 +2100,7 @@ function PlanSummaryBubble({ msg, copy }) {
|
|
|
1987
2100
|
Box,
|
|
1988
2101
|
{ marginBottom: metaItems.length > 0 || summary.warningSteps || summary.failedSteps || shortFile ? 1 : 0, flexDirection: 'column' },
|
|
1989
2102
|
h(Text, { color: 'yellowBright' }, labels.approval),
|
|
1990
|
-
h(Text, { color: '
|
|
2103
|
+
h(Text, { color: 'white' }, summary.approval)
|
|
1991
2104
|
)
|
|
1992
2105
|
: null,
|
|
1993
2106
|
metaItems.length > 0
|
|
@@ -1996,7 +2109,7 @@ function PlanSummaryBubble({ msg, copy }) {
|
|
|
1996
2109
|
{ marginBottom: summary.warningSteps || summary.failedSteps || shortFile ? 1 : 0 },
|
|
1997
2110
|
...metaItems.flatMap((item, idx) => [
|
|
1998
2111
|
idx > 0 ? h(Text, { key: `sep-${idx}`, color: 'gray' }, ' ') : null,
|
|
1999
|
-
h(Text, { key: `meta-${idx}`, color: '
|
|
2112
|
+
h(Text, { key: `meta-${idx}`, color: 'white' }, item)
|
|
2000
2113
|
])
|
|
2001
2114
|
)
|
|
2002
2115
|
: null,
|
|
@@ -2005,7 +2118,7 @@ function PlanSummaryBubble({ msg, copy }) {
|
|
|
2005
2118
|
Box,
|
|
2006
2119
|
{ marginBottom: summary.failedSteps || shortFile ? 1 : 0, flexDirection: 'column' },
|
|
2007
2120
|
h(Text, { color: 'yellowBright' }, labels.warnings),
|
|
2008
|
-
h(Text, { color: '
|
|
2121
|
+
h(Text, { color: 'white' }, summary.warningSteps)
|
|
2009
2122
|
)
|
|
2010
2123
|
: null,
|
|
2011
2124
|
summary.failedSteps
|
|
@@ -2013,7 +2126,7 @@ function PlanSummaryBubble({ msg, copy }) {
|
|
|
2013
2126
|
Box,
|
|
2014
2127
|
{ marginBottom: shortFile ? 1 : 0, flexDirection: 'column' },
|
|
2015
2128
|
h(Text, { color: 'redBright' }, labels.failed),
|
|
2016
|
-
h(Text, { color: '
|
|
2129
|
+
h(Text, { color: 'white' }, summary.failedSteps)
|
|
2017
2130
|
)
|
|
2018
2131
|
: null,
|
|
2019
2132
|
shortFile
|
|
@@ -2309,6 +2422,7 @@ export function collapseActivityChainRows(inputRows, showToolDetails, copy, maxV
|
|
|
2309
2422
|
|
|
2310
2423
|
export function buildMessageRows(msg, showToolDetails, contentWidth = 72, copy) {
|
|
2311
2424
|
const rows = [];
|
|
2425
|
+
const isHistoryList = isHistoryListMessage(msg);
|
|
2312
2426
|
const pushTextRows = (text) => {
|
|
2313
2427
|
const lines = String(text || '').split('\n');
|
|
2314
2428
|
let codeFence = false;
|
|
@@ -2329,8 +2443,8 @@ export function buildMessageRows(msg, showToolDetails, contentWidth = 72, copy)
|
|
|
2329
2443
|
continue;
|
|
2330
2444
|
}
|
|
2331
2445
|
if (isMarkdownTableHeader(line, lines[lineIndex + 1])) {
|
|
2332
|
-
const tableLines = [line];
|
|
2333
|
-
lineIndex += 1; //
|
|
2446
|
+
const tableLines = [line, lines[lineIndex + 1]];
|
|
2447
|
+
lineIndex += 1; // separator included above
|
|
2334
2448
|
while (lineIndex + 1 < lines.length && splitMarkdownTableCells(lines[lineIndex + 1]).length > 1) {
|
|
2335
2449
|
tableLines.push(lines[lineIndex + 1]);
|
|
2336
2450
|
lineIndex += 1;
|
|
@@ -2339,7 +2453,8 @@ export function buildMessageRows(msg, showToolDetails, contentWidth = 72, copy)
|
|
|
2339
2453
|
continue;
|
|
2340
2454
|
}
|
|
2341
2455
|
let color = msg.color || roleStyle(msg.label).text || 'white';
|
|
2342
|
-
if (
|
|
2456
|
+
if (isHistoryList) color = historyListLineColor(line, color);
|
|
2457
|
+
else if (line.startsWith('#')) color = 'cyanBright';
|
|
2343
2458
|
else if (trimmed.startsWith('- ') || trimmed.startsWith('* ')) color = 'magentaBright';
|
|
2344
2459
|
else if (trimmed.startsWith('>')) color = 'yellow';
|
|
2345
2460
|
else if (/^[|└├│]/.test(trimmed)) color = 'gray';
|
|
@@ -2515,36 +2630,36 @@ export function renderMessageRow(msg, row, idx, loaderTick) {
|
|
|
2515
2630
|
return h(
|
|
2516
2631
|
Box,
|
|
2517
2632
|
{ key: `row-table-${msg.id}-${idx}`, marginLeft: 1 },
|
|
2518
|
-
h(Text, { color:
|
|
2633
|
+
h(Text, { color: 'white', bold: Boolean(row.isHeader) }, row.text)
|
|
2519
2634
|
);
|
|
2520
2635
|
}
|
|
2521
2636
|
if (row.kind === 'table-separator') {
|
|
2522
2637
|
return h(
|
|
2523
2638
|
Box,
|
|
2524
2639
|
{ key: `row-table-sep-${msg.id}-${idx}`, marginLeft: 1 },
|
|
2525
|
-
h(Text, { color: '
|
|
2640
|
+
h(Text, { color: 'white' }, row.text)
|
|
2526
2641
|
);
|
|
2527
2642
|
}
|
|
2528
2643
|
if (row.kind === 'table-vertical') {
|
|
2529
2644
|
return h(
|
|
2530
2645
|
Box,
|
|
2531
2646
|
{ key: `row-table-v-${msg.id}-${idx}`, marginLeft: 1 },
|
|
2532
|
-
h(Text, { color: '
|
|
2533
|
-
h(Text, { color: '
|
|
2647
|
+
h(Text, { color: 'white', bold: true }, `${row.label}:`),
|
|
2648
|
+
h(Text, { color: 'white' }, row.text ? ` ${row.text}` : '')
|
|
2534
2649
|
);
|
|
2535
2650
|
}
|
|
2536
2651
|
if (row.kind === 'table-vertical-continuation') {
|
|
2537
2652
|
return h(
|
|
2538
2653
|
Box,
|
|
2539
2654
|
{ key: `row-table-vc-${msg.id}-${idx}`, marginLeft: 3 },
|
|
2540
|
-
h(Text, { color: '
|
|
2655
|
+
h(Text, { color: 'white' }, row.text)
|
|
2541
2656
|
);
|
|
2542
2657
|
}
|
|
2543
2658
|
if (row.kind === 'table-vertical-separator') {
|
|
2544
2659
|
return h(
|
|
2545
2660
|
Box,
|
|
2546
2661
|
{ key: `row-table-vs-${msg.id}-${idx}`, marginLeft: 1 },
|
|
2547
|
-
h(Text, { color: '
|
|
2662
|
+
h(Text, { color: 'white' }, row.text)
|
|
2548
2663
|
);
|
|
2549
2664
|
}
|
|
2550
2665
|
if (row.kind === 'activity-collapsed') {
|
|
@@ -3150,6 +3265,38 @@ function PlanApprovalPanel({ request, inputValue, errorText, copy, cursorVisible
|
|
|
3150
3265
|
);
|
|
3151
3266
|
}
|
|
3152
3267
|
|
|
3268
|
+
function ReflectApprovalPanel({ request, inputValue, errorText, copy, cursorVisible }) {
|
|
3269
|
+
if (!request) return null;
|
|
3270
|
+
const placeholder = String(copy.reflectApproval.answerPlaceholder || '').trim();
|
|
3271
|
+
const lines = formatReflectApprovalLines(copy, request);
|
|
3272
|
+
return h(
|
|
3273
|
+
Box,
|
|
3274
|
+
{
|
|
3275
|
+
marginTop: 1,
|
|
3276
|
+
flexDirection: 'column',
|
|
3277
|
+
borderStyle: 'round',
|
|
3278
|
+
borderColor: 'yellowBright',
|
|
3279
|
+
paddingX: 1,
|
|
3280
|
+
paddingY: 0
|
|
3281
|
+
},
|
|
3282
|
+
...lines.map((line, index) =>
|
|
3283
|
+
h(Text, { key: `reflect-approval-line-${index}`, color: 'yellowBright' }, line)
|
|
3284
|
+
),
|
|
3285
|
+
h(
|
|
3286
|
+
Box,
|
|
3287
|
+
{ marginTop: 1 },
|
|
3288
|
+
h(Text, { color: 'yellowBright' }, `${copy.reflectApproval.answerLabel}: `),
|
|
3289
|
+
h(ApprovalCursorLine, {
|
|
3290
|
+
inputValue,
|
|
3291
|
+
placeholder: placeholder || ' ',
|
|
3292
|
+
cursorVisible,
|
|
3293
|
+
accent: 'yellowBright'
|
|
3294
|
+
})
|
|
3295
|
+
),
|
|
3296
|
+
errorText ? h(Text, { color: 'yellowBright' }, errorText) : null
|
|
3297
|
+
);
|
|
3298
|
+
}
|
|
3299
|
+
|
|
3153
3300
|
function SignatureBar({ version = '' }) {
|
|
3154
3301
|
return h(
|
|
3155
3302
|
Box,
|
|
@@ -3240,7 +3387,10 @@ export function ChatApp({ runtime, sessionId, model, sdkProvider = 'openai-compa
|
|
|
3240
3387
|
const [pendingPlanApproval, setPendingPlanApproval] = useState(null);
|
|
3241
3388
|
const [planApprovalInput, setPlanApprovalInput] = useState('');
|
|
3242
3389
|
const [planApprovalError, setPlanApprovalError] = useState('');
|
|
3243
|
-
const
|
|
3390
|
+
const [pendingReflectApproval, setPendingReflectApproval] = useState(null);
|
|
3391
|
+
const [reflectApprovalInput, setReflectApprovalInput] = useState('');
|
|
3392
|
+
const [reflectApprovalError, setReflectApprovalError] = useState('');
|
|
3393
|
+
const approvalLockActive = Boolean(pendingDeleteApproval || pendingRunApproval || pendingPlanApproval || pendingReflectApproval);
|
|
3244
3394
|
const activeAssistantIdRef = useRef(null);
|
|
3245
3395
|
const activeAssistantAutoSkillNamesRef = useRef([]);
|
|
3246
3396
|
const streamedAssistantHandledRef = useRef(false);
|
|
@@ -3656,6 +3806,13 @@ export function ChatApp({ runtime, sessionId, model, sdkProvider = 'openai-compa
|
|
|
3656
3806
|
setPlanState((prev) => ({ ...prev, pendingApproval: true }));
|
|
3657
3807
|
setPlanApprovalInput('');
|
|
3658
3808
|
setPlanApprovalError('');
|
|
3809
|
+
} else {
|
|
3810
|
+
const pendingReflectMeta = parsePendingReflectSkillMessage(result.text || '');
|
|
3811
|
+
if (pendingReflectMeta) {
|
|
3812
|
+
setPendingReflectApproval(pendingReflectMeta);
|
|
3813
|
+
setReflectApprovalInput('');
|
|
3814
|
+
setReflectApprovalError('');
|
|
3815
|
+
}
|
|
3659
3816
|
}
|
|
3660
3817
|
}
|
|
3661
3818
|
setMessages((prev) => [
|
|
@@ -3798,6 +3955,9 @@ export function ChatApp({ runtime, sessionId, model, sdkProvider = 'openai-compa
|
|
|
3798
3955
|
setPendingPlanApproval(null);
|
|
3799
3956
|
setPlanApprovalInput('');
|
|
3800
3957
|
setPlanApprovalError('');
|
|
3958
|
+
setPendingReflectApproval(null);
|
|
3959
|
+
setReflectApprovalInput('');
|
|
3960
|
+
setReflectApprovalError('');
|
|
3801
3961
|
setPlanState({
|
|
3802
3962
|
current: 0,
|
|
3803
3963
|
total: 0,
|
|
@@ -4485,6 +4645,46 @@ export function ChatApp({ runtime, sessionId, model, sdkProvider = 'openai-compa
|
|
|
4485
4645
|
return;
|
|
4486
4646
|
}
|
|
4487
4647
|
|
|
4648
|
+
if (pendingReflectApproval) {
|
|
4649
|
+
if (key.return) {
|
|
4650
|
+
const parsed = parseReflectApprovalAnswer(reflectApprovalInput);
|
|
4651
|
+
if (parsed.action === 'approve' || parsed.action === 'reject' || parsed.action === 'edit') {
|
|
4652
|
+
setPendingReflectApproval(null);
|
|
4653
|
+
setReflectApprovalInput('');
|
|
4654
|
+
setReflectApprovalError('');
|
|
4655
|
+
runSubmission(parsed.command);
|
|
4656
|
+
} else if (parsed.action === 'missing_feedback') {
|
|
4657
|
+
setReflectApprovalError(copy.reflectApproval.missingFeedback);
|
|
4658
|
+
} else {
|
|
4659
|
+
setReflectApprovalError(copy.reflectApproval.invalidAnswer);
|
|
4660
|
+
}
|
|
4661
|
+
return;
|
|
4662
|
+
}
|
|
4663
|
+
|
|
4664
|
+
if (isBackspaceKey(value, key) || isDeleteKey(value, key)) {
|
|
4665
|
+
setReflectApprovalInput((prev) => prev.slice(0, -1));
|
|
4666
|
+
setReflectApprovalError('');
|
|
4667
|
+
return;
|
|
4668
|
+
}
|
|
4669
|
+
|
|
4670
|
+
if (isPrintableInput(value, key)) {
|
|
4671
|
+
setReflectApprovalInput((prev) => `${prev}${value}`);
|
|
4672
|
+
setReflectApprovalError('');
|
|
4673
|
+
return;
|
|
4674
|
+
}
|
|
4675
|
+
|
|
4676
|
+
if (key.ctrl && value === 'c') {
|
|
4677
|
+
if (busy && typeof runtime.abort === 'function') {
|
|
4678
|
+
runtime.abort();
|
|
4679
|
+
return;
|
|
4680
|
+
}
|
|
4681
|
+
exit();
|
|
4682
|
+
return;
|
|
4683
|
+
}
|
|
4684
|
+
|
|
4685
|
+
return;
|
|
4686
|
+
}
|
|
4687
|
+
|
|
4488
4688
|
if (key.upArrow) {
|
|
4489
4689
|
if (suggestionNav && commandSuggestions.length > 0) {
|
|
4490
4690
|
setMenuIndex((prev) => moveSuggestionSelection(prev, commandSuggestions.length, 'up'));
|
|
@@ -4754,6 +4954,17 @@ export function ChatApp({ runtime, sessionId, model, sdkProvider = 'openai-compa
|
|
|
4754
4954
|
}
|
|
4755
4955
|
}, [runtimeState?.pendingPlanApproval, busy]);
|
|
4756
4956
|
|
|
4957
|
+
useEffect(() => {
|
|
4958
|
+
const pending = Boolean(runtimeState?.pendingReflectSkill);
|
|
4959
|
+
if (!pending) {
|
|
4960
|
+
setPendingReflectApproval(null);
|
|
4961
|
+
return;
|
|
4962
|
+
}
|
|
4963
|
+
if (!busy) {
|
|
4964
|
+
setPendingReflectApproval((prev) => prev || { scope: '', name: '', targetPath: '' });
|
|
4965
|
+
}
|
|
4966
|
+
}, [runtimeState?.pendingReflectSkill, busy]);
|
|
4967
|
+
|
|
4757
4968
|
useEffect(() => {
|
|
4758
4969
|
if (commandSuggestions.length === 0) {
|
|
4759
4970
|
setSuggestionNav(false);
|
|
@@ -4800,7 +5011,9 @@ export function ChatApp({ runtime, sessionId, model, sdkProvider = 'openai-compa
|
|
|
4800
5011
|
? { text: copy.runApproval.inputLocked }
|
|
4801
5012
|
: pendingPlanApproval
|
|
4802
5013
|
? { text: copy.planApproval.inputLocked }
|
|
4803
|
-
:
|
|
5014
|
+
: pendingReflectApproval
|
|
5015
|
+
? { text: copy.reflectApproval.inputLocked }
|
|
5016
|
+
: null;
|
|
4804
5017
|
|
|
4805
5018
|
return h(
|
|
4806
5019
|
Box,
|
|
@@ -4850,6 +5063,13 @@ export function ChatApp({ runtime, sessionId, model, sdkProvider = 'openai-compa
|
|
|
4850
5063
|
copy,
|
|
4851
5064
|
cursorVisible
|
|
4852
5065
|
}),
|
|
5066
|
+
h(ReflectApprovalPanel, {
|
|
5067
|
+
request: pendingReflectApproval,
|
|
5068
|
+
inputValue: reflectApprovalInput,
|
|
5069
|
+
errorText: reflectApprovalError,
|
|
5070
|
+
copy,
|
|
5071
|
+
cursorVisible
|
|
5072
|
+
}),
|
|
4853
5073
|
debugKeys
|
|
4854
5074
|
? h(
|
|
4855
5075
|
Box,
|