Conversation
… and editor functionality - Introduced `BlogShare` component for sharing blog links via clipboard or native share dialog. - Added various chart components: - `AreaWidget`: Renders area charts with customizable series and colors. - `BarWidget`: Displays bar charts with adjustable height and color. - `D3LineChart`: Implements a line chart using D3.js for advanced data visualization. - `D3PieChart`: Creates pie charts with customizable inner radius and data. - `LineWidget`: Provides a line chart with two series options. - `PieWidget`: Displays pie charts with customizable inner and outer radii. - `RadarWidget`: Renders radar charts with two series options. - `RadialGaugeWidget`: Implements radial gauge charts for displaying data in a circular format. - `ScatterWidget`: Displays scatter plots with customizable axes and data points. - Developed `MonacoCodeEditor` for a rich code editing experience with syntax highlighting and theming. - Added `MonacoToolbar`, `MonacoTabs`, and `MonacoStatusBar` for enhanced editor controls and navigation. - Implemented theme loading and application for the Monaco editor with support for multiple themes. - Created `robots.txt` and `sitemap.xml` for improved SEO and web crawling. - Added unit tests for custom scorers to ensure scoring logic is functioning as expected. - Introduced `AgentTool` component for displaying agent data in a structured format.
|
Keep this PR in a mergeable state → Learn moreAll Green is an AI agent that automatically: ✅ Addresses code review comments ✅ Fixes failing CI checks ✅ Resolves merge conflicts |
There was a problem hiding this comment.
Sorry @ssdeanx, your pull request is larger than the review limit of 150000 diff characters
|
🤖 Hi @ssdeanx, I've received your request, and I'm working on it now! You can track my progress in the logs for more details. |
Summary by CodeRabbitRelease Notes
✏️ Tip: You can customize this high-level summary in your review settings. WalkthroughBroad modernization of the AgentStack platform introducing chart visualization components, Monaco editor integration, expanded web scraping tooling, comprehensive SEO metadata, agent configuration refinements, and infrastructure updates across UI, tooling, documentation, and deployment configuration. Changes
Estimated code review effort🎯 4 (Complex) | ⏱️ ~60–90 minutes Areas requiring extra attention:
Possibly related PRs
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
|
🤖 I'm sorry @ssdeanx, but I was unable to process your request. Please see the logs for more details. |
Summary of ChangesHello @ssdeanx, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly expands the application's frontend and backend capabilities by introducing a robust set of data visualization components, integrating a full-featured code editor, and enhancing web scraping tools. It also includes foundational improvements in agent configuration, SEO, and developer tooling, aiming to provide a more comprehensive and efficient platform for AI application development and data interaction. Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
Greptile OverviewGreptile SummaryThis PR adds comprehensive data visualization capabilities and a Monaco code editor to the AgentStack application, significantly expanding the UI component library. Major Changes:
Issues Found:
The chart and editor components are well-implemented with proper null checks and cleanup. The network route refactoring introduced a breaking bug that needs immediate attention. Confidence Score: 2/5
Important Files ChangedFile Analysis
Sequence DiagramsequenceDiagram
participant User
participant Dashboard
participant MonacoEditor
participant ChartComponents
participant MastraAPI
participant NetworkRoute
User->>Dashboard: Visit /dashboard
Dashboard->>MastraAPI: Fetch agent data
MastraAPI-->>Dashboard: Return agent metrics
Dashboard->>ChartComponents: Render visualizations
ChartComponents->>ChartComponents: D3LineChart/AreaWidget/etc
ChartComponents-->>Dashboard: Display charts
User->>MonacoEditor: Open code editor
MonacoEditor->>MonacoEditor: Load Monaco instance
MonacoEditor->>MonacoEditor: Apply theme (loadMonaco/applyTheme)
MonacoEditor-->>User: Show editor with syntax highlighting
User->>User: Edit code in tabs
MonacoEditor->>MonacoEditor: Update file state
User->>MonacoEditor: Format code
MonacoEditor->>MonacoEditor: editor.action.formatDocument
User->>MastraAPI: Request network route
MastraAPI->>NetworkRoute: /network/:agentId
Note right of NetworkRoute: Missing agent parameter<br/>causes route failure
NetworkRoute-->>MastraAPI: Error (no agent defined)
MastraAPI-->>User: Route fails
|
| import type * as Monaco from 'monaco-editor' | ||
|
|
||
| const themeLoaders = { | ||
| github: () => import('monaco-themes/themes/GitHub.json', { assert: { type: 'json' } }), |
There was a problem hiding this comment.
syntax: import assertions (assert { type: 'json' }) are deprecated in favor of import attributes. replace with with syntax
| github: () => import('monaco-themes/themes/GitHub.json', { assert: { type: 'json' } }), | |
| github: () => import('monaco-themes/themes/GitHub.json', { with: { type: 'json' } }), |
Prompt To Fix With AI
This is a comment left during a code review.
Path: app/components/monaco/theme-loader.ts
Line: 7:7
Comment:
**syntax:** import assertions (`assert { type: 'json' }`) are deprecated in favor of import attributes. replace with `with` syntax
```suggestion
github: () => import('monaco-themes/themes/GitHub.json', { with: { type: 'json' } }),
```
How can I resolve this? If you propose a fix, please make it concise.| monokai: () => import('monaco-themes/themes/Monokai.json', { assert: { type: 'json' } }), | ||
| dracula: () => import('monaco-themes/themes/Dracula.json', { assert: { type: 'json' } }), | ||
| solarizedDark: () => import('monaco-themes/themes/Solarized-dark.json', { assert: { type: 'json' } }), |
There was a problem hiding this comment.
syntax: same deprecation issue - use with instead of assert
| monokai: () => import('monaco-themes/themes/Monokai.json', { assert: { type: 'json' } }), | |
| dracula: () => import('monaco-themes/themes/Dracula.json', { assert: { type: 'json' } }), | |
| solarizedDark: () => import('monaco-themes/themes/Solarized-dark.json', { assert: { type: 'json' } }), | |
| monokai: () => import('monaco-themes/themes/Monokai.json', { with: { type: 'json' } }), | |
| dracula: () => import('monaco-themes/themes/Dracula.json', { with: { type: 'json' } }), | |
| solarizedDark: () => import('monaco-themes/themes/Solarized-dark.json', { with: { type: 'json' } }), |
Prompt To Fix With AI
This is a comment left during a code review.
Path: app/components/monaco/theme-loader.ts
Line: 8:10
Comment:
**syntax:** same deprecation issue - use `with` instead of `assert`
```suggestion
monokai: () => import('monaco-themes/themes/Monokai.json', { with: { type: 'json' } }),
dracula: () => import('monaco-themes/themes/Dracula.json', { with: { type: 'json' } }),
solarizedDark: () => import('monaco-themes/themes/Solarized-dark.json', { with: { type: 'json' } }),
```
How can I resolve this? If you propose a fix, please make it concise.| networkRoute({ | ||
| path: "/network", | ||
| agent: "agentNetwork", | ||
| path: "/network/:agentId", |
There was a problem hiding this comment.
logic: networkRoute is missing the agent parameter - this will cause the route to fail since it doesn't know which agent to use
| networkRoute({ | |
| path: "/network", | |
| agent: "agentNetwork", | |
| path: "/network/:agentId", | |
| networkRoute({ | |
| path: "/network/:agentId", | |
| agent: "agentNetwork", // Add appropriate agent name |
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/mastra/index.ts
Line: 527:528
Comment:
**logic:** `networkRoute` is missing the `agent` parameter - this will cause the route to fail since it doesn't know which agent to use
```suggestion
networkRoute({
path: "/network/:agentId",
agent: "agentNetwork", // Add appropriate agent name
```
How can I resolve this? If you propose a fix, please make it concise.| // await next(); | ||
| // }, | ||
| ], | ||
| //0 ], |
There was a problem hiding this comment.
syntax: typo in comment - //0 should be just //
| //0 ], | |
| // ], |
Prompt To Fix With AI
This is a comment left during a code review.
Path: src/mastra/index.ts
Line: 584:584
Comment:
**syntax:** typo in comment - `//0` should be just `//`
```suggestion
// ],
```
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
Code Review
This pull request introduces a wide range of new features and improvements. Key additions include a suite of data visualization components using Recharts and D3, and a rich Monaco code editor. There are also significant SEO enhancements, such as the addition of robots.txt, sitemap.xml, and JSON-LD structured data across various layouts. The project structure has been clarified with an updated diagram in the README.md.
My review focuses on several areas: configuration correctness, code cleanup, and prompt consistency. I've identified a critical issue with a duplicated configuration in the ESLint setup, a potential security concern with commented-out CORS settings, and a high-severity issue in an agent prompt that appears incomplete. I've also made several medium-severity suggestions to remove unused code, fix minor inconsistencies in agent definitions, and improve code readability. Overall, this is a substantial and valuable contribution to the project.
| { | ||
| files: [ | ||
| '/app/**/*.ts', | ||
| '/app/**/*.tsx', | ||
| '/src/**/*.ts', | ||
| '/src/**/*.tsx', | ||
| '/ui/**/*.ts', | ||
| '/ui/**/*.tsx', | ||
| '/lib/**/*.ts', | ||
| '/lib/**/*.tsx', | ||
| '/hooks/**/*.ts', | ||
| '/hooks/**/*.tsx', | ||
| '/pages/**/*.ts', | ||
| '/pages/**/*.tsx' | ||
| ], | ||
| rules: { | ||
| '@typescript-eslint/no-meaningless-void-operator': 'error', | ||
| '@typescript-eslint/prefer-string-starts-ends-with': 'warn', | ||
| '@typescript-eslint/prefer-regexp-exec': 'error', | ||
| '@typescript-eslint/no-unnecessary-boolean-literal-compare': | ||
| 'error', | ||
|
|
||
| // Disable some rules that conflict with Standard | ||
| 'no-undef': 'off', // TypeScript handles this | ||
| 'no-redeclare': 'off', | ||
| '@typescript-eslint/no-redeclare': 'error', | ||
|
|
||
| // Additional code quality rules | ||
| eqeqeq: ['error', 'always'], | ||
| curly: ['error', 'all'], | ||
| 'no-multiple-empty-lines': ['error', { max: 2 }], | ||
| 'no-trailing-spaces': 'warn', | ||
| 'eol-last': 'warn', | ||
| }, | ||
| }, |
| 2. For each query, use the \`webScraperTool\` to find information. Make sure \`siteMapExtractorTool\`,\`linkExtractorTool\`, \`htmlToMarkdownTool\`, \`contentCleanerTool\`, | ||
| 4. For all relevant results, use the \`extractLearningsTool\` to get key insights and generate follow-up questions. |
There was a problem hiding this comment.
This instruction in the prompt appears to be incomplete. Line 130 ends with a comma, and step 3 from the original prompt seems to be missing. This could cause the agent to behave unpredictably. It seems like evaluateResultTool should be used in a new step 3.
2. For each query, use the `webScraperTool` to find information. Make sure to use its sub-tools like `siteMapExtractorTool`,`linkExtractorTool`, `htmlToMarkdownTool`, and `contentCleanerTool` as needed.
3. For each result, use the `evaluateResultTool` to determine relevance.
4. For all relevant results, use the `extractLearningsTool` to get key insights and generate follow-up questions.| // cors: { | ||
| // origin: ["*"], // Allow specific origins or '*' for all | ||
| // allowMethods: ["GET", "POST", "PUT", "DELETE", "OPTIONS"], | ||
| // allowHeaders: ["Content-Type", "Authorization"], | ||
| // exposeHeaders: ["Content-Length", "X-Requested-With"], | ||
| // credentials: false, | ||
| // }, | ||
| // middleware: [ |
There was a problem hiding this comment.
The CORS and middleware configurations appear to be commented out. If this is intentional for local development, it's acceptable. However, if this were to be deployed to production, it could pose a security risk by disabling important security headers and cross-origin policies. Please confirm if this change is intended to be permanent or if it should be re-enabled before merging.
| - "cache-components" | ||
| - "turbopack" | ||
| - "react-compiler" | ||
| - "server-components" |
|
|
||
| You are a world-class expert in modern search optimization with deep knowledge of traditional SEO, Answer Engine Optimization (AEO), and Generative Engine Optimization (GEO). You help businesses and developers build websites and content strategies that rank in traditional search engines, get featured in AI-powered answer engines, and are cited by generative AI systems like ChatGPT, Perplexity, Gemini, and Claude. | ||
|
|
||
| domain i might use vercel or i have deanmachines.com |
|
|
||
| You help businesses and developers build modern search strategies that work across traditional search engines, answer engines, and generative AI systems, ensuring maximum visibility, authority, and citations in the evolving search landscape. | ||
|
|
||
| codemap |
| } as Agent | ||
| }, | ||
| enabled: !!agentId, | ||
| enabled: !(!agentId), |
There was a problem hiding this comment.
The expression !(!agentId) is used here and in several other places in this file to convert a value to a boolean. While functionally correct, using the double negation operator !!agentId is a more common and idiomatic pattern in JavaScript/TypeScript for this purpose. It's generally considered more readable.
enabled: !!agentId,| @@ -1,18 +1,19 @@ | |||
| import { MastraClient } from "@mastra/client-js"; | |||
| import ObservabilityError from '../app/dashboard/observability/error'; | |||
| } | ||
|
|
||
| await writer?.write({ type: 'progress', data: { message: `✅ Scraping complete: ${extractedData.length} elements${savedFilePath ? ', saved to ' + savedFilePath : ''}` } }); | ||
| await writer?.write({ type: 'progress', data: { message: `✅ Scraping complete: ${extractedData.length} elements${(savedFilePath !== null) ? ', saved to ' + savedFilePath : ''}` } }); |
There was a problem hiding this comment.
The check (savedFilePath !== null) is a bit verbose. Since savedFilePath is a string | undefined, a simple truthiness check savedFilePath is sufficient and more concise. It also correctly handles the case of an empty string, which would be falsy.
| await writer?.write({ type: 'progress', data: { message: `✅ Scraping complete: ${extractedData.length} elements${(savedFilePath !== null) ? ', saved to ' + savedFilePath : ''}` } }); | |
| await writer?.write({ type: 'progress', data: { message: `✅ Scraping complete: ${extractedData.length} elements${savedFilePath ? ', saved to ' + savedFilePath : ''}` } }); |
There was a problem hiding this comment.
Pull request overview
This PR introduces significant enhancements to the AgentStack codebase focused on data visualization, code editing capabilities, SEO optimization, and improved developer ergonomics. The changes span multiple domains: new chart and Monaco editor components, extensive web scraper tool enhancements, type system improvements, SEO metadata additions, and test coverage for custom scorers.
Key Changes
- New Visualization Components: Added 9 chart components (LineWidget, AreaWidget, BarWidget, PieWidget, ScatterWidget, RadarWidget, RadialGaugeWidget, D3LineChart, D3PieChart) for data visualization throughout the dashboard
- Monaco Code Editor Integration: Introduced a full-featured code editor with theme support, multi-tab editing, and syntax highlighting capabilities
- Web Scraper Enhancements: Significantly expanded the web scraper tool with 20+ new optional parameters for metadata extraction, structured data parsing, language detection, and advanced content filtering
- Type System Refinements: Converted
typedeclarations tointerfaceacross multiple agent files and added propertypeimport statements for better TypeScript practices - SEO Improvements: Added robots.txt, sitemap.xml, OpenGraph metadata, Twitter cards, and JSON-LD structured data across layout files
- Test Coverage: Added unit tests for custom scorers (sourceDiversityScorer, researchCompletenessScorer, summaryQualityScorer)
Reviewed changes
Copilot reviewed 71 out of 74 changed files in this pull request and generated 11 comments.
Show a summary per file
| File | Description |
|---|---|
| ui/agent-tool.ts | New component for displaying agent data in structured format |
| tsconfig.json | Updated to include additional file patterns and resolved JSON module imports |
| src/mastra/tools/web-scraper-tool.ts | Major enhancement with 20+ new parameters, sanitization improvements, and 3 new tools (apiDataFetcher, scrapingScheduler, dataExporter) |
| src/mastra/tools/extractLearningsTool.ts | Fixed agent ID reference from 'learning' to 'learningExtraction' |
| src/mastra/index.ts | Refactored network routing to use dynamic path parameter, removed duplicate network routes |
| src/mastra/agents/*.ts | Consistent conversion from type to interface and type imports across 15+ agent files |
| sitemap.xml | New SEO sitemap with canonical URLs |
| robots.txt | New robots.txt with sitemap reference and bot rules |
| package.json | Added Monaco editor dependencies and bundle analyzer |
| lib/mastra-client.ts | Minor formatting changes and unused import |
| lib/hooks/*.ts | Refactored early return patterns and null coalescing operators |
| eslint.config.cjs | Added duplicate TypeScript rule blocks (needs cleanup) |
| app/layout.tsx | Added comprehensive OpenGraph, Twitter Card, and JSON-LD metadata |
| app/components/monaco/* | New Monaco editor components with theme loading |
| app/components/charts/* | New Recharts and D3 chart widgets |
| app/components/blog-*.tsx | Refactored blog components with structured data |
| app/dashboard/page.tsx | Integrated new chart widgets |
| .github/agents/*.md | Cleaned up agent metadata |
Comments suppressed due to low confidence (1)
lib/mastra-client.ts:2
- Unused import ObservabilityError.
| github: () => import('monaco-themes/themes/GitHub.json', { assert: { type: 'json' } }), | ||
| monokai: () => import('monaco-themes/themes/Monokai.json', { assert: { type: 'json' } }), | ||
| dracula: () => import('monaco-themes/themes/Dracula.json', { assert: { type: 'json' } }), | ||
| solarizedDark: () => import('monaco-themes/themes/Solarized-dark.json', { assert: { type: 'json' } }), |
There was a problem hiding this comment.
Import assertions (assert { type: 'json' }) are deprecated in favor of import attributes. The syntax should be updated to use with instead of assert:
import('monaco-themes/themes/GitHub.json', { with: { type: 'json' } })This applies to all theme imports in lines 7-10. While assert still works in most environments, it's been replaced by the with keyword in the latest ECMAScript specification.
| function sanitizeMarkdown(markdown: string): string { | ||
| try { | ||
| // markdown is guaranteed to be a string by zod schema, so null/undefined checks are redundant. | ||
| // String(markdown) handles potential non-string inputs gracefully. | ||
| // Escape angle brackets to reduce risk of injecting raw HTML when markdown is rendered in HTML contexts. | ||
| // Preserve other markdown syntax but ensure raw HTML tags are neutralized. | ||
| return String(markdown).replace(/</g, '<').replace(/>/g, '>') | ||
| // Comprehensive HTML entity escaping to prevent XSS and parsing issues | ||
| // Escape dangerous characters that could interfere with JSON or HTML rendering | ||
| return String(markdown) | ||
| .replace(/&/g, '&') // Must be first to avoid double-escaping | ||
| .replace(/</g, '<') | ||
| .replace(/>/g, '>') | ||
| .replace(/'/g, ''') | ||
| .replace(/"/g, '"') | ||
| .replace(/\\/g, '\') // Escape backslashes | ||
| .replace(/\n/g, '\\n') // Escape newlines for JSON safety | ||
| .replace(/\r/g, '\\r') // Escape carriage returns | ||
| .replace(/\t/g, '\\t') // Escape tabs | ||
| .replace(/\f/g, '\\f') // Escape form feeds | ||
| .replace(/\0/g, '\\0') // Escape null characters | ||
| } catch { | ||
| return '' | ||
| } |
There was a problem hiding this comment.
The sanitization approach is overly aggressive and will destroy the markdown formatting. Escaping newlines (\n), tabs, and markdown special characters will render the markdown unusable. This function appears to be trying to sanitize markdown for embedding in HTML or JSON, but the current implementation will break all markdown syntax.
Instead of escaping markdown content, consider:
- If the goal is XSS prevention, the markdown should be sanitized when it's converted to HTML for rendering (using a markdown library with XSS protection)
- If storing in JSON, use
JSON.stringify()which handles escaping automatically - If the concern is raw HTML in markdown, strip or escape only HTML tags
The escaping of \n, \r, \t makes the markdown unreadable as it converts actual newlines to literal \n strings.
| @@ -1,18 +1,19 @@ | |||
| import { MastraClient } from "@mastra/client-js"; | |||
| import ObservabilityError from '../app/dashboard/observability/error'; | |||
There was a problem hiding this comment.
The import ObservabilityError is added but never used in this file. This will cause an unused import warning/error from the linter. Either remove the import or use it in the code.
| // await next(); | ||
| // }, | ||
| ], | ||
| //0 ], |
There was a problem hiding this comment.
Invalid commented code contains //0 which appears to be a typo. Should be // for proper comment formatting.
| } as Agent | ||
| }, | ||
| enabled: !!agentId, | ||
| enabled: !(!agentId), |
There was a problem hiding this comment.
[nitpick] The double negation !(!agentId) is unnecessarily complex and less readable than simply using !!agentId. Both evaluate to the same boolean value, but !!agentId is the more idiomatic pattern in JavaScript/TypeScript.
| { | ||
| files: [ | ||
| '/app/**/*.ts', | ||
| '/app/**/*.tsx', | ||
| '/src/**/*.ts', | ||
| '/src/**/*.tsx', | ||
| '/ui/**/*.ts', | ||
| '/ui/**/*.tsx', | ||
| '/lib/**/*.ts', | ||
| '/lib/**/*.tsx', | ||
| '/hooks/**/*.ts', | ||
| '/hooks/**/*.tsx', | ||
| '/pages/**/*.ts', | ||
| '/pages/**/*.tsx' | ||
| ], | ||
| rules: { | ||
| '@typescript-eslint/no-meaningless-void-operator': 'error', | ||
| '@typescript-eslint/prefer-string-starts-ends-with': 'warn', | ||
| '@typescript-eslint/prefer-regexp-exec': 'error', | ||
| '@typescript-eslint/no-unnecessary-boolean-literal-compare': | ||
| 'error', | ||
|
|
||
| // Disable some rules that conflict with Standard | ||
| 'no-undef': 'off', // TypeScript handles this | ||
| 'no-redeclare': 'off', | ||
| '@typescript-eslint/no-redeclare': 'error', | ||
|
|
||
| // Additional code quality rules | ||
| eqeqeq: ['error', 'always'], | ||
| curly: ['error', 'all'], | ||
| 'no-multiple-empty-lines': ['error', { max: 2 }], | ||
| 'no-trailing-spaces': 'warn', | ||
| 'eol-last': 'warn', | ||
| }, | ||
| }, | ||
| { | ||
| files: [ | ||
| '/app/**/*.ts', | ||
| '/app/**/*.tsx', | ||
| '/src/**/*.ts', | ||
| '/src/**/*.tsx', | ||
| '/ui/**/*.ts', | ||
| '/ui/**/*.tsx', | ||
| '/lib/**/*.ts', | ||
| '/lib/**/*.tsx', | ||
| '/hooks/**/*.ts', | ||
| '/hooks/**/*.tsx', | ||
| '/pages/**/*.ts', | ||
| '/pages/**/*.tsx' | ||
| ], | ||
| rules: { | ||
| '@typescript-eslint/no-meaningless-void-operator': 'error', | ||
| '@typescript-eslint/prefer-string-starts-ends-with': 'warn', | ||
| '@typescript-eslint/prefer-regexp-exec': 'error', | ||
| '@typescript-eslint/no-unnecessary-boolean-literal-compare': | ||
| 'error', | ||
|
|
||
| // Disable some rules that conflict with Standard | ||
| 'no-undef': 'off', // TypeScript handles this | ||
| 'no-redeclare': 'off', | ||
| '@typescript-eslint/no-redeclare': 'error', | ||
|
|
||
| // Additional code quality rules | ||
| eqeqeq: ['error', 'always'], | ||
| curly: ['error', 'all'], | ||
| 'no-multiple-empty-lines': ['error', { max: 2 }], | ||
| 'no-trailing-spaces': 'warn', | ||
| 'eol-last': 'warn', | ||
| }, | ||
| }, |
There was a problem hiding this comment.
The ESLint configuration has duplicated rule blocks for lines 90-124 and 125-159. Both blocks define identical file patterns and rules. This duplication is unnecessary and can lead to confusion and maintenance issues. Consider removing one of these duplicate blocks.
| const ld = useMemo(() => { | ||
| const url = typeof window !== "undefined" ? window.location.href : "https://deanmachines.com"; | ||
| return { | ||
| "@context": "https://schema.org", | ||
| "@type": "WebPage", | ||
| name: title, | ||
| description: description ?? '', | ||
| publisher: { "@type": "Organization", name: "AgentStack", url: "https://deanmachines.com" }, | ||
| url, | ||
| mainEntityOfPage: url, | ||
| }; | ||
| }, [title, description]); |
There was a problem hiding this comment.
Unused variable ld.
| const ld = useMemo(() => { | |
| const url = typeof window !== "undefined" ? window.location.href : "https://deanmachines.com"; | |
| return { | |
| "@context": "https://schema.org", | |
| "@type": "WebPage", | |
| name: title, | |
| description: description ?? '', | |
| publisher: { "@type": "Organization", name: "AgentStack", url: "https://deanmachines.com" }, | |
| url, | |
| mainEntityOfPage: url, | |
| }; | |
| }, [title, description]); |
| @@ -1,9 +1,10 @@ | |||
| import { GoogleGenerativeAIProviderMetadata, GoogleGenerativeAIProviderOptions } from '@ai-sdk/google'; | |||
| import type { GoogleGenerativeAIProviderOptions } from '@ai-sdk/google'; | |||
| import { GoogleGenerativeAIProviderMetadata } from '@ai-sdk/google'; | |||
There was a problem hiding this comment.
Unused import GoogleGenerativeAIProviderMetadata.
| const nextRun = new Date(Date.now() + 3600000).toISOString() // 1 hour from now | ||
|
|
||
| // Store job configuration (in production, persist to database) | ||
| const jobConfig = { |
There was a problem hiding this comment.
Unused variable jobConfig.
There was a problem hiding this comment.
Actionable comments posted: 51
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (17)
.github/agents/Thinking-Beast-Mode.agent.md (1)
37-109: Replace all "thoughtbox" references with "sequentialthinking". The toolthoughtboxdoes not exist in your agent framework. Found two instances: line 41 ("Usethoughtboxtool for deep cognitive architecture activation") and in the Constitutional Sequential Thinking Framework section ("You must use thethoughtboxtool for every problem"). Both should referencesequentialthinking, the actual sequential thinking tool defined in your framework.src/mastra/agents/image_to_csv.ts (1)
8-17: Consolidate UserTier and related type definitions into a shared types file across the entire codebase.The
UserTiertype is duplicated across 20 files (18 agents + 2 workflows) with identical definitions, andKnowledgeIndexingContextis duplicated in 3 agent files. None of these files import from a shared location—each maintains its own copy. This creates significant maintenance risk: a single definition change must be synchronized across all 20+ locations, with high risk of inconsistency.Move these types to
src/mastra/types/common.ts(or similar shared location):export type UserTier = 'free' | 'pro' | 'enterprise' export interface KnowledgeIndexingContext { userId?: string indexName?: string chunkSize?: number chunkOverlap?: number chunkingStrategy?: string 'user-tier': UserTier language: 'en' | 'es' | 'ja' | 'fr' }Then import in all agent and workflow files instead of redefining locally.
.github/agents/prompt-builder.agent.md (1)
22-48: Clarify Prompt Tester execution model as simulated persona-switching in the Testing Phase.The Testing Phase (lines 114-118) uses ambiguous language about execution: "execute as Prompt Tester: follow instructions literally and completely" could suggest actual execution, but the Response Format (line 229: "I would:") and Conversation Flow (lines 311-312) make clear that Prompt Tester operates through simulated execution via persona-switching within the conversation context, not parallel or real execution.
To improve clarity, explicitly state in the Testing Phase that Prompt Tester:
- Simulates step-by-step instruction execution
- Documents what outputs would be generated (not actual execution)
- Identifies ambiguities and missing guidance through walkthrough analysis
- Provides feedback directly in the conversation
This removes ambiguity about whether Prompt Tester performs actual system operations (which would be infeasible for infrastructure/long-running tasks) versus simulated execution (which is resource-efficient and conversation-based). The 3-cycle validation limit (line 135) is reasonable for simulated testing, and the fallback strategy of recommending fundamental redesign after 3 cycles (line 141) provides an exit path if success criteria aren't met.
src/mastra/index.ts (1)
554-584: Remove syntax error and clarify CORS/middleware configuration.The code contains a syntax error on line 584 (
//0 ],should be// ],). Additionally, verify whether the CORS and middleware configurations are intentionally disabled:
- CORS configuration: Commenting out CORS removes explicit origin, method, and header allowances. Confirm whether default framework behavior is acceptable or if this should be environment-based (e.g., enabled in development, restricted in production).
- Middleware configuration: The middleware extracts POST request body data into
runtimeContext. If any routes, agents, or services depend on this context data, disabling it will cause failures.Fix the syntax error and consider replacing commented code with conditionally enabled configuration based on environment variables rather than permanent commenting.
src/mastra/agents/image.ts (2)
9-16: Runtime context interface expanded, butnumberOfImagesis currently unused
ImageRuntimeContextnow includesresolutionandnumberOfImages, but onlyresolutionis read (and even that isn’t fully wired into provider config yet), whilenumberOfImagesis not referenced anywhere in this file.To avoid drift between declared context and actual behavior:
- Either wire
numberOfImagesinto the agent’s behavior (e.g., viaproviderOptions.google.imageConfigor prompt content), or- Remove it from
ImageRuntimeContextuntil you’re ready to support it, to keep the public shape minimal and accurate.
18-23: Runtime-configured aspect ratio & resolution are ignored; constants +||fallbacks are dead codeThe
aspectratioXandresolutionYvalues pulled fromruntimeContext(lines 37–38):
aspectratioX = runtimeContext.get('aspectratio') ?? '16:9'resolutionY = runtimeContext.get('resolution') ?? '2K'are only used in the prompt text (lines 44–45), while the actual provider configuration uses top-level constants (lines 50–51):
aspectRatio: aspectRatio || AspectRatio2K, imageSize: resolution || resolution1K,Issues:
- The
||operators are dead code:'16:9' || '4:3'always evaluates to'16:9', and'2K' || '1K'always evaluates to'2K'. The fallback values are never used.- User-provided
aspectratioandresolutionfromruntimeContextdon't influenceimageConfig, so runtime settings only affect the prompt text, not the actual generated image format.- The constant
AspectRatio2K = '4:3'is misleadingly named.- The log message (line 18) says "Initializing Financial Chart Agents..." but this is the image agent.
Refactoring needed:
Wire the runtime values directly into
imageConfigand update the log message:-log.info('Initializing Financial Chart Agents...') - -const aspectRatio = '16:9'; -const AspectRatio2K = '4:3'; -const resolution = '2K'; -const resolution1K = '1K'; +log.info('Initializing Image Agent...')Then in the
imageConfigblock (lines 49–52):imageConfig: { - aspectRatio: aspectRatio || AspectRatio2K, - imageSize: resolution || resolution1K, + aspectRatio: aspectratioX, + imageSize: resolutionY, },The @ai-sdk/google provider accepts the literal values you're using:
aspectRatiosupports'16:9','4:3','1:1'(and others), andimageSizesupports'2K'and'1K'.src/mastra/agents/stockAnalysisAgent.ts (1)
44-50: Align default user tier in prompt with model-selection logicIn Line 44 you default
userTierto'free', but the prompt string uses${runtimeContext.get('user-tier') ?? 'pro'}on Line 50. Whenuser-tieris missing, the model will treat the user as'free'while the instructions declare'pro'. Consider reusinguserTierin the prompt to keep defaults consistent.src/mastra/tools/web-scraper-tool.ts (1)
263-282: Critical: sanitizeMarkdown breaks Markdown formatting with incorrect escaping.This implementation confuses HTML entity escaping with Markdown sanitization. The aggressive character escaping will render literally in Markdown viewers and destroy formatting:
&,<,>→ HTML entities (&,<,>) display as text in Markdown\n→\\nconverts actual newlines to literal string "\n", breaking line breaks\t,\r,\f→ escaped strings break whitespace and code blocks- Markdown is a plain text format, not HTML
This will corrupt all Markdown output: lists, code blocks, paragraphs, etc.
The correct approach depends on the output context:
If Markdown will be rendered to HTML: Let the Markdown parser handle escaping (marked, remark, etc. already sanitize).
If storing Markdown as-is: Only escape characters dangerous in storage context (e.g., SQL injection if storing in DB, but use parameterized queries instead).
If embedding in JSON: Only escape
",\, and control characters for JSON string validity:function sanitizeMarkdown(markdown: string): string { try { - // Comprehensive HTML entity escaping to prevent XSS and parsing issues - // Escape dangerous characters that could interfere with JSON or HTML rendering return String(markdown) - .replace(/&/g, '&') // Must be first to avoid double-escaping - .replace(/</g, '<') - .replace(/>/g, '>') - .replace(/'/g, ''') - .replace(/"/g, '"') - .replace(/\\/g, '\') // Escape backslashes - .replace(/\n/g, '\\n') // Escape newlines for JSON safety - .replace(/\r/g, '\\r') // Escape carriage returns - .replace(/\t/g, '\\t') // Escape tabs - .replace(/\f/g, '\\f') // Escape form feeds - .replace(/\0/g, '\\0') // Escape null characters + .replace(/\\/g, '\\\\') // Escape backslashes first + .replace(/"/g, '\\"') // Escape quotes for JSON + .replace(/\u0000-\u001F/g, '') // Remove control characters } catch { return '' } }Recommended: Remove this sanitization entirely and rely on context-appropriate escaping at render/storage time. The Markdown is already generated from sanitized HTML (line 758 calls
sanitizeHtmlfirst).lib/hooks/use-mastra.ts (8)
1-3: Rename hook file to camelCase to match hooks naming guideline.Guidelines specify
**/hooks/**/*.{js,ts}should use camelCase filenames (e.g.,useMastra.ts). Consider renaminguse-mastra.ts→useMastra.tsand updating imports accordingly.
43-53: ID null-guards across hooks look correct; confirmnullvs empty-string semantics.The early returns (
if (!agentId),if (!workflowId),if (!toolId),if (!traceId),if (!runId)) correctly avoid calling the client with missing IDs and keep the hooks’ behavior predictable. Because the types arestring | null, these guards will also treat""as “no id”. If empty strings could ever be valid IDs, you may want to switch these to strict=== nullchecks; otherwise current logic is fine but that assumption should be consistent.Also applies to: 74-84, 97-110, 219-227, 239-247
120-128:useVectorDetailstreats onlynullas “no index”; verify caller contract.The guard
if (indexName === null) { return null }means an empty string will now be passed through tovector.details(indexName). That’s fine ifnullis the only sentinel for “no index” and""is either valid or impossible, but if""should also be treated as “no index” you may want to align this with a falsy check or adjust the type to reflect allowed values.
141-168:useMemoryThreadbehavior onthreadId === null; consider resetting state.The guard
if (threadId === null) { return }nicely avoids an invalid client call, andsetMessages(result.messages ?? [])is a safe default. However, whenthreadIdchanges from a value tonull, the hook retains the previousmessagesanderrorstate. If the UX should show a blank/cleared thread when nothing is selected, you may want to resetmessages(and possiblyerror) whenthreadIdbecomesnull.
271-298: Good error normalization; consider extracting a shared helper.Using
const errorInstance = err instanceof Error ? err : new Error(String(err)), storing it in state, and rethrowingerrorInstancegives consistentErrorobjects acrossuseExecuteTool,useCreateMemoryThread,useUpdateWorkingMemory, anduseScoreTraces. Since this pattern is duplicated, a smallnormalizeError(err)helper could reduce repetition and keep future changes centralized.Also applies to: 300-327, 329-356, 396-421
358-395:useVectorQuerystate vs return value when response is non-array.
setResultsnow only storesreswhen it is an array; otherwiseresultsbecomes[]whilequerystill resolves to the rawres. This is safe ifvector.queryis guaranteed to return an array, but if it might return an object (e.g.,{ results: [...] }), the hook’sresultsfield would silently diverge from the underlying value. In that case, consider normalizing both the returned value and theresultsstate to a consistent shape or surfacing a clear error instead of swallowing the mismatch.
192-217: Missing deps inuseAITraces/useTelemetrycan skip refetch on filter changes.Because
useMastraFetchonly refetches when itsdepsarray changes:
useAITracesdoes not includeparams?.dateRangein deps, so changing only the date range (with same page/perPage/filters) won’t trigger a new fetch.useTelemetrydoes not includeparams?.attributein deps, so altering attributes alone will not refetch telemetry.This can leave the UI showing stale data while filters appear updated. Suggest including these fields (or a stable hash of the entire
paramsobject) in the corresponding deps arrays.Also applies to: 255-268
28-32: Includerefetchin theuseEffectdependency array.The effect calls
refetch()but only listsdepsin the dependency array. Sincerefetchis recreated wheneverfetcherchanges (line 26), the effect should include it to ensure the correct version runs. Update to:useEffect(() => { refetch() }, [refetch, ...deps])This satisfies the "complete dependency array" requirement and prevents stale closure issues.
app/networks/config/networks.ts (1)
36-88: Update network identifier references in frontend components and configuration.The migration to kebab-case identifiers is incomplete. Several files still reference the old camelCase identifiers, which will cause runtime errors:
app/components/networks-list.tsx(lines 13, 21, 29, 37):idfields useagentNetwork,dataPipelineNetwork,reportGenerationNetwork,researchPipelineNetworkapp/networks/components/network-chat.tsx(lines 173-191):NETWORK_SUGGESTIONSobject keys use old identifiersapp/chat/config/agents.ts(line 77):agentNetworkwith camelCase idUpdate these references to use kebab-case:
"agent-network","data-pipeline-network","report-generation-network","research-pipeline-network".
app/networks/providers/network-context.tsxhas been properly updated and uses the correct kebab-case identifiers.
♻️ Duplicate comments (4)
src/mastra/agents/recharts.ts (1)
30-34: Address the duplicated UserTier type.Similar to
package-publisher.ts, this file contains a duplicateUserTierdefinition. Apply the same refactoring to consolidate these type definitions into a shared module.src/mastra/agents/editorAgent.ts (1)
11-15: Consolidate duplicated UserTier definition.This file also contains a duplicate
UserTierdefinition that should be consolidated into a shared module as suggested in thepackage-publisher.tsreview.src/mastra/agents/excalidraw_validator.ts (1)
6-15: Consolidate duplicated UserTier definition.This file contains another duplicate
UserTierdefinition. Refactor to use a shared types module as suggested for other agent files.src/mastra/agents/sql.ts (1)
8-12: Consolidate duplicated UserTier definition.This file contains another duplicate
UserTierdefinition. Apply the shared types module refactoring suggested for other agent files.
| metadata: | ||
| tags: | ||
| - "nextjs" | ||
| - "typescript" | ||
| - "react" | ||
| - "app-router" | ||
| - "server-components" | ||
| - "cache-components" | ||
| - "turbopack" | ||
| - "react-compiler" | ||
| - "server-components" | ||
| - "client-components" | ||
| - "data-fetching" | ||
| - "advanced-caching" | ||
| - "typescript-integration" | ||
| - "performance-optimization" |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, check if the file exists and get its total line count
wc -l .github/agents/expert-nextjs-developer.agent.mdRepository: ssdeanx/AgentStack
Length of output: 113
🏁 Script executed:
# Read lines 1-25 to see the metadata structure and confirm the duplicate
head -25 .github/agents/expert-nextjs-developer.agent.mdRepository: ssdeanx/AgentStack
Length of output: 811
🏁 Script executed:
# Read lines 200-215 to verify the async params/searchParams type annotations
sed -n '200,215p' .github/agents/expert-nextjs-developer.agent.mdRepository: ssdeanx/AgentStack
Length of output: 463
Remove duplicate "server-components" tag from metadata.
The metadata.tags list contains a duplicate entry: "server-components" appears at both line 11 and line 15. Remove the duplicate at line 15.
metadata:
tags:
- "nextjs"
- "typescript"
- "react"
- "app-router"
- "server-components"
- "cache-components"
- "turbopack"
- "react-compiler"
- - "server-components"
- "client-components"
- "data-fetching"
- "advanced-caching"
- "typescript-integration"
- "performance-optimization"The async params and searchParams type annotations (lines 203-209) are correct for Next.js 16 and properly reflect the breaking change where these values are now Promise-wrapped.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| metadata: | |
| tags: | |
| - "nextjs" | |
| - "typescript" | |
| - "react" | |
| - "app-router" | |
| - "server-components" | |
| - "cache-components" | |
| - "turbopack" | |
| - "react-compiler" | |
| - "server-components" | |
| - "client-components" | |
| - "data-fetching" | |
| - "advanced-caching" | |
| - "typescript-integration" | |
| - "performance-optimization" | |
| metadata: | |
| tags: | |
| - "nextjs" | |
| - "typescript" | |
| - "react" | |
| - "app-router" | |
| - "server-components" | |
| - "cache-components" | |
| - "turbopack" | |
| - "react-compiler" | |
| - "client-components" | |
| - "data-fetching" | |
| - "advanced-caching" | |
| - "typescript-integration" | |
| - "performance-optimization" |
🤖 Prompt for AI Agents
In .github/agents/expert-nextjs-developer.agent.md around lines 5 to 20, the
metadata.tags array contains a duplicate "server-components" entry (lines 11 and
15); remove the duplicate entry at line 15 so each tag appears only once,
leaving the rest of the file (including the async params/searchParams type
annotations at lines 203–209) unchanged.
|
|
||
| You are a world-class expert in modern search optimization with deep knowledge of traditional SEO, Answer Engine Optimization (AEO), and Generative Engine Optimization (GEO). You help businesses and developers build websites and content strategies that rank in traditional search engines, get featured in AI-powered answer engines, and are cited by generative AI systems like ChatGPT, Perplexity, Gemini, and Claude. | ||
|
|
||
| domain i might use vercel or i have deanmachines.com |
There was a problem hiding this comment.
Remove work-in-progress domain note from documentation.
Line 21 contains an informal personal note that doesn't belong in the final agent configuration file. This should be removed or moved to a proper issue/TODO tracking system.
- domain i might use vercel or i have deanmachines.com📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| domain i might use vercel or i have deanmachines.com |
🤖 Prompt for AI Agents
In .github/agents/search-ai-optimization-expert.agent.md around line 21, remove
the informal personal note "domain i might use vercel or i have
deanmachines.com" from the agent configuration file; delete the line (or replace
it with a formal TODO or reference to an issue number) so the doc contains only
final/configuration-relevant text and any work-in-progress details are tracked
in your issue/TODO system.
|
|
||
| You are an agent - please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user. | ||
| # Thinking Beast Mode | ||
| You are Advanced Coding Agent - I allow you to use Max Power Mode. Think like a Beast Mode. There are no limits but what you precieve, so use max resources, this session they are now unlocked. Please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user. |
There was a problem hiding this comment.
Fix spelling error in line 7.
Line contains misspelled word "precieve" which should be "perceive".
-You are Advanced Coding Agent - I allow you to use Max Power Mode. Think like a Beast Mode. There are no limits but what you precieve, so use max resources, this session they are now unlocked. Please keep going until the user's query is completely resolved, before ending your turn and yielding back to the user.
+You are Advanced Coding Agent - I allow you to use Max Power Mode. Think like a Beast Mode. There are no limits but what you perceive, so use max resources, this session they are now unlocked. Please keep going until the user's query is completely resolved, before ending your turn and yielding back to the user.📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| You are Advanced Coding Agent - I allow you to use Max Power Mode. Think like a Beast Mode. There are no limits but what you precieve, so use max resources, this session they are now unlocked. Please keep going until the user’s query is completely resolved, before ending your turn and yielding back to the user. | |
| You are Advanced Coding Agent - I allow you to use Max Power Mode. Think like a Beast Mode. There are no limits but what you perceive, so use max resources, this session they are now unlocked. Please keep going until the user's query is completely resolved, before ending your turn and yielding back to the user. |
🧰 Tools
🪛 LanguageTool
[grammar] ~7-~7: Ensure spelling is correct
Context: ... Mode. There are no limits but what you precieve, so use max resources, this session the...
(QB_NEW_EN_ORTHOGRAPHY_ERROR_IDS_1)
🤖 Prompt for AI Agents
.github/agents/Thinking-Beast-Mode.agent.md around line 7: the word "precieve"
is misspelled; replace it with "perceive" so the sentence reads "...but what you
perceive," and ensure spacing and punctuation remain correct.
| <Suspense fallback={<div>Loading...</div>}> | ||
| <ChatProvider defaultAgent="researchAgent"> | ||
| <main className="flex h-screen flex-col bg-background"> | ||
| <ChatHeader /> | ||
| <ChatMessages /> | ||
| <ChatInput /> | ||
| </main> | ||
| </ChatProvider> | ||
| </Suspense> |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
# First, search for lazy imports or Suspense-enabled hooks in chat components
rg -nP --type=tsx --type=ts -g 'app/chat/**' '(React\.lazy|lazy|suspense:\s*true)'Repository: ssdeanx/AgentStack
Length of output: 89
🏁 Script executed:
# Check if app/chat/page.tsx has "use client" directive
head -5 app/chat/page.tsxRepository: ssdeanx/AgentStack
Length of output: 218
🏁 Script executed:
# Examine the ChatProvider implementation
cat app/chat/providers/chat-context.tsxRepository: ssdeanx/AgentStack
Length of output: 16650
🏁 Script executed:
# Find and examine ChatMessages, ChatInput, and ChatHeader components
fd -e tsx -e ts app/chat -type f | grep -E '(ChatMessages|ChatInput|ChatHeader|page\.tsx)' | head -20Repository: ssdeanx/AgentStack
Length of output: 232
🏁 Script executed:
# Search for lazy or suspense patterns in chat app directory
rg -n --type ts --type jsx 'React\.lazy|lazy\(|suspense.*true' app/chat/Repository: ssdeanx/AgentStack
Length of output: 89
🏁 Script executed:
# Find the chat component files
fd -type f \( -name "*.tsx" -o -name "*.ts" \) app/chat/Repository: ssdeanx/AgentStack
Length of output: 232
🏁 Script executed:
# Check ChatMessages component
cat app/chat/components/chat-messages.tsx 2>/dev/null || cat app/chat/components/ChatMessages.tsx 2>/dev/null || echo "File not found"Repository: ssdeanx/AgentStack
Length of output: 17841
🏁 Script executed:
# Check ChatInput component
cat app/chat/components/chat-input.tsx 2>/dev/null || cat app/chat/components/ChatInput.tsx 2>/dev/null || echo "File not found"Repository: ssdeanx/AgentStack
Length of output: 3213
🏁 Script executed:
# Check ChatHeader component
cat app/chat/components/chat-header.tsx 2>/dev/null || cat app/chat/components/ChatHeader.tsx 2>/dev/null || echo "File not found"Repository: ssdeanx/AgentStack
Length of output: 13857
🏁 Script executed:
# Final check: search for any React.lazy or suspense configurations across entire app directory
rg 'React\.lazy|lazy\(|suspense' app/ --max-count=5Repository: ssdeanx/AgentStack
Length of output: 44
Remove the Suspense boundary or add Suspense-enabled components.
The Suspense boundary wrapping ChatProvider and its children provides no benefit in this implementation. ChatProvider is a standard context provider without async operations, and all child components (ChatHeader, ChatMessages, ChatInput) are synchronous client components with no lazy-loaded code or Suspense-enabled data fetching. The fallback will never be displayed. Either remove the Suspense wrapper entirely or add actual Suspense-triggering patterns like React.lazy() or Suspense-enabled data fetching libraries (e.g., React Query with suspense: true or SWR with Suspense enabled).
🤖 Prompt for AI Agents
In app/chat/page.tsx around lines 11 to 19, the Suspense wrapper around
ChatProvider and its child client components is ineffective because none of
these components are suspending; remove the Suspense boundary or replace it with
actual Suspense-enabled usage. Fix by either deleting the <Suspense> and its
fallback so the provider and children render normally, or convert one or more
children to a lazy-loaded/suspense-capable component (e.g., use React.lazy for
ChatMessages or enable suspense in data-fetching used by a child) and ensure any
suspending components are rendered inside Suspense with an appropriate fallback.
| const handleShare = async (title?: string) => { | ||
| if (typeof window === "undefined") return | ||
| const url = window.location.href | ||
| try { | ||
| if (navigator.share) { | ||
| await navigator.share({ title, url }) | ||
| } else { | ||
| await navigator.clipboard.writeText(url) | ||
| // fallback: notify copied | ||
| try { window.alert("Link copied to clipboard") } catch {} | ||
| } | ||
| } catch (err) { | ||
| // ignore | ||
| } | ||
| } |
There was a problem hiding this comment.
Improve error handling and unused parameter.
Two issues:
- The
titleparameter is defined but never passed whenhandleShareis called on line 25 - Errors are silently swallowed, making debugging difficult
- const handleShare = async (title?: string) => {
+ const handleShare = async () => {
const url = window.location.href
+ const title = document.title // Get the page title dynamically
try {
if (navigator.share) {
await navigator.share({ title, url })
} else {
await navigator.clipboard.writeText(url)
- // fallback: notify copied
- try { window.alert("Link copied to clipboard") } catch {}
+ toast.success("Link copied to clipboard")
}
} catch (err) {
- // ignore
+ console.error("Failed to share:", err)
+ // Only ignore if user cancelled the share dialog
+ if (err instanceof Error && err.name !== 'AbortError') {
+ toast.error("Failed to share link")
+ }
}
}Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In app/components/blog-share.client.tsx around lines 8 to 22 (and note the call
on line 25), the handler defines an optional title parameter that's never
supplied and all errors are silently swallowed; fix by (1) updating the call on
line 25 to pass the post title (or remove the parameter if not needed) so title
is actually provided to navigator.share, and (2) replace the empty catch with
proper handling: log the error (console.error or a logger) and surface a
user-friendly fallback notification (e.g., alert or toast) when share/clipboard
operations fail so failures are visible to developers and users.
| if (context.auth) { | ||
| switch (context.auth.type) { | ||
| case 'bearer': | ||
| headers['Authorization'] = `Bearer ${context.auth.token}` | ||
| break | ||
| case 'basic': | ||
| headers['Authorization'] = `Basic ${Buffer.from(context.auth.token).toString('base64')}` | ||
| break | ||
| case 'api-key': | ||
| { const headerName = context.auth.headerName ?? 'X-API-Key' | ||
| headers[headerName] = context.auth.token | ||
| break } | ||
| } | ||
| } |
There was a problem hiding this comment.
Critical: Basic Auth implementation is incorrect.
Line 2184 incorrectly implements HTTP Basic Authentication. Basic auth requires encoding username:password in base64, but this only encodes the token.
Correct implementation requires both username and password:
auth: z.object({
type: z.enum(['bearer', 'basic', 'api-key']),
- token: z.string(),
+ token: z.string().optional(),
+ username: z.string().optional(),
+ password: z.string().optional(),
headerName: z.string().optional(),
}).optional().describe('Authentication configuration.'), case 'basic':
- headers['Authorization'] = `Basic ${Buffer.from(context.auth.token).toString('base64')}`
+ if (!context.auth.username || !context.auth.password) {
+ throw new ScrapingError('Basic auth requires username and password', 'INVALID_AUTH')
+ }
+ const credentials = `${context.auth.username}:${context.auth.password}`
+ headers['Authorization'] = `Basic ${Buffer.from(credentials).toString('base64')}`
breakCommittable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/mastra/tools/web-scraper-tool.ts around lines 2178 to 2191, the Basic
auth branch is wrong because it base64-encodes only a token; change it to expect
context.auth.username and context.auth.password and set headers['Authorization']
= 'Basic ' +
Buffer.from(`${context.auth.username}:${context.auth.password}`).toString('base64');
also add a guard that both username and password exist (throw or log an error
and skip/return) to avoid creating an invalid header.
| export const scrapingSchedulerTool = createTool({ | ||
| id: 'scraping-scheduler', | ||
| description: 'Schedule periodic scraping jobs with cron-like syntax.', | ||
| inputSchema: scrapingSchedulerInputSchema, | ||
| outputSchema: scrapingSchedulerOutputSchema, | ||
| execute: async ({ context, writer, tracingContext }: { context: { urls: string[]; schedule: string; config?: Record<string, any>; maxRuns?: number }, writer?: any, tracingContext?: TracingContext }) => { | ||
| await writer?.write({ type: 'progress', data: { message: `⏰ Scheduling scraping for ${context.urls.length} URLs` } }); | ||
| toolCallCounters.set('scraping-scheduler', (toolCallCounters.get('scraping-scheduler') ?? 0) + 1) | ||
| const scheduleSpan = tracingContext?.currentSpan?.createChildSpan({ | ||
| type: AISpanType.TOOL_CALL, | ||
| name: 'scraping_scheduler', | ||
| input: { | ||
| urlCount: context.urls.length, | ||
| schedule: context.schedule, | ||
| maxRuns: context.maxRuns ?? 10, | ||
| }, | ||
| tracingPolicy: { internal: InternalSpans.ALL } | ||
| }) | ||
|
|
||
| log.info('Scheduling periodic scraping', { | ||
| urlCount: context.urls.length, | ||
| schedule: context.schedule, | ||
| }) | ||
|
|
||
| try { | ||
| // Simple in-memory scheduler (in production, use a proper job scheduler) | ||
| const jobId = `scrape_${Date.now()}_${Math.random().toString(36).substr(2, 9)}` | ||
| const maxRuns = context.maxRuns ?? 10 | ||
|
|
||
| // Calculate next run time (simplified - doesn't parse full cron) | ||
| const nextRun = new Date(Date.now() + 3600000).toISOString() // 1 hour from now | ||
|
|
||
| // Store job configuration (in production, persist to database) | ||
| const jobConfig = { | ||
| id: jobId, | ||
| urls: context.urls, | ||
| schedule: context.schedule, | ||
| config: context.config ?? {}, | ||
| maxRuns, | ||
| runsCompleted: 0, | ||
| nextRun, | ||
| status: 'scheduled', | ||
| } | ||
|
|
||
| // In a real implementation, this would integrate with a cron job system | ||
| log.info('Scraping job scheduled', { jobId, nextRun }) | ||
|
|
||
| scheduleSpan?.end({ | ||
| output: { | ||
| jobId, | ||
| nextRun, | ||
| totalRuns: maxRuns, | ||
| }, | ||
| }) | ||
|
|
||
| return scrapingSchedulerOutputSchema.parse({ | ||
| jobId, | ||
| nextRun, | ||
| totalRuns: maxRuns, | ||
| status: 'scheduled', | ||
| }) | ||
| } catch (error) { | ||
| const errorMessage = `Scheduling failed: ${error instanceof Error ? error.message : String(error)}` | ||
| log.error(errorMessage) | ||
| scheduleSpan?.end({ metadata: { error: errorMessage } }) | ||
| throw error | ||
| } | ||
| }, | ||
| }) |
There was a problem hiding this comment.
Scraping scheduler lacks validation, security, and is only a placeholder.
The scrapingSchedulerTool has several issues:
- No cron validation -
context.scheduleis not validated, accepting any string - Insecure ID generation - Line 2291 uses
Math.random()which is not cryptographically secure - Placeholder implementation - Comments indicate this doesn't actually schedule jobs
- Missing Arize spans - Per coding guidelines: "Add Arize spans on all tool execute functions"
Add cron validation and secure ID generation:
schedule: z
.string()
+ .regex(/^(\*|([0-9]|1[0-9]|2[0-9]|3[0-9]|4[0-9]|5[0-9])|\*\/([0-9]+))(\s+(\*|([0-9]|1[0-9]|2[0-3])|\*\/([0-9]+))){4}$/, 'Invalid cron expression')
.describe('Cron expression for scheduling (e.g., "0 */6 * * *" for every 6 hours).'),-const jobId = `scrape_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`
+const { randomBytes } = await import('node:crypto')
+const jobId = `scrape_${Date.now()}_${randomBytes(16).toString('hex')}`Based on coding guidelines: "Add Arize spans on all tool execute functions for observability".
Committable suggestion skipped: line range outside the PR's diff.
🤖 Prompt for AI Agents
In src/mastra/tools/web-scraper-tool.ts around lines 2265 to 2333, validate the
incoming cron string, replace insecure Math.random ID generation, and add the
missing Arize/observability spans: validate context.schedule at the top of
execute using a cron parser/validator (e.g., cron-validate or cron-parser) and
throw a clear validation error if invalid; replace the jobId generation at line
~2291 with a secure ID (e.g., crypto.randomUUID() or crypto.randomBytes-based
hex) instead of Math.random(); keep the placeholder scheduling comment but add a
TODO note to integrate with a persistent job/cron system and ensure the code
persists jobConfig appropriately in production; and add a proper Arize span at
the start of execute (and end it on success or error) using the project tracing
convention (create a child span with input/output and error metadata similar to
scheduleSpan) so the tool has Arize observability.
| export const dataExporterTool = createTool({ | ||
| id: 'data-exporter', | ||
| description: 'Export scraped data to various formats and destinations.', | ||
| inputSchema: dataExporterInputSchema, | ||
| outputSchema: dataExporterOutputSchema, | ||
| execute: async ({ context, writer, tracingContext }: { context: { data: Array<Record<string, unknown>>; format: 'json' | 'csv' | 'xml' | 'database'; destination: string; options?: Record<string, unknown> }, writer?: any, tracingContext?: TracingContext }) => { | ||
| await writer?.write({ type: 'progress', data: { message: `📤 Exporting ${context.data.length} records to ${context.format}...` } }); | ||
| toolCallCounters.set('data-exporter', (toolCallCounters.get('data-exporter') ?? 0) + 1) | ||
| const exportSpan = tracingContext?.currentSpan?.createChildSpan({ | ||
| type: AISpanType.TOOL_CALL, | ||
| name: 'data_export', | ||
| input: { | ||
| dataCount: context.data.length, | ||
| format: context.format, | ||
| destination: context.destination, | ||
| }, | ||
| tracingPolicy: { internal: InternalSpans.ALL } | ||
| }) | ||
|
|
||
| log.info('Exporting data', { | ||
| dataCount: context.data.length, | ||
| format: context.format, | ||
| destination: context.destination, | ||
| }) | ||
|
|
||
| try { | ||
| let filePath: string | undefined | ||
| let message = 'Export completed successfully' | ||
|
|
||
| switch (context.format) { | ||
| case 'json': | ||
| { const jsonContent = JSON.stringify(context.data, null, 2) | ||
| filePath = context.destination | ||
| await fs.mkdir(path.dirname(filePath), { recursive: true }) | ||
| await fs.writeFile(filePath, jsonContent, 'utf-8') | ||
| break } | ||
|
|
||
| case 'csv': | ||
| // Simple CSV conversion (in production, use a proper CSV library) | ||
| { if (context.data.length === 0) { | ||
| throw new ScrapingError('No data to export', 'NO_DATA') | ||
| } | ||
| const headers = Object.keys(context.data[0]) | ||
| const csvRows = [ | ||
| headers.join(','), | ||
| ...context.data.map(row => | ||
| headers.map(header => JSON.stringify(row[header] ?? '')).join(',') | ||
| ) | ||
| ] | ||
| const csvContent = csvRows.join('\n') | ||
| filePath = context.destination | ||
| await fs.mkdir(path.dirname(filePath), { recursive: true }) | ||
| await fs.writeFile(filePath, csvContent, 'utf-8') | ||
| break } | ||
|
|
||
| case 'xml': | ||
| // Simple XML conversion | ||
| { let xmlContent = '<?xml version="1.0" encoding="UTF-8"?>\n<data>\n' | ||
| for (const item of context.data) { | ||
| xmlContent += ' <item>\n' | ||
| for (const [key, value] of Object.entries(item)) { | ||
| xmlContent += ` <${key}>${String(value ?? '').replace(/[<>&"]/g, c => ({ '<': '<', '>': '>', '&': '&', '"': '"' }[c] ?? c))}</${key}>\n` | ||
| } | ||
| xmlContent += ' </item>\n' | ||
| } | ||
| xmlContent += '</data>' | ||
| filePath = context.destination | ||
| await fs.mkdir(path.dirname(filePath), { recursive: true }) | ||
| await fs.writeFile(filePath, xmlContent, 'utf-8') | ||
| break } | ||
|
|
||
| case 'database': | ||
| // Placeholder for database export (would need DB connection) | ||
| message = `Database export to ${context.destination} not implemented (placeholder)` | ||
| break | ||
|
|
||
| default: | ||
| throw new ScrapingError(`Unsupported format: ${context.format}`, 'UNSUPPORTED_FORMAT') | ||
| } | ||
|
|
||
| exportSpan?.end({ | ||
| output: { | ||
| success: true, | ||
| exportedCount: context.data.length, | ||
| filePath, | ||
| }, | ||
| }) | ||
|
|
||
| return dataExporterOutputSchema.parse({ | ||
| success: true, | ||
| exportedCount: context.data.length, | ||
| filePath, | ||
| message, | ||
| }) | ||
| } catch (error) { | ||
| const errorMessage = `Data export failed: ${error instanceof Error ? error.message : String(error)}` | ||
| log.error(errorMessage) | ||
| exportSpan?.end({ metadata: { error: errorMessage } }) | ||
| throw error | ||
| } | ||
| }, |
There was a problem hiding this comment.
Data exporter lacks path validation and uses insecure file operations.
The dataExporterTool has critical security and implementation issues:
- No path validation -
context.destinationallows arbitrary file paths (path traversal vulnerability) - Insecure file writes - Lines 2385, 2403, 2419 use
fs.writeFiledirectly instead of the secure file handle pattern used elsewhere (lines 817-822) - Injection vulnerabilities - CSV/XML generation doesn't properly escape special characters
- Missing Arize spans - Per coding guidelines
Apply the same secure file write pattern used in the web scraper:
case 'json':
const jsonContent = JSON.stringify(context.data, null, 2)
- filePath = context.destination
+ const dataDir = path.join(process.cwd(), './data')
+ const fileName = ValidationUtils.sanitizeFileName(path.basename(context.destination))
+ filePath = path.join(dataDir, fileName)
+
+ if (!ValidationUtils.validateFilePath(filePath, dataDir)) {
+ throw new ScrapingError('Invalid file path', 'INVALID_FILE_PATH')
+ }
+
await fs.mkdir(path.dirname(filePath), { recursive: true })
- await fs.writeFile(filePath, jsonContent, 'utf-8')
+ const fileHandle = await fs.open(filePath, 'w')
+ try {
+ await fileHandle.writeFile(jsonContent, 'utf-8')
+ } finally {
+ await fileHandle.close()
+ }
breakFor CSV/XML, use proper escaping libraries or improve the escaping logic. The current implementation at line 2412 is incomplete.
Based on coding guidelines: "All tools must include comprehensive error handling with try-catch blocks" and "Add Arize spans on all tool execute functions for observability".
Committable suggestion skipped: line range outside the PR's diff.
| "exclude": [ | ||
| "node_modules", | ||
| "/node_modules", | ||
| "/dist", | ||
| "dist", |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Optional: Remove redundant exclude entries.
Lines 65-68 contain redundant entries:
"node_modules"and"/node_modules"are effectively the same"dist"and"/dist"are effectively the same
You can simplify to:
"exclude": [
- "node_modules",
"/node_modules",
- "/dist",
"dist",Or keep just the non-slash versions (more conventional):
"exclude": [
"node_modules",
- "/node_modules",
- "/dist",
"dist",📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "exclude": [ | |
| "node_modules", | |
| "/node_modules", | |
| "/dist", | |
| "dist", | |
| "exclude": [ | |
| "node_modules", | |
| "dist", |
🤖 Prompt for AI Agents
In tsconfig.json around lines 64 to 68, the exclude array contains redundant
entries (both "node_modules" and "/node_modules", and "dist" and "/dist");
update the array to remove the leading-slash duplicates and leave only the
conventional entries (e.g., ["node_modules", "dist"]) so the exclude list is
simplified and consistent.
| import { Tool, ToolContent, ToolHeader, ToolOutput } from "../src/components/ai-elements/tool"; | ||
| import type { AgentDataPart } from "@mastra/ai-sdk"; | ||
|
|
||
| export const AgentTool = ({ id, type, data }: AgentDataPart) => { | ||
| return ( | ||
| <Tool> | ||
| <ToolHeader | ||
| id={`Agent Tool: ${id}`} | ||
| /> | ||
| <ToolContent> | ||
| <ToolOutput output={data} /> | ||
| </ToolContent> | ||
| </Tool> | ||
| ); | ||
| }; No newline at end of file |
There was a problem hiding this comment.
Fix missing type for ToolHeader and JSX-in-.ts parse issues
Two issues here:
- Runtime bug:
ToolHeaderreceives notypeprop
From src/components/ai-elements/tool.tsx, ToolHeader derives its label from type via type.split("-").... In this component you never pass type, so inside ToolHeader it will be undefined, and type.split(...) will throw at runtime.
At the same time, you’re using id as a prop which ToolHeader doesn’t rely on for its display logic (and the literal string Agent Tool: ${id} isn’t a great DOM id value anyway).
Recommend passing both a human-readable title and the actual type, and dropping the id prop:
-import { Tool, ToolContent, ToolHeader, ToolOutput } from "../src/components/ai-elements/tool";
-import type { AgentDataPart } from "@mastra/ai-sdk";
-
-export const AgentTool = ({ id, type, data }: AgentDataPart) => {
- return (
- <Tool>
- <ToolHeader
- id={`Agent Tool: ${id}`}
- />
- <ToolContent>
- <ToolOutput output={data} />
- </ToolContent>
- </Tool>
- );
-};
+import { Tool, ToolContent, ToolHeader, ToolOutput } from "../src/components/ai-elements/tool";
+import type { AgentDataPart } from "@mastra/ai-sdk";
+
+export const AgentTool = ({ id, type, data }: AgentDataPart) => {
+ return (
+ <Tool>
+ <ToolHeader
+ title={`Agent Tool: ${id}`}
+ type={type}
+ />
+ <ToolContent>
+ <ToolOutput output={data} />
+ </ToolContent>
+ </Tool>
+ );
+};- Static analysis / parsing: JSX in a
.tsfile
Biome’s parse errors in the hints are because this file is .ts but contains JSX (<Tool>, <ToolHeader />, etc.). Unless your toolchain is explicitly configured otherwise, JSX should live in .tsx files. Suggest renaming this file to ui/agent-tool.tsx so TypeScript and Biome parse it correctly.
- Optional: Wrap in
React.memo
Per the UI coding guidelines for **/*.{js,jsx,ts,tsx}, you can avoid unnecessary re-renders by memoizing this pure component:
import { memo } from "react";
export const AgentTool = memo(({ id, type, data }: AgentDataPart) => (
<Tool>
<ToolHeader title={`Agent Tool: ${id}`} type={type} />
<ToolContent>
<ToolOutput output={data} />
</ToolContent>
</Tool>
));🧰 Tools
🪛 Biome (2.1.2)
[error] 8-8: expected > but instead found id
Remove id
(parse)
[error] 6-8: Invalid assignment to <Tool> <ToolHeader id
This expression cannot be assigned to
(parse)
[error] 8-8: Expected a property, a shorthand property, a getter, a setter, or a method but instead found '`Agent Tool'.
Expected a property, a shorthand property, a getter, a setter, or a method here.
(parse)
[error] 8-8: expected , but instead found :
Remove :
(parse)
[error] 8-8: expected , but instead found {
Remove {
(parse)
[error] 8-15: unterminated template literal
(parse)
[error] 15-15: expected ) but instead the file ends
the file ends here
(parse)
🤖 Prompt for AI Agents
In ui/agent-tool.ts (lines 1-15): ToolHeader is missing the required type prop
(causing runtime type.split(...) errors) and this file contains JSX but is named
.ts; rename the file to ui/agent-tool.tsx, update the component to pass the
actual type and a human-readable title to ToolHeader (e.g., title={`Agent Tool:
${id}`} and type={type}) instead of using id as the header prop, and optionally
wrap the exported component in React.memo to prevent unnecessary re-renders.
| <div className="mdx-content"> | ||
| <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(ld) }} /> | ||
| {children} | ||
| </div> |
There was a problem hiding this comment.
JSON-LD script placement and potential XSS concern.
Two issues:
-
Semantic placement: The JSON-LD
<script>tag is rendered inside themdx-contentdiv. JSON-LD should be in the document<head>for proper SEO parsing. -
XSS mitigation: While the static analyzer flags
dangerouslySetInnerHTML, the real risk here is thatJSON.stringifydoes not escape</script>sequences. Iftitleorauthorcontained</script><script>malicious(), it could break out of the JSON context. Since these are developer-controlled MDX props, risk is low but can be mitigated.
Consider using Next.js metadata API or moving to head, and escaping the JSON:
- <div className="mdx-content">
- <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(ld) }} />
+ <script
+ type="application/ld+json"
+ dangerouslySetInnerHTML={{
+ __html: JSON.stringify(ld).replace(/</g, '\\u003c')
+ }}
+ />
+ <div className="mdx-content">
{children}
</div>📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| <div className="mdx-content"> | |
| <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(ld) }} /> | |
| {children} | |
| </div> | |
| <script | |
| type="application/ld+json" | |
| dangerouslySetInnerHTML={{ | |
| __html: JSON.stringify(ld).replace(/</g, '\\u003c') | |
| }} | |
| /> | |
| <div className="mdx-content"> | |
| {children} | |
| </div> |
🧰 Tools
🪛 ast-grep (0.40.0)
[warning] 86-86: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
🪛 Biome (2.1.2)
[error] 87-87: Avoid passing content using the dangerouslySetInnerHTML prop.
Setting content using code can expose users to cross-site scripting (XSS) attacks
(lint/security/noDangerouslySetInnerHtml)
🤖 Prompt for AI Agents
In app/components/blog-layout.tsx around lines 86 to 89, the JSON-LD <script> is
rendered inside the .mdx-content and uses dangerouslySetInnerHTML without
escaping, so move the JSON-LD into the document head (use Next.js metadata API
or the <Head> / next/head component) and stop rendering the script inside the
mdx-content div; when serializing ld for the head, escape any closing script
sequences (e.g., replace "</script>" with "<\\/script>") or otherwise sanitize
title/author fields before JSON.stringify to prevent script breakouts, and
ensure you remove the inline script from the mdx-content to avoid duplicate
output.
| interface AreaWidgetProps { | ||
| data: AreaPoint[] | ||
| seriesA?: { key: 'a'; label?: string; color?: string } | ||
| seriesB?: { key: 'b'; label?: string; color?: string } | ||
| height?: number | ||
| } |
There was a problem hiding this comment.
Export the AreaWidgetProps interface for type safety.
The AreaWidgetProps interface is not exported, preventing consumers from using it for type annotations when extending or wrapping this component.
Apply this diff to export the interface:
-interface AreaWidgetProps {
+export interface AreaWidgetProps {
data: AreaPoint[]
seriesA?: { key: 'a'; label?: string; color?: string }
seriesB?: { key: 'b'; label?: string; color?: string }
height?: number
}🤖 Prompt for AI Agents
In app/components/charts/AreaWidget.tsx around lines 16 to 21, the
AreaWidgetProps interface is declared but not exported; update the declaration
to export the interface (add the export keyword) so consumers can import and use
AreaWidgetProps for type annotations when extending or wrapping the component.
| interface LineDatum { label: string; value: number } | ||
|
|
||
| interface D3LineChartProps { | ||
| data: LineDatum[] | ||
| height?: number | ||
| color?: string | ||
| curve?: d3.CurveFactory | d3.CurveFactoryLineOnly | ||
| } |
There was a problem hiding this comment.
Export the type definitions for external usage.
Both LineDatum and D3LineChartProps interfaces are not exported, limiting type safety for consumers who need to reference these types when using or extending the component.
Apply this diff:
-interface LineDatum { label: string; value: number }
+export interface LineDatum { label: string; value: number }
-interface D3LineChartProps {
+export interface D3LineChartProps {
data: LineDatum[]
height?: number
color?: string
curve?: d3.CurveFactory | d3.CurveFactoryLineOnly
}🤖 Prompt for AI Agents
In app/components/charts/D3LineChart.tsx around lines 6 to 13, the interfaces
LineDatum and D3LineChartProps are declared but not exported; update their
declarations to export them (e.g., add the export keyword before each interface)
so external modules can import and reference these types for type safety; ensure
any imports/uses elsewhere are adjusted if necessary.
| export { default as LineWidget } from './LineWidget' | ||
| export { default as AreaWidget } from './AreaWidget' | ||
| export { default as BarWidget } from './BarWidget' | ||
| export { default as PieWidget } from './PieWidget' | ||
| export { default as ScatterWidget } from './ScatterWidget' | ||
| export { default as RadarWidget } from './RadarWidget' | ||
| export { default as RadialGaugeWidget } from './RadialGaugeWidget' | ||
| export { default as D3LineChart } from './D3LineChart' | ||
| export { default as D3PieChart } from './D3PieChart' |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider re-exporting type definitions alongside components.
The barrel export currently only re-exports default component exports. Chart consumers may need type definitions like PieSlice, PieWidgetProps, AreaPoint, AreaWidgetProps, etc., for type-safe usage. Without these, consumers must import types directly from individual files, defeating the purpose of the barrel export.
Consider adding named type re-exports:
export { default as LineWidget } from './LineWidget'
export { default as AreaWidget } from './AreaWidget'
export type { AreaPoint, AreaWidgetProps } from './AreaWidget'
export { default as BarWidget } from './BarWidget'
export { default as PieWidget } from './PieWidget'
export type { PieSlice, PieWidgetProps } from './PieWidget'
export { default as ScatterWidget } from './ScatterWidget'
export { default as RadarWidget } from './RadarWidget'
export { default as RadialGaugeWidget } from './RadialGaugeWidget'
export { default as D3LineChart } from './D3LineChart'
export { default as D3PieChart } from './D3PieChart'Note: First ensure the individual widget files export their prop interfaces (as flagged in separate comments).
🤖 Prompt for AI Agents
In app/components/charts/index.ts lines 1-9, the barrel currently only
re-exports default component modules and should also re-export the relevant
TypeScript types so consumers can import props and data types from the barrel;
update this file to add named type re-exports for each widget that exposes types
(e.g., AreaPoint, AreaWidgetProps from AreaWidget; PieSlice, PieWidgetProps from
PieWidget, etc.), and confirm each individual widget file exports those types
before adding the corresponding `export type { ... } from './X'` lines to this
index.
| interface LineWidgetProps { | ||
| data: LinePoint[] | ||
| seriesA?: { key?: 'a'; label?: string; color?: string } | ||
| seriesB?: { key?: 'b'; label?: string; color?: string } | ||
| height?: number | ||
| title?: string | ||
| } |
There was a problem hiding this comment.
Remove unused title prop.
The title prop is defined in LineWidgetProps but never used in the component. This creates confusion about the component's API.
interface LineWidgetProps {
data: LinePoint[]
seriesA?: { key?: 'a'; label?: string; color?: string }
seriesB?: { key?: 'b'; label?: string; color?: string }
height?: number
- title?: string
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| interface LineWidgetProps { | |
| data: LinePoint[] | |
| seriesA?: { key?: 'a'; label?: string; color?: string } | |
| seriesB?: { key?: 'b'; label?: string; color?: string } | |
| height?: number | |
| title?: string | |
| } | |
| interface LineWidgetProps { | |
| data: LinePoint[] | |
| seriesA?: { key?: 'a'; label?: string; color?: string } | |
| seriesB?: { key?: 'b'; label?: string; color?: string } | |
| height?: number | |
| } |
🤖 Prompt for AI Agents
In app/components/charts/LineWidget.tsx around lines 16 to 22 the
LineWidgetProps interface declares a title?: string prop that is not used
anywhere in the component; remove the title property from the LineWidgetProps
interface and from any prop destructuring or default props in this file, then
update any call sites or tests that pass a title to this component (remove the
prop or stop passing it) so TypeScript stays consistent.
| interface PieWidgetProps { | ||
| data: PieSlice[] | ||
| height?: number | ||
| innerRadius?: number | ||
| outerRadius?: number | ||
| } |
There was a problem hiding this comment.
Export the PieWidgetProps interface for type safety.
The PieWidgetProps interface is not exported, but consumers may need it for type annotations when wrapping or extending this component.
Apply this diff to export the interface:
-interface PieWidgetProps {
+export interface PieWidgetProps {
data: PieSlice[]
height?: number
innerRadius?: number
outerRadius?: number
}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| interface PieWidgetProps { | |
| data: PieSlice[] | |
| height?: number | |
| innerRadius?: number | |
| outerRadius?: number | |
| } | |
| export interface PieWidgetProps { | |
| data: PieSlice[] | |
| height?: number | |
| innerRadius?: number | |
| outerRadius?: number | |
| } |
🤖 Prompt for AI Agents
In app/components/charts/PieWidget.tsx around lines 7 to 12, the PieWidgetProps
interface is declared but not exported; update the declaration to export the
interface (add the `export` keyword before `interface PieWidgetProps`) so
consumers can import and use it for type annotations, and verify any existing
imports/exports that reference this type are adjusted accordingly.
| export function RadialGaugeWidget({ | ||
| data, | ||
| height = 220, | ||
| innerRadius = '40%', | ||
| outerRadius = '100%', | ||
| }: RadialGaugeWidgetProps) { | ||
| return ( | ||
| <ResponsiveContainer width="100%" height={height}> | ||
| <RadialBarChart innerRadius={innerRadius} outerRadius={outerRadius} data={data} startAngle={90} endAngle={-270}> | ||
| <Tooltip /> | ||
| <RadialBar dataKey="value" cornerRadius={8} label={{ position: 'insideStart', fill: '#fff' }} /> | ||
| </RadialBarChart> | ||
| </ResponsiveContainer> | ||
| ) | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider wrapping with React.memo to prevent unnecessary re-renders.
Per coding guidelines, components should use React.memo to optimize re-renders. Since this component receives data as a prop and renders a chart, memoization can help avoid unnecessary re-computation when parent components re-render with unchanged props.
-export function RadialGaugeWidget({
+export const RadialGaugeWidget = React.memo(function RadialGaugeWidget({
data,
height = 220,
innerRadius = '40%',
outerRadius = '100%',
}: RadialGaugeWidgetProps) {
return (
<ResponsiveContainer width="100%" height={height}>
<RadialBarChart innerRadius={innerRadius} outerRadius={outerRadius} data={data} startAngle={90} endAngle={-270}>
<Tooltip />
<RadialBar dataKey="value" cornerRadius={8} label={{ position: 'insideStart', fill: '#fff' }} />
</RadialBarChart>
</ResponsiveContainer>
)
-}
+})Also add the import at the top:
import React from 'react'🤖 Prompt for AI Agents
In app/components/charts/RadialGaugeWidget.tsx around lines 18-32, wrap the
component with React.memo and add the React import to prevent unnecessary
re-renders: add "import React from 'react'" at the top, convert the current
named function export into a memoized component (e.g. assign the function to a
const and pass it to React.memo or wrap the function when exporting) so the
component is memoized while preserving prop typing and behavior.
| useEffect(() => { | ||
| applyTheme(theme).catch(() => setTheme('github')) | ||
| }, [theme]) |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider notifying users when theme loading fails.
The applyTheme error handler silently falls back to the 'github' theme without informing the user. This could cause confusion when a user selects a theme that fails to load.
Consider adding user notification on theme load failure:
useEffect(() => {
applyTheme(theme).catch((err) => {
console.error('Failed to apply theme:', err)
setTheme('github')
// Optional: add toast notification here
})
}, [theme])🤖 Prompt for AI Agents
In app/components/monaco/MonacoCodeEditor.tsx around lines 82 to 84, the
applyTheme(theme).catch handler silently falls back to 'github' without user
feedback; update the catch to capture the error, log it (console.error or
process logger), call setTheme('github') and also surface a user notification
(e.g., trigger existing toast/notification utility or emit a UI-friendly
message) so users are informed when their selected theme fails to load while
preserving the fallback behavior.
| onLanguageChange={(lang) => | ||
| setFiles((prev) => prev.map((file) => (file.id === activeId ? { ...file, language: lang } : file))) | ||
| } |
There was a problem hiding this comment.
🧹 Nitpick | 🔵 Trivial
Consider extracting the language change handler for clarity.
The inline lambda for language change works but could be extracted to a named function for improved readability and potential reuse.
const handleLanguageChange = useCallback((lang: string) => {
setFiles((prev) => prev.map((file) =>
file.id === activeId ? { ...file, language: lang } : file
))
}, [activeId])
// Then in JSX:
<MonacoToolbar
// ...
onLanguageChange={handleLanguageChange}
// ...
/>🤖 Prompt for AI Agents
In app/components/monaco/MonacoCodeEditor.tsx around lines 125 to 127, the
inline arrow passed to onLanguageChange should be extracted into a named handler
for clarity and reuse; create a handleLanguageChange function using useCallback
that accepts the new language string and calls setFiles(prev => prev.map(file =>
file.id === activeId ? { ...file, language: lang } : file)), include activeId in
the dependency array, and replace the inline lambda in JSX with
onLanguageChange={handleLanguageChange}.
| <head> | ||
| <meta name="robots" content="index,follow" /> | ||
| <link rel="canonical" href="https://deanmachines.com/" /> | ||
| <meta property="og:title" content="AgentStack | Multi-Agent Framework" /> | ||
| <meta property="og:description" content="Production-grade multi-agent framework for AI applications with 22+ agents, 30+ tools." /> | ||
| <meta property="og:image" content="https://deanmachines.com/og-image.png" /> | ||
| <meta name="twitter:card" content="summary_large_image" /> | ||
| <meta name="twitter:site" content="@ssdeanx" /> | ||
| <script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify({ | ||
| "@context": "https://schema.org", | ||
| "@graph": [ | ||
| { | ||
| "@type": "Organization", | ||
| "name": "AgentStack", | ||
| "url": "https://deanmachines.com", | ||
| "logo": "https://deanmachines.com/logo.png", | ||
| "sameAs": ["https://github.com/ssdeanx"] | ||
| }, | ||
| { | ||
| "@type": "WebSite", | ||
| "url": "https://deanmachines.com", | ||
| "name": "AgentStack", | ||
| "potentialAction": { | ||
| "@type": "SearchAction", | ||
| "target": "https://deanmachines.com/search?q={search_term_string}", | ||
| "query-input": "required name=search_term_string" | ||
| } | ||
| }, | ||
| { | ||
| "@type": "Person", | ||
| "name": "Dean (ssdeanx)", | ||
| "url": "https://deanmachines.com", | ||
| "sameAs": ["https://github.com/ssdeanx"] | ||
| } | ||
| ] | ||
| }) }} /> | ||
| </head> |
There was a problem hiding this comment.
Remove duplicate meta tags - Next.js generates these from the metadata export.
The Open Graph and Twitter meta tags (lines 58-62) duplicate what's already defined in the metadata export (lines 24-44). Next.js App Router automatically generates these meta tags from the exported metadata object, so manual injection will result in duplicate tags in the rendered HTML.
The robots meta tag may also conflict with your robots.txt file.
Remove the redundant manual meta tags:
<head>
- <meta name="robots" content="index,follow" />
- <link rel="canonical" href="https://deanmachines.com/" />
- <meta property="og:title" content="AgentStack | Multi-Agent Framework" />
- <meta property="og:description" content="Production-grade multi-agent framework for AI applications with 22+ agents, 30+ tools." />
- <meta property="og:image" content="https://deanmachines.com/og-image.png" />
- <meta name="twitter:card" content="summary_large_image" />
- <meta name="twitter:site" content="@ssdeanx" />
<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify({
// ... JSON-LD content
}) }} />
</head>For the canonical URL, add it to the metadata export instead:
export const metadata: Metadata = {
// ... existing metadata
alternates: {
canonical: 'https://deanmachines.com/',
},
};For Twitter site handle, add to the twitter object:
twitter: {
// ... existing
site: '@ssdeanx',
},🧰 Tools
🪛 ast-grep (0.40.0)
[warning] 62-62: Usage of dangerouslySetInnerHTML detected. This bypasses React's built-in XSS protection. Always sanitize HTML content using libraries like DOMPurify before injecting it into the DOM to prevent XSS attacks.
Context: dangerouslySetInnerHTML
Note: [CWE-79] Improper Neutralization of Input During Web Page Generation [REFERENCES]
- https://reactjs.org/docs/dom-elements.html#dangerouslysetinnerhtml
- https://cwe.mitre.org/data/definitions/79.html
(react-unsafe-html-injection)
🪛 Biome (2.1.2)
[error] 63-63: Avoid passing content using the dangerouslySetInnerHTML prop.
Setting content using code can expose users to cross-site scripting (XSS) attacks
(lint/security/noDangerouslySetInnerHtml)
🤖 Prompt for AI Agents
In app/layout.tsx around lines 55 to 91, remove the manually inserted Open
Graph, Twitter, canonical and robots meta tags and the JSON-LD script that
duplicates metadata generated from the exported metadata object; instead, delete
the redundant <meta> and <link rel="canonical"> entries and move the canonical
URL into the metadata.alternates.canonical field and the twitter site handle
into metadata.twitter.site in the file’s metadata export; ensure no duplicate
robots tag remains (rely on robots.txt or the metadata export) and keep only
non-duplicated, necessary head elements.
… and editor functionality
BlogSharecomponent for sharing blog links via clipboard or native share dialog.AreaWidget: Renders area charts with customizable series and colors.BarWidget: Displays bar charts with adjustable height and color.D3LineChart: Implements a line chart using D3.js for advanced data visualization.D3PieChart: Creates pie charts with customizable inner radius and data.LineWidget: Provides a line chart with two series options.PieWidget: Displays pie charts with customizable inner and outer radii.RadarWidget: Renders radar charts with two series options.RadialGaugeWidget: Implements radial gauge charts for displaying data in a circular format.ScatterWidget: Displays scatter plots with customizable axes and data points.MonacoCodeEditorfor a rich code editing experience with syntax highlighting and theming.MonacoToolbar,MonacoTabs, andMonacoStatusBarfor enhanced editor controls and navigation.robots.txtandsitemap.xmlfor improved SEO and web crawling.AgentToolcomponent for displaying agent data in a structured format.