co-maintainer 0.4.10 → 0.4.11
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/package.json
CHANGED
|
@@ -4,4 +4,4 @@ export type ValidationResult = {
|
|
|
4
4
|
errors: string[];
|
|
5
5
|
warnings: string[];
|
|
6
6
|
};
|
|
7
|
-
export declare function validateSkill(markdown: string,
|
|
7
|
+
export declare function validateSkill(markdown: string, source?: Source, referenceIssues?: "error" | "warning"): Promise<ValidationResult>;
|
|
@@ -1,5 +1,4 @@
|
|
|
1
|
-
|
|
2
|
-
export async function validateSkill(markdown, outputDirectory, source, referenceIssues = "error") {
|
|
1
|
+
export async function validateSkill(markdown, source, referenceIssues = "error") {
|
|
3
2
|
const errors = [];
|
|
4
3
|
const warnings = [];
|
|
5
4
|
const referenceProblems = referenceIssues === "error" ? errors : warnings;
|
|
@@ -33,54 +32,6 @@ export async function validateSkill(markdown, outputDirectory, source, reference
|
|
|
33
32
|
if (source) {
|
|
34
33
|
const paths = new Set([...source.tree, ...Object.keys(source.files)]);
|
|
35
34
|
const repositoryName = String(source.repo.full_name ?? "");
|
|
36
|
-
const commands = new Set();
|
|
37
|
-
for (const [path, content] of Object.entries(source.files)) {
|
|
38
|
-
if (path.endsWith("package.json")) {
|
|
39
|
-
try {
|
|
40
|
-
const scripts = JSON.parse(content)
|
|
41
|
-
.scripts ?? {};
|
|
42
|
-
const prefix = source.tree.some((item) => /pnpm-lock\.yaml$/.test(item))
|
|
43
|
-
? "pnpm"
|
|
44
|
-
: source.tree.some((item) => /yarn\.lock$/.test(item))
|
|
45
|
-
? "yarn"
|
|
46
|
-
: "npm";
|
|
47
|
-
for (const name of Object.keys(scripts)) {
|
|
48
|
-
commands.add(`${prefix} ${name}`);
|
|
49
|
-
commands.add(`${prefix} run ${name}`);
|
|
50
|
-
commands.add(String(scripts[name]));
|
|
51
|
-
}
|
|
52
|
-
}
|
|
53
|
-
catch {
|
|
54
|
-
// Ignore malformed manifests; syntax validation is outside this gate.
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
if (/deno\.jsonc?$/.test(path)) {
|
|
58
|
-
try {
|
|
59
|
-
const tasks = JSON.parse(content).tasks ??
|
|
60
|
-
{};
|
|
61
|
-
for (const [name, task] of Object.entries(tasks)) {
|
|
62
|
-
commands.add(`deno task ${name}`);
|
|
63
|
-
commands.add(String(task));
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
catch {
|
|
67
|
-
// Ignore malformed manifests; syntax validation is outside this gate.
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
for (const match of content.matchAll(/^\s*run:\s*([^\s#].*?)\s*$/gm)) {
|
|
71
|
-
commands.add(match[1].replace(/^['"]|['"]$/g, ""));
|
|
72
|
-
}
|
|
73
|
-
for (const match of content.matchAll(/`((?:npm|pnpm|yarn|bun|deno|cargo|make|go|python|node|git)\s+[^`\n]+)`/g)) {
|
|
74
|
-
commands.add(match[1]);
|
|
75
|
-
}
|
|
76
|
-
for (const match of content.matchAll(/^\s*((?:npm|pnpm|yarn|bun|deno|cargo|make|go|python|node|git)\s+[^\n`]+)$/gm)) {
|
|
77
|
-
commands.add(match[1].trim());
|
|
78
|
-
}
|
|
79
|
-
}
|
|
80
|
-
if (source.tree.some((path) => /Cargo\.toml$/.test(path))) {
|
|
81
|
-
commands.add("cargo test");
|
|
82
|
-
commands.add("cargo build");
|
|
83
|
-
}
|
|
84
35
|
const references = [...markdown.matchAll(/`([^`\n]+)`/g)].map((match) => match[1]);
|
|
85
36
|
for (const reference of references) {
|
|
86
37
|
const isQualifier = /^[a-z][a-z\d_-]*:/i.test(reference);
|
|
@@ -109,27 +60,14 @@ export async function validateSkill(markdown, outputDirectory, source, reference
|
|
|
109
60
|
referenceProblems.push(`referenced path is absent from source: ${reference}`);
|
|
110
61
|
}
|
|
111
62
|
}
|
|
112
|
-
if (/^(?:npm|pnpm|yarn|bun|deno|cargo|make|go|python|node|git)\s/.test(reference) &&
|
|
113
|
-
![...commands].some((command) => reference === command ||
|
|
114
|
-
reference.startsWith(`${command} `) ||
|
|
115
|
-
command.startsWith(`${reference} `))) {
|
|
116
|
-
referenceProblems.push(`referenced command is absent from source: ${reference}`);
|
|
117
|
-
}
|
|
118
63
|
}
|
|
119
64
|
}
|
|
120
|
-
const
|
|
121
|
-
|
|
122
|
-
if (/^[a-z][a-z\d+.-]*:/i.test(
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
errors.push(`link is not relative: ${
|
|
126
|
-
continue;
|
|
127
|
-
}
|
|
128
|
-
try {
|
|
129
|
-
await stat(`${outputDirectory}/${link}`);
|
|
130
|
-
}
|
|
131
|
-
catch {
|
|
132
|
-
errors.push(`linked file does not exist: ${link}`);
|
|
65
|
+
for (const link of markdown.matchAll(/\[[^\]]+\]\(([^)]+)\)/g)) {
|
|
66
|
+
const target = link[1];
|
|
67
|
+
if (/^[a-z][a-z\d+.-]*:/i.test(target) ||
|
|
68
|
+
target.startsWith("/") ||
|
|
69
|
+
target.includes("..")) {
|
|
70
|
+
errors.push(`link is not relative: ${target}`);
|
|
133
71
|
}
|
|
134
72
|
}
|
|
135
73
|
return { valid: errors.length === 0, errors, warnings };
|
|
@@ -204,19 +204,19 @@ export async function runInitOrRemake(options) {
|
|
|
204
204
|
let result = await timed("assemble skill", options.logTime, () => assembleSkill(options.repo, facts, previousMarkdown, previous?.sectionHashes ?? {}, overrides));
|
|
205
205
|
const reviewDocuments = buildReviewDocuments(facts);
|
|
206
206
|
result.markdown = addReviewLink(result.markdown, reviewDocuments);
|
|
207
|
-
const validation = await timed("validate skill", options.logTime, () => validateSkill(result.markdown,
|
|
207
|
+
const validation = await timed("validate skill", options.logTime, () => validateSkill(result.markdown, source));
|
|
208
208
|
if (!validation.valid && Object.keys(overrides).length) {
|
|
209
209
|
log("validate", `AI output rejected: ${validation.errors.join("; ")}`);
|
|
210
210
|
overrides = {};
|
|
211
211
|
result = await timed("reassemble valid skill", options.logTime, () => assembleSkill(options.repo, facts, previousMarkdown, previous?.sectionHashes ?? {}, overrides));
|
|
212
212
|
result.markdown = addReviewLink(result.markdown, reviewDocuments);
|
|
213
213
|
}
|
|
214
|
-
const finalValidation = await timed("final skill validation", options.logTime, () => validateSkill(result.markdown,
|
|
214
|
+
const finalValidation = await timed("final skill validation", options.logTime, () => validateSkill(result.markdown, source, "warning"));
|
|
215
215
|
if (finalValidation.warnings.length) {
|
|
216
216
|
log("validate", `stale references kept: ${finalValidation.warnings.join(", ")}`);
|
|
217
217
|
}
|
|
218
218
|
if (!finalValidation.valid) {
|
|
219
|
-
|
|
219
|
+
log("validate", `skill problems kept: ${finalValidation.errors.join("; ")}`);
|
|
220
220
|
}
|
|
221
221
|
await timed("write skill and state", options.logTime, async () => {
|
|
222
222
|
const { withKnowledgeLock } = await import("../review/guides.js");
|