@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,61 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "leeway-sdk",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "LEEWAY™ — Logically Enhanced Engineering Web Architecture Yield. A lightweight autonomous code governance SDK that embeds identity, safety, readability, and repair assistance directly into the developer workflow.",
|
|
5
|
+
"main": "src/index.js",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"bin": {
|
|
8
|
+
"leeway": "src/cli/leeway.js"
|
|
9
|
+
},
|
|
10
|
+
"scripts": {
|
|
11
|
+
"start": "node src/cli/leeway.js",
|
|
12
|
+
"doctor": "node src/cli/leeway.js doctor",
|
|
13
|
+
"audit": "node src/cli/leeway.js audit",
|
|
14
|
+
"assess": "node src/cli/leeway.js assess",
|
|
15
|
+
"test": "node --test \"src/**/*.test.js\""
|
|
16
|
+
},
|
|
17
|
+
"keywords": [
|
|
18
|
+
"leeway",
|
|
19
|
+
"governance",
|
|
20
|
+
"ai-native",
|
|
21
|
+
"mcp",
|
|
22
|
+
"agents",
|
|
23
|
+
"llm",
|
|
24
|
+
"code-standards",
|
|
25
|
+
"sdk",
|
|
26
|
+
"autonomous",
|
|
27
|
+
"architecture"
|
|
28
|
+
],
|
|
29
|
+
"author": "Rapid Web Development",
|
|
30
|
+
"license": "MIT",
|
|
31
|
+
"engines": {
|
|
32
|
+
"node": ">=18.0.0"
|
|
33
|
+
},
|
|
34
|
+
"repository": {
|
|
35
|
+
"type": "git",
|
|
36
|
+
"url": "git+https://github.com/4citeB4U/LeeWay-Standards.git"
|
|
37
|
+
},
|
|
38
|
+
"bugs": {
|
|
39
|
+
"url": "https://github.com/4citeB4U/LeeWay-Standards/issues"
|
|
40
|
+
},
|
|
41
|
+
"homepage": "https://github.com/4citeB4U/LeeWay-Standards#readme",
|
|
42
|
+
"publishConfig": {
|
|
43
|
+
"access": "public"
|
|
44
|
+
},
|
|
45
|
+
"files": [
|
|
46
|
+
"src/",
|
|
47
|
+
"schemas/",
|
|
48
|
+
".leeway/",
|
|
49
|
+
"README.md",
|
|
50
|
+
"LICENSE",
|
|
51
|
+
"package.json"
|
|
52
|
+
],
|
|
53
|
+
"exports": {
|
|
54
|
+
".": "./src/index.js",
|
|
55
|
+
"./agents/*": "./src/agents/*",
|
|
56
|
+
"./core/*": "./src/core/*"
|
|
57
|
+
},
|
|
58
|
+
"dependencies": {
|
|
59
|
+
"leeway-sdk": "^1.0.1"
|
|
60
|
+
}
|
|
61
|
+
}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://leeway-standards.dev/schemas/leeway-config.schema.json",
|
|
4
|
+
"title": "LEEWAY SDK Configuration Schema",
|
|
5
|
+
"description": "Validates the .leeway/config.json project configuration file",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"properties": {
|
|
8
|
+
"version": {
|
|
9
|
+
"type": "string",
|
|
10
|
+
"description": "LEEWAY standard version this project targets",
|
|
11
|
+
"default": "1.0.0"
|
|
12
|
+
},
|
|
13
|
+
"project": {
|
|
14
|
+
"type": "object",
|
|
15
|
+
"required": ["name"],
|
|
16
|
+
"properties": {
|
|
17
|
+
"name": { "type": "string" },
|
|
18
|
+
"author": { "type": "string" },
|
|
19
|
+
"license": { "type": "string", "default": "MIT" }
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"rootDir": {
|
|
23
|
+
"type": "string",
|
|
24
|
+
"description": "Root directory for LEEWAY governance (relative to config file)",
|
|
25
|
+
"default": "src"
|
|
26
|
+
},
|
|
27
|
+
"agents": {
|
|
28
|
+
"type": "object",
|
|
29
|
+
"description": "Agent-specific configuration overrides",
|
|
30
|
+
"properties": {
|
|
31
|
+
"assess": { "type": "object" },
|
|
32
|
+
"align": { "type": "object", "properties": { "dryRun": { "type": "boolean", "default": true } } },
|
|
33
|
+
"audit": { "type": "object", "properties": { "outputDir": { "type": "string", "default": "reports" } } },
|
|
34
|
+
"secret-scan": { "type": "object" },
|
|
35
|
+
"health": { "type": "object", "properties": { "timeout": { "type": "number", "default": 5000 } } }
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"policies": {
|
|
39
|
+
"type": "object",
|
|
40
|
+
"description": "Policy enforcement overrides",
|
|
41
|
+
"additionalProperties": {
|
|
42
|
+
"type": "object",
|
|
43
|
+
"properties": {
|
|
44
|
+
"enforced": { "type": "boolean" },
|
|
45
|
+
"description": { "type": "string" }
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
},
|
|
49
|
+
"compliance": {
|
|
50
|
+
"type": "object",
|
|
51
|
+
"properties": {
|
|
52
|
+
"minimumScore": {
|
|
53
|
+
"type": "number",
|
|
54
|
+
"minimum": 0,
|
|
55
|
+
"maximum": 100,
|
|
56
|
+
"default": 60,
|
|
57
|
+
"description": "Minimum average compliance score to pass audit"
|
|
58
|
+
},
|
|
59
|
+
"blockOnFail": {
|
|
60
|
+
"type": "boolean",
|
|
61
|
+
"default": false,
|
|
62
|
+
"description": "Exit with error code if compliance is below minimum"
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
},
|
|
66
|
+
"discovery": {
|
|
67
|
+
"type": "object",
|
|
68
|
+
"properties": {
|
|
69
|
+
"baseUrl": { "type": "string", "format": "uri" },
|
|
70
|
+
"generateSitemap": { "type": "boolean", "default": false },
|
|
71
|
+
"generateSchema": { "type": "boolean", "default": false }
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"ignore": {
|
|
75
|
+
"type": "array",
|
|
76
|
+
"description": "Glob patterns to ignore during governance scans",
|
|
77
|
+
"items": { "type": "string" },
|
|
78
|
+
"default": ["node_modules/**", "dist/**", "build/**", ".next/**", "coverage/**"]
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"$id": "https://leeway-standards.dev/schemas/leeway-header.schema.json",
|
|
4
|
+
"title": "LEEWAY Header Schema",
|
|
5
|
+
"description": "Validates the LEEWAY identity header structure embedded in source files",
|
|
6
|
+
"type": "object",
|
|
7
|
+
"required": ["region", "tag", "fiveWH", "license"],
|
|
8
|
+
"properties": {
|
|
9
|
+
"region": {
|
|
10
|
+
"type": "string",
|
|
11
|
+
"description": "System region in dot-notation: DOMAIN.SUBDOMAIN.CONTEXT",
|
|
12
|
+
"pattern": "^[A-Z][A-Z0-9]*(\\.[A-Z][A-Z0-9]*)*$",
|
|
13
|
+
"examples": ["UI.APP.SURFACE", "CORE.SDK.ENTRY", "AI.AGENT.GOVERNANCE"]
|
|
14
|
+
},
|
|
15
|
+
"tag": {
|
|
16
|
+
"type": "string",
|
|
17
|
+
"description": "Discoverable tag in DOMAIN.SUBDOMAIN.ASSET.PURPOSE format",
|
|
18
|
+
"pattern": "^[A-Z][A-Z0-9]*(\\.[A-Z][A-Z0-9]*){2,}$",
|
|
19
|
+
"examples": ["UI.COMPONENT.NEXUS.BUTTON", "AI.AGENT.ASSESS.MAIN"]
|
|
20
|
+
},
|
|
21
|
+
"colorOnionHex": {
|
|
22
|
+
"type": "object",
|
|
23
|
+
"description": "Color classification for system layer visualization",
|
|
24
|
+
"properties": {
|
|
25
|
+
"NEON": { "type": "string", "pattern": "^#[0-9A-Fa-f]{6}$" },
|
|
26
|
+
"FLUO": { "type": "string", "pattern": "^#[0-9A-Fa-f]{6}$" },
|
|
27
|
+
"PASTEL": { "type": "string", "pattern": "^#[0-9A-Fa-f]{6}$" }
|
|
28
|
+
}
|
|
29
|
+
},
|
|
30
|
+
"iconAscii": {
|
|
31
|
+
"type": "object",
|
|
32
|
+
"description": "Icon reference for IDE and tooling visualization",
|
|
33
|
+
"properties": {
|
|
34
|
+
"family": { "type": "string", "enum": ["lucide", "heroicons", "material", "custom"] },
|
|
35
|
+
"glyph": { "type": "string" }
|
|
36
|
+
}
|
|
37
|
+
},
|
|
38
|
+
"fiveWH": {
|
|
39
|
+
"type": "object",
|
|
40
|
+
"description": "The 5WH identity block — answers fundamental questions about the file",
|
|
41
|
+
"required": ["WHAT", "WHY", "WHO", "WHERE", "WHEN", "HOW"],
|
|
42
|
+
"properties": {
|
|
43
|
+
"WHAT": { "type": "string", "description": "What does this file do?" },
|
|
44
|
+
"WHY": { "type": "string", "description": "Why does this file exist?" },
|
|
45
|
+
"WHO": { "type": "string", "description": "Who created or owns this file?" },
|
|
46
|
+
"WHERE": { "type": "string", "description": "Where does this file live in the system?" },
|
|
47
|
+
"WHEN": { "type": "string", "description": "When was this file created or last reviewed?" },
|
|
48
|
+
"HOW": { "type": "string", "description": "How does this file operate?" }
|
|
49
|
+
}
|
|
50
|
+
},
|
|
51
|
+
"agents": {
|
|
52
|
+
"type": "array",
|
|
53
|
+
"description": "List of LEEWAY agents authorized to act on this file",
|
|
54
|
+
"items": { "type": "string" },
|
|
55
|
+
"examples": [["ASSESS", "ALIGN", "AUDIT"]]
|
|
56
|
+
},
|
|
57
|
+
"license": {
|
|
58
|
+
"type": "string",
|
|
59
|
+
"description": "License identifier for this file",
|
|
60
|
+
"examples": ["MIT", "Apache-2.0", "GPL-3.0", "PROPRIETARY"]
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
/*
|
|
2
|
+
LEEWAY HEADER — DO NOT REMOVE
|
|
3
|
+
|
|
4
|
+
REGION: DOCS.AGENT.ARCHITECTURE
|
|
5
|
+
TAG: DOCS.STANDARD.ARCHITECTURE.MAP
|
|
6
|
+
|
|
7
|
+
COLOR_ONION_HEX:
|
|
8
|
+
NEON=#39FF14
|
|
9
|
+
FLUO=#0DFF94
|
|
10
|
+
PASTEL=#C7FFD8
|
|
11
|
+
|
|
12
|
+
ICON_ASCII:
|
|
13
|
+
family=lucide
|
|
14
|
+
glyph=network
|
|
15
|
+
|
|
16
|
+
5WH:
|
|
17
|
+
WHAT = Architecture map agent — produces a visual or JSON architecture map of the codebase
|
|
18
|
+
WHY = Understanding system architecture is essential for safe evolution and AI-assisted development
|
|
19
|
+
WHO = Rapid Web Development
|
|
20
|
+
WHERE = src/agents/discovery/architecture-map-agent.js
|
|
21
|
+
WHEN = 2026
|
|
22
|
+
HOW = Reads LEEWAY registry and builds a layered region/module graph in JSON and ASCII
|
|
23
|
+
|
|
24
|
+
AGENTS:
|
|
25
|
+
ARCHITECTURE
|
|
26
|
+
DOCS
|
|
27
|
+
REGISTRY
|
|
28
|
+
|
|
29
|
+
LICENSE:
|
|
30
|
+
MIT
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { readdir, readFile } from 'node:fs/promises';
|
|
34
|
+
import { join, relative, extname } from 'node:path';
|
|
35
|
+
import { parseHeader } from '../../core/header-parser.js';
|
|
36
|
+
|
|
37
|
+
const SKIP_DIRS = new Set(['.git', 'node_modules', 'dist', 'build', '.next', 'coverage', '.leeway']);
|
|
38
|
+
const CODE_EXTENSIONS = new Set(['.js', '.ts', '.jsx', '.tsx', '.mjs', '.cjs']);
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* ArchitectureMapAgent builds a structured architecture map from LEEWAY headers.
|
|
42
|
+
*/
|
|
43
|
+
export class ArchitectureMapAgent {
|
|
44
|
+
constructor(options = {}) {
|
|
45
|
+
this.rootDir = options.rootDir || process.cwd();
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Build a JSON architecture map.
|
|
50
|
+
* @returns {Promise<ArchitectureMap>}
|
|
51
|
+
*/
|
|
52
|
+
async buildMap() {
|
|
53
|
+
const map = {
|
|
54
|
+
agent: 'architecture-map-agent',
|
|
55
|
+
generated: new Date().toISOString(),
|
|
56
|
+
regions: {},
|
|
57
|
+
modules: {},
|
|
58
|
+
edges: [],
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
await this._walk(this.rootDir, map);
|
|
62
|
+
return map;
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
/**
|
|
66
|
+
* Build an ASCII text architecture diagram.
|
|
67
|
+
* @returns {Promise<string>}
|
|
68
|
+
*/
|
|
69
|
+
async buildAsciiDiagram() {
|
|
70
|
+
const map = await this.buildMap();
|
|
71
|
+
const lines = [
|
|
72
|
+
'╔════════════════════════════════════════╗',
|
|
73
|
+
'║ LEEWAY™ ARCHITECTURE MAP ║',
|
|
74
|
+
'╚════════════════════════════════════════╝',
|
|
75
|
+
'',
|
|
76
|
+
];
|
|
77
|
+
|
|
78
|
+
for (const [region, data] of Object.entries(map.regions)) {
|
|
79
|
+
lines.push(`┌─ ${region} (${data.color}) `);
|
|
80
|
+
for (const mod of data.modules) {
|
|
81
|
+
lines.push(`│ ├─ [${mod.tag || 'NO TAG'}] ${mod.path}`);
|
|
82
|
+
lines.push(`│ │ └─ ${mod.what || 'No description'}`);
|
|
83
|
+
}
|
|
84
|
+
lines.push('│');
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
return lines.join('\n');
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
async _walk(dir, map, depth = 0) {
|
|
91
|
+
if (depth > 8) return;
|
|
92
|
+
let entries;
|
|
93
|
+
try {
|
|
94
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
95
|
+
} catch {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
for (const entry of entries) {
|
|
100
|
+
if (SKIP_DIRS.has(entry.name)) continue;
|
|
101
|
+
const fullPath = join(dir, entry.name);
|
|
102
|
+
|
|
103
|
+
if (entry.isDirectory()) {
|
|
104
|
+
await this._walk(fullPath, map, depth + 1);
|
|
105
|
+
} else if (CODE_EXTENSIONS.has(extname(entry.name))) {
|
|
106
|
+
const relPath = relative(this.rootDir, fullPath);
|
|
107
|
+
try {
|
|
108
|
+
const content = await readFile(fullPath, 'utf8');
|
|
109
|
+
const header = parseHeader(content);
|
|
110
|
+
if (header) {
|
|
111
|
+
const regionKey = header.region?.split('.')?.[0] || 'UNKNOWN';
|
|
112
|
+
if (!map.regions[regionKey]) {
|
|
113
|
+
map.regions[regionKey] = { modules: [], color: this._regionColor(regionKey) };
|
|
114
|
+
}
|
|
115
|
+
map.regions[regionKey].modules.push({
|
|
116
|
+
path: relPath,
|
|
117
|
+
tag: header.tag,
|
|
118
|
+
what: header.fiveWH?.WHAT,
|
|
119
|
+
agents: header.agents,
|
|
120
|
+
});
|
|
121
|
+
map.modules[relPath] = { region: regionKey, tag: header.tag };
|
|
122
|
+
}
|
|
123
|
+
} catch {
|
|
124
|
+
continue;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
_regionColor(region) {
|
|
131
|
+
const colors = { UI: '#39FF14', CORE: '#FF6B35', DATA: '#4ECDC4', AI: '#A855F7', SEO: '#F59E0B', UTIL: '#6B7280', MCP: '#0EA5E9', SECURITY: '#EF4444' };
|
|
132
|
+
return colors[region] || '#888888';
|
|
133
|
+
}
|
|
134
|
+
}
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
/*
|
|
2
|
+
LEEWAY HEADER — DO NOT REMOVE
|
|
3
|
+
|
|
4
|
+
REGION: DOCS.AGENT.GENERATOR
|
|
5
|
+
TAG: DOCS.STANDARD.DOCS.AGENT
|
|
6
|
+
|
|
7
|
+
COLOR_ONION_HEX:
|
|
8
|
+
NEON=#39FF14
|
|
9
|
+
FLUO=#0DFF94
|
|
10
|
+
PASTEL=#C7FFD8
|
|
11
|
+
|
|
12
|
+
ICON_ASCII:
|
|
13
|
+
family=lucide
|
|
14
|
+
glyph=book-open
|
|
15
|
+
|
|
16
|
+
5WH:
|
|
17
|
+
WHAT = Docs agent — generates structured plain-English docs from LEEWAY headers and registries
|
|
18
|
+
WHY = Human-readable documentation must be automatically derivable from code headers
|
|
19
|
+
WHO = Rapid Web Development
|
|
20
|
+
WHERE = src/agents/discovery/docs-agent.js
|
|
21
|
+
WHEN = 2026
|
|
22
|
+
HOW = Reads LEEWAY headers and produces Markdown documentation summaries
|
|
23
|
+
|
|
24
|
+
AGENTS:
|
|
25
|
+
DOCS
|
|
26
|
+
EXPLAIN
|
|
27
|
+
REGISTRY
|
|
28
|
+
|
|
29
|
+
LICENSE:
|
|
30
|
+
MIT
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { readdir, readFile, writeFile, mkdir } from 'node:fs/promises';
|
|
34
|
+
import { join, relative, extname, dirname } from 'node:path';
|
|
35
|
+
import { parseHeader } from '../../core/header-parser.js';
|
|
36
|
+
|
|
37
|
+
const SKIP_DIRS = new Set(['.git', 'node_modules', 'dist', 'build', '.next', 'coverage', '.leeway']);
|
|
38
|
+
const CODE_EXTENSIONS = new Set(['.js', '.ts', '.jsx', '.tsx', '.mjs', '.cjs']);
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* DocsAgent generates Markdown documentation from LEEWAY headers.
|
|
42
|
+
*/
|
|
43
|
+
export class DocsAgent {
|
|
44
|
+
constructor(options = {}) {
|
|
45
|
+
this.rootDir = options.rootDir || process.cwd();
|
|
46
|
+
this.outputPath = options.outputPath || join(this.rootDir, 'docs', 'AUTO-GENERATED.md');
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Generate documentation for the entire codebase.
|
|
51
|
+
* @returns {Promise<string>} - Markdown string
|
|
52
|
+
*/
|
|
53
|
+
async generate() {
|
|
54
|
+
const docs = [];
|
|
55
|
+
docs.push('# LEEWAY Codebase Documentation\n');
|
|
56
|
+
docs.push(`> Auto-generated by LEEWAY docs-agent on ${new Date().toISOString()}\n`);
|
|
57
|
+
docs.push('---\n');
|
|
58
|
+
|
|
59
|
+
const sections = new Map();
|
|
60
|
+
await this._walk(this.rootDir, sections);
|
|
61
|
+
|
|
62
|
+
for (const [region, files] of [...sections.entries()].sort()) {
|
|
63
|
+
docs.push(`## ${region}\n`);
|
|
64
|
+
for (const file of files) {
|
|
65
|
+
docs.push(this._formatFileDoc(file));
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
return docs.join('\n');
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Generate and save documentation to disk.
|
|
74
|
+
* @returns {Promise<string>} - Path to saved file
|
|
75
|
+
*/
|
|
76
|
+
async generateAndSave() {
|
|
77
|
+
const content = await this.generate();
|
|
78
|
+
await mkdir(dirname(this.outputPath), { recursive: true });
|
|
79
|
+
await writeFile(this.outputPath, content, 'utf8');
|
|
80
|
+
return this.outputPath;
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
async _walk(dir, sections, depth = 0) {
|
|
84
|
+
if (depth > 8) return;
|
|
85
|
+
let entries;
|
|
86
|
+
try {
|
|
87
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
88
|
+
} catch {
|
|
89
|
+
return;
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
for (const entry of entries) {
|
|
93
|
+
if (SKIP_DIRS.has(entry.name)) continue;
|
|
94
|
+
const fullPath = join(dir, entry.name);
|
|
95
|
+
|
|
96
|
+
if (entry.isDirectory()) {
|
|
97
|
+
await this._walk(fullPath, sections, depth + 1);
|
|
98
|
+
} else if (CODE_EXTENSIONS.has(extname(entry.name))) {
|
|
99
|
+
const relPath = relative(this.rootDir, fullPath);
|
|
100
|
+
try {
|
|
101
|
+
const content = await readFile(fullPath, 'utf8');
|
|
102
|
+
const header = parseHeader(content);
|
|
103
|
+
if (header) {
|
|
104
|
+
const region = header.region?.split('.')?.[0] || 'UNKNOWN';
|
|
105
|
+
if (!sections.has(region)) sections.set(region, []);
|
|
106
|
+
sections.get(region).push({ path: relPath, header });
|
|
107
|
+
}
|
|
108
|
+
} catch {
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
_formatFileDoc({ path, header }) {
|
|
116
|
+
const wh = header.fiveWH || {};
|
|
117
|
+
return `### \`${path}\`
|
|
118
|
+
- **TAG**: \`${header.tag || 'N/A'}\`
|
|
119
|
+
- **WHAT**: ${wh.WHAT || 'N/A'}
|
|
120
|
+
- **WHY**: ${wh.WHY || 'N/A'}
|
|
121
|
+
- **HOW**: ${wh.HOW || 'N/A'}
|
|
122
|
+
- **Agents**: ${(header.agents || []).join(', ') || 'None declared'}
|
|
123
|
+
|
|
124
|
+
`;
|
|
125
|
+
}
|
|
126
|
+
}
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
/*
|
|
2
|
+
LEEWAY HEADER — DO NOT REMOVE
|
|
3
|
+
|
|
4
|
+
REGION: DOCS.AGENT.EXPLAIN
|
|
5
|
+
TAG: DOCS.STANDARD.EXPLAIN.MAIN
|
|
6
|
+
|
|
7
|
+
COLOR_ONION_HEX:
|
|
8
|
+
NEON=#39FF14
|
|
9
|
+
FLUO=#0DFF94
|
|
10
|
+
PASTEL=#C7FFD8
|
|
11
|
+
|
|
12
|
+
ICON_ASCII:
|
|
13
|
+
family=lucide
|
|
14
|
+
glyph=message-circle
|
|
15
|
+
|
|
16
|
+
5WH:
|
|
17
|
+
WHAT = Explain agent — explains a file, module, or service in plain English
|
|
18
|
+
WHY = Any file in a LEEWAY system must be self-explainable to any developer or AI tool
|
|
19
|
+
WHO = Rapid Web Development
|
|
20
|
+
WHERE = src/agents/discovery/explain-agent.js
|
|
21
|
+
WHEN = 2026
|
|
22
|
+
HOW = Reads LEEWAY headers and produces structured human-readable explanations
|
|
23
|
+
|
|
24
|
+
AGENTS:
|
|
25
|
+
EXPLAIN
|
|
26
|
+
DOCS
|
|
27
|
+
ASSESS
|
|
28
|
+
|
|
29
|
+
LICENSE:
|
|
30
|
+
MIT
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
import { readFile } from 'node:fs/promises';
|
|
34
|
+
import { join } from 'node:path';
|
|
35
|
+
import { parseHeader } from '../../core/header-parser.js';
|
|
36
|
+
import { classifyRegion } from '../../core/region-classifier.js';
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* ExplainAgent produces human-readable explanations of LEEWAY files and modules.
|
|
40
|
+
*/
|
|
41
|
+
export class ExplainAgent {
|
|
42
|
+
constructor(options = {}) {
|
|
43
|
+
this.rootDir = options.rootDir || process.cwd();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Explain a file in plain English.
|
|
48
|
+
*
|
|
49
|
+
* @param {string} filePath
|
|
50
|
+
* @returns {Promise<Explanation>}
|
|
51
|
+
*/
|
|
52
|
+
async explain(filePath) {
|
|
53
|
+
const fullPath = filePath.startsWith('/') ? filePath : join(this.rootDir, filePath);
|
|
54
|
+
const relPath = filePath.startsWith('/') ? filePath.replace(this.rootDir + '/', '') : filePath;
|
|
55
|
+
|
|
56
|
+
let content = '';
|
|
57
|
+
try {
|
|
58
|
+
content = await readFile(fullPath, 'utf8');
|
|
59
|
+
} catch {
|
|
60
|
+
return { file: relPath, explanation: 'File could not be read.', hasHeader: false };
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
const header = parseHeader(content);
|
|
64
|
+
if (!header) {
|
|
65
|
+
const { region, metadata } = classifyRegion(relPath);
|
|
66
|
+
return {
|
|
67
|
+
file: relPath,
|
|
68
|
+
hasHeader: false,
|
|
69
|
+
explanation: `This file does not have a LEEWAY header. Based on its path, it appears to be a ${metadata.label} (${region}) module. It has not been documented according to LEEWAY standards.`,
|
|
70
|
+
region,
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const wh = header.fiveWH || {};
|
|
75
|
+
const parts = [
|
|
76
|
+
`This file is "${relPath}".`,
|
|
77
|
+
wh.WHAT ? `It is: ${wh.WHAT}.` : null,
|
|
78
|
+
wh.WHY ? `It exists because: ${wh.WHY}.` : null,
|
|
79
|
+
header.region ? `It belongs to the ${header.region} system region.` : null,
|
|
80
|
+
header.tag ? `Its discovery tag is ${header.tag}.` : null,
|
|
81
|
+
wh.HOW ? `It operates via: ${wh.HOW}.` : null,
|
|
82
|
+
wh.WHO ? `It was created by: ${wh.WHO}.` : null,
|
|
83
|
+
header.agents?.length ? `It is managed by these agents: ${header.agents.join(', ')}.` : null,
|
|
84
|
+
].filter(Boolean);
|
|
85
|
+
|
|
86
|
+
return {
|
|
87
|
+
file: relPath,
|
|
88
|
+
hasHeader: true,
|
|
89
|
+
region: header.region,
|
|
90
|
+
tag: header.tag,
|
|
91
|
+
explanation: parts.join(' '),
|
|
92
|
+
fiveWH: wh,
|
|
93
|
+
};
|
|
94
|
+
}
|
|
95
|
+
}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
/*
|
|
2
|
+
LEEWAY HEADER — DO NOT REMOVE
|
|
3
|
+
|
|
4
|
+
REGION: SEO.AGENT.INTENT
|
|
5
|
+
TAG: SEO.INTENT.REGISTRY.MAIN
|
|
6
|
+
|
|
7
|
+
COLOR_ONION_HEX:
|
|
8
|
+
NEON=#39FF14
|
|
9
|
+
FLUO=#0DFF94
|
|
10
|
+
PASTEL=#C7FFD8
|
|
11
|
+
|
|
12
|
+
ICON_ASCII:
|
|
13
|
+
family=lucide
|
|
14
|
+
glyph=list-tree
|
|
15
|
+
|
|
16
|
+
5WH:
|
|
17
|
+
WHAT = Intent registry agent — tracks supported intents and their routes
|
|
18
|
+
WHY = AI agents and voice assistants need a machine-readable registry of supported intents
|
|
19
|
+
WHO = Rapid Web Development
|
|
20
|
+
WHERE = src/agents/discovery/intent-registry-agent.js
|
|
21
|
+
WHEN = 2026
|
|
22
|
+
HOW = Maintains a registry of intent-to-handler mappings in .leeway/intents.json
|
|
23
|
+
|
|
24
|
+
AGENTS:
|
|
25
|
+
INTENT
|
|
26
|
+
SCHEMA
|
|
27
|
+
REGISTRY
|
|
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
|
+
* IntentRegistryAgent tracks supported intents and routes.
|
|
38
|
+
*/
|
|
39
|
+
export class IntentRegistryAgent {
|
|
40
|
+
constructor(options = {}) {
|
|
41
|
+
this.rootDir = options.rootDir || process.cwd();
|
|
42
|
+
this.registryPath = join(this.rootDir, '.leeway', 'intents.json');
|
|
43
|
+
this.intents = new Map();
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Register an intent.
|
|
48
|
+
*
|
|
49
|
+
* @param {string} intentName
|
|
50
|
+
* @param {{ handler: string, description: string, examples?: string[], parameters?: object }} config
|
|
51
|
+
*/
|
|
52
|
+
register(intentName, config) {
|
|
53
|
+
this.intents.set(intentName.toLowerCase(), {
|
|
54
|
+
name: intentName,
|
|
55
|
+
handler: config.handler,
|
|
56
|
+
description: config.description,
|
|
57
|
+
examples: config.examples || [],
|
|
58
|
+
parameters: config.parameters || {},
|
|
59
|
+
registered: new Date().toISOString(),
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Resolve an intent by name.
|
|
65
|
+
*
|
|
66
|
+
* @param {string} intentName
|
|
67
|
+
* @returns {object | null}
|
|
68
|
+
*/
|
|
69
|
+
resolve(intentName) {
|
|
70
|
+
return this.intents.get(intentName.toLowerCase()) || null;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Persist the intent registry.
|
|
75
|
+
*/
|
|
76
|
+
async save() {
|
|
77
|
+
await mkdir(join(this.rootDir, '.leeway'), { recursive: true });
|
|
78
|
+
const data = {
|
|
79
|
+
version: '1.0',
|
|
80
|
+
updated: new Date().toISOString(),
|
|
81
|
+
intents: Object.fromEntries(this.intents),
|
|
82
|
+
};
|
|
83
|
+
await writeFile(this.registryPath, JSON.stringify(data, null, 2), 'utf8');
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
/**
|
|
87
|
+
* Load intents from disk.
|
|
88
|
+
*/
|
|
89
|
+
async load() {
|
|
90
|
+
try {
|
|
91
|
+
const raw = await readFile(this.registryPath, 'utf8');
|
|
92
|
+
const data = JSON.parse(raw);
|
|
93
|
+
for (const [key, val] of Object.entries(data.intents || {})) {
|
|
94
|
+
this.intents.set(key, val);
|
|
95
|
+
}
|
|
96
|
+
} catch {
|
|
97
|
+
// No registry yet
|
|
98
|
+
}
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
/**
|
|
102
|
+
* Export as a machine-readable discovery manifest.
|
|
103
|
+
* @returns {object}
|
|
104
|
+
*/
|
|
105
|
+
toDiscoveryManifest() {
|
|
106
|
+
return {
|
|
107
|
+
'@context': 'https://schema.org',
|
|
108
|
+
'@type': 'DataCatalog',
|
|
109
|
+
name: 'LEEWAY Intent Registry',
|
|
110
|
+
description: 'Machine-readable registry of supported intents and their handlers',
|
|
111
|
+
dataset: Array.from(this.intents.values()).map(intent => ({
|
|
112
|
+
'@type': 'Dataset',
|
|
113
|
+
name: intent.name,
|
|
114
|
+
description: intent.description,
|
|
115
|
+
keywords: intent.examples,
|
|
116
|
+
})),
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
}
|