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
39 changes: 39 additions & 0 deletions .agents/skills/agent-customization/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
---
name: agent-customization
description: LLM-powered injection of project context into installed agent templates via `aspens customize agents`
---

## Activation

This skill triggers when editing agent-customization files:
- `src/commands/customize.js`
- `src/prompts/customize-agents.md`

---

You are working on **agent customization** — the feature that reads a project's skills and AGENTS.md, then uses Claude CLI to inject project-specific context into generic agent files in `.claude/agents/`.

## Key Files
- `src/commands/customize.js` — Main command: finds agents, gathers context, calls Claude, writes results
- `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
- **Claude-only feature:** Customize command reads `.aspens.json` and throws `CliError` if repo is configured for Codex-only (`targets: ['codex']`). Codex CLI has no agent concept.
- **Context gathering:** `gatherProjectContext()` reads AGENTS.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
- **Claude-only** — throws `CliError` for Codex-only repos. Checks `readConfig(repoPath)` for target config.
- **Read-only tools only** — Claude agents never get write tools. All output goes through `parseFileOutput()` → `writeSkillFiles()`.
- **Context truncation** — AGENTS.md is capped at 3000 chars to avoid blowing up prompt size. Skills are read in full.
- **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.

---
**Last Updated:** 2026-04-02
20 changes: 20 additions & 0 deletions .agents/skills/architecture/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
---
name: architecture
description: >
Use when modifying imports, creating new files, refactoring modules,
or understanding how components relate. Not needed for simple single-file edits.
---

# Architecture

This skill provides codebase structure and import graph data.

When you need to understand file relationships, hub files, or domain clusters,
check `references/code-map.md` for the full import graph analysis.

## Key Rules

- Check hub files (high fan-in) before modifying - changes propagate widely
- Respect domain cluster boundaries - keep related files together
- Check cross-domain dependencies before creating new imports

22 changes: 22 additions & 0 deletions .agents/skills/architecture/references/code-map.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
# Code Map

## Key Files

**Hub files (most depended-on):**
- `src/lib/runner.js` - 8 dependents
- `src/lib/errors.js` - 7 dependents
- `src/lib/scanner.js` - 7 dependents
- `src/lib/target.js` - 7 dependents
- `src/lib/skill-writer.js` - 6 dependents

**Domain clusters:**

| Domain | Files | Top entries |
|--------|-------|-------------|
| src | 37 | `src/lib/runner.js`, `src/commands/doc-init.js`, `src/commands/doc-sync.js` |

**High-churn hotspots:**
- `src/commands/doc-init.js` - 27 changes
- `src/commands/doc-sync.js` - 19 changes
- `src/lib/runner.js` - 16 changes

68 changes: 68 additions & 0 deletions .agents/skills/base/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
---
name: base
description: Core conventions, tech stack, and project structure for aspens
---

## Activation

This is a **base skill** that always loads when working in this repository.

---

You are working in **aspens** — a CLI tool that generates and maintains AI-ready documentation (skill files + AGENTS.md) for any codebase. Supports multiple output targets (Claude Code, Codex CLI).

## Tech Stack
Node.js (ESM) | Commander | Vitest | es-module-lexer | @clack/prompts | picocolors

## Commands
- `npm test` — Run vitest suite
- `npm start` / `node bin/cli.js` — Run CLI
- `aspens scan [path]` — Deterministic repo analysis (no LLM)
- `aspens doc init [path]` — Generate skills + hooks + AGENTS.md (supports `--target claude|codex|all`, `--backend claude|codex`)
- `aspens doc sync [path]` — Incremental skill updates from git diffs
- `aspens doc graph [path]` — Rebuild import graph cache (`.claude/graph.json`)
- `aspens add <type> [name]` — Install templates (agents, commands, hooks)
- `aspens customize agents` — Inject project context into installed agents

## Architecture
CLI entry (`bin/cli.js`) → command handlers (`src/commands/`) → lib modules (`src/lib/`)

- `src/lib/scanner.js` — Deterministic repo scanner (languages, frameworks, domains, structure)
- `src/lib/graph-builder.js` — Static import analysis via es-module-lexer (hub files, clusters, priority)
- `src/lib/graph-persistence.js` — Graph serialization, subgraph extraction, code-map + index generation
- `src/lib/runner.js` — Claude/Codex CLI wrapper (`runClaude` for stream-json, `runCodex` for Codex JSONL)
- `src/lib/context-builder.js` — Assembles repo files into prompt-friendly context
- `src/lib/skill-writer.js` — Writes skill files and directory-scoped 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/lib/target.js` — Target definitions (claude/codex), config persistence (`.aspens.json`)
- `src/lib/target-transform.js` — Transforms Claude-format output to other target formats
- `src/lib/backend.js` — Backend detection and resolution (which CLI generates content)
- `src/prompts/` — Prompt templates with `{{partial}}` and `{{variable}}` substitution
- `src/templates/` — Bundled agents, commands, hooks, and settings for `aspens add` / `doc init`

