usetraceforge-cli 0.1.5 → 0.1.7

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/index.js CHANGED
@@ -3,6 +3,7 @@ import { intro, outro, text, select, spinner } from "@clack/prompts";
3
3
  import chalk from "chalk";
4
4
  import { execa } from "execa";
5
5
  import { installNextJs } from "./installers/nextjs.js";
6
+ import { runGeminiAgent } from "./installers/gemini.js";
6
7
  async function main() {
7
8
  console.log();
8
9
  intro(chalk.bgBlue.white.bold(" TraceForge Wizard "));
@@ -21,8 +22,9 @@ async function main() {
21
22
  const framework = await select({
22
23
  message: "Which framework are you using?",
23
24
  options: [
24
- { value: "nextjs", label: "Next.js (React)" },
25
- { value: "express", label: "Express (Node.js)" },
25
+ { value: "nextjs", label: "Next.js (Prebuilt)" },
26
+ { value: "express", label: "Express.js (Prebuilt)" },
27
+ { value: "gemini", label: "šŸ¤– Let AI scan my codebase (Gemini Agent)" },
26
28
  ],
27
29
  });
28
30
  if (typeof framework !== "string") {
@@ -47,9 +49,11 @@ async function main() {
47
49
  await installNextJs(apiKey, endpoint);
48
50
  }
49
51
  else if (framework === "express") {
50
- // await installExpress(apiKey);
51
52
  console.log(chalk.yellow("Express auto-installation coming soon."));
52
53
  }
54
+ else if (framework === "gemini") {
55
+ await runGeminiAgent(apiKey, endpoint);
56
+ }
53
57
  outro(chalk.green("✨ You're all set! TraceForge is now protecting your application."));
54
58
  }
55
59
  main().catch((err) => {
@@ -0,0 +1,81 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { text, spinner } from "@clack/prompts";
4
+ import chalk from "chalk";
5
+ import { GoogleGenerativeAI } from "@google/generative-ai";
6
+ function getFiles(dir, fileList = [], depth = 0) {
7
+ if (depth > 3)
8
+ return fileList;
9
+ const files = fs.readdirSync(dir);
10
+ for (const file of files) {
11
+ if (file === "node_modules" || file === ".git" || file === ".next" || file === "dist") {
12
+ continue;
13
+ }
14
+ const filePath = path.join(dir, file);
15
+ if (fs.statSync(filePath).isDirectory()) {
16
+ getFiles(filePath, fileList, depth + 1);
17
+ }
18
+ else {
19
+ fileList.push(filePath.replace(process.cwd(), ""));
20
+ }
21
+ }
22
+ return fileList;
23
+ }
24
+ export async function runGeminiAgent(apiKey, endpoint) {
25
+ console.log(chalk.blue("\nšŸ¤– Initializing TraceForge AI Agent..."));
26
+ // Try to find Gemini Key in .env
27
+ let geminiKey = process.env.GEMINI_API_KEY;
28
+ const envPath = path.resolve(process.cwd(), ".env");
29
+ if (!geminiKey && fs.existsSync(envPath)) {
30
+ const envContent = fs.readFileSync(envPath, "utf-8");
31
+ const match = envContent.match(/GEMINI_API_KEY="?([^"\n]+)"?/);
32
+ if (match)
33
+ geminiKey = match[1];
34
+ }
35
+ if (!geminiKey) {
36
+ const keyResponse = await text({
37
+ message: "Please enter your Google Gemini API Key (we won't save this):",
38
+ placeholder: "AIzaSy...",
39
+ });
40
+ if (typeof keyResponse === "symbol" || !keyResponse) {
41
+ console.log(chalk.red("Agent: Cancelled."));
42
+ return;
43
+ }
44
+ geminiKey = keyResponse;
45
+ }
46
+ const s = spinner();
47
+ s.start("Agent: Scanning your codebase...");
48
+ const files = getFiles(process.cwd());
49
+ const folderStructure = files.join("\n");
50
+ s.message("Agent: Analyzing architecture with Gemini...");
51
+ try {
52
+ const genAI = new GoogleGenerativeAI(geminiKey);
53
+ // Use gemini-pro as a safe fallback since 1.5-flash threw a 404 on your key
54
+ const model = genAI.getGenerativeModel({ model: "gemini/gemini-3.1-flash-lite" });
55
+ const prompt = `
56
+ I am trying to install an SDK called "usetraceforge".
57
+ I have the following files in my project:
58
+
59
+ ${folderStructure}
60
+
61
+ Here is the documentation for usetraceforge:
62
+ - Next.js 15+ Server: Create 'instrumentation.ts' (or 'src/instrumentation.ts') exporting 'register' and 'onRequestError(err, request)' which calls 'TraceForge.captureException(err, { tags: { route: request.url } })'.
63
+ - Next.js Client: Wrap your app with '<TraceForgeProvider>' from 'usetraceforge/react'.
64
+ - Express: Call 'app.use(TraceForge.expressErrorHandler())' AFTER all routes but BEFORE custom error handlers.
65
+ - Initialization: You must always call 'TraceForge.init({ apiKey, endpoint })' as early as possible.
66
+
67
+ The user's API Key is: ${apiKey}
68
+ The user's Endpoint is: ${endpoint}
69
+
70
+ Based on the folder structure above, tell the developer exactly which files they need to edit and provide the exact code snippets they need to copy and paste to configure TraceForge. Be concise, accurate, and professional.
71
+ `;
72
+ const result = await model.generateContent(prompt);
73
+ const response = result.response;
74
+ s.stop(chalk.green("Agent: Analysis complete!"));
75
+ console.log("\n" + chalk.cyan(response.text()) + "\n");
76
+ }
77
+ catch (error) {
78
+ s.stop(chalk.red("Agent: Failed to communicate with Gemini API."));
79
+ console.error(error.message || error);
80
+ }
81
+ }
@@ -2,10 +2,9 @@ import fs from "fs";
2
2
  import path from "path";
3
3
  import { spinner } from "@clack/prompts";
4
4
  import chalk from "chalk";
5
- import { Project, SyntaxKind } from "ts-morph";
6
5
  export async function installNextJs(apiKey, endpoint) {
7
6
  const s = spinner();
8
- s.start("Agent: Scanning and configuring Next.js...");
7
+ s.start("Agent: Configuring Next.js Instrumentation...");
9
8
  try {
10
9
  // 1. Add API key and endpoint to .env.local
11
10
  const envPath = path.resolve(process.cwd(), ".env.local");
@@ -19,93 +18,35 @@ export async function installNextJs(apiKey, endpoint) {
19
18
  else {
20
19
  fs.writeFileSync(envPath, envVar);
21
20
  }
22
- const project = new Project();
23
- // 2. Next.js Config AST Injection
24
- const configPaths = [
25
- path.resolve(process.cwd(), "next.config.ts"),
26
- path.resolve(process.cwd(), "next.config.mjs"),
27
- path.resolve(process.cwd(), "next.config.js"),
28
- ];
29
- let targetConfig = null;
30
- for (const p of configPaths) {
31
- if (fs.existsSync(p)) {
32
- targetConfig = p;
33
- break;
34
- }
35
- }
36
- if (targetConfig) {
37
- const sourceFile = project.addSourceFileAtPath(targetConfig);
38
- const text = sourceFile.getFullText();
39
- if (!text.includes("withTraceForgeConfig")) {
40
- const isEsm = targetConfig.endsWith(".ts") || targetConfig.endsWith(".mjs") || text.includes("export default");
41
- if (isEsm) {
42
- sourceFile.addImportDeclaration({
43
- namedImports: ["withTraceForgeConfig"],
44
- moduleSpecifier: "usetraceforge/next-plugin"
45
- });
46
- const defaultExport = sourceFile.getExportAssignment(d => !d.isExportEquals());
47
- if (defaultExport) {
48
- const expression = defaultExport.getExpression();
49
- defaultExport.setExpression(`withTraceForgeConfig(${expression.getText()})`);
50
- }
51
- else {
52
- console.log(chalk.yellow("\nAgent: Could not parse export default in next.config. Please wrap your config manually."));
53
- }
54
- }
55
- else {
56
- // Fallback to simpler regex for CJS files if AST binary expression is too complex
57
- let configCode = fs.readFileSync(targetConfig, "utf-8");
58
- configCode = `const { withTraceForgeConfig } = require("usetraceforge/next-plugin");\n` + configCode;
59
- configCode = configCode.replace(/module\.exports = (.+);/g, `module.exports = withTraceForgeConfig($1);`);
60
- fs.writeFileSync(targetConfig, configCode);
61
- }
62
- sourceFile.saveSync();
63
- }
64
- }
65
- else {
66
- console.log(chalk.yellow("\nAgent: Could not locate next.config.ts/mjs. Please configure Webpack loader manually."));
67
- }
68
- // 3. Layout AST Injection
69
- const layoutPaths = [
70
- path.resolve(process.cwd(), "app/layout.tsx"),
71
- path.resolve(process.cwd(), "src/app/layout.tsx"),
72
- ];
73
- let targetLayout = null;
74
- for (const p of layoutPaths) {
75
- if (fs.existsSync(p)) {
76
- targetLayout = p;
77
- break;
78
- }
79
- }
80
- if (targetLayout) {
81
- const sourceFile = project.addSourceFileAtPath(targetLayout);
82
- if (!sourceFile.getFullText().includes("TraceForgeProvider")) {
83
- sourceFile.addImportDeclaration({
84
- namedImports: ["TraceForgeProvider"],
85
- moduleSpecifier: "usetraceforge/react"
86
- });
87
- // Find body tag
88
- const jsxElements = sourceFile.getDescendantsOfKind(SyntaxKind.JsxElement);
89
- let bodyTag = jsxElements.find(el => el.getOpeningElement().getTagNameNode().getText() === "body");
90
- if (bodyTag) {
91
- const childrenText = bodyTag.getJsxChildren().map(c => c.getText()).join("");
92
- const opening = bodyTag.getOpeningElement().getText();
93
- const closing = bodyTag.getClosingElement().getText();
94
- bodyTag.replaceWithText(`${opening}\n <TraceForgeProvider>\n ${childrenText}\n </TraceForgeProvider>\n ${closing}`);
95
- sourceFile.saveSync();
96
- }
97
- else {
98
- console.log(chalk.yellow("\nAgent: Could not find <body> tag in layout.tsx. Please add <TraceForgeProvider> manually."));
99
- }
100
- }
21
+ // 2. Generate instrumentation.ts
22
+ const isSrc = fs.existsSync(path.resolve(process.cwd(), "src"));
23
+ const instrumentationDir = isSrc ? path.resolve(process.cwd(), "src") : process.cwd();
24
+ const instrumentationPath = path.resolve(instrumentationDir, "instrumentation.ts");
25
+ const instrumentationCode = `import TraceForge from "usetraceforge";
26
+
27
+ export function register() {
28
+ TraceForge.init({
29
+ apiKey: process.env.NEXT_PUBLIC_TRACEFORGE_API_KEY!,
30
+ endpoint: process.env.NEXT_PUBLIC_TRACEFORGE_INGEST_URL,
31
+ autoCapture: true,
32
+ });
33
+ }
34
+
35
+ export function onRequestError(err: any, request: any) {
36
+ TraceForge.captureException(err, { tags: { route: request.url } });
37
+ }
38
+ `;
39
+ if (!fs.existsSync(instrumentationPath)) {
40
+ fs.writeFileSync(instrumentationPath, instrumentationCode);
41
+ console.log(chalk.green(`\nAgent: Created ${isSrc ? "src/" : ""}instrumentation.ts!`));
101
42
  }
102
43
  else {
103
- console.log(chalk.yellow("\nAgent: Could not find layout.tsx. Please add <TraceForgeProvider> manually."));
44
+ console.log(chalk.yellow(`\nAgent: instrumentation.ts already exists. Please manually add TraceForge.`));
104
45
  }
105
46
  s.stop(chalk.green("Agent: Next.js configuration complete!"));
106
47
  }
107
48
  catch (error) {
108
- s.stop(chalk.red("Agent: An error occurred while parsing files. Please configure manually."));
49
+ s.stop(chalk.red("Agent: An error occurred."));
109
50
  console.error(error);
110
51
  }
111
52
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usetraceforge-cli",
3
- "version": "0.1.5",
3
+ "version": "0.1.7",
4
4
  "description": "TraceForge CLI Wizard for 2-click installations",
5
5
  "bin": {
6
6
  "traceforge": "./dist/index.js"
@@ -17,6 +17,7 @@
17
17
  },
18
18
  "dependencies": {
19
19
  "@clack/prompts": "^0.7.0",
20
+ "@google/generative-ai": "^0.24.1",
20
21
  "chalk": "^5.3.0",
21
22
  "execa": "^9.3.0",
22
23
  "ts-morph": "^28.0.0"