@agentlee5/agent-skills 1.0.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/.leeway/config.json +133 -0
- package/LICENSE +21 -0
- package/LeeWay-Standards/LICENSE +21 -0
- package/LeeWay-Standards/README.md +324 -0
- package/LeeWay-Standards/examples/NexusButton.tsx +90 -0
- package/LeeWay-Standards/examples/example-agent.js +89 -0
- package/LeeWay-Standards/package.json +61 -0
- package/LeeWay-Standards/schemas/leeway-config.schema.json +81 -0
- package/LeeWay-Standards/schemas/leeway-header.schema.json +63 -0
- package/LeeWay-Standards/src/agents/discovery/architecture-map-agent.js +134 -0
- package/LeeWay-Standards/src/agents/discovery/docs-agent.js +126 -0
- package/LeeWay-Standards/src/agents/discovery/explain-agent.js +95 -0
- package/LeeWay-Standards/src/agents/discovery/intent-registry-agent.js +119 -0
- package/LeeWay-Standards/src/agents/discovery/schema-agent.js +116 -0
- package/LeeWay-Standards/src/agents/discovery/sitemap-agent.js +88 -0
- package/LeeWay-Standards/src/agents/governance/align-agent.js +155 -0
- package/LeeWay-Standards/src/agents/governance/assess-agent.js +161 -0
- package/LeeWay-Standards/src/agents/governance/audit-agent.js +185 -0
- package/LeeWay-Standards/src/agents/integrity/circular-dependency-agent.js +88 -0
- package/LeeWay-Standards/src/agents/integrity/dependency-graph-agent.js +107 -0
- package/LeeWay-Standards/src/agents/integrity/duplicate-logic-agent.js +108 -0
- package/LeeWay-Standards/src/agents/integrity/import-agent.js +83 -0
- package/LeeWay-Standards/src/agents/integrity/module-policy-agent.js +94 -0
- package/LeeWay-Standards/src/agents/integrity/refactor-scan-agent.js +113 -0
- package/LeeWay-Standards/src/agents/integrity/syntax-agent.js +84 -0
- package/LeeWay-Standards/src/agents/mcp/endpoint-agent.js +106 -0
- package/LeeWay-Standards/src/agents/mcp/env-agent.js +111 -0
- package/LeeWay-Standards/src/agents/mcp/health-agent-lite.js +119 -0
- package/LeeWay-Standards/src/agents/mcp/manifest-agent.js +87 -0
- package/LeeWay-Standards/src/agents/mcp/port-agent.js +125 -0
- package/LeeWay-Standards/src/agents/mcp/process-agent.js +124 -0
- package/LeeWay-Standards/src/agents/mcp/runtime-agent.js +108 -0
- package/LeeWay-Standards/src/agents/mcp/transport-agent.js +78 -0
- package/LeeWay-Standards/src/agents/orchestration/doctor-agent.js +149 -0
- package/LeeWay-Standards/src/agents/orchestration/memory-agent-lite.js +125 -0
- package/LeeWay-Standards/src/agents/orchestration/router-agent.js +110 -0
- package/LeeWay-Standards/src/agents/security/permission-agent.js +98 -0
- package/LeeWay-Standards/src/agents/security/policy-agent.js +100 -0
- package/LeeWay-Standards/src/agents/security/privacy-agent.js +83 -0
- package/LeeWay-Standards/src/agents/security/prompt-security-agent.js +103 -0
- package/LeeWay-Standards/src/agents/security/secret-scan-agent.js +108 -0
- package/LeeWay-Standards/src/agents/security/tool-access-agent.js +105 -0
- package/LeeWay-Standards/src/agents/standards/authority-agent.js +114 -0
- package/LeeWay-Standards/src/agents/standards/discovery-pipeline-agent.js +91 -0
- package/LeeWay-Standards/src/agents/standards/header-agent.js +120 -0
- package/LeeWay-Standards/src/agents/standards/placement-agent.js +96 -0
- package/LeeWay-Standards/src/agents/standards/region-agent.js +99 -0
- package/LeeWay-Standards/src/agents/standards/registry-agent.js +153 -0
- package/LeeWay-Standards/src/agents/standards/tag-agent.js +111 -0
- package/LeeWay-Standards/src/cli/leeway.js +225 -0
- package/LeeWay-Standards/src/core/compliance-scorer.js +168 -0
- package/LeeWay-Standards/src/core/compliance-scorer.test.js +121 -0
- package/LeeWay-Standards/src/core/header-parser.js +207 -0
- package/LeeWay-Standards/src/core/header-parser.test.js +198 -0
- package/LeeWay-Standards/src/core/region-classifier.js +137 -0
- package/LeeWay-Standards/src/core/region-classifier.test.js +100 -0
- package/LeeWay-Standards/src/core/tag-validator.js +139 -0
- package/LeeWay-Standards/src/core/tag-validator.test.js +109 -0
- package/LeeWay-Standards/src/index.js +83 -0
- package/README.md +217 -0
- package/agent-config.yaml +456 -0
- package/agentbage.png.png +0 -0
- package/bin/leeway-skills-badge.js +52 -0
- package/bin/leeway-skills-mcp.js +48 -0
- package/bin/leeway-skills.js +160 -0
- package/bin/leeway-standards.js +49 -0
- package/config/.skillsignore +63 -0
- package/config/skills-config.json +70 -0
- package/documents/AGENT_LEARNING_REFERENCE.md +329 -0
- package/documents/AGENT_LEE_INTEGRATION.md +534 -0
- package/documents/COMPLETE_SYSTEM_OVERVIEW.md +502 -0
- package/documents/COMPREHENSIVE_SKILL_INTEGRATION_PLAN.md +644 -0
- package/documents/DIRECTORY_MAP.md +323 -0
- package/documents/EXTENDING.md +514 -0
- package/documents/FILE_DIRECTORY_GUIDE.md +427 -0
- package/documents/LEEWAY_BADGE_INTEGRATION.md +76 -0
- package/documents/LEEWAY_IMPLEMENTATION_SUMMARY.md +384 -0
- package/documents/LEEWAY_INTEGRATION_GUIDE.md +414 -0
- package/documents/LEEWAY_NPM_SDK.md +66 -0
- package/documents/LEEWAY_QUICK_START.md +288 -0
- package/documents/LEEWAY_SKILLS_BRANDING.md +375 -0
- package/documents/LEEWAY_SKILLS_MCP_SUMMARY.md +593 -0
- package/documents/LEEWAY_STANDARDS_COMPLIANCE.md +361 -0
- package/documents/LEEWAY_UNIFIED_ARCHITECTURE.md +473 -0
- package/documents/LEEWAY_WORKFLOWS_QUICK_REFERENCE.md +307 -0
- package/documents/LEEWAY_WORKFLOWS_STRATEGIC_PLAN.md +515 -0
- package/documents/LIFELONG_LEARNING_LAYER.md +478 -0
- package/documents/MCP_ARCHITECTURE.md +683 -0
- package/documents/QUICK_REFERENCE.md +301 -0
- package/documents/SETUP.md +325 -0
- package/documents/SETUP_SUMMARY.md +413 -0
- package/documents/SKILL_ACQUISITION_EXECUTIVE_SUMMARY.md +373 -0
- package/documents/SKILL_ACQUISITION_IMPLEMENTATION.md +692 -0
- package/documents/SKILL_ACQUISITION_MANIFEST.md +404 -0
- package/documents/SKILL_ACQUISITION_QUICK_REFERENCE.md +349 -0
- package/documents/SKILL_WORKFLOW_COMPOSITION_MATRIX.md +537 -0
- package/documents/STRUCTURE.md +382 -0
- package/documents/SYSTEM_TRANSFORMATION_SUMMARY.md +560 -0
- package/documents/USAGE.md +390 -0
- package/documents/WORKFLOW_ACQUISITION_MANIFEST.md +576 -0
- package/documents/aiskills.txt +460 -0
- package/mcp-server/README.md +697 -0
- package/mcp-server/dist/badge-proof.d.ts +66 -0
- package/mcp-server/dist/badge-proof.d.ts.map +1 -0
- package/mcp-server/dist/badge-proof.js +324 -0
- package/mcp-server/dist/badge-proof.js.map +1 -0
- package/mcp-server/dist/index.d.ts +64 -0
- package/mcp-server/dist/index.d.ts.map +1 -0
- package/mcp-server/dist/index.js +263 -0
- package/mcp-server/dist/index.js.map +1 -0
- package/mcp-server/dist/install-badge-proof.d.ts +3 -0
- package/mcp-server/dist/install-badge-proof.d.ts.map +1 -0
- package/mcp-server/dist/install-badge-proof.js +109 -0
- package/mcp-server/dist/install-badge-proof.js.map +1 -0
- package/mcp-server/package.json +43 -0
- package/mcp-server/src/badge-proof.ts +469 -0
- package/mcp-server/src/index.ts +355 -0
- package/mcp-server/src/install-badge-proof.ts +132 -0
- package/mcp-server/tsconfig.json +22 -0
- package/package.json +84 -0
- package/scripts/init-leeway.js +217 -0
- package/scripts/leeway-agents/compliance-monitor.js +374 -0
- package/scripts/leeway-agents/header-injector.js +321 -0
- package/scripts/skill-integration-toolkit.py +319 -0
- package/scripts/skills-registry.json +1117 -0
- package/scripts/sync-skills.ps1 +275 -0
- package/scripts/verify-leeway-setup.js +249 -0
- package/scripts/workflow-integration-toolkit.py +522 -0
- package/sdk/application-installer.js +92 -0
- package/sdk/index.js +43 -0
- package/sdk/paths.js +167 -0
- package/skills/agent-autonomy/autonomous-conductor/SKILL.md +206 -0
- package/skills/agent-autonomy/full-stack-delivery/SKILL.md +206 -0
- package/skills/agent-orchestration/multi-agent-orchestration/SKILL.md +68 -0
- package/skills/agent-patterns/agent-design-patterns/SKILL.md +70 -0
- package/skills/ai-ml/llm-prompting/SKILL.md +71 -0
- package/skills/ai-ml/ml-model-development/SKILL.md +67 -0
- package/skills/ai-ml/multimodal-systems/SKILL.md +71 -0
- package/skills/ai-ml/retrieval-generation-fine-tuning/SKILL.md +71 -0
- package/skills/architecture/system-design/SKILL.md +67 -0
- package/skills/code-analysis/refactoring/SKILL.md +64 -0
- package/skills/code-analysis/security-vulnerability-scanning/SKILL.md +71 -0
- package/skills/code-analysis/static-analysis/SKILL.md +64 -0
- package/skills/code-generation/full-stack-application/SKILL.md +70 -0
- package/skills/code-generation/microservices-architecture/SKILL.md +71 -0
- package/skills/code-generation/python-codegen/SKILL.md +64 -0
- package/skills/code-generation/typescript-codegen/SKILL.md +64 -0
- package/skills/data-analysis/advanced-analytics/SKILL.md +71 -0
- package/skills/data-analysis/pandas-analysis/SKILL.md +66 -0
- package/skills/database-design/database-design-optimization/SKILL.md +70 -0
- package/skills/debugging/javascript-debugging/SKILL.md +67 -0
- package/skills/debugging/python-debugging/SKILL.md +67 -0
- package/skills/devops/dockerfile-creation/SKILL.md +64 -0
- package/skills/devops/kubernetes-deployment/SKILL.md +65 -0
- package/skills/documentation/api-documentation/SKILL.md +67 -0
- package/skills/error-handling/resilience-patterns/SKILL.md +70 -0
- package/skills/git-workflow/git-collaboration/SKILL.md +67 -0
- package/skills/infrastructure/cicd-pipelines/SKILL.md +70 -0
- package/skills/infrastructure/infrastructure-as-code/SKILL.md +70 -0
- package/skills/observability/monitoring-and-observability/SKILL.md +70 -0
- package/skills/performance-optimization/performance-engineering/SKILL.md +70 -0
- package/skills/prompt-optimization/prompt-engineering-advanced/SKILL.md +70 -0
- package/skills/quality-assurance/deployment-validator/SKILL.md +382 -0
- package/skills/quality-assurance/web-security-sweep/SKILL.md +320 -0
- package/skills/rag-knowledge/rag-systems/SKILL.md +70 -0
- package/skills/research/knowledge-synthesis/SKILL.md +71 -0
- package/skills/security/authentication-authorization/SKILL.md +71 -0
- package/skills/security/code-security/SKILL.md +66 -0
- package/skills/security/secure-architecture/SKILL.md +71 -0
- package/skills/self-optimization/dev-loop-optimizer/SKILL.md +344 -0
- package/skills/self-optimization/memory-learning/SKILL.md +335 -0
- package/skills/self-optimization/runtime-self-profiling/SKILL.md +250 -0
- package/skills/testing/advanced-testing-strategies/SKILL.md +71 -0
- package/skills/testing/integration-testing/SKILL.md +66 -0
- package/skills/testing/load-testing-capacity/SKILL.md +71 -0
- package/skills/testing/unit-testing/SKILL.md +66 -0
- package/skills/tool-integration/custom-tool-creation/SKILL.md +70 -0
- package/skills/web-development/advanced-frontend-patterns/SKILL.md +71 -0
- package/skills/web-development/api-design/SKILL.md +71 -0
- package/skills/web-development/css-styling/SKILL.md +67 -0
- package/skills/web-development/react-development/SKILL.md +79 -0
- package/skills/workflow-composition/workflow-orchestration/SKILL.md +70 -0
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
/*
|
|
2
|
+
LEEWAY HEADER — DO NOT REMOVE
|
|
3
|
+
|
|
4
|
+
REGION: SEO.AGENT.SCHEMA
|
|
5
|
+
TAG: SEO.SCHEMA.AGENT.MAIN
|
|
6
|
+
|
|
7
|
+
COLOR_ONION_HEX:
|
|
8
|
+
NEON=#39FF14
|
|
9
|
+
FLUO=#0DFF94
|
|
10
|
+
PASTEL=#C7FFD8
|
|
11
|
+
|
|
12
|
+
ICON_ASCII:
|
|
13
|
+
family=lucide
|
|
14
|
+
glyph=code
|
|
15
|
+
|
|
16
|
+
5WH:
|
|
17
|
+
WHAT = Schema agent — generates machine-readable schemas and capability metadata
|
|
18
|
+
WHY = Applications must expose structured data for AI tools, search engines, and voice assistants
|
|
19
|
+
WHO = Rapid Web Development
|
|
20
|
+
WHERE = src/agents/discovery/schema-agent.js
|
|
21
|
+
WHEN = 2026
|
|
22
|
+
HOW = Generates Schema.org JSON-LD, OpenGraph, and capability manifest from LEEWAY metadata
|
|
23
|
+
|
|
24
|
+
AGENTS:
|
|
25
|
+
SCHEMA
|
|
26
|
+
SITEMAP
|
|
27
|
+
DISCOVERY
|
|
28
|
+
|
|
29
|
+
LICENSE:
|
|
30
|
+
MIT
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* SchemaAgent generates structured discovery metadata from LEEWAY headers and config.
|
|
35
|
+
*/
|
|
36
|
+
export class SchemaAgent {
|
|
37
|
+
/**
|
|
38
|
+
* Generate Schema.org JSON-LD for a software application.
|
|
39
|
+
*
|
|
40
|
+
* @param {{ name: string, description: string, url?: string, version?: string, author?: string }} config
|
|
41
|
+
* @returns {object} - JSON-LD schema object
|
|
42
|
+
*/
|
|
43
|
+
generateSoftwareApplicationSchema(config) {
|
|
44
|
+
return {
|
|
45
|
+
'@context': 'https://schema.org',
|
|
46
|
+
'@type': 'SoftwareApplication',
|
|
47
|
+
name: config.name,
|
|
48
|
+
description: config.description,
|
|
49
|
+
url: config.url || '',
|
|
50
|
+
version: config.version || '1.0.1',
|
|
51
|
+
author: {
|
|
52
|
+
'@type': 'Organization',
|
|
53
|
+
name: config.author || 'Unknown',
|
|
54
|
+
},
|
|
55
|
+
applicationCategory: 'DeveloperApplication',
|
|
56
|
+
operatingSystem: 'Cross-platform',
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/**
|
|
61
|
+
* Generate OpenGraph metadata tags.
|
|
62
|
+
*
|
|
63
|
+
* @param {{ title: string, description: string, url?: string, image?: string, type?: string }} config
|
|
64
|
+
* @returns {object[]} - Array of meta tag objects
|
|
65
|
+
*/
|
|
66
|
+
generateOpenGraphMeta(config) {
|
|
67
|
+
return [
|
|
68
|
+
{ property: 'og:title', content: config.title },
|
|
69
|
+
{ property: 'og:description', content: config.description },
|
|
70
|
+
{ property: 'og:type', content: config.type || 'website' },
|
|
71
|
+
{ property: 'og:url', content: config.url || '' },
|
|
72
|
+
{ property: 'og:image', content: config.image || '' },
|
|
73
|
+
];
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* Generate FAQ structured data from a Q&A list.
|
|
78
|
+
*
|
|
79
|
+
* @param {{ question: string, answer: string }[]} faqs
|
|
80
|
+
* @returns {object} - FAQ JSON-LD schema
|
|
81
|
+
*/
|
|
82
|
+
generateFAQSchema(faqs) {
|
|
83
|
+
return {
|
|
84
|
+
'@context': 'https://schema.org',
|
|
85
|
+
'@type': 'FAQPage',
|
|
86
|
+
mainEntity: faqs.map(({ question, answer }) => ({
|
|
87
|
+
'@type': 'Question',
|
|
88
|
+
name: question,
|
|
89
|
+
acceptedAnswer: {
|
|
90
|
+
'@type': 'Answer',
|
|
91
|
+
text: answer,
|
|
92
|
+
},
|
|
93
|
+
})),
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
/**
|
|
98
|
+
* Generate a LEEWAY capability manifest for a module.
|
|
99
|
+
*
|
|
100
|
+
* @param {{ name: string, region: string, tag: string, fiveWH: object, agents: string[] }} header
|
|
101
|
+
* @returns {object} - Capability manifest
|
|
102
|
+
*/
|
|
103
|
+
generateCapabilityManifest(header) {
|
|
104
|
+
return {
|
|
105
|
+
leeway: '1.0',
|
|
106
|
+
identity: {
|
|
107
|
+
name: header.name || header.fiveWH?.WHAT || 'Unknown',
|
|
108
|
+
region: header.region,
|
|
109
|
+
tag: header.tag,
|
|
110
|
+
},
|
|
111
|
+
fiveWH: header.fiveWH,
|
|
112
|
+
authorizedAgents: header.agents || [],
|
|
113
|
+
generated: new Date().toISOString(),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/*
|
|
2
|
+
LEEWAY HEADER — DO NOT REMOVE
|
|
3
|
+
|
|
4
|
+
REGION: SEO.AGENT.SITEMAP
|
|
5
|
+
TAG: SEO.SITEMAP.AGENT.MAIN
|
|
6
|
+
|
|
7
|
+
COLOR_ONION_HEX:
|
|
8
|
+
NEON=#39FF14
|
|
9
|
+
FLUO=#0DFF94
|
|
10
|
+
PASTEL=#C7FFD8
|
|
11
|
+
|
|
12
|
+
ICON_ASCII:
|
|
13
|
+
family=lucide
|
|
14
|
+
glyph=map
|
|
15
|
+
|
|
16
|
+
5WH:
|
|
17
|
+
WHAT = Sitemap agent — builds site and discovery maps from LEEWAY registry
|
|
18
|
+
WHY = Applications must expose sitemaps for crawlers, search engines, and AI indexers
|
|
19
|
+
WHO = Rapid Web Development
|
|
20
|
+
WHERE = src/agents/discovery/sitemap-agent.js
|
|
21
|
+
WHEN = 2026
|
|
22
|
+
HOW = Reads route registrations and LEEWAY headers to build XML and JSON sitemaps
|
|
23
|
+
|
|
24
|
+
AGENTS:
|
|
25
|
+
SITEMAP
|
|
26
|
+
SCHEMA
|
|
27
|
+
REGISTRY
|
|
28
|
+
|
|
29
|
+
LICENSE:
|
|
30
|
+
MIT
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* SitemapAgent builds XML and JSON sitemaps from route registrations.
|
|
35
|
+
*/
|
|
36
|
+
export class SitemapAgent {
|
|
37
|
+
constructor(options = {}) {
|
|
38
|
+
this.baseUrl = options.baseUrl || 'https://example.com';
|
|
39
|
+
this.routes = [];
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Add a route to the sitemap.
|
|
44
|
+
*
|
|
45
|
+
* @param {{ path: string, priority?: number, changefreq?: string, lastmod?: string }} route
|
|
46
|
+
*/
|
|
47
|
+
addRoute(route) {
|
|
48
|
+
this.routes.push({
|
|
49
|
+
path: route.path,
|
|
50
|
+
priority: route.priority || 0.5,
|
|
51
|
+
changefreq: route.changefreq || 'weekly',
|
|
52
|
+
lastmod: route.lastmod || new Date().toISOString().split('T')[0],
|
|
53
|
+
});
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* Generate an XML sitemap string.
|
|
58
|
+
* @returns {string}
|
|
59
|
+
*/
|
|
60
|
+
generateXML() {
|
|
61
|
+
const urls = this.routes.map(route => ` <url>
|
|
62
|
+
<loc>${this.baseUrl}${route.path}</loc>
|
|
63
|
+
<lastmod>${route.lastmod}</lastmod>
|
|
64
|
+
<changefreq>${route.changefreq}</changefreq>
|
|
65
|
+
<priority>${route.priority}</priority>
|
|
66
|
+
</url>`).join('\n');
|
|
67
|
+
|
|
68
|
+
return `<?xml version="1.0" encoding="UTF-8"?>
|
|
69
|
+
<urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9">
|
|
70
|
+
${urls}
|
|
71
|
+
</urlset>`;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Generate a JSON sitemap.
|
|
76
|
+
* @returns {object}
|
|
77
|
+
*/
|
|
78
|
+
generateJSON() {
|
|
79
|
+
return {
|
|
80
|
+
baseUrl: this.baseUrl,
|
|
81
|
+
generated: new Date().toISOString(),
|
|
82
|
+
routes: this.routes.map(r => ({
|
|
83
|
+
url: `${this.baseUrl}${r.path}`,
|
|
84
|
+
...r,
|
|
85
|
+
})),
|
|
86
|
+
};
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,155 @@
|
|
|
1
|
+
/*
|
|
2
|
+
LEEWAY HEADER — DO NOT REMOVE
|
|
3
|
+
|
|
4
|
+
REGION: AI.AGENT.GOVERNANCE
|
|
5
|
+
TAG: AI.AGENT.ALIGN.MAIN
|
|
6
|
+
|
|
7
|
+
COLOR_ONION_HEX:
|
|
8
|
+
NEON=#39FF14
|
|
9
|
+
FLUO=#0DFF94
|
|
10
|
+
PASTEL=#C7FFD8
|
|
11
|
+
|
|
12
|
+
ICON_ASCII:
|
|
13
|
+
family=lucide
|
|
14
|
+
glyph=align-left
|
|
15
|
+
|
|
16
|
+
5WH:
|
|
17
|
+
WHAT = Align agent — normalizes files, settings, transports, ports, and env structure
|
|
18
|
+
WHY = Drift in naming, structure, and configuration breaks governance and causes deployment failures
|
|
19
|
+
WHO = Rapid Web Development
|
|
20
|
+
WHERE = src/agents/governance/align-agent.js
|
|
21
|
+
WHEN = 2026
|
|
22
|
+
HOW = Reads assessment results, proposes normalized corrections, applies non-destructive patches
|
|
23
|
+
|
|
24
|
+
AGENTS:
|
|
25
|
+
ALIGN
|
|
26
|
+
ASSESS
|
|
27
|
+
|
|
28
|
+
LICENSE:
|
|
29
|
+
MIT
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import { readFile, writeFile, rename } from 'node:fs/promises';
|
|
33
|
+
import { join, basename, dirname, extname } from 'node:path';
|
|
34
|
+
import { buildHeader, parseHeader } from '../../core/header-parser.js';
|
|
35
|
+
import { inferTag } from '../../core/tag-validator.js';
|
|
36
|
+
import { classifyRegion } from '../../core/region-classifier.js';
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* AlignAgent normalizes files and structures according to LEEWAY standards.
|
|
40
|
+
* Operates in dry-run mode by default — set options.dryRun = false to apply changes.
|
|
41
|
+
*/
|
|
42
|
+
export class AlignAgent {
|
|
43
|
+
constructor(options = {}) {
|
|
44
|
+
this.rootDir = options.rootDir || process.cwd();
|
|
45
|
+
this.dryRun = options.dryRun !== false;
|
|
46
|
+
this.log = options.log || (() => {});
|
|
47
|
+
this.changes = [];
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Align a list of files that were identified as needing headers by the assess agent.
|
|
52
|
+
*
|
|
53
|
+
* @param {string[]} filePaths - Relative file paths from assessment
|
|
54
|
+
* @returns {Promise<AlignResult>}
|
|
55
|
+
*/
|
|
56
|
+
async alignHeaders(filePaths) {
|
|
57
|
+
const results = [];
|
|
58
|
+
|
|
59
|
+
for (const relPath of filePaths) {
|
|
60
|
+
const fullPath = join(this.rootDir, relPath);
|
|
61
|
+
try {
|
|
62
|
+
const content = await readFile(fullPath, 'utf8');
|
|
63
|
+
const existingHeader = parseHeader(content);
|
|
64
|
+
|
|
65
|
+
if (existingHeader) {
|
|
66
|
+
results.push({ file: relPath, action: 'skipped', reason: 'Header already present' });
|
|
67
|
+
continue;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const regionResult = classifyRegion(relPath);
|
|
71
|
+
const tag = inferTag(relPath);
|
|
72
|
+
const fileName = basename(relPath, extname(relPath));
|
|
73
|
+
|
|
74
|
+
const header = buildHeader({
|
|
75
|
+
region: regionResult.region,
|
|
76
|
+
tag,
|
|
77
|
+
fiveWH: {
|
|
78
|
+
WHAT: `${fileName} module`,
|
|
79
|
+
WHY: `Part of ${regionResult.region} region`,
|
|
80
|
+
WHO: 'LEEWAY Align Agent',
|
|
81
|
+
WHERE: relPath,
|
|
82
|
+
WHEN: new Date().getFullYear().toString(),
|
|
83
|
+
HOW: 'Auto-aligned by LEEWAY align-agent',
|
|
84
|
+
},
|
|
85
|
+
agents: ['ASSESS', 'ALIGN', 'AUDIT'],
|
|
86
|
+
license: 'MIT',
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
const newContent = header + '\n' + content;
|
|
90
|
+
|
|
91
|
+
if (!this.dryRun) {
|
|
92
|
+
await writeFile(fullPath, newContent, 'utf8');
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
this.changes.push({ type: 'header_added', file: relPath });
|
|
96
|
+
results.push({ file: relPath, action: 'header_added', dryRun: this.dryRun });
|
|
97
|
+
} catch (err) {
|
|
98
|
+
results.push({ file: relPath, action: 'error', reason: err.message });
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
return {
|
|
103
|
+
agent: 'align-agent',
|
|
104
|
+
dryRun: this.dryRun,
|
|
105
|
+
processed: results.length,
|
|
106
|
+
results,
|
|
107
|
+
changes: this.changes,
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
/**
|
|
112
|
+
* Align naming conventions for a list of files.
|
|
113
|
+
* Reports violations without renaming (renaming requires explicit approval).
|
|
114
|
+
*
|
|
115
|
+
* @param {string[]} filePaths
|
|
116
|
+
* @returns {Promise<object[]>}
|
|
117
|
+
*/
|
|
118
|
+
async checkNaming(filePaths) {
|
|
119
|
+
const violations = [];
|
|
120
|
+
|
|
121
|
+
for (const relPath of filePaths) {
|
|
122
|
+
const fileName = basename(relPath);
|
|
123
|
+
const dir = dirname(relPath).toLowerCase();
|
|
124
|
+
const ext = extname(fileName);
|
|
125
|
+
const base = basename(fileName, ext);
|
|
126
|
+
|
|
127
|
+
const isComponent = dir.includes('component') || dir.includes('page') || dir.includes('layout');
|
|
128
|
+
const isUtil = dir.includes('util') || dir.includes('helper');
|
|
129
|
+
|
|
130
|
+
if (isComponent) {
|
|
131
|
+
const isPascal = /^[A-Z][a-zA-Z0-9]*$/.test(base);
|
|
132
|
+
if (!isPascal) {
|
|
133
|
+
violations.push({
|
|
134
|
+
file: relPath,
|
|
135
|
+
issue: 'React component should use PascalCase',
|
|
136
|
+
current: base,
|
|
137
|
+
suggested: base.charAt(0).toUpperCase() + base.slice(1),
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
} else if (isUtil) {
|
|
141
|
+
const isCamel = /^[a-z][a-zA-Z0-9]*$/.test(base);
|
|
142
|
+
const isKebab = /^[a-z][a-z0-9-]*$/.test(base);
|
|
143
|
+
if (!isCamel && !isKebab) {
|
|
144
|
+
violations.push({
|
|
145
|
+
file: relPath,
|
|
146
|
+
issue: 'Utility file should use camelCase or kebab-case',
|
|
147
|
+
current: base,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
return violations;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
@@ -0,0 +1,161 @@
|
|
|
1
|
+
/*
|
|
2
|
+
LEEWAY HEADER — DO NOT REMOVE
|
|
3
|
+
|
|
4
|
+
REGION: AI.AGENT.GOVERNANCE
|
|
5
|
+
TAG: AI.AGENT.ASSESS.MAIN
|
|
6
|
+
|
|
7
|
+
COLOR_ONION_HEX:
|
|
8
|
+
NEON=#39FF14
|
|
9
|
+
FLUO=#0DFF94
|
|
10
|
+
PASTEL=#C7FFD8
|
|
11
|
+
|
|
12
|
+
ICON_ASCII:
|
|
13
|
+
family=lucide
|
|
14
|
+
glyph=search
|
|
15
|
+
|
|
16
|
+
5WH:
|
|
17
|
+
WHAT = Assess agent — surveys what already exists before anything is created or patched
|
|
18
|
+
WHY = Prevents duplicate work, identifies existing patterns, and gives align/audit agents accurate ground truth
|
|
19
|
+
WHO = Rapid Web Development
|
|
20
|
+
WHERE = src/agents/governance/assess-agent.js
|
|
21
|
+
WHEN = 2026
|
|
22
|
+
HOW = Event-driven file walker, dormant until invoked, returns structured inventory
|
|
23
|
+
|
|
24
|
+
AGENTS:
|
|
25
|
+
ASSESS
|
|
26
|
+
|
|
27
|
+
LICENSE:
|
|
28
|
+
MIT
|
|
29
|
+
*/
|
|
30
|
+
|
|
31
|
+
import { readdir, readFile, stat } from 'node:fs/promises';
|
|
32
|
+
import { join, relative, extname } from 'node:path';
|
|
33
|
+
import { parseHeader } from '../../core/header-parser.js';
|
|
34
|
+
import { classifyRegion } from '../../core/region-classifier.js';
|
|
35
|
+
|
|
36
|
+
const SKIP_DIRS = new Set(['.git', 'node_modules', 'dist', 'build', '.next', '.cache', 'coverage']);
|
|
37
|
+
const CODE_EXTENSIONS = new Set(['.js', '.ts', '.jsx', '.tsx', '.mjs', '.cjs', '.py', '.go', '.rs']);
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* AssessAgent surveys a directory for existing files, headers, regions, and patterns.
|
|
41
|
+
* Must be called before any modification or creation operations.
|
|
42
|
+
*/
|
|
43
|
+
export class AssessAgent {
|
|
44
|
+
constructor(options = {}) {
|
|
45
|
+
this.rootDir = options.rootDir || process.cwd();
|
|
46
|
+
this.maxDepth = options.maxDepth || 10;
|
|
47
|
+
this.log = options.log || (() => {});
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Run assessment on the root directory.
|
|
52
|
+
* @returns {Promise<AssessmentResult>}
|
|
53
|
+
*/
|
|
54
|
+
async run() {
|
|
55
|
+
const startTime = Date.now();
|
|
56
|
+
const inventory = {
|
|
57
|
+
totalFiles: 0,
|
|
58
|
+
codeFiles: 0,
|
|
59
|
+
filesWithHeaders: 0,
|
|
60
|
+
filesWithoutHeaders: [],
|
|
61
|
+
regionMap: {},
|
|
62
|
+
tagMap: {},
|
|
63
|
+
duplicateTags: [],
|
|
64
|
+
missingHeaders: [],
|
|
65
|
+
structure: {},
|
|
66
|
+
};
|
|
67
|
+
|
|
68
|
+
await this._walk(this.rootDir, inventory, 0);
|
|
69
|
+
this._detectDuplicateTags(inventory);
|
|
70
|
+
|
|
71
|
+
return {
|
|
72
|
+
agent: 'assess-agent',
|
|
73
|
+
rootDir: this.rootDir,
|
|
74
|
+
durationMs: Date.now() - startTime,
|
|
75
|
+
inventory,
|
|
76
|
+
summary: this._buildSummary(inventory),
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
async _walk(dir, inventory, depth) {
|
|
81
|
+
if (depth > this.maxDepth) return;
|
|
82
|
+
|
|
83
|
+
let entries;
|
|
84
|
+
try {
|
|
85
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
86
|
+
} catch {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
for (const entry of entries) {
|
|
91
|
+
if (SKIP_DIRS.has(entry.name)) continue;
|
|
92
|
+
const fullPath = join(dir, entry.name);
|
|
93
|
+
const relPath = relative(this.rootDir, fullPath);
|
|
94
|
+
|
|
95
|
+
if (entry.isDirectory()) {
|
|
96
|
+
inventory.structure[relPath] = 'directory';
|
|
97
|
+
await this._walk(fullPath, inventory, depth + 1);
|
|
98
|
+
} else if (entry.isFile()) {
|
|
99
|
+
inventory.totalFiles++;
|
|
100
|
+
inventory.structure[relPath] = 'file';
|
|
101
|
+
|
|
102
|
+
if (CODE_EXTENSIONS.has(extname(entry.name))) {
|
|
103
|
+
inventory.codeFiles++;
|
|
104
|
+
await this._analyzeFile(fullPath, relPath, inventory);
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
async _analyzeFile(fullPath, relPath, inventory) {
|
|
111
|
+
let content;
|
|
112
|
+
try {
|
|
113
|
+
content = await readFile(fullPath, 'utf8');
|
|
114
|
+
} catch {
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
const header = parseHeader(content);
|
|
119
|
+
if (header) {
|
|
120
|
+
inventory.filesWithHeaders++;
|
|
121
|
+
|
|
122
|
+
if (header.region) {
|
|
123
|
+
inventory.regionMap[header.region] = (inventory.regionMap[header.region] || 0) + 1;
|
|
124
|
+
}
|
|
125
|
+
if (header.tag) {
|
|
126
|
+
if (!inventory.tagMap[header.tag]) inventory.tagMap[header.tag] = [];
|
|
127
|
+
inventory.tagMap[header.tag].push(relPath);
|
|
128
|
+
}
|
|
129
|
+
} else {
|
|
130
|
+
inventory.filesWithoutHeaders.push(relPath);
|
|
131
|
+
inventory.missingHeaders.push(relPath);
|
|
132
|
+
|
|
133
|
+
const regionResult = classifyRegion(relPath);
|
|
134
|
+
const region = regionResult.region;
|
|
135
|
+
inventory.regionMap[`UNCLASSIFIED.${region}`] = (inventory.regionMap[`UNCLASSIFIED.${region}`] || 0) + 1;
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
_detectDuplicateTags(inventory) {
|
|
140
|
+
for (const [tag, files] of Object.entries(inventory.tagMap)) {
|
|
141
|
+
if (files.length > 1) {
|
|
142
|
+
inventory.duplicateTags.push({ tag, files });
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
_buildSummary(inventory) {
|
|
148
|
+
const headerCoverage = inventory.codeFiles > 0
|
|
149
|
+
? Math.round((inventory.filesWithHeaders / inventory.codeFiles) * 100)
|
|
150
|
+
: 0;
|
|
151
|
+
|
|
152
|
+
return {
|
|
153
|
+
headerCoverage: `${headerCoverage}%`,
|
|
154
|
+
codeFiles: inventory.codeFiles,
|
|
155
|
+
totalFiles: inventory.totalFiles,
|
|
156
|
+
filesNeedingHeaders: inventory.missingHeaders.length,
|
|
157
|
+
duplicateTagCount: inventory.duplicateTags.length,
|
|
158
|
+
regionsFound: Object.keys(inventory.regionMap).filter(r => !r.startsWith('UNCLASSIFIED')).length,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
}
|
|
@@ -0,0 +1,185 @@
|
|
|
1
|
+
/*
|
|
2
|
+
LEEWAY HEADER — DO NOT REMOVE
|
|
3
|
+
|
|
4
|
+
REGION: AI.AGENT.GOVERNANCE
|
|
5
|
+
TAG: AI.AGENT.AUDIT.MAIN
|
|
6
|
+
|
|
7
|
+
COLOR_ONION_HEX:
|
|
8
|
+
NEON=#39FF14
|
|
9
|
+
FLUO=#0DFF94
|
|
10
|
+
PASTEL=#C7FFD8
|
|
11
|
+
|
|
12
|
+
ICON_ASCII:
|
|
13
|
+
family=lucide
|
|
14
|
+
glyph=clipboard-check
|
|
15
|
+
|
|
16
|
+
5WH:
|
|
17
|
+
WHAT = Audit agent — scores compliance, generates reports, and blocks unsafe changes
|
|
18
|
+
WHY = LEEWAY systems must be continuously validated to remain stable and governable
|
|
19
|
+
WHO = Rapid Web Development
|
|
20
|
+
WHERE = src/agents/governance/audit-agent.js
|
|
21
|
+
WHEN = 2026
|
|
22
|
+
HOW = Aggregates compliance scores across all files, produces JSON and text reports
|
|
23
|
+
|
|
24
|
+
AGENTS:
|
|
25
|
+
AUDIT
|
|
26
|
+
ASSESS
|
|
27
|
+
|
|
28
|
+
LICENSE:
|
|
29
|
+
MIT
|
|
30
|
+
*/
|
|
31
|
+
|
|
32
|
+
import { readdir, readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
33
|
+
import { join, relative, extname } from 'node:path';
|
|
34
|
+
import { scoreCompliance } from '../../core/compliance-scorer.js';
|
|
35
|
+
|
|
36
|
+
const SKIP_DIRS = new Set(['.git', 'node_modules', 'dist', 'build', '.next', '.cache', 'coverage']);
|
|
37
|
+
const CODE_EXTENSIONS = new Set(['.js', '.ts', '.jsx', '.tsx', '.mjs', '.cjs']);
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* AuditAgent scores the entire codebase for LEEWAY compliance and generates reports.
|
|
41
|
+
*/
|
|
42
|
+
export class AuditAgent {
|
|
43
|
+
constructor(options = {}) {
|
|
44
|
+
this.rootDir = options.rootDir || process.cwd();
|
|
45
|
+
this.outputDir = options.outputDir || join(this.rootDir, 'reports');
|
|
46
|
+
this.log = options.log || (() => {});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Run a full compliance audit.
|
|
51
|
+
* @returns {Promise<AuditReport>}
|
|
52
|
+
*/
|
|
53
|
+
async run() {
|
|
54
|
+
const startTime = Date.now();
|
|
55
|
+
const fileResults = [];
|
|
56
|
+
|
|
57
|
+
await this._walkAndScore(this.rootDir, fileResults);
|
|
58
|
+
|
|
59
|
+
const totalScore = fileResults.length > 0
|
|
60
|
+
? Math.round(fileResults.reduce((sum, r) => sum + r.score, 0) / fileResults.length)
|
|
61
|
+
: 0;
|
|
62
|
+
|
|
63
|
+
const report = {
|
|
64
|
+
agent: 'audit-agent',
|
|
65
|
+
rootDir: this.rootDir,
|
|
66
|
+
timestamp: new Date().toISOString(),
|
|
67
|
+
durationMs: Date.now() - startTime,
|
|
68
|
+
summary: {
|
|
69
|
+
totalFiles: fileResults.length,
|
|
70
|
+
averageScore: totalScore,
|
|
71
|
+
complianceLevel: this._getLevel(totalScore),
|
|
72
|
+
passing: fileResults.filter(r => r.score >= 60).length,
|
|
73
|
+
failing: fileResults.filter(r => r.score < 60).length,
|
|
74
|
+
platinum: fileResults.filter(r => r.score >= 95).length,
|
|
75
|
+
gold: fileResults.filter(r => r.score >= 80 && r.score < 95).length,
|
|
76
|
+
silver: fileResults.filter(r => r.score >= 60 && r.score < 80).length,
|
|
77
|
+
bronze: fileResults.filter(r => r.score >= 40 && r.score < 60).length,
|
|
78
|
+
none: fileResults.filter(r => r.score < 40).length,
|
|
79
|
+
},
|
|
80
|
+
files: fileResults.sort((a, b) => a.score - b.score),
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
return report;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Run audit and write reports to the output directory.
|
|
88
|
+
* @returns {Promise<AuditReport>}
|
|
89
|
+
*/
|
|
90
|
+
async runAndSave() {
|
|
91
|
+
const report = await this.run();
|
|
92
|
+
|
|
93
|
+
try {
|
|
94
|
+
await mkdir(this.outputDir, { recursive: true });
|
|
95
|
+
const jsonPath = join(this.outputDir, 'leeway-audit.json');
|
|
96
|
+
const textPath = join(this.outputDir, 'leeway-audit.txt');
|
|
97
|
+
await writeFile(jsonPath, JSON.stringify(report, null, 2), 'utf8');
|
|
98
|
+
await writeFile(textPath, this._formatTextReport(report), 'utf8');
|
|
99
|
+
report.savedTo = { json: jsonPath, text: textPath };
|
|
100
|
+
} catch (err) {
|
|
101
|
+
report.saveError = err.message;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return report;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
async _walkAndScore(dir, results, depth = 0) {
|
|
108
|
+
if (depth > 10) return;
|
|
109
|
+
|
|
110
|
+
let entries;
|
|
111
|
+
try {
|
|
112
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
113
|
+
} catch {
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
for (const entry of entries) {
|
|
118
|
+
if (SKIP_DIRS.has(entry.name)) continue;
|
|
119
|
+
const fullPath = join(dir, entry.name);
|
|
120
|
+
|
|
121
|
+
if (entry.isDirectory()) {
|
|
122
|
+
await this._walkAndScore(fullPath, results, depth + 1);
|
|
123
|
+
} else if (entry.isFile() && CODE_EXTENSIONS.has(extname(entry.name))) {
|
|
124
|
+
const relPath = relative(this.rootDir, fullPath);
|
|
125
|
+
try {
|
|
126
|
+
const content = await readFile(fullPath, 'utf8');
|
|
127
|
+
const result = scoreCompliance({ filePath: relPath, content });
|
|
128
|
+
results.push({ file: relPath, ...result });
|
|
129
|
+
} catch {
|
|
130
|
+
results.push({ file: relPath, score: 0, level: 'NONE', issues: ['Could not read file'] });
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
_getLevel(score) {
|
|
137
|
+
if (score >= 95) return 'PLATINUM';
|
|
138
|
+
if (score >= 80) return 'GOLD';
|
|
139
|
+
if (score >= 60) return 'SILVER';
|
|
140
|
+
if (score >= 40) return 'BRONZE';
|
|
141
|
+
return 'NONE';
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
_formatTextReport(report) {
|
|
145
|
+
const lines = [
|
|
146
|
+
'═══════════════════════════════════════════════════',
|
|
147
|
+
' LEEWAY™ COMPLIANCE AUDIT REPORT ',
|
|
148
|
+
'═══════════════════════════════════════════════════',
|
|
149
|
+
`Timestamp : ${report.timestamp}`,
|
|
150
|
+
`Root Dir : ${report.rootDir}`,
|
|
151
|
+
`Duration : ${report.durationMs}ms`,
|
|
152
|
+
'',
|
|
153
|
+
'── SUMMARY ─────────────────────────────────────────',
|
|
154
|
+
`Total Files : ${report.summary.totalFiles}`,
|
|
155
|
+
`Average Score : ${report.summary.averageScore}/100`,
|
|
156
|
+
`Compliance Level: ${report.summary.complianceLevel}`,
|
|
157
|
+
`Passing (≥60) : ${report.summary.passing}`,
|
|
158
|
+
`Failing (<60) : ${report.summary.failing}`,
|
|
159
|
+
'',
|
|
160
|
+
'── BREAKDOWN ───────────────────────────────────────',
|
|
161
|
+
` 🥇 Platinum (95-100): ${report.summary.platinum}`,
|
|
162
|
+
` 🥈 Gold (80-94) : ${report.summary.gold}`,
|
|
163
|
+
` 🥉 Silver (60-79) : ${report.summary.silver}`,
|
|
164
|
+
` 🔶 Bronze (40-59) : ${report.summary.bronze}`,
|
|
165
|
+
` ❌ None (0-39) : ${report.summary.none}`,
|
|
166
|
+
'',
|
|
167
|
+
'── FILES NEEDING ATTENTION ─────────────────────────',
|
|
168
|
+
];
|
|
169
|
+
|
|
170
|
+
const failing = report.files.filter(f => f.score < 60).slice(0, 20);
|
|
171
|
+
if (failing.length === 0) {
|
|
172
|
+
lines.push(' ✅ All files pass minimum compliance threshold');
|
|
173
|
+
} else {
|
|
174
|
+
for (const f of failing) {
|
|
175
|
+
lines.push(` [${f.score.toString().padStart(3)}] ${f.file}`);
|
|
176
|
+
for (const issue of (f.issues || []).slice(0, 3)) {
|
|
177
|
+
lines.push(` ↳ ${issue}`);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
lines.push('═══════════════════════════════════════════════════');
|
|
183
|
+
return lines.join('\n');
|
|
184
|
+
}
|
|
185
|
+
}
|