// Briefs as documents (spec ยง5.2): `spectra-brief:/overall.md` and // `spectra-brief:/project/NAME.md`. Opening one reads it from Spectra; // saving it records a signed brief.set, so agents are told. import * as vscode from "vscode"; import type { Briefs } from "./spectra-api"; import { Bridge } from "./bridge"; export const SCHEME = "spectra-brief"; export function uriFor(project?: string): vscode.Uri { return vscode.Uri.from({ scheme: SCHEME, path: project ? `/project/${project}.md` : "/overall.md" }); } function projectOf(uri: vscode.Uri): string | undefined { const m = /^\/project\/([a-z0-9][a-z0-9-]*)\.md$/.exec(uri.path); if (m) return m[1]; if (uri.path === "/overall.md") return undefined; throw vscode.FileSystemError.FileNotFound(uri); } export class BriefFs implements vscode.FileSystemProvider { private readonly emitter = new vscode.EventEmitter(); readonly onDidChangeFile = this.emitter.event; private readonly times = new Map(); constructor(private readonly bridge: Bridge) {} watch(): vscode.Disposable { return new vscode.Disposable(() => undefined); } /** Tell open editors that briefs changed in Spectra. */ changed(): void { const now = Date.now(); const events: vscode.FileChangeEvent[] = []; for (const k of this.times.keys()) { this.times.set(k, now); events.push({ type: vscode.FileChangeType.Changed, uri: vscode.Uri.parse(k) }); } if (events.length) this.emitter.fire(events); } private async text(uri: vscode.Uri): Promise { const project = projectOf(uri); const b = await this.bridge.request("GET", "/api/v1/briefs"); return (project ? b.projects.find((p) => p.project === project)?.text : b.overall) ?? ""; } async stat(uri: vscode.Uri): Promise { projectOf(uri); const key = uri.toString(); if (!this.times.has(key)) this.times.set(key, Date.now()); const size = Buffer.byteLength(await this.text(uri).catch(() => "")); return { type: vscode.FileType.File, ctime: 0, mtime: this.times.get(key)!, size }; } async readFile(uri: vscode.Uri): Promise { return Buffer.from(await this.text(uri)); } async writeFile(uri: vscode.Uri, content: Uint8Array): Promise { const project = projectOf(uri); try { await this.bridge.request("PUT", "/api/v1/briefs", { project: project ?? null, text: Buffer.from(content).toString("utf8") }); } catch (e) { throw vscode.FileSystemError.NoPermissions(`Spectra did not take the brief: ${(e as Error).message}`); } this.times.set(uri.toString(), Date.now()); void vscode.window.setStatusBarMessage(`$(check) ${project ? `${project}'s brief` : "Overall brief"} signed into Spectra; agents are told on their next call`, 5000); } readDirectory(): [string, vscode.FileType][] { return []; } createDirectory(): void { throw vscode.FileSystemError.NoPermissions("briefs are not folders"); } delete(): void { throw vscode.FileSystemError.NoPermissions("clear a brief by saving it empty"); } rename(): void { throw vscode.FileSystemError.NoPermissions("briefs cannot be renamed"); } }