Skip to content

feat(cli): skillforge diff <a> <b> — structural SKILL.md comparison - #15

Merged
adityachilka1 merged 1 commit into
mainfrom
feat/diff
May 24, 2026
Merged

feat(cli): skillforge diff <a> <b> — structural SKILL.md comparison#15
adityachilka1 merged 1 commit into
mainfrom
feat/diff

Conversation

@adityachilka1

@adityachilka1 adityachilka1 commented May 24, 2026

Copy link
Copy Markdown
Owner

What

`skillforge diff ` — structural comparison of two SKILL.md files. Ninth piece of the authoring workflow after `init`, `validate`, `lint`, `pack`, `install`, `update`, `format`, and `inspect`. A plain `diff` on a SKILL.md is noisy: a reordered heading, a one-word frontmatter change, and a re-wrapped paragraph all look like sprawling churn. `diff` pulls the structural signal out.

```sh

Human-readable mode

skillforge diff ./code-review-v1/SKILL.md ./code-review-v2/SKILL.md

diff …v1/SKILL.md → …v2/SKILL.md

frontmatter: 1 headings: 2 body lines: +6 -3

FRONTMATTER

~ version: 0.1.0 → 0.2.0

HEADINGS

+ Examples

~ When to use it (position 1 → 2)

BODY

+6 -3 lines (coarse)

Machine-readable mode for CI

skillforge diff ./v1/SKILL.md ./v2/SKILL.md --json
```

Why

When skill authors maintain multiple versions of a skill — or review a contributor's update — they need a structured diff: what frontmatter fields changed, what sections were added/removed, what content moved. A plain `diff` makes a one-line description tweak look like a whole-paragraph rewrite. `skillforge diff` produces the tidy structural view.

Result shape

```ts
interface DiffResult {
pathA: string;
pathB: string;
frontmatter: {
added: Record<string, unknown>;
removed: Record<string, unknown>;
changed: Array<{ key: string; before: unknown; after: unknown }>;
};
bodyHeadings: {
added: string[];
removed: string[];
reordered: Array<{ heading: string; from: number; to: number }>;
};
bodyLinesDelta: { added: number; removed: number };
identical: boolean;
}
```

Semantics

Aspect Behaviour
Inputs Two paths to SKILL.md files. Both must validate against `SkillFrontmatterSchema` — broken files are refused so the structural view stays trustworthy.
Frontmatter equality `Object.is` for scalars, deep value comparison for arrays and plain objects. JSON round-trip deliberately avoided — `undefined` in arrays would silently coerce to `null` and false-positive as equal.
Heading extraction Level-2 and level-3 (`##`, `###`) headings, document order, code-fence aware. Mirrors `inspect.ts`'s parser but widened to `###` because authors often slice a section into named subsections.
Reorder calculation Computed over the intersection of heading sets, deduped by first occurrence — a single insert doesn't cascade as N moves. Only headings present in both files appear in `reordered`.
Body line delta Multiset diff over trailing-whitespace-stripped lines, trailing empties dropped. A coarse "how much body churn?" signal — not a Myers diff (that is what `git diff` is for).
`identical` True iff zero frontmatter changes, zero heading changes, zero line delta.
Side effects None — `diff` never writes.

CLI output taste

  • Short summary header — counts at a glance, full breakdown follows.
  • One section per kind of change (`FRONTMATTER`, `HEADINGS`, `BODY`), each in dim-uppercase for tracked structural feel — matches the `inspect` aesthetic.
  • Prose-oriented: paragraphs where the comparison reads as a sentence, lists only where items are genuinely parallel.
  • Additions in green (`+`), removals in red (`-`), value changes in yellow (`~`).
  • Long string values truncated with `…` so a multi-line `description` change stays scannable.
  • `--json` emits the full `DiffResult` for CI.

Exit codes

Situation Exit
`identical: true` 0
`identical: false` (files differ) 1
Validation/IO error (missing path, broken frontmatter) 2

Matches `mcp-devtools diff` convention — CI scripts can distinguish "noisy" from "broken".

Surface

  • `src/diff.ts` — `diffSkills()` plus internal helpers. ~290 LOC. No new runtime deps; composes `gray-matter` + `SkillFrontmatterSchema`.
  • `src/diff.test.ts` — 17 tests: identical files, frontmatter add / remove / change / array reorder / equal-array no-op, heading add / remove / reorder (Examples promotes ahead of When-to-use), fenced-code-block masking, body-line delta on prose-only changes, trailing-newline no-op, refusal on invalid frontmatter in either file, file-not-found errors on either side, and a combined-changes integration check. macOS `/tmp` trap handled with `realpathSync(await mkdtemp(...))` in `beforeEach`.
  • `src/cli.ts` — new `diff` subcommand with `--json` flag and the human-readable `printDiffReport()` formatter.
  • `src/index.ts` — re-exports the new public types.
  • `README.md` — new section; status line updated.
  • `CHANGELOG.md` — `Unreleased` entry.

Gates

