@aigencydev/cli 0.1.0 → 0.3.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/dist/agent/checkpoints.js +147 -0
- package/dist/agent/chunker.js +1 -19
- package/dist/agent/history.js +0 -9
- package/dist/agent/instructions.js +94 -0
- package/dist/agent/loop.js +3 -34
- package/dist/agent/memory.js +197 -103
- package/dist/agent/session-store.js +160 -0
- package/dist/agent/task-list.js +102 -0
- package/dist/api-client/auth.js +0 -10
- package/dist/api-client/base.js +0 -16
- package/dist/api-client/stream.js +0 -25
- package/dist/api-client/updates.js +2 -9
- package/dist/api-client/usage.js +0 -4
- package/dist/auth/oauth.js +0 -31
- package/dist/auth/pkce.js +1 -9
- package/dist/auth/session.js +0 -16
- package/dist/auth/storage.js +1 -36
- package/dist/cli.js +8 -13
- package/dist/commands/chat.js +14 -19
- package/dist/commands/help.js +0 -4
- package/dist/commands/init.js +327 -30
- package/dist/commands/login.js +0 -30
- package/dist/commands/logout.js +0 -5
- package/dist/commands/update.js +0 -9
- package/dist/commands/usage.js +0 -10
- package/dist/config/bootstrap.js +52 -0
- package/dist/config/defaults.js +4 -15
- package/dist/config/hooks.js +1 -23
- package/dist/config/paths.js +95 -38
- package/dist/config/settings.js +0 -21
- package/dist/index.js +0 -11
- package/dist/security/command-filter.js +0 -35
- package/dist/security/sandbox.js +1 -39
- package/dist/security/sanitize.js +0 -21
- package/dist/tools/bash.js +186 -32
- package/dist/tools/diff.js +0 -7
- package/dist/tools/edit-file.js +4 -12
- package/dist/tools/list-files.js +0 -5
- package/dist/tools/memory-tools.js +126 -0
- package/dist/tools/read-file.js +1 -7
- package/dist/tools/registry.js +7 -9
- package/dist/tools/search-files.js +2 -11
- package/dist/tools/task-tools.js +74 -0
- package/dist/tools/types.js +0 -4
- package/dist/tools/write-file.js +5 -12
- package/dist/types/index.js +0 -7
- package/dist/ui/App.js +160 -36
- package/dist/ui/InputBar.js +8 -9
- package/dist/ui/MessageList.js +25 -33
- package/dist/ui/ModeIndicator.js +6 -18
- package/dist/ui/PermissionPrompt.js +28 -29
- package/dist/ui/StatusBar.js +17 -17
- package/dist/ui/theme.js +112 -38
- package/dist/utils/abort.js +0 -12
- package/dist/utils/errors.js +0 -7
- package/dist/utils/formatting.js +0 -8
- package/dist/utils/logger.js +107 -17
- package/dist/version.js +1 -8
- package/package.json +1 -1
- package/dist/agent/chunker.js.map +0 -1
- package/dist/agent/history.js.map +0 -1
- package/dist/agent/loop.js.map +0 -1
- package/dist/agent/memory.js.map +0 -1
- package/dist/api-client/auth.js.map +0 -1
- package/dist/api-client/base.js.map +0 -1
- package/dist/api-client/stream.js.map +0 -1
- package/dist/api-client/updates.js.map +0 -1
- package/dist/api-client/usage.js.map +0 -1
- package/dist/auth/oauth.js.map +0 -1
- package/dist/auth/pkce.js.map +0 -1
- package/dist/auth/session.js.map +0 -1
- package/dist/auth/storage.js.map +0 -1
- package/dist/cli.js.map +0 -1
- package/dist/commands/chat.js.map +0 -1
- package/dist/commands/help.js.map +0 -1
- package/dist/commands/init.js.map +0 -1
- package/dist/commands/login.js.map +0 -1
- package/dist/commands/logout.js.map +0 -1
- package/dist/commands/update.js.map +0 -1
- package/dist/commands/usage.js.map +0 -1
- package/dist/config/defaults.js.map +0 -1
- package/dist/config/hooks.js.map +0 -1
- package/dist/config/paths.js.map +0 -1
- package/dist/config/settings.js.map +0 -1
- package/dist/index.js.map +0 -1
- package/dist/security/command-filter.js.map +0 -1
- package/dist/security/sandbox.js.map +0 -1
- package/dist/security/sanitize.js.map +0 -1
- package/dist/tools/bash.js.map +0 -1
- package/dist/tools/diff.js.map +0 -1
- package/dist/tools/edit-file.js.map +0 -1
- package/dist/tools/list-files.js.map +0 -1
- package/dist/tools/read-file.js.map +0 -1
- package/dist/tools/registry.js.map +0 -1
- package/dist/tools/search-files.js.map +0 -1
- package/dist/tools/types.js.map +0 -1
- package/dist/tools/write-file.js.map +0 -1
- package/dist/types/index.js.map +0 -1
- package/dist/ui/App.js.map +0 -1
- package/dist/ui/InputBar.js.map +0 -1
- package/dist/ui/MessageList.js.map +0 -1
- package/dist/ui/ModeIndicator.js.map +0 -1
- package/dist/ui/PermissionPrompt.js.map +0 -1
- package/dist/ui/StatusBar.js.map +0 -1
- package/dist/ui/theme.js.map +0 -1
- package/dist/utils/abort.js.map +0 -1
- package/dist/utils/errors.js.map +0 -1
- package/dist/utils/formatting.js.map +0 -1
- package/dist/utils/logger.js.map +0 -1
- package/dist/version.js.map +0 -1
package/dist/tools/bash.js
CHANGED
|
@@ -1,22 +1,109 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* bash_execute — sandboxed shell komutu.
|
|
3
|
-
*
|
|
4
|
-
* Windows'ta cmd.exe, Unix'te sh kullanır. 120 saniye timeout, 50 KB çıktı sınırı.
|
|
5
|
-
* Plan modda her şey yasak, default'ta permission prompt, auto'da classifier.
|
|
6
|
-
*/
|
|
7
1
|
import { spawn } from "node:child_process";
|
|
8
2
|
import { checkCommand, autoModeDecision } from "../security/command-filter.js";
|
|
9
3
|
import { checkPath } from "../security/sandbox.js";
|
|
10
4
|
const MAX_OUTPUT_BYTES = 50_000;
|
|
11
|
-
const DEFAULT_TIMEOUT_MS =
|
|
12
|
-
|
|
5
|
+
const DEFAULT_TIMEOUT_MS = 300_000;
|
|
6
|
+
const LIVE_OUTPUT_THROTTLE_MS = 150;
|
|
7
|
+
const INTERACTIVE_PATTERNS = [
|
|
8
|
+
{
|
|
9
|
+
regex: /\bcreate-next-app\b(?![^|]*(?:--ts|--javascript|--yes|-y\b))/i,
|
|
10
|
+
reason: "create-next-app interaktif modda çalıştırılıyor (sorulara cevap verilemez)",
|
|
11
|
+
suggestion: 'Non-interactive flag\'lerle çağırın, örn.:\n npx create-next-app@latest proje-adi --typescript --tailwind --eslint --app --src-dir --import-alias "@/*" --use-npm --yes',
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
regex: /\bcreate-react-app\b/i,
|
|
15
|
+
reason: "create-react-app interaktif",
|
|
16
|
+
suggestion: 'npx create-react-app proje-adi --template typescript',
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
regex: /\bnpm\s+init\b(?!\s+-y\b)(?!\s+--yes\b)/i,
|
|
20
|
+
reason: "npm init interaktif modda",
|
|
21
|
+
suggestion: "npm init -y",
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
regex: /\byarn\s+create\b(?![^|]*--yes)/i,
|
|
25
|
+
reason: "yarn create interaktif",
|
|
26
|
+
suggestion: "yarn create <template> --yes",
|
|
27
|
+
},
|
|
28
|
+
{
|
|
29
|
+
regex: /\bpnpm\s+create\b(?![^|]*--yes)/i,
|
|
30
|
+
reason: "pnpm create interaktif",
|
|
31
|
+
suggestion: "pnpm create <template> --yes",
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
regex: /\bgit\s+commit\b(?!\s+-m\b)(?!\s+--message\b)(?!\s+--no-edit\b)/i,
|
|
35
|
+
reason: "git commit mesajsız → editör açılır (interaktif)",
|
|
36
|
+
suggestion: 'git commit -m "mesajınız"',
|
|
37
|
+
},
|
|
38
|
+
{
|
|
39
|
+
regex: /\b(?:npm|yarn|pnpm)\s+(?:run\s+)?dev\b/i,
|
|
40
|
+
reason: "Dev server sonsuz çalışır, bash_execute bunu yönetemez",
|
|
41
|
+
suggestion: "Kullanıcıya dev server'ı manuel başlatmasını söyleyin:\n cd proje-adi && npm run dev",
|
|
42
|
+
},
|
|
43
|
+
{
|
|
44
|
+
regex: /\b(?:next|vite|nuxt|remix)\s+dev\b/i,
|
|
45
|
+
reason: "Dev server sonsuz çalışır, bash_execute bunu yönetemez",
|
|
46
|
+
suggestion: "Kullanıcıya dev server'ı manuel başlatmasını söyleyin:\n cd proje-adi && npm run dev",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
regex: /\bnodemon\b/i,
|
|
50
|
+
reason: "nodemon sonsuz çalışır (watch modu)",
|
|
51
|
+
suggestion: "Kullanıcıya nodemon'u manuel başlatmasını söyleyin",
|
|
52
|
+
},
|
|
53
|
+
{
|
|
54
|
+
regex: /\b(?:python|python3)\s+(?:-m\s+)?(?:manage\.py\s+runserver|http\.server)\b/i,
|
|
55
|
+
reason: "Python dev server sonsuz çalışır",
|
|
56
|
+
suggestion: "Kullanıcıya sunucuyu manuel başlatmasını söyleyin",
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
regex: /\b(?:rails|bundle\s+exec\s+rails)\s+server\b/i,
|
|
60
|
+
reason: "Rails server sonsuz çalışır",
|
|
61
|
+
suggestion: "Kullanıcıya Rails server'ı manuel başlatmasını söyleyin",
|
|
62
|
+
},
|
|
63
|
+
{
|
|
64
|
+
regex: /\bphp\s+artisan\s+serve\b/i,
|
|
65
|
+
reason: "Laravel serve sonsuz çalışır",
|
|
66
|
+
suggestion: "Kullanıcıya Laravel sunucusunu manuel başlatmasını söyleyin",
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
regex: /\bdocker(?:-|\s+)compose\s+up\b(?!\s+-d\b)(?!\s+--detach\b)/i,
|
|
70
|
+
reason: "docker compose up (detach'sız) foreground'da sonsuz çalışır",
|
|
71
|
+
suggestion: "docker compose up -d (detached mod)",
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
regex: /\btail\s+-f\b/i,
|
|
75
|
+
reason: "tail -f sürekli dosya takibi yapar",
|
|
76
|
+
suggestion: "tail -n 100 (son 100 satır için)",
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
regex: /\bwatch\s+/i,
|
|
80
|
+
reason: "watch komutu sürekli döngü",
|
|
81
|
+
suggestion: "Tek seferlik çağrı yapın",
|
|
82
|
+
},
|
|
83
|
+
];
|
|
84
|
+
function detectInteractive(command) {
|
|
85
|
+
for (const pattern of INTERACTIVE_PATTERNS) {
|
|
86
|
+
if (pattern.regex.test(command)) {
|
|
87
|
+
return { ok: false, reason: pattern.reason, suggestion: pattern.suggestion };
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
return { ok: true };
|
|
91
|
+
}
|
|
92
|
+
function runShell(command, cwd, timeoutMs, signal, onLive) {
|
|
13
93
|
return new Promise((resolve) => {
|
|
14
94
|
const isWin = process.platform === "win32";
|
|
15
95
|
const shell = isWin ? "cmd.exe" : "/bin/sh";
|
|
16
96
|
const args = isWin ? ["/d", "/s", "/c", command] : ["-c", command];
|
|
17
97
|
const child = spawn(shell, args, {
|
|
18
98
|
cwd,
|
|
19
|
-
|
|
99
|
+
stdio: ["ignore", "pipe", "pipe"],
|
|
100
|
+
env: {
|
|
101
|
+
...process.env,
|
|
102
|
+
CI: "1",
|
|
103
|
+
NO_COLOR: "0",
|
|
104
|
+
npm_config_yes: "true",
|
|
105
|
+
DEBIAN_FRONTEND: "noninteractive",
|
|
106
|
+
},
|
|
20
107
|
windowsHide: true,
|
|
21
108
|
});
|
|
22
109
|
let stdout = "";
|
|
@@ -24,13 +111,44 @@ function runShell(command, cwd, timeoutMs, signal) {
|
|
|
24
111
|
let outBytes = 0;
|
|
25
112
|
let errBytes = 0;
|
|
26
113
|
let timedOut = false;
|
|
114
|
+
let stdoutBuffer = "";
|
|
115
|
+
let stderrBuffer = "";
|
|
116
|
+
let flushTimer = null;
|
|
117
|
+
const scheduleFlush = () => {
|
|
118
|
+
if (flushTimer || !onLive)
|
|
119
|
+
return;
|
|
120
|
+
flushTimer = setTimeout(() => {
|
|
121
|
+
flushTimer = null;
|
|
122
|
+
if (stdoutBuffer) {
|
|
123
|
+
onLive("stdout", stdoutBuffer);
|
|
124
|
+
stdoutBuffer = "";
|
|
125
|
+
}
|
|
126
|
+
if (stderrBuffer) {
|
|
127
|
+
onLive("stderr", stderrBuffer);
|
|
128
|
+
stderrBuffer = "";
|
|
129
|
+
}
|
|
130
|
+
}, LIVE_OUTPUT_THROTTLE_MS);
|
|
131
|
+
};
|
|
132
|
+
const finalFlush = () => {
|
|
133
|
+
if (flushTimer) {
|
|
134
|
+
clearTimeout(flushTimer);
|
|
135
|
+
flushTimer = null;
|
|
136
|
+
}
|
|
137
|
+
if (onLive && stdoutBuffer) {
|
|
138
|
+
onLive("stdout", stdoutBuffer);
|
|
139
|
+
stdoutBuffer = "";
|
|
140
|
+
}
|
|
141
|
+
if (onLive && stderrBuffer) {
|
|
142
|
+
onLive("stderr", stderrBuffer);
|
|
143
|
+
stderrBuffer = "";
|
|
144
|
+
}
|
|
145
|
+
};
|
|
27
146
|
const timeoutHandle = setTimeout(() => {
|
|
28
147
|
timedOut = true;
|
|
29
148
|
try {
|
|
30
149
|
child.kill("SIGKILL");
|
|
31
150
|
}
|
|
32
151
|
catch {
|
|
33
|
-
// ignore
|
|
34
152
|
}
|
|
35
153
|
}, timeoutMs);
|
|
36
154
|
const onAbort = () => {
|
|
@@ -38,7 +156,6 @@ function runShell(command, cwd, timeoutMs, signal) {
|
|
|
38
156
|
child.kill("SIGKILL");
|
|
39
157
|
}
|
|
40
158
|
catch {
|
|
41
|
-
// ignore
|
|
42
159
|
}
|
|
43
160
|
};
|
|
44
161
|
if (signal) {
|
|
@@ -48,23 +165,34 @@ function runShell(command, cwd, timeoutMs, signal) {
|
|
|
48
165
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
49
166
|
}
|
|
50
167
|
child.stdout.on("data", (chunk) => {
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
168
|
+
const text = chunk.toString("utf8");
|
|
169
|
+
if (outBytes < MAX_OUTPUT_BYTES) {
|
|
170
|
+
const remaining = MAX_OUTPUT_BYTES - outBytes;
|
|
171
|
+
const trimmed = text.slice(0, remaining);
|
|
172
|
+
stdout += trimmed;
|
|
173
|
+
outBytes += trimmed.length;
|
|
174
|
+
}
|
|
175
|
+
if (onLive) {
|
|
176
|
+
stdoutBuffer += text;
|
|
177
|
+
scheduleFlush();
|
|
178
|
+
}
|
|
57
179
|
});
|
|
58
180
|
child.stderr.on("data", (chunk) => {
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
181
|
+
const text = chunk.toString("utf8");
|
|
182
|
+
if (errBytes < MAX_OUTPUT_BYTES) {
|
|
183
|
+
const remaining = MAX_OUTPUT_BYTES - errBytes;
|
|
184
|
+
const trimmed = text.slice(0, remaining);
|
|
185
|
+
stderr += trimmed;
|
|
186
|
+
errBytes += trimmed.length;
|
|
187
|
+
}
|
|
188
|
+
if (onLive) {
|
|
189
|
+
stderrBuffer += text;
|
|
190
|
+
scheduleFlush();
|
|
191
|
+
}
|
|
65
192
|
});
|
|
66
193
|
child.on("error", (err) => {
|
|
67
194
|
clearTimeout(timeoutHandle);
|
|
195
|
+
finalFlush();
|
|
68
196
|
resolve({
|
|
69
197
|
stdout,
|
|
70
198
|
stderr: `${stderr}\nSpawn hatası: ${err.message}`,
|
|
@@ -76,6 +204,7 @@ function runShell(command, cwd, timeoutMs, signal) {
|
|
|
76
204
|
clearTimeout(timeoutHandle);
|
|
77
205
|
if (signal)
|
|
78
206
|
signal.removeEventListener("abort", onAbort);
|
|
207
|
+
finalFlush();
|
|
79
208
|
resolve({
|
|
80
209
|
stdout,
|
|
81
210
|
stderr,
|
|
@@ -90,7 +219,7 @@ export const bashTool = async (params, ctx) => {
|
|
|
90
219
|
const command = (params.command || "");
|
|
91
220
|
const rawCwd = (params.working_directory || ctx.cwd);
|
|
92
221
|
const timeoutMs = Number.isInteger(params.timeout_ms)
|
|
93
|
-
? Math.min(
|
|
222
|
+
? Math.min(600_000, Math.max(5_000, Number(params.timeout_ms)))
|
|
94
223
|
: DEFAULT_TIMEOUT_MS;
|
|
95
224
|
if (!command) {
|
|
96
225
|
return {
|
|
@@ -100,7 +229,6 @@ export const bashTool = async (params, ctx) => {
|
|
|
100
229
|
duration: Date.now() - start,
|
|
101
230
|
};
|
|
102
231
|
}
|
|
103
|
-
// Command filter
|
|
104
232
|
const filterResult = checkCommand(command);
|
|
105
233
|
if (filterResult.verdict === "block") {
|
|
106
234
|
return {
|
|
@@ -112,7 +240,22 @@ export const bashTool = async (params, ctx) => {
|
|
|
112
240
|
duration: Date.now() - start,
|
|
113
241
|
};
|
|
114
242
|
}
|
|
115
|
-
|
|
243
|
+
const interactiveCheck = detectInteractive(command);
|
|
244
|
+
if (!interactiveCheck.ok) {
|
|
245
|
+
return {
|
|
246
|
+
success: false,
|
|
247
|
+
output: "",
|
|
248
|
+
error: `${interactiveCheck.reason}\n\n` +
|
|
249
|
+
`Öneri:\n${interactiveCheck.suggestion}\n\n` +
|
|
250
|
+
`Lütfen non-interactive flag'lerle tekrar deneyin.`,
|
|
251
|
+
metadata: {
|
|
252
|
+
blocked_by: "interactive_detector",
|
|
253
|
+
suggestion: interactiveCheck.suggestion,
|
|
254
|
+
},
|
|
255
|
+
approvalState: 0,
|
|
256
|
+
duration: Date.now() - start,
|
|
257
|
+
};
|
|
258
|
+
}
|
|
116
259
|
if (ctx.mode === "plan") {
|
|
117
260
|
return {
|
|
118
261
|
success: true,
|
|
@@ -122,7 +265,6 @@ export const bashTool = async (params, ctx) => {
|
|
|
122
265
|
duration: Date.now() - start,
|
|
123
266
|
};
|
|
124
267
|
}
|
|
125
|
-
// Working directory sandbox kontrolü
|
|
126
268
|
const cwdCheck = checkPath(rawCwd, ctx.cwd, { mustExist: true });
|
|
127
269
|
if (!cwdCheck.ok || !cwdCheck.realPath) {
|
|
128
270
|
return {
|
|
@@ -132,7 +274,6 @@ export const bashTool = async (params, ctx) => {
|
|
|
132
274
|
duration: Date.now() - start,
|
|
133
275
|
};
|
|
134
276
|
}
|
|
135
|
-
// Permission akışı
|
|
136
277
|
let approvalState = 2;
|
|
137
278
|
const needsPrompt = ctx.mode === "default" ||
|
|
138
279
|
ctx.mode === "accept_edits" ||
|
|
@@ -173,7 +314,19 @@ export const bashTool = async (params, ctx) => {
|
|
|
173
314
|
}
|
|
174
315
|
approvalState = 1;
|
|
175
316
|
}
|
|
176
|
-
const
|
|
317
|
+
const onLive = typeof ctx.onLiveOutput === "function"
|
|
318
|
+
? (kind, chunk) => {
|
|
319
|
+
ctx.onLiveOutput({ toolName: "bash_execute", kind, chunk });
|
|
320
|
+
}
|
|
321
|
+
: null;
|
|
322
|
+
if (ctx.onLiveOutput) {
|
|
323
|
+
ctx.onLiveOutput({
|
|
324
|
+
toolName: "bash_execute",
|
|
325
|
+
kind: "status",
|
|
326
|
+
chunk: `$ ${command}`,
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
const result = await runShell(command, cwdCheck.realPath, timeoutMs, ctx.signal, onLive);
|
|
177
330
|
const output = [
|
|
178
331
|
result.stdout && `--- stdout ---\n${result.stdout}`,
|
|
179
332
|
result.stderr && `--- stderr ---\n${result.stderr}`,
|
|
@@ -184,8 +337,10 @@ export const bashTool = async (params, ctx) => {
|
|
|
184
337
|
return {
|
|
185
338
|
success: false,
|
|
186
339
|
output,
|
|
187
|
-
error: `Komut zaman aşımına uğradı (${timeoutMs}
|
|
188
|
-
|
|
340
|
+
error: `Komut zaman aşımına uğradı (${Math.floor(timeoutMs / 1000)}s)\n\n` +
|
|
341
|
+
`Uzun süren komutlar için timeout_ms parametresini artırabilirsiniz (maks 600000 = 10 dk).\n` +
|
|
342
|
+
`Veya komutu arka planda çalıştırın (& ile) ve durumunu ayrı bir komutla kontrol edin.`,
|
|
343
|
+
metadata: { exit_code: result.exitCode, timed_out: true, timeout_ms: timeoutMs },
|
|
189
344
|
approvalState,
|
|
190
345
|
duration: Date.now() - start,
|
|
191
346
|
};
|
|
@@ -203,4 +358,3 @@ export const bashTool = async (params, ctx) => {
|
|
|
203
358
|
duration: Date.now() - start,
|
|
204
359
|
};
|
|
205
360
|
};
|
|
206
|
-
//# sourceMappingURL=bash.js.map
|
package/dist/tools/diff.js
CHANGED
|
@@ -1,9 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AIGENCY CLI — basit unified diff üretici.
|
|
3
|
-
*
|
|
4
|
-
* Dış bağımlılık (diff paketi) kullanmadan okunabilir bir +/- diff üretir.
|
|
5
|
-
* Myers algoritması yerine satır bazlı basit farklı bulma — çoğu use case için yeterli.
|
|
6
|
-
*/
|
|
7
1
|
export function simpleDiff(oldContent, newContent, maxLines = 40) {
|
|
8
2
|
const oldLines = oldContent.split(/\r?\n/);
|
|
9
3
|
const newLines = newContent.split(/\r?\n/);
|
|
@@ -53,4 +47,3 @@ export function summarizeDiff(oldContent, newContent) {
|
|
|
53
47
|
}
|
|
54
48
|
return { added, removed };
|
|
55
49
|
}
|
|
56
|
-
//# sourceMappingURL=diff.js.map
|
package/dist/tools/edit-file.js
CHANGED
|
@@ -1,14 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* edit_file — mevcut dosyada old_string'i new_string ile değiştir.
|
|
3
|
-
*
|
|
4
|
-
* Kurallar (benzersiz edit):
|
|
5
|
-
* - old_string tam olarak dosyada 1 kez geçmeli
|
|
6
|
-
* - 0 veya 1'den fazla eşleşme varsa reddedilir
|
|
7
|
-
* - Plan modda yasak, diğer modlarda permission prompt
|
|
8
|
-
*/
|
|
9
1
|
import fs from "node:fs/promises";
|
|
10
2
|
import { checkWritePath } from "../security/sandbox.js";
|
|
11
3
|
import { simpleDiff, summarizeDiff } from "./diff.js";
|
|
4
|
+
import { captureCheckpoint } from "../agent/checkpoints.js";
|
|
12
5
|
export const editFileTool = async (params, ctx) => {
|
|
13
6
|
const start = Date.now();
|
|
14
7
|
const rawPath = (params.path || params.filePath || "");
|
|
@@ -63,7 +56,6 @@ export const editFileTool = async (params, ctx) => {
|
|
|
63
56
|
duration: Date.now() - start,
|
|
64
57
|
};
|
|
65
58
|
}
|
|
66
|
-
// Mevcut içeriği oku
|
|
67
59
|
let existing;
|
|
68
60
|
try {
|
|
69
61
|
existing = await fs.readFile(check.realPath, "utf8");
|
|
@@ -76,7 +68,6 @@ export const editFileTool = async (params, ctx) => {
|
|
|
76
68
|
duration: Date.now() - start,
|
|
77
69
|
};
|
|
78
70
|
}
|
|
79
|
-
// Benzersizlik kontrolü
|
|
80
71
|
const firstIdx = existing.indexOf(oldString);
|
|
81
72
|
if (firstIdx === -1) {
|
|
82
73
|
return {
|
|
@@ -100,7 +91,6 @@ export const editFileTool = async (params, ctx) => {
|
|
|
100
91
|
existing.slice(firstIdx + oldString.length);
|
|
101
92
|
const diff = summarizeDiff(existing, updated);
|
|
102
93
|
const previewLines = simpleDiff(existing, updated, 20);
|
|
103
|
-
// Permission
|
|
104
94
|
let approvalState = 2;
|
|
105
95
|
if (ctx.mode === "default") {
|
|
106
96
|
if (!ctx.requestPermission) {
|
|
@@ -139,6 +129,9 @@ export const editFileTool = async (params, ctx) => {
|
|
|
139
129
|
approvalState = 1;
|
|
140
130
|
}
|
|
141
131
|
try {
|
|
132
|
+
if (ctx.sessionId && ctx.projectRoot) {
|
|
133
|
+
await captureCheckpoint(ctx.sessionId, check.realPath, "edit", ctx.projectRoot);
|
|
134
|
+
}
|
|
142
135
|
await fs.writeFile(check.realPath, updated, "utf8");
|
|
143
136
|
}
|
|
144
137
|
catch (err) {
|
|
@@ -162,4 +155,3 @@ export const editFileTool = async (params, ctx) => {
|
|
|
162
155
|
duration: Date.now() - start,
|
|
163
156
|
};
|
|
164
157
|
};
|
|
165
|
-
//# sourceMappingURL=edit-file.js.map
|
package/dist/tools/list-files.js
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* list_files — dizin listeleme tool'u (yerel).
|
|
3
|
-
*/
|
|
4
1
|
import fs from "node:fs/promises";
|
|
5
2
|
import path from "node:path";
|
|
6
3
|
import { checkPath } from "../security/sandbox.js";
|
|
7
4
|
const MAX_ENTRIES = 500;
|
|
8
5
|
const MAX_DEPTH = 4;
|
|
9
|
-
// Varsayılan olarak atlanacak isimler (yasak segmentlerle aynı mantık)
|
|
10
6
|
const SKIP_NAMES = new Set([
|
|
11
7
|
".git",
|
|
12
8
|
"node_modules",
|
|
@@ -113,4 +109,3 @@ export const listFilesTool = async (params, ctx) => {
|
|
|
113
109
|
duration: Date.now() - start,
|
|
114
110
|
};
|
|
115
111
|
};
|
|
116
|
-
//# sourceMappingURL=list-files.js.map
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import { saveMemoryEntry, readMemoryTopic, deleteMemoryEntry, loadMemorySnapshot, } from "../agent/memory.js";
|
|
2
|
+
const VALID_TYPES = new Set(["user", "feedback", "project", "reference"]);
|
|
3
|
+
export const saveMemoryToolHandler = async (params, ctx) => {
|
|
4
|
+
const start = Date.now();
|
|
5
|
+
const filename = String(params.filename || "").trim();
|
|
6
|
+
const name = String(params.name || "").trim();
|
|
7
|
+
const description = String(params.description || "").trim();
|
|
8
|
+
const type = String(params.type || "").trim();
|
|
9
|
+
const content = String(params.content || "").trim();
|
|
10
|
+
if (!filename || !name || !description || !type || !content) {
|
|
11
|
+
return {
|
|
12
|
+
success: false,
|
|
13
|
+
output: "",
|
|
14
|
+
error: "save_memory: filename, name, description, type ve content alanları zorunludur",
|
|
15
|
+
duration: Date.now() - start,
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
if (!VALID_TYPES.has(type)) {
|
|
19
|
+
return {
|
|
20
|
+
success: false,
|
|
21
|
+
output: "",
|
|
22
|
+
error: `save_memory: geçersiz tip '${type}' (user|feedback|project|reference olmalı)`,
|
|
23
|
+
duration: Date.now() - start,
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
const result = await saveMemoryEntry({ filename, name, description, type, content }, ctx.cwd);
|
|
27
|
+
if (!result.ok) {
|
|
28
|
+
return {
|
|
29
|
+
success: false,
|
|
30
|
+
output: "",
|
|
31
|
+
error: result.error,
|
|
32
|
+
duration: Date.now() - start,
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
return {
|
|
36
|
+
success: true,
|
|
37
|
+
output: `Memory kaydedildi: ${result.filename} (${type})`,
|
|
38
|
+
metadata: { filename: result.filename, type },
|
|
39
|
+
approvalState: 2,
|
|
40
|
+
duration: Date.now() - start,
|
|
41
|
+
};
|
|
42
|
+
};
|
|
43
|
+
export const readMemoryToolHandler = async (params, ctx) => {
|
|
44
|
+
const start = Date.now();
|
|
45
|
+
const filename = String(params.filename || "").trim();
|
|
46
|
+
if (!filename) {
|
|
47
|
+
return {
|
|
48
|
+
success: false,
|
|
49
|
+
output: "",
|
|
50
|
+
error: "read_memory: filename zorunlu",
|
|
51
|
+
duration: Date.now() - start,
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
const entry = await readMemoryTopic(filename, ctx.cwd);
|
|
55
|
+
if (!entry) {
|
|
56
|
+
return {
|
|
57
|
+
success: false,
|
|
58
|
+
output: "",
|
|
59
|
+
error: `read_memory: '${filename}' bulunamadı veya frontmatter eksik`,
|
|
60
|
+
duration: Date.now() - start,
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
const output = [
|
|
64
|
+
`# ${entry.frontmatter.name}`,
|
|
65
|
+
`Tip: ${entry.frontmatter.type}`,
|
|
66
|
+
`Açıklama: ${entry.frontmatter.description}`,
|
|
67
|
+
`Son güncelleme: ${entry.modifiedAt.toISOString()}`,
|
|
68
|
+
"",
|
|
69
|
+
"---",
|
|
70
|
+
"",
|
|
71
|
+
entry.body.trim(),
|
|
72
|
+
].join("\n");
|
|
73
|
+
return {
|
|
74
|
+
success: true,
|
|
75
|
+
output,
|
|
76
|
+
metadata: {
|
|
77
|
+
filename: entry.filename,
|
|
78
|
+
type: entry.frontmatter.type,
|
|
79
|
+
sizeBytes: entry.sizeBytes,
|
|
80
|
+
},
|
|
81
|
+
approvalState: 2,
|
|
82
|
+
duration: Date.now() - start,
|
|
83
|
+
};
|
|
84
|
+
};
|
|
85
|
+
export const deleteMemoryToolHandler = async (params, ctx) => {
|
|
86
|
+
const start = Date.now();
|
|
87
|
+
const filename = String(params.filename || "").trim();
|
|
88
|
+
if (!filename) {
|
|
89
|
+
return {
|
|
90
|
+
success: false,
|
|
91
|
+
output: "",
|
|
92
|
+
error: "delete_memory: filename zorunlu",
|
|
93
|
+
duration: Date.now() - start,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
try {
|
|
97
|
+
const deleted = await deleteMemoryEntry(filename, ctx.cwd);
|
|
98
|
+
if (!deleted) {
|
|
99
|
+
return {
|
|
100
|
+
success: false,
|
|
101
|
+
output: "",
|
|
102
|
+
error: `delete_memory: '${filename}' bulunamadı`,
|
|
103
|
+
duration: Date.now() - start,
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
return {
|
|
107
|
+
success: true,
|
|
108
|
+
output: `Memory silindi: ${filename}`,
|
|
109
|
+
metadata: { filename },
|
|
110
|
+
approvalState: 2,
|
|
111
|
+
duration: Date.now() - start,
|
|
112
|
+
};
|
|
113
|
+
}
|
|
114
|
+
catch (err) {
|
|
115
|
+
return {
|
|
116
|
+
success: false,
|
|
117
|
+
output: "",
|
|
118
|
+
error: `delete_memory hatası: ${err.message}`,
|
|
119
|
+
duration: Date.now() - start,
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
export async function listMemoryEntries(cwd = process.cwd()) {
|
|
124
|
+
const snapshot = await loadMemorySnapshot(cwd);
|
|
125
|
+
return snapshot.entries.map((e) => `${e.filename} [${e.frontmatter.type}] — ${e.frontmatter.description}`);
|
|
126
|
+
}
|
package/dist/tools/read-file.js
CHANGED
|
@@ -1,9 +1,6 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* read_file — dosya okuma tool'u (yerel).
|
|
3
|
-
*/
|
|
4
1
|
import fs from "node:fs/promises";
|
|
5
2
|
import { checkPath } from "../security/sandbox.js";
|
|
6
|
-
const MAX_BYTES = 512 * 1024;
|
|
3
|
+
const MAX_BYTES = 512 * 1024;
|
|
7
4
|
const DEFAULT_LINE_LIMIT = 2000;
|
|
8
5
|
export const readFileTool = async (params, ctx) => {
|
|
9
6
|
const start = Date.now();
|
|
@@ -71,7 +68,6 @@ export const readFileTool = async (params, ctx) => {
|
|
|
71
68
|
duration: Date.now() - start,
|
|
72
69
|
};
|
|
73
70
|
}
|
|
74
|
-
// NULL byte → binary dosya
|
|
75
71
|
if (content.includes("\u0000")) {
|
|
76
72
|
return {
|
|
77
73
|
success: false,
|
|
@@ -80,7 +76,6 @@ export const readFileTool = async (params, ctx) => {
|
|
|
80
76
|
duration: Date.now() - start,
|
|
81
77
|
};
|
|
82
78
|
}
|
|
83
|
-
// Satır aralığı uygula
|
|
84
79
|
const lines = content.split(/\r?\n/);
|
|
85
80
|
const startIdx = startLine && startLine > 0 ? startLine - 1 : 0;
|
|
86
81
|
const endIdx = endLine && endLine > 0 ? Math.min(endLine, lines.length) : startIdx + DEFAULT_LINE_LIMIT;
|
|
@@ -104,4 +99,3 @@ export const readFileTool = async (params, ctx) => {
|
|
|
104
99
|
duration: Date.now() - start,
|
|
105
100
|
};
|
|
106
101
|
};
|
|
107
|
-
//# sourceMappingURL=read-file.js.map
|
package/dist/tools/registry.js
CHANGED
|
@@ -1,16 +1,11 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* AIGENCY CLI — Yerel tool registry.
|
|
3
|
-
*
|
|
4
|
-
* Sunucu tarafında `lib/agent/cli-tool-registry.ts` LLM'e şemaları sunar.
|
|
5
|
-
* CLI burada tool'ları **yerel çalıştırır**. Sunucudan gelen cli_tool_dispatch
|
|
6
|
-
* event'i bu registry üzerinden dispatch edilir.
|
|
7
|
-
*/
|
|
8
1
|
import { readFileTool } from "./read-file.js";
|
|
9
2
|
import { listFilesTool } from "./list-files.js";
|
|
10
3
|
import { searchFilesTool } from "./search-files.js";
|
|
11
4
|
import { writeFileTool } from "./write-file.js";
|
|
12
5
|
import { editFileTool } from "./edit-file.js";
|
|
13
6
|
import { bashTool } from "./bash.js";
|
|
7
|
+
import { saveMemoryToolHandler, readMemoryToolHandler, deleteMemoryToolHandler, } from "./memory-tools.js";
|
|
8
|
+
import { writeTasksHandler, readTasksHandler } from "./task-tools.js";
|
|
14
9
|
const HANDLERS = {
|
|
15
10
|
read_file: readFileTool,
|
|
16
11
|
list_files: listFilesTool,
|
|
@@ -18,6 +13,11 @@ const HANDLERS = {
|
|
|
18
13
|
write_file: writeFileTool,
|
|
19
14
|
edit_file: editFileTool,
|
|
20
15
|
bash_execute: bashTool,
|
|
16
|
+
save_memory: saveMemoryToolHandler,
|
|
17
|
+
read_memory: readMemoryToolHandler,
|
|
18
|
+
delete_memory: deleteMemoryToolHandler,
|
|
19
|
+
write_tasks: writeTasksHandler,
|
|
20
|
+
read_tasks: readTasksHandler,
|
|
21
21
|
};
|
|
22
22
|
export function hasLocalTool(name) {
|
|
23
23
|
return name in HANDLERS;
|
|
@@ -38,7 +38,6 @@ export async function dispatchLocalTool(toolName, params, ctx) {
|
|
|
38
38
|
}
|
|
39
39
|
catch (err) {
|
|
40
40
|
const message = err.message || "Bilinmeyen hata";
|
|
41
|
-
// Path/username gibi sızıntıları temizle
|
|
42
41
|
const safe = message.replace(/[A-Z]:\\[^\s"')]+/gi, "[gizli]").slice(0, 200);
|
|
43
42
|
return {
|
|
44
43
|
success: false,
|
|
@@ -51,4 +50,3 @@ export async function dispatchLocalTool(toolName, params, ctx) {
|
|
|
51
50
|
export function listLocalTools() {
|
|
52
51
|
return Object.keys(HANDLERS);
|
|
53
52
|
}
|
|
54
|
-
//# sourceMappingURL=registry.js.map
|
|
@@ -1,16 +1,9 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* search_files — proje içi regex/literal arama (ripgrep benzeri).
|
|
3
|
-
*
|
|
4
|
-
* Not: Harici ripgrep kullanmak için exec yapmıyoruz (Bun compile'da bağımlı
|
|
5
|
-
* olmak istemiyoruz); kendi basit arama fonksiyonumuzu yazıyoruz. Büyük
|
|
6
|
-
* projelerde yavaş olabilir ama yazma/izin katmanı için yeterli.
|
|
7
|
-
*/
|
|
8
1
|
import fs from "node:fs/promises";
|
|
9
2
|
import path from "node:path";
|
|
10
3
|
import { checkPath } from "../security/sandbox.js";
|
|
11
4
|
const MAX_MATCHES = 200;
|
|
12
5
|
const MAX_FILES_SCANNED = 2000;
|
|
13
|
-
const MAX_FILE_SIZE = 512 * 1024;
|
|
6
|
+
const MAX_FILE_SIZE = 512 * 1024;
|
|
14
7
|
const SKIP_NAMES = new Set([
|
|
15
8
|
".git",
|
|
16
9
|
"node_modules",
|
|
@@ -25,7 +18,6 @@ const SKIP_NAMES = new Set([
|
|
|
25
18
|
".cache",
|
|
26
19
|
]);
|
|
27
20
|
function globToRegex(glob) {
|
|
28
|
-
// Çok basit glob → regex: *, **, ?
|
|
29
21
|
const escaped = glob
|
|
30
22
|
.replace(/[.+^${}()|[\]\\]/g, "\\$&")
|
|
31
23
|
.replace(/\*\*/g, "::DOUBLE_STAR::")
|
|
@@ -121,7 +113,7 @@ export const searchFilesTool = async (params, ctx) => {
|
|
|
121
113
|
continue;
|
|
122
114
|
}
|
|
123
115
|
if (content.includes("\u0000"))
|
|
124
|
-
continue;
|
|
116
|
+
continue;
|
|
125
117
|
const lines = content.split(/\r?\n/);
|
|
126
118
|
for (let i = 0; i < lines.length; i++) {
|
|
127
119
|
if (matches.length >= MAX_MATCHES)
|
|
@@ -156,4 +148,3 @@ export const searchFilesTool = async (params, ctx) => {
|
|
|
156
148
|
duration: Date.now() - start,
|
|
157
149
|
};
|
|
158
150
|
};
|
|
159
|
-
//# sourceMappingURL=search-files.js.map
|