@probelabs/probe 0.6.0-rc292 → 0.6.0-rc293
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/bin/binaries/{probe-v0.6.0-rc292-aarch64-apple-darwin.tar.gz → probe-v0.6.0-rc293-aarch64-apple-darwin.tar.gz} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc292-aarch64-unknown-linux-musl.tar.gz → probe-v0.6.0-rc293-aarch64-unknown-linux-musl.tar.gz} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc292-x86_64-apple-darwin.tar.gz → probe-v0.6.0-rc293-x86_64-apple-darwin.tar.gz} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc292-x86_64-pc-windows-msvc.zip → probe-v0.6.0-rc293-x86_64-pc-windows-msvc.zip} +0 -0
- package/bin/binaries/{probe-v0.6.0-rc292-x86_64-unknown-linux-musl.tar.gz → probe-v0.6.0-rc293-x86_64-unknown-linux-musl.tar.gz} +0 -0
- package/build/agent/shared/prompts.js +33 -3
- package/build/tools/fileTracker.js +33 -17
- package/build/tools/vercel.js +13 -10
- package/cjs/agent/ProbeAgent.cjs +75 -37
- package/cjs/index.cjs +82 -44
- package/package.json +1 -1
- package/src/agent/shared/prompts.js +33 -3
- package/src/tools/fileTracker.js +33 -17
- package/src/tools/vercel.js +13 -10
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
Binary file
|
|
@@ -90,9 +90,9 @@ If the solution is clear, you can jump to implementation right away. If not, ask
|
|
|
90
90
|
- Do not add code comments unless the logic is genuinely complex and non-obvious.
|
|
91
91
|
|
|
92
92
|
# Before Implementation
|
|
93
|
-
-
|
|
94
|
-
-
|
|
95
|
-
-
|
|
93
|
+
- Read tests first — find existing test files for the module you're changing. They reveal expected behavior, edge cases, and the project's testing patterns.
|
|
94
|
+
- Read neighboring files — understand naming conventions, error handling patterns, import style, and existing utilities before creating new ones.
|
|
95
|
+
- Trace the call chain — follow how the code you're changing is called and what depends on it. Check interfaces, types, and consumers.
|
|
96
96
|
- Focus on backward compatibility
|
|
97
97
|
- Consider scalability, maintainability, and extensibility in your analysis
|
|
98
98
|
|
|
@@ -117,6 +117,20 @@ Before building or testing, determine the project's toolchain:
|
|
|
117
117
|
- Read README for build/test instructions if the above are unclear
|
|
118
118
|
- Common patterns: \`make build\`/\`make test\`, \`npm run build\`/\`npm test\`, \`cargo build\`/\`cargo test\`, \`go build ./...\`/\`go test ./...\`, \`python -m pytest\`
|
|
119
119
|
|
|
120
|
+
# File Editing Rules
|
|
121
|
+
You have access to the \`edit\`, \`create\`, and \`multi_edit\` tools for modifying files. You MUST use these tools for ALL code changes. They are purpose-built, atomic, and safe.
|
|
122
|
+
|
|
123
|
+
DO NOT use sed, awk, echo/cat redirection, or heredocs to modify source code. These commands cause real damage in practice: truncated lines, duplicate code blocks, broken syntax. Every bad edit wastes iterations on fix-up commits.
|
|
124
|
+
|
|
125
|
+
Use the right tool:
|
|
126
|
+
1. To MODIFY existing code → \`edit\` tool (old_string → new_string, or start_line/end_line)
|
|
127
|
+
2. To CREATE a new file → \`create\` tool
|
|
128
|
+
3. To CHANGE multiple files at once → \`multi_edit\` tool
|
|
129
|
+
4. To READ code → \`extract\` or \`search\` tools
|
|
130
|
+
5. If \`edit\` fails with "file has not been read yet" → use \`extract\` with the EXACT same file path you will pass to \`edit\`. Relative vs absolute path mismatch causes this error. Use the same path format consistently. If it still fails, use bash \`cat\` to read the file, then use \`create\` to write the entire modified file. Do NOT fall back to sed.
|
|
131
|
+
|
|
132
|
+
Bash is fine for: formatters (gofmt, prettier, black), build/test/lint commands, git operations, and read-only file inspection (cat, head, tail). sed/awk should ONLY be used for trivial non-code tasks (e.g., config file tweaks) where the replacement is a simple literal string swap.
|
|
133
|
+
|
|
120
134
|
# During Implementation
|
|
121
135
|
- Always create a new branch before making changes to the codebase.
|
|
122
136
|
- Fix problems at the root cause, not with surface-level patches. Prefer general solutions over special cases.
|
|
@@ -143,6 +157,22 @@ Before committing or creating a PR, run through this checklist:
|
|
|
143
157
|
|
|
144
158
|
Do NOT skip verification. Do NOT proceed to PR creation with a broken build or failing tests.
|
|
145
159
|
|
|
160
|
+
# Output Integrity
|
|
161
|
+
Your final output MUST accurately reflect what ACTUALLY happened. Do NOT fabricate, hallucinate, or report aspirational results.
|
|
162
|
+
|
|
163
|
+
- Only report PR URLs you actually created or updated with \`gh pr create\` or \`git push\`. If you checked out an existing PR but did NOT push changes to it, do NOT claim you updated it.
|
|
164
|
+
- Describe what you ACTUALLY DID, not what you planned or intended to do. If you ran out of iterations, say so. If tests failed, say so.
|
|
165
|
+
- Only list files you actually modified AND committed.
|
|
166
|
+
- If you could not complete the task — ran out of iterations, tests failed, build broken, push rejected — report the real reason honestly.
|
|
167
|
+
|
|
168
|
+
NEVER claim success when:
|
|
169
|
+
- You did not run \`git push\` successfully
|
|
170
|
+
- Tests failed and you did not fix them
|
|
171
|
+
- You hit the iteration limit before completing the work
|
|
172
|
+
- You only analyzed/investigated but did not implement changes
|
|
173
|
+
|
|
174
|
+
A false success report is WORSE than an honest failure — it misleads the user into thinking work is done when it is not.
|
|
175
|
+
|
|
146
176
|
# GitHub Integration
|
|
147
177
|
- Use the \`gh\` CLI for all GitHub operations: issues, pull requests, checks, releases.
|
|
148
178
|
- To view issues or PRs: \`gh issue view <number>\`, \`gh pr view <number>\`.
|
|
@@ -12,9 +12,22 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import { createHash } from 'crypto';
|
|
15
|
-
import { resolve, isAbsolute } from 'path';
|
|
15
|
+
import { resolve, isAbsolute, normalize } from 'path';
|
|
16
16
|
import { findSymbol } from './symbolEdit.js';
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Normalize a file path for consistent storage and lookup.
|
|
20
|
+
* Resolves '.', '..', double slashes, and ensures absolute paths are canonical.
|
|
21
|
+
* Does NOT resolve symlinks (that would be expensive and might fail for non-existent files).
|
|
22
|
+
* @param {string} filePath - Path to normalize
|
|
23
|
+
* @returns {string} Normalized path
|
|
24
|
+
*/
|
|
25
|
+
function normalizePath(filePath) {
|
|
26
|
+
if (!filePath) return filePath;
|
|
27
|
+
// resolve() handles '.', '..', double slashes, and makes the path absolute
|
|
28
|
+
return resolve(filePath);
|
|
29
|
+
}
|
|
30
|
+
|
|
18
31
|
/**
|
|
19
32
|
* Compute a SHA-256 content hash for a code block.
|
|
20
33
|
* Normalizes trailing whitespace per line for robustness against editor formatting.
|
|
@@ -106,10 +119,11 @@ export class FileTracker {
|
|
|
106
119
|
* @param {string} resolvedPath - Absolute path to the file
|
|
107
120
|
*/
|
|
108
121
|
markFileSeen(resolvedPath) {
|
|
109
|
-
|
|
110
|
-
this.
|
|
122
|
+
const normalized = normalizePath(resolvedPath);
|
|
123
|
+
this._seenFiles.add(normalized);
|
|
124
|
+
this._textEditCounts.set(normalized, 0);
|
|
111
125
|
if (this.debug) {
|
|
112
|
-
console.error(`[FileTracker] Marked as seen: ${
|
|
126
|
+
console.error(`[FileTracker] Marked as seen: ${normalized}`);
|
|
113
127
|
}
|
|
114
128
|
}
|
|
115
129
|
|
|
@@ -119,7 +133,7 @@ export class FileTracker {
|
|
|
119
133
|
* @returns {boolean}
|
|
120
134
|
*/
|
|
121
135
|
isFileSeen(resolvedPath) {
|
|
122
|
-
return this._seenFiles.has(resolvedPath);
|
|
136
|
+
return this._seenFiles.has(normalizePath(resolvedPath));
|
|
123
137
|
}
|
|
124
138
|
|
|
125
139
|
/**
|
|
@@ -132,7 +146,7 @@ export class FileTracker {
|
|
|
132
146
|
* @param {string} [source='extract'] - How the content was obtained
|
|
133
147
|
*/
|
|
134
148
|
trackSymbolContent(resolvedPath, symbolName, code, startLine, endLine, source = 'extract') {
|
|
135
|
-
const key = `${resolvedPath}#${symbolName}`;
|
|
149
|
+
const key = `${normalizePath(resolvedPath)}#${symbolName}`;
|
|
136
150
|
const contentHash = computeContentHash(code);
|
|
137
151
|
this._contentRecords.set(key, {
|
|
138
152
|
contentHash,
|
|
@@ -154,7 +168,7 @@ export class FileTracker {
|
|
|
154
168
|
* @returns {Object|null} The stored record or null
|
|
155
169
|
*/
|
|
156
170
|
getSymbolRecord(resolvedPath, symbolName) {
|
|
157
|
-
return this._contentRecords.get(`${resolvedPath}#${symbolName}`) || null;
|
|
171
|
+
return this._contentRecords.get(`${normalizePath(resolvedPath)}#${symbolName}`) || null;
|
|
158
172
|
}
|
|
159
173
|
|
|
160
174
|
/**
|
|
@@ -165,7 +179,7 @@ export class FileTracker {
|
|
|
165
179
|
* @returns {{ok: boolean, reason?: string, message?: string}}
|
|
166
180
|
*/
|
|
167
181
|
checkSymbolContent(resolvedPath, symbolName, currentCode) {
|
|
168
|
-
const key = `${resolvedPath}#${symbolName}`;
|
|
182
|
+
const key = `${normalizePath(resolvedPath)}#${symbolName}`;
|
|
169
183
|
const record = this._contentRecords.get(key);
|
|
170
184
|
|
|
171
185
|
if (!record) {
|
|
@@ -253,7 +267,7 @@ export class FileTracker {
|
|
|
253
267
|
* @returns {{ok: boolean, reason?: string, message?: string}}
|
|
254
268
|
*/
|
|
255
269
|
checkBeforeEdit(resolvedPath) {
|
|
256
|
-
if (!this._seenFiles.has(resolvedPath)) {
|
|
270
|
+
if (!this._seenFiles.has(normalizePath(resolvedPath))) {
|
|
257
271
|
return {
|
|
258
272
|
ok: false,
|
|
259
273
|
reason: 'untracked',
|
|
@@ -269,8 +283,9 @@ export class FileTracker {
|
|
|
269
283
|
* @param {string} resolvedPath - Absolute path to the file
|
|
270
284
|
*/
|
|
271
285
|
async trackFileAfterWrite(resolvedPath) {
|
|
272
|
-
|
|
273
|
-
this.
|
|
286
|
+
const normalized = normalizePath(resolvedPath);
|
|
287
|
+
this._seenFiles.add(normalized);
|
|
288
|
+
this.invalidateFileRecords(normalized);
|
|
274
289
|
}
|
|
275
290
|
|
|
276
291
|
/**
|
|
@@ -279,10 +294,11 @@ export class FileTracker {
|
|
|
279
294
|
* @param {string} resolvedPath - Absolute path to the file
|
|
280
295
|
*/
|
|
281
296
|
recordTextEdit(resolvedPath) {
|
|
282
|
-
const
|
|
283
|
-
this._textEditCounts.
|
|
297
|
+
const normalized = normalizePath(resolvedPath);
|
|
298
|
+
const count = (this._textEditCounts.get(normalized) || 0) + 1;
|
|
299
|
+
this._textEditCounts.set(normalized, count);
|
|
284
300
|
if (this.debug) {
|
|
285
|
-
console.error(`[FileTracker] Text edit #${count} for ${
|
|
301
|
+
console.error(`[FileTracker] Text edit #${count} for ${normalized}`);
|
|
286
302
|
}
|
|
287
303
|
}
|
|
288
304
|
|
|
@@ -292,7 +308,7 @@ export class FileTracker {
|
|
|
292
308
|
* @returns {{ok: boolean, editCount?: number, message?: string}}
|
|
293
309
|
*/
|
|
294
310
|
checkTextEditStaleness(resolvedPath) {
|
|
295
|
-
const count = this._textEditCounts.get(resolvedPath) || 0;
|
|
311
|
+
const count = this._textEditCounts.get(normalizePath(resolvedPath)) || 0;
|
|
296
312
|
if (count >= this.maxConsecutiveTextEdits) {
|
|
297
313
|
return {
|
|
298
314
|
ok: false,
|
|
@@ -323,7 +339,7 @@ export class FileTracker {
|
|
|
323
339
|
* @param {string} resolvedPath - Absolute path to the file
|
|
324
340
|
*/
|
|
325
341
|
invalidateFileRecords(resolvedPath) {
|
|
326
|
-
const prefix = resolvedPath + '#';
|
|
342
|
+
const prefix = normalizePath(resolvedPath) + '#';
|
|
327
343
|
for (const key of this._contentRecords.keys()) {
|
|
328
344
|
if (key.startsWith(prefix)) {
|
|
329
345
|
this._contentRecords.delete(key);
|
|
@@ -340,7 +356,7 @@ export class FileTracker {
|
|
|
340
356
|
* @returns {boolean}
|
|
341
357
|
*/
|
|
342
358
|
isTracked(resolvedPath) {
|
|
343
|
-
return this.isFileSeen(resolvedPath);
|
|
359
|
+
return this.isFileSeen(normalizePath(resolvedPath));
|
|
344
360
|
}
|
|
345
361
|
|
|
346
362
|
/**
|
package/build/tools/vercel.js
CHANGED
|
@@ -385,7 +385,7 @@ export const searchTool = (options = {}) => {
|
|
|
385
385
|
? searchDelegateDescription
|
|
386
386
|
: searchDescription,
|
|
387
387
|
inputSchema: searchSchema,
|
|
388
|
-
execute: async ({ query: searchQuery, path, allow_tests, exact, maxTokens: paramMaxTokens, language, session, nextPage }) => {
|
|
388
|
+
execute: async ({ query: searchQuery, path, allow_tests, exact, maxTokens: paramMaxTokens, language, session, nextPage, workingDirectory }) => {
|
|
389
389
|
// Auto-quote mixed-case and underscore terms to prevent unwanted stemming/splitting
|
|
390
390
|
// Skip when exact=true since that already preserves the literal string
|
|
391
391
|
if (!exact && searchQuery) {
|
|
@@ -399,15 +399,18 @@ export const searchTool = (options = {}) => {
|
|
|
399
399
|
// Use parameter maxTokens if provided, otherwise use the default
|
|
400
400
|
const effectiveMaxTokens = paramMaxTokens || maxTokens;
|
|
401
401
|
|
|
402
|
+
// Use workingDirectory (injected by _buildNativeTools at runtime) > cwd from config > fallback
|
|
403
|
+
const effectiveSearchCwd = workingDirectory || options.cwd || '.';
|
|
404
|
+
|
|
402
405
|
// Parse and resolve paths (supports comma-separated and relative paths)
|
|
403
406
|
let searchPaths;
|
|
404
407
|
if (path) {
|
|
405
|
-
searchPaths = parseAndResolvePaths(path,
|
|
408
|
+
searchPaths = parseAndResolvePaths(path, effectiveSearchCwd);
|
|
406
409
|
}
|
|
407
410
|
|
|
408
411
|
// Default to cwd or '.' if no paths provided
|
|
409
412
|
if (!searchPaths || searchPaths.length === 0) {
|
|
410
|
-
searchPaths = [
|
|
413
|
+
searchPaths = [effectiveSearchCwd];
|
|
411
414
|
}
|
|
412
415
|
|
|
413
416
|
// Join paths with space for CLI (probe search supports multiple paths)
|
|
@@ -416,7 +419,7 @@ export const searchTool = (options = {}) => {
|
|
|
416
419
|
const searchOptions = {
|
|
417
420
|
query: searchQuery,
|
|
418
421
|
path: searchPath,
|
|
419
|
-
cwd:
|
|
422
|
+
cwd: effectiveSearchCwd, // Working directory for resolving relative paths
|
|
420
423
|
allowTests: allow_tests ?? true,
|
|
421
424
|
exact,
|
|
422
425
|
json: false,
|
|
@@ -473,7 +476,7 @@ export const searchTool = (options = {}) => {
|
|
|
473
476
|
const result = maybeAnnotate(await runRawSearch());
|
|
474
477
|
// Track files found in search results for staleness detection
|
|
475
478
|
if (options.fileTracker && typeof result === 'string') {
|
|
476
|
-
options.fileTracker.trackFilesFromOutput(result,
|
|
479
|
+
options.fileTracker.trackFilesFromOutput(result, effectiveSearchCwd).catch(() => {});
|
|
477
480
|
}
|
|
478
481
|
return result;
|
|
479
482
|
} catch (error) {
|
|
@@ -532,7 +535,7 @@ export const searchTool = (options = {}) => {
|
|
|
532
535
|
}
|
|
533
536
|
const fallbackResult = maybeAnnotate(await runRawSearch());
|
|
534
537
|
if (options.fileTracker && typeof fallbackResult === 'string') {
|
|
535
|
-
options.fileTracker.trackFilesFromOutput(fallbackResult,
|
|
538
|
+
options.fileTracker.trackFilesFromOutput(fallbackResult, effectiveSearchCwd).catch(() => {});
|
|
536
539
|
}
|
|
537
540
|
return fallbackResult;
|
|
538
541
|
}
|
|
@@ -614,7 +617,7 @@ export const searchTool = (options = {}) => {
|
|
|
614
617
|
try {
|
|
615
618
|
const fallbackResult2 = maybeAnnotate(await runRawSearch());
|
|
616
619
|
if (options.fileTracker && typeof fallbackResult2 === 'string') {
|
|
617
|
-
options.fileTracker.trackFilesFromOutput(fallbackResult2,
|
|
620
|
+
options.fileTracker.trackFilesFromOutput(fallbackResult2, effectiveSearchCwd).catch(() => {});
|
|
618
621
|
}
|
|
619
622
|
return fallbackResult2;
|
|
620
623
|
} catch (fallbackError) {
|
|
@@ -693,10 +696,10 @@ export const extractTool = (options = {}) => {
|
|
|
693
696
|
name: 'extract',
|
|
694
697
|
description: extractDescription,
|
|
695
698
|
inputSchema: extractSchema,
|
|
696
|
-
execute: async ({ targets, input_content, line, end_line, allow_tests, context_lines, format }) => {
|
|
699
|
+
execute: async ({ targets, input_content, line, end_line, allow_tests, context_lines, format, workingDirectory }) => {
|
|
697
700
|
try {
|
|
698
|
-
// Use
|
|
699
|
-
const effectiveCwd = options.cwd || '.';
|
|
701
|
+
// Use workingDirectory (injected by _buildNativeTools at runtime) > cwd from config > fallback
|
|
702
|
+
const effectiveCwd = workingDirectory || options.cwd || '.';
|
|
700
703
|
|
|
701
704
|
if (debug) {
|
|
702
705
|
if (targets) {
|
package/cjs/agent/ProbeAgent.cjs
CHANGED
|
@@ -27566,7 +27566,7 @@ var init_vercel = __esm({
|
|
|
27566
27566
|
name: "search",
|
|
27567
27567
|
description: searchDelegate ? searchDelegateDescription : searchDescription,
|
|
27568
27568
|
inputSchema: searchSchema,
|
|
27569
|
-
execute: async ({ query: searchQuery, path: path9, allow_tests, exact, maxTokens: paramMaxTokens, language, session, nextPage }) => {
|
|
27569
|
+
execute: async ({ query: searchQuery, path: path9, allow_tests, exact, maxTokens: paramMaxTokens, language, session, nextPage, workingDirectory }) => {
|
|
27570
27570
|
if (!exact && searchQuery) {
|
|
27571
27571
|
const originalQuery = searchQuery;
|
|
27572
27572
|
searchQuery = autoQuoteSearchTerms(searchQuery);
|
|
@@ -27575,18 +27575,19 @@ var init_vercel = __esm({
|
|
|
27575
27575
|
}
|
|
27576
27576
|
}
|
|
27577
27577
|
const effectiveMaxTokens = paramMaxTokens || maxTokens;
|
|
27578
|
+
const effectiveSearchCwd = workingDirectory || options.cwd || ".";
|
|
27578
27579
|
let searchPaths;
|
|
27579
27580
|
if (path9) {
|
|
27580
|
-
searchPaths = parseAndResolvePaths(path9,
|
|
27581
|
+
searchPaths = parseAndResolvePaths(path9, effectiveSearchCwd);
|
|
27581
27582
|
}
|
|
27582
27583
|
if (!searchPaths || searchPaths.length === 0) {
|
|
27583
|
-
searchPaths = [
|
|
27584
|
+
searchPaths = [effectiveSearchCwd];
|
|
27584
27585
|
}
|
|
27585
27586
|
const searchPath = searchPaths.join(" ");
|
|
27586
27587
|
const searchOptions = {
|
|
27587
27588
|
query: searchQuery,
|
|
27588
27589
|
path: searchPath,
|
|
27589
|
-
cwd:
|
|
27590
|
+
cwd: effectiveSearchCwd,
|
|
27590
27591
|
// Working directory for resolving relative paths
|
|
27591
27592
|
allowTests: allow_tests ?? true,
|
|
27592
27593
|
exact,
|
|
@@ -27637,7 +27638,7 @@ var init_vercel = __esm({
|
|
|
27637
27638
|
try {
|
|
27638
27639
|
const result = maybeAnnotate(await runRawSearch());
|
|
27639
27640
|
if (options.fileTracker && typeof result === "string") {
|
|
27640
|
-
options.fileTracker.trackFilesFromOutput(result,
|
|
27641
|
+
options.fileTracker.trackFilesFromOutput(result, effectiveSearchCwd).catch(() => {
|
|
27641
27642
|
});
|
|
27642
27643
|
}
|
|
27643
27644
|
return result;
|
|
@@ -27690,7 +27691,7 @@ var init_vercel = __esm({
|
|
|
27690
27691
|
}
|
|
27691
27692
|
const fallbackResult = maybeAnnotate(await runRawSearch());
|
|
27692
27693
|
if (options.fileTracker && typeof fallbackResult === "string") {
|
|
27693
|
-
options.fileTracker.trackFilesFromOutput(fallbackResult,
|
|
27694
|
+
options.fileTracker.trackFilesFromOutput(fallbackResult, effectiveSearchCwd).catch(() => {
|
|
27694
27695
|
});
|
|
27695
27696
|
}
|
|
27696
27697
|
return fallbackResult;
|
|
@@ -27753,7 +27754,7 @@ var init_vercel = __esm({
|
|
|
27753
27754
|
try {
|
|
27754
27755
|
const fallbackResult2 = maybeAnnotate(await runRawSearch());
|
|
27755
27756
|
if (options.fileTracker && typeof fallbackResult2 === "string") {
|
|
27756
|
-
options.fileTracker.trackFilesFromOutput(fallbackResult2,
|
|
27757
|
+
options.fileTracker.trackFilesFromOutput(fallbackResult2, effectiveSearchCwd).catch(() => {
|
|
27757
27758
|
});
|
|
27758
27759
|
}
|
|
27759
27760
|
return fallbackResult2;
|
|
@@ -27807,9 +27808,9 @@ var init_vercel = __esm({
|
|
|
27807
27808
|
name: "extract",
|
|
27808
27809
|
description: extractDescription,
|
|
27809
27810
|
inputSchema: extractSchema,
|
|
27810
|
-
execute: async ({ targets, input_content, line, end_line, allow_tests, context_lines, format }) => {
|
|
27811
|
+
execute: async ({ targets, input_content, line, end_line, allow_tests, context_lines, format, workingDirectory }) => {
|
|
27811
27812
|
try {
|
|
27812
|
-
const effectiveCwd = options.cwd || ".";
|
|
27813
|
+
const effectiveCwd = workingDirectory || options.cwd || ".";
|
|
27813
27814
|
if (debug) {
|
|
27814
27815
|
if (targets) {
|
|
27815
27816
|
console.error(`Executing extract with targets: "${targets}", cwd: "${effectiveCwd}", context lines: ${context_lines || 10}`);
|
|
@@ -48638,6 +48639,10 @@ var init_file_lister = __esm({
|
|
|
48638
48639
|
});
|
|
48639
48640
|
|
|
48640
48641
|
// src/tools/fileTracker.js
|
|
48642
|
+
function normalizePath(filePath) {
|
|
48643
|
+
if (!filePath) return filePath;
|
|
48644
|
+
return (0, import_path10.resolve)(filePath);
|
|
48645
|
+
}
|
|
48641
48646
|
function computeContentHash(content) {
|
|
48642
48647
|
const normalized = (content || "").split("\n").map((l) => l.trimEnd()).join("\n");
|
|
48643
48648
|
return (0, import_crypto3.createHash)("sha256").update(normalized).digest("hex").slice(0, 16);
|
|
@@ -48700,10 +48705,11 @@ var init_fileTracker = __esm({
|
|
|
48700
48705
|
* @param {string} resolvedPath - Absolute path to the file
|
|
48701
48706
|
*/
|
|
48702
48707
|
markFileSeen(resolvedPath) {
|
|
48703
|
-
|
|
48704
|
-
this.
|
|
48708
|
+
const normalized = normalizePath(resolvedPath);
|
|
48709
|
+
this._seenFiles.add(normalized);
|
|
48710
|
+
this._textEditCounts.set(normalized, 0);
|
|
48705
48711
|
if (this.debug) {
|
|
48706
|
-
console.error(`[FileTracker] Marked as seen: ${
|
|
48712
|
+
console.error(`[FileTracker] Marked as seen: ${normalized}`);
|
|
48707
48713
|
}
|
|
48708
48714
|
}
|
|
48709
48715
|
/**
|
|
@@ -48712,7 +48718,7 @@ var init_fileTracker = __esm({
|
|
|
48712
48718
|
* @returns {boolean}
|
|
48713
48719
|
*/
|
|
48714
48720
|
isFileSeen(resolvedPath) {
|
|
48715
|
-
return this._seenFiles.has(resolvedPath);
|
|
48721
|
+
return this._seenFiles.has(normalizePath(resolvedPath));
|
|
48716
48722
|
}
|
|
48717
48723
|
/**
|
|
48718
48724
|
* Store a content hash for a symbol in a file.
|
|
@@ -48724,7 +48730,7 @@ var init_fileTracker = __esm({
|
|
|
48724
48730
|
* @param {string} [source='extract'] - How the content was obtained
|
|
48725
48731
|
*/
|
|
48726
48732
|
trackSymbolContent(resolvedPath, symbolName, code, startLine, endLine, source = "extract") {
|
|
48727
|
-
const key = `${resolvedPath}#${symbolName}`;
|
|
48733
|
+
const key = `${normalizePath(resolvedPath)}#${symbolName}`;
|
|
48728
48734
|
const contentHash = computeContentHash(code);
|
|
48729
48735
|
this._contentRecords.set(key, {
|
|
48730
48736
|
contentHash,
|
|
@@ -48745,7 +48751,7 @@ var init_fileTracker = __esm({
|
|
|
48745
48751
|
* @returns {Object|null} The stored record or null
|
|
48746
48752
|
*/
|
|
48747
48753
|
getSymbolRecord(resolvedPath, symbolName) {
|
|
48748
|
-
return this._contentRecords.get(`${resolvedPath}#${symbolName}`) || null;
|
|
48754
|
+
return this._contentRecords.get(`${normalizePath(resolvedPath)}#${symbolName}`) || null;
|
|
48749
48755
|
}
|
|
48750
48756
|
/**
|
|
48751
48757
|
* Check if a symbol's current content matches what was stored.
|
|
@@ -48755,7 +48761,7 @@ var init_fileTracker = __esm({
|
|
|
48755
48761
|
* @returns {{ok: boolean, reason?: string, message?: string}}
|
|
48756
48762
|
*/
|
|
48757
48763
|
checkSymbolContent(resolvedPath, symbolName, currentCode) {
|
|
48758
|
-
const key = `${resolvedPath}#${symbolName}`;
|
|
48764
|
+
const key = `${normalizePath(resolvedPath)}#${symbolName}`;
|
|
48759
48765
|
const record2 = this._contentRecords.get(key);
|
|
48760
48766
|
if (!record2) {
|
|
48761
48767
|
return { ok: true };
|
|
@@ -48832,7 +48838,7 @@ var init_fileTracker = __esm({
|
|
|
48832
48838
|
* @returns {{ok: boolean, reason?: string, message?: string}}
|
|
48833
48839
|
*/
|
|
48834
48840
|
checkBeforeEdit(resolvedPath) {
|
|
48835
|
-
if (!this._seenFiles.has(resolvedPath)) {
|
|
48841
|
+
if (!this._seenFiles.has(normalizePath(resolvedPath))) {
|
|
48836
48842
|
return {
|
|
48837
48843
|
ok: false,
|
|
48838
48844
|
reason: "untracked",
|
|
@@ -48847,8 +48853,9 @@ var init_fileTracker = __esm({
|
|
|
48847
48853
|
* @param {string} resolvedPath - Absolute path to the file
|
|
48848
48854
|
*/
|
|
48849
48855
|
async trackFileAfterWrite(resolvedPath) {
|
|
48850
|
-
|
|
48851
|
-
this.
|
|
48856
|
+
const normalized = normalizePath(resolvedPath);
|
|
48857
|
+
this._seenFiles.add(normalized);
|
|
48858
|
+
this.invalidateFileRecords(normalized);
|
|
48852
48859
|
}
|
|
48853
48860
|
/**
|
|
48854
48861
|
* Record a text-mode edit (old_string/new_string) to a file.
|
|
@@ -48856,10 +48863,11 @@ var init_fileTracker = __esm({
|
|
|
48856
48863
|
* @param {string} resolvedPath - Absolute path to the file
|
|
48857
48864
|
*/
|
|
48858
48865
|
recordTextEdit(resolvedPath) {
|
|
48859
|
-
const
|
|
48860
|
-
this._textEditCounts.
|
|
48866
|
+
const normalized = normalizePath(resolvedPath);
|
|
48867
|
+
const count = (this._textEditCounts.get(normalized) || 0) + 1;
|
|
48868
|
+
this._textEditCounts.set(normalized, count);
|
|
48861
48869
|
if (this.debug) {
|
|
48862
|
-
console.error(`[FileTracker] Text edit #${count} for ${
|
|
48870
|
+
console.error(`[FileTracker] Text edit #${count} for ${normalized}`);
|
|
48863
48871
|
}
|
|
48864
48872
|
}
|
|
48865
48873
|
/**
|
|
@@ -48868,7 +48876,7 @@ var init_fileTracker = __esm({
|
|
|
48868
48876
|
* @returns {{ok: boolean, editCount?: number, message?: string}}
|
|
48869
48877
|
*/
|
|
48870
48878
|
checkTextEditStaleness(resolvedPath) {
|
|
48871
|
-
const count = this._textEditCounts.get(resolvedPath) || 0;
|
|
48879
|
+
const count = this._textEditCounts.get(normalizePath(resolvedPath)) || 0;
|
|
48872
48880
|
if (count >= this.maxConsecutiveTextEdits) {
|
|
48873
48881
|
return {
|
|
48874
48882
|
ok: false,
|
|
@@ -48897,7 +48905,7 @@ var init_fileTracker = __esm({
|
|
|
48897
48905
|
* @param {string} resolvedPath - Absolute path to the file
|
|
48898
48906
|
*/
|
|
48899
48907
|
invalidateFileRecords(resolvedPath) {
|
|
48900
|
-
const prefix = resolvedPath + "#";
|
|
48908
|
+
const prefix = normalizePath(resolvedPath) + "#";
|
|
48901
48909
|
for (const key of this._contentRecords.keys()) {
|
|
48902
48910
|
if (key.startsWith(prefix)) {
|
|
48903
48911
|
this._contentRecords.delete(key);
|
|
@@ -48913,7 +48921,7 @@ var init_fileTracker = __esm({
|
|
|
48913
48921
|
* @returns {boolean}
|
|
48914
48922
|
*/
|
|
48915
48923
|
isTracked(resolvedPath) {
|
|
48916
|
-
return this.isFileSeen(resolvedPath);
|
|
48924
|
+
return this.isFileSeen(normalizePath(resolvedPath));
|
|
48917
48925
|
}
|
|
48918
48926
|
/**
|
|
48919
48927
|
* Clear all tracking state.
|
|
@@ -69026,7 +69034,7 @@ var init_graph_builder = __esm({
|
|
|
69026
69034
|
applyLinkStyles() {
|
|
69027
69035
|
if (!this.pendingLinkStyles.length || !this.edges.length)
|
|
69028
69036
|
return;
|
|
69029
|
-
const
|
|
69037
|
+
const normalize4 = (s) => {
|
|
69030
69038
|
const out = {};
|
|
69031
69039
|
for (const [kRaw, vRaw] of Object.entries(s)) {
|
|
69032
69040
|
const k = kRaw.trim().toLowerCase();
|
|
@@ -69047,7 +69055,7 @@ var init_graph_builder = __esm({
|
|
|
69047
69055
|
return out;
|
|
69048
69056
|
};
|
|
69049
69057
|
for (const cmd of this.pendingLinkStyles) {
|
|
69050
|
-
const style =
|
|
69058
|
+
const style = normalize4(cmd.props);
|
|
69051
69059
|
for (const idx of cmd.indices) {
|
|
69052
69060
|
if (idx >= 0 && idx < this.edges.length) {
|
|
69053
69061
|
const e = this.edges[idx];
|
|
@@ -76314,7 +76322,7 @@ var require_layout = __commonJS({
|
|
|
76314
76322
|
"use strict";
|
|
76315
76323
|
var _ = require_lodash2();
|
|
76316
76324
|
var acyclic = require_acyclic();
|
|
76317
|
-
var
|
|
76325
|
+
var normalize4 = require_normalize();
|
|
76318
76326
|
var rank = require_rank();
|
|
76319
76327
|
var normalizeRanks = require_util().normalizeRanks;
|
|
76320
76328
|
var parentDummyChains = require_parent_dummy_chains();
|
|
@@ -76376,7 +76384,7 @@ var require_layout = __commonJS({
|
|
|
76376
76384
|
removeEdgeLabelProxies(g);
|
|
76377
76385
|
});
|
|
76378
76386
|
time3(" normalize.run", function() {
|
|
76379
|
-
|
|
76387
|
+
normalize4.run(g);
|
|
76380
76388
|
});
|
|
76381
76389
|
time3(" parentDummyChains", function() {
|
|
76382
76390
|
parentDummyChains(g);
|
|
@@ -76403,7 +76411,7 @@ var require_layout = __commonJS({
|
|
|
76403
76411
|
removeBorderNodes(g);
|
|
76404
76412
|
});
|
|
76405
76413
|
time3(" normalize.undo", function() {
|
|
76406
|
-
|
|
76414
|
+
normalize4.undo(g);
|
|
76407
76415
|
});
|
|
76408
76416
|
time3(" fixupEdgeLabelCoords", function() {
|
|
76409
76417
|
fixupEdgeLabelCoords(g);
|
|
@@ -82774,8 +82782,8 @@ var require_resolve = __commonJS({
|
|
|
82774
82782
|
}
|
|
82775
82783
|
return count;
|
|
82776
82784
|
}
|
|
82777
|
-
function getFullPath(resolver, id = "",
|
|
82778
|
-
if (
|
|
82785
|
+
function getFullPath(resolver, id = "", normalize4) {
|
|
82786
|
+
if (normalize4 !== false)
|
|
82779
82787
|
id = normalizeId(id);
|
|
82780
82788
|
const p = resolver.parse(id);
|
|
82781
82789
|
return _getFullPath(resolver, p);
|
|
@@ -84115,7 +84123,7 @@ var require_fast_uri = __commonJS({
|
|
|
84115
84123
|
"use strict";
|
|
84116
84124
|
var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizeComponentEncoding, isIPv4, nonSimpleDomain } = require_utils2();
|
|
84117
84125
|
var { SCHEMES, getSchemeHandler } = require_schemes();
|
|
84118
|
-
function
|
|
84126
|
+
function normalize4(uri, options) {
|
|
84119
84127
|
if (typeof uri === "string") {
|
|
84120
84128
|
uri = /** @type {T} */
|
|
84121
84129
|
serialize(parse11(uri, options), options);
|
|
@@ -84351,7 +84359,7 @@ var require_fast_uri = __commonJS({
|
|
|
84351
84359
|
}
|
|
84352
84360
|
var fastUri = {
|
|
84353
84361
|
SCHEMES,
|
|
84354
|
-
normalize:
|
|
84362
|
+
normalize: normalize4,
|
|
84355
84363
|
resolve: resolve9,
|
|
84356
84364
|
resolveComponent,
|
|
84357
84365
|
equal,
|
|
@@ -88558,9 +88566,9 @@ If the solution is clear, you can jump to implementation right away. If not, ask
|
|
|
88558
88566
|
- Do not add code comments unless the logic is genuinely complex and non-obvious.
|
|
88559
88567
|
|
|
88560
88568
|
# Before Implementation
|
|
88561
|
-
-
|
|
88562
|
-
-
|
|
88563
|
-
-
|
|
88569
|
+
- Read tests first \u2014 find existing test files for the module you're changing. They reveal expected behavior, edge cases, and the project's testing patterns.
|
|
88570
|
+
- Read neighboring files \u2014 understand naming conventions, error handling patterns, import style, and existing utilities before creating new ones.
|
|
88571
|
+
- Trace the call chain \u2014 follow how the code you're changing is called and what depends on it. Check interfaces, types, and consumers.
|
|
88564
88572
|
- Focus on backward compatibility
|
|
88565
88573
|
- Consider scalability, maintainability, and extensibility in your analysis
|
|
88566
88574
|
|
|
@@ -88585,6 +88593,20 @@ Before building or testing, determine the project's toolchain:
|
|
|
88585
88593
|
- Read README for build/test instructions if the above are unclear
|
|
88586
88594
|
- Common patterns: \`make build\`/\`make test\`, \`npm run build\`/\`npm test\`, \`cargo build\`/\`cargo test\`, \`go build ./...\`/\`go test ./...\`, \`python -m pytest\`
|
|
88587
88595
|
|
|
88596
|
+
# File Editing Rules
|
|
88597
|
+
You have access to the \`edit\`, \`create\`, and \`multi_edit\` tools for modifying files. You MUST use these tools for ALL code changes. They are purpose-built, atomic, and safe.
|
|
88598
|
+
|
|
88599
|
+
DO NOT use sed, awk, echo/cat redirection, or heredocs to modify source code. These commands cause real damage in practice: truncated lines, duplicate code blocks, broken syntax. Every bad edit wastes iterations on fix-up commits.
|
|
88600
|
+
|
|
88601
|
+
Use the right tool:
|
|
88602
|
+
1. To MODIFY existing code \u2192 \`edit\` tool (old_string \u2192 new_string, or start_line/end_line)
|
|
88603
|
+
2. To CREATE a new file \u2192 \`create\` tool
|
|
88604
|
+
3. To CHANGE multiple files at once \u2192 \`multi_edit\` tool
|
|
88605
|
+
4. To READ code \u2192 \`extract\` or \`search\` tools
|
|
88606
|
+
5. If \`edit\` fails with "file has not been read yet" \u2192 use \`extract\` with the EXACT same file path you will pass to \`edit\`. Relative vs absolute path mismatch causes this error. Use the same path format consistently. If it still fails, use bash \`cat\` to read the file, then use \`create\` to write the entire modified file. Do NOT fall back to sed.
|
|
88607
|
+
|
|
88608
|
+
Bash is fine for: formatters (gofmt, prettier, black), build/test/lint commands, git operations, and read-only file inspection (cat, head, tail). sed/awk should ONLY be used for trivial non-code tasks (e.g., config file tweaks) where the replacement is a simple literal string swap.
|
|
88609
|
+
|
|
88588
88610
|
# During Implementation
|
|
88589
88611
|
- Always create a new branch before making changes to the codebase.
|
|
88590
88612
|
- Fix problems at the root cause, not with surface-level patches. Prefer general solutions over special cases.
|
|
@@ -88611,6 +88633,22 @@ Before committing or creating a PR, run through this checklist:
|
|
|
88611
88633
|
|
|
88612
88634
|
Do NOT skip verification. Do NOT proceed to PR creation with a broken build or failing tests.
|
|
88613
88635
|
|
|
88636
|
+
# Output Integrity
|
|
88637
|
+
Your final output MUST accurately reflect what ACTUALLY happened. Do NOT fabricate, hallucinate, or report aspirational results.
|
|
88638
|
+
|
|
88639
|
+
- Only report PR URLs you actually created or updated with \`gh pr create\` or \`git push\`. If you checked out an existing PR but did NOT push changes to it, do NOT claim you updated it.
|
|
88640
|
+
- Describe what you ACTUALLY DID, not what you planned or intended to do. If you ran out of iterations, say so. If tests failed, say so.
|
|
88641
|
+
- Only list files you actually modified AND committed.
|
|
88642
|
+
- If you could not complete the task \u2014 ran out of iterations, tests failed, build broken, push rejected \u2014 report the real reason honestly.
|
|
88643
|
+
|
|
88644
|
+
NEVER claim success when:
|
|
88645
|
+
- You did not run \`git push\` successfully
|
|
88646
|
+
- Tests failed and you did not fix them
|
|
88647
|
+
- You hit the iteration limit before completing the work
|
|
88648
|
+
- You only analyzed/investigated but did not implement changes
|
|
88649
|
+
|
|
88650
|
+
A false success report is WORSE than an honest failure \u2014 it misleads the user into thinking work is done when it is not.
|
|
88651
|
+
|
|
88614
88652
|
# GitHub Integration
|
|
88615
88653
|
- Use the \`gh\` CLI for all GitHub operations: issues, pull requests, checks, releases.
|
|
88616
88654
|
- To view issues or PRs: \`gh issue view <number>\`, \`gh pr view <number>\`.
|
package/cjs/index.cjs
CHANGED
|
@@ -27260,6 +27260,10 @@ var init_symbolEdit = __esm({
|
|
|
27260
27260
|
});
|
|
27261
27261
|
|
|
27262
27262
|
// src/tools/fileTracker.js
|
|
27263
|
+
function normalizePath(filePath) {
|
|
27264
|
+
if (!filePath) return filePath;
|
|
27265
|
+
return (0, import_path6.resolve)(filePath);
|
|
27266
|
+
}
|
|
27263
27267
|
function computeContentHash(content) {
|
|
27264
27268
|
const normalized = (content || "").split("\n").map((l) => l.trimEnd()).join("\n");
|
|
27265
27269
|
return (0, import_crypto2.createHash)("sha256").update(normalized).digest("hex").slice(0, 16);
|
|
@@ -27322,10 +27326,11 @@ var init_fileTracker = __esm({
|
|
|
27322
27326
|
* @param {string} resolvedPath - Absolute path to the file
|
|
27323
27327
|
*/
|
|
27324
27328
|
markFileSeen(resolvedPath) {
|
|
27325
|
-
|
|
27326
|
-
this.
|
|
27329
|
+
const normalized = normalizePath(resolvedPath);
|
|
27330
|
+
this._seenFiles.add(normalized);
|
|
27331
|
+
this._textEditCounts.set(normalized, 0);
|
|
27327
27332
|
if (this.debug) {
|
|
27328
|
-
console.error(`[FileTracker] Marked as seen: ${
|
|
27333
|
+
console.error(`[FileTracker] Marked as seen: ${normalized}`);
|
|
27329
27334
|
}
|
|
27330
27335
|
}
|
|
27331
27336
|
/**
|
|
@@ -27334,7 +27339,7 @@ var init_fileTracker = __esm({
|
|
|
27334
27339
|
* @returns {boolean}
|
|
27335
27340
|
*/
|
|
27336
27341
|
isFileSeen(resolvedPath) {
|
|
27337
|
-
return this._seenFiles.has(resolvedPath);
|
|
27342
|
+
return this._seenFiles.has(normalizePath(resolvedPath));
|
|
27338
27343
|
}
|
|
27339
27344
|
/**
|
|
27340
27345
|
* Store a content hash for a symbol in a file.
|
|
@@ -27346,7 +27351,7 @@ var init_fileTracker = __esm({
|
|
|
27346
27351
|
* @param {string} [source='extract'] - How the content was obtained
|
|
27347
27352
|
*/
|
|
27348
27353
|
trackSymbolContent(resolvedPath, symbolName, code, startLine, endLine, source = "extract") {
|
|
27349
|
-
const key = `${resolvedPath}#${symbolName}`;
|
|
27354
|
+
const key = `${normalizePath(resolvedPath)}#${symbolName}`;
|
|
27350
27355
|
const contentHash = computeContentHash(code);
|
|
27351
27356
|
this._contentRecords.set(key, {
|
|
27352
27357
|
contentHash,
|
|
@@ -27367,7 +27372,7 @@ var init_fileTracker = __esm({
|
|
|
27367
27372
|
* @returns {Object|null} The stored record or null
|
|
27368
27373
|
*/
|
|
27369
27374
|
getSymbolRecord(resolvedPath, symbolName) {
|
|
27370
|
-
return this._contentRecords.get(`${resolvedPath}#${symbolName}`) || null;
|
|
27375
|
+
return this._contentRecords.get(`${normalizePath(resolvedPath)}#${symbolName}`) || null;
|
|
27371
27376
|
}
|
|
27372
27377
|
/**
|
|
27373
27378
|
* Check if a symbol's current content matches what was stored.
|
|
@@ -27377,7 +27382,7 @@ var init_fileTracker = __esm({
|
|
|
27377
27382
|
* @returns {{ok: boolean, reason?: string, message?: string}}
|
|
27378
27383
|
*/
|
|
27379
27384
|
checkSymbolContent(resolvedPath, symbolName, currentCode) {
|
|
27380
|
-
const key = `${resolvedPath}#${symbolName}`;
|
|
27385
|
+
const key = `${normalizePath(resolvedPath)}#${symbolName}`;
|
|
27381
27386
|
const record2 = this._contentRecords.get(key);
|
|
27382
27387
|
if (!record2) {
|
|
27383
27388
|
return { ok: true };
|
|
@@ -27454,7 +27459,7 @@ var init_fileTracker = __esm({
|
|
|
27454
27459
|
* @returns {{ok: boolean, reason?: string, message?: string}}
|
|
27455
27460
|
*/
|
|
27456
27461
|
checkBeforeEdit(resolvedPath) {
|
|
27457
|
-
if (!this._seenFiles.has(resolvedPath)) {
|
|
27462
|
+
if (!this._seenFiles.has(normalizePath(resolvedPath))) {
|
|
27458
27463
|
return {
|
|
27459
27464
|
ok: false,
|
|
27460
27465
|
reason: "untracked",
|
|
@@ -27469,8 +27474,9 @@ var init_fileTracker = __esm({
|
|
|
27469
27474
|
* @param {string} resolvedPath - Absolute path to the file
|
|
27470
27475
|
*/
|
|
27471
27476
|
async trackFileAfterWrite(resolvedPath) {
|
|
27472
|
-
|
|
27473
|
-
this.
|
|
27477
|
+
const normalized = normalizePath(resolvedPath);
|
|
27478
|
+
this._seenFiles.add(normalized);
|
|
27479
|
+
this.invalidateFileRecords(normalized);
|
|
27474
27480
|
}
|
|
27475
27481
|
/**
|
|
27476
27482
|
* Record a text-mode edit (old_string/new_string) to a file.
|
|
@@ -27478,10 +27484,11 @@ var init_fileTracker = __esm({
|
|
|
27478
27484
|
* @param {string} resolvedPath - Absolute path to the file
|
|
27479
27485
|
*/
|
|
27480
27486
|
recordTextEdit(resolvedPath) {
|
|
27481
|
-
const
|
|
27482
|
-
this._textEditCounts.
|
|
27487
|
+
const normalized = normalizePath(resolvedPath);
|
|
27488
|
+
const count = (this._textEditCounts.get(normalized) || 0) + 1;
|
|
27489
|
+
this._textEditCounts.set(normalized, count);
|
|
27483
27490
|
if (this.debug) {
|
|
27484
|
-
console.error(`[FileTracker] Text edit #${count} for ${
|
|
27491
|
+
console.error(`[FileTracker] Text edit #${count} for ${normalized}`);
|
|
27485
27492
|
}
|
|
27486
27493
|
}
|
|
27487
27494
|
/**
|
|
@@ -27490,7 +27497,7 @@ var init_fileTracker = __esm({
|
|
|
27490
27497
|
* @returns {{ok: boolean, editCount?: number, message?: string}}
|
|
27491
27498
|
*/
|
|
27492
27499
|
checkTextEditStaleness(resolvedPath) {
|
|
27493
|
-
const count = this._textEditCounts.get(resolvedPath) || 0;
|
|
27500
|
+
const count = this._textEditCounts.get(normalizePath(resolvedPath)) || 0;
|
|
27494
27501
|
if (count >= this.maxConsecutiveTextEdits) {
|
|
27495
27502
|
return {
|
|
27496
27503
|
ok: false,
|
|
@@ -27519,7 +27526,7 @@ var init_fileTracker = __esm({
|
|
|
27519
27526
|
* @param {string} resolvedPath - Absolute path to the file
|
|
27520
27527
|
*/
|
|
27521
27528
|
invalidateFileRecords(resolvedPath) {
|
|
27522
|
-
const prefix = resolvedPath + "#";
|
|
27529
|
+
const prefix = normalizePath(resolvedPath) + "#";
|
|
27523
27530
|
for (const key of this._contentRecords.keys()) {
|
|
27524
27531
|
if (key.startsWith(prefix)) {
|
|
27525
27532
|
this._contentRecords.delete(key);
|
|
@@ -27535,7 +27542,7 @@ var init_fileTracker = __esm({
|
|
|
27535
27542
|
* @returns {boolean}
|
|
27536
27543
|
*/
|
|
27537
27544
|
isTracked(resolvedPath) {
|
|
27538
|
-
return this.isFileSeen(resolvedPath);
|
|
27545
|
+
return this.isFileSeen(normalizePath(resolvedPath));
|
|
27539
27546
|
}
|
|
27540
27547
|
/**
|
|
27541
27548
|
* Clear all tracking state.
|
|
@@ -31588,7 +31595,7 @@ var init_esm3 = __esm({
|
|
|
31588
31595
|
});
|
|
31589
31596
|
|
|
31590
31597
|
// node_modules/path-scurry/dist/esm/index.js
|
|
31591
|
-
var import_node_path, import_node_url, import_fs4, actualFS, import_promises, realpathSync2, defaultFS, fsFromOption, uncDriveRegexp, uncToDrive, eitherSep, UNKNOWN, IFIFO, IFCHR, IFDIR, IFBLK, IFREG, IFLNK, IFSOCK, IFMT, IFMT_UNKNOWN, READDIR_CALLED, LSTAT_CALLED, ENOTDIR, ENOENT, ENOREADLINK, ENOREALPATH, ENOCHILD, TYPEMASK, entToType, normalizeCache,
|
|
31598
|
+
var import_node_path, import_node_url, import_fs4, actualFS, import_promises, realpathSync2, defaultFS, fsFromOption, uncDriveRegexp, uncToDrive, eitherSep, UNKNOWN, IFIFO, IFCHR, IFDIR, IFBLK, IFREG, IFLNK, IFSOCK, IFMT, IFMT_UNKNOWN, READDIR_CALLED, LSTAT_CALLED, ENOTDIR, ENOENT, ENOREADLINK, ENOREALPATH, ENOCHILD, TYPEMASK, entToType, normalizeCache, normalize2, normalizeNocaseCache, normalizeNocase, ResolveCache, ChildrenCache, setAsCwd, PathBase, PathWin32, PathPosix, PathScurryBase, PathScurryWin32, PathScurryPosix, PathScurryDarwin, Path, PathScurry;
|
|
31592
31599
|
var init_esm4 = __esm({
|
|
31593
31600
|
"node_modules/path-scurry/dist/esm/index.js"() {
|
|
31594
31601
|
init_esm2();
|
|
@@ -31643,7 +31650,7 @@ var init_esm4 = __esm({
|
|
|
31643
31650
|
TYPEMASK = 1023;
|
|
31644
31651
|
entToType = (s) => s.isFile() ? IFREG : s.isDirectory() ? IFDIR : s.isSymbolicLink() ? IFLNK : s.isCharacterDevice() ? IFCHR : s.isBlockDevice() ? IFBLK : s.isSocket() ? IFSOCK : s.isFIFO() ? IFIFO : UNKNOWN;
|
|
31645
31652
|
normalizeCache = /* @__PURE__ */ new Map();
|
|
31646
|
-
|
|
31653
|
+
normalize2 = (s) => {
|
|
31647
31654
|
const c = normalizeCache.get(s);
|
|
31648
31655
|
if (c)
|
|
31649
31656
|
return c;
|
|
@@ -31656,7 +31663,7 @@ var init_esm4 = __esm({
|
|
|
31656
31663
|
const c = normalizeNocaseCache.get(s);
|
|
31657
31664
|
if (c)
|
|
31658
31665
|
return c;
|
|
31659
|
-
const n =
|
|
31666
|
+
const n = normalize2(s.toLowerCase());
|
|
31660
31667
|
normalizeNocaseCache.set(s, n);
|
|
31661
31668
|
return n;
|
|
31662
31669
|
};
|
|
@@ -31823,7 +31830,7 @@ var init_esm4 = __esm({
|
|
|
31823
31830
|
*/
|
|
31824
31831
|
constructor(name15, type = UNKNOWN, root2, roots, nocase, children, opts) {
|
|
31825
31832
|
this.name = name15;
|
|
31826
|
-
this.#matchName = nocase ? normalizeNocase(name15) :
|
|
31833
|
+
this.#matchName = nocase ? normalizeNocase(name15) : normalize2(name15);
|
|
31827
31834
|
this.#type = type & TYPEMASK;
|
|
31828
31835
|
this.nocase = nocase;
|
|
31829
31836
|
this.roots = roots;
|
|
@@ -31916,7 +31923,7 @@ var init_esm4 = __esm({
|
|
|
31916
31923
|
return this.parent || this;
|
|
31917
31924
|
}
|
|
31918
31925
|
const children = this.children();
|
|
31919
|
-
const name15 = this.nocase ? normalizeNocase(pathPart) :
|
|
31926
|
+
const name15 = this.nocase ? normalizeNocase(pathPart) : normalize2(pathPart);
|
|
31920
31927
|
for (const p of children) {
|
|
31921
31928
|
if (p.#matchName === name15) {
|
|
31922
31929
|
return p;
|
|
@@ -32161,7 +32168,7 @@ var init_esm4 = __esm({
|
|
|
32161
32168
|
* directly.
|
|
32162
32169
|
*/
|
|
32163
32170
|
isNamed(n) {
|
|
32164
|
-
return !this.nocase ? this.#matchName ===
|
|
32171
|
+
return !this.nocase ? this.#matchName === normalize2(n) : this.#matchName === normalizeNocase(n);
|
|
32165
32172
|
}
|
|
32166
32173
|
/**
|
|
32167
32174
|
* Return the Path object corresponding to the target of a symbolic link.
|
|
@@ -32300,7 +32307,7 @@ var init_esm4 = __esm({
|
|
|
32300
32307
|
#readdirMaybePromoteChild(e, c) {
|
|
32301
32308
|
for (let p = c.provisional; p < c.length; p++) {
|
|
32302
32309
|
const pchild = c[p];
|
|
32303
|
-
const name15 = this.nocase ? normalizeNocase(e.name) :
|
|
32310
|
+
const name15 = this.nocase ? normalizeNocase(e.name) : normalize2(e.name);
|
|
32304
32311
|
if (name15 !== pchild.#matchName) {
|
|
32305
32312
|
continue;
|
|
32306
32313
|
}
|
|
@@ -53617,7 +53624,7 @@ var init_graph_builder = __esm({
|
|
|
53617
53624
|
applyLinkStyles() {
|
|
53618
53625
|
if (!this.pendingLinkStyles.length || !this.edges.length)
|
|
53619
53626
|
return;
|
|
53620
|
-
const
|
|
53627
|
+
const normalize4 = (s) => {
|
|
53621
53628
|
const out = {};
|
|
53622
53629
|
for (const [kRaw, vRaw] of Object.entries(s)) {
|
|
53623
53630
|
const k = kRaw.trim().toLowerCase();
|
|
@@ -53638,7 +53645,7 @@ var init_graph_builder = __esm({
|
|
|
53638
53645
|
return out;
|
|
53639
53646
|
};
|
|
53640
53647
|
for (const cmd of this.pendingLinkStyles) {
|
|
53641
|
-
const style =
|
|
53648
|
+
const style = normalize4(cmd.props);
|
|
53642
53649
|
for (const idx of cmd.indices) {
|
|
53643
53650
|
if (idx >= 0 && idx < this.edges.length) {
|
|
53644
53651
|
const e = this.edges[idx];
|
|
@@ -60905,7 +60912,7 @@ var require_layout = __commonJS({
|
|
|
60905
60912
|
"use strict";
|
|
60906
60913
|
var _ = require_lodash2();
|
|
60907
60914
|
var acyclic = require_acyclic();
|
|
60908
|
-
var
|
|
60915
|
+
var normalize4 = require_normalize();
|
|
60909
60916
|
var rank = require_rank();
|
|
60910
60917
|
var normalizeRanks = require_util().normalizeRanks;
|
|
60911
60918
|
var parentDummyChains = require_parent_dummy_chains();
|
|
@@ -60967,7 +60974,7 @@ var require_layout = __commonJS({
|
|
|
60967
60974
|
removeEdgeLabelProxies(g);
|
|
60968
60975
|
});
|
|
60969
60976
|
time3(" normalize.run", function() {
|
|
60970
|
-
|
|
60977
|
+
normalize4.run(g);
|
|
60971
60978
|
});
|
|
60972
60979
|
time3(" parentDummyChains", function() {
|
|
60973
60980
|
parentDummyChains(g);
|
|
@@ -60994,7 +61001,7 @@ var require_layout = __commonJS({
|
|
|
60994
61001
|
removeBorderNodes(g);
|
|
60995
61002
|
});
|
|
60996
61003
|
time3(" normalize.undo", function() {
|
|
60997
|
-
|
|
61004
|
+
normalize4.undo(g);
|
|
60998
61005
|
});
|
|
60999
61006
|
time3(" fixupEdgeLabelCoords", function() {
|
|
61000
61007
|
fixupEdgeLabelCoords(g);
|
|
@@ -67365,8 +67372,8 @@ var require_resolve = __commonJS({
|
|
|
67365
67372
|
}
|
|
67366
67373
|
return count;
|
|
67367
67374
|
}
|
|
67368
|
-
function getFullPath(resolver, id = "",
|
|
67369
|
-
if (
|
|
67375
|
+
function getFullPath(resolver, id = "", normalize4) {
|
|
67376
|
+
if (normalize4 !== false)
|
|
67370
67377
|
id = normalizeId(id);
|
|
67371
67378
|
const p = resolver.parse(id);
|
|
67372
67379
|
return _getFullPath(resolver, p);
|
|
@@ -68706,7 +68713,7 @@ var require_fast_uri = __commonJS({
|
|
|
68706
68713
|
"use strict";
|
|
68707
68714
|
var { normalizeIPv6, removeDotSegments, recomposeAuthority, normalizeComponentEncoding, isIPv4, nonSimpleDomain } = require_utils();
|
|
68708
68715
|
var { SCHEMES, getSchemeHandler } = require_schemes();
|
|
68709
|
-
function
|
|
68716
|
+
function normalize4(uri, options) {
|
|
68710
68717
|
if (typeof uri === "string") {
|
|
68711
68718
|
uri = /** @type {T} */
|
|
68712
68719
|
serialize(parse11(uri, options), options);
|
|
@@ -68942,7 +68949,7 @@ var require_fast_uri = __commonJS({
|
|
|
68942
68949
|
}
|
|
68943
68950
|
var fastUri = {
|
|
68944
68951
|
SCHEMES,
|
|
68945
|
-
normalize:
|
|
68952
|
+
normalize: normalize4,
|
|
68946
68953
|
resolve: resolve9,
|
|
68947
68954
|
resolveComponent,
|
|
68948
68955
|
equal,
|
|
@@ -73149,9 +73156,9 @@ If the solution is clear, you can jump to implementation right away. If not, ask
|
|
|
73149
73156
|
- Do not add code comments unless the logic is genuinely complex and non-obvious.
|
|
73150
73157
|
|
|
73151
73158
|
# Before Implementation
|
|
73152
|
-
-
|
|
73153
|
-
-
|
|
73154
|
-
-
|
|
73159
|
+
- Read tests first \u2014 find existing test files for the module you're changing. They reveal expected behavior, edge cases, and the project's testing patterns.
|
|
73160
|
+
- Read neighboring files \u2014 understand naming conventions, error handling patterns, import style, and existing utilities before creating new ones.
|
|
73161
|
+
- Trace the call chain \u2014 follow how the code you're changing is called and what depends on it. Check interfaces, types, and consumers.
|
|
73155
73162
|
- Focus on backward compatibility
|
|
73156
73163
|
- Consider scalability, maintainability, and extensibility in your analysis
|
|
73157
73164
|
|
|
@@ -73176,6 +73183,20 @@ Before building or testing, determine the project's toolchain:
|
|
|
73176
73183
|
- Read README for build/test instructions if the above are unclear
|
|
73177
73184
|
- Common patterns: \`make build\`/\`make test\`, \`npm run build\`/\`npm test\`, \`cargo build\`/\`cargo test\`, \`go build ./...\`/\`go test ./...\`, \`python -m pytest\`
|
|
73178
73185
|
|
|
73186
|
+
# File Editing Rules
|
|
73187
|
+
You have access to the \`edit\`, \`create\`, and \`multi_edit\` tools for modifying files. You MUST use these tools for ALL code changes. They are purpose-built, atomic, and safe.
|
|
73188
|
+
|
|
73189
|
+
DO NOT use sed, awk, echo/cat redirection, or heredocs to modify source code. These commands cause real damage in practice: truncated lines, duplicate code blocks, broken syntax. Every bad edit wastes iterations on fix-up commits.
|
|
73190
|
+
|
|
73191
|
+
Use the right tool:
|
|
73192
|
+
1. To MODIFY existing code \u2192 \`edit\` tool (old_string \u2192 new_string, or start_line/end_line)
|
|
73193
|
+
2. To CREATE a new file \u2192 \`create\` tool
|
|
73194
|
+
3. To CHANGE multiple files at once \u2192 \`multi_edit\` tool
|
|
73195
|
+
4. To READ code \u2192 \`extract\` or \`search\` tools
|
|
73196
|
+
5. If \`edit\` fails with "file has not been read yet" \u2192 use \`extract\` with the EXACT same file path you will pass to \`edit\`. Relative vs absolute path mismatch causes this error. Use the same path format consistently. If it still fails, use bash \`cat\` to read the file, then use \`create\` to write the entire modified file. Do NOT fall back to sed.
|
|
73197
|
+
|
|
73198
|
+
Bash is fine for: formatters (gofmt, prettier, black), build/test/lint commands, git operations, and read-only file inspection (cat, head, tail). sed/awk should ONLY be used for trivial non-code tasks (e.g., config file tweaks) where the replacement is a simple literal string swap.
|
|
73199
|
+
|
|
73179
73200
|
# During Implementation
|
|
73180
73201
|
- Always create a new branch before making changes to the codebase.
|
|
73181
73202
|
- Fix problems at the root cause, not with surface-level patches. Prefer general solutions over special cases.
|
|
@@ -73202,6 +73223,22 @@ Before committing or creating a PR, run through this checklist:
|
|
|
73202
73223
|
|
|
73203
73224
|
Do NOT skip verification. Do NOT proceed to PR creation with a broken build or failing tests.
|
|
73204
73225
|
|
|
73226
|
+
# Output Integrity
|
|
73227
|
+
Your final output MUST accurately reflect what ACTUALLY happened. Do NOT fabricate, hallucinate, or report aspirational results.
|
|
73228
|
+
|
|
73229
|
+
- Only report PR URLs you actually created or updated with \`gh pr create\` or \`git push\`. If you checked out an existing PR but did NOT push changes to it, do NOT claim you updated it.
|
|
73230
|
+
- Describe what you ACTUALLY DID, not what you planned or intended to do. If you ran out of iterations, say so. If tests failed, say so.
|
|
73231
|
+
- Only list files you actually modified AND committed.
|
|
73232
|
+
- If you could not complete the task \u2014 ran out of iterations, tests failed, build broken, push rejected \u2014 report the real reason honestly.
|
|
73233
|
+
|
|
73234
|
+
NEVER claim success when:
|
|
73235
|
+
- You did not run \`git push\` successfully
|
|
73236
|
+
- Tests failed and you did not fix them
|
|
73237
|
+
- You hit the iteration limit before completing the work
|
|
73238
|
+
- You only analyzed/investigated but did not implement changes
|
|
73239
|
+
|
|
73240
|
+
A false success report is WORSE than an honest failure \u2014 it misleads the user into thinking work is done when it is not.
|
|
73241
|
+
|
|
73205
73242
|
# GitHub Integration
|
|
73206
73243
|
- Use the \`gh\` CLI for all GitHub operations: issues, pull requests, checks, releases.
|
|
73207
73244
|
- To view issues or PRs: \`gh issue view <number>\`, \`gh pr view <number>\`.
|
|
@@ -101404,7 +101441,7 @@ var init_vercel = __esm({
|
|
|
101404
101441
|
name: "search",
|
|
101405
101442
|
description: searchDelegate ? searchDelegateDescription : searchDescription,
|
|
101406
101443
|
inputSchema: searchSchema,
|
|
101407
|
-
execute: async ({ query: searchQuery, path: path9, allow_tests, exact, maxTokens: paramMaxTokens, language, session, nextPage }) => {
|
|
101444
|
+
execute: async ({ query: searchQuery, path: path9, allow_tests, exact, maxTokens: paramMaxTokens, language, session, nextPage, workingDirectory }) => {
|
|
101408
101445
|
if (!exact && searchQuery) {
|
|
101409
101446
|
const originalQuery = searchQuery;
|
|
101410
101447
|
searchQuery = autoQuoteSearchTerms(searchQuery);
|
|
@@ -101413,18 +101450,19 @@ var init_vercel = __esm({
|
|
|
101413
101450
|
}
|
|
101414
101451
|
}
|
|
101415
101452
|
const effectiveMaxTokens = paramMaxTokens || maxTokens;
|
|
101453
|
+
const effectiveSearchCwd = workingDirectory || options.cwd || ".";
|
|
101416
101454
|
let searchPaths;
|
|
101417
101455
|
if (path9) {
|
|
101418
|
-
searchPaths = parseAndResolvePaths(path9,
|
|
101456
|
+
searchPaths = parseAndResolvePaths(path9, effectiveSearchCwd);
|
|
101419
101457
|
}
|
|
101420
101458
|
if (!searchPaths || searchPaths.length === 0) {
|
|
101421
|
-
searchPaths = [
|
|
101459
|
+
searchPaths = [effectiveSearchCwd];
|
|
101422
101460
|
}
|
|
101423
101461
|
const searchPath = searchPaths.join(" ");
|
|
101424
101462
|
const searchOptions = {
|
|
101425
101463
|
query: searchQuery,
|
|
101426
101464
|
path: searchPath,
|
|
101427
|
-
cwd:
|
|
101465
|
+
cwd: effectiveSearchCwd,
|
|
101428
101466
|
// Working directory for resolving relative paths
|
|
101429
101467
|
allowTests: allow_tests ?? true,
|
|
101430
101468
|
exact,
|
|
@@ -101475,7 +101513,7 @@ var init_vercel = __esm({
|
|
|
101475
101513
|
try {
|
|
101476
101514
|
const result = maybeAnnotate(await runRawSearch());
|
|
101477
101515
|
if (options.fileTracker && typeof result === "string") {
|
|
101478
|
-
options.fileTracker.trackFilesFromOutput(result,
|
|
101516
|
+
options.fileTracker.trackFilesFromOutput(result, effectiveSearchCwd).catch(() => {
|
|
101479
101517
|
});
|
|
101480
101518
|
}
|
|
101481
101519
|
return result;
|
|
@@ -101528,7 +101566,7 @@ var init_vercel = __esm({
|
|
|
101528
101566
|
}
|
|
101529
101567
|
const fallbackResult = maybeAnnotate(await runRawSearch());
|
|
101530
101568
|
if (options.fileTracker && typeof fallbackResult === "string") {
|
|
101531
|
-
options.fileTracker.trackFilesFromOutput(fallbackResult,
|
|
101569
|
+
options.fileTracker.trackFilesFromOutput(fallbackResult, effectiveSearchCwd).catch(() => {
|
|
101532
101570
|
});
|
|
101533
101571
|
}
|
|
101534
101572
|
return fallbackResult;
|
|
@@ -101591,7 +101629,7 @@ var init_vercel = __esm({
|
|
|
101591
101629
|
try {
|
|
101592
101630
|
const fallbackResult2 = maybeAnnotate(await runRawSearch());
|
|
101593
101631
|
if (options.fileTracker && typeof fallbackResult2 === "string") {
|
|
101594
|
-
options.fileTracker.trackFilesFromOutput(fallbackResult2,
|
|
101632
|
+
options.fileTracker.trackFilesFromOutput(fallbackResult2, effectiveSearchCwd).catch(() => {
|
|
101595
101633
|
});
|
|
101596
101634
|
}
|
|
101597
101635
|
return fallbackResult2;
|
|
@@ -101645,9 +101683,9 @@ var init_vercel = __esm({
|
|
|
101645
101683
|
name: "extract",
|
|
101646
101684
|
description: extractDescription,
|
|
101647
101685
|
inputSchema: extractSchema,
|
|
101648
|
-
execute: async ({ targets, input_content, line, end_line, allow_tests, context_lines, format }) => {
|
|
101686
|
+
execute: async ({ targets, input_content, line, end_line, allow_tests, context_lines, format, workingDirectory }) => {
|
|
101649
101687
|
try {
|
|
101650
|
-
const effectiveCwd = options.cwd || ".";
|
|
101688
|
+
const effectiveCwd = workingDirectory || options.cwd || ".";
|
|
101651
101689
|
if (debug) {
|
|
101652
101690
|
if (targets) {
|
|
101653
101691
|
console.error(`Executing extract with targets: "${targets}", cwd: "${effectiveCwd}", context lines: ${context_lines || 10}`);
|
package/package.json
CHANGED
|
@@ -90,9 +90,9 @@ If the solution is clear, you can jump to implementation right away. If not, ask
|
|
|
90
90
|
- Do not add code comments unless the logic is genuinely complex and non-obvious.
|
|
91
91
|
|
|
92
92
|
# Before Implementation
|
|
93
|
-
-
|
|
94
|
-
-
|
|
95
|
-
-
|
|
93
|
+
- Read tests first — find existing test files for the module you're changing. They reveal expected behavior, edge cases, and the project's testing patterns.
|
|
94
|
+
- Read neighboring files — understand naming conventions, error handling patterns, import style, and existing utilities before creating new ones.
|
|
95
|
+
- Trace the call chain — follow how the code you're changing is called and what depends on it. Check interfaces, types, and consumers.
|
|
96
96
|
- Focus on backward compatibility
|
|
97
97
|
- Consider scalability, maintainability, and extensibility in your analysis
|
|
98
98
|
|
|
@@ -117,6 +117,20 @@ Before building or testing, determine the project's toolchain:
|
|
|
117
117
|
- Read README for build/test instructions if the above are unclear
|
|
118
118
|
- Common patterns: \`make build\`/\`make test\`, \`npm run build\`/\`npm test\`, \`cargo build\`/\`cargo test\`, \`go build ./...\`/\`go test ./...\`, \`python -m pytest\`
|
|
119
119
|
|
|
120
|
+
# File Editing Rules
|
|
121
|
+
You have access to the \`edit\`, \`create\`, and \`multi_edit\` tools for modifying files. You MUST use these tools for ALL code changes. They are purpose-built, atomic, and safe.
|
|
122
|
+
|
|
123
|
+
DO NOT use sed, awk, echo/cat redirection, or heredocs to modify source code. These commands cause real damage in practice: truncated lines, duplicate code blocks, broken syntax. Every bad edit wastes iterations on fix-up commits.
|
|
124
|
+
|
|
125
|
+
Use the right tool:
|
|
126
|
+
1. To MODIFY existing code → \`edit\` tool (old_string → new_string, or start_line/end_line)
|
|
127
|
+
2. To CREATE a new file → \`create\` tool
|
|
128
|
+
3. To CHANGE multiple files at once → \`multi_edit\` tool
|
|
129
|
+
4. To READ code → \`extract\` or \`search\` tools
|
|
130
|
+
5. If \`edit\` fails with "file has not been read yet" → use \`extract\` with the EXACT same file path you will pass to \`edit\`. Relative vs absolute path mismatch causes this error. Use the same path format consistently. If it still fails, use bash \`cat\` to read the file, then use \`create\` to write the entire modified file. Do NOT fall back to sed.
|
|
131
|
+
|
|
132
|
+
Bash is fine for: formatters (gofmt, prettier, black), build/test/lint commands, git operations, and read-only file inspection (cat, head, tail). sed/awk should ONLY be used for trivial non-code tasks (e.g., config file tweaks) where the replacement is a simple literal string swap.
|
|
133
|
+
|
|
120
134
|
# During Implementation
|
|
121
135
|
- Always create a new branch before making changes to the codebase.
|
|
122
136
|
- Fix problems at the root cause, not with surface-level patches. Prefer general solutions over special cases.
|
|
@@ -143,6 +157,22 @@ Before committing or creating a PR, run through this checklist:
|
|
|
143
157
|
|
|
144
158
|
Do NOT skip verification. Do NOT proceed to PR creation with a broken build or failing tests.
|
|
145
159
|
|
|
160
|
+
# Output Integrity
|
|
161
|
+
Your final output MUST accurately reflect what ACTUALLY happened. Do NOT fabricate, hallucinate, or report aspirational results.
|
|
162
|
+
|
|
163
|
+
- Only report PR URLs you actually created or updated with \`gh pr create\` or \`git push\`. If you checked out an existing PR but did NOT push changes to it, do NOT claim you updated it.
|
|
164
|
+
- Describe what you ACTUALLY DID, not what you planned or intended to do. If you ran out of iterations, say so. If tests failed, say so.
|
|
165
|
+
- Only list files you actually modified AND committed.
|
|
166
|
+
- If you could not complete the task — ran out of iterations, tests failed, build broken, push rejected — report the real reason honestly.
|
|
167
|
+
|
|
168
|
+
NEVER claim success when:
|
|
169
|
+
- You did not run \`git push\` successfully
|
|
170
|
+
- Tests failed and you did not fix them
|
|
171
|
+
- You hit the iteration limit before completing the work
|
|
172
|
+
- You only analyzed/investigated but did not implement changes
|
|
173
|
+
|
|
174
|
+
A false success report is WORSE than an honest failure — it misleads the user into thinking work is done when it is not.
|
|
175
|
+
|
|
146
176
|
# GitHub Integration
|
|
147
177
|
- Use the \`gh\` CLI for all GitHub operations: issues, pull requests, checks, releases.
|
|
148
178
|
- To view issues or PRs: \`gh issue view <number>\`, \`gh pr view <number>\`.
|
package/src/tools/fileTracker.js
CHANGED
|
@@ -12,9 +12,22 @@
|
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
14
|
import { createHash } from 'crypto';
|
|
15
|
-
import { resolve, isAbsolute } from 'path';
|
|
15
|
+
import { resolve, isAbsolute, normalize } from 'path';
|
|
16
16
|
import { findSymbol } from './symbolEdit.js';
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Normalize a file path for consistent storage and lookup.
|
|
20
|
+
* Resolves '.', '..', double slashes, and ensures absolute paths are canonical.
|
|
21
|
+
* Does NOT resolve symlinks (that would be expensive and might fail for non-existent files).
|
|
22
|
+
* @param {string} filePath - Path to normalize
|
|
23
|
+
* @returns {string} Normalized path
|
|
24
|
+
*/
|
|
25
|
+
function normalizePath(filePath) {
|
|
26
|
+
if (!filePath) return filePath;
|
|
27
|
+
// resolve() handles '.', '..', double slashes, and makes the path absolute
|
|
28
|
+
return resolve(filePath);
|
|
29
|
+
}
|
|
30
|
+
|
|
18
31
|
/**
|
|
19
32
|
* Compute a SHA-256 content hash for a code block.
|
|
20
33
|
* Normalizes trailing whitespace per line for robustness against editor formatting.
|
|
@@ -106,10 +119,11 @@ export class FileTracker {
|
|
|
106
119
|
* @param {string} resolvedPath - Absolute path to the file
|
|
107
120
|
*/
|
|
108
121
|
markFileSeen(resolvedPath) {
|
|
109
|
-
|
|
110
|
-
this.
|
|
122
|
+
const normalized = normalizePath(resolvedPath);
|
|
123
|
+
this._seenFiles.add(normalized);
|
|
124
|
+
this._textEditCounts.set(normalized, 0);
|
|
111
125
|
if (this.debug) {
|
|
112
|
-
console.error(`[FileTracker] Marked as seen: ${
|
|
126
|
+
console.error(`[FileTracker] Marked as seen: ${normalized}`);
|
|
113
127
|
}
|
|
114
128
|
}
|
|
115
129
|
|
|
@@ -119,7 +133,7 @@ export class FileTracker {
|
|
|
119
133
|
* @returns {boolean}
|
|
120
134
|
*/
|
|
121
135
|
isFileSeen(resolvedPath) {
|
|
122
|
-
return this._seenFiles.has(resolvedPath);
|
|
136
|
+
return this._seenFiles.has(normalizePath(resolvedPath));
|
|
123
137
|
}
|
|
124
138
|
|
|
125
139
|
/**
|
|
@@ -132,7 +146,7 @@ export class FileTracker {
|
|
|
132
146
|
* @param {string} [source='extract'] - How the content was obtained
|
|
133
147
|
*/
|
|
134
148
|
trackSymbolContent(resolvedPath, symbolName, code, startLine, endLine, source = 'extract') {
|
|
135
|
-
const key = `${resolvedPath}#${symbolName}`;
|
|
149
|
+
const key = `${normalizePath(resolvedPath)}#${symbolName}`;
|
|
136
150
|
const contentHash = computeContentHash(code);
|
|
137
151
|
this._contentRecords.set(key, {
|
|
138
152
|
contentHash,
|
|
@@ -154,7 +168,7 @@ export class FileTracker {
|
|
|
154
168
|
* @returns {Object|null} The stored record or null
|
|
155
169
|
*/
|
|
156
170
|
getSymbolRecord(resolvedPath, symbolName) {
|
|
157
|
-
return this._contentRecords.get(`${resolvedPath}#${symbolName}`) || null;
|
|
171
|
+
return this._contentRecords.get(`${normalizePath(resolvedPath)}#${symbolName}`) || null;
|
|
158
172
|
}
|
|
159
173
|
|
|
160
174
|
/**
|
|
@@ -165,7 +179,7 @@ export class FileTracker {
|
|
|
165
179
|
* @returns {{ok: boolean, reason?: string, message?: string}}
|
|
166
180
|
*/
|
|
167
181
|
checkSymbolContent(resolvedPath, symbolName, currentCode) {
|
|
168
|
-
const key = `${resolvedPath}#${symbolName}`;
|
|
182
|
+
const key = `${normalizePath(resolvedPath)}#${symbolName}`;
|
|
169
183
|
const record = this._contentRecords.get(key);
|
|
170
184
|
|
|
171
185
|
if (!record) {
|
|
@@ -253,7 +267,7 @@ export class FileTracker {
|
|
|
253
267
|
* @returns {{ok: boolean, reason?: string, message?: string}}
|
|
254
268
|
*/
|
|
255
269
|
checkBeforeEdit(resolvedPath) {
|
|
256
|
-
if (!this._seenFiles.has(resolvedPath)) {
|
|
270
|
+
if (!this._seenFiles.has(normalizePath(resolvedPath))) {
|
|
257
271
|
return {
|
|
258
272
|
ok: false,
|
|
259
273
|
reason: 'untracked',
|
|
@@ -269,8 +283,9 @@ export class FileTracker {
|
|
|
269
283
|
* @param {string} resolvedPath - Absolute path to the file
|
|
270
284
|
*/
|
|
271
285
|
async trackFileAfterWrite(resolvedPath) {
|
|
272
|
-
|
|
273
|
-
this.
|
|
286
|
+
const normalized = normalizePath(resolvedPath);
|
|
287
|
+
this._seenFiles.add(normalized);
|
|
288
|
+
this.invalidateFileRecords(normalized);
|
|
274
289
|
}
|
|
275
290
|
|
|
276
291
|
/**
|
|
@@ -279,10 +294,11 @@ export class FileTracker {
|
|
|
279
294
|
* @param {string} resolvedPath - Absolute path to the file
|
|
280
295
|
*/
|
|
281
296
|
recordTextEdit(resolvedPath) {
|
|
282
|
-
const
|
|
283
|
-
this._textEditCounts.
|
|
297
|
+
const normalized = normalizePath(resolvedPath);
|
|
298
|
+
const count = (this._textEditCounts.get(normalized) || 0) + 1;
|
|
299
|
+
this._textEditCounts.set(normalized, count);
|
|
284
300
|
if (this.debug) {
|
|
285
|
-
console.error(`[FileTracker] Text edit #${count} for ${
|
|
301
|
+
console.error(`[FileTracker] Text edit #${count} for ${normalized}`);
|
|
286
302
|
}
|
|
287
303
|
}
|
|
288
304
|
|
|
@@ -292,7 +308,7 @@ export class FileTracker {
|
|
|
292
308
|
* @returns {{ok: boolean, editCount?: number, message?: string}}
|
|
293
309
|
*/
|
|
294
310
|
checkTextEditStaleness(resolvedPath) {
|
|
295
|
-
const count = this._textEditCounts.get(resolvedPath) || 0;
|
|
311
|
+
const count = this._textEditCounts.get(normalizePath(resolvedPath)) || 0;
|
|
296
312
|
if (count >= this.maxConsecutiveTextEdits) {
|
|
297
313
|
return {
|
|
298
314
|
ok: false,
|
|
@@ -323,7 +339,7 @@ export class FileTracker {
|
|
|
323
339
|
* @param {string} resolvedPath - Absolute path to the file
|
|
324
340
|
*/
|
|
325
341
|
invalidateFileRecords(resolvedPath) {
|
|
326
|
-
const prefix = resolvedPath + '#';
|
|
342
|
+
const prefix = normalizePath(resolvedPath) + '#';
|
|
327
343
|
for (const key of this._contentRecords.keys()) {
|
|
328
344
|
if (key.startsWith(prefix)) {
|
|
329
345
|
this._contentRecords.delete(key);
|
|
@@ -340,7 +356,7 @@ export class FileTracker {
|
|
|
340
356
|
* @returns {boolean}
|
|
341
357
|
*/
|
|
342
358
|
isTracked(resolvedPath) {
|
|
343
|
-
return this.isFileSeen(resolvedPath);
|
|
359
|
+
return this.isFileSeen(normalizePath(resolvedPath));
|
|
344
360
|
}
|
|
345
361
|
|
|
346
362
|
/**
|
package/src/tools/vercel.js
CHANGED
|
@@ -385,7 +385,7 @@ export const searchTool = (options = {}) => {
|
|
|
385
385
|
? searchDelegateDescription
|
|
386
386
|
: searchDescription,
|
|
387
387
|
inputSchema: searchSchema,
|
|
388
|
-
execute: async ({ query: searchQuery, path, allow_tests, exact, maxTokens: paramMaxTokens, language, session, nextPage }) => {
|
|
388
|
+
execute: async ({ query: searchQuery, path, allow_tests, exact, maxTokens: paramMaxTokens, language, session, nextPage, workingDirectory }) => {
|
|
389
389
|
// Auto-quote mixed-case and underscore terms to prevent unwanted stemming/splitting
|
|
390
390
|
// Skip when exact=true since that already preserves the literal string
|
|
391
391
|
if (!exact && searchQuery) {
|
|
@@ -399,15 +399,18 @@ export const searchTool = (options = {}) => {
|
|
|
399
399
|
// Use parameter maxTokens if provided, otherwise use the default
|
|
400
400
|
const effectiveMaxTokens = paramMaxTokens || maxTokens;
|
|
401
401
|
|
|
402
|
+
// Use workingDirectory (injected by _buildNativeTools at runtime) > cwd from config > fallback
|
|
403
|
+
const effectiveSearchCwd = workingDirectory || options.cwd || '.';
|
|
404
|
+
|
|
402
405
|
// Parse and resolve paths (supports comma-separated and relative paths)
|
|
403
406
|
let searchPaths;
|
|
404
407
|
if (path) {
|
|
405
|
-
searchPaths = parseAndResolvePaths(path,
|
|
408
|
+
searchPaths = parseAndResolvePaths(path, effectiveSearchCwd);
|
|
406
409
|
}
|
|
407
410
|
|
|
408
411
|
// Default to cwd or '.' if no paths provided
|
|
409
412
|
if (!searchPaths || searchPaths.length === 0) {
|
|
410
|
-
searchPaths = [
|
|
413
|
+
searchPaths = [effectiveSearchCwd];
|
|
411
414
|
}
|
|
412
415
|
|
|
413
416
|
// Join paths with space for CLI (probe search supports multiple paths)
|
|
@@ -416,7 +419,7 @@ export const searchTool = (options = {}) => {
|
|
|
416
419
|
const searchOptions = {
|
|
417
420
|
query: searchQuery,
|
|
418
421
|
path: searchPath,
|
|
419
|
-
cwd:
|
|
422
|
+
cwd: effectiveSearchCwd, // Working directory for resolving relative paths
|
|
420
423
|
allowTests: allow_tests ?? true,
|
|
421
424
|
exact,
|
|
422
425
|
json: false,
|
|
@@ -473,7 +476,7 @@ export const searchTool = (options = {}) => {
|
|
|
473
476
|
const result = maybeAnnotate(await runRawSearch());
|
|
474
477
|
// Track files found in search results for staleness detection
|
|
475
478
|
if (options.fileTracker && typeof result === 'string') {
|
|
476
|
-
options.fileTracker.trackFilesFromOutput(result,
|
|
479
|
+
options.fileTracker.trackFilesFromOutput(result, effectiveSearchCwd).catch(() => {});
|
|
477
480
|
}
|
|
478
481
|
return result;
|
|
479
482
|
} catch (error) {
|
|
@@ -532,7 +535,7 @@ export const searchTool = (options = {}) => {
|
|
|
532
535
|
}
|
|
533
536
|
const fallbackResult = maybeAnnotate(await runRawSearch());
|
|
534
537
|
if (options.fileTracker && typeof fallbackResult === 'string') {
|
|
535
|
-
options.fileTracker.trackFilesFromOutput(fallbackResult,
|
|
538
|
+
options.fileTracker.trackFilesFromOutput(fallbackResult, effectiveSearchCwd).catch(() => {});
|
|
536
539
|
}
|
|
537
540
|
return fallbackResult;
|
|
538
541
|
}
|
|
@@ -614,7 +617,7 @@ export const searchTool = (options = {}) => {
|
|
|
614
617
|
try {
|
|
615
618
|
const fallbackResult2 = maybeAnnotate(await runRawSearch());
|
|
616
619
|
if (options.fileTracker && typeof fallbackResult2 === 'string') {
|
|
617
|
-
options.fileTracker.trackFilesFromOutput(fallbackResult2,
|
|
620
|
+
options.fileTracker.trackFilesFromOutput(fallbackResult2, effectiveSearchCwd).catch(() => {});
|
|
618
621
|
}
|
|
619
622
|
return fallbackResult2;
|
|
620
623
|
} catch (fallbackError) {
|
|
@@ -693,10 +696,10 @@ export const extractTool = (options = {}) => {
|
|
|
693
696
|
name: 'extract',
|
|
694
697
|
description: extractDescription,
|
|
695
698
|
inputSchema: extractSchema,
|
|
696
|
-
execute: async ({ targets, input_content, line, end_line, allow_tests, context_lines, format }) => {
|
|
699
|
+
execute: async ({ targets, input_content, line, end_line, allow_tests, context_lines, format, workingDirectory }) => {
|
|
697
700
|
try {
|
|
698
|
-
// Use
|
|
699
|
-
const effectiveCwd = options.cwd || '.';
|
|
701
|
+
// Use workingDirectory (injected by _buildNativeTools at runtime) > cwd from config > fallback
|
|
702
|
+
const effectiveCwd = workingDirectory || options.cwd || '.';
|
|
700
703
|
|
|
701
704
|
if (debug) {
|
|
702
705
|
if (targets) {
|