<!--
hoody-app Subskill (http)
Auto-generated by Hoody Skills Generator
Generated: 2026-05-06T18:28:00.755Z
Model: mimo-v2.5-pro + fixer:mimo-v2.5-pro
Mode: http


Tokens: 10530

DO NOT EDIT MANUALLY - Changes will be overwritten on next generation
-->

# hoody-app Subskill

## Overview

The hoody-app service is Hoody's application execution engine. It provides a unified interface for discovering, configuring, and running applications from multiple package sources. Think of it as a smart launcher that resolves human-friendly app names into executable commands.

### When to Use hoody-app

- **Searching for applications** across configured package sources
- **Running applications** with automatic resolution and terminal delegation
- **Managing package sources** (npm, pip, cargo, brew, etc.)
- **Creating profiles** for different environments or preferences
- **Saving recipes** as reusable selector templates
- **Batch operations** for processing multiple app requests

### How It Fits Into Hoody Philosophy

Hoody-app embodies the "configure once, run anywhere" principle. You define your sources and preferences once, then use simple selectors to run any application. The service handles resolution, version selection, and command generation automatically.

### Base URL Pattern

```
https://{projectId}-{containerId}-app-{serviceId}.{node}.containers.hoody.icu
```

**Example**: `https://proj123-ctr456-app-svc789.us-east-1.containers.hoody.icu`

---

## Common Workflows

### 1. Health Check

Verify the service is running:

```
curl -s "https://{BASE_URL}/api/v1/run/health"
```

**Expected Response**:
```
{
  "status": "healthy",
  "service": "hoody-app",
  "version": "1.0.0",
  "timestamp": "2025-01-15T10:30:00Z",
  "uptime": 86400,
  "dependencies": {
    "database": "connected",
    "cache": "connected"
  },
  "metrics": {
    "requests_total": 1500,
    "active_jobs": 2
  },
  "environment": "production"
}
```

### 2. Search for Applications

Find available applications matching a query:

```
curl -s "https://{BASE_URL}/api/v1/run/search?app=react"
```

**Expected Response**:
```
{
  "set_id": "search-abc123",
  "candidates": [
    {
      "rank": 1,
      "app": "create-react-app",
      "version": "5.0.1",
      "source": "npm",
      "description": "Create React apps with no build configuration"
    },
    {
      "rank": 2,
      "app": "react-scripts",
      "version": "5.0.1",
      "source": "npm",
      "description": "Configuration and scripts for Create React App"
    }
  ],
  "total": 2,
  "query": "react"
}
```

### 3. Run an Application (GET)

Execute an application using query parameters:

```
curl -s "https://{BASE_URL}/api/v1/run/run?app=create-react-app"
```

**Expected Response**:
```
{
  "command": "npx create-react-app",
  "resolved_app": "create-react-app",
  "version": "5.0.1",
  "source": "npm",
  "terminal_id": null,
  "execution_mode": "command_only"
}
```

### 4. Run an Application (POST)

Execute with a JSON body for programmatic clients:

```
curl -s -X POST "https://{BASE_URL}/api/v1/run/run" \
  -H "Content-Type: application/json" \
  -d '{
  "app": "create-react-app"
}'
```

**Expected Response**:
```
{
  "command": "npx create-react-app",
  "resolved_app": "create-react-app",
  "version": "5.0.1",
  "source": "npm",
  "terminal_id": null,
  "execution_mode": "command_only"
}
```

### 5. Path-Based Execution

Use clean, bookmarkable URLs:

```
curl -s "https://{BASE_URL}/api/v1/run/go/create-react-app"
```

**Expected Response**:
```
{
  "command": "npx create-react-app",
  "resolved_app": "create-react-app",
  "version": "5.0.1",
  "source": "npm",
  "path_segments": ["create-react-app"]
}
```

### 6. Terminal-Anchored Execution

Specify both terminal and app in the path:

```
curl -s "https://{BASE_URL}/api/v1/run/t/term-123/go/create-react-app"
```

**Expected Response**:
```
{
  "command": "npx create-react-app",
  "resolved_app": "create-react-app",
  "version": "5.0.1",
  "source": "npm",
  "terminal_id": "term-123",
  "execution_mode": "terminal_delegated"
}
```

