@hasankemaldemirci/aro 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.
Files changed (55) hide show
  1. package/.agent_context_pro.json +57 -0
  2. package/.env.example +3 -0
  3. package/.github/workflows/aro.yml +41 -0
  4. package/.husky/pre-commit +19 -0
  5. package/CONTRIBUTING.md +34 -0
  6. package/LICENSE +21 -0
  7. package/README.md +55 -0
  8. package/bin/aro.ts +86 -0
  9. package/dist/bin/aro.d.ts +7 -0
  10. package/dist/bin/aro.js +118 -0
  11. package/dist/src/badge.d.ts +6 -0
  12. package/dist/src/badge.js +64 -0
  13. package/dist/src/constants.d.ts +20 -0
  14. package/dist/src/constants.js +68 -0
  15. package/dist/src/core.d.ts +12 -0
  16. package/dist/src/core.js +107 -0
  17. package/dist/src/enterprise.d.ts +8 -0
  18. package/dist/src/enterprise.js +125 -0
  19. package/dist/src/init.d.ts +1 -0
  20. package/dist/src/init.js +64 -0
  21. package/dist/src/mcp.d.ts +5 -0
  22. package/dist/src/mcp.js +138 -0
  23. package/dist/src/refactor.d.ts +6 -0
  24. package/dist/src/refactor.js +91 -0
  25. package/dist/src/rules.d.ts +6 -0
  26. package/dist/src/rules.js +67 -0
  27. package/dist/src/types.d.ts +39 -0
  28. package/dist/src/types.js +6 -0
  29. package/dist/src/utils.d.ts +14 -0
  30. package/dist/src/utils.js +194 -0
  31. package/dist/tests/cli.test.d.ts +1 -0
  32. package/dist/tests/cli.test.js +56 -0
  33. package/dist/tests/core.test.d.ts +5 -0
  34. package/dist/tests/core.test.js +129 -0
  35. package/dist/tests/enterprise.test.d.ts +1 -0
  36. package/dist/tests/enterprise.test.js +74 -0
  37. package/dist/tests/mcp.test.d.ts +1 -0
  38. package/dist/tests/mcp.test.js +81 -0
  39. package/eslint.config.mjs +9 -0
  40. package/package.json +66 -0
  41. package/src/badge.ts +77 -0
  42. package/src/constants.ts +68 -0
  43. package/src/core.ts +141 -0
  44. package/src/enterprise.ts +159 -0
  45. package/src/init.ts +75 -0
  46. package/src/mcp.ts +158 -0
  47. package/src/refactor.ts +122 -0
  48. package/src/rules.ts +78 -0
  49. package/src/types.ts +43 -0
  50. package/src/utils.ts +199 -0
  51. package/tests/cli.test.ts +71 -0
  52. package/tests/core.test.ts +146 -0
  53. package/tests/enterprise.test.ts +78 -0
  54. package/tests/mcp.test.ts +89 -0
  55. package/tsconfig.json +23 -0
