@cortexkit/aft-pi 0.36.1 → 0.37.1
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 +2 -3
- package/dist/bg-notifications.d.ts +5 -1
- package/dist/bg-notifications.d.ts.map +1 -1
- package/dist/index.d.ts +0 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +410 -250
- package/dist/sync-watch-abort.d.ts +26 -0
- package/dist/sync-watch-abort.d.ts.map +1 -0
- package/dist/tools/_shared.d.ts +1 -11
- package/dist/tools/_shared.d.ts.map +1 -1
- package/dist/tools/bash.d.ts +2 -2
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/conflicts.d.ts.map +1 -1
- package/dist/tools/hoisted.d.ts.map +1 -1
- package/dist/tools/navigate.d.ts +1 -1
- package/dist/tools/semantic.d.ts.map +1 -1
- package/dist/workflow-hints.d.ts.map +1 -1
- package/package.json +8 -8
- package/dist/tools/structure.d.ts +0 -32
- package/dist/tools/structure.d.ts.map +0 -1
package/dist/index.js
CHANGED
|
@@ -11678,6 +11678,9 @@ function error(message, meta) {
|
|
|
11678
11678
|
var CONFLICT_HINT = `
|
|
11679
11679
|
|
|
11680
11680
|
[Hint] Use aft_conflicts to see all conflict regions across files in a single call.`;
|
|
11681
|
+
var GREP_SEARCH_AFT_SEARCH_HINT = "DO NOT search code by running grep/rg in bash — it is unindexed, unranked, and serial. Use the `aft_search` tool instead (it auto-routes concepts, identifiers, regex, and literals).";
|
|
11682
|
+
var GREP_SEARCH_GREP_HINT = "DO NOT search code by running grep/rg in bash — it is unindexed, unranked, and serial. Use the `grep` tool instead (indexed and ranked).";
|
|
11683
|
+
var GREP_SEARCH_HINT_PREFIX = "DO NOT search code by running grep/rg in bash —";
|
|
11681
11684
|
function maybeAppendConflictsHint(output) {
|
|
11682
11685
|
if (!output.includes("Automatic merge failed; fix conflicts"))
|
|
11683
11686
|
return output;
|
|
@@ -11685,6 +11688,146 @@ function maybeAppendConflictsHint(output) {
|
|
|
11685
11688
|
return output;
|
|
11686
11689
|
return output + CONFLICT_HINT;
|
|
11687
11690
|
}
|
|
11691
|
+
function commandLeadsWithCodeSearch(command) {
|
|
11692
|
+
const trimmed = command.trim();
|
|
11693
|
+
if (!trimmed)
|
|
11694
|
+
return false;
|
|
11695
|
+
const afterCd = peelLeadingCdAnd(trimmed);
|
|
11696
|
+
if (afterCd === null)
|
|
11697
|
+
return false;
|
|
11698
|
+
const firstStage = firstPipelineStage(afterCd);
|
|
11699
|
+
if (firstStage === null)
|
|
11700
|
+
return false;
|
|
11701
|
+
const firstToken = readShellToken(firstStage, skipSpaces(firstStage, 0));
|
|
11702
|
+
if (firstToken === null)
|
|
11703
|
+
return false;
|
|
11704
|
+
return firstToken.token === "grep" || firstToken.token === "rg";
|
|
11705
|
+
}
|
|
11706
|
+
function maybeAppendGrepSearchHint(output, command, aftSearchRegistered) {
|
|
11707
|
+
if (output === "")
|
|
11708
|
+
return output;
|
|
11709
|
+
if (!commandLeadsWithCodeSearch(command))
|
|
11710
|
+
return output;
|
|
11711
|
+
if (output.includes(GREP_SEARCH_HINT_PREFIX))
|
|
11712
|
+
return output;
|
|
11713
|
+
const hint = aftSearchRegistered ? GREP_SEARCH_AFT_SEARCH_HINT : GREP_SEARCH_GREP_HINT;
|
|
11714
|
+
return `${output}
|
|
11715
|
+
|
|
11716
|
+
${hint}`;
|
|
11717
|
+
}
|
|
11718
|
+
function peelLeadingCdAnd(command) {
|
|
11719
|
+
const first = readShellToken(command, skipSpaces(command, 0));
|
|
11720
|
+
if (first === null)
|
|
11721
|
+
return null;
|
|
11722
|
+
if (first.token !== "cd")
|
|
11723
|
+
return command;
|
|
11724
|
+
const dir = readShellToken(command, skipSpaces(command, first.end));
|
|
11725
|
+
if (dir === null)
|
|
11726
|
+
return null;
|
|
11727
|
+
if (!dir.token)
|
|
11728
|
+
return command;
|
|
11729
|
+
const afterDir = skipSpaces(command, dir.end);
|
|
11730
|
+
if (!command.startsWith("&&", afterDir))
|
|
11731
|
+
return command;
|
|
11732
|
+
return command.slice(afterDir + 2).trim();
|
|
11733
|
+
}
|
|
11734
|
+
function firstPipelineStage(command) {
|
|
11735
|
+
let quote = "none";
|
|
11736
|
+
let firstPipeIndex;
|
|
11737
|
+
for (let index = 0;index < command.length; index++) {
|
|
11738
|
+
const ch = command[index];
|
|
11739
|
+
if (quote === "single") {
|
|
11740
|
+
if (ch === "'")
|
|
11741
|
+
quote = "none";
|
|
11742
|
+
continue;
|
|
11743
|
+
}
|
|
11744
|
+
if (quote === "double") {
|
|
11745
|
+
if (ch === '"') {
|
|
11746
|
+
quote = "none";
|
|
11747
|
+
} else if (ch === "\\") {
|
|
11748
|
+
index++;
|
|
11749
|
+
} else if (ch === "`") {
|
|
11750
|
+
return null;
|
|
11751
|
+
}
|
|
11752
|
+
continue;
|
|
11753
|
+
}
|
|
11754
|
+
if (ch === "'") {
|
|
11755
|
+
quote = "single";
|
|
11756
|
+
} else if (ch === '"') {
|
|
11757
|
+
quote = "double";
|
|
11758
|
+
} else if (ch === "\\") {
|
|
11759
|
+
index++;
|
|
11760
|
+
} else if (ch === "`") {
|
|
11761
|
+
return null;
|
|
11762
|
+
} else if (ch === "|") {
|
|
11763
|
+
if (command[index + 1] === "|") {
|
|
11764
|
+
index++;
|
|
11765
|
+
} else if (firstPipeIndex === undefined) {
|
|
11766
|
+
firstPipeIndex = index;
|
|
11767
|
+
}
|
|
11768
|
+
}
|
|
11769
|
+
}
|
|
11770
|
+
if (quote !== "none")
|
|
11771
|
+
return null;
|
|
11772
|
+
return command.slice(0, firstPipeIndex ?? command.length).trim();
|
|
11773
|
+
}
|
|
11774
|
+
function readShellToken(command, start) {
|
|
11775
|
+
let quote = "none";
|
|
11776
|
+
let token = "";
|
|
11777
|
+
let index = start;
|
|
11778
|
+
for (;index < command.length; index++) {
|
|
11779
|
+
const ch = command[index];
|
|
11780
|
+
if (quote === "single") {
|
|
11781
|
+
if (ch === "'") {
|
|
11782
|
+
quote = "none";
|
|
11783
|
+
} else {
|
|
11784
|
+
token += ch;
|
|
11785
|
+
}
|
|
11786
|
+
continue;
|
|
11787
|
+
}
|
|
11788
|
+
if (quote === "double") {
|
|
11789
|
+
if (ch === '"') {
|
|
11790
|
+
quote = "none";
|
|
11791
|
+
} else if (ch === "\\") {
|
|
11792
|
+
index++;
|
|
11793
|
+
token += command[index] ?? "\\";
|
|
11794
|
+
} else if (ch === "`") {
|
|
11795
|
+
return null;
|
|
11796
|
+
} else {
|
|
11797
|
+
token += ch;
|
|
11798
|
+
}
|
|
11799
|
+
continue;
|
|
11800
|
+
}
|
|
11801
|
+
if (/\s/.test(ch))
|
|
11802
|
+
break;
|
|
11803
|
+
if (isTokenBoundary(ch))
|
|
11804
|
+
break;
|
|
11805
|
+
if (ch === "'") {
|
|
11806
|
+
quote = "single";
|
|
11807
|
+
} else if (ch === '"') {
|
|
11808
|
+
quote = "double";
|
|
11809
|
+
} else if (ch === "\\") {
|
|
11810
|
+
index++;
|
|
11811
|
+
token += command[index] ?? "\\";
|
|
11812
|
+
} else if (ch === "`") {
|
|
11813
|
+
return null;
|
|
11814
|
+
} else {
|
|
11815
|
+
token += ch;
|
|
11816
|
+
}
|
|
11817
|
+
}
|
|
11818
|
+
if (quote !== "none")
|
|
11819
|
+
return null;
|
|
11820
|
+
return { token, end: index };
|
|
11821
|
+
}
|
|
11822
|
+
function isTokenBoundary(ch) {
|
|
11823
|
+
return ch === "|" || ch === ";" || ch === "&" || ch === "<" || ch === ">";
|
|
11824
|
+
}
|
|
11825
|
+
function skipSpaces(input, start) {
|
|
11826
|
+
let index = start;
|
|
11827
|
+
while (index < input.length && /\s/.test(input[index]))
|
|
11828
|
+
index++;
|
|
11829
|
+
return index;
|
|
11830
|
+
}
|
|
11688
11831
|
// ../aft-bridge/dist/bash-timeout.js
|
|
11689
11832
|
function resolveBashKillTimeout(modelTimeout, foregroundWaitMs) {
|
|
11690
11833
|
if (modelTimeout !== undefined && modelTimeout >= foregroundWaitMs) {
|
|
@@ -11698,6 +11841,21 @@ import { homedir } from "node:os";
|
|
|
11698
11841
|
import { join } from "node:path";
|
|
11699
11842
|
import { StringDecoder } from "node:string_decoder";
|
|
11700
11843
|
|
|
11844
|
+
// ../aft-bridge/dist/command-timeouts.js
|
|
11845
|
+
var LONG_RUNNING_COMMAND_TIMEOUT_MS = {
|
|
11846
|
+
callers: 60000,
|
|
11847
|
+
trace_to: 60000,
|
|
11848
|
+
trace_to_symbol: 60000,
|
|
11849
|
+
trace_data: 60000,
|
|
11850
|
+
impact: 60000,
|
|
11851
|
+
grep: 60000,
|
|
11852
|
+
glob: 60000,
|
|
11853
|
+
semantic_search: 60000
|
|
11854
|
+
};
|
|
11855
|
+
function timeoutForCommand(command) {
|
|
11856
|
+
return LONG_RUNNING_COMMAND_TIMEOUT_MS[command];
|
|
11857
|
+
}
|
|
11858
|
+
|
|
11701
11859
|
// ../aft-bridge/dist/status-bar.js
|
|
11702
11860
|
var STATUS_BAR_HEARTBEAT_CALLS = 15;
|
|
11703
11861
|
function createStatusBarEmitState() {
|
|
@@ -11745,6 +11903,27 @@ var DEFAULT_BRIDGE_TIMEOUT_MS = 30000;
|
|
|
11745
11903
|
var BRIDGE_HANG_TIMEOUT_THRESHOLD = 2;
|
|
11746
11904
|
var SEMANTIC_TIMEOUT_SAFETY_MARGIN_MS = 5000;
|
|
11747
11905
|
var MAX_STDOUT_BUFFER = 64 * 1024 * 1024;
|
|
11906
|
+
var STDOUT_BUFFER_COMPACT_THRESHOLD = 64 * 1024;
|
|
11907
|
+
var TERMINAL_BASH_STATUSES = new Set([
|
|
11908
|
+
"completed",
|
|
11909
|
+
"failed",
|
|
11910
|
+
"killed",
|
|
11911
|
+
"timed_out",
|
|
11912
|
+
"cancelled",
|
|
11913
|
+
"timeout"
|
|
11914
|
+
]);
|
|
11915
|
+
function isTerminalBashStatus(status) {
|
|
11916
|
+
return typeof status === "string" && TERMINAL_BASH_STATUSES.has(status);
|
|
11917
|
+
}
|
|
11918
|
+
function bashTaskIdFrom(response) {
|
|
11919
|
+
const snakeCase = response.task_id;
|
|
11920
|
+
if (typeof snakeCase === "string" && snakeCase.length > 0)
|
|
11921
|
+
return snakeCase;
|
|
11922
|
+
const camelCase = response.taskId;
|
|
11923
|
+
if (typeof camelCase === "string" && camelCase.length > 0)
|
|
11924
|
+
return camelCase;
|
|
11925
|
+
return;
|
|
11926
|
+
}
|
|
11748
11927
|
function tagStderrLine(line) {
|
|
11749
11928
|
return /^\[aft(-\w+)?\] /.test(line) ? line : `[aft] ${line}`;
|
|
11750
11929
|
}
|
|
@@ -11799,11 +11978,12 @@ function clampSemanticTimeout(configOverrides, bridgeTimeoutMs) {
|
|
|
11799
11978
|
if (typeof timeoutMs !== "number" || !Number.isFinite(timeoutMs)) {
|
|
11800
11979
|
return configOverrides;
|
|
11801
11980
|
}
|
|
11802
|
-
const
|
|
11981
|
+
const semanticTransportBudgetMs = Math.max(bridgeTimeoutMs, LONG_RUNNING_COMMAND_TIMEOUT_MS.semantic_search ?? 0);
|
|
11982
|
+
const maxSemanticTimeoutMs = semanticTransportBudgetMs > SEMANTIC_TIMEOUT_SAFETY_MARGIN_MS ? semanticTransportBudgetMs - SEMANTIC_TIMEOUT_SAFETY_MARGIN_MS : Math.max(1, semanticTransportBudgetMs - 1);
|
|
11803
11983
|
if (timeoutMs <= maxSemanticTimeoutMs) {
|
|
11804
11984
|
return configOverrides;
|
|
11805
11985
|
}
|
|
11806
|
-
warn(`semantic.timeout_ms=${timeoutMs} exceeds
|
|
11986
|
+
warn(`semantic.timeout_ms=${timeoutMs} exceeds the semantic transport budget; clamping to ${maxSemanticTimeoutMs}ms (budget: ${semanticTransportBudgetMs}ms)`);
|
|
11807
11987
|
return {
|
|
11808
11988
|
...configOverrides,
|
|
11809
11989
|
semantic: {
|
|
@@ -11829,8 +12009,10 @@ class BinaryBridge {
|
|
|
11829
12009
|
cwd;
|
|
11830
12010
|
process = null;
|
|
11831
12011
|
pending = new Map;
|
|
12012
|
+
outstandingBackgroundTaskIds = new Set;
|
|
11832
12013
|
nextId = 1;
|
|
11833
12014
|
stdoutBuffer = "";
|
|
12015
|
+
stdoutReadOffset = 0;
|
|
11834
12016
|
stderrBuffer = "";
|
|
11835
12017
|
stderrTail = [];
|
|
11836
12018
|
_restartCount = 0;
|
|
@@ -11940,6 +12122,9 @@ class BinaryBridge {
|
|
|
11940
12122
|
hasPendingRequests() {
|
|
11941
12123
|
return this.pending.size > 0;
|
|
11942
12124
|
}
|
|
12125
|
+
hasOutstandingBackgroundTasks() {
|
|
12126
|
+
return this.outstandingBackgroundTaskIds.size > 0;
|
|
12127
|
+
}
|
|
11943
12128
|
getCwd() {
|
|
11944
12129
|
return this.cwd;
|
|
11945
12130
|
}
|
|
@@ -12061,7 +12246,7 @@ class BinaryBridge {
|
|
|
12061
12246
|
entry.reject(new Error(`${this.errorPrefix} Request "${command}" (id=${id}) timed out after ${effectiveTimeoutMs}ms`));
|
|
12062
12247
|
this.handleTimeout(requestSessionId);
|
|
12063
12248
|
}, effectiveTimeoutMs);
|
|
12064
|
-
this.pending.set(id, { resolve, reject, timer, onProgress: options?.onProgress });
|
|
12249
|
+
this.pending.set(id, { resolve, reject, timer, onProgress: options?.onProgress, command });
|
|
12065
12250
|
if (!this.process?.stdin?.writable) {
|
|
12066
12251
|
this.pending.delete(id);
|
|
12067
12252
|
clearTimeout(timer);
|
|
@@ -12322,6 +12507,7 @@ class BinaryBridge {
|
|
|
12322
12507
|
});
|
|
12323
12508
|
this.process = child;
|
|
12324
12509
|
this.stdoutBuffer = "";
|
|
12510
|
+
this.stdoutReadOffset = 0;
|
|
12325
12511
|
this.stderrBuffer = "";
|
|
12326
12512
|
this.lastChildActivityAt = 0;
|
|
12327
12513
|
this.consecutiveRequestTimeouts = 0;
|
|
@@ -12366,24 +12552,41 @@ class BinaryBridge {
|
|
|
12366
12552
|
${tail}`;
|
|
12367
12553
|
}
|
|
12368
12554
|
onStdoutData(data) {
|
|
12555
|
+
if (this.stdoutReadOffset > STDOUT_BUFFER_COMPACT_THRESHOLD) {
|
|
12556
|
+
this.compactStdoutBuffer();
|
|
12557
|
+
}
|
|
12369
12558
|
this.stdoutBuffer += data;
|
|
12370
|
-
if (this.stdoutBuffer.length > MAX_STDOUT_BUFFER) {
|
|
12559
|
+
if (this.stdoutBuffer.length - this.stdoutReadOffset > MAX_STDOUT_BUFFER) {
|
|
12371
12560
|
this.handleCrash(new Error(`aft bridge stdout buffer exceeded ${MAX_STDOUT_BUFFER} bytes — killing bridge`));
|
|
12372
12561
|
return;
|
|
12373
12562
|
}
|
|
12374
12563
|
let newlineIdx;
|
|
12375
12564
|
while ((newlineIdx = this.stdoutBuffer.indexOf(`
|
|
12376
|
-
|
|
12377
|
-
const line = this.stdoutBuffer.slice(
|
|
12378
|
-
this.
|
|
12379
|
-
if (
|
|
12380
|
-
|
|
12381
|
-
|
|
12565
|
+
`, this.stdoutReadOffset)) !== -1) {
|
|
12566
|
+
const line = this.stdoutBuffer.slice(this.stdoutReadOffset, newlineIdx).trim();
|
|
12567
|
+
this.stdoutReadOffset = newlineIdx + 1;
|
|
12568
|
+
if (line) {
|
|
12569
|
+
this.processStdoutLine(line);
|
|
12570
|
+
}
|
|
12571
|
+
if (this.stdoutReadOffset > STDOUT_BUFFER_COMPACT_THRESHOLD && this.stdoutReadOffset > this.stdoutBuffer.length / 2) {
|
|
12572
|
+
this.compactStdoutBuffer();
|
|
12573
|
+
}
|
|
12382
12574
|
}
|
|
12575
|
+
if (this.stdoutReadOffset === this.stdoutBuffer.length) {
|
|
12576
|
+
this.stdoutBuffer = "";
|
|
12577
|
+
this.stdoutReadOffset = 0;
|
|
12578
|
+
}
|
|
12579
|
+
}
|
|
12580
|
+
compactStdoutBuffer() {
|
|
12581
|
+
if (this.stdoutReadOffset === 0)
|
|
12582
|
+
return;
|
|
12583
|
+
this.stdoutBuffer = this.stdoutBuffer.slice(this.stdoutReadOffset);
|
|
12584
|
+
this.stdoutReadOffset = 0;
|
|
12383
12585
|
}
|
|
12384
12586
|
flushStdoutBuffer() {
|
|
12385
|
-
const line = this.stdoutBuffer.trim();
|
|
12587
|
+
const line = this.stdoutBuffer.slice(this.stdoutReadOffset).trim();
|
|
12386
12588
|
this.stdoutBuffer = "";
|
|
12589
|
+
this.stdoutReadOffset = 0;
|
|
12387
12590
|
if (!line)
|
|
12388
12591
|
return;
|
|
12389
12592
|
this.processStdoutLine(line);
|
|
@@ -12416,6 +12619,9 @@ class BinaryBridge {
|
|
|
12416
12619
|
return;
|
|
12417
12620
|
}
|
|
12418
12621
|
if (response.type === "bash_completed") {
|
|
12622
|
+
const taskId = bashTaskIdFrom(response);
|
|
12623
|
+
if (taskId)
|
|
12624
|
+
this.outstandingBackgroundTaskIds.delete(taskId);
|
|
12419
12625
|
this.onBashCompletion?.(response, this);
|
|
12420
12626
|
return;
|
|
12421
12627
|
}
|
|
@@ -12446,6 +12652,7 @@ class BinaryBridge {
|
|
|
12446
12652
|
clearTimeout(entry.timer);
|
|
12447
12653
|
this.consecutiveRequestTimeouts = 0;
|
|
12448
12654
|
this.scheduleRestartCountReset();
|
|
12655
|
+
this.accountForBashTaskResponse(entry.command, response);
|
|
12449
12656
|
this.captureStatusBar(response);
|
|
12450
12657
|
entry.resolve(response);
|
|
12451
12658
|
} else if (typeof response.type === "string") {
|
|
@@ -12455,6 +12662,18 @@ class BinaryBridge {
|
|
|
12455
12662
|
this.warnVia(`Failed to parse stdout line: ${line}`);
|
|
12456
12663
|
}
|
|
12457
12664
|
}
|
|
12665
|
+
accountForBashTaskResponse(command, response) {
|
|
12666
|
+
const taskId = bashTaskIdFrom(response);
|
|
12667
|
+
if (!taskId)
|
|
12668
|
+
return;
|
|
12669
|
+
if (isTerminalBashStatus(response.status)) {
|
|
12670
|
+
this.outstandingBackgroundTaskIds.delete(taskId);
|
|
12671
|
+
return;
|
|
12672
|
+
}
|
|
12673
|
+
if (command === "bash" && response.success !== false) {
|
|
12674
|
+
this.outstandingBackgroundTaskIds.add(taskId);
|
|
12675
|
+
}
|
|
12676
|
+
}
|
|
12458
12677
|
captureStatusBar(response) {
|
|
12459
12678
|
const parsed = parseStatusBarCounts(response.status_bar);
|
|
12460
12679
|
if (parsed)
|
|
@@ -12466,6 +12685,7 @@ class BinaryBridge {
|
|
|
12466
12685
|
handleTimeout(triggeringSessionId) {
|
|
12467
12686
|
this.consecutiveRequestTimeouts = 0;
|
|
12468
12687
|
this.rejectAllPending(new Error(`${this.errorPrefix} bridge killed during sibling timeout — request aborted`));
|
|
12688
|
+
this.outstandingBackgroundTaskIds.clear();
|
|
12469
12689
|
if (this.process) {
|
|
12470
12690
|
this.process.kill("SIGKILL");
|
|
12471
12691
|
this.process = null;
|
|
@@ -12495,6 +12715,7 @@ class BinaryBridge {
|
|
|
12495
12715
|
}
|
|
12496
12716
|
this.clearRestartResetTimer();
|
|
12497
12717
|
this.configured = false;
|
|
12718
|
+
this.outstandingBackgroundTaskIds.clear();
|
|
12498
12719
|
const tail = this.formatStderrTail();
|
|
12499
12720
|
if (tail) {
|
|
12500
12721
|
this.errorVia(`Binary crashed (restarts: ${this._restartCount})${cause ? `: ${cause.message}` : ""}.${tail}`);
|
|
@@ -14220,14 +14441,27 @@ function isCompressorHandledRunner(stage) {
|
|
|
14220
14441
|
if (tokens.some((token) => token === "&&" || token === "||" || token.includes(";"))) {
|
|
14221
14442
|
return false;
|
|
14222
14443
|
}
|
|
14223
|
-
|
|
14224
|
-
|
|
14225
|
-
|
|
14226
|
-
|
|
14444
|
+
let tokenOffset = 0;
|
|
14445
|
+
while (tokenOffset < tokens.length && isEnvAssignment(tokens[tokenOffset])) {
|
|
14446
|
+
tokenOffset++;
|
|
14447
|
+
}
|
|
14448
|
+
const first = runnerName(tokens[tokenOffset]);
|
|
14449
|
+
const runnerArgs = tokens.slice(tokenOffset + 1);
|
|
14450
|
+
const second = runnerArgs[0];
|
|
14451
|
+
const third = runnerArgs[1];
|
|
14452
|
+
const rest = runnerArgs;
|
|
14227
14453
|
if (!first)
|
|
14228
14454
|
return false;
|
|
14229
|
-
if (first === "bun")
|
|
14230
|
-
|
|
14455
|
+
if (first === "bun") {
|
|
14456
|
+
let args = rest;
|
|
14457
|
+
if (args[0] === "--cwd")
|
|
14458
|
+
args = args.slice(2);
|
|
14459
|
+
else if (args[0]?.startsWith("--cwd="))
|
|
14460
|
+
args = args.slice(1);
|
|
14461
|
+
const sub = args[0];
|
|
14462
|
+
const subNext = args[1];
|
|
14463
|
+
return sub === "test" || sub === "run" && startsWithTest(subNext);
|
|
14464
|
+
}
|
|
14231
14465
|
if (first === "npm" || first === "pnpm") {
|
|
14232
14466
|
return second === "test" || second === "run" && startsWithTest(third);
|
|
14233
14467
|
}
|
|
@@ -14250,14 +14484,20 @@ function isCompressorHandledRunner(stage) {
|
|
|
14250
14484
|
return hasBuildTask(rest, ["test", "check", "build", "assemble", "clean"]);
|
|
14251
14485
|
}
|
|
14252
14486
|
if (first === "mvn" || first === "mvnw") {
|
|
14253
|
-
return hasBuildTask(rest, ["test", "verify", "package", "install"]);
|
|
14487
|
+
return hasBuildTask(rest, ["test", "verify", "package", "install", "clean"]);
|
|
14254
14488
|
}
|
|
14255
14489
|
if (first === "dotnet")
|
|
14256
14490
|
return ["test", "build"].includes(second ?? "");
|
|
14257
14491
|
if (first === "rspec")
|
|
14258
14492
|
return true;
|
|
14259
|
-
if (first === "rake")
|
|
14260
|
-
|
|
14493
|
+
if (first === "rake") {
|
|
14494
|
+
const positionals = rest.filter((a) => !a.startsWith("-"));
|
|
14495
|
+
if (positionals.length === 0)
|
|
14496
|
+
return false;
|
|
14497
|
+
if (positionals.some((a) => a.includes("/") || a.includes(".") || a.includes("=")))
|
|
14498
|
+
return false;
|
|
14499
|
+
return positionals.some((a) => a === "test" || a === "spec");
|
|
14500
|
+
}
|
|
14261
14501
|
if (first === "phpunit" || first === "pest")
|
|
14262
14502
|
return true;
|
|
14263
14503
|
if (first === "xcodebuild")
|
|
@@ -14280,6 +14520,11 @@ function isCompressorHandledRunner(stage) {
|
|
|
14280
14520
|
"nox"
|
|
14281
14521
|
].includes(first);
|
|
14282
14522
|
}
|
|
14523
|
+
function isEnvAssignment(token) {
|
|
14524
|
+
if (!token)
|
|
14525
|
+
return false;
|
|
14526
|
+
return /^[a-zA-Z_][a-zA-Z0-9_]*=/.test(token);
|
|
14527
|
+
}
|
|
14283
14528
|
function runnerName(token) {
|
|
14284
14529
|
if (!token)
|
|
14285
14530
|
return "";
|
|
@@ -14536,7 +14781,7 @@ function tokenizeStage(stage) {
|
|
|
14536
14781
|
// ../aft-bridge/dist/pool.js
|
|
14537
14782
|
import { realpathSync as realpathSync2 } from "node:fs";
|
|
14538
14783
|
import { homedir as homedir6 } from "node:os";
|
|
14539
|
-
var DEFAULT_IDLE_TIMEOUT_MS =
|
|
14784
|
+
var DEFAULT_IDLE_TIMEOUT_MS = 30 * 60 * 1000;
|
|
14540
14785
|
var DEFAULT_MAX_POOL_SIZE = 8;
|
|
14541
14786
|
var CLEANUP_INTERVAL_MS = 60 * 1000;
|
|
14542
14787
|
function canonicalHomeDir() {
|
|
@@ -14631,7 +14876,7 @@ class BridgePool {
|
|
|
14631
14876
|
cleanup() {
|
|
14632
14877
|
const now = Date.now();
|
|
14633
14878
|
for (const [dir, entry] of this.bridges) {
|
|
14634
|
-
if (entry.bridge.hasPendingRequests())
|
|
14879
|
+
if (entry.bridge.hasPendingRequests() || entry.bridge.hasOutstandingBackgroundTasks())
|
|
14635
14880
|
continue;
|
|
14636
14881
|
if (now - entry.lastUsed > this.idleTimeoutMs) {
|
|
14637
14882
|
entry.bridge.shutdown().catch((err) => this.error("cleanup shutdown failed:", err));
|
|
@@ -14639,7 +14884,7 @@ class BridgePool {
|
|
|
14639
14884
|
}
|
|
14640
14885
|
}
|
|
14641
14886
|
for (const bridge of this.staleBridges) {
|
|
14642
|
-
if (bridge.hasPendingRequests())
|
|
14887
|
+
if (bridge.hasPendingRequests() || bridge.hasOutstandingBackgroundTasks())
|
|
14643
14888
|
continue;
|
|
14644
14889
|
bridge.shutdown().catch((err) => this.error("stale cleanup shutdown failed:", err));
|
|
14645
14890
|
this.staleBridges.delete(bridge);
|
|
@@ -14649,7 +14894,7 @@ class BridgePool {
|
|
|
14649
14894
|
let oldestDir = null;
|
|
14650
14895
|
let oldestTime = Infinity;
|
|
14651
14896
|
for (const [dir, entry] of this.bridges) {
|
|
14652
|
-
if (entry.bridge.hasPendingRequests())
|
|
14897
|
+
if (entry.bridge.hasPendingRequests() || entry.bridge.hasOutstandingBackgroundTasks())
|
|
14653
14898
|
continue;
|
|
14654
14899
|
if (entry.lastUsed < oldestTime) {
|
|
14655
14900
|
oldestTime = entry.lastUsed;
|
|
@@ -15455,7 +15700,7 @@ import {
|
|
|
15455
15700
|
// package.json
|
|
15456
15701
|
var package_default = {
|
|
15457
15702
|
name: "@cortexkit/aft-pi",
|
|
15458
|
-
version: "0.
|
|
15703
|
+
version: "0.37.1",
|
|
15459
15704
|
type: "module",
|
|
15460
15705
|
description: "Pi coding agent extension for Agent File Tools (AFT) — tree-sitter and LSP-powered code analysis",
|
|
15461
15706
|
main: "dist/index.js",
|
|
@@ -15478,7 +15723,7 @@ var package_default = {
|
|
|
15478
15723
|
"test:unit": "bun test src/__tests__ --path-ignore-patterns 'src/__tests__/e2e/**'"
|
|
15479
15724
|
},
|
|
15480
15725
|
dependencies: {
|
|
15481
|
-
"@cortexkit/aft-bridge": "0.
|
|
15726
|
+
"@cortexkit/aft-bridge": "0.37.1",
|
|
15482
15727
|
"@xterm/headless": "^5.5.0",
|
|
15483
15728
|
"comment-json": "^5.0.0",
|
|
15484
15729
|
diff: "^8.0.4",
|
|
@@ -15486,12 +15731,12 @@ var package_default = {
|
|
|
15486
15731
|
zod: "^4.1.8"
|
|
15487
15732
|
},
|
|
15488
15733
|
optionalDependencies: {
|
|
15489
|
-
"@cortexkit/aft-darwin-arm64": "0.
|
|
15490
|
-
"@cortexkit/aft-darwin-x64": "0.
|
|
15491
|
-
"@cortexkit/aft-linux-arm64": "0.
|
|
15492
|
-
"@cortexkit/aft-linux-x64": "0.
|
|
15493
|
-
"@cortexkit/aft-win32-arm64": "0.
|
|
15494
|
-
"@cortexkit/aft-win32-x64": "0.
|
|
15734
|
+
"@cortexkit/aft-darwin-arm64": "0.37.1",
|
|
15735
|
+
"@cortexkit/aft-darwin-x64": "0.37.1",
|
|
15736
|
+
"@cortexkit/aft-linux-arm64": "0.37.1",
|
|
15737
|
+
"@cortexkit/aft-linux-x64": "0.37.1",
|
|
15738
|
+
"@cortexkit/aft-win32-arm64": "0.37.1",
|
|
15739
|
+
"@cortexkit/aft-win32-x64": "0.37.1"
|
|
15495
15740
|
},
|
|
15496
15741
|
devDependencies: {
|
|
15497
15742
|
"@earendil-works/pi-coding-agent": "*",
|
|
@@ -15749,19 +15994,6 @@ function isEmptyParam(value) {
|
|
|
15749
15994
|
return Object.keys(value).length === 0;
|
|
15750
15995
|
return false;
|
|
15751
15996
|
}
|
|
15752
|
-
var LONG_RUNNING_COMMAND_TIMEOUT_MS = {
|
|
15753
|
-
callers: 60000,
|
|
15754
|
-
trace_to: 60000,
|
|
15755
|
-
trace_to_symbol: 60000,
|
|
15756
|
-
trace_data: 60000,
|
|
15757
|
-
impact: 60000,
|
|
15758
|
-
grep: 60000,
|
|
15759
|
-
glob: 60000,
|
|
15760
|
-
semantic_search: 60000
|
|
15761
|
-
};
|
|
15762
|
-
function timeoutForCommand(command) {
|
|
15763
|
-
return LONG_RUNNING_COMMAND_TIMEOUT_MS[command];
|
|
15764
|
-
}
|
|
15765
15997
|
function asPlainObject(value) {
|
|
15766
15998
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
15767
15999
|
return;
|
|
@@ -32282,6 +32514,21 @@ function registerShutdownCleanup(fn) {
|
|
|
32282
32514
|
};
|
|
32283
32515
|
}
|
|
32284
32516
|
|
|
32517
|
+
// src/sync-watch-abort.ts
|
|
32518
|
+
var abortFlags = new Map;
|
|
32519
|
+
function signalSyncWatchAbort(sessionID) {
|
|
32520
|
+
const key = sessionID || "__default__";
|
|
32521
|
+
abortFlags.set(key, true);
|
|
32522
|
+
}
|
|
32523
|
+
function clearSyncWatchAbort(sessionID) {
|
|
32524
|
+
const key = sessionID || "__default__";
|
|
32525
|
+
abortFlags.delete(key);
|
|
32526
|
+
}
|
|
32527
|
+
function isSyncWatchAborted(sessionID) {
|
|
32528
|
+
const key = sessionID || "__default__";
|
|
32529
|
+
return abortFlags.get(key) === true;
|
|
32530
|
+
}
|
|
32531
|
+
|
|
32285
32532
|
// src/tools/ast.ts
|
|
32286
32533
|
import { StringEnum } from "@earendil-works/pi-ai";
|
|
32287
32534
|
import { Type as Type3 } from "typebox";
|
|
@@ -32399,7 +32646,6 @@ function formatDiffForPi(oldContent, newContent, contextLines = DEFAULT_CONTEXT_
|
|
|
32399
32646
|
}
|
|
32400
32647
|
|
|
32401
32648
|
// src/tools/hoisted.ts
|
|
32402
|
-
var DIAGNOSTICS_PARAM_DESCRIPTION = "When true, wait up to 3 seconds for fresh LSP diagnostics on the edited file and include them in the result. Defaults to the configured `lsp.diagnostics_on_edit` value (false unless configured); per-call true/false overrides. Use aft_inspect to check diagnostics across a batch of edits or before tests/commits.";
|
|
32403
32649
|
function diagnosticsOnEditDefault(ctx) {
|
|
32404
32650
|
return ctx.config.lsp?.diagnostics_on_edit ?? false;
|
|
32405
32651
|
}
|
|
@@ -32469,8 +32715,7 @@ var WriteParams = Type2.Object({
|
|
|
32469
32715
|
filePath: Type2.String({
|
|
32470
32716
|
description: "Path to the file to write (absolute or relative to project root)"
|
|
32471
32717
|
}),
|
|
32472
|
-
content: Type2.String({ description: "Full file contents to write" })
|
|
32473
|
-
diagnostics: Type2.Optional(Type2.Boolean({ description: DIAGNOSTICS_PARAM_DESCRIPTION }))
|
|
32718
|
+
content: Type2.String({ description: "Full file contents to write" })
|
|
32474
32719
|
});
|
|
32475
32720
|
var EditParams = Type2.Object({
|
|
32476
32721
|
filePath: Type2.String({
|
|
@@ -32482,8 +32727,7 @@ var EditParams = Type2.Object({
|
|
|
32482
32727
|
occurrence: optionalInt(0, Number.MAX_SAFE_INTEGER),
|
|
32483
32728
|
appendContent: Type2.Optional(Type2.String({
|
|
32484
32729
|
description: "Append text to the end of the file (creates the file if missing, parent dirs auto-created). When set, oldString/newString are ignored."
|
|
32485
|
-
}))
|
|
32486
|
-
diagnostics: Type2.Optional(Type2.Boolean({ description: DIAGNOSTICS_PARAM_DESCRIPTION }))
|
|
32730
|
+
}))
|
|
32487
32731
|
});
|
|
32488
32732
|
var GrepParams = Type2.Object({
|
|
32489
32733
|
pattern: Type2.String({ description: "Regex pattern to search for" }),
|
|
@@ -32491,8 +32735,7 @@ var GrepParams = Type2.Object({
|
|
|
32491
32735
|
description: "Path scope (file or directory; absolute or relative to project root)"
|
|
32492
32736
|
})),
|
|
32493
32737
|
include: Type2.Optional(Type2.String({ description: "Glob filter for included files (e.g. '*.ts,*.tsx')" })),
|
|
32494
|
-
caseSensitive: Type2.Optional(Type2.Boolean({ description: "Case-sensitive matching" }))
|
|
32495
|
-
contextLines: optionalInt(1, Number.MAX_SAFE_INTEGER)
|
|
32738
|
+
caseSensitive: Type2.Optional(Type2.Boolean({ description: "Case-sensitive matching" }))
|
|
32496
32739
|
});
|
|
32497
32740
|
function registerHoistedTools(pi, ctx, surface) {
|
|
32498
32741
|
if (surface.hoistRead) {
|
|
@@ -32507,7 +32750,9 @@ function registerHoistedTools(pi, ctx, surface) {
|
|
|
32507
32750
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
32508
32751
|
const offset = coerceOptionalInt(params.offset, "offset", 1, Number.MAX_SAFE_INTEGER);
|
|
32509
32752
|
const limit = coerceOptionalInt(params.limit, "limit", 1, Number.MAX_SAFE_INTEGER);
|
|
32510
|
-
const req = {
|
|
32753
|
+
const req = {
|
|
32754
|
+
file: await resolvePathArg(extCtx.cwd, params.path)
|
|
32755
|
+
};
|
|
32511
32756
|
if (offset !== undefined) {
|
|
32512
32757
|
req.start_line = offset;
|
|
32513
32758
|
if (limit !== undefined) {
|
|
@@ -32534,19 +32779,20 @@ function registerHoistedTools(pi, ctx, surface) {
|
|
|
32534
32779
|
pi.registerTool({
|
|
32535
32780
|
name: "write",
|
|
32536
32781
|
label: "write",
|
|
32537
|
-
description: "Write a file
|
|
32538
|
-
promptSnippet: "Create or overwrite files (uses filePath; auto-formats
|
|
32782
|
+
description: "Write content to a file, creating it and parent directories automatically. Existing files are backed up before overwriting (undo via aft_safety) and auto-formatted when the project has a formatter configured. Uses `filePath` (not `path`). For partial edits, use the `edit` tool.",
|
|
32783
|
+
promptSnippet: "Create or overwrite files (uses filePath; auto-formats)",
|
|
32539
32784
|
promptGuidelines: ["Use write only for new files or complete rewrites."],
|
|
32540
32785
|
parameters: WriteParams,
|
|
32541
32786
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
32542
|
-
await
|
|
32787
|
+
const filePath = await resolvePathArg(extCtx.cwd, params.filePath);
|
|
32788
|
+
await assertExternalDirectoryPermission(extCtx, filePath, "modify", {
|
|
32543
32789
|
restrictToProjectRoot: surface.restrictToProjectRoot
|
|
32544
32790
|
});
|
|
32545
32791
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
32546
32792
|
const response = await callBridge(bridge, "write", {
|
|
32547
|
-
file:
|
|
32793
|
+
file: filePath,
|
|
32548
32794
|
content: params.content,
|
|
32549
|
-
diagnostics:
|
|
32795
|
+
diagnostics: diagnosticsOnEditDefault(ctx),
|
|
32550
32796
|
include_diff_content: true
|
|
32551
32797
|
}, extCtx);
|
|
32552
32798
|
return buildMutationResult(response);
|
|
@@ -32563,8 +32809,8 @@ function registerHoistedTools(pi, ctx, surface) {
|
|
|
32563
32809
|
pi.registerTool({
|
|
32564
32810
|
name: "edit",
|
|
32565
32811
|
label: "edit",
|
|
32566
|
-
description: "Find-and-replace edit with progressive fuzzy matching (handles whitespace and Unicode drift). Uses `filePath`, `oldString`, `newString`. Errors on multiple matches — use `occurrence` to pick one, or `replaceAll: true`.
|
|
32567
|
-
promptSnippet: "Targeted find-and-replace (uses filePath/oldString/newString; occurrence or replaceAll for disambiguation; fuzzy whitespace matching). Pass appendContent to append to a file (creates if missing).
|
|
32812
|
+
description: "Find-and-replace edit with progressive fuzzy matching (handles whitespace and Unicode drift). Uses `filePath`, `oldString`, `newString`. Errors on multiple matches — use `occurrence` to pick one, or `replaceAll: true`.",
|
|
32813
|
+
promptSnippet: "Targeted find-and-replace (uses filePath/oldString/newString; occurrence or replaceAll for disambiguation; fuzzy whitespace matching). Pass appendContent to append to a file (creates if missing).",
|
|
32568
32814
|
promptGuidelines: [
|
|
32569
32815
|
"Prefer edit over write when changing part of an existing file.",
|
|
32570
32816
|
"Include enough surrounding context in oldString to make the match unique, or set replaceAll/occurrence explicitly.",
|
|
@@ -32572,26 +32818,27 @@ function registerHoistedTools(pi, ctx, surface) {
|
|
|
32572
32818
|
],
|
|
32573
32819
|
parameters: EditParams,
|
|
32574
32820
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
32575
|
-
await
|
|
32821
|
+
const filePath = await resolvePathArg(extCtx.cwd, params.filePath);
|
|
32822
|
+
await assertExternalDirectoryPermission(extCtx, filePath, "modify", {
|
|
32576
32823
|
restrictToProjectRoot: surface.restrictToProjectRoot
|
|
32577
32824
|
});
|
|
32578
32825
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
32579
32826
|
if (typeof params.appendContent === "string") {
|
|
32580
32827
|
const req2 = {
|
|
32581
32828
|
op: "append",
|
|
32582
|
-
file:
|
|
32829
|
+
file: filePath,
|
|
32583
32830
|
append_content: params.appendContent,
|
|
32584
|
-
diagnostics:
|
|
32831
|
+
diagnostics: diagnosticsOnEditDefault(ctx),
|
|
32585
32832
|
include_diff_content: true
|
|
32586
32833
|
};
|
|
32587
32834
|
const response2 = await callBridge(bridge, "edit_match", req2, extCtx);
|
|
32588
32835
|
return buildMutationResult(response2);
|
|
32589
32836
|
}
|
|
32590
32837
|
const req = {
|
|
32591
|
-
file:
|
|
32838
|
+
file: filePath,
|
|
32592
32839
|
match: params.oldString ?? "",
|
|
32593
32840
|
replacement: params.newString ?? "",
|
|
32594
|
-
diagnostics:
|
|
32841
|
+
diagnostics: diagnosticsOnEditDefault(ctx),
|
|
32595
32842
|
include_diff_content: true
|
|
32596
32843
|
};
|
|
32597
32844
|
if (params.replaceAll === true)
|
|
@@ -32631,9 +32878,6 @@ function registerHoistedTools(pi, ctx, surface) {
|
|
|
32631
32878
|
req.include = splitIncludeGlobs(params.include);
|
|
32632
32879
|
if (params.caseSensitive !== undefined)
|
|
32633
32880
|
req.case_sensitive = params.caseSensitive;
|
|
32634
|
-
const contextLines = coerceOptionalInt(params.contextLines, "contextLines", 1, Number.MAX_SAFE_INTEGER);
|
|
32635
|
-
if (contextLines !== undefined)
|
|
32636
|
-
req.context_lines = contextLines;
|
|
32637
32881
|
const response = await callBridge(bridge, "grep", req, extCtx);
|
|
32638
32882
|
const text = response.text ?? "";
|
|
32639
32883
|
return textResult(text);
|
|
@@ -33194,7 +33438,7 @@ function registerAstTools(pi, ctx, surface) {
|
|
|
33194
33438
|
if (!isEmptyParam(params.globs))
|
|
33195
33439
|
req.globs = params.globs;
|
|
33196
33440
|
if (params.contextLines !== undefined)
|
|
33197
|
-
req.
|
|
33441
|
+
req.context = params.contextLines;
|
|
33198
33442
|
const response = await callBridge(bridge, "ast_search", req, extCtx);
|
|
33199
33443
|
return textResult(response.text ?? JSON.stringify(response));
|
|
33200
33444
|
},
|
|
@@ -33348,22 +33592,28 @@ function getBashSpawnHook(pi) {
|
|
|
33348
33592
|
}
|
|
33349
33593
|
return api2.hooks?.bashSpawn;
|
|
33350
33594
|
}
|
|
33351
|
-
function registerBashTool(pi, ctx) {
|
|
33595
|
+
function registerBashTool(pi, ctx, aftSearchRegistered = false) {
|
|
33352
33596
|
const spawnHook = getBashSpawnHook(pi);
|
|
33597
|
+
const searchSteer = aftSearchRegistered ? "use `aft_search` (concepts, identifiers, regex, literals), `read`, `aft_outline`, or `aft_zoom` instead" : "use the `grep` tool, `read`, `aft_outline`, or `aft_zoom` instead";
|
|
33598
|
+
const bashCfg = resolveBashConfig(ctx.config);
|
|
33599
|
+
const compressionSentence = bashCfg.compress ? " Output is compressed by default; pass `compressed: false` for raw output." : "";
|
|
33600
|
+
const tasksSentence = bashCfg.background ? ' Pass `background: true` to run in the background and get a task_id for `bash_status`/`bash_kill`. Pass `pty: true` for interactive programs (REPLs, TUIs) and drive them with `bash_status({ output_mode: "screen" })` plus `bash_write`.' : " Commands that outlive the foreground wait window are promoted to background tasks; inspect them with `bash_status({ task_id })` or terminate with `bash_kill`.";
|
|
33353
33601
|
pi.registerTool({
|
|
33354
33602
|
name: "bash",
|
|
33355
33603
|
label: "bash",
|
|
33356
|
-
description:
|
|
33604
|
+
description: `Execute shell commands.${compressionSentence}${tasksSentence}
|
|
33605
|
+
|
|
33606
|
+
DO NOT use bash for code search or code exploration. If you are about to run grep, rg, sed, awk, find, or cat through bash to locate or read code: STOP — ${searchSteer}.`,
|
|
33357
33607
|
promptSnippet: "Run shell commands (timeout in milliseconds; supports workdir, background tasks, compressed output, PTY mode)",
|
|
33358
33608
|
promptGuidelines: [
|
|
33359
|
-
|
|
33609
|
+
`DO NOT use bash for code search or exploration — ${searchSteer}.`,
|
|
33360
33610
|
"Set compressed: false when you need ANSI color codes in the output."
|
|
33361
33611
|
],
|
|
33362
33612
|
parameters: BashParams,
|
|
33363
33613
|
async execute(_toolCallId, params, _signal, onUpdate, extCtx) {
|
|
33364
33614
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
33365
|
-
const
|
|
33366
|
-
const foregroundWaitMs = resolveForegroundWaitMs(
|
|
33615
|
+
const bashCfg2 = resolveBashConfig(ctx.config);
|
|
33616
|
+
const foregroundWaitMs = resolveForegroundWaitMs(bashCfg2.foreground_wait_window_ms);
|
|
33367
33617
|
const timeout = coerceOptionalInt(params.timeout, "timeout", 1, Number.MAX_SAFE_INTEGER);
|
|
33368
33618
|
const ptyRows = coerceOptionalInt(params.ptyRows, "ptyRows", 1, 60);
|
|
33369
33619
|
const ptyCols = coerceOptionalInt(params.ptyCols, "ptyCols", 1, 140);
|
|
@@ -33380,7 +33630,7 @@ function registerBashTool(pi, ctx) {
|
|
|
33380
33630
|
throw new Error(`BashSpawnHook failed: ${hookErr instanceof Error ? hookErr.message : String(hookErr)}`);
|
|
33381
33631
|
}
|
|
33382
33632
|
}
|
|
33383
|
-
const compressionEnabled =
|
|
33633
|
+
const compressionEnabled = bashCfg2.compress && params.compressed !== false;
|
|
33384
33634
|
const pipeStrip = maybeStripCompressorPipe(spawnContext.command, compressionEnabled);
|
|
33385
33635
|
const bridgeCommand = pipeStrip.command;
|
|
33386
33636
|
let streamed = "";
|
|
@@ -33425,7 +33675,7 @@ function registerBashTool(pi, ctx) {
|
|
|
33425
33675
|
throw new Error(status.message ?? "bash_status failed");
|
|
33426
33676
|
}
|
|
33427
33677
|
if (isTerminalStatus(status.status)) {
|
|
33428
|
-
return bashResult(appendPipeStripNote(withBashHints(formatForegroundResult(status)), pipeStrip.note), {
|
|
33678
|
+
return bashResult(appendPipeStripNote(withBashHints(formatForegroundResult(status), bridgeCommand, aftSearchRegistered), pipeStrip.note), {
|
|
33429
33679
|
exit_code: status.exit_code,
|
|
33430
33680
|
duration_ms: status.duration_ms,
|
|
33431
33681
|
truncated: status.output_truncated,
|
|
@@ -33452,7 +33702,7 @@ function registerBashTool(pi, ctx) {
|
|
|
33452
33702
|
task_id: taskId
|
|
33453
33703
|
};
|
|
33454
33704
|
const output = response.output ?? "";
|
|
33455
|
-
return bashResult(appendPipeStripNote(withBashHints(output), pipeStrip.note), details);
|
|
33705
|
+
return bashResult(appendPipeStripNote(withBashHints(output, bridgeCommand, aftSearchRegistered), pipeStrip.note), details);
|
|
33456
33706
|
},
|
|
33457
33707
|
renderCall(args, theme, context) {
|
|
33458
33708
|
return renderBashCall(args?.command, args?.description, theme, context);
|
|
@@ -33484,8 +33734,8 @@ function formatPromotionMessage(taskId, timeout, waitWindowMs) {
|
|
|
33484
33734
|
function formatSeconds(ms) {
|
|
33485
33735
|
return `${Number((ms / 1000).toFixed(1))}s`;
|
|
33486
33736
|
}
|
|
33487
|
-
function withBashHints(output) {
|
|
33488
|
-
return maybeAppendConflictsHint(output);
|
|
33737
|
+
function withBashHints(output, command, aftSearchRegistered) {
|
|
33738
|
+
return maybeAppendGrepSearchHint(maybeAppendConflictsHint(output), command, aftSearchRegistered);
|
|
33489
33739
|
}
|
|
33490
33740
|
function formatForegroundResult(data) {
|
|
33491
33741
|
const output = data.output_preview ?? "";
|
|
@@ -33567,11 +33817,41 @@ function createBashWatchTool(ctx) {
|
|
|
33567
33817
|
A notification will fire when the pattern matches or the task exits.`, watchDetails);
|
|
33568
33818
|
}
|
|
33569
33819
|
const data = await waitForBashStatus(ctx, bridge, extCtx, params.task_id, undefined, waitFor, true, Math.min(coerceOptionalInt(params.timeout_ms, "timeout_ms", 1, MAX_BASH_STATUS_WAIT_TIMEOUT_MS) ?? DEFAULT_BASH_STATUS_WAIT_TIMEOUT_MS, MAX_BASH_STATUS_WAIT_TIMEOUT_MS));
|
|
33820
|
+
if (data.waited?.reason === "user_message") {
|
|
33821
|
+
const convertedText = await convertToAsyncWatchOnAbort(bridge, extCtx, params.task_id, waitFor, params.once !== false);
|
|
33822
|
+
return textResult(convertedText, { waited: data.waited });
|
|
33823
|
+
}
|
|
33570
33824
|
const text = await formatBashStatus(extCtx, params.task_id, data, undefined);
|
|
33571
33825
|
return textResult(text, data);
|
|
33572
33826
|
}
|
|
33573
33827
|
};
|
|
33574
33828
|
}
|
|
33829
|
+
async function convertToAsyncWatchOnAbort(bridge, extCtx, taskId, waitFor, once) {
|
|
33830
|
+
if (!waitFor) {
|
|
33831
|
+
return `Sync watch for task ${taskId} was interrupted because you sent a message. ` + `The task is still running in the background. A completion reminder will be ` + `delivered automatically when the task exits; don't poll bash_status.`;
|
|
33832
|
+
}
|
|
33833
|
+
const notifyParams = {
|
|
33834
|
+
task_id: taskId,
|
|
33835
|
+
once
|
|
33836
|
+
};
|
|
33837
|
+
if (waitFor.kind === "regex")
|
|
33838
|
+
notifyParams.regex = waitFor.source;
|
|
33839
|
+
else
|
|
33840
|
+
notifyParams.pattern = waitFor.value;
|
|
33841
|
+
const sessionId = resolveSessionId(extCtx);
|
|
33842
|
+
markExplicitControl(sessionId, taskId, false);
|
|
33843
|
+
try {
|
|
33844
|
+
const registered = await callBashBridge(bridge, "bash_notify", notifyParams, extCtx);
|
|
33845
|
+
if (registered.success === false) {
|
|
33846
|
+
unmarkExplicitControl(sessionId, taskId);
|
|
33847
|
+
return `Sync watch for task ${taskId} was interrupted because you sent a message. ` + `Auto-registering an async watch failed (${String(registered.message ?? "unknown error")}). ` + `The task is still running in the background. A completion reminder will be ` + `delivered automatically when the task exits.`;
|
|
33848
|
+
}
|
|
33849
|
+
return `Sync watch for task ${taskId} was interrupted because you sent a message. ` + `The wait has been converted to an async watch (${registered.watch_id}). ` + `A notification will fire when the pattern matches or the task exits.`;
|
|
33850
|
+
} catch (err) {
|
|
33851
|
+
unmarkExplicitControl(sessionId, taskId);
|
|
33852
|
+
return `Sync watch for task ${taskId} was interrupted because you sent a message. ` + `Auto-registering an async watch failed (${err instanceof Error ? err.message : String(err)}). ` + `The task is still running in the background. A completion reminder will be ` + `delivered automatically when the task exits.`;
|
|
33853
|
+
}
|
|
33854
|
+
}
|
|
33575
33855
|
function createBashWriteTool(ctx) {
|
|
33576
33856
|
return {
|
|
33577
33857
|
name: "bash_write",
|
|
@@ -33647,6 +33927,7 @@ async function waitForBashStatus(ctx, bridge, extCtx, taskId, outputMode, waitFo
|
|
|
33647
33927
|
transportTimeoutMs: BASH_TRANSPORT_TIMEOUT_MS
|
|
33648
33928
|
};
|
|
33649
33929
|
const sessionId = resolveSessionId(extCtx);
|
|
33930
|
+
clearSyncWatchAbort(sessionId);
|
|
33650
33931
|
if (waitForExit)
|
|
33651
33932
|
markTaskWaiting(sessionId, taskId);
|
|
33652
33933
|
let sawTerminal = false;
|
|
@@ -33685,6 +33966,9 @@ async function waitForBashStatus(ctx, bridge, extCtx, taskId, outputMode, waitFo
|
|
|
33685
33966
|
}
|
|
33686
33967
|
return withWaited(data, { reason: "exited", elapsed_ms: Date.now() - startedAt });
|
|
33687
33968
|
}
|
|
33969
|
+
if (isSyncWatchAborted(sessionId)) {
|
|
33970
|
+
return withWaited(data, { reason: "user_message", elapsed_ms: Date.now() - startedAt });
|
|
33971
|
+
}
|
|
33688
33972
|
if (Date.now() >= deadline) {
|
|
33689
33973
|
return withWaited(data, { reason: "timeout", elapsed_ms: Date.now() - startedAt });
|
|
33690
33974
|
}
|
|
@@ -33953,8 +34237,12 @@ function registerConflictsTool(pi, ctx) {
|
|
|
33953
34237
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
33954
34238
|
const reqParams = {};
|
|
33955
34239
|
const path2 = params?.path;
|
|
33956
|
-
if (typeof path2 === "string" && path2.trim() !== "")
|
|
33957
|
-
|
|
34240
|
+
if (typeof path2 === "string" && path2.trim() !== "") {
|
|
34241
|
+
await assertExternalDirectoryPermission(extCtx, path2, "inspect", {
|
|
34242
|
+
restrictToProjectRoot: ctx.config.restrict_to_project_root ?? false
|
|
34243
|
+
});
|
|
34244
|
+
reqParams.path = await resolvePathArg(extCtx.cwd, path2);
|
|
34245
|
+
}
|
|
33958
34246
|
const response = await callBridge(bridge, "git_conflicts", reqParams, extCtx);
|
|
33959
34247
|
return textResult(response.text ?? JSON.stringify(response, null, 2));
|
|
33960
34248
|
},
|
|
@@ -35247,7 +35535,7 @@ function registerRefactorTool(pi, ctx) {
|
|
|
35247
35535
|
pi.registerTool({
|
|
35248
35536
|
name: "aft_refactor",
|
|
35249
35537
|
label: "refactor",
|
|
35250
|
-
description: "Workspace-wide refactoring that updates imports and references across files. `move` relocates a top-level symbol; `extract` pulls a line range into a new function
|
|
35538
|
+
description: "Workspace-wide refactoring that updates imports and references across files. `move` relocates a top-level symbol (not nested functions or class methods) to another file, rewriting imports workspace-wide; a checkpoint is created first. To move/rename a whole file, use aft_move. `extract` pulls a line range into a new function (TS/JS/TSX, Python). `inline` replaces a call with the function's body.",
|
|
35251
35539
|
parameters: RefactorParams,
|
|
35252
35540
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
35253
35541
|
const commandMap = {
|
|
@@ -35626,21 +35914,9 @@ function registerSemanticTool(pi, ctx) {
|
|
|
35626
35914
|
name: "aft_search",
|
|
35627
35915
|
label: "search",
|
|
35628
35916
|
description: [
|
|
35629
|
-
"
|
|
35917
|
+
"Search code with one tool: concepts, identifiers, error strings, regex, literals, and filenames are auto-routed to the right engine and returned ranked. Use it for any code search — including when you only know what the code does, not what it's named ('where is rate limiting handled', 'retry logic', '^export', 'Cargo.lock').",
|
|
35630
35918
|
"",
|
|
35631
|
-
"
|
|
35632
|
-
"- Exploring an unfamiliar area: 'where is rate limiting handled', 'how does auth flow work'",
|
|
35633
|
-
"- Concept doesn't appear as a literal string: 'retry logic', 'cache invalidation', 'graceful shutdown'",
|
|
35634
|
-
"- Filename-shaped concepts: 'the bridge spawn helper', 'the session detection module'",
|
|
35635
|
-
"- Regex-shaped or exact text queries when you want AFT to classify and route automatically",
|
|
35636
|
-
"- You know roughly what the function does but not what it's named",
|
|
35637
|
-
"",
|
|
35638
|
-
"When NOT to use:",
|
|
35639
|
-
"- You need exhaustive literal enumeration → use grep directly",
|
|
35640
|
-
"- You want the file/module structure → use aft_outline",
|
|
35641
|
-
"- You're following a call chain → use aft_callgraph",
|
|
35642
|
-
"",
|
|
35643
|
-
"Set hint to 'regex', 'literal', 'semantic', or 'auto' to override or document routing intent."
|
|
35919
|
+
"Set hint to 'regex', 'literal', or 'semantic' to force a lane."
|
|
35644
35920
|
].join(`
|
|
35645
35921
|
`),
|
|
35646
35922
|
parameters: SearchParams2,
|
|
@@ -35671,138 +35947,8 @@ ${extra}`;
|
|
|
35671
35947
|
});
|
|
35672
35948
|
}
|
|
35673
35949
|
|
|
35674
|
-
// src/tools/structure.ts
|
|
35675
|
-
import { StringEnum as StringEnum6 } from "@earendil-works/pi-ai";
|
|
35676
|
-
import { Type as Type14 } from "typebox";
|
|
35677
|
-
var TransformParams = Type14.Object({
|
|
35678
|
-
op: StringEnum6(["add_member", "add_derive", "wrap_try_catch", "add_decorator", "add_struct_tags"], { description: "Transformation operation" }),
|
|
35679
|
-
filePath: Type14.String({
|
|
35680
|
-
description: "Path to the source file (absolute or relative to project root)"
|
|
35681
|
-
}),
|
|
35682
|
-
container: Type14.Optional(Type14.String({ description: "Class/struct/impl name for add_member" })),
|
|
35683
|
-
code: Type14.Optional(Type14.String({ description: "Member code to insert (add_member)" })),
|
|
35684
|
-
target: Type14.Optional(Type14.String({ description: "Target symbol name" })),
|
|
35685
|
-
derives: Type14.Optional(Type14.Array(Type14.String(), { description: "Derive macro names (add_derive)" })),
|
|
35686
|
-
catchBody: Type14.Optional(Type14.String({ description: "Catch block body (wrap_try_catch, default: 'throw error;')" })),
|
|
35687
|
-
decorator: Type14.Optional(Type14.String({ description: "Decorator text without @ (add_decorator)" })),
|
|
35688
|
-
field: Type14.Optional(Type14.String({ description: "Struct field name (add_struct_tags)" })),
|
|
35689
|
-
tag: Type14.Optional(Type14.String({ description: "Tag key (add_struct_tags)" })),
|
|
35690
|
-
value: Type14.Optional(Type14.String({ description: "Tag value (add_struct_tags)" })),
|
|
35691
|
-
position: Type14.Optional(Type14.String({
|
|
35692
|
-
description: "Position hint: 'first', 'last' (default), 'before:name', 'after:name' for add_member"
|
|
35693
|
-
})),
|
|
35694
|
-
validate: Type14.Optional(StringEnum6(["syntax", "full"], {
|
|
35695
|
-
description: "Validation level (default: syntax)"
|
|
35696
|
-
}))
|
|
35697
|
-
});
|
|
35698
|
-
function buildTransformSections(args, payload, theme) {
|
|
35699
|
-
const response = asRecord2(payload);
|
|
35700
|
-
if (!response)
|
|
35701
|
-
return [theme.fg("muted", "No transform result.")];
|
|
35702
|
-
const target = asString(response.target) ?? asString(response.scope) ?? args.target ?? args.container ?? args.field ?? args.filePath;
|
|
35703
|
-
return [
|
|
35704
|
-
`${theme.fg("success", "transformed")} ${theme.fg("accent", args.op)}`,
|
|
35705
|
-
`${theme.fg("muted", "file")} ${theme.fg("accent", asString(response.file) ?? args.filePath)}`,
|
|
35706
|
-
target ? `${theme.fg("muted", "target")} ${target}` : theme.fg("muted", "No target metadata.")
|
|
35707
|
-
];
|
|
35708
|
-
}
|
|
35709
|
-
function renderTransformCall(args, theme, context) {
|
|
35710
|
-
const target = args.target ?? args.container ?? args.field;
|
|
35711
|
-
const summary = [
|
|
35712
|
-
theme.fg("accent", args.op),
|
|
35713
|
-
accentPath(theme, args.filePath),
|
|
35714
|
-
target ? theme.fg("toolOutput", target) : undefined
|
|
35715
|
-
].filter(Boolean).join(" ");
|
|
35716
|
-
return renderToolCall("transform", summary, theme, context);
|
|
35717
|
-
}
|
|
35718
|
-
function renderTransformResult(result, args, theme, context) {
|
|
35719
|
-
if (context.isError)
|
|
35720
|
-
return renderErrorResult(result, "transform failed", theme, context);
|
|
35721
|
-
return renderSections(buildTransformSections(args, extractStructuredPayload(result), theme), context);
|
|
35722
|
-
}
|
|
35723
|
-
function registerStructureTool(pi, ctx) {
|
|
35724
|
-
pi.registerTool({
|
|
35725
|
-
name: "aft_transform",
|
|
35726
|
-
label: "transform",
|
|
35727
|
-
description: "Scope-aware structural code transformations with correct indentation. Use aft_safety checkpoint/undo before risky transforms.",
|
|
35728
|
-
parameters: TransformParams,
|
|
35729
|
-
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
35730
|
-
validateTransformParams(params);
|
|
35731
|
-
const filePath = await resolvePathArg(extCtx.cwd, params.filePath);
|
|
35732
|
-
await assertExternalDirectoryPermission(extCtx, filePath, "modify", {
|
|
35733
|
-
restrictToProjectRoot: ctx.config.restrict_to_project_root ?? false
|
|
35734
|
-
});
|
|
35735
|
-
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
35736
|
-
const req = { file: filePath };
|
|
35737
|
-
if (params.container !== undefined)
|
|
35738
|
-
req.scope = params.container;
|
|
35739
|
-
if (params.code !== undefined)
|
|
35740
|
-
req.code = params.code;
|
|
35741
|
-
if (params.target !== undefined)
|
|
35742
|
-
req.target = params.target;
|
|
35743
|
-
if (params.derives !== undefined)
|
|
35744
|
-
req.derives = params.derives;
|
|
35745
|
-
if (params.catchBody !== undefined)
|
|
35746
|
-
req.catch_body = params.catchBody;
|
|
35747
|
-
if (params.decorator !== undefined)
|
|
35748
|
-
req.decorator = params.decorator;
|
|
35749
|
-
if (params.field !== undefined)
|
|
35750
|
-
req.field = params.field;
|
|
35751
|
-
if (params.tag !== undefined)
|
|
35752
|
-
req.tag = params.tag;
|
|
35753
|
-
if (params.value !== undefined)
|
|
35754
|
-
req.value = params.value;
|
|
35755
|
-
if (params.position !== undefined)
|
|
35756
|
-
req.position = params.position;
|
|
35757
|
-
if (params.validate !== undefined)
|
|
35758
|
-
req.validate = params.validate;
|
|
35759
|
-
const response = await callBridge(bridge, params.op, req, extCtx);
|
|
35760
|
-
return textResult(JSON.stringify(response, null, 2));
|
|
35761
|
-
},
|
|
35762
|
-
renderCall(args, theme, context) {
|
|
35763
|
-
return renderTransformCall(args, theme, context);
|
|
35764
|
-
},
|
|
35765
|
-
renderResult(result, _options, theme, context) {
|
|
35766
|
-
return renderTransformResult(result, context.args, theme, context);
|
|
35767
|
-
}
|
|
35768
|
-
});
|
|
35769
|
-
}
|
|
35770
|
-
function validateTransformParams(params) {
|
|
35771
|
-
const op = params.op;
|
|
35772
|
-
if (op === "add_member") {
|
|
35773
|
-
if (isEmptyParam(params.container)) {
|
|
35774
|
-
throw new Error("'container' is required for 'add_member' op");
|
|
35775
|
-
}
|
|
35776
|
-
if (isEmptyParam(params.code)) {
|
|
35777
|
-
throw new Error("'code' is required for 'add_member' op");
|
|
35778
|
-
}
|
|
35779
|
-
}
|
|
35780
|
-
if (op === "add_derive" || op === "wrap_try_catch" || op === "add_decorator" || op === "add_struct_tags") {
|
|
35781
|
-
if (isEmptyParam(params.target)) {
|
|
35782
|
-
throw new Error(`'target' is required for '${op}' op`);
|
|
35783
|
-
}
|
|
35784
|
-
}
|
|
35785
|
-
if (op === "add_derive" && isEmptyParam(params.derives)) {
|
|
35786
|
-
throw new Error("'derives' array is required for 'add_derive' op");
|
|
35787
|
-
}
|
|
35788
|
-
if (op === "add_decorator" && isEmptyParam(params.decorator)) {
|
|
35789
|
-
throw new Error("'decorator' is required for 'add_decorator' op");
|
|
35790
|
-
}
|
|
35791
|
-
if (op === "add_struct_tags") {
|
|
35792
|
-
if (isEmptyParam(params.field)) {
|
|
35793
|
-
throw new Error("'field' is required for 'add_struct_tags' op");
|
|
35794
|
-
}
|
|
35795
|
-
if (isEmptyParam(params.tag)) {
|
|
35796
|
-
throw new Error("'tag' is required for 'add_struct_tags' op");
|
|
35797
|
-
}
|
|
35798
|
-
if (isEmptyParam(params.value)) {
|
|
35799
|
-
throw new Error("'value' is required for 'add_struct_tags' op");
|
|
35800
|
-
}
|
|
35801
|
-
}
|
|
35802
|
-
}
|
|
35803
|
-
|
|
35804
35950
|
// src/workflow-hints.ts
|
|
35805
|
-
var HEADING = "##
|
|
35951
|
+
var HEADING = "## IMPORTANT NOTICE about your tools";
|
|
35806
35952
|
function buildWorkflowHints(opts) {
|
|
35807
35953
|
const sections = [];
|
|
35808
35954
|
const grepName = opts.hoistBuiltins ? "grep" : "aft_grep";
|
|
@@ -35816,14 +35962,28 @@ function buildWorkflowHints(opts) {
|
|
|
35816
35962
|
const hasBash = !opts.absentTools.has(bashName);
|
|
35817
35963
|
const hasBgBash = opts.bashBackgroundEnabled && hasBash && !opts.absentTools.has("bash_status");
|
|
35818
35964
|
if (hasBash && opts.bashCompressionEnabled) {
|
|
35819
|
-
sections.push(
|
|
35965
|
+
sections.push([
|
|
35966
|
+
"**Test/build output**: bash output is auto-compressed — failures and the summary are always kept. DO NOT pipe test/build commands through filters to summarize; that hides failures:",
|
|
35967
|
+
"- `bun test | grep fail` → run `bun test`",
|
|
35968
|
+
"- `cargo test 2>&1 | tail -20` → run `cargo test`",
|
|
35969
|
+
"- `npm run build | head -50` → run `npm run build`"
|
|
35970
|
+
].join(`
|
|
35971
|
+
`));
|
|
35820
35972
|
}
|
|
35821
35973
|
if (hasOutline && hasZoom) {
|
|
35822
35974
|
sections.push(`**Web/URL access**: \`aft_outline({ target: "<url>" })\` first for structure, then \`aft_zoom({ url: "<url>", symbols: "<heading>" })\` for the specific section.`);
|
|
35823
35975
|
}
|
|
35824
35976
|
if (hasOutline && hasZoom && (hasGrep || hasSearch)) {
|
|
35977
|
+
const searchName = hasSearch ? "aft_search" : grepName;
|
|
35825
35978
|
const locate = hasSearch ? '`aft_search` is the primary code-search tool: one call auto-routes concepts, identifiers, regex, error strings, and literals (pass `hint: "regex"`/`"literal"`/`"semantic"` to force a lane).' : `\`${grepName}\` (the tool — indexed and ranked) locates code.`;
|
|
35826
|
-
|
|
35979
|
+
const readName = opts.hoistBuiltins ? "read" : "aft_read";
|
|
35980
|
+
sections.push([
|
|
35981
|
+
`**Code exploration**: ${locate} Then \`aft_outline\` for structure → \`aft_zoom\` for symbol(s). DO NOT run \`grep\`/\`rg\`/\`find\`/\`sed\`/\`cat\` through \`bash\` to locate or read code — the bash path is unindexed, unranked, serial, and routinely surfaces the wrong hit. Keep \`bash\` for shell facts (git state, file metadata, processes). Reflex translations:`,
|
|
35982
|
+
`- \`grep -rn "handleAuth" src/\` in bash → \`${searchName}({ query: "handleAuth" })\``,
|
|
35983
|
+
`- \`find . -name "*.ts" | xargs grep watcher\` in bash → \`${searchName}({ query: "watcher invalidation" })\` (concepts work too)`,
|
|
35984
|
+
`- \`sed -n '100,160p' app.ts\` / \`cat app.ts\` in bash → \`${readName}({ filePath: "app.ts", startLine: 100, endLine: 160 })\``
|
|
35985
|
+
].join(`
|
|
35986
|
+
`));
|
|
35827
35987
|
}
|
|
35828
35988
|
if (hasInspect) {
|
|
35829
35989
|
sections.push("**Codebase health & diagnostics**: AFT does not surface compile/type errors automatically after edits — pull them with `aft_inspect`. Run it after a batch of edits and before you run tests or commit, when starting in unfamiliar code, or before a refactor/review. One call summarizes diagnostics (compile/type errors), TODOs, metrics, dead code, unused exports, and duplicates; pass `sections` for focused drill-down and `scope` to actively pull diagnostics for a specific file or directory. Its diagnostics are a fast checkpoint, not the authority — a clean `tsc` / `cargo check` / `pyright` run is the real gate. Treat `stale_categories` as a genuine stale-cache signal while an async Tier 2 refresh catches up.");
|
|
@@ -35841,13 +36001,22 @@ function buildWorkflowHints(opts) {
|
|
|
35841
36001
|
`));
|
|
35842
36002
|
}
|
|
35843
36003
|
if (hasBgBash) {
|
|
35844
|
-
sections.push(
|
|
36004
|
+
sections.push([
|
|
36005
|
+
`**Long-running commands** (builds, installs, full test suites): \`${bashName}({ background: true })\` returns a \`task_id\` immediately. Then do ONE of these:`,
|
|
36006
|
+
"1. Keep working on something independent of the result.",
|
|
36007
|
+
"2. End your turn — a completion reminder with the result arrives automatically.",
|
|
36008
|
+
`3. Need to react to a specific output line early? Register \`bash_watch({ task_id, pattern, background: true })\`, then end your turn — it pings you the moment the pattern appears.`,
|
|
36009
|
+
`\`bash_status({ task_id })\` is for inspecting output AFTER the reminder arrives, or one quick look at a live task — never call it repeatedly to wait: the reminder already delivers the result, and each poll wastes a turn. Sync \`bash_watch\` (without \`background: true\`) blocks your turn and locks the user out — reserve it for waits of a few seconds (a dev server printing its ready line), never for builds or test suites.`
|
|
36010
|
+
].join(`
|
|
36011
|
+
`));
|
|
35845
36012
|
sections.push(`**PTY / interactive commands**: PTY mode is for interactive REPLs and terminal apps (python, node, bash itself, vim). Start with \`${bashName}({ command: "python", pty: true, background: true })\`, read the screen with \`bash_status({ task_id, output_mode: "screen" })\`, and send input with \`bash_write({ task_id, input: "..." })\`.`);
|
|
35846
36013
|
}
|
|
35847
36014
|
if (sections.length === 0) {
|
|
35848
36015
|
return null;
|
|
35849
36016
|
}
|
|
35850
|
-
sections.unshift(
|
|
36017
|
+
sections.unshift(`You are equipped with a non-standard tool set: indexed code search, symbol-level reading, structural editing, and code analysis that are faster, more precise, and far cheaper in tokens than stitching together command-line utilities in bash. Always reach for these tools first.
|
|
36018
|
+
|
|
36019
|
+
**Parallel tool calls**: when several read-only operations are independent, emit them in ONE response instead of serializing — file reads, structure and symbol lookups, code search, diagnostics, and git status/diff/log. Sequence only when a call depends on a prior result or when a command mutates state.`);
|
|
35851
36020
|
return `${HEADING}
|
|
35852
36021
|
|
|
35853
36022
|
${sections.join(`
|
|
@@ -35939,21 +36108,15 @@ var PLUGIN_VERSION = (() => {
|
|
|
35939
36108
|
return "0.0.0";
|
|
35940
36109
|
}
|
|
35941
36110
|
})();
|
|
35942
|
-
var ANNOUNCEMENT_VERSION = "0.
|
|
36111
|
+
var ANNOUNCEMENT_VERSION = "0.37.0";
|
|
35943
36112
|
var ANNOUNCEMENT_FEATURES = [
|
|
35944
|
-
"
|
|
35945
|
-
"
|
|
35946
|
-
"
|
|
35947
|
-
"
|
|
36113
|
+
"Much lower background CPU: idle bridges shut down after 30 minutes, deleted project roots stop the watcher instead of spinning it, and background scans reuse warm caches on a bounded thread pool.",
|
|
36114
|
+
"`aft_transform` removed: usage data showed agents never called it — `edit` covers everything it did, and every request now carries a smaller tool surface.",
|
|
36115
|
+
"Leaner tools: `bash`, `write`, `edit`, `apply_patch`, `aft_search`, and `aft_refactor` descriptions trimmed and config-aware; system-prompt guidance rewritten around what to do instead of what to avoid.",
|
|
36116
|
+
"Background bash reminders are right-sized: failures keep head + tail context, successes show a short tail, and compressors never print a success summary for a non-zero exit."
|
|
35948
36117
|
];
|
|
35949
36118
|
var ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/DSa65w8wuf";
|
|
35950
|
-
var ALL_ONLY_TOOLS = new Set([
|
|
35951
|
-
"aft_callgraph",
|
|
35952
|
-
"aft_delete",
|
|
35953
|
-
"aft_move",
|
|
35954
|
-
"aft_transform",
|
|
35955
|
-
"aft_refactor"
|
|
35956
|
-
]);
|
|
36119
|
+
var ALL_ONLY_TOOLS = new Set(["aft_callgraph", "aft_delete", "aft_move", "aft_refactor"]);
|
|
35957
36120
|
var pendingEagerWarnings = new Map;
|
|
35958
36121
|
function isConfigureWarning(value) {
|
|
35959
36122
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
@@ -36031,7 +36194,6 @@ function resolveToolSurface(config2) {
|
|
|
36031
36194
|
move: false,
|
|
36032
36195
|
astSearch: false,
|
|
36033
36196
|
astReplace: false,
|
|
36034
|
-
structure: false,
|
|
36035
36197
|
refactor: false
|
|
36036
36198
|
};
|
|
36037
36199
|
}
|
|
@@ -36054,7 +36216,6 @@ function resolveToolSurface(config2) {
|
|
|
36054
36216
|
move: false,
|
|
36055
36217
|
astSearch: ok("ast_grep_search"),
|
|
36056
36218
|
astReplace: ok("ast_grep_replace"),
|
|
36057
|
-
structure: false,
|
|
36058
36219
|
refactor: false
|
|
36059
36220
|
};
|
|
36060
36221
|
if (surface === "all") {
|
|
@@ -36063,7 +36224,6 @@ function resolveToolSurface(config2) {
|
|
|
36063
36224
|
navigate: allOnly("aft_callgraph"),
|
|
36064
36225
|
delete: allOnly("aft_delete"),
|
|
36065
36226
|
move: allOnly("aft_move"),
|
|
36066
|
-
structure: allOnly("aft_transform"),
|
|
36067
36227
|
refactor: allOnly("aft_refactor")
|
|
36068
36228
|
};
|
|
36069
36229
|
}
|
|
@@ -36269,7 +36429,7 @@ ${lines}
|
|
|
36269
36429
|
}
|
|
36270
36430
|
const surface = resolveToolSurface(config2);
|
|
36271
36431
|
if (surface.hoistBash && resolveBashConfig(config2).enabled) {
|
|
36272
|
-
registerBashTool(pi, ctx);
|
|
36432
|
+
registerBashTool(pi, ctx, surface.semantic);
|
|
36273
36433
|
}
|
|
36274
36434
|
registerHoistedTools(pi, ctx, surface);
|
|
36275
36435
|
if (surface.outline || surface.zoom) {
|
|
@@ -36299,9 +36459,6 @@ ${lines}
|
|
|
36299
36459
|
if (surface.delete || surface.move) {
|
|
36300
36460
|
registerFsTools(pi, ctx, surface);
|
|
36301
36461
|
}
|
|
36302
|
-
if (surface.structure) {
|
|
36303
|
-
registerStructureTool(pi, ctx);
|
|
36304
|
-
}
|
|
36305
36462
|
if (surface.refactor) {
|
|
36306
36463
|
registerRefactorTool(pi, ctx);
|
|
36307
36464
|
}
|
|
@@ -36328,6 +36485,9 @@ ${lines}
|
|
|
36328
36485
|
runtime: pi
|
|
36329
36486
|
});
|
|
36330
36487
|
});
|
|
36488
|
+
pi.on("input", (_event, extCtx) => {
|
|
36489
|
+
signalSyncWatchAbort(resolveSessionId(extCtx));
|
|
36490
|
+
});
|
|
36331
36491
|
const unregisterShutdownCleanup = registerShutdownCleanup(async () => {
|
|
36332
36492
|
try {
|
|
36333
36493
|
await Promise.allSettled([abortInFlightAutoInstalls(), abortInFlightGithubInstalls()]);
|