@ottocode/sdk 0.1.173
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 +338 -0
- package/package.json +128 -0
- package/src/agent/types.ts +19 -0
- package/src/auth/src/copilot-oauth.ts +190 -0
- package/src/auth/src/index.ts +100 -0
- package/src/auth/src/oauth.ts +234 -0
- package/src/auth/src/openai-oauth.ts +394 -0
- package/src/auth/src/wallet.ts +51 -0
- package/src/browser.ts +32 -0
- package/src/config/src/index.ts +110 -0
- package/src/config/src/manager.ts +181 -0
- package/src/config/src/paths.ts +98 -0
- package/src/core/src/errors.ts +102 -0
- package/src/core/src/index.ts +108 -0
- package/src/core/src/providers/resolver.ts +244 -0
- package/src/core/src/streaming/artifacts.ts +41 -0
- package/src/core/src/terminals/bun-pty.ts +13 -0
- package/src/core/src/terminals/circular-buffer.ts +30 -0
- package/src/core/src/terminals/ensure-bun-pty.ts +70 -0
- package/src/core/src/terminals/index.ts +8 -0
- package/src/core/src/terminals/manager.ts +158 -0
- package/src/core/src/terminals/rust-libs.ts +30 -0
- package/src/core/src/terminals/terminal.ts +132 -0
- package/src/core/src/tools/bin-manager.ts +250 -0
- package/src/core/src/tools/builtin/bash.ts +155 -0
- package/src/core/src/tools/builtin/bash.txt +7 -0
- package/src/core/src/tools/builtin/file-cache.ts +39 -0
- package/src/core/src/tools/builtin/finish.ts +12 -0
- package/src/core/src/tools/builtin/finish.txt +10 -0
- package/src/core/src/tools/builtin/fs/cd.ts +19 -0
- package/src/core/src/tools/builtin/fs/cd.txt +5 -0
- package/src/core/src/tools/builtin/fs/index.ts +20 -0
- package/src/core/src/tools/builtin/fs/ls.ts +72 -0
- package/src/core/src/tools/builtin/fs/ls.txt +8 -0
- package/src/core/src/tools/builtin/fs/pwd.ts +17 -0
- package/src/core/src/tools/builtin/fs/pwd.txt +5 -0
- package/src/core/src/tools/builtin/fs/read.ts +119 -0
- package/src/core/src/tools/builtin/fs/read.txt +8 -0
- package/src/core/src/tools/builtin/fs/tree.ts +149 -0
- package/src/core/src/tools/builtin/fs/tree.txt +11 -0
- package/src/core/src/tools/builtin/fs/util.ts +95 -0
- package/src/core/src/tools/builtin/fs/write.ts +106 -0
- package/src/core/src/tools/builtin/fs/write.txt +11 -0
- package/src/core/src/tools/builtin/git.commit.txt +6 -0
- package/src/core/src/tools/builtin/git.diff.txt +5 -0
- package/src/core/src/tools/builtin/git.status.txt +5 -0
- package/src/core/src/tools/builtin/git.ts +151 -0
- package/src/core/src/tools/builtin/glob.ts +128 -0
- package/src/core/src/tools/builtin/glob.txt +10 -0
- package/src/core/src/tools/builtin/grep.ts +136 -0
- package/src/core/src/tools/builtin/grep.txt +9 -0
- package/src/core/src/tools/builtin/ignore.ts +45 -0
- package/src/core/src/tools/builtin/patch/apply.ts +546 -0
- package/src/core/src/tools/builtin/patch/constants.ts +5 -0
- package/src/core/src/tools/builtin/patch/normalize.ts +31 -0
- package/src/core/src/tools/builtin/patch/parse-enveloped.ts +209 -0
- package/src/core/src/tools/builtin/patch/parse-unified.ts +231 -0
- package/src/core/src/tools/builtin/patch/parse.ts +28 -0
- package/src/core/src/tools/builtin/patch/text.ts +23 -0
- package/src/core/src/tools/builtin/patch/types.ts +82 -0
- package/src/core/src/tools/builtin/patch.ts +167 -0
- package/src/core/src/tools/builtin/patch.txt +207 -0
- package/src/core/src/tools/builtin/progress.ts +55 -0
- package/src/core/src/tools/builtin/progress.txt +7 -0
- package/src/core/src/tools/builtin/ripgrep.ts +125 -0
- package/src/core/src/tools/builtin/ripgrep.txt +7 -0
- package/src/core/src/tools/builtin/terminal.ts +300 -0
- package/src/core/src/tools/builtin/terminal.txt +93 -0
- package/src/core/src/tools/builtin/todos.ts +66 -0
- package/src/core/src/tools/builtin/todos.txt +7 -0
- package/src/core/src/tools/builtin/websearch.ts +250 -0
- package/src/core/src/tools/builtin/websearch.txt +12 -0
- package/src/core/src/tools/error.ts +67 -0
- package/src/core/src/tools/loader.ts +421 -0
- package/src/core/src/types/index.ts +11 -0
- package/src/core/src/types/types.ts +4 -0
- package/src/core/src/utils/ansi.ts +27 -0
- package/src/core/src/utils/debug.ts +40 -0
- package/src/core/src/utils/logger.ts +150 -0
- package/src/index.ts +313 -0
- package/src/prompts/src/agents/build.txt +89 -0
- package/src/prompts/src/agents/general.txt +15 -0
- package/src/prompts/src/agents/plan.txt +10 -0
- package/src/prompts/src/agents/research.txt +50 -0
- package/src/prompts/src/base.txt +24 -0
- package/src/prompts/src/debug.ts +104 -0
- package/src/prompts/src/index.ts +1 -0
- package/src/prompts/src/modes/oneshot.txt +9 -0
- package/src/prompts/src/providers/anthropic.txt +247 -0
- package/src/prompts/src/providers/anthropicSpoof.txt +1 -0
- package/src/prompts/src/providers/default.txt +466 -0
- package/src/prompts/src/providers/google.txt +230 -0
- package/src/prompts/src/providers/moonshot.txt +24 -0
- package/src/prompts/src/providers/openai.txt +414 -0
- package/src/prompts/src/providers.ts +143 -0
- package/src/providers/src/anthropic-caching.ts +202 -0
- package/src/providers/src/anthropic-oauth-client.ts +157 -0
- package/src/providers/src/authorization.ts +17 -0
- package/src/providers/src/catalog-manual.ts +135 -0
- package/src/providers/src/catalog-merged.ts +9 -0
- package/src/providers/src/catalog.ts +8329 -0
- package/src/providers/src/copilot-client.ts +39 -0
- package/src/providers/src/env.ts +31 -0
- package/src/providers/src/google-client.ts +16 -0
- package/src/providers/src/index.ts +75 -0
- package/src/providers/src/moonshot-client.ts +25 -0
- package/src/providers/src/oauth-models.ts +39 -0
- package/src/providers/src/openai-oauth-client.ts +108 -0
- package/src/providers/src/opencode-client.ts +64 -0
- package/src/providers/src/openrouter-client.ts +31 -0
- package/src/providers/src/pricing.ts +178 -0
- package/src/providers/src/setu-client.ts +643 -0
- package/src/providers/src/utils.ts +210 -0
- package/src/providers/src/validate.ts +39 -0
- package/src/providers/src/zai-client.ts +47 -0
- package/src/skills/index.ts +34 -0
- package/src/skills/loader.ts +152 -0
- package/src/skills/parser.ts +108 -0
- package/src/skills/tool.ts +87 -0
- package/src/skills/types.ts +41 -0
- package/src/skills/validator.ts +110 -0
- package/src/types/src/auth.ts +33 -0
- package/src/types/src/config.ts +36 -0
- package/src/types/src/index.ts +20 -0
- package/src/types/src/provider.ts +71 -0
|
@@ -0,0 +1,167 @@
|
|
|
1
|
+
import { tool, type Tool } from 'ai';
|
|
2
|
+
import { z } from 'zod/v3';
|
|
3
|
+
import DESCRIPTION from './patch.txt' with { type: 'text' };
|
|
4
|
+
import { createToolError, type ToolResponse } from '../error.ts';
|
|
5
|
+
import { applyPatchOperations } from './patch/apply.ts';
|
|
6
|
+
import { parsePatchInput } from './patch/parse.ts';
|
|
7
|
+
import type {
|
|
8
|
+
AppliedPatchOperation,
|
|
9
|
+
PatchOperation,
|
|
10
|
+
RejectedPatch,
|
|
11
|
+
} from './patch/types.ts';
|
|
12
|
+
|
|
13
|
+
function serializeChanges(operations: AppliedPatchOperation[]) {
|
|
14
|
+
return operations.map((operation) => ({
|
|
15
|
+
filePath: operation.filePath,
|
|
16
|
+
kind: operation.kind,
|
|
17
|
+
hunks: operation.hunks.map((hunk) => ({
|
|
18
|
+
oldStart: hunk.oldStart,
|
|
19
|
+
oldLines: hunk.oldLines,
|
|
20
|
+
newStart: hunk.newStart,
|
|
21
|
+
newLines: hunk.newLines,
|
|
22
|
+
additions: hunk.additions,
|
|
23
|
+
deletions: hunk.deletions,
|
|
24
|
+
context: hunk.header.context,
|
|
25
|
+
})),
|
|
26
|
+
}));
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
function serializeRejected(rejected: RejectedPatch[]) {
|
|
30
|
+
if (rejected.length === 0) return undefined;
|
|
31
|
+
return rejected.map((item) => ({
|
|
32
|
+
filePath: item.filePath,
|
|
33
|
+
kind: item.kind,
|
|
34
|
+
reason: item.reason,
|
|
35
|
+
hunks:
|
|
36
|
+
item.operation.kind === 'update'
|
|
37
|
+
? item.operation.hunks.map((hunk) => ({
|
|
38
|
+
oldStart: hunk.header.oldStart,
|
|
39
|
+
oldLines: hunk.header.oldLines,
|
|
40
|
+
newStart: hunk.header.newStart,
|
|
41
|
+
newLines: hunk.header.newLines,
|
|
42
|
+
context: hunk.header.context,
|
|
43
|
+
lines: hunk.lines.map((line) => ({
|
|
44
|
+
kind: line.kind,
|
|
45
|
+
content: line.content,
|
|
46
|
+
})),
|
|
47
|
+
}))
|
|
48
|
+
: undefined,
|
|
49
|
+
}));
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export function buildApplyPatchTool(projectRoot: string): {
|
|
53
|
+
name: string;
|
|
54
|
+
tool: Tool;
|
|
55
|
+
} {
|
|
56
|
+
const applyPatch = tool({
|
|
57
|
+
description: DESCRIPTION,
|
|
58
|
+
inputSchema: z.object({
|
|
59
|
+
patch: z.string().min(1).describe('Unified diff patch content'),
|
|
60
|
+
allowRejects: z
|
|
61
|
+
.boolean()
|
|
62
|
+
.optional()
|
|
63
|
+
.default(false)
|
|
64
|
+
.describe(
|
|
65
|
+
'Allow hunks to be rejected without failing the whole operation',
|
|
66
|
+
),
|
|
67
|
+
fuzzyMatch: z
|
|
68
|
+
.boolean()
|
|
69
|
+
.optional()
|
|
70
|
+
.default(true)
|
|
71
|
+
.describe(
|
|
72
|
+
'Enable fuzzy matching with whitespace normalization (converts tabs to spaces for matching)',
|
|
73
|
+
),
|
|
74
|
+
}),
|
|
75
|
+
async execute({
|
|
76
|
+
patch,
|
|
77
|
+
allowRejects = false,
|
|
78
|
+
fuzzyMatch = true,
|
|
79
|
+
}: {
|
|
80
|
+
patch: string;
|
|
81
|
+
allowRejects?: boolean;
|
|
82
|
+
fuzzyMatch?: boolean;
|
|
83
|
+
}): Promise<
|
|
84
|
+
ToolResponse<{
|
|
85
|
+
output: string;
|
|
86
|
+
changes: unknown[];
|
|
87
|
+
artifact: unknown;
|
|
88
|
+
rejected?: unknown[];
|
|
89
|
+
}>
|
|
90
|
+
> {
|
|
91
|
+
if (!patch || patch.trim().length === 0) {
|
|
92
|
+
return createToolError(
|
|
93
|
+
'Missing required parameter: patch',
|
|
94
|
+
'validation',
|
|
95
|
+
{
|
|
96
|
+
parameter: 'patch',
|
|
97
|
+
value: patch,
|
|
98
|
+
suggestion: 'Provide patch content in enveloped format',
|
|
99
|
+
},
|
|
100
|
+
);
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
let operations: PatchOperation[];
|
|
104
|
+
try {
|
|
105
|
+
const parsed = parsePatchInput(patch);
|
|
106
|
+
operations = parsed.operations;
|
|
107
|
+
} catch (error) {
|
|
108
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
109
|
+
return createToolError(message, 'validation', {
|
|
110
|
+
parameter: 'patch',
|
|
111
|
+
suggestion:
|
|
112
|
+
'Provide patch content using the enveloped format (*** Begin Patch ... *** End Patch) or standard unified diff format (---/+++ headers).',
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
try {
|
|
117
|
+
const result = await applyPatchOperations(projectRoot, operations, {
|
|
118
|
+
useFuzzy: fuzzyMatch,
|
|
119
|
+
allowRejects,
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
const changes = serializeChanges(result.operations);
|
|
123
|
+
const rejected = serializeRejected(result.rejected);
|
|
124
|
+
|
|
125
|
+
const output: string[] = [];
|
|
126
|
+
if (result.operations.length > 0) {
|
|
127
|
+
output.push(
|
|
128
|
+
`Applied ${result.operations.length} operation${result.operations.length === 1 ? '' : 's'}`,
|
|
129
|
+
);
|
|
130
|
+
}
|
|
131
|
+
if (allowRejects && result.rejected.length > 0) {
|
|
132
|
+
output.push(
|
|
133
|
+
`Skipped ${result.rejected.length} operation${result.rejected.length === 1 ? '' : 's'} due to mismatches`,
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
if (output.length === 0) {
|
|
137
|
+
output.push('No changes applied');
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return {
|
|
141
|
+
ok: true,
|
|
142
|
+
output: output.join('; '),
|
|
143
|
+
changes,
|
|
144
|
+
artifact: {
|
|
145
|
+
kind: 'file_diff',
|
|
146
|
+
patch: result.normalizedPatch,
|
|
147
|
+
summary: result.summary,
|
|
148
|
+
},
|
|
149
|
+
rejected,
|
|
150
|
+
};
|
|
151
|
+
} catch (error) {
|
|
152
|
+
const errorMessage =
|
|
153
|
+
error instanceof Error ? error.message : String(error);
|
|
154
|
+
return createToolError(
|
|
155
|
+
`Failed to apply patch: ${errorMessage}`,
|
|
156
|
+
'execution',
|
|
157
|
+
{
|
|
158
|
+
suggestion:
|
|
159
|
+
'Check that the patch format is correct and target files exist',
|
|
160
|
+
},
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
},
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
return { name: 'apply_patch', tool: applyPatch };
|
|
167
|
+
}
|
|
@@ -0,0 +1,207 @@
|
|
|
1
|
+
Apply a patch to modify one or more files. The tool accepts the **enveloped patch format** (preferred) and will also auto-convert standard unified diffs (`--- / +++`) if you provide one.
|
|
2
|
+
|
|
3
|
+
**Quick checklist before you call the tool:**
|
|
4
|
+
- Finished patch must include both `*** Begin Patch` and `*** End Patch`
|
|
5
|
+
- Each change needs a directive (`*** Add File`, `*** Update File`, `*** Delete File`)
|
|
6
|
+
- Include real context lines (prefixed with space) around your changes
|
|
7
|
+
- Keep paths relative to the project root
|
|
8
|
+
- **Use multiple `@@` hunks for multiple edits to the same file - do NOT make separate tool calls**
|
|
9
|
+
|
|
10
|
+
**RECOMMENDED: Use apply_patch for targeted file edits to avoid rewriting entire files and wasting tokens.**
|
|
11
|
+
|
|
12
|
+
**FUZZY MATCHING**: By default, fuzzy matching is enabled to handle whitespace differences (tabs vs spaces).
|
|
13
|
+
Exact matching is tried first, then normalized matching if exact fails. Disable with `fuzzyMatch: false` if needed.
|
|
14
|
+
|
|
15
|
+
Use `apply_patch` only when:
|
|
16
|
+
- You want to make targeted edits to specific lines (primary use case)
|
|
17
|
+
- You want to make multiple related changes across different files in a single operation
|
|
18
|
+
- You need to add/delete entire files along with modifications
|
|
19
|
+
- You have JUST read the file immediately before (within the same response) and are confident the content hasn't changed
|
|
20
|
+
|
|
21
|
+
**CRITICAL - ALWAYS READ BEFORE PATCHING**: You MUST read the file content immediately before creating a patch.
|
|
22
|
+
Never rely on memory or previous reads. Even with fuzzy matching enabled (tolerates tabs vs spaces),
|
|
23
|
+
If the file content has changed significantly since you last read it, the patch may still fail.
|
|
24
|
+
|
|
25
|
+
**ALLOW REJECTS**: If you are applying a patch that might include stale hunks, set `allowRejects: true`.
|
|
26
|
+
The tool will apply the hunks it can and skip the rest, returning the skipped hunks with reasons.
|
|
27
|
+
|
|
28
|
+
**ALREADY APPLIED?**: If the removal lines are already gone or the additions are already present, the tool will treat the hunk as applied and move on—no need to resend the same change.
|
|
29
|
+
|
|
30
|
+
**Alternative: Use the `edit` tool if you need fuzzy matching or structured operations.**
|
|
31
|
+
|
|
32
|
+
## ⚠️ CRITICAL: Multiple Edits to Same File
|
|
33
|
+
|
|
34
|
+
**DO NOT make separate `apply_patch` calls for the same file!**
|
|
35
|
+
|
|
36
|
+
Instead, use multiple `@@` hunks in a single patch:
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
*** Begin Patch
|
|
40
|
+
*** Update File: src/app.ts
|
|
41
|
+
@@ first change - line 10
|
|
42
|
+
function init() {
|
|
43
|
+
- const port = 3000;
|
|
44
|
+
+ const port = 8080;
|
|
45
|
+
return port;
|
|
46
|
+
}
|
|
47
|
+
@@ second change - line 25
|
|
48
|
+
function start() {
|
|
49
|
+
- console.log("Starting...");
|
|
50
|
+
+ console.log("Server starting...");
|
|
51
|
+
init();
|
|
52
|
+
}
|
|
53
|
+
@@ third change - line 40
|
|
54
|
+
function cleanup() {
|
|
55
|
+
- // TODO
|
|
56
|
+
+ console.log("Cleaning up...");
|
|
57
|
+
}
|
|
58
|
+
*** End Patch
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
**This makes ONE tool call instead of three, saving tokens and reducing latency.**
|
|
62
|
+
|
|
63
|
+
## Patch Formats
|
|
64
|
+
|
|
65
|
+
### Preferred: Enveloped Patch
|
|
66
|
+
|
|
67
|
+
All patches must be wrapped in markers and use explicit file directives:
|
|
68
|
+
|
|
69
|
+
```
|
|
70
|
+
*** Begin Patch
|
|
71
|
+
*** Add File: path/to/file.txt
|
|
72
|
+
+line 1
|
|
73
|
+
+line 2
|
|
74
|
+
*** Update File: path/to/other.txt
|
|
75
|
+
-old line
|
|
76
|
+
+new line
|
|
77
|
+
*** Delete File: path/to/delete.txt
|
|
78
|
+
*** End Patch
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Also Supported: Standard Unified Diff
|
|
82
|
+
|
|
83
|
+
You can paste a regular `git diff` style patch:
|
|
84
|
+
|
|
85
|
+
```
|
|
86
|
+
diff --git a/src/app.ts b/src/app.ts
|
|
87
|
+
--- a/src/app.ts
|
|
88
|
+
+++ b/src/app.ts
|
|
89
|
+
@@
|
|
90
|
+
-const PORT = 3000;
|
|
91
|
+
+const PORT = 8080;
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
The tool will convert it automatically, but the enveloped format is still recommended because it is more explicit and easier to control.
|
|
95
|
+
|
|
96
|
+
## File Operations
|
|
97
|
+
|
|
98
|
+
### Add a new file:
|
|
99
|
+
```
|
|
100
|
+
*** Begin Patch
|
|
101
|
+
*** Add File: src/hello.ts
|
|
102
|
+
+export function hello() {
|
|
103
|
+
+ console.log("Hello!");
|
|
104
|
+
+}
|
|
105
|
+
*** End Patch
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
### Update an existing file (simple replacement):
|
|
109
|
+
```
|
|
110
|
+
*** Begin Patch
|
|
111
|
+
*** Update File: src/config.ts
|
|
112
|
+
-const PORT = 3000;
|
|
113
|
+
+const PORT = 8080;
|
|
114
|
+
*** End Patch
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
**CRITICAL**: The `-` lines must match EXACTLY what's in the file, character-for-character. If you're not 100% certain, use the `edit` tool instead.
|
|
118
|
+
|
|
119
|
+
### Update with context (recommended for precision):
|
|
120
|
+
```
|
|
121
|
+
*** Begin Patch
|
|
122
|
+
*** Update File: src/app.ts
|
|
123
|
+
@@ function main()
|
|
124
|
+
function main() {
|
|
125
|
+
- console.log("old");
|
|
126
|
+
+ console.log("new");
|
|
127
|
+
}
|
|
128
|
+
*** End Patch
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
**IMPORTANT**:
|
|
132
|
+
- The `@@` line is an OPTIONAL hint to help locate the change - it's a comment, not parsed as context
|
|
133
|
+
- REQUIRED: Actual context lines (starting with space ` `) that match the file exactly
|
|
134
|
+
- The context lines with space prefix are what the tool uses to find the location
|
|
135
|
+
- The `@@` line just helps humans/AI understand what section you're editing
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
### Update multiple locations in the same file:
|
|
139
|
+
```
|
|
140
|
+
*** Begin Patch
|
|
141
|
+
*** Update File: src/app.ts
|
|
142
|
+
@@ first section - near line 10
|
|
143
|
+
function init() {
|
|
144
|
+
- const port = 3000;
|
|
145
|
+
+ const port = 8080;
|
|
146
|
+
return port;
|
|
147
|
+
}
|
|
148
|
+
@@ second section - near line 25
|
|
149
|
+
function start() {
|
|
150
|
+
- console.log("Starting...");
|
|
151
|
+
+ console.log("Server starting...");
|
|
152
|
+
init();
|
|
153
|
+
}
|
|
154
|
+
*** End Patch
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
**IMPORTANT**: Use separate `@@` headers for each non-consecutive change location. This allows multiple edits to the same file in one patch, saving tokens and reducing tool calls.
|
|
158
|
+
|
|
159
|
+
### Delete a file:
|
|
160
|
+
```
|
|
161
|
+
*** Begin Patch
|
|
162
|
+
*** Delete File: old/unused.ts
|
|
163
|
+
*** End Patch
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
### Multiple operations in one patch:
|
|
167
|
+
```
|
|
168
|
+
*** Begin Patch
|
|
169
|
+
*** Add File: new.txt
|
|
170
|
+
+New content
|
|
171
|
+
*** Update File: existing.txt
|
|
172
|
+
-old
|
|
173
|
+
+new
|
|
174
|
+
*** Delete File: obsolete.txt
|
|
175
|
+
*** End Patch
|
|
176
|
+
```
|
|
177
|
+
|
|
178
|
+
## Line Prefixes
|
|
179
|
+
|
|
180
|
+
- Lines starting with `+` are added
|
|
181
|
+
- Lines starting with `-` are removed
|
|
182
|
+
- Lines starting with ` ` (space) are context (kept unchanged)
|
|
183
|
+
- Lines starting with `@@` are optional hints/comments (not parsed as context)
|
|
184
|
+
|
|
185
|
+
## Common Errors
|
|
186
|
+
|
|
187
|
+
**"Missing *** End Patch marker"**: Every patch MUST end with `*** End Patch` on its own line.
|
|
188
|
+
This is the most common error. Double-check your patch ends with:
|
|
189
|
+
```
|
|
190
|
+
*** End Patch
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
**"Failed to find expected lines"**: The file content doesn't match your patch. Common causes:
|
|
194
|
+
- Missing context lines (lines with space prefix)
|
|
195
|
+
- Using `@@` line as context instead of real context lines
|
|
196
|
+
- The file content has changed since you read it
|
|
197
|
+
- Whitespace/indentation mismatch
|
|
198
|
+
|
|
199
|
+
**Solution**: Always read the file immediately before patching and include actual context lines with space prefix.
|
|
200
|
+
|
|
201
|
+
## Important Notes
|
|
202
|
+
|
|
203
|
+
- **Patches are fragile**: Any mismatch in whitespace, indentation, or content will cause failure
|
|
204
|
+
- **Use `edit` for reliability**: The `edit` tool can make targeted changes without requiring exact matches
|
|
205
|
+
- All file paths are relative to the project root
|
|
206
|
+
- The enveloped format is the most reliable; unified diffs are converted automatically if provided
|
|
207
|
+
- Always wrap patches with `*** Begin Patch` and `*** End Patch` when you write them manually
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { tool } from 'ai';
|
|
2
|
+
import { z } from 'zod/v3';
|
|
3
|
+
import DESCRIPTION from './progress.txt' with { type: 'text' };
|
|
4
|
+
|
|
5
|
+
// Progress update tool: allows the model to emit lightweight status signals
|
|
6
|
+
// without revealing chain-of-thought. The runner/UI should surface these
|
|
7
|
+
// messages immediately.
|
|
8
|
+
const StageEnum = z.enum([
|
|
9
|
+
'planning',
|
|
10
|
+
'discovering',
|
|
11
|
+
'generating',
|
|
12
|
+
'preparing',
|
|
13
|
+
'writing',
|
|
14
|
+
'verifying',
|
|
15
|
+
]);
|
|
16
|
+
|
|
17
|
+
export const progressUpdateTool = tool({
|
|
18
|
+
description: DESCRIPTION,
|
|
19
|
+
inputSchema: z.object({
|
|
20
|
+
message: z
|
|
21
|
+
.string()
|
|
22
|
+
.min(1)
|
|
23
|
+
.max(200)
|
|
24
|
+
.describe('Short, user-facing status message (<= 200 chars).'),
|
|
25
|
+
pct: z
|
|
26
|
+
.number()
|
|
27
|
+
.min(0)
|
|
28
|
+
.max(100)
|
|
29
|
+
.optional()
|
|
30
|
+
.describe('Optional overall progress percent 0-100.'),
|
|
31
|
+
stage: StageEnum.optional().default('planning'),
|
|
32
|
+
}),
|
|
33
|
+
async execute({
|
|
34
|
+
message,
|
|
35
|
+
pct,
|
|
36
|
+
stage,
|
|
37
|
+
}: {
|
|
38
|
+
message: string;
|
|
39
|
+
pct?: number;
|
|
40
|
+
stage?: z.infer<typeof StageEnum>;
|
|
41
|
+
}) {
|
|
42
|
+
// Keep the tool lightweight; no side effects beyond the event itself.
|
|
43
|
+
// Returning the normalized payload allows generic renderers to inspect it if needed.
|
|
44
|
+
const normalizedPct =
|
|
45
|
+
typeof pct === 'number'
|
|
46
|
+
? Math.min(100, Math.max(0, Math.round(pct)))
|
|
47
|
+
: undefined;
|
|
48
|
+
return {
|
|
49
|
+
ok: true,
|
|
50
|
+
message,
|
|
51
|
+
pct: normalizedPct,
|
|
52
|
+
stage: stage ?? 'planning',
|
|
53
|
+
} as const;
|
|
54
|
+
},
|
|
55
|
+
});
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
- Emit a short, user-facing progress/status update
|
|
2
|
+
- Supports optional `pct` (0–100) and `stage` indicators
|
|
3
|
+
- Lightweight; intended for immediate UI display
|
|
4
|
+
|
|
5
|
+
Usage tips:
|
|
6
|
+
- Keep messages short (<= 200 chars) and informative
|
|
7
|
+
- Use multiple updates during long-running tasks
|
|
@@ -0,0 +1,125 @@
|
|
|
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(500),
|
|
28
|
+
}),
|
|
29
|
+
async execute({
|
|
30
|
+
query,
|
|
31
|
+
path = '.',
|
|
32
|
+
ignoreCase,
|
|
33
|
+
glob,
|
|
34
|
+
maxResults = 500,
|
|
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
|
+
}>
|
|
46
|
+
> {
|
|
47
|
+
function expandTilde(p: string) {
|
|
48
|
+
const home = process.env.HOME || process.env.USERPROFILE || '';
|
|
49
|
+
if (!home) return p;
|
|
50
|
+
if (p === '~') return home;
|
|
51
|
+
if (p.startsWith('~/')) return `${home}/${p.slice(2)}`;
|
|
52
|
+
return p;
|
|
53
|
+
}
|
|
54
|
+
const p = expandTilde(String(path ?? '.')).trim();
|
|
55
|
+
const isAbs = p.startsWith('/') || /^[A-Za-z]:[\\/]/.test(p);
|
|
56
|
+
const target = p ? (isAbs ? p : join(projectRoot, p)) : projectRoot;
|
|
57
|
+
const args = ['--no-heading', '--line-number', '--color=never'];
|
|
58
|
+
if (ignoreCase) args.push('-i');
|
|
59
|
+
if (Array.isArray(glob)) for (const g of glob) args.push('-g', g);
|
|
60
|
+
args.push('--max-count', String(maxResults));
|
|
61
|
+
args.push(query);
|
|
62
|
+
args.push(target);
|
|
63
|
+
|
|
64
|
+
try {
|
|
65
|
+
const rgBin = await resolveBinary('rg');
|
|
66
|
+
return await new Promise((resolve) => {
|
|
67
|
+
const proc = spawn(rgBin, args, { cwd: projectRoot });
|
|
68
|
+
let stdout = '';
|
|
69
|
+
let stderr = '';
|
|
70
|
+
|
|
71
|
+
proc.stdout.on('data', (data) => {
|
|
72
|
+
stdout += data.toString();
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
proc.stderr.on('data', (data) => {
|
|
76
|
+
stderr += data.toString();
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
proc.on('close', (code) => {
|
|
80
|
+
if (code !== 0 && code !== 1) {
|
|
81
|
+
resolve(
|
|
82
|
+
createToolError(
|
|
83
|
+
stderr.trim() || 'ripgrep failed',
|
|
84
|
+
'execution',
|
|
85
|
+
{
|
|
86
|
+
suggestion:
|
|
87
|
+
'Check if ripgrep (rg) is installed and the query is valid',
|
|
88
|
+
},
|
|
89
|
+
),
|
|
90
|
+
);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
const lines = stdout
|
|
95
|
+
.split('\n')
|
|
96
|
+
.filter(Boolean)
|
|
97
|
+
.slice(0, maxResults);
|
|
98
|
+
const matches = lines.map((l) => {
|
|
99
|
+
const parts = l.split(':');
|
|
100
|
+
if (parts.length < 3) return { file: '', line: 0, text: l };
|
|
101
|
+
const file = parts[0];
|
|
102
|
+
const line = Number.parseInt(parts[1], 10);
|
|
103
|
+
const text = parts.slice(2).join(':');
|
|
104
|
+
return { file, line, text };
|
|
105
|
+
});
|
|
106
|
+
resolve({ ok: true, count: matches.length, matches });
|
|
107
|
+
});
|
|
108
|
+
|
|
109
|
+
proc.on('error', (err) => {
|
|
110
|
+
resolve(
|
|
111
|
+
createToolError(String(err), 'execution', {
|
|
112
|
+
suggestion: 'Ensure ripgrep (rg) is installed',
|
|
113
|
+
}),
|
|
114
|
+
);
|
|
115
|
+
});
|
|
116
|
+
});
|
|
117
|
+
} catch (err) {
|
|
118
|
+
return createToolError(String(err), 'execution', {
|
|
119
|
+
suggestion: 'Ensure ripgrep (rg) is installed',
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
},
|
|
123
|
+
});
|
|
124
|
+
return { name: 'ripgrep', tool: rg };
|
|
125
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
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
|
+
|
|
5
|
+
Usage tips:
|
|
6
|
+
- Use the Grep tool for a friendly summary grouped by file
|
|
7
|
+
- Use the Glob tool first to limit the search set if needed
|