@cortexkit/aft-opencode 0.12.0 → 0.12.2

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.
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA8BxB,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwC1B,CAAC;AAEH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AA8HxD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAwBjE"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AA8BxB,eAAO,MAAM,eAAe;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAwC1B,CAAC;AAEH,MAAM,MAAM,SAAS,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,eAAe,CAAC,CAAC;AAmIxD;;;;;;;;;GASG;AACH,wBAAgB,aAAa,CAAC,gBAAgB,EAAE,MAAM,GAAG,SAAS,CAwBjE"}
package/dist/index.js CHANGED
@@ -7805,12 +7805,13 @@ var require_src2 = __commonJS((exports, module) => {
7805
7805
  // src/index.ts
7806
7806
  import { existsSync as existsSync8, mkdirSync as mkdirSync8, readFileSync as readFileSync5, writeFileSync as writeFileSync5 } from "fs";
7807
7807
  import { createRequire as createRequire2 } from "module";
7808
- import { homedir as homedir6 } from "os";
7808
+ import { homedir as homedir7 } from "os";
7809
7809
  import { join as join13 } from "path";
7810
7810
 
7811
7811
  // src/config.ts
7812
7812
  var import_comment_json = __toESM(require_src2(), 1);
7813
7813
  import { existsSync, readFileSync } from "fs";
7814
+ import { homedir } from "os";
7814
7815
  import { join as join2 } from "path";
7815
7816
 
7816
7817
  // ../../node_modules/.bun/zod@4.3.6/node_modules/zod/v4/classic/external.js
@@ -21514,7 +21515,11 @@ function mergeConfigs(base, override) {
21514
21515
  };
21515
21516
  }
21516
21517
  function getOpenCodeConfigDir() {
21517
- const xdgConfig = process.env.XDG_CONFIG_HOME || join2(process.env.HOME || "~", ".config");
21518
+ const envDir = process.env.OPENCODE_CONFIG_DIR?.trim();
21519
+ if (envDir) {
21520
+ return envDir;
21521
+ }
21522
+ const xdgConfig = process.env.XDG_CONFIG_HOME || join2(homedir(), ".config");
21518
21523
  return join2(xdgConfig, "opencode");
21519
21524
  }
21520
21525
  function loadAftConfig(projectDirectory) {
@@ -21535,7 +21540,7 @@ function loadAftConfig(projectDirectory) {
21535
21540
 
21536
21541
  // src/downloader.ts
21537
21542
  import { chmodSync, existsSync as existsSync2, mkdirSync, unlinkSync } from "fs";
21538
- import { homedir } from "os";
21543
+ import { homedir as homedir2 } from "os";
21539
21544
  import { join as join3 } from "path";
21540
21545
 
21541
21546
  // src/platform.ts
@@ -21557,10 +21562,10 @@ var REPO = "cortexkit/aft";
21557
21562
  function getCacheDir() {
21558
21563
  if (process.platform === "win32") {
21559
21564
  const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
21560
- const base2 = localAppData || join3(homedir(), "AppData", "Local");
21565
+ const base2 = localAppData || join3(homedir2(), "AppData", "Local");
21561
21566
  return join3(base2, "aft", "bin");
21562
21567
  }
21563
- const base = process.env.XDG_CACHE_HOME || join3(homedir(), ".cache");
21568
+ const base = process.env.XDG_CACHE_HOME || join3(homedir2(), ".cache");
21564
21569
  return join3(base, "aft", "bin");
21565
21570
  }
21566
21571
  function getBinaryName() {
@@ -21755,7 +21760,7 @@ function normalizeToolMap(tools) {
21755
21760
 
21756
21761
  // src/notifications.ts
21757
21762
  import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync } from "fs";
21758
- import { homedir as homedir2, platform } from "os";
21763
+ import { homedir as homedir3, platform } from "os";
21759
21764
  import { join as join4 } from "path";
21760
21765
  function isTuiMode() {
21761
21766
  return process.env.OPENCODE_CLIENT === "cli";
@@ -21777,7 +21782,7 @@ var WARNING_MARKER = `${AFT_MARKER} \u26A0\uFE0F`;
21777
21782
  var STATUS_MARKER = `${AFT_MARKER} \u2705`;
21778
21783
  function getDesktopStatePath() {
21779
21784
  const os2 = platform();
21780
- const home = homedir2();
21785
+ const home = homedir3();
21781
21786
  if (os2 === "darwin") {
21782
21787
  return join4(home, "Library", "Application Support", "ai.opencode.desktop", "opencode.global.dat");
21783
21788
  }
@@ -22059,8 +22064,7 @@ async function downloadOnnxRuntime(info, targetDir) {
22059
22064
  const { execSync } = await import("child_process");
22060
22065
  execSync(`tar xzf "${archivePath}" -C "${tmpDir}"`, { stdio: "pipe" });
22061
22066
  } else {
22062
- const { execSync } = await import("child_process");
22063
- execSync(`unzip -q "${archivePath}" -d "${tmpDir}"`, { stdio: "pipe" });
22067
+ await extractZipArchive(archivePath, tmpDir);
22064
22068
  }
22065
22069
  const extractedDir = join5(tmpDir, info.assetName, "lib");
22066
22070
  if (!existsSync4(extractedDir)) {
@@ -22118,10 +22122,42 @@ async function downloadOnnxRuntime(info, targetDir) {
22118
22122
  return null;
22119
22123
  }
22120
22124
  }
22125
+ async function extractZipArchive(archivePath, destinationDir) {
22126
+ const { execFileSync } = await import("child_process");
22127
+ if (process.platform === "win32") {
22128
+ let powershellError;
22129
+ try {
22130
+ execFileSync("powershell.exe", [
22131
+ "-NoProfile",
22132
+ "-NonInteractive",
22133
+ "-ExecutionPolicy",
22134
+ "Bypass",
22135
+ "-Command",
22136
+ "& { Expand-Archive -LiteralPath $args[0] -DestinationPath $args[1] -Force }",
22137
+ archivePath,
22138
+ destinationDir
22139
+ ], { stdio: "pipe", timeout: 120000 });
22140
+ return;
22141
+ } catch (err) {
22142
+ powershellError = err;
22143
+ warn(`PowerShell Expand-Archive failed, falling back to cmd/tar: ${String(err)}`);
22144
+ }
22145
+ try {
22146
+ execFileSync("cmd.exe", ["/d", "/s", "/c", `tar -xf "${archivePath}" -C "${destinationDir}"`], { stdio: "pipe", timeout: 120000 });
22147
+ return;
22148
+ } catch (cmdError) {
22149
+ throw new Error(`ZIP extraction failed via PowerShell and cmd/tar. PowerShell: ${String(powershellError)} | cmd/tar: ${String(cmdError)}`);
22150
+ }
22151
+ }
22152
+ execFileSync("unzip", ["-q", archivePath, "-d", destinationDir], {
22153
+ stdio: "pipe",
22154
+ timeout: 120000
22155
+ });
22156
+ }
22121
22157
 
22122
22158
  // src/bridge.ts
22123
22159
  import { spawn } from "child_process";
22124
- import { homedir as homedir3 } from "os";
22160
+ import { homedir as homedir4 } from "os";
22125
22161
  import { join as join6 } from "path";
22126
22162
  function compareSemver(a, b) {
22127
22163
  const pa = a.split(".").map(Number);
@@ -22279,7 +22315,7 @@ class BinaryBridge {
22279
22315
  stdio: ["pipe", "pipe", "pipe"],
22280
22316
  env: {
22281
22317
  ...process.env,
22282
- FASTEMBED_CACHE_DIR: process.env.FASTEMBED_CACHE_DIR || (typeof this.configOverrides.storage_dir === "string" ? join6(this.configOverrides.storage_dir, "semantic", "models") : join6(homedir3() || "", ".cache", "fastembed")),
22318
+ FASTEMBED_CACHE_DIR: process.env.FASTEMBED_CACHE_DIR || (typeof this.configOverrides.storage_dir === "string" ? join6(this.configOverrides.storage_dir, "semantic", "models") : join6(homedir4() || "", ".cache", "fastembed")),
22283
22319
  ...typeof this.configOverrides._ort_dylib_dir === "string" && {
22284
22320
  ORT_DYLIB_PATH: join6(this.configOverrides._ort_dylib_dir, process.platform === "win32" ? "onnxruntime.dll" : process.platform === "darwin" ? "libonnxruntime.dylib" : "libonnxruntime.so")
22285
22321
  }
@@ -22493,7 +22529,7 @@ class BridgePool {
22493
22529
  import { execSync, spawnSync } from "child_process";
22494
22530
  import { chmodSync as chmodSync3, copyFileSync, existsSync as existsSync5, mkdirSync as mkdirSync4 } from "fs";
22495
22531
  import { createRequire } from "module";
22496
- import { homedir as homedir4 } from "os";
22532
+ import { homedir as homedir5 } from "os";
22497
22533
  import { join as join7 } from "path";
22498
22534
  function copyToVersionedCache(npmBinaryPath) {
22499
22535
  try {
@@ -22573,7 +22609,7 @@ function findBinarySync() {
22573
22609
  if (result)
22574
22610
  return result;
22575
22611
  } catch {}
22576
- const cargoPath = join7(homedir4(), ".cargo", "bin", `aft${ext}`);
22612
+ const cargoPath = join7(homedir5(), ".cargo", "bin", `aft${ext}`);
22577
22613
  if (existsSync5(cargoPath))
22578
22614
  return cargoPath;
22579
22615
  return null;
@@ -22856,7 +22892,7 @@ import { existsSync as existsSync6, mkdirSync as mkdirSync6, readFileSync as rea
22856
22892
  import { dirname as dirname2, join as join10 } from "path";
22857
22893
 
22858
22894
  // src/shared/opencode-config-dir.ts
22859
- import { homedir as homedir5 } from "os";
22895
+ import { homedir as homedir6 } from "os";
22860
22896
  import { join as join9, resolve } from "path";
22861
22897
  function getCliConfigDir() {
22862
22898
  const envConfigDir = process.env.OPENCODE_CONFIG_DIR?.trim();
@@ -22864,9 +22900,9 @@ function getCliConfigDir() {
22864
22900
  return resolve(envConfigDir);
22865
22901
  }
22866
22902
  if (process.platform === "win32") {
22867
- return join9(homedir5(), ".config", "opencode");
22903
+ return join9(homedir6(), ".config", "opencode");
22868
22904
  }
22869
- return join9(process.env.XDG_CONFIG_HOME || join9(homedir5(), ".config"), "opencode");
22905
+ return join9(process.env.XDG_CONFIG_HOME || join9(homedir6(), ".config"), "opencode");
22870
22906
  }
22871
22907
  function getOpenCodeConfigDir2(_options) {
22872
22908
  return getCliConfigDir();
@@ -25234,7 +25270,7 @@ var plugin = async (input) => {
25234
25270
  configOverrides.experimental_search_index = aftConfig.experimental_search_index;
25235
25271
  if (aftConfig.experimental_semantic_search !== undefined)
25236
25272
  configOverrides.experimental_semantic_search = aftConfig.experimental_semantic_search;
25237
- const dataHome = process.env.XDG_DATA_HOME || join13(homedir6(), ".local", "share");
25273
+ const dataHome = process.env.XDG_DATA_HOME || join13(homedir7(), ".local", "share");
25238
25274
  configOverrides.storage_dir = join13(dataHome, "opencode", "storage", "plugin", "aft");
25239
25275
  if (aftConfig.experimental_semantic_search) {
25240
25276
  const storageDir2 = configOverrides.storage_dir;
@@ -1 +1 @@
1
- {"version":3,"file":"onnx-runtime.d.ts","sourceRoot":"","sources":["../src/onnx-runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AA0DH,4DAA4D;AAC5D,wBAAgB,0BAA0B,IAAI,OAAO,CAEpD;AAED,6EAA6E;AAC7E,wBAAgB,oBAAoB,IAAI,MAAM,CAQ7C;AAED;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA4BlF;AAgJD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAU3D"}
1
+ {"version":3,"file":"onnx-runtime.d.ts","sourceRoot":"","sources":["../src/onnx-runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;GAeG;AA0DH,4DAA4D;AAC5D,wBAAgB,0BAA0B,IAAI,OAAO,CAEpD;AAED,6EAA6E;AAC7E,wBAAgB,oBAAoB,IAAI,MAAM,CAQ7C;AAED;;;;;;;;GAQG;AACH,wBAAsB,iBAAiB,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CA4BlF;AA8LD;;GAEG;AACH,wBAAgB,kBAAkB,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,CAU3D"}
package/dist/tui.js ADDED
@@ -0,0 +1,386 @@
1
+ // @bun
2
+ // src/shared/rpc-client.ts
3
+ import { readFileSync } from "fs";
4
+
5
+ // src/shared/rpc-utils.ts
6
+ import { createHash } from "crypto";
7
+ import { join } from "path";
8
+ function projectHash(directory) {
9
+ const normalized = directory.replace(/\/+$/, "");
10
+ return createHash("sha256").update(normalized).digest("hex").slice(0, 16);
11
+ }
12
+ function rpcPortFilePath(storageDir, directory) {
13
+ const hash = projectHash(directory);
14
+ return join(storageDir, "rpc", hash, "port");
15
+ }
16
+
17
+ // src/shared/rpc-client.ts
18
+ var MAX_RETRIES = 10;
19
+ var RETRY_DELAY_MS = 500;
20
+ var REQUEST_TIMEOUT_MS = 5000;
21
+
22
+ class AftRpcClient {
23
+ port = null;
24
+ portFilePath;
25
+ healthChecked = false;
26
+ constructor(storageDir, directory) {
27
+ this.portFilePath = rpcPortFilePath(storageDir, directory);
28
+ }
29
+ async call(method, params = {}) {
30
+ const port = await this.resolvePort();
31
+ if (!port) {
32
+ throw new Error("AFT RPC server not available");
33
+ }
34
+ const response = await this.fetchWithTimeout(`http://127.0.0.1:${port}/rpc/${method}`, {
35
+ method: "POST",
36
+ headers: { "Content-Type": "application/json" },
37
+ body: JSON.stringify(params)
38
+ });
39
+ if (!response.ok) {
40
+ const text = await response.text();
41
+ throw new Error(`RPC ${method} failed (${response.status}): ${text}`);
42
+ }
43
+ return await response.json();
44
+ }
45
+ async isAvailable() {
46
+ try {
47
+ const port = await this.resolvePort();
48
+ return port !== null;
49
+ } catch {
50
+ return false;
51
+ }
52
+ }
53
+ async resolvePort() {
54
+ if (this.port && this.healthChecked) {
55
+ return this.port;
56
+ }
57
+ if (this.port) {
58
+ const alive = await this.healthCheck(this.port);
59
+ if (alive) {
60
+ this.healthChecked = true;
61
+ return this.port;
62
+ }
63
+ this.port = null;
64
+ this.healthChecked = false;
65
+ }
66
+ for (let attempt = 0;attempt < MAX_RETRIES; attempt++) {
67
+ const port = this.readPortFile();
68
+ if (port) {
69
+ const alive = await this.healthCheck(port);
70
+ if (alive) {
71
+ this.port = port;
72
+ this.healthChecked = true;
73
+ return port;
74
+ }
75
+ }
76
+ if (attempt < MAX_RETRIES - 1) {
77
+ await new Promise((r) => setTimeout(r, RETRY_DELAY_MS));
78
+ }
79
+ }
80
+ return null;
81
+ }
82
+ readPortFile() {
83
+ try {
84
+ const content = readFileSync(this.portFilePath, "utf-8").trim();
85
+ const port = Number.parseInt(content, 10);
86
+ if (Number.isNaN(port) || port <= 0 || port > 65535) {
87
+ return null;
88
+ }
89
+ return port;
90
+ } catch {
91
+ return null;
92
+ }
93
+ }
94
+ async healthCheck(port) {
95
+ try {
96
+ const response = await this.fetchWithTimeout(`http://127.0.0.1:${port}/health`, {
97
+ method: "GET"
98
+ });
99
+ return response.ok;
100
+ } catch {
101
+ return false;
102
+ }
103
+ }
104
+ async fetchWithTimeout(url, options) {
105
+ const controller = new AbortController;
106
+ const timeout = setTimeout(() => controller.abort(), REQUEST_TIMEOUT_MS);
107
+ try {
108
+ return await fetch(url, { ...options, signal: controller.signal });
109
+ } finally {
110
+ clearTimeout(timeout);
111
+ }
112
+ }
113
+ reset() {
114
+ this.port = null;
115
+ this.healthChecked = false;
116
+ }
117
+ }
118
+
119
+ // src/shared/status.ts
120
+ function asRecord(value) {
121
+ return typeof value === "object" && value !== null ? value : {};
122
+ }
123
+ function readString(value, fallback = "") {
124
+ return typeof value === "string" ? value : fallback;
125
+ }
126
+ function readNullableString(value) {
127
+ return typeof value === "string" ? value : null;
128
+ }
129
+ function readBoolean(value, fallback = false) {
130
+ return typeof value === "boolean" ? value : fallback;
131
+ }
132
+ function readNumber(value, fallback = 0) {
133
+ return typeof value === "number" && Number.isFinite(value) ? value : fallback;
134
+ }
135
+ function readOptionalNumber(value) {
136
+ return typeof value === "number" && Number.isFinite(value) ? value : null;
137
+ }
138
+ function formatFlag(enabled) {
139
+ return enabled ? "enabled" : "disabled";
140
+ }
141
+ function formatCount(value) {
142
+ return value == null ? "\u2014" : value.toLocaleString("en-US");
143
+ }
144
+ function formatBytes(bytes) {
145
+ if (!Number.isFinite(bytes) || bytes <= 0)
146
+ return "0 B";
147
+ const units = ["B", "KB", "MB", "GB", "TB"];
148
+ let value = bytes;
149
+ let unitIndex = 0;
150
+ while (value >= 1024 && unitIndex < units.length - 1) {
151
+ value /= 1024;
152
+ unitIndex++;
153
+ }
154
+ const decimals = value >= 10 || unitIndex === 0 ? 0 : 1;
155
+ return `${value.toFixed(decimals)} ${units[unitIndex]}`;
156
+ }
157
+ function coerceAftStatus(response) {
158
+ const features = asRecord(response.features);
159
+ const searchIndex = asRecord(response.search_index);
160
+ const semanticIndex = asRecord(response.semantic_index);
161
+ const disk = asRecord(response.disk);
162
+ const symbolCache = asRecord(response.symbol_cache);
163
+ return {
164
+ version: readString(response.version, "unknown"),
165
+ project_root: readNullableString(response.project_root),
166
+ features: {
167
+ format_on_edit: readBoolean(features.format_on_edit),
168
+ validate_on_edit: readString(features.validate_on_edit, "off"),
169
+ restrict_to_project_root: readBoolean(features.restrict_to_project_root),
170
+ experimental_search_index: readBoolean(features.experimental_search_index),
171
+ experimental_semantic_search: readBoolean(features.experimental_semantic_search)
172
+ },
173
+ search_index: {
174
+ status: readString(searchIndex.status, "unknown"),
175
+ files: readOptionalNumber(searchIndex.files),
176
+ trigrams: readOptionalNumber(searchIndex.trigrams)
177
+ },
178
+ semantic_index: {
179
+ status: readString(semanticIndex.status, "unknown"),
180
+ entries: readOptionalNumber(semanticIndex.entries),
181
+ dimension: readOptionalNumber(semanticIndex.dimension)
182
+ },
183
+ disk: {
184
+ storage_dir: readNullableString(disk.storage_dir),
185
+ trigram_disk_bytes: readNumber(disk.trigram_disk_bytes),
186
+ semantic_disk_bytes: readNumber(disk.semantic_disk_bytes)
187
+ },
188
+ lsp_servers: readNumber(response.lsp_servers),
189
+ symbol_cache: {
190
+ local_entries: readNumber(symbolCache.local_entries),
191
+ warm_entries: readNumber(symbolCache.warm_entries)
192
+ },
193
+ storage_dir: readNullableString(response.storage_dir)
194
+ };
195
+ }
196
+ function formatStatusDialogMessage(status) {
197
+ const lines = [
198
+ `AFT version: ${status.version}`,
199
+ `Project root: ${status.project_root ?? "(not configured)"}`,
200
+ "",
201
+ "Enabled features",
202
+ `- format_on_edit: ${formatFlag(status.features.format_on_edit)}`,
203
+ `- experimental_search_index: ${formatFlag(status.features.experimental_search_index)}`,
204
+ `- experimental_semantic_search: ${formatFlag(status.features.experimental_semantic_search)}`,
205
+ "",
206
+ "Search index",
207
+ `- status: ${status.search_index.status}`,
208
+ `- files: ${formatCount(status.search_index.files)}`,
209
+ `- trigrams: ${formatCount(status.search_index.trigrams)}`,
210
+ "",
211
+ "Semantic index",
212
+ `- status: ${status.semantic_index.status}`,
213
+ `- entries: ${formatCount(status.semantic_index.entries)}`
214
+ ];
215
+ if (status.semantic_index.dimension != null) {
216
+ lines.push(`- dimension: ${formatCount(status.semantic_index.dimension)}`);
217
+ }
218
+ lines.push("", "Disk usage", `- trigram index: ${formatBytes(status.disk.trigram_disk_bytes)}`, `- semantic index: ${formatBytes(status.disk.semantic_disk_bytes)}`, "", "Runtime", `- LSP servers: ${formatCount(status.lsp_servers)}`, `- symbol cache: ${formatCount(status.symbol_cache.local_entries)} local / ${formatCount(status.symbol_cache.warm_entries)} warm`);
219
+ if (status.storage_dir ?? status.disk.storage_dir) {
220
+ lines.push(`- storage dir: ${status.storage_dir ?? status.disk.storage_dir}`);
221
+ }
222
+ return lines.join(`
223
+ `);
224
+ }
225
+
226
+ // src/tui/index.tsx
227
+ import { jsxDEV } from "@opentui/solid/jsx-dev-runtime";
228
+ var STATUS_COMMAND = "aft-status";
229
+ var rpcClients = new Map;
230
+ function getRpcClient(directory) {
231
+ let client = rpcClients.get(directory);
232
+ if (client)
233
+ return client;
234
+ const home = process.env.HOME || process.env.USERPROFILE || "";
235
+ const dataHome = process.env.XDG_DATA_HOME || `${home}/.local/share`;
236
+ const storageDir = `${dataHome}/opencode/storage/plugin/aft`;
237
+ client = new AftRpcClient(storageDir, directory);
238
+ rpcClients.set(directory, client);
239
+ return client;
240
+ }
241
+ function getSessionId(api) {
242
+ try {
243
+ const route = api.route.current;
244
+ if (route?.name === "session" && route.params?.sessionID) {
245
+ return route.params.sessionID;
246
+ }
247
+ } catch {}
248
+ return null;
249
+ }
250
+ async function showStatusDialog(api) {
251
+ const sessionID = getSessionId(api);
252
+ if (!sessionID) {
253
+ api.ui.toast({ message: "No active session", variant: "warning", duration: 5000 });
254
+ return;
255
+ }
256
+ const directory = api.state.path.directory ?? "";
257
+ if (!directory) {
258
+ api.ui.toast({ message: "No project directory", variant: "warning", duration: 5000 });
259
+ return;
260
+ }
261
+ const client = getRpcClient(directory);
262
+ let currentMessage = "Connecting to AFT...";
263
+ try {
264
+ const response = await client.call("status", { sessionID });
265
+ if (response.success !== false) {
266
+ const status = coerceAftStatus(response);
267
+ currentMessage = formatStatusDialogMessage(status);
268
+ }
269
+ } catch {
270
+ currentMessage = "AFT is starting up. Status will refresh automatically...";
271
+ }
272
+ let dialogOpen = true;
273
+ let pollTimer = null;
274
+ api.ui.dialog.setSize("large");
275
+ api.ui.dialog.replace(() => {
276
+ if (!pollTimer) {
277
+ pollTimer = setInterval(async () => {
278
+ if (!dialogOpen) {
279
+ if (pollTimer)
280
+ clearInterval(pollTimer);
281
+ return;
282
+ }
283
+ try {
284
+ const response = await client.call("status", { sessionID });
285
+ if (response.success !== false) {
286
+ const status = coerceAftStatus(response);
287
+ const newMessage = formatStatusDialogMessage(status);
288
+ if (newMessage !== currentMessage) {
289
+ currentMessage = newMessage;
290
+ api.ui.dialog.replace(() => /* @__PURE__ */ jsxDEV(api.ui.DialogAlert, {
291
+ title: "AFT Status",
292
+ message: currentMessage,
293
+ onConfirm: () => {
294
+ dialogOpen = false;
295
+ if (pollTimer)
296
+ clearInterval(pollTimer);
297
+ api.ui.dialog.setSize("medium");
298
+ }
299
+ }, undefined, false, undefined, this), () => {
300
+ dialogOpen = false;
301
+ if (pollTimer)
302
+ clearInterval(pollTimer);
303
+ api.ui.dialog.setSize("medium");
304
+ });
305
+ }
306
+ }
307
+ } catch {}
308
+ }, 1500);
309
+ }
310
+ return /* @__PURE__ */ jsxDEV(api.ui.DialogAlert, {
311
+ title: "AFT Status",
312
+ message: currentMessage,
313
+ onConfirm: () => {
314
+ dialogOpen = false;
315
+ if (pollTimer)
316
+ clearInterval(pollTimer);
317
+ api.ui.dialog.setSize("medium");
318
+ }
319
+ }, undefined, false, undefined, this);
320
+ }, () => {
321
+ dialogOpen = false;
322
+ if (pollTimer)
323
+ clearInterval(pollTimer);
324
+ api.ui.dialog.setSize("medium");
325
+ });
326
+ }
327
+ async function showStartupNotifications(api) {
328
+ const directory = api.state.path.directory ?? "";
329
+ if (!directory)
330
+ return;
331
+ const client = getRpcClient(directory);
332
+ try {
333
+ const announcement = await client.call("get-announcement", {});
334
+ if (announcement.show && announcement.version && announcement.features?.length) {
335
+ const featureText = announcement.features.map((f) => ` \u2022 ${f}`).join(`
336
+ `);
337
+ api.ui.dialog.replace(() => /* @__PURE__ */ jsxDEV(api.ui.DialogAlert, {
338
+ title: `AFT v${announcement.version}`,
339
+ message: `What's new:
340
+
341
+ ${featureText}`,
342
+ onConfirm: () => {
343
+ client.call("mark-announced", {});
344
+ }
345
+ }, undefined, false, undefined, this), () => {
346
+ client.call("mark-announced", {});
347
+ });
348
+ return;
349
+ }
350
+ } catch {}
351
+ try {
352
+ const result = await client.call("get-warnings", {});
353
+ if (result.warnings?.length) {
354
+ const warningText = result.warnings.join(`
355
+
356
+ `);
357
+ api.ui.dialog.replace(() => /* @__PURE__ */ jsxDEV(api.ui.DialogAlert, {
358
+ title: "AFT Warning",
359
+ message: warningText,
360
+ onConfirm: () => {}
361
+ }, undefined, false, undefined, this), () => {});
362
+ }
363
+ } catch {}
364
+ }
365
+ var tui = async (api) => {
366
+ api.command.register(() => [
367
+ {
368
+ title: "AFT: Status",
369
+ value: "aft.status",
370
+ category: "AFT",
371
+ slash: { name: STATUS_COMMAND },
372
+ onSelect() {
373
+ showStatusDialog(api);
374
+ }
375
+ }
376
+ ]);
377
+ showStartupNotifications(api);
378
+ };
379
+ var id = "aft-opencode";
380
+ var tui_default = {
381
+ id,
382
+ tui
383
+ };
384
+ export {
385
+ tui_default as default
386
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cortexkit/aft-opencode",
3
- "version": "0.12.0",
3
+ "version": "0.12.2",
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",
@@ -20,7 +20,7 @@
20
20
  "README.md"
21
21
  ],
22
22
  "scripts": {
23
- "build": "bun build src/index.ts --outdir dist --target bun --format esm --external @opencode-ai/plugin && bun build src/cli/index.ts --outfile dist/cli.js --target node --format esm && tsc --emitDeclarationOnly",
23
+ "build": "bun build src/index.ts --outdir dist --target bun --format esm --external @opencode-ai/plugin && bun build src/tui/index.tsx --outfile dist/tui.js --target bun --format esm --external @opencode-ai/plugin --external @opencode-ai/plugin/tui --external @opentui/solid --external solid-js && bun build src/cli/index.ts --outfile dist/cli.js --target node --format esm && tsc --emitDeclarationOnly",
24
24
  "typecheck": "tsc --noEmit",
25
25
  "pretest": "cd ../.. && cargo build",
26
26
  "test": "bun test",
@@ -35,11 +35,11 @@
35
35
  "zod": "^4.1.8"
36
36
  },
37
37
  "optionalDependencies": {
38
- "@cortexkit/aft-darwin-arm64": "0.12.0",
39
- "@cortexkit/aft-darwin-x64": "0.12.0",
40
- "@cortexkit/aft-linux-arm64": "0.12.0",
41
- "@cortexkit/aft-linux-x64": "0.12.0",
42
- "@cortexkit/aft-win32-x64": "0.12.0"
38
+ "@cortexkit/aft-darwin-arm64": "0.12.2",
39
+ "@cortexkit/aft-darwin-x64": "0.12.2",
40
+ "@cortexkit/aft-linux-arm64": "0.12.2",
41
+ "@cortexkit/aft-linux-x64": "0.12.2",
42
+ "@cortexkit/aft-win32-x64": "0.12.2"
43
43
  },
44
44
  "devDependencies": {
45
45
  "@types/node": "^22.0.0",
@@ -1,5 +1,5 @@
1
1
  import { readFileSync } from "node:fs";
2
- import { rpcPortFilePath } from "./rpc-utils.js";
2
+ import { rpcPortFilePath } from "./rpc-utils";
3
3
 
4
4
  const MAX_RETRIES = 10;
5
5
  const RETRY_DELAY_MS = 500;
@@ -1,8 +1,8 @@
1
1
  import { mkdirSync, renameSync, unlinkSync, writeFileSync } from "node:fs";
2
2
  import { createServer, type IncomingMessage, type Server, type ServerResponse } from "node:http";
3
3
  import { dirname } from "node:path";
4
- import { log, warn } from "../logger.js";
5
- import { rpcPortFilePath } from "./rpc-utils.js";
4
+ import { log, warn } from "../logger";
5
+ import { rpcPortFilePath } from "./rpc-utils";
6
6
 
7
7
  type RpcHandler = (params: Record<string, unknown>) => Promise<Record<string, unknown>>;
8
8
 
@@ -1,8 +1,8 @@
1
1
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
2
2
  import { dirname, join } from "node:path";
3
3
  import { parse, stringify } from "comment-json";
4
- import { log } from "../logger.js";
5
- import { getOpenCodeConfigPaths } from "./opencode-config-dir.js";
4
+ import { log } from "../logger";
5
+ import { getOpenCodeConfigPaths } from "./opencode-config-dir";
6
6
 
7
7
  const PLUGIN_NAME = "@cortexkit/aft-opencode";
8
8
  const PLUGIN_ENTRY = `${PLUGIN_NAME}@latest`;
@@ -8,7 +8,7 @@ import {
8
8
  writeFileSync,
9
9
  } from "node:fs";
10
10
  import { join } from "node:path";
11
- import { log, warn } from "../logger.js";
11
+ import { log, warn } from "../logger";
12
12
 
13
13
  /** Max response body size (10 MB) */
14
14
  const MAX_RESPONSE_BYTES = 10 * 1024 * 1024;