@bradheitmann/odin-sentinel 0.4.11 → 0.4.13
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/.claude-plugin/marketplace.json +23 -0
- package/README.md +20 -16
- package/dist/src/mcp/server.js +32 -0
- package/dist/src/mcp/server.js.map +1 -1
- package/dist/src/protocol/repository.d.ts +6 -0
- package/dist/src/protocol/repository.js +12 -2
- package/dist/src/protocol/repository.js.map +1 -1
- package/dist/src/protocol/service.js +17 -9
- package/dist/src/protocol/service.js.map +1 -1
- package/dist/src/protocol/version.d.ts +2 -2
- package/dist/src/protocol/version.js +2 -2
- package/docs/guides/quick-start.md +7 -7
- package/docs/guides/quickstart-prompts.md +4 -4
- package/docs/reference/client-compatibility.md +1 -1
- package/docs/reference/distribution.md +11 -5
- package/docs/reference/public-surface-audit.md +2 -2
- package/package.json +5 -3
- package/plugins/odin-scp/.claude-plugin/plugin.json +25 -0
- package/plugins/odin-scp/README.md +62 -0
- package/plugins/odin-scp/skills/odin-scp/CHANGELOG.md +25 -0
- package/plugins/odin-scp/skills/odin-scp/SKILL.md +1518 -0
- package/plugins/odin-scp/skills/odin-scp/agents/openai.yaml +4 -0
- package/plugins/odin-scp/skills/odin-scp/references/boot-receipt-examples.md +439 -0
- package/plugins/odin-scp/skills/odin-scp/references/canonical-introduction-prompt.md +118 -0
- package/plugins/odin-scp/skills/odin-scp/references/harness-skill-targets.md +56 -0
- package/plugins/odin-scp/skills/odin-scp/references/team-bootstrap-runbook.md +298 -0
- package/plugins/odin-scp/skills/odin-scp/scripts/sync-installations.sh +233 -0
- package/protocol/SCP.md +3 -3
- package/protocol/bootstrap-skill.md +22 -13
- package/protocol/closeout.yaml +1 -1
- package/protocol/delegation.yaml +1 -1
- package/protocol/model-profiles.yaml +2 -2
- package/protocol/receipts/team-manifest.yaml +1 -1
- package/protocol/roles.yaml +1 -1
- package/protocol/skill-references/boot-receipt-examples.md +439 -0
- package/protocol/skill-references/canonical-introduction-prompt.md +118 -0
- package/protocol/skill-references/harness-skill-targets.md +56 -0
- package/protocol/skill-references/team-bootstrap-runbook.md +298 -0
- package/protocol/topology.yaml +1 -1
- package/scripts/audit/public-surface.mjs +32 -3
- package/scripts/audit/verify-pack.mjs +175 -12
- package/templates/team-manifest-template.yaml +6 -6
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
|
-
import { readFileSync } from "node:fs";
|
|
2
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "node:fs";
|
|
3
|
+
import { join } from "node:path";
|
|
3
4
|
import { pathToFileURL } from "node:url";
|
|
4
5
|
|
|
5
6
|
export const MINIMUM_COMPATIBLE_CHILD_MCP_VERSION = "0.4.5";
|
|
@@ -13,7 +14,11 @@ const requiredProtocolFiles = [
|
|
|
13
14
|
"protocol/delegation.yaml",
|
|
14
15
|
"protocol/receipts/boot-receipt.yaml",
|
|
15
16
|
"protocol/receipts/team-manifest.yaml",
|
|
16
|
-
"protocol/bootstrap-skill.md"
|
|
17
|
+
"protocol/bootstrap-skill.md",
|
|
18
|
+
"protocol/skill-references/boot-receipt-examples.md",
|
|
19
|
+
"protocol/skill-references/canonical-introduction-prompt.md",
|
|
20
|
+
"protocol/skill-references/harness-skill-targets.md",
|
|
21
|
+
"protocol/skill-references/team-bootstrap-runbook.md"
|
|
17
22
|
];
|
|
18
23
|
|
|
19
24
|
const requiredTemplateFiles = [
|
|
@@ -24,6 +29,7 @@ const requiredTemplateFiles = [
|
|
|
24
29
|
];
|
|
25
30
|
|
|
26
31
|
export const requiredPackageFiles = [
|
|
32
|
+
".claude-plugin/marketplace.json",
|
|
27
33
|
"dist/src/bin/index.js",
|
|
28
34
|
"dist/src/mcp/server.js",
|
|
29
35
|
"dist/src/protocol/index.js",
|
|
@@ -40,9 +46,22 @@ export const requiredPackageFiles = [
|
|
|
40
46
|
"docs/reference/distribution.md",
|
|
41
47
|
"docs/reference/public-surface-audit.md",
|
|
42
48
|
...requiredProtocolFiles,
|
|
49
|
+
"plugins/odin-scp/.claude-plugin/plugin.json",
|
|
50
|
+
"plugins/odin-scp/skills/odin-scp/SKILL.md",
|
|
51
|
+
"plugins/odin-scp/skills/odin-scp/CHANGELOG.md",
|
|
52
|
+
"plugins/odin-scp/skills/odin-scp/agents/openai.yaml",
|
|
53
|
+
"plugins/odin-scp/skills/odin-scp/references/boot-receipt-examples.md",
|
|
54
|
+
"plugins/odin-scp/skills/odin-scp/references/canonical-introduction-prompt.md",
|
|
55
|
+
"plugins/odin-scp/skills/odin-scp/references/harness-skill-targets.md",
|
|
56
|
+
"plugins/odin-scp/skills/odin-scp/references/team-bootstrap-runbook.md",
|
|
57
|
+
"plugins/odin-scp/skills/odin-scp/scripts/sync-installations.sh",
|
|
58
|
+
"plugins/odin-scp/README.md",
|
|
43
59
|
...requiredTemplateFiles,
|
|
44
60
|
"scripts/audit/public-surface.mjs",
|
|
45
61
|
"scripts/audit/verify-pack.mjs",
|
|
62
|
+
"scripts/protocol/install-activation-hooks.mjs",
|
|
63
|
+
"scripts/protocol/verify-governed-context.mjs",
|
|
64
|
+
"scripts/protocol/verify-instruction-read.mjs",
|
|
46
65
|
"AGENTS.md",
|
|
47
66
|
"CLAUDE.md",
|
|
48
67
|
"README.md",
|
|
@@ -67,14 +86,20 @@ const protocolResourceVersionLockedFiles = new Set([
|
|
|
67
86
|
"protocol/topology.yaml"
|
|
68
87
|
]);
|
|
69
88
|
|
|
70
|
-
const forbiddenPackagePrefixes = ["project/" + "planning" + "/", "." + "edge-" + "agentic" + "/local/"];
|
|
89
|
+
const forbiddenPackagePrefixes = ["docs/handoffs/", "project/" + "planning" + "/", "." + "edge-" + "agentic" + "/local/"];
|
|
90
|
+
const AUDIT_SCRIPT_EXEMPTIONS = new Set(["scripts/audit/public-surface.mjs", "scripts/audit/verify-pack.mjs"]);
|
|
91
|
+
const INTERNAL_HANDOFF_REFERENCE_EXEMPTIONS = new Set([...AUDIT_SCRIPT_EXEMPTIONS, "docs/reference/distribution.md"]);
|
|
71
92
|
const forbiddenPackagedContentRules = [
|
|
72
93
|
{ name: "local evidence path", pattern: new RegExp(`\\.${"edge-" + "agentic"}/local`, "i") },
|
|
73
94
|
{ name: "local ODIN audit path", pattern: /\.odin\/local\//i },
|
|
74
95
|
{ name: "private planning path", pattern: new RegExp(`project/${"planning"}/`, "i") },
|
|
96
|
+
{ name: "internal handoff path reference", pattern: /docs\/handoffs\//i, exemptFiles: INTERNAL_HANDOFF_REFERENCE_EXEMPTIONS },
|
|
75
97
|
{ name: "macOS home path", pattern: new RegExp(`/${"Users"}/[A-Za-z0-9._-]+/`) },
|
|
76
98
|
{ name: "Linux home path", pattern: /\/home\/[A-Za-z0-9._-]+\// },
|
|
77
|
-
{ name: "secret-looking assignment", pattern: /(api[_-]?key|secret|token|password)\s*[:=]\s*["'][^"']+["']/i }
|
|
99
|
+
{ name: "secret-looking quoted assignment", pattern: /(api[_-]?key|secret|token|password)\s*[:=]\s*["'][^"']+["']/i },
|
|
100
|
+
{ name: "secret-looking unquoted assignment", pattern: /(api[_-]?key|secret|token|password)\s*[:=]\s*[A-Za-z0-9._~+/=-]{16,}/i },
|
|
101
|
+
{ name: "bearer token literal", pattern: /\bBearer\s+[A-Za-z0-9._~+/=-]{20,}/i },
|
|
102
|
+
{ name: "URI credential literal", pattern: /[a-z][a-z0-9+.-]*:\/\/[^/\s:@]+:[^/\s:@]+@/i }
|
|
78
103
|
];
|
|
79
104
|
|
|
80
105
|
function asPathSet(paths) {
|
|
@@ -89,7 +114,10 @@ export function validatePackageMetadata(packageJson) {
|
|
|
89
114
|
if (!packageJson.license) errors.push("package.json missing license");
|
|
90
115
|
if (!packageJson.engines?.node) errors.push("package.json missing engines.node");
|
|
91
116
|
if (!Array.isArray(packageJson.files) || packageJson.files.length === 0) errors.push("package.json missing files allowlist");
|
|
92
|
-
|
|
117
|
+
if (packageJson.scripts?.prepublishOnly !== "pnpm run validate") {
|
|
118
|
+
errors.push("package.json prepublishOnly must run pnpm run validate");
|
|
119
|
+
}
|
|
120
|
+
for (const file of [".claude-plugin", "docs", "plugins", "protocol", "templates", "AGENTS.md", "CLAUDE.md", "README.md", "LICENSE"]) {
|
|
93
121
|
if (!packageJson.files?.includes(file)) errors.push(`package.json files allowlist missing ${file}`);
|
|
94
122
|
}
|
|
95
123
|
if (packageJson.odin?.publicVersion !== packageJson.version) {
|
|
@@ -106,6 +134,30 @@ export function validatePackageMetadata(packageJson) {
|
|
|
106
134
|
return errors;
|
|
107
135
|
}
|
|
108
136
|
|
|
137
|
+
function walkFiles(dir) {
|
|
138
|
+
if (!existsSync(dir)) return [];
|
|
139
|
+
return readdirSync(dir).flatMap((entry) => {
|
|
140
|
+
const path = join(dir, entry);
|
|
141
|
+
return statSync(path).isDirectory() ? walkFiles(path) : [path];
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
function expectedGeneratedDistFiles() {
|
|
146
|
+
const expected = new Set();
|
|
147
|
+
for (const file of walkFiles("src")) {
|
|
148
|
+
if (!file.endsWith(".ts") || file.endsWith(".d.ts")) continue;
|
|
149
|
+
const jsFile = file.replace(/^src\//, "dist/src/").replace(/\.ts$/, ".js");
|
|
150
|
+
expected.add(jsFile);
|
|
151
|
+
expected.add(`${jsFile}.map`);
|
|
152
|
+
expected.add(jsFile.replace(/\.js$/, ".d.ts"));
|
|
153
|
+
}
|
|
154
|
+
return expected;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function allowedGeneratedDistFiles() {
|
|
158
|
+
return new Set([...requiredPackageFiles.filter((file) => file.startsWith("dist/")), ...expectedGeneratedDistFiles()]);
|
|
159
|
+
}
|
|
160
|
+
|
|
109
161
|
export function validatePackFileList(pathsInput) {
|
|
110
162
|
const paths = asPathSet(pathsInput);
|
|
111
163
|
const errors = [];
|
|
@@ -118,6 +170,10 @@ export function validatePackFileList(pathsInput) {
|
|
|
118
170
|
const privatePaths = Array.from(paths).filter((file) => forbiddenPackagePrefixes.some((prefix) => file.startsWith(prefix)));
|
|
119
171
|
if (privatePaths.length > 0) errors.push(`Package includes private local paths: ${privatePaths.join(", ")}`);
|
|
120
172
|
|
|
173
|
+
const allowed = new Set([...requiredPackageFiles, ...allowedGeneratedDistFiles()]);
|
|
174
|
+
const unexpected = Array.from(paths).filter((file) => !allowed.has(file));
|
|
175
|
+
if (unexpected.length > 0) errors.push(`Package includes unexpected files: ${unexpected.join(", ")}`);
|
|
176
|
+
|
|
121
177
|
return errors;
|
|
122
178
|
}
|
|
123
179
|
|
|
@@ -125,6 +181,7 @@ export function validatePackFileContents(fileTextByPath) {
|
|
|
125
181
|
const findings = [];
|
|
126
182
|
for (const [file, text] of Object.entries(fileTextByPath)) {
|
|
127
183
|
for (const rule of forbiddenPackagedContentRules) {
|
|
184
|
+
if (rule.exemptFiles?.has(file)) continue;
|
|
128
185
|
if (rule.pattern.test(text)) findings.push(`${file}: ${rule.name}`);
|
|
129
186
|
}
|
|
130
187
|
}
|
|
@@ -186,6 +243,56 @@ export function findStaleVersionReferences(fileTextByPath, currentVersion, minim
|
|
|
186
243
|
return findings;
|
|
187
244
|
}
|
|
188
245
|
|
|
246
|
+
export function findUnpinnedInstallReferences(fileTextByPath, currentVersion) {
|
|
247
|
+
const findings = [];
|
|
248
|
+
const packageName = "@bradheitmann/odin-sentinel";
|
|
249
|
+
const pinned = `${packageName}@${currentVersion}`;
|
|
250
|
+
const commandMarkers = [
|
|
251
|
+
"pnpm",
|
|
252
|
+
"npx",
|
|
253
|
+
"npm",
|
|
254
|
+
"claude mcp",
|
|
255
|
+
"--package",
|
|
256
|
+
"installurl",
|
|
257
|
+
"\"args\"",
|
|
258
|
+
"args =",
|
|
259
|
+
"command"
|
|
260
|
+
];
|
|
261
|
+
|
|
262
|
+
for (const [file, text] of Object.entries(fileTextByPath)) {
|
|
263
|
+
if (AUDIT_SCRIPT_EXEMPTIONS.has(file)) continue;
|
|
264
|
+
const lines = text.split("\n");
|
|
265
|
+
for (const [index, line] of lines.entries()) {
|
|
266
|
+
if (!line.includes(packageName)) continue;
|
|
267
|
+
const windowText = lines.slice(Math.max(0, index - 2), Math.min(lines.length, index + 3)).join(" ");
|
|
268
|
+
const lowerWindow = windowText.toLowerCase();
|
|
269
|
+
if (!commandMarkers.some((marker) => lowerWindow.includes(marker))) continue;
|
|
270
|
+
if (windowText.includes(pinned)) continue;
|
|
271
|
+
findings.push(`${file}:${index + 1}: install command must pin ${pinned}`);
|
|
272
|
+
}
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
return findings;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
export function validateRuntimeVersionConstants(versionText, currentVersion, minimumCompatibleVersion = MINIMUM_COMPATIBLE_CHILD_MCP_VERSION, file = "src/protocol/version.ts") {
|
|
279
|
+
const errors = [];
|
|
280
|
+
const required = [
|
|
281
|
+
[`PROTOCOL_SCHEMA_VERSION`, currentVersion],
|
|
282
|
+
[`PUBLIC_LATEST_VERSION`, currentVersion],
|
|
283
|
+
[`MINIMUM_COMPATIBLE_MCP_VERSION`, minimumCompatibleVersion]
|
|
284
|
+
];
|
|
285
|
+
|
|
286
|
+
for (const [constant, expected] of required) {
|
|
287
|
+
const pattern = new RegExp(`\\b${constant}\\s*=\\s*["']${expected.replaceAll(".", "\\.")}["']`);
|
|
288
|
+
if (!pattern.test(versionText)) {
|
|
289
|
+
errors.push(`${file}: ${constant} must be ${expected}`);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
return errors;
|
|
294
|
+
}
|
|
295
|
+
|
|
189
296
|
export function validatePublicProtocolSync({ scpText, bootstrapText, currentVersion, minimumCompatibleVersion = MINIMUM_COMPATIBLE_CHILD_MCP_VERSION }) {
|
|
190
297
|
const errors = [];
|
|
191
298
|
const requiredMarkers = [
|
|
@@ -217,6 +324,9 @@ export function validatePluginSync({ pluginManifestText, pluginSkillText, plugin
|
|
|
217
324
|
manifest = {};
|
|
218
325
|
}
|
|
219
326
|
|
|
327
|
+
if (manifest.name !== "odin-scp") {
|
|
328
|
+
errors.push(`Claude plugin manifest name ${manifest.name ?? "<missing>"} must be odin-scp`);
|
|
329
|
+
}
|
|
220
330
|
if (manifest.version !== currentVersion) {
|
|
221
331
|
errors.push(`Claude plugin manifest version ${manifest.version ?? "<missing>"} must match package version ${currentVersion}`);
|
|
222
332
|
}
|
|
@@ -226,9 +336,13 @@ export function validatePluginSync({ pluginManifestText, pluginSkillText, plugin
|
|
|
226
336
|
} else {
|
|
227
337
|
if (server.command !== "pnpm") errors.push("Claude plugin odin-sentinel server must use pnpm");
|
|
228
338
|
const args = Array.isArray(server.args) ? server.args : [];
|
|
229
|
-
|
|
339
|
+
const expectedArgs = ["dlx", "--package", `@bradheitmann/odin-sentinel@${currentVersion}`, "odin-sentinel-mcp"];
|
|
340
|
+
for (const requiredArg of expectedArgs) {
|
|
230
341
|
if (!args.includes(requiredArg)) errors.push(`Claude plugin odin-sentinel args missing ${requiredArg}`);
|
|
231
342
|
}
|
|
343
|
+
if (JSON.stringify(args) !== JSON.stringify(expectedArgs)) {
|
|
344
|
+
errors.push(`Claude plugin odin-sentinel args must exactly equal ${JSON.stringify(expectedArgs)}`);
|
|
345
|
+
}
|
|
232
346
|
}
|
|
233
347
|
|
|
234
348
|
for (const marker of [`SCP_PUBLIC_VERSION: ${currentVersion}`, `MIN_COMPATIBLE_CHILD_MCP: ${minimumCompatibleVersion}`]) {
|
|
@@ -244,6 +358,39 @@ export function validatePluginSync({ pluginManifestText, pluginSkillText, plugin
|
|
|
244
358
|
return errors;
|
|
245
359
|
}
|
|
246
360
|
|
|
361
|
+
export function validateMarketplaceSync({ marketplaceText, currentVersion }) {
|
|
362
|
+
const errors = [];
|
|
363
|
+
let marketplace;
|
|
364
|
+
try {
|
|
365
|
+
marketplace = JSON.parse(marketplaceText);
|
|
366
|
+
} catch {
|
|
367
|
+
errors.push("Claude marketplace manifest must be valid JSON");
|
|
368
|
+
marketplace = {};
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const plugins = Array.isArray(marketplace.plugins) ? marketplace.plugins : [];
|
|
372
|
+
const odinScp = plugins.find((plugin) => plugin?.name === "odin-scp");
|
|
373
|
+
if (!odinScp) {
|
|
374
|
+
errors.push("Claude marketplace manifest must advertise odin-scp");
|
|
375
|
+
} else {
|
|
376
|
+
if (odinScp.source !== "./plugins/odin-scp") {
|
|
377
|
+
errors.push(`Claude marketplace odin-scp source ${odinScp.source ?? "<missing>"} must be ./plugins/odin-scp`);
|
|
378
|
+
}
|
|
379
|
+
if (odinScp.version !== currentVersion) {
|
|
380
|
+
errors.push(`Claude marketplace odin-scp version ${odinScp.version ?? "<missing>"} must match package version ${currentVersion}`);
|
|
381
|
+
}
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
const legacyName = `sentinel-${"coordination"}-${"protocol"}`;
|
|
385
|
+
const legacySource = `./plugins/${legacyName}`;
|
|
386
|
+
const staleLegacy = plugins.find((plugin) => plugin?.name === legacyName || plugin?.source === legacySource);
|
|
387
|
+
if (staleLegacy) {
|
|
388
|
+
errors.push("Claude marketplace manifest must not advertise the legacy long-name plugin");
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
return errors;
|
|
392
|
+
}
|
|
393
|
+
|
|
247
394
|
export function validatePackagedProtocolVersions(fileTextByPath, currentVersion) {
|
|
248
395
|
const errors = [];
|
|
249
396
|
for (const [file, text] of Object.entries(fileTextByPath)) {
|
|
@@ -280,11 +427,20 @@ function readPublicVersionFiles() {
|
|
|
280
427
|
"docs/reference/client-compatibility.md",
|
|
281
428
|
"docs/reference/distribution.md",
|
|
282
429
|
"docs/reference/public-surface-audit.md",
|
|
430
|
+
"src/protocol/version.ts",
|
|
431
|
+
".claude-plugin/marketplace.json",
|
|
283
432
|
"protocol/SCP.md",
|
|
284
433
|
"protocol/bootstrap-skill.md",
|
|
285
|
-
"plugins/
|
|
286
|
-
"plugins/
|
|
287
|
-
"plugins/
|
|
434
|
+
"plugins/odin-scp/.claude-plugin/plugin.json",
|
|
435
|
+
"plugins/odin-scp/skills/odin-scp/SKILL.md",
|
|
436
|
+
"plugins/odin-scp/skills/odin-scp/CHANGELOG.md",
|
|
437
|
+
"plugins/odin-scp/skills/odin-scp/agents/openai.yaml",
|
|
438
|
+
"plugins/odin-scp/skills/odin-scp/references/boot-receipt-examples.md",
|
|
439
|
+
"plugins/odin-scp/skills/odin-scp/references/canonical-introduction-prompt.md",
|
|
440
|
+
"plugins/odin-scp/skills/odin-scp/references/harness-skill-targets.md",
|
|
441
|
+
"plugins/odin-scp/skills/odin-scp/references/team-bootstrap-runbook.md",
|
|
442
|
+
"plugins/odin-scp/skills/odin-scp/scripts/sync-installations.sh",
|
|
443
|
+
"plugins/odin-scp/README.md"
|
|
288
444
|
].map((file) => [file, readFileSync(file, "utf8")]));
|
|
289
445
|
}
|
|
290
446
|
|
|
@@ -304,15 +460,22 @@ export function runVerifyPack({ pack, packageJson, publicVersionFiles, costPriva
|
|
|
304
460
|
...validatePackFileContents(packFileTexts),
|
|
305
461
|
...validatePackagedProtocolVersions(packFileTexts, packageJson.version),
|
|
306
462
|
...findStaleVersionReferences(publicVersionFiles, packageJson.version),
|
|
463
|
+
...findUnpinnedInstallReferences(packFileTexts, packageJson.version),
|
|
464
|
+
...validateRuntimeVersionConstants(publicVersionFiles["src/protocol/version.ts"], packageJson.version),
|
|
465
|
+
...validateRuntimeVersionConstants(packFileTexts["dist/src/protocol/version.js"] ?? "", packageJson.version, MINIMUM_COMPATIBLE_CHILD_MCP_VERSION, "dist/src/protocol/version.js"),
|
|
307
466
|
...validatePublicProtocolSync({
|
|
308
467
|
scpText: publicVersionFiles["protocol/SCP.md"],
|
|
309
468
|
bootstrapText: publicVersionFiles["protocol/bootstrap-skill.md"],
|
|
310
469
|
currentVersion: packageJson.version
|
|
311
470
|
}),
|
|
471
|
+
...validateMarketplaceSync({
|
|
472
|
+
marketplaceText: publicVersionFiles[".claude-plugin/marketplace.json"],
|
|
473
|
+
currentVersion: packageJson.version
|
|
474
|
+
}),
|
|
312
475
|
...validatePluginSync({
|
|
313
|
-
pluginManifestText: publicVersionFiles["plugins/
|
|
314
|
-
pluginSkillText: publicVersionFiles["plugins/
|
|
315
|
-
pluginReadmeText: publicVersionFiles["plugins/
|
|
476
|
+
pluginManifestText: publicVersionFiles["plugins/odin-scp/.claude-plugin/plugin.json"],
|
|
477
|
+
pluginSkillText: publicVersionFiles["plugins/odin-scp/skills/odin-scp/SKILL.md"],
|
|
478
|
+
pluginReadmeText: publicVersionFiles["plugins/odin-scp/README.md"],
|
|
316
479
|
currentVersion: packageJson.version,
|
|
317
480
|
expectedToolCount: extractToolCount(packageJson.description)
|
|
318
481
|
}),
|
|
@@ -30,7 +30,7 @@ role_slots:
|
|
|
30
30
|
workspace: "<workspace-ref>"
|
|
31
31
|
pane: "<pane-ref>"
|
|
32
32
|
surface: "<surface-ref>"
|
|
33
|
-
scp_context_source: "native
|
|
33
|
+
scp_context_source: "native odin-scp skill"
|
|
34
34
|
mcp_available: true
|
|
35
35
|
mcp_version: "0.4.5"
|
|
36
36
|
scp_skill_available: true
|
|
@@ -43,7 +43,7 @@ role_slots:
|
|
|
43
43
|
workspace: "<workspace-ref>"
|
|
44
44
|
pane: "<pane-ref>"
|
|
45
45
|
surface: "<surface-ref>"
|
|
46
|
-
scp_context_source: "native
|
|
46
|
+
scp_context_source: "native odin-scp skill"
|
|
47
47
|
mcp_available: true
|
|
48
48
|
mcp_version: "0.4.5"
|
|
49
49
|
scp_skill_available: true
|
|
@@ -58,7 +58,7 @@ role_slots:
|
|
|
58
58
|
workspace: "<workspace-ref>"
|
|
59
59
|
pane: "<pane-ref>"
|
|
60
60
|
surface: "<surface-ref>"
|
|
61
|
-
scp_context_source: "native
|
|
61
|
+
scp_context_source: "native odin-scp skill"
|
|
62
62
|
mcp_available: true
|
|
63
63
|
mcp_version: "0.4.5"
|
|
64
64
|
scp_skill_available: true
|
|
@@ -73,7 +73,7 @@ role_slots:
|
|
|
73
73
|
workspace: "<workspace-ref>"
|
|
74
74
|
pane: "<pane-ref>"
|
|
75
75
|
surface: "<surface-ref>"
|
|
76
|
-
scp_context_source: "native
|
|
76
|
+
scp_context_source: "native odin-scp skill"
|
|
77
77
|
mcp_available: true
|
|
78
78
|
mcp_version: "0.4.5"
|
|
79
79
|
scp_skill_available: true
|
|
@@ -121,7 +121,7 @@ user_defined_criteria:
|
|
|
121
121
|
# workspace: "workspace:1"
|
|
122
122
|
# pane: "pane:1"
|
|
123
123
|
# surface: "surface:1"
|
|
124
|
-
# scp_context_source: "native
|
|
124
|
+
# scp_context_source: "native odin-scp skill"
|
|
125
125
|
# mcp_available: true
|
|
126
126
|
# mcp_version: "0.4.5"
|
|
127
127
|
# scp_skill_available: true
|
|
@@ -134,7 +134,7 @@ user_defined_criteria:
|
|
|
134
134
|
# workspace: "workspace:1"
|
|
135
135
|
# pane: "pane:2"
|
|
136
136
|
# surface: "surface:2"
|
|
137
|
-
# scp_context_source: "native
|
|
137
|
+
# scp_context_source: "native odin-scp skill"
|
|
138
138
|
# mcp_available: true
|
|
139
139
|
# mcp_version: "0.4.5"
|
|
140
140
|
# scp_skill_available: true
|