@cortexkit/aft 0.34.0 → 0.35.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/dist/index.js CHANGED
@@ -46,11 +46,235 @@ var __export = (target, all) => {
46
46
  var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
47
47
  var __promiseAll = (args) => Promise.all(args);
48
48
 
49
- // src/lib/fs-util.ts
50
- import { existsSync, readdirSync, statSync } from "node:fs";
49
+ // src/lib/paths.ts
50
+ import { homedir, tmpdir } from "node:os";
51
51
  import { join } from "node:path";
52
+ function getAftBinaryCacheDir() {
53
+ if (process.env.AFT_CACHE_DIR) {
54
+ return join(process.env.AFT_CACHE_DIR, "bin");
55
+ }
56
+ if (process.platform === "win32") {
57
+ const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
58
+ const base2 = localAppData || join(homedir(), "AppData", "Local");
59
+ return join(base2, "aft", "bin");
60
+ }
61
+ const base = process.env.XDG_CACHE_HOME || join(homedir(), ".cache");
62
+ return join(base, "aft", "bin");
63
+ }
64
+ function getAftBinaryName() {
65
+ return process.platform === "win32" ? "aft.exe" : "aft";
66
+ }
67
+ function getAftLspPackagesDir() {
68
+ if (process.env.AFT_CACHE_DIR) {
69
+ return join(process.env.AFT_CACHE_DIR, "lsp-packages");
70
+ }
71
+ if (process.platform === "win32") {
72
+ const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
73
+ const base2 = localAppData || join(homedir(), "AppData", "Local");
74
+ return join(base2, "aft", "lsp-packages");
75
+ }
76
+ const base = process.env.XDG_CACHE_HOME || join(homedir(), ".cache");
77
+ return join(base, "aft", "lsp-packages");
78
+ }
79
+ function getAftLspBinariesDir() {
80
+ if (process.env.AFT_CACHE_DIR) {
81
+ return join(process.env.AFT_CACHE_DIR, "lsp-binaries");
82
+ }
83
+ if (process.platform === "win32") {
84
+ const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
85
+ const base2 = localAppData || join(homedir(), "AppData", "Local");
86
+ return join(base2, "aft", "lsp-binaries");
87
+ }
88
+ const base = process.env.XDG_CACHE_HOME || join(homedir(), ".cache");
89
+ return join(base, "aft", "lsp-binaries");
90
+ }
91
+ function homeDir() {
92
+ if (process.platform === "win32")
93
+ return process.env.USERPROFILE || process.env.HOME || homedir();
94
+ return process.env.HOME || homedir();
95
+ }
96
+ function dataHome() {
97
+ if (process.env.XDG_DATA_HOME)
98
+ return process.env.XDG_DATA_HOME;
99
+ if (process.platform === "win32") {
100
+ return process.env.LOCALAPPDATA || process.env.APPDATA || join(homeDir(), "AppData", "Local");
101
+ }
102
+ return join(homeDir(), ".local", "share");
103
+ }
104
+ function getCortexKitStorageRoot() {
105
+ return join(dataHome(), "cortexkit", "aft");
106
+ }
107
+ function getTmpLogPath(filename) {
108
+ return join(tmpdir(), filename);
109
+ }
110
+ var init_paths = () => {};
111
+
112
+ // src/lib/binary-probe.ts
113
+ import { execSync, spawnSync } from "node:child_process";
114
+ import { existsSync } from "node:fs";
115
+ import { createRequire } from "node:module";
116
+ import { homedir as homedir2 } from "node:os";
117
+ import { join as join2 } from "node:path";
118
+ async function loadPluginVersion() {
119
+ try {
120
+ const bridgePackageName = "@cortexkit/aft-bridge";
121
+ const bridge = await import(bridgePackageName);
122
+ if (typeof bridge.PLUGIN_VERSION === "string" && bridge.PLUGIN_VERSION.length > 0) {
123
+ return bridge.PLUGIN_VERSION;
124
+ }
125
+ } catch {}
126
+ const require2 = createRequire(import.meta.url);
127
+ for (const relPath of [
128
+ "../../../aft-bridge/package.json",
129
+ "../../package.json",
130
+ "../package.json"
131
+ ]) {
132
+ try {
133
+ const pkg = require2(relPath);
134
+ if (typeof pkg.version === "string" && pkg.version.length > 0)
135
+ return pkg.version;
136
+ } catch {}
137
+ }
138
+ return "unknown";
139
+ }
140
+ function parseVersionOutput(output) {
141
+ for (const line of output.split(/\r?\n/)) {
142
+ const match = line.trim().match(VERSION_LINE);
143
+ if (match?.[1])
144
+ return match[1];
145
+ }
146
+ return null;
147
+ }
148
+ function majorMinor(version) {
149
+ if (!version)
150
+ return null;
151
+ const match = version.trim().match(/^v?(\d+)\.(\d+)\.\d+(?:[-+][0-9A-Za-z.-]+)?$/);
152
+ if (!match)
153
+ return null;
154
+ return `${match[1]}.${match[2]}`;
155
+ }
156
+ function versionMatchesExpected(candidate, expectedVersion) {
157
+ const candidateMajorMinor = majorMinor(candidate);
158
+ const expectedMajorMinor = majorMinor(expectedVersion);
159
+ return candidateMajorMinor !== null && candidateMajorMinor === expectedMajorMinor;
160
+ }
161
+ function probeBinaryVersion(preferredVersion) {
162
+ return probeAftBinary(preferredVersion).version;
163
+ }
164
+ function probeAftBinary(preferredVersion) {
165
+ const expectedVersion = preferredVersion ?? PLUGIN_VERSION;
166
+ const expectedMajorMinor = majorMinor(expectedVersion);
167
+ const candidates = [];
168
+ for (const candidate of aftBinaryCandidates(preferredVersion)) {
169
+ try {
170
+ if (!existsSync(candidate))
171
+ continue;
172
+ const result = spawnSync(candidate, ["--version"], {
173
+ stdio: ["ignore", "pipe", "pipe"],
174
+ encoding: "utf-8",
175
+ timeout: 5000,
176
+ env: process.env
177
+ });
178
+ const output = `${result.stdout ?? ""}
179
+ ${result.stderr ?? ""}`.trim();
180
+ if (result.error || result.status !== 0) {
181
+ candidates.push({
182
+ path: candidate,
183
+ status: "error",
184
+ version: null,
185
+ ...output ? { output } : {},
186
+ error: result.error?.message ?? `exit status ${result.status ?? "unknown"}`
187
+ });
188
+ continue;
189
+ }
190
+ const version = parseVersionOutput(output);
191
+ if (!version) {
192
+ candidates.push({ path: candidate, status: "invalid", version: null, output });
193
+ continue;
194
+ }
195
+ if (!versionMatchesExpected(version, expectedVersion)) {
196
+ candidates.push({ path: candidate, status: "unmatched", version, output });
197
+ continue;
198
+ }
199
+ candidates.push({ path: candidate, status: "matched", version, output });
200
+ return { version, path: candidate, expectedVersion, expectedMajorMinor, candidates };
201
+ } catch (error) {
202
+ candidates.push({
203
+ path: candidate,
204
+ status: "error",
205
+ version: null,
206
+ error: error instanceof Error ? error.message : String(error)
207
+ });
208
+ }
209
+ }
210
+ return { version: null, path: null, expectedVersion, expectedMajorMinor, candidates };
211
+ }
212
+ function pushCandidate(candidates, candidate) {
213
+ if (!candidate)
214
+ return;
215
+ if (!candidates.includes(candidate))
216
+ candidates.push(candidate);
217
+ }
218
+ function firstExisting(candidates) {
219
+ for (const candidate of candidates) {
220
+ try {
221
+ if (!existsSync(candidate))
222
+ continue;
223
+ return candidate;
224
+ } catch {}
225
+ }
226
+ return null;
227
+ }
228
+ function platformKey(platform = process.platform, arch = process.arch) {
229
+ const table = {
230
+ darwin: { arm64: "darwin-arm64", x64: "darwin-x64" },
231
+ linux: { arm64: "linux-arm64", x64: "linux-x64" },
232
+ win32: { x64: "win32-x64" }
233
+ };
234
+ return table[platform]?.[arch] ?? null;
235
+ }
236
+ function aftBinaryCandidates(preferredVersion) {
237
+ const candidates = [];
238
+ if (preferredVersion) {
239
+ const tag = preferredVersion.startsWith("v") ? preferredVersion : `v${preferredVersion}`;
240
+ pushCandidate(candidates, join2(getAftBinaryCacheDir(), tag, getAftBinaryName()));
241
+ }
242
+ const key = platformKey();
243
+ if (key) {
244
+ try {
245
+ const require2 = createRequire(import.meta.url);
246
+ pushCandidate(candidates, require2.resolve(`@cortexkit/aft-${key}/bin/${getAftBinaryName()}`));
247
+ } catch {}
248
+ }
249
+ try {
250
+ const lookup = process.platform === "win32" ? "where aft" : "which aft";
251
+ const resolved = execSync(lookup, {
252
+ stdio: "pipe",
253
+ encoding: "utf-8",
254
+ env: process.env
255
+ }).trim();
256
+ if (resolved) {
257
+ pushCandidate(candidates, resolved.split(/\r?\n/)[0]);
258
+ }
259
+ } catch {}
260
+ pushCandidate(candidates, join2(homedir2(), ".cargo", "bin", getAftBinaryName()));
261
+ return candidates;
262
+ }
263
+ function findAftBinary(preferredVersion) {
264
+ return firstExisting(aftBinaryCandidates(preferredVersion));
265
+ }
266
+ var PLUGIN_VERSION, VERSION_LINE;
267
+ var init_binary_probe = __esm(async () => {
268
+ init_paths();
269
+ PLUGIN_VERSION = await loadPluginVersion();
270
+ VERSION_LINE = /^(?:aft\s+)?v?(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)$/i;
271
+ });
272
+
273
+ // src/lib/fs-util.ts
274
+ import { existsSync as existsSync2, readdirSync, statSync } from "node:fs";
275
+ import { join as join3 } from "node:path";
52
276
  function dirSize(path) {
53
- if (!existsSync(path)) {
277
+ if (!existsSync2(path)) {
54
278
  return 0;
55
279
  }
56
280
  const stat = statSync(path);
@@ -62,7 +286,7 @@ function dirSize(path) {
62
286
  }
63
287
  let total = 0;
64
288
  for (const entry of readdirSync(path)) {
65
- total += dirSize(join(path, entry));
289
+ total += dirSize(join3(path, entry));
66
290
  }
67
291
  return total;
68
292
  }
@@ -7685,10 +7909,10 @@ var require_stringify = __commonJS((exports, module) => {
7685
7909
  replacer = null;
7686
7910
  indent = EMPTY;
7687
7911
  };
7688
- var join2 = (one, two, gap) => one ? two ? one + two.trim() + LF + gap : one.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(one, gap)), gap) : two ? two.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(two, gap)), gap) : EMPTY;
7912
+ var join4 = (one, two, gap) => one ? two ? one + two.trim() + LF + gap : one.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(one, gap)), gap) : two ? two.trimRight() + repeat_line_breaks(Math.max(1, count_trailing_line_breaks(two, gap)), gap) : EMPTY;
7689
7913
  var join_content = (inside, value, gap) => {
7690
7914
  const comment = process_comments(value, PREFIX_BEFORE, gap + indent, true);
7691
- return join2(comment, inside, gap);
7915
+ return join4(comment, inside, gap);
7692
7916
  };
