@squads-sh/validator 0.1.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/adapters/filesystem.d.ts +11 -0
- package/dist/adapters/filesystem.d.ts.map +1 -0
- package/dist/adapters/filesystem.js +37 -0
- package/dist/adapters/filesystem.js.map +1 -0
- package/dist/categories/agents.d.ts +21 -0
- package/dist/categories/agents.d.ts.map +1 -0
- package/dist/categories/agents.js +214 -0
- package/dist/categories/agents.js.map +1 -0
- package/dist/categories/cross-refs.d.ts +13 -0
- package/dist/categories/cross-refs.d.ts.map +1 -0
- package/dist/categories/cross-refs.js +88 -0
- package/dist/categories/cross-refs.js.map +1 -0
- package/dist/categories/manifest.d.ts +19 -0
- package/dist/categories/manifest.d.ts.map +1 -0
- package/dist/categories/manifest.js +150 -0
- package/dist/categories/manifest.js.map +1 -0
- package/dist/categories/structure.d.ts +14 -0
- package/dist/categories/structure.d.ts.map +1 -0
- package/dist/categories/structure.js +76 -0
- package/dist/categories/structure.js.map +1 -0
- package/dist/categories/tasks.d.ts +20 -0
- package/dist/categories/tasks.d.ts.map +1 -0
- package/dist/categories/tasks.js +195 -0
- package/dist/categories/tasks.js.map +1 -0
- package/dist/categories/workflows.d.ts +17 -0
- package/dist/categories/workflows.d.ts.map +1 -0
- package/dist/categories/workflows.js +125 -0
- package/dist/categories/workflows.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +62 -0
- package/dist/index.js.map +1 -0
- package/dist/parser.d.ts +20 -0
- package/dist/parser.d.ts.map +1 -0
- package/dist/parser.js +58 -0
- package/dist/parser.js.map +1 -0
- package/dist/scoring.d.ts +22 -0
- package/dist/scoring.d.ts.map +1 -0
- package/dist/scoring.js +57 -0
- package/dist/scoring.js.map +1 -0
- package/dist/types.d.ts +101 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +19 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/enums.d.ts +9 -0
- package/dist/utils/enums.d.ts.map +1 -0
- package/dist/utils/enums.js +37 -0
- package/dist/utils/enums.js.map +1 -0
- package/dist/utils/patterns.d.ts +11 -0
- package/dist/utils/patterns.d.ts.map +1 -0
- package/dist/utils/patterns.js +11 -0
- package/dist/utils/patterns.js.map +1 -0
- package/package.json +34 -0
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
const CAT = "structure";
|
|
2
|
+
/**
|
|
3
|
+
* Category 2: Structure validation
|
|
4
|
+
* Weight: 15%
|
|
5
|
+
*
|
|
6
|
+
* Validates:
|
|
7
|
+
* - agents/ directory exists with at least 1 .md file
|
|
8
|
+
* - tasks/ directory exists (warning if missing)
|
|
9
|
+
* - workflows/ directory exists (warning if missing)
|
|
10
|
+
* - config/ directory exists (warning if missing)
|
|
11
|
+
* - Each file listed in components.* exists in the FileTree
|
|
12
|
+
*/
|
|
13
|
+
export function validateStructure(fileTree, manifest) {
|
|
14
|
+
const findings = [];
|
|
15
|
+
const dirs = new Set();
|
|
16
|
+
for (const path of fileTree.keys()) {
|
|
17
|
+
const firstSegment = path.split("/")[0];
|
|
18
|
+
if (firstSegment)
|
|
19
|
+
dirs.add(firstSegment);
|
|
20
|
+
}
|
|
21
|
+
// Check agents/ directory
|
|
22
|
+
const agentFiles = [...fileTree.keys()].filter((p) => p.startsWith("agents/") && p.endsWith(".md"));
|
|
23
|
+
if (agentFiles.length === 0) {
|
|
24
|
+
findings.push({
|
|
25
|
+
severity: "error",
|
|
26
|
+
category: CAT,
|
|
27
|
+
message: "agents/ directory must contain at least one .md file",
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
// Optional directories (warnings)
|
|
31
|
+
for (const dir of ["tasks", "workflows", "config"]) {
|
|
32
|
+
const hasFiles = [...fileTree.keys()].some((p) => p.startsWith(`${dir}/`));
|
|
33
|
+
if (!hasFiles) {
|
|
34
|
+
findings.push({
|
|
35
|
+
severity: "warning",
|
|
36
|
+
category: CAT,
|
|
37
|
+
message: `Optional directory ${dir}/ not found`,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// Validate components.* entries exist in FileTree
|
|
42
|
+
if (manifest?.components) {
|
|
43
|
+
const componentSections = [
|
|
44
|
+
["agents", "agents/"],
|
|
45
|
+
["tasks", "tasks/"],
|
|
46
|
+
["workflows", "workflows/"],
|
|
47
|
+
];
|
|
48
|
+
for (const [key, prefix] of componentSections) {
|
|
49
|
+
const list = manifest.components[key];
|
|
50
|
+
if (Array.isArray(list)) {
|
|
51
|
+
for (const item of list) {
|
|
52
|
+
if (typeof item !== "string")
|
|
53
|
+
continue;
|
|
54
|
+
// Try with and without extension
|
|
55
|
+
const candidates = [
|
|
56
|
+
`${prefix}${item}`,
|
|
57
|
+
`${prefix}${item}.md`,
|
|
58
|
+
`${prefix}${item}.yaml`,
|
|
59
|
+
`${prefix}${item}.yml`,
|
|
60
|
+
];
|
|
61
|
+
const found = candidates.some((c) => fileTree.has(c));
|
|
62
|
+
if (!found) {
|
|
63
|
+
findings.push({
|
|
64
|
+
severity: "error",
|
|
65
|
+
category: CAT,
|
|
66
|
+
message: `Component '${key}.${item}' listed in squad.yaml but file not found in ${prefix}`,
|
|
67
|
+
file: "squad.yaml",
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
return findings;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=structure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structure.js","sourceRoot":"","sources":["../../src/categories/structure.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { FileTree, ValidationFinding, ParsedTask } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Category 4: Tasks validation
|
|
4
|
+
* Weight: 15%
|
|
5
|
+
*
|
|
6
|
+
* Validates:
|
|
7
|
+
* - YAML frontmatter exists
|
|
8
|
+
* - task (camelCase with parentheses)
|
|
9
|
+
* - responsavel (non-empty)
|
|
10
|
+
* - responsavel_type in {Agente, Worker, Humano, Clone}
|
|
11
|
+
* - atomic_layer in {Atom, Molecule, Organism, Template, Page}
|
|
12
|
+
* - Entrada (array with at least 1 item, each with 4 fields)
|
|
13
|
+
* - Saida (array with at least 1 item, each with 4 fields)
|
|
14
|
+
* - Checklist (non-empty)
|
|
15
|
+
*/
|
|
16
|
+
export declare function validateTasks(fileTree: FileTree): {
|
|
17
|
+
findings: ValidationFinding[];
|
|
18
|
+
tasks: ParsedTask[];
|
|
19
|
+
};
|
|
20
|
+
//# sourceMappingURL=tasks.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tasks.d.ts","sourceRoot":"","sources":["../../src/categories/tasks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAO3E;;;;;;;;;;;;;GAaG;AACH,wBAAgB,aAAa,CAC3B,QAAQ,EAAE,QAAQ,GACjB;IAAE,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAAC,KAAK,EAAE,UAAU,EAAE,CAAA;CAAE,CAuIxD"}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
import { extractFrontmatter, hasFrontmatter } from "../parser.js";
|
|
2
|
+
import { CAMEL_CASE_PAREN } from "../utils/patterns.js";
|
|
3
|
+
import { ATOMIC_LAYERS, RESPONSAVEL_TYPES } from "../utils/enums.js";
|
|
4
|
+
const CAT = "tasks";
|
|
5
|
+
/**
|
|
6
|
+
* Category 4: Tasks validation
|
|
7
|
+
* Weight: 15%
|
|
8
|
+
*
|
|
9
|
+
* Validates:
|
|
10
|
+
* - YAML frontmatter exists
|
|
11
|
+
* - task (camelCase with parentheses)
|
|
12
|
+
* - responsavel (non-empty)
|
|
13
|
+
* - responsavel_type in {Agente, Worker, Humano, Clone}
|
|
14
|
+
* - atomic_layer in {Atom, Molecule, Organism, Template, Page}
|
|
15
|
+
* - Entrada (array with at least 1 item, each with 4 fields)
|
|
16
|
+
* - Saida (array with at least 1 item, each with 4 fields)
|
|
17
|
+
* - Checklist (non-empty)
|
|
18
|
+
*/
|
|
19
|
+
export function validateTasks(fileTree) {
|
|
20
|
+
const findings = [];
|
|
21
|
+
const tasks = [];
|
|
22
|
+
const taskFiles = [...fileTree.keys()].filter((p) => p.startsWith("tasks/") && p.endsWith(".md"));
|
|
23
|
+
if (taskFiles.length === 0) {
|
|
24
|
+
return { findings, tasks };
|
|
25
|
+
}
|
|
26
|
+
for (const file of taskFiles) {
|
|
27
|
+
const content = fileTree.get(file);
|
|
28
|
+
if (!hasFrontmatter(content)) {
|
|
29
|
+
findings.push({
|
|
30
|
+
severity: "error",
|
|
31
|
+
category: CAT,
|
|
32
|
+
message: "Task file missing YAML frontmatter block (--- ... ---)",
|
|
33
|
+
file,
|
|
34
|
+
});
|
|
35
|
+
tasks.push({ file });
|
|
36
|
+
continue;
|
|
37
|
+
}
|
|
38
|
+
const fm = extractFrontmatter(content);
|
|
39
|
+
if (!fm) {
|
|
40
|
+
findings.push({
|
|
41
|
+
severity: "error",
|
|
42
|
+
category: CAT,
|
|
43
|
+
message: "Task file has invalid YAML in frontmatter",
|
|
44
|
+
file,
|
|
45
|
+
});
|
|
46
|
+
tasks.push({ file });
|
|
47
|
+
continue;
|
|
48
|
+
}
|
|
49
|
+
const parsed = {
|
|
50
|
+
file,
|
|
51
|
+
task: fm["task"],
|
|
52
|
+
responsavel: fm["responsavel"],
|
|
53
|
+
responsavel_type: fm["responsavel_type"],
|
|
54
|
+
atomic_layer: fm["atomic_layer"],
|
|
55
|
+
Entrada: fm["Entrada"],
|
|
56
|
+
Saida: fm["Saida"],
|
|
57
|
+
Checklist: fm["Checklist"],
|
|
58
|
+
};
|
|
59
|
+
tasks.push(parsed);
|
|
60
|
+
// task identifier
|
|
61
|
+
if (!parsed.task) {
|
|
62
|
+
findings.push({
|
|
63
|
+
severity: "error",
|
|
64
|
+
category: CAT,
|
|
65
|
+
message: "Missing 'task' field",
|
|
66
|
+
file,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
else if (typeof parsed.task === "string" && !CAMEL_CASE_PAREN.test(parsed.task)) {
|
|
70
|
+
findings.push({
|
|
71
|
+
severity: "warning",
|
|
72
|
+
category: CAT,
|
|
73
|
+
message: `'task' should be camelCase with parentheses (e.g. myTask()), got: "${parsed.task}"`,
|
|
74
|
+
file,
|
|
75
|
+
});
|
|
76
|
+
}
|
|
77
|
+
// responsavel
|
|
78
|
+
if (!parsed.responsavel) {
|
|
79
|
+
findings.push({
|
|
80
|
+
severity: "error",
|
|
81
|
+
category: CAT,
|
|
82
|
+
message: "Missing 'responsavel' field",
|
|
83
|
+
file,
|
|
84
|
+
});
|
|
85
|
+
}
|
|
86
|
+
// responsavel_type
|
|
87
|
+
if (!parsed.responsavel_type) {
|
|
88
|
+
findings.push({
|
|
89
|
+
severity: "error",
|
|
90
|
+
category: CAT,
|
|
91
|
+
message: "Missing 'responsavel_type' field",
|
|
92
|
+
file,
|
|
93
|
+
});
|
|
94
|
+
}
|
|
95
|
+
else if (typeof parsed.responsavel_type === "string" &&
|
|
96
|
+
!RESPONSAVEL_TYPES.has(parsed.responsavel_type)) {
|
|
97
|
+
findings.push({
|
|
98
|
+
severity: "error",
|
|
99
|
+
category: CAT,
|
|
100
|
+
message: `'responsavel_type' must be one of {${[...RESPONSAVEL_TYPES].join(", ")}}, got: "${parsed.responsavel_type}"`,
|
|
101
|
+
file,
|
|
102
|
+
});
|
|
103
|
+
}
|
|
104
|
+
// atomic_layer
|
|
105
|
+
if (!parsed.atomic_layer) {
|
|
106
|
+
findings.push({
|
|
107
|
+
severity: "warning",
|
|
108
|
+
category: CAT,
|
|
109
|
+
message: "Missing 'atomic_layer' field",
|
|
110
|
+
file,
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
else if (typeof parsed.atomic_layer === "string" &&
|
|
114
|
+
!ATOMIC_LAYERS.has(parsed.atomic_layer)) {
|
|
115
|
+
findings.push({
|
|
116
|
+
severity: "warning",
|
|
117
|
+
category: CAT,
|
|
118
|
+
message: `'atomic_layer' should be one of {${[...ATOMIC_LAYERS].join(", ")}}, got: "${parsed.atomic_layer}"`,
|
|
119
|
+
file,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
// Entrada
|
|
123
|
+
validateContractArray(parsed.Entrada, "Entrada", file, findings);
|
|
124
|
+
// Saida
|
|
125
|
+
validateContractArray(parsed.Saida, "Saida", file, findings);
|
|
126
|
+
// Checklist
|
|
127
|
+
if (!parsed.Checklist) {
|
|
128
|
+
findings.push({
|
|
129
|
+
severity: "error",
|
|
130
|
+
category: CAT,
|
|
131
|
+
message: "Missing 'Checklist' field",
|
|
132
|
+
file,
|
|
133
|
+
});
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return { findings, tasks };
|
|
137
|
+
}
|
|
138
|
+
function validateContractArray(arr, name, file, findings) {
|
|
139
|
+
if (!arr || !Array.isArray(arr)) {
|
|
140
|
+
findings.push({
|
|
141
|
+
severity: "error",
|
|
142
|
+
category: CAT,
|
|
143
|
+
message: `Missing '${name}' field (must be an array)`,
|
|
144
|
+
file,
|
|
145
|
+
});
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
if (arr.length === 0) {
|
|
149
|
+
findings.push({
|
|
150
|
+
severity: "error",
|
|
151
|
+
category: CAT,
|
|
152
|
+
message: `'${name}' must have at least 1 item`,
|
|
153
|
+
file,
|
|
154
|
+
});
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
// Required fields with accepted aliases
|
|
158
|
+
const fieldChecks = [
|
|
159
|
+
{ canonical: "nome", aliases: ["nome", "campo"] },
|
|
160
|
+
{ canonical: "tipo", aliases: ["tipo"] },
|
|
161
|
+
{ canonical: "obrigatorio", aliases: ["obrigatorio", "persistido"] },
|
|
162
|
+
];
|
|
163
|
+
// "descricao" is recommended but optional — aliases: origen, destino
|
|
164
|
+
const optionalWithAliases = [
|
|
165
|
+
{ canonical: "descricao", aliases: ["descricao", "origen", "destino"] },
|
|
166
|
+
];
|
|
167
|
+
for (let i = 0; i < arr.length; i++) {
|
|
168
|
+
const item = arr[i];
|
|
169
|
+
if (!item || typeof item !== "object") {
|
|
170
|
+
findings.push({
|
|
171
|
+
severity: "warning",
|
|
172
|
+
category: CAT,
|
|
173
|
+
message: `'${name}[${i}]' is not a valid object`,
|
|
174
|
+
file,
|
|
175
|
+
});
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
const keys = Object.keys(item);
|
|
179
|
+
// Check required fields (with alias support)
|
|
180
|
+
const missing = fieldChecks
|
|
181
|
+
.filter((check) => !check.aliases.some((alias) => keys.includes(alias)))
|
|
182
|
+
.map((check) => check.canonical);
|
|
183
|
+
if (missing.length > 0) {
|
|
184
|
+
findings.push({
|
|
185
|
+
severity: "warning",
|
|
186
|
+
category: CAT,
|
|
187
|
+
message: `'${name}[${i}]' missing fields: ${missing.join(", ")}`,
|
|
188
|
+
file,
|
|
189
|
+
});
|
|
190
|
+
}
|
|
191
|
+
// Check optional fields (just a hint, no finding)
|
|
192
|
+
// "descricao" or its aliases (origen/destino) are recommended but not required
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
//# sourceMappingURL=tasks.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tasks.js","sourceRoot":"","sources":["../../src/categories/tasks.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { FileTree, ValidationFinding, ParsedWorkflow } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Category 5: Workflows validation
|
|
4
|
+
* Weight: 10%
|
|
5
|
+
*
|
|
6
|
+
* Validates:
|
|
7
|
+
* - Valid YAML
|
|
8
|
+
* - workflow_name (snake_case)
|
|
9
|
+
* - description (non-empty)
|
|
10
|
+
* - agent_sequence (array with at least 1 entry)
|
|
11
|
+
* - success_indicators (array with at least 1 entry)
|
|
12
|
+
*/
|
|
13
|
+
export declare function validateWorkflows(fileTree: FileTree): {
|
|
14
|
+
findings: ValidationFinding[];
|
|
15
|
+
workflows: ParsedWorkflow[];
|
|
16
|
+
};
|
|
17
|
+
//# sourceMappingURL=workflows.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflows.d.ts","sourceRoot":"","sources":["../../src/categories/workflows.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAM/E;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,QAAQ,GACjB;IAAE,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAAC,SAAS,EAAE,cAAc,EAAE,CAAA;CAAE,CAuHhE"}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
import { parseYaml } from "../parser.js";
|
|
2
|
+
import { SNAKE_CASE } from "../utils/patterns.js";
|
|
3
|
+
const CAT = "workflows";
|
|
4
|
+
/**
|
|
5
|
+
* Category 5: Workflows validation
|
|
6
|
+
* Weight: 10%
|
|
7
|
+
*
|
|
8
|
+
* Validates:
|
|
9
|
+
* - Valid YAML
|
|
10
|
+
* - workflow_name (snake_case)
|
|
11
|
+
* - description (non-empty)
|
|
12
|
+
* - agent_sequence (array with at least 1 entry)
|
|
13
|
+
* - success_indicators (array with at least 1 entry)
|
|
14
|
+
*/
|
|
15
|
+
export function validateWorkflows(fileTree) {
|
|
16
|
+
const findings = [];
|
|
17
|
+
const workflows = [];
|
|
18
|
+
const workflowFiles = [...fileTree.keys()].filter((p) => p.startsWith("workflows/") &&
|
|
19
|
+
(p.endsWith(".yaml") || p.endsWith(".yml")));
|
|
20
|
+
if (workflowFiles.length === 0) {
|
|
21
|
+
return { findings, workflows };
|
|
22
|
+
}
|
|
23
|
+
for (const file of workflowFiles) {
|
|
24
|
+
const content = fileTree.get(file);
|
|
25
|
+
const parsed = parseYaml(content);
|
|
26
|
+
if (!parsed) {
|
|
27
|
+
findings.push({
|
|
28
|
+
severity: "error",
|
|
29
|
+
category: CAT,
|
|
30
|
+
message: "Workflow file contains invalid YAML",
|
|
31
|
+
file,
|
|
32
|
+
});
|
|
33
|
+
workflows.push({ file });
|
|
34
|
+
continue;
|
|
35
|
+
}
|
|
36
|
+
const wf = {
|
|
37
|
+
file,
|
|
38
|
+
workflow_name: parsed["workflow_name"],
|
|
39
|
+
description: parsed["description"],
|
|
40
|
+
agent_sequence: parsed["agent_sequence"],
|
|
41
|
+
success_indicators: parsed["success_indicators"],
|
|
42
|
+
transitions: parsed["transitions"],
|
|
43
|
+
};
|
|
44
|
+
workflows.push(wf);
|
|
45
|
+
// workflow_name
|
|
46
|
+
if (!wf.workflow_name) {
|
|
47
|
+
findings.push({
|
|
48
|
+
severity: "error",
|
|
49
|
+
category: CAT,
|
|
50
|
+
message: "Missing 'workflow_name' field",
|
|
51
|
+
file,
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
else if (typeof wf.workflow_name === "string" &&
|
|
55
|
+
!SNAKE_CASE.test(wf.workflow_name)) {
|
|
56
|
+
findings.push({
|
|
57
|
+
severity: "warning",
|
|
58
|
+
category: CAT,
|
|
59
|
+
message: `'workflow_name' should be snake_case, got: "${wf.workflow_name}"`,
|
|
60
|
+
file,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
// description
|
|
64
|
+
if (!wf.description) {
|
|
65
|
+
findings.push({
|
|
66
|
+
severity: "warning",
|
|
67
|
+
category: CAT,
|
|
68
|
+
message: "Missing 'description' field",
|
|
69
|
+
file,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
// agent_sequence
|
|
73
|
+
if (!wf.agent_sequence) {
|
|
74
|
+
findings.push({
|
|
75
|
+
severity: "error",
|
|
76
|
+
category: CAT,
|
|
77
|
+
message: "Missing 'agent_sequence' field",
|
|
78
|
+
file,
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
else if (!Array.isArray(wf.agent_sequence)) {
|
|
82
|
+
findings.push({
|
|
83
|
+
severity: "error",
|
|
84
|
+
category: CAT,
|
|
85
|
+
message: "'agent_sequence' must be an array",
|
|
86
|
+
file,
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
else if (wf.agent_sequence.length === 0) {
|
|
90
|
+
findings.push({
|
|
91
|
+
severity: "error",
|
|
92
|
+
category: CAT,
|
|
93
|
+
message: "'agent_sequence' must have at least 1 entry",
|
|
94
|
+
file,
|
|
95
|
+
});
|
|
96
|
+
}
|
|
97
|
+
// success_indicators
|
|
98
|
+
if (!wf.success_indicators) {
|
|
99
|
+
findings.push({
|
|
100
|
+
severity: "error",
|
|
101
|
+
category: CAT,
|
|
102
|
+
message: "Missing 'success_indicators' field",
|
|
103
|
+
file,
|
|
104
|
+
});
|
|
105
|
+
}
|
|
106
|
+
else if (!Array.isArray(wf.success_indicators)) {
|
|
107
|
+
findings.push({
|
|
108
|
+
severity: "error",
|
|
109
|
+
category: CAT,
|
|
110
|
+
message: "'success_indicators' must be an array",
|
|
111
|
+
file,
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
else if (wf.success_indicators.length === 0) {
|
|
115
|
+
findings.push({
|
|
116
|
+
severity: "error",
|
|
117
|
+
category: CAT,
|
|
118
|
+
message: "'success_indicators' must have at least 1 entry",
|
|
119
|
+
file,
|
|
120
|
+
});
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
return { findings, workflows };
|
|
124
|
+
}
|
|
125
|
+
//# sourceMappingURL=workflows.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"workflows.js","sourceRoot":"","sources":["../../src/categories/workflows.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { FileTree, ValidationReport } from "./types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Validate an AIOS Squad from a FileTree.
|
|
4
|
+
*
|
|
5
|
+
* The FileTree is a Map<string, string> of relative paths to file contents.
|
|
6
|
+
* The validator is isomorphic — it works in Node.js, Deno, browsers, etc.
|
|
7
|
+
*
|
|
8
|
+
* @param fileTree Map of relative file paths to their contents
|
|
9
|
+
* @returns ValidationReport with score, status, and per-category results
|
|
10
|
+
*/
|
|
11
|
+
export declare function validateSquad(fileTree: FileTree): ValidationReport;
|
|
12
|
+
export type { FileTree, ValidationReport, ValidationStatus, ValidationFinding, CategoryResult, CategoryName, Severity, ParsedManifest, ParsedAgent, ParsedTask, ParsedWorkflow, } from "./types.js";
|
|
13
|
+
export { CATEGORY_WEIGHTS, CATEGORY_LABELS } from "./types.js";
|
|
14
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EACR,gBAAgB,EAGjB,MAAM,YAAY,CAAC;AAcpB;;;;;;;;GAQG;AACH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,QAAQ,GAAG,gBAAgB,CA0DlE;AAGD,YAAY,EACV,QAAQ,EACR,gBAAgB,EAChB,gBAAgB,EAChB,iBAAiB,EACjB,cAAc,EACd,YAAY,EACZ,QAAQ,EACR,cAAc,EACd,WAAW,EACX,UAAU,EACV,cAAc,GACf,MAAM,YAAY,CAAC;AAEpB,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import { validateManifest } from "./categories/manifest.js";
|
|
2
|
+
import { validateStructure } from "./categories/structure.js";
|
|
3
|
+
import { validateAgents } from "./categories/agents.js";
|
|
4
|
+
import { validateTasks } from "./categories/tasks.js";
|
|
5
|
+
import { validateWorkflows } from "./categories/workflows.js";
|
|
6
|
+
import { validateCrossReferences } from "./categories/cross-refs.js";
|
|
7
|
+
import { buildCategoryResult, computeWeightedScore, computeStatus, } from "./scoring.js";
|
|
8
|
+
/**
|
|
9
|
+
* Validate an AIOS Squad from a FileTree.
|
|
10
|
+
*
|
|
11
|
+
* The FileTree is a Map<string, string> of relative paths to file contents.
|
|
12
|
+
* The validator is isomorphic — it works in Node.js, Deno, browsers, etc.
|
|
13
|
+
*
|
|
14
|
+
* @param fileTree Map of relative file paths to their contents
|
|
15
|
+
* @returns ValidationReport with score, status, and per-category results
|
|
16
|
+
*/
|
|
17
|
+
export function validateSquad(fileTree) {
|
|
18
|
+
const allFindings = [];
|
|
19
|
+
// Category 1: Manifest
|
|
20
|
+
const { findings: manifestFindings, parsed: manifest } = validateManifest(fileTree);
|
|
21
|
+
allFindings.push(...manifestFindings);
|
|
22
|
+
// Category 2: Structure
|
|
23
|
+
const structureFindings = validateStructure(fileTree, manifest);
|
|
24
|
+
allFindings.push(...structureFindings);
|
|
25
|
+
// Category 3: Agents
|
|
26
|
+
const { findings: agentFindings, agents } = validateAgents(fileTree);
|
|
27
|
+
allFindings.push(...agentFindings);
|
|
28
|
+
// Category 4: Tasks
|
|
29
|
+
const { findings: taskFindings, tasks } = validateTasks(fileTree);
|
|
30
|
+
allFindings.push(...taskFindings);
|
|
31
|
+
// Category 5: Workflows
|
|
32
|
+
const { findings: workflowFindings, workflows } = validateWorkflows(fileTree);
|
|
33
|
+
allFindings.push(...workflowFindings);
|
|
34
|
+
// Category 6: Cross-references
|
|
35
|
+
const crossRefFindings = validateCrossReferences(agents, tasks, workflows);
|
|
36
|
+
allFindings.push(...crossRefFindings);
|
|
37
|
+
// Build category results
|
|
38
|
+
const categoryNames = [
|
|
39
|
+
"manifest",
|
|
40
|
+
"structure",
|
|
41
|
+
"agents",
|
|
42
|
+
"tasks",
|
|
43
|
+
"workflows",
|
|
44
|
+
"cross-refs",
|
|
45
|
+
];
|
|
46
|
+
const categories = categoryNames.map((name) => buildCategoryResult(name, allFindings));
|
|
47
|
+
const totalErrors = allFindings.filter((f) => f.severity === "error").length;
|
|
48
|
+
const totalWarnings = allFindings.filter((f) => f.severity === "warning").length;
|
|
49
|
+
return {
|
|
50
|
+
score: computeWeightedScore(categories),
|
|
51
|
+
status: computeStatus(totalErrors, totalWarnings),
|
|
52
|
+
categories,
|
|
53
|
+
findings: allFindings,
|
|
54
|
+
summary: {
|
|
55
|
+
totalErrors,
|
|
56
|
+
totalWarnings,
|
|
57
|
+
fileCount: fileTree.size,
|
|
58
|
+
},
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
export { CATEGORY_WEIGHTS, CATEGORY_LABELS } from "./types.js";
|
|
62
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAOA,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,MAAM,uBAAuB,CAAC;AACtD,OAAO,EAAE,iBAAiB,EAAE,MAAM,2BAA2B,CAAC;AAC9D,OAAO,EAAE,uBAAuB,EAAE,MAAM,4BAA4B,CAAC;AACrE,OAAO,EACL,mBAAmB,EACnB,oBAAoB,EACpB,aAAa,GACd,MAAM,cAAc,CAAC;AAEtB;;;;;;;;GAQG;AACH,MAAM,UAAU,aAAa,CAAC,QAAkB;IAC9C,MAAM,WAAW,GAAwB,EAAE,CAAC;IAE5C,uBAAuB;IACvB,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE,MAAM,EAAE,QAAQ,EAAE,GACpD,gBAAgB,CAAC,QAAQ,CAAC,CAAC;IAC7B,WAAW,CAAC,IAAI,CAAC,GAAG,gBAAgB,CAAC,CAAC;IAEtC,wBAAwB;IACxB,MAAM,iBAAiB,GAAG,iBAAiB,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAChE,WAAW,CAAC,IAAI,CAAC,GAAG,iBAAiB,CAAC,CAAC;IAEvC,qBAAqB;IACrB,MAAM,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,GAAG,cAAc,CAAC,QAAQ,CAAC,CAAC;IACrE,WAAW,CAAC,IAAI,CAAC,GAAG,aAAa,CAAC,CAAC;IAEnC,oBAAoB;IACpB,MAAM,EAAE,QAAQ,EAAE,YAAY,EAAE,KAAK,EAAE,GAAG,aAAa,CAAC,QAAQ,CAAC,CAAC;IAClE,WAAW,CAAC,IAAI,CAAC,GAAG,YAAY,CAAC,CAAC;IAElC,wBAAwB;IACxB,MAAM,EAAE,QAAQ,EAAE,gBAAgB,EAAE,SAAS,EAAE,GAAG,iBAAiB,CAAC,QAAQ,CAAC,CAAC;IAC9E,WAAW,CAAC,IAAI,CAAC,GAAG,gBAAgB,CAAC,CAAC;IAEtC,+BAA+B;IAC/B,MAAM,gBAAgB,GAAG,uBAAuB,CAAC,MAAM,EAAE,KAAK,EAAE,SAAS,CAAC,CAAC;IAC3E,WAAW,CAAC,IAAI,CAAC,GAAG,gBAAgB,CAAC,CAAC;IAEtC,yBAAyB;IACzB,MAAM,aAAa,GAAmB;QACpC,UAAU;QACV,WAAW;QACX,QAAQ;QACR,OAAO;QACP,WAAW;QACX,YAAY;KACb,CAAC;IAEF,MAAM,UAAU,GAAG,aAAa,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAC5C,mBAAmB,CAAC,IAAI,EAAE,WAAW,CAAC,CACvC,CAAC;IAEF,MAAM,WAAW,GAAG,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,OAAO,CAAC,CAAC,MAAM,CAAC;IAC7E,MAAM,aAAa,GAAG,WAAW,CAAC,MAAM,CACtC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,KAAK,SAAS,CAChC,CAAC,MAAM,CAAC;IAET,OAAO;QACL,KAAK,EAAE,oBAAoB,CAAC,UAAU,CAAC;QACvC,MAAM,EAAE,aAAa,CAAC,WAAW,EAAE,aAAa,CAAC;QACjD,UAAU;QACV,QAAQ,EAAE,WAAW;QACrB,OAAO,EAAE;YACP,WAAW;YACX,aAAa;YACb,SAAS,EAAE,QAAQ,CAAC,IAAI;SACzB;KACF,CAAC;AACJ,CAAC;AAiBD,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,YAAY,CAAC"}
|
package/dist/parser.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Extract YAML frontmatter from a Markdown file.
|
|
3
|
+
* Returns the parsed object or null if no frontmatter found.
|
|
4
|
+
*/
|
|
5
|
+
export declare function extractFrontmatter(content: string): Record<string, unknown> | null;
|
|
6
|
+
/**
|
|
7
|
+
* Parse a YAML string into an object.
|
|
8
|
+
* Returns null on parse failure.
|
|
9
|
+
*/
|
|
10
|
+
export declare function parseYaml(content: string): Record<string, unknown> | null;
|
|
11
|
+
/**
|
|
12
|
+
* Deep get a nested value by dot-separated path.
|
|
13
|
+
* e.g. getNestedValue(obj, "aios.minVersion")
|
|
14
|
+
*/
|
|
15
|
+
export declare function getNestedValue(obj: Record<string, unknown>, path: string): unknown;
|
|
16
|
+
/**
|
|
17
|
+
* Check if content has YAML frontmatter block.
|
|
18
|
+
*/
|
|
19
|
+
export declare function hasFrontmatter(content: string): boolean;
|
|
20
|
+
//# sourceMappingURL=parser.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.d.ts","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAGA;;;GAGG;AACH,wBAAgB,kBAAkB,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAalF;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,GAAG,IAAI,CAUzE;AAED;;;GAGG;AACH,wBAAgB,cAAc,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,IAAI,EAAE,MAAM,GAAG,OAAO,CAQlF;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAEvD"}
|
package/dist/parser.js
ADDED
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import yaml from "js-yaml";
|
|
2
|
+
import { FRONTMATTER_BLOCK } from "./utils/patterns.js";
|
|
3
|
+
/**
|
|
4
|
+
* Extract YAML frontmatter from a Markdown file.
|
|
5
|
+
* Returns the parsed object or null if no frontmatter found.
|
|
6
|
+
*/
|
|
7
|
+
export function extractFrontmatter(content) {
|
|
8
|
+
const match = content.match(FRONTMATTER_BLOCK);
|
|
9
|
+
if (!match || !match[1])
|
|
10
|
+
return null;
|
|
11
|
+
try {
|
|
12
|
+
const parsed = yaml.load(match[1]);
|
|
13
|
+
if (parsed && typeof parsed === "object") {
|
|
14
|
+
return parsed;
|
|
15
|
+
}
|
|
16
|
+
return null;
|
|
17
|
+
}
|
|
18
|
+
catch {
|
|
19
|
+
return null;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Parse a YAML string into an object.
|
|
24
|
+
* Returns null on parse failure.
|
|
25
|
+
*/
|
|
26
|
+
export function parseYaml(content) {
|
|
27
|
+
try {
|
|
28
|
+
const parsed = yaml.load(content);
|
|
29
|
+
if (parsed && typeof parsed === "object") {
|
|
30
|
+
return parsed;
|
|
31
|
+
}
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
catch {
|
|
35
|
+
return null;
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Deep get a nested value by dot-separated path.
|
|
40
|
+
* e.g. getNestedValue(obj, "aios.minVersion")
|
|
41
|
+
*/
|
|
42
|
+
export function getNestedValue(obj, path) {
|
|
43
|
+
const keys = path.split(".");
|
|
44
|
+
let current = obj;
|
|
45
|
+
for (const key of keys) {
|
|
46
|
+
if (current == null || typeof current !== "object")
|
|
47
|
+
return undefined;
|
|
48
|
+
current = current[key];
|
|
49
|
+
}
|
|
50
|
+
return current;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Check if content has YAML frontmatter block.
|
|
54
|
+
*/
|
|
55
|
+
export function hasFrontmatter(content) {
|
|
56
|
+
return FRONTMATTER_BLOCK.test(content);
|
|
57
|
+
}
|
|
58
|
+
//# sourceMappingURL=parser.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"parser.js","sourceRoot":"","sources":["../src/parser.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,SAAS,CAAC;AAC3B,OAAO,EAAE,iBAAiB,EAAE,MAAM,qBAAqB,CAAC;AAExD;;;GAGG;AACH,MAAM,UAAU,kBAAkB,CAAC,OAAe;IAChD,MAAM,KAAK,GAAG,OAAO,CAAC,KAAK,CAAC,iBAAiB,CAAC,CAAC;IAC/C,IAAI,CAAC,KAAK,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC;QAAE,OAAO,IAAI,CAAC;IAErC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC;QACnC,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YACzC,OAAO,MAAiC,CAAC;QAC3C,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,SAAS,CAAC,OAAe;IACvC,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QAClC,IAAI,MAAM,IAAI,OAAO,MAAM,KAAK,QAAQ,EAAE,CAAC;YACzC,OAAO,MAAiC,CAAC;QAC3C,CAAC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAC;IACd,CAAC;AACH,CAAC;AAED;;;GAGG;AACH,MAAM,UAAU,cAAc,CAAC,GAA4B,EAAE,IAAY;IACvE,MAAM,IAAI,GAAG,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAC7B,IAAI,OAAO,GAAY,GAAG,CAAC;IAC3B,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;QACvB,IAAI,OAAO,IAAI,IAAI,IAAI,OAAO,OAAO,KAAK,QAAQ;YAAE,OAAO,SAAS,CAAC;QACrE,OAAO,GAAI,OAAmC,CAAC,GAAG,CAAC,CAAC;IACtD,CAAC;IACD,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;GAEG;AACH,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,OAAO,iBAAiB,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AACzC,CAAC"}
|