Official

files

Built-in files UI (JavaScript UI plugin).

The app opens the listing; nothing installs until an agent in your Plugins workspace has read the files and you enable the plugin. In a terminal: cvg install convergence/files@0.2.0

Permissions in 0.2.0

  • Read files fs.readMediumReads files in the listed places.List the files of the workspacePlaces: the open workspace
  • Read chats chats.readMediumReads your transcripts and chat lists.Refresh the file list when an agent finishes a turn
  • Show panels ui.slotsLowShows views in the listed parts of the window.Show the files button and panelSlots: railright

Files

panel.ts1.6 KB
// File panel state, filtering and folder-first tree construction.
// Paths and selection remain workspace-relative, matching the kernel tree ids.
import * as z from "zod";
import type { EffectClient } from "convergence/effect";
import type * as Fiber from "effect/Fiber";
import type { AppState } from "convergence";
import type { Tabs } from "../tabs/contract.ts";

export interface FilesState {
  workspaceId: string | null;
  paths: readonly string[];
  loaded: boolean;
  query: string;
  // The convergence/tabs client while its provider runs, the open file it
  // last announced, and the fiber following it; selection stays empty
  // without it.
  tabs: EffectClient<typeof Tabs> | undefined;
  activePath: string | null;
  follow: Fiber.Fiber<void> | undefined;
}

export const initialState = (): FilesState => ({
  workspaceId: null,
  paths: [],
  loaded: false,
  query: "",
  tabs: undefined,
  activePath: null,
  follow: undefined,
});

export function visiblePaths(paths: readonly string[], query: string): readonly string[] {
  const q = query.trim().toLowerCase();
  return q ? paths.filter((path) => path.toLowerCase().includes(q)).slice(0, 500) : paths;
}

export function isOpen(app: AppState): boolean {
  return app.shared?.rightPanel === "files" || app.shared?.rightPanelPreview === "files";
}

export function selectedPath(activePath: string | null): string | null {
  return typeof activePath === "string" ? activePath : null;
}

const finished = z.object({ event: z.literal("run_finished") });
export const runFinished = (kind: unknown): boolean => finished.safeParse(kind).success;

Versions

VersionPublishedPlugin APISizePermissionsStatus
0.2.0latestOct 5, 2026>=2 <36.4 KB3 permissionsListed

Reviews and comments

0 threads · 0 reviews

No comments yet.