@open-product-primer/cli 2.1.0 → 2.2.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 (48) hide show
  1. package/dist/cli.js +2 -0
  2. package/dist/commands/validate.d.ts +2 -0
  3. package/dist/commands/validate.js +131 -0
  4. package/dist/lib/install-agent.js +78 -977
  5. package/dist/lib/spec-delta.d.ts +30 -0
  6. package/dist/lib/spec-delta.js +216 -0
  7. package/dist/lib/validate-checks.d.ts +13 -0
  8. package/dist/lib/validate-checks.js +145 -0
  9. package/dist/lib/workflow-renderer.d.ts +19 -0
  10. package/dist/lib/workflow-renderer.js +103 -0
  11. package/dist/lib/workflow-schema.d.ts +37 -0
  12. package/dist/lib/workflow-schema.js +126 -0
  13. package/dist/workflows/archive.inline.md +10 -0
  14. package/dist/workflows/archive.schema.yaml +13 -0
  15. package/dist/workflows/archive.template.md +106 -0
  16. package/dist/workflows/bet.cursor-command.md +1 -0
  17. package/dist/workflows/bet.inline.md +13 -0
  18. package/dist/workflows/bet.schema.yaml +14 -0
  19. package/dist/workflows/bet.template.md +114 -0
  20. package/dist/workflows/context.schema.yaml +13 -0
  21. package/dist/workflows/context.template.md +34 -0
  22. package/dist/workflows/criteria.cursor-command.md +1 -0
  23. package/dist/workflows/criteria.inline.md +11 -0
  24. package/dist/workflows/criteria.schema.yaml +14 -0
  25. package/dist/workflows/criteria.template.md +52 -0
  26. package/dist/workflows/note.cursor-command.md +1 -0
  27. package/dist/workflows/note.inline.md +11 -0
  28. package/dist/workflows/note.schema.yaml +14 -0
  29. package/dist/workflows/note.template.md +66 -0
  30. package/dist/workflows/pdr.cursor-command.md +1 -0
  31. package/dist/workflows/pdr.inline.md +10 -0
  32. package/dist/workflows/pdr.schema.yaml +14 -0
  33. package/dist/workflows/pdr.template.md +67 -0
  34. package/dist/workflows/promote.native.template.md +29 -0
  35. package/dist/workflows/promote.none.template.md +25 -0
  36. package/dist/workflows/promote.openspec.template.md +40 -0
  37. package/dist/workflows/promote.schema.yaml +14 -0
  38. package/dist/workflows/review.cursor-command.md +1 -0
  39. package/dist/workflows/review.inline.md +11 -0
  40. package/dist/workflows/review.schema.yaml +14 -0
  41. package/dist/workflows/review.template.md +69 -0
  42. package/dist/workflows/sequence.cursor-command.md +12 -0
  43. package/dist/workflows/sequence.inline.md +10 -0
  44. package/dist/workflows/sequence.schema.yaml +13 -0
  45. package/dist/workflows/sequence.template.md +88 -0
  46. package/dist/workflows/spec-authoring.schema.yaml +13 -0
  47. package/dist/workflows/spec-authoring.template.md +68 -0
  48. package/package.json +2 -2
