delimit-cli 2.1.0 → 2.2.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.
@@ -3,22 +3,41 @@ name: API Governance
3
3
  on:
4
4
  pull_request:
5
5
  paths:
6
- - '**.json'
7
- - '**.yaml'
8
- - '**.yml'
9
- - 'openapi/**'
10
- - 'api/**'
11
- - 'swagger/**'
6
+ - '**/*.yaml'
7
+ - '**/*.yml'
8
+ - '**/*.json'
9
+ - 'lib/**'
12
10
 
13
11
  jobs:
14
12
  api-check:
13
+ name: Delimit API Check
15
14
  runs-on: ubuntu-latest
15
+ permissions:
16
+ pull-requests: write
16
17
  steps:
17
- - uses: actions/checkout@v3
18
+ - uses: actions/checkout@v4
19
+
20
+ - uses: actions/checkout@v4
18
21
  with:
19
- fetch-depth: 0
20
-
21
- - name: Detect API breaking changes
22
+ ref: ${{ github.event.pull_request.base.sha }}
23
+ path: base
24
+
25
+ - name: Check for spec changes
26
+ id: spec-check
27
+ run: |
28
+ # Find any OpenAPI/Swagger specs in the repo
29
+ SPECS=$(find . -path ./base -prune -o \( -name "openapi*.yaml" -o -name "openapi*.yml" -o -name "openapi*.json" -o -name "swagger*.yaml" -o -name "swagger*.json" \) -print | head -1)
30
+ if [ -n "$SPECS" ]; then
31
+ echo "spec_found=true" >> $GITHUB_OUTPUT
32
+ echo "spec_path=$SPECS" >> $GITHUB_OUTPUT
33
+ else
34
+ echo "spec_found=false" >> $GITHUB_OUTPUT
35
+ fi
36
+
37
+ - name: Run Delimit
38
+ if: steps.spec-check.outputs.spec_found == 'true'
22
39
  uses: delimit-ai/delimit-action@v1
23
40
  with:
24
- advisory_mode: true
41
+ old_spec: base/${{ steps.spec-check.outputs.spec_path }}
42
+ new_spec: ${{ steps.spec-check.outputs.spec_path }}
43
+ mode: advisory
package/README.md CHANGED
@@ -1,97 +1,112 @@
1
- # Delimit
1
+ # delimit-cli
2
2
 
3
- **API governance CLI for development teams.** Detect breaking API changes, enforce policies, and maintain audit trails across your services.
3
+ **ESLint for API contracts** detect breaking changes, enforce semver, and generate migration guides for OpenAPI specs.
4
4
 
