infernoflow 0.12.0 → 0.14.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/dist/bin/infernoflow.mjs +23 -0
- package/dist/lib/commands/dashboard.mjs +399 -0
- package/dist/lib/commands/prComment.mjs +361 -0
- package/dist/lib/commands/publish.mjs +25 -2
- package/dist/lib/commands/setup.mjs +1 -0
- package/dist/lib/commands/teamSync.mjs +388 -0
- package/dist/lib/commands/version.mjs +282 -0
- package/dist/templates/ci/github-pr-comment.yml +50 -0
- package/dist/templates/cursor/inferno-mcp-server.mjs +13 -0
- package/package.json +5 -4
|
@@ -0,0 +1,361 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* infernoflow pr-comment
|
|
3
|
+
*
|
|
4
|
+
* Posts a capability drift analysis as a GitHub PR comment.
|
|
5
|
+
* Designed to run in CI (GitHub Actions) on pull_request events.
|
|
6
|
+
*
|
|
7
|
+
* Auto-reads context from GitHub Actions environment variables:
|
|
8
|
+
* GITHUB_TOKEN — required for posting comments
|
|
9
|
+
* GITHUB_REPOSITORY — e.g. "owner/repo"
|
|
10
|
+
* GITHUB_EVENT_PATH — path to the event JSON (contains PR number)
|
|
11
|
+
* GITHUB_SHA — current commit SHA
|
|
12
|
+
* GITHUB_BASE_REF — base branch name (e.g. "main")
|
|
13
|
+
*
|
|
14
|
+
* Usage:
|
|
15
|
+
* infernoflow pr-comment # auto-detect from CI env
|
|
16
|
+
* infernoflow pr-comment --pr 42 # explicit PR number
|
|
17
|
+
* infernoflow pr-comment --repo owner/r # explicit repo
|
|
18
|
+
* infernoflow pr-comment --token ghp_... # explicit token
|
|
19
|
+
* infernoflow pr-comment --dry-run # print comment without posting
|
|
20
|
+
* infernoflow pr-comment --json # machine-readable result
|
|
21
|
+
*/
|
|
22
|
+
|
|
23
|
+
import * as fs from "node:fs";
|
|
24
|
+
import * as path from "node:path";
|
|
25
|
+
import * as https from "node:https";
|
|
26
|
+
import { execSync } from "node:child_process";
|
|
27
|
+
import { header, ok, warn, info, done, bold, cyan, gray, green, red, yellow } from "../ui/output.mjs";
|
|
28
|
+
|
|
29
|
+
// ── git helpers ───────────────────────────────────────────────────────────────
|
|
30
|
+
|
|
31
|
+
function capture(cmd, cwd) {
|
|
32
|
+
try {
|
|
33
|
+
return execSync(cmd, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
34
|
+
} catch { return null; }
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function lastTag(cwd) {
|
|
38
|
+
return capture("git describe --tags --abbrev=0", cwd) || null;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function fileAtRef(ref, relPath, cwd) {
|
|
42
|
+
return capture(`git show "${ref}:${relPath}"`, cwd);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
// ── capability helpers ────────────────────────────────────────────────────────
|
|
46
|
+
|
|
47
|
+
function parseCaps(jsonText) {
|
|
48
|
+
if (!jsonText) return null;
|
|
49
|
+
try {
|
|
50
|
+
const obj = JSON.parse(jsonText);
|
|
51
|
+
const raw = obj.capabilities || [];
|
|
52
|
+
return raw.map(c => {
|
|
53
|
+
if (typeof c === "string") return { id: c, title: c };
|
|
54
|
+
return { id: c.id || c, title: c.title || c.id || String(c), status: c.status };
|
|
55
|
+
});
|
|
56
|
+
} catch { return null; }
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
function loadCapsFromDisk(infernoDir) {
|
|
60
|
+
for (const name of ["capabilities.json", "contract.json"]) {
|
|
61
|
+
const p = path.join(infernoDir, name);
|
|
62
|
+
if (fs.existsSync(p)) return parseCaps(fs.readFileSync(p, "utf8"));
|
|
63
|
+
}
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function loadCapsAtRef(ref, cwd) {
|
|
68
|
+
for (const name of ["capabilities.json", "contract.json"]) {
|
|
69
|
+
const content = fileAtRef(ref, `inferno/${name}`, cwd);
|
|
70
|
+
if (content) return parseCaps(content);
|
|
71
|
+
}
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
function diffCaps(before, after) {
|
|
76
|
+
const beforeMap = new Map(before.map(c => [c.id, c]));
|
|
77
|
+
const afterMap = new Map(after.map(c => [c.id, c]));
|
|
78
|
+
const added = after.filter(c => !beforeMap.has(c.id));
|
|
79
|
+
const removed = before.filter(c => !afterMap.has(c.id));
|
|
80
|
+
const changed = [];
|
|
81
|
+
for (const c of after) {
|
|
82
|
+
const old = beforeMap.get(c.id);
|
|
83
|
+
if (!old) continue;
|
|
84
|
+
const changes = [];
|
|
85
|
+
if (old.title !== c.title) changes.push({ field: "title", from: old.title, to: c.title });
|
|
86
|
+
if ((old.status || "") !== (c.status || "")) changes.push({ field: "status", from: old.status || "—", to: c.status || "—" });
|
|
87
|
+
if (changes.length) changed.push({ id: c.id, changes });
|
|
88
|
+
}
|
|
89
|
+
return { added, removed, changed };
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
function classifyBump(diff) {
|
|
93
|
+
if (diff.removed.length > 0) return "major";
|
|
94
|
+
if (diff.added.length > 0) return "minor";
|
|
95
|
+
if (diff.changed.length > 0) return "patch";
|
|
96
|
+
return "none";
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// ── comment builder ───────────────────────────────────────────────────────────
|
|
100
|
+
|
|
101
|
+
function buildComment(diff, bump, ref, currentVersion, nextVersion) {
|
|
102
|
+
const lines = [];
|
|
103
|
+
|
|
104
|
+
// Header
|
|
105
|
+
const bumpEmoji = bump === "major" ? "🔴" : bump === "minor" ? "🟡" : bump === "patch" ? "🟢" : "✅";
|
|
106
|
+
const bumpLabel = bump === "none" ? "No capability changes" : `${bump.toUpperCase()} bump recommended`;
|
|
107
|
+
lines.push(`## 🔥 infernoflow — Capability Analysis`);
|
|
108
|
+
lines.push(``);
|
|
109
|
+
lines.push(`${bumpEmoji} **${bumpLabel}**${bump !== "none" ? ` · \`${currentVersion}\` → \`${nextVersion}\`` : ""}`);
|
|
110
|
+
lines.push(``);
|
|
111
|
+
|
|
112
|
+
// Summary table
|
|
113
|
+
const hasChanges = diff.added.length || diff.removed.length || diff.changed.length;
|
|
114
|
+
if (!hasChanges) {
|
|
115
|
+
lines.push(`> No capability changes detected since \`${ref}\`. Contract is in sync.`);
|
|
116
|
+
} else {
|
|
117
|
+
lines.push(`| Change | Count |`);
|
|
118
|
+
lines.push(`|--------|-------|`);
|
|
119
|
+
if (diff.added.length) lines.push(`| ➕ Added | ${diff.added.length} |`);
|
|
120
|
+
if (diff.removed.length) lines.push(`| ❌ Removed | ${diff.removed.length} |`);
|
|
121
|
+
if (diff.changed.length) lines.push(`| ✏️ Modified | ${diff.changed.length} |`);
|
|
122
|
+
lines.push(``);
|
|
123
|
+
|
|
124
|
+
// Detail sections
|
|
125
|
+
if (diff.added.length) {
|
|
126
|
+
lines.push(`<details><summary>➕ Added capabilities (${diff.added.length})</summary>`);
|
|
127
|
+
lines.push(``);
|
|
128
|
+
for (const c of diff.added) lines.push(`- \`${c.id}\` — ${c.title}`);
|
|
129
|
+
lines.push(``);
|
|
130
|
+
lines.push(`</details>`);
|
|
131
|
+
lines.push(``);
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (diff.removed.length) {
|
|
135
|
+
lines.push(`<details><summary>❌ Removed capabilities (${diff.removed.length}) — breaking change</summary>`);
|
|
136
|
+
lines.push(``);
|
|
137
|
+
for (const c of diff.removed) lines.push(`- \`${c.id}\` — ${c.title}`);
|
|
138
|
+
lines.push(``);
|
|
139
|
+
lines.push(`</details>`);
|
|
140
|
+
lines.push(``);
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (diff.changed.length) {
|
|
144
|
+
lines.push(`<details><summary>✏️ Modified capabilities (${diff.changed.length})</summary>`);
|
|
145
|
+
lines.push(``);
|
|
146
|
+
for (const item of diff.changed) {
|
|
147
|
+
lines.push(`- \`${item.id}\``);
|
|
148
|
+
for (const ch of item.changes) lines.push(` - ${ch.field}: \`${ch.from}\` → \`${ch.to}\``);
|
|
149
|
+
}
|
|
150
|
+
lines.push(``);
|
|
151
|
+
lines.push(`</details>`);
|
|
152
|
+
lines.push(``);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Bump recommendation
|
|
157
|
+
if (bump === "major") {
|
|
158
|
+
lines.push(`> ⚠️ **Breaking change detected.** Capabilities were removed. Consider a major version bump.`);
|
|
159
|
+
lines.push(`> Run \`infernoflow version --apply\` to update \`package.json\`.`);
|
|
160
|
+
} else if (bump === "minor") {
|
|
161
|
+
lines.push(`> ℹ️ New capabilities added. A minor version bump is recommended.`);
|
|
162
|
+
lines.push(`> Run \`infernoflow version --apply\` to update \`package.json\`.`);
|
|
163
|
+
}
|
|
164
|
+
|
|
165
|
+
lines.push(``);
|
|
166
|
+
lines.push(`---`);
|
|
167
|
+
lines.push(`<sub>Generated by [infernoflow](https://github.com/ronmiz/infernoflow) · compared against \`${ref}\`</sub>`);
|
|
168
|
+
|
|
169
|
+
return lines.join("\n");
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
// ── GitHub API ────────────────────────────────────────────────────────────────
|
|
173
|
+
|
|
174
|
+
function githubRequest(method, pathname, body, token) {
|
|
175
|
+
return new Promise((resolve, reject) => {
|
|
176
|
+
const data = body ? JSON.stringify(body) : null;
|
|
177
|
+
const req = https.request({
|
|
178
|
+
hostname: "api.github.com",
|
|
179
|
+
path: pathname,
|
|
180
|
+
method,
|
|
181
|
+
headers: {
|
|
182
|
+
"Authorization": `Bearer ${token}`,
|
|
183
|
+
"Accept": "application/vnd.github+json",
|
|
184
|
+
"Content-Type": "application/json",
|
|
185
|
+
"User-Agent": "infernoflow-cli",
|
|
186
|
+
...(data ? { "Content-Length": Buffer.byteLength(data) } : {}),
|
|
187
|
+
},
|
|
188
|
+
}, (res) => {
|
|
189
|
+
let raw = "";
|
|
190
|
+
res.on("data", chunk => raw += chunk);
|
|
191
|
+
res.on("end", () => {
|
|
192
|
+
try { resolve({ status: res.statusCode, body: JSON.parse(raw) }); }
|
|
193
|
+
catch { resolve({ status: res.statusCode, body: raw }); }
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
req.on("error", reject);
|
|
197
|
+
if (data) req.write(data);
|
|
198
|
+
req.end();
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
async function findExistingComment(repo, prNumber, token) {
|
|
203
|
+
// Look for a previous infernoflow comment to update instead of creating a new one
|
|
204
|
+
const res = await githubRequest("GET", `/repos/${repo}/issues/${prNumber}/comments?per_page=100`, null, token);
|
|
205
|
+
if (res.status !== 200 || !Array.isArray(res.body)) return null;
|
|
206
|
+
return res.body.find(c => c.body && c.body.includes("🔥 infernoflow — Capability Analysis")) || null;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
async function postComment(repo, prNumber, body, token) {
|
|
210
|
+
// Update existing comment if found (avoids spam on multiple pushes)
|
|
211
|
+
const existing = await findExistingComment(repo, prNumber, token);
|
|
212
|
+
if (existing) {
|
|
213
|
+
return githubRequest("PATCH", `/repos/${repo}/issues/comments/${existing.id}`, { body }, token);
|
|
214
|
+
}
|
|
215
|
+
return githubRequest("POST", `/repos/${repo}/issues/${prNumber}/comments`, { body }, token);
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
// ── env helpers ───────────────────────────────────────────────────────────────
|
|
219
|
+
|
|
220
|
+
function readGithubEventPr() {
|
|
221
|
+
const eventPath = process.env.GITHUB_EVENT_PATH;
|
|
222
|
+
if (!eventPath || !fs.existsSync(eventPath)) return null;
|
|
223
|
+
try {
|
|
224
|
+
const event = JSON.parse(fs.readFileSync(eventPath, "utf8"));
|
|
225
|
+
return event.pull_request?.number || event.number || null;
|
|
226
|
+
} catch { return null; }
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function applyBump(version, type) {
|
|
230
|
+
const parts = (version || "0.0.0").split(".").map(Number);
|
|
231
|
+
if (type === "major") { parts[0]++; parts[1] = 0; parts[2] = 0; }
|
|
232
|
+
else if (type === "minor") { parts[1]++; parts[2] = 0; }
|
|
233
|
+
else if (type === "patch") { parts[2]++; }
|
|
234
|
+
return parts.join(".");
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
function readPackageVersion(cwd) {
|
|
238
|
+
const p = path.join(cwd, "package.json");
|
|
239
|
+
if (!fs.existsSync(p)) return "0.0.0";
|
|
240
|
+
try { return JSON.parse(fs.readFileSync(p, "utf8")).version || "0.0.0"; } catch { return "0.0.0"; }
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
// ── main ──────────────────────────────────────────────────────────────────────
|
|
244
|
+
|
|
245
|
+
export async function prCommentCommand(rawArgs) {
|
|
246
|
+
const args = rawArgs.slice(1);
|
|
247
|
+
const dryRun = args.includes("--dry-run");
|
|
248
|
+
const asJson = args.includes("--json");
|
|
249
|
+
|
|
250
|
+
const prIdx = args.indexOf("--pr");
|
|
251
|
+
const repoIdx = args.indexOf("--repo");
|
|
252
|
+
const tokenIdx = args.indexOf("--token");
|
|
253
|
+
const refIdx = args.indexOf("--ref");
|
|
254
|
+
|
|
255
|
+
const cwd = process.cwd();
|
|
256
|
+
const infernoDir = path.join(cwd, "inferno");
|
|
257
|
+
|
|
258
|
+
if (!asJson) header("infernoflow pr-comment");
|
|
259
|
+
|
|
260
|
+
// ── Resolve inputs ────────────────────────────────────────────────────────
|
|
261
|
+
const token = tokenIdx !== -1 ? args[tokenIdx + 1] : process.env.GITHUB_TOKEN;
|
|
262
|
+
const repo = repoIdx !== -1 ? args[repoIdx + 1] : process.env.GITHUB_REPOSITORY;
|
|
263
|
+
const prNumber = prIdx !== -1 ? parseInt(args[prIdx + 1], 10)
|
|
264
|
+
: readGithubEventPr();
|
|
265
|
+
|
|
266
|
+
let ref = refIdx !== -1 ? args[refIdx + 1] : null;
|
|
267
|
+
if (!ref) ref = process.env.GITHUB_BASE_REF ? `origin/${process.env.GITHUB_BASE_REF}` : null;
|
|
268
|
+
if (!ref) ref = lastTag(cwd);
|
|
269
|
+
if (!ref) {
|
|
270
|
+
const parentExists = capture("git rev-parse HEAD~1", cwd);
|
|
271
|
+
ref = parentExists ? "HEAD~1" : null;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
// ── Validate ──────────────────────────────────────────────────────────────
|
|
275
|
+
if (!fs.existsSync(infernoDir)) {
|
|
276
|
+
const msg = "inferno/ not found — run: infernoflow init";
|
|
277
|
+
if (asJson) { console.log(JSON.stringify({ ok: false, error: msg })); process.exit(1); }
|
|
278
|
+
warn(msg); process.exit(1);
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
if (!dryRun && !token) {
|
|
282
|
+
const msg = "No GitHub token found. Set GITHUB_TOKEN env var or use --token";
|
|
283
|
+
if (asJson) { console.log(JSON.stringify({ ok: false, error: msg })); process.exit(1); }
|
|
284
|
+
warn(msg); process.exit(1);
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
if (!dryRun && !repo) {
|
|
288
|
+
const msg = "No repository found. Set GITHUB_REPOSITORY env var or use --repo owner/repo";
|
|
289
|
+
if (asJson) { console.log(JSON.stringify({ ok: false, error: msg })); process.exit(1); }
|
|
290
|
+
warn(msg); process.exit(1);
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
if (!dryRun && !prNumber) {
|
|
294
|
+
const msg = "No PR number found. Use --pr <number> or run in GitHub Actions on pull_request event";
|
|
295
|
+
if (asJson) { console.log(JSON.stringify({ ok: false, error: msg })); process.exit(1); }
|
|
296
|
+
warn(msg); process.exit(1);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
// ── Load capabilities and compute diff ───────────────────────────────────
|
|
300
|
+
const current = loadCapsFromDisk(infernoDir);
|
|
301
|
+
const previous = ref ? loadCapsAtRef(ref, cwd) : null;
|
|
302
|
+
|
|
303
|
+
if (!current) {
|
|
304
|
+
const msg = "No capabilities.json or contract.json found in inferno/";
|
|
305
|
+
if (asJson) { console.log(JSON.stringify({ ok: false, error: msg })); process.exit(1); }
|
|
306
|
+
warn(msg); process.exit(1);
|
|
307
|
+
}
|
|
308
|
+
|
|
309
|
+
const diff = diffCaps(previous || [], current);
|
|
310
|
+
const bump = classifyBump(diff);
|
|
311
|
+
const currentVersion = readPackageVersion(cwd);
|
|
312
|
+
const nextVersion = bump !== "none" ? applyBump(currentVersion, bump) : currentVersion;
|
|
313
|
+
|
|
314
|
+
// ── Build comment ─────────────────────────────────────────────────────────
|
|
315
|
+
const commentBody = buildComment(diff, bump, ref || "HEAD", currentVersion, nextVersion);
|
|
316
|
+
|
|
317
|
+
// ── Dry run ───────────────────────────────────────────────────────────────
|
|
318
|
+
if (dryRun) {
|
|
319
|
+
if (asJson) {
|
|
320
|
+
console.log(JSON.stringify({ ok: true, dryRun: true, bump, currentVersion, nextVersion, comment: commentBody }));
|
|
321
|
+
} else {
|
|
322
|
+
console.log();
|
|
323
|
+
info("DRY RUN — comment that would be posted:");
|
|
324
|
+
console.log();
|
|
325
|
+
console.log(commentBody);
|
|
326
|
+
console.log();
|
|
327
|
+
}
|
|
328
|
+
return;
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
// ── Post comment ──────────────────────────────────────────────────────────
|
|
332
|
+
if (!asJson) info(`Posting to ${bold(repo)} PR #${prNumber}...`);
|
|
333
|
+
|
|
334
|
+
try {
|
|
335
|
+
const result = await postComment(repo, prNumber, commentBody, token);
|
|
336
|
+
|
|
337
|
+
if (result.status === 200 || result.status === 201) {
|
|
338
|
+
const commentUrl = result.body?.html_url || "";
|
|
339
|
+
if (asJson) {
|
|
340
|
+
console.log(JSON.stringify({ ok: true, bump, currentVersion, nextVersion, prNumber, repo, commentUrl }));
|
|
341
|
+
} else {
|
|
342
|
+
ok(`Comment posted → ${cyan(commentUrl || `PR #${prNumber}`)}`);
|
|
343
|
+
if (bump !== "none") {
|
|
344
|
+
console.log();
|
|
345
|
+
info(`Recommended bump: ${bold(bump.toUpperCase())} ${currentVersion} → ${nextVersion}`);
|
|
346
|
+
}
|
|
347
|
+
console.log();
|
|
348
|
+
}
|
|
349
|
+
} else {
|
|
350
|
+
const msg = `GitHub API error ${result.status}: ${JSON.stringify(result.body)}`;
|
|
351
|
+
if (asJson) { console.log(JSON.stringify({ ok: false, error: msg })); }
|
|
352
|
+
else { warn(msg); }
|
|
353
|
+
process.exit(1);
|
|
354
|
+
}
|
|
355
|
+
} catch (err) {
|
|
356
|
+
const msg = `Failed to post comment: ${err.message}`;
|
|
357
|
+
if (asJson) { console.log(JSON.stringify({ ok: false, error: msg })); }
|
|
358
|
+
else { warn(msg); }
|
|
359
|
+
process.exit(1);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
@@ -119,9 +119,9 @@ export async function publishCommand(rawArgs) {
|
|
|
119
119
|
const yes = args.includes("--yes") || args.includes("-y");
|
|
120
120
|
|
|
121
121
|
const bumpIdx = args.indexOf("--bump");
|
|
122
|
-
|
|
122
|
+
let bumpType = bumpIdx !== -1 ? (args[bumpIdx + 1] || "patch") : null;
|
|
123
123
|
|
|
124
|
-
if (!["patch", "minor", "major"].includes(bumpType)) {
|
|
124
|
+
if (bumpType && !["patch", "minor", "major"].includes(bumpType)) {
|
|
125
125
|
console.error(` Invalid --bump value: ${bumpType}. Must be patch, minor, or major.`);
|
|
126
126
|
process.exit(1);
|
|
127
127
|
}
|
|
@@ -136,6 +136,29 @@ export async function publishCommand(rawArgs) {
|
|
|
136
136
|
const pkgPath = path.join(PKG_ROOT, "package.json");
|
|
137
137
|
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
138
138
|
const oldVersion = pkg.version;
|
|
139
|
+
|
|
140
|
+
// ── Auto-detect bump type from capability diff if not specified ───────────
|
|
141
|
+
if (!bumpType) {
|
|
142
|
+
try {
|
|
143
|
+
const { versionCommand: _vc, ...versionModule } = await import("./version.mjs");
|
|
144
|
+
// Use the JSON output to get the recommendation
|
|
145
|
+
const { execSync: _exec } = await import("node:child_process");
|
|
146
|
+
const result = _exec("node " + JSON.stringify(path.join(PKG_ROOT, "bin", "infernoflow.mjs")) + " version --json", {
|
|
147
|
+
cwd: PKG_ROOT, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"],
|
|
148
|
+
});
|
|
149
|
+
const parsed = JSON.parse(result);
|
|
150
|
+
if (parsed.bump && parsed.bump !== "none") {
|
|
151
|
+
bumpType = parsed.bump;
|
|
152
|
+
info(`Auto-detected bump type: ${bold(cyan(bumpType))} (from capability diff)`);
|
|
153
|
+
} else {
|
|
154
|
+
bumpType = "patch";
|
|
155
|
+
info(`No capability changes detected — defaulting to ${bold("patch")}`);
|
|
156
|
+
}
|
|
157
|
+
} catch {
|
|
158
|
+
bumpType = "patch";
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
|
|
139
162
|
const newVersion = bumpVersion(oldVersion, bumpType);
|
|
140
163
|
|
|
141
164
|
console.log();
|