diff --git a/.claude/settings.json b/.claude/settings.json index b969289..aef5004 100644 --- a/.claude/settings.json +++ b/.claude/settings.json @@ -2,7 +2,8 @@ "permissions": { "allow": [ "Bash(git status:*)", "Bash(git diff:*)", "Bash(git log:*)", "Bash(git show:*)", - "Bash(git rev-parse:*)", "Bash(git add:*)", "Bash(git commit:*)" + "Bash(git rev-parse:*)", "Bash(git add:*)", "Bash(git commit:*)", + "Bash(bun install --frozen-lockfile)", "Bash(bun test:*)", "Bash(bun run typecheck)" ], "ask": [ "Bash(git push:*)", "Bash(git merge:*)", "Bash(git rebase:*)", "Bash(git switch:*)", diff --git a/.gemini/settings.json b/.gemini/settings.json index d699300..dd3a43a 100644 --- a/.gemini/settings.json +++ b/.gemini/settings.json @@ -3,7 +3,8 @@ "allowed": [ "run_shell_command(git status)", "run_shell_command(git diff)", "run_shell_command(git log)", "run_shell_command(git show)", "run_shell_command(git branch --list)", - "run_shell_command(git rev-parse)", "run_shell_command(git add)", "run_shell_command(git commit)" + "run_shell_command(git rev-parse)", "run_shell_command(git add)", "run_shell_command(git commit)", + "run_shell_command(bun install --frozen-lockfile)", "run_shell_command(bun test)", "run_shell_command(bun run typecheck)" ], "exclude": [ "run_shell_command(git push --force)", "run_shell_command(git push -f)", diff --git a/AGENTS.md b/AGENTS.md index 12cc17e..468c2a3 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -9,8 +9,8 @@ Deeper context on demand: `docs/ARCHITECTURE.md`, `docs/DECISIONS.md`, `docs/TOD ## Commands -TODO(maintainer): confirm commands once tooling exists. Planned (spec §6), none verified yet: -`bun install --frozen-lockfile`, `bun test`, `bun test test/.test.ts`, `bunx tsc --noEmit`. +Verified 2026-10-14 (see `docs/PERMISSIONS.md`): +`bun install --frozen-lockfile` · `bun test` · `bun test test/.test.ts` · `bun run typecheck`. Use `bun`/`bunx` — never `npm`, `npx`, `node` or `vitest`. ## Workflow diff --git a/bun.lock b/bun.lock new file mode 100644 index 0000000..ecac3fc --- /dev/null +++ b/bun.lock @@ -0,0 +1,66 @@ +{ + "lockfileVersion": 1, + "configVersion": 1, + "workspaces": { + "": { + "name": "mangabaka-seeder", + "devDependencies": { + "@types/bun": "^1.4.3", + "typescript": "^7.0.2", + }, + }, + }, + "packages": { + "@types/bun": ["@types/bun@1.4.3", "", { "dependencies": { "bun-types": "1.4.3" } }, "sha512-N67Pqy8JXbAY/j0tNraS3V5mubGvc6rrrNDmDCtZLlFzccbUJPDb0tvgUBC5q4m8ybkP93Jcn1QB+RYQ6PvBhw=="], + + "@types/node": ["@types/node@26.6.5", "", { "dependencies": { "undici-types": "~8.9.0" } }, "sha512-uMzsv9stDdofIZBx4UGh4Z9ZgXnGC9seRDscilBcTHAeOFk67MZg/k9bWVfO+bk4qZmG+l52hRnLwUiH2LnJTg=="], + + "@typescript/typescript-aix-ppc64": ["@typescript/typescript-aix-ppc64@7.0.2", "", { "os": "aix", "cpu": "ppc64" }, "sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ=="], + + "@typescript/typescript-darwin-arm64": ["@typescript/typescript-darwin-arm64@7.0.2", "", { "os": "darwin", "cpu": "arm64" }, "sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA=="], + + "@typescript/typescript-darwin-x64": ["@typescript/typescript-darwin-x64@7.0.2", "", { "os": "darwin", "cpu": "x64" }, "sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA=="], + + "@typescript/typescript-freebsd-arm64": ["@typescript/typescript-freebsd-arm64@7.0.2", "", { "os": "freebsd", "cpu": "arm64" }, "sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ=="], + + "@typescript/typescript-freebsd-x64": ["@typescript/typescript-freebsd-x64@7.0.2", "", { "os": "freebsd", "cpu": "x64" }, "sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw=="], + + "@typescript/typescript-linux-arm": ["@typescript/typescript-linux-arm@7.0.2", "", { "os": "linux", "cpu": "arm" }, "sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ=="], + + "@typescript/typescript-linux-arm64": ["@typescript/typescript-linux-arm64@7.0.2", "", { "os": "linux", "cpu": "arm64" }, "sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ=="], + + "@typescript/typescript-linux-loong64": ["@typescript/typescript-linux-loong64@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ=="], + + "@typescript/typescript-linux-mips64el": ["@typescript/typescript-linux-mips64el@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA=="], + + "@typescript/typescript-linux-ppc64": ["@typescript/typescript-linux-ppc64@7.0.2", "", { "os": "linux", "cpu": "ppc64" }, "sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA=="], + + "@typescript/typescript-linux-riscv64": ["@typescript/typescript-linux-riscv64@7.0.2", "", { "os": "linux", "cpu": "none" }, "sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ=="], + + "@typescript/typescript-linux-s390x": ["@typescript/typescript-linux-s390x@7.0.2", "", { "os": "linux", "cpu": "s390x" }, "sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw=="], + + "@typescript/typescript-linux-x64": ["@typescript/typescript-linux-x64@7.0.2", "", { "os": "linux", "cpu": "x64" }, "sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A=="], + + "@typescript/typescript-netbsd-arm64": ["@typescript/typescript-netbsd-arm64@7.0.2", "", { "os": "none", "cpu": "arm64" }, "sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA=="], + + "@typescript/typescript-netbsd-x64": ["@typescript/typescript-netbsd-x64@7.0.2", "", { "os": "none", "cpu": "x64" }, "sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA=="], + + "@typescript/typescript-openbsd-arm64": ["@typescript/typescript-openbsd-arm64@7.0.2", "", { "os": "openbsd", "cpu": "arm64" }, "sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ=="], + + "@typescript/typescript-openbsd-x64": ["@typescript/typescript-openbsd-x64@7.0.2", "", { "os": "openbsd", "cpu": "x64" }, "sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg=="], + + "@typescript/typescript-sunos-x64": ["@typescript/typescript-sunos-x64@7.0.2", "", { "os": "sunos", "cpu": "x64" }, "sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g=="], + + "@typescript/typescript-win32-arm64": ["@typescript/typescript-win32-arm64@7.0.2", "", { "os": "win32", "cpu": "arm64" }, "sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ=="], + + "@typescript/typescript-win32-x64": ["@typescript/typescript-win32-x64@7.0.2", "", { "os": "win32", "cpu": "x64" }, "sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g=="], + + "bun-types": ["bun-types@1.4.3", "", { "dependencies": { "@types/node": "*", "undici-types": "*" } }, "sha512-tinRJNGOmUN+tEAsn2y2e942n9JHRHR42iUYzDH75NmhJqVBokGSQkkfSFdNWwsANIdrK6ACH9t01M452YyTwg=="], + + "typescript": ["typescript@7.0.2", "", { "optionalDependencies": { "@typescript/typescript-aix-ppc64": "7.0.2", "@typescript/typescript-darwin-arm64": "7.0.2", "@typescript/typescript-darwin-x64": "7.0.2", "@typescript/typescript-freebsd-arm64": "7.0.2", "@typescript/typescript-freebsd-x64": "7.0.2", "@typescript/typescript-linux-arm": "7.0.2", "@typescript/typescript-linux-arm64": "7.0.2", "@typescript/typescript-linux-loong64": "7.0.2", "@typescript/typescript-linux-mips64el": "7.0.2", "@typescript/typescript-linux-ppc64": "7.0.2", "@typescript/typescript-linux-riscv64": "7.0.2", "@typescript/typescript-linux-s390x": "7.0.2", "@typescript/typescript-linux-x64": "7.0.2", "@typescript/typescript-netbsd-arm64": "7.0.2", "@typescript/typescript-netbsd-x64": "7.0.2", "@typescript/typescript-openbsd-arm64": "7.0.2", "@typescript/typescript-openbsd-x64": "7.0.2", "@typescript/typescript-sunos-x64": "7.0.2", "@typescript/typescript-win32-arm64": "7.0.2", "@typescript/typescript-win32-x64": "7.0.2" }, "bin": { "tsc": "bin/tsc" } }, "sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA=="], + + "undici-types": ["undici-types@8.12.1", "", {}, "sha512-nCiEcIx87oMw9u3Yzj2DuRBzv6p0e7akhy4HUPtpBiVWmAY3o6Phznst8QhmY1oLMttLgJ2tlAvsKuzCgBSoAw=="], + + "@types/node/undici-types": ["undici-types@8.9.0", "", {}, "sha512-KTDyRTYX8sWmKXAikPHHSyc63CRPETMctyjKFupcC6OBLXT3xsN0e9aF7m+mIXutFWpUXuedtowG7iLOzp0kQg=="], + } +} diff --git a/docs/PERMISSIONS.md b/docs/PERMISSIONS.md index de82caf..c85ccee 100644 --- a/docs/PERMISSIONS.md +++ b/docs/PERMISSIONS.md @@ -12,22 +12,20 @@ Run in this session. Only `worked` rows may appear in a tier, a tool file, or `A |---|---| | `git status`, `git log`, `git diff`, `git mv` | worked | | `bun --version` | worked (1.3.14) | -| `bun test` | failed — no test files yet (no `package.json`, no code) | -| `bun install --frozen-lockfile` | not run — no `package.json` / `bun.lock` yet | -| `bunx tsc --noEmit` | not run — `typescript` not installed yet | - -TODO(maintainer): confirm every row above that is not worked, once the Bun scaffold exists, then -add the verified ones to Allow here and in each tool file. +| `bun install --frozen-lockfile` | worked | +| `bun test` | worked | +| `bun run typecheck` (`tsc --noEmit`) | worked | ## Tiers | Tier | Commands | Why | |---|---|---| -| Allow | `git status`, `git diff`, `git log`, `git show`, `git branch --list`, `git rev-parse`, `git add`, `git commit` | Inspection and the dev loop; no prompt needed. | +| Allow | `git status`, `git diff`, `git log`, `git show`, `git branch --list`, `git rev-parse`, `git add`, `git commit`; `bun install --frozen-lockfile`, `bun test`, `bun run typecheck` | Inspection and the dev loop; no prompt needed. | | Ask | `git push`, `git merge`, `git rebase`, `git switch`, `git restore`, `git commit --amend`, `rm` (any); `bun add`, `bun remove`, `bun update`, `bun upgrade`; `bunx`, `npx` (download and run code, incl. `wrangler deploy`) | Leaves the machine, rewrites work, changes dependencies, or deploys — a human decides. | | Deny | `git push --force` / `-f` / `--force-with-lease`, `git reset --hard`, `git clean`, `git add -f` / `--force`, `git commit --no-verify` / `-n`, `rm -r` / `-R` / `-rf` / `-fr` / `-Rf` / `-fR` / `--recursive`, `sudo`; reading `.env`, `.env.*`, `.dev.vars`, `secrets/**` | Destroys work, skips checks, or exposes secrets. | ## Known gaps +- Codex: the allowed bun commands need no rule — they run inside the `workspace-write` sandbox. - Allowlists save prompts; they are not a security boundary. Use the tool's sandbox for isolation. - Prefix rules are bypassable: `git -C dir push`, flags placed later (`git push origin main --force`), `+refspec` pushes. Claude Code's `:*` matches at a word boundary, so each flag cluster must be diff --git a/package.json b/package.json new file mode 100644 index 0000000..48373fa --- /dev/null +++ b/package.json @@ -0,0 +1,18 @@ +{ + "name": "mangabaka-seeder", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "test": "bun test", + "typecheck": "tsc --noEmit", + "build:web": "bun build ./src/web/index.html --outdir dist/web --minify", + "build:userscript": "bun run scripts/build-userscript.ts", + "build": "bun run build:web && bun run build:userscript", + "start": "bun run src/server/bun.ts" + }, + "devDependencies": { + "@types/bun": "^1.4.3", + "typescript": "^7.0.2" + } +} diff --git a/src/core/version.ts b/src/core/version.ts new file mode 100644 index 0000000..90c1d35 --- /dev/null +++ b/src/core/version.ts @@ -0,0 +1,2 @@ +export const VERSION = "0.1.0"; +export const REPO_URL = "https://gitea.lucaswinther.info/lucasw89/mangabaka-seeder"; diff --git a/test/boundaries.test.ts b/test/boundaries.test.ts new file mode 100644 index 0000000..9fac9c7 --- /dev/null +++ b/test/boundaries.test.ts @@ -0,0 +1,35 @@ +import { expect, test } from "bun:test"; +import { Glob } from "bun"; + +// AGENTS.md: src/core runs in a browser, a userscript and Bun. +const CORE_FORBIDDEN: [RegExp, string][] = [ + [/\bdocument\b|\bwindow\b|\blocalStorage\b|\bsessionStorage\b/, "DOM global"], + [/\bBun\./, "Bun API"], + [/from\s+["']node:|import\(["']node:/, "node:* import"], + [/\bGM_\w+|\bGM\./, "GM_* API"], + [/from\s+["'](?:\.\.\/)+(?:web|server|userscript|cli)\//, "import from a front end"], +]; +// spec §4: these server files stay web-standard so a Workers entry can be added later. +const PORTABLE_SERVER = ["app.ts", "storage.ts", "rate-limit.ts", "config.ts", "assets-path.ts"]; +const PORTABLE_FORBIDDEN: [RegExp, string][] = [ + [/\bBun\./, "Bun API"], + [/from\s+["']node:|import\(["']node:/, "node:* import"], +]; + +async function violations(pattern: string, rules: [RegExp, string][], only?: string[]) { + const problems: string[] = []; + for await (const file of new Glob(pattern).scan(".")) { + if (only && !only.some((name) => file.endsWith(`/${name}`))) continue; + const text = await Bun.file(file).text(); + for (const [re, what] of rules) if (re.test(text)) problems.push(`${file}: ${what}`); + } + return problems; +} + +test("src/core uses no platform APIs and imports no front end", async () => { + expect(await violations("src/core/**/*.ts", CORE_FORBIDDEN)).toEqual([]); +}); + +test("portable server files use only web-standard APIs", async () => { + expect(await violations("src/server/*.ts", PORTABLE_FORBIDDEN, PORTABLE_SERVER)).toEqual([]); +}); diff --git a/test/version.test.ts b/test/version.test.ts new file mode 100644 index 0000000..6a0ba5e --- /dev/null +++ b/test/version.test.ts @@ -0,0 +1,7 @@ +import { expect, test } from "bun:test"; +import pkg from "../package.json"; +import { VERSION } from "../src/core/version"; + +test("core VERSION matches package.json", () => { + expect(VERSION).toBe(pkg.version); +}); diff --git a/tsconfig.json b/tsconfig.json new file mode 100644 index 0000000..756855f --- /dev/null +++ b/tsconfig.json @@ -0,0 +1,17 @@ +{ + "compilerOptions": { + "target": "ES2022", + "module": "ESNext", + "moduleResolution": "bundler", + "lib": ["ES2022", "DOM", "DOM.Iterable"], + "types": ["bun"], + "strict": true, + "noUncheckedIndexedAccess": true, + "noImplicitOverride": true, + "verbatimModuleSyntax": true, + "resolveJsonModule": true, + "skipLibCheck": true, + "noEmit": true + }, + "include": ["src", "test", "scripts"] +}