diff --git a/apps/tui/lib/coding-agent/package.json b/apps/tui/lib/coding-agent/package.json index a5d763d8..5e45b58c 100644 --- a/apps/tui/lib/coding-agent/package.json +++ b/apps/tui/lib/coding-agent/package.json @@ -18,6 +18,7 @@ "./auth-storage": "./src/core/auth-storage.ts", "./model-registry": "./src/core/model-registry.ts", "./settings-manager": "./src/core/settings-manager.ts", + "./slash-commands": "./src/core/slash-commands.ts", "./timings": "./src/core/timings.ts", "./messages": "./src/core/messages.ts", "./theme": "./src/modes/interactive/theme/theme.ts" diff --git a/apps/tui/src/native-agent-connection.ts b/apps/tui/src/native-agent-connection.ts index b27cf8be..5630613c 100644 --- a/apps/tui/src/native-agent-connection.ts +++ b/apps/tui/src/native-agent-connection.ts @@ -36,7 +36,7 @@ import type { } from "@earendil-works/pi-coding-agent/agent-connection"; import type { ThinkingLevel } from "@earendil-works/pi-agent-core"; import type { ContextTreeNode } from "../lib/coding-agent/src/core/context-tree.js"; -import { PERMISSIONS_COMMAND_ENABLED } from "../lib/coding-agent/src/core/slash-commands.js"; +import { PERMISSIONS_COMMAND_ENABLED } from "@earendil-works/pi-coding-agent/slash-commands"; import type { BashResult } from "../lib/coding-agent/src/core/bash-executor.js"; import { measureAsync } from "@earendil-works/pi-coding-agent/timings"; import { StdioJsonRpc, type JsonRpcId } from "./stdio-jsonrpc.js"; diff --git a/apps/tui/src/runtime-imports.test.ts b/apps/tui/src/runtime-imports.test.ts new file mode 100644 index 00000000..e8f660d0 --- /dev/null +++ b/apps/tui/src/runtime-imports.test.ts @@ -0,0 +1,40 @@ +import assert from "node:assert/strict"; +import test from "node:test"; +import { readdirSync, readFileSync } from "node:fs"; +import { dirname, join } from "node:path"; +import { fileURLToPath } from "node:url"; + +/** + * The release bundle deletes apps/tui's `lib/` directory after materializing + * the file: packages under node_modules (scripts/runtime/tui.ts). A runtime + * value import that escapes into `../lib/...` therefore only fails in the + * shipped product, never in the repo checkout. Keep value imports on package + * specifiers (`@earendil-works/...`); `import type` is erased at transpile and + * stays fine. + */ +const repoTui = join(dirname(fileURLToPath(import.meta.url)), ".."); + +function collectSourceFiles(directory: string): string[] { + const files: string[] = []; + for (const item of readdirSync(directory, { withFileTypes: true })) { + const path = join(directory, item.name); + if (item.isDirectory()) { + if (item.name !== "node_modules") files.push(...collectSourceFiles(path)); + } else if (item.name.endsWith(".ts") && !/\.(test|spec|d)\.ts$/.test(item.name)) { + files.push(path); + } + } + return files; +} + +test("runtime sources never value-import from the deleted lib directory", () => { + const offenders: string[] = []; + for (const path of collectSourceFiles(join(repoTui, "src"))) { + const text = readFileSync(path, "utf8"); + for (const match of text.matchAll(/import\s+(type\s+)?([^"]*?)from\s+"(\.\.[/])+lib[/]/g)) { + const isTypeOnly = Boolean(match[1]) || /^type[\s{]/.test(match[2]); + if (!isTypeOnly) offenders.push(`${path.replace(repoTui + "/", "")}: ${match[0]}`); + } + } + assert.deepEqual(offenders, []); +});