### 7. Preflight Check

Validate an execution plan without running:

```
curl -s -X POST "https://{BASE_URL}/api/v1/run/preflight" \
  -H "Content-Type: application/json" \
  -d '{
  "app": "create-react-app"
}'
```

**Expected Response**:
```
{
  "resolved_app": "create-react-app",
  "version": "5.0.1",
  "source": "npm",
  "command": "npx create-react-app",
  "dependencies": ["node", "npm"],
  "estimated_duration": "2-5 minutes",
  "warnings": []
}
```

### 8. List Package Sources

View all configured sources:

```
curl -s "https://{BASE_URL}/api/v1/run/sources"
```

**Expected Response**:
```
{
  "sources": [
    {
      "source_id": "npm-registry",
      "source_type": "npm",
      "provider": "npmjs",
      "enabled": true,
      "priority": 100,
      "config": {
        "registry": "https://registry.npmjs.org"
      }
    },
    {
      "source_id": "pypi-registry",
      "source_type": "pip",
      "provider": "pypi",
      "enabled": true,
      "priority": 90,
      "config": {
        "index_url": "https://pypi.org/simple"
      }
    }
  ],
  "total": 2
}
```

### 9. Add a Package Source

Register a new source:

```
curl -s -X POST "https://{BASE_URL}/api/v1/run/sources" \
  -H "Content-Type: application/json" \
  -d '{
  "source_id": "custom-registry",
  "source_type": "npm",
  "provider": "custom",
  "enabled": true,
  "priority": 50,
  "pin": {
    "url": "https://custom-registry.example.com"
  }
}'
```

**Expected Response**:
```
{
  "source_id": "custom-registry",
  "source_type": "npm",
  "provider": "custom",
  "enabled": true,
  "priority": 50,
  "pin": {
    "url": "https://custom-registry.example.com"
  },
  "created_at": "2025-01-15T10:30:00Z"
}
```

### 10. Sync a Source

Trigger synchronization for a specific source:

```
curl -s -X POST "https://{BASE_URL}/api/v1/run/sources/npm-registry/sync"
```

**Expected Response**:
```
{
  "job_id": "sync-job-abc123",
  "source_id": "npm-registry",
  "status": "queued",
  "message": "Sync job queued successfully"
}
```

### 11. Check Job Status

Poll for async job completion:

```
curl -s "https://{BASE_URL}/api/v1/run/jobs/sync-job-abc123?wait=done&timeout_ms=30000"
```

**Expected Response**:
```
{
  "job_id": "sync-job-abc123",
  "status": "completed",
  "progress": 100,
  "result": {
    "packages_synced": 15000,
    "duration_ms": 45000
  },
  "created_at": "2025-01-15T10:30:00Z",
  "completed_at": "2025-01-15T10:30:45Z"
}
```

### 12. List Profiles

View all user profiles:

```
curl -s "https://{BASE_URL}/api/v1/run/profiles"
```

**Expected Response**:
```
{
  "profiles": [
    {
      "name": "default",
      "description": "Default profile",
      "sources_mode": "all",
      "selected": true
    },
    {
      "name": "minimal",
      "description": "Minimal sources only",
      "sources_mode": "selected",
      "selected": false
    }
  ],
  "total": 2
}
```

### 13. Create a Profile

Define a new profile with source overrides:

```
curl -s -X POST "https://{BASE_URL}/api/v1/run/profiles" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "production",
  "description": "Production environment profile",
  "sources_mode": "selected",
  "sources": [
    {
      "source_id": "npm-registry",
      "enabled": true,
      "priority": 100
    }
  ]
}'
```

**Expected Response**:
```
{
  "name": "production",
  "description": "Production environment profile",
  "sources_mode": "selected",
  "sources": [
    {
      "source_id": "npm-registry",
      "enabled": true,
      "priority": 100
    }
  ],
  "created_at": "2025-01-15T10:30:00Z"
}
```

### 14. Select a Profile

Activate a profile:

```
curl -s -X POST "https://{BASE_URL}/api/v1/run/profiles/production/select"
```

