coderifts 4.6.0 → 4.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/CHANGELOG.md CHANGED
@@ -1,5 +1,71 @@
1
1
  # Changelog
2
2
 
3
+ ## 4.8.0
4
+
5
+ ### Fixed
6
+ - **VS Code / Copilot PreToolUse camelCase (DRIFT 1).** VS Code Agent hooks
7
+ pass `toolName` / `toolInput` / `filePath` (and `targetFile`). The hook
8
+ already tolerated `toolName`/`toolInput` but missed several property
9
+ aliases, so flattened camelCase payloads and `targetFile` were refused as
10
+ unparseable (fail-closed, safe, unusable). Every Claude-shaped read site
11
+ now accepts snake_case and camelCase: `tool_name`/`toolName`,
12
+ `tool_input`/`toolInput`, `file_path`/`filePath`/`path`/`target_file`/`targetFile`,
13
+ `content`/`contents`. Snake_case behaviour is unchanged. Garbage is still
14
+ fail-closed.
15
+
16
+ ### Changed
17
+ - **Matcher-inert hosts (DRIFT 2, documented, not a host fix).** VS Code
18
+ parses hook matchers but does not apply them — the hook fires on every
19
+ tool call. Non-Write/Edit/MultiEdit tools now exit **0** silently with no
20
+ network and **without requiring an API key** (parse → skip → key, so a
21
+ missing key cannot brick Read/Bash). Non-contract Write paths skip before
22
+ the key gate too. Garbage is still fail-closed (`stdin_unparseable`).
23
+ Spec-path Write/Edit/MultiEdit without a key is still fail-closed.
24
+
25
+ ### Added
26
+ - **`copilot` MCP host (DRIFT 3).** `MCP_HOSTS` grows a third entry:
27
+ `.vscode/mcp.json`, root key **`servers`** (not `mcpServers`),
28
+ `type: "http"` + `url`. The writer special-cases the root key; Claude and
29
+ Cursor stay on `mcpServers`. `coderifts init --agents --hosts` accepts
30
+ `copilot` and includes it in `all`. Merge preserves other `servers` (and
31
+ any leftover keys). `--check` uses the same predicate.
32
+ - README Copilot section: `hook install --claude` arms VS Code agent mode
33
+ (measured), matcher-inert behaviour, honest boundary (hooks gate the
34
+ agent, not the human; inline completions are not tool calls).
35
+
36
+ ## 4.7.0
37
+
38
+ ### Added
39
+ - **`coderifts enforce --check`** — provider-native CD enforcement evidence.
40
+ Queries GitHub (and local workflow files) and reports, per layer,
41
+ `VERIFIED` / `NOT_VERIFIED` / `UNVERIFIABLE(reason)`:
42
+ 1. required check `CodeRifts / contract-gate` on default-branch protection
43
+ 2. `enforce_admins` (admin bypass)
44
+ 3. `required_status_checks.strict`
45
+ 4. GitHub Environment protection rules (`--env`)
46
+ 5. workflow references `coderifts/contract-gate@v0` (local)
47
+ 6. workflow runs `coderifts deploy-gate` (local)
48
+
49
+ `inescapable_deploy` is **true only when every layer is VERIFIED**. Otherwise
50
+ the field stays false and the JSON carries the queried facts. `--json`
51
+ includes each layer's status, the API endpoint consulted, and `queried_at`.
52
+
53
+ Auth: `GITHUB_TOKEN` / `GH_TOKEN` (user). App installation token
54
+ (`GITHUB_APP_TOKEN`) is accepted but **cannot** query branch protection —
55
+ the GitHub App permission set is contents:read, pull_requests, checks
56
+ (AUDIT.md §2.4); `GET .../protection` needs `administration:read`. No
57
+ token → `UNVERIFIABLE` with the exact instruction. Token values are never
58
+ printed. GitLab/Bitbucket: no adapter (UNVERIFIABLE, never stubbed).
59
+
60
+ ### Changed
61
+ - **`observeCDEnforcement`:** `CODERIFTS_DEPLOY_NO_BYPASS` is recorded as
62
+ `host_claim_no_bypass` and no longer sets `bypass_possible: false`.
63
+ `--enforce` still marks the step ENFORCING (exit is already fail-closed)
64
+ but is a **host claim**. `bypass_possible` is false only when
65
+ `providerEvidence.inescapable_deploy === true` from `enforce --check`.
66
+ Guard `deployGate` still consumes `{ enforcement, bypass_possible }` — the
67
+ CLI must not pass a flag-derived “no bypass”.
68
+
3
69
  ## 4.6.0
