Imported from wethegit/wtc (
AGENTS.md). Install upstream withnpx skills add wethegit/wtc. Copyright stays with the author.
Overview
wtc is a CLI/TUI tool for managing GitHub repos, AWS Amplify projects, and Teamwork tasks. Built with Bun + OpenTUI Solid.
Important Files
Before making changes, read these files for context:
plans/PLAN.md— Full project roadmap, architecture, conventions, all phases
Tech Stack
| Concern | Choice |
|---|---|
| Runtime | Bun |
| TUI | @opentui/solid + solid-js |
| CLI parser | yargs |
| Linter | oxlint |
| Formatter | oxfmt |
| Pre-commit | husky + lint-staged |
| Release versions | Changesets |
| Config validation | zod |
Conventions
Commits
Conventional commits: feat:, fix:, chore:, docs:, test:, refactor:
- Do not make a commit unless requested, always check-in with user first before making the commit so they can check diff and ask for changes
- Stop at a breakpoint before every commit and before continuing to the next main step. The user will inspect the diff themselves; do not commit or continue until they explicitly approve.
TypeScript
- strict mode, no
any, noascasts where avoidable - Named exports only, no default exports
verbatimModuleSyntax— useimport typefor type-only imports- Files:
kebab-case.ts - Types:
PascalCase - Functions:
camelCase
Comments
- Do not add comments that restate obvious code mechanics.
- Add comments when they explain why code exists, why a tradeoff was chosen, or how a non-obvious constraint affects implementation.
- Add comments for complex systems, security-sensitive flows, persistence formats, API boundaries, and cache/invalidation behavior.
- Prefer comments on TypeScript interfaces/types when they clarify domain meaning or intended usage.
- If code needs a comment to justify an abstraction, first consider whether the abstraction should be removed instead.
TUI Key Labels
- When displaying keys to users in status bars, help text, docs, or command descriptions, prefer glyphs for arrows:
←,→,↑,↓instead ofleft,right,up,down. - Keybinding registration strings should still use the names expected by
@opentui/keymapsuch asctrl+leftandctrl+right.
Solid TUI Rendering
- Prefer Solid control-flow primitives such as
<Switch>,<Match>,<Show>, and<For>for component-level conditional rendering and lists instead of nested JSX ternaries. - Small inline value conditionals are fine when they keep markup clearer than an extracted control-flow block.
File Organization / Helper Scope
- Keep helpers scoped to the smallest place that needs them.
- Put values/functions in
consts.tsonly when they are shared across modules or own environment-variable behavior. - Keep filenames, local paths, and one-module constants inside the module that uses them.
- Do not create helper functions for one-use expressions.
- Prefer inline local constants over private one-line functions.
- Manager modules should contain domain behavior, not generic wrappers around simple file reads/writes.
- Do not split one workflow into private helpers unless those helpers are reused or represent meaningful domain behavior; keep the workflow in one function and add a clarifying comment when needed.
Third-Party API Requests
- For read operations, use REST-style names like
getTeamworkProjectMetadata()rather thanload*orfetch*when callers should not care whether data comes from cache or network. - A
get*API should own the full read path: check cache when applicable, fetch from the service when needed, save cache when applicable, and return the result. - Keep shared HTTP behavior, such as base URLs, auth headers, JSON parsing, timeouts, and status handling, in a provider client module so individual endpoint modules do not duplicate it.
- Do not create separate cache-read/cache-write/fetch helpers for every endpoint unless those helpers are reused across endpoints or encode meaningful domain behavior.
Examples:
getCacheDir()belongs insrc/api/cache/consts.tsbecause it is shared and ownsWTC_CACHE_DIR.getUserConfigDir()belongs insrc/api/config/consts.tsbecause it ownsWTC_CONFIG_DIR.- Cache filenames like
"tui-state.json"belong insrc/api/cache/consts.tsunder theCACHEobject, not in the domain module. getStatePath()should not exist if it only appendsSTATE_FILEtogetCacheDir()in one module.formatUserConfig()is valid because Bun's YAML parser does not preserve comments, so config saves need explicit commented formatting.
Error Handling & Logging
- Use the structured logger for all noteworthy events (scope
domain.flow.action) - API layer owns logging: network/file I/O wrapped in try/catch with
logError+ rethrow/fallback - TUI/CLI callers catch only for user-facing messages, do not re-log what the API logged
- Fire-and-forget async calls must have a
.catch()handler - Do not log secrets
Cache I/O
- All cache filenames live in
src/api/cache/consts.tsunder theCACHEobject - All file I/O uses primitives from
src/api/cache/manager.ts, neverBun.file()orBun.write()directly
Code Quality
bun run checkmust pass (tsc)bun run lintmust pass- No
anytypes, no default exports - Never write tests. Do not add, modify, or create test files. Do not add DI, abstractions, exports, or any other code whose only purpose is to support testing.
NEVER run binary compiled files.
