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.
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 -321
  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,147 +1,6 @@
1
- // Zero-dependency interactive prompts using readline
2
-
3
- import * as readline from "node:readline";
4
- import * as fs from "node:fs";
5
- import * as path from "node:path";
6
-
7
- function ask(question, defaultVal = "") {
8
- return new Promise(resolve => {
9
- const hint = defaultVal ? ` (${defaultVal})` : "";
10
- const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
11
- rl.question(` ${question}${hint}: `, answer => {
12
- rl.close();
13
- resolve(answer.trim() || defaultVal);
14
- });
15
- });
16
- }
17
-
18
- export async function promptInit() {
19
- const policyId = await ask("Project / policy name", process.env._INFERNO_DEFAULT_POLICY || "my-project");
20
- const caps = await ask("Capabilities (comma-separated)", "CreateTask, ReadTasks, UpdateTask, DeleteTask");
21
- return { policyId, capabilities: caps.split(",").map(c => c.trim()).filter(Boolean) };
22
- }
23
-
24
- function readJson(filePath) {
25
- try {
26
- return JSON.parse(fs.readFileSync(filePath, "utf8"));
27
- } catch {
28
- return null;
29
- }
30
- }
31
-
32
- export function loadImplementContext(cwd) {
33
- const infernoDir = path.join(cwd, "inferno");
34
- const contract = readJson(path.join(infernoDir, "contract.json")) || {};
35
- const caps = readJson(path.join(infernoDir, "capabilities.json")) || { capabilities: [] };
36
- const state = readJson(path.join(infernoDir, "context-state.json")) || {};
37
- const scenariosDir = path.join(infernoDir, "scenarios");
38
-
39
- const scenarios = [];
40
- if (fs.existsSync(scenariosDir)) {
41
- for (const fileName of fs.readdirSync(scenariosDir).filter((f) => f.endsWith(".json"))) {
42
- const scenario = readJson(path.join(scenariosDir, fileName));
43
- if (scenario) scenarios.push({ file: fileName, scenario });
44
- }
45
- }
46
-
47
- return { contract, caps, state, scenarios };
48
- }
49
-
50
- function renderCaps(capsRegistry) {
51
- const list = capsRegistry?.capabilities || [];
52
- if (list.length === 0) return "- none";
53
- return list.map((c) => `- ${c.id}: ${c.title || c.id}`).join("\n");
54
- }
55
-
56
- function renderScenarios(scenarios) {
57
- if (!scenarios || scenarios.length === 0) return "- none";
58
- return scenarios
59
- .map(({ file, scenario }) => {
60
- const covered = (scenario.capabilitiesCovered || []).join(", ") || "none";
61
- return `- ${file}: covers [${covered}]`;
62
- })
63
- .join("\n");
64
- }
65
-
66
- function baseContextBlock({ contract, caps, scenarios, state }) {
67
- const policy = contract?.policyId || "unknown-policy";
68
- const version = contract?.policyVersion ?? "unknown";
69
- const declared = (contract?.capabilities || []).join(", ") || "none";
70
- const working = state?.working || "not set";
71
- const intent = state?.intent || "not set";
72
-
73
- return [
74
- `Project policyId: ${policy}`,
75
- `Policy version: ${version}`,
76
- `Declared capabilities: [${declared}]`,
77
- `Working on: ${working}`,
78
- `Intent: ${intent}`,
79
- "",
80
- "Capabilities registry:",
81
- renderCaps(caps),
82
- "",
83
- "Scenarios:",
84
- renderScenarios(scenarios),
85
- ].join("\n");
86
- }
87
-
88
- export function buildCursorImplementPrompt({ task, contract, caps, scenarios, state }) {
89
- return [
90
- "You are a Cursor coding agent working inside my repository.",
91
- "Implement the task end-to-end with minimal reliable changes.",
92
- "",
93
- baseContextBlock({ contract, caps, scenarios, state }),
94
- "",
95
- `Task: ${task}`,
96
- "",
97
- "Requirements:",
98
- "1) Propose smallest safe implementation.",
99
- "2) Explain which files you changed and why.",
100
- "3) Implement production-ready code.",
101
- "4) Preserve backward compatibility unless explicitly requested.",
102
- "5) Update tests or add smoke checks.",
103
- "6) Provide run/verify commands.",
104
- "7) If assumptions are needed, state briefly and proceed with sensible defaults.",
105
- "",
106
- "Output format:",
107
- "- Plan (short)",
108
- "- Code changes (by file)",
109
- "- Tests updated/added",
110
- "- Commands to run",
111
- "- Acceptance checklist",
112
- "",
113
- "Quality bar:",
114
- "- No TODO placeholders in final code",
115
- "- Handle edge cases and errors",
116
- "- Keep naming/style consistent",
117
- "- Prefer simple maintainable solutions",
118
- "",
119
- "If model is overloaded (resource exhausted), retry with Auto/another model and continue deterministically.",
120
- ].join("\n");
121
- }
122
-
123
- export function buildGenericImplementPrompt({ task, contract, caps, scenarios, state }) {
124
- return [
125
- "You are my senior software engineer pair.",
126
- "Implement this task end-to-end in my project.",
127
- "",
128
- baseContextBlock({ contract, caps, scenarios, state }),
129
- "",
130
- `Goal: ${task}`,
131
- "",
132
- "Deliverables:",
133
- "- Short implementation plan",
134
- "- Exact file-level changes",
135
- "- Test updates",
136
- "- Verification commands",
137
- "- Final acceptance checklist",
138
- "",
139
- "Constraints:",
140
- "- Keep backward compatibility by default",
141
- "- Make minimal reliable changes",
142
- "- Handle edge cases and error states",
143
- "- Keep output concise and actionable",
144
- "",
145
- "If you encounter temporary model high-load errors, retry and preserve the same output structure.",
146
- ].join("\n");
147
- }
1
+ import*as f from"node:readline";import*as d from"node:fs";import*as a from"node:path";function m(e,n=""){return new Promise(t=>{const o=n?` (${n})`:"",i=f.createInterface({input:process.stdin,output:process.stdout});i.question(` ${e}${o}: `,r=>{i.close(),t(r.trim()||n)})})}async function b(){const e=await m("Project / policy name",process.env._INFERNO_DEFAULT_POLICY||"my-project"),n=await m("Capabilities (comma-separated)","CreateTask, ReadTasks, UpdateTask, DeleteTask");return{policyId:e,capabilities:n.split(",").map(t=>t.trim()).filter(Boolean)}}function p(e){try{return JSON.parse(d.readFileSync(e,"utf8"))}catch{return null}}function k(e){const n=a.join(e,"inferno"),t=p(a.join(n,"contract.json"))||{},o=p(a.join(n,"capabilities.json"))||{capabilities:[]},i=p(a.join(n,"context-state.json"))||{},r=a.join(n,"scenarios"),c=[];if(d.existsSync(r))for(const l of d.readdirSync(r).filter(s=>s.endsWith(".json"))){const s=p(a.join(r,l));s&&c.push({file:l,scenario:s})}return{contract:t,caps:o,state:i,scenarios:c}}function y(e){const n=e?.capabilities||[];return n.length===0?"- none":n.map(t=>`- ${t.id}: ${t.title||t.id}`).join(`
2
+ `)}function h(e){return!e||e.length===0?"- none":e.map(({file:n,scenario:t})=>{const o=(t.capabilitiesCovered||[]).join(", ")||"none";return`- ${n}: covers [${o}]`}).join(`
3
+ `)}function u({contract:e,caps:n,scenarios:t,state:o}){const i=e?.policyId||"unknown-policy",r=e?.policyVersion??"unknown",c=(e?.capabilities||[]).join(", ")||"none",l=o?.working||"not set",s=o?.intent||"not set";return[`Project policyId: ${i}`,`Policy version: ${r}`,`Declared capabilities: [${c}]`,`Working on: ${l}`,`Intent: ${s}`,"","Capabilities registry:",y(n),"","Scenarios:",h(t)].join(`
4
+ `)}function j({task:e,contract:n,caps:t,scenarios:o,state:i}){return["You are a Cursor coding agent working inside my repository.","Implement the task end-to-end with minimal reliable changes.","",u({contract:n,caps:t,scenarios:o,state:i}),"",`Task: ${e}`,"","Requirements:","1) Propose smallest safe implementation.","2) Explain which files you changed and why.","3) Implement production-ready code.","4) Preserve backward compatibility unless explicitly requested.","5) Update tests or add smoke checks.","6) Provide run/verify commands.","7) If assumptions are needed, state briefly and proceed with sensible defaults.","","Output format:","- Plan (short)","- Code changes (by file)","- Tests updated/added","- Commands to run","- Acceptance checklist","","Quality bar:","- No TODO placeholders in final code","- Handle edge cases and errors","- Keep naming/style consistent","- Prefer simple maintainable solutions","","If model is overloaded (resource exhausted), retry with Auto/another model and continue deterministically."].join(`
5
+ `)}function g({task:e,contract:n,caps:t,scenarios:o,state:i}){return["You are my senior software engineer pair.","Implement this task end-to-end in my project.","",u({contract:n,caps:t,scenarios:o,state:i}),"",`Goal: ${e}`,"","Deliverables:","- Short implementation plan","- Exact file-level changes","- Test updates","- Verification commands","- Final acceptance checklist","","Constraints:","- Keep backward compatibility by default","- Make minimal reliable changes","- Handle edge cases and error states","- Keep output concise and actionable","","If you encounter temporary model high-load errors, retry and preserve the same output structure."].join(`
6
+ `)}export{j as buildCursorImplementPrompt,g as buildGenericImplementPrompt,k as loadImplementContext,b as promptInit};
@@ -1,42 +1 @@
1
- import * as fs from "node:fs";
2
- import * as path from "node:path";
3
- import { installInfernoDraftTooling } from "./draftToolingInstall.mjs";
4
-
5
- /**
6
- * VS Code + GitHub Copilot agent hooks (Preview). See:
7
- * https://code.visualstudio.com/docs/copilot/customization/hooks
8
- *
9
- * @param {object} opts
10
- * @param {string} opts.cwd
11
- * @param {string} opts.templatesRoot
12
- * @param {boolean} opts.force
13
- * @param {boolean} opts.silent
14
- * @param {(msg: string) => void} [opts.logOk]
15
- * @param {(msg: string) => void} [opts.logWarn]
16
- */
17
- export function installVsCodeCopilotHooksArtifacts(opts) {
18
- const { cwd, templatesRoot, force, silent } = opts;
19
- const logOk = opts.logOk || (() => {});
20
- const logWarn = opts.logWarn || (() => {});
21
-
22
- function copyFile(src, dst) {
23
- if (fs.existsSync(dst) && !force) {
24
- if (!silent) logWarn("Skipped (exists): " + path.relative(cwd, dst));
25
- return false;
26
- }
27
- fs.mkdirSync(path.dirname(dst), { recursive: true });
28
- fs.copyFileSync(src, dst);
29
- if (!silent) logOk("Created: " + path.relative(cwd, dst));
30
- return true;
31
- }
32
-
33
- installInfernoDraftTooling({ cwd, templatesRoot, force, silent, logOk, logWarn });
34
-
35
- const srcHooks = path.join(templatesRoot, "github-hooks", "infernoflow-drafts.json");
36
- const dstHooks = path.join(cwd, ".github", "hooks", "infernoflow-drafts.json");
37
- const srcHookScript = path.join(templatesRoot, "scripts", "inferno-vscode-copilot-hook.mjs");
38
- const dstHookScript = path.join(cwd, "scripts", "inferno-vscode-copilot-hook.mjs");
39
-
40
- copyFile(srcHooks, dstHooks);
41
- copyFile(srcHookScript, dstHookScript);
42
- }
1
+ import*as e from"node:fs";import*as o from"node:path";import{installInfernoDraftTooling as j}from"./draftToolingInstall.mjs";function h(t){const{cwd:i,templatesRoot:r,force:c,silent:s}=t,f=t.logOk||(()=>{}),l=t.logWarn||(()=>{});function a(d,n){return e.existsSync(n)&&!c?(s||l("Skipped (exists): "+o.relative(i,n)),!1):(e.mkdirSync(o.dirname(n),{recursive:!0}),e.copyFileSync(d,n),s||f("Created: "+o.relative(i,n)),!0)}j({cwd:i,templatesRoot:r,force:c,silent:s,logOk:f,logWarn:l});const p=o.join(r,"github-hooks","infernoflow-drafts.json"),k=o.join(i,".github","hooks","infernoflow-drafts.json"),m=o.join(r,"scripts","inferno-vscode-copilot-hook.mjs"),u=o.join(i,"scripts","inferno-vscode-copilot-hook.mjs");a(p,k),a(m,u)}export{h as installVsCodeCopilotHooksArtifacts};
@@ -86,6 +86,25 @@ const TOOLS = [
86
86
  description: "Apply an infernoflow suggestion JSON. Use after infernoflow_run when you have a JSON response ready to apply.",
87
87
  inputSchema: { type: "object", properties: { json: { type: "string", description: "The JSON suggestion to apply" } }, required: ["json"] }
88
88
  },
