Skip to content

Feat/codex support#36

Merged
mvoutov merged 13 commits intomainfrom
feat/codex-support
Apr 7, 2026
Merged

Feat/codex support#36
mvoutov merged 13 commits intomainfrom
feat/codex-support

Conversation

@mvoutov
Copy link
Copy Markdown
Contributor

@mvoutov mvoutov commented Apr 7, 2026

What

Why

Closes #

How I tested

  • npm test passes
  • Tested against a real repo:
  • --dry-run output looks correct (if applicable)

Checklist

  • Changes are focused on a single feature or fix
  • Tests added or updated for any logic changes
  • No new dependencies added (or justified in the PR description)

Summary by CodeRabbit

  • New Features

    • Multi‑target generation (claude/codex/all) with --target/--backend, backend detection/resolve, runLLM router, Codex execution, and split write flow for canonical vs directory‑scoped outputs; Claude‑only hook behavior and Codex‑gating for certain commands.
  • Documentation

    • Many new/updated guides, skill specs, prompts, README/CLAUDE.md and AGENTS.md coverage describing multi‑target workflows, instructions-file templating, and conventions.
  • Tests

    • New suites for backend resolution, target/config lifecycle, transform/projection, and output parsing.
  • Chores

    • Package bumped to 0.6.0.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Apr 7, 2026

Caution

Review failed

Pull request was closed or merged during review

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This PR extends aspens from a Claude-only system to a multi-target architecture (Claude + Codex). It adds target/backend abstractions and config persistence, routes generation through a backend-aware runner, introduces post-generation transforms for non-claude targets, and updates prompts, docs, tests, and CLI options accordingly. (50 words)

Changes