Check Status
`pnpm install` no drift
`pnpm typecheck` (tsc --noEmit) clean
`pnpm lint` (biome) clean
`pnpm test` (vitest) 128/128 pass (17 new + 111 existing)
`pnpm build` (tsup) ESM + DTS build success
Smoke: hand-written `a.md` / `b.md` with a frontmatter field added and a heading renamed exits 1, prints FRONTMATTER + HEADINGS + BODY sections
Smoke: same pair with `--json | jq .` exits 1, well-formed JSON with all top-level keys
Smoke: same file twice exits 0, prints `≡ — structurally identical`
Smoke: missing path exits 2

On `diff` vs `format` / `inspect`

`format` fixes envelope drift. `inspect` reads a single skill. `diff` compares two skills. The three compose: format both files, inspect each, then diff. Each does one thing.

Don't refactor

No changes to `format` / `lint` / `inspect`. No new runtime deps. Uses `gray-matter` (already a dep).

Declaration of AI-Tools / LLMs usage

  • Claude (Opus) for design, implementation, tests, commit message, this PR body — reviewed by @adityachilka1 before push.

Summary by CodeRabbit

  • New Features

    • Added skillforge diff <a> <b> command to structurally compare two SKILL.md files, detecting frontmatter field changes, heading additions/removals/reordering, and body content deltas. Supports --json output and returns distinct exit codes.
  • Documentation

    • Updated CHANGELOG.md and README.md with the new diff command documentation.
  • Tests

    • Added comprehensive test suite for diff functionality across multiple comparison scenarios.

Review Change Stack

@coderabbitai

coderabbitai Bot commented May 24, 2026

Copy link
Copy Markdown

Caution

Review failed

Pull request was closed or merged during review

📝 Walkthrough

Walkthrough

This PR introduces skillforge diff <a> <b>, a new CLI command for structurally comparing two SKILL.md files. It adds a side-effect-free library module that computes frontmatter field changes, heading additions/removals/reorders, and body line deltas; wires it into the CLI with human-readable and JSON output; includes a comprehensive test suite; and documents the feature in user-facing guides.

Changes

Diff Feature Implementation

Layer / File(s) Summary
Diff library core logic
src/diff.ts
Core structural comparison module with deep frontmatter equality checking, heading extraction/diffing (skipping code blocks), body line delta computation via multiset normalization, async file reading, and frontmatter schema validation. Exports DiffResult and related types plus diffSkills(pathA, pathB) entrypoint.
Test suite for diff library
src/diff.test.ts
Comprehensive Vitest suite validating identical files, frontmatter additions/removals/changes (including array reorders), heading additions/removals/reorders, body line delta counting (excluding trailing newlines), validation errors for invalid frontmatter and missing files, and combined multi-aspect diffs.
CLI command registration and formatting
src/cli.ts
Registers diff <a> <b> command with --json flag, handles exit codes (0=identical, 1=differ, 2=error), prints human-readable reports summarizing frontmatter/heading/body-line changes with per-field details, and formats values (strings, arrays, objects) for inline display.
Public API re-exports
src/index.ts
Exposes diffSkills and diff-related types (DiffResult, FrontmatterDiff, FrontmatterChange, BodyHeadingsDiff, BodyLinesDelta, HeadingMove) from the package entry point.
User-facing documentation
CHANGELOG.md, README.md
Documents new skillforge diff <a> <b> command in version v0.0.2 status list, CLI usage section, and changelog, describing structural comparison scope, output modes, exit-code semantics, and validation requirements.

Sequence Diagram

sequenceDiagram
  participant User
  participant CLI
  participant diffSkills
  participant FileOps
  participant FrontmatterValidator
  participant DiffEngine
  User->>CLI: skillforge diff <a> <b>
  CLI->>diffSkills: diffSkills(pathA, pathB)
  diffSkills->>FileOps: read both files concurrently
  FileOps-->>diffSkills: file contents
  diffSkills->>FrontmatterValidator: parse and validate YAML frontmatter
  FrontmatterValidator-->>diffSkills: validated frontmatter or error
  diffSkills->>DiffEngine: compute frontmatter diff (added/removed/changed)
  diffSkills->>DiffEngine: extract and diff headings (added/removed/reordered)
  diffSkills->>DiffEngine: compute body line deltas (normalized)
  DiffEngine-->>diffSkills: DiffResult with all computed deltas
  diffSkills-->>CLI: DiffResult
  alt Identical
    CLI->>User: exit 0
  else Differ
    CLI->>User: print report + exit 1
  else Error
    CLI->>User: error message + exit 2
  end
Loading

🎯 3 (Moderate) | ⏱️ ~25 minutes

🐰 A rabbit hops through SKILL.md files with glee,
Diffing frontmatter, headings with such precision!
Arrays and bodies compared with care,
Fenced code blocks skipped with flair,
Exit codes tell the tale: same or different, error or free!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 58.33% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly and clearly describes the main change: a new CLI feature for structural comparison of SKILL.md files, which aligns perfectly with the PR's core objective.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ 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/diff

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint skipped: no ESLint configuration detected in root package.json. To enable, add eslint to devDependencies.


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.

@adityachilka1
adityachilka1 merged commit aea7423 into main May 24, 2026
3 of 4 checks passed
@adityachilka1
adityachilka1 deleted the feat/diff branch May 24, 2026 13:15
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