@cortexkit/aft-opencode 0.63.5 → 0.65.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/bg-notifications.d.ts.map +1 -1
- package/dist/config-live-reload.d.ts +4 -28
- package/dist/config-live-reload.d.ts.map +1 -1
- package/dist/config.d.ts +11 -40
- package/dist/config.d.ts.map +1 -1
- package/dist/entry/server.js +1726 -674
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1726 -674
- package/dist/lsp-auto-install.d.ts.map +1 -1
- package/dist/shared/session-directory.d.ts +11 -4
- package/dist/shared/session-directory.d.ts.map +1 -1
- package/dist/shared/status.d.ts +2 -6
- package/dist/shared/status.d.ts.map +1 -1
- package/dist/subc-tool-schemas.d.ts +4 -3
- package/dist/subc-tool-schemas.d.ts.map +1 -1
- package/dist/tool-registration.d.ts +1 -1
- package/dist/tool-registration.d.ts.map +1 -1
- package/dist/tools/_shared.d.ts +11 -17
- package/dist/tools/_shared.d.ts.map +1 -1
- package/dist/tools/bash.d.ts.map +1 -1
- package/dist/tools/bash_watch.d.ts +6 -3
- package/dist/tools/bash_watch.d.ts.map +1 -1
- package/dist/tools/definitions/v2.d.ts +26 -1
- package/dist/tools/definitions/v2.d.ts.map +1 -1
- package/dist/tools/inspect.d.ts +0 -15
- package/dist/tools/inspect.d.ts.map +1 -1
- package/dist/tui/preferences.d.ts +1 -1
- package/dist/tui/shared-sidebar.d.ts +4 -0
- package/dist/tui/shared-sidebar.d.ts.map +1 -0
- package/dist/tui/v2-status.d.ts +3 -2
- package/dist/tui/v2-status.d.ts.map +1 -1
- package/dist/v2-session-lifecycle.d.ts.map +1 -1
- package/dist/workflow-hints.d.ts +1 -5
- package/dist/workflow-hints.d.ts.map +1 -1
- package/package.json +14 -12
- package/src/shared/session-directory.ts +90 -17
- package/src/shared/status.ts +6 -26
- package/src/tui/index.tsx +8 -28
- package/src/tui/preferences.ts +1 -1
- package/src/tui/shared-sidebar.ts +14 -0
- package/src/tui/sidebar-view.tsx +257 -283
- package/src/tui/v2-status.ts +5 -14
- package/src/tui-compiled/index.tsx +6 -44
- package/src/tui-compiled/preferences.ts +1 -1
- package/src/tui-compiled/shared-sidebar-host.tsx +816 -0
- package/src/tui-compiled/shared-sidebar-local.tsx +816 -0
- package/src/tui-compiled/shared-sidebar.LICENSE +21 -0
- package/src/tui-compiled/shared-sidebar.ts +14 -0
- package/src/tui-compiled/sidebar-view.tsx +185 -261
- package/src/tui-compiled/sidebar.tsx +1 -0
- package/src/tui-compiled/v2-status.ts +5 -14
- package/src/tui-compiled/v2.tsx +1 -0
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
*
|
|
13
13
|
* The session itself stores the correct directory in OpenCode's SQLite
|
|
14
14
|
* (`Session.directory` in the SDK). This helper looks it up once per
|
|
15
|
-
* session and caches the result
|
|
16
|
-
*
|
|
15
|
+
* session and caches the result. A session.moved event invalidates the
|
|
16
|
+
* cached root because OpenCode 2 sessions can change Location.
|
|
17
17
|
*
|
|
18
18
|
* The bug should also be fixed upstream in OpenCode's
|
|
19
19
|
* `packages/opencode/src/session/registry.ts`. Until then this
|
|
@@ -50,11 +50,16 @@ interface SessionInfo {
|
|
|
50
50
|
* same test for its own session lookup.
|
|
51
51
|
*/
|
|
52
52
|
interface V2SessionInfo {
|
|
53
|
-
location?: { directory?: unknown };
|
|
53
|
+
location?: { directory?: unknown; workspaceID?: string };
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
interface SessionLocation {
|
|
57
|
+
directory: string;
|
|
58
|
+
workspaceID?: string;
|
|
54
59
|
}
|
|
55
60
|
|
|
56
61
|
interface V2PluginContextShape {
|
|
57
|
-
location:
|
|
62
|
+
location: SessionLocation;
|
|
58
63
|
session: {
|
|
59
64
|
get(input: { sessionID: string }): Effect.Effect<V2SessionInfo | undefined, unknown>;
|
|
60
65
|
};
|
|
@@ -92,6 +97,8 @@ interface OpenCodeClientShape {
|
|
|
92
97
|
interface CacheEntry {
|
|
93
98
|
/** Resolved directory, or `null` while a failed lookup is cooling down. */
|
|
94
99
|
directory: string | null;
|
|
100
|
+
/** V2 Location refs distinguish workspaces that share a directory. */
|
|
101
|
+
location?: SessionLocation;
|
|
95
102
|
/** When the answer was recorded; failed lookups become retryable after 30 seconds. */
|
|
96
103
|
recordedAt: number;
|
|
97
104
|
}
|
|
@@ -104,8 +111,9 @@ const lookupGenerations = new Map<string, object>();
|
|
|
104
111
|
/**
|
|
105
112
|
* Resolve the project directory the session was created with from the SDK's
|
|
106
113
|
* session object. Returns the SDK-reported directory, or `null` when the
|
|
107
|
-
* lookup is unavailable/fails
|
|
108
|
-
* directory
|
|
114
|
+
* lookup is unavailable/fails. Only session-scoped tool calls may fall back
|
|
115
|
+
* to their runtime directory; broadcast events must not open a route without
|
|
116
|
+
* a resolved session directory.
|
|
109
117
|
*
|
|
110
118
|
* This function is best-effort: any error (missing `client.session.get`,
|
|
111
119
|
* network failure, malformed response) is logged and recorded as a
|
|
@@ -139,6 +147,55 @@ export async function getSessionDirectory(
|
|
|
139
147
|
return lookup;
|
|
140
148
|
}
|
|
141
149
|
|
|
150
|
+
/** Session events may open routes only in the owning V2 Location. */
|
|
151
|
+
export async function getSessionDirectoryForEvent(
|
|
152
|
+
client: unknown,
|
|
153
|
+
sessionId: string,
|
|
154
|
+
instanceDirectory: string,
|
|
155
|
+
): Promise<string | null> {
|
|
156
|
+
const directory = await getSessionDirectory(client, sessionId, instanceDirectory);
|
|
157
|
+
if (
|
|
158
|
+
isV2PluginContext(client) &&
|
|
159
|
+
(directory !== client.location.directory ||
|
|
160
|
+
cache.get(sessionId)?.location?.workspaceID !== client.location.workspaceID)
|
|
161
|
+
)
|
|
162
|
+
return null;
|
|
163
|
+
return directory;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/** Deleted records cannot be queried: prefer the event's Location, then a previously resolved ref. */
|
|
167
|
+
export function getSessionDirectoryForDeletedEvent(
|
|
168
|
+
client: unknown,
|
|
169
|
+
sessionId: string,
|
|
170
|
+
eventLocation: unknown,
|
|
171
|
+
): string | null {
|
|
172
|
+
if (!sessionId || !isV2PluginContext(client)) return null;
|
|
173
|
+
let location: SessionLocation | undefined;
|
|
174
|
+
if (eventLocation === undefined) {
|
|
175
|
+
location = cache.get(sessionId)?.location;
|
|
176
|
+
} else {
|
|
177
|
+
if (!eventLocation || typeof eventLocation !== "object") return null;
|
|
178
|
+
const ref = eventLocation as { directory?: unknown; workspaceID?: unknown };
|
|
179
|
+
if (
|
|
180
|
+
typeof ref.directory !== "string" ||
|
|
181
|
+
!ref.directory ||
|
|
182
|
+
(ref.workspaceID !== undefined && typeof ref.workspaceID !== "string")
|
|
183
|
+
)
|
|
184
|
+
return null;
|
|
185
|
+
location = { directory: ref.directory, workspaceID: ref.workspaceID };
|
|
186
|
+
// The cleanup's bridge call also consults this cache. Preserve the event's
|
|
187
|
+
// ownership proof there instead of asking the host for an already-deleted record.
|
|
188
|
+
setCache(sessionId, location.directory, location);
|
|
189
|
+
}
|
|
190
|
+
if (
|
|
191
|
+
!location ||
|
|
192
|
+
location.directory !== client.location.directory ||
|
|
193
|
+
location.workspaceID !== client.location.workspaceID
|
|
194
|
+
)
|
|
195
|
+
return null;
|
|
196
|
+
return location.directory;
|
|
197
|
+
}
|
|
198
|
+
|
|
142
199
|
/** A late success may repair its provisional answer, but never overwrite a newer lookup. */
|
|
143
200
|
async function boundedLookup(
|
|
144
201
|
client: unknown,
|
|
@@ -152,6 +209,7 @@ async function boundedLookup(
|
|
|
152
209
|
if (oldest !== undefined) lookupGenerations.delete(oldest);
|
|
153
210
|
}
|
|
154
211
|
let timer: ReturnType<typeof setTimeout> | undefined;
|
|
212
|
+
let location: CacheEntry["location"];
|
|
155
213
|
try {
|
|
156
214
|
const timeout = new Promise<null>((resolve) => {
|
|
157
215
|
timer = setTimeout(() => {
|
|
@@ -163,10 +221,12 @@ async function boundedLookup(
|
|
|
163
221
|
}, LOOKUP_TIMEOUT_MS);
|
|
164
222
|
});
|
|
165
223
|
const directory = await Promise.race([
|
|
166
|
-
lookupSessionDirectory(client, sessionId, fallbackDirectory)
|
|
224
|
+
lookupSessionDirectory(client, sessionId, fallbackDirectory, (ref) => {
|
|
225
|
+
location = ref;
|
|
226
|
+
})
|
|
167
227
|
.then((directory) => {
|
|
168
228
|
if (lookupGenerations.get(sessionId) === generation && directory !== null) {
|
|
169
|
-
setCache(sessionId, directory);
|
|
229
|
+
setCache(sessionId, directory, location);
|
|
170
230
|
}
|
|
171
231
|
return directory;
|
|
172
232
|
})
|
|
@@ -175,7 +235,7 @@ async function boundedLookup(
|
|
|
175
235
|
}),
|
|
176
236
|
timeout,
|
|
177
237
|
]);
|
|
178
|
-
setCache(sessionId, directory);
|
|
238
|
+
setCache(sessionId, directory, location);
|
|
179
239
|
return directory;
|
|
180
240
|
} finally {
|
|
181
241
|
clearTimeout(timer);
|
|
@@ -186,8 +246,9 @@ async function lookupSessionDirectory(
|
|
|
186
246
|
client: unknown,
|
|
187
247
|
sessionId: string,
|
|
188
248
|
fallbackDirectory: string,
|
|
249
|
+
onLocation: (location: NonNullable<CacheEntry["location"]>) => void,
|
|
189
250
|
): Promise<string | null> {
|
|
190
|
-
if (isV2PluginContext(client)) return lookupV2SessionDirectory(client, sessionId);
|
|
251
|
+
if (isV2PluginContext(client)) return lookupV2SessionDirectory(client, sessionId, onLocation);
|
|
191
252
|
|
|
192
253
|
const c = client as OpenCodeClientShape;
|
|
193
254
|
const sessionApi = c?.session;
|
|
@@ -236,6 +297,7 @@ async function lookupSessionDirectory(
|
|
|
236
297
|
async function lookupV2SessionDirectory(
|
|
237
298
|
context: V2PluginContextShape,
|
|
238
299
|
sessionId: string,
|
|
300
|
+
onLocation: (location: NonNullable<CacheEntry["location"]>) => void,
|
|
239
301
|
): Promise<string | null> {
|
|
240
302
|
let dir: string | null = null;
|
|
241
303
|
let failure: string | undefined;
|
|
@@ -247,8 +309,10 @@ async function lookupV2SessionDirectory(
|
|
|
247
309
|
),
|
|
248
310
|
);
|
|
249
311
|
const directory = session?.location?.directory;
|
|
250
|
-
if (typeof directory === "string" && directory.length > 0)
|
|
251
|
-
|
|
312
|
+
if (typeof directory === "string" && directory.length > 0) {
|
|
313
|
+
dir = directory;
|
|
314
|
+
onLocation({ directory, workspaceID: session?.location?.workspaceID });
|
|
315
|
+
} else failure = "the session record has no location.directory";
|
|
252
316
|
} catch (err) {
|
|
253
317
|
failure = err instanceof Error ? err.message || err.name : String(err);
|
|
254
318
|
}
|
|
@@ -261,9 +325,13 @@ async function lookupV2SessionDirectory(
|
|
|
261
325
|
return dir;
|
|
262
326
|
}
|
|
263
327
|
|
|
264
|
-
function setCache(
|
|
328
|
+
function setCache(
|
|
329
|
+
sessionId: string,
|
|
330
|
+
directory: string | null,
|
|
331
|
+
location?: CacheEntry["location"],
|
|
332
|
+
): void {
|
|
265
333
|
if (cache.has(sessionId)) cache.delete(sessionId);
|
|
266
|
-
cache.set(sessionId, { directory, recordedAt: Date.now() });
|
|
334
|
+
cache.set(sessionId, { directory, location, recordedAt: Date.now() });
|
|
267
335
|
if (cache.size > CACHE_MAX_ENTRIES) {
|
|
268
336
|
const oldest = cache.keys().next().value;
|
|
269
337
|
if (oldest !== undefined) cache.delete(oldest);
|
|
@@ -385,6 +453,13 @@ async function boundedVerification(client: unknown, sessionId: string): Promise<
|
|
|
385
453
|
return dir;
|
|
386
454
|
}
|
|
387
455
|
|
|
456
|
+
/** Forget directory snapshots when the host moves or removes a session. */
|
|
457
|
+
export function clearSessionDirectory(sessionId: string): void {
|
|
458
|
+
cache.delete(sessionId);
|
|
459
|
+
lookupGenerations.delete(sessionId);
|
|
460
|
+
verifyCache.delete(sessionId);
|
|
461
|
+
}
|
|
462
|
+
|
|
388
463
|
/** Release transport routes on deletion without querying a session the host has already removed. */
|
|
389
464
|
export async function closeDeletedSessionRoutes(
|
|
390
465
|
pool: AftTransportPool,
|
|
@@ -395,9 +470,7 @@ export async function closeDeletedSessionRoutes(
|
|
|
395
470
|
const roots = new Set(pool.activeBridges().map((bridge) => bridge.getCwd()));
|
|
396
471
|
roots.add(getSessionDirectoryCached(sessionId) ?? fallbackDirectory);
|
|
397
472
|
await Promise.allSettled([...roots].map((root) => pool.closeSession(root, sessionId)));
|
|
398
|
-
|
|
399
|
-
lookupGenerations.delete(sessionId);
|
|
400
|
-
verifyCache.delete(sessionId);
|
|
473
|
+
clearSessionDirectory(sessionId);
|
|
401
474
|
}
|
|
402
475
|
|
|
403
476
|
/** Test-only: clear the cache between unit tests. */
|
package/src/shared/status.ts
CHANGED
|
@@ -59,11 +59,7 @@ export interface StatusBar {
|
|
|
59
59
|
disabled_categories?: string[];
|
|
60
60
|
errors?: number;
|
|
61
61
|
warnings?: number;
|
|
62
|
-
dead_code?: number;
|
|
63
|
-
unused_exports?: number;
|
|
64
|
-
duplicates?: number;
|
|
65
62
|
todos?: number;
|
|
66
|
-
tier2_stale?: boolean;
|
|
67
63
|
/**
|
|
68
64
|
* `no_language_server` means no server is running. `producer_missing`
|
|
69
65
|
* masks both counts when an expected project producer has failed, even if
|
|
@@ -147,12 +143,12 @@ export interface AftStatusSnapshot {
|
|
|
147
143
|
};
|
|
148
144
|
storage_dir: string | null;
|
|
149
145
|
/** Total checkpoints across all sessions sharing this bridge. */
|
|
150
|
-
checkpoints_total: number;
|
|
146
|
+
checkpoints_total: number | null;
|
|
151
147
|
/** Current session's own slice of undo/checkpoint state. */
|
|
152
148
|
session: {
|
|
153
149
|
id: string;
|
|
154
150
|
tracked_files: number;
|
|
155
|
-
checkpoints: number;
|
|
151
|
+
checkpoints: number | null;
|
|
156
152
|
};
|
|
157
153
|
/** Compression aggregate passthrough; rendering is added separately. */
|
|
158
154
|
compression?: StatusCompression;
|
|
@@ -233,9 +229,6 @@ function readStatusBar(value: unknown): StatusBar | undefined {
|
|
|
233
229
|
: [];
|
|
234
230
|
const errors = readOptionalNumber(bar.errors);
|
|
235
231
|
const warnings = readOptionalNumber(bar.warnings);
|
|
236
|
-
const deadCode = readOptionalNumber(bar.dead_code);
|
|
237
|
-
const unusedExports = readOptionalNumber(bar.unused_exports);
|
|
238
|
-
const duplicates = readOptionalNumber(bar.duplicates);
|
|
239
232
|
const todos = readOptionalNumber(bar.todos);
|
|
240
233
|
const noLanguageServer = bar.diagnostics === "no_language_server";
|
|
241
234
|
const producerMissing = bar.diagnostics === "producer_missing";
|
|
@@ -257,9 +250,6 @@ function readStatusBar(value: unknown): StatusBar | undefined {
|
|
|
257
250
|
if (
|
|
258
251
|
errors === null &&
|
|
259
252
|
warnings === null &&
|
|
260
|
-
deadCode === null &&
|
|
261
|
-
unusedExports === null &&
|
|
262
|
-
duplicates === null &&
|
|
263
253
|
todos === null &&
|
|
264
254
|
!noLanguageServer &&
|
|
265
255
|
!producerMissing &&
|
|
@@ -271,11 +261,7 @@ function readStatusBar(value: unknown): StatusBar | undefined {
|
|
|
271
261
|
...(disabled.length > 0 ? { disabled_categories: disabled } : {}),
|
|
272
262
|
...(errors !== null ? { errors } : {}),
|
|
273
263
|
...(warnings !== null ? { warnings } : {}),
|
|
274
|
-
...(deadCode !== null ? { dead_code: deadCode } : {}),
|
|
275
|
-
...(unusedExports !== null ? { unused_exports: unusedExports } : {}),
|
|
276
|
-
...(duplicates !== null ? { duplicates } : {}),
|
|
277
264
|
...(todos !== null ? { todos } : {}),
|
|
278
|
-
...(bar.tier2_stale === true ? { tier2_stale: true } : {}),
|
|
279
265
|
...(noLanguageServer ? { diagnostics: "no_language_server" as const } : {}),
|
|
280
266
|
...(producerMissing
|
|
281
267
|
? { diagnostics: "producer_missing" as const, diagnostics_gaps: diagnosticsGaps }
|
|
@@ -417,11 +403,11 @@ export function coerceAftStatus(response: Record<string, unknown>): AftStatusSna
|
|
|
417
403
|
warm_entries: readNumber(symbolCache.warm_entries),
|
|
418
404
|
},
|
|
419
405
|
storage_dir: readNullableString(response.storage_dir),
|
|
420
|
-
checkpoints_total:
|
|
406
|
+
checkpoints_total: readOptionalNumber(response.checkpoints_total),
|
|
421
407
|
session: {
|
|
422
408
|
id: readString(session.id, "__default__"),
|
|
423
409
|
tracked_files: readNumber(session.tracked_files),
|
|
424
|
-
checkpoints:
|
|
410
|
+
checkpoints: readOptionalNumber(session.checkpoints),
|
|
425
411
|
},
|
|
426
412
|
compression: readCompression(response.compression),
|
|
427
413
|
status_bar: readStatusBar(response.status_bar),
|
|
@@ -501,13 +487,10 @@ export function formatStatusDialogMessage(status: AftStatusSnapshot): string {
|
|
|
501
487
|
const rows = [
|
|
502
488
|
["errors", sb.errors],
|
|
503
489
|
["warnings", sb.warnings],
|
|
504
|
-
["dead code", sb.dead_code],
|
|
505
|
-
["unused exports", sb.unused_exports],
|
|
506
|
-
["duplicates", sb.duplicates],
|
|
507
490
|
["todos", sb.todos],
|
|
508
491
|
].filter((row): row is [string, number] => typeof row[1] === "number");
|
|
509
492
|
if (rows.length > 0) {
|
|
510
|
-
lines.push("",
|
|
493
|
+
lines.push("", "Code Health");
|
|
511
494
|
for (const [label, value] of rows) lines.push(`- ${label}: ${formatCount(value)}`);
|
|
512
495
|
}
|
|
513
496
|
}
|
|
@@ -660,13 +643,10 @@ export function formatStatusMarkdown(status: AftStatusSnapshot): string {
|
|
|
660
643
|
const rows = [
|
|
661
644
|
["Errors", sb.errors],
|
|
662
645
|
["Warnings", sb.warnings],
|
|
663
|
-
["Dead code", sb.dead_code],
|
|
664
|
-
["Unused exports", sb.unused_exports],
|
|
665
|
-
["Duplicates", sb.duplicates],
|
|
666
646
|
["TODOs", sb.todos],
|
|
667
647
|
].filter((row): row is [string, number] => typeof row[1] === "number");
|
|
668
648
|
if (rows.length > 0) {
|
|
669
|
-
lines.push("",
|
|
649
|
+
lines.push("", "### Code Health");
|
|
670
650
|
for (const [label, value] of rows) lines.push(`- **${label}:** ${formatCount(value)}`);
|
|
671
651
|
}
|
|
672
652
|
}
|
package/src/tui/index.tsx
CHANGED
|
@@ -155,7 +155,7 @@ function statusAcceptGate(directory: string): (result: unknown) => boolean {
|
|
|
155
155
|
};
|
|
156
156
|
}
|
|
157
157
|
|
|
158
|
-
const StatusDialog = (props: StatusDialogProps) => {
|
|
158
|
+
export const StatusDialog = (props: StatusDialogProps) => {
|
|
159
159
|
const theme = createMemo(() => (props.api as any).theme.current as TuiThemeCurrent);
|
|
160
160
|
const t = () => theme();
|
|
161
161
|
|
|
@@ -426,11 +426,15 @@ const StatusDialog = (props: StatusDialogProps) => {
|
|
|
426
426
|
</text>
|
|
427
427
|
</box>
|
|
428
428
|
<R theme={t()} label="Tracked files" value={String(status()!.session.tracked_files)} />
|
|
429
|
-
<R
|
|
429
|
+
<R
|
|
430
|
+
theme={t()}
|
|
431
|
+
label="Checkpoints"
|
|
432
|
+
value={status()!.session.checkpoints?.toString() ?? "—"}
|
|
433
|
+
/>
|
|
430
434
|
<R
|
|
431
435
|
theme={t()}
|
|
432
436
|
label="All-session checkpoints"
|
|
433
|
-
value={
|
|
437
|
+
value={status()!.checkpoints_total?.toString() ?? "—"}
|
|
434
438
|
tone="muted"
|
|
435
439
|
/>
|
|
436
440
|
</box>
|
|
@@ -496,7 +500,7 @@ const StatusDialog = (props: StatusDialogProps) => {
|
|
|
496
500
|
{statusBar() ? (
|
|
497
501
|
<box flexDirection="column" width="100%" marginTop={1}>
|
|
498
502
|
<text fg={t().text}>
|
|
499
|
-
<b>
|
|
503
|
+
<b>Code Health</b>
|
|
500
504
|
</text>
|
|
501
505
|
{(statusBar()!.disabled_categories ?? []).map((key) => (
|
|
502
506
|
<R theme={t()} label={key.replaceAll("_", " ")} value="○ off" tone="muted" />
|
|
@@ -517,30 +521,6 @@ const StatusDialog = (props: StatusDialogProps) => {
|
|
|
517
521
|
tone={statusBar()!.warnings! > 0 ? "warn" : "muted"}
|
|
518
522
|
/>
|
|
519
523
|
) : null}
|
|
520
|
-
{statusBar()!.dead_code !== undefined ? (
|
|
521
|
-
<R
|
|
522
|
-
theme={t()}
|
|
523
|
-
label="Dead Code"
|
|
524
|
-
value={formatCountShort(statusBar()!.dead_code)}
|
|
525
|
-
tone="muted"
|
|
526
|
-
/>
|
|
527
|
-
) : null}
|
|
528
|
-
{statusBar()!.unused_exports !== undefined ? (
|
|
529
|
-
<R
|
|
530
|
-
theme={t()}
|
|
531
|
-
label="Unused Exports"
|
|
532
|
-
value={formatCountShort(statusBar()!.unused_exports)}
|
|
533
|
-
tone="muted"
|
|
534
|
-
/>
|
|
535
|
-
) : null}
|
|
536
|
-
{statusBar()!.duplicates !== undefined ? (
|
|
537
|
-
<R
|
|
538
|
-
theme={t()}
|
|
539
|
-
label="Duplicates"
|
|
540
|
-
value={formatCountShort(statusBar()!.duplicates)}
|
|
541
|
-
tone="muted"
|
|
542
|
-
/>
|
|
543
|
-
) : null}
|
|
544
524
|
{statusBar()!.todos !== undefined ? (
|
|
545
525
|
<R
|
|
546
526
|
theme={t()}
|
package/src/tui/preferences.ts
CHANGED
|
@@ -40,7 +40,7 @@ export interface AftTuiPrefs {
|
|
|
40
40
|
rememberCollapsed: boolean;
|
|
41
41
|
collapsed: boolean | null;
|
|
42
42
|
/**
|
|
43
|
-
* Whether OpenCode 2 shows AFT's health line (`AFT E0 W0 |
|
|
43
|
+
* Whether OpenCode 2 shows AFT's health line (`AFT E0 W0 | T0`)
|
|
44
44
|
* in the prompt footer. Off hides it entirely; the sidebar and the status
|
|
45
45
|
* dialog are unaffected.
|
|
46
46
|
*/
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type * as SharedSidebar from "@cortexkit/opencode-sidebar";
|
|
2
|
+
|
|
3
|
+
// Raw-source hosts use their ordinary runtime; published hosts use the virtual
|
|
4
|
+
// runtime module selected by build-tui.ts instead of this loader.
|
|
5
|
+
const generatedModule = "../tui-compiled/shared-sidebar-local.tsx";
|
|
6
|
+
const sidebar: typeof SharedSidebar = await import(generatedModule).catch((cause: unknown) => {
|
|
7
|
+
throw new Error(
|
|
8
|
+
"AFT's generated shared sidebar could not be loaded. Run `bun run --cwd packages/opencode-plugin build:tui` before loading the TUI from source.",
|
|
9
|
+
{ cause },
|
|
10
|
+
);
|
|
11
|
+
});
|
|
12
|
+
|
|
13
|
+
export const SidebarFrame = sidebar.SidebarFrame;
|
|
14
|
+
export type { SidebarTheme } from "@cortexkit/opencode-sidebar";
|