@noobdemon/noob-cli 1.12.15 → 1.12.16
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/agent.js +24 -2
- package/src/repl/complete.js +1 -0
- package/src/repl/state.js +1 -0
- package/src/repl.js +30 -1
- package/src/tui.js +21 -2
- package/src/ui.js +1 -0
package/package.json
CHANGED
package/src/agent.js
CHANGED
|
@@ -170,6 +170,25 @@ function goalBlock(goal) {
|
|
|
170
170
|
].join('\n');
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
+
// Mode hint (build/plan/compose) — chỉ NHẮC mềm, KHÔNG hard-gate tool. build =
|
|
174
|
+
// default (full, không thêm khối). plan/compose chèn 1 đoạn hướng dẫn nhẹ vào
|
|
175
|
+
// system prompt để model điều chỉnh phong cách làm việc.
|
|
176
|
+
function modeBlock(uiMode) {
|
|
177
|
+
if (uiMode === 'plan') {
|
|
178
|
+
return [
|
|
179
|
+
'# CHẾ ĐỘ PLAN',
|
|
180
|
+
'Ưu tiên phân tích & đọc code trước. Đề xuất kế hoạch rõ ràng TRƯỚC khi sửa file. Tránh edit_file/write_file/run_command (thao tác thay đổi) trừ khi user đã xác nhận hướng đi. Mục tiêu: hiểu sâu & lên kế hoạch, không vội thực thi.',
|
|
181
|
+
].join('\n');
|
|
182
|
+
}
|
|
183
|
+
if (uiMode === 'compose') {
|
|
184
|
+
return [
|
|
185
|
+
'# CHẾ ĐỘ COMPOSE',
|
|
186
|
+
'Làm việc theo workflow có cấu trúc: (1) làm rõ specs/yêu cầu → (2) lập kế hoạch → (3) thực thi từng bước → (4) báo cáo kết quả. Giữ kỷ luật từng giai đoạn, không nhảy cóc.',
|
|
187
|
+
].join('\n');
|
|
188
|
+
}
|
|
189
|
+
return '';
|
|
190
|
+
}
|
|
191
|
+
|
|
173
192
|
// Môi trường chạy thực: model cần biết OS + shell để emit lệnh ĐÚNG. Không có
|
|
174
193
|
// khối này, trên Windows model hay emit lệnh Unix (wc/ls/cat/grep) → run_command
|
|
175
194
|
// (PowerShell) báo lỗi.
|
|
@@ -417,7 +436,7 @@ function relTime(ts) {
|
|
|
417
436
|
// chèn ngay sau SYSTEM để model biết và dùng được.
|
|
418
437
|
// recentSessions: breadcrumbs các phiên trước cùng workspace (repl.js cung cấp)
|
|
419
438
|
// → chèn ngay sau memoryBlock() để model "thấy" lịch sử dù chưa /resume.
|
|
420
|
-
export function buildSystem(history, extraToolsDoc, goal, recentSessions) {
|
|
439
|
+
export function buildSystem(history, extraToolsDoc, goal, recentSessions, uiMode) {
|
|
421
440
|
const parts = [SYSTEM, '', memoryBlock()];
|
|
422
441
|
// Auto-active skills (frontmatter `auto: true`) — luôn ON, không cần slash.
|
|
423
442
|
// Đặt sau memoryBlock để model thấy convention skill TRƯỚC khi vào tool/goal.
|
|
@@ -427,6 +446,8 @@ export function buildSystem(history, extraToolsDoc, goal, recentSessions) {
|
|
|
427
446
|
parts.push('', recentSessionsBlock(recentSessions));
|
|
428
447
|
}
|
|
429
448
|
if (goal && goal.trim()) parts.push('', goalBlock(goal));
|
|
449
|
+
const mode = modeBlock(uiMode);
|
|
450
|
+
if (mode) parts.push('', mode);
|
|
430
451
|
if (extraToolsDoc) parts.push('', extraToolsDoc);
|
|
431
452
|
parts.push('', runtimeContext());
|
|
432
453
|
return parts.join('\n');
|
|
@@ -545,6 +566,7 @@ export async function runAgent({
|
|
|
545
566
|
goal,
|
|
546
567
|
recentSessions,
|
|
547
568
|
pendingTasks,
|
|
569
|
+
uiMode,
|
|
548
570
|
}) {
|
|
549
571
|
// [GỠ BUDGET 2026-06-06] Không còn token budget enforcement. Agent/loop/sub-agent
|
|
550
572
|
// chạy không giới hạn token. Dừng theo: GOAL đạt, <<LOOP_DONE>>, <<ULTRA_DONE>>,
|
|
@@ -573,7 +595,7 @@ export async function runAgent({
|
|
|
573
595
|
await maybeSummarize(history, { model, signal });
|
|
574
596
|
} catch {}
|
|
575
597
|
|
|
576
|
-
const system = buildSystem(history, extraToolsDoc, goal, recentSessions);
|
|
598
|
+
const system = buildSystem(history, extraToolsDoc, goal, recentSessions, uiMode);
|
|
577
599
|
const message = buildUserMessage(history);
|
|
578
600
|
tokenMeter?.addInput(countTokens(system) + countTokens(message));
|
|
579
601
|
tokenMeter?.setContext(tokenMeter.total);
|
package/src/repl/complete.js
CHANGED
|
@@ -22,6 +22,7 @@ export const SLASH = [
|
|
|
22
22
|
{ name: '/improve', desc: 'phân tích workspace & gợi ý tính năng cải thiện' },
|
|
23
23
|
{ name: '/ultra', desc: 'tự hành: tự nghĩ & làm nhiệm vụ' },
|
|
24
24
|
{ name: '/agent', desc: 'bật/tắt agent mode (spawn sub-agent)' },
|
|
25
|
+
{ name: '/mode', desc: 'build|plan|compose — đổi chế độ agent (Ctrl+T cycle)' },
|
|
25
26
|
{ name: '/goal', desc: 'đặt HARD GOAL — model phải hướng tới tới khi /goal clear' },
|
|
26
27
|
{ name: '/loop', desc: 'chạy task định kỳ (vd: /loop 5m kiểm tra log) · /loop stop để dừng' },
|
|
27
28
|
{ name: '/tokens', desc: 'xem số token đã dùng phiên này' },
|
package/src/repl/state.js
CHANGED
|
@@ -18,6 +18,7 @@ export function createState(opts = {}, config) {
|
|
|
18
18
|
return {
|
|
19
19
|
model: findModel(opts.model) || findModel(config.model) || findModel(DEFAULT_MODEL),
|
|
20
20
|
mode: 'chat', // chat | merge | search
|
|
21
|
+
agentUiMode: 'build', // build | plan | compose — UI hint mode (Ctrl+T / /mode). KHÔNG đụng `mode`.
|
|
21
22
|
history: [],
|
|
22
23
|
autoApprove: new Set(), // tool name → 'a' (always, phiên)
|
|
23
24
|
autoApproveTurn: new Set(), // tool name → 't' (this turn, reset sau mỗi runAgent)
|
package/src/repl.js
CHANGED
|
@@ -160,6 +160,14 @@ export async function startRepl(opts = {}) {
|
|
|
160
160
|
tui.print(state.yolo ? c.err(' ' + t.yoloOn) : c.ok(' ' + t.yoloOff));
|
|
161
161
|
tui.setPrompt(promptStr(false));
|
|
162
162
|
},
|
|
163
|
+
onCtrlT: () => {
|
|
164
|
+
// Cycle build → plan → compose. UI hint mode (chỉ system-prompt hint).
|
|
165
|
+
const order = ['build', 'plan', 'compose'];
|
|
166
|
+
const next = order[(order.indexOf(state.agentUiMode) + 1) % order.length];
|
|
167
|
+
state.agentUiMode = next;
|
|
168
|
+
tui.setAgentMode(next);
|
|
169
|
+
tui.print(c.accent(' ◆ mode: ') + next);
|
|
170
|
+
},
|
|
163
171
|
completer: completeInput,
|
|
164
172
|
});
|
|
165
173
|
|
|
@@ -338,6 +346,7 @@ export async function startRepl(opts = {}) {
|
|
|
338
346
|
state.history = s.history || [];
|
|
339
347
|
state.mode = 'chat';
|
|
340
348
|
state.goal = s.goal || null; // khôi phục HARD GOAL nếu phiên cũ có
|
|
349
|
+
tui.setGoal(state.goal || '');
|
|
341
350
|
if (s.model) {
|
|
342
351
|
const m = findModel(s.model);
|
|
343
352
|
if (m) state.model = m;
|
|
@@ -1318,6 +1327,8 @@ NGUYÊN TẮC:
|
|
|
1318
1327
|
}
|
|
1319
1328
|
|
|
1320
1329
|
tui.start();
|
|
1330
|
+
tui.setAgentMode(state.agentUiMode);
|
|
1331
|
+
tui.setGoal(state.goal || '');
|
|
1321
1332
|
banner();
|
|
1322
1333
|
printStatus(state);
|
|
1323
1334
|
// noob.md status line — cho user thấy model có memory gì (số dòng, rules/notes,
|
|
@@ -1550,7 +1561,8 @@ NGUYÊN TẮC:
|
|
|
1550
1561
|
state.history,
|
|
1551
1562
|
state.agentMode ? spawnAgentToolsDoc(0) : '',
|
|
1552
1563
|
state.goal,
|
|
1553
|
-
sessions.list(5, process.cwd()).filter((s) => s.id !== session?.id)
|
|
1564
|
+
sessions.list(5, process.cwd()).filter((s) => s.id !== session?.id),
|
|
1565
|
+
state.agentUiMode
|
|
1554
1566
|
);
|
|
1555
1567
|
const userMessage = buildUserMessage(state.history);
|
|
1556
1568
|
tokenMeter.setContext(countTokens(systemPrompt) + countTokens(userMessage));
|
|
@@ -1585,6 +1597,7 @@ NGUYÊN TẮC:
|
|
|
1585
1597
|
goal: state.goal,
|
|
1586
1598
|
recentSessions: sessions.list(5, process.cwd()).filter((s) => s.id !== session?.id),
|
|
1587
1599
|
extraToolsDoc: state.agentMode ? spawnAgentToolsDoc(0) : '',
|
|
1600
|
+
uiMode: state.agentUiMode,
|
|
1588
1601
|
// Pending tasks: todo items chưa hoàn thành từ lượt trước → model tiếp tục ngay.
|
|
1589
1602
|
pendingTasks: (state.todos || []).filter((t) => !t.done).map((t) => t.text),
|
|
1590
1603
|
onStatus: () => tick(t.thinking),
|
|
@@ -1874,6 +1887,20 @@ NGUYÊN TẮC:
|
|
|
1874
1887
|
);
|
|
1875
1888
|
break;
|
|
1876
1889
|
}
|
|
1890
|
+
case 'mode': {
|
|
1891
|
+
const v = arg.toLowerCase().trim();
|
|
1892
|
+
if (!v) {
|
|
1893
|
+
console.log(c.accent(' ◆ mode: ') + state.agentUiMode);
|
|
1894
|
+
console.log(c.dim(' cú pháp: /mode build|plan|compose · hoặc Ctrl+T để cycle'));
|
|
1895
|
+
} else if (v === 'build' || v === 'plan' || v === 'compose') {
|
|
1896
|
+
state.agentUiMode = v;
|
|
1897
|
+
tui.setAgentMode(v);
|
|
1898
|
+
console.log(c.accent(' ◆ mode: ') + v);
|
|
1899
|
+
} else {
|
|
1900
|
+
console.log(c.err(' mode không hợp lệ: ') + v + c.dim(' (chọn build|plan|compose)'));
|
|
1901
|
+
}
|
|
1902
|
+
break;
|
|
1903
|
+
}
|
|
1877
1904
|
case 'kg': {
|
|
1878
1905
|
// Knowledge graph CRUD — port từ mcp-knowledge-graph, lưu .noob/kg.jsonl.
|
|
1879
1906
|
// Sub-cmd: list, path, add, obs, link, unlink, unobs, get, search, rm.
|
|
@@ -2012,10 +2039,12 @@ NGUYÊN TẮC:
|
|
|
2012
2039
|
v.toLowerCase() === 'xoa'
|
|
2013
2040
|
) {
|
|
2014
2041
|
state.goal = null;
|
|
2042
|
+
tui.setGoal('');
|
|
2015
2043
|
console.log(c.dim(' đã xoá goal'));
|
|
2016
2044
|
persist();
|
|
2017
2045
|
} else {
|
|
2018
2046
|
state.goal = v;
|
|
2047
|
+
tui.setGoal(v);
|
|
2019
2048
|
console.log(c.accent(' 🎯 đã đặt goal: ') + v);
|
|
2020
2049
|
persist();
|
|
2021
2050
|
}
|
package/src/tui.js
CHANGED
|
@@ -107,7 +107,7 @@ function truncStr(s, max) {
|
|
|
107
107
|
return s.length > max ? s.slice(0, max - 1) + '…' : s;
|
|
108
108
|
}
|
|
109
109
|
|
|
110
|
-
export function createTui({ onLine, onInterrupt, onEOF, onShiftTab, completer } = {}) {
|
|
110
|
+
export function createTui({ onLine, onInterrupt, onEOF, onShiftTab, onCtrlT, completer } = {}) {
|
|
111
111
|
const tty = !!(process.stdout.isTTY && process.stdin.isTTY) && process.env.NOOB_TUI !== '0';
|
|
112
112
|
const cols = () => process.stdout.columns || 80;
|
|
113
113
|
|
|
@@ -154,6 +154,8 @@ export function createTui({ onLine, onInterrupt, onEOF, onShiftTab, completer }
|
|
|
154
154
|
setMeta() {},
|
|
155
155
|
setTodos() {},
|
|
156
156
|
setUltra() {},
|
|
157
|
+
setAgentMode() {},
|
|
158
|
+
setGoal() {},
|
|
157
159
|
setBgAgents() {},
|
|
158
160
|
pushAgentLog() {},
|
|
159
161
|
clearAgentLog() {},
|
|
@@ -186,6 +188,8 @@ export function createTui({ onLine, onInterrupt, onEOF, onShiftTab, completer }
|
|
|
186
188
|
let todos = []; // [{text, done}] — danh sách todo đang chạy, repl parse từ model output
|
|
187
189
|
let ultra = false; // ULTRA mode đang bật? → segment trong status bar
|
|
188
190
|
let bgAgents = 0; // số sub-agent / workflow chạy nền → segment + gợi ý Ctrl+O
|
|
191
|
+
let agentMode = ''; // build | plan | compose — UI hint mode → segment status bar (Ctrl+T / /mode)
|
|
192
|
+
let goalText = ''; // HARD GOAL hiện tại (/goal) → segment 🎯 status bar (truncate)
|
|
189
193
|
// Ring buffer log sub-agent nền: KHÔNG in ra UI chính, chỉ vào đây. Ctrl+O mở
|
|
190
194
|
// overlay xem. Mỗi entry: chuỗi đã format (có thể kèm màu). Giữ tối đa MAX_LOG.
|
|
191
195
|
const MAX_AGENT_LOG = 500;
|
|
@@ -368,9 +372,11 @@ export function createTui({ onLine, onInterrupt, onEOF, onShiftTab, completer }
|
|
|
368
372
|
const spin = FRAMES[frame % FRAMES.length];
|
|
369
373
|
// Badge ghép vào mọi dòng status: ULTRA bật/tắt + số sub-agent nền (gợi ý
|
|
370
374
|
// Ctrl+O xem log). Reset ANSI từng đoạn (chalk tự đóng) → không bleed.
|
|
375
|
+
const modeBadge = agentMode ? c.tool(`◆ ${agentMode}`) : '';
|
|
376
|
+
const goalBadge = goalText ? c.accent('🎯 ') + c.dim(truncStr(goalText, 28)) : '';
|
|
371
377
|
const ultraBadge = ultra ? c.err('🚀 ULTRA') : '';
|
|
372
378
|
const bgBadge = bgAgents ? c.accent(`⊕ ${bgAgents} nền`) + c.dim(' (Ctrl+O)') : '';
|
|
373
|
-
const badges = [ultraBadge, bgBadge].filter(Boolean).join(c.dim(' · '));
|
|
379
|
+
const badges = [modeBadge, goalBadge, ultraBadge, bgBadge].filter(Boolean).join(c.dim(' · '));
|
|
374
380
|
const badgeTail = badges ? c.dim(' · ') + badges : '';
|
|
375
381
|
// Todo progress: 1 dòng gọn dùng chung (📋 done/total bar pct%). Dựng SẴN ở
|
|
376
382
|
// đây để ghép vào status line theo trạng thái — KHÔNG return sớm nuốt status.
|
|
@@ -800,6 +806,11 @@ export function createTui({ onLine, onInterrupt, onEOF, onShiftTab, completer }
|
|
|
800
806
|
i++;
|
|
801
807
|
continue;
|
|
802
808
|
} // Ctrl+O: bật/tắt overlay log sub-agent nền
|
|
809
|
+
if (ch === '\x14') {
|
|
810
|
+
onCtrlT?.();
|
|
811
|
+
i++;
|
|
812
|
+
continue;
|
|
813
|
+
} // Ctrl+T: cycle build/plan/compose mode
|
|
803
814
|
if (ch === '\x01') {
|
|
804
815
|
cur = 0;
|
|
805
816
|
draw();
|
|
@@ -933,6 +944,14 @@ export function createTui({ onLine, onInterrupt, onEOF, onShiftTab, completer }
|
|
|
933
944
|
ultra = !!on;
|
|
934
945
|
draw();
|
|
935
946
|
},
|
|
947
|
+
setAgentMode(mode) {
|
|
948
|
+
agentMode = mode || '';
|
|
949
|
+
draw();
|
|
950
|
+
},
|
|
951
|
+
setGoal(text) {
|
|
952
|
+
goalText = text || '';
|
|
953
|
+
draw();
|
|
954
|
+
},
|
|
936
955
|
setBgAgents(n) {
|
|
937
956
|
bgAgents = Math.max(0, n | 0);
|
|
938
957
|
draw();
|
package/src/ui.js
CHANGED