<!--
hoody-watch Subskill (sdk)
Auto-generated by Hoody Skills Generator
Generated: 2026-05-06T20:13:26.544Z
Model: mimo-v2.5-pro + fixer:mimo-v2.5-pro
Mode: sdk


Tokens: 5870

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

# hoody-watch Subskill

## Overview

The **hoody-watch** service provides real-time file system monitoring for Hoody Kit containers. It enables you to create watchers that observe specified filesystem paths and emit events when files are created, modified, or deleted.

### When to Use hoody-watch

- **Development hot-reload**: Watch source directories for changes to trigger rebuilds or restarts
- **Configuration monitoring**: Detect when config files are updated to trigger service reconfiguration
- **Log aggregation**: Monitor log directories for new entries
- **Build pipelines**: Watch for artifact generation to trigger downstream processes
- **Data ingestion**: Monitor drop folders for new files to process

### How It Fits Into Hoody Philosophy

hoody-watch embodies Hoody's principle of **declarative infrastructure**. Instead of writing custom file-watching scripts, you declare what paths to monitor and the service handles the rest. Combined with Hoody's automatic routing and authentication, watchers are securely accessible via standardized URLs without manual configuration.

### Service Architecture

```
┌─────────────���───────────────────────────────────────────┐
│                    hoody-watch Service                   │
├─────────────────────────────────────────────────────────┤
│  Watchers Registry    │    Event Stream Engine           │
│  - CRUD operations    │    - SSE (Server-Sent Events)    │
│  - Path configuration │    - WebSocket streaming         │
│                       │    - Historical event queries    │
└─────────────────────────────────────────────────────────┘
```

### Base URL

All requests use your service-specific base URL:

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

> **Note**: Replace placeholders with your actual Hoody project values. See the core SKILL.md for container creation and service discovery.

---

## Common Workflows

### Workflow 1: Create and Monitor a File Watcher

This is the fundamental workflow—create a watcher on specific paths and subscribe to its events.

```
import { HoodyClient } from '@hoody-ai/hoody-sdk'

const client = new HoodyClient({
  baseURL: 'https://api.hoody.icu',
  token: 'YOUR_TOKEN'
})

// Step 1: Create a watcher for source files
const watcher = await client.watch.watchers.create({
  paths: ['/app/src', '/app/config']
})

console.log(`Watcher created: ${watcher.id}`)

// Step 2: Verify the watcher is active
const details = await client.watch.watchers.get(watcher.id)
console.log(`Watcher status: ${details.status}`)
console.log(`Watching paths: ${details.paths.join(', ')}`)

// Step 3: Stream events in real-time via SSE
const eventStream = await client.watch.streams.streamSse(watcher.id)

// Process events as they arrive
for await (const event of eventStream) {
  console.log(`File changed: ${event.path} (${event.type})`)
}
```

### Workflow 2: List All Active Watchers

Retrieve and inspect all watchers in your service instance.

```
import { HoodyClient } from '@hoody-ai/hoody-sdk'

const client = new HoodyClient({
  baseURL: 'https://api.hoody.icu',
  token: 'YOUR_TOKEN'
})

// List watchers with pagination
const firstPage = await client.watch.watchers.list({
  page: 1,
  limit: 20
})

console.log(`Total watchers: ${firstPage.total}`)
console.log(`Watchers on this page: ${firstPage.items.length}`)

// Iterate through all watchers across pages
const allWatchers = await client.watch.watchers.listAll()
for (const watcher of allWatchers) {
  console.log(`- ${watcher.id}: ${watcher.paths.join(', ')}`)
}
```

### Workflow 3: Query Historical Events

Retrieve past events from a watcher for analysis or replay.

```
import { HoodyClient } from '@hoody-ai/hoody-sdk'

const client = new HoodyClient({
  baseURL: 'https://api.hoody.icu',
  token: 'YOUR_TOKEN'
})

const watcherId = 'your-watcher-id'

// Get recent events with pagination
const events = await client.watch.streams.listEvents(watcherId, {
  page: 1,
  limit: 50
})

console.log(`Total events: ${events.total}`)
for (const event of events.items) {
  console.log(`[${event.timestamp}] ${event.type}: ${event.path}`)
}

// Get events since a specific timestamp
const recentEvents = await client.watch.streams.listEvents(watcherId, {
  since_timestamp: '2025-01-15T10:00:00Z',
  limit: 100
})

// Collect all events across pages
const allEvents = await client.watch.streams.listEventsAll(watcherId)
console.log(`Collected ${allEvents.length} event batches`)
```

