Skip to content

Commit 0428b2e

Browse files
committed
Adding cartography
1 parent 85c60fb commit 0428b2e

10 files changed

Lines changed: 922 additions & 0 deletions

File tree

.gitignore

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,3 +45,29 @@ local
4545
.ignore
4646
opencode
4747
oh-my-opencode
48+
49+
# Cartography
50+
.slim/
51+
52+
# Python
53+
__pycache__/
54+
*.py[cod]
55+
*$py.class
56+
*.so
57+
.Python
58+
env/
59+
build/
60+
develop-eggs/
61+
dist/
62+
downloads/
63+
eggs/
64+
.eggs/
65+
lib/
66+
lib64/
67+
parts/
68+
sdist/
69+
var/
70+
wheels/
71+
*.egg-info/
72+
.installed.cfg
73+
*.egg

README.md

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -635,11 +635,19 @@ npx skills add https://github.com/brianlovin/claude-config --skill simplify -a o
635635

636636
### Available Skills
637637

638+
#### Recommended Skills (via npx)
639+
638640
| Skill | Description | Assigned To |
639641
|-------|-------------|-------------|
640642
| `simplify` | YAGNI code simplification expert | `orchestrator` |
641643
| `agent-browser` | High-performance browser automation | `designer` |
642644

645+
#### Custom Skills (bundled in repo)
646+
647+
| Skill | Description | Assigned To |
648+
|-------|-------------|-------------|
649+
| `cartography` | Repository understanding and hierarchical codemap generation | `orchestrator` |
650+
643651
### Configuration & Syntax
644652

