deel-local-cli 2.0.0 → 2.0.3

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.
Files changed (65) hide show
  1. package/README.ko.md +14 -5
  2. package/README.md +14 -5
  3. package/package.json +1 -1
  4. package/src/acp/serve.js +49 -12
  5. package/src/agent/commit.js +2 -2
  6. package/src/agent/compact.js +4 -4
  7. package/src/agent/grade.js +1 -1
  8. package/src/agent/loop.js +2 -2
  9. package/src/agent/memory.js +1 -1
  10. package/src/agent/models.js +1 -1
  11. package/src/agent/outschema.js +1 -1
  12. package/src/agent/pins.js +7 -2
  13. package/src/agent/project.js +1 -1
  14. package/src/agent/recall.js +1 -1
  15. package/src/agent/store.js +13 -26
  16. package/src/backend/ctxsize.js +2 -2
  17. package/src/backend/http.js +1 -1
  18. package/src/backend/learn.js +27 -1
  19. package/src/backend/mcp.js +97 -16
  20. package/src/backend/proxy.js +1 -1
  21. package/src/backend/quota.js +2 -2
  22. package/src/commands/common.js +2 -2
  23. package/src/commands/extend.js +0 -1
  24. package/src/commands/model.js +1 -2
  25. package/src/commands.js +4 -2
  26. package/src/completion.js +13 -3
  27. package/src/config.js +35 -13
  28. package/src/configexplain.js +3 -2
  29. package/src/doctor.js +1 -2
  30. package/src/jsonl.js +42 -0
  31. package/src/lsp/client.js +6 -46
  32. package/src/oneshot.js +22 -0
  33. package/src/pack/selfpack.js +2 -2
  34. package/src/pack/zip.js +18 -1
  35. package/src/plugins/manage.js +81 -14
  36. package/src/reap.js +70 -0
  37. package/src/report.js +1 -1
  38. package/src/safety/audit.js +4 -19
  39. package/src/safety/authcmd.js +1 -1
  40. package/src/safety/guard.js +81 -7
  41. package/src/safety/hooks.js +1 -1
  42. package/src/safety/keystore.js +18 -3
  43. package/src/safety/shellenv.js +1 -1
  44. package/src/safety/undo.js +44 -20
  45. package/src/skills/discover.js +1 -1
  46. package/src/tools/doc2md.js +25 -3
  47. package/src/tools/docs.js +3 -3
  48. package/src/tools/edit-match.js +1 -1
  49. package/src/tools/encoding.js +1 -1
  50. package/src/tools/excel-com.js +56 -30
  51. package/src/tools/fastgrep.js +65 -10
  52. package/src/tools/fsutil.js +8 -4
  53. package/src/tools/ignore.js +21 -1
  54. package/src/tools/index.js +77 -7
  55. package/src/tools/jobs.js +27 -5
  56. package/src/tools/pdf.js +1 -3
  57. package/src/tools/shell.js +27 -0
  58. package/src/tools/todo.js +1 -1
  59. package/src/tools/verify.js +17 -3
  60. package/src/tools/webfetch.js +12 -2
  61. package/src/tools/xlsx.js +4 -4
  62. package/src/ui/banner.js +1 -1
  63. package/src/ui/inputbox.js +1 -1
  64. package/src/ui/md.js +1 -1
  65. package/src/ui/motion.js +1 -1
package/src/tools/jobs.js CHANGED
@@ -25,9 +25,10 @@
25
25
  */
26
26
  import { spawn } from 'node:child_process';
27
27
  import { decode as decodeBytes, consoleCodepage } from './encoding.js';
28
- import { 셸명령 } from './shell.js';
28
+ import { 셸명령, 셸한계넘나 } from './shell.js';
29
29
  import { 셸환경 } from '../safety/shellenv.js';
30
30
  import { 말, 세말 } from '../i18n/index.js';
31
+ import { 신호에거두기 } from '../reap.js';
31
32
 
32
33
  /* 결과 한 줄을 잇는다 — 빈 조각은 버린다(tools/index.js 의 이어 와 같은 것). */