4
70
 
5
71
  **Version pick:** the parallel Cursor-hook round claimed **4.5.0**
package/README.md CHANGED
@@ -72,6 +72,7 @@ coderifts init --agents # all hosts, all four pieces
72
72
  coderifts init --agents --dry-run # plan only — writes nothing
73
73
  coderifts init --agents --hosts=claude # Claude Code only
74
74
  coderifts init --agents --hosts=cursor # Cursor only
75
+ coderifts init --agents --hosts=copilot # GitHub Copilot / VS Code only
75
76
  coderifts init --agents --no-hook # skip host hook install
76
77
  coderifts init --agents --no-workflow # skip .github/workflows/coderifts.yml
77
78
  coderifts init --agents --check # which of the four pieces are present
@@ -79,9 +80,9 @@ coderifts init --agents --check # which of the four pieces are present
79
80
 
80
81
  | Piece | What it writes | Host-specific shape |
81
82
  |-------|----------------|---------------------|
82
- | **MCP config** | `.mcp.json` (Claude Code), `.cursor/mcp.json` (Cursor) | Claude: `mcpServers.coderifts` with `type: "http"` + `url`. Cursor: `url` only (no `type`). Never `httpUrl` (that is Gemini, extension-level — this command does not write it). |
83
- | **Agent rules** | generated host files via `agent-setup`'s writer | `AGENTS.md` is always written. Claude also gets `CLAUDE.md`; Cursor also gets `.cursor/rules/coderifts.mdc`. `--hosts=all` adds Copilot, LangGraph, and OpenAI instructions. (`coderifts init agent` is a different command — it writes a `.coderifts.yml` template.) |
84
- | **Host hook** | `.claude/settings.json`, `.cursor/hooks.json` | Reuses `hook install --claude` / `--cursor` (JSON-merge, backup, idempotent) |
83
+ | **MCP config** | `.mcp.json` (Claude Code), `.cursor/mcp.json` (Cursor), `.vscode/mcp.json` (Copilot / VS Code) | Three shapes, never normalised into one: Claude `mcpServers` + `type: "http"` + `url`; Cursor `mcpServers` + `url` only (no `type`); Copilot **`servers`** (not `mcpServers`) + `type: "http"` + `url`. Never `httpUrl` (that is Gemini, extension-level — this command does not write it). |
84
+ | **Agent rules** | generated host files via `agent-setup`'s writer | `AGENTS.md` is always written. Claude also gets `CLAUDE.md`; Cursor also gets `.cursor/rules/coderifts.mdc`; Copilot also gets `.github/copilot-instructions.md`. `--hosts=all` adds LangGraph and OpenAI instructions. (`coderifts init agent` is a different command — it writes a `.coderifts.yml` template.) |
85
+ | **Host hook** | `.claude/settings.json`, `.cursor/hooks.json` | Reuses `hook install --claude` / `--cursor` (JSON-merge, backup, idempotent). Copilot shares Claude's `.claude/settings.json` — VS Code reads it by default. |
85
86
  | **CI workflow** | `.github/workflows/coderifts.yml` | `coderifts/contract-gate@v0`. `require-verified-monitoring` is commented (honest default: **false**) |
86
87
 
87
88
  **Next manual step:** add `CODERIFTS_API_KEY` to the repository secrets. The
@@ -168,8 +169,11 @@ gate reason + `renderDecisionWhy` when a body exists) and
168
169
  unsigned file still prints the unsigned-receipt truth loudly.
169
170
 
