infernoflow 0.32.8 → 0.32.9
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 +84 -255
- package/dist/lib/adopters/angular.mjs +1 -128
- package/dist/lib/adopters/css.mjs +1 -111
- package/dist/lib/adopters/react.mjs +1 -104
- package/dist/lib/ai/ideDetection.mjs +1 -31
- package/dist/lib/ai/localProvider.mjs +1 -88
- package/dist/lib/ai/providerRouter.mjs +2 -295
- package/dist/lib/commands/adopt.mjs +20 -869
- package/dist/lib/commands/adoptWizard.mjs +9 -320
- package/dist/lib/commands/agent.mjs +5 -191
- package/dist/lib/commands/ai.mjs +2 -407
- package/dist/lib/commands/audit.mjs +13 -300
- package/dist/lib/commands/changelog.mjs +26 -594
- package/dist/lib/commands/check.mjs +3 -184
- package/dist/lib/commands/ci.mjs +3 -208
- package/dist/lib/commands/claudeMd.mjs +25 -130
- package/dist/lib/commands/cloud.mjs +5 -521
- package/dist/lib/commands/context.mjs +31 -287
- package/dist/lib/commands/coverage.mjs +2 -282
- package/dist/lib/commands/dashboard.mjs +123 -635
- package/dist/lib/commands/demo.mjs +8 -465
- package/dist/lib/commands/diff.mjs +5 -274
- package/dist/lib/commands/docGate.mjs +2 -81
- package/dist/lib/commands/doctor.mjs +3 -321
- package/dist/lib/commands/explain.mjs +8 -438
- package/dist/lib/commands/export.mjs +10 -239
- package/dist/lib/commands/generateSkills.mjs +38 -163
- package/dist/lib/commands/graph.mjs +203 -321
- package/dist/lib/commands/health.mjs +2 -309
- package/dist/lib/commands/impact.mjs +2 -325
- package/dist/lib/commands/implement.mjs +7 -103
- package/dist/lib/commands/init.mjs +23 -475
- package/dist/lib/commands/installCursorHooks.mjs +1 -36
- package/dist/lib/commands/installVsCodeCopilotHooks.mjs +1 -37
- package/dist/lib/commands/link.mjs +2 -342
- package/dist/lib/commands/monorepo.mjs +4 -428
- package/dist/lib/commands/notify.mjs +4 -258
- package/dist/lib/commands/onboard.mjs +4 -296
- package/dist/lib/commands/prComment.mjs +2 -361
- package/dist/lib/commands/prImpact.mjs +2 -157
- package/dist/lib/commands/publish.mjs +15 -316
- package/dist/lib/commands/report.mjs +28 -272
- package/dist/lib/commands/review.mjs +9 -223
- package/dist/lib/commands/run.mjs +8 -336
- package/dist/lib/commands/scaffold.mjs +54 -419
- package/dist/lib/commands/scan.mjs +5 -558
- package/dist/lib/commands/scout.mjs +2 -291
- package/dist/lib/commands/setup.mjs +5 -310
- package/dist/lib/commands/share.mjs +13 -196
- package/dist/lib/commands/snapshot.mjs +3 -383
- package/dist/lib/commands/stability.mjs +2 -293
- package/dist/lib/commands/status.mjs +4 -172
- package/dist/lib/commands/suggest.mjs +21 -563
- package/dist/lib/commands/syncAuto.mjs +1 -96
- package/dist/lib/commands/synthesize.mjs +10 -228
- package/dist/lib/commands/teamSync.mjs +2 -388
- package/dist/lib/commands/test.mjs +6 -363
- package/dist/lib/commands/version.mjs +2 -282
- package/dist/lib/commands/vibe.mjs +7 -357
- package/dist/lib/commands/watch.mjs +4 -203
- package/dist/lib/commands/why.mjs +4 -358
- package/dist/lib/cursorHooksInstall.mjs +1 -60
- package/dist/lib/draftToolingInstall.mjs +7 -68
- package/dist/lib/git/detect-drift.mjs +4 -208
- package/dist/lib/learning/adapt.mjs +6 -101
- package/dist/lib/learning/observe.mjs +1 -119
- package/dist/lib/learning/patternDetector.mjs +1 -298
- package/dist/lib/learning/profile.mjs +2 -279
- package/dist/lib/learning/skillSynthesizer.mjs +24 -145
- package/dist/lib/templates/index.mjs +1 -131
- package/dist/lib/ui/errors.mjs +1 -142
- package/dist/lib/ui/output.mjs +6 -72
- package/dist/lib/ui/prompts.mjs +6 -147
- package/dist/lib/vsCodeCopilotHooksInstall.mjs +1 -42
- package/dist/templates/cursor/inferno-mcp-server.mjs +29 -0
- package/dist/templates/github-app/GITHUB_APP.md +67 -0
- package/dist/templates/github-app/app-manifest.json +20 -0
- package/package.json +1 -1
|
@@ -1,274 +1,5 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
*
|
|
7
|
-
* Usage:
|
|
8
|
-
* infernoflow diff # vs last tag
|
|
9
|
-
* infernoflow diff --ref v0.10.18 # vs specific tag / commit
|
|
10
|
-
* infernoflow diff --ref HEAD~5 # vs 5 commits ago
|
|
11
|
-
* infernoflow diff --json # machine-readable output
|
|
12
|
-
* infernoflow diff --summary # one-liner count only
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import * as fs from "node:fs";
|
|
16
|
-
import * as path from "node:path";
|
|
17
|
-
import { execSync } from "node:child_process";
|
|
18
|
-
import { header, ok, fail, warn, info, bold, cyan, gray, green, red, yellow } from "../ui/output.mjs";
|
|
19
|
-
|
|
20
|
-
// ── git helpers ──────────────────────────────────────────────────────────────
|
|
21
|
-
|
|
22
|
-
function capture(cmd, cwd) {
|
|
23
|
-
try {
|
|
24
|
-
return execSync(cmd, { cwd, encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] }).trim();
|
|
25
|
-
} catch {
|
|
26
|
-
return null;
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function lastTag(cwd) {
|
|
31
|
-
// Try to find the most recent reachable tag
|
|
32
|
-
const tag = capture("git describe --tags --abbrev=0", cwd);
|
|
33
|
-
if (tag) return tag;
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function fileAtRef(ref, relPath, cwd) {
|
|
38
|
-
// Returns file content at a git ref, or null if not found
|
|
39
|
-
const content = capture(`git show "${ref}:${relPath}"`, cwd);
|
|
40
|
-
return content;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
function currentBranch(cwd) {
|
|
44
|
-
return capture("git rev-parse --abbrev-ref HEAD", cwd) || "HEAD";
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
function refDescription(cwd, ref) {
|
|
48
|
-
// Human label: "v0.10.18 (2026-04-10)" or "HEAD~1"
|
|
49
|
-
const date = capture(`git log -1 --format=%ci "${ref}"`, cwd);
|
|
50
|
-
const short = date ? date.slice(0, 10) : null;
|
|
51
|
-
return short ? `${ref} ${gray("(" + short + ")")}` : ref;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// ── capability helpers ───────────────────────────────────────────────────────
|
|
55
|
-
|
|
56
|
-
function parseCaps(jsonText) {
|
|
57
|
-
// Accepts capabilities.json OR contract.json — both have a "capabilities" array
|
|
58
|
-
if (!jsonText) return null;
|
|
59
|
-
try {
|
|
60
|
-
const obj = JSON.parse(jsonText);
|
|
61
|
-
const raw = obj.capabilities || [];
|
|
62
|
-
// capabilities.json: array of objects { id, title, ... }
|
|
63
|
-
// contract.json: array of id strings
|
|
64
|
-
return raw.map(c => {
|
|
65
|
-
if (typeof c === "string") return { id: c, title: c };
|
|
66
|
-
return { id: c.id || c, title: c.title || c.id || String(c), since: c.since, status: c.status };
|
|
67
|
-
});
|
|
68
|
-
} catch {
|
|
69
|
-
return null;
|
|
70
|
-
}
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
function loadCapsAtRef(ref, infernoRelDir, cwd) {
|
|
74
|
-
// Try capabilities.json first, fall back to contract.json
|
|
75
|
-
const capsJson = fileAtRef(ref, `${infernoRelDir}/capabilities.json`, cwd);
|
|
76
|
-
if (capsJson) return parseCaps(capsJson);
|
|
77
|
-
const contractJson = fileAtRef(ref, `${infernoRelDir}/contract.json`, cwd);
|
|
78
|
-
return parseCaps(contractJson);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
function loadCapsFromDisk(infernoDir) {
|
|
82
|
-
const capsPath = path.join(infernoDir, "capabilities.json");
|
|
83
|
-
const contractPath = path.join(infernoDir, "contract.json");
|
|
84
|
-
if (fs.existsSync(capsPath)) {
|
|
85
|
-
return parseCaps(fs.readFileSync(capsPath, "utf8"));
|
|
86
|
-
}
|
|
87
|
-
if (fs.existsSync(contractPath)) {
|
|
88
|
-
return parseCaps(fs.readFileSync(contractPath, "utf8"));
|
|
89
|
-
}
|
|
90
|
-
return null;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// ── diff logic ───────────────────────────────────────────────────────────────
|
|
94
|
-
|
|
95
|
-
function diffCaps(before, after) {
|
|
96
|
-
const beforeMap = new Map(before.map(c => [c.id, c]));
|
|
97
|
-
const afterMap = new Map(after.map(c => [c.id, c]));
|
|
98
|
-
|
|
99
|
-
const added = after.filter(c => !beforeMap.has(c.id));
|
|
100
|
-
const removed = before.filter(c => !afterMap.has(c.id));
|
|
101
|
-
|
|
102
|
-
const changed = [];
|
|
103
|
-
for (const c of after) {
|
|
104
|
-
const old = beforeMap.get(c.id);
|
|
105
|
-
if (!old) continue;
|
|
106
|
-
const changes = [];
|
|
107
|
-
if (old.title !== c.title) changes.push({ field: "title", from: old.title, to: c.title });
|
|
108
|
-
if (old.status !== c.status && (old.status || c.status)) changes.push({ field: "status", from: old.status || "—", to: c.status || "—" });
|
|
109
|
-
if (changes.length) changed.push({ id: c.id, changes });
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
return { added, removed, changed };
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
// ── rendering ────────────────────────────────────────────────────────────────
|
|
116
|
-
|
|
117
|
-
function renderAdded(caps) {
|
|
118
|
-
if (!caps.length) return;
|
|
119
|
-
console.log(`\n ${bold(green("+ Added"))} ${gray("(" + caps.length + ")")}`);
|
|
120
|
-
for (const c of caps) {
|
|
121
|
-
const since = c.since ? gray(" since " + c.since) : "";
|
|
122
|
-
console.log(` ${green("+")} ${bold(c.id)} ${gray(c.title)}${since}`);
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function renderRemoved(caps) {
|
|
127
|
-
if (!caps.length) return;
|
|
128
|
-
console.log(`\n ${bold(red("- Removed"))} ${gray("(" + caps.length + ")")}`);
|
|
129
|
-
for (const c of caps) {
|
|
130
|
-
console.log(` ${red("-")} ${bold(c.id)} ${gray(c.title)}`);
|
|
131
|
-
}
|
|
132
|
-
}
|
|
133
|
-
|
|
134
|
-
function renderChanged(items) {
|
|
135
|
-
if (!items.length) return;
|
|
136
|
-
console.log(`\n ${bold(yellow("~ Changed"))} ${gray("(" + items.length + ")")}`);
|
|
137
|
-
for (const item of items) {
|
|
138
|
-
console.log(` ${yellow("~")} ${bold(item.id)}`);
|
|
139
|
-
for (const ch of item.changes) {
|
|
140
|
-
console.log(` ${gray(ch.field + ":")} ${red(ch.from)} → ${green(ch.to)}`);
|
|
141
|
-
}
|
|
142
|
-
}
|
|
143
|
-
}
|
|
144
|
-
|
|
145
|
-
function renderUnchanged(count) {
|
|
146
|
-
if (count === 0) return;
|
|
147
|
-
console.log(`\n ${gray(" Unchanged " + count)}`);
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
function renderSummaryLine(result, refLabel) {
|
|
151
|
-
const parts = [];
|
|
152
|
-
if (result.added.length) parts.push(green("+" + result.added.length + " added"));
|
|
153
|
-
if (result.removed.length) parts.push(red("-" + result.removed.length + " removed"));
|
|
154
|
-
if (result.changed.length) parts.push(yellow("~" + result.changed.length + " changed"));
|
|
155
|
-
if (!parts.length) parts.push(gray("no changes"));
|
|
156
|
-
console.log(` ${parts.join(" ")} ${gray("vs " + refLabel)}`);
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
// ── main ─────────────────────────────────────────────────────────────────────
|
|
160
|
-
|
|
161
|
-
export async function diffCommand(rawArgs) {
|
|
162
|
-
const args = rawArgs.slice(1);
|
|
163
|
-
|
|
164
|
-
const asJson = args.includes("--json");
|
|
165
|
-
const summary = args.includes("--summary");
|
|
166
|
-
|
|
167
|
-
const refIdx = args.indexOf("--ref");
|
|
168
|
-
let ref = refIdx !== -1 ? args[refIdx + 1] : null;
|
|
169
|
-
|
|
170
|
-
const cwd = process.cwd();
|
|
171
|
-
const infernoDir = path.join(cwd, "inferno");
|
|
172
|
-
const infernoRelDir = "inferno"; // relative for git show
|
|
173
|
-
|
|
174
|
-
if (!asJson) header("diff");
|
|
175
|
-
|
|
176
|
-
// ── Validate inferno/ exists ─────────────────────────────────────────────
|
|
177
|
-
if (!fs.existsSync(infernoDir)) {
|
|
178
|
-
if (asJson) {
|
|
179
|
-
console.log(JSON.stringify({ ok: false, error: "inferno_not_found" }));
|
|
180
|
-
process.exit(1);
|
|
181
|
-
}
|
|
182
|
-
fail("inferno/ not found", "Run: infernoflow init");
|
|
183
|
-
process.exit(1);
|
|
184
|
-
}
|
|
185
|
-
|
|
186
|
-
// ── Resolve ref ──────────────────────────────────────────────────────────
|
|
187
|
-
if (!ref) {
|
|
188
|
-
ref = lastTag(cwd);
|
|
189
|
-
if (!ref) {
|
|
190
|
-
// No tags — fall back to HEAD~1
|
|
191
|
-
const parentExists = capture("git rev-parse HEAD~1", cwd);
|
|
192
|
-
if (parentExists) {
|
|
193
|
-
ref = "HEAD~1";
|
|
194
|
-
} else {
|
|
195
|
-
if (asJson) {
|
|
196
|
-
console.log(JSON.stringify({ ok: false, error: "no_ref", hint: "No git tags found and no parent commit. Use --ref <commit>" }));
|
|
197
|
-
process.exit(1);
|
|
198
|
-
}
|
|
199
|
-
fail("No git tags found", "Create a tag first: git tag v0.1.0 or use --ref <commit>");
|
|
200
|
-
process.exit(1);
|
|
201
|
-
}
|
|
202
|
-
}
|
|
203
|
-
}
|
|
204
|
-
|
|
205
|
-
// ── Load capabilities ────────────────────────────────────────────────────
|
|
206
|
-
const current = loadCapsFromDisk(infernoDir);
|
|
207
|
-
if (!current) {
|
|
208
|
-
if (asJson) {
|
|
209
|
-
console.log(JSON.stringify({ ok: false, error: "no_capabilities_found" }));
|
|
210
|
-
process.exit(1);
|
|
211
|
-
}
|
|
212
|
-
fail("No capabilities.json or contract.json found in inferno/");
|
|
213
|
-
process.exit(1);
|
|
214
|
-
}
|
|
215
|
-
|
|
216
|
-
const previous = loadCapsAtRef(ref, infernoRelDir, cwd);
|
|
217
|
-
if (!previous) {
|
|
218
|
-
if (asJson) {
|
|
219
|
-
console.log(JSON.stringify({ ok: false, error: "ref_not_found", ref, hint: "Does inferno/capabilities.json exist at that ref?" }));
|
|
220
|
-
process.exit(1);
|
|
221
|
-
}
|
|
222
|
-
fail(`Could not read capabilities at ${ref}`, "The inferno/ directory may not exist at that ref");
|
|
223
|
-
process.exit(1);
|
|
224
|
-
}
|
|
225
|
-
|
|
226
|
-
// ── Compute diff ─────────────────────────────────────────────────────────
|
|
227
|
-
const result = diffCaps(previous, current);
|
|
228
|
-
const unchanged = current.length - result.added.length - result.changed.length;
|
|
229
|
-
|
|
230
|
-
// ── JSON output ──────────────────────────────────────────────────────────
|
|
231
|
-
if (asJson) {
|
|
232
|
-
console.log(JSON.stringify({
|
|
233
|
-
ok: true,
|
|
234
|
-
ref,
|
|
235
|
-
current: current.length,
|
|
236
|
-
previous: previous.length,
|
|
237
|
-
added: result.added,
|
|
238
|
-
removed: result.removed,
|
|
239
|
-
changed: result.changed,
|
|
240
|
-
unchanged,
|
|
241
|
-
}, null, 2));
|
|
242
|
-
return;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// ── Human output ─────────────────────────────────────────────────────────
|
|
246
|
-
const refLabel = refDescription(cwd, ref);
|
|
247
|
-
console.log();
|
|
248
|
-
console.log(` Comparing ${bold(cyan("current"))} vs ${bold(refLabel)}`);
|
|
249
|
-
console.log(` ${gray(current.length + " capabilities now / " + previous.length + " before")}`);
|
|
250
|
-
|
|
251
|
-
if (summary) {
|
|
252
|
-
renderSummaryLine(result, ref);
|
|
253
|
-
console.log();
|
|
254
|
-
return;
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
const hasAny = result.added.length || result.removed.length || result.changed.length;
|
|
258
|
-
|
|
259
|
-
if (!hasAny) {
|
|
260
|
-
console.log();
|
|
261
|
-
ok("No capability changes since " + ref);
|
|
262
|
-
console.log();
|
|
263
|
-
return;
|
|
264
|
-
}
|
|
265
|
-
|
|
266
|
-
renderAdded(result.added);
|
|
267
|
-
renderRemoved(result.removed);
|
|
268
|
-
renderChanged(result.changed);
|
|
269
|
-
renderUnchanged(unchanged);
|
|
270
|
-
|
|
271
|
-
console.log();
|
|
272
|
-
renderSummaryLine(result, ref);
|
|
273
|
-
console.log();
|
|
274
|
-
}
|
|
1
|
+
import*as u from"node:fs";import*as v from"node:path";import{execSync as C}from"node:child_process";import{header as N,ok as J,fail as m,bold as d,cyan as k,gray as c,green as $,red as y,yellow as x}from"../ui/output.mjs";function h(n,e){try{return C(n,{cwd:e,encoding:"utf8",stdio:["ignore","pipe","pipe"]}).trim()}catch{return null}}function D(n){const e=h("git describe --tags --abbrev=0",n);return e||null}function j(n,e,o){return h(`git show "${n}:${e}"`,o)}function z(n){return h("git rev-parse --abbrev-ref HEAD",n)||"HEAD"}function O(n,e){const o=h(`git log -1 --format=%ci "${e}"`,n),t=o?o.slice(0,10):null;return t?`${e} ${c("("+t+")")}`:e}function b(n){if(!n)return null;try{return(JSON.parse(n).capabilities||[]).map(t=>typeof t=="string"?{id:t,title:t}:{id:t.id||t,title:t.title||t.id||String(t),since:t.since,status:t.status})}catch{return null}}function _(n,e,o){const t=j(n,`${e}/capabilities.json`,o);if(t)return b(t);const g=j(n,`${e}/contract.json`,o);return b(g)}function E(n){const e=v.join(n,"capabilities.json"),o=v.join(n,"contract.json");return u.existsSync(e)?b(u.readFileSync(e,"utf8")):u.existsSync(o)?b(u.readFileSync(o,"utf8")):null}function R(n,e){const o=new Map(n.map(i=>[i.id,i])),t=new Map(e.map(i=>[i.id,i])),g=e.filter(i=>!o.has(i.id)),s=n.filter(i=>!t.has(i.id)),l=[];for(const i of e){const f=o.get(i.id);if(!f)continue;const a=[];f.title!==i.title&&a.push({field:"title",from:f.title,to:i.title}),f.status!==i.status&&(f.status||i.status)&&a.push({field:"status",from:f.status||"\u2014",to:i.status||"\u2014"}),a.length&&l.push({id:i.id,changes:a})}return{added:g,removed:s,changed:l}}function H(n){if(n.length){console.log(`
|
|
2
|
+
${d($("+ Added"))} ${c("("+n.length+")")}`);for(const e of n){const o=e.since?c(" since "+e.since):"";console.log(` ${$("+")} ${d(e.id)} ${c(e.title)}${o}`)}}}function M(n){if(n.length){console.log(`
|
|
3
|
+
${d(y("- Removed"))} ${c("("+n.length+")")}`);for(const e of n)console.log(` ${y("-")} ${d(e.id)} ${c(e.title)}`)}}function F(n){if(n.length){console.log(`
|
|
4
|
+
${d(x("~ Changed"))} ${c("("+n.length+")")}`);for(const e of n){console.log(` ${x("~")} ${d(e.id)}`);for(const o of e.changes)console.log(` ${c(o.field+":")} ${y(o.from)} \u2192 ${$(o.to)}`)}}}function U(n){n!==0&&console.log(`
|
|
5
|
+
${c(" Unchanged "+n)}`)}function w(n,e){const o=[];n.added.length&&o.push($("+"+n.added.length+" added")),n.removed.length&&o.push(y("-"+n.removed.length+" removed")),n.changed.length&&o.push(x("~"+n.changed.length+" changed")),o.length||o.push(c("no changes")),console.log(` ${o.join(" ")} ${c("vs "+e)}`)}async function G(n){const e=n.slice(1),o=e.includes("--json"),t=e.includes("--summary"),g=e.indexOf("--ref");let s=g!==-1?e[g+1]:null;const l=process.cwd(),i=v.join(l,"inferno"),f="inferno";o||N("diff"),u.existsSync(i)||(o&&(console.log(JSON.stringify({ok:!1,error:"inferno_not_found"})),process.exit(1)),m("inferno/ not found","Run: infernoflow init"),process.exit(1)),s||(s=D(l),s||(h("git rev-parse HEAD~1",l)?s="HEAD~1":(o&&(console.log(JSON.stringify({ok:!1,error:"no_ref",hint:"No git tags found and no parent commit. Use --ref <commit>"})),process.exit(1)),m("No git tags found","Create a tag first: git tag v0.1.0 or use --ref <commit>"),process.exit(1))));const a=E(i);a||(o&&(console.log(JSON.stringify({ok:!1,error:"no_capabilities_found"})),process.exit(1)),m("No capabilities.json or contract.json found in inferno/"),process.exit(1));const p=_(s,f,l);p||(o&&(console.log(JSON.stringify({ok:!1,error:"ref_not_found",ref:s,hint:"Does inferno/capabilities.json exist at that ref?"})),process.exit(1)),m(`Could not read capabilities at ${s}`,"The inferno/ directory may not exist at that ref"),process.exit(1));const r=R(p,a),S=a.length-r.added.length-r.changed.length;if(o){console.log(JSON.stringify({ok:!0,ref:s,current:a.length,previous:p.length,added:r.added,removed:r.removed,changed:r.changed,unchanged:S},null,2));return}const A=O(l,s);if(console.log(),console.log(` Comparing ${d(k("current"))} vs ${d(A)}`),console.log(` ${c(a.length+" capabilities now / "+p.length+" before")}`),t){w(r,s),console.log();return}if(!(r.added.length||r.removed.length||r.changed.length)){console.log(),J("No capability changes since "+s),console.log();return}H(r.added),M(r.removed),F(r.changed),U(S),console.log(),w(r,s),console.log()}export{G as diffCommand};
|
|
@@ -1,81 +1,2 @@
|
|
|
1
|
-
import
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
function sh(cmd) {
|
|
5
|
-
return execSync(cmd, { stdio: ["ignore", "pipe", "pipe"] }).toString("utf8").trim();
|
|
6
|
-
}
|
|
7
|
-
|
|
8
|
-
const CODE_PREFIXES = [
|
|
9
|
-
"src/", "frontend/", "backend/",
|
|
10
|
-
"app/", "pages/", "components/",
|
|
11
|
-
"Controllers/", "Services/", "Endpoints/",
|
|
12
|
-
"lib/", "api/", "server/"
|
|
13
|
-
];
|
|
14
|
-
|
|
15
|
-
export async function docGateCommand(opts = {}) {
|
|
16
|
-
const fromArgs = Array.isArray(opts);
|
|
17
|
-
const silent = fromArgs ? false : (opts?.silent || false);
|
|
18
|
-
const captureExit = fromArgs ? false : (opts?.captureExit || false);
|
|
19
|
-
const jsonOut = fromArgs ? opts.includes("--json") : Boolean(opts?.json);
|
|
20
|
-
const base = process.env.BASE_SHA || "HEAD~1";
|
|
21
|
-
const head = process.env.HEAD_SHA || "HEAD";
|
|
22
|
-
|
|
23
|
-
let files = [];
|
|
24
|
-
try {
|
|
25
|
-
const out = sh(`git diff --name-only ${base}..${head}`);
|
|
26
|
-
files = out ? out.split("\n").filter(Boolean) : [];
|
|
27
|
-
} catch {
|
|
28
|
-
if (jsonOut) {
|
|
29
|
-
console.log(JSON.stringify({ ok: true, skipped: true, reason: "no_git_available" }, null, 2));
|
|
30
|
-
return;
|
|
31
|
-
}
|
|
32
|
-
if (!silent) info(gray("doc-gate skipped (no git available)"));
|
|
33
|
-
return;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
if (files.length === 0) {
|
|
37
|
-
if (jsonOut) {
|
|
38
|
-
console.log(JSON.stringify({ ok: true, changedFiles: 0, changedCode: false, changedInferno: false }, null, 2));
|
|
39
|
-
return;
|
|
40
|
-
}
|
|
41
|
-
if (!silent) ok("doc-gate: no changed files");
|
|
42
|
-
return;
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const changedCode = files.some(f =>
|
|
46
|
-
CODE_PREFIXES.some(p => f.startsWith(p) || f.includes("/" + p))
|
|
47
|
-
);
|
|
48
|
-
const changedInferno = files.some(f => f.startsWith("inferno/"));
|
|
49
|
-
const codeFiles = files.filter(f => CODE_PREFIXES.some(p => f.startsWith(p))).slice(0, 5);
|
|
50
|
-
|
|
51
|
-
if (jsonOut) {
|
|
52
|
-
const payload = {
|
|
53
|
-
ok: !(changedCode && !changedInferno),
|
|
54
|
-
changedFiles: files.length,
|
|
55
|
-
changedCode,
|
|
56
|
-
changedInferno,
|
|
57
|
-
sampleCodeFiles: codeFiles,
|
|
58
|
-
hint: changedCode && !changedInferno ? "Update at least one file in inferno/ before committing" : null,
|
|
59
|
-
};
|
|
60
|
-
console.log(JSON.stringify(payload, null, 2));
|
|
61
|
-
if (!payload.ok) process.exit(1);
|
|
62
|
-
return;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
if (changedCode && !changedInferno) {
|
|
66
|
-
if (!silent) {
|
|
67
|
-
fail(
|
|
68
|
-
"Code changed but inferno/ was NOT updated",
|
|
69
|
-
"Update at least one file in inferno/ before committing"
|
|
70
|
-
);
|
|
71
|
-
if (codeFiles.length) {
|
|
72
|
-
console.log();
|
|
73
|
-
codeFiles.forEach(f => console.log(" " + gray("• " + f)));
|
|
74
|
-
}
|
|
75
|
-
}
|
|
76
|
-
if (captureExit) throw new Error("doc-gate failed");
|
|
77
|
-
process.exit(1);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
if (!silent) ok("doc-gate: docs are up to date");
|
|
81
|
-
}
|
|
1
|
+
import{execSync as m}from"node:child_process";import{ok as f,fail as E,info as y,gray as d}from"../ui/output.mjs";function S(n){return m(n,{stdio:["ignore","pipe","pipe"]}).toString("utf8").trim()}const g=["src/","frontend/","backend/","app/","pages/","components/","Controllers/","Services/","Endpoints/","lib/","api/","server/"];async function C(n={}){const a=Array.isArray(n),t=a?!1:n?.silent||!1,p=a?!1:n?.captureExit||!1,l=a?n.includes("--json"):!!n?.json,u=process.env.BASE_SHA||"HEAD~1",h=process.env.HEAD_SHA||"HEAD";let o=[];try{const e=S(`git diff --name-only ${u}..${h}`);o=e?e.split(`
|
|
2
|
+
`).filter(Boolean):[]}catch{if(l){console.log(JSON.stringify({ok:!0,skipped:!0,reason:"no_git_available"},null,2));return}t||y(d("doc-gate skipped (no git available)"));return}if(o.length===0){if(l){console.log(JSON.stringify({ok:!0,changedFiles:0,changedCode:!1,changedInferno:!1},null,2));return}t||f("doc-gate: no changed files");return}const i=o.some(e=>g.some(r=>e.startsWith(r)||e.includes("/"+r))),s=o.some(e=>e.startsWith("inferno/")),c=o.filter(e=>g.some(r=>e.startsWith(r))).slice(0,5);if(l){const e={ok:!(i&&!s),changedFiles:o.length,changedCode:i,changedInferno:s,sampleCodeFiles:c,hint:i&&!s?"Update at least one file in inferno/ before committing":null};console.log(JSON.stringify(e,null,2)),e.ok||process.exit(1);return}if(i&&!s){if(t||(E("Code changed but inferno/ was NOT updated","Update at least one file in inferno/ before committing"),c.length&&(console.log(),c.forEach(e=>console.log(" "+d("\u2022 "+e))))),p)throw new Error("doc-gate failed");process.exit(1)}t||f("doc-gate: docs are up to date")}export{C as docGateCommand};
|