33
34
  const 이어 = (...조각들) => 조각들.filter((x) => x != null && String(x) !== '').join(' · ');
@@ -313,10 +314,13 @@ export function 무리끊기(pid, { 곧장 = false, 늦게 = 800 } = {}) {
313
314
  * 기다림은 최선이 아니라 모두가 내는 값**이라 짧게 못 박는다. SIGKILL 은
314
315
  * 그대로 나가므로, 신호를 무시하는 손자를 살려 보내는 일은 없다.
315
316
  */
316
- const 못재고기다릴최대 = 400;
317
+ // 검사도 이 값을 쓴다 (test/jobs.test.js 의 「상한이 유닉스 갈래에서도
318
+ // 지켜진다」). 거기서 400 을 손으로 적어 두면, 이 값을 고친 날 그 검사는
319
+ // **조용히 아무것도 안 가르는 검사**가 된다.
320
+ export const 못재고기다릴최대 = 400;
317
321
 
318
322
  /** 무리끊기 의 **기다릴 수 있는** 판. 까닭은 바로 위 머리말에. */
319
- export async function 무리끊기기다려(pid, { 곧장 = false, 늦게 = 800 } = {}) {
323
+ async function 무리끊기기다려(pid, { 곧장 = false, 늦게 = 800 } = {}) {
320
324
  if (process.platform === 'win32' || !pid) return;
321
325
  if (곧장) { 신호주기(pid, 'SIGKILL'); return; }
322
326
  신호주기(pid, 'SIGTERM');
@@ -347,6 +351,19 @@ export async function 무리끊기기다려(pid, { 곧장 = false, 늦게 = 800
347
351
  export function 나무죽이기(kid, { 파이프끊기: 끊을까 = true, 곧장 = false, 상한 = 1500, 뿌리살리기 = true } = {}) {
348
352
  if (!kid) return true;
349
353
  let 나무끊었나 = true;
354
+ /*
355
+ * ── 끝난 것으로 본 아이에게는 taskkill 을 안 보낸다 (2.0.2 · 28회차 flake) ──────────
356
+ *
357
+ * Node 는 아이가 끝났다고 알리는 그 자리에서 프로세스 손잡이를 닫는다. 손잡이가 닫히면 윈도는
358
+ * 그 번호를 **곧바로 딴 프로세스에게 준다.** 그 뒤의 `taskkill /pid N /t` 는 우리 아이가 아니라
359
+ * 그 번호를 새로 받은 **남의 나무**를 끊는다 — 윈도 CI 에서 옆 대조군이 말없이 멈춘 것(28회차)이
360
+ * 이 모양이다. 상한에 잘려 다시 오는 길(뿌리살리기)에서 특히 그렇다: 첫 taskkill 이 뿌리를
361
+ * 죽이고 나서 두 번째가 같은 번호로 간다.
362
+ *
363
+ * 잃는 것은 없다. 뿌리가 끝난 뒤에는 /t 가 어차피 그 아래를 못 훑는다(남은무리끊기 머리말).
364
+ * 아직 안 끝난 아이는 손잡이가 열려 있어 번호가 안 바뀐다 — 그때만 보낸다.
365
+ */
366
+ const 끝난아이 = typeof kid.exitCode === 'number' || typeof kid.signalCode === 'string';
350
367
  if (process.platform === 'win32' && kid.pid) {
351
368
  /*
352
369
  * 상한은 부르는 쪽이 정한다. 여기는 끝날 때도 지나가는 자리라
@@ -354,7 +371,7 @@ export function 나무죽이기(kid, { 파이프끊기: 끊을까 = true, 곧장
354
371
  * 그래서 기본은 짧다. 사람이 「끝내」 라고 시킨 자리에서는 길게 준다 —
355
372
  * 거기서는 몇백 ms 늦는 것보다 안 죽는 것이 훨씬 나쁘다.
356
373
  */
357
- 나무끊었나 = 나무끊기(kid.pid, 상한);
374
+ if (!끝난아이) 나무끊었나 = 나무끊기(kid.pid, 상한);
358
375
  /*
359
376
  * 나무를 다 못 훑었으면 뿌리를 살려 둔다. 부르는 쪽이 다시 온다.
360
377
  *
@@ -672,8 +689,11 @@ export async function 띄우기(명령, { cwd, 설명 = null, 기다림 = 1500,
672
689
  return { error: `뒤에서 도는 명령이 이미 ${도는것.length}개입니다. Jobs 로 안 쓰는 것을 끝내고 다시 하세요.` };
673
690
  }
674
691
 
675
- const j = new 일감(다음번호++, 명령, 설명);
676
692
  const shell = 셸명령(명령);
693
+ // cmd 의 한 줄 한계를 넘으면 띄우기 전에 말한다 (tools/shell.js 셸한계넘나 · 2.0.2 S6). 번호를 받기 전이다.
694
+ const 한계 = 셸한계넘나(명령, shell);
695
+ if (한계) return { error: 한계 };
696
+ const j = new 일감(다음번호++, 명령, 설명);
677
697
  let kid;
678
698
  try {
679
699
  kid = spawn(shell.file, shell.args, {
@@ -915,6 +935,8 @@ export function 비우기() { 모두끝내기(); 다음번호 = 1; 치운것 = 0
915
935
 
916
936
  // 어떤 길로 끝나든 남기지 않는다. repl·oneshot 이 부르는 것과 겹쳐도 무해하다.
917
937
  process.once('exit', () => { try { 모두끝내기(); } catch { /* 끝나는 중이라 할 수 있는 게 없다 */ } });
938
+ // 신호(SIGINT·SIGTERM)로 끝나면 'exit' 이 안 돈다 — 그 길도 거둔다 (reap.js 머리말 · 2.0.2 M3).
939
+ 신호에거두기(모두끝내기);
918
940
 
919
941
  /*
920
942
  * ── 도구 ──────────────────────────────────────────────────────────────
package/src/tools/pdf.js CHANGED
@@ -1070,16 +1070,14 @@ function 글꼴만들기(문, 글꼴사전) {
1070
1070
  const 너비 = 너비표읽기(문, 안, 두바이트);
1071
1071
 
1072
1072
  // ① /ToUnicode 가 있으면 그것이 가장 정확하다.
1073
- let 표 = new Map();
1074
1073
  let 폭 = 두바이트 ? 2 : 1;
1075
1074
  const 투 = 문.풀기(안.ToUnicode);
1076
1075
  if (투?.날것) {
1077
1076
  const r = 흐름풀기(투, (x) => 문.풀기(x));
1078
1077
  if (r.ok) {
1079
1078
  const 읽은것 = 유니코드표읽기(r.자료.toString('latin1'));
1080
- 표 = 읽은것.표;
1081
1079
  if (읽은것.폭) 폭 = 읽은것.폭;
1082
- if (표.size) return { 폭, 표, 읽나: true, 너비 };
1080
+ if (읽은것.표.size) return { 폭, 표: 읽은것.표, 읽나: true, 너비 };
1083
1081
  }
1084
1082
  }
1085
1083
 
@@ -100,6 +100,33 @@ export function 정한셸() {
100
100
  /** 검사용 — 다음 정한셸() 이 다시 고르게. */
101
101
  export function 셸지우기() { 지금 = null; }
102
102
 
103
+ /*
104
+ * ── cmd.exe 는 한 줄에 8,191자까지다 (2.0.2 · S6) ─────────────────────────────
105
+ *
106
+ * cmd 자체의 한계라 우리가 늘릴 수 없다. 넘기면 cmd 가 「The command line is too long.」 한 줄을
107
+ * 내고 **아무것도 안 돌린다** — 그런데 한국어 윈도에서는 그 줄이 코드페이지째 깨져 오고, 모델은
108
+ * 제 명령이 왜 안 돌았는지 모른 채 같은 긴 명령을 또 낸다. 넘길 것이면 넘기기 전에 사람 말로
109
+ * 멈추고, 어떻게 하면 되는지(파일로 써서 돌리기)를 같이 말한다. Git Bash·파워셸은 이 한계가 없다.
110
+ *
111
+ * 8,191자는 명령만이 아니라 **cmd 가 받은 한 줄 전체**다 — `"C:\WINDOWS\system32\cmd.exe" /d /s /c "…"`.
112
+ * 재 보니 이 PC 에서 명령에 쓸 수 있는 것은 8,152자였다(cmd 자리 27자 + 앞뒤 붙이는 것). 그래서 cmd 자리와
113
+ * 붙이는 글자만큼 빼고 잰다. 붙이는 것은 ` /d /s /c ` 10자 · 명령을 싸는 따옴표 2자에 2자를 더 둔다.
114
+ */
115
+ const cmd한줄최대 = 8191;
116
+
117
+ /** 이 cmd 로 명령에 쓸 수 있는 글자 수. */
118
+ export function cmd명령최대(file = 'cmd.exe') { return cmd한줄최대 - (String(file).length + 14); }
119
+
120
+ /** cmd 로 넘기면 한 줄 한계를 넘나. 넘으면 모델에게 줄 말, 아니면 null. */
121
+ export function 셸한계넘나(cmd, { id, file } = 정한셸()) {
122
+ if (id !== 'cmd') return null;
123
+ const n = String(cmd ?? '').length;
124
+ const 최대 = cmd명령최대(file);
125
+ if (n <= 최대) return null;
126
+ return `명령이 ${n.toLocaleString('en-US')}자입니다 — cmd.exe 는 한 줄에 ${cmd한줄최대.toLocaleString('en-US')}자까지만 받습니다(cmd 자리를 빼면 명령에는 ${최대.toLocaleString('en-US')}자 · cmd 자체 한계라 돌리지 않았습니다).`
127
+ + ' 긴 내용은 Write 로 파일(.bat · .ps1 · .js 등)에 쓰고 그 파일을 돌리세요. Git Bash 를 깔면 이 한계가 없습니다.';
128
+ }
129
+
103
130
  /** 명령 하나를 셸에 넘길 모양. Bash 도구와 Jobs 가 둘 다 이걸 쓴다. */
104
131
  export function 셸명령(cmd) {
105
132
  const s = 정한셸();
package/src/tools/todo.js CHANGED
@@ -13,7 +13,7 @@ import { 말 } from '../i18n/index.js';
13
13
 
14
14
  /* 결과 한 줄을 잇는다 — 빈 조각은 버린다(tools/index.js 의 이어 와 같은 것). */
15
15
  const 이어 = (...조각들) => 조각들.filter((x) => x != null && String(x) !== '').join(' · ');
16
- export const STATES = ['todo', 'doing', 'done'];
16
+ const STATES = ['todo', 'doing', 'done'];
17
17
 
18
18
  const 표시 = { todo: '☐', doing: '▶', done: '☑' };
19
19
 
@@ -97,10 +97,16 @@ export function html보기(글, { 있는파일 = () => true } = {}) {
97
97
  const 탈 = [];
98
98
  const 쌓임 = [];
99
99
 
100
- // 주석과 script/style 안엣것은 빼고 본다 — 그 안의 `<` 는 태그가 아니다.
100
+ /*
101
+ * 주석과 script/style 안엣것은 빼고 본다 — 그 안의 `<` 는 태그가 아니다.
102
+ *
103
+ * 닫는 태그 무늬는 아래 참조 훑기와 **같은 잣대**여야 한다. 한쪽만 넓히면
104
+ * 같은 글을 두 자리가 다르게 읽는다 — 19회차에 아래만 고치고 여기를 지나칠
105
+ * 뻔했다. 한 자리를 고칠 때 같은 파일에 같은 병이 또 있나를 먼저 본다.
106
+ */
101
107
  const 뼈 = String(글)
102
108
  .replace(/<!--[\s\S]*?-->/g, '')
103
- .replace(/<(script|style)\b[^>]*>[\s\S]*?<\/\1\s*>/gi, '<$1></$1>');
109
+ .replace(/<(script|style)\b[^>]*>[\s\S]*?<\/\1(?:[\s/][^>]*)?>/gi, '<$1></$1>');
104
110
 
105
111
  for (const m of 뼈.matchAll(/<(\/?)([a-zA-Z!][a-zA-Z0-9-]*)([^>]*)>/g)) {
106
112
  const 닫는가 = m[1] === '/';
@@ -165,7 +171,15 @@ export function html보기(글, { 있는파일 = () => true } = {}) {
165
171
  */
166
172
  const 참조볼글 = String(글)
167
173
  .replace(/<!--[\s\S]*?-->/g, '')
168
- .replace(/(<(script|style)\b[^>]*>)[\s\S]*?(<\/\2\s*>)/gi, '$1$3');
174
+ /*
175
+ * 닫는 태그에 속성이 붙어도 브라우저는 닫는 것으로 본다 — webfetch.js 와 같은 잣대.
176
+ *
177
+ * 그런데 그 닫는 태그를 **그대로 남기면** 안 된다. `</script src="x.js">`
178
+ * 의 src 가 아래 참조 훑기에 걸려 「x.js 가 없습니다」 라는 **거짓 탈**이
179
+ * 된다(2차 눈이 재현해 줬다). 여는 태그는 진짜 참조라 남기고, 닫는
180
+ * 태그는 이름만 남겨 껍데기로 적는다.
181
+ */
182
+ .replace(/(<(script|style)\b[^>]*>)[\s\S]*?<\/\2(?:[\s/][^>]*)?>/gi, '$1</$2>');
169
183
  for (const m of 참조볼글
170
184
  .matchAll(/(?<![-\w.])(?:src|href)\s*=\s*(?:"([^"]*)"|'([^']*)')/gi)) {
171
185
  const 곳 = m[1] ?? m[2];
@@ -213,7 +213,17 @@ function 태그벗기기(html) {
213
213
  * 진짜 `</script>` 까지 본문을 통째로 먹고 `<!--` 만 남겼다. 브라우저는 **먼저 열린 쪽**을
214
214
  * 따른다 — 한 무늬의 갈래로 두면 정규식도 왼쪽에서 먼저 걸리는 쪽을 따른다.
215
215
  */
216
- 글 = 글.replace(/<!--[\s\S]*?-->|<script\b[\s\S]*?<\/script\s*>|<style\b[\s\S]*?<\/style\s*>/gi, ' ');
216
+ /*
217
+ * 닫는 태그는 `</script>` 만이 아니다 (2.0.0 CodeQL js/bad-tag-filter).
218
+ * 브라우저는 `</script foo>` 도, 줄바꿈이 낀 `</script\n>` 도, 빗금으로
219
+ * 바로 닫는 `</script/>` 도 닫는 것으로 본다. `\s*>` 로만 두면 그런 꼴을
220
+ * 못 잡고 **스크립트 속이 글로 새어** 모델에게 그대로 간다.
221
+ *
222
+ * 빗금 갈래를 빼먹었더니 아래 「안 닫힌 것은 끝까지」 가 그 뒤를 통째로
223
+ * 먹어 **본문이 통째로 사라졌다**(2차 눈이 재현해 줬다). 못 걸러 새는
224
+ * 것만 흠이 아니라, 멀쩡한 글을 지우는 것도 같은 무게의 흠이다.
225
+ */
226
+ 글 = 글.replace(/<!--[\s\S]*?-->|<script\b[\s\S]*?<\/script(?:[\s/][^>]*)?>|<style\b[\s\S]*?<\/style(?:[\s/][^>]*)?>/gi, ' ');
217
227
  if (글 === 전) break;
218
228
  }
219
229
  /*
@@ -293,7 +303,7 @@ export function 웹되돌림(다음, { allowPrivate = false } = {}) {
293
303
  * 첫 주소만 이름을 풀어 보면 소용이 없다. 공개 주소로 시작해 302 한 번으로
294
304
  * 사내로 들어가는 것이 가장 흔한 길이라, 홉마다 같은 것을 물어야 한다.
295
305
  */
296
- export async function 웹되돌림검사(다음, { allowPrivate = false } = {}) {
306
+ async function 웹되돌림검사(다음, { allowPrivate = false } = {}) {
297
307
  웹되돌림(다음, { allowPrivate });
298
308
  if (allowPrivate) return;
299
309
  const 푼것 = await 사설로풀리나(다음.hostname);
package/src/tools/xlsx.js CHANGED
@@ -232,12 +232,12 @@ function 날짜로(n) {
232
232
  * 이 밖을 가리키는 주소는 엑셀이 만든 파일에서 나올 수 없다. 나왔다면 누가
233
233
  * 손으로 적은 바이트다.
234
234
  */
235
- export const 엑셀최대행 = 1048576;
236
- export const 엑셀최대열 = 16384;
235
+ const 엑셀최대행 = 1048576;
236
+ const 엑셀최대열 = 16384;
237
237
  /** 빈 줄·빈 열이 이만큼 넘게 이어지면 자리를 안 지키고 건너뛰었다고 말한다. */
238
- export const 빈틈상한 = 1000;
238
+ const 빈틈상한 = 1000;
239
239
  /** 펼친 표(줄 × 폭)의 칸 수 상한. 넘는 아래쪽 줄은 빼고 뺐다고 말한다. */
240
- export const 칸수상한 = 1_000_000;
240
+ const 칸수상한 = 1_000_000;
241
241
 
242
242
  const 천단위 = (n) => String(n).replace(/\B(?=(\d{3})+(?!\d))/g, ',');
243
243
 
package/src/ui/banner.js CHANGED
@@ -95,7 +95,7 @@ const 들여 = ' ';
95
95
  const 채움 = '█';
96
96
 
97
97
  /** 글자 하나가 몇 칸인가 (한 배 기준). */
98
- export const 글자폭 = (ch) => (글꼴[ch]?.[0]?.length ?? 0);
98
+ const 글자폭 = (ch) => (글꼴[ch]?.[0]?.length ?? 0);
99
99
 
100
100
  /** 이름 전체가 몇 칸인가. @param 배 가로 배율(1 또는 2) */
101
101
  export function 이름폭(배 = 1, 글자수 = 이름.length) {
@@ -74,7 +74,7 @@ const 앞머리 = 5;
74
74
  // 상자가 차지할 최대 줄 수. 긴 글을 붙여넣어도 화면을 다 먹지 않게.
75
75
  export const 안쪽최대 = 8;
76
76
  // 일하는 중에 미리 쳐 두는 글은 이보다 좁게 보인다 — 위쪽 '일하는 중' 줄을 안 밀어내게.
77
- export const 대기최대 = 3;
77
+ const 대기최대 = 3;
78
78
 
79
79
  /**
80
80
  * 상자를 이룰 줄들과 커서 자리를 값으로 내놓는다.
package/src/ui/md.js CHANGED
@@ -304,7 +304,7 @@ export function 표그리기(줄들, 폭 = 80, 어떻게 = {}) {
304
304
  * 상한에 걸려 표를 나눠 그릴 때, 뒤 묶음에 앞 묶음의 너비를 그대로 물려 주려고
305
305
  * 쓴다. 묶음마다 제 폭을 다시 재면 같은 표인데 위아래의 세로줄이 안 맞는다.
306
306
  */
307
- export function 표칸너비(줄들, 폭 = 80) {
307
+ function 표칸너비(줄들, 폭 = 80) {
308
308
  return 짜기(줄들, 폭)?.너비 ?? null;
309
309
  }
310
310
 
package/src/ui/motion.js CHANGED
@@ -231,7 +231,7 @@ export const 틀 = Object.fromEntries(
231
231
  * 상자 높이와 커서 셈이 통째로 달라진다. 그래서 **가로로 누운 옆모습**으로
232
232
  * 그린다 — 세로로 선 정면 모습은 4점 안에서 절대 안 읽힌다.
233
233
  */
234
- export const 넓은칸수 = 6; // 점자 여섯 칸
234
+ const 넓은칸수 = 6; // 점자 여섯 칸
235
235
 
236
236
  /*
237
237
  * 중세 기사 — 옆모습. 왼쪽에 몸이 서 있고 오른쪽에서 칼이 움직인다.