#
tokens: 46293/50000 41/41 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── .github
│   ├── ISSUE_TEMPLATE
│   │   ├── bug_report.md
│   │   └── feature_request.md
│   └── workflows
│       ├── PR-verify.yml
│       └── sonar-branch-analysis.yml
├── .gitignore
├── .npmrc
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── demo
│   └── demo.gif
├── glama.json
├── jest.config.js
├── LICENSE
├── LICENSES
│   ├── Apache-2.0.txt
│   └── CC-BY-SA-4.0.txt
├── package-lock.json
├── package.json
├── README.md
├── repolinter.json
├── REUSE.toml
├── scripts
│   └── install_dependencies.sh
├── SECURITY.md
├── src
│   ├── adapters
│   │   ├── OrchestratorToIDB.ts
│   │   └── ParserToOrchestrator.ts
│   ├── idb
│   │   ├── IDBManager.ts
│   │   └── interfaces
│   │       └── IIDBManager.ts
│   ├── index.ts
│   ├── mcp
│   │   └── mcp-server.ts
│   ├── orchestrator
│   │   ├── __tests__
│   │   │   └── MCPOrchestrator.test.ts
│   │   ├── interfaces
│   │   │   └── IOrchestratorCommand.ts
│   │   └── MCPOrchestrator.ts
│   └── parser
│       ├── __tests__
│       │   └── NLParser.test.ts
│       ├── commands
│       │   ├── AccessibilityCommands.ts
│       │   ├── AppCommands.ts
│       │   ├── BaseCommandDefinition.ts
│       │   ├── CaptureCommands.ts
│       │   ├── CommandRegistry.ts
│       │   ├── DebugCommands.ts
│       │   ├── MiscCommands.ts
│       │   ├── SimulatorCommands.ts
│       │   └── UICommands.ts
│       ├── interfaces
│       │   └── IParser.ts
│       └── NLParser.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------

```
registry=https://registry.npmjs.org/

```

--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------

```
# Dependencies
node_modules/
venv/

# Build output
dist/
build/

# Logs
logs/
*.log
npm-debug.log*

# Environment variables
.env
.env.local
.env.*.local

# IDEs and editors
.idea/
.vscode/
*.swp
*.swo

# System files
.DS_Store
Thumbs.db

# Python
__pycache__/
*.py[cod]
*$py.class

```

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown
# 📱 MCP Server for iOS Simulator