Cohort / File(s) Summary
Skill docs & root docs
/.agents/skills/*/SKILL.md, /.claude/skills/*/skill.md, AGENTS.md, CLAUDE.md, .claude/skills/skill-rules.json
Add many new skill docs and update existing skill docs/rules to describe multi-target behavior and Codex support; add AGENTS.md and adjust CLAUDE.md.
Target & backend model
src/lib/target.js, src/lib/backend.js, .aspens.json
New TARGETS abstraction, config persistence (.aspens.json), target resolution helpers, allowed-path computation, and backend detection/resolution logic.
Transformation pipeline
src/lib/target-transform.js, src/lib/skill-writer.js
New transform utilities to project canonical Claude outputs to Codex/directory-scoped artifacts; add writeTransformedFiles() and validate transformed paths.
Runner & parsing I/O
src/lib/runner.js, src/lib/skill-reader.js, src/lib/context-builder.js, src/lib/graph-persistence.js
Add runLLM() router and runCodex() with Codex stream parsing; make parse APIs accept allowedPaths; graph persistence honors target capability; context builder supports alternate instruction files.
Commands & CLI
src/commands/doc-init.js, src/commands/doc-sync.js, src/commands/add.js, src/commands/customize.js, src/commands/doc-graph.js, bin/cli.js
Route generation via runLLM, add --target/--backend options, multi-target publish + transform, codex-only gating for certain add/customize flows, and persist/read .aspens.json.
Prompts & templates
src/prompts/*, src/prompts/partials/*
Parameterize hardcoded paths ({{skillsDir}}, {{instructionsFile}}, {{configDir}}) and adjust prompts to support multi-target variables and Codex single-file fallback behavior.
Hooks & git integration
.claude/hooks/graph-context-prompt.sh, src/lib/git-hook.js
Add graph-context prompt hook script and extend git-hook filtering to include .codex/, .agents/, AGENTS.md, and .aspens.json.
Scanner & discovery
src/lib/scanner.js, src/lib/skill-reader.js, src/lib/graph-persistence.js
Scanner detects Codex markers (hasCodexConfig, hasAgentsMd); skill discovery accepts skillFilename option; graph persistence can skip writes for targets without graph support.
Tests
tests/backend.test.js, tests/target.test.js, tests/target-transform.test.js, tests/parse-file-output.test.js, tests/prompt-loader.test.js
New and updated tests for backend resolution, target lifecycle/config, transform/projecting validations, parseFileOutput allowedPaths, and prompt loader changes.
Docs & guides
docs/target-support.md, README.md, CLAUDE.md
Add target-support guide and update README/CLAUDE.md for multi-target workflows, CLI flags, and operational guidance.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant User
    participant CLI as bin/cli.js
    participant Scanner as scanRepo()
    participant Resolver as target/backend resolver
    participant Backend as runLLM()/(claude|codex)
    participant Transformer as transformForTarget()
    participant Writer as writeSkillFiles()/writeTransformedFiles()
    participant Config as .aspens.json

    User->>CLI: aspens doc init --target all
    CLI->>Scanner: scanRepo()
    Scanner-->>CLI: repoGraph + target markers
    CLI->>Resolver: resolveTargets & resolveBackend()
    Resolver-->>CLI: targets, backendId
    CLI->>Backend: runLLM(prompt, backendId, cwd)
    Backend-->>CLI: canonicalFiles (Claude-format)
    CLI->>Transformer: transformForTarget(canonicalFiles, sourceTarget, destTarget, context)
    Transformer-->>CLI: transformedFiles
    CLI->>Writer: writeSkillFiles(canonicalFiles)
    CLI->>Writer: writeTransformedFiles(transformedFiles)
    Writer-->>CLI: write results
    CLI->>Config: writeConfig(targets, backend)
    Config-->>CLI: persisted
    CLI-->>User: Success
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related issues

Possibly related PRs

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (2 warnings)

Check name Status Explanation Resolution
Description check ⚠️ Warning The PR description is completely empty—the template placeholders for 'What', 'Why', and testing details remain unfilled with no substantive information provided. Fill out all required sections: summarize what multi-target support adds, why Codex integration was needed, confirm testing status, and verify the checklist items before merging.
Docstring Coverage ⚠️ Warning Docstring coverage is 34.83% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Feat/codex support' clearly identifies the primary feature being added: Codex CLI support alongside existing Claude functionality.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/codex-support

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 16

🧹 Nitpick comments (7)
.claude/hooks/graph-context-prompt.sh (1)

67-70: Grep pattern truncates messages containing quotes.

The pattern [^"]* stops at the first " character, so a message like [Graph] Found 5 files in "src/utils" becomes [Graph] Found 5 files in .

If this is intentional to strip JSON artifacts, consider documenting it. Otherwise:

-GRAPH_LINE=$(grep -o '\[Graph\] [^"]*' "$STDERR_FILE" | head -1)
+GRAPH_LINE=$(grep '^\[Graph\]' "$STDERR_FILE" | head -1)

Or if you need to strip only trailing JSON:

-GRAPH_LINE=$(grep -o '\[Graph\] [^"]*' "$STDERR_FILE" | head -1)
+GRAPH_LINE=$(grep -oP '\[Graph\] [^\n{]*' "$STDERR_FILE" | head -1)
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/hooks/graph-context-prompt.sh around lines 67 - 70, The grep pattern
used to populate GRAPH_LINE in graph-context-prompt.sh stops at the first
double-quote and truncates messages; update the grep regex in the GRAPH_LINE
assignment to capture the entire remainder of the line (e.g., replace the [^"]*
fragment with a pattern that matches the rest of the line) so messages like
`[Graph] Found 5 files in "src/utils"` are preserved; if you intentionally want
to strip only trailing JSON artifacts instead, apply a follow-up strip step
(e.g., a dedicated sed/awk cleanup) after setting GRAPH_LINE rather than
truncating via the grep pattern.
src/lib/graph-persistence.js (1)

496-503: Consider optional chaining for defensive access.

The target.supportsGraph access on line 501 is guarded by target &&, which is correct. However, the pattern could be slightly more concise with optional chaining.

Current code is functionally correct — this is a minor style suggestion.

✨ Optional simplification
-  if (target && target.supportsGraph === false) {
+  if (target?.supportsGraph === false) {
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/graph-persistence.js` around lines 496 - 503, In
persistGraphArtifacts, simplify the defensive check for target.supportsGraph by
using optional chaining; replace the current guarded check (target &&
target.supportsGraph === false) with a succinct target?.supportsGraph === false
condition so the function still returns serialized when target exists but
explicitly does not support graph artifacts.
src/lib/context-builder.js (1)

114-131: Hardcoded alternate file toggle may limit future extensibility.

Line 124 assumes only CLAUDE.md and AGENTS.md exist. If additional targets are added, this ternary won't scale.

Consider passing altInstructionsFile via options or deriving it from a target config, though this is fine for current scope.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/context-builder.js` around lines 114 - 131, The code in
context-builder.js hardcodes a single alternate file via the altInstructionsFile
ternary (based on instructionsFile) which won't scale; change the logic to
accept alternatives from options (e.g., options.altInstructionsFile or
options.instructionsAlternatives array) or a derived config and iterate over
that list instead of a binary ternary. Locate the instructionsFile variable and
the altInstructionsFile logic and replace the single-alt logic with a loop over
a provided array (falling back to ['AGENTS.md'] if none supplied) that checks
existsSync/readFileSafe for each candidate and pushes sections for any found
files.
docs/target-support.md (1)

58-58: Tighten repeated wording in this sentence.

Consider replacing one “right” to avoid repetition (“right instruction files in the right places”).

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@docs/target-support.md` at line 58, The sentence "Codex support is based on
writing the right instruction files in the right places" repeats "right"; change
one occurrence to a synonym (e.g., "correct", "appropriate", or "proper") so it
reads like "writing the right instruction files in the appropriate places"
(locate this sentence in docs/target-support.md).
.claude/skills/codex-support/skill.md (1)

2-3: Align codex-support with the canonical skill-domain taxonomy (or update that taxonomy).

This adds an eighth domain label. If domain handling is expected to remain the canonical seven, this should be folded into an existing domain (or the canonical list should be explicitly updated in the source-of-truth docs/tooling).

Based on learnings: "Skills should be organized into seven skill domains: agent-customization, claude-runner, doc-sync, import-graph, repo-scanning, skill-generation, and template-library".

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In @.claude/skills/codex-support/skill.md around lines 2 - 3, The skill
"codex-support" adds an eighth domain label and must be aligned with the
canonical seven-domain taxonomy: either change the skill's domain value to one
of the existing domains (agent-customization, claude-runner, doc-sync,
import-graph, repo-scanning, skill-generation, template-library) or update the
canonical taxonomy/source-of-truth to include the new domain; locate the skill
by the name "codex-support" in the skill metadata and either fold its domain
into the appropriate existing domain or modify the taxonomy docs/tooling that
enforce the seven-domain constraint so the new domain is accepted.
src/commands/add.js (1)

260-266: Unused variable: backendId computed but not used in scaffold path.

backendId is assigned at line 263 but only used in the --from code path (via generateSkillFromDoc). In the scaffold path (lines 330-375), it's never referenced.

Move backendId computation to where it's needed
 async function addSkillCommand(repoPath, name, options) {
   const config = readConfig(repoPath);
   const target = resolveSkillTarget(config);
-  const backendId = config?.backend || target.id;
   const skillsDir = join(repoPath, target.skillsDir);
   const skillFilename = target.skillFilename;
   const relSkillsDir = target.skillsDir;

The backendId is correctly computed inside generateSkillFromDoc at line 380, so this line is redundant.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/add.js` around lines 260 - 266, The variable backendId computed
in addSkillCommand is unused in the scaffold code path; remove the redundant
assignment in addSkillCommand and only compute backendId where it's actually
needed (e.g., inside generateSkillFromDoc), or move the computation to the
branch that calls generateSkillFromDoc so backendId is only created when used;
update references to backendId to ensure generateSkillFromDoc receives it from
its local computation or the branch that invokes it.
tests/target.test.js (1)

21-27: Cleanup race condition handling is acceptable but could be more explicit.

The empty catch block silently swallows errors. Consider logging in debug mode or making the comment more specific about what race condition is expected (e.g., parallel test runs, CI cleanup).

Optional: Add more context to the catch block
 afterAll(() => {
   try {
     if (existsSync(FIXTURES_DIR)) {
       rmSync(FIXTURES_DIR, { recursive: true, force: true });
     }
-  } catch { /* ignore cleanup race with other test files */ }
+  } catch (err) {
+    // Cleanup races can occur when parallel test suites share fixture parent dirs
+    if (process.env.DEBUG) console.warn('Fixture cleanup warning:', err.message);
+  }
 });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/target.test.js` around lines 21 - 27, The empty catch in the afterAll
cleanup silently swallows all errors; update the afterAll cleanup around
existsSync(FIXTURES_DIR) and rmSync(FIXTURES_DIR, { recursive: true, force: true
}) to either log the caught error at debug/trace level (e.g.,
processLogger.debug or console.debug) or replace the generic comment with a
specific one describing the expected race (e.g., "ignore cleanup race with
parallel test runs or CI artifacts removal"); ensure you still swallow the error
for test stability but include the error object in the debug log or a more
specific comment so failures can be diagnosed.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.aspens.json:
- Around line 1-7: The targets array in .aspens.json currently lists only
"codex" which prevents aspens from syncing and managing the Claude artifacts
under .claude; update the targets entry to include both platforms (set
"targets": ["claude","codex"]) so doc sync and commands like aspens add
agent/command/hook operate on both .claude and .agents, or if you've fully
migrated to Codex delete the .claude directory and keep targets as ["codex"];
adjust the "targets" key accordingly to match the repo's actual artifacts.

In `@README.md`:
- Line 7: The heading "Stop re-explaining your repo. Start shipping." is using
H3 (###) directly after the H1, causing a level jump; change that heading from
H3 to H2 (replace "### Stop re-explaining your repo. Start shipping." with "##
Stop re-explaining your repo. Start shipping.") so the markdown heading levels
increment properly and satisfy markdownlint.

In `@src/commands/customize.js`:
- Around line 16-24: Validate the requested command target before enforcing the
Codex-only gating: move or reorder the check that computes isCodexOnly (and the
subsequent CliError throw) in customize.js so it runs after the command/target
validation logic (the code that currently executes around lines 29-37), or
explicitly validate the target first and throw an "Unknown target" error if
invalid; keep references to readConfig, isCodexOnly, and the existing CliError
but ensure the "codex-only" error is only raised for a known valid target that
happens to be codex-only.

In `@src/commands/doc-init.js`:
- Around line 522-536: The current reuseExistingCanonical branch bypasses the
improvement backend by assigning loadTargetFiles(...) directly to allFiles;
change this so loaded Claude files are stored separately (e.g., canonicalFiles)
and do not short-circuit the improvement flow—keep reuseExistingCanonical
detection but avoid setting allFiles directly, then invoke the existing
improvement/publishing logic (the backend call that normally processes allFiles)
so the repo is checked and content is updated; use the same symbols shown:
reuseExistingCanonical, _reuseSourceTarget, loadTargetFiles, and allFiles to
locate and refactor the branch.
- Around line 58-64: The fallback that wraps untagged LLM output into a single
file should only apply for single-file prompts: in parseLLMOutput, change the
condition so you only create the fallback file when there are no parsed <file>
tags AND expectedPath is provided AND the prompt was targeting a single file
(e.g., allowedPaths exists and allowedPaths.length === 1 or expectedPath ===
allowedPaths[0]); keep the other checks (text.trim().length > 50). Ensure
generateAllAtOnce continues to pass allowedPaths/expectedPath so this
restriction prevents collapsing multi-file replies into CLAUDE.md.
- Around line 1063-1116: loadReusableDomains currently produces empty domain
scopes when Codex-transformed skills have their "## Activation" removed (due to
stripActivationSection used by transformDomainSkill) because
parseActivationPatterns returns []. Update loadReusableDomains to detect when a
skill's activationPatterns (or parseActivationPatterns result) is empty and then
fall back to deriving scope from an alternate source: first attempt to read
skill rules from skill-rules.json (as handled in the Claude branch) or, if
unavailable, use frontmatter fields (e.g., skill.frontmatter.path/name/other
metadata) to build directories/files; ensure this fallback logic is applied
where activationPatterns is consumed so domains get non-empty directories/files
even when parseActivationPatterns returns [].

In `@src/commands/doc-sync.js`:
- Around line 87-89: transformForTarget is being called with only scanResult, so
Codex graph artifacts never get emitted; when invoking transformForTarget (the
call with baseFiles, sourceTarget, target, { scanResult: scan }) include the
serialized graph from the scan (e.g. graphSerialized or scan.graphSerialized) in
the options passed to transformForTarget so the function can emit the Codex
architecture skill/reference. Update the caller in publishFilesForTargets to
forward graphSerialized alongside scanResult (or add graphSerialized:
scan.graphSerialized) so transformForTarget receives both scanResult and
graphSerialized.
- Around line 318-325: The code treats an unparseable assistant reply
(parseOutput returning []) the same as "no updates", so replace the silent
success path by detecting unparseable responses: after calling
parseOutput(result.text, allowedPaths) check whether result.text contains any
expected file markers (e.g. "<file>") or modify parseOutput to return a sentinel
(null/throw) on parse failure; if parse failed, stop the spinner and surface an
error via syncSpinner.stop and p.outro (or throw) instead of reporting "Docs are
up to date". Reference parseOutput, result.text, baseFiles, files,
publishFilesForTargets, and the existing syncSpinner.stop/p.outro calls when
implementing the check.

In `@src/lib/runner.js`:
- Around line 129-145: The runCodex function is currently passing the unsafe
'--full-auto' flag which grants workspace-write/shell permissions; remove
'--full-auto' from the args array and replace it with read-only sandboxing flags
such as '--sandbox', 'read-only' and an approval policy like
'--ask-for-approval', 'never' (or 'on-request' for interactive flows) so the
codex process is constrained to read-only file exploration; update the args
construction in runCodex to push these flags (and keep existing model/cwd
handling) so callers that expect read-only behavior (e.g., allowedTools:
['Read','Glob','Grep']) are honored.

In `@src/lib/skill-writer.js`:
- Around line 48-57: The writeTransformedFiles function currently only blocks
absolute paths and traversal but must also enforce allowed repo targets; update
the loop in writeTransformedFiles to validate each file.path using the same
rules as sanitizePath: reject paths with '..' or leading '/' (already done) and
additionally only allow paths that are exactly "CLAUDE.md" or "AGENTS.md" or
that start with the safe prefixes ".claude/", ".agents/", or ".codex/"; if a
path fails this allowed-path check, push a skipped result with reason 'unsafe
path' (same structure as existing skips) before attempting any write operations.

In `@src/lib/target-transform.js`:
- Around line 183-215: buildCodexSkillRefs currently always appends the
architecture skill ref, causing AGENTS.md to reference a non-existent file when
no graph was serialized; modify buildCodexSkillRefs(baseSkill, domainSkills,
destTarget) to accept a new boolean parameter (e.g. graphSerialized or hasGraph)
and only push the architecture entry ('architecture', destTarget.skillFilename
under destTarget.skillsDir) when that flag is true, then update callers
(syncCodexSkillsSection and generateCodexSkillReferences) to pass the graph
presence value (from generateCodexSkillReferences's graphSerialized) through so
the architecture line is gated by actual graph availability.

In `@src/lib/target.js`:
- Around line 177-180: The current hasCodexArtifacts check treats a lone
AGENTS.md (TARGETS.codex.instructionsFile) as enough to infer Codex; change the
logic in hasCodexArtifacts so Codex is only detected when either
TARGETS.codex.configDir ('.codex') or TARGETS.codex.skillsDir ('.agents/skills')
exists, or when the instructionsFile exists in combination with at least one of
those other artifacts; update the boolean expression that computes
hasCodexArtifacts (reference repoPath, TARGETS.codex.configDir,
TARGETS.codex.skillsDir, TARGETS.codex.instructionsFile) so a solitary AGENTS.md
no longer flips Codex mode.
- Around line 106-113: The loop that builds dirPrefixes currently uses
split('/')[0] for t.skillsDir and t.agentsDir which reduces ".agents/skills/**"
to ".agents/**"; change it so the skills allowlist remains at the full skills
path: replace dirPrefixes.add(t.skillsDir.split('/')[0] + '/') with
dirPrefixes.add(t.skillsDir + '/') and replace
dirPrefixes.add(t.agentsDir.split('/')[0] + '/') with
dirPrefixes.add(t.agentsDir + '/skills/'); keep the same
dirPrefixes.add(t.configDir + '/') and exactFiles.add(t.instructionsFile)
behavior.

In `@src/prompts/doc-sync-refresh.md`:
- Line 13: The template path currently hardcodes "billing"
("{{skillsDir}}/billing/{{skillFilename}}"), which causes refreshes to write to
the wrong skill; change the write target to use the mapped skill variable (e.g.,
"{{skillsDir}}/{{mappedSkill}}/{{skillFilename}}" or the equivalent variable
used by the skill-mapping code), and ensure the doc-sync flow follows the
required sequence: run prerequisite checks → compute git diff → map changed
files to skills via activation patterns (use the same mapping logic that
produces mappedSkill) → send filtered segments to Claude for updates (exclude
generic segments) → perform conditional writes only for the mappedSkill output.
Ensure any code that composes file paths references the mappedSkill variable
instead of the hardcoded "billing".

In `@src/prompts/doc-sync.md`:
- Line 13: The doc-sync output template currently hardcodes the skill path
segment ("{{skillsDir}}/billing/{{skillFilename}}"), which can misroute changes;
update the template and pipeline so the skill folder is derived from the
skill-mapping step (use a dynamic placeholder like "{{skill}}" instead of
"billing") and ensure the pipeline follows the required sequence: prerequisite
checks → git diff → skill mapping via activation patterns → Claude update
(filter generic segments) → conditional writes; update any references to the
hardcoded path in the doc-sync template and downstream write logic to use the
mapped "{{skill}}" variable so writes target the correct skill directory.

In `@tests/backend.test.js`:
- Around line 92-99: Replace the try/catch pattern with an assertion that the
call to resolveBackend throws: wrap the call in expect(() => resolveBackend({
available: { claude: false, codex: false } })) and assert it throws with a
message that contains BACKENDS.claude.installUrl and BACKENDS.codex.installUrl;
reference resolveBackend and BACKENDS.claude.installUrl /
BACKENDS.codex.installUrl so the test fails if no error is thrown and still
verifies both URLs are in the error message.

---

Nitpick comments:
In @.claude/hooks/graph-context-prompt.sh:
- Around line 67-70: The grep pattern used to populate GRAPH_LINE in
graph-context-prompt.sh stops at the first double-quote and truncates messages;
update the grep regex in the GRAPH_LINE assignment to capture the entire
remainder of the line (e.g., replace the [^"]* fragment with a pattern that
matches the rest of the line) so messages like `[Graph] Found 5 files in
"src/utils"` are preserved; if you intentionally want to strip only trailing
JSON artifacts instead, apply a follow-up strip step (e.g., a dedicated sed/awk
cleanup) after setting GRAPH_LINE rather than truncating via the grep pattern.

In @.claude/skills/codex-support/skill.md:
- Around line 2-3: The skill "codex-support" adds an eighth domain label and
must be aligned with the canonical seven-domain taxonomy: either change the
skill's domain value to one of the existing domains (agent-customization,
claude-runner, doc-sync, import-graph, repo-scanning, skill-generation,
template-library) or update the canonical taxonomy/source-of-truth to include
the new domain; locate the skill by the name "codex-support" in the skill
metadata and either fold its domain into the appropriate existing domain or
modify the taxonomy docs/tooling that enforce the seven-domain constraint so the
new domain is accepted.

In `@docs/target-support.md`:
- Line 58: The sentence "Codex support is based on writing the right instruction
files in the right places" repeats "right"; change one occurrence to a synonym
(e.g., "correct", "appropriate", or "proper") so it reads like "writing the
right instruction files in the appropriate places" (locate this sentence in
docs/target-support.md).

In `@src/commands/add.js`:
- Around line 260-266: The variable backendId computed in addSkillCommand is
unused in the scaffold code path; remove the redundant assignment in
addSkillCommand and only compute backendId where it's actually needed (e.g.,
inside generateSkillFromDoc), or move the computation to the branch that calls
generateSkillFromDoc so backendId is only created when used; update references
to backendId to ensure generateSkillFromDoc receives it from its local
computation or the branch that invokes it.

In `@src/lib/context-builder.js`:
- Around line 114-131: The code in context-builder.js hardcodes a single
alternate file via the altInstructionsFile ternary (based on instructionsFile)
which won't scale; change the logic to accept alternatives from options (e.g.,
options.altInstructionsFile or options.instructionsAlternatives array) or a
derived config and iterate over that list instead of a binary ternary. Locate
the instructionsFile variable and the altInstructionsFile logic and replace the
single-alt logic with a loop over a provided array (falling back to
['AGENTS.md'] if none supplied) that checks existsSync/readFileSafe for each
candidate and pushes sections for any found files.

In `@src/lib/graph-persistence.js`:
- Around line 496-503: In persistGraphArtifacts, simplify the defensive check
for target.supportsGraph by using optional chaining; replace the current guarded
check (target && target.supportsGraph === false) with a succinct
target?.supportsGraph === false condition so the function still returns
serialized when target exists but explicitly does not support graph artifacts.

In `@tests/target.test.js`:
- Around line 21-27: The empty catch in the afterAll cleanup silently swallows
all errors; update the afterAll cleanup around existsSync(FIXTURES_DIR) and
rmSync(FIXTURES_DIR, { recursive: true, force: true }) to either log the caught
error at debug/trace level (e.g., processLogger.debug or console.debug) or
replace the generic comment with a specific one describing the expected race
(e.g., "ignore cleanup race with parallel test runs or CI artifacts removal");
ensure you still swallow the error for test stability but include the error
object in the debug log or a more specific comment so failures can be diagnosed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: cae6a65e-cdfd-4846-90b2-b949ea276914

📥 Commits

Reviewing files that changed from the base of the PR and between 81d0874 and 185e870.

📒 Files selected for processing (56)
  • .agents/skills/agent-customization/SKILL.md
  • .agents/skills/architecture/SKILL.md
  • .agents/skills/architecture/references/code-map.md
  • .agents/skills/base/SKILL.md
  • .agents/skills/claude-runner/SKILL.md
  • .agents/skills/codex-support/SKILL.md
  • .agents/skills/doc-sync/SKILL.md
  • .agents/skills/import-graph/SKILL.md
  • .agents/skills/repo-scanning/SKILL.md
  • .agents/skills/skill-generation/SKILL.md
  • .agents/skills/template-library/SKILL.md
  • .aspens.json
  • .claude/hooks/graph-context-prompt.sh
  • .claude/skills/agent-customization/skill.md
  • .claude/skills/base/skill.md
  • .claude/skills/claude-runner/skill.md
  • .claude/skills/codex-support/skill.md
  • .claude/skills/doc-sync/skill.md
  • .claude/skills/import-graph/skill.md
  • .claude/skills/repo-scanning/skill.md
  • .claude/skills/skill-generation/skill.md
  • .claude/skills/skill-rules.json
  • .claude/skills/template-library/skill.md
  • AGENTS.md
  • CLAUDE.md
  • README.md
  • bin/cli.js
  • docs/target-support.md
  • src/commands/add.js
  • src/commands/customize.js
  • src/commands/doc-graph.js
  • src/commands/doc-init.js
  • src/commands/doc-sync.js
  • src/lib/backend.js
  • src/lib/context-builder.js
  • src/lib/git-hook.js
  • src/lib/graph-persistence.js
  • src/lib/runner.js
  • src/lib/scanner.js
  • src/lib/skill-reader.js
  • src/lib/skill-writer.js
  • src/lib/target-transform.js
  • src/lib/target.js
  • src/prompts/doc-init-claudemd.md
  • src/prompts/doc-init-domain.md
  • src/prompts/doc-init.md
  • src/prompts/doc-sync-refresh.md
  • src/prompts/doc-sync.md
  • src/prompts/partials/examples.md
  • src/prompts/partials/guideline-format.md
  • src/prompts/partials/skill-format.md
  • tests/backend.test.js
  • tests/parse-file-output.test.js
  • tests/prompt-loader.test.js
  • tests/target-transform.test.js
  • tests/target.test.js

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
src/commands/doc-init.js (1)

86-91: Module-level mutable state is a maintenance risk.

The _backendId, _primaryTarget, _allowedPaths, _repoPath, and _reuseSourceTarget variables create implicit coupling across functions. Consider passing these as parameters or bundling into a context object for testability.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/doc-init.js` around lines 86 - 91, The module-level mutable
variables (_backendId, _primaryTarget, _allowedPaths, _repoPath,
_reuseSourceTarget) introduce implicit coupling; refactor by creating a single
context object (e.g., docInitContext) that holds those properties and pass it
explicitly into functions that currently read/write them (notably docInitCommand
and runLLM and any helper functions referenced in this file). Update signatures
to accept the context parameter, replace direct references to the top-level
variables with context.property access, and initialize the context inside
docInitCommand before invoking downstream functions to preserve behavior and
improve testability.
src/lib/target.js (2)

