Skip to content

Commit fa7f53a

Browse files
authored
Merge pull request #39 from aspenkit/monorepo-aspens-usage
fix: monorepo fix for subdirectory projects + impact + readme
2 parents 6efb163 + 2c03344 commit fa7f53a

90 files changed

Lines changed: 6598 additions & 688 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.agents/skills/architecture/references/code-map.md

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -3,20 +3,20 @@
33
## Key Files
44

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

1212
**Domain clusters:**
1313

1414
| Domain | Files | Top entries |
1515
|--------|-------|-------------|
16-
| src | 37 | `src/lib/runner.js`, `src/commands/doc-init.js`, `src/commands/doc-sync.js` |
16+
| src | 44 | `src/commands/doc-init.js`, `src/lib/runner.js`, `src/lib/target.js` |
1717

1818
**High-churn hotspots:**
19-
- `src/commands/doc-init.js` - 27 changes
20-
- `src/commands/doc-sync.js` - 19 changes
21-
- `src/lib/runner.js` - 16 changes
19+
- `src/commands/doc-init.js` - 33 changes
20+
- `src/commands/doc-sync.js` - 20 changes
21+
- `src/lib/runner.js` - 17 changes
2222

.agents/skills/base/SKILL.md

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ This is a **base skill** that always loads when working in this repository.
99

1010
---
1111

12-
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).
12+
You are working in **aspens** — a CLI that keeps coding-agent context accurate as your codebase changes. Scans repos, generates project-specific instructions and skills for Claude Code and Codex CLI, and keeps them fresh.
1313

1414
## Tech Stack
1515
Node.js (ESM) | Commander | Vitest | es-module-lexer | @clack/prompts | picocolors
@@ -18,11 +18,13 @@ Node.js (ESM) | Commander | Vitest | es-module-lexer | @clack/prompts | picocolo
1818
- `npm test` — Run vitest suite
1919
- `npm start` / `node bin/cli.js` — Run CLI
2020
- `aspens scan [path]` — Deterministic repo analysis (no LLM)
21-
- `aspens doc init [path]` — Generate skills + hooks + AGENTS.md (supports `--target claude|codex|all`, `--backend claude|codex`)
21+
- `aspens doc init [path]` — Generate skills + hooks + AGENTS.md (`--target claude|codex|all`, `--recommended` for full recommended setup)
22+
- `aspens doc impact [path]` — Show freshness, coverage, and drift of generated context (`--apply` for auto-repair, `--backend`/`--model`/`--timeout`/`--verbose` for LLM interpretation)
2223
- `aspens doc sync [path]` — Incremental skill updates from git diffs
2324
- `aspens doc graph [path]` — Rebuild import graph cache (`.claude/graph.json`)
2425
- `aspens add <type> [name]` — Install templates (agents, commands, hooks)
2526
- `aspens customize agents` — Inject project context into installed agents
27+
- `aspens save-tokens [path]` — Install token-saving session settings (`--recommended` for no-prompt install, `--remove` to uninstall)
2628

