@open-product-primer/cli 2.5.1 → 2.7.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/commands/init.js +1 -1
- package/dist/commands/ovw.js +4 -2
- package/dist/lib/detect.js +6 -0
- package/dist/lib/hyperlink.d.ts +7 -0
- package/dist/lib/hyperlink.js +57 -0
- package/dist/lib/install-agent.d.ts +7 -1
- package/dist/lib/install-agent.js +141 -2
- package/dist/lib/workflow-schema.d.ts +9 -0
- package/dist/lib/workflow-schema.js +9 -0
- package/dist/workflows/bet.cursor-command.md +1 -1
- package/dist/workflows/bet.inline.md +1 -1
- package/dist/workflows/bet.template.md +2 -2
- package/dist/workflows/promote.native.template.md +1 -1
- package/dist/workflows/promote.none.template.md +1 -1
- package/dist/workflows/promote.openspec.template.md +1 -1
- package/dist/workflows/sequence.template.md +1 -1
- package/package.json +1 -1
package/dist/commands/init.js
CHANGED
|
@@ -48,7 +48,7 @@ function initCommand() {
|
|
|
48
48
|
return new commander_1.Command('init')
|
|
49
49
|
.description('Initialize oprim in the current repository')
|
|
50
50
|
.option('--name <name>', 'project name (defaults to directory name)')
|
|
51
|
-
.option('--agent <name>', 'AI agent to install skills for (repeatable; supported: claude, cursor, codex, gemini, poolside)', (val, prev) => [...prev, val], [])
|
|
51
|
+
.option('--agent <name>', 'AI agent to install skills for (repeatable; supported: claude, cursor, codex, gemini, poolside, vibe, qwen, kimi)', (val, prev) => [...prev, val], [])
|
|
52
52
|
.action(async (opts) => {
|
|
53
53
|
const projectRoot = process.cwd();
|
|
54
54
|
const projectName = opts.name ?? path.basename(projectRoot);
|
package/dist/commands/ovw.js
CHANGED
|
@@ -42,6 +42,7 @@ const path = __importStar(require("path"));
|
|
|
42
42
|
const fs = __importStar(require("fs"));
|
|
43
43
|
const chalk_1 = __importDefault(require("chalk"));
|
|
44
44
|
const yaml = __importStar(require("js-yaml"));
|
|
45
|
+
const hyperlink_1 = require("../lib/hyperlink");
|
|
45
46
|
function resolveBetDecisionPath(betsDir, betId) {
|
|
46
47
|
if (!fs.existsSync(betsDir))
|
|
47
48
|
return null;
|
|
@@ -128,15 +129,16 @@ function renderLane(label, bets, betsDir, showMeta) {
|
|
|
128
129
|
return;
|
|
129
130
|
}
|
|
130
131
|
for (const bet of bets) {
|
|
132
|
+
const entry = (0, hyperlink_1.hyperlinkBetEntry)(betsDir, bet.id, `${bet.id} ${bet.title}`);
|
|
131
133
|
if (showMeta) {
|
|
132
134
|
const meta = loadBetMeta(betsDir, bet.id);
|
|
133
135
|
const inline = renderInlineMeta(meta);
|
|
134
136
|
const blockers = renderBlockers(bet);
|
|
135
|
-
console.log(` ${
|
|
137
|
+
console.log(` ${entry} ${inline}${blockers}`);
|
|
136
138
|
}
|
|
137
139
|
else {
|
|
138
140
|
const blockers = renderBlockers(bet);
|
|
139
|
-
console.log(` ${
|
|
141
|
+
console.log(` ${entry}${blockers}`);
|
|
140
142
|
}
|
|
141
143
|
}
|
|
142
144
|
}
|
package/dist/lib/detect.js
CHANGED
|
@@ -84,6 +84,12 @@ function detectAvailableAgents(projectRoot) {
|
|
|
84
84
|
detected.push('gemini');
|
|
85
85
|
if (fs.existsSync(path.join(projectRoot, '.poolside')))
|
|
86
86
|
detected.push('poolside');
|
|
87
|
+
if (fs.existsSync(path.join(projectRoot, '.vibe')))
|
|
88
|
+
detected.push('vibe');
|
|
89
|
+
if (fs.existsSync(path.join(projectRoot, '.qwen')))
|
|
90
|
+
detected.push('qwen');
|
|
91
|
+
if (fs.existsSync(path.join(projectRoot, '.kimi')))
|
|
92
|
+
detected.push('kimi');
|
|
87
93
|
return detected;
|
|
88
94
|
}
|
|
89
95
|
function writeAgentsToConfig(agents, projectRoot) {
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
/** Wraps `text` in an OSC 8 terminal hyperlink escape sequence targeting `uri`. */
|
|
2
|
+
export declare function osc8(text: string, uri: string): string;
|
|
3
|
+
/**
|
|
4
|
+
* Resolves `betIdInput` to its `bet-decision.md` path under `betsDir` and returns an OSC 8
|
|
5
|
+
* hyperlink wrapping `text`. Falls back to plain `text` if the bet can't be resolved.
|
|
6
|
+
*/
|
|
7
|
+
export declare function hyperlinkBetEntry(betsDir: string, betIdInput: string, text: string): string;
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
18
|
+
var __importStar = (this && this.__importStar) || (function () {
|
|
19
|
+
var ownKeys = function(o) {
|
|
20
|
+
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
21
|
+
var ar = [];
|
|
22
|
+
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
23
|
+
return ar;
|
|
24
|
+
};
|
|
25
|
+
return ownKeys(o);
|
|
26
|
+
};
|
|
27
|
+
return function (mod) {
|
|
28
|
+
if (mod && mod.__esModule) return mod;
|
|
29
|
+
var result = {};
|
|
30
|
+
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
31
|
+
__setModuleDefault(result, mod);
|
|
32
|
+
return result;
|
|
33
|
+
};
|
|
34
|
+
})();
|
|
35
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
36
|
+
exports.osc8 = osc8;
|
|
37
|
+
exports.hyperlinkBetEntry = hyperlinkBetEntry;
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const spec_delta_1 = require("./spec-delta");
|
|
40
|
+
/** Wraps `text` in an OSC 8 terminal hyperlink escape sequence targeting `uri`. */
|
|
41
|
+
function osc8(text, uri) {
|
|
42
|
+
return `\x1b]8;;${uri}\x1b\\${text}\x1b]8;;\x1b\\`;
|
|
43
|
+
}
|
|
44
|
+
function fileUri(absPath) {
|
|
45
|
+
return 'file://' + encodeURI(absPath.split(path.sep).join('/'));
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Resolves `betIdInput` to its `bet-decision.md` path under `betsDir` and returns an OSC 8
|
|
49
|
+
* hyperlink wrapping `text`. Falls back to plain `text` if the bet can't be resolved.
|
|
50
|
+
*/
|
|
51
|
+
function hyperlinkBetEntry(betsDir, betIdInput, text) {
|
|
52
|
+
const dirName = (0, spec_delta_1.resolveBetDirectory)(betsDir, betIdInput);
|
|
53
|
+
if (!dirName)
|
|
54
|
+
return text;
|
|
55
|
+
const decisionPath = path.join(betsDir, dirName, 'bet-decision.md');
|
|
56
|
+
return osc8(text, fileUri(decisionPath));
|
|
57
|
+
}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type Agent = 'claude' | 'cursor' | 'codex' | 'gemini' | 'poolside';
|
|
1
|
+
export type Agent = 'claude' | 'cursor' | 'codex' | 'gemini' | 'poolside' | 'vibe' | 'qwen' | 'kimi';
|
|
2
2
|
export declare const SUPPORTED_AGENTS: readonly Agent[];
|
|
3
3
|
export declare function promptFrameworkSelection(projectRoot: string): Promise<string>;
|
|
4
4
|
export declare function resolveSpecFramework(projectRoot: string): string;
|
|
@@ -10,6 +10,9 @@ export declare const OPRIM_CONTEXT_SKILL_STEP = "## Step 0: Check relevant produ
|
|
|
10
10
|
export declare const CLAUDE_SKILLS: Record<string, string>;
|
|
11
11
|
export declare const CLAUDE_COMMANDS: Record<string, string>;
|
|
12
12
|
export declare const POOLSIDE_SKILLS: Record<string, string>;
|
|
13
|
+
export declare const VIBE_SKILLS: Record<string, string>;
|
|
14
|
+
export declare const QWEN_SKILLS: Record<string, string>;
|
|
15
|
+
export declare const KIMI_SKILLS: Record<string, string>;
|
|
13
16
|
export declare const CURSOR_SKILLS: Record<string, string>;
|
|
14
17
|
export declare const CURSOR_COMMANDS: Record<string, string>;
|
|
15
18
|
export declare function specAuthoringSkill(): string;
|
|
@@ -17,3 +20,6 @@ export declare function writeAgentInstructionFile(filePath: string, section: str
|
|
|
17
20
|
export declare function codexInstructions(): string;
|
|
18
21
|
export declare function geminiInstructions(): string;
|
|
19
22
|
export declare function poolsideInstructions(): string;
|
|
23
|
+
export declare function vibeInstructions(): string;
|
|
24
|
+
export declare function qwenInstructions(): string;
|
|
25
|
+
export declare function kimiInstructions(): string;
|
|
@@ -36,7 +36,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
36
36
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
37
|
};
|
|
38
38
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
-
exports.CURSOR_COMMANDS = exports.CURSOR_SKILLS = exports.POOLSIDE_SKILLS = exports.CLAUDE_COMMANDS = exports.CLAUDE_SKILLS = exports.OPRIM_CONTEXT_SKILL_STEP = exports.SUPPORTED_AGENTS = void 0;
|
|
39
|
+
exports.CURSOR_COMMANDS = exports.CURSOR_SKILLS = exports.KIMI_SKILLS = exports.QWEN_SKILLS = exports.VIBE_SKILLS = exports.POOLSIDE_SKILLS = exports.CLAUDE_COMMANDS = exports.CLAUDE_SKILLS = exports.OPRIM_CONTEXT_SKILL_STEP = exports.SUPPORTED_AGENTS = void 0;
|
|
40
40
|
exports.promptFrameworkSelection = promptFrameworkSelection;
|
|
41
41
|
exports.resolveSpecFramework = resolveSpecFramework;
|
|
42
42
|
exports.promptAgentSelection = promptAgentSelection;
|
|
@@ -48,6 +48,9 @@ exports.writeAgentInstructionFile = writeAgentInstructionFile;
|
|
|
48
48
|
exports.codexInstructions = codexInstructions;
|
|
49
49
|
exports.geminiInstructions = geminiInstructions;
|
|
50
50
|
exports.poolsideInstructions = poolsideInstructions;
|
|
51
|
+
exports.vibeInstructions = vibeInstructions;
|
|
52
|
+
exports.qwenInstructions = qwenInstructions;
|
|
53
|
+
exports.kimiInstructions = kimiInstructions;
|
|
51
54
|
const path = __importStar(require("path"));
|
|
52
55
|
const fs = __importStar(require("fs"));
|
|
53
56
|
const chalk_1 = __importDefault(require("chalk"));
|
|
@@ -56,7 +59,16 @@ const detect_1 = require("./detect");
|
|
|
56
59
|
const config_merge_1 = require("./config-merge");
|
|
57
60
|
const workflow_schema_1 = require("./workflow-schema");
|
|
58
61
|
const workflow_renderer_1 = require("./workflow-renderer");
|
|
59
|
-
exports.SUPPORTED_AGENTS = [
|
|
62
|
+
exports.SUPPORTED_AGENTS = [
|
|
63
|
+
'claude',
|
|
64
|
+
'cursor',
|
|
65
|
+
'codex',
|
|
66
|
+
'gemini',
|
|
67
|
+
'poolside',
|
|
68
|
+
'vibe',
|
|
69
|
+
'qwen',
|
|
70
|
+
'kimi',
|
|
71
|
+
];
|
|
60
72
|
// oprim/config.yaml (via integrations.spec_framework) is the source of truth for the
|
|
61
73
|
// selected speccing framework; .claude/hooks/config.json is checked only as a fallback for
|
|
62
74
|
// projects that installed before that key existed.
|
|
@@ -124,6 +136,9 @@ async function promptAgentSelection(projectRoot) {
|
|
|
124
136
|
{ name: 'Codex', value: 'codex', checked: detected.includes('codex') },
|
|
125
137
|
{ name: 'Gemini CLI', value: 'gemini', checked: detected.includes('gemini') },
|
|
126
138
|
{ name: 'Poolside', value: 'poolside', checked: detected.includes('poolside') },
|
|
139
|
+
{ name: 'Mistral Vibe', value: 'vibe', checked: detected.includes('vibe') },
|
|
140
|
+
{ name: 'Qwen Code', value: 'qwen', checked: detected.includes('qwen') },
|
|
141
|
+
{ name: 'Kimi CLI', value: 'kimi', checked: detected.includes('kimi') },
|
|
127
142
|
],
|
|
128
143
|
});
|
|
129
144
|
}
|
|
@@ -281,6 +296,109 @@ function installAgentSkills(agent, projectRoot, framework = 'openspec', pdrSurfa
|
|
|
281
296
|
console.log(chalk_1.default.dim(' .poolside/ created — Poolside will discover these files automatically.'));
|
|
282
297
|
}
|
|
283
298
|
}
|
|
299
|
+
else if (agent === 'vibe') {
|
|
300
|
+
const vibeDir = path.join(projectRoot, '.vibe');
|
|
301
|
+
const dirCreated = !fs.existsSync(vibeDir);
|
|
302
|
+
const skillsBase = path.join(vibeDir, 'skills');
|
|
303
|
+
for (const id of POOLSIDE_SKILL_WORKFLOW_IDS) {
|
|
304
|
+
const schema = (0, workflow_schema_1.loadWorkflowSchema)(id, projectRoot);
|
|
305
|
+
if (!schema.vibe.skill || !schema.skillName)
|
|
306
|
+
continue;
|
|
307
|
+
(0, scaffold_1.writeFile)(path.join(skillsBase, schema.skillName, 'SKILL.md'), (0, workflow_renderer_1.renderSkillBody)(id, projectRoot));
|
|
308
|
+
console.log(chalk_1.default.green('✓') + ` .vibe/skills/${schema.skillName}/SKILL.md`);
|
|
309
|
+
}
|
|
310
|
+
const vibeSpecSkillPath = path.join(skillsBase, 'oprim-spec', 'SKILL.md');
|
|
311
|
+
if (framework === 'native') {
|
|
312
|
+
(0, scaffold_1.writeFile)(vibeSpecSkillPath, (0, workflow_renderer_1.renderSkillBody)('spec-authoring', projectRoot));
|
|
313
|
+
console.log(chalk_1.default.green('✓') + ' .vibe/skills/oprim-spec/SKILL.md');
|
|
314
|
+
}
|
|
315
|
+
else if (fs.existsSync(vibeSpecSkillPath)) {
|
|
316
|
+
fs.unlinkSync(vibeSpecSkillPath);
|
|
317
|
+
try {
|
|
318
|
+
fs.rmdirSync(path.dirname(vibeSpecSkillPath));
|
|
319
|
+
}
|
|
320
|
+
catch { /* not empty or already gone */ }
|
|
321
|
+
console.log(chalk_1.default.dim(' removed .vibe/skills/oprim-spec/SKILL.md'));
|
|
322
|
+
}
|
|
323
|
+
const agentsFile = path.join(projectRoot, 'AGENTS.md');
|
|
324
|
+
writeAgentInstructionFile(agentsFile, vibeInstructions());
|
|
325
|
+
console.log(chalk_1.default.green('✓') + ' AGENTS.md (oprim section written)');
|
|
326
|
+
if (dirCreated) {
|
|
327
|
+
console.log(chalk_1.default.dim(' .vibe/ created — Mistral Vibe will discover these files automatically.'));
|
|
328
|
+
}
|
|
329
|
+
}
|
|
330
|
+
else if (agent === 'qwen') {
|
|
331
|
+
const qwenDir = path.join(projectRoot, '.qwen');
|
|
332
|
+
const dirCreated = !fs.existsSync(qwenDir);
|
|
333
|
+
const skillsBase = path.join(qwenDir, 'skills');
|
|
334
|
+
for (const id of POOLSIDE_SKILL_WORKFLOW_IDS) {
|
|
335
|
+
const schema = (0, workflow_schema_1.loadWorkflowSchema)(id, projectRoot);
|
|
336
|
+
if (!schema.qwen.skill || !schema.skillName)
|
|
337
|
+
continue;
|
|
338
|
+
(0, scaffold_1.writeFile)(path.join(skillsBase, schema.skillName, 'SKILL.md'), (0, workflow_renderer_1.renderSkillBody)(id, projectRoot));
|
|
339
|
+
console.log(chalk_1.default.green('✓') + ` .qwen/skills/${schema.skillName}/SKILL.md`);
|
|
340
|
+
}
|
|
341
|
+
const qwenSpecSkillPath = path.join(skillsBase, 'oprim-spec', 'SKILL.md');
|
|
342
|
+
if (framework === 'native') {
|
|
343
|
+
(0, scaffold_1.writeFile)(qwenSpecSkillPath, (0, workflow_renderer_1.renderSkillBody)('spec-authoring', projectRoot));
|
|
344
|
+
console.log(chalk_1.default.green('✓') + ' .qwen/skills/oprim-spec/SKILL.md');
|
|
345
|
+
}
|
|
346
|
+
else if (fs.existsSync(qwenSpecSkillPath)) {
|
|
347
|
+
fs.unlinkSync(qwenSpecSkillPath);
|
|
348
|
+
try {
|
|
349
|
+
fs.rmdirSync(path.dirname(qwenSpecSkillPath));
|
|
350
|
+
}
|
|
351
|
+
catch { /* not empty or already gone */ }
|
|
352
|
+
console.log(chalk_1.default.dim(' removed .qwen/skills/oprim-spec/SKILL.md'));
|
|
353
|
+
}
|
|
354
|
+
const agentsFile = path.join(projectRoot, 'AGENTS.md');
|
|
355
|
+
writeAgentInstructionFile(agentsFile, qwenInstructions());
|
|
356
|
+
console.log(chalk_1.default.green('✓') + ' AGENTS.md (oprim section written)');
|
|
357
|
+
if (dirCreated) {
|
|
358
|
+
console.log(chalk_1.default.dim(' .qwen/ created — Qwen Code will discover these files automatically.'));
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
else if (agent === 'kimi') {
|
|
362
|
+
// Kimi CLI is a split-path install: .kimi/ is the detection signal, but skills are
|
|
363
|
+
// discovered from a project-root .skills/ directory, not .kimi/skills/ — see
|
|
364
|
+
// kimi-cli-agent-support spec.
|
|
365
|
+
const kimiDir = path.join(projectRoot, '.kimi');
|
|
366
|
+
const kimiDirCreated = !fs.existsSync(kimiDir);
|
|
367
|
+
if (kimiDirCreated) {
|
|
368
|
+
fs.mkdirSync(kimiDir, { recursive: true });
|
|
369
|
+
}
|
|
370
|
+
const skillsBase = path.join(projectRoot, '.skills');
|
|
371
|
+
const skillsDirCreated = !fs.existsSync(skillsBase);
|
|
372
|
+
for (const id of POOLSIDE_SKILL_WORKFLOW_IDS) {
|
|
373
|
+
const schema = (0, workflow_schema_1.loadWorkflowSchema)(id, projectRoot);
|
|
374
|
+
if (!schema.kimi.skill || !schema.skillName)
|
|
375
|
+
continue;
|
|
376
|
+
(0, scaffold_1.writeFile)(path.join(skillsBase, schema.skillName, 'SKILL.md'), (0, workflow_renderer_1.renderSkillBody)(id, projectRoot));
|
|
377
|
+
console.log(chalk_1.default.green('✓') + ` .skills/${schema.skillName}/SKILL.md`);
|
|
378
|
+
}
|
|
379
|
+
const kimiSpecSkillPath = path.join(skillsBase, 'oprim-spec', 'SKILL.md');
|
|
380
|
+
if (framework === 'native') {
|
|
381
|
+
(0, scaffold_1.writeFile)(kimiSpecSkillPath, (0, workflow_renderer_1.renderSkillBody)('spec-authoring', projectRoot));
|
|
382
|
+
console.log(chalk_1.default.green('✓') + ' .skills/oprim-spec/SKILL.md');
|
|
383
|
+
}
|
|
384
|
+
else if (fs.existsSync(kimiSpecSkillPath)) {
|
|
385
|
+
fs.unlinkSync(kimiSpecSkillPath);
|
|
386
|
+
try {
|
|
387
|
+
fs.rmdirSync(path.dirname(kimiSpecSkillPath));
|
|
388
|
+
}
|
|
389
|
+
catch { /* not empty or already gone */ }
|
|
390
|
+
console.log(chalk_1.default.dim(' removed .skills/oprim-spec/SKILL.md'));
|
|
391
|
+
}
|
|
392
|
+
const agentsFile = path.join(projectRoot, 'AGENTS.md');
|
|
393
|
+
writeAgentInstructionFile(agentsFile, kimiInstructions());
|
|
394
|
+
console.log(chalk_1.default.green('✓') + ' AGENTS.md (oprim section written)');
|
|
395
|
+
if (kimiDirCreated) {
|
|
396
|
+
console.log(chalk_1.default.dim(' .kimi/ created — Kimi CLI will discover this directory automatically.'));
|
|
397
|
+
}
|
|
398
|
+
if (skillsDirCreated) {
|
|
399
|
+
console.log(chalk_1.default.dim(' .skills/ created — Kimi CLI will discover these files automatically.'));
|
|
400
|
+
}
|
|
401
|
+
}
|
|
284
402
|
else if (agent === 'codex') {
|
|
285
403
|
const agentsFile = path.join(projectRoot, 'AGENTS.md');
|
|
286
404
|
writeAgentInstructionFile(agentsFile, codexInstructions());
|
|
@@ -441,6 +559,18 @@ exports.POOLSIDE_SKILLS = Object.fromEntries(POOLSIDE_SKILL_WORKFLOW_IDS.map((id
|
|
|
441
559
|
const schema = (0, workflow_schema_1.loadWorkflowSchema)(id);
|
|
442
560
|
return [schema.skillName, (0, workflow_renderer_1.renderSkillBody)(id)];
|
|
443
561
|
}));
|
|
562
|
+
exports.VIBE_SKILLS = Object.fromEntries(POOLSIDE_SKILL_WORKFLOW_IDS.map((id) => {
|
|
563
|
+
const schema = (0, workflow_schema_1.loadWorkflowSchema)(id);
|
|
564
|
+
return [schema.skillName, (0, workflow_renderer_1.renderSkillBody)(id)];
|
|
565
|
+
}));
|
|
566
|
+
exports.QWEN_SKILLS = Object.fromEntries(POOLSIDE_SKILL_WORKFLOW_IDS.map((id) => {
|
|
567
|
+
const schema = (0, workflow_schema_1.loadWorkflowSchema)(id);
|
|
568
|
+
return [schema.skillName, (0, workflow_renderer_1.renderSkillBody)(id)];
|
|
569
|
+
}));
|
|
570
|
+
exports.KIMI_SKILLS = Object.fromEntries(POOLSIDE_SKILL_WORKFLOW_IDS.map((id) => {
|
|
571
|
+
const schema = (0, workflow_schema_1.loadWorkflowSchema)(id);
|
|
572
|
+
return [schema.skillName, (0, workflow_renderer_1.renderSkillBody)(id)];
|
|
573
|
+
}));
|
|
444
574
|
exports.CURSOR_SKILLS = Object.fromEntries(CURSOR_SKILL_WORKFLOW_IDS.map((id) => {
|
|
445
575
|
const schema = (0, workflow_schema_1.loadWorkflowSchema)(id);
|
|
446
576
|
return [schema.skillName, (0, workflow_renderer_1.renderSkillBody)(id)];
|
|
@@ -632,3 +762,12 @@ function geminiInstructions() {
|
|
|
632
762
|
function poolsideInstructions() {
|
|
633
763
|
return (0, workflow_renderer_1.renderAgentInstructions)();
|
|
634
764
|
}
|
|
765
|
+
function vibeInstructions() {
|
|
766
|
+
return (0, workflow_renderer_1.renderAgentInstructions)();
|
|
767
|
+
}
|
|
768
|
+
function qwenInstructions() {
|
|
769
|
+
return (0, workflow_renderer_1.renderAgentInstructions)();
|
|
770
|
+
}
|
|
771
|
+
function kimiInstructions() {
|
|
772
|
+
return (0, workflow_renderer_1.renderAgentInstructions)();
|
|
773
|
+
}
|
|
@@ -68,6 +68,12 @@ function parseSchema(raw, filePath) {
|
|
|
68
68
|
const claude = requireField(obj, 'claude', filePath);
|
|
69
69
|
const cursor = requireField(obj, 'cursor', filePath);
|
|
70
70
|
const poolside = requireField(obj, 'poolside', filePath);
|
|
71
|
+
// vibe/qwen/kimi are optional and mirror poolside by default — see mistral-vibe-agent-support,
|
|
72
|
+
// qwen-code-agent-support, and kimi-cli-agent-support specs. This keeps existing bundled/forked
|
|
73
|
+
// schemas (none declare a `vibe:`/`qwen:`/`kimi:` key) working unchanged.
|
|
74
|
+
const vibe = obj.vibe ?? poolside;
|
|
75
|
+
const qwen = obj.qwen ?? poolside;
|
|
76
|
+
const kimi = obj.kimi ?? poolside;
|
|
71
77
|
requireField(obj, 'inline', filePath);
|
|
72
78
|
return {
|
|
73
79
|
id: obj.id,
|
|
@@ -78,6 +84,9 @@ function parseSchema(raw, filePath) {
|
|
|
78
84
|
claude: { skill: Boolean(claude.skill), command: claude.command ?? null },
|
|
79
85
|
cursor: { skill: Boolean(cursor.skill), command: cursor.command ?? null },
|
|
80
86
|
poolside: { skill: Boolean(poolside.skill) },
|
|
87
|
+
vibe: { skill: Boolean(vibe.skill) },
|
|
88
|
+
qwen: { skill: Boolean(qwen.skill) },
|
|
89
|
+
kimi: { skill: Boolean(kimi.skill) },
|
|
81
90
|
inline: Boolean(obj.inline),
|
|
82
91
|
variants: obj.variants ?? null,
|
|
83
92
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
Create a new bet in `oprim/bets/pending/`. First explain: "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." Then show: "Naming tip: verb + object [for context] — Good: 'Improve bet naming for scannability' / Bad: 'Naming'". Scan `oprim/bets/pending/BET-(\d+)` and `oprim/bets/archived/BET-(\d+)` dirs for next ID (zero-padded, default 001). Check `oprim/sequence.yaml` exists (stop if not — advise oprim init). Read `oprim/config.yaml`'s `rules.bet` — if non-empty, apply it as additional guidance and reflect it in the generated content; if empty, behavior is unchanged. After receiving the title, validate: if fewer than 4 words OR fewer than 25 characters, warn "this title may be too vague", suggest a reformulation, and ask "Proceed anyway? (y/N)" — if "n", prompt for a revised title. Gather: decision (default Build now), owner, review date, why-now, alternatives, expected outcomes, kill criteria, PDR links. Write `oprim/bets/pending/BET-NNN/bet-decision.md` with an inline naming tip comment in the header. Append entry to sequence.yaml backlog: `{
|
|
1
|
+
Create a new bet in `oprim/bets/pending/`. First explain: "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." Then show: "Naming tip: verb + object [for context] — Good: 'Improve bet naming for scannability' / Bad: 'Naming'". Scan `oprim/bets/pending/BET-(\d+)` and `oprim/bets/archived/BET-(\d+)` dirs for next ID (zero-padded, default 001). Check `oprim/sequence.yaml` exists (stop if not — advise oprim init). Read `oprim/config.yaml`'s `rules.bet` — if non-empty, apply it as additional guidance and reflect it in the generated content; if empty, behavior is unchanged. After receiving the title, validate: if fewer than 4 words OR fewer than 25 characters, warn "this title may be too vague", suggest a reformulation, and ask "Proceed anyway? (y/N)" — if "n", prompt for a revised title. Gather: decision (default Build now), owner, review date, why-now, alternatives, expected outcomes, kill criteria, PDR links. Write `oprim/bets/pending/BET-NNN/bet-decision.md` with an inline naming tip comment in the header. Append entry to sequence.yaml backlog: `{title, id, blocked_by: [], unlocks: [], requires_pdrs: []}`. Then ask: "Do you want to scaffold a discovery.md now? (y/N)" — if "y", write `oprim/bets/pending/BET-NNN/discovery.md` from the discovery template (sections: Problem Framing, User Research Signals, Competitive Context, Open Questions); if "n" or Enter, skip silently. Report what was created.
|
|
@@ -8,6 +8,6 @@ Create a new bet in `oprim/bets/pending/` and register it on the sequencing boar
|
|
|
8
8
|
4b. Read `oprim/config.yaml`'s `rules.bet` — if non-empty, apply it as additional guidance and reflect it in the generated content; if empty, behavior is unchanged.
|
|
9
9
|
5. Gather: decision (default Build now), owner, review date (YYYY-MM-DD), why now, alternatives, expected outcomes, kill criteria, PDR links.
|
|
10
10
|
6. Write `oprim/bets/pending/BET-NNN/bet-decision.md` with all fields.
|
|
11
|
-
7. Append to `oprim/sequence.yaml` backlog: `{
|
|
11
|
+
7. Append to `oprim/sequence.yaml` backlog: `{title, id, blocked_by: [], unlocks: [], requires_pdrs: []}`.
|
|
12
12
|
8. Ask: "Scaffold a discovery.md now? (y/N)" — if "y", write `oprim/bets/pending/BET-NNN/discovery.md`.
|
|
13
13
|
9. Report what was created.
|
|
@@ -99,8 +99,8 @@ Prepend the frontmatter block from step 4b, if one was prepared.
|
|
|
99
99
|
### 6. Append to oprim/sequence.yaml backlog
|
|
100
100
|
Read → parse YAML → append → write back (2-space indentation):
|
|
101
101
|
```yaml
|
|
102
|
-
-
|
|
103
|
-
|
|
102
|
+
- title: "<title>"
|
|
103
|
+
id: BET-NNN
|
|
104
104
|
blocked_by: []
|
|
105
105
|
unlocks: []
|
|
106
106
|
requires_pdrs: []
|
|
@@ -24,6 +24,6 @@ Promote an atomic note into a bet, or a prioritized bet into a capability spec.
|
|
|
24
24
|
2. **Assign the next BET ID** — scan both `oprim/bets/pending/` and `oprim/bets/archived/` for directories matching `BET-(\d+)(-[^/]*)?`, max+1 zero-padded to 3 digits (default 001) — same convention `oprim-bet` uses
|
|
25
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
26
|
4. **Draft the bet** — write `oprim/bets/pending/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: `{
|
|
27
|
+
5. **Register the new bet** — append to `oprim/sequence.yaml` backlog: `{title, id, blocked_by: [], unlocks: [], requires_pdrs: []}`
|
|
28
28
|
6. **Link back** — add `Bets: BET-NNN` to the note (creating or extending its `## Bets` section)
|
|
29
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
|
|
@@ -20,6 +20,6 @@ Promote an atomic note into a bet, or a prioritized bet into a capability spec.
|
|
|
20
20
|
2. **Assign the next BET ID** — scan both `oprim/bets/pending/` and `oprim/bets/archived/` for directories matching `BET-(\d+)(-[^/]*)?`, max+1 zero-padded to 3 digits (default 001) — same convention `oprim-bet` uses
|
|
21
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
22
|
4. **Draft the bet** — write `oprim/bets/pending/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: `{
|
|
23
|
+
5. **Register the new bet** — append to `oprim/sequence.yaml` backlog: `{title, id, blocked_by: [], unlocks: [], requires_pdrs: []}`
|
|
24
24
|
6. **Link back** — add `Bets: BET-NNN` to the note (creating or extending its `## Bets` section)
|
|
25
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
|
|
@@ -35,6 +35,6 @@ Promote an atomic note into a bet, or a prioritized bet into a capability spec.
|
|
|
35
35
|
2. **Assign the next BET ID** — scan both `oprim/bets/pending/` and `oprim/bets/archived/` for directories matching `BET-(\d+)(-[^/]*)?`, max+1 zero-padded to 3 digits (default 001) — same convention `oprim-bet` uses
|
|
36
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
37
|
4. **Draft the bet** — write `oprim/bets/pending/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: `{
|
|
38
|
+
5. **Register the new bet** — append to `oprim/sequence.yaml` backlog: `{title, id, blocked_by: [], unlocks: [], requires_pdrs: []}`
|
|
39
39
|
6. **Link back** — add `Bets: BET-NNN` to the note (creating or extending its `## Bets` section)
|
|
40
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
|
|
@@ -79,7 +79,7 @@ Ask: "Apply this change? (y/N)"
|
|
|
79
79
|
|
|
80
80
|
### 5. Write sequence.yaml
|
|
81
81
|
|
|
82
|
-
Read `oprim/sequence.yaml`. Remove the bet entry from its current lane. Insert
|
|
82
|
+
Read `oprim/sequence.yaml`. Remove the bet entry from its current lane. If the entry's fields are ordered `id:` before `title:`, reorder them to `title:` before `id:` (leave `blocked_by`, `unlocks`, `requires_pdrs` order unchanged). Insert the entry into the target lane. Write back with 2-space indentation. Do not modify any other entries.
|
|
83
83
|
|
|
84
84
|
### 6. Regenerate view
|
|
85
85
|
|