**Expected Response**:
```
{
  "selected_profile": "production",
  "message": "Profile 'production' is now active"
}
```

### 15. List Recipes

View saved selector templates:

```
curl -s "https://{BASE_URL}/api/v1/run/recipes"
```

**Expected Response**:
```
{
  "recipes": [
    {
      "name": "react-app",
      "description": "Create a new React application",
      "selector": {
        "app": "create-react-app"
      },
      "created_at": "2025-01-10T08:00:00Z"
    }
  ],
  "total": 1
}
```

### 16. Create a Recipe

Save a reusable selector template:

```
curl -s -X POST "https://{BASE_URL}/api/v1/run/recipes" \
  -H "Content-Type: application/json" \
  -d '{
  "name": "vue-app",
  "description": "Create a new Vue.js application",
  "selector": {
    "app": "@vue/cli"
  }
}'
```

**Expected Response**:
```
{
  "name": "vue-app",
  "description": "Create a new Vue.js application",
  "selector": {
    "app": "@vue/cli"
  },
  "created_at": "2025-01-15T10:30:00Z"
}
```

### 17. Run a Recipe

Execute a saved recipe:

```
curl -s -X POST "https://{BASE_URL}/api/v1/run/recipes/vue-app/run"
```

**Expected Response**:
```
{
  "command": "npx @vue/cli",
  "resolved_app": "@vue/cli",
  "version": "5.0.8",
  "source": "npm",
  "recipe_name": "vue-app",
  "execution_mode": "command_only"
}
```

### 18. Get Full Configuration

Retrieve the complete runtime configuration:

```
curl -s "https://{BASE_URL}/api/v1/run/config"
```

**Expected Response**:
```
{
  "sources": [
    {
      "source_id": "npm-registry",
      "source_type": "npm",
      "provider": "npmjs",
      "enabled": true,
      "priority": 100
    }
  ],
  "profiles": [
    {
      "name": "default",
      "selected": true
    }
  ],
  "selected_profile": "default",
  "recipes": [
    {
      "name": "react-app"
    }
  ]
}
```

---

## Advanced Operations

### 19. Paged Search

Handle large result sets with cursor-based pagination:

```
curl -s -X POST "https://{BASE_URL}/api/v1/run/search/paged" \
  -H "Content-Type: application/json" \
  -d '{
  "selector": {
    "app": "react"
  },
  "page_size": 10,
  "cursor": null
}'
```

**Expected Response**:
```
{
  "set_id": "paged-xyz789",
  "candidates": [
    {
      "rank": 1,
      "app": "create-react-app",
      "version": "5.0.1",
      "source": "npm"
    }
  ],
  "page_size": 10,
  "cursor": "eyJyYW5rIjoxMH0=",
  "has_more": true,
  "total_estimated": 150
}
```

**Next Page**:
```
curl -s -X POST "https://{BASE_URL}/api/v1/run/search/paged" \
  -H "Content-Type: application/json" \
  -d '{
  "selector": {
    "app": "react"
  },
  "page_size": 10,
  "cursor": "eyJyYW5rIjoxMH0="
}'
```

### 20. Async Search Job

Queue a background search for large result sets:

```
curl -s -X POST "https://{BASE_URL}/api/v1/run/search/jobs" \
  -H "Content-Type: application/json" \
  -d '{
  "app": "react"
}'
```

**Expected Response**:
```
{
  "job_id": "search-job-def456",
  "status": "queued",
  "message": "Search job queued successfully"
}
```

**Poll for completion**:
```
curl -s "https://{BASE_URL}/api/v1/run/jobs/search-job-def456?wait=done&timeout_ms=60000"
```

### 21. Batch Operations

Process multiple requests in a single call:

```
curl -s -X POST "https://{BASE_URL}/api/v1/run/batch" \
  -H "Content-Type: application/json" \
  -d '{
  "items": [
    {
      "request_id": "req-001",
      "mode": "search",
      "selector": {
        "app": "react"
      }
    },
    {
      "request_id": "req-002",
      "mode": "run",
      "selector": {
        "app": "vue"
      }
    },
    {
      "request_id": "req-003",
      "mode": "search",
      "selector": {
        "app": "angular"
      }
    }
  ]
}'
```

