@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 CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noobdemon/noob-cli",
3
- "version": "1.12.15",
3
+ "version": "1.12.16",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
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);
@@ -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
@@ -53,6 +53,7 @@ export function banner() {
53
53
  const tag = chalk.hex('#e8e3d8').italic(t.tagline);
54
54
  const meta = ' ' + c.dim('agentic terminal') + ' ' + diamond + ' ' + tag;
55
55
  console.log('\n' + wordmark);
56
+ console.log('');
56
57
  console.log(meta + '\n');
57
58
  }
58
59