delimit-cli 4.7.2 → 4.7.4

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.
@@ -100,89 +100,117 @@ class CIFormatter:
100
100
  decision = result.get("decision", "unknown")
101
101
  violations = result.get("violations", [])
102
102
  summary = result.get("summary", {})
103
- semver = result.get("semver") # optional dict from semver_classifier
103
+ semver = result.get("semver")
104
+ all_changes = result.get("all_changes", [])
105
+ migration = result.get("migration")
104
106
 
105
- # Header include semver badge when available
106
- bump_badge = ""
107
- if semver:
108
- bump = semver.get("bump", "unknown")
109
- bump_badge = {"major": " `MAJOR`", "minor": " `MINOR`", "patch": " `PATCH`", "none": ""}.get(bump, "")
107
+ bc = summary.get("breaking_changes", 0)
108
+ total = summary.get("total_changes", 0)
109
+ additive = total - bc
110
110
 
111
- if decision == "fail":
112
- lines.append(f"## 🚨 Delimit: Breaking Changes{bump_badge}\n")
113
- elif decision == "warn":
114
- lines.append(f"## ⚠️ Delimit: Potential Issues{bump_badge}\n")
111
+ errors = [v for v in violations if v.get("severity") == "error"]
112
+ warnings = [v for v in violations if v.get("severity") == "warning"]
113
+
114
+ if bc == 0:
115
+ # ── GREEN PATH ──
116
+ bump_label = "NONE"
117
+ if semver:
118
+ bump_label = semver.get("bump", "none").upper()
119
+ lines.append("\U0001f6e1\ufe0f **Governance Passed**\n")
120
+ if total > 0:
121
+ lines.append(
122
+ f"> **No breaking API changes detected.** "
123
+ f"{additive} additive change{'s' if additive != 1 else ''} "
124
+ f"found \u2014 Semver: **{bump_label}**\n"
125
+ )
126
+ else:
127
+ lines.append("> **No breaking API changes detected.**\n")
128
+
129
+ # Additive changes
130
+ safe_changes = [c for c in all_changes if not c.get("is_breaking")]
131
+ if safe_changes and len(safe_changes) <= 15:
132
+ lines.append("<details>")
133
+ lines.append(f"<summary>\u2705 New additions ({len(safe_changes)})</summary>\n")
134
+ for c in safe_changes:
135
+ lines.append(f"- `{c.get('path', '')}` \u2014 {c.get('message', '')}")
136
+ lines.append("</details>\n")
115
137
  else:
116
- lines.append(f"## API Changes Look Good{bump_badge}\n")
138
+ # ── RED PATH ──
139
+ lines.append("\U0001f6e1\ufe0f **Breaking API Changes Detected**\n")
117
140
 
118
- # Semver + summary table
119
- lines.append("| Metric | Value |")
120
- lines.append("|--------|-------|")
121
- if semver:
122
- lines.append(f"| Semver bump | `{semver.get('bump', 'unknown')}` |")
123
- if semver.get("next_version"):
124
- lines.append(f"| Next version | `{semver['next_version']}` |")
125
- lines.append(f"| Total changes | {summary.get('total_changes', 0)} |")
126
- lines.append(f"| Breaking | {summary.get('breaking_changes', 0)} |")
127
- if summary.get("violations", 0) > 0:
128
- lines.append(f"| Policy violations | {summary['violations']} |")
129
- lines.append("")
141
+ # Summary card
142
+ parts = [f"\U0001f534 **{bc} breaking change{'s' if bc != 1 else ''}**"]
143
+ parts.append("Semver: **MAJOR**")
144
+ if semver and semver.get("next_version"):
145
+ parts.append(f"Next: `{semver['next_version']}`")
146
+ separator = " \u00b7 "
147
+ lines.append(f"> {separator.join(parts)}\n")
130
148
 
131
- # Violations table
132
- if violations:
133
- errors = [v for v in violations if v.get("severity") == "error"]
134
- warnings = [v for v in violations if v.get("severity") == "warning"]
149
+ # Stats table
150
+ lines.append("| | Count |")
151
+ lines.append("|---|---|")
152
+ lines.append(f"| Total changes | {total} |")
153
+ lines.append(f"| Breaking | {bc} |")
154
+ lines.append(f"| Additive | {additive} |")
155
+ if len(warnings) > 0:
156
+ lines.append(f"| Warnings | {len(warnings)} |")
157
+ if summary.get("violations", 0) > 0:
158
+ lines.append(f"| Policy violations | {summary['violations']} |")
159
+ lines.append("")
135
160
 
