npm package: @specterorg/sdk
Description: Lightweight SDK for sending user feedback signals to Specter
Latest version: 0.3.0

# @specterorg/sdk

Lightweight TypeScript client for sending signals to [Specter](https://specter.draftlabs.org) — the automated learning system for product improvements.

Zero dependencies. Works in Node.js 18+ and all modern browsers. Signals are auto-batched and flushed on a configurable interval.

## Install

```bash
npm install @specterorg/sdk
```

## Quick Start

```typescript
import Specter from '@specterorg/sdk';

const specter = new Specter({
  apiKey: 'spk_your_product_api_key',
});

// Track any signal type relevant to your product
specter.track('thumbs_up', { context: 'chat-response' });
specter.track('task_completed', { duration: 45, assignee: 'alice' });
specter.track('swap_failed', { pair: 'ETH/USDC', error: 'slippage' });
specter.track('error', { endpoint: '/api/checkout', status: 500 });

// Signals are batched and sent automatically every 5 seconds
// or when 10 signals accumulate — whichever comes first.

// On shutdown, flush remaining signals
await specter.destroy();
```

## Configuration

```typescript
const specter = new Specter({
  apiKey: 'spk_...',            // Required — your product API key
  host: 'https://specter.draftlabs.org', // API host (default)
  batchSize: 10,                // Auto-flush threshold (default: 10)
  flushInterval: 5000,          // Auto-flush interval in ms (default: 5000)
  maxRetries: 2,                // Retry attempts on failure (default: 2)
  defaultContextId: 'uuid',     // Default context for all signals (optional)
  onError: (err, signals) => {  // Error handler (default: console.error)
    console.error('Failed to send signals:', err.message);
  },
});
```

## API

### `specter.track(type, payload?, options?)`

Queue a signal.

| Param | Type | Description |
|-------|------|-------------|
| `type` | `string` | Any signal type (e.g. `"thumbs_up"`, `"error"`, `"task_completed"`) |
| `payload` | `object` | Arbitrary metadata (optional) |
| `options.contextId` | `string` | UUID of the product context (overrides defaultContextId) |

### `specter.setDefaultContext(contextId)`

Set the default context ID for all subsequent signals. Pass `undefined` to clear.

```typescript
// All signals will be tagged with this context
specter.setDefaultContext('your-context-uuid');

specter.track('thumbs_up'); // → context_id = 'your-context-uuid'
specter.track('error');     // → context_id = 'your-context-uuid'

// Override per-signal
specter.track('custom', {}, { contextId: 'other-uuid' }); // → context_id = 'other-uuid'

// Clear default
specter.setDefaultContext(undefined);
```

### `specter.getDefaultContext()`

Returns the current default context ID, or `undefined` if not set.

### `specter.flush()`

Immediately send all queued signals. Returns a Promise.

### `specter.destroy()`

Flush remaining signals and stop the auto-flush timer. The instance should not be used after this call.

## Reading Proposals

Fetch AI-generated proposals for your product using the API key:

```typescript
const res = await fetch('https://specter.draftlabs.org/api/proposals?status=pending', {
  headers: { 'x-api-key': 'spk_...' },
});
const proposals = await res.json();
```

Query parameters:
- `status` — Filter by proposal status: `pending`, `approved`, `rejected`, `deferred`
- `limit` — Max results (default: 50, max: 200)

## Webhooks

Configure a webhook URL in Settings to receive POST notifications when new proposals are generated:

```json
{
  "event": "proposal.created",
  "product_id": "uuid",
  "product_name": "My App",
  "context_id": "uuid",
  "context_name": "checkout-flow",
  "diagnosis": "...",
  "proposed_change": "...",
  "priority_score": 85,
  "signal_count": 42
}
```

## Browser Usage

```html
<script type="module">
  import Specter from 'https://esm.sh/@specterorg/sdk';

  const specter = new Specter({ apiKey: 'spk_...' });

  document.querySelector('.thumbs-up').addEventListener('click', () => {
    specter.track('thumbs_up', { page: location.pathname });
  });

  window.addEventListener('beforeunload', () => specter.destroy());
</script>
```

## License

MIT