Skip to content

MRSF CLI

License: MITMRSF v1.0 Draft@mrsf/cli on npmnpm downloads (cli)

Command-line tool and Node.js library for Sidemark — the Markdown Review Sidecar Format (MRSF v1.0).

Validate sidecars, add and resolve review comments, re-anchor comments after document edits, and integrate MRSF into CI/CD pipelines — all from the terminal.

  ╔╦╗╦═╗╔═╗╔═╗
  ║║║╠╦╝╚═╗╠╣
  ╩ ╩╩╚═╚═╝╚
  Markdown Review Sidecar Format

Quick Start

bash
# Install globally
npm install -g @mrsf/cli

# …or run without installing
npx @mrsf/cli --help

From source

bash
git clone https://github.com/wictorwilen/MRSF.git
cd MRSF/cli
npm install
npm run build
node dist/bin.js --help

Commands

mrsf validate [files...]

Validate sidecar files against the JSON Schema and MRSF spec rules.

bash
# Validate a single sidecar
mrsf validate docs/architecture.md.review.yaml

# Validate all sidecars discovered in the workspace
mrsf validate

# Treat warnings as errors (useful for CI)
mrsf validate --strict
OptionDescription
-s, --strictTreat warnings as errors (exit non-zero)

mrsf add <document>

Add a review comment to a document's sidecar file. Creates the sidecar if it doesn't exist. Automatically populates selected_text from the document when a line is specified, and stamps the current git commit.

bash
# Add a line comment
mrsf add docs/api.md \
  --author "alice" \
  --text "Clarify the rate limit behavior" \
  --line 42

# Add a column-span comment with type and severity
mrsf add docs/api.md \
  --author "bob" \
  --text "Typo: should be 'response'" \
  --line 18 --start-column 10 --end-column 18 \
  --type suggestion --severity low

# Reply to an existing comment
mrsf add docs/api.md \
  --author "alice" \
  --text "Good catch, fixed in abc123" \
  --reply-to 3eeccbd3

# Attach tool-specific x_* metadata
mrsf add docs/api.md \
  --author "bot" \
  --text "Flagging this for follow-up" \
  --line 27 \
  --ext x_source=triage-bot \
  --ext x_score=0.91 \
  --ext 'x_labels=["needs-review","docs"]'
OptionDescription
-a, --author <name>Comment author (required)
-t, --text <text>Comment body (required)
-l, --line <n>Line number (1-based)
--end-line <n>End line (inclusive)
--start-column <n>Start column (0-based)
--end-column <n>End column (0-based)
--type <type>Category: suggestion, issue, question, accuracy, style, clarity
--severity <level>Importance: low, medium, high
--reply-to <id>Reply to an existing comment by ID
--selected-text <text>Override auto-detected selected text
--ext <key=value>Add an x_* extension field; repeat for multiple fields

--ext values are stored as flat x_* fields in the sidecar. Primitive JSON literals like numbers, true, false, and null are parsed automatically, and objects/arrays can be passed as JSON.


mrsf resolve <sidecar> <id>

Resolve (or unresolve) a comment by ID.

bash
# Resolve a comment
mrsf resolve docs/api.md.review.yaml 1d3c72b0

# Unresolve it
mrsf resolve --undo docs/api.md.review.yaml 1d3c72b0

# Resolve and cascade to all direct replies
mrsf resolve --cascade docs/api.md.review.yaml 1d3c72b0
OptionDescription
--cascadeAlso resolve direct replies
-u, --undoUnresolve instead of resolving

mrsf list [files...]

List and filter comments across one or more sidecar files.

bash
# List all open comments
mrsf list --open docs/api.md.review.yaml

# Filter by author and severity
mrsf list --author alice --severity high

# Summary view (counts, types, severities)
mrsf list --summary

# Machine-readable JSON output
mrsf list --json --open
OptionDescription
--openShow only open (unresolved) comments
--resolvedShow only resolved comments
--orphanedShow only orphaned comments
--author <name>Filter by author
--type <type>Filter by comment type
--severity <level>Filter by severity
--summaryShow aggregate summary instead of individual comments
--jsonOutput as JSON

mrsf reanchor [files...]