[![MCP Server](https://glama.ai/mcp/servers/@InditexTech/mcp-server-simulator-ios-idb/badge)](https://glama.ai/mcp/servers/@InditexTech/mcp-server-simulator-ios-idb)

A Model Context Protocol (MCP) server that enables LLMs to interact with iOS simulators through natural language commands.

## ℹ️ Overview

This MCP server provides a bridge between Large Language Models (LLMs) and iOS simulators, offering comprehensive control through natural language commands. Here's what it can do:

For detailed usage, see the Installation guide and Supported Commands sections. You can use this server either through direct MCP integration or as a standalone library.

Check out the Architecture section to understand how the components work together to enable natural language control of iOS simulators.

![demo](demo/demo.gif)

### 🎮 Simulator Control
- Create and manage simulator sessions
- Boot, shutdown, and monitor simulator states
- List available and running simulators
- Focus simulator windows

### 📱 Application Management
- Install and manage iOS applications
- Launch, terminate, and uninstall apps
- Monitor app states and verify installations
- Handle app permissions and configurations

### 🖱️ UI Interaction & Testing
- Interact with the simulator UI
- Execute tap, swipe, and button press actions
- Input text and key sequences
- Access accessibility elements for UI testing
- Record videos of UI interactions

### 🛠️ Development & Debugging
- Capture screenshots and system logs
- Debug applications in real-time
- Monitor and analyze crash logs
- Install dynamic libraries and manage app data

### ⚡ Advanced Features
- Additional functionality includes:
  - Location simulation
  - Media injection
  - URL scheme handling
  - Contact database management
  - Keychain operations

For detailed usage, see the Installation guide and Supported Commands sections. You can use this server either through direct MCP integration or as a standalone library.

Check out the Architecture section to understand how the components work together to enable natural language control of iOS simulators.

## 📋 Requirements

- **macOS**: Required for iOS simulator support
- **Node.js**: v14.0.0 or higher
- **Homebrew**: Required for installing dependencies
- **XCode**: With iOS simulators installed

## 🚀 Installation

The easiest way to install this server is through Cline:

1. Simply ask Cline:
```
Add this mcp to cline https://github.com/InditexTech/mcp-server-simulator-ios-idb
```

2. Cline will handle the installation process automatically, including dependency management and configuration.

Alternatively, you can install it manually:

```bash
# Clone the repository
git clone https://github.com/InditexTech/mcp-server-simulator-ios-idb.git
cd mcp-server-simulator-ios-idb

# Create and activate Python virtual environment
python3 -m venv venv
source venv/bin/activate  # On Unix/macOS

# Install dependencies
npm install

# Build the project
npm run build

# Start the project
npm start

# Run tests
npm test
```

The installation process will automatically:
1. Check if you're running macOS
2. Install idb-companion via Homebrew
3. Install fb-idb via pip in the virtual environment

Note: Make sure to keep the virtual environment activated while using the server. If you close your terminal and come back later, you'll need to reactivate the virtual environment with the `source venv/bin/activate` command before running `npm start`.

## 🔌 MCP Integration

To use this server with Claude or other LLM assistants:

1. Add the server to your MCP settings in Claude Desktop:

```json
{
  "mcpServers": {
    "ios-simulator": {
      "command": "node",
      "args": ["/path/to/mcp-server-simulator-ios-idb/dist/index.js"],
      "env": {}
    }
  }
}
```

2. The LLM can now use natural language commands to control iOS simulators:

```
create a simulator session with iPhone 14
install app /path/to/my-app.ipa
launch app com.example.myapp
tap at 100, 200
take a screenshot
```

## 📚 Usage as a Library

You can also use this package as a library in your own projects:

### 🔰 Basic Usage

```typescript
import { createMCPServer } from 'mcp-server-simulator-ios-idb';

async function main() {
  // Create an instance of the MCP server
  const { orchestrator } = createMCPServer();
  
  // Process natural language commands
  
  // Create a simulator session
  const sessionResult = await orchestrator.processInstruction('create session');
  console.log(`Session created: ${sessionResult.data}`);
  
  // Interact with the simulator
  await orchestrator.processInstruction('tap at 100, 200');
  
  // Capture a screenshot
  const screenshotResult = await orchestrator.processInstruction('take screenshot');
  console.log(`Screenshot saved at: ${screenshotResult.data}`);
}

main().catch(console.error);
```

### 🚀 Advanced Usage

You can also use the individual components directly:

```typescript
import { 
  IDBManager, 
  NLParser, 
  MCPOrchestrator,
  ParserToOrchestrator,
  OrchestratorToIDB
} from 'mcp-server-simulator-ios-idb';

// Create instances
const idbManager = new IDBManager();
const parser = new NLParser();
const orchestrator = new MCPOrchestrator(parser, idbManager);

// Use the components directly
const sessionId = await idbManager.createSimulatorSession({
  deviceName: 'iPhone 12',
  platformVersion: '15.0'
});

await idbManager.tap(sessionId, 100, 200);
```

## 🏗️ Project Structure

```
mcp-server-simulator-ios-idb/
├── src/                      # Source code
│   ├── adapters/             # Adapter components
│   ├── idb/                  # IDB manager implementation
│   ├── mcp/                  # MCP server implementation
│   ├── orchestrator/         # Command orchestrator
│   ├── parser/              # Natural language parser
│   └── index.ts             # Main entry point
├── types/                   # TypeScript type definitions
├── scripts/                 # Installation scripts
├── package.json            # Project configuration
└── tsconfig.json          # TypeScript configuration
```

## 🎯 Supported Commands

The NLParser supports the following natural language commands:

### 🎮 Simulator Management
| Command | Description | Example |
|---------|-------------|---------|
| Create session | Creates a new simulator session | "create session", "create simulator iPhone 12" |
| Terminate session | Terminates the current session | "terminate session", "close simulator" |
| List simulators | Lists available simulators | "list simulators", "show simulators" |
| List booted simulators | Lists running simulators | "list booted simulators", "show running simulators" |
| Boot simulator | Boots a simulator by UDID | "boot simulator 5A321B8F-4D85-4267-9F79-2F5C91D142C2" |
| Shutdown simulator | Shuts down a simulator | "shutdown simulator 5A321B8F-4D85-4267-9F79-2F5C91D142C2" |
| Focus simulator | Brings simulator window to front | "focus simulator", "bring simulator to front" |
| List simulator sessions | Lists active simulator sessions | "list simulator sessions", "show active sessions" |

### 📱 App Management
| Command | Description | Example |
|---------|-------------|---------|
| Install app | Installs an app on the simulator | "install app /path/to/app.ipa" |
| Launch app | Launches an app on the simulator | "launch app com.example.app" |
| Terminate app | Terminates a running app | "terminate app com.example.app" |
| Uninstall app | Uninstalls an app | "uninstall app com.example.app" |
| List apps | Lists installed applications | "list apps", "show installed apps" |
| Check if app installed | Checks if an app is installed | "is app com.example.app installed" |

### 🖱️ UI Interaction
| Command | Description | Example |
|---------|-------------|---------|
| Tap | Taps at specific coordinates | "tap at 100, 200" |
| Swipe | Performs a swipe gesture | "swipe from 100, 200 to 300, 400" |
| Press button | Presses a device button | "press button HOME", "press button SIRI" |
| Input text | Types text | "input text Hello World" |
| Press key | Presses a key by code | "press key 4" |
| Press key sequence | Presses a sequence of keys | "press key sequence 4 5 6" |

### ♿ Accessibility
| Command | Description | Example |
|---------|-------------|---------|
| Describe elements | Lists all accessibility elements | "describe all elements", "show accessibility elements" |
| Describe point | Describes element at coordinates | "describe point 100, 200", "what's at 150, 300" |

### 📸 Capture and Logs
| Command | Description | Example |
|---------|-------------|---------|
| Take screenshot | Captures a screenshot | "take screenshot", "capture screen" |
| Record video | Records screen activity | "record video /path/output.mp4" |
| Stop recording | Stops video recording | "stop recording", "stop video recording" |
| Get logs | Retrieves system or app logs | "get logs", "get logs for com.example.app" |

### 🐛 Debug
| Command | Description | Example |
|---------|-------------|---------|
| Start debug | Starts a debug session | "debug app com.example.app", "start debug com.example.app" |
| Stop debug | Stops a debug session | "stop debug", "terminate debug session" |
| Debug status | Gets debug session status | "debug status", "show debug info" |

### 💥 Crash Logs
| Command | Description | Example |
|---------|-------------|---------|
| List crash logs | Lists available crash logs | "list crash logs", "show crash logs" |
| Show crash log | Shows content of a crash log | "show crash log crash_2023-01-01" |
| Delete crash logs | Deletes crash logs | "delete crash logs", "clear crash logs" |

### 🔧 Additional Commands
| Command | Description | Example |
|---------|-------------|---------|
| Install dylib | Installs a dynamic library | "install dylib /path/to/library.dylib" |
| Open URL | Opens a URL in the simulator | "open url https://example.com" |
| Clear keychain | Clears the simulator's keychain | "clear keychain" |
| Set location | Sets the simulator's location | "set location 37.7749, -122.4194" |
| Add media | Adds media to the camera roll | "add media /path/to/image.jpg" |
| Approve permissions | Approves app permissions | "approve permissions com.example.app photos camera" |
| Update contacts | Updates contacts database | "update contacts /path/to/contacts.sqlite" |

The interface supports all commands available in the idb CLI tool, providing a comprehensive set of operations for iOS simulator automation.

## 🔍 Architecture

The server consists of three main components:

1. **IDBManager**: Low-level component that interacts directly with iOS simulators through idb.
2. **NLParser**: Component that interprets natural language instructions and converts them into structured commands.
3. **MCPOrchestrator**: Central component that coordinates interactions between the parser and the IDBManager.

These components are connected through adapters:
- **ParserToOrchestrator**: Converts parser results into orchestrator commands.
- **OrchestratorToIDB**: Translates orchestrator commands into IDBManager calls.

## 🔌 MCP Integration

To use this server with the Model Context Protocol:

1. Add the server to your MCP settings:

```json
{
  "mcpServers": {
    "ios-simulator": {
      "command": "node",
      "args": ["/path/to/mcp-server-simulator-ios-idb/dist/index.js"],
      "env": {}
    }
  }
}
```

2. Connect to the server in your LLM application:

```typescript
const result = await useMcpTool({
  serverName: "ios-simulator",
  toolName: "process-instruction",
  arguments: {
    instruction: "create simulator session"
  }
});
```

## 🙏 Acknowledgments

This project would not be possible without [facebook/idb](https://github.com/facebook/idb), which provides the underlying iOS simulator control capabilities. We extend our sincere gratitude to the Facebook/Meta team and all contributors to the idb project for creating and maintaining such a powerful and reliable tool.

## 📄 License

This tool is available as open source under the terms of the Apache-2.0.

```

--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------

```markdown
<!--
SPDX-FileCopyrightText: 2025 Industria de Diseño Textil S.A. INDITEX

SPDX-License-Identifier: CC-BY-SA-4.0
-->

# Security

We at Inditex believe that responsible disclosure of security vulnerabilities helps us ensure the security and privacy of all opensource community.

If you believe you have found a security vulnerability in any Inditex repository that meets Inditex definition of a security vulnerability, please report it to us as described below. We appreciate the hard work maintainers put into fixing vulnerabilities and understand that sometimes more time is required to properly address an issue.

## Reporting security issues

> [!CAUTION]
> Do not file public issues on GitHub for security vulnerabilities

* Let us know by submitting the finding through our [disclosure submission program](https://vdp.inditex.com) as soon as possible, upon discovery of a potential security issue.
* Once we've assessed your report, we will create a GitHub "security advisory", which will allow the reporter and Inditex team to work on the issue in a confidential manner. We will invite you as a collaborator to the advisory and any needed trusted persons.
* That "security advisory" will also allow us to have a temporary private fork, to work on the fix in confidentiality.
* Once a fix is ready, we will include the fix in our next release and mark that release as a security release.
* Details on the issue will be embargoed for 30 days to give users an oppurtunity to upgrade, after which we will coordinate disclosure with the researcher(s).
* If you've contributed the fix, you will be credited for it.

```

--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------

```markdown
<!--
SPDX-FileCopyrightText: 2024 Industria de Diseño Textil S.A. INDITEX

SPDX-License-Identifier: CC-BY-SA-4.0
-->

# Contributing

Thank you for your interest in contributing to this project! We value and appreciate any contributions you can make.
To maintain a collaborative and respectful environment, please consider the following guidelines when contributing to
this project.

## Prerequisites

- Before starting to contribute to the code, you must first sign the [Contributor License Agreement (CLA)](https://github.com/InditexTech/foss/blob/main/documents/ITX_OSS_CLA.pdf).

## How to Contribute

1. Open an issue to discuss and gather feedback on the feature or fix you wish to address.
2. Fork the repository and clone it to your local machine.
3. Create a new branch to work on your contribution: `git checkout -b your-branch-name`.
4. Make the necessary changes in your local branch.
5. Ensure that your code follows the established project style and formatting guidelines.
6. Perform testing to ensure your changes do not introduce errors.
7. Make clear and descriptive commits that explain your changes.
8. Push your branch to the remote repository: `git push origin your-branch-name`.
9. Open a pull request describing your changes and linking the corresponding issue.
10. Await comments and discussions on your pull request. Make any necessary modifications based on the received feedback.
11. Once your pull request is approved, your contribution will be merged into the main branch.

## Contribution Guidelines

- All contributors are expected to follow the project's [code of conduct](CODE_of_CONDUCT.md). Please be respectful and
considerate towards other contributors.
- Before starting work on a new feature or fix, check existing [issues](../../issues) and [pull requests](../../pulls)
to avoid duplications and unnecessary discussions.
- If you wish to work on an existing issue, comment on the issue to inform other contributors that you are working on it.
This will help coordinate efforts and prevent conflicts.
- It is always advisable to discuss and gather feedback from the community before making significant changes to the
project's structure or architecture.
- Ensure a clean and organized commit history. Divide your changes into logical and descriptive commits. We recommend to use the [Conventional Commits Specification](https://www.conventionalcommits.org/en/v1.0.0/)
- Document any new changes or features you add. This will help other contributors and project users understand your work
and its purpose.
- Be sure to link the corresponding issue in your pull request to maintain proper tracking of contributions.
- Remember to add license and copyright information following the [REUSE Specification](https://reuse.software/spec/#copyright-and-licensing-information).

```

--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------

```markdown
<!--
SPDX-FileCopyrightText: 2024 Industria de Diseño Textil S.A. INDITEX

SPDX-License-Identifier: CC-BY-SA-4.0
-->

# Contributor Covenant Code of Conduct

> Note: This Code of Conduct is a baseline for this project and it doesn't overpass
current [Inditex's corporate ethics](https://www.inditex.com/itxcomweb/en/group/our-ethical-commitment).

## Our Pledge

We as members, contributors, and leaders pledge to make participation in our
community a harassment-free experience for everyone, regardless of age, body
size, visible or invisible disability, ethnicity, sex characteristics, gender
identity and expression, level of experience, education, socio-economic status,
nationality, personal appearance, race, caste, color, religion, or sexual
identity and orientation.

We pledge to act and interact in ways that contribute to an open, welcoming,
diverse, inclusive, and healthy community.

## Our Standards

Examples of behavior that contributes to a positive environment for our
community include:

* Demonstrating empathy and kindness toward other people
* Being respectful of differing opinions, viewpoints, and experiences
* Giving and gracefully accepting constructive feedback
* Accepting responsibility and apologizing to those affected by our mistakes,
  and learning from the experience
* Focusing on what is best not just for us as individuals, but for the overall
  community

Examples of unacceptable behavior include:

* The use of sexualized language or imagery, and sexual attention or advances of
  any kind
* Trolling, insulting or derogatory comments, and personal or political attacks
* Public or private harassment
* Publishing others' private information, such as a physical or email address,
  without their explicit permission
* Other conduct which could reasonably be considered inappropriate in a
  professional setting

## Enforcement Responsibilities

Community leaders are responsible for clarifying and enforcing our standards of
acceptable behavior and will take appropriate and fair corrective action in
response to any behavior that they deem inappropriate, threatening, offensive,
or harmful.

Community leaders have the right and responsibility to remove, edit, or reject
comments, commits, code, wiki edits, issues, and other contributions that are
not aligned to this Code of Conduct, and will communicate reasons for moderation
decisions when appropriate.

## Scope

This Code of Conduct applies within all community spaces, and also applies when
an individual is officially representing the community in public spaces.
Examples of representing our community include using an official email address,
posting via an official social media account, or acting as an appointed
representative at an online or offline event.

## Enforcement

Instances of abusive, harassing, or otherwise unacceptable behavior may be
reported to the community leaders responsible for enforcement at
[[email protected]](mailto:[email protected]).
All complaints will be reviewed and investigated promptly and fairly.

All community leaders are obligated to respect the privacy and security of the
reporter of any incident.

## Enforcement Guidelines

Community leaders will follow these Community Impact Guidelines in determining
the consequences for any action they deem in violation of this Code of Conduct:

### 1. Correction

**Community Impact**: Use of inappropriate language or other behavior deemed
unprofessional or unwelcome in the community.

**Consequence**: A private, written warning from community leaders, providing
clarity around the nature of the violation and an explanation of why the
behavior was inappropriate. A public apology may be requested.

### 2. Warning

**Community Impact**: A violation through a single incident or series of
actions.

**Consequence**: A warning with consequences for continued behavior. No
interaction with the people involved, including unsolicited interaction with
those enforcing the Code of Conduct, for a specified period of time. This
includes avoiding interactions in community spaces as well as external channels
like social media. Violating these terms may lead to a temporary or permanent
ban.

### 3. Temporary Ban

**Community Impact**: A serious violation of community standards, including
sustained inappropriate behavior.

**Consequence**: A temporary ban from any sort of interaction or public
communication with the community for a specified period of time. No public or
private interaction with the people involved, including unsolicited interaction
with those enforcing the Code of Conduct, is allowed during this period.
Violating these terms may lead to a permanent ban.

### 4. Permanent Ban

**Community Impact**: Demonstrating a pattern of violation of community
standards, including sustained inappropriate behavior, harassment of an
individual, or aggression toward or disparagement of classes of individuals.

**Consequence**: A permanent ban from any sort of public interaction within the
community.

## Attribution

This Code of Conduct is adapted from the [Contributor Covenant][homepage],
version 2.1, available at
[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1].

Community Impact Guidelines were inspired by
[Mozilla's code of conduct enforcement ladder][Mozilla CoC].

For answers to common questions about this code of conduct, see the FAQ at
[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at
[https://www.contributor-covenant.org/translations][translations].

[homepage]: https://www.contributor-covenant.org
[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html
[Mozilla CoC]: https://github.com/mozilla/diversity
[FAQ]: https://www.contributor-covenant.org/faq
[translations]: https://www.contributor-covenant.org/translations

```

--------------------------------------------------------------------------------
/glama.json:
--------------------------------------------------------------------------------

```json
{
  "$schema": "https://glama.ai/mcp/schemas/server.json",
  "maintainers": [
    "arturonaredo"
  ]
}

```

--------------------------------------------------------------------------------
/jest.config.js:
--------------------------------------------------------------------------------

```javascript
/** @type {import('ts-jest').JestConfigWithTsJest} */
export default {
  preset: 'ts-jest',
  testEnvironment: 'node',
  extensionsToTreatAsEsm: ['.ts'],
  moduleNameMapper: {
    '^(\\.{1,2}/.*)\\.js$': '$1',
  },
  transform: {
    '^.+\\.tsx?$': ['ts-jest', {
      useESM: true,
    }],
  },
  testMatch: [
    '**/__tests__/**/*.test.ts',
    '!**/*.d.ts'
  ],
};

```

--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/feature_request.md:
--------------------------------------------------------------------------------

```markdown
---
name: Feature request
about: Suggest an idea for this project
title: ''
labels: ''
assignees: ''

---

**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]

**Describe the solution you'd like**
A clear and concise description of what you want to happen.

**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.

**Additional context**
Add any other context or screenshots about the feature request here.

```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
{
  "compilerOptions": {
    "target": "ES2020",
    "module": "NodeNext",
    "moduleResolution": "NodeNext",
    "lib": ["ES2020", "DOM"],
    "declaration": true,
    "outDir": "./dist",
    "rootDir": "./src",
    "strict": true,
    "esModuleInterop": true,
    "skipLibCheck": true,
    "forceConsistentCasingInFileNames": true,
    "resolveJsonModule": true,
    "types": ["node", "jest"],
    "typeRoots": ["./node_modules/@types", "./types"],
    "allowSyntheticDefaultImports": true,
    "noImplicitAny": false
  },
  "ts-node": {
    "esm": true
  },
  "include": [
    "src/**/*.ts",
    "src/**/__tests__/**/*.ts",
    "types/**/*.d.ts"
  ],
  "exclude": [
    "node_modules",
    "dist"
  ]
}

```

--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug_report.md:
--------------------------------------------------------------------------------

```markdown
---
name: Bug report
about: Create a report to help us improve
title: ''
labels: ''
assignees: ''

---

**Describe the bug**
A clear and concise description of what the bug is.

**To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

**Expected behavior**
A clear and concise description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain your problem.

**Desktop (please complete the following information):**
 - OS: [e.g. iOS]
 - Browser [e.g. chrome, safari]
 - Version [e.g. 22]

**Smartphone (please complete the following information):**
 - Device: [e.g. iPhone6]
 - OS: [e.g. iOS8.1]
 - Browser [e.g. stock browser, safari]
 - Version [e.g. 22]

**Additional context**
Add any other context about the problem here.

```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "mcp-server-simulator-ios-idb",
  "version": "1.0.1",
  "description": "Model Context Protocol server for iOS simulator automation via IDB",
  "main": "dist/index.js",
  "type": "module",
  "scripts": {
    "test": "NODE_OPTIONS=--experimental-vm-modules jest",
    "build": "tsc",
    "start": "node dist/index.js",
    "postinstall": "chmod +x ./scripts/install_dependencies.sh && ./scripts/install_dependencies.sh"
  },
  "engines": {
    "node": ">=14.0.0",
    "os": "darwin"
  },
  "author": "arturono[at]inditex[dot]com",
  "license": "Apache-2.0",
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.6.1",
    "uuid": "^9.0.0",
    "zod": "^3.22.4"
  },
  "devDependencies": {
    "@types/jest": "^29.5.14",
    "@types/node": "^20.11.0",
    "@types/uuid": "^9.0.0",
    "jest": "^29.7.0",
    "ts-jest": "^29.2.6",
    "typescript": "^5.0.0"
  }
}

```

--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------

```markdown
# Changelog

All notable changes to this project will be documented in this file.

The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## [1.0.1] - 2025-04-02

### Fixed
- Fixed logs directory creation issue where logs were being created in the root directory instead of the application directory
- Updated both index.ts and mcp-server.ts to use ESM equivalent of __dirname instead of process.cwd()

## [1.0.0] - 2025-03-20

### Added
- Initial release
- Natural Language Parser for IDB commands
- MCP Server implementation
- IDB Manager with complete command support
- Orchestrator for command handling
- Support for all IDB simulator management features
- Extensive test coverage for core components
- Command registry with accessibility, app, capture, debug, misc, simulator and UI commands
- Documentation and examples
- Security policy
- Contribution guidelines
- Code of conduct

```

--------------------------------------------------------------------------------
/scripts/install_dependencies.sh:
--------------------------------------------------------------------------------

```bash
# SPDX-FileCopyrightText: © 2025 Industria de Diseño Textil S.A. INDITEX
# SPDX-License-Identifier: Apache-2.0
#!/bin/bash

# Dependency installation script for MCP
# This script will run during the MCP package installation

# Check that we are on macOS
if [[ "$OSTYPE" != "darwin"* ]]; then
    echo "Error: This package only works on macOS"
    exit 1
fi

# Check if homebrew is installed
if ! command -v brew &> /dev/null; then
    echo "Error: Homebrew is not installed. Please install Homebrew first."
    echo "Visit https://brew.sh for installation instructions"
    exit 1
fi

# Install dependencies using Homebrew
echo "Installing idb-companion..."
brew tap facebook/fb
brew install idb-companion

# Check if we're in a virtual environment
if [[ -z "$VIRTUAL_ENV" ]]; then
    echo "Error: Python virtual environment not activated"
    echo "Please activate your virtual environment first:"
    echo "  source venv/bin/activate  # On Unix/macOS"
    echo "  .\\venv\\Scripts\\activate  # On Windows"
    exit 1
fi

# Install Python dependencies in virtual environment
echo "Installing idb Python client in virtual environment..."
python3 -m pip install --upgrade pip
python3 -m pip install fb-idb

# Verify installation
if python3 -m pip show fb-idb &> /dev/null; then
    echo "✅ Dependencies installed successfully"
else
    echo "❌ Error installing dependencies"
    exit 1
fi

```

--------------------------------------------------------------------------------
/REUSE.toml:
--------------------------------------------------------------------------------

```toml
version = 1
SPDX-PackageName = "mcp-server-simulator-ios-idb"
SPDX-PackageSupplier = "2025 Industria de Diseño Textil S.A."
SPDX-PackageDownloadLocation = "https://github.com/InditexTech/mcp-server-simulator-ios-idb"

# Computer-generated files.
[[annotations]]
path = [
    ".gitignore",
    ".npmrc",
    "package*.json",
    "repolinter.json",
    "tsconfig.json",
    "jest.config.js",
    "glama.json",
]
SPDX-FileCopyrightText = "2025 Industria Textil de Diseño, S.A."
SPDX-License-Identifier = "Apache-2.0"

[[annotations]]
path = ".github/ISSUE_TEMPLATE/**"
SPDX-FileCopyrightText = "2025 Industria Textil de Diseño, S.A."
SPDX-License-Identifier = "Apache-2.0"

[[annotations]]
path = ".github/workflows/**"
SPDX-FileCopyrightText = "2025 Industria Textil de Diseño, S.A."
SPDX-License-Identifier = "Apache-2.0"

[[annotations]]
path = "README.md"
SPDX-FileCopyrightText = "2025 Industria Textil de Diseño, S.A."
SPDX-License-Identifier = "Apache-2.0"

[[annotations]]
path = "CHANGELOG.md"
SPDX-FileCopyrightText = "2025 Industria Textil de Diseño, S.A."
SPDX-License-Identifier = "Apache-2.0"

[[annotations]]
path = "demo/demo.gif"
SPDX-FileCopyrightText = "2025 Industria Textil de Diseño, S.A."
SPDX-License-Identifier = "Apache-2.0"

[[annotations]]
path = "src/**/__tests__/*"
SPDX-FileCopyrightText = "2025 Industria Textil de Diseño, S.A."
SPDX-License-Identifier = "Apache-2.0"

```

--------------------------------------------------------------------------------
/src/parser/commands/BaseCommandDefinition.ts:
--------------------------------------------------------------------------------

```typescript
// SPDX-FileCopyrightText: © 2025 Industria de Diseño Textil S.A. INDITEX
// SPDX-License-Identifier: Apache-2.0

import { ParseResult } from '../interfaces/IParser.js';

export interface CommandDefinition {
  command: string;
  patterns: RegExp[];
  description: string;
  requiredParameters: string[];
  optionalParameters: string[];
  examples: string[];
  parameterExtractors: Record<string, (match: RegExpMatchArray) => any>;
}

export abstract class BaseCommandDefinition {
  protected abstract definitions: CommandDefinition[];

  getDefinitions(): CommandDefinition[] {
    return this.definitions;
  }

  parseCommand(text: string): ParseResult | null {
    const normalizedText = text.trim().toLowerCase();
    
    for (const definition of this.definitions) {
      for (const pattern of definition.patterns) {
        const match = normalizedText.match(pattern);
        if (match) {
          const parameters: Record<string, any> = {};
          for (const [paramName, extractor] of Object.entries(definition.parameterExtractors)) {
            const value = extractor(match);
            if (value !== undefined) {
              parameters[paramName] = value;
            }
          }
          
          return {
            command: definition.command,
            parameters,
            confidence: 0.9,
            originalText: text
          };
        }
      }
    }
    
    return null;
  }
}

```

--------------------------------------------------------------------------------
/src/parser/commands/AccessibilityCommands.ts:
--------------------------------------------------------------------------------

```typescript
// SPDX-FileCopyrightText: © 2025 Industria de Diseño Textil S.A. INDITEX
// SPDX-License-Identifier: Apache-2.0

import { BaseCommandDefinition, CommandDefinition } from './BaseCommandDefinition.js';

export class AccessibilityCommands extends BaseCommandDefinition {
  protected definitions: CommandDefinition[] = [
    {
      command: 'describe elements',
      patterns: [
        /describir\s+(todos\s+los\s+)?elementos/i,
        /describe\s+all\s+elements/i,
        /mostrar\s+elementos\s+de\s+accesibilidad/i
      ],
      description: 'Describes all accessibility elements on the screen',
      requiredParameters: [],
      optionalParameters: ['sessionId'],
      examples: [
        'describir todos los elementos',
        'describe all elements',
        'mostrar elementos de accesibilidad'
      ],
      parameterExtractors: {}
    },
    {
      command: 'describe point',
      patterns: [
        /describir\s+punto\s+(?<x>\d+)\s*,\s*(?<y>\d+)/i,
        /describe\s+point\s+(?<x>\d+)\s*,\s*(?<y>\d+)/i,
        /qué\s+hay\s+en\s+(?<x>\d+)\s*,\s*(?<y>\d+)/i
      ],
      description: 'Describes the accessibility element at a specific point',
      requiredParameters: ['x', 'y'],
      optionalParameters: ['sessionId'],
      examples: [
        'describir punto 100, 200',
        'describe point 150, 300',
        'qué hay en 200, 400'
      ],
      parameterExtractors: {
        x: (match) => parseInt(match.groups?.x || '0', 10),
        y: (match) => parseInt(match.groups?.y || '0', 10)
      }
    }
  ];
}

```

--------------------------------------------------------------------------------
/.github/workflows/sonar-branch-analysis.yml:
--------------------------------------------------------------------------------

```yaml
name: sonar-branch-analysis

on:
  pull_request:
    types: [closed]
    branches: ["main"]

env:
  NODE_VERSION: '20.x'
  PYTHON_VERSION: '3.11'

jobs:
  verify:
    if: github.event.pull_request.merged == true
    name: Verify
    runs-on: macos-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0

      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: ${{ env.PYTHON_VERSION }}

      - name: Create and activate Python virtual environment
        run: |
          python -m venv venv
          source venv/bin/activate
          python -m pip install --upgrade pip
          echo "VIRTUAL_ENV=$VIRTUAL_ENV" >> $GITHUB_ENV
          echo "PATH=$VIRTUAL_ENV/bin:$PATH" >> $GITHUB_ENV

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'

      - name: Install dependencies
        run: |
          source venv/bin/activate
          npm ci

      - name: Build
        run: npm run build

      - name: Run tests
        run: npm test

      - name: SonarCloud Scan
        uses: sonarsource/sonarcloud-github-action@master
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        with:
          args: >
            -Dsonar.branch.name=main
            -Dsonar.projectKey=mcp-server-simulator-ios-idb
            -Dsonar.organization=com.inditex
            -Dsonar.sources=src
            -Dsonar.tests=src/**/__tests__
            -Dsonar.typescript.lcov.reportPaths=coverage/lcov.info
            -Dsonar.javascript.lcov.reportPaths=coverage/lcov.info

```

--------------------------------------------------------------------------------
/src/parser/interfaces/IParser.ts:
--------------------------------------------------------------------------------

```typescript
// SPDX-FileCopyrightText: © 2025 Industria de Diseño Textil S.A. INDITEX
// SPDX-License-Identifier: Apache-2.0

/**
 * IParser - Interface for natural language parser
 * 
 * This interface defines the necessary methods to interpret natural
 * language instructions and convert them into structured commands.
 */

export interface ParseResult {
  command: string;
  parameters: Record<string, any>;
  confidence: number;
  originalText: string;
}

export interface ValidationResult {
  isValid: boolean;
  missingParameters?: string[];
  invalidParameters?: Record<string, string>;
  suggestions?: string[];
  errorMessage?: string;
}

export interface IParser {
  /**
   * Parses a natural language instruction into a command structure
   * @param text Natural language instruction text
   * @returns Parsing result with extracted command and parameters
   */
  parseInstruction(text: string): Promise<ParseResult>;

  /**
   * Validates if an instruction has all required parameters
   * @param parseResult Parsing result to validate
   * @returns Validation result
   */
  validateInstruction(parseResult: ParseResult): Promise<ValidationResult>;

  /**
   * Normalizes parameters of a parsed instruction
   * @param parseResult Parsing result to normalize
   * @returns Parsing result with normalized parameters
   */
  normalizeParameters(parseResult: ParseResult): Promise<ParseResult>;

  /**
   * Generates suggestions to complete an incomplete instruction
   * @param partialText Partial instruction text
   * @returns List of suggestions to complete the instruction
   */
  suggestCompletions(partialText: string): Promise<string[]>;

  /**
   * Gets the list of commands supported by the parser
   * @returns List of supported commands with their descriptions
   */
  getSupportedCommands(): Promise<Array<{
    command: string;
    description: string;
    requiredParameters: string[];
    optionalParameters: string[];
  }>>;
}

```

--------------------------------------------------------------------------------
/src/parser/commands/CommandRegistry.ts:
--------------------------------------------------------------------------------

```typescript
// SPDX-FileCopyrightText: © 2025 Industria de Diseño Textil S.A. INDITEX
// SPDX-License-Identifier: Apache-2.0

import { ParseResult } from '../interfaces/IParser.js';
import { BaseCommandDefinition } from './BaseCommandDefinition.js';

export class CommandRegistry {
  private commandHandlers: BaseCommandDefinition[] = [];

  registerHandler(handler: BaseCommandDefinition) {
    this.commandHandlers.push(handler);
  }

  parseInstruction(text: string): ParseResult {
    for (const handler of this.commandHandlers) {
      const result = handler.parseCommand(text);
      if (result) {
        return result;
      }
    }
    
    throw new Error(`Could not understand the instruction: ${text}`);
  }

  async getSupportedCommands(): Promise<Array<{
    command: string;
    description: string;
    requiredParameters: string[];
    optionalParameters: string[];
  }>> {
    return this.commandHandlers
      .flatMap(handler => handler.getDefinitions())
      .map(definition => ({
        command: definition.command,
        description: definition.description,
        requiredParameters: definition.requiredParameters,
        optionalParameters: definition.optionalParameters
      }));
  }

  async suggestCompletions(partialText: string): Promise<string[]> {
    const normalizedPartial = partialText.trim().toLowerCase();
    
    if (!normalizedPartial) {
      return [
        'create session',
        'list simulators',
        'install app',
        'launch app',
        'terminate session'
      ];
    }
    
    const suggestions = new Set<string>();
    
    for (const handler of this.commandHandlers) {
      for (const definition of handler.getDefinitions()) {
        if (definition.command.toLowerCase().includes(normalizedPartial)) {
          suggestions.add(definition.command);
        }
        
        for (const example of definition.examples) {
          if (example.toLowerCase().includes(normalizedPartial)) {
            suggestions.add(example);
          }
        }
      }
    }
    
    return Array.from(suggestions).slice(0, 5);
  }
}

```

--------------------------------------------------------------------------------
/.github/workflows/PR-verify.yml:
--------------------------------------------------------------------------------

```yaml
name: PR-verify

on:
  pull_request:

env:
  NODE_VERSION: '20.x'
  PYTHON_VERSION: '3.11'

jobs:
  verify:
    name: Verify
    runs-on: macos-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          ref: ${{ github.event.pull_request.head.sha }}

      - name: Setup Python
        uses: actions/setup-python@v5
        with:
          python-version: ${{ env.PYTHON_VERSION }}

      - name: Create and activate Python virtual environment
        run: |
          python -m venv venv
          source venv/bin/activate
          python -m pip install --upgrade pip
          echo "VIRTUAL_ENV=$VIRTUAL_ENV" >> $GITHUB_ENV
          echo "PATH=$VIRTUAL_ENV/bin:$PATH" >> $GITHUB_ENV

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}
          cache: 'npm'

      - name: Install dependencies
        run: |
          source venv/bin/activate
          npm ci

      - name: Build
        run: npm run build

      - name: Run tests
        run: npm test

      - name: SonarCloud Scan
        if: ${{ vars.IS_INDITEXTECH_REPO == 'true' }}
        uses: sonarsource/sonarcloud-github-action@master
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
          SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
        with:
          args: >
            -Dsonar.projectKey=mcp-server-simulator-ios-idb
            -Dsonar.organization=com.inditex
            -Dsonar.sources=src
            -Dsonar.tests=src/**/__tests__
            -Dsonar.typescript.lcov.reportPaths=coverage/lcov.info
            -Dsonar.javascript.lcov.reportPaths=coverage/lcov.info

  repo-linter:
    name: Repo Linter
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          ref: ${{ github.event.pull_request.head.sha }}

      - name: Setup Node.js
        uses: actions/setup-node@v4
        with:
          node-version: ${{ env.NODE_VERSION }}

      - name: Execute repolinter
        run: |
          npm install -g [email protected]
          repolinter lint .

  reuse-compliance:
    name: REUSE Compliance
    runs-on: ubuntu-latest
    steps:
      - name: Checkout
        uses: actions/checkout@v4
        with:
          fetch-depth: 0
          ref: ${{ github.event.pull_request.head.sha }}

      - name: REUSE Compliance Check
        uses: fsfe/reuse-action@v5

```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env node
// SPDX-FileCopyrightText: © 2025 Industria de Diseño Textil S.A. INDITEX
// SPDX-License-Identifier: Apache-2.0

// IMMEDIATELY disable all console output to avoid interfering with stdio transport
// This must come before any other imports to ensure nothing writes to stdout
console.log = () => {}; 
console.info = () => {};
console.warn = () => {};
console.error = (msg) => {
  // Only log errors to stderr, not stdout
  process.stderr.write(`${msg}\n`);
};
console.debug = () => {};

import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import mcpServer from './mcp/mcp-server.js';

/**
 * MCP Server for iOS simulator
 * 
 * This file exports all the components needed to use the MCP Server
 * for iOS simulator automation through idb.
 * 
 * It also serves as the entry point for starting the MCP server.
 */

// Export interfaces
export { IIDBManager, SimulatorInfo, AppInfo, SessionConfig } from './idb/interfaces/IIDBManager.js';
export { IParser, ParseResult, ValidationResult } from './parser/interfaces/IParser.js';
export { 
  IOrchestratorCommand,
  CommandType,
  CommandResult,
  CommandContext,
  SequenceCommand,
  ConditionalCommand,
  CommandFactory
} from './orchestrator/interfaces/IOrchestratorCommand.js';

// Export implementations
export { IDBManager } from './idb/IDBManager.js';
export { NLParser } from './parser/NLParser.js';
export { MCPOrchestrator } from './orchestrator/MCPOrchestrator.js';

// Export adapters
export { ParserToOrchestrator } from './adapters/ParserToOrchestrator.js';
export { OrchestratorToIDB } from './adapters/OrchestratorToIDB.js';

/**
 * Main entry point for the MCP server
 * 
 * This code runs when the server is started directly.
 * It sets up the environment and launches the MCP server.
 */

// Create logs directory with error handling
try {
  // Get the directory name using ESM approach
  const __filename = fileURLToPath(import.meta.url);
  const __dirname = path.dirname(__filename);
  // Go up one level from the src directory to the project root
  const logsDir = path.join(__dirname, '..', 'logs');
  
  if (!fs.existsSync(logsDir)) {
    fs.mkdirSync(logsDir, { recursive: true });
  }
} catch (err) {
  process.stderr.write(`Error creating logs directory: ${err}\n`);
}

async function start() {
  try {
    // Start MCP server
    await mcpServer.start();
  } catch (error) {
    process.stderr.write(`Error starting MCP server: ${error}\n`);
    process.exit(1);
  }
}

// Handle termination signals
process.on('SIGINT', () => {
  process.exit(0);
});

process.on('SIGTERM', () => {
  process.exit(0);
});

// Handle uncaught exceptions
process.on('uncaughtException', (error) => {
  process.stderr.write(`Uncaught exception: ${error.stack}\n`);
  process.exit(1);
});

process.on('unhandledRejection', (reason) => {
  process.stderr.write(`Unhandled rejection: ${reason}\n`);
  process.exit(1);
});

// Start the server
start();

```

--------------------------------------------------------------------------------
/src/orchestrator/interfaces/IOrchestratorCommand.ts:
--------------------------------------------------------------------------------

```typescript
// SPDX-FileCopyrightText: © 2025 Industria de Diseño Textil S.A. INDITEX
// SPDX-License-Identifier: Apache-2.0

/**
 * IOrchestratorCommand - Interface for orchestrator commands
 * 
 * This interface defines the structure of commands that the orchestrator
 * can handle and execute through the IDBManager.
 */

export enum CommandType {
  // Simulator management commands
  CREATE_SIMULATOR_SESSION = 'createSimulatorSession',
  TERMINATE_SIMULATOR_SESSION = 'terminateSimulatorSession',
  LIST_AVAILABLE_SIMULATORS = 'listAvailableSimulators',
  LIST_BOOTED_SIMULATORS = 'listBootedSimulators',
  BOOT_SIMULATOR = 'bootSimulator',
  SHUTDOWN_SIMULATOR = 'shutdownSimulator',
  
  // Application management commands
  INSTALL_APP = 'installApp',
  LAUNCH_APP = 'launchApp',
  TERMINATE_APP = 'terminateApp',
  
  // UI interaction commands
  TAP = 'tap',
  SWIPE = 'swipe',
  
  // Capture and logging commands
  TAKE_SCREENSHOT = 'takeScreenshot',
  GET_SYSTEM_LOGS = 'getSystemLogs',
  GET_APP_LOGS = 'getAppLogs',
  
  // Verification commands
  IS_SIMULATOR_BOOTED = 'isSimulatorBooted',
  IS_APP_INSTALLED = 'isAppInstalled',
  
  // Composite commands
  SEQUENCE = 'sequence',
  CONDITIONAL = 'conditional'
}

export interface CommandResult {
  success: boolean;
  data?: any;
  error?: string;
  timestamp: number;
}

export interface CommandContext {
  sessionId?: string;
  previousResults?: Record<string, CommandResult>;
  variables?: Record<string, any>;
}

export interface IOrchestratorCommand {
  /**
   * Command type
   */
  type: CommandType;
  
  /**
   * Command-specific parameters
   */
  parameters: Record<string, any>;
  
  /**
   * Unique command ID
   */
  id: string;
  
  /**
   * Human-readable command description
   */
  description?: string;
  
  /**
   * Maximum execution time in milliseconds
   */
  timeout?: number;
  
  /**
   * Number of retries in case of failure
   */
  retries?: number;
  
  /**
   * Parameter validation function
   */
  validate?: (context: CommandContext) => Promise<boolean>;
  
  /**
   * Parameter transformation function before execution
   */
  transformParameters?: (context: CommandContext) => Promise<Record<string, any>>;
  
  /**
   * Custom error handling function
   */
  onError?: (error: Error, context: CommandContext) => Promise<CommandResult>;
}

/**
 * Sequence command that executes multiple commands in order
 */
export interface SequenceCommand extends IOrchestratorCommand {
  type: CommandType.SEQUENCE;
  parameters: {
    commands: IOrchestratorCommand[];
    stopOnError?: boolean;
  };
}

/**
 * Conditional command that executes one command or another based on a condition
 */
export interface ConditionalCommand extends IOrchestratorCommand {
  type: CommandType.CONDITIONAL;
  parameters: {
    condition: (context: CommandContext) => Promise<boolean>;
    ifTrue: IOrchestratorCommand;
    ifFalse?: IOrchestratorCommand;
  };
}

/**
 * Command factory to create IOrchestratorCommand instances
 */
export interface CommandFactory {
  createCommand(type: CommandType, parameters: Record<string, any>, description?: string): IOrchestratorCommand;
  createSequence(commands: IOrchestratorCommand[], stopOnError?: boolean): SequenceCommand;
  createConditional(
    condition: (context: CommandContext) => Promise<boolean>,
    ifTrue: IOrchestratorCommand,
    ifFalse?: IOrchestratorCommand
  ): ConditionalCommand;
}

```

--------------------------------------------------------------------------------
/src/parser/commands/CaptureCommands.ts:
--------------------------------------------------------------------------------

```typescript
// SPDX-FileCopyrightText: © 2025 Industria de Diseño Textil S.A. INDITEX
// SPDX-License-Identifier: Apache-2.0

import { BaseCommandDefinition, CommandDefinition } from './BaseCommandDefinition.js';

export class CaptureCommands extends BaseCommandDefinition {
  protected definitions: CommandDefinition[] = [
    {
      command: 'capture screen',
      patterns: [
        /capturar\s+(la\s+)?pantalla(\s+en\s+(?<outputPath>[^\s,]+))?/i,
        /screenshot(\s+en\s+(?<outputPath>[^\s,]+))?/i,
        /tomar\s+(una\s+)?captura(\s+en\s+(?<outputPath>[^\s,]+))?/i,
        /capture\s+(the\s+)?screen(\s+to\s+(?<outputPath>[^\s,]+))?/i,
        /take\s+(a\s+)?screenshot(\s+to\s+(?<outputPath>[^\s,]+))?/i
      ],
      description: 'Captures a screenshot of the simulator',
      requiredParameters: [],
      optionalParameters: ['outputPath', 'sessionId'],
      examples: [
        'capturar pantalla',
        'screenshot en /ruta/captura.png',
        'tomar una captura',
        'capture screen',
        'take screenshot to /path/capture.png'
      ],
      parameterExtractors: {
        outputPath: (match) => match.groups?.outputPath?.trim()
      }
    },
    {
      command: 'record video',
      patterns: [
        /grabar\s+video\s+(?<outputPath>[^\s,]+)/i,
        /record\s+video\s+(?<outputPath>[^\s,]+)/i,
        /iniciar\s+grabación\s+de\s+video\s+(?<outputPath>[^\s,]+)/i,
        /start\s+recording\s+video\s+(?<outputPath>[^\s,]+)/i,
        /begin\s+video\s+recording\s+(?<outputPath>[^\s,]+)/i
      ],
      description: 'Starts video recording of the simulator',
      requiredParameters: ['outputPath'],
      optionalParameters: ['sessionId'],
      examples: [
        'grabar video /ruta/video.mp4',
        'record video /tmp/captura.mp4',
        'iniciar grabación de video /ruta/salida.mp4',
        'record video /path/video.mp4',
        'start recording video /path/output.mp4'
      ],
      parameterExtractors: {
        outputPath: (match) => match.groups?.outputPath?.trim()
      }
    },
    {
      command: 'stop recording',
      patterns: [
        /detener\s+grabación(\s+de\s+video)?/i,
        /parar\s+grabación(\s+de\s+video)?/i,
        /stop\s+recording/i,
        /end\s+recording/i,
        /stop\s+video\s+recording/i
      ],
      description: 'Stops video recording of the simulator',
      requiredParameters: [],
      optionalParameters: ['recordingId', 'sessionId'],
      examples: [
        'detener grabación',
        'parar grabación de video',
        'stop recording',
        'end recording',
        'stop video recording'
      ],
      parameterExtractors: {}
    },
    {
      command: 'get logs',
      patterns: [
        /obtener\s+logs(\s+de\s+(?<bundleId>[^\s,]+))?/i,
        /mostrar\s+logs(\s+de\s+(?<bundleId>[^\s,]+))?/i,
        /get\s+logs(\s+for\s+(?<bundleId>[^\s,]+))?/i,
        /show\s+logs(\s+for\s+(?<bundleId>[^\s,]+))?/i,
        /display\s+logs(\s+for\s+(?<bundleId>[^\s,]+))?/i
      ],
      description: 'Gets system logs or logs from a specific application',
      requiredParameters: [],
      optionalParameters: ['bundleId', 'limit', 'sessionId'],
      examples: [
        'obtener logs',
        'mostrar logs de com.example.app',
        'get logs for com.apple.mobilesafari',
        'show logs',
        'display logs for com.example.app'
      ],
      parameterExtractors: {
        bundleId: (match) => match.groups?.bundleId?.trim()
      }
    }
  ];
}

```

--------------------------------------------------------------------------------
/src/parser/commands/DebugCommands.ts:
--------------------------------------------------------------------------------

```typescript
// SPDX-FileCopyrightText: © 2025 Industria de Diseño Textil S.A. INDITEX
// SPDX-License-Identifier: Apache-2.0

import { BaseCommandDefinition, CommandDefinition } from './BaseCommandDefinition.js';

export class DebugCommands extends BaseCommandDefinition {
  protected definitions: CommandDefinition[] = [
    {
      command: 'start debug',
      patterns: [
        /iniciar\s+debug\s+(?<bundleId>[^\s,]+)/i,
        /start\s+debug\s+(?<bundleId>[^\s,]+)/i,
        /debug\s+app\s+(?<bundleId>[^\s,]+)/i,
        /begin\s+debug\s+(?<bundleId>[^\s,]+)/i,
        /launch\s+debug\s+(?<bundleId>[^\s,]+)/i
      ],
      description: 'Starts a debug session for an application',
      requiredParameters: ['bundleId'],
      optionalParameters: ['sessionId'],
      examples: [
        'iniciar debug com.example.app',
        'start debug com.apple.mobilesafari',
        'debug app com.example.app',
        'begin debug com.example.app',
        'launch debug com.apple.mobilesafari'
      ],
      parameterExtractors: {
        bundleId: (match) => match.groups?.bundleId?.trim()
      }
    },
    {
      command: 'stop debug',
      patterns: [
        /detener\s+debug/i,
        /parar\s+debug/i,
        /stop\s+debug/i,
        /end\s+debug/i,
        /terminate\s+debug/i
      ],
      description: 'Stops a debug session',
      requiredParameters: [],
      optionalParameters: ['sessionId'],
      examples: [
        'detener debug',
        'parar debug',
        'stop debug',
        'end debug',
        'terminate debug'
      ],
      parameterExtractors: {}
    },
    {
      command: 'debug status',
      patterns: [
        /estado\s+debug/i,
        /status\s+debug/i,
        /información\s+debug/i,
        /debug\s+status/i,
        /get\s+debug\s+status/i
      ],
      description: 'Gets the debug session status',
      requiredParameters: [],
      optionalParameters: ['sessionId'],
      examples: [
        'estado debug',
        'status debug',
        'información debug',
        'debug status',
        'get debug status'
      ],
      parameterExtractors: {}
    },
    {
      command: 'list crash logs',
      patterns: [
        /listar\s+crash\s+logs/i,
        /mostrar\s+crash\s+logs/i,
        /list\s+crash\s+logs/i,
        /show\s+crash\s+logs/i,
        /display\s+crash\s+logs/i
      ],
      description: 'Lists available crash logs',
      requiredParameters: [],
      optionalParameters: ['bundleId', 'sessionId'],
      examples: [
        'listar crash logs',
        'mostrar crash logs',
        'list crash logs',
        'show crash logs',
        'display crash logs'
      ],
      parameterExtractors: {}
    },
    {
      command: 'show crash log',
      patterns: [
        /mostrar\s+crash\s+log\s+(?<crashName>[^\s,]+)/i,
        /ver\s+crash\s+log\s+(?<crashName>[^\s,]+)/i,
        /show\s+crash\s+log\s+(?<crashName>[^\s,]+)/i,
        /display\s+crash\s+log\s+(?<crashName>[^\s,]+)/i,
        /view\s+crash\s+log\s+(?<crashName>[^\s,]+)/i
      ],
      description: 'Gets the content of a crash log',
      requiredParameters: ['crashName'],
      optionalParameters: ['sessionId'],
      examples: [
        'mostrar crash log crash_2023-01-01',
        'ver crash log app_crash_123',
        'show crash log system_crash',
        'display crash log error_log_123',
        'view crash log app_crash_456'
      ],
      parameterExtractors: {
        crashName: (match) => match.groups?.crashName?.trim()
      }
    },
    {
      command: 'delete crash logs',
      patterns: [
        /eliminar\s+crash\s+logs/i,
        /borrar\s+crash\s+logs/i,
        /delete\s+crash\s+logs/i,
        /remove\s+crash\s+logs/i,
        /clear\s+crash\s+logs/i
      ],
      description: 'Deletes crash logs',
      requiredParameters: [],
      optionalParameters: ['bundleId', 'sessionId', 'all'],
      examples: [
        'eliminar crash logs',
        'borrar crash logs',
        'delete crash logs',
        'remove crash logs',
        'clear crash logs'
      ],
      parameterExtractors: {}
    }
  ];
}

```

--------------------------------------------------------------------------------
/src/parser/commands/AppCommands.ts:
--------------------------------------------------------------------------------

```typescript
// SPDX-FileCopyrightText: © 2025 Industria de Diseño Textil S.A. INDITEX
// SPDX-License-Identifier: Apache-2.0

import { BaseCommandDefinition, CommandDefinition } from './BaseCommandDefinition.js';

export class AppCommands extends BaseCommandDefinition {
  protected definitions: CommandDefinition[] = [
    {
      command: 'install app',
      patterns: [
        /instalar\s+(la\s+)?app(\s+en\s+la\s+ruta)?\s+(?<appPath>[^\s,]+)/i,
        /instalar\s+(la\s+)?aplicación(\s+en\s+la\s+ruta)?\s+(?<appPath>[^\s,]+)/i,
        /install\s+(the\s+)?app(\s+at)?\s+(?<appPath>[^\s,]+)/i,
        /install\s+(the\s+)?application(\s+at)?\s+(?<appPath>[^\s,]+)/i
      ],
      description: 'Installs an application on the simulator',
      requiredParameters: ['appPath'],
      optionalParameters: ['sessionId'],
      examples: [
        'instalar app /ruta/a/la/app.ipa',
        'instalar la aplicación /ruta/a/la/app.app',
        'install app /path/to/app.ipa',
        'install application /path/to/app.app'
      ],
      parameterExtractors: {
        appPath: (match) => match.groups?.appPath?.trim()
      }
    },
    {
      command: 'launch app',
      patterns: [
        /lanzar\s+(la\s+)?app\s+(?<bundleId>[^\s,]+)/i,
        /abrir\s+(la\s+)?app\s+(?<bundleId>[^\s,]+)/i,
        /iniciar\s+(la\s+)?app\s+(?<bundleId>[^\s,]+)/i,
        /launch\s+(the\s+)?app\s+(?<bundleId>[^\s,]+)/i,
        /open\s+(the\s+)?app\s+(?<bundleId>[^\s,]+)/i,
        /start\s+(the\s+)?app\s+(?<bundleId>[^\s,]+)/i
      ],
      description: 'Launches an application on the simulator',
      requiredParameters: ['bundleId'],
      optionalParameters: ['sessionId'],
      examples: [
        'lanzar app com.example.app',
        'abrir app com.apple.mobilesafari',
        'launch app com.example.app',
        'open app com.apple.mobilesafari'
      ],
      parameterExtractors: {
        bundleId: (match) => match.groups?.bundleId?.trim()
      }
    },
    {
      command: 'terminate app',
      patterns: [
        /terminar\s+(la\s+)?app\s+(?<bundleId>[^\s,]+)/i,
        /cerrar\s+(la\s+)?app\s+(?<bundleId>[^\s,]+)/i,
        /matar\s+(la\s+)?app\s+(?<bundleId>[^\s,]+)/i,
        /terminate\s+(the\s+)?app\s+(?<bundleId>[^\s,]+)/i,
        /close\s+(the\s+)?app\s+(?<bundleId>[^\s,]+)/i,
        /kill\s+(the\s+)?app\s+(?<bundleId>[^\s,]+)/i
      ],
      description: 'Terminates a running application',
      requiredParameters: ['bundleId'],
      optionalParameters: ['sessionId'],
      examples: [
        'terminar app com.example.app',
        'cerrar app com.apple.mobilesafari',
        'matar app com.example.app',
        'terminate app com.example.app',
        'close app com.apple.mobilesafari',
        'kill app com.example.app'
      ],
      parameterExtractors: {
        bundleId: (match) => match.groups?.bundleId?.trim()
      }
    },
    {
      command: 'uninstall app',
      patterns: [
        /desinstalar\s+(la\s+)?app\s+(?<bundleId>[^\s,]+)/i,
        /eliminar\s+(la\s+)?app\s+(?<bundleId>[^\s,]+)/i,
        /borrar\s+(la\s+)?app\s+(?<bundleId>[^\s,]+)/i,
        /uninstall\s+(the\s+)?app\s+(?<bundleId>[^\s,]+)/i,
        /remove\s+(the\s+)?app\s+(?<bundleId>[^\s,]+)/i,
        /delete\s+(the\s+)?app\s+(?<bundleId>[^\s,]+)/i
      ],
      description: 'Uninstalls an application',
      requiredParameters: ['bundleId'],
      optionalParameters: ['sessionId'],
      examples: [
        'desinstalar app com.example.app',
        'eliminar app com.apple.mobilesafari',
        'borrar app com.example.app',
        'uninstall app com.example.app',
        'remove app com.apple.mobilesafari',
        'delete app com.example.app'
      ],
      parameterExtractors: {
        bundleId: (match) => match.groups?.bundleId?.trim()
      }
    },
    {
      command: 'list apps',
      patterns: [
        /listar\s+(las\s+)?apps/i,
        /mostrar\s+(las\s+)?apps/i,
        /qué\s+apps\s+(hay|están\s+instaladas)/i,
        /list\s+(the\s+)?apps/i,
        /show\s+(the\s+)?apps/i,
        /what\s+apps\s+(are\s+there|are\s+installed)/i
      ],
      description: 'Lists installed applications',
      requiredParameters: [],
      optionalParameters: ['sessionId'],
      examples: [
        'listar apps',
        'mostrar apps',
        'qué apps hay',
        'list apps',
        'show apps',
        'what apps are installed'
      ],
      parameterExtractors: {}
    }
  ];
}

```

--------------------------------------------------------------------------------
/src/parser/NLParser.ts:
--------------------------------------------------------------------------------

```typescript
// SPDX-FileCopyrightText: © 2025 Industria de Diseño Textil S.A. INDITEX
// SPDX-License-Identifier: Apache-2.0

import { IParser, ParseResult, ValidationResult } from './interfaces/IParser.js';
import { CommandRegistry } from './commands/CommandRegistry.js';
import { SimulatorCommands } from './commands/SimulatorCommands.js';
import { AppCommands } from './commands/AppCommands.js';
import { UICommands } from './commands/UICommands.js';
import { AccessibilityCommands } from './commands/AccessibilityCommands.js';
import { CaptureCommands } from './commands/CaptureCommands.js';
import { DebugCommands } from './commands/DebugCommands.js';
import { MiscCommands } from './commands/MiscCommands.js';

/**
 * Natural language parser implementation
 */
export class NLParser implements IParser {
  private commandRegistry: CommandRegistry;

  /**
   * Constructor
   */
  constructor() {
    this.commandRegistry = new CommandRegistry();
    
    // Register all command handlers
    this.commandRegistry.registerHandler(new SimulatorCommands());
    this.commandRegistry.registerHandler(new AppCommands());
    this.commandRegistry.registerHandler(new UICommands());
    this.commandRegistry.registerHandler(new AccessibilityCommands());
    this.commandRegistry.registerHandler(new CaptureCommands());
    this.commandRegistry.registerHandler(new DebugCommands());
    this.commandRegistry.registerHandler(new MiscCommands());
  }

  /**
   * Parses a natural language instruction into a command structure
   * @param text Natural language instruction text
   * @returns Parsing result with extracted command and parameters
   */
  async parseInstruction(text: string): Promise<ParseResult> {
    return this.commandRegistry.parseInstruction(text);
  }

  /**
   * Validates if an instruction has all required parameters
   * @param parseResult Parsing result to validate
   * @returns Validation result
   */
  async validateInstruction(parseResult: ParseResult): Promise<ValidationResult> {
    // Get supported command definitions
    const supportedCommands = await this.commandRegistry.getSupportedCommands();
    const definition = supportedCommands.find(cmd => cmd.command === parseResult.command);
    
    if (!definition) {
      return {
        isValid: false,
        errorMessage: `Unrecognized command: ${parseResult.command}`
      };
    }
    
    // Verify required parameters
    const missingParameters = definition.requiredParameters.filter(
      param => !(param in parseResult.parameters)
    );
    
    if (missingParameters.length > 0) {
      return {
        isValid: false,
        missingParameters,
        errorMessage: `Missing required parameters: ${missingParameters.join(', ')}`
      };
    }
    
    // Validate parameter types
    const invalidParameters: Record<string, string> = {};
    for (const [param, value] of Object.entries(parseResult.parameters)) {
      if (typeof value === 'undefined' || value === null) {
        invalidParameters[param] = 'Value cannot be null or undefined';
      }
    }
    
    if (Object.keys(invalidParameters).length > 0) {
      return {
        isValid: false,
        invalidParameters,
        errorMessage: 'Some parameters have invalid values'
      };
    }
    
    return {
      isValid: true
    };
  }

  /**
   * Normalizes parameters of a parsed instruction
   * @param parseResult Parsing result to normalize
   * @returns Parsing result with normalized parameters
   */
  async normalizeParameters(parseResult: ParseResult): Promise<ParseResult> {
    const normalizedParameters = { ...parseResult.parameters };
    
    // Normalize numeric values
    for (const [key, value] of Object.entries(normalizedParameters)) {
      if (typeof value === 'string' && !isNaN(Number(value))) {
        normalizedParameters[key] = Number(value);
      }
    }
    
    // Normalize booleans
    for (const [key, value] of Object.entries(normalizedParameters)) {
      if (typeof value === 'string' && ['true', 'false'].includes(value.toLowerCase())) {
        normalizedParameters[key] = value.toLowerCase() === 'true';
      }
    }
    
    return {
      ...parseResult,
      parameters: normalizedParameters
    };
  }

  /**
   * Generates suggestions to complete an incomplete instruction
   * @param partialText Partial instruction text
   * @returns List of suggestions to complete the instruction
   */
  async suggestCompletions(partialText: string): Promise<string[]> {
    return this.commandRegistry.suggestCompletions(partialText);
  }

  /**
   * Gets the list of commands supported by the parser
   * @returns List of supported commands with their descriptions
   */
  async getSupportedCommands(): Promise<Array<{
    command: string;
    description: string;
    requiredParameters: string[];
    optionalParameters: string[];
  }>> {
    return this.commandRegistry.getSupportedCommands();
  }
}

```

--------------------------------------------------------------------------------
/src/mcp/mcp-server.ts:
--------------------------------------------------------------------------------

```typescript
// SPDX-FileCopyrightText: © 2025 Industria de Diseño Textil S.A. INDITEX
// SPDX-License-Identifier: Apache-2.0

import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import {
  CallToolRequestSchema,
  ErrorCode,
  McpError,
} from '@modelcontextprotocol/sdk/types.js';
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';

// Export implementations
import { IDBManager } from '../idb/IDBManager.js';
import { NLParser } from '../parser/NLParser.js';
import { MCPOrchestrator } from '../orchestrator/MCPOrchestrator.js';

// Log configuration
// Get the directory name using ESM approach
const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);
// Go up two levels from the src/mcp directory to the project root
const logsDir = path.join(__dirname, '..', '..', 'logs');
if (!fs.existsSync(logsDir)) {
  fs.mkdirSync(logsDir, { recursive: true });
}

// Function to write logs to file without using console
const logToFile = (message: string, level: string = 'info') => {
  try {
    const timestamp = new Date().toISOString();
    const logMessage = `${timestamp} [${level.toUpperCase()}] ${message}\n`;
    fs.appendFileSync(path.join(logsDir, 'mcp-server.log'), logMessage);
  } catch (error) {
    // Not much we can do if logging fails
  }
};

/**
 * Create a complete MCP Server instance
 * @returns Object with all necessary instances
 */
export function createMCPServer() {
  // Create instances
  const idbManager = new IDBManager();
  const parser = new NLParser();
  const orchestrator = new MCPOrchestrator(parser, idbManager);
  
  return {
    idbManager,
    parser,
    orchestrator
  };
}

/**
 * MCP Server implementation for iOS simulator
 */
class MCPSimulatorServer {
  private server: Server;
  private orchestrator: ReturnType<typeof createMCPServer>['orchestrator'];

  constructor() {
    // Create component instances
    const { orchestrator } = createMCPServer();
    this.orchestrator = orchestrator;

    // Create MCP server
    this.server = new Server(
      {
        name: 'iOS Simulator MCP Server',
        version: '1.0.1',
      },
      {
        capabilities: {
          resources: {},
          tools: {},
        },
      }
    );

    // Register tools
    this.registerTools();

    // Handle errors
    this.server.onerror = (error) => {
      logToFile(`MCP server error: ${error}`, 'error');
    };

    // Handle termination signals
    process.on('SIGINT', async () => {
      await this.close();
      process.exit(0);
    });

    process.on('SIGTERM', async () => {
      await this.close();
      process.exit(0);
    });
  }

  /**
   * Register MCP server tools
   */
  private registerTools() {
    // Main tool for processing natural language instructions
    this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
      if (request.params.name === 'process-instruction') {
        const instruction = request.params.arguments?.instruction;
        
        if (!instruction || typeof instruction !== 'string') {
          throw new McpError(
            ErrorCode.InvalidParams,
            'Text instruction is required'
          );
        }

        logToFile(`Processing instruction: ${instruction}`);
        
        try {
          const result = await this.orchestrator.processInstruction(instruction);
          
          return {
            content: [
              {
                type: 'text',
                text: JSON.stringify(result),
              },
            ],
          };
        } catch (error) {
          logToFile(`Error processing instruction: ${error}`, 'error');
          
          return {
            content: [
              {
                type: 'text',
                text: `Error: ${error instanceof Error ? error.message : String(error)}`,
              },
            ],
            isError: true,
          };
        }
      }

      throw new McpError(
        ErrorCode.MethodNotFound,
        `Unknown tool: ${request.params.name}`
      );
    });
  }

  /**
   * Start the MCP server with stdio transport
   */
  async start() {
    logToFile('Starting MCP server with stdio transport');
    
    try {
      const transport = new StdioServerTransport();
      await this.server.connect(transport);
      
      logToFile('MCP server started successfully');
    } catch (error) {
      logToFile(`Error starting MCP server: ${error}`, 'error');
      throw error;
    }
  }

  /**
   * Close the MCP server
   */
  async close() {
    logToFile('Closing MCP server');
    
    try {
      await this.server.close();
      logToFile('MCP server closed successfully');
    } catch (error) {
      logToFile(`Error closing MCP server: ${error}`, 'error');
    }
  }
}

// Export a server instance
export default new MCPSimulatorServer();

```

--------------------------------------------------------------------------------
/src/parser/commands/SimulatorCommands.ts:
--------------------------------------------------------------------------------

```typescript
// SPDX-FileCopyrightText: © 2025 Industria de Diseño Textil S.A. INDITEX
// SPDX-License-Identifier: Apache-2.0

import { BaseCommandDefinition, CommandDefinition } from './BaseCommandDefinition.js';

export class SimulatorCommands extends BaseCommandDefinition {
  protected definitions: CommandDefinition[] = [
    {
      command: 'create session',
      patterns: [
        /crear\s+(una\s+)?sesión(\s+de\s+simulador)?(\s+con\s+(?<deviceName>[^,]+))?/i,
        /iniciar\s+(un\s+)?simulador(\s+(?<deviceName>[^,]+))?/i,
        /create\s+(a\s+)?session(\s+with\s+(?<deviceName>[^,]+))?/i,
        /start\s+(a\s+)?simulator(\s+(?<deviceName>[^,]+))?/i,
        /launch\s+(a\s+)?simulator(\s+(?<deviceName>[^,]+))?/i
      ],
      description: 'Creates a new simulator session',
      requiredParameters: [],
      optionalParameters: ['deviceName', 'platformVersion', 'autoboot'],
      examples: [
        'crear sesión',
        'crear una sesión de simulador',
        'iniciar simulador iPhone 12',
        'create session with iPhone 12',
        'start simulator iPhone 13'
      ],
      parameterExtractors: {
        deviceName: (match) => match.groups?.deviceName?.trim()
      }
    },
    {
      command: 'end session',
      patterns: [
        /terminar\s+(la\s+)?sesión/i,
        /cerrar\s+(el\s+)?simulador/i,
        /end\s+(the\s+)?session/i,
        /close\s+(the\s+)?simulator/i,
        /terminate\s+(the\s+)?session/i
      ],
      description: 'Ends the current simulator session',
      requiredParameters: [],
      optionalParameters: ['sessionId'],
      examples: [
        'terminar sesión',
        'cerrar simulador',
        'end session',
        'close simulator',
        'terminate session'
      ],
      parameterExtractors: {}
    },
    {
      command: 'list simulators',
      patterns: [
        /listar\s+(los\s+)?simuladores/i,
        /mostrar\s+(los\s+)?simuladores/i,
        /qué\s+simuladores\s+(hay|están\s+disponibles)/i,
        /list\s+(all\s+)?simulators/i,
        /show\s+(all\s+)?simulators/i,
        /display\s+(all\s+)?simulators/i
      ],
      description: 'Lists available simulators',
      requiredParameters: [],
      optionalParameters: [],
      examples: [
        'listar simuladores',
        'mostrar simuladores',
        'qué simuladores hay',
        'list simulators',
        'show all simulators',
        'display simulators'
      ],
      parameterExtractors: {}
    },
    {
      command: 'list booted simulators',
      patterns: [
        /listar\s+(los\s+)?simuladores\s+arrancados/i,
        /mostrar\s+(los\s+)?simuladores\s+arrancados/i,
        /qué\s+simuladores\s+están\s+arrancados/i,
        /list\s+(all\s+)?booted\s+simulators/i,
        /show\s+running\s+simulators/i,
        /display\s+active\s+simulators/i
      ],
      description: 'Lists booted simulators',
      requiredParameters: [],
      optionalParameters: [],
      examples: [
        'listar simuladores arrancados',
        'mostrar simuladores arrancados',
        'qué simuladores están arrancados',
        'list booted simulators',
        'show running simulators',
        'display active simulators'
      ],
      parameterExtractors: {}
    },
    {
      command: 'boot simulator',
      patterns: [
        /arrancar\s+(el\s+)?simulador\s+(?<udid>[a-zA-Z0-9-]+)/i,
        /bootear\s+(el\s+)?simulador\s+(?<udid>[a-zA-Z0-9-]+)/i,
        /boot\s+(the\s+)?simulator\s+(?<udid>[a-zA-Z0-9-]+)/i,
        /start\s+(the\s+)?simulator\s+(?<udid>[a-zA-Z0-9-]+)/i
      ],
      description: 'Boots a simulator by its UDID',
      requiredParameters: ['udid'],
      optionalParameters: [],
      examples: [
        'arrancar simulador 5A321B8F-4D85-4267-9F79-2F5C91D142C2',
        'bootear simulador 5A321B8F-4D85-4267-9F79-2F5C91D142C2',
        'boot simulator 5A321B8F-4D85-4267-9F79-2F5C91D142C2',
        'start simulator 5A321B8F-4D85-4267-9F79-2F5C91D142C2'
      ],
      parameterExtractors: {
        udid: (match) => match.groups?.udid?.trim()
      }
    },
    {
      command: 'shutdown simulator',
      patterns: [
        /apagar\s+(el\s+)?simulador\s+(?<udid>[a-zA-Z0-9-]+)/i,
        /shutdown\s+(el\s+)?simulador\s+(?<udid>[a-zA-Z0-9-]+)/i,
        /shutdown\s+(the\s+)?simulator\s+(?<udid>[a-zA-Z0-9-]+)/i,
        /turn\s+off\s+(the\s+)?simulator\s+(?<udid>[a-zA-Z0-9-]+)/i
      ],
      description: 'Shuts down a simulator by its UDID',
      requiredParameters: ['udid'],
      optionalParameters: [],
      examples: [
        'apagar simulador 5A321B8F-4D85-4267-9F79-2F5C91D142C2',
        'shutdown simulador 5A321B8F-4D85-4267-9F79-2F5C91D142C2',
        'shutdown simulator 5A321B8F-4D85-4267-9F79-2F5C91D142C2',
        'turn off simulator 5A321B8F-4D85-4267-9F79-2F5C91D142C2'
      ],
      parameterExtractors: {
        udid: (match) => match.groups?.udid?.trim()
      }
    },
    {
      command: 'focus simulator',
      patterns: [
        /enfocar\s+(el\s+)?simulador/i,
        /focus\s+(el\s+)?simulador/i,
        /traer\s+(el\s+)?simulador\s+al\s+frente/i,
        /focus\s+(the\s+)?simulator/i,
        /bring\s+(the\s+)?simulator\s+to\s+front/i
      ],
      description: 'Focuses the simulator window',
      requiredParameters: [],
      optionalParameters: ['sessionId'],
      examples: [
        'enfocar simulador',
        'focus simulador',
        'traer simulador al frente',
        'focus simulator',
        'bring simulator to front'
      ],
      parameterExtractors: {}
    }
  ];
}

```

--------------------------------------------------------------------------------
/src/parser/commands/UICommands.ts:
--------------------------------------------------------------------------------

```typescript
// SPDX-FileCopyrightText: © 2025 Industria de Diseño Textil S.A. INDITEX
// SPDX-License-Identifier: Apache-2.0

import { BaseCommandDefinition, CommandDefinition } from './BaseCommandDefinition.js';

export class UICommands extends BaseCommandDefinition {
  protected definitions: CommandDefinition[] = [
    {
      command: 'tap',
      patterns: [
        /tap(\s+at)?\s+(?<x>\d+)\s*,\s*(?<y>\d+)/i,
        /tocar(\s+en)?\s+(?<x>\d+)\s*,\s*(?<y>\d+)/i,
        /pulsar(\s+en)?\s+(?<x>\d+)\s*,\s*(?<y>\d+)/i
      ],
      description: 'Performs a tap at the specified coordinates',
      requiredParameters: ['x', 'y'],
      optionalParameters: ['sessionId', 'duration'],
      examples: [
        'tap en 100, 200',
        'tocar 150, 300',
        'pulsar en 200, 400',
        'tap at 100, 200',
        'tap 150, 300'
      ],
      parameterExtractors: {
        x: (match) => parseInt(match.groups?.x || '0', 10),
        y: (match) => parseInt(match.groups?.y || '0', 10)
      }
    },
    {
      command: 'swipe',
      patterns: [
        /swipe\s+from\s+(?<startX>\d+)\s*,\s*(?<startY>\d+)\s+to\s+(?<endX>\d+)\s*,\s*(?<endY>\d+)(\s+with\s+duration\s+(?<duration>\d+))?/i,
        /deslizar\s+desde\s+(?<startX>\d+)\s*,\s*(?<startY>\d+)\s+hasta\s+(?<endX>\d+)\s*,\s*(?<endY>\d+)(\s+con\s+duración\s+(?<duration>\d+))?/i
      ],
      description: 'Performs a swipe from one point to another',
      requiredParameters: ['startX', 'startY', 'endX', 'endY'],
      optionalParameters: ['duration', 'delta', 'sessionId'],
      examples: [
        'swipe desde 100, 200 hasta 300, 400',
        'deslizar desde 150, 300 hasta 150, 100 con duración 500',
        'swipe from 100, 200 to 300, 400',
        'swipe from 150, 300 to 150, 100 with duration 500'
      ],
      parameterExtractors: {
        startX: (match) => parseInt(match.groups?.startX || '0', 10),
        startY: (match) => parseInt(match.groups?.startY || '0', 10),
        endX: (match) => parseInt(match.groups?.endX || '0', 10),
        endY: (match) => parseInt(match.groups?.endY || '0', 10),
        duration: (match) => match.groups?.duration ? parseInt(match.groups.duration, 10) : undefined
      }
    },
    {
      command: 'press device button',
      patterns: [
        /presionar\s+(el\s+)?botón\s+del\s+dispositivo\s+(?<button>APPLE_PAY|HOME|LOCK|SIDE_BUTTON|SIRI)/i,
        /pulsar\s+(el\s+)?botón\s+del\s+dispositivo\s+(?<button>APPLE_PAY|HOME|LOCK|SIDE_BUTTON|SIRI)/i,
        /presionar\s+(el\s+)?botón\s+físico\s+(?<button>APPLE_PAY|HOME|LOCK|SIDE_BUTTON|SIRI)/i,
        /press\s+(the\s+)?device\s+button\s+(?<button>APPLE_PAY|HOME|LOCK|SIDE_BUTTON|SIRI)/i,
        /push\s+(the\s+)?device\s+button\s+(?<button>APPLE_PAY|HOME|LOCK|SIDE_BUTTON|SIRI)/i,
        /tap\s+(the\s+)?device\s+button\s+(?<button>APPLE_PAY|HOME|LOCK|SIDE_BUTTON|SIRI)/i
      ],
      description: 'Presses a hardware device button',
      requiredParameters: ['button'],
      optionalParameters: ['duration', 'sessionId'],
      examples: [
        'presionar botón del dispositivo HOME',
        'pulsar botón del dispositivo SIRI',
        'presionar botón físico HOME',
        'press device button HOME',
        'push device button SIRI',
        'tap device button LOCK'
      ],
      parameterExtractors: {
        button: (match) => match.groups?.button?.trim().toUpperCase() as any
      }
    },
    {
      command: 'input text',
      patterns: [
        /introducir\s+texto\s+(?<text>.+)/i,
        /escribir\s+texto\s+(?<text>.+)/i,
        /input\s+text\s+(?<text>.+)/i,
        /type\s+text\s+(?<text>.+)/i,
        /enter\s+text\s+(?<text>.+)/i
      ],
      description: 'Inputs text in the simulator',
      requiredParameters: ['text'],
      optionalParameters: ['sessionId'],
      examples: [
        'introducir texto Hola mundo',
        'escribir texto Prueba de texto',
        'input text Hello world',
        'type text Test message',
        'enter text Hello'
      ],
      parameterExtractors: {
        text: (match) => match.groups?.text?.trim()
      }
    },
    {
      command: 'press key',
      patterns: [
        /presionar\s+(la\s+)?tecla\s+(?<keyCode>\d+)/i,
        /pulsar\s+(la\s+)?tecla\s+(?<keyCode>\d+)/i,
        /press\s+key\s+(?<keyCode>\d+)/i,
        /hit\s+key\s+(?<keyCode>\d+)/i,
        /type\s+key\s+(?<keyCode>\d+)/i
      ],
      description: 'Presses a specific key by its code',
      requiredParameters: ['keyCode'],
      optionalParameters: ['duration', 'sessionId'],
      examples: [
        'presionar tecla 4',
        'pulsar la tecla 65',
        'press key 4',
        'hit key 65',
        'type key 13'
      ],
      parameterExtractors: {
        keyCode: (match) => parseInt(match.groups?.keyCode || '0', 10)
      }
    },
    {
      command: 'press key sequence',
      patterns: [
        /presionar\s+secuencia\s+de\s+teclas\s+(?<keyCodes>[\d\s]+)/i,
        /pulsar\s+secuencia\s+de\s+teclas\s+(?<keyCodes>[\d\s]+)/i,
        /press\s+key\s+sequence\s+(?<keyCodes>[\d\s]+)/i,
        /type\s+key\s+sequence\s+(?<keyCodes>[\d\s]+)/i,
        /enter\s+key\s+sequence\s+(?<keyCodes>[\d\s]+)/i
      ],
      description: 'Presses a sequence of keys',
      requiredParameters: ['keyCodes'],
      optionalParameters: ['sessionId'],
      examples: [
        'presionar secuencia de teclas 4 5 6',
        'pulsar secuencia de teclas 65 66 67',
        'press key sequence 4 5 6',
        'type key sequence 65 66 67',
        'enter key sequence 13 14 15'
      ],
      parameterExtractors: {
        keyCodes: (match) => match.groups?.keyCodes?.trim().split(/\s+/).map(k => parseInt(k, 10))
      }
    }
  ];
}

```

--------------------------------------------------------------------------------
/src/adapters/ParserToOrchestrator.ts:
--------------------------------------------------------------------------------

```typescript
// SPDX-FileCopyrightText: © 2025 Industria de Diseño Textil S.A. INDITEX
// SPDX-License-Identifier: Apache-2.0

import { ParseResult } from '../parser/interfaces/IParser.js';
import { 
  CommandType, 
  IOrchestratorCommand,
  CommandFactory
} from '../orchestrator/interfaces/IOrchestratorCommand.js';

/**
 * Adapter that converts parser results into orchestrator commands
 */
export class ParserToOrchestrator {
  private commandFactory: CommandFactory;
  private commandMappings: Record<string, CommandType>;

  /**
   * Constructor
   * @param commandFactory Command factory to create IOrchestratorCommand instances
   */
  constructor(commandFactory: CommandFactory) {
    this.commandFactory = commandFactory;
    
    // Mapping natural language commands to CommandType
    this.commandMappings = {
      // Simulator management commands
      'crear sesión': CommandType.CREATE_SIMULATOR_SESSION,
      'crear simulador': CommandType.CREATE_SIMULATOR_SESSION,
      'iniciar simulador': CommandType.CREATE_SIMULATOR_SESSION,
      'terminar sesión': CommandType.TERMINATE_SIMULATOR_SESSION,
      'cerrar simulador': CommandType.TERMINATE_SIMULATOR_SESSION,
      'listar simuladores': CommandType.LIST_AVAILABLE_SIMULATORS,
      'mostrar simuladores': CommandType.LIST_AVAILABLE_SIMULATORS,
      'listar simuladores arrancados': CommandType.LIST_BOOTED_SIMULATORS,
      'arrancar simulador': CommandType.BOOT_SIMULATOR,
      'apagar simulador': CommandType.SHUTDOWN_SIMULATOR,
      
      // Application management commands
      'instalar app': CommandType.INSTALL_APP,
      'instalar aplicación': CommandType.INSTALL_APP,
      'lanzar app': CommandType.LAUNCH_APP,
      'abrir app': CommandType.LAUNCH_APP,
      'iniciar app': CommandType.LAUNCH_APP,
      'cerrar app': CommandType.TERMINATE_APP,
      'terminar app': CommandType.TERMINATE_APP,
      
      // UI interaction commands
      'tap': CommandType.TAP,
      'tocar': CommandType.TAP,
      'pulsar': CommandType.TAP,
      'swipe': CommandType.SWIPE,
      'deslizar': CommandType.SWIPE,
      
      // Screenshot and logs commands
      'capturar pantalla': CommandType.TAKE_SCREENSHOT,
      'screenshot': CommandType.TAKE_SCREENSHOT,
      'captura': CommandType.TAKE_SCREENSHOT,
      'logs': CommandType.GET_SYSTEM_LOGS,
      'logs del sistema': CommandType.GET_SYSTEM_LOGS,
      'logs de app': CommandType.GET_APP_LOGS,
      
      // Verification commands
      'verificar simulador': CommandType.IS_SIMULATOR_BOOTED,
      'comprobar simulador': CommandType.IS_SIMULATOR_BOOTED,
      'verificar app': CommandType.IS_APP_INSTALLED,
      'comprobar app': CommandType.IS_APP_INSTALLED
    };
  }

  /**
   * Converts a parser result into an orchestrator command
   * @param parseResult Parser result
   * @returns Orchestrator command
   */
  public convertToCommand(parseResult: ParseResult): IOrchestratorCommand {
    // Determine command type
    const commandType = this.mapToCommandType(parseResult.command);
    
    // Convert parameters
    const parameters = this.convertParameters(commandType, parseResult.parameters);
    
    // Create and return command
    return this.commandFactory.createCommand(
      commandType,
      parameters,
      `Command generated from: "${parseResult.originalText}"`
    );
  }

  /**
   * Maps a natural language command to a CommandType
   * @param naturalCommand Natural language command
   * @returns Corresponding CommandType
   */
  private mapToCommandType(naturalCommand: string): CommandType {
    const lowerCommand = naturalCommand.toLowerCase();
    
    // Look for exact match
    if (this.commandMappings[lowerCommand]) {
      return this.commandMappings[lowerCommand];
    }
    
    // Look for partial match
    for (const [key, value] of Object.entries(this.commandMappings)) {
      if (lowerCommand.includes(key)) {
        return value;
      }
    }
    
    // If no match found, throw error
    throw new Error(`Could not map command "${naturalCommand}" to a CommandType`);
  }

  /**
   * Converts parser parameters to orchestrator parameters
   * @param commandType Command type
   * @param parserParameters Parser parameters
   * @returns Orchestrator parameters
   */
  private convertParameters(
    commandType: CommandType, 
    parserParameters: Record<string, any>
  ): Record<string, any> {
    // Clone parameters to avoid modifying the original
    const parameters = { ...parserParameters };
    
    // Convert specific parameters based on command type
    switch (commandType) {
      case CommandType.TAP:
        // Ensure x and y are numbers
        if (parameters.x !== undefined) {
          parameters.x = Number(parameters.x);
        }
        if (parameters.y !== undefined) {
          parameters.y = Number(parameters.y);
        }
        break;
        
      case CommandType.SWIPE:
        // Ensure coordinates are numbers
        if (parameters.startX !== undefined) {
          parameters.startX = Number(parameters.startX);
        }
        if (parameters.startY !== undefined) {
          parameters.startY = Number(parameters.startY);
        }
        if (parameters.endX !== undefined) {
          parameters.endX = Number(parameters.endX);
        }
        if (parameters.endY !== undefined) {
          parameters.endY = Number(parameters.endY);
        }
        if (parameters.duration !== undefined) {
          parameters.duration = Number(parameters.duration);
        }
        break;
        
      case CommandType.CREATE_SIMULATOR_SESSION:
        // Convert autoboot to boolean if it's a string
        if (typeof parameters.autoboot === 'string') {
          parameters.autoboot = parameters.autoboot.toLowerCase() === 'true';
        }
        break;
    }
    
    return parameters;
  }
}

```

--------------------------------------------------------------------------------
/src/parser/__tests__/NLParser.test.ts:
--------------------------------------------------------------------------------

```typescript
import { NLParser } from '../NLParser.js';
import { ParseResult, ValidationResult } from '../interfaces/IParser.js';

// Add Jest types to global scope
declare global {
  // eslint-disable-next-line @typescript-eslint/no-namespace
  namespace jest {
    interface Matchers<R> {
      [key: string]: any;
    }
  }
}

describe('NLParser', () => {
  let parser: NLParser;

  beforeEach(() => {
    parser = new NLParser();
  });

  describe('parseInstruction', () => {
    it('should parse a valid instruction', async () => {
      const result = await parser.parseInstruction('launch app com.example.app');
      expect(result).toBeDefined();
      expect(result.command).toBe('launch app');
      expect(result.parameters).toHaveProperty('bundleId');
      expect(result.parameters.bundleId).toBe('com.example.app');
    });

    it('should handle unknown instructions', async () => {
      try {
        await parser.parseInstruction('invalid command here');
        fail('Should have thrown an error');
      } catch (error: any) {
        expect(error).toBeDefined();
        expect(error.message).toContain('Could not understand the instruction');
      }
    });
  });

  describe('validateInstruction', () => {
    it('should validate a complete instruction', async () => {
      const parseResult: ParseResult = {
        command: 'launch app',
        parameters: {
          bundleId: 'com.example.app'
        },
        confidence: 1.0,
        originalText: 'launch app com.example.app'
      };

      const result = await parser.validateInstruction(parseResult);
      expect(result.isValid).toBe(true);
    });

    it('should detect missing required parameters', async () => {
      const parseResult: ParseResult = {
        command: 'launch app',
        parameters: {},
        confidence: 1.0,
        originalText: 'launch app'
      };

      const result = await parser.validateInstruction(parseResult);
      expect(result.isValid).toBe(false);
      expect(result.errorMessage).toContain('Missing required parameters');
    });

    it('should detect unrecognized commands', async () => {
      const parseResult: ParseResult = {
        command: 'invalid-command',
        parameters: {},
        confidence: 0.0,
        originalText: 'invalid-command'
      };

      const result = await parser.validateInstruction(parseResult);
      expect(result.isValid).toBe(false);
      expect(result.errorMessage).toContain('Unrecognized command');
    });
  });

  describe('normalizeParameters', () => {
    it('should normalize numeric string values to numbers', async () => {
      const parseResult: ParseResult = {
        command: 'set-location',
        parameters: {
          latitude: '37.7749',
          longitude: '-122.4194'
        },
        confidence: 1.0,
        originalText: 'set location 37.7749 -122.4194'
      };

      const result = await parser.normalizeParameters(parseResult);
      expect(typeof result.parameters.latitude).toBe('number');
      expect(typeof result.parameters.longitude).toBe('number');
      expect(result.parameters.latitude).toBe(37.7749);
      expect(result.parameters.longitude).toBe(-122.4194);
    });

    it('should normalize boolean string values to booleans', async () => {
      const parseResult: ParseResult = {
        command: 'set-preference',
        parameters: {
          enabled: 'true',
          disabled: 'false'
        },
        confidence: 1.0,
        originalText: 'set preference enabled true disabled false'
      };

      const result = await parser.normalizeParameters(parseResult);
      expect(typeof result.parameters.enabled).toBe('boolean');
      expect(typeof result.parameters.disabled).toBe('boolean');
      expect(result.parameters.enabled).toBe(true);
      expect(result.parameters.disabled).toBe(false);
    });

    it('should preserve non-numeric and non-boolean string values', async () => {
      const parseResult: ParseResult = {
        command: 'test-command',
        parameters: {
          text: 'hello world',
          mixedValue: '123abc'
        },
        confidence: 1.0,
        originalText: 'test command text "hello world" mixedValue 123abc'
      };

      const result = await parser.normalizeParameters(parseResult);
      expect(typeof result.parameters.text).toBe('string');
      expect(typeof result.parameters.mixedValue).toBe('string');
      expect(result.parameters.text).toBe('hello world');
      expect(result.parameters.mixedValue).toBe('123abc');
    });
  });

  describe('suggestCompletions', () => {
    it('should suggest completions for partial text', async () => {
      const suggestions = await parser.suggestCompletions('launch');
      expect(Array.isArray(suggestions)).toBe(true);
      expect(suggestions.length).toBeGreaterThan(0);
      expect(suggestions.some(s => s.startsWith('launch'))).toBe(true);
    });

    it('should return empty array for completely irrelevant text', async () => {
      const suggestions = await parser.suggestCompletions('xyzabc123');
      expect(Array.isArray(suggestions)).toBe(true);
      expect(suggestions.length).toBe(0);
    });
  });

  describe('getSupportedCommands', () => {
    it('should return a list of supported commands', async () => {
      const commands = await parser.getSupportedCommands();
      expect(Array.isArray(commands)).toBe(true);
      expect(commands.length).toBeGreaterThan(0);
      
      // Check command structure
      const command = commands[0];
      expect(command).toHaveProperty('command');
      expect(command).toHaveProperty('description');
      expect(command).toHaveProperty('requiredParameters');
      expect(command).toHaveProperty('optionalParameters');
      expect(Array.isArray(command.requiredParameters)).toBe(true);
      expect(Array.isArray(command.optionalParameters)).toBe(true);
    });
  });
});

```

--------------------------------------------------------------------------------
/src/adapters/OrchestratorToIDB.ts:
--------------------------------------------------------------------------------

```typescript
// SPDX-FileCopyrightText: © 2025 Industria de Diseño Textil S.A. INDITEX
// SPDX-License-Identifier: Apache-2.0

import { 
  CommandType, 
  IOrchestratorCommand, 
  CommandResult 
} from '../orchestrator/interfaces/IOrchestratorCommand.js';
import { IIDBManager } from '../idb/interfaces/IIDBManager.js';

/**
 * Adapter that converts orchestrator commands into IDBManager calls
 */
export class OrchestratorToIDB {
  private idbManager: IIDBManager;

  /**
   * Constructor
   * @param idbManager IDBManager instance to execute commands
   */
  constructor(idbManager: IIDBManager) {
    this.idbManager = idbManager;
  }

  /**
   * Executes an orchestrator command using IDBManager
   * @param command Command to execute
   * @param sessionId Simulator session ID (optional)
   * @returns Execution result
   */
  public async executeCommand(command: IOrchestratorCommand, sessionId?: string): Promise<CommandResult> {
    try {
      const startTime = Date.now();
      let result: any;

      // Execute command based on its type
      switch (command.type) {
        // Simulator management commands
        case CommandType.CREATE_SIMULATOR_SESSION:
          result = await this.idbManager.createSimulatorSession(command.parameters);
          break;

        case CommandType.TERMINATE_SIMULATOR_SESSION:
          await this.idbManager.terminateSimulatorSession(
            command.parameters.sessionId || sessionId || ''
          );
          result = { sessionId: command.parameters.sessionId || sessionId };
          break;

        case CommandType.LIST_AVAILABLE_SIMULATORS:
          result = await this.idbManager.listAvailableSimulators();
          break;

        case CommandType.LIST_BOOTED_SIMULATORS:
          result = await this.idbManager.listBootedSimulators();
          break;

        case CommandType.BOOT_SIMULATOR:
          await this.idbManager.bootSimulatorByUDID(command.parameters.udid);
          result = { udid: command.parameters.udid };
          break;

        case CommandType.SHUTDOWN_SIMULATOR:
          if (command.parameters.udid) {
            await this.idbManager.shutdownSimulatorByUDID(command.parameters.udid);
            result = { udid: command.parameters.udid };
          } else {
            await this.idbManager.shutdownSimulator(
              command.parameters.sessionId || sessionId || ''
            );
            result = { sessionId: command.parameters.sessionId || sessionId };
          }
          break;

        // Application management commands
        case CommandType.INSTALL_APP:
          result = await this.idbManager.installApp(
            command.parameters.sessionId || sessionId || '',
            command.parameters.appPath
          );
          break;

        case CommandType.LAUNCH_APP:
          await this.idbManager.launchApp(
            command.parameters.sessionId || sessionId || '',
            command.parameters.bundleId
          );
          result = { bundleId: command.parameters.bundleId };
          break;

        case CommandType.TERMINATE_APP:
          await this.idbManager.terminateApp(
            command.parameters.sessionId || sessionId || '',
            command.parameters.bundleId
          );
          result = { bundleId: command.parameters.bundleId };
          break;

        // UI interaction commands
        case CommandType.TAP:
          await this.idbManager.tap(
            command.parameters.sessionId || sessionId || '',
            command.parameters.x,
            command.parameters.y
          );
          result = { x: command.parameters.x, y: command.parameters.y };
          break;

        case CommandType.SWIPE:
          await this.idbManager.swipe(
            command.parameters.sessionId || sessionId || '',
            command.parameters.startX,
            command.parameters.startY,
            command.parameters.endX,
            command.parameters.endY,
            command.parameters.duration
          );
          result = {
            startX: command.parameters.startX,
            startY: command.parameters.startY,
            endX: command.parameters.endX,
            endY: command.parameters.endY
          };
          break;

        // Screenshot and logging commands
        case CommandType.TAKE_SCREENSHOT:
          result = await this.idbManager.takeScreenshot(
            command.parameters.sessionId || sessionId || '',
            command.parameters.outputPath
          );
          break;

        case CommandType.GET_SYSTEM_LOGS:
          result = await this.idbManager.getSystemLogs(
            command.parameters.sessionId || sessionId || '',
            command.parameters.options
          );
          break;

        case CommandType.GET_APP_LOGS:
          result = await this.idbManager.getAppLogs(
            command.parameters.sessionId || sessionId || '',
            command.parameters.bundleId
          );
          break;

        // Verification commands
        case CommandType.IS_SIMULATOR_BOOTED:
          result = await this.idbManager.isSimulatorBooted(
            command.parameters.sessionId || sessionId || ''
          );
          break;

        case CommandType.IS_APP_INSTALLED:
          result = await this.idbManager.isAppInstalled(
            command.parameters.sessionId || sessionId || '',
            command.parameters.bundleId
          );
          break;

        default:
          throw new Error(`Unsupported command type: ${command.type}`);
      }

      // Create and return the result
      return {
        success: true,
        data: result,
        timestamp: Date.now()
      };
    } catch (error: any) {
      // Handle errors
      console.error(`Error executing command ${command.type}:`, error);
      
      // If there's a custom error handler, use it
      if (command.onError) {
        return command.onError(error, { sessionId });
      }
      
      // Return error result
      return {
        success: false,
        error: error.message || 'Unknown error',
        timestamp: Date.now()
      };
    }
  }
}

```

--------------------------------------------------------------------------------
/src/parser/commands/MiscCommands.ts:
--------------------------------------------------------------------------------

```typescript
// SPDX-FileCopyrightText: © 2025 Industria de Diseño Textil S.A. INDITEX
// SPDX-License-Identifier: Apache-2.0

import { BaseCommandDefinition, CommandDefinition } from './BaseCommandDefinition.js';

export class MiscCommands extends BaseCommandDefinition {
  protected definitions: CommandDefinition[] = [
    {
      command: 'install dylib',
      patterns: [
        /instalar\s+dylib\s+(?<dylibPath>[^\s,]+)/i,
        /install\s+dylib\s+(?<dylibPath>[^\s,]+)/i,
        /add\s+dylib\s+(?<dylibPath>[^\s,]+)/i,
        /load\s+dylib\s+(?<dylibPath>[^\s,]+)/i
      ],
      description: 'Installs a dynamic library (.dylib)',
      requiredParameters: ['dylibPath'],
      optionalParameters: ['sessionId'],
      examples: [
        'instalar dylib /ruta/a/lib.dylib',
        'install dylib /tmp/library.dylib',
        'add dylib /path/to/lib.dylib',
        'load dylib /path/to/library.dylib'
      ],
      parameterExtractors: {
        dylibPath: (match) => match.groups?.dylibPath?.trim()
      }
    },
    {
      command: 'open url',
      patterns: [
        /abrir\s+url\s+(?<url>[^\s]+)/i,
        /open\s+url\s+(?<url>[^\s]+)/i,
        /navegar\s+a\s+(?<url>[^\s]+)/i,
        /navigate\s+to\s+(?<url>[^\s]+)/i,
        /browse\s+to\s+(?<url>[^\s]+)/i
      ],
      description: 'Opens a URL in the simulator',
      requiredParameters: ['url'],
      optionalParameters: ['sessionId'],
      examples: [
        'abrir url https://example.com',
        'open url https://google.com',
        'navegar a https://apple.com',
        'navigate to https://example.com',
        'browse to https://apple.com'
      ],
      parameterExtractors: {
        url: (match) => match.groups?.url?.trim()
      }
    },
    {
      command: 'clear keychain',
      patterns: [
        /limpiar\s+keychain/i,
        /clear\s+keychain/i,
        /borrar\s+keychain/i,
        /reset\s+keychain/i,
        /empty\s+keychain/i
      ],
      description: 'Clears the simulator keychain',
      requiredParameters: [],
      optionalParameters: ['sessionId'],
      examples: [
        'limpiar keychain',
        'clear keychain',
        'borrar keychain',
        'reset keychain',
        'empty keychain'
      ],
      parameterExtractors: {}
    },
    {
      command: 'set location',
      patterns: [
        /establecer\s+ubicación\s+(?<latitude>-?\d+(\.\d+)?)\s*,\s*(?<longitude>-?\d+(\.\d+)?)/i,
        /set\s+location\s+(?<latitude>-?\d+(\.\d+)?)\s*,\s*(?<longitude>-?\d+(\.\d+)?)/i,
        /cambiar\s+ubicación\s+a\s+(?<latitude>-?\d+(\.\d+)?)\s*,\s*(?<longitude>-?\d+(\.\d+)?)/i,
        /update\s+location\s+(?<latitude>-?\d+(\.\d+)?)\s*,\s*(?<longitude>-?\d+(\.\d+)?)/i,
        /change\s+location\s+to\s+(?<latitude>-?\d+(\.\d+)?)\s*,\s*(?<longitude>-?\d+(\.\d+)?)/i
      ],
      description: 'Sets the simulator location',
      requiredParameters: ['latitude', 'longitude'],
      optionalParameters: ['sessionId'],
      examples: [
        'establecer ubicación 37.7749, -122.4194',
        'set location 40.7128, -74.0060',
        'cambiar ubicación a 51.5074, -0.1278',
        'update location 48.8566, 2.3522',
        'change location to 35.6762, 139.6503'
      ],
      parameterExtractors: {
        latitude: (match) => parseFloat(match.groups?.latitude || '0'),
        longitude: (match) => parseFloat(match.groups?.longitude || '0')
      }
    },
    {
      command: 'add media',
      patterns: [
        /añadir\s+media\s+(?<mediaPaths>.+)/i,
        /add\s+media\s+(?<mediaPaths>.+)/i,
        /importar\s+multimedia\s+(?<mediaPaths>.+)/i,
        /import\s+media\s+(?<mediaPaths>.+)/i,
        /upload\s+media\s+(?<mediaPaths>.+)/i
      ],
      description: 'Adds media files to the simulator camera roll',
      requiredParameters: ['mediaPaths'],
      optionalParameters: ['sessionId'],
      examples: [
        'añadir media /ruta/imagen.jpg /ruta/video.mp4',
        'add media /tmp/photo.png',
        'importar multimedia /ruta/a/fotos/*.jpg',
        'import media /path/to/photos/*.jpg',
        'upload media /path/video.mp4'
      ],
      parameterExtractors: {
        mediaPaths: (match) => match.groups?.mediaPaths?.trim().split(/\s+/)
      }
    },
    {
      command: 'approve permissions',
      patterns: [
        /aprobar\s+permisos\s+(?<bundleId>[^\s,]+)\s+(?<permissions>.+)/i,
        /approve\s+permissions\s+(?<bundleId>[^\s,]+)\s+(?<permissions>.+)/i,
        /dar\s+permisos\s+a\s+(?<bundleId>[^\s,]+)\s+(?<permissions>.+)/i,
        /grant\s+permissions\s+(?<bundleId>[^\s,]+)\s+(?<permissions>.+)/i,
        /allow\s+permissions\s+(?<bundleId>[^\s,]+)\s+(?<permissions>.+)/i
      ],
      description: 'Approves permissions for an application',
      requiredParameters: ['bundleId', 'permissions'],
      optionalParameters: ['sessionId'],
      examples: [
        'aprobar permisos com.example.app photos camera',
        'approve permissions com.apple.mobilesafari contacts',
        'dar permisos a com.example.app photos',
        'grant permissions com.example.app camera',
        'allow permissions com.example.app location'
      ],
      parameterExtractors: {
        bundleId: (match) => match.groups?.bundleId?.trim(),
        permissions: (match) => match.groups?.permissions?.trim().split(/\s+/)
      }
    },
    {
      command: 'update contacts',
      patterns: [
        /actualizar\s+contactos\s+(?<dbPath>[^\s,]+)/i,
        /update\s+contacts\s+(?<dbPath>[^\s,]+)/i,
        /importar\s+contactos\s+(?<dbPath>[^\s,]+)/i,
        /import\s+contacts\s+(?<dbPath>[^\s,]+)/i,
        /load\s+contacts\s+(?<dbPath>[^\s,]+)/i
      ],
      description: 'Updates the simulator contacts database',
      requiredParameters: ['dbPath'],
      optionalParameters: ['sessionId'],
      examples: [
        'actualizar contactos /ruta/a/contactos.sqlite',
        'update contacts /tmp/contacts.db',
        'importar contactos /ruta/contacts.sqlite',
        'import contacts /path/to/contacts.db',
        'load contacts /path/contacts.sqlite'
      ],
      parameterExtractors: {
        dbPath: (match) => match.groups?.dbPath?.trim()
      }
    }
  ];
}

```

--------------------------------------------------------------------------------
/repolinter.json:
--------------------------------------------------------------------------------

```json
{
  "$schema": "https://raw.githubusercontent.com/todogroup/repolinter/master/rulesets/schema.json",
  "version": 2,
  "axioms": {
    "linguist": "language",
    "licensee": "license",
    "packagers": "packager"
  },
  "rules": {
    "license-file-exists": {
      "level": "error",
      "rule": {
        "type": "file-existence",
        "options": {
          "globsAny": ["LICENSE*", "COPYING*"],
          "nocase": true
        }
      }
    },
    "readme-file-exists": {
      "level": "error",
      "rule": {
        "type": "file-existence",
        "options": {
          "globsAny": ["README*"],
          "nocase": true
        }
      }
    },
    "contributing-file-exists": {
      "level": "error",
      "rule": {
        "type": "file-existence",
        "options": {
          "globsAny": ["{docs/,.github/,}CONTRIB*"],
          "nocase": true
        }
      }
    },
    "code-of-conduct-file-exists": {
      "level": "error",
      "rule": {
        "type": "file-existence",
        "options": {
          "globsAny": [
            "{docs/,.github/,}CODEOFCONDUCT*",
            "{docs/,.github/,}CODE-OF-CONDUCT*",
            "{docs/,.github/,}CODE_OF_CONDUCT*"
          ],
          "nocase": true
        }
      }
    },
    "changelog-file-exists": {
      "level": "error",
      "rule": {
        "type": "file-existence",
        "options": {
          "globsAny": ["CHANGELOG*"],
          "nocase": true
        }
      }
    },
    "security-file-exists": {
      "level": "error",
      "rule": {
        "type": "file-existence",
        "options": {
          "globsAny": ["{docs/,.github/,}SECURITY.md"]
        }
      }
    },
    "support-file-exists": {
      "level": "off",
      "rule": {
        "type": "file-existence",
        "options": {
          "globsAny": ["{docs/,.github/,}SUPPORT*"],
          "nocase": true
        }
      }
    },
    "readme-references-license": {
      "level": "off",
      "rule": {
        "type": "file-contents",
        "options": {
          "globsAll": ["README*"],
          "content": "license",
          "flags": "i"
        }
      }
    },
    "binaries-not-present": {
      "level": "error",
      "rule": {
        "type": "file-type-exclusion",
        "options": {
          "type": ["/*.exe", "/.dll", "!node_modules/"]
        }
      }
    },
    "test-directory-exists": {
      "level": "off",
      "rule": {
        "type": "directory-existence",
        "options": {
          "globsAny": ["/test", "/specs"],
          "nocase": true
        }
      }
    },
    "integrates-with-ci": {
      "level": "error",
      "rule": {
        "type": "file-existence",
        "options": {
          "globsAny": [
            ".gitlab-ci.yml",
            ".travis.yml",
            "appveyor.yml",
            ".appveyor.yml",
            "circle.yml",
            ".circleci/config.yml",
            "Jenkinsfile",
            ".drone.yml",
            ".github/workflows/",
            "azure-pipelines.yml"
          ]
        }
      }
    },
    "code-of-conduct-file-contains-email": {
      "level": "off",
      "rule": {
        "type": "file-contents",
        "options": {
          "globsAll": [
            "CODEOFCONDUCT",
            "CODE-OF-CONDUCT*",
            "CODE_OF_CONDUCT*",
            ".github/CODEOFCONDUCT*",
            ".github/CODE-OF-CONDUCT*",
            ".github/CODE_OF_CONDUCT*"
          ],
          "content": ".+@.+..+",
          "flags": "i",
          "human-readable-content": "email address"
        }
      }
    },
    "source-license-headers-exist": {
      "level": "warning",
      "rule": {
        "type": "file-starts-with",
        "options": {
          "globsAll": ["./**/*.go"],
          "lineCount": 5,
          "patterns": ["Copyright", "License"],
          "flags": "i"
        }
      }
    },
    "github-issue-template-exists": {
      "level": "error",
      "rule": {
        "type": "file-existence",
        "options": {
          "dirs": true,
          "globsAny": ["ISSUE_TEMPLATE", ".github/ISSUE_TEMPLATE*"]
        }
      }
    },
    "github-pull-request-template-exists": {
      "level": "off",
      "rule": {
        "type": "file-existence",
        "options": {
          "dirs": true,
          "globsAny": [
            "PULL_REQUEST_TEMPLATE*",
            ".github/PULL_REQUEST_TEMPLATE*"
          ]
        }
      }
    },
    "javascript-package-metadata-exists": {
      "level": "error",
      "where": ["language=javascript"],
      "rule": {
        "type": "file-existence",
        "options": {
          "globsAny": ["package.json"]
        }
      }
    },
    "ruby-package-metadata-exists": {
      "level": "error",
      "where": ["language=ruby"],
      "rule": {
        "type": "file-existence",
        "options": {
          "globsAny": ["Gemfile"]
        }
      }
    },
    "java-package-metadata-exists": {
      "level": "error",
      "where": ["language=java"],
      "rule": {
        "type": "file-existence",
        "options": {
          "globsAny": ["pom.xml", "build.xml", "build.gradle"]
        }
      }
    },
    "python-package-metadata-exists": {
      "level": "error",
      "where": ["language=python"],
      "rule": {
        "type": "file-existence",
        "options": {
          "globsAny": ["setup.py", "requirements.txt"]
        }
      }
    },
    "objective-c-package-metadata-exists": {
      "level": "error",
      "where": ["language=objective-c"],
      "rule": {
        "type": "file-existence",
        "options": {
          "globsAny": ["Cartfile", "Podfile", ".podspec"]
        }
      }
    },
    "swift-package-metadata-exists": {
      "level": "error",
      "where": ["language=swift"],
      "rule": {
        "type": "file-existence",
        "options": {
          "globsAny": ["Package.swift"]
        }
      }
    },
    "erlang-package-metadata-exists": {
      "level": "error",
      "where": ["language=erlang"],
      "rule": {
        "type": "file-existence",
        "options": {
          "globsAny": ["rebar.config"]
        }
      }
    },
    "elixir-package-metadata-exists": {
      "level": "error",
      "where": ["language=elixir"],
      "rule": {
        "type": "file-existence",
        "options": {
          "globsAny": ["mix.exs"]
        }
      }
    },
    "license-detectable-by-licensee": {
      "level": "off",
      "where": ["license="],
      "rule": {
        "type": "license-detectable-by-licensee",
        "options": {}
      }
    },
    "notice-file-exists": {
      "level": "error",
      "where": ["license=Apache-2.0"],
      "rule": {
        "type": "file-existence",
        "options": {
          "globsAny": ["NOTICE*"],
          "fail-message": "The NOTICE file is described in section 4.4 of the Apache License version 2.0. Its presence is not mandated by the license itself, but by ASF policy."
        }
      }
    },
    "best-practices-badge-present": {
      "level": "off",
      "rule": {
        "type": "best-practices-badge-present"
      }
    },
    "internal-file-not-exists": {
      "level": "off",
      "rule": {
        "type": "file-not-exists",
        "options": {
          "globsAll": [
            ".secrets.baseline",
            "sherpa-config.yml",
            ".snyk",
            "sonar-project.properties",
            ".drafterconfig.yml",
            "application-configmap.yml",
            "application-secret.yml"
          ],
          "nocase": true
        }
      }
    }
  }
}
```

--------------------------------------------------------------------------------
/src/orchestrator/__tests__/MCPOrchestrator.test.ts:
--------------------------------------------------------------------------------

```typescript
import { jest } from '@jest/globals';
import { MCPOrchestrator } from '../MCPOrchestrator.js';
import { IParser, ParseResult, ValidationResult } from '../../parser/interfaces/IParser.js';
import { IIDBManager } from '../../idb/interfaces/IIDBManager.js';
import { CommandType, CommandResult, IOrchestratorCommand } from '../interfaces/IOrchestratorCommand.js';

// Mock implementations
const mockParser: jest.Mocked<IParser> = {
  parseInstruction: jest.fn(),
  validateInstruction: jest.fn(),
  normalizeParameters: jest.fn(),
  getSupportedCommands: jest.fn(),
  suggestCompletions: jest.fn()
};

const mockIDBManager: jest.Mocked<IIDBManager> = {
  // Required simulator management methods
  createSimulatorSession: jest.fn(),
  terminateSimulatorSession: jest.fn(),
  listAvailableSimulators: jest.fn(),
  listBootedSimulators: jest.fn(),
  listSimulatorSessions: jest.fn(),
  bootSimulatorByUDID: jest.fn(),
  shutdownSimulatorByUDID: jest.fn(),
  shutdownSimulator: jest.fn(),
  isSimulatorBooted: jest.fn(),

  // Required app management methods
  installApp: jest.fn(),
  launchApp: jest.fn(),
  terminateApp: jest.fn(),
  isAppInstalled: jest.fn(),

  // Required UI interaction methods
  tap: jest.fn(),
  swipe: jest.fn(),

  // Required screenshots and logs methods
  takeScreenshot: jest.fn(),
  getSystemLogs: jest.fn(),
  getAppLogs: jest.fn()
};

describe('MCPOrchestrator', () => {
  let orchestrator: MCPOrchestrator;

  beforeEach(() => {
    // Clear all mocks
    jest.clearAllMocks();
    orchestrator = new MCPOrchestrator(mockParser, mockIDBManager);
  });

  describe('processInstruction', () => {
    it('should process a valid instruction successfully', async () => {
      const instruction = 'lanzar app com.example.app';
      const parseResult: ParseResult = {
        command: 'lanzar app',
        parameters: { bundleId: 'com.example.app' },
        confidence: 1.0,
        originalText: instruction
      };
      const validationResult: ValidationResult = {
        isValid: true
      };

      mockParser.parseInstruction.mockResolvedValue(parseResult);
      mockParser.validateInstruction.mockResolvedValue(validationResult);
      mockParser.normalizeParameters.mockResolvedValue(parseResult);
      mockIDBManager.launchApp.mockResolvedValue();

      const result = await orchestrator.processInstruction(instruction);
      expect(result.success).toBe(true);
      expect(mockParser.parseInstruction).toHaveBeenCalledWith(instruction);
      expect(mockParser.validateInstruction).toHaveBeenCalledWith(parseResult);
      expect(mockParser.normalizeParameters).toHaveBeenCalledWith(parseResult);
    });

    it('should handle invalid instructions', async () => {
      const instruction = 'invalid command';
      const parseResult: ParseResult = {
        command: 'unknown',
        parameters: {},
        confidence: 0.0,
        originalText: instruction
      };
      const validationResult: ValidationResult = {
        isValid: false,
        errorMessage: 'Invalid command'
      };

      mockParser.parseInstruction.mockResolvedValue(parseResult);
      mockParser.validateInstruction.mockResolvedValue(validationResult);

      const result = await orchestrator.processInstruction(instruction);
      expect(result.success).toBe(false);
      expect(result.error).toBe('Invalid command');
    });
  });

  describe('executeCommand', () => {
    it('should execute a simple command successfully', async () => {
      const command: IOrchestratorCommand = {
        type: CommandType.LAUNCH_APP,
        parameters: { bundleId: 'com.example.app' },
        id: '123',
        description: 'Launch app'
      };

      mockIDBManager.launchApp.mockResolvedValue();

      const result = await orchestrator.executeCommand(command);
      expect(result.success).toBe(true);
    });

    it('should execute a sequence command', async () => {
      const command: IOrchestratorCommand = {
        type: CommandType.SEQUENCE,
        parameters: {
          commands: [
            {
              type: CommandType.LAUNCH_APP,
              parameters: { bundleId: 'com.example.app' },
              id: '123',
              description: 'Launch app'
            },
            {
              type: CommandType.TAP,
              parameters: { x: 100, y: 200 },
              id: '456',
              description: 'Tap screen'
            }
          ],
          stopOnError: true
        },
        id: '789',
        description: 'Sequence command'
      };

      mockIDBManager.launchApp.mockResolvedValue();
      mockIDBManager.tap.mockResolvedValue();

      const result = await orchestrator.executeCommand(command);
      expect(result.success).toBe(true);
      expect(mockIDBManager.launchApp).toHaveBeenCalled();
      expect(mockIDBManager.tap).toHaveBeenCalled();
    });
  });

  describe('session management', () => {
    it('should manage session ID correctly', () => {
      const sessionId = 'test-session-123';
      orchestrator.setActiveSessionId(sessionId);
      expect(orchestrator.getActiveSessionId()).toBe(sessionId);

      orchestrator.setActiveSessionId(null);
      expect(orchestrator.getActiveSessionId()).toBeNull();
    });

    it('should update session ID after creating a simulator session', async () => {
      const command: IOrchestratorCommand = {
        type: CommandType.CREATE_SIMULATOR_SESSION,
        parameters: { udid: 'test-simulator' },
        id: '123',
        description: 'Create simulator session'
      };

      mockIDBManager.createSimulatorSession.mockResolvedValue('new-session-123');

      const result = await orchestrator.executeCommand(command);
      expect(result.success).toBe(true);
      expect(orchestrator.getActiveSessionId()).toBe('new-session-123');
    });
  });

  describe('command history', () => {
    it('should maintain command history', async () => {
      const command: IOrchestratorCommand = {
        type: CommandType.LAUNCH_APP,
        parameters: { bundleId: 'com.example.app' },
        id: '123',
        description: 'Launch app'
      };

      mockIDBManager.launchApp.mockResolvedValue();

      await orchestrator.executeCommand(command);
      const history = orchestrator.getCommandHistory();
      
      expect(history.length).toBe(1);
      expect(history[0].command).toEqual(command);
      expect(history[0].result.success).toBe(true);
    });

    it('should respect history limit', async () => {
      const command1: IOrchestratorCommand = {
        type: CommandType.LAUNCH_APP,
        parameters: { bundleId: 'com.example.app1' },
        id: '123',
        description: 'Launch app 1'
      };

      const command2: IOrchestratorCommand = {
        type: CommandType.LAUNCH_APP,
        parameters: { bundleId: 'com.example.app2' },
        id: '456',
        description: 'Launch app 2'
      };

      mockIDBManager.launchApp.mockResolvedValue();

      await orchestrator.executeCommand(command1);
      await orchestrator.executeCommand(command2);
      
      const limitedHistory = orchestrator.getCommandHistory(1);
      expect(limitedHistory.length).toBe(1);
      expect(limitedHistory[0].command).toEqual(command2);
    });
  });

  describe('event handling', () => {
    it('should handle event listeners correctly', () => {
      const mockListener = jest.fn();
      const event = 'testEvent';
      const data = { test: true };

      // Add listener
      orchestrator.on(event, mockListener);
      orchestrator['emit'](event, data);
      expect(mockListener).toHaveBeenCalledWith(data);

      // Remove listener
      orchestrator.off(event, mockListener);
      orchestrator['emit'](event, data);
      expect(mockListener).toHaveBeenCalledTimes(1);
    });

    it('should emit session events', async () => {
      const sessionCreatedListener = jest.fn();
      orchestrator.on('sessionCreated', sessionCreatedListener);

      const command: IOrchestratorCommand = {
        type: CommandType.CREATE_SIMULATOR_SESSION,
        parameters: { udid: 'test-simulator' },
        id: '123',
        description: 'Create simulator session'
      };

      mockIDBManager.createSimulatorSession.mockResolvedValue('new-session-123');

      await orchestrator.executeCommand(command);
      expect(sessionCreatedListener).toHaveBeenCalledWith({ sessionId: 'new-session-123' });
    });
  });
});

```

--------------------------------------------------------------------------------
/LICENSES/Apache-2.0.txt:
--------------------------------------------------------------------------------

```
                                 Apache License
                           Version 2.0, January 2004
                        http://www.apache.org/licenses/

   TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION

   1. Definitions.

      "License" shall mean the terms and conditions for use, reproduction,
      and distribution as defined by Sections 1 through 9 of this document.

      "Licensor" shall mean the copyright owner or entity authorized by
      the copyright owner that is granting the License.

      "Legal Entity" shall mean the union of the acting entity and all
      other entities that control, are controlled by, or are under common
      control with that entity. For the purposes of this definition,
      "control" means (i) the power, direct or indirect, to cause the
      direction or management of such entity, whether by contract or
      otherwise, or (ii) ownership of fifty percent (50%) or more of the
      outstanding shares, or (iii) beneficial ownership of such entity.

      "You" (or "Your") shall mean an individual or Legal Entity
      exercising permissions granted by this License.

      "Source" form shall mean the preferred form for making modifications,
      including but not limited to software source code, documentation
      source, and configuration files.

      "Object" form shall mean any form resulting from mechanical
      transformation or translation of a Source form, including but
      not limited to compiled object code, generated documentation,
      and conversions to other media types.

      "Work" shall mean the work of authorship, whether in Source or
      Object form, made available under the License, as indicated by a
      copyright notice that is included in or attached to the work
      (an example is provided in the Appendix below).

      "Derivative Works" shall mean any work, whether in Source or Object
      form, that is based on (or derived from) the Work and for which the
      editorial revisions, annotations, elaborations, or other modifications
      represent, as a whole, an original work of authorship. For the purposes
      of this License, Derivative Works shall not include works that remain
      separable from, or merely link (or bind by name) to the interfaces of,
      the Work and Derivative Works thereof.

      "Contribution" shall mean any work of authorship, including
      the original version of the Work and any modifications or additions
      to that Work or Derivative Works thereof, that is intentionally
      submitted to Licensor for inclusion in the Work by the copyright owner
      or by an individual or Legal Entity authorized to submit on behalf of
      the copyright owner. For the purposes of this definition, "submitted"
      means any form of electronic, verbal, or written communication sent
      to the Licensor or its representatives, including but not limited to
      communication on electronic mailing lists, source code control systems,
      and issue tracking systems that are managed by, or on behalf of, the
      Licensor for the purpose of discussing and improving the Work, but
      excluding communication that is conspicuously marked or otherwise
      designated in writing by the copyright owner as "Not a Contribution."

      "Contributor" shall mean Licensor and any individual or Legal Entity
      on behalf of whom a Contribution has been received by Licensor and
      subsequently incorporated within the Work.

   2. Grant of Copyright License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      copyright license to reproduce, prepare Derivative Works of,
      publicly display, publicly perform, sublicense, and distribute the
      Work and such Derivative Works in Source or Object form.

   3. Grant of Patent License. Subject to the terms and conditions of
      this License, each Contributor hereby grants to You a perpetual,
      worldwide, non-exclusive, no-charge, royalty-free, irrevocable
      (except as stated in this section) patent license to make, have made,
      use, offer to sell, sell, import, and otherwise transfer the Work,
      where such license applies only to those patent claims licensable
      by such Contributor that are necessarily infringed by their
      Contribution(s) alone or by combination of their Contribution(s)
      with the Work to which such Contribution(s) was submitted. If You
      institute patent litigation against any entity (including a
      cross-claim or counterclaim in a lawsuit) alleging that the Work
      or a Contribution incorporated within the Work constitutes direct
      or contributory patent infringement, then any patent licenses
      granted to You under this License for that Work shall terminate
      as of the date such litigation is filed.

   4. Redistribution. You may reproduce and distribute copies of the
      Work or Derivative Works thereof in any medium, with or without
      modifications, and in Source or Object form, provided that You
      meet the following conditions:

      (a) You must give any other recipients of the Work or
          Derivative Works a copy of this License; and

      (b) You must cause any modified files to carry prominent notices
          stating that You changed the files; and

      (c) You must retain, in the Source form of any Derivative Works
          that You distribute, all copyright, patent, trademark, and
          attribution notices from the Source form of the Work,
          excluding those notices that do not pertain to any part of
          the Derivative Works; and

      (d) If the Work includes a "NOTICE" text file as part of its
          distribution, then any Derivative Works that You distribute must
          include a readable copy of the attribution notices contained
          within such NOTICE file, excluding those notices that do not
          pertain to any part of the Derivative Works, in at least one
          of the following places: within a NOTICE text file distributed
          as part of the Derivative Works; within the Source form or
          documentation, if provided along with the Derivative Works; or,
          within a display generated by the Derivative Works, if and
          wherever such third-party notices normally appear. The contents
          of the NOTICE file are for informational purposes only and
          do not modify the License. You may add Your own attribution
          notices within Derivative Works that You distribute, alongside
          or as an addendum to the NOTICE text from the Work, provided
          that such additional attribution notices cannot be construed
          as modifying the License.

      You may add Your own copyright statement to Your modifications and
      may provide additional or different license terms and conditions
      for use, reproduction, or distribution of Your modifications, or
      for any such Derivative Works as a whole, provided Your use,
      reproduction, and distribution of the Work otherwise complies with
      the conditions stated in this License.

   5. Submission of Contributions. Unless You explicitly state otherwise,
      any Contribution intentionally submitted for inclusion in the Work
      by You to the Licensor shall be under the terms and conditions of
      this License, without any additional terms or conditions.
      Notwithstanding the above, nothing herein shall supersede or modify
      the terms of any separate license agreement you may have executed
      with Licensor regarding such Contributions.

   6. Trademarks. This License does not grant permission to use the trade
      names, trademarks, service marks, or product names of the Licensor,
      except as required for reasonable and customary use in describing the
      origin of the Work and reproducing the content of the NOTICE file.

   7. Disclaimer of Warranty. Unless required by applicable law or
      agreed to in writing, Licensor provides the Work (and each
      Contributor provides its Contributions) on an "AS IS" BASIS,
      WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
      implied, including, without limitation, any warranties or conditions
      of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
      PARTICULAR PURPOSE. You are solely responsible for determining the
      appropriateness of using or redistributing the Work and assume any
      risks associated with Your exercise of permissions under this License.

   8. Limitation of Liability. In no event and under no legal theory,
      whether in tort (including negligence), contract, or otherwise,
      unless required by applicable law (such as deliberate and grossly
      negligent acts) or agreed to in writing, shall any Contributor be
      liable to You for damages, including any direct, indirect, special,
      incidental, or consequential damages of any character arising as a
      result of this License or out of the use or inability to use the
      Work (including but not limited to damages for loss of goodwill,
      work stoppage, computer failure or malfunction, or any and all
      other commercial damages or losses), even if such Contributor
      has been advised of the possibility of such damages.

   9. Accepting Warranty or Additional Liability. While redistributing
      the Work or Derivative Works thereof, You may choose to offer,
      and charge a fee for, acceptance of support, warranty, indemnity,
      or other liability obligations and/or rights consistent with this
      License. However, in accepting such obligations, You may act only
      on Your own behalf and on Your sole responsibility, not on behalf
      of any other Contributor, and only if You agree to indemnify,
      defend, and hold each Contributor harmless for any liability
      incurred by, or claims asserted against, such Contributor by reason
      of your accepting any such warranty or additional liability.

   END OF TERMS AND CONDITIONS

   APPENDIX: How to apply the Apache License to your work.

      To apply the Apache License to your work, attach the following
      boilerplate notice, with the fields enclosed by brackets "[]"
      replaced with your own identifying information. (Don't include
      the brackets!)  The text should be enclosed in the appropriate
      comment syntax for the file format. We also recommend that a
      file or class name and description of purpose be included on the
      same "printed page" as the copyright notice for easier
      identification within third-party archives.

   Copyright [yyyy] [name of copyright owner]

   Licensed under the Apache License, Version 2.0 (the "License");
   you may not use this file except in compliance with the License.
   You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

   Unless required by applicable law or agreed to in writing, software
   distributed under the License is distributed on an "AS IS" BASIS,
   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   See the License for the specific language governing permissions and
   limitations under the License.

```

--------------------------------------------------------------------------------
/src/idb/interfaces/IIDBManager.ts:
--------------------------------------------------------------------------------

```typescript
// SPDX-FileCopyrightText: © 2025 Industria de Diseño Textil S.A. INDITEX
// SPDX-License-Identifier: Apache-2.0

/**
 * IIDBManager - Interface for managing iOS simulators through idb
 * 
 * This interface defines the necessary methods to interact with iOS simulators
 * using the idb tool (https://fbidb.io/docs/commands).
 */

export interface SimulatorInfo {
  udid: string;
  name: string;
  state: 'Booted' | 'Shutdown' | 'Unknown';
  os: string;
  deviceType: string;
}

export interface AppInfo {
  bundleId: string;
  name: string;
  installedPath?: string;
}

export interface SessionConfig {
  deviceName?: string;
  platformVersion?: string;
  timeout?: number;
  autoboot?: boolean;
}

export type ButtonType = 'APPLE_PAY' | 'HOME' | 'LOCK' | 'SIDE_BUTTON' | 'SIRI';

export interface CrashLogInfo {
  name: string;
  bundleId?: string;
  date: Date;
  path: string;
}

export interface AccessibilityInfo {
  identifier?: string;
  label?: string;
  frame: {
    x: number;
    y: number;
    width: number;
    height: number;
  };
  type?: string;
  value?: string;
  children?: AccessibilityInfo[];
}

export interface IIDBManager {
  // === Simulator Management ===
  
  /**
   * Initializes a new simulator session
   * @param config Optional configuration for the session
   * @returns Created session ID
   */
  createSimulatorSession(config?: SessionConfig): Promise<string>;

  /**
   * Terminates an existing simulator session
   * @param sessionId Session ID to terminate
   */
  terminateSimulatorSession(sessionId: string): Promise<void>;

  /**
   * Lists all available simulators
   * @returns List of simulator information
   */
  listAvailableSimulators(): Promise<SimulatorInfo[]>;

  /**
   * Lists currently booted simulators
   * @returns List of booted simulator information
   */
  listBootedSimulators(): Promise<SimulatorInfo[]>;

  /**
   * Boots a simulator by its UDID
   * @param udid UDID of the simulator to boot
   */
  bootSimulatorByUDID(udid: string): Promise<void>;

  /**
   * Shuts down a simulator by its UDID
   * @param udid UDID of the simulator to shut down
   */
  shutdownSimulatorByUDID(udid: string): Promise<void>;

  /**
   * Shuts down a simulator by its session ID
   * @param sessionId Session ID of the simulator to shut down
   */
  shutdownSimulator(sessionId: string): Promise<void>;

  /**
   * Lists active simulator sessions
   * @returns List of active session IDs
   */
  listSimulatorSessions(): Promise<string[]>;

  /**
   * Verifies if a simulator is booted
   * @param sessionId Session ID of the simulator
   * @returns true if booted, false otherwise
   */
  isSimulatorBooted(sessionId: string): Promise<boolean>;

  /**
   * Focuses the simulator window
   * @param sessionId Session ID of the simulator
   */
  focusSimulator?(sessionId: string): Promise<void>;

  // === Application Management ===

  /**
   * Installs an application on the simulator
   * @param sessionId Session ID of the simulator
   * @param appPath Path to the .app or .ipa file to install
   * @returns Information about the installed application
   */
  installApp(sessionId: string, appPath: string): Promise<AppInfo>;

  /**
   * Launches an application on the simulator
   * @param sessionId Session ID of the simulator
   * @param bundleId Bundle ID of the application to launch
   * @param env Environment variables (optional)
   * @param args Launch arguments (optional)
   */
  launchApp(
    sessionId: string, 
    bundleId: string, 
    env?: Record<string, string>,
    args?: string[]
  ): Promise<void>;

  /**
   * Terminates a running application
   * @param sessionId Session ID of the simulator
   * @param bundleId Bundle ID of the application to terminate
   */
  terminateApp(sessionId: string, bundleId: string): Promise<void>;

  /**
   * Uninstalls an application
   * @param sessionId Session ID of the simulator
   * @param bundleId Bundle ID of the application to uninstall
   */
  uninstallApp?(sessionId: string, bundleId: string): Promise<void>;

  /**
   * Lists installed applications
   * @param sessionId Session ID of the simulator
   * @returns List of application information
   */
  listApps?(sessionId: string): Promise<AppInfo[]>;

  /**
   * Verifies if an application is installed
   * @param sessionId Session ID of the simulator
   * @param bundleId Bundle ID of the application
   * @returns true if installed, false otherwise
   */
  isAppInstalled(sessionId: string, bundleId: string): Promise<boolean>;

  // === UI Interaction ===

  /**
   * Performs a tap at the specified coordinates
   * @param sessionId Session ID of the simulator
   * @param x X coordinate
   * @param y Y coordinate
   * @param duration Tap duration in milliseconds (optional)
   */
  tap(sessionId: string, x: number, y: number, duration?: number): Promise<void>;

  /**
   * Performs a swipe on the screen
   * @param sessionId Session ID of the simulator
   * @param startX Initial X coordinate
   * @param startY Initial Y coordinate
   * @param endX Final X coordinate
   * @param endY Final Y coordinate
   * @param duration Swipe duration in milliseconds (optional)
   * @param delta Size of each swipe step (optional)
   */
  swipe(
    sessionId: string,
    startX: number,
    startY: number,
    endX: number,
    endY: number,
    duration?: number,
    delta?: number
  ): Promise<void>;

  /**
   * Presses a device button
   * @param sessionId Session ID of the simulator
   * @param button Type of button to press
   * @param duration Press duration in milliseconds (optional)
   */
  pressButton?(
    sessionId: string,
    button: ButtonType,
    duration?: number
  ): Promise<void>;

  /**
   * Inputs text into the simulator
   * @param sessionId Session ID of the simulator
   * @param text Text to input
   */
  inputText?(sessionId: string, text: string): Promise<void>;

  /**
   * Presses a specific key by its code
   * @param sessionId Session ID of the simulator
   * @param keyCode Key code
   * @param duration Press duration in milliseconds (optional)
   */
  pressKey?(
    sessionId: string,
    keyCode: number,
    duration?: number
  ): Promise<void>;

  /**
   * Presses a sequence of keys
   * @param sessionId Session ID of the simulator
   * @param keyCodes List of key codes to press sequentially
   */
  pressKeySequence?(
    sessionId: string,
    keyCodes: number[]
  ): Promise<void>;

  // === Accessibility ===

  /**
   * Describes all accessibility elements on the screen
   * @param sessionId Session ID of the simulator
   * @returns Accessibility information for all elements
   */
  describeAllElements?(sessionId: string): Promise<AccessibilityInfo[]>;

  /**
   * Describes the accessibility element at a specific point
   * @param sessionId Session ID of the simulator
   * @param x X coordinate
   * @param y Y coordinate
   * @returns Accessibility information for the element at the point
   */
  describePointElement?(
    sessionId: string,
    x: number,
    y: number
  ): Promise<AccessibilityInfo | null>;

  // === Screenshots and Logs ===

  /**
   * Takes a screenshot of the simulator
   * @param sessionId Session ID of the simulator
   * @param outputPath Path where to save the screenshot (optional)
   * @returns Buffer with the image or path to the saved file
   */
  takeScreenshot(sessionId: string, outputPath?: string): Promise<any | string>;

  /**
   * Starts video recording of the simulator
   * @param sessionId Session ID of the simulator
   * @param outputPath Path where to save the video
   * @returns Recording ID
   */
  startVideoRecording?(sessionId: string, outputPath: string): Promise<string>;

  /**
   * Stops video recording of the simulator
   * @param sessionId Session ID of the simulator
   * @param recordingId Recording ID to stop
   */
  stopVideoRecording?(sessionId: string, recordingId: string): Promise<void>;

  /**
   * Gets simulator system logs
   * @param sessionId Session ID of the simulator
   * @param options Filtering options (optional)
   * @returns System logs
   */
  getSystemLogs(sessionId: string, options?: { 
    bundle?: string;
    since?: Date;
    limit?: number;
  }): Promise<string>;

  /**
   * Gets logs for a specific application
   * @param sessionId Session ID of the simulator
   * @param bundleId Bundle ID of the application
   * @returns Application logs
   */
  getAppLogs(sessionId: string, bundleId: string): Promise<string>;

  // === Debug ===

  /**
   * Starts a debug session for an application
   * @param sessionId Session ID of the simulator
   * @param bundleId Bundle ID of the application to debug
   * @returns Connection port for the debugger
   */
  startDebugServer?(sessionId: string, bundleId: string): Promise<number>;

  /**
   * Stops a debug session
   * @param sessionId Session ID of the simulator
   */
  stopDebugServer?(sessionId: string): Promise<void>;

  /**
   * Gets the debug session status
   * @param sessionId Session ID of the simulator
   * @returns Information about the debug session
   */
  getDebugServerStatus?(sessionId: string): Promise<{
    running: boolean;
    port?: number;
    bundleId?: string;
  }>;

  // === Crash Logs ===

  /**
   * Lists available crash logs
   * @param sessionId Session ID of the simulator
   * @param options Filtering options (optional)
   * @returns List of crash log information
   */
  listCrashLogs?(sessionId: string, options?: {
    bundleId?: string;
    before?: Date;
    since?: Date;
  }): Promise<CrashLogInfo[]>;

  /**
   * Gets the content of a crash log
   * @param sessionId Session ID of the simulator
   * @param crashName Name of the crash log
   * @returns Content of the crash log
   */
  getCrashLog?(sessionId: string, crashName: string): Promise<string>;

  /**
   * Deletes crash logs
   * @param sessionId Session ID of the simulator
   * @param options Options for deleting logs (optional)
   */
  deleteCrashLogs?(sessionId: string, options: {
    crashNames?: string[];
    bundleId?: string;
    before?: Date;
    since?: Date;
    all?: boolean;
  }): Promise<void>;

  // === Miscellaneous ===

  /**
   * Installs a dynamic library (.dylib)
   * @param sessionId Session ID of the simulator
   * @param dylibPath Path to the .dylib library
   */
  installDylib?(sessionId: string, dylibPath: string): Promise<void>;

  /**
   * Opens a URL in the simulator
   * @param sessionId Session ID of the simulator
   * @param url URL to open
   */
  openUrl?(sessionId: string, url: string): Promise<void>;

  /**
   * Clears the simulator keychain
   * @param sessionId Session ID of the simulator
   */
  clearKeychain?(sessionId: string): Promise<void>;

  /**
   * Sets the simulator location
   * @param sessionId Session ID of the simulator
   * @param latitude Latitude
   * @param longitude Longitude
   */
  setLocation?(
    sessionId: string,
    latitude: number,
    longitude: number
  ): Promise<void>;

  /**
   * Adds media files to the simulator camera roll
   * @param sessionId Session ID of the simulator
   * @param mediaPaths Paths to media files
   */
  addMedia?(sessionId: string, mediaPaths: string[]): Promise<void>;

  /**
   * Approves permissions for an application
   * @param sessionId Session ID of the simulator
   * @param bundleId Bundle ID of the application
   * @param permissions Permissions to approve (photos, camera, contacts, etc.)
   */
  approvePermissions?(
    sessionId: string,
    bundleId: string,
    permissions: string[]
  ): Promise<void>;

  /**
   * Updates the simulator contacts database
   * @param sessionId Session ID of the simulator
   * @param dbPath Path to the contacts database
   */
  updateContacts?(sessionId: string, dbPath: string): Promise<void>;
}

```

--------------------------------------------------------------------------------
/src/orchestrator/MCPOrchestrator.ts:
--------------------------------------------------------------------------------

```typescript
// SPDX-FileCopyrightText: © 2025 Industria de Diseño Textil S.A. INDITEX
// SPDX-License-Identifier: Apache-2.0

import { v4 as uuidv4 } from 'uuid';
import { IParser, ParseResult } from '../parser/interfaces/IParser.js';
import { 
  IOrchestratorCommand, 
  CommandType, 
  CommandResult, 
  CommandContext,
  SequenceCommand,
  ConditionalCommand
} from './interfaces/IOrchestratorCommand.js';
import { ParserToOrchestrator } from '../adapters/ParserToOrchestrator.js';
import { OrchestratorToIDB } from '../adapters/OrchestratorToIDB.js';
import { IIDBManager } from '../idb/interfaces/IIDBManager.js';

/**
 * Implementation of the command factory
 */
class CommandFactoryImpl {
  /**
   * Creates an orchestrator command
   * @param type Command type
   * @param parameters Command parameters
   * @param description Command description
   * @returns Created command
   */
  createCommand(type: CommandType, parameters: Record<string, any>, description?: string): IOrchestratorCommand {
    return {
      type,
      parameters,
      id: uuidv4(),
      description,
      timeout: 30000, // 30 seconds by default
      retries: 1      // 1 retry by default
    };
  }

  /**
   * Creates a sequence command
   * @param commands Commands to execute in sequence
   * @param stopOnError Whether to stop on error
   * @returns Sequence command
   */
  createSequence(commands: IOrchestratorCommand[], stopOnError: boolean = true): SequenceCommand {
    return {
      type: CommandType.SEQUENCE,
      parameters: {
        commands,
        stopOnError
      },
      id: uuidv4(),
      description: `Sequence of ${commands.length} commands`
    };
  }

  /**
   * Creates a conditional command
   * @param condition Condition function
   * @param ifTrue Command to execute if condition is true
   * @param ifFalse Command to execute if condition is false
   * @returns Conditional command
   */
  createConditional(
    condition: (context: CommandContext) => Promise<boolean>,
    ifTrue: IOrchestratorCommand,
    ifFalse?: IOrchestratorCommand
  ): ConditionalCommand {
    return {
      type: CommandType.CONDITIONAL,
      parameters: {
        condition,
        ifTrue,
        ifFalse
      },
      id: uuidv4(),
      description: 'Conditional command'
    };
  }
}

/**
 * MCP Central Orchestrator
 * 
 * Coordinates interactions between the natural language parser and the IDB manager,
 * facilitating the execution of iOS simulator commands.
 */
export class MCPOrchestrator {
  private parser: IParser;
  private idbManager: IIDBManager;
  private parserToOrchestrator: ParserToOrchestrator;
  private orchestratorToIDB: OrchestratorToIDB;
  private commandFactory: CommandFactoryImpl;
  private activeSessionId: string | null = null;
  private commandHistory: Array<{
    command: IOrchestratorCommand;
    result: CommandResult;
    timestamp: number;
  }> = [];
  private eventListeners: Record<string, Array<(data: any) => void>> = {};

  /**
   * Constructor
   * @param parser Natural language parser instance
   * @param idbManager IDB manager instance
   */
  constructor(parser: IParser, idbManager: IIDBManager) {
    this.parser = parser;
    this.idbManager = idbManager;
    this.commandFactory = new CommandFactoryImpl();
    this.parserToOrchestrator = new ParserToOrchestrator(this.commandFactory);
    this.orchestratorToIDB = new OrchestratorToIDB(idbManager);
  }

  /**
   * Processes a natural language instruction
   * @param instruction Natural language instruction
   * @returns Execution result
   */
  public async processInstruction(instruction: string): Promise<CommandResult> {
    try {
      // Parse the instruction
      const parseResult = await this.parser.parseInstruction(instruction);
      
      // Validate the instruction
      const validationResult = await this.parser.validateInstruction(parseResult);
      if (!validationResult.isValid) {
        return {
          success: false,
          error: validationResult.errorMessage || 'Invalid instruction',
          timestamp: Date.now()
        };
      }
      
      // Normalize parameters
      const normalizedResult = await this.parser.normalizeParameters(parseResult);
      
      // Convert to orchestrator command
      const command = this.parserToOrchestrator.convertToCommand(normalizedResult);
      
      // Execute the command
      return this.executeCommand(command);
    } catch (error: any) {
      console.error('Error processing instruction:', error);
      return {
        success: false,
        error: error.message || 'Unknown error',
        timestamp: Date.now()
      };
    }
  }

  /**
   * Executes an orchestrator command
   * @param command Command to execute
   * @returns Execution result
   */
  public async executeCommand(command: IOrchestratorCommand): Promise<CommandResult> {
    try {
      let result: CommandResult;
      
      // Execute command based on its type
      if (command.type === CommandType.SEQUENCE) {
        result = await this.executeSequenceCommand(command as SequenceCommand);
      } else if (command.type === CommandType.CONDITIONAL) {
        result = await this.executeConditionalCommand(command as ConditionalCommand);
      } else {
        // Validate parameters if validation function exists
        if (command.validate) {
          const isValid = await command.validate({ 
            sessionId: this.activeSessionId || undefined 
          });
          if (!isValid) {
            return {
              success: false,
              error: 'Parameter validation failed',
              timestamp: Date.now()
            };
          }
        }
        
        // Transform parameters if transformation function exists
        if (command.transformParameters) {
          command.parameters = await command.transformParameters({ 
            sessionId: this.activeSessionId || undefined 
          });
        }
        
        // Execute the command
        result = await this.orchestratorToIDB.executeCommand(command, this.activeSessionId || undefined);
        
        // If it's a session creation command and successful, save the session ID
        if (command.type === CommandType.CREATE_SIMULATOR_SESSION && result.success && result.data) {
          this.activeSessionId = result.data;
          this.emit('sessionCreated', { sessionId: this.activeSessionId });
        }
        
        // If it's a session termination command and successful, clear the session ID
        if (command.type === CommandType.TERMINATE_SIMULATOR_SESSION && result.success) {
          const oldSessionId = this.activeSessionId;
          this.activeSessionId = null;
          this.emit('sessionTerminated', { sessionId: oldSessionId });
        }
      }
      
      // Save to history
      this.commandHistory.push({
        command,
        result,
        timestamp: Date.now()
      });
      
      // Emit command executed event
      this.emit('commandExecuted', {
        command,
        result
      });
      
      return result;
    } catch (error: any) {
      console.error('Error executing command:', error);
      return {
        success: false,
        error: error.message || 'Unknown error',
        timestamp: Date.now()
      };
    }
  }

  /**
   * Executes a sequence command
   * @param sequenceCommand Sequence command
   * @returns Execution result
   */
  private async executeSequenceCommand(sequenceCommand: SequenceCommand): Promise<CommandResult> {
    const results: CommandResult[] = [];
    const context: CommandContext = {
      sessionId: this.activeSessionId || undefined,
      previousResults: {},
      variables: {}
    };
    
    try {
      // Execute each command in sequence
      for (const command of sequenceCommand.parameters.commands) {
        const result = await this.executeCommand(command);
        results.push(result);
        
        // Save result in context
        context.previousResults![command.id] = result;
        
        // If there's an error and stopOnError is true, stop execution
        if (!result.success && sequenceCommand.parameters.stopOnError) {
          return {
            success: false,
            data: {
              results,
              completedCommands: sequenceCommand.parameters.commands.indexOf(command) + 1,
              totalCommands: sequenceCommand.parameters.commands.length
            },
            error: `Error in command ${command.id}: ${result.error}`,
            timestamp: Date.now()
          };
        }
      }
      
      // All commands executed successfully
      return {
        success: true,
        data: {
          results,
          completedCommands: sequenceCommand.parameters.commands.length,
          totalCommands: sequenceCommand.parameters.commands.length
        },
        timestamp: Date.now()
      };
    } catch (error: any) {
      console.error('Error executing sequence:', error);
      return {
        success: false,
        data: {
          results,
          completedCommands: results.length,
          totalCommands: sequenceCommand.parameters.commands.length
        },
        error: error.message || 'Unknown error',
        timestamp: Date.now()
      };
    }
  }

  /**
   * Executes a conditional command
   * @param conditionalCommand Conditional command
   * @returns Execution result
   */
  private async executeConditionalCommand(conditionalCommand: ConditionalCommand): Promise<CommandResult> {
    try {
      // Evaluate condition
      const context: CommandContext = {
        sessionId: this.activeSessionId || undefined,
        previousResults: {},
        variables: {}
      };
      
      const conditionResult = await conditionalCommand.parameters.condition(context);
      
      // Execute corresponding command based on condition result
      if (conditionResult) {
        return this.executeCommand(conditionalCommand.parameters.ifTrue);
      } else if (conditionalCommand.parameters.ifFalse) {
        return this.executeCommand(conditionalCommand.parameters.ifFalse);
      } else {
        // No command for false case
        return {
          success: true,
          data: {
            conditionResult,
            executed: false
          },
          timestamp: Date.now()
        };
      }
    } catch (error: any) {
      console.error('Error executing conditional command:', error);
      return {
        success: false,
        error: error.message || 'Unknown error',
        timestamp: Date.now()
      };
    }
  }

  /**
   * Gets command history
   * @param limit Command limit to return (optional)
   * @returns Command history
   */
  public getCommandHistory(limit?: number): Array<{
    command: IOrchestratorCommand;
    result: CommandResult;
    timestamp: number;
  }> {
    if (limit) {
      return this.commandHistory.slice(-limit);
    }
    return [...this.commandHistory];
  }

  /**
   * Gets the active session ID
   * @returns Active session ID or null if no active session
   */
  public getActiveSessionId(): string | null {
    return this.activeSessionId;
  }

  /**
   * Sets the active session ID
   * @param sessionId Session ID to set
   */
  public setActiveSessionId(sessionId: string | null): void {
    this.activeSessionId = sessionId;
    if (sessionId) {
      this.emit('sessionActivated', { sessionId });
    } else {
      this.emit('sessionDeactivated', {});
    }
  }

  /**
   * Registers an event listener
   * @param event Event name
   * @param listener Function to execute when event occurs
   */
  public on(event: string, listener: (data: any) => void): void {
    if (!this.eventListeners[event]) {
      this.eventListeners[event] = [];
    }
    this.eventListeners[event].push(listener);
  }

  /**
   * Removes an event listener
   * @param event Event name
   * @param listener Function to remove
   */
  public off(event: string, listener: (data: any) => void): void {
    if (this.eventListeners[event]) {
      this.eventListeners[event] = this.eventListeners[event].filter(l => l !== listener);
    }
  }

  /**
   * Emits an event
   * @param event Event name
   * @param data Event data
   */
  private emit(event: string, data: any): void {
    if (this.eventListeners[event]) {
      for (const listener of this.eventListeners[event]) {
        try {
          listener(data);
        } catch (error) {
          console.error(`Error in event listener ${event}:`, error);
        }
      }
    }
  }
}

```

--------------------------------------------------------------------------------
/src/idb/IDBManager.ts:
--------------------------------------------------------------------------------

```typescript
// SPDX-FileCopyrightText: © 2025 Industria de Diseño Textil S.A. INDITEX
// SPDX-License-Identifier: Apache-2.0

import { exec } from 'child_process';
import { promisify } from 'util';
import * as fs from 'fs';
import * as path from 'path';
import { 
  IIDBManager, 
  SimulatorInfo, 
  AppInfo, 
  SessionConfig,
  ButtonType,
  AccessibilityInfo,
  CrashLogInfo
} from './interfaces/IIDBManager.js';

const execAsync = promisify(exec);

/**
 * IDB manager implementation for interacting with iOS simulators
 */
export class IDBManager implements IIDBManager {
  private sessions: Map<string, string> = new Map(); // sessionId -> udid
  private sessionCounter: number = 0;

  private async executeCommand(command: string): Promise<string> {
    try {
      const { stdout } = await execAsync(command);
      return stdout.trim();
    } catch (error: any) {
      console.error(`Error executing idb command: ${command}`);
      console.error(error.message);
      throw new Error(`Error executing idb command: ${error.message}`);
    }
  }

  private async verifyIDBAvailability(): Promise<void> {
    try {
      await this.executeCommand('idb --version');
    } catch (error) {
      throw new Error('idb is not installed or not available in PATH. Make sure idb-companion and fb-idb are properly installed.');
    }
  }

  private generateSessionId(): string {
    return `session_${Date.now()}_${this.sessionCounter++}`;
  }

  async createSimulatorSession(config?: SessionConfig): Promise<string> {
    await this.verifyIDBAvailability();
    let udid: string;

    if (config?.deviceName) {
      const simulators = await this.listAvailableSimulators();
      const simulator = simulators.find(sim => 
        sim.name === config.deviceName && 
        (!config.platformVersion || sim.os.includes(config.platformVersion))
      );

      if (!simulator) {
        throw new Error(`No simulator found with name ${config.deviceName}`);
      }
      udid = simulator.udid;
    } else {
      const simulators = await this.listAvailableSimulators();
      if (simulators.length === 0) {
        throw new Error('No available simulators found');
      }
      udid = simulators[0].udid;
    }

    if (config?.autoboot !== false) {
      await this.bootSimulatorByUDID(udid);
    }

    const sessionId = this.generateSessionId();
    this.sessions.set(sessionId, udid);
    return sessionId;
  }

  async terminateSimulatorSession(sessionId: string): Promise<void> {
    const udid = this.sessions.get(sessionId);
    if (!udid) {
      throw new Error(`Session not found: ${sessionId}`);
    }
    this.sessions.delete(sessionId);
  }

  async listAvailableSimulators(): Promise<SimulatorInfo[]> {
    await this.verifyIDBAvailability();
    const output = await this.executeCommand('xcrun simctl list devices --json');
    const data = JSON.parse(output);
    const simulators: SimulatorInfo[] = [];
    
    Object.entries(data.devices).forEach(([runtimeName, devices]: [string, any]) => {
      devices.forEach((device: any) => {
        simulators.push({
          udid: device.udid,
          name: device.name,
          state: device.state === 'Booted' ? 'Booted' : 
                 device.state === 'Shutdown' ? 'Shutdown' : 'Unknown',
          os: runtimeName.replace('com.apple.CoreSimulator.SimRuntime.', ''),
          deviceType: device.deviceTypeIdentifier || 'Unknown'
        });
      });
    });
    
    return simulators;
  }

  async listBootedSimulators(): Promise<SimulatorInfo[]> {
    const simulators = await this.listAvailableSimulators();
    return simulators.filter(sim => sim.state === 'Booted');
  }

  async bootSimulatorByUDID(udid: string): Promise<void> {
    await this.verifyIDBAvailability();
    const simulators = await this.listBootedSimulators();
    if (simulators.some(sim => sim.udid === udid)) {
      return;
    }
    
    await this.executeCommand(`xcrun simctl boot ${udid}`);
    let attempts = 0;
    const maxAttempts = 30;
    
    while (attempts < maxAttempts) {
      try {
        const booted = await this.listBootedSimulators();
        if (booted.some(sim => sim.udid === udid)) {
          await new Promise(resolve => setTimeout(resolve, 2000));
          return;
        }
      } catch (error) {
        // Ignore errors during boot
      }
      await new Promise(resolve => setTimeout(resolve, 1000));
      attempts++;
    }
    
    throw new Error(`Timeout waiting for simulator ${udid} to boot`);
  }

  async shutdownSimulator(sessionId: string): Promise<void> {
    const udid = this.sessions.get(sessionId);
    if (!udid) {
      throw new Error(`Session not found: ${sessionId}`);
    }
    await this.shutdownSimulatorByUDID(udid);
  }

  async shutdownSimulatorByUDID(udid: string): Promise<void> {
    await this.verifyIDBAvailability();
    await this.executeCommand(`xcrun simctl shutdown ${udid}`);
  }

  async installApp(sessionId: string, appPath: string): Promise<AppInfo> {
    const udid = this.sessions.get(sessionId);
    if (!udid) {
      throw new Error(`Session not found: ${sessionId}`);
    }
    
    if (!fs.existsSync(appPath)) {
      throw new Error(`File does not exist: ${appPath}`);
    }
    
    await this.executeCommand(`idb install --udid ${udid} ${appPath}`);
    
    const appName = path.basename(appPath, path.extname(appPath));
    const bundleId = `com.example.${appName}`;
    
    return {
      bundleId,
      name: appName,
      installedPath: appPath
    };
  }

  async launchApp(sessionId: string, bundleId: string): Promise<void> {
    const udid = this.sessions.get(sessionId);
    if (!udid) {
      throw new Error(`Session not found: ${sessionId}`);
    }
    await this.executeCommand(`idb launch --udid ${udid} ${bundleId}`);
  }

  async terminateApp(sessionId: string, bundleId: string): Promise<void> {
    const udid = this.sessions.get(sessionId);
    if (!udid) {
      throw new Error(`Session not found: ${sessionId}`);
    }
    await this.executeCommand(`idb terminate --udid ${udid} ${bundleId}`);
  }

  async tap(sessionId: string, x: number, y: number): Promise<void> {
    const udid = this.sessions.get(sessionId);
    if (!udid) {
      throw new Error(`Session not found: ${sessionId}`);
    }
    await this.executeCommand(`idb ui --udid ${udid} tap ${x} ${y}`);
  }

  async swipe(
    sessionId: string,
    startX: number,
    startY: number,
    endX: number,
    endY: number,
    duration: number = 100
  ): Promise<void> {
    const udid = this.sessions.get(sessionId);
    if (!udid) {
      throw new Error(`Session not found: ${sessionId}`);
    }
    await this.executeCommand(
      `idb ui --udid ${udid} swipe ${startX} ${startY} ${endX} ${endY} ${duration}`
    );
  }

  async takeScreenshot(sessionId: string, outputPath?: string): Promise<Buffer | string> {
    const udid = this.sessions.get(sessionId);
    if (!udid) {
      throw new Error(`Session not found: ${sessionId}`);
    }
    
    const tempPath = outputPath || path.join(process.cwd(), `screenshot_${Date.now()}.png`);
    await this.executeCommand(`idb screenshot --udid ${udid} ${tempPath}`);
    
    if (outputPath) {
      return outputPath;
    } else {
      const buffer = fs.readFileSync(tempPath);
      fs.unlinkSync(tempPath);
      return buffer;
    }
  }

  async getSystemLogs(sessionId: string, options?: { 
    bundle?: string;
    since?: Date;
    limit?: number;
  }): Promise<string> {
    const udid = this.sessions.get(sessionId);
    if (!udid) {
      throw new Error(`Session not found: ${sessionId}`);
    }
    
    let command = `idb log --udid ${udid}`;
    if (options?.bundle) command += ` --bundle ${options.bundle}`;
    if (options?.limit) command += ` --limit ${options.limit}`;
    return this.executeCommand(`${command} --timeout 5`);
  }

  async getAppLogs(sessionId: string, bundleId: string): Promise<string> {
    return this.getSystemLogs(sessionId, { bundle: bundleId });
  }

  async listSimulatorSessions(): Promise<string[]> {
    return Array.from(this.sessions.keys());
  }

  async isSimulatorBooted(sessionId: string): Promise<boolean> {
    const udid = this.sessions.get(sessionId);
    if (!udid) {
      throw new Error(`Session not found: ${sessionId}`);
    }
    const simulators = await this.listBootedSimulators();
    return simulators.some(sim => sim.udid === udid);
  }

  async isAppInstalled(sessionId: string, bundleId: string): Promise<boolean> {
    const udid = this.sessions.get(sessionId);
    if (!udid) {
      throw new Error(`Session not found: ${sessionId}`);
    }
    try {
      await this.executeCommand(`idb list-apps --udid ${udid} | grep "${bundleId}"`);
      return true;
    } catch (error) {
      return false;
    }
  }

  async focusSimulator(sessionId: string): Promise<void> {
    const udid = this.sessions.get(sessionId);
    if (!udid) {
      throw new Error(`Session not found: ${sessionId}`);
    }
    await this.executeCommand(`idb focus --udid ${udid}`);
  }

  async uninstallApp(sessionId: string, bundleId: string): Promise<void> {
    const udid = this.sessions.get(sessionId);
    if (!udid) {
      throw new Error(`Session not found: ${sessionId}`);
    }
    await this.executeCommand(`idb uninstall --udid ${udid} ${bundleId}`);
  }

  async listApps(sessionId: string): Promise<AppInfo[]> {
    const udid = this.sessions.get(sessionId);
    if (!udid) {
      throw new Error(`Session not found: ${sessionId}`);
    }
    const output = await this.executeCommand(`idb list-apps --udid ${udid} --json`);
    const apps = JSON.parse(output);
    return apps.map((app: any) => ({
      bundleId: app.bundle_id,
      name: app.name || app.bundle_id,
      installedPath: app.install_path
    }));
  }

  async pressButton(sessionId: string, button: ButtonType, duration?: number): Promise<void> {
    const udid = this.sessions.get(sessionId);
    if (!udid) {
      throw new Error(`Session not found: ${sessionId}`);
    }
    let command = `idb ui --udid ${udid} button ${button}`;
    if (duration) command += ` --duration ${duration}`;
    await this.executeCommand(command);
  }

  async inputText(sessionId: string, text: string): Promise<void> {
    const udid = this.sessions.get(sessionId);
    if (!udid) {
      throw new Error(`Session not found: ${sessionId}`);
    }
    const escapedText = text.replace(/"/g, '\\"');
    await this.executeCommand(`idb ui --udid ${udid} text "${escapedText}"`);
  }

  async pressKey(sessionId: string, keyCode: number, duration?: number): Promise<void> {
    const udid = this.sessions.get(sessionId);
    if (!udid) {
      throw new Error(`Session not found: ${sessionId}`);
    }
    let command = `idb ui --udid ${udid} key ${keyCode}`;
    if (duration) command += ` --duration ${duration}`;
    await this.executeCommand(command);
  }

  async pressKeySequence(sessionId: string, keyCodes: number[]): Promise<void> {
    const udid = this.sessions.get(sessionId);
    if (!udid) {
      throw new Error(`Session not found: ${sessionId}`);
    }
    const keyCodesStr = keyCodes.join(' ');
    await this.executeCommand(`idb ui --udid ${udid} key-sequence ${keyCodesStr}`);
  }

  async getDebugServerStatus(sessionId: string): Promise<{ running: boolean; port?: number; bundleId?: string; }> {
    const udid = this.sessions.get(sessionId);
    if (!udid) {
      throw new Error(`Session not found: ${sessionId}`);
    }
    try {
      const output = await this.executeCommand(`idb debugserver status --udid ${udid}`);
      if (output.includes("No debug server running")) {
        return { running: false };
      }
      const portMatch = output.match(/port: (\d+)/);
      const bundleMatch = output.match(/bundle_id: ([^\s]+)/);
      return {
        running: true,
        port: portMatch ? parseInt(portMatch[1], 10) : undefined,
        bundleId: bundleMatch ? bundleMatch[1] : undefined
      };
    } catch (error) {
      return { running: false };
    }
  }

  async listCrashLogs(sessionId: string, options?: {
    bundleId?: string;
    before?: Date;
    since?: Date;
  }): Promise<CrashLogInfo[]> {
    const udid = this.sessions.get(sessionId);
    if (!udid) {
      throw new Error(`Session not found: ${sessionId}`);
    }
    let command = `idb crash list --udid ${udid}`;
    if (options?.bundleId) command += ` --bundle-id ${options.bundleId}`;
    if (options?.before) command += ` --before ${options.before.toISOString()}`;
    if (options?.since) command += ` --since ${options.since.toISOString()}`;
    const output = await this.executeCommand(command);
    const lines = output.split('\n').filter(Boolean);
    return lines.map(line => {
      const parts = line.split(' - ');
      return {
        name: parts[0].trim(),
        bundleId: parts[1]?.trim(),
        date: new Date(parts[2]?.trim() || Date.now()),
        path: parts[3]?.trim() || ''
      };
    });
  }

  async getCrashLog(sessionId: string, crashName: string): Promise<string> {
    const udid = this.sessions.get(sessionId);
    if (!udid) {
      throw new Error(`Session not found: ${sessionId}`);
    }
    return this.executeCommand(`idb crash show --udid ${udid} ${crashName}`);
  }

  async deleteCrashLogs(sessionId: string, options: {
    crashNames?: string[];
    bundleId?: string;
    before?: Date;
    since?: Date;
    all?: boolean;
  }): Promise<void> {
    const udid = this.sessions.get(sessionId);
    if (!udid) {
      throw new Error(`Session not found: ${sessionId}`);
    }
    if (options.all) {
      await this.executeCommand(`idb crash delete --udid ${udid} --all`);
      return;
    }
    if (options.crashNames?.length) {
      for (const crashName of options.crashNames) {
        await this.executeCommand(`idb crash delete --udid ${udid} ${crashName}`);
      }
      return;
    }
    let command = `idb crash delete --udid ${udid}`;
    if (options.bundleId) command += ` --bundle-id ${options.bundleId}`;
    if (options.before) command += ` --before ${options.before.toISOString()}`;
    if (options.since) command += ` --since ${options.since.toISOString()}`;
    await this.executeCommand(command);
  }

  async installDylib(sessionId: string, dylibPath: string): Promise<void> {
    const udid = this.sessions.get(sessionId);
    if (!udid) {
      throw new Error(`Session not found: ${sessionId}`);
    }
    await this.executeCommand(`idb dylib install --udid ${udid} ${dylibPath}`);
  }

  async openUrl(sessionId: string, url: string): Promise<void> {
    const udid = this.sessions.get(sessionId);
    if (!udid) {
      throw new Error(`Session not found: ${sessionId}`);
    }
    await this.executeCommand(`idb open --udid ${udid} ${url}`);
  }

  async clearKeychain(sessionId: string): Promise<void> {
    const udid = this.sessions.get(sessionId);
    if (!udid) {
      throw new Error(`Session not found: ${sessionId}`);
    }
    await this.executeCommand(`idb clear_keychain --udid ${udid}`);
  }

  async setLocation(sessionId: string, latitude: number, longitude: number): Promise<void> {
    const udid = this.sessions.get(sessionId);
    if (!udid) {
      throw new Error(`Session not found: ${sessionId}`);
    }
    await this.executeCommand(`idb set_location --udid ${udid} ${latitude} ${longitude}`);
  }

  async addMedia(sessionId: string, mediaPaths: string[]): Promise<void> {
    const udid = this.sessions.get(sessionId);
    if (!udid) {
      throw new Error(`Session not found: ${sessionId}`);
    }
    const mediaPathsStr = mediaPaths.join(' ');
    await this.executeCommand(`idb add-media --udid ${udid} ${mediaPathsStr}`);
  }

  async approvePermissions(sessionId: string, bundleId: string, permissions: string[]): Promise<void> {
    const udid = this.sessions.get(sessionId);
    if (!udid) {
      throw new Error(`Session not found: ${sessionId}`);
    }
    const permissionsStr = permissions.join(' ');
    await this.executeCommand(`idb approve --udid ${udid} ${bundleId} ${permissionsStr}`);
  }

  async updateContacts(sessionId: string, dbPath: string): Promise<void> {
    const udid = this.sessions.get(sessionId);
    if (!udid) {
      throw new Error(`Session not found: ${sessionId}`);
    }
    await this.executeCommand(`idb contacts update --udid ${udid} ${dbPath}`);
  }
}

```

--------------------------------------------------------------------------------
/LICENSES/CC-BY-SA-4.0.txt:
--------------------------------------------------------------------------------

```
Attribution-ShareAlike 4.0 International

=======================================================================

Creative Commons Corporation ("Creative Commons") is not a law firm and
does not provide legal services or legal advice. Distribution of
Creative Commons public licenses does not create a lawyer-client or
other relationship. Creative Commons makes its licenses and related
information available on an "as-is" basis. Creative Commons gives no
warranties regarding its licenses, any material licensed under their
terms and conditions, or any related information. Creative Commons
disclaims all liability for damages resulting from their use to the
fullest extent possible.

Using Creative Commons Public Licenses

Creative Commons public licenses provide a standard set of terms and
conditions that creators and other rights holders may use to share
original works of authorship and other material subject to copyright
and certain other rights specified in the public license below. The
following considerations are for informational purposes only, are not
exhaustive, and do not form part of our licenses.

     Considerations for licensors: Our public licenses are
     intended for use by those authorized to give the public
     permission to use material in ways otherwise restricted by
     copyright and certain other rights. Our licenses are
     irrevocable. Licensors should read and understand the terms
     and conditions of the license they choose before applying it.
     Licensors should also secure all rights necessary before
     applying our licenses so that the public can reuse the
     material as expected. Licensors should clearly mark any
     material not subject to the license. This includes other CC-
     licensed material, or material used under an exception or
     limitation to copyright. More considerations for licensors:
    wiki.creativecommons.org/Considerations_for_licensors

     Considerations for the public: By using one of our public
     licenses, a licensor grants the public permission to use the
     licensed material under specified terms and conditions. If
     the licensor's permission is not necessary for any reason--for
     example, because of any applicable exception or limitation to
     copyright--then that use is not regulated by the license. Our
     licenses grant only permissions under copyright and certain
     other rights that a licensor has authority to grant. Use of
     the licensed material may still be restricted for other
     reasons, including because others have copyright or other
     rights in the material. A licensor may make special requests,
     such as asking that all changes be marked or described.
     Although not required by our licenses, you are encouraged to
     respect those requests where reasonable. More considerations
     for the public:
    wiki.creativecommons.org/Considerations_for_licensees

=======================================================================

Creative Commons Attribution-ShareAlike 4.0 International Public
License

By exercising the Licensed Rights (defined below), You accept and agree
to be bound by the terms and conditions of this Creative Commons
Attribution-ShareAlike 4.0 International Public License ("Public
License"). To the extent this Public License may be interpreted as a
contract, You are granted the Licensed Rights in consideration of Your
acceptance of these terms and conditions, and the Licensor grants You
such rights in consideration of benefits the Licensor receives from
making the Licensed Material available under these terms and
conditions.


Section 1 -- Definitions.

  a. Adapted Material means material subject to Copyright and Similar
     Rights that is derived from or based upon the Licensed Material
     and in which the Licensed Material is translated, altered,
     arranged, transformed, or otherwise modified in a manner requiring
     permission under the Copyright and Similar Rights held by the
     Licensor. For purposes of this Public License, where the Licensed
     Material is a musical work, performance, or sound recording,
     Adapted Material is always produced where the Licensed Material is
     synched in timed relation with a moving image.

  b. Adapter's License means the license You apply to Your Copyright
     and Similar Rights in Your contributions to Adapted Material in
     accordance with the terms and conditions of this Public License.

  c. BY-SA Compatible License means a license listed at
     creativecommons.org/compatiblelicenses, approved by Creative
     Commons as essentially the equivalent of this Public License.

  d. Copyright and Similar Rights means copyright and/or similar rights
     closely related to copyright including, without limitation,
     performance, broadcast, sound recording, and Sui Generis Database
     Rights, without regard to how the rights are labeled or
     categorized. For purposes of this Public License, the rights
     specified in Section 2(b)(1)-(2) are not Copyright and Similar
     Rights.

  e. Effective Technological Measures means those measures that, in the
     absence of proper authority, may not be circumvented under laws
     fulfilling obligations under Article 11 of the WIPO Copyright
     Treaty adopted on December 20, 1996, and/or similar international
     agreements.

  f. Exceptions and Limitations means fair use, fair dealing, and/or
     any other exception or limitation to Copyright and Similar Rights
     that applies to Your use of the Licensed Material.

  g. License Elements means the license attributes listed in the name
     of a Creative Commons Public License. The License Elements of this
     Public License are Attribution and ShareAlike.

  h. Licensed Material means the artistic or literary work, database,
     or other material to which the Licensor applied this Public
     License.

  i. Licensed Rights means the rights granted to You subject to the
     terms and conditions of this Public License, which are limited to
     all Copyright and Similar Rights that apply to Your use of the
     Licensed Material and that the Licensor has authority to license.

  j. Licensor means the individual(s) or entity(ies) granting rights
     under this Public License.

  k. Share means to provide material to the public by any means or
     process that requires permission under the Licensed Rights, such
     as reproduction, public display, public performance, distribution,
     dissemination, communication, or importation, and to make material
     available to the public including in ways that members of the
     public may access the material from a place and at a time
     individually chosen by them.

  l. Sui Generis Database Rights means rights other than copyright
     resulting from Directive 96/9/EC of the European Parliament and of
     the Council of 11 March 1996 on the legal protection of databases,
     as amended and/or succeeded, as well as other essentially
     equivalent rights anywhere in the world.

  m. You means the individual or entity exercising the Licensed Rights
     under this Public License. Your has a corresponding meaning.


Section 2 -- Scope.

  a. License grant.

       1. Subject to the terms and conditions of this Public License,
          the Licensor hereby grants You a worldwide, royalty-free,
          non-sublicensable, non-exclusive, irrevocable license to
          exercise the Licensed Rights in the Licensed Material to:

            a. reproduce and Share the Licensed Material, in whole or
               in part; and

            b. produce, reproduce, and Share Adapted Material.

       2. Exceptions and Limitations. For the avoidance of doubt, where
          Exceptions and Limitations apply to Your use, this Public
          License does not apply, and You do not need to comply with
          its terms and conditions.

       3. Term. The term of this Public License is specified in Section
          6(a).

       4. Media and formats; technical modifications allowed. The
          Licensor authorizes You to exercise the Licensed Rights in
          all media and formats whether now known or hereafter created,
          and to make technical modifications necessary to do so. The
          Licensor waives and/or agrees not to assert any right or
          authority to forbid You from making technical modifications
          necessary to exercise the Licensed Rights, including
          technical modifications necessary to circumvent Effective
          Technological Measures. For purposes of this Public License,
          simply making modifications authorized by this Section 2(a)
          (4) never produces Adapted Material.

       5. Downstream recipients.

            a. Offer from the Licensor -- Licensed Material. Every
               recipient of the Licensed Material automatically
               receives an offer from the Licensor to exercise the
               Licensed Rights under the terms and conditions of this
               Public License.

            b. Additional offer from the Licensor -- Adapted Material.
               Every recipient of Adapted Material from You
               automatically receives an offer from the Licensor to
               exercise the Licensed Rights in the Adapted Material
               under the conditions of the Adapter's License You apply.

            c. No downstream restrictions. You may not offer or impose
               any additional or different terms or conditions on, or
               apply any Effective Technological Measures to, the
               Licensed Material if doing so restricts exercise of the
               Licensed Rights by any recipient of the Licensed
               Material.

       6. No endorsement. Nothing in this Public License constitutes or
          may be construed as permission to assert or imply that You
          are, or that Your use of the Licensed Material is, connected
          with, or sponsored, endorsed, or granted official status by,
          the Licensor or others designated to receive attribution as
          provided in Section 3(a)(1)(A)(i).

  b. Other rights.

       1. Moral rights, such as the right of integrity, are not
          licensed under this Public License, nor are publicity,
          privacy, and/or other similar personality rights; however, to
          the extent possible, the Licensor waives and/or agrees not to
          assert any such rights held by the Licensor to the limited
          extent necessary to allow You to exercise the Licensed
          Rights, but not otherwise.

       2. Patent and trademark rights are not licensed under this
          Public License.

       3. To the extent possible, the Licensor waives any right to
          collect royalties from You for the exercise of the Licensed
          Rights, whether directly or through a collecting society
          under any voluntary or waivable statutory or compulsory
          licensing scheme. In all other cases the Licensor expressly
          reserves any right to collect such royalties.


Section 3 -- License Conditions.

Your exercise of the Licensed Rights is expressly made subject to the
following conditions.

  a. Attribution.

       1. If You Share the Licensed Material (including in modified
          form), You must:

            a. retain the following if it is supplied by the Licensor
               with the Licensed Material:

                 i. identification of the creator(s) of the Licensed
                    Material and any others designated to receive
                    attribution, in any reasonable manner requested by
                    the Licensor (including by pseudonym if
                    designated);

                ii. a copyright notice;

               iii. a notice that refers to this Public License;

                iv. a notice that refers to the disclaimer of
                    warranties;

                 v. a URI or hyperlink to the Licensed Material to the
                    extent reasonably practicable;

            b. indicate if You modified the Licensed Material and
               retain an indication of any previous modifications; and

            c. indicate the Licensed Material is licensed under this
               Public License, and include the text of, or the URI or
               hyperlink to, this Public License.

       2. You may satisfy the conditions in Section 3(a)(1) in any
          reasonable manner based on the medium, means, and context in
          which You Share the Licensed Material. For example, it may be
          reasonable to satisfy the conditions by providing a URI or
          hyperlink to a resource that includes the required
          information.

       3. If requested by the Licensor, You must remove any of the
          information required by Section 3(a)(1)(A) to the extent
          reasonably practicable.

  b. ShareAlike.

     In addition to the conditions in Section 3(a), if You Share
     Adapted Material You produce, the following conditions also apply.

       1. The Adapter's License You apply must be a Creative Commons
          license with the same License Elements, this version or
          later, or a BY-SA Compatible License.

       2. You must include the text of, or the URI or hyperlink to, the
          Adapter's License You apply. You may satisfy this condition
          in any reasonable manner based on the medium, means, and
          context in which You Share Adapted Material.

       3. You may not offer or impose any additional or different terms
          or conditions on, or apply any Effective Technological
          Measures to, Adapted Material that restrict exercise of the
          rights granted under the Adapter's License You apply.


Section 4 -- Sui Generis Database Rights.

Where the Licensed Rights include Sui Generis Database Rights that
apply to Your use of the Licensed Material:

  a. for the avoidance of doubt, Section 2(a)(1) grants You the right
     to extract, reuse, reproduce, and Share all or a substantial
     portion of the contents of the database;

  b. if You include all or a substantial portion of the database
     contents in a database in which You have Sui Generis Database
     Rights, then the database in which You have Sui Generis Database
     Rights (but not its individual contents) is Adapted Material,
     including for purposes of Section 3(b); and

  c. You must comply with the conditions in Section 3(a) if You Share
     all or a substantial portion of the contents of the database.

For the avoidance of doubt, this Section 4 supplements and does not
replace Your obligations under this Public License where the Licensed
Rights include other Copyright and Similar Rights.


Section 5 -- Disclaimer of Warranties and Limitation of Liability.

  a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
     EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
     AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
     ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
     IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
     WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
     PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
     ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
     KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
     ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.

  b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
     TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
     NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
     INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
     COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
     USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
     ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
     DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
     IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.

  c. The disclaimer of warranties and limitation of liability provided
     above shall be interpreted in a manner that, to the extent
     possible, most closely approximates an absolute disclaimer and
     waiver of all liability.


Section 6 -- Term and Termination.

  a. This Public License applies for the term of the Copyright and
     Similar Rights licensed here. However, if You fail to comply with
     this Public License, then Your rights under this Public License
     terminate automatically.

  b. Where Your right to use the Licensed Material has terminated under
     Section 6(a), it reinstates:

       1. automatically as of the date the violation is cured, provided
          it is cured within 30 days of Your discovery of the
          violation; or

       2. upon express reinstatement by the Licensor.

     For the avoidance of doubt, this Section 6(b) does not affect any
     right the Licensor may have to seek remedies for Your violations
     of this Public License.

  c. For the avoidance of doubt, the Licensor may also offer the
     Licensed Material under separate terms or conditions or stop
     distributing the Licensed Material at any time; however, doing so
     will not terminate this Public License.

  d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
     License.


Section 7 -- Other Terms and Conditions.

  a. The Licensor shall not be bound by any additional or different
     terms or conditions communicated by You unless expressly agreed.

  b. Any arrangements, understandings, or agreements regarding the
     Licensed Material not stated herein are separate from and
     independent of the terms and conditions of this Public License.


Section 8 -- Interpretation.

  a. For the avoidance of doubt, this Public License does not, and
     shall not be interpreted to, reduce, limit, restrict, or impose
     conditions on any use of the Licensed Material that could lawfully
     be made without permission under this Public License.

  b. To the extent possible, if any provision of this Public License is
     deemed unenforceable, it shall be automatically reformed to the
     minimum extent necessary to make it enforceable. If the provision
     cannot be reformed, it shall be severed from this Public License
     without affecting the enforceability of the remaining terms and
     conditions.

  c. No term or condition of this Public License will be waived and no
     failure to comply consented to unless expressly agreed to by the
     Licensor.

  d. Nothing in this Public License constitutes or may be interpreted
     as a limitation upon, or waiver of, any privileges and immunities
     that apply to the Licensor or You, including from the legal
     processes of any jurisdiction or authority.


=======================================================================

Creative Commons is not a party to its public
licenses. Notwithstanding, Creative Commons may elect to apply one of
its public licenses to material it publishes and in those instances
will be considered the “Licensor.” The text of the Creative Commons
public licenses is dedicated to the public domain under the CC0 Public
Domain Dedication. Except for the limited purpose of indicating that
material is shared under a Creative Commons public license or as
otherwise permitted by the Creative Commons policies published at
creativecommons.org/policies, Creative Commons does not authorize the
use of the trademark "Creative Commons" or any other trademark or logo
of Creative Commons without its prior written consent including,
without limitation, in connection with any unauthorized modifications
to any of its public licenses or any other arrangements,
understandings, or agreements concerning use of licensed material. For
the avoidance of doubt, this paragraph does not form part of the
public licenses.

Creative Commons may be contacted at creativecommons.org.


```