@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,11 @@
|
|
|
1
|
+
import type { FileTree } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Build a FileTree from a local filesystem directory.
|
|
4
|
+
* Recursively reads all files and returns a Map<relativePath, content>.
|
|
5
|
+
* Only reads text files (.yaml, .yml, .md, .json).
|
|
6
|
+
*
|
|
7
|
+
* @param dirPath Absolute path to the squad directory
|
|
8
|
+
* @returns FileTree ready for validateSquad()
|
|
9
|
+
*/
|
|
10
|
+
export declare function buildFileTreeFromFS(dirPath: string): Promise<FileTree>;
|
|
11
|
+
//# sourceMappingURL=filesystem.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filesystem.d.ts","sourceRoot":"","sources":["../../src/adapters/filesystem.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,aAAa,CAAC;AAE5C;;;;;;;GAOG;AACH,wBAAsB,mBAAmB,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAI5E"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { readFile, readdir } from "node:fs/promises";
|
|
2
|
+
import { join, relative } from "node:path";
|
|
3
|
+
/**
|
|
4
|
+
* Build a FileTree from a local filesystem directory.
|
|
5
|
+
* Recursively reads all files and returns a Map<relativePath, content>.
|
|
6
|
+
* Only reads text files (.yaml, .yml, .md, .json).
|
|
7
|
+
*
|
|
8
|
+
* @param dirPath Absolute path to the squad directory
|
|
9
|
+
* @returns FileTree ready for validateSquad()
|
|
10
|
+
*/
|
|
11
|
+
export async function buildFileTreeFromFS(dirPath) {
|
|
12
|
+
const fileTree = new Map();
|
|
13
|
+
await walkDir(dirPath, dirPath, fileTree);
|
|
14
|
+
return fileTree;
|
|
15
|
+
}
|
|
16
|
+
const TEXT_EXTENSIONS = new Set([".yaml", ".yml", ".md", ".json", ".txt"]);
|
|
17
|
+
async function walkDir(basePath, currentPath, fileTree) {
|
|
18
|
+
const entries = await readdir(currentPath, { withFileTypes: true });
|
|
19
|
+
for (const entry of entries) {
|
|
20
|
+
const fullPath = join(currentPath, entry.name);
|
|
21
|
+
if (entry.isDirectory()) {
|
|
22
|
+
// Skip hidden directories and node_modules
|
|
23
|
+
if (entry.name.startsWith(".") || entry.name === "node_modules")
|
|
24
|
+
continue;
|
|
25
|
+
await walkDir(basePath, fullPath, fileTree);
|
|
26
|
+
}
|
|
27
|
+
else if (entry.isFile()) {
|
|
28
|
+
const ext = entry.name.substring(entry.name.lastIndexOf("."));
|
|
29
|
+
if (!TEXT_EXTENSIONS.has(ext))
|
|
30
|
+
continue;
|
|
31
|
+
const relativePath = relative(basePath, fullPath);
|
|
32
|
+
const content = await readFile(fullPath, "utf-8");
|
|
33
|
+
fileTree.set(relativePath, content);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
//# sourceMappingURL=filesystem.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"filesystem.js","sourceRoot":"","sources":["../../src/adapters/filesystem.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,OAAO,EAAQ,MAAM,kBAAkB,CAAC;AAC3D,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,MAAM,WAAW,CAAC;AAG3C;;;;;;;GAOG;AACH,MAAM,CAAC,KAAK,UAAU,mBAAmB,CAAC,OAAe;IACvD,MAAM,QAAQ,GAAa,IAAI,GAAG,EAAE,CAAC;IACrC,MAAM,OAAO,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC1C,OAAO,QAAQ,CAAC;AAClB,CAAC;AAED,MAAM,eAAe,GAAG,IAAI,GAAG,CAAC,CAAC,OAAO,EAAE,MAAM,EAAE,KAAK,EAAE,OAAO,EAAE,MAAM,CAAC,CAAC,CAAC;AAE3E,KAAK,UAAU,OAAO,CACpB,QAAgB,EAChB,WAAmB,EACnB,QAAkB;IAElB,MAAM,OAAO,GAAG,MAAM,OAAO,CAAC,WAAW,EAAE,EAAE,aAAa,EAAE,IAAI,EAAE,CAAC,CAAC;IAEpE,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC5B,MAAM,QAAQ,GAAG,IAAI,CAAC,WAAW,EAAE,KAAK,CAAC,IAAI,CAAC,CAAC;QAE/C,IAAI,KAAK,CAAC,WAAW,EAAE,EAAE,CAAC;YACxB,2CAA2C;YAC3C,IAAI,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,cAAc;gBAAE,SAAS;YAC1E,MAAM,OAAO,CAAC,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;QAC9C,CAAC;aAAM,IAAI,KAAK,CAAC,MAAM,EAAE,EAAE,CAAC;YAC1B,MAAM,GAAG,GAAG,KAAK,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;YAC9D,IAAI,CAAC,eAAe,CAAC,GAAG,CAAC,GAAG,CAAC;gBAAE,SAAS;YAExC,MAAM,YAAY,GAAG,QAAQ,CAAC,QAAQ,EAAE,QAAQ,CAAC,CAAC;YAClD,MAAM,OAAO,GAAG,MAAM,QAAQ,CAAC,QAAQ,EAAE,OAAO,CAAC,CAAC;YAClD,QAAQ,CAAC,GAAG,CAAC,YAAY,EAAE,OAAO,CAAC,CAAC;QACtC,CAAC;IACH,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { FileTree, ValidationFinding, ParsedAgent } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Category 3: Agents validation
|
|
4
|
+
* Weight: 20%
|
|
5
|
+
*
|
|
6
|
+
* Validates:
|
|
7
|
+
* - YAML frontmatter exists
|
|
8
|
+
* - agent.id (kebab-case)
|
|
9
|
+
* - agent.name (non-empty)
|
|
10
|
+
* - agent.title (non-empty)
|
|
11
|
+
* - agent.icon (non-empty)
|
|
12
|
+
* - agent.whenToUse (non-empty)
|
|
13
|
+
* - persona_profile.archetype in {Builder, Guardian, Balancer, Flow_Master}
|
|
14
|
+
* - persona_profile.communication.tone (non-empty, known value)
|
|
15
|
+
* - greeting_levels has at least 3 keys
|
|
16
|
+
*/
|
|
17
|
+
export declare function validateAgents(fileTree: FileTree): {
|
|
18
|
+
findings: ValidationFinding[];
|
|
19
|
+
agents: ParsedAgent[];
|
|
20
|
+
};
|
|
21
|
+
//# sourceMappingURL=agents.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agents.d.ts","sourceRoot":"","sources":["../../src/categories/agents.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAC;AAO5E;;;;;;;;;;;;;;GAcG;AACH,wBAAgB,cAAc,CAC5B,QAAQ,EAAE,QAAQ,GACjB;IAAE,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAAC,MAAM,EAAE,WAAW,EAAE,CAAA;CAAE,CAoN1D"}
|
|
@@ -0,0 +1,214 @@
|
|
|
1
|
+
import { extractFrontmatter, hasFrontmatter } from "../parser.js";
|
|
2
|
+
import { KEBAB_CASE } from "../utils/patterns.js";
|
|
3
|
+
import { ARCHETYPES, TONES } from "../utils/enums.js";
|
|
4
|
+
const CAT = "agents";
|
|
5
|
+
/**
|
|
6
|
+
* Category 3: Agents validation
|
|
7
|
+
* Weight: 20%
|
|
8
|
+
*
|
|
9
|
+
* Validates:
|
|
10
|
+
* - YAML frontmatter exists
|
|
11
|
+
* - agent.id (kebab-case)
|
|
12
|
+
* - agent.name (non-empty)
|
|
13
|
+
* - agent.title (non-empty)
|
|
14
|
+
* - agent.icon (non-empty)
|
|
15
|
+
* - agent.whenToUse (non-empty)
|
|
16
|
+
* - persona_profile.archetype in {Builder, Guardian, Balancer, Flow_Master}
|
|
17
|
+
* - persona_profile.communication.tone (non-empty, known value)
|
|
18
|
+
* - greeting_levels has at least 3 keys
|
|
19
|
+
*/
|
|
20
|
+
export function validateAgents(fileTree) {
|
|
21
|
+
const findings = [];
|
|
22
|
+
const agents = [];
|
|
23
|
+
const agentFiles = [...fileTree.keys()].filter((p) => p.startsWith("agents/") && p.endsWith(".md"));
|
|
24
|
+
if (agentFiles.length === 0) {
|
|
25
|
+
return { findings, agents };
|
|
26
|
+
}
|
|
27
|
+
for (const file of agentFiles) {
|
|
28
|
+
const content = fileTree.get(file);
|
|
29
|
+
if (!hasFrontmatter(content)) {
|
|
30
|
+
findings.push({
|
|
31
|
+
severity: "error",
|
|
32
|
+
category: CAT,
|
|
33
|
+
message: "Agent file missing YAML frontmatter block (--- ... ---)",
|
|
34
|
+
file,
|
|
35
|
+
});
|
|
36
|
+
agents.push({ file });
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
const fm = extractFrontmatter(content);
|
|
40
|
+
if (!fm) {
|
|
41
|
+
findings.push({
|
|
42
|
+
severity: "error",
|
|
43
|
+
category: CAT,
|
|
44
|
+
message: "Agent file has invalid YAML in frontmatter",
|
|
45
|
+
file,
|
|
46
|
+
});
|
|
47
|
+
agents.push({ file });
|
|
48
|
+
continue;
|
|
49
|
+
}
|
|
50
|
+
const agentBlock = fm["agent"];
|
|
51
|
+
const personaBlock = fm["persona_profile"];
|
|
52
|
+
// Support greeting_levels as top-level OR nested inside persona_profile.communication
|
|
53
|
+
const greetingBlock = (fm["greeting_levels"]
|
|
54
|
+
?? personaBlock?.["communication"]?.["greeting_levels"]);
|
|
55
|
+
const parsed = {
|
|
56
|
+
file,
|
|
57
|
+
agent: agentBlock
|
|
58
|
+
? {
|
|
59
|
+
name: agentBlock["name"],
|
|
60
|
+
id: agentBlock["id"],
|
|
61
|
+
title: agentBlock["title"],
|
|
62
|
+
icon: agentBlock["icon"],
|
|
63
|
+
whenToUse: agentBlock["whenToUse"],
|
|
64
|
+
}
|
|
65
|
+
: undefined,
|
|
66
|
+
persona_profile: personaBlock
|
|
67
|
+
? {
|
|
68
|
+
archetype: personaBlock["archetype"],
|
|
69
|
+
communication: personaBlock["communication"]
|
|
70
|
+
? {
|
|
71
|
+
tone: personaBlock["communication"]["tone"],
|
|
72
|
+
}
|
|
73
|
+
: undefined,
|
|
74
|
+
}
|
|
75
|
+
: undefined,
|
|
76
|
+
greeting_levels: greetingBlock,
|
|
77
|
+
};
|
|
78
|
+
agents.push(parsed);
|
|
79
|
+
// Validate agent block
|
|
80
|
+
if (!agentBlock) {
|
|
81
|
+
findings.push({
|
|
82
|
+
severity: "error",
|
|
83
|
+
category: CAT,
|
|
84
|
+
message: "Missing 'agent' block in frontmatter",
|
|
85
|
+
file,
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
else {
|
|
89
|
+
// agent.id
|
|
90
|
+
if (!agentBlock["id"]) {
|
|
91
|
+
findings.push({
|
|
92
|
+
severity: "error",
|
|
93
|
+
category: CAT,
|
|
94
|
+
message: "Missing 'agent.id'",
|
|
95
|
+
file,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
else if (typeof agentBlock["id"] === "string" &&
|
|
99
|
+
!KEBAB_CASE.test(agentBlock["id"])) {
|
|
100
|
+
findings.push({
|
|
101
|
+
severity: "error",
|
|
102
|
+
category: CAT,
|
|
103
|
+
message: `'agent.id' must be kebab-case, got: "${agentBlock["id"]}"`,
|
|
104
|
+
file,
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
// agent.name
|
|
108
|
+
if (!agentBlock["name"]) {
|
|
109
|
+
findings.push({
|
|
110
|
+
severity: "error",
|
|
111
|
+
category: CAT,
|
|
112
|
+
message: "Missing 'agent.name'",
|
|
113
|
+
file,
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
// agent.title
|
|
117
|
+
if (!agentBlock["title"]) {
|
|
118
|
+
findings.push({
|
|
119
|
+
severity: "error",
|
|
120
|
+
category: CAT,
|
|
121
|
+
message: "Missing 'agent.title'",
|
|
122
|
+
file,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
// agent.icon
|
|
126
|
+
if (!agentBlock["icon"]) {
|
|
127
|
+
findings.push({
|
|
128
|
+
severity: "warning",
|
|
129
|
+
category: CAT,
|
|
130
|
+
message: "Missing 'agent.icon'",
|
|
131
|
+
file,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
// agent.whenToUse
|
|
135
|
+
if (!agentBlock["whenToUse"]) {
|
|
136
|
+
findings.push({
|
|
137
|
+
severity: "warning",
|
|
138
|
+
category: CAT,
|
|
139
|
+
message: "Missing 'agent.whenToUse'",
|
|
140
|
+
file,
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
// Validate persona_profile
|
|
145
|
+
if (!personaBlock) {
|
|
146
|
+
findings.push({
|
|
147
|
+
severity: "error",
|
|
148
|
+
category: CAT,
|
|
149
|
+
message: "Missing 'persona_profile' block in frontmatter",
|
|
150
|
+
file,
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
// archetype
|
|
155
|
+
const archetype = personaBlock["archetype"];
|
|
156
|
+
if (!archetype) {
|
|
157
|
+
findings.push({
|
|
158
|
+
severity: "error",
|
|
159
|
+
category: CAT,
|
|
160
|
+
message: "Missing 'persona_profile.archetype'",
|
|
161
|
+
file,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
else if (typeof archetype === "string" && !ARCHETYPES.has(archetype)) {
|
|
165
|
+
findings.push({
|
|
166
|
+
severity: "error",
|
|
167
|
+
category: CAT,
|
|
168
|
+
message: `'persona_profile.archetype' must be one of {${[...ARCHETYPES].join(", ")}}, got: "${archetype}"`,
|
|
169
|
+
file,
|
|
170
|
+
});
|
|
171
|
+
}
|
|
172
|
+
// communication.tone
|
|
173
|
+
const comm = personaBlock["communication"];
|
|
174
|
+
if (!comm || !comm["tone"]) {
|
|
175
|
+
findings.push({
|
|
176
|
+
severity: "warning",
|
|
177
|
+
category: CAT,
|
|
178
|
+
message: "Missing 'persona_profile.communication.tone'",
|
|
179
|
+
file,
|
|
180
|
+
});
|
|
181
|
+
}
|
|
182
|
+
else if (typeof comm["tone"] === "string" && !TONES.has(comm["tone"])) {
|
|
183
|
+
findings.push({
|
|
184
|
+
severity: "warning",
|
|
185
|
+
category: CAT,
|
|
186
|
+
message: `'persona_profile.communication.tone' has unknown value: "${comm["tone"]}"`,
|
|
187
|
+
file,
|
|
188
|
+
});
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
// Validate greeting_levels
|
|
192
|
+
if (!greetingBlock) {
|
|
193
|
+
findings.push({
|
|
194
|
+
severity: "warning",
|
|
195
|
+
category: CAT,
|
|
196
|
+
message: "Missing 'greeting_levels' block",
|
|
197
|
+
file,
|
|
198
|
+
});
|
|
199
|
+
}
|
|
200
|
+
else {
|
|
201
|
+
const keys = Object.keys(greetingBlock);
|
|
202
|
+
if (keys.length < 3) {
|
|
203
|
+
findings.push({
|
|
204
|
+
severity: "warning",
|
|
205
|
+
category: CAT,
|
|
206
|
+
message: `'greeting_levels' should have at least 3 keys, found ${keys.length}`,
|
|
207
|
+
file,
|
|
208
|
+
});
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
return { findings, agents };
|
|
213
|
+
}
|
|
214
|
+
//# sourceMappingURL=agents.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"agents.js","sourceRoot":"","sources":["../../src/categories/agents.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { ValidationFinding, ParsedAgent, ParsedTask, ParsedWorkflow } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Category 6: Cross-reference validation
|
|
4
|
+
* Weight: 15%
|
|
5
|
+
*
|
|
6
|
+
* Validates:
|
|
7
|
+
* - task.responsavel references an existing agent.name
|
|
8
|
+
* - workflow.agent_sequence entries reference existing agent.id values
|
|
9
|
+
* - agent IDs are unique
|
|
10
|
+
* - task identifiers are unique
|
|
11
|
+
*/
|
|
12
|
+
export declare function validateCrossReferences(agents: ParsedAgent[], tasks: ParsedTask[], workflows: ParsedWorkflow[]): ValidationFinding[];
|
|
13
|
+
//# sourceMappingURL=cross-refs.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cross-refs.d.ts","sourceRoot":"","sources":["../../src/categories/cross-refs.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,iBAAiB,EACjB,WAAW,EACX,UAAU,EACV,cAAc,EACf,MAAM,aAAa,CAAC;AAIrB;;;;;;;;;GASG;AACH,wBAAgB,uBAAuB,CACrC,MAAM,EAAE,WAAW,EAAE,EACrB,KAAK,EAAE,UAAU,EAAE,EACnB,SAAS,EAAE,cAAc,EAAE,GAC1B,iBAAiB,EAAE,CAoFrB"}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
const CAT = "cross-refs";
|
|
2
|
+
/**
|
|
3
|
+
* Category 6: Cross-reference validation
|
|
4
|
+
* Weight: 15%
|
|
5
|
+
*
|
|
6
|
+
* Validates:
|
|
7
|
+
* - task.responsavel references an existing agent.name
|
|
8
|
+
* - workflow.agent_sequence entries reference existing agent.id values
|
|
9
|
+
* - agent IDs are unique
|
|
10
|
+
* - task identifiers are unique
|
|
11
|
+
*/
|
|
12
|
+
export function validateCrossReferences(agents, tasks, workflows) {
|
|
13
|
+
const findings = [];
|
|
14
|
+
// Build lookup sets
|
|
15
|
+
const agentNames = new Set();
|
|
16
|
+
const agentIds = new Set();
|
|
17
|
+
const agentIdCounts = new Map();
|
|
18
|
+
const taskIdCounts = new Map();
|
|
19
|
+
// Collect agent names and IDs, check uniqueness
|
|
20
|
+
for (const agent of agents) {
|
|
21
|
+
if (agent.agent?.name) {
|
|
22
|
+
agentNames.add(agent.agent.name);
|
|
23
|
+
}
|
|
24
|
+
if (agent.agent?.id) {
|
|
25
|
+
agentIds.add(agent.agent.id);
|
|
26
|
+
const files = agentIdCounts.get(agent.agent.id) || [];
|
|
27
|
+
files.push(agent.file);
|
|
28
|
+
agentIdCounts.set(agent.agent.id, files);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
// Check agent ID uniqueness
|
|
32
|
+
for (const [id, files] of agentIdCounts) {
|
|
33
|
+
if (files.length > 1) {
|
|
34
|
+
findings.push({
|
|
35
|
+
severity: "error",
|
|
36
|
+
category: CAT,
|
|
37
|
+
message: `Duplicate agent ID "${id}" found in: ${files.join(", ")}`,
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
// Collect task IDs and check uniqueness
|
|
42
|
+
for (const task of tasks) {
|
|
43
|
+
if (task.task) {
|
|
44
|
+
const files = taskIdCounts.get(task.task) || [];
|
|
45
|
+
files.push(task.file);
|
|
46
|
+
taskIdCounts.set(task.task, files);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
for (const [id, files] of taskIdCounts) {
|
|
50
|
+
if (files.length > 1) {
|
|
51
|
+
findings.push({
|
|
52
|
+
severity: "error",
|
|
53
|
+
category: CAT,
|
|
54
|
+
message: `Duplicate task identifier "${id}" found in: ${files.join(", ")}`,
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
// Validate task.responsavel → agent.name
|
|
59
|
+
for (const task of tasks) {
|
|
60
|
+
if (task.responsavel && agentNames.size > 0) {
|
|
61
|
+
if (!agentNames.has(task.responsavel)) {
|
|
62
|
+
findings.push({
|
|
63
|
+
severity: "error",
|
|
64
|
+
category: CAT,
|
|
65
|
+
message: `Task references agent "${task.responsavel}" but no agent with that name exists`,
|
|
66
|
+
file: task.file,
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
// Validate workflow.agent_sequence → agent.id
|
|
72
|
+
for (const wf of workflows) {
|
|
73
|
+
if (wf.agent_sequence && Array.isArray(wf.agent_sequence) && agentIds.size > 0) {
|
|
74
|
+
for (const agentId of wf.agent_sequence) {
|
|
75
|
+
if (typeof agentId === "string" && !agentIds.has(agentId)) {
|
|
76
|
+
findings.push({
|
|
77
|
+
severity: "error",
|
|
78
|
+
category: CAT,
|
|
79
|
+
message: `Workflow "${wf.workflow_name || wf.file}" references agent ID "${agentId}" not found in agents/`,
|
|
80
|
+
file: wf.file,
|
|
81
|
+
});
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
return findings;
|
|
87
|
+
}
|
|
88
|
+
//# sourceMappingURL=cross-refs.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cross-refs.js","sourceRoot":"","sources":["../../src/categories/cross-refs.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { FileTree, ValidationFinding, ParsedManifest } from "../types.js";
|
|
2
|
+
/**
|
|
3
|
+
* Category 1: Manifest validation (squad.yaml)
|
|
4
|
+
* Weight: 25%
|
|
5
|
+
*
|
|
6
|
+
* Validates:
|
|
7
|
+
* - squad.yaml exists and is valid YAML
|
|
8
|
+
* - name (kebab-case)
|
|
9
|
+
* - version (semver)
|
|
10
|
+
* - description (non-empty)
|
|
11
|
+
* - aios.minVersion (semver string)
|
|
12
|
+
* - aios.type == "squad"
|
|
13
|
+
* - components.agents has at least 1 entry
|
|
14
|
+
*/
|
|
15
|
+
export declare function validateManifest(fileTree: FileTree): {
|
|
16
|
+
findings: ValidationFinding[];
|
|
17
|
+
parsed: ParsedManifest | null;
|
|
18
|
+
};
|
|
19
|
+
//# sourceMappingURL=manifest.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.d.ts","sourceRoot":"","sources":["../../src/categories/manifest.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAM/E;;;;;;;;;;;;GAYG;AACH,wBAAgB,gBAAgB,CAC9B,QAAQ,EAAE,QAAQ,GACjB;IAAE,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAAC,MAAM,EAAE,cAAc,GAAG,IAAI,CAAA;CAAE,CAuIlE"}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { parseYaml, getNestedValue } from "../parser.js";
|
|
2
|
+
import { KEBAB_CASE, SEMVER } from "../utils/patterns.js";
|
|
3
|
+
const CAT = "manifest";
|
|
4
|
+
/**
|
|
5
|
+
* Category 1: Manifest validation (squad.yaml)
|
|
6
|
+
* Weight: 25%
|
|
7
|
+
*
|
|
8
|
+
* Validates:
|
|
9
|
+
* - squad.yaml exists and is valid YAML
|
|
10
|
+
* - name (kebab-case)
|
|
11
|
+
* - version (semver)
|
|
12
|
+
* - description (non-empty)
|
|
13
|
+
* - aios.minVersion (semver string)
|
|
14
|
+
* - aios.type == "squad"
|
|
15
|
+
* - components.agents has at least 1 entry
|
|
16
|
+
*/
|
|
17
|
+
export function validateManifest(fileTree) {
|
|
18
|
+
const findings = [];
|
|
19
|
+
const content = fileTree.get("squad.yaml");
|
|
20
|
+
if (!content) {
|
|
21
|
+
findings.push({
|
|
22
|
+
severity: "error",
|
|
23
|
+
category: CAT,
|
|
24
|
+
message: "squad.yaml not found",
|
|
25
|
+
});
|
|
26
|
+
return { findings, parsed: null };
|
|
27
|
+
}
|
|
28
|
+
const parsed = parseYaml(content);
|
|
29
|
+
if (!parsed) {
|
|
30
|
+
findings.push({
|
|
31
|
+
severity: "error",
|
|
32
|
+
category: CAT,
|
|
33
|
+
message: "squad.yaml contains invalid YAML",
|
|
34
|
+
file: "squad.yaml",
|
|
35
|
+
});
|
|
36
|
+
return { findings, parsed: null };
|
|
37
|
+
}
|
|
38
|
+
// name
|
|
39
|
+
if (!parsed.name) {
|
|
40
|
+
findings.push({
|
|
41
|
+
severity: "error",
|
|
42
|
+
category: CAT,
|
|
43
|
+
message: "squad.yaml missing required field: name",
|
|
44
|
+
file: "squad.yaml",
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
else if (typeof parsed.name !== "string" || !KEBAB_CASE.test(parsed.name)) {
|
|
48
|
+
findings.push({
|
|
49
|
+
severity: "error",
|
|
50
|
+
category: CAT,
|
|
51
|
+
message: `squad.yaml 'name' must be kebab-case, got: "${parsed.name}"`,
|
|
52
|
+
file: "squad.yaml",
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
// version
|
|
56
|
+
if (!parsed.version) {
|
|
57
|
+
findings.push({
|
|
58
|
+
severity: "error",
|
|
59
|
+
category: CAT,
|
|
60
|
+
message: "squad.yaml missing required field: version",
|
|
61
|
+
file: "squad.yaml",
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
else if (typeof parsed.version !== "string" || !SEMVER.test(parsed.version)) {
|
|
65
|
+
findings.push({
|
|
66
|
+
severity: "error",
|
|
67
|
+
category: CAT,
|
|
68
|
+
message: `squad.yaml 'version' must be semver (X.Y.Z), got: "${parsed.version}"`,
|
|
69
|
+
file: "squad.yaml",
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
// description
|
|
73
|
+
if (!parsed.description) {
|
|
74
|
+
findings.push({
|
|
75
|
+
severity: "error",
|
|
76
|
+
category: CAT,
|
|
77
|
+
message: "squad.yaml missing required field: description",
|
|
78
|
+
file: "squad.yaml",
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
// aios block
|
|
82
|
+
const aios = parsed.aios;
|
|
83
|
+
if (!aios || typeof aios !== "object") {
|
|
84
|
+
findings.push({
|
|
85
|
+
severity: "error",
|
|
86
|
+
category: CAT,
|
|
87
|
+
message: "squad.yaml missing required field: aios",
|
|
88
|
+
file: "squad.yaml",
|
|
89
|
+
});
|
|
90
|
+
}
|
|
91
|
+
else {
|
|
92
|
+
const minVersion = getNestedValue(parsed, "aios.minVersion");
|
|
93
|
+
if (!minVersion) {
|
|
94
|
+
findings.push({
|
|
95
|
+
severity: "error",
|
|
96
|
+
category: CAT,
|
|
97
|
+
message: "squad.yaml missing required field: aios.minVersion",
|
|
98
|
+
file: "squad.yaml",
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
else if (typeof minVersion === "string" && !SEMVER.test(minVersion) && !/^\d+\.\d+\.\d+/.test(minVersion)) {
|
|
102
|
+
findings.push({
|
|
103
|
+
severity: "warning",
|
|
104
|
+
category: CAT,
|
|
105
|
+
message: `squad.yaml 'aios.minVersion' should be semver, got: "${minVersion}"`,
|
|
106
|
+
file: "squad.yaml",
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
const aiosType = getNestedValue(parsed, "aios.type");
|
|
110
|
+
if (!aiosType) {
|
|
111
|
+
findings.push({
|
|
112
|
+
severity: "error",
|
|
113
|
+
category: CAT,
|
|
114
|
+
message: "squad.yaml missing required field: aios.type",
|
|
115
|
+
file: "squad.yaml",
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
else if (aiosType !== "squad") {
|
|
119
|
+
findings.push({
|
|
120
|
+
severity: "error",
|
|
121
|
+
category: CAT,
|
|
122
|
+
message: `squad.yaml 'aios.type' must be "squad", got: "${aiosType}"`,
|
|
123
|
+
file: "squad.yaml",
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
// components.agents
|
|
128
|
+
const components = parsed.components;
|
|
129
|
+
if (!components || typeof components !== "object") {
|
|
130
|
+
findings.push({
|
|
131
|
+
severity: "warning",
|
|
132
|
+
category: CAT,
|
|
133
|
+
message: "squad.yaml missing 'components' block",
|
|
134
|
+
file: "squad.yaml",
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
else {
|
|
138
|
+
const agents = components.agents;
|
|
139
|
+
if (!agents || !Array.isArray(agents) || agents.length === 0) {
|
|
140
|
+
findings.push({
|
|
141
|
+
severity: "error",
|
|
142
|
+
category: CAT,
|
|
143
|
+
message: "squad.yaml 'components.agents' must list at least 1 agent",
|
|
144
|
+
file: "squad.yaml",
|
|
145
|
+
});
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
return { findings, parsed };
|
|
149
|
+
}
|
|
150
|
+
//# sourceMappingURL=manifest.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"manifest.js","sourceRoot":"","sources":["../../src/categories/manifest.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import type { FileTree, ValidationFinding, ParsedManifest } from "../types.js";
|
|
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 declare function validateStructure(fileTree: FileTree, manifest: ParsedManifest | null): ValidationFinding[];
|
|
14
|
+
//# sourceMappingURL=structure.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structure.d.ts","sourceRoot":"","sources":["../../src/categories/structure.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,aAAa,CAAC;AAI/E;;;;;;;;;;GAUG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,QAAQ,EAClB,QAAQ,EAAE,cAAc,GAAG,IAAI,GAC9B,iBAAiB,EAAE,CAoErB"}
|