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,96 +1 @@
|
|
|
1
|
-
import
|
|
2
|
-
import * as path from "node:path";
|
|
3
|
-
import { fileURLToPath } from "node:url";
|
|
4
|
-
import { header, section, ok, warn, yellow, gray } from "../ui/output.mjs";
|
|
5
|
-
|
|
6
|
-
const __filename = fileURLToPath(import.meta.url);
|
|
7
|
-
const __dirname = path.dirname(__filename);
|
|
8
|
-
const binPath = path.resolve(__dirname, "..", "..", "bin", "infernoflow.mjs");
|
|
9
|
-
|
|
10
|
-
function runCliJson(args) {
|
|
11
|
-
const out = execFileSync(process.execPath, [binPath, ...args], { encoding: "utf8", stdio: ["ignore", "pipe", "pipe"] });
|
|
12
|
-
return JSON.parse(out);
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
function tryRunCliJson(args) {
|
|
16
|
-
try {
|
|
17
|
-
return { ok: true, data: runCliJson(args) };
|
|
18
|
-
} catch (err) {
|
|
19
|
-
const stdout = err?.stdout?.toString?.() || "";
|
|
20
|
-
try {
|
|
21
|
-
return { ok: false, data: JSON.parse(stdout) };
|
|
22
|
-
} catch {
|
|
23
|
-
return { ok: false, data: { ok: false, errors: ["command_failed"] } };
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
export async function syncCommand(args = []) {
|
|
29
|
-
const auto = args.includes("--auto");
|
|
30
|
-
const asJson = args.includes("--json");
|
|
31
|
-
const dryRun = args.includes("--dry-run");
|
|
32
|
-
|
|
33
|
-
if (!auto) {
|
|
34
|
-
const payload = { ok: false, error: "missing_required_flag", hint: "Use: infernoflow sync --auto" };
|
|
35
|
-
if (asJson) {
|
|
36
|
-
console.log(JSON.stringify(payload, null, 2));
|
|
37
|
-
process.exit(1);
|
|
38
|
-
}
|
|
39
|
-
header("sync");
|
|
40
|
-
warn("missing --auto flag");
|
|
41
|
-
console.log(` ${yellow("→")} infernoflow sync --auto`);
|
|
42
|
-
console.log();
|
|
43
|
-
process.exit(1);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const impact = tryRunCliJson(["pr-impact", "--json"]);
|
|
47
|
-
const needsSync = !impact.data?.ok;
|
|
48
|
-
const confidence = impact.data?.confidence || "low";
|
|
49
|
-
const policyDecision = confidence === "high" ? "auto" : confidence === "medium" ? "ask" : "block";
|
|
50
|
-
const actions = needsSync
|
|
51
|
-
? ["Generate inferno update proposal (suggest)", "Review changes", "Validate with check --json"]
|
|
52
|
-
: ["No inferno drift detected", "Validate with check --json"];
|
|
53
|
-
|
|
54
|
-
const check = tryRunCliJson(["check", "--json"]);
|
|
55
|
-
const payload = {
|
|
56
|
-
ok: impact.ok && check.ok && !!check.data?.ok,
|
|
57
|
-
mode: "auto-skeleton",
|
|
58
|
-
dryRun,
|
|
59
|
-
needsSync,
|
|
60
|
-
didApply: false,
|
|
61
|
-
confidence,
|
|
62
|
-
policyDecision,
|
|
63
|
-
actions,
|
|
64
|
-
prImpact: impact.data,
|
|
65
|
-
postCheck: check.data,
|
|
66
|
-
reasonCodes: [
|
|
67
|
-
...(needsSync ? ["DRIFT_DETECTED"] : ["NO_DRIFT"]),
|
|
68
|
-
`POLICY_${policyDecision.toUpperCase()}`,
|
|
69
|
-
...(policyDecision === "auto" ? ["AUTO_APPLY_DISABLED_IN_SKELETON"] : []),
|
|
70
|
-
],
|
|
71
|
-
};
|
|
72
|
-
|
|
73
|
-
if (asJson) {
|
|
74
|
-
console.log(JSON.stringify(payload, null, 2));
|
|
75
|
-
process.exit(payload.ok ? 0 : 1);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
header("sync --auto");
|
|
79
|
-
section("State");
|
|
80
|
-
if (needsSync) warn("Inferno drift detected");
|
|
81
|
-
else ok("No inferno drift detected");
|
|
82
|
-
ok(`Confidence: ${gray(confidence)}`);
|
|
83
|
-
ok(`Policy decision: ${gray(policyDecision)}`);
|
|
84
|
-
ok(`Apply mode: ${gray("skeleton (no file writes)")}`);
|
|
85
|
-
if (dryRun) ok("Dry run enabled");
|
|
86
|
-
|
|
87
|
-
section("Plan");
|
|
88
|
-
actions.forEach((a) => console.log(` ${yellow("→")} ${a}`));
|
|
89
|
-
|
|
90
|
-
section("Validation");
|
|
91
|
-
if (check.ok && check.data?.ok) ok("Post-check passed");
|
|
92
|
-
else warn("Post-check failed; see infernoflow check --json");
|
|
93
|
-
console.log();
|
|
94
|
-
process.exit(payload.ok ? 0 : 1);
|
|
95
|
-
}
|
|
96
|
-
|
|
1
|
+
import{execFileSync as w}from"node:child_process";import*as y from"node:path";import{fileURLToPath as P}from"node:url";import{header as h,section as d,ok as e,warn as f,yellow as g,gray as p}from"../ui/output.mjs";const S=P(import.meta.url),C=y.dirname(S),N=y.resolve(C,"..","..","bin","infernoflow.mjs");function D(o){const n=w(process.execPath,[N,...o],{encoding:"utf8",stdio:["ignore","pipe","pipe"]});return JSON.parse(n)}function _(o){try{return{ok:!0,data:D(o)}}catch(n){const s=n?.stdout?.toString?.()||"";try{return{ok:!1,data:JSON.parse(s)}}catch{return{ok:!1,data:{ok:!1,errors:["command_failed"]}}}}}async function E(o=[]){const n=o.includes("--auto"),s=o.includes("--json"),u=o.includes("--dry-run");n||(s&&(console.log(JSON.stringify({ok:!1,error:"missing_required_flag",hint:"Use: infernoflow sync --auto"},null,2)),process.exit(1)),h("sync"),f("missing --auto flag"),console.log(` ${g("\u2192")} infernoflow sync --auto`),console.log(),process.exit(1));const c=_(["pr-impact","--json"]),i=!c.data?.ok,a=c.data?.confidence||"low",r=a==="high"?"auto":a==="medium"?"ask":"block",k=i?["Generate inferno update proposal (suggest)","Review changes","Validate with check --json"]:["No inferno drift detected","Validate with check --json"],t=_(["check","--json"]),l={ok:c.ok&&t.ok&&!!t.data?.ok,mode:"auto-skeleton",dryRun:u,needsSync:i,didApply:!1,confidence:a,policyDecision:r,actions:k,prImpact:c.data,postCheck:t.data,reasonCodes:[...i?["DRIFT_DETECTED"]:["NO_DRIFT"],`POLICY_${r.toUpperCase()}`,...r==="auto"?["AUTO_APPLY_DISABLED_IN_SKELETON"]:[]]};s&&(console.log(JSON.stringify(l,null,2)),process.exit(l.ok?0:1)),h("sync --auto"),d("State"),i?f("Inferno drift detected"):e("No inferno drift detected"),e(`Confidence: ${p(a)}`),e(`Policy decision: ${p(r)}`),e(`Apply mode: ${p("skeleton (no file writes)")}`),u&&e("Dry run enabled"),d("Plan"),k.forEach(m=>console.log(` ${g("\u2192")} ${m}`)),d("Validation"),t.ok&&t.data?.ok?e("Post-check passed"):f("Post-check failed; see infernoflow check --json"),console.log(),process.exit(l.ok?0:1)}export{E as syncCommand};
|
|
@@ -1,228 +1,10 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
*
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
* infernoflow synthesize --threshold 2 — surface patterns seen 2+ times
|
|
12
|
-
* infernoflow synthesize --watch — re-run every 60s, surface new candidates
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import * as fs from "node:fs";
|
|
16
|
-
import * as path from "node:path";
|
|
17
|
-
import * as readline from "node:readline";
|
|
18
|
-
import { header, ok, warn, info, done, bold, cyan, yellow, green, gray } from "../ui/output.mjs";
|
|
19
|
-
import { readProfile, writeProfile } from "../learning/profile.mjs";
|
|
20
|
-
import { detectPatterns, mergeCandidates, pendingCandidates } from "../learning/patternDetector.mjs";
|
|
21
|
-
import { synthesizeCandidate } from "../learning/skillSynthesizer.mjs";
|
|
22
|
-
import { observeCommandStart } from "../learning/observe.mjs";
|
|
23
|
-
|
|
24
|
-
function findInfernoDir(cwd) {
|
|
25
|
-
const infernoDir = path.join(cwd, "inferno");
|
|
26
|
-
if (!fs.existsSync(infernoDir)) return null;
|
|
27
|
-
return infernoDir;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
function ask(rl, question) {
|
|
31
|
-
return new Promise(resolve => rl.question(question, a => resolve(a.trim().toLowerCase())));
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
function renderCandidate(c, index) {
|
|
35
|
-
const type = c.type === "agent" ? cyan("agent") : green("skill");
|
|
36
|
-
const conf = Math.round(c.confidence * 100);
|
|
37
|
-
const confStr = conf >= 80 ? green(`${conf}%`) : conf >= 60 ? yellow(`${conf}%`) : gray(`${conf}%`);
|
|
38
|
-
const freq = gray(`seen ${c.frequency}×`);
|
|
39
|
-
|
|
40
|
-
console.log(`\n ${bold(`[${index + 1}]`)} ${type} ${bold(c.name)} ${confStr} confidence ${freq}`);
|
|
41
|
-
console.log(` Pattern: ${cyan(c.trigger)}`);
|
|
42
|
-
if (c.steps) console.log(` Steps: ${c.steps.join(" → ")}`);
|
|
43
|
-
if (c.examples?.length) {
|
|
44
|
-
console.log(` Examples: ${c.examples.slice(0, 2).map(e => `"${e}"`).join(", ")}`);
|
|
45
|
-
}
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
async function reviewInteractive(cwd, infernoDir, profile, candidates) {
|
|
49
|
-
if (candidates.length === 0) {
|
|
50
|
-
info("No new candidates to review.");
|
|
51
|
-
return { approved: 0, rejected: 0 };
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
const rl = readline.createInterface({ input: process.stdin, output: process.stdout });
|
|
55
|
-
let approved = 0, rejected = 0;
|
|
56
|
-
|
|
57
|
-
console.log(`\n ${bold("Candidates found:")} ${candidates.length}\n`);
|
|
58
|
-
console.log(gray(" For each: [y] approve [n] reject [s] skip [q] quit\n"));
|
|
59
|
-
|
|
60
|
-
for (let i = 0; i < candidates.length; i++) {
|
|
61
|
-
const c = candidates[i];
|
|
62
|
-
renderCandidate(c, i);
|
|
63
|
-
|
|
64
|
-
const ans = await ask(rl, `\n Approve? [y/n/s/q]: `);
|
|
65
|
-
|
|
66
|
-
if (ans === "q") break;
|
|
67
|
-
if (ans === "s") continue;
|
|
68
|
-
|
|
69
|
-
const all = [...(profile.agentCandidates || []), ...(profile.skillCandidates || [])];
|
|
70
|
-
const entry = all.find(x => x.id === c.id);
|
|
71
|
-
if (!entry) continue;
|
|
72
|
-
|
|
73
|
-
if (ans === "y") {
|
|
74
|
-
entry.status = "approved";
|
|
75
|
-
try {
|
|
76
|
-
const { filePaths } = synthesizeCandidate(cwd, infernoDir, c);
|
|
77
|
-
const approvedList = c.type === "agent" ? "approvedAgents" : "approvedSkills";
|
|
78
|
-
if (!profile[approvedList]) profile[approvedList] = [];
|
|
79
|
-
profile[approvedList].push({
|
|
80
|
-
id: c.id,
|
|
81
|
-
name: c.name,
|
|
82
|
-
filePaths,
|
|
83
|
-
approvedAt: new Date().toISOString(),
|
|
84
|
-
});
|
|
85
|
-
ok(` ${c.type === "agent" ? "Agent" : "Skill"} created:`);
|
|
86
|
-
for (const fp of filePaths) console.log(` ${green("→")} ${fp}`);
|
|
87
|
-
approved++;
|
|
88
|
-
} catch (err) {
|
|
89
|
-
warn(` Could not write files: ${err.message}`);
|
|
90
|
-
}
|
|
91
|
-
} else {
|
|
92
|
-
entry.status = "rejected";
|
|
93
|
-
rejected++;
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
|
|
97
|
-
rl.close();
|
|
98
|
-
return { approved, rejected };
|
|
99
|
-
}
|
|
100
|
-
|
|
101
|
-
function autoApprove(cwd, infernoDir, profile, candidates, threshold = 0.8) {
|
|
102
|
-
let approved = 0;
|
|
103
|
-
for (const c of candidates) {
|
|
104
|
-
if (c.confidence < threshold) continue;
|
|
105
|
-
|
|
106
|
-
const all = [...(profile.agentCandidates || []), ...(profile.skillCandidates || [])];
|
|
107
|
-
const entry = all.find(x => x.id === c.id);
|
|
108
|
-
if (!entry) continue;
|
|
109
|
-
|
|
110
|
-
entry.status = "approved";
|
|
111
|
-
try {
|
|
112
|
-
const { filePaths } = synthesizeCandidate(cwd, infernoDir, c);
|
|
113
|
-
const approvedList = c.type === "agent" ? "approvedAgents" : "approvedSkills";
|
|
114
|
-
if (!profile[approvedList]) profile[approvedList] = [];
|
|
115
|
-
profile[approvedList].push({
|
|
116
|
-
id: c.id,
|
|
117
|
-
name: c.name,
|
|
118
|
-
filePaths,
|
|
119
|
-
approvedAt: new Date().toISOString(),
|
|
120
|
-
});
|
|
121
|
-
ok(`Auto-approved ${c.type}: ${bold(c.name)} (${Math.round(c.confidence * 100)}% confidence)`);
|
|
122
|
-
for (const fp of filePaths) console.log(` ${green("→")} ${fp}`);
|
|
123
|
-
approved++;
|
|
124
|
-
} catch (err) {
|
|
125
|
-
warn(`Could not write ${c.name}: ${err.message}`);
|
|
126
|
-
}
|
|
127
|
-
}
|
|
128
|
-
return approved;
|
|
129
|
-
}
|
|
130
|
-
|
|
131
|
-
export async function synthesizeCommand(args) {
|
|
132
|
-
const cwd = process.cwd();
|
|
133
|
-
const isAuto = args.includes("--auto");
|
|
134
|
-
const isJson = args.includes("--json");
|
|
135
|
-
const isWatch = args.includes("--watch");
|
|
136
|
-
const threshold = (() => {
|
|
137
|
-
const idx = args.indexOf("--threshold");
|
|
138
|
-
return idx !== -1 ? Number(args[idx + 1]) || 3 : 3;
|
|
139
|
-
})();
|
|
140
|
-
|
|
141
|
-
const infernoDir = findInfernoDir(cwd);
|
|
142
|
-
if (!infernoDir) {
|
|
143
|
-
if (isJson) {
|
|
144
|
-
process.stdout.write(JSON.stringify({ ok: false, error: "inferno/ not found" }) + "\n");
|
|
145
|
-
} else {
|
|
146
|
-
warn("inferno/ not found — run infernoflow init first");
|
|
147
|
-
}
|
|
148
|
-
process.exit(1);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
observeCommandStart(infernoDir, "synthesize");
|
|
152
|
-
|
|
153
|
-
async function runOnce() {
|
|
154
|
-
const profile = readProfile(infernoDir);
|
|
155
|
-
|
|
156
|
-
// ── Detect patterns ──────────────────────────────────────────────────────
|
|
157
|
-
const { agentCandidates, skillCandidates } = detectPatterns(profile, { minFreq: threshold });
|
|
158
|
-
mergeCandidates(profile, { agentCandidates, skillCandidates });
|
|
159
|
-
|
|
160
|
-
const pending = pendingCandidates(profile);
|
|
161
|
-
|
|
162
|
-
// ── JSON mode ────────────────────────────────────────────────────────────
|
|
163
|
-
if (isJson) {
|
|
164
|
-
writeProfile(infernoDir, profile);
|
|
165
|
-
process.stdout.write(JSON.stringify({
|
|
166
|
-
ok: true,
|
|
167
|
-
pendingCount: pending.length,
|
|
168
|
-
candidates: pending,
|
|
169
|
-
sessions: profile.recentSessions?.length || 0,
|
|
170
|
-
}, null, 2) + "\n");
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
header("infernoflow synthesize");
|
|
175
|
-
info(`Sessions analyzed: ${bold(String(profile.recentSessions?.length || 0))}`);
|
|
176
|
-
info(`Minimum frequency: ${bold(String(threshold))}×`);
|
|
177
|
-
|
|
178
|
-
if (pending.length === 0) {
|
|
179
|
-
console.log();
|
|
180
|
-
if ((profile.recentSessions?.length || 0) < threshold) {
|
|
181
|
-
warn(`Not enough session data yet — need at least ${threshold} sessions with similar commands.`);
|
|
182
|
-
warn(`Keep using infernoflow and run synthesize again soon.`);
|
|
183
|
-
} else {
|
|
184
|
-
ok("No new patterns detected — your workflow is already well captured.");
|
|
185
|
-
}
|
|
186
|
-
writeProfile(infernoDir, profile);
|
|
187
|
-
return;
|
|
188
|
-
}
|
|
189
|
-
|
|
190
|
-
info(`New patterns detected: ${bold(String(pending.length))}`);
|
|
191
|
-
|
|
192
|
-
// ── Auto mode ────────────────────────────────────────────────────────────
|
|
193
|
-
let approved;
|
|
194
|
-
if (isAuto) {
|
|
195
|
-
info(`Auto-approving candidates with ≥80% confidence...`);
|
|
196
|
-
approved = autoApprove(cwd, infernoDir, profile, pending);
|
|
197
|
-
} else {
|
|
198
|
-
const result = await reviewInteractive(cwd, infernoDir, profile, pending);
|
|
199
|
-
approved = result.approved;
|
|
200
|
-
if (result.rejected > 0) info(`Rejected: ${result.rejected}`);
|
|
201
|
-
}
|
|
202
|
-
|
|
203
|
-
writeProfile(infernoDir, profile);
|
|
204
|
-
|
|
205
|
-
if (approved > 0) {
|
|
206
|
-
console.log();
|
|
207
|
-
done(`${approved} skill${approved !== 1 ? "s/agents" : "/agent"} synthesized`);
|
|
208
|
-
console.log(`\n ${bold("What was created:")}`);
|
|
209
|
-
for (const item of [...(profile.approvedSkills || []), ...(profile.approvedAgents || [])].slice(-approved)) {
|
|
210
|
-
const type = profile.approvedAgents?.find(a => a.id === item.id) ? "agent" : "skill";
|
|
211
|
-
console.log(` ${green("✔")} ${type}: ${bold(item.name)}`);
|
|
212
|
-
for (const fp of item.filePaths || []) console.log(` ${gray(fp)}`);
|
|
213
|
-
}
|
|
214
|
-
console.log(`\n ${bold("Next:")} Run ${cyan("infernoflow agent list")} to see your agents`);
|
|
215
|
-
console.log(` or ${cyan("infernoflow agent run <name>")} to execute one`);
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
if (isWatch) {
|
|
220
|
-
info("Watching for new patterns — press Ctrl+C to stop");
|
|
221
|
-
const INTERVAL = 60_000;
|
|
222
|
-
await runOnce();
|
|
223
|
-
setInterval(runOnce, INTERVAL);
|
|
224
|
-
await new Promise(() => {}); // keep alive
|
|
225
|
-
} else {
|
|
226
|
-
await runOnce();
|
|
227
|
-
}
|
|
228
|
-
}
|
|
1
|
+
import*as j from"node:fs";import*as x from"node:path";import*as q from"node:readline";import{header as N,ok as C,warn as $,info as g,done as P,bold as f,cyan as w,yellow as z,green as h,gray as y}from"../ui/output.mjs";import{readProfile as I,writeProfile as k}from"../learning/profile.mjs";import{detectPatterns as O,mergeCandidates as D,pendingCandidates as b}from"../learning/patternDetector.mjs";import{synthesizeCandidate as A}from"../learning/skillSynthesizer.mjs";import{observeCommandStart as L}from"../learning/observe.mjs";function J(t){const i=x.join(t,"inferno");return j.existsSync(i)?i:null}function M(t,i){return new Promise(o=>t.question(i,s=>o(s.trim().toLowerCase())))}function R(t,i){const o=t.type==="agent"?w("agent"):h("skill"),s=Math.round(t.confidence*100),u=s>=80?h(`${s}%`):s>=60?z(`${s}%`):y(`${s}%`),a=y(`seen ${t.frequency}\xD7`);console.log(`
|
|
2
|
+
${f(`[${i+1}]`)} ${o} ${f(t.name)} ${u} confidence ${a}`),console.log(` Pattern: ${w(t.trigger)}`),t.steps&&console.log(` Steps: ${t.steps.join(" \u2192 ")}`),t.examples?.length&&console.log(` Examples: ${t.examples.slice(0,2).map(n=>`"${n}"`).join(", ")}`)}async function W(t,i,o,s){if(s.length===0)return g("No new candidates to review."),{approved:0,rejected:0};const u=q.createInterface({input:process.stdin,output:process.stdout});let a=0,n=0;console.log(`
|
|
3
|
+
${f("Candidates found:")} ${s.length}
|
|
4
|
+
`),console.log(y(` For each: [y] approve [n] reject [s] skip [q] quit
|
|
5
|
+
`));for(let p=0;p<s.length;p++){const e=s[p];R(e,p);const c=await M(u,`
|
|
6
|
+
Approve? [y/n/s/q]: `);if(c==="q")break;if(c==="s")continue;const l=[...o.agentCandidates||[],...o.skillCandidates||[]].find(r=>r.id===e.id);if(l)if(c==="y"){l.status="approved";try{const{filePaths:r}=A(t,i,e),d=e.type==="agent"?"approvedAgents":"approvedSkills";o[d]||(o[d]=[]),o[d].push({id:e.id,name:e.name,filePaths:r,approvedAt:new Date().toISOString()}),C(` ${e.type==="agent"?"Agent":"Skill"} created:`);for(const v of r)console.log(` ${h("\u2192")} ${v}`);a++}catch(r){$(` Could not write files: ${r.message}`)}}else l.status="rejected",n++}return u.close(),{approved:a,rejected:n}}function E(t,i,o,s,u=.8){let a=0;for(const n of s){if(n.confidence<u)continue;const e=[...o.agentCandidates||[],...o.skillCandidates||[]].find(c=>c.id===n.id);if(e){e.status="approved";try{const{filePaths:c}=A(t,i,n),m=n.type==="agent"?"approvedAgents":"approvedSkills";o[m]||(o[m]=[]),o[m].push({id:n.id,name:n.name,filePaths:c,approvedAt:new Date().toISOString()}),C(`Auto-approved ${n.type}: ${f(n.name)} (${Math.round(n.confidence*100)}% confidence)`);for(const l of c)console.log(` ${h("\u2192")} ${l}`);a++}catch(c){$(`Could not write ${n.name}: ${c.message}`)}}}return a}async function B(t){const i=process.cwd(),o=t.includes("--auto"),s=t.includes("--json"),u=t.includes("--watch"),a=(()=>{const e=t.indexOf("--threshold");return e!==-1&&Number(t[e+1])||3})(),n=J(i);n||(s?process.stdout.write(JSON.stringify({ok:!1,error:"inferno/ not found"})+`
|
|
7
|
+
`):$("inferno/ not found \u2014 run infernoflow init first"),process.exit(1)),L(n,"synthesize");async function p(){const e=I(n),{agentCandidates:c,skillCandidates:m}=O(e,{minFreq:a});D(e,{agentCandidates:c,skillCandidates:m});const l=b(e);if(s){k(n,e),process.stdout.write(JSON.stringify({ok:!0,pendingCount:l.length,candidates:l,sessions:e.recentSessions?.length||0},null,2)+`
|
|
8
|
+
`);return}if(N("infernoflow synthesize"),g(`Sessions analyzed: ${f(String(e.recentSessions?.length||0))}`),g(`Minimum frequency: ${f(String(a))}\xD7`),l.length===0){console.log(),(e.recentSessions?.length||0)<a?($(`Not enough session data yet \u2014 need at least ${a} sessions with similar commands.`),$("Keep using infernoflow and run synthesize again soon.")):C("No new patterns detected \u2014 your workflow is already well captured."),k(n,e);return}g(`New patterns detected: ${f(String(l.length))}`);let r;if(o)g("Auto-approving candidates with \u226580% confidence..."),r=E(i,n,e,l);else{const d=await W(i,n,e,l);r=d.approved,d.rejected>0&&g(`Rejected: ${d.rejected}`)}if(k(n,e),r>0){console.log(),P(`${r} skill${r!==1?"s/agents":"/agent"} synthesized`),console.log(`
|
|
9
|
+
${f("What was created:")}`);for(const d of[...e.approvedSkills||[],...e.approvedAgents||[]].slice(-r)){const v=e.approvedAgents?.find(S=>S.id===d.id)?"agent":"skill";console.log(` ${h("\u2714")} ${v}: ${f(d.name)}`);for(const S of d.filePaths||[])console.log(` ${y(S)}`)}console.log(`
|
|
10
|
+
${f("Next:")} Run ${w("infernoflow agent list")} to see your agents`),console.log(` or ${w("infernoflow agent run <name>")} to execute one`)}}if(u){g("Watching for new patterns \u2014 press Ctrl+C to stop");const e=6e4;await p(),setInterval(p,e),await new Promise(()=>{})}else await p()}export{B as synthesizeCommand};
|