|
| 1 | +# AGENTS.md - OpenCode Team Sync (oct) |
| 2 | + |
| 3 | +This file provides guidance for AI coding agents working on this codebase. |
| 4 | + |
| 5 | +## Project Overview |
| 6 | + |
| 7 | +OpenCode Team Sync (oct) is a CLI tool for synchronizing OpenCode configurations |
| 8 | +(agents, skills, MCP servers) across teams via Git repositories. The project is |
| 9 | +currently in the planning/specification phase. |
| 10 | + |
| 11 | +**Tech Stack**: TypeScript 5.x, Node.js 18+, Commander.js, Vitest, tsup |
| 12 | + |
| 13 | +## Build & Development Commands |
| 14 | + |
| 15 | +```bash |
| 16 | +npm install # Install dependencies |
| 17 | +npm run build # Build the project |
| 18 | +npm test # Run all tests |
| 19 | +npx vitest run path/to/file.test.ts # Run a single test file |
| 20 | +npx vitest run --testNamePattern "pattern" # Run tests matching a pattern |
| 21 | +npx vitest watch # Run tests in watch mode |
| 22 | +npm run test:coverage # Run tests with coverage |
| 23 | +npx tsc --noEmit # Type checking |
| 24 | +npm run lint # Linting |
| 25 | +npm run format # Formatting |
| 26 | +``` |
| 27 | + |
| 28 | +## Project Structure |
| 29 | + |
| 30 | +``` |
| 31 | +src/ |
| 32 | +├── cli/commands/ # init, sync, status, list, validate, update, rollback, remove, clean, info |
| 33 | +├── core/ # discovery, git, sync, validator, namespace, lockfile, tags |
| 34 | +├── schemas/ # Zod schemas for agent, skill, mcp, manifest, lockfile |
| 35 | +├── types/ # TypeScript interfaces |
| 36 | +└── utils/ # fs-utils, path-utils, hash-utils, error-utils, logger |
| 37 | +
|
| 38 | +tests/ |
| 39 | +├── unit/ # Fast, isolated tests (~70%) |
| 40 | +├── integration/ # Multi-module tests (~25%) |
| 41 | +├── fixtures/ # Test data |
| 42 | +└── helpers/ # Test utilities |
| 43 | +``` |
| 44 | + |
| 45 | +## Code Style Guidelines |
| 46 | + |
| 47 | +### TypeScript Conventions |
| 48 | + |
| 49 | +- Use strict mode (`"strict": true` in tsconfig.json) |
| 50 | +- Prefer `interface` over `type` for object shapes |
| 51 | +- Use explicit return types on public functions |
| 52 | +- Avoid `any` - use `unknown` with type guards |
| 53 | +- Use `readonly` for immutable properties |
| 54 | + |
| 55 | +### Naming Conventions |
| 56 | + |
| 57 | +- **Files**: kebab-case (`git-manager.ts`, `sync-engine.ts`) |
| 58 | +- **Classes**: PascalCase (`GitManager`, `SyncEngine`) |
| 59 | +- **Interfaces**: PascalCase, no `I` prefix (`ConfigEntry`, not `IConfigEntry`) |
| 60 | +- **Functions/Methods**: camelCase (`discoverConfigs`, `validateSchema`) |
| 61 | +- **Constants**: UPPER_SNAKE_CASE (`DEFAULT_TIMEOUT`, `ERROR_CODES`) |
| 62 | + |
| 63 | +### Imports |
| 64 | + |
| 65 | +Group imports: external deps, internal modules, types. Use ES modules exclusively. |
| 66 | + |
| 67 | +```typescript |
| 68 | +import { Command } from 'commander'; |
| 69 | +import { z } from 'zod'; |
| 70 | +import { GitManager } from '@/core/git/git-manager'; |
| 71 | +import type { ConfigEntry } from '@/types'; |
| 72 | +``` |
| 73 | + |
| 74 | +### Error Handling |
| 75 | + |
| 76 | +Use custom error classes extending `OpenCodeTeamError` with error codes: |
| 77 | + |
| 78 | +```typescript |
| 79 | +export class GitCloneError extends GitError { |
| 80 | + constructor(url: string, cause?: Error) { |
| 81 | + super(`Failed to clone repository: ${url}`, 'GIT_CLONE_ERROR', { url, cause: cause?.message }); |
| 82 | + } |
| 83 | +} |
| 84 | +``` |
| 85 | + |
| 86 | +### Async/Await |
| 87 | + |
| 88 | +- Always use async/await over raw Promises |
| 89 | +- Handle errors with try-catch, not .catch() |
| 90 | +- Use Promise.all() for parallel independent operations |
| 91 | + |
| 92 | +### Schema Validation (Zod) |
| 93 | + |
| 94 | +Define schemas in `src/schemas/`, export inferred types alongside: |
| 95 | + |
| 96 | +```typescript |
| 97 | +export const AgentFrontmatterSchema = z.object({ |
| 98 | + description: z.string().min(1).max(1024), |
| 99 | + mode: z.enum(['primary', 'subagent', 'all']).optional(), |
| 100 | + tags: z.array(z.string().regex(/^[a-z0-9][a-z0-9-]*[a-z0-9]$/)).optional(), |
| 101 | +}); |
| 102 | +export type AgentFrontmatter = z.infer<typeof AgentFrontmatterSchema>; |
| 103 | +``` |
| 104 | + |
| 105 | +### Testing |
| 106 | + |
| 107 | +- Test files: `*.test.ts` - use descriptive names, Arrange-Act-Assert pattern |
| 108 | +- Mock external deps (Git, file system), target 80%+ coverage |
| 109 | + |
| 110 | +## Architecture Principles |
| 111 | + |
| 112 | +1. **Separation of Concerns**: CLI, Business Logic, Data Access separated |
| 113 | +2. **Dependency Injection**: Services injected, not hard-coded |
| 114 | +3. **Fail-Fast**: Validate early, fail with clear messages |
| 115 | +4. **Idempotency**: Operations can be safely retried |
| 116 | +5. **Atomicity**: Changes are all-or-nothing where possible |
| 117 | + |
| 118 | +## Key Interfaces |
| 119 | + |
| 120 | +```typescript |
| 121 | +interface ConfigEntry { |
| 122 | + path: string; // Relative path in repo |
| 123 | + type: ConfigType; // 'agent' | 'skill' | 'mcp' |
| 124 | + name: string; // Derived from filename/directory |
| 125 | + hash: string; // SHA-256 of content |
| 126 | + tags: string[]; |
| 127 | +} |
| 128 | + |
| 129 | +interface SyncResult { |
| 130 | + added: ConfigEntry[]; |
| 131 | + updated: ConfigEntry[]; |
| 132 | + removed: string[]; |
| 133 | + conflicts: Conflict[]; |
| 134 | + errors: SyncError[]; |
| 135 | +} |
| 136 | +``` |
| 137 | + |
| 138 | +## Configuration Types |
| 139 | + |
| 140 | +- **Agents**: Markdown files with YAML frontmatter (`.md`) |
| 141 | +- **Skills**: SKILL.md files in skill directories |
| 142 | +- **MCP Servers**: JSON/YAML configuration files |
| 143 | + |
| 144 | +## Namespace Isolation |
| 145 | + |
| 146 | +Team configs: `{config_dir}/{type}/team/`, Personal: `{config_dir}/{type}/personal/` |
| 147 | +Personal always takes precedence over team configs. |
0 commit comments