@ottocode/sdk 0.1.301 → 0.1.303
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/package.json +35 -28
- package/src/core/src/search/fff.ts +215 -0
- package/src/core/src/tools/builtin/fs/read.txt +1 -1
- package/src/core/src/tools/builtin/glob.txt +1 -1
- package/src/core/src/tools/builtin/search.ts +157 -0
- package/src/core/src/tools/builtin/search.txt +14 -0
- package/src/core/src/tools/builtin/shell.ts +23 -0
- package/src/core/src/tools/builtin/shell.txt +3 -1
- package/src/core/src/tools/loader.ts +3 -3
- package/src/prompts/src/agents/build.txt +2 -1
- package/src/prompts/src/agents/plan.txt +2 -1
- package/src/prompts/src/agents/research.txt +1 -1
- package/src/prompts/src/providers/anthropic.txt +2 -2
- package/src/prompts/src/providers/default.txt +2 -2
- package/src/prompts/src/providers/glm.txt +2 -2
- package/src/prompts/src/providers/google.txt +1 -1
- package/src/prompts/src/providers/moonshot.txt +2 -2
- package/src/prompts/src/providers/openai.txt +2 -2
- package/src/providers/src/catalog.ts +200 -206
- package/src/providers/src/oauth-models.ts +1 -0
- package/src/core/src/tools/builtin/ripgrep.ts +0 -188
- package/src/core/src/tools/builtin/ripgrep.txt +0 -14
|
@@ -1,188 +0,0 @@
|
|
|
1
|
-
import { tool, type Tool } from 'ai';
|
|
2
|
-
import { z } from 'zod/v3';
|
|
3
|
-
import { spawn } from 'node:child_process';
|
|
4
|
-
import { join } from 'node:path';
|
|
5
|
-
import DESCRIPTION from './ripgrep.txt' with { type: 'text' };
|
|
6
|
-
import { createToolError, type ToolResponse } from '../error.ts';
|
|
7
|
-
import { resolveBinary } from '../bin-manager.ts';
|
|
8
|
-
|
|
9
|
-
export function buildRipgrepTool(projectRoot: string): {
|
|
10
|
-
name: string;
|
|
11
|
-
tool: Tool;
|
|
12
|
-
} {
|
|
13
|
-
const rg = tool({
|
|
14
|
-
description: DESCRIPTION,
|
|
15
|
-
inputSchema: z.object({
|
|
16
|
-
query: z.string().min(1).describe('Search pattern (regex by default)'),
|
|
17
|
-
path: z
|
|
18
|
-
.string()
|
|
19
|
-
.optional()
|
|
20
|
-
.default('.')
|
|
21
|
-
.describe('Relative path to search in'),
|
|
22
|
-
ignoreCase: z.boolean().optional().default(false),
|
|
23
|
-
glob: z
|
|
24
|
-
.array(z.string())
|
|
25
|
-
.optional()
|
|
26
|
-
.describe('One or more glob patterns to include'),
|
|
27
|
-
maxResults: z.number().int().min(1).max(5000).optional().default(100),
|
|
28
|
-
}),
|
|
29
|
-
async execute({
|
|
30
|
-
query,
|
|
31
|
-
path = '.',
|
|
32
|
-
ignoreCase,
|
|
33
|
-
glob,
|
|
34
|
-
maxResults = 100,
|
|
35
|
-
}: {
|
|
36
|
-
query: string;
|
|
37
|
-
path?: string;
|
|
38
|
-
ignoreCase?: boolean;
|
|
39
|
-
glob?: string[];
|
|
40
|
-
maxResults?: number;
|
|
41
|
-
}): Promise<
|
|
42
|
-
ToolResponse<{
|
|
43
|
-
count: number;
|
|
44
|
-
matches: Array<{ file: string; line: number; text: string }>;
|
|
45
|
-
truncated?: boolean;
|
|
46
|
-
shownMatches?: number;
|
|
47
|
-
files?: Array<{ file: string; matches: number }>;
|
|
48
|
-
}>
|
|
49
|
-
> {
|
|
50
|
-
function expandTilde(p: string) {
|
|
51
|
-
const home = process.env.HOME || process.env.USERPROFILE || '';
|
|
52
|
-
if (!home) return p;
|
|
53
|
-
if (p === '~') return home;
|
|
54
|
-
if (p.startsWith('~/')) return `${home}/${p.slice(2)}`;
|
|
55
|
-
return p;
|
|
56
|
-
}
|
|
57
|
-
const p = expandTilde(String(path ?? '.')).trim();
|
|
58
|
-
const isAbs = p.startsWith('/') || /^[A-Za-z]:[\\/]/.test(p);
|
|
59
|
-
const target = p ? (isAbs ? p : join(projectRoot, p)) : projectRoot;
|
|
60
|
-
const args = [
|
|
61
|
-
'--no-heading',
|
|
62
|
-
'--line-number',
|
|
63
|
-
'--color=never',
|
|
64
|
-
'--max-columns',
|
|
65
|
-
'240',
|
|
66
|
-
'--max-columns-preview',
|
|
67
|
-
];
|
|
68
|
-
if (ignoreCase) args.push('-i');
|
|
69
|
-
if (Array.isArray(glob)) for (const g of glob) args.push('-g', g);
|
|
70
|
-
args.push('--max-count', String(maxResults));
|
|
71
|
-
args.push(query);
|
|
72
|
-
args.push(target);
|
|
73
|
-
|
|
74
|
-
try {
|
|
75
|
-
const rgBin = await resolveBinary('rg');
|
|
76
|
-
return await new Promise((resolve) => {
|
|
77
|
-
const proc = spawn(rgBin, args, { cwd: projectRoot });
|
|
78
|
-
let stderr = '';
|
|
79
|
-
let pendingLine = '';
|
|
80
|
-
let truncated = false;
|
|
81
|
-
let settled = false;
|
|
82
|
-
const TEXT_MAX = 200;
|
|
83
|
-
const matches: Array<{ file: string; line: number; text: string }> =
|
|
84
|
-
[];
|
|
85
|
-
const fileCounts = new Map<string, number>();
|
|
86
|
-
|
|
87
|
-
const parseLine = (lineText: string) => {
|
|
88
|
-
if (!lineText || matches.length >= maxResults) return;
|
|
89
|
-
const m = lineText.match(/^(.+?):(\d+):(.*)$/s);
|
|
90
|
-
const match = (() => {
|
|
91
|
-
if (!m) {
|
|
92
|
-
return {
|
|
93
|
-
file: '',
|
|
94
|
-
line: 0,
|
|
95
|
-
text:
|
|
96
|
-
lineText.length > TEXT_MAX
|
|
97
|
-
? `${lineText.slice(0, TEXT_MAX)}…`
|
|
98
|
-
: lineText,
|
|
99
|
-
};
|
|
100
|
-
}
|
|
101
|
-
const file = m[1];
|
|
102
|
-
const line = Number.parseInt(m[2], 10);
|
|
103
|
-
const raw = m[3];
|
|
104
|
-
const text =
|
|
105
|
-
raw.length > TEXT_MAX ? `${raw.slice(0, TEXT_MAX)}…` : raw;
|
|
106
|
-
return { file, line, text };
|
|
107
|
-
})();
|
|
108
|
-
matches.push(match);
|
|
109
|
-
if (match.file) {
|
|
110
|
-
fileCounts.set(match.file, (fileCounts.get(match.file) ?? 0) + 1);
|
|
111
|
-
}
|
|
112
|
-
if (matches.length >= maxResults) {
|
|
113
|
-
truncated = true;
|
|
114
|
-
proc.kill('SIGTERM');
|
|
115
|
-
}
|
|
116
|
-
};
|
|
117
|
-
|
|
118
|
-
const resolveSuccess = () => {
|
|
119
|
-
if (settled) return;
|
|
120
|
-
settled = true;
|
|
121
|
-
const files = Array.from(fileCounts.entries()).map(
|
|
122
|
-
([file, count]) => ({ file, matches: count }),
|
|
123
|
-
);
|
|
124
|
-
resolve({
|
|
125
|
-
ok: true,
|
|
126
|
-
count: matches.length,
|
|
127
|
-
matches,
|
|
128
|
-
...(truncated
|
|
129
|
-
? { truncated: true, shownMatches: matches.length }
|
|
130
|
-
: {}),
|
|
131
|
-
...(files.length ? { files } : {}),
|
|
132
|
-
});
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
proc.stdout.on('data', (data) => {
|
|
136
|
-
if (matches.length >= maxResults) return;
|
|
137
|
-
pendingLine += data.toString();
|
|
138
|
-
const lines = pendingLine.split('\n');
|
|
139
|
-
pendingLine = lines.pop() ?? '';
|
|
140
|
-
for (const line of lines) {
|
|
141
|
-
parseLine(line);
|
|
142
|
-
if (matches.length >= maxResults) break;
|
|
143
|
-
}
|
|
144
|
-
});
|
|
145
|
-
|
|
146
|
-
proc.stderr.on('data', (data) => {
|
|
147
|
-
stderr += data.toString();
|
|
148
|
-
});
|
|
149
|
-
|
|
150
|
-
proc.on('close', (code) => {
|
|
151
|
-
if (pendingLine && matches.length < maxResults)
|
|
152
|
-
parseLine(pendingLine);
|
|
153
|
-
if (!truncated && code !== 0 && code !== 1) {
|
|
154
|
-
resolve(
|
|
155
|
-
createToolError(
|
|
156
|
-
stderr.trim() || 'ripgrep failed',
|
|
157
|
-
'execution',
|
|
158
|
-
{
|
|
159
|
-
suggestion:
|
|
160
|
-
'Check if ripgrep (rg) is installed and the query is valid',
|
|
161
|
-
},
|
|
162
|
-
),
|
|
163
|
-
);
|
|
164
|
-
return;
|
|
165
|
-
}
|
|
166
|
-
|
|
167
|
-
resolveSuccess();
|
|
168
|
-
});
|
|
169
|
-
|
|
170
|
-
proc.on('error', (err) => {
|
|
171
|
-
if (settled) return;
|
|
172
|
-
settled = true;
|
|
173
|
-
resolve(
|
|
174
|
-
createToolError(String(err), 'execution', {
|
|
175
|
-
suggestion: 'Ensure ripgrep (rg) is installed',
|
|
176
|
-
}),
|
|
177
|
-
);
|
|
178
|
-
});
|
|
179
|
-
});
|
|
180
|
-
} catch (err) {
|
|
181
|
-
return createToolError(String(err), 'execution', {
|
|
182
|
-
suggestion: 'Ensure ripgrep (rg) is installed',
|
|
183
|
-
});
|
|
184
|
-
}
|
|
185
|
-
},
|
|
186
|
-
});
|
|
187
|
-
return { name: 'ripgrep', tool: rg };
|
|
188
|
-
}
|
|
@@ -1,14 +0,0 @@
|
|
|
1
|
-
- Search files using ripgrep (rg) with regex patterns
|
|
2
|
-
- Returns a flat list of matches with `file`, `line`, and `text`
|
|
3
|
-
- Supports include globs and case-insensitive search
|
|
4
|
-
- Respects `.gitignore` by default
|
|
5
|
-
|
|
6
|
-
This is the only content-search tool available. Use it for any text search across the codebase.
|
|
7
|
-
|
|
8
|
-
## Usage tips
|
|
9
|
-
|
|
10
|
-
- Narrow the search set with `glob` first if the pattern may match too broadly.
|
|
11
|
-
- Prefer narrow `path` and `glob` values over repo-wide searches.
|
|
12
|
-
- Keep `maxResults` low for broad searches; the tool stops after the global limit.
|
|
13
|
-
- Batch independent searches (e.g. multiple function names) in a single turn for parallel execution.
|
|
14
|
-
- Use `ignoreCase: true` for case-insensitive matching; pass `glob` patterns (e.g. `["*.ts"]`) to limit file types.
|