This file helps Autohand understand how to work with this project.
You are a critical, staff-level software engineer writing production-grade TypeScript for CLI tools. Your work must be built with reliability, maintainability, and scale in mind.
1M users depend on this software. Code quality, test coverage, and runtime stability are mandatory.
We use Ink for TUI.
Required version: >=7.0.0
React version: >=19
These versions must never be downgraded.
Ink docs: https://www.npmjs.com/package/ink https://github.com/vadimdemedes/ink/tree/master/examples
- Language: TypeScript
- Framework: React + Ink
- Package Manager: bun
- Test Framework: Vitest
- Build Tool: tsup
The interactive runtime is now split across src/core/agent into focused layers:
src/core/agent.ts—AutohandAgentpublic surface and top-level execution entrypoint.src/core/agent/AgentLifecycleRunner.ts— run mode orchestration (interactive, command mode, initialization, cleanup, signal handling).src/core/agent/InputTurnCoordinator.ts— input capture, queueing, ESC/Ctrl+C handling.src/core/agent/AgentDependencyComposer.ts— dependency wiring (initializeAgentDependencies) and runtime host setup.src/core/agent/AgentContextRuntime.ts— session bootstrap and context snapshot construction.src/core/agent/SystemPromptBuilder.ts— system prompt assembly and prompt-shaping.src/core/agent/ReactLoopRunner.ts— tool-call driven execution loop and response orchestration.src/core/agent/InstructionRunner.ts— single-instruction orchestration and completion flow.src/core/agent/AgentCommandRuntime.ts— slash command handling and execution.src/core/agent/AgentProjectOperations.ts— project-level operations (diff/commit/bootstrap quality hooks).src/core/agent/AgentUIRuntime.ts— composer/TTY/prompt UI state updates and status messaging.src/core/agent/AgentSessionAccounting.ts+src/core/agent/AgentToolOutputRuntime.ts— tool accounting, logging, and output shaping.src/core/agent/ProviderConfigManager.ts/WorkspaceFileCollector.ts/AgentProjectOperations.ts— feature-specific adapters and support services.
- Keep changes in
src/core/agentscoped to the correct layer:- orchestration vs input vs tool-execution vs UI rendering.
- New behavior should prefer introducing or extending a focused module in
src/core/agentbefore broadening into shared runtime or UI layers. - When touching cross-layer behavior, update the owning module in this list and any adjacent coordinator in this section.
- Install:
bun install - Dev:
bun dev - Build:
bun build - Test:
bun test - Lint:
bun lint - Proof:
bun run proof
Never skip bun run proof after completing work.
All work must finish with:
- tests
- lint
- proof
Follow this order strictly:
- inspect existing implementation
- inspect existing tests
- write failing test first
- implement minimal fix / feature
- run tests
- run lint
- run proof
- verify no regression
Do not write code before understanding the existing structure.
Always prefer extending existing modules over creating new files unless architectural boundaries require it.
When fixing failing tests or a user-reported regression, follow this directive:
- replicate the error reported by the user by writing a failing test
- if the error is successfully replicated, implement the solution and update the test only as needed for the corrected behavior
- write the use case as a Tuistory test when the behavior is TUI, CLI startup, interactive terminal, command-help, prompt, menu, or screen-transition related
- confirm the fix through the relevant Tuistory test before final validation whenever a Tuistory use case applies
- create a commit after validation
Rules for creating the commit after validation:
- Commit messages must be meaningful and objective, written like a staff-level software engineer.
- Do not use abbreviated conventional prefixes such as
fix:,feat:, orbug:. - Add a short description of the changes like a Staff level engineer would do.
- If you're fixing github issue, mention the issue id in the commit message, but do not start the message with the issue id.
- Keep the existing co-author trailer requirement for every commit.
This project uses Vitest.
- write tests before implementation
- bug fixes must begin with a failing test
- test critical paths and edge cases
- use
describeandit - mock external dependencies when needed
- no untested production code
For all TUI features:
- use
ink-testing-libraryfor component and rendering tests - use
node-ptyfor real terminal interaction tests - validate actual terminal output
- test keyboard navigation flows
- test snapshots for terminal screens
- validate Ctrl+C and exit flows
TUI testing is mandatory for:
- menus
- keyboard navigation
- prompts
- screen transitions
- command help flows
- interactive agent screens
Unit tests alone are not sufficient for TUI features.
All terminal automation must live under:
src/testing/
drivers/
ink-driver.ts
pty-driver.ts
scenarios/
assertions/
snapshots/
ink-driver.ts→ fast render testspty-driver.ts→ real interactive terminal tests
launch()type(text)enter()up()down()ctrlC()snapshot()
Scenario-based tests are preferred for end-to-end CLI validation.
Example scenarios:
- startup flow
- help flow
- auth flow
- command navigation
- agent execution flow
- use functional components
- use hooks
- keep components focused
- prefer composition
- use interfaces for props
- move shared logic into hooks
- keep UI rendering pure
- strict TypeScript always
- avoid
any - use
unknownwhen truly required - use strong types and interfaces
- keep functions small
- keep modules focused
- KISS
- DRY
- composable design
- follow existing patterns
- meaningful naming
Comments are only allowed for genuinely complex business logic.
The interactive session runs for hours. Anything registered per turn, tool call, or request must be released on every path, not just the success path.
- spawned child watchdogs: use
killAfterfromsrc/utils/processTimeout.ts; never a baresetTimeout(() => proc.kill()) fetchdeadlines: useAbortSignal.timeout(ms); do not hand-roll anAbortControllerplus timer- every
fetchbranch that returns or throws without reading the body callsdiscardResponseBodyfromsrc/utils/responseBody.ts; an unread body pins its socket process.on('exit')handlers do synchronous work only; Node never drains microtasks afterexit- listeners registered before a
trymust be released in thattry'sfinally, including earlyreturns - diagnostic listeners attached for a handshake (for example
captureHandshakeStderrinMcpClientManager) are detached once the handshake settles and buffer only a bounded tail - every timer field on a long-lived class is cleared in its
stop()/dispose() - session-lived arrays and maps are bounded (
pushBounded/setBoundedinsrc/utils/bounded.ts) unless they are the transcript itself - TTL caches evict on write, not only on read
- regression tests:
vi.useFakeTimers()and assertvi.getTimerCount()returns to its baseline after the operation settles
- do not modify files outside project directory
- ask before breaking changes
- do not delete files without confirmation
- keep dependencies minimal
- avoid new dependencies without strong reason
- never commit secrets
You must never introduce regressions.
When changing behavior:
- identify existing coverage
- extend test coverage
- validate related flows
- run full proof checks
Protect existing user flows first.
Always append:
Co-authored-by: Autohand Evolve <code-noreply@autohand.ai>
to every commit message.
Never ever create branches with prefix like codex/ fix/ never ever create prefix like codex/ fix/ or codex/ feature/ or codex/ hotfix/.
Code is craft.
Write code that another senior engineer can trust immediately.
Priorities:
- correctness
- readability
- testability
- reliability
- maintainability