Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .commitlintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"scope-enum": [
2,
"always",
["repo", "nvim", "wezterm", "tmux", "clrs", "swatch", "scripts", "shared", "global"]
["repo", "nvim", "wezterm", "kitty", "tmux", "clrs", "swatch", "scripts", "shared", "global"]
],
"scope-empty": [2, "never"],
"subject-empty": [2, "never"],
Expand Down
3 changes: 3 additions & 0 deletions .github/workflows/code-checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,9 @@ jobs:
node-version: 24
cache: pnpm

- name: Install LuaJIT
run: brew install luajit

- name: Install dependencies
run: pnpm install --frozen-lockfile

Expand Down
117 changes: 49 additions & 68 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,54 +12,51 @@
- Root package scripts are the repository's public command surface. Keep them
limited to repository-wide workflows and Forge-owned domains; do not add root
aliases that only forward to one package's domain-specific command.
- Workspace packages expose the shared `ensure`, `build`, `lint:check`,
`lint:fix`, `typecheck`, and `test` scripts when applicable so root aggregation
discovers them automatically. `ensure` prepares missing external build inputs
without refreshing valid existing input. Package-specific maintenance commands
remain in that package and are invoked with
- Workspace packages expose `ensure`, `build`, and `sync` when applicable. Root
lifecycle commands aggregate those package-owned scripts. `ensure`
prepares missing external build inputs without refreshing valid existing
input. Package-specific maintenance commands remain in that package and are invoked with
`pnpm --filter <package> <command>`.
- Packages must not install configuration directly into user directories.
Installation targets remain explicit in `forge.config.yaml`; do not auto-scan
`packages/`. Package-owned persistent runtime state lives under `~/.forge/`.
- Packages own syncing of their runtime artifacts into community-standard user
directories. `forge.config.yaml#modules` explicitly registers enabled
configuration manifests. Domain-relative sources resolve from the manifest
directory; Forge aggregates and applies their installation plans.
- Swatch's built catalog is the sole Theme data source. Its persistent state
`~/.forge/swatch/current.json` stores only the selected Theme ID. `current` and
`current --json` initialize the catalog default only when that state is
missing; `current --path` never creates it, and `use` writes only an explicitly
validated selection. Invalid or dangling state must fail and can only be
repaired by an explicit valid selection.
`${XDG_STATE_HOME:-$HOME/.local/state}/swatch/current.json` stores only the
selected Theme ID. `current` and `current --json` initialize the catalog
default only when that state is missing; `current --path` never creates it,
and `use` writes only an explicitly validated selection. Invalid or dangling
state must fail and can only be repaired by an explicit valid selection.
- Swatch integrations consume Theme data through the public `swatch current
--json` interface and use `current --path` only to register state watchers.
They must not duplicate catalog/state validation. Runtime discovery must start
from the current shell startup files, discard inherited shell-derived paths,
and never cache discovered paths across consumer configuration reloads.
- Workspace packages that produce CLIs are build-first: runtime commands use
their complete `dist/` output and must not execute TypeScript sources
directly.
- There is no CI workflow yet. Add one only when a real need appears.
- `forge.config.yaml` is the single source of truth for `brew`, `links`, and
`build` tasks. Do not hard-code new targets inside scripts. Config sources
live under `configs/`.
- `.github/workflows/code-checks.yml` runs the repository checks in CI.
- `forge.config.yaml` owns repository-wide Homebrew requirements and the explicit
config module registry. Config manifests own generic `sync` entries; packages
do not expose Forge-specific manifests.

## Scripts Layout

- `scripts/` root holds only CLI entrypoints. Entrypoints are thin: parse args,
call into lib/ or a domain folder, handle exit code. No business logic.
- Commands map 1:1 to entrypoint files (`pnpm sync` -> `scripts/sync.ts`).
- Commands map 1:1 to entrypoint files (`pnpm start` -> `scripts/start.ts`).
- Shared infrastructure goes in `scripts/lib/` (config loading, path helpers).
- Domain logic goes in a domain folder (`builders/`, future `doctor/`, etc.).

## Commands

