@open-product-primer/cli 0.13.0 → 1.0.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/doctor.js +5 -0
- package/dist/lib/integrity.d.ts +8 -0
- package/dist/lib/integrity.js +115 -0
- package/package.json +1 -1
package/dist/commands/doctor.js
CHANGED
|
@@ -43,6 +43,7 @@ const fs = __importStar(require("fs"));
|
|
|
43
43
|
const chalk_1 = __importDefault(require("chalk"));
|
|
44
44
|
const detect_1 = require("../lib/detect");
|
|
45
45
|
const measure_1 = require("../lib/measure");
|
|
46
|
+
const integrity_1 = require("../lib/integrity");
|
|
46
47
|
const AGENT_DIRS = {
|
|
47
48
|
claude: '.claude',
|
|
48
49
|
cursor: '.cursor',
|
|
@@ -204,6 +205,10 @@ function doctorCommand() {
|
|
|
204
205
|
});
|
|
205
206
|
}
|
|
206
207
|
}
|
|
208
|
+
// ── Sequence integrity checks ─────────────────────────────────────────────
|
|
209
|
+
(0, integrity_1.checkSequenceIntegrity)(projectRoot, checks);
|
|
210
|
+
// ── Skill version drift checks ────────────────────────────────────────────
|
|
211
|
+
(0, integrity_1.checkSkillVersionDrift)(projectRoot, checks);
|
|
207
212
|
// ── Agent environment checks ──────────────────────────────────────────────
|
|
208
213
|
const configAgents = (0, detect_1.readAgentsFromConfig)(projectRoot);
|
|
209
214
|
if (configAgents !== null) {
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export interface Check {
|
|
2
|
+
name: string;
|
|
3
|
+
pass: boolean;
|
|
4
|
+
note?: string;
|
|
5
|
+
required: boolean;
|
|
6
|
+
}
|
|
7
|
+
export declare function checkSequenceIntegrity(projectRoot: string, checks: Check[]): void;
|
|
8
|
+
export declare function checkSkillVersionDrift(projectRoot: string, checks: Check[]): void;
|
|
@@ -0,0 +1,115 @@
|
|
|
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.checkSequenceIntegrity = checkSequenceIntegrity;
|
|
37
|
+
exports.checkSkillVersionDrift = checkSkillVersionDrift;
|
|
38
|
+
const path = __importStar(require("path"));
|
|
39
|
+
const fs = __importStar(require("fs"));
|
|
40
|
+
const yaml = __importStar(require("js-yaml"));
|
|
41
|
+
const install_agent_1 = require("./install-agent");
|
|
42
|
+
function checkSequenceIntegrity(projectRoot, checks) {
|
|
43
|
+
const sequencePath = path.join(projectRoot, 'oprim', 'sequence.yaml');
|
|
44
|
+
if (!fs.existsSync(sequencePath))
|
|
45
|
+
return;
|
|
46
|
+
let seq;
|
|
47
|
+
try {
|
|
48
|
+
seq = yaml.load(fs.readFileSync(sequencePath, 'utf-8'));
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
checks.push({
|
|
52
|
+
name: 'sequence: could not parse sequence.yaml',
|
|
53
|
+
pass: false,
|
|
54
|
+
note: 'Fix YAML syntax errors in oprim/sequence.yaml',
|
|
55
|
+
required: false,
|
|
56
|
+
});
|
|
57
|
+
return;
|
|
58
|
+
}
|
|
59
|
+
const lanes = [seq.now, seq.next, seq.later, seq.backlog].filter((l) => Array.isArray(l));
|
|
60
|
+
const allIds = new Set(lanes.flat().map((e) => e.id));
|
|
61
|
+
const wipLimit = seq.wip_limits?.now;
|
|
62
|
+
const nowCount = seq.now?.length ?? 0;
|
|
63
|
+
if (wipLimit !== undefined && nowCount > wipLimit) {
|
|
64
|
+
checks.push({
|
|
65
|
+
name: `sequence: WIP limit exceeded (${nowCount} active, limit ${wipLimit})`,
|
|
66
|
+
pass: false,
|
|
67
|
+
note: 'Move items out of now: to respect wip_limits.now',
|
|
68
|
+
required: false,
|
|
69
|
+
});
|
|
70
|
+
}
|
|
71
|
+
for (const lane of lanes) {
|
|
72
|
+
for (const entry of lane) {
|
|
73
|
+
for (const ref of entry.blocked_by ?? []) {
|
|
74
|
+
if (!allIds.has(ref)) {
|
|
75
|
+
checks.push({
|
|
76
|
+
name: `sequence: dangling blocked_by reference ${ref}`,
|
|
77
|
+
pass: false,
|
|
78
|
+
note: `${entry.id} references ${ref} which is not in the sequence`,
|
|
79
|
+
required: false,
|
|
80
|
+
});
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
for (const ref of entry.unlocks ?? []) {
|
|
84
|
+
if (!allIds.has(ref)) {
|
|
85
|
+
checks.push({
|
|
86
|
+
name: `sequence: dangling unlocks reference ${ref}`,
|
|
87
|
+
pass: false,
|
|
88
|
+
note: `${entry.id} references ${ref} which is not in the sequence`,
|
|
89
|
+
required: false,
|
|
90
|
+
});
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
function checkSkillVersionDrift(projectRoot, checks) {
|
|
97
|
+
const skillsDir = path.join(projectRoot, '.claude', 'skills');
|
|
98
|
+
if (!fs.existsSync(skillsDir))
|
|
99
|
+
return;
|
|
100
|
+
for (const [name, bundledContent] of Object.entries(install_agent_1.CLAUDE_SKILLS)) {
|
|
101
|
+
const skillPath = path.join(skillsDir, name, 'SKILL.md');
|
|
102
|
+
if (!fs.existsSync(skillPath))
|
|
103
|
+
continue;
|
|
104
|
+
const installed = fs.readFileSync(skillPath, 'utf-8');
|
|
105
|
+
const stripped = installed.replace('\n\n' + install_agent_1.OPRIM_CONTEXT_SKILL_STEP + '\n\n', '\n\n');
|
|
106
|
+
if (stripped !== bundledContent) {
|
|
107
|
+
checks.push({
|
|
108
|
+
name: `agent: Claude skill ${name} is out of date`,
|
|
109
|
+
pass: false,
|
|
110
|
+
note: "Run 'oprim update' to refresh",
|
|
111
|
+
required: false,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
}
|