- TypeScript 95%
- JavaScript 4.6%
- Makefile 0.4%
| Filename | Latest commit message | Latest commit date |
|---|---|---|
Mined from a sweep of the local per-repo agent configs. The useful half of that dump is which repos are worth dispatching remote work into; the system prompts themselves aren't worth vendoring, because every one of them is a CLAUDE.md that already lives in the repo being mounted — a sprite reads it at session start the same way Claude Code does locally, and a mount never drifts the way a copied prompt would. So this adds environments only, and the existing specialists attach to them. home-cloud is build-and-lint only. `make verify` (build → lint → check) is pure synthesis and runs fine in a sandbox, but the ansible playbooks, kubectl, and flux all reach the Mac hosts over Tailscale, which a sprite has no route to. There's no root package.json — `make install` is what installs the pinned @intentius/chant under chant/. jobban needs Elixir 1.19 + Erlang/OTP 28, several majors ahead of Debian's `elixir`, so the toolchain comes from mise at the versions the repo's own Dockerfile pins. `mix test` wants a live Postgres on localhost as postgres/postgres, which config/test.exs confirms, so the setup script starts the server and sets that password. Co-authored-by: Claude Opus 5 <noreply@anthropic.com> |
||
| scripts | ||
| src | ||
| .gitignore | ||
| .infisical.json | ||
| chant.config.ts | ||
| Makefile | ||
| OPERATIONS.md | ||
| package-lock.json | ||
| package.json | ||
| README.md | ||
| tsconfig.json | ||
agent-specs
Personal Fountain specs — agents, environments, vaults — kept independent of the Fountain codebase so the same manifest can be applied against any Fountain instance (local dev, hosted prod, a sprite I just spun up).
Declared as typed chant resources via the fountain lexicon (@intentius/chant-lexicon-fountain) rather than hand-written YAML — real import/export, editor autocompletion, and build-time lint (dangling environment references, open networking, secret-shaped literals, etc.) before anything reaches the API.
Layout
.
├── src/
│ ├── environments/ # one Environment per file
│ ├── vaults/ # one Vault per file
│ └── agents/
│ ├── teams/ # one folder per orchestrator (tech-lead, captain-picard, …)
│ └── specialists/ # one folder per discipline (engineering, growth, design, content, …)
├── chant.config.ts # declares the fountain lexicon
├── scripts/apply.mjs # applies dist/fountain.yaml via bulk POST /api/apply
└── .infisical.json # binds this repo to the right Infisical project
chant build walks src/, type-checks and lints every declared Environment / Vault / Agent, and serializes them to dist/fountain.yaml (ejectable — fountain apply -f dist/fountain.yaml accepts it verbatim). scripts/apply.mjs resolves the manifest's infisical:// secret URIs from the environment and sends it through fountain's bulk POST /api/apply.
Install
npm install # chant + the fountain lexicon
The fountain CLI itself is still needed for running conversations (fountain run, fountain conv ... — see below), not for applying this manifest:
make install # downloads fountain-darwin-arm64 to ~/.local/bin/fountain
Or grab a binary directly:
curl -L -o ~/.local/bin/fountain \
https://github.com/BinaryBourbon/fountain/releases/latest/download/fountain-darwin-arm64
chmod +x ~/.local/bin/fountain
Linux + amd64 variants are attached to the same release.
Authenticate
Two separate credentials, for two separate tools:
-
scripts/apply.mjs(reconciling this manifest) needsFOUNTAIN_TOKEN(andFOUNTAIN_ENDPOINTif not the hosted default) as environment variables — mint one viaPOST /api/auth/tokenor the account UI. AddFOUNTAIN_TOKENto Infisical (env=dev) somake applypicks it up the same way it already does forGITHUB_TOKENetc., or export it locally. -
The
fountainCLI (running conversations) uses its own login, unrelated to the above:fountain auth login # prompts for base URL + API key, writes ~/.fountain/credentials fountain auth whoami # confirmFor multi-instance setups use named profiles:
fountain auth login --profile <name>, thenFOUNTAIN_PROFILE=<name> make run AGENT=... PROMPT=...(or pass--profile).
Apply
Secrets are pulled from Infisical (env=dev, root path) via the Infisical CLI. The .infisical.json in this repo binds it to the right project; sign in once and make apply resolves the rest.
infisical login # one time
make apply # → infisical run --env=dev -- npm run apply
# (chant build --output dist/fountain.yaml --format json && node scripts/apply.mjs)
npm run apply is idempotent by name: create-if-new, update-by-name, and (opt-in, off by default) prune of chant-owned resources — every resource here carries the managed-by: chant metadata marker so ownership is unambiguous.
Prune deletes chant-owned resources the manifest no longer declares; hand-made resources have no ownership marker and are never touched. It's off by default so a partial build can't read as "delete everything I didn't emit":
PRUNE=1 make apply
Two layers of ${VAR} substitution
Same syntax, different scopes:
- apply-time —
secrets:entries resolve from Infisical (infisical://...), local env, or--varflags. That's how this repo stays free of literal tokens. - provision-time — every other
${VAR}(e.g.mcp_serversheaders) resolves at sprite spawn from the environment's secrets ∪ the conversation's vault.
For secrets: you can also use other external resolvers if you have the relevant CLI installed:
op://<vault>/<item>/<field>— 1Password CLIbws://<secret-uuid>— Bitwarden Secrets Manager CLIinfisical://<project?>/<env>/<path?>/<name>— Infisical CLI (currently in use)
Adding / editing a resource
Add or edit a .ts file in the matching src/ subdirectory:
import { Environment } from "@intentius/chant-lexicon-fountain";
const env = new Environment({
name: "team-env",
networking_type: "limited",
networking_config: { allowed_hosts: ["github.com"] },
metadata: { "managed-by": "chant" },
});
export { env as "team-env" };
The export name is the upsert key — always export under the resource's own name. chant derives each resource's logical name from the exporting identifier, and that logical name (not the name you pass to the constructor) is what fountainApply sends as the upsert key and what cross-resource references serialize to. Since a fountain name like team-env isn't a valid JS identifier, bind the constructor to a camelCase const and re-export it under the literal name; importers use the matching import { "team-env" as env } from "…".
Nothing upstream catches a mismatch — exporting as export const teamEnv = … type-checks, lints, and applies cleanly, it just creates a second resource named teamEnv alongside team-env instead of updating it. The whole estate diverged that way once, so scripts/check-names.mjs runs as part of npm run build and fails the build (and therefore the apply) if any resource's export name and declared name disagree.
The filename is just for humans. Re-running make apply reconciles in place. npx chant build alone (no --output) is a fast way to lint a change without applying it.
The lexicon's own reference (fields, lint rules, secrets model, locked-sandbox posture) lives at intentius.io/chant/lexicons/fountain; the underlying manifest format is documented in the Fountain repo's help pages.