coderifts 3.0.0 → 3.1.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/bin/coderifts.js +32 -0
- package/dist/cli.js +327 -1
- package/package.json +1 -1
package/bin/coderifts.js
CHANGED
|
@@ -169,6 +169,38 @@ program
|
|
|
169
169
|
runAgentSetup(options, { exit: true });
|
|
170
170
|
});
|
|
171
171
|
|
|
172
|
+
// ── copilot-setup — emit GitHub Copilot MCP configs (VS Code + cloud agent + custom agent) ──
|
|
173
|
+
// Single-source from CANONICAL_TOOL_NAMES; root keys: servers (VS Code) vs mcpServers (cloud).
|
|
174
|
+
program
|
|
175
|
+
.command('copilot-setup')
|
|
176
|
+
.description('Write GitHub Copilot MCP configs (.vscode/mcp.json + cloud-agent paste JSON + docs)')
|
|
177
|
+
.option('--out <dir>', 'Target directory (default: current working directory)')
|
|
178
|
+
.option('--check', 'Exit 0 if on-disk files match embedded content; exit 1 on drift')
|
|
179
|
+
.option('--force', 'Overwrite existing files (default: skip collisions)')
|
|
180
|
+
.action((options) => {
|
|
181
|
+
const { runCopilotSetup } = require('../src/commands/copilot-setup');
|
|
182
|
+
runCopilotSetup(options, { exit: true });
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
// ── lock — agent contract lockfile v1 (observed usage; ID847) ──
|
|
186
|
+
// GET /api/v1/lock?repo= → write coderifts.lock. Observed-only; empty agents when none recorded.
|
|
187
|
+
program
|
|
188
|
+
.command('lock [repo]')
|
|
189
|
+
.description('Fetch the observed agent-contract lockfile (coderifts.lock v1) for a repo')
|
|
190
|
+
.option('--repo <owner/repo>', 'Repository (owner/repo); also accepted as a positional argument')
|
|
191
|
+
.option('--out <path>', 'Output path (default: coderifts.lock in cwd)')
|
|
192
|
+
.option('--json', 'Print the lock document JSON to stdout (still writes --out)')
|
|
193
|
+
.action(async (repoPositional, options) => {
|
|
194
|
+
const { runLock } = require('../src/commands/lock');
|
|
195
|
+
const result = await runLock({
|
|
196
|
+
...options,
|
|
197
|
+
repo: options.repo || repoPositional || null,
|
|
198
|
+
});
|
|
199
|
+
if (result && typeof result.exitCode === 'number') {
|
|
200
|
+
process.exitCode = result.exitCode;
|
|
201
|
+
}
|
|
202
|
+
});
|
|
203
|
+
|
|
172
204
|
// ── hook command group ──
|
|
173
205
|
const hookCmd = program
|
|
174
206
|
.command('hook')
|
package/dist/cli.js
CHANGED
|
@@ -3007,7 +3007,7 @@ var require_package = __commonJS({
|
|
|
3007
3007
|
"package.json"(exports2, module2) {
|
|
3008
3008
|
module2.exports = {
|
|
3009
3009
|
name: "coderifts",
|
|
3010
|
-
version: "3.
|
|
3010
|
+
version: "3.1.0",
|
|
3011
3011
|
description: "Detect breaking API changes from the command line. Works locally or with the CodeRifts cloud API.",
|
|
3012
3012
|
author: "CodeRifts <hello@coderifts.com>",
|
|
3013
3013
|
license: "MIT",
|
|
@@ -13549,10 +13549,15 @@ var require_cloud = __commonJS({
|
|
|
13549
13549
|
const q = encodeURIComponent(String(repo || ""));
|
|
13550
13550
|
return cloudRequest("GET", `/api/v1/enforcement-status?repo=${q}`, apiKey);
|
|
13551
13551
|
}
|
|
13552
|
+
function cloudGetLock(repo, apiKey) {
|
|
13553
|
+
const q = encodeURIComponent(String(repo || ""));
|
|
13554
|
+
return cloudRequest("GET", `/api/v1/lock?repo=${q}`, apiKey);
|
|
13555
|
+
}
|
|
13552
13556
|
module2.exports = {
|
|
13553
13557
|
cloudDiff,
|
|
13554
13558
|
cloudRequest,
|
|
13555
13559
|
cloudGetEnforcementStatus,
|
|
13560
|
+
cloudGetLock,
|
|
13556
13561
|
API_BASE
|
|
13557
13562
|
};
|
|
13558
13563
|
}
|
|
@@ -97229,6 +97234,313 @@ ${USAGE}` };
|
|
|
97229
97234
|
}
|
|
97230
97235
|
});
|
|
97231
97236
|
|
|
97237
|
+
// src/copilot-mcp.embedded.js
|
|
97238
|
+
var require_copilot_mcp_embedded = __commonJS({
|
|
97239
|
+
"src/copilot-mcp.embedded.js"(exports2, module2) {
|
|
97240
|
+
"use strict";
|
|
97241
|
+
var COPILOT_MCP_PATHS = Object.freeze([
|
|
97242
|
+
".vscode/mcp.json",
|
|
97243
|
+
"copilot-cloud-agent-mcp.json",
|
|
97244
|
+
"copilot-custom-agent-mcp.frontmatter.md",
|
|
97245
|
+
"docs/copilot-mcp.md"
|
|
97246
|
+
]);
|
|
97247
|
+
var COPILOT_MCP_FILES = Object.freeze({
|
|
97248
|
+
".vscode/mcp.json": '{\n "$comment": "GENERATED by scripts/generate-copilot-mcp.js from MCP_MANIFEST / CANONICAL_TOOL_NAMES (src/routes/mcp-streamable.js). Do not hand-edit; re-run the generator.",\n "servers": {\n "coderifts": {\n "type": "http",\n "url": "https://app.coderifts.com/mcp",\n "headers": {\n "Authorization": "Bearer ${input:coderifts_api_key}"\n }\n }\n },\n "inputs": [\n {\n "type": "promptString",\n "id": "coderifts_api_key",\n "description": "CodeRifts API key (https://coderifts.com) \u2014 Authorization Bearer",\n "password": true\n }\n ]\n}\n',
|
|
97249
|
+
"copilot-cloud-agent-mcp.json": '{\n "mcpServers": {\n "coderifts": {\n "type": "http",\n "url": "https://app.coderifts.com/mcp",\n "tools": [\n "preflight_change_set",\n "verify_receipt",\n "get_decision_details"\n ],\n "headers": {\n "Authorization": "Bearer ${COPILOT_MCP_CODERIFTS_API_KEY}"\n }\n }\n }\n}\n',
|
|
97250
|
+
"copilot-custom-agent-mcp.frontmatter.md": "---\n# GENERATED by scripts/generate-copilot-mcp.js from MCP_MANIFEST / CANONICAL_TOOL_NAMES (src/routes/mcp-streamable.js). Do not hand-edit; re-run the generator.\nname: coderifts-governance\ndescription: >\n CodeRifts API governance \u2014 preflight contract change sets before merge/deploy/publish,\n verify receipts, look up prior decisions. Branch on execution_action only.\ntools: ['coderifts/preflight_change_set', 'coderifts/verify_receipt', 'coderifts/get_decision_details']\nmcp-servers:\n coderifts:\n type: http\n url: https://app.coderifts.com/mcp\n tools:\n - preflight_change_set\n - verify_receipt\n - get_decision_details\n headers:\n Authorization: Bearer ${{ secrets.COPILOT_MCP_CODERIFTS_API_KEY }}\n---\n\nYou are a CodeRifts-aware agent. Before merge, deploy, publish, or tool registration when\ncontract artifacts changed, call `coderifts/preflight_change_set` with the complete base\u2192head\nchange set. Branch on `execution_action` only (CONTINUE, CONTINUE_WITH_MONITORING,\nREQUEST_APPROVAL, STOP). Do not treat `decision` or `safe_for_agent` as control flow.\nUse `coderifts/verify_receipt` to check an existing receipt; `coderifts/get_decision_details`\nfor a prior decision_id.\n",
|
|
97251
|
+
"docs/copilot-mcp.md": '# CodeRifts + GitHub Copilot MCP\n\n<!-- GENERATED by scripts/generate-copilot-mcp.js from MCP_MANIFEST / CANONICAL_TOOL_NAMES (src/routes/mcp-streamable.js). Do not hand-edit; re-run the generator. -->\n\nWire the **hosted** CodeRifts MCP server (`https://app.coderifts.com/mcp`) into every Copilot surface from **one**\nsource of truth (`CANONICAL_TOOL_NAMES` in `src/routes/mcp-streamable.js`).\n\n## Canonical tools (live tools/list)\n\n- `preflight_change_set`\n- `verify_receipt`\n- `get_decision_details`\n\nDo **not** list hidden/advanced aliases here. Only these 3 tools are the default\nagent-facing surface.\n\n## The servers-vs-mcpServers trap\n\n| Surface | Config location | Root key | Auth |\n|---------|-----------------|----------|------|\n| **VS Code / Copilot Chat** | `.vscode/mcp.json` | **`servers`** | `${input:coderifts_api_key}` + `inputs[]` |\n| **Copilot cloud agent + code review** | Repo **Settings \u2192 Copilot \u2192 MCP servers** (paste JSON) | **`mcpServers`** | Agents secret `COPILOT_MCP_CODERIFTS_API_KEY` in `headers` |\n| **Custom agent** (org/enterprise) | Agent profile `.md` YAML frontmatter | **`mcp-servers`** | `${{ secrets.COPILOT_MCP_CODERIFTS_API_KEY }}` |\n\nCursor / Claude Desktop / Grok use `mcpServers` in their own files \u2014 that is a **different**\necosystem. Do not copy a Cursor config into `.vscode/mcp.json`, and do not paste a VS Code\n`servers` document into GitHub repo Settings.\n\n## 1. VS Code / Copilot Chat (developer surface)\n\n1. Get an API key at https://coderifts.com\n2. Write the generated file to `.vscode/mcp.json` (or run `coderifts copilot-setup`)\n3. Reload VS Code; when prompted, paste the API key for `coderifts_api_key`\n4. In Copilot Chat Agent mode, confirm tools: preflight_change_set, verify_receipt, get_decision_details\n\n```json\n{\n "$comment": "GENERATED by scripts/generate-copilot-mcp.js from MCP_MANIFEST / CANONICAL_TOOL_NAMES (src/routes/mcp-streamable.js). Do not hand-edit; re-run the generator.",\n "servers": {\n "coderifts": {\n "type": "http",\n "url": "https://app.coderifts.com/mcp",\n "headers": {\n "Authorization": "Bearer ${input:coderifts_api_key}"\n }\n }\n },\n "inputs": [\n {\n "type": "promptString",\n "id": "coderifts_api_key",\n "description": "CodeRifts API key (https://coderifts.com) \u2014 Authorization Bearer",\n "password": true\n }\n ]\n}\n```\n\n## 2. Copilot cloud agent (PR / issue governance surface)\n\n1. Repo **Settings \u2192 Copilot \u2192 MCP servers** (or **Cloud agent \u2192 MCP configuration**)\n2. Paste the JSON below (root key **`mcpServers`**, includes required `tools` allowlist)\n3. Add an Agents secret: name `COPILOT_MCP_CODERIFTS_API_KEY`, value = CodeRifts API key\n4. Save. Validate: assign an issue to Copilot \u2192 session logs \u2192 **Start MCP Servers**\n\n```json\n{\n "mcpServers": {\n "coderifts": {\n "type": "http",\n "url": "https://app.coderifts.com/mcp",\n "tools": [\n "preflight_change_set",\n "verify_receipt",\n "get_decision_details"\n ],\n "headers": {\n "Authorization": "Bearer ${COPILOT_MCP_CODERIFTS_API_KEY}"\n }\n }\n }\n}\n```\n\nNotes:\n\n- Cloud agent does **not** support OAuth remote MCP; Bearer is correct for CodeRifts.\n- Cloud agent does **not** support interactive `inputs` \u2014 use Agents secrets only.\n- `tools` is required; list the 3 canonical tools (or `["*"]` only if you accept every tool the server exposes).\n\n## 3. Custom agent (optional org/enterprise)\n\nAdd `mcp-servers` to the agent profile frontmatter (see generated\n`copilot-custom-agent-mcp.frontmatter.md`). Tool names may be namespaced as\n`coderifts/<tool>` in the profile `tools` list.\n\n## Regenerate / drift-check\n\n```bash\nnode scripts/generate-copilot-mcp.js\nnode scripts/generate-copilot-mcp.js --check\ncoderifts copilot-setup --out . # write into a repo\ncoderifts copilot-setup --check # drift vs embedded\n```\n\nServer URL and tool names always come from the live manifest \u2014 never hand-edit tool lists.\n'
|
|
97252
|
+
});
|
|
97253
|
+
module2.exports = { COPILOT_MCP_FILES, COPILOT_MCP_PATHS };
|
|
97254
|
+
}
|
|
97255
|
+
});
|
|
97256
|
+
|
|
97257
|
+
// src/commands/copilot-setup.js
|
|
97258
|
+
var require_copilot_setup = __commonJS({
|
|
97259
|
+
"src/commands/copilot-setup.js"(exports2, module2) {
|
|
97260
|
+
"use strict";
|
|
97261
|
+
var fs = require("fs");
|
|
97262
|
+
var path = require("path");
|
|
97263
|
+
var chalk = require_source();
|
|
97264
|
+
var { COPILOT_MCP_FILES, COPILOT_MCP_PATHS } = require_copilot_mcp_embedded();
|
|
97265
|
+
if (process.env.NO_COLOR) chalk.level = 0;
|
|
97266
|
+
var USAGE = `Usage: coderifts copilot-setup [--out <dir>] [--check] [--force]
|
|
97267
|
+
|
|
97268
|
+
--out <dir> Target directory (default: current working directory)
|
|
97269
|
+
--check Exit 0 if on-disk files match embedded content; exit 1 on drift
|
|
97270
|
+
--force Overwrite existing files (default: skip collisions)
|
|
97271
|
+
Unknown flags exit 1 (never silently ignored).
|
|
97272
|
+
|
|
97273
|
+
Writes:
|
|
97274
|
+
.vscode/mcp.json VS Code / Copilot Chat (root key: servers)
|
|
97275
|
+
copilot-cloud-agent-mcp.json Paste into Settings \u2192 Copilot \u2192 MCP (mcpServers)
|
|
97276
|
+
copilot-custom-agent-mcp.frontmatter.md Custom agent YAML frontmatter
|
|
97277
|
+
docs/copilot-mcp.md Install guide
|
|
97278
|
+
`;
|
|
97279
|
+
function parseCopilotSetupArgs(argv) {
|
|
97280
|
+
const args = argv.slice(2);
|
|
97281
|
+
let out = null;
|
|
97282
|
+
let check = false;
|
|
97283
|
+
let force = false;
|
|
97284
|
+
let i = 0;
|
|
97285
|
+
while (i < args.length && !String(args[i]).startsWith("-")) i += 1;
|
|
97286
|
+
while (i < args.length) {
|
|
97287
|
+
const a = args[i];
|
|
97288
|
+
if (a === "--out") {
|
|
97289
|
+
const v = args[i + 1];
|
|
97290
|
+
if (v == null || v.startsWith("-")) {
|
|
97291
|
+
return { out, check, force, error: `copilot-setup: --out requires a path
|
|
97292
|
+
${USAGE}` };
|
|
97293
|
+
}
|
|
97294
|
+
out = path.resolve(v);
|
|
97295
|
+
i += 2;
|
|
97296
|
+
continue;
|
|
97297
|
+
}
|
|
97298
|
+
if (a === "--check") {
|
|
97299
|
+
check = true;
|
|
97300
|
+
i += 1;
|
|
97301
|
+
continue;
|
|
97302
|
+
}
|
|
97303
|
+
if (a === "--force") {
|
|
97304
|
+
force = true;
|
|
97305
|
+
i += 1;
|
|
97306
|
+
continue;
|
|
97307
|
+
}
|
|
97308
|
+
if (a.startsWith("-")) {
|
|
97309
|
+
return { out, check, force, error: `copilot-setup: unrecognized argument: ${a}
|
|
97310
|
+
${USAGE}` };
|
|
97311
|
+
}
|
|
97312
|
+
return { out, check, force, error: `copilot-setup: unrecognized argument: ${a}
|
|
97313
|
+
${USAGE}` };
|
|
97314
|
+
}
|
|
97315
|
+
return { out, check, force };
|
|
97316
|
+
}
|
|
97317
|
+
function runCopilotSetup(options = {}, deps = {}) {
|
|
97318
|
+
const log = deps.log || console.log.bind(console);
|
|
97319
|
+
const logErr = deps.logErr || console.error.bind(console);
|
|
97320
|
+
const doExit = deps.exit !== false;
|
|
97321
|
+
const cwd = deps.cwd || process.cwd();
|
|
97322
|
+
const exists = deps.exists || fs.existsSync.bind(fs);
|
|
97323
|
+
const readFile = deps.readFile || ((p) => fs.readFileSync(p, "utf8"));
|
|
97324
|
+
const writeFile = deps.writeFile || ((p, c) => {
|
|
97325
|
+
fs.mkdirSync(path.dirname(p), { recursive: true });
|
|
97326
|
+
fs.writeFileSync(p, c, "utf8");
|
|
97327
|
+
});
|
|
97328
|
+
let outDir = options.out ? path.resolve(String(options.out)) : cwd;
|
|
97329
|
+
let check = !!options.check;
|
|
97330
|
+
let force = !!options.force;
|
|
97331
|
+
if (deps.argv) {
|
|
97332
|
+
const parsed = parseCopilotSetupArgs(deps.argv);
|
|
97333
|
+
if (parsed.error) {
|
|
97334
|
+
logErr(parsed.error.trimEnd());
|
|
97335
|
+
if (doExit) process.exit(1);
|
|
97336
|
+
return { exitCode: 1, code: "USAGE", message: parsed.error };
|
|
97337
|
+
}
|
|
97338
|
+
if (parsed.out) outDir = parsed.out;
|
|
97339
|
+
check = parsed.check;
|
|
97340
|
+
force = parsed.force;
|
|
97341
|
+
}
|
|
97342
|
+
const files = deps.files || COPILOT_MCP_FILES;
|
|
97343
|
+
const relPaths = deps.paths || COPILOT_MCP_PATHS;
|
|
97344
|
+
if (check) {
|
|
97345
|
+
let stale = false;
|
|
97346
|
+
for (const rel of relPaths) {
|
|
97347
|
+
const fp = path.join(outDir, rel);
|
|
97348
|
+
const expected = files[rel];
|
|
97349
|
+
if (!exists(fp)) {
|
|
97350
|
+
logErr(chalk.red(`copilot-setup --check: missing ${rel}`));
|
|
97351
|
+
stale = true;
|
|
97352
|
+
continue;
|
|
97353
|
+
}
|
|
97354
|
+
const onDisk = readFile(fp);
|
|
97355
|
+
if (onDisk !== expected) {
|
|
97356
|
+
logErr(chalk.red(`copilot-setup --check: drift ${rel}`));
|
|
97357
|
+
stale = true;
|
|
97358
|
+
}
|
|
97359
|
+
}
|
|
97360
|
+
if (stale) {
|
|
97361
|
+
logErr("Run: coderifts copilot-setup --out " + outDir + " --force");
|
|
97362
|
+
if (doExit) process.exit(1);
|
|
97363
|
+
return { exitCode: 1, code: "DRIFT", outDir };
|
|
97364
|
+
}
|
|
97365
|
+
log(chalk.green(`copilot-setup: up to date (${outDir}, ${relPaths.length} files)`));
|
|
97366
|
+
if (doExit) process.exit(0);
|
|
97367
|
+
return { exitCode: 0, code: "UP_TO_DATE", outDir };
|
|
97368
|
+
}
|
|
97369
|
+
const summary = { written: [], skipped: [], forced: [] };
|
|
97370
|
+
for (const rel of relPaths) {
|
|
97371
|
+
const fp = path.join(outDir, rel);
|
|
97372
|
+
const content = files[rel];
|
|
97373
|
+
if (exists(fp) && !force) {
|
|
97374
|
+
summary.skipped.push(rel);
|
|
97375
|
+
continue;
|
|
97376
|
+
}
|
|
97377
|
+
if (exists(fp) && force) summary.forced.push(rel);
|
|
97378
|
+
writeFile(fp, content);
|
|
97379
|
+
summary.written.push(rel);
|
|
97380
|
+
}
|
|
97381
|
+
if (!options.json) {
|
|
97382
|
+
log(chalk.bold("CodeRifts copilot-setup"));
|
|
97383
|
+
log(` target: ${outDir}`);
|
|
97384
|
+
for (const rel of summary.written) {
|
|
97385
|
+
const tag = summary.forced.includes(rel) ? "overwrote" : "wrote";
|
|
97386
|
+
log(chalk.green(` ${tag}: ${rel}`));
|
|
97387
|
+
}
|
|
97388
|
+
for (const rel of summary.skipped) {
|
|
97389
|
+
log(chalk.yellow(` skipped: ${rel} (exists; use --force to overwrite)`));
|
|
97390
|
+
}
|
|
97391
|
+
log("");
|
|
97392
|
+
log(chalk.dim(' VS Code: open .vscode/mcp.json \u2014 root key is "servers"'));
|
|
97393
|
+
log(chalk.dim(" Cloud: paste copilot-cloud-agent-mcp.json into Settings \u2192 Copilot \u2192 MCP"));
|
|
97394
|
+
log(chalk.dim(" Secret: COPILOT_MCP_CODERIFTS_API_KEY (Agents secret) = CodeRifts API key"));
|
|
97395
|
+
log(chalk.dim(` ${summary.written.length} written, ${summary.skipped.length} skipped`));
|
|
97396
|
+
}
|
|
97397
|
+
if (doExit) process.exit(0);
|
|
97398
|
+
return { exitCode: 0, code: "OK", outDir, ...summary };
|
|
97399
|
+
}
|
|
97400
|
+
module2.exports = {
|
|
97401
|
+
runCopilotSetup,
|
|
97402
|
+
parseCopilotSetupArgs,
|
|
97403
|
+
COPILOT_MCP_FILES,
|
|
97404
|
+
COPILOT_MCP_PATHS,
|
|
97405
|
+
USAGE
|
|
97406
|
+
};
|
|
97407
|
+
}
|
|
97408
|
+
});
|
|
97409
|
+
|
|
97410
|
+
// src/commands/lock.js
|
|
97411
|
+
var require_lock = __commonJS({
|
|
97412
|
+
"src/commands/lock.js"(exports2, module2) {
|
|
97413
|
+
"use strict";
|
|
97414
|
+
var fs = require("fs");
|
|
97415
|
+
var path = require("path");
|
|
97416
|
+
var chalk = require_source();
|
|
97417
|
+
var { getApiKey } = require_config();
|
|
97418
|
+
var { cloudGetLock } = require_cloud();
|
|
97419
|
+
if (process.env.NO_COLOR) chalk.level = 0;
|
|
97420
|
+
var DEFAULT_OUT = "coderifts.lock";
|
|
97421
|
+
var EPHEMERAL_LOCK_KEYS = Object.freeze([
|
|
97422
|
+
"correlation_id",
|
|
97423
|
+
"generated_at",
|
|
97424
|
+
"request_correlation_id",
|
|
97425
|
+
"meta"
|
|
97426
|
+
]);
|
|
97427
|
+
var COMMITTED_KEY_ORDER = Object.freeze([
|
|
97428
|
+
"lockfile_version",
|
|
97429
|
+
"repo",
|
|
97430
|
+
"provenance",
|
|
97431
|
+
"agents",
|
|
97432
|
+
"last_accepted_fingerprint",
|
|
97433
|
+
"decision_spec_version",
|
|
97434
|
+
"receipt_kind"
|
|
97435
|
+
]);
|
|
97436
|
+
var USAGE = [
|
|
97437
|
+
"Usage: coderifts lock --repo owner/repo [--out coderifts.lock]",
|
|
97438
|
+
" or: coderifts lock owner/repo [--out path]",
|
|
97439
|
+
"",
|
|
97440
|
+
"Fetches the observed agent-contract lockfile (coderifts.lock v1) and writes it to disk.",
|
|
97441
|
+
"Requires a cloud API key (coderifts login or CODERIFTS_API_KEY).",
|
|
97442
|
+
"Observed-only: agents[] come from real usage observations; empty when none recorded.",
|
|
97443
|
+
"Written file is byte-stable for the same state (no correlation_id / generated_at)."
|
|
97444
|
+
].join("\n");
|
|
97445
|
+
function isValidRepo(full) {
|
|
97446
|
+
const parts = String(full || "").split("/");
|
|
97447
|
+
if (parts.length !== 2) return false;
|
|
97448
|
+
const [owner, repo] = parts.map((p) => p.trim());
|
|
97449
|
+
return !!(owner && repo);
|
|
97450
|
+
}
|
|
97451
|
+
function toCommittedLockfile(doc) {
|
|
97452
|
+
const src = doc && typeof doc === "object" && !Array.isArray(doc) ? doc : {};
|
|
97453
|
+
const out = {};
|
|
97454
|
+
for (const key of COMMITTED_KEY_ORDER) {
|
|
97455
|
+
if (!Object.prototype.hasOwnProperty.call(src, key)) continue;
|
|
97456
|
+
if (EPHEMERAL_LOCK_KEYS.includes(key)) continue;
|
|
97457
|
+
out[key] = src[key];
|
|
97458
|
+
}
|
|
97459
|
+
for (const k of EPHEMERAL_LOCK_KEYS) {
|
|
97460
|
+
if (Object.prototype.hasOwnProperty.call(out, k)) delete out[k];
|
|
97461
|
+
}
|
|
97462
|
+
return out;
|
|
97463
|
+
}
|
|
97464
|
+
function renderLockfileJson(doc) {
|
|
97465
|
+
return `${JSON.stringify(doc, null, 2)}
|
|
97466
|
+
`;
|
|
97467
|
+
}
|
|
97468
|
+
async function runLock(options = {}, deps = {}) {
|
|
97469
|
+
const getKey = deps.getApiKey || getApiKey;
|
|
97470
|
+
const fetchLock = deps.cloudGetLock || cloudGetLock;
|
|
97471
|
+
const log = deps.log || console.log;
|
|
97472
|
+
const errLog = deps.errLog || console.error;
|
|
97473
|
+
const writeFile = deps.writeFile || ((p, c) => {
|
|
97474
|
+
fs.mkdirSync(path.dirname(path.resolve(p)), { recursive: true });
|
|
97475
|
+
fs.writeFileSync(p, c, "utf8");
|
|
97476
|
+
});
|
|
97477
|
+
const cwd = deps.cwd || process.cwd();
|
|
97478
|
+
const repo = options.repo != null && String(options.repo).trim() ? String(options.repo).trim() : null;
|
|
97479
|
+
if (!repo) {
|
|
97480
|
+
errLog(chalk.red("Error: missing repo"));
|
|
97481
|
+
errLog(USAGE);
|
|
97482
|
+
return { exitCode: 1, error: "missing_repo" };
|
|
97483
|
+
}
|
|
97484
|
+
if (!isValidRepo(repo)) {
|
|
97485
|
+
errLog(chalk.red("Error: repo must be in owner/repo form (e.g. coderifts/app)"));
|
|
97486
|
+
return { exitCode: 1, error: "invalid_repo" };
|
|
97487
|
+
}
|
|
97488
|
+
const apiKey = getKey();
|
|
97489
|
+
if (!apiKey) {
|
|
97490
|
+
errLog(chalk.red("Error: no API key. Run `coderifts login` or set CODERIFTS_API_KEY."));
|
|
97491
|
+
return { exitCode: 1, error: "missing_api_key" };
|
|
97492
|
+
}
|
|
97493
|
+
let doc;
|
|
97494
|
+
try {
|
|
97495
|
+
doc = await fetchLock(repo, apiKey);
|
|
97496
|
+
} catch (e) {
|
|
97497
|
+
const msg = e && e.message ? String(e.message) : "request failed";
|
|
97498
|
+
errLog(chalk.red(`Error: ${msg}`));
|
|
97499
|
+
if (e && e.code) errLog(chalk.dim(` (${e.code})`));
|
|
97500
|
+
return { exitCode: 1, error: msg };
|
|
97501
|
+
}
|
|
97502
|
+
if (!doc || typeof doc !== "object" || doc.lockfile_version == null) {
|
|
97503
|
+
errLog(chalk.red("Error: invalid lockfile response from API"));
|
|
97504
|
+
return { exitCode: 1, error: "invalid_response" };
|
|
97505
|
+
}
|
|
97506
|
+
const committed = toCommittedLockfile(doc);
|
|
97507
|
+
const body = renderLockfileJson(committed);
|
|
97508
|
+
const outRel = options.out != null && String(options.out).trim() ? String(options.out).trim() : DEFAULT_OUT;
|
|
97509
|
+
const outPath = path.isAbsolute(outRel) ? outRel : path.join(cwd, outRel);
|
|
97510
|
+
try {
|
|
97511
|
+
writeFile(outPath, body);
|
|
97512
|
+
} catch (e) {
|
|
97513
|
+
const msg = e && e.message ? String(e.message) : "write failed";
|
|
97514
|
+
errLog(chalk.red(`Error: failed to write ${outPath}: ${msg}`));
|
|
97515
|
+
return { exitCode: 1, error: msg };
|
|
97516
|
+
}
|
|
97517
|
+
if (options.json) {
|
|
97518
|
+
log(body.trimEnd());
|
|
97519
|
+
} else {
|
|
97520
|
+
const nAgents = Array.isArray(committed.agents) ? committed.agents.length : 0;
|
|
97521
|
+
const nOps = Array.isArray(committed.agents) ? committed.agents.reduce((n, a) => n + (a.operations && a.operations.length || 0), 0) : 0;
|
|
97522
|
+
log(chalk.bold("CodeRifts lock (observed)"));
|
|
97523
|
+
log(` repo: ${committed.repo || repo}`);
|
|
97524
|
+
log(` wrote: ${outPath}`);
|
|
97525
|
+
log(` agents: ${nAgents} operations: ${nOps}`);
|
|
97526
|
+
log(` fingerprint: ${committed.last_accepted_fingerprint || chalk.dim("(none)")}`);
|
|
97527
|
+
log(chalk.dim(" Observed-only; file is byte-stable (no correlation_id / generated_at)."));
|
|
97528
|
+
}
|
|
97529
|
+
return { exitCode: 0, doc, committed, outPath };
|
|
97530
|
+
}
|
|
97531
|
+
module2.exports = {
|
|
97532
|
+
runLock,
|
|
97533
|
+
renderLockfileJson,
|
|
97534
|
+
toCommittedLockfile,
|
|
97535
|
+
isValidRepo,
|
|
97536
|
+
DEFAULT_OUT,
|
|
97537
|
+
EPHEMERAL_LOCK_KEYS,
|
|
97538
|
+
COMMITTED_KEY_ORDER,
|
|
97539
|
+
USAGE
|
|
97540
|
+
};
|
|
97541
|
+
}
|
|
97542
|
+
});
|
|
97543
|
+
|
|
97232
97544
|
// corpus/vectors-mcp-fpfn.json
|
|
97233
97545
|
var require_vectors_mcp_fpfn = __commonJS({
|
|
97234
97546
|
"corpus/vectors-mcp-fpfn.json"(exports2, module2) {
|
|
@@ -99180,6 +99492,20 @@ program.command("agent-setup").description("Write AGENTS.md / CLAUDE.md / Cursor
|
|
|
99180
99492
|
const { runAgentSetup } = require_agent_setup();
|
|
99181
99493
|
runAgentSetup(options, { exit: true });
|
|
99182
99494
|
});
|
|
99495
|
+
program.command("copilot-setup").description("Write GitHub Copilot MCP configs (.vscode/mcp.json + cloud-agent paste JSON + docs)").option("--out <dir>", "Target directory (default: current working directory)").option("--check", "Exit 0 if on-disk files match embedded content; exit 1 on drift").option("--force", "Overwrite existing files (default: skip collisions)").action((options) => {
|
|
99496
|
+
const { runCopilotSetup } = require_copilot_setup();
|
|
99497
|
+
runCopilotSetup(options, { exit: true });
|
|
99498
|
+
});
|
|
99499
|
+
program.command("lock [repo]").description("Fetch the observed agent-contract lockfile (coderifts.lock v1) for a repo").option("--repo <owner/repo>", "Repository (owner/repo); also accepted as a positional argument").option("--out <path>", "Output path (default: coderifts.lock in cwd)").option("--json", "Print the lock document JSON to stdout (still writes --out)").action(async (repoPositional, options) => {
|
|
99500
|
+
const { runLock } = require_lock();
|
|
99501
|
+
const result = await runLock({
|
|
99502
|
+
...options,
|
|
99503
|
+
repo: options.repo || repoPositional || null
|
|
99504
|
+
});
|
|
99505
|
+
if (result && typeof result.exitCode === "number") {
|
|
99506
|
+
process.exitCode = result.exitCode;
|
|
99507
|
+
}
|
|
99508
|
+
});
|
|
99183
99509
|
var hookCmd = program.command("hook").description("Manage the CodeRifts pre-push Git hook");
|
|
99184
99510
|
hookCmd.command("install").description("Install the CodeRifts pre-push hook in the current Git repo").action(() => {
|
|
99185
99511
|
const { install } = require_hook();
|
package/package.json
CHANGED