openspecpm 0.1.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/CHANGELOG.md +86 -0
  2. package/LICENSE +21 -0
  3. package/README.md +352 -0
  4. package/cli/bin/openspecpm.js +198 -0
  5. package/cli/src/adapters/azure.js +230 -0
  6. package/cli/src/adapters/base.js +86 -0
  7. package/cli/src/adapters/github.js +224 -0
  8. package/cli/src/adapters/gitlab.js +170 -0
  9. package/cli/src/adapters/index.js +54 -0
  10. package/cli/src/adapters/jira.js +228 -0
  11. package/cli/src/adapters/linear.js +261 -0
  12. package/cli/src/audit.js +78 -0
  13. package/cli/src/bdd/linter.js +183 -0
  14. package/cli/src/bdd/templates.js +172 -0
  15. package/cli/src/commands/assign.js +78 -0
  16. package/cli/src/commands/blocked.js +17 -0
  17. package/cli/src/commands/bug-report.js +78 -0
  18. package/cli/src/commands/bulk.js +67 -0
  19. package/cli/src/commands/comment.js +83 -0
  20. package/cli/src/commands/decompose.js +106 -0
  21. package/cli/src/commands/doctor.js +61 -0
  22. package/cli/src/commands/fan-out.js +79 -0
  23. package/cli/src/commands/help.js +69 -0
  24. package/cli/src/commands/init.js +111 -0
  25. package/cli/src/commands/next.js +18 -0
  26. package/cli/src/commands/propose.js +67 -0
  27. package/cli/src/commands/reconcile.js +74 -0
  28. package/cli/src/commands/search.js +52 -0
  29. package/cli/src/commands/ship.js +79 -0
  30. package/cli/src/commands/standup.js +42 -0
  31. package/cli/src/commands/status.js +29 -0
  32. package/cli/src/commands/sync.js +128 -0
  33. package/cli/src/commands/validate.js +79 -0
  34. package/cli/src/commands/watch.js +67 -0
  35. package/cli/src/config.js +43 -0
  36. package/cli/src/frontmatter.js +22 -0
  37. package/cli/src/http.js +92 -0
  38. package/cli/src/install-hints.js +44 -0
  39. package/cli/src/notify.js +56 -0
  40. package/cli/src/openspec-bridge.js +64 -0
  41. package/cli/src/ratelimit.js +50 -0
  42. package/cli/src/telemetry.js +45 -0
  43. package/cli/src/tracking.js +197 -0
  44. package/package.json +60 -0
  45. package/skill/openspecpm/SKILL.md +74 -0
  46. package/skill/openspecpm/references/conventions.md +105 -0
  47. package/skill/openspecpm/references/execute.md +62 -0
  48. package/skill/openspecpm/references/plan.md +47 -0
  49. package/skill/openspecpm/references/structure.md +52 -0
  50. package/skill/openspecpm/references/sync.md +56 -0
  51. package/skill/openspecpm/references/track.md +55 -0