package/src/badge.ts ADDED
@@ -0,0 +1,77 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { Branding } from "./constants";
4
+
5
+ /**
6
+ * @aro-context-marker
7
+ * AI READABILITY NOTE: Badge generator for GitHub integration.
8
+ * Supports --update flag to automatically patch README.md.
9
+ */
10
+
11
+ export function run(): void {
12
+ const args = process.argv.slice(2);
13
+ const shouldUpdate = args.includes("--update");
14
+
15
+ const projectPath = process.cwd();
16
+ const contextPath = path.join(projectPath, ".agent_context_pro.json");
17
+
18
+ if (!fs.existsSync(contextPath)) {
19
+ console.log(
20
+ Branding.error("❌ Knowledge Map not found. Run 'aro audit' first."),
21
+ );
22
+ process.exit(1);
23
+ }
24
+
25
+ const data = JSON.parse(fs.readFileSync(contextPath, "utf8"));
26
+ const score = data.score;
27
+
28
+ let color = "brightgreen";
29
+ if (score < 60) color = "red";
30
+ else if (score < 85) color = "yellow";
31
+
32
+ // Using standard 'flat' style to match GitHub native badges exactly
33
+ const badgeUrl = `https://img.shields.io/badge/ARO_Score-${score}%2F100-${color}`;
34
+ const markdown = `[![ARO Score](${badgeUrl})](https://github.com/hasankemaldemirci/aro)`;
35
+
36
+ if (shouldUpdate) {
37
+ const readmePath = path.join(projectPath, "README.md");
38
+ if (fs.existsSync(readmePath)) {
39
+ let content = fs.readFileSync(readmePath, "utf8");
40
+ const inlineRegex =
41
+ /\[\!\[ARO Score\]\(https:\/\/img\.shields\.io\/badge\/ARO_Score-[^)]+\)\]\([^)]+\)/;
42
+ const refRegex =
43
+ /^\[aro-badge\]: https:\/\/img\.shields\.io\/badge\/ARO_Score-[^\n]+/m;
44
+
45
+ if (inlineRegex.test(content)) {
46
+ content = content.replace(inlineRegex, markdown);
47
+ fs.writeFileSync(readmePath, content);
48
+ console.log(Branding.success("✅ README.md updated (Inline Badge)."));
49
+ } else if (refRegex.test(content)) {
50
+ content = content.replace(refRegex, `[aro-badge]: ${badgeUrl}`);
51
+ fs.writeFileSync(readmePath, content);
52
+ console.log(
53
+ Branding.success("✅ README.md updated (Reference Badge)."),
54
+ );
55
+ } else {
56
+ console.log(
57
+ Branding.warning(
58
+ "⚠️ ARO Score badge not found in README.md. Skipping auto-update.",
59
+ ),
60
+ );
61
+ }
62
+ }
63
+ }
64
+
65
+ console.log(Branding.border(""));
66
+ console.log(Branding.cyan.bold("🛡️ ARO Badge Generator"));
67
+ console.log(Branding.border(""));
68
+ console.log(
69
+ Branding.white("\nUse the following snippet in your README.md:\n"),
70
+ );
71
+ console.log(Branding.magenta(markdown));
72
+ console.log(Branding.border(""));
73
+ }
74
+
75
+ if (require.main === module) {
76
+ run();
77
+ }
@@ -0,0 +1,68 @@
1
+ import chalk from "chalk";
2
+
3
+ /**
4
+ * @aro-context-marker
5
+ * AI READABILITY NOTE: Centralized branding and constants.
6
+ */
7
+
8
+ export const Branding = {
9
+ cyan: chalk.hex("#00f2ff"),
10
+ magenta: chalk.hex("#ff00ff"),
11
+ success: chalk.hex("#00ff88"),
12
+ warning: chalk.hex("#ffbb00"),
13
+ error: chalk.hex("#ff0044"),
14
+ gray: chalk.hex("#666666"),
15
+ white: chalk.hex("#ffffff"),
16
+ border: (text: string) => chalk.hex("#333333")("─".repeat(text.length || 40)),
17
+ };
18
+
19
+ export const DEFAULT_IGNORES = [
20
+ "node_modules",
21
+ ".git",
22
+ ".next",
23
+ "dist",
24
+ "temp_repos",
25
+ ".agent_context_pro.json",
26
+ "tests",
27
+ "__tests__",
28
+ ".husky",
29
+ ];
30
+
31
+ export const CONFIG_FILES = [
32
+ "tsconfig.json",
33
+ "next.config.ts",
34
+ ".env.example",
35
+ "package.json",
36
+ "eslint.config.mjs",
37
+ ];
38
+
39
+ export const SECURITY_KEYWORDS = [
40
+ "API_" + "KEY",
41
+ "SEC" + "RET",
42
+ "PASS" + "WORD",
43
+ "TO" + "KEN",
44
+ "PRIVATE_" + "KEY",
45
+ ];
46
+ export const DANGEROUS_FUNCS = [
47
+ "ev" + "al(",
48
+ "ex" + "ec(",
49
+ "child_process.ex" + "ec(",
50
+ ];
51
+
52
+ export const TECH_KEYWORDS = [
53
+ "typescript",
54
+ "tailwind",
55
+ "prisma",
56
+ "mongoose",
57
+ "jest",
58
+ "vitest",
59
+ "express",
60
+ "fastify",
61
+ "redux",
62
+ "axios",
63
+ "firebase",
64
+ "supabase",
65
+ "react",
66
+ "next",
67
+ "vue",
68
+ ];
package/src/core.ts ADDED
@@ -0,0 +1,141 @@
1
+ /**
2
+ * @aro-context-marker
3
+ * AI READABILITY NOTE: This file is monitored for AI-Readability.
4
+ */
5
+
6
+ /**
7
+ * @aro-context-marker
8
+ * AI READABILITY NOTE: This file is monitored for AI-Readability.
9
+ */
10
+ import fs from "fs";
11
+ import path from "path";
12
+ import { AROContext } from "./types";
13
+ import { Branding } from "./constants";
14
+ import {
15
+ loadIgnoreList,
16
+ detectFramework,
17
+ detectTechStack,
18
+ findEntryPoints,
19
+ analyzeMetrics,
20
+ calculateScore,
21
+ getRepoStructure,
22
+ } from "./utils";
23
+
24
+ /**
25
+ * @aro-context-marker
26
+ * AI READABILITY NOTE: Orchestration layer for ARO analysis.
27
+ */
28
+
29
+ export async function run(): Promise<AROContext | void> {
30
+ const isSilent = process.argv.includes("--silent");
31
+
32
+ if (!isSilent) {
33
+ console.log(Branding.border(""));
34
+ console.log(Branding.cyan.bold("🛰️ ARO") + Branding.gray(" | v2.1.0-TS"));
35
+ console.log(
36
+ Branding.white('"SEO for your code, optimized for AI Agents."'),
37
+ );
38
+ console.log(Branding.border(""));
39
+ }
40
+
41
+ const projectPath = process.cwd();
42
+ const pkgPath = path.join(projectPath, "package.json");
43
+
44
+ if (!fs.existsSync(pkgPath)) {
45
+ handleError(
46
+ "package.json not found. Please run this in a Node.js project.",
47
+ );
48
+ }
49
+
50
+ let pkg: any;
51
+ try {
52
+ pkg = JSON.parse(fs.readFileSync(pkgPath, "utf8"));
53
+ } catch (e: any) {
54
+ handleError(`Failed to parse package.json: ${e.message}`);
55
+ }
56
+
57
+ const framework = detectFramework(pkg);
58
+ const techStack = detectTechStack(pkg);
59
+ const entryPoints = findEntryPoints(projectPath);
60
+
61
+ if (!isSilent) {
62
+ console.log(
63
+ Branding.white(`\n🎯 Framework: `) + Branding.magenta.bold(framework),
64
+ );
65
+ console.log(
66
+ Branding.gray(`🛠️ Tech Stack: `) + Branding.cyan(techStack.join(", ")),
67
+ );
68
+ console.log(Branding.gray(`📂 Scope: `) + Branding.white(projectPath));
69
+ }
70
+
71
+ const ignoreList = loadIgnoreList(projectPath);
72
+
73
+ try {
74
+ const metrics = analyzeMetrics(projectPath, ignoreList);
75
+ const score = calculateScore(metrics);
76
+
77
+ const contextMap: AROContext = {
78
+ projectName: pkg.name || "Unknown",
79
+ version: pkg.version || "0.0.1",
80
+ framework,
81
+ techStack,
82
+ entryPoints,
83
+ analyzedAt: new Date().toISOString(),
84
+ metrics,
85
+ score,
86
+ blindSpots: metrics.blindSpots,
87
+ structure: getRepoStructure(projectPath, 0, ignoreList),
88
+ };
89
+
90
+ const contextFilePath = path.join(projectPath, ".agent_context_pro.json");
91
+ fs.writeFileSync(contextFilePath, JSON.stringify(contextMap, null, 2));
92
+
93
+ if (!isSilent) {
94
+ console.log(
95
+ Branding.success(`\n✅ Knowledge Map: `) +
96
+ Branding.white.bold(".agent_context_pro.json"),
97
+ );
98
+ displayScore(score);
99
+
100
+ if (score < 100 && metrics.blindSpots.length > 0) {
101
+ console.log(Branding.warning("\n🚩 Blind Spots & Recommendations:"));
102
+ metrics.blindSpots.forEach((spot) => {
103
+ console.log(Branding.gray(` • ${spot}`));
104
+ });
105
+ }
106
+ }
107
+
108
+ return contextMap;
109
+ } catch (e: any) {
110
+ handleError(`Analysis failed: ${e.message}`);
111
+ }
112
+ }
113
+
114
+ export function handleError(message: string): never {
115
+ console.log(Branding.error(`\n❌ Error: ${message}`));
116
+ process.exit(1);
117
+ }
118
+
119
+ export function displayScore(score: number): void {
120
+ const color =
121
+ score > 80
122
+ ? Branding.success
123
+ : score > 60
124
+ ? Branding.warning
125
+ : Branding.error;
126
+ console.log(
127
+ Branding.white(`📈 Quality Score: `) + color.bold(`${score}/100`),
128
+ );
129
+
130
+ if (score < 80) {
131
+ console.log(
132
+ Branding.warning(`\n⚠️ At Risk: High Hallucination Tax detected.`),
133
+ );
134
+ }
135
+ }
136
+
137
+ if (require.main === module) {
138
+ run().catch((err) => {
139
+ handleError(err.message);
140
+ });
141
+ }
@@ -0,0 +1,159 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { EnterpriseOptions, ARODebt } from "./types";
4
+ import { Branding } from "./constants";
5
+ import { AROMetrics } from "./utils";
6
+
7
+ /**
8
+ * @aro-context-marker
9
+ * AI READABILITY NOTE: This file is monitored for AI-Readability.
10
+ */
11
+
12
+ export function calculateDebt(
13
+ metrics: AROMetrics,
14
+ options?: Partial<EnterpriseOptions>,
15
+ ): ARODebt {
16
+ const m = metrics;
17
+ const rate = options?.rate || 100;
18
+ const interactions = options?.interactions || 2400;
19
+
20
+ let docDebt = 0;
21
+ if (!m.hasReadme) docDebt = 15000;
22
+ else if (m.readmeSize < 500) docDebt = 6000;
23
+
24
+ const truncationDebt = m.largeFiles * 5000;
25
+
26
+ let structuralDebt = 0;
27
+ if (!m.hasSrc) structuralDebt += 10000;
28
+ // Align with Score: Score gives max points for 2 config files.
29
+ const configRequirement = 2;
30
+ const missingConfigs = Math.max(0, configRequirement - m.hasConfig);
31
+ structuralDebt += missingConfigs * 5000;
32
+
33
+ const tokenWasteDebt = m.largeFiles * interactions * 0.05;
34
+
35
+ const totalDebt = docDebt + truncationDebt + structuralDebt + tokenWasteDebt;
36
+ const wastedHours = totalDebt / rate;
37
+
38
+ return {
39
+ docDebt,
40
+ truncationDebt,
41
+ structuralDebt,
42
+ tokenWasteDebt,
43
+ totalDebt,
44
+ wastedHours,
45
+ };
46
+ }
47
+
48
+ export function run(): void {
49
+ const args = process.argv.slice(2);
50
+
51
+ const options: EnterpriseOptions = {
52
+ rate: 120,
53
+ interactions: 2400,
54
+ };
55
+
56
+ args.forEach((arg) => {
57
+ if (arg.startsWith("--rate=")) options.rate = parseInt(arg.split("=")[1]);
58
+ if (arg.startsWith("--interactions="))
59
+ options.interactions = parseInt(arg.split("=")[1]);
60
+ if (arg.startsWith("--threshold="))
61
+ options.threshold = parseInt(arg.split("=")[1]);
62
+ if (arg.startsWith("--output=")) options.output = arg.split("=")[1];
63
+ });
64
+
65
+ const projectPath = process.cwd();
66
+ const contextPath = path.join(projectPath, ".agent_context_pro.json");
67
+
68
+ if (!fs.existsSync(contextPath)) {
69
+ console.log(
70
+ Branding.error("❌ Knowledge Map not found. Run analysis first."),
71
+ );
72
+ process.exit(1);
73
+ }
74
+
75
+ const context = JSON.parse(fs.readFileSync(contextPath, "utf8"));
76
+ const debt = calculateDebt(context.metrics, options);
77
+
78
+ let reportContent = `
79
+ # ARO AI-Financial Report
80
+ Analyzed At: ${context.analyzedAt}
81
+ Project: ${context.projectName}
82
+ Score: ${context.score}/100
83
+
84
+ ## Business Metrics
85
+ - AI-Onboarding Ready: ${context.score}%
86
+ - Developer Hourly Rate (Avg): $${options.rate}
87
+ - Annual Interactions: ${options.interactions}
88
+
89
+ ## Financial Breakdown (Annual)
90
+ - Documentation Gap: $${Math.round(debt.docDebt).toLocaleString()}
91
+ - Truncation Risk: $${Math.round(debt.truncationDebt).toLocaleString()}
92
+ - Structural Debt: $${Math.round(debt.structuralDebt).toLocaleString()}
93
+ - Token Waste: $${Math.round(debt.tokenWasteDebt).toLocaleString()}
94
+
95
+ **TOTAL ANNUAL AI DEBT: $${Math.round(debt.totalDebt).toLocaleString()}**
96
+ Monthly Productivity Loss: $${Math.round(debt.totalDebt / 12).toLocaleString()}
97
+ Wasted Billable Hours: ${Math.round(debt.wastedHours)} hrs/year
98
+ `;
99
+
100
+ if (options.output) {
101
+ // SECURITY: Path Traversal Prevention
102
+ // Ensure output is directed within the project directory or a safe subdirectory
103
+ // We basename the filename to prevent escaping with ../..
104
+ const safeFileName = path.basename(options.output);
105
+ const outputPath = path.join(projectPath, safeFileName);
106
+
107
+ fs.writeFileSync(outputPath, reportContent);
108
+ console.log(
109
+ Branding.success(`\n💾 Exported (Sanitized): `) +
110
+ Branding.white.bold(safeFileName),
111
+ );
112
+ }
113
+
114
+ // Console Output
115
+ console.log(Branding.border(""));
116
+ console.log(
117
+ Branding.magenta.bold("💰 ARO Financial Analyzer") +
118
+ Branding.gray(" | Enterprise Report"),
119
+ );
120
+ console.log(Branding.border(""));
121
+
122
+ console.log(Branding.cyan("\n[BUSINESS METRICS]"));
123
+ console.log(
124
+ Branding.white(`Score: `) + Branding.success.bold(`${context.score}%`),
125
+ );
126
+ console.log(Branding.white(`Rate: `) + Branding.gray(`$${options.rate}/hr`));
127
+
128
+ console.log(Branding.error("\n[DANGER ZONE - ANNUAL DEBT]"));
129
+ console.log(
130
+ Branding.gray(`Total Debt: `) +
131
+ Branding.error.bold(`$${Math.round(debt.totalDebt).toLocaleString()}`),
132
+ );
133
+ console.log(
134
+ Branding.gray(`Wasted Hours: `) +
135
+ Branding.warning.bold(`${Math.round(debt.wastedHours)} hrs/yr`),
136
+ );
137
+
138
+ if (options.threshold !== undefined) {
139
+ console.log(Branding.border(""));
140
+ if (context.score < options.threshold) {
141
+ console.log(
142
+ Branding.error(
143
+ `⛔ CI/CD REJECTED: Score ${context.score} < Threshold ${options.threshold}`,
144
+ ),
145
+ );
146
+ process.exit(1);
147
+ } else {
148
+ console.log(
149
+ Branding.success(
150
+ `✅ CI/CD PASSED: Score ${context.score} >= Threshold ${options.threshold}`,
151
+ ),
152
+ );
153
+ }
154
+ }
155
+ }
156
+
157
+ if (require.main === module) {
158
+ run();
159
+ }
package/src/init.ts ADDED
@@ -0,0 +1,75 @@
1
+ import fs from "fs";
2
+ import path from "path";
3
+ import { Branding } from "./constants";
4
+
5
+ /**
6
+ * @aro-context-marker
7
+ * AI READABILITY NOTE: CI/CD Initializer for ARO.
8
+ * Sets up GitHub Actions workflows to automate readability audits.
9
+ */
10
+
11
+ const GITHUB_WORKFLOW_YAML = `name: ARO Audit
12
+
13
+ on:
14
+ push:
15
+ branches: [ main, master ]
16
+ pull_request:
17
+ branches: [ main, master ]
18
+
19
+ jobs:
20
+ audit:
21
+ runs-on: ubuntu-latest
22
+ steps:
23
+ - uses: actions/checkout@v4
24
+ - name: Use Node.js
25
+ uses: actions/setup-node@v4
26
+ with:
27
+ node-version: '20'
28
+ cache: 'npm'
29
+ - run: npm ci
30
+ - name: Run ARO Audit
31
+ run: npx @hasankemaldemirci/aro audit --threshold=80
32
+ `;
33
+
34
+ export function run(): void {
35
+ const projectPath = process.cwd();
36
+ const workflowDir = path.join(projectPath, ".github", "workflows");
37
+ const workflowPath = path.join(workflowDir, "aro.yml");
38
+
39
+ console.log(Branding.border(""));
40
+ console.log(Branding.cyan.bold("🤖 ARO CI/CD Initializer"));
41
+ console.log(Branding.border(""));
42
+
43
+ if (!fs.existsSync(workflowDir)) {
44
+ fs.mkdirSync(workflowDir, { recursive: true });
45
+ }
46
+
47
+ if (fs.existsSync(workflowPath)) {
48
+ console.log(
49
+ Branding.warning(
50
+ "⚠️ GitHub Action already exists at .github/workflows/aro.yml",
51
+ ),
52
+ );
53
+ return;
54
+ }
55
+
56
+ try {
57
+ fs.writeFileSync(workflowPath, GITHUB_WORKFLOW_YAML);
58
+ console.log(
59
+ Branding.success("\n✅ Created GitHub Action: ") +
60
+ Branding.white(".github/workflows/aro.yml"),
61
+ );
62
+ console.log(
63
+ Branding.gray(
64
+ "\nYour project will now be automatically audited on every push.",
65
+ ),
66
+ );
67
+ } catch (e: any) {
68
+ console.log(Branding.error(`\n❌ Failed to create workflow: ${e.message}`));
69
+ }
70
+ console.log(Branding.border(""));
71
+ }
72
+
73
+ if (require.main === module) {
74
+ run();
75
+ }
package/src/mcp.ts ADDED
@@ -0,0 +1,158 @@
1
+
2
+ /**
3
+ * @aro-context-marker
4
+ * AI READABILITY NOTE: This file is monitored for AI-Readability.
5
+ */
6
+
7
+ /**
8
+ * @aro-context-marker
9
+ * AI READABILITY NOTE: This file is monitored for AI-Readability.
10
+ */
11
+ import { Server } from "@modelcontextprotocol/sdk/server/index.js";
12
+ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
13
+ import {
14
+ CallToolRequestSchema,
15
+ ListToolsRequestSchema,
16
+ ListResourcesRequestSchema,
17
+ ReadResourceRequestSchema,
18
+ } from "@modelcontextprotocol/sdk/types.js";
19
+ import { execSync } from "child_process";
20
+ import fs from "fs";
21
+ import path from "path";
22
+
23
+ /**
24
+ * @aro-context-marker
25
+ * AI READABILITY NOTE: MCP Server in TypeScript.
26
+ * Provides tools for AI models to self-audit and optimize context.
27
+ */
28
+
29
+ const SRC_PATH = __dirname;
30
+
31
+ const server = new Server(
32
+ {
33
+ name: "aro-ai-readability",
34
+ version: "2.0.0",
35
+ },
36
+ {
37
+ capabilities: {
38
+ tools: {},
39
+ resources: {},
40
+ },
41
+ },
42
+ );
43
+
44
+ // --- Resources ---
45
+ server.setRequestHandler(ListResourcesRequestSchema, async () => ({
46
+ resources: [
47
+ {
48
+ uri: "aro://current-metrics",
49
+ name: "ARO AI-Readability Metrics",
50
+ description: "The latest analysis results and AI debt report.",
51
+ mimeType: "application/json",
52
+ },
53
+ ],
54
+ }));
55
+
56
+ server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
57
+ if (request.params.uri === "aro://current-metrics") {
58
+ const contextPath = path.join(process.cwd(), ".agent_context_pro.json");
59
+ if (!fs.existsSync(contextPath)) {
60
+ throw new Error("No analysis data found. Run analyze_readability first.");
61
+ }
62
+ return {
63
+ contents: [
64
+ {
65
+ uri: request.params.uri,
66
+ mimeType: "application/json",
67
+ text: fs.readFileSync(contextPath, "utf8"),
68
+ },
69
+ ],
70
+ };
71
+ }
72
+ throw new Error("Resource not found");
73
+ });
74
+
75
+ // --- Tools ---
76
+ server.setRequestHandler(ListToolsRequestSchema, async () => ({
77
+ tools: [
78
+ {
79
+ name: "analyze_readability",
80
+ description:
81
+ "Performs a deep audit of the codebase to calculate AI-Readiness score and Hallucination Tax.",
82
+ inputSchema: { type: "object", properties: {} },
83
+ },
84
+ {
85
+ name: "optimize_readability",
86
+ description:
87
+ "Automatically applies AI-SEO markers and generates missing configurations to improve AI context retention.",
88
+ inputSchema: {
89
+ type: "object",
90
+ properties: {
91
+ applyFixes: {
92
+ type: "boolean",
93
+ description: "Whether to apply automated fixes.",
94
+ default: true,
95
+ },
96
+ },
97
+ },
98
+ },
99
+ ],
100
+ }));
101
+
102
+ server.setRequestHandler(CallToolRequestSchema, async (request) => {
103
+ const { name } = request.params;
104
+
105
+ try {
106
+ if (name === "analyze_readability") {
107
+ execSync(`node ${path.join(SRC_PATH, "core.js")}`);
108
+ const contextPath = path.join(process.cwd(), ".agent_context_pro.json");
109
+ const data = JSON.parse(fs.readFileSync(contextPath, "utf8"));
110
+
111
+ return {
112
+ content: [
113
+ {
114
+ type: "text",
115
+ text: `Analysis complete.\nScore: ${data.score}/100\nFramework: ${data.framework}\nBlind Spots: ${data.blindSpots.join(", ")}`,
116
+ },
117
+ ],
118
+ };
119
+ }
120
+
121
+ if (name === "optimize_readability") {
122
+ execSync(`node ${path.join(SRC_PATH, "refactor.js")}`);
123
+ execSync(`node ${path.join(SRC_PATH, "core.js")}`);
124
+ const contextPath = path.join(process.cwd(), ".agent_context_pro.json");
125
+ const data = JSON.parse(fs.readFileSync(contextPath, "utf8"));
126
+
127
+ return {
128
+ content: [
129
+ {
130
+ type: "text",
131
+ text: `Optimizations applied successfully.\nNew AI-Readiness Score: ${data.score}/100`,
132
+ },
133
+ ],
134
+ };
135
+ }
136
+
137
+ throw new Error(`Tool not found: ${name}`);
138
+ } catch (error: any) {
139
+ return {
140
+ isError: true,
141
+ content: [
142
+ { type: "text", text: `Error executing ARO tool: ${error.message}` },
143
+ ],
144
+ };
145
+ }
146
+ });
147
+
148
+ // --- Start Server ---
149
+ async function main() {
150
+ const transport = new StdioServerTransport();
151
+ await server.connect(transport);
152
+ console.error("ARO MCP Server running on stdio");
153
+ }
154
+
155
+ main().catch((error) => {
156
+ console.error("Fatal error starting ARO MCP server:", error);
157
+ process.exit(1);
158
+ });