89
+ {
90
+ name: "infernoflow_cloud_push",
91
+ description: "CALL THIS AUTOMATICALLY when the developer says 'sync', 'push to cloud', or 'share with the team'. Uploads the local capability contract to infernoflow cloud so the whole team sees the latest state. Requires infernoflow cloud init to have been run first.",
92
+ inputSchema: { type: "object", properties: {
93
+ dryRun: { type: "boolean", description: "If true, show what would be pushed without sending" }
94
+ }}
95
+ },
96
+ {
97
+ name: "infernoflow_cloud_pull",
98
+ description: "CALL THIS AUTOMATICALLY when the developer says 'pull from cloud', 'sync from team', or 'get latest contract'. Downloads the latest capability contract from infernoflow cloud. Warns if there are conflicts between local and remote.",
99
+ inputSchema: { type: "object", properties: {
100
+ force: { type: "boolean", description: "Overwrite local contract even if there are conflicts" }
101
+ }}
102
+ },
103
+ {
104
+ name: "infernoflow_cloud_status",
105
+ description: "Check whether local capabilities are in sync with infernoflow cloud. Call when the developer asks 'are we in sync?' or 'what does the team see?'. Returns local vs remote capability counts and hash comparison.",
106
+ inputSchema: { type: "object", properties: {} }
107
+ },
89
108
  ];
