@noobdemon/noob-cli 1.13.3 → 1.13.4
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/CHANGELOG.md +11 -0
- package/package.json +1 -1
- package/src/i18n.js +1 -0
- package/src/repl.js +16 -1
- package/src/tools.js +4 -1
- package/src/tui.js +25 -5
package/CHANGELOG.md
CHANGED
|
@@ -2,6 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
Tất cả thay đổi đáng kể của `@noobdemon/noob-cli` được ghi vào file này.
|
|
4
4
|
|
|
5
|
+
## [1.13.4] - 2026-06-29
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
- **Ctrl+Backspace / Ctrl+W xoá nguyên từ** (`src/tui.js`): trên thanh nhập, Ctrl+Backspace (`\x08`/Ctrl+H ở phần lớn terminal) và Ctrl+W xoá cả từ trước con trỏ thay vì từng ký tự. Dùng cùng quy ước ranh giới từ với di chuyển từ (khoảng trắng + chip ảnh dán tính là ranh giới).
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- **Chặn slash command khi model đang chạy** (`src/repl.js` + `src/i18n.js`): gõ slash (`/kc`, `/model`, …) lúc task đang chạy không còn bị xếp hàng / chèn nhầm cho AI — in cảnh báo `⏳ model đang chạy — chờ xong task mới dùng được lệnh` và bỏ qua. Tin nhắn thường vẫn xếp hàng/chèn cho AI như cũ.
|
|
12
|
+
|
|
13
|
+
### Verified
|
|
14
|
+
- `npm test` 109/109 pass.
|
|
15
|
+
|
|
5
16
|
## [1.13.3] - 2026-06-29
|
|
6
17
|
|
|
7
18
|
### Fixed
|
package/package.json
CHANGED
package/src/i18n.js
CHANGED
|
@@ -12,6 +12,7 @@ export const t = {
|
|
|
12
12
|
running: 'đang chạy…',
|
|
13
13
|
denied: 'đã từ chối',
|
|
14
14
|
queued: (n, txt) => `⏎ đã xếp hàng [${n}] · gửi khi model xong: ${txt}`,
|
|
15
|
+
slashBusy: '⏳ model đang chạy — chờ xong task mới dùng được lệnh (Ctrl+C để dừng)',
|
|
15
16
|
queueCleared: (n) => `(đã xoá ${n} tin đang xếp hàng)`,
|
|
16
17
|
steerHint:
|
|
17
18
|
'💬 Gõ + Enter bất cứ lúc nào để chèn ý cho AI giữa chừng (không ngắt task đang chạy).',
|
package/src/repl.js
CHANGED
|
@@ -164,6 +164,12 @@ export async function startRepl(opts = {}) {
|
|
|
164
164
|
onLine: (line) => {
|
|
165
165
|
// Submit khi KHÔNG có read() đang chờ = tin xếp hàng. Đang chạy task → sẽ
|
|
166
166
|
// CHÈN cho AI ở bước kế tiếp (steering); rảnh → gửi như lượt mới.
|
|
167
|
+
// ponytail: slash command lúc busy KHÔNG vào pending — chặn, bắt user chờ
|
|
168
|
+
// model xong task mới dùng lệnh (tránh lệnh bị xếp hàng / chèn nhầm cho AI).
|
|
169
|
+
if (abort && line.trim().startsWith('/')) {
|
|
170
|
+
tui.print(c.dim(' ' + t.slashBusy));
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
167
173
|
pending.push(line);
|
|
168
174
|
tui.print(
|
|
169
175
|
abort
|
|
@@ -1869,7 +1875,16 @@ NGUYÊN TẮC:
|
|
|
1869
1875
|
return await execToolCore(name, input, { retried: true });
|
|
1870
1876
|
}
|
|
1871
1877
|
console.log(c.err(' ✗ ' + err.message));
|
|
1872
|
-
|
|
1878
|
+
// Framing: lỗi tool = GỌI SAI CÁCH, không phải mất quyền. Model có lịch sử
|
|
1879
|
+
// diễn giải nhầm 'ERROR:' thành 'mình không có quyền truy cập tool' rồi bỏ
|
|
1880
|
+
// cuộc. Nói rõ model VẪN có quyền, chỉ cần sửa tham số rồi gọi lại.
|
|
1881
|
+
return {
|
|
1882
|
+
allow: true,
|
|
1883
|
+
result:
|
|
1884
|
+
'ERROR: ' +
|
|
1885
|
+
err.message +
|
|
1886
|
+
'\n[Đây là lỗi do tham số gọi tool chưa đúng, KHÔNG phải mất quyền truy cập. Bạn VẪN có quyền chạy tool này — đọc kỹ thông báo lỗi, sửa tham số (path/old_string/...) rồi GỌI LẠI. Đừng kết luận là không truy cập được.]',
|
|
1887
|
+
};
|
|
1873
1888
|
}
|
|
1874
1889
|
}
|
|
1875
1890
|
|
package/src/tools.js
CHANGED
|
@@ -373,7 +373,10 @@ export const TOOLS = {
|
|
|
373
373
|
checkAbort(signal);
|
|
374
374
|
const file = abs(p);
|
|
375
375
|
const data = await fs.readFile(file, 'utf8');
|
|
376
|
-
if (old_string === new_string)
|
|
376
|
+
if (old_string === new_string)
|
|
377
|
+
throw new Error(
|
|
378
|
+
`old_string and new_string are identical in ${rel(file)} — no change to make. This is a CALL mistake, NOT a permissions/access problem: you DO have edit access. Set new_string to the DIFFERENT text you want, then call edit_file again.`
|
|
379
|
+
);
|
|
377
380
|
const useCRLF = data.includes('\r\n');
|
|
378
381
|
const adapt = (s) => {
|
|
379
382
|
const lf = s.replace(/\r\n/g, '\n');
|
package/src/tui.js
CHANGED
|
@@ -322,9 +322,7 @@ export function createTui({ onLine, onInterrupt, onEOF, onShiftTab, onCtrlT, com
|
|
|
322
322
|
if (x.image) return c.dim(imageLabel(x));
|
|
323
323
|
if (x.paste === undefined) return x.c;
|
|
324
324
|
const preview = pastePreview(x.paste);
|
|
325
|
-
const label = preview
|
|
326
|
-
? `[pasted ${x.lines} lines: "${preview}"]`
|
|
327
|
-
: `[pasted ${x.lines} lines]`;
|
|
325
|
+
const label = preview ? `[pasted ${x.lines} lines: "${preview}"]` : `[pasted ${x.lines} lines]`;
|
|
328
326
|
return c.dim(label);
|
|
329
327
|
};
|
|
330
328
|
const coloredInput = () => {
|
|
@@ -424,7 +422,8 @@ export function createTui({ onLine, onInterrupt, onEOF, onShiftTab, onCtrlT, com
|
|
|
424
422
|
let body = l;
|
|
425
423
|
if (i === curLine) {
|
|
426
424
|
if (colInLine >= l.length) body = l + `${REV} ${UNREV}`;
|
|
427
|
-
else
|
|
425
|
+
else
|
|
426
|
+
body = l.slice(0, colInLine) + `${REV}${l[colInLine]}${UNREV}` + l.slice(colInLine + 1);
|
|
428
427
|
}
|
|
429
428
|
return (i === 0 ? promptLabel : indent) + body;
|
|
430
429
|
})
|
|
@@ -716,6 +715,19 @@ export function createTui({ onLine, onInterrupt, onEOF, onShiftTab, onCtrlT, com
|
|
|
716
715
|
histPos = null;
|
|
717
716
|
}
|
|
718
717
|
}
|
|
718
|
+
// Ctrl+Backspace / Ctrl+W: xoá nguyên từ trước con trỏ (cùng quy ước ranh giới
|
|
719
|
+
// từ với moveWordLeft — khoảng trắng + chip dán tính là ranh giới).
|
|
720
|
+
function deleteWordLeft() {
|
|
721
|
+
const start = cur;
|
|
722
|
+
while (cur > 0 && charAt(cur - 1) === ' ') cur -= 1;
|
|
723
|
+
if (cur > 0 && charAt(cur - 1) === null)
|
|
724
|
+
cur -= 1; // qua 1 chip
|
|
725
|
+
else while (cur > 0 && charAt(cur - 1) !== null && charAt(cur - 1) !== ' ') cur -= 1;
|
|
726
|
+
if (cur < start) {
|
|
727
|
+
cells.splice(cur, start - cur);
|
|
728
|
+
histPos = null;
|
|
729
|
+
}
|
|
730
|
+
}
|
|
719
731
|
// null = ô dán (coi như ranh giới từ); ngược lại trả ký tự của ô.
|
|
720
732
|
const charAt = (k) =>
|
|
721
733
|
cells[k] && cells[k].paste === undefined && !cells[k].image ? cells[k].c : null;
|
|
@@ -923,7 +935,15 @@ export function createTui({ onLine, onInterrupt, onEOF, onShiftTab, onCtrlT, com
|
|
|
923
935
|
i++;
|
|
924
936
|
continue;
|
|
925
937
|
}
|
|
926
|
-
if (ch === '\
|
|
938
|
+
if (ch === '\x08' || ch === '\x17') {
|
|
939
|
+
// Ctrl+Backspace (nhiều terminal gửi \x08 = Ctrl+H) và Ctrl+W: xoá 1 từ.
|
|
940
|
+
deleteWordLeft();
|
|
941
|
+
refreshMenu();
|
|
942
|
+
draw();
|
|
943
|
+
i++;
|
|
944
|
+
continue;
|
|
945
|
+
} // Ctrl+Backspace / Ctrl+W
|
|
946
|
+
if (ch === '\x7f') {
|
|
927
947
|
backspace();
|
|
928
948
|
refreshMenu();
|
|
929
949
|
draw();
|