161
+ # Violations table
136
162
  if errors or warnings:
137
- lines.append("### Violations\n")
138
- lines.append("| Severity | Rule | Description | Location |")
139
- lines.append("|----------|------|-------------|----------|")
163
+ lines.append("### Breaking Changes\n")
164
+ lines.append("| Severity | Change | Location |")
165
+ lines.append("|----------|--------|----------|")
140
166
 
141
167
  for v in errors:
142
- rule = v.get("name", v.get("rule", "Unknown"))
143
168
  desc = v.get("message", "Unknown violation")
144
169
  location = v.get("path", "-")
145
- lines.append(f"| 🔴 **Error** | {rule} | {desc} | `{location}` |")
170
+ lines.append(f"| \U0001f534 Critical | {desc} | `{location}` |")
146
171
 
147
172
  for v in warnings:
148
- rule = v.get("name", v.get("rule", "Unknown"))
149
173
  desc = v.get("message", "Unknown warning")
150
174
  location = v.get("path", "-")
151
- lines.append(f"| 🟡 Warning | {rule} | {desc} | `{location}` |")
175
+ lines.append(f"| \U0001f7e1 Warning | {desc} | `{location}` |")
152
176
 
153
177
  lines.append("")
154
178
 
155
- # Detailed changes
156
- all_changes = result.get("all_changes", [])
157
- if all_changes and len(all_changes) <= 10:
158
- lines.append("<details>")
159
- lines.append("<summary>All changes</summary>\n")
160
- lines.append("```")
161
- for change in all_changes:
162
- breaking = "BREAKING" if change.get("is_breaking") else "safe"
163
- lines.append(f"[{breaking}] {change.get('message', 'Unknown change')}")
164
- lines.append("```")
165
- lines.append("</details>\n")
179
+ # Migration guidance
180
+ if migration and decision == "fail":
181
+ lines.append("<details>")
182
+ lines.append("<summary>\U0001f4cb Migration guide</summary>\n")
183
+ lines.append(migration)
184
+ lines.append("\n</details>\n")
185
+ elif errors and decision == "fail":
186
+ lines.append("<details>")
187
+ lines.append("<summary>\U0001f4cb Migration guide</summary>\n")
188
+ lines.append("1. **Restore removed endpoints** \u2014 deprecate before removing")
189
+ lines.append("2. **Make parameters optional** \u2014 don't add required params")
190
+ lines.append("3. **Use versioning** \u2014 create `/v2/` for breaking changes")
191
+ lines.append("4. **Gradual migration** \u2014 provide guides and time")
192
+ lines.append("\n</details>\n")
166
193
 
167
- # Migration guidance (from explainer) when available
168
- migration = result.get("migration")
169
- if migration and decision == "fail":
170
- lines.append("<details>")
171
- lines.append("<summary>Migration guide</summary>\n")
172
- lines.append(migration)
173
- lines.append("\n</details>\n")
194
+ # Additive changes
195
+ safe_changes = [c for c in all_changes if not c.get("is_breaking")]
196
+ if safe_changes and len(safe_changes) <= 15:
197
+ lines.append("<details>")
198
+ lines.append(f"<summary>\u2705 New additions ({len(safe_changes)})</summary>\n")
199
+ for c in safe_changes:
200
+ lines.append(f"- `{c.get('path', '')}` \u2014 {c.get('message', '')}")
201
+ lines.append("</details>\n")
174
202
 
175
- # Remediation
176
- if violations and decision == "fail" and not migration:
177
- lines.append("### 💡 How to Fix\n")
178
- lines.append("1. **Restore removed endpoints** — deprecate before removing")
179
- lines.append("2. **Make parameters optional** — don't add required params")
180
- lines.append("3. **Use versioning** — create `/v2/` for breaking changes")
181
- lines.append("4. **Gradual migration** — provide guides and time")
182
- lines.append("")
203
+ lines.append("> **Fix locally:** `npx delimit-cli lint`\n")
183
204
 
184
205
  lines.append("---")
185
- lines.append("*Generated by [Delimit](https://github.com/delimit-ai/delimit) — ESLint for API contracts*")
206
+ lines.append(
207
+ "Powered by [Delimit](https://delimit.ai) \u00b7 "
208
+ "[Docs](https://delimit.ai/docs) \u00b7 "
209
+ "[Install](https://github.com/marketplace/actions/delimit-api-governance)"
210
+ )
211
+
212
+ if bc == 0:
213
+ lines.append("\nKeep Building.")
186
214
 
187
215
  return "\n".join(lines)
188
216