Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 12 additions & 4 deletions .claude/skills/agent-customization/skill.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,23 @@ You are working on **agent customization** — the feature that reads a project'
- `src/prompts/customize-agents.md` — System prompt telling Claude how to customize agents
- `src/lib/runner.js` — `runClaude()`, `loadPrompt()`, `parseFileOutput()` shared across commands
- `src/lib/skill-writer.js` — `writeSkillFiles()` writes parsed output to disk
- `src/lib/timeout.js` — `resolveTimeout()` for timeout handling (default 300s)

## Key Concepts
- **Context gathering:** `gatherProjectContext()` reads CLAUDE.md (truncated at 3000 chars), all `.claude/skills/**/*.md`, and lists `.claude/guidelines/` paths without reading their contents.
- **Agent discovery:** `findAgents()` recursively walks `.claude/agents/`, reads `.md` files, extracts the `name:` from YAML frontmatter via regex.
- **Read-only tools:** Claude is invoked with `allowedTools: ['Read', 'Glob', 'Grep']` no writes allowed during the LLM call.
- **Context gathering:** `gatherProjectContext()` reads CLAUDE.md (truncated at 3000 chars), all `.claude/skills/**/*.md` in full, and lists `.claude/guidelines/` paths without reading their contents.
- **Agent discovery:** `findAgents()` recursively walks `.claude/agents/`, reads `.md` files, extracts `name:` via regex — falls back to filename if no frontmatter match.
- **Read-only tools:** Claude is invoked with `allowedTools: ['Read', 'Glob', 'Grep']` and no maxTokens cap (unlike doc-init which sets per-call limits).
- **Output parsing:** Claude returns `<file path="...">content</file>` XML tags, parsed by `parseFileOutput()`. Only `.claude/` paths are allowed.

## Critical Rules
- **Read-only tools only** — Claude agents never get write tools. All output goes through `parseFileOutput()` → `writeSkillFiles()`.
- **Context truncation** — CLAUDE.md is capped at 3000 chars to avoid blowing up prompt size. Skills are read in full.
- **Agent frontmatter** — agents must have `name:` in YAML frontmatter or they won't be discovered by `findAgents()`.
- **Path safety** — `parseFileOutput()` only allows writes to `.claude/` prefixed paths. Customized agents stay in `.claude/agents/`.
- **Dry-run support** — `--dry-run` flag previews output without writing. Confirmation prompt shown before writes.
- **Model override** — `--model` flag passed through to `runClaude()` for model selection.

## References
- **Patterns:** `.claude/guidelines/claude-runner/patterns.md`

---
**Last Updated:** 2026-03-28
6 changes: 5 additions & 1 deletion .claude/skills/base/skill.md
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,10 @@ CLI entry (`bin/cli.js`) → command handlers (`src/commands/`) → lib modules
- `src/lib/context-builder.js` — Assembles repo files into prompt-friendly context
- `src/lib/skill-writer.js` — Writes skill files, generates skill-rules.json, merges settings
- `src/lib/skill-reader.js` — Parses skill files, frontmatter, activation patterns, keywords
- `src/lib/diff-helpers.js` — Targeted file diffs and prioritized diff truncation for doc-sync
- `src/lib/git-helpers.js` — Git repo detection, diff retrieval, log formatting
- `src/lib/git-hook.js` — Post-commit git hook installation/removal for auto doc-sync
- `src/lib/timeout.js` — Timeout resolution (`--timeout` flag > `ASPENS_TIMEOUT` env > default)
- `src/lib/errors.js` — `CliError` class (structured errors caught by CLI top-level handler)
- `src/prompts/` — Prompt templates with `{{partial}}` and `{{variable}}` substitution
- `src/templates/` — Bundled agents, commands, hooks, and settings for `aspens add` / `doc init`
Expand All @@ -56,4 +60,4 @@ CLI entry (`bin/cli.js`) → command handlers (`src/commands/`) → lib modules
- `tests/` — Vitest test files

---
**Last Updated:** 2026-03-24
**Last Updated:** 2026-03-28
13 changes: 8 additions & 5 deletions .claude/skills/claude-runner/skill.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,9 @@ This skill triggers when editing claude-runner files:
- `src/lib/runner.js`
- `src/lib/skill-writer.js`
- `src/lib/skill-reader.js`
- `src/lib/timeout.js`
- `src/prompts/**/*.md`
- `tests/*extract*`, `tests/*parse*`, `tests/*prompt*`, `tests/*skill-writer*`
- `tests/*extract*`, `tests/*parse*`, `tests/*prompt*`, `tests/*skill-writer*`, `tests/*skill-mapper*`, `tests/*timeout*`

