Skip to content

About

Streaming SDK to continuously consume specs from prooph board and trigger commands

Resources

Stars

3 stars

Watchers

0 watching

Forks

Repository files navigation

@proophboard/spec-stream

Stream changes from your prooph board event model to your terminal, and run a command — usually an AI coding agent — for every change.

You model a feature on prooph board (even from your phone). The moment you save a change, spec-stream receives it in real time and runs the command you configured — kicking off an agent to implement the spec, regenerate docs, notify your team, or anything else.

prooph board change ──▶ realtime event ──▶ spec-stream ──▶ your command / AI agent

Status: beta. The CLI is implemented and covered by an automated test suite, and our first end-to-end tests against a live prooph board workspace confirmed it works — realtime events trigger commands, and own-write filtering behaves as designed. APIs and config may still change before a stable 1.0 release. Feedback and issues welcome.


Why

  • Spec-driven, continuously. Write a spec in a sticky note; an agent starts working when you save.
  • Work from anywhere. Edit the board on mobile while away — agents run on your machine or a server.
  • Lightweight. A tiny, dependency-minimal CLI. No framework, no daemon manager required.
  • Controllable. Decide exactly which events trigger which commands, and how to queue, debounce, dedupe, or batch them so agents don't collide.

Prerequisites

  • Node.js ≥ 18 (≥ 20 recommended).
  • A prooph board API key for the workspace you want to stream. Create one in prooph board (Settings → API Keys). It looks like pb_1a2b3c….

Installation

spec-stream is a CLI you can run directly with npx — no install step required:

npx @proophboard/spec-stream --help

To pin a version (recommended while in beta):

npx @proophboard/spec-stream@latest run

Or install it globally / as a project dev dependency if you prefer:

# global
npm install -g @proophboard/spec-stream
spec-stream --help

# project (dev dependency)
npm install --save-dev @proophboard/spec-stream
npx spec-stream --help

Requires Node.js ≥ 18. The package ships as ESM.


Quick start

1. Provide your API key (never put it in the config file)

export PROOPHBOARD_API_KEY="pb_xxxxxxxxxxxxxxxx"
# or put it in a .env file (see below)

2. Create proophboard.spec-stream.json in your project

Scaffold one instantly with:

npx @proophboard/spec-stream init

This writes a starter proophboard.spec-stream.json into the current directory with an example echo rule you can edit. (Use init --force to overwrite an existing file.)

Or create it by hand:

{
  "endpoint": "https://flow.prooph-board.com/api",
  "rules": [
    {
      "on": "element-description-changed",
      "run": "echo \"Spec changed for $SPEC_STREAM_ELEMENT_NAME\"",
      "concurrency": { "key": "element", "mode": "debounce", "wait": 5000 }
    }
  ]
}

This runs your command whenever a sticky note's description changes, collapsing rapid edits to the same note into a single run.

3. Run it

npx @proophboard/spec-stream

You'll see a live log of connections, incoming events, and command runs. Press Ctrl-C to stop. Now edit an element description on your board and watch the command fire.


A more realistic example: trigger an AI agent

{
  "endpoint": "https://flow.prooph-board.com/api",
  "maxConcurrent": 3,
  "rules": [
    {
      "id": "implement-spec",
      "on": ["element-description-changed", "element-details-changed"],
      "when": { "elementType": ["command", "ui", "event"] },
      "run": "claude -p \"Implement the spec for '$SPEC_STREAM_ELEMENT_NAME'. Full event on stdin.\"",
      "cwd": "./",
      "concurrency": { "key": "element", "mode": "debounce", "wait": 8000, "max": 1 }
    },
    {
      "id": "build-planned-slice",
      "on": "slice-status-changed",
      "when": { "data": { "newValue.status": ["planned"] } },
      "run": "claude -p \"Build the slice $SPEC_STREAM_SLICE_ID. Full event on stdin.\"",
      "cwd": "./",
      "concurrency": { "key": "slice", "mode": "queue", "max": 1 }
    }
  ]
}
  • The first rule reacts to specs on command / ui / event elements.
  • Rapid re-saves of the same element collapse into one agent run (debounce).
  • The same element is never worked on by two agents at once (key: element, max: 1), but different elements run in parallel — up to maxConcurrent.
  • The second rule shows a common workflow: when you flip a slice's status to planned on the board, a build agent starts implementing it. The when.data filter matches the new status in the event payload (newValue.status), so only the planned transition fires — not every status change. when.data can match any field in the event by dot-path.

