Repository: sirji-ai/sirji (language: python)

Issue:
PyPI package for Sirji messaging protocol implementation to create, validate and parse messages.
## The problem to solve

To make the Sirji message protocol reusable between different user interfaces like GUI, VS Code extension, this issue aims at publishing the Sirji message protocol as a package.

## Tasks
- [x] Message parsing functionality for converting a string message to a message object.
- [x] Permissions matrix to store the allowed actions from one agent to another.
- [x] Message classes based on actions
- [x] Factory over the message classes
- [x] Custom exceptions to be exposed
- [x] System prompt generation and factory over it
- [x] Enums for actions and agents to avoid hardcodings
- [x] Ability to send messages for READ_DIR and READ_FILE
- [x] Test cases with a threshold of 80% coverage
- [x] README with usages
- [x] Publish to PyPI
- [x] Integrate with VS Code Extension
Merging the current state of vs code UI to vs code extension



Discussion / hints:

Fix patch:
diff --git a/.gitignore b/.gitignore
index 8aa84b90..baab043e 100644
--- a/.gitignore
+++ b/.gitignore
@@ -12,4 +12,20 @@ __pycache__/
 # Virtual environment
 venv
 
-.DS_Store
\ No newline at end of file
+.DS_Store
+
+node_modules
+out
+dist
+.vscode-test/
+*.vsix
+*.egg-info
+
+.coverage
+
+.pytest_cache
+
+# Dev scripts
+read_all.py
+
+.sirji
\ No newline at end of file
diff --git a/.prettierrc b/.prettierrc
index 45c8fa14..ca64adc9 100644
--- a/.prettierrc
+++ b/.prettierrc
@@ -1,11 +1,30 @@
 {
-  "printWidth": 120,
-  "tabWidth": 1,
-  "useTabs": false,
-  "semi": true,
-  "singleQuote": true,
-  "trailingComma": "none",
-  "bracketSpacing": true,
-  "arrowParens": "always",
-  "proseWrap": "preserve"
+ "singleQuote": true,
+ "trailingComma": "all",
+ "semi": true,
+ "tabWidth": 2,
+ "useTabs": false,
+ "arrowParens": "always",
+ "printWidth": 200,
+ "bracketSpacing": true,
+ "jsxBracketSameLine": false,
+ "overrides": [
+  {
+   "files": "*.ts",
+   "options": {
+    "singleQuote": true,
+    "trailingComma": "none",
+    "semi": true,
+    "tabWidth": 2
+   }
+  },
+  {
+   "files": "*.py",
+   "options": {
+    "singleQuote": true,
+    "trailingComma": "none",
+    "tabWidth": 2
+   }
+  }
+ ]
 }
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
new file mode 100644
index 00000000..0b5d808f
--- /dev/null
+++ b/CONTRIBUTING.md
@@ -0,0 +1,100 @@
+# Contributing to Sirji
+
+Thank you for your interest in contributing to Sirji, a VS Code Extension that aims to enhance your coding experience. This guide is designed to help you set up your environment for contributing to Sirji and its components, including the associated PyPI packages.
+
+## Prerequisites
+
+Make sure you have installed all of the following prerequisites on your machine:
+
+- Visual Studio Code (>= 1.80.2)
+- Node.js (>= 18) and npm (>= 8.19)
+- Python (>= 3.10) - Make sure `python --version` runs without error.
+- tee command - Make sure `which tee` runs without error.
+
+To check whether your machine meets these prerequisites, run:
+
+```zsh
+sh check_prerequisites.sh
+```
+
+Also, you will need an OpenAI API key.
+
+## Contributing to VS Code Extension
+
+### 1. Clone the Repository
+
+First, clone the Sirji repository to your local machine using the following command:
+
+```zsh
+git clone git@github.com:sirji-ai/sirji.git
+```
+
+### 2. Navigate to the Extension Directory
+
+Once the repository is cloned, switch to the VS Code Extension directory:
+
+```zsh
+cd sirji/vscode-extension
+```
+
+### 3. Open the Project in VS Code
+
+Open the folder in Visual Studio Code by running:
+
+```zsh
+code .
+```
+
+### 4. Install Dependencies
+
+Install the project dependencies:
+
+```zsh
+npm install
+```
+
+### 5. Compile TypeScript
+
+Compile the TypeScript code to JavaScript:
+
+```zsh
+npm run compile
+```
+
+### 6. Run Sirji in Debug Mode
+
+To start debugging the extension and see your changes in action, follow these steps:
+
+- **Open the Run and Debug View**
+
+  - You can open this view from the Activity Bar on the left side of the window or by using the shortcut `Cmd+Shift+D`.
+
+- **Select Debug Configuration**
+
+  - From the dropdown menu at the top of the **Run and Debug** view, select the `Run Extension` option.
+
+- **Start Debugging**
+
+  - Press the **Start Debugging** button (the green play icon) to launch a new VS Code window (Extension Development Host) where the extension will be loaded.
+
+### 7. Activating Sirji
+
+To activate the extension in the Extension Development Host:
+
+- Open the Command Palette with `Cmd+Shift+P`, type `Sirji`, and press `Enter`.
+
+Now the Sirji chat interface should open, allowing you to interact with Sirji via the chat.
+
+## Contributing to the PyPI Packages
+
+To contribute to one of the PyPI packages (`sirji-agents`, `sirji-messages`, `sirji-tools`), please refer to the instructions provided in the respective `README` files within their directories:
+
+- [Agents README](./agents/README.md) for contributing to `sirji-agents`.
+- [Messages README](./messages/README.md) for contributing to `sirji-messages`.
+- [Tools README](./tools/README.md) for contributing to `sirji-tools`.
+
+## Questions or Issues?
+
+If you face any issues or have questions about contributing, don’t hesitate to open an issue in our GitHub repository. We're here to help and look forward to your contributions!
+
+Thank you for considering contributing to Sirji. Happy coding!
diff --git a/ISSUES.md b/ISSUES.md
new file mode 100644
index 00000000..13f26364
--- /dev/null
+++ b/ISSUES.md
@@ -0,0 +1,28 @@
+# Reporting Issues
+
+We appreciate your contributions to identifying issues and suggesting improvements to the Sirji VS Code Extension. This guide will help you in reporting issues you encounter.
+
+## How to Report an Issue Effectively
+
+1. **Navigate to the [Issues section](https://github.com/sirji-ai/sirji/issues) of the Sirji repository.**
+2. **Click on the *New Issue* button** to create a new issue.
+3. **Select a template** that best matches your issue type: "Bug report", "Enhancement" or "Other Question".
+4. **Provide a concise and descriptive title** that clearly summarizes the issue.
+5. **Fill out the issue template** with as much detail as you can. The more information you provide, the easier it will be for contributors to understand and address the issue:
+    - **Describe the bug** or issue in detail.
+    - **Steps to reproduce**: List the steps that lead to the problematic behavior.
+    - **Expected behavior**: Describe what you expected to happen.
+    - **Actual behavior**: Describe what actually happened.
+    - **Screenshots**: If applicable, add screenshots to help explain your issue.
+    - **Environment details**: Include any relevant details about your environment setup (VS Code version, operating system, extension version, etc.).
+6. **Label your issue**, if possible. Common labels include `bug`, `enhancement`, or `question`.
+
+## What Happens Next?
+
+After you submit an issue, the maintainers will review it. They may ask for further details or suggest potential fixes. Keep an eye on your issue for any updates or requests for additional information.
+
+## Contributions
+
+Remember, addressing issues is a collaborative effort. If you find an issue you'd like to help solve, feel free to comment on it and/or submit a pull request with a proposed fix. Refer to our [CONTRIBUTING.md](./CONTRIBUTING.md) file for guidelines on contributing.
+
+Thank you for helping us improve the Sirji VS Code Extension. Your efforts and cooperation are highly appreciated!
\ No newline at end of file
diff --git a/README.md b/README.md
index 9c4c0ad8..f49dbafb 100644
--- a/README.md
+++ b/README.md
@@ -1,55 +1,106 @@
-# Sirji>
+<p align="center">
+  <a href="." target="blank"><img src="https://github.com/sirji-ai/sirji/assets/7627517/363fc6dd-69af-4d84-8b7c-a91ec092058d" width="250" alt="Sirji Logo" /></a>
+</p>
 
-> “Sirji” is everywhere. A fun jab among friends. A chuckle on the Zoom call. Much more than respect. It's our vibe.
+<p align="center">
+  <em>Sirji is an Open Source AI Software Development Agent.</em>
+</p>
 
-Inspired by <a href="https://www.cognition-labs.com/introducing-devin" target="_blank">Devin</a>, Sirji is an initiative by <a href="https://truesparrow.com/" target="_blank">True Sparrow</a> to build an open-source AI software development agent that solves complex problems. It will automatically create a plan to solve the problem statement, prioritize it, organize research, write code, execute it, and fix issues.
+<p align="center">
+  Built with ❤️ by <a href="https://truesparrow.com/" target="_blank">True Sparrow</a>
+</p>
 
-## Demo Videos
+<p align="center">
+  <img alt="GitHub License" src="https://img.shields.io/github/license/sirji-ai/sirji">
+  <img alt="GitHub commit activity" src="https://img.shields.io/github/commit-activity/m/sirji-ai/sirji">
+  <img alt="GitHub Issues or Pull Requests" src="https://img.shields.io/github/issues/sirji-ai/sirji">
+</p>
 
-For demo videos, visit [here](./demos).
+<p align="center">
+  <img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/sirji-ai/sirji">
+  <img alt="GitHub forks" src="https://img.shields.io/github/forks/sirji-ai/sirji">
+  <img alt="GitHub watchers" src="https://img.shields.io/github/watchers/sirji-ai/sirji">
+</p>
 
-## How to Use<a name="how-to-use"></a>
+## Sirji
 
-The dogfood version of Sirji is ready.
+Sirji is a Visual Studio Code extension that works as an AI software development agent.
 
-Follow these [setup steps](./docs/setup-steps.md) to start Sirji.
+It is a virtual software developer that includes individual agents for planning, coding, researching, and executing projects.
 
-## Capabilities<a name="capabilities"></a>
+Sirji solves users’ problem statements ranging from a new greenfield project to an existing brownfield project where it enhances existing code, fixes bugs, and writes test cases.
 
-Sirji is being built using Python. It currently uses OpenAI chat completions API and OpenAI assistants API.
+The extension leverages the capabilities of VS Code, including the Editor, Terminal, and Project Explorer.
 
-To begin with, it will be equipped with:
+It provides an interactive chat interface through which users submit their problem statements, answer questions, and provide feedback to Sirji.
 
-- **Chat Terminal**: To give problem statements and continuously interact with Sirji.
+Additionally and most importantly, Sirji sets up your local development environment by installing system-level packages as well as programming language-specific dependencies. It later executes the generated code in your local development environment.
 
-- **Shell**: To create, modify, and execute files and install packages.
+## Demo Video
 
-- **Browser**: To research different topics to solve the problem.
+```
+Create a fully interactive website for playing the Tic-Tac-Toe game.
+```
 
-## How It Works<a name="how-it-works"></a>
+Watch on YouTube: <a href="https://www.youtube.com/watch?v=DC_gmz04Ovs" target="_blank">https://www.youtube.com/watch?v=DC_gmz04Ovs</a>
 
-In the dogfood version of Sirji, we are planning to have 4 layers in its architecture as shown in the high-level architecture diagram below.
+<a href="https://www.youtube.com/watch?v=DC_gmz04Ovs" target="_blank"><img src="https://github.com/sirji-ai/sirji/assets/7627517/fff071a6-f019-4797-ad44-55d7670e819f" alt="Tic Tac Toe game by Sirji"></a>
 
-- User Interaction Layer, i.e. Sirji>, will be the front-facing layer interacting with the user. Users can give the problem statement, and give suggestions or modification requests through this layer. Here, users can also see the progress made by Sirji. A command line chat terminal will be used to take user inputs.
 
-- The Agents and Tools Layer will contain multiple agents and tools to be used by Sirji. The available agents will be the Planner, Coder, Security Analyser, Researcher, and Debugger. And, the available tools will be Crawler, Executor, and Logger. To check the work done by agents and tools, we will publish logs in their respective log files.
+## Prerequisites
 
-- The last two layers will consist of the Model & Embeddings Layer, and the Capabilities Layer with browser and shell access.
+Sirji has been tested on **macOS** only for now. We know there are certain OS-specific functionalities that we will soon generalize.
 
-![Sirji Architecture Diagram](https://github.com/sirji-ai/sirji/assets/4491083/4204d366-ccbc-473a-8a0b-233333ce1fdc)
+Make sure you have installed all of the following prerequisites on your machine:
 
-## Sequence Diagram<a name="sequence-diagram"></a>
+- Visual Studio Code (>= 1.80.2)
+- Node.js (>= 18) and npm (>= 8.19)
+- Python (>= 3.10) - Make sure `python --version` runs without error.
+- tee command - Make sure `which tee` runs without error.
 
-The sequence diagram below shows how the user initiates Sirji by giving a problem statement through a chat terminal. From this point onwards, Sirji initiates a hands-free solution approach. However, the user can interact with Sirji by sending messages and asking to improve or modify the solution.
+Also, you will need an OpenAI API key to access the GPT-4 Turbo model.
 
-![Sirji Sequence Diagram](https://github.com/sirji-ai/sirji/assets/4491083/807e62d8-3ded-47c8-81cb-89dfa959ff72)
+## Installation
 
-## Supported AI Models<a name="supported-ai-models"></a>
+You can start using Sirji by installing this [extension](https://marketplace.visualstudio.com/items?itemName=TrueSparrow.sirji) from the Visual Studio Marketplace.
 
-We are planning to use the `gpt-4-turbo-preview` model for the dogfood release. But the package will be designed to be composable for supporting other AI models too.
+## Architecture
 
-## Contribution
+Sirji gets the work done using it's following agents:
 
-We welcome more helping hands to make Sirji better. Feel free to report issues, and raise PRs for fixes & enhancements.
+- The **Planning Agent** takes a problem statement and breaks it down into steps.
+- The **Coding Agent** proceeds step by step through the generated steps to solve the problem programmatically.
+- The **Research Agent** utilizes RAG (Retrieval-Augmented Generation) and gets trained on URLs and search terms. It can later use this acquired knowledge to answer questions posed by the Coding Agent.
+- The **Executor Agent** is responsible for Filesystem CRUD, executing commands, and installing dependencies. The Executor Agent is implemented directly within the extension and is written in TypeScript.
 
-<p align="left">Built with :heart: by <a href="https://truesparrow.com/" target="_blank">True Sparrow</a></p>
+### Architecture Diagram
+
+<img width="100%" alt="VS Code Extension - Architecture" src="https://github.com/sirji-ai/sirji/assets/7627517/0cee6e34-a42a-4db0-81db-d2f930132465">
+
+### PyPI Packages
+
+The Planning Agent, Coding Agent, and Research Agent are developed within the Python package [`sirji-agents`](https://pypi.org/project/sirji-agents/) (located in the `agents` folder of this monorepo). <a href="https://pypi.org/project/sirji-agents/"><img src="https://img.shields.io/pypi/v/sirji-agents.svg" alt="Sirji Agents on PyPI" height="15"></a>
+
+Communication among these agents is facilitated through a defined message protocol. The Message Factory (responsible for creating, reading, updating, and deleting messages according to the message protocol) and the permissions matrix are developed in the Python package [`sirji-messages`](https://pypi.org/project/sirji-messages/) (located in the `messages` folder of this monorepo).<a href="https://pypi.org/project/sirji-messages/"><img src="https://img.shields.io/pypi/v/sirji-messages.svg" alt="Sirji Messages on PyPI" height="15"></a>
+
+The tools for crawling URLs (converting them into markdowns), searching for terms on Google, and a custom logger are developed within the Python package [`sirji-tools`](https://pypi.org/project/sirji-tools/) (located in the `tools` folder of this monorepo). <a href="https://pypi.org/project/sirji-tools/"><img src="https://img.shields.io/pypi/v/sirji-tools.svg" alt="Sirji Tools on PyPI" height="15"></a>
+
+All these packages are invoked by Python Adapter Scripts, which are spawned by the extension.
+
+## Contributing
+
+We welcome contributions to Sirji! If you're interested in helping improve this VS Code extension, please take a look at our [Contributing Guidelines](./CONTRIBUTING.md) for more information on how to get started.
+
+Thank you for considering contributing to Sirji. We look forward to your contributions!
+
+## Reporting Issues
+
+If you run into any issues or have suggestions, please report them by following our [issue reporting guidelines](./ISSUES.md). Your reports help us make Sirji better for everyone.
+
+## Stay In Touch
+
+<a href="https://calendly.com/nishith-true-sparrow/30min" target="_blank">Office Hours</a>
+
+## License
+
+Distributed under the MIT License. See `LICENSE` for more information.
diff --git a/agents/LICENSE b/agents/LICENSE
new file mode 100644
index 00000000..4df90ae4
--- /dev/null
+++ b/agents/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 True Sparrow
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/agents/MANIFEST.in b/agents/MANIFEST.in
new file mode 100644
index 00000000..cc0d1164
--- /dev/null
+++ b/agents/MANIFEST.in
@@ -0,0 +1,1 @@
+include LICENSE
\ No newline at end of file
diff --git a/agents/README.md b/agents/README.md
new file mode 100644
index 00000000..9cecc617
--- /dev/null
+++ b/agents/README.md
@@ -0,0 +1,191 @@
+<p align="center">
+  <a href="." target="blank"><img src="https://github.com/sirji-ai/sirji/assets/7627517/363fc6dd-69af-4d84-8b7c-a91ec092058d" width="250" alt="Sirji Logo" /></a>
+</p>
+
+<p align="center">
+  <em>Sirji is an Open Source AI Software Development Agent.</em>
+</p>
+
+<p align="center">
+  Built with ❤️ by <a href="https://truesparrow.com/" target="_blank">True Sparrow</a>
+</p>
+
+<p align="center">
+  <img alt="GitHub License" src="https://img.shields.io/github/license/sirji-ai/sirji">
+  <img alt="GitHub commit activity" src="https://img.shields.io/github/commit-activity/m/sirji-ai/sirji">
+  <img alt="GitHub Issues or Pull Requests" src="https://img.shields.io/github/issues/sirji-ai/sirji">
+  <img alt="PyPI sirji-agents" src="https://img.shields.io/pypi/v/sirji-agents.svg">
+</p>
+
+<p align="center">
+  <img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/sirji-ai/sirji">
+  <img alt="GitHub forks" src="https://img.shields.io/github/forks/sirji-ai/sirji">
+  <img alt="GitHub watchers" src="https://img.shields.io/github/watchers/sirji-ai/sirji">
+</p>
+
+## Sirji Agents
+
+`sirji-agents` is a PyPI package that implements three key agents for Sirji:
+
+- Coding Agent
+- Planning Agent
+- Research Agent
+
+It Utilizes:
+
+- OpenAI Chat Completions API
+- OpenAI Assistants API
+
+This package communicates with models and RAG assistants.
+
+## Installation
+
+### Setup Virtual Environment
+
+We recommend setting up a virtual environment to isolate Python dependencies, ensuring project-specific packages without conflicting with system-wide installations.
+
+```zsh
+python3 -m venv venv
+source venv/bin/activate
+```
+
+### Install Package
+
+Install the package from PyPi:
+
+```zsh
+pip install sirji-agents
+```
+
+Run the following command to install playwright:
+
+```zsh
+playwright install
+```
+
+## Usage
+
+### Environment Variables
+
+Ensure that following environment variables are set:
+
+```zsh
+export SIRJI_WORKSPACE="Absolute folder path for Sirji to use as it's workspace. Note that a .sirji folder will be created inside it."
+export SIRJI_RUN_ID='Unique alphanumeric ID for each run. Note that a sub folder named by this ID will be created inside of .sirji folder to store logs, etc.'
+export SIRJI_OPENAI_API_KEY='OpenAI API key for Chat Completions API and Assistants API'
+```
+
+### Research Agent
+
+### Initialization
+
+```python
+from sirji_agents import ResearchAgent
+
+# Initialize Researcher without assistant ID
+researcher = ResearchAgent('openai_assistant', 'openai_assistant')
+
+# init_payload fetched from researcher object should be persisted
+init_payload = researcher.init_payload
+
+# Initialize Researcher with assistant ID
+researcher = ResearchAgent('openai_assistant', 'openai_assistant', init_payload)
+```
+
+Some example message handling usages are given below.
+
+#### Train using URL
+
+```python
+from sirji_messages import MessageFactory, ActionEnum
+
+message_class = MessageFactory[ActionEnum.TRAIN_USING_URL.name]
+message_str = message_class().generate({"url": "https://finance.yahoo.com/quote/API/"})
+
+researcher.message(message_str)
+```
+
+#### Infer
+
+```python
+from sirji_messages import MessageFactory, ActionEnum
+
+message_class = MessageFactory[ActionEnum.INFER.name]
+message_str = message_class().generate({"details": "How to use yahoo finance api?"})
+
+response, total_tokens = researcher.message(message_str)
+```
+
+### Coding Agent
+
+```python
+from sirji_agents import CodingAgent
+
+# Initialize Coding Agent
+coder = CodingAgent()
+
+# Construct a message for problem statement
+from sirji_messages import MessageFactory, ActionEnum
+message_class = MessageFactory[ActionEnum.PROBLEM_STATEMENT.name]
+message_str = message_class().generate({"details": "Create a python executable file to find out the factorial of a number"})
+
+# At the beginning, history is empty
+coder_history = []
+
+# call the Coder and update the history variable
+response_message, coder_history, prompt_tokens, completion_tokens = coder.message(message_str, coder_history)
+
+# Now in the new history:
+# coder_history[0] is the system prompt
+# coder_history[1] is the message from User to Coder passing the problem statement
+# coder_history[2] is the response from the LLM inference
+
+# Persist the history variable for future use.
+```
+
+### Planning Agent
+
+```python
+from sirji_agents import PlanningAgent
+
+# Initialize Planning Agent
+planner = PlanningAgent()
+
+# Construct a message for generate steps message
+from sirji_messages import MessageFactory, ActionEnum
+message_class = MessageFactory[ActionEnum.GENERATE_STEPS.name]
+message_str = message_class().generate({"details": "Create a python executable file to find out the factorial of a number"})
+
+# In the actual flow, this message_str will be obtained as a response from Coder.
+
+# At the beginning, history is empty
+planner_history = []
+
+# call the Planner and update the history variable
+response_message, planner_history, prompt_tokens, completion_tokens = planner.message(message_str, planner_history)
+```
+
+## For Contributors
+
+1. Fork and clone the repository.
+2. Create and activate the virtual environment as described above.
+3. Set the environment variables as described above.
+4. Install the package in editable mode by running the following command from repository root:
+
+```zsh
+pip install -e .
+```
+
+5. Run the following command to install playwright:
+
+```zsh
+playwright install
+```
+
+## Running Tests and Coverage Analysis
+
+TODO
+
+## License
+
+Distributed under the MIT License. See `LICENSE` for more information.
diff --git a/agents/requirements.txt b/agents/requirements.txt
new file mode 100644
index 00000000..6b0d326a
--- /dev/null
+++ b/agents/requirements.txt
@@ -0,0 +1,3 @@
+sirji-messages==0.0.13
+sirji-tools==0.0.9
+openai==1.14.1
diff --git a/agents/setup.py b/agents/setup.py
new file mode 100644
index 00000000..a87c1d77
--- /dev/null
+++ b/agents/setup.py
@@ -0,0 +1,22 @@
+from setuptools import setup, find_packages
+
+setup(
+    name='sirji-agents',
+    version='0.0.18',
+    author='Sirji',
+    description='Research, Coding and Planning agents used by Sirji.',
+    license='MIT',
+    long_description=open('README.md').read(),
+    long_description_content_type='text/markdown',
+    url='https://github.com/sirji-ai/sirji',
+    packages=find_packages(),
+    include_package_data=True,  # This includes non-code files specified in MANIFEST.in
+    install_requires=[
+        x for x in open("./requirements.txt", "r+").readlines() if x.strip()
+    ],
+    python_requires='>=3.6',
+    classifiers=[],
+    entry_points={
+        'console_scripts': [],
+    },
+)
diff --git a/agents/sirji_agents/__init__.py b/agents/sirji_agents/__init__.py
new file mode 100644
index 00000000..3ec41b9c
--- /dev/null
+++ b/agents/sirji_agents/__init__.py
@@ -0,0 +1,10 @@
+from .researcher import ResearchAgent
+from .llm.coder import CodingAgent
+from .llm.planner import PlanningAgent
+
+
+__all__ = [
+    'ResearchAgent',
+    'CodingAgent',
+    'PlanningAgent'
+]
diff --git a/sirji/agents/__init__.py b/agents/sirji_agents/llm/__init__.py
similarity index 100%
rename from sirji/agents/__init__.py
rename to agents/sirji_agents/llm/__init__.py
diff --git a/agents/sirji_agents/llm/base.py b/agents/sirji_agents/llm/base.py
new file mode 100644
index 00000000..052597bf
--- /dev/null
+++ b/agents/sirji_agents/llm/base.py
@@ -0,0 +1,106 @@
+from openai import OpenAI
+import os
+
+from sirji_messages import AgentSystemPromptFactory, message_parse, MessageParsingError, MessageValidationError
+
+
+class SingletonMeta(type):
+    """Singleton Meta Class for ensuring one instance creation."""
+    _instances = {}
+
+    def __call__(cls, *args, **kwargs):
+        if cls not in cls._instances:
+            cls._instances[cls] = super().__call__(*args, **kwargs)
+        return cls._instances[cls]
+
+
+class LLMAgentBase(metaclass=SingletonMeta):
+    def __init__(self, agent_enum, logger):
+        api_key = os.environ.get("SIRJI_OPENAI_API_KEY")
+        if api_key is None:
+            raise ValueError(
+                "OpenAI API key is not set as an environment variable")
+
+        self.client = OpenAI(api_key=api_key)
+        self.agent_enum = agent_enum
+        self.logger = logger
+
+    def message(self, input_message, history=[]):
+        conversation = self.__prepare_conversation(input_message, history)
+
+        self.logger.info(f"Incoming: \n{input_message}")
+        self.logger.info("Calling OpenAI Chat Completions API\n")
+
+        response_message, prompt_tokens, completion_tokens = self.__get_response(conversation)
+
+        return response_message, conversation, prompt_tokens, completion_tokens
+
+    def __prepare_conversation(self, input_message, history):
+        conversation = []
+
+        if not history:
+            prompt_class = AgentSystemPromptFactory[self.agent_enum.name]
+            conversation.append(
+                {"role": "system", "content": prompt_class().system_prompt()})
+        else:
+            conversation = history
+
+        parsed_input_message = message_parse(input_message)
+        conversation.append({"role": "user", "content": input_message, "parsed_content": parsed_input_message})
+
+        return conversation
+
+    def __get_response(self, conversation):
+        
+        retry_llm_count = 0
+        response_message = ''
+        prompt_tokens = 0
+        completion_tokens = 0
+
+        while(True):
+            response_message, current_prompt_tokens, current_completion_tokens = self.__call_llm(conversation)
+            
+            prompt_tokens += current_prompt_tokens
+            completion_tokens += current_completion_tokens
+            try:
+                # Attempt parsing
+                parsed_response_message = message_parse(response_message)
+                conversation.append({"role": "assistant", "content": response_message, "parsed_content": parsed_response_message})
+                break
+            except (MessageParsingError, MessageValidationError) as e:
+            # Handling both MessageParsingError and MessageValidationError similarly
+                self.logger.info("Error while parsing the message.\n")
+                retry_llm_count += 1
+                if retry_llm_count > 2:
+                    raise e
+                self.logger.info(f"Requesting LLM to resend the message in correct format.\n")
+                conversation.append({"role": "assistant", "content": response_message, "parsed_content": {}})
+                conversation.append({"role": "user", "content": "The last message was not as per the allowed message formats. Please resend it with proper formatting."})
+            except Exception as e:
+                self.logger.info(f"Generic error while parsing message. Error: {e}\n")
+                raise e
+            
+            
+        return response_message, prompt_tokens, completion_tokens
+    
+    def __call_llm(self, conversation):
+        history = []
+
+        for message in conversation:
+            history.append({"role": message['role'], "content": message['content']})
+
+        chat_completion = self.client.chat.completions.create(
+            messages=history,
+            model="gpt-4-turbo-preview",
+            temperature=0,
+            max_tokens=4095,
+        )
+
+        response_message = chat_completion.choices[0].message.content
+
+        # Get the total tokens used in the response
+        prompt_tokens = chat_completion.usage.prompt_tokens
+        completion_tokens = chat_completion.usage.completion_tokens
+        self.logger.info(f"Raw response from Chat Completions API: \n{response_message}\n\n\n")
+
+        return response_message, prompt_tokens, completion_tokens
diff --git a/agents/sirji_agents/llm/coder.py b/agents/sirji_agents/llm/coder.py
new file mode 100644
index 00000000..524fb3ab
--- /dev/null
+++ b/agents/sirji_agents/llm/coder.py
@@ -0,0 +1,9 @@
+from sirji_messages import AgentEnum
+from sirji_tools.logger import c_logger as logger
+
+from .base import LLMAgentBase
+
+
+class CodingAgent(LLMAgentBase):
+    def __init__(self):
+        super().__init__(AgentEnum.CODER, logger)
diff --git a/agents/sirji_agents/llm/planner.py b/agents/sirji_agents/llm/planner.py
new file mode 100644
index 00000000..44b1efcc
--- /dev/null
+++ b/agents/sirji_agents/llm/planner.py
@@ -0,0 +1,9 @@
+from sirji_messages import AgentEnum
+from sirji_tools.logger import p_logger as logger
+
+from .base import LLMAgentBase
+
+
+class PlanningAgent(LLMAgentBase):
+    def __init__(self):
+        super().__init__(AgentEnum.PLANNER, logger)
diff --git a/sirji/agents/researcher/README.md b/agents/sirji_agents/researcher/README.md
similarity index 75%
rename from sirji/agents/researcher/README.md
rename to agents/sirji_agents/researcher/README.md
index 45fa2b91..54fb0609 100644
--- a/sirji/agents/researcher/README.md
+++ b/agents/sirji_agents/researcher/README.md
@@ -1,6 +1,6 @@
 # Research Agent or Researcher
 
-Whenever Sirji comes across requirements in which there are knowledge points, outside of its knowledge, it invokes the Researcher module. Researcher is based on the RAG (Retrieval-Augmented Generation) framework.
+Whenever Sirji comes across requirements in which there are knowledge points, outside of its knowledge, it invokes the Research Agent (Researcher). Researcher is based on the RAG (Retrieval-Augmented Generation) framework.
 
 Current implementation uses OpenAI Assistants API. We have taken care of making the module composable, which will make strategy changes easier (described in detail below).
 
@@ -22,21 +22,3 @@ In this part, the LLM model is called to infer using a prompt that has both the
 ## Fun Fact
 
 When developing the Researcher module, we needed to go through the OpenAI Assistants API documentation. This documentation was outside the knowledge of our LLM (gpt-4-turbo-preview). So the model was not able to assist us in development. Rather than going through the documentation manually, we thought of using the Sirji approach to research. We manually indexed (manual, since the automated process is what we needed to develop) a new assistant with the PDF prints of the documentation. After this indexing, the assistant helped us to write the Researcher. This also proved to us that the Sirji way of research works!
-
-## Sample Usage
-
-```python
-from sirji.agents.researcher import Researcher
-
-# Initialize Researcher
-researcher = Researcher('openai_assistant', 'openai_assistant')
-
-# For given URLs, convert to markdown files. Generate and store embeddings.
-researcher.index(['https://example.com'])
-
-# For a given query, perform Google search, convert result pages to markdown. Generate and store embeddings.
-researcher.search_and_index('YOUR QUERY HERE')
-
-# Retrieve the matching context uisng embeddings and pass it along prompt for inference.
-researcher.infer('Sample question')
-```
diff --git a/agents/sirji_agents/researcher/__init__.py b/agents/sirji_agents/researcher/__init__.py
new file mode 100644
index 00000000..9fd1e4fc
--- /dev/null
+++ b/agents/sirji_agents/researcher/__init__.py
@@ -0,0 +1,3 @@
+from .researcher import ResearchAgent
+
+__all__ = ['ResearchAgent']
diff --git a/sirji/agents/researcher/embeddings/__init__.py b/agents/sirji_agents/researcher/embeddings/__init__.py
similarity index 100%
rename from sirji/agents/researcher/embeddings/__init__.py
rename to agents/sirji_agents/researcher/embeddings/__init__.py
diff --git a/sirji/agents/researcher/embeddings/base.py b/agents/sirji_agents/researcher/embeddings/base.py
similarity index 100%
rename from sirji/agents/researcher/embeddings/base.py
rename to agents/sirji_agents/researcher/embeddings/base.py
diff --git a/agents/sirji_agents/researcher/embeddings/factory.py b/agents/sirji_agents/researcher/embeddings/factory.py
new file mode 100644
index 00000000..bad4c8f3
--- /dev/null
+++ b/agents/sirji_agents/researcher/embeddings/factory.py
@@ -0,0 +1,10 @@
+from .openai_assistant import OpenAIAssistantEmbeddings
+
+
+class EmbeddingsFactory:
+    @classmethod
+    def get_instance(cls, embeddings_type, init_payload):
+        if embeddings_type == "openai_assistant":
+            return OpenAIAssistantEmbeddings(init_payload)
+        else:
+            raise ValueError("Unsupported embeddings_type.")
diff --git a/sirji/agents/researcher/embeddings/openai_assistant.py b/agents/sirji_agents/researcher/embeddings/openai_assistant.py
similarity index 76%
rename from sirji/agents/researcher/embeddings/openai_assistant.py
rename to agents/sirji_agents/researcher/embeddings/openai_assistant.py
index b7e5c15d..d0625f0c 100644
--- a/sirji/agents/researcher/embeddings/openai_assistant.py
+++ b/agents/sirji_agents/researcher/embeddings/openai_assistant.py
@@ -1,13 +1,15 @@
 import os
 import json
 from openai import OpenAI
+
+from sirji_tools.logger import r_logger as logger
+
 from .base import BaseEmbeddings
-from sirji.tools.logger import researcher as logger
 
 
 class OpenAIAssistantEmbeddings(BaseEmbeddings):
 
-    def __init__(self):
+    def __init__(self, init_payload={}):
         logger.info("Initializing OpenAI Assistant Embeddings")
 
         # Fetch OpenAI API key from environment variable
@@ -20,10 +22,22 @@ def __init__(self):
         # Initialize OpenAI client
         self.client = OpenAI(api_key=api_key)
 
-        # Create assistant and preserve assistant_id
-        self.assistant_id = self._create_assistant()
+        # Existing initialization logic
+        self.init_payload = init_payload
+
+        # Check for assistant_id in the payload
+        if 'assistant_id' in self.init_payload:
+            self.assistant_id = self.init_payload['assistant_id']
+            logger.info(f"Using existing assistant ID: {self.assistant_id}")
+        else:
+            self.assistant_id = self._create_assistant()
+            # Update payload
+            self.init_payload['assistant_id'] = self.assistant_id
+
+            # Provide a way to output this updated init_payload
+            logger.info(f"New assistant created with ID: {self.assistant_id}")
 
-        self.index_file_path = 'workspace/researcher/file_index.json'
+        self.index_file_path = os.path.join(self._get_workspace_folder(), '.sirji', self._get_run_id_folder(), 'researcher', 'file_index.json')
 
         # Load or initialize the index file
         self.index_data = self._load_or_initialize_index_file()
@@ -66,8 +80,22 @@ def retrieve_context(self, problem_statement):
         For OpenAI Assistants API, this step is not needed.
         To re-use the assistant_id, pass it in the retrieved context.
         """
-        return self.assistant_id
+        return ""
+
+    def _get_workspace_folder(self):
+        workspace = os.environ.get("SIRJI_WORKSPACE")
+        if workspace is None:
+            raise ValueError(
+                "SIRJI_WORKSPACE is not set as an environment variable")
+        return workspace
 
+    def _get_run_id_folder(self):
+        run_id = os.environ.get("SIRJI_RUN_ID")
+        if run_id is None:
+            raise ValueError(
+                "SIRJI_RUN_ID is not set as an environment variable")
+        return run_id
+    
     def _create_assistant(self):
         logger.info("Creating a new assistant instance")
         """
diff --git a/sirji/agents/researcher/inferer/__init__.py b/agents/sirji_agents/researcher/inferer/__init__.py
similarity index 100%
rename from sirji/agents/researcher/inferer/__init__.py
rename to agents/sirji_agents/researcher/inferer/__init__.py
diff --git a/sirji/agents/researcher/inferer/base.py b/agents/sirji_agents/researcher/inferer/base.py
similarity index 100%
rename from sirji/agents/researcher/inferer/base.py
rename to agents/sirji_agents/researcher/inferer/base.py
diff --git a/agents/sirji_agents/researcher/inferer/factory.py b/agents/sirji_agents/researcher/inferer/factory.py
new file mode 100644
index 00000000..26afefd8
--- /dev/null
+++ b/agents/sirji_agents/researcher/inferer/factory.py
@@ -0,0 +1,10 @@
+from .openai_assistant import OpenAIAssistantInferer
+
+
+class InfererFactory:
+    @classmethod
+    def get_instance(cls, inferer_type, init_payload):
+        if inferer_type == "openai_assistant":
+            return OpenAIAssistantInferer(init_payload)
+        else:
+            raise ValueError("Unsupported inferer_type.")
diff --git a/sirji/agents/researcher/inferer/openai_assistant.py b/agents/sirji_agents/researcher/inferer/openai_assistant.py
similarity index 64%
rename from sirji/agents/researcher/inferer/openai_assistant.py
rename to agents/sirji_agents/researcher/inferer/openai_assistant.py
index 3fe2c4a8..79eb45ee 100644
--- a/sirji/agents/researcher/inferer/openai_assistant.py
+++ b/agents/sirji_agents/researcher/inferer/openai_assistant.py
@@ -1,19 +1,23 @@
 import os
 import time
 from openai import OpenAI
-from sirji.tools.logger import researcher as logger
+
+from sirji_tools.logger import r_logger as logger
 
 # Assuming .base contains your ResearcherInfererBase
 from .base import ResearcherInfererBase
 
 
 class OpenAIAssistantInferer(ResearcherInfererBase):
-    def __init__(self):
+    def __init__(self, init_payload):
         logger.info("Initializing OpenAI Assistant Inferer")
         """
         Initializes the OpenAIAssistantInferer by setting up the OpenAI client 
         with the API key obtained from environment variables.
         """
+
+        self.init_payload = init_payload
+
         # Fetch OpenAI API key from an environment variable
         api_key = os.environ.get("SIRJI_OPENAI_API_KEY")
 
@@ -24,42 +28,38 @@ def __init__(self):
         # Initialize OpenAI client
         self.client = OpenAI(api_key=api_key)
 
-        # Placeholder for the OpenAI Assistant object
-        self.assistant = None
-        # Placeholder for storing the current assistant's ID
-        self.assistant_id = None
-        # Placeholder for the conversation thread with the assistant
-        self.thread = None
-        
+        # Reading the assistant ID from init_payload
+        self.assistant_id = self.init_payload['assistant_id']
+
+        if 'thread_id' not in self.init_payload or not self.init_payload['thread_id']:
+            assistant = self.client.beta.assistants.retrieve(
+                self.assistant_id)
+            thread = self.client.beta.threads.create()
+            self.init_payload['thread_id'] = thread.id
+
         logger.info("Completed initializing OpenAI Assistant Inferer")
 
     def infer(self, retrieved_context, problem_statement):
         logger.info("Started inferring using OpenAI Assistant Inferer")
-        
+
         """
         Infers an answer by calling a chat model using the retrieved context and the problem statement.
 
         :param retrieved_context: The context information retrieved based on embeddings match.
         :param problem_statement: The initial problem statement or query.
-        :return: The model's response based on the combined information of the problem statement and the retrieved context.
+        :return: A tuple containing the model's response based on the combined information of the problem statement & the retrieved context, the number of prompt tokens used in the run, and the number of completion tokens used in the run.
         """
 
-        # Note: In this case, the assistant id is passed as retrieved context.
-        assistant_id = retrieved_context
-
-        # Setup assistant if not already created or if the assistant_id has changed.
-        self._set_assistant(assistant_id)
-
         # Generate a prompt to send to the assistant
         prompt = self.generate_prompt(retrieved_context, problem_statement)
 
         # Send the generated prompt to the assistant
         self.client.beta.threads.messages.create(
-            thread_id=self.thread.id,
+            thread_id=self.init_payload['thread_id'],
             role="user",
             content=prompt,
         )
-        
+
         logger.info("Completed inferring using OpenAI Assistant Inferer")
 
         # Fetch and return the assistant's response
@@ -77,48 +77,35 @@ def generate_prompt(self, retrieved_context, problem_statement):
         # Customize this method to combine problem_statement and retrieved_context into a coherent prompt.
         return problem_statement
 
-    def _set_assistant(self, assistant_id):
-        logger.info("Setting up OpenAI Assistant Inferer")
-        """
-        Ensures the assistant object is set up correctly, creating a new assistant 
-        and thread if the assistant ID changes or isn't set yet.
-
-        :param assistant_id: The OpenAI Assistant ID to be used.
-        """
-        if not self.assistant or self.assistant_id != assistant_id:
-            self.assistant_id = assistant_id
-            self.assistant = self.client.beta.assistants.retrieve(assistant_id)
-            # Create a new conversation thread with the assistant
-            self.thread = self.client.beta.threads.create()
-        
-        logger.info("Completed setting up OpenAI Assistant Inferer")
-
     def _fetch_response(self):
         logger.info("Fetching response using OpenAI Assistant Inferer")
-        
+
         """
         Initiates a run and waits for the assistant's response, then retrieves and returns the last message.
 
-        :return: The text of the assistant's latest message.
+        :return: A tuple containing the text of the assistant's latest message, the number of prompt tokens used in the run, and the number of completion tokens used in the run.
         """
         # Start a run to fetch the assistant's response
         run = self.client.beta.threads.runs.create(
-            thread_id=self.thread.id,
+            thread_id=self.init_payload['thread_id'],
             assistant_id=self.assistant_id,
             model="gpt-4-turbo-preview",
             tools=[{"type": "retrieval"}]
         )
+
+        logger.info(
+            "Completed fetching response using OpenAI Assistant Inferer")
         
-        logger.info("Completed fetching response using OpenAI Assistant Inferer")
 
         # Loop until the run status is 'completed'
         while run.status != "completed":
             run = self.client.beta.threads.runs.retrieve(
-                thread_id=self.thread.id, run_id=run.id)
+                thread_id=self.init_payload['thread_id'], run_id=run.id)
+            
             time.sleep(1)  # Sleep to prevent overwhelming the API
 
         # Retrieve and return the last message content from the thread
         messages = self.client.beta.threads.messages.list(
-            thread_id=self.thread.id)
+            thread_id=self.init_payload['thread_id'])
         new_message = messages.data[0].content[0].text.value
-        return new_message
+        return new_message, run.usage.prompt_tokens, run.usage.completion_tokens
diff --git a/agents/sirji_agents/researcher/researcher.py b/agents/sirji_agents/researcher/researcher.py
new file mode 100644
index 00000000..709f3a7d
--- /dev/null
+++ b/agents/sirji_agents/researcher/researcher.py
@@ -0,0 +1,112 @@
+import os
+
+from sirji_tools.crawler import crawl_urls
+from sirji_tools.search import search_for
+from sirji_tools.logger import r_logger as logger
+
+from sirji_messages import message_parse, MessageFactory, ActionEnum
+
+from .embeddings.factory import EmbeddingsFactory
+from .inferer.factory import InfererFactory
+
+
+class ResearchAgent:
+    def __init__(self, embeddings_type, inferer_type, init_payload={}):
+        """Initialize Researcher with specific embeddings and inferer types."""
+        logger.info("Initializing researcher...")
+
+        self._embeddings_manager = EmbeddingsFactory.get_instance(
+            embeddings_type, init_payload)
+
+        self.init_payload = self._embeddings_manager.init_payload
+
+        self._inferer = InfererFactory.get_instance(
+            inferer_type, self.init_payload)
+
+        self._research_folder = os.path.join(self._get_workspace_folder(), '.sirji', self._get_run_id_folder(), "researcher")
+
+        logger.info("Completed initializing researcher")
+
+    def message(self, input_message):
+        """Public method to process input messages and dispatch actions."""
+        parsed_message = message_parse(input_message)
+        action = parsed_message.get("ACTION")
+
+        if action == ActionEnum.TRAIN_USING_SEARCH_TERM.name:
+            return self._handle_train_using_search_term(parsed_message)
+        elif action == ActionEnum.TRAIN_USING_URL.name:
+            return self._handle_train_using_url(parsed_message)
+        elif action == ActionEnum.INFER.name:
+            return self._handle_infer(parsed_message)
+        
+    def _get_workspace_folder(self):
+        workspace = os.environ.get("SIRJI_WORKSPACE")
+        if workspace is None:
+            raise ValueError(
+                "SIRJI_WORKSPACE is not set as an environment variable")
+        return workspace
+    
+    def _get_run_id_folder(self):
+        run_id = os.environ.get("SIRJI_RUN_ID")
+        if run_id is None:
+            raise ValueError(
+                "SIRJI_RUN_ID is not set as an environment variable")
+        return run_id
+
+    def _handle_train_using_search_term(self, parsed_message):
+        """Private method to handle training using a search term."""
+        logger.info(
+            f"Training using search term: {parsed_message.get('TERM')}")
+        self._search_and_index(parsed_message.get('TERM'))
+
+        return self._generate_message(ActionEnum.TRAINING_OUTPUT, "Training using search term completed successfully"), 0, 0
+
+    def _handle_train_using_url(self, parsed_message):
+        """Private method to handle training using a specific URL."""
+        logger.info(f"Training using URL: {parsed_message.get('URL')}")
+        self._index([parsed_message.get('URL')])
+
+        return self._generate_message(ActionEnum.TRAINING_OUTPUT, "Training using url completed successfully"), 0, 0
+
+    def _handle_infer(self, parsed_message):
+        """Private method to handle inference requests."""
+        logger.info(f"Infering: {parsed_message.get('DETAILS')}")
+        response, prompt_tokens, completion_tokens= self._infer(parsed_message.get('DETAILS'))
+
+        return self._generate_message(ActionEnum.RESPONSE, response), prompt_tokens, completion_tokens
+
+    def _generate_message(self, action_enum, details):
+        """Generate standardized messages for responses based on action enum."""
+        message_class = MessageFactory[action_enum.name]
+        return message_class().generate({"details": details})
+
+    def _index(self, urls):
+        """Index given URLs."""
+        logger.info("Started indexing the URLs")
+        crawl_urls(urls, self._research_folder)
+        self.__reindex()
+        logger.info("Completed indexing the URLs")
+
+    def _search_and_index(self, query):
+        """Search for a query and index resulting URLs."""
+        urls = search_for(query)
+        self._index(urls)
+
+    def _infer(self, problem_statement):
+        """Infer based on the given problem statement and context."""
+        retrieved_context = self._embeddings_manager.retrieve_context(
+            problem_statement)
+        return self._inferer.infer(retrieved_context, problem_statement)
+
+    def __reindex(self):
+        """Re-index the research folder recursively. Note: This is treated exceptionally private."""
+        logger.info("Recursively indexing the research folder")
+        for root, dirs, files in os.walk(self._research_folder):
+            for folder in dirs:
+                folder_path = os.path.join(root, folder)
+                # Improved logging
+                logger.info(f"Indexing folder: {folder_path}")
+
+                response = self._embeddings_manager.index(folder_path)
+                # Optionally handle the response
+        logger.info("Completed recursive indexing of the research folder")
diff --git a/check_prerequisites.sh b/check_prerequisites.sh
new file mode 100644
index 00000000..951837b0
--- /dev/null
+++ b/check_prerequisites.sh
@@ -0,0 +1,49 @@
+#!/bin/bash
+
+# Function to compare versions
+version_gte() { test "$(printf '%s\n' "$@" | sort -V | head -n 1)" != "$1"; }
+
+# Initial error message
+errorMsg=""
+
+# Check Visual Studio Code version
+vscode_version=$(code --version | head -n 1)
+required_vscode_version="1.80.2"
+if ! command -v code &> /dev/null || ! version_gte "$vscode_version" "$required_vscode_version"; then
+    errorMsg+="Visual Studio Code version >= 1.80.2 is required. Current version: $vscode_version\n"
+fi
+
+# Check Node.js version
+node_version=$(node --version | cut -d 'v' -f 2)
+required_node_version="18"
+if ! command -v node &> /dev/null || ! version_gte "$node_version" "$required_node_version"; then
+    errorMsg+="Node.js version >= 18 is required. Current version: $node_version\n"
+fi
+
+# Check NPM version
+npm_version=$(npm --version)
+required_npm_version="8.19"
+if ! command -v npm &> /dev/null || ! version_gte "$npm_version" "$required_npm_version"; then
+    errorMsg+="npm version >= 8.19 is required. Current version: $npm_version\n"
+fi
+
+# Check Python version
+python_version=$(python3 --version 2>&1 | sed 's/Python //g')
+required_python_version="3.10"
+if ! command -v python3 &> /dev/null || ! version_gte "$python_version" "$required_python_version"; then
+    errorMsg+="Python version >= 3.10 is required. Current version: $python_version\n"
+fi
+
+# Check if tee command is available
+if ! command -v tee &> /dev/null; then
+    errorMsg+="tee command is not available or not in PATH.\n"
+fi
+
+# Display error messages if any prerequisites are missing
+if [[ -n $errorMsg ]]; then
+    echo -e "Some prerequisites are missing or don't meet the version requirements:\n"
+    echo -e "$errorMsg"
+    exit 1
+else
+    echo "All prerequisites are met."
+fi
\ No newline at end of file
diff --git a/demos/README.md b/demos/README.md
index 88901c9f..56a6a901 100644
--- a/demos/README.md
+++ b/demos/README.md
@@ -1,47 +1,18 @@
 # Sirji Demos
 
-Following are some demo videos illustrating [Sirji](https://github.com/sirji-ai/sirji)'s approach to various problems.
+Following are some select demo videos illustrating [Sirji](https://github.com/sirji-ai/sirji)'s problem-solving flows.
 
-## Demo 1: Generate README
+## Demo 1: Tic-Tac-Toe Game
 
 ### Problem Statement
 
 ```
-Clone the "sirji" repository: git@github.com:sirji-ai/sirji.git and checkout the "readme-demo" branch. Read and understand the content of all the files in the folder: "sirji/tools/crawler". Afterward, create a README.md file in that folder giving sample usages, a brief description of the steps performed, etc.
+Create a fully interactive website for playing the Tic-Tac-Toe game.
 ```
 
 ### Demo Video
 
-Watch on YouTube: https://www.youtube.com/watch?v=B9kWchC6lKw
+Watch on YouTube: https://www.youtube.com/watch?v=DC_gmz04Ovs
 
-[![Sirji generates README](https://github.com/sirji-ai/sirji/assets/7627517/9fa49bee-8488-403d-a8b2-54c5aff05628)](https://www.youtube.com/watch?v=B9kWchC6lKw)
+[![Tic Tac Toe game by Sirji](https://github.com/sirji-ai/sirji/assets/7627517/fff071a6-f019-4797-ad44-55d7670e819f)](https://www.youtube.com/watch?v=DC_gmz04Ovs)
 
-
-## Demo 2: Generate Models, Migrations, and Routes
-
-### Problem Statement
-```
-Create an Express App. Use PostgreSQL as the database and Sequelize as ORM.
-Create migration files and model files by understanding the DBML file content: https://github.com/sirji-ai/sirji/blob/main/demos/2-routes-and-models-from-specs/DBML.txt
-Create API routes in modular files by understanding the OpenAPI specs file content: https://github.com/sirji-ai/sirji/blob/main/demos/2-routes-and-models-from-specs/OpenAPI.yml
-```
-
-### Demo Video
-
-Watch on YouTube: https://www.youtube.com/watch?v=Bl73DOqXrtg
-
-[![Sirji generating specs based models, migrations and routes](https://github.com/sirji-ai/sirji/assets/7627517/ceac9f4f-ec4b-48ec-bd75-7de8fbafa376)](https://www.youtube.com/watch?v=Bl73DOqXrtg)
-
-
-## Demo 3: Stock Price Website
-
-### Problem Statement
-
-```
-Generate a simple website to show the last closing, opening and current price of user entered stock.
-```
-### Demo Video
-
-Watch on YouTube: https://www.youtube.com/watch?v=fmYhbTGSTlA
-
-[![Sirji generating stock price website](https://github.com/sirji-ai/sirji/assets/7627517/a7abda56-fc99-406d-8b94-553ec8946799)](https://www.youtube.com/watch?v=fmYhbTGSTlA)
diff --git a/messages/LICENSE b/messages/LICENSE
new file mode 100644
index 00000000..4df90ae4
--- /dev/null
+++ b/messages/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 True Sparrow
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/messages/MANIFEST.in b/messages/MANIFEST.in
new file mode 100644
index 00000000..cc0d1164
--- /dev/null
+++ b/messages/MANIFEST.in
@@ -0,0 +1,1 @@
+include LICENSE
\ No newline at end of file
diff --git a/messages/README.md b/messages/README.md
new file mode 100644
index 00000000..cc9af05b
--- /dev/null
+++ b/messages/README.md
@@ -0,0 +1,173 @@
+<p align="center">
+  <a href="." target="blank"><img src="https://github.com/sirji-ai/sirji/assets/7627517/363fc6dd-69af-4d84-8b7c-a91ec092058d" width="250" alt="Sirji Logo" /></a>
+</p>
+
+<p align="center">
+  <em>Sirji is an Open Source AI Software Development Agent.</em>
+</p>
+
+<p align="center">
+  Built with ❤️ by <a href="https://truesparrow.com/" target="_blank">True Sparrow</a>
+</p>
+
+<p align="center">
+  <img alt="GitHub License" src="https://img.shields.io/github/license/sirji-ai/sirji">
+  <img alt="GitHub commit activity" src="https://img.shields.io/github/commit-activity/m/sirji-ai/sirji">
+  <img alt="GitHub Issues or Pull Requests" src="https://img.shields.io/github/issues/sirji-ai/sirji">
+  <img alt="PyPI sirji-messages" src="https://img.shields.io/pypi/v/sirji-messages.svg">
+</p>
+
+<p align="center">
+  <img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/sirji-ai/sirji">
+  <img alt="GitHub forks" src="https://img.shields.io/github/forks/sirji-ai/sirji">
+  <img alt="GitHub watchers" src="https://img.shields.io/github/watchers/sirji-ai/sirji">
+</p>
+
+## Sirji Messages
+
+`sirji-messages` is a PyPI package that implements the Sirji message protocol with following highlights:
+
+- Message Factory
+- Permissions Matrix (for defining message actions allowed between two agents)
+- System prompt generation
+
+## Installation
+
+### Setup Virtual Environment
+
+We recommend setting up a virtual environment to isolate Python dependencies, ensuring project-specific packages without conflicting with system-wide installations.
+
+```zsh
+python3 -m venv venv
+source venv/bin/activate
+```
+
+### Install Package
+
+Install the package from PyPi:
+
+```zsh
+pip install sirji-messages
+```
+
+## Usage
+
+### Message Parsing
+
+Parse structured message strings into Python dictionaries for easy access to the message components.
+
+````python
+from sirji_messages import message_parse
+
+# Example message string to parse
+message_str = """```
+FROM: CODER
+TO: USER
+ACTION: INFORM
+DETAILS: Welcome to sirji-messages. Here's how you can start.
+```"""
+
+# Parsing the message
+message = message_parse(message_str)
+print(message)
+````
+
+### Permission Validation
+
+Determine if a specified action is allowed between two agents based on predefined permission rules.
+
+```python
+from sirji_messages import permissions_dict, validate_permission, AgentEnum
+
+# Example check if a CODER can QUESTION a USER
+is_allowed = validate_permission("CODER", "USER", "QUESTION")
+print(f"Is allowed: {is_allowed}")
+
+# Get a direct look at permissions dictionary for CODER sending to USER
+print(permissions_dict[(AgentEnum.CODER, AgentEnum.USER)])
+```
+
+### Handling Custom Exceptions
+
+Efficiently manage parsing and validation errors with custom exceptions for improved error handling and debugging.
+
+```python
+from sirji_messages import MessageParsingError, MessageValidationError, message_parse
+
+try:
+    # Attempt parsing an incorrectly formatted message
+    message_parse("INCORRECT_FORMAT")
+except MessageParsingError as e:
+    print(f"Parsing Error: {e}")
+except MessageValidationError as e:
+    print(f"Validation Error: {e}")
+```
+
+### Enums for Intuitive References
+
+Use enums (`ActionEnum`, `AgentEnum`) to reference actions and agent types programmatically, enhancing code clarity and reducing errors.
+
+```python
+from sirji_messages import ActionEnum, AgentEnum
+
+# Example usage of enums for action and agent reference
+action = ActionEnum.ACKNOWLEDGE
+agent = AgentEnum.CODER
+
+# Accessing enum properties
+print(f"Action: {action.name}, Agent: {agent.full_name}")
+
+# Access to enums using [] is also possible
+action = ActionEnum['ACKNOWLEDGE']
+agent = AgentEnum['CODER']
+```
+
+### Factories for Dynamic Message and Prompt Creation
+
+Utilize factories (`MessageFactory`, `AgentSystemPromptFactory`) to instantiate message and prompt classes dynamically based on enums. It simplifies creating custom messages or retrieving specific system prompts without hardcoding class names.
+
+```python
+from sirji_messages import MessageFactory, ActionEnum, AgentSystemPromptFactory, AgentEnum
+
+# Message class instantiation from an action enum
+message_class = MessageFactory[ActionEnum.INFORM.name]
+print(f"Sample INFORM message:\n{message_class().sample()}")
+
+# Generate message using passed template variables
+generated_messages = message_class().generate({"details": "Some sample information."})
+print(f"Generated INFORM message:\n{generated_messages}")
+
+# System prompt class instantiation from an agent enum
+prompt_class = AgentSystemPromptFactory[AgentEnum.CODER.name]
+print(f"CODER system prompt: {prompt_class().system_prompt()}")
+```
+
+## For Contributors
+
+1. Fork and clone the repository.
+2. Create and activate the virtual environment as described above.
+3. Install the package in editable mode by running the following command from repository root:
+
+```zsh
+pip install -e .
+```
+
+## Running Tests and Coverage Analysis
+
+Follow the above mentioned steps for "contributors", before running the test cases.
+
+```zsh
+# Install testing dependencies
+pip install pytest coverage
+
+# Execute tests
+pytest
+
+# Measure coverage, excluding test files
+coverage run --omit="tests/*" -m pytest
+coverage report
+```
+
+## License
+
+Distributed under the MIT License. See `LICENSE` for more information.
diff --git a/sirji/agents/debugger.py b/messages/requirements.txt
similarity index 100%
rename from sirji/agents/debugger.py
rename to messages/requirements.txt
diff --git a/messages/setup.py b/messages/setup.py
new file mode 100644
index 00000000..b02bd249
--- /dev/null
+++ b/messages/setup.py
@@ -0,0 +1,22 @@
+from setuptools import setup, find_packages
+
+setup(
+    name='sirji-messages',
+    version='0.0.13',
+    author='Sirji',
+    description='Sirji messaging protocol implementation to create, validate and parse messages.',
+    license='MIT',
+    long_description=open('README.md').read(),
+    long_description_content_type='text/markdown',
+    url='https://github.com/sirji-ai/sirji',
+    packages=find_packages(),
+    include_package_data=True,  # This includes non-code files specified in MANIFEST.in
+    install_requires=[
+        x for x in open("./requirements.txt", "r+").readlines() if x.strip()
+    ],
+    python_requires='>=3.6',
+    classifiers=[],
+    entry_points={
+        'console_scripts': [],
+    },
+)
diff --git a/messages/sirji_messages/__init__.py b/messages/sirji_messages/__init__.py
new file mode 100644
index 00000000..4fabd39a
--- /dev/null
+++ b/messages/sirji_messages/__init__.py
@@ -0,0 +1,20 @@
+from .action_enum import ActionEnum
+from .agent_enum import AgentEnum
+from .messages.factory import MessageFactory
+from .system_prompts.factory import AgentSystemPromptFactory
+from .custom_exceptions import MessageParsingError, MessageValidationError
+from .permissions import validate_permission, permissions_dict
+from .parser import parse as message_parse
+
+
+__all__ = [
+    'message_parse',
+    'MessageFactory',
+    'ActionEnum',
+    'AgentEnum',
+    'MessageParsingError',
+    'MessageValidationError',
+    'validate_permission',
+    'permissions_dict',
+    'AgentSystemPromptFactory',
+]
diff --git a/messages/sirji_messages/action_enum.py b/messages/sirji_messages/action_enum.py
new file mode 100644
index 00000000..383da6a3
--- /dev/null
+++ b/messages/sirji_messages/action_enum.py
@@ -0,0 +1,27 @@
+from enum import Enum, auto
+
+
+class ActionEnum(Enum):
+    ACKNOWLEDGE = auto()
+    ANSWER = auto()
+    EXECUTE_COMMAND = auto()
+    RUN_SERVER = auto()
+    CREATE_FILE = auto()
+    GENERATE_STEPS = auto()
+    PROBLEM_STATEMENT = auto()
+    INFER = auto()
+    INFORM = auto()
+    INSTALL_PACKAGE = auto()
+    OUTPUT = auto()
+    QUESTION = auto()
+    RESPONSE = auto()
+    SOLUTION_COMPLETE = auto()
+    STEP_COMPLETED = auto()
+    STEP_STARTED = auto()
+    STEPS = auto()
+    TRAIN_USING_SEARCH_TERM = auto()
+    TRAIN_USING_URL = auto()
+    FEEDBACK = auto()
+    TRAINING_OUTPUT = auto()
+    READ_FILE = auto()
+    READ_DIR = auto()
diff --git a/messages/sirji_messages/agent_enum.py b/messages/sirji_messages/agent_enum.py
new file mode 100644
index 00000000..c0807606
--- /dev/null
+++ b/messages/sirji_messages/agent_enum.py
@@ -0,0 +1,15 @@
+from enum import Enum
+
+
+class AgentEnum(Enum):
+    def __new__(cls, value, name):
+        member = object.__new__(cls)
+        member._value_ = value
+        member.full_name = name
+        return member
+
+    CODER = (1, "Coding Agent")
+    PLANNER = (2, "Planning Agent")
+    EXECUTOR = (3, "Execution Agent")
+    RESEARCHER = (4, "Research Agent")
+    USER = (5, "End User")
diff --git a/messages/sirji_messages/custom_exceptions.py b/messages/sirji_messages/custom_exceptions.py
new file mode 100644
index 00000000..01e20383
--- /dev/null
+++ b/messages/sirji_messages/custom_exceptions.py
@@ -0,0 +1,14 @@
+class MessageValidationError(Exception):
+    """Exception raised for errors in the input message format."""
+
+    def __init__(self, message="Invalid message format"):
+        self.message = message
+        super().__init__(self.message)
+
+
+class MessageParsingError(Exception):
+    """Exception raised for errors in parsing the message contents."""
+
+    def __init__(self, message="Error parsing message"):
+        self.message = message
+        super().__init__(self.message)
diff --git a/sirji/chat_providers/__init__.py b/messages/sirji_messages/messages/__init__.py
similarity index 100%
rename from sirji/chat_providers/__init__.py
rename to messages/sirji_messages/messages/__init__.py
diff --git a/sirji/config/__init__.py b/messages/sirji_messages/messages/actions/__init__.py
similarity index 100%
rename from sirji/config/__init__.py
rename to messages/sirji_messages/messages/actions/__init__.py
diff --git a/messages/sirji_messages/messages/actions/acknowledge.py b/messages/sirji_messages/messages/actions/acknowledge.py
new file mode 100644
index 00000000..df49b173
--- /dev/null
+++ b/messages/sirji_messages/messages/actions/acknowledge.py
@@ -0,0 +1,29 @@
+import textwrap
+
+from sirji_messages import AgentEnum, ActionEnum
+from .base import BaseMessages
+
+
+class AcknowledgeMessage(BaseMessages):
+
+    def __init__(self):
+        self.action = ActionEnum.ACKNOWLEDGE.name
+        self.from_agent = AgentEnum.USER.name
+        self.to_agent = AgentEnum.CODER.name
+
+        super().__init__()
+
+    def template_payload_part(self):
+        return textwrap.dedent("""
+            DETAILS: Sure.
+            """)
+
+    def sample(self):
+        return self.generate({})
+
+    def description(self):
+        return "The message acknowledgment:"
+
+    @staticmethod
+    def custom_properties():
+        return ['DETAILS']
diff --git a/messages/sirji_messages/messages/actions/answer.py b/messages/sirji_messages/messages/actions/answer.py
new file mode 100644
index 00000000..37fc2e37
--- /dev/null
+++ b/messages/sirji_messages/messages/actions/answer.py
@@ -0,0 +1,30 @@
+import textwrap
+
+from sirji_messages import AgentEnum, ActionEnum
+from .base import BaseMessages
+
+
+class AnswerMessage(BaseMessages):
+    def __init__(self):
+        self.action = ActionEnum.ANSWER.name
+        self.from_agent = AgentEnum.USER.name
+        self.to_agent = AgentEnum.CODER.name
+
+        super().__init__()
+
+    def template_payload_part(self):
+        return textwrap.dedent("""
+          DETAILS: {details}
+          """)
+
+    def sample(self):
+        return self.generate({
+            "details": "Multiline answer to the asked question."
+        })
+
+    def description(self):
+        return "The answer to the asked question:"
+
+    @staticmethod
+    def custom_properties():
+        return ['DETAILS']
diff --git a/messages/sirji_messages/messages/actions/base.py b/messages/sirji_messages/messages/actions/base.py
new file mode 100644
index 00000000..38a7d437
--- /dev/null
+++ b/messages/sirji_messages/messages/actions/base.py
@@ -0,0 +1,51 @@
+import textwrap
+from abc import ABC, abstractmethod
+
+
+class BaseMessages(ABC):
+
+    @abstractmethod
+    def template_payload_part(self):
+        """
+        Message template's payload part which is custom to the message action.
+        """
+        pass
+
+    @abstractmethod
+    def sample(self):
+        """
+        Sample message using the message template.
+        """
+        pass
+
+    @abstractmethod
+    def description(self):
+        """
+        Description of the message.
+        """
+        pass
+
+    @staticmethod
+    @abstractmethod
+    def custom_properties():
+        """
+        List of custom properties of the message.
+        """
+        pass
+
+    def generate(self, obj):
+        return self.template().format(**obj)
+
+    def template_prefix_part(self):
+        return textwrap.dedent(f"""
+            ```
+            FROM: {self.from_agent}
+            TO: {self.to_agent}
+            ACTION: {self.action}""")
+
+    def template_suffix_part(self):
+        return textwrap.dedent(f"""```
+            """)
+
+    def template(self):
+        return self.template_prefix_part() + self.template_payload_part() + self.template_suffix_part()
diff --git a/messages/sirji_messages/messages/actions/create_file.py b/messages/sirji_messages/messages/actions/create_file.py
new file mode 100644
index 00000000..2623b62b
--- /dev/null
+++ b/messages/sirji_messages/messages/actions/create_file.py
@@ -0,0 +1,34 @@
+import textwrap
+
+from sirji_messages import AgentEnum, ActionEnum
+from .base import BaseMessages
+
+
+class CreateFileMessage(BaseMessages):
+
+    def __init__(self):
+        self.action = ActionEnum.CREATE_FILE.name
+        self.from_agent = AgentEnum.CODER.name
+        self.to_agent = AgentEnum.EXECUTOR.name
+
+        super().__init__()
+
+    def template_payload_part(self):
+        return textwrap.dedent("""
+          FILENAME: {file_name}
+          CONTENT:
+          {content}
+          """)
+
+    def sample(self):
+        return self.generate({
+            "file_name": "File name along with the path.",
+            "content": "Multiline file content. It should start from a new line."
+        })
+
+    def description(self):
+        return "To create a file (with content):"
+
+    @staticmethod
+    def custom_properties():
+        return ['FILENAME', 'CONTENT']
diff --git a/messages/sirji_messages/messages/actions/execute_command.py b/messages/sirji_messages/messages/actions/execute_command.py
new file mode 100644
index 00000000..94e395f6
--- /dev/null
+++ b/messages/sirji_messages/messages/actions/execute_command.py
@@ -0,0 +1,31 @@
+import textwrap
+
+from sirji_messages import AgentEnum, ActionEnum
+from .base import BaseMessages
+
+
+class ExecuteCommandMessage(BaseMessages):
+
+    def __init__(self):
+        self.action = ActionEnum.EXECUTE_COMMAND.name
+        self.from_agent = AgentEnum.CODER.name
+        self.to_agent = AgentEnum.EXECUTOR.name
+
+        super().__init__()
+
+    def template_payload_part(self):
+        return textwrap.dedent("""
+          COMMAND: {command}
+          """)
+
+    def sample(self):
+        return self.generate({
+            "command": "Command to execute."
+        })
+
+    def description(self):
+        return "To execute a command:"
+
+    @staticmethod
+    def custom_properties():
+        return ['COMMAND']
diff --git a/messages/sirji_messages/messages/actions/feedback.py b/messages/sirji_messages/messages/actions/feedback.py
new file mode 100644
index 00000000..c172af81
--- /dev/null
+++ b/messages/sirji_messages/messages/actions/feedback.py
@@ -0,0 +1,31 @@
+import textwrap
+
+from sirji_messages import AgentEnum, ActionEnum
+from .base import BaseMessages
+
+
+class FeedbackMessage(BaseMessages):
+
+    def __init__(self):
+        self.action = ActionEnum.FEEDBACK.name
+        self.from_agent = AgentEnum.USER.name
+        self.to_agent = AgentEnum.CODER.name
+
+        super().__init__()
+
+    def template_payload_part(self):
+        return textwrap.dedent("""
+          DETAILS: {details}
+          """)
+
+    def sample(self):
+        return self.generate({
+            "details": "Feedback on the solution provided."
+        })
+
+    def description(self):
+        return "Feedback on the solution provided:"
+
+    @staticmethod
+    def custom_properties():
+        return ['DETAILS']
diff --git a/messages/sirji_messages/messages/actions/generate_steps.py b/messages/sirji_messages/messages/actions/generate_steps.py
new file mode 100644
index 00000000..f1ed749f
--- /dev/null
+++ b/messages/sirji_messages/messages/actions/generate_steps.py
@@ -0,0 +1,31 @@
+import textwrap
+
+from sirji_messages import AgentEnum, ActionEnum
+from .base import BaseMessages
+
+
+class GenerateStepsMessage(BaseMessages):
+
+    def __init__(self):
+        self.action = ActionEnum.GENERATE_STEPS.name
+        self.from_agent = AgentEnum.CODER.name
+        self.to_agent = AgentEnum.PLANNER.name
+
+        super().__init__()
+
+    def template_payload_part(self):
+        return textwrap.dedent("""
+          DETAILS: {details}
+          """)
+
+    def sample(self):
+        return self.generate({
+            "details": "Problem statement (PS) here."
+        })
+
+    def description(self):
+        return "Generate steps for the problem statement (PS):"
+
+    @staticmethod
+    def custom_properties():
+        return ['DETAILS']
diff --git a/messages/sirji_messages/messages/actions/infer.py b/messages/sirji_messages/messages/actions/infer.py
new file mode 100644
index 00000000..c20e4ab8
--- /dev/null
+++ b/messages/sirji_messages/messages/actions/infer.py
@@ -0,0 +1,31 @@
+import textwrap
+
+from sirji_messages import AgentEnum, ActionEnum
+from .base import BaseMessages
+
+
+class InferMessage(BaseMessages):
+
+    def __init__(self):
+        self.action = ActionEnum.INFER.name
+        self.from_agent = AgentEnum.CODER.name
+        self.to_agent = AgentEnum.RESEARCHER.name
+
+        super().__init__()
+
+    def template_payload_part(self):
+        return textwrap.dedent("""
+          DETAILS: {details}
+          """)
+
+    def sample(self):
+        return self.generate({
+            "details": "Question to extract answers, code examples, GitHub URLs, relevant external URLs from the trained content."
+        })
+
+    def description(self):
+        return "Ask questions on the trained content:"
+
+    @staticmethod
+    def custom_properties():
+        return ['DETAILS']
diff --git a/messages/sirji_messages/messages/actions/inform.py b/messages/sirji_messages/messages/actions/inform.py
new file mode 100644
index 00000000..431807bc
--- /dev/null
+++ b/messages/sirji_messages/messages/actions/inform.py
@@ -0,0 +1,31 @@
+import textwrap
+
+from sirji_messages import AgentEnum, ActionEnum
+from .base import BaseMessages
+
+
+class InformMessage(BaseMessages):
+
+    def __init__(self):
+        self.action = ActionEnum.INFORM.name
+        self.from_agent = AgentEnum.CODER.name
+        self.to_agent = AgentEnum.USER.name
+
+        super().__init__()
+
+    def template_payload_part(self):
+        return textwrap.dedent("""
+          DETAILS: {details}
+          """)
+
+    def sample(self):
+        return self.generate({
+            "details": "Details of your decisions or choices."
+        })
+
+    def description(self):
+        return "To inform about your decisions or choices:"
+
+    @staticmethod
+    def custom_properties():
+        return ['DETAILS']
diff --git a/messages/sirji_messages/messages/actions/install_package.py b/messages/sirji_messages/messages/actions/install_package.py
new file mode 100644
index 00000000..95adc775
--- /dev/null
+++ b/messages/sirji_messages/messages/actions/install_package.py
@@ -0,0 +1,31 @@
+import textwrap
+
+from sirji_messages import AgentEnum, ActionEnum
+from .base import BaseMessages
+
+
+class InstallPackageMessage(BaseMessages):
+
+    def __init__(self):
+        self.action = ActionEnum.INSTALL_PACKAGE.name
+        self.from_agent = AgentEnum.CODER.name
+        self.to_agent = AgentEnum.EXECUTOR.name
+
+        super().__init__()
+
+    def template_payload_part(self):
+        return textwrap.dedent("""
+          COMMAND: {command}
+          """)
+
+    def sample(self):
+        return self.generate({
+            "command": "Command to install the package or library."
+        })
+
+    def description(self):
+        return "To install a package or library:"
+
+    @staticmethod
+    def custom_properties():
+        return ['COMMAND']
diff --git a/messages/sirji_messages/messages/actions/output.py b/messages/sirji_messages/messages/actions/output.py
new file mode 100644
index 00000000..f099d4c0
--- /dev/null
+++ b/messages/sirji_messages/messages/actions/output.py
@@ -0,0 +1,32 @@
+import textwrap
+
+from sirji_messages import AgentEnum, ActionEnum
+from .base import BaseMessages
+
+
+class OutputMessage(BaseMessages):
+
+    def __init__(self):
+        self.action = ActionEnum.OUTPUT.name
+        self.from_agent = AgentEnum.EXECUTOR.name
+        self.to_agent = AgentEnum.CODER.name
+
+        super().__init__()
+
+    def template_payload_part(self):
+        return textwrap.dedent("""
+          DETAILS:
+          {details}
+          """)
+
+    def sample(self):
+        return self.generate({
+            "details": "Multilined response."
+        })
+
+    def description(self):
+        return "The response output:"
+
+    @staticmethod
+    def custom_properties():
+        return ['DETAILS']
diff --git a/messages/sirji_messages/messages/actions/problem_statement.py b/messages/sirji_messages/messages/actions/problem_statement.py
new file mode 100644
index 00000000..1891ba64
--- /dev/null
+++ b/messages/sirji_messages/messages/actions/problem_statement.py
@@ -0,0 +1,31 @@
+import textwrap
+
+from sirji_messages import AgentEnum, ActionEnum
+from .base import BaseMessages
+
+
+class ProblemStatementMessage(BaseMessages):
+
+    def __init__(self):
+        self.action = ActionEnum.PROBLEM_STATEMENT.name
+        self.from_agent = AgentEnum.USER.name
+        self.to_agent = AgentEnum.CODER.name
+
+        super().__init__()
+
+    def template_payload_part(self):
+        return textwrap.dedent("""
+          DETAILS: {details}
+          """)
+
+    def sample(self):
+        return self.generate({
+            "details": "The problem statement (PS) that needs to be solved programmatically."
+        })
+
+    def description(self):
+        return "The problem statement (PS) that needs to be solved programmatically:"
+
+    @staticmethod
+    def custom_properties():
+        return ['DETAILS']
diff --git a/messages/sirji_messages/messages/actions/question.py b/messages/sirji_messages/messages/actions/question.py
new file mode 100644
index 00000000..70ebf808
--- /dev/null
+++ b/messages/sirji_messages/messages/actions/question.py
@@ -0,0 +1,31 @@
+import textwrap
+
+from sirji_messages import AgentEnum, ActionEnum
+from .base import BaseMessages
+
+
+class QuestionMessage(BaseMessages):
+
+    def __init__(self):
+        self.action = ActionEnum.QUESTION.name
+        self.from_agent = AgentEnum.CODER.name
+        self.to_agent = AgentEnum.USER.name
+
+        super().__init__()
+
+    def template_payload_part(self):
+        return textwrap.dedent("""
+          DETAILS: {details}
+          """)
+
+    def sample(self):
+        return self.generate({
+            "details": "A concise question to understand the problem statement better."
+        })
+
+    def description(self):
+        return "Ask questions to understand the problem statement better:"
+
+    @staticmethod
+    def custom_properties():
+        return ['DETAILS']
diff --git a/messages/sirji_messages/messages/actions/read_dir.py b/messages/sirji_messages/messages/actions/read_dir.py
new file mode 100644
index 00000000..0877fa4b
--- /dev/null
+++ b/messages/sirji_messages/messages/actions/read_dir.py
@@ -0,0 +1,31 @@
+import textwrap
+
+from sirji_messages import AgentEnum, ActionEnum
+from .base import BaseMessages
+
+
+class ReadDirMessage(BaseMessages):
+
+    def __init__(self):
+        self.action = ActionEnum.READ_DIR.name
+        self.from_agent = AgentEnum.CODER.name
+        self.to_agent = AgentEnum.EXECUTOR.name
+
+        super().__init__()
+
+    def template_payload_part(self):
+        return textwrap.dedent("""
+          DIRPATH: {dir_path}
+          """)
+
+    def sample(self):
+        return self.generate({
+            "dir_path": "Name of a directory alongwith the path.",
+        })
+
+    def description(self):
+        return "To read the content from all files in a directory and all its subdirectories at once (separated by a divider):"
+
+    @staticmethod
+    def custom_properties():
+        return ['DIRPATH']
diff --git a/messages/sirji_messages/messages/actions/read_file.py b/messages/sirji_messages/messages/actions/read_file.py
new file mode 100644
index 00000000..80a936ea
--- /dev/null
+++ b/messages/sirji_messages/messages/actions/read_file.py
@@ -0,0 +1,31 @@
+import textwrap
+
+from sirji_messages import AgentEnum, ActionEnum
+from .base import BaseMessages
+
+
+class ReadFileMessage(BaseMessages):
+
+    def __init__(self):
+        self.action = ActionEnum.READ_FILE.name
+        self.from_agent = AgentEnum.CODER.name
+        self.to_agent = AgentEnum.EXECUTOR.name
+
+        super().__init__()
+
+    def template_payload_part(self):
+        return textwrap.dedent("""
+          FILENAME: {file_name}
+          """)
+
+    def sample(self):
+        return self.generate({
+            "file_name": "File name along with the path.",
+        })
+
+    def description(self):
+        return "To read a file:"
+
+    @staticmethod
+    def custom_properties():
+        return ['FILENAME']
diff --git a/messages/sirji_messages/messages/actions/response.py b/messages/sirji_messages/messages/actions/response.py
new file mode 100644
index 00000000..2dac77ff
--- /dev/null
+++ b/messages/sirji_messages/messages/actions/response.py
@@ -0,0 +1,32 @@
+import textwrap
+
+from sirji_messages import AgentEnum, ActionEnum
+from .base import BaseMessages
+
+
+class ResponseMessage(BaseMessages):
+
+    def __init__(self):
+        self.action = ActionEnum.RESPONSE.name
+        self.from_agent = AgentEnum.RESEARCHER.name
+        self.to_agent = AgentEnum.CODER.name
+
+        super().__init__()
+
+    def template_payload_part(self):
+        return textwrap.dedent("""
+          DETAILS:
+          {details}
+          """)
+
+    def sample(self):
+        return self.generate({
+            "details": "Multilined response."
+        })
+
+    def description(self):
+        return "The response output:"
+
+    @staticmethod
+    def custom_properties():
+        return ['DETAILS']
diff --git a/messages/sirji_messages/messages/actions/run_server.py b/messages/sirji_messages/messages/actions/run_server.py
new file mode 100644
index 00000000..a06d3658
--- /dev/null
+++ b/messages/sirji_messages/messages/actions/run_server.py
@@ -0,0 +1,31 @@
+import textwrap
+
+from sirji_messages import AgentEnum, ActionEnum
+from .base import BaseMessages
+
+
+class RunServerMessage(BaseMessages):
+
+    def __init__(self):
+        self.action = ActionEnum.RUN_SERVER.name
+        self.from_agent = AgentEnum.CODER.name
+        self.to_agent = AgentEnum.EXECUTOR.name
+
+        super().__init__()
+
+    def template_payload_part(self):
+        return textwrap.dedent("""
+          COMMAND: {command}
+          """)
+
+    def sample(self):
+        return self.generate({
+            "command": "Command to start server process."
+        })
+
+    def description(self):
+        return "To start server process:"
+
+    @staticmethod
+    def custom_properties():
+        return ['COMMAND']
diff --git a/messages/sirji_messages/messages/actions/solution_complete.py b/messages/sirji_messages/messages/actions/solution_complete.py
new file mode 100644
index 00000000..bf57a1ff
--- /dev/null
+++ b/messages/sirji_messages/messages/actions/solution_complete.py
@@ -0,0 +1,31 @@
+import textwrap
+
+from sirji_messages import AgentEnum, ActionEnum
+from .base import BaseMessages
+
+
+class SolutionCompleteMessage(BaseMessages):
+
+    def __init__(self):
+        self.action = ActionEnum.SOLUTION_COMPLETE.name
+        self.from_agent = AgentEnum.CODER.name
+        self.to_agent = AgentEnum.USER.name
+
+        super().__init__()
+
+    def template_payload_part(self):
+        return textwrap.dedent("""
+          DETAILS: {details}
+          """)
+
+    def sample(self):
+        return self.generate({
+            "details": "A concise message to inform that the solution is complete."
+        })
+
+    def description(self):
+        return "To inform that the solution to the problem is complete:"
+
+    @staticmethod
+    def custom_properties():
+        return ['DETAILS']
diff --git a/messages/sirji_messages/messages/actions/step_completed.py b/messages/sirji_messages/messages/actions/step_completed.py
new file mode 100644
index 00000000..2fb9143b
--- /dev/null
+++ b/messages/sirji_messages/messages/actions/step_completed.py
@@ -0,0 +1,31 @@
+import textwrap
+
+from sirji_messages import AgentEnum, ActionEnum
+from .base import BaseMessages
+
+
+class StepCompletedMessage(BaseMessages):
+
+    def __init__(self):
+        self.action = ActionEnum.STEP_COMPLETED.name
+        self.from_agent = AgentEnum.CODER.name
+        self.to_agent = AgentEnum.USER.name
+
+        super().__init__()
+
+    def template_payload_part(self):
+        return textwrap.dedent("""
+          DETAILS: {details}
+          """)
+
+    def sample(self):
+        return self.generate({
+            "details": "Example details 'Step # completed'."
+        })
+
+    def description(self):
+        return "Inform when a step is complete, before moving to the next step:"
+
+    @staticmethod
+    def custom_properties():
+        return ['DETAILS']
diff --git a/messages/sirji_messages/messages/actions/step_started.py b/messages/sirji_messages/messages/actions/step_started.py
new file mode 100644
index 00000000..dbfa93c2
--- /dev/null
+++ b/messages/sirji_messages/messages/actions/step_started.py
@@ -0,0 +1,31 @@
+import textwrap
+
+from sirji_messages import AgentEnum, ActionEnum
+from .base import BaseMessages
+
+
+class StepStartedMessage(BaseMessages):
+
+    def __init__(self):
+        self.action = ActionEnum.STEP_STARTED.name
+        self.from_agent = AgentEnum.CODER.name
+        self.to_agent = AgentEnum.USER.name
+
+        super().__init__()
+
+    def template_payload_part(self):
+        return textwrap.dedent("""
+          DETAILS: {details}
+          """)
+
+    def sample(self):
+        return self.generate({
+            "details": "Example details 'Step # started'."
+        })
+
+    def description(self):
+        return "Inform the step you are about to start on:"
+
+    @staticmethod
+    def custom_properties():
+        return ['DETAILS']
diff --git a/messages/sirji_messages/messages/actions/steps.py b/messages/sirji_messages/messages/actions/steps.py
new file mode 100644
index 00000000..0cb6f80a
--- /dev/null
+++ b/messages/sirji_messages/messages/actions/steps.py
@@ -0,0 +1,32 @@
+import textwrap
+
+from sirji_messages import AgentEnum, ActionEnum
+from .base import BaseMessages
+
+
+class StepsMessage(BaseMessages):
+
+    def __init__(self):
+        self.action = ActionEnum.STEPS.name
+        self.from_agent = AgentEnum.PLANNER.name
+        self.to_agent = AgentEnum.CODER.name
+
+        super().__init__()
+
+    def template_payload_part(self):
+        return textwrap.dedent("""
+          DETAILS:
+          {details}
+          """)
+
+    def sample(self):
+        return self.generate({
+            "details": "List of steps to solve the problem. Each step must start with 'Step #: ....'."
+        })
+
+    def description(self):
+        return "List of steps required to solve the problem:"
+
+    @staticmethod
+    def custom_properties():
+        return ['DETAILS']
\ No newline at end of file
diff --git a/messages/sirji_messages/messages/actions/train_using_search_term.py b/messages/sirji_messages/messages/actions/train_using_search_term.py
new file mode 100644
index 00000000..fe0a548f
--- /dev/null
+++ b/messages/sirji_messages/messages/actions/train_using_search_term.py
@@ -0,0 +1,31 @@
+import textwrap
+
+from sirji_messages import AgentEnum, ActionEnum
+from .base import BaseMessages
+
+
+class TrainUsingSearchTermMessage(BaseMessages):
+
+    def __init__(self):
+        self.action = ActionEnum.TRAIN_USING_SEARCH_TERM.name
+        self.from_agent = AgentEnum.CODER.name
+        self.to_agent = AgentEnum.RESEARCHER.name
+
+        super().__init__()
+
+    def template_payload_part(self):
+        return textwrap.dedent("""
+          TERM: {term}
+          """)
+
+    def sample(self):
+        return self.generate({
+            "term": "The search term that needs to be crawled and trained on."
+        })
+
+    def description(self):
+        return "Train using a search term:"
+
+    @staticmethod
+    def custom_properties():
+        return ['TERM']
diff --git a/messages/sirji_messages/messages/actions/train_using_url.py b/messages/sirji_messages/messages/actions/train_using_url.py
new file mode 100644
index 00000000..2810133d
--- /dev/null
+++ b/messages/sirji_messages/messages/actions/train_using_url.py
@@ -0,0 +1,31 @@
+import textwrap
+
+from sirji_messages import AgentEnum, ActionEnum
+from .base import BaseMessages
+
+
+class TrainUsingUrlMessage(BaseMessages):
+
+    def __init__(self):
+        self.action = ActionEnum.TRAIN_USING_URL.name
+        self.from_agent = AgentEnum.CODER.name
+        self.to_agent = AgentEnum.RESEARCHER.name
+
+        super().__init__()
+
+    def template_payload_part(self):
+        return textwrap.dedent("""
+          URL: {url}
+          """)
+
+    def sample(self):
+        return self.generate({
+            "url": "The URL that needs to be crawled, parsed, and trained to answer questions."
+        })
+
+    def description(self):
+        return "Train using a URL:"
+
+    @staticmethod
+    def custom_properties():
+        return ['URL']
diff --git a/messages/sirji_messages/messages/actions/training_output.py b/messages/sirji_messages/messages/actions/training_output.py
new file mode 100644
index 00000000..e2808df8
--- /dev/null
+++ b/messages/sirji_messages/messages/actions/training_output.py
@@ -0,0 +1,32 @@
+import textwrap
+
+from sirji_messages import AgentEnum, ActionEnum
+from .base import BaseMessages
+
+
+class TrainingOutputMessage(BaseMessages):
+
+    def __init__(self):
+        self.action = ActionEnum.TRAINING_OUTPUT.name
+        self.from_agent = AgentEnum.RESEARCHER.name
+        self.to_agent = AgentEnum.CODER.name
+
+        super().__init__()
+
+    def template_payload_part(self):
+        return textwrap.dedent("""
+          DETAILS:
+          {details}
+          """)
+
+    def sample(self):
+        return self.generate({
+            "details": "Multilined training output."
+        })
+
+    def description(self):
+        return "The training output:"
+
+    @staticmethod
+    def custom_properties():
+        return ['DETAILS']
diff --git a/messages/sirji_messages/messages/factory.py b/messages/sirji_messages/messages/factory.py
new file mode 100644
index 00000000..46d2a7b4
--- /dev/null
+++ b/messages/sirji_messages/messages/factory.py
@@ -0,0 +1,69 @@
+from sirji_messages import ActionEnum
+
+# Import all the message classes
+from .actions.acknowledge import AcknowledgeMessage
+from .actions.answer import AnswerMessage
+from .actions.execute_command import ExecuteCommandMessage
+from .actions.run_server import RunServerMessage
+from .actions.create_file import CreateFileMessage
+from .actions.generate_steps import GenerateStepsMessage
+from .actions.problem_statement import ProblemStatementMessage
+from .actions.infer import InferMessage
+from .actions.inform import InformMessage
+from .actions.install_package import InstallPackageMessage
+from .actions.output import OutputMessage
+from .actions.question import QuestionMessage
+from .actions.response import ResponseMessage
+from .actions.solution_complete import SolutionCompleteMessage
+from .actions.step_completed import StepCompletedMessage
+from .actions.step_started import StepStartedMessage
+from .actions.steps import StepsMessage
+from .actions.train_using_search_term import TrainUsingSearchTermMessage
+from .actions.train_using_url import TrainUsingUrlMessage
+from .actions.feedback import FeedbackMessage
+from .actions.training_output import TrainingOutputMessage
+from .actions.read_dir import ReadDirMessage
+from .actions.read_file import ReadFileMessage
+
+
+class MetaMessageFactory(type):
+    def __getitem__(cls, action):
+        try:
+            action_type = ActionEnum[action]
+            return cls._message_map[action_type]
+        except KeyError as e:
+            raise AttributeError(
+                f"{action} not found in MessageFactory or ActionType Enum.") from e
+
+# Use the metaclass for our factory
+
+
+class MessageFactory(metaclass=MetaMessageFactory):
+
+    # Map ActionTypes to their respective message classes
+    _message_map = {
+        ActionEnum.ACKNOWLEDGE: AcknowledgeMessage,
+        ActionEnum.ANSWER: AnswerMessage,
+        ActionEnum.EXECUTE_COMMAND: ExecuteCommandMessage,
+        ActionEnum.RUN_SERVER: RunServerMessage,
+        ActionEnum.CREATE_FILE: CreateFileMessage,
+        ActionEnum.GENERATE_STEPS: GenerateStepsMessage,
+        ActionEnum.PROBLEM_STATEMENT: ProblemStatementMessage,
+        ActionEnum.INFER: InferMessage,
+        ActionEnum.INFORM: InformMessage,
+        ActionEnum.INSTALL_PACKAGE: InstallPackageMessage,
+        ActionEnum.OUTPUT: OutputMessage,
+        ActionEnum.QUESTION: QuestionMessage,
+        ActionEnum.RESPONSE: ResponseMessage,
+        ActionEnum.SOLUTION_COMPLETE: SolutionCompleteMessage,
+        ActionEnum.STEP_COMPLETED: StepCompletedMessage,
+        ActionEnum.STEP_STARTED: StepStartedMessage,
+        ActionEnum.STEPS: StepsMessage,
+        ActionEnum.TRAIN_USING_SEARCH_TERM: TrainUsingSearchTermMessage,
+        ActionEnum.TRAIN_USING_URL: TrainUsingUrlMessage,
+        ActionEnum.FEEDBACK: FeedbackMessage,
+        ActionEnum.TRAINING_OUTPUT: TrainingOutputMessage,
+        ActionEnum.READ_DIR: ReadDirMessage,
+        ActionEnum.READ_FILE: ReadFileMessage
+    }
+
diff --git a/messages/sirji_messages/parser.py b/messages/sirji_messages/parser.py
new file mode 100644
index 00000000..a0927579
--- /dev/null
+++ b/messages/sirji_messages/parser.py
@@ -0,0 +1,118 @@
+from .messages.factory import MessageFactory
+from .custom_exceptions import MessageParsingError, MessageValidationError
+from .action_enum import ActionEnum
+from sirji_messages import validate_permission
+
+
+def parse(input_message):
+    input_message = _discard_format_deviations(input_message)
+    lines = _validate_message(input_message)
+    message_info = _extract_message_info(lines)
+    payload = "\n".join(lines[3:])
+    custom_properties = MessageFactory[message_info["ACTION"]].custom_properties()
+
+    payload_dict = _parse_payload(payload, custom_properties)
+
+    parsed_message = {"FROM": message_info["FROM"], "TO": message_info["TO"],
+                      "ACTION": message_info["ACTION"], **payload_dict}
+
+    if parsed_message.get("ACTION") == ActionEnum.STEPS.name:
+        parsed_message = {
+            **parsed_message, "PARSED_STEPS": _parse_steps(parsed_message)}
+
+    return parsed_message
+
+def _discard_format_deviations(input_message):
+    input_message = input_message.strip()
+
+    # Locate the positions of the first and last backticks (`).
+    start_index = input_message.find("```")
+    end_index = input_message.rfind("```")
+
+    # Extract the message prefix, message, and message suffix.
+    message_prefix = input_message[:start_index].strip()
+    # Including backticks as part of the message content.
+    message_content = input_message[start_index:end_index+3].strip()
+    message_suffix = input_message[end_index+3:].strip()
+
+    return message_content
+
+
+def _validate_message(message):
+    message = message.strip()
+    if not (message.startswith("```") and message.endswith("```")):
+        raise MessageValidationError("Message must start and end with ```")
+    message = message[3:-3].strip()
+    lines = message.split("\n")
+    if len(lines) < 4:
+        raise MessageValidationError(
+            "Message does not meet the minimum length requirement")
+    return lines
+
+
+def _extract_message_info(lines):
+    try:
+        message_info = {line.split(":", 1)[0].strip(): line.split(":", 1)[
+            1].strip() for line in lines[:3]}
+    except (IndexError, ValueError):
+        raise MessageParsingError(
+            "Message metadata (FROM, TO, ACTION) is improperly formed")
+
+    from_str = message_info.get("FROM")
+    to_str = message_info.get("TO")
+    action_str = message_info.get("ACTION")
+
+    if not all([from_str, to_str, action_str]):
+        raise MessageValidationError(
+            "FROM, TO, and ACTION fields are required")
+    if not validate_permission(from_str, to_str, action_str):
+        raise MessageValidationError(
+            f"{from_str} is not allowed to send {action_str} action to {to_str}")
+
+    return message_info
+
+
+def _parse_payload(payload, custom_properties):
+    payload_dict = {}
+    last_key = None
+    for line in payload.split("\n"):
+        if ":" in line:
+            key, value = [part.strip() for part in line.split(":", 1)]
+            if key in custom_properties:
+                payload_dict[key] = value
+                last_key = key
+            elif last_key:
+                payload_dict[last_key] += "\n" + line
+        elif last_key:
+            payload_dict[last_key] += "\n" + line
+    return payload_dict
+
+
+def _parse_steps(parsed_message):
+    details = parsed_message.get("DETAILS", "")
+    parsed_steps = []
+    
+    if any(line.strip().startswith("Step") for line in details.split("\n")): 
+        current_step_number = None
+        current_step_description = ""
+        for line in details.split("\n"):
+            line = line.strip() 
+            if line.startswith("Step"):
+                if current_step_number is not None:
+                    parsed_steps.append(
+                        {"step": current_step_number.strip(), "description": current_step_description.strip()})
+                current_step_number, current_step_description = [part.strip() for part in line.split(":", 1)]
+            else:
+                current_step_description += f"\n{line}"
+        if current_step_number is not None:
+            parsed_steps.append({"step": current_step_number.strip(),
+                                "description": current_step_description.strip()})
+    else:
+        step_number = 1
+        for line in details.split("\n"):
+            line = line.strip()
+            if line:  
+                parsed_steps.append({"step": f"Step {step_number}", "description": line})
+                step_number += 1
+
+    return parsed_steps
\ No newline at end of file
diff --git a/messages/sirji_messages/permissions.py b/messages/sirji_messages/permissions.py
new file mode 100644
index 00000000..7590aa6e
--- /dev/null
+++ b/messages/sirji_messages/permissions.py
@@ -0,0 +1,72 @@
+from sirji_messages import ActionEnum, AgentEnum
+
+# Defining permissions_dict
+# Key: (from_agent, to_agent)
+# Value: set of allowed actions between the agents
+permissions_dict = {
+    (AgentEnum.CODER, AgentEnum.EXECUTOR): {
+        ActionEnum.CREATE_FILE,
+        ActionEnum.EXECUTE_COMMAND,
+        ActionEnum.RUN_SERVER,
+        ActionEnum.INSTALL_PACKAGE,
+        ActionEnum.READ_FILE,
+        ActionEnum.READ_DIR
+    },
+    (AgentEnum.CODER, AgentEnum.PLANNER): (
+        ActionEnum.GENERATE_STEPS
+    ),
+    (AgentEnum.CODER, AgentEnum.RESEARCHER): (
+        ActionEnum.TRAIN_USING_URL,
+        ActionEnum.INFER
+    ),
+    (AgentEnum.CODER, AgentEnum.USER): (
+        ActionEnum.QUESTION,
+        ActionEnum.STEP_STARTED,
+        ActionEnum.STEP_COMPLETED,
+        ActionEnum.SOLUTION_COMPLETE
+    ),
+    (AgentEnum.EXECUTOR, AgentEnum.CODER): (
+        ActionEnum.OUTPUT
+    ),
+    (AgentEnum.PLANNER, AgentEnum.CODER): (
+        ActionEnum.STEPS
+    ),
+    (AgentEnum.RESEARCHER, AgentEnum.CODER): (
+        ActionEnum.RESPONSE,
+        ActionEnum.TRAINING_OUTPUT
+    ),
+    (AgentEnum.USER, AgentEnum.CODER): (
+        ActionEnum.PROBLEM_STATEMENT,
+        ActionEnum.ANSWER,
+        ActionEnum.ACKNOWLEDGE,
+        ActionEnum.FEEDBACK
+    )
+}
+
+
+def validate_permission(from_str, to_str, action_str):
+    try:
+        # Convert the string representations to corresponding enum values
+        from_agent = AgentEnum[from_str]
+        to_agent = AgentEnum[to_str]
+        action = ActionEnum[action_str]
+
+        # Check if the (from_agent, to_agent) pair is in the permissions_dict
+        if (from_agent, to_agent) in permissions_dict:
+            # Get the allowed actions for the given agent pair
+            allowed_actions = permissions_dict[(from_agent, to_agent)]
+
+            # If allowed_actions is directly an ActionEnum (not iterable), wrap it in a set
+            if isinstance(allowed_actions, ActionEnum):
+                allowed_actions = {allowed_actions}
+            # If it's a tuple or another iterable
+            elif not isinstance(allowed_actions, set):
+                allowed_actions = set(allowed_actions)
+
+            return action in allowed_actions
+        else:
+            # The agent pair does not have any permissions_dict defined
+            return False
+    except KeyError:
+        # The provided string values do not match any enum
+        return False
diff --git a/sirji/messages/__init__.py b/messages/sirji_messages/system_prompts/__init__.py
similarity index 100%
rename from sirji/messages/__init__.py
rename to messages/sirji_messages/system_prompts/__init__.py
diff --git a/sirji/prompts/__init__.py b/messages/sirji_messages/system_prompts/agents/__init__.py
similarity index 100%
rename from sirji/prompts/__init__.py
rename to messages/sirji_messages/system_prompts/agents/__init__.py
diff --git a/messages/sirji_messages/system_prompts/agents/base.py b/messages/sirji_messages/system_prompts/agents/base.py
new file mode 100644
index 00000000..7db5484e
--- /dev/null
+++ b/messages/sirji_messages/system_prompts/agents/base.py
@@ -0,0 +1,141 @@
+from abc import ABC, abstractmethod
+
+from sirji_messages import MessageFactory, ActionEnum
+
+
+class AgentSystemPromptBase(ABC):
+
+    def system_prompt(self):
+        system_prompt = ""
+        system_prompt += f"{self.intro()}\n"
+
+        system_prompt += "Your job is to:"
+        system_prompt += f"{self.responsibilities()}\n"
+
+        system_prompt += f"{self.can_interact_with()}"
+
+        system_prompt += f"{self.other_agents_capabilities()}"
+
+        system_prompt += f"{self.interactions()}"
+
+        system_prompt += f"{self.ending_prompt()}"
+
+        # print(system_prompt)
+
+        return system_prompt
+
+    @abstractmethod
+    def short_name(self):
+        """
+        Agent short name - taken from correct agent enum.
+        """
+        pass
+
+    @abstractmethod
+    def intro(self):
+        """
+        Agent intro.
+        """
+        pass
+
+    @abstractmethod
+    def responsibilities(self):
+        """
+        Agent responsibilities.
+        """
+        pass
+
+    @abstractmethod
+    def capabilities(self):
+        """
+        Agent capabilties.
+        """
+        pass
+
+    @abstractmethod
+    def ending_prompt(self):
+        """
+        Agent ending message for the system prompt.
+        """
+        pass
+
+    def can_interact_with(self):
+        can_interact_with_message = ""
+
+        other_agents = self.get_other_agents()
+
+        if not other_agents:
+            return can_interact_with_message
+
+        can_interact_with_message += f"You can interact with:\n"
+        for agent_enum in other_agents:
+            can_interact_with_message += f"- {agent_enum.full_name} ({agent_enum.name}).\n"
+
+        can_interact_with_message += "\n"
+
+        return can_interact_with_message
+
+    def other_agents_capabilities(self):
+        other_agents_capabilities_message = ""
+
+        other_agents = self.get_other_agents()
+
+        if not other_agents:
+            return other_agents_capabilities_message
+
+        for agent_enum in other_agents:
+            other_agents_capabilities_message += f"{agent_enum.name} can:"
+
+            from sirji_messages.system_prompts.factory import AgentSystemPromptFactory
+
+            other_agents_capabilities_message += f"{AgentSystemPromptFactory[agent_enum.name]().capabilities()}\n\n"
+
+        return other_agents_capabilities_message
+
+    def interactions(self):
+        interactions_message = "Allowed responses and their formats are described below.\n\n"
+
+        # We create a helper function to reduce redundancy
+        def append_action_messages(actions, heading):
+            action_message = heading
+
+            # Check if 'actions' is a single ActionEnum object and not an iterable (list, set, etc.)
+            if isinstance(actions, ActionEnum):
+                actions = [actions]  # Convert it into a list
+
+            for count, action_enum in enumerate(actions, start=1):
+                message_obj = MessageFactory[action_enum.name]()
+                action_message += f"{count}. {message_obj.description()}\n{message_obj.sample()}\n"
+
+            return action_message
+
+        from sirji_messages import permissions_dict
+
+        # Iterating through each key-value pair in the permissions_dict
+        for agents, actions in permissions_dict.items():
+            from_agent, to_agent = agents  # Unpacking the key which is a tuple of two agents
+
+            # Dynamically determining which messages to append based on the agent's role
+            if self.short_name() == from_agent.name:
+                heading = f"The allowed responses that you can send to the {to_agent.name} are:\n\n"
+                interactions_message += append_action_messages(
+                    actions, heading)
+            elif self.short_name() == to_agent.name:
+                heading = f"The allowed responses from the {from_agent.name} to you are:\n\n"
+                interactions_message += append_action_messages(
+                    actions, heading)
+
+        return interactions_message
+
+    def get_other_agents(self):
+        other_agents = set()
+        from sirji_messages import permissions_dict
+        for agents, _ in permissions_dict.items():
+            from_agent, to_agent = agents  # Unpacking the key which is a tuple of two agents
+
+            if self.short_name() == from_agent.name:
+                other_agents.add(to_agent)
+            elif self.short_name() == to_agent.name:
+                other_agents.add(from_agent)
+
+        return other_agents
diff --git a/messages/sirji_messages/system_prompts/agents/coder.py b/messages/sirji_messages/system_prompts/agents/coder.py
new file mode 100644
index 00000000..66d52031
--- /dev/null
+++ b/messages/sirji_messages/system_prompts/agents/coder.py
@@ -0,0 +1,64 @@
+import textwrap
+
+from sirji_messages import AgentEnum, ActionEnum
+
+from .base import AgentSystemPromptBase
+
+
+class CoderSystemPrompt(AgentSystemPromptBase):
+
+    def name(self):
+        return AgentEnum.CODER.full_name
+
+    def short_name(self):
+        return AgentEnum.CODER.name
+
+    def intro(self):
+        return textwrap.dedent(f"""
+          You are a {self.name()} ({self.short_name()}), an expert software engineer skilled in writing code across various areas. Your expertise includes multiple backend programming languages, frontend technologies, DevOps technologies, databases, caching, third-party libraries, and APIs. Additionally, you are proficient in writing test cases, creating documentation, and programmatically solving given problem statements (PS).
+          """)
+    # - Ensure that every code and command execution output is always written in a log file using packages like "tee" and execute in background using "nohup". For example: nohup command | tee <<log file name which can be used to check the output>> &
+    def responsibilities(self):
+        return textwrap.dedent(f"""
+            - Ensure you write a user story for the problem statement and get it finalized with the {AgentEnum.USER.name}:
+                - First understand the given problem statement, write a user story by enhancing the requirements and features in problem statement.
+                - Make sure that key points, information and data is always present in the user story.
+                - Have the features listed as points in the user story.
+                - Then send this user story as a {ActionEnum.QUESTION.name} to get a confirmation on. The message details should look like: "\nHere's the user story based on your problem statement:\n\n <<user story>> \n\nDoes everything look good, or would you like any changes?"
+            - Once user story is finalized, by considering the user story and the features in it prepare a list of architecture components needed and get it finalized with the {AgentEnum.USER.name}:
+                - Prepare list of architecture components including things like programming language (example: Python, Node.js), Framework (example: Express, Flask), Database (example: PostgreSQL, MySQL), Cache (example: Memcache, Redis), etc.
+                - Do not include Hosting component.
+                - Have only those components which can run on or can be called from a local development environment.
+                - Have the architecture components listed as points and sub-points.
+                - Then send the architecture components as a {ActionEnum.QUESTION.name} to get a confirmation on. The message details should look like: "\nHere are the main parts of the architecture::\n\n <<architecture components>> \n\nDoes everything look good to you, or do you need any adjustments?"
+            - Once architecture components are finalized, then {ActionEnum.GENERATE_STEPS.name} to solve the problem statement:
+                - Share the problem statement, finalized user story and finalized architecture compoents with {AgentEnum.PLANNER.name} to get the list of steps on how to solve the problem statement (PS).
+                - The message details shared with the planner should look like "\n\nProblem Statement (PS):\n<<problem statement>>\n\nUser Story:\n<<finalized user story>>\n\nArchitecture Components:\n<<finalized architecture components>>"
+            - Once steps are generated, Solve the problem statement programatically by following the generated steps:
+                - Write concrete code and not just conceptualize or simulate it.
+                - Always write end-to-end working code in accordance with the problem statement, user story, and architectural components. Strictly don't just create an outline or folder structure.
+                - Use Python, if the programming language cannot be inferred from PS.
+                - Identify URLs (Excluding GitHub) present in the PS, on which you have no knowledge and want to be trained on or researched. After training, infer from the trained content/knowledge. If the response of the inference has new URLs (Excluding GitHub), on which you have no knowledge, you can get trained on them too.
+                - Follow the generated steps sequentially to solve the PS.
+                - Follow secure software development practices while generating code.
+                - Ensure that you don't create any file/folder outside of current directory, i.e. './'
+                - Read the GitHub files by first cloning the repository and then reading the files at once.
+            - Strictly make sure:
+                - To respond with the {ActionEnum.RUN_SERVER.name} action to run a server like continuous process.
+                - Do not execute chained commands with the {ActionEnum.EXECUTE_COMMAND.name} action.
+                - Ensure if the step is to verify whether a command is installed or not, you check them one at a time.
+                - Ensure to always call 'pwd' command before using 'cd' command (either in a standalone way).
+                - On getting file not found error for files which you created earlier, reason might be a wrongly assumed current directory. Check the present working directory using 'pwd' command.
+                - Pose explicit questions only when you have no other option but to reach out to the user. An ideal question format is direct, such as "Should I ...?" Additionally, it is crucial to ask such questions only when absolutely necessary.
+                - Always notify about the step started before you start working on it. Similarly, notify about the step completed before you move to the next step.
+                - Only interact with the agents listed below using the allowed responses, also mentioned below.
+                - Ensure the response is also enclosed inside 3 backticks (```).
+                - If error is encountered in execution of a command, then find the root cause and resolve the issue by solving the root cause.
+                - End the conversation if your solution is complete as per the problem statement, finalized user story and finalized architecture components.
+            """)
+
+    def capabilities(self):
+        return ""  # This should not be called.
+
+    def ending_prompt(self):
+        return ""
diff --git a/messages/sirji_messages/system_prompts/agents/executor.py b/messages/sirji_messages/system_prompts/agents/executor.py
new file mode 100644
index 00000000..84fd8048
--- /dev/null
+++ b/messages/sirji_messages/system_prompts/agents/executor.py
@@ -0,0 +1,36 @@
+import textwrap
+
+from sirji_messages import AgentEnum, ActionEnum
+
+from .planner import PlannerSystemPrompt
+
+from .base import AgentSystemPromptBase
+
+
+class ExecutorSystemPrompt(AgentSystemPromptBase):
+
+    def name(self):
+        return AgentEnum.EXECUTOR.full_name
+
+    def short_name(self):
+        return AgentEnum.EXECUTOR.name
+
+    def intro(self):
+        return ""  # This will never be called.
+
+    def responsibilities(self):
+        return ""  # This will never be called.
+
+    def capabilities(self):
+        return textwrap.dedent("""
+          - Interact with macOS terminal.
+          - Run server like continuous process using {ActionEnum.RUN_SERVER.name} action.
+          - Create or update files using {ActionEnum.CREATE_FILE.name} action.
+          - Read content from a single file using {ActionEnum.READ_FILE.name} action.
+          - Read the content from all files in a directory and all its subdirectories at once (separated by a divider) using {ActionEnum.READ_DIR.name} action.
+          - Install required dependencies/packages/libraries using {ActionEnum.INSTALL_PACKAGE.name} action.
+          - Execute code and command using {ActionEnum.EXECUTE_COMMAND.name} action. Chained commands (using &&) are not supported.
+          """)
+
+    def ending_prompt(self):
+        return ""  # This will never be called.
diff --git a/messages/sirji_messages/system_prompts/agents/planner.py b/messages/sirji_messages/system_prompts/agents/planner.py
new file mode 100644
index 00000000..ec146bcd
--- /dev/null
+++ b/messages/sirji_messages/system_prompts/agents/planner.py
@@ -0,0 +1,40 @@
+import textwrap
+
+from sirji_messages import AgentEnum
+
+from .base import AgentSystemPromptBase
+
+
+class PlannerSystemPrompt(AgentSystemPromptBase):
+
+    def name(self):
+        return AgentEnum.PLANNER.full_name
+
+    def short_name(self):
+        return AgentEnum.PLANNER.name
+
+    def intro(self):
+        return textwrap.dedent(f"""
+          You are a {self.name()} ({self.short_name()}), an expert software architect skilled in devising steps to solve the given problem statement (PS). These steps will be utilized by an expert software developer whose expertise includes multiple backend programming languages, frontend technologies, DevOps technologies, databases, caching, third-party libraries, and APIs, and who is proficient in writing test cases and creating documentation.
+          """)
+    # - Ensure that all the steps are about either create file or install package or execute command or execute code to debug or git clone or read single file or read all files in a directory (including those in its subdirectories).
+    # - Don't explain the steps further using sub-steps.
+    # - Focus on particular data points given in the PS and not solve the problem in an over-generalized manner.
+    def responsibilities(self):
+        return textwrap.dedent(f"""
+          - Pay close attention to problem statement, user story and architecture components while generating steps to solve the problem programmatically.
+          - Ensure that each step in the list of steps should start with 'Step #: ....'
+          - Explain, in each step, what exact task needs to be done. For example: In Javascript file, implement xyz methods having abc capabilities.
+          - Directing the installation of programming language-specific packages or libraries in the project folder, ensuring there is no interference with machine-level/global installations. For instance, utilize venv for installing Python dependencies and package.json for Node.js dependencies.
+          - Ensuring that you first verify whether a system-level command is already installed. If it is not installed, you must inquire whether you have permission to install it.
+          - Ensure not to ask to create a new folder for the project. Treat '.' as the project root.
+          - Always add a step to execute the code and evaluate the response output. If the response has errors, solve them before moving ahead.
+          """)
+
+    def capabilities(self):
+        return textwrap.dedent("""
+          - Generate steps to solve the problem programmatically.
+          """)
+
+    def ending_prompt(self):
+        return ""
diff --git a/messages/sirji_messages/system_prompts/agents/researcher.py b/messages/sirji_messages/system_prompts/agents/researcher.py
new file mode 100644
index 00000000..ff7886f8
--- /dev/null
+++ b/messages/sirji_messages/system_prompts/agents/researcher.py
@@ -0,0 +1,29 @@
+import textwrap
+
+from sirji_messages import AgentEnum
+
+from .base import AgentSystemPromptBase
+
+
+class ResearcherSystemPrompt(AgentSystemPromptBase):
+
+    def name(self):
+        return AgentEnum.RESEARCHER.full_name
+
+    def short_name(self):
+        return AgentEnum.RESEARCHER.name
+
+    def intro(self):
+        return ""  # This will never be called.
+
+    def responsibilities(self):
+        return ""  # This will never be called.
+
+    def capabilities(self):
+        return textwrap.dedent("""
+            - Crawl the given URL and get trained on its content.
+            - Infer from the trained content/knowledge and try to respond to questions asked.
+            """)
+
+    def ending_prompt(self):
+        return ""  # This will never be called.
diff --git a/messages/sirji_messages/system_prompts/agents/user.py b/messages/sirji_messages/system_prompts/agents/user.py
new file mode 100644
index 00000000..4361ad00
--- /dev/null
+++ b/messages/sirji_messages/system_prompts/agents/user.py
@@ -0,0 +1,33 @@
+import textwrap
+
+from sirji_messages import AgentEnum
+
+from .planner import PlannerSystemPrompt
+
+from .base import AgentSystemPromptBase
+
+
+class UserSystemPrompt(AgentSystemPromptBase):
+
+    def name(self):
+        return AgentEnum.USER.full_name
+
+    def short_name(self):
+        return AgentEnum.USER.name
+
+    def intro(self):
+        return ""  # This will never be called.
+
+    def responsibilities(self):
+        return ""  # This will never be called.
+
+    def capabilities(self):
+        return textwrap.dedent("""
+          - Provide the problem statement (PS).   
+          - Provide the answer to the question asked.             
+          - Acknowledge started or completed steps.
+          - Acknowledge the final solution.
+          """)
+
+    def ending_prompt(self):
+        return ""  # This will never be called.
diff --git a/messages/sirji_messages/system_prompts/factory.py b/messages/sirji_messages/system_prompts/factory.py
new file mode 100644
index 00000000..3c53938f
--- /dev/null
+++ b/messages/sirji_messages/system_prompts/factory.py
@@ -0,0 +1,32 @@
+from sirji_messages import AgentEnum
+
+# Import all the agent system prompt classes
+from .agents.coder import CoderSystemPrompt
+from .agents.planner import PlannerSystemPrompt
+from .agents.researcher import ResearcherSystemPrompt
+from .agents.executor import ExecutorSystemPrompt
+from .agents.user import UserSystemPrompt
+
+
+class MetaAgentSystemPromptFactory(type):
+    def __getitem__(cls, agent):
+        try:
+            agent_enum = AgentEnum[agent]
+            return cls._agent_system_prompt_map[agent_enum]
+        except KeyError as e:
+            raise AttributeError(
+                f"{agent} not found in AgentSystemPromptFactory or Agent Enum.") from e
+
+# Use the metaclass for our factory
+
+
+class AgentSystemPromptFactory(metaclass=MetaAgentSystemPromptFactory):
+
+    # Map ActionTypes to their respective message classes
+    _agent_system_prompt_map = {
+        AgentEnum.CODER: CoderSystemPrompt,
+        AgentEnum.PLANNER: PlannerSystemPrompt,
+        AgentEnum.RESEARCHER: ResearcherSystemPrompt,
+        AgentEnum.EXECUTOR: ExecutorSystemPrompt,
+        AgentEnum.USER: UserSystemPrompt
+    }
diff --git a/sirji/agents/coder.py b/sirji/agents/coder.py
deleted file mode 100644
index 3251edf0..00000000
--- a/sirji/agents/coder.py
+++ /dev/null
@@ -1,60 +0,0 @@
-from openai import OpenAI
-import os
-
-from sirji.prompts.coder import CoderPrompt
-
-from sirji.tools.logger import coder as cLogger
-
-
-class SingletonMeta(type):
-    """
-    This is a metaclass that will be used to create a Singleton class.
-    It ensures that only one instance of the Singleton class exists.
-    """
-    _instances = {}
-
-    def __call__(cls, *args, **kwargs):
-        # If an instance of the class does not exist, create one; otherwise, return the existing one.
-        if cls not in cls._instances:
-            cls._instances[cls] = super().__call__(*args, **kwargs)
-        return cls._instances[cls]
-
-
-class Coder(metaclass=SingletonMeta):
-    def __init__(self):
-        # Initialize conversation
-        self.conversation = [
-            {'role': 'system', 'content': CoderPrompt().system_prompt()}]
-
-        # Fetch OpenAI API key from environment variable
-        api_key = os.environ.get("SIRJI_OPENAI_API_KEY")
-
-        if api_key is None:
-            raise ValueError(
-                "OpenAI API key is not set as an environment variable")
-
-        # Initialize OpenAI client
-        self.client = OpenAI(api_key=api_key)
-
-    def message(self, input_message):
-        # Append user's input message to the conversation
-        self.conversation.append({'role': 'user', 'content': input_message})
-
-        cLogger.info(f"Incoming: \n{input_message}")
-
-        cLogger.info("Calling OpenAI Chat Completions API")
-
-        chat_completion = self.client.chat.completions.create(
-            messages=self.conversation,
-            model="gpt-4-turbo-preview",
-            temperature=0,
-            max_tokens=4095,
-        )
-
-        response_message = chat_completion.choices[0].message.content
-
-        self.conversation.append(
-            {'role': 'assistant', 'content': response_message})
-        
-        cLogger.info(f"Outgoing: \n{response_message}")
-        return response_message
diff --git a/sirji/agents/planner.py b/sirji/agents/planner.py
deleted file mode 100644
index 82c56f01..00000000
--- a/sirji/agents/planner.py
+++ /dev/null
@@ -1,67 +0,0 @@
-from openai import OpenAI
-import os
-
-from sirji.prompts.planner import PlannerPrompt
-from sirji.messages.parser import MessageParser
-from sirji.storage.steps import initialize_steps
-
-from sirji.tools.logger import planner as pLogger
-
-class SingletonMeta(type):
-    """
-    This is a metaclass that will be used to create a Singleton class.
-    It ensures that only one instance of the Singleton class exists.
-    """
-    _instances = {}
-
-    def __call__(cls, *args, **kwargs):
-        # If an instance of the class does not exist, create one; otherwise, return the existing one.
-        if cls not in cls._instances:
-            cls._instances[cls] = super().__call__(*args, **kwargs)
-        return cls._instances[cls]
-
-
-class Planner(metaclass=SingletonMeta):
-    def __init__(self):
-        # Initialize conversation
-        self.conversation = [
-            {'role': 'system', 'content': PlannerPrompt('Coding Agent', 'Coder').system_prompt()}]
-
-        # Fetch OpenAI API key from environment variable
-        api_key = os.environ.get("SIRJI_OPENAI_API_KEY")
-
-        if api_key is None:
-            raise ValueError(
-                "OpenAI API key is not set as an environment variable")
-
-        # Initialize OpenAI client
-        self.client = OpenAI(api_key=api_key)
-        pass
-
-    def message(self, input_message):
-        # Append user's input message to the conversation
-        self.conversation.append({'role': 'user', 'content': input_message})
-
-        chat_completion = self.client.chat.completions.create(
-            messages=self.conversation,
-            model="gpt-4-turbo-preview",
-            temperature=0,
-            max_tokens=4095,
-        )
-
-        response_message = chat_completion.choices[0].message.content
-
-        # Storing 
-        self.store_steps(response_message)
-
-        self.conversation.append(
-            {'role': 'assistant', 'content': response_message})
-        return response_message
-    
-    def store_steps(self, message):
-        steps = MessageParser.parse_steps(message)
-
-        for index, description in enumerate(steps, start=1):
-            pLogger.info(f"[ ] Step {index}: {description}")
-
-        initialize_steps(steps)
diff --git a/sirji/agents/researcher/__init__.py b/sirji/agents/researcher/__init__.py
deleted file mode 100644
index 2cb22e6c..00000000
--- a/sirji/agents/researcher/__init__.py
+++ /dev/null
@@ -1,3 +0,0 @@
-from .researcher import Researcher
-
-__all__ = ['Researcher']
diff --git a/sirji/agents/researcher/embeddings/factory.py b/sirji/agents/researcher/embeddings/factory.py
deleted file mode 100644
index 05902030..00000000
--- a/sirji/agents/researcher/embeddings/factory.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from .openai_assistant import OpenAIAssistantEmbeddings
-from sirji.tools.logger import researcher as logger
-
-class EmbeddingsFactory:
-    @classmethod
-    def get_instance(cls, embeddings_type):
-        logger.info(f"Getting instance for embeddings type: {embeddings_type} from embeddings factory")
-
-        if embeddings_type == "openai_assistant":
-            return OpenAIAssistantEmbeddings()
-        else:
-            raise ValueError(
-                "Unsupported embeddings_type. Please provide a valid embeddings_type.")
-
-# Example usage:
-# embeddings = EmbeddingsFactory.get_instance("openai_assistant")
diff --git a/sirji/agents/researcher/inferer/factory.py b/sirji/agents/researcher/inferer/factory.py
deleted file mode 100644
index 6b01ffaa..00000000
--- a/sirji/agents/researcher/inferer/factory.py
+++ /dev/null
@@ -1,16 +0,0 @@
-from .openai_assistant import OpenAIAssistantInferer
-from sirji.tools.logger import researcher as logger
-
-class InfererFactory:
-    @classmethod
-    def get_instance(cls, inferer_type):
-        logger.info(f"Getting instance for instance type: {inferer_type} from inferer factory")
-        
-        if inferer_type == "openai_assistant":
-            return OpenAIAssistantInferer()
-        else:
-            raise ValueError(
-                "Unsupported embeddings_type. Please provide a valid embeddings_type.")
-
-# Example usage:
-# embeddings = InfererFactory.get_instance("openai_assistant")
diff --git a/sirji/agents/researcher/researcher.py b/sirji/agents/researcher/researcher.py
deleted file mode 100644
index ea53b007..00000000
--- a/sirji/agents/researcher/researcher.py
+++ /dev/null
@@ -1,86 +0,0 @@
-import os
-
-from .embeddings.factory import EmbeddingsFactory
-from .inferer.factory import InfererFactory
-from sirji.tools.crawler import crawl_urls
-from sirji.tools.search import search_for
-from sirji.tools.logger import researcher as logger
-from sirji.messages.parser import MessageParser
-
-from sirji.messages.response import ResponseMessage
-from sirji.messages.output import OutputMessage
-
-class Researcher:
-    def __init__(self, embeddings_type, inferer_type):
-        logger.info("Initializing researcher...")
-
-        # Initialize the embeddings manager
-        self.embeddings_manager = EmbeddingsFactory.get_instance(
-            embeddings_type)
-
-        # Initialize the inferer
-        self.inferer = InfererFactory.get_instance(inferer_type)
-
-        self.research_folder = 'workspace/researcher'
-
-        logger.info("Completed initializing researcher")
-
-    def message(self, input_message):     
-        parsed_message = MessageParser.parse(input_message)
-
-        action = parsed_message.get("ACTION")
-        from_user = parsed_message.get("FROM")
-        to_user = parsed_message.get("TO")
-
-        if action == "train-using-search-term":
-            logger.info(f"Training using search term: {parsed_message.get('TERM')}")
-            self.search_and_index(parsed_message.get('TERM'))
-
-            return OutputMessage(to_user).generate(from_user, {
-                "details": "Training using search term completed successfully"  # Noting returned from the search_and_index method
-            })
-        elif action == "train-using-url":
-            logger.info(f"Training using URL: {parsed_message.get('URL')}")
-            self.index([parsed_message.get('URL')])
-            return OutputMessage(to_user).generate(from_user, {
-                "details": "Training using url completed successfully"  # Noting returned from the index method
-            })
-        elif action == "infer":
-            logger.info(f"Infering: {parsed_message.get('DETAILS')}")
-
-            response = self.infer(parsed_message.get('DETAILS'))
-            return ResponseMessage(to_user).generate(from_user, {
-                "details": response 
-            })
-        
-    def index(self, urls):
-        logger.info("Started indexing the URLs")
-        crawl_urls(urls, self.research_folder)
-        self._reindex()
-        logger.info("Completed indexing the URLs")
-
-    def search_and_index(self, query):
-        urls = search_for(query)
-        self.index(urls)
-
-    def infer(self, problem_statement):
-        retrieved_context = self.embeddings_manager.retrieve_context(
-            problem_statement)
-
-        return self.inferer.infer(retrieved_context, problem_statement)
-
-    def _reindex(self):
-        logger.info("Recursively indexing the research folder")
-
-        # Recursively walk through all folders and sub-folders
-        for root, dirs, files in os.walk(self.research_folder):
-            for folder in dirs:
-                folder_path = os.path.join(root, folder)
-                print(folder_path)
-
-                # Call embeddings_manager.index on each folder
-                response = self.embeddings_manager.index(folder_path)
-                # Optional: You may want to do something with the response
-
-        logger.info(
-            "Completed recursive indexing of the research folder")
diff --git a/sirji/config/config_loader.py b/sirji/config/config_loader.py
deleted file mode 100644
index 8bc916ab..00000000
--- a/sirji/config/config_loader.py
+++ /dev/null
@@ -1,10 +0,0 @@
-import os
-import json
-
-def load_config(file_name):
-    # Get the directory of the current script
-    dir_path = os.path.dirname(os.path.realpath(__file__))
-    # Construct full file path
-    file_path = os.path.join(dir_path, file_name)
-    with open(file_path, 'r') as file:
-        return json.load(file)
\ No newline at end of file
diff --git a/sirji/config/google_config.json b/sirji/config/google_config.json
deleted file mode 100644
index 9e9f1199..00000000
--- a/sirji/config/google_config.json
+++ /dev/null
@@ -1,4 +0,0 @@
-{
-  "google_search_url": "https://www.googleapis.com/customsearch/v1",
-  "search_results_per_page": 10
-}
\ No newline at end of file
diff --git a/sirji/agents/README.md b/sirji/desktop/agents/README.md
similarity index 100%
rename from sirji/agents/README.md
rename to sirji/desktop/agents/README.md
diff --git a/sirji/chat_providers/factory.py b/sirji/desktop/agents/__init__.py
similarity index 100%
rename from sirji/chat_providers/factory.py
rename to sirji/desktop/agents/__init__.py
diff --git a/sirji/chat_providers/openai.py b/sirji/desktop/agents/debugger.py
similarity index 100%
rename from sirji/chat_providers/openai.py
rename to sirji/desktop/agents/debugger.py
diff --git a/sirji/agents/executor.py b/sirji/desktop/agents/executor.py
similarity index 100%
rename from sirji/agents/executor.py
rename to sirji/desktop/agents/executor.py
diff --git a/sirji/agents/user.py b/sirji/desktop/agents/user.py
similarity index 100%
rename from sirji/agents/user.py
rename to sirji/desktop/agents/user.py
diff --git a/demos/1-generate-readme/README.md b/sirji/desktop/demos/1-generate-readme/README.md
similarity index 100%
rename from demos/1-generate-readme/README.md
rename to sirji/desktop/demos/1-generate-readme/README.md
diff --git a/demos/2-routes-and-models-from-specs/DBML.txt b/sirji/desktop/demos/2-routes-and-models-from-specs/DBML.txt
similarity index 100%
rename from demos/2-routes-and-models-from-specs/DBML.txt
rename to sirji/desktop/demos/2-routes-and-models-from-specs/DBML.txt
diff --git a/demos/2-routes-and-models-from-specs/OpenAPI.yml b/sirji/desktop/demos/2-routes-and-models-from-specs/OpenAPI.yml
similarity index 100%
rename from demos/2-routes-and-models-from-specs/OpenAPI.yml
rename to sirji/desktop/demos/2-routes-and-models-from-specs/OpenAPI.yml
diff --git a/demos/2-routes-and-models-from-specs/README.md b/sirji/desktop/demos/2-routes-and-models-from-specs/README.md
similarity index 100%
rename from demos/2-routes-and-models-from-specs/README.md
rename to sirji/desktop/demos/2-routes-and-models-from-specs/README.md
diff --git a/demos/3-stock-price-website/README.md b/sirji/desktop/demos/3-stock-price-website/README.md
similarity index 100%
rename from demos/3-stock-price-website/README.md
rename to sirji/desktop/demos/3-stock-price-website/README.md
diff --git a/sirji/desktop/demos/README.md b/sirji/desktop/demos/README.md
new file mode 100644
index 00000000..88901c9f
--- /dev/null
+++ b/sirji/desktop/demos/README.md
@@ -0,0 +1,47 @@
+# Sirji Demos
+
+Following are some demo videos illustrating [Sirji](https://github.com/sirji-ai/sirji)'s approach to various problems.
+
+## Demo 1: Generate README
+
+### Problem Statement
+
+```
+Clone the "sirji" repository: git@github.com:sirji-ai/sirji.git and checkout the "readme-demo" branch. Read and understand the content of all the files in the folder: "sirji/tools/crawler". Afterward, create a README.md file in that folder giving sample usages, a brief description of the steps performed, etc.
+```
+
+### Demo Video
+
+Watch on YouTube: https://www.youtube.com/watch?v=B9kWchC6lKw
+
+[![Sirji generates README](https://github.com/sirji-ai/sirji/assets/7627517/9fa49bee-8488-403d-a8b2-54c5aff05628)](https://www.youtube.com/watch?v=B9kWchC6lKw)
+
+
+## Demo 2: Generate Models, Migrations, and Routes
+
+### Problem Statement
+```
+Create an Express App. Use PostgreSQL as the database and Sequelize as ORM.
+Create migration files and model files by understanding the DBML file content: https://github.com/sirji-ai/sirji/blob/main/demos/2-routes-and-models-from-specs/DBML.txt
+Create API routes in modular files by understanding the OpenAPI specs file content: https://github.com/sirji-ai/sirji/blob/main/demos/2-routes-and-models-from-specs/OpenAPI.yml
+```
+
+### Demo Video
+
+Watch on YouTube: https://www.youtube.com/watch?v=Bl73DOqXrtg
+
+[![Sirji generating specs based models, migrations and routes](https://github.com/sirji-ai/sirji/assets/7627517/ceac9f4f-ec4b-48ec-bd75-7de8fbafa376)](https://www.youtube.com/watch?v=Bl73DOqXrtg)
+
+
+## Demo 3: Stock Price Website
+
+### Problem Statement
+
+```
+Generate a simple website to show the last closing, opening and current price of user entered stock.
+```
+### Demo Video
+
+Watch on YouTube: https://www.youtube.com/watch?v=fmYhbTGSTlA
+
+[![Sirji generating stock price website](https://github.com/sirji-ai/sirji/assets/7627517/a7abda56-fc99-406d-8b94-553ec8946799)](https://www.youtube.com/watch?v=fmYhbTGSTlA)
diff --git a/docs/setup-steps.md b/sirji/desktop/docs/setup-steps.md
similarity index 100%
rename from docs/setup-steps.md
rename to sirji/desktop/docs/setup-steps.md
diff --git a/sirji/storage/steps.py b/sirji/desktop/storage/steps.py
similarity index 100%
rename from sirji/storage/steps.py
rename to sirji/desktop/storage/steps.py
diff --git a/sirji/view/README.md b/sirji/desktop/view/README.md
similarity index 100%
rename from sirji/view/README.md
rename to sirji/desktop/view/README.md
diff --git a/sirji/executor.py b/sirji/desktop/view/__init__.py
similarity index 100%
rename from sirji/executor.py
rename to sirji/desktop/view/__init__.py
diff --git a/sirji/view/chat.py b/sirji/desktop/view/chat.py
similarity index 100%
rename from sirji/view/chat.py
rename to sirji/desktop/view/chat.py
diff --git a/sirji/view/screen.py b/sirji/desktop/view/screen.py
similarity index 100%
rename from sirji/view/screen.py
rename to sirji/desktop/view/screen.py
diff --git a/sirji/view/terminal.py b/sirji/desktop/view/terminal.py
similarity index 100%
rename from sirji/view/terminal.py
rename to sirji/desktop/view/terminal.py
diff --git a/sirji/messages/acknowledge.py b/sirji/messages/acknowledge.py
deleted file mode 100644
index 32a71814..00000000
--- a/sirji/messages/acknowledge.py
+++ /dev/null
@@ -1,25 +0,0 @@
-import textwrap
-from sirji.messages.base import BaseMessages
-
-
-class AcknowledgeMessage(BaseMessages):
-
-    def template(self):
-        return textwrap.dedent("""
-            ```
-            FROM: {implementor}
-            TO: {interactor}
-            ACTION: acknowledge
-            DETAILS: Sure.
-            ```
-            """)
-
-    def sample(self, interactor):
-        return self.generate(interactor, {})
-
-    def description(self):
-        return "The message acknowledgment:"
-
-    @staticmethod
-    def properties():
-        return ['FROM', 'TO', 'ACTION', 'DETAILS']
diff --git a/sirji/messages/answer.py b/sirji/messages/answer.py
deleted file mode 100644
index 59b79946..00000000
--- a/sirji/messages/answer.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import textwrap
-
-from sirji.messages.base import BaseMessages
-
-
-class AnswerMessage(BaseMessages):
-
-    def template(self):
-        return textwrap.dedent("""
-			```
-			FROM: {implementor}
-			TO: {interactor}
-			ACTION: answer
-			DETAILS: {details}
-			```
-			""")
-
-    def sample(self, interactor):
-        return self.generate(interactor, {
-            "details": "Multiline answer to the asked question."
-        })
-
-    def description(self):
-        return "The answer to the asked question:"
-
-    @staticmethod
-    def properties():
-        return ['FROM', 'TO', 'ACTION', 'DETAILS']
diff --git a/sirji/messages/base.py b/sirji/messages/base.py
deleted file mode 100644
index d7018da9..00000000
--- a/sirji/messages/base.py
+++ /dev/null
@@ -1,44 +0,0 @@
-from abc import ABC, abstractmethod
-
-
-class BaseMessages(ABC):
-
-    def __init__(self, implementor):
-        self.implementor = implementor
-
-    @abstractmethod
-    def template(self):
-        """
-        Message template.
-        """
-        pass
-
-    @abstractmethod
-    def sample(self, interactor):
-        """
-        Sample message using the message template.
-
-        Parameters:
-        interactor (str): The interactor represents the entity interacting with the message template. 
-        """
-        pass
-
-    @abstractmethod
-    def description(self):
-        """
-        Description of the message.
-        """
-        pass
-
-    @staticmethod
-    @abstractmethod
-    def properties():
-        """
-        List of available properties of the message.
-        """
-        pass
-
-    def generate(self, interactor, obj):
-        obj["implementor"] = self.implementor
-        obj["interactor"] = interactor
-        return self.template().format(**obj)
diff --git a/sirji/messages/create_file.py b/sirji/messages/create_file.py
deleted file mode 100644
index a9262a8c..00000000
--- a/sirji/messages/create_file.py
+++ /dev/null
@@ -1,31 +0,0 @@
-import textwrap
-
-from sirji.messages.base import BaseMessages
-
-
-class CreateFileMessage(BaseMessages):
-
-    def template(self):
-        return textwrap.dedent("""
-          ```
-          FROM: {interactor}
-          TO: {implementor}
-          ACTION: create-file
-          FILENAME: {file_name}
-          CONTENT:
-          {content}
-          ```
-          """)
-
-    def sample(self, interactor):
-        return self.generate(interactor, {
-            "file_name": "File name along with the path.",
-            "content": "Multiline file content. It should start from a new line."
-        })
-
-    def description(self):
-        return "To create a file (with content):"
-
-    @staticmethod
-    def properties():
-        return ['FROM', 'TO', 'ACTION', 'FILENAME', 'CONTENT']
diff --git a/sirji/messages/execute_command.py b/sirji/messages/execute_command.py
deleted file mode 100644
index 20fc35b0..00000000
--- a/sirji/messages/execute_command.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import textwrap
-
-from sirji.messages.base import BaseMessages
-
-
-class ExecuteCommandMessage(BaseMessages):
-
-    def template(self):
-        return textwrap.dedent("""
-        ```
-        FROM: {interactor}
-        TO: {implementor}
-        ACTION: execute-command
-        COMMAND: {command}							 
-        ```
-        """)
-
-    def sample(self, interactor):
-        return self.generate(interactor, {
-            "command": "Command to execute."
-        })
-
-    def description(self):
-        return "To execute a command:"
-
-    @staticmethod
-    def properties():
-        return ['FROM', 'TO', 'ACTION', 'COMMAND']
diff --git a/sirji/messages/feedback.py b/sirji/messages/feedback.py
deleted file mode 100644
index fa4c965d..00000000
--- a/sirji/messages/feedback.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import textwrap
-
-from sirji.messages.base import BaseMessages
-
-
-class FeedbackMessage(BaseMessages):
-
-    def template(self):
-        return textwrap.dedent("""
-          ```
-          FROM: {implementor}
-          TO: {interactor}
-          ACTION: feedback
-          DETAILS: {details}
-          ```
-          """)
-
-    def sample(self, interactor):
-        return self.generate(interactor, {
-            "details": "Feedback on the solution provided."
-        })
-
-    def description(self):
-        return "Feedback on the solution provided:"
-
-    @staticmethod
-    def properties():
-        return ['FROM', 'TO', 'ACTION', 'DETAILS']
diff --git a/sirji/messages/generate_steps.py b/sirji/messages/generate_steps.py
deleted file mode 100644
index d5c5e89b..00000000
--- a/sirji/messages/generate_steps.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import textwrap
-
-from sirji.messages.base import BaseMessages
-
-
-class GenerateStepsMessage(BaseMessages):
-
-    def template(self):
-        return textwrap.dedent("""
-          ```
-          FROM: {interactor}
-          TO: {implementor}
-          ACTION: generate-steps
-          DETAILS: {details}
-          ```
-          """)
-
-    def sample(self, interactor):
-        return self.generate(interactor, {
-            "details": "Problem statement (PS) here."
-        })
-
-    def description(self):
-        return "Generate steps for the problem statement (PS):"
-
-    @staticmethod
-    def properties():
-        return ['FROM', 'TO', 'ACTION', 'DETAILS']
diff --git a/sirji/messages/infer.py b/sirji/messages/infer.py
deleted file mode 100644
index 3d4fbe2e..00000000
--- a/sirji/messages/infer.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import textwrap
-
-from sirji.messages.base import BaseMessages
-
-
-class InferMessage(BaseMessages):
-
-    def template(self):
-        return textwrap.dedent("""
-          ```
-          FROM: {interactor}
-          TO: {implementor}
-          ACTION: infer
-          DETAILS: {details}
-          ```
-          """)
-
-    def sample(self, interactor):
-        return self.generate(interactor, {
-            "details": "Question to extract answers, code examples, GitHub URLs, relevant external URLs from the trained content."
-        })
-
-    def description(self):
-        return "Ask questions on the trained content:"
-
-    @staticmethod
-    def properties():
-        return ['FROM', 'TO', 'ACTION', 'DETAILS']
diff --git a/sirji/messages/inform.py b/sirji/messages/inform.py
deleted file mode 100644
index 4825fa29..00000000
--- a/sirji/messages/inform.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import textwrap
-
-from sirji.messages.base import BaseMessages
-
-
-class InformMessage(BaseMessages):
-
-    def template(self):
-        return textwrap.dedent("""
-          ```
-          FROM: {interactor}
-          TO: {implementor}
-          ACTION: inform
-          DETAILS: {details}
-          ```
-          """)
-
-    def sample(self, interactor):
-        return self.generate(interactor, {
-            "details": "Details of your decisions or choices."
-        })
-
-    def description(self):
-        return "To inform about your decisions or choices:"
-
-    @staticmethod
-    def properties():
-        return ['FROM', 'TO', 'ACTION', 'DETAILS']
diff --git a/sirji/messages/install_package.py b/sirji/messages/install_package.py
deleted file mode 100644
index a09a8d78..00000000
--- a/sirji/messages/install_package.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import textwrap
-
-from sirji.messages.base import BaseMessages
-
-
-class InstallPackageMessage(BaseMessages):
-
-    def template(self):
-        return textwrap.dedent("""
-          ```
-          FROM: {interactor}
-          TO: {implementor}
-          ACTION: install-package
-          COMMAND: {command}
-          ```
-          """)
-
-    def sample(self, interactor):
-        return self.generate(interactor, {
-            "command": "Command to install the package or library."
-        })
-
-    def description(self):
-        return "To install a package or library:"
-
-    @staticmethod
-    def properties():
-        return ['FROM', 'TO', 'ACTION', 'COMMAND']
diff --git a/sirji/messages/output.py b/sirji/messages/output.py
deleted file mode 100644
index 2d772ed7..00000000
--- a/sirji/messages/output.py
+++ /dev/null
@@ -1,29 +0,0 @@
-import textwrap
-
-from sirji.messages.base import BaseMessages
-
-
-class OutputMessage(BaseMessages):
-
-    def template(self):
-        return textwrap.dedent("""
-          ```
-          FROM: {implementor}
-          TO: {interactor}
-          ACTION: output
-          DETAILS:
-          {details}
-          ```
-          """)
-
-    def sample(self, interactor):
-        return self.generate(interactor, {
-            "details": "Multilined response."
-        })
-
-    def description(self):
-        return "The response output:"
-
-    @staticmethod
-    def properties():
-        return ['FROM', 'TO', 'ACTION', 'DETAILS']
diff --git a/sirji/messages/parser.py b/sirji/messages/parser.py
deleted file mode 100644
index 53f3137f..00000000
--- a/sirji/messages/parser.py
+++ /dev/null
@@ -1,139 +0,0 @@
-from sirji.messages.parser_factory import ParserFactory
-
-
-def discard_format_deviations(input_message):
-    input_message = input_message.strip()
-
-    # Locate the positions of the first and last backticks (`).
-    start_index = input_message.find("```")
-    end_index = input_message.rfind("```")
-
-    # Extract the message prefix, message, and message suffix.
-    message_prefix = input_message[:start_index].strip()
-    # Including backticks as part of the message content.
-    message_content = input_message[start_index:end_index+3].strip()
-    message_suffix = input_message[end_index+3:].strip()
-
-    return message_content
-
-
-class MessageParser:
-    @staticmethod
-    def parse(input_message):
-
-        input_message = discard_format_deviations(input_message)
-
-        # Check if the input message starts and ends with ```
-        if not input_message.startswith("```") or not input_message.endswith("```"):
-            return "Invalid message"
-
-        # Remove the ``` from the start and end
-        input_message = input_message.strip("```").strip()
-
-        # Split the input message into lines
-        lines = input_message.split("\n")
-
-        # Check if there are at least 4 lines
-        if len(lines) < 4:
-            return "Invalid message"
-
-        # Extract FROM, TO, and ACTION from the first 3 lines
-        from_user = lines[0].split(":")[1].strip()
-        to_user = lines[1].split(":")[1].strip()
-        action = lines[2].split(":")[1].strip()
-
-        # Check if any of FROM, TO, or ACTION is missing
-        if not from_user or not to_user or not action:
-            return "Invalid message"
-
-        # Get the rest of the lines as the payload
-        payload = "\n".join(lines[3:])
-
-        available_properties = ParserFactory.get_parser(action).properties()
-
-        # Now, for payload split it by new line if line include `:` then consider it a key. But check if the key is available in the available_properties. If yes, then consider it as a key, otherwise consider it as a value of the previous key.
-
-        payload = payload.split("\n")
-
-        payload_dict = {}
-        last_key = None
-
-        for line in payload:
-            if ":" in line:
-                key, value = line.split(":", 1)
-                key = key.strip()
-                value = value
-
-                if key in available_properties:
-                    payload_dict[key] = value
-                    last_key = key
-                elif last_key:
-                    payload_dict[last_key] += "\n" + line
-            elif last_key:
-                payload_dict[last_key] += "\n" + line
-        input_text_obj = {
-            "FROM": from_user,
-            "TO": to_user,
-            "ACTION": action,
-        }
-        # Merge the payload_dict with input_text_obj
-
-        output = {**input_text_obj, **payload_dict}
-
-        return output
-
-        # Example usage
-
-        # input_message = """
-        # ```
-        # FROM: user
-        # TO: sirji
-        # ACTION: question
-        # DETAILS: How to create a file in Python?
-        # ```
-        # """
-
-        # print(Parser.parse(input_message))
-        # {'FROM': 'user', 'TO': 'sirji', 'ACTION': 'question', 'DETAILS': 'How to create a file in Python?'}
-    
-    @staticmethod
-    def parse_steps(message):
-        parsed_message = MessageParser.parse(message)
-
-        if parsed_message.get("ACTION") == "steps":
-            details = parsed_message.get("DETAILS")
-            
-            # Initialize an empty array to store the parsed steps
-            parsed_steps = []
-
-            # Split the details into lines
-            steps_lines = details.strip().split("\n")
-
-            # Initialize variables to store current step details
-            current_step_number = None
-            current_step_description = ""
-
-            # Iterate over each line to parse the steps
-            for line in steps_lines:
-                # Check if the line starts with "Step" followed by a number
-                if line.strip().startswith("Step"):
-                    # If it's a new step, add the previous step to the parsed_steps list
-                    if current_step_number is not None:
-                        parsed_steps.append(current_step_description.strip())
-                    
-                    # Extract the step number and description from the line
-                    step_parts = line.split(":", 1)
-                    current_step_number = step_parts[0].strip()
-                    current_step_description = step_parts[1].strip()
-                else:
-                    # If the line doesn't start with "Step", it's part of the current step's description
-                    # Append it to the current step description
-                    current_step_description += "\n" + line.strip()
-
-            # Add the last step to the parsed_steps list
-            if current_step_number is not None:
-                parsed_steps.append(current_step_description.strip())
-            
-            return parsed_steps
-        else:
-            return "Invalid message"
\ No newline at end of file
diff --git a/sirji/messages/parser_factory.py b/sirji/messages/parser_factory.py
deleted file mode 100644
index 51644414..00000000
--- a/sirji/messages/parser_factory.py
+++ /dev/null
@@ -1,64 +0,0 @@
-from sirji.messages.acknowledge import AcknowledgeMessage
-from sirji.messages.answer import AnswerMessage
-from sirji.messages.execute_command import ExecuteCommandMessage
-from sirji.messages.create_file import CreateFileMessage
-from sirji.messages.generate_steps import GenerateStepsMessage
-from sirji.messages.problem_statement import ProblemStatementMessage
-from sirji.messages.infer import InferMessage
-from sirji.messages.inform import InformMessage
-from sirji.messages.install_package import InstallPackageMessage
-from sirji.messages.output import OutputMessage
-from sirji.messages.question import QuestionMessage
-from sirji.messages.response import ResponseMessage
-from sirji.messages.solution_complete import SolutionCompleteMessage
-from sirji.messages.step_completed import StepCompletedMessage
-from sirji.messages.step_started import StepStartedMessage
-from sirji.messages.steps import StepsMessage
-from sirji.messages.train_using_search_term import TrainUsingSearchTermMessage
-from sirji.messages.train_using_url import TrainUsingUrlMessage
-from sirji.messages.feedback import FeedbackMessage
-
-class ParserFactory:
-    
-    @staticmethod
-    def get_parser(action_type):
-        if action_type == "acknowledge":
-            return AcknowledgeMessage
-        elif action_type == "answer":
-            return AnswerMessage
-        elif action_type == "execute-command":
-            return ExecuteCommandMessage
-        elif action_type == "create-file":
-            return CreateFileMessage
-        elif action_type == "generate-steps":
-            return GenerateStepsMessage
-        elif action_type == "problem-statement":
-            return ProblemStatementMessage
-        elif action_type == "infer":
-            return InferMessage
-        elif action_type == "inform":
-            return InformMessage
-        elif action_type == "install-package":
-            return InstallPackageMessage
-        elif action_type == "output":
-            return OutputMessage
-        elif action_type == "question":
-            return QuestionMessage
-        elif action_type == "response":
-            return ResponseMessage
-        elif action_type == "solution-complete":
-            return SolutionCompleteMessage
-        elif action_type == "step-completed":
-            return StepCompletedMessage
-        elif action_type == "step-started":
-            return StepStartedMessage
-        elif action_type == "steps":
-            return StepsMessage
-        elif action_type == "train-using-search-term":
-            return TrainUsingSearchTermMessage
-        elif action_type == "train-using-url":
-            return TrainUsingUrlMessage
-        elif action_type == "feedback":
-            return FeedbackMessage
-        else:
-            raise Exception("Invalid action type: " + action_type)
diff --git a/sirji/messages/problem_statement.py b/sirji/messages/problem_statement.py
deleted file mode 100644
index c1711e28..00000000
--- a/sirji/messages/problem_statement.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import textwrap
-
-from sirji.messages.base import BaseMessages
-
-
-class ProblemStatementMessage(BaseMessages):
-
-    def template(self):
-        return textwrap.dedent("""
-          ```
-          FROM: {interactor}
-          TO: {implementor}
-          ACTION: problem-statement
-          DETAILS: {details}
-          ```
-          """)
-
-    def sample(self, interactor):
-        return self.generate(interactor, {
-            "details": "The problem statement (PS) that needs to be solved programmatically."
-        })
-
-    def description(self):
-        return "The problem statement (PS) that needs to be solved programmatically:"
-
-    @staticmethod
-    def properties():
-        return ['FROM', 'TO', 'ACTION', 'DETAILS']
diff --git a/sirji/messages/question.py b/sirji/messages/question.py
deleted file mode 100644
index 0a7ec0ee..00000000
--- a/sirji/messages/question.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import textwrap
-
-from sirji.messages.base import BaseMessages
-
-
-class QuestionMessage(BaseMessages):
-
-    def template(self):
-        return textwrap.dedent("""
-          ```
-          FROM: {interactor}
-          TO: {implementor}						 
-          ACTION: question
-          DETAILS: {details}
-          ```
-          """)
-
-    def sample(self, interactor):
-        return self.generate(interactor, {
-            "details": "A concise question to understand the problem statement better."
-        })
-
-    def description(self):
-        return "Ask questions to understand the problem statement better:"
-
-    @staticmethod
-    def properties():
-        return ['FROM', 'TO', 'ACTION', 'DETAILS']
diff --git a/sirji/messages/response.py b/sirji/messages/response.py
deleted file mode 100644
index b645aca3..00000000
--- a/sirji/messages/response.py
+++ /dev/null
@@ -1,29 +0,0 @@
-import textwrap
-
-from sirji.messages.base import BaseMessages
-
-
-class ResponseMessage(BaseMessages):
-
-    def template(self):
-        return textwrap.dedent("""
-          ```
-          FROM: {implementor}
-          TO: {interactor}
-          ACTION: response
-          DETAILS:
-          {details}
-          ```
-          """)
-
-    def sample(self, interactor):
-        return self.generate(interactor, {
-            "details": "Multilined response."
-        })
-
-    def description(self):
-        return "The response output:"
-
-    @staticmethod
-    def properties():
-        return ['FROM', 'TO', 'ACTION', 'DETAILS']
diff --git a/sirji/messages/solution_complete.py b/sirji/messages/solution_complete.py
deleted file mode 100644
index 82a03fcb..00000000
--- a/sirji/messages/solution_complete.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import textwrap
-
-from sirji.messages.base import BaseMessages
-
-
-class SolutionCompleteMessage(BaseMessages):
-
-    def template(self):
-        return textwrap.dedent("""
-          ```
-          FROM: {interactor}
-          TO: {implementor}
-          ACTION: solution-complete
-          DETAILS: {details}
-          ```
-          """)
-
-    def sample(self, interactor):
-        return self.generate(interactor, {
-            "details": "A concise message to inform that the solution is complete."
-        })
-
-    def description(self):
-        return "To inform that the solution to the problem is complete:"
-
-    @staticmethod
-    def properties():
-        return ['FROM', 'TO', 'ACTION', 'DETAILS']
diff --git a/sirji/messages/step_completed.py b/sirji/messages/step_completed.py
deleted file mode 100644
index 1a4a298a..00000000
--- a/sirji/messages/step_completed.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import textwrap
-
-from sirji.messages.base import BaseMessages
-
-
-class StepCompletedMessage(BaseMessages):
-
-    def template(self):
-        return textwrap.dedent("""
-          ```
-          FROM: {interactor}
-          TO: {implementor}
-          ACTION: step-completed
-          DETAILS: {details}
-          ```
-          """)
-
-    def sample(self, interactor):
-        return self.generate(interactor, {
-            "details": "Example details 'Step # completed'."
-        })
-
-    def description(self):
-        return "Inform when a step is complete, before moving to the next step:"
-
-    @staticmethod
-    def properties():
-        return ['FROM', 'TO', 'ACTION', 'DETAILS']
diff --git a/sirji/messages/step_started.py b/sirji/messages/step_started.py
deleted file mode 100644
index 8ad0438e..00000000
--- a/sirji/messages/step_started.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import textwrap
-
-from sirji.messages.base import BaseMessages
-
-
-class StepStartedMessage(BaseMessages):
-
-    def template(self):
-        return textwrap.dedent("""
-          ```
-          FROM: {interactor}
-          TO: {implementor}
-          ACTION: step-started
-          DETAILS: {details}
-          ```
-          """)
-
-    def sample(self, interactor):
-        return self.generate(interactor, {
-            "details": "Example details 'Step # started'."
-        })
-
-    def description(self):
-        return "Inform the step you are about to start on:"
-
-    @staticmethod
-    def properties():
-        return ['FROM', 'TO', 'ACTION', 'DETAILS']
diff --git a/sirji/messages/steps.py b/sirji/messages/steps.py
deleted file mode 100644
index a695d1da..00000000
--- a/sirji/messages/steps.py
+++ /dev/null
@@ -1,29 +0,0 @@
-import textwrap
-
-from sirji.messages.base import BaseMessages
-
-
-class StepsMessage(BaseMessages):
-
-    def template(self):
-        return textwrap.dedent("""
-          ```
-          FROM: {implementor}
-          TO: {interactor}
-          ACTION: steps
-          DETAILS:
-          {details}
-          ```
-          """)
-
-    def sample(self, interactor):
-        return self.generate(interactor, {
-            "details": "List of steps to solve the problem. Each step is described as 'Step #: ....'."
-        })
-
-    def description(self):
-        return "List of steps required to solve the problem:"
-
-    @staticmethod
-    def properties():
-        return ['FROM', 'TO', 'ACTION', 'DETAILS']
diff --git a/sirji/messages/train_using_search_term.py b/sirji/messages/train_using_search_term.py
deleted file mode 100644
index 2ebea4c9..00000000
--- a/sirji/messages/train_using_search_term.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import textwrap
-
-from sirji.messages.base import BaseMessages
-
-
-class TrainUsingSearchTermMessage(BaseMessages):
-
-    def template(self):
-        return textwrap.dedent("""
-          ```
-          FROM: {interactor}
-          TO: {implementor}
-          ACTION: train-using-search-term
-          TERM: {term}
-          ```
-          """)
-
-    def sample(self, interactor):
-        return self.generate(interactor, {
-            "term": "The search term that needs to be crawled and trained on."
-        })
-
-    def description(self):
-        return "Train using a search term:"
-
-    @staticmethod
-    def properties():
-        return ['FROM', 'TO', 'ACTION', 'TERM']
diff --git a/sirji/messages/train_using_url.py b/sirji/messages/train_using_url.py
deleted file mode 100644
index 9ce67217..00000000
--- a/sirji/messages/train_using_url.py
+++ /dev/null
@@ -1,28 +0,0 @@
-import textwrap
-
-from sirji.messages.base import BaseMessages
-
-
-class TrainUsingUrlMessage(BaseMessages):
-
-    def template(self):
-        return textwrap.dedent("""
-          ```
-          FROM: {interactor}
-          TO: {implementor}
-          ACTION: train-using-url
-          URL: {url}
-          ```
-          """)
-
-    def sample(self, interactor):
-        return self.generate(interactor, {
-            "url": "The URL that needs to be crawled, parsed, and trained to answer questions."
-        })
-
-    def description(self):
-        return "Train using a URL:"
-
-    @staticmethod
-    def properties():
-        return ['FROM', 'TO', 'ACTION', 'URL']
diff --git a/sirji/prompts/base.py b/sirji/prompts/base.py
deleted file mode 100644
index a79580ad..00000000
--- a/sirji/prompts/base.py
+++ /dev/null
@@ -1,151 +0,0 @@
-from abc import ABC, abstractmethod
-
-
-class PromptGeneratorBase(ABC):
-
-    def __init__(self, caller_name, caller_short_name):
-        self.caller_name = caller_name
-        self.caller_short_name = caller_short_name
-
-    def system_prompt(self):
-        system_prompt = ""
-        system_prompt += f"{self.intro_prompt()}\n"
-        system_prompt += "Your job is to:"
-        system_prompt += f"{self.responsibilities_prompt()}\n"
-        system_prompt += f"{self.caller_agent_interaction_message_prompt()}\n"
-        system_prompt += f"{self.can_interact_with_other_agents_prompt()}"
-        system_prompt += f"{self.other_agents_capability_prompt()}"
-        system_prompt += f"{self.other_agents_interaction_message_prompt()}"
-        system_prompt += f"{self.ending_prompt()}"
-
-        #print(system_prompt)
-
-        return system_prompt
-
-    @abstractmethod
-    def name(self):
-        """
-        Agent name.
-        """
-        pass
-
-    @abstractmethod
-    def short_name(self):
-        """
-        Agent short name.
-        """
-        pass
-
-    @abstractmethod
-    def intro_prompt(self):
-        """
-        Agent intro for the system prompt.
-        """
-        pass
-
-    @abstractmethod
-    def responsibilities_prompt(self):
-        """
-        Agent responsibilities for the system prompt.
-        """
-        pass
-
-    @abstractmethod
-    def capabilities_prompt(self):
-        """
-        Agent capabilities for the system prompt.
-        """
-        pass
-
-    @abstractmethod
-    def interact_with(self):
-        """
-        List of agent class instances that this agent can interact with.
-        """
-        pass
-
-    @abstractmethod
-    def incoming_message_instances(self):
-        """
-        List of incoming message class instances that this agent can receive.
-        """
-        pass
-
-    @abstractmethod
-    def outgoing_message_instances(self):
-        """
-        List of outgoing message class instances that this agent can send.
-        """
-        pass
-
-    @abstractmethod
-    def ending_prompt(self):
-        """
-        Agent ending message for the system prompt.
-        """
-        pass
-
-    def can_interact_with_other_agents_prompt(self):
-        can_interact_with_prompt = ""
-
-        if self.interact_with():
-            can_interact_with_prompt += f"You can interact with:\n"
-
-            for instance in self.interact_with():
-                can_interact_with_prompt += f"- {instance.name()} ({instance.short_name()}).\n"
-
-            can_interact_with_prompt += "\n"
-
-        return can_interact_with_prompt
-
-    def other_agents_capability_prompt(self):
-        interact_with_prompt = ""
-
-        for instance in self.interact_with():
-            interact_with_prompt += f"{instance.short_name()} can:"
-            interact_with_prompt += f"{instance.capabilities_prompt()}\n\n"
-
-        return interact_with_prompt
-
-    def other_agents_interaction_message_prompt(self):
-        interaction_message_prompt = ""
-        if self.interact_with():
-            interaction_message_prompt += "Allowed responses and their formats are described below.\n\n"
-
-            for instance in self.interact_with():
-                interaction_message_prompt += f"The allowed responses that you can send to the {instance.short_name()} are:\n\n"
-                incoming_message_display_count = 1
-                incoming_message_instances = instance.incoming_message_instances()
-                for message_instance in incoming_message_instances:
-                    interaction_message_prompt += f"{incoming_message_display_count}. {message_instance.description()}\n"
-                    interaction_message_prompt += f"{message_instance.sample(self.short_name())}\n"
-                    incoming_message_display_count += 1
-                interaction_message_prompt += f"The allowed responses from the {instance.short_name()} to you are:\n\n"
-                outgoing_message_display_count = 1
-                outgoing_message_instances = instance.outgoing_message_instances()
-                for message_instance in outgoing_message_instances:
-                    interaction_message_prompt += f"{outgoing_message_display_count}. {message_instance.description()}\n"
-                    interaction_message_prompt += f"{message_instance.sample(self.short_name())}\n"
-                    outgoing_message_display_count += 1
-        return interaction_message_prompt
-
-    def caller_agent_interaction_message_prompt(self):
-        interaction_message_prompt = ""
-        if self.incoming_message_instances() or self.outgoing_message_instances():
-            interaction_message_prompt += "Allowed responses and their formats are described below.\n\n"
-
-            interaction_message_prompt += f"The allowed responses from the {self.caller_short_name} to you are:\n\n"
-            incoming_message_display_count = 1
-            incoming_message_instances = self.incoming_message_instances()
-            for message_instance in incoming_message_instances:
-                interaction_message_prompt += f"{incoming_message_display_count}. {message_instance.description()}\n"
-                interaction_message_prompt += f"{message_instance.sample(self.caller_short_name)}\n"
-                incoming_message_display_count += 1
-            interaction_message_prompt += f"The allowed responses that you can send to the {self.caller_short_name} are:\n\n"
-            outgoing_message_display_count = 1
-            outgoing_message_instances = self.outgoing_message_instances()
-            for message_instance in outgoing_message_instances:
-                interaction_message_prompt += f"{outgoing_message_display_count}. {message_instance.description()}\n"
-                interaction_message_prompt += f"{message_instance.sample(self.caller_short_name)}\n"
-                outgoing_message_display_count += 1
-        return interaction_message_prompt
diff --git a/sirji/prompts/coder.py b/sirji/prompts/coder.py
deleted file mode 100644
index 9b334425..00000000
--- a/sirji/prompts/coder.py
+++ /dev/null
@@ -1,81 +0,0 @@
-import textwrap
-
-from .planner import PlannerPrompt
-from .researcher import ResearcherPrompt
-from .executor import ExecutorPrompt
-from .user import UserPrompt
-
-from .base import PromptGeneratorBase
-
-
-class CoderPrompt(PromptGeneratorBase):
-
-    def __init__(self):
-        super().__init__(None, None)
-
-    def name(self):
-        return "Coding Agent"
-
-    def short_name(self):
-        return "Coder"
-
-    def intro_prompt(self):
-        return textwrap.dedent(f"""
-          You are a {self.name()} ({self.short_name()}), helping programmatically solve a given problem statement (PS).
-          """)
-
-    # def responsibilities_prompt(self):
-    #     return textwrap.dedent(f"""
-    #         - Pay close attention to PS and try to programmatically solve it as asked.
-    #         - Use Python, if the programming language cannot be inferred from PS.
-    #         - Identify URLs or terms (outside of your knowledge) in the PS, which you want to be trained on or researched. After training, infer from the trained content/knowledge. If the response of the inference has new URLs or terms (outside of your knowledge), you can get trained on them as well.
-    #         - Get the list of non-technical steps generated before you start solving the problem statement (PS).
-    #         - Follow the generated non-technical steps sequentially to solve the PS.
-    #         - Ask questions, if essential.                             
-    #         - Always notify about the step started before you start working on it. Similarly, notify about the step completed before you move to the next step.
-    #         - Follow secure software development practices while generating code.
-    #         - Ensure that you don't create any file/folder outside of "workspace/code" folder.
-    #         - Navigate to the workspace folder using 'cd' command, then execute all subsequent commands. Example `cd <<workspace folder>> && <<your executable>>`
-    #         - Understand the information included in the external files by using a combination of wget and cat commands.
-    #         - Only interact with the agents listed below using the allowed responses, also mentioned below.
-    #         - Ensure the response is also enclosed inside 3 backticks (```).
-    #         - End the conversation if you find that the PS cannot be solved programmatically or your solution is complete.
-    #         """)
-
-    def responsibilities_prompt(self):
-        return textwrap.dedent(f"""
-            - Pay close attention to PS and try to programmatically solve it as asked.
-            - Use Python, if the programming language cannot be inferred from PS.
-            - Identify URLs (Excluding GitHub) present in the PS, on which you have no knowledge and want to be trained on or researched. After training, infer from the trained content/knowledge. If the response of the inference has new URLs (Excluding GitHub), on which you have no knowledge, you can get trained on them too.
-            - Get the list of non-technical steps generated before you start solving the problem statement (PS).
-            - Follow the generated non-technical steps sequentially to solve the PS.
-            - Ask questions, if essential.                             
-            - Always notify about the step started before you start working on it. Similarly, notify about the step completed before you move to the next step.
-            - Follow secure software development practices while generating code.
-            - Ensure that you don't create any file/folder outside of current directory, i.e. './'
-            - Read the GitHub files by first cloning the repository and then reading the files. 
-            - Ensure that every code and command execution output is always written in a log file using packages like "tee" and execute in background using "nohup". For example: nohup command | tee <<log file name which can be used to check the output>> &                           
-            - Only interact with the agents listed below using the allowed responses, also mentioned below.
-            - Ensure the response is also enclosed inside 3 backticks (```).
-            - End the conversation if you find that the PS cannot be solved programmatically or your solution is complete.
-            """)
-
-    def capabilities_prompt(self):
-        return ""  # This should not be called.
-
-    def interact_with(self):
-        return [
-            UserPrompt(self.name(), self.short_name()),
-            PlannerPrompt(self.name(), self.short_name()),
-            ExecutorPrompt(self.name(), self.short_name()),
-            ResearcherPrompt(self.name(), self.short_name())
-        ]
-
-    def incoming_message_instances(self):
-        return []
-
-    def outgoing_message_instances(self):
-        return []
-
-    def ending_prompt(self):
-        return ""
diff --git a/sirji/prompts/executor.py b/sirji/prompts/executor.py
deleted file mode 100644
index 1e107ca6..00000000
--- a/sirji/prompts/executor.py
+++ /dev/null
@@ -1,54 +0,0 @@
-import textwrap
-
-from .planner import PlannerPrompt
-
-from .base import PromptGeneratorBase
-
-from sirji.messages.create_file import CreateFileMessage
-from sirji.messages.execute_command import ExecuteCommandMessage
-from sirji.messages.install_package import InstallPackageMessage
-from sirji.messages.output import OutputMessage
-
-
-class ExecutorPrompt(PromptGeneratorBase):
-
-    def __init__(self, caller_name, caller_short_name):
-        super().__init__(caller_name, caller_short_name)
-
-    def name(self):
-        return "Execution Agent"
-
-    def short_name(self):
-        return "Executor"
-
-    def intro_prompt(self):
-        return ""  # This will never be called.
-
-    def responsibilities_prompt(self):
-        return ""  # This will never be called.
-
-    def capabilities_prompt(self):
-        return textwrap.dedent("""
-          - Interact with macOS terminal.
-          - Create or update files on macOS.
-          - Install required dependencies/packages/libraries on macOS.
-          - Execute code on macOS.
-          """)
-
-    def interact_with(self):
-        return [PlannerPrompt(self.name(), self.short_name())]
-
-    def incoming_message_instances(self):
-        return [
-            CreateFileMessage(self.short_name()),
-            ExecuteCommandMessage(self.short_name()),
-            InstallPackageMessage(self.short_name())
-        ]
-
-    def outgoing_message_instances(self):
-        return [
-            OutputMessage(self.short_name())
-        ]
-
-    def ending_prompt(self):
-        return ""  # This will never be called.
diff --git a/sirji/prompts/planner.py b/sirji/prompts/planner.py
deleted file mode 100644
index 8ce05f66..00000000
--- a/sirji/prompts/planner.py
+++ /dev/null
@@ -1,52 +0,0 @@
-import textwrap
-
-from sirji.messages.generate_steps import GenerateStepsMessage
-
-from sirji.messages.steps import StepsMessage
-
-from .base import PromptGeneratorBase
-
-
-class PlannerPrompt(PromptGeneratorBase):
-
-    def __init__(self, caller_name, caller_short_name):
-        super().__init__(caller_name, caller_short_name)
-
-    def name(self):
-        return "Planning Agent"
-
-    def short_name(self):
-        return "Planner"
-
-    def intro_prompt(self):
-        return textwrap.dedent(f"""
-          You are a {self.name()} ({self.short_name()}), helping generate a list of non-technical steps required to solve the given problem statement (PS).
-          """)
-
-    def responsibilities_prompt(self):
-        return textwrap.dedent(f"""
-          - Pay close attention to PS while generating non-technical steps to solve the problem programmatically.
-          - Use Python, if the programming language cannot be inferred from PS.
-          - Don't explain the steps further using sub-steps.
-          - Generate concise steps enough to solve the problem statement.
-          - Ensure that all the steps should be about either create file or install package or execute command or execute code to debug or git clone or read files.
-          - Always add a step to execute the code and evaluate the response output. If the response has errors, solve them before moving ahead.                   
-          - Focus on particular data points given in the PS and not solve the problem in a over-generalized manner.
-          """)
-
-    def capabilities_prompt(self):
-        return textwrap.dedent("""
-          - Generate non-technical steps to solve the problem programmatically.
-          """)
-
-    def interact_with(self):
-        return []
-
-    def incoming_message_instances(self):
-        return [GenerateStepsMessage(self.short_name())]
-
-    def outgoing_message_instances(self):
-        return [StepsMessage(self.short_name())]
-
-    def ending_prompt(self):
-        return ""
diff --git a/sirji/prompts/researcher.py b/sirji/prompts/researcher.py
deleted file mode 100644
index 31159f46..00000000
--- a/sirji/prompts/researcher.py
+++ /dev/null
@@ -1,55 +0,0 @@
-import textwrap
-
-from .base import PromptGeneratorBase
-
-from sirji.messages.infer import InferMessage
-from sirji.messages.train_using_search_term import TrainUsingSearchTermMessage
-from sirji.messages.train_using_url import TrainUsingUrlMessage
-
-from sirji.messages.response import ResponseMessage
-from sirji.messages.output import OutputMessage
-
-
-class ResearcherPrompt(PromptGeneratorBase):
-    def __init__(self, caller_name, caller_short_name):
-        super().__init__(caller_name, caller_short_name)
-
-    def name(self):
-        return "Research Agent"
-
-    def short_name(self):
-        return "Researcher"
-
-    def intro_prompt(self):
-        return ""  # This will never be called.
-
-    def responsibilities_prompt(self):
-        return ""  # This will never be called.
-
-    # def capabilities_prompt(self):
-    #     return textwrap.dedent("""
-    #         - Crawl the given URL and get trained on its content.
-    #         - Search for a term, crawl the first few result URLs, and get trained on their content.
-    #         - Infer from the trained content/knowledge and try to respond to questions asked.
-    #         """)
-
-    def capabilities_prompt(self):
-        return textwrap.dedent("""
-            - Crawl the given URL and get trained on its content.
-            - Infer from the trained content/knowledge and try to respond to questions asked.
-            """)
-
-    def ending_prompt(self):
-        return ""  # This will never be called.
-
-    def interact_with(self):
-        return []
-
-    # def incoming_message_instances(self):
-    #     return [TrainUsingSearchTermMessage(self.short_name()), TrainUsingUrlMessage(self.short_name()), InferMessage(self.short_name())]
-
-    def incoming_message_instances(self):
-        return [TrainUsingUrlMessage(self.short_name()), InferMessage(self.short_name())]
-
-    def outgoing_message_instances(self):
-        return [ResponseMessage(self.short_name()), OutputMessage(self.short_name())]
diff --git a/sirji/prompts/user.py b/sirji/prompts/user.py
deleted file mode 100644
index b05bbb8f..00000000
--- a/sirji/prompts/user.py
+++ /dev/null
@@ -1,66 +0,0 @@
-import textwrap
-
-from .planner import PlannerPrompt
-
-from .base import PromptGeneratorBase
-
-from sirji.messages.answer import AnswerMessage
-from sirji.messages.acknowledge import AcknowledgeMessage
-from sirji.messages.question import QuestionMessage
-from sirji.messages.inform import InformMessage
-from sirji.messages.step_completed import StepCompletedMessage
-from sirji.messages.step_started import StepStartedMessage
-from sirji.messages.solution_complete import SolutionCompleteMessage
-from sirji.messages.feedback import FeedbackMessage
-
-from sirji.messages.problem_statement import ProblemStatementMessage
-
-
-class UserPrompt(PromptGeneratorBase):
-
-    def __init__(self, caller_name, caller_short_name):
-        super().__init__(caller_name, caller_short_name)
-
-    def name(self):
-        return "End User"
-
-    def short_name(self):
-        return "User"
-
-    def intro_prompt(self):
-        return ""  # This will never be called.
-
-    def responsibilities_prompt(self):
-        return ""  # This will never be called.
-
-    def capabilities_prompt(self):
-        return textwrap.dedent("""
-          - Provide the problem statement (PS).   
-          - Provide the answer to the question asked.             
-          - Acknowledge the informational messages.
-          - Acknowledge started or completed steps.
-          - Acknowledge the final solution.
-          """)
-
-    def interact_with(self):
-        return [PlannerPrompt(self.name(), self.short_name())]
-
-    def incoming_message_instances(self):
-        return [
-            QuestionMessage(self.short_name()),
-            InformMessage(self.short_name()),
-            StepStartedMessage(self.short_name()),
-            StepCompletedMessage(self.short_name()),
-            SolutionCompleteMessage(self.short_name())
-        ]
-
-    def outgoing_message_instances(self):
-        return [
-            ProblemStatementMessage(self.short_name()),
-            AnswerMessage(self.short_name()),
-            AcknowledgeMessage(self.short_name()),
-            FeedbackMessage(self.short_name())
-        ]
-
-    def ending_prompt(self):
-        return ""  # This will never be called.
diff --git a/sirji/tools/logger/__init__.py b/sirji/tools/logger/__init__.py
deleted file mode 100644
index c671a4a6..00000000
--- a/sirji/tools/logger/__init__.py
+++ /dev/null
@@ -1,8 +0,0 @@
-from .logger import LoggerManager
-
-coder = LoggerManager().coder
-researcher = LoggerManager().researcher
-planner = LoggerManager().planner
-executor = LoggerManager().executor
-sirji = LoggerManager().sirji
-user = LoggerManager().user
diff --git a/sirji/tools/search/search.py b/sirji/tools/search/search.py
deleted file mode 100644
index a6492efa..00000000
--- a/sirji/tools/search/search.py
+++ /dev/null
@@ -1,40 +0,0 @@
-import os
-import requests
-from sirji.config.config_loader import load_config
-from sirji.tools.logger import researcher as logger
-
-# Now you can access the environment variables using os.getenv
-GOOGLE_SEARCH_API_KEY = os.environ.get("SIRJI_GOOGLE_SEARCH_API_KEY")
-GOOGLE_SEARCH_ENGINE_ID = os.environ.get("SIRJI_GOOGLE_SEARCH_ENGINE_ID")
-
-google_config = load_config("google_config.json")
-
-
-def search_for(query):
-    logger.info(f"Started searching for query term: {query}")
-
-    if (query == ''):
-        return KeyError("Query is empty")
-
-    search_url = google_config['google_search_url']
-    params = {
-        'q': query,
-        'key': GOOGLE_SEARCH_API_KEY,
-        'cx': GOOGLE_SEARCH_ENGINE_ID,
-        'num': google_config['search_results_per_page']
-    }
-
-    response = requests.get(search_url, params=params)
-    response.raise_for_status()  # Raises an exception for HTTP errors
-    search_results = response.json()
-
-    urls = [item['link'] for item in search_results.get('items', [])]
-
-    logger.info(f"Completed searching for query term: {query}")
-
-    return urls
-
-# Example usage:
-# search_term = ''
-# urls = search_for(search_term)
-# print(urls)
diff --git a/sirji/vscode-extension/.eslintrc.json b/sirji/vscode-extension/.eslintrc.json
new file mode 100644
index 00000000..86c86f37
--- /dev/null
+++ b/sirji/vscode-extension/.eslintrc.json
@@ -0,0 +1,30 @@
+{
+    "root": true,
+    "parser": "@typescript-eslint/parser",
+    "parserOptions": {
+        "ecmaVersion": 6,
+        "sourceType": "module"
+    },
+    "plugins": [
+        "@typescript-eslint"
+    ],
+    "rules": {
+        "@typescript-eslint/naming-convention": [
+            "warn",
+            {
+                "selector": "import",
+                "format": [ "camelCase", "PascalCase" ]
+            }
+        ],
+        "@typescript-eslint/semi": "warn",
+        "curly": "warn",
+        "eqeqeq": "warn",
+        "no-throw-literal": "warn",
+        "semi": "off"
+    },
+    "ignorePatterns": [
+        "out",
+        "dist",
+        "**/*.d.ts"
+    ]
+}
\ No newline at end of file
diff --git a/sirji/vscode-extension/.gitignore b/sirji/vscode-extension/.gitignore
new file mode 100644
index 00000000..0b60dfa1
--- /dev/null
+++ b/sirji/vscode-extension/.gitignore
@@ -0,0 +1,5 @@
+out
+dist
+node_modules
+.vscode-test/
+*.vsix
diff --git a/sirji/vscode-extension/.vscode/extensions.json b/sirji/vscode-extension/.vscode/extensions.json
new file mode 100644
index 00000000..ac83a21a
--- /dev/null
+++ b/sirji/vscode-extension/.vscode/extensions.json
@@ -0,0 +1,8 @@
+{
+	// See http://go.microsoft.com/fwlink/?LinkId=827846
+	// for the documentation about the extensions.json format
+	"recommendations": [
+		"dbaeumer.vscode-eslint",
+    "ms-vscode.extension-test-runner"
+	]
+}
\ No newline at end of file
diff --git a/sirji/vscode-extension/.vscode/launch.json b/sirji/vscode-extension/.vscode/launch.json
new file mode 100644
index 00000000..4e947522
--- /dev/null
+++ b/sirji/vscode-extension/.vscode/launch.json
@@ -0,0 +1,21 @@
+// A launch configuration that compiles the extension and then opens it inside a new window
+// Use IntelliSense to learn about possible attributes.
+// Hover to view descriptions of existing attributes.
+// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
+{
+	"version": "0.2.0",
+	"configurations": [
+		{
+			"name": "Run Extension",
+			"type": "extensionHost",
+			"request": "launch",
+			"args": [
+				"--extensionDevelopmentPath=${workspaceFolder}"
+			],
+			"outFiles": [
+				"${workspaceFolder}/out/**/*.js"
+			],
+			"preLaunchTask": "${defaultBuildTask}"
+		}
+	]
+}
\ No newline at end of file
diff --git a/sirji/vscode-extension/.vscode/settings.json b/sirji/vscode-extension/.vscode/settings.json
new file mode 100644
index 00000000..0429f05c
--- /dev/null
+++ b/sirji/vscode-extension/.vscode/settings.json
@@ -0,0 +1,11 @@
+// Place your settings in this file to overwrite default and user settings.
+{
+  "files.exclude": {
+      "out": false // set this to true to hide the "out" folder with the compiled JS files
+  },
+  "search.exclude": {
+      "out": true // set this to false to include "out" folder in search results
+  },
+  // Turn off tsc task auto detection since we have the necessary tasks as npm scripts
+  "typescript.tsc.autoDetect": "off"
+}
\ No newline at end of file
diff --git a/sirji/vscode-extension/.vscode/tasks.json b/sirji/vscode-extension/.vscode/tasks.json
new file mode 100644
index 00000000..241aa6d9
--- /dev/null
+++ b/sirji/vscode-extension/.vscode/tasks.json
@@ -0,0 +1,20 @@
+// See https://go.microsoft.com/fwlink/?LinkId=733558
+// for the documentation about the tasks.json format
+{
+	"version": "2.0.0",
+	"tasks": [
+		{
+			"type": "npm",
+			"script": "watch",
+			"problemMatcher": "$tsc-watch",
+			"isBackground": true,
+			"presentation": {
+				"reveal": "never"
+			},
+			"group": {
+				"kind": "build",
+				"isDefault": true
+			}
+		}
+	]
+}
\ No newline at end of file
diff --git a/sirji/vscode-extension/.vscodeignore b/sirji/vscode-extension/.vscodeignore
new file mode 100644
index 00000000..72aa0fe2
--- /dev/null
+++ b/sirji/vscode-extension/.vscodeignore
@@ -0,0 +1,11 @@
+.vscode/**
+.vscode-test/**
+src/**
+.gitignore
+.yarnrc
+vsc-extension-quickstart.md
+**/tsconfig.json
+**/.eslintrc.json
+**/*.map
+**/*.ts
+**/.vscode-test.*
diff --git a/sirji/vscode-extension/CHANGELOG.md b/sirji/vscode-extension/CHANGELOG.md
new file mode 100644
index 00000000..1f666efb
--- /dev/null
+++ b/sirji/vscode-extension/CHANGELOG.md
@@ -0,0 +1,3 @@
+# ChangeLog
+
+## v0.0.1
diff --git a/sirji/vscode-extension/LICENSE b/sirji/vscode-extension/LICENSE
new file mode 100644
index 00000000..4df90ae4
--- /dev/null
+++ b/sirji/vscode-extension/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 True Sparrow
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/sirji/vscode-extension/README.md b/sirji/vscode-extension/README.md
new file mode 100644
index 00000000..8f475338
--- /dev/null
+++ b/sirji/vscode-extension/README.md
@@ -0,0 +1,89 @@
+<p align="center">
+  <a href="." target="blank"><img src="https://github.com/sirji-ai/sirji/assets/7627517/363fc6dd-69af-4d84-8b7c-a91ec092058d" width="250" alt="Sirji Logo" /></a>
+</p>
+
+<p align="center">
+  <em>Sirji is an Open Source AI Software Development Agent.</em>
+</p>
+
+<p align="center">
+  Built with ❤️ by <a href="https://truesparrow.com/" target="_blank">True Sparrow</a>
+</p>
+
+<p align="center">
+  <img alt="GitHub License" src="https://img.shields.io/github/license/sirji-ai/sirji">
+  <img alt="GitHub commit activity" src="https://img.shields.io/github/commit-activity/m/sirji-ai/sirji">
+  <img alt="GitHub Issues or Pull Requests" src="https://img.shields.io/github/issues/sirji-ai/sirji">
+</p>
+
+<p align="center">
+  <img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/sirji-ai/sirji">
+  <img alt="GitHub forks" src="https://img.shields.io/github/forks/sirji-ai/sirji">
+  <img alt="GitHub watchers" src="https://img.shields.io/github/watchers/sirji-ai/sirji">
+</p>
+
+## Sirji
+
+Sirji is a Visual Studio Code Extension that acts as an AI Software Development Agent, an open-source alternative to Devin. It functions as a virtual software developer, geared towards solving the given problem statement. These problem statements can either involve fresh, greenfield development or efforts aimed at enhancing existing code, bug fixing, documentation, and test case creation in brownfield development.
+
+The extension leverages the capabilities of VS Code, including the Editor, Terminal, Browser, and Project Explorer.
+
+Additionally, it provides an interactive chat interface through which users can submit their problem statements, enhancement requests, feedback, and answers to requests for elaboration.
+
+## Demo Videos
+
+TODO: Show 2 good demo videos - side by side. Then afterwards, give a link to the demos page.
+
+## Prerequisites
+
+Make sure you have installed all of the following prerequisites on your machine:
+
+- Visual Studio Code (>= 1.80.2)
+- Node.js (>= 18) and npm (>= 8.19)
+- Python (>= 3.10) - Make sure `python --version` runs without error.
+- tee command - Make sure `which tee` runs without error.
+
+To check whether your machine meets these prerequisites, run:
+
+```zsh
+sh check_prerequisites.sh
+```
+
+Also, you will need an OpenAI API key.
+
+## Contributing
+
+We welcome contributions to Sirji! If you're interested in helping improve this VS Code extension, please take a look at our [Contributing Guidelines](./CONTRIBUTING.md) for more information on how to get started.
+
+Thank you for considering contributing to Sirji. We look forward to your contributions!
+
+## Reporting Issues
+
+If you run into any issues or have suggestions, please report them by following our [issue reporting guidelines](./ISSUES.md). Your reports help us make Sirji better for everyone.
+
+## Architecture
+
+Sirji gets the work done using it's following agents:
+
+- The **Planning Agent** takes a problem statement and breaks it down into steps.
+- The **Coding Agent** proceeds step by step through the generated steps to solve the problem programmatically.
+- The **Research Agent** utilizes RAG (Retrieval-Augmented Generation) and gets trained on URLs and search terms. It can later use this acquired knowledge to answer questions posed by the Coding Agent.
+- The **Executor Agent** is responsible for Filesystem CRUD, executing commands, and installing dependencies. The Executor Agent is implemented directly within the extension and is written in TypeScript.
+
+### Architecture Diagram
+
+<img width="100%" alt="VS Code Extension - Architecture" src="https://github.com/sirji-ai/sirji/assets/7627517/0cee6e34-a42a-4db0-81db-d2f930132465">
+
+### PyPI Packages
+
+The Planning Agent, Coding Agent, and Research Agent are developed within the Python package [`sirji-agents`](https://pypi.org/project/sirji-agents/) (located in the `agents` folder of this monorepo). <a href="https://pypi.org/project/sirji-agents/"><img src="https://img.shields.io/pypi/v/sirji-agents.svg" alt="Sirji Agents on PyPI" height="15"></a>
+
+Communication among these agents is facilitated through a defined message protocol. The Message Factory (responsible for creating, reading, updating, and deleting messages according to the message protocol) and the permissions matrix are developed in the Python package [`sirji-messages`](https://pypi.org/project/sirji-messages/) (located in the `messages` folder of this monorepo).<a href="https://pypi.org/project/sirji-messages/"><img src="https://img.shields.io/pypi/v/sirji-messages.svg" alt="Sirji Messages on PyPI" height="15"></a>
+
+The tools for crawling URLs (converting them into markdowns), searching for terms on Google, and a custom logger are developed within the Python package [`sirji-tools`](https://pypi.org/project/sirji-tools/) (located in the `tools` folder of this monorepo). <a href="https://pypi.org/project/sirji-tools/"><img src="https://img.shields.io/pypi/v/sirji-tools.svg" alt="Sirji Tools on PyPI" height="15"></a>
+
+All these packages are invoked by Python Adapter Scripts, which are spawned by the extension.
+
+## License
+
+Distributed under the MIT License. See `LICENSE` for more information.
diff --git a/sirji/vscode-extension/package-lock.json b/sirji/vscode-extension/package-lock.json
new file mode 100644
index 00000000..ac26f065
--- /dev/null
+++ b/sirji/vscode-extension/package-lock.json
@@ -0,0 +1,2307 @@
+{
+  "name": "sirji",
+  "version": "0.0.1",
+  "lockfileVersion": 1,
+  "requires": true,
+  "dependencies": {
+    "@aashutoshrathi/word-wrap": {
+      "version": "1.2.6",
+      "resolved": "https://registry.npmjs.org/@aashutoshrathi/word-wrap/-/word-wrap-1.2.6.tgz",
+      "integrity": "sha512-1Yjs2SvM8TflER/OD3cOjhWWOZb58A2t7wpE2S9XfBYTiIl+XFhQG2bjy4Pu1I+EAlCNUzRDYDdFwFYUKvXcIA==",
+      "dev": true
+    },
+    "@babel/runtime": {
+      "version": "7.24.1",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.24.1.tgz",
+      "integrity": "sha512-+BIznRzyqBf+2wCTxcKE3wDjfGeCoVE61KSHGpkzqrLi8qxqFwBeUFyId2cxkTmm55fzDGnm0+yCxaxygrLUnQ==",
+      "dev": true,
+      "requires": {
+        "regenerator-runtime": "^0.14.0"
+      }
+    },
+    "@bcoe/v8-coverage": {
+      "version": "0.2.3",
+      "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz",
+      "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==",
+      "dev": true
+    },
+    "@eslint-community/eslint-utils": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
+      "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==",
+      "dev": true,
+      "requires": {
+        "eslint-visitor-keys": "^3.3.0"
+      }
+    },
+    "@eslint-community/regexpp": {
+      "version": "4.10.0",
+      "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz",
+      "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==",
+      "dev": true
+    },
+    "@eslint/eslintrc": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz",
+      "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==",
+      "dev": true,
+      "requires": {
+        "ajv": "^6.12.4",
+        "debug": "^4.3.2",
+        "espree": "^9.6.0",
+        "globals": "^13.19.0",
+        "ignore": "^5.2.0",
+        "import-fresh": "^3.2.1",
+        "js-yaml": "^4.1.0",
+        "minimatch": "^3.1.2",
+        "strip-json-comments": "^3.1.1"
+      },
+      "dependencies": {
+        "brace-expansion": {
+          "version": "1.1.11",
+          "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+          "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+          "dev": true,
+          "requires": {
+            "balanced-match": "^1.0.0",
+            "concat-map": "0.0.1"
+          }
+        },
+        "minimatch": {
+          "version": "3.1.2",
+          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+          "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+          "dev": true,
+          "requires": {
+            "brace-expansion": "^1.1.7"
+          }
+        }
+      }
+    },
+    "@eslint/js": {
+      "version": "8.57.0",
+      "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz",
+      "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==",
+      "dev": true
+    },
+    "@humanwhocodes/config-array": {
+      "version": "0.11.14",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz",
+      "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==",
+      "dev": true,
+      "requires": {
+        "@humanwhocodes/object-schema": "^2.0.2",
+        "debug": "^4.3.1",
+        "minimatch": "^3.0.5"
+      },
+      "dependencies": {
+        "brace-expansion": {
+          "version": "1.1.11",
+          "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+          "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+          "dev": true,
+          "requires": {
+            "balanced-match": "^1.0.0",
+            "concat-map": "0.0.1"
+          }
+        },
+        "minimatch": {
+          "version": "3.1.2",
+          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+          "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+          "dev": true,
+          "requires": {
+            "brace-expansion": "^1.1.7"
+          }
+        }
+      }
+    },
+    "@humanwhocodes/module-importer": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz",
+      "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==",
+      "dev": true
+    },
+    "@humanwhocodes/object-schema": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.2.tgz",
+      "integrity": "sha512-6EwiSjwWYP7pTckG6I5eyFANjPhmPjUX9JRLUSfNPC7FX7zK9gyZAfUEaECL6ALTpGX5AjnBq3C9XmVWPitNpw==",
+      "dev": true
+    },
+    "@isaacs/cliui": {
+      "version": "8.0.2",
+      "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
+      "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==",
+      "dev": true,
+      "requires": {
+        "string-width": "^5.1.2",
+        "string-width-cjs": "npm:string-width@^4.2.0",
+        "strip-ansi": "^7.0.1",
+        "strip-ansi-cjs": "npm:strip-ansi@^6.0.1",
+        "wrap-ansi": "^8.1.0",
+        "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "6.0.1",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+          "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+          "dev": true
+        },
+        "ansi-styles": {
+          "version": "6.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+          "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+          "dev": true
+        },
+        "emoji-regex": {
+          "version": "9.2.2",
+          "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+          "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+          "dev": true
+        },
+        "string-width": {
+          "version": "5.1.2",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+          "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+          "dev": true,
+          "requires": {
+            "eastasianwidth": "^0.2.0",
+            "emoji-regex": "^9.2.2",
+            "strip-ansi": "^7.0.1"
+          }
+        },
+        "strip-ansi": {
+          "version": "7.1.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz",
+          "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^6.0.1"
+          }
+        },
+        "wrap-ansi": {
+          "version": "8.1.0",
+          "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
+          "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^6.1.0",
+            "string-width": "^5.0.1",
+            "strip-ansi": "^7.0.1"
+          }
+        }
+      }
+    },
+    "@istanbuljs/schema": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz",
+      "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==",
+      "dev": true
+    },
+    "@jridgewell/resolve-uri": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz",
+      "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==",
+      "dev": true
+    },
+    "@jridgewell/sourcemap-codec": {
+      "version": "1.4.15",
+      "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.4.15.tgz",
+      "integrity": "sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==",
+      "dev": true
+    },
+    "@jridgewell/trace-mapping": {
+      "version": "0.3.25",
+      "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.25.tgz",
+      "integrity": "sha512-vNk6aEwybGtawWmy/PzwnGDOjCkLWSD2wqvjGGAgOAwCGWySYXfYoxt00IJkTF+8Lb57DwOb3Aa0o9CApepiYQ==",
+      "dev": true,
+      "requires": {
+        "@jridgewell/resolve-uri": "^3.1.0",
+        "@jridgewell/sourcemap-codec": "^1.4.14"
+      }
+    },
+    "@nodelib/fs.scandir": {
+      "version": "2.1.5",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",
+      "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==",
+      "dev": true,
+      "requires": {
+        "@nodelib/fs.stat": "2.0.5",
+        "run-parallel": "^1.1.9"
+      }
+    },
+    "@nodelib/fs.stat": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz",
+      "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==",
+      "dev": true
+    },
+    "@nodelib/fs.walk": {
+      "version": "1.2.8",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz",
+      "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==",
+      "dev": true,
+      "requires": {
+        "@nodelib/fs.scandir": "2.1.5",
+        "fastq": "^1.6.0"
+      }
+    },
+    "@pkgjs/parseargs": {
+      "version": "0.11.0",
+      "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz",
+      "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==",
+      "dev": true,
+      "optional": true
+    },
+    "@tootallnate/once": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz",
+      "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==",
+      "dev": true
+    },
+    "@types/istanbul-lib-coverage": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz",
+      "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==",
+      "dev": true
+    },
+    "@types/json-schema": {
+      "version": "7.0.15",
+      "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz",
+      "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==",
+      "dev": true
+    },
+    "@types/mocha": {
+      "version": "10.0.6",
+      "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-10.0.6.tgz",
+      "integrity": "sha512-dJvrYWxP/UcXm36Qn36fxhUKu8A/xMRXVT2cliFF1Z7UA9liG5Psj3ezNSZw+5puH2czDXRLcXQxf8JbJt0ejg==",
+      "dev": true
+    },
+    "@types/node": {
+      "version": "18.19.28",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.28.tgz",
+      "integrity": "sha512-J5cOGD9n4x3YGgVuaND6khm5x07MMdAKkRyXnjVR6KFhLMNh2yONGiP7Z+4+tBOt5mK+GvDTiacTOVGGpqiecw==",
+      "dev": true,
+      "requires": {
+        "undici-types": "~5.26.4"
+      }
+    },
+    "@types/semver": {
+      "version": "7.5.8",
+      "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz",
+      "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==",
+      "dev": true
+    },
+    "@types/vscode": {
+      "version": "1.87.0",
+      "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.87.0.tgz",
+      "integrity": "sha512-y3yYJV2esWr8LNjp3VNbSMWG7Y43jC8pCldG8YwiHGAQbsymkkMMt0aDT1xZIOFM2eFcNiUc+dJMx1+Z0UT8fg==",
+      "dev": true
+    },
+    "@typescript-eslint/eslint-plugin": {
+      "version": "7.4.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.4.0.tgz",
+      "integrity": "sha512-yHMQ/oFaM7HZdVrVm/M2WHaNPgyuJH4WelkSVEWSSsir34kxW2kDJCxlXRhhGWEsMN0WAW/vLpKfKVcm8k+MPw==",
+      "dev": true,
+      "requires": {
+        "@eslint-community/regexpp": "^4.5.1",
+        "@typescript-eslint/scope-manager": "7.4.0",
+        "@typescript-eslint/type-utils": "7.4.0",
+        "@typescript-eslint/utils": "7.4.0",
+        "@typescript-eslint/visitor-keys": "7.4.0",
+        "debug": "^4.3.4",
+        "graphemer": "^1.4.0",
+        "ignore": "^5.2.4",
+        "natural-compare": "^1.4.0",
+        "semver": "^7.5.4",
+        "ts-api-utils": "^1.0.1"
+      }
+    },
+    "@typescript-eslint/parser": {
+      "version": "7.4.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.4.0.tgz",
+      "integrity": "sha512-ZvKHxHLusweEUVwrGRXXUVzFgnWhigo4JurEj0dGF1tbcGh6buL+ejDdjxOQxv6ytcY1uhun1p2sm8iWStlgLQ==",
+      "dev": true,
+      "requires": {
+        "@typescript-eslint/scope-manager": "7.4.0",
+        "@typescript-eslint/types": "7.4.0",
+        "@typescript-eslint/typescript-estree": "7.4.0",
+        "@typescript-eslint/visitor-keys": "7.4.0",
+        "debug": "^4.3.4"
+      }
+    },
+    "@typescript-eslint/scope-manager": {
+      "version": "7.4.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.4.0.tgz",
+      "integrity": "sha512-68VqENG5HK27ypafqLVs8qO+RkNc7TezCduYrx8YJpXq2QGZ30vmNZGJJJC48+MVn4G2dCV8m5ZTVnzRexTVtw==",
+      "dev": true,
+      "requires": {
+        "@typescript-eslint/types": "7.4.0",
+        "@typescript-eslint/visitor-keys": "7.4.0"
+      }
+    },
+    "@typescript-eslint/type-utils": {
+      "version": "7.4.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.4.0.tgz",
+      "integrity": "sha512-247ETeHgr9WTRMqHbbQdzwzhuyaJ8dPTuyuUEMANqzMRB1rj/9qFIuIXK7l0FX9i9FXbHeBQl/4uz6mYuCE7Aw==",
+      "dev": true,
+      "requires": {
+        "@typescript-eslint/typescript-estree": "7.4.0",
+        "@typescript-eslint/utils": "7.4.0",
+        "debug": "^4.3.4",
+        "ts-api-utils": "^1.0.1"
+      }
+    },
+    "@typescript-eslint/types": {
+      "version": "7.4.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.4.0.tgz",
+      "integrity": "sha512-mjQopsbffzJskos5B4HmbsadSJQWaRK0UxqQ7GuNA9Ga4bEKeiO6b2DnB6cM6bpc8lemaPseh0H9B/wyg+J7rw==",
+      "dev": true
+    },
+    "@typescript-eslint/typescript-estree": {
+      "version": "7.4.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.4.0.tgz",
+      "integrity": "sha512-A99j5AYoME/UBQ1ucEbbMEmGkN7SE0BvZFreSnTd1luq7yulcHdyGamZKizU7canpGDWGJ+Q6ZA9SyQobipePg==",
+      "dev": true,
+      "requires": {
+        "@typescript-eslint/types": "7.4.0",
+        "@typescript-eslint/visitor-keys": "7.4.0",
+        "debug": "^4.3.4",
+        "globby": "^11.1.0",
+        "is-glob": "^4.0.3",
+        "minimatch": "9.0.3",
+        "semver": "^7.5.4",
+        "ts-api-utils": "^1.0.1"
+      }
+    },
+    "@typescript-eslint/utils": {
+      "version": "7.4.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.4.0.tgz",
+      "integrity": "sha512-NQt9QLM4Tt8qrlBVY9lkMYzfYtNz8/6qwZg8pI3cMGlPnj6mOpRxxAm7BMJN9K0AiY+1BwJ5lVC650YJqYOuNg==",
+      "dev": true,
+      "requires": {
+        "@eslint-community/eslint-utils": "^4.4.0",
+        "@types/json-schema": "^7.0.12",
+        "@types/semver": "^7.5.0",
+        "@typescript-eslint/scope-manager": "7.4.0",
+        "@typescript-eslint/types": "7.4.0",
+        "@typescript-eslint/typescript-estree": "7.4.0",
+        "semver": "^7.5.4"
+      }
+    },
+    "@typescript-eslint/visitor-keys": {
+      "version": "7.4.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.4.0.tgz",
+      "integrity": "sha512-0zkC7YM0iX5Y41homUUeW1CHtZR01K3ybjM1l6QczoMuay0XKtrb93kv95AxUGwdjGr64nNqnOCwmEl616N8CA==",
+      "dev": true,
+      "requires": {
+        "@typescript-eslint/types": "7.4.0",
+        "eslint-visitor-keys": "^3.4.1"
+      }
+    },
+    "@ungap/structured-clone": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz",
+      "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==",
+      "dev": true
+    },
+    "@vscode/test-cli": {
+      "version": "0.0.6",
+      "resolved": "https://registry.npmjs.org/@vscode/test-cli/-/test-cli-0.0.6.tgz",
+      "integrity": "sha512-4i61OUv5PQr3GxhHOuUgHdgBDfIO/kXTPCsEyFiMaY4SOqQTgkTmyZLagHehjOgCfsXdcrJa3zgQ7zoc+Dh6hQ==",
+      "dev": true,
+      "requires": {
+        "@types/mocha": "^10.0.2",
+        "c8": "^9.1.0",
+        "chokidar": "^3.5.3",
+        "enhanced-resolve": "^5.15.0",
+        "glob": "^10.3.10",
+        "minimatch": "^9.0.3",
+        "mocha": "^10.2.0",
+        "supports-color": "^9.4.0",
+        "yargs": "^17.7.2"
+      }
+    },
+    "@vscode/test-electron": {
+      "version": "2.3.9",
+      "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.3.9.tgz",
+      "integrity": "sha512-z3eiChaCQXMqBnk2aHHSEkobmC2VRalFQN0ApOAtydL172zXGxTwGrRtviT5HnUB+Q+G3vtEYFtuQkYqBzYgMA==",
+      "dev": true,
+      "requires": {
+        "http-proxy-agent": "^4.0.1",
+        "https-proxy-agent": "^5.0.0",
+        "jszip": "^3.10.1",
+        "semver": "^7.5.2"
+      }
+    },
+    "abbrev": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
+      "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==",
+      "dev": true
+    },
+    "acorn": {
+      "version": "8.11.3",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz",
+      "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==",
+      "dev": true
+    },
+    "acorn-jsx": {
+      "version": "5.3.2",
+      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz",
+      "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==",
+      "dev": true
+    },
+    "agent-base": {
+      "version": "6.0.2",
+      "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz",
+      "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==",
+      "dev": true,
+      "requires": {
+        "debug": "4"
+      }
+    },
+    "ajv": {
+      "version": "6.12.6",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
+      "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==",
+      "dev": true,
+      "requires": {
+        "fast-deep-equal": "^3.1.1",
+        "fast-json-stable-stringify": "^2.0.0",
+        "json-schema-traverse": "^0.4.1",
+        "uri-js": "^4.2.2"
+      }
+    },
+    "ansi-colors": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz",
+      "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==",
+      "dev": true
+    },
+    "ansi-regex": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz",
+      "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==",
+      "dev": true
+    },
+    "ansi-styles": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz",
+      "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==",
+      "dev": true,
+      "requires": {
+        "color-convert": "^2.0.1"
+      }
+    },
+    "anymatch": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz",
+      "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==",
+      "dev": true,
+      "requires": {
+        "normalize-path": "^3.0.0",
+        "picomatch": "^2.0.4"
+      }
+    },
+    "argparse": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz",
+      "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==",
+      "dev": true
+    },
+    "array-union": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+      "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+      "dev": true
+    },
+    "balanced-match": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz",
+      "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==",
+      "dev": true
+    },
+    "binary-extensions": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz",
+      "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==",
+      "dev": true
+    },
+    "brace-expansion": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz",
+      "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==",
+      "dev": true,
+      "requires": {
+        "balanced-match": "^1.0.0"
+      }
+    },
+    "braces": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+      "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+      "dev": true,
+      "requires": {
+        "fill-range": "^7.0.1"
+      }
+    },
+    "browser-stdout": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz",
+      "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==",
+      "dev": true
+    },
+    "c8": {
+      "version": "9.1.0",
+      "resolved": "https://registry.npmjs.org/c8/-/c8-9.1.0.tgz",
+      "integrity": "sha512-mBWcT5iqNir1zIkzSPyI3NCR9EZCVI3WUD+AVO17MVWTSFNyUueXE82qTeampNtTr+ilN/5Ua3j24LgbCKjDVg==",
+      "dev": true,
+      "requires": {
+        "@bcoe/v8-coverage": "^0.2.3",
+        "@istanbuljs/schema": "^0.1.3",
+        "find-up": "^5.0.0",
+        "foreground-child": "^3.1.1",
+        "istanbul-lib-coverage": "^3.2.0",
+        "istanbul-lib-report": "^3.0.1",
+        "istanbul-reports": "^3.1.6",
+        "test-exclude": "^6.0.0",
+        "v8-to-istanbul": "^9.0.0",
+        "yargs": "^17.7.2",
+        "yargs-parser": "^21.1.1"
+      }
+    },
+    "callsites": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz",
+      "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
+      "dev": true
+    },
+    "camelcase": {
+      "version": "6.3.0",
+      "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz",
+      "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==",
+      "dev": true
+    },
+    "chalk": {
+      "version": "4.1.2",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz",
+      "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==",
+      "dev": true,
+      "requires": {
+        "ansi-styles": "^4.1.0",
+        "supports-color": "^7.1.0"
+      },
+      "dependencies": {
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
+      }
+    },
+    "chokidar": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz",
+      "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==",
+      "dev": true,
+      "requires": {
+        "anymatch": "~3.1.2",
+        "braces": "~3.0.2",
+        "fsevents": "~2.3.2",
+        "glob-parent": "~5.1.2",
+        "is-binary-path": "~2.1.0",
+        "is-glob": "~4.0.1",
+        "normalize-path": "~3.0.0",
+        "readdirp": "~3.6.0"
+      }
+    },
+    "cliui": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+      "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+      "dev": true,
+      "requires": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.1",
+        "wrap-ansi": "^7.0.0"
+      }
+    },
+    "color-convert": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
+      "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==",
+      "dev": true,
+      "requires": {
+        "color-name": "~1.1.4"
+      }
+    },
+    "color-name": {
+      "version": "1.1.4",
+      "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz",
+      "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
+      "dev": true
+    },
+    "concat-map": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
+      "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==",
+      "dev": true
+    },
+    "concurrently": {
+      "version": "8.2.2",
+      "resolved": "https://registry.npmjs.org/concurrently/-/concurrently-8.2.2.tgz",
+      "integrity": "sha512-1dP4gpXFhei8IOtlXRE/T/4H88ElHgTiUzh71YUmtjTEHMSRS2Z/fgOxHSxxusGHogsRfxNq1vyAwxSC+EVyDg==",
+      "dev": true,
+      "requires": {
+        "chalk": "^4.1.2",
+        "date-fns": "^2.30.0",
+        "lodash": "^4.17.21",
+        "rxjs": "^7.8.1",
+        "shell-quote": "^1.8.1",
+        "spawn-command": "0.0.2",
+        "supports-color": "^8.1.1",
+        "tree-kill": "^1.2.2",
+        "yargs": "^17.7.2"
+      },
+      "dependencies": {
+        "supports-color": {
+          "version": "8.1.1",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+          "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
+      }
+    },
+    "convert-source-map": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz",
+      "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==",
+      "dev": true
+    },
+    "core-util-is": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz",
+      "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==",
+      "dev": true
+    },
+    "cross-spawn": {
+      "version": "7.0.3",
+      "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+      "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+      "dev": true,
+      "requires": {
+        "path-key": "^3.1.0",
+        "shebang-command": "^2.0.0",
+        "which": "^2.0.1"
+      }
+    },
+    "date-fns": {
+      "version": "2.30.0",
+      "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz",
+      "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==",
+      "dev": true,
+      "requires": {
+        "@babel/runtime": "^7.21.0"
+      }
+    },
+    "debug": {
+      "version": "4.3.4",
+      "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz",
+      "integrity": "sha512-PRWFHuSU3eDtQJPvnNY7Jcket1j0t5OuOsFzPPzsekD52Zl8qUfFIPEiswXqIvHWGVHOgX+7G/vCNNhehwxfkQ==",
+      "dev": true,
+      "requires": {
+        "ms": "2.1.2"
+      }
+    },
+    "decamelize": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz",
+      "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==",
+      "dev": true
+    },
+    "deep-is": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz",
+      "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==",
+      "dev": true
+    },
+    "diff": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz",
+      "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==",
+      "dev": true
+    },
+    "dir-glob": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+      "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+      "dev": true,
+      "requires": {
+        "path-type": "^4.0.0"
+      }
+    },
+    "doctrine": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
+      "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==",
+      "dev": true,
+      "requires": {
+        "esutils": "^2.0.2"
+      }
+    },
+    "eastasianwidth": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz",
+      "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==",
+      "dev": true
+    },
+    "emoji-regex": {
+      "version": "8.0.0",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz",
+      "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==",
+      "dev": true
+    },
+    "enhanced-resolve": {
+      "version": "5.16.0",
+      "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.16.0.tgz",
+      "integrity": "sha512-O+QWCviPNSSLAD9Ucn8Awv+poAkqn3T1XY5/N7kR7rQO9yfSGWkYZDwpJ+iKF7B8rxaQKWngSqACpgzeapSyoA==",
+      "dev": true,
+      "requires": {
+        "graceful-fs": "^4.2.4",
+        "tapable": "^2.2.0"
+      }
+    },
+    "escalade": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.2.tgz",
+      "integrity": "sha512-ErCHMCae19vR8vQGe50xIsVomy19rg6gFu3+r3jkEO46suLMWBksvVyoGgQV+jOfl84ZSOSlmv6Gxa89PmTGmA==",
+      "dev": true
+    },
+    "escape-string-regexp": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz",
+      "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==",
+      "dev": true
+    },
+    "eslint": {
+      "version": "8.57.0",
+      "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz",
+      "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==",
+      "dev": true,
+      "requires": {
+        "@eslint-community/eslint-utils": "^4.2.0",
+        "@eslint-community/regexpp": "^4.6.1",
+        "@eslint/eslintrc": "^2.1.4",
+        "@eslint/js": "8.57.0",
+        "@humanwhocodes/config-array": "^0.11.14",
+        "@humanwhocodes/module-importer": "^1.0.1",
+        "@nodelib/fs.walk": "^1.2.8",
+        "@ungap/structured-clone": "^1.2.0",
+        "ajv": "^6.12.4",
+        "chalk": "^4.0.0",
+        "cross-spawn": "^7.0.2",
+        "debug": "^4.3.2",
+        "doctrine": "^3.0.0",
+        "escape-string-regexp": "^4.0.0",
+        "eslint-scope": "^7.2.2",
+        "eslint-visitor-keys": "^3.4.3",
+        "espree": "^9.6.1",
+        "esquery": "^1.4.2",
+        "esutils": "^2.0.2",
+        "fast-deep-equal": "^3.1.3",
+        "file-entry-cache": "^6.0.1",
+        "find-up": "^5.0.0",
+        "glob-parent": "^6.0.2",
+        "globals": "^13.19.0",
+        "graphemer": "^1.4.0",
+        "ignore": "^5.2.0",
+        "imurmurhash": "^0.1.4",
+        "is-glob": "^4.0.0",
+        "is-path-inside": "^3.0.3",
+        "js-yaml": "^4.1.0",
+        "json-stable-stringify-without-jsonify": "^1.0.1",
+        "levn": "^0.4.1",
+        "lodash.merge": "^4.6.2",
+        "minimatch": "^3.1.2",
+        "natural-compare": "^1.4.0",
+        "optionator": "^0.9.3",
+        "strip-ansi": "^6.0.1",
+        "text-table": "^0.2.0"
+      },
+      "dependencies": {
+        "brace-expansion": {
+          "version": "1.1.11",
+          "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+          "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+          "dev": true,
+          "requires": {
+            "balanced-match": "^1.0.0",
+            "concat-map": "0.0.1"
+          }
+        },
+        "glob-parent": {
+          "version": "6.0.2",
+          "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz",
+          "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==",
+          "dev": true,
+          "requires": {
+            "is-glob": "^4.0.3"
+          }
+        },
+        "minimatch": {
+          "version": "3.1.2",
+          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+          "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+          "dev": true,
+          "requires": {
+            "brace-expansion": "^1.1.7"
+          }
+        }
+      }
+    },
+    "eslint-scope": {
+      "version": "7.2.2",
+      "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz",
+      "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==",
+      "dev": true,
+      "requires": {
+        "esrecurse": "^4.3.0",
+        "estraverse": "^5.2.0"
+      }
+    },
+    "eslint-visitor-keys": {
+      "version": "3.4.3",
+      "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz",
+      "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==",
+      "dev": true
+    },
+    "espree": {
+      "version": "9.6.1",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz",
+      "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==",
+      "dev": true,
+      "requires": {
+        "acorn": "^8.9.0",
+        "acorn-jsx": "^5.3.2",
+        "eslint-visitor-keys": "^3.4.1"
+      }
+    },
+    "esquery": {
+      "version": "1.5.0",
+      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.5.0.tgz",
+      "integrity": "sha512-YQLXUplAwJgCydQ78IMJywZCceoqk1oH01OERdSAJc/7U2AylwjhSCLDEtqwg811idIS/9fIU5GjG73IgjKMVg==",
+      "dev": true,
+      "requires": {
+        "estraverse": "^5.1.0"
+      }
+    },
+    "esrecurse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz",
+      "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==",
+      "dev": true,
+      "requires": {
+        "estraverse": "^5.2.0"
+      }
+    },
+    "estraverse": {
+      "version": "5.3.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz",
+      "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==",
+      "dev": true
+    },
+    "esutils": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
+      "dev": true
+    },
+    "fast-deep-equal": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+      "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+      "dev": true
+    },
+    "fast-glob": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz",
+      "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==",
+      "dev": true,
+      "requires": {
+        "@nodelib/fs.stat": "^2.0.2",
+        "@nodelib/fs.walk": "^1.2.3",
+        "glob-parent": "^5.1.2",
+        "merge2": "^1.3.0",
+        "micromatch": "^4.0.4"
+      }
+    },
+    "fast-json-stable-stringify": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz",
+      "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==",
+      "dev": true
+    },
+    "fast-levenshtein": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+      "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
+      "dev": true
+    },
+    "fastq": {
+      "version": "1.17.1",
+      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz",
+      "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==",
+      "dev": true,
+      "requires": {
+        "reusify": "^1.0.4"
+      }
+    },
+    "file-entry-cache": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
+      "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==",
+      "dev": true,
+      "requires": {
+        "flat-cache": "^3.0.4"
+      }
+    },
+    "fill-range": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
+      "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+      "dev": true,
+      "requires": {
+        "to-regex-range": "^5.0.1"
+      }
+    },
+    "find-up": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz",
+      "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==",
+      "dev": true,
+      "requires": {
+        "locate-path": "^6.0.0",
+        "path-exists": "^4.0.0"
+      }
+    },
+    "flat": {
+      "version": "5.0.2",
+      "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz",
+      "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==",
+      "dev": true
+    },
+    "flat-cache": {
+      "version": "3.2.0",
+      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz",
+      "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==",
+      "dev": true,
+      "requires": {
+        "flatted": "^3.2.9",
+        "keyv": "^4.5.3",
+        "rimraf": "^3.0.2"
+      }
+    },
+    "flatted": {
+      "version": "3.3.1",
+      "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz",
+      "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==",
+      "dev": true
+    },
+    "foreground-child": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz",
+      "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==",
+      "dev": true,
+      "requires": {
+        "cross-spawn": "^7.0.0",
+        "signal-exit": "^4.0.1"
+      }
+    },
+    "fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==",
+      "dev": true
+    },
+    "fsevents": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz",
+      "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==",
+      "dev": true,
+      "optional": true
+    },
+    "get-caller-file": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz",
+      "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==",
+      "dev": true
+    },
+    "glob": {
+      "version": "10.3.12",
+      "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.12.tgz",
+      "integrity": "sha512-TCNv8vJ+xz4QiqTpfOJA7HvYv+tNIRHKfUWw/q+v2jdgN4ebz+KY9tGx5J4rHP0o84mNP+ApH66HRX8us3Khqg==",
+      "dev": true,
+      "requires": {
+        "foreground-child": "^3.1.0",
+        "jackspeak": "^2.3.6",
+        "minimatch": "^9.0.1",
+        "minipass": "^7.0.4",
+        "path-scurry": "^1.10.2"
+      }
+    },
+    "glob-parent": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz",
+      "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==",
+      "dev": true,
+      "requires": {
+        "is-glob": "^4.0.1"
+      }
+    },
+    "globals": {
+      "version": "13.24.0",
+      "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz",
+      "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==",
+      "dev": true,
+      "requires": {
+        "type-fest": "^0.20.2"
+      }
+    },
+    "globby": {
+      "version": "11.1.0",
+      "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz",
+      "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==",
+      "dev": true,
+      "requires": {
+        "array-union": "^2.1.0",
+        "dir-glob": "^3.0.1",
+        "fast-glob": "^3.2.9",
+        "ignore": "^5.2.0",
+        "merge2": "^1.4.1",
+        "slash": "^3.0.0"
+      }
+    },
+    "graceful-fs": {
+      "version": "4.2.11",
+      "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz",
+      "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==",
+      "dev": true
+    },
+    "graphemer": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz",
+      "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==",
+      "dev": true
+    },
+    "has-flag": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+      "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+      "dev": true
+    },
+    "he": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz",
+      "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==",
+      "dev": true
+    },
+    "html-escaper": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
+      "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
+      "dev": true
+    },
+    "http-proxy-agent": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz",
+      "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==",
+      "dev": true,
+      "requires": {
+        "@tootallnate/once": "1",
+        "agent-base": "6",
+        "debug": "4"
+      }
+    },
+    "https-proxy-agent": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.1.tgz",
+      "integrity": "sha512-dFcAjpTQFgoLMzC2VwU+C/CbS7uRL0lWmxDITmqm7C+7F0Odmj6s9l6alZc6AELXhrnggM2CeWSXHGOdX2YtwA==",
+      "dev": true,
+      "requires": {
+        "agent-base": "6",
+        "debug": "4"
+      }
+    },
+    "ignore": {
+      "version": "5.3.1",
+      "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.1.tgz",
+      "integrity": "sha512-5Fytz/IraMjqpwfd34ke28PTVMjZjJG2MPn5t7OE4eUCUNf8BAa7b5WUS9/Qvr6mwOQS7Mk6vdsMno5he+T8Xw==",
+      "dev": true
+    },
+    "ignore-by-default": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/ignore-by-default/-/ignore-by-default-1.0.1.tgz",
+      "integrity": "sha512-Ius2VYcGNk7T90CppJqcIkS5ooHUZyIQK+ClZfMfMNFEF9VSE73Fq+906u/CWu92x4gzZMWOwfFYckPObzdEbA==",
+      "dev": true
+    },
+    "immediate": {
+      "version": "3.0.6",
+      "resolved": "https://registry.npmjs.org/immediate/-/immediate-3.0.6.tgz",
+      "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==",
+      "dev": true
+    },
+    "import-fresh": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz",
+      "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==",
+      "dev": true,
+      "requires": {
+        "parent-module": "^1.0.0",
+        "resolve-from": "^4.0.0"
+      }
+    },
+    "imurmurhash": {
+      "version": "0.1.4",
+      "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz",
+      "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==",
+      "dev": true
+    },
+    "inflight": {
+      "version": "1.0.6",
+      "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
+      "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==",
+      "dev": true,
+      "requires": {
+        "once": "^1.3.0",
+        "wrappy": "1"
+      }
+    },
+    "inherits": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
+      "dev": true
+    },
+    "is-binary-path": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz",
+      "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==",
+      "dev": true,
+      "requires": {
+        "binary-extensions": "^2.0.0"
+      }
+    },
+    "is-extglob": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz",
+      "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==",
+      "dev": true
+    },
+    "is-fullwidth-code-point": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+      "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+      "dev": true
+    },
+    "is-glob": {
+      "version": "4.0.3",
+      "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz",
+      "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==",
+      "dev": true,
+      "requires": {
+        "is-extglob": "^2.1.1"
+      }
+    },
+    "is-number": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
+      "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==",
+      "dev": true
+    },
+    "is-path-inside": {
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz",
+      "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==",
+      "dev": true
+    },
+    "is-plain-obj": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz",
+      "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==",
+      "dev": true
+    },
+    "is-unicode-supported": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz",
+      "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==",
+      "dev": true
+    },
+    "isarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==",
+      "dev": true
+    },
+    "isexe": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz",
+      "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==",
+      "dev": true
+    },
+    "istanbul-lib-coverage": {
+      "version": "3.2.2",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz",
+      "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==",
+      "dev": true
+    },
+    "istanbul-lib-report": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz",
+      "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==",
+      "dev": true,
+      "requires": {
+        "istanbul-lib-coverage": "^3.0.0",
+        "make-dir": "^4.0.0",
+        "supports-color": "^7.1.0"
+      },
+      "dependencies": {
+        "supports-color": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz",
+          "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        }
+      }
+    },
+    "istanbul-reports": {
+      "version": "3.1.7",
+      "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz",
+      "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==",
+      "dev": true,
+      "requires": {
+        "html-escaper": "^2.0.0",
+        "istanbul-lib-report": "^3.0.0"
+      }
+    },
+    "jackspeak": {
+      "version": "2.3.6",
+      "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz",
+      "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==",
+      "dev": true,
+      "requires": {
+        "@isaacs/cliui": "^8.0.2",
+        "@pkgjs/parseargs": "^0.11.0"
+      }
+    },
+    "js-yaml": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz",
+      "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==",
+      "dev": true,
+      "requires": {
+        "argparse": "^2.0.1"
+      }
+    },
+    "json-buffer": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz",
+      "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==",
+      "dev": true
+    },
+    "json-schema-traverse": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz",
+      "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==",
+      "dev": true
+    },
+    "json-stable-stringify-without-jsonify": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz",
+      "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==",
+      "dev": true
+    },
+    "jszip": {
+      "version": "3.10.1",
+      "resolved": "https://registry.npmjs.org/jszip/-/jszip-3.10.1.tgz",
+      "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
+      "dev": true,
+      "requires": {
+        "lie": "~3.3.0",
+        "pako": "~1.0.2",
+        "readable-stream": "~2.3.6",
+        "setimmediate": "^1.0.5"
+      }
+    },
+    "keyv": {
+      "version": "4.5.4",
+      "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz",
+      "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==",
+      "dev": true,
+      "requires": {
+        "json-buffer": "3.0.1"
+      }
+    },
+    "levn": {
+      "version": "0.4.1",
+      "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz",
+      "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==",
+      "dev": true,
+      "requires": {
+        "prelude-ls": "^1.2.1",
+        "type-check": "~0.4.0"
+      }
+    },
+    "lie": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmjs.org/lie/-/lie-3.3.0.tgz",
+      "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+      "dev": true,
+      "requires": {
+        "immediate": "~3.0.5"
+      }
+    },
+    "locate-path": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
+      "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==",
+      "dev": true,
+      "requires": {
+        "p-locate": "^5.0.0"
+      }
+    },
+    "lodash": {
+      "version": "4.17.21",
+      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
+      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
+      "dev": true
+    },
+    "lodash.merge": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
+      "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
+      "dev": true
+    },
+    "log-symbols": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+      "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+      "dev": true,
+      "requires": {
+        "chalk": "^4.1.0",
+        "is-unicode-supported": "^0.1.0"
+      }
+    },
+    "lru-cache": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz",
+      "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==",
+      "dev": true,
+      "requires": {
+        "yallist": "^4.0.0"
+      }
+    },
+    "make-dir": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz",
+      "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==",
+      "dev": true,
+      "requires": {
+        "semver": "^7.5.3"
+      }
+    },
+    "merge2": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
+      "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==",
+      "dev": true
+    },
+    "micromatch": {
+      "version": "4.0.5",
+      "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz",
+      "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==",
+      "dev": true,
+      "requires": {
+        "braces": "^3.0.2",
+        "picomatch": "^2.3.1"
+      }
+    },
+    "minimatch": {
+      "version": "9.0.3",
+      "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz",
+      "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==",
+      "dev": true,
+      "requires": {
+        "brace-expansion": "^2.0.1"
+      }
+    },
+    "minipass": {
+      "version": "7.0.4",
+      "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.0.4.tgz",
+      "integrity": "sha512-jYofLM5Dam9279rdkWzqHozUo4ybjdZmCsDHePy5V/PbBcVMiSZR97gmAy45aqi8CK1lG2ECd356FU86avfwUQ==",
+      "dev": true
+    },
+    "mocha": {
+      "version": "10.4.0",
+      "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.4.0.tgz",
+      "integrity": "sha512-eqhGB8JKapEYcC4ytX/xrzKforgEc3j1pGlAXVy3eRwrtAy5/nIfT1SvgGzfN0XZZxeLq0aQWkOUAmqIJiv+bA==",
+      "dev": true,
+      "requires": {
+        "ansi-colors": "4.1.1",
+        "browser-stdout": "1.3.1",
+        "chokidar": "3.5.3",
+        "debug": "4.3.4",
+        "diff": "5.0.0",
+        "escape-string-regexp": "4.0.0",
+        "find-up": "5.0.0",
+        "glob": "8.1.0",
+        "he": "1.2.0",
+        "js-yaml": "4.1.0",
+        "log-symbols": "4.1.0",
+        "minimatch": "5.0.1",
+        "ms": "2.1.3",
+        "serialize-javascript": "6.0.0",
+        "strip-json-comments": "3.1.1",
+        "supports-color": "8.1.1",
+        "workerpool": "6.2.1",
+        "yargs": "16.2.0",
+        "yargs-parser": "20.2.4",
+        "yargs-unparser": "2.0.0"
+      },
+      "dependencies": {
+        "chokidar": {
+          "version": "3.5.3",
+          "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz",
+          "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==",
+          "dev": true,
+          "requires": {
+            "anymatch": "~3.1.2",
+            "braces": "~3.0.2",
+            "fsevents": "~2.3.2",
+            "glob-parent": "~5.1.2",
+            "is-binary-path": "~2.1.0",
+            "is-glob": "~4.0.1",
+            "normalize-path": "~3.0.0",
+            "readdirp": "~3.6.0"
+          }
+        },
+        "cliui": {
+          "version": "7.0.4",
+          "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz",
+          "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==",
+          "dev": true,
+          "requires": {
+            "string-width": "^4.2.0",
+            "strip-ansi": "^6.0.0",
+            "wrap-ansi": "^7.0.0"
+          }
+        },
+        "glob": {
+          "version": "8.1.0",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz",
+          "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==",
+          "dev": true,
+          "requires": {
+            "fs.realpath": "^1.0.0",
+            "inflight": "^1.0.4",
+            "inherits": "2",
+            "minimatch": "^5.0.1",
+            "once": "^1.3.0"
+          }
+        },
+        "minimatch": {
+          "version": "5.0.1",
+          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.0.1.tgz",
+          "integrity": "sha512-nLDxIFRyhDblz3qMuq+SoRZED4+miJ/G+tdDrjkkkRnjAsBexeGpgjLEQ0blJy7rHhR2b93rhQY4SvyWu9v03g==",
+          "dev": true,
+          "requires": {
+            "brace-expansion": "^2.0.1"
+          }
+        },
+        "ms": {
+          "version": "2.1.3",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz",
+          "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==",
+          "dev": true
+        },
+        "supports-color": {
+          "version": "8.1.1",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz",
+          "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^4.0.0"
+          }
+        },
+        "yargs": {
+          "version": "16.2.0",
+          "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
+          "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==",
+          "dev": true,
+          "requires": {
+            "cliui": "^7.0.2",
+            "escalade": "^3.1.1",
+            "get-caller-file": "^2.0.5",
+            "require-directory": "^2.1.1",
+            "string-width": "^4.2.0",
+            "y18n": "^5.0.5",
+            "yargs-parser": "^20.2.2"
+          }
+        },
+        "yargs-parser": {
+          "version": "20.2.4",
+          "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz",
+          "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==",
+          "dev": true
+        }
+      }
+    },
+    "ms": {
+      "version": "2.1.2",
+      "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz",
+      "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==",
+      "dev": true
+    },
+    "natural-compare": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz",
+      "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==",
+      "dev": true
+    },
+    "nodemon": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/nodemon/-/nodemon-3.1.0.tgz",
+      "integrity": "sha512-xqlktYlDMCepBJd43ZQhjWwMw2obW/JRvkrLxq5RCNcuDDX1DbcPT+qT1IlIIdf+DhnWs90JpTMe+Y5KxOchvA==",
+      "dev": true,
+      "requires": {
+        "chokidar": "^3.5.2",
+        "debug": "^4",
+        "ignore-by-default": "^1.0.1",
+        "minimatch": "^3.1.2",
+        "pstree.remy": "^1.1.8",
+        "semver": "^7.5.3",
+        "simple-update-notifier": "^2.0.0",
+        "supports-color": "^5.5.0",
+        "touch": "^3.1.0",
+        "undefsafe": "^2.0.5"
+      },
+      "dependencies": {
+        "brace-expansion": {
+          "version": "1.1.11",
+          "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+          "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+          "dev": true,
+          "requires": {
+            "balanced-match": "^1.0.0",
+            "concat-map": "0.0.1"
+          }
+        },
+        "has-flag": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz",
+          "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==",
+          "dev": true
+        },
+        "minimatch": {
+          "version": "3.1.2",
+          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+          "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+          "dev": true,
+          "requires": {
+            "brace-expansion": "^1.1.7"
+          }
+        },
+        "supports-color": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz",
+          "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==",
+          "dev": true,
+          "requires": {
+            "has-flag": "^3.0.0"
+          }
+        }
+      }
+    },
+    "nopt": {
+      "version": "1.0.10",
+      "resolved": "https://registry.npmjs.org/nopt/-/nopt-1.0.10.tgz",
+      "integrity": "sha512-NWmpvLSqUrgrAC9HCuxEvb+PSloHpqVu+FqcO4eeF2h5qYRhA7ev6KvelyQAKtegUbC6RypJnlEOhd8vloNKYg==",
+      "dev": true,
+      "requires": {
+        "abbrev": "1"
+      }
+    },
+    "normalize-path": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz",
+      "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==",
+      "dev": true
+    },
+    "once": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz",
+      "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==",
+      "dev": true,
+      "requires": {
+        "wrappy": "1"
+      }
+    },
+    "optionator": {
+      "version": "0.9.3",
+      "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.3.tgz",
+      "integrity": "sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==",
+      "dev": true,
+      "requires": {
+        "@aashutoshrathi/word-wrap": "^1.2.3",
+        "deep-is": "^0.1.3",
+        "fast-levenshtein": "^2.0.6",
+        "levn": "^0.4.1",
+        "prelude-ls": "^1.2.1",
+        "type-check": "^0.4.0"
+      }
+    },
+    "p-limit": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz",
+      "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==",
+      "dev": true,
+      "requires": {
+        "yocto-queue": "^0.1.0"
+      }
+    },
+    "p-locate": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz",
+      "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==",
+      "dev": true,
+      "requires": {
+        "p-limit": "^3.0.2"
+      }
+    },
+    "pako": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz",
+      "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw==",
+      "dev": true
+    },
+    "parent-module": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz",
+      "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==",
+      "dev": true,
+      "requires": {
+        "callsites": "^3.0.0"
+      }
+    },
+    "path-exists": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz",
+      "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==",
+      "dev": true
+    },
+    "path-is-absolute": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz",
+      "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==",
+      "dev": true
+    },
+    "path-key": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz",
+      "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==",
+      "dev": true
+    },
+    "path-scurry": {
+      "version": "1.10.2",
+      "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.10.2.tgz",
+      "integrity": "sha512-7xTavNy5RQXnsjANvVvMkEjvloOinkAjv/Z6Ildz9v2RinZ4SBKTWFOVRbaF8p0vpHnyjV/UwNDdKuUv6M5qcA==",
+      "dev": true,
+      "requires": {
+        "lru-cache": "^10.2.0",
+        "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
+      },
+      "dependencies": {
+        "lru-cache": {
+          "version": "10.2.0",
+          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz",
+          "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==",
+          "dev": true
+        }
+      }
+    },
+    "path-type": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+      "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+      "dev": true
+    },
+    "picomatch": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
+      "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==",
+      "dev": true
+    },
+    "prelude-ls": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz",
+      "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==",
+      "dev": true
+    },
+    "process-nextick-args": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==",
+      "dev": true
+    },
+    "pstree.remy": {
+      "version": "1.1.8",
+      "resolved": "https://registry.npmjs.org/pstree.remy/-/pstree.remy-1.1.8.tgz",
+      "integrity": "sha512-77DZwxQmxKnu3aR542U+X8FypNzbfJ+C5XQDk3uWjWxn6151aIMGthWYRXTqT1E5oJvg+ljaa2OJi+VfvCOQ8w==",
+      "dev": true
+    },
+    "punycode": {
+      "version": "2.3.1",
+      "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
+      "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
+      "dev": true
+    },
+    "queue-microtask": {
+      "version": "1.2.3",
+      "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz",
+      "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==",
+      "dev": true
+    },
+    "randombytes": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz",
+      "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==",
+      "dev": true,
+      "requires": {
+        "safe-buffer": "^5.1.0"
+      }
+    },
+    "readable-stream": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.8.tgz",
+      "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+      "dev": true,
+      "requires": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.3",
+        "isarray": "~1.0.0",
+        "process-nextick-args": "~2.0.0",
+        "safe-buffer": "~5.1.1",
+        "string_decoder": "~1.1.1",
+        "util-deprecate": "~1.0.1"
+      },
+      "dependencies": {
+        "safe-buffer": {
+          "version": "5.1.2",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+          "dev": true
+        }
+      }
+    },
+    "readdirp": {
+      "version": "3.6.0",
+      "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz",
+      "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==",
+      "dev": true,
+      "requires": {
+        "picomatch": "^2.2.1"
+      }
+    },
+    "regenerator-runtime": {
+      "version": "0.14.1",
+      "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
+      "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
+      "dev": true
+    },
+    "require-directory": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz",
+      "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
+      "dev": true
+    },
+    "resolve-from": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz",
+      "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==",
+      "dev": true
+    },
+    "reusify": {
+      "version": "1.0.4",
+      "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
+      "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
+      "dev": true
+    },
+    "rimraf": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
+      "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==",
+      "dev": true,
+      "requires": {
+        "glob": "^7.1.3"
+      },
+      "dependencies": {
+        "brace-expansion": {
+          "version": "1.1.11",
+          "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+          "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+          "dev": true,
+          "requires": {
+            "balanced-match": "^1.0.0",
+            "concat-map": "0.0.1"
+          }
+        },
+        "glob": {
+          "version": "7.2.3",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+          "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+          "dev": true,
+          "requires": {
+            "fs.realpath": "^1.0.0",
+            "inflight": "^1.0.4",
+            "inherits": "2",
+            "minimatch": "^3.1.1",
+            "once": "^1.3.0",
+            "path-is-absolute": "^1.0.0"
+          }
+        },
+        "minimatch": {
+          "version": "3.1.2",
+          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+          "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+          "dev": true,
+          "requires": {
+            "brace-expansion": "^1.1.7"
+          }
+        }
+      }
+    },
+    "run-parallel": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz",
+      "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==",
+      "dev": true,
+      "requires": {
+        "queue-microtask": "^1.2.2"
+      }
+    },
+    "rxjs": {
+      "version": "7.8.1",
+      "resolved": "https://registry.npmjs.org/rxjs/-/rxjs-7.8.1.tgz",
+      "integrity": "sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg==",
+      "dev": true,
+      "requires": {
+        "tslib": "^2.1.0"
+      }
+    },
+    "safe-buffer": {
+      "version": "5.2.1",
+      "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz",
+      "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==",
+      "dev": true
+    },
+    "semver": {
+      "version": "7.6.0",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.0.tgz",
+      "integrity": "sha512-EnwXhrlwXMk9gKu5/flx5sv/an57AkRplG3hTK68W7FRDN+k+OWBj65M7719OkA82XLBxrcX0KSHj+X5COhOVg==",
+      "dev": true,
+      "requires": {
+        "lru-cache": "^6.0.0"
+      }
+    },
+    "serialize-javascript": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.0.tgz",
+      "integrity": "sha512-Qr3TosvguFt8ePWqsvRfrKyQXIiW+nGbYpy8XK24NQHE83caxWt+mIymTT19DGFbNWNLfEwsrkSmN64lVWB9ag==",
+      "dev": true,
+      "requires": {
+        "randombytes": "^2.1.0"
+      }
+    },
+    "setimmediate": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz",
+      "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA==",
+      "dev": true
+    },
+    "shebang-command": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
+      "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==",
+      "dev": true,
+      "requires": {
+        "shebang-regex": "^3.0.0"
+      }
+    },
+    "shebang-regex": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz",
+      "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==",
+      "dev": true
+    },
+    "shell-quote": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/shell-quote/-/shell-quote-1.8.1.tgz",
+      "integrity": "sha512-6j1W9l1iAs/4xYBI1SYOVZyFcCis9b4KCLQ8fgAGG07QvzaRLVVRQvAy85yNmmZSjYjg4MWh4gNvlPujU/5LpA==",
+      "dev": true
+    },
+    "signal-exit": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz",
+      "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==",
+      "dev": true
+    },
+    "simple-update-notifier": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/simple-update-notifier/-/simple-update-notifier-2.0.0.tgz",
+      "integrity": "sha512-a2B9Y0KlNXl9u/vsW6sTIu9vGEpfKu2wRV6l1H3XEas/0gUIzGzBoP/IouTcUQbm9JWZLH3COxyn03TYlFax6w==",
+      "dev": true,
+      "requires": {
+        "semver": "^7.5.3"
+      }
+    },
+    "slash": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+      "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+      "dev": true
+    },
+    "spawn-command": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmjs.org/spawn-command/-/spawn-command-0.0.2.tgz",
+      "integrity": "sha512-zC8zGoGkmc8J9ndvml8Xksr1Amk9qBujgbF0JAIWO7kXr43w0h/0GJNM/Vustixu+YE8N/MTrQ7N31FvHUACxQ==",
+      "dev": true
+    },
+    "string-width": {
+      "version": "4.2.3",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "dev": true,
+      "requires": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      }
+    },
+    "string-width-cjs": {
+      "version": "npm:string-width@4.2.3",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz",
+      "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==",
+      "dev": true,
+      "requires": {
+        "emoji-regex": "^8.0.0",
+        "is-fullwidth-code-point": "^3.0.0",
+        "strip-ansi": "^6.0.1"
+      }
+    },
+    "string_decoder": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz",
+      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+      "dev": true,
+      "requires": {
+        "safe-buffer": "~5.1.0"
+      },
+      "dependencies": {
+        "safe-buffer": {
+          "version": "5.1.2",
+          "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz",
+          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
+          "dev": true
+        }
+      }
+    },
+    "strip-ansi": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "dev": true,
+      "requires": {
+        "ansi-regex": "^5.0.1"
+      }
+    },
+    "strip-ansi-cjs": {
+      "version": "npm:strip-ansi@6.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz",
+      "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==",
+      "dev": true,
+      "requires": {
+        "ansi-regex": "^5.0.1"
+      }
+    },
+    "strip-json-comments": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz",
+      "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==",
+      "dev": true
+    },
+    "supports-color": {
+      "version": "9.4.0",
+      "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz",
+      "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==",
+      "dev": true
+    },
+    "tapable": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.2.1.tgz",
+      "integrity": "sha512-GNzQvQTOIP6RyTfE2Qxb8ZVlNmw0n88vp1szwWRimP02mnTsx3Wtn5qRdqY9w2XduFNUgvOwhNnQsjwCp+kqaQ==",
+      "dev": true
+    },
+    "test-exclude": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz",
+      "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==",
+      "dev": true,
+      "requires": {
+        "@istanbuljs/schema": "^0.1.2",
+        "glob": "^7.1.4",
+        "minimatch": "^3.0.4"
+      },
+      "dependencies": {
+        "brace-expansion": {
+          "version": "1.1.11",
+          "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
+          "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
+          "dev": true,
+          "requires": {
+            "balanced-match": "^1.0.0",
+            "concat-map": "0.0.1"
+          }
+        },
+        "glob": {
+          "version": "7.2.3",
+          "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
+          "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==",
+          "dev": true,
+          "requires": {
+            "fs.realpath": "^1.0.0",
+            "inflight": "^1.0.4",
+            "inherits": "2",
+            "minimatch": "^3.1.1",
+            "once": "^1.3.0",
+            "path-is-absolute": "^1.0.0"
+          }
+        },
+        "minimatch": {
+          "version": "3.1.2",
+          "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz",
+          "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==",
+          "dev": true,
+          "requires": {
+            "brace-expansion": "^1.1.7"
+          }
+        }
+      }
+    },
+    "text-table": {
+      "version": "0.2.0",
+      "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz",
+      "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==",
+      "dev": true
+    },
+    "to-regex-range": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz",
+      "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==",
+      "dev": true,
+      "requires": {
+        "is-number": "^7.0.0"
+      }
+    },
+    "touch": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/touch/-/touch-3.1.0.tgz",
+      "integrity": "sha512-WBx8Uy5TLtOSRtIq+M03/sKDrXCLHxwDcquSP2c43Le03/9serjQBIztjRz6FkJez9D/hleyAXTBGLwwZUw9lA==",
+      "dev": true,
+      "requires": {
+        "nopt": "~1.0.10"
+      }
+    },
+    "tree-kill": {
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/tree-kill/-/tree-kill-1.2.2.tgz",
+      "integrity": "sha512-L0Orpi8qGpRG//Nd+H90vFB+3iHnue1zSSGmNOOCh1GLJ7rUKVwV2HvijphGQS2UmhUZewS9VgvxYIdgr+fG1A==",
+      "dev": true
+    },
+    "ts-api-utils": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz",
+      "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==",
+      "dev": true
+    },
+    "tslib": {
+      "version": "2.6.2",
+      "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz",
+      "integrity": "sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q==",
+      "dev": true
+    },
+    "type-check": {
+      "version": "0.4.0",
+      "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz",
+      "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==",
+      "dev": true,
+      "requires": {
+        "prelude-ls": "^1.2.1"
+      }
+    },
+    "type-fest": {
+      "version": "0.20.2",
+      "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz",
+      "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==",
+      "dev": true
+    },
+    "typescript": {
+      "version": "5.4.3",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.3.tgz",
+      "integrity": "sha512-KrPd3PKaCLr78MalgiwJnA25Nm8HAmdwN3mYUYZgG/wizIo9EainNVQI9/yDavtVFRN2h3k8uf3GLHuhDMgEHg==",
+      "dev": true
+    },
+    "undefsafe": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmjs.org/undefsafe/-/undefsafe-2.0.5.tgz",
+      "integrity": "sha512-WxONCrssBM8TSPRqN5EmsjVrsv4A8X12J4ArBiiayv3DyyG3ZlIg6yysuuSYdZsVz3TKcTg2fd//Ujd4CHV1iA==",
+      "dev": true
+    },
+    "undici-types": {
+      "version": "5.26.5",
+      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz",
+      "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==",
+      "dev": true
+    },
+    "uri-js": {
+      "version": "4.4.1",
+      "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz",
+      "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==",
+      "dev": true,
+      "requires": {
+        "punycode": "^2.1.0"
+      }
+    },
+    "util-deprecate": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz",
+      "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==",
+      "dev": true
+    },
+    "v8-to-istanbul": {
+      "version": "9.2.0",
+      "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.2.0.tgz",
+      "integrity": "sha512-/EH/sDgxU2eGxajKdwLCDmQ4FWq+kpi3uCmBGpw1xJtnAxEjlD8j8PEiGWpCIMIs3ciNAgH0d3TTJiUkYzyZjA==",
+      "dev": true,
+      "requires": {
+        "@jridgewell/trace-mapping": "^0.3.12",
+        "@types/istanbul-lib-coverage": "^2.0.1",
+        "convert-source-map": "^2.0.0"
+      }
+    },
+    "which": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz",
+      "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==",
+      "dev": true,
+      "requires": {
+        "isexe": "^2.0.0"
+      }
+    },
+    "workerpool": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.2.1.tgz",
+      "integrity": "sha512-ILEIE97kDZvF9Wb9f6h5aXK4swSlKGUcOEGiIYb2OOu/IrDU9iwj0fD//SsA6E5ibwJxpEvhullJY4Sl4GcpAw==",
+      "dev": true
+    },
+    "wrap-ansi": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "dev": true,
+      "requires": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      }
+    },
+    "wrap-ansi-cjs": {
+      "version": "npm:wrap-ansi@7.0.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz",
+      "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==",
+      "dev": true,
+      "requires": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      }
+    },
+    "wrappy": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz",
+      "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
+      "dev": true
+    },
+    "y18n": {
+      "version": "5.0.8",
+      "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+      "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+      "dev": true
+    },
+    "yallist": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
+      "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==",
+      "dev": true
+    },
+    "yargs": {
+      "version": "17.7.2",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+      "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+      "dev": true,
+      "requires": {
+        "cliui": "^8.0.1",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "require-directory": "^2.1.1",
+        "string-width": "^4.2.3",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^21.1.1"
+      }
+    },
+    "yargs-parser": {
+      "version": "21.1.1",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+      "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+      "dev": true
+    },
+    "yargs-unparser": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz",
+      "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==",
+      "dev": true,
+      "requires": {
+        "camelcase": "^6.0.0",
+        "decamelize": "^4.0.0",
+        "flat": "^5.0.2",
+        "is-plain-obj": "^2.1.0"
+      }
+    },
+    "yocto-queue": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz",
+      "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==",
+      "dev": true
+    }
+  }
+}
diff --git a/sirji/vscode-extension/package.json b/sirji/vscode-extension/package.json
new file mode 100644
index 00000000..af6dfd35
--- /dev/null
+++ b/sirji/vscode-extension/package.json
@@ -0,0 +1,82 @@
+{
+  "name": "sirji",
+  "displayName": "Sirji",
+  "description": "Sirji is an AI Software Development Agent.",
+  "version": "0.0.5",
+  "publisher": "TrueSparrow",
+  "icon": "out/assets/sirji.png",
+  "repository": {
+    "type": "git",
+    "url": "https://github.com/sirji-ai/sirji"
+  },
+  "bugs": {
+    "url": "https://github.com/sirji-ai/sirji/issues"
+  },
+  "qna": "https://github.com/orgs/sirji-ai/discussions/categories/q-a",
+  "license": "MIT",
+  "engines": {
+    "vscode": "^1.80.2"
+  },
+  "categories": [
+    "Programming Languages",
+    "Machine Learning",
+    "Education"
+  ],
+  "keywords": [
+    "ai",
+    "sirji",
+    "true sparrow",
+    "openai",
+    "snippets",
+    "documentation",
+    "javascript",
+    "python",
+    "typescript",
+    "php",
+    "go",
+    "golang",
+    "ruby",
+    "c++",
+    "c#",
+    "java",
+    "kotlin",
+    "software development agent",
+    "opensource devin"
+  ],
+  "activationEvents": [],
+  "main": "./out/extension.js",
+  "contributes": {
+    "commands": [
+      {
+        "command": "sirji.chat",
+        "title": "Sirji"
+      }
+    ]
+  },
+  "scripts": {
+    "vscode:prepublish": "npm run compile",
+    "compile": "tsc -p ./ && cp -r src/views src/py_scripts src/assets out/",
+    "watch-ts": "tsc -watch -p ./",
+    "watch-other": "nodemon --watch src --ext html,css,js,py --exec 'mkdir -p out/ && cp -r src/views src/py_scripts src/assets out/'",
+    "watch": "concurrently npm:watch-*",
+    "pretest": "npm run compile && npm run lint",
+    "lint": "eslint src --ext ts",
+    "test": "vscode-test",
+    "prepack": "cp ../../README.md README.md && cp ../../LICENSE LICENSE",
+    "package": "vsce package",
+    "publish": "vsce publish"
+  },
+  "devDependencies": {
+    "@types/mocha": "^10.0.6",
+    "@types/node": "18.x",
+    "@types/vscode": "^1.80.2",
+    "@typescript-eslint/eslint-plugin": "^7.0.2",
+    "@typescript-eslint/parser": "^7.0.2",
+    "@vscode/test-cli": "^0.0.6",
+    "@vscode/test-electron": "^2.3.9",
+    "concurrently": "^8.2.2",
+    "eslint": "^8.56.0",
+    "nodemon": "^3.1.0",
+    "typescript": "^5.3.3"
+  }
+}
diff --git a/sirji/vscode-extension/src/assets/sirji.png b/sirji/vscode-extension/src/assets/sirji.png
new file mode 100644
index 00000000..8184e237
Binary files /dev/null and b/sirji/vscode-extension/src/assets/sirji.png differ
diff --git a/sirji/vscode-extension/src/extension.ts b/sirji/vscode-extension/src/extension.ts
new file mode 100644
index 00000000..4f82a4c3
--- /dev/null
+++ b/sirji/vscode-extension/src/extension.ts
@@ -0,0 +1,45 @@
+import * as vscode from 'vscode';
+import { Facilitator } from './utils/facilitator';
+
+// Variable to keep track of the chat panel instance
+let chatPanel: vscode.WebviewPanel | undefined = undefined;
+
+/**
+ * Function to activate the extension.
+ * This function is called when the extension is activated.
+ * It registers a command and adds it to the context's subscriptions.
+ *
+ * @param context - The extension context
+ */
+function activate(context: vscode.ExtensionContext) {
+  // Register the command `sirji.chat`. This command is defined in the package.json file.
+  let disposable = vscode.commands.registerCommand('sirji.chat', async function () {
+    // If chatPanel already exists, reveal it in the first column without creating a new one
+    if (chatPanel) {
+      chatPanel.reveal(vscode.ViewColumn.One);
+      return;
+    }
+    // Else, create a new chatPanel instance using the Facilitator class and listen for its disposal.
+    else {
+      chatPanel = await new Facilitator(context).init();
+      chatPanel?.onDidDispose(() => {
+        // Clean up when the chatPanel is closed by setting it to undefined
+        chatPanel = undefined;
+      });
+    }
+  });
+
+  // Add the command to the context's subscriptions to ensure proper disposal
+  context.subscriptions.push(disposable);
+}
+
+/**
+ * Function to deactivate the extension.
+ * This function is called when the extension is deactivated.
+ * Currently, there is no cleanup necessary on deactivation.
+ */
+function deactivate() {}
+
+// Export the activate and deactivate functions to be called by VS Code
+exports.activate = activate;
+exports.deactivate = deactivate;
diff --git a/sirji/vscode-extension/src/py_scripts/agents/coding_agent.py b/sirji/vscode-extension/src/py_scripts/agents/coding_agent.py
new file mode 100644
index 00000000..4da0a2ce
--- /dev/null
+++ b/sirji/vscode-extension/src/py_scripts/agents/coding_agent.py
@@ -0,0 +1,99 @@
+import argparse
+import os
+import json
+from sirji_messages import MessageFactory, ActionEnum, AgentEnum
+from sirji_agents import CodingAgent
+
+class CodingAgentRunner:
+    def get_workplace_file_path(self, filename):
+        return os.path.join(self._get_workspace_folder(), '.sirji', self._get_run_id_folder(), filename)
+    
+    def _get_workspace_folder(self):
+        workspace = os.environ.get("SIRJI_WORKSPACE")
+        if workspace is None:
+            raise ValueError(
+                "SIRJI_WORKSPACE is not set as an environment variable")
+        return workspace
+    
+    def _get_run_id_folder(self):
+        run_id = os.environ.get("SIRJI_RUN_ID")
+        if run_id is None:
+            raise ValueError(
+                "SIRJI_RUN_ID is not set as an environment variable")
+        return run_id
+
+    def read_or_initialize_conversation_file(self, file_path):
+        if not os.path.exists(file_path) or os.path.getsize(file_path) == 0:
+            with open(file_path, 'w') as file:
+                json.dump({"conversations": [], "prompt_tokens": 0, "completion_tokens": 0}, file, indent=4)
+                return [], 0, 0
+        else:
+            with open(file_path, 'r') as file:
+                contents = json.load(file)
+                return contents["conversations"], contents["prompt_tokens"], contents["completion_tokens"]
+
+    def write_conversations_to_file(self, file_path, conversations, prompt_tokens, completion_tokens):
+        with open(file_path, 'w') as file:
+            json.dump({"conversations": conversations, "prompt_tokens": prompt_tokens, "completion_tokens": completion_tokens}, file, indent=4)
+  
+
+    def process_input_file(self, input_file_path, conversations):
+        with open(input_file_path, 'r') as file:
+            contents = file.read()
+
+        if not conversations:
+            message_class = MessageFactory[ActionEnum.PROBLEM_STATEMENT.name]
+            message_str = message_class().generate({"details": contents})
+        elif conversations[-1]['parsed_content']['TO'] == AgentEnum.USER.name:
+            last_action = conversations[-1]['parsed_content']['ACTION']
+
+            if last_action in [ActionEnum.INFORM.name, ActionEnum.QUESTION.name]:
+                message_class = MessageFactory[ActionEnum.ANSWER.name]
+                message_str = message_class().generate({"details": contents})
+            elif last_action == ActionEnum.SOLUTION_COMPLETE.name:
+                message_class = MessageFactory[ActionEnum.FEEDBACK.name]
+                message_str = message_class().generate({"details": contents})
+            elif last_action in [ActionEnum.STEP_STARTED.name, ActionEnum.STEP_COMPLETED.name]:
+                message_class = MessageFactory[ActionEnum.ACKNOWLEDGE.name]
+                message_str = message_class().generate({"details": contents})
+                print(f"Message: {message_str}")
+            else:
+                raise ValueError(f"Invalid action: {last_action}")
+        elif conversations[-1]['parsed_content']['TO'] == AgentEnum.EXECUTOR.name:
+            last_action = conversations[-1]['parsed_content']['ACTION']
+
+            if last_action in [ActionEnum.EXECUTE_COMMAND.name, ActionEnum.RUN_SERVER.name, ActionEnum.CREATE_FILE.name, ActionEnum.INSTALL_PACKAGE.name, ActionEnum.READ_FILE.name, ActionEnum.READ_DIR.name]:
+                message_class = MessageFactory[ActionEnum.OUTPUT.name]
+                message_str = message_class().generate({"details": contents})
+            else:
+                raise ValueError(f"Invalid action: {last_action}")
+        else:
+            message_str = contents
+
+        return message_str
+
+    def process_message(self, message_str, conversations):
+        coder = CodingAgent()
+        return coder.message(message_str, conversations)
+        
+    def main(self, input_file, conversation_file):
+        input_file_path = self.get_workplace_file_path(input_file)
+        conversation_file_path = self.get_workplace_file_path(conversation_file)
+
+        conversations, prompt_tokens, completion_tokens = self.read_or_initialize_conversation_file(conversation_file_path)
+        message_str = self.process_input_file(input_file_path, conversations)
+
+        response, conversations, prompt_tokens_consumed, completion_tokens_consumed = self.process_message(message_str, conversations)
+        
+        prompt_tokens += prompt_tokens_consumed
+        completion_tokens += completion_tokens_consumed
+        self.write_conversations_to_file(conversation_file_path, conversations, prompt_tokens, completion_tokens)
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description="Process interactions.")
+    parser.add_argument("--input", required=True, help="Input file name")
+    parser.add_argument("--conversation", required=True, help="Conversation file name")
+    
+    args = parser.parse_args()
+    agent_runner = CodingAgentRunner()
+    agent_runner.main(args.input, args.conversation)
\ No newline at end of file
diff --git a/sirji/vscode-extension/src/py_scripts/agents/planning_agent.py b/sirji/vscode-extension/src/py_scripts/agents/planning_agent.py
new file mode 100644
index 00000000..6843ac60
--- /dev/null
+++ b/sirji/vscode-extension/src/py_scripts/agents/planning_agent.py
@@ -0,0 +1,68 @@
+import argparse
+import os
+import json
+from sirji_agents import PlanningAgent
+
+class PlanningAgentRunner:
+    def get_workplace_file_path(self, filename):
+        return os.path.join(self._get_workspace_folder(), '.sirji', self._get_run_id_folder(), filename)
+    
+    def _get_workspace_folder(self):
+        workspace = os.environ.get("SIRJI_WORKSPACE")
+        if workspace is None:
+            raise ValueError(
+                "SIRJI_WORKSPACE is not set as an environment variable")
+        return workspace
+    
+    def _get_run_id_folder(self):
+        run_id = os.environ.get("SIRJI_RUN_ID")
+        if run_id is None:
+            raise ValueError(
+                "SIRJI_RUN_ID is not set as an environment variable")
+        return run_id
+
+    def read_or_initialize_conversation_file(self, file_path):
+            if not os.path.exists(file_path) or os.path.getsize(file_path) == 0:
+                with open(file_path, 'w') as file:
+                    json.dump({"conversations": [], "prompt_tokens": 0, "completion_tokens": 0}, file, indent=4)
+                    return [], 0, 0
+            else:
+                with open(file_path, 'r') as file:
+                    contents = json.load(file)
+                    return contents["conversations"], contents["prompt_tokens"], contents["completion_tokens"]
+
+    def write_conversations_to_file(self, file_path, conversations, prompt_tokens, completion_tokens):
+        with open(file_path, 'w') as file:
+            json.dump({"conversations": conversations, "prompt_tokens": prompt_tokens, "completion_tokens": completion_tokens}, file, indent=4)
+    
+    def process_input_file(self, input_file_path, conversations):
+        with open(input_file_path, 'r') as file:
+            contents = file.read()
+        return contents
+
+    def process_message(self, message_str, conversations):
+        planner = PlanningAgent()
+        return planner.message(message_str, conversations)
+        
+    def main(self, input_file, conversation_file):
+        input_file_path = self.get_workplace_file_path(input_file)
+        conversation_file_path = self.get_workplace_file_path(conversation_file)
+
+        conversations, prompt_tokens, completion_tokens = self.read_or_initialize_conversation_file(conversation_file_path)
+
+        message_str = self.process_input_file(input_file_path, conversations)
+
+        response, conversations, prompt_tokens_consumed, completion_tokens_consumed = self.process_message(message_str, conversations)
+        
+        prompt_tokens += prompt_tokens_consumed
+        completion_tokens += completion_tokens_consumed
+        self.write_conversations_to_file(conversation_file_path, conversations, prompt_tokens, completion_tokens)
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description="Process interactions.")
+    parser.add_argument("--input", required=True, help="Input file name")
+    parser.add_argument("--conversation", required=True, help="Conversation file name")
+    
+    args = parser.parse_args()
+    agent_runner = PlanningAgentRunner()
+    agent_runner.main(args.input, args.conversation)
\ No newline at end of file
diff --git a/sirji/vscode-extension/src/py_scripts/agents/research_agent.py b/sirji/vscode-extension/src/py_scripts/agents/research_agent.py
new file mode 100644
index 00000000..e14192b7
--- /dev/null
+++ b/sirji/vscode-extension/src/py_scripts/agents/research_agent.py
@@ -0,0 +1,83 @@
+import argparse
+import os
+import json
+from sirji_messages import message_parse
+from sirji_agents import ResearchAgent
+
+class ResearchAgentRunner:
+    def get_workplace_file_path(self, filename):
+        return os.path.join(self._get_workspace_folder(), '.sirji', self._get_run_id_folder(), filename)
+    
+    def _get_workspace_folder(self):
+        workspace = os.environ.get("SIRJI_WORKSPACE")
+        if workspace is None:
+            raise ValueError(
+                "SIRJI_WORKSPACE is not set as an environment variable")
+        return workspace
+    
+    def _get_run_id_folder(self):
+        run_id = os.environ.get("SIRJI_RUN_ID")
+        if run_id is None:
+            raise ValueError(
+                "SIRJI_RUN_ID is not set as an environment variable")
+        return run_id 
+    
+    def read_or_initialize_conversation_file(self, file_path):
+        if not os.path.exists(file_path) or os.path.getsize(file_path) == 0:
+            with open(file_path, 'w') as file:
+                json.dump({"conversations": [], "init_payload": {}, "prompt_tokens": 0, "completion_tokens": 0}, file, indent=4)
+                return [], {}, 0, 0
+        else:
+            with open(file_path, 'r') as file:
+                contents = json.load(file)
+                return contents["conversations"], contents["init_payload"], contents["prompt_tokens"], contents["completion_tokens"]
+
+    def write_conversations_to_file(self, file_path, conversations, init_payload, prompt_tokens, completion_tokens):
+        with open(file_path, 'w') as file:
+            json.dump({"conversations": conversations, "init_payload": init_payload, "prompt_tokens": prompt_tokens, "completion_tokens": completion_tokens}, file, indent=4)
+
+    def process_input_file(self, input_file_path, conversations):
+        with open(input_file_path, 'r') as file:
+            contents = file.read()
+        return contents
+
+    def process_message(self, message_str, conversations, init_payload):
+        # Appending the input message to the conversations
+        conversations.append({"role": "user", "content": message_str, "parsed_content": message_parse(message_str)})
+
+        researcher = ResearchAgent('openai_assistant', 'openai_assistant', init_payload)
+
+        response, prompt_tokens, completion_tokens = researcher.message(message_str)
+
+        updated_init_payload = researcher.init_payload
+
+        # Updating the init_payload in place, preserving the reference
+        init_payload.update(updated_init_payload)
+
+        # Appending the response to the conversations
+        conversations.append({"role": "assistant", "content": response, "parsed_content": message_parse(response)})
+
+        return response, prompt_tokens, completion_tokens
+        
+    def main(self, input_file, conversation_file):
+        input_file_path = self.get_workplace_file_path(input_file)
+        conversation_file_path = self.get_workplace_file_path(conversation_file)
+
+        conversations, init_payload, prompt_tokens, completion_tokens = self.read_or_initialize_conversation_file(conversation_file_path)
+
+        message_str = self.process_input_file(input_file_path, conversations)
+
+        response, prompt_tokens_consumed, completion_tokens_consumed = self.process_message(message_str, conversations, init_payload)
+
+        prompt_tokens += prompt_tokens_consumed
+        completion_tokens += completion_tokens_consumed
+        self.write_conversations_to_file(conversation_file_path, conversations, init_payload, prompt_tokens, completion_tokens)
+
+if __name__ == "__main__":
+    parser = argparse.ArgumentParser(description="Process interactions.")
+    parser.add_argument("--input", required=True, help="Input file name")
+    parser.add_argument("--conversation", required=True, help="Conversation file name")
+    
+    args = parser.parse_args()
+    agent_runner = ResearchAgentRunner()
+    agent_runner.main(args.input, args.conversation)
\ No newline at end of file
diff --git a/sirji/vscode-extension/src/py_scripts/requirements.txt b/sirji/vscode-extension/src/py_scripts/requirements.txt
new file mode 100644
index 00000000..0afabbda
--- /dev/null
+++ b/sirji/vscode-extension/src/py_scripts/requirements.txt
@@ -0,0 +1,3 @@
+urllib3==1.25.11
+sirji-messages==0.0.13
+sirji-agents==0.0.18
\ No newline at end of file
diff --git a/sirji/vscode-extension/src/py_scripts/setup_virtual_env.py b/sirji/vscode-extension/src/py_scripts/setup_virtual_env.py
new file mode 100644
index 00000000..758eabd7
--- /dev/null
+++ b/sirji/vscode-extension/src/py_scripts/setup_virtual_env.py
@@ -0,0 +1,58 @@
+import argparse
+import os
+import subprocess
+import sys
+
+def create_venv(venv):
+    """
+    Create a virtual environment if it doesn't exist.
+    """
+    if not os.path.exists(venv):
+        print(f"Creating virtual environment at {venv}")
+        subprocess.check_call([sys.executable, "-m", "venv", venv])
+    else:
+        print("Virtual environment already exists.")
+    return os.path.join(venv, "bin", "python") if os.name != "nt" else os.path.join(venv, "Scripts", "python.exe"), os.path.join(venv, "bin", 'playwright') 
+
+def install_packages(venv_python, requirements_path):
+    """
+    Install packages from a requirements.txt file using pip in the virtual environment.
+    """
+    print(f"Installing packages from {requirements_path}...")
+    subprocess.check_call([venv_python, "-m", "pip", "install", "-r", requirements_path])
+
+def install_playwright(venv_playwright):
+    """
+    Install playwright in the virtual environment.
+    """
+    print(f"Installing playwright...")
+    subprocess.check_call([venv_playwright, "install"])
+
+def parse_arguments():
+    """
+    Parse command-line arguments.
+    """
+    parser = argparse.ArgumentParser(description="Create a virtual environment and install packages from requirements.txt.")
+    parser.add_argument("--venv", help="The directory where the virtual environment will be created.")
+    return parser.parse_args()
+
+if __name__ == "__main__":
+    args = parse_arguments()
+    
+    venv_python, venv_playwright = create_venv(args.venv)
+
+    # Assuming the requirements.txt file is in the same directory as this script
+    script_dir = os.path.dirname(os.path.realpath(__file__))
+    requirements_path = os.path.join(script_dir, "requirements.txt")
+    
+    if os.path.isfile(requirements_path):
+        install_packages(venv_python, requirements_path)
+    else:
+        print(f"Error: requirements.txt file not found in {script_dir}")
+        sys.exit(1)
+
+    install_playwright(venv_playwright)
+
+    print(f"Setup complete. Virtual environment '{args.venv}' is ready to use with packages from requirements.txt.")
+
+    sys.exit(0)
\ No newline at end of file
diff --git a/sirji/vscode-extension/src/utils/constants.ts b/sirji/vscode-extension/src/utils/constants.ts
new file mode 100644
index 00000000..164fe8d2
--- /dev/null
+++ b/sirji/vscode-extension/src/utils/constants.ts
@@ -0,0 +1,59 @@
+import path from 'path';
+
+export const Constants = {
+  ENV_VARS_KEY: 'sirjiSecrets',
+  HISTORY_FOLDER: '.sirji',
+  PYHTON_VENV_FOLDER: getPythonVenvPath(),
+  CODER_JSON_FILE: 'coder.json',
+  PLANNER_JSON_FILE: 'planner.json',
+  RESEARCHER_JSON_FILE: 'researcher.json',
+  PYTHON_INPUT_FILE: 'input.txt',
+  PROMPT_TOKEN_PRICE_PER_MILLION_TOKENS: 10,
+  COMPLETION_TOKEN_PRICE_PER_MILLION_TOKENS: 30
+};
+
+function getPythonVenvPath(): string {
+  return path.join('.sirji', 'venv');
+}
+
+export const ACTION_ENUM = {
+  //user and coder conversations
+  PROBLEM_STATEMENT: 'PROBLEM_STATEMENT',
+  QUESTION: 'QUESTION',
+  ANSWER: 'ANSWER',
+  INFORM: 'INFORM',
+  STEP_STARTED: 'STEP_STARTED',
+  STEP_COMPLETED: 'STEP_COMPLETED',
+  ACKNOWLEDGE: 'ACKNOWLEDGE',
+  SOLUTION_COMPLETE: 'SOLUTION_COMPLETE',
+  FEEDBACK: 'FEEDBACK',
+
+  // coder and planner conversations
+  GENERATE_STEPS: 'GENERATE_STEPS',
+  STEPS: 'STEPS',
+
+  //coder and researcher conversations
+  TRAIN_USING_URL: 'TRAIN_USING_URL',
+  TRAIN_USING_SEARCH_TERM: 'TRAIN_USING_SEARCH_TERM',
+  TRAINING_OUTPUT: 'TRAINING_OUTPUT',
+  INFER: 'INFER',
+  RESPONSE: 'RESPONSE',
+
+  //coder and executor conversations
+  EXECUTE_COMMAND: 'EXECUTE_COMMAND',
+  RUN_SERVER: 'RUN_SERVER',
+  CREATE_FILE: 'CREATE_FILE',
+  READ_FILE: 'READ_FILE',
+  READ_DIR: 'READ_DIR',
+  INSTALL_PACKAGE: 'INSTALL_PACKAGE',
+  OUTPUT: 'OUTPUT',
+  OPEN_BROWSER: 'OPEN_BROWSER'
+};
+
+export const ACTOR_ENUM = {
+  USER: 'USER',
+  CODER: 'CODER',
+  PLANNER: 'PLANNER',
+  RESEARCHER: 'RESEARCHER',
+  EXECUTOR: 'EXECUTOR'
+};
diff --git a/sirji/vscode-extension/src/utils/create_file.ts b/sirji/vscode-extension/src/utils/create_file.ts
new file mode 100644
index 00000000..cac63152
--- /dev/null
+++ b/sirji/vscode-extension/src/utils/create_file.ts
@@ -0,0 +1,24 @@
+import * as vscode from 'vscode';
+import * as path from 'path';
+import * as fs from 'fs';
+
+export async function createFile(workspaceRootPath: string, fileName: string, fileContent: string): Promise<string> {
+  try {
+    const uri = vscode.Uri.file(path.join(workspaceRootPath, fileName));
+
+    const directoryPath = path.dirname(uri.fsPath);
+    if (!fs.existsSync(directoryPath)) {
+      await vscode.workspace.fs.createDirectory(vscode.Uri.file(directoryPath));
+    }
+
+    const content = Buffer.from(fileContent, 'utf8');
+    await vscode.workspace.fs.writeFile(uri, content);
+
+    console.log(`File created successfully: ${uri.fsPath}`);
+
+    return 'Done';
+  } catch (e) {
+    const errorMessage = `Failed to create or write to the file. Error: ${e}`;
+    return errorMessage;
+  }
+}
diff --git a/sirji/vscode-extension/src/utils/execute_command.ts b/sirji/vscode-extension/src/utils/execute_command.ts
new file mode 100644
index 00000000..6f7acb55
--- /dev/null
+++ b/sirji/vscode-extension/src/utils/execute_command.ts
@@ -0,0 +1,66 @@
+import * as vscode from 'vscode';
+import * as fs from 'fs';
+import * as path from 'path';
+
+const fsPromises = fs.promises;
+let sirjiTerminal: vscode.Terminal | undefined;
+
+async function checkForChanges(filePath: string, previousContent: string): Promise<{ hasChanges: boolean; currentContent: string }> {
+  const currentContent = await fsPromises.readFile(filePath, 'utf-8');
+  const hasChanges = currentContent !== previousContent;
+  return {
+    hasChanges,
+    currentContent
+  };
+}
+
+function constructResponse(isRunning: boolean, tempFilePath: string, tempFileContent: string): string {
+  let response = isRunning ? 'Execute Command still running.' : 'Execute command complete.';
+  response += ` Command execution output from ${tempFilePath}:\n`;
+  response += tempFileContent;
+  return response;
+}
+
+export async function executeCommand(command: string, workspaceRootPath: string): Promise<string> {
+  if (!sirjiTerminal) {
+    sirjiTerminal = vscode.window.createTerminal(`Sirji Terminal`);
+    vscode.window.onDidCloseTerminal((terminal) => {
+      if (terminal === sirjiTerminal) {
+        sirjiTerminal = undefined;
+      }
+    });
+  }
+
+  sirjiTerminal.show(true);
+
+  const fileName = `output.txt`;
+  const filePath = path.join(workspaceRootPath, fileName);
+  if (command.startsWith('source ')) {
+    command = `${command} 2> "${filePath}"`;
+  } else {
+    command = `${command} 2>&1 | tee "${filePath}"`;
+  }
+
+  sirjiTerminal.sendText(command);
+
+  await new Promise((resolve) => setTimeout(resolve, 1000));
+
+  let previousContent = '';
+
+  const checkForChangesAndConstructResponse = (): Promise<string> => {
+    return new Promise((resolve, reject) => {
+      const intervalId = setInterval(async () => {
+        const { hasChanges, currentContent } = await checkForChanges(filePath, previousContent);
+
+        if (!hasChanges) {
+          clearInterval(intervalId);
+          resolve(constructResponse(false, filePath, currentContent));
+        } else {
+          previousContent = currentContent;
+        }
+      }, 10000);
+    });
+  };
+
+  return await checkForChangesAndConstructResponse();
+}
diff --git a/sirji/vscode-extension/src/utils/execute_task.ts b/sirji/vscode-extension/src/utils/execute_task.ts
new file mode 100644
index 00000000..cd78e1de
--- /dev/null
+++ b/sirji/vscode-extension/src/utils/execute_task.ts
@@ -0,0 +1,94 @@
+import * as vscode from 'vscode';
+import * as fs from 'fs';
+import * as path from 'path';
+
+let currentTaskExecution: any = null;
+
+async function checkForChanges(filePath: string, previousContent: string): Promise<{ hasChanges: boolean; currentContent: string }> {
+  const currentContent = fs.readFileSync(filePath, 'utf-8');
+
+  const hasChanges = currentContent !== previousContent;
+
+  return {
+    hasChanges,
+    currentContent
+  };
+}
+
+function constructResponse(isRunning: Boolean, tempFilePath: string, tempFileContent: string): string {
+  let response = isRunning ? 'Execute Command still running.' : 'Execute command complete. ';
+  response += `Command execution output from ${tempFilePath}\n`;
+  response += `${tempFileContent}`;
+  return response;
+}
+
+export async function executeTask(command: string, workspaceRootPath: string): Promise<string> {
+  return new Promise(async (resolve, reject) => {
+    const tempFileName = `output.txt`;
+    const tempFilePath = path.join(workspaceRootPath, tempFileName);
+    let tempFileContent: string;
+
+    command = `(${command}) 2>&1 | tee "${tempFilePath}"`;
+
+    const shellExecution = new vscode.ShellExecution(command);
+    const taskDefinition = { type: 'shell' };
+    const task = new vscode.Task(taskDefinition, vscode.TaskScope.Workspace, 'Sirji', 'Custom', shellExecution);
+
+    const disposables: vscode.Disposable[] = [];
+
+    let isTaskExecutionInProgress = false;
+    let checkTaskPeriodically: any = null;
+
+    try {
+      console.log(` executeTask currentTaskExecution ${command}:`, { currentTaskExecution });
+      if (currentTaskExecution) {
+        try {
+          currentTaskExecution.terminate();
+          console.log('executeTask Terminated existing task.');
+        } catch (terminateError) {
+          console.error('executeTask Error terminating existing task:', terminateError);
+        }
+      }
+
+      const execution = await vscode.tasks.executeTask(task);
+
+      console.log(`executeTask new task ${command}:`, { isTaskExecutionInProgress, tempFileName });
+
+      disposables.push(
+        vscode.tasks.onDidStartTask((event) => {
+          console.log(`executeTask inside onDidStartTask ${command}:`, event);
+          if (event.execution.task === task) {
+            if (event.execution.task.name === 'Sirji') {
+              currentTaskExecution = event.execution;
+            }
+            isTaskExecutionInProgress = true;
+            console.log(`executeTask setting setInterval ${command}:`, { isTaskExecutionInProgress, tempFileName });
+            checkTaskPeriodically = setInterval(async function () {
+              const response = await checkForChanges(tempFilePath, tempFileContent);
+
+              console.log(`executeTask setInterval executed ${command}:`, { isTaskExecutionInProgress, tempFileName });
+
+              tempFileContent = response.currentContent;
+              clearInterval(checkTaskPeriodically);
+              return resolve(constructResponse(isTaskExecutionInProgress, tempFilePath, tempFileContent));
+            }, 5000);
+          }
+        })
+      );
+
+      disposables.push(
+        vscode.tasks.onDidEndTask(async (event) => {
+          console.log(`executeTask inside onDidEndTaskProcess ${command}:`, event);
+          isTaskExecutionInProgress = false;
+          clearInterval(checkTaskPeriodically);
+          const response = await checkForChanges(tempFilePath, tempFileContent);
+          tempFileContent = response.currentContent;
+          return resolve(constructResponse(isTaskExecutionInProgress, tempFilePath, tempFileContent));
+        })
+      );
+    } catch (error) {
+      console.error(`Failed to execute task: ${error}`);
+      throw error;
+    }
+  });
+}
diff --git a/sirji/vscode-extension/src/utils/facilitator.ts b/sirji/vscode-extension/src/utils/facilitator.ts
new file mode 100644
index 00000000..0c6f232c
--- /dev/null
+++ b/sirji/vscode-extension/src/utils/facilitator.ts
@@ -0,0 +1,697 @@
+import * as vscode from 'vscode';
+import { randomBytes } from 'crypto';
+import path from 'path';
+
+import { renderView } from './render_view';
+import { MaintainHistory } from './maintain_history';
+import { invokeAgent } from './invoke_agent';
+import { SecretStorage } from './secret_storage';
+import { Constants, ACTOR_ENUM, ACTION_ENUM } from './constants';
+import { openBrowser } from './open_browser';
+import { executeCommand } from './execute_command';
+import { createFile } from './create_file';
+import { readContent } from './read_content';
+import { executeTask } from './execute_task';
+
+export class Facilitator {
+  private context: vscode.ExtensionContext | undefined;
+  private workspaceRootUri: any;
+  private workspaceRootPath: any;
+  private sirjiRunId: string = '';
+  private chatPanel: vscode.WebviewPanel | undefined;
+  private secretManager: SecretStorage | undefined;
+  private envVars: any = undefined;
+  private historyManager: MaintainHistory | undefined;
+  private isPlannerTabShown: Boolean = false;
+  private isResearcherTabShown: Boolean = false;
+  private isCoderTabShown: Boolean = false;
+
+  public constructor(context: vscode.ExtensionContext) {
+    const oThis = this;
+
+    oThis.context = context;
+  }
+
+  public async init() {
+    const oThis = this;
+
+    // Setup workspace
+    await oThis.selectWorkspace();
+
+    // Setup Environment
+    await oThis.setupEnvironment();
+
+    // Setup secret manager
+    await oThis.setupSecretManager();
+
+    // Setup History Manager
+    // oThis.setupHistoryManager();
+
+    // Open Chat Panel
+    oThis.openChatViewPanel();
+
+    return oThis.chatPanel;
+  }
+
+  private async setupEnvironment() {
+    const oThis = this;
+    oThis.sirjiRunId = randomBytes(16).toString('hex');
+  }
+
+  private async selectWorkspace(): Promise<void> {
+    const oThis = this;
+
+    oThis.workspaceRootUri = vscode.workspace.workspaceFolders ? vscode.workspace.workspaceFolders[0].uri : null;
+    oThis.workspaceRootPath = oThis.workspaceRootUri ? oThis.workspaceRootUri.fsPath : null;
+
+    if (!oThis.workspaceRootUri) {
+      // Prompt user to open a folder
+      const openFolderMsg = 'No workspace/folder is open. Please open a folder to proceed.';
+      await vscode.window.showErrorMessage(openFolderMsg, 'Open Folder').then(async (selection) => {
+        if (selection === 'Open Folder') {
+          await vscode.commands.executeCommand('workbench.action.files.openFolder');
+          return; // Exit the current command execution to avoid further operations until folder is opened
+        }
+      });
+    }
+  }
+
+  private setupHistoryManager() {
+    const oThis = this;
+    oThis.historyManager = new MaintainHistory();
+
+    oThis.historyManager.createHistoryFolder(oThis.workspaceRootPath, oThis.sirjiRunId);
+  }
+
+  private async setupSecretManager() {
+    const oThis = this;
+
+    oThis.secretManager = new SecretStorage(oThis.context);
+    await oThis.retrieveSecret();
+  }
+
+  private async retrieveSecret() {
+    const oThis = this;
+
+    oThis.envVars = await oThis.secretManager?.retrieveSecret(Constants.ENV_VARS_KEY);
+  }
+
+  private async setSecretEnvVars(data: any) {
+    const oThis = this;
+
+    let responseContent;
+
+    try {
+      await oThis.secretManager?.storeSecret(Constants.ENV_VARS_KEY, JSON.stringify(data));
+      responseContent = {
+        success: true,
+        message: 'Great! Your environment is all setup and ready to roll! What would you like me to build today?'
+      };
+
+      await oThis.retrieveSecret();
+    } catch (error) {
+      console.log(error);
+      responseContent = {
+        success: false,
+        message: error
+      };
+    }
+
+    oThis.chatPanel?.webview.postMessage({
+      type: 'settingSaved',
+      content: { message: responseContent, allowUserMessage: true }
+    });
+  }
+
+  private openChatViewPanel() {
+    const oThis = this;
+
+    oThis.chatPanel = renderView(oThis.context, 'chat', oThis.workspaceRootUri, oThis.workspaceRootPath, oThis.sirjiRunId);
+
+    oThis.chatPanel.webview.onDidReceiveMessage(
+      async (message: any) => {
+        await oThis.handleMessagesFromChatPanel(message);
+      },
+      undefined,
+      (oThis.context || {}).subscriptions
+    );
+  }
+
+  private async readCoderLogs() {
+    const oThis = this;
+
+    const coderConversationFilePath = path.join(oThis.workspaceRootPath, Constants.HISTORY_FOLDER, oThis.sirjiRunId, 'logs', 'coder.log');
+
+    let coderLogFileContent = '';
+    
+    if (oThis.historyManager?.checkIfFileExists(coderConversationFilePath)) {
+      coderLogFileContent = oThis.historyManager?.readFile(coderConversationFilePath);
+      // return coderLogFileContent;
+    }
+
+    this.chatPanel?.webview.postMessage({
+      type: 'coderLogs',
+      content: coderLogFileContent
+    });
+  }
+
+  private async readPlannerLogs() {
+    const oThis = this;
+
+    const plannerConversationFilePath = path.join(oThis.workspaceRootPath, Constants.HISTORY_FOLDER, oThis.sirjiRunId, 'logs', 'planner.log');
+
+    let plannerLogFileContent = '';
+    if (oThis.historyManager?.checkIfFileExists(plannerConversationFilePath)) {
+      plannerLogFileContent = oThis.historyManager?.readFile(plannerConversationFilePath);
+      // return plannerLogFileContent;
+    }
+
+    oThis.chatPanel?.webview.postMessage({
+      type: 'plannerLogs',
+      content: plannerLogFileContent
+    });
+  }
+
+  private async readResearcherLogs() {
+    const oThis = this;
+
+    const researcherConversationFilePath = path.join(oThis.workspaceRootPath, Constants.HISTORY_FOLDER, oThis.sirjiRunId, 'logs', 'researcher.log');
+
+    let researcherLogFileContent = '';
+    if (oThis.historyManager?.checkIfFileExists(researcherConversationFilePath)) {
+      researcherLogFileContent = oThis.historyManager?.readFile(researcherConversationFilePath);
+      // return researcherLogFileContent;
+    }
+
+    this.chatPanel?.webview.postMessage({
+      type: 'researcherLogs',
+      content: researcherLogFileContent
+    });
+  }
+
+  private async sendWelcomeMessage() {
+    const oThis = this;
+
+    oThis.chatPanel?.webview.postMessage({
+      type: 'botMessage',
+      content: {
+        message: 'Hello, I am Sirji. Please wait while i am setting up the workspace...',
+        allowUserMessage: false,
+        messageInputText: 'Sirji> is setting up the workspace... Please wait...'
+      }
+    });
+
+    // Setup Python virtual env and Install dependencies
+
+    try {
+      await oThis.setupVirtualEnv();
+    } catch (error) {
+      oThis.chatPanel?.webview.postMessage({
+        type: 'botMessage',
+        content: {
+          message: `Unable to setup python virtual environment. Error: ${error}`,
+          allowUserMessage: false
+        }
+      });
+      return;
+    }
+
+    if (!oThis.envVars) {
+      oThis.chatPanel?.webview.postMessage({
+        type: 'botMessage',
+        content: { message: "Please configure your environment by simply tapping on the settings icon. Let's get you all set up and ready to go!", allowUserMessage: false }
+      });
+    } else {
+      oThis.chatPanel?.webview.postMessage({
+        type: 'botMessage',
+        content: { message: 'I am all setup! What would you like me to build today?', allowUserMessage: true }
+      });
+    }
+  }
+
+  private async setupVirtualEnv(): Promise<void> {
+    const oThis = this;
+
+    try {
+      await invokeAgent(oThis.context, oThis.workspaceRootPath, oThis.sirjiRunId, path.join(__dirname, '..', 'py_scripts', 'setup_virtual_env.py'), [
+        '--venv',
+        path.join(oThis.workspaceRootPath, Constants.PYHTON_VENV_FOLDER)
+      ]);
+    } catch (error) {
+      oThis.sendErrorToChatPanel(error);
+    }
+  }
+
+  private async handleMessagesFromChatPanel(message: any) {
+    const oThis = this;
+
+    switch (message.type) {
+      case 'webViewReady':
+        await oThis.sendWelcomeMessage();
+        break;
+
+      case 'saveSettings':
+        await oThis.setSecretEnvVars(message.content);
+        break;
+
+      case 'requestPlannerLogs':
+        await oThis.readPlannerLogs();
+        break;
+
+      case 'requestResearcherLogs':
+        await oThis.readResearcherLogs();
+        break;
+
+      case 'requestCoderLogs':
+        await oThis.readCoderLogs();
+        break;
+
+      case 'userMessage':
+        if (!oThis.historyManager) {
+          oThis.setupHistoryManager();
+        }
+
+        await oThis.initFacilitation(message.content, {
+          TO: ACTOR_ENUM.CODER
+        });
+        break;
+
+      default:
+        vscode.window.showErrorMessage(`Unknown message received from chat panel: ${message}`);
+    }
+  }
+
+  private async initFacilitation(rawMessage: string, parsedMessage: any) {
+    const oThis = this;
+
+    let keepFacilitating: Boolean = true;
+    while (keepFacilitating) {
+      console.log('inside while loop', parsedMessage);
+      const inputFilePath = path.join(oThis.workspaceRootPath, Constants.HISTORY_FOLDER, oThis.sirjiRunId, Constants.PYTHON_INPUT_FILE);
+      switch (parsedMessage.TO) {
+        case ACTOR_ENUM.CODER:
+          if (parsedMessage.ACTION === ACTION_ENUM.STEPS) {
+            oThis.chatPanel?.webview.postMessage({
+              type: 'plannedSteps',
+              content: parsedMessage.PARSED_STEPS
+            });
+          }
+
+          if (!oThis.isCoderTabShown) {
+            oThis.isCoderTabShown = true;
+            oThis.chatPanel?.webview.postMessage({
+              type: 'showCoderTab',
+              content: {
+                sirjiRunId: oThis.sirjiRunId,
+                logs: oThis.readCoderLogs()
+              }
+            });
+          }
+
+          oThis.historyManager?.writeFile(inputFilePath, rawMessage);
+
+          oThis.toCoderRelayToChatPanel(parsedMessage);
+
+          const coderConversationFilePath = path.join(oThis.workspaceRootPath, Constants.HISTORY_FOLDER, oThis.sirjiRunId, Constants.CODER_JSON_FILE);
+
+          const codingAgentPath = path.join(__dirname, '..', 'py_scripts', 'agents', 'coding_agent.py');
+
+          try {
+            await invokeAgent(oThis.context, oThis.workspaceRootPath, oThis.sirjiRunId, codingAgentPath, ['--input', inputFilePath, '--conversation', coderConversationFilePath]);
+          } catch (error) {
+            oThis.sendErrorToChatPanel(error);
+          }
+
+          const coderConversationContent = JSON.parse(oThis.historyManager?.readFile(coderConversationFilePath));
+
+          const lastCoderMessage: any = coderConversationContent.conversations[coderConversationContent.conversations.length - 1];
+
+          console.log('lastCoderMessage', lastCoderMessage);
+
+          rawMessage = lastCoderMessage?.content;
+
+          parsedMessage = lastCoderMessage?.parsed_content;
+
+          oThis.fromCoderRelayToChatPanel(parsedMessage);
+
+          break;
+
+        case ACTOR_ENUM.RESEARCHER:
+          if (!oThis.isResearcherTabShown) {
+            oThis.isResearcherTabShown = true;
+            oThis.chatPanel?.webview.postMessage({
+              type: 'showResearcherTab',
+              content: {
+                sirjiRunId: oThis.sirjiRunId
+              }
+            });
+          }
+          oThis.historyManager?.writeFile(inputFilePath, rawMessage);
+
+          const researcherConversationFilePath = path.join(oThis.workspaceRootPath, Constants.HISTORY_FOLDER, oThis.sirjiRunId, Constants.RESEARCHER_JSON_FILE);
+
+          const researcherAgentPath = path.join(__dirname, '..', 'py_scripts', 'agents', 'research_agent.py');
+
+          try {
+            await invokeAgent(oThis.context, oThis.workspaceRootPath, oThis.sirjiRunId, researcherAgentPath, ['--input', inputFilePath, '--conversation', researcherConversationFilePath]);
+          } catch (error) {
+            oThis.sendErrorToChatPanel(error);
+          }
+
+          const researcherConversationContent = JSON.parse(oThis.historyManager?.readFile(researcherConversationFilePath));
+
+          const lastResearcherMessage: any = researcherConversationContent.conversations[researcherConversationContent.conversations.length - 1];
+
+          console.log('lastResearcherMessage', lastResearcherMessage);
+
+          rawMessage = lastResearcherMessage?.content;
+
+          parsedMessage = lastResearcherMessage?.parsed_content;
+
+          break;
+
+        case ACTOR_ENUM.PLANNER:
+          if (!oThis.isPlannerTabShown) {
+            oThis.isPlannerTabShown = true;
+            oThis.chatPanel?.webview.postMessage({
+              type: 'showPlannerTab',
+              content: {
+                sirjiRunId: oThis.sirjiRunId
+              }
+            });
+          }
+          oThis.historyManager?.writeFile(inputFilePath, rawMessage);
+
+          const plannerConversationFilePath = path.join(oThis.workspaceRootPath, Constants.HISTORY_FOLDER, oThis.sirjiRunId, Constants.PLANNER_JSON_FILE);
+
+          const plannerAgentPath = path.join(__dirname, '..', 'py_scripts', 'agents', 'planning_agent.py');
+
+          try {
+            await invokeAgent(oThis.context, oThis.workspaceRootPath, oThis.sirjiRunId, plannerAgentPath, ['--input', inputFilePath, '--conversation', plannerConversationFilePath]);
+          } catch (error) {
+            oThis.sendErrorToChatPanel(error);
+          }
+
+          const plannerConversationContent = JSON.parse(oThis.historyManager?.readFile(plannerConversationFilePath));
+
+          const lastPlannerMessage: any = plannerConversationContent.conversations[plannerConversationContent.conversations.length - 1];
+
+          console.log('lastPlannerMessage', lastPlannerMessage);
+
+          rawMessage = lastPlannerMessage?.content;
+
+          parsedMessage = lastPlannerMessage?.parsed_content;
+
+          break;
+
+        case ACTOR_ENUM.USER:
+          if (parsedMessage.ACTION === ACTION_ENUM.STEP_STARTED) {
+            oThis.chatPanel?.webview.postMessage({
+              type: 'plannedStepStart',
+              content: parsedMessage.DETAILS
+            });
+
+            rawMessage = 'sure';
+            parsedMessage = {
+              TO: ACTOR_ENUM.CODER
+            };
+          }
+
+          if (parsedMessage.ACTION === ACTION_ENUM.STEP_COMPLETED) {
+            oThis.chatPanel?.webview.postMessage({
+              type: 'plannedStepComplete',
+              content: parsedMessage.DETAILS
+            });
+
+            rawMessage = 'sure';
+            parsedMessage = {
+              TO: ACTOR_ENUM.CODER
+            };
+          }
+
+          if (parsedMessage.ACTION === ACTION_ENUM.SOLUTION_COMPLETE) {
+            keepFacilitating = false;
+            oThis.chatPanel?.webview.postMessage({
+              type: 'solutionCompleted',
+              content: { message: parsedMessage.DETAILS, allowUserMessage: true }
+            });
+          }
+
+          if (parsedMessage.ACTION === ACTION_ENUM.QUESTION || parsedMessage.ACTION === ACTION_ENUM.INFORM) {
+            keepFacilitating = false;
+            oThis.chatPanel?.webview.postMessage({
+              type: 'botMessage',
+              content: { message: parsedMessage.DETAILS, allowUserMessage: true }
+            });
+          }
+          break;
+
+        case ACTOR_ENUM.EXECUTOR:
+          switch (parsedMessage.ACTION) {
+            case ACTION_ENUM.OPEN_BROWSER:
+              //TODO:Implement this
+              openBrowser(parsedMessage.URL);
+              console.log('Browse', parsedMessage);
+              break;
+
+            case ACTION_ENUM.INSTALL_PACKAGE:
+              const installPackageLogPath = path.join(oThis.workspaceRootPath, Constants.HISTORY_FOLDER, oThis.sirjiRunId);
+              const installPackageCommandRes = await executeCommand(parsedMessage.COMMAND, installPackageLogPath);
+              rawMessage = installPackageCommandRes;
+              parsedMessage = {
+                TO: ACTOR_ENUM.CODER
+              };
+              console.log('installPackageCommandRes', installPackageCommandRes);
+              break;
+
+            case ACTION_ENUM.EXECUTE_COMMAND:
+              const executedCommandLogPath = path.join(oThis.workspaceRootPath, Constants.HISTORY_FOLDER, oThis.sirjiRunId);
+
+              const executedCommandRes = await executeCommand(parsedMessage.COMMAND, executedCommandLogPath);
+
+              rawMessage = executedCommandRes;
+              parsedMessage = {
+                TO: ACTOR_ENUM.CODER
+              };
+              console.log('executedCommandRes', executedCommandRes);
+              break;
+
+            case ACTION_ENUM.RUN_SERVER:
+              const runServerLogPath = path.join(oThis.workspaceRootPath, Constants.HISTORY_FOLDER, oThis.sirjiRunId);
+
+              const runServerRes = await executeTask(parsedMessage.COMMAND, runServerLogPath);
+
+              rawMessage = runServerRes;
+              parsedMessage = {
+                TO: ACTOR_ENUM.CODER
+              };
+              console.log('runServerRes', runServerRes);
+              break;
+
+            case ACTION_ENUM.CREATE_FILE:
+              const createFileRes = await createFile(oThis.workspaceRootPath, parsedMessage.FILENAME, parsedMessage.CONTENT);
+              rawMessage = createFileRes;
+              parsedMessage = {
+                TO: ACTOR_ENUM.CODER
+              };
+              console.log('Create', createFileRes);
+              break;
+
+            case ACTION_ENUM.READ_DIR:
+              const readDirContentRes = await readContent(oThis.workspaceRootPath, parsedMessage.DIRPATH);
+              rawMessage = readDirContentRes;
+              parsedMessage = {
+                TO: ACTOR_ENUM.CODER
+              };
+              break;
+
+            case ACTION_ENUM.READ_FILE:
+              const readFileContentRes = await readContent(oThis.workspaceRootPath, parsedMessage.FILENAME);
+              rawMessage = readFileContentRes;
+              parsedMessage = {
+                TO: ACTOR_ENUM.CODER
+              };
+              break;
+
+            default:
+              console.log('Execution default', parsedMessage);
+              oThis.chatPanel?.webview.postMessage({
+                type: 'botMessage',
+                content: { message: `Executor called with unknown action: ${parsedMessage.ACTION}. Raw message: ${rawMessage}`, allowUserMessage: true }
+              });
+              keepFacilitating = false;
+              break;
+          }
+
+          break;
+
+        default:
+          console.log('Actor default', parsedMessage);
+          oThis.chatPanel?.webview.postMessage({
+            type: 'botMessage',
+            content: { message: `Received message with unknown TO: ${parsedMessage.TO}. Raw message: ${rawMessage}`, allowUserMessage: true }
+          });
+          keepFacilitating = false;
+          break;
+      }
+
+      const totalTokensUsed = await oThis.calculateTotalTokensUsed();
+
+      oThis.chatPanel?.webview.postMessage({
+        type: 'tokenUsed',
+        content: {
+          message: totalTokensUsed,
+          allowUserMessage: false
+        }
+      });
+    }
+  }
+
+  private fromCoderRelayToChatPanel(parsedMessage: any) {
+    const oThis = this;
+
+    let contentMessage = null;
+
+    if (!parsedMessage || !parsedMessage.ACTION) {
+      return;
+    }
+
+    switch (parsedMessage.ACTION) {
+      case ACTION_ENUM.GENERATE_STEPS:
+        contentMessage = 'Generating steps to solve the given problem statement.';
+        break;
+
+      case ACTION_ENUM.CREATE_FILE:
+        contentMessage = `Creating File: ${parsedMessage.FILENAME}`;
+        break;
+
+      case ACTION_ENUM.EXECUTE_COMMAND:
+        contentMessage = `Executing Command: ${parsedMessage.COMMAND}`;
+        break;
+
+      case ACTION_ENUM.RUN_SERVER:
+        contentMessage = `Running Server: ${parsedMessage.COMMAND}`;
+        break;
+
+      case ACTION_ENUM.INSTALL_PACKAGE:
+        contentMessage = `Installing Package: ${parsedMessage.COMMAND}`;
+        break;
+
+      case ACTION_ENUM.READ_FILE:
+        contentMessage = `Reading File: ${parsedMessage.FILENAME}`;
+        break;
+
+      case ACTION_ENUM.READ_DIR:
+        contentMessage = `Reading Files in Folder (and its Sub-Folders): ${parsedMessage.DIRPATH}`;
+        break;
+
+      case ACTION_ENUM.TRAIN_USING_URL:
+        contentMessage = `Training Research Agent (RAG): Using contents from ${parsedMessage.URL}`;
+        break;
+
+      case ACTION_ENUM.INFER:
+        contentMessage = 'Inferring from the Research Agent based on trained knowledge';
+        break;
+
+      default:
+        break;
+    }
+
+    if (!contentMessage) {
+      return;
+    }
+
+    oThis.chatPanel?.webview.postMessage({
+      type: 'botMessage',
+      content: {
+        message: contentMessage,
+        allowUserMessage: false
+      }
+    });
+  }
+
+  private toCoderRelayToChatPanel(parsedMessage: any) {
+    const oThis = this;
+
+    let contentMessage = null;
+
+    if (!parsedMessage || !parsedMessage.ACTION) {
+      return;
+    }
+
+    switch (parsedMessage.ACTION) {
+      case ACTION_ENUM.STEPS:
+        contentMessage = 'Steps generation done.';
+        break;
+
+      default:
+        break;
+    }
+
+    if (!contentMessage) {
+      return;
+    }
+
+    oThis.chatPanel?.webview.postMessage({
+      type: 'botMessage',
+      content: {
+        message: contentMessage,
+        allowUserMessage: false
+      }
+    });
+  }
+
+  private async calculateTotalTokensUsed() {
+    const oThis = this;
+
+    const coderConversationFilePath = path.join(oThis.workspaceRootPath, Constants.HISTORY_FOLDER, oThis.sirjiRunId, Constants.CODER_JSON_FILE);
+    const researcherConversationFilePath = path.join(oThis.workspaceRootPath, Constants.HISTORY_FOLDER, oThis.sirjiRunId, Constants.RESEARCHER_JSON_FILE);
+    const plannerConversationFilePath = path.join(oThis.workspaceRootPath, Constants.HISTORY_FOLDER, oThis.sirjiRunId, Constants.PLANNER_JSON_FILE);
+
+    const coderTokensUsed = await oThis.getTokensUsed(coderConversationFilePath);
+    const researcherTokensUsed = await oThis.getTokensUsed(researcherConversationFilePath);
+    const plannerTokensUsed = await oThis.getTokensUsed(plannerConversationFilePath);
+
+    const totalPromptTokens = coderTokensUsed.prompt_tokens + researcherTokensUsed.prompt_tokens + plannerTokensUsed.prompt_tokens;
+
+    const totalCompletionTokens = coderTokensUsed.completion_tokens + researcherTokensUsed.completion_tokens + plannerTokensUsed.completion_tokens;
+
+    const totalPromptTokensValueInDollar = (totalPromptTokens * Constants.PROMPT_TOKEN_PRICE_PER_MILLION_TOKENS) / 1000000.0;
+    const totalCompletionTokensValueInDollar = (totalCompletionTokens * Constants.COMPLETION_TOKEN_PRICE_PER_MILLION_TOKENS) / 1000000.0;
+
+    return {
+      total_prompt_tokens: totalPromptTokens,
+      total_completion_tokens: totalCompletionTokens,
+      total_prompt_tokens_value: totalPromptTokensValueInDollar,
+      total_completion_tokens_value: totalCompletionTokensValueInDollar
+    };
+  }
+
+  private async getTokensUsed(conversationFilePath: string): Promise<any> {
+    const oThis = this;
+
+    if (oThis.historyManager?.checkIfFileExists(conversationFilePath)) {
+      const conversationContent = JSON.parse(oThis.historyManager?.readFile(conversationFilePath));
+      return {
+        prompt_tokens: conversationContent.prompt_tokens,
+        completion_tokens: conversationContent.completion_tokens
+      };
+    }
+
+    return {
+      prompt_tokens: 0,
+      completion_tokens: 0
+    };
+  }
+
+  private sendErrorToChatPanel(error: any) {
+    const oThis = this;
+
+    const detailedErrorMessage = `An error occurred during the execution of the Python script: : ${error}`;
+
+    oThis.chatPanel?.webview.postMessage({
+      type: 'botMessage',
+      content: { message: detailedErrorMessage, allowUserMessage: true }
+    });
+  }
+}
diff --git a/sirji/vscode-extension/src/utils/invoke_agent.ts b/sirji/vscode-extension/src/utils/invoke_agent.ts
new file mode 100644
index 00000000..d35c7883
--- /dev/null
+++ b/sirji/vscode-extension/src/utils/invoke_agent.ts
@@ -0,0 +1,76 @@
+import * as childProcess from 'child_process';
+import * as vscode from 'vscode';
+import path from 'path';
+import os from 'os';
+import { SecretStorage } from './secret_storage';
+import { Constants } from './constants';
+import fs from 'fs';
+
+export async function invokeAgent(context: vscode.ExtensionContext | undefined, workspaceRootPath: string, sirjiRunId: string, scriptPath: string, args: string[] = []): Promise<any> {
+  console.log('Executing command:', 'python3', [scriptPath, ...args].join(' '));
+  const response = await executePythonScript(context, workspaceRootPath, sirjiRunId, scriptPath, args);
+  console.log(response);
+  return response;
+}
+
+function getPythonPath(workspaceRootPath: string, venvPath: string): string {
+  let pythonPath = 'python3';
+
+  if (fs.existsSync(venvPath)) {
+    // Determine the correct path for the Python executable based on the OS
+    const pythonExecutable = os.platform() === 'win32' ? 'python.exe' : 'bin/python3';
+
+    // Generate the full path to the Python executable inside the virtual environment
+    pythonPath = path.join(venvPath, pythonExecutable);
+  }
+
+  return pythonPath;
+}
+
+async function getEnvVars(context: vscode.ExtensionContext | undefined, workspaceRootPath: string): Promise<any> {
+  const secretManager = new SecretStorage(context);
+  const envVars = JSON.parse((await secretManager.retrieveSecret(Constants.ENV_VARS_KEY)) || '{}');
+  envVars.SIRJI_WORKSPACE = workspaceRootPath;
+
+  return envVars;
+}
+
+async function executePythonScript(context: vscode.ExtensionContext | undefined, workspaceRootPath: string, sirjiRunId: string, scriptPath: string, args: string[] = []): Promise<any> {
+  const venvPath = path.join(workspaceRootPath, Constants.PYHTON_VENV_FOLDER);
+  const pythonPath = getPythonPath(workspaceRootPath, venvPath);
+  const envVars = await getEnvVars(context, workspaceRootPath);
+  envVars.SIRJI_RUN_ID = sirjiRunId;
+
+  return new Promise((resolve, reject) => {
+    const process = childProcess.spawn(pythonPath, [scriptPath, ...args], {
+      env: envVars
+    });
+
+    let responseData = '',
+      errorData = '';
+
+    process.stdout.on('data', (data) => {
+      responseData += data.toString();
+    });
+
+    process.stderr.on('data', (data) => {
+      errorData += data.toString();
+    });
+
+    process.on('error', (error) => {
+      vscode.window.showErrorMessage(`Sirji> Python script execution error: ${error.message}`);
+      reject(error);
+    });
+
+    process.on('close', (code) => {
+      if (code === 0) {
+        resolve(responseData);
+      } else {
+        if (errorData) {
+          vscode.window.showErrorMessage(`Sirji> Python script execution error: ${errorData}`);
+        }
+        reject(errorData);
+      }
+    });
+  });
+}
diff --git a/sirji/vscode-extension/src/utils/maintain_history.ts b/sirji/vscode-extension/src/utils/maintain_history.ts
new file mode 100644
index 00000000..54720c78
--- /dev/null
+++ b/sirji/vscode-extension/src/utils/maintain_history.ts
@@ -0,0 +1,23 @@
+import * as fs from 'fs';
+import * as path from 'path';
+import { Constants } from './constants';
+
+export class MaintainHistory {
+  public createHistoryFolder(workspaceRootPath: any, sirjiRunId: string): void {
+    const dirPath = path.join(workspaceRootPath, Constants.HISTORY_FOLDER, sirjiRunId);
+    fs.mkdirSync(dirPath, { recursive: true });
+  }
+
+  public writeFile(filePath: any, data: any): void {
+    fs.writeFileSync(filePath, data, 'utf-8');
+  }
+
+  public readFile(filePath: any): any {
+    const rawData = fs.readFileSync(filePath, 'utf-8');
+    return rawData;
+  }
+
+  public checkIfFileExists(filePath: any): boolean {
+    return fs.existsSync(filePath);
+  }
+}
diff --git a/sirji/vscode-extension/src/utils/open_browser.ts b/sirji/vscode-extension/src/utils/open_browser.ts
new file mode 100644
index 00000000..f601c7ed
--- /dev/null
+++ b/sirji/vscode-extension/src/utils/open_browser.ts
@@ -0,0 +1,6 @@
+import * as vscode from "vscode";
+
+export function openBrowser(url: string) {
+  console.log("openBrowser", url);
+  vscode.commands.executeCommand("simpleBrowser.api.open", url);
+}
diff --git a/sirji/vscode-extension/src/utils/read_content.ts b/sirji/vscode-extension/src/utils/read_content.ts
new file mode 100644
index 00000000..2727b6ad
--- /dev/null
+++ b/sirji/vscode-extension/src/utils/read_content.ts
@@ -0,0 +1,63 @@
+import * as vscode from 'vscode';
+import * as path from 'path';
+import * as fs from 'fs/promises';
+
+const SKIP_LIST = ['__pycache__', '.git', '.github', '.gitlab', '.vscode', '.idea', 'node_modules', '.DS_Store', 'venv', '.venv', '.sass-cache', 'dist', 'out', 'build', 'logs', '.npm', 'temp', 'tmp'];
+
+export async function readContent(workspaceRootPath: string, inputPath: string): Promise<string> {
+  let fullPath = path.join(workspaceRootPath, inputPath);
+
+  function shouldSkip(name: string): boolean {
+    return SKIP_LIST.includes(name);
+  }
+
+  async function readFile(filePath: string): Promise<string> {
+    try {
+      const content = await fs.readFile(filePath, 'utf8');
+      const relativePath = path.relative(workspaceRootPath, filePath);
+      return `File: ${relativePath}\n\n${content}\n\n---\n\n`;
+    } catch (err) {
+      console.error(`Failed to read file ${filePath}:`, err);
+      throw err;
+    }
+  }
+
+  async function readDirectory(directoryPath: string): Promise<string> {
+    try {
+      let files = await fs.readdir(directoryPath, { withFileTypes: true });
+      let results = [];
+
+      for (const file of files) {
+        // Skip logic implementation
+        if (shouldSkip(file.name)) {
+          continue; // Skips current iteration if name matches any in SKIP_LIST
+        }
+
+        const currentPath = path.join(directoryPath, file.name);
+        if (file.isDirectory()) {
+          results.push(await readDirectory(currentPath));
+        } else {
+          results.push(await readFile(currentPath));
+        }
+      }
+
+      return results.join('');
+    } catch (err) {
+      console.error(`Failed to read directory ${directoryPath}:`, err);
+      throw err;
+    }
+  }
+
+  try {
+    const stats = await fs.stat(fullPath);
+
+    if (stats.isDirectory()) {
+      return readDirectory(fullPath);
+    } else {
+      return readFile(fullPath);
+    }
+  } catch (e) {
+    const errorMessage = `Failed to read the path. Error: ${e}`;
+    return errorMessage;
+  }
+}
diff --git a/sirji/vscode-extension/src/utils/render_view.ts b/sirji/vscode-extension/src/utils/render_view.ts
new file mode 100644
index 00000000..e6b4faae
--- /dev/null
+++ b/sirji/vscode-extension/src/utils/render_view.ts
@@ -0,0 +1,63 @@
+import * as vscode from 'vscode';
+import path from 'path';
+import * as fs from 'fs';
+
+export function renderView(context: vscode.ExtensionContext | undefined, view: string, workspaceRootUri: any, workspaceRootPath: any, sirjiRunId: string): vscode.WebviewPanel {
+  let viewDetails: any;
+
+  if (view === 'chat') {
+    viewDetails = getChatViewDetails(context);
+  } else {
+    throw new Error(`View not defined ${view} in renderer.ts`);
+  }
+
+  viewDetails.replaceInHtml.nonce = sirjiRunId;
+
+  const panel = viewDetails.panel;
+
+  panel.webview.html = getWebviewContent(viewDetails.htmlFilePath, viewDetails.replaceInHtml);
+
+  return panel;
+}
+
+function getWebviewContent(htmlFilePath: string, replaceInHtml: { [key: string]: string }): string {
+  // load required html file
+  let htmlContent = fs.readFileSync(htmlFilePath, { encoding: 'utf8' });
+
+  htmlContent = Object.keys(replaceInHtml).reduce((str, key) => {
+    const regex = new RegExp(`\\$\\{${key}\\}`, 'g');
+    return str.replace(regex, replaceInHtml[key]);
+  }, htmlContent);
+
+  checkForUnresolvedPlaceholders(htmlContent);
+
+  return htmlContent;
+}
+
+function checkForUnresolvedPlaceholders(htmlContent: string): void {
+  const placeholderRegex = /\$\{[^}]+\}/;
+  const match = htmlContent.match(placeholderRegex);
+
+  if (match) {
+    throw new Error(`Unresolved placeholder found: ${match[0]}`);
+  }
+}
+
+function getChatViewDetails(context: vscode.ExtensionContext | undefined): object {
+  const panel = vscode.window.createWebviewPanel('SirjiChat', 'Sirji', vscode.ViewColumn.One, {
+    enableScripts: true,
+    retainContextWhenHidden: true
+  });
+
+  const htmlFilePath = path.join(__dirname, '..', 'views', 'chat', 'chat.html');
+  const chatScriptUri = panel.webview.asWebviewUri(vscode.Uri.file(path.join(__dirname, '..', 'views', 'chat', 'chat.js')));
+  const chatStyleUri = panel.webview.asWebviewUri(vscode.Uri.file(path.join(__dirname, '..', 'views', 'chat', 'chat.css')));
+  return {
+    panel: panel,
+    htmlFilePath: htmlFilePath,
+    replaceInHtml: {
+      chatScriptUri: chatScriptUri,
+      chatStyleUri: chatStyleUri
+    }
+  };
+}
diff --git a/sirji/vscode-extension/src/utils/secret_storage.ts b/sirji/vscode-extension/src/utils/secret_storage.ts
new file mode 100644
index 00000000..00d747d0
--- /dev/null
+++ b/sirji/vscode-extension/src/utils/secret_storage.ts
@@ -0,0 +1,30 @@
+import * as vscode from 'vscode';
+
+export class SecretStorage {
+  private context: vscode.ExtensionContext | undefined = undefined;
+
+  public constructor(context: vscode.ExtensionContext | undefined) {
+    this.context = context;
+  }
+
+  public async storeSecret(key: string, value: any): Promise<void> {
+    if (!this.context) {
+      throw new Error('SecretStorage not initialized');
+    }
+
+    await this.context.secrets.store(key, value);
+  }
+
+  public async removeSecret(key: string): Promise<void> {
+    await this.context?.secrets.delete(key);
+  }
+
+  public async retrieveSecret(key: string): Promise<string | undefined> {
+    if (!this.context) {
+      throw new Error('SecretStorage not initialized');
+    }
+
+    const secret = await this.context.secrets.get(key);
+    return secret;
+  }
+}
diff --git a/sirji/vscode-extension/src/views/chat/chat.css b/sirji/vscode-extension/src/views/chat/chat.css
new file mode 100644
index 00000000..9762396f
--- /dev/null
+++ b/sirji/vscode-extension/src/views/chat/chat.css
@@ -0,0 +1,650 @@
+body[data-vscode-theme-kind='vscode-dark'] {
+
+  .icon svg > path {
+    fill: white;
+  }
+}
+
+body[data-vscode-theme-kind='vscode-light'] {
+
+  .icon svg > path {
+    fill: black;
+  }
+}
+
+body[data-vscode-theme-kind='vscode-dark'] {
+
+  .boticon svg>path {
+    fill: white;
+  }
+}
+
+body[data-vscode-theme-kind='vscode-light'] {
+
+  .boticon svg>path {
+    fill: white;
+  }
+}
+
+body,
+html {
+  margin: 0;
+  padding: 0;
+  height: 100%;
+  box-sizing: border-box;
+}
+
+#chatContainer {
+  display: flex;
+  flex-direction: column;
+  height: 100vh;
+  margin-top: 45px;
+}
+
+#messageContainer {
+  display: flex;
+  flex-direction: column;
+  padding: 20px;
+  overflow-y: auto;
+  flex-grow: 1;
+}
+
+.user-message:last-child,
+.bot-message:last-child {
+  margin-bottom: 70px;
+}
+
+/* Adjusted message styles for flexible width  */
+/* .user {
+  min-width: 100px;
+  max-width: 80%;
+  padding: 7px 10px;
+  border-radius: 6px;
+  color: white;
+} */
+
+.user {
+  background-color: #4caf50;
+  align-self: flex-end;
+  margin-right: auto;
+  flex: 1;
+  padding: 7px 10px;
+  border-radius: 6px;
+  color: white;
+}
+
+.user-message {
+  display: flex;
+  justify-content: flex-end;
+  align-items: center;
+  align-self: end;
+  width: fit-content;
+  max-width: 80%;
+  margin-bottom: 10px;
+  gap: 4px;
+}
+
+.user-text-inactive{
+  align-self: flex-end;
+  margin-left: auto;
+  min-width: 100px;
+  max-width: 80%;
+  padding: 7px 10px;
+  border-radius: 6px;
+  color: white;
+  opacity: 0.9;
+}
+
+.bot {
+  background-color: #666;
+  align-self: center;
+  margin-right: auto;
+  flex: 1;
+  padding: 7px 10px;
+  border-radius: 6px;
+  color: white;
+}
+
+.bot-message {
+  display: flex;
+  align-items: flex-start;
+  width: fit-content;
+  max-width: 80%;
+  margin-bottom: 10px;
+  gap: 4px;
+}
+
+.message-loader{
+  display: flex;
+  align-items: center;
+  height: 100%;
+}
+
+/* Input container fixed at the bottom */
+#inputContainer {
+  display: flex;
+  justify-content: space-between;
+  align-items: flex-end;
+  padding: 10px;
+  background: #f1f1f1;
+  position: fixed;
+  bottom: 0;
+  left: 0;
+  width: 100%;
+  box-sizing: border-box;
+}
+
+textarea {
+  flex-grow: 1;
+  margin-right: 10px;
+  padding: 10px 10px 0px 10px;
+  border: none;
+  border-radius: 5px;
+  overflow-y: hidden;
+  resize: none;
+  font-size: 13px;
+}
+
+#sendBtn {
+  padding: 10px 20px;
+  border: none;
+  border-radius: 5px;
+  background-color: #302f2f;
+  color: white;
+  font-weight: bold;
+  cursor: pointer;
+  transition: background-color 0.3s, transform 0.2s;
+}
+
+#sendBtn:disabled {
+  background-color: #ccc;
+  cursor: not-allowed;
+}
+
+/* #sendBtn:hover {
+  background-color: #444;
+  transform: scale(1.05);
+} */
+
+/** Top Controls **/
+.fixedHeader {
+  display: flex;
+  justify-content: space-between;
+  align-items: center;
+  gap: 10px;
+  position: fixed;
+  height: 46px;
+  width: -webkit-fill-available;
+  top: 0px;
+  padding: 0px 10px;
+  background-color: #1e1e1e;
+}
+
+.right {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  gap: 10px;
+}
+
+
+.progressContainer {
+  display: flex;
+  justify-content: flex-end;
+  align-items: center;
+  cursor: pointer;
+}
+
+#openSettings {
+  display: flex;
+  cursor: pointer;
+  font-size: 24px;
+  width: 30px;
+  height: 30px;
+  justify-content: center;
+  align-items: center;
+}
+
+#progressCircle {
+  visibility: hidden;
+  justify-content: center;
+  align-items: center;
+  position: relative;
+  height: 30px;
+  gap: 5px;
+}
+
+#progressCircle:hover {
+  cursor: pointer;
+}
+
+#progressRingCircle {
+  transition: stroke-dashoffset 0.35s;
+  transform: rotate(-90deg);
+  transform-origin: 50% 50%;
+}
+
+#progressText {
+  font-size: 13px;
+  font-weight: 400;
+  line-height: 12.1px;
+  text-align: left;
+  background-color: transparent;
+  border-radius: 2px;
+  padding: 2px 4px;
+  color: white;
+  transition: background-color 0.5s ease-in-out; /* Add transition for background-color */
+}
+
+/** Setting Modal **/
+
+.modal {
+  position: fixed;
+  top: 0;
+  left: 0;
+  width: 100%;
+  height: 100%;
+  background-color: rgba(
+    0,
+    0,
+    0,
+    0.5
+  ); 
+  display: none;
+  justify-content: center;
+  align-items: center;
+}
+
+.modal-body {
+  position: relative;
+  background-color: white;
+  border: 1px solid white;
+  border-radius: 9px;
+  max-height: calc(
+    100% - 120px
+  ); 
+  overflow: hidden; 
+  display: flex;
+  flex-direction: column;
+
+  background-color: #1e1e1e;
+}
+
+.modal-header {
+  padding: 20px 40px 0 40px;
+  position: sticky;
+  top: 0;
+  z-index: 1;
+}
+
+.modal-content {
+  flex-grow: 1; 
+  overflow-y: auto; 
+  padding: 0 40px 20px 40px;
+}
+
+.settings-modal-container {
+  display: flex;
+  flex-direction: column;
+  width: 720px;
+}
+
+.planner-modal-container {
+  width: 720px;
+}
+
+.modal-heading {
+  font-family: Arial;
+  font-size: 12px;
+  font-weight: 700;
+  line-height: 13.8px;
+  color: #ffffff;
+
+  margin-bottom: 5px;
+  margin-top: 15px;
+}
+
+.modal-subheading {
+  font-family: Arial;
+  font-size: 10px;
+  font-weight: 400;
+  line-height: 11.5px;
+  color: #ffffff;
+
+  margin-bottom: 10px;
+}
+
+#closeSettings {
+  color: #aaa;
+  float: right;
+  font-size: 28px;
+  font-weight: bold;
+  cursor: pointer;
+
+  position: absolute;
+  top: 12px;
+  right: 12px;
+}
+
+#closeSettings:hover,
+#closeSettings:focus {
+  color: black;
+  text-decoration: none;
+  cursor: pointer;
+}
+
+.error-message {
+  color: red;
+  font-size: 12px;
+  margin-top: 5px;
+}
+
+/** Planner Modal **/
+#closePlanner {
+  color: #aaa;
+  float: right;
+  font-size: 28px;
+  font-weight: bold;
+  cursor: pointer;
+
+  position: absolute;
+  top: 12px;
+  right: 12px;
+}
+
+#closePlanner:hover,
+#closePlanner:focus {
+  color: black;
+  text-decoration: none;
+  cursor: pointer;
+}
+
+#plannerStepsList {
+  list-style-type: none;
+  padding: 0;
+  margin: 0;
+}
+
+
+.stepItem {
+  display: flex;
+  align-items: center;
+  gap: 10px;
+  padding: 10px 10px 10px 0;
+  border-radius: 5px;
+
+  font-size: 12px;
+  font-weight: 700;
+  line-height: 13.8px;
+  font-family: Arial;
+  color: #9d9d9d;
+}
+
+.stepItem label {
+  margin: 0;
+  flex-grow: 1;
+  color: inherit;
+}
+
+
+.checkbox {
+  /* Hide the default checkbox */
+  appearance: none;
+  -webkit-appearance: none;
+  -moz-appearance: none;
+  min-width: 20px;
+  height: 20px;
+  border: 2px solid #666666; 
+  border-radius: 50%; 
+  cursor: pointer;
+  outline: none;
+  position: relative;
+}
+
+.checkbox:checked {
+  background-color: #4caf50; 
+  border: 2px solid #4caf50;
+}
+
+/* Hide the default checkbox and add a custom tick mark using the ::after pseudo-element */
+.checkbox:checked::after {
+  content: "✔"; 
+  font-size: 12px; 
+  color: white; 
+  position: absolute;
+  top: 50%; 
+  left: 50%; 
+  transform: translate(-50%, -50%); 
+}
+
+/* Style the label for better usability */
+label {
+  margin-left: 10px; 
+  cursor: pointer; 
+}
+
+.loader {
+  border: 3px solid #f3f3f3;
+  border-top: 3px solid #3498db;
+  border-radius: 50%;
+  min-width: 20px;
+  height: 20px;
+  animation: spin 1s linear infinite;
+}
+
+@keyframes spin {
+  0% {
+    transform: rotate(0deg);
+  }
+
+  100% {
+    transform: rotate(360deg);
+  }
+}
+
+.tokensContainer {
+  display: none;  
+  justify-content: center;
+  align-items: center;
+  position: relative;
+  gap: 5px;
+  cursor: pointer;
+}
+
+.tooltip {
+  visibility: hidden;
+  background-color: rgba(217, 217, 217, 1);
+  color: black;
+  text-align: center;
+  padding: 7px;
+  border-radius: 6px;
+  position: absolute;
+  z-index: 1;
+  left: 38%;
+  transform: translateX(-50%);
+  opacity: 0;
+  transition: opacity 0.3s;
+  width: max-content;
+  top: 25px;
+  bottom: 0;
+  height: fit-content;
+}
+
+.tooltip-text {
+  font-family: Arial;
+  font-size: 13px;
+  font-weight: 400;
+  line-height: 12.1px;
+  text-align: left;
+  color: rgba(32, 32, 33, 1);
+  background-color: rgba(217, 217, 217, 1);
+}
+
+.tooltip-arrow {
+  width: 0;
+  height: 0;
+  border-style: solid;
+  border-width: 0 5px 5px 5px; /* Changed border-width values */
+  border-color: transparent transparent rgba(217, 217, 217, 1) transparent;
+  position: absolute;
+  top: -5px; /* Adjust this value as needed */
+  left: 50%;
+  transform: translateX(-50%);
+}
+
+.tokensContainer:hover .tooltip {
+  visibility: visible;
+  opacity: 1;
+}
+
+.tokens {
+  color: white;
+  font-size: 13px;
+  font-weight: 400;
+  line-height: 12.1px;
+}
+
+.settings-modal-heading {
+  font-family: Arial;
+  font-size: 16px;
+  font-weight: 700;
+  line-height: 18.4px;
+  text-align: left;
+  margin-top: 20px;
+  color: white;
+}
+
+.save-settings-button {
+  padding: 10px 20px;
+  margin-bottom: 20px;
+  color: white;
+  border: 1px solid #2382d7;
+  background-color: #0079d5;
+  border-radius: 5px;
+  font-family: Arial;
+  font-size: 12px;
+  font-weight: 700;
+  line-height: 13.8px;
+  text-align: left;
+  cursor: pointer;
+  transition: background-color 0.3s, transform 0.2s;
+}
+
+.input-wrapper {
+  margin-bottom: 25px;
+  width: 400px;
+  margin-top: 20px;
+}
+
+.input-wrapper label {
+  display: block;
+  margin-bottom: 10px;
+  font-size: 14px;
+  color: white;
+
+  margin-left: 0px;
+  font-family: Arial;
+  font-size: 14px;
+  font-weight: 400;
+  line-height: 16.1px;
+  text-align: left;
+}
+
+.input-wrapper input[type="text"] {
+  width: calc(100% - 10px);
+  padding: 10px;
+  border: 1px solid #454545;
+  border-radius: 5px;
+  font-size: 13px;
+  box-sizing: border-box;
+  outline: none;
+  background-color: #242424;
+  color: white;
+}
+
+.input-wrapper input[type="text"]:hover {
+  border-color: #888;
+}
+
+.left {
+  display: flex;
+  justify-content: center;
+  align-items: center;
+  gap: 20px;
+}
+
+.tab {
+  display: none;
+}
+
+.tab.active {
+  display: block;
+}
+
+.tab-button {
+  color: #9d9d9d;
+  cursor: pointer;
+}
+
+.tab-button:hover {
+  color: #ffffff;
+}
+
+.tab-button.active {
+  color: #ffffff;
+  border-bottom: 1px solid #2f79ce;
+}
+
+.tooltip-body{
+  padding-top: 3px;
+  padding-bottom: 3px;
+}
+
+.hr{
+  margin: 7px 0;
+  border-bottom: 1px solid grey;
+}
+
+.p-20{
+  padding: 20px;
+}
+
+.pt-45{
+  padding-top: 45px;
+}
+
+.text-inactive{
+  color: #9d9d9d;
+}
+
+.toolbar-icon:hover {
+  svg > path {
+    fill: #a1a1a1;
+  }
+}
+
+#dots #dot1{
+  animation: load 1s infinite;
+}
+
+#dots #dot2{
+  animation: load 1s infinite;
+  animation-delay: 0.2s;
+}
+
+#dots #dot3{
+  animation: load 1s infinite;
+  animation-delay: 0.4s;
+}
+
+@keyframes load{
+  0%{
+    opacity: 0;
+  }
+  50%{
+    opacity: 1;
+  }
+  100%{
+    opacity: 0;
+  }
+}
\ No newline at end of file
diff --git a/sirji/vscode-extension/src/views/chat/chat.html b/sirji/vscode-extension/src/views/chat/chat.html
new file mode 100644
index 00000000..6503da2b
--- /dev/null
+++ b/sirji/vscode-extension/src/views/chat/chat.html
@@ -0,0 +1,132 @@
+<!DOCTYPE html>
+<html lang="en">
+
+<head>
+  <title>Sirji Chat</title>
+  <meta charset="UTF-8" />
+  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
+  <!-- Use a meta tag to enforce Content Security Policy -->
+  <meta http-equiv="Content-Security-Policy"
+    content="default-src 'none'; style-src-elem  'nonce-${nonce}'; script-src 'nonce-${nonce}';" />
+  <link nonce="${nonce}" rel="stylesheet" type="text/css" href="${chatStyleUri}" />
+</head>
+
+<body>
+  <main class="tab-container">
+
+    <div class="fixedHeader">
+      <div class="left">
+        <div class="tab-button active" data-tab="chatTerminalTab">Chat</div>
+        <div class="tab-button" data-tab="coderTab">Coder</div>
+        <div class="tab-button" data-tab="plannerTab">Planner</div>
+        <div class="tab-button" data-tab="researcherTab">Researcher</div>
+      </div>
+      <div class="right">
+        <div class="progressContainer toolbar-icon" id="progressCircle">
+          <svg width="22" id="progressRing" height="22" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg">
+            <path d="M13 0C12.7348 0 12.4804 0.105357 12.2929 0.292893C12.1053 0.48043 12 0.734784 12 1V8C12 8.26522 12.1053 8.51957 12.2929 8.70711C12.4804 8.89464 12.7348 9 13 9C13.8805 9.0002 14.7363 9.29091 15.4347 9.82707C16.1332 10.3632 16.6352 11.1148 16.863 11.9654C17.0907 12.8159 17.0315 13.7178 16.6944 14.5313C16.3573 15.3447 15.7613 16.0242 14.9987 16.4643C14.2361 16.9045 13.3496 17.0807 12.4766 16.9657C11.6037 16.8507 10.7931 16.4509 10.1705 15.8283C9.54792 15.2056 9.14819 14.395 9.03329 13.522C8.91838 12.649 9.09473 11.7625 9.53498 11C9.60067 10.8862 9.6433 10.7607 9.66044 10.6304C9.67758 10.5002 9.6689 10.3679 9.63489 10.241C9.60088 10.1141 9.54221 9.99519 9.46222 9.891C9.38223 9.78681 9.28251 9.69939 9.16873 9.63375L3.10623 6.13375C2.99248 6.06806 2.8669 6.02543 2.73667 6.00829C2.60644 5.99114 2.47411 5.99983 2.34723 6.03384C2.22036 6.06785 2.10142 6.12652 1.99723 6.20651C1.89304 6.28649 1.80562 6.38622 1.73998 6.5C0.309142 8.97828 -0.263979 11.8594 0.109499 14.6966C0.482977 17.5338 1.78218 20.1685 3.80563 22.1921C5.82907 24.2157 8.46367 25.515 11.3008 25.8887C14.138 26.2624 17.0192 25.6894 19.4976 24.2587C21.976 22.8281 23.913 20.6196 25.0083 17.9758C26.1037 15.3321 26.2961 12.4008 25.5557 9.63656C24.8153 6.87233 23.1835 4.42963 20.9134 2.6873C18.6432 0.944964 15.8617 0.000367546 13 0ZM3.01123 8.3875L7.37498 10.9075C7.12666 11.5772 6.99967 12.2858 6.99998 13C6.99998 13.1912 7.00998 13.375 7.02748 13.565L2.15998 14.875C1.78039 12.6762 2.07723 10.4139 3.01123 8.3875ZM2.67748 16.8013L7.55248 15.4963C7.96006 16.3847 8.57811 17.1604 9.35309 17.7561C10.1281 18.3518 11.0366 18.7496 12 18.915V23.9538C9.92976 23.7619 7.956 22.9885 6.30647 21.7229C4.65694 20.4574 3.39888 18.7512 2.67748 16.8013ZM14 23.9538V18.915C15.3975 18.6781 16.666 17.9541 17.5807 16.8713C18.4955 15.7885 18.9973 14.4168 18.9973 12.9994C18.9973 11.5819 18.4955 10.2102 17.5807 9.12743C16.666 8.04463 15.3975 7.32063 14 7.08375V2.045C16.7327 2.29467 19.2735 3.55675 21.1235 5.58349C22.9735 7.61022 23.9991 10.2553 23.9991 12.9994C23.9991 15.7435 22.9735 18.3885 21.1235 20.4153C19.2735 22.442 16.7327 23.7041 14 23.9538Z" fill="white" />
+          </svg>
+          <div id="progressText">0 of 0</div>
+        </div>
+        <div class="tokensContainer toolbar-icon" id="jTokensContainer">
+          <svg width="22" height="6" viewBox="0 0 22 6" fill="none" xmlns="http://www.w3.org/2000/svg">
+            <path fill-rule="evenodd" clip-rule="evenodd"
+              d="M1 0C0.447715 0 0 0.447715 0 1V5C0 5.55228 0.447715 6 1 6H6H11H21C21.5523 6 22 5.55228 22 5V1C22 0.447715 21.5523 0 21 0H11H6H1ZM5 1H2H1V2V4V5H2H5V1ZM6 5H7H10H11V4V2V1H10H7H6V2V4V5ZM12 1V5H20H21V4V2V1H20H12Z"
+              fill="white" />
+          </svg>
+          <div class="tokens" id="jTokensUsed">0 tokens</div>
+          <div class="tooltip">
+            <div class="tooltip-body">
+              <div class="tooltip-text" id="jPromptTokensUsed">Prompt Tokens - 0 | $0</div>
+              <div class="hr"></div>
+              <div class="tooltip-text" id="jCompletionTokensUsed">Completion Tokens - 0 | $0</div>
+            </div>
+            <div class="tooltip-arrow"></div>
+          </div>
+        </div>
+        <div id="openSettings" class="toolbar-icon">
+          <svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 256 256">
+            <path
+              d="M128 80a48 48 0 1 0 48 48 48.05 48.05 0 0 0-48-48m0 80a32 32 0 1 1 32-32 32 32 0 0 1-32 32m88-29.84q.06-2.16 0-4.32l14.92-18.64a8 8 0 0 0 1.48-7.06 107.2 107.2 0 0 0-10.88-26.25 8 8 0 0 0-6-3.93l-23.72-2.64q-1.48-1.56-3-3L186 40.54a8 8 0 0 0-3.94-6 107.7 107.7 0 0 0-26.25-10.87 8 8 0 0 0-7.06 1.49L130.16 40h-4.32L107.2 25.11a8 8 0 0 0-7.06-1.48 107.6 107.6 0 0 0-26.25 10.88 8 8 0 0 0-3.93 6l-2.64 23.76q-1.56 1.49-3 3L40.54 70a8 8 0 0 0-6 3.94 107.7 107.7 0 0 0-10.87 26.25 8 8 0 0 0 1.49 7.06L40 125.84v4.32L25.11 148.8a8 8 0 0 0-1.48 7.06 107.2 107.2 0 0 0 10.88 26.25 8 8 0 0 0 6 3.93l23.72 2.64q1.49 1.56 3 3L70 215.46a8 8 0 0 0 3.94 6 107.7 107.7 0 0 0 26.25 10.87 8 8 0 0 0 7.06-1.49L125.84 216q2.16.06 4.32 0l18.64 14.92a8 8 0 0 0 7.06 1.48 107.2 107.2 0 0 0 26.25-10.88 8 8 0 0 0 3.93-6l2.64-23.72q1.56-1.48 3-3l23.78-2.8a8 8 0 0 0 6-3.94 107.7 107.7 0 0 0 10.87-26.25 8 8 0 0 0-1.49-7.06Zm-16.1-6.5a74 74 0 0 1 0 8.68 8 8 0 0 0 1.74 5.48l14.19 17.73a91.6 91.6 0 0 1-6.23 15l-22.6 2.56a8 8 0 0 0-5.1 2.64 74 74 0 0 1-6.14 6.14 8 8 0 0 0-2.64 5.1l-2.51 22.58a91.3 91.3 0 0 1-15 6.23l-17.74-14.19a8 8 0 0 0-5-1.75h-.48a74 74 0 0 1-8.68 0 8 8 0 0 0-5.48 1.74l-17.78 14.2a91.6 91.6 0 0 1-15-6.23L82.89 187a8 8 0 0 0-2.64-5.1 74 74 0 0 1-6.14-6.14 8 8 0 0 0-5.1-2.64l-22.58-2.52a91.3 91.3 0 0 1-6.23-15l14.19-17.74a8 8 0 0 0 1.74-5.48 74 74 0 0 1 0-8.68 8 8 0 0 0-1.74-5.48L40.2 100.45a91.6 91.6 0 0 1 6.23-15L69 82.89a8 8 0 0 0 5.1-2.64 74 74 0 0 1 6.14-6.14A8 8 0 0 0 82.89 69l2.51-22.57a91.3 91.3 0 0 1 15-6.23l17.74 14.19a8 8 0 0 0 5.48 1.74 74 74 0 0 1 8.68 0 8 8 0 0 0 5.48-1.74l17.77-14.19a91.6 91.6 0 0 1 15 6.23L173.11 69a8 8 0 0 0 2.64 5.1 74 74 0 0 1 6.14 6.14 8 8 0 0 0 5.1 2.64l22.58 2.51a91.3 91.3 0 0 1 6.23 15l-14.19 17.74a8 8 0 0 0-1.74 5.53Z" fill="white"/>
+          </svg>
+        </div>
+      </div>
+    </div>
+
+    <div id="chatTerminalTab" class="tab active">
+      <div id="chatContainer">
+        <div id="messageContainer"></div>
+        <div id="inputContainer">
+          <textarea id="userInput" placeholder="Type a message..."></textarea>
+          <button id="sendBtn">Send</button>
+        </div>
+      </div>
+    </div>
+  
+    <div id="plannerTab" class="tab p-20">
+      <div id="plannerLogs" class="pt-45">
+        <div class="text-inactive">This is where the logs from the planner will be displayed.</div>
+      </div>
+    </div>
+  
+    <div id="coderTab" class="tab p-20">
+      <div id="coderLogs" class="pt-45">
+        <div class="text-inactive">This is where the logs from the coder will be displayed.</div>
+      </div>
+    </div>
+  
+    <div id="researcherTab" class="tab p-20">
+      <div id="researcherLogs" class="pt-45">
+        <div class="text-inactive">This is where the logs from the researcher will be displayed.</div>
+      </div>
+    </div>
+
+    <div id="plannerModal" class="modal">
+      <div class="modal-body">
+        <div class="modal-header">
+          <div class="planner-modal-header">
+            <span id="closePlanner">
+              <svg width="26" height="26" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg">
+                <path d="M16.8249 10.3248L14.1487 13L16.8249 15.6752C16.9003 15.7506 16.9602 15.8403 17.0011 15.9389C17.0419 16.0375 17.063 16.1432 17.063 16.25C17.063 16.3568 17.0419 16.4625 17.0011 16.5611C16.9602 16.6597 16.9003 16.7494 16.8249 16.8248C16.7494 16.9003 16.6597 16.9602 16.5611 17.0011C16.4625 17.0419 16.3568 17.063 16.25 17.063C16.1432 17.063 16.0375 17.0419 15.9389 17.0011C15.8403 16.9602 15.7507 16.9003 15.6752 16.8248L13 14.1487L10.3248 16.8248C10.2494 16.9003 10.1597 16.9602 10.0611 17.0011C9.96248 17.0419 9.85676 17.063 9.75001 17.063C9.64325 17.063 9.53753 17.0419 9.4389 17.0011C9.34027 16.9602 9.25065 16.9003 9.17516 16.8248C9.09967 16.7494 9.03979 16.6597 8.99894 16.5611C8.95808 16.4625 8.93705 16.3568 8.93705 16.25C8.93705 16.1432 8.95808 16.0375 8.99894 15.9389C9.03979 15.8403 9.09967 15.7506 9.17516 15.6752L11.8513 13L9.17516 10.3248C9.0227 10.1724 8.93705 9.96561 8.93705 9.75C8.93705 9.53439 9.0227 9.32761 9.17516 9.17516C9.32762 9.0227 9.5344 8.93705 9.75001 8.93705C9.96561 8.93705 10.1724 9.0227 10.3248 9.17516L13 11.8513L15.6752 9.17516C15.7507 9.09967 15.8403 9.03979 15.9389 8.99893C16.0375 8.95808 16.1432 8.93705 16.25 8.93705C16.3568 8.93705 16.4625 8.95808 16.5611 8.99893C16.6597 9.03979 16.7494 9.09967 16.8249 9.17516C16.9003 9.25065 16.9602 9.34027 17.0011 9.4389C17.0419 9.53753 17.063 9.64324 17.063 9.75C17.063 9.85676 17.0419 9.96247 17.0011 10.0611C16.9602 10.1597 16.9003 10.2494 16.8249 10.3248ZM23.5625 13C23.5625 15.0891 22.943 17.1312 21.7824 18.8682C20.6218 20.6052 18.9721 21.959 17.0421 22.7585C15.1121 23.5579 12.9883 23.7671 10.9394 23.3595C8.89044 22.952 7.00838 21.946 5.53119 20.4688C4.054 18.9916 3.04802 17.1096 2.64046 15.0606C2.2329 13.0117 2.44208 10.888 3.24153 8.95791C4.04098 7.02786 5.3948 5.37822 7.13179 4.2176C8.86879 3.05698 10.9109 2.4375 13 2.4375C15.8004 2.44046 18.4853 3.55424 20.4656 5.53445C22.4458 7.51466 23.5595 10.1996 23.5625 13ZM21.9375 13C21.9375 11.2323 21.4133 9.50436 20.4313 8.03459C19.4492 6.56483 18.0534 5.41929 16.4202 4.74283C14.7871 4.06637 12.9901 3.88938 11.2564 4.23423C9.52268 4.57909 7.93017 5.4303 6.68024 6.68023C5.43031 7.93017 4.57909 9.52268 4.23424 11.2564C3.88938 12.9901 4.06637 14.7871 4.74283 16.4202C5.41929 18.0533 6.56483 19.4492 8.0346 20.4313C9.50436 21.4133 11.2323 21.9375 13 21.9375C15.3696 21.9348 17.6413 20.9923 19.3168 19.3168C20.9923 17.6413 21.9348 15.3695 21.9375 13Z" fill="white" />
+              </svg>
+            </span>
+            <div class="modal-heading">Planner</div>
+            <div class="modal-subheading" id="progressTextInsideModal">2 of 5 tasks done</div>
+          </div>
+        </div>
+        <div class="modal-content">
+          <ul class="planner-modal-container" id="plannerStepsList"></ul>
+        </div>
+      </div>
+    </div>
+
+    <div id="settingsModal" class="modal">
+      <div class="modal-body setting-modal-body">
+        <div class="modal-header">
+          <span id="closeSettings">
+            <svg width="26" height="26" viewBox="0 0 26 26" fill="none" xmlns="http://www.w3.org/2000/svg">
+              <path d="M16.8249 10.3248L14.1487 13L16.8249 15.6752C16.9003 15.7506 16.9602 15.8403 17.0011 15.9389C17.0419 16.0375 17.063 16.1432 17.063 16.25C17.063 16.3568 17.0419 16.4625 17.0011 16.5611C16.9602 16.6597 16.9003 16.7494 16.8249 16.8248C16.7494 16.9003 16.6597 16.9602 16.5611 17.0011C16.4625 17.0419 16.3568 17.063 16.25 17.063C16.1432 17.063 16.0375 17.0419 15.9389 17.0011C15.8403 16.9602 15.7507 16.9003 15.6752 16.8248L13 14.1487L10.3248 16.8248C10.2494 16.9003 10.1597 16.9602 10.0611 17.0011C9.96248 17.0419 9.85676 17.063 9.75001 17.063C9.64325 17.063 9.53753 17.0419 9.4389 17.0011C9.34027 16.9602 9.25065 16.9003 9.17516 16.8248C9.09967 16.7494 9.03979 16.6597 8.99894 16.5611C8.95808 16.4625 8.93705 16.3568 8.93705 16.25C8.93705 16.1432 8.95808 16.0375 8.99894 15.9389C9.03979 15.8403 9.09967 15.7506 9.17516 15.6752L11.8513 13L9.17516 10.3248C9.0227 10.1724 8.93705 9.96561 8.93705 9.75C8.93705 9.53439 9.0227 9.32761 9.17516 9.17516C9.32762 9.0227 9.5344 8.93705 9.75001 8.93705C9.96561 8.93705 10.1724 9.0227 10.3248 9.17516L13 11.8513L15.6752 9.17516C15.7507 9.09967 15.8403 9.03979 15.9389 8.99893C16.0375 8.95808 16.1432 8.93705 16.25 8.93705C16.3568 8.93705 16.4625 8.95808 16.5611 8.99893C16.6597 9.03979 16.7494 9.09967 16.8249 9.17516C16.9003 9.25065 16.9602 9.34027 17.0011 9.4389C17.0419 9.53753 17.063 9.64324 17.063 9.75C17.063 9.85676 17.0419 9.96247 17.0011 10.0611C16.9602 10.1597 16.9003 10.2494 16.8249 10.3248ZM23.5625 13C23.5625 15.0891 22.943 17.1312 21.7824 18.8682C20.6218 20.6052 18.9721 21.959 17.0421 22.7585C15.1121 23.5579 12.9883 23.7671 10.9394 23.3595C8.89044 22.952 7.00838 21.946 5.53119 20.4688C4.054 18.9916 3.04802 17.1096 2.64046 15.0606C2.2329 13.0117 2.44208 10.888 3.24153 8.95791C4.04098 7.02786 5.3948 5.37822 7.13179 4.2176C8.86879 3.05698 10.9109 2.4375 13 2.4375C15.8004 2.44046 18.4853 3.55424 20.4656 5.53445C22.4458 7.51466 23.5595 10.1996 23.5625 13ZM21.9375 13C21.9375 11.2323 21.4133 9.50436 20.4313 8.03459C19.4492 6.56483 18.0534 5.41929 16.4202 4.74283C14.7871 4.06637 12.9901 3.88938 11.2564 4.23423C9.52268 4.57909 7.93017 5.4303 6.68024 6.68023C5.43031 7.93017 4.57909 9.52268 4.23424 11.2564C3.88938 12.9901 4.06637 14.7871 4.74283 16.4202C5.41929 18.0533 6.56483 19.4492 8.0346 20.4313C9.50436 21.4133 11.2323 21.9375 13 21.9375C15.3696 21.9348 17.6413 20.9923 19.3168 19.3168C20.9923 17.6413 21.9348 15.3695 21.9375 13Z" fill="white" />
+            </svg>
+          </span>
+          <div class="settings-modal-heading">Environment Variables Configuration</div>
+        </div>
+        <div class="modal-content">
+          <div class="settings-modal-container">
+            <div class="input-wrapper">
+              <label for="SIRJI_OPENAI_API_KEY">OpenAI API Key</label>
+              <input type="text" id="SIRJI_OPENAI_API_KEY" placeholder="Enter here" />
+              <span class="error-message" id="SIRJI_OPENAI_API_KEY_ERROR"></span>
+            </div>
+            <div>
+              <span class="error-message" id="save_settings_error"></span>
+            </div>
+          </div>
+          <button class="save-settings-button" id="saveSettings">Save</button>
+        </div>
+      </div>
+    </div>
+  </main>
+  <script type="module" nonce="${nonce}" src="${chatScriptUri}"></script>
+</body>
+
+</html>
\ No newline at end of file
diff --git a/sirji/vscode-extension/src/views/chat/chat.js b/sirji/vscode-extension/src/views/chat/chat.js
new file mode 100644
index 00000000..315ffd5c
--- /dev/null
+++ b/sirji/vscode-extension/src/views/chat/chat.js
@@ -0,0 +1,592 @@
+const BotSvg = `
+<svg width="24" height="24" viewBox="0 0 300 300" fill="none" xmlns="http://www.w3.org/2000/svg">
+<rect width="300" height="300" fill="#3D3933"/>
+<path fill-rule="evenodd" clip-rule="evenodd" d="M145.906 24.0177C137.403 26.5548 132.305 32.9899 132.305 41.188C132.305 47.7511 136.323 54.7205 142.136 58.2396C143.382 58.9946 144.559 59.2345 144.75 59.509C145.318 60.3215 145.462 83.6735 144.909 85.1216C144.274 86.7856 137.453 94.7265 130.019 102.456L128.663 103.866L124.98 100.013C122.954 97.8936 120.47 95.2434 119.459 94.1242C118.448 93.0044 116.823 91.8753 115.848 91.6155C114.653 91.2971 113.93 90.6045 113.632 89.494C113.077 87.4275 111.007 84.771 108.841 83.3464C105.292 81.014 99.5514 82.6161 96.2157 86.8697C95.0588 88.3451 94.7728 89.4699 94.7728 92.5486C94.7728 95.9644 94.9763 96.5994 96.6178 98.3073C97.6327 99.3628 99.2804 100.634 100.28 101.13C102.453 102.211 106.954 102.321 107.96 101.318C108.505 100.776 110.18 102.155 114.872 107.009L121.066 113.415L117.175 117.772C115.035 120.17 112.756 122.756 112.11 123.522C111.465 124.287 110.745 124.913 110.511 124.913C110.278 124.913 107.478 122.312 104.29 119.133C98.1947 113.054 93.122 109.145 90.4662 108.48C89.5934 108.262 88.8793 107.835 88.8793 107.532C88.8793 107.229 88.344 106.981 87.6901 106.981C86.8458 106.981 86.1801 106.144 85.396 104.098C83.4834 99.1056 80.8202 97.0873 76.147 97.0873C68.6468 97.0873 64.0573 105.549 68.0971 111.927C70.6276 115.923 75.3597 117.736 79.3313 116.231C80.8524 115.654 81.2476 115.809 83.1019 117.712C84.2415 118.881 86.3563 120.395 87.8018 121.077C90.3595 122.282 100.805 132.188 101.148 133.734C101.452 135.101 85.9599 150.158 76.4354 157.753C74.2375 159.506 72.3001 161.138 72.1295 161.379C71.7051 161.982 65.4053 166.342 64.9605 166.342C63.9568 166.342 60.3426 169.726 60.3426 170.666C60.3426 172.072 62.7024 177.469 63.927 178.864C65.4624 180.613 67.0499 180.271 71.8193 177.168C72.8429 176.501 73.9037 175.602 74.1767 175.169C74.4496 174.735 74.959 174.381 75.3088 174.381C76.1203 174.381 82.6757 169.298 82.6757 168.669C82.6757 168.41 83.0708 168.197 83.5547 168.197C84.038 168.197 84.5262 167.958 84.6404 167.665C84.7539 167.373 86.2428 165.946 87.9488 164.495C100.598 153.735 116.577 137.815 127.919 124.672C129.354 123.009 130.858 121.306 131.261 120.888C131.665 120.47 133.809 118.091 136.027 115.603C143.759 106.928 144.873 105.744 145.303 105.744C145.542 105.744 147.241 107.623 149.081 109.918C152.085 113.667 157.46 120.118 161.06 124.295C161.793 125.145 164.905 128.771 167.976 132.354C171.047 135.936 174.664 140.11 176.014 141.629C179.056 145.053 203.854 169.718 206.663 172.115C214.428 178.741 216.707 180.564 217.225 180.564C217.547 180.564 217.905 180.815 218.019 181.121C218.261 181.774 226.942 188.603 227.53 188.603C227.75 188.603 228.363 189.035 228.893 189.563C230.771 191.435 232.355 190.404 234.578 185.863C237.176 180.558 237.15 180.504 229.151 174.357C225.947 171.895 222.849 169.432 222.266 168.884C221.683 168.337 219.715 166.636 217.895 165.106C208.143 156.908 191.992 140.834 181.304 128.689C179.091 126.175 176.853 123.671 176.328 123.124C175.804 122.577 173.292 119.626 170.746 116.565C168.2 113.504 165.95 110.861 165.746 110.691C165.237 110.267 154.377 96.3577 153.766 95.3467C153.424 94.7815 157.75 90.1086 167.921 80.0524L182.568 65.5719L186.228 65.9535C189.255 66.2688 190.378 66.0969 192.735 64.9604C194.3 64.2048 195.582 63.2278 195.582 62.7894C195.582 62.3516 196.001 61.6467 196.513 61.2231C197.865 60.1039 197.743 54.3242 196.317 51.9868C192.027 44.9562 181.999 45.4836 178.456 52.9261C177.903 54.0886 177.033 55.0396 176.521 55.0396C176.011 55.0396 171.625 58.9371 166.774 63.7008C161.924 68.464 157.767 72.174 157.537 71.9453C157.307 71.7165 157.119 68.8245 157.119 65.5188V59.509L159.446 58.4251C162.238 57.1234 165.823 53.6353 167.759 50.3358C168.959 48.2916 169.172 47.0437 169.177 42.0543C169.182 37.2343 168.944 35.7304 167.854 33.6738C165.412 29.0702 161.827 26.2406 155.592 23.9942C151.897 22.6629 150.436 22.6666 145.906 24.0177ZM146.764 36.5763C142.72 39.0708 142.723 44.7218 146.769 47.4648C153.074 51.7395 161.06 45.2555 157.302 38.9131C155.223 35.4052 150.398 34.3354 146.764 36.5763ZM113.221 55.6468C110.972 56.6009 109.608 58.234 108.418 61.3981C107.477 63.9024 107.42 64.7322 108.045 66.8148C108.941 69.7928 112.575 73.5901 114.531 73.5901C115.824 73.5901 115.95 73.8757 115.982 76.8914C116.004 79.0223 116.43 80.8315 117.182 81.9928C118.376 83.838 122.39 87.804 123.068 87.809C123.913 87.8158 131.684 80.003 131.681 79.149C131.68 78.6426 130.909 77.3929 129.968 76.3726C126.472 72.5822 125.148 69.3037 126.722 68.3341C127.69 67.738 127.45 62.1067 126.361 59.8528C124.828 56.6826 122.638 55.4137 118.362 55.2208C116.306 55.128 113.993 55.3197 113.221 55.6468ZM76.1743 71.2941C70.8559 74.2962 69.4135 80.9379 73.0482 85.6874C76.137 89.724 80.4635 90.6472 85.1807 88.2758C88.1603 86.7782 90.7404 82.8325 90.7404 79.7736C90.7404 76.9502 88.1504 72.78 85.4673 71.2842C82.3115 69.5244 79.304 69.5275 76.1743 71.2941ZM214.503 71.419C213.48 71.8698 211.965 72.9606 211.138 73.8417C209.672 75.4043 209.434 75.4451 201.755 75.4451C197.421 75.4451 192.991 75.6213 191.911 75.8371C189.006 76.4159 180.308 85.3344 177.752 90.3554C175.301 95.1692 174.312 99.3109 175.212 100.987C175.927 102.319 177.618 104.315 182.312 109.368L185.482 112.78L185.933 105.256C186.438 96.8189 186.892 95.769 192.658 89.6801L195.598 86.5754H203.313C210.86 86.5754 211.089 86.6162 213.787 88.4304C215.4 89.5156 217.354 90.2855 218.495 90.2855C221.336 90.2855 225.085 88.1491 226.889 85.5025C232.223 77.6792 223.307 67.5408 214.503 71.419ZM200.618 96.4807C198.349 97.4435 195.616 100.892 195.48 102.962C195.133 108.274 196.387 111.868 199.001 113.056C200.132 113.569 200.421 114.365 200.756 117.9C200.976 120.227 201.158 124.54 201.161 127.483L201.165 132.837L204.418 135.522C206.206 136.999 208.37 138.921 209.226 139.794C211.484 142.096 218.913 147.792 219.657 147.792C220.007 147.792 220.386 148.022 220.5 148.303C220.7 148.797 231.643 156.465 234.708 158.259C239.387 160.997 241.413 161.446 242.586 160.004C243.708 158.625 245.291 153.141 244.954 151.801C244.685 150.73 237.164 145.152 231.63 141.918C230.388 141.192 225.19 137.184 224.739 136.604C224.569 136.385 222.335 134.534 219.776 132.492C211.794 126.122 212.332 127.075 212.332 119.302C212.332 114.527 212.591 112.095 213.203 111.133C214.438 109.191 214.946 105.165 214.253 102.813C213.929 101.712 212.718 99.8118 211.563 98.5918C209.702 96.6248 209.044 96.3434 205.78 96.1128C203.753 95.97 201.431 96.1357 200.618 96.4807ZM228.151 105.737C225.549 107.444 224.118 110.407 224.123 114.082C224.13 119.669 227.637 123.05 233.424 123.05C239.289 123.05 242.733 119.556 242.725 113.613C242.721 109.99 241.456 107.552 238.61 105.681C236.153 104.067 230.652 104.096 228.151 105.737ZM143.419 126.273C142.878 126.715 141.342 128.469 140.007 130.169C138.671 131.869 137.438 133.4 137.267 133.57C137.096 133.74 135.142 135.965 132.925 138.515C125.992 146.488 124.223 148.355 113.034 159.504C102.691 169.811 98.5818 173.58 93.1307 177.76C91.8161 178.767 90.7404 179.811 90.7404 180.078C90.7404 180.345 90.3452 180.564 89.8614 180.564C89.3781 180.564 88.8899 180.817 88.7757 181.127C88.4879 181.911 79.3629 188.362 75.3467 190.621C73.5191 191.65 71.8025 193.077 71.5327 193.793C70.9781 195.265 72.9577 199.687 75.2574 202.113C76.9807 203.931 79.1508 203.615 83.0771 200.974C84.5963 199.951 85.9909 199.115 86.1758 199.115C86.3613 199.115 87.1522 198.628 87.9345 198.033C88.7162 197.438 89.9736 196.533 90.7286 196.023C96.9013 191.851 111.199 179.71 119.187 171.857C124.394 166.739 137.283 152.944 139.29 150.342C140.371 148.94 141.475 147.792 141.743 147.792C142.011 147.792 142.231 147.404 142.231 146.931C142.231 146.22 143.46 145.319 144.429 145.319C144.819 145.319 147.126 147.911 149.758 151.305C152.169 154.415 152.209 154.543 151.114 155.686C150.493 156.333 148.869 158.21 147.504 159.858C142.83 165.499 139.468 169.132 132.323 176.26C121.169 187.387 116.847 191.076 103.392 200.949C98.3101 204.679 93.8112 208.022 93.3943 208.378C92.9775 208.733 91.4092 209.675 89.9098 210.47C83.8308 213.691 82.9641 214.858 84.283 218.043C85.7986 221.702 88.1299 224.467 89.6994 224.467C91.0922 224.467 100.801 219.16 101.183 218.19C101.297 217.902 101.649 217.665 101.965 217.665C103.163 217.665 121.419 203.961 128.125 198.027C133.091 193.634 150.292 176.347 153.416 172.611C154.941 170.786 156.468 168.977 156.809 168.59C166.458 157.646 169.101 154.172 168.509 153.217C167.888 152.216 165.397 149.377 162.989 146.927C161.952 145.873 159.44 143.075 157.406 140.711C155.372 138.347 153.289 135.958 152.777 135.403C152.265 134.847 150.73 133.044 149.365 131.395C144.167 125.118 144.491 125.397 143.419 126.273ZM63.6894 129.189C62.6298 129.452 61.033 130.197 60.1416 130.845C58.3673 132.135 56 136.527 56 138.53C56 140.718 57.8251 144.325 59.8252 146.091C61.5095 147.578 62.2626 147.792 65.8167 147.792C70.9192 147.792 73.4161 146.284 75.2195 142.112C75.9081 140.519 76.472 138.901 76.472 138.517C76.472 138.133 75.9081 136.514 75.2195 134.921C73.2368 130.335 68.5221 127.992 63.6894 129.189ZM174.73 166.806C173.415 168.422 172.122 169.837 171.857 169.952C171.591 170.067 171.078 170.711 170.717 171.384C170.137 172.463 171.151 173.68 179.314 181.687C190.173 192.343 193.444 195.383 198.063 199.12C199.94 200.638 201.615 202.056 201.786 202.272C202.255 202.863 212.947 210.425 215.507 211.976C216.741 212.725 218.158 213.337 218.656 213.337C219.842 213.337 221.738 211.132 223.059 208.214C224.936 204.07 224.914 204.028 218.185 199.099C216.16 197.616 214.224 196.094 213.883 195.718C213.542 195.34 211.867 193.951 210.161 192.629C208.455 191.307 206.605 189.791 206.05 189.26C205.495 188.728 203.546 187.042 201.717 185.511C197.819 182.25 189.037 173.968 183.194 168.043C180.93 165.748 178.637 163.869 178.099 163.869C177.561 163.869 176.045 165.19 174.73 166.806ZM199.728 218.129C199.58 218.384 198.988 220.541 198.413 222.921C196.019 232.836 192.687 239.365 187.453 244.398C182.774 248.897 180.065 250.059 174.179 250.092C168.846 250.122 166.76 249.385 163.223 246.219C159.214 242.632 156.905 241.87 150.51 242.021C147.272 242.098 144.434 242.423 144.202 242.744C143.774 243.337 141.97 244.653 136.765 248.165C134.093 249.969 133.449 250.129 128.853 250.129C124.771 250.129 123.31 249.852 120.907 248.621C116.18 246.201 111.415 241.065 108.669 235.432C107.885 233.822 107.069 232.506 106.856 232.506C106.447 232.506 99.1464 235.863 97.5644 236.778C96.0352 237.663 96.7394 240.71 99.5738 245.471C101.028 247.912 102.496 250.39 102.838 250.977C106.911 257.984 118.224 267.963 126.256 271.635C127.536 272.22 129.49 273.109 130.599 273.612C132.498 274.472 134.872 275.133 140.68 276.422C142.045 276.725 146.947 276.985 151.575 277C159.639 277.026 166.652 275.935 171.078 273.968C177.219 271.238 181.399 269.085 183.459 267.592C188.274 264.101 198.055 254.37 198.06 253.066C198.062 252.641 198.298 252.293 198.584 252.293C198.871 252.293 199.432 251.667 199.832 250.902C200.231 250.136 201.097 248.675 201.757 247.655C202.415 246.635 203.68 244.27 204.566 242.399C205.451 240.529 206.445 238.515 206.773 237.924C207.532 236.556 211.024 226.828 211.084 225.911C211.088 225.855 209.651 224.755 207.889 223.467C206.129 222.179 203.834 220.346 202.789 219.395C200.819 217.602 200.187 217.34 199.728 218.129Z" fill="white"/>
+</svg>
+`;
+
+const UserSvg = `
+<svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 256 256"><path d="M230.92 212c-15.23-26.33-38.7-45.21-66.09-54.16a72 72 0 1 0-73.66 0c-27.39 8.94-50.86 27.82-66.09 54.16a8 8 0 1 0 13.85 8c18.84-32.56 52.14-52 89.07-52s70.23 19.44 89.07 52a8 8 0 1 0 13.85-8M72 96a56 56 0 1 1 56 56 56.06 56.06 0 0 1-56-56" fill="#fff"/></svg>
+`;
+
+const loaderSvg = `
+  <svg id="dots" width="25px" height="22px" viewBox="0 0 132 58" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" xmlns:sketch="http://www.bohemiancoding.com/sketch/ns">
+    <defs></defs>
+    <g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" sketch:type="MSPage">
+        <g id="dots" sketch:type="MSArtboardGroup" fill="#A3A3A3">
+            <circle id="dot1" sketch:type="MSShapeGroup" cx="25" cy="30" r="13"></circle>
+            <circle id="dot2" sketch:type="MSShapeGroup" cx="65" cy="30" r="13"></circle>
+            <circle id="dot3" sketch:type="MSShapeGroup" cx="105" cy="30" r="13"></circle>
+        </g>
+    </g>
+  </svg>
+`;
+
+let stepsArray = [];
+let totalStepsCompleted = 0;
+let coderTabInterval;
+let plannerTabInterval;
+let researcherTabInterval;
+
+const userInput = document.getElementById('userInput');
+userInput.addEventListener('keydown', onInputChange);
+userInput.addEventListener('paste', () => setTimeout(adjustTextAreaHeight, 0));
+document.getElementById('sendBtn').addEventListener('click', sendUserMessage);
+
+// Settings modal
+document.getElementById('saveSettings').onclick = function () {
+  saveSettings();
+};
+document.getElementById('openSettings').onclick = function () {
+  openSettings();
+};
+document.getElementById('closeSettings').onclick = function () {
+  closeSettings();
+};
+
+// Planner modal
+document.getElementById('progressCircle').addEventListener('click', () => {
+  document.getElementById('plannerModal').style.display = 'flex';
+});
+
+document.getElementById('closePlanner').addEventListener('click', () => {
+  document.getElementById('plannerModal').style.display = 'none';
+});
+
+// IMP: Acquire the VS Code API
+const vscode = acquireVsCodeApi();
+
+// Listen for messages from the extension
+window.addEventListener('message', (event) => {
+  console.log(`Received message in chat.js: ${event.data.type}`, event.data);
+  switch (event.data.type) {
+    case 'settingSaved':
+      settingSaved(event.data.content.message);
+      disableSendButton(!event.data.content.allowUserMessage);
+      break;
+
+    case 'botMessage':
+      sendBotMessage(event.data.content.message, event.data.content.allowUserMessage);
+      disableSendButton(!event.data.content.allowUserMessage, event.data.content.messageInputText);
+      break;
+
+    case 'plannedSteps':
+      totalStepsCompleted = 0;
+      displayPlannedSteps(event.data.content);
+      break;
+
+    case 'plannedStepStart':
+      updateStepStatus(event.data.content, 'started');
+      break;
+
+    case 'plannedStepComplete':
+      updateStepStatus(event.data.content, 'completed');
+      break;
+
+    case 'solutionCompleted':
+      sendBotMessage(event.data.content.message, event.data.content.allowUserMessage);
+      disableSendButton(!event.data.content.allowUserMessage);
+      removRecentUserLoader();
+      markSolutionCompleted(event.data.content);
+      break;
+
+    case 'tokenUsed':
+      displayTokenUsed(event.data.content.message);
+      break;
+
+    case 'showCoderTab':
+      displayCoderTab(event.data.content);
+      break;
+
+    case 'showPlannerTab':
+      displayPlannerTab(event.data.content);
+      break;
+
+    case 'showResearcherTab':
+      displayResearcherTab(event.data.content);
+      break;
+
+    case 'plannerLogs':
+      displayPlannerLogs(event.data.content);
+      break;
+
+    case 'researcherLogs':
+      displayResearcherLogs(event.data.content);
+      break;
+
+    case 'coderLogs':
+      displayCoderLogs(event.data.content);
+      break;
+
+    default:
+      sendBotMessage(`Unknown message received from facilitator: ${event.data}`, false);
+  }
+});
+
+function updateIconColors() {
+  const isDarkMode = window.matchMedia('(prefers-color-scheme: dark)').matches;
+
+  const iconColor = isDarkMode ? '#FFFFFF' : '#000000';
+
+  document.querySelectorAll('.icon').forEach((iconElement) => {
+    iconElement.style.color = iconColor;
+  });
+}
+
+function sendUserMessage() {
+  const message = userInput.value.trim();
+  if (message) {
+    displayMessage(message, 'user');
+    vscode.postMessage({ type: 'userMessage', content: message });
+    userInput.value = '';
+    adjustTextAreaHeight();
+  }
+  disableSendButton(true);
+}
+
+function sendBotMessage(message, allowUserInput) {
+  message = message.trim();
+  if (message) {
+    displayMessage(message, 'bot', allowUserInput);
+    adjustTextAreaHeight();
+  }
+}
+
+function onInputChange(event) {
+  removRecentUserLoader();
+  adjustTextAreaHeight();
+  
+  // Check if "Command" key (Mac) or "Ctrl" key (Windows/Linux) and "Enter" key are pressed
+  if ((event.metaKey || event.ctrlKey) && event.key === 'Enter') {
+    sendUserMessage();
+  }
+}
+
+function adjustTextAreaHeight() {
+  const minHeight = 15;
+  const maxHeight = 90;
+
+  userInput.style.height = minHeight + 'px';
+  const newHeight = Math.min(maxHeight, Math.max(userInput.scrollHeight, minHeight));
+  userInput.style.height = newHeight + 'px';
+}
+
+function displayMessage(msg, sender, allowUserInput) {
+  const chatListContainerElement = document.getElementById('messageContainer');
+
+  const messageElement = createMessageElement(msg, sender, allowUserInput);
+
+  userInput.value = '';
+
+  // Defer the scrolling a bit to ensure layout updates
+  chatListContainerElement.appendChild(messageElement);
+
+  if (sender === "bot" && allowUserInput) {
+    displayMessage("Waiting for your input", "user", true); 
+  }
+
+  chatListContainerElement.scrollTop = chatListContainerElement.scrollHeight + 10;
+}
+
+function removRecentUserLoader() {
+  const userMessages = document.querySelectorAll(".user-input-waiting");
+
+  if (userMessages.length <= 0) {
+    return;
+  }
+
+  const recentMessage = userMessages[userMessages.length - 1];
+
+  recentMessage.remove();
+}
+
+function createMessageElement(msg, sender, allowUserInput) {
+  removeAllLoaderInstanceFromDOM("message-loader");
+  
+  const chatElement = document.createElement('div');
+
+
+  // Construct user message HTML format
+  if (sender === 'user') {
+    if (allowUserInput) {
+      const loaderElement = document.createElement('div');
+      loaderElement.classList.add('message-loader');
+      loaderElement.innerHTML = loaderSvg;
+      chatElement.appendChild(loaderElement);
+    }
+
+    const messageElement = document.createElement('div');
+    if (allowUserInput) {
+      messageElement.classList.add('user-text-inactive');
+      chatElement.classList.add('user-message', 'user-input-waiting');
+    } else {
+      messageElement.classList.add('user');
+      chatElement.classList.add('user-message');
+    }
+    messageElement.textContent = msg;
+
+    chatElement.appendChild(messageElement);
+    // Replace new line characters with HTML line break to properly display in HTML
+    const formattedMessage = messageElement.innerHTML.replace(/\n/g, '<br>');
+    messageElement.innerHTML = formattedMessage;
+
+    const iconElement = document.createElement('div');
+    iconElement.classList.add('icon');
+    iconElement.innerHTML = UserSvg;
+    chatElement.appendChild(iconElement);
+  }
+
+  // Construct bot message HTML format
+  if (sender === 'bot') {
+    chatElement.classList.add('bot-message');
+    const iconElement = document.createElement('div');
+    iconElement.classList.add('boticon');
+    iconElement.innerHTML = BotSvg;
+    chatElement.appendChild(iconElement);
+
+    const messageElement = document.createElement('div');
+    messageElement.classList.add('bot');
+    messageElement.textContent = msg;
+    chatElement.appendChild(messageElement);
+    const formattedMessage = messageElement.innerHTML.replace(/\n/g, '<br>');
+    messageElement.innerHTML = formattedMessage;
+
+    if (!allowUserInput) {
+      const loaderElement = document.createElement('div');
+      loaderElement.classList.add('message-loader');
+      loaderElement.innerHTML = loaderSvg;
+      chatElement.appendChild(loaderElement);
+    }
+  }
+  
+  return chatElement;
+}
+
+function removeAllLoaderInstanceFromDOM(className) {
+  var elementsToRemove = document.querySelectorAll(`.${className}`);
+
+  // Iterate over the NodeList and remove each element
+  elementsToRemove.forEach(function (element) {
+    element.remove();
+  });
+}
+
+function openSettings() {
+  document.getElementById('settingsModal').style.display = 'flex';
+  //  vscode.postMessage({ type: 'requestEnvVariables' });
+}
+
+function closeSettings() {
+  document.getElementById('settingsModal').style.display = 'none';
+}
+
+function saveSettings() {
+  const openAIKey = document.getElementById('SIRJI_OPENAI_API_KEY').value.trim();
+
+  let isValid = true;
+
+  document.getElementById('save_settings_error').textContent = '';
+
+  if (!openAIKey) {
+    document.getElementById('SIRJI_OPENAI_API_KEY_ERROR').textContent = 'OpenAI API Key is required.';
+    isValid = false;
+  } else {
+    document.getElementById('SIRJI_OPENAI_API_KEY_ERROR').textContent = '';
+  }
+
+  if (isValid) {
+    const saveButton = document.getElementById('saveSettings');
+    saveButton.textContent = 'Saving...';
+    saveButton.disabled = true;
+
+    const settings = {
+      SIRJI_OPENAI_API_KEY: openAIKey,
+    };
+
+    vscode.postMessage({ type: 'saveSettings', content: settings });
+  }
+}
+
+function settingSaved(data) {
+  const saveButton = document.getElementById('saveSettings');
+  saveButton.textContent = 'Save';
+  saveButton.disabled = false;
+  if (data.success) {
+    sendBotMessage(data.message, false);
+    closeSettings();
+  } else {
+    document.getElementById('save_settings_error').textContent = `Settings can not be saved. Please contact Sirji team. Error: ${data.message}`;
+  }
+}
+
+function updateStepStatus(message, status) {
+  let stepNumber = message.match(/\d+/g);
+
+  if (!stepNumber) {
+    return;
+  }
+
+  if (stepNumber && stepNumber.length === 0) {
+    return;
+  }
+
+  if (stepsArray?.length === 0 || !stepsArray) {
+    return;
+  }
+
+  if (stepNumber.length === 1) {
+    console.log('Updating step:', stepNumber[0], stepsArray[0]);
+    stepsArray[stepNumber[0] - 1].status = status;
+    if (status === 'completed') {
+      for (let i = 0; i < stepNumber[0] - 1; i++) {
+        stepsArray[i].status = status;
+      }
+    }
+  }
+
+  let maxStepNumber = 0;
+  if (stepNumber.length === 2) {
+    maxStepNumber = Math.max(...stepNumber);
+    for (let i = 0; i < maxStepNumber - 1; i++) {
+      stepsArray[i].status = status;
+    }
+  }
+
+  if (status === 'completed') {
+    totalStepsCompleted = stepsArray.length;
+    setProgress(totalStepsCompleted, stepsArray.length);
+
+    toggleProgressTextColor();
+  }
+
+  totalStepsCompleted = stepsArray.filter((step) => step.status === 'completed').length;
+
+  displayPlannedSteps(stepsArray);
+}
+
+function displayPlannedSteps(steps) {
+  setProgress(totalStepsCompleted, steps.length);
+  document.getElementById('progressCircle').style.visibility = 'visible';
+  stepsArray = steps;
+  const listElement = document.getElementById('plannerStepsList');
+  listElement.innerHTML = '';
+
+  if (!steps) {
+    return;
+  }
+
+  if (steps.length === 0) {
+    return;
+  }
+
+  steps.forEach((step, index) => {
+    step.status = step.status || '';
+    const listItem = document.createElement('li');
+    listItem.className = 'stepItem';
+    const stepDescription = step.description;
+
+    if (step.status === 'started') {
+      listItem.innerHTML = `<div class="loader"></div><label>${stepDescription}</label>`;
+    } else {
+      listItem.innerHTML = `
+      <input type="checkbox" class="checkbox" id="checkbox-${index}" ${step.status === 'completed' ? 'checked' : ''}>
+      <label for="checkbox-${index}">${stepDescription}</label>
+      `;
+    }
+    listElement.appendChild(listItem);
+  });
+}
+
+function setProgress(x, y) {
+  const progressText = document.getElementById('progressText');
+  progressText.textContent = `${x} of ${y} tasks done`;
+
+  const progressTextInsideModal = document.getElementById('progressTextInsideModal');
+  progressTextInsideModal.textContent = `${x} of ${y} tasks done`;
+}
+
+function disableSendButton(disable, message) {
+  const sendButton = document.getElementById('sendBtn');
+  if (sendButton) {
+    sendButton.disabled = disable;
+    updatePlaceholder(disable, message);
+  }
+}
+
+function updatePlaceholder(disable, message) {
+  const placeholderText = disable ? message || 'Sirji> is working on the problem. We will open the chat window when we have some information, questions, or feedback..' : 'Type a message...';
+  userInput.placeholder = placeholderText;
+  userInput.disabled = disable;
+
+  adjustTextAreaHeight();
+}
+
+function markSolutionCompleted() {
+  if (stepsArray?.length === 0) {
+    return;
+  }
+
+  stepsArray.forEach((step) => {
+    step.status = 'completed';
+  });
+  totalStepsCompleted = stepsArray.length;
+  setProgress(stepsArray.length, stepsArray.length);
+  displayPlannedSteps(stepsArray);
+}
+
+function toggleProgressTextColor() {
+  // Select the element with id "progressText"
+  const progressText = document.getElementById('progressText');
+
+  // Update the background color
+  progressText.style.backgroundColor = '#78a866';
+
+  setTimeout(() => {
+    progressText.style.backgroundColor = 'transparent';
+  }, 2000);
+}
+
+function convertNumber(number) {
+  if (number >= 1000000) {
+      return (Math.ceil(number / 100000) / 10).toFixed(1).replace('.0', '') + 'M';
+  } else if (number >= 100000) {
+      return (Math.ceil(number / 10000) / 100).toFixed(1).replace('.0', '') + 'M';
+  } else if (number >= 1000) {
+      return (Math.ceil(number / 1000)).toFixed(1).replace('.0', '') + 'K';
+  } else {
+      return ""; // Don't show tokens less than 1K
+  }
+}
+
+function displayTokenUsed(data = {}) {
+  const { total_completion_tokens = 0, total_completion_tokens_value = 0, total_prompt_tokens = 0, total_prompt_tokens_value = 0 } = data;
+
+  const totalTokensUsed = total_completion_tokens + total_prompt_tokens;
+
+  if (totalTokensUsed < 1000) {
+    return;
+  }
+
+  updateTokensUsed(totalTokensUsed);
+
+  updateTooltipTokenValues(data);
+}
+
+function updateTokensUsed(totalTokensUsed) {
+  const tokens = convertNumber(totalTokensUsed);
+
+  const jTokensUsed = document.getElementById('jTokensUsed');
+  jTokensUsed.textContent = `${tokens} tokens`;
+
+  document.getElementById('jTokensContainer').style.display = 'flex';
+}
+
+function updateTooltipTokenValues(tokenValues) {
+  const { total_completion_tokens = 0, total_completion_tokens_value = 0, total_prompt_tokens = 0, total_prompt_tokens_value = 0 } = tokenValues;
+
+  const jPromptTokensUsed = document.getElementById('jPromptTokensUsed');
+  jPromptTokensUsed.textContent = `Prompt Tokens - ${total_prompt_tokens} | $${total_prompt_tokens_value.toFixed(2)}`;
+
+  const jCompletionTokensUsed = document.getElementById('jCompletionTokensUsed');
+  jCompletionTokensUsed.textContent = `Completion Tokens - ${total_completion_tokens} | $${total_completion_tokens_value.toFixed(2)}`;
+}
+
+function displayPlannerLogs(data) {
+  const plannerLogs = document.getElementById('plannerLogs');
+  plannerLogs.innerText = data;
+}
+
+function displayResearcherLogs(data) {
+  const researcherLogs = document.getElementById('researcherLogs');
+  researcherLogs.innerText = data;
+}
+
+function displayCoderLogs(data) {
+  const coderLogs = document.getElementById('coderLogs');
+  coderLogs.innerText = data;
+}
+
+function displayCoderTab(data) {
+  // showTab("coderTab");
+  const coderTab = document.getElementById('coderTab');
+  // coderTab.innerHTML = data;
+
+  coderTabInterval = setInterval(() => {
+    vscode.postMessage({ type: 'requestCoderLogs' });
+  }, 10000);
+}
+
+function displayPlannerTab(data) {
+  // showTab("plannerTab");
+  const plannerTab = document.getElementById('plannerTab');
+  // plannerTab.innerHTML = data;
+
+  plannerTabInterval = setInterval(() => {
+    vscode.postMessage({ type: 'requestPlannerLogs' });
+  }, 10000);
+}
+
+function displayResearcherTab(data) {
+  // showTab("researcherTab");
+  const researcherTab = document.getElementById('researcherTab');
+  // researcherTab.innerHTML = data;
+
+  researcherTabInterval = setInterval(() => {
+    vscode.postMessage({ type: 'requestResearcherLogs' });
+  }, 10000);
+}
+
+function closeCoderTab() {
+  clearInterval(coderTabInterval);
+  document.getElementById('coderTab').innerHTML = '';
+}
+
+function closePlannerTab() {
+  clearInterval(plannerTabInterval);
+  document.getElementById('plannerTab').innerHTML = '';
+}
+
+function closeResearcherTab() {
+  clearInterval(researcherTabInterval);
+  document.getElementById('researcherTab').innerHTML = '';
+}
+
+const tabButtons = document.querySelectorAll('.tab-button');
+
+tabButtons.forEach(function (button) {
+  button.addEventListener('click', function () {
+    const tabName = this.getAttribute('data-tab');
+    showTab(tabName);
+  });
+});
+
+function showTab(tabName) {
+  let i, tabcontent, tablinks;
+
+  // Get all elements with class="tab" and hide them
+  tabcontent = document.getElementsByClassName('tab');
+  for (i = 0; i < tabcontent.length; i++) {
+    tabcontent[i].style.display = 'none';
+  }
+
+  // Get all elements with class="tab-button" and remove the class "active"
+  tablinks = document.getElementsByClassName('tab-button');
+  for (i = 0; i < tablinks.length; i++) {
+    tablinks[i].classList.remove('active');
+  }
+
+  // Show the current tab, and add an "active" class to the button that opened the tab
+  document.getElementById(tabName).style.display = 'block';
+  document.querySelector('[data-tab="' + tabName + '"]').classList.add('active');
+}
+
+// Show the initial tab on page load
+showTab('chatTerminalTab');
+
+
+updateIconColors();
+
+vscode.postMessage({
+  type: 'webViewReady',
+  content: true,
+});
diff --git a/sirji/vscode-extension/tsconfig.json b/sirji/vscode-extension/tsconfig.json
new file mode 100644
index 00000000..6954702e
--- /dev/null
+++ b/sirji/vscode-extension/tsconfig.json
@@ -0,0 +1,17 @@
+{
+	"compilerOptions": {
+		"module": "Node16",
+		"target": "ES2022",
+		"outDir": "out",
+		"lib": [
+			"ES2022"
+		],
+		"sourceMap": true,
+		"rootDir": "src",
+		"strict": true   /* enable all strict type-checking options */
+		/* Additional Checks */
+		// "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */
+		// "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */
+		// "noUnusedParameters": true,  /* Report errors on unused parameters. */
+	}
+}
diff --git a/sirji/vscode-extension/vsc-extension-quickstart.md b/sirji/vscode-extension/vsc-extension-quickstart.md
new file mode 100644
index 00000000..9d331794
--- /dev/null
+++ b/sirji/vscode-extension/vsc-extension-quickstart.md
@@ -0,0 +1,43 @@
+# Welcome to your VS Code Extension
+
+## What's in the folder
+
+* This folder contains all of the files necessary for your extension.
+* `package.json` - this is the manifest file in which you declare your extension and command.
+  * The sample plugin registers a command and defines its title and command name. With this information VS Code can show the command in the command palette. It doesn’t yet need to load the plugin.
+* `src/extension.ts` - this is the main file where you will provide the implementation of your command.
+  * The file exports one function, `activate`, which is called the very first time your extension is activated (in this case by executing the command). Inside the `activate` function we call `registerCommand`.
+  * We pass the function containing the implementation of the command as the second parameter to `registerCommand`.
+
+## Get up and running straight away
+
+* Press `F5` to open a new window with your extension loaded.
+* Run your command from the command palette by pressing (`Ctrl+Shift+P` or `Cmd+Shift+P` on Mac) and typing `Hello World`.
+* Set breakpoints in your code inside `src/extension.ts` to debug your extension.
+* Find output from your extension in the debug console.
+
+## Make changes
+
+* You can relaunch the extension from the debug toolbar after changing code in `src/extension.ts`.
+* You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes.
+
+## Explore the API
+
+* You can open the full set of our API when you open the file `node_modules/@types/vscode/index.d.ts`.
+
+## Run tests
+
+* Install the [Extension Test Runner](https://marketplace.visualstudio.com/items?itemName=ms-vscode.extension-test-runner)
+* Run the "watch" task via the **Tasks: Run Task** command. Make sure this is running, or tests might not be discovered.
+* Open the Testing view from the activity bar and click the Run Test" button, or use the hotkey `Ctrl/Cmd + ; A`
+* See the output of the test result in the Test Results view.
+* Make changes to `src/test/extension.test.ts` or create new test files inside the `test` folder.
+  * The provided test runner will only consider files matching the name pattern `**.test.ts`.
+  * You can create folders inside the `test` folder to structure your tests any way you want.
+
+## Go further
+
+* [Follow UX guidelines](https://code.visualstudio.com/api/ux-guidelines/overview) to create extensions that seamlessly integrate with VS Code's native interface and patterns.
+ * Reduce the extension size and improve the startup time by [bundling your extension](https://code.visualstudio.com/api/working-with-extensions/bundling-extension).
+ * [Publish your extension](https://code.visualstudio.com/api/working-with-extensions/publishing-extension) on the VS Code extension marketplace.
+ * Automate builds by setting up [Continuous Integration](https://code.visualstudio.com/api/working-with-extensions/continuous-integration).
\ No newline at end of file
diff --git a/tools/LICENSE b/tools/LICENSE
new file mode 100644
index 00000000..4df90ae4
--- /dev/null
+++ b/tools/LICENSE
@@ -0,0 +1,21 @@
+MIT License
+
+Copyright (c) 2024 True Sparrow
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all
+copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+SOFTWARE.
diff --git a/tools/MANIFEST.in b/tools/MANIFEST.in
new file mode 100644
index 00000000..cc0d1164
--- /dev/null
+++ b/tools/MANIFEST.in
@@ -0,0 +1,1 @@
+include LICENSE
\ No newline at end of file
diff --git a/tools/README.md b/tools/README.md
new file mode 100644
index 00000000..c2e04573
--- /dev/null
+++ b/tools/README.md
@@ -0,0 +1,140 @@
+<p align="center">
+  <a href="." target="blank"><img src="https://github.com/sirji-ai/sirji/assets/7627517/363fc6dd-69af-4d84-8b7c-a91ec092058d" width="250" alt="Sirji Logo" /></a>
+</p>
+
+<p align="center">
+  <em>Sirji is an Open Source AI Software Development Agent.</em>
+</p>
+
+<p align="center">
+  Built with ❤️ by <a href="https://truesparrow.com/" target="_blank">True Sparrow</a>
+</p>
+
+<p align="center">
+  <img alt="GitHub License" src="https://img.shields.io/github/license/sirji-ai/sirji">
+  <img alt="GitHub commit activity" src="https://img.shields.io/github/commit-activity/m/sirji-ai/sirji">
+  <img alt="GitHub Issues or Pull Requests" src="https://img.shields.io/github/issues/sirji-ai/sirji">
+  <img alt="PyPI sirji-tools" src="https://img.shields.io/pypi/v/sirji-tools.svg">
+</p>
+
+<p align="center">
+  <img alt="GitHub Repo stars" src="https://img.shields.io/github/stars/sirji-ai/sirji">
+  <img alt="GitHub forks" src="https://img.shields.io/github/forks/sirji-ai/sirji">
+  <img alt="GitHub watchers" src="https://img.shields.io/github/watchers/sirji-ai/sirji">
+</p>
+
+## Sirji Tools
+
+`sirji-tools` is a PyPI package that provides tools for:
+
+- Crawling (downloading web pages to markdown files)
+- Searching on Google
+- Custom Logging
+
+## Installation
+
+### Setup Virtual Environment
+
+We recommend setting up a virtual environment to isolate Python dependencies, ensuring project-specific packages without conflicting with system-wide installations.
+
+```zsh
+python3 -m venv venv
+source venv/bin/activate
+```
+
+### Install Package
+
+Install the package from PyPi:
+
+```zsh
+pip install sirji-tools
+```
+
+Run the following command to install playwright:
+
+```zsh
+playwright install
+```
+
+## Usage
+
+### Environment Variables
+
+Ensure that following environment variables are set:
+
+```zsh
+export SIRJI_WORKSPACE="Absolute folder path for Sirji to use as it's workspace. Note that a .sirji folder will be created inside it."
+export SIRJI_RUN_ID='Unique alphanumeric ID for each run. Note that a sub folder named by this ID will be created inside of .sirji folder to store logs, etc.'
+```
+
+### Crawl URLs
+
+Crawl URLs tool will be used to crawl the web pages and extract the information from the web pages. And store the information for the further processing by researcher.
+
+```python
+from sirji_tools import crawl_urls
+
+urls = ['https://www.google.com', 'https://www.yahoo.com']
+
+crawl_urls(urls, 'workspace/researcher')
+```
+
+### Search
+
+Search tool will be used to search the information from the web pages based on the search terms provided.
+It returns the list of URLs related to the search terms.
+
+```python
+from sirji_tools import search_for
+
+search_term = 'python programming'
+
+urls = search_for(search_term)
+```
+
+### Logger
+
+Logger tool will be used to log the information in the log file. It will be used to log the information to show the progress of the execution.
+
+```python
+from sirji_tools.logger import p_logger
+
+p_logger.info("Log line here")
+```
+
+## For Contributors
+
+1. Fork and clone the repository.
+2. Create and activate the virtual environment as described above.
+3. Set the environment variables as described above.
+4. Install the package in editable mode by running the following command from repository root:
+
+```zsh
+pip install -e .
+```
+
+5. Run the following command to install playwright:
+
+```zsh
+playwright install
+```
+
+## Running Tests and Coverage Analysis
+
+Follow the above mentioned steps for "contributors", before running the test cases.
+
+```zsh
+# Install testing dependencies
+pip install pytest coverage
+
+# Execute tests
+pytest
+
+# Measure coverage, excluding test files
+coverage run --omit="tests/*" -m pytest
+coverage report
+```
+
+## License
+
+Distributed under the MIT License. See `LICENSE` for more information.
diff --git a/tools/requirements.txt b/tools/requirements.txt
new file mode 100644
index 00000000..98cb8250
--- /dev/null
+++ b/tools/requirements.txt
@@ -0,0 +1,4 @@
+requests==2.31.0
+pypdf2==3.0.1
+markdownify==0.11.6
+playwright==1.42.0
\ No newline at end of file
diff --git a/tools/setup.py b/tools/setup.py
new file mode 100644
index 00000000..e1bc413f
--- /dev/null
+++ b/tools/setup.py
@@ -0,0 +1,22 @@
+from setuptools import setup, find_packages
+
+setup(
+    name='sirji-tools',
+    version='0.0.9',
+    author='Sirji',
+    description='Crawler and search tools used by Sirji.',
+    license='MIT',
+    long_description=open('README.md').read(),
+    long_description_content_type='text/markdown',
+    url='https://github.com/sirji-ai/sirji',
+    packages=find_packages(),
+    include_package_data=True,  # This includes non-code files specified in MANIFEST.in
+    install_requires=[
+        x for x in open("./requirements.txt", "r+").readlines() if x.strip()
+    ],
+    python_requires='>=3.6',
+    classifiers=[],
+    entry_points={
+        'console_scripts': [],
+    },
+)
diff --git a/workspace/.keep b/tools/sirji_tools/__init__.py
similarity index 100%
rename from workspace/.keep
rename to tools/sirji_tools/__init__.py
diff --git a/sirji/tools/crawler/README.md b/tools/sirji_tools/crawler/README.md
similarity index 97%
rename from sirji/tools/crawler/README.md
rename to tools/sirji_tools/crawler/README.md
index 2652f704..9366a7fe 100644
--- a/sirji/tools/crawler/README.md
+++ b/tools/sirji_tools/crawler/README.md
@@ -18,7 +18,7 @@ The module prominently uses the **Factory Design Pattern** to dynamically select
 To use the crawler module, you need to pass a list of URLs and an output directory where the crawled content will be saved.
 
 ```python
-from sirji.tools.crawler import crawl_urls
+from sirji_tools.crawler import crawl_urls
 
 urls = [
     'http://example.com',
diff --git a/sirji/tools/crawler/__init__.py b/tools/sirji_tools/crawler/__init__.py
similarity index 100%
rename from sirji/tools/crawler/__init__.py
rename to tools/sirji_tools/crawler/__init__.py
diff --git a/sirji/tools/crawler/base.py b/tools/sirji_tools/crawler/base.py
similarity index 89%
rename from sirji/tools/crawler/base.py
rename to tools/sirji_tools/crawler/base.py
index 55e49e84..933b2bf6 100644
--- a/sirji/tools/crawler/base.py
+++ b/tools/sirji_tools/crawler/base.py
@@ -1,10 +1,7 @@
 import os
-import time
 import hashlib
-from urllib.parse import urlparse
-
-from sirji.tools.logger import researcher as logger
 
+from sirji_tools.logger import r_logger as logger
 
 class BaseContentHandler:
     def handle(self, url, output_dir):
diff --git a/sirji/tools/crawler/crawler.py b/tools/sirji_tools/crawler/crawler.py
similarity index 92%
rename from sirji/tools/crawler/crawler.py
rename to tools/sirji_tools/crawler/crawler.py
index e9fa49fc..9332d2f1 100644
--- a/sirji/tools/crawler/crawler.py
+++ b/tools/sirji_tools/crawler/crawler.py
@@ -1,5 +1,5 @@
 from .factory import ContentHandlerFactory
-from sirji.tools.logger import researcher as logger
+from sirji_tools.logger import r_logger as logger
 
 
 def crawl_urls(urls, output_dir):
diff --git a/sirji/tools/crawler/factory.py b/tools/sirji_tools/crawler/factory.py
similarity index 94%
rename from sirji/tools/crawler/factory.py
rename to tools/sirji_tools/crawler/factory.py
index e29eadf0..2fef2623 100644
--- a/sirji/tools/crawler/factory.py
+++ b/tools/sirji_tools/crawler/factory.py
@@ -2,7 +2,7 @@
 from .web_page_handler import WebPageHandler
 from .pdf_handler import PDFHandler
 from .github_handler import GitHubHandler
-from sirji.tools.logger import researcher as logger
+from sirji_tools.logger import r_logger as logger
 
 class ContentHandlerFactory:
     @classmethod
diff --git a/sirji/tools/crawler/github_handler.py b/tools/sirji_tools/crawler/github_handler.py
similarity index 95%
rename from sirji/tools/crawler/github_handler.py
rename to tools/sirji_tools/crawler/github_handler.py
index 5f935d8d..80f1fd44 100644
--- a/sirji/tools/crawler/github_handler.py
+++ b/tools/sirji_tools/crawler/github_handler.py
@@ -3,7 +3,7 @@
 import subprocess
 import shutil
 from urllib.parse import urlparse
-from sirji.tools.logger import researcher as logger
+from sirji_tools.logger import r_logger as logger
 
 class GitHubHandler(BaseContentHandler):
     def handle(self, url, output_dir):
diff --git a/sirji/tools/crawler/pdf_handler.py b/tools/sirji_tools/crawler/pdf_handler.py
similarity index 95%
rename from sirji/tools/crawler/pdf_handler.py
rename to tools/sirji_tools/crawler/pdf_handler.py
index 3cfea073..d3d9df00 100644
--- a/sirji/tools/crawler/pdf_handler.py
+++ b/tools/sirji_tools/crawler/pdf_handler.py
@@ -3,7 +3,7 @@
 import requests
 import PyPDF2
 from markdownify import markdownify as md
-from sirji.tools.logger import researcher as logger
+from sirji_tools.logger import r_logger as logger
 
 class PDFHandler(BaseContentHandler):
     def handle(self, url, output_dir):
diff --git a/sirji/tools/crawler/web_page_handler.py b/tools/sirji_tools/crawler/web_page_handler.py
similarity index 96%
rename from sirji/tools/crawler/web_page_handler.py
rename to tools/sirji_tools/crawler/web_page_handler.py
index b5423e00..3e4d22a4 100644
--- a/sirji/tools/crawler/web_page_handler.py
+++ b/tools/sirji_tools/crawler/web_page_handler.py
@@ -4,7 +4,7 @@
 import re
 
 from .base import BaseContentHandler
-from sirji.tools.logger import researcher as logger
+from sirji_tools.logger import r_logger as logger
 
 
 class WebPageHandler(BaseContentHandler):
diff --git a/sirji/tools/logger/README.md b/tools/sirji_tools/logger/README.md
similarity index 88%
rename from sirji/tools/logger/README.md
rename to tools/sirji_tools/logger/README.md
index 14314227..b0cd8f70 100644
--- a/sirji/tools/logger/README.md
+++ b/tools/sirji_tools/logger/README.md
@@ -27,30 +27,30 @@ export SIRJI_LOG_LEVEL=info
 
 ### Sample Usage of Logger specific to coder:
 ```python
-from .tools.logger import coder as logger
+from sirji_tools.logger import c_logger as logger
 logger.info("Log line here")
 ```
 
 ### Sample Usage of Logger specific to researcher:
 ```python
-from .tools.logger import researcher as logger
+from sirji_tools.logger import r_logger as logger
 logger.info("Log line here")
 ```
 
 ### Sample Usage of Logger specific to planner:
 ```python
-from .tools.logger import planner as logger
+from sirji_tools.logger import p_logger as logger
 logger.info("Log line here")
 ```
 
 ### Sample Usage of Logger specific to executor:
 ```python
-from .tools.logger import executor as logger
+from sirji_tools.logger import e_logger as logger
 logger.info("Log line here")
 ```
 
 ### Sample Usage of Logger specific to sirji:
 ```python
-from .tools.logger import sirji as logger
+from sirji_tools.logger import s_logger as logger
 logger.info("Log line here")
 ```
\ No newline at end of file
diff --git a/tools/sirji_tools/logger/__init__.py b/tools/sirji_tools/logger/__init__.py
new file mode 100644
index 00000000..6e9e3bdd
--- /dev/null
+++ b/tools/sirji_tools/logger/__init__.py
@@ -0,0 +1,8 @@
+from .logger import LoggerManager
+
+c_logger = LoggerManager().coder
+r_logger = LoggerManager().researcher
+p_logger = LoggerManager().planner
+e_logger = LoggerManager().executor
+s_logger = LoggerManager().sirji
+u_logger = LoggerManager().user
diff --git a/sirji/tools/logger/logger.py b/tools/sirji_tools/logger/logger.py
similarity index 84%
rename from sirji/tools/logger/logger.py
rename to tools/sirji_tools/logger/logger.py
index 9edab39e..b14af0ef 100644
--- a/sirji/tools/logger/logger.py
+++ b/tools/sirji_tools/logger/logger.py
@@ -25,7 +25,21 @@ def __init__(self, file_name, log_level):
         self.logger = self._setup_logger(file_name, log_level)
     
     def _log_folder(self):
-        return os.path.join("workspace", "logs")
+        return os.path.join(self._get_workspace_folder(), '.sirji', self._get_run_id_folder(), "logs")
+    
+    def _get_workspace_folder(self):
+        workspace = os.environ.get("SIRJI_WORKSPACE")
+        if workspace is None:
+            raise ValueError(
+                "SIRJI_WORKSPACE is not set as an environment variable")
+        return workspace
+
+    def _get_run_id_folder(self):
+        run_id = os.environ.get("SIRJI_RUN_ID")
+        if run_id is None:
+            raise ValueError(
+                "SIRJI_RUN_ID is not set as an environment variable")
+        return run_id 
     
     def _log_file_path(self, file_name):
         return os.path.join(self._log_folder(), file_name)
diff --git a/sirji/tools/search/__init__.py b/tools/sirji_tools/search/__init__.py
similarity index 100%
rename from sirji/tools/search/__init__.py
rename to tools/sirji_tools/search/__init__.py
diff --git a/tools/sirji_tools/search/search.py b/tools/sirji_tools/search/search.py
new file mode 100644
index 00000000..17e73b69
--- /dev/null
+++ b/tools/sirji_tools/search/search.py
@@ -0,0 +1,50 @@
+from playwright.sync_api import sync_playwright
+import time
+from urllib.parse import urlparse, parse_qs
+
+def search_for(query):
+    with sync_playwright() as p:
+        # Launch the browser
+        browser = p.chromium.launch(headless=False)
+
+        context = browser.new_context(
+            user_agent="Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/58.0.3029.110 Safari/537.3"
+        )
+        page = context.new_page()
+
+        # Navigate to Google
+        page.goto('https://www.google.com')
+
+        # Perform the search
+        page.fill('input[name=q]', query)
+        page.press('input[name=q]', 'Enter')
+
+        time.sleep(10)  # Ensure search results have loaded
+
+        filtered_urls = []
+        exclude_domains_list = ['maps.google.com', 'accounts.google.com']  # Assuming maps.google.com is the domain to block
+
+        # Extract all URLs
+        links = page.query_selector_all('a[href]')
+        seen_domains = set()
+        for link in links:
+            url = link.get_attribute('href')
+            if not url:
+                continue
+
+            parsed_url = urlparse(url)
+            query_params = parse_qs(parsed_url.query)
+            
+            if not (parsed_url.path == '/url' and 'url' in query_params):
+                continue
+
+            target_url = query_params['url'][0]
+            target_url_parsed = urlparse(target_url)
+            domain = target_url_parsed.netloc
+            # Check if the domain exists and is not in the blocked domains list
+            if domain and domain not in exclude_domains_list and domain not in seen_domains:
+                seen_domains.add(domain)
+                filtered_urls.append(target_url)
+
+        browser.close()
+        return filtered_urls[:10]


Test patch:
diff --git a/sirji/tools/__init__.py b/messages/tests/__init__.py
similarity index 100%
rename from sirji/tools/__init__.py
rename to messages/tests/__init__.py
diff --git a/sirji/view/__init__.py b/messages/tests/integration/__init__.py
similarity index 100%
rename from sirji/view/__init__.py
rename to messages/tests/integration/__init__.py
diff --git a/messages/tests/integration/test_message_factory.py b/messages/tests/integration/test_message_factory.py
new file mode 100644
index 00000000..dac3ce59
--- /dev/null
+++ b/messages/tests/integration/test_message_factory.py
@@ -0,0 +1,34 @@
+import pytest
+from sirji_messages import ActionEnum
+from sirji_messages.messages.factory import MessageFactory
+
+# Here we're importing specific message classes for comparison
+from sirji_messages.messages.actions.acknowledge import AcknowledgeMessage
+from sirji_messages.messages.actions.question import QuestionMessage
+from sirji_messages.messages.actions.inform import InformMessage
+from sirji_messages.messages.actions.problem_statement import ProblemStatementMessage
+
+# Test Cases for MessageFactory
+
+
+def test_message_factory_acknowledge():
+    assert isinstance(MessageFactory[ActionEnum.ACKNOWLEDGE.name](
+    ), AcknowledgeMessage), "Incorrect message class for ACKNOWLEDGE action"
+
+
+def test_message_factory_question():
+    assert isinstance(MessageFactory[ActionEnum.QUESTION.name](
+    ), QuestionMessage), "Incorrect message class for QUESTION action"
+
+
+def test_message_factory_inform():
+    assert isinstance(MessageFactory[ActionEnum.INFORM.name](
+    ), InformMessage), "Incorrect message class for INFORM action"
+
+
+def test_message_factory_problem_statement():
+    assert isinstance(MessageFactory[ActionEnum.PROBLEM_STATEMENT.name](
+    ), ProblemStatementMessage), "Incorrect message class for PROBLEM_STATEMENT action"
+
+# Optionally, you could add tests to verify the correct behavior or outputs of these messages or their methods.
+# For example, you could test the generation of sample messages, ensuring it matches expected formats.
diff --git a/messages/tests/integration/test_system_prompts.py b/messages/tests/integration/test_system_prompts.py
new file mode 100644
index 00000000..e4e61eee
--- /dev/null
+++ b/messages/tests/integration/test_system_prompts.py
@@ -0,0 +1,41 @@
+import pytest
+from sirji_messages import AgentEnum
+from sirji_messages.system_prompts.factory import AgentSystemPromptFactory
+
+# Here we're importing the specific system prompt classes for comparison
+from sirji_messages.system_prompts.agents.coder import CoderSystemPrompt
+from sirji_messages.system_prompts.agents.planner import PlannerSystemPrompt
+from sirji_messages.system_prompts.agents.user import UserSystemPrompt
+from sirji_messages.system_prompts.agents.executor import ExecutorSystemPrompt
+from sirji_messages.system_prompts.agents.researcher import ResearcherSystemPrompt
+
+# Test Cases for AgentSystemPromptFactory
+
+
+def test_system_prompt_factory_coder():
+    assert isinstance(AgentSystemPromptFactory[AgentEnum.CODER.name](
+    ), CoderSystemPrompt), "Incorrect system prompt for CODER"
+
+
+def test_system_prompt_factory_planner():
+    assert isinstance(AgentSystemPromptFactory[AgentEnum.PLANNER.name](
+    ), PlannerSystemPrompt), "Incorrect system prompt for PLANNER"
+
+
+def test_system_prompt_factory_user():
+    # Noting here that User might not invoke prompts, but testing for completeness
+    assert isinstance(AgentSystemPromptFactory[AgentEnum.USER.name](
+    ), UserSystemPrompt), "Incorrect system prompt for USER"
+
+
+def test_system_prompt_factory_executor():
+    assert isinstance(AgentSystemPromptFactory[AgentEnum.EXECUTOR.name](
+    ), ExecutorSystemPrompt), "Incorrect system prompt for EXECUTOR"
+
+
+def test_system_prompt_factory_researcher():
+    assert isinstance(AgentSystemPromptFactory[AgentEnum.RESEARCHER.name](
+    ), ResearcherSystemPrompt), "Incorrect system prompt for RESEARCHER"
+
+# Optionally, you could test for the expected outputs or properties of each class, ensuring integration
+# between the system prompts and their respective agent roles perform correctly.
diff --git a/messages/tests/integration/test_system_prompts_base.py b/messages/tests/integration/test_system_prompts_base.py
new file mode 100644
index 00000000..9b2becef
--- /dev/null
+++ b/messages/tests/integration/test_system_prompts_base.py
@@ -0,0 +1,20 @@
+import pytest
+from sirji_messages.system_prompts.agents.base import AgentSystemPromptBase
+from sirji_messages.system_prompts.factory import AgentSystemPromptFactory
+from sirji_messages import AgentEnum
+
+
+@pytest.mark.parametrize("agent_class", [
+    AgentSystemPromptFactory[AgentEnum.CODER.name],
+    AgentSystemPromptFactory[AgentEnum.PLANNER.name],
+    # Add all agent classes
+])
+def test_agent_system_prompt_base_integration(agent_class):
+    agent_instance = agent_class()
+    prompt_text = agent_instance.system_prompt()
+
+    assert isinstance(prompt_text, str), "System prompt text must be a string"
+    assert len(prompt_text) > 0, "System prompt text should not be empty"
+    # This can be expanded with specific checks for each agent type
+
+# Additionally, test other methods and properties if applicable
diff --git a/sirji/chat_providers/base.py b/messages/tests/unit/__init__.py
similarity index 100%
rename from sirji/chat_providers/base.py
rename to messages/tests/unit/__init__.py
diff --git a/messages/tests/unit/test_actions_base.py b/messages/tests/unit/test_actions_base.py
new file mode 100644
index 00000000..894badf3
--- /dev/null
+++ b/messages/tests/unit/test_actions_base.py
@@ -0,0 +1,31 @@
+import pytest
+from sirji_messages.messages.actions.base import BaseMessages
+
+# Creating a concrete class by inheriting from BaseMessages for testing purposes
+
+
+class ConcreteMessage(BaseMessages):
+    def template_payload_part(self):
+        return "Payload part"
+
+    def sample(self):
+        return "Sample message"
+
+    def description(self):
+        return "Description"
+
+    def custom_properties(self):
+        return ['DETAILS']
+
+
+def test_base_messages_instantiation():
+    concrete_message = ConcreteMessage()
+    assert isinstance(concrete_message, BaseMessages)
+
+
+def test_base_messages_methods():
+    concrete_message = ConcreteMessage()
+    assert concrete_message.template_payload_part() == "Payload part"
+    assert concrete_message.sample() == "Sample message"
+    assert concrete_message.description() == "Description"
+    assert concrete_message.custom_properties() == ['DETAILS']
diff --git a/messages/tests/unit/test_custom_exceptions.py b/messages/tests/unit/test_custom_exceptions.py
new file mode 100644
index 00000000..0a3c16e4
--- /dev/null
+++ b/messages/tests/unit/test_custom_exceptions.py
@@ -0,0 +1,16 @@
+import pytest
+from sirji_messages import MessageValidationError, MessageParsingError
+
+
+def test_message_validation_error():
+    with pytest.raises(MessageValidationError) as exc_info:
+        raise MessageValidationError("Custom error message")
+    assert str(
+        exc_info.value) == "Custom error message", "MessageValidationError does not show the correct message"
+
+
+def test_message_parsing_error():
+    with pytest.raises(MessageParsingError) as exc_info:
+        raise MessageParsingError("Parsing failed")
+    assert str(
+        exc_info.value) == "Parsing failed", "MessageParsingError does not show the correct message"
diff --git a/messages/tests/unit/test_enums.py b/messages/tests/unit/test_enums.py
new file mode 100644
index 00000000..2d9addb5
--- /dev/null
+++ b/messages/tests/unit/test_enums.py
@@ -0,0 +1,30 @@
+import pytest
+from sirji_messages import AgentEnum, ActionEnum
+
+# Test Cases for AgentEnum
+
+
+def test_agent_enum_values_and_full_names():
+    assert AgentEnum.CODER.value == 1, "CODER value mismatch"
+    assert AgentEnum.CODER.full_name == "Coding Agent", "CODER full name mismatch"
+    assert AgentEnum.USER.value == 5, "USER value mismatch"
+    assert AgentEnum.USER.full_name == "End User", "USER full name mismatch"
+
+
+def test_agent_enum_names():
+    assert AgentEnum.CODER.name == "CODER", "Incorrect name for CODER"
+    assert AgentEnum.USER.name == "USER", "Incorrect name for USER"
+
+# Test Cases for ActionEnum
+
+
+def test_action_enum_auto_values():
+    # Test specific actions to ensure their auto-generated values are distinct
+    assert ActionEnum.ACKNOWLEDGE.value != ActionEnum.ANSWER.value, "ACKNOWLEDGE and ANSWER values should not be equal"
+    assert ActionEnum.EXECUTE_COMMAND.value != ActionEnum.CREATE_FILE.value, "EXECUTE_COMMAND and CREATE_FILE values should not be equal"
+
+
+def test_action_enum_membership():
+    # Check if certain actions are in the ActionEnum
+    assert "INFORM" in ActionEnum.__members__, "INFORM should be a member of ActionEnum"
+    assert "QUESTION" in ActionEnum.__members__, "QUESTION should be a member of ActionEnum"
diff --git a/messages/tests/unit/test_parser.py b/messages/tests/unit/test_parser.py
new file mode 100644
index 00000000..a26ba004
--- /dev/null
+++ b/messages/tests/unit/test_parser.py
@@ -0,0 +1,101 @@
+import pytest
+from sirji_messages import message_parse, MessageParsingError, MessageValidationError
+
+# Test Cases for message_parse function
+
+
+def test_message_parse_valid_input():
+    input_message = """
+    ```
+    FROM: CODER
+    TO: USER
+    ACTION: QUESTION
+    DETAILS: Some information.
+    ```
+    """
+    expected_output = {
+        'FROM': 'CODER',
+        'TO': 'USER',
+        'ACTION': 'QUESTION',
+        'DETAILS': 'Some information.'
+    }
+    assert message_parse(
+        input_message) == expected_output, "Valid message parsing failed"
+
+
+def test_message_parse_invalid_format_raises_error():
+    input_message = "FROM: CODER\nTO: USER\nACTION: QUESTION\nDETAILS: Some information."
+    with pytest.raises(MessageValidationError):
+        message_parse(input_message)
+
+
+def test_message_parse_missing_action_raises_error():
+    input_message = """
+    ```
+    FROM: CODER
+    TO: USER
+    DETAILS: Missing action field.
+    ```
+    """
+    with pytest.raises(MessageValidationError):
+        message_parse(input_message)
+
+
+def test_message_parse_incorrect_message_raises_error():
+    input_message = """
+    ```
+    This is not a correctly formatted message.
+    ```
+    """
+    with pytest.raises(MessageValidationError):
+        message_parse(input_message)
+
+
+def test_message_parse_permission_denied_error():
+    input_message = """
+    ```
+    FROM: USER
+    TO: CODER
+    ACTION: CREATE_FILE
+    DETAILS: Attempting an unauthorized action.
+    ```
+    """
+    with pytest.raises(MessageValidationError):
+        message_parse(input_message)
+
+# Testing with extra spaces around the key elements
+
+
+def test_message_parse_with_extra_spaces():
+    input_message = """
+    ```
+    FROM :  CODER
+    TO : USER
+    ACTION : QUESTION
+    DETAILS : Extra spaces around colons.
+    ```
+    """
+    expected_output = {
+        'FROM': 'CODER',
+        'TO': 'USER',
+        'ACTION': 'QUESTION',
+        'DETAILS': 'Extra spaces around colons.'
+    }
+    assert message_parse(
+        input_message) == expected_output, "Message parsing failed with extra spaces"
+
+# Adjusting the test to align with the current implementation's case sensitivity
+
+
+def test_message_parse_case_sensitivity():
+    input_message = """
+    ```
+    from: CODER
+    to: USER
+    action: QUESTION
+    details: Checking case sensitivity.
+    ```
+    """
+    # Expecting a MessageValidationError due to case sensitivity
+    with pytest.raises(MessageValidationError):
+        message_parse(input_message)
diff --git a/messages/tests/unit/test_permissions.py b/messages/tests/unit/test_permissions.py
new file mode 100644
index 00000000..d133e9cc
--- /dev/null
+++ b/messages/tests/unit/test_permissions.py
@@ -0,0 +1,44 @@
+import pytest
+from sirji_messages import validate_permission, AgentEnum, ActionEnum
+
+# Test cases for validate_permission function
+
+
+def test_permission_valid():
+    # Assuming Coder has permission to inform a User
+    assert validate_permission(
+        "CODER", "USER", "QUESTION"), "Valid permission was denied"
+
+
+def test_permission_invalid_action():
+    # Assuming Coder does not have permission to execute a command directly to a User
+    assert not validate_permission(
+        "CODER", "USER", "EXECUTE_COMMAND"), "Invalid action was erroneously permitted"
+
+
+def test_permission_invalid_agent_pair():
+    # Assuming no direct interaction is allowed from Executor to User
+    assert not validate_permission(
+        "EXECUTOR", "USER", "QUESTION"), "Invalid agent pair was erroneously permitted"
+
+
+def test_permission_nonexistent_from_agent():
+    assert not validate_permission(
+        "FAKE_AGENT", "USER", "QUESTION"), "Permission check with nonexistent 'from' agent should be denied"
+
+
+def test_permission_nonexistent_to_agent():
+    assert not validate_permission(
+        "CODER", "FAKE_AGENT", "QUESTION"), "Permission check with nonexistent 'to' agent should be denied"
+
+
+def test_permission_nonexistent_action():
+    assert not validate_permission(
+        "CODER", "USER", "FAKE_ACTION"), "Permission check with nonexistent action should be denied"
+
+# Testing with invalid agent enums
+
+
+def test_permission_with_invalid_agent_enum():
+    assert not validate_permission(
+        "INVALID_AGENT", "USER", "QUESTION"), "Permission check with invalid enum should return False instead of raising KeyError"
diff --git a/sirji/vscode-extension/.vscode-test.mjs b/sirji/vscode-extension/.vscode-test.mjs
new file mode 100644
index 00000000..b62ba25f
--- /dev/null
+++ b/sirji/vscode-extension/.vscode-test.mjs
@@ -0,0 +1,5 @@
+import { defineConfig } from '@vscode/test-cli';
+
+export default defineConfig({
+	files: 'out/test/**/*.test.js',
+});
diff --git a/sirji/vscode-extension/src/test/extension.test.ts b/sirji/vscode-extension/src/test/extension.test.ts
new file mode 100644
index 00000000..4ca0ab41
--- /dev/null
+++ b/sirji/vscode-extension/src/test/extension.test.ts
@@ -0,0 +1,15 @@
+import * as assert from 'assert';
+
+// You can import and use all API from the 'vscode' module
+// as well as import your extension to test it
+import * as vscode from 'vscode';
+// import * as myExtension from '../../extension';
+
+suite('Extension Test Suite', () => {
+	vscode.window.showInformationMessage('Start all tests.');
+
+	test('Sample test', () => {
+		assert.strictEqual(-1, [1, 2, 3].indexOf(5));
+		assert.strictEqual(-1, [1, 2, 3].indexOf(0));
+	});
+});
diff --git a/tools/tests/test_crawler.py b/tools/tests/test_crawler.py
new file mode 100644
index 00000000..4b2f623b
--- /dev/null
+++ b/tools/tests/test_crawler.py
@@ -0,0 +1,37 @@
+import pytest
+from unittest.mock import patch, MagicMock
+from sirji_tools.crawler import crawl_urls
+from sirji_tools.crawler.factory import ContentHandlerFactory
+
+class TestCrawler:
+    @patch('sirji_tools.crawler.factory.ContentHandlerFactory.get_handler')
+    def test_crawl_urls_with_mock_handlers(self, mock_get_handler):
+        # Setup
+        # Mocking the handler to simulate handling URLs without actual network operations
+        mock_handler = MagicMock()
+        mock_get_handler.return_value = mock_handler
+
+        # Test data
+        urls = ['https://example.com/page1', 'https://example.com/page2']
+        output_dir = 'test_output'
+
+        # Execute the function under test
+        crawl_urls(urls, output_dir)
+
+        # Assertions
+        assert mock_get_handler.call_count == len(urls), "get_handler was not called the expected number of times."
+        mock_handler.handle.assert_called()
+        assert mock_handler.handle.call_count == len(urls), "URL handler's handle method was not called the expected number of times."
+
+    @patch('sirji_tools.crawler.web_page_handler.WebPageHandler.handle')
+    def test_crawl_urls_with_specific_handler(self, mock_handle):
+        # Setup
+        # No need to mock get_handler here since we're directly testing with a specific handler type
+        urls = ['https://example.com/page1']
+        output_dir = 'test_output'
+
+        # Execute the function under test with a setup that would use the WebPageHandler
+        crawl_urls(urls, output_dir)
+
+        # Assertions
+        mock_handle.assert_called_once_with(urls[0], output_dir), "WebPageHandler's handle method was not called as expected."
diff --git a/tools/tests/test_logger.py b/tools/tests/test_logger.py
new file mode 100644
index 00000000..6a635acd
--- /dev/null
+++ b/tools/tests/test_logger.py
@@ -0,0 +1,43 @@
+import pytest
+from unittest.mock import patch, MagicMock
+from sirji_tools.logger import LoggerManager
+
+class TestLoggerManager:
+    @patch('sirji_tools.logger.logger.logging')
+    def test_logger_manager_initialization(self, mock_logging):
+        # Setup
+        mock_logging.getLogger.return_value = MagicMock()
+
+        # Test LoggerManager initialization
+        logger_manager = LoggerManager()
+
+        # Assertions to ensure that logger instances are created
+        assert logger_manager.coder is not None, "LoggerManager did not initialize a coder logger."
+        assert logger_manager.researcher is not None, "LoggerManager did not initialize a researcher logger."
+        assert logger_manager.planner is not None, "LoggerManager did not initialize a planner logger."
+        assert logger_manager.executor is not None, "LoggerManager did not initialize an executor logger."
+        assert logger_manager.sirji is not None, "LoggerManager did not initialize a sirji logger."
+        assert logger_manager.user is not None, "LoggerManager did not initialize a user logger."
+
+    @patch('sirji_tools.logger.logger.logging')
+    def test_logger_manager_logging(self, mock_logging):
+        # Setup
+        mock_logger = MagicMock()
+        mock_logging.getLogger.return_value = mock_logger
+
+        # Test logging through LoggerManager
+        logger_manager = LoggerManager()
+        logger_manager.coder.info("Test message for coder")
+        logger_manager.researcher.info("Test message for researcher")
+        logger_manager.planner.info("Test message for planner")
+        logger_manager.executor.info("Test message for executor")
+        logger_manager.sirji.info("Test message for sirji")
+        logger_manager.user.info("Test message for user")
+
+        # Verify
+        mock_logger.info.assert_any_call("Test message for coder"), "LoggerManager's coder did not log message correctly."
+        mock_logger.info.assert_any_call("Test message for researcher"), "LoggerManager's researcher did not log message correctly."
+        mock_logger.info.assert_any_call("Test message for planner"), "LoggerManager's planner did not log message correctly."
+        mock_logger.info.assert_any_call("Test message for executor"), "LoggerManager's executor did not log message correctly."
+        mock_logger.info.assert_any_call("Test message for sirji"), "LoggerManager's sirji did not log message correctly."
+        mock_logger.info.assert_any_call("Test message for user"), "LoggerManager's user did not log message correctly."
\ No newline at end of file
diff --git a/tools/tests/test_pdf_handler.py b/tools/tests/test_pdf_handler.py
new file mode 100644
index 00000000..c8ffc39d
--- /dev/null
+++ b/tools/tests/test_pdf_handler.py
@@ -0,0 +1,51 @@
+import pytest
+from unittest.mock import patch, MagicMock
+from sirji_tools.crawler.pdf_handler import PDFHandler
+
+class TestPDFHandler:
+    @patch('sirji_tools.crawler.pdf_handler.requests.get')
+    @patch('sirji_tools.crawler.pdf_handler.PyPDF2.PdfReader')
+    def test_handle_pdf_success(self, mock_pdf_reader, mock_get, tmp_path):
+        # Setup
+        # Mocking the response of requests.get to simulate downloading a PDF
+        mock_response = MagicMock()
+        mock_response.content = b'PDF content'
+        mock_get.return_value = mock_response
+
+        # Mocking PyPDF2 to simulate reading a PDF file
+        mock_pdf_instance = MagicMock()
+        mock_pdf_instance.pages = [MagicMock(extract_text=MagicMock(return_value="Page text"))]
+        mock_pdf_reader.return_value = mock_pdf_instance
+
+        # Test data
+        url = 'https://example.com/test.pdf'
+        output_dir = tmp_path / "test_output"
+        output_dir.mkdir()  # Ensure the directory exists
+
+        # Execute the function under test
+        handler = PDFHandler()
+        handler.handle(url, str(output_dir))  # Convert Path object to string if necessary
+
+        # Assertions
+        mock_get.assert_called_once_with(url)
+        mock_pdf_reader.assert_called_once()
+        # Additional assertions can be added here to verify the content was saved correctly
+
+    @patch('sirji_tools.crawler.pdf_handler.requests.get')
+    def test_handle_pdf_network_failure(self, mock_get, tmp_path):
+        # Setup
+        # Simulating a network failure during PDF download
+        mock_get.side_effect = Exception("Network failure")
+
+        # Test data
+        url = 'https://example.com/failing_url.pdf'
+        output_dir = tmp_path / "test_output"
+        output_dir.mkdir()  # Ensure the directory exists
+
+        # Execute the function under test
+        handler = PDFHandler()
+
+        # Assertions
+        with pytest.raises(Exception) as exc_info:
+            handler.handle(url, str(output_dir))  # Convert Path object to string if necessary
+        assert str(exc_info.value) == "Network failure", "PDFHandler did not handle network failure as expected."
\ No newline at end of file
diff --git a/tools/tests/test_search.py b/tools/tests/test_search.py
new file mode 100644
index 00000000..e605cf16
--- /dev/null
+++ b/tools/tests/test_search.py
@@ -0,0 +1,37 @@
+import pytest
+from unittest.mock import patch, MagicMock  # Updated import here
+from sirji_tools.search import search_for
+
+@patch('sirji_tools.search.search.sync_playwright')
+def test_search_for_returns_filtered_urls(mock_playwright):
+    # Setup the mock environment
+    mock_browser_context = mock_playwright.return_value.__enter__.return_value.chromium.launch.return_value.new_context.return_value
+    mock_browser_page = mock_browser_context.new_page.return_value
+    
+    # Mocking the navigation to Google's search page
+    mock_browser_page.goto.return_value = None
+    
+    # Mocking the filling and pressing enter on the search input
+    mock_browser_page.fill.return_value = None
+    mock_browser_page.press.return_value = None
+    
+    # Simulate a delay for loading search results
+    # skipping the time.sleep through mock
+    
+    # Mocking the query_selector_all method to return mock links as search results
+    mock_browser_page.query_selector_all.return_value = [
+        MagicMock(get_attribute=MagicMock(return_value='https://www.google.com/url?url=https://www.example.com')),
+        MagicMock(get_attribute=MagicMock(return_value='https://www.google.com/url?url=https://www.maps.google.com')),
+        MagicMock(get_attribute=MagicMock(return_value='https://www.google.com/url?url=https://www.example2.com')),
+        # Add more mock results as needed
+    ]
+    
+    expected_urls = ['https://www.example.com', 'https://www.example2.com']  # Expected output after filtering excluded domains and duplicates
+    actual_urls = search_for('test query')
+    
+    # Assertions
+    assert len(actual_urls) <= 10, "The function returned more than 10 URLs."
+    assert set(expected_urls).issubset(set(actual_urls)), "The returned URLs are not as expected after filtering."
+    mock_browser_page.goto.assert_called_once_with('https://www.google.com')
+    mock_browser_page.fill.assert_called_once_with('input[name=q]', 'test query')
+    mock_browser_page.press.assert_called_once_with('input[name=q]', 'Enter')
\ No newline at end of file