@@ -0,0 +1,10 @@
1
+ ### PDR authoring (oprim-pdr)
2
+ Create a new Product Decision Record in `oprim/decisions/`.
3
+
4
+ 1. Ask for decision title.
5
+ 2. Assign next PDR ID: scan `oprim/decisions/PDR-(\d+)-`, max+1 zero-padded to 3 digits (default 001).
6
+ 2b. Read `oprim/config.yaml`'s `rules.pdr` — if non-empty, apply it as additional guidance and reflect it in the generated content; if empty, behavior is unchanged.
7
+ 3. Gather: context, decision, alternatives, consequences, evidence, related bets/specs.
8
+ 4. Ask if superseding an existing PDR.
9
+ 5. Write `oprim/decisions/PDR-NNN-<slug>.md`. If superseding, update old PDR Status.
10
+ 6. Report what was created.
@@ -0,0 +1,14 @@
1
+ id: pdr
2
+ skillName: oprim-pdr
3
+ title: null
4
+ description: Create a new Product Decision Record in oprim/decisions/ with auto-assigned ID and guided prompting
5
+ cursorDescription: Create a new Product Decision Record with auto-assigned ID
6
+ claude:
7
+ skill: true
8
+ command: null
9
+ cursor:
10
+ skill: true
11
+ command: oprim-pdr.md
12
+ poolside:
13
+ skill: true
14
+ inline: true
@@ -0,0 +1,67 @@
1
+ ---
2
+ name: oprim-pdr
3
+ description: Create a new Product Decision Record in oprim/decisions/ with auto-assigned ID and guided prompting
4
+ ---
5
+
6
+ Create a new Product Decision Record (PDR) in `oprim/decisions/`.
7
+
8
+ **Interactive prompts:** Use the **AskUserQuestion tool** for every question in this skill — do not write questions as plain text.
9
+
10
+ ## Steps
11
+
12
+ ### 1. Get the decision title
13
+ If not provided, ask: "What is the title of this product decision?"
14
+
15
+ ### 2. Assign the next PDR ID
16
+ Scan `oprim/decisions/` for files matching `PDR-(\d+)-`. Extract all integers. Assign max+1, zero-padded to 3 digits. Default `001` if none found.
17
+ Slug: title → lowercase → spaces to hyphens → remove non-alphanumeric (except hyphens).
18
+ Output path: `oprim/decisions/PDR-NNN-<slug>.md`
19
+
20
+ ### 2b. Check for custom rules
21
+ Read `oprim/config.yaml`. If it has a non-empty `rules.pdr` value, treat it as additional guidance from the team — factor it into the questions you ask in step 3 and reflect it in the generated content. If `rules.pdr` is absent or empty, skip this step; behavior is unchanged.
22
+
23
+ ### 3. Gather content
24
+ Ask: Context (what forced this decision), Decision (clear statement), Alternatives considered (why rejected), Consequences (positives / trade-offs / follow-ups), Evidence links (optional), Related bets (optional), Related OpenSpec changes (optional).
25
+
26
+ ### 4. Check for supersession
27
+ Ask: "Does this supersede an existing PDR? If so, which ID? (Enter to skip)"
28
+
29
+ ### 4b. Check for OKF frontmatter
30
+ Read `oprim/templates/pdr.md`. If it begins with a YAML frontmatter block (`---` ... `---`), this workspace has OKF frontmatter enabled. Ask for a one-line description and comma-separated tags (subject-area keywords). Prepare a frontmatter block with `type: pdr`, `title: <title>`, `description: <description>`, `tags: [<tags>]`, `timestamp: <today's date, ISO 8601>`, to prepend in step 5.
31
+ If no frontmatter block is found in the template, skip this step — write the file with no frontmatter, matching current behavior.
32
+
33
+ ### 5. Write the PDR file
34
+ Prepend the frontmatter block from step 4b, if one was prepared.
35
+ ```
36
+ # PDR-NNN: <title>
37
+
38
+ ## Status
39
+ Proposed
40
+
41
+ ## Context
42
+ <context>
43
+
44
+ ## Decision
45
+ <decision>
46
+
47
+ ## Alternatives considered
48
+ <alternatives as bullet list>
49
+
50
+ ## Consequences
51
+ - Positive: <...>
52
+ - Trade-offs: <...>
53
+ - Follow-ups: <...>
54
+
55
+ ## Evidence
56
+ <evidence or "None">
57
+
58
+ ## Related
59
+ - Bets: <BET-IDs or "None">
60
+ - OpenSpec: <change paths or "None">
61
+ - Supersedes: <PDR-ID or "None">
62
+ ```
63
+
64
+ ### 6. Update superseded PDR (if applicable)
65
+ Read the superseded file → replace Status value with `Superseded by PDR-NNN` → write back.
66
+
67
+ ### 7. Report what was created
@@ -0,0 +1,29 @@
1
+
2
+ Promote an atomic note into a bet, or a prioritized bet into a capability spec. The promotion path is determined solely by the prefix of the ID argument — there is no separate command for each.
3
+
4
+ **Input**: Specify an ID (e.g., `/oprim:promote BET-042` or `/oprim:promote NOTE-005`) or omit to be prompted.
5
+
6
+ ### 0. Determine the promotion path from the ID prefix
7
+ - `BET-` → **A. Bet → native oprim spec**
8
+ - `NOTE-` → **B. Note → Bet**
9
+ - Anything else → report "Unrecognized ID prefix — expected BET- or NOTE-" and stop. Do not silently do nothing.
10
+
11
+ ## A. Bet → native oprim spec
12
+
13
+ 1. **Locate the bet** — read `oprim/bets/BET-XXX/bet-decision.md`
14
+ 2. **Validate status** — decision must be "Build now"
15
+ 3. **Check authority boundary** — confirm primer artifact owns why/order/outcome only
16
+ 4. **Generate the native spec delta(s)** — for each capability listed under the bet's `## Capabilities` section (or a single capability derived from the bet title if none is listed), invoke the `oprim-spec` skill with this bet as context to write `oprim/bets/BET-XXX/specs/<capability>/spec.md` — a delta using `## ADDED`/`## MODIFIED`/`## REMOVED Requirements` headers reflecting the bet's why/outcome. No OpenSpec change directory is created and OpenSpec need not be installed. Nothing is written to `oprim/specs/<capability>/spec.md` (current truth) at promote time — that only happens when this bet is archived.
17
+ 5. **Link artifacts** — add `- Spec (delta): oprim/bets/BET-XXX/specs/<capability>/spec.md` (one line per capability) to the bet-decision `## Links` section
18
+ 6. **Copy criteria** — if `oprim/bets/BET-XXX/criteria.yaml` exists, note it alongside the spec link
19
+ 7. **Report** — show what was created and linked, and note that merge-on-archive will fold the delta into `oprim/specs/` when the bet archives
20
+
21
+ ## B. Note → Bet
22
+
23
+ 1. **Locate the note** — read `oprim/notes/NOTE-XXX-<slug>.md`
24
+ 2. **Assign the next BET ID** — scan both `oprim/bets/` and `oprim/bets/archived/` for directories matching `BET-(\d+)(-[^/]*)?`, max+1 zero-padded to 3 digits (default 001) — same convention `oprim-bet` uses
25
+ 3. **Derive the slug** from the note's title (lowercase, non-alphanumeric → hyphen, collapse/trim hyphens, truncate to 40 chars at a hyphen boundary)
26
+ 4. **Draft the bet** — write `oprim/bets/BET-NNN-<slug>/bet-decision.md` from the standard bet-decision structure, pre-filling only `## Why now` from the note's body. Leave `Alternatives considered`, `Expected outcomes`, and `Kill criteria / rollback trigger` as template placeholders — draft from the note, don't fabricate content it doesn't support. Ask for `Owner` and `Review date`; default `Decision: Build now` and `Date` to today.
27
+ 5. **Register the new bet** — append to `oprim/sequence.yaml` backlog: `{id, title, blocked_by: [], unlocks: [], requires_pdrs: []}`
28
+ 6. **Link back** — add `Bets: BET-NNN` to the note (creating or extending its `## Bets` section)
29
+ 7. **Report** — show the new bet's path and flag that `Alternatives considered`, `Expected outcomes`, and `Kill criteria` still need authoring before this bet can itself be promoted
@@ -0,0 +1,25 @@
1
+
2
+ Promote an atomic note into a bet, or a prioritized bet into a capability spec. The promotion path is determined solely by the prefix of the ID argument — there is no separate command for each.
3
+
4
+ **Input**: Specify an ID (e.g., `/oprim:promote BET-042` or `/oprim:promote NOTE-005`) or omit to be prompted.
5
+
6
+ ### 0. Determine the promotion path from the ID prefix
7
+ - `BET-` → **A. Bet → spec (no framework configured)**
8
+ - `NOTE-` → **B. Note → Bet**
9
+ - Anything else → report "Unrecognized ID prefix — expected BET- or NOTE-" and stop. Do not silently do nothing.
10
+
11
+ ## A. Bet → spec (no framework configured)
12
+
13
+ 1. **Locate the bet** — read `oprim/bets/BET-XXX/bet-decision.md`
14
+ 2. **Validate status** — decision must be "Build now"
15
+ 3. **Report and stop** — no speccing framework is configured (`integrations.spec_framework: none`). Add `- Spec: none (no speccing framework configured)` to the bet-decision `## Links` section. No spec artifact is created.
16
+
17
+ ## B. Note → Bet
18
+
19
+ 1. **Locate the note** — read `oprim/notes/NOTE-XXX-<slug>.md`
20
+ 2. **Assign the next BET ID** — scan both `oprim/bets/` and `oprim/bets/archived/` for directories matching `BET-(\d+)(-[^/]*)?`, max+1 zero-padded to 3 digits (default 001) — same convention `oprim-bet` uses
21
+ 3. **Derive the slug** from the note's title (lowercase, non-alphanumeric → hyphen, collapse/trim hyphens, truncate to 40 chars at a hyphen boundary)
22
+ 4. **Draft the bet** — write `oprim/bets/BET-NNN-<slug>/bet-decision.md` from the standard bet-decision structure, pre-filling only `## Why now` from the note's body. Leave `Alternatives considered`, `Expected outcomes`, and `Kill criteria / rollback trigger` as template placeholders — draft from the note, don't fabricate content it doesn't support. Ask for `Owner` and `Review date`; default `Decision: Build now` and `Date` to today.
23
+ 5. **Register the new bet** — append to `oprim/sequence.yaml` backlog: `{id, title, blocked_by: [], unlocks: [], requires_pdrs: []}`
24
+ 6. **Link back** — add `Bets: BET-NNN` to the note (creating or extending its `## Bets` section)
25
+ 7. **Report** — show the new bet's path and flag that `Alternatives considered`, `Expected outcomes`, and `Kill criteria` still need authoring before this bet can itself be promoted
@@ -0,0 +1,40 @@
1
+
2
+ Promote an atomic note into a bet, or a prioritized bet into a capability spec. The promotion path is determined solely by the prefix of the ID argument — there is no separate command for each.
3
+
4
+ **Input**: Specify an ID (e.g., `/oprim:promote BET-042` or `/oprim:promote NOTE-005`) or omit to be prompted.
5
+
6
+ ### 0. Determine the promotion path from the ID prefix
7
+ - `BET-` → **A. Bet → OpenSpec change**
8
+ - `NOTE-` → **B. Note → Bet**
9
+ - Anything else → report "Unrecognized ID prefix — expected BET- or NOTE-" and stop. Do not silently do nothing.
10
+
11
+ ## A. Bet → OpenSpec change
12
+
13
+ 1. **Locate the bet** — read `oprim/bets/BET-XXX/bet-decision.md`
14
+ 2. **Validate status** — decision must be "Build now"
15
+ 3. **Check authority boundary** — confirm primer artifact owns why/order/outcome only
16
+ 4. **Create OpenSpec change** — derive the change name as `bet-NNN-<slug>` where `NNN` is the zero-padded bet number (e.g. BET-004 → `bet-004`) and `<slug>` is a short kebab-case summary of the change. Then invoke the `/openspec-propose` skill (or `/opsx:propose`) with that name to create the change directory with **all required artifacts**: `proposal.md`, `design.md`, `tasks.md`, and `specs/<capability>/spec.md` for every capability listed under `## Capabilities`.
17
+ - Pass the bet decision content as context so the proposal reflects the bet's why/outcome
18
+ - **Do not manually create a partial change directory** — the propose skill ensures no artifact is omitted
19
+ - The spec file(s) are mandatory: each capability modified or added must have WHEN/THEN scenarios under `## ADDED Requirements` or `## MODIFIED Requirements`
20
+ 5. **Link artifacts**:
21
+ - Add OpenSpec change path to bet-decision `## Links` section
22
+ - Add bet ID to OpenSpec proposal `## Context` section
23
+ 6. **Copy criteria** — if `oprim/bets/BET-XXX/criteria.yaml` exists, link it from OpenSpec proposal
24
+ 7. **Verify completeness** — confirm the change directory contains:
25
+ - `proposal.md`
26
+ - `design.md`
27
+ - `tasks.md`
28
+ - `specs/<capability>/spec.md` for each capability in `## Capabilities`
29
+ If any artifact is missing, create it before reporting done.
30
+ 8. **Report** — show what was linked and what remains for engineering
31
+
32
+ ## B. Note → Bet
33
+
34
+ 1. **Locate the note** — read `oprim/notes/NOTE-XXX-<slug>.md`
35
+ 2. **Assign the next BET ID** — scan both `oprim/bets/` and `oprim/bets/archived/` for directories matching `BET-(\d+)(-[^/]*)?`, max+1 zero-padded to 3 digits (default 001) — same convention `oprim-bet` uses
36
+ 3. **Derive the slug** from the note's title (lowercase, non-alphanumeric → hyphen, collapse/trim hyphens, truncate to 40 chars at a hyphen boundary)
37
+ 4. **Draft the bet** — write `oprim/bets/BET-NNN-<slug>/bet-decision.md` from the standard bet-decision structure, pre-filling only `## Why now` from the note's body. Leave `Alternatives considered`, `Expected outcomes`, and `Kill criteria / rollback trigger` as template placeholders — draft from the note, don't fabricate content it doesn't support. Ask for `Owner` and `Review date`; default `Decision: Build now` and `Date` to today.
38
+ 5. **Register the new bet** — append to `oprim/sequence.yaml` backlog: `{id, title, blocked_by: [], unlocks: [], requires_pdrs: []}`
39
+ 6. **Link back** — add `Bets: BET-NNN` to the note (creating or extending its `## Bets` section)
40
+ 7. **Report** — show the new bet's path and flag that `Alternatives considered`, `Expected outcomes`, and `Kill criteria` still need authoring before this bet can itself be promoted
@@ -0,0 +1,14 @@
1
+ id: promote
2
+ skillName: null
3
+ title: "OPRIM: Promote"
4
+ description: Promote a note into a bet, or a prioritized bet into a capability spec
5
+ claude:
6
+ skill: false
7
+ command: promote.md
8
+ cursor:
9
+ skill: false
10
+ command: oprim-promote.md
11
+ poolside:
12
+ skill: false
13
+ inline: false
14
+ variants: [openspec, native, none]
@@ -0,0 +1 @@
1
+ Create KPI review in `oprim/reviews/YYYY-MM-DD-BET-NNN-kpi.md`. Read `oprim/config.yaml`'s `rules.review` — if non-empty, apply it as additional guidance and reflect it in the generated content; if empty, behavior is unchanged. Read `criteria.yaml` for pre-fill (baseline/target). Check `oprim/bets/BET-NNN/measurements/` for `run-*.yaml` files — if found, use the most recent to pre-populate actuals and status (include "Actuals from run: YYYY-MM-DD" note). If no run result, ask for each metric's actual value. Status: actual >= target → hit, actual < target → missed, not provided → pending. Ask reviewer name and decision quality notes. Write review with metric table and Actions checklist. Report what was created.
@@ -0,0 +1,11 @@
1
+ ### KPI review (oprim-review)
2
+ Create a KPI review artifact in `oprim/reviews/`.
3
+
4
+ 1. Ask which bet (e.g. BET-042).
5
+ 1b. Read `oprim/config.yaml`'s `rules.review` — if non-empty, apply it as additional guidance and reflect it in the generated content; if empty, behavior is unchanged.
6
+ 2. Read `oprim/bets/BET-NNN/criteria.yaml` for pre-fill. Check `oprim/bets/BET-NNN/measurements/` for `run-*.yaml` — use most recent if present.
7
+ 3. If no run result, ask for each metric's actual value.
8
+ 4. Status: actual >= target → hit; actual < target → missed; not provided → pending.
9
+ 5. Ask reviewer name and decision quality notes.
10
+ 6. Write `oprim/reviews/YYYY-MM-DD-BET-NNN-kpi.md`.
11
+ 7. Report what was created.
@@ -0,0 +1,14 @@
1
+ id: review
2
+ skillName: oprim-review
3
+ title: null
4
+ description: Create a KPI review artifact for a completed bet, pre-filled from criteria.yaml with actuals gathered from the user
5
+ cursorDescription: Create a KPI review artifact pre-filled from a bet's criteria contract
6
+ claude:
7
+ skill: true
8
+ command: null
9
+ cursor:
10
+ skill: true
11
+ command: oprim-review.md
12
+ poolside:
13
+ skill: true
14
+ inline: true
@@ -0,0 +1,69 @@
1
+ ---
2
+ name: oprim-review
3
+ description: Create a KPI review artifact for a completed bet, pre-filled from criteria.yaml with actuals gathered from the user
4
+ ---
5
+
6
+ Create a KPI review in `oprim/reviews/`.
7
+
8
+ **Interactive prompts:** Use the **AskUserQuestion tool** for every question in this skill — do not write questions as plain text.
9
+
10
+ ## Steps
11
+
12
+ ### 1. Identify the bet
13
+ If not provided, ask: "Which bet are you reviewing? (e.g. BET-042)"
14
+
15
+ ### 1b. Check for custom rules
16
+ Read `oprim/config.yaml`. If it has a non-empty `rules.review` value, treat it as additional guidance from the team — factor it into the questions you ask in step 4 and reflect it in the generated review content. If `rules.review` is absent or empty, skip this step; behavior is unchanged.
17
+
18
+ ### 2. Load criteria and check for a run result
19
+
20
+ Read `oprim/bets/BET-NNN/criteria.yaml` if it exists (pre-fills baseline and target).
21
+ If not found: inform user and continue with empty metrics list.
22
+
23
+ **Check for measurement run result:** Scan `oprim/bets/BET-NNN/measurements/` for files matching `run-*.yaml`. If any exist, sort by filename (date-based) and read the most recent.
24
+
25
+ **If a run result exists:** use it to pre-populate actuals and status for every metric. Skip step 3 for those metrics. Note the run date — include "Actuals from run: YYYY-MM-DD" in the review artifact.
26
+
27
+ **If no run result exists:** proceed to step 3 to gather actuals manually.
28
+
29
+ ### 3. Gather actuals per metric (only when no run result)
30
+ For each metric show name/baseline/target and ask: "What was the actual result? (number or 'pending')"
31
+
32
+ Status logic:
33
+ - actual >= target → `hit`
34
+ - actual < target → `missed`
35
+ - 'pending' or not provided → `pending`
36
+
37
+ ### 4. Get review metadata
38
+ Ask: reviewer name, decision quality notes.
39
+
40
+ ### 5. Output path
41
+ `oprim/reviews/YYYY-MM-DD-BET-NNN-kpi.md` (today's date)
42
+
43
+ ### 5b. Check for OKF frontmatter
44
+ Read `oprim/templates/kpi-review.md`. If it begins with a YAML frontmatter block (`---` ... `---`), this workspace has OKF frontmatter enabled. Ask for a one-line description and comma-separated tags (derived from the reviewed bet's subject area). Prepare a frontmatter block with `type: kpi-review`, `title: <bet ID and title>`, `description: <description>`, `tags: [<tags>]`, `timestamp: <review date, ISO 8601>`, to prepend in step 6.
45
+ If no frontmatter block is found in the template, skip this step — write the file with no frontmatter, matching current behavior.
46
+
47
+ ### 6. Write the review file
48
+ Prepend the frontmatter block from step 5b, if one was prepared.
49
+ ```markdown
50
+ # KPI Review: BET-NNN
51
+
52
+ **Review date:** YYYY-MM-DD
53
+ **Reviewed by:** <reviewer>
54
+ **Actuals from run:** YYYY-MM-DD ← include only when a run result was ingested
55
+
56
+ | Metric | Baseline | Target | Actual | Status |
57
+ |--------|----------|--------|--------|--------|
58
+ | <name> | <baseline> | <target> | <actual> | <status> |
59
+
60
+ ## Decision quality
61
+ <notes>
62
+
63
+ ## Actions
64
+ - [ ] Update bet-decision outcome section
65
+ - [ ] Update affected PDRs
66
+ - [ ] Re-sequence impacted bets
67
+ ```
68
+
69
+ ### 7. Report what was created
@@ -0,0 +1,12 @@
1
+
2
+ Validate the primer sequencing board and suggest rebalancing if needed.
3
+
4
+ **Steps**
5
+
6
+ 1. **Read board** — load `oprim/sequence.yaml`
7
+ 2. **Check WIP limits** — compare `now` count against `wip_limits.now`
8
+ 3. **Validate blockers** — for each bet in `now`, confirm all `blocked_by` entries are complete or absent
9
+ 4. **Validate PDR preconditions** — confirm all `requires_pdrs` entries exist in `oprim/decisions/`
10
+ 5. **Report violations** — list any WIP excess, unresolved blockers, or missing PDRs
11
+ 6. **Suggest moves** — recommend bets to defer to `next` or `later` to resolve violations
12
+ 7. **Regenerate view** — run `node oprim/scripts/generate-sequence-view.js` from the project root to update `oprim/sequence-view.md`
@@ -0,0 +1,10 @@
1
+ ### Sequencing board (oprim-sequence)
2
+ Validate the primer sequencing board and regenerate the visual view.
3
+
4
+ 1. **Read board** — load `oprim/sequence.yaml`
5
+ 2. **Check WIP limits** — compare `now` count against `wip_limits.now`
6
+ 3. **Validate blockers** — for each bet in `now`, confirm all `blocked_by` entries are complete or absent
7
+ 4. **Validate PDR preconditions** — confirm all `requires_pdrs` entries exist in `oprim/decisions/`
8
+ 5. **Report violations** — list any WIP excess, unresolved blockers, or missing PDRs
9
+ 6. **Suggest moves** — recommend bets to defer to `next` or `later` to resolve violations
10
+ 7. **Regenerate view** — run `node oprim/scripts/generate-sequence-view.js` from the project root to update `oprim/sequence-view.md`
@@ -0,0 +1,13 @@
1
+ id: sequence
2
+ skillName: oprim-sequence
3
+ title: "OPRIM: Sequence"
4
+ description: Validate and update the primer sequencing board
5
+ claude:
6
+ skill: true
7
+ command: sequence.md
8
+ cursor:
9
+ skill: false
10
+ command: oprim-sequence.md
11
+ poolside:
12
+ skill: true
13
+ inline: true
@@ -0,0 +1,88 @@
1
+ ---
2
+ name: oprim-sequence
3
+ description: Validate and update the primer sequencing board — triage mode computes board health and surfaces specific suggestions; seeded mode targets a specific context
4
+ ---
5
+
6
+ Manage the primer sequencing board in `oprim/sequence.yaml`.
7
+
8
+ **Interactive prompts:** Use the **AskUserQuestion tool** for every question in this skill — do not write questions as plain text.
9
+
10
+ ## Entry modes
11
+
12
+ **Triage mode** — no intention provided: reads board state, computes health, surfaces specific actionable suggestions.
13
+ **Seeded mode** — context pre-provided (e.g. from a lifecycle hook): skips full triage and targets the most relevant move for that context.
14
+
15
+ ## Steps
16
+
17
+ ### 1. Determine mode
18
+
19
+ If lifecycle context was provided as an argument or pre-seeded in the conversation (e.g. `bet-created`, `bet-promoted`, or a specific bet ID was just archived), enter **Seeded mode** — go to Step 2B.
20
+
21
+ Otherwise, enter **Triage mode** — go to Step 2A.
22
+
23
+ ### 2A. Triage mode — compute board health
24
+
25
+ Read `oprim/sequence.yaml`. Compute:
26
+
27
+ - **WIP utilization**: count entries in `now` vs `wip_limits.now`
28
+ - **WIP violations**: entries in `now` that exceed the WIP limit
29
+ - **Blocked Now bets**: entries in `now` whose `blocked_by` list contains a bet ID still present in any active lane (now/next/later/backlog)
30
+ - **Ready-to-pull bets**: entries in `next` whose `blocked_by` list is empty or all resolved (each blocked_by ID is absent from all active lanes)
31
+ - **PDR gaps**: entries in any lane whose `requires_pdrs` list contains a PDR ID not found in `oprim/decisions/`
32
+
33
+ Surface **ranked suggestions**, most urgent first:
34
+ 1. WIP violations → name each excess bet, suggest deferring to `next` or `later`
35
+ 2. Blocked Now bets → name the bet and its unresolved blocker, suggest deferring until blocker resolves
36
+ 3. Open Now slot (count < `wip_limits.now`) with a ready Next bet → name a specific bet to pull into `now`
37
+ 4. PDR gaps → name the bet and missing PDR, suggest creating it first
38
+
39
+ Each suggestion must name the exact bet ID, current lane, target lane, and reason.
40
+
41
+ If the board is healthy (no violations, no ready moves): report current WIP utilization and state the board is healthy. Offer to move something anyway if the user wants.
42
+
43
+ After surfacing suggestions, ask: "Which move would you like to make?"
44
+
45
+ ### 2B. Seeded mode — target specific context
46
+
47
+ Use the provided context to jump to the most relevant suggestion:
48
+
49
+ - `bet-created`: A new bet just landed in backlog. Check if `now` has an open slot and `next` has a ready bet to pull. If so, suggest the specific bet to pull. Otherwise, confirm the new bet is in backlog and the board looks healthy.
50
+ - `bet-promoted`: A bet was just promoted to an OpenSpec change. Verify the bet is still correctly sequenced and surface any resequencing action the promotion warrants.
51
+ - Bet archived (e.g. "BET-011 was archived"): Check if `now` dropped below `wip_limits.now`. If so, find the most ready bet in `next` and suggest pulling it.
52
+
53
+ If no relevant move is found for the provided context, fall back to Triage mode (Step 2A).
54
+
55
+ ### 3. Validate the requested move
56
+
57
+ Before executing any move, verify all three constraints:
58
+
59
+ **WIP limit**: Moving a bet to `now` must not push the count above `wip_limits.now`. If it would, explain the violation and suggest deferring an existing `now` bet first.
60
+
61
+ **Blocker resolution**: Moving a bet to `now` requires its `blocked_by` list to be empty or all resolved (IDs absent from all active lanes). If unresolved, name each unresolved blocker.
62
+
63
+ **PDR preconditions**: Moving a bet to `now` requires all `requires_pdrs` entries to exist as files in `oprim/decisions/`. If any are missing, name them and suggest creating them first.
64
+
65
+ If the move is invalid: explain which constraint failed and suggest the nearest valid alternative. Do not proceed to Step 4.
66
+
67
+ ### 4. Preview and confirm
68
+
69
+ Show the exact YAML change before writing. Name the entry that will move: its `id`, `title`, source lane, and target lane.
70
+
71
+ ```
72
+ Before: BET-005 is in next (now: 1/2 slots filled)
73
+ After: BET-005 moves to now (now: 2/2 slots filled)
74
+ ```
75
+
76
+ Ask: "Apply this change? (y/N)"
77
+ - If "n" or Enter: stop, no changes made.
78
+ - If "y": proceed to Step 5.
79
+
80
+ ### 5. Write sequence.yaml
81
+
82
+ Read `oprim/sequence.yaml`. Remove the bet entry from its current lane. Insert it into the target lane. Write back with 2-space indentation. Do not modify any other entries.
83
+
84
+ ### 6. Regenerate view
85
+
86
+ Run `node oprim/scripts/generate-sequence-view.js` from the project root to update `oprim/sequence-view.md`.
87
+
88
+ ### 7. Report what was done
@@ -0,0 +1,13 @@
1
+ id: spec-authoring
2
+ skillName: oprim-spec
3
+ title: null
4
+ description: Generate a native oprim capability spec delta at oprim/bets/BET-NNN-<slug>/specs/<capability>/spec.md while a bet is active, in RFC 2119 (SHALL/SHOULD/MAY) requirements and Gherkin scenarios — folded into oprim/specs/<capability>/spec.md (current truth) when the bet is archived
5
+ claude:
6
+ skill: true
7
+ command: null
8
+ cursor:
9
+ skill: true
10
+ command: null
11
+ poolside:
12
+ skill: true
13
+ inline: false
@@ -0,0 +1,68 @@
1
+ ---
2
+ name: oprim-spec
3
+ description: Generate a native oprim capability spec delta at oprim/bets/BET-NNN-<slug>/specs/<capability>/spec.md while a bet is active, in RFC 2119 (SHALL/SHOULD/MAY) requirements and Gherkin scenarios — folded into oprim/specs/<capability>/spec.md (current truth) when the bet is archived
4
+ ---
5
+
6
+ Generate a capability spec delta for an active bet — RFC 2119 requirements plus Gherkin scenarios, no OpenSpec required. This skill never writes to `oprim/specs/` directly; `oprim-archive` folds the delta into current truth when the bet is archived.
7
+
8
+ **Interactive prompts:** Use the **AskUserQuestion tool** for every question in this skill — do not write questions as plain text.
9
+
10
+ ## Steps
11
+
12
+ ### 1. Get the active bet
13
+ If a bet ID was provided as context (e.g. invoked from `/oprim:promote`), use it directly. Otherwise ask: "Which bet is this spec change for? (e.g. BET-005)"
14
+
15
+ Resolve it to a directory in `oprim/bets/` using the same two patterns `oprim-archive` uses: exact `BET-NNN/` (legacy, no slug) or the slug variant `BET-NNN-<slug>/`. If neither matches, report "Bet BET-NNN was not found in oprim/bets/ — spec deltas can only be authored against an active bet" and stop.
16
+
17
+ ### 2. Get the capability name and description
18
+ If not provided, ask: "What capability are you specifying? (a short name, e.g. 'spec-authoring')" and "What does it do? (one or two sentences)"
19
+
20
+ ### 2b. Derive the slug
21
+ From the capability name: lowercase all characters, replace any character that is not a letter or digit with a hyphen, collapse consecutive hyphens to one, strip leading/trailing hyphens. This becomes `<capability>`.
22
+ Output path: `oprim/bets/<resolved-bet-dir>/specs/<capability>/spec.md` (a delta, not `oprim/specs/<capability>/spec.md` — that file is current truth and is only ever written by `oprim-archive`'s merge step).
23
+
24
+ ### 2c. Check for custom rules
25
+ Read `oprim/config.yaml`. If it has a non-empty `rules.spec` value, treat it as additional guidance from the team — factor it into the requirements and scenarios you draft. If `rules.spec` is absent or empty, skip this step; behavior is unchanged.
26
+
27
+ ### 3. Determine the delta type for each requirement
28
+ For each requirement, ask whether it is new (**ADDED**), a change to an existing current-truth requirement (**MODIFIED**), or a removal of one (**REMOVED**).
29
+
30
+ - **ADDED**: gather the requirement statement fresh.
31
+ - **MODIFIED / REMOVED**: read `oprim/specs/<capability>/spec.md` if it exists and list its `### Requirement:` headers so the user can pick the one being changed. The header text must match exactly (whitespace-insensitive) for `oprim-archive`'s merge step to find it later. If the file doesn't exist yet, MODIFIED/REMOVED aren't possible for this capability — fall back to ADDED.
32
+
33
+ ### 4. Gather requirements and scenarios
34
+ For ADDED and MODIFIED requirements, phrase each as an RFC 2119 statement using SHALL (mandatory), SHOULD (recommended), or MAY (optional), then ask for at least one scenario: a WHEN (trigger) and a THEN (expected outcome), with an optional GIVEN (context) and additional AND steps. REMOVED requirements only need the matching header — no new scenarios.
35
+
36
+ ### 5. Write the delta file
37
+ Append to (or create) `oprim/bets/<resolved-bet-dir>/specs/<capability>/spec.md`, grouping requirements under the matching section header — only include a section if it has at least one requirement under it:
38
+
39
+ ```markdown
40
+ ## ADDED Requirements
41
+
42
+ ### Requirement: <capability> SHALL/SHOULD/MAY <requirement statement>
43
+ <one-sentence elaboration>
44
+
45
+ #### Scenario: <scenario title>
46
+ - **GIVEN** <context> (optional)
47
+ - **WHEN** <trigger>
48
+ - **THEN** <outcome>
49
+ - **AND** <additional outcome> (optional)
50
+
51
+ ## MODIFIED Requirements
52
+
53
+ ### Requirement: <exact header text matched from oprim/specs/<capability>/spec.md>
54
+ <revised elaboration>
55
+
56
+ #### Scenario: <scenario title>
57
+ - **WHEN** <trigger>
58
+ - **THEN** <outcome>
59
+
60
+ ## REMOVED Requirements
61
+
62
+ ### Requirement: <exact header text matched from oprim/specs/<capability>/spec.md>
63
+ ```
64
+
65
+ If the delta file already exists (a prior spec-authoring pass for this bet/capability), append new requirements to the matching section, creating that section if it's not yet present.
66
+
67
+ ### 6. Report what was created
68
+ Show the delta file path, which bet it's scoped to, and a summary of the ADDED/MODIFIED/REMOVED requirements captured. Note that it merges into `oprim/specs/<capability>/spec.md` when `BET-NNN` is archived — nothing is current truth yet.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-product-primer/cli",
3
- "version": "2.1.0",
3
+ "version": "2.2.0",
4
4
  "description": "Open Product Primer CLI — product decisions, sequencing, and KPI tracking for repositories",
5
5
  "keywords": [
6
6
  "product",
@@ -35,7 +35,7 @@
35
35
  "README.md"
36
36
  ],
37
37
  "scripts": {
38
- "build": "tsc",
38
+ "build": "tsc && node scripts/copy-workflow-assets.js",
39
39
  "dev": "tsc --watch",
40
40
  "start": "node dist/cli.js",
41
41
  "test": "vitest run",