infernoflow 0.32.7 → 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.
Files changed (78) hide show
  1. package/dist/bin/infernoflow.mjs +84 -255
  2. package/dist/lib/adopters/angular.mjs +1 -128
  3. package/dist/lib/adopters/css.mjs +1 -111
  4. package/dist/lib/adopters/react.mjs +1 -104
  5. package/dist/lib/ai/ideDetection.mjs +1 -31
  6. package/dist/lib/ai/localProvider.mjs +1 -88
  7. package/dist/lib/ai/providerRouter.mjs +2 -295
  8. package/dist/lib/commands/adopt.mjs +20 -869
  9. package/dist/lib/commands/adoptWizard.mjs +9 -320
  10. package/dist/lib/commands/agent.mjs +5 -191
  11. package/dist/lib/commands/ai.mjs +2 -407
  12. package/dist/lib/commands/audit.mjs +13 -300
  13. package/dist/lib/commands/changelog.mjs +26 -594
  14. package/dist/lib/commands/check.mjs +3 -184
  15. package/dist/lib/commands/ci.mjs +3 -208
  16. package/dist/lib/commands/claudeMd.mjs +25 -130
  17. package/dist/lib/commands/cloud.mjs +5 -521
  18. package/dist/lib/commands/context.mjs +31 -287
  19. package/dist/lib/commands/coverage.mjs +2 -282
  20. package/dist/lib/commands/dashboard.mjs +123 -635
  21. package/dist/lib/commands/demo.mjs +8 -465
  22. package/dist/lib/commands/diff.mjs +5 -274
  23. package/dist/lib/commands/docGate.mjs +2 -81
  24. package/dist/lib/commands/doctor.mjs +3 -321
  25. package/dist/lib/commands/explain.mjs +8 -438
  26. package/dist/lib/commands/export.mjs +10 -239
  27. package/dist/lib/commands/generateSkills.mjs +38 -163
  28. package/dist/lib/commands/graph.mjs +203 -320
  29. package/dist/lib/commands/health.mjs +2 -309
  30. package/dist/lib/commands/impact.mjs +2 -325
  31. package/dist/lib/commands/implement.mjs +7 -103
  32. package/dist/lib/commands/init.mjs +23 -475
  33. package/dist/lib/commands/installCursorHooks.mjs +1 -36
  34. package/dist/lib/commands/installVsCodeCopilotHooks.mjs +1 -37
  35. package/dist/lib/commands/link.mjs +2 -342
  36. package/dist/lib/commands/monorepo.mjs +4 -428
  37. package/dist/lib/commands/notify.mjs +4 -258
  38. package/dist/lib/commands/onboard.mjs +4 -296
  39. package/dist/lib/commands/prComment.mjs +2 -361
  40. package/dist/lib/commands/prImpact.mjs +2 -157
  41. package/dist/lib/commands/publish.mjs +15 -316
  42. package/dist/lib/commands/report.mjs +28 -272
  43. package/dist/lib/commands/review.mjs +9 -223
  44. package/dist/lib/commands/run.mjs +8 -336
  45. package/dist/lib/commands/scaffold.mjs +54 -419
  46. package/dist/lib/commands/scan.mjs +5 -558
  47. package/dist/lib/commands/scout.mjs +2 -291
  48. package/dist/lib/commands/setup.mjs +5 -310
  49. package/dist/lib/commands/share.mjs +13 -196
  50. package/dist/lib/commands/snapshot.mjs +3 -383
  51. package/dist/lib/commands/stability.mjs +2 -293
  52. package/dist/lib/commands/status.mjs +4 -172
  53. package/dist/lib/commands/suggest.mjs +21 -563
  54. package/dist/lib/commands/syncAuto.mjs +1 -96
  55. package/dist/lib/commands/synthesize.mjs +10 -228
  56. package/dist/lib/commands/teamSync.mjs +2 -388
  57. package/dist/lib/commands/test.mjs +6 -363
  58. package/dist/lib/commands/version.mjs +2 -282
  59. package/dist/lib/commands/vibe.mjs +7 -357
  60. package/dist/lib/commands/watch.mjs +4 -203
  61. package/dist/lib/commands/why.mjs +4 -358
  62. package/dist/lib/cursorHooksInstall.mjs +1 -60
  63. package/dist/lib/draftToolingInstall.mjs +7 -68
  64. package/dist/lib/git/detect-drift.mjs +4 -208
  65. package/dist/lib/learning/adapt.mjs +6 -101
  66. package/dist/lib/learning/observe.mjs +1 -119
  67. package/dist/lib/learning/patternDetector.mjs +1 -298
  68. package/dist/lib/learning/profile.mjs +2 -279
  69. package/dist/lib/learning/skillSynthesizer.mjs +24 -145
  70. package/dist/lib/templates/index.mjs +1 -131
  71. package/dist/lib/ui/errors.mjs +1 -142
  72. package/dist/lib/ui/output.mjs +6 -72
  73. package/dist/lib/ui/prompts.mjs +6 -147
  74. package/dist/lib/vsCodeCopilotHooksInstall.mjs +1 -42
  75. package/dist/templates/cursor/inferno-mcp-server.mjs +29 -0
  76. package/dist/templates/github-app/GITHUB_APP.md +67 -0
  77. package/dist/templates/github-app/app-manifest.json +20 -0
  78. package/package.json +1 -1