**Expected Response**:
```
{
  "results": [
    {
      "request_id": "req-001",
      "status": "success",
      "data": {
        "candidates": [
          {
            "rank": 1,
            "app": "create-react-app",
            "version": "5.0.1"
          }
        ]
      }
    },
    {
      "request_id": "req-002",
      "status": "success",
      "data": {
        "command": "npx @vue/cli",
        "resolved_app": "@vue/cli"
      }
    },
    {
      "request_id": "req-003",
      "status": "success",
      "data": {
        "candidates": [
          {
            "rank": 1,
            "app": "@angular/cli",
            "version": "17.0.0"
          }
        ]
      }
    }
  ],
  "total": 3,
  "successful": 3,
  "failed": 0
}
```

### 22. Sync All Sources

Trigger synchronization for all enabled sources:

```
curl -s -X POST "https://{BASE_URL}/api/v1/run/sources/sync"
```

**Expected Response**:
```
{
  "job_id": "sync-all-ghi789",
  "status": "queued",
  "message": "Sync job queued for all enabled sources"
}
```

**Monitor progress**:
```
curl -s "https://{BASE_URL}/api/v1/run/jobs/sync-all-ghi789?wait=done&timeout_ms=120000"
```

### 23. Source Diagnostics

Get runtime health data for a source:

```
curl -s "https://{BASE_URL}/api/v1/run/sources/npm-registry/diagnostics"
```

**Expected Response**:
```
{
  "source_id": "npm-registry",
  "status": "healthy",
  "last_sync": "2025-01-15T10:00:00Z",
  "last_search": "2025-01-15T10:30:00Z",
  "packages_indexed": 1500000,
  "recent_failures": [],
  "latency_ms": 45
}
```

### 24. Update a Source

Partially update source configuration:

```
curl -s -X PATCH "https://{BASE_URL}/api/v1/run/sources/npm-registry" \
  -H "Content-Type: application/json" \
  -d '{
  "priority": 200,
  "enabled": true
}'
```

**Expected Response**:
```
{
  "source_id": "npm-registry",
  "source_type": "npm",
  "provider": "npmjs",
  "enabled": true,
  "priority": 200,
  "updated_at": "2025-01-15T10:35:00Z"
}
```

### 25. Update a Profile

Modify profile settings:

```
curl -s -X PATCH "https://{BASE_URL}/api/v1/run/profiles/production" \
  -H "Content-Type: application/json" \
  -d '{
  "description": "Updated production profile",
  "sources_mode": "all"
}'
```

**Expected Response**:
```
{
  "name": "production",
  "description": "Updated production profile",
  "sources_mode": "all",
  "updated_at": "2025-01-15T10:40:00Z"
}
```

### 26. Update a Recipe

Modify a saved recipe:

```
curl -s -X PATCH "https://{BASE_URL}/api/v1/run/recipes/vue-app" \
  -H "Content-Type: application/json" \
  -d '{
  "description": "Updated Vue.js recipe with latest version",
  "selector": {
    "app": "@vue/cli",
    "version": "latest"
  }
}'
```

**Expected Response**:
```
{
  "name": "vue-app",
  "description": "Updated Vue.js recipe with latest version",
  "selector": {
    "app": "@vue/cli",
    "version": "latest"
  },
  "updated_at": "2025-01-15T10:45:00Z"
}
```

### 27. Search a Recipe

Resolve a recipe to candidates:

```
curl -s -X POST "https://{BASE_URL}/api/v1/run/recipes/vue-app/search"
```

**Expected Response**:
```
{
  "recipe_name": "vue-app",
  "candidates": [
    {
      "rank": 1,
      "app": "@vue/cli",
      "version": "5.0.8",
      "source": "npm"
    }
  ],
  "total": 1
}
```

### 28. Delete Operations

**Delete a source**:
```
curl -s -X DELETE "https://{BASE_URL}/api/v1/run/sources/custom-registry"
```

**Expected Response**: HTTP 204 No Content

**Delete a profile**:
```
curl -s -X DELETE "https://{BASE_URL}/api/v1/run/profiles/production"
```

**Expected Response**: HTTP 204 No Content