---

Expand All @@ -19,8 +20,9 @@ You are working on the **Claude CLI execution layer** — the bridge between ass
## Key Files
- `src/lib/runner.js` — `runClaude()`, `loadPrompt()`, `parseFileOutput()`, `validateSkillFiles()`, `extractResultFromStream()`
- `src/lib/skill-writer.js` — `writeSkillFiles()`, `extractRulesFromSkills()`, `generateDomainPatterns()`, `mergeSettings()`
- `src/lib/skill-reader.js` — `findSkillFiles()`, `parseFrontmatter()`, `parseActivationPatterns()`, `parseKeywords()`
- `src/prompts/` — Markdown prompt templates; `partials/` subdir holds reusable fragments
- `src/lib/skill-reader.js` — `findSkillFiles()`, `parseFrontmatter()`, `parseActivationPatterns()`, `parseKeywords()`, `fileMatchesActivation()`, `getActivationBlock()`, `GENERIC_PATH_SEGMENTS`
- `src/lib/timeout.js` — `resolveTimeout()` — priority: `--timeout` flag > `ASPENS_TIMEOUT` env var > caller fallback
- `src/prompts/` — Markdown prompt templates; `partials/` subdir holds reusable fragments (`skill-format`, `guideline-format`, `examples`)

## Key Concepts
- **Stream-JSON protocol:** `runClaude()` always passes `--verbose --output-format stream-json`. Output is NDJSON: `type: 'result'` has final text + usage; `type: 'assistant'` has text/tool_use blocks; `type: 'user'` has tool_result blocks.
Expand All @@ -35,8 +37,9 @@ You are working on the **Claude CLI execution layer** — the bridge between ass
- **Both `--verbose` and `--output-format stream-json` are required** — omitting either breaks stream parsing.
- **Path sanitization is non-negotiable** — `sanitizePath()` blocks `..` traversal, absolute paths, and any path not under `.claude/` or exactly `CLAUDE.md`.
- **Prompt partials resolve before variables** — `{{skill-format}}` resolves to `partials/skill-format.md` first. If no file, falls through to variable substitution.
- **Timeout auto-scales** small: 120s, medium: 300s, large: 600s, very-large: 900s. User `--timeout` overrides.
- **Timeout resolution:** `resolveTimeout(flagValue, fallbackSeconds)` — `--timeout` flag wins, then `ASPENS_TIMEOUT` env, then caller-provided fallback. Size-based defaults (small: 120s, medium: 300s, large: 600s, very-large: 900s) are set by command handlers, not runner.
- **`mergeSettings` preserves non-aspens hooks** — identifies aspens hooks by `ASPENS_HOOK_MARKERS` (`skill-activation-prompt`, `post-tool-use-tracker`), replaces matching entries, preserves everything else.
- **Debug mode:** Set `ASPENS_DEBUG=1` to dump raw stream-json to `/tmp/aspens-debug-stream.json`.

---
**Last Updated:** 2026-03-24
**Last Updated:** 2026-03-28
34 changes: 23 additions & 11 deletions .claude/skills/doc-sync/skill.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,37 +8,49 @@ description: Incremental skill updater that maps git diffs to affected skills an
This skill triggers when editing doc-sync-related files:
- `src/commands/doc-sync.js`
- `src/prompts/doc-sync.md`
- `src/prompts/doc-sync-refresh.md`
- `src/lib/git-helpers.js`
- `src/lib/diff-helpers.js`
- `src/lib/git-hook.js`

Keywords: doc-sync, refresh, sync, git-hook

---

You are working on **doc-sync**, the incremental skill update command (`aspens doc sync`).

## Key Files
- `src/commands/doc-sync.js` — Main command: git diff → graph rebuild → skill mapping → Claude update → write
- `src/prompts/doc-sync.md` — System prompt sent to Claude (uses `{{skill-format}}` partial)
- `src/commands/doc-sync.js` — Main command: git diff → graph rebuild → skill mapping → Claude update → write. Also contains refresh mode and `skillToDomain()` export.
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Clarify that graph rebuild is optional.

This line reads as unconditional, but doc-sync now skips graph work when --no-graph is set.

