refactor: restructure repo for cleaner GitHub landing page

Reduce visible top-level items from 21 to 14:
- Untrack test-results/, tmp/, public symlink (added to .gitignore)
- Move agent-teams/ → docs/agent-teams/
- Move mobile-test/ → test/mobile/
- Move tools/remotion/ → scripts/remotion/
- Move eslint.config.js, vitest.config.ts → config/

All path references updated across CLAUDE.md, package.json,
.prettierignore, vitest configs, and capture scripts.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
arkonandClaude Opus 4.6 committed 2026-04-03 16:23:54 +02:00
1 parent 5078f5251d
commit 7101e64800
82 files changed
+53 -46

No files matched your search

+6 -1
View File
@@ -48,7 +48,12 @@ Thumbs.db
# Generated output # Generated output
out/ out/
screenshots-echo-diag/ screenshots-echo-diag/
tools/remotion/out/ scripts/remotion/out/
# Artifacts that should not be tracked
test-results/
tmp/
public
# Claude Code plan tracking # Claude Code plan tracking
plan.json plan.json
+1 -1
View File
@@ -6,4 +6,4 @@ src/web/public/app.js
src/web/public/styles.css src/web/public/styles.css
src/web/public/mobile.css src/web/public/mobile.css
src/web/public/index.html src/web/public/index.html
tools/ scripts/remotion/
+4 -4
View File
@@ -80,7 +80,7 @@ Codeman is a Claude Code session manager with web interface and autonomous Ralph
**CI**: `.github/workflows/ci.yml` runs `typecheck`, `lint`, `format:check` on push to master/main and on PRs (Node 22). Tests excluded (they spawn tmux). **CI**: `.github/workflows/ci.yml` runs `typecheck`, `lint`, `format:check` on push to master/main and on PRs (Node 22). Tests excluded (they spawn tmux).
**Code style**: Prettier (`singleQuote: true`, `printWidth: 120`, `trailingComma: "es5"`). ESLint flat config (`eslint.config.js`) allows `no-console`, warns on `@typescript-eslint/no-explicit-any`. Ignores: `app.js`, `scripts/**/*.mjs`, `src/web/public/vendor/**`, `tools/**`, `remotion/**`. **Code style**: Prettier (`singleQuote: true`, `printWidth: 120`, `trailingComma: "es5"`). ESLint flat config (`config/eslint.config.js`) allows `no-console`, warns on `@typescript-eslint/no-explicit-any`. Ignores: `app.js`, `scripts/**/*.mjs`, `src/web/public/vendor/**`, `scripts/remotion/**`.
## Common Gotchas ## Common Gotchas
@@ -136,7 +136,7 @@ Codeman is a Claude Code session manager with web interface and autonomous Ralph
**Hook events**: Claude Code hooks trigger via `/api/hook-event`. Key events: `permission_prompt`, `elicitation_dialog`, `idle_prompt`, `stop`, `teammate_idle`, `task_completed`. See `src/hooks-config.ts`. **Hook events**: Claude Code hooks trigger via `/api/hook-event`. Key events: `permission_prompt`, `elicitation_dialog`, `idle_prompt`, `stop`, `teammate_idle`, `task_completed`. See `src/hooks-config.ts`.
**Agent Teams**: `TeamWatcher` polls `~/.claude/teams/`, matches to sessions via `leadSessionId`. Teammates are in-process threads appearing as subagents. Enable: `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1`. See `agent-teams/`. **Agent Teams**: `TeamWatcher` polls `~/.claude/teams/`, matches to sessions via `leadSessionId`. Teammates are in-process threads appearing as subagents. Enable: `CLAUDE_CODE_EXPERIMENTAL_AGENT_TEAMS=1`. See `docs/agent-teams/`.
**Circuit breaker**: Prevents respawn thrashing. States: `CLOSED` → `HALF_OPEN` → `OPEN`. Reset: `/api/sessions/:id/ralph-circuit-breaker/reset`. **Circuit breaker**: Prevents respawn thrashing. States: `CLOSED` → `HALF_OPEN` → `OPEN`. Reset: `/api/sessions/:id/ralph-circuit-breaker/reset`.
@@ -204,7 +204,7 @@ npx vitest run -t "pattern" # By name (SAFE)
**Ports**: Pick unique ports manually. Search `const PORT =` before adding new tests. **Ports**: Pick unique ports manually. Search `const PORT =` before adding new tests.
**Respawn tests**: Use `MockSession` from `test/respawn-test-utils.ts`. **Route tests**: `app.inject()` in `test/routes/`. **Mobile tests**: Playwright suite in `mobile-test/` (135 device profiles). **Respawn tests**: Use `MockSession` from `test/respawn-test-utils.ts`. **Route tests**: `app.inject()` in `test/routes/`. **Mobile tests**: Playwright suite in `test/mobile/` (135 device profiles).
## Screenshots ## Screenshots
@@ -226,7 +226,7 @@ Target: 20 sessions, 50 agent windows at 60fps. Limits in `src/config/`: termina
## References ## References
Deep-dive docs in `docs/`: `respawn-state-machine.md`, `ralph-wiggum-guide.md`, `claude-code-hooks-reference.md`, `terminal-anti-flicker.md`, `opencode-integration.md`, `qr-auth-plan.md`, `orchestrator-loop-architecture.md`, `browser-testing-guide.md`. Agent Teams: `agent-teams/README.md`. SSE events: `src/web/sse-events.ts` + `constants.js`. Deep-dive docs in `docs/`: `respawn-state-machine.md`, `ralph-wiggum-guide.md`, `claude-code-hooks-reference.md`, `terminal-anti-flicker.md`, `opencode-integration.md`, `qr-auth-plan.md`, `orchestrator-loop-architecture.md`, `browser-testing-guide.md`. Agent Teams: `docs/agent-teams/README.md`. SSE events: `src/web/sse-events.ts` + `constants.js`.
## Scripts ## Scripts
+1 -2
View File
@@ -22,8 +22,7 @@ export default tseslint.config(
'src/web/public/vendor/**', 'src/web/public/vendor/**',
'src/web/public/app.js', 'src/web/public/app.js',
'scripts/**/*.mjs', 'scripts/**/*.mjs',
'tools/**', 'scripts/remotion/**',
'remotion/**',
], ],
} }
); );
@@ -1,7 +1,11 @@
import { resolve } from 'node:path';
import { defineConfig } from 'vitest/config'; import { defineConfig } from 'vitest/config';
const root = resolve(import.meta.dirname, '..');
export default defineConfig({ export default defineConfig({
test: { test: {
root,
globals: true, globals: true,
environment: 'node', environment: 'node',
include: ['test/**/*.test.ts'], include: ['test/**/*.test.ts'],
File renamed without changes.
File renamed without changes.
File renamed without changes.
+5 -5
View File
@@ -15,12 +15,12 @@
"dev": "tsx src/index.ts web", "dev": "tsx src/index.ts web",
"web": "node dist/index.js web", "web": "node dist/index.js web",
"clean": "rm -rf dist", "clean": "rm -rf dist",
"test": "vitest run", "test": "vitest run --config config/vitest.config.ts",
"test:watch": "vitest", "test:watch": "vitest --config config/vitest.config.ts",
"test:coverage": "vitest run --coverage", "test:coverage": "vitest run --config config/vitest.config.ts --coverage",
"typecheck": "tsc --noEmit", "typecheck": "tsc --noEmit",
"lint": "eslint 'src/**/*.ts'", "lint": "eslint --config config/eslint.config.js 'src/**/*.ts'",
"lint:fix": "eslint 'src/**/*.ts' --fix", "lint:fix": "eslint --config config/eslint.config.js 'src/**/*.ts' --fix",
"format": "prettier --write 'src/**/*.ts'", "format": "prettier --write 'src/**/*.ts'",
"format:check": "prettier --check 'src/**/*.ts'", "format:check": "prettier --check 'src/**/*.ts'",
"capture:subagents": "node scripts/capture-subagent-screenshots.mjs", "capture:subagents": "node scripts/capture-subagent-screenshots.mjs",
-1
View File
@@ -1 +0,0 @@
tools/remotion/public
+1 -1
View File
@@ -428,7 +428,7 @@ const SUBAGENT_ACTIVITY = {
'agent-002': [ 'agent-002': [
{ type: 'tool', tool: 'Glob', input: { pattern: 'test/**/*.test.ts' }, timestamp: new Date().toISOString(), agentId: 'agent-002' }, { type: 'tool', tool: 'Glob', input: { pattern: 'test/**/*.test.ts' }, timestamp: new Date().toISOString(), agentId: 'agent-002' },
{ type: 'tool', tool: 'Read', input: { file_path: '/home/arkon/codeman/test/respawn-test-utils.ts' }, timestamp: new Date().toISOString(), agentId: 'agent-002' }, { type: 'tool', tool: 'Read', input: { file_path: '/home/arkon/codeman/test/respawn-test-utils.ts' }, timestamp: new Date().toISOString(), agentId: 'agent-002' },
{ type: 'tool', tool: 'Read', input: { file_path: '/home/arkon/codeman/vitest.config.ts' }, timestamp: new Date().toISOString(), agentId: 'agent-002' }, { type: 'tool', tool: 'Read', input: { file_path: '/home/arkon/codeman/config/vitest.config.ts' }, timestamp: new Date().toISOString(), agentId: 'agent-002' },
{ type: 'message', role: 'assistant', text: 'Analyzing test patterns: MockSession, unique ports, fileParallelism: false...', timestamp: new Date().toISOString(), agentId: 'agent-002' }, { type: 'message', role: 'assistant', text: 'Analyzing test patterns: MockSession, unique ports, fileParallelism: false...', timestamp: new Date().toISOString(), agentId: 'agent-002' },
], ],
}; };
+14 -14
View File
@@ -9,7 +9,7 @@
* *
* Usage: node scripts/capture-video-screenshots.mjs * Usage: node scripts/capture-video-screenshots.mjs
* Port: 3198 (static file server) * Port: 3198 (static file server)
* Output: remotion/public/ (6 PNGs) * Output: scripts/scripts/remotion/public/ (6 PNGs)
*/ */
import { chromium } from 'playwright'; import { chromium } from 'playwright';
@@ -21,7 +21,7 @@ import { fileURLToPath } from 'url';
const __dirname = fileURLToPath(new URL('.', import.meta.url)); const __dirname = fileURLToPath(new URL('.', import.meta.url));
const PROJECT_ROOT = join(__dirname, '..'); const PROJECT_ROOT = join(__dirname, '..');
const PUBLIC_DIR = join(PROJECT_ROOT, 'src', 'web', 'public'); const PUBLIC_DIR = join(PROJECT_ROOT, 'src', 'web', 'public');
const OUTPUT_DIR = join(PROJECT_ROOT, 'remotion', 'public'); const OUTPUT_DIR = join(PROJECT_ROOT, 'scripts', 'remotion', 'public');
const PORT = 3198; const PORT = 3198;
const DESKTOP_VIEWPORT = { width: 1920, height: 1080 }; const DESKTOP_VIEWPORT = { width: 1920, height: 1080 };
@@ -516,7 +516,7 @@ async function captureDesktopWelcome(browser) {
path: join(OUTPUT_DIR, 'desktop-welcome.png'), path: join(OUTPUT_DIR, 'desktop-welcome.png'),
fullPage: false, fullPage: false,
}); });
console.log(' Saved: remotion/public/desktop-welcome.png'); console.log(' Saved: scripts/remotion/public/desktop-welcome.png');
} finally { } finally {
await context.close(); await context.close();
} }
@@ -545,7 +545,7 @@ async function captureDesktopClaude(browser) {
path: join(OUTPUT_DIR, 'desktop-claude.png'), path: join(OUTPUT_DIR, 'desktop-claude.png'),
fullPage: false, fullPage: false,
}); });
console.log(' Saved: remotion/public/desktop-claude.png'); console.log(' Saved: scripts/remotion/public/desktop-claude.png');
} finally { } finally {
await context.close(); await context.close();
} }
@@ -575,7 +575,7 @@ async function captureDesktopBothClaude(browser) {
path: join(OUTPUT_DIR, 'desktop-both-claude.png'), path: join(OUTPUT_DIR, 'desktop-both-claude.png'),
fullPage: false, fullPage: false,
}); });
console.log(' Saved: remotion/public/desktop-both-claude.png'); console.log(' Saved: scripts/remotion/public/desktop-both-claude.png');
} finally { } finally {
await context.close(); await context.close();
} }
@@ -605,7 +605,7 @@ async function captureDesktopBothOpencode(browser) {
path: join(OUTPUT_DIR, 'desktop-both-opencode.png'), path: join(OUTPUT_DIR, 'desktop-both-opencode.png'),
fullPage: false, fullPage: false,
}); });
console.log(' Saved: remotion/public/desktop-both-opencode.png'); console.log(' Saved: scripts/remotion/public/desktop-both-opencode.png');
} finally { } finally {
await context.close(); await context.close();
} }
@@ -634,7 +634,7 @@ async function captureMobileClaude(browser) {
path: join(OUTPUT_DIR, 'mobile-claude.png'), path: join(OUTPUT_DIR, 'mobile-claude.png'),
fullPage: false, fullPage: false,
}); });
console.log(' Saved: remotion/public/mobile-claude.png'); console.log(' Saved: scripts/remotion/public/mobile-claude.png');
} finally { } finally {
await context.close(); await context.close();
} }
@@ -663,7 +663,7 @@ async function captureMobileOpencode(browser) {
path: join(OUTPUT_DIR, 'mobile-opencode.png'), path: join(OUTPUT_DIR, 'mobile-opencode.png'),
fullPage: false, fullPage: false,
}); });
console.log(' Saved: remotion/public/mobile-opencode.png'); console.log(' Saved: scripts/remotion/public/mobile-opencode.png');
} finally { } finally {
await context.close(); await context.close();
} }
@@ -706,12 +706,12 @@ async function main() {
console.log('All 6 screenshots captured!'); console.log('All 6 screenshots captured!');
console.log('='.repeat(60)); console.log('='.repeat(60));
console.log('\nOutput files:'); console.log('\nOutput files:');
console.log(' remotion/public/desktop-welcome.png'); console.log(' scripts/remotion/public/desktop-welcome.png');
console.log(' remotion/public/desktop-claude.png'); console.log(' scripts/remotion/public/desktop-claude.png');
console.log(' remotion/public/desktop-both-claude.png'); console.log(' scripts/remotion/public/desktop-both-claude.png');
console.log(' remotion/public/desktop-both-opencode.png'); console.log(' scripts/remotion/public/desktop-both-opencode.png');
console.log(' remotion/public/mobile-claude.png'); console.log(' scripts/remotion/public/mobile-claude.png');
console.log(' remotion/public/mobile-opencode.png'); console.log(' scripts/remotion/public/mobile-opencode.png');
} catch (err) { } catch (err) {
console.error('\nFatal error:', err.message); console.error('\nFatal error:', err.message);
console.error(err.stack); console.error(err.stack);
File renamed without changes.
File renamed without changes.
File renamed without changes.
-4
View File
@@ -1,4 +0,0 @@
{
"status": "failed",
"failedTests": []
}
@@ -18,20 +18,20 @@ Validates Codeman's mobile UI across 135 devices, covering:
```bash ```bash
# Run all mobile tests # Run all mobile tests
npx vitest run --config mobile-test/vitest.config.ts npx vitest run --config test/mobile/vitest.config.ts
# Run a single test file # Run a single test file
npx vitest run --config mobile-test/vitest.config.ts mobile-test/keyboard.test.ts npx vitest run --config test/mobile/vitest.config.ts test/mobile/keyboard.test.ts
# Quick mode — 6 representative devices, skip full matrix # Quick mode — 6 representative devices, skip full matrix
CI_QUICK=1 npx vitest run --config mobile-test/vitest.config.ts CI_QUICK=1 npx vitest run --config test/mobile/vitest.config.ts
# Full device matrix only (135 devices) # Full device matrix only (135 devices)
npx vitest run --config mobile-test/vitest.config.ts mobile-test/device-matrix.test.ts npx vitest run --config test/mobile/vitest.config.ts test/mobile/device-matrix.test.ts
# Update visual baselines (delete old baselines, re-run) # Update visual baselines (delete old baselines, re-run)
rm -rf mobile-test/snapshots/*.png rm -rf test/mobile/snapshots/*.png
npx vitest run --config mobile-test/vitest.config.ts mobile-test/visual-regression.test.ts npx vitest run --config test/mobile/vitest.config.ts test/mobile/visual-regression.test.ts
``` ```
## Test Files ## Test Files
@@ -126,7 +126,7 @@ Swipe simulation includes intermediate move points and timing to satisfy `SwipeH
Uses `pixelmatch` + `pngjs` (both in devDeps) for pixel-level comparison: Uses `pixelmatch` + `pngjs` (both in devDeps) for pixel-level comparison:
- **Baselines** stored in `mobile-test/snapshots/` (git-tracked) - **Baselines** stored in `test/mobile/snapshots/` (git-tracked)
- **Tolerance**: 0.5% pixel diff (handles anti-aliasing) - **Tolerance**: 0.5% pixel diff (handles anti-aliasing)
- **On failure**: `.actual.png` and `.diff.png` generated (gitignored) - **On failure**: `.actual.png` and `.diff.png` generated (gitignored)
- **Update**: Delete baseline PNGs and re-run — new baselines auto-created - **Update**: Delete baseline PNGs and re-run — new baselines auto-created
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -96,7 +96,7 @@ export const SUBAGENT = {
export const VISUAL = { export const VISUAL = {
DEFAULT_THRESHOLD: 0.1, DEFAULT_THRESHOLD: 0.1,
MAX_DIFF_PERCENT: 0.5, MAX_DIFF_PERCENT: 0.5,
SNAPSHOT_DIR: 'mobile-test/snapshots', SNAPSHOT_DIR: 'test/mobile/snapshots',
} as const; } as const;
// Visual regression breakpoints (width values for screenshot comparison) // Visual regression breakpoints (width values for screenshot comparison)
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -166,19 +166,19 @@ describe('Visual Regression', () => {
* To update visual baselines: * To update visual baselines:
* *
* 1. Delete the specific snapshot: * 1. Delete the specific snapshot:
* rm mobile-test/snapshots/<name>.png * rm test/mobile/snapshots/<name>.png
* *
* 2. Re-run the test: * 2. Re-run the test:
* npx vitest run mobile-test/visual-regression.test.ts * npx vitest run test/mobile/visual-regression.test.ts
* *
* 3. Review the new baseline: * 3. Review the new baseline:
* The test will create a fresh .png and pass (isNewBaseline = true). * The test will create a fresh .png and pass (isNewBaseline = true).
* *
* To update ALL baselines: * To update ALL baselines:
* rm -rf mobile-test/snapshots/ && npx vitest run mobile-test/visual-regression.test.ts * rm -rf test/mobile/snapshots/ && npx vitest run test/mobile/visual-regression.test.ts
* *
* Diff images for failed comparisons are saved as <name>.diff.png * Diff images for failed comparisons are saved as <name>.diff.png
* alongside the baseline in mobile-test/snapshots/. * alongside the baseline in test/mobile/snapshots/.
*/ */
it('placeholder — see comments above for baseline update instructions', () => { it('placeholder — see comments above for baseline update instructions', () => {
expect(true).toBe(true); expect(true).toBe(true);
@@ -1,10 +1,14 @@
import { resolve } from 'node:path';
import { defineConfig } from 'vitest/config'; import { defineConfig } from 'vitest/config';
const root = resolve(import.meta.dirname, '../..');
export default defineConfig({ export default defineConfig({
test: { test: {
root,
globals: true, globals: true,
environment: 'node', environment: 'node',
include: ['mobile-test/**/*.test.ts'], include: ['test/mobile/**/*.test.ts'],
setupFiles: ['./test/setup.ts'], setupFiles: ['./test/setup.ts'],
fileParallelism: false, fileParallelism: false,
testTimeout: 60_000, testTimeout: 60_000,
Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 144 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 147 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 148 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB