Imported from ensemblr-hq/ensemblr (
AGENTS.md). Install upstream withnpx skills add ensemblr-hq/ensemblr. Copyright stays with the author.
Agent Instructions
These instructions apply to the whole repository.
Repository Layout
This is a Bun workspaces monorepo.
| Path | What lives there |
|---|---|
apps/desktop/ |
Ensemblr, the Electron desktop app: its source, tests, scripts, docs, JSON Schemas, assets, Nix packaging, changelog, and release notes. |
apps/website/ |
The marketing and documentation site. Not started yet. |
packages/shared/ |
Code the apps share, such as UI pieces lifted out of the desktop app. Not started yet. |
The root holds only what the whole repository shares: the workspace manifest (package.json) and lockfile, the install settings (bunfig.toml; the toolchain itself is the Nix dev shell in flake.nix), the house Biome config, the monorepo-level scripts in scripts/, CI (.github/), agent tooling (.claude/, .codex/, .agents/, .mcp.json), Ensemblr's repository settings (.ensemblr/), the Nix flake entrypoint, and the community files. Keep it that way: anything that belongs to one app or package lives under that workspace's directory.
- Before working under
apps/desktop/, readapps/desktop/AGENTS.md. Its paths, and those in the scopedAGENTS.mdfiles beneath it, are relative toapps/desktop/. - When you write a path in prose, write it from the repository root, workspace prefix included —
apps/desktop/src/main/main.ts, notsrc/main/main.ts.
Workspaces
- The root
package.jsonworkspacesglobsapps/*andpackages/*; a directory becomes a workspace when it gets apackage.json. Name a new package@ensemblr/<name>, mark itprivate, and depend on another workspace with"workspace:*". The desktop app keeps the package nameensemblr, which its Electron identity and release tooling read. - Bun runs a workspace's own
preinstallandpostinstallonly when it first installs that workspace — not when a dependency changes, and not on an install with nothing to do. The root manifest'spreinstallandpostinstalltherefore run every workspace's (bun run --workspaces --if-present) on every install, the way a single-package root's ran before; on a first install they run twice, so keep them idempotent. - Each workspace owns its
check,typecheck, andtestscripts. The root's scripts of the same name run every workspace's (bun run --workspaces --if-present <script>);checkfirst runs the lockfile check and Biome over the whole tree (each file under its nearestbiome.json). - Install from anywhere with
bun installorbun ci; Bun installs every workspace and hoists dependencies into the rootnode_modules. Add a dependency to the workspace that uses it (bun add <pkg>inside that workspace's directory, orbun add <pkg> --cwd apps/desktop), never to the root manifest. - A new workspace manifest changes what
bun installlays out, so it moves the Nix deps hash: re-pin withapps/desktop/nix/update-pins.sh deps(thenix-depscheck reports the new hash on a pull request).
Project Naming
- This project was previously called
piductor, thenEnsemble. If agents find references topiductororEnsemblein code, documentation, branches, commits, issues, or planning notes, interpret them as references toEnsemblrunless the local context clearly says otherwise. The current product name isEnsemblr(domainensemblr.dev).
App Scaffolding Requires Current Official Docs
When scaffolding an app, project, framework integration, SDK integration, CLI setup, or cloud-service setup, agents must not rely on training data, memory, or recalled commands.
Required workflow:
- Inspect the local repo first so generated files and commands fit the existing project direction.
- Use Context7 MCP before selecting install steps, package names, CLI flags, templates, or generated-file structure.
- Start with
resolve-library-idfor the relevant library, framework, SDK, CLI, or cloud service unless the exact Context7 library ID is already known. - Call
query-docswith the selected library ID and the full scaffolding question. - If Context7 is unavailable, incomplete, or lacks the relevant tool, check the current official documentation online.
- Prefer official install directions, official starter templates, and official CLI tools such as documented
create,init, or generator commands. - Do not invent or guess package names, versions, CLI flags, templates, config keys, or setup steps.
- If official docs and local repo conventions conflict, preserve local conventions where possible and call out the tradeoff before making a risky change.
- In the final response, mention the documentation source and the exact official command or CLI path used.
Scaffold provenance guardrail:
- Do not hand-author generated app structure from memory when an official generator exists.
- Run the official generator in
.context/or another disposable directory first, then copy or adapt from that generated output. - If the generator conflicts with Bun, hooks, existing files, or other repo policy, stop and explain the conflict before choosing a workaround.
- Record scaffold provenance in the final response or a tracked audit note: documentation source, exact generator command, generated files used, and every intentional deviation.
- Treat manually added package names, versions, config keys, templates, or generated-file structure as invalid unless they are directly backed by current official docs, generator output, or an explicit user decision.
Package Manager Policy
This repository enforces Bun for JavaScript and TypeScript package management. Bun installs packages and runs package.json scripts; Node 24 remains the runtime (see .claude/rules/stack.md; the pin is engines.node in the root package.json), and Bun does not shim itself as node.
Run everything inside nix develop. The flake's dev shell (apps/desktop/nix/dev-shell.nix) is the only supported development environment: it puts Node 24, Bun, and the native-module toolchain first on PATH. Enter it with nix develop, or prefix a single command with nix develop -c <cmd>; it works from apps/desktop/ too, because Nix searches upward for flake.nix. No script checks the Node version or the host toolchain any more, so a command run outside the shell is unsupported rather than refused. Intel Macs (x86_64-darwin) get no shell, because nixpkgs dropped the platform.
- Use
bun installinstead ofnpm install,pnpm install, oryarn install. Usebun cifor a frozen install that must matchbun.lockexactly — it is what the workspace setup script runs, asnix develop -c bun ci. - Use
bun run <script>instead ofnpm run <script>,pnpm run <script>, oryarn run <script>. - Use
bunx <package>instead ofnpx,pnpx, oryarn dlx. - Use
bun add <package>(bun add -dfor a dev dependency) andbun remove <package>for dependency changes. - Do not create
package-lock.json,pnpm-lock.yaml,yarn.lock, or the binarybun.lockb.bun.lock(text) is the lockfile of record, andbunfig.tomlsetssaveTextLockfile = true. bun.lockstays atlockfileVersion: 1. Dependabot-core's Bun parser raises on a higher version, so dependency PRs would stop arriving. Bun 1.4 stamps 2 on a lockfile regenerated from scratch, so never deletebun.lockand reinstall to "refresh" it.bun run check:lockfile(part of the rootbun run check) enforces this;apps/desktop/docs/build-and-release.md#bun-and-noderecords how the current file was produced and why a plainbun installwithout a lockfile silently re-resolves the whole graph.- The root
package.jsonsetspackageManagerto the Bun version (bun@1.4.2); workspace manifests do not repeat it. bunfig.tomlpinslinker = "hoisted"because Forge'sPACKAGE_KEEP_*filters inapps/desktop/forge.config.tsmatch flatnode_modules/<pkg>/paths. Do not switch to the isolated linker.- The root
package.json#trustedDependenciesis the complete list of packages whose install scripts run (an explicit list replaces Bun's built-in allowlist).node-ptyis deliberately absent — its binding must be built by Forge against Electron's ABI, not by Bun against Node's. Never runbun pm trust --all. Bun readstrustedDependenciesandoverridesfrom the root manifest only, so both stay there even though every entry today serves the desktop app. - The local Codex hook
.codex/hooks/enforce-bun-package-manager.sh(plus the Claude hook.claude/hooks/enforce-bun.sh) block directnpm,npx,pnpm,pnpx,yarn,yarnpkg, and matchingcorepackpackage-manager calls. - Never set
PATHin.ensemblr/settings.toml's[environment_variables]. Ensemblr resolves a login-shellPATHfor the workspace directory only when noPATHkey is present, so defining one — even empty — silently disables the resolver. That resolver is an app feature for users' repositories; this repository does not depend on it for its toolchain. - Keep every setup and run script behind
nix develop -c, as.ensemblr/settings.tomldoes. The shell supplies the pinned Node, Bun, and compiler regardless of what the host's shell startup puts onPATH.
Biome Policy
This repository uses Biome instead of ESLint and Prettier.
- The root
biome.jsonholds the house style. A workspace's ownbiome.jsonsets"root": falseand"extends": "//", and adds only that workspace's ignores. - Run
bun run checkbefore finishing changes that touch JavaScript, TypeScript, JSX, TSX, CSS, or JSON. From the root it runs the lockfile check, Biome over the whole tree, and every workspace'scheck; inside a workspace it runs that workspace's checks. - Use
bun run check:fixto apply safe Biome fixes, including formatting and import organization. - Keep
bun run typecheckas a separate verification step for TypeScript type errors. From the root it runs every workspace'stypecheck. - Do not add ESLint or Prettier configuration unless the user explicitly asks for it.
Testing Policy
Vitest is the mandated test runner. Bun is the package manager, but bun test is Bun's own test runner, not Vitest — never run it here. Use bun run test, which invokes each workspace's test script (Vitest).
- Do not import from
bun:test. Do not add Jest, Mocha, or any other runner. - Run Vitest with
bunx vitestfrom inside a workspace (for examplebunx vitest run <file>inapps/desktop/). - The desktop app's suite layout, its
electron --testmain-process suites, and its DOM harness are inapps/desktop/AGENTS.md.
Scoped Agent Instructions
- This root file contains repository-wide defaults. Before editing a workspace or a subtree, check for the closest scoped
AGENTS.md; scoped instructions are more specific and override these general rules. apps/desktop/AGENTS.mdcarries the desktop app's policies — state management, Tailwind, localization, config schemas, documentation coverage, and code review — and points at its own scoped files underapps/desktop/src/.
Module And File Organization
- Check for shallow modules before adding new abstractions. Prefer deep modules: small public interfaces that hide meaningful implementation complexity.
- Avoid shallow modules: large interfaces, many props or methods, or wrappers that mostly pass values through without reducing complexity.
- Before introducing a helper, wrapper, hook, component, or service, ask whether it reduces the number of methods, simplifies parameters, or hides complexity inside the module. If not, inline it or consolidate it with a more appropriate module.
- Preserve required entrypoints such as Electron, Vite, renderer, preload, and framework route files. Move implementation behind them instead of moving paths that tooling expects.
- Organize files by the ownership model that fits the subtree: file type first where the scoped guidance says so, concern first where runtime services own the boundary, and root public entrypoints where a shared contract needs a stable import path.
- Keep broadly reusable primitives in shared locations, and keep feature-specific helpers, components, services, mocks, and fixtures under the feature or concern that owns them.
- When a concern spans multiple files, expose the intended public surface through a stable entrypoint and keep private helpers in sibling implementation files.
Type Organization
- Put exported types in the concern-owned type or contract module that represents their public boundary.
- Co-locate types with implementation only when they are not exported and are not used elsewhere.
- Prefer inline prop and options types when the shape is small and the inline type remains readable.
- For React components: if the component takes four or fewer props and the prop type is not exported, inline the prop shape directly on the function parameter. Lift it into a named
Propsinterface only when it grows beyond four props, is exported, or is referenced from more than one place. - Avoid creating one-off exported
Props,Options, or domain type names unless they are reused, part of a public module interface, or materially improve readability.
Documentation And Comments
- Every function carries a JSDoc block, and function bodies stay comment-free apart from a short why the code cannot express. See @.claude/rules/jsdoc.md and @.claude/rules/comments.md; they hold for every workspace unless that workspace's
AGENTS.mdnarrows them. - The desktop app's coverage rules (which declarations, which exclusions) are in
apps/desktop/AGENTS.md.
Issue Tracker And Pull Request Workflow
- Never mark a tracker issue as
Donefrom agent work. When implementation and verification are complete, move it toIn Reviewand let a human decide whether it is finished. - Never create a pull request unless the user explicitly asks for one in the current task. Do not infer PR creation from completed work, tracker-backed work, or branch readiness.
- When a change is backed by a tracker issue, put that issue's identifier in the branch name, the commits, and the PR title.
