Gajae-Code (gjc) is a Bun-workspace TypeScript monorepo with Rust natives. This file is the repo-local operating contract: architecture map, dev utilities, and the rules that are not derivable from docs/. For deep dives, start at docs/ (per-topic) and docs/tools/ (per-tool runtime docs).
Runtime is Bun (bun@<pinned in package.json>); everything runs from source via bun, and release binaries are compiled with packages/coding-agent/scripts/build-binary.ts.
Dependency direction (roughly bottom-up):
utils ─┬─▶ ai ─────┬─▶ agent ─▶ coding-agent (gjc CLI, primary product surface)
├─▶ tui ────┘ │
└─▶ natives (napi-rs ◀── crates/pi-natives)
└─▶ stats (dashboard), bridge-client, sdk surfaces
| Workspace | Role |
|---|---|
packages/coding-agent |
Main gjc CLI. Entry: src/cli.ts. Subsystems live in src/ (tools, tui, session, sdk, workflow, hooks, lsp, daemon, …). Unless stated otherwise, work targets this package. |
packages/agent |
Agent runtime: tool calling, state, orchestration. |
packages/ai |
Multi-provider LLM client with streaming. src/models.json is generated — never edit; regenerate via bun run generate-models. |
packages/tui |
Terminal UI library with differential rendering. |
packages/natives + packages/natives-<platform> |
napi-rs bindings over crates/pi-natives (text/image/grep/shell/pty). See docs/natives-*.md. |
packages/stats |
Local observability dashboard (gjc stats). |
packages/utils |
Shared utilities (@gajae-code/pi-utils): logger, isCompiledBinary, path/string helpers. |
packages/bridge-client |
OOO bridge client (docs/ooo-bridge-extension-contract.md). |
packages/*-benchmark |
Edit / orchestration-token benchmarks; not shipped. |
crates/ |
Rust: pi-natives, pi-shell/brush-* (vendored shell), pi-ast, pi-iso, git-daemon, gjc-sdk. Driven via bun scripts/run-rs-task.ts. |
python/gjc-sdk |
Python SDK (check:py-sdk, test:py-sdk). |
When the user says "agent" or asks why the agent behaves a certain way, they mean the coding-agent CLI implementation, not the assistant editing the repo.
Run the CLI from source — no build step needed:
bun run dev # run gjc from source (packages/coding-agent/src/cli.ts)
bun run dev -- <args> # e.g. bun run dev -- stats --help
bun run stats # gjc stats from sourceOne-time / environment setup:
bun run install:dev # bun install + workspace links + dev:link + setup defaults
bun run dev:link # symlink `gjc` on PATH to the source CLI (scripts/dev-link.ts)
bun run dev:doctor # verify PATH resolution of `gjc` points at this workspace
bun run install:defaults # (re)install bundled default definitionsVerification (never run tsc/npx tsc directly at repo root; use these):
bun run check # full TS + Rust checks (types, schemas, gates, workspaces)
bun run check:ts # TS-only aggregate
bun --cwd=packages/<pkg> run check # targeted package typecheck
bun test packages/<pkg>/test/<file>.test.ts # targeted tests — prefer this first
bun run test # full TS + Rust test suites (slow)
bun run lint / fmt / fix # biome + workspace variants; :rs suffix for RustGenerated artifacts — change the generator, then regenerate; check enforces sync:
bun run generate-schemas # schemas/*.schema.json (check:schemas)
bun run generate-models # packages/ai/src/models.json
bun run generate-plugins # plugins/ (check:plugins)
bun run generate-docs-index # coding-agent docs indexOther useful entry points:
bun run ci:test:smoke # --version/--help/--smoke-test fast sanity
bun run restart:sdk-broker # restart the local SDK broker
bun run conformance:run # ACP conformance
bun run bench:edit / bench:orchestration-tokens
bun run stats:sync / stats:tools / stats:edits # session-stats analysis (python3)Required rebrand/default-surface gates after workflow-definition changes:
bun scripts/check-visible-definitions.tsbun scripts/verify-g002-gates.tsbun scripts/rebrand-inventory.ts --strictbun test packages/coding-agent/test/default-gjc-definitions.test.ts
GJC exposes exactly four default workflow skills (deep-interview, ralplan, ultragoal, team; bundled at packages/coding-agent/src/defaults/gjc/skills/) and exactly four role agents (executor, architect, planner, critic; bundled at packages/coding-agent/src/prompts/agents/). Do not add, document, install, or route to additional defaults without an explicit product decision and gate update.
- Do not commit repo-visible
.gjcdefault definitions; runtime.gjcdiscovery covers local overrides. - Runtime state, plans, specs, and ledgers belong under
.gjc/. - Public commands, paths, and examples must use
gjcand.gjc; preserve upstream attribution in source comments where appropriate. - Keep source-bundled skills/agents in sync with tests/gates; do not rely on committed
.gjccopies. - Planning workflows (
deep-interview,ralplan) never execute implementation without explicit user approval; artifacts staypending approvaluntil then. - Subagent await timeouts are observation windows, not failure signals; inspect before cancelling.
- No
anyunless absolutely necessary. - Never use
ReturnType<>; write the actual type name. - No inline imports: no
await import(), noimport("pkg").Type. Top-level imports only. - Check
node_modulesfor external API types instead of guessing. - Prefer
export * from "./module"in barrel files; remove redundant paths on ambiguity. - Use ES
#privatefields; noprivate/protected/publicmodifiers except constructor parameter properties. - Use
Promise.withResolvers()instead ofnew Promise((resolve, reject) => ...). - Prompts live in static
.mdfiles imported withwith { type: "text" }; never build prompts inline.
| Operation | Use | Avoid |
|---|---|---|
| File read/write | Bun.file(), Bun.write() |
readFileSync, writeFileSync |
| Spawn simple commands | Bun Shell ($\cmd``) |
child_process |
| Sleep | Bun.sleep(ms) |
timeout promises |
| JSON5/JSONL | Bun.JSON5, Bun.JSONL |
ad-hoc parsers |
| String width/wrap | Bun.stringWidth, Bun.wrapAnsi |
custom ANSI wrapping |
Use namespace imports for Node modules (import * as fs from "node:fs/promises", same for path, os). Use node:fs/promises for directory ops; skip redundant parent-dir creation before Bun.write().
Spawn workers with the compile-safe hybrid pattern:
import { isCompiledBinary } from "@gajae-code/pi-utils";
const worker = isCompiledBinary()
? new Worker("./packages/<pkg>/src/<worker>.ts", { type: "module" })
: new Worker(new URL("./<worker>.ts", import.meta.url).href, { type: "module" });Every worker entry must also be listed as an extra compile entrypoint in packages/coding-agent/scripts/build-binary.ts. Validate new worker paths with the relevant smoke test; gjc --smoke-test covers the stats sync worker.
No console.log/console.warn/console.error in packages/coding-agent/ — it corrupts TUI rendering. Use the centralized logger from @gajae-code/pi-utils.
All text in tool renderers must be sanitized: replaceTabs(), truncateToWidth()/ui.truncate() with shared limits, shortenPath() for home paths, shared preview constants for previews. Apply to success, error, diff, and streaming render paths alike.
For UI/dashboard/TUI visual work, follow docs/ui-design-visual-qa.md before broad product-screen implementation.
Test externally observable contracts: behavior, output shape, state transitions, error mapping, regression-prone parsing boundaries.
Avoid placeholder tests, tautologies, broad not.toThrow() assertions, duplicated coverage, long-lived global mutations, and mock.module(). Prefer vi.spyOn(...) with cleanup. Compile-time guarantees belong in type checks, not runtime tests.
-
Always commit incrementally; atomic commits are preferred. One logical change per commit — never batch unrelated work.
-
For targeted branch / PR-like work, always open a PR targeting
dev. -
Commit messages use the lore format: conventional-commit subject, a short why-focused body, then structured trailers. Include only the trailers that apply.
feat(auth): switch session store from JWT to server-side sessions Client-side JWTs leaked user roles into browser storage. Server-side sessions let us revoke access instantly on permission changes. Lore-id: a1b2c3d4 Constraint: must support horizontal scaling -- use Redis-backed store Constraint: session TTL must not exceed 24h per compliance policy Rejected: JWT with short expiry | still leaks roles to client Rejected: encrypted JWT | adds decryption overhead on every request Confidence: high Scope-risk: wide Reversibility: migration-needed Directive: do not cache session objects at the application layer Tested: concurrent session creation under load Not-tested: Redis failover behavior Supersedes: f7e8d9c0 -
Package changelogs live at
packages/*/CHANGELOG.md; add entries under## [Unreleased], never edit released sections. -
Release flow:
bun run release(scripts/release.ts) after changelogs and verification are complete.