@open-product-primer/cli 0.4.1 → 0.5.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.
@@ -68,6 +68,10 @@ function detectAvailableAgents(projectRoot) {
68
68
  detected.push('claude');
69
69
  if (fs.existsSync(path.join(projectRoot, '.cursor')))
70
70
  detected.push('cursor');
71
+ if (fs.existsSync(path.join(projectRoot, 'AGENTS.md')))
72
+ detected.push('codex');
73
+ if (fs.existsSync(path.join(projectRoot, 'GEMINI.md')))
74
+ detected.push('gemini');
71
75
  return detected;
72
76
  }
73
77
  function writeAgentsToConfig(agents, projectRoot) {
@@ -1,4 +1,4 @@
1
- export type Agent = 'claude' | 'cursor';
1
+ export type Agent = 'claude' | 'cursor' | 'codex' | 'gemini';
2
2
  export declare const SUPPORTED_AGENTS: readonly Agent[];
3
3
  export declare function promptFrameworkSelection(projectRoot: string): Promise<string>;
4
4
  export declare function promptAgentSelection(projectRoot: string): Promise<string[]>;
@@ -7,3 +7,6 @@ export declare const CLAUDE_SKILLS: Record<string, string>;
7
7
  export declare const CLAUDE_COMMANDS: Record<string, string>;
8
8
  export declare const CURSOR_SKILLS: Record<string, string>;
9
9
  export declare const CURSOR_COMMANDS: Record<string, string>;
10
+ export declare function writeAgentInstructionFile(filePath: string, section: string): void;
11
+ export declare function codexInstructions(): string;
12
+ export declare function geminiInstructions(): string;
@@ -40,12 +40,15 @@ exports.CURSOR_COMMANDS = exports.CURSOR_SKILLS = exports.CLAUDE_COMMANDS = expo
40
40
  exports.promptFrameworkSelection = promptFrameworkSelection;
41
41
  exports.promptAgentSelection = promptAgentSelection;
42
42
  exports.installAgentSkills = installAgentSkills;
43
+ exports.writeAgentInstructionFile = writeAgentInstructionFile;
44
+ exports.codexInstructions = codexInstructions;
45
+ exports.geminiInstructions = geminiInstructions;
43
46
  const path = __importStar(require("path"));
44
47
  const fs = __importStar(require("fs"));
45
48
  const chalk_1 = __importDefault(require("chalk"));
46
49
  const scaffold_1 = require("./scaffold");
47
50
  const detect_1 = require("./detect");
48
- exports.SUPPORTED_AGENTS = ['claude', 'cursor'];
51
+ exports.SUPPORTED_AGENTS = ['claude', 'cursor', 'codex', 'gemini'];
49
52
  async function promptFrameworkSelection(projectRoot) {
50
53
  const configPath = path.join(projectRoot, '.claude', 'hooks', 'config.json');
51
54
  if (fs.existsSync(configPath)) {
@@ -81,6 +84,8 @@ async function promptAgentSelection(projectRoot) {
81
84
  choices: [
82
85
  { name: 'Claude Code', value: 'claude', checked: detected.includes('claude') },
83
86
  { name: 'Cursor', value: 'cursor', checked: detected.includes('cursor') },
87
+ { name: 'Codex', value: 'codex', checked: detected.includes('codex') },
88
+ { name: 'Gemini CLI', value: 'gemini', checked: detected.includes('gemini') },
84
89
  ],
85
90
  });
86
91
  }
@@ -131,6 +136,16 @@ function installAgentSkills(agent, projectRoot, framework = 'openspec') {
131
136
  console.log(chalk_1.default.dim(' .claude/ created — Claude Code will discover these files automatically.'));
132
137
  }
133
138
  }
139
+ else if (agent === 'codex') {
140
+ const agentsFile = path.join(projectRoot, 'AGENTS.md');
141
+ writeAgentInstructionFile(agentsFile, codexInstructions());
142
+ console.log(chalk_1.default.green('✓') + ' AGENTS.md (oprim section written)');
143
+ }
144
+ else if (agent === 'gemini') {
145
+ const geminiFile = path.join(projectRoot, 'GEMINI.md');
146
+ writeAgentInstructionFile(geminiFile, geminiInstructions());
147
+ console.log(chalk_1.default.green('✓') + ' GEMINI.md (oprim section written)');
148
+ }
134
149
  else if (agent === 'cursor') {
135
150
  const cursorDir = path.join(projectRoot, '.cursor');
136
151
  const dirCreated = !fs.existsSync(cursorDir);
@@ -707,3 +722,92 @@ Validate the primer sequencing board and suggest rebalancing if needed.
707
722
  6. **Suggest moves** — recommend bets to defer to \`next\` or \`later\` to resolve violations
708
723
  `;
709
724
  }
