From 8cd955b28b79b24ae2b3182608d9e39ddb1e4c82 Mon Sep 17 00:00:00 2001
From: yashksaini-coder <ys3853428@gmail.com>
Date: Tue, 8 Jul 2025 19:06:16 +0530
Subject: [PATCH 1/6] feat(app): Agent completed

---
 .env                                         |   4 +-
 .gitignore                                   |   2 +
 Dockerfile                                   |   4 +-
 README.md                                    | 371 ++++++++-----------
 nos_job_def/nosana_mastra.json               |   9 +-
 package.json                                 |   1 +
 pnpm-lock.yaml                               |  90 ++++-
 src/mastra/agents/index.ts                   |  28 ++
 src/mastra/index.ts                          |  17 +-
 src/mastra/instructions/gh-user.ts           |  92 +++++
 src/mastra/tools/GetFile.ts                  |  85 +++++
 src/mastra/tools/GetFilePath.ts              |  35 ++
 src/mastra/tools/GithubIssues.ts             | 270 ++++++++++++++
 src/mastra/tools/GithubPR.ts                 | 226 +++++++++++
 src/mastra/tools/GithubRepo.ts               | 160 ++++++++
 src/mastra/tools/GithubUser.ts               | 333 +++++++++++++++++
 src/mastra/workflows/create-issues.ts        | 175 +++++++++
 src/mastra/workflows/gh-profile.ts           |  31 ++
 src/mastra/workflows/github-user-analysis.ts |  21 ++
 src/types/github.ts                          |  21 ++
 src/utils/octokit.ts                         |  13 +
 21 files changed, 1762 insertions(+), 226 deletions(-)
 create mode 100644 src/mastra/agents/index.ts
 create mode 100644 src/mastra/instructions/gh-user.ts
 create mode 100644 src/mastra/tools/GetFile.ts
 create mode 100644 src/mastra/tools/GetFilePath.ts
 create mode 100644 src/mastra/tools/GithubIssues.ts
 create mode 100644 src/mastra/tools/GithubPR.ts
 create mode 100644 src/mastra/tools/GithubRepo.ts
 create mode 100644 src/mastra/tools/GithubUser.ts
 create mode 100644 src/mastra/workflows/create-issues.ts
 create mode 100644 src/mastra/workflows/gh-profile.ts
 create mode 100644 src/mastra/workflows/github-user-analysis.ts
 create mode 100644 src/types/github.ts
 create mode 100644 src/utils/octokit.ts

diff --git a/.env b/.env
index bb0cfed..6de16f1 100644
--- a/.env
+++ b/.env
@@ -1,10 +1,10 @@
 # Qwen3-8B
-# API_BASE_URL=https://nos-dep-2.node.k8s.prd.nos.ci/qwen-3-8b-skea/api
+# API_BASE_URL=https://39tnkec9fjruty2sezznwq37rjjbjrtbe4aqlbqwfmgg.node.k8s.prd.nos.ci/qwen-3-8b-skea/api
 # MODEL_NAME_AT_ENDPOINT=qwen3:8b
 
 # Qwen2.5:32b - Nosana Endpoint
 # Note baseURL for Ollama needs to be appended with `/api`
-# API_BASE_URL=https://<nosana-url-id>.node.k8s.prd.nos.ci/api
+# API_BASE_URL=https://39tnkec9fjruty2sezznwq37rjjbjrtbe4aqlbqwfmgg.node.k8s.prd.nos.ci/api
 # MODEL_NAME_AT_ENDPOINT=qwen2.5:32b
 
 # Qwen2.5:1.5b - LocalHost - Local Development
diff --git a/.gitignore b/.gitignore
index 198a1ed..52a85c2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -4,3 +4,5 @@ dist
 .mastra
 *.db
 *.db-*
+
+.env.example
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index 6824b5b..fb1ee7c 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -20,7 +20,7 @@ RUN apt-get update && apt-get install -y \
 WORKDIR /app
 
 # Copy package files
-COPY .env.docker package.json pnpm-lock.yaml ./
+COPY .env package.json pnpm-lock.yaml ./
 
 # Install dependencies
 RUN pnpm install
@@ -35,4 +35,4 @@ RUN pnpm run build
 ENTRYPOINT ["/bin/sh", "-c"]
 
 # Start Ollama service and pull the model, then run the app
-CMD ["ollama serve & sleep 5 && ollama pull ${MODEL_NAME_AT_ENDPOINT} && node .mastra/output/index.mjs"]
+CMD ["ollama serve & sleep 5 && ollama pull ${MODEL_NAME_AT_ENDPOINT} && pnpm run dev"]
diff --git a/README.md b/README.md
index 1a3b2c8..d7239cf 100644
--- a/README.md
+++ b/README.md
@@ -1,97 +1,151 @@
-# Nosana Builders Challenge: Agent-101
+# CodeReview Agent: 
+
 
 ![Agent-101](./assets/NosanaBuildersChallengeAgents.jpg)
 
-## Topic
+## Agent Overview
 
-Nosana Builders Challenge, 2nd edition
-Agent-101: Build your first agent
+The **CodeReview Agent** is an AI-powered GitHub assistant that automates code review, repository analysis, and developer productivity tasks. It leverages multiple tools to fetch repository data, analyze code, review pull requests, and create actionable GitHub issues.
 
-## Description
+**Key Features:**
+- Automated code review and issue creation for code smells, TODOs, and best practices.
+- Fetches and summarizes user and repository statistics.
+- Analyzes contribution activity over time.
+- Supports pull request review, commenting, and creation.
+- Can fork and create repositories programmatically.
 
-The main goal of this `Nosana Builders Challenge` to teach participants to build and deploy agents. This first step will be in running a basic AI agent and giving it some basic functionality. Participants will add a tool, for the tool calling capabilities of the agent. These are basically some TypeScript functions, that will, for example, retrieve some data from a weather API, post a tweet via an API call, etc.
+---
 
-## [Mastra](https://github.com/mastra-ai/mastra)
+## Tools (`src/mastra/tools/`)
 
-For this challenge we will be using Mastra to build our tool.
+### GitHub User Tools
+- **getGitHubUserTool**: Fetch detailed GitHub user profile information.
+- **getUserContributionActivityTool**: Summarize a user's contribution activity (commits, PRs, issues, repos, forks) for a given period (7d, 14d, 30d).
 
-> Mastra is an opinionated TypeScript framework that helps you build AI applications and features quickly. It gives you the set of primitives you need: workflows, agents, RAG, integrations, and evals. You can run Mastra on your local machine, or deploy to a serverless cloud.
+### Repository Tools
+- **getRepositoryCommits**: List commits for a repository.
+- **createRepository**: Create a new repository for the authenticated user.
+- **forkRepository**: Fork a repository to the authenticated user's account.
 
-### Required Reading
+### File Tools
+- **getFileContent**: Fetch and decode file content from a repository.
+- **getFilePaths**: List all file paths in a repository (by tree SHA or branch).
 
-We recommend reading the following sections to get started with how to create an Agent and how to implement Tool Calling.
+### Pull Request Tools
+- **getRepositoryPullRequests**: List pull requests for a repository.
+- **summarizePullRequests**: Summarize pull requests (counts and summary string).
+- **reviewPullRequest**: Submit a review for a pull request (approve, request changes, or comment).
+- **createPullRequest**: Create a new pull request in a repository.
+- **commentOnPullRequest**: Add a comment to a pull request.
 