90
109
 
91
110
  // ── git drift detection (inline — no external imports in this template file) ─
@@ -589,6 +608,16 @@ function handleTool(id, name, input) {
589
608
  if (input.ref) parts.push(`--ref "${input.ref}"`);
590
609
  if (input.apply) parts.push("--apply");
591
610
  text = runCmd(parts.join(" "));
611
+ } else if (name === "infernoflow_cloud_push") {
612
+ const parts = ["cloud", "push", "--json"];
613
+ if (input.dryRun) parts.push("--dry-run");
614
+ text = runCmd(parts.join(" "));
615
+ } else if (name === "infernoflow_cloud_pull") {
616
+ const parts = ["cloud", "pull", "--json"];
617
+ if (input.force) parts.push("--force");
618
+ text = runCmd(parts.join(" "));
619
+ } else if (name === "infernoflow_cloud_status") {
620
+ text = runCmd("cloud status --json");
592
621
  } else { return sendError(id, -32601, `Unknown tool: ${name}`); }
593
622
  sendResult(id, { content: [{ type: "text", text: text || "(no output)" }] });
594
623
  } catch (err) { sendError(id, -32000, err.message); }
@@ -0,0 +1,67 @@
1
+ # infernoflow GitHub App
2
+
3
+ A GitHub App that automatically posts capability drift analysis on every PR — without needing a GitHub Actions workflow in each repo.
4
+
5
+ ## How it differs from the Actions workflow
6
+
7
+ | | GitHub Actions workflow | GitHub App |
8
+ |---|---|---|
9
+ | Setup per repo | Copy `.github/workflows/infernoflow.yml` | Install app once, covers all repos |
10
+ | Token needed | Yes (needs `GITHUB_TOKEN`) | No (app has its own token) |
11
+ | Works on forks | Limited | Yes |
12
+ | Private repos | Needs repo write permission | Yes, with install approval |
13
+
14
+ ## Install
15
+
16
+ ### Option 1 — Install the hosted app (fastest)
17
+
18
+ Visit: **https://github.com/apps/infernoflow**
19
+
20
+ Click Install → select repos → done. No configuration needed.
21
+
22
+ ### Option 2 — Self-host your own app (full control)
23
+
24
+ 1. Go to: **https://github.com/settings/apps/new**
25
+
26
+ 2. Paste the contents of `app-manifest.json` (update `YOUR_DOMAIN` first):
27
+ ```
28
+ Name: infernoflow
29
+ Homepage: https://github.com/ronmiz/infernoflow
30
+ Webhook URL: https://your-worker.workers.dev/github/webhook
31
+ Permissions:
32
+ Pull requests: Read & Write
33
+ Contents: Read
34
+ Checks: Read & Write
35
+ Events:
36
+ Pull request, Push
37
+ ```
38
+
39
+ 3. After creating, note your App ID and generate a Private Key (download the `.pem` file).
40
+
41
+ 4. Deploy the GitHub App worker (see `cloud-backend/github-app-worker.mjs`):
42
+ ```bash
43
+ cd cloud-backend
44
+ GITHUB_APP_ID=12345 \
45
+ GITHUB_PRIVATE_KEY="$(cat your-app.pem)" \
46
+ npx wrangler deploy github-app-worker.mjs --name infernoflow-app
47
+ ```
48
+
49
+ 5. Install the app on your repos at:
50
+ `https://github.com/apps/YOUR_APP_NAME`
51
+
52
+ ## What happens on a PR
53
+
54
+ When a PR is opened or updated, the app:
55
+
56
+ 1. Checks out the `inferno/contract.json` from both base and head branches
57
+ 2. Runs a capability diff
58
+ 3. Posts a comment with the drift analysis (same format as `infernoflow pr-comment`)
59
+ 4. Sets a GitHub Check (green ✔ / yellow ⚠ / red ✗)
60
+
61
+ ## Environment variables
62
+
63
+ | Variable | Description |
64
+ |---|---|
65
+ | `GITHUB_APP_ID` | Your app's numeric ID |
66
+ | `GITHUB_PRIVATE_KEY` | PEM-encoded private key |
67
+ | `GITHUB_WEBHOOK_SECRET` | Optional — validates webhook payloads |
@@ -0,0 +1,20 @@
1
+ {
2
+ "name": "infernoflow",
3
+ "description": "Automatic capability drift analysis on every PR — powered by infernoflow",
4
+ "url": "https://github.com/ronmiz/infernoflow",
5
+ "hook_attributes": {
6
+ "url": "https://infernoflow-app.YOUR_DOMAIN.workers.dev/github/webhook"
7
+ },
8
+ "redirect_url": "https://infernoflow-app.YOUR_DOMAIN.workers.dev/github/callback",
9
+ "public": true,
10
+ "default_permissions": {
11
+ "pull_requests": "write",
12
+ "contents": "read",
13
+ "checks": "write",
14
+ "metadata": "read"
15
+ },
16
+ "default_events": [
17
+ "pull_request",
18
+ "push"
19
+ ]
20
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "infernoflow",
3
- "version": "0.32.8",
3
+ "version": "0.32.9",
4
4
  "description": "The forge for liquid code - keep capabilities, contracts, and docs in sync.",
5
5
  "type": "module",
6
6
  "bin": {