@open-product-primer/cli 0.1.0 → 0.1.2

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/cli.js CHANGED
@@ -12,8 +12,8 @@ const measure_1 = require("./commands/measure");
12
12
  const package_json_1 = __importDefault(require("../package.json"));
13
13
  const program = new commander_1.Command();
14
14
  program
15
- .name('open-product-primer')
16
- .description('Open Product Primer — product decisions, sequencing, and KPI tracking')
15
+ .name('oprim')
16
+ .description('oprim — product decisions, sequencing, and KPI tracking')
17
17
  .version(package_json_1.default.version);
18
18
  program.addCommand((0, init_1.initCommand)());
19
19
  program.addCommand((0, update_1.updateCommand)());
@@ -49,7 +49,7 @@ const AGENT_DIRS = {
49
49
  };
50
50
  function doctorCommand() {
51
51
  return new commander_1.Command('doctor')
52
- .description('Check Open Product Primer install health and integration readiness')
52
+ .description('Check oprim install health and integration readiness')
53
53
  .action(() => {
54
54
  const projectRoot = process.cwd();
55
55
  const checks = [];
@@ -154,7 +154,7 @@ function doctorCommand() {
154
154
  required: false,
155
155
  });
156
156
  }
157
- console.log(chalk_1.default.bold('Open Product Primer') + ' — health check\n');
157
+ console.log(chalk_1.default.bold('oprim') + ' — health check\n');
158
158
  for (const check of checks) {
159
159
  const icon = check.pass ? chalk_1.default.green('✓') : check.required ? chalk_1.default.red('✗') : chalk_1.default.yellow('○');
160
160
  const label = check.pass ? chalk_1.default.white(check.name) : chalk_1.default.gray(check.name);
@@ -46,13 +46,13 @@ const install_agent_1 = require("../lib/install-agent");
46
46
  const templates_1 = require("../lib/templates");
47
47
  function initCommand() {
48
48
  return new commander_1.Command('init')
49
- .description('Initialize Open Product Primer in the current repository')
49
+ .description('Initialize oprim in the current repository')
50
50
  .option('--name <name>', 'project name (defaults to directory name)')
51
51
  .option('--agent <name>', 'AI agent to install skills for (repeatable; supported: claude, cursor)', (val, prev) => [...prev, val], [])
52
52
  .action(async (opts) => {
53
53
  const projectRoot = process.cwd();
54
54
  const projectName = opts.name ?? path.basename(projectRoot);
55
- console.log(chalk_1.default.bold('Open Product Primer') + ' — initializing project workspace...\n');
55
+ console.log(chalk_1.default.bold('oprim') + ' — initializing project workspace...\n');
56
56
  const openspec = (0, detect_1.detectOpenSpec)(projectRoot);
57
57
  const graphify = (0, detect_1.detectGraphify)(projectRoot);
58
58
  if (openspec.detected)
@@ -93,22 +93,13 @@ function initCommand() {
93
93
  }
94
94
  else {
95
95
  const existingAgents = (0, detect_1.readAgentsFromConfig)(projectRoot);
96
- if (existingAgents !== null) {
96
+ if (existingAgents !== null && existingAgents.length > 0) {
97
97
  selectedAgents = existingAgents;
98
- if (selectedAgents.length > 0) {
99
- console.log('\n' + chalk_1.default.dim(`Re-installing for configured agents: ${selectedAgents.join(', ')}`));
100
- }
98
+ console.log('\n' + chalk_1.default.dim(`Re-installing for configured agents: ${selectedAgents.join(', ')}`));
101
99
  }
102
100
  else {
103
101
  console.log('');
104
- const { checkbox } = await Promise.resolve().then(() => __importStar(require('@inquirer/prompts')));
105
- selectedAgents = await checkbox({
106
- message: 'Which AI tools should /oprim:* skills be installed for?',
107
- choices: [
108
- { name: 'Claude Code', value: 'claude' },
109
- { name: 'Cursor', value: 'cursor' },
110
- ],
111
- });
102
+ selectedAgents = await (0, install_agent_1.promptAgentSelection)(projectRoot);
112
103
  }
113
104
  }
114
105
  (0, detect_1.writeAgentsToConfig)(selectedAgents, projectRoot);
@@ -46,7 +46,7 @@ const detect_1 = require("../lib/detect");
46
46
  function updateCommand() {
47
47
  return new commander_1.Command('update')
48
48
  .description('Refresh /oprim:* assistant commands and skills from package templates')
49
- .action(() => {
49
+ .action(async () => {
50
50
  const projectRoot = process.cwd();
51
51
  const configAgents = (0, detect_1.readAgentsFromConfig)(projectRoot);
52
52
  if (configAgents !== null && configAgents.length > 0) {
@@ -55,27 +55,50 @@ function updateCommand() {
55
55
  }
56
56
  console.log(`\nAgent skills updated: ${configAgents.join(', ')}`);
57
57
  }
58
- else if (configAgents !== null && configAgents.length === 0) {
59
- console.log(chalk_1.default.yellow('No agents configured') + ' — run ' + chalk_1.default.cyan('oprim init') + ' to select agents');
60
- }
61
58
  else {
62
59
  // Legacy: fall back to directory detection
63
- let updated = 0;
60
+ const legacyAgents = [];
64
61
  if (fs.existsSync(path.join(projectRoot, '.claude'))) {
65
62
  (0, install_agent_1.installAgentSkills)('claude', projectRoot);
66
- updated++;
63
+ legacyAgents.push('claude');
67
64
  }
68
65
  if (fs.existsSync(path.join(projectRoot, '.cursor'))) {
69
66
  (0, install_agent_1.installAgentSkills)('cursor', projectRoot);
70
- updated++;
67
+ legacyAgents.push('cursor');
71
68
  }
72
- if (updated === 0) {
73
- console.log(chalk_1.default.yellow('No assistant environments detected (.claude/, .cursor/).'));
74
- console.log('Run this command from a repository where AI tools are configured.');
69
+ if (legacyAgents.length > 0) {
70
+ console.log(`\nAgent skills updated for ${legacyAgents.length} environment(s).`);
75
71
  }
76
72
  else {
77
- console.log(`\nAgent skills updated for ${updated} environment(s).`);
73
+ console.log(chalk_1.default.yellow('No agents configured or detected.'));
78
74
  }
79
75
  }
76
+ // ── Post-update: offer to add more agents ────────────────────────────────
77
+ const currentAgents = configAgents ?? [];
78
+ console.log('');
79
+ const { confirm } = await Promise.resolve().then(() => __importStar(require('@inquirer/prompts')));
80
+ const addMore = await confirm({
81
+ message: 'Would you like to install skills for additional agents?',
82
+ default: false,
83
+ });
84
+ if (!addMore) {
85
+ console.log('\nRun ' + chalk_1.default.cyan('oprim doctor') + ' to verify your setup.');
86
+ return;
87
+ }
88
+ console.log('');
89
+ const selected = await (0, install_agent_1.promptAgentSelection)(projectRoot);
90
+ if (selected.length === 0) {
91
+ console.log('\n' + chalk_1.default.yellow('No agents selected.'));
92
+ console.log('\nRun ' + chalk_1.default.cyan('oprim doctor') + ' to verify your setup.');
93
+ return;
94
+ }
95
+ console.log('\n' + chalk_1.default.bold('Installing agent skills...'));
96
+ for (const agent of selected) {
97
+ (0, install_agent_1.installAgentSkills)(agent, projectRoot);
98
+ }
99
+ const merged = Array.from(new Set([...currentAgents, ...selected]));
100
+ (0, detect_1.writeAgentsToConfig)(merged, projectRoot);
101
+ console.log('\n' + chalk_1.default.green('✓') + ` Agent skills installed: ${selected.join(', ')}`);
102
+ console.log('\nRun ' + chalk_1.default.cyan('oprim doctor') + ' to verify your setup.');
80
103
  });
81
104
  }
@@ -7,4 +7,5 @@ export declare function detectGraphify(projectRoot: string): {
7
7
  graphDir: string | null;
8
8
  };
9
9
  export declare function readAgentsFromConfig(projectRoot: string): string[] | null;
10
+ export declare function detectAvailableAgents(projectRoot: string): string[];
10
11
  export declare function writeAgentsToConfig(agents: string[], projectRoot: string): void;
@@ -36,6 +36,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.detectOpenSpec = detectOpenSpec;
37
37
  exports.detectGraphify = detectGraphify;
38
38
  exports.readAgentsFromConfig = readAgentsFromConfig;
39
+ exports.detectAvailableAgents = detectAvailableAgents;
39
40
  exports.writeAgentsToConfig = writeAgentsToConfig;
40
41
  const fs = __importStar(require("fs"));
41
42
  const path = __importStar(require("path"));
@@ -61,6 +62,14 @@ function readAgentsFromConfig(projectRoot) {
61
62
  return null;
62
63
  return agents;
63
64
  }
65
+ function detectAvailableAgents(projectRoot) {
66
+ const detected = [];
67
+ if (fs.existsSync(path.join(projectRoot, '.claude')))
68
+ detected.push('claude');
69
+ if (fs.existsSync(path.join(projectRoot, '.cursor')))
70
+ detected.push('cursor');
71
+ return detected;
72
+ }
64
73
  function writeAgentsToConfig(agents, projectRoot) {
65
74
  const configPath = path.join(projectRoot, 'primer', 'config.yaml');
66
75
  if (!fs.existsSync(configPath))
@@ -1,5 +1,6 @@
1
1
  export type Agent = 'claude' | 'cursor';
2
2
  export declare const SUPPORTED_AGENTS: readonly Agent[];
3
+ export declare function promptAgentSelection(projectRoot: string): Promise<string[]>;
3
4
  export declare function installAgentSkills(agent: Agent, projectRoot: string): void;
4
5
  export declare const CLAUDE_SKILLS: Record<string, string>;
5
6
  export declare const CLAUDE_COMMANDS: Record<string, string>;
@@ -37,12 +37,29 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
37
37
  };
38
38
  Object.defineProperty(exports, "__esModule", { value: true });
39
39
  exports.CURSOR_COMMANDS = exports.CURSOR_SKILLS = exports.CLAUDE_COMMANDS = exports.CLAUDE_SKILLS = exports.SUPPORTED_AGENTS = void 0;
40
+ exports.promptAgentSelection = promptAgentSelection;
40
41
  exports.installAgentSkills = installAgentSkills;
41
42
  const path = __importStar(require("path"));
42
43
  const fs = __importStar(require("fs"));
43
44
  const chalk_1 = __importDefault(require("chalk"));
44
45
  const scaffold_1 = require("./scaffold");
46
+ const detect_1 = require("./detect");
45
47
  exports.SUPPORTED_AGENTS = ['claude', 'cursor'];
48
+ async function promptAgentSelection(projectRoot) {
49
+ const detected = (0, detect_1.detectAvailableAgents)(projectRoot);
50
+ if (detected.length > 0) {
51
+ console.log(chalk_1.default.dim(`Auto-detected AI tool environments: ${detected.join(', ')}`));
52
+ }
53
+ console.log('');
54
+ const { checkbox } = await Promise.resolve().then(() => __importStar(require('@inquirer/prompts')));
55
+ return checkbox({
56
+ message: 'Which AI tools should /oprim:* skills be installed for?',
57
+ choices: [
58
+ { name: 'Claude Code', value: 'claude', checked: detected.includes('claude') },
59
+ { name: 'Cursor', value: 'cursor', checked: detected.includes('cursor') },
60
+ ],
61
+ });
62
+ }
46
63
  function installAgentSkills(agent, projectRoot) {
47
64
  if (agent === 'claude') {
48
65
  const claudeDir = path.join(projectRoot, '.claude');
@@ -397,12 +414,21 @@ Promote a prioritized bet to an OpenSpec change and link criteria contracts.
397
414
  1. **Locate the bet** — read \`primer/bets/BET-XXX/bet-decision.md\`
398
415
  2. **Validate status** — decision must be "Build now"
399
416
  3. **Check authority boundary** — confirm primer artifact owns why/order/outcome only
400
- 4. **Create OpenSpec change** — run \`openspec propose <change-name>\` or create change directory
417
+ 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\`.
418
+ - Pass the bet decision content as context so the proposal reflects the bet's why/outcome
419
+ - **Do not manually create a partial change directory** — the propose skill ensures no artifact is omitted
420
+ - The spec file(s) are mandatory: each capability modified or added must have WHEN/THEN scenarios under \`## ADDED Requirements\` or \`## MODIFIED Requirements\`
401
421
  5. **Link artifacts**:
402
422
  - Add OpenSpec change path to bet-decision \`## Links\` section
403
- - Add bet ID to OpenSpec proposal context
423
+ - Add bet ID to OpenSpec proposal \`## Context\` section
404
424
  6. **Copy criteria** — if \`primer/bets/BET-XXX/criteria.yaml\` exists, link it from OpenSpec proposal
405
- 7. **Report** — show what was linked and what remains for engineering
425
+ 7. **Verify completeness** — confirm the change directory contains:
426
+ - \`proposal.md\`
427
+ - \`design.md\`
428
+ - \`tasks.md\`
429
+ - \`specs/<capability>/spec.md\` for each capability in \`## Capabilities\`
430
+ If any artifact is missing, create it before reporting done.
431
+ 8. **Report** — show what was linked and what remains for engineering
406
432
  `;
407
433
  }
408
434
  function sequenceContent() {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-product-primer/cli",
3
- "version": "0.1.0",
3
+ "version": "0.1.2",
4
4
  "description": "Open Product Primer CLI — product decisions, sequencing, and KPI tracking for repositories",
5
5
  "keywords": [
6
6
  "product",