diff --git a/packages/pi-coding-agent/src/core/lsp/config.ts b/packages/pi-coding-agent/src/core/lsp/config.ts index 29401a363..37429be80 100644 --- a/packages/pi-coding-agent/src/core/lsp/config.ts +++ b/packages/pi-coding-agent/src/core/lsp/config.ts @@ -4,7 +4,7 @@ import * as os from "node:os"; import * as path from "node:path"; import { spawnSync } from "node:child_process"; import YAML from "yaml"; -import { globSync } from "glob"; +import { globSync } from "node:fs"; import { CONFIG_DIR_NAME } from "../../config.js"; import { isRecord } from "./helpers.js"; import type { ServerConfig } from "./types.js"; @@ -151,7 +151,7 @@ export function hasRootMarkers(cwd: string, markers: string[]): boolean { for (const marker of markers) { if (marker.includes("*")) { try { - const matches = globSync(marker, { cwd, nodir: false }); + const matches = globSync(marker, { cwd }); if (matches.length > 0) { return true; } diff --git a/packages/pi-coding-agent/src/core/lsp/utils.ts b/packages/pi-coding-agent/src/core/lsp/utils.ts index da037cfda..67d8cf2ca 100644 --- a/packages/pi-coding-agent/src/core/lsp/utils.ts +++ b/packages/pi-coding-agent/src/core/lsp/utils.ts @@ -1,6 +1,6 @@ import * as fsPromises from "node:fs/promises"; import path from "node:path"; -import { glob } from "glob"; +import { glob } from "node:fs/promises"; import { isEnoent } from "./helpers.js"; import type { CallHierarchyItem, @@ -533,7 +533,7 @@ export async function collectGlobMatches( maxMatches: number, ): Promise<{ matches: string[]; truncated: boolean }> { const normalizedLimit = Number.isFinite(maxMatches) ? Math.max(1, Math.trunc(maxMatches)) : 1; - const allMatches = await glob(pattern, { cwd }); + const allMatches: string[] = []; for await (const p of glob(pattern, { cwd })) allMatches.push(p); if (allMatches.length > normalizedLimit) { return { matches: allMatches.slice(0, normalizedLimit), truncated: true }; } diff --git a/src/cli-stats.ts b/src/cli-stats.ts index a20f3ad45..3333e113c 100644 --- a/src/cli-stats.ts +++ b/src/cli-stats.ts @@ -1,6 +1,6 @@ import { existsSync } from "node:fs"; -import { createRequire } from "node:module"; import { join } from "node:path"; +import { DatabaseSync } from "node:sqlite"; export interface ModelStatsRow { model_id: string; @@ -33,7 +33,6 @@ interface ParsedStatsArgs { unitType?: string; } -const require = createRequire(import.meta.url); export function parseDurationSeconds(value: string): number { const match = value.trim().match(/^(\d+(?:\.\d+)?)([smhdw])$/i); @@ -203,42 +202,7 @@ function usage(): string { } function openSqliteDb(dbPath: string): SqliteDb { - try { - const sqlite = require("node:sqlite") as { - DatabaseSync?: new ( - path: string, - options?: Record, - ) => SqliteDb; - }; - if (sqlite.DatabaseSync) { - return new sqlite.DatabaseSync(dbPath, { readOnly: true }); - } - } catch { - // Try better-sqlite3 below. - } - - try { - const mod = require("better-sqlite3") as - | { - default?: new ( - path: string, - options?: Record, - ) => SqliteDb; - } - | (new ( - path: string, - options?: Record, - ) => SqliteDb); - const Database = typeof mod === "function" ? mod : mod.default; - if (Database) - return new Database(dbPath, { readonly: true, fileMustExist: true }); - } catch { - // Report a single actionable error below. - } - - throw new Error( - "No SQLite provider available (tried node:sqlite, better-sqlite3)", - ); + return new DatabaseSync(dbPath, { readOnly: true }) as SqliteDb; } export async function runStatsCli( diff --git a/src/resources/extensions/sf/sf-db.ts b/src/resources/extensions/sf/sf-db.ts index 073ee2ec6..dc64e23f0 100644 --- a/src/resources/extensions/sf/sf-db.ts +++ b/src/resources/extensions/sf/sf-db.ts @@ -1,6 +1,5 @@ // SF Database Abstraction Layer -// Provides a SQLite database with provider fallback chain: -// node:sqlite (built-in) → better-sqlite3 (npm) → null (unavailable) +// Provides a SQLite database via node:sqlite (Node >= 24 built-in). // // Exposes a unified sync API for decisions and requirements storage. // Schema is initialized on first open with WAL mode for file-backed DBs. @@ -21,8 +20,8 @@ // excluded from this invariant. import { copyFileSync, existsSync, mkdirSync, realpathSync } from "node:fs"; -import { createRequire } from "node:module"; import { dirname } from "node:path"; +import { DatabaseSync } from "node:sqlite"; import { SF_STALE_STATE, SFError } from "./errors.js"; import { getGateIdsForTurn, type OwnerTurn } from "./gate-registry.js"; import type { VisionAlignmentMeetingRecord } from "./milestone-quality.js"; @@ -42,7 +41,6 @@ import { logError, logWarning } from "./workflow-logger.js"; // pure structure with no runtime coupling. import type { StateManifest } from "./workflow-manifest.js"; -const _require = createRequire(import.meta.url); interface DbStatement { run(...params: unknown[]): unknown; @@ -56,69 +54,14 @@ interface DbAdapter { close(): void; } -type ProviderName = "node:sqlite" | "better-sqlite3"; -let providerName: ProviderName | null = null; -let providerModule: unknown = null; let loadAttempted = false; -function suppressSqliteWarning(): void { - const origEmit = process.emit; - // Override via loose cast: Node's overloaded emit signature is not directly assignable. - (process as any).emit = (event: string, ...args: unknown[]): boolean => { - if ( - event === "warning" && - args[0] && - typeof args[0] === "object" && - "name" in args[0] && - (args[0] as { name: string }).name === "ExperimentalWarning" && - "message" in args[0] && - typeof (args[0] as { message: string }).message === "string" && - (args[0] as { message: string }).message.includes("SQLite") - ) { - return false; - } - return origEmit.apply(process, [event, ...args] as Parameters< - typeof process.emit - >) as unknown as boolean; - }; -} function loadProvider(): void { if (loadAttempted) return; loadAttempted = true; - - try { - suppressSqliteWarning(); - const mod = _require("node:sqlite"); - if (mod.DatabaseSync) { - providerModule = mod; - providerName = "node:sqlite"; - return; - } - } catch { - // unavailable - } - - try { - const mod = _require("better-sqlite3"); - if (typeof mod === "function" || (mod && mod.default)) { - providerModule = mod.default || mod; - providerName = "better-sqlite3"; - return; - } - } catch { - // unavailable - } - - const nodeMajor = parseInt(process.versions.node.split(".")[0], 10); - const versionHint = - nodeMajor < 22 - ? ` SF requires Node >= 22.0.0 (current: v${process.versions.node}). Upgrade Node to fix this.` - : ""; - process.stderr.write( - `sf-db: No SQLite provider available (tried node:sqlite, better-sqlite3).${versionHint}\n`, - ); + // node:sqlite is built-in in Node >= 24 } function normalizeRow(row: unknown): Record | undefined { @@ -184,17 +127,7 @@ function createAdapter(rawDb: unknown): DbAdapter { function openRawDb(path: string): unknown { loadProvider(); - if (!providerModule || !providerName) return null; - - if (providerName === "node:sqlite") { - const { DatabaseSync } = providerModule as { - DatabaseSync: new (path: string) => unknown; - }; - return new DatabaseSync(path); - } - - const Database = providerModule as new (path: string) => unknown; - return new Database(path); + return new DatabaseSync(path); } const SCHEMA_VERSION = 21; @@ -1479,9 +1412,9 @@ let _dbOpenAttempted = false; /** * Get the name of the SQLite provider currently loaded (or null if unavailable). */ -export function getDbProvider(): ProviderName | null { +export function getDbProvider(): string | null { loadProvider(); - return providerName; + return "node:sqlite"; } /** @@ -1830,8 +1763,6 @@ export function _getAdapter(): DbAdapter | null { export function _resetProvider(): void { loadAttempted = false; - providerModule = null; - providerName = null; } export function upsertDecision(d: Omit): void { diff --git a/src/resources/extensions/sf/unit-ownership.ts b/src/resources/extensions/sf/unit-ownership.ts index 544f8cc83..5d3087a7f 100644 --- a/src/resources/extensions/sf/unit-ownership.ts +++ b/src/resources/extensions/sf/unit-ownership.ts @@ -13,10 +13,9 @@ // Copyright (c) 2026 Jeremy McSpadden import { mkdirSync } from "node:fs"; -import { createRequire } from "node:module"; import { join } from "node:path"; +import { DatabaseSync } from "node:sqlite"; -const _require = createRequire(import.meta.url); // ─── Types ─────────────────────────────────────────────────────────────── @@ -38,60 +37,14 @@ interface DbLike { close(): void; } -type ProviderName = "node:sqlite" | "better-sqlite3"; -let providerName: ProviderName | null = null; -let providerModule: unknown = null; let loadAttempted = false; -function suppressSqliteWarning(): void { - const origEmit = process.emit; - // Override via loose cast: Node's overloaded emit signature is not directly assignable. - (process as any).emit = (event: string, ...args: unknown[]): boolean => { - if ( - event === "warning" && - args[0] && - typeof args[0] === "object" && - "name" in args[0] && - (args[0] as { name: string }).name === "ExperimentalWarning" && - "message" in args[0] && - typeof (args[0] as { message: string }).message === "string" && - (args[0] as { message: string }).message.includes("SQLite") - ) { - return false; - } - return origEmit.apply(process, [event, ...args] as Parameters< - typeof process.emit - >) as unknown as boolean; - }; -} function loadProvider(): void { if (loadAttempted) return; loadAttempted = true; - - try { - suppressSqliteWarning(); - const mod = _require("node:sqlite"); - if (mod.DatabaseSync) { - providerModule = mod; - providerName = "node:sqlite"; - return; - } - } catch { - // unavailable - } - - try { - const mod = _require("better-sqlite3"); - if (typeof mod === "function" || (mod && mod.default)) { - providerModule = mod.default || mod; - providerName = "better-sqlite3"; - return; - } - } catch { - // unavailable - } + // node:sqlite is built-in in Node >= 24 } function normalizeRow(row: unknown): Record | undefined { @@ -104,17 +57,7 @@ function normalizeRow(row: unknown): Record | undefined { function openRawDb(path: string): unknown { loadProvider(); - if (!providerModule || !providerName) return null; - - if (providerName === "node:sqlite") { - const { DatabaseSync } = providerModule as { - DatabaseSync: new (path: string) => unknown; - }; - return new DatabaseSync(path); - } - - const Database = providerModule as new (path: string) => unknown; - return new Database(path); + return new DatabaseSync(path); } function wrapDb(rawDb: unknown): DbLike {