@noobdemon/noob-cli 1.10.18 → 1.10.19

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.10.18",
3
+ "version": "1.10.19",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
@@ -43,7 +43,6 @@
43
43
  "gpt-tokenizer": "^3.4.0",
44
44
  "gradient-string": "^3.0.0",
45
45
  "marked": "^15.0.12",
46
- "marked-terminal": "^7.3.0",
47
- "ora": "^8.2.0"
46
+ "marked-terminal": "^7.3.0"
48
47
  }
49
48
  }
package/src/agent.js CHANGED
@@ -5,7 +5,7 @@ import { listRoots } from "./tools.js";
5
5
  import { t } from "./i18n.js";
6
6
  import { countTokens } from "./tokens.js";
7
7
 
8
- export const SYSTEM = `You are noob, an agentic coding assistant in the spirit of Claude Code. You help with software engineering tasks by reading and editing files and running commands in the user's current working directory.
8
+ const SYSTEM = `You are noob, an agentic coding assistant in the spirit of Claude Code. You help with software engineering tasks by reading and editing files and running commands in the user's current working directory.
9
9
 
10
10
  You do NOT access anything yourself. Instead, a local runtime executes tools on your behalf: you emit a tool-call JSON block, the runtime runs it on the user's machine and replies with the result. This is fully supported — never claim you "can't access the terminal/filesystem". Just emit the tool call.
11
11
 