**Delete a recipe**:
```
curl -s -X DELETE "https://{BASE_URL}/api/v1/run/recipes/vue-app"
```

**Expected Response**: HTTP 204 No Content

### 29. Get OpenAPI Specification

Retrieve the API documentation:

**YAML format**:
```
curl -s "https://{BASE_URL}/api/v1/run/openapi.yaml"
```

**JSON format**:
```
curl -s "https://{BASE_URL}/api/v1/run/openapi.json"
```

---

## Error Recovery Patterns

### Common Error Responses

**404 Not Found**:
```
{
  "error": "not_found",
  "message": "Source 'invalid-source' not found",
  "status": 404
}
```

**400 Bad Request**:
```
{
  "error": "validation_error",
  "message": "Missing required field: app",
  "status": 400
}
```

**409 Conflict**:
```
{
  "error": "conflict",
  "message": "Profile 'production' already exists",
  "status": 409
}
```

### Recovery Strategies

1. **Source not found**: Verify source_id exists via `GET /api/v1/run/sources`
2. **Profile not found**: Check available profiles via `GET /api/v1/run/profiles`
3. **Recipe not found**: List recipes via `GET /api/v1/run/recipes`
4. **Job timeout**: Increase `timeout_ms` parameter or poll again
5. **Sync failure**: Check diagnostics via `GET /api/v1/run/sources/{source_id}/diagnostics`

---

## Performance Considerations

1. **Use paged search** for large result sets instead of unbounded searches
2. **Leverage async jobs** for long-running operations (sync, large searches)
3. **Cache set_id** from search results for subsequent operations
4. **Set appropriate timeouts** based on operation complexity:
   - Health checks: 5-10 seconds
   - Simple searches: 30 seconds
   - Sync operations: 120+ seconds
5. **Use batch operations** when processing multiple requests to reduce HTTP overhead

---

## Quick Reference

### Most Common Endpoints

| Operation | Method | Path | Description |
|-----------|--------|------|-------------|
| Health Check | GET | `/api/v1/run/health` | Service health status |
| Search Apps | GET | `/api/v1/run/search?app={query}` | Find applications |
| Run App | POST | `/api/v1/run/run` | Execute an application |
| List Sources | GET | `/api/v1/run/sources` | View package sources |
| List Profiles | GET | `/api/v1/run/profiles` | View user profiles |
| List Recipes | GET | `/api/v1/run/recipes` | View saved recipes |
| Get Config | GET | `/api/v1/run/config` | Full runtime config |

### Essential Parameters

| Parameter | Type | Description |
|-----------|------|-------------|
| `app` | string | Application name or query |
| `source_id` | string | Unique source identifier |
| `profile` | string | Profile name |
| `name` | string | Recipe name |
| `job_id` | string | Async job identifier |
| `cursor` | string | Pagination cursor |
| `wait` | string | Long-poll mode (`done`) |
| `timeout_ms` | integer | Poll timeout in milliseconds |

### Typical Response Formats

**Success Response**:
```
{
  "status": "success",
  "data": {}
}
```

**List Response**:
```
{
  "items": [],
  "total": 10,
  "page": 1
}
```

**Job Response**:
```
{
  "job_id": "job-123",
  "status": "queued|running|completed|failed",
  "progress": 50
}
```

**Error Response**:
```
{
  "error": "error_code",
  "message": "Human-readable description",
  "status": 400
}
```

---

## Schema Validation Checklist

Before making any API call, verify:

- ✅ All required fields are included in request body
- ✅ Field types match schema (string, integer, boolean, object, array)
- ✅ Enum values are valid where specified
- ✅ JSON is valid (no trailing commas, proper quoting)
- ✅ Base URL follows the Hoody Kit pattern
- ✅ Paths match the endpoint inventory exactly
- ✅ Use `-s` flag with curl to suppress progress output

---

## Additional Resources

- **OpenAPI Spec**: `GET /api/v1/run/openapi.yaml` or `/api/v1/run/openapi.json`
- **Health Status**: `GET /api/v1/run/health`
- **Full Configuration**: `GET /api/v1/run/config`

For container creation and service discovery, refer to the core SKILL.md documentation.