@cortexkit/aft-opencode 0.34.0 → 0.35.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/dist/config.d.ts +8 -6
- package/dist/config.d.ts.map +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +190 -49
- package/dist/shared/bash-hints.d.ts +1 -23
- package/dist/shared/bash-hints.d.ts.map +1 -1
- package/dist/status-bar-inject.d.ts +11 -0
- package/dist/status-bar-inject.d.ts.map +1 -0
- package/dist/tui.js +88 -5
- package/dist/workflow-hints.d.ts.map +1 -1
- package/package.json +8 -8
- package/src/shared/bash-hints.ts +4 -45
- package/src/tui/index.tsx +15 -1
- package/src/tui/sidebar.tsx +106 -4
package/dist/config.d.ts
CHANGED
|
@@ -7,8 +7,8 @@ export declare const ConfigureWarningsDeliveryEnum: z.ZodEnum<{
|
|
|
7
7
|
}>;
|
|
8
8
|
export type ConfigureWarningsDelivery = z.infer<typeof ConfigureWarningsDeliveryEnum>;
|
|
9
9
|
export declare const LspServerSchema: z.ZodObject<{
|
|
10
|
-
extensions: z.ZodArray<z.ZodString
|
|
11
|
-
binary: z.ZodString
|
|
10
|
+
extensions: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
11
|
+
binary: z.ZodOptional<z.ZodString>;
|
|
12
12
|
args: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
13
13
|
root_markers: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
14
14
|
disabled: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
@@ -101,8 +101,8 @@ export declare const AftConfigSchema: z.ZodObject<{
|
|
|
101
101
|
}, z.core.$strip>>;
|
|
102
102
|
lsp: z.ZodOptional<z.ZodObject<{
|
|
103
103
|
servers: z.ZodOptional<z.ZodRecord<z.ZodString, z.ZodObject<{
|
|
104
|
-
extensions: z.ZodArray<z.ZodString
|
|
105
|
-
binary: z.ZodString
|
|
104
|
+
extensions: z.ZodOptional<z.ZodArray<z.ZodString>>;
|
|
105
|
+
binary: z.ZodOptional<z.ZodString>;
|
|
106
106
|
args: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
107
107
|
root_markers: z.ZodDefault<z.ZodOptional<z.ZodArray<z.ZodString>>>;
|
|
108
108
|
disabled: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
@@ -141,8 +141,10 @@ export type AftConfig = z.infer<typeof AftConfigSchema>;
|
|
|
141
141
|
export type LspServerConfig = z.infer<typeof LspServerSchema>;
|
|
142
142
|
export interface ConfigureLspServer {
|
|
143
143
|
id: string;
|
|
144
|
-
extensions
|
|
145
|
-
|
|
144
|
+
/** Omitted when overriding a built-in server to inherit its extensions. */
|
|
145
|
+
extensions?: string[];
|
|
146
|
+
/** Omitted when overriding a built-in server to inherit its binary. */
|
|
147
|
+
binary?: string;
|
|
146
148
|
args: string[];
|
|
147
149
|
root_markers: string[];
|
|
148
150
|
disabled: boolean;
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAcA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,qBAAqB,CAAC;AA6MlD;;;;;;;;;;;;;;;;;GAiBG;AAQH,QAAA,MAAM,MAAM,EAAE,MAA6D,CAAC;AA80B5E,eAAe,MAAM,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -11593,11 +11593,81 @@ function error(message, meta) {
|
|
|
11593
11593
|
console.error(`[aft-bridge] ERROR: ${message}`);
|
|
11594
11594
|
}
|
|
11595
11595
|
}
|
|
11596
|
+
// ../aft-bridge/dist/bash-hints.js
|
|
11597
|
+
var CONFLICT_HINT = `
|
|
11598
|
+
|
|
11599
|
+
[Hint] Use aft_conflicts to see all conflict regions across files in a single call.`;
|
|
11600
|
+
var GREP_HINT = `
|
|
11601
|
+
|
|
11602
|
+
[Hint] Use the grep tool instead of bash for faster indexed search.`;
|
|
11603
|
+
function maybeAppendConflictsHint(output) {
|
|
11604
|
+
if (!output.includes("Automatic merge failed; fix conflicts"))
|
|
11605
|
+
return output;
|
|
11606
|
+
if (!/^CONFLICT \(|^error: could not apply /m.test(output))
|
|
11607
|
+
return output;
|
|
11608
|
+
return output + CONFLICT_HINT;
|
|
11609
|
+
}
|
|
11610
|
+
function maybeAppendGrepHint(output, command) {
|
|
11611
|
+
const probe = command !== undefined ? command : output.slice(0, 300).split(`
|
|
11612
|
+
`)[0] ?? "";
|
|
11613
|
+
if (!/\b(rg|grep)\s/.test(probe))
|
|
11614
|
+
return output;
|
|
11615
|
+
return output + GREP_HINT;
|
|
11616
|
+
}
|
|
11596
11617
|
// ../aft-bridge/dist/bridge.js
|
|
11597
11618
|
import { spawn } from "node:child_process";
|
|
11598
11619
|
import { homedir } from "node:os";
|
|
11599
11620
|
import { join } from "node:path";
|
|
11600
11621
|
import { StringDecoder } from "node:string_decoder";
|
|
11622
|
+
|
|
11623
|
+
// ../aft-bridge/dist/status-bar.js
|
|
11624
|
+
var STATUS_BAR_HEARTBEAT_CALLS = 15;
|
|
11625
|
+
function createStatusBarEmitState() {
|
|
11626
|
+
return { callsSinceEmit: 0 };
|
|
11627
|
+
}
|
|
11628
|
+
function parseStatusBarCounts(value) {
|
|
11629
|
+
if (!value || typeof value !== "object")
|
|
11630
|
+
return;
|
|
11631
|
+
const record = value;
|
|
11632
|
+
const num = (key) => {
|
|
11633
|
+
const raw = record[key];
|
|
11634
|
+
return typeof raw === "number" && Number.isFinite(raw) ? raw : 0;
|
|
11635
|
+
};
|
|
11636
|
+
return {
|
|
11637
|
+
errors: num("errors"),
|
|
11638
|
+
warnings: num("warnings"),
|
|
11639
|
+
dead_code: num("dead_code"),
|
|
11640
|
+
unused_exports: num("unused_exports"),
|
|
11641
|
+
duplicates: num("duplicates"),
|
|
11642
|
+
todos: num("todos"),
|
|
11643
|
+
tier2_stale: record.tier2_stale === true
|
|
11644
|
+
};
|
|
11645
|
+
}
|
|
11646
|
+
function countsEqual(a, b) {
|
|
11647
|
+
return a.errors === b.errors && a.warnings === b.warnings && a.dead_code === b.dead_code && a.unused_exports === b.unused_exports && a.duplicates === b.duplicates && a.todos === b.todos && a.tier2_stale === b.tier2_stale;
|
|
11648
|
+
}
|
|
11649
|
+
function shouldEmitStatusBar(state, next) {
|
|
11650
|
+
const changed = state.last === undefined || !countsEqual(state.last, next);
|
|
11651
|
+
state.callsSinceEmit += 1;
|
|
11652
|
+
const heartbeat = state.callsSinceEmit >= STATUS_BAR_HEARTBEAT_CALLS;
|
|
11653
|
+
if (changed || heartbeat) {
|
|
11654
|
+
state.last = next;
|
|
11655
|
+
state.callsSinceEmit = 0;
|
|
11656
|
+
return true;
|
|
11657
|
+
}
|
|
11658
|
+
return false;
|
|
11659
|
+
}
|
|
11660
|
+
function formatStatusBar(counts) {
|
|
11661
|
+
const staleMark = counts.tier2_stale ? "~" : "";
|
|
11662
|
+
return `[AFT E${counts.errors} W${counts.warnings} | ` + `${staleMark}D${counts.dead_code} U${counts.unused_exports} C${counts.duplicates} | ` + `T${counts.todos}]`;
|
|
11663
|
+
}
|
|
11664
|
+
function statusBarLine(counts) {
|
|
11665
|
+
return `
|
|
11666
|
+
|
|
11667
|
+
${formatStatusBar(counts)}`;
|
|
11668
|
+
}
|
|
11669
|
+
|
|
11670
|
+
// ../aft-bridge/dist/bridge.js
|
|
11601
11671
|
var DEFAULT_BRIDGE_TIMEOUT_MS = 30000;
|
|
11602
11672
|
var BRIDGE_HANG_TIMEOUT_THRESHOLD = 2;
|
|
11603
11673
|
var SEMANTIC_TIMEOUT_SAFETY_MARGIN_MS = 5000;
|
|
@@ -11708,6 +11778,7 @@ class BinaryBridge {
|
|
|
11708
11778
|
configureWarningClients = new Map;
|
|
11709
11779
|
restartResetTimer = null;
|
|
11710
11780
|
lastChildActivityAt = 0;
|
|
11781
|
+
lastStatusBar;
|
|
11711
11782
|
consecutiveRequestTimeouts = 0;
|
|
11712
11783
|
errorPrefix;
|
|
11713
11784
|
logger;
|
|
@@ -12085,6 +12156,7 @@ class BinaryBridge {
|
|
|
12085
12156
|
this.spawnProcess(triggeringSessionId);
|
|
12086
12157
|
}
|
|
12087
12158
|
spawnProcess(triggeringSessionId) {
|
|
12159
|
+
this.lastStatusBar = undefined;
|
|
12088
12160
|
if (triggeringSessionId) {
|
|
12089
12161
|
this.sessionLogVia(triggeringSessionId, `Spawning binary: ${this.binaryPath} (cwd: ${this.cwd})`);
|
|
12090
12162
|
} else {
|
|
@@ -12286,6 +12358,7 @@ class BinaryBridge {
|
|
|
12286
12358
|
clearTimeout(entry.timer);
|
|
12287
12359
|
this.consecutiveRequestTimeouts = 0;
|
|
12288
12360
|
this.scheduleRestartCountReset();
|
|
12361
|
+
this.captureStatusBar(response);
|
|
12289
12362
|
entry.resolve(response);
|
|
12290
12363
|
} else if (typeof response.type === "string") {
|
|
12291
12364
|
this.logVia(`Ignoring unknown stdout push frame type: ${response.type}`);
|
|
@@ -12295,6 +12368,14 @@ class BinaryBridge {
|
|
|
12295
12368
|
}
|
|
12296
12369
|
}
|
|
12297
12370
|
}
|
|
12371
|
+
captureStatusBar(response) {
|
|
12372
|
+
const parsed = parseStatusBarCounts(response.status_bar);
|
|
12373
|
+
if (parsed)
|
|
12374
|
+
this.lastStatusBar = parsed;
|
|
12375
|
+
}
|
|
12376
|
+
getStatusBar() {
|
|
12377
|
+
return this.lastStatusBar;
|
|
12378
|
+
}
|
|
12298
12379
|
handleTimeout(triggeringSessionId) {
|
|
12299
12380
|
this.consecutiveRequestTimeouts = 0;
|
|
12300
12381
|
this.rejectAllPending(new Error(`${this.errorPrefix} bridge killed during sibling timeout — request aborted`));
|
|
@@ -12659,6 +12740,20 @@ async function fetchLatestTag() {
|
|
|
12659
12740
|
clearTimeout(timeout);
|
|
12660
12741
|
}
|
|
12661
12742
|
}
|
|
12743
|
+
// ../aft-bridge/dist/jsonc.js
|
|
12744
|
+
function stripJsoncSymbols(value) {
|
|
12745
|
+
if (Array.isArray(value)) {
|
|
12746
|
+
return value.map((item) => stripJsoncSymbols(item));
|
|
12747
|
+
}
|
|
12748
|
+
if (value !== null && typeof value === "object") {
|
|
12749
|
+
const out = {};
|
|
12750
|
+
for (const [key, val] of Object.entries(value)) {
|
|
12751
|
+
out[key] = stripJsoncSymbols(val);
|
|
12752
|
+
}
|
|
12753
|
+
return out;
|
|
12754
|
+
}
|
|
12755
|
+
return value;
|
|
12756
|
+
}
|
|
12662
12757
|
// ../aft-bridge/dist/migration.js
|
|
12663
12758
|
import { spawnSync as spawnSync2 } from "node:child_process";
|
|
12664
12759
|
import { existsSync as existsSync4, mkdirSync as mkdirSync4 } from "node:fs";
|
|
@@ -12717,7 +12812,7 @@ function markAnnouncementSeen(storageRoot, harness, currentVersion) {
|
|
|
12717
12812
|
|
|
12718
12813
|
// ../aft-bridge/dist/resolver.js
|
|
12719
12814
|
import { execSync } from "node:child_process";
|
|
12720
|
-
import { chmodSync as chmodSync2, copyFileSync as copyFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync3, renameSync as renameSync3, unlinkSync as unlinkSync2 } from "node:fs";
|
|
12815
|
+
import { chmodSync as chmodSync2, closeSync as closeSync2, copyFileSync as copyFileSync2, existsSync as existsSync3, mkdirSync as mkdirSync3, openSync as openSync2, readSync, renameSync as renameSync3, unlinkSync as unlinkSync2 } from "node:fs";
|
|
12721
12816
|
import { createRequire as createRequire2 } from "node:module";
|
|
12722
12817
|
import { homedir as homedir3 } from "node:os";
|
|
12723
12818
|
import { join as join4 } from "node:path";
|
|
@@ -12798,6 +12893,37 @@ function probeBinaryCandidate(binaryPath, source, expectedVersion) {
|
|
|
12798
12893
|
function parsePathLookupOutput(output) {
|
|
12799
12894
|
return output.split(/\r?\n/).map((candidate) => candidate.trim()).filter(Boolean);
|
|
12800
12895
|
}
|
|
12896
|
+
function isNativeExecutable(binaryPath) {
|
|
12897
|
+
let fd = null;
|
|
12898
|
+
try {
|
|
12899
|
+
fd = openSync2(binaryPath, "r");
|
|
12900
|
+
const buf = Buffer.alloc(4);
|
|
12901
|
+
const read = readSync(fd, buf, 0, 4, 0);
|
|
12902
|
+
if (read < 2)
|
|
12903
|
+
return false;
|
|
12904
|
+
const b0 = buf[0];
|
|
12905
|
+
const b1 = buf[1];
|
|
12906
|
+
if (b0 === 35 && b1 === 33)
|
|
12907
|
+
return false;
|
|
12908
|
+
const m32 = buf.readUInt32BE(0);
|
|
12909
|
+
const machO = new Set([4277009102, 4277009103, 3472551422, 3489328638, 3405691582]);
|
|
12910
|
+
if (read >= 4 && machO.has(m32))
|
|
12911
|
+
return true;
|
|
12912
|
+
if (read >= 4 && m32 === 2135247942)
|
|
12913
|
+
return true;
|
|
12914
|
+
if (b0 === 77 && b1 === 90)
|
|
12915
|
+
return true;
|
|
12916
|
+
return false;
|
|
12917
|
+
} catch {
|
|
12918
|
+
return false;
|
|
12919
|
+
} finally {
|
|
12920
|
+
if (fd !== null) {
|
|
12921
|
+
try {
|
|
12922
|
+
closeSync2(fd);
|
|
12923
|
+
} catch {}
|
|
12924
|
+
}
|
|
12925
|
+
}
|
|
12926
|
+
}
|
|
12801
12927
|
function platformKey(platform = process.platform, arch = process.arch) {
|
|
12802
12928
|
const archMap = PLATFORM_ARCH_MAP[platform];
|
|
12803
12929
|
if (!archMap) {
|
|
@@ -12851,6 +12977,10 @@ function findBinarySync(expectedVersion) {
|
|
|
12851
12977
|
stdio: ["pipe", "pipe", "pipe"]
|
|
12852
12978
|
}).trim();
|
|
12853
12979
|
for (const candidate of parsePathLookupOutput(result)) {
|
|
12980
|
+
if (!isNativeExecutable(candidate)) {
|
|
12981
|
+
warn(`PATH binary at ${candidate} is not a native executable (script shim?); skipping`);
|
|
12982
|
+
continue;
|
|
12983
|
+
}
|
|
12854
12984
|
const usable = probeBinaryCandidate(candidate, "PATH", expectedVersion);
|
|
12855
12985
|
if (usable)
|
|
12856
12986
|
return usable;
|
|
@@ -13000,7 +13130,7 @@ async function ensureStorageMigrated(opts) {
|
|
|
13000
13130
|
// ../aft-bridge/dist/onnx-runtime.js
|
|
13001
13131
|
import { execFileSync } from "node:child_process";
|
|
13002
13132
|
import { createHash as createHash2 } from "node:crypto";
|
|
13003
|
-
import { chmodSync as chmodSync3, closeSync as
|
|
13133
|
+
import { chmodSync as chmodSync3, closeSync as closeSync3, copyFileSync as copyFileSync3, createWriteStream as createWriteStream2, existsSync as existsSync5, lstatSync, mkdirSync as mkdirSync5, openSync as openSync3, readdirSync, readFileSync as readFileSync3, readlinkSync, realpathSync, rmSync as rmSync2, statSync as statSync2, symlinkSync, unlinkSync as unlinkSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
13004
13134
|
import { basename, dirname as dirname3, isAbsolute, join as join6, relative, resolve, win32 } from "node:path";
|
|
13005
13135
|
import { Readable as Readable2 } from "node:stream";
|
|
13006
13136
|
import { pipeline as pipeline2 } from "node:stream/promises";
|
|
@@ -13575,13 +13705,13 @@ function sha256File(path) {
|
|
|
13575
13705
|
function acquireLock(lockPath) {
|
|
13576
13706
|
const tryClaim = () => {
|
|
13577
13707
|
try {
|
|
13578
|
-
const fd =
|
|
13708
|
+
const fd = openSync3(lockPath, "wx");
|
|
13579
13709
|
try {
|
|
13580
13710
|
writeFileSync2(fd, `${process.pid}
|
|
13581
13711
|
${new Date().toISOString()}
|
|
13582
13712
|
`);
|
|
13583
13713
|
} finally {
|
|
13584
|
-
|
|
13714
|
+
closeSync3(fd);
|
|
13585
13715
|
}
|
|
13586
13716
|
return true;
|
|
13587
13717
|
} catch (err) {
|
|
@@ -13717,7 +13847,8 @@ class BridgePool {
|
|
|
13717
13847
|
onBashLongRunning: options.onBashLongRunning,
|
|
13718
13848
|
onBashPatternMatch: options.onBashPatternMatch,
|
|
13719
13849
|
errorPrefix: options.errorPrefix,
|
|
13720
|
-
logger: options.logger
|
|
13850
|
+
logger: options.logger,
|
|
13851
|
+
childEnv: options.childEnv
|
|
13721
13852
|
};
|
|
13722
13853
|
this.configOverrides = configOverrides;
|
|
13723
13854
|
if (Number.isFinite(this.idleTimeoutMs)) {
|
|
@@ -13850,6 +13981,9 @@ class BridgePool {
|
|
|
13850
13981
|
_testGetConfigOverrides() {
|
|
13851
13982
|
return { ...this.configOverrides };
|
|
13852
13983
|
}
|
|
13984
|
+
_testGetBridgeOptions() {
|
|
13985
|
+
return { ...this.bridgeOptions };
|
|
13986
|
+
}
|
|
13853
13987
|
}
|
|
13854
13988
|
function normalizeKey(projectRoot) {
|
|
13855
13989
|
const stripped = projectRoot.replace(/[/\\]+$/, "");
|
|
@@ -14897,10 +15031,10 @@ function formatDuration(completion) {
|
|
|
14897
15031
|
}
|
|
14898
15032
|
|
|
14899
15033
|
// src/config.ts
|
|
14900
|
-
var import_comment_json = __toESM(require_src2(), 1);
|
|
14901
15034
|
import { existsSync as existsSync6, readFileSync as readFileSync4, renameSync as renameSync4, unlinkSync as unlinkSync4, writeFileSync as writeFileSync3 } from "node:fs";
|
|
14902
15035
|
import { homedir as homedir5 } from "node:os";
|
|
14903
15036
|
import { join as join8 } from "node:path";
|
|
15037
|
+
var import_comment_json = __toESM(require_src2(), 1);
|
|
14904
15038
|
|
|
14905
15039
|
// ../../node_modules/.bun/zod@4.3.6/node_modules/zod/v4/classic/external.js
|
|
14906
15040
|
var exports_external = {};
|
|
@@ -28473,8 +28607,8 @@ var LspExtensionSchema = exports_external.string().trim().min(1).refine((value)
|
|
|
28473
28607
|
message: "Extension must include characters other than leading dots"
|
|
28474
28608
|
});
|
|
28475
28609
|
var LspServerEntrySchema = exports_external.object({
|
|
28476
|
-
extensions: exports_external.array(LspExtensionSchema).min(1),
|
|
28477
|
-
binary: exports_external.string().trim().min(1),
|
|
28610
|
+
extensions: exports_external.array(LspExtensionSchema).min(1).optional(),
|
|
28611
|
+
binary: exports_external.string().trim().min(1).optional(),
|
|
28478
28612
|
args: exports_external.array(exports_external.string()).optional().default([]),
|
|
28479
28613
|
root_markers: exports_external.array(exports_external.string().trim().min(1)).optional().default([".git"]),
|
|
28480
28614
|
disabled: exports_external.boolean().optional().default(false),
|
|
@@ -28579,12 +28713,16 @@ function resolveLspConfigForConfigure(config2) {
|
|
|
28579
28713
|
const servers = Object.entries(config2.lsp?.servers ?? {}).map(([id, server]) => {
|
|
28580
28714
|
const entry = {
|
|
28581
28715
|
id,
|
|
28582
|
-
extensions: server.extensions.map(normalizeLspExtension),
|
|
28583
|
-
binary: server.binary,
|
|
28584
28716
|
args: server.args,
|
|
28585
28717
|
root_markers: server.root_markers,
|
|
28586
28718
|
disabled: server.disabled
|
|
28587
28719
|
};
|
|
28720
|
+
if (server.extensions && server.extensions.length > 0) {
|
|
28721
|
+
entry.extensions = server.extensions.map(normalizeLspExtension);
|
|
28722
|
+
}
|
|
28723
|
+
if (server.binary) {
|
|
28724
|
+
entry.binary = server.binary;
|
|
28725
|
+
}
|
|
28588
28726
|
if (server.env && Object.keys(server.env).length > 0) {
|
|
28589
28727
|
entry.env = server.env;
|
|
28590
28728
|
}
|
|
@@ -28900,14 +29038,15 @@ function loadConfigFromPath(configPath) {
|
|
|
28900
29038
|
const content = readFileSync4(configPath, "utf-8");
|
|
28901
29039
|
const rawConfig = import_comment_json.parse(content);
|
|
28902
29040
|
migrateRawConfig(rawConfig, configPath, { log: log2, warn: warn2 });
|
|
28903
|
-
const
|
|
29041
|
+
const cleanConfig = stripJsoncSymbols(rawConfig);
|
|
29042
|
+
const result = AftConfigSchema.safeParse(cleanConfig);
|
|
28904
29043
|
if (result.success) {
|
|
28905
29044
|
log2(`Config loaded from ${configPath}`);
|
|
28906
29045
|
return result.data;
|
|
28907
29046
|
}
|
|
28908
29047
|
const errorMsg = result.error.issues.map((i) => `${i.path.join(".")}: ${i.message}`).join(", ");
|
|
28909
29048
|
warn2(`Config validation error in ${configPath}: ${errorMsg}`);
|
|
28910
|
-
return parseConfigPartially(
|
|
29049
|
+
return parseConfigPartially(cleanConfig);
|
|
28911
29050
|
} catch (err) {
|
|
28912
29051
|
const errorMsg = err instanceof Error ? err.message : String(err);
|
|
28913
29052
|
error2(`Error loading config from ${configPath}: ${errorMsg}`);
|
|
@@ -29612,10 +29751,10 @@ async function flushConfigureWarningsOnIdle(sessionId) {
|
|
|
29612
29751
|
|
|
29613
29752
|
// src/hooks/auto-update-checker/index.ts
|
|
29614
29753
|
import {
|
|
29615
|
-
closeSync as
|
|
29754
|
+
closeSync as closeSync4,
|
|
29616
29755
|
existsSync as existsSync10,
|
|
29617
29756
|
mkdirSync as mkdirSync6,
|
|
29618
|
-
openSync as
|
|
29757
|
+
openSync as openSync4,
|
|
29619
29758
|
readFileSync as readFileSync8,
|
|
29620
29759
|
renameSync as renameSync5,
|
|
29621
29760
|
rmSync as rmSync4,
|
|
@@ -30166,7 +30305,7 @@ function claimCheckSlot(storageDir, intervalMs) {
|
|
|
30166
30305
|
const lockPath = `${file2}.lock`;
|
|
30167
30306
|
let lockFd;
|
|
30168
30307
|
try {
|
|
30169
|
-
lockFd =
|
|
30308
|
+
lockFd = openSync4(lockPath, "wx");
|
|
30170
30309
|
writeFileSync6(lockFd, JSON.stringify({ pid: process.pid, startedMs: Date.now() }));
|
|
30171
30310
|
} catch (err) {
|
|
30172
30311
|
if (err.code !== "EEXIST") {
|
|
@@ -30177,7 +30316,7 @@ function claimCheckSlot(storageDir, intervalMs) {
|
|
|
30177
30316
|
const lock = {
|
|
30178
30317
|
release: () => {
|
|
30179
30318
|
try {
|
|
30180
|
-
|
|
30319
|
+
closeSync4(lockFd);
|
|
30181
30320
|
} catch {}
|
|
30182
30321
|
rmSync4(lockPath, { force: true });
|
|
30183
30322
|
}
|
|
@@ -30307,9 +30446,9 @@ import { join as join15 } from "node:path";
|
|
|
30307
30446
|
|
|
30308
30447
|
// src/lsp-cache.ts
|
|
30309
30448
|
import {
|
|
30310
|
-
closeSync as
|
|
30449
|
+
closeSync as closeSync5,
|
|
30311
30450
|
mkdirSync as mkdirSync7,
|
|
30312
|
-
openSync as
|
|
30451
|
+
openSync as openSync5,
|
|
30313
30452
|
readFileSync as readFileSync9,
|
|
30314
30453
|
statSync as statSync4,
|
|
30315
30454
|
unlinkSync as unlinkSync5,
|
|
@@ -30402,13 +30541,13 @@ function acquireInstallLock(lockKey) {
|
|
|
30402
30541
|
const lock = lockPath(lockKey);
|
|
30403
30542
|
const tryClaim = () => {
|
|
30404
30543
|
try {
|
|
30405
|
-
const fd =
|
|
30544
|
+
const fd = openSync5(lock, "wx");
|
|
30406
30545
|
try {
|
|
30407
30546
|
writeFileSync7(fd, `${process.pid}
|
|
30408
30547
|
${new Date().toISOString()}
|
|
30409
30548
|
`);
|
|
30410
30549
|
} finally {
|
|
30411
|
-
|
|
30550
|
+
closeSync5(fd);
|
|
30412
30551
|
}
|
|
30413
30552
|
return true;
|
|
30414
30553
|
} catch (err) {
|
|
@@ -32015,29 +32154,6 @@ function normalizeToolMap(tools) {
|
|
|
32015
32154
|
}
|
|
32016
32155
|
return tools;
|
|
32017
32156
|
}
|
|
32018
|
-
|
|
32019
|
-
// src/shared/bash-hints.ts
|
|
32020
|
-
var CONFLICT_HINT = `
|
|
32021
|
-
|
|
32022
|
-
[Hint] Use aft_conflicts to see all conflict regions across files in a single call.`;
|
|
32023
|
-
var GREP_HINT = `
|
|
32024
|
-
|
|
32025
|
-
[Hint] Use the grep tool instead of bash for faster indexed search.`;
|
|
32026
|
-
function maybeAppendConflictsHint(output) {
|
|
32027
|
-
if (!output.includes("Automatic merge failed; fix conflicts"))
|
|
32028
|
-
return output;
|
|
32029
|
-
if (!/^CONFLICT \(|^error: could not apply /m.test(output))
|
|
32030
|
-
return output;
|
|
32031
|
-
return output + CONFLICT_HINT;
|
|
32032
|
-
}
|
|
32033
|
-
function maybeAppendGrepHint(output) {
|
|
32034
|
-
const firstLine = output.slice(0, 300).split(`
|
|
32035
|
-
`)[0] ?? "";
|
|
32036
|
-
if (!/\b(rg|grep)\s/.test(firstLine))
|
|
32037
|
-
return output;
|
|
32038
|
-
return output + GREP_HINT;
|
|
32039
|
-
}
|
|
32040
|
-
|
|
32041
32157
|
// src/shared/pty-cache.ts
|
|
32042
32158
|
var import_headless = __toESM(require_xterm_headless(), 1);
|
|
32043
32159
|
import * as fs2 from "node:fs/promises";
|
|
@@ -32631,6 +32747,22 @@ function registerShutdownCleanup(fn) {
|
|
|
32631
32747
|
};
|
|
32632
32748
|
}
|
|
32633
32749
|
|
|
32750
|
+
// src/status-bar-inject.ts
|
|
32751
|
+
var emitStateBySession = new Map;
|
|
32752
|
+
function statusBarSuffixForSession(sessionID, counts) {
|
|
32753
|
+
if (!counts)
|
|
32754
|
+
return "";
|
|
32755
|
+
let state = emitStateBySession.get(sessionID);
|
|
32756
|
+
if (!state) {
|
|
32757
|
+
state = createStatusBarEmitState();
|
|
32758
|
+
emitStateBySession.set(sessionID, state);
|
|
32759
|
+
}
|
|
32760
|
+
return shouldEmitStatusBar(state, counts) ? statusBarLine(counts) : "";
|
|
32761
|
+
}
|
|
32762
|
+
function clearStatusBarSession(sessionID) {
|
|
32763
|
+
emitStateBySession.delete(sessionID);
|
|
32764
|
+
}
|
|
32765
|
+
|
|
32634
32766
|
// src/tools/ast.ts
|
|
32635
32767
|
import { tool as tool3 } from "@opencode-ai/plugin";
|
|
32636
32768
|
|
|
@@ -36905,6 +37037,7 @@ function buildWorkflowHints(opts) {
|
|
|
36905
37037
|
}
|
|
36906
37038
|
if (hasInspect) {
|
|
36907
37039
|
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.");
|
|
37040
|
+
sections.push("**AFT status bar**: tool results may end with a one-line health bar `[AFT E<errors> W<warnings> | D<dead-code> U<unused-exports> C<clone/dup-groups> | T<todos>]` — an IDE-style glance that appears when a count changes. `E`/`W` are live LSP diagnostics for files touched this session (your universal compile-error signal across every language with an LSP). A `~` before `D` means the dead-code/unused/dup counts predate your latest edit — run `aft_inspect` for current numbers and detail. When `E>0`, you likely just introduced errors; investigate before moving on.");
|
|
36908
37041
|
}
|
|
36909
37042
|
if (hasNavigate) {
|
|
36910
37043
|
sections.push([
|
|
@@ -36984,13 +37117,14 @@ var PLUGIN_VERSION = (() => {
|
|
|
36984
37117
|
return "0.0.0";
|
|
36985
37118
|
}
|
|
36986
37119
|
})();
|
|
36987
|
-
var ANNOUNCEMENT_VERSION = "0.
|
|
37120
|
+
var ANNOUNCEMENT_VERSION = "0.35.0";
|
|
36988
37121
|
var ANNOUNCEMENT_FEATURES = [
|
|
36989
|
-
"
|
|
36990
|
-
"New
|
|
36991
|
-
"`
|
|
36992
|
-
"
|
|
36993
|
-
"`
|
|
37122
|
+
"Leaner semantic indexing — local embedding now uses about half the CPU and a fraction of the peak memory, with no change to results and no re-index needed.",
|
|
37123
|
+
"New IDE-style status bar on tool results — live LSP errors/warnings plus dead-code, unused-export, duplicate, and TODO counts at a glance.",
|
|
37124
|
+
"`aft_inspect` is more accurate (dead-code reachability, duplicate collapse, ranked findings) and a background scheduler keeps its counts fresh during long sessions.",
|
|
37125
|
+
"The TUI sidebar is now collapsible — click the `AFT` header to toggle a condensed view.",
|
|
37126
|
+
"`npx @cortexkit/aft --version` reports CLI, binary, and per-harness versions; `doctor --issue` can scope its report to a single session.",
|
|
37127
|
+
"New SCSS support and `.inc` files parsed as PHP."
|
|
36994
37128
|
];
|
|
36995
37129
|
var ANNOUNCEMENT_FOOTER = "Join us on Discord: https://discord.gg/DSa65w8wuf";
|
|
36996
37130
|
var plugin = async (input) => initializePluginForDirectory(input);
|
|
@@ -37398,6 +37532,7 @@ Install: ${getManualInstallHint()}`).catch(() => {});
|
|
|
37398
37532
|
const sessionID = extractSessionID(eventInput.event.properties);
|
|
37399
37533
|
if ((eventType === "session.deleted" || eventType === "session.shutdown") && sessionID) {
|
|
37400
37534
|
inspectTier2Idle.clear(sessionID);
|
|
37535
|
+
clearStatusBarSession(sessionID);
|
|
37401
37536
|
return;
|
|
37402
37537
|
}
|
|
37403
37538
|
if (eventType !== "session.idle")
|
|
@@ -37460,6 +37595,12 @@ Install: ${getManualInstallHint()}`).catch(() => {});
|
|
|
37460
37595
|
}
|
|
37461
37596
|
const sessionDir = getSessionDirectoryCached(toolInput.sessionID) ?? input.directory;
|
|
37462
37597
|
await appendInTurnBgCompletions({ ctx, directory: sessionDir, sessionID: toolInput.sessionID }, output);
|
|
37598
|
+
if (output.output !== undefined) {
|
|
37599
|
+
const activeBridge = ctx.pool.getActiveBridgeForRoot(sessionDir);
|
|
37600
|
+
const suffix = statusBarSuffixForSession(toolInput.sessionID, activeBridge?.getStatusBar());
|
|
37601
|
+
if (suffix)
|
|
37602
|
+
output.output += suffix;
|
|
37603
|
+
}
|
|
37463
37604
|
},
|
|
37464
37605
|
config: async (config2) => {
|
|
37465
37606
|
if (!config2 || typeof config2 !== "object")
|
|
@@ -1,24 +1,2 @@
|
|
|
1
|
-
|
|
2
|
-
* Append the `aft_conflicts` hint when the output indicates a real git merge
|
|
3
|
-
* or rebase produced conflicts.
|
|
4
|
-
*
|
|
5
|
-
* Gated on BOTH:
|
|
6
|
-
* - the "Automatic merge failed; fix conflicts" marker, AND
|
|
7
|
-
* - a git-conflict signal (`CONFLICT (...)` line or `error: could not apply`)
|
|
8
|
-
*
|
|
9
|
-
* Both conditions are required because `aft_conflicts` calls `git ls-files -u`,
|
|
10
|
-
* which fails with "not a git repository" outside a git working tree. The
|
|
11
|
-
* marker string can legitimately appear in docs, READMEs, test fixtures, and
|
|
12
|
-
* grep output, so we cannot key off it alone — a false-positive hint sends
|
|
13
|
-
* agents into a confusing error.
|
|
14
|
-
*/
|
|
15
|
-
export declare function maybeAppendConflictsHint(output: string): string;
|
|
16
|
-
/**
|
|
17
|
-
* Append the grep-tool hint when the bash command was a grep/rg invocation.
|
|
18
|
-
*
|
|
19
|
-
* Detected by examining the FIRST LINE of output (which is the echoed command
|
|
20
|
-
* line on most shells in foreground mode). We cap the slice at 300 chars so
|
|
21
|
-
* a single huge first line doesn't slow this hook.
|
|
22
|
-
*/
|
|
23
|
-
export declare function maybeAppendGrepHint(output: string): string;
|
|
1
|
+
export { maybeAppendConflictsHint, maybeAppendGrepHint } from "@cortexkit/aft-bridge";
|
|
24
2
|
//# sourceMappingURL=bash-hints.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"bash-hints.d.ts","sourceRoot":"","sources":["../../src/shared/bash-hints.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"bash-hints.d.ts","sourceRoot":"","sources":["../../src/shared/bash-hints.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,wBAAwB,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type StatusBarCounts } from "@cortexkit/aft-bridge";
|
|
2
|
+
/**
|
|
3
|
+
* Decide whether to surface the bar for this session+counts and, if so, return
|
|
4
|
+
* the line to append to the tool output. Returns `""` when suppressed
|
|
5
|
+
* (unchanged since last emit and heartbeat not elapsed) or when counts are
|
|
6
|
+
* absent (no inspect scan has populated Tier-2 yet).
|
|
7
|
+
*/
|
|
8
|
+
export declare function statusBarSuffixForSession(sessionID: string, counts: StatusBarCounts | undefined): string;
|
|
9
|
+
/** Drop a session's emit state (on session delete/shutdown). */
|
|
10
|
+
export declare function clearStatusBarSession(sessionID: string): void;
|
|
11
|
+
//# sourceMappingURL=status-bar-inject.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"status-bar-inject.d.ts","sourceRoot":"","sources":["../src/status-bar-inject.ts"],"names":[],"mappings":"AAOA,OAAO,EAEL,KAAK,eAAe,EAIrB,MAAM,uBAAuB,CAAC;AAI/B;;;;;GAKG;AACH,wBAAgB,yBAAyB,CACvC,SAAS,EAAE,MAAM,EACjB,MAAM,EAAE,eAAe,GAAG,SAAS,GAClC,MAAM,CAQR;AAED,gEAAgE;AAChE,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,CAE7D"}
|
package/dist/tui.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// src/tui/index.tsx
|
|
2
2
|
import { createMemo as createMemo2, createSignal as createSignal2, onCleanup as onCleanup2 } from "solid-js";
|
|
3
3
|
// package.json
|
|
4
|
-
var version = "0.
|
|
4
|
+
var version = "0.35.1";
|
|
5
5
|
|
|
6
6
|
// src/shared/rpc-client.ts
|
|
7
7
|
import { existsSync, readdirSync, readFileSync, unlinkSync } from "node:fs";
|
|
@@ -560,6 +560,37 @@ var SectionHeader = (props) => /* @__PURE__ */ jsxDEV("box", {
|
|
|
560
560
|
}, undefined, false, undefined, this)
|
|
561
561
|
}, undefined, false, undefined, this)
|
|
562
562
|
}, undefined, false, undefined, this);
|
|
563
|
+
function toneColor(theme, tone) {
|
|
564
|
+
switch (tone) {
|
|
565
|
+
case "ok":
|
|
566
|
+
return theme.success ?? theme.accent;
|
|
567
|
+
case "warn":
|
|
568
|
+
return theme.warning;
|
|
569
|
+
case "err":
|
|
570
|
+
return theme.error;
|
|
571
|
+
default:
|
|
572
|
+
return theme.textMuted;
|
|
573
|
+
}
|
|
574
|
+
}
|
|
575
|
+
var CollapsedRow = (props) => /* @__PURE__ */ jsxDEV("box", {
|
|
576
|
+
width: "100%",
|
|
577
|
+
flexDirection: "row",
|
|
578
|
+
justifyContent: "space-between",
|
|
579
|
+
children: [
|
|
580
|
+
/* @__PURE__ */ jsxDEV("text", {
|
|
581
|
+
fg: props.theme.textMuted,
|
|
582
|
+
children: props.label
|
|
583
|
+
}, undefined, false, undefined, this),
|
|
584
|
+
props.children
|
|
585
|
+
]
|
|
586
|
+
}, undefined, true, undefined, this);
|
|
587
|
+
function collapsedCompressionValue(compression) {
|
|
588
|
+
if (!compression || compression.project.events <= 0)
|
|
589
|
+
return null;
|
|
590
|
+
const { savings_tokens, original_tokens } = compression.project;
|
|
591
|
+
const pct = original_tokens > 0 ? Math.round(savings_tokens / original_tokens * 100) : 0;
|
|
592
|
+
return `${formatCount(savings_tokens)} / ${pct}%`;
|
|
593
|
+
}
|
|
563
594
|
function resolveTuiStorageDir() {
|
|
564
595
|
return resolveCortexKitStorageRoot();
|
|
565
596
|
}
|
|
@@ -579,8 +610,12 @@ function scopedSidebarSnapshot(scoped, directory, sessionID) {
|
|
|
579
610
|
return null;
|
|
580
611
|
return scoped.snapshot;
|
|
581
612
|
}
|
|
613
|
+
function shouldSuppressUninitializedDowngrade(incomingCacheRole, haveInitializedForContext) {
|
|
614
|
+
return incomingCacheRole === "not_initialized" && haveInitializedForContext;
|
|
615
|
+
}
|
|
582
616
|
var SidebarContent = (props) => {
|
|
583
617
|
const [status, setStatus] = createSignal(null);
|
|
618
|
+
const [collapsed, setCollapsed] = createSignal(false);
|
|
584
619
|
let inflight = null;
|
|
585
620
|
let generation = 0;
|
|
586
621
|
let debounceTimer;
|
|
@@ -637,6 +672,10 @@ var SidebarContent = (props) => {
|
|
|
637
672
|
return;
|
|
638
673
|
if (response && response.success !== false) {
|
|
639
674
|
const snapshot = coerceAftStatus(response);
|
|
675
|
+
const current = status();
|
|
676
|
+
const haveGoodForContext = current !== null && current.directory === directory && current.sessionID === sid && current.snapshot.cache_role !== "not_initialized";
|
|
677
|
+
if (shouldSuppressUninitializedDowngrade(snapshot.cache_role, haveGoodForContext))
|
|
678
|
+
return;
|
|
640
679
|
setStatus({ directory, sessionID: sid, snapshot });
|
|
641
680
|
requestRender();
|
|
642
681
|
}
|
|
@@ -749,6 +788,10 @@ var SidebarContent = (props) => {
|
|
|
749
788
|
flexDirection: "row",
|
|
750
789
|
justifyContent: "space-between",
|
|
751
790
|
alignItems: "center",
|
|
791
|
+
onMouseDown: () => {
|
|
792
|
+
if (!notInitialized())
|
|
793
|
+
setCollapsed((x) => !x);
|
|
794
|
+
},
|
|
752
795
|
children: [
|
|
753
796
|
/* @__PURE__ */ jsxDEV("box", {
|
|
754
797
|
flexDirection: "row",
|
|
@@ -761,8 +804,11 @@ var SidebarContent = (props) => {
|
|
|
761
804
|
children: /* @__PURE__ */ jsxDEV("text", {
|
|
762
805
|
fg: props.theme.background,
|
|
763
806
|
children: /* @__PURE__ */ jsxDEV("b", {
|
|
764
|
-
children:
|
|
765
|
-
|
|
807
|
+
children: [
|
|
808
|
+
notInitialized() ? "" : collapsed() ? "▶ " : "▼ ",
|
|
809
|
+
"AFT"
|
|
810
|
+
]
|
|
811
|
+
}, undefined, true, undefined, this)
|
|
766
812
|
}, undefined, false, undefined, this)
|
|
767
813
|
}, undefined, false, undefined, this),
|
|
768
814
|
s()?.degraded && /* @__PURE__ */ jsxDEV("box", {
|
|
@@ -807,7 +853,39 @@ var SidebarContent = (props) => {
|
|
|
807
853
|
children: s().message || "AFT bridge is now spawned lazily, information here will be populated after first tool call."
|
|
808
854
|
}, undefined, false, undefined, this)
|
|
809
855
|
}, undefined, false, undefined, this),
|
|
810
|
-
!notInitialized() && /* @__PURE__ */ jsxDEV(
|
|
856
|
+
!notInitialized() && collapsed() && /* @__PURE__ */ jsxDEV("box", {
|
|
857
|
+
width: "100%",
|
|
858
|
+
flexDirection: "column",
|
|
859
|
+
children: [
|
|
860
|
+
/* @__PURE__ */ jsxDEV(CollapsedRow, {
|
|
861
|
+
theme: props.theme,
|
|
862
|
+
label: "Search Index",
|
|
863
|
+
children: /* @__PURE__ */ jsxDEV("text", {
|
|
864
|
+
fg: toneColor(props.theme, searchStatus().tone),
|
|
865
|
+
children: "●"
|
|
866
|
+
}, undefined, false, undefined, this)
|
|
867
|
+
}, undefined, false, undefined, this),
|
|
868
|
+
/* @__PURE__ */ jsxDEV(CollapsedRow, {
|
|
869
|
+
theme: props.theme,
|
|
870
|
+
label: "Semantic Index",
|
|
871
|
+
children: /* @__PURE__ */ jsxDEV("text", {
|
|
872
|
+
fg: toneColor(props.theme, semanticStatus().tone),
|
|
873
|
+
children: "●"
|
|
874
|
+
}, undefined, false, undefined, this)
|
|
875
|
+
}, undefined, false, undefined, this),
|
|
876
|
+
collapsedCompressionValue(s()?.compression) && /* @__PURE__ */ jsxDEV(CollapsedRow, {
|
|
877
|
+
theme: props.theme,
|
|
878
|
+
label: "Compression",
|
|
879
|
+
children: /* @__PURE__ */ jsxDEV("text", {
|
|
880
|
+
fg: props.theme.textMuted,
|
|
881
|
+
children: /* @__PURE__ */ jsxDEV("b", {
|
|
882
|
+
children: collapsedCompressionValue(s()?.compression)
|
|
883
|
+
}, undefined, false, undefined, this)
|
|
884
|
+
}, undefined, false, undefined, this)
|
|
885
|
+
}, undefined, false, undefined, this)
|
|
886
|
+
]
|
|
887
|
+
}, undefined, true, undefined, this),
|
|
888
|
+
!notInitialized() && !collapsed() && /* @__PURE__ */ jsxDEV(Fragment, {
|
|
811
889
|
children: [
|
|
812
890
|
/* @__PURE__ */ jsxDEV(SectionHeader, {
|
|
813
891
|
theme: props.theme,
|
|
@@ -1018,7 +1096,12 @@ var StatusDialog = (props) => {
|
|
|
1018
1096
|
if (controller.signal.aborted || requestGeneration !== pollGeneration)
|
|
1019
1097
|
return;
|
|
1020
1098
|
if (response.success !== false) {
|
|
1021
|
-
|
|
1099
|
+
const snapshot = coerceAftStatus(response);
|
|
1100
|
+
const current = status();
|
|
1101
|
+
if (shouldSuppressUninitializedDowngrade(snapshot.cache_role, current !== null && current.cache_role !== "not_initialized")) {
|
|
1102
|
+
return;
|
|
1103
|
+
}
|
|
1104
|
+
setStatus(snapshot);
|
|
1022
1105
|
setError(null);
|
|
1023
1106
|
}
|
|
1024
1107
|
} catch {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"workflow-hints.d.ts","sourceRoot":"","sources":["../src/workflow-hints.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,SAAS,EAAqB,MAAM,aAAa,CAAC;AAEhE,MAAM,WAAW,iBAAiB;IAChC,oEAAoE;IACpE,WAAW,EAAE,SAAS,GAAG,aAAa,GAAG,KAAK,CAAC;IAC/C,4EAA4E;IAC5E,aAAa,EAAE,OAAO,CAAC;IACvB,mEAAmE;IACnE,eAAe,EAAE,OAAO,CAAC;IACzB,wEAAwE;IACxE,qBAAqB,EAAE,OAAO,CAAC;IAC/B,4DAA4D;IAC5D,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC5B;AAID;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,GAAG,IAAI,
|
|
1
|
+
{"version":3,"file":"workflow-hints.d.ts","sourceRoot":"","sources":["../src/workflow-hints.ts"],"names":[],"mappings":"AAUA,OAAO,EAAE,KAAK,SAAS,EAAqB,MAAM,aAAa,CAAC;AAEhE,MAAM,WAAW,iBAAiB;IAChC,oEAAoE;IACpE,WAAW,EAAE,SAAS,GAAG,aAAa,GAAG,KAAK,CAAC;IAC/C,4EAA4E;IAC5E,aAAa,EAAE,OAAO,CAAC;IACvB,mEAAmE;IACnE,eAAe,EAAE,OAAO,CAAC;IACzB,wEAAwE;IACxE,qBAAqB,EAAE,OAAO,CAAC;IAC/B,4DAA4D;IAC5D,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,CAAC;CAC5B;AAID;;;;GAIG;AACH,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,iBAAiB,GAAG,MAAM,GAAG,IAAI,CA+FzE;AAED;;;;;;;GAOG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,GAAG,IAAI,CAQjG"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@cortexkit/aft-opencode",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.35.1",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"description": "OpenCode plugin for Agent File Tools (AFT) — tree-sitter and lsp powered code analysis",
|
|
6
6
|
"main": "dist/index.js",
|
|
@@ -30,19 +30,19 @@
|
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
32
|
"@clack/prompts": "^1.2.0",
|
|
33
|
-
"@cortexkit/aft-bridge": "0.
|
|
33
|
+
"@cortexkit/aft-bridge": "0.35.1",
|
|
34
34
|
"@xterm/headless": "^5.5.0",
|
|
35
35
|
"comment-json": "^4.6.2",
|
|
36
36
|
"undici": "^7.25.0",
|
|
37
37
|
"zod": "^4.1.8"
|
|
38
38
|
},
|
|
39
39
|
"optionalDependencies": {
|
|
40
|
-
"@cortexkit/aft-darwin-arm64": "0.
|
|
41
|
-
"@cortexkit/aft-darwin-x64": "0.
|
|
42
|
-
"@cortexkit/aft-linux-arm64": "0.
|
|
43
|
-
"@cortexkit/aft-linux-x64": "0.
|
|
44
|
-
"@cortexkit/aft-win32-arm64": "0.
|
|
45
|
-
"@cortexkit/aft-win32-x64": "0.
|
|
40
|
+
"@cortexkit/aft-darwin-arm64": "0.35.1",
|
|
41
|
+
"@cortexkit/aft-darwin-x64": "0.35.1",
|
|
42
|
+
"@cortexkit/aft-linux-arm64": "0.35.1",
|
|
43
|
+
"@cortexkit/aft-linux-x64": "0.35.1",
|
|
44
|
+
"@cortexkit/aft-win32-arm64": "0.35.1",
|
|
45
|
+
"@cortexkit/aft-win32-x64": "0.35.1"
|
|
46
46
|
},
|
|
47
47
|
"devDependencies": {
|
|
48
48
|
"@opencode-ai/plugin": "^1.15.11",
|
package/src/shared/bash-hints.ts
CHANGED
|
@@ -1,45 +1,4 @@
|
|
|
1
|
-
//
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
|
|
5
|
-
// (or the original string when no hint should fire) so the hook can compose
|
|
6
|
-
// them without managing state.
|
|
7
|
-
|
|
8
|
-
const CONFLICT_HINT =
|
|
9
|
-
"\n\n[Hint] Use aft_conflicts to see all conflict regions across files in a single call.";
|
|
10
|
-
const GREP_HINT = "\n\n[Hint] Use the grep tool instead of bash for faster indexed search.";
|
|
11
|
-
|
|
12
|
-
/**
|
|
13
|
-
* Append the `aft_conflicts` hint when the output indicates a real git merge
|
|
14
|
-
* or rebase produced conflicts.
|
|
15
|
-
*
|
|
16
|
-
* Gated on BOTH:
|
|
17
|
-
* - the "Automatic merge failed; fix conflicts" marker, AND
|
|
18
|
-
* - a git-conflict signal (`CONFLICT (...)` line or `error: could not apply`)
|
|
19
|
-
*
|
|
20
|
-
* Both conditions are required because `aft_conflicts` calls `git ls-files -u`,
|
|
21
|
-
* which fails with "not a git repository" outside a git working tree. The
|
|
22
|
-
* marker string can legitimately appear in docs, READMEs, test fixtures, and
|
|
23
|
-
* grep output, so we cannot key off it alone — a false-positive hint sends
|
|
24
|
-
* agents into a confusing error.
|
|
25
|
-
*/
|
|
26
|
-
export function maybeAppendConflictsHint(output: string): string {
|
|
27
|
-
if (!output.includes("Automatic merge failed; fix conflicts")) return output;
|
|
28
|
-
// git merge prints "CONFLICT (content|file|...): ..." per file.
|
|
29
|
-
// git rebase / git am print "error: could not apply <sha>" per failed pick.
|
|
30
|
-
if (!/^CONFLICT \(|^error: could not apply /m.test(output)) return output;
|
|
31
|
-
return output + CONFLICT_HINT;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
/**
|
|
35
|
-
* Append the grep-tool hint when the bash command was a grep/rg invocation.
|
|
36
|
-
*
|
|
37
|
-
* Detected by examining the FIRST LINE of output (which is the echoed command
|
|
38
|
-
* line on most shells in foreground mode). We cap the slice at 300 chars so
|
|
39
|
-
* a single huge first line doesn't slow this hook.
|
|
40
|
-
*/
|
|
41
|
-
export function maybeAppendGrepHint(output: string): string {
|
|
42
|
-
const firstLine = output.slice(0, 300).split("\n")[0] ?? "";
|
|
43
|
-
if (!/\b(rg|grep)\s/.test(firstLine)) return output;
|
|
44
|
-
return output + GREP_HINT;
|
|
45
|
-
}
|
|
1
|
+
// Bash-output hint nudges now live in the shared bridge package so both plugin
|
|
2
|
+
// hosts use one implementation. Re-exported here to keep the existing OpenCode
|
|
3
|
+
// import path stable.
|
|
4
|
+
export { maybeAppendConflictsHint, maybeAppendGrepHint } from "@cortexkit/aft-bridge";
|
package/src/tui/index.tsx
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
createAftSidebarSlot,
|
|
18
18
|
formatCompressionSidebarRows,
|
|
19
19
|
resolveTuiStorageDir,
|
|
20
|
+
shouldSuppressUninitializedDowngrade,
|
|
20
21
|
} from "./sidebar";
|
|
21
22
|
|
|
22
23
|
// The TUI talks to the server plugin via AftRpcClient. The client reads the
|
|
@@ -160,7 +161,20 @@ const StatusDialog = (props: StatusDialogProps) => {
|
|
|
160
161
|
);
|
|
161
162
|
if (controller.signal.aborted || requestGeneration !== pollGeneration) return;
|
|
162
163
|
if ((response as Record<string, unknown>).success !== false) {
|
|
163
|
-
|
|
164
|
+
const snapshot = coerceAftStatus(response as Record<string, unknown>);
|
|
165
|
+
// Stale-while-revalidate: don't downgrade a good snapshot to a transient
|
|
166
|
+
// `not_initialized` (bridge mid-respawn / session-dir key miss) — it
|
|
167
|
+
// arrives as success:true and would blank the dialog until the next poll.
|
|
168
|
+
const current = status();
|
|
169
|
+
if (
|
|
170
|
+
shouldSuppressUninitializedDowngrade(
|
|
171
|
+
snapshot.cache_role,
|
|
172
|
+
current !== null && current.cache_role !== "not_initialized",
|
|
173
|
+
)
|
|
174
|
+
) {
|
|
175
|
+
return;
|
|
176
|
+
}
|
|
177
|
+
setStatus(snapshot);
|
|
164
178
|
setError(null);
|
|
165
179
|
}
|
|
166
180
|
} catch {
|
package/src/tui/sidebar.tsx
CHANGED
|
@@ -142,6 +142,43 @@ const SectionHeader = (props: { theme: TuiThemeCurrent; title: string; marginTop
|
|
|
142
142
|
</box>
|
|
143
143
|
);
|
|
144
144
|
|
|
145
|
+
// Map a status tone to a theme color — used for the collapsed-view status dots.
|
|
146
|
+
function toneColor(theme: TuiThemeCurrent, tone: "ok" | "warn" | "err" | "muted"): string {
|
|
147
|
+
switch (tone) {
|
|
148
|
+
case "ok":
|
|
149
|
+
return theme.success ?? theme.accent;
|
|
150
|
+
case "warn":
|
|
151
|
+
return theme.warning;
|
|
152
|
+
case "err":
|
|
153
|
+
return theme.error;
|
|
154
|
+
default:
|
|
155
|
+
return theme.textMuted;
|
|
156
|
+
}
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
// Collapsed-view row: label on the left, a status dot (or compact value) on the
|
|
160
|
+
// right. Mirrors the expanded StatRow layout so the columns line up.
|
|
161
|
+
const CollapsedRow = (props: { theme: TuiThemeCurrent; label: string; children: JSX.Element }) => (
|
|
162
|
+
<box width="100%" flexDirection="row" justifyContent="space-between">
|
|
163
|
+
<text fg={props.theme.textMuted}>{props.label}</text>
|
|
164
|
+
{props.children}
|
|
165
|
+
</box>
|
|
166
|
+
);
|
|
167
|
+
|
|
168
|
+
// Compact "saved / ratio" string for the collapsed Compression row — e.g.
|
|
169
|
+
// "7.6M / 64%". Uses the local `formatCount` (not the aft-bridge token
|
|
170
|
+
// formatter) so the TUI bundle doesn't pull the bridge barrel, which exports
|
|
171
|
+
// URL-fetch helpers unsuitable for Bun's TUI runtime. Returns null when no
|
|
172
|
+
// compression has been recorded yet.
|
|
173
|
+
export function collapsedCompressionValue(
|
|
174
|
+
compression: StatusCompression | undefined,
|
|
175
|
+
): string | null {
|
|
176
|
+
if (!compression || compression.project.events <= 0) return null;
|
|
177
|
+
const { savings_tokens, original_tokens } = compression.project;
|
|
178
|
+
const pct = original_tokens > 0 ? Math.round((savings_tokens / original_tokens) * 100) : 0;
|
|
179
|
+
return `${formatCount(savings_tokens)} / ${pct}%`;
|
|
180
|
+
}
|
|
181
|
+
|
|
145
182
|
// v0.27 moved AFT storage to the CortexKit root. TUI code must use a
|
|
146
183
|
// lightweight local path helper rather than the shared bridge barrel, which
|
|
147
184
|
// also exports URL-fetch helpers unsuitable for Bun's TUI runtime.
|
|
@@ -177,6 +214,22 @@ export function scopedSidebarSnapshot(
|
|
|
177
214
|
return scoped.snapshot;
|
|
178
215
|
}
|
|
179
216
|
|
|
217
|
+
/**
|
|
218
|
+
* Stale-while-revalidate guard. A transient `not_initialized` snapshot (bridge
|
|
219
|
+
* mid-respawn after a binary swap, or a momentary session-dir key miss) arrives
|
|
220
|
+
* over RPC as `success: true`, so a naive `setStatus` would overwrite a good
|
|
221
|
+
* snapshot and collapse the panel to the lazy-bridge placeholder — the blank
|
|
222
|
+
* flicker that recovers on the next poll. Suppress the downgrade only when we
|
|
223
|
+
* already hold initialized data for the same context; never blocks the first
|
|
224
|
+
* real snapshot, and a genuine context switch clears separately.
|
|
225
|
+
*/
|
|
226
|
+
export function shouldSuppressUninitializedDowngrade(
|
|
227
|
+
incomingCacheRole: string | undefined,
|
|
228
|
+
haveInitializedForContext: boolean,
|
|
229
|
+
): boolean {
|
|
230
|
+
return incomingCacheRole === "not_initialized" && haveInitializedForContext;
|
|
231
|
+
}
|
|
232
|
+
|
|
180
233
|
const SidebarContent = (props: {
|
|
181
234
|
api: TuiPluginApi;
|
|
182
235
|
sessionID: () => string;
|
|
@@ -184,6 +237,9 @@ const SidebarContent = (props: {
|
|
|
184
237
|
pluginVersion: string;
|
|
185
238
|
}) => {
|
|
186
239
|
const [status, setStatus] = createSignal<ScopedSidebarStatus | null>(null);
|
|
240
|
+
// Collapsed/expanded toggle — local UI state (not persisted), mirroring
|
|
241
|
+
// OpenCode's native MCP sidebar section. Default expanded.
|
|
242
|
+
const [collapsed, setCollapsed] = createSignal(false);
|
|
187
243
|
let inflight: {
|
|
188
244
|
controller: AbortController;
|
|
189
245
|
generation: number;
|
|
@@ -251,6 +307,16 @@ const SidebarContent = (props: {
|
|
|
251
307
|
if (currentDirectory() !== directory || props.sessionID() !== sid) return;
|
|
252
308
|
if (response && (response as Record<string, unknown>).success !== false) {
|
|
253
309
|
const snapshot = coerceAftStatus(response as Record<string, unknown>);
|
|
310
|
+
// Stale-while-revalidate: keep the last-good snapshot instead of
|
|
311
|
+
// flickering to the lazy-bridge placeholder on a transient
|
|
312
|
+
// not_initialized. See shouldSuppressUninitializedDowngrade.
|
|
313
|
+
const current = status();
|
|
314
|
+
const haveGoodForContext =
|
|
315
|
+
current !== null &&
|
|
316
|
+
current.directory === directory &&
|
|
317
|
+
current.sessionID === sid &&
|
|
318
|
+
current.snapshot.cache_role !== "not_initialized";
|
|
319
|
+
if (shouldSuppressUninitializedDowngrade(snapshot.cache_role, haveGoodForContext)) return;
|
|
254
320
|
setStatus({ directory, sessionID: sid, snapshot });
|
|
255
321
|
requestRender();
|
|
256
322
|
}
|
|
@@ -394,12 +460,27 @@ const SidebarContent = (props: {
|
|
|
394
460
|
paddingLeft={1}
|
|
395
461
|
paddingRight={1}
|
|
396
462
|
>
|
|
397
|
-
{/* Header: AFT badge + binary version + degraded badge
|
|
398
|
-
|
|
463
|
+
{/* Header: triangle toggle + AFT badge + binary version + degraded badge.
|
|
464
|
+
Clicking the header row collapses/expands the panel (mirrors OpenCode's
|
|
465
|
+
native MCP sidebar section). Only interactive once initialized — the
|
|
466
|
+
lazy-bridge placeholder has nothing to collapse. */}
|
|
467
|
+
<box
|
|
468
|
+
flexDirection="row"
|
|
469
|
+
justifyContent="space-between"
|
|
470
|
+
alignItems="center"
|
|
471
|
+
onMouseDown={() => {
|
|
472
|
+
if (!notInitialized()) setCollapsed((x) => !x);
|
|
473
|
+
}}
|
|
474
|
+
>
|
|
399
475
|
<box flexDirection="row" alignItems="center">
|
|
476
|
+
{/* Triangle lives inside the accent badge so the toggle reads as one
|
|
477
|
+
unit: "▶ AFT" / "▼ AFT". Hidden pre-init (nothing to collapse). */}
|
|
400
478
|
<box paddingLeft={1} paddingRight={1} backgroundColor={props.theme.accent}>
|
|
401
479
|
<text fg={props.theme.background}>
|
|
402
|
-
<b>
|
|
480
|
+
<b>
|
|
481
|
+
{notInitialized() ? "" : collapsed() ? "▶ " : "▼ "}
|
|
482
|
+
AFT
|
|
483
|
+
</b>
|
|
403
484
|
</text>
|
|
404
485
|
</box>
|
|
405
486
|
{s()?.degraded && (
|
|
@@ -445,8 +526,29 @@ const SidebarContent = (props: {
|
|
|
445
526
|
</box>
|
|
446
527
|
)}
|
|
447
528
|
|
|
529
|
+
{/* Collapsed view — condensed status dots + compact compression. Shown
|
|
530
|
+
only when initialized AND collapsed. Three rows mirroring the section
|
|
531
|
+
order of the expanded grid. */}
|
|
532
|
+
{!notInitialized() && collapsed() && (
|
|
533
|
+
<box width="100%" flexDirection="column">
|
|
534
|
+
<CollapsedRow theme={props.theme} label="Search Index">
|
|
535
|
+
<text fg={toneColor(props.theme, searchStatus().tone)}>●</text>
|
|
536
|
+
</CollapsedRow>
|
|
537
|
+
<CollapsedRow theme={props.theme} label="Semantic Index">
|
|
538
|
+
<text fg={toneColor(props.theme, semanticStatus().tone)}>●</text>
|
|
539
|
+
</CollapsedRow>
|
|
540
|
+
{collapsedCompressionValue(s()?.compression) && (
|
|
541
|
+
<CollapsedRow theme={props.theme} label="Compression">
|
|
542
|
+
<text fg={props.theme.textMuted}>
|
|
543
|
+
<b>{collapsedCompressionValue(s()?.compression)}</b>
|
|
544
|
+
</text>
|
|
545
|
+
</CollapsedRow>
|
|
546
|
+
)}
|
|
547
|
+
</box>
|
|
548
|
+
)}
|
|
549
|
+
|
|
448
550
|
{/* Search index */}
|
|
449
|
-
{!notInitialized() && (
|
|
551
|
+
{!notInitialized() && !collapsed() && (
|
|
450
552
|
<>
|
|
451
553
|
<SectionHeader theme={props.theme} title="Search Index" />
|
|
452
554
|
<StatRow
|