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.
- 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 -320
- 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,103 +1,7 @@
|
|
|
1
|
-
import fs from "node:
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
buildGenericImplementPrompt,
|
|
9
|
-
} from "../ui/prompts.mjs";
|
|
10
|
-
|
|
11
|
-
function getFlagValue(args, flag) {
|
|
12
|
-
const idx = args.indexOf(flag);
|
|
13
|
-
return idx !== -1 && args[idx + 1] ? args[idx + 1] : null;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
function extractTask(args) {
|
|
17
|
-
const skipNextFor = new Set(["--mode"]);
|
|
18
|
-
const parts = [];
|
|
19
|
-
for (let i = 0; i < args.length; i += 1) {
|
|
20
|
-
const token = args[i];
|
|
21
|
-
if (token.startsWith("-")) {
|
|
22
|
-
if (skipNextFor.has(token)) i += 1;
|
|
23
|
-
continue;
|
|
24
|
-
}
|
|
25
|
-
if (i === 0) continue; // command name
|
|
26
|
-
parts.push(token);
|
|
27
|
-
}
|
|
28
|
-
return parts.join(" ").trim();
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function copyToClipboard(text) {
|
|
32
|
-
try {
|
|
33
|
-
const p = process.platform;
|
|
34
|
-
if (p === "win32") execSync("clip", { input: text });
|
|
35
|
-
else if (p === "darwin") execSync("pbcopy", { input: text });
|
|
36
|
-
else {
|
|
37
|
-
try { execSync("xclip -selection clipboard", { input: text }); }
|
|
38
|
-
catch { execSync("xsel --clipboard --input", { input: text }); }
|
|
39
|
-
}
|
|
40
|
-
return true;
|
|
41
|
-
} catch {
|
|
42
|
-
return false;
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export async function implementCommand(args = []) {
|
|
47
|
-
header("implement");
|
|
48
|
-
|
|
49
|
-
const cwd = process.cwd();
|
|
50
|
-
const infernoDir = path.join(cwd, "inferno");
|
|
51
|
-
if (!fs.existsSync(infernoDir)) {
|
|
52
|
-
errorAndExit("inferno/ not found", "Run: infernoflow init");
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
const mode = (getFlagValue(args, "--mode") || "both").toLowerCase();
|
|
56
|
-
const copyFlag = args.includes("--copy") || args.includes("-c");
|
|
57
|
-
if (!["cursor", "generic", "both"].includes(mode)) {
|
|
58
|
-
errorAndExit("Invalid --mode value", "Use: --mode cursor|generic|both");
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
const rawTask = extractTask(args);
|
|
62
|
-
if (!rawTask) {
|
|
63
|
-
errorAndExit("No task provided", 'Usage: infernoflow implement "your task description"');
|
|
64
|
-
}
|
|
65
|
-
|
|
66
|
-
const context = loadImplementContext(cwd);
|
|
67
|
-
const cursorPrompt = buildCursorImplementPrompt({ task: rawTask, ...context });
|
|
68
|
-
const genericPrompt = buildGenericImplementPrompt({ task: rawTask, ...context });
|
|
69
|
-
|
|
70
|
-
info(`Task: ${cyan(rawTask)}`);
|
|
71
|
-
info(`Mode: ${cyan(mode)}`);
|
|
72
|
-
warn("If you hit model high-load/resource-exhausted, retry with Auto/another model.");
|
|
73
|
-
|
|
74
|
-
if (mode === "cursor" || mode === "both") {
|
|
75
|
-
section("Cursor Agent Prompt");
|
|
76
|
-
console.log();
|
|
77
|
-
console.log(gray("─".repeat(50)));
|
|
78
|
-
console.log(cursorPrompt);
|
|
79
|
-
console.log(gray("─".repeat(50)));
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
if (mode === "generic" || mode === "both") {
|
|
83
|
-
section("Generic Agent Prompt");
|
|
84
|
-
console.log();
|
|
85
|
-
console.log(gray("─".repeat(50)));
|
|
86
|
-
console.log(genericPrompt);
|
|
87
|
-
console.log(gray("─".repeat(50)));
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
if (copyFlag) {
|
|
91
|
-
const textToCopy =
|
|
92
|
-
mode === "cursor"
|
|
93
|
-
? cursorPrompt
|
|
94
|
-
: mode === "generic"
|
|
95
|
-
? genericPrompt
|
|
96
|
-
: `## Cursor Agent Prompt\n\n${cursorPrompt}\n\n## Generic Agent Prompt\n\n${genericPrompt}`;
|
|
97
|
-
const ok = copyToClipboard(textToCopy);
|
|
98
|
-
if (ok) info(`Copied ${mode} prompt${mode === "both" ? "s" : ""} to clipboard.`);
|
|
99
|
-
else warn("Clipboard copy failed. Copy from terminal output.");
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
console.log();
|
|
103
|
-
}
|
|
1
|
+
import y from"node:fs";import b from"node:path";import{execSync as c}from"node:child_process";import{header as k,section as f,info as m,warn as d,cyan as h,gray as s,errorAndExit as u}from"../ui/output.mjs";import{loadImplementContext as w,buildCursorImplementPrompt as x,buildGenericImplementPrompt as C}from"../ui/prompts.mjs";function P(o,n){const t=o.indexOf(n);return t!==-1&&o[t+1]?o[t+1]:null}function A(o){const n=new Set(["--mode"]),t=[];for(let e=0;e<o.length;e+=1){const r=o[e];if(r.startsWith("-")){n.has(r)&&(e+=1);continue}e!==0&&t.push(r)}return t.join(" ").trim()}function $(o){try{const n=process.platform;if(n==="win32")c("clip",{input:o});else if(n==="darwin")c("pbcopy",{input:o});else try{c("xclip -selection clipboard",{input:o})}catch{c("xsel --clipboard --input",{input:o})}return!0}catch{return!1}}async function j(o=[]){k("implement");const n=process.cwd(),t=b.join(n,"inferno");y.existsSync(t)||u("inferno/ not found","Run: infernoflow init");const e=(P(o,"--mode")||"both").toLowerCase(),r=o.includes("--copy")||o.includes("-c");["cursor","generic","both"].includes(e)||u("Invalid --mode value","Use: --mode cursor|generic|both");const i=A(o);i||u("No task provided",'Usage: infernoflow implement "your task description"');const a=w(n),l=x({task:i,...a}),p=C({task:i,...a});if(m(`Task: ${h(i)}`),m(`Mode: ${h(e)}`),d("If you hit model high-load/resource-exhausted, retry with Auto/another model."),(e==="cursor"||e==="both")&&(f("Cursor Agent Prompt"),console.log(),console.log(s("\u2500".repeat(50))),console.log(l),console.log(s("\u2500".repeat(50)))),(e==="generic"||e==="both")&&(f("Generic Agent Prompt"),console.log(),console.log(s("\u2500".repeat(50))),console.log(p),console.log(s("\u2500".repeat(50)))),r){const g=e==="cursor"?l:e==="generic"?p:`## Cursor Agent Prompt
|
|
2
|
+
|
|
3
|
+
${l}
|
|
4
|
+
|
|
5
|
+
## Generic Agent Prompt
|
|
6
|
+
|
|
7
|
+
${p}`;$(g)?m(`Copied ${e} prompt${e==="both"?"s":""} to clipboard.`):d("Clipboard copy failed. Copy from terminal output.")}console.log()}export{j as implementCommand};
|
|
@@ -1,482 +1,30 @@
|
|
|
1
|
-
import * as
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
import {
|
|
7
|
-
discoverProjectSignals,
|
|
8
|
-
reviewCapabilitiesInteractive,
|
|
9
|
-
writeAdoptionBaseline,
|
|
10
|
-
buildAdoptionReport,
|
|
11
|
-
summarizeCapabilities,
|
|
12
|
-
buildSignalsReport,
|
|
13
|
-
} from "./adopt.mjs";
|
|
14
|
-
import { installCursorHooksArtifacts } from "../cursorHooksInstall.mjs";
|
|
15
|
-
import { installVsCodeCopilotHooksArtifacts } from "../vsCodeCopilotHooksInstall.mjs";
|
|
16
|
-
|
|
17
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
18
|
-
|
|
19
|
-
function getTemplatesRoot() {
|
|
20
|
-
return path.resolve(__dirname, "../../templates");
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
function ask(rl, question, defaultVal = "") {
|
|
24
|
-
return new Promise(resolve => {
|
|
25
|
-
const hint = defaultVal ? gray(` (${defaultVal})`) : "";
|
|
26
|
-
rl.question(` ${question}${hint}: `, answer => {
|
|
27
|
-
resolve(answer.trim() || defaultVal);
|
|
28
|
-
});
|
|
29
|
-
});
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function getArgValue(args, ...flags) {
|
|
33
|
-
for (const flag of flags) {
|
|
34
|
-
const i = args.indexOf(flag);
|
|
35
|
-
if (i !== -1 && args[i + 1] && !args[i + 1].startsWith("-")) return args[i + 1];
|
|
36
|
-
}
|
|
37
|
-
return null;
|
|
38
|
-
}
|
|
39
|
-
|
|
40
|
-
function copyFile(src, dst, force, silent = false) {
|
|
41
|
-
if (fs.existsSync(dst) && !force) {
|
|
42
|
-
if (!silent) warn("Skipped (exists): " + path.relative(process.cwd(), dst));
|
|
43
|
-
return false;
|
|
44
|
-
}
|
|
45
|
-
fs.mkdirSync(path.dirname(dst), { recursive: true });
|
|
46
|
-
fs.copyFileSync(src, dst);
|
|
47
|
-
if (!silent) ok("Created: " + cyan(path.relative(process.cwd(), dst)));
|
|
48
|
-
return true;
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
function copyDirDeep(srcDir, dstDir, force) {
|
|
52
|
-
fs.mkdirSync(dstDir, { recursive: true });
|
|
53
|
-
for (const entry of fs.readdirSync(srcDir, { withFileTypes: true })) {
|
|
54
|
-
const src = path.join(srcDir, entry.name);
|
|
55
|
-
const dst = path.join(dstDir, entry.name);
|
|
56
|
-
if (entry.isDirectory()) copyDirDeep(src, dst, force);
|
|
57
|
-
else copyFile(src, dst, force);
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function upsertScripts(cwd, silent = false) {
|
|
62
|
-
const pkgPath = path.join(cwd, "package.json");
|
|
63
|
-
if (!fs.existsSync(pkgPath)) return;
|
|
64
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
65
|
-
pkg.scripts = pkg.scripts || {};
|
|
66
|
-
let changed = false;
|
|
67
|
-
const toAdd = {
|
|
68
|
-
"inferno:check": "infernoflow check",
|
|
69
|
-
"inferno:status": "infernoflow status",
|
|
70
|
-
"inferno:gate": "infernoflow doc-gate",
|
|
71
|
-
"inferno:impact": "infernoflow pr-impact --json",
|
|
72
|
-
"inferno:sync": "infernoflow sync --auto --json",
|
|
73
|
-
"inferno:run": "infernoflow run \"sync check\" --provider auto --json",
|
|
74
|
-
"inferno:hooks": "node scripts/inferno-install-hooks.mjs"
|
|
75
|
-
};
|
|
76
|
-
for (const [k, v] of Object.entries(toAdd)) {
|
|
77
|
-
if (!pkg.scripts[k]) { pkg.scripts[k] = v; changed = true; }
|
|
78
|
-
}
|
|
79
|
-
if (changed) {
|
|
80
|
-
fs.writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + "\n", "utf8");
|
|
81
|
-
if (!silent) ok("Updated " + cyan("package.json") + " scripts");
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
function detectProjectName(cwd) {
|
|
86
|
-
const pkgPath = path.join(cwd, "package.json");
|
|
87
|
-
if (fs.existsSync(pkgPath)) {
|
|
88
|
-
try {
|
|
89
|
-
const pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
|
|
90
|
-
if (pkg.name) return pkg.name.replace(/[^a-z0-9_-]/gi, "_");
|
|
91
|
-
} catch {}
|
|
92
|
-
}
|
|
93
|
-
return path.basename(cwd);
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
function writeContract(contractPath, policyId, capabilities) {
|
|
97
|
-
const contract = {
|
|
98
|
-
policyId,
|
|
99
|
-
policyVersion: 1,
|
|
100
|
-
capabilities,
|
|
101
|
-
rules: {
|
|
102
|
-
docsRequiredOnCapabilityChange: true,
|
|
103
|
-
requireScenarioForEachCapability: true,
|
|
104
|
-
requireChangelogOnCapabilityChange: true
|
|
105
|
-
}
|
|
106
|
-
};
|
|
107
|
-
fs.writeFileSync(contractPath, JSON.stringify(contract, null, 2) + "\n");
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function writeCapabilities(capsPath, capabilities) {
|
|
111
|
-
const registry = {
|
|
112
|
-
schemaVersion: 1,
|
|
113
|
-
capabilities: capabilities.map(id => ({
|
|
114
|
-
id,
|
|
115
|
-
title: id.replace(/([A-Z])/g, " $1").trim(),
|
|
116
|
-
since: "0.1.0"
|
|
117
|
-
}))
|
|
118
|
-
};
|
|
119
|
-
fs.writeFileSync(capsPath, JSON.stringify(registry, null, 2) + "\n");
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
function writeScenario(scenariosDir, capabilities) {
|
|
123
|
-
fs.mkdirSync(scenariosDir, { recursive: true });
|
|
124
|
-
const scenario = {
|
|
125
|
-
scenarioId: "happy_path",
|
|
126
|
-
description: "Basic happy-path flow covering all capabilities",
|
|
127
|
-
capabilitiesCovered: capabilities,
|
|
128
|
-
steps: capabilities.map(c => ({
|
|
129
|
-
action: c,
|
|
130
|
-
expect: `${c} works as expected`
|
|
131
|
-
}))
|
|
132
|
-
};
|
|
133
|
-
fs.writeFileSync(
|
|
134
|
-
path.join(scenariosDir, "happy_path.json"),
|
|
135
|
-
JSON.stringify(scenario, null, 2) + "\n"
|
|
136
|
-
);
|
|
137
|
-
}
|
|
138
|
-
|
|
139
|
-
function writeChangelog(changelogPath, policyId) {
|
|
140
|
-
const content = `# Changelog — ${policyId}
|
|
1
|
+
import*as r from"node:fs";import*as t from"node:path";import*as G from"node:readline";import{fileURLToPath as ne}from"node:url";import{header as oe,ok as d,warn as $,done as D,nextSteps as ie,cyan as l,yellow as N,gray as S}from"../ui/output.mjs";import{discoverProjectSignals as I,reviewCapabilitiesInteractive as te,writeAdoptionBaseline as se,buildAdoptionReport as re,summarizeCapabilities as ae,buildSignalsReport as ce}from"./adopt.mjs";import{installCursorHooksArtifacts as le}from"../cursorHooksInstall.mjs";import{installVsCodeCopilotHooksArtifacts as pe}from"../vsCodeCopilotHooksInstall.mjs";const fe=t.dirname(ne(import.meta.url));function de(){return t.resolve(fe,"../../templates")}function P(e,n,i=""){return new Promise(s=>{const u=i?S(` (${i})`):"";e.question(` ${n}${u}: `,y=>{s(y.trim()||i)})})}function J(e,...n){for(const i of n){const s=e.indexOf(i);if(s!==-1&&e[s+1]&&!e[s+1].startsWith("-"))return e[s+1]}return null}function T(e,n,i,s=!1){return r.existsSync(n)&&!i?(s||$("Skipped (exists): "+t.relative(process.cwd(),n)),!1):(r.mkdirSync(t.dirname(n),{recursive:!0}),r.copyFileSync(e,n),s||d("Created: "+l(t.relative(process.cwd(),n))),!0)}function ue(e,n,i){r.mkdirSync(n,{recursive:!0});for(const s of r.readdirSync(e,{withFileTypes:!0})){const u=t.join(e,s.name),y=t.join(n,s.name);s.isDirectory()?ue(u,y,i):T(u,y,i)}}function me(e,n=!1){const i=t.join(e,"package.json");if(!r.existsSync(i))return;const s=JSON.parse(r.readFileSync(i,"utf8"));s.scripts=s.scripts||{};let u=!1;const y={"inferno:check":"infernoflow check","inferno:status":"infernoflow status","inferno:gate":"infernoflow doc-gate","inferno:impact":"infernoflow pr-impact --json","inferno:sync":"infernoflow sync --auto --json","inferno:run":'infernoflow run "sync check" --provider auto --json',"inferno:hooks":"node scripts/inferno-install-hooks.mjs"};for(const[g,v]of Object.entries(y))s.scripts[g]||(s.scripts[g]=v,u=!0);u&&(r.writeFileSync(i,JSON.stringify(s,null,2)+`
|
|
2
|
+
`,"utf8"),n||d("Updated "+l("package.json")+" scripts"))}function U(e){const n=t.join(e,"package.json");if(r.existsSync(n))try{const i=JSON.parse(r.readFileSync(n,"utf8"));if(i.name)return i.name.replace(/[^a-z0-9_-]/gi,"_")}catch{}return t.basename(e)}function ye(e,n,i){const s={policyId:n,policyVersion:1,capabilities:i,rules:{docsRequiredOnCapabilityChange:!0,requireScenarioForEachCapability:!0,requireChangelogOnCapabilityChange:!0}};r.writeFileSync(e,JSON.stringify(s,null,2)+`
|
|
3
|
+
`)}function ge(e,n){const i={schemaVersion:1,capabilities:n.map(s=>({id:s,title:s.replace(/([A-Z])/g," $1").trim(),since:"0.1.0"}))};r.writeFileSync(e,JSON.stringify(i,null,2)+`
|
|
4
|
+
`)}function we(e,n){r.mkdirSync(e,{recursive:!0});const i={scenarioId:"happy_path",description:"Basic happy-path flow covering all capabilities",capabilitiesCovered:n,steps:n.map(s=>({action:s,expect:`${s} works as expected`}))};r.writeFileSync(t.join(e,"happy_path.json"),JSON.stringify(i,null,2)+`
|
|
5
|
+
`)}function K(e,n){const i=`# Changelog \u2014 ${n}
|
|
141
6
|
|
|
142
7
|
## Unreleased
|
|
143
8
|
|
|
144
9
|
- Initial capabilities defined
|
|
145
10
|
|
|
146
|
-
## 0.1.0
|
|
11
|
+
## 0.1.0 \u2014 Initial release
|
|
147
12
|
|
|
148
13
|
- Project initialized with infernoflow
|
|
149
|
-
`;
|
|
150
|
-
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
const tmpl = tmplMod?.getTemplate(templateName);
|
|
167
|
-
if (!tmpl) {
|
|
168
|
-
const available = tmplMod ? tmplMod.listTemplates().map(t => t.name).join(", ") : "rest-api, nextjs, cli, graphql, monorepo";
|
|
169
|
-
warn(`Unknown template: ${templateName}. Available: ${available}`);
|
|
170
|
-
process.exit(1);
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
const infernoDir = path.join(cwd, "inferno");
|
|
174
|
-
const scenDir = path.join(infernoDir, "scenarios");
|
|
175
|
-
if (!fs.existsSync(infernoDir)) fs.mkdirSync(infernoDir, { recursive: true });
|
|
176
|
-
if (!fs.existsSync(scenDir)) fs.mkdirSync(scenDir, { recursive: true });
|
|
177
|
-
|
|
178
|
-
const policyId = detectProjectName(cwd);
|
|
179
|
-
const caps = tmpl.capabilities;
|
|
180
|
-
|
|
181
|
-
// Write contract.json
|
|
182
|
-
fs.writeFileSync(path.join(infernoDir, "contract.json"), JSON.stringify({
|
|
183
|
-
policyId, policyVersion: 1,
|
|
184
|
-
capabilities: caps.map(c => c.id),
|
|
185
|
-
}, null, 2) + "\n");
|
|
186
|
-
|
|
187
|
-
// Write capabilities.json
|
|
188
|
-
fs.writeFileSync(path.join(infernoDir, "capabilities.json"), JSON.stringify({
|
|
189
|
-
capabilities: caps.map(c => ({
|
|
190
|
-
id: c.id, description: c.description,
|
|
191
|
-
since: new Date().toISOString().slice(0, 10), source: `template:${templateName}`,
|
|
192
|
-
})),
|
|
193
|
-
}, null, 2) + "\n");
|
|
194
|
-
|
|
195
|
-
// Write one scenario per capability
|
|
196
|
-
for (const cap of caps) {
|
|
197
|
-
fs.writeFileSync(path.join(scenDir, `${cap.id}.json`), JSON.stringify({
|
|
198
|
-
id: `${cap.id}-happy-path`, capability: cap.id,
|
|
199
|
-
description: `Happy path for ${cap.description || cap.id}`,
|
|
200
|
-
steps: [
|
|
201
|
-
{ action: "invoke", target: cap.id, input: {} },
|
|
202
|
-
{ action: "assert", field: "status", value: "success" },
|
|
203
|
-
],
|
|
204
|
-
capabilitiesCovered: [cap.id],
|
|
205
|
-
}, null, 2) + "\n");
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
// Write CHANGELOG.md
|
|
209
|
-
writeChangelog(path.join(infernoDir, "CHANGELOG.md"), policyId);
|
|
210
|
-
|
|
211
|
-
// Write CONTEXT.md hint
|
|
212
|
-
fs.writeFileSync(path.join(infernoDir, "CONTEXT.md"),
|
|
213
|
-
`# ${policyId} — infernoflow context\n\n> Template: ${templateName} — ${tmpl.description}\n\n## Hint\n${tmpl.contextHint}\n\n## Capabilities (${caps.length})\n${caps.map(c => `- \`${c.id}\`: ${c.description}`).join("\n")}\n`
|
|
214
|
-
);
|
|
215
|
-
|
|
216
|
-
if (tmpl.scripts) {
|
|
217
|
-
info(`Suggested package.json scripts for this template:`);
|
|
218
|
-
Object.entries(tmpl.scripts).forEach(([k, v]) => console.log(` ${bold(k)}: ${gray(v)}`));
|
|
219
|
-
console.log();
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
done(`Initialised from template ${bold(cyan(templateName))} — ${bold(String(caps.length))} capabilities`);
|
|
223
|
-
console.log();
|
|
224
|
-
info(`Run ${cyan("infernoflow vibe")} to start vibe coding mode`);
|
|
225
|
-
console.log();
|
|
226
|
-
return;
|
|
227
|
-
}
|
|
228
|
-
const cursorHooks = args.includes("--cursor-hooks");
|
|
229
|
-
const vscodeCopilotHooks = args.includes("--vscode-copilot-hooks");
|
|
230
|
-
const reportJson = args.includes("--report-json");
|
|
231
|
-
const reportJsonOnly = args.includes("--report-json-only");
|
|
232
|
-
const reportHumanOnly = args.includes("--report-human-only");
|
|
233
|
-
const langOverride = getArgValue(args, "--lang");
|
|
234
|
-
const frameworkOverride = getArgValue(args, "--framework");
|
|
235
|
-
const projectTypeOverride = getArgValue(args, "--project-type");
|
|
236
|
-
const silent = reportJsonOnly;
|
|
237
|
-
|
|
238
|
-
if (reportJsonOnly && reportHumanOnly) {
|
|
239
|
-
console.error("Error: --report-json-only and --report-human-only cannot be used together.");
|
|
240
|
-
process.exit(1);
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
if (!silent) {
|
|
244
|
-
header("init");
|
|
245
|
-
}
|
|
246
|
-
|
|
247
|
-
const infernoDir = path.join(cwd, "inferno");
|
|
248
|
-
const workflowsDir = path.join(cwd, ".github", "workflows");
|
|
249
|
-
if (fs.existsSync(infernoDir) && !force) {
|
|
250
|
-
if (silent) {
|
|
251
|
-
console.log(JSON.stringify({ ok: false, error: "inferno_exists", hint: "Use --force to overwrite" }, null, 2));
|
|
252
|
-
process.exit(1);
|
|
253
|
-
}
|
|
254
|
-
warn("inferno/ already exists. Use --force to overwrite.");
|
|
255
|
-
console.log();
|
|
256
|
-
process.exit(0);
|
|
257
|
-
}
|
|
258
|
-
|
|
259
|
-
const detectedName = detectProjectName(cwd);
|
|
260
|
-
const defaultCaps = "CreateTask, ReadTasks, UpdateTask, ToggleComplete, DeleteTask";
|
|
261
|
-
|
|
262
|
-
let policyId = detectedName;
|
|
263
|
-
let capabilities = defaultCaps.split(",").map(c => c.trim());
|
|
264
|
-
|
|
265
|
-
if (adopt) {
|
|
266
|
-
const profileOverrides = {
|
|
267
|
-
language: langOverride || undefined,
|
|
268
|
-
framework: frameworkOverride || undefined,
|
|
269
|
-
projectType: projectTypeOverride || undefined,
|
|
270
|
-
};
|
|
271
|
-
let signals = discoverProjectSignals(cwd, profileOverrides);
|
|
272
|
-
if (!yes && !reportJsonOnly) {
|
|
273
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
274
|
-
const profile = signals.developmentProfile || {};
|
|
275
|
-
const detected = profile.detected || {};
|
|
276
|
-
console.log(gray(" Review inferred development stack (press Enter to accept detected values)\n"));
|
|
277
|
-
const language = await ask(rl, "Language", profile.language || detected.language || "unknown");
|
|
278
|
-
const framework = await ask(rl, "Framework", profile.framework || detected.framework || "unknown");
|
|
279
|
-
const projectType = await ask(rl, "Project type", profile.projectType || detected.projectType || "unknown");
|
|
280
|
-
rl.close();
|
|
281
|
-
signals = discoverProjectSignals(cwd, { language, framework, projectType });
|
|
282
|
-
}
|
|
283
|
-
const inferred = signals.capabilities;
|
|
284
|
-
const summarized = summarizeCapabilities(inferred);
|
|
285
|
-
if (reportJsonOnly) {
|
|
286
|
-
console.log(
|
|
287
|
-
JSON.stringify(
|
|
288
|
-
{
|
|
289
|
-
mode: "adopt",
|
|
290
|
-
policyId: detectedName,
|
|
291
|
-
inferredCapabilities: summarized,
|
|
292
|
-
components: signals.components,
|
|
293
|
-
displayFields: signals.displayFields,
|
|
294
|
-
externalLibraries: signals.externalLibraries,
|
|
295
|
-
uiLayout: signals.uiLayout,
|
|
296
|
-
styling: signals.styling,
|
|
297
|
-
developmentProfile: signals.developmentProfile,
|
|
298
|
-
apiCalls: signals.apiCalls,
|
|
299
|
-
},
|
|
300
|
-
null,
|
|
301
|
-
2
|
|
302
|
-
)
|
|
303
|
-
);
|
|
304
|
-
} else {
|
|
305
|
-
console.log();
|
|
306
|
-
console.log(gray(buildAdoptionReport(inferred)));
|
|
307
|
-
console.log();
|
|
308
|
-
console.log(gray(buildSignalsReport(signals)));
|
|
309
|
-
console.log();
|
|
310
|
-
if (reportJson && !reportHumanOnly) {
|
|
311
|
-
console.log(
|
|
312
|
-
JSON.stringify(
|
|
313
|
-
{
|
|
314
|
-
mode: "adopt",
|
|
315
|
-
policyId: detectedName,
|
|
316
|
-
inferredCapabilities: summarized,
|
|
317
|
-
components: signals.components,
|
|
318
|
-
displayFields: signals.displayFields,
|
|
319
|
-
externalLibraries: signals.externalLibraries,
|
|
320
|
-
uiLayout: signals.uiLayout,
|
|
321
|
-
styling: signals.styling,
|
|
322
|
-
developmentProfile: signals.developmentProfile,
|
|
323
|
-
apiCalls: signals.apiCalls,
|
|
324
|
-
},
|
|
325
|
-
null,
|
|
326
|
-
2
|
|
327
|
-
)
|
|
328
|
-
);
|
|
329
|
-
console.log();
|
|
330
|
-
}
|
|
331
|
-
}
|
|
332
|
-
const reviewed = await reviewCapabilitiesInteractive(inferred, yes);
|
|
333
|
-
policyId = detectedName;
|
|
334
|
-
capabilities = reviewed.map((c) => c.id);
|
|
335
|
-
} else if (!yes) {
|
|
336
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
337
|
-
console.log(gray(" Press Enter to accept defaults\n"));
|
|
338
|
-
policyId = await ask(rl, "Project / policy name", detectedName);
|
|
339
|
-
const capsRaw = await ask(rl, "Capabilities (comma-separated)", defaultCaps);
|
|
340
|
-
capabilities = capsRaw.split(",").map(c => c.trim()).filter(Boolean);
|
|
341
|
-
rl.close();
|
|
342
|
-
console.log();
|
|
343
|
-
}
|
|
344
|
-
|
|
345
|
-
// Write files
|
|
346
|
-
fs.mkdirSync(infernoDir, { recursive: true });
|
|
347
|
-
|
|
348
|
-
if (adopt) {
|
|
349
|
-
const capDetails = capabilities.map((id) => ({
|
|
350
|
-
id,
|
|
351
|
-
title: id.replace(/([A-Z])/g, " $1").trim(),
|
|
352
|
-
}));
|
|
353
|
-
const signals = discoverProjectSignals(cwd, {
|
|
354
|
-
language: langOverride || undefined,
|
|
355
|
-
framework: frameworkOverride || undefined,
|
|
356
|
-
projectType: projectTypeOverride || undefined,
|
|
357
|
-
});
|
|
358
|
-
writeAdoptionBaseline(infernoDir, policyId, capDetails, signals);
|
|
359
|
-
if (!silent) {
|
|
360
|
-
ok("Created: " + cyan("inferno/contract.json"));
|
|
361
|
-
ok("Created: " + cyan("inferno/capabilities.json"));
|
|
362
|
-
ok("Created: " + cyan("inferno/scenarios/adoption_baseline.json"));
|
|
363
|
-
ok("Created: " + cyan("inferno/adoption_profile.json"));
|
|
364
|
-
ok("Created: " + cyan("inferno/CHANGELOG.md"));
|
|
365
|
-
}
|
|
366
|
-
} else {
|
|
367
|
-
writeContract(path.join(infernoDir, "contract.json"), policyId, capabilities);
|
|
368
|
-
if (!silent) ok("Created: " + cyan("inferno/contract.json"));
|
|
369
|
-
|
|
370
|
-
writeCapabilities(path.join(infernoDir, "capabilities.json"), capabilities);
|
|
371
|
-
if (!silent) ok("Created: " + cyan("inferno/capabilities.json"));
|
|
372
|
-
|
|
373
|
-
writeScenario(path.join(infernoDir, "scenarios"), capabilities);
|
|
374
|
-
if (!silent) ok("Created: " + cyan("inferno/scenarios/happy_path.json"));
|
|
375
|
-
|
|
376
|
-
writeChangelog(path.join(infernoDir, "CHANGELOG.md"), policyId);
|
|
377
|
-
if (!silent) ok("Created: " + cyan("inferno/CHANGELOG.md"));
|
|
378
|
-
}
|
|
379
|
-
|
|
380
|
-
// Copy doc-gate script
|
|
381
|
-
const templates = getTemplatesRoot();
|
|
382
|
-
const srcScript = path.join(templates, "scripts", "inferno-doc-gate.mjs");
|
|
383
|
-
const dstScript = path.join(cwd, "scripts", "inferno-doc-gate.mjs");
|
|
384
|
-
copyFile(srcScript, dstScript, force, silent);
|
|
385
|
-
const srcHookScript = path.join(templates, "scripts", "inferno-install-hooks.mjs");
|
|
386
|
-
const dstHookScript = path.join(cwd, "scripts", "inferno-install-hooks.mjs");
|
|
387
|
-
copyFile(srcHookScript, dstHookScript, force, silent);
|
|
388
|
-
const srcWorkflow = path.join(templates, "ci", "github-inferno-check.yml");
|
|
389
|
-
const dstWorkflow = path.join(workflowsDir, "infernoflow-check.yml");
|
|
390
|
-
copyFile(srcWorkflow, dstWorkflow, force, silent);
|
|
391
|
-
|
|
392
|
-
upsertScripts(cwd, silent);
|
|
393
|
-
|
|
394
|
-
if (cursorHooks) {
|
|
395
|
-
installCursorHooksArtifacts({
|
|
396
|
-
cwd,
|
|
397
|
-
templatesRoot: templates,
|
|
398
|
-
force,
|
|
399
|
-
silent,
|
|
400
|
-
logOk: (msg) => {
|
|
401
|
-
if (!silent) ok(msg);
|
|
402
|
-
},
|
|
403
|
-
logWarn: (msg) => {
|
|
404
|
-
if (!silent) warn(msg);
|
|
405
|
-
},
|
|
406
|
-
});
|
|
407
|
-
}
|
|
408
|
-
if (vscodeCopilotHooks) {
|
|
409
|
-
installVsCodeCopilotHooksArtifacts({
|
|
410
|
-
cwd,
|
|
411
|
-
templatesRoot: templates,
|
|
412
|
-
force,
|
|
413
|
-
silent,
|
|
414
|
-
logOk: (msg) => {
|
|
415
|
-
if (!silent) ok(msg);
|
|
416
|
-
},
|
|
417
|
-
logWarn: (msg) => {
|
|
418
|
-
if (!silent) warn(msg);
|
|
419
|
-
},
|
|
420
|
-
});
|
|
421
|
-
}
|
|
422
|
-
|
|
423
|
-
if (adopt) {
|
|
424
|
-
const statePath = path.join(infernoDir, "context-state.json");
|
|
425
|
-
let state = {};
|
|
426
|
-
try {
|
|
427
|
-
state = JSON.parse(fs.readFileSync(statePath, "utf8"));
|
|
428
|
-
} catch {}
|
|
429
|
-
const signals = discoverProjectSignals(cwd, {
|
|
430
|
-
language: langOverride || undefined,
|
|
431
|
-
framework: frameworkOverride || undefined,
|
|
432
|
-
projectType: projectTypeOverride || undefined,
|
|
433
|
-
});
|
|
434
|
-
state.stack = signals.developmentProfile;
|
|
435
|
-
fs.writeFileSync(statePath, JSON.stringify(state, null, 2) + "\n", "utf8");
|
|
436
|
-
if (!silent) ok("Created: " + cyan("inferno/context-state.json"));
|
|
437
|
-
}
|
|
438
|
-
|
|
439
|
-
if (!silent) {
|
|
440
|
-
done("infernoflow initialized!");
|
|
441
|
-
|
|
442
|
-
// AI provider nudge — show once at init if nothing is configured
|
|
443
|
-
const intPath = path.join(infernoDir, "integrations.json");
|
|
444
|
-
const hasAiKey = process.env.ANTHROPIC_API_KEY || process.env.OPENAI_API_KEY ||
|
|
445
|
-
process.env.GOOGLE_AI_API_KEY || process.env.OPENROUTER_API_KEY ||
|
|
446
|
-
process.env.GEMINI_API_KEY;
|
|
447
|
-
if (!hasAiKey && !fs.existsSync(intPath)) {
|
|
448
|
-
console.log();
|
|
449
|
-
console.log(` ${yellow("💡")} ${bold("Tip:")} connect an AI provider for explain, why, review, and changelog AI.`);
|
|
450
|
-
console.log(` ${cyan("infernoflow ai setup")} — takes 60 seconds`);
|
|
451
|
-
}
|
|
452
|
-
|
|
453
|
-
nextSteps([
|
|
454
|
-
cyan("infernoflow status") + " — see your contract at a glance",
|
|
455
|
-
cyan("infernoflow check") + " — validate everything",
|
|
456
|
-
(adopt ? "Review inferred baseline in " : "Edit ") + yellow("inferno/capabilities.json") + (adopt ? " and refine IDs/titles" : " to describe each capability in detail"),
|
|
457
|
-
"Add more " + yellow("inferno/scenarios/*.json") + " files for edge cases",
|
|
458
|
-
"Add " + cyan("inferno:check") + " to your CI pipeline",
|
|
459
|
-
...(cursorHooks
|
|
460
|
-
? [
|
|
461
|
-
"Restart Cursor — hooks write assistant text to " + yellow("inferno/CONTEXT.draft.md"),
|
|
462
|
-
"Promote when ready: " + cyan("npm run inferno:promote-draft -- --append-notes"),
|
|
463
|
-
]
|
|
464
|
-
: []),
|
|
465
|
-
...(vscodeCopilotHooks
|
|
466
|
-
? [
|
|
467
|
-
"Restart VS Code — Copilot hooks append prompts + assistant (from transcript) to " +
|
|
468
|
-
yellow("inferno/CONTEXT.draft.md"),
|
|
469
|
-
"Promote when ready: " + cyan("npm run inferno:promote-draft -- --append-notes"),
|
|
470
|
-
]
|
|
471
|
-
: []),
|
|
472
|
-
...(!cursorHooks && !vscodeCopilotHooks
|
|
473
|
-
? [
|
|
474
|
-
"Optional: " +
|
|
475
|
-
cyan("infernoflow install-cursor-hooks") +
|
|
476
|
-
" or " +
|
|
477
|
-
cyan("infernoflow install-vscode-copilot-hooks"),
|
|
478
|
-
]
|
|
479
|
-
: []),
|
|
480
|
-
]);
|
|
481
|
-
}
|
|
482
|
-
}
|
|
14
|
+
`;r.writeFileSync(e,i)}async function Ce(e){const n=process.cwd(),i=e.includes("--force")||e.includes("-f"),s=e.includes("--yes")||e.includes("-y"),u=e.includes("--adopt"),y=e.indexOf("--template"),g=y!==-1?e[y+1]:null;if(g){let a;try{a=await import("../templates/index.mjs")}catch{}const o=a?.getTemplate(g);if(!o){const c=a?a.listTemplates().map(j=>j.name).join(", "):"rest-api, nextjs, cli, graphql, monorepo";$(`Unknown template: ${g}. Available: ${c}`),process.exit(1)}const f=t.join(n,"inferno"),h=t.join(f,"scenarios");r.existsSync(f)||r.mkdirSync(f,{recursive:!0}),r.existsSync(h)||r.mkdirSync(h,{recursive:!0});const x=U(n),m=o.capabilities;r.writeFileSync(t.join(f,"contract.json"),JSON.stringify({policyId:x,policyVersion:1,capabilities:m.map(c=>c.id)},null,2)+`
|
|
15
|
+
`),r.writeFileSync(t.join(f,"capabilities.json"),JSON.stringify({capabilities:m.map(c=>({id:c.id,description:c.description,since:new Date().toISOString().slice(0,10),source:`template:${g}`}))},null,2)+`
|
|
16
|
+
`);for(const c of m)r.writeFileSync(t.join(h,`${c.id}.json`),JSON.stringify({id:`${c.id}-happy-path`,capability:c.id,description:`Happy path for ${c.description||c.id}`,steps:[{action:"invoke",target:c.id,input:{}},{action:"assert",field:"status",value:"success"}],capabilitiesCovered:[c.id]},null,2)+`
|
|
17
|
+
`);K(t.join(f,"CHANGELOG.md"),x),r.writeFileSync(t.join(f,"CONTEXT.md"),`# ${x} \u2014 infernoflow context
|
|
18
|
+
|
|
19
|
+
> Template: ${g} \u2014 ${o.description}
|
|
20
|
+
|
|
21
|
+
## Hint
|
|
22
|
+
${o.contextHint}
|
|
23
|
+
|
|
24
|
+
## Capabilities (${m.length})
|
|
25
|
+
${m.map(c=>`- \`${c.id}\`: ${c.description}`).join(`
|
|
26
|
+
`)}
|
|
27
|
+
`),o.scripts&&(info("Suggested package.json scripts for this template:"),Object.entries(o.scripts).forEach(([c,j])=>console.log(` ${bold(c)}: ${S(j)}`)),console.log()),D(`Initialised from template ${bold(l(g))} \u2014 ${bold(String(m.length))} capabilities`),console.log(),info(`Run ${l("infernoflow vibe")} to start vibe coding mode`),console.log();return}const v=e.includes("--cursor-hooks"),E=e.includes("--vscode-copilot-hooks"),q=e.includes("--report-json"),A=e.includes("--report-json-only"),H=e.includes("--report-human-only"),F=J(e,"--lang"),_=J(e,"--framework"),R=J(e,"--project-type"),p=A;A&&H&&(console.error("Error: --report-json-only and --report-human-only cannot be used together."),process.exit(1)),p||oe("init");const w=t.join(n,"inferno"),z=t.join(n,".github","workflows");r.existsSync(w)&&!i&&(p&&(console.log(JSON.stringify({ok:!1,error:"inferno_exists",hint:"Use --force to overwrite"},null,2)),process.exit(1)),$("inferno/ already exists. Use --force to overwrite."),console.log(),process.exit(0));const C=U(n),L="CreateTask, ReadTasks, UpdateTask, ToggleComplete, DeleteTask";let b=C,k=L.split(",").map(a=>a.trim());if(u){let o=I(n,{language:F||void 0,framework:_||void 0,projectType:R||void 0});if(!s&&!A){const m=G.createInterface({input:process.stdin,output:process.stdout}),c=o.developmentProfile||{},j=c.detected||{};console.log(S(` Review inferred development stack (press Enter to accept detected values)
|
|
28
|
+
`));const Z=await P(m,"Language",c.language||j.language||"unknown"),Q=await P(m,"Framework",c.framework||j.framework||"unknown"),ee=await P(m,"Project type",c.projectType||j.projectType||"unknown");m.close(),o=I(n,{language:Z,framework:Q,projectType:ee})}const f=o.capabilities,h=ae(f);A?console.log(JSON.stringify({mode:"adopt",policyId:C,inferredCapabilities:h,components:o.components,displayFields:o.displayFields,externalLibraries:o.externalLibraries,uiLayout:o.uiLayout,styling:o.styling,developmentProfile:o.developmentProfile,apiCalls:o.apiCalls},null,2)):(console.log(),console.log(S(re(f))),console.log(),console.log(S(ce(o))),console.log(),q&&!H&&(console.log(JSON.stringify({mode:"adopt",policyId:C,inferredCapabilities:h,components:o.components,displayFields:o.displayFields,externalLibraries:o.externalLibraries,uiLayout:o.uiLayout,styling:o.styling,developmentProfile:o.developmentProfile,apiCalls:o.apiCalls},null,2)),console.log()));const x=await te(f,s);b=C,k=x.map(m=>m.id)}else if(!s){const a=G.createInterface({input:process.stdin,output:process.stdout});console.log(S(` Press Enter to accept defaults
|
|
29
|
+
`)),b=await P(a,"Project / policy name",C),k=(await P(a,"Capabilities (comma-separated)",L)).split(",").map(f=>f.trim()).filter(Boolean),a.close(),console.log()}if(r.mkdirSync(w,{recursive:!0}),u){const a=k.map(f=>({id:f,title:f.replace(/([A-Z])/g," $1").trim()})),o=I(n,{language:F||void 0,framework:_||void 0,projectType:R||void 0});se(w,b,a,o),p||(d("Created: "+l("inferno/contract.json")),d("Created: "+l("inferno/capabilities.json")),d("Created: "+l("inferno/scenarios/adoption_baseline.json")),d("Created: "+l("inferno/adoption_profile.json")),d("Created: "+l("inferno/CHANGELOG.md")))}else ye(t.join(w,"contract.json"),b,k),p||d("Created: "+l("inferno/contract.json")),ge(t.join(w,"capabilities.json"),k),p||d("Created: "+l("inferno/capabilities.json")),we(t.join(w,"scenarios"),k),p||d("Created: "+l("inferno/scenarios/happy_path.json")),K(t.join(w,"CHANGELOG.md"),b),p||d("Created: "+l("inferno/CHANGELOG.md"));const O=de(),W=t.join(O,"scripts","inferno-doc-gate.mjs"),Y=t.join(n,"scripts","inferno-doc-gate.mjs");T(W,Y,i,p);const V=t.join(O,"scripts","inferno-install-hooks.mjs"),B=t.join(n,"scripts","inferno-install-hooks.mjs");T(V,B,i,p);const X=t.join(O,"ci","github-inferno-check.yml"),M=t.join(z,"infernoflow-check.yml");if(T(X,M,i,p),me(n,p),v&&le({cwd:n,templatesRoot:O,force:i,silent:p,logOk:a=>{p||d(a)},logWarn:a=>{p||$(a)}}),E&&pe({cwd:n,templatesRoot:O,force:i,silent:p,logOk:a=>{p||d(a)},logWarn:a=>{p||$(a)}}),u){const a=t.join(w,"context-state.json");let o={};try{o=JSON.parse(r.readFileSync(a,"utf8"))}catch{}const f=I(n,{language:F||void 0,framework:_||void 0,projectType:R||void 0});o.stack=f.developmentProfile,r.writeFileSync(a,JSON.stringify(o,null,2)+`
|
|
30
|
+
`,"utf8"),p||d("Created: "+l("inferno/context-state.json"))}if(!p){D("infernoflow initialized!");const a=t.join(w,"integrations.json");!(process.env.ANTHROPIC_API_KEY||process.env.OPENAI_API_KEY||process.env.GOOGLE_AI_API_KEY||process.env.OPENROUTER_API_KEY||process.env.GEMINI_API_KEY)&&!r.existsSync(a)&&(console.log(),console.log(` ${N("\u{1F4A1}")} ${bold("Tip:")} connect an AI provider for explain, why, review, and changelog AI.`),console.log(` ${l("infernoflow ai setup")} \u2014 takes 60 seconds`)),ie([l("infernoflow status")+" \u2014 see your contract at a glance",l("infernoflow check")+" \u2014 validate everything",(u?"Review inferred baseline in ":"Edit ")+N("inferno/capabilities.json")+(u?" and refine IDs/titles":" to describe each capability in detail"),"Add more "+N("inferno/scenarios/*.json")+" files for edge cases","Add "+l("inferno:check")+" to your CI pipeline",...v?["Restart Cursor \u2014 hooks write assistant text to "+N("inferno/CONTEXT.draft.md"),"Promote when ready: "+l("npm run inferno:promote-draft -- --append-notes")]:[],...E?["Restart VS Code \u2014 Copilot hooks append prompts + assistant (from transcript) to "+N("inferno/CONTEXT.draft.md"),"Promote when ready: "+l("npm run inferno:promote-draft -- --append-notes")]:[],...!v&&!E?["Optional: "+l("infernoflow install-cursor-hooks")+" or "+l("infernoflow install-vscode-copilot-hooks")]:[]])}}export{Ce as initCommand};
|
|
@@ -1,36 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { fileURLToPath } from "node:url";
|
|
3
|
-
import { header, ok, warn, done, nextSteps, cyan, yellow } from "../ui/output.mjs";
|
|
4
|
-
import { installCursorHooksArtifacts } from "../cursorHooksInstall.mjs";
|
|
5
|
-
|
|
6
|
-
const __dirname = path.dirname(fileURLToPath(import.meta.url));
|
|
7
|
-
|
|
8
|
-
function getTemplatesRoot() {
|
|
9
|
-
return path.resolve(__dirname, "../../templates");
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export async function installCursorHooksCommand(args) {
|
|
13
|
-
const cwd = process.cwd();
|
|
14
|
-
const force = args.includes("--force") || args.includes("-f");
|
|
15
|
-
|
|
16
|
-
header("install-cursor-hooks");
|
|
17
|
-
|
|
18
|
-
installCursorHooksArtifacts({
|
|
19
|
-
cwd,
|
|
20
|
-
templatesRoot: getTemplatesRoot(),
|
|
21
|
-
force,
|
|
22
|
-
silent: false,
|
|
23
|
-
logOk: (msg) => ok(msg),
|
|
24
|
-
logWarn: (msg) => warn(msg),
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
done("Cursor draft hooks installed");
|
|
28
|
-
|
|
29
|
-
nextSteps([
|
|
30
|
-
"Restart Cursor (or reload window) so " + yellow(".cursor/hooks.json") + " is picked up",
|
|
31
|
-
"Use Agent chat — each assistant reply appends to " + yellow("inferno/CONTEXT.draft.md") + " (gitignored)",
|
|
32
|
-
cyan("npm run inferno:promote-draft") + " — preview draft",
|
|
33
|
-
cyan("npm run inferno:promote-draft -- --append-notes") + " — merge into inferno/CONTEXT.md under Decisions",
|
|
34
|
-
cyan("npm run inferno:promote-draft -- --clear") + " — discard draft",
|
|
35
|
-
]);
|
|
36
|
-
}
|
|
1
|
+
import*as t from"node:path";import{fileURLToPath as i}from"node:url";import{header as d,ok as m,warn as p,done as l,nextSteps as f,cyan as r,yellow as n}from"../ui/output.mjs";import{installCursorHooksArtifacts as c}from"../cursorHooksInstall.mjs";const u=t.dirname(i(import.meta.url));function h(){return t.resolve(u,"../../templates")}async function g(e){const s=process.cwd(),a=e.includes("--force")||e.includes("-f");d("install-cursor-hooks"),c({cwd:s,templatesRoot:h(),force:a,silent:!1,logOk:o=>m(o),logWarn:o=>p(o)}),l("Cursor draft hooks installed"),f(["Restart Cursor (or reload window) so "+n(".cursor/hooks.json")+" is picked up","Use Agent chat \u2014 each assistant reply appends to "+n("inferno/CONTEXT.draft.md")+" (gitignored)",r("npm run inferno:promote-draft")+" \u2014 preview draft",r("npm run inferno:promote-draft -- --append-notes")+" \u2014 merge into inferno/CONTEXT.md under Decisions",r("npm run inferno:promote-draft -- --clear")+" \u2014 discard draft"])}export{g as installCursorHooksCommand};
|