7693
7917
  var stringify_string = (holder, key, value) => {
7694
7918
  const raw = get_raw_string_literal(holder, key);
@@ -7710,13 +7934,13 @@ var require_stringify = __commonJS((exports, module) => {
7710
7934
  if (i !== 0) {
7711
7935
  inside += COMMA;
7712
7936
  }
7713
- const before = join2(after_comma, process_comments(value, BEFORE(i), deeper_gap), deeper_gap);
7937
+ const before = join4(after_comma, process_comments(value, BEFORE(i), deeper_gap), deeper_gap);
7714
7938
  inside += before || LF + deeper_gap;
7715
7939
  inside += stringify(i, value, deeper_gap) || STR_NULL;
7716
7940
  inside += process_comments(value, AFTER_VALUE(i), deeper_gap);
7717
7941
  after_comma = process_comments(value, AFTER(i), deeper_gap);
7718
7942
  }
7719
- inside += join2(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
7943
+ inside += join4(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
7720
7944
  return BRACKET_OPEN + join_content(inside, value, gap) + BRACKET_CLOSE;
7721
7945
  };
7722
7946
  var object_stringify = (value, gap) => {
@@ -7737,13 +7961,13 @@ var require_stringify = __commonJS((exports, module) => {
7737
7961
  inside += COMMA;
7738
7962
  }
7739
7963
  first = false;
7740
- const before = join2(after_comma, process_comments(value, BEFORE(key), deeper_gap), deeper_gap);
7964
+ const before = join4(after_comma, process_comments(value, BEFORE(key), deeper_gap), deeper_gap);
7741
7965
  inside += before || LF + deeper_gap;
7742
7966
  inside += quote(key) + process_comments(value, AFTER_PROP(key), deeper_gap) + COLON + process_comments(value, AFTER_COLON(key), deeper_gap) + SPACE + sv + process_comments(value, AFTER_VALUE(key), deeper_gap);
7743
7967
  after_comma = process_comments(value, AFTER(key), deeper_gap);
7744
7968
  };
7745
7969
  keys.forEach(iteratee);
7746
- inside += join2(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
7970
+ inside += join4(after_comma, process_comments(value, PREFIX_AFTER, deeper_gap), deeper_gap);
7747
7971
  return CURLY_BRACKET_OPEN + join_content(inside, value, gap) + CURLY_BRACKET_CLOSE;
7748
7972
  };
7749
7973
  function stringify(key, holder, gap) {
@@ -7836,21 +8060,21 @@ var require_src2 = __commonJS((exports, module) => {
7836
8060
  });
7837
8061
 
7838
8062
  // src/lib/jsonc.ts
7839
- import { existsSync as existsSync2, mkdirSync, readFileSync, writeFileSync } from "node:fs";
8063
+ import { existsSync as existsSync3, mkdirSync, readFileSync, writeFileSync } from "node:fs";
7840
8064
  import { dirname } from "node:path";
7841
8065
  function detectJsoncFile(configDir, baseName) {
7842
8066
  const jsoncPath = `${configDir}/${baseName}.jsonc`;
7843
8067
  const jsonPath = `${configDir}/${baseName}.json`;
7844
- if (existsSync2(jsoncPath)) {
8068
+ if (existsSync3(jsoncPath)) {
7845
8069
  return { path: jsoncPath, format: "jsonc" };
7846
8070
  }
7847
- if (existsSync2(jsonPath)) {
8071
+ if (existsSync3(jsonPath)) {
7848
8072
  return { path: jsonPath, format: "json" };
7849
8073
  }
7850
8074
  return { path: jsonPath, format: "none" };
7851
8075
  }
7852
8076
  function readJsoncFile(path) {
7853
- if (!existsSync2(path)) {
8077
+ if (!existsSync3(path)) {
7854
8078
  return { value: null };
7855
8079
  }
7856
8080
  try {
@@ -7871,7 +8095,7 @@ function writeJsoncFile(path, value, format = "json") {
7871
8095
  `);
7872
8096
  }
7873
8097
  function ensureAftSchemaUrl(path, format) {
7874
- const existed = existsSync2(path);
8098
+ const existed = existsSync3(path);
7875
8099
  if (!existed) {
7876
8100
  const writeFormat = format === "jsonc" ? "jsonc" : "json";
7877
8101
  writeJsoncFile(path, { $schema: AFT_SCHEMA_URL }, writeFormat);
@@ -7906,73 +8130,10 @@ var init_jsonc = __esm(() => {
7906
8130
  import_comment_json = __toESM(require_src2(), 1);
7907
8131
  });
7908
8132
 
7909
- // src/lib/paths.ts
7910
- import { homedir, tmpdir } from "node:os";
7911
- import { join as join2 } from "node:path";
7912
- function getAftBinaryCacheDir() {
7913
- if (process.env.AFT_CACHE_DIR) {
7914
- return join2(process.env.AFT_CACHE_DIR, "bin");
7915
- }
7916
- if (process.platform === "win32") {
7917
- const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
7918
- const base2 = localAppData || join2(homedir(), "AppData", "Local");
7919
- return join2(base2, "aft", "bin");
7920
- }
7921
- const base = process.env.XDG_CACHE_HOME || join2(homedir(), ".cache");
7922
- return join2(base, "aft", "bin");
7923
- }
7924
- function getAftBinaryName() {
7925
- return process.platform === "win32" ? "aft.exe" : "aft";
7926
- }
7927
- function getAftLspPackagesDir() {
7928
- if (process.env.AFT_CACHE_DIR) {
7929
- return join2(process.env.AFT_CACHE_DIR, "lsp-packages");
7930
- }
7931
- if (process.platform === "win32") {
7932
- const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
7933
- const base2 = localAppData || join2(homedir(), "AppData", "Local");
7934
- return join2(base2, "aft", "lsp-packages");
7935
- }
7936
- const base = process.env.XDG_CACHE_HOME || join2(homedir(), ".cache");
7937
- return join2(base, "aft", "lsp-packages");
7938
- }
7939
- function getAftLspBinariesDir() {
7940
- if (process.env.AFT_CACHE_DIR) {
7941
- return join2(process.env.AFT_CACHE_DIR, "lsp-binaries");
7942
- }
7943
- if (process.platform === "win32") {
7944
- const localAppData = process.env.LOCALAPPDATA || process.env.APPDATA;
7945
- const base2 = localAppData || join2(homedir(), "AppData", "Local");
7946
- return join2(base2, "aft", "lsp-binaries");
7947
- }
7948
- const base = process.env.XDG_CACHE_HOME || join2(homedir(), ".cache");
7949
- return join2(base, "aft", "lsp-binaries");
7950
- }
7951
- function homeDir() {
7952
- if (process.platform === "win32")
7953
- return process.env.USERPROFILE || process.env.HOME || homedir();
7954
- return process.env.HOME || homedir();
7955
- }
7956
- function dataHome() {
7957
- if (process.env.XDG_DATA_HOME)
7958
- return process.env.XDG_DATA_HOME;
7959
- if (process.platform === "win32") {
7960
- return process.env.LOCALAPPDATA || process.env.APPDATA || join2(homeDir(), "AppData", "Local");
7961
- }
7962
- return join2(homeDir(), ".local", "share");
7963
- }
7964
- function getCortexKitStorageRoot() {
7965
- return join2(dataHome(), "cortexkit", "aft");
7966
- }
7967
- function getTmpLogPath(filename) {
7968
- return join2(tmpdir(), filename);
7969
- }
7970
- var init_paths = () => {};
7971
-
7972
8133
  // src/lib/self-version.ts
7973
- import { createRequire } from "node:module";
8134
+ import { createRequire as createRequire2 } from "node:module";
7974
8135
  function getSelfVersion() {
7975
- const require2 = createRequire(import.meta.url);
8136
+ const require2 = createRequire2(import.meta.url);
7976
8137
  for (const relPath of ["../../package.json", "../package.json"]) {
7977
8138
  try {
7978
8139
  const version = require2(relPath).version;
@@ -7986,27 +8147,27 @@ function getSelfVersion() {
7986
8147
  var init_self_version = () => {};
7987
8148
 
7988
8149
  // src/adapters/opencode.ts
7989
- import { execSync } from "node:child_process";
7990
- import { existsSync as existsSync3, readFileSync as readFileSync2, rmSync, statSync as statSync2 } from "node:fs";
7991
- import { homedir as homedir2 } from "node:os";
7992
- import { dirname as dirname2, join as join3, parse, resolve } from "node:path";
8150
+ import { execSync as execSync2 } from "node:child_process";
8151
+ import { existsSync as existsSync4, readFileSync as readFileSync2, rmSync, statSync as statSync2 } from "node:fs";
8152
+ import { homedir as homedir3 } from "node:os";
8153
+ import { dirname as dirname2, join as join4, parse, resolve } from "node:path";
7993
8154
  import { fileURLToPath } from "node:url";
7994
8155
  function getOpenCodeConfigDir() {
7995
8156
  const envDir = process.env.OPENCODE_CONFIG_DIR?.trim();
7996
8157
  if (envDir)
7997
8158
  return resolve(envDir);
7998
- const xdg = process.env.XDG_CONFIG_HOME || join3(homedir2(), ".config");
7999
- return join3(xdg, "opencode");
8159
+ const xdg = process.env.XDG_CONFIG_HOME || join4(homedir3(), ".config");
8160
+ return join4(xdg, "opencode");
8000
8161
  }
8001
8162
  function getOpenCodeCacheDir() {
8002
8163
  const xdg = process.env.XDG_CACHE_HOME;
8003
8164
  if (xdg)
8004
- return join3(xdg, "opencode");
8165
+ return join4(xdg, "opencode");
8005
8166
  if (process.platform === "win32") {
8006
- const localAppData = process.env.LOCALAPPDATA ?? join3(homedir2(), "AppData", "Local");
8007
- return join3(localAppData, "opencode");
8167
+ const localAppData = process.env.LOCALAPPDATA ?? join4(homedir3(), "AppData", "Local");
8168
+ return join4(localAppData, "opencode");
8008
8169
  }
8009
- return join3(homedir2(), ".cache", "opencode");
8170
+ return join4(homedir3(), ".cache", "opencode");
8010
8171
  }
8011
8172
  function pathFromEntry(entry) {
8012
8173
  if (entry.startsWith("file://")) {
@@ -8025,13 +8186,13 @@ function pathPointsToOurPlugin(entry) {
8025
8186
  if (!fsPath)
8026
8187
  return false;
8027
8188
  try {
8028
- if (!existsSync3(fsPath))
8189
+ if (!existsSync4(fsPath))
8029
8190
  return false;
8030
8191
  let searchDir = statSync2(fsPath).isDirectory() ? fsPath : dirname2(fsPath);
8031
8192
  let pkgJsonPath = null;
8032
8193
  while (true) {
8033
- const candidate = join3(searchDir, "package.json");
8034
- if (existsSync3(candidate)) {
8194
+ const candidate = join4(searchDir, "package.json");
8195
+ if (existsSync4(candidate)) {
8035
8196
  pkgJsonPath = candidate;
8036
8197
  break;
8037
8198
  }
@@ -8063,7 +8224,7 @@ class OpenCodeAdapter {
8063
8224
  pluginEntryWithVersion = PLUGIN_ENTRY;
8064
8225
  isInstalled() {
8065
8226
  try {
8066
- execSync("opencode --version", { stdio: "ignore" });
8227
+ execSync2("opencode --version", { stdio: "ignore" });
8067
8228
  return true;
8068
8229
  } catch {
8069
8230
  return false;
@@ -8071,7 +8232,7 @@ class OpenCodeAdapter {
8071
8232
  }
8072
8233
  getHostVersion() {
8073
8234
  try {
8074
- return execSync("opencode --version", { encoding: "utf-8", stdio: "pipe" }).trim();
8235
+ return execSync2("opencode --version", { encoding: "utf-8", stdio: "pipe" }).trim();
8075
8236
  } catch {
8076
8237
  return null;
8077
8238
  }
@@ -8140,11 +8301,11 @@ class OpenCodeAdapter {
8140
8301
  };
8141
8302
  }
8142
8303
  getPluginCacheInfo() {
8143
- const path = join3(getOpenCodeCacheDir(), "packages", PLUGIN_ENTRY);
8304
+ const path = join4(getOpenCodeCacheDir(), "packages", PLUGIN_ENTRY);
8144
8305
  let cached;
8145
8306
  try {
8146
- const installedPkgPath = join3(path, "node_modules", "@cortexkit", "aft-opencode", "package.json");
8147
- if (existsSync3(installedPkgPath)) {
8307
+ const installedPkgPath = join4(path, "node_modules", "@cortexkit", "aft-opencode", "package.json");
8308
+ if (existsSync4(installedPkgPath)) {
8148
8309
  const pkg = JSON.parse(readFileSync2(installedPkgPath, "utf-8"));
8149
8310
  cached = typeof pkg.version === "string" ? pkg.version : undefined;
8150
8311
  }
@@ -8155,7 +8316,7 @@ class OpenCodeAdapter {
8155
8316
  path,
8156
8317
  cached,
8157
8318
  latest: getSelfVersion(),
8158
- exists: existsSync3(path)
8319
+ exists: existsSync4(path)
8159
8320
  };
8160
8321
  }
8161
8322
  getStorageDir() {
@@ -8204,11 +8365,11 @@ class OpenCodeAdapter {
8204
8365
  describeStorageSubtrees() {
8205
8366
  const storage = this.getStorageDir();
8206
8367
  return {
8207
- index: dirSize(join3(storage, "index")),
8208
- semantic: dirSize(join3(storage, "semantic")),
8209
- backups: dirSize(join3(storage, "backups")),
8210
- url_cache: dirSize(join3(storage, "url_cache")),
8211
- onnxruntime: dirSize(join3(storage, "onnxruntime"))
8368
+ index: dirSize(join4(storage, "index")),
8369
+ semantic: dirSize(join4(storage, "semantic")),
8370
+ backups: dirSize(join4(storage, "backups")),
8371
+ url_cache: dirSize(join4(storage, "url_cache")),
8372
+ onnxruntime: dirSize(join4(storage, "onnxruntime"))
8212
8373
  };
8213
8374
  }
8214
8375
  }
@@ -8222,18 +8383,18 @@ var init_opencode = __esm(() => {
8222
8383
  });
8223
8384
 
8224
8385
  // src/adapters/pi.ts
8225
- import { execSync as execSync2, spawnSync } from "node:child_process";
8226
- import { existsSync as existsSync4, readFileSync as readFileSync3 } from "node:fs";
8227
- import { homedir as homedir3 } from "node:os";
8228
- import { join as join4 } from "node:path";
8229
- function getPiAgentDir() {
8386
+ import { execSync as execSync3, spawnSync as spawnSync2 } from "node:child_process";
8387
+ import { existsSync as existsSync5, readFileSync as readFileSync3 } from "node:fs";
8388
+ import { homedir as homedir4 } from "node:os";
8389
+ import { join as join5 } from "node:path";
8390
+ function getPiAgentDir() {
8230
8391
  const envHome = process.platform === "win32" ? process.env.USERPROFILE : process.env.HOME;
8231
- const home = envHome && envHome.length > 0 ? envHome : homedir3();
8232
- return join4(home, ".pi", "agent");
8392
+ const home = envHome && envHome.length > 0 ? envHome : homedir4();
8393
+ return join5(home, ".pi", "agent");
8233
8394
  }
8234
8395
  function readPiExtensionIndex() {
8235
- const settingsPath = join4(getPiAgentDir(), "settings.json");
8236
- if (existsSync4(settingsPath)) {
8396
+ const settingsPath = join5(getPiAgentDir(), "settings.json");
8397
+ if (existsSync5(settingsPath)) {
8237
8398
  try {
8238
8399
  const raw = readFileSync3(settingsPath, "utf-8");
8239
8400
  const trimmed = raw.replace(/^\uFEFF/, "");
@@ -8246,13 +8407,13 @@ function readPiExtensionIndex() {
8246
8407
  } catch {}
8247
8408
  }
8248
8409
  const candidates = [
8249
- join4(getPiAgentDir(), "extensions.json"),
8250
- join4(getPiAgentDir(), "extensions.jsonc"),
8251
- join4(getPiAgentDir(), "config.json"),
8252
- join4(getPiAgentDir(), "config.jsonc")
8410
+ join5(getPiAgentDir(), "extensions.json"),
8411
+ join5(getPiAgentDir(), "extensions.jsonc"),
8412
+ join5(getPiAgentDir(), "config.json"),
8413
+ join5(getPiAgentDir(), "config.jsonc")
8253
8414
  ];
8254
8415
  for (const path of candidates) {
8255
- if (!existsSync4(path))
8416
+ if (!existsSync5(path))
8256
8417
  continue;
8257
8418
  try {
8258
8419
  const { value } = readJsoncFile(path);
@@ -8285,15 +8446,15 @@ function piEntryMatchesAft(entry) {
8285
8446
  } else if (entry.startsWith("/")) {
8286
8447
  resolved = entry;
8287
8448
  } else if (entry.length > 0) {
8288
- resolved = join4(getPiAgentDir(), entry);
8449
+ resolved = join5(getPiAgentDir(), entry);
8289
8450
  }
8290
8451
  if (!resolved)
8291
8452
  return false;
8292
8453
  try {
8293
- if (!existsSync4(resolved))
8454
+ if (!existsSync5(resolved))
8294
8455
  return false;
8295
- const pkgPath = join4(resolved, "package.json");
8296
- if (!existsSync4(pkgPath))
8456
+ const pkgPath = join5(resolved, "package.json");
8457
+ if (!existsSync5(pkgPath))
8297
8458
  return false;
8298
8459
  const pkg = JSON.parse(readFileSync3(pkgPath, "utf-8"));
8299
8460
  return pkg.name === PLUGIN_NAME2;
@@ -8313,7 +8474,7 @@ class PiAdapter {
8313
8474
  pluginEntryWithVersion = PLUGIN_ENTRY2;
8314
8475
  isInstalled() {
8315
8476
  try {
8316
- execSync2("pi --version", { stdio: "ignore" });
8477
+ execSync3("pi --version", { stdio: "ignore" });
8317
8478
  return true;
8318
8479
  } catch {
8319
8480
  return false;
@@ -8321,7 +8482,7 @@ class PiAdapter {
8321
8482
  }
8322
8483
  getHostVersion() {
8323
8484
  try {
8324
- const result = spawnSync("pi", ["--version"], {
8485
+ const result = spawnSync2("pi", ["--version"], {
8325
8486
  stdio: ["ignore", "pipe", "pipe"],
8326
8487
  encoding: "utf-8"
8327
8488
  });
@@ -8342,7 +8503,7 @@ class PiAdapter {
8342
8503
  const aft = detectJsoncFile(configDir, "aft");
8343
8504
  return {
8344
8505
  configDir,
8345
- harnessConfig: index.path ?? join4(configDir, "extensions.json"),
8506
+ harnessConfig: index.path ?? join5(configDir, "extensions.json"),
8346
8507
  harnessConfigFormat: index.path ? "json" : "none",
8347
8508
  aftConfig: aft.path,
8348
8509
  aftConfigFormat: aft.format
@@ -8369,7 +8530,7 @@ class PiAdapter {
8369
8530
  };
8370
8531
  }
8371
8532
  try {
8372
- execSync2(`pi install ${PLUGIN_ENTRY2}`, { stdio: "inherit" });
8533
+ execSync3(`pi install ${PLUGIN_ENTRY2}`, { stdio: "inherit" });
8373
8534
  return {
8374
8535
  ok: true,
8375
8536
  action: "added",
@@ -8387,11 +8548,11 @@ class PiAdapter {
8387
8548
  }
8388
8549
  getPluginCacheInfo() {
8389
8550
  const candidates = [
8390
- join4(getPiAgentDir(), "node_modules", "@cortexkit", "aft-pi", "package.json"),
8391
- join4(getPiAgentDir(), "extensions", "node_modules", "@cortexkit", "aft-pi", "package.json")
8551
+ join5(getPiAgentDir(), "node_modules", "@cortexkit", "aft-pi", "package.json"),
8552
+ join5(getPiAgentDir(), "extensions", "node_modules", "@cortexkit", "aft-pi", "package.json")
8392
8553
  ];
8393
8554
  for (const candidate of candidates) {
8394
- if (!existsSync4(candidate))
8555
+ if (!existsSync5(candidate))
8395
8556
  continue;
8396
8557
  try {
8397
8558
  const pkg = JSON.parse(readFileSync3(candidate, "utf-8"));
@@ -8405,7 +8566,7 @@ class PiAdapter {
8405
8566
  } catch {}
8406
8567
  }
8407
8568
  return {
8408
- path: join4(getPiAgentDir(), "extensions"),
8569
+ path: join5(getPiAgentDir(), "extensions"),
8409
8570
  exists: false
8410
8571
  };
8411
8572
  }
@@ -8427,11 +8588,11 @@ class PiAdapter {
8427
8588
  describeStorageSubtrees() {
8428
8589
  const storage = this.getStorageDir();
8429
8590
  return {
8430
- index: dirSize(join4(storage, "index")),
8431
- semantic: dirSize(join4(storage, "semantic")),
8432
- backups: dirSize(join4(storage, "backups")),
8433
- url_cache: dirSize(join4(storage, "url_cache")),
8434
- onnxruntime: dirSize(join4(storage, "onnxruntime"))
8591
+ index: dirSize(join5(storage, "index")),
8592
+ semantic: dirSize(join5(storage, "semantic")),
8593
+ backups: dirSize(join5(storage, "backups")),
8594
+ url_cache: dirSize(join5(storage, "url_cache")),
8595
+ onnxruntime: dirSize(join5(storage, "onnxruntime"))
8435
8596
  };
8436
8597
  }
8437
8598
  }
@@ -8444,6 +8605,9 @@ var init_pi = __esm(() => {
8444
8605
  });
8445
8606
 
8446
8607
  // src/adapters/index.ts
8608
+ function getAllAdapters() {
8609
+ return ALL;
8610
+ }
8447
8611
  function getAdapter(kind) {
8448
8612
  const found = ALL.find((a) => a.kind === kind);
8449
8613
  if (!found)
@@ -9903,11 +10067,59 @@ async function resolveAdaptersForCommand(argv, options) {
9903
10067
  })), installed.map((a) => a.kind));
9904
10068
  return picks.map((kind) => getAdapter(kind));
9905
10069
  }
10070
+ function getAllRegistryAdapters() {
10071
+ return getAllAdapters();
10072
+ }
9906
10073
  var init_harness_select = __esm(() => {
9907
10074
  init_adapters();
9908
10075
  init_prompts();
9909
10076
  });
9910
10077
 
10078
+ // src/commands/version.ts
10079
+ var exports_version = {};
10080
+ __export(exports_version, {
10081
+ runVersion: () => runVersion
10082
+ });
10083
+ function runVersion() {
10084
+ const cliVersion = getSelfVersion();
10085
+ const binaryVersion = probeBinaryVersion();
10086
+ const lines = [];
10087
+ lines.push("");
10088
+ lines.push(" AFT versions");
10089
+ lines.push("");
10090
+ lines.push(` @cortexkit/aft (this CLI) v${cliVersion}`);
10091
+ lines.push(` aft binary ${binaryVersion ?? "not installed"}`);
10092
+ lines.push("");
10093
+ const adapters = getAllRegistryAdapters();
10094
+ const labelWidth = Math.max(...adapters.map((adapter) => adapter.displayName.length));
10095
+ for (const adapter of adapters) {
10096
+ const label = adapter.displayName.padEnd(labelWidth);
10097
+ if (!adapter.isInstalled()) {
10098
+ lines.push(` ${label} host not installed`);
10099
+ continue;
10100
+ }
10101
+ const host = adapter.getHostVersion() ?? "unknown";
10102
+ const registered = adapter.hasPluginEntry();
10103
+ let pluginPart;
10104
+ if (!registered) {
10105
+ pluginPart = "plugin not registered";
10106
+ } else {
10107
+ const cached = adapter.getPluginCacheInfo().cached;
10108
+ pluginPart = cached ? `plugin ${cached}` : "plugin registered (version unknown)";
10109
+ }
10110
+ lines.push(` ${label} host ${host} · ${pluginPart}`);
10111
+ }
10112
+ lines.push("");
10113
+ console.log(lines.join(`
10114
+ `));
10115
+ return 0;
10116
+ }
10117
+ var init_version = __esm(async () => {
10118
+ init_harness_select();
10119
+ init_self_version();
10120
+ await init_binary_probe();
10121
+ });
10122
+
9911
10123
  // src/commands/setup.ts
9912
10124
  var exports_setup = {};
9913
10125
  __export(exports_setup, {
@@ -10101,167 +10313,6 @@ ${stderrTrimmed}`);
10101
10313
  var MAX_NOISE_LINES_IN_ERROR = 5;
10102
10314
  var init_aft_bridge = () => {};
10103
10315
 
10104
- // src/lib/binary-probe.ts
10105
- import { execSync as execSync3, spawnSync as spawnSync2 } from "node:child_process";
10106
- import { existsSync as existsSync5 } from "node:fs";
10107
- import { createRequire as createRequire2 } from "node:module";
10108
- import { homedir as homedir4 } from "node:os";
10109
- import { join as join5 } from "node:path";
10110
- async function loadPluginVersion() {
10111
- try {
10112
- const bridgePackageName = "@cortexkit/aft-bridge";
10113
- const bridge = await import(bridgePackageName);
10114
- if (typeof bridge.PLUGIN_VERSION === "string" && bridge.PLUGIN_VERSION.length > 0) {
10115
- return bridge.PLUGIN_VERSION;
10116
- }
10117
- } catch {}
10118
- const require2 = createRequire2(import.meta.url);
10119
- for (const relPath of [
10120
- "../../../aft-bridge/package.json",
10121
- "../../package.json",
10122
- "../package.json"
10123
- ]) {
10124
- try {
10125
- const pkg = require2(relPath);
10126
- if (typeof pkg.version === "string" && pkg.version.length > 0)
10127
- return pkg.version;
10128
- } catch {}
10129
- }
10130
- return "unknown";
10131
- }
10132
- function parseVersionOutput(output) {
10133
- for (const line of output.split(/\r?\n/)) {
10134
- const match = line.trim().match(VERSION_LINE);
10135
- if (match?.[1])
10136
- return match[1];
10137
- }
10138
- return null;
10139
- }
10140
- function majorMinor(version) {
10141
- if (!version)
10142
- return null;
10143
- const match = version.trim().match(/^v?(\d+)\.(\d+)\.\d+(?:[-+][0-9A-Za-z.-]+)?$/);
10144
- if (!match)
10145
- return null;
10146
- return `${match[1]}.${match[2]}`;
10147
- }
10148
- function versionMatchesExpected(candidate, expectedVersion) {
10149
- const candidateMajorMinor = majorMinor(candidate);
10150
- const expectedMajorMinor = majorMinor(expectedVersion);
10151
- return candidateMajorMinor !== null && candidateMajorMinor === expectedMajorMinor;
10152
- }
10153
- function probeBinaryVersion(preferredVersion) {
10154
- return probeAftBinary(preferredVersion).version;
10155
- }
10156
- function probeAftBinary(preferredVersion) {
10157
- const expectedVersion = preferredVersion ?? PLUGIN_VERSION;
10158
- const expectedMajorMinor = majorMinor(expectedVersion);
10159
- const candidates = [];
10160
- for (const candidate of aftBinaryCandidates(preferredVersion)) {
10161
- try {
10162
- if (!existsSync5(candidate))
10163
- continue;
10164
- const result = spawnSync2(candidate, ["--version"], {
10165
- stdio: ["ignore", "pipe", "pipe"],
10166
- encoding: "utf-8",
10167
- timeout: 5000,
10168
- env: process.env
10169
- });
10170
- const output = `${result.stdout ?? ""}
10171
- ${result.stderr ?? ""}`.trim();
10172
- if (result.error || result.status !== 0) {
10173
- candidates.push({
10174
- path: candidate,
10175
- status: "error",
10176
- version: null,
10177
- ...output ? { output } : {},
10178
- error: result.error?.message ?? `exit status ${result.status ?? "unknown"}`
10179
- });
10180
- continue;
10181
- }
10182
- const version = parseVersionOutput(output);
10183
- if (!version) {
10184
- candidates.push({ path: candidate, status: "invalid", version: null, output });
10185
- continue;
10186
- }
10187
- if (!versionMatchesExpected(version, expectedVersion)) {
10188
- candidates.push({ path: candidate, status: "unmatched", version, output });
10189
- continue;
10190
- }
10191
- candidates.push({ path: candidate, status: "matched", version, output });
10192
- return { version, path: candidate, expectedVersion, expectedMajorMinor, candidates };
10193
- } catch (error) {
10194
- candidates.push({
10195
- path: candidate,
10196
- status: "error",
10197
- version: null,
10198
- error: error instanceof Error ? error.message : String(error)
10199
- });
10200
- }
10201
- }
10202
- return { version: null, path: null, expectedVersion, expectedMajorMinor, candidates };
10203
- }
10204
- function pushCandidate(candidates, candidate) {
10205
- if (!candidate)
10206
- return;
10207
- if (!candidates.includes(candidate))
10208
- candidates.push(candidate);
10209
- }
10210
- function firstExisting(candidates) {
10211
- for (const candidate of candidates) {
10212
- try {
10213
- if (!existsSync5(candidate))
10214
- continue;
10215
- return candidate;
10216
- } catch {}
10217
- }
10218
- return null;
10219
- }
10220
- function platformKey(platform = process.platform, arch = process.arch) {
10221
- const table = {
10222
- darwin: { arm64: "darwin-arm64", x64: "darwin-x64" },
10223
- linux: { arm64: "linux-arm64", x64: "linux-x64" },
10224
- win32: { x64: "win32-x64" }
10225
- };
10226
- return table[platform]?.[arch] ?? null;
10227
- }
10228
- function aftBinaryCandidates(preferredVersion) {
10229
- const candidates = [];
10230
- if (preferredVersion) {
10231
- const tag = preferredVersion.startsWith("v") ? preferredVersion : `v${preferredVersion}`;
10232
- pushCandidate(candidates, join5(getAftBinaryCacheDir(), tag, getAftBinaryName()));
10233
- }
10234
- const key = platformKey();
10235
- if (key) {
10236
- try {
10237
- const require2 = createRequire2(import.meta.url);
10238
- pushCandidate(candidates, require2.resolve(`@cortexkit/aft-${key}/bin/${getAftBinaryName()}`));
10239
- } catch {}
10240
- }
10241
- try {
10242
- const lookup = process.platform === "win32" ? "where aft" : "which aft";
10243
- const resolved = execSync3(lookup, {
10244
- stdio: "pipe",
10245
- encoding: "utf-8",
10246
- env: process.env
10247
- }).trim();
10248
- if (resolved) {
10249
- pushCandidate(candidates, resolved.split(/\r?\n/)[0]);
10250
- }
10251
- } catch {}
10252
- pushCandidate(candidates, join5(homedir4(), ".cargo", "bin", getAftBinaryName()));
10253
- return candidates;
10254
- }
10255
- function findAftBinary(preferredVersion) {
10256
- return firstExisting(aftBinaryCandidates(preferredVersion));
10257
- }
10258
- var PLUGIN_VERSION, VERSION_LINE;
10259
- var init_binary_probe = __esm(async () => {
10260
- init_paths();
10261
- PLUGIN_VERSION = await loadPluginVersion();
10262
- VERSION_LINE = /^(?:aft\s+)?v?(\d+\.\d+\.\d+(?:[-+][0-9A-Za-z.-]+)?)$/i;
10263
- });
10264
-
10265
10316
  // src/commands/lsp.ts
10266
10317
  var exports_lsp = {};
10267
10318
  __export(exports_lsp, {
@@ -11552,6 +11603,12 @@ function createGitHubIssue(repo, title, body) {
11552
11603
  var init_github = () => {};
11553
11604
 
11554
11605
  // src/lib/issue-body.ts
11606
+ function filterLogToSession(logText, sessionId) {
11607
+ const bareId = sessionId.replace(/^ses_/, "");
11608
+ const keepTokens = [`[ses_${bareId}]`, `[${bareId}]`];
11609
+ return logText.split(/\r?\n/).filter((line) => !SESSION_TAG_PATTERN.test(line) || keepTokens.some((token) => line.includes(token))).join(`
11610
+ `);
11611
+ }
11555
11612
  function isErrorLogLine(line) {
11556
11613
  return ERROR_LOG_PATTERNS.some((rx) => rx.test(line));
11557
11614
  }
@@ -11629,8 +11686,9 @@ function truncateToByteBudget(input, maxBytes) {
11629
11686
  }
11630
11687
  return buf.subarray(0, end).toString("utf8");
11631
11688
  }
11632
- var MAX_GITHUB_BODY_BYTES = 60000, ERROR_LOG_PATTERNS;
11689
+ var MAX_GITHUB_BODY_BYTES = 60000, SESSION_TAG_PATTERN, ERROR_LOG_PATTERNS;
11633
11690
  var init_issue_body = __esm(() => {
11691
+ SESSION_TAG_PATTERN = /\[ses_[^\]\s]+\]|\[[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}\]/;
11634
11692
  ERROR_LOG_PATTERNS = [
11635
11693
  /\bcrashed:/i,
11636
11694
  /\bfailed:/i,
@@ -11726,6 +11784,189 @@ var init_onnx_fix = __esm(() => {
11726
11784
  init_prompts();
11727
11785
  });
11728
11786
 
11787
+ // src/lib/sessions.ts
11788
+ import { existsSync as existsSync13, readdirSync as readdirSync6, readFileSync as readFileSync4, statSync as statSync8 } from "node:fs";
11789
+ import { createRequire as createRequire3 } from "node:module";
11790
+ import { homedir as homedir7 } from "node:os";
11791
+ import { basename as basename2, join as join11 } from "node:path";
11792
+ function listRecentSessions(adapter) {
11793
+ try {
11794
+ if (adapter.kind === "opencode")
11795
+ return listRecentOpenCodeSessions();
11796
+ if (adapter.kind === "pi")
11797
+ return listRecentPiSessions();
11798
+ return [];
11799
+ } catch {
11800
+ return [];
11801
+ }
11802
+ }
11803
+ function mapOpenCodeSessionRows(rows) {
11804
+ return rows.map((row) => {
11805
+ if (typeof row.id !== "string" || row.id.length === 0)
11806
+ return null;
11807
+ if (typeof row.title !== "string" || row.title.length === 0)
11808
+ return null;
11809
+ const lastActivity = typeof row.time_updated === "number" ? row.time_updated : Number(row.time_updated);
11810
+ if (!Number.isFinite(lastActivity))
11811
+ return null;
11812
+ return {
11813
+ id: row.id,
11814
+ title: row.title,
11815
+ lastActivity
11816
+ };
11817
+ }).filter((session) => session !== null).sort((a, b) => b.lastActivity - a.lastActivity).slice(0, MAX_RECENT_SESSIONS);
11818
+ }
11819
+ function listRecentOpenCodeSessions() {
11820
+ const dbPath = join11(getXdgDataHome(), "opencode", "opencode.db");
11821
+ if (!existsSync13(dbPath))
11822
+ return [];
11823
+ let db = null;
11824
+ try {
11825
+ const require2 = createRequire3(import.meta.url);
11826
+ const sqlite = require2("node:sqlite");
11827
+ db = new sqlite.DatabaseSync(dbPath, { readOnly: true });
11828
+ const rows = db.prepare("SELECT id, title, time_updated FROM session ORDER BY time_updated DESC LIMIT 5").all();
11829
+ return mapOpenCodeSessionRows(rows);
11830
+ } catch {
11831
+ return [];
11832
+ } finally {
11833
+ try {
11834
+ db?.close();
11835
+ } catch {}
11836
+ }
11837
+ }
11838
+ function getXdgDataHome() {
11839
+ const xdgDataHome = process.env.XDG_DATA_HOME;
11840
+ return xdgDataHome && xdgDataHome.length > 0 ? xdgDataHome : join11(homedir7(), ".local", "share");
11841
+ }
11842
+ function listRecentPiSessions() {
11843
+ return listPiSessionsFromDir(join11(getHomeDir(), ".pi", "agent", "sessions"));
11844
+ }
11845
+ function getHomeDir() {
11846
+ const envHome = process.platform === "win32" ? process.env.USERPROFILE : process.env.HOME;
11847
+ return envHome && envHome.length > 0 ? envHome : homedir7();
11848
+ }
11849
+ function listPiSessionsFromDir(sessionsDir) {
11850
+ try {
11851
+ if (!existsSync13(sessionsDir))
11852
+ return [];
11853
+ const files = collectJsonlFiles(sessionsDir).map((filePath) => {
11854
+ try {
11855
+ const stats = statSync8(filePath);
11856
+ return { filePath, mtimeMs: stats.mtimeMs };
11857
+ } catch {
11858
+ return null;
11859
+ }
11860
+ }).filter((entry) => entry !== null).sort((a, b) => b.mtimeMs - a.mtimeMs).slice(0, MAX_RECENT_SESSIONS * 4);
11861
+ const sessions = [];
11862
+ for (const file of files) {
11863
+ const parsed = parsePiSessionJsonl(readFileSync4(file.filePath, "utf8"), basename2(file.filePath));
11864
+ if (!parsed)
11865
+ continue;
11866
+ sessions.push({ ...parsed, lastActivity: file.mtimeMs });
11867
+ if (sessions.length >= MAX_RECENT_SESSIONS)
11868
+ break;
11869
+ }
11870
+ return sessions;
11871
+ } catch {
11872
+ return [];
11873
+ }
11874
+ }
11875
+ function collectJsonlFiles(root) {
11876
+ const files = [];
11877
+ const stack = [root];
11878
+ while (stack.length > 0) {
11879
+ const dir = stack.pop();
11880
+ if (!dir)
11881
+ continue;
11882
+ let entries;
11883
+ try {
11884
+ entries = readdirSync6(dir, { withFileTypes: true });
11885
+ } catch {
11886
+ continue;
11887
+ }
11888
+ for (const entry of entries) {
11889
+ const path = join11(dir, entry.name);
11890
+ if (entry.isDirectory()) {
11891
+ stack.push(path);
11892
+ } else if (entry.isFile() && entry.name.endsWith(".jsonl")) {
11893
+ files.push(path);
11894
+ }
11895
+ }
11896
+ }
11897
+ return files;
11898
+ }
11899
+ function parsePiSessionJsonl(jsonl, fallbackFilename = "") {
11900
+ let id = extractUuidFromFilename(fallbackFilename);
11901
+ let title = null;
11902
+ for (const line of jsonl.split(/\r?\n/)) {
11903
+ const trimmed = line.trim();
11904
+ if (trimmed.length === 0)
11905
+ continue;
11906
+ let value;
11907
+ try {
11908
+ value = JSON.parse(trimmed);
11909
+ } catch {
11910
+ continue;
11911
+ }
11912
+ if (!value || typeof value !== "object")
11913
+ continue;
11914
+ const record = value;
11915
+ if (record.type === "session" && typeof record.id === "string" && record.id.length > 0) {
11916
+ id = record.id;
11917
+ }
11918
+ if (title === null) {
11919
+ const maybeTitle = extractPiUserMessageText(record);
11920
+ if (maybeTitle)
11921
+ title = truncateTitle(maybeTitle);
11922
+ }
11923
+ if (id && title)
11924
+ break;
11925
+ }
11926
+ if (!id)
11927
+ return null;
11928
+ return { id, title: title ?? id };
11929
+ }
11930
+ function extractPiUserMessageText(record) {
11931
+ if (record.type !== "message")
11932
+ return null;
11933
+ const message = record.message;
11934
+ if (!message || typeof message !== "object")
11935
+ return null;
11936
+ const messageRecord = message;
11937
+ if (messageRecord.role !== "user")
11938
+ return null;
11939
+ return extractTextFromContent(messageRecord.content);
11940
+ }
11941
+ function extractTextFromContent(content) {
11942
+ if (typeof content === "string")
11943
+ return content.trim() || null;
11944
+ if (!Array.isArray(content))
11945
+ return null;
11946
+ const parts = content.map((part) => {
11947
+ if (typeof part === "string")
11948
+ return part;
11949
+ if (!part || typeof part !== "object")
11950
+ return "";
11951
+ const partRecord = part;
11952
+ return partRecord.type === "text" && typeof partRecord.text === "string" ? partRecord.text : "";
11953
+ }).filter((text2) => text2.trim().length > 0);
11954
+ const joined = parts.join(" ").trim();
11955
+ return joined.length > 0 ? joined : null;
11956
+ }
11957
+ function extractUuidFromFilename(filename) {
11958
+ const match = filename.match(/([0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12})\.jsonl$/i);
11959
+ return match?.[1] ?? null;
11960
+ }
11961
+ function truncateTitle(title, maxLength = 60) {
11962
+ const normalized = title.replace(/\s+/g, " ").trim();
11963
+ if (normalized.length <= maxLength)
11964
+ return normalized;
11965
+ return `${normalized.slice(0, Math.max(0, maxLength - 1))}…`;
11966
+ }
11967
+ var MAX_RECENT_SESSIONS = 5;
11968
+ var init_sessions = () => {};
11969
+
11729
11970
  // src/commands/doctor.ts
11730
11971
  var exports_doctor = {};
11731
11972
  __export(exports_doctor, {
@@ -11744,17 +11985,17 @@ __export(exports_doctor, {
11744
11985
  });
11745
11986
  import {
11746
11987
  chmodSync,
11747
- existsSync as existsSync13,
11988
+ existsSync as existsSync14,
11748
11989
  mkdirSync as mkdirSync2,
11749
11990
  mkdtempSync,
11750
- readFileSync as readFileSync4,
11991
+ readFileSync as readFileSync5,
11751
11992
  realpathSync as realpathSync3,
11752
11993
  rmSync as rmSync4,
11753
- statSync as statSync8,
11994
+ statSync as statSync9,
11754
11995
  writeFileSync as writeFileSync2
11755
11996
  } from "node:fs";
11756
11997
  import { tmpdir as tmpdir2 } from "node:os";
11757
- import { join as join11 } from "node:path";
11998
+ import { join as join12 } from "node:path";
11758
11999
  async function runDoctor(options) {
11759
12000
  if (options.issue) {
11760
12001
  return runIssueFlow(options.argv);
@@ -11767,7 +12008,7 @@ async function runDoctor(options) {
11767
12008
  return runClearFlow(options.argv);
11768
12009
  }
11769
12010
  const adapters = await resolveAdaptersForCommand(options.argv, {
11770
- allowMulti: true,
12011
+ allowMulti: false,
11771
12012
  verb: "diagnose"
11772
12013
  });
11773
12014
  const report = await collectDiagnostics(adapters);
@@ -11798,6 +12039,10 @@ async function runDoctor(options) {
11798
12039
  O2.info(` aft config: ${h.aftConfig.exists ? h.configPaths.aftConfig : "(not set)"}`);
11799
12040
  if (h.aftConfig.parseError) {
11800
12041
  O2.error(` aft config parse error: ${h.aftConfig.parseError}`);
12042
+ } else if (h.aftConfig.exists) {
12043
+ const { value } = readJsoncFile(h.configPaths.aftConfig);
12044
+ const schemaSet = value?.$schema === AFT_SCHEMA_URL;
12045
+ O2.info(` aft config $schema: ${schemaSet ? "set" : "not set — run `aft doctor --fix` for editor autocomplete"}`);
11801
12046
  }
11802
12047
  O2.info(` storage: ${formatDoctorStorageStatus(h)}`);
11803
12048
  if (h.onnxRuntime.required) {
@@ -11904,7 +12149,7 @@ function clearOldBinaries() {
11904
12149
  errors: [],
11905
12150
  keptVersion: keepTag
11906
12151
  };
11907
- if (!existsSync13(info.path)) {
12152
+ if (!existsSync14(info.path)) {
11908
12153
  O2.info(`Binary cache: nothing to clear at ${info.path}`);
11909
12154
  return result;
11910
12155
  }
@@ -11914,10 +12159,10 @@ function clearOldBinaries() {
11914
12159
  return result;
11915
12160
  }
11916
12161
  for (const version of stale) {
11917
- const dir = join11(info.path, version);
12162
+ const dir = join12(info.path, version);
11918
12163
  let bytes = 0;
11919
12164
  try {
11920
- bytes = statSync8(dir).isDirectory() ? dirSize(dir) : 0;
12165
+ bytes = statSync9(dir).isDirectory() ? dirSize(dir) : 0;
11921
12166
  } catch {
11922
12167
  bytes = 0;
11923
12168
  }
@@ -11937,6 +12182,42 @@ function clearOldBinaries() {
11937
12182
  }
11938
12183
  return result;
11939
12184
  }
12185
+ function findSchemaFixTargets(adapters) {
12186
+ const targets = [];
12187
+ for (const adapter of adapters) {
12188
+ if (!adapter.isInstalled())
12189
+ continue;
12190
+ let aftConfig;
12191
+ let aftConfigFormat;
12192
+ try {
12193
+ ({ aftConfig, aftConfigFormat } = adapter.detectConfigPaths());
12194
+ } catch {
12195
+ continue;
12196
+ }
12197
+ const { value } = readJsoncFile(aftConfig);
12198
+ if (value?.$schema === AFT_SCHEMA_URL)
12199
+ continue;
12200
+ targets.push({ adapter, aftConfig, aftConfigFormat });
12201
+ }
12202
+ return targets;
12203
+ }
12204
+ function applySchemaFixes(targets) {
12205
+ let changed = 0;
12206
+ let errors = 0;
12207
+ for (const target of targets) {
12208
+ try {
12209
+ const result = ensureAftSchemaUrl(target.aftConfig, target.aftConfigFormat);
12210
+ if (result.action === "added" || result.action === "updated") {
12211
+ changed += 1;
12212
+ O2.success(`${target.adapter.displayName}: ${result.message}`);
12213
+ }
12214
+ } catch (error) {
12215
+ errors += 1;
12216
+ O2.warn(`${target.adapter.displayName}: could not set $schema on ${target.aftConfig}: ${error instanceof Error ? error.message : String(error)}`);
12217
+ }
12218
+ }
12219
+ return { changed, errors };
12220
+ }
11940
12221
  function buildDoctorFixPlan(adapters, report) {
11941
12222
  const items = [];
11942
12223
  const adaptersByKind = new Map(adapters.map((adapter) => [adapter.kind, adapter]));
@@ -11984,6 +12265,12 @@ function buildDoctorFixPlan(adapters, report) {
11984
12265
  });
11985
12266
  }
11986
12267
  }
12268
+ for (const target of findSchemaFixTargets(adapters)) {
12269
+ items.push({
12270
+ kind: "schema",
12271
+ message: `Will add the AFT config $schema URL to ${target.aftConfig} (editor autocomplete + validation)`
12272
+ });
12273
+ }
11987
12274
  return items;
11988
12275
  }
11989
12276
  function shouldSkipDoctorFixConfirmation(argv) {
@@ -12022,7 +12309,7 @@ function logUnmatchedBinaryCandidates(expectedVersion) {
12022
12309
  }
12023
12310
  async function runFixFlow(argv) {
12024
12311
  const adapters = await resolveAdaptersForCommand(argv, {
12025
- allowMulti: true,
12312
+ allowMulti: false,
12026
12313
  verb: "auto-fix issues for"
12027
12314
  });
12028
12315
  O2.info("Running diagnostics to identify auto-fixable issues…");
@@ -12044,6 +12331,7 @@ async function runFixFlow(argv) {
12044
12331
  }
12045
12332
  await fixPluginEntries(adapters);
12046
12333
  const storageSummary = ensureStorageDirsForRegisteredPlugins(adapters);
12334
+ const schemaSummary = applySchemaFixes(findSchemaFixTargets(adapters));
12047
12335
  let binaryDownloaded = false;
12048
12336
  let binaryDownloadSkipped = false;
12049
12337
  let binaryDownloadError = null;
@@ -12072,7 +12360,7 @@ async function runFixFlow(argv) {
12072
12360
  }
12073
12361
  }
12074
12362
  const onnxResult = await runOnnxFix(adapters, report, { yes: true });
12075
- if (onnxResult === null && !binaryDownloaded && !binaryDownloadSkipped && !binaryDownloadError && storageSummary.created === 0 && storageSummary.errors === 0) {
12363
+ if (onnxResult === null && !binaryDownloaded && !binaryDownloadSkipped && !binaryDownloadError && storageSummary.created === 0 && storageSummary.errors === 0 && schemaSummary.changed === 0 && schemaSummary.errors === 0) {
12076
12364
  O2.info("No auto-fixable issues detected.");
12077
12365
  wt("If you're still seeing 'Semantic Index: failed' in the TUI sidebar, run `aft doctor` (without --fix) for a full diagnostic dump.", "Tip");
12078
12366
  const afterReport2 = await collectDiagnostics(adapters);
@@ -12080,7 +12368,7 @@ async function runFixFlow(argv) {
12080
12368
  gt(stillHasProblems2 ? "Done — some issues remain." : "Done.");
12081
12369
  return stillHasProblems2 ? 1 : 0;
12082
12370
  }
12083
- const hadErrors = (onnxResult?.errors.length ?? 0) > 0 || binaryDownloadError !== null || storageSummary.errors > 0;
12371
+ const hadErrors = (onnxResult?.errors.length ?? 0) > 0 || binaryDownloadError !== null || storageSummary.errors > 0 || schemaSummary.errors > 0;
12084
12372
  const afterReport = await collectDiagnostics(adapters);
12085
12373
  const stillHasProblems = hasDoctorProblems(afterReport);
12086
12374
  gt(hadErrors ? "Done — some fixes failed." : stillHasProblems ? "Done — some issues remain." : "Done.");
@@ -12135,7 +12423,7 @@ function ensureStorageDirsForRegisteredPlugins(adapters) {
12135
12423
  if (!adapter.isInstalled() || !adapter.hasPluginEntry())
12136
12424
  continue;
12137
12425
  const storageDir = adapter.getStorageDir();
12138
- if (existsSync13(storageDir))
12426
+ if (existsSync14(storageDir))
12139
12427
  continue;
12140
12428
  mkdirSync2(storageDir, { recursive: true });
12141
12429
  summary.created += 1;
@@ -12238,11 +12526,11 @@ function deriveIssueTitleFromBody(body) {
12238
12526
  function writeIssueReviewFile(body) {
12239
12527
  let reviewDir = null;
12240
12528
  try {
12241
- reviewDir = mkdtempSync(join11(tmpdir2(), "aft-issue-"));
12529
+ reviewDir = mkdtempSync(join12(tmpdir2(), "aft-issue-"));
12242
12530
  if (process.platform !== "win32") {
12243
12531
  chmodSync(reviewDir, 448);
12244
12532
  }
12245
- const outPath = join11(reviewDir, "issue.md");
12533
+ const outPath = join12(reviewDir, "issue.md");
12246
12534
  writeFileSync2(outPath, `${body}
12247
12535
  `, { encoding: "utf8", mode: 384, flag: "wx" });
12248
12536
  return { path: outPath, realPath: realpathSync3(outPath) };
@@ -12263,12 +12551,33 @@ function readReviewedIssueFile(reviewFile) {
12263
12551
  O2.error(`Review file path changed before filing; refusing to read ${reviewFile.path}.`);
12264
12552
  return null;
12265
12553
  }
12266
- return readFileSync4(reviewFile.path, "utf8");
12554
+ return readFileSync5(reviewFile.path, "utf8");
12267
12555
  } catch (err) {
12268
12556
  O2.error(`Failed to read reviewed issue report: ${err instanceof Error ? err.message : String(err)}`);
12269
12557
  return null;
12270
12558
  }
12271
12559
  }
12560
+ async function promptForIssueSession(adapter) {
12561
+ const sessions = listRecentSessions(adapter);
12562
+ if (sessions.length === 0)
12563
+ return null;
12564
+ const allLogsValue = "__all__";
12565
+ const selected = await selectOne("Is this issue about a specific session?", [
12566
+ { label: "General — not session-specific (include all logs)", value: allLogsValue },
12567
+ ...sessions.map((session) => ({
12568
+ label: truncateTitle(session.title),
12569
+ value: session.id,
12570
+ hint: shortSessionId(session.id)
12571
+ }))
12572
+ ]);
12573
+ if (selected === allLogsValue)
12574
+ return null;
12575
+ return sessions.find((session) => session.id === selected) ?? null;
12576
+ }
12577
+ function shortSessionId(id) {
12578
+ const bareId = id.replace(/^ses_/, "");
12579
+ return bareId.length <= 12 ? bareId : bareId.slice(0, 12);
12580
+ }
12272
12581
  async function runIssueFlow(argv) {
12273
12582
  mt("AFT doctor --issue");
12274
12583
  if (!isInteractiveTerminal()) {
@@ -12277,28 +12586,33 @@ async function runIssueFlow(argv) {
12277
12586
  return 0;
12278
12587
  }
12279
12588
  const adapters = await resolveAdaptersForCommand(argv, {
12280
- allowMulti: true,
12589
+ allowMulti: false,
12281
12590
  verb: "include in the issue"
12282
12591
  });
12283
12592
  const description = await text("Describe the problem you're running into:", {
12284
12593
  placeholder: "What happened? What did you expect? Steps to reproduce…",
12285
12594
  validate: (value) => value.trim().length === 0 ? "Please enter a short description." : undefined
12286
12595
  });
12596
+ const selectedSession = await promptForIssueSession(adapters[0]);
12597
+ const selectedBareSessionId = selectedSession?.id.replace(/^ses_/, "") ?? null;
12287
12598
  const report = await collectDiagnostics(adapters);
12288
12599
  const logSections = adapters.map((adapter) => {
12289
12600
  const path = adapter.getLogFile();
12290
12601
  const tail = tailLogFile(path, 200);
12602
+ const scopedTail = selectedBareSessionId ? filterLogToSession(tail, selectedBareSessionId) : tail;
12291
12603
  return `#### ${adapter.displayName} log (${path})
12292
12604
 
12293
12605
  \`\`\`
12294
- ${tail || "<no log output>"}
12606
+ ${scopedTail || "<no log output>"}
12295
12607
  \`\`\`
12296
12608
  `;
12297
12609
  }).join(`
12298
12610
  `);
12299
12611
  const errorScanWindow = adapters.map((adapter) => {
12300
12612
  const path = adapter.getLogFile();
12301
- return sanitizeContent(tailLogFile(path, 4000));
12613
+ const tail = tailLogFile(path, 4000);
12614
+ const scopedTail = selectedBareSessionId ? filterLogToSession(tail, selectedBareSessionId) : tail;
12615
+ return sanitizeContent(scopedTail);
12302
12616
  }).join(`
12303
12617
  `);
12304
12618
  const recentErrorLines = extractRecentErrors(errorScanWindow, 20);
@@ -12314,6 +12628,7 @@ ${tail || "<no log output>"}
12314
12628
  `- AFT binary: ${report.binaryVersion ?? "unknown"}`,
12315
12629
  `- OS: ${report.platform} ${report.arch}`,
12316
12630
  `- Node: ${report.nodeVersion}`,
12631
+ ...selectedSession ? [`- Session: ses_${selectedBareSessionId} (${truncateTitle(selectedSession.title)})`] : [],
12317
12632
  "",
12318
12633
  "## Diagnostics",
12319
12634
  renderDiagnosticsMarkdown(report),
@@ -12378,11 +12693,13 @@ var init_doctor = __esm(async () => {
12378
12693
  init_github();
12379
12694
  init_harness_select();
12380
12695
  init_issue_body();
12696
+ init_jsonc();
12381
12697
  init_lsp_cache();
12382
12698
  init_onnx_fix();
12383
12699
  init_prompts();
12384
12700
  init_sanitize();
12385
12701
  init_self_version();
12702
+ init_sessions();
12386
12703
  await __promiseAll([
12387
12704
  init_binary_probe(),
12388
12705
  init_diagnostics()
@@ -12413,6 +12730,7 @@ function printHelp() {
12413
12730
  console.log(" -------");
12414
12731
  console.log("");
12415
12732
  console.log(" Commands:");
12733
+ console.log(" --version Show CLI, binary, and per-harness plugin versions");
12416
12734
  console.log(" setup Interactive setup wizard");
12417
12735
  console.log(" doctor Check and fix configuration issues");
12418
12736
  console.log(" doctor lsp <file> Inspect LSP setup for one file");
@@ -12434,6 +12752,10 @@ function printHelp() {
12434
12752
  console.log("");
12435
12753
  }
12436
12754
  async function main() {
12755
+ if (command === "--version" || command === "-v" || command === "-V" || command === "version") {
12756
+ const { runVersion: runVersion2 } = await init_version().then(() => exports_version);
12757
+ return runVersion2();
12758
+ }
12437
12759
  if (command === "setup") {
12438
12760
  const { runSetup: runSetup2 } = await Promise.resolve().then(() => (init_setup(), exports_setup));
12439
12761
  return runSetup2(args);