@jeeva1398/eventa 0.1.0 → 0.1.1
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/README.md +1 -1
- package/dist/index.js +115 -24
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -27,7 +27,7 @@ Eventa parses the stack trace or `tsc` output, reads the source lines it points
|
|
|
27
27
|
Reviews `git diff --staged`, falling back to unstaged changes. Use `--base main` to review everything since `main`. Each file gets static checks first (missing `await`, SQL/command injection, `eval`, sync fs, empty `catch`, disabled TLS, hard-coded secrets), then the model confirms or dismisses them and reports `- [severity] line N: problem → fix`.
|
|
28
28
|
|
|
29
29
|
### `eventa deps`
|
|
30
|
-
Runs `npm audit` and `npm outdated` and scans your imports. It prints the vulnerabilities with their **exact fix commands**, outdated packages (flagging major bumps), unused dependencies and packages that are imported but not declared. Then it
|
|
30
|
+
Runs `npm audit` and `npm outdated` and scans your imports. It prints the vulnerabilities with their **exact fix commands**, outdated packages (flagging major bumps), unused dependencies and packages that are imported but not declared. Then it prints an **action plan** built only from facts: the fix commands, verified breaking-change notes for about 25 popular packages (others say "read the changelog" instead of guessing), and cleanup commands. It is instant and works without the model. Add `--ai` for optional extra commentary from the model.
|
|
31
31
|
|
|
32
32
|
### More
|
|
33
33
|
- `eventa ask <question>`: any Node.js question
|
package/dist/index.js
CHANGED
|
@@ -559,6 +559,11 @@ function fixPlan(vulns) {
|
|
|
559
559
|
}
|
|
560
560
|
return [...groups].map(([command, fixes]) => ({ command, fixes }));
|
|
561
561
|
}
|
|
562
|
+
function breaking(current, latest) {
|
|
563
|
+
const [cMajor, cMinor] = current.split(".");
|
|
564
|
+
const [lMajor, lMinor] = latest.split(".");
|
|
565
|
+
return cMajor !== lMajor || cMajor === "0" && cMinor !== lMinor;
|
|
566
|
+
}
|
|
562
567
|
function parseOutdated(text) {
|
|
563
568
|
const json = parseJson(text) ?? {};
|
|
564
569
|
return Object.entries(json).map(([name, v]) => ({
|
|
@@ -566,7 +571,7 @@ function parseOutdated(text) {
|
|
|
566
571
|
current: v.current,
|
|
567
572
|
wanted: v.wanted,
|
|
568
573
|
latest: v.latest,
|
|
569
|
-
major: Boolean(v.current && v.latest && v.current
|
|
574
|
+
major: Boolean(v.current && v.latest && breaking(v.current, v.latest))
|
|
570
575
|
}));
|
|
571
576
|
}
|
|
572
577
|
function packageName(specifier) {
|
|
@@ -644,21 +649,99 @@ async function collectDeps(cwd) {
|
|
|
644
649
|
};
|
|
645
650
|
}
|
|
646
651
|
|
|
652
|
+
// src/prompts/upgrades.ts
|
|
653
|
+
var UPGRADE_NOTES = {
|
|
654
|
+
"express@5": "Express 5 uses path-to-regexp v8: wildcard routes must be named (`/*splat`) and optional params use braces; `req.param()` and `app.del()` were removed; rejected promises in async handlers now go to error middleware. Run your route tests.",
|
|
655
|
+
"body-parser@2": "body-parser 2 (used by Express 5) drops deprecated options and changes `urlencoded` `extended` default to false; check form parsing.",
|
|
656
|
+
"chalk@5": "chalk 5 is ESM-only; `require('chalk')` fails in CommonJS. Stay on 4 for CJS or migrate the project to ESM.",
|
|
657
|
+
"node-fetch@3": "node-fetch 3 is ESM-only. On Node 18+ prefer the built-in global `fetch` and remove the dependency.",
|
|
658
|
+
uuid: "Deep imports such as `require('uuid/v4')` were removed in v8; use `const { v4 } = require('uuid')`.",
|
|
659
|
+
"mongoose@7": "Mongoose 7 removed callback support from model methods (use async/await) and changed the `strictQuery` default.",
|
|
660
|
+
"mongoose@8": "Mongoose 7 removed callback support from model methods (use async/await) and changed the `strictQuery` default; Mongoose 8 needs MongoDB 4.4+.",
|
|
661
|
+
"jest@28": "Jest 28 no longer bundles jsdom (install `jest-environment-jsdom`) and renamed `testURL` to `testEnvironmentOptions.url`.",
|
|
662
|
+
"jest@29": "Jest 28 no longer bundles jsdom (install `jest-environment-jsdom`) and renamed `testURL` to `testEnvironmentOptions.url`; Jest 29 changed the default snapshot format.",
|
|
663
|
+
"eslint@9": "ESLint 9 uses flat config (`eslint.config.js`) by default and ignores `.eslintrc`; many plugins need updated versions.",
|
|
664
|
+
"typescript@5": "TypeScript 5 deprecates flags such as `importsNotUsedAsValues` and implements standard decorators unless `experimentalDecorators` is set; expect some new type errors.",
|
|
665
|
+
"axios@1": "axios 1.x adds an `exports` map, so deep imports like `axios/lib/...` break, and some TypeScript types were renamed.",
|
|
666
|
+
helmet: "Newer helmet enables a strict Content-Security-Policy and cross-origin headers by default, which can block inline scripts and third-party assets; test your pages.",
|
|
667
|
+
inquirer: "inquirer 9+ is ESM-only and v10+ moved to the new `@inquirer/prompts` API.",
|
|
668
|
+
got: "got 12+ is ESM-only and changed several option names; consider native `fetch` for simple calls.",
|
|
669
|
+
"webpack@5": "webpack 5 removed automatic Node.js polyfills (`Buffer`, `process`, `crypto`) for browser bundles; add `resolve.fallback` where needed. It also fixes ERR_OSSL_EVP_UNSUPPORTED on Node 17+.",
|
|
670
|
+
dotenv: "Low risk: dotenv 16 keeps the same `config()` API and adds multiline values.",
|
|
671
|
+
"@nestjs/core@11": "Nest 11 requires Node 20+ and uses Express 5 by default, so wildcard routes change syntax (`*` must be named, e.g. `*splat`). Upgrade all `@nestjs/*` packages together.",
|
|
672
|
+
"@nestjs/common@11": "Nest 11 requires Node 20+ and uses Express 5 by default, so wildcard routes change syntax (`*` must be named, e.g. `*splat`). Upgrade all `@nestjs/*` packages together.",
|
|
673
|
+
"@prisma/client": "Prisma 5 removed `rejectOnNotFound` (use `findUniqueOrThrow`); Prisma 6 needs Node 18.18+ and TypeScript 5.1+, and `Bytes` fields become `Uint8Array`. Upgrade `prisma` and `@prisma/client` to the same version and re-run `prisma generate`.",
|
|
674
|
+
prisma: "Upgrade `prisma` and `@prisma/client` to the same version and re-run `prisma generate`; Prisma 5 removed `rejectOnNotFound`, Prisma 6 needs Node 18.18+ and TypeScript 5.1+.",
|
|
675
|
+
"class-validator@0": "Low risk for most apps: 0.14 fixes a security issue where unknown values skipped validation; `forbidUnknownValues` now defaults to true, so plain objects without decorators are rejected.",
|
|
676
|
+
"redis@4": "node-redis 4 is promise-based: call `await client.connect()` before use, and commands are camelCase (`hSet`, not `hset`) with no callbacks.",
|
|
677
|
+
glob: "glob 9+ has a promise API only (no callbacks) and a named export: `import { glob } from 'glob'`.",
|
|
678
|
+
rimraf: "rimraf 4+ has a promise API only and a named export: `import { rimraf } from 'rimraf'`."
|
|
679
|
+
};
|
|
680
|
+
var major = (version) => version?.split(".")[0];
|
|
681
|
+
function upgradeNote(name, latest) {
|
|
682
|
+
return UPGRADE_NOTES[`${name}@${major(latest)}`] ?? UPGRADE_NOTES[name];
|
|
683
|
+
}
|
|
684
|
+
|
|
685
|
+
// src/context/depsPlan.ts
|
|
686
|
+
var SEVERITY_RANK = ["critical", "high", "moderate", "low", "info"];
|
|
687
|
+
function buildPlan(r) {
|
|
688
|
+
const sections = [];
|
|
689
|
+
const byName = new Map(r.vulnerabilities.map((v) => [v.name, v]));
|
|
690
|
+
if (r.vulnerabilities.length) {
|
|
691
|
+
const items = fixPlan(r.vulnerabilities).map(({ command, fixes }) => {
|
|
692
|
+
const vulns = fixes.map((n) => byName.get(n));
|
|
693
|
+
const worst = vulns.map((v) => v.severity).sort((a, b) => SEVERITY_RANK.indexOf(a) - SEVERITY_RANK.indexOf(b))[0];
|
|
694
|
+
const titles = vulns.map((v) => `${v.name}: ${v.title ?? "advisory"}`).join("; ");
|
|
695
|
+
if (command === "no fix available") return `- **No fix available** (${worst}): ${titles}. Replace the package or check whether the vulnerable code is reachable.`;
|
|
696
|
+
const major2 = command.endsWith("(major)");
|
|
697
|
+
return `- \`${command.replace(" (major)", "")}\`${major2 ? " **(major version, see notes below)**" : ""} (${worst}): ${titles}.`;
|
|
698
|
+
});
|
|
699
|
+
sections.push(`**1. Security**
|
|
700
|
+
${items.join("\n")}`);
|
|
701
|
+
}
|
|
702
|
+
const majors = r.outdated.filter((o) => o.major);
|
|
703
|
+
if (majors.length) {
|
|
704
|
+
const items = majors.map((o) => {
|
|
705
|
+
const note = upgradeNote(o.name, o.latest);
|
|
706
|
+
return `- **${o.name} ${o.current ?? "?"} \u2192 ${o.latest}**: ${note ?? "no verified notes. Read its changelog / release notes before upgrading."}`;
|
|
707
|
+
});
|
|
708
|
+
sections.push(`**2. Major upgrades**
|
|
709
|
+
${items.join("\n")}`);
|
|
710
|
+
}
|
|
711
|
+
const cleanup = [];
|
|
712
|
+
if (r.unused.length) cleanup.push(`- \`npm uninstall ${r.unused.join(" ")}\`: not imported anywhere (double-check config files, scripts and dynamic requires first).`);
|
|
713
|
+
if (r.missing.length) cleanup.push(`- \`npm install ${r.missing.join(" ")}\`: imported in code but missing from package.json.`);
|
|
714
|
+
const minors = r.outdated.filter((o) => !o.major);
|
|
715
|
+
if (minors.length) cleanup.push(`- \`npm update\`: safe minor/patch updates for ${minors.map((m) => m.name).join(", ")}.`);
|
|
716
|
+
if (cleanup.length) sections.push(`**3. Cleanup**
|
|
717
|
+
${cleanup.join("\n")}`);
|
|
718
|
+
return sections.join("\n\n");
|
|
719
|
+
}
|
|
720
|
+
|
|
647
721
|
// src/prompts/deps.ts
|
|
648
722
|
function depsPrompt(r, nodeVersion) {
|
|
649
|
-
const
|
|
723
|
+
const lines2 = [`Node ${nodeVersion}`];
|
|
650
724
|
if (r.vulnerabilities.length) {
|
|
651
|
-
|
|
652
|
-
|
|
725
|
+
const title = new Map(r.vulnerabilities.map((v) => [v.name, v.title]));
|
|
726
|
+
lines2.push("Vulnerabilities:", ...r.vulnerabilities.slice(0, 20).map((v) => `- ${v.name} [${v.severity}] ${v.title ?? ""}`));
|
|
727
|
+
lines2.push(
|
|
728
|
+
"Fix commands (already computed and correct):",
|
|
729
|
+
...fixPlan(r.vulnerabilities).map((f) => `- ${f.command} \u2192 fixes ${f.fixes.map((n) => title.get(n) ? `${n} (${title.get(n)})` : n).join(", ")}`)
|
|
730
|
+
);
|
|
653
731
|
}
|
|
654
732
|
if (r.outdated.length) {
|
|
655
|
-
|
|
733
|
+
lines2.push("Outdated:", ...r.outdated.slice(0, 25).map((o) => `- ${o.name} ${o.current ?? "not installed"} \u2192 latest ${o.latest}${o.major ? " (MAJOR)" : ""}`));
|
|
734
|
+
const notes = r.outdated.filter((o) => o.major).map((o) => [o, upgradeNote(o.name, o.latest)]);
|
|
735
|
+
const known = notes.filter(([, n]) => n);
|
|
736
|
+
if (known.length) lines2.push("Verified upgrade notes:", ...known.map(([o, n]) => `- ${o.name} \u2192 ${o.latest}: ${n}`));
|
|
737
|
+
const unknown = notes.filter(([, n]) => !n).map(([o]) => o.name);
|
|
738
|
+
if (unknown.length) lines2.push(`No verified notes for: ${unknown.join(", ")}`);
|
|
656
739
|
}
|
|
657
|
-
if (r.unused.length)
|
|
658
|
-
if (r.missing.length)
|
|
740
|
+
if (r.unused.length) lines2.push(`Possibly unused dependencies: ${r.unused.join(", ")}`);
|
|
741
|
+
if (r.missing.length) lines2.push(`Imported but not declared in package.json: ${r.missing.join(", ")}`);
|
|
659
742
|
return {
|
|
660
|
-
instruction: "You are given facts about a Node.js project's dependencies. Write a short, prioritized action plan:\n1. Security: repeat each fix command exactly as given and say in one line why it matters. Never change versions.\n2. Major upgrades (marked MAJOR only): for each,
|
|
661
|
-
input:
|
|
743
|
+
instruction: "You are given facts about a Node.js project's dependencies. Write a short, prioritized action plan:\n1. Security: repeat each fix command exactly as given and say in one line why it matters, based only on the advisory titles. Never change versions.\n2. Major upgrades (marked MAJOR only): restate the verified upgrade note for each. For packages with no verified notes, say to read their changelog before upgrading; do not guess API changes.\n3. Cleanup: unused packages to remove and missing packages to add.\nDo not repeat the raw lists. Do not invent versions, advisories or APIs. Skip any section with nothing to do.",
|
|
744
|
+
input: lines2.join("\n")
|
|
662
745
|
};
|
|
663
746
|
}
|
|
664
747
|
|
|
@@ -702,9 +785,10 @@ async function depsCommand(config2, opts) {
|
|
|
702
785
|
const spinner = !opts.json && process.stderr.isTTY ? ora2({ text: "Running npm audit and npm outdated\u2026", stream: process.stderr }).start() : null;
|
|
703
786
|
const report = await collectDeps(cwd).finally(() => spinner?.stop());
|
|
704
787
|
const wantAi = opts.ai && !isHealthy(report);
|
|
788
|
+
const plan = isHealthy(report) ? "" : buildPlan(report);
|
|
705
789
|
if (opts.json) {
|
|
706
790
|
const analysis = wantAi ? await generate(config2, depsPrompt(report, process.versions.node), { json: true }) : void 0;
|
|
707
|
-
printJson({ ...report, analysis });
|
|
791
|
+
printJson({ ...report, plan, analysis });
|
|
708
792
|
return;
|
|
709
793
|
}
|
|
710
794
|
printReport(report);
|
|
@@ -712,11 +796,18 @@ async function depsCommand(config2, opts) {
|
|
|
712
796
|
console.log(chalk3.green("\u2714 Dependencies look healthy."));
|
|
713
797
|
return;
|
|
714
798
|
}
|
|
799
|
+
console.log(chalk3.bold.underline("Action plan"));
|
|
800
|
+
await renderStream(lines(plan), { raw: opts.raw });
|
|
715
801
|
if (wantAi) {
|
|
716
|
-
console.log(chalk3.bold.underline("
|
|
802
|
+
console.log(chalk3.bold.underline("\nAI notes"));
|
|
717
803
|
await generate(config2, depsPrompt(report, process.versions.node), opts);
|
|
804
|
+
} else {
|
|
805
|
+
console.log(chalk3.dim("\nRun with --ai for extra AI commentary."));
|
|
718
806
|
}
|
|
719
807
|
}
|
|
808
|
+
async function* lines(text) {
|
|
809
|
+
yield text;
|
|
810
|
+
}
|
|
720
811
|
|
|
721
812
|
// src/commands/doctor.ts
|
|
722
813
|
import chalk4 from "chalk";
|
|
@@ -735,8 +826,8 @@ async function probe(name, check) {
|
|
|
735
826
|
}
|
|
736
827
|
}
|
|
737
828
|
async function doctorCommand(config2) {
|
|
738
|
-
const
|
|
739
|
-
if (
|
|
829
|
+
const major2 = Number(process.versions.node.split(".")[0]);
|
|
830
|
+
if (major2 >= 20) ok(`Node.js ${process.versions.node}`);
|
|
740
831
|
else fail(`Node.js ${process.versions.node}`, "Eventa needs Node.js 20 or newer");
|
|
741
832
|
const ollamaOk = await probe(`Ollama (${config2.model})`, () => new OllamaProvider(config2.model, config2.ollamaHost).check());
|
|
742
833
|
const localOk = await probe(`Built-in runtime (${config2.localModel})`, () => new LocalProvider(config2.localModel).check());
|
|
@@ -744,7 +835,7 @@ async function doctorCommand(config2) {
|
|
|
744
835
|
console.log(chalk4.dim(`
|
|
745
836
|
Provider setting: ${config2.provider} \u2192 using ${active.name} (${active.model})`));
|
|
746
837
|
if (!ollamaOk && !localOk) console.log(chalk4.dim("Neither is ready yet. Eventa installs the built-in runtime and model on first use, or run `eventa model pull` now."));
|
|
747
|
-
return
|
|
838
|
+
return major2 >= 20 && (ollamaOk || localOk);
|
|
748
839
|
}
|
|
749
840
|
|
|
750
841
|
// src/commands/model.ts
|
|
@@ -860,12 +951,12 @@ function userFrames(parsed, cwd, limit = 3) {
|
|
|
860
951
|
}
|
|
861
952
|
function readSnippet(file, marked, radius = 15, cwd = process.cwd()) {
|
|
862
953
|
const targets = (Array.isArray(marked) ? marked : [marked]).slice().sort((a, b) => a - b);
|
|
863
|
-
const
|
|
954
|
+
const lines2 = readFileSync3(file, "utf8").split(/\r?\n/);
|
|
864
955
|
const start = Math.max(1, targets[0] - radius);
|
|
865
|
-
const end = Math.min(
|
|
956
|
+
const end = Math.min(lines2.length, targets[targets.length - 1] + radius);
|
|
866
957
|
const body = [];
|
|
867
958
|
for (let n = start; n <= end; n++) {
|
|
868
|
-
body.push(targets.includes(n) ? `${
|
|
959
|
+
body.push(targets.includes(n) ? `${lines2[n - 1]} // <-- line ${n}, in the stack trace` : lines2[n - 1]);
|
|
869
960
|
}
|
|
870
961
|
return `// ${relative(cwd, file) || file} (lines ${start}-${end})
|
|
871
962
|
${body.join("\n")}`;
|
|
@@ -873,12 +964,12 @@ ${body.join("\n")}`;
|
|
|
873
964
|
function snippetsFor(frames, radius, cwd) {
|
|
874
965
|
const byFile = /* @__PURE__ */ new Map();
|
|
875
966
|
for (const f of frames) {
|
|
876
|
-
const
|
|
877
|
-
const last =
|
|
878
|
-
if (last === void 0 || Math.abs(f.line - last) <= radius * 2)
|
|
879
|
-
byFile.set(f.file,
|
|
967
|
+
const lines2 = byFile.get(f.file) ?? [];
|
|
968
|
+
const last = lines2[lines2.length - 1];
|
|
969
|
+
if (last === void 0 || Math.abs(f.line - last) <= radius * 2) lines2.push(f.line);
|
|
970
|
+
byFile.set(f.file, lines2);
|
|
880
971
|
}
|
|
881
|
-
return [...byFile].map(([file,
|
|
972
|
+
return [...byFile].map(([file, lines2]) => readSnippet(file, lines2, radius, cwd));
|
|
882
973
|
}
|
|
883
974
|
|
|
884
975
|
// src/context/tokens.ts
|
|
@@ -1197,7 +1288,7 @@ async function reviewCommand(config2, opts) {
|
|
|
1197
1288
|
|
|
1198
1289
|
// src/index.ts
|
|
1199
1290
|
var program = new Command();
|
|
1200
|
-
program.name("eventa").description("Offline AI assistant for Node.js developers").version("0.1.
|
|
1291
|
+
program.name("eventa").description("Offline AI assistant for Node.js developers").version("0.1.1").option("-m, --model <name>", "model to use (overrides config)").option("--host <url>", "Ollama host URL").option("-p, --provider <name>", "auto | ollama | local").option("--raw", "print plain output without terminal formatting");
|
|
1201
1292
|
var configFrom = (cmd) => {
|
|
1202
1293
|
const g = cmd.optsWithGlobals();
|
|
1203
1294
|
if (g.provider && !["auto", "ollama", "local"].includes(g.provider)) throw new Error("--provider must be auto, ollama or local");
|
|
@@ -1215,7 +1306,7 @@ program.command("review").description("review your staged git diff (falls back t
|
|
|
1215
1306
|
const { config: config2, raw } = configFrom(cmd);
|
|
1216
1307
|
await reviewCommand(config2, { ...opts, raw });
|
|
1217
1308
|
});
|
|
1218
|
-
program.command("deps").description("audit dependencies: vulnerabilities, outdated, unused and missing packages").option("--
|
|
1309
|
+
program.command("deps").description("audit dependencies: vulnerabilities, outdated, unused and missing packages").option("--ai", "also ask the model for extra commentary (the action plan itself is always deterministic)").option("--json", "print JSON output").action(async (opts, cmd) => {
|
|
1219
1310
|
const { config: config2, raw } = configFrom(cmd);
|
|
1220
1311
|
await depsCommand(config2, { ...opts, raw });
|
|
1221
1312
|
});
|