170
171
  **`--enforce` / `CODERIFTS_DEPLOY_ENFORCE=true`:** accepted silently (no-op
171
- for exit — default is already fail-closed). Still attests ENFORCING for the
172
- `inescapable_deploy` claim.
172
+ for exit — default is already fail-closed). This is a **host claim** that the
173
+ step is ENFORCING. It does **not** set `inescapable_deploy`. That field is
174
+ true only from queried pipeline protection (`coderifts enforce --check`).
175
+ `CODERIFTS_DEPLOY_NO_BYPASS` is recorded as a host claim and does not clear
176
+ `bypass_possible`.
173
177
 
174
178
  **Infra vs policy:** missing `--env` / `--artifact`, malformed receipt JSON, or
175
179
  an unreachable `--keys` registry prints distinct `INFRA` wording plus
@@ -344,6 +348,37 @@ weakened by `CODERIFTS_ADVISORY`.
344
348
  CODERIFTS_ADVISORY=1 coderifts claude-hook
345
349
  ```
346
350
 
351
+ ### GitHub Copilot / VS Code agent mode
352
+
353
+ Measured (VS Code **1.110**, Agent hooks Preview, 2026-08-24): VS Code **reads
354
+ `.claude/settings.json` by default**. `coderifts hook install --claude`
355
+ therefore arms Copilot agent mode — there is no separate `--copilot` hook
356
+ installer. VS Code Agent hooks **are deny-capable**: PreToolUse honours
357
+ `permissionDecision` allow|deny|ask and **exit 2**.
358
+
359
+ **Property names (CLI 4.8.0):** VS Code passes camelCase (`toolName` /
360
+ `toolInput` / `filePath` / `targetFile`). The hook accepts those alongside
361
+ Claude's snake_case (`tool_name` / `tool_input` / `file_path`). Flattened
362
+ payloads (path on the root object) are accepted. Garbage input is still
363
+ fail-closed.
364
+
365
+ **Matcher limitation (host behaviour, not a CLI bug):** VS Code **parses** hook
366
+ matchers (`Write|Edit|MultiEdit`) but **does not apply them**. The hook fires
367
+ on every tool call. Non-governed tools (Read, Bash, …) and non-contract paths
368
+ exit **0** silently with **no network** and **no API key required** — cheap
369
+ and fail-open for anything that is not a Write/Edit/MultiEdit of the spec. It
370
+ is noisy (every tool call invokes the process) and safe (irrelevant tools are
371
+ not blocked). A spec-path write without a key is still fail-closed.
372
+
373
+ **Honest boundary:** hooks gate the **agent**, not the human. Inline
374
+ completions (ghost text) are not tool calls and are not governed. A developer
375
+ typing in the editor is not a PreToolUse event.
376
+
377
+ **MCP:** `coderifts init --agents` (and `--hosts=copilot`) writes
378
+ `.vscode/mcp.json` with root key **`servers`** — a third shape. Claude Code and
379
+ Cursor use `mcpServers`; Copilot cloud Settings paste also uses `mcpServers`.
380
+ Do not copy one file onto another host.
381
+
347
382
  ### `coderifts status [repo]`
348
383
 
349
384
  **Read-only** cross-layer enforcement report (Runtime / Merge / Deploy, plus
@@ -382,13 +417,44 @@ failed, **1** on missing repo/key, API error, or any apply failure.
382
417
  |------|-------------|---------|
383
418
  | `[repo]` / `--repo <owner/repo>` | Repository | none (required) |
384
419
  | `--apply` | Run underlying setup commands | dry-run |
420
+ | `--check` | Query GitHub + local workflows (read-only evidence) | `false` |
421
+ | `--env <name>` | With `--check`: GitHub Environment to query | none |
422
+ | `--branch <name>` | With `--check`: branch (default: repo default) | default branch |
385
423
  | `--json` | Machine-readable JSON result | `false` |
386
424
 
387
425
  ```bash
388
426
  coderifts enforce owner/repo
389
427
  coderifts enforce --repo owner/repo --apply
