usetraceforge-cli 0.1.4 → 0.1.6

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,76 @@
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
+ const model = genAI.getGenerativeModel({ model: "gemini-1.5-flash" });
54
+ const prompt = `
55
+ I am trying to install an SDK called "usetraceforge".
56
+ I have the following files in my project:
57
+
58
+ ${folderStructure}
59
+
60
+ Please tell me EXACTLY how to configure TraceForge for my framework.
61
+ If this is Next.js 15, tell me to create 'instrumentation.ts'.
62
+ If this is Express, tell me to add 'TraceForge.expressErrorHandler()'.
63
+ Keep your answer very short, professional, and provide the exact code snippets I need to copy and paste.
64
+ The API Key they should use is: ${apiKey}
65
+ The Endpoint is: ${endpoint}
66
+ `;
67
+ const result = await model.generateContent(prompt);
68
+ const response = result.response;
69
+ s.stop(chalk.green("Agent: Analysis complete!"));
70
+ console.log("\n" + chalk.cyan(response.text()) + "\n");
71
+ }
72
+ catch (error) {
73
+ s.stop(chalk.red("Agent: Failed to communicate with Gemini API."));
74
+ console.error(error.message || error);
75
+ }
76
+ }
@@ -4,7 +4,7 @@ import { spinner } from "@clack/prompts";
4
4
  import chalk from "chalk";
5
5
  export async function installNextJs(apiKey, endpoint) {
6
6
  const s = spinner();
7
- s.start("Configuring Next.js...");
7
+ s.start("Agent: Configuring Next.js Instrumentation...");
8
8
  try {
9
9
  // 1. Add API key and endpoint to .env.local
10
10
  const envPath = path.resolve(process.cwd(), ".env.local");
@@ -18,92 +18,35 @@ export async function installNextJs(apiKey, endpoint) {
18
18
  else {
19
19
  fs.writeFileSync(envPath, envVar);
20
20
  }
21
- // 2. Generate TraceForgeProvider.tsx
22
- const componentsDir = path.resolve(process.cwd(), "components");
23
- if (!fs.existsSync(componentsDir)) {
24
- fs.mkdirSync(componentsDir);
25
- }
26
- const providerCode = `"use client";
27
- import { useEffect } from "react";
28
- import TraceForge from "usetraceforge";
29
- import { TraceForgeErrorBoundary } from "usetraceforge/react";
30
-
31
- let initialized = false;
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";
32
26
 
33
- export function TraceForgeProvider({ children }: { children: React.ReactNode }) {
34
- useEffect(() => {
35
- if (initialized) return;
36
- TraceForge.init({
37
- apiKey: process.env.NEXT_PUBLIC_TRACEFORGE_API_KEY!,
38
- endpoint: process.env.NEXT_PUBLIC_TRACEFORGE_INGEST_URL,
39
- autoCapture: true,
40
- });
41
- initialized = true;
42
- }, []);
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
+ }
43
34
 
44
- return <TraceForgeErrorBoundary>{children}</TraceForgeErrorBoundary>;
35
+ export function onRequestError(err: any, request: any) {
36
+ TraceForge.captureException(err, { tags: { route: request.url } });
45
37
  }
46
38
  `;
47
- fs.writeFileSync(path.join(componentsDir, "TraceForgeProvider.tsx"), providerCode);
48
- // 3. Find layout.tsx
49
- const layoutPaths = [
50
- path.resolve(process.cwd(), "app/layout.tsx"),
51
- path.resolve(process.cwd(), "src/app/layout.tsx"),
52
- ];
53
- let targetLayout = null;
54
- for (const p of layoutPaths) {
55
- if (fs.existsSync(p)) {
56
- targetLayout = p;
57
- break;
58
- }
59
- }
60
- if (!targetLayout) {
61
- s.stop(chalk.yellow("Could not find app/layout.tsx. Please configure TraceForge manually."));
62
- return;
39
+ if (!fs.existsSync(instrumentationPath)) {
40
+ fs.writeFileSync(instrumentationPath, instrumentationCode);
41
+ console.log(chalk.green(`\nAgent: Created ${isSrc ? "src/" : ""}instrumentation.ts!`));
63
42
  }
64
- // 4. Inject code into layout.tsx
65
- let layoutCode = fs.readFileSync(targetLayout, "utf-8");
66
- if (layoutCode.includes("TraceForgeProvider")) {
67
- s.stop(chalk.green("TraceForge is already configured in layout.tsx!"));
68
- return;
69
- }
70
- const importStatement = `import { TraceForgeProvider } from "@/components/TraceForgeProvider";\n`;
71
- layoutCode = importStatement + layoutCode;
72
- layoutCode = layoutCode.replace(/(<body[^>]*>)/g, `$1\n <TraceForgeProvider>`);
73
- layoutCode = layoutCode.replace(/<\/body>/g, ` </TraceForgeProvider>\n </body>`);
74
- fs.writeFileSync(targetLayout, layoutCode);
75
- // 5. Update next.config.js / next.config.mjs
76
- const configPaths = [
77
- path.resolve(process.cwd(), "next.config.mjs"),
78
- path.resolve(process.cwd(), "next.config.js"),
79
- ];
80
- let targetConfig = null;
81
- for (const p of configPaths) {
82
- if (fs.existsSync(p)) {
83
- targetConfig = p;
84
- break;
85
- }
86
- }
87
- if (targetConfig) {
88
- let configCode = fs.readFileSync(targetConfig, "utf-8");
89
- if (!configCode.includes("withTraceForgeConfig")) {
90
- const isMjs = targetConfig.endsWith(".mjs") || configCode.includes("export default");
91
- // Add import
92
- if (isMjs) {
93
- configCode = `import { withTraceForgeConfig } from "usetraceforge/next-plugin";\n` + configCode;
94
- configCode = configCode.replace(/export default (.+);/g, `export default withTraceForgeConfig($1);`);
95
- }
96
- else {
97
- configCode = `const { withTraceForgeConfig } = require("usetraceforge/next-plugin");\n` + configCode;
98
- configCode = configCode.replace(/module\.exports = (.+);/g, `module.exports = withTraceForgeConfig($1);`);
99
- }
100
- fs.writeFileSync(targetConfig, configCode);
101
- }
43
+ else {
44
+ console.log(chalk.yellow(`\nAgent: instrumentation.ts already exists. Please manually add TraceForge.`));
102
45
  }
103
- s.stop(chalk.green("Next.js configuration complete!"));
46
+ s.stop(chalk.green("Agent: Next.js configuration complete!"));
104
47
  }
105
48
  catch (error) {
106
- s.stop(chalk.red("Failed to configure Next.js automatically."));
49
+ s.stop(chalk.red("Agent: An error occurred."));
107
50
  console.error(error);
108
51
  }
109
52
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "usetraceforge-cli",
3
- "version": "0.1.4",
3
+ "version": "0.1.6",
4
4
  "description": "TraceForge CLI Wizard for 2-click installations",
5
5
  "bin": {
6
6
  "traceforge": "./dist/index.js"
@@ -17,12 +17,14 @@
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
- "execa": "^9.3.0"
22
+ "execa": "^9.3.0",
23
+ "ts-morph": "^28.0.0"
22
24
  },
23
25
  "devDependencies": {
24
26
  "@types/node": "^20.0.0",
25
- "typescript": "^5.5.4",
26
- "tsx": "^4.16.2"
27
+ "tsx": "^4.16.2",
28
+ "typescript": "^5.5.4"
27
29
  }
28
30
  }