@open-product-primer/cli 2.8.0 → 2.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/cli.js +6 -0
- package/dist/commands/init.js +1 -1
- package/dist/commands/list.d.ts +2 -0
- package/dist/commands/list.js +54 -0
- package/dist/commands/show.d.ts +2 -0
- package/dist/commands/show.js +138 -0
- package/dist/commands/status.d.ts +2 -0
- package/dist/commands/status.js +95 -0
- package/dist/lib/artifacts.d.ts +44 -0
- package/dist/lib/artifacts.js +163 -0
- package/dist/lib/detect.js +2 -0
- package/dist/lib/install-agent.d.ts +3 -1
- package/dist/lib/install-agent.js +42 -1
- package/dist/lib/workflow-schema.d.ts +3 -0
- package/dist/lib/workflow-schema.js +6 -3
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -13,6 +13,9 @@ const migrate_1 = require("./commands/migrate");
|
|
|
13
13
|
const measure_1 = require("./commands/measure");
|
|
14
14
|
const ovw_1 = require("./commands/ovw");
|
|
15
15
|
const context_1 = require("./commands/context");
|
|
16
|
+
const list_1 = require("./commands/list");
|
|
17
|
+
const show_1 = require("./commands/show");
|
|
18
|
+
const status_1 = require("./commands/status");
|
|
16
19
|
const package_json_1 = __importDefault(require("../package.json"));
|
|
17
20
|
const program = new commander_1.Command();
|
|
18
21
|
program
|
|
@@ -27,4 +30,7 @@ program.addCommand((0, migrate_1.migrateCommand)());
|
|
|
27
30
|
program.addCommand((0, measure_1.measureCommand)());
|
|
28
31
|
program.addCommand((0, ovw_1.ovwCommand)());
|
|
29
32
|
program.addCommand((0, context_1.contextCommand)());
|
|
33
|
+
program.addCommand((0, list_1.listCommand)());
|
|
34
|
+
program.addCommand((0, show_1.showCommand)());
|
|
35
|
+
program.addCommand((0, status_1.statusCommand)());
|
|
30
36
|
program.parse();
|
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, vibe, qwen, kimi)', (val, prev) => [...prev, val], [])
|
|
51
|
+
.option('--agent <name>', 'AI agent to install skills for (repeatable; supported: claude, cursor, codex, gemini, poolside, vibe, qwen, kimi, dsh)', (val, prev) => [...prev, val], [])
|
|
52
52
|
.action(async (opts) => {
|
|
53
53
|
const projectRoot = process.cwd();
|
|
54
54
|
const projectName = opts.name ?? path.basename(projectRoot);
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.listCommand = listCommand;
|
|
7
|
+
const commander_1 = require("commander");
|
|
8
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
9
|
+
const artifacts_1 = require("../lib/artifacts");
|
|
10
|
+
function printTable(label, rows, columns) {
|
|
11
|
+
console.log(chalk_1.default.bold.cyan(`\n── ${label} ──`));
|
|
12
|
+
if (rows.length === 0) {
|
|
13
|
+
console.log(' (none)');
|
|
14
|
+
return;
|
|
15
|
+
}
|
|
16
|
+
for (const row of rows) {
|
|
17
|
+
console.log(` ${columns(row).join(' ')}`);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
function listCommand() {
|
|
21
|
+
return new commander_1.Command('list')
|
|
22
|
+
.description('Enumerate oprim artifacts (bets, decisions, notes) by type')
|
|
23
|
+
.option('-b, --bets', 'list bets')
|
|
24
|
+
.option('-d, --decisions', 'list decisions (PDRs)')
|
|
25
|
+
.option('-n, --notes', 'list notes')
|
|
26
|
+
.option('--json', 'print machine-readable JSON instead of the human-readable table')
|
|
27
|
+
.action((opts) => {
|
|
28
|
+
const projectRoot = process.cwd();
|
|
29
|
+
const anyTypeRequested = !!(opts.bets || opts.decisions || opts.notes);
|
|
30
|
+
const showBets = anyTypeRequested ? !!opts.bets : true;
|
|
31
|
+
const showDecisions = !!opts.decisions;
|
|
32
|
+
const showNotes = !!opts.notes;
|
|
33
|
+
const bets = showBets ? (0, artifacts_1.listBets)(projectRoot) : undefined;
|
|
34
|
+
const decisions = showDecisions ? (0, artifacts_1.listDecisions)(projectRoot) : undefined;
|
|
35
|
+
const notes = showNotes ? (0, artifacts_1.listNotes)(projectRoot) : undefined;
|
|
36
|
+
if (opts.json) {
|
|
37
|
+
const result = {};
|
|
38
|
+
if (bets)
|
|
39
|
+
result.bets = bets;
|
|
40
|
+
if (decisions)
|
|
41
|
+
result.decisions = decisions;
|
|
42
|
+
if (notes)
|
|
43
|
+
result.notes = notes;
|
|
44
|
+
console.log(JSON.stringify(result));
|
|
45
|
+
return;
|
|
46
|
+
}
|
|
47
|
+
if (bets)
|
|
48
|
+
printTable('BETS', bets, (b) => [b.id, b.status ?? '-', b.title]);
|
|
49
|
+
if (decisions)
|
|
50
|
+
printTable('DECISIONS', decisions, (d) => [d.id, d.status ?? '-', d.title]);
|
|
51
|
+
if (notes)
|
|
52
|
+
printTable('NOTES', notes, (n) => [n.id, n.tags.join(',') || '-', n.title]);
|
|
53
|
+
});
|
|
54
|
+
}
|
|
@@ -0,0 +1,138 @@
|
|
|
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
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.showCommand = showCommand;
|
|
40
|
+
const commander_1 = require("commander");
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const fs = __importStar(require("fs"));
|
|
43
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
44
|
+
const spec_delta_1 = require("../lib/spec-delta");
|
|
45
|
+
const artifacts_1 = require("../lib/artifacts");
|
|
46
|
+
function extractLinks(content) {
|
|
47
|
+
const match = content.match(/^##\s*Links\s*\n([\s\S]*?)(?=\n##\s|$)/m);
|
|
48
|
+
return match ? match[1].trim() : null;
|
|
49
|
+
}
|
|
50
|
+
function resolveBet(projectRoot, idInput) {
|
|
51
|
+
for (const sub of ['pending', 'archived']) {
|
|
52
|
+
const betsDir = path.join(projectRoot, 'oprim', 'bets', sub);
|
|
53
|
+
const resolved = (0, spec_delta_1.resolveBetDirectory)(betsDir, idInput);
|
|
54
|
+
if (!resolved)
|
|
55
|
+
continue;
|
|
56
|
+
const decisionPath = path.join(betsDir, resolved, 'bet-decision.md');
|
|
57
|
+
if (!fs.existsSync(decisionPath))
|
|
58
|
+
continue;
|
|
59
|
+
const content = fs.readFileSync(decisionPath, 'utf-8');
|
|
60
|
+
const { title, status } = (0, artifacts_1.parseBetDecision)(content);
|
|
61
|
+
return {
|
|
62
|
+
type: 'bet',
|
|
63
|
+
id: resolved.match(/^BET-\d+/)?.[0] ?? resolved,
|
|
64
|
+
path: path.relative(projectRoot, decisionPath),
|
|
65
|
+
fields: { title, status, links: extractLinks(content) },
|
|
66
|
+
content,
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
return null;
|
|
70
|
+
}
|
|
71
|
+
function resolveDecision(projectRoot, idInput) {
|
|
72
|
+
const filePath = (0, artifacts_1.resolvePdrPath)(projectRoot, idInput);
|
|
73
|
+
if (!filePath)
|
|
74
|
+
return null;
|
|
75
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
76
|
+
const { id, title, status } = (0, artifacts_1.parsePdrFile)(content);
|
|
77
|
+
if (!id)
|
|
78
|
+
return null;
|
|
79
|
+
return {
|
|
80
|
+
type: 'decision',
|
|
81
|
+
id,
|
|
82
|
+
path: path.relative(projectRoot, filePath),
|
|
83
|
+
fields: { title, status },
|
|
84
|
+
content,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
function resolveNote(projectRoot, idInput) {
|
|
88
|
+
const filePath = (0, artifacts_1.resolveNotePath)(projectRoot, idInput);
|
|
89
|
+
if (!filePath)
|
|
90
|
+
return null;
|
|
91
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
92
|
+
const { title, tags } = (0, artifacts_1.parseNoteFrontmatter)(content);
|
|
93
|
+
const id = path.basename(filePath).match(/^(NOTE-\d+)/)?.[1] ?? idInput;
|
|
94
|
+
return {
|
|
95
|
+
type: 'note',
|
|
96
|
+
id,
|
|
97
|
+
path: path.relative(projectRoot, filePath),
|
|
98
|
+
fields: { title, tags },
|
|
99
|
+
content,
|
|
100
|
+
};
|
|
101
|
+
}
|
|
102
|
+
function resolveArtifact(projectRoot, idInput) {
|
|
103
|
+
const normalized = idInput.trim().toUpperCase();
|
|
104
|
+
if (normalized.startsWith('BET'))
|
|
105
|
+
return resolveBet(projectRoot, idInput);
|
|
106
|
+
if (normalized.startsWith('PDR'))
|
|
107
|
+
return resolveDecision(projectRoot, idInput);
|
|
108
|
+
if (normalized.startsWith('NOTE'))
|
|
109
|
+
return resolveNote(projectRoot, idInput);
|
|
110
|
+
return null;
|
|
111
|
+
}
|
|
112
|
+
function showCommand() {
|
|
113
|
+
return new commander_1.Command('show')
|
|
114
|
+
.description('Show a single oprim artifact (bet, decision, or note) resolved by ID')
|
|
115
|
+
.argument('<id>', 'artifact ID, e.g. BET-030, PDR-005, or NOTE-012')
|
|
116
|
+
.option('--json', 'print machine-readable JSON instead of the human-readable view')
|
|
117
|
+
.action((idInput, opts) => {
|
|
118
|
+
const projectRoot = process.cwd();
|
|
119
|
+
const result = resolveArtifact(projectRoot, idInput);
|
|
120
|
+
if (!result) {
|
|
121
|
+
console.error(`${idInput} was not found — checked bets, decisions, and notes.`);
|
|
122
|
+
process.exitCode = 1;
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
if (opts.json) {
|
|
126
|
+
console.log(JSON.stringify({ type: result.type, id: result.id, path: result.path, ...result.fields, content: result.content }));
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
console.log(chalk_1.default.bold(`${result.id}`) + chalk_1.default.dim(` (${result.type}) ${result.path}`));
|
|
130
|
+
for (const [key, value] of Object.entries(result.fields)) {
|
|
131
|
+
if (value === null || value === undefined || value === '')
|
|
132
|
+
continue;
|
|
133
|
+
console.log(chalk_1.default.dim(`${key}:`) + ` ${Array.isArray(value) ? value.join(', ') : value}`);
|
|
134
|
+
}
|
|
135
|
+
console.log('');
|
|
136
|
+
console.log(result.content);
|
|
137
|
+
});
|
|
138
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
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
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
36
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
37
|
+
};
|
|
38
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
39
|
+
exports.statusCommand = statusCommand;
|
|
40
|
+
const commander_1 = require("commander");
|
|
41
|
+
const path = __importStar(require("path"));
|
|
42
|
+
const fs = __importStar(require("fs"));
|
|
43
|
+
const chalk_1 = __importDefault(require("chalk"));
|
|
44
|
+
const yaml = __importStar(require("js-yaml"));
|
|
45
|
+
function statusCommand() {
|
|
46
|
+
return new commander_1.Command('status')
|
|
47
|
+
.description('Show sequencing board state (now/next/later/backlog) and WIP limit usage')
|
|
48
|
+
.option('--json', 'print machine-readable JSON instead of the human-readable report')
|
|
49
|
+
.action((opts) => {
|
|
50
|
+
const projectRoot = process.cwd();
|
|
51
|
+
const sequencePath = path.join(projectRoot, 'oprim', 'sequence.yaml');
|
|
52
|
+
if (!fs.existsSync(sequencePath)) {
|
|
53
|
+
console.error("No oprim/sequence.yaml found — run 'oprim init' first");
|
|
54
|
+
process.exitCode = 1;
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
let board;
|
|
58
|
+
try {
|
|
59
|
+
board = yaml.load(fs.readFileSync(sequencePath, 'utf-8')) ?? {};
|
|
60
|
+
}
|
|
61
|
+
catch {
|
|
62
|
+
console.error('Failed to parse oprim/sequence.yaml');
|
|
63
|
+
process.exitCode = 1;
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
const now = board.now ?? [];
|
|
67
|
+
const next = board.next ?? [];
|
|
68
|
+
const later = board.later ?? [];
|
|
69
|
+
const backlog = board.backlog ?? [];
|
|
70
|
+
if (opts.json) {
|
|
71
|
+
console.log(JSON.stringify({ wip_limits: board.wip_limits ?? {}, now, next, later, backlog }));
|
|
72
|
+
return;
|
|
73
|
+
}
|
|
74
|
+
console.log(chalk_1.default.bold('oprim status') + '\n');
|
|
75
|
+
const wipLimit = board.wip_limits?.now;
|
|
76
|
+
const wipLine = wipLimit !== undefined ? `${now.length}/${wipLimit}` : `${now.length}`;
|
|
77
|
+
console.log(chalk_1.default.dim(`WIP (now): ${wipLine}`));
|
|
78
|
+
const lanes = [
|
|
79
|
+
['now', now],
|
|
80
|
+
['next', next],
|
|
81
|
+
['later', later],
|
|
82
|
+
['backlog', backlog],
|
|
83
|
+
];
|
|
84
|
+
for (const [label, bets] of lanes) {
|
|
85
|
+
console.log(chalk_1.default.bold.cyan(`\n── ${label.toUpperCase()} ──`));
|
|
86
|
+
if (bets.length === 0) {
|
|
87
|
+
console.log(' (empty)');
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
for (const bet of bets) {
|
|
91
|
+
console.log(` ${bet.id} ${bet.title}`);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
});
|
|
95
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
export interface BetSummary {
|
|
2
|
+
id: string;
|
|
3
|
+
title: string;
|
|
4
|
+
status: string | null;
|
|
5
|
+
path: string;
|
|
6
|
+
}
|
|
7
|
+
export interface DecisionSummary {
|
|
8
|
+
id: string;
|
|
9
|
+
title: string;
|
|
10
|
+
status: string | null;
|
|
11
|
+
path: string;
|
|
12
|
+
}
|
|
13
|
+
export interface NoteSummary {
|
|
14
|
+
id: string;
|
|
15
|
+
title: string;
|
|
16
|
+
tags: string[];
|
|
17
|
+
path: string;
|
|
18
|
+
}
|
|
19
|
+
/** Parses a bet-decision.md body for its title and `- Decision: <status>` line. */
|
|
20
|
+
export declare function parseBetDecision(content: string): {
|
|
21
|
+
title: string;
|
|
22
|
+
status: string | null;
|
|
23
|
+
};
|
|
24
|
+
/** Enumerates bets under oprim/bets/pending/ and oprim/bets/archived/. */
|
|
25
|
+
export declare function listBets(projectRoot: string): BetSummary[];
|
|
26
|
+
/** Parses a PDR file for its id, title, and status (ported from decisionsViewScriptTemplate). */
|
|
27
|
+
export declare function parsePdrFile(content: string): {
|
|
28
|
+
id: string | null;
|
|
29
|
+
title: string;
|
|
30
|
+
status: string | null;
|
|
31
|
+
};
|
|
32
|
+
/** Enumerates decisions (PDRs) under oprim/decisions/. */
|
|
33
|
+
export declare function listDecisions(projectRoot: string): DecisionSummary[];
|
|
34
|
+
/** Parses a note's frontmatter (title, tags) — falls back to empty values if frontmatter is absent/unparsable. */
|
|
35
|
+
export declare function parseNoteFrontmatter(content: string): {
|
|
36
|
+
title: string;
|
|
37
|
+
tags: string[];
|
|
38
|
+
};
|
|
39
|
+
/** Enumerates notes under oprim/notes/. */
|
|
40
|
+
export declare function listNotes(projectRoot: string): NoteSummary[];
|
|
41
|
+
/** Resolves a PDR ID (accepting `pdr-005`, `005`, `5`, or `PDR-005`) to its file path under oprim/decisions/. */
|
|
42
|
+
export declare function resolvePdrPath(projectRoot: string, idInput: string): string | null;
|
|
43
|
+
/** Resolves a note ID (accepting `note-005`, `005`, `5`, or `NOTE-005`) to its file path under oprim/notes/. */
|
|
44
|
+
export declare function resolveNotePath(projectRoot: string, idInput: string): string | null;
|
|
@@ -0,0 +1,163 @@
|
|
|
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.parseBetDecision = parseBetDecision;
|
|
37
|
+
exports.listBets = listBets;
|
|
38
|
+
exports.parsePdrFile = parsePdrFile;
|
|
39
|
+
exports.listDecisions = listDecisions;
|
|
40
|
+
exports.parseNoteFrontmatter = parseNoteFrontmatter;
|
|
41
|
+
exports.listNotes = listNotes;
|
|
42
|
+
exports.resolvePdrPath = resolvePdrPath;
|
|
43
|
+
exports.resolveNotePath = resolveNotePath;
|
|
44
|
+
const path = __importStar(require("path"));
|
|
45
|
+
const fs = __importStar(require("fs"));
|
|
46
|
+
const yaml = __importStar(require("js-yaml"));
|
|
47
|
+
function extractBetId(dirName) {
|
|
48
|
+
return dirName.match(/^BET-\d+/)?.[0] ?? dirName;
|
|
49
|
+
}
|
|
50
|
+
/** Parses a bet-decision.md body for its title and `- Decision: <status>` line. */
|
|
51
|
+
function parseBetDecision(content) {
|
|
52
|
+
const titleMatch = content.match(/^#\s*Decision:\s*BET-\d+\s+(.*)$/m);
|
|
53
|
+
const statusMatch = content.match(/^-\s*Decision:\s*(.+)$/m);
|
|
54
|
+
return {
|
|
55
|
+
title: titleMatch ? titleMatch[1].trim() : '',
|
|
56
|
+
status: statusMatch ? statusMatch[1].trim() : null,
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
/** Enumerates bets under oprim/bets/pending/ and oprim/bets/archived/. */
|
|
60
|
+
function listBets(projectRoot) {
|
|
61
|
+
const results = [];
|
|
62
|
+
for (const sub of ['pending', 'archived']) {
|
|
63
|
+
const dir = path.join(projectRoot, 'oprim', 'bets', sub);
|
|
64
|
+
if (!fs.existsSync(dir))
|
|
65
|
+
continue;
|
|
66
|
+
const entries = fs.readdirSync(dir, { withFileTypes: true }).filter((e) => e.isDirectory());
|
|
67
|
+
for (const entry of entries) {
|
|
68
|
+
const decisionPath = path.join(dir, entry.name, 'bet-decision.md');
|
|
69
|
+
if (!fs.existsSync(decisionPath))
|
|
70
|
+
continue;
|
|
71
|
+
const content = fs.readFileSync(decisionPath, 'utf-8');
|
|
72
|
+
const { title, status } = parseBetDecision(content);
|
|
73
|
+
results.push({
|
|
74
|
+
id: extractBetId(entry.name),
|
|
75
|
+
title,
|
|
76
|
+
status,
|
|
77
|
+
path: path.relative(projectRoot, decisionPath),
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
return results;
|
|
82
|
+
}
|
|
83
|
+
/** Parses a PDR file for its id, title, and status (ported from decisionsViewScriptTemplate). */
|
|
84
|
+
function parsePdrFile(content) {
|
|
85
|
+
const idMatch = content.match(/^#\s*(PDR-\d+)[:\s]*(.*)$/m);
|
|
86
|
+
const id = idMatch ? idMatch[1] : null;
|
|
87
|
+
const title = idMatch ? idMatch[2].trim() : '';
|
|
88
|
+
const statusMatch = content.match(/^##\s*Status\s*\n(.+)$/m) ?? content.match(/^Status:\s*(.+)$/m);
|
|
89
|
+
const status = statusMatch ? statusMatch[1].trim() : null;
|
|
90
|
+
return { id, title, status };
|
|
91
|
+
}
|
|
92
|
+
/** Enumerates decisions (PDRs) under oprim/decisions/. */
|
|
93
|
+
function listDecisions(projectRoot) {
|
|
94
|
+
const dir = path.join(projectRoot, 'oprim', 'decisions');
|
|
95
|
+
if (!fs.existsSync(dir))
|
|
96
|
+
return [];
|
|
97
|
+
const results = [];
|
|
98
|
+
const files = fs.readdirSync(dir).filter((f) => /^PDR-\d+.*\.md$/.test(f));
|
|
99
|
+
for (const file of files) {
|
|
100
|
+
const filePath = path.join(dir, file);
|
|
101
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
102
|
+
const { id, title, status } = parsePdrFile(content);
|
|
103
|
+
if (!id)
|
|
104
|
+
continue;
|
|
105
|
+
results.push({ id, title, status, path: path.relative(projectRoot, filePath) });
|
|
106
|
+
}
|
|
107
|
+
return results;
|
|
108
|
+
}
|
|
109
|
+
/** Parses a note's frontmatter (title, tags) — falls back to empty values if frontmatter is absent/unparsable. */
|
|
110
|
+
function parseNoteFrontmatter(content) {
|
|
111
|
+
const fmMatch = content.match(/^---\n([\s\S]*?)\n---/);
|
|
112
|
+
if (!fmMatch)
|
|
113
|
+
return { title: '', tags: [] };
|
|
114
|
+
try {
|
|
115
|
+
const fm = (yaml.load(fmMatch[1]) ?? {});
|
|
116
|
+
return { title: fm.title ?? '', tags: fm.tags ?? [] };
|
|
117
|
+
}
|
|
118
|
+
catch {
|
|
119
|
+
return { title: '', tags: [] };
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
/** Enumerates notes under oprim/notes/. */
|
|
123
|
+
function listNotes(projectRoot) {
|
|
124
|
+
const dir = path.join(projectRoot, 'oprim', 'notes');
|
|
125
|
+
if (!fs.existsSync(dir))
|
|
126
|
+
return [];
|
|
127
|
+
const results = [];
|
|
128
|
+
const files = fs.readdirSync(dir).filter((f) => /^NOTE-\d+.*\.md$/.test(f));
|
|
129
|
+
for (const file of files) {
|
|
130
|
+
const idMatch = file.match(/^(NOTE-\d+)/);
|
|
131
|
+
if (!idMatch)
|
|
132
|
+
continue;
|
|
133
|
+
const filePath = path.join(dir, file);
|
|
134
|
+
const content = fs.readFileSync(filePath, 'utf-8');
|
|
135
|
+
const { title, tags } = parseNoteFrontmatter(content);
|
|
136
|
+
results.push({ id: idMatch[1], title, tags, path: path.relative(projectRoot, filePath) });
|
|
137
|
+
}
|
|
138
|
+
return results;
|
|
139
|
+
}
|
|
140
|
+
function normalizeArtifactId(prefix, input) {
|
|
141
|
+
const digits = input.replace(/[^0-9]/g, '');
|
|
142
|
+
return `${prefix}-${digits.padStart(3, '0')}`;
|
|
143
|
+
}
|
|
144
|
+
function resolveByPrefix(dir, id) {
|
|
145
|
+
if (!fs.existsSync(dir))
|
|
146
|
+
return null;
|
|
147
|
+
const files = fs.readdirSync(dir).filter((f) => f.endsWith('.md'));
|
|
148
|
+
const exact = files.find((f) => f === `${id}.md`);
|
|
149
|
+
if (exact)
|
|
150
|
+
return path.join(dir, exact);
|
|
151
|
+
const slugMatches = files.filter((f) => f.startsWith(`${id}-`));
|
|
152
|
+
if (slugMatches.length === 1)
|
|
153
|
+
return path.join(dir, slugMatches[0]);
|
|
154
|
+
return null;
|
|
155
|
+
}
|
|
156
|
+
/** Resolves a PDR ID (accepting `pdr-005`, `005`, `5`, or `PDR-005`) to its file path under oprim/decisions/. */
|
|
157
|
+
function resolvePdrPath(projectRoot, idInput) {
|
|
158
|
+
return resolveByPrefix(path.join(projectRoot, 'oprim', 'decisions'), normalizeArtifactId('PDR', idInput));
|
|
159
|
+
}
|
|
160
|
+
/** Resolves a note ID (accepting `note-005`, `005`, `5`, or `NOTE-005`) to its file path under oprim/notes/. */
|
|
161
|
+
function resolveNotePath(projectRoot, idInput) {
|
|
162
|
+
return resolveByPrefix(path.join(projectRoot, 'oprim', 'notes'), normalizeArtifactId('NOTE', idInput));
|
|
163
|
+
}
|
package/dist/lib/detect.js
CHANGED
|
@@ -90,6 +90,8 @@ function detectAvailableAgents(projectRoot) {
|
|
|
90
90
|
detected.push('qwen');
|
|
91
91
|
if (fs.existsSync(path.join(projectRoot, '.kimi')))
|
|
92
92
|
detected.push('kimi');
|
|
93
|
+
if (fs.existsSync(path.join(projectRoot, '.dsh')))
|
|
94
|
+
detected.push('dsh');
|
|
93
95
|
return detected;
|
|
94
96
|
}
|
|
95
97
|
function writeAgentsToConfig(agents, projectRoot) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export type Agent = 'claude' | 'cursor' | 'codex' | 'gemini' | 'poolside' | 'vibe' | 'qwen' | 'kimi';
|
|
1
|
+
export type Agent = 'claude' | 'cursor' | 'codex' | 'gemini' | 'poolside' | 'vibe' | 'qwen' | 'kimi' | 'dsh';
|
|
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;
|
|
@@ -13,6 +13,7 @@ export declare const POOLSIDE_SKILLS: Record<string, string>;
|
|
|
13
13
|
export declare const VIBE_SKILLS: Record<string, string>;
|
|
14
14
|
export declare const QWEN_SKILLS: Record<string, string>;
|
|
15
15
|
export declare const KIMI_SKILLS: Record<string, string>;
|
|
16
|
+
export declare const DSH_SKILLS: Record<string, string>;
|
|
16
17
|
export declare const CURSOR_SKILLS: Record<string, string>;
|
|
17
18
|
export declare const CURSOR_COMMANDS: Record<string, string>;
|
|
18
19
|
export declare function specAuthoringSkill(): string;
|
|
@@ -23,3 +24,4 @@ export declare function poolsideInstructions(): string;
|
|
|
23
24
|
export declare function vibeInstructions(): string;
|
|
24
25
|
export declare function qwenInstructions(): string;
|
|
25
26
|
export declare function kimiInstructions(): string;
|
|
27
|
+
export declare function dshInstructions(): 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.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;
|
|
39
|
+
exports.CURSOR_COMMANDS = exports.CURSOR_SKILLS = exports.DSH_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;
|
|
@@ -51,6 +51,7 @@ exports.poolsideInstructions = poolsideInstructions;
|
|
|
51
51
|
exports.vibeInstructions = vibeInstructions;
|
|
52
52
|
exports.qwenInstructions = qwenInstructions;
|
|
53
53
|
exports.kimiInstructions = kimiInstructions;
|
|
54
|
+
exports.dshInstructions = dshInstructions;
|
|
54
55
|
const path = __importStar(require("path"));
|
|
55
56
|
const fs = __importStar(require("fs"));
|
|
56
57
|
const chalk_1 = __importDefault(require("chalk"));
|
|
@@ -68,6 +69,7 @@ exports.SUPPORTED_AGENTS = [
|
|
|
68
69
|
'vibe',
|
|
69
70
|
'qwen',
|
|
70
71
|
'kimi',
|
|
72
|
+
'dsh',
|
|
71
73
|
];
|
|
72
74
|
// oprim/config.yaml (via integrations.spec_framework) is the source of truth for the
|
|
73
75
|
// selected speccing framework; .claude/hooks/config.json is checked only as a fallback for
|
|
@@ -139,6 +141,7 @@ async function promptAgentSelection(projectRoot) {
|
|
|
139
141
|
{ name: 'Mistral Vibe', value: 'vibe', checked: detected.includes('vibe') },
|
|
140
142
|
{ name: 'Qwen Code', value: 'qwen', checked: detected.includes('qwen') },
|
|
141
143
|
{ name: 'Kimi CLI', value: 'kimi', checked: detected.includes('kimi') },
|
|
144
|
+
{ name: 'DeepSeek Harness', value: 'dsh', checked: detected.includes('dsh') },
|
|
142
145
|
],
|
|
143
146
|
});
|
|
144
147
|
}
|
|
@@ -360,6 +363,37 @@ function installAgentSkills(agent, projectRoot, framework = 'openspec', pdrSurfa
|
|
|
360
363
|
console.log(chalk_1.default.dim(' .qwen/ created — Qwen Code will discover these files automatically.'));
|
|
361
364
|
}
|
|
362
365
|
}
|
|
366
|
+
else if (agent === 'dsh') {
|
|
367
|
+
const dshDir = path.join(projectRoot, '.dsh');
|
|
368
|
+
const dirCreated = !fs.existsSync(dshDir);
|
|
369
|
+
const skillsBase = path.join(dshDir, 'skills');
|
|
370
|
+
for (const id of POOLSIDE_SKILL_WORKFLOW_IDS) {
|
|
371
|
+
const schema = (0, workflow_schema_1.loadWorkflowSchema)(id, projectRoot);
|
|
372
|
+
if (!schema.dsh.skill || !schema.skillName)
|
|
373
|
+
continue;
|
|
374
|
+
(0, scaffold_1.writeFile)(path.join(skillsBase, schema.skillName, 'SKILL.md'), (0, workflow_renderer_1.renderSkillBody)(id, projectRoot));
|
|
375
|
+
console.log(chalk_1.default.green('✓') + ` .dsh/skills/${schema.skillName}/SKILL.md`);
|
|
376
|
+
}
|
|
377
|
+
const dshSpecSkillPath = path.join(skillsBase, 'oprim-spec', 'SKILL.md');
|
|
378
|
+
if (framework === 'native') {
|
|
379
|
+
(0, scaffold_1.writeFile)(dshSpecSkillPath, (0, workflow_renderer_1.renderSkillBody)('spec-authoring', projectRoot));
|
|
380
|
+
console.log(chalk_1.default.green('✓') + ' .dsh/skills/oprim-spec/SKILL.md');
|
|
381
|
+
}
|
|
382
|
+
else if (fs.existsSync(dshSpecSkillPath)) {
|
|
383
|
+
fs.unlinkSync(dshSpecSkillPath);
|
|
384
|
+
try {
|
|
385
|
+
fs.rmdirSync(path.dirname(dshSpecSkillPath));
|
|
386
|
+
}
|
|
387
|
+
catch { /* not empty or already gone */ }
|
|
388
|
+
console.log(chalk_1.default.dim(' removed .dsh/skills/oprim-spec/SKILL.md'));
|
|
389
|
+
}
|
|
390
|
+
const agentsFile = path.join(projectRoot, 'AGENTS.md');
|
|
391
|
+
writeAgentInstructionFile(agentsFile, dshInstructions());
|
|
392
|
+
console.log(chalk_1.default.green('✓') + ' AGENTS.md (oprim section written)');
|
|
393
|
+
if (dirCreated) {
|
|
394
|
+
console.log(chalk_1.default.dim(' .dsh/ created — DeepSeek Harness will discover these files automatically.'));
|
|
395
|
+
}
|
|
396
|
+
}
|
|
363
397
|
else if (agent === 'kimi') {
|
|
364
398
|
// Kimi CLI is a split-path install: .kimi/ is the detection signal, but skills are
|
|
365
399
|
// discovered from a project-root .skills/ directory, not .kimi/skills/ — see
|
|
@@ -573,6 +607,10 @@ exports.KIMI_SKILLS = Object.fromEntries(POOLSIDE_SKILL_WORKFLOW_IDS.map((id) =>
|
|
|
573
607
|
const schema = (0, workflow_schema_1.loadWorkflowSchema)(id);
|
|
574
608
|
return [schema.skillName, (0, workflow_renderer_1.renderSkillBody)(id)];
|
|
575
609
|
}));
|
|
610
|
+
exports.DSH_SKILLS = Object.fromEntries(POOLSIDE_SKILL_WORKFLOW_IDS.map((id) => {
|
|
611
|
+
const schema = (0, workflow_schema_1.loadWorkflowSchema)(id);
|
|
612
|
+
return [schema.skillName, (0, workflow_renderer_1.renderSkillBody)(id)];
|
|
613
|
+
}));
|
|
576
614
|
exports.CURSOR_SKILLS = Object.fromEntries(CURSOR_SKILL_WORKFLOW_IDS.map((id) => {
|
|
577
615
|
const schema = (0, workflow_schema_1.loadWorkflowSchema)(id);
|
|
578
616
|
return [schema.skillName, (0, workflow_renderer_1.renderSkillBody)(id)];
|
|
@@ -773,3 +811,6 @@ function qwenInstructions() {
|
|
|
773
811
|
function kimiInstructions() {
|
|
774
812
|
return (0, workflow_renderer_1.renderAgentInstructions)();
|
|
775
813
|
}
|
|
814
|
+
function dshInstructions() {
|
|
815
|
+
return (0, workflow_renderer_1.renderAgentInstructions)();
|
|
816
|
+
}
|
|
@@ -68,12 +68,14 @@ 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,
|
|
73
|
-
// schemas (none declare a `vibe:`/`qwen:`/`kimi:` key) working
|
|
71
|
+
// vibe/qwen/kimi/dsh are optional and mirror poolside by default — see mistral-vibe-agent-support,
|
|
72
|
+
// qwen-code-agent-support, kimi-cli-agent-support, and deepseek-harness-agent-support specs. This
|
|
73
|
+
// keeps existing bundled/forked schemas (none declare a `vibe:`/`qwen:`/`kimi:`/`dsh:` key) working
|
|
74
|
+
// unchanged.
|
|
74
75
|
const vibe = obj.vibe ?? poolside;
|
|
75
76
|
const qwen = obj.qwen ?? poolside;
|
|
76
77
|
const kimi = obj.kimi ?? poolside;
|
|
78
|
+
const dsh = obj.dsh ?? poolside;
|
|
77
79
|
requireField(obj, 'inline', filePath);
|
|
78
80
|
return {
|
|
79
81
|
id: obj.id,
|
|
@@ -87,6 +89,7 @@ function parseSchema(raw, filePath) {
|
|
|
87
89
|
vibe: { skill: Boolean(vibe.skill) },
|
|
88
90
|
qwen: { skill: Boolean(qwen.skill) },
|
|
89
91
|
kimi: { skill: Boolean(kimi.skill) },
|
|
92
|
+
dsh: { skill: Boolean(dsh.skill) },
|
|
90
93
|
inline: Boolean(obj.inline),
|
|
91
94
|
variants: obj.variants ?? null,
|
|
92
95
|
};
|