@noobdemon/noob-cli 1.0.3 → 1.0.5

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.0.3",
3
+ "version": "1.0.5",
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
package/src/i18n.js CHANGED
@@ -55,7 +55,8 @@ export const t = {
55
55
  cmdLogin: "/login <key> đăng nhập bằng API key",
56
56
  cmdLogout: "/logout đăng xuất",
57
57
  cmdUsage: "/usage xem hạn mức key còn lại",
58
- cmdStatus: "/status xem mô hình + thư mục hiện tại",
58
+ cmdStatus: "/status xem mô hình, version, trạng thái yolo, thư mục",
59
+ cmdVersion: "/version /v xem version hiện tại + trạng thái yolo",
59
60
  cmdExit: "/exit /quit thoát",
60
61
  tip1: "• Mô tả việc cần làm; noob sẽ đọc/sửa file & chạy lệnh giúp bạn.",
61
62
  tip2: "• Thao tác nguy hiểm sẽ hỏi phép, trừ khi bật yolo (Shift+Tab).",
package/src/repl.js CHANGED
@@ -20,6 +20,14 @@ export async function startRepl(opts = {}) {
20
20
  yolo: !!opts.yolo,
21
21
  };
22
22
 
23
+ // Prompt = dòng trạng thái sống. Luôn phản ánh yolo + version theo thời gian
24
+ // thực (vẽ lại mỗi lượt và ngay khi Shift+Tab), nên không cần gõ /status.
25
+ const promptStr = (lead = true) => {
26
+ const nl = lead ? "\n" : "";
27
+ const yolo = state.yolo ? c.err("⚡ yolo ") : "";
28
+ return c.user(nl + t.promptYou) + yolo + c.dim("v" + CURRENT + " › ");
29
+ };
30
+
23
31
  const rl = readline.createInterface({ input: process.stdin, output: process.stdout, prompt: "" });
24
32
  let closed = false;
25
33
  const queue = []; // lines typed/piped, consumed in order
@@ -64,7 +72,8 @@ export async function startRepl(opts = {}) {
64
72
  process.stdin.on("keypress", (_str, key) => {
65
73
  if (!key || key.name !== "tab" || !key.shift) return;
66
74
  state.yolo = !state.yolo;
67
- console.log(state.yolo ? c.err("\n " + t.yoloOn) : c.ok("\n " + t.yoloOff));
75
+ console.log(state.yolo ? c.err(" " + t.yoloOn) : c.ok(" " + t.yoloOff));
76
+ rl.setPrompt(promptStr(false)); // cập nhật dòng trạng thái ngay lập tức
68
77
  rl.prompt(true);
69
78
  });
70
79
  }
@@ -88,6 +97,20 @@ export async function startRepl(opts = {}) {
88
97
  rl.prompt(true);
89
98
  });
90
99
 
100
+ // Đừng để một lỗi bất ngờ làm "tự động tắt" CLI. Nguyên nhân hay gặp:
101
+ // tiến trình cập nhật nền (spawn npm) phát sự kiện 'error' không ai bắt,
102
+ // hoặc lỗi async trong một lượt → Node thoát ngay. Ở đây bắt lại, in ra,
103
+ // rồi vẽ lại prompt để phiên làm việc vẫn sống.
104
+ process.on("uncaughtException", (err) => {
105
+ if (abort) { abort.abort(); abort = null; }
106
+ console.log(c.err("\n ✗ lỗi: " + (err?.message || err)));
107
+ if (!closed) rl.prompt(true);
108
+ });
109
+ process.on("unhandledRejection", (err) => {
110
+ console.log(c.err("\n ✗ lỗi nền: " + (err?.message || err)));
111
+ if (!closed) rl.prompt(true);
112
+ });
113
+
91
114
  banner();
92
115
  printStatus(state);
93
116
  if (!config.apiKey) console.log("\n" + c.tool(" " + t.notLoggedIn) + "\n");
