Skip to content

Tools

Reactive

Four primitives for turning one-shot pipelines into continuous ones – watch, listen, record, replay.


The four primitives

TypeScript
pluck.watch(uri, options);    // poll → yield WatchEvent on every change
pluck.listen(uri, options);   // poll + sense + fire a handler when a condition matches
pluck.record(uri, options);   // run the pipeline AND save a deterministic trace
pluck.replay(tracePath);      // re-run the pipeline from a trace, zero network

All four are first-class methods on PluckInstance. They compose: record a single run, replay it deterministically in CI, or watch a URI and compose each change event into a downstream action.


pluck.watch(uri) – change detection

Poll a URI at an interval, yield a WatchEvent whenever the content hash changes:

TypeScript
import { createPluck } from "@sizls/pluck";

const pluck = createPluck();

for await (const event of pluck.watch("https://status.example.com", {
  interval: 10_000,      // poll every 10 seconds
  maxDuration: 60_000,   // stop after 1 minute
})) {
  switch (event.type) {
    case "initial":
      console.log("Baseline established:", event.result?.text.slice(0, 50));
      break;
    case "changed":
      console.log("Content changed!", event.result?.text.slice(0, 200));
      await slack.post("#alerts", `Status page updated: ${event.uri}`);
      break;
    case "unchanged":
      // skipped – no alert noise
      break;
    case "error":
      console.error("Poll failed:", event.error.message);
      break;
    case "stopped":
      console.log("Watcher stopped gracefully.");
      break;
  }
}

The CLI equivalent is a one-liner:

Shell
pluck watch https://status.example.com --interval 30s --on-change "./alert.sh"

pluck watch is the reactive primitive for anything URL-based – status pages, pricing pages, news headlines, API responses. For signal-based watching (mic, video), use pluck.listen.


pluck.listen(uri, opts) – sense-triggered handlers

listen combines watch-style polling with sense-based condition evaluation. Fire a handler when a sense condition matches:

TypeScript
const stop = await pluck.listen("mic://input", {
  on: { ultrasonic: { present: true } }, // fire when ultrasonic carrier is detected
  interval: 5_000,
  handler: async (event) => {
    console.log("Ultrasonic carrier detected:", event.carrierHz);
    await pluck.act("https://hooks.example/alert", {
      action: "post",
      input: { kind: "ultrasonic", carrierHz: event.carrierHz },
    });
  },
});

// Later:
await stop();

listen is the reactive primitive for signals. Detect infrasonic rumbles (seismic precursors / HVAC failures), ultrasonic beacons (cross-device tracking), DTMF tones (line monitoring), rPPG pulse changes. Every sensor from Concepts: Sense is available as a trigger condition.


pluck.record(uri) – capture a deterministic fixture

record runs the full pipeline AND writes a .plucktrace.json bundle with every intermediate phase output:

TypeScript
await pluck.record("https://news.ycombinator.com", {
  out: "./fixtures/hn.plucktrace.json",
});
// Writes ./fixtures/hn.plucktrace.json with the complete run.

The CLI equivalent:

Shell
pluck record https://news.ycombinator.com -o ./fixtures/hn.plucktrace.json

Use it to seed CI fixtures. Run your pipeline once against live data, commit the fixture, then replay it deterministically forever.


pluck.replay(tracePath) – deterministic re-run

replay takes a .plucktrace.json and reproduces the pipeline run bit-for-bit – no network, no side-effects:

TypeScript
import { replay } from "@sizls/pluck";

const result = await replay("./fixtures/hn.plucktrace.json");
// Same result as the original run, same PluckResult shape.

result.output("markdown");   // works exactly as before
result.receipt;          // same signed receipt if the original had one

The CLI equivalent is a one-liner:

Shell
pluck replay ./fixtures/hn.plucktrace.json
pluck replay ./fixtures/hn.plucktrace.json --format json

Use it for CI snapshot-testing: record once, replay in every commit, fail the build if the output shape changes.


Composing the four

The primitives compose into common patterns:

TypeScript
// Pattern 1 – watch + act: status page changes trigger a webhook.
for await (const event of pluck.watch(uri, { interval: 60_000 })) {
  if (event.type === "changed") {
    await pluck.act("https://hooks.example/status", {
      action: "post",
      input: { uri, diff: event.diff },
    });
  }
}

// Pattern 2 – record + replay: deterministic CI.
// (once) pluck record https://news.ycombinator.com -o ./fixtures/hn.plucktrace.json
// (CI)   pluck replay ./fixtures/hn.plucktrace.json --strict

// Pattern 3 – listen + act: signal detection fires a real-world action.
await pluck.listen("mic://", {
  on: { dtmf: { present: true } },
  handler: async (event) => {
    await pluck.act("postgres://ops/alerts", {
      action: "insert",
      input: { kind: "dtmf", digits: event.digits, at: event.startTime },
    });
  },
});

What's next

  • Concepts: Sense – the sensor catalogue that listen consumes.
  • Studio – visualise recorded traces with time-travel scrubbing.
  • Reference: CLIpluck watch, pluck listen, pluck record, pluck replay.
Edit this page on GitHub
Previous
Studio

Ready to build?

Install Pluck and follow the Quick Start guide to wire MCP-first data pipelines into your agents and fleets in minutes.

Get started →