coderifts 4.7.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 +33 -0
- package/README.md +35 -3
- package/bin/coderifts.js +1 -1
- package/dist/cli.js +222 -67
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,38 @@
|
|
|
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
|
+
|
|
3
36
|
## 4.7.0
|
|
4
37
|
|
|
5
38
|
### Added
|
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) |
|
|
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
|
|
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
|
|
@@ -347,6 +348,37 @@ weakened by `CODERIFTS_ADVISORY`.
|
|
|
347
348
|
CODERIFTS_ADVISORY=1 coderifts claude-hook
|
|
348
349
|
```
|
|
349
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
|
+
|
|
350
382
|
### `coderifts status [repo]`
|
|
351
383
|
|
|
352
384
|
**Read-only** cross-layer enforcement report (Runtime / Merge / Deploy, plus
|
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')
|
package/dist/cli.js
CHANGED
|
@@ -3028,7 +3028,7 @@ var require_package = __commonJS({
|
|
|
3028
3028
|
"package.json"(exports2, module2) {
|
|
3029
3029
|
module2.exports = {
|
|
3030
3030
|
name: "coderifts",
|
|
3031
|
-
version: "4.
|
|
3031
|
+
version: "4.8.0",
|
|
3032
3032
|
description: "Detect breaking API changes from the command line. Works locally or with the CodeRifts cloud API.",
|
|
3033
3033
|
author: "CodeRifts <hello@coderifts.com>",
|
|
3034
3034
|
license: "MIT",
|
|
@@ -74363,7 +74363,7 @@ var require_claude_hook = __commonJS({
|
|
|
74363
74363
|
"Parse-gap DEFAULT (exit 2): unparseable stdin / missing file_path \u2014 refusing (fail-closed);",
|
|
74364
74364
|
" set CODERIFTS_ADVISORY=1 to soften. JSONL hook_blocked cause stdin_unparseable|missing_file_path.",
|
|
74365
74365
|
"",
|
|
74366
|
-
"Still exit 0 (nothing to govern): non-spec path, identical content.",
|
|
74366
|
+
"Still exit 0 (nothing to govern): non-spec path, identical content, non-governed tool.",
|
|
74367
74367
|
"",
|
|
74368
74368
|
"CONTINUE_WITH_MONITORING: allow only if the host asserts a sink",
|
|
74369
74369
|
" (CODERIFTS_MONITORING_SINK_WIRED=1|true or git config coderifts.monitoringSinkWired).",
|
|
@@ -74504,6 +74504,42 @@ var require_claude_hook = __commonJS({
|
|
|
74504
74504
|
if (fromGit) return fromGit;
|
|
74505
74505
|
return DEFAULT_SPEC_PATH;
|
|
74506
74506
|
}
|
|
74507
|
+
var GOVERNED_TOOLS = /* @__PURE__ */ new Set(["write", "edit", "multiedit", "multi_edit"]);
|
|
74508
|
+
function isGovernedTool(name) {
|
|
74509
|
+
return GOVERNED_TOOLS.has(String(name || "").toLowerCase());
|
|
74510
|
+
}
|
|
74511
|
+
function pickToolField(obj, ...keys) {
|
|
74512
|
+
if (!obj || typeof obj !== "object" || Array.isArray(obj)) return void 0;
|
|
74513
|
+
for (const k of keys) {
|
|
74514
|
+
if (!Object.prototype.hasOwnProperty.call(obj, k)) continue;
|
|
74515
|
+
const v = obj[k];
|
|
74516
|
+
if (v === void 0 || v === null) continue;
|
|
74517
|
+
return v;
|
|
74518
|
+
}
|
|
74519
|
+
return void 0;
|
|
74520
|
+
}
|
|
74521
|
+
function filePathFromInput(toolInput) {
|
|
74522
|
+
const v = pickToolField(toolInput, "file_path", "filePath", "path", "target_file", "targetFile");
|
|
74523
|
+
return typeof v === "string" && v !== "" ? v : void 0;
|
|
74524
|
+
}
|
|
74525
|
+
function contentFromInput(toolInput) {
|
|
74526
|
+
const v = pickToolField(toolInput, "content", "contents");
|
|
74527
|
+
return typeof v === "string" ? v : void 0;
|
|
74528
|
+
}
|
|
74529
|
+
function coerceToolInput(value) {
|
|
74530
|
+
if (value && typeof value === "object" && !Array.isArray(value)) return value;
|
|
74531
|
+
if (typeof value === "string") {
|
|
74532
|
+
const t = value.trim();
|
|
74533
|
+
if (!t) return null;
|
|
74534
|
+
try {
|
|
74535
|
+
const parsed = JSON.parse(t);
|
|
74536
|
+
if (parsed && typeof parsed === "object" && !Array.isArray(parsed)) return parsed;
|
|
74537
|
+
} catch {
|
|
74538
|
+
return null;
|
|
74539
|
+
}
|
|
74540
|
+
}
|
|
74541
|
+
return null;
|
|
74542
|
+
}
|
|
74507
74543
|
function parseStdinJson(raw) {
|
|
74508
74544
|
if (raw == null || String(raw).trim() === "") {
|
|
74509
74545
|
return { ok: false, reason: "empty stdin" };
|
|
@@ -74517,9 +74553,9 @@ var require_claude_hook = __commonJS({
|
|
|
74517
74553
|
if (!obj || typeof obj !== "object" || Array.isArray(obj)) {
|
|
74518
74554
|
return { ok: false, reason: "stdin JSON is not an object" };
|
|
74519
74555
|
}
|
|
74520
|
-
const toolName = obj
|
|
74521
|
-
let toolInput = obj
|
|
74522
|
-
if (toolInput == null && obj
|
|
74556
|
+
const toolName = pickToolField(obj, "tool_name", "toolName", "name");
|
|
74557
|
+
let toolInput = coerceToolInput(pickToolField(obj, "tool_input", "toolInput", "input"));
|
|
74558
|
+
if (toolInput == null && filePathFromInput(obj)) toolInput = obj;
|
|
74523
74559
|
if (!toolName || typeof toolName !== "string") {
|
|
74524
74560
|
return { ok: false, reason: "missing tool_name" };
|
|
74525
74561
|
}
|
|
@@ -74543,16 +74579,17 @@ var require_claude_hook = __commonJS({
|
|
|
74543
74579
|
return false;
|
|
74544
74580
|
}
|
|
74545
74581
|
function deriveAfterContent(toolName, toolInput, diskBefore) {
|
|
74546
|
-
const name = String(toolName || "");
|
|
74547
|
-
if (name === "
|
|
74548
|
-
|
|
74582
|
+
const name = String(toolName || "").toLowerCase();
|
|
74583
|
+
if (name === "write") {
|
|
74584
|
+
const content = contentFromInput(toolInput);
|
|
74585
|
+
if (typeof content !== "string") {
|
|
74549
74586
|
return { ok: false, reason: "Write tool_input.content missing or not a string" };
|
|
74550
74587
|
}
|
|
74551
|
-
return { ok: true, after:
|
|
74588
|
+
return { ok: true, after: content };
|
|
74552
74589
|
}
|
|
74553
|
-
if (name === "
|
|
74554
|
-
const oldS = toolInput
|
|
74555
|
-
const newS = toolInput
|
|
74590
|
+
if (name === "edit") {
|
|
74591
|
+
const oldS = pickToolField(toolInput, "old_string", "oldString");
|
|
74592
|
+
const newS = pickToolField(toolInput, "new_string", "newString");
|
|
74556
74593
|
if (typeof oldS !== "string" || typeof newS !== "string") {
|
|
74557
74594
|
return { ok: false, reason: "Edit tool_input.old_string/new_string missing" };
|
|
74558
74595
|
}
|
|
@@ -74561,16 +74598,16 @@ var require_claude_hook = __commonJS({
|
|
|
74561
74598
|
}
|
|
74562
74599
|
return { ok: true, after: diskBefore.replace(oldS, newS) };
|
|
74563
74600
|
}
|
|
74564
|
-
if (name === "
|
|
74565
|
-
const edits = toolInput
|
|
74601
|
+
if (name === "multiedit" || name === "multi_edit") {
|
|
74602
|
+
const edits = pickToolField(toolInput, "edits", "Edits");
|
|
74566
74603
|
if (!Array.isArray(edits) || edits.length === 0) {
|
|
74567
74604
|
return { ok: false, reason: "MultiEdit tool_input.edits missing or empty" };
|
|
74568
74605
|
}
|
|
74569
74606
|
let cur = diskBefore;
|
|
74570
74607
|
for (let i = 0; i < edits.length; i++) {
|
|
74571
74608
|
const e = edits[i] || {};
|
|
74572
|
-
const oldS = e
|
|
74573
|
-
const newS = e
|
|
74609
|
+
const oldS = pickToolField(e, "old_string", "oldString");
|
|
74610
|
+
const newS = pickToolField(e, "new_string", "newString");
|
|
74574
74611
|
if (typeof oldS !== "string" || typeof newS !== "string") {
|
|
74575
74612
|
return { ok: false, reason: `MultiEdit edits[${i}] missing old_string/new_string` };
|
|
74576
74613
|
}
|
|
@@ -74581,7 +74618,7 @@ var require_claude_hook = __commonJS({
|
|
|
74581
74618
|
}
|
|
74582
74619
|
return { ok: true, after: cur };
|
|
74583
74620
|
}
|
|
74584
|
-
return { ok: false, reason: `unsupported tool_name for content derive: ${
|
|
74621
|
+
return { ok: false, reason: `unsupported tool_name for content derive: ${toolName}` };
|
|
74585
74622
|
}
|
|
74586
74623
|
function isV2DecisionBody(d, dr) {
|
|
74587
74624
|
if (dr && typeof dr === "object") return true;
|
|
@@ -74742,18 +74779,6 @@ var require_claude_hook = __commonJS({
|
|
|
74742
74779
|
}
|
|
74743
74780
|
return r;
|
|
74744
74781
|
};
|
|
74745
|
-
const apiKey = resolveApiKey({ ...deps, cwd, env });
|
|
74746
|
-
if (!apiKey) {
|
|
74747
|
-
logEv({ type: "preflight_unavailable", cause: "missing_api_key" });
|
|
74748
|
-
return emitTerminal(failClosedOrAdvisory({
|
|
74749
|
-
advisory,
|
|
74750
|
-
strict,
|
|
74751
|
-
site: "missing_api_key",
|
|
74752
|
-
why: "no API key",
|
|
74753
|
-
softMsg: "CodeRifts claude-hook: no API key (coderifts login / CODERIFTS_API_KEY / git config coderifts.apiKey) \u2014 allowing",
|
|
74754
|
-
errLog
|
|
74755
|
-
}));
|
|
74756
|
-
}
|
|
74757
74782
|
const PARSE_GAP_STDERR = "unparseable input \u2014 refusing (fail-closed); set CODERIFTS_ADVISORY=1 to soften";
|
|
74758
74783
|
const raw = typeof options.stdin === "string" ? options.stdin : readStdin();
|
|
74759
74784
|
const parsed = parseStdinJson(raw);
|
|
@@ -74767,7 +74792,11 @@ var require_claude_hook = __commonJS({
|
|
|
74767
74792
|
}
|
|
74768
74793
|
const { toolName, toolInput } = parsed;
|
|
74769
74794
|
tool = toolName;
|
|
74770
|
-
|
|
74795
|
+
if (!isGovernedTool(toolName)) {
|
|
74796
|
+
logEv({ type: "detection_skip", signals: ["not_governed_tool"], tool });
|
|
74797
|
+
return emitTerminal({ exitCode: 0, reason: "not_governed_tool" });
|
|
74798
|
+
}
|
|
74799
|
+
const filePath = filePathFromInput(toolInput);
|
|
74771
74800
|
if (!filePath || typeof filePath !== "string") {
|
|
74772
74801
|
if (advisory && !strict) {
|
|
74773
74802
|
errLog("CodeRifts claude-hook: tool_input.file_path missing \u2014 allowing (CODERIFTS_ADVISORY)");
|
|
@@ -74782,6 +74811,18 @@ var require_claude_hook = __commonJS({
|
|
|
74782
74811
|
logEv({ type: "detection_skip", signals: ["not_spec_path"], tool, path: filePath });
|
|
74783
74812
|
return emitTerminal({ exitCode: 0, reason: "not_spec_path" });
|
|
74784
74813
|
}
|
|
74814
|
+
const apiKey = resolveApiKey({ ...deps, cwd, env });
|
|
74815
|
+
if (!apiKey) {
|
|
74816
|
+
logEv({ type: "preflight_unavailable", cause: "missing_api_key" });
|
|
74817
|
+
return emitTerminal(failClosedOrAdvisory({
|
|
74818
|
+
advisory,
|
|
74819
|
+
strict,
|
|
74820
|
+
site: "missing_api_key",
|
|
74821
|
+
why: "no API key",
|
|
74822
|
+
softMsg: "CodeRifts claude-hook: no API key (coderifts login / CODERIFTS_API_KEY / git config coderifts.apiKey) \u2014 allowing",
|
|
74823
|
+
errLog
|
|
74824
|
+
}));
|
|
74825
|
+
}
|
|
74785
74826
|
const absPath = path.isAbsolute(filePath) ? filePath : path.resolve(cwd, filePath);
|
|
74786
74827
|
let diskBefore = "";
|
|
74787
74828
|
if (exists(absPath)) {
|
|
@@ -74941,6 +74982,11 @@ var require_claude_hook = __commonJS({
|
|
|
74941
74982
|
module2.exports = {
|
|
74942
74983
|
runClaudeHook,
|
|
74943
74984
|
parseStdinJson,
|
|
74985
|
+
pickToolField,
|
|
74986
|
+
filePathFromInput,
|
|
74987
|
+
contentFromInput,
|
|
74988
|
+
isGovernedTool,
|
|
74989
|
+
coerceToolInput,
|
|
74944
74990
|
isSpecPath,
|
|
74945
74991
|
deriveAfterContent,
|
|
74946
74992
|
mapDecisionSeverity,
|
|
@@ -142589,6 +142635,13 @@ var require_key_store = __commonJS({
|
|
|
142589
142635
|
function findByPrefix(prefix) {
|
|
142590
142636
|
return keyRecords.find((r) => r.keyPrefix === prefix) || null;
|
|
142591
142637
|
}
|
|
142638
|
+
function findAllByPrefix(prefix) {
|
|
142639
|
+
if (!prefix || typeof prefix !== "string") return [];
|
|
142640
|
+
return keyRecords.filter((r) => r.keyPrefix === prefix);
|
|
142641
|
+
}
|
|
142642
|
+
function isFullKeyToken(token) {
|
|
142643
|
+
return typeof token === "string" && token.length > 12;
|
|
142644
|
+
}
|
|
142592
142645
|
function findRecordByKey(key) {
|
|
142593
142646
|
if (!key || typeof key !== "string") return null;
|
|
142594
142647
|
const prefix = getKeyPrefix(key);
|
|
@@ -142672,14 +142725,38 @@ var require_key_store = __commonJS({
|
|
|
142672
142725
|
function _setGenerateKeyStringForTest(fn) {
|
|
142673
142726
|
_generateKeyStringForTest = typeof fn === "function" ? fn : null;
|
|
142674
142727
|
}
|
|
142675
|
-
function
|
|
142676
|
-
const idx = keyRecords.
|
|
142728
|
+
function deleteRecord(record, ownerEmail) {
|
|
142729
|
+
const idx = keyRecords.indexOf(record);
|
|
142677
142730
|
if (idx === -1) return false;
|
|
142678
142731
|
if (ownerEmail && keyRecords[idx].owner !== ownerEmail) return false;
|
|
142679
142732
|
keyRecords.splice(idx, 1);
|
|
142680
142733
|
saveKeys();
|
|
142681
142734
|
return true;
|
|
142682
142735
|
}
|
|
142736
|
+
function revokeKey({ token, ownerEmail } = {}) {
|
|
142737
|
+
const raw = typeof token === "string" ? token : "";
|
|
142738
|
+
if (!raw) return { ok: false, code: "not_found", count: 0 };
|
|
142739
|
+
if (isFullKeyToken(raw)) {
|
|
142740
|
+
const rec = findRecordByKey(raw);
|
|
142741
|
+
if (!rec) return { ok: false, code: "not_found", count: 0 };
|
|
142742
|
+
if (!deleteRecord(rec, ownerEmail || null)) {
|
|
142743
|
+
return { ok: false, code: "unauthorized", count: 1, prefix: rec.keyPrefix };
|
|
142744
|
+
}
|
|
142745
|
+
return { ok: true, code: "deleted", count: 1, prefix: rec.keyPrefix };
|
|
142746
|
+
}
|
|
142747
|
+
const matches = findAllByPrefix(raw);
|
|
142748
|
+
if (matches.length === 0) return { ok: false, code: "not_found", count: 0 };
|
|
142749
|
+
if (matches.length > 1) {
|
|
142750
|
+
return { ok: false, code: "ambiguous", count: matches.length, prefix: raw };
|
|
142751
|
+
}
|
|
142752
|
+
if (!deleteRecord(matches[0], ownerEmail || null)) {
|
|
142753
|
+
return { ok: false, code: "unauthorized", count: 1, prefix: raw };
|
|
142754
|
+
}
|
|
142755
|
+
return { ok: true, code: "deleted", count: 1, prefix: raw };
|
|
142756
|
+
}
|
|
142757
|
+
function deleteByPrefix(prefix, ownerEmail) {
|
|
142758
|
+
return revokeKey({ token: prefix, ownerEmail }).ok === true;
|
|
142759
|
+
}
|
|
142683
142760
|
module2.exports = {
|
|
142684
142761
|
createKey,
|
|
142685
142762
|
hasKey,
|
|
@@ -142689,7 +142766,9 @@ var require_key_store = __commonJS({
|
|
|
142689
142766
|
listKeysByOwner,
|
|
142690
142767
|
deleteByPrefix,
|
|
142691
142768
|
findByPrefix,
|
|
142769
|
+
findAllByPrefix,
|
|
142692
142770
|
findRecordByKey,
|
|
142771
|
+
revokeKey,
|
|
142693
142772
|
FREE_TIER_REQUESTS_PER_MONTH,
|
|
142694
142773
|
FREE_TIER_REQUESTS_PER_MINUTE,
|
|
142695
142774
|
PREFIX_REGENERATE_ATTEMPTS,
|
|
@@ -198815,9 +198894,10 @@ var require_remediation_taxonomy = __commonJS({
|
|
|
198815
198894
|
break;
|
|
198816
198895
|
}
|
|
198817
198896
|
case "COVERAGE_GAP": {
|
|
198818
|
-
|
|
198897
|
+
const detail = str(pattern && pattern.description) || str(pattern && pattern.error);
|
|
198898
|
+
target = str(pattern && pattern.affected_path) || "analysis";
|
|
198819
198899
|
target_ref = {};
|
|
198820
|
-
instruction = "Do not treat this as a schema edit: restore analyzable artifacts / wait for coverage; re-preflight when analysis is complete.";
|
|
198900
|
+
instruction = detail ? `fix the spec: ${detail}` : "Do not treat this as a schema edit: restore analyzable artifacts / wait for coverage; re-preflight when analysis is complete.";
|
|
198821
198901
|
break;
|
|
198822
198902
|
}
|
|
198823
198903
|
default: {
|
|
@@ -198872,6 +198952,21 @@ var require_remediation_taxonomy = __commonJS({
|
|
|
198872
198952
|
if (patterns.length > 0) {
|
|
198873
198953
|
return patterns.map((pattern) => remediationFromDetection({ pattern, ir: null }));
|
|
198874
198954
|
}
|
|
198955
|
+
const degradedReasons = Array.isArray(verdict.degraded_reasons) ? verdict.degraded_reasons : [];
|
|
198956
|
+
if (degradedReasons.length > 0) {
|
|
198957
|
+
return degradedReasons.map((r) => {
|
|
198958
|
+
const msg = r && (r.message || r.code) ? String(r.message || r.code) : "analysis incomplete";
|
|
198959
|
+
return remediationFromDetection({
|
|
198960
|
+
pattern: {
|
|
198961
|
+
name: "COVERAGE_GAP",
|
|
198962
|
+
degraded: true,
|
|
198963
|
+
description: msg,
|
|
198964
|
+
affected_path: r && r.code || "analysis"
|
|
198965
|
+
},
|
|
198966
|
+
ir: null
|
|
198967
|
+
});
|
|
198968
|
+
});
|
|
198969
|
+
}
|
|
198875
198970
|
if (verdict.coverage_gap === true || verdict.degraded === true) {
|
|
198876
198971
|
return [remediationFromDetection({ pattern: { name: "COVERAGE_GAP", degraded: true }, ir: null })];
|
|
198877
198972
|
}
|
|
@@ -206628,8 +206723,19 @@ var require_remediation_transaction = __commonJS({
|
|
|
206628
206723
|
if (artifactIds.length > 0) scope.artifact_ids = artifactIds;
|
|
206629
206724
|
return scope;
|
|
206630
206725
|
}
|
|
206726
|
+
function isDegradedContext(coreVerdict, context = {}) {
|
|
206727
|
+
if (context.analysis_complete === false) return true;
|
|
206728
|
+
if (Array.isArray(context.degraded_reasons) && context.degraded_reasons.length > 0) return true;
|
|
206729
|
+
if (coreVerdict && coreVerdict.degraded === true) return true;
|
|
206730
|
+
if (Array.isArray(coreVerdict && coreVerdict.degraded_reasons) && coreVerdict.degraded_reasons.length > 0) {
|
|
206731
|
+
return true;
|
|
206732
|
+
}
|
|
206733
|
+
return false;
|
|
206734
|
+
}
|
|
206631
206735
|
function buildRemediationTransaction({ decision, fingerprint, coreVerdict, context = {} }) {
|
|
206632
|
-
|
|
206736
|
+
const degraded = isDegradedContext(coreVerdict, context);
|
|
206737
|
+
const emit = decision === "BLOCK" || decision === "REQUIRE_APPROVAL" || degraded;
|
|
206738
|
+
if (!emit) return null;
|
|
206633
206739
|
if (typeof fingerprint !== "string" || !fingerprint) return null;
|
|
206634
206740
|
const verdictForTaxonomy = {
|
|
206635
206741
|
...coreVerdict && typeof coreVerdict === "object" ? coreVerdict : {}
|
|
@@ -206639,6 +206745,13 @@ var require_remediation_transaction = __commonJS({
|
|
|
206639
206745
|
verdictForTaxonomy.detected_patterns = context.detectedPatterns;
|
|
206640
206746
|
}
|
|
206641
206747
|
}
|
|
206748
|
+
if (degraded) {
|
|
206749
|
+
verdictForTaxonomy.degraded = true;
|
|
206750
|
+
const reasons = Array.isArray(context.degraded_reasons) && context.degraded_reasons.length ? context.degraded_reasons : verdictForTaxonomy.degraded_reasons;
|
|
206751
|
+
if (Array.isArray(reasons) && reasons.length) {
|
|
206752
|
+
verdictForTaxonomy.degraded_reasons = reasons;
|
|
206753
|
+
}
|
|
206754
|
+
}
|
|
206642
206755
|
const required_changes = buildRemediations(verdictForTaxonomy);
|
|
206643
206756
|
const profile = resolveFingerprintProfile(context);
|
|
206644
206757
|
return {
|
|
@@ -206673,6 +206786,7 @@ var require_remediation_transaction = __commonJS({
|
|
|
206673
206786
|
resolveFingerprintProfile,
|
|
206674
206787
|
deriveRecheckScope,
|
|
206675
206788
|
blockRequiredActionCore,
|
|
206789
|
+
isDegradedContext,
|
|
206676
206790
|
PROFILE_CRBUNDLE_V1,
|
|
206677
206791
|
PROFILE_VERDICT_FP_V1,
|
|
206678
206792
|
RESUBMISSION_UNCHANGED,
|
|
@@ -206848,7 +206962,7 @@ var require_decision_result = __commonJS({
|
|
|
206848
206962
|
pattern_sources: null,
|
|
206849
206963
|
// Control core for retrieval; null = not supplied (must never render as type none for BLOCK)
|
|
206850
206964
|
required_action_core: null,
|
|
206851
|
-
// ID850 v1 —
|
|
206965
|
+
// ID850 v1 — remediation transaction (null / omit on ALLOW/WARN with complete analysis)
|
|
206852
206966
|
remediation_transaction: null,
|
|
206853
206967
|
// ID637 v5 slice 4 — change-set completeness commitment (honest; not a proof on generic path).
|
|
206854
206968
|
// SERVER-authored mode; body_hash covered. Never boolean completeness:true.
|
|
@@ -209804,6 +209918,7 @@ var require_mcp_repo_config = __commonJS({
|
|
|
209804
209918
|
id: "claude",
|
|
209805
209919
|
relPath: ".mcp.json",
|
|
209806
209920
|
label: "Claude Code",
|
|
209921
|
+
rootKey: "mcpServers",
|
|
209807
209922
|
// Measured: type+url required. url-without-type is a config error.
|
|
209808
209923
|
entry: Object.freeze({
|
|
209809
209924
|
type: "http",
|
|
@@ -209818,6 +209933,7 @@ var require_mcp_repo_config = __commonJS({
|
|
|
209818
209933
|
id: "cursor",
|
|
209819
209934
|
relPath: ".cursor/mcp.json",
|
|
209820
209935
|
label: "Cursor",
|
|
209936
|
+
rootKey: "mcpServers",
|
|
209821
209937
|
// Measured: url only — Cursor docs do not use type for remote HTTP.
|
|
209822
209938
|
entry: Object.freeze({
|
|
209823
209939
|
url: MCP_URL,
|
|
@@ -209826,8 +209942,24 @@ var require_mcp_repo_config = __commonJS({
|
|
|
209826
209942
|
})
|
|
209827
209943
|
}),
|
|
209828
209944
|
shapeNote: "mcpServers.coderifts url (no type \u2014 Cursor remote form)"
|
|
209945
|
+
}),
|
|
209946
|
+
copilot: Object.freeze({
|
|
209947
|
+
id: "copilot",
|
|
209948
|
+
relPath: ".vscode/mcp.json",
|
|
209949
|
+
label: "GitHub Copilot / VS Code",
|
|
209950
|
+
// Measured: THIRD variant — root key is `servers`, not mcpServers.
|
|
209951
|
+
rootKey: "servers",
|
|
209952
|
+
entry: Object.freeze({
|
|
209953
|
+
type: "http",
|
|
209954
|
+
url: MCP_URL,
|
|
209955
|
+
headers: Object.freeze({
|
|
209956
|
+
Authorization: "Bearer ${env:CODERIFTS_API_KEY}"
|
|
209957
|
+
})
|
|
209958
|
+
}),
|
|
209959
|
+
shapeNote: "servers.coderifts type=http url (root key servers, not mcpServers)"
|
|
209829
209960
|
})
|
|
209830
209961
|
});
|
|
209962
|
+
var MCP_HOST_IDS = Object.freeze(Object.keys(MCP_HOSTS));
|
|
209831
209963
|
function deepEqual(a, b) {
|
|
209832
209964
|
if (a === b) return true;
|
|
209833
209965
|
try {
|
|
@@ -209836,25 +209968,26 @@ var require_mcp_repo_config = __commonJS({
|
|
|
209836
209968
|
return false;
|
|
209837
209969
|
}
|
|
209838
209970
|
}
|
|
209839
|
-
function planMcpMerge(existing, entry) {
|
|
209971
|
+
function planMcpMerge(existing, entry, rootKey = "mcpServers") {
|
|
209972
|
+
const key = rootKey || "mcpServers";
|
|
209840
209973
|
const hadFile = existing != null;
|
|
209841
209974
|
const base = existing && typeof existing === "object" && !Array.isArray(existing) ? existing : {};
|
|
209842
|
-
const
|
|
209843
|
-
const current = Object.prototype.hasOwnProperty.call(
|
|
209975
|
+
const group = base[key] && typeof base[key] === "object" && !Array.isArray(base[key]) ? base[key] : {};
|
|
209976
|
+
const current = Object.prototype.hasOwnProperty.call(group, SERVER_NAME) ? group[SERVER_NAME] : void 0;
|
|
209844
209977
|
if (deepEqual(current, entry)) {
|
|
209845
209978
|
return { action: "skip", next: null, reason: "already present, identical" };
|
|
209846
209979
|
}
|
|
209847
209980
|
const next = {
|
|
209848
209981
|
...base,
|
|
209849
|
-
|
|
209850
|
-
...
|
|
209982
|
+
[key]: {
|
|
209983
|
+
...group,
|
|
209851
209984
|
[SERVER_NAME]: entry
|
|
209852
209985
|
}
|
|
209853
209986
|
};
|
|
209854
209987
|
if (!hadFile) {
|
|
209855
209988
|
return { action: "create", next, reason: "created" };
|
|
209856
209989
|
}
|
|
209857
|
-
const reason = current === void 0 ?
|
|
209990
|
+
const reason = current === void 0 ? `added ${key}.coderifts; preserved other servers` : `updated ${key}.coderifts; preserved other servers`;
|
|
209858
209991
|
return { action: "merge", next, reason };
|
|
209859
209992
|
}
|
|
209860
209993
|
function serializeMcp(doc) {
|
|
@@ -209923,7 +210056,7 @@ var require_mcp_repo_config = __commonJS({
|
|
|
209923
210056
|
existing = {};
|
|
209924
210057
|
}
|
|
209925
210058
|
}
|
|
209926
|
-
const plan = planMcpMerge(existing, host.entry);
|
|
210059
|
+
const plan = planMcpMerge(existing, host.entry, host.rootKey);
|
|
209927
210060
|
if (plan.action === "skip") {
|
|
209928
210061
|
return { action: "skip", relPath: host.relPath, reason: plan.reason, exitCode: 0 };
|
|
209929
210062
|
}
|
|
@@ -209960,7 +210093,7 @@ var require_mcp_repo_config = __commonJS({
|
|
|
209960
210093
|
if (!exists(fp)) return false;
|
|
209961
210094
|
try {
|
|
209962
210095
|
const doc = JSON.parse(readFile(fp));
|
|
209963
|
-
return planMcpMerge(doc, host.entry).action === "skip";
|
|
210096
|
+
return planMcpMerge(doc, host.entry, host.rootKey).action === "skip";
|
|
209964
210097
|
} catch {
|
|
209965
210098
|
return false;
|
|
209966
210099
|
}
|
|
@@ -209969,6 +210102,7 @@ var require_mcp_repo_config = __commonJS({
|
|
|
209969
210102
|
MCP_URL,
|
|
209970
210103
|
SERVER_NAME,
|
|
209971
210104
|
MCP_HOSTS,
|
|
210105
|
+
MCP_HOST_IDS,
|
|
209972
210106
|
planMcpMerge,
|
|
209973
210107
|
serializeMcp,
|
|
209974
210108
|
writeHostMcpConfig,
|
|
@@ -210084,6 +210218,7 @@ var require_init_agents = __commonJS({
|
|
|
210084
210218
|
var { installClaudeHook, installCursorHook } = require_hook();
|
|
210085
210219
|
var {
|
|
210086
210220
|
MCP_HOSTS,
|
|
210221
|
+
MCP_HOST_IDS,
|
|
210087
210222
|
writeHostMcpConfig,
|
|
210088
210223
|
mcpConfigPresent
|
|
210089
210224
|
} = require_mcp_repo_config();
|
|
@@ -210095,15 +210230,15 @@ var require_init_agents = __commonJS({
|
|
|
210095
210230
|
workflowPresent
|
|
210096
210231
|
} = require_contract_gate_workflow();
|
|
210097
210232
|
if (process.env.NO_COLOR) chalk.level = 0;
|
|
210098
|
-
var USAGE = `Usage: coderifts init --agents [--hosts=claude,cursor,all] [--no-hook] [--no-workflow] [--dry-run] [--check] [--out <dir>]
|
|
210233
|
+
var USAGE = `Usage: coderifts init --agents [--hosts=claude,cursor,copilot,all] [--no-hook] [--no-workflow] [--dry-run] [--check] [--out <dir>]
|
|
210099
210234
|
|
|
210100
210235
|
Wire this repository for governed agent work in ONE command:
|
|
210101
|
-
1. repo-level MCP config (per host: .mcp.json / .cursor/mcp.json)
|
|
210236
|
+
1. repo-level MCP config (per host: .mcp.json / .cursor/mcp.json / .vscode/mcp.json)
|
|
210102
210237
|
2. generated agent rule files (reuses agent-setup)
|
|
210103
|
-
3. host hook (Claude Code
|
|
210238
|
+
3. host hook (Claude Code / VS Code Copilot via .claude/settings.json; Cursor via .cursor/hooks.json)
|
|
210104
210239
|
4. .github/workflows/coderifts.yml (coderifts/contract-gate@v0)
|
|
210105
210240
|
|
|
210106
|
-
--hosts <list> claude, cursor, or all (default: all)
|
|
210241
|
+
--hosts <list> claude, cursor, copilot, or all (default: all)
|
|
210107
210242
|
--no-hook Skip host hook install
|
|
210108
210243
|
--no-workflow Skip writing the CI workflow
|
|
210109
210244
|
--dry-run Print the plan without writing
|
|
@@ -210114,7 +210249,8 @@ var require_init_agents = __commonJS({
|
|
|
210114
210249
|
var SHARED_RULE = "AGENTS.md";
|
|
210115
210250
|
var HOST_RULES = Object.freeze({
|
|
210116
210251
|
claude: Object.freeze(["CLAUDE.md"]),
|
|
210117
|
-
cursor: Object.freeze([".cursor/rules/coderifts.mdc"])
|
|
210252
|
+
cursor: Object.freeze([".cursor/rules/coderifts.mdc"]),
|
|
210253
|
+
copilot: Object.freeze([".github/copilot-instructions.md"])
|
|
210118
210254
|
});
|
|
210119
210255
|
var EXTRA_RULES_ON_ALL = Object.freeze([
|
|
210120
210256
|
".github/copilot-instructions.md",
|
|
@@ -210134,27 +210270,28 @@ var require_init_agents = __commonJS({
|
|
|
210134
210270
|
" \xB7 write Gemini MCP config (Gemini uses httpUrl at extension level, not a repo file)"
|
|
210135
210271
|
]);
|
|
210136
210272
|
function parseHosts(raw) {
|
|
210273
|
+
const allHosts = [...MCP_HOST_IDS];
|
|
210137
210274
|
if (raw == null || raw === true || String(raw).trim() === "") {
|
|
210138
|
-
return { hosts:
|
|
210275
|
+
return { hosts: allHosts, allRules: true };
|
|
210139
210276
|
}
|
|
210140
210277
|
const parts = String(raw).split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
|
|
210141
210278
|
if (parts.length === 0) {
|
|
210142
|
-
return { hosts:
|
|
210279
|
+
return { hosts: allHosts, allRules: true, error: `init --agents: --hosts requires a value
|
|
210143
210280
|
${USAGE}` };
|
|
210144
210281
|
}
|
|
210145
|
-
const allowed = /* @__PURE__ */ new Set([
|
|
210282
|
+
const allowed = /* @__PURE__ */ new Set([...MCP_HOST_IDS, "all"]);
|
|
210146
210283
|
for (const p of parts) {
|
|
210147
210284
|
if (!allowed.has(p)) {
|
|
210148
210285
|
return {
|
|
210149
210286
|
hosts: [],
|
|
210150
210287
|
allRules: false,
|
|
210151
|
-
error: `init --agents: unknown host "${p}" (use claude, cursor, all)
|
|
210288
|
+
error: `init --agents: unknown host "${p}" (use claude, cursor, copilot, all)
|
|
210152
210289
|
${USAGE}`
|
|
210153
210290
|
};
|
|
210154
210291
|
}
|
|
210155
210292
|
}
|
|
210156
210293
|
if (parts.includes("all")) {
|
|
210157
|
-
return { hosts:
|
|
210294
|
+
return { hosts: allHosts, allRules: true };
|
|
210158
210295
|
}
|
|
210159
210296
|
const hosts = [];
|
|
210160
210297
|
for (const p of parts) {
|
|
@@ -210186,7 +210323,25 @@ ${USAGE}`
|
|
|
210186
210323
|
return hostId === "cursor" ? ".cursor/hooks.json" : ".claude/settings.json";
|
|
210187
210324
|
}
|
|
210188
210325
|
function hookNoteForHost(hostId) {
|
|
210189
|
-
|
|
210326
|
+
if (hostId === "cursor") return "preToolUse \u2192 coderifts cursor-hook";
|
|
210327
|
+
if (hostId === "copilot") {
|
|
210328
|
+
return "PreToolUse \u2192 coderifts claude-hook (VS Code reads .claude/settings.json by default)";
|
|
210329
|
+
}
|
|
210330
|
+
return "PreToolUse \u2192 coderifts claude-hook";
|
|
210331
|
+
}
|
|
210332
|
+
function hookInstallerForHost(hostId) {
|
|
210333
|
+
return hostId === "cursor" ? installCursorHook : installClaudeHook;
|
|
210334
|
+
}
|
|
210335
|
+
function uniqueHookHosts(hosts) {
|
|
210336
|
+
const seen = /* @__PURE__ */ new Set();
|
|
210337
|
+
const out = [];
|
|
210338
|
+
for (const h of hosts) {
|
|
210339
|
+
const rel = hookRelForHost(h);
|
|
210340
|
+
if (seen.has(rel)) continue;
|
|
210341
|
+
seen.add(rel);
|
|
210342
|
+
out.push(h);
|
|
210343
|
+
}
|
|
210344
|
+
return out;
|
|
210190
210345
|
}
|
|
210191
210346
|
function wantHook(options) {
|
|
210192
210347
|
if (options.noHook === true) return false;
|
|
@@ -210233,9 +210388,9 @@ ${USAGE}`
|
|
|
210233
210388
|
const hooksMissing = [];
|
|
210234
210389
|
const hooksWanted = wantHook(options);
|
|
210235
210390
|
if (hooksWanted) {
|
|
210236
|
-
for (const h of hosts) {
|
|
210391
|
+
for (const h of uniqueHookHosts(hosts)) {
|
|
210237
210392
|
const rel = hookRelForHost(h);
|
|
210238
|
-
const installer = h
|
|
210393
|
+
const installer = hookInstallerForHost(h);
|
|
210239
210394
|
const r = installer({ cwd: outDir, silent: true, dryRun: true });
|
|
210240
210395
|
if (r && r.code === "NOOP") hooksPresent.push(rel);
|
|
210241
210396
|
else hooksMissing.push(rel);
|
|
@@ -210366,7 +210521,7 @@ ${USAGE}`
|
|
|
210366
210521
|
items.push({ section: "rules", action: "skip", relPath: rel, note: "exists; installer does not overwrite" });
|
|
210367
210522
|
}
|
|
210368
210523
|
if (!wantHook(options)) {
|
|
210369
|
-
for (const h of hosts) {
|
|
210524
|
+
for (const h of uniqueHookHosts(hosts)) {
|
|
210370
210525
|
items.push({
|
|
210371
210526
|
section: "hooks",
|
|
210372
210527
|
action: "skip",
|
|
@@ -210375,8 +210530,8 @@ ${USAGE}`
|
|
|
210375
210530
|
});
|
|
210376
210531
|
}
|
|
210377
210532
|
} else {
|
|
210378
|
-
for (const h of hosts) {
|
|
210379
|
-
const installer = h
|
|
210533
|
+
for (const h of uniqueHookHosts(hosts)) {
|
|
210534
|
+
const installer = hookInstallerForHost(h);
|
|
210380
210535
|
const r = installer({
|
|
210381
210536
|
cwd: outDir,
|
|
210382
210537
|
silent: true,
|
|
@@ -238767,7 +238922,7 @@ var require_outcome = __commonJS({
|
|
|
238767
238922
|
var require_cursor_hook = __commonJS({
|
|
238768
238923
|
"src/commands/cursor-hook.js"(exports2, module2) {
|
|
238769
238924
|
"use strict";
|
|
238770
|
-
var { runClaudeHook } = require_claude_hook();
|
|
238925
|
+
var { runClaudeHook, pickToolField, filePathFromInput, contentFromInput, coerceToolInput } = require_claude_hook();
|
|
238771
238926
|
var USAGE = `Usage: coderifts cursor-hook
|
|
238772
238927
|
|
|
238773
238928
|
Cursor preToolUse adapter. Reads Cursor's hook JSON on stdin, emits
|
|
@@ -238794,15 +238949,15 @@ Unparseable stdin denies (fail-closed).
|
|
|
238794
238949
|
if (!obj || typeof obj !== "object" || Array.isArray(obj)) {
|
|
238795
238950
|
return { ok: false, reason: "stdin JSON is not an object" };
|
|
238796
238951
|
}
|
|
238797
|
-
const toolName =
|
|
238798
|
-
if (!toolName) return { ok: false, reason: "missing tool_name" };
|
|
238799
|
-
const toolInput = obj
|
|
238952
|
+
const toolName = pickToolField(obj, "tool_name", "toolName", "name");
|
|
238953
|
+
if (typeof toolName !== "string" || !toolName) return { ok: false, reason: "missing tool_name" };
|
|
238954
|
+
const toolInput = coerceToolInput(pickToolField(obj, "tool_input", "toolInput", "input")) || (filePathFromInput(obj) ? obj : null);
|
|
238800
238955
|
if (!toolInput) return { ok: false, reason: "missing tool_input" };
|
|
238801
238956
|
return { ok: true, payload: obj, toolName, toolInput };
|
|
238802
238957
|
}
|
|
238803
238958
|
function toClaudeShape(toolName, toolInput) {
|
|
238804
|
-
const filePath = toolInput
|
|
238805
|
-
const content = toolInput
|
|
238959
|
+
const filePath = filePathFromInput(toolInput) || null;
|
|
238960
|
+
const content = contentFromInput(toolInput);
|
|
238806
238961
|
const out = { ...toolInput };
|
|
238807
238962
|
if (filePath) out.file_path = filePath;
|
|
238808
238963
|
if (content !== void 0) out.content = content;
|
|
@@ -240564,7 +240719,7 @@ program.command("registry-gate [dir]").description("Admit a directory of OpenAPI
|
|
|
240564
240719
|
process.exitCode = code;
|
|
240565
240720
|
process.exit(code);
|
|
240566
240721
|
});
|
|
240567
|
-
program.command("init [template]").description("Generate a .coderifts.yml from a policy template, or wire governed agent work (--agents)").option("--agents", "Wire this repo for governed agent work (MCP config, rule files, host hook, CI gate)").option("--hosts <list>", "With --agents: claude, cursor, or all (default: all)", "all").option("--no-hook", "With --agents: skip host hook install").option("--no-workflow", "With --agents: skip writing .github/workflows/coderifts.yml").option("--dry-run", "With --agents: print the plan without writing").option("--check", "With --agents: report which of the four pieces are present (no write)").option("--out <dir>", "With --agents: target directory (default: cwd)").action(async (template, options) => {
|
|
240722
|
+
program.command("init [template]").description("Generate a .coderifts.yml from a policy template, or wire governed agent work (--agents)").option("--agents", "Wire this repo for governed agent work (MCP config, rule files, host hook, CI gate)").option("--hosts <list>", "With --agents: claude, cursor, copilot, or all (default: all)", "all").option("--no-hook", "With --agents: skip host hook install").option("--no-workflow", "With --agents: skip writing .github/workflows/coderifts.yml").option("--dry-run", "With --agents: print the plan without writing").option("--check", "With --agents: report which of the four pieces are present (no write)").option("--out <dir>", "With --agents: target directory (default: cwd)").action(async (template, options) => {
|
|
240568
240723
|
const {
|
|
240569
240724
|
runInitAgents,
|
|
240570
240725
|
agentFlagsRequireAgents,
|
package/package.json
CHANGED