@@ -112,18 +135,26 @@ export async function startRepl(opts = {}) {
112
135
 
113
136
  // Main loop — runs until /exit, double Ctrl+C, or EOF. Never exits after a task.
114
137
  while (true) {
115
- const raw = await ask(c.user("\n" + t.promptYou) + c.dim("› "));
138
+ const raw = await ask(promptStr());
116
139
  if (raw == null) break; // stdin fully closed and drained
117
140
  const input = raw.trim();
118
141
  if (!input) continue;
119
- if (input.startsWith("/")) {
120
- const done = await command(input);
121
- if (done) break;
122
- continue;
142
+ // Bọc cả lượt: một lỗi trong xử lý lệnh/agent không được phép thoát ra
143
+ // ngoài vòng lặp (sẽ rơi vào .catch ở bin/noob.js → process.exit(1) =
144
+ // "tự động tắt"). Bắt ở đây, in lỗi, rồi tiếp tục vòng lặp.
145
+ try {
146
+ if (input.startsWith("/")) {
147
+ const done = await command(input);
148
+ if (done) break;
149
+ continue;
150
+ }
151
+ await handle(input);
152
+ } catch (err) {
153
+ printError(err);
123
154
  }
124
- await handle(input);
125
155
  }
126
156
  rl.close();
157
+ process.exit(0);
127
158
 
128
159
  // ── turn handler ─────────────────────────────────────────────────────────
129
160
  async function handle(text) {
@@ -277,6 +308,10 @@ export async function startRepl(opts = {}) {
277
308
  case "status":
278
309
  printStatus(state);
279
310
  break;
311
+ case "version":
312
+ case "v":
313
+ console.log(c.dim(" noob ") + c.accent("v" + CURRENT) + (state.yolo ? c.err(" ⚡ yolo: BẬT") : c.dim(" yolo: tắt")));
314
+ break;
280
315
  case "exit":
281
316
  case "quit":
282
317
  case "q":
@@ -337,7 +372,10 @@ export async function startRepl(opts = {}) {
337
372
  const mode =
338
373
  s.mode === "merge" ? c.tool("Merge AI") : s.mode === "search" ? c.accent("Tìm web") : modelBadge(s.model);
339
374
  const key = config.apiKey ? c.ok(" 🔑") : c.err(" 🔒");
340
- console.log(" " + mode + key + (s.yolo ? c.err(" yolo") : "") + c.dim(" thư mục: " + shortCwd()));
375
+ const yolo = s.yolo ? c.err(" yolo: BẬT") : c.dim(" yolo: tắt");
376
+ console.log(
377
+ " " + mode + key + yolo + c.dim(" v" + CURRENT) + c.dim(" thư mục: " + shortCwd()),
378
+ );
341
379
  }
342
380
  }
343
381
 
@@ -399,6 +437,7 @@ function printHelp() {
399
437
  " " + t.cmdUpdate,
400
438
  " " + t.cmdClear,
401
439
  " " + t.cmdStatus,
440
+ " " + t.cmdVersion,
402
441
  " " + t.cmdExit,
403
442
  "",
404
443
  chalk.bold(t.helpTips),
package/src/update.js CHANGED
@@ -61,6 +61,11 @@ export function runUpdate({ background = false } = {}) {
61
61
  }
62
62
  if (background) {
63
63
  const child = spawn(cmd, args, { detached: true, stdio: "ignore", env, shell: isWin });
64
+ // QUAN TRỌNG: nếu spawn lỗi (vd npm không có trong PATH, hoặc shell trục
65
+ // trặc trên Windows) mà không có listener 'error', Node sẽ ném
66
+ // uncaughtException → tiến trình tự tắt ngay sau khi khởi động. Nuốt lỗi ở
67
+ // đây vì cập nhật nền chỉ là "best effort".
68
+ child.on("error", () => {});
64
69
  child.unref();
65
70
  return Promise.resolve(true);
66
71
  }