coderifts 1.9.0 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -64,6 +64,48 @@ Interactive configuration generator. Creates a `.coderifts.yml` file with indust
64
64
 
65
65
  Save your CodeRifts API key for cloud features. Get a free key at [app.coderifts.com/api/signup](https://app.coderifts.com/api/signup).
66
66
 
67
+ ### `coderifts publish-gate`
68
+
69
+ Gate **npm publish** on the same contract-preflight family as the pre-push hook:
70
+ `before` = last published / merge-base baseline, `after` = working tree. Exit **0**
71
+ only when `execution_action` permits publish; **BLOCK**, resolver errors, empty
72
+ before, and preflight unreachability all exit **1** (fail-closed). Empty `before`
73
+ is **never** treated as a silent NEW_ARTIFACT pass.
74
+
75
+ | Flag | Description | Default |
76
+ |------|-------------|---------|
77
+ | `--spec <path>` | Contract artifact path | `git config coderifts.specPath` or `api/openapi.yaml` |
78
+ | `--json` | Machine-readable result | `false` |
79
+
80
+ **Recommended package.json wiring:**
81
+
82
+ ```json
83
+ {
84
+ "scripts": {
85
+ "prepublishOnly": "coderifts publish-gate"
86
+ }
87
+ }
88
+ ```
89
+
90
+ **Before resolution order (fail-closed):**
91
+ 1. Git tag of `package.json` version (`vX.Y.Z`, then `X.Y.Z`)
92
+ 2. `git merge-base HEAD origin/main` (fallbacks: `origin/master`, `main`, `master`)
93
+ 3. Exit 1 with a clear error — never invent an empty baseline
94
+
95
+ On success the command prints a **receipt reference** when the preflight path issues one.
96
+
97
+ ### Git hook (`coderifts hook install`)
98
+
99
+ Installs a **pre-push** hook that diffs the configured OpenAPI path against a
100
+ resolved baseline (remote tip, or for new branches a merge-base with the
101
+ default branch). Empty-before allow is not used. The hook prefers closed-set
102
+ `execution_action` from the diff API (legacy `omega_decision` only when action
103
+ is absent). Configure with `git config coderifts.apiKey` and optional
104
+ `git config coderifts.specPath`.
105
+
106
+ After upgrading the `coderifts` CLI, re-run `coderifts hook install` so the
107
+ installed hook matches the package (already-installed hooks are not
108
+ auto-updated).
67
109
  ## CI/CD Integration
68
110
 
69
111
  ### GitHub Actions
package/bin/coderifts.js CHANGED
@@ -42,6 +42,23 @@ program
42
42
  await runDeployGate(options);
43
43
  });
44
44
 
45
+ // ── publish-gate — npm prepublishOnly (same decision family as pre-push, fail-closed before) ──
46
+ // Exit status is set only at this command boundary (library never process.exit).
47
+ // program.parse() does not await async actions; process.exit here makes the gate's
48
+ // fail-closed status authoritative for npm prepublishOnly / child_process callers.
49
+ program
50
+ .command('publish-gate')
51
+ .description('Gate npm publish on contract-artifact preflight (before=git baseline, after=working tree)')
52
+ .option('--spec <path>', 'Contract artifact path (default: git config coderifts.specPath or api/openapi.yaml)')
53
+ .option('--json', 'Machine-readable JSON result')
54
+ .action(async (options) => {
55
+ const { runPublishGate } = require('../src/commands/publish-gate');
56
+ const result = await runPublishGate(options);
57
+ const code = result && typeof result.exitCode === 'number' ? result.exitCode : 1;
58
+ process.exitCode = code;
59
+ process.exit(code);
60
+ });
61
+
45
62
  // ── init command ──
46
63
  program
47
64
  .command('init [template]')
@@ -107,4 +124,13 @@ corpusCmd
107
124
  corpusVerify(options);
108
125
  });
109
126
 
110
- program.parse();
127
+ // parseAsync: required so async command actions (publish-gate, diff, deploy-gate, …)
128
+ // complete before the process ends. program.parse() discards the action promise and
129
+ // can leave process.exitCode at the default 0 even after a fail-closed gate printed errors.
130
+ program.parseAsync(process.argv).catch((err) => {
131
+ console.error(err);
132
+ process.exitCode = typeof process.exitCode === 'number' && process.exitCode !== 0
133
+ ? process.exitCode
134
+ : 1;
135
+ process.exit(process.exitCode);
136
+ });