Re-anchor comments after the source document has been edited. Uses a multi-step resolution algorithm (§7.4):

  1. Diff-based shift — uses git diff to calculate line offsets
  2. Exact text match — searches for selected_text verbatim
  3. Fuzzy match — token-level LCS + Levenshtein similarity
  4. Line/column fallback — checks whether original position is still plausible
  5. Orphan — retains unresolvable comments and marks them for review
bash
# Dry run — see what would change without writing
mrsf reanchor --dry-run docs/api.md.review.yaml

# Re-anchor with a higher fuzzy threshold (stricter matching)
mrsf reanchor --threshold 0.8

# Only process sidecars for staged files (pre-commit hook)
mrsf reanchor --staged

# Disable git (pure text-based matching only)
mrsf reanchor --no-git

# Override the from-commit for all comments
mrsf reanchor --from abc1234

# Also update selected_text to match the current document text (opt-in)
mrsf reanchor --update-text
OptionDescriptionDefault
-n, --dry-runReport without modifying filesfalse
-t, --threshold <n>Fuzzy match threshold (0.0–1.0)0.6
--stagedOnly process sidecars for git-staged documentsfalse
--no-gitDisable git integration entirelygit enabled
--from <commit>Override from-commit for diff calculationper-comment commit field
--update-textAlso replace selected_text with current document textfalse

Text preservation (§6.2): By default, re-anchoring preserves the original selected_text and records the current document text in anchored_text. Use --update-text to opt in to overwriting selected_text directly.


mrsf status [files...]

Assess anchor health for each comment.

bash
mrsf status docs/api.md.review.yaml
mrsf status --json

Health states:

StateMeaning
freshselected_text matches at the recorded position
staleselected_text matches but at a different position, or commit differs
orphanedselected_text not found in the document
unknownNo selected_text to verify
OptionDescription
--jsonOutput as JSON

mrsf init <document>

Create an empty sidecar file for a Markdown document.

bash
mrsf init docs/new-feature.md
mrsf init --force docs/existing.md   # overwrite
OptionDescription
-f, --forceOverwrite an existing sidecar

mrsf rename <old-document> <new-document>

Update a sidecar after its source document has been renamed or moved.

bash
mrsf rename docs/old-name.md docs/new-name.md

Updates the document field inside the sidecar, writes it to the new path, and removes the old sidecar file.

mrsf watch [files...]

Watch files for changes and continuously validate (and optionally reanchor).

bash
# Validate all sidecars on every change
mrsf watch

# Watch specific files
mrsf watch docs/api.md docs/guide.md

# Auto-reanchor when Markdown files change
mrsf watch --reanchor

# Preview reanchor changes without writing
mrsf watch --reanchor --dry-run

# Custom threshold and strict validation
mrsf watch --reanchor --threshold 0.8 --strict

Trigger rules (avoids feedback loops):

File typeAction
.md (Markdown)Reanchor (if --reanchor is set), then validate sidecar
.review.yaml / .review.jsonValidate only
OptionDescription
--reanchorAuto-reanchor when Markdown files change
-n, --dry-runPreview reanchor changes without writing
-t, --threshold <n>Fuzzy match threshold 0.0–1.0 (default 0.6)
-s, --strictTreat validation warnings as errors
--no-gitDisable git integration
--from <commit>Override from-commit for reanchor
--update-textUpdate selected_text on reanchor
-f, --forceForce reanchor — update commit, clear audit fields
--debounce <ms>Debounce interval in ms (default 300)

Press Ctrl+C to stop — a summary of events, reanchors, and errors is printed.

Global Options

These apply to all commands:

OptionDescription
--cwd <dir>Override the working directory
--config <path>Path to .mrsf.yaml configuration
--no-colorDisable coloured output
-q, --quietSuppress informational output
-V, --versionPrint version
-h, --helpShow help

CI/CD Integration

GitHub Actions

yaml
name: MRSF Review Lint
on: [pull_request]
jobs:
  mrsf:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      - uses: actions/setup-node@v4
        with:
          node-version: 20
      - run: npx @mrsf/cli validate --strict
      - run: npx @mrsf/cli reanchor --staged --dry-run

Git Pre-commit Hook

bash
#!/usr/bin/env bash
# .git/hooks/pre-commit
npx @mrsf/cli reanchor --staged
npx @mrsf/cli validate --strict

Git Post-merge Hook

