@open-product-primer/cli 2.0.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.
- package/README.md +6 -0
- package/dist/cli.js +4 -0
- package/dist/commands/context.d.ts +2 -0
- package/dist/commands/context.js +188 -0
- package/dist/commands/doctor.js +49 -0
- package/dist/commands/validate.d.ts +2 -0
- package/dist/commands/validate.js +131 -0
- package/dist/lib/config-merge.js +5 -1
- package/dist/lib/install-agent.js +78 -938
- package/dist/lib/remote-context.d.ts +50 -0
- package/dist/lib/remote-context.js +299 -0
- package/dist/lib/spec-delta.d.ts +30 -0
- package/dist/lib/spec-delta.js +216 -0
- package/dist/lib/templates.js +2 -1
- package/dist/lib/validate-checks.d.ts +13 -0
- package/dist/lib/validate-checks.js +145 -0
- package/dist/lib/workflow-renderer.d.ts +19 -0
- package/dist/lib/workflow-renderer.js +103 -0
- package/dist/lib/workflow-schema.d.ts +37 -0
- package/dist/lib/workflow-schema.js +126 -0
- package/dist/workflows/archive.inline.md +10 -0
- package/dist/workflows/archive.schema.yaml +13 -0
- package/dist/workflows/archive.template.md +106 -0
- package/dist/workflows/bet.cursor-command.md +1 -0
- package/dist/workflows/bet.inline.md +13 -0
- package/dist/workflows/bet.schema.yaml +14 -0
- package/dist/workflows/bet.template.md +114 -0
- package/dist/workflows/context.schema.yaml +13 -0
- package/dist/workflows/context.template.md +34 -0
- package/dist/workflows/criteria.cursor-command.md +1 -0
- package/dist/workflows/criteria.inline.md +11 -0
- package/dist/workflows/criteria.schema.yaml +14 -0
- package/dist/workflows/criteria.template.md +52 -0
- package/dist/workflows/note.cursor-command.md +1 -0
- package/dist/workflows/note.inline.md +11 -0
- package/dist/workflows/note.schema.yaml +14 -0
- package/dist/workflows/note.template.md +66 -0
- package/dist/workflows/pdr.cursor-command.md +1 -0
- package/dist/workflows/pdr.inline.md +10 -0
- package/dist/workflows/pdr.schema.yaml +14 -0
- package/dist/workflows/pdr.template.md +67 -0
- package/dist/workflows/promote.native.template.md +29 -0
- package/dist/workflows/promote.none.template.md +25 -0
- package/dist/workflows/promote.openspec.template.md +40 -0
- package/dist/workflows/promote.schema.yaml +14 -0
- package/dist/workflows/review.cursor-command.md +1 -0
- package/dist/workflows/review.inline.md +11 -0
- package/dist/workflows/review.schema.yaml +14 -0
- package/dist/workflows/review.template.md +69 -0
- package/dist/workflows/sequence.cursor-command.md +12 -0
- package/dist/workflows/sequence.inline.md +10 -0
- package/dist/workflows/sequence.schema.yaml +13 -0
- package/dist/workflows/sequence.template.md +88 -0
- package/dist/workflows/spec-authoring.schema.yaml +13 -0
- package/dist/workflows/spec-authoring.template.md +68 -0
- package/package.json +2 -2
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: oprim-bet
|
|
3
|
+
description: Create a new bet directory and bet-decision artifact in oprim/bets/, and add the bet to oprim/sequence.yaml backlog
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Create a new bet in `oprim/bets/` and register it on the sequencing board.
|
|
7
|
+
|
|
8
|
+
**Interactive prompts:** Use the **AskUserQuestion tool** for every question in this skill — do not write questions as plain text.
|
|
9
|
+
|
|
10
|
+
## What you're creating
|
|
11
|
+
|
|
12
|
+
A bet is a product decision you're committing to explore: a problem worth solving, a hypothesis worth testing, or a direction worth taking. You'll name it, explain why now, and set a kill criterion so you know when to stop.
|
|
13
|
+
|
|
14
|
+
## Steps
|
|
15
|
+
|
|
16
|
+
### 1. Get the bet title
|
|
17
|
+
Display the naming convention before asking:
|
|
18
|
+
|
|
19
|
+
> **Naming tip:** Use "verb + object [for context]"
|
|
20
|
+
> - Good: "Improve bet naming for scannability"
|
|
21
|
+
> - Bad: "Naming"
|
|
22
|
+
|
|
23
|
+
If not provided, ask: "What is the title of this bet?"
|
|
24
|
+
|
|
25
|
+
After receiving the title, validate: if fewer than 4 words OR fewer than 25 characters:
|
|
26
|
+
- Show: "Warning: this title may be too vague to scan at a glance."
|
|
27
|
+
- Suggest a reformulation, e.g. "Consider: 'Improve <what> for <why>'"
|
|
28
|
+
- Ask: "Proceed with this title anyway? (y/N)"
|
|
29
|
+
- If "n" or Enter: ask for a revised title and re-validate
|
|
30
|
+
- If "y": proceed with the original title
|
|
31
|
+
|
|
32
|
+
### 2. Assign the next BET ID
|
|
33
|
+
Scan both `oprim/bets/` and `oprim/bets/archived/` for directories whose names match `BET-(\d+)(-[^/]*)?` (handles both `BET-NNN/` and `BET-NNN-<slug>/`). Extract the numeric part from each match. Assign max+1, zero-padded to 3 digits. Default `001` if none found in either location.
|
|
34
|
+
|
|
35
|
+
### 2b. Derive the slug
|
|
36
|
+
From the bet title: 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, truncate to 40 characters at the last hyphen boundary. This becomes `<slug>`. Example: "Add title slugs to bet directories for scannability" → `add-title-slugs-to-bet-dirs-for`.
|
|
37
|
+
|
|
38
|
+
### 3. Check sequence.yaml exists
|
|
39
|
+
If `oprim/sequence.yaml` not found: report and stop — advise `oprim init`.
|
|
40
|
+
|
|
41
|
+
### 3b. Check for custom rules
|
|
42
|
+
Read `oprim/config.yaml`. If it has a non-empty `rules.bet` 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 `bet-decision.md` content. If `rules.bet` is absent or empty, skip this step; behavior is unchanged.
|
|
43
|
+
|
|
44
|
+
### 4. Gather content
|
|
45
|
+
Ask: Decision (Build now / Defer / Kill, default Build now), Owner, Review date (YYYY-MM-DD), Why now, Alternatives considered, Expected outcomes (metric: baseline → target in timeframe), Kill criteria / rollback trigger, PDR links (optional).
|
|
46
|
+
|
|
47
|
+
Then ask about reversibility:
|
|
48
|
+
- "Is this a **2-way door** (reversible — easy to undo, safe to try) or a **1-way door** (hard to reverse — requires high confidence)?"
|
|
49
|
+
|
|
50
|
+
Then ask about each of the four risk dimensions (Low / Medium / High + short rationale):
|
|
51
|
+
- "**Value risk**: Will users/customers actually use or buy this? (Low / Medium / High — and why?)"
|
|
52
|
+
- "**Usability risk**: Can users figure out how to use it without help? (Low / Medium / High — and why?)"
|
|
53
|
+
- "**Feasibility risk**: Can we build this with our current skills, time, and technology? (Low / Medium / High — and why?)"
|
|
54
|
+
- "**Business viability risk**: Does this solution work for the business (revenue, legal, ops)? (Low / Medium / High — and why?)"
|
|
55
|
+
|
|
56
|
+
### 4b. Check for OKF frontmatter
|
|
57
|
+
Read `oprim/templates/bet-decision.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: bet-decision`, `title: <title>`, `description: <description>`, `tags: [<tags>]`, `timestamp: <today's date, ISO 8601>`, to prepend in step 5.
|
|
58
|
+
If no frontmatter block is found in the template, skip this step — write the file with no frontmatter, matching current behavior.
|
|
59
|
+
|
|
60
|
+
### 5. Write oprim/bets/BET-NNN-<slug>/bet-decision.md
|
|
61
|
+
Prepend the frontmatter block from step 4b, if one was prepared.
|
|
62
|
+
```
|
|
63
|
+
# Decision: BET-NNN <title>
|
|
64
|
+
<!-- Naming tip: verb + object [for context] — e.g. "Improve bet naming for scannability" not "Naming" -->
|
|
65
|
+
|
|
66
|
+
## Status
|
|
67
|
+
- Decision: <decision>
|
|
68
|
+
- Date: <today YYYY-MM-DD>
|
|
69
|
+
- Owner: <owner>
|
|
70
|
+
- Review date: <review date>
|
|
71
|
+
|
|
72
|
+
## Door type
|
|
73
|
+
- [<x if 2-way>] 2-way door (reversible — safe to try, easy to undo)
|
|
74
|
+
- [<x if 1-way>] 1-way door (hard to reverse — requires higher confidence before committing)
|
|
75
|
+
|
|
76
|
+
## Risk profile
|
|
77
|
+
- **Value risk**: <Low / Medium / High> — <rationale>
|
|
78
|
+
- **Usability risk**: <Low / Medium / High> — <rationale>
|
|
79
|
+
- **Feasibility risk**: <Low / Medium / High> — <rationale>
|
|
80
|
+
- **Business viability risk**: <Low / Medium / High> — <rationale>
|
|
81
|
+
|
|
82
|
+
## Why now
|
|
83
|
+
<why-now as bullet list>
|
|
84
|
+
|
|
85
|
+
## Alternatives considered
|
|
86
|
+
<alternatives as bullet list>
|
|
87
|
+
|
|
88
|
+
## Expected outcomes
|
|
89
|
+
<outcomes as bullet list>
|
|
90
|
+
|
|
91
|
+
## Kill criteria / rollback trigger
|
|
92
|
+
<kill criteria as bullet list>
|
|
93
|
+
|
|
94
|
+
## Links
|
|
95
|
+
- PDRs: <PDR-IDs or "None">
|
|
96
|
+
- OpenSpec change: <to be filled when promoted>
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
### 6. Append to oprim/sequence.yaml backlog
|
|
100
|
+
Read → parse YAML → append → write back (2-space indentation):
|
|
101
|
+
```yaml
|
|
102
|
+
- id: BET-NNN
|
|
103
|
+
title: "<title>"
|
|
104
|
+
blocked_by: []
|
|
105
|
+
unlocks: []
|
|
106
|
+
requires_pdrs: []
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
### 7. Prompt for optional discovery scaffolding
|
|
110
|
+
Ask: "Do you want to scaffold a discovery.md now? (y/N)"
|
|
111
|
+
- If "y": write `oprim/bets/BET-NNN-<slug>/discovery.md` from the discovery template (same structure as `oprim/templates/discovery.md`).
|
|
112
|
+
- If "n" or Enter: skip silently.
|
|
113
|
+
|
|
114
|
+
### 8. Report what was created
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
id: context
|
|
2
|
+
skillName: oprim-context-init
|
|
3
|
+
title: "OPRIM: Context Init"
|
|
4
|
+
description: Declare the current project a citable remote context, guided by a short Q&A to draft its description
|
|
5
|
+
claude:
|
|
6
|
+
skill: true
|
|
7
|
+
command: context-init.md
|
|
8
|
+
cursor:
|
|
9
|
+
skill: false
|
|
10
|
+
command: null
|
|
11
|
+
poolside:
|
|
12
|
+
skill: false
|
|
13
|
+
inline: false
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: oprim-context-init
|
|
3
|
+
description: Guide the user through drafting a description before declaring the current project a citable remote context
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Declare the current project a citable remote context, with a clear description other projects and agents can use to decide whether to pull it.
|
|
7
|
+
|
|
8
|
+
**Interactive prompts:** Use the **AskUserQuestion tool** for every question in this skill — do not write questions as plain text.
|
|
9
|
+
|
|
10
|
+
## What this does
|
|
11
|
+
|
|
12
|
+
A remote context is an oprim workspace (decisions, bets, specs) that other projects can reference read-only via `oprim context register`. Without a description, other projects have no cheap way to know what a remote context covers short of fully pulling it — so this skill exists to make sure one gets written.
|
|
13
|
+
|
|
14
|
+
## Steps
|
|
15
|
+
|
|
16
|
+
### 1. Check for an existing identity
|
|
17
|
+
Check whether `.oprim-context/context.yaml` already exists in the current project. If it does, report that a remote context identity already exists (do not re-run the drafting flow below) and stop.
|
|
18
|
+
|
|
19
|
+
### 2. Ask what this workspace covers
|
|
20
|
+
Ask the user, one at a time:
|
|
21
|
+
- "What does this project's oprim workspace cover? (e.g. product decisions, a specific domain, a team's specs)"
|
|
22
|
+
- "Who is this meant for — which teams or projects would reference it?"
|
|
23
|
+
|
|
24
|
+
If the user declines to answer either question, treat that as opting out of guided drafting — skip to step 4 with no description.
|
|
25
|
+
|
|
26
|
+
### 3. Draft and confirm the description
|
|
27
|
+
From the answers, draft a single-sentence description (aim for under 120 characters — this is what `oprim context list` will show other projects). Show the draft to the user and ask: "Use this description? (Enter to accept, or type a replacement)"
|
|
28
|
+
|
|
29
|
+
### 4. Call oprim context init
|
|
30
|
+
- If a description was drafted or accepted: use the Bash tool to run `oprim context init --description "<final text>"`.
|
|
31
|
+
- If the user opted out in step 2: warn clearly that the resulting remote context will show as description-less in `oprim context list`, then use the Bash tool to run `oprim context init` with no `--description` flag.
|
|
32
|
+
|
|
33
|
+
### 5. Report what was created
|
|
34
|
+
Report the path (`.oprim-context/context.yaml`) and the description that was set (or the description-less warning, if opted out).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Add metrics to `oprim/bets/BET-NNN/criteria.yaml`. Verify bet dir exists. Gather: metric ID, name, baseline, target, timeframe, launch date, segment. Ask source type (amplitude or bigquery). Amplitude: event, aggregation, denominator_event. BigQuery: table, metric_column, filter, aggregation, denominator_query. If file exists: append to metrics list (never overwrite). If not: create. Ask if adding more metrics. Report what was created.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
### Criteria authoring (oprim-criteria)
|
|
2
|
+
Create or append to `oprim/bets/BET-NNN/criteria.yaml`.
|
|
3
|
+
|
|
4
|
+
1. Ask which bet (e.g. BET-042). Verify dir exists.
|
|
5
|
+
2. Gather: metric ID (snake_case), name, baseline, target, timeframe, launch date, segment.
|
|
6
|
+
3. Ask source type (amplitude / bigquery).
|
|
7
|
+
- Amplitude: event, aggregation (unique_users/event_count/property_sum), denominator_event.
|
|
8
|
+
- BigQuery: table, metric_column, filter, aggregation, denominator_query.
|
|
9
|
+
4. If file exists: append to `metrics` list (never overwrite). If not: create.
|
|
10
|
+
5. Ask if adding more metrics.
|
|
11
|
+
6. Report what was created.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
id: criteria
|
|
2
|
+
skillName: oprim-criteria
|
|
3
|
+
title: null
|
|
4
|
+
description: Create or append to a criteria.yaml contract for a bet, with structured Amplitude and BigQuery source mapping
|
|
5
|
+
cursorDescription: Create or append to a criteria.yaml contract for a bet
|
|
6
|
+
claude:
|
|
7
|
+
skill: true
|
|
8
|
+
command: null
|
|
9
|
+
cursor:
|
|
10
|
+
skill: true
|
|
11
|
+
command: oprim-criteria.md
|
|
12
|
+
poolside:
|
|
13
|
+
skill: true
|
|
14
|
+
inline: true
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: oprim-criteria
|
|
3
|
+
description: Create or append to a criteria.yaml contract for a bet, with structured Amplitude and BigQuery source mapping
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Create or append to `oprim/bets/BET-NNN/criteria.yaml`.
|
|
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 adding criteria for? (e.g. BET-042)"
|
|
14
|
+
|
|
15
|
+
### 2. Verify bet exists
|
|
16
|
+
If `oprim/bets/BET-NNN/` not found: report and stop — advise using the `oprim-bet` skill first.
|
|
17
|
+
|
|
18
|
+
### 3. Gather metric details
|
|
19
|
+
Ask: metric ID (snake_case), metric name, baseline (numeric), target (numeric), timeframe, launch date (YYYY-MM-DD or TBD), segment (optional).
|
|
20
|
+
|
|
21
|
+
### 4. Gather source mapping
|
|
22
|
+
Ask: source type (amplitude / bigquery)
|
|
23
|
+
|
|
24
|
+
If amplitude: event name, aggregation (unique_users / event_count / property_sum), denominator event (optional).
|
|
25
|
+
```yaml
|
|
26
|
+
source:
|
|
27
|
+
type: amplitude
|
|
28
|
+
definition:
|
|
29
|
+
event: <event_name>
|
|
30
|
+
aggregation: <aggregation>
|
|
31
|
+
denominator_event: <event_name | null>
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
If bigquery: table, metric column, SQL filter, aggregation (sum / count / count_distinct / avg), denominator query (optional).
|
|
35
|
+
```yaml
|
|
36
|
+
source:
|
|
37
|
+
type: bigquery
|
|
38
|
+
definition:
|
|
39
|
+
table: "<project.dataset.table>"
|
|
40
|
+
metric_column: "<column>"
|
|
41
|
+
filter: "<sql_filter>"
|
|
42
|
+
aggregation: <aggregation>
|
|
43
|
+
denominator_query: <sql | null>
|
|
44
|
+
```
|
|
45
|
+
|
|
46
|
+
### 5. Build metric entry and write
|
|
47
|
+
If file exists: read → parse → append to `metrics` → write back (never overwrite).
|
|
48
|
+
If not: create with `metrics:` list.
|
|
49
|
+
|
|
50
|
+
### 6. Ask if more metrics needed. If yes, return to step 3.
|
|
51
|
+
|
|
52
|
+
### 7. Report what was created
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Create a new note in `oprim/notes/` for lightweight thinking capture — an observation, idea, or connection that hasn't yet earned a place in a bet or PDR. Notes carry no owner or kill criterion; promote one into a bet later with `/oprim:promote NOTE-NNN`. Ask for a short title. Scan `oprim/notes/NOTE-(\d+)-` for the next id (zero-padded, default 001). Ask for the note body (free-form), tags, and optional related BET-IDs. Tags are checked against `oprim/config.yaml`'s `notes.tags`; any new tag is accepted and appended to that list rather than rejected — the vocabulary grows from usage. Read `oprim/templates/note.md` — if its frontmatter has a `description:` field, this workspace is on the OKF tier and needs a one-line description; if it has no `description:` field, use the minimal tier; if the file doesn't exist, fall back to reading `okf.enabled` directly from `oprim/config.yaml`. Write `oprim/notes/NOTE-NNN-<slug>.md` with the correct frontmatter tier and a `## Bets` section listing any related BET-IDs. For each related bet, append `- Notes: NOTE-NNN` to that bet-decision's `## Links` section. Report what was created.
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
### Note authoring (oprim-note)
|
|
2
|
+
Create a new note in `oprim/notes/` for lightweight thinking capture — not a bet, no owner or kill criterion.
|
|
3
|
+
|
|
4
|
+
1. Ask for a short title.
|
|
5
|
+
2. Assign next NOTE ID: scan `oprim/notes/NOTE-(\d+)-`, max+1 zero-padded to 3 digits (default 001).
|
|
6
|
+
3. Ask for the note body (free-form), tags, and optional related BET-IDs.
|
|
7
|
+
4. Tags: check against `oprim/config.yaml`'s `notes.tags` — accept and append any new tag rather than rejecting it (the vocabulary grows from usage).
|
|
8
|
+
5. Check `oprim/templates/note.md`: a `description:` field in its frontmatter means the OKF tier (gather a one-line description); no field means the minimal tier; if the file is missing, fall back to `okf.enabled` in `oprim/config.yaml`.
|
|
9
|
+
6. Write `oprim/notes/NOTE-NNN-<slug>.md` with the correct frontmatter tier and a `## Bets` section.
|
|
10
|
+
7. For each related bet, append `- Notes: NOTE-NNN` to that bet's `## Links` section.
|
|
11
|
+
8. Report what was created.
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
id: note
|
|
2
|
+
skillName: oprim-note
|
|
3
|
+
title: null
|
|
4
|
+
description: Create a new atomic note in oprim/notes/ for lightweight thinking capture, with tiered frontmatter and optional bet links
|
|
5
|
+
cursorDescription: Create a new atomic note for lightweight thinking capture
|
|
6
|
+
claude:
|
|
7
|
+
skill: true
|
|
8
|
+
command: null
|
|
9
|
+
cursor:
|
|
10
|
+
skill: true
|
|
11
|
+
command: oprim-note.md
|
|
12
|
+
poolside:
|
|
13
|
+
skill: true
|
|
14
|
+
inline: true
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: oprim-note
|
|
3
|
+
description: Create a new atomic note in oprim/notes/ for lightweight thinking capture, with tiered frontmatter and optional bet links
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
Create a new note in `oprim/notes/` for lightweight thinking capture — an observation, idea, or connection that hasn't yet earned a place in a bet or PDR.
|
|
7
|
+
|
|
8
|
+
**Interactive prompts:** Use the **AskUserQuestion tool** for every question in this skill — do not write questions as plain text.
|
|
9
|
+
|
|
10
|
+
## What you're creating
|
|
11
|
+
|
|
12
|
+
A note is a small, disposable unit of thinking: an observation, a stray idea, or a connection between bets, captured before it's proven enough to belong in a discovery hypothesis or bet-decision. Notes carry no owner and no kill criterion — they're not commitments. Promote a note into a bet later with `/oprim:promote NOTE-NNN` once it's worth committing to.
|
|
13
|
+
|
|
14
|
+
## Steps
|
|
15
|
+
|
|
16
|
+
### 1. Get the note title
|
|
17
|
+
If not provided, ask: "What is this note about? (a short title)"
|
|
18
|
+
|
|
19
|
+
### 2. Assign the next NOTE ID
|
|
20
|
+
Scan `oprim/notes/` for files matching `NOTE-(\d+)-`. Extract the numeric part from each match. Assign max+1, zero-padded to 3 digits. Default `001` if none found.
|
|
21
|
+
|
|
22
|
+
### 2b. Derive the slug
|
|
23
|
+
From the note title: 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, truncate to 40 characters at the last hyphen boundary. This becomes `<slug>`.
|
|
24
|
+
Output path: `oprim/notes/NOTE-NNN-<slug>.md`
|
|
25
|
+
|
|
26
|
+
### 3. Gather the note body
|
|
27
|
+
Ask: "What's the observation, idea, or connection?" (free-form prose — this becomes the note body).
|
|
28
|
+
|
|
29
|
+
### 4. Gather tags
|
|
30
|
+
Read `oprim/config.yaml`. If it has a `notes:` section with a `tags:` list, show it and ask the user to pick from it or add new ones. If `notes.tags` is absent or empty, ask for tags directly (comma-separated) — there's no vocabulary yet to constrain against.
|
|
31
|
+
A tag not already in `notes.tags` SHALL be accepted, never rejected, and appended to `oprim/config.yaml`'s `notes.tags` list (creating the `notes:` section if absent) — the vocabulary grows from usage rather than requiring upfront authoring.
|
|
32
|
+
|
|
33
|
+
### 5. Gather optional bet links
|
|
34
|
+
Ask: "Does this relate to any existing bets? (comma-separated BET-IDs, or Enter to skip)"
|
|
35
|
+
|
|
36
|
+
### 6. Check the frontmatter tier
|
|
37
|
+
Read `oprim/templates/note.md`.
|
|
38
|
+
- If it exists and its frontmatter block contains a `description:` field, this workspace is on the **OKF tier** — ask for a one-line description.
|
|
39
|
+
- If it exists with no `description:` field, use the **minimal tier** — skip the description.
|
|
40
|
+
- If the file doesn't exist (project initialized before notes were introduced), read `oprim/config.yaml` directly: `okf.enabled: true` → OKF tier (ask for a description); otherwise → minimal tier.
|
|
41
|
+
|
|
42
|
+
### 7. Write oprim/notes/NOTE-NNN-<slug>.md
|
|
43
|
+
|
|
44
|
+
Minimal tier:
|
|
45
|
+
```
|
|
46
|
+
---
|
|
47
|
+
type: note
|
|
48
|
+
title: "<title>"
|
|
49
|
+
tags: [<tags>]
|
|
50
|
+
timestamp: <today, ISO 8601>
|
|
51
|
+
---
|
|
52
|
+
|
|
53
|
+
# Note: <title>
|
|
54
|
+
|
|
55
|
+
<body>
|
|
56
|
+
|
|
57
|
+
## Bets
|
|
58
|
+
- <BET-IDs from step 5, or "None">
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
OKF tier: same as above, with `description: "<description>"` inserted immediately after `title`.
|
|
62
|
+
|
|
63
|
+
### 8. Link back from referenced bets
|
|
64
|
+
For each BET-ID gathered in step 5: read `oprim/bets/BET-NNN/bet-decision.md`, and add `- Notes: NOTE-NNN` under its `## Links` section (append to an existing `Notes:` line, or add a new one).
|
|
65
|
+
|
|
66
|
+
### 9. Report what was created
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
Create a new PDR in `oprim/decisions/`. Scan for `PDR-(\d+)-` to assign next ID (zero-padded, default 001). 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. Gather: title, context, decision, alternatives, consequences, evidence, related bets/specs. Ask if superseding an existing PDR. Write `oprim/decisions/PDR-NNN-<slug>.md`. If superseding: update old PDR Status to "Superseded by PDR-NNN". Report what was created.
|
|
@@ -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
|