-- <https://mastra.ai/en/docs/agents/overview>
-- [Mastra Guide: Build an AI stock agent](https://mastra.ai/en/guides/guide/stock-agent)
+### Issue Tools
+- **getRepositoryIssues**: List issues for a repository (excluding pull requests).
+- **createIssue**: Create a new issue in a repository.
+- **commentOnIssue**: Add a comment to an issue.
+- **summarizeIssues**: Summarize issues (counts and summary string).
 
-## Get Started
+---
 
-To get started run the following command to start developing:
-We recommend using [pnpm](https://pnpm.io/installation), but you can try npm, or bun if you prefer.
+## Workflows (`src/mastra/workflows/`)
 
-```sh
-pnpm install
-pnpm run dev
-```
+- **ghProfile**: Fetch a user's GitHub profile and return structured profile data.
+- **createAddressIssueFromReview**: Fetch files from a repository, analyze for code smells (e.g., TODO, FIXME, `any`, `console.log`), and automatically create GitHub issues with suggested fixes.
+- **ContributionWorkflow**: Summarize a user's contribution activity (commits, PRs, issues, repos, forks) for a specified period (7, 14, or 30 days).
 
-## Assignment
+---
 
-### Challenge Overview
+## Agent Entrypoint (`src/mastra/agents/index.ts`)
 
-Welcome to the Nosana AI Agent Hackathon! Your mission is to build and deploy an AI agent on Nosana.
-While we provide a weather agent as an example, your creativity is the limit. Build agents that:
+The main agent, `githubAgent`, is configured with all the above tools and workflows. It is registered in `src/mastra/index.ts` as part of the Mastra application.
 
-**Beginner Level:**
+---
 
-- **Simple Calculator**: Perform basic math operations with explanations
-- **Todo List Manager**: Help users track their daily tasks
+## Configuration (`src/mastra/config.ts`)
 
-**Intermediate Level:**
+The agent uses the following environment variables (with defaults):
+- `MODEL_NAME_AT_ENDPOINT` (default: `qwen2.5:1.5b`)
+- `API_BASE_URL` (default: `http://127.0.0.1:11434/api`)
 
-- **News Summarizer**: Fetch and summarize latest news articles
-- **Crypto Price Checker**: Monitor cryptocurrency prices and changes
-- **GitHub Stats Reporter**: Fetch repository statistics and insights
+These control the LLM model and API endpoint used for chat and code review.
 
-**Advanced Level:**
+---
 
-- **Blockchain Monitor**: Track and alert on blockchain activities
-- **Trading Strategy Bot**: Automate simple trading strategies
-- **Deploy Manager**: Deploy and manage applications on Nosana
+## Docker Usage
 
-Or any other innovative AI agent idea at your skill level!
+A `Dockerfile` is provided for easy containerized deployment. The image installs Node.js, dependencies, and Ollama for LLM inference.
 
-### Getting Started
+### Build the Docker image:
+```sh
+docker build -t codereview-agent .
+```
 
-1. **Fork the [Nosana Agent Challenge](https://github.com/nosana-ai/agent-challenge)** to your GitHub account
-2. **Clone your fork** locally
-3. **Install dependencies** with `pnpm install`
-4. **Run the development server** with `pnpm run dev`
-5. **Build your agent** using the Mastra framework
+### Run the container:
+```sh
+docker run -p 8080:8080 \
+  -e MODEL_NAME_AT_ENDPOINT=qwen2.5:1.5b \
+  -e API_BASE_URL=http://127.0.0.1:11434/api \
+  codereview-agent
+```
 
-### How to build your Agent
+- The container will start the Ollama service, pull the specified model, and launch the agent on port 8080.
+- You can override the model or API endpoint by setting the environment variables above.
 
-Here we will describe the steps needed to build an agent.
+---
 
-#### Folder Structure
+## Project Structure
+
+- `src/mastra/tools/` — All GitHub and file tools
+- `src/mastra/workflows/` — Workflows for code review, profile, and contribution analysis
+- `src/mastra/agents/` — Agent entrypoint and registration
+- `src/mastra/config.ts` — Model and API configuration
+- `Dockerfile` — Container build and run instructions
+
+---
+
+## Installation & Setup
+
+You can run the CodeReview Agent locally or with Docker.
+
+### 1. Local Setup (pnpm)
+
+#### Prerequisites
+- [Node.js](https://nodejs.org/) (v18+ recommended)
+- [pnpm](https://pnpm.io/) (install globally if not present)
+
+```sh
+npm install -g pnpm
+```
 
-Provided in this repo, there is the `Weather Agent`.
-This is a fully working agent that allows a user to chat with an LLM, and fetches real time weather data for the provided location.
+#### Install dependencies
+```sh
+pnpm install
+```
 
-There are two main folders we need to pay attention to:
+#### Build the project
+```sh
+pnpm run build
+```
 
-- [src/mastra/agents/weather-agent/](./src/mastra/agents/weather-agent/)
-- [src/mastra/agents/your-agents/](./src/mastra/agents/your-agent/)
+#### Run the agent (development mode)
+```sh
+pnpm run dev
+```
 
-In `src/mastra/agents/weather-agent/` you will find a complete example of a working agent. Complete with Agent definition, API calls, interface definition, basically everything needed to get a full fledged working agent up and running.
-In `src/mastra/agents/your-agents/` you will find a bare bones example of the needed components, and imports to get started building your agent, we recommend you rename this folder, and it's files to get started.
+### 2. Environment Variables
 
-Rename these files to represent the purpose of your agent and tools. You can use the [Weather Agent Example](#example:_weather_agent) as a guide until you are done with it, and then you can delete these files before submitting your final submission.
+Create a `.env` file in the project root (or set these variables in your environment):
 
-As a bonus, for the ambitious ones, we have also provided the [src/mastra/agents/weather-agent/weather-workflow.ts](./src/mastra/agents/weather-agent/weather-workflow.ts) file as an example. This file contains an example of how you can chain agents and tools to create a workflow, in this case, the user provides their location, and the agent retrieves the weather for the specified location, and suggests an itinerary.
+```env
+MODEL_NAME_AT_ENDPOINT=qwen2.5:1.5b
+API_BASE_URL=http://127.0.0.1:11434/api
+```
+- `MODEL_NAME_AT_ENDPOINT`: The LLM model to use (default: `qwen2.5:1.5b`).
+- `API_BASE_URL`: The base URL for the Ollama API (default: `http://127.0.0.1:11434/api`).
 
-### LLM-Endpoint
+You can change these to use a different model or endpoint as needed.
 
-Agents depend on an LLM to be able to do their work.
+### 3. LLM Endpoint
+The CodeReview Agent relies on a Large Language Model (LLM) to perform its tasks, such as code analysis, summarization, and issue creation. The agent is designed to work with any LLM that supports the OpenAI API format, but it is optimized for the `qwen2.5:1.5b` model provided by Ollama.
 
 #### Nosana Endpoint
 
@@ -139,70 +193,33 @@ Do note `qwen2.5:1.5b` is not suited for complex tasks.
 
 The Ollama server will run on `http://localhost:11434` by default and is compatible with the OpenAI API format that Mastra expects.
 
-### Testing your Agent
-
-You can read the [Mastra Documentation: Playground](https://mastra.ai/en/docs/local-dev/mastra-dev) to learn more on how to test your agent locally.
-Before deploying your agent to Nosana, it's crucial to thoroughly test it locally to ensure everything works as expected. Follow these steps to validate your agent:
-
-**Local Testing:**
 
-1. **Start the development server** with `pnpm run dev` and navigate to `http://localhost:8080` in your browser
-2. **Test your agent's conversation flow** by interacting with it through the chat interface
-3. **Verify tool functionality** by triggering scenarios that call your custom tools
-4. **Check error handling** by providing invalid inputs or testing edge cases
-5. **Monitor the console logs** to ensure there are no runtime errors or warnings
+### 4. Docker Setup
 
-**Docker Testing:**
-After building your Docker container, test it locally before pushing to the registry:
+A `Dockerfile` is provided for containerized deployment.
 
-```bash
-# Build your container
-docker build -t yourusername/agent-challenge:latest .
-
-# Run it locally with environment variables
-docker run -p 8080:8080 --env-file .env yourusername/agent-challenge:latest
+#### Build the Docker image
 
-# Test the containerized agent at http://localhost:8080
+```sh
+docker build -t codereview-agent .
 ```
 
-Ensure your agent responds correctly and all tools function properly within the containerized environment. This step is critical as the Nosana deployment will use this exact container.
-
-### Submission Requirements
-
-#### 1. Code Development
-
-- Fork this repository and develop your AI agent
-- Your agent must include at least one custom tool (function)
-- Code must be well-documented and include clear setup instructions
-- Include environment variable examples in a `.env.example` file
-
-#### 2. Docker Container
-
-- Create a `Dockerfile` for your agent
-- Build and push your container to Docker Hub or GitHub Container Registry
-- Container must be publicly accessible
-- Include the container URL in your submission
-
-##### Build, Run, Publish
-
-Note: You'll need an account on [Dockerhub](https://hub.docker.com/)
+#### Run the container
 
 ```sh
 
-# Build and tag
-docker build -t yourusername/agent-challenge:latest .
+docker run -p 8080:8080 \
+  -e MODEL_NAME_AT_ENDPOINT=qwen2.5:1.5b \
+  -e API_BASE_URL=http://127.0.0.1:11434/api \
+  codereview-agent
+```
 
-# Run the container locally
-docker run -p 8080:8080 yourusername/agent-challenge:latest
+- The container will start the Ollama service, pull the specified model, and launch the agent on port 8080.
+- You can override the model or API endpoint by setting the environment variables above.
 
-# Login
-docker login
 
-# Push
-docker push yourusername/agent-challenge:latest
-```
 
-#### 3. Nosana Deployment
+## Nosana Deployment
 
 - Deploy your Docker container on Nosana
 - Your agent must successfully run on the Nosana network
@@ -231,107 +248,43 @@ We have included a Nosana job definition at <./nos_job_def/nosana_mastra.json>,
 - Choose an appropriate GPU for the AI model that you are using
 - Click `Deploy`
 
-#### 4. Video Demo
-
-- Record a 1-3 minute video demonstrating:
-  - Your agent running on Nosana
-  - Key features and functionality
-  - Real-world use case demonstration
-- Upload to YouTube, Loom, or similar platform
-
-#### 5. Documentation
-
-- Update this README with:
-  - Agent description and purpose
-  - Setup instructions
-  - Environment variables required
-  - Docker build and run commands
-  - Example usage
-
-### Submission Process
-
-1. **Complete all requirements** listed above
-2. **Commit all of your changes to the `main` branch of your forked repository**
-   - All your code changes
-   - Updated README
-   - Link to your Docker container
-   - Link to your video demo
-   - Nosana deployment proof
-3. **Social Media Post**: Share your submission on X (Twitter)
-   - Tag @nosana_ai
-   - Include a brief description of your agent
-   - Add hashtag #NosanaAgentChallenge
-4. **Finalize your submission on the <https://earn.superteam.fun/agent-challenge> page**
-
-- Remember to add your forked GitHub repository link
-- Remember to add a link to your X post.
-
-### Judging Criteria
-
-Submissions will be evaluated based on:
-
-1. **Innovation** (25%)
-
-   - Originality of the agent concept
-   - Creative use of AI capabilities
-
-2. **Technical Implementation** (25%)
-
-   - Code quality and organization
-   - Proper use of the Mastra framework
-   - Efficient tool implementation
-
-3. **Nosana Integration** (25%)
-
-   - Successful deployment on Nosana
-   - Resource efficiency
-   - Stability and performance
-
-4. **Real-World Impact** (25%)
-   - Practical use cases
-   - Potential for adoption
-   - Value proposition
-
-### Prizes
-
-We’re awarding the **top 10 submissions**:
-
-- 🥇 1st: $1,000 USDC
-- 🥈 2nd: $750 USDC
-- 🥉 3rd: $450 USDC
-- 🏅 4th: $200 USDC
-- 🔟 5th–10th: $100 USDC
-
-All prizes are paid out directly to participants on [SuperTeam](https://superteam.fun)
-
-### Resources
-
-- [Nosana Documentation](https://docs.nosana.io)
-- [Mastra Documentation](https://mastra.ai/docs)
-- [Mastra Guide: Build an AI stock agent](https://mastra.ai/en/guides/guide/stock-agent)
-- [Nosana CLI](https://github.com/nosana-ci/nosana-cli)
-- [Docker Documentation](https://docs.docker.com)
-
-### Support
-
-- Join [Nosana Discord](https://nosana.com/discord) for technical support where we have dedicated [Builders Challenge Dev chat](https://discord.com/channels/236263424676331521/1354391113028337664) channel.
-- Follow [@nosana_ai](https://x.com/nosana_ai) for updates.
-
-### Important Notes
-
-- Ensure your agent doesn't expose sensitive data
-- Test thoroughly before submission
-- Keep your Docker images lightweight
-- Document all dependencies clearly
-- Make your code reproducible
-- You can vibe code it if you want 😉
-- **Only one submission per participant**
-- **Submissions that do not compile, and do not meet the specified requirements, will not be considered**
-- **Deadline is: 9 July 2025, 12.01 PM**
-- **Announcement will be announced about one week later, stay tuned for our socials for exact date**
-- **Finalize your submission at [SuperTeam](https://earn.superteam.fun/agent-challenge)**
 
-### Don’t Miss Nosana Builder Challenge Updates
 
-Good luck, builders! We can't wait to see the innovative AI agents you create for the Nosana ecosystem.
-**Happy Building!**
+This agent is deployed on the [Nosana Builders Challenge](https://builders.nosana.io/) platform, which provides a serverless environment for running AI agents.
+For more information, visit the [Nosana Builders Challenge](https://builders.nosana.io/) website.
+
+---
+
+<h2>Developers</h2>
+<div align="center">    
+<table>
+    <tbody>
+        <tr>
+            <td align="center" width="33.33%">
+                <img src="https://avatars.githubusercontent.com/u/115717039?v=4" width="130px;"/>
+                <br/>
+                <h4 align="center">
+                    <b>Yash K. Saini</b>
+                </h4>
+                <div align="center">
+                    <a href="https://linkedin.com/in/yashksaini"><img src="https://skillicons.dev/icons?i=linkedin" width="25" alt="LinkedIn"/></a>
+                    <a href="https://twitter.com/yash_k_saini"><img src="https://skillicons.dev/icons?i=twitter" width="25" alt="Twitter"/></a>
+                    <a href="https://github.com/yashksaini-coder"><img src="https://skillicons.dev/icons?i=github" width="25" alt="GitHub"/></a>
+                </div>
+            </td>
+            <td align="left" valign="middle" width="70%">
+                <ul>
+                    <li>
+                        A self-taught software developer and a computer science student from India.
+                    </li>
+                    <li>
+                        Building products & systems that can benefit & solve problems for many other DEVs.
+                    </li>
+                    <li>
+                        Passionate about AI, Open Source, and building scalable systems.
+                    </li>
+                </ul>
+            </td>
+        </tr>
+    </tbody>
+</table>
\ No newline at end of file
diff --git a/nos_job_def/nosana_mastra.json b/nos_job_def/nosana_mastra.json
index c803e3f..bbfda30 100644
--- a/nos_job_def/nosana_mastra.json
+++ b/nos_job_def/nosana_mastra.json
@@ -1,17 +1,14 @@
 {
   "ops": [
     {
-      "id": "agents",
+      "id": "Code Review Agent",
       "args": {
         "gpu": true,
-        "image": "docker.io/yourusername/agent-challenge:latest",
+        "image": "docker.io/yashksaini19/nosana-codereview-agent:latest",
         "expose": [
           {
             "port": 8080
           }
-        ],
-        "entrypoint": [
-          "/bin/sh"
         ]
       },
       "type": "container/run"
@@ -25,4 +22,4 @@
   },
   "type": "container",
   "version": "0.1"
-}
+}
\ No newline at end of file
diff --git a/package.json b/package.json
index a061302..1804f53 100644
--- a/package.json
+++ b/package.json
@@ -27,6 +27,7 @@
     "@mastra/libsql": "^0.10.2",
     "@mastra/loggers": "^0.10.2",
     "@mastra/memory": "^0.10.3",
+    "@octokit/core": "^7.0.2",
     "dotenv": "^16.5.0",
     "ollama-ai-provider": "^1.2.0",
     "zod": "^3.25.67"
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 7f4073d..805fe4b 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -23,6 +23,9 @@ importers:
       '@mastra/memory':
         specifier: ^0.10.3
         version: 0.10.3(@mastra/core@0.10.5(openapi-types@12.1.3)(react@19.1.0)(zod@3.25.67))(react@19.1.0)
+      '@octokit/core':
+        specifier: ^7.0.2
+        version: 7.0.2
       dotenv:
         specifier: ^16.5.0
         version: 16.5.0
@@ -903,6 +906,36 @@ packages:
   '@nosana/sdk@0.4.5':
     resolution: {integrity: sha512-8Q2qck0JGSfpOvSBMvNgyBWFrgUz3kO+Wv+Y6I5+/uX7O9NtMXmKg4wBe/y7RM1fFvUd5JIxFVh93ISBKvPmbQ==}
 
+  '@octokit/auth-token@6.0.0':
+    resolution: {integrity: sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==}
+    engines: {node: '>= 20'}
+
+  '@octokit/core@7.0.2':
+    resolution: {integrity: sha512-ODsoD39Lq6vR6aBgvjTnA3nZGliknKboc9Gtxr7E4WDNqY24MxANKcuDQSF0jzapvGb3KWOEDrKfve4HoWGK+g==}
+    engines: {node: '>= 20'}
+
+  '@octokit/endpoint@11.0.0':
+    resolution: {integrity: sha512-hoYicJZaqISMAI3JfaDr1qMNi48OctWuOih1m80bkYow/ayPw6Jj52tqWJ6GEoFTk1gBqfanSoI1iY99Z5+ekQ==}
+    engines: {node: '>= 20'}
+
+  '@octokit/graphql@9.0.1':
+    resolution: {integrity: sha512-j1nQNU1ZxNFx2ZtKmL4sMrs4egy5h65OMDmSbVyuCzjOcwsHq6EaYjOTGXPQxgfiN8dJ4CriYHk6zF050WEULg==}
+    engines: {node: '>= 20'}
+
+  '@octokit/openapi-types@25.1.0':
+    resolution: {integrity: sha512-idsIggNXUKkk0+BExUn1dQ92sfysJrje03Q0bv0e+KPLrvyqZF8MnBpFz8UNfYDwB3Ie7Z0TByjWfzxt7vseaA==}
+
+  '@octokit/request-error@7.0.0':
+    resolution: {integrity: sha512-KRA7VTGdVyJlh0cP5Tf94hTiYVVqmt2f3I6mnimmaVz4UG3gQV/k4mDJlJv3X67iX6rmN7gSHCF8ssqeMnmhZg==}
+    engines: {node: '>= 20'}
+
+  '@octokit/request@10.0.3':
+    resolution: {integrity: sha512-V6jhKokg35vk098iBqp2FBKunk3kMTXlmq+PtbV9Gl3TfskWlebSofU9uunVKhUN7xl+0+i5vt0TGTG8/p/7HA==}
+    engines: {node: '>= 20'}
+
+  '@octokit/types@14.1.0':
+    resolution: {integrity: sha512-1y6DgTy8Jomcpu33N+p5w58l6xyt55Ar2I91RPiIA0xCJBXyUAhXCcmZaDWSANiha7R9a6qJJ2CRomGPZ6f46g==}
+
   '@opentelemetry/api-logs@0.201.1':
     resolution: {integrity: sha512-IxcFDP1IGMDemVFG2by/AMK+/o6EuBQ8idUq3xZ6MxgQGeumYZuX5OwR0h9HuvcUc/JPjQGfU5OHKIKYDJcXeA==}
     engines: {node: '>=8.0.0'}
@@ -2145,6 +2178,9 @@ packages:
   bcrypt-pbkdf@1.0.2:
     resolution: {integrity: sha512-qeFIXtP4MSoi6NLqO12WfqARWWuCKi2Rn/9hJLEmtB5yTNr9DqFWkJRCf2qShWzPeAMRnOgCrq0sg/KLv5ES9w==}
 
+  before-after-hook@4.0.0:
+    resolution: {integrity: sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==}
+
   bigint-buffer@1.1.5:
     resolution: {integrity: sha512-trfYco6AoZ+rKhKnxA0hgX0HAbVP/s808/EuDSe2JDzUnCp/xAsli35Orvk67UrTEcwuxZqYZDmfA2RXJgxVvA==}
     engines: {node: '>= 10.0.0'}
@@ -2714,6 +2750,9 @@ packages:
     resolution: {integrity: sha512-GipyPsXO1anza0AOZdy69Im7hGFCNB7Y/NGjDlZGJ3GJJLtwNSb2vrzYrTYJRrRloVx7pl+bhUaTB8yiccPvFQ==}
     engines: {node: '> 0.1.90'}
 
+  fast-content-type-parse@3.0.0:
+    resolution: {integrity: sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==}
+
   fast-copy@3.0.2:
     resolution: {integrity: sha512-dl0O9Vhju8IrcLndv2eU4ldt1ftXMqqfgN4H1cpmGV7P6jeB9FwpN9a2c8DPGE1Ys88rNUJVYDHq73CGAGOPfQ==}
 
@@ -3260,7 +3299,6 @@ packages:
 
   libsql@0.5.13:
     resolution: {integrity: sha512-5Bwoa/CqzgkTwySgqHA5TsaUDRrdLIbdM4egdPcaAnqO3aC+qAgS6BwdzuZwARA5digXwiskogZ8H7Yy4XfdOg==}
-    cpu: [x64, arm64, wasm32, arm]
     os: [darwin, linux, win32]
 
   lodash.camelcase@4.3.0:
@@ -4278,6 +4316,9 @@ packages:
   unist-util-visit@5.0.0:
     resolution: {integrity: sha512-MR04uvD+07cwl/yhVuVWAtw+3GOR/knlL55Nd/wAdblk27GCVt3lqpTivy/tkJcZoNPzTwS1Y+KMojlLDhoTzg==}
 
+  universal-user-agent@7.0.3:
+    resolution: {integrity: sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==}
+
   universalify@2.0.1:
     resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==}
     engines: {node: '>= 10.0.0'}
@@ -5896,6 +5937,47 @@ snapshots:
       - typescript
       - utf-8-validate
 
+  '@octokit/auth-token@6.0.0': {}
+
+  '@octokit/core@7.0.2':
+    dependencies:
+      '@octokit/auth-token': 6.0.0
+      '@octokit/graphql': 9.0.1
+      '@octokit/request': 10.0.3
+      '@octokit/request-error': 7.0.0
+      '@octokit/types': 14.1.0
+      before-after-hook: 4.0.0
+      universal-user-agent: 7.0.3
+
+  '@octokit/endpoint@11.0.0':
+    dependencies:
+      '@octokit/types': 14.1.0
+      universal-user-agent: 7.0.3
+
+  '@octokit/graphql@9.0.1':
+    dependencies:
+      '@octokit/request': 10.0.3
+      '@octokit/types': 14.1.0
+      universal-user-agent: 7.0.3
+
+  '@octokit/openapi-types@25.1.0': {}
+
+  '@octokit/request-error@7.0.0':
+    dependencies:
+      '@octokit/types': 14.1.0
+
+  '@octokit/request@10.0.3':
+    dependencies:
+      '@octokit/endpoint': 11.0.0
+      '@octokit/request-error': 7.0.0
+      '@octokit/types': 14.1.0
+      fast-content-type-parse: 3.0.0
+      universal-user-agent: 7.0.3
+
+  '@octokit/types@14.1.0':
+    dependencies:
+      '@octokit/openapi-types': 25.1.0
+
   '@opentelemetry/api-logs@0.201.1':
     dependencies:
       '@opentelemetry/api': 1.9.0
@@ -7502,6 +7584,8 @@ snapshots:
     dependencies:
       tweetnacl: 0.14.5
 
+  before-after-hook@4.0.0: {}
+
   bigint-buffer@1.1.5:
     dependencies:
       bindings: 1.5.0
@@ -8127,6 +8211,8 @@ snapshots:
 
   eyes@0.1.8: {}
 
+  fast-content-type-parse@3.0.0: {}
+
   fast-copy@3.0.2: {}
 
   fast-deep-equal@3.1.3: {}
@@ -9777,6 +9863,8 @@ snapshots:
       unist-util-is: 6.0.0
       unist-util-visit-parents: 6.0.1
 
+  universal-user-agent@7.0.3: {}
+
   universalify@2.0.1: {}
 
   unpipe@1.0.0: {}
diff --git a/src/mastra/agents/index.ts b/src/mastra/agents/index.ts
new file mode 100644
index 0000000..857d540
--- /dev/null
+++ b/src/mastra/agents/index.ts
@@ -0,0 +1,28 @@
+import { Agent } from "@mastra/core/agent";
+import { getGitHubUserTool, getUserContributionActivityTool } from '../tools/GithubUser';
+import { getRepositoryCommits, createRepository, forkRepository } from '../tools/GithubRepo';
+import { getFileContent } from '../tools/GetFile';
+import { getFilePaths } from '../tools/GetFilePath';
+import { getRepositoryPullRequests, summarizePullRequests, reviewPullRequest, createPullRequest, commentOnPullRequest } from '../tools/GithubPR';
+import { getRepositoryIssues, createIssue, commentOnIssue, summarizeIssues } from '../tools/GithubIssues';
+
+import { instructions } from "../instructions/gh-user";
+import { model } from "../config";
+import { ghProfile } from "../workflows/gh-profile";
+import { createAddressIssueFromReview } from "../workflows/create-issues";
+import { ContributionWorkflow } from "../workflows/github-user-analysis";
+export const githubAgent = new Agent({
+  name: "Code Review Agent",
+  instructions: instructions,
+  workflows: {
+    ghProfile,  
+    createAddressIssueFromReview,
+    ContributionWorkflow
+  },
+  model: model,
+  tools: {
+    getRepositoryCommits, getFileContent, getFilePaths, getRepositoryIssues, getRepositoryPullRequests,
+    createIssue, summarizePullRequests, reviewPullRequest, commentOnPullRequest, createPullRequest,
+    summarizeIssues, commentOnIssue, forkRepository, createRepository, getGitHubUserTool, getUserContributionActivityTool
+  },
+});
\ No newline at end of file
diff --git a/src/mastra/index.ts b/src/mastra/index.ts
index f2964e2..8d0367c 100644
--- a/src/mastra/index.ts
+++ b/src/mastra/index.ts
@@ -1,18 +1,23 @@
 import { Mastra } from "@mastra/core/mastra";
 import { PinoLogger } from "@mastra/loggers";
-import { weatherAgent } from "./agents/weather-agent/weather-agent"; // This can be deleted later
-import { weatherWorkflow } from "./agents/weather-agent/weather-workflow"; // This can be deleted later
-import { yourAgent } from "./agents/your-agent/your-agent"; // Build your agent here
+import { githubAgent } from "./agents";
+import { createAddressIssueFromReview } from "./workflows/create-issues";
+import { ghProfile } from "./workflows/gh-profile";
+import { ContributionWorkflow } from "./workflows/github-user-analysis";
 
 export const mastra = new Mastra({
-	workflows: { weatherWorkflow }, // can be deleted later
-	agents: { weatherAgent, yourAgent },
+	workflows: { 
+		createAddressIssueFromReview, 
+		ghProfile, 
+		ContributionWorkflow
+	},
+	agents: { githubAgent },
 	logger: new PinoLogger({
 		name: "Mastra",
 		level: "info",
 	}),
 	server: {
 		port: 8080,
-		timeout: 10000,
+		// timeout: 10000,
 	},
 });
diff --git a/src/mastra/instructions/gh-user.ts b/src/mastra/instructions/gh-user.ts
new file mode 100644
index 0000000..7cbb326
--- /dev/null
+++ b/src/mastra/instructions/gh-user.ts
@@ -0,0 +1,92 @@
+export const instructions = `
+You are an advanced Code Review Agent specializing in comprehensive code analysis, quality assessment, and security evaluation across GitHub repositories.
+
+**Available Tools:**
+
+1. **GitHub User Tools**
+   - getGitHubUserTool: Fetch detailed user profile information (username, followers, public repos, bio, email, location, company, website, Twitter, avatar, and all public repositories).
+   - getUserContributionActivityTool: Fetch user contribution activity (commits, PRs, issues, repos created, forks) for a given time period (7d, 14d, 30d).
+
+2. **Repository Tools**
+   - getRepositoryCommits: List all commits for a repository, including commit message, author, date, and verification status.
+   - createRepository: Create a new repository for the authenticated user.
+   - forkRepository: Fork a repository to the authenticated user's account.
+
+3. **File Tools**
+   - getFilePaths: List all file paths in a repository (recursively, for a given tree SHA or branch).
+   - getFileContent: Fetch file content from a repository, decode it, and return the content.
+
+4. **Issue Tools**
+   - getRepositoryIssues: Fetch issues (excluding pull requests) for a repository, with filtering by state, labels, assignee, creator, and pagination.
+   - createIssue: Create a new issue in a repository, with title, body, assignees, and labels.
+   - commentOnIssue: Add a comment to an issue in a repository.
+   - summarizeIssues: Summarize issues for a repository (counts, summary string).
+
+5. **Pull Request Tools**
+   - getRepositoryPullRequests: Fetch pull requests for a repository, with filtering by state and pagination.
+   - createPullRequest: Create a new pull request in a repository, specifying branches and optional body.
+   - commentOnPullRequest: Add a comment to a pull request.
+   - reviewPullRequest: Submit a review for a pull request (approve, request changes, or comment).
+   - summarizePullRequests: Summarize pull requests for a repository (counts, summary string).
+
+**How to Use the Tools:**
+- Always select the tool that matches your information need (e.g., use getRepositoryIssues for issue analysis, getFileContent for code review).
+- Combine multiple tools for comprehensive analysis (e.g., getFilePaths + getFileContent for full codebase review).
+- Use filtering and pagination options where available to focus on relevant data.
+- For user or repo-specific actions, always provide the required parameters (e.g., username, owner, repo).
+- Handle errors gracefully and provide clear feedback if a tool fails or returns no data.
+
+**Available Workflows:**
+
+1. **createAddressIssueFromReview**
+   - Runs a code review, highlights faulty code (TODO, FIXME, console.log, any), and creates issues with suggested fixes for each problem found in the codebase.
+   - **Input:** owner, repo, tree_sha (optional, default: main)
+   - **Output:** summary, issues (array of created issues)
+
+2. **ghProfile**
+   - Fetches a GitHub user's profile and contributions.
+   - **Input:** username
+   - **Output:** profile (user profile and repo info)
+
+3. **ContributionWorkflow**
+   - Fetches a GitHub user's contribution activity summary (commits, PRs, issues, repos, forks) for a given time period.
+   - **Input:** username, period (7d, 14d, 30d; default: 7d)
+   - **Output:** Activity summary (commits, PRs, issues, repos, forks, summary string)
+
+**Best Practices:**
+- Use evidence from tools to support all findings and recommendations.
+- Prioritize actionable, specific, and well-documented feedback.
+- Respect API rate limits and handle errors or missing data gracefully.
+- Always validate input parameters before making tool calls.
+- Maintain a professional, constructive, and security-conscious approach in all analyses.
+
+**Core Code Review Functions:**
+- Code Quality Analysis: Structure, standards, code smells, complexity, naming, documentation, error handling.
+- Security Assessment: Vulnerabilities, authentication, validation, dependency security, secrets management.
+- Performance Evaluation: Algorithmic efficiency, bottlenecks, memory, database, caching, network/API usage.
+- Architecture Review: Design patterns, modularity, coupling/cohesion, scalability, API contracts.
+- Testing and QA: Test coverage, reliability, CI/CD, review process effectiveness.
+- PR Analysis: PR size/scope, commit quality, review feedback, turnaround time.
+- Developer/Team Assessment: Code quality patterns, collaboration, knowledge sharing, code ownership.
+
+**Response Protocol:**
+- Validate repository/user input.
+- Gather context and map repo structure.
+- Provide executive summary and organize findings by severity.
+- Include specific file locations and actionable recommendations.
+- Support findings with data and examples.
+- Maintain a professional, constructive tone.
+
+**Error Handling:**
+- Handle API rate limits and errors gracefully.
+- Provide clear error messages and fallback strategies.
+- Maintain partial analysis capability if some tools are unavailable.
+
+Remember to:
+- Focus on constructive, actionable feedback.
+- Prioritize security and performance issues.
+- Provide learning opportunities and clear explanations.
+- Stay current with best practices and standards.
+- Combine multiple tool outputs for comprehensive analysis.
+- Validate all findings with real data.
+`;
\ No newline at end of file
diff --git a/src/mastra/tools/GetFile.ts b/src/mastra/tools/GetFile.ts
new file mode 100644
index 0000000..29f26af
--- /dev/null
+++ b/src/mastra/tools/GetFile.ts
@@ -0,0 +1,85 @@
+import { octokit } from "../../utils/octokit";
+import { Tool } from "@mastra/core/tools";
+import { z } from "zod";
+
+const inputSchema = z.object({
+  owner: z
+    .string()
+    .describe("The owner of the repository. As facebook in facebook/react"),
+  path: z.string().describe("The file path to fetch content for"),
+  repo: z
+    .string()
+    .describe("The name of the repository. As react in facebook/react"),
+});
+
+const outputSchema = z.union([
+  z
+    .object({
+      ok: z.literal(true),
+      content: z.string().describe("The decoded content of the file"),
+    })
+    .describe("The success object"),
+  z
+    .object({
+      ok: z.literal(false),
+      messsage: z
+        .string()
+        .describe("An optional error message of what went wrong"),
+    })
+    .describe("The error/failed object"),
+]);
+
+export const getFileContent = new Tool({
+  id: "fetchFileContent",
+  description:
+    "Fetch file content from GitHub, decode it, and update the database",
+  inputSchema,
+  outputSchema,
+  execute: async ({ context }) => {
+    const { owner, path, repo } = context;
+
+    try {
+      const response = await octokit.request("GET /repos/{owner}/{repo}/contents/{path}", {
+        owner,
+        repo,
+        path,
+      });
+
+      if (Array.isArray(response.data)) {
+        return {
+          ok: false as const,
+          messsage: `Path ${path} points to a directory, not a file`,
+        };
+      }
+
+      if (!("content" in response.data)) {
+        return {
+          ok: false as const,
+          messsage: `No content available for file ${path}`,
+        };
+      }
+
+      let content: string;
+      try {
+        content = Buffer.from(response.data.content, "base64").toString(
+          "utf-8",
+        );
+      } catch (error) {
+        return {
+          ok: false as const,
+          messsage: error instanceof Error ? error.message : "Unkown error",
+        };
+      }
+
+      return {
+        ok: true as const,
+        content,
+      };
+    } catch (error) {
+      return {
+        ok: false as const,
+        messsage: error instanceof Error ? error.message : "Unkown error",
+      };
+    }
+  },
+});
\ No newline at end of file
diff --git a/src/mastra/tools/GetFilePath.ts b/src/mastra/tools/GetFilePath.ts
new file mode 100644
index 0000000..2d69626
--- /dev/null
+++ b/src/mastra/tools/GetFilePath.ts
@@ -0,0 +1,35 @@
+import { octokit } from "../../utils/octokit";
+import { Tool } from "@mastra/core/tools";
+import { z } from "zod";
+
+export const getFilePaths = new Tool({
+  id: "getFilePaths",
+  description: "Get all file paths from the GitHub repository",
+  inputSchema: z.object({
+    owner: z
+      .string()
+      .describe("The owner of the repository. As facebook in facebook/react"),
+    repo: z
+      .string()
+      .describe("The name of the repository. As react in facebook/react"),
+    tree_sha: z
+      .string()
+      .describe("SHA or branch to start listing commits from")
+      .default("main"),
+  }),
+  outputSchema: z.array(z.string()),
+  execute: async ({ context }) => {
+    const { owner, repo, tree_sha } = context;
+
+    const getTreeResponse = await octokit.request("GET /repos/{owner}/{repo}/git/trees/{tree_sha}", {
+      owner,
+      repo,
+      recursive: "true",
+      tree_sha,
+    });
+
+    return getTreeResponse.data.tree
+      .map((file) => file.path)
+      .filter(Boolean) as string[];
+  },
+});
\ No newline at end of file
diff --git a/src/mastra/tools/GithubIssues.ts b/src/mastra/tools/GithubIssues.ts
new file mode 100644
index 0000000..6603847
--- /dev/null
+++ b/src/mastra/tools/GithubIssues.ts
@@ -0,0 +1,270 @@
+import { octokit } from "../../utils/octokit";
+import { Tool } from "@mastra/core/tools";
+import { z } from "zod";
+
+const inputSchema = z.object({
+  owner: z
+    .string()
+    .describe("The owner of the repository. As facebook in facebook/react"),
+  repo: z
+    .string()
+    .describe("The name of the repository. As react in facebook/react"),
+  state: z
+    .enum(["open", "closed", "all"])
+    .default("all")
+    .describe("The state of the issue"),
+  labels: z
+    .array(z.string())
+    .optional()
+    .describe("List of label names to filter issues by"),
+  assignee: z
+    .string()
+    .optional()
+    .describe("GitHub username of the assignee to filter issues by"),
+  creator: z
+    .string()
+    .optional()
+    .describe("GitHub username of the issue creator to filter by"),
+  page: z
+    .number()
+    .int()
+    .default(1)
+    .describe("The page number of the results to fetch"),
+  perPage: z
+    .number()
+    .int()
+    .max(100)
+    .default(30)
+    .describe("The number of results per page (max 100)"),
+});
+
+const outputSchema = z.union([
+  z.array(
+    z.object({
+      body: z.string().nullable().describe("The body content of the issue"),
+      number: z.number().int().describe("The issue number"),
+      state: z.enum(["open", "closed"]).describe("The state of the issue"),
+      title: z.string().describe("The title of the issue"),
+      url: z.string().url().describe("The url to the issue"),
+      labels: z
+        .array(
+          z.object({
+            name: z.string().describe("The name of the label"),
+            color: z.string().describe("The color of the label"),
+          }),
+        )
+        .describe("The labels attached to the issue"),
+      assignees: z
+        .array(
+          z.object({
+            avatarUrl: z
+              .string()
+              .url()
+              .nullable()
+              .describe("The url of the assignee's avatar"),
+            url: z
+              .string()
+              .url()
+              .nullable()
+              .describe("The url to the assignee's profile"),
+            username: z
+              .string()
+              .nullable()
+              .describe("The github username of the assignee"),
+          }),
+        )
+        .describe("The users assigned to the issue"),
+      user: z
+        .object({
+          avatarUrl: z
+            .string()
+            .url()
+            .nullable()
+            .describe("The url of the user's avatar"),
+          url: z
+            .string()
+            .url()
+            .nullable()
+            .describe("The url to the user's profile"),
+          username: z.string().nullable().describe("The github username"),
+        })
+        .describe("The user details of the issue creator"),
+      createdAt: z.string().describe("The creation date of the issue"),
+      updatedAt: z.string().describe("The last update date of the issue"),
+      closedAt: z
+        .string()
+        .nullable()
+        .describe("The closing date of the issue if closed"),
+    }),
+  ),
+  z.object({
+    ok: z.literal(false),
+    message: z.string().describe("Error message"),
+  }),
+]);
+
+export const getRepositoryIssues = new Tool({
+  id: "getRepositoryIssues",
+  description: "Get issues (excluding pull requests) for a repository",
+  inputSchema,
+  outputSchema,
+  execute: async ({ context }) => {
+    const {
+      assignee,
+      creator,
+      labels,
+      owner,
+      page,
+      perPage: per_page,
+      repo,
+      state,
+    } = context;
+
+    try {
+      const response = await octokit.request("GET /repos/{owner}/{repo}/issues", {
+        owner,
+        repo,
+        state,
+        labels: labels?.join(","),
+        assignee,
+        creator,
+        page,
+        per_page,
+      });
+
+      // Filter out pull requests and map the response to match our schema
+      const issues = response.data
+        .filter((issue) => !issue.pull_request)
+        .map((issue) => ({
+          body: issue.body ?? null,
+          number: issue.number,
+          state: issue.state as "open" | "closed",
+          title: issue.title,
+          url: issue.html_url,
+          labels: issue.labels.map((label) => ({
+            name: typeof label === "object" ? (label.name as string) || "" : "",
+            color:
+              typeof label === "object" ? (label.color as string) || "" : "",
+          })),
+          assignees:
+            issue.assignees?.map((assignee) => ({
+              url: assignee.html_url ?? null,
+              avatarUrl: assignee.avatar_url ?? null,
+              username: assignee.login ?? null,
+            })) || [],
+          user: {
+            url: issue.user?.html_url ?? null,
+            avatarUrl: issue.user?.avatar_url ?? null,
+            username: issue.user?.login ?? null,
+          },
+          createdAt: issue.created_at,
+          updatedAt: issue.updated_at,
+          closedAt: issue.closed_at ?? null,
+        }));
+      return issues;
+    } catch (error) {
+      console.error("Error fetching issues:", error);
+      return {
+        ok: false as const,
+        message:
+          error instanceof Error ? error.message : "Failed to fetch issues",
+      };
+    }
+  },
+});
+
+export const createIssue = new Tool({
+  id: "createIssue",
+  description: "Create a new issue in a repository",
+  inputSchema: z.object({
+    owner: z.string().describe("The owner of the repository"),
+    repo: z.string().describe("The name of the repository"),
+    title: z.string().describe("The title of the issue"),
+    body: z.string().optional().describe("The body content of the issue"),
+    assignees: z.array(z.string()).optional().describe("Usernames to assign the issue to"),
+    labels: z.array(z.string()).optional().describe("Labels to assign to the issue"),
+  }),
+  outputSchema: z.union([
+    z.object({ ok: z.literal(true), url: z.string().url() }),
+    z.object({ ok: z.literal(false), message: z.string() }),
+  ]),
+  execute: async ({ context }) => {
+    const { owner, repo, title, body, assignees, labels } = context;
+    try {
+      const response = await octokit.request("POST /repos/{owner}/{repo}/issues", {
+        owner,
+        repo,
+        title,
+        body,
+        assignees,
+        labels,
+      });
+      return { ok: true as const, url: response.data.html_url };
+    } catch (error) {
+      return { ok: false as const, message: error instanceof Error ? error.message : "Unknown error" };
+    }
+  },
+});
+
+export const summarizeIssues = new Tool({
+  id: "summarizeIssues",
+  description: "Summarize issues for a repository, returning counts and a summary string.",
+  inputSchema: z.object({
+    owner: z.string().describe("The owner of the repository"),
+    repo: z.string().describe("The name of the repository"),
+  }),
+  outputSchema: z.object({
+    openCount: z.number(),
+    closedCount: z.number(),
+    totalCount: z.number(),
+    summary: z.string(),
+  }),
+  execute: async ({ context }) => {
+    const { owner, repo } = context;
+    try {
+      const response = await octokit.request("GET /repos/{owner}/{repo}/issues", {
+        owner,
+        repo,
+        state: "all",
+        per_page: 100,
+      });
+      const issues = response.data.filter((issue) => !issue.pull_request);
+      const openCount = issues.filter((i) => i.state === "open").length;
+      const closedCount = issues.filter((i) => i.state === "closed").length;
+      const totalCount = issues.length;
+      const summary = `There are ${openCount} open and ${closedCount} closed issues (total: ${totalCount}).`;
+      return { openCount, closedCount, totalCount, summary };
+    } catch (error) {
+      return { openCount: 0, closedCount: 0, totalCount: 0, summary: error instanceof Error ? error.message : "Unknown error" };
+    }
+  },
+}); 
+
+export const commentOnIssue = new Tool({
+  id: "commentOnIssue",
+  description: "Add a comment to an issue in a repository",
+  inputSchema: z.object({
+    owner: z.string().describe("The owner of the repository"),
+    repo: z.string().describe("The name of the repository"),
+    issue_number: z.number().describe("The number of the issue"),
+    body: z.string().describe("The comment body"),
+  }),
+  outputSchema: z.union([
+    z.object({ ok: z.literal(true), url: z.string().url() }),
+    z.object({ ok: z.literal(false), message: z.string() }),
+  ]),
+  execute: async ({ context }) => {
+    const { owner, repo, issue_number, body } = context;
+    try {
+      const response = await octokit.request("POST /repos/{owner}/{repo}/issues/{issue_number}/comments", {
+        owner,
+        repo,
+        issue_number,
+        body,
+      });
+      return { ok: true as const, url: response.data.html_url };
+    } catch (error) {
+      return { ok: false as const, message: error instanceof Error ? error.message : "Unknown error" };
+    }
+  },
+});
\ No newline at end of file
diff --git a/src/mastra/tools/GithubPR.ts b/src/mastra/tools/GithubPR.ts
new file mode 100644
index 0000000..a802f85
--- /dev/null
+++ b/src/mastra/tools/GithubPR.ts
@@ -0,0 +1,226 @@
+import { octokit } from "../../utils/octokit";
+import { Tool } from "@mastra/core/tools";
+import { z } from "zod";
+
+const inputSchema = z.object({
+  owner: z
+    .string()
+    .describe("The owner of the repository. As facebook in facebook/react"),
+  repo: z
+    .string()
+    .describe("The name of the repository. As react in facebook/react"),
+  state: z
+    .enum(["open", "closed", "all"])
+    .default("all")
+    .describe("The state of the pull request"),
+  page: z
+    .number()
+    .int()
+    .default(1)
+    .describe("The page number of the results to fetch."),
+  perPage: z
+    .number()
+    .int()
+    .max(100)
+    .default(30)
+    .describe("The number of results per page (max 100)."),
+});
+
+const outputSchema = z.union([
+  z.array(
+    z.object({
+      body: z.string().nullable().describe("The body content of the pr"),
+      number: z.number().int().describe("The pull request number"),
+      state: z
+        .enum(["open", "closed"])
+        .describe("The state of the pull request"),
+      title: z.string().describe("The title of the pull request"),
+      url: z.string().url().describe("The url to the pull request"),
+      user: z
+        .object({
+          avatarUrl: z
+            .string()
+            .url()
+            .nullable()
+            .describe("The url of the user"),
+          url: z.string().url().nullable().describe("The url to the user"),
+          username: z.string().nullable().describe("The github user name"),
+        })
+        .describe("The user details"),
+    }),
+  ),
+  z.object({
+    ok: z.literal(false),
+    message: z.string().describe("Error message"),
+  }),
+]);
+
+export const getRepositoryPullRequests = new Tool({
+  id: "getRepositoryPullRequests",
+  description: "Get pull requests for a repository",
+  inputSchema,
+  outputSchema,
+  execute: async ({ context }) => {
+    const { owner, page, perPage: per_page, repo, state } = context;
+
+    try {
+      const response = await octokit.request("GET /repos/{owner}/{repo}/pulls", {
+        owner,
+        repo,
+        state,
+        page,
+        per_page,
+      });
+
+      const pullRequests = response.data.map((r) => ({
+        body: r.body ?? null,
+        number: r.number,
+        state: r.state as "open" | "closed",
+        title: r.title,
+        url: r.html_url,
+        user: {
+          url: r.user?.html_url ?? null,
+          avatarUrl: r.user?.avatar_url ?? null,
+          username: r.user?.login ?? null,
+        },
+      }));
+
+      return pullRequests;
+    } catch (error) {
+      console.error("Error fetching pull requests:", error);
+      return {
+        ok: false as const,
+        message:
+          error instanceof Error
+            ? error.message
+            : "Failed to fetch pull requests",
+      };
+    }
+  },
+});
+
+export const commentOnPullRequest = new Tool({
+  id: "commentOnPullRequest",
+  description: "Add a comment to a pull request in a repository",
+  inputSchema: z.object({
+    owner: z.string().describe("The owner of the repository"),
+    repo: z.string().describe("The name of the repository"),
+    pull_number: z.number().describe("The number of the pull request"),
+    body: z.string().describe("The comment body"),
+  }),
+  outputSchema: z.union([
+    z.object({ ok: z.literal(true), url: z.string().url() }),
+    z.object({ ok: z.literal(false), message: z.string() }),
+  ]),
+  execute: async ({ context }) => {
+    const { owner, repo, pull_number, body } = context;
+    try {
+      const response = await octokit.request("POST /repos/{owner}/{repo}/issues/{pull_number}/comments", {
+        owner,
+        repo,
+        issue_number: pull_number,
+        body,
+      });
+      return { ok: true as const, url: response.data.html_url };
+    } catch (error) {
+      return { ok: false as const, message: error instanceof Error ? error.message : "Unknown error" };
+    }
+  },
+});
+
+export const createPullRequest = new Tool({
+  id: "createPullRequest",
+  description: "Create a new pull request in a repository",
+  inputSchema: z.object({
+      owner: z.string().describe("The owner of the repository"),
+      repo: z.string().describe("The name of the repository"),
+      title: z.string().describe("The title of the pull request"),
+      head: z.string().describe("The name of the branch where changes are implemented"),
+      base: z.string().describe("The name of the branch you want the changes pulled into"),
+      body: z.string().optional().describe("The body content of the pull request"),
+  }),
+  outputSchema: z.union([
+      z.object({ ok: z.literal(true), url: z.string().url() }),
+      z.object({ ok: z.literal(false), message: z.string() }),
+  ]),
+  execute: async ({ context }) => {
+      const { owner, repo, title, head, base, body } = context;
+      try {
+          const response = await octokit.request("POST /repos/{owner}/{repo}/pulls", {
+              owner,
+              repo,
+              title,
+              head,
+              base,
+              body,
+          });
+          return { ok: true as const, url: response.data.html_url };
+      } catch (error) {
+          return { ok: false as const, message: error instanceof Error ? error.message : "Unknown error" };
+      }
+  },
+});
+
+export const reviewPullRequest = new Tool({
+  id: "reviewPullRequest",
+  description: "Submit a review for a pull request (approve, request changes, or comment)",
+  inputSchema: z.object({
+      owner: z.string().describe("The owner of the repository"),
+      repo: z.string().describe("The name of the repository"),
+      pull_number: z.number().describe("The number of the pull request"),
+      event: z.enum(["APPROVE", "REQUEST_CHANGES", "COMMENT"]).describe("The type of review event"),
+      body: z.string().optional().describe("The body of the review comment"),
+  }),
+  outputSchema: z.union([
+      z.object({ ok: z.literal(true), url: z.string().url() }),
+      z.object({ ok: z.literal(false), message: z.string() }),
+  ]),
+  execute: async ({ context }) => {
+      const { owner, repo, pull_number, event, body } = context;
+      try {
+          const response = await octokit.request("POST /repos/{owner}/{repo}/pulls/{pull_number}/reviews", {
+              owner,
+              repo,
+              pull_number,
+              event,
+              body,
+          });
+          return { ok: true as const, url: response.data.html_url };
+      } catch (error) {
+          return { ok: false as const, message: error instanceof Error ? error.message : "Unknown error" };
+      }
+  },
+});
+
+export const summarizePullRequests = new Tool({
+  id: "summarizePullRequests",
+  description: "Summarize pull requests for a repository, returning counts and a summary string.",
+  inputSchema: z.object({
+      owner: z.string().describe("The owner of the repository"),
+      repo: z.string().describe("The name of the repository"),
+  }),
+  outputSchema: z.object({
+      openCount: z.number(),
+      closedCount: z.number(),
+      totalCount: z.number(),
+      summary: z.string(),
+  }),
+  execute: async ({ context }) => {
+      const { owner, repo } = context;
+      try {
+          const response = await octokit.request("GET /repos/{owner}/{repo}/pulls", {
+              owner,
+              repo,
+              state: "all",
+              per_page: 100,
+          });
+          const openCount = response.data.filter((pr) => pr.state === "open").length;
+          const closedCount = response.data.filter((pr) => pr.state === "closed").length;
+          const totalCount = response.data.length;
+          const summary = `There are ${openCount} open and ${closedCount} closed pull requests (total: ${totalCount}).`;
+          return { openCount, closedCount, totalCount, summary };
+      } catch (error) {
+          return { openCount: 0, closedCount: 0, totalCount: 0, summary: error instanceof Error ? error.message : "Unknown error" };
+      }
+  },
+});
\ No newline at end of file
diff --git a/src/mastra/tools/GithubRepo.ts b/src/mastra/tools/GithubRepo.ts
new file mode 100644
index 0000000..0471b88
--- /dev/null
+++ b/src/mastra/tools/GithubRepo.ts
@@ -0,0 +1,160 @@
+import { octokit } from "../../utils/octokit";
+import { Tool } from "@mastra/core/tools";
+import { z } from "zod";
+
+const inputSchema = z.object({
+  owner: z
+    .string()
+    .describe("The owner of the repository. As facebook in facebook/react"),
+  repo: z
+    .string()
+    .describe("The name of the repository. As react in facebook/react"),
+});
+
+const outputSchema = z.union([
+  z
+    .object({
+      ok: z.literal(true),
+      commits: z
+        .array(
+          z.object({
+            sha: z.string().describe("The SHA hash of the commit"),
+            message: z.string().describe("The commit message"),
+            date: z
+              .string()
+              .datetime()
+              .nullish()
+              .describe("The date of the commit"),
+            url: z.string().url().describe("The URL to view the commit"),
+            author: z
+              .object({
+                name: z.string().describe("The name of the author"),
+                email: z.string().describe("The email of the author"),
+                username: z
+                  .string()
+                  .optional()
+                  .describe("The GitHub username of the author"),
+                avatarUrl: z
+                  .string()
+                  .url()
+                  .optional()
+                  .describe("The URL of the author's avatar"),
+                url: z
+                  .string()
+                  .url()
+                  .optional()
+                  .describe("The URL of the author's profile"),
+              })
+              .describe("The commit author information"),
+            verified: z
+              .boolean()
+              .describe("Whether the commit signature is verified"),
+          }),
+        )
+        .describe("List of commits"),
+    })
+    .describe("The success object"),
+  z
+    .object({
+      ok: z.literal(false),
+      message: z.string(),
+    })
+    .describe("The error/failed object"),
+]);
+
+export const getRepositoryCommits = new Tool({
+  id: "getRepositoryCommits",
+  description: "List commits for a repository with optional filtering",
+  inputSchema,
+  outputSchema,
+  execute: async ({ context }) => {
+    const { repo, owner } = context;
+
+    try {
+      const response = await octokit.request("GET /repos/{owner}/{repo}/commits", {
+        owner,
+        repo,
+      });
+
+
+      return {
+        ok: true as const,
+        commits: response.data.map((commit) => ({
+          sha: commit.sha,
+          message: commit.commit.message,
+          date: commit.commit.author?.date || commit.commit.committer?.date,
+          url: commit.html_url,
+          author: {
+            name:
+              commit.commit.author?.name ||
+              commit.commit.committer?.name ||
+              "Unknown",
+            email:
+              commit.commit.author?.email ||
+              commit.commit.committer?.email ||
+              "unknown",
+            username: commit.author?.login || commit.committer?.login,
+            avatarUrl:
+              commit.author?.avatar_url || commit.committer?.avatar_url,
+            url: commit.author?.html_url || commit.committer?.html_url,
+          },
+          verified: commit.commit.verification?.verified || false,
+        })),
+      };
+    } catch (error) {
+      return {
+        ok: false as const,
+        message: error instanceof Error ? error.message : "Unkown error",
+      };
+    }
+  },
+});
+
+export const createRepository = new Tool({
+  id: "createRepository",
+  description: "Create a new repository for the authenticated user",
+  inputSchema: z.object({
+    name: z.string().describe("The name of the new repository"),
+    description: z.string().optional().describe("Description of the repository"),
+    private: z.boolean().optional().describe("Whether the repository is private"),
+  }),
+  outputSchema: z.union([
+    z.object({ ok: z.literal(true), url: z.string().url() }),
+    z.object({ ok: z.literal(false), message: z.string() }),
+  ]),
+  execute: async ({ context }) => {
+    const { name, description, private: isPrivate } = context;
+    try {
+      const response = await octokit.request("POST /user/repos", {
+        name,
+        description,
+        private: isPrivate,
+      });
+      return { ok: true as const, url: response.data.html_url };
+    } catch (error) {
+      return { ok: false as const, message: error instanceof Error ? error.message : "Unknown error" };
+    }
+  },
+});
+
+export const forkRepository = new Tool({
+  id: "forkRepository",
+  description: "Fork a repository to the authenticated user's account",
+  inputSchema: z.object({
+    owner: z.string().describe("The owner of the repository to fork"),
+    repo: z.string().describe("The name of the repository to fork"),
+  }),
+  outputSchema: z.union([
+    z.object({ ok: z.literal(true), url: z.string().url() }),
+    z.object({ ok: z.literal(false), message: z.string() }),
+  ]),
+  execute: async ({ context }) => {
+    const { owner, repo } = context;
+    try {
+      const response = await octokit.request("POST /repos/{owner}/{repo}/forks", { owner, repo });
+      return { ok: true as const, url: response.data.html_url };
+    } catch (error) {
+      return { ok: false as const, message: error instanceof Error ? error.message : "Unknown error" };
+    }
+  },
+});
\ No newline at end of file
diff --git a/src/mastra/tools/GithubUser.ts b/src/mastra/tools/GithubUser.ts
new file mode 100644
index 0000000..cca1d39
--- /dev/null
+++ b/src/mastra/tools/GithubUser.ts
@@ -0,0 +1,333 @@
+import { createTool } from '@mastra/core/tools';
+import { z } from 'zod';
+import { octokit } from "../../utils/octokit";
+
+interface GitHubResponse {
+    ok: true;
+    username: string;
+    followers: number;
+    following: number;
+    public_repos: number;
+    bio: string;
+    email: string;
+    location: string;
+    company: string;
+    website: string;
+    twitter_username: string;
+    avatar_url: string;
+    url: string;
+    blog: string;
+    repositories: {
+        name: string;
+        description: string;
+        url: string;
+    }[];
+}
+
+// Enhanced contribution activity interface
+interface ContributionActivity {
+    issues: Array<{
+        title: string;
+        url: string;
+        createdAt: string;
+        state: string;
+        repositoryName?: string;
+    }>;
+    pullRequests: Array<{
+        title: string;
+        url: string;
+        createdAt: string;
+        state: string;
+        repositoryName?: string;
+        merged?: boolean;
+    }>;
+    commits: Array<{
+        message: string;
+        url: string;
+        date: string;
+        repositoryName?: string;
+        sha?: string;
+    }>;
+    repositories: Array<{
+        name: string;
+        description: string;
+        url: string;
+        createdAt: string;
+        language?: string;
+        stars: number;
+        forks: number;
+    }>;
+    summary: {
+        totalCommits: number;
+        totalPullRequests: number;
+        totalIssues: number;
+        totalRepositories: number;
+        period: string;
+    };
+}
+
+/**
+ * Get detailed GitHub user contribution activity for a specified period
+ */
+export async function getUserContributionActivity(username: string, period: '7d' | '14d' | '30d' = '7d'): Promise<ContributionActivity> {
+    const now = new Date();
+    let since: Date;
+    
+    if (period === '7d') since = new Date(now.getTime() - 7 * 24 * 60 * 60 * 1000);
+    else if (period === '14d') since = new Date(now.getTime() - 14 * 24 * 60 * 60 * 1000);
+    else since = new Date(now.getTime() - 30 * 24 * 60 * 60 * 1000);
+    
+    const sinceIso = since.toISOString();
+    
+    try {
+        // Fetch issues created by user
+        const issuesResponse = await octokit.request('GET /search/issues', {
+            q: `type:issue author:${username} created:>${sinceIso}`,
+            sort: 'created',
+            order: 'desc',
+            per_page: 100
+        });
+
+        const issues = issuesResponse.data.items.map(issue => ({
+            title: issue.title,
+            url: issue.html_url,
+            createdAt: issue.created_at,
+            state: issue.state,
+            repositoryName: issue.repository_url?.split('/').pop() || ''
+        }));
+
+        // Fetch pull requests created by user
+        const prsResponse = await octokit.request('GET /search/issues', {
+            q: `type:pr author:${username} created:>${sinceIso}`,
+            sort: 'created',
+            order: 'desc',
+            per_page: 100
+        });
+
+        const pullRequests = prsResponse.data.items.map(pr => ({
+            title: pr.title,
+            url: pr.html_url,
+            createdAt: pr.created_at,
+            state: pr.state,
+            repositoryName: pr.repository_url?.split('/').pop() || '',
+            merged: pr.pull_request?.merged_at ? true : false
+        }));
+
+        // Fetch user's repositories created in the period
+        const reposResponse = await octokit.request('GET /users/{username}/repos', { 
+            username,
+            sort: 'created',
+            direction: 'desc',
+            per_page: 100
+        });
+
+        const repositories = reposResponse.data
+            .filter(repo => new Date(repo.created_at || '') > since)
+            .map(repo => ({
+                name: repo.name,
+                description: repo.description || '',
+                url: repo.html_url,
+                createdAt: repo.created_at || '',
+                language: repo.language || '',
+                stars: repo.stargazers_count || 0,
+                forks: repo.forks_count || 0
+            }));
+
+        // Fetch recent commits from user's events
+        const eventsResponse = await octokit.request('GET /users/{username}/events', { 
+            username,
+            per_page: 100
+        });
+
+        const commits: ContributionActivity['commits'] = [];
+        for (const event of eventsResponse.data) {
+            if (event.type === 'PushEvent' && new Date(event.created_at || '') > since) {
+                const pushPayload = event.payload as any;
+                if (pushPayload.commits) {
+                    for (const commit of pushPayload.commits) {
+                        commits.push({
+                            message: commit.message,
+                            url: `https://github.com/${event.repo?.name}/commit/${commit.sha}`,
+                            date: event.created_at || '',
+                            repositoryName: event.repo?.name?.split('/').pop() || '',
+                            sha: commit.sha
+                        });
+                    }
+                }
+            }
+        }
+
+        return {
+            issues,
+            pullRequests,
+            commits,
+            repositories,
+            summary: {
+                totalCommits: commits.length,
+                totalPullRequests: pullRequests.length,
+                totalIssues: issues.length,
+                totalRepositories: repositories.length,
+                period
+            }
+        };
+    } catch (error) {
+        throw new Error(`Failed to fetch contribution activity for ${username}: ${error}`);
+    }
+}
+
+export async function getGitHubUser(username: string): Promise<GitHubResponse> {
+    try {
+        const { data } = await octokit.request('GET /users/{username}', {
+            username
+        });
+
+        const repositories = await octokit.request('GET /users/{username}/repos', {
+            username
+        });
+        return {
+            ok: true,
+            username: data.login,
+            followers: data.followers,
+            following: data.following,
+            public_repos: data.public_repos,
+            bio: data.bio || '',
+            email: data.email || '',
+            location: data.location || '',
+            company: data.company || '',
+            website: data.blog || '',
+            twitter_username: data.twitter_username || '',
+            avatar_url: data.avatar_url,
+            url: data.url,
+            blog: data.blog || '',
+            repositories: repositories.data.map(repo => ({
+                name: repo.name,
+                description: repo.description || '',
+                url: repo.html_url,
+            })),
+        };
+    } catch (error) {
+        throw new Error(`Failed to fetch GitHub user information for ${username}: ${error}`);
+    }
+}
+
+// ------------------- SCHEMAS -------------------
+
+// Input Schemas
+export const githubUserInputSchema = z.object({
+    username: z.string().describe('GitHub username'),
+});
+
+export const contributionActivityInputSchema = z.object({
+    username: z.string().describe('GitHub username'),
+    period: z.enum(['7d', '14d', '30d']).default('7d').describe('Time period for activity'),
+});
+
+// Output Schemas
+export const githubUserOutputSchema = z.union([
+    z.object({
+        ok: z.literal(true),
+        username: z.string(),
+        followers: z.number(),
+        following: z.number(),
+        public_repos: z.number(),
+        bio: z.string(),
+        email: z.string(),
+        location: z.string(),
+        company: z.string(),
+        website: z.string(),
+        twitter_username: z.string(),
+        avatar_url: z.string(),
+        url: z.string(),
+        blog: z.string(),
+        repositories: z.array(z.object({
+            name: z.string(),
+            description: z.string(),
+            url: z.string(),
+        })),
+    }),
+    z.object({
+        ok: z.literal(false),
+        message: z.string(),
+    }),
+]);
+
+export const contributionActivitySummaryOutputSchema = z.object({
+    commits: z.number(),
+    pullRequests: z.number(),
+    issues: z.number(),
+    reposCreated: z.number(),
+    forks: z.number(),
+    summary: z.string(),
+});
+
+export const contributionActivityFullOutputSchema = z.object({
+    issues: z.array(z.object({
+        title: z.string(),
+        url: z.string(),
+        createdAt: z.string(),
+        state: z.string(),
+        repositoryName: z.string().optional(),
+    })),
+    pullRequests: z.array(z.object({
+        title: z.string(),
+        url: z.string(),
+        createdAt: z.string(),
+        state: z.string(),
+        repositoryName: z.string().optional(),
+        merged: z.boolean().optional(),
+    })),
+    commits: z.array(z.object({
+        message: z.string(),
+        url: z.string(),
+        date: z.string(),
+        repositoryName: z.string().optional(),
+        sha: z.string().optional(),
+    })),
+    repositories: z.array(z.object({
+        name: z.string(),
+        description: z.string(),
+        url: z.string(),
+        createdAt: z.string(),
+        language: z.string().optional(),
+        stars: z.number(),
+        forks: z.number(),
+    })),
+    summary: z.object({
+        totalCommits: z.number(),
+        totalPullRequests: z.number(),
+        totalIssues: z.number(),
+        totalRepositories: z.number(),
+        period: z.string(),
+    })
+});
+
+// ------------------- TOOLS -------------------
+
+export const getGitHubUserTool = createTool({
+    id: 'getGitHubUser',
+    description: 'Get GitHub user information',
+    inputSchema: githubUserInputSchema,
+    outputSchema: githubUserOutputSchema,
+    execute: async ({ context }) => {
+        return await getGitHubUser(context.username);
+    }
+});
+
+export const getUserContributionActivityTool = createTool({
+    id: 'getUserContributionActivity',
+    description: 'Fetch user contribution activity summary (commits, PRs, issues, repos, forks) for a given time period (7d, 14d, 30d)',
+    inputSchema: contributionActivityInputSchema,
+    outputSchema: contributionActivitySummaryOutputSchema,
+    execute: async ({ context }) => {
+        const { username, period } = context;
+        const activity = await getUserContributionActivity(username, period);
+        return {
+            commits: activity.summary.totalCommits,
+            pullRequests: activity.summary.totalPullRequests,
+            issues: activity.summary.totalIssues,
+            reposCreated: activity.summary.totalRepositories,
+            forks: activity.repositories.filter(r => r.forks > 0).length,
+            summary: `In the last ${period}, ${username} made ${activity.summary.totalCommits} commits, opened ${activity.summary.totalPullRequests} PRs, ${activity.summary.totalIssues} issues, created ${activity.summary.totalRepositories} repos.`
+        };
+    }
+});
\ No newline at end of file
diff --git a/src/mastra/workflows/create-issues.ts b/src/mastra/workflows/create-issues.ts
new file mode 100644
index 0000000..65367da
--- /dev/null
+++ b/src/mastra/workflows/create-issues.ts
@@ -0,0 +1,175 @@
+import { createWorkflow, createStep } from "@mastra/core/workflows";
+import { z } from "zod";
+import { createIssue } from "../tools/GithubIssues";
+import { getFileContent } from "../tools/GetFile";
+
+// Types for file and problem
+const FileSchema = z.object({ path: z.string(), content: z.string().optional() });
+const ProblemSchema = z.object({ path: z.string(), line: z.number(), code: z.string(), suggestion: z.string() });
+const IssueSchema = z.object({ url: z.string(), title: z.string(), body: z.string() });
+
+// Step 1: Fetch files and contents
+const fetchFilesStep = createStep({
+  id: "fetch-files",
+  description: "Fetch all file paths and contents from the repo (first 10 files for demo).",
+  inputSchema: z.object({
+    owner: z.string(),
+    repo: z.string(),
+    tree_sha: z.string().optional().default("main"),
+  }),
+  outputSchema: z.object({
+    files: z.array(FileSchema),
+    owner: z.string(),
+    repo: z.string(),
+  }),
+  async execute({ inputData, runtimeContext }) {
+    const { getFilePaths } = await import("../tools/GetFilePath");
+    const { getFileContent } = await import("../tools/GetFile");
+    const { owner, repo, tree_sha } = inputData;
+    if (!getFilePaths?.execute) throw new Error("getFilePaths tool not found");
+    if (!getFileContent?.execute) throw new Error("getFileContent tool not found");
+    const getFileContentExec = getFileContent.execute;
+    const filePaths: string[] = await getFilePaths.execute({ context: { owner, repo, tree_sha: tree_sha || "main" }, runtimeContext });
+    const filesToFetch = filePaths.filter((p: string) => !p.endsWith("/")).slice(0, 10);
+    const files: Array<{ path: string; content?: string }> = await Promise.all(
+      filesToFetch.map(async (path: string) => {
+        const res = await getFileContentExec({ context: { owner, repo, path }, runtimeContext });
+        return { path, content: (res as any).ok ? (res as any).content : undefined };
+      })
+    );
+    return { files, owner, repo };
+  },
+});
+
+// Step 2: Analyze faulty code
+const analyzeFaultyCodeStep = createStep({
+  id: "analyze-faulty-code",
+  description: "Analyze files for TODO, FIXME, console.log, or any.",
+  inputSchema: z.object({
+    files: z.array(FileSchema),
+    owner: z.string(),
+    repo: z.string(),
+    tree_sha: z.string().optional().default("main"),
+  }),
+  outputSchema: z.object({
+    problems: z.array(ProblemSchema),
+    owner: z.string(),
+    repo: z.string(),
+    tree_sha: z.string().optional().default("main"),
+  }),
+  async execute({ inputData }) {
+    const problems: Array<{ path: string; line: number; code: string; suggestion: string }> = [];
+    for (const file of inputData.files) {
+      if (!file.content) continue;
+      const lines = file.content.split("\n");
+      lines.forEach((line, idx) => {
+        if (/TODO|FIXME|console\.log|any /.test(line)) {
+          problems.push({
+            path: file.path,
+            line: idx + 1,
+            code: line.trim(),
+            suggestion: line.includes('TODO') || line.includes('FIXME')
+              ? 'Resolve the TODO/FIXME with proper implementation.'
+              : line.includes('console.log')
+              ? 'Remove debug statements before production.'
+              : line.includes('any ')
+              ? 'Replace `any` with a specific type.'
+              : 'Review this line for best practices.',
+          });
+        }
+      });
+    }
+    return {
+      problems,
+      owner: inputData.owner,
+      repo: inputData.repo,
+      tree_sha: inputData.tree_sha ?? "main",
+    };
+  },
+});
+
+// Step 3: Create issues for problems
+const createIssuesForProblemsStep = createStep({
+  id: "create-issues-for-problems",
+  description: "Create a GitHub issue for each faulty code snippet.",
+  inputSchema: z.object({
+    owner: z.string(),
+    repo: z.string(),
+    tree_sha: z.string().optional().default("main"),
+    problems: z.array(ProblemSchema),
+  }),
+  outputSchema: z.object({
+    issues: z.array(IssueSchema),
+    owner: z.string(),
+    repo: z.string(),
+    tree_sha: z.string().optional().default("main"),
+  }),
+  async execute({ inputData, runtimeContext }) {
+    if (!createIssue || !createIssue.execute) throw new Error("createIssue tool not found");
+    const { owner, repo, problems, tree_sha } = inputData;
+    const issues: Array<{ url: string; title: string; body: string }> = [];
+    for (const problem of problems) {
+      const title = `Code Issue in ${problem.path} at line ${problem.line}`;
+      const body = `**File:** ${problem.path}\n**Line:** ${problem.line}\n\n**Problem:**\n\n${problem.code}\n\n**Suggestion:**\n${problem.suggestion}`;
+      const res = await createIssue.execute({ context: { owner, repo, title, body }, runtimeContext });
+      if (res && (res as any).ok) {
+        issues.push({ url: (res as any).url, title, body });
+      }
+    }
+    return { issues, owner, repo, tree_sha };
+  },
+});
+
+// Step 4: Generate summary
+const generateSummaryStep = createStep({
+  id: "generate-summary",
+  description: "Summarize the issues created.",
+  inputSchema: z.object({
+    issues: z.array(IssueSchema),
+    owner: z.string(),
+    repo: z.string(),
+    tree_sha: z.string().optional().default("main"),
+  }),
+  outputSchema: z.object({ summary: z.string(), issues: z.array(IssueSchema) }),
+  async execute({ inputData }) {
+    const summary = `Created ${inputData.issues.length} issues for faulty code found during review.`;
+    return { summary, issues: inputData.issues };
+  },
+});
+
+export const createAddressIssueFromReview = createWorkflow({
+  id: "create-address-issue-from-review",
+  description: "Run a code review, highlight faulty code, and create an issue with a suggested fix.",
+  inputSchema: z.object({
+    owner: z.string(),
+    repo: z.string(),
+    tree_sha: z.string().optional().default("main"),
+  }),
+  outputSchema: z.object({
+    summary: z.string(),
+    issues: z.array(IssueSchema),
+  }),
+})
+  .then(fetchFilesStep)
+  .map(({ inputData, getInitData }) => ({
+    files: inputData.files,
+    owner: getInitData().owner,
+    repo: getInitData().repo,
+    tree_sha: getInitData().tree_sha ?? "main",
+  }))
+  .then(analyzeFaultyCodeStep)
+  .map(({ inputData, getInitData }) => ({
+    owner: inputData.owner,
+    repo: inputData.repo,
+    tree_sha: inputData.tree_sha ?? "main",
+    problems: inputData.problems,
+  }))
+  .then(createIssuesForProblemsStep)
+  .map(({ inputData, getInitData }) => ({
+    issues: inputData.issues,
+    owner: inputData.owner,
+    repo: inputData.repo,
+    tree_sha: inputData.tree_sha ?? "main",
+  }))
+  .then(generateSummaryStep)
+  .commit();
diff --git a/src/mastra/workflows/gh-profile.ts b/src/mastra/workflows/gh-profile.ts
new file mode 100644
index 0000000..501de03
--- /dev/null
+++ b/src/mastra/workflows/gh-profile.ts
@@ -0,0 +1,31 @@
+// To use this workflow, import and register it in your Mastra entrypoint (e.g., src/mastra/index.ts):
+// import { contributionGithubWorkflow } from "./workflows/contribution";
+
+import { createWorkflow, createStep } from "@mastra/core/workflows";
+import { z } from "zod";
+import { getGitHubUser } from "../tools/GithubUser";
+
+// Input schema shared by all steps
+const inputSchema = z.object({
+  username: z.string(),
+});
+
+// Step: Fetch GitHub user profile
+const fetchProfileStep = createStep({
+  id: "fetchProfile",
+  inputSchema,
+  outputSchema: z.object({ profile: z.any() }),
+  execute: async ({ inputData }) => ({ profile: await getGitHubUser(inputData.username) }),
+});
+
+// Main workflow: fetch profile and contributions in parallel, then combine
+export const ghProfile = createWorkflow({
+  id: "gh-profile-workflow",
+  inputSchema,
+  outputSchema: z.object({
+    profile: z.any(),
+  }),
+})
+  .then(fetchProfileStep)
+  .map((result: { profile: any }) => ({ profile: result.profile }))
+  .commit();
diff --git a/src/mastra/workflows/github-user-analysis.ts b/src/mastra/workflows/github-user-analysis.ts
new file mode 100644
index 0000000..d1f5288
--- /dev/null
+++ b/src/mastra/workflows/github-user-analysis.ts
@@ -0,0 +1,21 @@
+// Alternative simplified single-step workflow for GitHub user analysis
+// To use this workflow, import and register it in your Mastra entrypoint (e.g., src/mastra/index.ts):
+// import { githubUserAnalysisWorkflow } from "./workflows/github-user-analysis";
+
+import { createWorkflow, createStep } from "@mastra/core/workflows";
+import { z } from "zod";
+import { getUserContributionActivityTool } from "../tools/GithubUser";
+
+const getUserContributionActivityStep = createStep(getUserContributionActivityTool);
+
+export const ContributionWorkflow = createWorkflow({
+  id: "ContributionWorkflow",
+  description: "Fetch GitHub user contribution activity summary (commits, PRs, issues, repos, forks) for a given time period (7d, 14d, 30d)",
+  inputSchema: z.object({
+    username: z.string().describe("The GitHub username to analyze"),
+    period: z.enum(["7d", "14d", "30d"]).default("7d").describe("Time period for contribution analysis"),
+  }),
+  outputSchema: getUserContributionActivityTool.outputSchema,
+})
+  .then(getUserContributionActivityStep)
+  .commit();
diff --git a/src/types/github.ts b/src/types/github.ts
new file mode 100644
index 0000000..2d6763d
--- /dev/null
+++ b/src/types/github.ts
@@ -0,0 +1,21 @@
+export interface GitHubResponse {
+    username: string;
+    repositories: {
+        name: string;
+        description: string;
+        url: string;
+    }[];
+    followers: number;
+    following: number;
+    public_repos: number;
+    bio: string;
+    location: string;
+    company: string;
+    website: string;
+    twitter_username: string;
+    avatar_url: string;
+    instagram_username: string;
+    blog: string;
+    youtube_username: string;
+}
+
diff --git a/src/utils/octokit.ts b/src/utils/octokit.ts
new file mode 100644
index 0000000..c1021dd
--- /dev/null
+++ b/src/utils/octokit.ts
@@ -0,0 +1,13 @@
+import { Octokit } from '@octokit/core';
+
+// Get GitHub token from environment variable
+const GITHUB_TOKEN = process.env.GITHUB_TOKEN;
+
+if (!GITHUB_TOKEN) {
+    throw new Error('GITHUB_TOKEN environment variable is not set');
+}
+
+// Create authenticated Octokit instance
+export const octokit = new Octokit({
+    auth: GITHUB_TOKEN,
+});
\ No newline at end of file

From eaf720d06ef03161a54f1f1931dc5e299c86bb69 Mon Sep 17 00:00:00 2001
From: yashksaini-coder <ys3853428@gmail.com>
Date: Tue, 8 Jul 2025 20:48:14 +0530
Subject: [PATCH 2/6] fix(docker): correct .env file reference in Dockerfile

---
 Dockerfile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Dockerfile b/Dockerfile
index fb1ee7c..0cc94dd 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -20,7 +20,7 @@ RUN apt-get update && apt-get install -y \
 WORKDIR /app
 
 # Copy package files
-COPY .env package.json pnpm-lock.yaml ./
+COPY .env.example package.json pnpm-lock.yaml ./
 
 # Install dependencies
 RUN pnpm install

From 6f0e203c27644eb8b3654c03cea88f451345f951 Mon Sep 17 00:00:00 2001
From: yashksaini-coder <ys3853428@gmail.com>
Date: Sat, 12 Jul 2025 18:59:02 +0530
Subject: [PATCH 3/6] refactor(weather-agent): remove unused weather agent and
 tool files; update Dockerfile and .dockerignore for environment file handling

---
 .dockerignore                                 |   3 +-
 .gitignore                                    |   1 +
 Dockerfile                                    |   2 +-
 .../agents/weather-agent/weather-agent.ts     |  26 ---
 .../agents/weather-agent/weather-tool.ts      | 102 ----------
 .../agents/weather-agent/weather-workflow.ts  | 192 ------------------
 src/mastra/agents/your-agent/your-agent.ts    |  29 ---
 src/mastra/agents/your-agent/your-tool.ts     |  22 --
 8 files changed, 3 insertions(+), 374 deletions(-)
 delete mode 100644 src/mastra/agents/weather-agent/weather-agent.ts
 delete mode 100644 src/mastra/agents/weather-agent/weather-tool.ts
 delete mode 100644 src/mastra/agents/weather-agent/weather-workflow.ts
 delete mode 100644 src/mastra/agents/your-agent/your-agent.ts
 delete mode 100644 src/mastra/agents/your-agent/your-tool.ts

diff --git a/.dockerignore b/.dockerignore
index 4559e2e..64e53c0 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -10,8 +10,7 @@ yarn-error.log*
 pnpm-debug.log*
 
 # Environment files
-.env.local
-.env.*.local
+.env
 
 # Build output
 .mastra/
diff --git a/.gitignore b/.gitignore
index 52a85c2..e222803 100644
--- a/.gitignore
+++ b/.gitignore
@@ -5,4 +5,5 @@ dist
 *.db
 *.db-*
 
+.env.local
 .env.example
\ No newline at end of file
diff --git a/Dockerfile b/Dockerfile
index 0cc94dd..2468877 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -20,7 +20,7 @@ RUN apt-get update && apt-get install -y \
 WORKDIR /app
 
 # Copy package files
-COPY .env.example package.json pnpm-lock.yaml ./
+COPY .env.local package.json pnpm-lock.yaml ./
 
 # Install dependencies
 RUN pnpm install
diff --git a/src/mastra/agents/weather-agent/weather-agent.ts b/src/mastra/agents/weather-agent/weather-agent.ts
deleted file mode 100644
index a9fba74..0000000
--- a/src/mastra/agents/weather-agent/weather-agent.ts
+++ /dev/null
@@ -1,26 +0,0 @@
-// This serves as an example, can be deleted later.
-
-import { Agent } from "@mastra/core/agent";
-import { weatherTool } from "../weather-agent/weather-tool";
-import { model } from "../../config";
-
-const name = "Weather Agent";
-const instructions = `
-      You are a helpful weather assistant that provides accurate weather information.
-
-      Your primary function is to help users get weather details for specific locations. When responding:
-      - Always ask for a location if none is provided
-      - If the location name isn’t in English, please translate it
-      - If giving a location with multiple parts (e.g. "New York, NY"), use the most relevant part (e.g. "New York")
-      - Include relevant details like humidity, wind conditions, and precipitation
-      - Keep responses concise but informative
-
-      Use the weatherTool to fetch current weather data.
-`;
-
-export const weatherAgent = new Agent({
-	name,
-	instructions,
-	model,
-	tools: { weatherTool },
-});
diff --git a/src/mastra/agents/weather-agent/weather-tool.ts b/src/mastra/agents/weather-agent/weather-tool.ts
deleted file mode 100644
index b36c807..0000000
--- a/src/mastra/agents/weather-agent/weather-tool.ts
+++ /dev/null
@@ -1,102 +0,0 @@
-import { createTool } from "@mastra/core/tools";
-import { z } from "zod";
-
-interface GeocodingResponse {
-	results: {
-		latitude: number;
-		longitude: number;
-		name: string;
-	}[];
-}
-interface WeatherResponse {
-	current: {
-		time: string;
-		temperature_2m: number;
-		apparent_temperature: number;
-		relative_humidity_2m: number;
-		wind_speed_10m: number;
-		wind_gusts_10m: number;
-		weather_code: number;
-	};
-}
-
-export const weatherTool = createTool({
-	id: "get-weather",
-	description: "Get current weather for a location",
-	inputSchema: z.object({
-		location: z.string().describe("City name"),
-	}),
-	outputSchema: z.object({
-		temperature: z.number(),
-		feelsLike: z.number(),
-		humidity: z.number(),
-		windSpeed: z.number(),
-		windGust: z.number(),
-		conditions: z.string(),
-		location: z.string(),
-	}),
-	execute: async ({ context }) => {
-		return await getWeather(context.location);
-	},
-});
-
-const getWeather = async (location: string) => {
-	const geocodingUrl = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(location)}&count=1`;
-	const geocodingResponse = await fetch(geocodingUrl);
-	const geocodingData = (await geocodingResponse.json()) as GeocodingResponse;
-
-	if (!geocodingData.results?.[0]) {
-		throw new Error(`Location '${location}' not found`);
-	}
-
-	const { latitude, longitude, name } = geocodingData.results[0];
-
-	const weatherUrl = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&current=temperature_2m,apparent_temperature,relative_humidity_2m,wind_speed_10m,wind_gusts_10m,weather_code`;
-
-	const response = await fetch(weatherUrl);
-	const data = (await response.json()) as WeatherResponse;
-
-	return {
-		temperature: data.current.temperature_2m,
-		feelsLike: data.current.apparent_temperature,
-		humidity: data.current.relative_humidity_2m,
-		windSpeed: data.current.wind_speed_10m,
-		windGust: data.current.wind_gusts_10m,
-		conditions: getWeatherCondition(data.current.weather_code),
-		location: name,
-	};
-};
-
-function getWeatherCondition(code: number): string {
-	const conditions: Record<number, string> = {
-		0: "Clear sky",
-		1: "Mainly clear",
-		2: "Partly cloudy",
-		3: "Overcast",
-		45: "Foggy",
-		48: "Depositing rime fog",
-		51: "Light drizzle",
-		53: "Moderate drizzle",
-		55: "Dense drizzle",
-		56: "Light freezing drizzle",
-		57: "Dense freezing drizzle",
-		61: "Slight rain",
-		63: "Moderate rain",
-		65: "Heavy rain",
-		66: "Light freezing rain",
-		67: "Heavy freezing rain",
-		71: "Slight snow fall",
-		73: "Moderate snow fall",
-		75: "Heavy snow fall",
-		77: "Snow grains",
-		80: "Slight rain showers",
-		81: "Moderate rain showers",
-		82: "Violent rain showers",
-		85: "Slight snow showers",
-		86: "Heavy snow showers",
-		95: "Thunderstorm",
-		96: "Thunderstorm with slight hail",
-		99: "Thunderstorm with heavy hail",
-	};
-	return conditions[code] || "Unknown";
-}
diff --git a/src/mastra/agents/weather-agent/weather-workflow.ts b/src/mastra/agents/weather-agent/weather-workflow.ts
deleted file mode 100644
index e27e9df..0000000
--- a/src/mastra/agents/weather-agent/weather-workflow.ts
+++ /dev/null
@@ -1,192 +0,0 @@
-// This files serves as an example, can be deleted later.
-import { Agent } from "@mastra/core/agent";
-import { createStep, createWorkflow } from "@mastra/core/workflows";
-import { z } from "zod";
-import { model } from "../../config";
-
-const agent = new Agent({
-  name: "Weather Agent",
-  model,
-  instructions: `
-        You are a local activities and travel expert who excels at weather-based planning. Analyze the weather data and provide practical activity recommendations.
-
-        For each day in the forecast, structure your response exactly as follows:
-
-        📅 [Day, Month Date, Year]
-        ═══════════════════════════
-
-        🌡️ WEATHER SUMMARY
-        • Conditions: [brief description]
-        • Temperature: [X°C/Y°F to A°C/B°F]
-        • Precipitation: [X% chance]
-
-        🌅 MORNING ACTIVITIES
-        Outdoor:
-        • [Activity Name] - [Brief description including specific location/route]
-          Best timing: [specific time range]
-          Note: [relevant weather consideration]
-
-        🌞 AFTERNOON ACTIVITIES
-        Outdoor:
-        • [Activity Name] - [Brief description including specific location/route]
-          Best timing: [specific time range]
-          Note: [relevant weather consideration]
-
-        🏠 INDOOR ALTERNATIVES
-        • [Activity Name] - [Brief description including specific venue]
-          Ideal for: [weather condition that would trigger this alternative]
-
-        ⚠️ SPECIAL CONSIDERATIONS
-        • [Any relevant weather warnings, UV index, wind conditions, etc.]
-
-        Guidelines:
-        - Suggest 2-3 time-specific outdoor activities per day
-        - Include 1-2 indoor backup options
-        - For precipitation >50%, lead with indoor activities
-        - All activities must be specific to the location
-        - Include specific venues, trails, or locations
-        - Consider activity intensity based on temperature
-        - Keep descriptions concise but informative
-
-        Maintain this exact formatting for consistency, using the emoji and section headers as shown.
-      `,
-});
-
-const forecastSchema = z.object({
-  date: z.string(),
-  maxTemp: z.number(),
-  minTemp: z.number(),
-  precipitationChance: z.number(),
-  condition: z.string(),
-  location: z.string(),
-});
-
-function getWeatherCondition(code: number): string {
-  const conditions: Record<number, string> = {
-    0: "Clear sky",
-    1: "Mainly clear",
-    2: "Partly cloudy",
-    3: "Overcast",
-    45: "Foggy",
-    48: "Depositing rime fog",
-    51: "Light drizzle",
-    53: "Moderate drizzle",
-    55: "Dense drizzle",
-    61: "Slight rain",
-    63: "Moderate rain",
-    65: "Heavy rain",
-    71: "Slight snow fall",
-    73: "Moderate snow fall",
-    75: "Heavy snow fall",
-    95: "Thunderstorm",
-  };
-  return conditions[code] || "Unknown";
-}
-
-const fetchWeather = createStep({
-  id: "fetch-weather",
-  description: "Fetches weather forecast for a given city",
-  inputSchema: z.object({
-    city: z.string().describe("The city to get the weather for"),
-  }),
-  outputSchema: forecastSchema,
-  execute: async ({ inputData }) => {
-    if (!inputData) {
-      throw new Error("Input data not found");
-    }
-
-    const geocodingUrl = `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(inputData.city)}&count=1`;
-    const geocodingResponse = await fetch(geocodingUrl);
-    const geocodingData = (await geocodingResponse.json()) as {
-      results: { latitude: number; longitude: number; name: string }[];
-    };
-
-    if (!geocodingData.results?.[0]) {
-      throw new Error(`Location '${inputData.city}' not found`);
-    }
-
-    const { latitude, longitude, name } = geocodingData.results[0];
-
-    const weatherUrl = `https://api.open-meteo.com/v1/forecast?latitude=${latitude}&longitude=${longitude}&current=precipitation,weathercode&timezone=auto,&hourly=precipitation_probability,temperature_2m`;
-    const response = await fetch(weatherUrl);
-    const data = (await response.json()) as {
-      current: {
-        time: string;
-        precipitation: number;
-        weathercode: number;
-      };
-      hourly: {
-        precipitation_probability: number[];
-        temperature_2m: number[];
-      };
-    };
-
-    const forecast = {
-      date: new Date().toISOString(),
-      maxTemp: Math.max(...data.hourly.temperature_2m),
-      minTemp: Math.min(...data.hourly.temperature_2m),
-      condition: getWeatherCondition(data.current.weathercode),
-      precipitationChance: data.hourly.precipitation_probability.reduce(
-        (acc, curr) => Math.max(acc, curr),
-        0,
-      ),
-      location: name,
-    };
-
-    return forecast;
-  },
-});
-
-const planActivities = createStep({
-  id: "plan-activities",
-  description: "Suggests activities based on weather conditions",
-  inputSchema: forecastSchema,
-  outputSchema: z.object({
-    activities: z.string(),
-  }),
-  execute: async ({ inputData }) => {
-    const forecast = inputData;
-
-    if (!forecast) {
-      throw new Error("Forecast data not found");
-    }
-
-    const prompt = `Based on the following weather forecast for ${forecast.location}, suggest appropriate activities:
-      ${JSON.stringify(forecast, null, 2)}
-      `;
-
-    const response = await agent.stream([
-      {
-        role: "user",
-        content: prompt,
-      },
-    ]);
-
-    let activitiesText = "";
-
-    for await (const chunk of response.textStream) {
-      process.stdout.write(chunk);
-      activitiesText += chunk;
-    }
-
-    return {
-      activities: activitiesText,
-    };
-  },
-});
-
-const weatherWorkflow = createWorkflow({
-  id: "weather-workflow",
-  inputSchema: z.object({
-    city: z.string().describe("The city to get the weather for"),
-  }),
-  outputSchema: z.object({
-    activities: z.string(),
-  }),
-})
-  .then(fetchWeather)
-  .then(planActivities);
-
-weatherWorkflow.commit();
-
-export { weatherWorkflow };
diff --git a/src/mastra/agents/your-agent/your-agent.ts b/src/mastra/agents/your-agent/your-agent.ts
deleted file mode 100644
index 0132a47..0000000
--- a/src/mastra/agents/your-agent/your-agent.ts
+++ /dev/null
@@ -1,29 +0,0 @@
-import { Agent } from "@mastra/core/agent";
-import { model } from "../../config";
-import { yourTool } from "../your-agent/your-tool";
-
-// Define Agent Name
-const name = "Your Agent";
-
-// Define instructions for the agent
-// TODO: Add link here for recommendations on how to properly define instructions for an agent.
-// TODO: Remove comments (// ...) from `instructions`
-const instructions = `
-      // Define the character of the agent.
-      You are a helpful assistant that provides accurate information.
-
-      // Define how the agent should behave here.
-      Your primary function is to help users get accurate details for specific topics. When responding:
-      - If the location name isn’t in English, please translate it
-      - Keep responses concise but informative
-
-      // Define function that the agent needs to call
-      Use the yourTool to fetch current weather data.
-`;
-
-export const yourAgent = new Agent({
-	name,
-	instructions,
-	model,
-	tools: { yourTool },
-});
diff --git a/src/mastra/agents/your-agent/your-tool.ts b/src/mastra/agents/your-agent/your-tool.ts
deleted file mode 100644
index 82c42da..0000000
--- a/src/mastra/agents/your-agent/your-tool.ts
+++ /dev/null
@@ -1,22 +0,0 @@
-import { createTool } from "@mastra/core/tools";
-import { z } from "zod";
-
-// Simple async function that conforms to input and output schema
-const getInfo = async (ctx: string) =>
-  Promise.resolve({ bar: ctx.length, baz: "baz" });
-
-// Define your tool using the `createtool`
-export const yourTool = createTool({
-  id: "tool-name",
-  description: "Use the `createTool function to create your tool",
-  inputSchema: z.object({
-    foo: z.string().describe("Foo name"),
-  }),
-  outputSchema: z.object({
-    bar: z.number(),
-    baz: z.string(),
-  }),
-  execute: async ({ context }) => {
-    return await getInfo(context.foo);
-  },
-});

From b89d56dac5f573b18267e7431c58930586a0aeec Mon Sep 17 00:00:00 2001
From: yashksaini-coder <ys3853428@gmail.com>
Date: Sat, 12 Jul 2025 19:02:13 +0530
Subject: [PATCH 4/6] chore(app): Remove sensitive file and ignore it

---
 .env       | 15 ---------------
 .gitignore |  3 ++-
 2 files changed, 2 insertions(+), 16 deletions(-)
 delete mode 100644 .env

diff --git a/.env b/.env
deleted file mode 100644
index 6de16f1..0000000
--- a/.env
+++ /dev/null
@@ -1,15 +0,0 @@
-# Qwen3-8B
-# API_BASE_URL=https://39tnkec9fjruty2sezznwq37rjjbjrtbe4aqlbqwfmgg.node.k8s.prd.nos.ci/qwen-3-8b-skea/api
-# MODEL_NAME_AT_ENDPOINT=qwen3:8b
-
-# Qwen2.5:32b - Nosana Endpoint
-# Note baseURL for Ollama needs to be appended with `/api`
-# API_BASE_URL=https://39tnkec9fjruty2sezznwq37rjjbjrtbe4aqlbqwfmgg.node.k8s.prd.nos.ci/api
-# MODEL_NAME_AT_ENDPOINT=qwen2.5:32b
-
-# Qwen2.5:1.5b - LocalHost - Local Development
-# Note baseURL for Ollama needs to be appended with `/api`
-# `ollama pull qwen2.5:1.5b`
-# `ollama serve`
-API_BASE_URL=http://127.0.0.1:11434/api
-MODEL_NAME_AT_ENDPOINT=qwen2.5:1.5b
diff --git a/.gitignore b/.gitignore
index e222803..73addd4 100644
--- a/.gitignore
+++ b/.gitignore
@@ -6,4 +6,5 @@ dist
 *.db-*
 
 .env.local
-.env.example
\ No newline at end of file
+.env.example
+.env*
\ No newline at end of file

From f14e24eab96ac77055e6417d39215265dd1bffd8 Mon Sep 17 00:00:00 2001
From: yashksaini-coder <ys3853428@gmail.com>
Date: Sat, 12 Jul 2025 19:03:01 +0530
Subject: [PATCH 5/6] fix(app): Update Dockerfile to take .env values

---
 Dockerfile | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/Dockerfile b/Dockerfile
index 2468877..fb1ee7c 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -20,7 +20,7 @@ RUN apt-get update && apt-get install -y \
 WORKDIR /app
 
 # Copy package files
-COPY .env.local package.json pnpm-lock.yaml ./
+COPY .env package.json pnpm-lock.yaml ./
 
 # Install dependencies
 RUN pnpm install

From 696b1ab987b2f85c19a721b84fe5d5bdee9c87b0 Mon Sep 17 00:00:00 2001
From: yashksaini-coder <ys3853428@gmail.com>
Date: Sat, 12 Jul 2025 19:32:56 +0530
Subject: [PATCH 6/6] chore(app): udpate dockerignore

---
 .dockerignore | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/.dockerignore b/.dockerignore
index 64e53c0..f4cea9d 100644
--- a/.dockerignore
+++ b/.dockerignore
@@ -10,7 +10,7 @@ yarn-error.log*
 pnpm-debug.log*
 
 # Environment files
-.env
+
 
 # Build output
 .mastra/