package/CHANGELOG.md ADDED
@@ -0,0 +1,86 @@
1
+ # Changelog
2
+
3
+ All notable changes to OpenSpecPM are documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## [Unreleased]
9
+
10
+ ### Post-Sprint 6 — docs, CI, v2 planning
11
+
12
+ - **v2 roadmap scaffolded as 6 OpenSpec changes** under `openspec/changes/` (dogfood: the tool plans itself with itself). Each change has a full proposal, dependency-aware tasks.md, and BDD scenarios. Roadmap index lives at `openspec/changes/README.md`. Features: `dependency-graph`, `bdd-llm-reviewer`, `spec-to-tests`, `traceability-export`, `additional-adapters` (Notion + ClickUp + Asana), `agent-orchestrator`.
13
+ - **CI tests badge moved to a Gist-backed shields.io endpoint** updated via `schneegans/dynamic-badges-action`. The previous workflow tried to push the badge JSON back to `main`, which branch protection (rightly) rejects. The gist approach updates badge data without ever pushing to main. Required repo settings: `GIST_SECRET` secret (PAT with `gist` scope) + `TESTS_BADGE_GIST_ID` variable.
14
+ - **Test-count parsing fix in `.github/workflows/test.yml`**: `node --test` emits a `ℹ`-prefixed summary on TTY and `#`-prefixed (TAP) on CI. The old regex only matched `ℹ`, so badges silently read 0 in CI. Regex now matches both.
15
+ - **README screenshot pipeline** at `docs/screenshots/render.ps1`: self-contained PowerShell renderer that scaffolds sample OpenSpec changes via `propose --offline`, captures 6 commands (`help-table`, `doctor`, `status`, `next`, `blocked`, `validate`) as terminal-style PNGs using `System.Drawing`, then cleans up. Working tree stays clean.
16
+ - **README ASCII flow diagram** converted to a Mermaid `flowchart LR` block, matching the existing Architecture + Lifecycle diagrams.
17
+ - **Stale-doc sweep**: Linear and GitLab (added Sprints 5–6) now appear in `SKILL.md`, `references/sync.md` (field-mapping table + capabilities + opening line), `references/structure.md` (hierarchy table), `references/conventions.md` (env vars), `SECURITY.md`, both issue templates, and the PR template. `SKILL.md` script-first table now includes `assign`, `watch`, `doctor --install`, `doctor --setup-auth`, `sync --all`, `ship --all-ready`. `CONTRIBUTING.md` test count corrected (49 → 91).
18
+
19
+ ### Sprint 6
20
+
21
+ - `doctor --install`: OS-detected install hints (winget on Windows, brew on macOS, apt on Linux) for `gh`, `az`, and `openspec`. Linear/GitLab/Jira don't need a CLI.
22
+ - `doctor --setup-auth`: prints the PAT/token creation URL and required scopes for each adapter. Reduces the #1 onboarding cliff to one command.
23
+ - Change-type templates (`cli/src/bdd/templates.js`): `feature`, `bug`, `refactor`, `incident`. `propose --type bug` (etc.) selects the template; `--offline` scaffolds from templates without calling `openspec` so users without OpenSpec installed can still start.
24
+ - Brownfield-aware `init`: detects existing `openspec/` and notes that it will be reused rather than re-initialized.
25
+ - Bulk operations: `sync --all` walks every change with confirmation + per-feature error isolation; `ship --all-ready` ships changes whose tasks are all `sync_state: created` (no pending/failed).
26
+ - `sync --diff`: prints the adapter + capabilities summary alongside the call plan.
27
+ - `watch [feature]`: debounced recursive `fs.watch` over `openspec/changes/`. Re-runs BDD lint per change, or `validate` with `--all`. SIGINT-clean.
28
+ - Notifications (`cli/src/notify.js`): Slack incoming-webhook + Teams MessageCard + generic JSON envelope. Configured via `config.notify.{slack,teams,generic}`. Wired into `standup --broadcast`. Errors per target are collected, never raised.
29
+ - Telemetry scaffold (`cli/src/telemetry.js`): opt-in via `config.telemetry.enabled = true`. Alpha policy: data is mirrored to the audit log only — **no network calls**. Captures command/duration/adapter/OS; never feature names or repo identifiers.
30
+ - Plugin hook documented: `registerAdapter()` was added in Sprint 5; templates and notify both expose their config shapes for third parties to extend.
31
+ - Tests: +14 (templates per type, notify routing per platform, install-hints lookup). Total 91/91 green.
32
+
33
+ ### Sprint 5
34
+
35
+ - Linear adapter (GraphQL at `api.linear.app/graphql`). Bearer auth via `LINEAR_API_KEY`. Full 9-method implementation: projectCreate for epics, issueCreate with parent linkage, cycle/estimate fields for sprints/story-points, workflow-state lookup for close, viewer query for doctor.
36
+ - GitLab adapter (REST v4). PAT auth via `GITLAB_TOKEN` with `api` scope. Issues + issue links (`relates_to`/`blocks`), milestones as sprints, `weight` as story points, `state_event=close` for close.
37
+ - Plugin hook: `registerAdapter(name, ctor, { aliases })` in `cli/src/adapters/index.js` lets third parties register without forking.
38
+ - Cross-feature `depends_on`: tasks may reference `<feature>/<task-title>` or `<feature>/<external-id>`. `findNextTasks` and `findBlockedTasks` walk the full change set and resolve across features. Legacy same-change deps still work.
39
+ - `assign <feature> <task>` command: sets assignee / sprint / iteration / area / story-points on a synced work item via `adapter.updateWorkItem`. Backend-agnostic surface — adapters pick up the keys they support.
40
+ - GitHub adapter: `listChildren(parent)` and `removeChild(parent, child)` for full sub-issue hierarchy management.
41
+ - Integration test harness under `cli/tests/integration/` — gated on `OPENSPECPM_INTEGRATION=1` + per-backend env vars. README + harness helpers; CI does not run them.
42
+ - Init wizard adds Linear + GitLab options with auth hints.
43
+ - Tests: +14 (Linear adapter contract, GitLab adapter contract, cross-feature deps). Total 77/77 green.
44
+
45
+ ### Sprint 4
46
+
47
+ - `comment <feature> <task>`: post local `progress.md` (or `-m "..."`) to the PM tool with an auto-generated `<!-- SYNCED: <iso> -->` marker; appends to local progress for traceability.
48
+ - `reconcile <feature>`: fetches every task with an `external_id` via `adapter.getWorkItem` and mirrors the remote `status`/`assignee` into local task frontmatter. Detects out-of-band closes so `next`/`blocked` reflect remote truth.
49
+ - `decompose <feature>`: extracts tasks from proposal headings, GitHub-style checklists, "Tasks" sections, and BDD scenarios in `specs/`. Refuses to overwrite an existing `tasks.md` without `--force`.
50
+ - `validate`: walks every change checking proposal frontmatter shape, task schema (`sync_state` enum, required fields, duplicate titles), `depends_on` reference resolution, and BDD lint summary. Exits non-zero on any error.
51
+ - `search <query>`: case-insensitive regex grep across `openspec/changes/**/*.md`. `--case-sensitive` and `-l <limit>` flags.
52
+ - `fan-out <feature>`: emits ready-to-paste agent prompts for `parallel: true` tasks with no unmet deps. Each prompt embeds the proposal summary, design notes, and the linked BDD spec as acceptance criteria.
53
+ - `bug-report <feature> <task> --title "..."`: files a regression bug via `adapter.createWorkItem`, links it to the original via `linkWorkItems`, comments on the original. Works against all three adapters.
54
+ - `help-table [topic]`: CCPM-style topical help. Groups commands by phase (Setup / Plan / Sync / Track / Execute-Ship).
55
+ - Audit log (`cli/src/audit.js`): every command appends a JSONL entry to `.openspecpm/audit.log` with timestamp, args (secrets scrubbed), and result/error. Wrapped via `audited()` helper in `cli/bin/openspecpm.js`.
56
+ - Tests: +9 covering audit (record + scrub + audited wrapper), validate inputs, decompose heuristics + idempotency, search. Total 58/58 green.
57
+
58
+ ### Sprint 3
59
+
60
+ - BDD linter (`cli/src/bdd/linter.js`): parses `Scenario:` blocks, runs heuristic checks (one Given/When/Then, observable verbs in Then, deny-list for vague phrases, tautology detection via word-bigram similarity). Soft mode at `propose`, hard mode at `sync` with `--force` override.
61
+ - Tracking commands: `status` (per-change task counts), `standup` (recent `progress.md` updates with `--since 12h/2d/1w`), `next` (open tasks with satisfied deps), `blocked` (tasks waiting on unmet deps with reasons).
62
+ - `ship <feature>`: closes every synced work item via the adapter, closes the epic, then shells out to `openspec archive`. Two-step confirmation (or `-y`).
63
+ - `cli/src/tracking.js` helper: `listChanges`, `loadChange`, `findNextTasks`, `findBlockedTasks`, `findRecentUpdates`, `unmetDeps`, `summarizeChange`.
64
+ - `references/track.md` skill doc.
65
+ - Final SKILL.md description with all Sprint 3 trigger phrases and sharpened non-triggers vs CCPM.
66
+ - 12 new tests (BDD linter + tracking), 49 total.
67
+
68
+ ### Sprint 2
69
+
70
+ - Azure DevOps Boards adapter (REST + PAT auth). All 9 adapter methods implemented: WIQL list, JSON-Patch create/update, Parent/Child hierarchy links, state-based close, comments.
71
+ - Jira adapter (REST v3 + email/API-token auth). All 9 methods implemented: JQL list, ADF descriptions, issue links, transition-based close, comments.
72
+ - Shared HTTP helper (`cli/src/http.js`) with Basic-auth injection, JSON parsing, status-code-aware remediation hints.
73
+ - Contract tests for both REST adapters against mocked `fetch` (21 new tests, 37 total).
74
+ - Skill references: `structure.md`, `sync.md`, `execute.md` (covering capabilities-driven hierarchy collapse, idempotency contract, field-mapping table per backend, hidden-by-default worktrees).
75
+ - `doctor ado` and `doctor jira` validate auth + reach the backend's identity endpoint.
76
+
77
+ ### Sprint 1
78
+
79
+ - Repo scaffold: Node CLI with Commander, OpenSpec bridge with version-probe anti-corruption layer.
80
+ - Adapter base class + `capabilities()` contract.
81
+ - GitHub adapter (uses `gh` CLI).
82
+ - `openspecpm init` interactive wizard (`@clack/prompts`).
83
+ - `openspecpm doctor github` with English remediation hints.
84
+ - `openspecpm propose` (wraps OpenSpec) and `openspecpm sync` (idempotent, frontmatter-tracked).
85
+ - Agent Skill scaffold under `skill/openspecpm/` with conventions + plan references.
86
+ - 16 unit + contract tests; GitHub Actions CI on Node 20.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 aks-builds
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,352 @@
1
+ # OpenSpecPM — Spec-driven PM for any backend
2
+
3
+ [![test](https://github.com/aks-builds/openspecpm/actions/workflows/test.yml/badge.svg?event=push)](https://github.com/aks-builds/openspecpm/actions/workflows/test.yml)
4
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
5
+ [![tests](https://img.shields.io/endpoint?url=https%3A%2F%2Fgist.githubusercontent.com%2Faks-builds%2F03ce34dc5c6486c004dd8cf4c27ea87c%2Fraw%2Ftests.json)](cli/tests)
6
+
7
+ > Spec-driven, BDD-shaped project management for AI agents — author once in [OpenSpec](https://github.com/Fission-AI/OpenSpec), sync to GitHub Issues, Azure DevOps Boards, or Jira.
8
+
9
+ OpenSpecPM turns natural-language intent ("plan X", "sync the X epic", "what's blocked", "ship X") into a disciplined flow:
10
+
11
+ ```mermaid
12
+ flowchart LR
13
+ Idea["💡 **Idea**"]
14
+ Proposal["📝 **proposal.md**<br/>(OpenSpec)"]
15
+ BDD["📋 **BDD specs**<br/>Given / When / Then"]
16
+ Tasks["✅ **tasks**"]
17
+ Tracked["🎯 **Tracked work items**<br/>GitHub · ADO · Jira"]
18
+ Shipped["🚀 **Shipped code**"]
19
+
20
+ Idea --> Proposal --> BDD --> Tasks --> Tracked --> Shipped
21
+
22
+ classDef ideaC fill:#FFF9C4,stroke:#F9A825,color:#000
23
+ classDef artifactC fill:#D5E8D4,stroke:#82B366,color:#000
24
+ classDef extC fill:#DAE8FC,stroke:#6C8EBF,color:#000
25
+ classDef doneC fill:#C8E6C9,stroke:#2E7D32,color:#000
26
+
27
+ class Idea ideaC
28
+ class Proposal,BDD,Tasks artifactC
29
+ class Tracked extC
30
+ class Shipped doneC
31
+ ```
32
+
33
+ It is a sibling of [CCPM](https://github.com/automazeio/ccpm), with three differences:
34
+
35
+ 1. **OpenSpec drives spec authoring** — every feature gets `proposal.md`, `design.md`, `tasks.md`, and a `specs/` folder of BDD scenarios.
36
+ 2. **The PM tool is pluggable** — an interactive wizard at `init` time picks GitHub Issues/Projects, Azure DevOps Boards, or Jira.
37
+ 3. **Built for non-engineers too** — PMs/BAs/PgMs can drive the flow. A `doctor` command owns auth-setup pain. Worktrees are hidden by default.
38
+
39
+ ## Install
40
+
41
+ ```bash
42
+ # Standalone CLI (any harness)
43
+ npx openspecpm@latest init
44
+
45
+ # Claude Code Agent Skill
46
+ # Copy skill/openspecpm/ into your Claude Code skills directory.
47
+ # SKILL.md handles routing — just talk to Claude.
48
+ ```
49
+
50
+ OpenSpecPM shells out to [OpenSpec](https://github.com/Fission-AI/OpenSpec); install it first:
51
+
52
+ ```bash
53
+ npm install -g @fission-ai/openspec
54
+ ```
55
+
56
+ ## In action
57
+
58
+ > A walkthrough of OpenSpecPM running against two sample features (`dark-mode`, `auth-rate-limit`). Source images live in [`docs/screenshots/`](docs/screenshots/); regenerate with `pwsh docs/screenshots/render.ps1` after CLI output changes — the renderer sets up its own sample data and cleans up after itself.
59
+
60
+ **1 · Phase-grouped command reference** — `help-table` shows every command grouped by workflow phase (Setup → Plan → Sync → Track → Execute/Ship):
61
+
62
+ ![openspecpm help-table](docs/screenshots/help-table.png)
63
+
64
+ **2 · Health check across every adapter** — `doctor` diagnoses auth + tooling for all five backends (GitHub, Azure DevOps, Jira, Linear, GitLab) with an English remediation hint on every failure:
65
+
66
+ ![openspecpm doctor](docs/screenshots/doctor.png)
67
+
68
+ **3 · Multi-feature status** — `status` shows the configured adapter and per-change task counts (`synced / pending / failed / done`) at a glance:
69
+
70
+ ![openspecpm status](docs/screenshots/status.png)
71
+
72
+ **4 · "What can I work on right now?"** — `next` lists tasks with no unmet dependencies, marking parallel-safe ones so multiple agents can pick them up:
73
+
74
+ ![openspecpm next](docs/screenshots/next.png)
75
+
76
+ **5 · "What's waiting on what?"** — `blocked` lists every task held up by a dependency and names the blocker, so the path to unblocking is one read away:
77
+
78
+ ![openspecpm blocked](docs/screenshots/blocked.png)
79
+
80
+ **6 · Project-wide validation** — `validate` runs the schema check and BDD linter across every change and reports per-feature error + warning counts:
81
+
82
+ ![openspecpm validate](docs/screenshots/validate.png)
83
+
84
+ ## Quick start
85
+
86
+ ```bash
87
+ # 1. One-time setup. The wizard asks which PM tool your team uses.
88
+ npx openspecpm init
89
+
90
+ # 2. Verify auth before doing anything remote.
91
+ npx openspecpm doctor
92
+
93
+ # 3. Author a proposal. OpenSpec generates proposal.md, design.md, tasks.md,
94
+ # and BDD scenarios in specs/. Soft BDD-lint runs after authoring.
95
+ npx openspecpm propose dark-mode --prompt "Per-user dark theme with persistence."
96
+
97
+ # 4. Review the generated files. Refine BDD scenarios until lint is clean.
98
+
99
+ # 5. Sync to the PM tool. Hard BDD lint runs first; pass --force to override.
100
+ npx openspecpm sync dark-mode
101
+
102
+ # 6. Pick up where you left off.
103
+ npx openspecpm next # tasks ready to start
104
+ npx openspecpm blocked # tasks waiting on dependencies
105
+ npx openspecpm standup # progress updates in the last 24h
106
+
107
+ # 7. When the feature is verified, close + archive.
108
+ npx openspecpm ship dark-mode
109
+ ```
110
+
111
+ ## Command reference
112
+
113
+ | Command | What it does |
114
+ |---|---|
115
+ | `init` | Interactive wizard. Picks the PM tool. Writes `.openspecpm/config.json`. |
116
+ | `doctor [adapter]` | Auth/tooling health check. English remediation hints on every failure. |
117
+ | `propose <feature>` | Shell out to OpenSpec; create `openspec/changes/<feature>/`. Soft-lint BDD scenarios. |
118
+ | `decompose <feature>` | Extract tasks from proposal headings/checklists + BDD scenarios into `tasks.md`. |
119
+ | `sync <feature>` | Hard-lint BDD, then create/update work items in the PM tool. Idempotent. |
120
+ | `comment <feature> <task>` | Broadcast local `progress.md` (or `-m`) to the PM tool with `<!-- SYNCED -->` marker. |
121
+ | `reconcile <feature>` | Pull remote work-item state into local frontmatter. Detects out-of-band closes. |
122
+ | `bug-report <feature> <task> --title "…"` | File a linked regression against a shipped task. |
123
+ | `status` | Per-change task counts: pending / created / failed / done. |
124
+ | `standup [--since 24h]` | Recent `progress.md` updates, newest first. |
125
+ | `next [-l 5]` | Tasks with no unmet dependencies. |
126
+ | `blocked` | Tasks waiting on unmet dependencies (with reasons). |
127
+ | `validate` | Schema + dependency + BDD-lint sweep across every change. |
128
+ | `search <query>` | Grep across proposals, specs, tasks, progress notes. |
129
+ | `fan-out <feature>` | Emit ready-to-paste agent prompts for `parallel: true` tasks. |
130
+ | `ship <feature> [-y]` | Close all task work items + close the epic + archive the OpenSpec change. |
131
+ | `help-table [topic]` | Context-aware command reference grouped by workflow phase. |
132
+
133
+ Every command appends a JSONL entry (secrets scrubbed) to `.openspecpm/audit.log`.
134
+
135
+ ## Architecture
136
+
137
+ ```mermaid
138
+ flowchart TD
139
+ PM([👤 Project Manager / BA])
140
+ Dev([👤 Developer])
141
+ Agent([🤖 AI Agent · Claude Code])
142
+
143
+ Skill["**Agent Skill**<br/>skill/openspecpm/SKILL.md<br/><br/>Routes natural-language intent"]
144
+ CLI["**Node CLI**<br/>cli/bin/openspecpm.js<br/><br/>Commander dispatch + audit"]
145
+
146
+ subgraph CMDS["📋 Commands · cli/src/commands/"]
147
+ direction LR
148
+ Setup["**① Setup**<br/>init • doctor"]
149
+ Plan["**② Plan**<br/>propose • decompose"]
150
+ SyncCmd["**③ Sync**<br/>sync • comment • reconcile<br/>assign • bug-report"]
151
+ Track["**④ Track**<br/>status • standup • next<br/>blocked • validate • search • watch"]
152
+ Exec["**⑤ Execute / Ship**<br/>fan-out • ship • help-table"]
153
+ end
154
+
155
+ subgraph CORE["⚙️ Core services · cli/src/"]
156
+ direction LR
157
+ Bridge["**OpenSpec Bridge + BDD**<br/>openspec-bridge.js<br/>bdd/linter.js"]
158
+ TrackingS["**Tracking + Audit**<br/>tracking.js<br/>audit.js"]
159
+ HTTP["**HTTP + Rate-limit**<br/>http.js<br/>ratelimit.js"]
160
+ IO["**Config + Notify + Telemetry**<br/>config.js • notify.js<br/>telemetry.js"]
161
+ end
162
+
163
+ subgraph ADAPTERS["🔌 Adapter contract · cli/src/adapters/ · 9 methods + capabilities()"]
164
+ direction LR
165
+ GHA["**GitHub**<br/>depth 2"]
166
+ AzA["**Azure DevOps**<br/>depth 4"]
167
+ JiA["**Jira**<br/>depth 3"]
168
+ LiA["**Linear**<br/>depth 2"]
169
+ GlA["**GitLab**<br/>depth 2"]
170
+ end
171
+
172
+ subgraph EXT["☁️ External PM systems"]
173
+ direction LR
174
+ GHE[("GitHub<br/>Issues / Projects")]
175
+ AzE[("Azure DevOps<br/>Boards")]
176
+ JiE[("Jira<br/>Cloud / Server")]
177
+ LiE[("Linear")]
178
+ GlE[("GitLab<br/>Issues")]
179
+ end
180
+
181
+ subgraph PERSIST["💾 Persistence & sinks"]
182
+ direction LR
183
+ FS1["📁 **openspec/**<br/>changes/&lt;feature&gt;/<br/>• proposal.md<br/>• specs/*.md (BDD)<br/>• tasks.md<br/>• updates/progress.md"]
184
+ FS2["📁 **.openspecpm/**<br/>• config.json<br/>• audit.log (JSONL)<br/>• state.json"]
185
+ Sinks["🔔 **Webhooks**<br/>• Slack<br/>• Teams<br/>• Generic JSON"]
186
+ end
187
+
188
+ PM --> Skill
189
+ Dev --> CLI
190
+ Agent --> Skill
191
+ Skill -- invokes --> CLI
192
+ CLI --> CMDS
193
+ CMDS --> CORE
194
+ CORE --> ADAPTERS
195
+
196
+ GHA --> GHE
197
+ AzA --> AzE
198
+ JiA --> JiE
199
+ LiA --> LiE
200
+ GlA --> GlE
201
+
202
+ Bridge -. writes .-> FS1
203
+ TrackingS -. writes .-> FS1
204
+ IO -. writes .-> FS2
205
+ IO -. broadcast .-> Sinks
206
+
207
+ classDef user fill:#DAE8FC,stroke:#6C8EBF,color:#000
208
+ classDef agent fill:#FFE0B2,stroke:#D97757,color:#000
209
+ classDef entry fill:#C5E1A5,stroke:#558B2F,color:#000
210
+ classDef skill fill:#FFE0B2,stroke:#D97757,color:#000
211
+ classDef github fill:#222,stroke:#000,color:#fff
212
+ classDef azure fill:#0078D7,stroke:#005A9E,color:#fff
213
+ classDef jira fill:#0052CC,stroke:#003580,color:#fff
214
+ classDef linear fill:#5E6AD2,stroke:#3F47A0,color:#fff
215
+ classDef gitlab fill:#FC6D26,stroke:#C44A19,color:#fff
216
+ classDef ext fill:#fff,stroke:#333,stroke-width:3px,color:#000
217
+ classDef fs fill:#fff,stroke:#444,stroke-width:2px,color:#000
218
+
219
+ class PM,Dev user
220
+ class Agent agent
221
+ class CLI entry
222
+ class Skill skill
223
+ class GHA github
224
+ class AzA azure
225
+ class JiA jira
226
+ class LiA linear
227
+ class GlA gitlab
228
+ class GHE,AzE,JiE,LiE,GlE ext
229
+ class FS1,FS2,Sinks fs
230
+ ```
231
+
232
+ ## Lifecycle
233
+
234
+ ```mermaid
235
+ flowchart LR
236
+ Idea["💡 **Idea**<br/>stakeholder feature,<br/>bug, or refactor"]
237
+
238
+ subgraph P1["① PLAN"]
239
+ direction TB
240
+ Propose["**openspecpm propose**<br/>Shells out to OpenSpec<br/>Soft BDD lint"]
241
+ Decompose["**openspecpm decompose**<br/>Extract tasks from<br/>proposal + BDD"]
242
+ Propose --> Decompose
243
+ end
244
+
245
+ subgraph P2["② REVIEW + SYNC"]
246
+ direction TB
247
+ Review["👀 **Human review**<br/>Sign off on BDD"]
248
+ Validate["**openspecpm validate**<br/>Schema + BDD sweep"]
249
+ SyncCmd["**openspecpm sync**<br/>Hard BDD lint<br/>Idempotent"]
250
+ Review --> Validate --> SyncCmd
251
+ end
252
+
253
+ subgraph P3["③ EXECUTE"]
254
+ direction TB
255
+ Next["**openspecpm next**"]
256
+ FanOut["**openspecpm fan-out**<br/>Parallel agent prompts"]
257
+ Build["🤖 **Implement**<br/>BDD = acceptance criteria"]
258
+ Comment["**openspecpm comment**<br/>Broadcast progress"]
259
+ Reconcile["**openspecpm reconcile**<br/>Pull remote state"]
260
+ Next --> FanOut --> Build --> Comment --> Reconcile
261
+ end
262
+
263
+ subgraph P4["④ TRACK"]
264
+ direction TB
265
+ Status["**Track commands**<br/>status • standup<br/>blocked • search<br/>watch • bug-report"]
266
+ end
267
+
268
+ subgraph P5["⑤ SHIP"]
269
+ direction TB
270
+ Ship["**openspecpm ship**<br/>Close tasks + epic<br/>Archive change"]
271
+ Shipped["🚀 **Shipped**"]
272
+ Ship --> Shipped
273
+ end
274
+
275
+ Idea --> P1
276
+ P1 --> P2
277
+ P2 --> P3
278
+ P3 --> P4
279
+ P4 --> P5
280
+ Shipped -. next feature .-> Idea
281
+
282
+ classDef ideaC fill:#FFF9C4,stroke:#F9A825,color:#000
283
+ classDef cmdC fill:#D5E8D4,stroke:#82B366,color:#000
284
+ classDef humanC fill:#FFF9C4,stroke:#F9A825,color:#000
285
+ classDef agentC fill:#FFE0B2,stroke:#D97757,color:#000
286
+ classDef shipC fill:#FFCDD2,stroke:#D32F2F,color:#000
287
+ classDef doneC fill:#C8E6C9,stroke:#2E7D32,color:#000
288
+
289
+ class Idea ideaC
290
+ class Propose,Decompose,Validate,SyncCmd,Next,FanOut,Comment,Reconcile,Status cmdC
291
+ class Review humanC
292
+ class Build agentC
293
+ class Ship shipC
294
+ class Shipped doneC
295
+ ```
296
+
297
+ > Cross-cutting on every command: audit log (`.openspecpm/audit.log`, secrets scrubbed) · token-bucket rate-limiting per adapter · OpenSpec version probe · optional opt-in telemetry.
298
+
299
+ ## Workflow phases
300
+
301
+ OpenSpecPM is organized into five phases, each with a reference doc under [`skill/openspecpm/references/`](skill/openspecpm/references/):
302
+
303
+ 1. **Plan** ([`plan.md`](skill/openspecpm/references/plan.md)) — Capture requirements through an OpenSpec proposal + BDD scenarios.
304
+ 2. **Structure** ([`structure.md`](skill/openspecpm/references/structure.md)) — Decompose into tasks with explicit dependencies and parallelism hints.
305
+ 3. **Sync** ([`sync.md`](skill/openspecpm/references/sync.md)) — Push to the PM tool. Capabilities-driven hierarchy collapse for flatter backends.
306
+ 4. **Execute** ([`execute.md`](skill/openspecpm/references/execute.md)) — Start work on a tracked item. Optional worktrees, parallel-agent dispatch.
307
+ 5. **Track** ([`track.md`](skill/openspecpm/references/track.md)) — Status, standup, next, blocked, ship.
308
+
309
+ ## Architecture highlights
310
+
311
+ - **Adapter contract.** Every backend implements the same 9-method interface plus `capabilities()` reporting hierarchy depth (GitHub=2, Linear=2, GitLab=2, Jira=3, ADO=4). The sync layer collapses levels gracefully.
312
+ - **OpenSpec anti-corruption layer.** Version-pinned probe on every CLI invocation. One constant absorbs upstream path moves.
313
+ - **Idempotent sync.** Each task carries `sync_state` + `external_id` in frontmatter. Re-running `sync` skips created items and retries failures. Comments use `<!-- SYNCED: <ts> -->` markers to prevent duplication.
314
+ - **Token-bucket rate-limiting.** Per-adapter presets tuned for each backend's published limits.
315
+ - **BDD linter.** Heuristic checks: one Given/When/Then per scenario, observable verbs in Then, deny-list for vague phrases ("should work"), tautology detection via word-bigram similarity.
316
+
317
+ ## Roadmap
318
+
319
+ Active v2 work is tracked as OpenSpec changes under [`openspec/changes/`](openspec/changes/README.md). Six features are scaffolded with full proposals, dependency-aware task lists, and BDD scenarios: dependency-graph visualization, LLM-backed BDD reviewer, spec → test scaffolding, compliance traceability export, three new adapters (Notion / ClickUp / Asana), and a real agent orchestrator that graduates `fan-out` from prompt-emitter to dispatcher.
320
+
321
+ OpenSpecPM dogfoods itself: anyone can `openspecpm next` to see what's ready to start, or `openspecpm sync <change>` to push any of the six into a tracked PM tool.
322
+
323
+ ## Project structure
324
+
325
+ ```
326
+ openspecpm/
327
+ ├── README.md this file
328
+ ├── LICENSE MIT
329
+ ├── CHANGELOG.md
330
+ ├── package.json
331
+ ├── .github/workflows/test.yml
332
+ ├── skill/openspecpm/ Claude Code Agent Skill
333
+ │ ├── SKILL.md
334
+ │ └── references/ conventions, plan, structure, sync, execute, track
335
+ └── cli/
336
+ ├── bin/openspecpm.js Commander entrypoint
337
+ ├── src/
338
+ │ ├── commands/ init, doctor, propose, sync, status, standup, next, blocked, ship
339
+ │ ├── adapters/ base, github, azure, jira, index
340
+ │ ├── bdd/ linter, templates
341
+ │ ├── http.js REST helper for ADO + Jira
342
+ │ ├── tracking.js listChanges, findNext, findBlocked, findRecent
343
+ │ ├── openspec-bridge.js
344
+ │ ├── config.js
345
+ │ ├── frontmatter.js
346
+ │ └── ratelimit.js
347
+ └── tests/ unit + contract tests (count badge is auto-updated by CI)
348
+ ```
349
+
350
+ ## License
351
+
352
+ [MIT](LICENSE)
@@ -0,0 +1,198 @@
1
+ #!/usr/bin/env node
2
+ import { Command } from 'commander';
3
+ import { audited } from '../src/audit.js';
4
+ import { runInit } from '../src/commands/init.js';
5
+ import { runDoctor } from '../src/commands/doctor.js';
6
+ import { runPropose } from '../src/commands/propose.js';
7
+ import { runDecompose } from '../src/commands/decompose.js';
8
+ import { runSync } from '../src/commands/sync.js';
9
+ import { runComment } from '../src/commands/comment.js';
10
+ import { runReconcile } from '../src/commands/reconcile.js';
11
+ import { runStatus } from '../src/commands/status.js';
12
+ import { runStandup } from '../src/commands/standup.js';
13
+ import { runNext } from '../src/commands/next.js';
14
+ import { runBlocked } from '../src/commands/blocked.js';
15
+ import { runValidate } from '../src/commands/validate.js';
16
+ import { runSearch } from '../src/commands/search.js';
17
+ import { runFanOut } from '../src/commands/fan-out.js';
18
+ import { runBugReport } from '../src/commands/bug-report.js';
19
+ import { runShip } from '../src/commands/ship.js';
20
+ import { runHelp } from '../src/commands/help.js';
21
+ import { runAssign } from '../src/commands/assign.js';
22
+ import { runSyncAll, runShipAllReady } from '../src/commands/bulk.js';
23
+ import { runWatch } from '../src/commands/watch.js';
24
+
25
+ const program = new Command();
26
+
27
+ program
28
+ .name('openspecpm')
29
+ .description('Spec-driven, BDD-shaped project management for AI agents.')
30
+ .version('0.1.0-alpha.0');
31
+
32
+ program
33
+ .command('init')
34
+ .description('Interactive wizard: pick a PM tool and write .openspecpm/config.json')
35
+ .option('--non-interactive', 'Fail instead of prompting when input is required')
36
+ .action((opts) => audited('init', runInit)(opts).catch(fatal));
37
+
38
+ program
39
+ .command('doctor [adapter]')
40
+ .description('Check auth + tooling health for one or all adapters')
41
+ .option('--install', 'Print OS-specific install commands for missing CLIs')
42
+ .option('--setup-auth', 'Print PAT/token URLs and required scopes')
43
+ .action((adapter, opts) => audited('doctor', runDoctor)({ adapter, install: opts.install, setupAuth: opts.setupAuth }).catch(fatal));
44
+
45
+ program
46
+ .command('propose <feature>')
47
+ .description('Create an OpenSpec proposal (proposal.md, design.md, tasks.md, specs/) for <feature>')
48
+ .option('-p, --prompt <text>', 'One-line description for the AI to seed the proposal')
49
+ .option('-t, --type <type>', 'Change type: feature | bug | refactor | incident', 'feature')
50
+ .option('--offline', 'Scaffold from templates without calling the openspec CLI')
51
+ .action((feature, opts) => audited('propose', runPropose)({ feature, prompt: opts.prompt, type: opts.type, offline: opts.offline }).catch(fatal));
52
+
53
+ program
54
+ .command('decompose <feature>')
55
+ .description('Extract tasks from proposal + BDD scenarios into tasks.md')
56
+ .option('--force', 'Merge into an existing tasks.md instead of refusing')
57
+ .action((feature, opts) => audited('decompose', runDecompose)({ feature, force: opts.force }).catch(fatal));
58
+
59
+ program
60
+ .command('sync [feature]')
61
+ .description('Push an OpenSpec change to the configured PM tool (idempotent; BDD-linted)')
62
+ .option('--all', 'Sync every change under openspec/changes/')
63
+ .option('--dry-run', 'Print the call plan without making remote changes')
64
+ .option('--force', 'Bypass BDD lint errors')
65
+ .option('--diff', 'Show the call plan in detail')
66
+ .option('-y, --yes', 'Skip the confirmation prompt for --all')
67
+ .action((feature, opts) => {
68
+ if (opts.all) {
69
+ return audited('sync-all', runSyncAll)({ dryRun: opts.dryRun, force: opts.force, yes: opts.yes }).catch(fatal);
70
+ }
71
+ if (!feature) {
72
+ process.stderr.write('error: provide a <feature> or pass --all\n');
73
+ process.exit(1);
74
+ }
75
+ return audited('sync', runSync)({ feature, dryRun: opts.dryRun, force: opts.force, diff: opts.diff }).catch(fatal);
76
+ });
77
+
78
+ program
79
+ .command('comment <feature> <task>')
80
+ .description('Post a progress comment to the PM tool work item')
81
+ .option('-m, --message <text>', 'Inline message (otherwise reads from local progress.md)')
82
+ .option('--dry-run', 'Print the comment instead of posting')
83
+ .action((feature, task, opts) =>
84
+ audited('comment', runComment)({ feature, task, message: opts.message, dryRun: opts.dryRun }).catch(fatal),
85
+ );
86
+
87
+ program
88
+ .command('reconcile <feature>')
89
+ .description('Pull remote state back into local task frontmatter')
90
+ .option('--dry-run', 'Show drift without writing local changes')
91
+ .action((feature, opts) => audited('reconcile', runReconcile)({ feature, dryRun: opts.dryRun }).catch(fatal));
92
+
93
+ program
94
+ .command('status')
95
+ .description('Local snapshot: configured adapter + per-change task counts')
96
+ .action(() => audited('status', runStatus)().catch(fatal));
97
+
98
+ program
99
+ .command('standup')
100
+ .description('Show progress updates within a recent window')
101
+ .option('--since <window>', 'Time window: e.g. 12h, 2d, 1w', '24h')
102
+ .option('--broadcast', 'Also POST to configured Slack/Teams/generic webhooks')
103
+ .action((opts) => audited('standup', runStandup)({ since: opts.since, broadcast: opts.broadcast }).catch(fatal));
104
+
105
+ program
106
+ .command('next')
107
+ .description('List tasks ready to start (no unmet dependencies)')
108
+ .option('-l, --limit <n>', 'Max items to show', (v) => parseInt(v, 10), 5)
109
+ .action((opts) => audited('next', runNext)({ limit: opts.limit }).catch(fatal));
110
+
111
+ program
112
+ .command('blocked')
113
+ .description('List tasks waiting on unmet dependencies')
114
+ .action(() => audited('blocked', runBlocked)().catch(fatal));
115
+
116
+ program
117
+ .command('validate')
118
+ .description('Schema + dependency + BDD-lint sweep across every change')
119
+ .action(() => audited('validate', runValidate)().catch(fatal));
120
+
121
+ program
122
+ .command('search <query>')
123
+ .description('Grep across proposals, specs, tasks, and progress notes')
124
+ .option('-l, --limit <n>', 'Max matches to show', (v) => parseInt(v, 10), 50)
125
+ .option('--case-sensitive', 'Match case')
126
+ .action((query, opts) =>
127
+ audited('search', runSearch)({ query, limit: opts.limit, caseSensitive: opts.caseSensitive }).catch(fatal),
128
+ );
129
+
130
+ program
131
+ .command('fan-out <feature>')
132
+ .description('Emit ready-to-paste agent prompts for parallel:true tasks')
133
+ .option('-l, --limit <n>', 'Max prompts to emit', (v) => parseInt(v, 10), 5)
134
+ .action((feature, opts) => audited('fan-out', runFanOut)({ feature, limit: opts.limit }).catch(fatal));
135
+
136
+ program
137
+ .command('bug-report <feature> <task>')
138
+ .description('File a regression bug linked to a shipped task')
139
+ .option('-t, --title <text>', 'Bug title (required)')
140
+ .option('-b, --body <text>', 'Bug body')
141
+ .action((feature, task, opts) =>
142
+ audited('bug-report', runBugReport)({ feature, task, title: opts.title, body: opts.body }).catch(fatal),
143
+ );
144
+
145
+ program
146
+ .command('assign <feature> <task>')
147
+ .description('Set assignee / sprint / iteration / area / story-points on a synced task')
148
+ .option('--assignee <id>', 'Assignee id (backend-specific)')
149
+ .option('--sprint <id>', 'Sprint / cycle / milestone id (whichever the backend supports)')
150
+ .option('--iteration <path>', 'Iteration path (Azure DevOps; alias for --sprint elsewhere)')
151
+ .option('--area <path>', 'Area path (Azure DevOps)')
152
+ .option('--story-points <n>', 'Story points / estimate / weight')
153
+ .action((feature, task, opts) =>
154
+ audited('assign', runAssign)({
155
+ feature, task,
156
+ assignee: opts.assignee, sprint: opts.sprint, iteration: opts.iteration,
157
+ area: opts.area, storyPoints: opts.storyPoints,
158
+ }).catch(fatal),
159
+ );
160
+
161
+ program
162
+ .command('ship [feature]')
163
+ .description('Close all work items for <feature> and archive the OpenSpec change')
164
+ .option('--all-ready', 'Ship every change whose tasks are all synced (no pending/failed)')
165
+ .option('-y, --yes', 'Skip the confirmation prompt')
166
+ .option('--skip-archive', 'Close work items but leave openspec/changes/<feature>/ in place')
167
+ .action((feature, opts) => {
168
+ if (opts.allReady) {
169
+ return audited('ship-all-ready', runShipAllReady)({ yes: opts.yes, skipArchive: opts.skipArchive }).catch(fatal);
170
+ }
171
+ if (!feature) {
172
+ process.stderr.write('error: provide a <feature> or pass --all-ready\n');
173
+ process.exit(1);
174
+ }
175
+ return audited('ship', runShip)({ feature, yes: opts.yes, skipArchive: opts.skipArchive }).catch(fatal);
176
+ });
177
+
178
+ program
179
+ .command('watch [feature]')
180
+ .description('Re-lint on file change. Use --all to validate the whole project on every change.')
181
+ .option('--all', 'Validate every change instead of linting one feature')
182
+ .option('--debounce <ms>', 'Debounce window in milliseconds', (v) => parseInt(v, 10), 300)
183
+ .action((feature, opts) =>
184
+ audited('watch', runWatch)({ feature, allChanges: opts.all, debounceMs: opts.debounce }).catch(fatal),
185
+ );
186
+
187
+ program
188
+ .command('help-table [topic]')
189
+ .description('Context-aware help grouped by workflow phase')
190
+ .action((topic) => { runHelp({ topic }); });
191
+
192
+ program.parseAsync(process.argv);
193
+
194
+ function fatal(err) {
195
+ process.stderr.write(`\n✖ ${err.message}\n`);
196
+ if (err.remediation) process.stderr.write(` → ${err.remediation}\n`);
197
+ process.exit(1);
198
+ }