add project setup

This commit is contained in:
Noah Waldner
2026-02-27 10:54:10 +01:00
parent b99821cd10
commit fc6a74ab73
24 changed files with 4019 additions and 10 deletions
+12
View File
@@ -0,0 +1,12 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
+40
View File
@@ -0,0 +1,40 @@
name: CI
on:
push:
branches: [master, main]
pull_request:
jobs:
ci:
name: Typecheck & Lint (Node ${{ matrix.node-version }})
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20, 22]
steps:
- uses: actions/checkout@v4
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'npm'
- name: Install dependencies
run: npm ci
- name: Type check
run: npm run typecheck
- name: Lint
run: npm run lint
- name: Format check
run: npm run format:check
# Note: The test suite is intentionally excluded from CI.
# Tests spawn real tmux sessions and require a full system environment.
# Run tests locally with: npx vitest run test/<file>.test.ts
+8
View File
@@ -41,6 +41,14 @@ Thumbs.db
*.tmp
*.temp
# Generated output
out/
screenshots-echo-diag/
tools/remotion/out/
# Claude Code plan tracking
plan.json
# Unfinished TUI (local development only)
src/tui/
.claude/
+1
View File
@@ -0,0 +1 @@
22
+9
View File
@@ -0,0 +1,9 @@
dist/
coverage/
node_modules/
src/web/public/vendor/
src/web/public/app.js
src/web/public/styles.css
src/web/public/mobile.css
src/web/public/index.html
tools/
+8
View File
@@ -0,0 +1,8 @@
{
"singleQuote": true,
"semi": true,
"tabWidth": 2,
"printWidth": 100,
"trailingComma": "es5",
"endOfLine": "lf"
}
+100
View File
@@ -0,0 +1,100 @@
# Contributing to Codeman
## Prerequisites
- **Node.js 22** (use `nvm use` — `.nvmrc` is included)
- **tmux** — required for session management
- **Claude CLI** — installed and accessible in `$PATH`
## First-Time Setup
```bash
# 1. Clone and enter the repo
git clone https://github.com/Ark0N/Codeman.git
cd Codeman
# 2. Pin Node version
nvm use
# 3. Install dependencies (also links packages/xterm-zerolag-input workspace)
npm install
# 4. Start the dev server
npm run dev
# Open http://localhost:3000
```
## Development Commands
| Command | Purpose |
|---------|---------|
| `npm run dev` | Dev server at localhost:3000 |
| `npm run typecheck` | TypeScript type check |
| `npm run lint` | ESLint |
| `npm run lint:fix` | Auto-fix lint issues |
| `npm run format` | Prettier format |
| `npm run format:check` | Check formatting (used in CI) |
| `npm run build` | Production build |
| `npm run clean` | Remove dist/ |
## Running Tests Safely
> **CRITICAL:** If you are running inside a Codeman-managed tmux session, **never** run the full test suite — it spawns and kills tmux sessions and will crash your own session.
```bash
# Safe: run a single test file
npx vitest run test/<file>.test.ts
# Safe: run tests matching a name pattern
npx vitest run -t "pattern"
# DANGEROUS — only run outside of Codeman:
# npx vitest run
```
Test files use unique ports starting at 3150. Search `const PORT =` before adding a new test file.
## Code Style
- **TypeScript strict mode** — all strict flags are enabled in `tsconfig.json`. Code must pass `npm run typecheck` with zero errors.
- **ESLint** — run `npm run lint` before submitting a PR. Use `npm run lint:fix` for auto-fixable issues.
- **Prettier** — run `npm run format` to format source files. CI enforces `format:check`.
- **Import conventions:**
- Utilities: `import { LRUMap, stripAnsi } from './utils'`
- Types: `import type { SessionState } from './types'`
- Config: `import { MAX_TERMINAL_BUFFER_SIZE } from './config/buffer-limits'`
## Architecture Quick Reference
| Concern | File |
|---------|------|
| Session/PTY | `src/session.ts` |
| Web server (~105 routes) | `src/web/server.ts` |
| All TypeScript types | `src/types.ts` |
| Frontend (vanilla JS) | `src/web/public/app.js` |
| Respawn state machine | `src/respawn-controller.ts` |
| Task/Ralph loop | `src/ralph-loop.ts`, `src/ralph-tracker.ts` |
See `CLAUDE.md` for the full architecture reference.
## PR Checklist
- [ ] `npm run typecheck` passes
- [ ] `npm run lint` passes (or issues are documented)
- [ ] `npm run format:check` passes
- [ ] Relevant test file updated or added
- [ ] No full test suite run inside a Codeman session
- [ ] tmux safety respected (no blind `tmux kill-session`)
## Repository Layout
```
src/ TypeScript source
test/ Vitest tests (run individually)
scripts/ Build and utility scripts
packages/ Local npm workspaces (xterm-zerolag-input)
tools/ Dev utilities (Remotion video generation)
docs/ Architecture docs and planning documents
mobile-test/ Playwright mobile test suite
agent-teams/ Agent Teams feature docs
```
+28
View File
@@ -0,0 +1,28 @@
// @ts-check
import eslint from '@eslint/js';
import tseslint from 'typescript-eslint';
export default tseslint.config(
eslint.configs.recommended,
tseslint.configs.recommended,
{
rules: {
'no-console': 'warn',
'no-debugger': 'error',
// Relax some rules that conflict with existing patterns
'@typescript-eslint/no-explicit-any': 'warn',
'@typescript-eslint/no-unused-vars': 'off', // TypeScript compiler already handles this
},
},
{
ignores: [
'dist/**',
'node_modules/**',
'coverage/**',
'src/web/public/vendor/**',
'src/web/public/app.js',
'scripts/**/*.mjs',
'remotion/**',
],
}
);
+3744 -5
View File
File diff suppressed because it is too large Load Diff
+16 -5
View File
@@ -10,17 +10,24 @@
},
"scripts": {
"postinstall": "node scripts/postinstall.js",
"build": "tsc && chmod +x dist/index.js && mkdir -p dist/web dist/templates dist/web/public/vendor && cp -r src/web/public dist/web/ && cp src/templates/case-template.md dist/templates/ && cp node_modules/xterm/css/xterm.css dist/web/public/vendor/ && npx esbuild node_modules/xterm/lib/xterm.js --minify --outfile=dist/web/public/vendor/xterm.min.js && npx esbuild node_modules/xterm-addon-fit/lib/xterm-addon-fit.js --minify --outfile=dist/web/public/vendor/xterm-addon-fit.min.js && cp node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js dist/web/public/vendor/xterm-addon-webgl.min.js && npx esbuild node_modules/xterm-addon-unicode11/lib/xterm-addon-unicode11.js --minify --outfile=dist/web/public/vendor/xterm-addon-unicode11.min.js && npx esbuild dist/web/public/app.js --minify --drop:console --outfile=dist/web/public/app.js --allow-overwrite && npx esbuild dist/web/public/styles.css --minify --outfile=dist/web/public/styles.css --allow-overwrite && npx esbuild dist/web/public/mobile.css --minify --outfile=dist/web/public/mobile.css --allow-overwrite && for f in dist/web/public/*.js dist/web/public/*.css dist/web/public/*.html dist/web/public/vendor/*.js dist/web/public/vendor/*.css; do [ -f \"$f\" ] && gzip -9 -k -f \"$f\" && { brotli -9 -k -f \"$f\" 2>/dev/null || true; }; done",
"build": "node scripts/build.mjs",
"start": "node dist/index.js",
"dev": "tsx src/index.ts",
"dev": "tsx src/index.ts web",
"web": "node dist/index.js web",
"clean": "rm -rf dist",
"test": "vitest run",
"test:watch": "vitest",
"test:coverage": "vitest run --coverage",
"typecheck": "tsc --noEmit",
"lint": "eslint 'src/**/*.ts'",
"lint:fix": "eslint 'src/**/*.ts' --fix",
"format": "prettier --write 'src/**/*.ts'",
"format:check": "prettier --check 'src/**/*.ts'",
"capture:subagents": "node scripts/capture-subagent-screenshots.mjs"
},
"workspaces": [
"packages/*"
],
"keywords": [
"claude",
"claude-code",
@@ -40,15 +47,12 @@
"@fastify/compress": "^8.3.1",
"@fastify/cookie": "^11.0.2",
"@fastify/static": "^8.0.0",
"@remotion/cli": "4.0.429",
"@remotion/transitions": "4.0.429",
"chalk": "^5.3.0",
"chokidar": "^3.6.0",
"commander": "^12.1.0",
"fastify": "^5.1.0",
"node-pty": "^1.1.0",
"qrcode": "^1.5.4",
"remotion": "4.0.429",
"uuid": "^10.0.0",
"web-push": "^3.6.7",
"xterm": "^5.3.0",
@@ -58,6 +62,9 @@
"zod": "^4.3.6"
},
"devDependencies": {
"@eslint/js": "^9.0.0",
"@remotion/cli": "4.0.429",
"@remotion/transitions": "4.0.429",
"@types/node": "^20.19.33",
"@types/pngjs": "^6.0.5",
"@types/react": "^19.2.14",
@@ -66,12 +73,16 @@
"@vitest/coverage-v8": "^4.0.18",
"agent-browser": "^0.6.0",
"esbuild": "^0.27.3",
"eslint": "^9.0.0",
"pixelmatch": "^6.0.0",
"playwright": "^1.58.0",
"pngjs": "^7.0.0",
"prettier": "^3.4.0",
"puppeteer": "^24.36.0",
"remotion": "4.0.429",
"tsx": "^4.15.0",
"typescript": "^5.9.3",
"typescript-eslint": "^8.0.0",
"vitest": "^4.0.18"
},
"engines": {
+53
View File
@@ -0,0 +1,53 @@
#!/usr/bin/env node
/**
* Build script for Codeman.
* Extracted from the package.json one-liner for readability and debuggability.
*
* Steps:
* 1. TypeScript compilation
* 2. Copy static assets (web/public, templates)
* 3. Build vendor xterm bundles
* 4. Minify frontend assets (app.js, styles.css, mobile.css)
* 5. Compress with gzip + brotli
*/
import { execSync } from 'child_process';
import { fileURLToPath } from 'url';
import { join } from 'path';
const ROOT = join(fileURLToPath(import.meta.url), '..', '..');
function run(label, cmd) {
console.log(`\n[build] ${label}`);
execSync(cmd, { stdio: 'inherit', cwd: ROOT, shell: true });
}
// 1. TypeScript compilation
run('tsc', 'tsc');
run('chmod dist/index.js', 'chmod +x dist/index.js');
// 2. Copy static assets
run('prepare dirs', 'mkdir -p dist/web dist/templates dist/web/public/vendor');
run('copy web assets', 'cp -r src/web/public dist/web/');
run('copy template', 'cp src/templates/case-template.md dist/templates/');
// 3. Vendor xterm bundles
run('xterm css', 'cp node_modules/xterm/css/xterm.css dist/web/public/vendor/');
run('xterm js', 'npx esbuild node_modules/xterm/lib/xterm.js --minify --outfile=dist/web/public/vendor/xterm.min.js');
run('xterm-addon-fit', 'npx esbuild node_modules/xterm-addon-fit/lib/xterm-addon-fit.js --minify --outfile=dist/web/public/vendor/xterm-addon-fit.min.js');
run('xterm-addon-webgl', 'cp node_modules/xterm-addon-webgl/lib/xterm-addon-webgl.js dist/web/public/vendor/xterm-addon-webgl.min.js');
run('xterm-addon-unicode11', 'npx esbuild node_modules/xterm-addon-unicode11/lib/xterm-addon-unicode11.js --minify --outfile=dist/web/public/vendor/xterm-addon-unicode11.min.js');
// 4. Minify frontend assets
run('minify app.js', 'npx esbuild dist/web/public/app.js --minify --drop:console --outfile=dist/web/public/app.js --allow-overwrite');
run('minify styles.css', 'npx esbuild dist/web/public/styles.css --minify --outfile=dist/web/public/styles.css --allow-overwrite');
run('minify mobile.css', 'npx esbuild dist/web/public/mobile.css --minify --outfile=dist/web/public/mobile.css --allow-overwrite');
// 5. Compress with gzip + brotli
run(
'compress',
`for f in dist/web/public/*.js dist/web/public/*.css dist/web/public/*.html dist/web/public/vendor/*.js dist/web/public/vendor/*.css; do` +
` [ -f "$f" ] && gzip -9 -k -f "$f" && { brotli -9 -k -f "$f" 2>/dev/null || true; }; done`
);
console.log('\n✓ Build complete');

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 41 KiB

Before

Width:  |  Height:  |  Size: 37 KiB

After

Width:  |  Height:  |  Size: 37 KiB

Before

Width:  |  Height:  |  Size: 39 KiB

After

Width:  |  Height:  |  Size: 39 KiB

Before

Width:  |  Height:  |  Size: 57 KiB

After

Width:  |  Height:  |  Size: 57 KiB

Before

Width:  |  Height:  |  Size: 21 KiB

After

Width:  |  Height:  |  Size: 21 KiB

Before

Width:  |  Height:  |  Size: 22 KiB

After

Width:  |  Height:  |  Size: 22 KiB