Your command receives the change as SPEC_STREAM_* environment variables and the full event as JSON on stdin. See docs/command-context.md.


Running in the background

Foreground is the default (like tail -f). To run detached:

spec-stream start      # start in the background (writes a PID file)
spec-stream status     # is it running? connection + counters
spec-stream logs -f    # follow the log
spec-stream stop       # graceful stop (waits for running commands to finish)

For servers, run the foreground command under systemd/Docker and let the manager handle restarts. See docs/background-mode.md.


Using a .env file

# .env
PROOPHBOARD_API_KEY=pb_xxxxxxxxxxxxxxxx
  • Node ≥ 20.6: node --env-file=.env … (or spec-stream loads it automatically when present).
  • Older Node: install the optional dotenv dependency.

.env is already git-ignored in this project. Never commit your API key, and never put it in proophboard.spec-stream.json.


Which events can I react to?

Any prooph board changelog event — element-description-changed, element-details-changed, element-added, element-renamed, element-comment-added, slice-added, chapter-added, and ~40 more. Use a single type, a list, or "*".

The full catalog with payloads is in docs/event-reference.md. The most useful for spec-driven automation are usually element-description-changed and element-details-changed.


Controlling concurrency

The key feature for automation is deciding when commands run so agents don't fight over the same work:

Mode Use it when
parallel Independent side effects (notifications).
queue Every change matters; process one at a time per key.
debounce A spec is being edited; collapse a burst into one run.
dedupe Avoid duplicate long-running jobs on the same target.
batch Coalesce many related changes into a single pass.

Combined with a concurrency key (element, slice, chapter, global, or a custom template), this gives precise control. Full guide with timelines: docs/concurrency.md.


Avoiding feedback loops (own writes)

If your triggered agents write back to the board (via the prooph board API/MCP using the same key), those changes would themselves be changelog events. By default, spec-stream ignores events made by its own API-key user, so an agent never re-triggers itself.

  • This is automatic — no configuration needed.
  • Opt a rule back in with "consumeOwnEvents": true if you do want it to react to its own user's changes.
  • Your commands also receive SPEC_STREAM_SELF_USER_ID / SPEC_STREAM_SELF_EMAIL so they can distinguish their own writes.

Reliability

Once running, spec-stream stays up until you stop it:

  • Command errors never stop the stream — they're logged and the loop continues.
  • Connection drops trigger reconnection with capped exponential backoff (up to every 30 minutes, retrying indefinitely) and a 30s health check.
  • Missed events during a disconnect are replayed on reconnect.
  • Invalid startup config (missing key, bad config) fails fast with a clear message — the one case where it exits on purpose.

Details: docs/reconnection.md.


Security

  • The API key is a secret: env/.env only, never in config, never logged (redacted).
  • Invoked commands inherit PROOPHBOARD_API_KEY. Commands run with spec-stream's full environment, so every command (and its subprocesses) can read the API key. This is handy for agents that write back to prooph board, but means you should only run trusted commands and prefer a read-only key. See docs/command-context.md.
  • Configured commands run with your privileges. The config file controls what gets executed — treat it as trusted and review rules before running.
  • Event data is untrusted (anyone who can edit the board produces it). spec-stream passes values as discrete env vars and JSON stdin rather than interpolating them into a shell. If your run string embeds values, quote them, or use the safer command + args form (shell: false).

Configuration reference

Every option is documented in docs/config-schema.md. Paths (config discovery, logs, PID file) are in docs/paths.md.


How it works (short version)

spec-stream exchanges your pb_… API key for a short-lived access token via prooph board's token endpoint (no long-lived refresh token — the key stays the single credential, so revoking it stops the stream), then subscribes to your workspace's realtime changelog stream. Matching events are routed through a scheduler (queue/debounce/dedupe/batch) and executed as child processes.

The full architecture is in AGENT.md and docs/.


Local model sync