bash
#!/usr/bin/env bash
# .git/hooks/post-merge
npx @mrsf/cli reanchor

Library Usage

The CLI is built library-first. All functionality is available as importable functions:

typescript
import {
  parseSidecar,
  parseSidecarLenient,
  writeSidecar,
  validate,
  reanchorDocument,
  addComment,
  resolveComment,
  removeComment,
  filterComments,
  summarize,
  discoverSidecar,
  discoverAllSidecars,
} from "@mrsf/cli";

// Validate a sidecar programmatically
const doc = await parseSidecar("docs/api.md.review.yaml");
const result = await validate(doc);
console.log(result.valid, result.errors, result.warnings);

// Add a comment
const updated = addComment(doc, lines, {
  author: "ci-bot",
  text: "Auto-generated review comment",
  line: 10,
});
await writeSidecar("docs/api.md.review.yaml", updated);

Browser-safe reanchoring

The default @mrsf/cli entrypoint is Node-oriented and includes file system and git-backed helpers such as parseSidecar, readDocumentLines, and reanchorFile. For browser bundles and editor integrations, use the @mrsf/cli/browser subpath instead.

@mrsf/cli/browser exports the pure text-based matching and reanchoring primitives only:

typescript
import {
  applyReanchorResults,
  reanchorDocumentText,
} from "@mrsf/cli/browser";

const results = reanchorDocumentText(sidecar, markdownText);
const changed = applyReanchorResults(sidecar, results);

console.log(changed, results.map((result) => result.status));

Use this entrypoint when you already have the document text and sidecar object in memory. It intentionally excludes Node-only helpers such as sidecar discovery, file parsing, file writing, and git-aware diff reanchoring.

Configuration

Place a .mrsf.yaml at your repository root to configure sidecar discovery:

yaml
# Store all sidecars in a central directory instead of co-located
sidecar_root: .reviews

With this config, the sidecar for docs/architecture.md is resolved as .reviews/docs/architecture.md.review.yaml.

See §3.2 Alternate Sidecar Location in the spec for details.

Extension Fields (x_*)

MRSF reserves the x_ prefix for tool-specific, non-standard extension properties (see §10 Extension Fields). These fields may appear on the top-level document object or on individual comments. The CLI preserves them during round-trip writes and exposes them through the catch-all index signature on both MrsfDocument and Comment.

Built-in x_ fields

The CLI and Sidemark VS Code extension use the following extension fields:

FieldScopeTypeSet byDescription
x_reanchor_statuscomment"anchored" | "shifted" | "fuzzy" | "orphaned"mrsf reanchorResult of the most recent reanchor pass. orphaned means the anchor text could not be found in the current document.
x_reanchor_scorecommentnumber (0–1)mrsf reanchorConfidence score from the reanchor algorithm. 1.0 = exact match, lower values indicate fuzzy or shifted matches.

Adding your own fields

Any property whose key starts with x_ is valid on both the document root and on each comment. The CLI and Sidemark will silently preserve these fields through all operations (validate, add, resolve, reanchor, etc.).

yaml
comments:
  - id: abc123
    author: ci-bot
    timestamp: "2025-01-15T10:00:00Z"
    text: Possible security concern
    resolved: false
    line: 42
    # Custom extension fields
    x_ai_confidence: 0.92
    x_tool_name: security-scanner
    x_category: injection

Rules (from the spec):

  • Keys MUST start with x_ (underscore, not hyphen).
  • Implementations MUST NOT assign semantic meaning to x_ fields defined by other tools.
  • Future MRSF spec versions will never introduce normative fields with the x_ prefix.
  • Unknown x_ fields are preserved by the CLI's round-trip writer and never stripped or modified.

Programmatic access

typescript
import { parseSidecar } from "@mrsf/cli";

const doc = await parseSidecar("file.md.review.yaml");
for (const comment of doc.comments) {
  // Access built-in x_ fields
  if (comment.x_reanchor_status === "orphaned") {
    console.log(`Comment ${comment.id} is orphaned`);
  }
  // Access custom x_ fields
  const confidence = comment.x_ai_confidence as number | undefined;
}

Requirements

  • Node.js 18 or later
  • Git (optional — enables diff-based re-anchoring, commit stamping, rename detection)

License

MIT — see LICENSE.

Released under the MIT License.