- commit
- 406608f94cf52eb41e6b0af7610eef45bd0371c7
- parent
- bc6a1cc62a582f701515b262ce2e132bea61e3fb
- author
- dami etoile <dami.etoile@gmail.com>
| A | openspec/ | 0 | |
| A | openspec/ | 0 | |
| A | openspec/ | 80 | +++++++++++++++++++++ |
| A | openspec/ | 114 | ++++++++++++++++++++++++++++++ |
| A | openspec/ | 99 | ++++++++++++++++++++++++++ |
| A | openspec/ | 82 | ++++++++++++++++++++++ |
| A | openspec/ | 97 | ++++++++++++++++++++++++++ |
File mode 100644
File mode 100644
@@ -0,0 +1,80 @@
1+# Agent OpenSpec Tools2+3+## Purpose4+5+The plugin SHALL expose OpenSpec-aware Hermes tools that let agents discover, inspect, validate, and act on repo-local OpenSpec artifacts without manually assembling shell commands. Tools are expected to be safe for maintainers to use across multiple registered repositories and predictable enough for other agents to depend on.6+7+## Requirements8+9+### Requirement: Registered-source context resolution10+The plugin SHALL provide an `openspec_context` tool that resolves user-facing OpenSpec identifiers into the source repository path and relevant artifact content.11+12+#### Scenario: Resolve a bare source name13+- **GIVEN** a source is registered with a vanity name or falls back to its path basename14+- **WHEN** an agent calls `openspec_context` with that source name15+- **THEN** the tool returns the repository path, workdir hint, active changes with tokens, and current specs with tokens16+17+#### Scenario: Resolve a change token18+- **GIVEN** a registered source contains an active or archived change directory with `proposal.md`19+- **WHEN** an agent calls `openspec_context` with `<source>/<os_token>` for that change20+- **THEN** the tool returns the change name, archive status, proposal content, tasks content, design content, and each delta spec file's relative path and content21+22+#### Scenario: Resolve a spec token23+- **GIVEN** a registered source contains a spec under `openspec/specs/`24+- **WHEN** an agent calls `openspec_context` with `<source>/<os_token>` for that spec25+- **THEN** the tool returns the spec path and full spec content26+27+#### Scenario: Missing source or artifact28+- **WHEN** an agent calls `openspec_context` with an unknown source or unknown artifact token29+- **THEN** the tool returns `ok: false` and a direct error explaining what could not be resolved30+31+### Requirement: CLI-backed tool gating32+The plugin SHALL only expose CLI-backed tools when an executable OpenSpec CLI binary is available.33+34+#### Scenario: OpenSpec binary available35+- **GIVEN** `OPENSPEC_BIN`, `PATH`, or the user's `~/.npm-global/bin/openspec` points to an executable binary36+- **WHEN** Hermes registers plugin tools37+- **THEN** `openspec_list`, `openspec_show`, `openspec_validate`, `openspec_status`, and `openspec_instructions` are available38+39+#### Scenario: OpenSpec binary unavailable40+- **GIVEN** no executable OpenSpec CLI binary is found41+- **WHEN** Hermes registers plugin tools42+- **THEN** only `openspec_context` remains available because it reads the registry database and filesystem directly43+44+### Requirement: CLI command execution contract45+CLI-backed tools SHALL execute OpenSpec commands in a resolved project workdir and return structured JSON-compatible results.46+47+#### Scenario: Successful command48+- **WHEN** a CLI-backed tool runs an OpenSpec command successfully49+- **THEN** the tool returns `ok: true`, the command, workdir, exit code, parsed stdout when stdout is JSON, stderr, and truncation status50+51+#### Scenario: Failed command52+- **WHEN** a CLI-backed tool receives a non-zero OpenSpec CLI exit code53+- **THEN** the tool returns `ok: false` with the same command, workdir, stdout, stderr, exit code, and truncation status54+55+#### Scenario: Invalid workdir56+- **WHEN** a CLI-backed tool is called with a missing or non-directory workdir57+- **THEN** the tool returns `ok: false` and does not execute the OpenSpec CLI58+59+#### Scenario: Command timeout60+- **WHEN** an OpenSpec CLI command runs longer than the configured timeout61+- **THEN** the tool returns `ok: false` with the command, workdir, and timeout error62+63+### Requirement: Artifact listing and inspection64+The plugin SHALL provide tools that map directly to OpenSpec list/show/status/instructions workflows.65+66+#### Scenario: List changes or specs67+- **WHEN** an agent calls `openspec_list` with kind `changes` or `specs`68+- **THEN** the tool invokes the OpenSpec CLI JSON list command for that kind and respects supported sort options69+70+#### Scenario: Show a change or spec71+- **WHEN** an agent calls `openspec_show` with a target name and optional flags72+- **THEN** the tool invokes the OpenSpec CLI JSON show command with the requested type, delta, requirements, scenario, or requirement filters73+74+#### Scenario: Validate artifacts75+- **WHEN** an agent calls `openspec_validate`76+- **THEN** the tool invokes the OpenSpec CLI validation command with strict JSON non-interactive output and the requested target or scope77+78+#### Scenario: Retrieve implementation instructions79+- **WHEN** an agent calls `openspec_instructions`80+- **THEN** the tool returns OpenSpec's enriched instructions for the requested artifact, schema, and optional change@@ -0,0 +1,114 @@
1+# OpenSpec Change Board2+3+## Purpose4+5+The dashboard plugin SHALL present repo-local OpenSpec work as a maintainer-facing board that surfaces ideas, active changes, archived changes, task progress, and rich artifact details. The board exists to make OpenSpec state inspectable without leaving the Hermes dashboard.6+7+## Requirements8+9+### Requirement: Board scan model10+The dashboard API SHALL scan each initialized source's `openspec/` directory into board data without persisting change or spec records in the plugin database.11+12+#### Scenario: Active change scan13+- **GIVEN** a directory exists under `openspec/changes/` with a `proposal.md`14+- **WHEN** the source is scanned15+- **THEN** the API includes it as a change with name, token, title, status, artifact presence flags, and task stats when available16+17+#### Scenario: Archived change scan18+- **GIVEN** a directory exists under `openspec/changes/archive/` with a `proposal.md`19+- **WHEN** the source is scanned20+- **THEN** the API includes it as an archived change and assigns it the archived status21+22+#### Scenario: Idea scan23+- **GIVEN** a markdown file exists under `openspec/ideas/`24+- **WHEN** the source is scanned25+- **THEN** the API includes it as an idea with name, token, title, and ideas status26+27+#### Scenario: Current spec scan28+- **GIVEN** a markdown file exists under `openspec/specs/`29+- **WHEN** the source is scanned30+- **THEN** the API includes it as a current spec with relative path, token, and title31+32+### Requirement: Change status derivation33+The dashboard API SHALL derive change board status from OpenSpec files and task completion rather than storing status manually.34+35+#### Scenario: Draft status36+- **GIVEN** a change has a proposal but no tasks file or no parseable task stats37+- **WHEN** the board scan runs38+- **THEN** the change status is `draft`39+40+#### Scenario: Todo status41+- **GIVEN** a change has tasks and zero completed checklist items42+- **WHEN** the board scan runs43+- **THEN** the change status is `todo`44+45+#### Scenario: In-progress status46+- **GIVEN** a change has both completed and incomplete checklist items47+- **WHEN** the board scan runs48+- **THEN** the change status is `in_progress`49+50+#### Scenario: Done status51+- **GIVEN** a change has tasks and every checklist item is complete52+- **WHEN** the board scan runs53+- **THEN** the change status is `done`54+55+#### Scenario: Archived status56+- **GIVEN** a change lives under `openspec/changes/archive/`57+- **WHEN** the board scan runs58+- **THEN** the change status is `archived` regardless of task completion59+60+### Requirement: Change detail API61+The dashboard API SHALL expose full detail for a selected change.62+63+#### Scenario: Existing change detail64+- **GIVEN** a selected source contains the requested change65+- **WHEN** the dashboard requests change detail66+- **THEN** the API returns the change name, title, status, archive flag, task stats, proposal markdown, design markdown, tasks markdown, and delta spec entries67+68+#### Scenario: Delta spec comparison69+- **GIVEN** a change contains delta spec files under its `specs/` directory70+- **WHEN** the API returns change detail71+- **THEN** each delta spec entry includes relative path, proposed content, current worktree spec content when present, status, and a unified diff when content changed72+73+#### Scenario: Missing change detail74+- **WHEN** the dashboard requests a change that does not exist in the selected source75+- **THEN** the API returns a not-found response76+77+### Requirement: Idea detail API78+The dashboard API SHALL expose full markdown detail for selected idea files.79+80+#### Scenario: Existing idea detail81+- **GIVEN** an idea markdown file exists under `openspec/ideas/`82+- **WHEN** the dashboard requests idea detail83+- **THEN** the API returns the idea name, title, ideas status, and markdown content84+85+#### Scenario: Missing idea detail86+- **WHEN** the dashboard requests an idea that does not exist87+- **THEN** the API returns a not-found response88+89+### Requirement: Board user interface90+The dashboard UI SHALL render changes and ideas in status-oriented columns and let maintainers inspect one selected artifact at a time.91+92+#### Scenario: Board columns93+- **WHEN** the dashboard renders an initialized source94+- **THEN** it displays ideas, draft, todo, in-progress, done, and archived groups using the source scan payload95+96+#### Scenario: Artifact selection97+- **WHEN** the user selects a change or idea card98+- **THEN** the dashboard opens a focused detail panel for that artifact rather than requiring the user to navigate away99+100+#### Scenario: Change artifact tabs101+- **GIVEN** a selected change includes proposal, tasks, design, or spec deltas102+- **WHEN** the detail panel renders103+- **THEN** the user can switch among those artifact views and see missing artifacts clearly104+105+### Requirement: Markdown and task rendering106+The dashboard UI SHALL render OpenSpec markdown and tasks in readable HTML while keeping the plugin self-contained.107+108+#### Scenario: Markdown display109+- **WHEN** proposal, design, idea, or spec markdown is displayed110+- **THEN** the UI renders common markdown elements including headings, bold text, inline code, lists, horizontal rules, and paragraphs111+112+#### Scenario: Task checklist display113+- **WHEN** a tasks document contains OpenSpec checklist items114+- **THEN** the UI displays task structure and completion status in a parsed, readable form instead of raw undifferentiated markdown@@ -0,0 +1,99 @@
1+# Dashboard Source Management2+3+## Purpose4+5+The dashboard plugin SHALL let maintainers register, inspect, initialize, update, and remove OpenSpec-capable repositories from the Hermes dashboard. Source management must work even before a repository has been initialized with an `openspec/` directory.6+7+## Requirements8+9+### Requirement: Source listing API10+The dashboard API SHALL list registered sources with live validity, repository, and OpenSpec scan information.11+12+#### Scenario: Valid initialized source13+- **GIVEN** a registered source path resolves to a repository with an `openspec/` directory14+- **WHEN** the dashboard requests the source list15+- **THEN** the API returns the source as valid with repo root, OpenSpec path, scanned changes, scanned ideas, scanned specs, and status counts16+17+#### Scenario: Existing but uninitialized source18+- **GIVEN** a registered source path resolves to an existing repository without an `openspec/` directory19+- **WHEN** the dashboard requests the source list20+- **THEN** the API returns the source as invalid with the repo root and a `No openspec/ directory found` error21+22+#### Scenario: Missing path23+- **GIVEN** a registered source path no longer exists24+- **WHEN** the dashboard requests the source list25+- **THEN** the API returns the source as invalid with an explanatory error instead of dropping the source26+27+### Requirement: Source path resolution28+The dashboard API SHALL normalize user-provided paths and derive the repository root when possible.29+30+#### Scenario: Path inside a git repository31+- **GIVEN** a user submits a path inside a git repository32+- **WHEN** the API registers or updates a source33+- **THEN** it resolves the repository root for validation and payload metadata34+35+#### Scenario: Non-git directory36+- **GIVEN** a user submits an existing directory that is not inside a git repository37+- **WHEN** the API registers or updates a source38+- **THEN** it accepts that directory as the source root39+40+#### Scenario: Invalid path input41+- **WHEN** a user submits an empty path, null-byte path, unparseable path, or non-existent path42+- **THEN** the API rejects the request with a 400-level error and does not modify the registry43+44+### Requirement: Source CRUD operations45+The dashboard API SHALL expose create, update, and delete operations for registry sources.46+47+#### Scenario: Register source48+- **WHEN** the dashboard submits a valid source path and optional display name49+- **THEN** the API stores the source and returns the source payload with live validity metadata50+51+#### Scenario: Update source52+- **GIVEN** a registered source exists53+- **WHEN** the dashboard submits a new valid path or name for that source54+- **THEN** the API updates the registry and returns the refreshed source payload55+56+#### Scenario: Duplicate path conflict57+- **GIVEN** another source already uses a submitted path58+- **WHEN** the dashboard creates or updates a source with that path59+- **THEN** the API returns a conflict response and preserves the existing registry state60+61+#### Scenario: Remove source62+- **GIVEN** a registered source exists63+- **WHEN** the dashboard deletes the source64+- **THEN** the API removes it from the registry and subsequent source lists omit it65+66+### Requirement: Source initialization67+The dashboard API SHALL initialize a registered source that exists but lacks an `openspec/` directory.68+69+#### Scenario: OpenSpec CLI available70+- **GIVEN** the OpenSpec CLI binary is available71+- **WHEN** the dashboard initializes an uninitialized source72+- **THEN** the API runs `openspec init <repo-root> --tools none` and returns the refreshed source payload73+74+#### Scenario: OpenSpec CLI unavailable75+- **GIVEN** the OpenSpec CLI binary is unavailable76+- **WHEN** the dashboard initializes an uninitialized source77+- **THEN** the API creates a minimal `openspec/changes`, `openspec/specs`, and `openspec/ideas` directory structure and returns the refreshed source payload78+79+#### Scenario: Already initialized source80+- **GIVEN** the source already has an `openspec/` directory81+- **WHEN** the dashboard initializes the source82+- **THEN** the API returns success with an `Already initialized` message and does not modify existing artifacts83+84+### Requirement: Dashboard source controls85+The dashboard UI SHALL present compact controls for selecting and managing registered sources.86+87+#### Scenario: Source selection88+- **GIVEN** one or more registered sources exist89+- **WHEN** the dashboard renders the OpenSpec tab90+- **THEN** the user can select the active source from a top-level control and see invalid sources labelled as invalid91+92+#### Scenario: Source actions93+- **WHEN** the user opens source management controls94+- **THEN** the user can refresh sources, add a source, edit the active source, remove the active source, and initialize an uninitialized active source when applicable95+96+#### Scenario: Empty source list97+- **GIVEN** no sources are registered98+- **WHEN** the dashboard renders the OpenSpec tab99+- **THEN** it shows an empty-state path to add a source instead of rendering a broken board@@ -0,0 +1,82 @@
1+# OpenSpec Source Registry2+3+## Purpose4+5+The plugin SHALL maintain a lightweight registry of repositories that the OpenSpec dashboard and agent tools can address by stable, copyable identifiers. The registry is intentionally separate from OpenSpec artifact storage: sources are stored in SQLite, while changes and specs are read live from each repository's filesystem.6+7+## Requirements8+9+### Requirement: Profile-local source storage10+The registry SHALL store OpenSpec sources in the active Hermes home so each Hermes profile has an independent source list.11+12+#### Scenario: Registry database path13+- **WHEN** the registry opens its database14+- **THEN** it uses `<hermes_home>/openspec.db` from Hermes' profile-aware home resolver15+16+#### Scenario: Database initialization17+- **WHEN** source data is first read or written18+- **THEN** the registry creates the source table and unique path index if they do not already exist19+20+### Requirement: Source record identity21+Each registered source SHALL have a stable internal token, an optional vanity name, an absolute or user-expanded path string, and a creation timestamp.22+23+#### Scenario: Add new source24+- **GIVEN** a path is not already registered25+- **WHEN** the registry adds the source26+- **THEN** it generates an `os_` token, stores the source, and returns token/id aliases, name, path, and created_at27+28+#### Scenario: Duplicate source path29+- **GIVEN** a source path is already registered30+- **WHEN** the registry attempts to add the same normalized path again31+- **THEN** it rejects the operation with a duplicate-source error32+33+#### Scenario: Update source34+- **GIVEN** an existing source token35+- **WHEN** the registry updates the source path or vanity name36+- **THEN** it preserves the token and creation timestamp while replacing the path and name37+38+#### Scenario: Remove source39+- **GIVEN** an existing source token40+- **WHEN** the registry removes the source41+- **THEN** the source no longer appears in registry listings or identifier resolution42+43+### Requirement: Human-facing source names44+The registry SHALL resolve sources by explicit vanity name first and by path basename when no vanity name is set.45+46+#### Scenario: Explicit vanity name47+- **GIVEN** a source has a non-empty `name`48+- **WHEN** an agent resolves that name case-insensitively49+- **THEN** the registry returns the matching source50+51+#### Scenario: Basename fallback52+- **GIVEN** a source has no explicit `name`53+- **WHEN** an agent resolves the basename of the source path case-insensitively54+- **THEN** the registry returns the matching source55+56+### Requirement: Deterministic artifact tokens57+The registry SHALL derive copyable `os_` artifact tokens deterministically from change names and spec paths rather than storing per-artifact rows.58+59+#### Scenario: Change token derivation60+- **WHEN** a change folder name is converted into a token61+- **THEN** the token is a stable `os_` value derived from the change name62+63+#### Scenario: Spec token derivation64+- **WHEN** a spec path is converted into a token65+- **THEN** the token is a stable `os_` value derived from the `spec:<relative-path>` key66+67+#### Scenario: Filesystem stays authoritative68+- **GIVEN** a change or spec is renamed, added, archived, or removed on disk69+- **WHEN** tools scan the repository70+- **THEN** the current filesystem state determines which artifact tokens resolve71+72+### Requirement: Legacy source migration73+The registry SHALL support one-time migration from legacy dashboard configuration sources into the SQLite registry.74+75+#### Scenario: Legacy sources exist76+- **GIVEN** the Hermes dashboard config contains legacy OpenSpec source entries77+- **WHEN** the plugin API lists registered sources for the first time in a process78+- **THEN** it attempts to migrate those sources into the registry idempotently79+80+#### Scenario: Legacy migration failure81+- **WHEN** legacy source migration raises an exception82+- **THEN** source listing continues using the SQLite registry without crashing the dashboard API@@ -0,0 +1,97 @@
1+# Spec Browser and Diff View2+3+## Purpose4+5+The dashboard plugin SHALL provide a spec browser for current OpenSpec specs and diffs across working tree and git refs. The browser must help maintainers inspect requirements, scenarios, metadata, and changes without reading raw markdown unless they choose to.6+7+## Requirements8+9+### Requirement: Current spec browser mode10+The dashboard API SHALL return the current worktree spec set for an initialized source when no diff parameters are requested.11+12+#### Scenario: List current specs13+- **GIVEN** markdown specs exist under `openspec/specs/`14+- **WHEN** the dashboard requests the spec browser without before/after refs and without dirty mode15+- **THEN** the API returns mode `current`, current branch, each spec path, token, title, status `current`, content, and changed flag `false`16+17+#### Scenario: Current spec timestamps18+- **GIVEN** git history or filesystem metadata is available for a current spec19+- **WHEN** the API returns current spec browser entries20+- **THEN** each entry includes most-recent edit time when available and creation time when git history can determine it21+22+### Requirement: Dirty worktree diff mode23+The dashboard API SHALL compare a git ref to current worktree specs when dirty mode is requested.24+25+#### Scenario: Dirty diff defaults26+- **WHEN** dirty mode is requested without an explicit before ref27+- **THEN** the API compares `HEAD` to the working tree28+29+#### Scenario: Dirty diff filters unchanged specs30+- **GIVEN** some specs match the selected before ref exactly31+- **WHEN** dirty mode is requested32+- **THEN** unchanged specs are omitted from the returned files list33+34+#### Scenario: Dirty diff entry35+- **GIVEN** a spec was added, modified, or deleted relative to the before ref36+- **WHEN** dirty mode is requested37+- **THEN** the API returns before content, after content, status, changed flag, unified diff, and available timestamps for that spec38+39+### Requirement: Ref-to-ref diff mode40+The dashboard API SHALL compare specs between two explicit git refs when both before and after refs are supplied.41+42+#### Scenario: Valid ref comparison43+- **GIVEN** both before and after refs are supplied44+- **WHEN** the dashboard requests the spec browser45+- **THEN** the API returns mode `refs`, both labels, all spec paths present in either ref, per-spec status, before content, after content, changed flag, and unified diff when changed46+47+#### Scenario: Incomplete ref comparison48+- **WHEN** only one of before or after is supplied outside dirty mode49+- **THEN** the API rejects the request with a 400-level error explaining that both refs are required50+51+### Requirement: Safe spec path handling52+The API SHALL prevent spec detail and git content reads from escaping `openspec/specs/`.53+54+#### Scenario: Worktree spec path traversal55+- **WHEN** a spec detail request contains an absolute path or parent-directory traversal outside the specs root56+- **THEN** the API returns not found and does not read outside the specs directory57+58+#### Scenario: Ref spec path traversal59+- **WHEN** a git-ref spec read is requested for an absolute path or a path containing `..`60+- **THEN** the API returns no content and does not invoke git with an escaped specs path61+62+### Requirement: Spec detail API63+The dashboard API SHALL expose the content of a selected current spec.64+65+#### Scenario: Existing spec detail66+- **GIVEN** a requested spec path exists under `openspec/specs/`67+- **WHEN** the dashboard requests spec detail68+- **THEN** the API returns the path, title, and markdown content69+70+#### Scenario: Missing spec detail71+- **WHEN** the dashboard requests a spec path that does not exist under `openspec/specs/`72+- **THEN** the API returns a not-found response73+74+### Requirement: Structured requirement display75+The dashboard UI SHALL render OpenSpec requirements and scenarios as structured content in spec and diff views when the format is recognized.76+77+#### Scenario: Requirement section parsing78+- **GIVEN** spec content contains OpenSpec requirement headings and scenario headings79+- **WHEN** the UI renders the spec or diff view80+- **THEN** it displays requirement names, requirement text, scenario names, and scenario bullets as structured elements instead of a raw markdown block81+82+#### Scenario: Unknown markdown fallback83+- **GIVEN** spec content does not match the recognized OpenSpec requirement format84+- **WHEN** the UI renders the spec or diff view85+- **THEN** it falls back to safe markdown rendering rather than hiding content86+87+### Requirement: Copyable identifiers88+The spec browser SHALL expose copyable identifiers for sources, changes, and specs that agents can resolve later.89+90+#### Scenario: Copy spec token91+- **GIVEN** a spec browser entry has a source name and spec token92+- **WHEN** the user copies the identifier chip93+- **THEN** the copied text is `<source-name>/<os_token>` and can be resolved by `openspec_context`94+95+#### Scenario: Copy feedback96+- **WHEN** a user copies an identifier chip97+- **THEN** the UI gives brief visible feedback that the value was copied