Skip to content

feat(watch): external WatchCapability loading from .squad/capabilities/ #918

Closed

Description

@tamirdresher

Summary

Allow users to define custom WatchCapabilities in their repo without forking squad-cli. The watch system already has a clean plugin interface (WatchCapability with name, phase, preflight(), execute()), a typed registry, and phase-based execution. The only missing piece is a loader that discovers and registers user-defined capabilities from outside the package.

Motivation

Users want to define deterministic, code-enforced workflows that run as part of the watch loop — for example, an issue-pipeline capability that enforces: issue > branch > implement (fresh copilot session) > PR > review (separate session, different prompt) > check approval > merge. Today this requires forking because createDefaultRegistry() only registers built-in capabilities.

The watch architecture is the right place for this: each LLM invocation is a fresh session (no drift), the pipeline between steps is TypeScript (not prompt), and the capability interface is already typed and tested.

Proposed Design

Loading mechanism

After createDefaultRegistry() in runWatch(), scan for user capabilities:

  1. Local repo: .squad/capabilities/*.js — ES module default-exporting a WatchCapability
  2. npm packages: Convention TBD (e.g. squad-capability-* or @squad-capability/*)

Each file/package exports a class or object implementing WatchCapability. The loader does import() + registry.register().

Config integration

User capabilities are enabled/disabled the same way as built-in ones — via config.json keys or --<name> / --no-<name> CLI flags.

Sample capability

Ship a sample issue-pipeline capability (in .squad/templates/ or as a docs example) that demonstrates the pattern:

  • Phase: post-triage
  • Behavior: For each triaged issue without a PR:
    1. Create branch (code)
    2. Spawn copilot session with implement-only prompt (LLM)
    3. Open PR via gh pr create (code)
    4. Spawn separate copilot session with review-only prompt (LLM, different context)
    5. Check review decision (code)
    6. If approved + CI green → merge (code)
    7. If rejected → loop back to step 2 with different prompt context

What's NOT in scope

  • Changing the WatchCapability interface (it's already good)
  • Moving built-in capabilities to external loading (they stay hardcoded)
  • Runtime hot-reloading (capabilities are loaded once at watch startup)

Implementation estimate

~50-100 lines in runWatch() for the loader + docs + sample capability.

Context

This came from a conversation with @tamirdresher and a colleague (Yoni) about enforcing issue>PR>review>merge pipelines without LLM drift. The watch architecture already solves the drift problem (fresh session per invocation), but lacks the extensibility hook for users to define their own pipeline steps.

Metadata

Metadata

Assignees

No one assigned

    Labels

    enhancementNew feature or improvement

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions