@cortexkit/aft-opencode 0.22.1 → 0.24.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/tui.js CHANGED
@@ -1,8 +1,10 @@
1
1
  // @bun
2
+ // src/tui/index.tsx
3
+ import { createMemo as createMemo2, createSignal as createSignal2, onCleanup as onCleanup2 } from "solid-js";
2
4
  // package.json
3
5
  var package_default = {
4
6
  name: "@cortexkit/aft-opencode",
5
- version: "0.22.1",
7
+ version: "0.24.0",
6
8
  type: "module",
7
9
  description: "OpenCode plugin for Agent File Tools (AFT) \u2014 tree-sitter and lsp powered code analysis",
8
10
  main: "dist/index.js",
@@ -30,7 +32,7 @@ var package_default = {
30
32
  },
31
33
  dependencies: {
32
34
  "@clack/prompts": "^1.2.0",
33
- "@cortexkit/aft-bridge": "0.22.1",
35
+ "@cortexkit/aft-bridge": "0.24.0",
34
36
  "@opencode-ai/plugin": "^1.14.39",
35
37
  "@opencode-ai/sdk": "^1.14.39",
36
38
  "comment-json": "^4.6.2",
@@ -38,11 +40,11 @@ var package_default = {
38
40
  zod: "^4.1.8"
39
41
  },
40
42
  optionalDependencies: {
41
- "@cortexkit/aft-darwin-arm64": "0.22.1",
42
- "@cortexkit/aft-darwin-x64": "0.22.1",
43
- "@cortexkit/aft-linux-arm64": "0.22.1",
44
- "@cortexkit/aft-linux-x64": "0.22.1",
45
- "@cortexkit/aft-win32-x64": "0.22.1"
43
+ "@cortexkit/aft-darwin-arm64": "0.24.0",
44
+ "@cortexkit/aft-darwin-x64": "0.24.0",
45
+ "@cortexkit/aft-linux-arm64": "0.24.0",
46
+ "@cortexkit/aft-linux-x64": "0.24.0",
47
+ "@cortexkit/aft-win32-x64": "0.24.0"
46
48
  },
47
49
  devDependencies: {
48
50
  "@types/node": "^22.0.0",
@@ -303,12 +305,6 @@ function readNumber(value, fallback = 0) {
303
305
  function readOptionalNumber(value) {
304
306
  return typeof value === "number" && Number.isFinite(value) ? value : null;
305
307
  }
306
- function formatFlag(enabled) {
307
- return enabled ? "enabled" : "disabled";
308
- }
309
- function formatCount(value) {
310
- return value == null ? "\u2014" : value.toLocaleString("en-US");
311
- }
312
308
  function formatBytes(bytes) {
313
309
  if (!Number.isFinite(bytes) || bytes <= 0)
314
310
  return "0 B";
@@ -336,6 +332,8 @@ function coerceAftStatus(response) {
336
332
  return {
337
333
  version: readString(response.version, "unknown"),
338
334
  project_root: readNullableString(response.project_root),
335
+ canonical_root: readNullableString(response.canonical_root),
336
+ cache_role: readString(response.cache_role, "not_initialized"),
339
337
  features: {
340
338
  format_on_edit: readBoolean(features.format_on_edit),
341
339
  validate_on_edit: readString(features.validate_on_edit, "off"),
@@ -379,54 +377,6 @@ function coerceAftStatus(response) {
379
377
  }
380
378
  };
381
379
  }
382
- function formatStatusDialogMessage(status) {
383
- const lines = [
384
- `AFT version: ${status.version}`,
385
- `Project root: ${status.project_root ?? "(not configured)"}`,
386
- "",
387
- "Enabled features",
388
- `- format_on_edit: ${formatFlag(status.features.format_on_edit)}`,
389
- `- search_index: ${formatFlag(status.features.search_index)}`,
390
- `- semantic_search: ${formatFlag(status.features.semantic_search)}`,
391
- "",
392
- "Search index",
393
- `- status: ${status.search_index.status}`,
394
- `- files: ${formatCount(status.search_index.files)}`,
395
- `- trigrams: ${formatCount(status.search_index.trigrams)}`,
396
- "",
397
- "Semantic index",
398
- `- status: ${status.semantic_index.status}`,
399
- `- entries: ${formatCount(status.semantic_index.entries)}`
400
- ];
401
- if (status.semantic_index.backend) {
402
- lines.push(`- backend: ${status.semantic_index.backend}`);
403
- }
404
- if (status.semantic_index.model) {
405
- lines.push(`- model: ${status.semantic_index.model}`);
406
- }
407
- if (status.semantic_index.dimension != null) {
408
- lines.push(`- dimension: ${formatCount(status.semantic_index.dimension)}`);
409
- }
410
- 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`);
411
- if (status.storage_dir ?? status.disk.storage_dir) {
412
- lines.push(`- storage dir: ${status.storage_dir ?? status.disk.storage_dir}`);
413
- }
414
- lines.push("", "Current session", `- id: ${status.session.id}`, `- tracked files: ${formatCount(status.session.tracked_files)}`, `- checkpoints: ${formatCount(status.session.checkpoints)}`, `- project checkpoints (all sessions): ${formatCount(status.checkpoints_total)}`);
415
- if (status.semantic_index.stage) {
416
- lines.push("", "Semantic stage", status.semantic_index.stage);
417
- }
418
- if (status.semantic_index.files != null) {
419
- lines.push(`- semantic files: ${formatCount(status.semantic_index.files)}`);
420
- }
421
- if (status.semantic_index.entries_done != null || status.semantic_index.entries_total != null) {
422
- lines.push(`- semantic progress: ${formatCount(status.semantic_index.entries_done ?? null)} / ${formatCount(status.semantic_index.entries_total ?? null)}`);
423
- }
424
- if (status.semantic_index.error) {
425
- lines.push("", "Semantic error", status.semantic_index.error);
426
- }
427
- return lines.join(`
428
- `);
429
- }
430
380
 
431
381
  // src/tui/sidebar.tsx
432
382
  import { createEffect, createMemo, createSignal, on, onCleanup } from "solid-js";
@@ -445,7 +395,7 @@ function formatBytes2(n) {
445
395
  return `${Math.round(n / 1024)} KB`;
446
396
  return `${n} B`;
447
397
  }
448
- function formatCount2(n) {
398
+ function formatCount(n) {
449
399
  if (n == null || !Number.isFinite(n))
450
400
  return "\u2014";
451
401
  if (n >= 1e6)
@@ -658,7 +608,7 @@ var SidebarContent = (props) => {
658
608
  (s()?.search_index?.files ?? null) != null && /* @__PURE__ */ jsxDEV(StatRow, {
659
609
  theme: props.theme,
660
610
  label: "Files",
661
- value: formatCount2(s().search_index.files),
611
+ value: formatCount(s().search_index.files),
662
612
  tone: "muted"
663
613
  }, undefined, false, undefined, this),
664
614
  /* @__PURE__ */ jsxDEV(StatRow, {
@@ -680,13 +630,13 @@ var SidebarContent = (props) => {
680
630
  s()?.semantic_index?.status === "loading" && s()?.semantic_index?.entries_total != null && s().semantic_index.entries_total > 0 && /* @__PURE__ */ jsxDEV(StatRow, {
681
631
  theme: props.theme,
682
632
  label: "Progress",
683
- value: `${formatCount2(s().semantic_index.entries_done)} / ${formatCount2(s().semantic_index.entries_total)}`,
633
+ value: `${formatCount(s().semantic_index.entries_done)} / ${formatCount(s().semantic_index.entries_total)}`,
684
634
  tone: "warn"
685
635
  }, undefined, false, undefined, this),
686
636
  (s()?.semantic_index?.entries ?? null) != null && /* @__PURE__ */ jsxDEV(StatRow, {
687
637
  theme: props.theme,
688
638
  label: "Entries",
689
- value: formatCount2(s().semantic_index.entries),
639
+ value: formatCount(s().semantic_index.entries),
690
640
  tone: "muted"
691
641
  }, undefined, false, undefined, this),
692
642
  /* @__PURE__ */ jsxDEV(StatRow, {
@@ -750,6 +700,361 @@ function getSessionId(api) {
750
700
  } catch {}
751
701
  return null;
752
702
  }
703
+ var POLL_INTERVAL_MS2 = 1500;
704
+ function formatCountShort(value) {
705
+ if (value == null || !Number.isFinite(value))
706
+ return "\u2014";
707
+ if (value >= 1e6)
708
+ return `${(value / 1e6).toFixed(1)}M`;
709
+ if (value >= 1000)
710
+ return `${Math.round(value / 1000)}K`;
711
+ return String(value);
712
+ }
713
+ function statusTone(status) {
714
+ switch (status) {
715
+ case "ready":
716
+ return "ok";
717
+ case "loading":
718
+ case "building":
719
+ return "warn";
720
+ case "failed":
721
+ case "error":
722
+ return "err";
723
+ default:
724
+ return "muted";
725
+ }
726
+ }
727
+ function pickToneColor(theme, tone) {
728
+ switch (tone) {
729
+ case "ok":
730
+ return theme.success ?? theme.accent;
731
+ case "warn":
732
+ return theme.warning;
733
+ case "err":
734
+ return theme.error;
735
+ case "muted":
736
+ return theme.textMuted;
737
+ }
738
+ }
739
+ var R = (props) => {
740
+ const fg = createMemo2(() => {
741
+ if (!props.tone)
742
+ return props.theme.text;
743
+ if (props.tone === "accent")
744
+ return props.theme.accent;
745
+ return pickToneColor(props.theme, props.tone);
746
+ });
747
+ return /* @__PURE__ */ jsxDEV2("box", {
748
+ flexDirection: "row",
749
+ width: "100%",
750
+ justifyContent: "space-between",
751
+ children: [
752
+ /* @__PURE__ */ jsxDEV2("text", {
753
+ fg: props.theme.textMuted,
754
+ children: props.label
755
+ }, undefined, false, undefined, this),
756
+ /* @__PURE__ */ jsxDEV2("text", {
757
+ fg: fg(),
758
+ children: props.value
759
+ }, undefined, false, undefined, this)
760
+ ]
761
+ }, undefined, true, undefined, this);
762
+ };
763
+ var StatusDialog = (props) => {
764
+ const theme = createMemo2(() => props.api.theme.current);
765
+ const t = () => theme();
766
+ const [status, setStatus] = createSignal2(props.initial);
767
+ const [error, setError] = createSignal2(props.initialError);
768
+ const timer = setInterval(async () => {
769
+ try {
770
+ const response = await props.client.call("status", { sessionID: props.sessionID });
771
+ if (response.success !== false) {
772
+ setStatus(coerceAftStatus(response));
773
+ setError(null);
774
+ }
775
+ } catch {}
776
+ }, POLL_INTERVAL_MS2);
777
+ onCleanup2(() => clearInterval(timer));
778
+ const cacheRoleTone = (role) => role === "main" ? "accent" : role === "worktree" ? "warn" : "muted";
779
+ return /* @__PURE__ */ jsxDEV2("box", {
780
+ flexDirection: "column",
781
+ width: "100%",
782
+ paddingLeft: 2,
783
+ paddingRight: 2,
784
+ paddingTop: 1,
785
+ paddingBottom: 1,
786
+ children: [
787
+ /* @__PURE__ */ jsxDEV2("box", {
788
+ justifyContent: "center",
789
+ width: "100%",
790
+ marginBottom: 1,
791
+ flexDirection: "row",
792
+ gap: 2,
793
+ children: [
794
+ /* @__PURE__ */ jsxDEV2("text", {
795
+ fg: t().accent,
796
+ children: /* @__PURE__ */ jsxDEV2("b", {
797
+ children: "\u26A1 AFT Status"
798
+ }, undefined, false, undefined, this)
799
+ }, undefined, false, undefined, this),
800
+ /* @__PURE__ */ jsxDEV2("text", {
801
+ fg: t().textMuted,
802
+ children: [
803
+ "v",
804
+ status()?.version ?? package_default.version
805
+ ]
806
+ }, undefined, true, undefined, this)
807
+ ]
808
+ }, undefined, true, undefined, this),
809
+ error() ? /* @__PURE__ */ jsxDEV2("box", {
810
+ width: "100%",
811
+ marginBottom: 1,
812
+ children: /* @__PURE__ */ jsxDEV2("text", {
813
+ fg: t().warning,
814
+ children: error()
815
+ }, undefined, false, undefined, this)
816
+ }, undefined, false, undefined, this) : null,
817
+ status() ? /* @__PURE__ */ jsxDEV2("box", {
818
+ flexDirection: "column",
819
+ width: "100%",
820
+ marginBottom: 1,
821
+ children: [
822
+ /* @__PURE__ */ jsxDEV2(R, {
823
+ theme: t(),
824
+ label: "Project root",
825
+ value: status().project_root ?? "(not configured)"
826
+ }, undefined, false, undefined, this),
827
+ /* @__PURE__ */ jsxDEV2(R, {
828
+ theme: t(),
829
+ label: "Canonical root",
830
+ value: status().canonical_root ?? "(not configured)"
831
+ }, undefined, false, undefined, this),
832
+ /* @__PURE__ */ jsxDEV2(R, {
833
+ theme: t(),
834
+ label: "Cache role",
835
+ value: status().cache_role,
836
+ tone: cacheRoleTone(status().cache_role)
837
+ }, undefined, false, undefined, this)
838
+ ]
839
+ }, undefined, true, undefined, this) : null,
840
+ status() ? /* @__PURE__ */ jsxDEV2("box", {
841
+ flexDirection: "row",
842
+ width: "100%",
843
+ gap: 4,
844
+ children: [
845
+ /* @__PURE__ */ jsxDEV2("box", {
846
+ flexDirection: "column",
847
+ flexGrow: 1,
848
+ flexBasis: 0,
849
+ children: [
850
+ /* @__PURE__ */ jsxDEV2("text", {
851
+ fg: t().text,
852
+ children: /* @__PURE__ */ jsxDEV2("b", {
853
+ children: "Search index"
854
+ }, undefined, false, undefined, this)
855
+ }, undefined, false, undefined, this),
856
+ /* @__PURE__ */ jsxDEV2(R, {
857
+ theme: t(),
858
+ label: "Status",
859
+ value: status().search_index.status,
860
+ tone: statusTone(status().search_index.status)
861
+ }, undefined, false, undefined, this),
862
+ /* @__PURE__ */ jsxDEV2(R, {
863
+ theme: t(),
864
+ label: "Files",
865
+ value: formatCountShort(status().search_index.files)
866
+ }, undefined, false, undefined, this),
867
+ /* @__PURE__ */ jsxDEV2(R, {
868
+ theme: t(),
869
+ label: "Trigrams",
870
+ value: formatCountShort(status().search_index.trigrams)
871
+ }, undefined, false, undefined, this),
872
+ /* @__PURE__ */ jsxDEV2(R, {
873
+ theme: t(),
874
+ label: "Disk",
875
+ value: formatBytes(status().disk.trigram_disk_bytes),
876
+ tone: "muted"
877
+ }, undefined, false, undefined, this),
878
+ /* @__PURE__ */ jsxDEV2("box", {
879
+ marginTop: 1,
880
+ children: /* @__PURE__ */ jsxDEV2("text", {
881
+ fg: t().text,
882
+ children: /* @__PURE__ */ jsxDEV2("b", {
883
+ children: "Runtime"
884
+ }, undefined, false, undefined, this)
885
+ }, undefined, false, undefined, this)
886
+ }, undefined, false, undefined, this),
887
+ /* @__PURE__ */ jsxDEV2(R, {
888
+ theme: t(),
889
+ label: "LSP servers",
890
+ value: String(status().lsp_servers)
891
+ }, undefined, false, undefined, this),
892
+ /* @__PURE__ */ jsxDEV2(R, {
893
+ theme: t(),
894
+ label: "Symbol cache (local)",
895
+ value: formatCountShort(status().symbol_cache.local_entries)
896
+ }, undefined, false, undefined, this),
897
+ /* @__PURE__ */ jsxDEV2(R, {
898
+ theme: t(),
899
+ label: "Symbol cache (warm)",
900
+ value: formatCountShort(status().symbol_cache.warm_entries),
901
+ tone: "muted"
902
+ }, undefined, false, undefined, this),
903
+ /* @__PURE__ */ jsxDEV2("box", {
904
+ marginTop: 1,
905
+ children: /* @__PURE__ */ jsxDEV2("text", {
906
+ fg: t().text,
907
+ children: /* @__PURE__ */ jsxDEV2("b", {
908
+ children: "Features"
909
+ }, undefined, false, undefined, this)
910
+ }, undefined, false, undefined, this)
911
+ }, undefined, false, undefined, this),
912
+ /* @__PURE__ */ jsxDEV2(R, {
913
+ theme: t(),
914
+ label: "format_on_edit",
915
+ value: status().features.format_on_edit ? "on" : "off",
916
+ tone: status().features.format_on_edit ? "ok" : "muted"
917
+ }, undefined, false, undefined, this),
918
+ /* @__PURE__ */ jsxDEV2(R, {
919
+ theme: t(),
920
+ label: "search_index",
921
+ value: status().features.search_index ? "on" : "off",
922
+ tone: status().features.search_index ? "ok" : "muted"
923
+ }, undefined, false, undefined, this),
924
+ /* @__PURE__ */ jsxDEV2(R, {
925
+ theme: t(),
926
+ label: "semantic_search",
927
+ value: status().features.semantic_search ? "on" : "off",
928
+ tone: status().features.semantic_search ? "ok" : "muted"
929
+ }, undefined, false, undefined, this)
930
+ ]
931
+ }, undefined, true, undefined, this),
932
+ /* @__PURE__ */ jsxDEV2("box", {
933
+ flexDirection: "column",
934
+ flexGrow: 1,
935
+ flexBasis: 0,
936
+ children: [
937
+ /* @__PURE__ */ jsxDEV2("text", {
938
+ fg: t().text,
939
+ children: /* @__PURE__ */ jsxDEV2("b", {
940
+ children: "Semantic index"
941
+ }, undefined, false, undefined, this)
942
+ }, undefined, false, undefined, this),
943
+ /* @__PURE__ */ jsxDEV2(R, {
944
+ theme: t(),
945
+ label: "Status",
946
+ value: status().semantic_index.status,
947
+ tone: statusTone(status().semantic_index.status)
948
+ }, undefined, false, undefined, this),
949
+ /* @__PURE__ */ jsxDEV2(R, {
950
+ theme: t(),
951
+ label: "Entries",
952
+ value: formatCountShort(status().semantic_index.entries)
953
+ }, undefined, false, undefined, this),
954
+ status().semantic_index.backend ? /* @__PURE__ */ jsxDEV2(R, {
955
+ theme: t(),
956
+ label: "Backend",
957
+ value: status().semantic_index.backend,
958
+ tone: "muted"
959
+ }, undefined, false, undefined, this) : null,
960
+ status().semantic_index.model ? /* @__PURE__ */ jsxDEV2(R, {
961
+ theme: t(),
962
+ label: "Model",
963
+ value: status().semantic_index.model,
964
+ tone: "muted"
965
+ }, undefined, false, undefined, this) : null,
966
+ status().semantic_index.dimension != null ? /* @__PURE__ */ jsxDEV2(R, {
967
+ theme: t(),
968
+ label: "Dimension",
969
+ value: String(status().semantic_index.dimension),
970
+ tone: "muted"
971
+ }, undefined, false, undefined, this) : null,
972
+ /* @__PURE__ */ jsxDEV2(R, {
973
+ theme: t(),
974
+ label: "Disk",
975
+ value: formatBytes(status().disk.semantic_disk_bytes),
976
+ tone: "muted"
977
+ }, undefined, false, undefined, this),
978
+ /* @__PURE__ */ jsxDEV2("box", {
979
+ marginTop: 1,
980
+ children: /* @__PURE__ */ jsxDEV2("text", {
981
+ fg: t().text,
982
+ children: /* @__PURE__ */ jsxDEV2("b", {
983
+ children: "Current session"
984
+ }, undefined, false, undefined, this)
985
+ }, undefined, false, undefined, this)
986
+ }, undefined, false, undefined, this),
987
+ /* @__PURE__ */ jsxDEV2(R, {
988
+ theme: t(),
989
+ label: "Tracked files",
990
+ value: String(status().session.tracked_files)
991
+ }, undefined, false, undefined, this),
992
+ /* @__PURE__ */ jsxDEV2(R, {
993
+ theme: t(),
994
+ label: "Checkpoints",
995
+ value: String(status().session.checkpoints)
996
+ }, undefined, false, undefined, this),
997
+ /* @__PURE__ */ jsxDEV2(R, {
998
+ theme: t(),
999
+ label: "All-session checkpoints",
1000
+ value: String(status().checkpoints_total),
1001
+ tone: "muted"
1002
+ }, undefined, false, undefined, this)
1003
+ ]
1004
+ }, undefined, true, undefined, this)
1005
+ ]
1006
+ }, undefined, true, undefined, this) : null,
1007
+ status()?.semantic_index.stage ? /* @__PURE__ */ jsxDEV2("box", {
1008
+ flexDirection: "column",
1009
+ width: "100%",
1010
+ marginTop: 1,
1011
+ children: [
1012
+ /* @__PURE__ */ jsxDEV2("text", {
1013
+ fg: t().text,
1014
+ children: /* @__PURE__ */ jsxDEV2("b", {
1015
+ children: "Semantic build progress"
1016
+ }, undefined, false, undefined, this)
1017
+ }, undefined, false, undefined, this),
1018
+ /* @__PURE__ */ jsxDEV2(R, {
1019
+ theme: t(),
1020
+ label: "Stage",
1021
+ value: status().semantic_index.stage
1022
+ }, undefined, false, undefined, this),
1023
+ status().semantic_index.files != null ? /* @__PURE__ */ jsxDEV2(R, {
1024
+ theme: t(),
1025
+ label: "Files seen",
1026
+ value: formatCountShort(status().semantic_index.files)
1027
+ }, undefined, false, undefined, this) : null,
1028
+ status().semantic_index.entries_done != null || status().semantic_index.entries_total != null ? /* @__PURE__ */ jsxDEV2(R, {
1029
+ theme: t(),
1030
+ label: "Progress",
1031
+ value: `${formatCountShort(status().semantic_index.entries_done ?? null)} / ${formatCountShort(status().semantic_index.entries_total ?? null)}`
1032
+ }, undefined, false, undefined, this) : null
1033
+ ]
1034
+ }, undefined, true, undefined, this) : null,
1035
+ status()?.semantic_index.error ? /* @__PURE__ */ jsxDEV2("box", {
1036
+ marginTop: 1,
1037
+ width: "100%",
1038
+ children: /* @__PURE__ */ jsxDEV2("text", {
1039
+ fg: t().error,
1040
+ children: [
1041
+ "\u26A0 ",
1042
+ status().semantic_index.error
1043
+ ]
1044
+ }, undefined, true, undefined, this)
1045
+ }, undefined, false, undefined, this) : null,
1046
+ /* @__PURE__ */ jsxDEV2("box", {
1047
+ marginTop: 1,
1048
+ justifyContent: "flex-end",
1049
+ width: "100%",
1050
+ children: /* @__PURE__ */ jsxDEV2("text", {
1051
+ fg: t().textMuted,
1052
+ children: "Enter or Esc to close"
1053
+ }, undefined, false, undefined, this)
1054
+ }, undefined, false, undefined, this)
1055
+ ]
1056
+ }, undefined, true, undefined, this);
1057
+ };
753
1058
  async function showStatusDialog(api) {
754
1059
  const sessionID = getSessionId(api);
755
1060
  if (!sessionID) {
@@ -762,68 +1067,29 @@ async function showStatusDialog(api) {
762
1067
  return;
763
1068
  }
764
1069
  const client = getRpcClient(directory);
765
- let currentMessage = "Connecting to AFT...";
1070
+ let initial = null;
1071
+ let initialError = null;
766
1072
  try {
767
1073
  const response = await client.call("status", { sessionID });
768
1074
  if (response.success !== false) {
769
- const status = coerceAftStatus(response);
770
- currentMessage = formatStatusDialogMessage(status);
1075
+ initial = coerceAftStatus(response);
1076
+ } else {
1077
+ initialError = "AFT bridge returned an error response.";
771
1078
  }
772
1079
  } catch {
773
- currentMessage = "AFT is starting up. Status will refresh automatically...";
1080
+ initialError = "AFT is starting up. Status will refresh automatically...";
774
1081
  }
775
- let dialogOpen = true;
776
- let pollTimer = null;
777
1082
  api.ui.dialog.setSize("large");
778
- api.ui.dialog.replace(() => {
779
- if (!pollTimer) {
780
- pollTimer = setInterval(async () => {
781
- if (!dialogOpen) {
782
- if (pollTimer)
783
- clearInterval(pollTimer);
784
- return;
785
- }
786
- try {
787
- const response = await client.call("status", { sessionID });
788
- if (response.success !== false) {
789
- const status = coerceAftStatus(response);
790
- const newMessage = formatStatusDialogMessage(status);
791
- if (newMessage !== currentMessage) {
792
- currentMessage = newMessage;
793
- api.ui.dialog.replace(() => /* @__PURE__ */ jsxDEV2(api.ui.DialogAlert, {
794
- title: "AFT Status",
795
- message: currentMessage,
796
- onConfirm: () => {
797
- dialogOpen = false;
798
- if (pollTimer)
799
- clearInterval(pollTimer);
800
- api.ui.dialog.setSize("medium");
801
- }
802
- }, undefined, false, undefined, this), () => {
803
- dialogOpen = false;
804
- if (pollTimer)
805
- clearInterval(pollTimer);
806
- api.ui.dialog.setSize("medium");
807
- });
808
- }
809
- }
810
- } catch {}
811
- }, 1500);
1083
+ api.ui.dialog.replace(() => /* @__PURE__ */ jsxDEV2(StatusDialog, {
1084
+ api,
1085
+ client,
1086
+ sessionID,
1087
+ initial,
1088
+ initialError,
1089
+ onClose: () => {
1090
+ api.ui.dialog.setSize("medium");
812
1091
  }
813
- return /* @__PURE__ */ jsxDEV2(api.ui.DialogAlert, {
814
- title: "AFT Status",
815
- message: currentMessage,
816
- onConfirm: () => {
817
- dialogOpen = false;
818
- if (pollTimer)
819
- clearInterval(pollTimer);
820
- api.ui.dialog.setSize("medium");
821
- }
822
- }, undefined, false, undefined, this);
823
- }, () => {
824
- dialogOpen = false;
825
- if (pollTimer)
826
- clearInterval(pollTimer);
1092
+ }, undefined, false, undefined, this), () => {
827
1093
  api.ui.dialog.setSize("medium");
828
1094
  });
829
1095
  }
@@ -1 +1 @@
1
- {"version":3,"file":"workflow-hints.d.ts","sourceRoot":"","sources":["../src/workflow-hints.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,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,CAsEzE;AAED;;;GAGG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,SAAS,EAAE,aAAa,EAAE,GAAG,CAAC,MAAM,CAAC,GAAG,MAAM,GAAG,IAAI,CAQjG"}
1
+ {"version":3,"file":"workflow-hints.d.ts","sourceRoot":"","sources":["../src/workflow-hints.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,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,CAmEzE;AAED;;;GAGG;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.22.1",
3
+ "version": "0.24.0",
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",
@@ -28,7 +28,7 @@
28
28
  },
29
29
  "dependencies": {
30
30
  "@clack/prompts": "^1.2.0",
31
- "@cortexkit/aft-bridge": "0.22.1",
31
+ "@cortexkit/aft-bridge": "0.24.0",
32
32
  "@opencode-ai/plugin": "^1.14.39",
33
33
  "@opencode-ai/sdk": "^1.14.39",
34
34
  "comment-json": "^4.6.2",
@@ -36,11 +36,11 @@
36
36
  "zod": "^4.1.8"
37
37
  },
38
38
  "optionalDependencies": {
39
- "@cortexkit/aft-darwin-arm64": "0.22.1",
40
- "@cortexkit/aft-darwin-x64": "0.22.1",
41
- "@cortexkit/aft-linux-arm64": "0.22.1",
42
- "@cortexkit/aft-linux-x64": "0.22.1",
43
- "@cortexkit/aft-win32-x64": "0.22.1"
39
+ "@cortexkit/aft-darwin-arm64": "0.24.0",
40
+ "@cortexkit/aft-darwin-x64": "0.24.0",
41
+ "@cortexkit/aft-linux-arm64": "0.24.0",
42
+ "@cortexkit/aft-linux-x64": "0.24.0",
43
+ "@cortexkit/aft-win32-x64": "0.24.0"
44
44
  },
45
45
  "devDependencies": {
46
46
  "@types/node": "^22.0.0",
@@ -1,6 +1,8 @@
1
1
  export interface AftStatusSnapshot {
2
2
  version: string;
3
3
  project_root: string | null;
4
+ canonical_root: string | null;
5
+ cache_role: string;
4
6
  features: {
5
7
  format_on_edit: boolean;
6
8
  validate_on_edit: string;
@@ -108,6 +110,8 @@ export function coerceAftStatus(response: Record<string, unknown>): AftStatusSna
108
110
  return {
109
111
  version: readString(response.version, "unknown"),
110
112
  project_root: readNullableString(response.project_root),
113
+ canonical_root: readNullableString(response.canonical_root),
114
+ cache_role: readString(response.cache_role, "not_initialized"),
111
115
  features: {
112
116
  format_on_edit: readBoolean(features.format_on_edit),
113
117
  validate_on_edit: readString(features.validate_on_edit, "off"),
@@ -154,10 +158,19 @@ export function coerceAftStatus(response: Record<string, unknown>): AftStatusSna
154
158
  };
155
159
  }
156
160
 
161
+ /**
162
+ * Plain-text status renderer used by the Desktop `sendIgnoredMessage` path,
163
+ * which can only show a plain string. The TUI dialog uses a custom JSX
164
+ * component in `tui/index.tsx` (see `StatusDialog`) so it can render with
165
+ * themed colors, proper flex columns, and right-aligned values instead of
166
+ * monospace padding.
167
+ */
157
168
  export function formatStatusDialogMessage(status: AftStatusSnapshot): string {
158
169
  const lines = [
159
170
  `AFT version: ${status.version}`,
160
171
  `Project root: ${status.project_root ?? "(not configured)"}`,
172
+ `Canonical root: ${status.canonical_root ?? "(not configured)"}`,
173
+ `Cache role: ${status.cache_role}`,
161
174
  "",
162
175
  "Enabled features",
163
176
  `- format_on_edit: ${formatFlag(status.features.format_on_edit)}`,
@@ -179,7 +192,6 @@ export function formatStatusDialogMessage(status: AftStatusSnapshot): string {
179
192
  if (status.semantic_index.model) {
180
193
  lines.push(`- model: ${status.semantic_index.model}`);
181
194
  }
182
-
183
195
  if (status.semantic_index.dimension != null) {
184
196
  lines.push(`- dimension: ${formatCount(status.semantic_index.dimension)}`);
185
197
  }
@@ -232,6 +244,8 @@ export function formatStatusMarkdown(status: AftStatusSnapshot): string {
232
244
  "",
233
245
  `- **Version:** \`${status.version}\``,
234
246
  `- **Project root:** \`${status.project_root ?? "(not configured)"}\``,
247
+ `- **Canonical root:** \`${status.canonical_root ?? "(not configured)"}\``,
248
+ `- **Cache role:** \`${status.cache_role}\``,
235
249
  "",
236
250
  "### Enabled features",
237
251
  `- \`format_on_edit\`: ${formatFlag(status.features.format_on_edit)}`,