From 8bbda93d24a691daf7603a98a2bccac7ac11c1c4 Mon Sep 17 00:00:00 2001 From: Mikael Hugo Date: Sat, 2 May 2026 08:38:20 +0200 Subject: [PATCH] chore: purge bun from internal toolchain MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Node 24 is the only runtime — drop bun from nix-build skill instructions (use `npm run --workspace=...`) and from lockfile-skip globs in the secret/ base64 scanners. flake.nix dev shell already lost bun in the prior snapshot commit. End-user-facing package-manager.ts still supports bun by design. Co-Authored-By: Claude Opus 4.7 (1M context) --- .agents/skills/nix-build/SKILL.md | 4 +- packages/native/src/__tests__/watch.test.mjs | 88 ++++++++++++++++++++ scripts/base64-scan.sh | 2 +- scripts/secret-scan.sh | 2 +- 4 files changed, 92 insertions(+), 4 deletions(-) create mode 100644 packages/native/src/__tests__/watch.test.mjs diff --git a/.agents/skills/nix-build/SKILL.md b/.agents/skills/nix-build/SKILL.md index 0646c0474..b965e9301 100644 --- a/.agents/skills/nix-build/SKILL.md +++ b/.agents/skills/nix-build/SKILL.md @@ -3,11 +3,11 @@ name: nix-build description: Build any @singularity-forge/* package (or the full stack) via nix develop. Pass a package name like "pi-coding-agent", "native", "mcp-server", or "all" for a full core build. --- -All build commands in this repo must run inside `nix develop`. Never use bare cargo/bun/rustc. +All build commands in this repo must run inside `nix develop`. Never use bare cargo/rustc. For a single package: ``` -nix develop --command bash -c "bun run --filter @singularity-forge/ build" +nix develop --command bash -c "npm run --workspace=@singularity-forge/ build" ``` For the full core build (native + all TS packages): diff --git a/packages/native/src/__tests__/watch.test.mjs b/packages/native/src/__tests__/watch.test.mjs new file mode 100644 index 000000000..d917fea43 --- /dev/null +++ b/packages/native/src/__tests__/watch.test.mjs @@ -0,0 +1,88 @@ +import { describe, test } from "vitest"; +import assert from "node:assert/strict"; +import * as path from "node:path"; +import { fileURLToPath } from "node:url"; +import * as fs from "node:fs"; +import * as os from "node:os"; + +const __dirname = path.dirname(fileURLToPath(import.meta.url)); +const { watchTree } = await import(path.resolve(__dirname, "../../dist/edit/index.js")); + +function makeTmpDir() { + return fs.mkdtempSync(path.join(os.tmpdir(), "sf-watch-test-")); +} + +function waitForEvents(root, options, predicate, timeoutMs = 3000) { + return new Promise((resolve, reject) => { + const watch = watchTree(root, options ?? null); + const timer = setTimeout(() => { + watch.stop(); + reject(new Error(`Timed out after ${timeoutMs}ms waiting for events in ${root}`)); + }, timeoutMs); + + watch.on("events", (events) => { + if (predicate(events)) { + clearTimeout(timer); + watch.stop(); + resolve(events); + } + }); + }); +} + +describe("native edit: watchTree()", () => { + test("detects file creation", async ({ onTestFinished }) => { + const dir = makeTmpDir(); + onTestFinished(() => fs.rmSync(dir, { recursive: true, force: true })); + + const pending = waitForEvents( + dir, + { debounceMs: 50 }, + (events) => events.some((event) => event.path.endsWith("hello.txt")), + ); + + await new Promise((resolve) => setTimeout(resolve, 100)); + fs.writeFileSync(path.join(dir, "hello.txt"), "hi"); + + const events = await pending; + assert.ok(events.some((event) => event.path.endsWith("hello.txt"))); + }); + + test("respects ignore patterns", async ({ onTestFinished }) => { + const dir = makeTmpDir(); + onTestFinished(() => fs.rmSync(dir, { recursive: true, force: true })); + + const collected = []; + const watch = watchTree(dir, { ignore: ["*.log"], debounceMs: 50 }); + watch.on("events", (events) => collected.push(...events)); + + await new Promise((resolve) => setTimeout(resolve, 100)); + fs.writeFileSync(path.join(dir, "ignored.log"), "log data"); + fs.writeFileSync(path.join(dir, "kept.txt"), "text data"); + await new Promise((resolve) => setTimeout(resolve, 600)); + watch.stop(); + + assert.equal(collected.filter((event) => event.path.endsWith("ignored.log")).length, 0); + assert.ok(collected.some((event) => event.path.endsWith("kept.txt"))); + }); + + test("stop ends the watch", async ({ onTestFinished }) => { + const dir = makeTmpDir(); + onTestFinished(() => fs.rmSync(dir, { recursive: true, force: true })); + + const received = []; + const watch = watchTree(dir, { debounceMs: 50 }); + watch.on("events", (events) => received.push(...events)); + + await new Promise((resolve) => setTimeout(resolve, 100)); + fs.writeFileSync(path.join(dir, "before.txt"), "a"); + await new Promise((resolve) => setTimeout(resolve, 300)); + watch.stop(); + + const countAfterStop = received.length; + fs.writeFileSync(path.join(dir, "after.txt"), "b"); + await new Promise((resolve) => setTimeout(resolve, 300)); + + assert.equal(received.length, countAfterStop); + }); +}); diff --git a/scripts/base64-scan.sh b/scripts/base64-scan.sh index 5732ca36a..74f4f7610 100755 --- a/scripts/base64-scan.sh +++ b/scripts/base64-scan.sh @@ -102,7 +102,7 @@ should_scan() { *.zip|*.tar|*.gz|*.tgz|*.bz2|*.7z|*.rar|\ *.exe|*.dll|*.so|*.dylib|*.o|*.a|\ *.pdf|*.doc|*.docx|*.xls|*.xlsx|\ - *.lock|package-lock.json|pnpm-lock.yaml|bun.lock|\ + *.lock|package-lock.json|pnpm-lock.yaml|\ *.min.js|*.min.css|*.map|\ *.node|*.wasm) return 1 ;; diff --git a/scripts/secret-scan.sh b/scripts/secret-scan.sh index 741890e9f..1fb53d4a2 100755 --- a/scripts/secret-scan.sh +++ b/scripts/secret-scan.sh @@ -116,7 +116,7 @@ should_scan() { *.zip|*.tar|*.gz|*.tgz|*.bz2|*.7z|*.rar|\ *.exe|*.dll|*.so|*.dylib|*.o|*.a|\ *.pdf|*.doc|*.docx|*.xls|*.xlsx|\ - *.lock|package-lock.json|pnpm-lock.yaml|bun.lock|\ + *.lock|package-lock.json|pnpm-lock.yaml|\ *.min.js|*.min.css|*.map|\ *.node|*.wasm) return 1 ;;