Proposed fix
-- `src/commands/doc-sync.js` — Main command: git diff → graph rebuild → skill mapping → Claude update → write. Also contains refresh mode and `skillToDomain()` export.
+- `src/commands/doc-sync.js` — Main command: git diff → (optional graph rebuild, unless `--no-graph`) → skill mapping → Claude update → write. Also contains refresh mode and `skillToDomain()` export.
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- `src/commands/doc-sync.js` — Main command: git diff → graph rebuild → skill mapping → Claude update → write. Also contains refresh mode and `skillToDomain()` export.
- `src/commands/doc-sync.js` — Main command: git diff → (optional graph rebuild, unless `--no-graph`) → skill mapping → Claude update → write. Also contains refresh mode and `skillToDomain()` export.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/skills/doc-sync/skill.md at line 23, Update the doc summary line for
src/commands/doc-sync.js to clarify that the graph rebuild step is optional and
is skipped when the --no-graph flag is passed; explicitly mention the sequence
as "git diff → (optional) graph rebuild → skill mapping → Claude update → write"
and note that doc-sync supports a refresh mode and exports skillToDomain().

- `src/prompts/doc-sync.md` — System prompt for diff-based sync (uses `{{skill-format}}` partial)
- `src/prompts/doc-sync-refresh.md` — System prompt for `--refresh` mode (full skill review)
- `src/lib/git-helpers.js` — `isGitRepo()`, `getGitDiff()`, `getGitLog()`, `getChangedFiles()` — git primitives
- `src/lib/diff-helpers.js` — `getSelectedFilesDiff()`, `buildPrioritizedDiff()`, `truncateDiff()` — diff budgeting
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

Fix helper name mismatch in the doc.

The command imports truncate (not truncateDiff) from src/lib/diff-helpers.js, so this reference is currently inaccurate.

Proposed fix
-- `src/lib/diff-helpers.js` — `getSelectedFilesDiff()`, `buildPrioritizedDiff()`, `truncateDiff()` — diff budgeting
+- `src/lib/diff-helpers.js` — `getSelectedFilesDiff()`, `buildPrioritizedDiff()`, `truncate()` — diff budgeting
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
- `src/lib/diff-helpers.js``getSelectedFilesDiff()`, `buildPrioritizedDiff()`, `truncateDiff()` — diff budgeting
- `src/lib/diff-helpers.js``getSelectedFilesDiff()`, `buildPrioritizedDiff()`, `truncate()` — diff budgeting
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/skills/doc-sync/skill.md at line 27, The doc lists `truncateDiff()`
but the module actually exports `truncate`, so update the reference in the doc
to match the real helper name: change `truncateDiff()` to `truncate` (or
alternatively change the import in the command to import `truncateDiff` from
`src/lib/diff-helpers.js` if you prefer that name); ensure the entry alongside
`getSelectedFilesDiff()` and `buildPrioritizedDiff()` uses the exact exported
symbol `truncate` so the documentation matches the code.

- `src/lib/git-hook.js` — `installGitHook()` / `removeGitHook()` for post-commit auto-sync
- `src/lib/context-builder.js` — `buildDomainContext()`, `buildBaseContext()` used by refresh mode
- `src/lib/runner.js` — `runClaude()`, `loadPrompt()`, `parseFileOutput()` shared across commands
- `src/lib/skill-writer.js` — `writeSkillFiles()` writes `{ path, content }[]` to disk
- `src/lib/graph-persistence.js` — `loadGraph()`, `extractSubgraph()`, `formatNavigationContext()` for graph context
- `src/lib/skill-writer.js` — `writeSkillFiles()`, `extractRulesFromSkills()` for output

