@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 +2 -2
- package/dist/commands/doctor.js +2 -2
- package/dist/commands/init.js +5 -14
- package/dist/commands/update.js +34 -11
- package/dist/lib/detect.d.ts +1 -0
- package/dist/lib/detect.js +9 -0
- package/dist/lib/install-agent.d.ts +1 -0
- package/dist/lib/install-agent.js +29 -3
- package/package.json +1 -1
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('
|
|
16
|
-
.description('
|
|
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)());
|
package/dist/commands/doctor.js
CHANGED
|
@@ -49,7 +49,7 @@ const AGENT_DIRS = {
|
|
|
49
49
|
};
|
|
50
50
|
function doctorCommand() {
|
|
51
51
|
return new commander_1.Command('doctor')
|
|
52
|
-
.description('Check
|
|
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('
|
|
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);
|
package/dist/commands/init.js
CHANGED
|
@@ -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
|
|
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('
|
|
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
|
-
|
|
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
|
-
|
|
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);
|
package/dist/commands/update.js
CHANGED
|
@@ -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
|
-
|
|
60
|
+
const legacyAgents = [];
|
|
64
61
|
if (fs.existsSync(path.join(projectRoot, '.claude'))) {
|
|
65
62
|
(0, install_agent_1.installAgentSkills)('claude', projectRoot);
|
|
66
|
-
|
|
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
|
-
|
|
67
|
+
legacyAgents.push('cursor');
|
|
71
68
|
}
|
|
72
|
-
if (
|
|
73
|
-
console.log(
|
|
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(
|
|
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
|
}
|
package/dist/lib/detect.d.ts
CHANGED
|
@@ -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;
|
package/dist/lib/detect.js
CHANGED
|
@@ -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** —
|
|
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
|
|
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. **
|
|
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() {
|