### Workflow 4: Health Check and Service Verification

Verify the watch service is operational before creating watchers.

```
import { HoodyClient } from '@hoody-ai/hoody-sdk'

const client = new HoodyClient({
  baseURL: 'https://api.hoody.icu',
  token: 'YOUR_TOKEN'
})

// Perform health check
const health = await client.watch.health.check()
console.log(`Service status: ${health.status}`)

if (health.status !== 'healthy') {
  throw new Error('Watch service is not healthy')
}

// Safe to proceed with watcher operations
const watcher = await client.watch.watchers.create({
  paths: ['/data/incoming']
})
```

### Workflow 5: Delete a Watcher

Clean up watchers that are no longer needed.

```
import { HoodyClient } from '@hoody-ai/hoody-sdk'

const client = new HoodyClient({
  baseURL: 'https://api.hoody.icu',
  token: 'YOUR_TOKEN'
})

const watcherId = 'watcher-to-remove'

// Verify watcher exists before deletion
const watcher = await client.watch.watchers.get(watcherId)
console.log(`Deleting watcher: ${watcher.id} (${watcher.paths.join(', ')})`)

// Delete the watcher
const result = await client.watch.watchers.delete(watcherId)
console.log(`Deletion result: ${result.success}`)
```

---

## Advanced Operations

### Advanced Workflow 1: WebSocket Streaming with Reconnection

Build a resilient event listener using WebSocket with automatic reconnection.

```
import { HoodyClient } from '@hoody-ai/hoody-sdk'

const client = new HoodyClient({
  baseURL: 'https://api.hoody.icu',
  token: 'YOUR_TOKEN'
})

const watcherId = 'your-watcher-id'
let lastEventId: number | undefined

async function connectWebSocket() {
  try {
    const wsStream = await client.watch.streams.streamWs(watcherId, {
      since_id: lastEventId
    })

    for await (const event of wsStream) {
      lastEventId = event.id
      await processEvent(event)
    }
  } catch (error) {
    console.error('WebSocket disconnected, reconnecting in 5s...')
    await new Promise(resolve => setTimeout(resolve, 5000))
    await connectWebSocket()
  }
}

async function processEvent(event: any) {
  console.log(`Processing: ${event.type} on ${event.path}`)
  // Add your event handling logic here
}

// Start the WebSocket listener
await connectWebSocket()
```

### Advanced Workflow 2: Async Iterator for Batch Processing

Use async iterators to process events in controlled batches.

```
import { HoodyClient } from '@hoody-ai/hoody-sdk'

const client = new HoodyClient({
  baseURL: 'https://api.hoody.icu',
  token: 'YOUR_TOKEN'
})

const watcherId = 'your-watcher-id'

// Create an async iterator for events
const eventIterator = client.watch.streams.listEventsIterator(watcherId, {
  limit: 100
})

let batch: any[] = []
const BATCH_SIZE = 10

for await (const event of eventIterator) {
  batch.push(event)

  if (batch.length >= BATCH_SIZE) {
    await processBatch(batch)
    batch = []
  }
}

// Process remaining events
if (batch.length > 0) {
  await processBatch(batch)
}

async function processBatch(events: any[]) {
  console.log(`Processing batch of ${events.length} events`)
  // Implement your batch processing logic
}
```

### Advanced Workflow 3: Multi-Watcher Orchestration

Create and manage multiple watchers for different monitoring needs.

```
import { HoodyClient } from '@hoody-ai/hoody-sdk'

const client = new HoodyClient({
  baseURL: 'https://api.hoody.icu',
  token: 'YOUR_TOKEN'
})

// Define watcher configurations
const watcherConfigs = [
  { name: 'source-watcher', paths: ['/app/src'] },
  { name: 'config-watcher', paths: ['/app/config', '/app/.env'] },
  { name: 'data-watcher', paths: ['/data/uploads', '/data/imports'] }
]

// Create all watchers
const watchers = await Promise.all(
  watcherConfigs.map(config =>
    client.watch.watchers.create({ paths: config.paths })
  )
)

console.log(`Created ${watchers.length} watchers`)

// Start SSE streams for all watchers
const streams = await Promise.all(
  watchers.map(watcher =>
    client.watch.streams.streamSse(watcher.id)
  )
)

// Merge and process events from all streams
async function* mergeStreams(...streams: AsyncIterable<any>[]) {
  for (const stream of streams) {
    yield* stream
  }
}

for await (const event of mergeStreams(...streams)) {
  console.log(`Event from watcher: ${event.path} - ${event.type}`)
}
```

