@cortexkit/aft-pi 0.36.0 → 0.37.0
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 +611 -269
- package/dist/status-bar-inject.d.ts +1 -1
- package/dist/status-bar-inject.d.ts.map +1 -1
- package/dist/tools/_shared.d.ts +1 -11
- package/dist/tools/_shared.d.ts.map +1 -1
- package/dist/tools/bash.d.ts +1 -1
- 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/inspect.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
|
+
}
|
|
12574
|
+
}
|
|
12575
|
+
if (this.stdoutReadOffset === this.stdoutBuffer.length) {
|
|
12576
|
+
this.stdoutBuffer = "";
|
|
12577
|
+
this.stdoutReadOffset = 0;
|
|
12382
12578
|
}
|
|
12383
12579
|
}
|
|
12580
|
+
compactStdoutBuffer() {
|
|
12581
|
+
if (this.stdoutReadOffset === 0)
|
|
12582
|
+
return;
|
|
12583
|
+
this.stdoutBuffer = this.stdoutBuffer.slice(this.stdoutReadOffset);
|
|
12584
|
+
this.stdoutReadOffset = 0;
|
|
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}`);
|
|
@@ -12857,6 +13078,11 @@ function formatEditSummary(data) {
|
|
|
12857
13078
|
const n = data.files_modified;
|
|
12858
13079
|
return `Applied edits to ${n} file${n === 1 ? "" : "s"}.`;
|
|
12859
13080
|
}
|
|
13081
|
+
if (typeof data.total_files === "number") {
|
|
13082
|
+
const files = data.total_files;
|
|
13083
|
+
const reps = data.total_replacements ?? 0;
|
|
13084
|
+
return `Edited ${files} file${files === 1 ? "" : "s"} (${reps} replacement${reps === 1 ? "" : "s"}).`;
|
|
13085
|
+
}
|
|
12860
13086
|
const additions = data.diff?.additions ?? 0;
|
|
12861
13087
|
const deletions = data.diff?.deletions ?? 0;
|
|
12862
13088
|
const counts = `+${additions}/-${deletions}`;
|
|
@@ -14071,7 +14297,23 @@ function isProcessAlive(pid) {
|
|
|
14071
14297
|
}
|
|
14072
14298
|
}
|
|
14073
14299
|
// ../aft-bridge/dist/pipe-strip.js
|
|
14074
|
-
var NOISE_FILTERS = new Set([
|
|
14300
|
+
var NOISE_FILTERS = new Set([
|
|
14301
|
+
"grep",
|
|
14302
|
+
"rg",
|
|
14303
|
+
"head",
|
|
14304
|
+
"tail",
|
|
14305
|
+
"cat",
|
|
14306
|
+
"less",
|
|
14307
|
+
"more",
|
|
14308
|
+
"sed",
|
|
14309
|
+
"awk",
|
|
14310
|
+
"cut",
|
|
14311
|
+
"sort",
|
|
14312
|
+
"uniq",
|
|
14313
|
+
"tr",
|
|
14314
|
+
"column",
|
|
14315
|
+
"fold"
|
|
14316
|
+
]);
|
|
14075
14317
|
var GREP_GUARD_FLAGS = new Set([
|
|
14076
14318
|
"c",
|
|
14077
14319
|
"count",
|
|
@@ -14085,6 +14327,8 @@ var GREP_GUARD_FLAGS = new Set([
|
|
|
14085
14327
|
function maybeStripCompressorPipe(command, compressionEnabled) {
|
|
14086
14328
|
if (!compressionEnabled)
|
|
14087
14329
|
return { command, stripped: false };
|
|
14330
|
+
if (containsUnsplittableConstruct(command))
|
|
14331
|
+
return { command, stripped: false };
|
|
14088
14332
|
const chain = splitTopLevelAndChain(command);
|
|
14089
14333
|
if (chain === null)
|
|
14090
14334
|
return { command, stripped: false };
|
|
@@ -14098,7 +14342,7 @@ function maybeStripCompressorPipe(command, compressionEnabled) {
|
|
|
14098
14342
|
return { command, stripped: false };
|
|
14099
14343
|
const filterStages = stages.slice(1).map((stage) => stage.trim());
|
|
14100
14344
|
for (const stage of filterStages) {
|
|
14101
|
-
if (!
|
|
14345
|
+
if (!filterStageIsSafeToDrop(stage))
|
|
14102
14346
|
return { command, stripped: false };
|
|
14103
14347
|
}
|
|
14104
14348
|
const filters = filterStages.join(" | ");
|
|
@@ -14144,6 +14388,14 @@ function splitTopLevelAndChain(command) {
|
|
|
14144
14388
|
return null;
|
|
14145
14389
|
if (char === ";")
|
|
14146
14390
|
return null;
|
|
14391
|
+
if (char === `
|
|
14392
|
+
` || char === "\r")
|
|
14393
|
+
return null;
|
|
14394
|
+
if (char === "&") {
|
|
14395
|
+
const prev = command[index - 1];
|
|
14396
|
+
if (prev !== ">" && next !== ">")
|
|
14397
|
+
return null;
|
|
14398
|
+
}
|
|
14147
14399
|
}
|
|
14148
14400
|
segments.push(command.slice(start));
|
|
14149
14401
|
return segments;
|
|
@@ -14186,45 +14438,262 @@ function isCompressorHandledRunner(stage) {
|
|
|
14186
14438
|
const tokens = tokenizeStage(stage);
|
|
14187
14439
|
if (tokens.length === 0)
|
|
14188
14440
|
return false;
|
|
14189
|
-
const [first, second, third] = tokens;
|
|
14190
|
-
if (!first)
|
|
14191
|
-
return false;
|
|
14192
14441
|
if (tokens.some((token) => token === "&&" || token === "||" || token.includes(";"))) {
|
|
14193
14442
|
return false;
|
|
14194
14443
|
}
|
|
14444
|
+
const first = runnerName(tokens[0]);
|
|
14445
|
+
const second = tokens[1];
|
|
14446
|
+
const third = tokens[2];
|
|
14447
|
+
const rest = tokens.slice(1);
|
|
14448
|
+
if (!first)
|
|
14449
|
+
return false;
|
|
14195
14450
|
if (first === "bun")
|
|
14196
14451
|
return second === "test" || second === "run" && startsWithTest(third);
|
|
14197
|
-
if (first === "
|
|
14198
|
-
return ["test", "build", "check", "clippy"].includes(second ?? "");
|
|
14199
|
-
if (first === "go")
|
|
14200
|
-
return second === "test" || second === "build";
|
|
14201
|
-
if (["npm", "pnpm"].includes(first)) {
|
|
14452
|
+
if (first === "npm" || first === "pnpm") {
|
|
14202
14453
|
return second === "test" || second === "run" && startsWithTest(third);
|
|
14203
14454
|
}
|
|
14204
|
-
if (first === "yarn")
|
|
14205
|
-
return second === "
|
|
14455
|
+
if (first === "yarn") {
|
|
14456
|
+
return startsWithTest(second) || second === "run" && startsWithTest(third);
|
|
14457
|
+
}
|
|
14458
|
+
if (first === "deno")
|
|
14459
|
+
return ["test", "lint", "check", "bench"].includes(second ?? "");
|
|
14460
|
+
if (first === "npx") {
|
|
14461
|
+
return ["tsc", "eslint", "vitest", "jest", "playwright", "biome"].includes(second ?? "");
|
|
14462
|
+
}
|
|
14206
14463
|
if (first === "playwright")
|
|
14207
14464
|
return second === "test";
|
|
14208
|
-
if (first === "
|
|
14209
|
-
return ["
|
|
14210
|
-
|
|
14465
|
+
if (first === "cargo") {
|
|
14466
|
+
return ["test", "build", "check", "clippy", "nextest"].includes(second ?? "");
|
|
14467
|
+
}
|
|
14468
|
+
if (first === "go")
|
|
14469
|
+
return ["test", "build", "vet"].includes(second ?? "");
|
|
14470
|
+
if (first === "gradle" || first === "gradlew") {
|
|
14471
|
+
return hasBuildTask(rest, ["test", "check", "build", "assemble", "clean"]);
|
|
14472
|
+
}
|
|
14473
|
+
if (first === "mvn" || first === "mvnw") {
|
|
14474
|
+
return hasBuildTask(rest, ["test", "verify", "package", "install"]);
|
|
14475
|
+
}
|
|
14476
|
+
if (first === "dotnet")
|
|
14477
|
+
return ["test", "build"].includes(second ?? "");
|
|
14478
|
+
if (first === "rspec")
|
|
14479
|
+
return true;
|
|
14480
|
+
if (first === "rake")
|
|
14481
|
+
return second === "test" || second === "spec";
|
|
14482
|
+
if (first === "phpunit" || first === "pest")
|
|
14483
|
+
return true;
|
|
14484
|
+
if (first === "xcodebuild")
|
|
14485
|
+
return xcodebuildHasBuildAction(rest);
|
|
14486
|
+
if (first === "swift")
|
|
14487
|
+
return second === "test" || second === "build";
|
|
14488
|
+
if (first === "make" || first === "gmake") {
|
|
14489
|
+
return hasBuildTask(rest, ["test", "check", "lint", "clean"]);
|
|
14490
|
+
}
|
|
14491
|
+
return [
|
|
14492
|
+
"vitest",
|
|
14493
|
+
"jest",
|
|
14494
|
+
"pytest",
|
|
14495
|
+
"tsc",
|
|
14496
|
+
"eslint",
|
|
14497
|
+
"biome",
|
|
14498
|
+
"ruff",
|
|
14499
|
+
"mypy",
|
|
14500
|
+
"tox",
|
|
14501
|
+
"nox"
|
|
14502
|
+
].includes(first);
|
|
14503
|
+
}
|
|
14504
|
+
function runnerName(token) {
|
|
14505
|
+
if (!token)
|
|
14506
|
+
return "";
|
|
14507
|
+
const slash = token.lastIndexOf("/");
|
|
14508
|
+
return slash === -1 ? token : token.slice(slash + 1);
|
|
14509
|
+
}
|
|
14510
|
+
function hasBuildTask(args, tasks) {
|
|
14511
|
+
const isAllowedTask = (arg) => tasks.some((task) => arg === task || arg.endsWith(`:${task}`));
|
|
14512
|
+
const isFlagOrProperty = (arg) => arg.startsWith("-") || arg.includes("=");
|
|
14513
|
+
let sawAllowed = false;
|
|
14514
|
+
for (const arg of args) {
|
|
14515
|
+
if (isFlagOrProperty(arg))
|
|
14516
|
+
continue;
|
|
14517
|
+
if (!isAllowedTask(arg))
|
|
14518
|
+
return false;
|
|
14519
|
+
sawAllowed = true;
|
|
14520
|
+
}
|
|
14521
|
+
return sawAllowed;
|
|
14211
14522
|
}
|
|
14212
14523
|
function startsWithTest(token) {
|
|
14213
14524
|
return token?.startsWith("test") === true;
|
|
14214
14525
|
}
|
|
14215
|
-
|
|
14216
|
-
|
|
14217
|
-
|
|
14526
|
+
var XCODEBUILD_VALUE_FLAGS = new Set([
|
|
14527
|
+
"-scheme",
|
|
14528
|
+
"-target",
|
|
14529
|
+
"-project",
|
|
14530
|
+
"-workspace",
|
|
14531
|
+
"-configuration",
|
|
14532
|
+
"-sdk",
|
|
14533
|
+
"-destination",
|
|
14534
|
+
"-arch",
|
|
14535
|
+
"-derivedDataPath",
|
|
14536
|
+
"-resultBundlePath",
|
|
14537
|
+
"-xcconfig",
|
|
14538
|
+
"-toolchain"
|
|
14539
|
+
]);
|
|
14540
|
+
var XCODEBUILD_BUILD_ACTIONS = new Set([
|
|
14541
|
+
"build",
|
|
14542
|
+
"test",
|
|
14543
|
+
"build-for-testing",
|
|
14544
|
+
"test-without-building",
|
|
14545
|
+
"analyze"
|
|
14546
|
+
]);
|
|
14547
|
+
function xcodebuildHasBuildAction(args) {
|
|
14548
|
+
for (let i = 0;i < args.length; i++) {
|
|
14549
|
+
const arg = args[i];
|
|
14550
|
+
if (arg.startsWith("-")) {
|
|
14551
|
+
if (XCODEBUILD_VALUE_FLAGS.has(arg))
|
|
14552
|
+
i++;
|
|
14553
|
+
continue;
|
|
14554
|
+
}
|
|
14555
|
+
if (XCODEBUILD_BUILD_ACTIONS.has(arg))
|
|
14556
|
+
return true;
|
|
14557
|
+
}
|
|
14558
|
+
return false;
|
|
14559
|
+
}
|
|
14560
|
+
function containsUnsplittableConstruct(command) {
|
|
14561
|
+
let quote = null;
|
|
14562
|
+
let escaped = false;
|
|
14563
|
+
for (let i = 0;i < command.length; i++) {
|
|
14564
|
+
const char = command[i];
|
|
14565
|
+
if (escaped) {
|
|
14566
|
+
escaped = false;
|
|
14567
|
+
continue;
|
|
14568
|
+
}
|
|
14569
|
+
if (char === "\\" && quote !== "'") {
|
|
14570
|
+
escaped = true;
|
|
14571
|
+
continue;
|
|
14572
|
+
}
|
|
14573
|
+
if (quote === "'") {
|
|
14574
|
+
if (char === "'")
|
|
14575
|
+
quote = null;
|
|
14576
|
+
continue;
|
|
14577
|
+
}
|
|
14578
|
+
if (quote === '"') {
|
|
14579
|
+
if (char === '"')
|
|
14580
|
+
quote = null;
|
|
14581
|
+
else if (char === "`")
|
|
14582
|
+
return true;
|
|
14583
|
+
else if (char === "$" && command[i + 1] === "(")
|
|
14584
|
+
return true;
|
|
14585
|
+
continue;
|
|
14586
|
+
}
|
|
14587
|
+
if (char === "'" || char === '"') {
|
|
14588
|
+
quote = char;
|
|
14589
|
+
continue;
|
|
14590
|
+
}
|
|
14591
|
+
if (char === "`")
|
|
14592
|
+
return true;
|
|
14593
|
+
if (char === "(" || char === ")")
|
|
14594
|
+
return true;
|
|
14595
|
+
}
|
|
14596
|
+
return false;
|
|
14597
|
+
}
|
|
14598
|
+
var READS_FILE_OPERAND = new Set(["cat", "tac", "nl", "less", "more"]);
|
|
14599
|
+
function filterStageIsSafeToDrop(stage) {
|
|
14600
|
+
const head = tokenizeStage(stage)[0];
|
|
14218
14601
|
if (!head)
|
|
14219
14602
|
return false;
|
|
14220
14603
|
if (head === "wc")
|
|
14221
14604
|
return false;
|
|
14222
14605
|
if (!NOISE_FILTERS.has(head))
|
|
14223
14606
|
return false;
|
|
14224
|
-
if ((
|
|
14607
|
+
if (/[<>]/.test(stage))
|
|
14608
|
+
return false;
|
|
14609
|
+
if (hasUnquotedBackground(stage))
|
|
14610
|
+
return false;
|
|
14611
|
+
const args = tokenizeStage(stage).slice(1);
|
|
14612
|
+
const hasFlag = (...names) => args.some((a) => names.some((n) => a === n || a.startsWith(`${n}=`)));
|
|
14613
|
+
if (head === "grep" || head === "rg") {
|
|
14614
|
+
if (hasIntentChangingGrepFlag(args))
|
|
14615
|
+
return false;
|
|
14616
|
+
if (countBareOperands(args) > 1)
|
|
14617
|
+
return false;
|
|
14618
|
+
return true;
|
|
14619
|
+
}
|
|
14620
|
+
if (head === "head" || head === "tail") {
|
|
14621
|
+
if (bareOperands(args).some((op) => !/^\d+$/.test(op)))
|
|
14622
|
+
return false;
|
|
14623
|
+
return true;
|
|
14624
|
+
}
|
|
14625
|
+
if (READS_FILE_OPERAND.has(head)) {
|
|
14626
|
+
if (countBareOperands(args) > 0)
|
|
14627
|
+
return false;
|
|
14628
|
+
return true;
|
|
14629
|
+
}
|
|
14630
|
+
if (head === "sed") {
|
|
14631
|
+
if (hasFlag("-i", "--in-place"))
|
|
14632
|
+
return false;
|
|
14633
|
+
if (countBareOperands(args) > 1)
|
|
14634
|
+
return false;
|
|
14635
|
+
return true;
|
|
14636
|
+
}
|
|
14637
|
+
if (head === "awk") {
|
|
14638
|
+
if (countBareOperands(args) > 1)
|
|
14639
|
+
return false;
|
|
14640
|
+
return true;
|
|
14641
|
+
}
|
|
14642
|
+
if (head === "sort" && hasFlag("-o", "--output"))
|
|
14643
|
+
return false;
|
|
14644
|
+
if (bareOperands(args).some((op) => op.includes("/") || op.includes(".")))
|
|
14225
14645
|
return false;
|
|
14226
14646
|
return true;
|
|
14227
14647
|
}
|
|
14648
|
+
function bareOperands(args) {
|
|
14649
|
+
const out = [];
|
|
14650
|
+
let afterDoubleDash = false;
|
|
14651
|
+
for (const arg of args) {
|
|
14652
|
+
if (!afterDoubleDash && arg === "--") {
|
|
14653
|
+
afterDoubleDash = true;
|
|
14654
|
+
continue;
|
|
14655
|
+
}
|
|
14656
|
+
if (!afterDoubleDash && arg.startsWith("-") && arg !== "-")
|
|
14657
|
+
continue;
|
|
14658
|
+
out.push(arg);
|
|
14659
|
+
}
|
|
14660
|
+
return out;
|
|
14661
|
+
}
|
|
14662
|
+
function countBareOperands(args) {
|
|
14663
|
+
return bareOperands(args).length;
|
|
14664
|
+
}
|
|
14665
|
+
function hasUnquotedBackground(stage) {
|
|
14666
|
+
let quote = null;
|
|
14667
|
+
let escaped = false;
|
|
14668
|
+
for (let i = 0;i < stage.length; i++) {
|
|
14669
|
+
const char = stage[i];
|
|
14670
|
+
if (escaped) {
|
|
14671
|
+
escaped = false;
|
|
14672
|
+
continue;
|
|
14673
|
+
}
|
|
14674
|
+
if (char === "\\" && quote !== "'") {
|
|
14675
|
+
escaped = true;
|
|
14676
|
+
continue;
|
|
14677
|
+
}
|
|
14678
|
+
if (quote) {
|
|
14679
|
+
if (char === quote)
|
|
14680
|
+
quote = null;
|
|
14681
|
+
continue;
|
|
14682
|
+
}
|
|
14683
|
+
if (char === "'" || char === '"') {
|
|
14684
|
+
quote = char;
|
|
14685
|
+
continue;
|
|
14686
|
+
}
|
|
14687
|
+
if (char === "&") {
|
|
14688
|
+
const prev = stage[i - 1];
|
|
14689
|
+
const next = stage[i + 1];
|
|
14690
|
+
if (prev === "&" || next === "&" || prev === ">" || next === ">")
|
|
14691
|
+
continue;
|
|
14692
|
+
return true;
|
|
14693
|
+
}
|
|
14694
|
+
}
|
|
14695
|
+
return false;
|
|
14696
|
+
}
|
|
14228
14697
|
function hasIntentChangingGrepFlag(args) {
|
|
14229
14698
|
for (const arg of args) {
|
|
14230
14699
|
if (arg === "--")
|
|
@@ -14288,7 +14757,7 @@ function tokenizeStage(stage) {
|
|
|
14288
14757
|
// ../aft-bridge/dist/pool.js
|
|
14289
14758
|
import { realpathSync as realpathSync2 } from "node:fs";
|
|
14290
14759
|
import { homedir as homedir6 } from "node:os";
|
|
14291
|
-
var DEFAULT_IDLE_TIMEOUT_MS =
|
|
14760
|
+
var DEFAULT_IDLE_TIMEOUT_MS = 30 * 60 * 1000;
|
|
14292
14761
|
var DEFAULT_MAX_POOL_SIZE = 8;
|
|
14293
14762
|
var CLEANUP_INTERVAL_MS = 60 * 1000;
|
|
14294
14763
|
function canonicalHomeDir() {
|
|
@@ -14383,7 +14852,7 @@ class BridgePool {
|
|
|
14383
14852
|
cleanup() {
|
|
14384
14853
|
const now = Date.now();
|
|
14385
14854
|
for (const [dir, entry] of this.bridges) {
|
|
14386
|
-
if (entry.bridge.hasPendingRequests())
|
|
14855
|
+
if (entry.bridge.hasPendingRequests() || entry.bridge.hasOutstandingBackgroundTasks())
|
|
14387
14856
|
continue;
|
|
14388
14857
|
if (now - entry.lastUsed > this.idleTimeoutMs) {
|
|
14389
14858
|
entry.bridge.shutdown().catch((err) => this.error("cleanup shutdown failed:", err));
|
|
@@ -14391,7 +14860,7 @@ class BridgePool {
|
|
|
14391
14860
|
}
|
|
14392
14861
|
}
|
|
14393
14862
|
for (const bridge of this.staleBridges) {
|
|
14394
|
-
if (bridge.hasPendingRequests())
|
|
14863
|
+
if (bridge.hasPendingRequests() || bridge.hasOutstandingBackgroundTasks())
|
|
14395
14864
|
continue;
|
|
14396
14865
|
bridge.shutdown().catch((err) => this.error("stale cleanup shutdown failed:", err));
|
|
14397
14866
|
this.staleBridges.delete(bridge);
|
|
@@ -14401,7 +14870,7 @@ class BridgePool {
|
|
|
14401
14870
|
let oldestDir = null;
|
|
14402
14871
|
let oldestTime = Infinity;
|
|
14403
14872
|
for (const [dir, entry] of this.bridges) {
|
|
14404
|
-
if (entry.bridge.hasPendingRequests())
|
|
14873
|
+
if (entry.bridge.hasPendingRequests() || entry.bridge.hasOutstandingBackgroundTasks())
|
|
14405
14874
|
continue;
|
|
14406
14875
|
if (entry.lastUsed < oldestTime) {
|
|
14407
14876
|
oldestTime = entry.lastUsed;
|
|
@@ -15207,7 +15676,7 @@ import {
|
|
|
15207
15676
|
// package.json
|
|
15208
15677
|
var package_default = {
|
|
15209
15678
|
name: "@cortexkit/aft-pi",
|
|
15210
|
-
version: "0.
|
|
15679
|
+
version: "0.37.0",
|
|
15211
15680
|
type: "module",
|
|
15212
15681
|
description: "Pi coding agent extension for Agent File Tools (AFT) — tree-sitter and LSP-powered code analysis",
|
|
15213
15682
|
main: "dist/index.js",
|
|
@@ -15230,7 +15699,7 @@ var package_default = {
|
|
|
15230
15699
|
"test:unit": "bun test src/__tests__ --path-ignore-patterns 'src/__tests__/e2e/**'"
|
|
15231
15700
|
},
|
|
15232
15701
|
dependencies: {
|
|
15233
|
-
"@cortexkit/aft-bridge": "0.
|
|
15702
|
+
"@cortexkit/aft-bridge": "0.37.0",
|
|
15234
15703
|
"@xterm/headless": "^5.5.0",
|
|
15235
15704
|
"comment-json": "^5.0.0",
|
|
15236
15705
|
diff: "^8.0.4",
|
|
@@ -15238,12 +15707,12 @@ var package_default = {
|
|
|
15238
15707
|
zod: "^4.1.8"
|
|
15239
15708
|
},
|
|
15240
15709
|
optionalDependencies: {
|
|
15241
|
-
"@cortexkit/aft-darwin-arm64": "0.
|
|
15242
|
-
"@cortexkit/aft-darwin-x64": "0.
|
|
15243
|
-
"@cortexkit/aft-linux-arm64": "0.
|
|
15244
|
-
"@cortexkit/aft-linux-x64": "0.
|
|
15245
|
-
"@cortexkit/aft-win32-arm64": "0.
|
|
15246
|
-
"@cortexkit/aft-win32-x64": "0.
|
|
15710
|
+
"@cortexkit/aft-darwin-arm64": "0.37.0",
|
|
15711
|
+
"@cortexkit/aft-darwin-x64": "0.37.0",
|
|
15712
|
+
"@cortexkit/aft-linux-arm64": "0.37.0",
|
|
15713
|
+
"@cortexkit/aft-linux-x64": "0.37.0",
|
|
15714
|
+
"@cortexkit/aft-win32-arm64": "0.37.0",
|
|
15715
|
+
"@cortexkit/aft-win32-x64": "0.37.0"
|
|
15247
15716
|
},
|
|
15248
15717
|
devDependencies: {
|
|
15249
15718
|
"@earendil-works/pi-coding-agent": "*",
|
|
@@ -15501,19 +15970,6 @@ function isEmptyParam(value) {
|
|
|
15501
15970
|
return Object.keys(value).length === 0;
|
|
15502
15971
|
return false;
|
|
15503
15972
|
}
|
|
15504
|
-
var LONG_RUNNING_COMMAND_TIMEOUT_MS = {
|
|
15505
|
-
callers: 60000,
|
|
15506
|
-
trace_to: 60000,
|
|
15507
|
-
trace_to_symbol: 60000,
|
|
15508
|
-
trace_data: 60000,
|
|
15509
|
-
impact: 60000,
|
|
15510
|
-
grep: 60000,
|
|
15511
|
-
glob: 60000,
|
|
15512
|
-
semantic_search: 60000
|
|
15513
|
-
};
|
|
15514
|
-
function timeoutForCommand(command) {
|
|
15515
|
-
return LONG_RUNNING_COMMAND_TIMEOUT_MS[command];
|
|
15516
|
-
}
|
|
15517
15973
|
function asPlainObject(value) {
|
|
15518
15974
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
15519
15975
|
return;
|
|
@@ -31881,7 +32337,7 @@ function sendFeatureAnnouncement(version2, features, footer, storageDir) {
|
|
|
31881
32337
|
// src/status-bar-inject.ts
|
|
31882
32338
|
var DEFAULT_SESSION = "__default__";
|
|
31883
32339
|
var emitStateBySession = new Map;
|
|
31884
|
-
function statusBarBlockForSession(sessionID, counts) {
|
|
32340
|
+
function statusBarBlockForSession(sessionID, counts, force = false) {
|
|
31885
32341
|
if (!counts)
|
|
31886
32342
|
return;
|
|
31887
32343
|
const key = sessionID ?? DEFAULT_SESSION;
|
|
@@ -31890,6 +32346,11 @@ function statusBarBlockForSession(sessionID, counts) {
|
|
|
31890
32346
|
state = createStatusBarEmitState();
|
|
31891
32347
|
emitStateBySession.set(key, state);
|
|
31892
32348
|
}
|
|
32349
|
+
if (force) {
|
|
32350
|
+
state.last = counts;
|
|
32351
|
+
state.callsSinceEmit = 0;
|
|
32352
|
+
return formatStatusBar(counts);
|
|
32353
|
+
}
|
|
31893
32354
|
return shouldEmitStatusBar(state, counts) ? formatStatusBar(counts) : undefined;
|
|
31894
32355
|
}
|
|
31895
32356
|
|
|
@@ -32146,7 +32607,6 @@ function formatDiffForPi(oldContent, newContent, contextLines = DEFAULT_CONTEXT_
|
|
|
32146
32607
|
}
|
|
32147
32608
|
|
|
32148
32609
|
// src/tools/hoisted.ts
|
|
32149
|
-
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.";
|
|
32150
32610
|
function diagnosticsOnEditDefault(ctx) {
|
|
32151
32611
|
return ctx.config.lsp?.diagnostics_on_edit ?? false;
|
|
32152
32612
|
}
|
|
@@ -32216,8 +32676,7 @@ var WriteParams = Type2.Object({
|
|
|
32216
32676
|
filePath: Type2.String({
|
|
32217
32677
|
description: "Path to the file to write (absolute or relative to project root)"
|
|
32218
32678
|
}),
|
|
32219
|
-
content: Type2.String({ description: "Full file contents to write" })
|
|
32220
|
-
diagnostics: Type2.Optional(Type2.Boolean({ description: DIAGNOSTICS_PARAM_DESCRIPTION }))
|
|
32679
|
+
content: Type2.String({ description: "Full file contents to write" })
|
|
32221
32680
|
});
|
|
32222
32681
|
var EditParams = Type2.Object({
|
|
32223
32682
|
filePath: Type2.String({
|
|
@@ -32229,8 +32688,7 @@ var EditParams = Type2.Object({
|
|
|
32229
32688
|
occurrence: optionalInt(0, Number.MAX_SAFE_INTEGER),
|
|
32230
32689
|
appendContent: Type2.Optional(Type2.String({
|
|
32231
32690
|
description: "Append text to the end of the file (creates the file if missing, parent dirs auto-created). When set, oldString/newString are ignored."
|
|
32232
|
-
}))
|
|
32233
|
-
diagnostics: Type2.Optional(Type2.Boolean({ description: DIAGNOSTICS_PARAM_DESCRIPTION }))
|
|
32691
|
+
}))
|
|
32234
32692
|
});
|
|
32235
32693
|
var GrepParams = Type2.Object({
|
|
32236
32694
|
pattern: Type2.String({ description: "Regex pattern to search for" }),
|
|
@@ -32238,8 +32696,7 @@ var GrepParams = Type2.Object({
|
|
|
32238
32696
|
description: "Path scope (file or directory; absolute or relative to project root)"
|
|
32239
32697
|
})),
|
|
32240
32698
|
include: Type2.Optional(Type2.String({ description: "Glob filter for included files (e.g. '*.ts,*.tsx')" })),
|
|
32241
|
-
caseSensitive: Type2.Optional(Type2.Boolean({ description: "Case-sensitive matching" }))
|
|
32242
|
-
contextLines: optionalInt(1, Number.MAX_SAFE_INTEGER)
|
|
32699
|
+
caseSensitive: Type2.Optional(Type2.Boolean({ description: "Case-sensitive matching" }))
|
|
32243
32700
|
});
|
|
32244
32701
|
function registerHoistedTools(pi, ctx, surface) {
|
|
32245
32702
|
if (surface.hoistRead) {
|
|
@@ -32254,7 +32711,9 @@ function registerHoistedTools(pi, ctx, surface) {
|
|
|
32254
32711
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
32255
32712
|
const offset = coerceOptionalInt(params.offset, "offset", 1, Number.MAX_SAFE_INTEGER);
|
|
32256
32713
|
const limit = coerceOptionalInt(params.limit, "limit", 1, Number.MAX_SAFE_INTEGER);
|
|
32257
|
-
const req = {
|
|
32714
|
+
const req = {
|
|
32715
|
+
file: await resolvePathArg(extCtx.cwd, params.path)
|
|
32716
|
+
};
|
|
32258
32717
|
if (offset !== undefined) {
|
|
32259
32718
|
req.start_line = offset;
|
|
32260
32719
|
if (limit !== undefined) {
|
|
@@ -32281,19 +32740,20 @@ function registerHoistedTools(pi, ctx, surface) {
|
|
|
32281
32740
|
pi.registerTool({
|
|
32282
32741
|
name: "write",
|
|
32283
32742
|
label: "write",
|
|
32284
|
-
description: "Write a file
|
|
32285
|
-
promptSnippet: "Create or overwrite files (uses filePath; auto-formats
|
|
32743
|
+
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.",
|
|
32744
|
+
promptSnippet: "Create or overwrite files (uses filePath; auto-formats)",
|
|
32286
32745
|
promptGuidelines: ["Use write only for new files or complete rewrites."],
|
|
32287
32746
|
parameters: WriteParams,
|
|
32288
32747
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
32289
|
-
await
|
|
32748
|
+
const filePath = await resolvePathArg(extCtx.cwd, params.filePath);
|
|
32749
|
+
await assertExternalDirectoryPermission(extCtx, filePath, "modify", {
|
|
32290
32750
|
restrictToProjectRoot: surface.restrictToProjectRoot
|
|
32291
32751
|
});
|
|
32292
32752
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
32293
32753
|
const response = await callBridge(bridge, "write", {
|
|
32294
|
-
file:
|
|
32754
|
+
file: filePath,
|
|
32295
32755
|
content: params.content,
|
|
32296
|
-
diagnostics:
|
|
32756
|
+
diagnostics: diagnosticsOnEditDefault(ctx),
|
|
32297
32757
|
include_diff_content: true
|
|
32298
32758
|
}, extCtx);
|
|
32299
32759
|
return buildMutationResult(response);
|
|
@@ -32310,8 +32770,8 @@ function registerHoistedTools(pi, ctx, surface) {
|
|
|
32310
32770
|
pi.registerTool({
|
|
32311
32771
|
name: "edit",
|
|
32312
32772
|
label: "edit",
|
|
32313
|
-
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`.
|
|
32314
|
-
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).
|
|
32773
|
+
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`.",
|
|
32774
|
+
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).",
|
|
32315
32775
|
promptGuidelines: [
|
|
32316
32776
|
"Prefer edit over write when changing part of an existing file.",
|
|
32317
32777
|
"Include enough surrounding context in oldString to make the match unique, or set replaceAll/occurrence explicitly.",
|
|
@@ -32319,26 +32779,27 @@ function registerHoistedTools(pi, ctx, surface) {
|
|
|
32319
32779
|
],
|
|
32320
32780
|
parameters: EditParams,
|
|
32321
32781
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
32322
|
-
await
|
|
32782
|
+
const filePath = await resolvePathArg(extCtx.cwd, params.filePath);
|
|
32783
|
+
await assertExternalDirectoryPermission(extCtx, filePath, "modify", {
|
|
32323
32784
|
restrictToProjectRoot: surface.restrictToProjectRoot
|
|
32324
32785
|
});
|
|
32325
32786
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
32326
32787
|
if (typeof params.appendContent === "string") {
|
|
32327
32788
|
const req2 = {
|
|
32328
32789
|
op: "append",
|
|
32329
|
-
file:
|
|
32790
|
+
file: filePath,
|
|
32330
32791
|
append_content: params.appendContent,
|
|
32331
|
-
diagnostics:
|
|
32792
|
+
diagnostics: diagnosticsOnEditDefault(ctx),
|
|
32332
32793
|
include_diff_content: true
|
|
32333
32794
|
};
|
|
32334
32795
|
const response2 = await callBridge(bridge, "edit_match", req2, extCtx);
|
|
32335
32796
|
return buildMutationResult(response2);
|
|
32336
32797
|
}
|
|
32337
32798
|
const req = {
|
|
32338
|
-
file:
|
|
32799
|
+
file: filePath,
|
|
32339
32800
|
match: params.oldString ?? "",
|
|
32340
32801
|
replacement: params.newString ?? "",
|
|
32341
|
-
diagnostics:
|
|
32802
|
+
diagnostics: diagnosticsOnEditDefault(ctx),
|
|
32342
32803
|
include_diff_content: true
|
|
32343
32804
|
};
|
|
32344
32805
|
if (params.replaceAll === true)
|
|
@@ -32378,9 +32839,6 @@ function registerHoistedTools(pi, ctx, surface) {
|
|
|
32378
32839
|
req.include = splitIncludeGlobs(params.include);
|
|
32379
32840
|
if (params.caseSensitive !== undefined)
|
|
32380
32841
|
req.case_sensitive = params.caseSensitive;
|
|
32381
|
-
const contextLines = coerceOptionalInt(params.contextLines, "contextLines", 1, Number.MAX_SAFE_INTEGER);
|
|
32382
|
-
if (contextLines !== undefined)
|
|
32383
|
-
req.context_lines = contextLines;
|
|
32384
32842
|
const response = await callBridge(bridge, "grep", req, extCtx);
|
|
32385
32843
|
const text = response.text ?? "";
|
|
32386
32844
|
return textResult(text);
|
|
@@ -32941,7 +33399,7 @@ function registerAstTools(pi, ctx, surface) {
|
|
|
32941
33399
|
if (!isEmptyParam(params.globs))
|
|
32942
33400
|
req.globs = params.globs;
|
|
32943
33401
|
if (params.contextLines !== undefined)
|
|
32944
|
-
req.
|
|
33402
|
+
req.context = params.contextLines;
|
|
32945
33403
|
const response = await callBridge(bridge, "ast_search", req, extCtx);
|
|
32946
33404
|
return textResult(response.text ?? JSON.stringify(response));
|
|
32947
33405
|
},
|
|
@@ -33095,22 +33553,28 @@ function getBashSpawnHook(pi) {
|
|
|
33095
33553
|
}
|
|
33096
33554
|
return api2.hooks?.bashSpawn;
|
|
33097
33555
|
}
|
|
33098
|
-
function registerBashTool(pi, ctx) {
|
|
33556
|
+
function registerBashTool(pi, ctx, aftSearchRegistered = false) {
|
|
33099
33557
|
const spawnHook = getBashSpawnHook(pi);
|
|
33558
|
+
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";
|
|
33559
|
+
const bashCfg = resolveBashConfig(ctx.config);
|
|
33560
|
+
const compressionSentence = bashCfg.compress ? " Output is compressed by default; pass `compressed: false` for raw output." : "";
|
|
33561
|
+
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`.";
|
|
33100
33562
|
pi.registerTool({
|
|
33101
33563
|
name: "bash",
|
|
33102
33564
|
label: "bash",
|
|
33103
|
-
description:
|
|
33565
|
+
description: `Execute shell commands.${compressionSentence}${tasksSentence}
|
|
33566
|
+
|
|
33567
|
+
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}.`,
|
|
33104
33568
|
promptSnippet: "Run shell commands (timeout in milliseconds; supports workdir, background tasks, compressed output, PTY mode)",
|
|
33105
33569
|
promptGuidelines: [
|
|
33106
|
-
|
|
33570
|
+
`DO NOT use bash for code search or exploration — ${searchSteer}.`,
|
|
33107
33571
|
"Set compressed: false when you need ANSI color codes in the output."
|
|
33108
33572
|
],
|
|
33109
33573
|
parameters: BashParams,
|
|
33110
33574
|
async execute(_toolCallId, params, _signal, onUpdate, extCtx) {
|
|
33111
33575
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
33112
|
-
const
|
|
33113
|
-
const foregroundWaitMs = resolveForegroundWaitMs(
|
|
33576
|
+
const bashCfg2 = resolveBashConfig(ctx.config);
|
|
33577
|
+
const foregroundWaitMs = resolveForegroundWaitMs(bashCfg2.foreground_wait_window_ms);
|
|
33114
33578
|
const timeout = coerceOptionalInt(params.timeout, "timeout", 1, Number.MAX_SAFE_INTEGER);
|
|
33115
33579
|
const ptyRows = coerceOptionalInt(params.ptyRows, "ptyRows", 1, 60);
|
|
33116
33580
|
const ptyCols = coerceOptionalInt(params.ptyCols, "ptyCols", 1, 140);
|
|
@@ -33127,7 +33591,7 @@ function registerBashTool(pi, ctx) {
|
|
|
33127
33591
|
throw new Error(`BashSpawnHook failed: ${hookErr instanceof Error ? hookErr.message : String(hookErr)}`);
|
|
33128
33592
|
}
|
|
33129
33593
|
}
|
|
33130
|
-
const compressionEnabled =
|
|
33594
|
+
const compressionEnabled = bashCfg2.compress && params.compressed !== false;
|
|
33131
33595
|
const pipeStrip = maybeStripCompressorPipe(spawnContext.command, compressionEnabled);
|
|
33132
33596
|
const bridgeCommand = pipeStrip.command;
|
|
33133
33597
|
let streamed = "";
|
|
@@ -33162,9 +33626,7 @@ function registerBashTool(pi, ctx) {
|
|
|
33162
33626
|
if (response.status === "running" && taskId) {
|
|
33163
33627
|
if (effectiveBackground) {
|
|
33164
33628
|
trackBgTask(resolveSessionId(extCtx), taskId);
|
|
33165
|
-
return bashResult(formatBackgroundLaunch(taskId, params.pty === true), {
|
|
33166
|
-
task_id: taskId
|
|
33167
|
-
});
|
|
33629
|
+
return bashResult(appendPipeStripNote(formatBackgroundLaunch(taskId, params.pty === true), pipeStrip.note), { task_id: taskId });
|
|
33168
33630
|
}
|
|
33169
33631
|
const waitTimeoutMs = effectiveTimeout !== undefined ? Math.min(effectiveTimeout, foregroundWaitMs) : foregroundWaitMs;
|
|
33170
33632
|
const startedAt = Date.now();
|
|
@@ -33174,7 +33636,7 @@ function registerBashTool(pi, ctx) {
|
|
|
33174
33636
|
throw new Error(status.message ?? "bash_status failed");
|
|
33175
33637
|
}
|
|
33176
33638
|
if (isTerminalStatus(status.status)) {
|
|
33177
|
-
return bashResult(appendPipeStripNote(withBashHints(formatForegroundResult(status)), pipeStrip.note), {
|
|
33639
|
+
return bashResult(appendPipeStripNote(withBashHints(formatForegroundResult(status), bridgeCommand, aftSearchRegistered), pipeStrip.note), {
|
|
33178
33640
|
exit_code: status.exit_code,
|
|
33179
33641
|
duration_ms: status.duration_ms,
|
|
33180
33642
|
truncated: status.output_truncated,
|
|
@@ -33188,9 +33650,7 @@ function registerBashTool(pi, ctx) {
|
|
|
33188
33650
|
throw new Error(promoted.message ?? "bash_promote failed");
|
|
33189
33651
|
}
|
|
33190
33652
|
trackBgTask(resolveSessionId(extCtx), taskId);
|
|
33191
|
-
return bashResult(formatPromotionMessage(taskId, effectiveTimeout, foregroundWaitMs), {
|
|
33192
|
-
task_id: taskId
|
|
33193
|
-
});
|
|
33653
|
+
return bashResult(appendPipeStripNote(formatPromotionMessage(taskId, effectiveTimeout, foregroundWaitMs), pipeStrip.note), { task_id: taskId });
|
|
33194
33654
|
}
|
|
33195
33655
|
await sleep(FOREGROUND_POLL_INTERVAL_MS);
|
|
33196
33656
|
}
|
|
@@ -33203,7 +33663,7 @@ function registerBashTool(pi, ctx) {
|
|
|
33203
33663
|
task_id: taskId
|
|
33204
33664
|
};
|
|
33205
33665
|
const output = response.output ?? "";
|
|
33206
|
-
return bashResult(appendPipeStripNote(withBashHints(output), pipeStrip.note), details);
|
|
33666
|
+
return bashResult(appendPipeStripNote(withBashHints(output, bridgeCommand, aftSearchRegistered), pipeStrip.note), details);
|
|
33207
33667
|
},
|
|
33208
33668
|
renderCall(args, theme, context) {
|
|
33209
33669
|
return renderBashCall(args?.command, args?.description, theme, context);
|
|
@@ -33235,8 +33695,8 @@ function formatPromotionMessage(taskId, timeout, waitWindowMs) {
|
|
|
33235
33695
|
function formatSeconds(ms) {
|
|
33236
33696
|
return `${Number((ms / 1000).toFixed(1))}s`;
|
|
33237
33697
|
}
|
|
33238
|
-
function withBashHints(output) {
|
|
33239
|
-
return maybeAppendConflictsHint(output);
|
|
33698
|
+
function withBashHints(output, command, aftSearchRegistered) {
|
|
33699
|
+
return maybeAppendGrepSearchHint(maybeAppendConflictsHint(output), command, aftSearchRegistered);
|
|
33240
33700
|
}
|
|
33241
33701
|
function formatForegroundResult(data) {
|
|
33242
33702
|
const output = data.output_preview ?? "";
|
|
@@ -33704,8 +34164,12 @@ function registerConflictsTool(pi, ctx) {
|
|
|
33704
34164
|
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
33705
34165
|
const reqParams = {};
|
|
33706
34166
|
const path2 = params?.path;
|
|
33707
|
-
if (typeof path2 === "string" && path2.trim() !== "")
|
|
33708
|
-
|
|
34167
|
+
if (typeof path2 === "string" && path2.trim() !== "") {
|
|
34168
|
+
await assertExternalDirectoryPermission(extCtx, path2, "inspect", {
|
|
34169
|
+
restrictToProjectRoot: ctx.config.restrict_to_project_root ?? false
|
|
34170
|
+
});
|
|
34171
|
+
reqParams.path = await resolvePathArg(extCtx.cwd, path2);
|
|
34172
|
+
}
|
|
33709
34173
|
const response = await callBridge(bridge, "git_conflicts", reqParams, extCtx);
|
|
33710
34174
|
return textResult(response.text ?? JSON.stringify(response, null, 2));
|
|
33711
34175
|
},
|
|
@@ -34251,7 +34715,15 @@ function registerInspectTool(pi, ctx) {
|
|
|
34251
34715
|
const topK = validateOptionalTopK(params.topK);
|
|
34252
34716
|
const response = await callBridge(bridge, "inspect", { sections, scope, topK }, extCtx);
|
|
34253
34717
|
runPendingTier2Categories(bridge, tier2RefreshCategories(response), extCtx);
|
|
34254
|
-
|
|
34718
|
+
const body = response.text;
|
|
34719
|
+
if (typeof body === "string") {
|
|
34720
|
+
const diagnostics = diagnosticsSummaryPart(asRecord2(response.summary));
|
|
34721
|
+
const text = diagnostics ? body ? `${body}
|
|
34722
|
+
|
|
34723
|
+
${diagnostics}` : diagnostics : body;
|
|
34724
|
+
return textResult(text, response);
|
|
34725
|
+
}
|
|
34726
|
+
return textResult(JSON.stringify(response, null, 2), response);
|
|
34255
34727
|
},
|
|
34256
34728
|
renderCall(args, theme, context) {
|
|
34257
34729
|
return renderInspectCall(args, theme, context);
|
|
@@ -34990,7 +35462,7 @@ function registerRefactorTool(pi, ctx) {
|
|
|
34990
35462
|
pi.registerTool({
|
|
34991
35463
|
name: "aft_refactor",
|
|
34992
35464
|
label: "refactor",
|
|
34993
|
-
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
|
|
35465
|
+
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.",
|
|
34994
35466
|
parameters: RefactorParams,
|
|
34995
35467
|
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
34996
35468
|
const commandMap = {
|
|
@@ -35369,21 +35841,9 @@ function registerSemanticTool(pi, ctx) {
|
|
|
35369
35841
|
name: "aft_search",
|
|
35370
35842
|
label: "search",
|
|
35371
35843
|
description: [
|
|
35372
|
-
"
|
|
35373
|
-
"",
|
|
35374
|
-
"When to reach for it:",
|
|
35375
|
-
"- Exploring an unfamiliar area: 'where is rate limiting handled', 'how does auth flow work'",
|
|
35376
|
-
"- Concept doesn't appear as a literal string: 'retry logic', 'cache invalidation', 'graceful shutdown'",
|
|
35377
|
-
"- Filename-shaped concepts: 'the bridge spawn helper', 'the session detection module'",
|
|
35378
|
-
"- Regex-shaped or exact text queries when you want AFT to classify and route automatically",
|
|
35379
|
-
"- You know roughly what the function does but not what it's named",
|
|
35844
|
+
"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').",
|
|
35380
35845
|
"",
|
|
35381
|
-
"
|
|
35382
|
-
"- You need exhaustive literal enumeration → use grep directly",
|
|
35383
|
-
"- You want the file/module structure → use aft_outline",
|
|
35384
|
-
"- You're following a call chain → use aft_callgraph",
|
|
35385
|
-
"",
|
|
35386
|
-
"Set hint to 'regex', 'literal', 'semantic', or 'auto' to override or document routing intent."
|
|
35846
|
+
"Set hint to 'regex', 'literal', or 'semantic' to force a lane."
|
|
35387
35847
|
].join(`
|
|
35388
35848
|
`),
|
|
35389
35849
|
parameters: SearchParams2,
|
|
@@ -35414,138 +35874,8 @@ ${extra}`;
|
|
|
35414
35874
|
});
|
|
35415
35875
|
}
|
|
35416
35876
|
|
|
35417
|
-
// src/tools/structure.ts
|
|
35418
|
-
import { StringEnum as StringEnum6 } from "@earendil-works/pi-ai";
|
|
35419
|
-
import { Type as Type14 } from "typebox";
|
|
35420
|
-
var TransformParams = Type14.Object({
|
|
35421
|
-
op: StringEnum6(["add_member", "add_derive", "wrap_try_catch", "add_decorator", "add_struct_tags"], { description: "Transformation operation" }),
|
|
35422
|
-
filePath: Type14.String({
|
|
35423
|
-
description: "Path to the source file (absolute or relative to project root)"
|
|
35424
|
-
}),
|
|
35425
|
-
container: Type14.Optional(Type14.String({ description: "Class/struct/impl name for add_member" })),
|
|
35426
|
-
code: Type14.Optional(Type14.String({ description: "Member code to insert (add_member)" })),
|
|
35427
|
-
target: Type14.Optional(Type14.String({ description: "Target symbol name" })),
|
|
35428
|
-
derives: Type14.Optional(Type14.Array(Type14.String(), { description: "Derive macro names (add_derive)" })),
|
|
35429
|
-
catchBody: Type14.Optional(Type14.String({ description: "Catch block body (wrap_try_catch, default: 'throw error;')" })),
|
|
35430
|
-
decorator: Type14.Optional(Type14.String({ description: "Decorator text without @ (add_decorator)" })),
|
|
35431
|
-
field: Type14.Optional(Type14.String({ description: "Struct field name (add_struct_tags)" })),
|
|
35432
|
-
tag: Type14.Optional(Type14.String({ description: "Tag key (add_struct_tags)" })),
|
|
35433
|
-
value: Type14.Optional(Type14.String({ description: "Tag value (add_struct_tags)" })),
|
|
35434
|
-
position: Type14.Optional(Type14.String({
|
|
35435
|
-
description: "Position hint: 'first', 'last' (default), 'before:name', 'after:name' for add_member"
|
|
35436
|
-
})),
|
|
35437
|
-
validate: Type14.Optional(StringEnum6(["syntax", "full"], {
|
|
35438
|
-
description: "Validation level (default: syntax)"
|
|
35439
|
-
}))
|
|
35440
|
-
});
|
|
35441
|
-
function buildTransformSections(args, payload, theme) {
|
|
35442
|
-
const response = asRecord2(payload);
|
|
35443
|
-
if (!response)
|
|
35444
|
-
return [theme.fg("muted", "No transform result.")];
|
|
35445
|
-
const target = asString(response.target) ?? asString(response.scope) ?? args.target ?? args.container ?? args.field ?? args.filePath;
|
|
35446
|
-
return [
|
|
35447
|
-
`${theme.fg("success", "transformed")} ${theme.fg("accent", args.op)}`,
|
|
35448
|
-
`${theme.fg("muted", "file")} ${theme.fg("accent", asString(response.file) ?? args.filePath)}`,
|
|
35449
|
-
target ? `${theme.fg("muted", "target")} ${target}` : theme.fg("muted", "No target metadata.")
|
|
35450
|
-
];
|
|
35451
|
-
}
|
|
35452
|
-
function renderTransformCall(args, theme, context) {
|
|
35453
|
-
const target = args.target ?? args.container ?? args.field;
|
|
35454
|
-
const summary = [
|
|
35455
|
-
theme.fg("accent", args.op),
|
|
35456
|
-
accentPath(theme, args.filePath),
|
|
35457
|
-
target ? theme.fg("toolOutput", target) : undefined
|
|
35458
|
-
].filter(Boolean).join(" ");
|
|
35459
|
-
return renderToolCall("transform", summary, theme, context);
|
|
35460
|
-
}
|
|
35461
|
-
function renderTransformResult(result, args, theme, context) {
|
|
35462
|
-
if (context.isError)
|
|
35463
|
-
return renderErrorResult(result, "transform failed", theme, context);
|
|
35464
|
-
return renderSections(buildTransformSections(args, extractStructuredPayload(result), theme), context);
|
|
35465
|
-
}
|
|
35466
|
-
function registerStructureTool(pi, ctx) {
|
|
35467
|
-
pi.registerTool({
|
|
35468
|
-
name: "aft_transform",
|
|
35469
|
-
label: "transform",
|
|
35470
|
-
description: "Scope-aware structural code transformations with correct indentation. Use aft_safety checkpoint/undo before risky transforms.",
|
|
35471
|
-
parameters: TransformParams,
|
|
35472
|
-
async execute(_toolCallId, params, _signal, _onUpdate, extCtx) {
|
|
35473
|
-
validateTransformParams(params);
|
|
35474
|
-
const filePath = await resolvePathArg(extCtx.cwd, params.filePath);
|
|
35475
|
-
await assertExternalDirectoryPermission(extCtx, filePath, "modify", {
|
|
35476
|
-
restrictToProjectRoot: ctx.config.restrict_to_project_root ?? false
|
|
35477
|
-
});
|
|
35478
|
-
const bridge = bridgeFor(ctx, extCtx.cwd);
|
|
35479
|
-
const req = { file: filePath };
|
|
35480
|
-
if (params.container !== undefined)
|
|
35481
|
-
req.scope = params.container;
|
|
35482
|
-
if (params.code !== undefined)
|
|
35483
|
-
req.code = params.code;
|
|
35484
|
-
if (params.target !== undefined)
|
|
35485
|
-
req.target = params.target;
|
|
35486
|
-
if (params.derives !== undefined)
|
|
35487
|
-
req.derives = params.derives;
|
|
35488
|
-
if (params.catchBody !== undefined)
|
|
35489
|
-
req.catch_body = params.catchBody;
|
|
35490
|
-
if (params.decorator !== undefined)
|
|
35491
|
-
req.decorator = params.decorator;
|
|
35492
|
-
if (params.field !== undefined)
|
|
35493
|
-
req.field = params.field;
|
|
35494
|
-
if (params.tag !== undefined)
|
|
35495
|
-
req.tag = params.tag;
|
|
35496
|
-
if (params.value !== undefined)
|
|
35497
|
-
req.value = params.value;
|
|
35498
|
-
if (params.position !== undefined)
|
|
35499
|
-
req.position = params.position;
|
|
35500
|
-
if (params.validate !== undefined)
|
|
35501
|
-
req.validate = params.validate;
|
|
35502
|
-
const response = await callBridge(bridge, params.op, req, extCtx);
|
|
35503
|
-
return textResult(JSON.stringify(response, null, 2));
|
|
35504
|
-
},
|
|
35505
|
-
renderCall(args, theme, context) {
|
|
35506
|
-
return renderTransformCall(args, theme, context);
|
|
35507
|
-
},
|
|
35508
|
-
renderResult(result, _options, theme, context) {
|
|
35509
|
-
return renderTransformResult(result, context.args, theme, context);
|
|
35510
|
-
}
|
|
35511
|
-
});
|
|
35512
|
-
}
|
|
35513
|
-
function validateTransformParams(params) {
|
|
35514
|
-
const op = params.op;
|
|
35515
|
-
if (op === "add_member") {
|
|
35516
|
-
if (isEmptyParam(params.container)) {
|
|
35517
|
-
throw new Error("'container' is required for 'add_member' op");
|
|
35518
|
-
}
|
|
35519
|
-
if (isEmptyParam(params.code)) {
|
|
35520
|
-
throw new Error("'code' is required for 'add_member' op");
|
|
35521
|
-
}
|
|
35522
|
-
}
|
|
35523
|
-
if (op === "add_derive" || op === "wrap_try_catch" || op === "add_decorator" || op === "add_struct_tags") {
|
|
35524
|
-
if (isEmptyParam(params.target)) {
|
|
35525
|
-
throw new Error(`'target' is required for '${op}' op`);
|
|
35526
|
-
}
|
|
35527
|
-
}
|
|
35528
|
-
if (op === "add_derive" && isEmptyParam(params.derives)) {
|
|
35529
|
-
throw new Error("'derives' array is required for 'add_derive' op");
|
|
35530
|
-
}
|
|
35531
|
-
if (op === "add_decorator" && isEmptyParam(params.decorator)) {
|
|
35532
|
-
throw new Error("'decorator' is required for 'add_decorator' op");
|
|
35533
|
-
}
|
|
35534
|
-
if (op === "add_struct_tags") {
|
|
35535
|
-
if (isEmptyParam(params.field)) {
|
|
35536
|
-
throw new Error("'field' is required for 'add_struct_tags' op");
|
|
35537
|
-
}
|
|
35538
|
-
if (isEmptyParam(params.tag)) {
|
|
35539
|
-
throw new Error("'tag' is required for 'add_struct_tags' op");
|
|
35540
|
-
}
|
|
35541
|
-
if (isEmptyParam(params.value)) {
|
|
35542
|
-
throw new Error("'value' is required for 'add_struct_tags' op");
|
|
35543
|
-
}
|
|
35544
|
-
}
|
|
35545
|
-
}
|
|
35546
|
-
|
|
35547
35877
|
// src/workflow-hints.ts
|
|
35548
|
-
var HEADING = "##
|
|
35878
|
+
var HEADING = "## IMPORTANT NOTICE about your tools";
|
|
35549
35879
|
function buildWorkflowHints(opts) {
|
|
35550
35880
|
const sections = [];
|
|
35551
35881
|
const grepName = opts.hoistBuiltins ? "grep" : "aft_grep";
|
|
@@ -35559,14 +35889,28 @@ function buildWorkflowHints(opts) {
|
|
|
35559
35889
|
const hasBash = !opts.absentTools.has(bashName);
|
|
35560
35890
|
const hasBgBash = opts.bashBackgroundEnabled && hasBash && !opts.absentTools.has("bash_status");
|
|
35561
35891
|
if (hasBash && opts.bashCompressionEnabled) {
|
|
35562
|
-
sections.push(
|
|
35892
|
+
sections.push([
|
|
35893
|
+
"**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:",
|
|
35894
|
+
"- `bun test | grep fail` → run `bun test`",
|
|
35895
|
+
"- `cargo test 2>&1 | tail -20` → run `cargo test`",
|
|
35896
|
+
"- `npm run build | head -50` → run `npm run build`"
|
|
35897
|
+
].join(`
|
|
35898
|
+
`));
|
|
35563
35899
|
}
|
|
35564
35900
|
if (hasOutline && hasZoom) {
|
|
35565
35901
|
sections.push(`**Web/URL access**: \`aft_outline({ target: "<url>" })\` first for structure, then \`aft_zoom({ url: "<url>", symbols: "<heading>" })\` for the specific section.`);
|
|
35566
35902
|
}
|
|
35567
35903
|
if (hasOutline && hasZoom && (hasGrep || hasSearch)) {
|
|
35904
|
+
const searchName = hasSearch ? "aft_search" : grepName;
|
|
35568
35905
|
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.`;
|
|
35569
|
-
|
|
35906
|
+
const readName = opts.hoistBuiltins ? "read" : "aft_read";
|
|
35907
|
+
sections.push([
|
|
35908
|
+
`**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:`,
|
|
35909
|
+
`- \`grep -rn "handleAuth" src/\` in bash → \`${searchName}({ query: "handleAuth" })\``,
|
|
35910
|
+
`- \`find . -name "*.ts" | xargs grep watcher\` in bash → \`${searchName}({ query: "watcher invalidation" })\` (concepts work too)`,
|
|
35911
|
+
`- \`sed -n '100,160p' app.ts\` / \`cat app.ts\` in bash → \`${readName}({ filePath: "app.ts", startLine: 100, endLine: 160 })\``
|
|
35912
|
+
].join(`
|
|
35913
|
+
`));
|
|
35570
35914
|
}
|
|
35571
35915
|
if (hasInspect) {
|
|
35572
35916
|
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.");
|
|
@@ -35584,13 +35928,22 @@ function buildWorkflowHints(opts) {
|
|
|
35584
35928
|
`));
|
|
35585
35929
|
}
|
|
35586
35930
|
if (hasBgBash) {
|
|
35587
|
-
sections.push(
|
|
35931
|
+
sections.push([
|
|
35932
|
+
`**Long-running commands** (builds, installs, full test suites): \`${bashName}({ background: true })\` returns a \`task_id\` immediately. Then do ONE of these:`,
|
|
35933
|
+
"1. Keep working on something independent of the result.",
|
|
35934
|
+
"2. End your turn — a completion reminder with the result arrives automatically.",
|
|
35935
|
+
`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.`,
|
|
35936
|
+
`\`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.`
|
|
35937
|
+
].join(`
|
|
35938
|
+
`));
|
|
35588
35939
|
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: "..." })\`.`);
|
|
35589
35940
|
}
|
|
35590
35941
|
if (sections.length === 0) {
|
|
35591
35942
|
return null;
|
|
35592
35943
|
}
|
|
35593
|
-
sections.unshift(
|
|
35944
|
+
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.
|
|
35945
|
+
|
|
35946
|
+
**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.`);
|
|
35594
35947
|
return `${HEADING}
|
|
35595
35948
|
|
|
35596
35949
|
${sections.join(`
|
|
@@ -35682,21 +36035,15 @@ var PLUGIN_VERSION = (() => {
|
|
|
35682
36035
|
return "0.0.0";
|
|
35683
36036
|
}
|
|
35684
36037
|
})();
|
|
35685
|
-
var ANNOUNCEMENT_VERSION = "0.
|
|
36038
|
+
var ANNOUNCEMENT_VERSION = "0.37.0";
|
|
35686
36039
|
var ANNOUNCEMENT_FEATURES = [
|
|
35687
|
-
"
|
|
35688
|
-
"
|
|
35689
|
-
"
|
|
35690
|
-
"
|
|
36040
|
+
"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.",
|
|
36041
|
+
"`aft_transform` removed: usage data showed agents never called it — `edit` covers everything it did, and every request now carries a smaller tool surface.",
|
|
36042
|
+
"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.",
|
|
36043
|
+
"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."
|
|
35691
36044
|
];
|
|
35692
36045
|
var ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/DSa65w8wuf";
|
|
35693
|
-
var ALL_ONLY_TOOLS = new Set([
|
|
35694
|
-
"aft_callgraph",
|
|
35695
|
-
"aft_delete",
|
|
35696
|
-
"aft_move",
|
|
35697
|
-
"aft_transform",
|
|
35698
|
-
"aft_refactor"
|
|
35699
|
-
]);
|
|
36046
|
+
var ALL_ONLY_TOOLS = new Set(["aft_callgraph", "aft_delete", "aft_move", "aft_refactor"]);
|
|
35700
36047
|
var pendingEagerWarnings = new Map;
|
|
35701
36048
|
function isConfigureWarning(value) {
|
|
35702
36049
|
if (!value || typeof value !== "object" || Array.isArray(value))
|
|
@@ -35774,7 +36121,6 @@ function resolveToolSurface(config2) {
|
|
|
35774
36121
|
move: false,
|
|
35775
36122
|
astSearch: false,
|
|
35776
36123
|
astReplace: false,
|
|
35777
|
-
structure: false,
|
|
35778
36124
|
refactor: false
|
|
35779
36125
|
};
|
|
35780
36126
|
}
|
|
@@ -35797,7 +36143,6 @@ function resolveToolSurface(config2) {
|
|
|
35797
36143
|
move: false,
|
|
35798
36144
|
astSearch: ok("ast_grep_search"),
|
|
35799
36145
|
astReplace: ok("ast_grep_replace"),
|
|
35800
|
-
structure: false,
|
|
35801
36146
|
refactor: false
|
|
35802
36147
|
};
|
|
35803
36148
|
if (surface === "all") {
|
|
@@ -35806,7 +36151,6 @@ function resolveToolSurface(config2) {
|
|
|
35806
36151
|
navigate: allOnly("aft_callgraph"),
|
|
35807
36152
|
delete: allOnly("aft_delete"),
|
|
35808
36153
|
move: allOnly("aft_move"),
|
|
35809
|
-
structure: allOnly("aft_transform"),
|
|
35810
36154
|
refactor: allOnly("aft_refactor")
|
|
35811
36155
|
};
|
|
35812
36156
|
}
|
|
@@ -36012,7 +36356,7 @@ ${lines}
|
|
|
36012
36356
|
}
|
|
36013
36357
|
const surface = resolveToolSurface(config2);
|
|
36014
36358
|
if (surface.hoistBash && resolveBashConfig(config2).enabled) {
|
|
36015
|
-
registerBashTool(pi, ctx);
|
|
36359
|
+
registerBashTool(pi, ctx, surface.semantic);
|
|
36016
36360
|
}
|
|
36017
36361
|
registerHoistedTools(pi, ctx, surface);
|
|
36018
36362
|
if (surface.outline || surface.zoom) {
|
|
@@ -36042,9 +36386,6 @@ ${lines}
|
|
|
36042
36386
|
if (surface.delete || surface.move) {
|
|
36043
36387
|
registerFsTools(pi, ctx, surface);
|
|
36044
36388
|
}
|
|
36045
|
-
if (surface.structure) {
|
|
36046
|
-
registerStructureTool(pi, ctx);
|
|
36047
|
-
}
|
|
36048
36389
|
if (surface.refactor) {
|
|
36049
36390
|
registerRefactorTool(pi, ctx);
|
|
36050
36391
|
}
|
|
@@ -36054,8 +36395,9 @@ ${lines}
|
|
|
36054
36395
|
const sessionID = resolveSessionId(extCtx);
|
|
36055
36396
|
const bgContent = await appendToolResultBgCompletions({ ctx, directory: extCtx.cwd, sessionID }, event.content);
|
|
36056
36397
|
let content = bgContent ?? event.content;
|
|
36398
|
+
const isInspect = typeof event.details === "object" && event.details !== null && "scanner_state" in event.details;
|
|
36057
36399
|
const activeBridge = pool.getActiveBridgeForRoot(extCtx.cwd);
|
|
36058
|
-
const bar = statusBarBlockForSession(sessionID, activeBridge?.getStatusBar());
|
|
36400
|
+
const bar = statusBarBlockForSession(sessionID, activeBridge?.getStatusBar(), isInspect);
|
|
36059
36401
|
if (bar)
|
|
36060
36402
|
content = [...content, { type: "text", text: bar }];
|
|
36061
36403
|
if (content === event.content)
|