428
+ coderifts enforce --check --repo owner/repo --env production --json
390
429
  ```
391
430
 
431
+ ### `coderifts enforce --check`
432
+
433
+ Provider-native pipeline-protection evidence. This is **not** a host claim:
434
+ `--enforce` and `CODERIFTS_DEPLOY_NO_BYPASS` do **not** set
435
+ `inescapable_deploy`. The field is true only when every layer below is
436
+ `VERIFIED`.
437
+
438
+ | Layer | VERIFIED when |
439
+ |-------|----------------|
440
+ | `required_check` | Default-branch protection requires `CodeRifts / contract-gate` |
441
+ | `enforce_admins` | `enforce_admins.enabled` is **true** (admins cannot bypass). `false` = NOT_VERIFIED (admin bypass open) |
442
+ | `strict_status_checks` | `required_status_checks.strict` is true |
443
+ | `environment_protection` | `--env` names a GitHub Environment with protection rules or a deployment branch policy |
444
+ | `workflow_contract_gate` | a local `.github/workflows` file references `coderifts/contract-gate@v0` |
445
+ | `workflow_deploy_gate` | a local workflow runs `coderifts deploy-gate` |
446
+
447
+ Statuses: `VERIFIED` / `NOT_VERIFIED` / `UNVERIFIABLE(reason)`. Auth:
448
+ `GITHUB_TOKEN` (never printed). **No token** → API layers `UNVERIFIABLE` with
449
+ the instruction to set `GITHUB_TOKEN` (repo + `administration:read`). The
450
+ CodeRifts GitHub App **cannot** query branch protection (permissions are
451
+ contents / pull requests / checks — not `administration:read`).
452
+
453
+ `--json` is the quotable evidence document (`spec`:
454
+ `provider-enforcement-evidence.v1`): each layer's status, the API endpoint
455
+ consulted, `queried_at`. Exit **0** when a report was produced (evidence, not
456
+ a gate). GitLab/Bitbucket: no adapter — `UNVERIFIABLE`, never stubbed.
457
+
392
458
  ### Agent host files (`coderifts agent-setup`)
393
459
 
394
460
  Writes the six CodeRifts agent-host rule files **plus** the Claude Code
package/bin/coderifts.js CHANGED
@@ -92,7 +92,7 @@ program
92
92
  .command('init [template]')
93
93
  .description('Generate a .coderifts.yml from a policy template, or wire governed agent work (--agents)')
94
94
  .option('--agents', 'Wire this repo for governed agent work (MCP config, rule files, host hook, CI gate)')
95
- .option('--hosts <list>', 'With --agents: claude, cursor, or all (default: all)', 'all')
95
+ .option('--hosts <list>', 'With --agents: claude, cursor, copilot, or all (default: all)', 'all')
96
96
  .option('--no-hook', 'With --agents: skip host hook install')
97
97
  .option('--no-workflow', 'With --agents: skip writing .github/workflows/coderifts.yml')
98
98
  .option('--dry-run', 'With --agents: print the plan without writing')
@@ -184,9 +184,13 @@ program
184
184
  // No new GitHub writes with the CodeRifts API key (user's gh for merge; local fs for hook).
185
185
  program
186
186
  .command('enforce [repo]')
187
- .description('Close enforcement gaps by chaining setup-required-check / hook / deploy guidance (dry-run default; use --apply to mutate)')
187
+ .description('Close enforcement gaps (dry-run default), or --check to query provider-native pipeline protection evidence')
188
188
  .option('--repo <owner/repo>', 'Repository (owner/repo); also accepted as a positional argument')
189
189
  .option('--apply', 'Actually run underlying setup commands (default: dry-run only)')
190
+ .option('--check', 'Query GitHub + local workflows; report VERIFIED / NOT_VERIFIED / UNVERIFIABLE (read-only)')
191
+ .option('--env <name>', 'With --check: GitHub Environment name to query for protection rules')
192
+ .option('--branch <name>', 'With --check: branch to query (default: repository default branch)')
193
+ .option('--provider <name>', 'With --check: provider adapter (github only; others UNVERIFIABLE, never stubbed)', 'github')
190
194
  .option('--json', 'Machine-readable JSON result')
191
195
  .action(async (repoPositional, options) => {
192
196
  const { runEnforce } = require('../src/commands/enforce');
@@ -194,6 +198,7 @@ program
194
198
  ...options,
195
199
  repo: options.repo || repoPositional || null,
196
200
  apply: !!options.apply,
201
+ check: !!options.check,
197
202
  });
198
203
  if (result && typeof result.exitCode === 'number') {
199
204
  process.exitCode = result.exitCode;