spec-stream can mirror your entire prooph board workspace into a local file tree and keep it up to date in near-realtime from the same changelog stream it already consumes. AI agents can then read the model with plain filesystem tools (grep, glob, cat) instead of making API calls.

With the optional sync-back command, edits to the local file tree are pushed back to prooph board — making the sync fully two-way:

prooph board ──▶ spec-stream run ──▶ .spec-stream/model/   (kept live)
                                             │
                          agent edits files ─┘
                                             │
                          spec-stream sync-back ──▶ prooph board

sync-back works standalone — no git required.

Enable it

Add a localSync block to your proophboard.spec-stream.json:

{
  "endpoint": "https://flow.prooph-board.com/api",
  "localSync": {
    "enabled": true,
    "dir": ".spec-stream/model"
  },
  "rules": []
}

On the next spec-stream run (or start), it fetches the full workspace via the REST API, writes the initial file tree, then applies every incoming changelog event incrementally. Restarts catch up on missed events automatically.

What gets written

.spec-stream/
  sync-state.json            # resume cursor (do not edit)
  model/
    workspace.json
    uuid-index.json          # flat { uuid → "relative/dir" } for fast id resolution
    chapters/
      [Context]/
        [Chapter name]/
          chapter.json
          index.md           # generated slice summary
          slices/
            [index]_[Slice]/
              slice.json
              details.md
              lanes/
                [laneType]/
                  [Lane]/
                    elements/
                      [index]_[Element]/
                        element.json
                        description.md
                        details.md
                        play-function.ts   # if set
                        play-type.ts       # if set
          scenarios/
            [Scenario name]/
              scenario.json  # Exploration Mode scenario
    element-details/         # canonical shared details (one per name+type+context)
    lane-details/            # canonical shared lane details
    milestones/
      [Milestone]/
        milestone.json
        description.md

Every .json carries the raw values (names, ids). Directory names use sanitized slugs — safe for all filesystems and easy to grep. UUIDs are not embedded in paths; uuid-index.json maps every entity UUID to its directory for O(1) resolution. Use @proophboard/spec-stream sync --rebuild to force a full rebuild from the REST API at any time.

Enable two-way sync (sync-back)

Run sync-back any time you want to push local edits back to the board — after an agent finishes, before a deploy, or on demand:

npx @proophboard/spec-stream sync-back

Preview what would be synced without making any changes:

npx @proophboard/spec-stream sync-back --dry-run --verbose

You can also wire it into a git pre-commit hook if you want automatic sync on every commit:

cat > .git/hooks/pre-commit << 'EOF'
#!/bin/sh
npx @proophboard/spec-stream sync-back
EOF
chmod +x .git/hooks/pre-commit

If the sync fails, the hook aborts the commit so API errors are caught early.

Validate the model before committing

After editing model files, run a structural consistency check before pushing:

npx @proophboard/spec-stream model validate

This checks all .json files for syntax errors, missing required fields, and dangling UUID references (element laneId/sliceId, scenario expectation sliceId/elementId). Exits 0 if clean, 1 with a per-issue report if not. Add --verbose to see every checked path.

Agent skill: give agents the schema

MODEL_SCHEMA.md at the repo root is a compact reference document describing the full file tree layout, every JSON schema, and the play-function.ts / play-type.ts / scenario.json conventions. It is designed to be loaded as a skill or system-prompt context for AI agents so they can read and write the local model correctly without trial and error:

  • What each file means and which ones are editable
  • JSON field names, types, and allowed values for every entity
  • How to write a scenario from scratch, step by step
  • How sync-back translates file edits into API calls

Full details in docs/sync-back.md.

Full layout details, the event→mutation table, and convergence guarantees are in docs/local-sync.md.


Documentation

  • AGENT.md — idea + overall architecture (start here to contribute).
  • docs/ — architecture, auth, config, events, concurrency, logging, paths, reconnection, background mode, and the prooph board dependency.
  • docs/local-sync.md — local model sync: layout, event→mutation table, convergence.
  • docs/sync-back.md — two-way sync: pushing local edits back to prooph board.

License

MIT © prooph board

About

Streaming SDK to continuously consume specs from prooph board and trigger commands

Resources

Stars

3 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages