| 398 | |
| 399 | function lensContractual(file, content) { |
| 400 | // Only check skill files |
| 401 | if (!/^skills\/.*\.md$/.test(file)) return []; |
| 402 | |
| 403 | const violations = []; |
| 404 | |
| 405 | // Check for required frontmatter fields |
| 406 | const frontmatterMatch = content.match(/^---\n([\s\S]*?)\n---/); |
| 407 | if (!frontmatterMatch) { |
| 408 | violations.push({ |
| 409 | file, |
| 410 | lens: 'contractual', |
| 411 | message: 'Skill file missing frontmatter block (---)', |
| 412 | }); |
| 413 | return violations; |
| 414 | } |
| 415 | |
| 416 | const fm = frontmatterMatch[1]; |
| 417 | const requiredFields = ['name', 'description']; |
| 418 | for (const field of requiredFields) { |
| 419 | if (!new RegExp(`^${field}:`, 'm').test(fm)) { |
| 420 | violations.push({ |
| 421 | file, |
| 422 | lens: 'contractual', |
| 423 | message: `Skill file missing required frontmatter field: ${field}`, |
| 424 | }); |
| 425 | } |
| 426 | } |
| 427 | |
| 428 | // Check for required sections |
| 429 | const requiredSections = ['## Protocol', '## Identity']; |
| 430 | for (const section of requiredSections) { |
| 431 | if (!content.includes(section)) { |
| 432 | violations.push({ |
| 433 | file, |
| 434 | lens: 'contractual', |