Skip to content

Fix/doc sync prioritize#29

Merged
mvoutov merged 7 commits intomainfrom
fix/doc-sync-prioritize
Mar 24, 2026
Merged

Fix/doc sync prioritize#29
mvoutov merged 7 commits intomainfrom
fix/doc-sync-prioritize

Conversation

@mvoutov
Copy link
Copy Markdown
Contributor

@mvoutov mvoutov commented Mar 24, 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

    • New CLI: aspens doc graph, aspens add skill (including --from) and doc sync --refresh; --hooks-only flag.
  • Improvements

    • Interactive multiselect for large doc-sync diffs and prioritized diff truncation.
    • Centralized CLI error handling and clearer timeout/env warnings.
    • Automatic generation/refresh of skill rules and merged settings; graph artifacts auto-ignored in Git.
  • Tests

    • New tests for add-skill, activation matching, and timeout resolution.

@coderabbitai
Copy link
Copy Markdown

coderabbitai bot commented Mar 24, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a1b36257-fc71-4239-a31a-3b7f396c60e5

📥 Commits

Reviewing files that changed from the base of the PR and between 6f943f8 and aa261f7.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (2)
  • CHANGELOG.md
  • package.json
✅ Files skipped from review due to trivial changes (2)
  • package.json
  • CHANGELOG.md

Walkthrough

Adds skill scaffolding (aspens add skill with --from), a aspens doc graph/doc sync --refresh import‑graph workflow with persisted .claude artifacts, new git-hook and git helper modules, skill read/write/validation modules, prioritized diff selection and interactive picker, centralized timeout resolution, and CliError-based CLI control flow.

Changes

Cohort / File(s) Summary
Documentation
\.claude/skills/.../skill.md, CLAUDE.md, CHANGELOG.md
Docs updated to reflect new aspens doc graph, graph-persistence, skill-reader/writer/runner changes, skill-rule generation, hook/settings scope, and CliError convention.
CLI entrypoint
bin/cli.js
Wired doc sync --refresh, added skill type for aspens add and --from/--timeout/--model/--verbose options.
Add command & prompts
src/commands/add.js, src/prompts/add-skill.md, tests/add-skill.test.js
Implements addSkillCommand (list/scaffold/--from modes), runs Claude prompt to generate skill files, writes .claude/skills/<name>/skill.md, sanitizes names, and regenerates .claude/skills/skill-rules.json.
Doc-sync / doc-graph / graph persistence
src/commands/doc-sync.js, src/commands/doc-graph.js*, src/lib/graph-persistence.js, \.gitignore
Adds --refresh path to rebuild/persist import graph, persists .claude/graph.json/index/code-map, ensures .gitignore entries, and wires doc-graph command.
Skill I/O & activation parsing
src/lib/skill-reader.js, src/lib/skill-writer.js*
New activation extraction/matching APIs and documented expansion of writer responsibilities (rule generation, domain patterns, settings merge).
Runner & validation
src/lib/runner.js*
Runner surface documented to include validateSkillFiles() alongside existing runner functions.
Git helpers & hooks
src/lib/git-helpers.js, src/lib/git-hook.js, tests/git-hook.test.js
New git helper utilities (isGitRepo, getGitDiff/getChangedFiles/getGitLog) and dedicated install/remove hook functions with managed block/cooldown; tests updated to import new module.
Diff helpers
src/lib/diff-helpers.js
New selected-files diff, prioritized 80k budgeting (60k skill-relevant / 20k other), and safe truncation utilities.
Timeout & errors
src/lib/timeout.js, src/lib/errors.js, src/commands/customize.js, src/commands/doc-init.js
Added resolveTimeout() with env precedence and envWarning, extended CliError to accept cause, and replaced inline timeout logic with resolver and warnings.
Prompts & behavior rules
src/prompts/doc-sync.md, src/prompts/doc-sync-refresh.md, src/prompts/doc-init-claudemd.md
Prompts updated for diff-truncation handling, doc-refresh workflow, and mandatory CLAUDE.md Behavior section.
Tests
tests/skill-mapper.test.js, tests/timeout.test.js, tests/add-skill.test.js, tests/git-hook.test.js
Added/updated tests for activation parsing, timeout precedence, add-skill scaffolding, and git-hook behavior.
Misc / packaging
package.json
Bumped version 0.3.0 → 0.4.0.

