Skip to content

Commit 44cead3

Browse files
authored
[codex] add config setup skill routing (#61)
* add config setup skill routing * fix config setup review feedback
1 parent 2ba872c commit 44cead3

12 files changed

Lines changed: 698 additions & 31 deletions

File tree

.claude/skills/codemap/SKILL.md

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
---
22
name: codemap
3-
description: Analyze codebase structure, dependencies, changes, cross-agent handoffs, and get code-aware intelligence. Use when user asks about project structure, where code is located, how files connect, what changed, how to resume work, before starting any coding task, or when you need risk analysis and skill guidance.
3+
description: Analyze codebase structure, dependencies, changes, cross-agent handoffs, and get code-aware intelligence. Use when user asks about project structure, where code is located, how files connect, what changed, how to resume work, before starting any coding task, when you need risk analysis and skill guidance, or when Codemap should tune project config before analysis.
44
---
55

66
# Codemap
77

88
Codemap gives you instant architectural context about any codebase. It classifies your intent, detects risk, matches relevant skills, and tracks your working set — all automatically via hooks.
99

10+
Codemap should also keep its own per-project config healthy. On first use in a repo, or when output is obviously noisy, tune `.codemap/config.json` before doing deeper analysis so future calls stay code-first instead of asset-first.
11+
1012
## Commands
1113

1214
```bash
@@ -21,12 +23,30 @@ codemap handoff --json . # Machine-readable handoff payload
2123
codemap skill list # Show available skills with descriptions
2224
codemap skill show <name> # Get full skill instructions
2325
codemap skill init # Create custom skill template
26+
codemap config show # Show current project config
2427
codemap context # Universal JSON context envelope
2528
codemap context --for "prompt" # With pre-classified intent + matched skills
2629
codemap context --compact # Minimal for token-constrained agents
2730
codemap serve --port 9471 # HTTP API for non-MCP integrations
2831
```
2932

33+
## First-Use Setup
34+
35+
Before deeper Codemap analysis in a repo:
36+
37+
1. Check `.codemap/config.json`.
38+
2. If it is missing, clearly boilerplate, or obviously too noisy for the stack, run `codemap skill show config-setup` and follow it.
39+
3. After writing or improving config, rerun `codemap .` and `codemap --deps`.
40+
41+
Treat config as repo memory. Once tuned, future Codemap calls should benefit automatically.
42+
43+
Signals that config needs setup or tuning:
44+
- `.codemap/config.json` is missing
45+
- config only contains generic auto-detected `only` values with no real project shaping
46+
- large non-code directories dominate the tree output
47+
- stack-specific noise is overwhelming source structure (`.xcassets`, screenshots, PDFs, training-data, fixtures, generated files, models, vendor directories)
48+
- the repo stack is obvious, but the config does not reflect it
49+
3050
## When to Use
3151

3252
### ALWAYS run `codemap .` when:
@@ -56,6 +76,12 @@ codemap serve --port 9471 # HTTP API for non-MCP integrations
5676
- You need guidance for a specific task (hub editing, refactoring, testing)
5777
- Risk level is medium or high
5878

79+
### Run `codemap skill show config-setup` when:
80+
- The repo has no `.codemap/config.json`
81+
- The config looks like a bare bootstrap and not a real project policy
82+
- Codemap output is cluttered by large non-code directories
83+
- You want Codemap to make better future decisions for this specific repo
84+
5985
### Run `codemap context` when:
6086
- Piping codemap intelligence to another tool
6187
- Need a structured JSON summary of the project state
@@ -84,6 +110,7 @@ Skills matched: hub-safety, refactor — run `codemap skill show <name>` for gui
84110

85111
| Skill | When to Pull |
86112
|-------|-------------|
113+
| `config-setup` | Missing, boilerplate, or noisy `.codemap/config.json` |
87114
| `hub-safety` | Editing files imported by 3+ others |
88115
| `refactor` | Restructuring, renaming, moving code |
89116
| `test-first` | Writing tests, TDD workflows |
Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
---
2+
name: config-setup
3+
description: Set up or tune .codemap/config.json so Codemap focuses on code-relevant parts of the repo. Use when config is missing, boilerplate, noisy, or mismatched to the stack.
4+
---
5+
6+
# Codemap Config Setup
7+
8+
## Goal
9+
10+
Write or improve `.codemap/config.json` so future Codemap calls stay focused on the code that matters for this repo.
11+
12+
## Use this when
13+
14+
1. `.codemap/config.json` is missing
15+
2. The existing config looks like a bare bootstrap instead of a real project policy
16+
3. Codemap output is dominated by assets, fixtures, generated files, vendor trees, PDFs, screenshots, models, or training data
17+
4. The project stack is obvious, but Codemap is not prioritizing the right parts of the repo
18+
19+
## Workflow
20+
21+
1. Inspect the repo quickly before writing config
22+
- Run `codemap .`
23+
- If needed, run `codemap --deps .`
24+
- Note the stack markers (`Cargo.toml`, `Package.swift`, `*.xcodeproj`, `go.mod`, `package.json`, `pyproject.toml`, etc.)
25+
- Identify large non-code directories and noisy extensions
26+
27+
2. Decide whether config is missing, boilerplate, or tuned
28+
- Missing: no `.codemap/config.json`
29+
- Boilerplate: only generic `only` values, no real shaping, no excludes despite obvious noise
30+
- Tuned: contains intentional project-specific includes/excludes, depth, or routing hints
31+
32+
3. Write a conservative code-first config
33+
- Keep primary source-language `only` values when they help
34+
- Add `exclude` entries for obvious non-code noise
35+
- Set a moderate `depth` when the repo is broad
36+
- Avoid overfitting or excluding real source directories
37+
38+
4. Prefer stack-aware defaults
39+
- Rust: focus `src`, `tests`, `benches`, `examples`; de-prioritize corpora, sample PDFs, training data, large generated artifacts
40+
- iOS/Swift: focus app/framework source, tests, package/project manifests; de-prioritize `.xcassets`, screenshots, snapshots, vendor/build outputs
41+
- TS/JS: focus `src`, `apps`, `packages`, `tests`; de-prioritize `dist`, `coverage`, Storybook assets, large fixture payloads
42+
- Python: focus package roots, tests, tool config; de-prioritize notebooks, data dumps, models, fixtures when they overwhelm code
43+
- Go: focus packages, cmd, internal, tests; de-prioritize generated assets, sample data, vendor-like noise
44+
45+
5. Preserve user intent
46+
- If config already looks curated, do not replace it wholesale
47+
- Make minimal edits and explain why
48+
49+
6. Verify immediately
50+
- Rerun `codemap .`
51+
- If the repo still looks noisy, refine `exclude` and possibly `depth`
52+
- Only rerun `codemap --deps .` after tree output looks reasonable

cmd/config.go

Lines changed: 27 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -160,19 +160,30 @@ func initProjectConfig(root string) (configInitResult, error) {
160160
}
161161

162162
func configShow(root string) {
163-
cfg := config.Load(root)
164-
if isConfigEmpty(cfg) {
165-
cfgPath := config.ConfigPath(root)
166-
if _, err := os.Stat(cfgPath); os.IsNotExist(err) {
167-
fmt.Println("No config file found.")
168-
fmt.Printf("Run 'codemap config init' to create %s\n", cfgPath)
169-
} else {
170-
fmt.Println("Config is empty (no filters active).")
163+
assessment := config.AssessSetup(root)
164+
cfgPath := config.ConfigPath(root)
165+
switch assessment.State {
166+
case config.SetupStateMissing:
167+
fmt.Println("No config file found.")
168+
fmt.Printf("Run 'codemap config init' to create %s\n", cfgPath)
169+
return
170+
case config.SetupStateEmpty:
171+
fmt.Println("Config is empty (no filters active).")
172+
if len(assessment.Reasons) > 0 {
173+
fmt.Println(assessment.Reasons[0])
171174
}
172175
return
176+
case config.SetupStateMalformed:
177+
fmt.Println("Config is malformed or unreadable.")
178+
if len(assessment.Reasons) > 0 {
179+
fmt.Println(assessment.Reasons[0])
180+
}
181+
fmt.Printf("Fix %s or rerun 'codemap config init' to recreate it.\n", cfgPath)
182+
return
173183
}
174184

175-
fmt.Printf("Config: %s\n", config.ConfigPath(root))
185+
cfg := config.Load(root)
186+
fmt.Printf("Config: %s\n", cfgPath)
176187
fmt.Println()
177188
if len(cfg.Only) > 0 {
178189
fmt.Printf(" only: %s\n", strings.Join(cfg.Only, ", "))
@@ -229,23 +240,14 @@ func configShow(root string) {
229240
fmt.Printf(" require_docs_for: %s\n", strings.Join(cfg.Drift.RequireDocsFor, ", "))
230241
}
231242
}
243+
244+
if assessment.State == config.SetupStateBoilerplate {
245+
fmt.Println()
246+
fmt.Println("Note: this config still looks like a bootstrap.")
247+
fmt.Println("Run `codemap skill show config-setup` to tune it for this repo.")
248+
}
232249
}
233250

234251
func isConfigEmpty(cfg config.ProjectConfig) bool {
235-
if len(cfg.Only) > 0 || len(cfg.Exclude) > 0 || cfg.Depth > 0 {
236-
return false
237-
}
238-
if strings.TrimSpace(cfg.Mode) != "" {
239-
return false
240-
}
241-
if cfg.Budgets.SessionStartBytes > 0 || cfg.Budgets.DiffBytes > 0 || cfg.Budgets.MaxHubs > 0 {
242-
return false
243-
}
244-
if strings.TrimSpace(cfg.Routing.Retrieval.Strategy) != "" || cfg.Routing.Retrieval.TopK > 0 || len(cfg.Routing.Subsystems) > 0 {
245-
return false
246-
}
247-
if cfg.Drift.Enabled || cfg.Drift.RecentCommits > 0 || len(cfg.Drift.RequireDocsFor) > 0 {
248-
return false
249-
}
250-
return true
252+
return cfg.IsZero()
251253
}

cmd/config_more_test.go

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,44 @@ func TestConfigShowNoConfigFile(t *testing.T) {
9292
}
9393
}
9494

95+
func TestConfigShowMalformedConfig(t *testing.T) {
96+
root := t.TempDir()
97+
cfgPath := config.ConfigPath(root)
98+
if err := os.MkdirAll(filepath.Dir(cfgPath), 0o755); err != nil {
99+
t.Fatal(err)
100+
}
101+
if err := os.WriteFile(cfgPath, []byte("{broken"), 0o644); err != nil {
102+
t.Fatal(err)
103+
}
104+
105+
out := captureOutput(func() { configShow(root) })
106+
if !strings.Contains(out, "malformed or unreadable") {
107+
t.Fatalf("expected malformed guidance, got:\n%s", out)
108+
}
109+
if !strings.Contains(out, "rerun 'codemap config init'") {
110+
t.Fatalf("expected recreate guidance, got:\n%s", out)
111+
}
112+
}
113+
114+
func TestConfigShowBoilerplateConfigSuggestsTuning(t *testing.T) {
115+
root := t.TempDir()
116+
cfgPath := config.ConfigPath(root)
117+
if err := os.MkdirAll(filepath.Dir(cfgPath), 0o755); err != nil {
118+
t.Fatal(err)
119+
}
120+
if err := os.WriteFile(cfgPath, []byte(`{"only":["rs","toml"],"depth":4}`+"\n"), 0o644); err != nil {
121+
t.Fatal(err)
122+
}
123+
124+
out := captureOutput(func() { configShow(root) })
125+
if !strings.Contains(out, "looks like a bootstrap") {
126+
t.Fatalf("expected bootstrap note, got:\n%s", out)
127+
}
128+
if !strings.Contains(out, "config-setup") {
129+
t.Fatalf("expected config-setup guidance, got:\n%s", out)
130+
}
131+
}
132+
95133
func mustWriteConfigFixture(t *testing.T, path string, content string) {
96134
t.Helper()
97135
if err := os.MkdirAll(filepath.Dir(path), 0o755); err != nil {

cmd/hooks.go

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -300,6 +300,8 @@ func hookSessionStart(root string) error {
300300
structureBudget := projCfg.SessionStartOutputBytes()
301301
maxHubs := projCfg.HubDisplayLimit()
302302

303+
showConfigSetupHint(root)
304+
303305
exe, err := os.Executable()
304306
if err == nil {
305307
depth := limits.AdaptiveDepth(fileCount)
@@ -782,6 +784,7 @@ func showMatchedSkills(root string, intent TaskIntent) {
782784
}
783785

784786
matches := idx.MatchSkills(intent.Category, intent.Files, langs, 3)
787+
matches = injectConfigSetupSkill(root, idx, matches)
785788
if len(matches) == 0 {
786789
return
787790
}
@@ -808,6 +811,64 @@ func showMatchedSkills(root string, intent TaskIntent) {
808811
fmt.Printf("Skills matched: %s — run `codemap skill show <name>` for guidance\n", strings.Join(names, ", "))
809812
}
810813

814+
func injectConfigSetupSkill(root string, idx *skills.SkillIndex, matches []skills.MatchResult) []skills.MatchResult {
815+
assessment := config.AssessSetup(root)
816+
if !assessment.NeedsAttention() {
817+
return matches
818+
}
819+
820+
skill, ok := idx.ByName["config-setup"]
821+
if !ok || skill == nil {
822+
return matches
823+
}
824+
for _, match := range matches {
825+
if match.Skill != nil && match.Skill.Meta.Name == "config-setup" {
826+
return matches
827+
}
828+
}
829+
830+
reason := "config:" + string(assessment.State)
831+
if len(assessment.Reasons) > 0 {
832+
reason = reason + " " + assessment.Reasons[0]
833+
}
834+
835+
injected := skills.MatchResult{
836+
Skill: skill,
837+
Score: 100,
838+
Reason: reason,
839+
}
840+
matches = append([]skills.MatchResult{injected}, matches...)
841+
if len(matches) > 3 {
842+
matches = matches[:3]
843+
}
844+
return matches
845+
}
846+
847+
func showConfigSetupHint(root string) {
848+
assessment := config.AssessSetup(root)
849+
if !assessment.NeedsAttention() {
850+
return
851+
}
852+
853+
type configHint struct {
854+
State string `json:"state"`
855+
Reasons []string `json:"reasons,omitempty"`
856+
}
857+
if data, err := json.Marshal(configHint{
858+
State: string(assessment.State),
859+
Reasons: assessment.Reasons,
860+
}); err == nil {
861+
fmt.Printf("<!-- codemap:config %s -->\n", string(data))
862+
}
863+
864+
fmt.Println("⚙️ Codemap config setup recommended:")
865+
for _, reason := range assessment.Reasons {
866+
fmt.Printf(" • %s\n", reason)
867+
}
868+
fmt.Println(" • Run `codemap skill show config-setup` and tune `.codemap/config.json` before deeper analysis.")
869+
fmt.Println()
870+
}
871+
811872
// showDriftWarnings checks and displays documentation drift warnings.
812873
func showDriftWarnings(root string, cfg config.DriftConfig, routing config.RoutingConfig) {
813874
warnings := CheckDrift(root, cfg, routing)
@@ -1441,6 +1502,7 @@ func hookSessionStartMultiRepo(root string, childRepos []string) error {
14411502

14421503
repoPath := filepath.Join(root, repo)
14431504
projCfg := config.Load(repoPath)
1505+
showConfigSetupHint(repoPath)
14441506

14451507
depth := 2
14461508
if projCfg.Depth > 0 {

0 commit comments

Comments
 (0)