@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,106 @@
|
|
|
1
|
+
/*
|
|
2
|
+
LEEWAY HEADER — DO NOT REMOVE
|
|
3
|
+
|
|
4
|
+
REGION: MCP.AGENT.ENDPOINT
|
|
5
|
+
TAG: MCP.ENDPOINT.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=globe
|
|
15
|
+
|
|
16
|
+
5WH:
|
|
17
|
+
WHAT = Endpoint agent — detects existing health and HTTP endpoints safely
|
|
18
|
+
WHY = Prevents duplicate endpoint registration and detects port conflicts before startup
|
|
19
|
+
WHO = Rapid Web Development
|
|
20
|
+
WHERE = src/agents/mcp/endpoint-agent.js
|
|
21
|
+
WHEN = 2026
|
|
22
|
+
HOW = Reads manifest files and scans source for endpoint declarations
|
|
23
|
+
|
|
24
|
+
AGENTS:
|
|
25
|
+
ENDPOINT
|
|
26
|
+
TRANSPORT
|
|
27
|
+
PORT
|
|
28
|
+
|
|
29
|
+
LICENSE:
|
|
30
|
+
MIT
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { readFile } from 'node:fs/promises';
|
|
34
|
+
import { join } from 'node:path';
|
|
35
|
+
|
|
36
|
+
const ENDPOINT_PATTERNS = [
|
|
37
|
+
{ type: 'express', pattern: /app\.(get|post|put|delete|patch|use)\s*\(\s*['"`]([^'"`]+)['"`]/g },
|
|
38
|
+
{ type: 'fastify', pattern: /fastify\.(get|post|put|delete|patch)\s*\(\s*['"`]([^'"`]+)['"`]/g },
|
|
39
|
+
{ type: 'http', pattern: /createServer|http\.listen\s*\(\s*(\d+)/g },
|
|
40
|
+
];
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* EndpointAgent detects existing HTTP endpoints and health checks in source files.
|
|
44
|
+
*/
|
|
45
|
+
export class EndpointAgent {
|
|
46
|
+
constructor(options = {}) {
|
|
47
|
+
this.rootDir = options.rootDir || process.cwd();
|
|
48
|
+
this.knownEndpoints = new Map();
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
/**
|
|
52
|
+
* Scan a file for endpoint declarations.
|
|
53
|
+
*
|
|
54
|
+
* @param {string} filePath
|
|
55
|
+
* @returns {Promise<EndpointScanResult>}
|
|
56
|
+
*/
|
|
57
|
+
async scanFile(filePath) {
|
|
58
|
+
const fullPath = filePath.startsWith('/') ? filePath : join(this.rootDir, filePath);
|
|
59
|
+
let content = '';
|
|
60
|
+
try {
|
|
61
|
+
content = await readFile(fullPath, 'utf8');
|
|
62
|
+
} catch {
|
|
63
|
+
return { file: filePath, endpoints: [], status: 'error' };
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const endpoints = [];
|
|
67
|
+
for (const { type, pattern } of ENDPOINT_PATTERNS) {
|
|
68
|
+
const regex = new RegExp(pattern.source, pattern.flags);
|
|
69
|
+
let match;
|
|
70
|
+
while ((match = regex.exec(content)) !== null) {
|
|
71
|
+
endpoints.push({
|
|
72
|
+
type,
|
|
73
|
+
method: match[1]?.toUpperCase() || 'UNKNOWN',
|
|
74
|
+
path: match[2] || '/',
|
|
75
|
+
line: content.slice(0, match.index).split('\n').length,
|
|
76
|
+
});
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
return { file: filePath, endpoints, status: 'scanned' };
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Check for duplicate endpoints across multiple scan results.
|
|
85
|
+
*
|
|
86
|
+
* @param {EndpointScanResult[]} scanResults
|
|
87
|
+
* @returns {{ duplicates: object[], unique: object[] }}
|
|
88
|
+
*/
|
|
89
|
+
detectDuplicates(scanResults) {
|
|
90
|
+
const seen = new Map();
|
|
91
|
+
const duplicates = [];
|
|
92
|
+
|
|
93
|
+
for (const result of scanResults) {
|
|
94
|
+
for (const ep of result.endpoints) {
|
|
95
|
+
const key = `${ep.method}:${ep.path}`;
|
|
96
|
+
if (seen.has(key)) {
|
|
97
|
+
duplicates.push({ key, files: [seen.get(key).file, result.file], endpoint: ep });
|
|
98
|
+
} else {
|
|
99
|
+
seen.set(key, { ...ep, file: result.file });
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return { duplicates, unique: Array.from(seen.values()) };
|
|
105
|
+
}
|
|
106
|
+
}
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
/*
|
|
2
|
+
LEEWAY HEADER — DO NOT REMOVE
|
|
3
|
+
|
|
4
|
+
REGION: MCP.AGENT.ENV
|
|
5
|
+
TAG: MCP.ENV.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=settings
|
|
15
|
+
|
|
16
|
+
5WH:
|
|
17
|
+
WHAT = Env agent — validates required vs optional environment variables
|
|
18
|
+
WHY = Missing or misconfigured env vars cause runtime failures that are hard to debug
|
|
19
|
+
WHO = Rapid Web Development
|
|
20
|
+
WHERE = src/agents/mcp/env-agent.js
|
|
21
|
+
WHEN = 2026
|
|
22
|
+
HOW = Reads .leeway/env-schema.json and validates against process.env
|
|
23
|
+
|
|
24
|
+
AGENTS:
|
|
25
|
+
ENV
|
|
26
|
+
RUNTIME
|
|
27
|
+
ALIGN
|
|
28
|
+
|
|
29
|
+
LICENSE:
|
|
30
|
+
MIT
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { readFile } from 'node:fs/promises';
|
|
34
|
+
import { join } from 'node:path';
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* EnvAgent validates environment variable presence and format.
|
|
38
|
+
*/
|
|
39
|
+
export class EnvAgent {
|
|
40
|
+
constructor(options = {}) {
|
|
41
|
+
this.rootDir = options.rootDir || process.cwd();
|
|
42
|
+
this.schema = null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Load the env schema from .leeway/env-schema.json or .env.schema.json
|
|
47
|
+
*/
|
|
48
|
+
async loadSchema() {
|
|
49
|
+
const paths = [
|
|
50
|
+
join(this.rootDir, '.leeway', 'env-schema.json'),
|
|
51
|
+
join(this.rootDir, '.env.schema.json'),
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
for (const p of paths) {
|
|
55
|
+
try {
|
|
56
|
+
const raw = await readFile(p, 'utf8');
|
|
57
|
+
this.schema = JSON.parse(raw);
|
|
58
|
+
return this.schema;
|
|
59
|
+
} catch {
|
|
60
|
+
continue;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
this.schema = { required: [], optional: [] };
|
|
65
|
+
return this.schema;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Validate current environment against the schema.
|
|
70
|
+
*
|
|
71
|
+
* @param {object} [env] - Environment to validate (defaults to process.env)
|
|
72
|
+
* @returns {Promise<EnvValidationResult>}
|
|
73
|
+
*/
|
|
74
|
+
async validate(env = process.env) {
|
|
75
|
+
if (!this.schema) await this.loadSchema();
|
|
76
|
+
|
|
77
|
+
const missing = [];
|
|
78
|
+
const present = [];
|
|
79
|
+
const warnings = [];
|
|
80
|
+
|
|
81
|
+
for (const varDef of this.schema.required || []) {
|
|
82
|
+
const name = typeof varDef === 'string' ? varDef : varDef.name;
|
|
83
|
+
const pattern = typeof varDef === 'object' ? varDef.pattern : null;
|
|
84
|
+
|
|
85
|
+
if (!env[name]) {
|
|
86
|
+
missing.push(name);
|
|
87
|
+
} else {
|
|
88
|
+
if (pattern && !new RegExp(pattern).test(env[name])) {
|
|
89
|
+
warnings.push(`${name} does not match expected pattern`);
|
|
90
|
+
}
|
|
91
|
+
present.push(name);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
for (const varDef of this.schema.optional || []) {
|
|
96
|
+
const name = typeof varDef === 'string' ? varDef : varDef.name;
|
|
97
|
+
if (!env[name]) {
|
|
98
|
+
warnings.push(`Optional env var ${name} is not set`);
|
|
99
|
+
} else {
|
|
100
|
+
present.push(name);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
valid: missing.length === 0,
|
|
106
|
+
missing,
|
|
107
|
+
present,
|
|
108
|
+
warnings,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/*
|
|
2
|
+
LEEWAY HEADER — DO NOT REMOVE
|
|
3
|
+
|
|
4
|
+
REGION: MCP.AGENT.HEALTH
|
|
5
|
+
TAG: MCP.HEALTH.AGENT.LITE
|
|
6
|
+
|
|
7
|
+
COLOR_ONION_HEX:
|
|
8
|
+
NEON=#39FF14
|
|
9
|
+
FLUO=#0DFF94
|
|
10
|
+
PASTEL=#C7FFD8
|
|
11
|
+
|
|
12
|
+
ICON_ASCII:
|
|
13
|
+
family=lucide
|
|
14
|
+
glyph=heart-pulse
|
|
15
|
+
|
|
16
|
+
5WH:
|
|
17
|
+
WHAT = Health agent lite — runs lightweight startup and readiness checks
|
|
18
|
+
WHY = Services must pass readiness gates before routing traffic or accepting requests
|
|
19
|
+
WHO = Rapid Web Development
|
|
20
|
+
WHERE = src/agents/mcp/health-agent-lite.js
|
|
21
|
+
WHEN = 2026
|
|
22
|
+
HOW = Runs a series of configurable health checks, returns pass/fail with details
|
|
23
|
+
|
|
24
|
+
AGENTS:
|
|
25
|
+
HEALTH
|
|
26
|
+
RUNTIME
|
|
27
|
+
ENV
|
|
28
|
+
|
|
29
|
+
LICENSE:
|
|
30
|
+
MIT
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* HealthAgentLite performs lightweight startup and readiness checks.
|
|
35
|
+
*/
|
|
36
|
+
export class HealthAgentLite {
|
|
37
|
+
constructor(options = {}) {
|
|
38
|
+
this.checks = options.checks || [];
|
|
39
|
+
this.timeout = options.timeout || 5000;
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Register a health check function.
|
|
44
|
+
*
|
|
45
|
+
* @param {string} name
|
|
46
|
+
* @param {() => Promise<{ healthy: boolean, details?: string }>} fn
|
|
47
|
+
*/
|
|
48
|
+
register(name, fn) {
|
|
49
|
+
this.checks.push({ name, fn });
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Run all registered health checks.
|
|
54
|
+
* @returns {Promise<HealthReport>}
|
|
55
|
+
*/
|
|
56
|
+
async run() {
|
|
57
|
+
const results = [];
|
|
58
|
+
let allHealthy = true;
|
|
59
|
+
|
|
60
|
+
for (const check of this.checks) {
|
|
61
|
+
const startTime = Date.now();
|
|
62
|
+
try {
|
|
63
|
+
const result = await Promise.race([
|
|
64
|
+
check.fn(),
|
|
65
|
+
new Promise((_, reject) => setTimeout(() => reject(new Error('timeout')), this.timeout)),
|
|
66
|
+
]);
|
|
67
|
+
|
|
68
|
+
const duration = Date.now() - startTime;
|
|
69
|
+
results.push({
|
|
70
|
+
name: check.name,
|
|
71
|
+
healthy: result.healthy,
|
|
72
|
+
details: result.details || null,
|
|
73
|
+
durationMs: duration,
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
if (!result.healthy) allHealthy = false;
|
|
77
|
+
} catch (err) {
|
|
78
|
+
results.push({
|
|
79
|
+
name: check.name,
|
|
80
|
+
healthy: false,
|
|
81
|
+
details: err.message,
|
|
82
|
+
durationMs: Date.now() - startTime,
|
|
83
|
+
});
|
|
84
|
+
allHealthy = false;
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
return {
|
|
89
|
+
healthy: allHealthy,
|
|
90
|
+
timestamp: new Date().toISOString(),
|
|
91
|
+
checks: results,
|
|
92
|
+
summary: `${results.filter(r => r.healthy).length}/${results.length} checks passed`,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Get built-in system health checks.
|
|
98
|
+
* @returns {HealthAgentLite}
|
|
99
|
+
*/
|
|
100
|
+
static withSystemChecks(options = {}) {
|
|
101
|
+
const agent = new HealthAgentLite(options);
|
|
102
|
+
|
|
103
|
+
agent.register('memory', async () => {
|
|
104
|
+
const { heapUsed, heapTotal } = process.memoryUsage();
|
|
105
|
+
const usagePercent = Math.round((heapUsed / heapTotal) * 100);
|
|
106
|
+
return {
|
|
107
|
+
healthy: usagePercent < 90,
|
|
108
|
+
details: `Heap usage: ${usagePercent}% (${Math.round(heapUsed / 1024 / 1024)}MB / ${Math.round(heapTotal / 1024 / 1024)}MB)`,
|
|
109
|
+
};
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
agent.register('uptime', async () => ({
|
|
113
|
+
healthy: true,
|
|
114
|
+
details: `Process uptime: ${Math.round(process.uptime())}s`,
|
|
115
|
+
}));
|
|
116
|
+
|
|
117
|
+
return agent;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
/*
|
|
2
|
+
LEEWAY HEADER — DO NOT REMOVE
|
|
3
|
+
|
|
4
|
+
REGION: MCP.AGENT.MANIFEST
|
|
5
|
+
TAG: MCP.MANIFEST.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=file-check
|
|
15
|
+
|
|
16
|
+
5WH:
|
|
17
|
+
WHAT = Manifest agent — validates MCP manifest completeness and tool definitions
|
|
18
|
+
WHY = Incomplete or malformed manifests prevent MCP servers from being discovered and used
|
|
19
|
+
WHO = Rapid Web Development
|
|
20
|
+
WHERE = src/agents/mcp/manifest-agent.js
|
|
21
|
+
WHEN = 2026
|
|
22
|
+
HOW = Reads manifest JSON, validates required fields and tool schemas
|
|
23
|
+
|
|
24
|
+
AGENTS:
|
|
25
|
+
MANIFEST
|
|
26
|
+
TRANSPORT
|
|
27
|
+
ENDPOINT
|
|
28
|
+
|
|
29
|
+
LICENSE:
|
|
30
|
+
MIT
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { readFile } from 'node:fs/promises';
|
|
34
|
+
import { join } from 'node:path';
|
|
35
|
+
|
|
36
|
+
const REQUIRED_MANIFEST_FIELDS = ['name', 'version', 'description'];
|
|
37
|
+
const REQUIRED_TOOL_FIELDS = ['name', 'description', 'inputSchema'];
|
|
38
|
+
|
|
39
|
+
/**
|
|
40
|
+
* ManifestAgent validates MCP manifest files for completeness.
|
|
41
|
+
*/
|
|
42
|
+
export class ManifestAgent {
|
|
43
|
+
constructor(options = {}) {
|
|
44
|
+
this.rootDir = options.rootDir || process.cwd();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Validate a manifest object or load from file.
|
|
49
|
+
*
|
|
50
|
+
* @param {string | object} manifestOrPath - Manifest object or path to manifest file
|
|
51
|
+
* @returns {Promise<ManifestValidationResult>}
|
|
52
|
+
*/
|
|
53
|
+
async validate(manifestOrPath) {
|
|
54
|
+
let manifest = manifestOrPath;
|
|
55
|
+
|
|
56
|
+
if (typeof manifestOrPath === 'string') {
|
|
57
|
+
const fullPath = manifestOrPath.startsWith('/') ? manifestOrPath : join(this.rootDir, manifestOrPath);
|
|
58
|
+
try {
|
|
59
|
+
const raw = await readFile(fullPath, 'utf8');
|
|
60
|
+
manifest = JSON.parse(raw);
|
|
61
|
+
} catch (err) {
|
|
62
|
+
return { valid: false, issues: [`Cannot load manifest: ${err.message}`] };
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
const issues = [];
|
|
67
|
+
|
|
68
|
+
for (const field of REQUIRED_MANIFEST_FIELDS) {
|
|
69
|
+
if (!manifest[field]) issues.push(`Missing required field: ${field}`);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
if (manifest.tools && Array.isArray(manifest.tools)) {
|
|
73
|
+
for (const tool of manifest.tools) {
|
|
74
|
+
for (const field of REQUIRED_TOOL_FIELDS) {
|
|
75
|
+
if (!tool[field]) issues.push(`Tool "${tool.name || 'unknown'}" missing field: ${field}`);
|
|
76
|
+
}
|
|
77
|
+
if (tool.inputSchema && typeof tool.inputSchema !== 'object') {
|
|
78
|
+
issues.push(`Tool "${tool.name}" inputSchema must be an object`);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
} else if (manifest.tools !== undefined) {
|
|
82
|
+
issues.push('manifest.tools must be an array');
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
return { valid: issues.length === 0, issues, manifest };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
@@ -0,0 +1,125 @@
|
|
|
1
|
+
/*
|
|
2
|
+
LEEWAY HEADER — DO NOT REMOVE
|
|
3
|
+
|
|
4
|
+
REGION: MCP.AGENT.PORT
|
|
5
|
+
TAG: MCP.PORT.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=radio
|
|
15
|
+
|
|
16
|
+
5WH:
|
|
17
|
+
WHAT = Port agent — assigns and validates ports from a registry to prevent collisions
|
|
18
|
+
WHY = Port collisions cause service startup failures; governance requires a centralized port registry
|
|
19
|
+
WHO = Rapid Web Development
|
|
20
|
+
WHERE = src/agents/mcp/port-agent.js
|
|
21
|
+
WHEN = 2026
|
|
22
|
+
HOW = Maintains a port registry in .leeway/ports.json, validates before assignment
|
|
23
|
+
|
|
24
|
+
AGENTS:
|
|
25
|
+
PORT
|
|
26
|
+
ENDPOINT
|
|
27
|
+
TRANSPORT
|
|
28
|
+
|
|
29
|
+
LICENSE:
|
|
30
|
+
MIT
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
34
|
+
import { join } from 'node:path';
|
|
35
|
+
|
|
36
|
+
const DEFAULT_PORT_RANGES = {
|
|
37
|
+
UI: [3000, 3099],
|
|
38
|
+
API: [4000, 4099],
|
|
39
|
+
AI: [5000, 5099],
|
|
40
|
+
MCP: [6000, 6099],
|
|
41
|
+
DATA: [7000, 7099],
|
|
42
|
+
UTIL: [8000, 8099],
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* PortAgent manages port assignments to prevent collisions.
|
|
47
|
+
*/
|
|
48
|
+
export class PortAgent {
|
|
49
|
+
constructor(options = {}) {
|
|
50
|
+
this.rootDir = options.rootDir || process.cwd();
|
|
51
|
+
this.registryPath = join(this.rootDir, '.leeway', 'ports.json');
|
|
52
|
+
this.registry = null;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
async _loadRegistry() {
|
|
56
|
+
if (this.registry) return this.registry;
|
|
57
|
+
try {
|
|
58
|
+
const raw = await readFile(this.registryPath, 'utf8');
|
|
59
|
+
this.registry = JSON.parse(raw);
|
|
60
|
+
} catch {
|
|
61
|
+
this.registry = { assignments: {}, reserved: [22, 80, 443, 8080, 8443] };
|
|
62
|
+
}
|
|
63
|
+
return this.registry;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async _saveRegistry() {
|
|
67
|
+
await mkdir(join(this.rootDir, '.leeway'), { recursive: true });
|
|
68
|
+
await writeFile(this.registryPath, JSON.stringify(this.registry, null, 2), 'utf8');
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Check if a port is available (not assigned).
|
|
73
|
+
*
|
|
74
|
+
* @param {number} port
|
|
75
|
+
* @returns {Promise<{ available: boolean, assignedTo?: string }>}
|
|
76
|
+
*/
|
|
77
|
+
async checkPort(port) {
|
|
78
|
+
const reg = await this._loadRegistry();
|
|
79
|
+
const assignedTo = Object.entries(reg.assignments).find(([, p]) => p === port)?.[0];
|
|
80
|
+
const isReserved = reg.reserved.includes(port);
|
|
81
|
+
|
|
82
|
+
return {
|
|
83
|
+
available: !assignedTo && !isReserved,
|
|
84
|
+
assignedTo: assignedTo || (isReserved ? 'SYSTEM_RESERVED' : null),
|
|
85
|
+
};
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
/**
|
|
89
|
+
* Assign a port to a service.
|
|
90
|
+
*
|
|
91
|
+
* @param {string} serviceName
|
|
92
|
+
* @param {number} [preferredPort]
|
|
93
|
+
* @param {string} [region]
|
|
94
|
+
* @returns {Promise<{ port: number, assigned: boolean, conflict?: string }>}
|
|
95
|
+
*/
|
|
96
|
+
async assignPort(serviceName, preferredPort, region = 'UTIL') {
|
|
97
|
+
const reg = await this._loadRegistry();
|
|
98
|
+
|
|
99
|
+
if (reg.assignments[serviceName]) {
|
|
100
|
+
return { port: reg.assignments[serviceName], assigned: false, reason: 'Already assigned' };
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
let port = preferredPort;
|
|
104
|
+
if (port) {
|
|
105
|
+
const check = await this.checkPort(port);
|
|
106
|
+
if (!check.available) {
|
|
107
|
+
return { port: null, assigned: false, conflict: `Port ${port} already assigned to ${check.assignedTo}` };
|
|
108
|
+
}
|
|
109
|
+
} else {
|
|
110
|
+
const [rangeStart, rangeEnd] = DEFAULT_PORT_RANGES[region] || DEFAULT_PORT_RANGES.UTIL;
|
|
111
|
+
for (let p = rangeStart; p <= rangeEnd; p++) {
|
|
112
|
+
const check = await this.checkPort(p);
|
|
113
|
+
if (check.available) { port = p; break; }
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
if (!port) {
|
|
118
|
+
return { port: null, assigned: false, reason: 'No available port in range' };
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
reg.assignments[serviceName] = port;
|
|
122
|
+
await this._saveRegistry();
|
|
123
|
+
return { port, assigned: true };
|
|
124
|
+
}
|
|
125
|
+
}
|
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
/*
|
|
2
|
+
LEEWAY HEADER — DO NOT REMOVE
|
|
3
|
+
|
|
4
|
+
REGION: MCP.AGENT.PROCESS
|
|
5
|
+
TAG: MCP.PROCESS.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=cpu
|
|
15
|
+
|
|
16
|
+
5WH:
|
|
17
|
+
WHAT = Process agent — handles process ownership, shutdown sequencing, and zombie cleanup
|
|
18
|
+
WHY = Unmanaged processes cause resource leaks, port conflicts, and failed restarts
|
|
19
|
+
WHO = Rapid Web Development
|
|
20
|
+
WHERE = src/agents/mcp/process-agent.js
|
|
21
|
+
WHEN = 2026
|
|
22
|
+
HOW = Tracks PID registry in .leeway/processes.json, validates liveness, signals shutdown
|
|
23
|
+
|
|
24
|
+
AGENTS:
|
|
25
|
+
PROCESS
|
|
26
|
+
PORT
|
|
27
|
+
HEALTH
|
|
28
|
+
|
|
29
|
+
LICENSE:
|
|
30
|
+
MIT
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
34
|
+
import { join } from 'node:path';
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* ProcessAgent tracks and manages process ownership and lifecycle.
|
|
38
|
+
*/
|
|
39
|
+
export class ProcessAgent {
|
|
40
|
+
constructor(options = {}) {
|
|
41
|
+
this.rootDir = options.rootDir || process.cwd();
|
|
42
|
+
this.registryPath = join(this.rootDir, '.leeway', 'processes.json');
|
|
43
|
+
this.registry = null;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
async _load() {
|
|
47
|
+
if (this.registry) return this.registry;
|
|
48
|
+
try {
|
|
49
|
+
const raw = await readFile(this.registryPath, 'utf8');
|
|
50
|
+
this.registry = JSON.parse(raw);
|
|
51
|
+
} catch {
|
|
52
|
+
this.registry = { processes: {} };
|
|
53
|
+
}
|
|
54
|
+
return this.registry;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
async _save() {
|
|
58
|
+
await mkdir(join(this.rootDir, '.leeway'), { recursive: true });
|
|
59
|
+
await writeFile(this.registryPath, JSON.stringify(this.registry, null, 2), 'utf8');
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
/**
|
|
63
|
+
* Register a process.
|
|
64
|
+
*
|
|
65
|
+
* @param {string} name - Process identifier
|
|
66
|
+
* @param {number} pid - OS process ID
|
|
67
|
+
* @param {object} [meta] - Additional metadata
|
|
68
|
+
*/
|
|
69
|
+
async register(name, pid, meta = {}) {
|
|
70
|
+
const reg = await this._load();
|
|
71
|
+
reg.processes[name] = { pid, registered: new Date().toISOString(), ...meta };
|
|
72
|
+
await this._save();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Check if a process is alive.
|
|
77
|
+
*
|
|
78
|
+
* @param {string} name
|
|
79
|
+
* @returns {Promise<{ alive: boolean, pid: number | null }>}
|
|
80
|
+
*/
|
|
81
|
+
async isAlive(name) {
|
|
82
|
+
const reg = await this._load();
|
|
83
|
+
const entry = reg.processes[name];
|
|
84
|
+
if (!entry) return { alive: false, pid: null };
|
|
85
|
+
|
|
86
|
+
try {
|
|
87
|
+
process.kill(entry.pid, 0);
|
|
88
|
+
return { alive: true, pid: entry.pid };
|
|
89
|
+
} catch {
|
|
90
|
+
return { alive: false, pid: entry.pid };
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Clean up zombie entries (processes that are no longer alive).
|
|
96
|
+
* @returns {Promise<string[]>} - Names of cleaned processes
|
|
97
|
+
*/
|
|
98
|
+
async cleanZombies() {
|
|
99
|
+
const reg = await this._load();
|
|
100
|
+
const cleaned = [];
|
|
101
|
+
|
|
102
|
+
for (const [name, entry] of Object.entries(reg.processes)) {
|
|
103
|
+
try {
|
|
104
|
+
process.kill(entry.pid, 0);
|
|
105
|
+
} catch {
|
|
106
|
+
delete reg.processes[name];
|
|
107
|
+
cleaned.push(name);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (cleaned.length > 0) await this._save();
|
|
112
|
+
return cleaned;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Deregister a process entry.
|
|
117
|
+
* @param {string} name
|
|
118
|
+
*/
|
|
119
|
+
async deregister(name) {
|
|
120
|
+
const reg = await this._load();
|
|
121
|
+
delete reg.processes[name];
|
|
122
|
+
await this._save();
|
|
123
|
+
}
|
|
124
|
+
}
|