## Critical Conventions
- **Pure ESM** — `"type": "module"` throughout; use `import`/`export`, never `require()`
- **es-module-lexer WASM** — must `await init` before calling `parse()` in graph-builder
- **Claude CLI execution** — `runClaude()` spawns `claude -p` with stream-json; always use `--verbose` flag with stream-json
- **Codex CLI execution** — `runCodex()` spawns `codex exec --json --sandbox read-only --ask-for-approval never --ephemeral`; returns `{ text, usage }` matching `runClaude` interface
- **Path sanitization** — `parseFileOutput()` restricts writes to `.claude/` and `AGENTS.md` by default; accepts `allowedPaths` override for multi-target
- **Prompt partials** — `{{name}}` in prompt files resolves to `src/prompts/partials/name.md` first, then falls back to template variables
- **Target/Backend distinction** — Target = output format/location; Backend = which LLM CLI generates content. Config persisted in `.aspens.json`
- **Scanner is deterministic** — no LLM calls; pure filesystem analysis
- **CliError pattern** — command handlers throw `CliError` instead of calling `process.exit()`; caught at top level in `bin/cli.js`

## Structure
- `bin/` — CLI entry point (commander setup, CliError handler)
- `src/commands/` — Command handlers (scan, doc-init, doc-sync, doc-graph, add, customize)
- `src/lib/` — Core library modules
- `src/prompts/` — Prompt templates + partials
- `src/templates/` — Installable agents, commands, hooks, settings
- `tests/` — Vitest test files

---
**Last Updated:** 2026-04-02
53 changes: 53 additions & 0 deletions .agents/skills/claude-runner/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
name: claude-runner
description: Claude/Codex CLI execution layer — prompt loading, stream-json parsing, file output extraction, path sanitization, skill file writing, and skill rule generation
---