5
- [![npm](https://img.shields.io/npm/v/delimit)](https://www.npmjs.com/package/delimit)
5
+ [![npm](https://img.shields.io/npm/v/delimit-cli)](https://www.npmjs.com/package/delimit-cli)
6
6
  [![License: MIT](https://img.shields.io/badge/License-MIT-blue.svg)](https://opensource.org/licenses/MIT)
7
7
 
8
8
  ## Install
9
9
 
10
10
  ```bash
11
- npm install -g delimit
11
+ npm install -g delimit-cli
12
12
  ```
13
13
 
14
+ This installs the `delimit` command globally.
15
+
14
16
  ## Quick Start
15
17
 
16
18
  ```bash
17
- # Check current governance status
18
- delimit status
19
+ # Initialize a policy file in your repo
20
+ delimit init
21
+
22
+ # Detect breaking changes between two specs
23
+ delimit lint api/openapi-old.yaml api/openapi-new.yaml
19
24
 
20
- # Set up governance for this repo (advisory mode by default)
21
- delimit install --mode advisory
25
+ # See raw diff output
26
+ delimit diff api/openapi-old.yaml api/openapi-new.yaml
22
27
 
23
- # Validate an API spec
24
- delimit validate api/openapi.yaml
28
+ # Generate a human-readable explanation
29
+ delimit explain api/openapi-old.yaml api/openapi-new.yaml
25
30
  ```
26
31
 
27
- ## Governance Modes
32
+ ## Commands
33
+
34
+ | Command | Description |
35
+ |---------|-------------|
36
+ | `delimit init` | Create `.delimit/policies.yml` with default rules |
37
+ | `delimit lint <old> <new>` | Diff + policy check with semver badge and violations |
38
+ | `delimit diff <old> <new>` | Raw diff with `[BREAKING]`/`[safe]` tags |
39
+ | `delimit explain <old> <new>` | Human-readable change explanation |
28
40
 
29
- Delimit supports three enforcement levels. You choose:
41
+ ### Options
30
42
 
31
43
  ```bash
32
- delimit install --mode advisory # Warnings only, no blocking
33
- delimit install --mode guarded # Soft enforcement with overrides
34
- delimit install --mode enforce # Strict policy enforcement
35
- ```
44
+ # Specify explainer template (default: developer)
45
+ delimit explain old.yaml new.yaml -t migration
46
+ delimit explain old.yaml new.yaml -t pr_comment
47
+ delimit explain old.yaml new.yaml -t changelog
36
48
 
37
- ### Scope Control
49
+ # Include semver classification
50
+ delimit lint old.yaml new.yaml --current-version 1.0.0
38
51
 
39
- ```bash
40
- delimit install --scope repo # Current repository only
41
- delimit install --scope global # All repositories on this machine
52
+ # Use custom policy file
53
+ delimit lint old.yaml new.yaml -p .delimit/policies.yml
42
54
  ```
43
55
 
44
- ## What It Does
56
+ ### Explainer Templates
45
57
 
46
- - **API Change Detection** — Identifies breaking changes in OpenAPI/Swagger specs
47
- - **Policy Enforcement** — Applies configurable governance rules to API changes
48
- - **Evidence Collection** Records audit trail of all governance decisions
49
- - **MCP Integration** Works with Claude, Gemini, and other AI coding tools via Model Context Protocol
58
+ | Template | Audience |
59
+ |----------|----------|
60
+ | `developer` | Technical details for engineers |
61
+ | `team_lead` | Summary for engineering managers |
62
+ | `product` | Non-technical overview for PMs |
63
+ | `migration` | Step-by-step migration guide |
64
+ | `changelog` | Ready-to-paste changelog entry |
65
+ | `pr_comment` | GitHub PR comment format |
66
+ | `slack` | Slack message format |
50
67
 
51
- ## Usage with CI
68
+ ## CI/CD Integration
52
69
 
53
- For CI/CD integration, use the GitHub Action instead:
70
+ For automated PR checks, use the GitHub Action:
54
71
 
55
72
  ```yaml
56
73
  - uses: delimit-ai/delimit-action@v1
74
+ with:
75
+ old_spec: base/api/openapi.yaml
76
+ new_spec: api/openapi.yaml
57
77
  ```
58
78
 
59
- See [delimit-action](https://github.com/delimit-ai/delimit-action) for full CI setup.
79
+ See [Delimit API Governance](https://github.com/marketplace/actions/delimit-api-governance) on the GitHub Marketplace.
60
80
 
61
- ## Commands
81
+ ## Custom Policies
62
82
 
63
- | Command | Description |
64
- |---------|-------------|
65
- | `delimit status` | Show current governance state |
66
- | `delimit install` | Set up governance hooks and config |
67
- | `delimit validate <spec>` | Validate an API specification |
68
- | `delimit doctor` | Diagnose configuration issues |
69
- | `delimit uninstall` | Clean removal of all hooks and config |
70
-
71
- ## Uninstall
72
-
73
- ```bash
74
- delimit uninstall
75
- ```
76
-
77
- Cleanly removes all hooks, PATH modifications, and configuration files.
78
-
79
- ## Configuration
80
-
81
- Create `.delimit/policies.yml` in your repository:
83
+ Create `.delimit/policies.yml`:
82
84
 
83
85
  ```yaml
84
86
  rules:
85
87
  - id: no_endpoint_removal
86
88
  change_types: [endpoint_removed]
87
89
  severity: error
90
+ action: forbid
88
91
  message: "Endpoints cannot be removed without deprecation"
92
+
93
+ - id: warn_type_change
94
+ change_types: [type_changed]
95
+ severity: warning
96
+ action: warn
97
+ message: "Type change may break clients"
89
98
  ```
90
99
 
100
+ ## Supported Specs
101
+
102
+ - OpenAPI 3.0.x and 3.1.x
103
+ - Swagger 2.0
104
+ - YAML and JSON formats
105
+
91
106
  ## Links
92
107
 
93
- - [GitHub Action](https://github.com/delimit-ai/delimit-action) — CI/CD integration
94
- - [Website](https://delimit.ai) — Documentation and platform
108
+ - [GitHub Action](https://github.com/marketplace/actions/delimit-api-governance) — CI/CD integration
109
+ - [GitHub](https://github.com/delimit-ai/delimit) — Source code
95
110
  - [Issues](https://github.com/delimit-ai/delimit/issues) — Bug reports and feature requests
96
111
 
97
112
  ## License
@@ -783,20 +783,66 @@ rules: []
783
783
  });
784
784
 
785
785
  // Lint command — diff + policy (primary command)
786
+ // Supports zero-spec mode: `delimit lint` (no args) auto-extracts from FastAPI
786
787
  program
787
- .command('lint <old_spec> <new_spec>')
788
+ .command('lint [old_spec] [new_spec]')
788
789
  .description('Lint API specs for breaking changes and policy violations')
789
790
  .option('-p, --policy <file>', 'Custom policy file')
790
791
  .option('--current-version <ver>', 'Current API version for semver bump')
791
792
  .option('-n, --name <name>', 'API name for context')
792
793
  .option('--json', 'Output raw JSON')
794
+ .option('-d, --dir <path>', 'Project directory for zero-spec mode', '.')
793
795
  .action(async (oldSpec, newSpec, options) => {
794
796
  try {
795
- const result = apiEngine.lint(
796
- path.resolve(oldSpec),
797
- path.resolve(newSpec),
798
- { policy: options.policy, version: options.currentVersion, name: options.name }
799
- );
797
+ let result;
798
+
799
+ if (!oldSpec || !newSpec) {
800
+ // Zero-spec mode: extract from framework source
801
+ console.log(chalk.gray('No spec files provided — detecting framework...'));
802
+ const zeroResult = apiEngine.zeroSpec(path.resolve(options.dir));
803
+
804
+ if (!zeroResult.success) {
805
+ console.error(chalk.red(`\n ${zeroResult.error}\n`));
806
+ if (zeroResult.error_type === 'no_framework') {
807
+ console.log(' Usage: delimit lint <old_spec> <new_spec>');
808
+ console.log(' Or run from a FastAPI project directory.\n');
809
+ }
810
+ process.exit(1);
811
+ return;
812
+ }
813
+
814
+ console.log(chalk.green(` ${zeroResult.message}`));
815
+ console.log(` Extracted: ${zeroResult.paths_count} paths, ${zeroResult.schemas_count} schemas`);
816
+ console.log(` Spec: ${zeroResult.spec_path}\n`);
817
+
818
+ // Check for baseline
819
+ const baselineDir = path.join(path.resolve(options.dir), '.delimit');
820
+ const baselinePath = path.join(baselineDir, 'baseline.yaml');
821
+
822
+ if (!fs.existsSync(baselinePath)) {
823
+ // First run: save baseline
824
+ fs.mkdirSync(baselineDir, { recursive: true });
825
+ const yaml = require('js-yaml');
826
+ fs.writeFileSync(baselinePath, yaml.dump(zeroResult.spec));
827
+ console.log(chalk.green(' Saved baseline to .delimit/baseline.yaml'));
828
+ console.log(' Run again after making changes to see the diff.\n');
829
+ process.exit(0);
830
+ return;
831
+ }
832
+
833
+ // Compare against baseline
834
+ result = apiEngine.lint(
835
+ baselinePath,
836
+ zeroResult.spec_path,
837
+ { policy: options.policy, version: options.currentVersion, name: options.name }
838
+ );
839
+ } else {
840
+ result = apiEngine.lint(
841
+ path.resolve(oldSpec),
842
+ path.resolve(newSpec),
843
+ { policy: options.policy, version: options.currentVersion, name: options.name }
844
+ );
845
+ }
800
846
 
801
847
  if (options.json) {
802
848
  console.log(JSON.stringify(result, null, 2));
package/lib/api-engine.js CHANGED
@@ -153,4 +153,42 @@ function semver(oldSpec, newSpec, currentVersion) {
153
153
  ].join('\n'));
154
154
  }
155
155
 
156
- module.exports = { lint, diff, explain, semver, GATEWAY_ROOT };
156
+ /**
157
+ * delimit zero-spec — extract OpenAPI from framework source code
158
+ */
159
+ function zeroSpec(projectDir, opts = {}) {
160
+ const args = [];
161
+ if (opts.pythonBin) args.push(`python_bin=${pyStr(opts.pythonBin)}`);
162
+
163
+ return runGateway([
164
+ 'import json, sys',
165
+ 'sys.path.insert(0, ".")',
166
+ 'from core.zero_spec.detector import detect_framework, Framework',
167
+ 'from core.zero_spec.fastapi_extractor import extract_fastapi_spec',
168
+ 'from core.zero_spec.nestjs_extractor import extract_nestjs_spec',
169
+ 'from core.zero_spec.express_extractor import extract_express_spec',
170
+ `info = detect_framework(${pyStr(projectDir)})`,
171
+ 'r = {"framework": info.framework.value, "confidence": info.confidence, "message": info.message}',
172
+ 'if info.framework == Framework.FASTAPI:',
173
+ ` ext = extract_fastapi_spec(info, ${pyStr(projectDir)}${opts.pythonBin ? `, python_bin=${pyStr(opts.pythonBin)}` : ''})`,
174
+ ' r.update(ext)',
175
+ ' if ext.get("success") and info.app_locations:',
176
+ ' r["app_file"] = info.app_locations[0].file',
177
+ 'elif info.framework == Framework.NESTJS:',
178
+ ` ext = extract_nestjs_spec(info, ${pyStr(projectDir)})`,
179
+ ' r.update(ext)',
180
+ ' if ext.get("success") and info.app_locations:',
181
+ ' r["app_file"] = info.app_locations[0].file',
182
+ 'elif info.framework == Framework.EXPRESS:',
183
+ ` ext = extract_express_spec(info, ${pyStr(projectDir)})`,
184
+ ' r.update(ext)',
185
+ ' if ext.get("success") and info.app_locations:',
186
+ ' r["app_file"] = info.app_locations[0].file',
187
+ 'else:',
188
+ ' r["success"] = False',
189
+ ' r["error"] = "No supported API framework detected"',
190
+ 'print(json.dumps(r, default=str))',
191
+ ].join('\n'));
192
+ }
193
+
194
+ module.exports = { lint, diff, explain, semver, zeroSpec, GATEWAY_ROOT };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "delimit-cli",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "description": "ESLint for API contracts — detect breaking changes, enforce semver, and generate migration guides for OpenAPI specs",
5
5
  "main": "index.js",
6
6
  "bin": {