- Use `pnpm install` if dependencies need refresh; the repo pins `pnpm@10.29.3`
via `packageManager`.
- Use `pnpm start` for full local setup after dependencies are installed. It
runs `brew`, `build`, then `sync` and stops at the first failed step.
- Use `pnpm brew` to install Homebrew taps, formulae, and casks declared under
`brew:` in `forge.config.yaml`. The command is idempotent and skips already
installed entries.
- Use `pnpm start` for full local setup after dependencies are installed. It runs
`brew`, `build`, then `sync`, stopping at the first failed stage.
- Use `pnpm sync:preview` for a read-only link plan.
- Do not use `pnpm setup`; that is pnpm's built-in global setup command, not
this repo's flow.
- Use `pnpm ensure` to prepare and validate workspace build prerequisites.
- Use `pnpm build` to ensure prerequisites, build workspace packages, and then
run the builders declared under `build:` in `forge.config.yaml`.
- Use `pnpm sync:preview` to preview configured symlinks before applying them
with `pnpm sync`; do not use `pnpm link`, which is pnpm's built-in package
linking command.
- Run `pnpm test` to run Forge tests and tests in workspace packages that define
a `test` script.
- Run `pnpm check` for full non-mutating verification: repository-wide linting,
Expand All @@ -73,50 +70,34 @@
`pnpm --filter @forge/swatch schemes:check`; do not add a root forwarding
alias for them.

## Build System

- `scripts/build.ts` is a dispatcher: it looks up builders by name in an
explicit registry and calls them. Adding a builder means adding a file under
`scripts/builders/` and registering it in the registry. Builders with options
validate them with their own zod `optsSchema`.
- Config-level schema only validates base task shape (`builder`/`source`/
`output`); each builder validates its own `opts`.
- Forge-generated regular files carry a `GENERATED_BY_FORGE` marker, and
`writeGeneratedFile` in `scripts/builders/shared.ts` only replaces regular
files that contain it. Never remove the marker or bypass this check.
- Unknown `{{ token }}` placeholders fail loudly; each templating builder
defines its own token set.
- The `zshenv` builder may initialize the configured machine-local environment
file when missing, but it must never overwrite or delete an existing one.
- The `zsh-runtime` builder explicitly links package-owned CLI and completion
artifacts into root runtime outputs. It derives `PATH` and `fpath` directories
from each declared output and writes the final `.zshrc` from explicitly
ordered sources; it must not scan workspace packages or assume fixed runtime
folders.
- Newly created runtime links use relative targets. An existing symlink is kept
when it resolves to the declared source, even if its stored target is absolute.
The generated runtime fragment contains build-time-resolved absolute discovery
paths and must be rebuilt after moving the repository.
- Zsh sources are split into explicitly ordered files under `configs/zsh/`.
The `zsh-runtime` builder assembles them into the Forge-owned
`dist/zsh/.zshrc`; it must not auto-scan the source directory. Persistent Zsh
data and completion dumps live together under `~/.forge/zsh`, leaving
`dist/zsh` disposable.

## Sync Script
## Convergence

- TypeScript scripts are run with `tsx`; do not add a build step just to
execute them.
- Do not auto-scan `configs/` unless the repo gains a concrete convention for
targets.
- Modules are enabled only through `forge.config.yaml#modules`; never scan
directories for manifests or infer module identity and type from a path.
- Module names and normalized manifest paths must be globally unique. Modules
use repository-relative manifest paths and load in declaration order, but
conflicting outputs always fail rather than allowing later modules to override
earlier ones.
- `sync` is always an array. Entry names must be unique within a domain and final
targets globally unique. Forge merges every domain's entries into one plan
before writing anything.
- The linker is intentionally conservative: already-correct symlinks are
skipped, conflicting symlinks or real files/directories fail, and nothing is
overwritten.
- Sync plans every configured link before writing. If any target conflicts,
`pnpm sync` must fail without creating any links from that plan.
- `pnpm sync:preview` reports conflicts without failing because local machines
may already have unmanaged dotfiles; `pnpm sync` is the command that must
fail on conflicts.
- Forge plans every configured link before writing. If any target conflicts,
`pnpm start` must fail without creating any links from that plan.
- Build only generates artifacts inside the repository; domains without
generated artifacts do not define a build step. Sync is the only lifecycle
that exposes repository content in user directories.
- Packages that produce CLIs sync commands and integrations through their own
lifecycle without root knowledge of package layouts. Root sync runs package
sync scripts before applying links from registered config manifests.
- Zsh uses `~/.zshenv` to set `ZDOTDIR` to
`${XDG_CONFIG_HOME:-$HOME/.config}/zsh`. History lives under XDG state and
completion data under XDG cache. User commands and completions use standard
XDG bin/data locations.
- Keep `pnpm sync:preview` in the verification path when changing link targets.

## Formatting And Hooks
Expand All @@ -134,5 +115,5 @@
YAML files are formatted with Prettier and staged Lua files with StyLua.
- Husky `commit-msg` runs `pnpm exec commitlint --edit "$1"`.
- Commit messages must use conventional types from `.commitlintrc.json` and a
non-empty scope. Allowed scopes are `repo`, `nvim`, `wezterm`, `tmux`,
non-empty scope. Allowed scopes are `repo`, `nvim`, `wezterm`, `kitty`, `tmux`,
`clrs`, `swatch`, `scripts`, `shared`, and `global`.
Loading
Loading