@semalt-ai/code 1.8.3 → 1.8.5
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.claude/settings.local.json +3 -1
- package/CLAUDE.md +4 -1
- package/TECHNICAL_DEBT.md +66 -0
- package/index.js +23 -9
- package/lib/agent.js +407 -129
- package/lib/api.js +105 -39
- package/lib/args.js +22 -0
- package/lib/commands.js +367 -132
- package/lib/config.js +14 -0
- package/lib/constants.js +1 -1
- package/lib/debug.js +106 -0
- package/lib/permissions.js +9 -8
- package/lib/proc.js +96 -0
- package/lib/prompts.js +8 -10
- package/lib/tool_specs.js +14 -7
- package/lib/tools.js +299 -118
- package/lib/ui/chat-history.js +37 -8
- package/lib/ui/create-ui.js +63 -38
- package/lib/ui/diff.js +4 -3
- package/lib/ui/format.js +321 -0
- package/lib/ui/input-field.js +134 -59
- package/lib/ui/layout.js +0 -2
- package/lib/ui/messages.js +44 -0
- package/lib/ui/select.js +114 -0
- package/lib/ui/status-bar.js +135 -28
- package/lib/ui/stream.js +8 -12
- package/lib/ui/terminal.js +12 -4
- package/lib/ui/theme.js +25 -4
- package/lib/ui/utils.js +94 -27
- package/lib/ui/writer.js +391 -45
- package/lib/ui.js +6 -6
- package/package.json +1 -1
- package/lib/ui/legacy.js +0 -130
package/lib/config.js
CHANGED
|
@@ -20,6 +20,7 @@ function _maybeWarnApiKeyAny(cfg) {
|
|
|
20
20
|
}
|
|
21
21
|
if (_LOCAL_HOSTS.has(host)) return;
|
|
22
22
|
_apiKeyAnyWarned = true;
|
|
23
|
+
// audit: allowed — pre-UI startup warning, fires once before TUI initialises.
|
|
23
24
|
process.stderr.write(
|
|
24
25
|
"⚠ api_key='any' against non-local endpoint — requests will likely fail " +
|
|
25
26
|
"with 401. Run 'semalt-code config set api_key <key>' to set a real key.\n"
|
|
@@ -110,6 +111,18 @@ function configSet(key, value) {
|
|
|
110
111
|
return cfg;
|
|
111
112
|
}
|
|
112
113
|
|
|
114
|
+
// Resolves whether the active profile uses native function calling.
|
|
115
|
+
// Defaults to true if no profile match is found (matches normalizeConfig
|
|
116
|
+
// default and the agent.js lookup fallback).
|
|
117
|
+
function isNativeToolsActive(model) {
|
|
118
|
+
const cfg = loadConfig();
|
|
119
|
+
if (!Array.isArray(cfg.models)) return true;
|
|
120
|
+
const profile = cfg.models.find(
|
|
121
|
+
(p) => p && p.api_base === cfg.api_base && p.model === model
|
|
122
|
+
);
|
|
123
|
+
return !(profile && profile.native_tools === false);
|
|
124
|
+
}
|
|
125
|
+
|
|
113
126
|
const REDACTED_KEYS = new Set(['api_key', 'auth_token']);
|
|
114
127
|
|
|
115
128
|
function configShow(systemPromptOverride = null) {
|
|
@@ -131,6 +144,7 @@ function configShow(systemPromptOverride = null) {
|
|
|
131
144
|
module.exports = {
|
|
132
145
|
configSet,
|
|
133
146
|
configShow,
|
|
147
|
+
isNativeToolsActive,
|
|
134
148
|
loadConfig,
|
|
135
149
|
normalizeConfig,
|
|
136
150
|
saveConfig,
|
package/lib/constants.js
CHANGED
package/lib/debug.js
ADDED
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
// Two mutually-exclusive debug modes, configured once at startup from the
|
|
4
|
+
// CLI flags (--debug or --debug-file <path>).
|
|
5
|
+
//
|
|
6
|
+
// off — no debug output anywhere.
|
|
7
|
+
// simple — visible inline. Basic per-iteration info routed through
|
|
8
|
+
// writer.scrollback so the TUI keeps working (no SSE dumps,
|
|
9
|
+
// no per-chunk noise).
|
|
10
|
+
// file — every debug call (basic AND extended) is written to a file.
|
|
11
|
+
// Nothing debug-related goes to stdout. The TUI stays clean.
|
|
12
|
+
//
|
|
13
|
+
// Two log functions with a clear semantic split:
|
|
14
|
+
//
|
|
15
|
+
// log(line) — "always-on" debug. Visible in simple mode (scrollback)
|
|
16
|
+
// and file mode (file). Silent in off mode.
|
|
17
|
+
// logExtended(line) — extended traces (raw SSE, request bodies, delta
|
|
18
|
+
// accumulators). Visible only in file mode.
|
|
19
|
+
//
|
|
20
|
+
// File-mode lines are formatted as `[ISO-timestamp] <line>\n` so they're
|
|
21
|
+
// greppable and tail-friendly.
|
|
22
|
+
|
|
23
|
+
const fs = require('fs');
|
|
24
|
+
|
|
25
|
+
let mode = 'off';
|
|
26
|
+
let fileStream = null;
|
|
27
|
+
|
|
28
|
+
function init({ debug, debugFile } = {}) {
|
|
29
|
+
if (debug && debugFile) {
|
|
30
|
+
// Belt-and-braces: cli.js (args parser) errors out before this is ever
|
|
31
|
+
// reached. Throw rather than silently coerce so any internal misuse is
|
|
32
|
+
// surfaced loudly.
|
|
33
|
+
throw new Error('debug and debugFile are mutually exclusive');
|
|
34
|
+
}
|
|
35
|
+
if (debugFile) {
|
|
36
|
+
mode = 'file';
|
|
37
|
+
fileStream = fs.createWriteStream(debugFile, { flags: 'a' });
|
|
38
|
+
const ts = new Date().toISOString();
|
|
39
|
+
try {
|
|
40
|
+
fileStream.write(`\n[${ts}] [session] semalt-code debug session start pid=${process.pid}\n`);
|
|
41
|
+
} catch {}
|
|
42
|
+
} else if (debug) {
|
|
43
|
+
mode = 'simple';
|
|
44
|
+
} else {
|
|
45
|
+
mode = 'off';
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function isActive() { return mode !== 'off'; }
|
|
50
|
+
function isSimple() { return mode === 'simple'; }
|
|
51
|
+
function isFile() { return mode === 'file'; }
|
|
52
|
+
function getMode() { return mode; }
|
|
53
|
+
|
|
54
|
+
function _writeFile(line) {
|
|
55
|
+
if (!fileStream) return;
|
|
56
|
+
const ts = new Date().toISOString();
|
|
57
|
+
try { fileStream.write(`[${ts}] ${line}\n`); } catch {}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// "Always-on" debug — visible in simple mode (scrollback) and file mode (file).
|
|
61
|
+
// Silent in off mode. Multi-line input gets one timestamp per line in file mode
|
|
62
|
+
// so each line stays greppable.
|
|
63
|
+
function log(line) {
|
|
64
|
+
if (mode === 'off') return;
|
|
65
|
+
const s = String(line);
|
|
66
|
+
if (mode === 'simple') {
|
|
67
|
+
// Lazy-require to avoid a require cycle: writer pulls in this module
|
|
68
|
+
// for its own drift diagnostic.
|
|
69
|
+
const writer = require('./ui/writer');
|
|
70
|
+
writer.scrollback(s);
|
|
71
|
+
} else {
|
|
72
|
+
for (const l of s.split('\n')) _writeFile(l);
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Extended-only debug — visible in file mode only. Used for high-volume
|
|
77
|
+
// per-chunk traces (raw SSE, request body dumps, accumulator state) that
|
|
78
|
+
// would shred the TUI if printed inline.
|
|
79
|
+
function logExtended(line) {
|
|
80
|
+
if (mode !== 'file') return;
|
|
81
|
+
const s = String(line);
|
|
82
|
+
for (const l of s.split('\n')) _writeFile(l);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function close() {
|
|
86
|
+
if (fileStream) {
|
|
87
|
+
try {
|
|
88
|
+
const ts = new Date().toISOString();
|
|
89
|
+
fileStream.write(`[${ts}] [session] end pid=${process.pid}\n`);
|
|
90
|
+
fileStream.end();
|
|
91
|
+
} catch {}
|
|
92
|
+
fileStream = null;
|
|
93
|
+
}
|
|
94
|
+
mode = 'off';
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
module.exports = {
|
|
98
|
+
init,
|
|
99
|
+
isActive,
|
|
100
|
+
isSimple,
|
|
101
|
+
isFile,
|
|
102
|
+
getMode,
|
|
103
|
+
log,
|
|
104
|
+
logExtended,
|
|
105
|
+
close,
|
|
106
|
+
};
|
package/lib/permissions.js
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
'use strict';
|
|
2
2
|
|
|
3
|
+
const writer = require('./ui/writer');
|
|
4
|
+
const messages = require('./ui/messages');
|
|
5
|
+
|
|
3
6
|
const TIER_FS = ['read_file', 'write_file', 'append_file', 'delete_file', 'list_dir', 'make_dir', 'move_file', 'copy_file', 'file_stat', 'search_files', 'store_memory', 'recall_memory'];
|
|
4
7
|
const TIER_EXEC = ['exec'];
|
|
5
8
|
const TIER_NET = ['http_get', 'download'];
|
|
@@ -117,7 +120,7 @@ function createPermissionManager(ui, { allowedTiers = [], readonly = false } = {
|
|
|
117
120
|
if (uiCallbacks) {
|
|
118
121
|
uiCallbacks.onAddMessage({ role: 'system', content: `✓ Auto-approved: ${description}` });
|
|
119
122
|
} else {
|
|
120
|
-
|
|
123
|
+
messages.sysSuccess(`Auto-approved: ${description}`);
|
|
121
124
|
}
|
|
122
125
|
}
|
|
123
126
|
|
|
@@ -133,7 +136,7 @@ function createPermissionManager(ui, { allowedTiers = [], readonly = false } = {
|
|
|
133
136
|
}
|
|
134
137
|
|
|
135
138
|
if (!process.stdout.isTTY || !process.stdin.isTTY) {
|
|
136
|
-
|
|
139
|
+
writer.scrollback(` [non-TTY] Auto-approving: ${description}`);
|
|
137
140
|
return true;
|
|
138
141
|
}
|
|
139
142
|
|
|
@@ -155,13 +158,11 @@ function createPermissionManager(ui, { allowedTiers = [], readonly = false } = {
|
|
|
155
158
|
return true;
|
|
156
159
|
}
|
|
157
160
|
|
|
158
|
-
// Fallback:
|
|
161
|
+
// Fallback: TTY interactive select (used outside of chat UI)
|
|
159
162
|
const alwaysLabel = tag ? `Yes, always for <${tag}>` : 'Yes, always';
|
|
160
163
|
const choices = ['Yes', alwaysLabel, 'No'];
|
|
161
164
|
|
|
162
|
-
|
|
163
|
-
console.log(` ${FG_YELLOW}${BOLD}⚠ Permission required${RST}`);
|
|
164
|
-
console.log(` ${FG_GRAY}${actionType}: ${description}${RST}`);
|
|
165
|
+
writer.scrollback(`\n ${FG_YELLOW}${BOLD}⚠ Permission required${RST}\n ${FG_GRAY}${actionType}: ${description}${RST}`);
|
|
165
166
|
|
|
166
167
|
const selectedIndex = await interactiveSelect(
|
|
167
168
|
choices,
|
|
@@ -174,13 +175,13 @@ function createPermissionManager(ui, { allowedTiers = [], readonly = false } = {
|
|
|
174
175
|
);
|
|
175
176
|
|
|
176
177
|
if (selectedIndex === null || selectedIndex === 2) {
|
|
177
|
-
|
|
178
|
+
writer.scrollback(` ${FG_RED}✗${RST} ${FG_DARK}Denied${RST}`);
|
|
178
179
|
return false;
|
|
179
180
|
}
|
|
180
181
|
|
|
181
182
|
if (selectedIndex === 1 && tag) {
|
|
182
183
|
state.sessionApprovedTags.add(tag);
|
|
183
|
-
|
|
184
|
+
writer.scrollback(` ${FG_GREEN}✓${RST} ${FG_DARK}Auto-approve enabled for <${tag}> this session${RST}`);
|
|
184
185
|
}
|
|
185
186
|
|
|
186
187
|
return true;
|
package/lib/proc.js
ADDED
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
const dbg = require('./debug');
|
|
4
|
+
|
|
5
|
+
// Platform-aware subprocess spawn + tree-kill helpers.
|
|
6
|
+
//
|
|
7
|
+
// Why this module exists: when a child is started with `shell: true`, the
|
|
8
|
+
// PID Node hands back is the shell wrapper (`sh -c "..."` on POSIX, `cmd.exe
|
|
9
|
+
// /c "..."` on Windows). Calling `child.kill()` kills the wrapper, but its
|
|
10
|
+
// descendants (the actual `find`, `grep`, `bash` pipeline) become orphans
|
|
11
|
+
// and keep running. To abort cleanly we have to kill the whole process tree.
|
|
12
|
+
//
|
|
13
|
+
// Constraint from the project: no other file imports `process.kill` or
|
|
14
|
+
// `child.kill` directly — those calls live here. `tools.js` (and any future
|
|
15
|
+
// caller) only knows about `spawnWithGroup` and `killTreeEscalating`.
|
|
16
|
+
|
|
17
|
+
const isWindows = process.platform === 'win32';
|
|
18
|
+
|
|
19
|
+
// Wrap `child_process.spawn` so the resulting child is addressable as a
|
|
20
|
+
// process group. POSIX: `detached: true` makes the child a process-group
|
|
21
|
+
// leader, so `process.kill(-pid, sig)` reaches all descendants. Windows:
|
|
22
|
+
// taskkill /T walks the PID hierarchy itself, so `detached` is unnecessary
|
|
23
|
+
// and actively harmful — it would spawn the child in a new console window.
|
|
24
|
+
function spawnWithGroup(spawn, command, args, opts = {}) {
|
|
25
|
+
const finalOpts = { ...opts };
|
|
26
|
+
if (!isWindows) finalOpts.detached = true;
|
|
27
|
+
return spawn(command, args, finalOpts);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function killTree(child, signal) {
|
|
31
|
+
if (!child || child.killed || child.exitCode !== null || child.pid == null) return;
|
|
32
|
+
if (isWindows) {
|
|
33
|
+
// taskkill /T = traverse children, /F = force. windowsHide prevents the
|
|
34
|
+
// brief CMD window flash. Fire and forget — taskkill exits on its own
|
|
35
|
+
// and we don't care about its result code (the child's `exit` event is
|
|
36
|
+
// the authoritative signal).
|
|
37
|
+
const { spawn } = require('child_process');
|
|
38
|
+
try {
|
|
39
|
+
const args = ['/PID', String(child.pid), '/T'];
|
|
40
|
+
if (signal === 'SIGKILL') args.push('/F');
|
|
41
|
+
const tk = spawn('taskkill', args, { windowsHide: true, stdio: 'ignore' });
|
|
42
|
+
tk.on('error', () => {});
|
|
43
|
+
tk.unref();
|
|
44
|
+
} catch {
|
|
45
|
+
// taskkill failed to launch (PID already gone, or taskkill missing on
|
|
46
|
+
// a stripped-down Windows image). The child's exit event will still
|
|
47
|
+
// fire if the process is gone; nothing else to do here.
|
|
48
|
+
}
|
|
49
|
+
} else {
|
|
50
|
+
try {
|
|
51
|
+
// Negative PID = whole process group. Requires detached:true at spawn.
|
|
52
|
+
process.kill(-child.pid, signal || 'SIGTERM');
|
|
53
|
+
} catch (err) {
|
|
54
|
+
// ESRCH = process group already gone. Anything else is unexpected but
|
|
55
|
+
// not fatal — surface only when debug is active for triage.
|
|
56
|
+
if (err.code !== 'ESRCH') {
|
|
57
|
+
dbg.log(`[killTree] kill failed: ${err.code} ${err.message}`);
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
// Send SIGTERM (or taskkill graceful), wait 2s, escalate to SIGKILL (or
|
|
64
|
+
// taskkill /F) if the tree didn't exit. Hard-coded 2s grace per the abort
|
|
65
|
+
// requirements — long enough for well-behaved children to clean up, short
|
|
66
|
+
// enough that a stuck `trap "" TERM` process doesn't tie up the agent.
|
|
67
|
+
function killTreeEscalating(child) {
|
|
68
|
+
killTree(child, 'SIGTERM');
|
|
69
|
+
const escalation = setTimeout(() => {
|
|
70
|
+
if (child.exitCode === null && !child.killed) killTree(child, 'SIGKILL');
|
|
71
|
+
}, 2000);
|
|
72
|
+
// Don't keep the event loop alive solely for the escalation timer; if the
|
|
73
|
+
// process exits naturally first, the `once('exit')` listener clears it.
|
|
74
|
+
escalation.unref();
|
|
75
|
+
child.once('exit', () => clearTimeout(escalation));
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Future Windows-enablement notes:
|
|
79
|
+
// - Job objects (CreateJobObject API via a native binding) give stronger
|
|
80
|
+
// tree-kill guarantees than taskkill, especially for grandchild
|
|
81
|
+
// processes that detach themselves. Consider migrating if taskkill
|
|
82
|
+
// proves unreliable for nested children.
|
|
83
|
+
// - Windows has no SIGTERM/SIGKILL distinction at the OS level for
|
|
84
|
+
// spawned processes. taskkill (without /F) attempts WM_CLOSE-style
|
|
85
|
+
// graceful close; /F is a hard terminate. The 2s escalation here maps
|
|
86
|
+
// to "graceful taskkill, then forceful taskkill" — same shape as POSIX.
|
|
87
|
+
// - shell: true on Windows uses cmd.exe by default. Cross-platform
|
|
88
|
+
// command translation (find, grep, etc.) is the tool layer's problem,
|
|
89
|
+
// not this module's.
|
|
90
|
+
|
|
91
|
+
module.exports = {
|
|
92
|
+
spawnWithGroup,
|
|
93
|
+
killTree,
|
|
94
|
+
killTreeEscalating,
|
|
95
|
+
isWindows,
|
|
96
|
+
};
|
package/lib/prompts.js
CHANGED
|
@@ -9,6 +9,7 @@ const WRAPPER_NAMES = new Set([
|
|
|
9
9
|
'parameter',
|
|
10
10
|
'tool_call',
|
|
11
11
|
'function_call',
|
|
12
|
+
'function',
|
|
12
13
|
]);
|
|
13
14
|
|
|
14
15
|
// For each tool tag: required attributes and a one-line purpose.
|
|
@@ -32,7 +33,7 @@ const TOOL_TAG_SPECS = {
|
|
|
32
33
|
edit_file: { attrs: ['path', 'line'], purpose: 'Replace a single line in a file (inline content = new line).' },
|
|
33
34
|
search_files: { attrs: ['pattern?', 'dir?'], purpose: 'Find files by glob pattern.' },
|
|
34
35
|
search_in_file: { attrs: ['path'], purpose: 'Regex search inside a file (inline content = pattern).' },
|
|
35
|
-
replace_in_file: { attrs: ['path', 'search', 'replace'], purpose: 'Regex replace inside a file.' },
|
|
36
|
+
replace_in_file: { attrs: ['path', 'search', 'replace'], purpose: 'Regex replace inside a file; inline content is interpreted as regex flags (e.g. g, i, gi).' },
|
|
36
37
|
get_env: { attrs: [], purpose: 'Read an env var (inline content = name).' },
|
|
37
38
|
set_env: { attrs: ['name', 'value'], purpose: 'Set an env var for this process.' },
|
|
38
39
|
download: { attrs: [], purpose: 'HTTP download to the CWD (inline content = URL).' },
|
|
@@ -80,8 +81,8 @@ ${TAG_INVENTORY}
|
|
|
80
81
|
## Reasoning vs planning — IMPORTANT:
|
|
81
82
|
|
|
82
83
|
- Your internal chain-of-thought reasoning uses your native \`<think>...</think>\` block. Use it normally for deliberation. Do NOT treat \`<think>\` as a user-facing tool and do NOT try to emit \`<think>\` as an action — it is reserved for your own reasoning and is handled by the runtime.
|
|
83
|
-
- When you need to explicitly record a short plan that the agent framework can see (for logging or hand-off between steps), use \`<plan>...</plan>\` instead. \`<plan>\`
|
|
84
|
-
-
|
|
84
|
+
- When you need to explicitly record a short plan that the agent framework can see (for logging or hand-off between steps), use \`<plan>...</plan>\` instead. Both \`<think>\` and \`<plan>\` are display-only tags handled by the runtime — never emit either as an action.
|
|
85
|
+
- The valid action tags are the ones listed above.
|
|
85
86
|
|
|
86
87
|
## STRICT RULES — follow exactly:
|
|
87
88
|
|
|
@@ -94,12 +95,9 @@ ${TAG_INVENTORY}
|
|
|
94
95
|
7. Be concise. Provide working solutions. Use markdown for code blocks in explanations.
|
|
95
96
|
8. Current working directory: __CWD__
|
|
96
97
|
|
|
97
|
-
Response contract
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
(b) <final_answer>...</final_answer> containing your answer to the user.
|
|
101
|
-
A response containing neither is invalid and will be rejected.
|
|
102
|
-
Do not describe actions in prose outside these two forms. Either call the tool, or wrap your final reply in <final_answer>.`;
|
|
98
|
+
Response contract:
|
|
99
|
+
- If the task requires an action, emit the appropriate tool tag(s) — do not narrate intended actions in prose without the tag.
|
|
100
|
+
- If the task is complete or the user's question can be answered directly without running a tool, reply in plain prose. No special wrapper is needed.`;
|
|
103
101
|
|
|
104
102
|
const NATIVE_SYSTEM_PROMPT_TEMPLATE = `You are Semalt.AI, an expert AI coding assistant running in the user's terminal. Use the provided tools to execute shell commands and file operations; do not just print instructions. Each call is approved by the user before execution, and the result is returned to you for the next step.
|
|
105
103
|
|
|
@@ -107,7 +105,7 @@ Use \`<think>...</think>\` for internal reasoning (runtime-handled; never emit a
|
|
|
107
105
|
|
|
108
106
|
Be concise. Use markdown for code blocks in explanations. Current working directory: __CWD__
|
|
109
107
|
|
|
110
|
-
Response contract:
|
|
108
|
+
Response contract: if the task requires an action, emit one or more tool calls — do not narrate intended actions in prose without the tool call. Otherwise, answer in plain prose; no special wrapper is needed.`;
|
|
111
109
|
|
|
112
110
|
function getSystemPrompt(nativeTools = false) {
|
|
113
111
|
const template = nativeTools ? NATIVE_SYSTEM_PROMPT_TEMPLATE : SYSTEM_PROMPT_TEMPLATE;
|
package/lib/tool_specs.js
CHANGED
|
@@ -75,9 +75,10 @@ const TOOL_SPECS = {
|
|
|
75
75
|
content: {
|
|
76
76
|
type: 'string',
|
|
77
77
|
description: 'Full UTF-8 text to write as the new file contents',
|
|
78
|
+
default: '',
|
|
78
79
|
},
|
|
79
80
|
},
|
|
80
|
-
required: ['path'
|
|
81
|
+
required: ['path'],
|
|
81
82
|
},
|
|
82
83
|
},
|
|
83
84
|
|
|
@@ -93,9 +94,10 @@ const TOOL_SPECS = {
|
|
|
93
94
|
content: {
|
|
94
95
|
type: 'string',
|
|
95
96
|
description: 'Full UTF-8 text to write as the initial file contents',
|
|
97
|
+
default: '',
|
|
96
98
|
},
|
|
97
99
|
},
|
|
98
|
-
required: ['path'
|
|
100
|
+
required: ['path'],
|
|
99
101
|
},
|
|
100
102
|
},
|
|
101
103
|
|
|
@@ -111,9 +113,10 @@ const TOOL_SPECS = {
|
|
|
111
113
|
content: {
|
|
112
114
|
type: 'string',
|
|
113
115
|
description: 'UTF-8 text to append to the end of the file',
|
|
116
|
+
default: '',
|
|
114
117
|
},
|
|
115
118
|
},
|
|
116
|
-
required: ['path'
|
|
119
|
+
required: ['path'],
|
|
117
120
|
},
|
|
118
121
|
},
|
|
119
122
|
|
|
@@ -241,9 +244,10 @@ const TOOL_SPECS = {
|
|
|
241
244
|
content: {
|
|
242
245
|
type: 'string',
|
|
243
246
|
description: 'New text for the target line; trailing newline is added automatically when the file is rejoined',
|
|
247
|
+
default: '',
|
|
244
248
|
},
|
|
245
249
|
},
|
|
246
|
-
required: ['path', 'line'
|
|
250
|
+
required: ['path', 'line'],
|
|
247
251
|
},
|
|
248
252
|
},
|
|
249
253
|
|
|
@@ -255,6 +259,7 @@ const TOOL_SPECS = {
|
|
|
255
259
|
pattern: {
|
|
256
260
|
type: 'string',
|
|
257
261
|
description: 'Glob pattern such as "*.ts" or "src/**/*.js"; matches against the basename when the pattern contains no slash, otherwise against the relative path',
|
|
262
|
+
default: '*',
|
|
258
263
|
},
|
|
259
264
|
dir: {
|
|
260
265
|
type: 'string',
|
|
@@ -262,7 +267,7 @@ const TOOL_SPECS = {
|
|
|
262
267
|
default: '.',
|
|
263
268
|
},
|
|
264
269
|
},
|
|
265
|
-
required: [
|
|
270
|
+
required: [],
|
|
266
271
|
},
|
|
267
272
|
},
|
|
268
273
|
|
|
@@ -300,6 +305,7 @@ const TOOL_SPECS = {
|
|
|
300
305
|
replace: {
|
|
301
306
|
type: 'string',
|
|
302
307
|
description: 'Replacement string; supports the standard $1, $2, $& back-references',
|
|
308
|
+
default: '',
|
|
303
309
|
},
|
|
304
310
|
flags: {
|
|
305
311
|
type: 'string',
|
|
@@ -307,7 +313,7 @@ const TOOL_SPECS = {
|
|
|
307
313
|
default: '',
|
|
308
314
|
},
|
|
309
315
|
},
|
|
310
|
-
required: ['path', 'search'
|
|
316
|
+
required: ['path', 'search'],
|
|
311
317
|
},
|
|
312
318
|
},
|
|
313
319
|
|
|
@@ -370,9 +376,10 @@ const TOOL_SPECS = {
|
|
|
370
376
|
content: {
|
|
371
377
|
type: 'string',
|
|
372
378
|
description: 'Base64-encoded payload to decode and write as the file contents',
|
|
379
|
+
default: '',
|
|
373
380
|
},
|
|
374
381
|
},
|
|
375
|
-
required: ['path'
|
|
382
|
+
required: ['path'],
|
|
376
383
|
},
|
|
377
384
|
},
|
|
378
385
|
|