/** * The real candidate sources behind `/workflow` completion (spec §14.6/§14.7) — the only part of the * feature that touches a disk, so completions.ts can stay pure. * * Both sources run on a keystroke, which dictates their shape: * * - **Installed workflow identity comes from filenames, never an import.** Completion and run lookup * share the same cheap directory enumeration, so a completed suggestion names the exact file the * command will load. Declared definition names remain internal to workflow composition. * - **The run listing is memoized for one second.** Listing parses every run's log, so a burst of * keystrokes must cost one read; one second is short enough that nothing has to invalidate it after a * run starts, is cancelled, or is deleted (spec §14.6). * * The location is captured rather than resolved per call because the completion callback is handed no * context at all — no `cwd`, no session manager, no store (spec §14.2). It captures BOTH values the * command handler builds its store from (spec §14.7): authored workflows are keyed by project root * (§6.8) and a run's artifacts by the harness's session directory (§8.9), and `runArtifactsDir` needs * the pair to resolve either way. The extension re-captures them from every `session_start`; this copy * exists for completion only, and the handlers keep resolving from their own invocation's context. */ import type { CompletionSources } from "./completions.ts" import { createFsStore } from "./fs-store.ts" import { runArtifactsDir } from "./project-dir.ts" import type { RunSummary } from "./types.ts" import { listWorkflowIdentities } from "./workflow-catalog.ts" /** How long one run listing is reused for (spec §14.6). */ const RUNS_MEMO_MS = 1000 /** Completion sources bound to a location the extension re-points on every session start. */ export interface ProjectCompletionSources extends CompletionSources { /** Point the sources at the project and session directory of the session that just started (spec §14.7). */ setProject(cwd: string, sessionDir: string): void } export function createCompletionSources(): ProjectCompletionSources { // The pre-first-start fallback (spec §14.7): a keystroke cannot wait for `session_start`, and an empty // session dir is exactly what `--no-session` hands the handlers, which `runArtifactsDir` already covers. let cwd = process.cwd() let sessionDir = "" let memo: { at: number; runs: Promise } | undefined return { setProject(nextCwd: string, nextSessionDir: string): void { cwd = nextCwd sessionDir = nextSessionDir memo = undefined // the memo holds another location's runs }, async workflows(): Promise { return listWorkflowIdentities(cwd).catch(() => []) }, runs(): Promise { const at = Date.now() if (!memo || at - memo.at >= RUNS_MEMO_MS) { // Completion is advisory (spec §14.1): a corrupt or unreadable log costs the user suggestions, // never the keystroke. memo = { at, runs: createFsStore(runArtifactsDir(cwd, sessionDir)) .list() .catch(() => []), } } return memo.runs }, } }