645653
You can customize which skills each agent is allowed to use in `~/.config/opencode/oh-my-opencode-slim.json`.
@@ -688,6 +696,53 @@ You can customize which skills each agent is allowed to use in `~/.config/openco
688696

689697
`agent-browser` provides full high-performance browser automation capabilities. It allows agents to browse the web, interact with elements, and capture screenshots for visual state verification.
690698

699+
### Cartography
700+
701+
**Automated repository mapping through hierarchical codemaps.**
702+
703+
<img src="img/cartography.png" alt="Cartography Skill" width="800" style="border-radius: 10px; margin: 20px 0;">
704+
705+
`cartography` empowers the Orchestrator to build and maintain a deep architectural understanding of any codebase. Instead of reading thousands of lines of code every time, agents refer to hierarchical `codemap.md` files that describe the *why* and *how* of each directory.
706+
707+
**How to use:**
708+
709+
Just ask the **Orchestrator** to `run cartography`. It will automatically detect if it needs to initialize a new map or update an existing one.
710+
711+
**Why it's useful:**
712+
713+
- **Instant Onboarding:** Help agents (and humans) understand unfamiliar codebases in seconds.
714+
- **Efficient Context:** Agents only read architectural summaries, saving tokens and improving accuracy.
715+
- **Change Detection:** Only modified folders are re-analyzed, making updates fast and efficient.
716+
- **Timeless Documentation:** Focuses on high-level design patterns that don't get stale.
717+
718+
<details>
719+
<summary><b>Technical Details & Manual Control</b></summary>
720+
721+
The skill uses a background Python engine (`cartographer.py`) to manage state and detect changes.
722+
723+
**How it works under the hood:**
724+
725+
1. **Initialize** - Orchestrator analyzes repo structure and runs `init` to create `.slim/cartography.json` (hashes) and empty templates.
726+
2. **Map** - Orchestrator spawns specialized **Explorer** sub-agents to fill codemaps with timeless architectural details (Responsibility, Design, Flow, Integration).
727+
3. **Update** - On subsequent runs, the engine detects changed files and only refreshes codemaps for affected folders.
728+
729+
**Manual Commands:**
730+
731+
```bash
732+
# Initialize mapping manually
733+
python ~/.config/opencode/skills/cartography/scripts/cartographer.py init \
734+
--root . \
735+
--include "src/**/*.ts" \
736+
--exclude "**/*.test.ts"
737+
738+
# Check for changes since last map
739+
python ~/.config/opencode/skills/cartography/scripts/cartographer.py changes --root .
740+
741+
# Sync hashes after manual map updates
742+
python ~/.config/opencode/skills/cartography/scripts/cartographer.py update --root .
743+
```
744+
</details>
745+
691746
---
692747

693748
## 🔌 MCP Servers

img/cartography.png

235 KB
Loading

src/cli/custom-skills.ts

Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
import {
2+
copyFileSync,
3+
existsSync,
4+
mkdirSync,
5+
readdirSync,
6+
statSync,
7+
} from 'node:fs';
8+
import { join, dirname } from 'node:path';
9+
import { homedir } from 'node:os';
10+
11+
/**
12+
* A custom skill bundled in this repository.
13+
* Unlike npx-installed skills, these are copied from src/skills/ to ~/.config/opencode/skills/
14+
*/
15+
export interface CustomSkill {
16+
/** Skill name (folder name) */
17+
name: string;
18+
/** Human-readable description */
19+
description: string;
20+
/** List of agents that should auto-allow this skill */
21+
allowedAgents: string[];
22+
/** Source path in this repo (relative to project root) */
23+
sourcePath: string;
24+
}
25+
26+
/**
27+
* Registry of custom skills bundled in this repository.
28+
*/
29+
export const CUSTOM_SKILLS: CustomSkill[] = [
30+
{
31+
name: 'cartography',
32+
description: 'Repository understanding and hierarchical codemap generation',
33+
allowedAgents: ['orchestrator'],
34+
sourcePath: 'src/skills/cartography',
35+
},
36+
];
37+
38+
/**
39+
* Get the target directory for custom skills installation.
40+
*/
41+
export function getCustomSkillsDir(): string {
42+
return join(homedir(), '.config', 'opencode', 'skills');
43+
}
44+
45+
/**
46+
* Recursively copy a directory.
47+
*/
48+
function copyDirRecursive(src: string, dest: string): void {
49+
if (!existsSync(dest)) {
50+
mkdirSync(dest, { recursive: true });
51+
}
52+
53+
const entries = readdirSync(src);
54+
for (const entry of entries) {
55+
const srcPath = join(src, entry);
56+
const destPath = join(dest, entry);
57+
const stat = statSync(srcPath);
58+
59+
if (stat.isDirectory()) {
60+
copyDirRecursive(srcPath, destPath);
61+
} else {
62+
const destDir = dirname(destPath);
63+
if (!existsSync(destDir)) {
64+
mkdirSync(destDir, { recursive: true });
65+
}
66+
copyFileSync(srcPath, destPath);
67+
}
68+
}
69+
}
70+
71+
/**
72+
* Install a custom skill by copying from src/skills/ to ~/.config/opencode/skills/
73+
* @param skill - The custom skill to install
74+
* @param projectRoot - Root directory of oh-my-opencode-slim project
75+
* @returns True if installation succeeded, false otherwise
76+
*/
77+
export function installCustomSkill(
78+
skill: CustomSkill,
79+
projectRoot: string,
80+
): boolean {
81+
try {
82+
const sourcePath = join(projectRoot, skill.sourcePath);
83+
const targetPath = join(getCustomSkillsDir(), skill.name);
84+
85+
// Validate source exists
86+
if (!existsSync(sourcePath)) {
87+
console.error(`Custom skill source not found: ${sourcePath}`);
88+
return false;
89+
}
90+
91+
// Copy skill directory
92+
copyDirRecursive(sourcePath, targetPath);
93+
94+
return true;
95+
} catch (error) {
96+
console.error(`Failed to install custom skill: ${skill.name}`, error);
97+
return false;
98+
}
99+
}

src/cli/install.ts

Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010
writeLiteConfig,
1111
} from './config-manager';
1212
import { RECOMMENDED_SKILLS, installSkill } from './skills';
13+
import { CUSTOM_SKILLS, installCustomSkill } from './custom-skills';
1314
import type {
1415
BooleanArg,
1516
ConfigMergeResult,
@@ -157,6 +158,7 @@ function argsToConfig(args: InstallArgs): InstallConfig {
157158
hasOpencodeZen: true, // Always enabled - free models available to all users
158159
hasTmux: args.tmux === 'yes',
159160
installSkills: args.skills === 'yes',
161+
installCustomSkills: args.skills === 'yes', // Install custom skills when skills=yes
160162
};
161163
}
162164

@@ -226,12 +228,24 @@ async function runInteractiveMode(
226228
const skills = await askYesNo(rl, 'Install recommended skills?', 'yes');
227229
console.log();
228230

231+
// Custom skills prompt
232+
console.log(`${BOLD}Custom Skills:${RESET}`);
233+
for (const skill of CUSTOM_SKILLS) {
234+
console.log(
235+
` ${SYMBOLS.bullet} ${BOLD}${skill.name}${RESET}: ${skill.description}`,
236+
);
237+
}
238+
console.log();
239+
const customSkills = await askYesNo(rl, 'Install custom skills?', 'yes');
240+
console.log();
241+
229242
return {
230243
hasAntigravity: antigravity === 'yes',
231244
hasOpenAI: openai === 'yes',
232245
hasOpencodeZen: true,
233246
hasTmux: false,
234247
installSkills: skills === 'yes',
248+
installCustomSkills: customSkills === 'yes',
235249
};
236250
} finally {
237251
rl.close();
@@ -248,6 +262,7 @@ async function runInstall(config: InstallConfig): Promise<number> {
248262
let totalSteps = 4; // Base: check opencode, add plugin, disable default agents, write lite config
249263
if (config.hasAntigravity) totalSteps += 1; // provider config only (no auth plugin needed)
250264
if (config.installSkills) totalSteps += 1; // skills installation
265+
if (config.installCustomSkills) totalSteps += 1; // custom skills installation
251266

252267
let step = 1;
253268

@@ -292,6 +307,25 @@ async function runInstall(config: InstallConfig): Promise<number> {
292307
);
293308
}
294309

310+
// Install custom skills if requested
311+
if (config.installCustomSkills) {
312+
printStep(step++, totalSteps, 'Installing custom skills...');
313+
let customSkillsInstalled = 0;
314+
const projectRoot = process.cwd(); // Assumes running from project root
315+
for (const skill of CUSTOM_SKILLS) {
316+
printInfo(`Installing ${skill.name}...`);
317+
if (installCustomSkill(skill, projectRoot)) {
318+
printSuccess(`Installed: ${skill.name}`);
319+
customSkillsInstalled++;
320+
} else {
321+
printWarning(`Failed to install: ${skill.name}`);
322+
}
323+
}
324+
printSuccess(
325+
`${customSkillsInstalled}/${CUSTOM_SKILLS.length} custom skills installed`,
326+
);
327+
}
328+
295329
// Summary
296330
console.log();
297331
console.log(formatConfigSummary(config));

src/cli/types.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@ export interface InstallConfig {
2121
hasOpencodeZen: boolean;
2222
hasTmux: boolean;
2323
installSkills: boolean;
24+
installCustomSkills: boolean;
2425
}
2526

2627
export interface ConfigMergeResult {

src/skills/cartography/README.md

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
# Cartography Skill
2+
3+
Repository understanding and hierarchical codemap generation.
4+
5+
## Overview
6+
7+
Cartography helps orchestrators map and understand codebases by:
8+
9+
1. Selecting relevant code/config files using LLM judgment
10+
2. Creating `.slim/cartography.json` for change tracking
11+
3. Generating empty `codemap.md` templates for explorers to fill in
12+
13+
## Commands
14+
15+
```bash
16+
# Initialize mapping
17+
python cartographer.py init --root /repo --include "src/**/*.ts" --exclude "node_modules/**"
18+
19+
# Check what changed
20+
python cartographer.py changes --root /repo
21+
22+
# Update hashes
23+
python cartographer.py update --root /repo
24+
```
25+
26+
## Outputs
27+
28+
### .slim/cartography.json
29+
30+
```json
31+
{
32+
"metadata": {
33+
"version": "1.0.0",
34+
"last_run": "2026-01-25T19:00:00Z",
35+
"include_patterns": ["src/**/*.ts"],
36+
"exclude_patterns": ["node_modules/**"]
37+
},
38+
"file_hashes": {
39+
"src/index.ts": "abc123..."
40+
},
41+
"folder_hashes": {
42+
"src": "def456..."
43+
}
44+
}
45+
```
46+
47+
### codemap.md (per folder)
48+
49+
Empty templates created in each folder for explorers to fill with:
50+
- Responsibility
51+
- Design patterns
52+
- Data/control flow
53+
- Integration points
54+
55+
## Installation
56+
57+
Installed automatically via oh-my-opencode-slim installer when custom skills are enabled.

0 commit comments

Comments
 (0)