@mrsf/cli 0.7.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,513 +1,513 @@
1
- # MRSF CLI
2
-
3
- [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
4
- [![MRSF v1.0 Draft](https://img.shields.io/badge/MRSF-v1.0%20Draft-blue)](MRSF-v1.0.md)
5
- [![@mrsf/cli on npm](https://img.shields.io/npm/v/@mrsf/cli?label=%40mrsf%2Fcli)](https://www.npmjs.com/package/@mrsf/cli)
6
- [![npm downloads (cli)](https://img.shields.io/npm/dm/@mrsf/cli?label=cli%20downloads)](https://www.npmjs.com/package/@mrsf/cli)
7
-
8
- Command-line tool and Node.js library for **Sidemark** — the **Markdown Review Sidecar Format** ([MRSF v1.0](../MRSF-v1.0.md)).
9
-
10
- Validate sidecars, add and resolve review comments, re-anchor comments after document edits, and integrate MRSF into CI/CD pipelines — all from the terminal.
11
-
12
- ```
13
- ╔╦╗╦═╗╔═╗╔═╗
14
- ║║║╠╦╝╚═╗╠╣
15
- ╩ ╩╩╚═╚═╝╚
16
- Markdown Review Sidecar Format
17
- ```
18
-
19
- ## Quick Start
20
-
21
- ```bash
22
- # Install globally
23
- npm install -g @mrsf/cli
24
-
25
- # …or run without installing
26
- npx @mrsf/cli --help
27
- ```
28
-
29
- ### From source
30
-
31
- ```bash
32
- git clone https://github.com/wictorwilen/MRSF.git
33
- cd MRSF/cli
34
- npm install
35
- npm run build
36
- node dist/bin.js --help
37
- ```
38
-
39
- ## Browser / Embedder API (`@mrsf/cli/browser`)
40
-
41
- Use `@mrsf/cli/browser` in Vite, Electron renderers, and editor extensions that
42
- need side-effect-free MRSF helpers without Node built-ins. This entrypoint does
43
- not import `fs`, git helpers, or other Node-only APIs, and is designed to be
44
- tree-shakeable.
45
-
46
- Key exports include:
47
-
48
- - Types: `MrsfDocument`, `Comment`, `AnchorPosition`
49
- - Anchoring: `resolveAnchor`, `reanchorComment`, `reanchorDocumentText`,
50
- `toReanchorLines`, `applyReanchorResults`
51
- - Validation: `validateDocument`, `mrsfSchema`
52
- - Identity: `formatAuthor`, `parseAuthor`, `newCommentId`
53
- - Pure fuzzy helpers: `exactMatch`, `normalizedMatch`, `fuzzySearch`,
54
- `combinedScore`
55
-
56
- Example editor decoration flow:
57
-
58
- ```ts
59
- import { resolveAnchor, type Comment } from "@mrsf/cli/browser";
60
-
61
- function decorationRange(comment: Comment, text: string) {
62
- const anchor = resolveAnchor(comment, text);
63
- if (anchor.from == null || anchor.to == null) return undefined;
64
- return { from: anchor.from, to: anchor.to };
65
- }
66
- ```
67
-
68
- ## Commands
69
-
70
- ### `mrsf validate [files...]`
71
-
72
- Validate sidecar files against the JSON Schema and MRSF spec rules.
73
-
74
- ```bash
75
- # Validate a single sidecar
76
- mrsf validate docs/architecture.md.review.yaml
77
-
78
- # Validate all sidecars discovered in the workspace
79
- mrsf validate
80
-
81
- # Treat warnings as errors (useful for CI)
82
- mrsf validate --strict
83
- ```
84
-
85
- | Option | Description |
86
- |---|---|
87
- | `-s, --strict` | Treat warnings as errors (exit non-zero) |
88
-
89
- ---
90
-
91
- ### `mrsf add <document>`
92
-
93
- 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. When `--author` is omitted, MRSF uses `user.name` from the repository-local `.git/config`; global Git identity is not used.
94
-
95
- ```bash
96
- # Add a line comment
97
- mrsf add docs/api.md \
98
- --author "alice" \
99
- --text "Clarify the rate limit behavior" \
100
- --line 42
101
-
102
- # Add a column-span comment with type and severity
103
- mrsf add docs/api.md \
104
- --author "bob" \
105
- --text "Typo: should be 'response'" \
106
- --line 18 --start-column 10 --end-column 18 \
107
- --type suggestion --severity low
108
-
109
- # Reply to an existing comment
110
- mrsf add docs/api.md \
111
- --author "alice" \
112
- --text "Good catch, fixed in abc123" \
113
- --reply-to 3eeccbd3
114
-
115
- # Attach tool-specific x_* metadata
116
- mrsf add docs/api.md \
117
- --author "bot" \
118
- --text "Flagging this for follow-up" \
119
- --line 27 \
120
- --ext x_source=triage-bot \
121
- --ext x_score=0.91 \
122
- --ext 'x_labels=["needs-review","docs"]'
123
- ```
124
-
125
- | Option | Description |
126
- |---|---|
127
- | `-a, --author <name>` | Comment author; overrides repository-local Git `user.name` |
128
- | `-t, --text <text>` | Comment body (required) |
129
- | `-l, --line <n>` | Line number (1-based) |
130
- | `--end-line <n>` | End line (inclusive) |
131
- | `--start-column <n>` | Start column (0-based) |
132
- | `--end-column <n>` | End column (0-based) |
133
- | `--type <type>` | Category: `suggestion`, `issue`, `question`, `accuracy`, `style`, `clarity` |
134
- | `--severity <level>` | Importance: `low`, `medium`, `high` |
135
- | `--reply-to <id>` | Reply to an existing comment by ID |
136
- | `--selected-text <text>` | Override auto-detected selected text |
137
- | `--ext <key=value>` | Add an `x_*` extension field; repeat for multiple fields |
138
-
139
- `--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.
140
-
141
- ---
142
-
143
- ### `mrsf resolve <sidecar> <id>`
144
-
145
- Resolve (or unresolve) a comment by ID.
146
-
147
- ```bash
148
- # Resolve a comment
149
- mrsf resolve docs/api.md.review.yaml 1d3c72b0
150
-
151
- # Unresolve it
152
- mrsf resolve --undo docs/api.md.review.yaml 1d3c72b0
153
-
154
- # Resolve and cascade to all direct replies
155
- mrsf resolve --cascade docs/api.md.review.yaml 1d3c72b0
156
- ```
157
-
158
- | Option | Description |
159
- |---|---|
160
- | `--cascade` | Also resolve direct replies |
161
- | `-u, --undo` | Unresolve instead of resolving |
162
-
163
- ---
164
-
165
- ### `mrsf list [files...]`
166
-
167
- List and filter comments across one or more sidecar files.
168
-
169
- ```bash
170
- # List all open comments
171
- mrsf list --open docs/api.md.review.yaml
172
-
173
- # Filter by author and severity
174
- mrsf list --author alice --severity high
175
-
176
- # Summary view (counts, types, severities)
177
- mrsf list --summary
178
-
179
- # Machine-readable JSON output
180
- mrsf list --json --open
181
- ```
182
-
183
- | Option | Description |
184
- |---|---|
185
- | `--open` | Show only open (unresolved) comments |
186
- | `--resolved` | Show only resolved comments |
187
- | `--orphaned` | Show only orphaned comments |
188
- | `--author <name>` | Filter by author |
189
- | `--type <type>` | Filter by comment type |
190
- | `--severity <level>` | Filter by severity |
191
- | `--summary` | Show aggregate summary instead of individual comments |
192
- | `--json` | Output as JSON |
193
-
194
- ---
195
-
196
- ### `mrsf reanchor [files...]`
197
-
198
- Re-anchor comments after the source document has been edited. The repository's
199
- **Anchor Mesh** implementation combines verified Git shifts, source-revision
200
- projection, Markdown structure, bounded text matching, confidence calibration,
201
- and nearby comments as landmarks. It prefers `ambiguous` or `orphaned` results
202
- over an unsupported relocation.
203
-
204
- See the
205
- [non-normative implementation guide](https://sidemark.org/guide/reanchoring)
206
- for the complete pipeline, execution modes, tuning values, and evaluation
207
- methodology.
208
-
209
- ```bash
210
- # Dry run — see what would change without writing
211
- mrsf reanchor --dry-run docs/api.md.review.yaml
212
-
213
- # Re-anchor with a higher fuzzy threshold (stricter matching)
214
- mrsf reanchor --threshold 0.8
215
-
216
- # Only process sidecars for staged files (pre-commit hook)
217
- mrsf reanchor --staged
218
-
219
- # Disable git (pure text-based matching only)
220
- mrsf reanchor --no-git
221
-
222
- # Override the from-commit for all comments
223
- mrsf reanchor --from abc1234
224
-
225
- # Also update selected_text to match the current document text (opt-in)
226
- mrsf reanchor --update-text
227
- ```
228
-
229
- | Option | Description | Default |
230
- |---|---|---|
231
- | `-n, --dry-run` | Report without modifying files | `false` |
232
- | `-t, --threshold <n>` | Fuzzy match threshold (0.0–1.0) | `0.6` |
233
- | `--staged` | Only process sidecars for git-staged documents | `false` |
234
- | `--no-git` | Disable git integration entirely | git enabled |
235
- | `--from <commit>` | Override from-commit for diff calculation | per-comment `commit` field |
236
- | `--update-text` | Also replace `selected_text` with current document text | `false` |
237
-
238
- > **Text preservation (§6.2):** By default, re-anchoring preserves the
239
- > original `selected_text` and records the current document text in
240
- > `anchored_text`. Use `--update-text` to opt in to overwriting
241
- > `selected_text` directly.
242
-
243
- ---
244
-
245
- ### `mrsf status [files...]`
246
-
247
- Assess anchor health for each comment.
248
-
249
- ```bash
250
- mrsf status docs/api.md.review.yaml
251
- mrsf status --json
252
- ```
253
-
254
- Health states:
255
-
256
- | State | Meaning |
257
- |---|---|
258
- | `fresh` | `selected_text` matches at the recorded position |
259
- | `stale` | `selected_text` matches but at a different position, or commit differs |
260
- | `orphaned` | `selected_text` not found in the document |
261
- | `unknown` | No `selected_text` to verify |
262
-
263
- | Option | Description |
264
- |---|---|
265
- | `--json` | Output as JSON |
266
-
267
- ---
268
-
269
- ### `mrsf init <document>`
270
-
271
- Create an empty sidecar file for a Markdown document.
272
-
273
- ```bash
274
- mrsf init docs/new-feature.md
275
- mrsf init --force docs/existing.md # overwrite
276
- ```
277
-
278
- | Option | Description |
279
- |---|---|
280
- | `-f, --force` | Overwrite an existing sidecar |
281
-
282
- ---
283
-
284
- ### `mrsf rename <old-document> <new-document>`
285
-
286
- Update a sidecar after its source document has been renamed or moved.
287
-
288
- ```bash
289
- mrsf rename docs/old-name.md docs/new-name.md
290
- ```
291
-
292
- Updates the `document` field inside the sidecar, writes it to the new path, and removes the old sidecar file.
293
-
294
- ### `mrsf watch [files...]`
295
-
296
- Watch files for changes and continuously validate (and optionally reanchor).
297
-
298
- ```bash
299
- # Validate all sidecars on every change
300
- mrsf watch
301
-
302
- # Watch specific files
303
- mrsf watch docs/api.md docs/guide.md
304
-
305
- # Auto-reanchor when Markdown files change
306
- mrsf watch --reanchor
307
-
308
- # Preview reanchor changes without writing
309
- mrsf watch --reanchor --dry-run
310
-
311
- # Custom threshold and strict validation
312
- mrsf watch --reanchor --threshold 0.8 --strict
313
- ```
314
-
315
- **Trigger rules** (avoids feedback loops):
316
-
317
- | File type | Action |
318
- |---|---|
319
- | `.md` (Markdown) | Reanchor (if `--reanchor` is set), then validate sidecar |
320
- | `.review.yaml` / `.review.json` | Validate only |
321
-
322
- | Option | Description |
323
- |---|---|
324
- | `--reanchor` | Auto-reanchor when Markdown files change |
325
- | `-n, --dry-run` | Preview reanchor changes without writing |
326
- | `-t, --threshold <n>` | Fuzzy match threshold 0.0–1.0 (default `0.6`) |
327
- | `-s, --strict` | Treat validation warnings as errors |
328
- | `--no-git` | Disable git integration |
329
- | `--from <commit>` | Override from-commit for reanchor |
330
- | `--update-text` | Update `selected_text` on reanchor |
331
- | `-f, --force` | Force reanchor — update commit, clear audit fields |
332
- | `--debounce <ms>` | Debounce interval in ms (default `300`) |
333
-
334
- Press `Ctrl+C` to stop — a summary of events, reanchors, and errors is printed.
335
-
336
- ## Global Options
337
-
338
- These apply to all commands:
339
-
340
- | Option | Description |
341
- |---|---|
342
- | `--cwd <dir>` | Override the working directory |
343
- | `--config <path>` | Path to `.mrsf.yaml` configuration |
344
- | `--no-color` | Disable coloured output |
345
- | `-q, --quiet` | Suppress informational output |
346
- | `-V, --version` | Print version |
347
- | `-h, --help` | Show help |
348
-
349
- ## CI/CD Integration
350
-
351
- ### GitHub Actions
352
-
353
- ```yaml
354
- name: MRSF Review Lint
355
- on: [pull_request]
356
- jobs:
357
- mrsf:
358
- runs-on: ubuntu-latest
359
- steps:
360
- - uses: actions/checkout@v4
361
- - uses: actions/setup-node@v4
362
- with:
363
- node-version: 20
364
- - run: npx @mrsf/cli validate --strict
365
- - run: npx @mrsf/cli reanchor --staged --dry-run
366
- ```
367
-
368
- ### Git Pre-commit Hook
369
-
370
- ```bash
371
- #!/usr/bin/env bash
372
- # .git/hooks/pre-commit
373
- npx @mrsf/cli reanchor --staged
374
- npx @mrsf/cli validate --strict
375
- ```
376
-
377
- ### Git Post-merge Hook
378
-
379
- ```bash
380
- #!/usr/bin/env bash
381
- # .git/hooks/post-merge
382
- npx @mrsf/cli reanchor
383
- ```
384
-
385
- ## Library Usage
386
-
387
- The CLI is built library-first. All functionality is available as importable functions:
388
-
389
- ```typescript
390
- import {
391
- parseSidecar,
392
- parseSidecarLenient,
393
- writeSidecar,
394
- validate,
395
- reanchorDocument,
396
- addComment,
397
- resolveComment,
398
- removeComment,
399
- filterComments,
400
- summarize,
401
- discoverSidecar,
402
- discoverAllSidecars,
403
- } from "@mrsf/cli";
404
-
405
- // Validate a sidecar programmatically
406
- const doc = await parseSidecar("docs/api.md.review.yaml");
407
- const result = await validate(doc);
408
- console.log(result.valid, result.errors, result.warnings);
409
-
410
- // Add a comment
411
- const updated = addComment(doc, lines, {
412
- author: "ci-bot",
413
- text: "Auto-generated review comment",
414
- line: 10,
415
- });
416
- await writeSidecar("docs/api.md.review.yaml", updated);
417
- ```
418
-
419
- ### Browser-safe reanchoring
420
-
421
- 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.
422
-
423
- `@mrsf/cli/browser` exports the pure text-based matching and reanchoring primitives only:
424
-
425
- ```typescript
426
- import {
427
- applyReanchorResults,
428
- reanchorDocumentText,
429
- } from "@mrsf/cli/browser";
430
-
431
- const results = reanchorDocumentText(sidecar, markdownText);
432
- const changed = applyReanchorResults(sidecar, results);
433
-
434
- console.log(changed, results.map((result) => result.status));
435
- ```
436
-
437
- 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.
438
-
439
- ## Configuration
440
-
441
- Place a `.mrsf.yaml` at your repository root to configure sidecar discovery:
442
-
443
- ```yaml
444
- # Store all sidecars in a central directory instead of co-located
445
- sidecar_root: .reviews
446
- ```
447
-
448
- With this config, the sidecar for `docs/architecture.md` is resolved as `.reviews/docs/architecture.md.review.yaml`.
449
-
450
- See [§3.2 Alternate Sidecar Location](../MRSF-v1.0.md) in the spec for details.
451
-
452
- ## Extension Fields (`x_*`)
453
-
454
- MRSF reserves the `x_` prefix for tool-specific, non-standard extension properties (see [§10 Extension Fields](../MRSF-v1.0.md)). 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`.
455
-
456
- ### Built-in `x_` fields
457
-
458
- The CLI and Sidemark VS Code extension use the following extension fields:
459
-
460
- | Field | Scope | Type | Set by | Description |
461
- |---|---|---|---|---|
462
- | `x_reanchor_status` | comment | `"anchored"` \| `"shifted"` \| `"fuzzy"` \| `"ambiguous"` \| `"orphaned"` | `mrsf reanchor` | Result of the most recent reanchor pass. `ambiguous` means evidence conflicts or multiple candidates remain; `orphaned` means no defensible anchor was found. |
463
- | `x_reanchor_score` | comment | `number` (0–1) | `mrsf reanchor` | Confidence score from the reanchor algorithm. `1.0` = exact match, lower values indicate fuzzy or shifted matches. |
464
-
465
- ### Adding your own fields
466
-
467
- 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.).
468
-
469
- ```yaml
470
- comments:
471
- - id: abc123
472
- author: ci-bot
473
- timestamp: "2025-01-15T10:00:00Z"
474
- text: Possible security concern
475
- resolved: false
476
- line: 42
477
- # Custom extension fields
478
- x_ai_confidence: 0.92
479
- x_tool_name: security-scanner
480
- x_category: injection
481
- ```
482
-
483
- **Rules** (from the spec):
484
-
485
- - Keys MUST start with `x_` (underscore, not hyphen).
486
- - Implementations MUST NOT assign semantic meaning to `x_` fields defined by other tools.
487
- - Future MRSF spec versions will never introduce normative fields with the `x_` prefix.
488
- - Unknown `x_` fields are preserved by the CLI's round-trip writer and never stripped or modified.
489
-
490
- ### Programmatic access
491
-
492
- ```typescript
493
- import { parseSidecar } from "@mrsf/cli";
494
-
495
- const doc = await parseSidecar("file.md.review.yaml");
496
- for (const comment of doc.comments) {
497
- // Access built-in x_ fields
498
- if (comment.x_reanchor_status === "orphaned") {
499
- console.log(`Comment ${comment.id} is orphaned`);
500
- }
501
- // Access custom x_ fields
502
- const confidence = comment.x_ai_confidence as number | undefined;
503
- }
504
- ```
505
-
506
- ## Requirements
507
-
508
- - Node.js 18 or later
509
- - Git (optional — enables diff-based re-anchoring, commit stamping, rename detection)
510
-
511
- ## License
512
-
513
- MIT — see [LICENSE](../LICENSE).
1
+ # MRSF CLI
2
+
3
+ [![License: MIT](https://img.shields.io/badge/license-MIT-green.svg)](LICENSE)
4
+ [![MRSF v1.0 Draft](https://img.shields.io/badge/MRSF-v1.0%20Draft-blue)](MRSF-v1.0.md)
5
+ [![@mrsf/cli on npm](https://img.shields.io/npm/v/@mrsf/cli?label=%40mrsf%2Fcli)](https://www.npmjs.com/package/@mrsf/cli)
6
+ [![npm downloads (cli)](https://img.shields.io/npm/dm/@mrsf/cli?label=cli%20downloads)](https://www.npmjs.com/package/@mrsf/cli)
7
+
8
+ Command-line tool and Node.js library for **Sidemark** — the **Markdown Review Sidecar Format** ([MRSF v1.0](../MRSF-v1.0.md)).
9
+
10
+ Validate sidecars, add and resolve review comments, re-anchor comments after document edits, and integrate MRSF into CI/CD pipelines — all from the terminal.
11
+
12
+ ```
13
+ ╔╦╗╦═╗╔═╗╔═╗
14
+ ║║║╠╦╝╚═╗╠╣
15
+ ╩ ╩╩╚═╚═╝╚
16
+ Markdown Review Sidecar Format
17
+ ```
18
+
19
+ ## Quick Start
20
+
21
+ ```bash
22
+ # Install globally
23
+ npm install -g @mrsf/cli
24
+
25
+ # …or run without installing
26
+ npx @mrsf/cli --help
27
+ ```
28
+
29
+ ### From source
30
+
31
+ ```bash
32
+ git clone https://github.com/wictorwilen/MRSF.git
33
+ cd MRSF/cli
34
+ npm install
35
+ npm run build
36
+ node dist/bin.js --help
37
+ ```
38
+
39
+ ## Browser / Embedder API (`@mrsf/cli/browser`)
40
+
41
+ Use `@mrsf/cli/browser` in Vite, Electron renderers, and editor extensions that
42
+ need side-effect-free MRSF helpers without Node built-ins. This entrypoint does
43
+ not import `fs`, git helpers, or other Node-only APIs, and is designed to be
44
+ tree-shakeable.
45
+
46
+ Key exports include:
47
+
48
+ - Types: `MrsfDocument`, `Comment`, `AnchorPosition`
49
+ - Anchoring: `resolveAnchor`, `reanchorComment`, `reanchorDocumentText`,
50
+ `toReanchorLines`, `applyReanchorResults`
51
+ - Validation: `validateDocument`, `mrsfSchema`
52
+ - Identity: `formatAuthor`, `parseAuthor`, `newCommentId`
53
+ - Pure fuzzy helpers: `exactMatch`, `normalizedMatch`, `fuzzySearch`,
54
+ `combinedScore`
55
+
56
+ Example editor decoration flow:
57
+
58
+ ```ts
59
+ import { resolveAnchor, type Comment } from "@mrsf/cli/browser";
60
+
61
+ function decorationRange(comment: Comment, text: string) {
62
+ const anchor = resolveAnchor(comment, text);
63
+ if (anchor.from == null || anchor.to == null) return undefined;
64
+ return { from: anchor.from, to: anchor.to };
65
+ }
66
+ ```
67
+
68
+ ## Commands
69
+
70
+ ### `mrsf validate [files...]`
71
+
72
+ Validate sidecar files against the JSON Schema and MRSF spec rules.
73
+
74
+ ```bash
75
+ # Validate a single sidecar
76
+ mrsf validate docs/architecture.md.review.yaml
77
+
78
+ # Validate all sidecars discovered in the workspace
79
+ mrsf validate
80
+
81
+ # Treat warnings as errors (useful for CI)
82
+ mrsf validate --strict
83
+ ```
84
+
85
+ | Option | Description |
86
+ |---|---|
87
+ | `-s, --strict` | Treat warnings as errors (exit non-zero) |
88
+
89
+ ---
90
+
91
+ ### `mrsf add <document>`
92
+
93
+ 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. When `--author` is omitted, MRSF uses `user.name` from the repository-local `.git/config`; global Git identity is not used.
94
+
95
+ ```bash
96
+ # Add a line comment
97
+ mrsf add docs/api.md \
98
+ --author "alice" \
99
+ --text "Clarify the rate limit behavior" \
100
+ --line 42
101
+
102
+ # Add a column-span comment with type and severity
103
+ mrsf add docs/api.md \
104
+ --author "bob" \
105
+ --text "Typo: should be 'response'" \
106
+ --line 18 --start-column 10 --end-column 18 \
107
+ --type suggestion --severity low
108
+
109
+ # Reply to an existing comment
110
+ mrsf add docs/api.md \
111
+ --author "alice" \
112
+ --text "Good catch, fixed in abc123" \
113
+ --reply-to 3eeccbd3
114
+
115
+ # Attach tool-specific x_* metadata
116
+ mrsf add docs/api.md \
117
+ --author "bot" \
118
+ --text "Flagging this for follow-up" \
119
+ --line 27 \
120
+ --ext x_source=triage-bot \
121
+ --ext x_score=0.91 \
122
+ --ext 'x_labels=["needs-review","docs"]'
123
+ ```
124
+
125
+ | Option | Description |
126
+ |---|---|
127
+ | `-a, --author <name>` | Comment author; overrides repository-local Git `user.name` |
128
+ | `-t, --text <text>` | Comment body (required) |
129
+ | `-l, --line <n>` | Line number (1-based) |
130
+ | `--end-line <n>` | End line (inclusive) |
131
+ | `--start-column <n>` | Start column (0-based) |
132
+ | `--end-column <n>` | End column (0-based) |
133
+ | `--type <type>` | Category: `suggestion`, `issue`, `question`, `accuracy`, `style`, `clarity` |
134
+ | `--severity <level>` | Importance: `low`, `medium`, `high` |
135
+ | `--reply-to <id>` | Reply to an existing comment by ID |
136
+ | `--selected-text <text>` | Override auto-detected selected text |
137
+ | `--ext <key=value>` | Add an `x_*` extension field; repeat for multiple fields |
138
+
139
+ `--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.
140
+
141
+ ---
142
+
143
+ ### `mrsf resolve <sidecar> <id>`
144
+
145
+ Resolve (or unresolve) a comment by ID.
146
+
147
+ ```bash
148
+ # Resolve a comment
149
+ mrsf resolve docs/api.md.review.yaml 1d3c72b0
150
+
151
+ # Unresolve it
152
+ mrsf resolve --undo docs/api.md.review.yaml 1d3c72b0
153
+
154
+ # Resolve and cascade to all direct replies
155
+ mrsf resolve --cascade docs/api.md.review.yaml 1d3c72b0
156
+ ```
157
+
158
+ | Option | Description |
159
+ |---|---|
160
+ | `--cascade` | Also resolve direct replies |
161
+ | `-u, --undo` | Unresolve instead of resolving |
162
+
163
+ ---
164
+
165
+ ### `mrsf list [files...]`
166
+
167
+ List and filter comments across one or more sidecar files.
168
+
169
+ ```bash
170
+ # List all open comments
171
+ mrsf list --open docs/api.md.review.yaml
172
+
173
+ # Filter by author and severity
174
+ mrsf list --author alice --severity high
175
+
176
+ # Summary view (counts, types, severities)
177
+ mrsf list --summary
178
+
179
+ # Machine-readable JSON output
180
+ mrsf list --json --open
181
+ ```
182
+
183
+ | Option | Description |
184
+ |---|---|
185
+ | `--open` | Show only open (unresolved) comments |
186
+ | `--resolved` | Show only resolved comments |
187
+ | `--orphaned` | Show only orphaned comments |
188
+ | `--author <name>` | Filter by author |
189
+ | `--type <type>` | Filter by comment type |
190
+ | `--severity <level>` | Filter by severity |
191
+ | `--summary` | Show aggregate summary instead of individual comments |
192
+ | `--json` | Output as JSON |
193
+
194
+ ---
195
+
196
+ ### `mrsf reanchor [files...]`
197
+
198
+ Re-anchor comments after the source document has been edited. The repository's
199
+ **Anchor Mesh** implementation combines verified Git shifts, source-revision
200
+ projection, Markdown structure, bounded text matching, confidence calibration,
201
+ and nearby comments as landmarks. It prefers `ambiguous` or `orphaned` results
202
+ over an unsupported relocation.
203
+
204
+ See the
205
+ [non-normative implementation guide](https://sidemark.org/guide/reanchoring)
206
+ for the complete pipeline, execution modes, tuning values, and evaluation
207
+ methodology.
208
+
209
+ ```bash
210
+ # Dry run — see what would change without writing
211
+ mrsf reanchor --dry-run docs/api.md.review.yaml
212
+
213
+ # Re-anchor with a higher fuzzy threshold (stricter matching)
214
+ mrsf reanchor --threshold 0.8
215
+
216
+ # Only process sidecars for staged files (pre-commit hook)
217
+ mrsf reanchor --staged
218
+
219
+ # Disable git (pure text-based matching only)
220
+ mrsf reanchor --no-git
221
+
222
+ # Override the from-commit for all comments
223
+ mrsf reanchor --from abc1234
224
+
225
+ # Also update selected_text to match the current document text (opt-in)
226
+ mrsf reanchor --update-text
227
+ ```
228
+
229
+ | Option | Description | Default |
230
+ |---|---|---|
231
+ | `-n, --dry-run` | Report without modifying files | `false` |
232
+ | `-t, --threshold <n>` | Fuzzy match threshold (0.0–1.0) | `0.6` |
233
+ | `--staged` | Only process sidecars for git-staged documents | `false` |
234
+ | `--no-git` | Disable git integration entirely | git enabled |
235
+ | `--from <commit>` | Override from-commit for diff calculation | per-comment `commit` field |
236
+ | `--update-text` | Also replace `selected_text` with current document text | `false` |
237
+
238
+ > **Text preservation (§6.2):** By default, re-anchoring preserves the
239
+ > original `selected_text` and records the current document text in
240
+ > `anchored_text`. Use `--update-text` to opt in to overwriting
241
+ > `selected_text` directly.
242
+
243
+ ---
244
+
245
+ ### `mrsf status [files...]`
246
+
247
+ Assess anchor health for each comment.
248
+
249
+ ```bash
250
+ mrsf status docs/api.md.review.yaml
251
+ mrsf status --json
252
+ ```
253
+
254
+ Health states:
255
+
256
+ | State | Meaning |
257
+ |---|---|
258
+ | `fresh` | `selected_text` matches at the recorded position |
259
+ | `stale` | `selected_text` matches but at a different position, or commit differs |
260
+ | `orphaned` | `selected_text` not found in the document |
261
+ | `unknown` | No `selected_text` to verify |
262
+
263
+ | Option | Description |
264
+ |---|---|
265
+ | `--json` | Output as JSON |
266
+
267
+ ---
268
+
269
+ ### `mrsf init <document>`
270
+
271
+ Create an empty sidecar file for a Markdown document.
272
+
273
+ ```bash
274
+ mrsf init docs/new-feature.md
275
+ mrsf init --force docs/existing.md # overwrite
276
+ ```
277
+
278
+ | Option | Description |
279
+ |---|---|
280
+ | `-f, --force` | Overwrite an existing sidecar |
281
+
282
+ ---
283
+
284
+ ### `mrsf rename <old-document> <new-document>`
285
+
286
+ Update a sidecar after its source document has been renamed or moved.
287
+
288
+ ```bash
289
+ mrsf rename docs/old-name.md docs/new-name.md
290
+ ```
291
+
292
+ Updates the `document` field inside the sidecar, writes it to the new path, and removes the old sidecar file.
293
+
294
+ ### `mrsf watch [files...]`
295
+
296
+ Watch files for changes and continuously validate (and optionally reanchor).
297
+
298
+ ```bash
299
+ # Validate all sidecars on every change
300
+ mrsf watch
301
+
302
+ # Watch specific files
303
+ mrsf watch docs/api.md docs/guide.md
304
+
305
+ # Auto-reanchor when Markdown files change
306
+ mrsf watch --reanchor
307
+
308
+ # Preview reanchor changes without writing
309
+ mrsf watch --reanchor --dry-run
310
+
311
+ # Custom threshold and strict validation
312
+ mrsf watch --reanchor --threshold 0.8 --strict
313
+ ```
314
+
315
+ **Trigger rules** (avoids feedback loops):
316
+
317
+ | File type | Action |
318
+ |---|---|
319
+ | `.md` (Markdown) | Reanchor (if `--reanchor` is set), then validate sidecar |
320
+ | `.review.yaml` / `.review.json` | Validate only |
321
+
322
+ | Option | Description |
323
+ |---|---|
324
+ | `--reanchor` | Auto-reanchor when Markdown files change |
325
+ | `-n, --dry-run` | Preview reanchor changes without writing |
326
+ | `-t, --threshold <n>` | Fuzzy match threshold 0.0–1.0 (default `0.6`) |
327
+ | `-s, --strict` | Treat validation warnings as errors |
328
+ | `--no-git` | Disable git integration |
329
+ | `--from <commit>` | Override from-commit for reanchor |
330
+ | `--update-text` | Update `selected_text` on reanchor |
331
+ | `-f, --force` | Force reanchor — update commit, clear audit fields |
332
+ | `--debounce <ms>` | Debounce interval in ms (default `300`) |
333
+
334
+ Press `Ctrl+C` to stop — a summary of events, reanchors, and errors is printed.
335
+
336
+ ## Global Options
337
+
338
+ These apply to all commands:
339
+
340
+ | Option | Description |
341
+ |---|---|
342
+ | `--cwd <dir>` | Override the working directory |
343
+ | `--config <path>` | Path to `.mrsf.yaml` configuration |
344
+ | `--no-color` | Disable coloured output |
345
+ | `-q, --quiet` | Suppress informational output |
346
+ | `-V, --version` | Print version |
347
+ | `-h, --help` | Show help |
348
+
349
+ ## CI/CD Integration
350
+
351
+ ### GitHub Actions
352
+
353
+ ```yaml
354
+ name: MRSF Review Lint
355
+ on: [pull_request]
356
+ jobs:
357
+ mrsf:
358
+ runs-on: ubuntu-latest
359
+ steps:
360
+ - uses: actions/checkout@v4
361
+ - uses: actions/setup-node@v4
362
+ with:
363
+ node-version: 20
364
+ - run: npx @mrsf/cli validate --strict
365
+ - run: npx @mrsf/cli reanchor --staged --dry-run
366
+ ```
367
+
368
+ ### Git Pre-commit Hook
369
+
370
+ ```bash
371
+ #!/usr/bin/env bash
372
+ # .git/hooks/pre-commit
373
+ npx @mrsf/cli reanchor --staged
374
+ npx @mrsf/cli validate --strict
375
+ ```
376
+
377
+ ### Git Post-merge Hook
378
+
379
+ ```bash
380
+ #!/usr/bin/env bash
381
+ # .git/hooks/post-merge
382
+ npx @mrsf/cli reanchor
383
+ ```
384
+
385
+ ## Library Usage
386
+
387
+ The CLI is built library-first. All functionality is available as importable functions:
388
+
389
+ ```typescript
390
+ import {
391
+ parseSidecar,
392
+ parseSidecarLenient,
393
+ writeSidecar,
394
+ validate,
395
+ reanchorDocument,
396
+ addComment,
397
+ resolveComment,
398
+ removeComment,
399
+ filterComments,
400
+ summarize,
401
+ discoverSidecar,
402
+ discoverAllSidecars,
403
+ } from "@mrsf/cli";
404
+
405
+ // Validate a sidecar programmatically
406
+ const doc = await parseSidecar("docs/api.md.review.yaml");
407
+ const result = await validate(doc);
408
+ console.log(result.valid, result.errors, result.warnings);
409
+
410
+ // Add a comment
411
+ const updated = addComment(doc, lines, {
412
+ author: "ci-bot",
413
+ text: "Auto-generated review comment",
414
+ line: 10,
415
+ });
416
+ await writeSidecar("docs/api.md.review.yaml", updated);
417
+ ```
418
+
419
+ ### Browser-safe reanchoring
420
+
421
+ 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.
422
+
423
+ `@mrsf/cli/browser` exports the pure text-based matching and reanchoring primitives only:
424
+
425
+ ```typescript
426
+ import {
427
+ applyReanchorResults,
428
+ reanchorDocumentText,
429
+ } from "@mrsf/cli/browser";
430
+
431
+ const results = reanchorDocumentText(sidecar, markdownText);
432
+ const changed = applyReanchorResults(sidecar, results);
433
+
434
+ console.log(changed, results.map((result) => result.status));
435
+ ```
436
+
437
+ 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.
438
+
439
+ ## Configuration
440
+
441
+ Place a `.mrsf.yaml` at your repository root to configure sidecar discovery:
442
+
443
+ ```yaml
444
+ # Store all sidecars in a central directory instead of co-located
445
+ sidecar_root: .reviews
446
+ ```
447
+
448
+ With this config, the sidecar for `docs/architecture.md` is resolved as `.reviews/docs/architecture.md.review.yaml`.
449
+
450
+ See [§3.2 Alternate Sidecar Location](../MRSF-v1.0.md) in the spec for details.
451
+
452
+ ## Extension Fields (`x_*`)
453
+
454
+ MRSF reserves the `x_` prefix for tool-specific, non-standard extension properties (see [§10 Extension Fields](../MRSF-v1.0.md)). 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`.
455
+
456
+ ### Built-in `x_` fields
457
+
458
+ The CLI and Sidemark VS Code extension use the following extension fields:
459
+
460
+ | Field | Scope | Type | Set by | Description |
461
+ |---|---|---|---|---|
462
+ | `x_reanchor_status` | comment | `"anchored"` \| `"shifted"` \| `"fuzzy"` \| `"ambiguous"` \| `"orphaned"` | `mrsf reanchor` | Result of the most recent reanchor pass. `ambiguous` means evidence conflicts or multiple candidates remain; `orphaned` means no defensible anchor was found. |
463
+ | `x_reanchor_score` | comment | `number` (0–1) | `mrsf reanchor` | Confidence score from the reanchor algorithm. `1.0` = exact match, lower values indicate fuzzy or shifted matches. |
464
+
465
+ ### Adding your own fields
466
+
467
+ 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.).
468
+
469
+ ```yaml
470
+ comments:
471
+ - id: abc123
472
+ author: ci-bot
473
+ timestamp: "2025-01-15T10:00:00Z"
474
+ text: Possible security concern
475
+ resolved: false
476
+ line: 42
477
+ # Custom extension fields
478
+ x_ai_confidence: 0.92
479
+ x_tool_name: security-scanner
480
+ x_category: injection
481
+ ```
482
+
483
+ **Rules** (from the spec):
484
+
485
+ - Keys MUST start with `x_` (underscore, not hyphen).
486
+ - Implementations MUST NOT assign semantic meaning to `x_` fields defined by other tools.
487
+ - Future MRSF spec versions will never introduce normative fields with the `x_` prefix.
488
+ - Unknown `x_` fields are preserved by the CLI's round-trip writer and never stripped or modified.
489
+
490
+ ### Programmatic access
491
+
492
+ ```typescript
493
+ import { parseSidecar } from "@mrsf/cli";
494
+
495
+ const doc = await parseSidecar("file.md.review.yaml");
496
+ for (const comment of doc.comments) {
497
+ // Access built-in x_ fields
498
+ if (comment.x_reanchor_status === "orphaned") {
499
+ console.log(`Comment ${comment.id} is orphaned`);
500
+ }
501
+ // Access custom x_ fields
502
+ const confidence = comment.x_ai_confidence as number | undefined;
503
+ }
504
+ ```
505
+
506
+ ## Requirements
507
+
508
+ - Node.js 18 or later
509
+ - Git (optional — enables diff-based re-anchoring, commit stamping, rename detection)
510
+
511
+ ## License
512
+
513
+ MIT — see [LICENSE](../LICENSE).