### Error Recovery Pattern

Handle common errors gracefully when working with watchers.

```
import { HoodyClient } from '@hoody-ai/hoody-sdk'

const client = new HoodyClient({
  baseURL: 'https://api.hoody.icu',
  token: 'YOUR_TOKEN'
})

async function safeCreateWatcher(paths: string[]) {
  try {
    const watcher = await client.watch.watchers.create({ paths })
    return watcher
  } catch (error: any) {
    if (error.status === 409) {
      console.log('Watcher already exists for these paths')
      // List existing watchers to find the one
      const existing = await client.watch.watchers.listAll()
      return existing.find(w =>
        w.paths.length === paths.length &&
        w.paths.every(p => paths.includes(p))
      )
    }
    if (error.status === 400) {
      console.error('Invalid paths specified:', error.message)
      return null
    }
    throw error
  }
}

async function safeDeleteWatcher(id: string) {
  try {
    await client.watch.watchers.delete(id)
    return true
  } catch (error: any) {
    if (error.status === 404) {
      console.log('Watcher already deleted or not found')
      return true
    }
    throw error
  }
}
```

### Performance Considerations

- **Pagination**: Use `limit` parameter to control response size (default varies by service)
- **Streaming preference**: SSE/WebSocket for real-time needs; REST polling for infrequent checks
- **Event filtering**: Use `since_id` or `since_timestamp` to fetch only new events
- **Batch operations**: Use `listAll()` sparingly; prefer paginated `list()` for large datasets
- **Connection management**: Close SSE/WebSocket streams when no longer needed

---

## Quick Reference

### Essential Endpoints

| Operation | SDK Method | HTTP |
|-----------|------------|------|
| Health check | `client.watch.health.check()` | GET /api/v1/watch/health |
| List watchers | `client.watch.watchers.list()` | GET /watchers |
| Create watcher | `client.watch.watchers.create(data)` | POST /watchers |
| Get watcher | `client.watch.watchers.get(id)` | GET /watchers/{id} |
| Delete watcher | `client.watch.watchers.delete(id)` | DELETE /watchers/{id} |
| List events | `client.watch.streams.listEvents(id)` | GET /watchers/{id}/events |
| Stream SSE | `client.watch.streams.streamSse(id)` | GET /watchers/{id}/events/sse |
| Stream WS | `client.watch.streams.streamWs(id)` | GET /watchers/{id}/events/ws |

### Required Parameters

| Endpoint | Required Fields |
|----------|-----------------|
| POST /watchers | `paths` (array of strings) |
| GET/DELETE /watchers/{id} | `id` (path parameter) |
| GET /watchers/{id}/events* | `id` (path parameter) |

### Optional Parameters

| Endpoint | Optional Fields |
|----------|-----------------|
| GET /watchers | `page`, `limit` |
| GET /watchers/{id}/events | `since_id`, `since_timestamp`, `page`, `limit` |
| GET /watchers/{id}/events/sse | `since_id`, `since_timestamp` |
| GET /watchers/{id}/events/ws | `since_id`, `since_timestamp` |

### Typical Response Formats

**Watcher Object**:
```
{
  "id": "watch_abc123",
  "paths": ["/app/src", "/app/config"],
  "status": "active",
  "created_at": "2025-01-15T10:30:00Z"
}
```

**Event Object**:
```
{
  "id": 1234,
  "type": "modified",
  "path": "/app/src/index.ts",
  "timestamp": "2025-01-15T10:35:22Z"
}
```

**List Response**:
```
{
  "items": [],
  "total": 5,
  "page": 1,
  "limit": 20
}
```

### SDK Setup Reminder

```
import { HoodyClient } from '@hoody-ai/hoody-sdk'

const client = new HoodyClient({
  baseURL: 'https://api.hoody.icu',
  token: 'YOUR_TOKEN'
})
```

> **Important**: The `baseURL` for SDK initialization points to the Hoody API (`https://api.hoody.icu`), which handles authentication and routing to your specific watch service instance. Do not use the container URL directly with the SDK.