204-219: loadConfig auto-persists inferred config by default.

When recovering config from artifacts, the function writes .aspens.json unless persist: false is passed. This could surprise users who only wanted to read config. Consider documenting this side effect in the JSDoc.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/target.js` around lines 204 - 219, The loadConfig function
auto-persists an inferred config by calling writeConfig(repoPath, inferred)
unless options.persist is explicitly false; update the JSDoc for loadConfig to
clearly state this side effect (that when readConfig returns null and
inferConfig produces a config, the function will write .aspens.json unless
options.persist === false) and reference the behavior involving readConfig,
inferConfig, and writeConfig so callers know to pass { persist: false } when
they only want to read without writing.

8-9: mkdirSync is imported but never used.

The import includes mkdirSync but writeConfig writes directly to the repo root (.aspens.json), which doesn't require directory creation. This is harmless dead code but could be cleaned up.

Remove unused import
-import { existsSync, readFileSync, writeFileSync, mkdirSync } from 'fs';
+import { existsSync, readFileSync, writeFileSync } from 'fs';
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/target.js` around lines 8 - 9, Remove the unused mkdirSync import
from the top-level import list in src/lib/target.js; update the import statement
that currently imports existsSync, readFileSync, writeFileSync, mkdirSync from
'fs' to only import the used symbols (existsSync, readFileSync, writeFileSync)
so there is no dead/unused import; you can leave writeConfig and other functions
(e.g., writeConfig, any uses of join/dirname) unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/commands/add.js`:
- Line 8: The file defines a duplicate runLLM implementation that duplicates
logic already exported from src/lib/runner.js; update the import list in
src/commands/add.js to include runLLM from '../lib/runner.js' (alongside
runClaude, runCodex, loadPrompt, parseFileOutput) and then remove the local
runLLM function declaration (the duplicate) so the module uses the shared runLLM
export; ensure any references in add.js call the imported runLLM.

---

Nitpick comments:
In `@src/commands/doc-init.js`:
- Around line 86-91: The module-level mutable variables (_backendId,
_primaryTarget, _allowedPaths, _repoPath, _reuseSourceTarget) introduce implicit
coupling; refactor by creating a single context object (e.g., docInitContext)
that holds those properties and pass it explicitly into functions that currently
read/write them (notably docInitCommand and runLLM and any helper functions
referenced in this file). Update signatures to accept the context parameter,
replace direct references to the top-level variables with context.property
access, and initialize the context inside docInitCommand before invoking
downstream functions to preserve behavior and improve testability.

In `@src/lib/target.js`:
- Around line 204-219: The loadConfig function auto-persists an inferred config
by calling writeConfig(repoPath, inferred) unless options.persist is explicitly
false; update the JSDoc for loadConfig to clearly state this side effect (that
when readConfig returns null and inferConfig produces a config, the function
will write .aspens.json unless options.persist === false) and reference the
behavior involving readConfig, inferConfig, and writeConfig so callers know to
pass { persist: false } when they only want to read without writing.
- Around line 8-9: Remove the unused mkdirSync import from the top-level import
list in src/lib/target.js; update the import statement that currently imports
existsSync, readFileSync, writeFileSync, mkdirSync from 'fs' to only import the
used symbols (existsSync, readFileSync, writeFileSync) so there is no
dead/unused import; you can leave writeConfig and other functions (e.g.,
writeConfig, any uses of join/dirname) unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0febfbd5-77f0-4c72-b737-ca947c9037cb

📥 Commits

Reviewing files that changed from the base of the PR and between 185e870 and e6fba62.

📒 Files selected for processing (19)
  • .aspens.json
  • .claude/hooks/graph-context-prompt.sh
  • README.md
  • docs/target-support.md
  • src/commands/add.js
  • src/commands/customize.js
  • src/commands/doc-init.js
  • src/commands/doc-sync.js
  • src/lib/context-builder.js
  • src/lib/graph-persistence.js
  • src/lib/runner.js
  • src/lib/skill-reader.js
  • src/lib/skill-writer.js
  • src/lib/target-transform.js
  • src/lib/target.js
  • src/prompts/doc-sync-refresh.md
  • src/prompts/doc-sync.md
  • tests/backend.test.js
  • tests/target.test.js
✅ Files skipped from review due to trivial changes (5)
  • .aspens.json
  • src/prompts/doc-sync-refresh.md
  • .claude/hooks/graph-context-prompt.sh
  • tests/backend.test.js
  • docs/target-support.md
🚧 Files skipped from review as they are similar to previous changes (8)
  • src/commands/customize.js
  • src/prompts/doc-sync.md
  • src/lib/graph-persistence.js
  • src/lib/context-builder.js
  • src/lib/skill-writer.js
  • tests/target.test.js
  • src/lib/target-transform.js
  • src/commands/doc-sync.js

Copy link
Copy Markdown

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/lib/target.js (1)

180-183: Simplify hasCodexArtifacts to the effective condition.

Current logic is equivalent to hasCodexConfig || hasCodexSkills. Keeping the minimal expression makes intent clearer and avoids future drift.

Proposed simplification
-  const hasCodexArtifacts =
-    hasCodexConfig ||
-    hasCodexSkills ||
-    (hasCodexInstructions && (hasCodexConfig || hasCodexSkills));
+  const hasCodexArtifacts = hasCodexConfig || hasCodexSkills;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/target.js` around lines 180 - 183, The expression assigned to
hasCodexArtifacts is redundant: simplify its definition to the effective
condition hasCodexConfig || hasCodexSkills by removing the unnecessary
(hasCodexInstructions && (hasCodexConfig || hasCodexSkills)) term; update the
initializer for hasCodexArtifacts accordingly (keeping the same variable name)
and run tests to ensure no behavioral change.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/lib/target.js`:
- Around line 141-149: readConfig currently parses and returns any JSON from
CONFIG_FILE without validation; update readConfig (and the similar logic around
the other occurrence at the second block) to validate the parsed object against
the expected schema before returning: ensure top-level shape and types (e.g.,
that "targets" is an array when present, required keys exist, and other fields
have correct types), and if validation fails return null so loadConfig can fall
back to inference; implement this by adding a lightweight schema check inside
readConfig (use explicit type checks or a small validator function referenced
from readConfig) and reject malformed-but-parseable configs.

---

Nitpick comments:
In `@src/lib/target.js`:
- Around line 180-183: The expression assigned to hasCodexArtifacts is
redundant: simplify its definition to the effective condition hasCodexConfig ||
hasCodexSkills by removing the unnecessary (hasCodexInstructions &&
(hasCodexConfig || hasCodexSkills)) term; update the initializer for
hasCodexArtifacts accordingly (keeping the same variable name) and run tests to
ensure no behavioral change.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 65387ffb-5fce-433b-a038-f465630ae191

📥 Commits

Reviewing files that changed from the base of the PR and between e6fba62 and 87997f3.

📒 Files selected for processing (12)
  • .agents/skills/architecture/references/code-map.md
  • .agents/skills/claude-runner/SKILL.md
  • .agents/skills/codex-support/SKILL.md
  • .agents/skills/doc-sync/SKILL.md
  • .agents/skills/skill-generation/SKILL.md
  • .claude/skills/claude-runner/skill.md
  • .claude/skills/codex-support/skill.md
  • .claude/skills/doc-sync/skill.md
  • .claude/skills/skill-generation/skill.md
  • AGENTS.md
  • src/commands/add.js
  • src/lib/target.js
✅ Files skipped from review due to trivial changes (7)
  • .agents/skills/architecture/references/code-map.md
  • AGENTS.md
  • .agents/skills/codex-support/SKILL.md
  • .agents/skills/doc-sync/SKILL.md
  • .agents/skills/skill-generation/SKILL.md
  • .agents/skills/claude-runner/SKILL.md
  • .claude/skills/doc-sync/skill.md
🚧 Files skipped from review as they are similar to previous changes (3)
  • .claude/skills/codex-support/skill.md
  • src/commands/add.js
  • .claude/skills/claude-runner/skill.md

@mvoutov mvoutov merged commit 6efb163 into main Apr 7, 2026
2 of 3 checks passed
@mvoutov mvoutov deleted the feat/codex-support branch April 7, 2026 21:10
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant