<!--
hoody-watch Subskill (sdk)
Auto-generated by Hoody Skills Generator
Generated: 2026-06-20T00:03:55.093Z
Model: mimo-v2.5-pro + fixer:z-ai/glm-5.1
Mode: sdk


Tokens: 6053

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

# hoody-watch Subskill

## Overview

**hoody-watch** is a filesystem monitoring service for Hoody Kit containers. It enables you to create watchers that monitor specified filesystem paths for changes (file creation, modification, deletion) and receive real-time event notifications via SSE (Server-Sent Events) or WebSocket connections.

### When to Use hoody-watch

- **Configuration monitoring**: Watch config files for runtime changes
- **Build output tracking**: Monitor build directories for new artifacts
- **Log file monitoring**: Detect new log entries or log rotation
- **State file observation**: Track application state changes on disk
- **Development hot-reload**: Watch source files for changes during development

### How It Fits Into Hoody Philosophy

hoody-watch follows Hoody's container-first approach by providing filesystem awareness directly within your running containers. Instead of requiring external monitoring tools or host-level filesystem access, you declaratively specify which paths to watch and receive standardized event streams. This integrates naturally with Hoody's authentication, routing, and multi-tenant isolation—your watchers are scoped to your container instance and accessible through the standard Hoody Kit service URL pattern.

---

## Common Workflows

### Workflow 1: Health Check

Verify the hoody-watch service is running and responsive.

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

const client = new HoodyClient({
  baseURL: 'https://{projectId}-{containerId}-watch-{serviceId}.{node}.containers.hoody.com',
  token: 'YOUR_TOKEN'
})

const health = await client.watch.health.check()
console.log(health)
```

> **Note:** All subsequent examples assume this `client` instance. Import and setup lines are omitted for brevity.

Expected response:

```
{
  "status": "ok"
}
```

### Workflow 2: Create a Filesystem Watcher

Set up a watcher to monitor one or more filesystem paths.

```
const watcher = await client.watch.watchers.create({
  paths: ['/app/config', '/var/log/application.log']
})

console.log(watcher)
```

Expected response:

```
{
  "id": "w_abc123def456",
  "paths": ["/app/config", "/var/log/application.log"],
  "status": "active",
  "created_at": "2025-01-15T10:30:00Z"
}
```

Save the returned `id` for all subsequent operations.

### Workflow 3: List All Active Watchers

Retrieve all watchers currently configured in your container.

```
const watchers = await client.watch.watchers.list()
console.log(watchers)
```

For paginated results, use explicit parameters:

```
const watchers = await client.watch.watchers.list({
  page: 1,
  limit: 20
})
```

To automatically collect all pages into a single array:

```
const allWatchers = await client.watch.watchers.listAll()
```

To iterate asynchronously without loading all pages into memory:

```
const iterator = client.watch.watchers.listIterator()

for await (const page of iterator) {
  console.log(page)
}
```

### Workflow 4: Retrieve a Specific Watcher

Get details for a single watcher by its ID.

```
const watcher = await client.watch.watchers.get({ id: 'w_abc123def456' })
console.log(watcher)
```

Expected response:

```
{
  "id": "w_abc123def456",
  "paths": ["/app/config", "/var/log/application.log"],
  "status": "active",
  "created_at": "2025-01-15T10:30:00Z"
}
```

### Workflow 5: List Historical Events

Query past filesystem events for a watcher with pagination and filtering.

```
const events = await client.watch.streams.listEvents({ id: 'w_abc123def456' })
console.log(events)
```

Filter events since a specific event ID:

```
const events = await client.watch.streams.listEvents('w_abc123def456', {
  since_id: 42
})
```

Filter events since a specific timestamp:

```
const events = await client.watch.streams.listEvents('w_abc123def456', {
  since_timestamp: '2025-01-15T10:00:00Z'
})
```

Paginate through results:

```
const events = await client.watch.streams.listEvents('w_abc123def456', {
  page: 1,
  limit: 50
})
```

### Workflow 6: Delete a Watcher

Remove a watcher when it is no longer needed.

```
const result = await client.watch.watchers.delete({ id: 'w_abc123def456' })
console.log(result)
```

Expected response:

```
{
  "id": "w_abc123def456",
  "deleted": true
}
```

Always verify deletion by attempting to retrieve the watcher:

```
try {
  await client.watch.watchers.get({ id: 'w_abc123def456' })
  console.log('Watcher still exists')
} catch (error) {
  if (error.status === 404) {
    console.log('Watcher successfully deleted')
  } else {
    console.error('Unexpected error verifying deletion:', error)
  }
}
```

---

## Advanced Operations

### Real-Time Event Streaming via SSE

Subscribe to filesystem events in real-time using Server-Sent Events.

```
const eventStream = await client.watch.streams.streamSse({ id: 'w_abc123def456' })
```

Resume from a specific event to avoid gaps after reconnection:

```
const eventStream = await client.watch.streams.streamSse('w_abc123def456', {
  since_id: 100
})
```

Resume from a timestamp:

```
const eventStream = await client.watch.streams.streamSse('w_abc123def456', {
  since_timestamp: '2025-01-15T10:00:00Z'
})
```

### Real-Time Event Streaming via WebSocket

Subscribe to filesystem events using WebSocket for bidirectional communication.

```
await client.watch.streams.streamWs({ id: 'w_abc123def456' })
```

Resume from a specific event:

```
await client.watch.streams.streamWs('w_abc123def456', {
  since_id: 100
})
```

Resume from a timestamp:

```
await client.watch.streams.streamWs('w_abc123def456', {
  since_timestamp: '2025-01-15T10:00:00Z'
})
```

### Complete Lifecycle: Watch, Stream, and Cleanup

A full workflow that creates a watcher, streams events, and cleans up.

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

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

// Step 2: Start streaming events
const eventStream = await client.watch.streams.streamSse(watcher.id)

// Step 3: Process events (implementation depends on your SSE client)
// eventStream.on('event', (event) => { ... })

// Step 4: When done, clean up
await client.watch.watchers.delete(watcher.id)
```

### Iterating Over All Historical Events

Collect all pages of events automatically without manual pagination.

```
const allEvents = await client.watch.streams.listEventsAll({ id: 'w_abc123def456' })
```

Filter across all pages:

```
const allEvents = await client.watch.streams.listEventsAll('w_abc123def456', {
  since_timestamp: '2025-01-15T00:00:00Z'
})
```

For memory-efficient iteration:

```
const iterator = client.watch.streams.listEventsIterator({ id: 'w_abc123def456' })

for await (const page of iterator) {
  for (const event of page.events) {
    console.log(event)
  }
}
```

### Error Recovery Pattern

Gracefully handle watcher failures and re-establish monitoring.

```
async function watchWithErrorRecovery(paths: string[]) {
  let lastEventId: number | null = null

  while (true) {
    try {
      const watcher = await client.watch.watchers.create({ paths })

      const eventStream = await client.watch.streams.streamSse(
        watcher.id,
        lastEventId ? { since_id: lastEventId } : undefined
      )

      // Process stream until disconnection
      for await (const event of eventStream) {
        lastEventId = event.id
      }

    } catch (error) {
      console.error('Watch error, retrying in 5 seconds:', error)
      await new Promise(resolve => setTimeout(resolve, 5000))
    }
  }
}
```

### Performance Considerations

1. **Path specificity**: Watch only necessary paths rather than broad directories to reduce event volume
2. **Event filtering**: Use `since_id` or `since_timestamp` parameters to avoid re-processing old events
3. **Pagination size**: Use appropriate `limit` values when listing events to balance memory and request overhead
4. **Cleanup**: Always delete watchers when no longer needed to free resources
5. **SSE vs WebSocket**: Use SSE for unidirectional streaming; use WebSocket only when bidirectional communication is required

---

## Quick Reference

### Endpoints

| Method | Path | SDK Method |
|--------|------|------------|
| GET | `/api/v1/watch/health` | `client.watch.health.check()` |
| GET | `/watchers` | `client.watch.watchers.list()` |
| POST | `/watchers` | `client.watch.watchers.create(data)` |
| GET | `/watchers/{id}` | `client.watch.watchers.get(id)` |
| DELETE | `/watchers/{id}` | `client.watch.watchers.delete(id)` |
| GET | `/watchers/{id}/events` | `client.watch.streams.listEvents(id)` |
| GET | `/watchers/{id}/events/sse` | `client.watch.streams.streamSse(id)` |
| GET | `/watchers/{id}/events/ws` | `client.watch.streams.streamWs(id)` |

### Essential Parameters

**POST `/watchers` (Create Watcher)**
- `paths` (array, **required**): Absolute or relative filesystem paths to watch

**GET `/watchers/{id}/events` (List Events)**
- `since_id` (integer, optional): Return events after this event ID
- `since_timestamp` (string, optional): Return events after this ISO 8601 timestamp
- `page` (integer, optional): Page number for pagination
- `limit` (integer, optional): Number of results per page

**GET `/watchers` (List Watchers)**
- `page` (integer, optional): Page number for pagination
- `limit` (integer, optional): Number of results per page

### Typical Response Formats

**Watcher Object:**

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

**Delete Response:**

```
{
  "id": "w_abc123def456",
  "deleted": true
}
```

**Event History Response:**

```
{
  "events": [
    {
      "id": 1,
      "watcher_id": "w_abc123def456",
      "path": "/app/config/settings.json",
      "type": "modified",
      "timestamp": "2025-01-15T10:35:00Z"
    }
  ],
  "page": 1,
  "limit": 50,
  "total": 1
}
```

**Health Check Response:**

```
{
  "status": "ok"
}
```

### Service Base URL Pattern

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

Replace `{projectId}`, `{containerId}`, `{serviceId}`, and `{node}` with your actual Hoody Kit service values. See the core SKILL.md for container creation and service discovery.