@@ -359,7 +359,7 @@ function memoryBlock() {
359
359
  // breadcrumbs để (a) trả lời "phiên trước làm gì" mà KHÔNG cần /resume, (b) gợi
360
360
  // ý /resume nếu user muốn tiếp tục. Bỏ qua phiên hiện tại (repl.js lọc).
361
361
  // recentSessions: [{ id, title, turns, updatedAt }] — đã sort mới → cũ.
362
- export function recentSessionsBlock(recentSessions) {
362
+ function recentSessionsBlock(recentSessions) {
363
363
  if (!recentSessions || !recentSessions.length) return "";
364
364
  const lines = [
365
365
  "# RECENT SESSIONS IN THIS WORKSPACE (newest first)",
@@ -375,7 +375,7 @@ export function recentSessionsBlock(recentSessions) {
375
375
  }
376
376
 
377
377
  // "X ago" ngắn gọn, tiếng Việt. Dùng cho noob.md mtime + recent sessions.
378
- export function relTime(ts) {
378
+ function relTime(ts) {
379
379
  if (!ts) return "—";
380
380
  const ms = Date.now() - ts;
381
381
  if (ms < 5000) return "vừa xong"; // < 5s hoặc tương lai → "vừa xong" (tránh "0s trước" xấu)
@@ -426,8 +426,10 @@ export function buildUserMessage(history) {
426
426
  function isIncompleteResponse(text) {
427
427
  if (!text) return false;
428
428
  const t = text.trimEnd();
429
- // Kết thúc bằng dấu cuối danh sách chưa đóng: "A.", "B.", "1.", "(1)" mà không có gì sau
430
- if (/[A-Z]\.\s*$/.test(t) || /\(\d+\)\s*$/.test(t) || /^\d+\.\s*$/m.test(t)) return true;
429
+ // Kết thúc bằng dấu cuối danh sách chưa đóng: "A.", "B.", "1.", "(1)" mà không có gì sau.
430
+ // Lưu ý: dùng anchor cuối CHUỖI (không phải /m anchor cuối DÒNG) list bullet hợp lệ
431
+ // ở giữa văn bản (vd "1. xxx\n2. yyy") không được coi là incomplete.
432
+ if (/[A-Z]\.\s*$/.test(t) || /\(\d+\)\s*$/.test(t) || /(^|\n)\d+\.\s*$/.test(t)) return true;
431
433
  // Kết thúc giữa câu — không có dấu câu cuối cùng (. ! ? : ; ) và không phải markdown/code
432
434
  const lastChar = t.slice(-1);
433
435
  if (lastChar && !/[.!?:;)\]"'`#>\n]/.test(lastChar) && t.length > 50) {
@@ -435,7 +437,7 @@ function isIncompleteResponse(text) {
435
437
  const lastLine = t.split("\n").pop().trim();
436
438
  if (/\s(ví|vd|hay|hoặc|và|nhưng|mà|hoac|or|and|but|e\.g|i\.e)\s*$/i.test(lastLine)) return true;
437
439
  // Dòng cuối là 1 câu bắt đầu nhưng chưa xong (có chủ ngữ nhưng không có vị ngữ hoàn chỉnh)
438
- if (/\s(Bạn|Bạn|Bạn|Bạn|Bạn|Bạn|Bạn|Bạn|Bạn|Bạn|Bạn|Bạn|Bạn)\s+(muốn|có|thấy|nên|cần|đã|đang|sẽ|chọn|chọn|chọn)\s*$/i.test(lastLine)) return true;
440
+ if (/\s(Bạn|Tôi|Mình|Anh|Em|Chị|Ông|Bà|Họ)\s+(muốn|có|thấy|nên|cần|đã|đang|sẽ|chọn|định|sắp|vừa)\s*$/i.test(lastLine)) return true;
439
441
  }
440
442
  return false;
441
443
  }
@@ -445,7 +447,7 @@ function isIncompleteResponse(text) {
445
447
  // contains its own ```code``` fences (e.g. a README), and the first inner fence
446
448
  // would close the block early and break the JSON. Instead, find the ```tool (or
447
449
  // ```json) opener and brace-match the first balanced JSON object after it.
448
- export function parseToolCall(text) {
450
+ function parseToolCall(text) {
449
451
  for (const fence of ["tool", "json"]) {
450
452
  const open = text.match(new RegExp("```" + fence + "[ \\t]*\\n"));
451
453
  if (!open) continue;
package/src/repl.js CHANGED
@@ -17,7 +17,8 @@ import { checkLatest, runUpdate, CURRENT } from "./update.js";
17
17
  import * as sessions from "./sessions.js";
18
18
  import { loadSkill, listSkills } from "./skills.js";
19
19
  import { saveWorkflow, loadWorkflow, listWorkflows, deleteWorkflow, workflowsDir } from "./workflows.js";
20
- import { getBuiltinWorkflow, listBuiltinWorkflows, loadBuiltinPrompt } from "./workflows-builtin.js";
20
+ import { getBuiltinWorkflow, listBuiltinWorkflows, loadBuiltinPrompt } from "./workflows-builtin.js";
21
+
21
22
 
22
23
  // Lệnh dùng cho autocomplete. Gõ "/l" → lọc các lệnh có "l" (login, logout,
23
24
  // clear, models, yolo…); ↑/↓ chọn, Tab điền, Enter chạy mục đang sáng.
@@ -102,7 +103,7 @@ function fileMatches(frag) {
102
103
  }
103
104
 
104
105
  // Gợi ý cho thanh nhập: /lệnh (điền-rồi-gửi) hoặc @file (chỉ chèn, gõ tiếp).
105
- export function completeInput(text) {
106
+ function completeInput(text) {
106
107
  if (text.startsWith("/") && !/\s/.test(text)) {
107
108
  const q = text.slice(1).toLowerCase();
108
109
  const items = SLASH.filter((cmd) => cmd.name.slice(1).toLowerCase().includes(q));
@@ -120,7 +121,7 @@ export function completeInput(text) {
120
121
 
121
122
  // File thật được nhắc bằng @ trong tin nhắn → thêm chú thích để model đọc nhanh,
122
123
  // đúng chỗ (bỏ qua @ không trỏ tới file có thật, vd @tên người).
123
- export function mentionedFiles(text) {
124
+ function mentionedFiles(text) {
124
125
  const out = new Set();
125
126
  const re = /(?:^|\s)@([^\s]+)/g;
126
127
  let m;
@@ -316,6 +317,7 @@ export async function startRepl(opts = {}) {
316
317
  const m = findModel(s.model);
317
318
  if (m) state.model = m;
318
319
  }
320
+ if (s.tokens) tokenMeter.restore(s.tokens); // khôi phục counter cộng dồn để hiển thị nhất quán
319
321
  updateTitle();
320
322
  // Re-arm /loop nếu phiên cũ đang chạy loop (timer/running không serialize được).
321
323
  if (s.loop && s.loop.task && s.loop.intervalMs) {
@@ -1850,15 +1852,15 @@ NGUYÊN TẮC:
1850
1852
  function fmtK(n) {
1851
1853
  return n >= 1000000 ? (n / 1000000).toFixed(1) + "M" : n >= 1000 ? (n / 1000).toFixed(1) + "k" : String(n);
1852
1854
  }
1853
- function printAnswer(text, name, color) {
1854
- if (!text?.trim()) return;
1855
- console.log("\n" + chalk.hex(color).bold(" ● " + name));
1856
- console.log(
1857
- renderMarkdown(text)
1858
- .split("\n")
1859
- .map((l) => " " + l)
1860
- .join("\n") + "\n",
1861
- );
1855
+ function printAnswer(text, name, color) {
1856
+ if (!text?.trim()) return;
1857
+ console.log("\n" + chalk.hex(color).bold(" ● " + name));
1858
+ console.log(
1859
+ renderMarkdown(text)
1860
+ .split("\n")
1861
+ .map((l) => " " + l)
1862
+ .join("\n") + "\n",
1863
+ );
1862
1864
  }
1863
1865
 
1864
1866
  // In câu trả lời theo dòng token thời gian thực. Vì model emit lời + (tuỳ chọn)
@@ -1888,25 +1890,25 @@ function makeStreamPrinter(name, color) {
1888
1890
  get suppressing() {
1889
1891
  return suppress;
1890
1892
  },
1891
- push(delta) {
1892
- buf += delta;
1893
- if (suppress) return;
1894
- const f = buf.indexOf("```tool");
1895
- if (f !== -1) {
1896
- write(buf.slice(printed, f));
1897
- printed = buf.length;
1898
- suppress = true;
1899
- return;
1900
- }
1901
- const safeEnd = Math.max(printed, buf.length - HOLD);
1902
- if (safeEnd > printed) {
1903
- write(buf.slice(printed, safeEnd));
1904
- printed = safeEnd;
1905
- }
1906
- },
1907
- flush() {
1908
- if (!suppress && printed < buf.length) write(buf.slice(printed));
1909
- if (started) process.stdout.write("\n");
1893
+ push(delta) {
1894
+ buf += delta;
1895
+ if (suppress) return;
1896
+ const f = buf.indexOf("```tool");
1897
+ if (f !== -1) {
1898
+ write(buf.slice(printed, f));
1899
+ printed = buf.length;
1900
+ suppress = true;
1901
+ return;
1902
+ }
1903
+ const safeEnd = Math.max(printed, buf.length - HOLD);
1904
+ if (safeEnd > printed) {
1905
+ write(buf.slice(printed, safeEnd));
1906
+ printed = safeEnd;
1907
+ }
1908
+ },
1909
+ flush() {
1910
+ if (!suppress && printed < buf.length) write(buf.slice(printed));
1911
+ if (started) process.stdout.write("\n");
1910
1912
  },
1911
1913
  };
1912
1914
  }
package/src/sessions.js CHANGED
@@ -109,7 +109,9 @@ export function latest(cwd = null) {
109
109
  return l.length ? load(l[0].id) : null;
110
110
  }
111
111
 
112
+ // Xoá file phiên theo id. Best-effort — trả false nếu không tồn tại / không xoá được.
112
113
  export function remove(id) {
114
+ if (!id) return false;
113
115
  try {
114
116
  fs.unlinkSync(path.join(DIR, id + ".json"));
115
117
  return true;
package/src/ui.js CHANGED
@@ -8,7 +8,7 @@ import { PROVIDERS, providerColor } from "./models.js";
8
8
  import { t } from "./i18n.js";
9
9
 
10
10
  const BRAND = ["#a78bfa", "#3b82f6", "#06b6d4"];
11
- export const brand = gradient(BRAND);
11
+ const brand = gradient(BRAND);
12
12
 
13
13
  export const c = {
14
14
  dim: chalk.hex("#6b7280"),
@@ -35,7 +35,7 @@ export function banner() {
35
35
  console.log(c.dim(" ") + brand("Noob Demon") + c.dim(" · " + t.tagline + "\n"));
36
36
  }
37
37
 
38
- export function rule(label = "") {
38
+ function rule(label = "") {
39
39
  const w = Math.min(term(), 100);
40
40
  if (!label) return c.dim("─".repeat(w));
41
41
  const head = `── ${label} `;
package/src/update.js CHANGED
@@ -23,7 +23,7 @@ function cmp(a, b) {
23
23
  return 0;
24
24
  }
25
25
 
26
- export async function fetchLatest(timeout = 2500) {
26
+ async function fetchLatest(timeout = 2500) {
27
27
  const ctrl = new AbortController();
28
28
  const t = setTimeout(() => ctrl.abort(), timeout);
29
29
  try {