## Key Concepts
- **Diff-based flow:** Gets `git diff HEAD~N..HEAD` and `git log`, feeds them plus existing skill contents and graph context to Claude.
- **Refresh mode (`--refresh`):** Skips diff entirely. Reviews every skill against the current codebase. Base skill refreshed first, then domain skills in parallel batches of `PARALLEL_LIMIT` (3). Also refreshes CLAUDE.md and reports uncovered domains.
- **Graph rebuild on every sync:** Calls `buildRepoGraph` + `persistGraphArtifacts` to keep `.claude/graph.json` fresh. Graph failure is non-fatal.
- **Graph-aware skill mapping:** `mapChangesToSkills()` checks not just direct file matches but also whether changed files are imported by files matching a skill's activation block.
- **Graph-aware skill mapping:** `mapChangesToSkills()` checks direct file matches via `fileMatchesActivation()` (from `skill-reader.js`) and also whether changed files are imported by files matching a skill's activation block.
- **Interactive file picker:** When diff exceeds 80k chars and TTY is available, offers multiselect with skill-relevant files pre-selected.
- **Prioritized diff:** Skill-relevant files get 60k char budget, everything else gets 20k (80k total). Cuts at `diff --git` boundaries.
- **Skill mapping:** Matches changed file names and meaningful path segments against `## Activation` sections. Generic segments excluded via `GENERIC_PATH_SEGMENTS`.
- **Prioritized diff:** `buildPrioritizedDiff()` gives skill-relevant files 60k char budget, everything else 20k (80k total). Cuts at `diff --git` boundaries.
- **Token optimization:** Affected skills sent in full; non-affected skills send only path + description line.
- **Diff truncation:** `truncateDiff()` caps at configurable limit, cutting at the last `diff --git` boundary. CLAUDE.md capped at 5,000 chars.
- **Git hook:** `installGitHook()` creates a `post-commit` hook with 5-minute cooldown lock file. `removeGitHook()` removes via `>>>` / `<<<` markers.
- **Skill-rules regeneration:** After writing, regenerates `skill-rules.json` via `extractRulesFromSkills()` so hooks see updated activation patterns.
- **Git hook:** `installGitHook()` creates a `post-commit` hook with 5-minute cooldown lock file (`/tmp/aspens-sync-*.lock` keyed by repo path hash). `removeGitHook()` removes via `>>>` / `<<<` markers.
- **Force writes:** doc-sync always calls `writeSkillFiles` with `force: true`.

## Critical Rules
- `runClaude` is called with `allowedTools: ['Read', 'Glob', 'Grep']` — doc-sync must never grant write tools.
- `parseFileOutput` restricts paths to `.claude/` prefix and `CLAUDE.md` exactly — any other path is silently dropped.
- `getGitDiff` gracefully falls back from N commits to 1 if fewer available. `actualCommits` tracks what was used.
- The command exits early with `CliError` if `.claude/skills/` doesn't exist.
- The hook cooldown uses `/tmp/aspens-sync-*.lock` keyed by repo path hash — don't change naming without updating cleanup.
- `checkMissingHooks()` in `bin/cli.js` warns when skills exist but hooks are missing (pre-0.2.2 installs).

## References
- **Patterns:** `src/lib/skill-reader.js` — `GENERIC_PATH_SEGMENTS`, `fileMatchesActivation()`, `getActivationBlock()`

---
**Last Updated:** 2026-03-24
**Last Updated:** 2026-03-28
22 changes: 15 additions & 7 deletions .claude/skills/import-graph/skill.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,23 @@ This skill triggers when editing import-graph-related files:
- `src/lib/graph-builder.js`
- `src/lib/graph-persistence.js`
- `src/commands/doc-graph.js`
- `src/templates/hooks/graph-context-prompt.mjs`
- `src/templates/hooks/graph-context-prompt.sh`
- `tests/graph-builder.test.js`
- `tests/graph-persistence.test.js`

---

You are working on the **import graph system** — static analysis that parses JS/TS and Python source files to produce dependency graphs, plus persistence/query layers for runtime use.

## Key Files
- `src/lib/graph-builder.js` — Core graph logic: walk, parse, metrics, ranking, clustering (691 lines)
- `src/lib/graph-builder.js` — Core graph logic: walk, parse, metrics, ranking, clustering (690 lines)
- `src/lib/graph-persistence.js` — Serialize, persist, load, subgraph extraction, code-map, graph-index
- `src/commands/doc-graph.js` — Standalone `aspens doc graph` command
- `src/lib/scanner.js` — Provides `detectEntryPoints()`, only internal dependency of graph-builder
- `tests/graph-builder.test.js` — Tests using temp fixture directories
- `src/templates/hooks/graph-context-prompt.mjs` — Standalone hook mirroring `extractSubgraph` logic
- `tests/graph-builder.test.js` — Graph builder tests using temp fixture directories
- `tests/graph-persistence.test.js` — Persistence layer tests

## Key Concepts
**graph-builder.js** — `buildRepoGraph(repoPath, languages?)` runs a 9-step pipeline:
Expand All @@ -32,17 +37,20 @@ You are working on the **import graph system** — static analysis that parses J
- `extractSubgraph(graph, filePaths)` returns 1-hop neighborhood of mentioned files + relevant hubs/hotspots/clusters
- `formatNavigationContext(subgraph)` renders compact markdown (~50 line budget) for prompt injection
- `extractFileReferences(prompt, graph)` tiered extraction: explicit paths → bare filenames → cluster keywords
- `generateCodeMap()` standalone overview for graph hook consumption
- `generateGraphIndex()` tiny inverted index (export names → files, hub basenames, cluster labels)
- `generateCodeMap()` / `writeCodeMap()` standalone overview for graph hook consumption
- `generateGraphIndex()` / `saveGraphIndex()` tiny inverted index (export names → files, hub basenames, cluster labels)