725
+ // ─── Instruction-file helpers (Codex / Gemini CLI) ────────────────────────────
726
+ const OPRIM_START = '<!-- oprim:start -->';
727
+ const OPRIM_END = '<!-- oprim:end -->';
728
+ function writeAgentInstructionFile(filePath, section) {
729
+ const delimited = `${OPRIM_START}\n${section}\n${OPRIM_END}`;
730
+ if (!fs.existsSync(filePath)) {
731
+ fs.writeFileSync(filePath, delimited + '\n', 'utf-8');
732
+ return;
733
+ }
734
+ const existing = fs.readFileSync(filePath, 'utf-8');
735
+ const startIdx = existing.indexOf(OPRIM_START);
736
+ const endIdx = existing.indexOf(OPRIM_END);
737
+ if (startIdx !== -1 && endIdx !== -1 && endIdx > startIdx) {
738
+ const before = existing.slice(0, startIdx);
739
+ const after = existing.slice(endIdx + OPRIM_END.length);
740
+ fs.writeFileSync(filePath, before + delimited + after, 'utf-8');
741
+ }
742
+ else {
743
+ const separator = existing.endsWith('\n') ? '\n' : '\n\n';
744
+ fs.writeFileSync(filePath, existing + separator + delimited + '\n', 'utf-8');
745
+ }
746
+ }
747
+ function oprimWorkflowsInline() {
748
+ return `
749
+ ## oprim workflows
750
+
751
+ ### Bet authoring (oprim-bet)
752
+ Create a new bet in \`oprim/bets/\` and register it on the sequencing board.
753
+
754
+ 1. Show naming tip: "verb + object [for context] — e.g. 'Improve bet naming for scannability'"
755
+ 2. Ask for the bet title. Validate: fewer than 4 words OR fewer than 25 chars → warn, suggest reformulation, ask "Proceed anyway? (y/N)".
756
+ 3. Assign next BET ID: scan \`oprim/bets/BET-(\\d+)\` dirs, max+1 zero-padded to 3 digits (default 001).
757
+ 4. Check \`oprim/sequence.yaml\` exists — stop if not, advise \`oprim init\`.
758
+ 5. Gather: decision (default Build now), owner, review date (YYYY-MM-DD), why now, alternatives, expected outcomes, kill criteria, PDR links.
759
+ 6. Write \`oprim/bets/BET-NNN/bet-decision.md\` with all fields.
760
+ 7. Append to \`oprim/sequence.yaml\` backlog: \`{id, title, blocked_by: [], unlocks: [], requires_pdrs: []}\`.
761
+ 8. Ask: "Scaffold a discovery.md now? (y/N)" — if "y", write \`oprim/bets/BET-NNN/discovery.md\`.
762
+ 9. Report what was created.
763
+
764
+ ### Criteria authoring (oprim-criteria)
765
+ Create or append to \`oprim/bets/BET-NNN/criteria.yaml\`.
766
+
767
+ 1. Ask which bet (e.g. BET-042). Verify dir exists.
768
+ 2. Gather: metric ID (snake_case), name, baseline, target, timeframe, launch date, segment.
769
+ 3. Ask source type (amplitude / bigquery).
770
+ - Amplitude: event, aggregation (unique_users/event_count/property_sum), denominator_event.
771
+ - BigQuery: table, metric_column, filter, aggregation, denominator_query.
772
+ 4. If file exists: append to \`metrics\` list (never overwrite). If not: create.
773
+ 5. Ask if adding more metrics.
774
+ 6. Report what was created.
775
+
776
+ ### PDR authoring (oprim-pdr)
777
+ Create a new Product Decision Record in \`oprim/decisions/\`.
778
+
779
+ 1. Ask for decision title.
780
+ 2. Assign next PDR ID: scan \`oprim/decisions/PDR-(\\d+)-\`, max+1 zero-padded to 3 digits (default 001).
781
+ 3. Gather: context, decision, alternatives, consequences, evidence, related bets/specs.
782
+ 4. Ask if superseding an existing PDR.
783
+ 5. Write \`oprim/decisions/PDR-NNN-<slug>.md\`. If superseding, update old PDR Status.
784
+ 6. Report what was created.
785
+
786
+ ### KPI review (oprim-review)
787
+ Create a KPI review artifact in \`oprim/reviews/\`.
788
+
789
+ 1. Ask which bet (e.g. BET-042).
790
+ 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.
791
+ 3. If no run result, ask for each metric's actual value.
792
+ 4. Status: actual >= target → hit; actual < target → missed; not provided → pending.
793
+ 5. Ask reviewer name and decision quality notes.
794
+ 6. Write \`oprim/reviews/YYYY-MM-DD-BET-NNN-kpi.md\`.
795
+ 7. Report what was created.
796
+
797
+ ### Bet archiving (oprim-archive)
798
+ Archive a completed bet.
799
+
800
+ 1. Ask for bet ID (accept bet-005, 005, 5, BET-005 — normalize to BET-NNN).
801
+ 2. Verify \`oprim/bets/BET-NNN/\` exists.
802
+ 3. Check \`oprim/sequence.yaml\` for entries where \`blocked_by\` or \`unlocks\` reference the target bet — warn if found, ask "Archive anyway? (y/N)".
803
+ 4. Move directory: \`oprim/bets/BET-NNN → oprim/bets/archived/BET-NNN\`.
804
+ 5. Remove the bet entry from \`oprim/sequence.yaml\`.
805
+ 6. Report what was done.
806
+ `;
807
+ }
808
+ function codexInstructions() {
809
+ return oprimWorkflowsInline();
810
+ }
811
+ function geminiInstructions() {
812
+ return oprimWorkflowsInline();
813
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-product-primer/cli",
3
- "version": "0.4.1",
3
+ "version": "0.5.0",
4
4
  "description": "Open Product Primer CLI — product decisions, sequencing, and KPI tracking for repositories",
5
5
  "keywords": [
6
6
  "product",