@@ -1,338 +1,10 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
- import { execFileSync } from "node:child_process";
4
- import { fileURLToPath } from "node:url";
5
- import { generateWithLocalModel } from "../ai/localProvider.mjs";
6
- import { resolveProvider } from "../ai/providerRouter.mjs";
7
- import {
8
- buildPrompt,
9
- loadSuggestContext,
10
- parseSuggestionJson,
11
- validateSuggestion,
12
- detectSuggestionConflicts,
13
- applyChanges,
14
- } from "./suggest.mjs";
15
- import { header, section, ok, warn, fail, info, gray } from "../ui/output.mjs";
1
+ import*as a from"node:fs";import*as p from"node:path";import{execFileSync as T}from"node:child_process";import{fileURLToPath as L}from"node:url";import{generateWithLocalModel as $}from"../ai/localProvider.mjs";import{resolveProvider as q}from"../ai/providerRouter.mjs";import{buildPrompt as W,loadSuggestContext as G,parseSuggestionJson as O,validateSuggestion as M,detectSuggestionConflicts as V,applyChanges as K}from"./suggest.mjs";import{header as U,section as B,ok as x,warn as F,fail as v,info as h,gray as j}from"../ui/output.mjs";const z=L(import.meta.url),H=p.dirname(z),Q=p.resolve(H,"..","..","bin","infernoflow.mjs");function D(t){try{const e=T(process.execPath,[Q,...t],{encoding:"utf8",stdio:["ignore","pipe","pipe"]});return{ok:!0,data:JSON.parse(e)}}catch(e){const n=e?.stdout?.toString?.()||"";try{return{ok:!1,data:JSON.parse(n)}}catch{return{ok:!1,data:{ok:!1,errors:["command_failed"]}}}}}function m(t,e,n,o,s={}){const r={ts:new Date().toISOString(),stage:n,status:o,...s};if(e.push(r),t)return;const c=`${n}: ${o}`;o==="ok"?x(c):o==="warn"?F(c):o==="fail"?v(c):h(c)}function X(t){const e=p.join(t,"inferno"),n=[],o=r=>{for(const c of a.readdirSync(r,{withFileTypes:!0})){const f=p.join(r,c.name);c.isDirectory()?o(f):n.push(f)}};a.existsSync(e)&&o(e);const s=new Map;return n.forEach(r=>s.set(r,a.readFileSync(r,"utf8"))),s}function Y(t,e){const n=p.join(t,"inferno");if(a.existsSync(n)){const o=[],s=r=>{for(const c of a.readdirSync(r,{withFileTypes:!0})){const f=p.join(r,c.name);c.isDirectory()?s(f):o.push(f)}};s(n),o.forEach(r=>{e.has(r)||a.unlinkSync(r)})}for(const[o,s]of e.entries())a.mkdirSync(p.dirname(o),{recursive:!0}),a.writeFileSync(o,s,"utf8")}function Z(t,e){const n=p.join(t,"inferno","runs");a.mkdirSync(n,{recursive:!0});const o=p.join(n,`${Date.now()}.json`);return a.writeFileSync(o,JSON.stringify(e,null,2)+`
2
+ `,"utf8"),p.relative(t,o)}function A(t,e,n=null){const o=t.indexOf(e);return o!==-1&&t[o+1]&&!t[o+1].startsWith("-")?t[o+1]:n}function ee(t){const e=new Set(["--provider","--ide"]),n=[];for(let o=1;o<t.length;o++){const s=t[o];if(s.startsWith("-")){e.has(s)&&(o+=1);continue}n.push(s)}return n.join(" ").trim()}function oe(t,e){return{summary:`Prompt fallback only: ${t}`,newCapabilities:[],removedCapabilities:[],updatedScenarios:[],changelogEntry:`- Prompt fallback mode for task: ${t} (no automatic contract mutation).`,_meta:{actionRequired:!0,nextStep:"Run infernoflow suggest or provide an agent bridge for automatic apply.",capabilitiesCount:(e?.capabilities||[]).length}}}async function te(t){if(process.env.INFERNO_AGENT_MOCK_RESPONSE)return process.env.INFERNO_AGENT_MOCK_RESPONSE;const e=process.env.INFERNO_AGENT_RESPONSE_FILE?process.env.INFERNO_AGENT_RESPONSE_FILE:p.join(process.cwd(),"inferno","agent-response.json");if(a.existsSync(e)){const r=a.readFileSync(e,"utf8");return a.unlinkSync(e),r}const n=p.join(process.cwd(),"inferno"),o=p.join(n,"agent-prompt.md");a.existsSync(o)&&a.unlinkSync(o),a.writeFileSync(o,t,"utf8"),process.stderr.write(`
3
+ \u2139 Prompt written to inferno/agent-prompt.md
4
+ `),process.stderr.write(` \u2192 Open it, paste into Cursor or Claude
5
+ `),process.stderr.write(` \u2192 Save the JSON reply to: inferno/agent-response.json
6
+ `),process.stderr.write(` Waiting up to 5 minutes...
16
7
 
17
- const __filename = fileURLToPath(import.meta.url);
18
- const __dirname = path.dirname(__filename);
19
- const binPath = path.resolve(__dirname, "..", "..", "bin", "infernoflow.mjs");
20
-
21
- function runCliJson(args) {
22
- try {
23
- const out = execFileSync(process.execPath, [binPath, ...args], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
24
- return { ok: true, data: JSON.parse(out) };
25
- } catch (err) {
26
- const stdout = err?.stdout?.toString?.() || "";
27
- try {
28
- return { ok: false, data: JSON.parse(stdout) };
29
- } catch {
30
- return { ok: false, data: { ok: false, errors: ["command_failed"] } };
31
- }
32
- }
33
- }
34
-
35
- function stageEvent(asJson, events, stage, status, details = {}) {
36
- const ev = { ts: new Date().toISOString(), stage, status, ...details };
37
- events.push(ev);
38
- if (asJson) return;
39
- const text = `${stage}: ${status}`;
40
- if (status === "ok") ok(text);
41
- else if (status === "warn") warn(text);
42
- else if (status === "fail") fail(text);
43
- else info(text);
44
- }
45
-
46
- function snapshotInferno(cwd) {
47
- const infernoDir = path.join(cwd, "inferno");
48
- const targets = [];
49
- const walk = (dir) => {
50
- for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
51
- const p = path.join(dir, entry.name);
52
- if (entry.isDirectory()) walk(p);
53
- else targets.push(p);
54
- }
55
- };
56
- if (fs.existsSync(infernoDir)) walk(infernoDir);
57
- const snapshot = new Map();
58
- targets.forEach((filePath) => snapshot.set(filePath, fs.readFileSync(filePath, "utf8")));
59
- return snapshot;
60
- }
61
-
62
- function restoreSnapshot(cwd, snapshot) {
63
- const infernoDir = path.join(cwd, "inferno");
64
- if (fs.existsSync(infernoDir)) {
65
- const existing = [];
66
- const walk = (dir) => {
67
- for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
68
- const p = path.join(dir, entry.name);
69
- if (entry.isDirectory()) walk(p);
70
- else existing.push(p);
71
- }
72
- };
73
- walk(infernoDir);
74
- existing.forEach((filePath) => {
75
- if (!snapshot.has(filePath)) fs.unlinkSync(filePath);
76
- });
77
- }
78
- for (const [filePath, content] of snapshot.entries()) {
79
- fs.mkdirSync(path.dirname(filePath), { recursive: true });
80
- fs.writeFileSync(filePath, content, "utf8");
81
- }
82
- }
83
-
84
- function writeRunArtifact(cwd, artifact) {
85
- const runsDir = path.join(cwd, "inferno", "runs");
86
- fs.mkdirSync(runsDir, { recursive: true });
87
- const filePath = path.join(runsDir, `${Date.now()}.json`);
88
- fs.writeFileSync(filePath, JSON.stringify(artifact, null, 2) + "\n", "utf8");
89
- return path.relative(cwd, filePath);
90
- }
91
-
92
- function getOptionValue(args, flag, fallback = null) {
93
- const idx = args.indexOf(flag);
94
- if (idx !== -1 && args[idx + 1] && !args[idx + 1].startsWith("-")) return args[idx + 1];
95
- return fallback;
96
- }
97
-
98
- function extractTask(args) {
99
- const takesValue = new Set(["--provider", "--ide"]);
100
- const out = [];
101
- for (let i = 1; i < args.length; i++) {
102
- const token = args[i];
103
- if (token.startsWith("-")) {
104
- if (takesValue.has(token)) i += 1;
105
- continue;
106
- }
107
- out.push(token);
108
- }
109
- return out.join(" ").trim();
110
- }
111
-
112
- function buildPromptFallbackSuggestion(task, contract) {
113
- return {
114
- summary: `Prompt fallback only: ${task}`,
115
- newCapabilities: [],
116
- removedCapabilities: [],
117
- updatedScenarios: [],
118
- changelogEntry: `- Prompt fallback mode for task: ${task} (no automatic contract mutation).`,
119
- _meta: {
120
- actionRequired: true,
121
- nextStep: "Run infernoflow suggest or provide an agent bridge for automatic apply.",
122
- capabilitiesCount: (contract?.capabilities || []).length,
123
- },
124
- };
125
- }
126
-
127
- async function generateWithIdeAgent(prompt) {
128
- if (process.env.INFERNO_AGENT_MOCK_RESPONSE) return process.env.INFERNO_AGENT_MOCK_RESPONSE;
129
- const responseFile = process.env.INFERNO_AGENT_RESPONSE_FILE
130
- ? process.env.INFERNO_AGENT_RESPONSE_FILE
131
- : path.join(process.cwd(), "inferno", "agent-response.json");
132
- if (fs.existsSync(responseFile)) {
133
- const data = fs.readFileSync(responseFile, "utf8");
134
- fs.unlinkSync(responseFile);
135
- return data;
136
- }
137
- const infernoDir = path.join(process.cwd(), "inferno");
138
- const promptFile = path.join(infernoDir, "agent-prompt.md");
139
- if (fs.existsSync(promptFile)) fs.unlinkSync(promptFile);
140
- fs.writeFileSync(promptFile, prompt, "utf8");
141
- process.stderr.write("\n \u2139 Prompt written to inferno/agent-prompt.md\n");
142
- process.stderr.write(" \u2192 Open it, paste into Cursor or Claude\n");
143
- process.stderr.write(" \u2192 Save the JSON reply to: inferno/agent-response.json\n");
144
- process.stderr.write(" Waiting up to 5 minutes...\n\n");
145
- const deadline = Date.now() + 300_000;
146
- while (Date.now() < deadline) {
147
- await new Promise(r => setTimeout(r, 1000));
148
- if (fs.existsSync(responseFile)) {
149
- const data = fs.readFileSync(responseFile, "utf8");
150
- fs.unlinkSync(responseFile);
151
- process.stderr.write(" \u2714 Response received\n\n");
152
- return data;
153
- }
154
- }
155
- throw new Error("ide_agent_bridge_timeout");
156
- }
157
-
158
- export async function runCommand(args = []) {
159
- const asJson = args.includes("--json");
160
- const dryRun = args.includes("--dry-run");
161
- const noRollback = args.includes("--no-rollback");
162
- const providerRequested = (getOptionValue(args, "--provider", "auto") || "auto").toLowerCase();
163
- const ideRequested = (getOptionValue(args, "--ide", "auto") || "auto").toLowerCase();
164
- const task = extractTask(args) || "sync check";
165
- const cwd = process.cwd();
166
- const events = [];
167
- const reasonCodes = [];
168
-
169
- if (!asJson) header("run");
170
- stageEvent(asJson, events, "init", "info", { task, dryRun, noRollback });
171
-
172
- // detect
173
- const impact = runCliJson(["pr-impact", "--json"]);
174
- stageEvent(asJson, events, "detect", impact.data?.ok ? "ok" : "warn", { confidence: impact.data?.confidence || "low" });
175
-
176
- const routed = await resolveProvider(providerRequested, ideRequested);
177
- reasonCodes.push(...(routed.reasonCodes || []));
178
- if (routed.error === "agent_unavailable") {
179
- const payload = {
180
- ok: false,
181
- error: "agent_unavailable",
182
- providerRequested,
183
- providerResolved: routed.providerResolved,
184
- ideDetected: routed.ideDetected,
185
- agentAvailable: routed.agentAvailable,
186
- reasonCodes,
187
- events,
188
- };
189
- if (asJson) console.log(JSON.stringify(payload, null, 2));
190
- else fail("provider agent unavailable", "Use --provider auto|local|prompt");
191
- process.exit(1);
192
- }
193
- stageEvent(asJson, events, "route", "ok", {
194
- providerRequested,
195
- providerResolved: routed.providerResolved,
196
- ideDetected: routed.ideDetected,
197
- agentAvailable: routed.agentAvailable,
198
- });
199
-
200
- const ctx = loadSuggestContext(cwd);
201
- if (!ctx?.contract) {
202
- const payload = { ok: false, error: "inferno_missing", events };
203
- if (asJson) console.log(JSON.stringify(payload, null, 2));
204
- else fail("inferno/ missing or invalid");
205
- process.exit(1);
206
- }
207
-
208
- // propose
209
- const prompt = buildPrompt({
210
- description: task,
211
- contract: ctx.contract,
212
- capabilities: ctx.capabilities,
213
- scenarios: ctx.scenarios,
214
- });
215
- let suggestion;
216
- try {
217
- if (routed.providerResolved === "local") {
218
- const raw = await generateWithLocalModel(prompt);
219
- suggestion = parseSuggestionJson(raw);
220
- } else if (routed.providerResolved === "agent") {
221
- const raw = await generateWithIdeAgent(prompt);
222
- suggestion = parseSuggestionJson(raw);
223
- } else {
224
- suggestion = buildPromptFallbackSuggestion(task, ctx.contract);
225
- }
226
- } catch (err) {
227
- const payload = { ok: false, error: "proposal_failed", reason: String(err.message || err), reasonCodes, events };
228
- if (asJson) console.log(JSON.stringify(payload, null, 2));
229
- else fail("proposal generation failed", err.message);
230
- process.exit(1);
231
- }
232
- stageEvent(asJson, events, "propose", "ok", {
233
- newCapabilities: (suggestion.newCapabilities || []).length,
234
- removedCapabilities: (suggestion.removedCapabilities || []).length,
235
- });
236
-
237
- const schemaErrors = routed.providerResolved === "prompt" ? [] : validateSuggestion(suggestion);
238
- const conflictErrors = routed.providerResolved === "prompt" ? [] : detectSuggestionConflicts(ctx.contract, suggestion);
239
- if (schemaErrors.length || conflictErrors.length) {
240
- const payload = {
241
- ok: false,
242
- error: "invalid_suggestion",
243
- issues: [...schemaErrors, ...conflictErrors],
244
- events,
245
- };
246
- if (asJson) console.log(JSON.stringify(payload, null, 2));
247
- else fail("suggestion invalid", payload.issues[0]);
248
- process.exit(1);
249
- }
250
-
251
- const snapshot = snapshotInferno(cwd);
252
- let rolledBack = false;
253
- let applyChanged = false;
254
- let validationPassed = false;
255
-
256
- try {
257
- if (dryRun) {
258
- stageEvent(asJson, events, "apply", "info", { dryRun: true });
259
- } else if (routed.providerResolved === "prompt") {
260
- stageEvent(asJson, events, "apply", "warn", {
261
- skipped: true,
262
- reason: "prompt_fallback_requires_manual_step",
263
- });
264
- } else {
265
- applyChanged = applyChanges({
266
- cwd,
267
- contract: ctx.contract,
268
- capabilities: ctx.capabilities,
269
- suggestion,
270
- version: ctx.version,
271
- quiet: asJson,
272
- });
273
- stageEvent(asJson, events, "apply", "ok", { changed: applyChanged });
274
- }
275
-
276
- let check = runCliJson(["check", "--json"]);
277
- if (process.env.INFERNO_TEST_FORCE_VALIDATE_FAIL === "1") {
278
- check = { ok: false, data: { ok: false, errors: ["forced_validation_failure"] } };
279
- }
280
- if (!check.ok || !check.data?.ok) {
281
- throw new Error(`validation_failed:${(check.data?.errors || []).join(",")}`);
282
- }
283
- validationPassed = true;
284
- stageEvent(asJson, events, "validate", "ok");
285
- } catch (err) {
286
- stageEvent(asJson, events, "validate", "fail", { reason: String(err.message || err) });
287
- if (!dryRun && !noRollback) {
288
- restoreSnapshot(cwd, snapshot);
289
- rolledBack = true;
290
- stageEvent(asJson, events, "rollback", "ok");
291
- }
292
- }
293
-
294
- const artifact = {
295
- task,
296
- dryRun,
297
- noRollback,
298
- rolledBack,
299
- applyChanged,
300
- suggestionSummary: suggestion.summary || "",
301
- touchedCapabilities: [
302
- ...(suggestion.newCapabilities || []).map((c) => c.id),
303
- ...(suggestion.removedCapabilities || []),
304
- ],
305
- events,
306
- };
307
- const artifactPath = writeRunArtifact(cwd, artifact);
308
-
309
- const payload = {
310
- ok: validationPassed,
311
- mode: "run",
312
- task,
313
- dryRun,
314
- providerRequested,
315
- providerResolved: routed.providerResolved,
316
- ideDetected: routed.ideDetected,
317
- agentAvailable: routed.agentAvailable,
318
- reasonCodes: Array.from(new Set(reasonCodes)),
319
- rolledBack,
320
- applyChanged,
321
- artifactPath,
322
- events,
323
- };
324
-
325
- if (asJson) {
326
- console.log(JSON.stringify(payload, null, 2));
327
- process.exit(payload.ok ? 0 : 1);
328
- }
329
-
330
- section("Result");
331
- info(`task: ${gray(task)}`);
332
- info(`artifact: ${gray(artifactPath)}`);
333
- if (payload.ok) ok("run completed");
334
- else warn("run rolled back after failed validation");
335
- console.log();
336
- process.exit(payload.ok ? 0 : 1);
337
- }
8
+ `);const s=Date.now()+3e5;for(;Date.now()<s;)if(await new Promise(r=>setTimeout(r,1e3)),a.existsSync(e)){const r=a.readFileSync(e,"utf8");return a.unlinkSync(e),process.stderr.write(` \u2714 Response received
338
9
 
10
+ `),r}throw new Error("ide_agent_bridge_timeout")}async function le(t=[]){const e=t.includes("--json"),n=t.includes("--dry-run"),o=t.includes("--no-rollback"),s=(A(t,"--provider","auto")||"auto").toLowerCase(),r=(A(t,"--ide","auto")||"auto").toLowerCase(),c=ee(t)||"sync check",f=process.cwd(),l=[],y=[];e||U("run"),m(e,l,"init","info",{task:c,dryRun:n,noRollback:o});const b=D(["pr-impact","--json"]);m(e,l,"detect",b.data?.ok?"ok":"warn",{confidence:b.data?.confidence||"low"});const d=await q(s,r);if(y.push(...d.reasonCodes||[]),d.error==="agent_unavailable"){const i={ok:!1,error:"agent_unavailable",providerRequested:s,providerResolved:d.providerResolved,ideDetected:d.ideDetected,agentAvailable:d.agentAvailable,reasonCodes:y,events:l};e?console.log(JSON.stringify(i,null,2)):v("provider agent unavailable","Use --provider auto|local|prompt"),process.exit(1)}m(e,l,"route","ok",{providerRequested:s,providerResolved:d.providerResolved,ideDetected:d.ideDetected,agentAvailable:d.agentAvailable});const g=G(f);g?.contract||(e?console.log(JSON.stringify({ok:!1,error:"inferno_missing",events:l},null,2)):v("inferno/ missing or invalid"),process.exit(1));const _=W({description:c,contract:g.contract,capabilities:g.capabilities,scenarios:g.scenarios});let u;try{if(d.providerResolved==="local"){const i=await $(_);u=O(i)}else if(d.providerResolved==="agent"){const i=await te(_);u=O(i)}else u=oe(c,g.contract)}catch(i){const J={ok:!1,error:"proposal_failed",reason:String(i.message||i),reasonCodes:y,events:l};e?console.log(JSON.stringify(J,null,2)):v("proposal generation failed",i.message),process.exit(1)}m(e,l,"propose","ok",{newCapabilities:(u.newCapabilities||[]).length,removedCapabilities:(u.removedCapabilities||[]).length});const R=d.providerResolved==="prompt"?[]:M(u),E=d.providerResolved==="prompt"?[]:V(g.contract,u);if(R.length||E.length){const i={ok:!1,error:"invalid_suggestion",issues:[...R,...E],events:l};e?console.log(JSON.stringify(i,null,2)):v("suggestion invalid",i.issues[0]),process.exit(1)}const P=X(f);let w=!1,S=!1,N=!1;try{n?m(e,l,"apply","info",{dryRun:!0}):d.providerResolved==="prompt"?m(e,l,"apply","warn",{skipped:!0,reason:"prompt_fallback_requires_manual_step"}):(S=K({cwd:f,contract:g.contract,capabilities:g.capabilities,suggestion:u,version:g.version,quiet:e}),m(e,l,"apply","ok",{changed:S}));let i=D(["check","--json"]);if(process.env.INFERNO_TEST_FORCE_VALIDATE_FAIL==="1"&&(i={ok:!1,data:{ok:!1,errors:["forced_validation_failure"]}}),!i.ok||!i.data?.ok)throw new Error(`validation_failed:${(i.data?.errors||[]).join(",")}`);N=!0,m(e,l,"validate","ok")}catch(i){m(e,l,"validate","fail",{reason:String(i.message||i)}),!n&&!o&&(Y(f,P),w=!0,m(e,l,"rollback","ok"))}const I={task:c,dryRun:n,noRollback:o,rolledBack:w,applyChanged:S,suggestionSummary:u.summary||"",touchedCapabilities:[...(u.newCapabilities||[]).map(i=>i.id),...u.removedCapabilities||[]],events:l},C=Z(f,I),k={ok:N,mode:"run",task:c,dryRun:n,providerRequested:s,providerResolved:d.providerResolved,ideDetected:d.ideDetected,agentAvailable:d.agentAvailable,reasonCodes:Array.from(new Set(y)),rolledBack:w,applyChanged:S,artifactPath:C,events:l};e&&(console.log(JSON.stringify(k,null,2)),process.exit(k.ok?0:1)),B("Result"),h(`task: ${j(c)}`),h(`artifact: ${j(C)}`),k.ok?x("run completed"):F("run rolled back after failed validation"),console.log(),process.exit(k.ok?0:1)}export{le as runCommand};