## Critical Rules
- **`await init` before any `parseJsImports` call.** es-module-lexer requires WASM initialization.
- **Priority formula is load-bearing:** `fanIn * 3.0 + exportCount * 1.5 + (isEntry ? 10.0 : 0) + churn * 2.0 + (1/(depth+1)) * 1.0`. Downstream consumers depend on this ranking.
- **All paths are repo-relative strings**, never absolute. Resolution functions convert abs→relative.
- **Graph artifacts are gitignored** — `ensureGraphGitignore()` adds `.claude/graph.json`, `.claude/graph-index.json`, `.claude/code-map.md` to prevent commit loops.
- **Graph artifacts are gitignored** — `ensureGraphGitignore()` (internal to persistence) adds `.claude/graph.json`, `.claude/graph-index.json`, `.claude/code-map.md` to prevent commit loops.
- **Errors are swallowed, not thrown** in graph-builder — parse failures return empty/null. The graph must always complete.
- **`extractSubgraph` logic is mirrored** in `graph-context-prompt.mjs` (standalone hook, no imports). Keep both in sync.
- **`extractSubgraph` logic is mirrored** in `graph-context-prompt.mjs` (`buildNeighborhood()`). Keep both in sync.
- **doc-sync rebuilds graph on every sync** — calls `buildRepoGraph` + `persistGraphArtifacts` to keep it fresh.

## References
- **Hook mirror:** `src/templates/hooks/graph-context-prompt.mjs`

---
**Last Updated:** 2026-03-24
**Last Updated:** 2026-03-28
10 changes: 8 additions & 2 deletions .claude/skills/repo-scanning/skill.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ This skill triggers when editing repo-scanning files:
- `src/commands/scan.js`
- `tests/scanner.test.js`

Keywords: scanRepo, detectLanguages, detectFrameworks, detectDomains, detectEntryPoints, health check

---

You are working on **aspens' repo scanning system** — a fully deterministic analyzer (no LLM calls) that detects languages, frameworks, structure, domains, entry points, size, and health issues for any repository.

## Key Files
- `src/lib/scanner.js` — Core `scanRepo()` function and all detection logic (languages, frameworks, structure, domains, entry points, size, health)
- `src/commands/scan.js` — CLI command that calls `scanRepo()`, optionally builds import graph via `graph-builder.js`, and renders pretty or JSON output
- `src/commands/scan.js` — CLI command that calls `scanRepo()`, optionally builds import graph via `graph-builder.js`, and renders pretty or JSON output. Contains `formatGraphForDisplay()` which transforms raw graph data into display-ready shape
- `src/lib/graph-builder.js` — Builds import graph; imports `detectEntryPoints` from scanner. Called by `scanCommand` but graph failure is non-fatal
- `tests/scanner.test.js` — Uses temporary fixture directories created in `tests/fixtures/scanner/`, cleaned up in `afterAll`

Expand All @@ -28,6 +30,7 @@ You are working on **aspens' repo scanning system** — a fully deterministic an
- **extraDomains:** User-specified domains merged via `mergeExtraDomains()` — marked with `userSpecified: true`, resolved against source root then repo root
- **Source root:** First match of `src`, `app`, `lib`, `server`, `pages` via `findSourceRoot()`
- **Size estimation:** Lines estimated at ~40 bytes/line from `stat.size`, walk capped at depth 5
- **Graph is opt-out:** `scanCommand` builds graph by default (`options.graph !== false`); errors are caught and only logged with `--verbose`

## Critical Rules
- **`SOURCE_EXTS`**: Only `.py`, `.ts`, `.js`, `.tsx`, `.jsx`, `.rb`, `.go`, `.rs` — adding a language requires updating this set AND the `detectLanguages` indicators
Expand All @@ -38,5 +41,8 @@ You are working on **aspens' repo scanning system** — a fully deterministic an
- **Tests use real filesystem fixtures**, not mocks — create fixtures with `createFixture(name, files)` pattern, always clean up
- **`detectEntryPoints` is exported** and reused by `graph-builder.js` — changing its signature breaks the graph builder

## References
- **No guidelines directory** — `.claude/guidelines/` does not exist yet for this domain

---
**Last Updated:** 2026-03-21
**Last Updated:** 2026-03-28
Loading
Loading