@open-product-primer/cli 0.7.1 → 0.10.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/dist/lib/install-agent.js +195 -45
- package/package.json +1 -1
|
@@ -272,6 +272,7 @@ exports.CLAUDE_SKILLS = {
|
|
|
272
272
|
'oprim-criteria': criteriaSkill(),
|
|
273
273
|
'oprim-review': reviewSkill(),
|
|
274
274
|
'oprim-archive': archiveSkill(),
|
|
275
|
+
'oprim-sequence': oprimSequenceSkill(),
|
|
275
276
|
};
|
|
276
277
|
// ─── Claude command wrappers (thin, invoke skill) ────────────────────────────
|
|
277
278
|
exports.CLAUDE_COMMANDS = {
|
|
@@ -289,7 +290,7 @@ exports.CURSOR_SKILLS = {
|
|
|
289
290
|
// ─── Cursor command files (full inline — no Skill tool in Cursor) ────────────
|
|
290
291
|
exports.CURSOR_COMMANDS = {
|
|
291
292
|
'oprim-promote.md': cursorWrapper('oprim-promote', 'Promote a prioritized bet to an OpenSpec change', promoteContent()),
|
|
292
|
-
'oprim-sequence.md': cursorWrapper('oprim-sequence', 'Validate and update the primer sequencing board',
|
|
293
|
+
'oprim-sequence.md': cursorWrapper('oprim-sequence', 'Validate and update the primer sequencing board', sequenceInlineContent()),
|
|
293
294
|
'oprim-pdr.md': cursorWrapper('oprim-pdr', 'Create a new Product Decision Record with auto-assigned ID', pdrInlineContent()),
|
|
294
295
|
'oprim-bet.md': cursorWrapper('oprim-bet', 'Create a new bet decision and register it on the sequencing board', betInlineContent()),
|
|
295
296
|
'oprim-criteria.md': cursorWrapper('oprim-criteria', 'Create or append to a criteria.yaml contract for a bet', criteriaInlineContent()),
|
|
@@ -327,6 +328,8 @@ description: Create a new Product Decision Record in oprim/decisions/ with auto-
|
|
|
327
328
|
|
|
328
329
|
Create a new Product Decision Record (PDR) in \`oprim/decisions/\`.
|
|
329
330
|
|
|
331
|
+
**Interactive prompts:** Use the **AskUserQuestion tool** for every question in this skill — do not write questions as plain text.
|
|
332
|
+
|
|
330
333
|
## Steps
|
|
331
334
|
|
|
332
335
|
### 1. Get the decision title
|
|
@@ -387,6 +390,8 @@ description: Create a new bet directory and bet-decision artifact in oprim/bets/
|
|
|
387
390
|
|
|
388
391
|
Create a new bet in \`oprim/bets/\` and register it on the sequencing board.
|
|
389
392
|
|
|
393
|
+
**Interactive prompts:** Use the **AskUserQuestion tool** for every question in this skill — do not write questions as plain text.
|
|
394
|
+
|
|
390
395
|
## Steps
|
|
391
396
|
|
|
392
397
|
### 1. Get the bet title
|
|
@@ -406,7 +411,10 @@ After receiving the title, validate: if fewer than 4 words OR fewer than 25 char
|
|
|
406
411
|
- If "y": proceed with the original title
|
|
407
412
|
|
|
408
413
|
### 2. Assign the next BET ID
|
|
409
|
-
Scan \`oprim/bets/\` for directories
|
|
414
|
+
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.
|
|
415
|
+
|
|
416
|
+
### 2b. Derive the slug
|
|
417
|
+
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\`.
|
|
410
418
|
|
|
411
419
|
### 3. Check sequence.yaml exists
|
|
412
420
|
If \`oprim/sequence.yaml\` not found: report and stop — advise \`oprim init\`.
|
|
@@ -414,7 +422,7 @@ If \`oprim/sequence.yaml\` not found: report and stop — advise \`oprim init\`.
|
|
|
414
422
|
### 4. Gather content
|
|
415
423
|
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).
|
|
416
424
|
|
|
417
|
-
### 5. Write oprim/bets/BET-NNN
|
|
425
|
+
### 5. Write oprim/bets/BET-NNN-<slug>/bet-decision.md
|
|
418
426
|
\`\`\`
|
|
419
427
|
# Decision: BET-NNN <title>
|
|
420
428
|
<!-- Naming tip: verb + object [for context] — e.g. "Improve bet naming for scannability" not "Naming" -->
|
|
@@ -454,7 +462,7 @@ Read → parse YAML → append → write back (2-space indentation):
|
|
|
454
462
|
|
|
455
463
|
### 7. Prompt for optional discovery scaffolding
|
|
456
464
|
Ask: "Do you want to scaffold a discovery.md now? (y/N)"
|
|
457
|
-
- If "y": write \`oprim/bets/BET-NNN
|
|
465
|
+
- If "y": write \`oprim/bets/BET-NNN-<slug>/discovery.md\` from the discovery template (same structure as \`oprim/templates/discovery.md\`).
|
|
458
466
|
- If "n" or Enter: skip silently.
|
|
459
467
|
|
|
460
468
|
### 8. Report what was created
|
|
@@ -468,6 +476,8 @@ description: Create or append to a criteria.yaml contract for a bet, with struct
|
|
|
468
476
|
|
|
469
477
|
Create or append to \`oprim/bets/BET-NNN/criteria.yaml\`.
|
|
470
478
|
|
|
479
|
+
**Interactive prompts:** Use the **AskUserQuestion tool** for every question in this skill — do not write questions as plain text.
|
|
480
|
+
|
|
471
481
|
## Steps
|
|
472
482
|
|
|
473
483
|
### 1. Identify the bet
|
|
@@ -516,11 +526,13 @@ If not: create with \`metrics:\` list.
|
|
|
516
526
|
function archiveSkill() {
|
|
517
527
|
return `---
|
|
518
528
|
name: oprim-archive
|
|
519
|
-
description: Archive a completed bet — moves it to oprim/bets/archived/
|
|
529
|
+
description: Archive a completed bet — moves it to oprim/bets/archived/ and removes its sequence.yaml entry
|
|
520
530
|
---
|
|
521
531
|
|
|
522
532
|
Archive a completed bet by moving it to \`oprim/bets/archived/\` and removing it from \`sequence.yaml\`.
|
|
523
533
|
|
|
534
|
+
**Interactive prompts:** Use the **AskUserQuestion tool** for every question in this skill — do not write questions as plain text.
|
|
535
|
+
|
|
524
536
|
## Steps
|
|
525
537
|
|
|
526
538
|
### 1. Get the bet ID
|
|
@@ -531,11 +543,19 @@ If not provided, ask: "Which bet ID would you like to archive? (e.g., BET-005)"
|
|
|
531
543
|
|
|
532
544
|
Normalize the input: accept \`bet-005\`, \`005\`, \`5\`, or \`BET-005\` — always treat as \`BET-NNN\` zero-padded to 3 digits.
|
|
533
545
|
|
|
534
|
-
### 2.
|
|
546
|
+
### 2. Resolve the bet directory
|
|
547
|
+
|
|
548
|
+
Look for the bet directory in \`oprim/bets/\` using two patterns:
|
|
549
|
+
1. Exact match: \`oprim/bets/BET-NNN/\` (legacy non-slug format)
|
|
550
|
+
2. Slug variant: any directory starting with \`BET-NNN-\` (e.g., \`BET-NNN-<slug>/\`)
|
|
535
551
|
|
|
536
|
-
|
|
552
|
+
Use whichever pattern matches. Call this the **resolved directory name**.
|
|
553
|
+
|
|
554
|
+
If multiple directories match (e.g., both \`BET-NNN/\` and \`BET-NNN-slug/\` exist):
|
|
555
|
+
- Report: "Ambiguous: found multiple directories for BET-NNN: [list them]. Please archive manually."
|
|
556
|
+
- Stop.
|
|
537
557
|
|
|
538
|
-
If
|
|
558
|
+
If neither pattern matches:
|
|
539
559
|
- Report: "Bet BET-NNN was not found in oprim/bets/. Nothing was changed."
|
|
540
560
|
- Stop.
|
|
541
561
|
|
|
@@ -565,9 +585,9 @@ Create the archive subfolder if it doesn't exist:
|
|
|
565
585
|
mkdir -p oprim/bets/archived
|
|
566
586
|
\`\`\`
|
|
567
587
|
|
|
568
|
-
Move the directory:
|
|
588
|
+
Move the resolved directory:
|
|
569
589
|
\`\`\`bash
|
|
570
|
-
mv oprim/bets
|
|
590
|
+
mv oprim/bets/<resolved-dir> oprim/bets/archived/<resolved-dir>
|
|
571
591
|
\`\`\`
|
|
572
592
|
|
|
573
593
|
### 5. Remove the bet entry from sequence.yaml
|
|
@@ -580,7 +600,7 @@ Read \`oprim/sequence.yaml\`, parse it, and remove the entry with \`id: BET-NNN\
|
|
|
580
600
|
## Bet Archived
|
|
581
601
|
|
|
582
602
|
**Bet:** BET-NNN
|
|
583
|
-
**Archived to:** oprim/bets/archived
|
|
603
|
+
**Archived to:** oprim/bets/archived/<resolved-dir>/
|
|
584
604
|
**Removed from sequence.yaml:** ✓
|
|
585
605
|
|
|
586
606
|
The bet is preserved in full at the archive location.
|
|
@@ -617,6 +637,97 @@ List each match on its own line, then return — the invoking skill continues to
|
|
|
617
637
|
If no PDRs match: exit silently — produce no output.
|
|
618
638
|
`;
|
|
619
639
|
}
|
|
640
|
+
function oprimSequenceSkill() {
|
|
641
|
+
return `---
|
|
642
|
+
name: oprim-sequence
|
|
643
|
+
description: Validate and update the primer sequencing board — triage mode computes board health and surfaces specific suggestions; seeded mode targets a specific context
|
|
644
|
+
---
|
|
645
|
+
|
|
646
|
+
Manage the primer sequencing board in \`oprim/sequence.yaml\`.
|
|
647
|
+
|
|
648
|
+
**Interactive prompts:** Use the **AskUserQuestion tool** for every question in this skill — do not write questions as plain text.
|
|
649
|
+
|
|
650
|
+
## Entry modes
|
|
651
|
+
|
|
652
|
+
**Triage mode** — no intention provided: reads board state, computes health, surfaces specific actionable suggestions.
|
|
653
|
+
**Seeded mode** — context pre-provided (e.g. from a lifecycle hook): skips full triage and targets the most relevant move for that context.
|
|
654
|
+
|
|
655
|
+
## Steps
|
|
656
|
+
|
|
657
|
+
### 1. Determine mode
|
|
658
|
+
|
|
659
|
+
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.
|
|
660
|
+
|
|
661
|
+
Otherwise, enter **Triage mode** — go to Step 2A.
|
|
662
|
+
|
|
663
|
+
### 2A. Triage mode — compute board health
|
|
664
|
+
|
|
665
|
+
Read \`oprim/sequence.yaml\`. Compute:
|
|
666
|
+
|
|
667
|
+
- **WIP utilization**: count entries in \`now\` vs \`wip_limits.now\`
|
|
668
|
+
- **WIP violations**: entries in \`now\` that exceed the WIP limit
|
|
669
|
+
- **Blocked Now bets**: entries in \`now\` whose \`blocked_by\` list contains a bet ID still present in any active lane (now/next/later/backlog)
|
|
670
|
+
- **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)
|
|
671
|
+
- **PDR gaps**: entries in any lane whose \`requires_pdrs\` list contains a PDR ID not found in \`oprim/decisions/\`
|
|
672
|
+
|
|
673
|
+
Surface **ranked suggestions**, most urgent first:
|
|
674
|
+
1. WIP violations → name each excess bet, suggest deferring to \`next\` or \`later\`
|
|
675
|
+
2. Blocked Now bets → name the bet and its unresolved blocker, suggest deferring until blocker resolves
|
|
676
|
+
3. Open Now slot (count < \`wip_limits.now\`) with a ready Next bet → name a specific bet to pull into \`now\`
|
|
677
|
+
4. PDR gaps → name the bet and missing PDR, suggest creating it first
|
|
678
|
+
|
|
679
|
+
Each suggestion must name the exact bet ID, current lane, target lane, and reason.
|
|
680
|
+
|
|
681
|
+
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.
|
|
682
|
+
|
|
683
|
+
After surfacing suggestions, ask: "Which move would you like to make?"
|
|
684
|
+
|
|
685
|
+
### 2B. Seeded mode — target specific context
|
|
686
|
+
|
|
687
|
+
Use the provided context to jump to the most relevant suggestion:
|
|
688
|
+
|
|
689
|
+
- \`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.
|
|
690
|
+
- \`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.
|
|
691
|
+
- 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.
|
|
692
|
+
|
|
693
|
+
If no relevant move is found for the provided context, fall back to Triage mode (Step 2A).
|
|
694
|
+
|
|
695
|
+
### 3. Validate the requested move
|
|
696
|
+
|
|
697
|
+
Before executing any move, verify all three constraints:
|
|
698
|
+
|
|
699
|
+
**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.
|
|
700
|
+
|
|
701
|
+
**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.
|
|
702
|
+
|
|
703
|
+
**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.
|
|
704
|
+
|
|
705
|
+
If the move is invalid: explain which constraint failed and suggest the nearest valid alternative. Do not proceed to Step 4.
|
|
706
|
+
|
|
707
|
+
### 4. Preview and confirm
|
|
708
|
+
|
|
709
|
+
Show the exact YAML change before writing. Name the entry that will move: its \`id\`, \`title\`, source lane, and target lane.
|
|
710
|
+
|
|
711
|
+
\`\`\`
|
|
712
|
+
Before: BET-005 is in next (now: 1/2 slots filled)
|
|
713
|
+
After: BET-005 moves to now (now: 2/2 slots filled)
|
|
714
|
+
\`\`\`
|
|
715
|
+
|
|
716
|
+
Ask: "Apply this change? (y/N)"
|
|
717
|
+
- If "n" or Enter: stop, no changes made.
|
|
718
|
+
- If "y": proceed to Step 5.
|
|
719
|
+
|
|
720
|
+
### 5. Write sequence.yaml
|
|
721
|
+
|
|
722
|
+
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.
|
|
723
|
+
|
|
724
|
+
### 6. Regenerate view
|
|
725
|
+
|
|
726
|
+
Run \`node oprim/scripts/generate-sequence-view.js\` from the project root to update \`oprim/sequence-view.md\`.
|
|
727
|
+
|
|
728
|
+
### 7. Report what was done
|
|
729
|
+
`;
|
|
730
|
+
}
|
|
620
731
|
function archiveCommandContent() {
|
|
621
732
|
return `Use the Skill tool to invoke the \`oprim-archive\` skill.`;
|
|
622
733
|
}
|
|
@@ -628,6 +739,8 @@ description: Create a KPI review artifact for a completed bet, pre-filled from c
|
|
|
628
739
|
|
|
629
740
|
Create a KPI review in \`oprim/reviews/\`.
|
|
630
741
|
|
|
742
|
+
**Interactive prompts:** Use the **AskUserQuestion tool** for every question in this skill — do not write questions as plain text.
|
|
743
|
+
|
|
631
744
|
## Steps
|
|
632
745
|
|
|
633
746
|
### 1. Identify the bet
|
|
@@ -703,54 +816,88 @@ function hooksConfig(framework) {
|
|
|
703
816
|
}, null, 2) + '\n');
|
|
704
817
|
}
|
|
705
818
|
const ON_PROMPT_SUBMIT_HOOK = `#!/usr/bin/env bash
|
|
706
|
-
# UserPromptSubmit hook: detects
|
|
819
|
+
# UserPromptSubmit hook: detects lifecycle slash commands and sets pending flags.
|
|
707
820
|
|
|
708
|
-
|
|
709
|
-
|
|
821
|
+
archive_flag=".claude/hooks/.archive-pending"
|
|
822
|
+
nudge_flag=".claude/hooks/.sequence-nudge"
|
|
710
823
|
|
|
711
|
-
|
|
712
|
-
if [ -f "$config_file" ]; then
|
|
713
|
-
framework=$(python3 -c "import sys,json; print(json.load(open('$config_file')).get('framework','openspec'))" 2>/dev/null || echo "openspec")
|
|
714
|
-
fi
|
|
824
|
+
input=$(cat)
|
|
715
825
|
|
|
716
|
-
|
|
826
|
+
# Archive co-archival detection
|
|
827
|
+
if printf '%s' "$input" | grep -qE '"prompt"[[:space:]]*:[[:space:]]*"[^"]*(opsx:archive|openspec-archive-change)'; then
|
|
828
|
+
prompt=$(printf '%s' "$input" | grep -oE '"prompt"[[:space:]]*:[[:space:]]*"[^"]*"' | sed 's/.*"prompt"[[:space:]]*:[[:space:]]*"//;s/"$//')
|
|
829
|
+
arg=$(printf '%s' "$prompt" | sed 's|^[[:space:]]*/[^[:space:]]* *||' | awk '{print $1}' | sed 's|^@||' | sed 's|.*/changes/||' | sed 's|/$||' | xargs 2>/dev/null || true)
|
|
830
|
+
printf '%s' "$arg" > "$archive_flag"
|
|
831
|
+
fi
|
|
717
832
|
|
|
718
|
-
|
|
833
|
+
# Bet creation detection
|
|
834
|
+
if printf '%s' "$input" | grep -qE '"prompt"[[:space:]]*:[[:space:]]*"[^"]*oprim:bet'; then
|
|
835
|
+
printf 'bet-created' > "$nudge_flag"
|
|
836
|
+
fi
|
|
719
837
|
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
echo "$arg" > "$flag_file"
|
|
724
|
-
fi
|
|
838
|
+
# Bet promotion detection
|
|
839
|
+
if printf '%s' "$input" | grep -qE '"prompt"[[:space:]]*:[[:space:]]*"[^"]*oprim:promote'; then
|
|
840
|
+
printf 'bet-promoted' > "$nudge_flag"
|
|
725
841
|
fi
|
|
726
842
|
`;
|
|
727
843
|
const ON_STOP_HOOK = `#!/usr/bin/env bash
|
|
728
|
-
# Stop hook:
|
|
844
|
+
# Stop hook: archive co-archival coordination and sequencing nudges.
|
|
729
845
|
|
|
730
|
-
|
|
731
|
-
|
|
846
|
+
archive_flag=".claude/hooks/.archive-pending"
|
|
847
|
+
nudge_flag=".claude/hooks/.sequence-nudge"
|
|
732
848
|
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
if [ -z "$change" ]; then
|
|
737
|
-
latest=$(ls openspec/changes/archive/ 2>/dev/null | sort -r | head -1)
|
|
738
|
-
[ -z "$latest" ] && exit 0
|
|
739
|
-
change=$(echo "$latest" | sed -E 's/^[0-9]{4}-[0-9]{2}-[0-9]{2}-//')
|
|
740
|
-
fi
|
|
849
|
+
# --- Archive co-archival block ---
|
|
850
|
+
if [ -f "$archive_flag" ]; then
|
|
851
|
+
change=$(tr -d '[:space:]' < "$archive_flag")
|
|
741
852
|
|
|
742
|
-
[ -z "$change" ]
|
|
853
|
+
if [ -z "$change" ]; then
|
|
854
|
+
latest=$(ls openspec/changes/archive/ 2>/dev/null | sort -r | head -1)
|
|
855
|
+
[ -n "$latest" ] && change=$(echo "$latest" | sed -E 's/^[0-9]{4}-[0-9]{2}-[0-9]{2}-//')
|
|
856
|
+
fi
|
|
743
857
|
|
|
744
|
-
|
|
745
|
-
|
|
858
|
+
if [ -n "$change" ]; then
|
|
859
|
+
archive_dir=$(ls openspec/changes/archive/ 2>/dev/null | grep -F "$change" | sort -r | head -1)
|
|
860
|
+
if [ -n "$archive_dir" ]; then
|
|
861
|
+
rm -f "$archive_flag"
|
|
862
|
+
proposal="openspec/changes/archive/$archive_dir/proposal.md"
|
|
863
|
+
if [ -f "$proposal" ]; then
|
|
864
|
+
bet_id=$(grep -oE 'BET-[0-9]+' "$proposal" | head -1)
|
|
865
|
+
if [ -n "$bet_id" ]; then
|
|
866
|
+
printf '{"decision":"block","reason":"The openspec change '''%s''' was just archived. Its proposal.md references %s. Please invoke \`/oprim:archive %s\` to co-archive the linked bet."}\\n' "$change" "$bet_id" "$bet_id"
|
|
867
|
+
exit 0
|
|
868
|
+
fi
|
|
869
|
+
fi
|
|
870
|
+
fi
|
|
871
|
+
fi
|
|
872
|
+
fi
|
|
746
873
|
|
|
747
|
-
|
|
748
|
-
[ -f "$
|
|
874
|
+
# --- Sequencing nudge from lifecycle event ---
|
|
875
|
+
if [ -f "$nudge_flag" ]; then
|
|
876
|
+
context=$(cat "$nudge_flag")
|
|
877
|
+
rm -f "$nudge_flag"
|
|
878
|
+
|
|
879
|
+
case "$context" in
|
|
880
|
+
bet-created)
|
|
881
|
+
printf '\\n💡 A new bet was added to your backlog. Run \`/oprim:sequence\` to check if it should be pulled into Now or Next.\\n'
|
|
882
|
+
;;
|
|
883
|
+
bet-promoted)
|
|
884
|
+
printf '\\n💡 A bet was promoted to an OpenSpec change. Run \`/oprim:sequence\` to verify the board reflects this.\\n'
|
|
885
|
+
;;
|
|
886
|
+
esac
|
|
887
|
+
fi
|
|
749
888
|
|
|
750
|
-
|
|
751
|
-
[ -
|
|
889
|
+
# --- Open Now slot check ---
|
|
890
|
+
if [ -f "oprim/sequence.yaml" ]; then
|
|
891
|
+
wip_limit=$(awk '/^wip_limits:/{in_wip=1} in_wip && /^ now:/{print $2; exit} /^[^ ]/{in_wip=0}' oprim/sequence.yaml 2>/dev/null)
|
|
892
|
+
now_count=$(awk '/^now:/{in_now=1; next} in_now && /^[^ ]/{in_now=0} in_now && /^ - id:/{count++} END{print count+0}' oprim/sequence.yaml 2>/dev/null)
|
|
893
|
+
next_count=$(awk '/^next:/{in_next=1; next} in_next && /^[^ ]/{in_next=0} in_next && /^ - id:/{count++} END{print count+0}' oprim/sequence.yaml 2>/dev/null)
|
|
752
894
|
|
|
753
|
-
|
|
895
|
+
if [ -n "$wip_limit" ] && [ -n "$now_count" ] && [ -n "$next_count" ]; then
|
|
896
|
+
if [ "$now_count" -lt "$wip_limit" ] 2>/dev/null && [ "$next_count" -gt 0 ] 2>/dev/null; then
|
|
897
|
+
printf '\\n💡 Now lane has capacity (%s/%s). Run \`/oprim:sequence\` to pull something from Next.\\n' "$now_count" "$wip_limit"
|
|
898
|
+
fi
|
|
899
|
+
fi
|
|
900
|
+
fi
|
|
754
901
|
`;
|
|
755
902
|
// Merge UserPromptSubmit + Stop hooks into .claude/settings.json without clobbering existing entries.
|
|
756
903
|
// Also removes the legacy PostToolUse/Skill hook from on-skill-archive.sh if present.
|
|
@@ -810,7 +957,7 @@ function mergeClaudeSettingsHooks(claudeDir) {
|
|
|
810
957
|
fs.writeFileSync(settingsPath, JSON.stringify(settings, null, 2) + '\n', 'utf-8');
|
|
811
958
|
console.log(chalk_1.default.green('✓') + ' .claude/settings.json (UserPromptSubmit + Stop hooks registered)');
|
|
812
959
|
}
|
|
813
|
-
// ─── Legacy content (promote
|
|
960
|
+
// ─── Legacy content (promote remains inline; sequence now delegates to skill) ─
|
|
814
961
|
function promoteContent() {
|
|
815
962
|
return `
|
|
816
963
|
Promote a prioritized bet to an OpenSpec change and link criteria contracts.
|
|
@@ -840,6 +987,9 @@ Promote a prioritized bet to an OpenSpec change and link criteria contracts.
|
|
|
840
987
|
`;
|
|
841
988
|
}
|
|
842
989
|
function sequenceContent() {
|
|
990
|
+
return `Use the Skill tool to invoke the \`oprim-sequence\` skill.`;
|
|
991
|
+
}
|
|
992
|
+
function sequenceInlineContent() {
|
|
843
993
|
return `
|
|
844
994
|
Validate the primer sequencing board and suggest rebalancing if needed.
|
|
845
995
|
|