lonny-agent 0.2.4 → 0.2.6
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.md +326 -326
- package/dist/agent/llm.d.ts +4 -0
- package/dist/agent/llm.d.ts.map +1 -1
- package/dist/agent/prompt-builder.d.ts.map +1 -1
- package/dist/agent/prompt-builder.js +13 -9
- package/dist/agent/prompt-builder.js.map +1 -1
- package/dist/agent/providers/google.d.ts.map +1 -1
- package/dist/agent/providers/google.js.map +1 -1
- package/dist/agent/providers/openai.d.ts.map +1 -1
- package/dist/agent/providers/openai.js +43 -3
- package/dist/agent/providers/openai.js.map +1 -1
- package/dist/agent/session.d.ts +4 -0
- package/dist/agent/session.d.ts.map +1 -1
- package/dist/agent/session.js +45 -7
- package/dist/agent/session.js.map +1 -1
- package/dist/config/index.d.ts.map +1 -1
- package/dist/config/index.js +7 -1
- package/dist/config/index.js.map +1 -1
- package/dist/tools/__tests__/bash.test.js +1 -1
- package/dist/tools/__tests__/bash.test.js.map +1 -1
- package/dist/tools/__tests__/edit.test.js +875 -371
- package/dist/tools/__tests__/edit.test.js.map +1 -1
- package/dist/tools/__tests__/grep.test.js +32 -0
- package/dist/tools/__tests__/grep.test.js.map +1 -1
- package/dist/tools/__tests__/sed.test.d.ts +2 -0
- package/dist/tools/__tests__/sed.test.d.ts.map +1 -0
- package/dist/tools/__tests__/sed.test.js +228 -0
- package/dist/tools/__tests__/sed.test.js.map +1 -0
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash.js +83 -10
- package/dist/tools/bash.js.map +1 -1
- package/dist/tools/edit.d.ts +50 -9
- package/dist/tools/edit.d.ts.map +1 -1
- package/dist/tools/edit.js +328 -158
- package/dist/tools/edit.js.map +1 -1
- package/dist/tools/grep.d.ts.map +1 -1
- package/dist/tools/grep.js +54 -30
- package/dist/tools/grep.js.map +1 -1
- package/dist/tools/registry.d.ts +0 -6
- package/dist/tools/registry.d.ts.map +1 -1
- package/dist/tools/registry.js +2 -29
- package/dist/tools/registry.js.map +1 -1
- package/dist/tools/sed.d.ts +4 -0
- package/dist/tools/sed.d.ts.map +1 -0
- package/dist/tools/sed.js +121 -0
- package/dist/tools/sed.js.map +1 -0
- package/dist/web/index.d.ts.map +1 -1
- package/dist/web/index.js +26 -3
- package/dist/web/index.js.map +1 -1
- package/dist/web/public/app.js +5 -855
- package/dist/web/public/confirm.js +70 -0
- package/dist/web/public/index.html +121 -120
- package/dist/web/public/input.js +107 -0
- package/dist/web/public/messages.js +395 -0
- package/dist/web/public/sidebar.js +82 -0
- package/dist/web/public/state.js +72 -0
- package/dist/web/public/style.css +996 -949
- package/dist/web/public/utils.js +37 -0
- package/dist/web/public/websocket.js +267 -0
- package/dist/web/public/ws.js +19 -0
- package/dist/web/session-bridge.d.ts.map +1 -1
- package/dist/web/session-bridge.js +3 -7
- package/dist/web/session-bridge.js.map +1 -1
- package/package.json +55 -54
- package/scripts/copy-native.mjs +24 -0
- package/scripts/copy-web.mjs +15 -0
- package/src/agent/index.ts +25 -25
- package/src/agent/llm.ts +4 -0
- package/src/agent/prompt-builder.ts +13 -9
- package/src/agent/providers/anthropic.ts +179 -179
- package/src/agent/providers/google.ts +210 -211
- package/src/agent/providers/ollama.ts +186 -186
- package/src/agent/providers/openai.ts +61 -5
- package/src/agent/session.ts +59 -3
- package/src/config/index.ts +342 -335
- package/src/tools/__tests__/bash.test.ts +1 -1
- package/src/tools/__tests__/edit.test.ts +981 -385
- package/src/tools/__tests__/grep.test.ts +35 -0
- package/src/tools/bash.ts +97 -11
- package/src/tools/edit.ts +362 -169
- package/src/tools/git.ts +76 -76
- package/src/tools/grep.ts +57 -30
- package/src/tools/registry.ts +2 -31
- package/src/tui/index.ts +786 -786
- package/src/web/index.ts +25 -3
- package/src/web/public/app.js +5 -855
- package/src/web/public/confirm.js +70 -0
- package/src/web/public/index.html +121 -120
- package/src/web/public/input.js +107 -0
- package/src/web/public/messages.js +395 -0
- package/src/web/public/sidebar.js +82 -0
- package/src/web/public/state.js +72 -0
- package/src/web/public/style.css +996 -949
- package/src/web/public/utils.js +37 -0
- package/src/web/public/websocket.js +267 -0
- package/src/web/public/ws.js +19 -0
- package/src/web/session-bridge.ts +193 -194
- package/.claude/settings.local.json +0 -14
- package/.kilo/plans/1780064105789-mighty-pixel.md +0 -171
- package/.kilo/plans/1780293725888-quick-wizard.md +0 -62
- package/.lonny/plan-web-cwd-status.md +0 -38
- package/src/tools/exec.ts +0 -348
package/src/tools/git.ts
CHANGED
|
@@ -1,76 +1,76 @@
|
|
|
1
|
-
import { execSync } from 'node:child_process'
|
|
2
|
-
import type { Tool, ToolDefinition } from './types.js'
|
|
3
|
-
|
|
4
|
-
export const createGitTool = (cwd: string): Tool => {
|
|
5
|
-
const definition: ToolDefinition = {
|
|
6
|
-
name: 'git',
|
|
7
|
-
description:
|
|
8
|
-
'Run git commands for repository operations. Supports status, diff, log, show, branch, and other read-only git operations.',
|
|
9
|
-
parameters: {
|
|
10
|
-
command: {
|
|
11
|
-
type: 'string',
|
|
12
|
-
description: 'Git subcommand and arguments (e.g. "status", "diff", "log --oneline -5")',
|
|
13
|
-
required: true,
|
|
14
|
-
},
|
|
15
|
-
path: { type: 'string', description: 'Repository path (default: cwd)', required: false },
|
|
16
|
-
},
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const execute = async (
|
|
20
|
-
input: Record<string, unknown>,
|
|
21
|
-
): Promise<{ success: boolean; output: string; error?: string }> => {
|
|
22
|
-
const gitCmd = String(input.command || '').trim()
|
|
23
|
-
const repoPath = String(input.path || cwd)
|
|
24
|
-
|
|
25
|
-
if (!gitCmd) {
|
|
26
|
-
return { success: false, output: '', error: 'command is required' }
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
// Safety: block destructive commands
|
|
30
|
-
const destructivePatterns = [
|
|
31
|
-
/^reset\s+(--hard|--soft|--mixed|--merge|--keep)/i,
|
|
32
|
-
/^push\s+.*(--force|-f)/i,
|
|
33
|
-
/^(rebase|merge|cherry-pick|revert)\b/i,
|
|
34
|
-
/^branch\s+-[dD]/i,
|
|
35
|
-
/^tag\s+-[dD]/i,
|
|
36
|
-
/^(clean|gc|prune)\b/i,
|
|
37
|
-
/^update-ref\b/i,
|
|
38
|
-
/^rm\b/i,
|
|
39
|
-
/^commit\s+--amend/i,
|
|
40
|
-
/^checkout\s+.*-B/i,
|
|
41
|
-
]
|
|
42
|
-
|
|
43
|
-
for (const pattern of destructivePatterns) {
|
|
44
|
-
if (pattern.test(gitCmd)) {
|
|
45
|
-
return {
|
|
46
|
-
success: false,
|
|
47
|
-
output: '',
|
|
48
|
-
error: `Destructive git command not allowed: "${gitCmd}". Only read-only operations are permitted.`,
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
try {
|
|
54
|
-
const result = execSync(`git ${gitCmd}`, {
|
|
55
|
-
cwd: repoPath,
|
|
56
|
-
encoding: 'utf-8',
|
|
57
|
-
timeout: 30000,
|
|
58
|
-
maxBuffer: 10 * 1024 * 1024, // 10MB
|
|
59
|
-
})
|
|
60
|
-
|
|
61
|
-
return { success: true, output: result.trim() || '(empty output)' }
|
|
62
|
-
} catch (err) {
|
|
63
|
-
const error = err as { stderr?: string; stdout?: string; message?: string }
|
|
64
|
-
const stderr = error.stderr || ''
|
|
65
|
-
const stdout = error.stdout || ''
|
|
66
|
-
const msg = error.message || String(err)
|
|
67
|
-
return {
|
|
68
|
-
success: false,
|
|
69
|
-
output: stdout.trim(),
|
|
70
|
-
error: stderr.trim() || msg,
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
return { definition, execute }
|
|
76
|
-
}
|
|
1
|
+
import { execSync } from 'node:child_process'
|
|
2
|
+
import type { Tool, ToolDefinition } from './types.js'
|
|
3
|
+
|
|
4
|
+
export const createGitTool = (cwd: string): Tool => {
|
|
5
|
+
const definition: ToolDefinition = {
|
|
6
|
+
name: 'git',
|
|
7
|
+
description:
|
|
8
|
+
'Run git commands for repository operations. Supports status, diff, log, show, branch, and other read-only git operations.',
|
|
9
|
+
parameters: {
|
|
10
|
+
command: {
|
|
11
|
+
type: 'string',
|
|
12
|
+
description: 'Git subcommand and arguments (e.g. "status", "diff", "log --oneline -5")',
|
|
13
|
+
required: true,
|
|
14
|
+
},
|
|
15
|
+
path: { type: 'string', description: 'Repository path (default: cwd)', required: false },
|
|
16
|
+
},
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const execute = async (
|
|
20
|
+
input: Record<string, unknown>,
|
|
21
|
+
): Promise<{ success: boolean; output: string; error?: string }> => {
|
|
22
|
+
const gitCmd = String(input.command || '').trim()
|
|
23
|
+
const repoPath = String(input.path || cwd)
|
|
24
|
+
|
|
25
|
+
if (!gitCmd) {
|
|
26
|
+
return { success: false, output: '', error: 'command is required' }
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Safety: block destructive commands
|
|
30
|
+
const destructivePatterns = [
|
|
31
|
+
/^reset\s+(--hard|--soft|--mixed|--merge|--keep)/i,
|
|
32
|
+
/^push\s+.*(--force|-f)/i,
|
|
33
|
+
/^(rebase|merge|cherry-pick|revert)\b/i,
|
|
34
|
+
/^branch\s+-[dD]/i,
|
|
35
|
+
/^tag\s+-[dD]/i,
|
|
36
|
+
/^(clean|gc|prune)\b/i,
|
|
37
|
+
/^update-ref\b/i,
|
|
38
|
+
/^rm\b/i,
|
|
39
|
+
/^commit\s+--amend/i,
|
|
40
|
+
/^checkout\s+.*-B/i,
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
for (const pattern of destructivePatterns) {
|
|
44
|
+
if (pattern.test(gitCmd)) {
|
|
45
|
+
return {
|
|
46
|
+
success: false,
|
|
47
|
+
output: '',
|
|
48
|
+
error: `Destructive git command not allowed: "${gitCmd}". Only read-only operations are permitted.`,
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
try {
|
|
54
|
+
const result = execSync(`git ${gitCmd}`, {
|
|
55
|
+
cwd: repoPath,
|
|
56
|
+
encoding: 'utf-8',
|
|
57
|
+
timeout: 30000,
|
|
58
|
+
maxBuffer: 10 * 1024 * 1024, // 10MB
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
return { success: true, output: result.trim() || '(empty output)' }
|
|
62
|
+
} catch (err) {
|
|
63
|
+
const error = err as { stderr?: string; stdout?: string; message?: string }
|
|
64
|
+
const stderr = error.stderr || ''
|
|
65
|
+
const stdout = error.stdout || ''
|
|
66
|
+
const msg = error.message || String(err)
|
|
67
|
+
return {
|
|
68
|
+
success: false,
|
|
69
|
+
output: stdout.trim(),
|
|
70
|
+
error: stderr.trim() || msg,
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
return { definition, execute }
|
|
76
|
+
}
|
package/src/tools/grep.ts
CHANGED
|
@@ -1,16 +1,20 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFileSync } from 'node:child_process'
|
|
2
2
|
import * as fs from 'node:fs/promises'
|
|
3
3
|
import * as path from 'node:path'
|
|
4
4
|
import { fmtErr } from './errors.js'
|
|
5
5
|
import type { Tool, ToolResult } from './types.js'
|
|
6
6
|
|
|
7
|
+
let _hasRg: boolean | undefined
|
|
8
|
+
|
|
7
9
|
function hasRg(): boolean {
|
|
10
|
+
if (_hasRg !== undefined) return _hasRg
|
|
8
11
|
try {
|
|
9
|
-
|
|
10
|
-
|
|
12
|
+
execFileSync('rg', ['--version'], { stdio: 'pipe' })
|
|
13
|
+
_hasRg = true
|
|
11
14
|
} catch {
|
|
12
|
-
|
|
15
|
+
_hasRg = false
|
|
13
16
|
}
|
|
17
|
+
return _hasRg
|
|
14
18
|
}
|
|
15
19
|
|
|
16
20
|
/** Simple glob-to-regex for the `include` parameter (e.g. "*.ts", "*.{ts,tsx}"). */
|
|
@@ -25,9 +29,11 @@ function includeRe(include: string): RegExp {
|
|
|
25
29
|
continue
|
|
26
30
|
}
|
|
27
31
|
if (ch === '*') {
|
|
32
|
+
// Handle ** (match across dirs) and * (match within a single dir segment)
|
|
28
33
|
if (include[i + 1] === '*') {
|
|
29
34
|
re += '.*'
|
|
30
|
-
|
|
35
|
+
// Skip all consecutive asterisks so `***` doesn't leave a trailing literal `*`
|
|
36
|
+
while (i < include.length && include[i] === '*') i++
|
|
31
37
|
continue
|
|
32
38
|
}
|
|
33
39
|
re += '[^/]*'
|
|
@@ -48,6 +54,12 @@ function includeRe(include: string): RegExp {
|
|
|
48
54
|
i++
|
|
49
55
|
continue
|
|
50
56
|
}
|
|
57
|
+
// Escape special regex characters (except those we handle above)
|
|
58
|
+
if (/[.+^${}()|[\]\\]/.test(ch)) {
|
|
59
|
+
re += '\\' + ch
|
|
60
|
+
i++
|
|
61
|
+
continue
|
|
62
|
+
}
|
|
51
63
|
re += ch
|
|
52
64
|
i++
|
|
53
65
|
}
|
|
@@ -64,32 +76,38 @@ async function nodeGrep(dir: string, re: RegExp, incRe: RegExp | null): Promise<
|
|
|
64
76
|
const results: NodeGrepMatch[] = []
|
|
65
77
|
try {
|
|
66
78
|
const entries = await fs.readdir(dir, { withFileTypes: true })
|
|
67
|
-
const
|
|
79
|
+
const promises: Promise<void>[] = []
|
|
80
|
+
|
|
68
81
|
for (const e of entries) {
|
|
69
82
|
const full = path.join(dir, e.name)
|
|
70
83
|
if (e.isDirectory()) {
|
|
71
84
|
if (e.name === '.git' || e.name === 'node_modules') continue
|
|
72
|
-
|
|
85
|
+
promises.push(
|
|
86
|
+
nodeGrep(full, re, incRe).then(sub => {
|
|
87
|
+
for (const m of sub) results.push(m)
|
|
88
|
+
}),
|
|
89
|
+
)
|
|
73
90
|
} else if (e.isFile()) {
|
|
74
91
|
if (incRe && !incRe.test(e.name)) continue
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
92
|
+
promises.push(
|
|
93
|
+
(async () => {
|
|
94
|
+
try {
|
|
95
|
+
const content = await fs.readFile(full, 'utf-8')
|
|
96
|
+
const lines = content.split('\n')
|
|
97
|
+
for (let i = 0; i < lines.length; i++) {
|
|
98
|
+
if (re.test(lines[i])) {
|
|
99
|
+
results.push({ file: full, line: i + 1, text: lines[i] })
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
} catch {
|
|
103
|
+
/* permission denied etc */
|
|
81
104
|
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
/* permission denied etc */
|
|
85
|
-
}
|
|
105
|
+
})(),
|
|
106
|
+
)
|
|
86
107
|
}
|
|
87
108
|
}
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
for (const sm of subMatches) {
|
|
91
|
-
results.push(...sm)
|
|
92
|
-
}
|
|
109
|
+
|
|
110
|
+
await Promise.all(promises)
|
|
93
111
|
} catch {
|
|
94
112
|
/* permission denied etc */
|
|
95
113
|
}
|
|
@@ -120,11 +138,23 @@ export function createGrepTool(cwd: string): Tool {
|
|
|
120
138
|
|
|
121
139
|
try {
|
|
122
140
|
if (useRg) {
|
|
123
|
-
|
|
124
|
-
if (include)
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
141
|
+
const args = ['-n', '--no-heading', pattern]
|
|
142
|
+
if (include) args.push('-g', include)
|
|
143
|
+
args.push(searchPath)
|
|
144
|
+
try {
|
|
145
|
+
const output = execFileSync('rg', args, {
|
|
146
|
+
encoding: 'utf-8',
|
|
147
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
148
|
+
})
|
|
149
|
+
return { success: true, output: output || 'No matches found.' }
|
|
150
|
+
} catch (rgErr: unknown) {
|
|
151
|
+
const rgMsg = fmtErr(rgErr)
|
|
152
|
+
// ripgrep exit code 1 = no matches, exit code 2 = error
|
|
153
|
+
if (rgMsg.includes('exit code 1')) {
|
|
154
|
+
return { success: true, output: 'No matches found.' }
|
|
155
|
+
}
|
|
156
|
+
throw rgErr
|
|
157
|
+
}
|
|
128
158
|
}
|
|
129
159
|
|
|
130
160
|
const re = new RegExp(pattern)
|
|
@@ -141,9 +171,6 @@ export function createGrepTool(cwd: string): Tool {
|
|
|
141
171
|
return { success: true, output }
|
|
142
172
|
} catch (err) {
|
|
143
173
|
const msg = fmtErr(err)
|
|
144
|
-
if (useRg && msg.includes('exit code 1')) {
|
|
145
|
-
return { success: true, output: 'No matches found.' }
|
|
146
|
-
}
|
|
147
174
|
return { success: false, output: '', error: `Grep failed: ${msg}` }
|
|
148
175
|
}
|
|
149
176
|
},
|
package/src/tools/registry.ts
CHANGED
|
@@ -2,7 +2,6 @@ import type { FileReadTracker } from '../diff/apply.js'
|
|
|
2
2
|
import { bashTool } from './bash.js'
|
|
3
3
|
import { createEditTool } from './edit.js'
|
|
4
4
|
import { fmtErr } from './errors.js'
|
|
5
|
-
import { createExecTool, updateExecToolDefinition } from './exec.js'
|
|
6
5
|
import { fetchTool } from './fetch.js'
|
|
7
6
|
import { createFindTool } from './find.js'
|
|
8
7
|
import { createGitTool } from './git.js'
|
|
@@ -50,9 +49,6 @@ function normalizeToolInput(
|
|
|
50
49
|
if (toolName === 'write_plan') {
|
|
51
50
|
return { filename: input, content: input }
|
|
52
51
|
}
|
|
53
|
-
if (toolName === 'exec') {
|
|
54
|
-
return { code: input }
|
|
55
|
-
}
|
|
56
52
|
// For tools that take a single string param, guess from the definition
|
|
57
53
|
const params = Object.keys(_definition.parameters)
|
|
58
54
|
if (params.length === 1) {
|
|
@@ -97,8 +93,6 @@ export class ToolRegistry {
|
|
|
97
93
|
private tools: Map<string, Tool> = new Map()
|
|
98
94
|
private context: ToolContext
|
|
99
95
|
private plugins: Map<string, ToolPlugin> = new Map()
|
|
100
|
-
/** Reference to the exec tool for dynamic description updates */
|
|
101
|
-
private execTool: Tool | null = null
|
|
102
96
|
|
|
103
97
|
constructor(context: ToolContext) {
|
|
104
98
|
this.context = context
|
|
@@ -107,7 +101,7 @@ export class ToolRegistry {
|
|
|
107
101
|
|
|
108
102
|
/** Register all built-in tools */
|
|
109
103
|
private registerBuiltins(): void {
|
|
110
|
-
// ask mode: only fetch and search
|
|
104
|
+
// ask mode: only fetch and search
|
|
111
105
|
if (this.context.mode === 'ask') {
|
|
112
106
|
this.register(fetchTool)
|
|
113
107
|
this.register(searchTool)
|
|
@@ -128,27 +122,12 @@ export class ToolRegistry {
|
|
|
128
122
|
this.register(createGitTool(this.context.cwd))
|
|
129
123
|
this.register(createInstallSkillTool(this.context.cwd))
|
|
130
124
|
this.register(createEditTool(this.context.applier, this.context.cwd))
|
|
131
|
-
this.registerExecTool()
|
|
132
125
|
} else {
|
|
133
126
|
// Plan mode: read-only investigation + write_plan
|
|
134
127
|
this.register(createWritePlanTool(this.context.cwd, this.context.onPlanWritten))
|
|
135
128
|
}
|
|
136
129
|
}
|
|
137
130
|
|
|
138
|
-
/** Register the exec tool with dynamic type declarations for all registered tools */
|
|
139
|
-
private registerExecTool(): void {
|
|
140
|
-
const execTool = createExecTool(() => Array.from(this.tools.values()))
|
|
141
|
-
this.execTool = execTool
|
|
142
|
-
this.register(execTool)
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
/** Refresh the exec tool's description to include current tool type declarations */
|
|
146
|
-
private refreshExecDescription(): void {
|
|
147
|
-
if (this.execTool) {
|
|
148
|
-
updateExecToolDefinition(this.execTool, this.getDefinitions())
|
|
149
|
-
}
|
|
150
|
-
}
|
|
151
|
-
|
|
152
131
|
setMode(mode: 'code' | 'plan' | 'ask'): void {
|
|
153
132
|
// Update context.mode FIRST so registerBuiltins() and mode-based logic
|
|
154
133
|
// use the NEW mode, not the stale one (fixes bug when switching from
|
|
@@ -158,20 +137,16 @@ export class ToolRegistry {
|
|
|
158
137
|
if (mode === 'code') {
|
|
159
138
|
// Re-register all built-in tools for code mode
|
|
160
139
|
this.tools.clear()
|
|
161
|
-
this.execTool = null
|
|
162
140
|
this.registerBuiltins()
|
|
163
141
|
} else if (mode === 'plan') {
|
|
164
|
-
// Keep existing tools but swap edit/write_plan
|
|
142
|
+
// Keep existing tools but swap edit/write_plan
|
|
165
143
|
if (!this.tools.has('write_plan')) {
|
|
166
144
|
this.register(createWritePlanTool(this.context.cwd, this.context.onPlanWritten))
|
|
167
145
|
}
|
|
168
146
|
this.tools.delete('edit')
|
|
169
|
-
this.tools.delete('exec')
|
|
170
|
-
this.execTool = null
|
|
171
147
|
} else if (mode === 'ask') {
|
|
172
148
|
// Ask mode: only fetch and search
|
|
173
149
|
this.tools.clear()
|
|
174
|
-
this.execTool = null
|
|
175
150
|
this.register(fetchTool)
|
|
176
151
|
this.register(searchTool)
|
|
177
152
|
}
|
|
@@ -180,10 +155,6 @@ export class ToolRegistry {
|
|
|
180
155
|
/** Register a single tool */
|
|
181
156
|
register(tool: Tool): void {
|
|
182
157
|
this.tools.set(tool.definition.name, tool)
|
|
183
|
-
// Refresh exec tool description to include the new tool's type declarations
|
|
184
|
-
if (this.execTool && tool.definition.name !== 'exec') {
|
|
185
|
-
this.refreshExecDescription()
|
|
186
|
-
}
|
|
187
158
|
}
|
|
188
159
|
|
|
189
160
|
/** Register a tool plugin (lazy creation pattern) */
|