Skip to content

Commit 13f50c3

Browse files
authored
docs: refactor AGENTS.md for clarity and completeness (#53)
- update root AGENTS.md with expanded code map and conventions - add CI/testing details and Refs column to symbol table - restructure src/lib/AGENTS.md into layered architecture view - consolidate module documentation into Discovery/Conversion/Config layers - simplify data flow diagrams and streamline patterns section - add centrality notes and implementation details
1 parent 38e69f0 commit 13f50c3

2 files changed

Lines changed: 112 additions & 125 deletions

File tree

AGENTS.md

Lines changed: 43 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# AGENTS.md - Coding Agent Guidelines for Systematic
22

3-
**Generated:** 2026-02-02 | **Commit:** decbf40 | **Branch:** main
3+
**Generated:** 2026-02-08 | **Commit:** 38e69f0 | **Branch:** main
44

55
## Overview
66

@@ -30,20 +30,21 @@ bun test --filter "pattern" # Filter tests
3030
- **Modules:** ESM (`"type": "module"`)
3131
- **Linter:** Biome (not ESLint/Prettier)
3232
- **Tests:** `bun:test`
33+
- **CI:** GitHub Actions (semantic-release, OSSF Scorecard, CodeQL)
3334

3435
## Structure
3536

3637
```
3738
systematic/
3839
├── src/
3940
│ ├── index.ts # Plugin entry (SystematicPlugin)
40-
│ ├── cli.ts # CLI entry
41+
│ ├── cli.ts # CLI entry (list/convert/config commands)
4142
│ └── lib/ # Core implementation (see src/lib/AGENTS.md)
4243
├── skills/ # 8 bundled skills (SKILL.md format)
4344
├── agents/ # 11 bundled agents (4 categories)
44-
├── commands/ # 9 bundled commands
45+
├── commands/ # 9 bundled commands (with workflows/ subdir)
4546
├── tests/
46-
│ ├── unit/ # 9 test files
47+
│ ├── unit/ # 10 test files
4748
│ └── integration/ # 2 test files
4849
└── dist/ # Build output
4950
```
@@ -55,29 +56,41 @@ systematic/
5556
| Plugin hooks (config, tool, system.transform) | `src/index.ts` |
5657
| Config merging logic | `src/lib/config-handler.ts` |
5758
| Skill tool implementation | `src/lib/skill-tool.ts` |
59+
| Skill loading + formatting | `src/lib/skill-loader.ts` |
5860
| Bootstrap injection | `src/lib/bootstrap.ts` |
59-
| CEP conversion | `src/lib/converter.ts` |
61+
| CEP→OpenCode conversion | `src/lib/converter.ts` |
62+
| YAML frontmatter parsing | `src/lib/frontmatter.ts` |
63+
| Agent config validation | `src/lib/validation.ts` |
6064
| Asset discovery | `src/lib/skills.ts`, `agents.ts`, `commands.ts` |
65+
| Directory walking | `src/lib/walk-dir.ts` |
66+
| Config loading (JSONC) | `src/lib/config.ts` |
67+
| CLI commands | `src/cli.ts` |
6168
| Add new skill | `skills/<name>/SKILL.md` |
6269
| Add new agent | `agents/<category>/<name>.md` |
6370
| Add new command | `commands/<name>.md` |
6471

6572
## Code Map
6673

67-
| Symbol | Type | Location | Role |
68-
|--------|------|----------|------|
69-
| `SystematicPlugin` | export | src/index.ts:30 | Main plugin factory |
70-
| `createConfigHandler` | fn | src/lib/config-handler.ts:182 | Config hook impl |
71-
| `createSkillTool` | fn | src/lib/skill-tool.ts:35 | systematic_skill tool |
72-
| `getBootstrapContent` | fn | src/lib/bootstrap.ts:32 | System prompt injection |
73-
| `convertContent` | fn | src/lib/converter.ts:234 | CEP→OpenCode conversion |
74-
| `findSkillsInDir` | fn | src/lib/skills.ts:90 | Skill discovery |
75-
| `loadConfig` | fn | src/lib/config.ts:47 | JSONC config loading |
74+
| Symbol | Type | Location | Refs | Role |
75+
|--------|------|----------|------|------|
76+
| `SystematicPlugin` | export | src/index.ts:30 | 2 | Main plugin factory |
77+
| `createConfigHandler` | fn | src/lib/config-handler.ts:182 | 3 | Config hook impl |
78+
| `createSkillTool` | fn | src/lib/skill-tool.ts:87 | 2 | systematic_skill tool factory |
79+
| `getBootstrapContent` | fn | src/lib/bootstrap.ts:32 | 2 | System prompt injection |
80+
| `convertContent` | fn | src/lib/converter.ts:234 | 4 | CEP→OpenCode conversion |
81+
| `findSkillsInDir` | fn | src/lib/skills.ts:90 | 6 | Skill discovery (highest centrality) |
82+
| `findAgentsInDir` | fn | src/lib/agents.ts:47 || Agent discovery |
83+
| `findCommandsInDir` | fn | src/lib/commands.ts:27 || Command discovery |
84+
| `loadConfig` | fn | src/lib/config.ts:47 || JSONC config loading |
85+
| `parseFrontmatter` | fn | src/lib/frontmatter.ts || YAML frontmatter extraction |
86+
| `walkDir` | fn | src/lib/walk-dir.ts || Recursive dir walker |
87+
| `loadSkill` | fn | src/lib/skill-loader.ts || Skill content loading + wrapping |
7688

7789
## Conventions
7890

7991
### Formatting (Biome)
8092
- 2 spaces, single quotes, semicolons as-needed
93+
- Biome warns on: `noExcessiveCognitiveComplexity`, `noNonNullAssertion`
8194

8295
### Imports
8396
```typescript
@@ -87,10 +100,11 @@ import { loadConfig } from './lib/config.js' // Internal with .js extension
87100
```
88101
89102
### TypeScript
90-
- Function declarations over classes
91-
- Explicit return types
92-
- Interfaces for data structures
93-
- Union types for constrained values
103+
- Function declarations over classes (zero classes in codebase)
104+
- Explicit return types on exported functions
105+
- Interfaces for data structures (SkillInfo, AgentInfo, etc.)
106+
- Union types + const enums for constrained values
107+
- `unknown` with type guards instead of `any`
94108
95109
### Error Handling
96110
- Return null/empty for non-critical failures
@@ -103,6 +117,13 @@ import { loadConfig } from './lib/config.js' // Internal with .js extension
103117
- Types/Interfaces: PascalCase
104118
- Tests: `*.test.ts`
105119
120+
### Testing
121+
- Bun built-in test runner (`bun:test`)
122+
- `describe`/`it` nesting with `beforeEach`/`afterEach`
123+
- Real temp directories for FS isolation (`mkdtempSync`/`rmSync`)
124+
- No mocking libraries — creates real temp files
125+
- Integration tests gracefully skip if external deps unavailable
126+
106127
## Anti-Patterns
107128
108129
- `require()` — use ESM imports
@@ -142,12 +163,15 @@ description: Use when [condition] — [what it does]
142163
# Skill Content
143164
```
144165

166+
Skills are registered as commands with `systematic:` prefix.
167+
145168
## Config Priority
146169

147170
project `.opencode/systematic.json` > user `~/.config/opencode/systematic.json` > defaults
148171

149172
## Notes
150173

151174
- Bootstrap injection is opt-out via `bootstrap.enabled: false`
152-
- Skills are registered as commands (prefixed `systematic:`)
175+
- Converter caches results using file mtime
176+
- CLI commands: `list` (skills/agents/commands), `convert` (file conversion), `config show/path`
153177
- Experimental hook: `experimental.chat.system.transform`

src/lib/AGENTS.md

Lines changed: 69 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -1,128 +1,91 @@
1-
# src/lib — Core Implementation Modules
2-
3-
Plugin internals. 12 modules handling config, conversion, discovery, and tool implementation.
4-
5-
## Module Map
6-
7-
| Module | Purpose | Key Exports |
8-
|--------|---------|-------------|
9-
| `config-handler.ts` | OpenCode config hook | `createConfigHandler()` |
10-
| `skill-tool.ts` | systematic_skill tool | `createSkillTool()` |
11-
| `skill-loader.ts` | Skill file loading | `loadSkill()` |
12-
| `skills.ts` | Skill discovery | `findSkillsInDir()`, `extractFrontmatter()` |
13-
| `agents.ts` | Agent discovery | `findAgentsInDir()`, `extractAgentFrontmatter()` |
14-
| `commands.ts` | Command discovery | `findCommandsInDir()`, `extractCommandFrontmatter()` |
15-
| `converter.ts` | CEP→OpenCode conversion | `convertContent()`, `convertFileWithCache()` |
16-
| `frontmatter.ts` | YAML frontmatter utils | `parseFrontmatter()`, `serializeFrontmatter()` |
17-
| `bootstrap.ts` | System prompt injection | `getBootstrapContent()` |
18-
| `config.ts` | JSONC config loading | `loadConfig()`, `getConfigPaths()` |
19-
| `validation.ts` | Input validation | Validation helpers |
20-
| `walk-dir.ts` | Directory traversal | `walkDir()` |
1+
# src/lib — Core Implementation
2+
3+
13 modules implementing plugin logic: discovery, conversion, config, and tool registration.
214

225
## Data Flow
236

247
```
25-
Plugin init
26-
27-
loadConfig() ← reads JSONC from project/user paths
28-
29-
createConfigHandler() ← merges bundled assets into OpenCode config
30-
31-
├─ findSkillsInDir() + loadSkill() → skills as commands
32-
├─ findAgentsInDir() + extractAgentFrontmatter() → agent configs
33-
└─ findCommandsInDir() + extractCommandFrontmatter() → command configs
34-
35-
└─ convertContent() / convertFileWithCache() ← CEP→OpenCode transform
36-
37-
createSkillTool() ← registers systematic_skill tool
38-
39-
└─ findSkillsInDir() → formats skill list as XML
40-
└─ loadSkill() → returns skill body on demand
41-
42-
getBootstrapContent() ← reads using-systematic SKILL.md for system prompt
43-
```
8+
loadConfig() → createConfigHandler() → {
9+
findSkillsInDir() → loadSkillAsCommand() → OpenCode config
10+
findAgentsInDir() → loadAgentAsConfig() → OpenCode config
11+
findCommandsInDir() → loadCommandAsConfig() → OpenCode config
12+
}
4413
45-
## Key Interfaces
14+
createSkillTool() → discoverSkillFiles() → loadSkill() → formatted output
15+
getBootstrapContent() → reads using-systematic SKILL.md → system prompt
16+
```
4617

47-
```typescript
48-
// skills.ts
49-
interface SkillInfo {
50-
path: string
51-
skillFile: string
52-
name: string
53-
description: string
54-
// ... frontmatter fields
55-
}
18+
All discovery follows same pattern: `dir → walkDir() → find files → parseFrontmatter() → typed array`
5619

57-
// config.ts
58-
interface SystematicConfig {
59-
disabled_skills: string[]
60-
disabled_agents: string[]
61-
disabled_commands: string[]
62-
bootstrap: { enabled: boolean; file?: string }
63-
}
20+
## Modules
6421

65-
// config-handler.ts
66-
interface ConfigHandlerDeps {
67-
directory: string
68-
bundledSkillsDir: string
69-
bundledAgentsDir: string
70-
bundledCommandsDir: string
71-
}
72-
```
22+
### Discovery Layer
7323

74-
## Converter Details
24+
| Module | Key Exports | Role |
25+
|--------|-------------|------|
26+
| `walk-dir.ts` | `walkDir`, `WalkEntry`, `WalkOptions` | Recursive dir walker with depth + category tracking |
27+
| `skills.ts` | `findSkillsInDir`, `SkillInfo`, `SkillFrontmatter` | Skill discovery (maxDepth, frontmatter extraction) |
28+
| `agents.ts` | `findAgentsInDir`, `AgentInfo`, `AgentFrontmatter` | Agent discovery (category from subdir name) |
29+
| `commands.ts` | `findCommandsInDir`, `CommandInfo`, `CommandFrontmatter` | Command discovery |
30+
| `frontmatter.ts` | `parseFrontmatter`, `formatFrontmatter`, `stripFrontmatter` | YAML frontmatter parse/format/strip |
7531

76-
Transforms Claude Code (CEP) content to OpenCode format:
32+
### Conversion Layer
7733

78-
| Transformation | From | To |
79-
|----------------|------|-----|
80-
| Tool names | `TodoWrite` | `todowrite` |
81-
| Tool refs | `Task` | `delegate_task` |
82-
| Path separators | `\` | `/` |
83-
| Model names | `claude-3-opus` | Normalized |
84-
| Temperature | Inferred from content | 0.0-1.0 |
34+
| Module | Key Exports | Role |
35+
|--------|-------------|------|
36+
| `converter.ts` | `convertContent`, `convertFileWithCache`, `clearConverterCache` | CEP→OpenCode transforms (tool names, models, body refs) |
37+
| `skill-loader.ts` | `loadSkill`, `LoadedSkill`, `SKILL_PREFIX` | Loads + wraps skill content in XML template |
38+
| `validation.ts` | `isAgentMode`, `isPermissionSetting`, `buildPermissionObject` | Agent config extraction + type guards |
8539

86-
Caching: `convertFileWithCache()` uses file mtime to avoid re-parsing.
40+
### Config & Integration Layer
8741

88-
## Discovery Patterns
42+
| Module | Key Exports | Role |
43+
|--------|-------------|------|
44+
| `config.ts` | `loadConfig`, `getConfigPaths`, `SystematicConfig`, `DEFAULT_CONFIG` | JSONC config loading + merging |
45+
| `config-handler.ts` | `createConfigHandler`, `ConfigHandlerDeps` | OpenCode config hook (collects + converts all assets) |
46+
| `skill-tool.ts` | `createSkillTool`, `SkillToolOptions` | `systematic_skill` tool (XML description, skill execution) |
47+
| `bootstrap.ts` | `getBootstrapContent`, `BootstrapDeps` | System prompt injection (using-systematic skill) |
8948

90-
All discovery functions:
91-
1. Take a directory path
92-
2. Use `walkDir()` for traversal
93-
3. Look for specific files (SKILL.md, *.md)
94-
4. Extract YAML frontmatter
95-
5. Return typed array of results
49+
## Key Interfaces
9650

97-
Example:
9851
```typescript
99-
const skills = findSkillsInDir(bundledSkillsDir, 'bundled', 3)
100-
// Returns SkillInfo[] with name, description, path, etc.
52+
// Discovery
53+
interface SkillInfo { path, skillFile, name, description }
54+
interface AgentInfo { name, file, category }
55+
interface CommandInfo { name, file, category }
56+
interface WalkEntry { path, name, isDirectory, depth, category }
57+
58+
// Config
59+
interface SystematicConfig { disabled_skills, disabled_agents, disabled_commands, bootstrap: BootstrapConfig }
60+
interface ConfigHandlerDeps { directory, bundledSkillsDir, bundledAgentsDir, bundledCommandsDir }
61+
62+
// Conversion
63+
type ContentType = 'skill' | 'agent' | 'command'
64+
type SourceType = 'cep' | 'opencode'
65+
interface ConvertOptions { source, agentMode, skipBodyTransform }
10166
```
10267

103-
## Config Loading
68+
## Converter Details
10469

105-
Priority chain:
106-
1. `loadConfig(projectDir)` reads from:
107-
- `~/.config/opencode/systematic.json` (user)
108-
- `<projectDir>/.opencode/systematic.json` (project)
109-
2. Merges with `DEFAULT_CONFIG`
110-
3. Arrays merged uniquely via `mergeArraysUnique()`
70+
CEP→OpenCode transforms:
71+
- **Tool names**: `TodoWrite``todowrite`, `Task``delegate_task`, `Skill``skill`
72+
- **Models**: Claude model name normalization
73+
- **Body**: Replaces tool references outside code blocks (regex-based)
74+
- **Frontmatter**: Strips CEP-only fields, adds OpenCode fields
75+
- **Caching**: `convertFileWithCache` uses file mtime for invalidation
11176

112-
## Testing
77+
## Patterns
11378

114-
Unit tests in `tests/unit/`:
115-
- `skills.test.ts` — skill discovery
116-
- `config-handler.test.ts` — config merging
117-
- `converter.test.ts` — CEP conversion
118-
- `frontmatter.test.ts` — YAML parsing
79+
- **Function-only**: Zero classes. All modules export factory functions or pure helpers
80+
- **Interface-first**: Data shapes defined as interfaces, logic as functions
81+
- **Null returns**: Non-critical failures return `null`/`undefined` (not throws)
82+
- **Type guards**: `validation.ts` provides safe extraction from `unknown` frontmatter data
83+
- **Const enums**: `AgentMode`, `PermissionSetting` for compile-time safety
11984

120-
Pattern:
121-
```typescript
122-
describe('moduleName', () => {
123-
let testDir: string
124-
beforeEach(() => { testDir = fs.mkdtempSync(...) })
125-
afterEach(() => { fs.rmSync(testDir, { recursive: true }) })
126-
test('behavior', () => { ... })
127-
})
128-
```
85+
## Notes
86+
87+
- `findSkillsInDir` is highest-centrality function (6 references across 3 modules)
88+
- `SKILL_PREFIX` = `'systematic:'` — all skills registered with this prefix
89+
- `parseFrontmatter` is regex-based (not a YAML library for delimiter detection)
90+
- `formatFrontmatter` uses `js-yaml` dump with `noRefs` and core schema
91+
- `config-handler.ts` contains internal `loadAgentAsConfig`/`loadCommandAsConfig`/`loadSkillAsCommand` — the glue between discovery and OpenCode config output

0 commit comments

Comments
 (0)