deel-local-cli 1.20.12 → 2.0.0
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/README.ko.md +96 -77
- package/README.md +119 -80
- package/bin/deel.js +398 -32
- package/package.json +2 -1
- package/src/acp/jsonrpc.js +87 -7
- package/src/acp/map.js +62 -13
- package/src/acp/serve.js +338 -30
- package/src/agent/agents.js +110 -12
- package/src/agent/askcheck.js +50 -6
- package/src/agent/asks.js +59 -5
- package/src/agent/budget.js +9 -2
- package/src/agent/card.js +8 -2
- package/src/agent/commit.js +168 -16
- package/src/agent/compact.js +93 -21
- package/src/agent/{/354/213/240/353/242/260/353/217/204.js → confidence.js} +1 -1
- package/src/agent/effort.js +85 -15
- package/src/agent/evidence.js +63 -7
- package/src/agent/evolve.js +132 -24
- package/src/agent/grade.js +12 -3
- package/src/agent/loop.js +408 -81
- package/src/agent/memory.js +176 -29
- package/src/agent/mention.js +22 -5
- package/src/agent/models.js +31 -7
- package/src/agent/modes.js +25 -10
- package/src/agent/outschema.js +381 -56
- package/src/agent/pins.js +33 -2
- package/src/agent/project.js +73 -8
- package/src/agent/recall.js +40 -9
- package/src/agent/{/354/204/261/355/225/234/352/270/260/353/241/235.js → recordshape.js} +19 -8
- package/src/agent/review.js +143 -13
- package/src/agent/route.js +506 -32
- package/src/agent/salvage.js +2 -1
- package/src/agent/session.js +457 -31
- package/src/agent/store.js +232 -22
- package/src/agent/threads.js +52 -3
- package/src/backend/adapter.js +508 -85
- package/src/backend/azure.js +12 -1
- package/src/backend/cachemark.js +31 -5
- package/src/backend/clientcert.js +23 -1
- package/src/backend/ctxsize.js +56 -9
- package/src/backend/detect.js +252 -19
- package/src/backend/http.js +383 -42
- package/src/backend/learn.js +101 -11
- package/src/backend/mcp.js +333 -24
- package/src/backend/price.js +61 -13
- package/src/backend/probe.js +188 -36
- package/src/backend/proxy.js +108 -14
- package/src/backend/quota.js +226 -30
- package/src/backend/retry.js +46 -3
- package/src/backend/scan.js +47 -10
- package/src/backend/scanui.js +57 -7
- package/src/backend/toolfit.js +204 -22
- package/src/backend/vision.js +14 -1
- package/src/backend/wire.js +58 -10
- package/src/cmdnames.js +16 -0
- package/src/commands/common.js +70 -0
- package/src/commands/extend.js +300 -0
- package/src/commands/model.js +852 -0
- package/src/commands/view.js +328 -0
- package/src/commands/work.js +823 -0
- package/src/commands.js +193 -2082
- package/src/completion.js +45 -3
- package/src/config.js +452 -39
- package/src/configexplain.js +125 -14
- package/src/doctor.js +164 -26
- package/src/i18n/en.js +22 -0
- package/src/i18n/index.js +31 -1
- package/src/i18n/ja.js +25 -1
- package/src/i18n/ko.js +25 -2
- package/src/i18n/zh.js +25 -1
- package/src/lsp/client.js +111 -9
- package/src/lsp/servers.js +86 -22
- package/src/oneshot.js +325 -65
- package/src/pack/sbom.js +28 -5
- package/src/pack/selfpack.js +54 -5
- package/src/pack/sheet.en.js +24 -3
- package/src/pack/tar.js +45 -8
- package/src/pack/zip.js +103 -8
- package/src/plugins/manage.js +165 -14
- package/src/preview/serve.js +239 -23
- package/src/providers/bedrock.js +30 -5
- package/src/providers/gemini.js +8 -0
- package/src/providers/index.js +17 -1
- package/src/repl.js +254 -61
- package/src/report.js +7 -2
- package/src/reset.js +151 -25
- package/src/safety/audit.js +108 -8
- package/src/safety/authcmd.js +75 -5
- package/src/safety/guard.js +507 -15
- package/src/safety/hooks.js +172 -20
- package/src/safety/keystore.js +178 -18
- package/src/safety/network.js +209 -6
- package/src/safety/policy.js +377 -36
- package/src/safety/runmode.js +9 -2
- package/src/safety/secrets.js +243 -17
- package/src/safety/shellenv.js +64 -3
- package/src/safety/trust.js +298 -32
- package/src/safety/undo.js +409 -17
- package/src/setup.js +175 -17
- package/src/skills/discover.js +122 -19
- package/src/stats.js +57 -16
- package/src/tools/{/355/231/225/354/235/270/353/262/225.js → checkmethods.js} +26 -1
- package/src/tools/clipboard.js +53 -20
- package/src/tools/convert.js +118 -37
- package/src/tools/desc.en.js +48 -5
- package/src/tools/doc2md.js +11 -1
- package/src/tools/docs.js +94 -18
- package/src/tools/edit-match.js +212 -13
- package/src/tools/encoding.js +525 -28
- package/src/tools/excel-com.js +63 -15
- package/src/tools/excel.js +12 -7
- package/src/tools/fastgrep.js +444 -36
- package/src/tools/fig.js +40 -9
- package/src/tools/fsutil.js +235 -21
- package/src/tools/hwpxwrite.js +80 -19
- package/src/tools/ignore.js +55 -7
- package/src/tools/index.js +1029 -145
- package/src/tools/jobs.js +325 -50
- package/src/tools/kiwi.js +50 -5
- package/src/tools/lsp.js +164 -32
- package/src/tools/outline.js +52 -12
- package/src/tools/pdf.js +366 -56
- package/src/tools/shell.js +12 -4
- package/src/tools/spawn.js +203 -18
- package/src/tools/todo.js +73 -4
- package/src/tools/verify.js +312 -43
- package/src/tools/webfetch.js +184 -25
- package/src/tools/xlsx.js +232 -31
- package/src/ui/ansi.js +209 -15
- package/src/ui/banner.js +2 -1
- package/src/ui/complete.js +46 -4
- package/src/ui/diff.js +4 -2
- package/src/ui/export.js +91 -17
- package/src/ui/inputbox.js +58 -18
- package/src/ui/intro.js +9 -2
- package/src/ui/level.js +32 -7
- package/src/ui/md.js +101 -15
- package/src/ui/motion.js +5 -2
- package/src/ui/notify.js +7 -1
- package/src/ui/office.js +37 -5
- package/src/ui/pastechip.js +3 -2
- package/src/ui/pick.js +85 -12
- package/src/ui/prompt.js +176 -13
- package/src/ui/screen.js +2 -1
- package/src/ui/spinner.js +6 -1
- package/src/ui/status.js +80 -12
- package/src/ui/wrap.js +23 -37
- /package/src/agent/{/353/213/250/352/263/204.js" → phase.js} +0 -0
- /package/src/skills/builtin/{/352/271/212/354/235/264/354/236/210/352/262/214-/353/247/214/353/223/244/352/270/260/SKILL.md" → build-deep/SKILL.md} +0 -0
- /package/src/skills/builtin/{/354/260/250/352/267/274/354/260/250/352/267/274-/353/224/224/353/262/204/352/271/205/SKILL.md" → debug-step-by-step/SKILL.md} +0 -0
- /package/src/skills/builtin/{/353/201/235/352/271/214/354/247/200-/355/225/230/352/270/260/SKILL.md" → finish-all/SKILL.md} +0 -0
- /package/src/skills/builtin/{/354/212/244/354/212/244/353/241/234-/352/262/200/355/206/240/SKILL.md" → self-review/SKILL.md} +0 -0
- /package/src/skills/builtin/{/354/275/224/353/223/234-/354/244/204/354/235/264/352/270/260/SKILL.md" → simplify-code/SKILL.md} +0 -0
- /package/src/skills/builtin/{/354/260/224/353/237/254/353/263/264/352/270/260/SKILL.md" → spike/SKILL.md} +0 -0
- /package/src/skills/builtin/{/352/262/200/354/202/254-/353/250/274/354/240/200/SKILL.md" → test-first/SKILL.md} +0 -0
package/src/agent/memory.js
CHANGED
|
@@ -20,8 +20,10 @@
|
|
|
20
20
|
* 기억은 **매 요청마다** 통째로 나간다. 백 줄이면 백 줄이 매번 나간다.
|
|
21
21
|
* 그래서 자리를 정해 두고, 넘으면 넘었다고 말한다.
|
|
22
22
|
*/
|
|
23
|
-
import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'node:fs';
|
|
23
|
+
import { existsSync, readFileSync, writeFileSync, mkdirSync, renameSync, rmSync } from 'node:fs';
|
|
24
24
|
import { dirname, join } from 'node:path';
|
|
25
|
+
import { createHash } from 'node:crypto';
|
|
26
|
+
import { 믿나, 신뢰자리, 고른경로, BOM떼기 } from '../safety/trust.js';
|
|
25
27
|
|
|
26
28
|
// 기억 전체가 차지할 수 있는 최대 글자.
|
|
27
29
|
//
|
|
@@ -44,30 +46,132 @@ const 머리말 = `# 기억
|
|
|
44
46
|
직접 고치셔도 됩니다. 틀린 줄은 지우세요. 지우면 그걸로 끝입니다.
|
|
45
47
|
`;
|
|
46
48
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
+
/*
|
|
50
|
+
* ── 남의 저장소에 딸려 온 memory.md 는 싣지 않는다 (2.0.0 4회차 사냥) ─────
|
|
51
|
+
*
|
|
52
|
+
* 이 파일은 **저장소 안**에 있다. 그래서 저장소에 딸려 온다 — 남의 저장소를
|
|
53
|
+
* clone 하고 켜기만 하면, 남이 적어 둔 줄이 아래 프롬프트토막() 의 머리
|
|
54
|
+
* 「지난 대화에서 정한 것. 사용자가 다시 말하지 않아도 지킨다」 를 달고 매 턴
|
|
55
|
+
* 시스템 글에 실렸다. 사람이 한 번도 말한 적 없는 것을 「사람이 정한 것」 이라고
|
|
56
|
+
* 모델에게 우기는 자리다. 스킬·훅·프로젝트 설정은 이미 믿는 폴더에서만 읽는다
|
|
57
|
+
* (skills/discover.js · safety/hooks.js · safety/trust.js).
|
|
58
|
+
*
|
|
59
|
+
* 그런데 믿는 폴더만 읽으면 **기억이라는 기능이 죽는다.** 사람은 제 폴더에서
|
|
60
|
+
* `deel trust` 를 안 치고도 기억을 쓴다 — Remember 로 적고, 다음 대화에서 안
|
|
61
|
+
* 실리면 「기억했습니다」 는 거짓말이 된다.
|
|
62
|
+
*
|
|
63
|
+
* 그래서 둘을 가르는 것은 **누가 적었나**다. 이 PC 의 deel 이 파일을 쓸 때마다
|
|
64
|
+
* 그 글의 지문을 살림 자리(`~/.deel/memory-own.json`)에 적어 둔다. 살림 자리는
|
|
65
|
+
* 저장소가 못 건드리므로, 지문이 맞는 파일은 이 PC 에서 적은 그대로다.
|
|
66
|
+
*
|
|
67
|
+
* 믿는 폴더 → 싣는다 (손으로 고친 것도)
|
|
68
|
+
* 안 믿는 폴더 · 지문이 맞다 → 싣는다 (이 PC 의 deel 이 적은 그대로)
|
|
69
|
+
* 안 믿는 폴더 · 지문이 안 맞다 → 안 싣고, 안 실었다고 말한다
|
|
70
|
+
*
|
|
71
|
+
* 남의 줄이 든 파일에 한 줄 **더해서** 쓸 때는 지문을 안 적는다. 적으면 남의
|
|
72
|
+
* 줄이 내 줄 틈에 끼어 「이 PC 가 적은 것」 으로 세탁된다.
|
|
73
|
+
*/
|
|
74
|
+
export const 내것표자리 = (env = process.env) => join(dirname(신뢰자리(env)), 'memory-own.json');
|
|
75
|
+
/** 지문을 들고 있는 폴더 수. 넘으면 오래 안 쓴 것부터 뺀다 — 빠진 폴더는 믿기 전까지 안 실릴 뿐이다. */
|
|
76
|
+
const 내것표최대 = 200;
|
|
77
|
+
/*
|
|
78
|
+
* 지문은 **줄끝과 BOM 을 고른 뒤** 뜬다.
|
|
79
|
+
*
|
|
80
|
+
* git 체크아웃(core.autocrlf)이나 윈도 편집기는 LF 를 CRLF 로 바꾸고, 파워셸은 BOM 을 붙인다.
|
|
81
|
+
* 바이트 그대로 떴더니 내용은 한 글자도 안 바뀐 내 기억이 안 믿는 폴더에서 안 실렸다
|
|
82
|
+
* (4회차 Gemini 리뷰). 줄끝·BOM 은 뜻이 아니라서 남이 이것으로 줄을 끼워 넣을 수는 없다.
|
|
83
|
+
*/
|
|
84
|
+
const 지문뜨기 = (글) => createHash('sha256').update(BOM떼기(String(글)).replace(/\r\n?/g, '\n')).digest('hex');
|
|
85
|
+
|
|
86
|
+
function 내것표읽기(env) {
|
|
87
|
+
try {
|
|
88
|
+
const j = JSON.parse(BOM떼기(readFileSync(내것표자리(env), 'utf8')));
|
|
89
|
+
return j && typeof j === 'object' && !Array.isArray(j) ? j : {};
|
|
90
|
+
} catch { return {}; }
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function 내것적기(p, 글, env) {
|
|
94
|
+
const 표자리 = 내것표자리(env);
|
|
95
|
+
const 임시 = `${표자리}.${process.pid}.${Date.now()}.tmp`;
|
|
96
|
+
try {
|
|
97
|
+
const 표 = 내것표읽기(env);
|
|
98
|
+
const 열쇠 = 고른경로(p);
|
|
99
|
+
delete 표[열쇠]; // 지웠다 다시 넣어 맨 뒤(최근)로 보낸다
|
|
100
|
+
표[열쇠] = 지문뜨기(글);
|
|
101
|
+
const 열쇠들 = Object.keys(표);
|
|
102
|
+
for (const k of 열쇠들.slice(0, Math.max(0, 열쇠들.length - 내것표최대))) delete 표[k];
|
|
103
|
+
mkdirSync(dirname(표자리), { recursive: true });
|
|
104
|
+
// 옆에 쓰고 이름을 바꾼다. 반쯤 쓴 표를 다른 deel 이 읽으면 모든 기억이 남의 것이 된다.
|
|
105
|
+
writeFileSync(임시, JSON.stringify(표, null, 2) + '\n', 'utf8');
|
|
106
|
+
renameSync(임시, 표자리);
|
|
107
|
+
} catch {
|
|
108
|
+
// 못 적으면 다음 대화에서 안 믿는 폴더면 안 실릴 뿐이고, 그때 안 실었다고 말한다.
|
|
109
|
+
try { rmSync(임시, { force: true }); } catch { /* 남아도 표는 멀쩡하다 */ }
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* 파일에서 기억 줄을 읽어 온다. 머리말과 빈 줄은 뺀다.
|
|
115
|
+
*
|
|
116
|
+
* 줄들은 **걸러지지 않은 그대로**다 — 사람이 /memory 로 보고 번호로 지우는 자리라
|
|
117
|
+
* 파일에 있는 것은 다 보여야 한다. 모델에게 실어도 되는지는 `믿음` 이 말한다.
|
|
118
|
+
*
|
|
119
|
+
* @returns {{줄들:string[], 자리:string, 있음:boolean, 믿음:boolean, 안믿음:boolean}}
|
|
120
|
+
*/
|
|
121
|
+
export function 읽기(root, { env = process.env } = {}) {
|
|
49
122
|
const p = 자리(root);
|
|
50
|
-
|
|
123
|
+
const 없음 = { 줄들: [], 자리: p, 있음: false, 믿음: true, 안믿음: false };
|
|
124
|
+
if (!existsSync(p)) return 없음;
|
|
51
125
|
let 글;
|
|
52
|
-
try { 글 = readFileSync(p, 'utf8'); } catch { return
|
|
126
|
+
try { 글 = readFileSync(p, 'utf8'); } catch { return 없음; }
|
|
53
127
|
const 줄들 = [];
|
|
128
|
+
/*
|
|
129
|
+
* 머리말 문장은 **그 문장의 앞머리**로 가른다 (2.0.0 6회차 · Gemini 기억6z-a A2).
|
|
130
|
+
*
|
|
131
|
+
* `이 파일은` · `매 요청마다` · `직접 고치` 두세 마디로 가르고 있었다. 이 파일은 머리에
|
|
132
|
+
* 「직접 고치셔도 됩니다」 라고 적어 둔 글이라 사람이 `- ` 없이 손으로 줄을 적는데,
|
|
133
|
+
* 「매 요청마다 테스트를 돌린다」 같은 흔한 줄이 머리말로 걸러져 조용히 빠졌다 — /memory
|
|
134
|
+
* 목록에도 안 떠 왜 안 지켜지는지 볼 길이 없다. 머리말 세 줄은 처음부터 한 글자도 안
|
|
135
|
+
* 바뀌었으므로(git 이력) 긴 앞머리로 좁혀도 옛 파일의 머리말은 그대로 걸러진다.
|
|
136
|
+
*/
|
|
137
|
+
const 머리말앞머리 = ['이 파일은 deel 이 대화 사이에', '매 요청마다 모델에게 통째로', '직접 고치셔도 됩니다'];
|
|
54
138
|
for (const raw of 글.split(/\r?\n/)) {
|
|
55
139
|
const l = raw.trim();
|
|
56
140
|
if (!l) continue;
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
141
|
+
/*
|
|
142
|
+
* 머리말은 **마크다운 제목 꼴**로 가른다 (2.0.0 8회차 스키마). `#` 한 글자만 보았더니
|
|
143
|
+
* 손으로 적은 `#include <stdio.h>` · `#!/bin/sh` 줄이 제목으로 걸러져 통째로 사라졌다 —
|
|
144
|
+
* /memory 목록에도 안 떠 왜 안 지켜지는지 볼 길이 없다 (A2 와 같은 꼴의 조용한 버림).
|
|
145
|
+
*/
|
|
146
|
+
if (/^#{1,6}(\s|$)/.test(l)) continue; // 머리말
|
|
147
|
+
if (머리말앞머리.some((x) => l.startsWith(x))) continue;
|
|
148
|
+
/*
|
|
149
|
+
* 목록 표시는 **뒤에 빈칸이 있을 때만** 뗀다 (2.0.0 8회차 스키마). `\s*` 가 빈칸 0개에도
|
|
150
|
+
* 걸려 `-O3` 은 `O3`, `*args` 는 `args` 로 실렸다 — 매 요청마다 틀린 깃발 이름이 나갔다.
|
|
151
|
+
* 표시만 남은 줄(`-`)은 아래에서 빈 기억으로 안 세야 하므로 줄 끝도 같이 받는다 (6회차 A3).
|
|
152
|
+
*/
|
|
153
|
+
const 줄 = l.replace(/^[-*](?:\s+|$)/, ''); // 목록 표시는 떼고 담는다
|
|
154
|
+
// 표시만 남은 줄(`-`)은 빈 기억이 아니다 (6회차 A3). 담으면 번호 하나를 먹고 빈 `- ` 가 실린다.
|
|
155
|
+
if (줄) 줄들.push(줄);
|
|
61
156
|
}
|
|
62
|
-
|
|
157
|
+
const 믿음 = 믿나(root, { env }) || 내것표읽기(env)[고른경로(p)] === 지문뜨기(글);
|
|
158
|
+
// 실을 줄이 없으면 안 실은 것도 없다 — 빈 파일로 「안 실었습니다」 를 띄우지 않는다.
|
|
159
|
+
return { 줄들, 자리: p, 있음: true, 믿음, 안믿음: !믿음 && 줄들.length > 0 };
|
|
63
160
|
}
|
|
64
161
|
|
|
65
|
-
/**
|
|
66
|
-
|
|
162
|
+
/**
|
|
163
|
+
* 줄들을 파일로 되돌려 쓴다.
|
|
164
|
+
*
|
|
165
|
+
* `내것` 이 참이면 이 PC 가 적은 것으로 지문을 남긴다. 남의 줄이 섞인 채로 쓰는
|
|
166
|
+
* 자리(안 믿는 폴더의 남의 파일에 더하기·지우기)는 거짓으로 부른다.
|
|
167
|
+
*/
|
|
168
|
+
export function 쓰기(root, 줄들, { env = process.env, 내것 = true } = {}) {
|
|
67
169
|
const p = 자리(root);
|
|
68
170
|
mkdirSync(dirname(p), { recursive: true });
|
|
69
171
|
const 몸 = 줄들.map((l) => `- ${l}`).join('\n');
|
|
70
|
-
|
|
172
|
+
const 글 = `${머리말}\n${몸}\n`;
|
|
173
|
+
writeFileSync(p, 글, 'utf8');
|
|
174
|
+
if (내것) 내것적기(p, 글, env);
|
|
71
175
|
return p;
|
|
72
176
|
}
|
|
73
177
|
|
|
@@ -79,11 +183,11 @@ export function 쓰기(root, 줄들) {
|
|
|
79
183
|
*
|
|
80
184
|
* @returns {{ok: boolean, why?: string, 줄: string, 줄수: number, 넘침?: boolean}}
|
|
81
185
|
*/
|
|
82
|
-
export function 더하기(root,
|
|
186
|
+
export function 더하기(root, 글, { env = process.env } = {}) {
|
|
83
187
|
const 줄 = 다듬기(글);
|
|
84
188
|
if (!줄) return { ok: false, why: '적을 내용이 비었습니다', 줄: '', 줄수: 0 };
|
|
85
189
|
|
|
86
|
-
const {
|
|
190
|
+
const { 줄들, 믿음 } = 읽기(root, { env });
|
|
87
191
|
if (줄들.some((x) => 같은말(x, 줄))) {
|
|
88
192
|
return { ok: false, why: '이미 기억하고 있습니다', 줄, 줄수: 줄들.length };
|
|
89
193
|
}
|
|
@@ -96,31 +200,41 @@ export function 더하기(root, 글) {
|
|
|
96
200
|
새것.shift();
|
|
97
201
|
넘침 = true;
|
|
98
202
|
}
|
|
99
|
-
|
|
100
|
-
|
|
203
|
+
// 남의 줄이 든 파일이면 지문을 안 남긴다 — 위 내것표 머리말의 「세탁」 이다.
|
|
204
|
+
/*
|
|
205
|
+
* 남의 줄이 **하나도 없으면**(머리말만 남은 파일 · 남의 줄을 다 지운 뒤) 새로 쓰는 글은
|
|
206
|
+
* 이 PC 가 적은 줄뿐이다 — 쓰기() 가 머리말까지 새로 쓰므로 남의 글자는 한 자도 안 남는다.
|
|
207
|
+
* 그런데도 지문을 안 남겨, 「기억했습니다」 뒤로 한 번도 안 실렸다 (2.0.0 6회차 · Gemini
|
|
208
|
+
* 기억6z-b Z1·Z2). 비우기() 의 「남은 줄이 없으니 남의 것도 없다」 와 같은 셈이다.
|
|
209
|
+
*/
|
|
210
|
+
const 내것 = 믿음 || 줄들.length === 0;
|
|
211
|
+
쓰기(root, 새것, { env, 내것 });
|
|
212
|
+
// 안실림: 적기는 했지만 믿는 폴더가 아니고 남의 줄이 섞여 있어 다음 요청에 안 실린다.
|
|
213
|
+
return { ok: true, 줄, 줄수: 새것.length, 넘침, 안실림: !내것 };
|
|
101
214
|
}
|
|
102
215
|
|
|
103
216
|
/** 번호로 지운다(1부터). 사람이 /memory 화면을 보고 고르는 자리다. */
|
|
104
|
-
export function 지우기(root,
|
|
105
|
-
const {
|
|
217
|
+
export function 지우기(root, 번호, { env = process.env } = {}) {
|
|
218
|
+
const { 줄들, 믿음 } = 읽기(root, { env });
|
|
106
219
|
const i = Number(번호) - 1;
|
|
107
220
|
if (!Number.isInteger(i) || i < 0 || i >= 줄들.length) return { ok: false, why: '그런 번호가 없습니다' };
|
|
108
221
|
const 뺀것 = 줄들[i];
|
|
109
222
|
const 남은것 = 줄들.filter((_, n) => n !== i);
|
|
110
|
-
쓰기(root,
|
|
223
|
+
쓰기(root, 남은것, { env, 내것: 믿음 });
|
|
111
224
|
return { ok: true, 뺀것, 줄수: 남은것.length };
|
|
112
225
|
}
|
|
113
226
|
|
|
114
|
-
/** 통째로 비운다. */
|
|
115
|
-
export function 비우기(root) {
|
|
116
|
-
쓰기(root, []);
|
|
227
|
+
/** 통째로 비운다. 남은 줄이 없으니 남의 것도 없다 — 이 PC 것으로 적는다. */
|
|
228
|
+
export function 비우기(root, { env = process.env } = {}) {
|
|
229
|
+
쓰기(root, [], { env });
|
|
117
230
|
return { ok: true };
|
|
118
231
|
}
|
|
119
232
|
|
|
120
233
|
/** 한 줄로 다듬는다 — 줄바꿈을 없애고 길이를 자른다. */
|
|
121
234
|
function 다듬기(글) {
|
|
122
235
|
let s = String(글 ?? '').replace(/\s+/g, ' ').trim();
|
|
123
|
-
|
|
236
|
+
// 읽기() 와 같은 무늬 — 빈칸 없이 붙은 `-O3` 의 첫 글자를 먹지 않는다 (2.0.0 8회차 스키마).
|
|
237
|
+
s = s.replace(/^[-*](?:\s+|$)/, '');
|
|
124
238
|
if (s.length > 한줄최대) s = s.slice(0, 한줄최대) + '…';
|
|
125
239
|
return s;
|
|
126
240
|
}
|
|
@@ -132,8 +246,20 @@ function 다듬기(글) {
|
|
|
132
246
|
* 공백·조사·문장부호를 털고 견준다. 완벽할 필요는 없다 —
|
|
133
247
|
* 여기서 하려는 것은 '똑같은 말이 쌓이는 것' 을 막는 것뿐이다.
|
|
134
248
|
*/
|
|
249
|
+
// 낱말 끝에서 터는 조사. 긴 것부터 적어야 「으로」 가 「로」 로 먼저 잘리지 않는다.
|
|
250
|
+
const 조사끝 = /(?:으로|에서|에게|부터|까지|보다|은|는|이|가|을|를|의|에|와|과|로|도|만)$/;
|
|
135
251
|
function 같은말(a, b) {
|
|
136
|
-
const 털기 = (s) => String(s).toLowerCase()
|
|
252
|
+
const 털기 = (s) => String(s).toLowerCase()
|
|
253
|
+
.replace(/[.,!?~·'"()[\]]/g, ' ')
|
|
254
|
+
/*
|
|
255
|
+
* 조사는 **낱말 끝에서만** 턴다 (2.0.0 8회차 스키마). 여태 위 머리말이 「조사를 턴다」 고
|
|
256
|
+
* 적어 두고 안 털어, 「검증 포트는 7080 이다」 와 「검증 포트가 7080 이다」 가 두 줄로
|
|
257
|
+
* 쌓였다 — 이 함수가 막으려던 바로 그 꼴이다. 낱말 통째로인 한 글자는 안 턴다.
|
|
258
|
+
* 「이 파일」 의 「이」 는 조사가 아니다.
|
|
259
|
+
*/
|
|
260
|
+
.split(/\s+/)
|
|
261
|
+
.map((낱말) => 낱말.replace(/[가-힣]{2,}$/, (끝) => 끝.replace(조사끝, '')))
|
|
262
|
+
.join('');
|
|
137
263
|
const x = 털기(a);
|
|
138
264
|
const y = 털기(b);
|
|
139
265
|
if (x === y) return true;
|
|
@@ -147,10 +273,31 @@ function 같은말(a, b) {
|
|
|
147
273
|
*
|
|
148
274
|
* 없으면 빈 글을 돌려준다 — "기억: (없음)" 같은 줄을 넣으면 그 자체가
|
|
149
275
|
* 매 요청마다 나가는 쓰레기가 된다.
|
|
276
|
+
*
|
|
277
|
+
* 안 믿는 폴더에서 이 PC 가 적은 그대로가 아니면 **안 싣는다** (위 내것표 머리말).
|
|
278
|
+
* 안 실었다는 말은 읽기() 의 `안믿음` 으로 화면이 한다.
|
|
279
|
+
*
|
|
280
|
+
* ── 읽을 때도 자리를 지킨다 (2.0.0 4회차 사냥) ─────────────────────────
|
|
281
|
+
*
|
|
282
|
+
* 상한(기억최대·줄최대·한줄최대)은 더하기() 에만 걸려 있었다. 그런데 이 파일은
|
|
283
|
+
* 「직접 고치셔도 됩니다」 라고 머리에 적어 둔 글이다. 사람이 3,000줄을 붙여 넣으면
|
|
284
|
+
* 30만 자가 **매 요청마다** 통째로 나갔다 — 머리말이 「왜 상한을 두나」 에 적어 둔
|
|
285
|
+
* 바로 그 일이다. 그래서 싣는 자리에서 한 번 더 자른다. 더하기() 와 같은 셈으로
|
|
286
|
+
* **최근 것(뒤쪽)** 을 남기고, 안 실은 줄이 있으면 몇 줄인지 말한다.
|
|
150
287
|
*/
|
|
151
|
-
export function 프롬프트토막(root) {
|
|
152
|
-
const {
|
|
153
|
-
if (!줄들.length) return '';
|
|
288
|
+
export function 프롬프트토막(root, { env = process.env } = {}) {
|
|
289
|
+
const { 줄들, 믿음 } = 읽기(root, { env });
|
|
290
|
+
if (!줄들.length || !믿음) return '';
|
|
291
|
+
const 다듬은 = 줄들.map((l) => (l.length > 한줄최대 ? `${l.slice(0, 한줄최대)}…` : l));
|
|
292
|
+
const 실을것 = [];
|
|
293
|
+
let 합 = -1; // 줄 사이 '\n' 을 같이 센다 (더하기 의 join 과 같은 셈)
|
|
294
|
+
for (let i = 다듬은.length - 1; i >= 0; i--) {
|
|
295
|
+
if (실을것.length >= 줄최대 || 합 + 다듬은[i].length + 1 > 기억최대) break;
|
|
296
|
+
실을것.unshift(다듬은[i]);
|
|
297
|
+
합 += 다듬은[i].length + 1;
|
|
298
|
+
}
|
|
299
|
+
const 뺀수 = 다듬은.length - 실을것.length;
|
|
154
300
|
return '\n--- 기억 (지난 대화에서 정한 것. 사용자가 다시 말하지 않아도 지킨다) ---\n'
|
|
155
|
-
+
|
|
301
|
+
+ (뺀수 ? `(자리가 넘쳐 앞의 ${뺀수}줄은 안 실었습니다 — /memory 로 정리하세요)\n` : '')
|
|
302
|
+
+ 실을것.map((l) => `- ${l}`).join('\n');
|
|
156
303
|
}
|
package/src/agent/mention.js
CHANGED
|
@@ -35,8 +35,10 @@ export function findMentions(text) {
|
|
|
35
35
|
for (let i = 0; i < s.length; i++) {
|
|
36
36
|
if (s[i] !== '@') continue;
|
|
37
37
|
// 앞이 글자면 지목이 아니다 — hong@example.com 의 @ 가 여기서 걸러진다.
|
|
38
|
+
// 감싸는 짝의 여는 쪽(백틱 · 「 · 『)도 받는다. 닫는 쪽은 이미 안다 — 백틱은 아래에서
|
|
39
|
+
// 경로의 끝이고 」』 는 뒤에붙는것 이 뗀다. 여는 쪽만 몰라서 `@src/a.js` 가 조용히 안 붙었다.
|
|
38
40
|
const 앞 = i > 0 ? s[i - 1] : ' ';
|
|
39
|
-
if (!/[\s(\[{'"]/.test(앞)) continue;
|
|
41
|
+
if (!/[\s(\[{'"`「『]/.test(앞)) continue;
|
|
40
42
|
|
|
41
43
|
// @"띄어쓰기 든 이름.txt" — 따옴표로 묶으면 통째로 하나다.
|
|
42
44
|
if (s[i + 1] === '"' || s[i + 1] === "'") {
|
|
@@ -142,6 +144,8 @@ export function expand(text, { scope = null, budget = 기본예산, seen = null,
|
|
|
142
144
|
const show = scope.show ? scope.show(자리.abs) : 자리.path;
|
|
143
145
|
let 몸통;
|
|
144
146
|
let 통째로 = true;
|
|
147
|
+
// 내용이 한 자도 안 실린 자리(못 읽은 파일 · 그림 못 보는 모델의 그림). 아래 머리말이 이것을 센다.
|
|
148
|
+
let 붙음 = true;
|
|
145
149
|
|
|
146
150
|
try {
|
|
147
151
|
if (statSync(자리.abs).isDirectory()) {
|
|
@@ -171,6 +175,7 @@ ${몸통}
|
|
|
171
175
|
? `(못 붙였습니다: ${것.왜})`
|
|
172
176
|
: '(그림입니다. 지금 붙어 있는 모델은 그림을 못 봅니다 — 안 붙였습니다.)';
|
|
173
177
|
통째로 = false;
|
|
178
|
+
붙음 = false;
|
|
174
179
|
} else {
|
|
175
180
|
// 사내 파일은 CP949 가 흔하다. 그냥 읽으면 통째로 깨진다.
|
|
176
181
|
const 읽음 = readTextFull(자리.abs);
|
|
@@ -193,18 +198,30 @@ ${몸통}
|
|
|
193
198
|
// 못 읽는 파일(바이너리 등)은 붙이지 않는다. 그렇다고 말은 해 준다.
|
|
194
199
|
몸통 = `(못 읽었습니다: ${err.message})`;
|
|
195
200
|
통째로 = false;
|
|
201
|
+
붙음 = false;
|
|
196
202
|
}
|
|
197
203
|
|
|
198
204
|
const 토막 = `\n\n--- ${show} ---\n${몸통}\n--- ${show} 끝 ---`;
|
|
199
205
|
남은예산 -= estimateTokens(토막);
|
|
200
206
|
붙일것.push(토막);
|
|
201
|
-
attached.push({ path: 자리.abs, show, full:
|
|
207
|
+
attached.push({ path: 자리.abs, show, full: 통째로, 붙음 });
|
|
202
208
|
if (남은예산 <= 0) break;
|
|
203
209
|
}
|
|
204
210
|
|
|
205
211
|
if (!붙일것.length) return { text: 원문, attached, missing, blocked, 그림들 };
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
212
|
+
/*
|
|
213
|
+
* 머리말은 **내용이 실제로 실린 것만** 센다 (2.0.0 8회차 스키마).
|
|
214
|
+
*
|
|
215
|
+
* 못 읽은 파일과 「그림을 못 보는 모델의 그림」 도 attached 에 세어져, 한 자도 안 실린
|
|
216
|
+
* 파일에 「이미 읽은 것으로 치고 답하세요」 가 붙었다. 모델에게 그것은 「이 파일은
|
|
217
|
+
* 이미 봤다」 는 말이라, Read 도 안 부르고 안 본 파일의 내용을 지어낸다 — 이 파일
|
|
218
|
+
* 머리말이 막으려던 「사람은 자기가 안 보낸 것이 왜 거기 있는지 모른다」 의 반대쪽이다.
|
|
219
|
+
*/
|
|
220
|
+
const 붙은것 = attached.filter((a) => a.붙음 !== false);
|
|
221
|
+
const 머리 = 붙은것.length === 0
|
|
222
|
+
? '아래는 사용자가 @ 로 지목한 파일인데 내용을 못 붙였습니다. 지어내지 말고, 필요하면 Read 로 읽으세요.'
|
|
223
|
+
: (붙은것.length === 1
|
|
224
|
+
? '아래는 사용자가 @ 로 지목한 파일입니다. 이미 읽은 것으로 치고 답하세요.'
|
|
225
|
+
: `아래는 사용자가 @ 로 지목한 파일 ${붙은것.length}개입니다. 이미 읽은 것으로 치고 답하세요.`);
|
|
209
226
|
return { text: `${원문}\n\n${머리}${붙일것.join('')}`, attached, missing, blocked, 그림들 };
|
|
210
227
|
}
|
package/src/agent/models.js
CHANGED
|
@@ -43,6 +43,21 @@ import { 정책읽기 } from '../safety/policy.js';
|
|
|
43
43
|
// 컨텍스트를 못 알아냈을 때 쓰는 값. repl.js 와 같은 값을 봐야 한다.
|
|
44
44
|
export const CTX_DEFAULT = 32768;
|
|
45
45
|
|
|
46
|
+
/*
|
|
47
|
+
* 크기 칸(ctx · maxTokens)은 **양의 유한수만** 받는다 (사냥5 L5-6).
|
|
48
|
+
*
|
|
49
|
+
* `ctx: 0` 이 그대로 conn 에 실리면 접기·요약이 영영 안 돈다 — 창 0 에서 몇 퍼센트를
|
|
50
|
+
* 썼는지 못 재기 때문이다. `maxTokens: 0` 은 모든 요청을 바닥값 512 로 묶는다. 설정
|
|
51
|
+
* 파일 한 칸의 실수가 「답이 짧다」 · 「창이 넘쳐 400」 으로만 보이고 까닭은 어디에도
|
|
52
|
+
* 안 뜬다. 0·음수·NaN·글자·참거짓은 「안 적은 것」 으로 치고 다음 차례로 넘긴다.
|
|
53
|
+
* oneshot.js 도 conn 을 지을 때 이걸 쓴다 — 두 벌이면 한쪽만 고쳐진다.
|
|
54
|
+
*/
|
|
55
|
+
export function 양수크기(v) {
|
|
56
|
+
if (v === null || v === undefined || typeof v === 'boolean' || (typeof v === 'string' && !v.trim())) return null;
|
|
57
|
+
const n = Number(v);
|
|
58
|
+
return Number.isFinite(n) && n > 0 ? n : null;
|
|
59
|
+
}
|
|
60
|
+
|
|
46
61
|
/**
|
|
47
62
|
* 프로필 하나로 연결을 만든다.
|
|
48
63
|
*
|
|
@@ -58,8 +73,8 @@ export function 연결만들기(prof, { ctx = null, maxTokens = null } = {}) {
|
|
|
58
73
|
auth: prof.auth,
|
|
59
74
|
key: resolveKey(prof),
|
|
60
75
|
model: prof.model,
|
|
61
|
-
ctx: ctx ?? prof.ctx ?? CTX_DEFAULT,
|
|
62
|
-
maxTokens: maxTokens ?? prof.maxTokens ?? null,
|
|
76
|
+
ctx: 양수크기(ctx) ?? 양수크기(prof.ctx) ?? CTX_DEFAULT,
|
|
77
|
+
maxTokens: 양수크기(maxTokens) ?? 양수크기(prof.maxTokens) ?? null,
|
|
63
78
|
streaming: prof.streaming ?? false,
|
|
64
79
|
잠잠: prof.잠잠 ?? prof.streamIdleMs ?? 잠잠기본,
|
|
65
80
|
// 바이트는 오는데 내용이 안 올 때의 전체 상한 (backend/http.js 의 무소식기본).
|
|
@@ -100,10 +115,12 @@ export function 프로필들(cfg = load()) {
|
|
|
100
115
|
/**
|
|
101
116
|
* 이름으로 프로필 찾기.
|
|
102
117
|
*
|
|
103
|
-
* id → 이름 → 모델
|
|
104
|
-
*
|
|
118
|
+
* id → 이름 → 모델 이름이 딱 맞는지 보고, 없으면 셋 가운데 어디에든 **들어 있는지**
|
|
119
|
+
* (부분 일치) 본다. 사람은 `/ask small ...` · `/ask coder ...` 처럼 기억나는 대로
|
|
120
|
+
* 치지, 설정에 적은 id 를 외우고 있지 않다. 여기 전에는 「앞부분 일치」 라고 적혀
|
|
121
|
+
* 있었는데 코드는 처음부터 부분 일치였다 — 말을 코드에 맞췄다 (6회차 여러모델6aw-b MD1).
|
|
105
122
|
*
|
|
106
|
-
*
|
|
123
|
+
* 부분 일치에서 **둘 이상 걸리면 고르지 않는다.** 아무거나 골라 주면
|
|
107
124
|
* 물어본 사람은 어느 모델이 답했는지 모른 채로 그 답을 믿게 된다.
|
|
108
125
|
*/
|
|
109
126
|
export function 프로필찾기(이름, cfg = load()) {
|
|
@@ -112,7 +129,12 @@ export function 프로필찾기(이름, cfg = load()) {
|
|
|
112
129
|
const 목록 = 프로필들(cfg);
|
|
113
130
|
if (!목록.length) return { ok: false, why: '설정에 프로필이 하나도 없습니다', 후보: [] };
|
|
114
131
|
|
|
115
|
-
|
|
132
|
+
/*
|
|
133
|
+
* 없는 칸은 **빈 글자**다 (2.0.0 8회차 스키마). `String(p.id)` 가 id 를 안 적은 프로필을
|
|
134
|
+
* 글자 "undefined" 로 만들어, 빈 값이 이름 자리로 흘러든 `/ask undefined …` 가 그 프로필에
|
|
135
|
+
* 「딱 맞는 것」 으로 걸렸다. 아래 부분 일치 줄은 처음부터 `?? ''` 였다.
|
|
136
|
+
*/
|
|
137
|
+
const 딱 = 목록.find((p) => String(p.id ?? '').toLowerCase() === q)
|
|
116
138
|
?? 목록.find((p) => String(p.name ?? '').toLowerCase() === q)
|
|
117
139
|
?? 목록.find((p) => String(p.model ?? '').toLowerCase() === q);
|
|
118
140
|
if (딱) return { ok: true, prof: 딱, 후보: [딱] };
|
|
@@ -193,7 +215,9 @@ export function 목록보기(cfg = load()) {
|
|
|
193
215
|
model: p.model ?? '',
|
|
194
216
|
어디,
|
|
195
217
|
로컬,
|
|
196
|
-
|
|
218
|
+
// 같은 프로필인지는 **같은 객체**로 본다. id 만 견주면 이름만 적은(id 없는) 프로필이 둘일 때
|
|
219
|
+
// undefined === undefined 로 둘 다 지금이 됐다 (6회차 여러모델6aw-b MD3).
|
|
220
|
+
지금: 지금 != null && (지금 === p || (p.id != null && 지금.id === p.id)),
|
|
197
221
|
};
|
|
198
222
|
});
|
|
199
223
|
}
|
package/src/agent/modes.js
CHANGED
|
@@ -77,25 +77,36 @@ const 쪼개기 = ['Task'];
|
|
|
77
77
|
* 두 번째 줄(나란히 부르기)은 값이 아니라 **시간**이다. 읽기 도구는 같이
|
|
78
78
|
* 돌게 되어 있는데(loop.js 의 묶기), 한 개씩 부르면 그 수만큼 왕복이 는다.
|
|
79
79
|
*/
|
|
80
|
-
|
|
80
|
+
/*
|
|
81
|
+
* 아래 넷은 **검사가 잣대로 쓰라고** 내보낸다 (test/modes.test.js).
|
|
82
|
+
*
|
|
83
|
+
* 영문 모드 글(`sayEn` · `say짧게En`)은 배열이 아니라 긴 문자열이라 이 상수를
|
|
84
|
+
* 펴 넣을 수가 없다 — 그래서 같은 문장을 손으로 옮겨 적는다. 옮기면 떨어진다.
|
|
85
|
+
* 실제로 2.0.0 8회차에 다섯 자리에서 「Never whole files.」 가 빠져 있었고,
|
|
86
|
+
* 작은 창을 영어로 켠 사람만 그 규칙을 한 번도 못 받았다.
|
|
87
|
+
*
|
|
88
|
+
* 그래서 잣대를 여기 한 벌만 두고, 검사가 한국어 판과 영문 판이 **같은 규칙을**
|
|
89
|
+
* 싣고 있는지 짝을 맞춰 본다. 안 쓰는 export 로 보인다고 지우면 그 짝이 죽는다.
|
|
90
|
+
*/
|
|
91
|
+
export const 읽는법 = [
|
|
81
92
|
'- 남의 코드는 Outline 으로 모양부터 본다. Grep 으로 그 말이 닿는 자리를 다 모은 뒤,',
|
|
82
93
|
' **짚은 자리 앞뒤만** Read 한다 (offset·limit). 통째로 읽지 마라 — 한 번에 다',
|
|
83
94
|
' 부어 넣으면 뒤로 갈수록 앞엣것이 접혀 나가고, 잊은 채로 「없다」 고 답하게 된다.',
|
|
84
95
|
'- 서로 상관없는 읽기는 **한 번에 같이 부른다.** 읽기 도구는 나란히 돈다.',
|
|
85
96
|
' 한 개씩 부르면 그 수만큼 왕복이 늘고, 그게 그대로 사람이 기다리는 시간이다.',
|
|
86
97
|
];
|
|
87
|
-
const 읽는법짧게 = [
|
|
98
|
+
export const 읽는법짧게 = [
|
|
88
99
|
'- 남의 코드는 Outline → Grep → **짚은 자리 앞뒤만** Read (offset·limit). 통째로 읽지 마라.',
|
|
89
100
|
'- 상관없는 읽기는 한 번에 같이 부른다 — 나란히 돈다.',
|
|
90
101
|
];
|
|
91
|
-
const 읽는법En = [
|
|
102
|
+
export const 읽는법En = [
|
|
92
103
|
'- For code you did not write, get the shape with Outline. Use Grep to gather every place it touches,',
|
|
93
104
|
' then Read **only around the lines you found** (offset/limit). Do not read whole files - pour it all',
|
|
94
105
|
' in at once and the earlier turns fold away, and you answer having forgotten them.',
|
|
95
106
|
'- Fire unrelated reads **together in one message.** Read tools run side by side; one call at a time',
|
|
96
107
|
' adds that many round trips, and that is the waiting the person feels.',
|
|
97
108
|
];
|
|
98
|
-
const 읽는법짧게En = [
|
|
109
|
+
export const 읽는법짧게En = [
|
|
99
110
|
'- Code you did not write: Outline -> Grep -> Read **only around what you found** (offset/limit). Never whole files.',
|
|
100
111
|
'- Fire unrelated reads together in one message - they run side by side.',
|
|
101
112
|
];
|
|
@@ -115,7 +126,7 @@ export const MODES = {
|
|
|
115
126
|
id: 'auto',
|
|
116
127
|
hintEn: "picks the right mode for what you ask",
|
|
117
128
|
sayEn: "You are in **Auto** mode. What comes next is not fixed.\n\n- Work out what kind of job this is first, then do it that way.\n If it is an edit, read before editing. If it is a diagnosis, confirm before concluding.\n If it is an explanation, back it with the files.\n- For a large job, break it into steps with TodoWrite and **finish everything you wrote down.**\n Do not write the list and then ask. If there are several chunks, hand them off with Task —\n a subtask runs in its own window, so yours does not fill up.\n- For code you did not write, get the shape with Outline. Use Grep to gather every place it\n touches, then Read **only around the lines you found** (offset/limit). Do not read whole\n files - pour it all in at once and the earlier turns fold away, and you answer having\n forgotten them.\n- Fire unrelated reads **together in one message.** Read tools run side by side; one call at\n a time adds that many round trips, and that is the waiting the person feels.\n- Making several files: one Write call (files array). Do not call it once per file.\n Several places to edit: one Edit call the same way (edits array).\n- Commands that never end (dev servers, watch) need background: true on Bash. Called plainly\n they die on timeout. After starting one, read its output with Jobs, and end it with Jobs when done.\n- Verify what can be verified — call Verify. Do not call something done that you did not check.\n- Do what the job needs. Do not start work the job did not ask for.",
|
|
118
|
-
say짧게En: "**Auto** mode. What comes next is not fixed.\n- Work out what kind of job this is, then do it that way.\n- Large job: break it up with TodoWrite and **finish it all.** Several chunks: hand off with Task.\n- Code you did not write: Outline -> Grep -> Read **only around what you found** (offset/limit).\n- Fire unrelated reads together in one message - they run side by side.\n- Several files: one Write (files array). Several edits: one Edit (edits array).\n- Commands that never end (dev server, watch): background: true on Bash. Read with Jobs, end with Jobs.\n- Verify before you finish. Do not call something done that you did not check.\n- Do what the job needs. Do not start work the job did not ask for.",
|
|
129
|
+
say짧게En: "**Auto** mode. What comes next is not fixed.\n- Work out what kind of job this is, then do it that way.\n- Large job: break it up with TodoWrite and **finish it all.** Several chunks: hand off with Task.\n- Code you did not write: Outline -> Grep -> Read **only around what you found** (offset/limit). Never whole files.\n- Fire unrelated reads together in one message - they run side by side.\n- Several files: one Write (files array). Several edits: one Edit (edits array).\n- Commands that never end (dev server, watch): background: true on Bash. Read with Jobs, end with Jobs.\n- Verify before you finish. Do not call something done that you did not check.\n- Do what the job needs. Do not start work the job did not ask for.",
|
|
119
130
|
name: '종합',
|
|
120
131
|
en: 'Auto',
|
|
121
132
|
glyph: '◎',
|
|
@@ -124,6 +135,10 @@ export const MODES = {
|
|
|
124
135
|
effort: 'save',
|
|
125
136
|
think: null,
|
|
126
137
|
// 작은 창용 짧은 판. 빠진 규칙은 없고 설득하는 문장만 없다 — 아래 말() 참고.
|
|
138
|
+
//
|
|
139
|
+
// 손으로 옮겨 적는 글이라 옮기며 한 마디씩 떨어진다 (2.0.0 8회차 판정). 다섯 자리에서
|
|
140
|
+
// 「통째로 읽지 마라 / Never whole files.」 가 빠져 있었다 — 작은 창을 영어로 켠 사람만
|
|
141
|
+
// 그 규칙을 한 번도 못 받았다. test/modes.test.js 가 한국어 짧은 판과 짝을 맞춰 본다.
|
|
127
142
|
say짧게: [
|
|
128
143
|
'**종합** 모드다. 무슨 일이 올지 정해져 있지 않다.',
|
|
129
144
|
'- 무슨 일인지 먼저 가늠하고 그에 맞게 해라.',
|
|
@@ -158,7 +173,7 @@ export const MODES = {
|
|
|
158
173
|
id: 'code',
|
|
159
174
|
hintEn: "edits and builds",
|
|
160
175
|
sayEn: "This is **implementation**. Follow this order.\n\n1. For code you did not write, look at the **shape first** with Outline - a folder through\n Outline is a fraction of the size. Grep to gather every place the thing you are changing\n touches, then Read **only around the lines you found** (offset/limit). Never whole files.\n Fire unrelated reads **together in one message** - read tools run side by side.\n2. Always Read a file before editing it. The tool refuses an edit to a file you have not read.\n3. Follow the conventions of the surrounding code — naming, error handling, comment density.\n Do not import a new convention. Do what this code already does.\n4. Make and change every file the job needs. Do not touch one file and stop.\n For something new, lay out the folder structure first and create **several files in one Write**\n (files array). Several places to change go in **one Edit** (edits array).\n One call per item adds that many round trips, and minutes go with them.\n5. If the work splits into separate strands, hand a chunk off with Task.\n A subtask runs in its own window and returns only a summary — your window does not fill up.\n6. **Call Verify before you finish.** A file existing and a file working are different things.\n Fix what comes back and call it again. Say \"I could not verify this\" for anything you did not check.\n For things you only learn by running (dev servers, watch), give Bash **background: true**.\n Called plainly they never end and die on timeout. Read output with Jobs, and always end it\n with Jobs when done — otherwise that server keeps holding the port.\n7. When done, say what you changed and why in a line or two. Do not paste the code back.",
|
|
161
|
-
say짧게En: "**Implementation.** Follow this order.\n1. Code you did not write: Outline -> Grep -> Read **only around what you found** (offset/limit).\n Fire unrelated reads together in one message - they run side by side.\n2. Always Read a file before editing it.\n3. Follow the surrounding conventions. Do not import a new one.\n4. Several files: one Write (files array). Several edits: one Edit (edits array).\n5. Several strands: hand off with Task.\n6. **Verify before you finish.** Fix what comes back and call it again.\n If it must be run, Bash with background: true — called plainly it dies on timeout. Read with Jobs, end with Jobs.\n7. Say what changed and why in a line or two. Do not paste code.",
|
|
176
|
+
say짧게En: "**Implementation.** Follow this order.\n1. Code you did not write: Outline -> Grep -> Read **only around what you found** (offset/limit). Never whole files.\n Fire unrelated reads together in one message - they run side by side.\n2. Always Read a file before editing it.\n3. Follow the surrounding conventions. Do not import a new one.\n4. Several files: one Write (files array). Several edits: one Edit (edits array).\n5. Several strands: hand off with Task.\n6. **Verify before you finish.** Fix what comes back and call it again.\n If it must be run, Bash with background: true — called plainly it dies on timeout. Read with Jobs, end with Jobs.\n7. Say what changed and why in a line or two. Do not paste code.",
|
|
162
177
|
name: '코드',
|
|
163
178
|
en: 'Code',
|
|
164
179
|
glyph: '◆',
|
|
@@ -328,7 +343,7 @@ export const MODES = {
|
|
|
328
343
|
].join('\n'),
|
|
329
344
|
say짧게En: [
|
|
330
345
|
"**Inspection.** You change nothing.",
|
|
331
|
-
"1. Outline -> Grep every place it touches -> Read **only around what you found** (offset/limit).",
|
|
346
|
+
"1. Outline -> Grep every place it touches -> Read **only around what you found** (offset/limit). Never whole files.",
|
|
332
347
|
" Fire unrelated reads together - they run side by side.",
|
|
333
348
|
"2. Per finding: what - where (path:line + function) - the order of events that triggers it - why - what it costs.",
|
|
334
349
|
"3. **Only what the code shows.** No guesses. Say what you could not look at.",
|
|
@@ -344,7 +359,7 @@ export const MODES = {
|
|
|
344
359
|
think: 'high',
|
|
345
360
|
say짧게: [
|
|
346
361
|
'**점검**이다. 아무것도 바꾸지 않는다.',
|
|
347
|
-
'1. Outline → 그 말이 닿는 자리를 Grep 으로 **다** 모으기 → 짚은 자리 앞뒤만 Read (offset·limit).',
|
|
362
|
+
'1. Outline → 그 말이 닿는 자리를 Grep 으로 **다** 모으기 → 짚은 자리 앞뒤만 Read (offset·limit). 통째로 읽지 마라.',
|
|
348
363
|
' 상관없는 읽기는 한 번에 같이 부른다 — 나란히 돈다.',
|
|
349
364
|
'2. 찾은 것마다: 무엇 · 어디(경로:줄 과 함수) · 일어나는 차례 · 왜 · 무엇을 잃나.',
|
|
350
365
|
'3. **코드에 있는 것만.** 짐작은 적지 마라. 못 본 자리는 못 봤다고 적어라.',
|
|
@@ -422,7 +437,7 @@ export const MODES = {
|
|
|
422
437
|
id: 'plan',
|
|
423
438
|
hintEn: "plan first · run it after approval",
|
|
424
439
|
sayEn: "This is **planning**. You have not been given the tools that change files.\nDo not try to edit code. Produce a plan and stop.\n\nConfirm first — a plan built without knowing the current state is a wish, not a plan.\n Start with Outline for the shape, narrow with Glob/Grep, then Read **only around the lines\n you found** (offset/limit). Fire unrelated reads together in one message.\n\nThen write it in this order.\n 1. Goal — what does \"done\" look like (as a sentence you can check)\n 2. Current state — the files involved and what they do now (point with path:line)\n 3. What changes — per file, what and why\n 4. Order — step by step. Each step small enough to check on its own\n 5. Risks — what could break, and how to get back if it does\n 6. How to check — what do you run to know it worked\n\nWrite the steps into TodoWrite as well. After approval you continue straight from them.\n The number of steps is not fixed — match it to the size of the job. Do not force it to three.\n A small job ends in two or three; a large one lists all ten or more.\nIf something is unknown, do not invent it — write \"this needs to be confirmed\".\n\nEnd with \"Shall I go ahead with this?\".\n You cannot make the edits in this mode — you have no tools for it. Once approved,\n **tell the person to type `/code`** — you have no way to switch modes yourself.",
|
|
425
|
-
say짧게En: "**Planning.** You have no tools that change files. Produce a plan and stop.\n- Confirm first — a plan built without the current state is a wish, not a plan.\n Outline -> Glob/Grep -> Read **only around what you found** (offset/limit). Unrelated reads together.\n- Write it in this order: 1) goal (a sentence you can check) 2) current state (path:line)\n 3) what changes, per file, and why 4) order — each step small enough to check on its own\n 5) risks and how to get back 6) how to check it worked.\n- Put the steps in TodoWrite too. Match the number to the size of the job — do not force it to three.\n- If something is unknown, do not invent it — write \"this needs to be confirmed\".\n- End with \"Shall I go ahead with this?\". You have no tools to make the edits, so once approved,\n **tell the person to type `/code`** — you cannot switch modes yourself.",
|
|
440
|
+
say짧게En: "**Planning.** You have no tools that change files. Produce a plan and stop.\n- Confirm first — a plan built without the current state is a wish, not a plan.\n Outline -> Glob/Grep -> Read **only around what you found** (offset/limit). Never whole files. Unrelated reads together.\n- Write it in this order: 1) goal (a sentence you can check) 2) current state (path:line)\n 3) what changes, per file, and why 4) order — each step small enough to check on its own\n 5) risks and how to get back 6) how to check it worked.\n- Put the steps in TodoWrite too. Match the number to the size of the job — do not force it to three.\n- If something is unknown, do not invent it — write \"this needs to be confirmed\".\n- End with \"Shall I go ahead with this?\". You have no tools to make the edits, so once approved,\n **tell the person to type `/code`** — you cannot switch modes yourself.",
|
|
426
441
|
name: '계획',
|
|
427
442
|
en: 'Plan',
|
|
428
443
|
glyph: '☰',
|
|
@@ -433,7 +448,7 @@ export const MODES = {
|
|
|
433
448
|
say짧게: [
|
|
434
449
|
'**계획** 모드다. 파일을 바꾸는 도구는 없다. 계획을 내고 멈춘다.',
|
|
435
450
|
'- 먼저 확인하라 — 지금 상태를 모르면 계획이 아니라 희망이다.',
|
|
436
|
-
' Outline → Glob/Grep → **짚은 자리 앞뒤만** Read (offset·limit). 상관없는 읽기는 한 번에.',
|
|
451
|
+
' Outline → Glob/Grep → **짚은 자리 앞뒤만** Read (offset·limit). 통째로 읽지 마라. 상관없는 읽기는 한 번에.',
|
|
437
452
|
'- 이 차례로 적어라: 1) 목표(확인할 수 있는 문장) 2) 지금 상태(경로:줄) 3) 바꿀 것(파일별로',
|
|
438
453
|
' 무엇을 왜) 4) 순서(각 단계는 따로 확인할 수 있는 크기로) 5) 위험과 되돌리는 길 6) 확인 방법.',
|
|
439
454
|
'- 단계는 TodoWrite 로도 적어라. 수는 일의 크기에 맞춘다 — 세 개로 맞추지 마라.',
|