## Activation

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/*skill-mapper*`, `tests/*timeout*`

---

You are working on the **CLI execution layer** — the bridge between assembled prompts and the `claude -p` / `codex exec` CLIs, plus skill file I/O.

## Key Files
- `src/lib/runner.js` — `runClaude()`, `runCodex()`, `runLLM()`, `loadPrompt()`, `parseFileOutput()`, `validateSkillFiles()`, `extractResultFromStream()` (exported); `extractResultFromCodexStream()`, `normalizeCodexItemType()`, `collectCodexText()`, `handleStreamEvent()`, `sanitizePath()` (internal)
- `src/lib/skill-writer.js` — `writeSkillFiles()`, `writeTransformedFiles()`, `extractRulesFromSkills()`, `generateDomainPatterns()`, `mergeSettings()`
- `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 `skill-format.md`, `guideline-format.md`, `examples.md`

## Key Concepts
- **Stream-JSON protocol (Claude):** `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.
- **JSONL protocol (Codex):** `runCodex()` spawns `codex exec --json --sandbox read-only --ask-for-approval never --ephemeral`. Prompt is passed via **stdin** (`'-'` placeholder arg) to avoid shell arg length limits. Stdin write happens **after** event handlers are attached so fast failures are captured. Events: `item.completed`/`item.updated` with normalized types.
- **Unified routing:** `runLLM(prompt, options, backendId)` is the shared entry point — dispatches to `runClaude()` or `runCodex()` based on `backendId`. Exported from `runner.js` so command handlers no longer need local routing helpers.
- **Codex internals (private):** `normalizeCodexItemType()` converts PascalCase/kebab-case to snake_case. `collectCodexText()` recursively extracts text from nested event content. Both are internal to runner.js.
- **Prompt templating:** `loadPrompt(name, vars)` resolves `{{partial-name}}` from `src/prompts/partials/` first, then substitutes `{{varName}}` from `vars`. Target-specific vars (`skillsDir`, `skillFilename`, `instructionsFile`, `configDir`) are passed by command handlers.
- **File output parsing:** Primary: `<file path="...">content</file>` XML tags. Fallback: `<!-- file: path -->` comment markers. `parseFileOutput(output, allowedPaths)` accepts optional `{ dirPrefixes, exactFiles }` to override default allowed paths.
- **Path sanitization:** `sanitizePath(rawPath, allowedPaths)` (internal) blocks `..` traversal, absolute paths. Defaults: `.claude/` prefix + `AGENTS.md` exact. Multi-target callers pass expanded allowed paths via `getAllowedPaths()` from `target.js`.
- **Validation:** `validateSkillFiles()` checks for truncation (XML tag collisions), missing frontmatter, missing sections, bad file path references.
- **Skill rules generation:** `extractRulesFromSkills()` reads all skills via `skill-reader.js`, produces `skill-rules.json` (v2.0) with file patterns, keywords, and intent patterns.
- **Domain patterns:** `generateDomainPatterns()` converts file patterns to bash `detect_skill_domain()` function using `BEGIN/END` markers.
- **Settings merge:** `mergeSettings()` merges aspens hook config into existing `settings.json`, detecting aspens-managed hooks by `ASPENS_HOOK_MARKERS` (`skill-activation-prompt`, `post-tool-use-tracker`).
- **Directory-scoped writes:** `writeTransformedFiles()` handles files outside `.claude/` (e.g., `src/billing/AGENTS.md`) with explicit path allowlist — only `AGENTS.md`, `AGENTS.md` exact files and `.claude/`, `.agents/`, `.codex/` prefixes are permitted.
- **`findSkillFiles` matching:** Only matches the exact `skillFilename` (e.g., `skill.md` or `SKILL.md`), not arbitrary `.md` files in the skills directory.

## Critical Rules
- **Both `--verbose` and `--output-format stream-json` are required for Claude** — omitting either breaks stream parsing.
- **Codex uses `--json --sandbox read-only --ask-for-approval never --ephemeral`** — `--sandbox read-only` restricts filesystem access, `--ask-for-approval never` skips prompts, `--ephemeral` avoids persisting conversation. Prompt goes via stdin, not as a CLI arg.
- **Codex stdin write order matters** — event handlers (`stdout`, `stderr`, `close`, `error`) must be attached before writing to stdin, so fast failures are captured.
- **Path sanitization is non-negotiable** — `sanitizePath()` blocks `..` traversal, absolute paths, and any path not in the allowed set.
- **Prompt partials resolve before variables** — `{{skill-format}}` resolves to `partials/skill-format.md` first. If no file, falls through to variable substitution.
- **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`, replaces matching entries, preserves everything else.
- **Debug mode:** Set `ASPENS_DEBUG=1` to dump raw stream-json to `$TMPDIR/aspens-debug-stream.json` (Claude) or `$TMPDIR/aspens-debug-codex-stream.json` (Codex). Codex also logs exit code and output length to stderr.

---
**Last Updated:** 2026-04-07
52 changes: 52 additions & 0 deletions .agents/skills/codex-support/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
---
name: codex-support
description: Multi-target output system — target abstraction, backend routing, content transforms for Codex CLI and future targets
---

## Activation

This skill triggers when editing codex-support files:
- `src/lib/target.js`
- `src/lib/target-transform.js`
- `src/lib/backend.js`
- `tests/target.test.js`
- `tests/target-transform.test.js`
- `tests/backend.test.js`

Keywords: codex, target, backend, AGENTS.md, directory-scoped, transform, multi-target

---

You are working on **multi-target output support** — the system that lets aspens generate documentation for Claude Code, Codex CLI, or both simultaneously.

## Key Files
- `src/lib/target.js` — Target definitions (`TARGETS`), `getAllowedPaths()`, path helpers, config persistence (`.aspens.json`)
- `src/lib/target-transform.js` — Transforms Claude-format output to other target formats; `projectCodexDomainDocs()`, `validateTransformedFiles()`, content sanitization
- `src/lib/backend.js` — Backend detection (`detectAvailableBackends`) and resolution (`resolveBackend`) with fallback logic

## Key Concepts
- **Target vs Backend:** Target = where output goes (claude → `.claude/skills/`, codex → `.agents/skills/` + directory-scoped `AGENTS.md`). Backend = which LLM CLI generates the content (`claude -p` or `codex exec`).
- **Target definitions:** `TARGETS.claude` (centralized) and `TARGETS.codex` (directory-scoped). Each defines paths and capability flags: `supportsHooks`, `supportsSettings`, `supportsGraph`, `supportsSkills`, `needsActivationSection`, `needsCodeMapEmbed`, `supportsMCP`. Codex also has `maxInstructionsBytes` (32 KiB) and `userSkillsDir`.
- **Canonical generation:** Generation always produces Claude-canonical format first. Prompts always receive `CANONICAL_VARS` (hardcoded Claude paths from `doc-init.js`). Transforms run **after** generation to produce other target formats.
- **Content transform:** `transformForTarget()` remaps paths and content. For Codex: base skill → root `AGENTS.md`, domain skills → both `.agents/skills/{domain}/SKILL.md` and source directory `AGENTS.md`. `generateCodexSkillReferences()` creates `.agents/skills/architecture/` with code-map data.
- **Content sanitization:** `sanitizeCodexInstructions()` and `sanitizeCodexSkill()` strip Claude-specific references (hooks, skill-rules.json, Claude Code mentions) from Codex output.
- **`getAllowedPaths(targets)`** — Returns `{ dirPrefixes, exactFiles }` union across all active targets. Dir prefixes use **full** target paths (e.g., `.agents/skills/`, not `.agents/`), providing tighter path validation.
- **Backend detection:** `detectAvailableBackends()` checks if `claude` and `codex` CLIs are installed. `resolveBackend()` picks best match: explicit flag > target match > fallback.
- **Config persistence:** `.aspens.json` at repo root stores `{ targets, backend, version }`. `readConfig()` returns `null` if missing **or if the config is structurally invalid** — callers default to `'claude'` target. Validation via internal `isValidConfig()` ensures `targets` is a non-empty array of known target keys, `backend` (if present) is a known target key, and `version` (if present) is a string.
- **Multi-target publish:** `doc-sync` uses `publishFilesForTargets()` to generate output for all configured targets from a single LLM run — source target files kept as-is, other targets get transforms applied.
- **Codex inference tightened:** `inferConfig()` only adds `'codex'` to inferred targets when `.codex/` config dir or `.agents/skills/` dir exists — a standalone `AGENTS.md` without either is not sufficient.
- **Conditional architecture ref:** Codex `buildCodexSkillRefs()` only includes the architecture skill reference when a graph was actually serialized (`hasGraph` parameter).

## Critical Rules
- **Generation always targets Claude canonical format first** — transforms run after, never during. Prompts always receive `CANONICAL_VARS`.
- **Split write logic:** `writeSkillFiles()` handles direct-write files (`.claude/`, `.agents/`, `AGENTS.md`, root `AGENTS.md`). `writeTransformedFiles()` handles directory-scoped `AGENTS.md` (e.g., `src/billing/AGENTS.md`) with an explicit path allowlist and warn-and-skip policy.
- **Path safety:** `validateTransformedFiles()` in `target-transform.js` rejects absolute paths, traversal, and unexpected filenames. `writeTransformedFiles()` enforces the same checks plus an allowlist (`AGENTS.md`/`AGENTS.md` exact, `.claude/`/`.agents/`/`.codex/` prefixes).
- **Codex-only restrictions:** `add agent/command/hook` and `customize agents` throw `CliError` for Codex-only repos. `add skill` works for both targets.
- **Graph/hooks are Claude-only** — `persistGraphArtifacts()` returns data without writing files when `target.supportsGraph === false`. Hook installation skipped when `supportsHooks === false`.
- **Config validation is defensive** — `readConfig()` treats malformed but parseable JSON (e.g., wrong types for `targets`/`backend`/`version`) as invalid and returns `null`, same as missing config.

## References
- **Patterns:** See `src/lib/target.js` for all target property definitions

---
**Last Updated:** 2026-04-07
Loading
Loading