Files
Codewalkers/docs/git-process-logging.md
Lukas May 34578d39c6 refactor: Restructure monorepo to apps/server/ and apps/web/ layout
Move src/ → apps/server/ and packages/web/ → apps/web/ to adopt
standard monorepo conventions (apps/ for runnable apps, packages/
for reusable libraries). Update all config files, shared package
imports, test fixtures, and documentation to reflect new paths.

Key fixes:
- Update workspace config to ["apps/*", "packages/*"]
- Update tsconfig.json rootDir/include for apps/server/
- Add apps/web/** to vitest exclude list
- Update drizzle.config.ts schema path
- Fix ensure-schema.ts migration path detection (3 levels up in dev,
  2 levels up in dist)
- Fix tests/integration/cli-server.test.ts import paths
- Update packages/shared imports to apps/server/ paths
- Update all docs/ files with new paths
2026-03-03 11:22:53 +01:00

137 lines
4.5 KiB
Markdown

# Git, Process, and Logging Modules
Three infrastructure modules supporting agent execution.
## Git Module (`apps/server/git/`)
Manages git worktrees for isolated agent workspaces.
### Architecture
- **Port**: `WorktreeManager` interface
- **Adapter**: `SimpleGitWorktreeManager` using simple-git library
### WorktreeManager Methods
| Method | Purpose |
|--------|---------|
| `create(id, branch, baseBranch?)` | Create worktree with new branch (default base: 'main') |
| `remove(id)` | Clean up worktree directory |
| `list()` | All worktrees including main |
| `get(id)` | Specific worktree by ID |
| `diff(id)` | Changed files vs HEAD |
| `merge(id, targetBranch)` | Merge worktree branch into target |
### Worktree Storage
Worktrees stored in `.cw-worktrees/` subdirectory of the repo. Each agent gets a worktree at `.cw-worktrees/agent/<alias>/`.
### Merge Flow
1. Check out target branch
2. `git merge <source> --no-edit`
3. On success: emit `worktree:merged`
4. On conflict: `git merge --abort`, emit `worktree:conflict` with conflicting file list
5. Restore original branch
### BranchManager (`apps/server/git/branch-manager.ts`)
- **Port**: `BranchManager` interface
- **Adapter**: `SimpleGitBranchManager` using simple-git
| Method | Purpose |
|--------|---------|
| `ensureBranch(repoPath, branch, baseBranch)` | Create branch from base if it doesn't exist (idempotent) |
| `mergeBranch(repoPath, source, target)` | Merge via ephemeral worktree, returns conflict info |
| `diffBranches(repoPath, base, head)` | Three-dot diff between branches |
| `deleteBranch(repoPath, branch)` | Delete local branch (no-op if missing) |
| `branchExists(repoPath, branch)` | Check local branches |
| `remoteBranchExists(repoPath, branch)` | Check remote tracking branches (`origin/<branch>`) |
`remoteBranchExists` is used by `registerProject` and `updateProject` to validate that a project's default branch actually exists in the cloned repository before saving.
### Project Clones
- `cloneProject(url, destPath)` — Simple git clone wrapper
- `ensureProjectClone(project, workspaceRoot)` — Idempotent: checks if clone exists, clones if not
- `getProjectCloneDir(name, id)` — Canonical path: `repos/<sanitized-name>-<id>/`
### Events Emitted
`worktree:created`, `worktree:removed`, `worktree:merged`, `worktree:conflict`
---
## Process Module (`apps/server/process/`)
Spawns, tracks, and controls child processes.
### Classes
**ProcessRegistry** — In-memory metadata store:
- `register(info)`, `unregister(id)`, `get(id)`, `getAll()`, `getByPid(pid)`, `updateStatus(id, status)`
**ProcessManager** — Lifecycle management:
| Method | Purpose |
|--------|---------|
| `spawn(options)` | Spawn detached process (survives parent exit) |
| `stop(id)` | SIGTERM → wait 5s → SIGKILL |
| `stopAll()` | Stop all running processes in parallel |
| `restart(id)` | Stop + re-spawn with same options |
| `isRunning(id)` | Check with `process.kill(pid, 0)` |
### Spawn Details
- Uses `execa` with `detached: true`, `stdio: 'ignore'`
- Calls `subprocess.unref()` so parent can exit
- Exit handler updates registry and emits events
### Events Emitted
`process:spawned`, `process:stopped`, `process:crashed`
---
## Logger Module (`apps/server/logger/`)
Structured logging via **pino**.
### Usage
```typescript
import { createModuleLogger } from '../logger/index.js';
const log = createModuleLogger('my-module');
log.info({ key: 'value' }, 'message');
```
### Configuration
| Env Var | Effect |
|---------|--------|
| `CW_LOG_LEVEL` | Override log level |
| `CW_LOG_PRETTY` | Set to `'1'` for human-readable output |
| `NODE_ENV=development` | Default to 'debug' level |
### Output
- Default: JSON to stderr (fd 2)
- Pretty mode: pino-pretty to stdout with colors and timestamps
---
## Logging Module (`apps/server/logging/`)
File-based per-process output capture (separate from pino).
### Classes
**LogManager** — Directory management:
- Base dir: `~/.cw/logs/`
- Structure: `{processId}/stdout.log`, `{processId}/stderr.log`
- `cleanOldLogs(retainDays)` — removes old directories by mtime
**ProcessLogWriter** — File I/O with timestamps:
- `open()` — create directories and append-mode WriteStreams
- `writeStdout(data)` / `writeStderr(data)` — prefix each line with `[YYYY-MM-DD HH:mm:ss.SSS]`
- Handles backpressure (waits for drain event)
- Emits `log:entry` event via EventBus
### Factory
```typescript
import { createLogger } from './logging/index.js';
const writer = createLogger(processId, eventBus);
await writer.open();
await writer.writeStdout('output data');
await writer.close();
```