Files marked with * are referenced/expanded in docs and include new or changed modules introduced by this PR.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant CLI as bin/cli.js
    participant AddCmd as src/commands/add.js
    participant Claude as Claude API
    participant FS as File System
    participant SkillLib as src/lib/skill-reader.js
    participant Rules as src/lib/skill-writer.js

    User->>CLI: aspens add skill --from <path>
    CLI->>AddCmd: addCommand('skill', name, {from, timeout, model})
    AddCmd->>FS: validate reference file exists
    AddCmd->>AddCmd: loadPrompt('add-skill', ...)
    AddCmd->>Claude: runClaude(prompt + repo context)
    Claude-->>AddCmd: generated skill file(s)
    AddCmd->>FS: parseFileOutput → write skill files
    AddCmd->>SkillLib: extract activation/metadata
    AddCmd->>Rules: extractRulesFromSkills() → write skill-rules.json
    FS-->>User: skill created and rules updated
Loading
sequenceDiagram
    participant User
    participant CLI as bin/cli.js
    participant DocSync as src/commands/doc-sync.js
    participant Git as src/lib/git-helpers.js
    participant Graph as src/lib/graph-builder.js
    participant Persist as src/lib/graph-persistence.js
    participant Claude as Claude API
    participant FS as File System

    User->>CLI: aspens doc sync --refresh
    CLI->>DocSync: docSyncCommand(path, {refresh: true})
    DocSync->>Git: scan repo / getChangedFiles()
    Git-->>DocSync: changed file list
    DocSync->>Graph: buildRepoGraph(repoPath)
    Graph->>Persist: persistGraphArtifacts(rawGraph)
    Persist-->>DocSync: graph artifacts (.claude/*)
    loop batched domains
        DocSync->>Claude: run doc-sync-refresh prompt for domain
        Claude-->>DocSync: updated skill content
    end
    DocSync->>FS: write updated skill files
    DocSync-->>User: report coverage and updates
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

  • feat: graph #21 — import-graph/doc-graph and graph-persistence work (strong code overlap with graph artifacts and doc-graph).
  • Fix/doc sync hook automation #15 — git-hook installation/removal refactor and related hook logic (directly related to new git-hook module).
  • Fixes skills generation #16 — skill I/O, rule generation, and hook/settings changes (overlapping skill-reader/writer and rules generation).
🚥 Pre-merge checks | ❌ 3

❌ Failed checks (2 warnings, 1 inconclusive)

Check name Status Explanation Resolution
Description check ⚠️ Warning PR description is entirely a template with placeholders unfilled. No concrete details about what changes were made, why they were needed, or how they were tested. All checkboxes remain unchecked and rationale fields are empty. Fill in all template sections: briefly describe the diff prioritization/interactive picker feature added, explain why it improves doc sync, confirm test results, and check off applicable checkboxes.
Docstring Coverage ⚠️ Warning Docstring coverage is 14.29% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive Title is vague and uses non-descriptive terms ('fix' + 'doc sync prioritize'). While it hints at a doc sync issue, it doesn't clearly explain what problem is being solved or what the main change accomplishes. Use a more descriptive title that clearly indicates the main change, e.g., 'Add diff prioritization and interactive file picker to doc sync' or 'Implement skill-aware diff budgeting for doc sync'.

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

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix/doc-sync-prioritize

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: 6

🧹 Nitpick comments (9)
tests/timeout.test.js (1)

43-73: Add edge-case tests for empty and partially numeric env values.

Given Line 16/17 behavior in src/lib/timeout.js, add coverage for ASPENS_TIMEOUT='' and ASPENS_TIMEOUT='30s' so malformed input handling is locked down.

Proposed test additions
+  it('returns envWarning when env var is empty string', () => {
+    process.env.ASPENS_TIMEOUT = '';
+    const { timeoutMs, envWarning } = resolveTimeout(undefined, 120);
+    expect(envWarning).toBe(true);
+    expect(timeoutMs).toBe(120000);
+  });
+
+  it('returns envWarning when env var is partially numeric', () => {
+    process.env.ASPENS_TIMEOUT = '30s';
+    const { timeoutMs, envWarning } = resolveTimeout(undefined, 120);
+    expect(envWarning).toBe(true);
+    expect(timeoutMs).toBe(120000);
+  });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@tests/timeout.test.js` around lines 43 - 73, Add two edge-case tests for
resolveTimeout to assert malformed env inputs: set process.env.ASPENS_TIMEOUT =
'' and process.env.ASPENS_TIMEOUT = '30s', call resolveTimeout(undefined, 120)
and ensure envWarning is true and timeoutMs equals the default 120000; mirror
the style of existing tests (clean/reset env between tests if needed) and place
them alongside the other ASPENS_TIMEOUT cases in tests/timeout.test.js so
resolveTimeout's handling of empty and partially numeric strings is covered.
src/commands/customize.js (1)

14-14: Surface invalid ASPENS_TIMEOUT warnings in this command too.

Line 14 ignores envWarning, so bad env input silently falls back. Emitting a warning here would align behavior with other commands and reduce config confusion.

Proposed tweak
-  const { timeoutMs } = resolveTimeout(options.timeout, 300);
+  const { timeoutMs, envWarning } = resolveTimeout(options.timeout, 300);
+  if (envWarning) {
+    p.log.warn('Invalid ASPENS_TIMEOUT; using default timeout.');
+  }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/commands/customize.js` at line 14, resolveTimeout currently returns an
envWarning that is ignored in customize.js; update the destructuring to capture
envWarning (e.g. const { timeoutMs, envWarning } =
resolveTimeout(options.timeout, 300);) and, if envWarning is truthy, surface it
(e.g. emit a warning via console.warn or the existing logger.warn) so invalid
ASPENS_TIMEOUT inputs are reported; keep timeoutMs usage unchanged.
src/lib/errors.js (1)

12-13: Use a nullish check when forwarding cause.

Current truthy check drops falsy causes. Prefer cause === undefined so all explicit causes are preserved.

Proposed fix
-  constructor(message, { exitCode = 1, logged = false, cause } = {}) {
-    super(message, cause ? { cause } : undefined);
+  constructor(message, { exitCode = 1, logged = false, cause } = {}) {
+    super(message, cause === undefined ? undefined : { cause });
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/errors.js` around lines 12 - 13, The constructor currently forwards
the cause using a truthy check which drops falsy explicit causes; update the
super call in the constructor (the line with super(message, ...)) to forward the
cause when it is explicitly provided (use a check like cause !== undefined or
cause === undefined ? undefined : { cause }) so that null/false/0/'' are
preserved as explicit causes.
src/commands/doc-init.js (1)

22-27: Consider handling envWarning for consistency.

resolveTimeout returns { timeoutMs, envWarning }, but envWarning is ignored here. In doc-sync.js, this warning is used to notify users of invalid ASPENS_TIMEOUT values. Consider logging a warning if envWarning is true.

♻️ Optional: add envWarning handling
 function autoTimeout(scan, userTimeout) {
   const sizeDefaults = { 'small': 120, 'medium': 300, 'large': 600, 'very-large': 900 };
   const fallback = sizeDefaults[scan.size?.category] || 300;
-  const { timeoutMs } = resolveTimeout(userTimeout, fallback);
-  return timeoutMs;
+  const { timeoutMs, envWarning } = resolveTimeout(userTimeout, fallback);
+  return { timeoutMs, envWarning };
 }

Then destructure and optionally warn in the caller.

🤖 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 22 - 27, autoTimeout currently ignores
the envWarning returned by resolveTimeout; update autoTimeout to destructure
both { timeoutMs, envWarning } from resolveTimeout(scan, fallback) and, when
envWarning is truthy, emit the same warning used in doc-sync.js (e.g., call the
project's warning logger or console.warn with a clear message) so users are
notified of invalid ASPENS_TIMEOUT values; reference the autoTimeout function
and resolveTimeout call when making the change.
CLAUDE.md (1)

20-38: Add language specifier to fenced code block.

The architecture diagram code block on line 20 is missing a language specifier. Consider adding text or plaintext for lint compliance.

♻️ Add language specifier
-```
+```text
 bin/cli.js              # entry point — Commander program, CliError handler
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@CLAUDE.md` around lines 20 - 38, The fenced code block that shows the
repository file tree (the block containing "bin/cli.js ... tests/") is missing a
language specifier; update that opening fence to include a plain text language
(for example use "text" or "plaintext") so the block becomes ```text to satisfy
the linter and ensure consistent rendering.
src/lib/git-helpers.js (1)

1-10: Minor: isGitRepo uses execSync while others use execFileSync.

execFileSync is generally preferred (no shell interpolation risks). Consider consistency:

Optional: use execFileSync
 export function isGitRepo(repoPath) {
   try {
-    execSync('git rev-parse --git-dir', { cwd: repoPath, stdio: 'pipe', timeout: 5000 });
+    execFileSync('git', ['rev-parse', '--git-dir'], { cwd: repoPath, stdio: 'pipe', timeout: 5000 });
     return true;
   } catch {
     return false;
   }
 }
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/git-helpers.js` around lines 1 - 10, The isGitRepo function uses
execSync which invokes a shell while other helpers use execFileSync; change
isGitRepo to call execFileSync('git', ['rev-parse','--git-dir'], { cwd:
repoPath, stdio: 'pipe', timeout: 5000 }) so it avoids shell interpolation and
matches the existing pattern, preserving the same try/catch return-true/false
behavior in the isGitRepo export.
src/lib/diff-helpers.js (1)

45-52: Edge case: truncation falls back to maxChars if no diff --git boundary found.

When the diff contains no diff --git marker after position 0, lastHunkBoundary is -1, so cutPoint becomes maxChars—which could still cut mid-line. This is acceptable for rare edge cases, but worth noting.

Optional: cut at last newline instead
   const truncated = diff.slice(0, maxChars);
   const lastHunkBoundary = truncated.lastIndexOf('\ndiff --git');
-  const cutPoint = lastHunkBoundary > 0 ? lastHunkBoundary : maxChars;
+  const cutPoint = lastHunkBoundary > 0 ? lastHunkBoundary : truncated.lastIndexOf('\n');
+  const finalCut = cutPoint > 0 ? cutPoint : maxChars;
-  return diff.slice(0, cutPoint) + `\n\n... (diff truncated — use Read tool to see full files)`;
+  return diff.slice(0, finalCut) + `\n\n... (diff truncated — use Read tool to see full files)`;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/lib/diff-helpers.js` around lines 45 - 52, The truncateDiff function
currently falls back to cutPoint === maxChars which can split a line mid-text;
update truncateDiff to, when lastHunkBoundary <= 0, search truncated
(diff.slice(0, maxChars)) for the last newline character ('\n') and use that
index (or maxChars if none found) as the cutPoint so truncation prefers
whole-line boundaries; refer to the truncateDiff function and variables
truncated, lastHunkBoundary, and cutPoint when making the change.
tests/add-skill.test.js (1)

72-80: Date comparison has a rare edge case.

If the test runs exactly at midnight UTC while addCommand executes on the previous day (or vice versa), the assertion could fail. Unlikely in practice, but worth knowing.

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

In `@tests/add-skill.test.js` around lines 72 - 80, The test's strict equality on
a single ISO date can fail around UTC midnight; update the assertion in the test
that calls addCommand and reads the generated skill.md to accept a small
UTC-boundary variance: compute today = new Date().toISOString().split('T')[0]
and also yesterday = new Date(Date.now() -
86400000).toISOString().split('T')[0], then assert that content contains either
`**Last Updated:** ${today}` or `**Last Updated:** ${yesterday}` (or
alternatively parse the date from content and assert it's within one day);
modify the test surrounding addCommand, readFileSync, TEST_DIR and the skill.md
check accordingly.
src/commands/add.js (1)

302-309: Use the shared writer for scaffolded skills.

This path still hand-writes .claude/skills/<name>/skill.md. Reusing writeSkillFiles() keeps add skill aligned with the rest of the skill pipeline's create/overwrite behavior instead of growing a second write path here.

Based on learnings, write skills to .claude/skills/<name>/skill.md in the target repository using the skill-writer module.

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

In `@src/commands/add.js` around lines 302 - 309, Replace the direct fs writes to
skillDir/skillPath with the shared skill-writer: stop using
mkdirSync()/writeFileSync() in add.js and call writeSkillFiles(...) to create
the .claude/skills/<name>/skill.md in the target repo so the add flow reuses the
same create/overwrite semantics; reference the existing scaffold string and pass
it (and the target repo/skillsDir context) into writeSkillFiles, then keep the
existing console logs and updateSkillRules(skillsDir) call after the writer
completes.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In @.claude/skills/skill-generation/skill.md:
- Around line 22-32: The pipeline definition is inconsistent between the file
list and Key Concepts: update the sequence so both places use the same 9-step
flow; choose one canonical sequence (e.g., either "scan → graph → discovery →
strategy → mode → generate → validate → preview → write → hooks" or "scan +
graph → discovery → strategy → mode → generate → validate → write → hooks") and
replace the other occurrence accordingly in this document (update the bullet
under `src/commands/doc-init.js` and the "9-step pipeline" line in Key Concepts)
so both `src/commands/doc-init.js` description and the Key Concepts paragraph
match exactly.

In `@src/commands/add.js`:
- Around line 364-377: The current code writes every file returned by
parseFileOutput() directly (iterating over files and calling writeFileSync on
fullPath), which allows a model to overwrite unrelated repo files; instead,
ensure you reject or ignore any file whose path is not exactly
.claude/skills/<skillName>/skill.md (or otherwise constrained into the
.claude/skills/<skillName>/ directory), and route the approved content through
the existing writeSkillFiles(...) function from the skill-writer module so the
command uses the same safe write path as the rest of the pipeline; update the
code around parseFileOutput, the files loop, and use writeSkillFiles(skillName,
content, repoPath) (or the actual signature) rather than writeFileSync to
perform the final write.

In `@src/commands/doc-sync.js`:
- Around line 127-146: The picker allows an empty selection which then calls
getSelectedFilesDiff(repoPath, selectedFiles, actualCommits) with selectedFiles
=== [], removing path filtering; guard against this by checking selectedFiles
after the p.multiselect return: if selectedFiles.length === 0, call p.cancel('No
files selected') and return (or set activeDiff to an empty/zero diff and skip
sending), otherwise proceed; update the logic around selectedFiles,
changedFiles, and the activeDiff assignment that currently calls
getSelectedFilesDiff to ensure an empty selection never falls back to the full
diff.
- Around line 247-256: After writeSkillFiles(...) completes, regenerate the
skill rules artifact so hooks see updated Activation/Keywords; call the same
rule-build logic used elsewhere (e.g., invoke updateSkillRules or the function
used by refreshAllSkills()) immediately after writeSkillFiles returns, import
the helper if necessary, run it synchronously and surface any errors before
exiting, and ensure the skill-rules.json file is written/overwritten so the
runtime uses the new patterns.
- Around line 373-454: The parsed Claude outputs (from parseFileOutput) are
being pushed wholesale into allUpdatedFiles, allowing unrelated files to
overwrite other skills or CLAUDE.md; instead, after calling
parseFileOutput(result.text) filter the returned files to an allowlist for the
current refresh: in the base refresh block (where baseSpinner is used) accept
only files whose path matches the expected base file(s) for that pass; in the
domain batch block (inside the batch.map async where skillSpinner is used)
filter files to only include those with file.path === skill.path (or other
allowed paths for that skill) before pushing to allUpdatedFiles; and in the
CLAUDE.md branch (using claudeMdPath / 'CLAUDE.md') accept only files that
target CLAUDE.md. Only push the filtered results to allUpdatedFiles and preserve
the existing handling of empty results and spinner messages.

In `@src/lib/timeout.js`:
- Around line 16-19: The current ASPENS_TIMEOUT handling in src/lib/timeout.js
uses parseInt and accepts strings like "30s"; change the logic that reads
process.env.ASPENS_TIMEOUT so you only accept a pure numeric string (e.g., match
/^\d+$/) before converting to a number, and if it fails validation do not use
the partially parsed value but instead return the default/undefined timeout with
envWarning: true; update the branch that currently returns { timeoutMs: parsed *
1000, envWarning: false } to only run when the raw env value is a pure integer
string and otherwise set envWarning true and avoid using parseInt results.

---

Nitpick comments:
In `@CLAUDE.md`:
- Around line 20-38: The fenced code block that shows the repository file tree
(the block containing "bin/cli.js ... tests/") is missing a language specifier;
update that opening fence to include a plain text language (for example use
"text" or "plaintext") so the block becomes ```text to satisfy the linter and
ensure consistent rendering.

In `@src/commands/add.js`:
- Around line 302-309: Replace the direct fs writes to skillDir/skillPath with
the shared skill-writer: stop using mkdirSync()/writeFileSync() in add.js and
call writeSkillFiles(...) to create the .claude/skills/<name>/skill.md in the
target repo so the add flow reuses the same create/overwrite semantics;
reference the existing scaffold string and pass it (and the target
repo/skillsDir context) into writeSkillFiles, then keep the existing console
logs and updateSkillRules(skillsDir) call after the writer completes.

In `@src/commands/customize.js`:
- Line 14: resolveTimeout currently returns an envWarning that is ignored in
customize.js; update the destructuring to capture envWarning (e.g. const {
timeoutMs, envWarning } = resolveTimeout(options.timeout, 300);) and, if
envWarning is truthy, surface it (e.g. emit a warning via console.warn or the
existing logger.warn) so invalid ASPENS_TIMEOUT inputs are reported; keep
timeoutMs usage unchanged.

In `@src/commands/doc-init.js`:
- Around line 22-27: autoTimeout currently ignores the envWarning returned by
resolveTimeout; update autoTimeout to destructure both { timeoutMs, envWarning }
from resolveTimeout(scan, fallback) and, when envWarning is truthy, emit the
same warning used in doc-sync.js (e.g., call the project's warning logger or
console.warn with a clear message) so users are notified of invalid
ASPENS_TIMEOUT values; reference the autoTimeout function and resolveTimeout
call when making the change.

In `@src/lib/diff-helpers.js`:
- Around line 45-52: The truncateDiff function currently falls back to cutPoint
=== maxChars which can split a line mid-text; update truncateDiff to, when
lastHunkBoundary <= 0, search truncated (diff.slice(0, maxChars)) for the last
newline character ('\n') and use that index (or maxChars if none found) as the
cutPoint so truncation prefers whole-line boundaries; refer to the truncateDiff
function and variables truncated, lastHunkBoundary, and cutPoint when making the
change.

In `@src/lib/errors.js`:
- Around line 12-13: The constructor currently forwards the cause using a truthy
check which drops falsy explicit causes; update the super call in the
constructor (the line with super(message, ...)) to forward the cause when it is
explicitly provided (use a check like cause !== undefined or cause === undefined
? undefined : { cause }) so that null/false/0/'' are preserved as explicit
causes.

In `@src/lib/git-helpers.js`:
- Around line 1-10: The isGitRepo function uses execSync which invokes a shell
while other helpers use execFileSync; change isGitRepo to call
execFileSync('git', ['rev-parse','--git-dir'], { cwd: repoPath, stdio: 'pipe',
timeout: 5000 }) so it avoids shell interpolation and matches the existing
pattern, preserving the same try/catch return-true/false behavior in the
isGitRepo export.

In `@tests/add-skill.test.js`:
- Around line 72-80: The test's strict equality on a single ISO date can fail
around UTC midnight; update the assertion in the test that calls addCommand and
reads the generated skill.md to accept a small UTC-boundary variance: compute
today = new Date().toISOString().split('T')[0] and also yesterday = new
Date(Date.now() - 86400000).toISOString().split('T')[0], then assert that
content contains either `**Last Updated:** ${today}` or `**Last Updated:**
${yesterday}` (or alternatively parse the date from content and assert it's
within one day); modify the test surrounding addCommand, readFileSync, TEST_DIR
and the skill.md check accordingly.

In `@tests/timeout.test.js`:
- Around line 43-73: Add two edge-case tests for resolveTimeout to assert
malformed env inputs: set process.env.ASPENS_TIMEOUT = '' and
process.env.ASPENS_TIMEOUT = '30s', call resolveTimeout(undefined, 120) and
ensure envWarning is true and timeoutMs equals the default 120000; mirror the
style of existing tests (clean/reset env between tests if needed) and place them
alongside the other ASPENS_TIMEOUT cases in tests/timeout.test.js so
resolveTimeout's handling of empty and partially numeric strings is covered.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 0284929a-77f6-4a9b-b6a6-0b9b0d3b087a

📥 Commits

Reviewing files that changed from the base of the PR and between b1e3a71 and 51d3d82.

📒 Files selected for processing (28)
  • .claude/skills/base/skill.md
  • .claude/skills/claude-runner/skill.md
  • .claude/skills/doc-sync/skill.md
  • .claude/skills/import-graph/skill.md
  • .claude/skills/skill-generation/skill.md
  • .claude/skills/template-library/skill.md
  • .gitignore
  • CLAUDE.md
  • bin/cli.js
  • src/commands/add.js
  • src/commands/customize.js
  • src/commands/doc-init.js
  • src/commands/doc-sync.js
  • src/lib/diff-helpers.js
  • src/lib/errors.js
  • src/lib/git-helpers.js
  • src/lib/git-hook.js
  • src/lib/graph-persistence.js
  • src/lib/skill-reader.js
  • src/lib/timeout.js
  • src/prompts/add-skill.md
  • src/prompts/doc-init-claudemd.md
  • src/prompts/doc-sync-refresh.md
  • src/prompts/doc-sync.md
  • tests/add-skill.test.js
  • tests/git-hook.test.js
  • tests/skill-mapper.test.js
  • tests/timeout.test.js

@mvoutov mvoutov marked this pull request as draft March 24, 2026 21:00
@mvoutov mvoutov marked this pull request as ready for review March 24, 2026 21:00
@mvoutov mvoutov merged commit 2f85c42 into main Mar 24, 2026
3 checks passed
@mvoutov mvoutov deleted the fix/doc-sync-prioritize branch March 24, 2026 21:01
@coderabbitai coderabbitai bot mentioned this pull request Mar 28, 2026
6 tasks
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