2729
## Architecture
2830
CLI entry (`bin/cli.js`) → command handlers (`src/commands/`) → lib modules (`src/lib/`)
@@ -35,15 +37,17 @@ CLI entry (`bin/cli.js`) → command handlers (`src/commands/`) → lib modules
3537
- `src/lib/skill-writer.js` — Writes skill files and directory-scoped files, generates skill-rules.json, merges settings
3638
- `src/lib/skill-reader.js` — Parses skill files, frontmatter, activation patterns, keywords
3739
- `src/lib/diff-helpers.js` — Targeted file diffs and prioritized diff truncation for doc-sync
38-
- `src/lib/git-helpers.js` — Git repo detection, diff retrieval, log formatting
39-
- `src/lib/git-hook.js` — Post-commit git hook installation/removal for auto doc-sync
40+
- `src/lib/git-helpers.js` — Git repo detection, git root resolution, diff retrieval, log formatting
41+
- `src/lib/git-hook.js` — Post-commit git hook installation/removal for auto doc-sync (monorepo-aware)
42+
- `src/lib/impact.js` — Context health analysis: domain coverage, hub surfacing, drift detection, hook health, save-tokens health, usefulness summary, value comparison, opportunities
43+
- `src/lib/save-tokens.js` — Save-tokens config defaults, settings builders, gitignore/readme generators
4044
- `src/lib/timeout.js` — Timeout resolution (`--timeout` flag > `ASPENS_TIMEOUT` env > default)
4145
- `src/lib/errors.js``CliError` class (structured errors caught by CLI top-level handler)
42-
- `src/lib/target.js` — Target definitions (claude/codex), config persistence (`.aspens.json`)
46+
- `src/lib/target.js` — Target definitions (claude/codex), config persistence (`.aspens.json`) with `saveTokens` feature config
4347
- `src/lib/target-transform.js` — Transforms Claude-format output to other target formats
4448
- `src/lib/backend.js` — Backend detection and resolution (which CLI generates content)
4549
- `src/prompts/` — Prompt templates with `{{partial}}` and `{{variable}}` substitution
46-
- `src/templates/` — Bundled agents, commands, hooks, and settings for `aspens add` / `doc init`
50+
- `src/templates/` — Bundled agents, commands, hooks, and settings for `aspens add` / `doc init` / `save-tokens`
4751

4852
## Critical Conventions
4953
- **Pure ESM**`"type": "module"` throughout; use `import`/`export`, never `require()`
@@ -55,14 +59,15 @@ CLI entry (`bin/cli.js`) → command handlers (`src/commands/`) → lib modules
5559
- **Target/Backend distinction** — Target = output format/location; Backend = which LLM CLI generates content. Config persisted in `.aspens.json`
5660
- **Scanner is deterministic** — no LLM calls; pure filesystem analysis
5761
- **CliError pattern** — command handlers throw `CliError` instead of calling `process.exit()`; caught at top level in `bin/cli.js`
62+
- **Monorepo support**`getGitRoot()` resolves the actual git root; hooks, sync, and impact scope to the subdirectory project path
5863

5964
## Structure
6065
- `bin/` — CLI entry point (commander setup, CliError handler)
61-
- `src/commands/` — Command handlers (scan, doc-init, doc-sync, doc-graph, add, customize)
66+
- `src/commands/` — Command handlers (scan, doc-init, doc-impact, doc-sync, doc-graph, add, customize, save-tokens)
6267
- `src/lib/` — Core library modules
6368
- `src/prompts/` — Prompt templates + partials
6469
- `src/templates/` — Installable agents, commands, hooks, settings
6570
- `tests/` — Vitest test files
6671

6772
---
68-
**Last Updated:** 2026-04-02
73+
**Last Updated:** 2026-04-09

.agents/skills/claude-runner/SKILL.md

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -18,15 +18,16 @@ This skill triggers when editing claude-runner files:
1818
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.
1919

2020
## Key Files
21-
- `src/lib/runner.js``runClaude()`, `runCodex()`, `runLLM()`, `loadPrompt()`, `parseFileOutput()`, `validateSkillFiles()`, `extractResultFromStream()` (exported); `extractResultFromCodexStream()`, `normalizeCodexItemType()`, `collectCodexText()`, `handleStreamEvent()`, `sanitizePath()` (internal)
21+
- `src/lib/runner.js``runClaude()`, `runCodex()`, `runLLM()`, `loadPrompt()`, `parseFileOutput()`, `validateSkillFiles()`, `extractResultFromStream()` (exported); `extractResultFromCodexStream()`, `normalizeCodexItemType()`, `collectCodexText()`, `handleStreamEvent()`, `sanitizePath()`, `getCodexExecCapabilities()` (internal)
2222
- `src/lib/skill-writer.js``writeSkillFiles()`, `writeTransformedFiles()`, `extractRulesFromSkills()`, `generateDomainPatterns()`, `mergeSettings()`
2323
- `src/lib/skill-reader.js``findSkillFiles()`, `parseFrontmatter()`, `parseActivationPatterns()`, `parseKeywords()`, `fileMatchesActivation()`, `getActivationBlock()`, `GENERIC_PATH_SEGMENTS`
2424
- `src/lib/timeout.js``resolveTimeout()` — priority: `--timeout` flag > `ASPENS_TIMEOUT` env var > caller fallback
2525
- `src/prompts/` — Markdown prompt templates; `partials/` subdir holds `skill-format.md`, `guideline-format.md`, `examples.md`
2626

2727
## Key Concepts
2828
- **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.
29-
- **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.
29+
- **JSONL protocol (Codex):** `runCodex()` spawns `codex exec --json --sandbox read-only --ephemeral`. The `--ask-for-approval never` flag is **conditionally included** based on capability detection (see below). 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.
30+
- **Codex capability detection:** `getCodexExecCapabilities()` (internal, cached) runs `codex exec --help` and checks if `--ask-for-approval` appears in the help text. Result is cached in module-level `codexExecCapabilities` variable. If the help check fails (e.g., codex not installed), capabilities default to `{ supportsAskForApproval: false }`. `runCodex()` only adds `--ask-for-approval never` when `supportsAskForApproval` is true.
3031
- **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.
3132
- **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.
3233
- **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.
@@ -35,19 +36,19 @@ You are working on the **CLI execution layer** — the bridge between assembled
3536
- **Validation:** `validateSkillFiles()` checks for truncation (XML tag collisions), missing frontmatter, missing sections, bad file path references.
3637
- **Skill rules generation:** `extractRulesFromSkills()` reads all skills via `skill-reader.js`, produces `skill-rules.json` (v2.0) with file patterns, keywords, and intent patterns.
3738
- **Domain patterns:** `generateDomainPatterns()` converts file patterns to bash `detect_skill_domain()` function using `BEGIN/END` markers.
38-
- **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`).
39+
- **Settings merge:** `mergeSettings()` merges aspens hook config into existing `settings.json`. Detects aspens-managed hooks by `ASPENS_HOOK_MARKERS` (`skill-activation-prompt`, `graph-context-prompt`, `post-tool-use-tracker`, `save-tokens-statusline`, `save-tokens-prompt-guard`, `save-tokens-precompact`). Also handles `statusLine` merging — replaces existing statusLine only if the current one is aspens-managed (detected by `isAspensHook`), preserving user-custom statusLine configs. After merging hooks, `dedupeAspensHookEntries()` removes duplicate aspens-managed entries per event type.
3940
- **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.
4041
- **`findSkillFiles` matching:** Only matches the exact `skillFilename` (e.g., `skill.md` or `SKILL.md`), not arbitrary `.md` files in the skills directory.
4142

4243
## Critical Rules
4344
- **Both `--verbose` and `--output-format stream-json` are required for Claude** — omitting either breaks stream parsing.
44-
- **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.
45+
- **Codex uses `--json --sandbox read-only --ephemeral`**`--sandbox read-only` restricts filesystem access, `--ephemeral` avoids persisting conversation. `--ask-for-approval never` is added only if `getCodexExecCapabilities()` confirms support. Prompt goes via stdin, not as a CLI arg.
4546
- **Codex stdin write order matters** — event handlers (`stdout`, `stderr`, `close`, `error`) must be attached before writing to stdin, so fast failures are captured.
4647
- **Path sanitization is non-negotiable**`sanitizePath()` blocks `..` traversal, absolute paths, and any path not in the allowed set.
4748
- **Prompt partials resolve before variables**`{{skill-format}}` resolves to `partials/skill-format.md` first. If no file, falls through to variable substitution.
4849
- **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.
49-
- **`mergeSettings` preserves non-aspens hooks** — identifies aspens hooks by `ASPENS_HOOK_MARKERS`, replaces matching entries, preserves everything else.
50+
- **`mergeSettings` preserves non-aspens hooks and statusLine** — identifies aspens hooks by `ASPENS_HOOK_MARKERS` (now includes save-tokens markers), replaces matching entries, preserves everything else. StatusLine only replaced if current one is aspens-managed. Post-merge deduplication ensures no duplicate aspens entries accumulate.
5051
- **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.
5152

5253
---
53-
**Last Updated:** 2026-04-07
54+
**Last Updated:** 2026-04-10

0 commit comments

Comments
 (0)