ai-readme-mcp 0.3.2 ā 0.4.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/README.md +115 -72
- package/dist/index.js +141 -310
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -46,9 +46,9 @@ This MCP (Model Context Protocol) server automates the entire workflow:
|
|
|
46
46
|
|
|
47
47
|
- š **Auto-discovers** all AI_README.md files in your project
|
|
48
48
|
- šÆ **Routes context** - AI gets the most relevant README for the code it's editing
|
|
49
|
+
- š **Guided initialization** - `init_ai_readme` scans for empty files and guides AI through population
|
|
49
50
|
- āļø **Updates automatically** - AI can add new conventions it discovers while coding
|
|
50
51
|
- ā
**Validates quality** - Ensures READMEs are concise and optimized for AI consumption
|
|
51
|
-
- š¤ **Smart generation** - Auto-creates AI_README based on your project structure
|
|
52
52
|
|
|
53
53
|
**Result:** Every AI interaction in your project follows your team's standards and produces consistent, high-quality code.
|
|
54
54
|
|
|
@@ -58,9 +58,9 @@ This MCP (Model Context Protocol) server automates the entire workflow:
|
|
|
58
58
|
|
|
59
59
|
- š **Automatic Discovery** - Scan and index all AI_README.md files in your project
|
|
60
60
|
- šÆ **Smart Context Routing** - Find relevant README content based on file paths
|
|
61
|
+
- š **Guided Initialization** - `init_ai_readme` tool scans for empty files and guides AI through population
|
|
61
62
|
- š **Update & Sync** - AI can both read and update AI_README files
|
|
62
63
|
- ā
**Validation & Quality** - Ensure README consistency with token limits and structure checks
|
|
63
|
-
- š¤ **Smart Initialization** - Auto-generate AI_README files based on project analysis
|
|
64
64
|
- šļø **Monorepo Support** - Place AI_README.md files at different folder levels; the tool automatically finds and uses the most relevant one
|
|
65
65
|
- š¦ **Easy Integration** - Works seamlessly with Cursor, Claude Code, and other MCP clients
|
|
66
66
|
|
|
@@ -265,37 +265,81 @@ Add to `claude_desktop_config.json`:
|
|
|
265
265
|
|
|
266
266
|
### Create Your First AI_README
|
|
267
267
|
|
|
268
|
-
You
|
|
268
|
+
You have two main approaches to create and populate AI_README files:
|
|
269
269
|
|
|
270
|
-
|
|
271
|
-
The tool automatically detects your project and generates relevant content:
|
|
272
|
-
- Detects project type (library/application/monorepo)
|
|
273
|
-
- Identifies language (TypeScript, Python, Go, Rust, Java, etc.)
|
|
274
|
-
- Recognizes framework (React, Vue, Next.js, Express, NestJS, etc.)
|
|
275
|
-
- Analyzes directory structure
|
|
276
|
-
- Generates framework-specific conventions
|
|
270
|
+
#### Option 1: Automated Initialization (Recommended)
|
|
277
271
|
|
|
278
|
-
|
|
279
|
-
> "Please use init_ai_readme to create an AI_README.md for this project"
|
|
272
|
+
Use the `init_ai_readme` tool to automatically scan and populate empty AI_README files:
|
|
280
273
|
|
|
281
|
-
|
|
274
|
+
**Step 1:** Create empty AI_README.md files where needed
|
|
275
|
+
|
|
276
|
+
```bash
|
|
277
|
+
# Example: Create empty AI_READMEs in different directories
|
|
278
|
+
touch AI_README.md
|
|
279
|
+
touch apps/backend/AI_README.md
|
|
280
|
+
touch apps/frontend/AI_README.md
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
**Step 2:** Trigger the initialization
|
|
284
|
+
|
|
285
|
+
In your AI assistant (Claude Code, Cursor, etc.), simply say:
|
|
286
|
+
|
|
287
|
+
> "Please run init_ai_readme for this project"
|
|
288
|
+
|
|
289
|
+
Or more explicitly:
|
|
290
|
+
|
|
291
|
+
> "Initialize AI_README files"
|
|
292
|
+
> "Help me populate the empty AI_README files"
|
|
293
|
+
|
|
294
|
+
**What happens:**
|
|
295
|
+
- š Scans your project for empty AI_README files
|
|
296
|
+
- š Creates root-level AI_README if none exist
|
|
297
|
+
- š Provides detailed step-by-step instructions for each file
|
|
298
|
+
- š¤ AI assistant will then:
|
|
299
|
+
- Explore relevant directories
|
|
300
|
+
- Analyze your codebase (tech stack, patterns, conventions)
|
|
301
|
+
- Populate each AI_README with relevant documentation
|
|
302
|
+
|
|
303
|
+
**When to use `init_ai_readme`:**
|
|
304
|
+
- First time setting up AI_README in your project
|
|
305
|
+
- After creating new empty AI_README.md files in subdirectories
|
|
306
|
+
- When `get_context_for_file` detects empty AI_README files
|
|
307
|
+
- To batch-process multiple empty AI_README files
|
|
308
|
+
|
|
309
|
+
#### Option 2: Manual Creation
|
|
310
|
+
|
|
311
|
+
Create and write AI_README.md files yourself:
|
|
282
312
|
|
|
283
313
|
```markdown
|
|
284
314
|
# My Project
|
|
285
315
|
|
|
286
|
-
##
|
|
316
|
+
## Tech Stack
|
|
287
317
|
- Framework: React + Express
|
|
288
318
|
- Database: PostgreSQL
|
|
319
|
+
- Testing: Jest + Vitest
|
|
320
|
+
|
|
321
|
+
## Architecture Patterns
|
|
322
|
+
- Follows MVC pattern for backend
|
|
323
|
+
- Component-based architecture for frontend
|
|
324
|
+
- RESTful API design
|
|
289
325
|
|
|
290
326
|
## Coding Conventions
|
|
291
327
|
- Use TypeScript for all files
|
|
292
328
|
- Components in PascalCase
|
|
293
|
-
-
|
|
329
|
+
- Functions and variables in camelCase
|
|
330
|
+
- Test coverage: 80%+ required
|
|
294
331
|
|
|
295
332
|
## Testing
|
|
296
333
|
Run tests with: `npm test`
|
|
334
|
+
Write tests alongside source files
|
|
297
335
|
```
|
|
298
336
|
|
|
337
|
+
**Best Practices:**
|
|
338
|
+
- Keep it concise (< 400 tokens is ideal)
|
|
339
|
+
- Focus on conventions, not documentation
|
|
340
|
+
- Update as your project evolves
|
|
341
|
+
- Use AI to help maintain it
|
|
342
|
+
|
|
299
343
|
### Multi-Level AI_README (Not Just for Monorepos!)
|
|
300
344
|
|
|
301
345
|
**The power of this tool is multi-level documentation** - not just for monorepos, but for **any project** that wants to organize conventions by module or feature.
|
|
@@ -324,10 +368,11 @@ my-monorepo/
|
|
|
324
368
|
```
|
|
325
369
|
|
|
326
370
|
**Smart Empty README Handling:**
|
|
327
|
-
- š
|
|
371
|
+
- š Create empty `AI_README.md` files in subdirectories where you need specific conventions
|
|
372
|
+
- š Run `init_ai_readme` tool (just tell your AI: "Please initialize AI_README files")
|
|
373
|
+
- š¤ AI automatically analyzes each directory and populates conventions
|
|
328
374
|
- š For subdirectories with parent READMEs, generates differential content (only module-specific conventions)
|
|
329
375
|
- š For root directories, generates full project analysis
|
|
330
|
-
- Ask your AI: "This AI_README.md is empty, can you help fill it?"
|
|
331
376
|
|
|
332
377
|
When AI works on a file, it automatically gets:
|
|
333
378
|
- The **most relevant** AI_README (closest parent directory)
|
|
@@ -409,61 +454,6 @@ npm run dev
|
|
|
409
454
|
|
|
410
455
|
## š ļø Available MCP Tools
|
|
411
456
|
|
|
412
|
-
### `init_ai_readme`
|
|
413
|
-
|
|
414
|
-
Initialize a new AI_README.md file with smart content generation based on project analysis.
|
|
415
|
-
|
|
416
|
-
```typescript
|
|
417
|
-
// Parameters
|
|
418
|
-
{
|
|
419
|
-
targetPath: string; // Required: Directory where AI_README.md will be created
|
|
420
|
-
projectName?: string; // Optional: Project name (auto-detected if not provided)
|
|
421
|
-
overwrite?: boolean; // Optional: Overwrite existing file (default: false)
|
|
422
|
-
smart?: boolean; // Optional: Enable smart content generation (default: true)
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
// Returns
|
|
426
|
-
{
|
|
427
|
-
success: boolean;
|
|
428
|
-
readmePath: string;
|
|
429
|
-
action: 'created' | 'filled' | 'overwritten';
|
|
430
|
-
projectInfo?: {
|
|
431
|
-
projectName: string;
|
|
432
|
-
projectType: 'library' | 'application' | 'monorepo' | 'unknown';
|
|
433
|
-
language: string;
|
|
434
|
-
framework?: string;
|
|
435
|
-
packageManager?: string;
|
|
436
|
-
hasTests: boolean;
|
|
437
|
-
mainDirs: string[];
|
|
438
|
-
};
|
|
439
|
-
message: string;
|
|
440
|
-
}
|
|
441
|
-
```
|
|
442
|
-
|
|
443
|
-
**Smart Mode Features**:
|
|
444
|
-
- Auto-detects project type, language, and framework
|
|
445
|
-
- Generates framework-specific conventions (React, Vue, Python, etc.)
|
|
446
|
-
- For subdirectories with parent READMEs, generates differential content
|
|
447
|
-
- Handles empty files (< 50 chars) by auto-filling them
|
|
448
|
-
- Analyzes directory structure and dependencies
|
|
449
|
-
|
|
450
|
-
**Example Usage**:
|
|
451
|
-
|
|
452
|
-
```typescript
|
|
453
|
-
// Create smart README for React project
|
|
454
|
-
{
|
|
455
|
-
targetPath: "/path/to/react-app",
|
|
456
|
-
smart: true
|
|
457
|
-
}
|
|
458
|
-
// Result: Auto-detects React, generates component naming conventions, etc.
|
|
459
|
-
|
|
460
|
-
// Fill empty subdirectory README
|
|
461
|
-
{
|
|
462
|
-
targetPath: "/path/to/monorepo/apps/frontend"
|
|
463
|
-
}
|
|
464
|
-
// Result: Generates differential content extending root README
|
|
465
|
-
```
|
|
466
|
-
|
|
467
457
|
### `discover_ai_readmes`
|
|
468
458
|
|
|
469
459
|
Scans your project and discovers all AI_README.md files.
|
|
@@ -586,6 +576,59 @@ Update an AI_README.md file with specified operations.
|
|
|
586
576
|
}
|
|
587
577
|
```
|
|
588
578
|
|
|
579
|
+
### `init_ai_readme`
|
|
580
|
+
|
|
581
|
+
Initialize and populate empty AI_README files in your project.
|
|
582
|
+
|
|
583
|
+
```typescript
|
|
584
|
+
// Parameters
|
|
585
|
+
{
|
|
586
|
+
projectRoot: string; // Required: Project root directory
|
|
587
|
+
excludePatterns?: string[]; // Optional: Glob patterns to exclude
|
|
588
|
+
targetPath?: string; // Optional: Specific directory to initialize
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
// Returns
|
|
592
|
+
{
|
|
593
|
+
success: boolean;
|
|
594
|
+
message: string;
|
|
595
|
+
readmesToInitialize: string[]; // Paths to empty AI_README files
|
|
596
|
+
instructions: string; // Detailed step-by-step guide for populating
|
|
597
|
+
}
|
|
598
|
+
```
|
|
599
|
+
|
|
600
|
+
**Features:**
|
|
601
|
+
- š Scans project for empty or missing AI_README files
|
|
602
|
+
- š Creates root-level AI_README if none exist
|
|
603
|
+
- š Generates detailed step-by-step instructions for each file
|
|
604
|
+
- šÆ Can target specific directories with `targetPath` parameter
|
|
605
|
+
- š¤ Guides AI through analysis: tech stack, patterns, conventions
|
|
606
|
+
|
|
607
|
+
**Example Usage:**
|
|
608
|
+
|
|
609
|
+
```typescript
|
|
610
|
+
// Initialize all empty AI_READMEs in project
|
|
611
|
+
{
|
|
612
|
+
projectRoot: "/path/to/project"
|
|
613
|
+
}
|
|
614
|
+
|
|
615
|
+
// Initialize only in specific directory
|
|
616
|
+
{
|
|
617
|
+
projectRoot: "/path/to/project",
|
|
618
|
+
targetPath: "apps/backend"
|
|
619
|
+
}
|
|
620
|
+
```
|
|
621
|
+
|
|
622
|
+
**Typical Workflow:**
|
|
623
|
+
1. AI assistant runs `init_ai_readme`
|
|
624
|
+
2. Receives detailed instructions for each empty file
|
|
625
|
+
3. Follows instructions:
|
|
626
|
+
- Uses `Glob` to scan directory
|
|
627
|
+
- Reads 2-5 key source files
|
|
628
|
+
- Analyzes tech stack, patterns, conventions
|
|
629
|
+
4. Uses `update_ai_readme` to populate each file
|
|
630
|
+
5. Verifies with `get_context_for_file` or `validate_ai_readmes`
|
|
631
|
+
|
|
589
632
|
### `validate_ai_readmes`
|
|
590
633
|
|
|
591
634
|
Validate all AI_README.md files in your project for quality and token efficiency.
|
package/dist/index.js
CHANGED
|
@@ -296,6 +296,8 @@ async function getContextForFile(input) {
|
|
|
296
296
|
const index = await scanner.scan();
|
|
297
297
|
const router = new ContextRouter(index);
|
|
298
298
|
const contexts = await router.getContextForFile(filePath, includeRoot);
|
|
299
|
+
const hasEmptyReadmes = contexts.some((ctx) => !ctx.content || ctx.content.trim().length === 0);
|
|
300
|
+
const hasNoReadmes = contexts.length === 0;
|
|
299
301
|
const formattedContexts = contexts.map((ctx) => ({
|
|
300
302
|
path: ctx.path,
|
|
301
303
|
relevance: ctx.relevance,
|
|
@@ -305,33 +307,39 @@ async function getContextForFile(input) {
|
|
|
305
307
|
let promptText = `## \u{1F4DA} Project Context for: ${filePath}
|
|
306
308
|
|
|
307
309
|
`;
|
|
308
|
-
if (
|
|
309
|
-
promptText += `\u26A0\uFE0F **
|
|
310
|
+
if (hasNoReadmes || hasEmptyReadmes) {
|
|
311
|
+
promptText += `\u26A0\uFE0F **Empty or missing AI_README files detected.**
|
|
310
312
|
|
|
311
313
|
`;
|
|
312
|
-
promptText +=
|
|
313
|
-
|
|
314
|
-
promptText += `- Project architecture and conventions
|
|
314
|
+
promptText += `**Recommended Action:** Use the \`init_ai_readme\` tool to automatically populate AI_README files.
|
|
315
|
+
|
|
315
316
|
`;
|
|
316
|
-
promptText +=
|
|
317
|
+
promptText += `\`\`\`
|
|
317
318
|
`;
|
|
318
|
-
promptText +=
|
|
319
|
+
promptText += `init_ai_readme({ projectRoot: "${projectRoot.replace(/\\/g, "/")}" })
|
|
319
320
|
`;
|
|
320
|
-
promptText +=
|
|
321
|
+
promptText += `\`\`\`
|
|
321
322
|
|
|
322
323
|
`;
|
|
323
|
-
promptText +=
|
|
324
|
+
promptText += `This tool will:
|
|
324
325
|
`;
|
|
325
|
-
promptText +=
|
|
326
|
+
promptText += `- Scan the project for empty AI_README files
|
|
326
327
|
`;
|
|
327
|
-
promptText += `-
|
|
328
|
+
promptText += `- Guide you through populating them with conventions
|
|
328
329
|
`;
|
|
329
|
-
promptText += `-
|
|
330
|
+
promptText += `- Ensure consistent documentation across your project
|
|
331
|
+
|
|
330
332
|
`;
|
|
331
|
-
promptText +=
|
|
333
|
+
promptText += `\u{1F4A1} Alternatively, you can manually create and populate AI_README.md files, then call this tool again.
|
|
334
|
+
|
|
335
|
+
`;
|
|
336
|
+
promptText += `---
|
|
337
|
+
|
|
332
338
|
`;
|
|
333
|
-
}
|
|
334
|
-
|
|
339
|
+
}
|
|
340
|
+
const nonEmptyContexts = contexts.filter((ctx) => ctx.content && ctx.content.trim().length > 0);
|
|
341
|
+
if (nonEmptyContexts.length > 0) {
|
|
342
|
+
for (const ctx of nonEmptyContexts) {
|
|
335
343
|
if (ctx.relevance === "root") {
|
|
336
344
|
promptText += `### Root Conventions (${ctx.path})
|
|
337
345
|
|
|
@@ -385,14 +393,10 @@ var ReadmeUpdater = class {
|
|
|
385
393
|
*/
|
|
386
394
|
async update(readmePath, operations) {
|
|
387
395
|
try {
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
error: `File not found: ${readmePath}`,
|
|
392
|
-
changes: []
|
|
393
|
-
};
|
|
396
|
+
let content = "";
|
|
397
|
+
if (existsSync(readmePath)) {
|
|
398
|
+
content = await readFile3(readmePath, "utf-8");
|
|
394
399
|
}
|
|
395
|
-
const content = await readFile3(readmePath, "utf-8");
|
|
396
400
|
let updatedContent = content;
|
|
397
401
|
const changes = [];
|
|
398
402
|
for (const operation of operations) {
|
|
@@ -916,353 +920,180 @@ async function validateAIReadmes(input) {
|
|
|
916
920
|
|
|
917
921
|
// src/tools/init.ts
|
|
918
922
|
import { z as z5 } from "zod";
|
|
919
|
-
import {
|
|
920
|
-
import { join as join5, dirname as dirname4, relative } from "path";
|
|
921
|
-
import { existsSync as existsSync4 } from "fs";
|
|
922
|
-
import { fileURLToPath } from "url";
|
|
923
|
-
|
|
924
|
-
// src/core/detector.ts
|
|
925
|
-
import { readFile as readFile5, readdir, stat } from "fs/promises";
|
|
923
|
+
import { writeFile as writeFile2 } from "fs/promises";
|
|
926
924
|
import { join as join4 } from "path";
|
|
927
925
|
import { existsSync as existsSync3 } from "fs";
|
|
928
|
-
var ProjectDetector = class {
|
|
929
|
-
constructor(targetPath) {
|
|
930
|
-
this.targetPath = targetPath;
|
|
931
|
-
}
|
|
932
|
-
/**
|
|
933
|
-
* Detect project information by analyzing files and structure
|
|
934
|
-
*/
|
|
935
|
-
async detect() {
|
|
936
|
-
const info = {
|
|
937
|
-
projectName: "Project",
|
|
938
|
-
projectType: "unknown",
|
|
939
|
-
language: "JavaScript",
|
|
940
|
-
hasTests: false,
|
|
941
|
-
mainDirs: []
|
|
942
|
-
};
|
|
943
|
-
const packageJsonPath = join4(this.targetPath, "package.json");
|
|
944
|
-
if (existsSync3(packageJsonPath)) {
|
|
945
|
-
await this.analyzePackageJson(packageJsonPath, info);
|
|
946
|
-
}
|
|
947
|
-
if (existsSync3(join4(this.targetPath, "requirements.txt")) || existsSync3(join4(this.targetPath, "setup.py")) || existsSync3(join4(this.targetPath, "pyproject.toml"))) {
|
|
948
|
-
info.language = "Python";
|
|
949
|
-
}
|
|
950
|
-
if (existsSync3(join4(this.targetPath, "go.mod"))) {
|
|
951
|
-
info.language = "Go";
|
|
952
|
-
}
|
|
953
|
-
if (existsSync3(join4(this.targetPath, "Cargo.toml"))) {
|
|
954
|
-
info.language = "Rust";
|
|
955
|
-
}
|
|
956
|
-
if (existsSync3(join4(this.targetPath, "pom.xml")) || existsSync3(join4(this.targetPath, "build.gradle"))) {
|
|
957
|
-
info.language = "Java";
|
|
958
|
-
}
|
|
959
|
-
await this.analyzeStructure(info);
|
|
960
|
-
return info;
|
|
961
|
-
}
|
|
962
|
-
/**
|
|
963
|
-
* Analyze package.json to extract project information
|
|
964
|
-
*/
|
|
965
|
-
async analyzePackageJson(path, info) {
|
|
966
|
-
try {
|
|
967
|
-
const content = await readFile5(path, "utf-8");
|
|
968
|
-
const pkg = JSON.parse(content);
|
|
969
|
-
if (pkg.name) {
|
|
970
|
-
info.projectName = pkg.name;
|
|
971
|
-
}
|
|
972
|
-
if (pkg.devDependencies?.typescript || pkg.dependencies?.typescript) {
|
|
973
|
-
info.language = "TypeScript";
|
|
974
|
-
}
|
|
975
|
-
if (pkg.dependencies?.react || pkg.devDependencies?.react) {
|
|
976
|
-
info.framework = "React";
|
|
977
|
-
} else if (pkg.dependencies?.vue || pkg.devDependencies?.vue) {
|
|
978
|
-
info.framework = "Vue";
|
|
979
|
-
} else if (pkg.dependencies?.["@angular/core"]) {
|
|
980
|
-
info.framework = "Angular";
|
|
981
|
-
} else if (pkg.dependencies?.next) {
|
|
982
|
-
info.framework = "Next.js";
|
|
983
|
-
} else if (pkg.dependencies?.express) {
|
|
984
|
-
info.framework = "Express";
|
|
985
|
-
} else if (pkg.dependencies?.nestjs || pkg.dependencies?.["@nestjs/core"]) {
|
|
986
|
-
info.framework = "NestJS";
|
|
987
|
-
}
|
|
988
|
-
const lockFiles = await readdir(this.targetPath);
|
|
989
|
-
if (lockFiles.includes("pnpm-lock.yaml")) {
|
|
990
|
-
info.packageManager = "pnpm";
|
|
991
|
-
} else if (lockFiles.includes("yarn.lock")) {
|
|
992
|
-
info.packageManager = "yarn";
|
|
993
|
-
} else if (lockFiles.includes("bun.lockb")) {
|
|
994
|
-
info.packageManager = "bun";
|
|
995
|
-
} else if (lockFiles.includes("package-lock.json")) {
|
|
996
|
-
info.packageManager = "npm";
|
|
997
|
-
}
|
|
998
|
-
if (pkg.workspaces || existsSync3(join4(this.targetPath, "pnpm-workspace.yaml"))) {
|
|
999
|
-
info.projectType = "monorepo";
|
|
1000
|
-
}
|
|
1001
|
-
if (!info.projectType || info.projectType === "unknown") {
|
|
1002
|
-
if (pkg.main || pkg.exports) {
|
|
1003
|
-
info.projectType = "library";
|
|
1004
|
-
} else {
|
|
1005
|
-
info.projectType = "application";
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
if (pkg.dependencies) {
|
|
1009
|
-
info.dependencies = Object.keys(pkg.dependencies).slice(0, 5);
|
|
1010
|
-
}
|
|
1011
|
-
} catch (error) {
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
/**
|
|
1015
|
-
* Analyze directory structure
|
|
1016
|
-
*/
|
|
1017
|
-
async analyzeStructure(info) {
|
|
1018
|
-
try {
|
|
1019
|
-
const entries = await readdir(this.targetPath);
|
|
1020
|
-
const dirs = [];
|
|
1021
|
-
for (const entry of entries) {
|
|
1022
|
-
try {
|
|
1023
|
-
const entryPath = join4(this.targetPath, entry);
|
|
1024
|
-
const stats = await stat(entryPath);
|
|
1025
|
-
if (stats.isDirectory()) {
|
|
1026
|
-
if (["src", "lib", "app", "pages", "components", "api", "server", "client"].includes(entry)) {
|
|
1027
|
-
dirs.push(entry);
|
|
1028
|
-
}
|
|
1029
|
-
if (["test", "tests", "__tests__", "spec"].includes(entry)) {
|
|
1030
|
-
info.hasTests = true;
|
|
1031
|
-
}
|
|
1032
|
-
if (["apps", "packages", "modules"].includes(entry)) {
|
|
1033
|
-
info.projectType = "monorepo";
|
|
1034
|
-
dirs.push(entry);
|
|
1035
|
-
}
|
|
1036
|
-
}
|
|
1037
|
-
} catch {
|
|
1038
|
-
}
|
|
1039
|
-
}
|
|
1040
|
-
info.mainDirs = dirs;
|
|
1041
|
-
} catch (error) {
|
|
1042
|
-
}
|
|
1043
|
-
}
|
|
1044
|
-
};
|
|
1045
|
-
|
|
1046
|
-
// src/tools/init.ts
|
|
1047
926
|
var initSchema = z5.object({
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
smart: z5.boolean().optional().describe("Enable smart content generation based on project analysis (default: true)")
|
|
927
|
+
projectRoot: z5.string().describe("The root directory of the project"),
|
|
928
|
+
excludePatterns: z5.array(z5.string()).optional().describe("Glob patterns to exclude when scanning"),
|
|
929
|
+
targetPath: z5.string().optional().describe("Specific directory to initialize (optional, defaults to scanning entire project)")
|
|
1052
930
|
});
|
|
1053
931
|
async function initAIReadme(input) {
|
|
1054
|
-
const {
|
|
1055
|
-
const
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
if (fileExists) {
|
|
1069
|
-
const existingContent = await readFile6(readmePath, "utf-8");
|
|
1070
|
-
isEmpty = existingContent.trim().length < 50;
|
|
1071
|
-
if (!isEmpty && !overwrite) {
|
|
1072
|
-
return {
|
|
1073
|
-
success: false,
|
|
1074
|
-
error: "AI_README.md already exists with content",
|
|
1075
|
-
message: `AI_README.md already exists at ${readmePath}. Use overwrite: true to replace it.`,
|
|
1076
|
-
existingPath: readmePath
|
|
1077
|
-
};
|
|
1078
|
-
}
|
|
932
|
+
const { projectRoot, excludePatterns, targetPath } = input;
|
|
933
|
+
const scanner = new AIReadmeScanner(projectRoot, {
|
|
934
|
+
excludePatterns,
|
|
935
|
+
cacheContent: true
|
|
936
|
+
});
|
|
937
|
+
const index = await scanner.scan();
|
|
938
|
+
const emptyReadmes = [];
|
|
939
|
+
for (const readme of index.readmes) {
|
|
940
|
+
if (!readme.content || readme.content.trim().length === 0) {
|
|
941
|
+
emptyReadmes.push({
|
|
942
|
+
path: readme.path,
|
|
943
|
+
dirPath: readme.path.replace(/[\/\\]AI_README\.md$/, ""),
|
|
944
|
+
needsCreation: false
|
|
945
|
+
});
|
|
1079
946
|
}
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
detectedInfo = projectInfo;
|
|
1086
|
-
const parentReadme = await findParentReadme(targetPath);
|
|
1087
|
-
const isSubdirectory = parentReadme !== null;
|
|
1088
|
-
content = await generateSmartContent(projectInfo, targetPath, isSubdirectory, parentReadme);
|
|
1089
|
-
} else {
|
|
1090
|
-
const __filename2 = fileURLToPath(import.meta.url);
|
|
1091
|
-
const __dirname2 = dirname4(__filename2);
|
|
1092
|
-
const templatePath = join5(__dirname2, "..", "..", "docs", "templates", "basic.md");
|
|
1093
|
-
if (!existsSync4(templatePath)) {
|
|
1094
|
-
return {
|
|
1095
|
-
success: false,
|
|
1096
|
-
error: `Template file not found: ${templatePath}`,
|
|
1097
|
-
message: "Template file is missing. Please check installation."
|
|
1098
|
-
};
|
|
1099
|
-
}
|
|
1100
|
-
content = await readFile6(templatePath, "utf-8");
|
|
1101
|
-
const finalProjectName = projectName || "Project Name";
|
|
1102
|
-
content = content.replace(/\{\{PROJECT_NAME\}\}/g, finalProjectName);
|
|
947
|
+
}
|
|
948
|
+
if (index.readmes.length === 0) {
|
|
949
|
+
const rootReadmePath = join4(projectRoot, "AI_README.md");
|
|
950
|
+
if (!existsSync3(rootReadmePath)) {
|
|
951
|
+
await writeFile2(rootReadmePath, "", "utf-8");
|
|
1103
952
|
}
|
|
1104
|
-
|
|
1105
|
-
|
|
953
|
+
emptyReadmes.push({
|
|
954
|
+
path: rootReadmePath,
|
|
955
|
+
dirPath: projectRoot,
|
|
956
|
+
needsCreation: true
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
let targetReadmes = emptyReadmes;
|
|
960
|
+
if (targetPath) {
|
|
961
|
+
const normalizedTarget = targetPath.replace(/\\/g, "/");
|
|
962
|
+
targetReadmes = emptyReadmes.filter(
|
|
963
|
+
(r) => r.dirPath.replace(/\\/g, "/").includes(normalizedTarget)
|
|
964
|
+
);
|
|
965
|
+
}
|
|
966
|
+
if (targetReadmes.length === 0) {
|
|
1106
967
|
return {
|
|
1107
968
|
success: true,
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
projectInfo: smart ? detectedInfo : void 0,
|
|
1111
|
-
message: `Successfully ${action} AI_README.md at ${readmePath}. ${smart ? "Content generated based on project analysis." : "Edit the file to customize it for your project."}`
|
|
1112
|
-
};
|
|
1113
|
-
} catch (error) {
|
|
1114
|
-
return {
|
|
1115
|
-
success: false,
|
|
1116
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1117
|
-
message: `Failed to create AI_README.md: ${error instanceof Error ? error.message : String(error)}`
|
|
969
|
+
message: "\u2705 All AI_README files are already populated!",
|
|
970
|
+
initialized: []
|
|
1118
971
|
};
|
|
1119
972
|
}
|
|
1120
|
-
}
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
}
|
|
973
|
+
let promptText = `# \u{1F680} AI_README Initialization
|
|
974
|
+
|
|
975
|
+
`;
|
|
976
|
+
promptText += `Found ${targetReadmes.length} AI_README file(s) that need population.
|
|
977
|
+
|
|
978
|
+
`;
|
|
979
|
+
if (targetReadmes.some((r) => r.needsCreation)) {
|
|
980
|
+
promptText += `\u2705 Created empty AI_README file(s) at:
|
|
981
|
+
`;
|
|
982
|
+
for (const readme of targetReadmes.filter((r) => r.needsCreation)) {
|
|
983
|
+
promptText += `- ${readme.path.replace(/\\/g, "/")}
|
|
984
|
+
`;
|
|
1131
985
|
}
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
currentPath = parentPath;
|
|
986
|
+
promptText += `
|
|
987
|
+
`;
|
|
1135
988
|
}
|
|
1136
|
-
|
|
1137
|
-
}
|
|
1138
|
-
async function generateSmartContent(projectInfo, targetPath, isSubdirectory, parentPath) {
|
|
1139
|
-
let content = `# ${projectInfo.projectName}
|
|
989
|
+
promptText += `## \u{1F4CB} Required Actions
|
|
1140
990
|
|
|
1141
991
|
`;
|
|
1142
|
-
|
|
1143
|
-
const relativePath = relative(parentPath, targetPath);
|
|
1144
|
-
content += `> This README extends the root AI_README.md with ${relativePath}-specific conventions.
|
|
992
|
+
promptText += `You must populate the following AI_README files by analyzing their respective directories:
|
|
1145
993
|
|
|
1146
994
|
`;
|
|
1147
|
-
|
|
1148
|
-
|
|
995
|
+
for (let i = 0; i < targetReadmes.length; i++) {
|
|
996
|
+
const readme = targetReadmes[i];
|
|
997
|
+
const readmePath = readme.path.replace(/\\/g, "/");
|
|
998
|
+
const dirPath = readme.dirPath.replace(/\\/g, "/");
|
|
999
|
+
promptText += `### ${i + 1}. ${readmePath}
|
|
1149
1000
|
|
|
1150
1001
|
`;
|
|
1151
|
-
|
|
1002
|
+
promptText += `**Directory to analyze:** \`${dirPath}\`
|
|
1003
|
+
|
|
1152
1004
|
`;
|
|
1153
|
-
|
|
1005
|
+
promptText += `**Steps:**
|
|
1006
|
+
|
|
1154
1007
|
`;
|
|
1155
|
-
|
|
1156
|
-
content += `- **Framework:** ${projectInfo.framework}
|
|
1008
|
+
promptText += `1. **Scan directory contents:**
|
|
1157
1009
|
`;
|
|
1158
|
-
|
|
1159
|
-
content += `
|
|
1010
|
+
promptText += ` \`\`\`
|
|
1160
1011
|
`;
|
|
1161
|
-
|
|
1162
|
-
|
|
1012
|
+
promptText += ` Use Glob: pattern="**/*", path="${dirPath}"
|
|
1013
|
+
`;
|
|
1014
|
+
promptText += ` \`\`\`
|
|
1163
1015
|
|
|
1164
1016
|
`;
|
|
1165
|
-
|
|
1166
|
-
content += `- ${dir}/ - [Add description]
|
|
1017
|
+
promptText += `2. **Read key source files** (pick 2-5 representative files):
|
|
1167
1018
|
`;
|
|
1168
|
-
|
|
1169
|
-
content += `
|
|
1019
|
+
promptText += ` - Configuration files (package.json, tsconfig.json, etc.)
|
|
1170
1020
|
`;
|
|
1171
|
-
|
|
1172
|
-
|
|
1021
|
+
promptText += ` - Main source files
|
|
1022
|
+
`;
|
|
1023
|
+
promptText += ` - Important modules/components
|
|
1173
1024
|
|
|
1174
1025
|
`;
|
|
1175
|
-
|
|
1026
|
+
promptText += `3. **Analyze and identify:**
|
|
1176
1027
|
`;
|
|
1177
|
-
|
|
1178
|
-
|
|
1028
|
+
promptText += ` - \u{1F4E6} **Tech Stack**: Frameworks, libraries, languages, tools
|
|
1179
1029
|
`;
|
|
1180
|
-
|
|
1030
|
+
promptText += ` - \u{1F3D7}\uFE0F **Architecture**: Project structure, design patterns
|
|
1181
1031
|
`;
|
|
1182
|
-
|
|
1183
|
-
content += `- Use TypeScript strict mode
|
|
1032
|
+
promptText += ` - \u{1F4DD} **Coding Conventions**: Naming, formatting, patterns
|
|
1184
1033
|
`;
|
|
1185
|
-
|
|
1034
|
+
promptText += ` - \u{1F5C2}\uFE0F **File Structure**: Directory organization, module boundaries
|
|
1035
|
+
|
|
1186
1036
|
`;
|
|
1187
|
-
|
|
1188
|
-
content += `- Follow PEP 8 style guide
|
|
1037
|
+
promptText += `4. **Populate AI_README:**
|
|
1189
1038
|
`;
|
|
1190
|
-
|
|
1039
|
+
promptText += ` \`\`\`
|
|
1191
1040
|
`;
|
|
1192
|
-
|
|
1193
|
-
content += `- [Add more style guidelines]
|
|
1194
|
-
|
|
1041
|
+
promptText += ` Use update_ai_readme:
|
|
1195
1042
|
`;
|
|
1196
|
-
|
|
1197
|
-
content += `### ${projectInfo.framework} Conventions
|
|
1043
|
+
promptText += ` {
|
|
1198
1044
|
`;
|
|
1199
|
-
|
|
1200
|
-
content += `- Component naming: PascalCase
|
|
1045
|
+
promptText += ` readmePath: "${readmePath}",
|
|
1201
1046
|
`;
|
|
1202
|
-
|
|
1047
|
+
promptText += ` operations: [{
|
|
1203
1048
|
`;
|
|
1204
|
-
|
|
1049
|
+
promptText += ` type: "append",
|
|
1205
1050
|
`;
|
|
1206
|
-
|
|
1207
|
-
content += `- Component naming: PascalCase or kebab-case
|
|
1051
|
+
promptText += ` content: "<your analysis in markdown format>"
|
|
1208
1052
|
`;
|
|
1209
|
-
|
|
1053
|
+
promptText += ` }]
|
|
1210
1054
|
`;
|
|
1211
|
-
}
|
|
1212
|
-
content += `
|
|
1055
|
+
promptText += ` }
|
|
1213
1056
|
`;
|
|
1214
|
-
|
|
1215
|
-
content += `## Testing
|
|
1057
|
+
promptText += ` \`\`\`
|
|
1216
1058
|
|
|
1217
1059
|
`;
|
|
1218
|
-
|
|
1219
|
-
content += `- Tests are located in test directories
|
|
1060
|
+
promptText += ` **Include these sections:**
|
|
1220
1061
|
`;
|
|
1221
|
-
|
|
1222
|
-
if (projectInfo.packageManager) {
|
|
1223
|
-
content += `- Run: \`${projectInfo.packageManager} test\`
|
|
1062
|
+
promptText += ` - \`## Tech Stack\` - List frameworks, libraries, tools
|
|
1224
1063
|
`;
|
|
1225
|
-
|
|
1226
|
-
content += `- Run: [Add test command]
|
|
1064
|
+
promptText += ` - \`## Architecture Patterns\` - Design patterns, project structure
|
|
1227
1065
|
`;
|
|
1228
|
-
|
|
1229
|
-
content += `- Coverage target: [Add target or "not enforced"]
|
|
1230
|
-
|
|
1066
|
+
promptText += ` - \`## Coding Conventions\` - Naming, formatting, best practices
|
|
1231
1067
|
`;
|
|
1232
|
-
|
|
1233
|
-
content += `## Key Dependencies
|
|
1068
|
+
promptText += ` - \`## File Structure\` - Directory organization (brief)
|
|
1234
1069
|
|
|
1235
1070
|
`;
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
`;
|
|
1239
|
-
}
|
|
1240
|
-
content += `
|
|
1071
|
+
promptText += ` **Keep it concise:** AI_READMEs should be <400 tokens. Focus on actionable conventions.
|
|
1072
|
+
|
|
1241
1073
|
`;
|
|
1242
1074
|
}
|
|
1243
|
-
|
|
1075
|
+
promptText += `---
|
|
1244
1076
|
|
|
1245
1077
|
`;
|
|
1246
|
-
|
|
1247
|
-
content += `- Install: \`${projectInfo.packageManager} install\`
|
|
1078
|
+
promptText += `\u{1F4A1} **Tips:**
|
|
1248
1079
|
`;
|
|
1249
|
-
|
|
1080
|
+
promptText += `- Work through each README sequentially
|
|
1250
1081
|
`;
|
|
1251
|
-
|
|
1082
|
+
promptText += `- Be concise - every token counts!
|
|
1252
1083
|
`;
|
|
1253
|
-
|
|
1254
|
-
content += `
|
|
1084
|
+
promptText += `- Focus on conventions that help generate better code
|
|
1255
1085
|
`;
|
|
1256
|
-
|
|
1086
|
+
promptText += `- After completing all, you can verify with \`validate_ai_readmes\`
|
|
1257
1087
|
|
|
1258
1088
|
`;
|
|
1259
|
-
|
|
1260
|
-
`;
|
|
1261
|
-
content += `- [Security considerations]
|
|
1262
|
-
`;
|
|
1263
|
-
content += `- [Performance considerations]
|
|
1089
|
+
promptText += `**Start with the first AI_README now!**
|
|
1264
1090
|
`;
|
|
1265
|
-
return
|
|
1091
|
+
return {
|
|
1092
|
+
success: true,
|
|
1093
|
+
message: `Found ${targetReadmes.length} AI_README file(s) to initialize`,
|
|
1094
|
+
readmesToInitialize: targetReadmes.map((r) => r.path.replace(/\\/g, "/")),
|
|
1095
|
+
instructions: promptText
|
|
1096
|
+
};
|
|
1266
1097
|
}
|
|
1267
1098
|
|
|
1268
1099
|
// src/index.ts
|
|
@@ -1287,12 +1118,12 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
1287
1118
|
},
|
|
1288
1119
|
{
|
|
1289
1120
|
name: "get_context_for_file",
|
|
1290
|
-
description:
|
|
1121
|
+
description: '\u26A0\uFE0F REQUIRED: Call this tool BEFORE creating or editing ANY file to get project conventions.\n\nGets relevant AI_README context for a specific file path. Returns formatted guidelines that MUST be followed when writing code.\n\n**When to call:**\n- BEFORE using Write tool (creating new files)\n- BEFORE using Edit tool (modifying existing files)\n- AFTER updating AI_README (to get fresh context)\n- Works even if the target file does not exist yet\n\n**Workflows:**\n1. Following conventions: get_context_for_file \u2192 Write/Edit\n2. Establishing NEW conventions: update_ai_readme \u2192 get_context_for_file \u2192 Write/Edit\n3. Empty AI_README detected: Use `init_ai_readme` tool (tool will suggest this)\n4. Document discovered patterns: Write/Edit \u2192 update_ai_readme\n\n**Example:** Before creating Button.tsx, call get_context_for_file with filePath="src/components/Button.tsx" to learn styling preferences (CSS Modules vs Tailwind), naming conventions, component patterns, etc.',
|
|
1291
1122
|
inputSchema: zodToJsonSchema(getContextSchema)
|
|
1292
1123
|
},
|
|
1293
1124
|
{
|
|
1294
1125
|
name: "update_ai_readme",
|
|
1295
|
-
description:
|
|
1126
|
+
description: 'Update an AI_README.md file to document conventions, patterns, or architectural decisions. Supports append, prepend, replace, insert-after, insert-before operations.\n\n**CRITICAL: Token Efficiency Rules**\n- Keep content EXTREMELY concise (< 400 tokens ideal, < 600 warning, > 1000 error)\n- Only document ACTIONABLE conventions that affect code generation\n- NO explanations, NO examples, NO verbose descriptions\n- Use bullet points, avoid complete sentences when possible\n- Focus on: tech stack, naming rules, patterns, architectural decisions\n- AVOID: project background, how-to guides, documentation, obvious practices\n\n**When to use:**\n1. BEFORE code changes: Establishing NEW conventions\n - User requests style/approach change (e.g., "use CSS Modules")\n - Choosing between approaches (e.g., state management)\n - Setting up new architectural patterns\n - Workflow: update_ai_readme \u2192 get_context_for_file \u2192 Write/Edit\n\n2. AFTER code changes: Documenting discovered patterns\n - Found consistent patterns in existing code\n - Workflow: Write/Edit \u2192 update_ai_readme\n\n**Quality checklist before updating:**\nIs this a CONVENTION or PATTERN (not documentation)?\nWill this help AI generate better code?\nIs it concise (<3 words per bullet)?\nDoes it avoid obvious/general practices?\nReject: project descriptions, tutorials, general best practices\n\n**Example (GOOD):**\n- Use CSS Modules\n- Components in PascalCase\n- Test coverage: 80%+\n\n**Example (BAD - too verbose):**\n- We use CSS Modules for styling because it provides better type safety and scoping compared to Tailwind...',
|
|
1296
1127
|
inputSchema: zodToJsonSchema(updateSchema)
|
|
1297
1128
|
},
|
|
1298
1129
|
{
|
|
@@ -1302,7 +1133,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
1302
1133
|
},
|
|
1303
1134
|
{
|
|
1304
1135
|
name: "init_ai_readme",
|
|
1305
|
-
description:
|
|
1136
|
+
description: '\u{1F680} Initialize and populate empty AI_README files in your project.\n\nScans the project for empty or missing AI_README files and guides you through populating them with project conventions.\n\n**When to use:**\n- First time setting up AI_README in a project\n- When get_context_for_file detects empty AI_README files\n- After creating new empty AI_README.md files manually\n- To populate multiple AI_README files at once\n\n**What it does:**\n1. Scans project for empty AI_README files\n2. Creates root-level AI_README if none exist\n3. Provides step-by-step instructions to populate each file\n4. Guides analysis of tech stack, patterns, and conventions\n\n**Workflow:**\n1. Call init_ai_readme\n2. Follow the instructions to explore directories\n3. Use update_ai_readme to populate each file\n4. Call get_context_for_file to verify and use conventions\n\n**Example:** `init_ai_readme({ projectRoot: "/path/to/project" })`',
|
|
1306
1137
|
inputSchema: zodToJsonSchema(initSchema)
|
|
1307
1138
|
}
|
|
1308
1139
|
]
|
|
@@ -1366,7 +1197,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1366
1197
|
content: [
|
|
1367
1198
|
{
|
|
1368
1199
|
type: "text",
|
|
1369
|
-
text: JSON.stringify(result, null, 2)
|
|
1200
|
+
text: result.instructions || JSON.stringify(result, null, 2)
|
|
1370
1201
|
}
|
|
1371
1202
|
]
|
|
1372
1203
|
};
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/index.ts","../src/tools/discover.ts","../src/core/scanner.ts","../src/tools/getContext.ts","../src/core/router.ts","../src/tools/update.ts","../src/core/updater.ts","../src/core/validator.ts","../src/types/index.ts","../src/tools/validate.ts","../src/tools/init.ts","../src/core/detector.ts"],"sourcesContent":["/**\n * AI_README MCP Server\n * Entry point for the MCP server\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { zodToJsonSchema } from 'zod-to-json-schema';\nimport {\n discoverAIReadmes,\n discoverSchema,\n type DiscoverInput,\n} from './tools/discover.js';\nimport {\n getContextForFile,\n getContextSchema,\n type GetContextInput,\n} from './tools/getContext.js';\nimport {\n updateAIReadme,\n updateSchema,\n type UpdateInput,\n} from './tools/update.js';\nimport {\n validateAIReadmes,\n validateSchema,\n type ValidateInput,\n} from './tools/validate.js';\nimport {\n initAIReadme,\n initSchema,\n type InitInput,\n} from './tools/init.js';\n\nconst server = new Server(\n {\n name: 'ai-readme-mcp',\n version: '0.3.2',\n },\n {\n capabilities: {\n tools: {},\n },\n }\n);\n\n// Register tool: list_tools\nserver.setRequestHandler(ListToolsRequestSchema, async () => {\n return {\n tools: [\n {\n name: 'discover_ai_readmes',\n description:\n 'Scan the project and discover all AI_README.md files. Returns an index of all README files with their paths, scopes, and coverage patterns.',\n inputSchema: zodToJsonSchema(discoverSchema),\n },\n {\n name: 'get_context_for_file',\n description:\n 'Get relevant AI_README context for a specific file path. Returns formatted context from relevant README files to help understand project conventions. Use this BEFORE creating or editing files - works even if the file does not exist yet.\\n\\nIMPORTANT: When establishing NEW conventions (e.g., switching from Tailwind to CSS Modules), update the AI_README FIRST, then call this tool to get the updated context before writing code.',\n inputSchema: zodToJsonSchema(getContextSchema),\n },\n {\n name: 'update_ai_readme',\n description:\n 'Update an AI_README.md file to document conventions, patterns, or architectural decisions. Supports append, prepend, replace, insert-after, insert-before operations. Auto-validates after update.\\n\\nTwo usage scenarios:\\n1. BEFORE code changes: Establish new conventions (update AI_README ā get_context ā write code)\\n2. AFTER code changes: Document discovered patterns in existing code\\n\\nNote: Only update when documenting NEW conventions - avoid duplicates.',\n inputSchema: zodToJsonSchema(updateSchema),\n },\n {\n name: 'validate_ai_readmes',\n description:\n 'Validate all AI_README.md files in a project. Checks token count, structure, and content quality. Returns validation results with suggestions for improvement.',\n inputSchema: zodToJsonSchema(validateSchema),\n },\n {\n name: 'init_ai_readme',\n description:\n 'Initialize or fill an AI_README.md file with smart content based on project analysis. Auto-detects project type, language, and framework. Use this to create new READMEs or fill empty ones.',\n inputSchema: zodToJsonSchema(initSchema),\n },\n ],\n };\n});\n\n// Register tool: call_tool\nserver.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n\n try {\n if (name === 'discover_ai_readmes') {\n const input = discoverSchema.parse(args) as DiscoverInput;\n const result = await discoverAIReadmes(input);\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(result, null, 2),\n },\n ],\n };\n }\n\n if (name === 'get_context_for_file') {\n const input = getContextSchema.parse(args) as GetContextInput;\n const result = await getContextForFile(input);\n return {\n content: [\n {\n type: 'text',\n text: result.formattedPrompt,\n },\n ],\n };\n }\n\n if (name === 'update_ai_readme') {\n const input = updateSchema.parse(args) as UpdateInput;\n const result = await updateAIReadme(input);\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(result, null, 2),\n },\n ],\n };\n }\n\n if (name === 'validate_ai_readmes') {\n const input = validateSchema.parse(args) as ValidateInput;\n const result = await validateAIReadmes(input);\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(result, null, 2),\n },\n ],\n };\n }\n\n if (name === 'init_ai_readme') {\n const input = initSchema.parse(args) as InitInput;\n const result = await initAIReadme(input);\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(result, null, 2),\n },\n ],\n };\n }\n\n throw new Error(`Unknown tool: ${name}`);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n});\n\nasync function main() {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n console.error('AI_README MCP Server started');\n console.error('Available tools: discover_ai_readmes, get_context_for_file, update_ai_readme, validate_ai_readmes, init_ai_readme');\n}\n\nmain().catch((error) => {\n console.error('Fatal error:', error);\n process.exit(1);\n});\n","/**\n * MCP Tool: discover_ai_readmes\n * Scans the project and discovers all AI_README.md files\n */\n\nimport { z } from 'zod';\nimport { AIReadmeScanner } from '../core/scanner.js';\n\nexport const discoverSchema = z.object({\n projectRoot: z.string().describe('The root directory of the project'),\n excludePatterns: z\n .array(z.string())\n .optional()\n .describe(\"Glob patterns to exclude (e.g., ['node_modules/**', '.git/**'])\"),\n});\n\nexport type DiscoverInput = z.infer<typeof discoverSchema>;\n\nexport async function discoverAIReadmes(input: DiscoverInput) {\n const { projectRoot, excludePatterns } = input;\n\n // Create scanner with options\n const scanner = new AIReadmeScanner(projectRoot, {\n excludePatterns,\n cacheContent: false, // Don't cache content in discovery phase\n });\n\n // Scan the project\n const index = await scanner.scan();\n\n // Format the response\n return {\n projectRoot: index.projectRoot,\n totalFound: index.readmes.length,\n readmeFiles: index.readmes.map((readme) => ({\n path: readme.path,\n scope: readme.scope,\n level: readme.level,\n patterns: readme.patterns,\n })),\n lastUpdated: index.lastUpdated.toISOString(),\n };\n}\n","/**\n * AIReadmeScanner - Scans project directories for AI_README.md files\n */\n\nimport { glob } from 'glob';\nimport { readFile } from 'fs/promises';\nimport { dirname, join } from 'path';\nimport type { ReadmeEntry, ReadmeIndex, ScannerOptions } from '../types/index.js';\n\nexport class AIReadmeScanner {\n private projectRoot: string;\n private options: Required<ScannerOptions>;\n\n constructor(projectRoot: string, options?: ScannerOptions) {\n this.projectRoot = projectRoot;\n this.options = {\n excludePatterns: options?.excludePatterns || [\n '**/node_modules/**',\n '**/.git/**',\n '**/dist/**',\n '**/build/**',\n '**/.next/**',\n '**/coverage/**',\n ],\n cacheContent: options?.cacheContent ?? true,\n readmeFilename: options?.readmeFilename || 'AI_README.md',\n };\n }\n\n /**\n * Scan the project directory for AI_README.md files\n */\n async scan(): Promise<ReadmeIndex> {\n const pattern = `**/${this.options.readmeFilename}`;\n const ignore = this.options.excludePatterns;\n\n // Find all AI_README.md files\n const files = await glob(pattern, {\n cwd: this.projectRoot,\n ignore,\n absolute: false,\n nodir: true,\n });\n\n // Build ReadmeEntry objects\n const readmes: ReadmeEntry[] = [];\n\n for (const file of files) {\n const entry = await this.createReadmeEntry(file);\n readmes.push(entry);\n }\n\n // Sort by level (root first, then deeper levels)\n readmes.sort((a, b) => a.level - b.level);\n\n return {\n projectRoot: this.projectRoot,\n readmes,\n lastUpdated: new Date(),\n };\n }\n\n /**\n * Create a ReadmeEntry from a file path\n */\n private async createReadmeEntry(filePath: string): Promise<ReadmeEntry> {\n // Normalize path to use forward slashes (Unix-style) for consistency\n const normalizedPath = filePath.replace(/\\\\/g, '/');\n const dir = dirname(normalizedPath);\n const level = dir === '.' ? 0 : dir.split('/').length;\n const scope = dir === '.' ? 'root' : dir.replace(/\\//g, '-');\n\n // Generate glob patterns this README covers\n const patterns = this.generatePatterns(dir);\n\n // Optionally cache content\n let content: string | undefined;\n if (this.options.cacheContent) {\n try {\n const fullPath = join(this.projectRoot, filePath);\n content = await readFile(fullPath, 'utf-8');\n } catch (error) {\n console.error(`Failed to read ${filePath}:`, error);\n }\n }\n\n return {\n path: normalizedPath,\n scope,\n level,\n patterns,\n content,\n };\n }\n\n /**\n * Generate glob patterns that this README covers\n */\n private generatePatterns(dir: string): string[] {\n if (dir === '.') {\n return ['**/*']; // Root level covers everything\n }\n\n return [\n `${dir}/**/*`, // All files in this directory and subdirectories\n `${dir}/*`, // Direct children\n ];\n }\n\n /**\n * Refresh the scan (re-scan the project)\n */\n async refresh(): Promise<ReadmeIndex> {\n return this.scan();\n }\n\n /**\n * Get the project root directory\n */\n getProjectRoot(): string {\n return this.projectRoot;\n }\n\n /**\n * Get current scanner options\n */\n getOptions(): Required<ScannerOptions> {\n return { ...this.options };\n }\n}\n","/**\n * MCP Tool: get_context_for_file\n * Gets relevant AI_README context for a specific file path\n */\n\nimport { z } from 'zod';\nimport { AIReadmeScanner } from '../core/scanner.js';\nimport { ContextRouter } from '../core/router.js';\n\nexport const getContextSchema = z.object({\n projectRoot: z.string().describe('The root directory of the project'),\n filePath: z\n .string()\n .describe('The file path to get context for (relative to project root)'),\n includeRoot: z\n .boolean()\n .optional()\n .default(true)\n .describe('Whether to include root-level AI_README (default: true)'),\n excludePatterns: z\n .array(z.string())\n .optional()\n .describe('Glob patterns to exclude when scanning'),\n});\n\nexport type GetContextInput = z.infer<typeof getContextSchema>;\n\nexport async function getContextForFile(input: GetContextInput) {\n const { projectRoot, filePath, includeRoot, excludePatterns } = input;\n\n // First, scan the project to build the index\n const scanner = new AIReadmeScanner(projectRoot, {\n excludePatterns,\n cacheContent: true, // Cache content for context retrieval\n });\n\n const index = await scanner.scan();\n\n // Create router and get context\n const router = new ContextRouter(index);\n const contexts = await router.getContextForFile(filePath, includeRoot);\n\n // Format the response with a helpful prompt template\n const formattedContexts = contexts.map((ctx) => ({\n path: ctx.path,\n relevance: ctx.relevance,\n distance: ctx.distance,\n content: ctx.content,\n }));\n\n // Generate a formatted prompt for the AI\n let promptText = `## š Project Context for: ${filePath}\\n\\n`;\n\n if (contexts.length === 0) {\n // No AI_README found - provide helpful guidance\n promptText += `ā ļø **No AI_README.md files found in this project.**\\n\\n`;\n promptText += `To get the most out of AI assistance, consider creating an AI_README.md file to document:\\n`;\n promptText += `- Project architecture and conventions\\n`;\n promptText += `- Coding standards and best practices\\n`;\n promptText += `- Testing requirements\\n`;\n promptText += `- Common patterns to follow\\n\\n`;\n promptText += `**Quick Start:**\\n`;\n promptText += `Use the \\`init_ai_readme\\` tool to create a template:\\n`;\n promptText += `- Creates AI_README.md from a customizable template\\n`;\n promptText += `- Helps maintain consistency across your team\\n`;\n promptText += `- Improves AI output quality\\n`;\n } else {\n for (const ctx of formattedContexts) {\n if (ctx.relevance === 'root') {\n promptText += `### Root Conventions (${ctx.path})\\n\\n`;\n } else if (ctx.relevance === 'direct') {\n promptText += `### Direct Module Conventions (${ctx.path})\\n\\n`;\n } else {\n promptText += `### Parent Module Conventions (${ctx.path})\\n\\n`;\n }\n\n promptText += ctx.content + '\\n\\n';\n }\n\n promptText += `---\\n`;\n promptText += `**Important:**\\n`;\n promptText += `- Follow the above conventions when making changes\\n`;\n promptText += `- When establishing NEW conventions: update AI_README first ā get context ā write code\\n`;\n promptText += `- When discovering patterns in existing code: document them in AI_README afterward\\n`;\n }\n\n return {\n filePath,\n totalContexts: contexts.length,\n contexts: formattedContexts,\n formattedPrompt: promptText,\n };\n}\n","/**\n * ContextRouter - Routes file paths to relevant AI_README contexts\n */\n\nimport { minimatch } from 'minimatch';\nimport { dirname, join } from 'path';\nimport { readFile } from 'fs/promises';\nimport type { ReadmeContext, ReadmeIndex, ReadmeEntry } from '../types/index.js';\n\nexport class ContextRouter {\n private index: ReadmeIndex;\n\n constructor(index: ReadmeIndex) {\n this.index = index;\n }\n\n /**\n * Get relevant AI_README contexts for a specific file path\n * @param filePath - The file path relative to project root\n * @param includeRoot - Whether to include root-level README (default: true)\n */\n async getContextForFile(\n filePath: string,\n includeRoot: boolean = true\n ): Promise<ReadmeContext[]> {\n const contexts: ReadmeContext[] = [];\n\n // Find matching READMEs\n for (const readme of this.index.readmes) {\n const match = this.matchesPath(filePath, readme);\n\n if (!match) continue;\n\n // Skip root if not requested\n if (!includeRoot && readme.level === 0) continue;\n\n // Calculate distance from file to README\n const distance = this.calculateDistance(filePath, readme);\n\n // Get content\n const content = await this.getReadmeContent(readme);\n\n // Determine relevance\n const relevance = this.determineRelevance(filePath, readme);\n\n contexts.push({\n path: readme.path,\n content,\n relevance,\n distance,\n });\n }\n\n // Sort by distance (closest first) and then by relevance\n contexts.sort((a, b) => {\n if (a.distance !== b.distance) {\n return a.distance - b.distance;\n }\n // If same distance, prioritize direct > parent > root\n const relevanceOrder = { direct: 0, parent: 1, root: 2 };\n return relevanceOrder[a.relevance] - relevanceOrder[b.relevance];\n });\n\n return contexts;\n }\n\n /**\n * Get contexts for multiple files\n */\n async getContextForFiles(filePaths: string[]): Promise<Map<string, ReadmeContext[]>> {\n const results = new Map<string, ReadmeContext[]>();\n\n for (const filePath of filePaths) {\n const contexts = await this.getContextForFile(filePath);\n results.set(filePath, contexts);\n }\n\n return results;\n }\n\n /**\n * Check if a file path matches a README's patterns\n */\n private matchesPath(filePath: string, readme: ReadmeEntry): boolean {\n return readme.patterns.some(pattern => {\n return minimatch(filePath, pattern, { dot: true });\n });\n }\n\n /**\n * Calculate the directory distance between a file and a README\n */\n private calculateDistance(filePath: string, readme: ReadmeEntry): number {\n const fileDir = dirname(filePath);\n const readmeDir = dirname(readme.path);\n\n // If README is at root\n if (readmeDir === '.') {\n return fileDir === '.' ? 0 : fileDir.split('/').length;\n }\n\n // If file is in the same directory as README\n if (fileDir === readmeDir) {\n return 0;\n }\n\n // If file is in a subdirectory of README's directory\n if (fileDir.startsWith(readmeDir + '/')) {\n const subPath = fileDir.slice(readmeDir.length + 1);\n return subPath.split('/').length;\n }\n\n // Calculate levels up\n const fileParts = fileDir.split('/');\n const readmeParts = readmeDir.split('/');\n\n // Find common ancestor\n let commonDepth = 0;\n for (let i = 0; i < Math.min(fileParts.length, readmeParts.length); i++) {\n if (fileParts[i] === readmeParts[i]) {\n commonDepth++;\n } else {\n break;\n }\n }\n\n // Distance is the sum of levels to go up and down\n return (fileParts.length - commonDepth) + (readmeParts.length - commonDepth);\n }\n\n /**\n * Determine the relevance type of a README for a file\n */\n private determineRelevance(\n filePath: string,\n readme: ReadmeEntry\n ): 'root' | 'direct' | 'parent' {\n const fileDir = dirname(filePath);\n const readmeDir = dirname(readme.path);\n\n // Root level README\n if (readmeDir === '.') {\n return 'root';\n }\n\n // Direct match (same directory)\n if (fileDir === readmeDir) {\n return 'direct';\n }\n\n // Parent directory\n if (fileDir.startsWith(readmeDir + '/')) {\n return 'parent';\n }\n\n return 'parent';\n }\n\n /**\n * Get the content of a README (from cache or file system)\n */\n private async getReadmeContent(readme: ReadmeEntry): Promise<string> {\n // Return cached content if available\n if (readme.content) {\n return readme.content;\n }\n\n // Otherwise read from file system\n try {\n const fullPath = join(this.index.projectRoot, readme.path);\n return await readFile(fullPath, 'utf-8');\n } catch (error) {\n console.error(`Failed to read ${readme.path}:`, error);\n return `[Error: Could not read ${readme.path}]`;\n }\n }\n\n /**\n * Update the index (useful after re-scanning)\n */\n updateIndex(index: ReadmeIndex): void {\n this.index = index;\n }\n\n /**\n * Get the current index\n */\n getIndex(): ReadmeIndex {\n return this.index;\n }\n}\n","import { z } from 'zod';\nimport { dirname } from 'path';\nimport { ReadmeUpdater, UpdateOperation } from '../core/updater.js';\nimport { ReadmeValidator } from '../core/validator.js';\n\n/**\n * Zod schema for update operation\n */\nconst updateOperationSchema = z.object({\n type: z.enum(['replace', 'append', 'prepend', 'insert-after', 'insert-before']).describe(\n 'Type of update operation'\n ),\n section: z.string().optional().describe(\n 'Section heading to target (e.g., \"## Coding Conventions\")'\n ),\n searchText: z.string().optional().describe(\n 'Text to search for (required for replace operation)'\n ),\n content: z.string().describe('Content to add or replace'),\n});\n\n/**\n * Zod schema for update_ai_readme tool\n */\nexport const updateSchema = z.object({\n readmePath: z.string().describe('Path to the AI_README.md file to update'),\n operations: z.array(updateOperationSchema).describe(\n 'List of update operations to perform'\n ),\n});\n\nexport type UpdateInput = z.infer<typeof updateSchema>;\n\n/**\n * Update an AI_README.md file with specified operations\n *\n * @param input - Update parameters\n * @returns Update result with changes\n *\n * @example\n * ```typescript\n * await updateAIReadme({\n * readmePath: 'apps/frontend/AI_README.md',\n * operations: [{\n * type: 'insert-after',\n * section: '## Directory Structure',\n * content: 'āāā src/hooks/ # Custom React hooks'\n * }]\n * });\n * ```\n *\n * Note: Changes are immediately written to the file.\n * Use git to track changes and rollback if needed.\n */\nexport async function updateAIReadme(input: UpdateInput) {\n const { readmePath, operations } = input;\n\n const updater = new ReadmeUpdater();\n\n // Perform update\n const result = await updater.update(readmePath, operations as UpdateOperation[]);\n\n if (!result.success) {\n return {\n success: false,\n readmePath,\n error: result.error,\n summary: `Failed to update ${readmePath}: ${result.error}`,\n };\n }\n\n // Auto-validate after update\n try {\n const projectRoot = dirname(dirname(readmePath)); // Approximate project root\n const config = await ReadmeValidator.loadConfig(projectRoot);\n const validator = new ReadmeValidator(config || undefined);\n const validation = await validator.validate(readmePath);\n\n // Collect validation warnings\n const warnings = validation.issues\n .filter(i => i.type === 'warning' || i.type === 'error')\n .map(i => `[${i.type.toUpperCase()}] ${i.message}`);\n\n const workflowTip = result.changes.some(c =>\n c.operation === 'replace' || c.operation === 'append' || c.operation === 'prepend'\n )\n ? '\\n\\nš” NEXT STEP: Use get_context_for_file before writing code to ensure you\\'re following the updated conventions.'\n : '';\n\n return {\n success: true,\n readmePath,\n changes: result.changes,\n summary: `Successfully updated ${readmePath} with ${result.changes.length} operation(s). Use 'git diff' to review changes.${workflowTip}`,\n validation: {\n valid: validation.valid,\n score: validation.score,\n warnings: warnings.length > 0 ? warnings : undefined,\n stats: validation.stats,\n },\n };\n } catch (validationError) {\n // If validation fails, still return success for the update\n return {\n success: true,\n readmePath,\n changes: result.changes,\n summary: `Successfully updated ${readmePath} with ${result.changes.length} operation(s). Use 'git diff' to review changes.`,\n validation: {\n valid: false,\n error: validationError instanceof Error ? validationError.message : 'Validation failed',\n },\n };\n }\n}\n","import { readFile, writeFile } from 'fs/promises';\nimport { existsSync } from 'fs';\n\nexport interface UpdateOperation {\n type: 'replace' | 'append' | 'prepend' | 'insert-after' | 'insert-before';\n section?: string; // Section heading to target (e.g., \"## Coding Conventions\")\n searchText?: string; // Text to search for (for replace operations)\n content: string; // Content to add/replace\n}\n\nexport interface UpdateResult {\n success: boolean;\n changes: {\n operation: string;\n section?: string;\n linesAdded: number;\n linesRemoved: number;\n }[];\n error?: string;\n}\n\n/**\n * ReadmeUpdater - Handles updating AI_README.md files\n *\n * Features:\n * - Section-based updates\n * - Multiple operation types (append, prepend, replace, insert-after, insert-before)\n * - Detailed change tracking\n *\n * Note: Version control is handled by Git. Use git diff/revert for rollback.\n */\nexport class ReadmeUpdater {\n constructor() {\n // No configuration needed\n }\n\n /**\n * Update a README file with given operations\n *\n * @param readmePath - Path to the AI_README.md file\n * @param operations - List of update operations\n * @returns Update result with changes\n */\n async update(\n readmePath: string,\n operations: UpdateOperation[]\n ): Promise<UpdateResult> {\n try {\n // Check if file exists\n if (!existsSync(readmePath)) {\n return {\n success: false,\n error: `File not found: ${readmePath}`,\n changes: [],\n };\n }\n\n // Read current content\n const content = await readFile(readmePath, 'utf-8');\n\n // Apply operations\n let updatedContent = content;\n const changes: UpdateResult['changes'] = [];\n\n for (const operation of operations) {\n const result = await this.applyOperation(updatedContent, operation);\n updatedContent = result.content;\n changes.push(result.change);\n }\n\n // Write updated content\n await writeFile(readmePath, updatedContent, 'utf-8');\n\n return {\n success: true,\n changes,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n changes: [],\n };\n }\n }\n\n /**\n * Apply a single update operation to content\n *\n * @param content - Current content\n * @param operation - Operation to apply\n * @returns Updated content and change info\n */\n private async applyOperation(\n content: string,\n operation: UpdateOperation\n ): Promise<{ content: string; change: UpdateResult['changes'][0] }> {\n const lines = content.split('\\n');\n let updatedLines = [...lines];\n let linesAdded = 0;\n let linesRemoved = 0;\n\n switch (operation.type) {\n case 'append': {\n // Add content to the end\n updatedLines.push('', operation.content);\n linesAdded = operation.content.split('\\n').length + 1;\n break;\n }\n\n case 'prepend': {\n // Add content to the beginning\n updatedLines.unshift(operation.content, '');\n linesAdded = operation.content.split('\\n').length + 1;\n break;\n }\n\n case 'replace': {\n // Replace specific text\n if (!operation.searchText) {\n throw new Error('searchText is required for replace operation');\n }\n\n const originalContent = updatedLines.join('\\n');\n const newContent = originalContent.replace(\n operation.searchText,\n operation.content\n );\n\n if (originalContent === newContent) {\n throw new Error(`Text not found: ${operation.searchText}`);\n }\n\n updatedLines = newContent.split('\\n');\n linesRemoved = operation.searchText.split('\\n').length;\n linesAdded = operation.content.split('\\n').length;\n break;\n }\n\n case 'insert-after': {\n // Insert content after a section\n if (!operation.section) {\n throw new Error('section is required for insert-after operation');\n }\n\n const sectionIndex = this.findSectionIndex(updatedLines, operation.section);\n if (sectionIndex === -1) {\n throw new Error(`Section not found: ${operation.section}`);\n }\n\n // Find the end of the section content\n const insertIndex = this.findSectionEnd(updatedLines, sectionIndex);\n updatedLines.splice(insertIndex, 0, '', operation.content);\n linesAdded = operation.content.split('\\n').length + 1;\n break;\n }\n\n case 'insert-before': {\n // Insert content before a section\n if (!operation.section) {\n throw new Error('section is required for insert-before operation');\n }\n\n const sectionIndex = this.findSectionIndex(updatedLines, operation.section);\n if (sectionIndex === -1) {\n throw new Error(`Section not found: ${operation.section}`);\n }\n\n updatedLines.splice(sectionIndex, 0, operation.content, '');\n linesAdded = operation.content.split('\\n').length + 1;\n break;\n }\n\n default:\n throw new Error(`Unknown operation type: ${operation.type}`);\n }\n\n return {\n content: updatedLines.join('\\n'),\n change: {\n operation: operation.type,\n section: operation.section,\n linesAdded,\n linesRemoved,\n },\n };\n }\n\n /**\n * Find the index of a section heading\n *\n * @param lines - Content lines\n * @param section - Section heading (e.g., \"## Coding Conventions\")\n * @returns Index of the section, or -1 if not found\n */\n private findSectionIndex(lines: string[], section: string): number {\n return lines.findIndex((line) => line.trim() === section.trim());\n }\n\n /**\n * Find the end of a section (before the next section of same or higher level)\n *\n * @param lines - Content lines\n * @param startIndex - Index of the section heading\n * @returns Index where the section ends\n */\n private findSectionEnd(lines: string[], startIndex: number): number {\n const startLine = lines[startIndex];\n if (!startLine) {\n return lines.length;\n }\n\n const sectionLevel = this.getSectionLevel(startLine);\n\n for (let i = startIndex + 1; i < lines.length; i++) {\n const line = lines[i];\n if (!line) continue;\n\n const trimmedLine = line.trim();\n if (trimmedLine.startsWith('#')) {\n const level = this.getSectionLevel(trimmedLine);\n if (level <= sectionLevel) {\n return i;\n }\n }\n }\n\n return lines.length;\n }\n\n /**\n * Get the heading level of a markdown section\n *\n * @param line - Line containing a heading\n * @returns Heading level (1-6)\n */\n private getSectionLevel(line: string): number {\n const match = line.match(/^(#{1,6})\\s/);\n return match && match[1] ? match[1].length : 0;\n }\n}\n","import { readFile } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport { join } from 'path';\nimport type {\n ValidationConfig,\n ResolvedValidationConfig,\n ValidationResult,\n ValidationIssue,\n} from '../types/index.js';\nimport { DEFAULT_VALIDATION_CONFIG } from '../types/index.js';\n\n/**\n * ReadmeValidator - Validates AI_README.md files\n *\n * Features:\n * - Token count validation\n * - Structure validation\n * - Content quality checks\n * - Configurable rules via constructor parameter\n */\nexport class ReadmeValidator {\n private config: ResolvedValidationConfig;\n\n constructor(config?: Partial<ValidationConfig>) {\n // Merge user config with defaults\n this.config = this.mergeConfig(config || {});\n }\n\n /**\n * Merge user config with default config\n */\n private mergeConfig(userConfig: Partial<ValidationConfig>): ResolvedValidationConfig {\n return {\n maxTokens: userConfig.maxTokens ?? DEFAULT_VALIDATION_CONFIG.maxTokens,\n rules: {\n ...DEFAULT_VALIDATION_CONFIG.rules,\n ...(userConfig.rules || {}),\n },\n tokenLimits: {\n ...DEFAULT_VALIDATION_CONFIG.tokenLimits,\n ...(userConfig.tokenLimits || {}),\n },\n };\n }\n\n /**\n * Validate a single AI_README.md file\n *\n * @param readmePath - Path to the README file\n * @returns Validation result\n */\n async validate(readmePath: string): Promise<ValidationResult> {\n const issues: ValidationIssue[] = [];\n\n // Check file exists\n if (!existsSync(readmePath)) {\n return {\n valid: false,\n filePath: readmePath,\n issues: [\n {\n type: 'error',\n rule: 'structure',\n message: `File not found: ${readmePath}`,\n },\n ],\n };\n }\n\n // Read content\n const content = await readFile(readmePath, 'utf-8');\n\n // Check if file is empty\n if (content.trim().length === 0) {\n issues.push({\n type: 'error',\n rule: 'empty-content',\n message: 'README file is empty',\n suggestion: 'Add content to the README file',\n });\n }\n\n // Calculate statistics\n const lines = content.split('\\n');\n const tokens = this.estimateTokens(content);\n const characters = content.length;\n\n // Validate token count\n this.validateTokenCount(tokens, issues);\n\n // Validate structure\n this.validateStructure(content, lines, issues);\n\n // Validate line length\n this.validateLineLength(lines, issues);\n\n // Validate code blocks\n this.validateCodeBlocks(content, issues);\n\n // Calculate score\n const score = this.calculateScore(issues, tokens);\n\n return {\n valid: !issues.some((i) => i.type === 'error'),\n filePath: readmePath,\n issues,\n score,\n stats: {\n tokens,\n lines: lines.length,\n characters,\n },\n };\n }\n\n /**\n * Estimate token count (simple word-based estimation)\n * Formula: words * 1.3 (approximate token-to-word ratio)\n */\n private estimateTokens(content: string): number {\n const words = content.split(/\\s+/).filter((w) => w.length > 0).length;\n return Math.round(words * 1.3);\n }\n\n /**\n * Validate token count against limits\n */\n private validateTokenCount(tokens: number, issues: ValidationIssue[]): void {\n const { tokenLimits, maxTokens } = this.config;\n\n if (tokens > tokenLimits.error) {\n issues.push({\n type: 'error',\n rule: 'token-count',\n message: `README is too long (${tokens} tokens). Maximum recommended: ${maxTokens} tokens.`,\n suggestion: 'Remove unnecessary content, use bullet points instead of paragraphs, and avoid code examples.',\n });\n } else if (tokens > tokenLimits.warning) {\n issues.push({\n type: 'warning',\n rule: 'token-count',\n message: `README is quite long (${tokens} tokens). Consider keeping it under ${tokenLimits.good} tokens.`,\n suggestion: 'Simplify content and remove redundant information.',\n });\n } else if (tokens > tokenLimits.good) {\n issues.push({\n type: 'info',\n rule: 'token-count',\n message: `README length is acceptable (${tokens} tokens).`,\n });\n }\n }\n\n /**\n * Validate README structure\n */\n private validateStructure(_content: string, lines: string[], issues: ValidationIssue[]): void {\n // Check for H1 heading\n if (this.config.rules.requireH1) {\n const hasH1 = lines.some((line) => line.trim().match(/^#\\s+[^#]/));\n if (!hasH1) {\n issues.push({\n type: 'error',\n rule: 'require-h1',\n message: 'README must have a H1 heading (# Title)',\n suggestion: 'Add a title at the beginning of the file: # Project Name',\n });\n }\n }\n\n // Check for required sections\n if (this.config.rules.requireSections && this.config.rules.requireSections.length > 0) {\n for (const section of this.config.rules.requireSections) {\n const hasSection = lines.some((line) => line.trim() === section);\n if (!hasSection) {\n issues.push({\n type: 'warning',\n rule: 'require-sections',\n message: `Missing required section: ${section}`,\n suggestion: `Add section: ${section}`,\n });\n }\n }\n }\n }\n\n /**\n * Validate line length\n */\n private validateLineLength(lines: string[], issues: ValidationIssue[]): void {\n const { maxLineLength } = this.config.rules;\n const longLines = lines\n .map((line, index) => ({ line, index }))\n .filter(({ line }) => line.length > maxLineLength);\n\n if (longLines.length > 3) {\n // Only warn if there are many long lines\n issues.push({\n type: 'info',\n rule: 'line-length',\n message: `${longLines.length} lines exceed ${maxLineLength} characters`,\n suggestion: 'Consider breaking long lines for better readability',\n });\n }\n }\n\n /**\n * Validate code blocks\n */\n private validateCodeBlocks(content: string, issues: ValidationIssue[]): void {\n if (!this.config.rules.allowCodeBlocks) {\n const codeBlockCount = (content.match(/```/g) || []).length / 2;\n if (codeBlockCount > 0) {\n issues.push({\n type: 'warning',\n rule: 'code-blocks',\n message: `Found ${codeBlockCount} code blocks. Code examples consume many tokens.`,\n suggestion: 'Remove code examples or move them to separate documentation.',\n });\n }\n }\n }\n\n /**\n * Calculate quality score (0-100)\n */\n private calculateScore(issues: ValidationIssue[], tokens: number): number {\n let score = 100;\n\n // Deduct points for issues\n for (const issue of issues) {\n if (issue.type === 'error') score -= 20;\n else if (issue.type === 'warning') score -= 10;\n else if (issue.type === 'info') score -= 2;\n }\n\n // Deduct points for excessive length\n const { tokenLimits } = this.config;\n if (tokens > tokenLimits.error) score -= 30;\n else if (tokens > tokenLimits.warning) score -= 15;\n else if (tokens < tokenLimits.excellent) score += 10; // Bonus for concise READMEs\n\n return Math.max(0, Math.min(100, score));\n }\n\n /**\n * Load validation config from .aireadme.config.json\n *\n * @param projectRoot - Project root directory\n * @returns Validation config or null if not found\n */\n static async loadConfig(projectRoot: string): Promise<Partial<ValidationConfig> | null> {\n const configPath = join(projectRoot, '.aireadme.config.json');\n\n if (!existsSync(configPath)) {\n return null;\n }\n\n try {\n const content = await readFile(configPath, 'utf-8');\n const config = JSON.parse(content);\n return config.validation || config; // Support both formats\n } catch (error) {\n console.error(`Failed to load config from ${configPath}:`, error);\n return null;\n }\n }\n}\n","/**\n * Core type definitions for AI_README MCP Server\n */\n\n/**\n * Represents a single AI_README.md file entry in the index\n */\nexport interface ReadmeEntry {\n /** Absolute or relative path to the AI_README.md file */\n path: string;\n /** Scope identifier (e.g., 'root', 'frontend', 'backend') */\n scope: string;\n /** Directory level depth (0 for root) */\n level: number;\n /** Glob patterns this README covers */\n patterns: string[];\n /** Cached content of the README (optional) */\n content?: string;\n}\n\n/**\n * Index of all AI_README files in the project\n */\nexport interface ReadmeIndex {\n /** Root directory of the project */\n projectRoot: string;\n /** List of discovered README entries */\n readmes: ReadmeEntry[];\n /** Timestamp of last index update */\n lastUpdated: Date;\n}\n\n/**\n * Context information for a specific file\n */\nexport interface ReadmeContext {\n /** Path to the AI_README.md file */\n path: string;\n /** Content of the README */\n content: string;\n /** Relevance type */\n relevance: 'root' | 'direct' | 'parent';\n /** Distance in directory levels from target file */\n distance: number;\n}\n\n/**\n * Options for the scanner\n */\nexport interface ScannerOptions {\n /** Patterns to exclude from scanning */\n excludePatterns?: string[];\n /** Whether to cache README contents */\n cacheContent?: boolean;\n /** Custom README filename (default: 'AI_README.md') */\n readmeFilename?: string;\n}\n\n/**\n * Update action types for AI_README modifications\n */\nexport type UpdateAction = 'append' | 'replace' | 'delete';\n\n/**\n * Result of an update operation\n */\nexport interface UpdateResult {\n /** Whether the update succeeded */\n success: boolean;\n /** Path to the updated file */\n updatedPath: string;\n /** Path to backup file (if created) */\n backupPath?: string;\n /** Diff of changes made */\n diff: string;\n}\n\n/**\n * Options for the updater\n */\nexport interface UpdaterOptions {\n /** Directory for backups (default: '.ai_readme_history') */\n backupDir?: string;\n /** Whether to create backups */\n createBackup?: boolean;\n}\n\n/**\n * Validation configuration\n */\nexport interface ValidationConfig {\n /** Maximum tokens allowed (default: 500) */\n maxTokens?: number;\n /** Validation rules */\n rules?: {\n /** Require H1 heading (default: true) */\n requireH1?: boolean;\n /** Required sections (default: []) */\n requireSections?: string[];\n /** Allow code blocks (default: true) */\n allowCodeBlocks?: boolean;\n /** Maximum line length (default: 120) */\n maxLineLength?: number;\n };\n /** Token limit thresholds */\n tokenLimits?: {\n /** Excellent: under this many tokens (default: 300) */\n excellent?: number;\n /** Good: under this many tokens (default: 500) */\n good?: number;\n /** Warning: under this many tokens (default: 800) */\n warning?: number;\n /** Error: over this many tokens (default: 1200) */\n error?: number;\n };\n}\n\n/**\n * Fully resolved validation configuration (all properties required)\n */\nexport interface ResolvedValidationConfig {\n maxTokens: number;\n rules: {\n requireH1: boolean;\n requireSections: string[];\n allowCodeBlocks: boolean;\n maxLineLength: number;\n };\n tokenLimits: {\n excellent: number;\n good: number;\n warning: number;\n error: number;\n };\n}\n\n/**\n * Default validation configuration\n */\nexport const DEFAULT_VALIDATION_CONFIG: ResolvedValidationConfig = {\n maxTokens: 400,\n rules: {\n requireH1: true,\n requireSections: [],\n allowCodeBlocks: false,\n maxLineLength: 100,\n },\n tokenLimits: {\n excellent: 200,\n good: 400,\n warning: 600,\n error: 1000,\n },\n};\n\n/**\n * Validation issue severity\n */\nexport type ValidationSeverity = 'error' | 'warning' | 'info';\n\n/**\n * Validation rule type\n */\nexport type ValidationRule =\n | 'token-count'\n | 'require-h1'\n | 'require-sections'\n | 'code-blocks'\n | 'line-length'\n | 'empty-content'\n | 'structure';\n\n/**\n * Validation issue\n */\nexport interface ValidationIssue {\n /** Issue severity */\n type: ValidationSeverity;\n /** Rule that triggered this issue */\n rule: ValidationRule;\n /** Issue message */\n message: string;\n /** Line number (if applicable) */\n line?: number;\n /** Suggestion for fixing */\n suggestion?: string;\n}\n\n/**\n * Validation result for a single README\n */\nexport interface ValidationResult {\n /** Whether validation passed */\n valid: boolean;\n /** Path to the README file */\n filePath: string;\n /** List of issues found */\n issues: ValidationIssue[];\n /** Quality score (0-100) */\n score?: number;\n /** Token count statistics */\n stats?: {\n tokens: number;\n lines: number;\n characters: number;\n };\n}\n","import { z } from 'zod';\nimport { ReadmeValidator } from '../core/validator.js';\nimport { AIReadmeScanner } from '../core/scanner.js';\nimport type { ValidationConfig } from '../types/index.js';\n\n/**\n * Zod schema for validate_ai_readmes tool\n */\nexport const validateSchema = z.object({\n projectRoot: z.string().describe('The root directory of the project'),\n excludePatterns: z.array(z.string()).optional().describe(\n 'Glob patterns to exclude (e.g., [\"node_modules/**\", \".git/**\"])'\n ),\n config: z.object({\n maxTokens: z.number().optional(),\n rules: z.object({\n requireH1: z.boolean().optional(),\n requireSections: z.array(z.string()).optional(),\n allowCodeBlocks: z.boolean().optional(),\n maxLineLength: z.number().optional(),\n }).optional(),\n tokenLimits: z.object({\n excellent: z.number().optional(),\n good: z.number().optional(),\n warning: z.number().optional(),\n error: z.number().optional(),\n }).optional(),\n }).optional().describe('Custom validation configuration (optional, uses defaults if not provided)'),\n});\n\nexport type ValidateInput = z.infer<typeof validateSchema>;\n\n/**\n * Validate all AI_README.md files in a project\n *\n * @param input - Validation parameters\n * @returns Validation results for all README files\n *\n * @example\n * ```typescript\n * await validateAIReadmes({\n * projectRoot: '/path/to/project',\n * excludePatterns: ['node_modules/**'],\n * config: {\n * maxTokens: 500,\n * rules: {\n * requireH1: true,\n * requireSections: ['## Architecture', '## Conventions']\n * }\n * }\n * });\n * ```\n */\nexport async function validateAIReadmes(input: ValidateInput) {\n const { projectRoot, excludePatterns, config: userConfig } = input;\n\n try {\n // Use provided config, fallback to file config if available, then defaults\n let config: Partial<ValidationConfig> | undefined = userConfig;\n\n if (!config) {\n // Optionally load from .aireadme.config.json if it exists\n const fileConfig = await ReadmeValidator.loadConfig(projectRoot);\n if (fileConfig) {\n config = fileConfig;\n }\n }\n\n // Create validator with config\n const validator = new ReadmeValidator(config);\n\n // Scan for all README files\n const scanner = new AIReadmeScanner(projectRoot, {\n excludePatterns: excludePatterns || [],\n cacheContent: false,\n });\n const index = await scanner.scan();\n\n // Validate each README\n const results = [];\n for (const readme of index.readmes) {\n const result = await validator.validate(readme.path);\n results.push(result);\n }\n\n // Calculate overall statistics\n const totalFiles = results.length;\n const validFiles = results.filter(r => r.valid).length;\n const totalIssues = results.reduce((sum, r) => sum + r.issues.length, 0);\n const averageScore = totalFiles > 0\n ? Math.round(results.reduce((sum, r) => sum + (r.score || 0), 0) / totalFiles)\n : 0;\n\n // Group issues by severity\n const issuesBySeverity = {\n error: 0,\n warning: 0,\n info: 0,\n };\n for (const result of results) {\n for (const issue of result.issues) {\n issuesBySeverity[issue.type]++;\n }\n }\n\n return {\n success: true,\n projectRoot,\n summary: {\n totalFiles,\n validFiles,\n invalidFiles: totalFiles - validFiles,\n totalIssues,\n averageScore,\n issuesBySeverity,\n },\n results,\n message: totalIssues === 0\n ? `All ${totalFiles} README files passed validation! Average score: ${averageScore}/100`\n : `Found ${totalIssues} issues across ${totalFiles} README files. ${validFiles} files passed validation.`,\n };\n } catch (error) {\n return {\n success: false,\n projectRoot,\n error: error instanceof Error ? error.message : String(error),\n message: `Validation failed: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n}\n","import { z } from 'zod';\nimport { readFile, writeFile } from 'fs/promises';\nimport { join, dirname, relative } from 'path';\nimport { existsSync } from 'fs';\nimport { fileURLToPath } from 'url';\nimport { ProjectDetector } from '../core/detector.js';\nimport { AIReadmeScanner } from '../core/scanner.js';\n\n/**\n * Zod schema for init_ai_readme tool\n */\nexport const initSchema = z.object({\n targetPath: z.string().describe('Directory where AI_README.md will be created'),\n projectName: z.string().optional().describe('Project name to use in the template (optional)'),\n overwrite: z.boolean().optional().describe('Whether to overwrite existing AI_README.md (default: false)'),\n smart: z.boolean().optional().describe('Enable smart content generation based on project analysis (default: true)'),\n});\n\nexport type InitInput = z.infer<typeof initSchema>;\n\n/**\n * Initialize a new AI_README.md file from template\n *\n * @param input - Initialization parameters\n * @returns Result with created file path\n *\n * @example\n * ```typescript\n * await initAIReadme({\n * targetPath: '/path/to/project',\n * projectName: 'My Awesome Project',\n * overwrite: false\n * });\n * ```\n */\nexport async function initAIReadme(input: InitInput) {\n const { targetPath, projectName, overwrite = false } = input;\n const smart = input.smart !== false; // Default to true unless explicitly set to false\n\n console.error(`[DEBUG] init_ai_readme: smart=${smart}, input.smart=${input.smart}`);\n\n try {\n // Validate target path\n if (!existsSync(targetPath)) {\n return {\n success: false,\n error: `Target directory does not exist: ${targetPath}`,\n message: `Failed to create AI_README.md: Directory not found`,\n };\n }\n\n const readmePath = join(targetPath, 'AI_README.md');\n\n // Check if file exists and is empty\n const fileExists = existsSync(readmePath);\n let isEmpty = false;\n\n if (fileExists) {\n const existingContent = await readFile(readmePath, 'utf-8');\n isEmpty = existingContent.trim().length < 50; // Consider empty if less than 50 characters\n\n if (!isEmpty && !overwrite) {\n return {\n success: false,\n error: 'AI_README.md already exists with content',\n message: `AI_README.md already exists at ${readmePath}. Use overwrite: true to replace it.`,\n existingPath: readmePath,\n };\n }\n }\n\n let content: string;\n let detectedInfo: any = {};\n\n if (smart) {\n // Smart mode: detect project and generate customized content\n const detector = new ProjectDetector(targetPath);\n const projectInfo = await detector.detect();\n detectedInfo = projectInfo;\n\n // Check if there's a parent AI_README\n const parentReadme = await findParentReadme(targetPath);\n const isSubdirectory = parentReadme !== null;\n\n // Generate smart content\n content = await generateSmartContent(projectInfo, targetPath, isSubdirectory, parentReadme);\n } else {\n // Basic mode: use template\n const __filename = fileURLToPath(import.meta.url);\n const __dirname = dirname(__filename);\n const templatePath = join(__dirname, '..', '..', 'docs', 'templates', 'basic.md');\n\n if (!existsSync(templatePath)) {\n return {\n success: false,\n error: `Template file not found: ${templatePath}`,\n message: 'Template file is missing. Please check installation.',\n };\n }\n\n content = await readFile(templatePath, 'utf-8');\n const finalProjectName = projectName || 'Project Name';\n content = content.replace(/\\{\\{PROJECT_NAME\\}\\}/g, finalProjectName);\n }\n\n // Write to target\n await writeFile(readmePath, content, 'utf-8');\n\n const action = fileExists ? (isEmpty ? 'filled' : 'overwritten') : 'created';\n\n return {\n success: true,\n readmePath,\n action,\n projectInfo: smart ? detectedInfo : undefined,\n message: `Successfully ${action} AI_README.md at ${readmePath}. ${smart ? 'Content generated based on project analysis.' : 'Edit the file to customize it for your project.'}`,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n message: `Failed to create AI_README.md: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n}\n\n/**\n * Find parent AI_README.md by traversing up the directory tree\n */\nasync function findParentReadme(targetPath: string): Promise<string | null> {\n let currentPath = dirname(targetPath);\n const root = '/';\n\n while (currentPath !== root) {\n const readmePath = join(currentPath, 'AI_README.md');\n if (existsSync(readmePath)) {\n const content = await readFile(readmePath, 'utf-8');\n // Only consider it a parent if it has content\n if (content.trim().length > 50) {\n return currentPath;\n }\n }\n\n const parentPath = dirname(currentPath);\n if (parentPath === currentPath) break; // Reached filesystem root\n currentPath = parentPath;\n }\n\n return null;\n}\n\n/**\n * Generate smart content based on project analysis\n */\nasync function generateSmartContent(\n projectInfo: any,\n targetPath: string,\n isSubdirectory: boolean,\n parentPath: string | null\n): Promise<string> {\n let content = `# ${projectInfo.projectName}\\n\\n`;\n\n // Add subdirectory note if applicable\n if (isSubdirectory && parentPath) {\n const relativePath = relative(parentPath, targetPath);\n content += `> This README extends the root AI_README.md with ${relativePath}-specific conventions.\\n\\n`;\n }\n\n // Architecture section\n content += `## Architecture\\n\\n`;\n content += `- **Type:** ${projectInfo.projectType}\\n`;\n content += `- **Language:** ${projectInfo.language}\\n`;\n if (projectInfo.framework) {\n content += `- **Framework:** ${projectInfo.framework}\\n`;\n }\n content += `\\n`;\n\n // Directory Structure\n if (projectInfo.mainDirs && projectInfo.mainDirs.length > 0) {\n content += `## Directory Structure\\n\\n`;\n for (const dir of projectInfo.mainDirs) {\n content += `- ${dir}/ - [Add description]\\n`;\n }\n content += `\\n`;\n }\n\n // Coding Conventions\n content += `## Coding Conventions\\n\\n`;\n content += `### File Naming\\n`;\n content += `- [Add your file naming conventions here]\\n\\n`;\n\n content += `### Code Style\\n`;\n if (projectInfo.language === 'TypeScript') {\n content += `- Use TypeScript strict mode\\n`;\n content += `- Prefer interfaces over types for object shapes\\n`;\n } else if (projectInfo.language === 'Python') {\n content += `- Follow PEP 8 style guide\\n`;\n content += `- Use type hints\\n`;\n }\n content += `- [Add more style guidelines]\\n\\n`;\n\n // Framework-specific conventions\n if (projectInfo.framework) {\n content += `### ${projectInfo.framework} Conventions\\n`;\n if (projectInfo.framework === 'React') {\n content += `- Component naming: PascalCase\\n`;\n content += `- Hooks naming: use prefix 'use'\\n`;\n content += `- Prefer functional components\\n`;\n } else if (projectInfo.framework === 'Vue') {\n content += `- Component naming: PascalCase or kebab-case\\n`;\n content += `- Use Composition API\\n`;\n }\n content += `\\n`;\n }\n\n // Testing\n content += `## Testing\\n\\n`;\n if (projectInfo.hasTests) {\n content += `- Tests are located in test directories\\n`;\n }\n if (projectInfo.packageManager) {\n content += `- Run: \\`${projectInfo.packageManager} test\\`\\n`;\n } else {\n content += `- Run: [Add test command]\\n`;\n }\n content += `- Coverage target: [Add target or \"not enforced\"]\\n\\n`;\n\n // Dependencies\n if (projectInfo.dependencies && projectInfo.dependencies.length > 0) {\n content += `## Key Dependencies\\n\\n`;\n for (const dep of projectInfo.dependencies) {\n content += `- ${dep} - [Add purpose]\\n`;\n }\n content += `\\n`;\n }\n\n // Development\n content += `## Development\\n\\n`;\n if (projectInfo.packageManager) {\n content += `- Install: \\`${projectInfo.packageManager} install\\`\\n`;\n content += `- Dev: \\`${projectInfo.packageManager} run dev\\`\\n`;\n content += `- Build: \\`${projectInfo.packageManager} run build\\`\\n`;\n }\n content += `\\n`;\n\n // Important Notes\n content += `## Important Notes\\n\\n`;\n content += `- [Add critical information that AI should know]\\n`;\n content += `- [Security considerations]\\n`;\n content += `- [Performance considerations]\\n`;\n\n return content;\n}\n","/**\r\n * ProjectDetector - Analyzes project directory to detect type, framework, and structure\r\n */\r\n\r\nimport { readFile, readdir, stat } from 'fs/promises';\r\nimport { join } from 'path';\r\nimport { existsSync } from 'fs';\r\n\r\nexport interface ProjectInfo {\r\n projectName: string;\r\n projectType: 'library' | 'application' | 'monorepo' | 'unknown';\r\n language: string;\r\n framework?: string;\r\n packageManager?: 'npm' | 'yarn' | 'pnpm' | 'bun';\r\n hasTests: boolean;\r\n mainDirs: string[];\r\n dependencies?: string[];\r\n}\r\n\r\nexport class ProjectDetector {\r\n constructor(private targetPath: string) {}\r\n\r\n /**\r\n * Detect project information by analyzing files and structure\r\n */\r\n async detect(): Promise<ProjectInfo> {\r\n const info: ProjectInfo = {\r\n projectName: 'Project',\r\n projectType: 'unknown',\r\n language: 'JavaScript',\r\n hasTests: false,\r\n mainDirs: [],\r\n };\r\n\r\n // Check package.json\r\n const packageJsonPath = join(this.targetPath, 'package.json');\r\n if (existsSync(packageJsonPath)) {\r\n await this.analyzePackageJson(packageJsonPath, info);\r\n }\r\n\r\n // Check for Python\r\n if (existsSync(join(this.targetPath, 'requirements.txt')) ||\r\n existsSync(join(this.targetPath, 'setup.py')) ||\r\n existsSync(join(this.targetPath, 'pyproject.toml'))) {\r\n info.language = 'Python';\r\n }\r\n\r\n // Check for Go\r\n if (existsSync(join(this.targetPath, 'go.mod'))) {\r\n info.language = 'Go';\r\n }\r\n\r\n // Check for Rust\r\n if (existsSync(join(this.targetPath, 'Cargo.toml'))) {\r\n info.language = 'Rust';\r\n }\r\n\r\n // Check for Java\r\n if (existsSync(join(this.targetPath, 'pom.xml')) ||\r\n existsSync(join(this.targetPath, 'build.gradle'))) {\r\n info.language = 'Java';\r\n }\r\n\r\n // Analyze directory structure\r\n await this.analyzeStructure(info);\r\n\r\n return info;\r\n }\r\n\r\n /**\r\n * Analyze package.json to extract project information\r\n */\r\n private async analyzePackageJson(path: string, info: ProjectInfo): Promise<void> {\r\n try {\r\n const content = await readFile(path, 'utf-8');\r\n const pkg = JSON.parse(content);\r\n\r\n // Project name\r\n if (pkg.name) {\r\n info.projectName = pkg.name;\r\n }\r\n\r\n // Detect TypeScript\r\n if (pkg.devDependencies?.typescript || pkg.dependencies?.typescript) {\r\n info.language = 'TypeScript';\r\n }\r\n\r\n // Detect framework\r\n if (pkg.dependencies?.react || pkg.devDependencies?.react) {\r\n info.framework = 'React';\r\n } else if (pkg.dependencies?.vue || pkg.devDependencies?.vue) {\r\n info.framework = 'Vue';\r\n } else if (pkg.dependencies?.['@angular/core']) {\r\n info.framework = 'Angular';\r\n } else if (pkg.dependencies?.next) {\r\n info.framework = 'Next.js';\r\n } else if (pkg.dependencies?.express) {\r\n info.framework = 'Express';\r\n } else if (pkg.dependencies?.nestjs || pkg.dependencies?.['@nestjs/core']) {\r\n info.framework = 'NestJS';\r\n }\r\n\r\n // Detect package manager\r\n const lockFiles = await readdir(this.targetPath);\r\n if (lockFiles.includes('pnpm-lock.yaml')) {\r\n info.packageManager = 'pnpm';\r\n } else if (lockFiles.includes('yarn.lock')) {\r\n info.packageManager = 'yarn';\r\n } else if (lockFiles.includes('bun.lockb')) {\r\n info.packageManager = 'bun';\r\n } else if (lockFiles.includes('package-lock.json')) {\r\n info.packageManager = 'npm';\r\n }\r\n\r\n // Detect monorepo\r\n if (pkg.workspaces || existsSync(join(this.targetPath, 'pnpm-workspace.yaml'))) {\r\n info.projectType = 'monorepo';\r\n }\r\n\r\n // Detect project type\r\n if (!info.projectType || info.projectType === 'unknown') {\r\n if (pkg.main || pkg.exports) {\r\n info.projectType = 'library';\r\n } else {\r\n info.projectType = 'application';\r\n }\r\n }\r\n\r\n // Get main dependencies\r\n if (pkg.dependencies) {\r\n info.dependencies = Object.keys(pkg.dependencies).slice(0, 5);\r\n }\r\n } catch (error) {\r\n // Ignore parsing errors\r\n }\r\n }\r\n\r\n /**\r\n * Analyze directory structure\r\n */\r\n private async analyzeStructure(info: ProjectInfo): Promise<void> {\r\n try {\r\n const entries = await readdir(this.targetPath);\r\n const dirs: string[] = [];\r\n\r\n for (const entry of entries) {\r\n try {\r\n const entryPath = join(this.targetPath, entry);\r\n const stats = await stat(entryPath);\r\n\r\n if (stats.isDirectory()) {\r\n // Check for common directories\r\n if (['src', 'lib', 'app', 'pages', 'components', 'api', 'server', 'client'].includes(entry)) {\r\n dirs.push(entry);\r\n }\r\n\r\n // Check for tests\r\n if (['test', 'tests', '__tests__', 'spec'].includes(entry)) {\r\n info.hasTests = true;\r\n }\r\n\r\n // Check for monorepo patterns\r\n if (['apps', 'packages', 'modules'].includes(entry)) {\r\n info.projectType = 'monorepo';\r\n dirs.push(entry);\r\n }\r\n }\r\n } catch {\r\n // Skip entries we can't access\r\n }\r\n }\r\n\r\n info.mainDirs = dirs;\r\n } catch (error) {\r\n // Ignore directory read errors\r\n }\r\n }\r\n}\r\n"],"mappings":"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","names":["z","dirname","join","readFile","z","z","dirname","readFile","readFile","existsSync","join","existsSync","readFile","join","z","dirname","z","z","z","readFile","writeFile","join","dirname","existsSync","readFile","join","existsSync","z","existsSync","join","readFile","__filename","__dirname","dirname","writeFile"]}
|
|
1
|
+
{"version":3,"sources":["../src/index.ts","../src/tools/discover.ts","../src/core/scanner.ts","../src/tools/getContext.ts","../src/core/router.ts","../src/tools/update.ts","../src/core/updater.ts","../src/core/validator.ts","../src/types/index.ts","../src/tools/validate.ts","../src/tools/init.ts"],"sourcesContent":["/**\n * AI_README MCP Server\n * Entry point for the MCP server\n */\n\nimport { Server } from '@modelcontextprotocol/sdk/server/index.js';\nimport { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';\nimport {\n CallToolRequestSchema,\n ListToolsRequestSchema,\n} from '@modelcontextprotocol/sdk/types.js';\nimport { zodToJsonSchema } from 'zod-to-json-schema';\nimport {\n discoverAIReadmes,\n discoverSchema,\n type DiscoverInput,\n} from './tools/discover.js';\nimport {\n getContextForFile,\n getContextSchema,\n type GetContextInput,\n} from './tools/getContext.js';\nimport {\n updateAIReadme,\n updateSchema,\n type UpdateInput,\n} from './tools/update.js';\nimport {\n validateAIReadmes,\n validateSchema,\n type ValidateInput,\n} from './tools/validate.js';\nimport {\n initAIReadme,\n initSchema,\n type InitInput,\n} from './tools/init.js';\n\nconst server = new Server(\n {\n name: 'ai-readme-mcp',\n version: '0.3.2',\n },\n {\n capabilities: {\n tools: {},\n },\n }\n);\n\n// Register tool: list_tools\nserver.setRequestHandler(ListToolsRequestSchema, async () => {\n return {\n tools: [\n {\n name: 'discover_ai_readmes',\n description:\n 'Scan the project and discover all AI_README.md files. Returns an index of all README files with their paths, scopes, and coverage patterns.',\n inputSchema: zodToJsonSchema(discoverSchema),\n },\n {\n name: 'get_context_for_file',\n description:\n 'ā ļø REQUIRED: Call this tool BEFORE creating or editing ANY file to get project conventions.\\n\\n' +\n 'Gets relevant AI_README context for a specific file path. Returns formatted guidelines that MUST be followed when writing code.\\n\\n' +\n '**When to call:**\\n' +\n '- BEFORE using Write tool (creating new files)\\n' +\n '- BEFORE using Edit tool (modifying existing files)\\n' +\n '- AFTER updating AI_README (to get fresh context)\\n' +\n '- Works even if the target file does not exist yet\\n\\n' +\n '**Workflows:**\\n' +\n '1. Following conventions: get_context_for_file ā Write/Edit\\n' +\n '2. Establishing NEW conventions: update_ai_readme ā get_context_for_file ā Write/Edit\\n' +\n '3. Empty AI_README detected: Use `init_ai_readme` tool (tool will suggest this)\\n' +\n '4. Document discovered patterns: Write/Edit ā update_ai_readme\\n\\n' +\n '**Example:** Before creating Button.tsx, call get_context_for_file with filePath=\"src/components/Button.tsx\" to learn styling preferences (CSS Modules vs Tailwind), naming conventions, component patterns, etc.',\n inputSchema: zodToJsonSchema(getContextSchema),\n },\n {\n name: 'update_ai_readme',\n description:\n 'Update an AI_README.md file to document conventions, patterns, or architectural decisions. Supports append, prepend, replace, insert-after, insert-before operations.\\n\\n' +\n '**CRITICAL: Token Efficiency Rules**\\n' +\n '- Keep content EXTREMELY concise (< 400 tokens ideal, < 600 warning, > 1000 error)\\n' +\n '- Only document ACTIONABLE conventions that affect code generation\\n' +\n '- NO explanations, NO examples, NO verbose descriptions\\n' +\n '- Use bullet points, avoid complete sentences when possible\\n' +\n '- Focus on: tech stack, naming rules, patterns, architectural decisions\\n' +\n '- AVOID: project background, how-to guides, documentation, obvious practices\\n\\n' +\n '**When to use:**\\n' +\n '1. BEFORE code changes: Establishing NEW conventions\\n' +\n ' - User requests style/approach change (e.g., \"use CSS Modules\")\\n' +\n ' - Choosing between approaches (e.g., state management)\\n' +\n ' - Setting up new architectural patterns\\n' +\n ' - Workflow: update_ai_readme ā get_context_for_file ā Write/Edit\\n\\n' +\n '2. AFTER code changes: Documenting discovered patterns\\n' +\n ' - Found consistent patterns in existing code\\n' +\n ' - Workflow: Write/Edit ā update_ai_readme\\n\\n' +\n '**Quality checklist before updating:**\\n' +\n 'Is this a CONVENTION or PATTERN (not documentation)?\\n' +\n 'Will this help AI generate better code?\\n' +\n 'Is it concise (<3 words per bullet)?\\n' +\n 'Does it avoid obvious/general practices?\\n' +\n 'Reject: project descriptions, tutorials, general best practices\\n\\n' +\n '**Example (GOOD):**\\n' +\n '- Use CSS Modules\\n' +\n '- Components in PascalCase\\n' +\n '- Test coverage: 80%+\\n\\n' +\n '**Example (BAD - too verbose):**\\n' +\n '- We use CSS Modules for styling because it provides better type safety and scoping compared to Tailwind...',\n inputSchema: zodToJsonSchema(updateSchema),\n },\n {\n name: 'validate_ai_readmes',\n description:\n 'Validate all AI_README.md files in a project. Checks token count, structure, and content quality. Returns validation results with suggestions for improvement.',\n inputSchema: zodToJsonSchema(validateSchema),\n },\n {\n name: 'init_ai_readme',\n description:\n 'š Initialize and populate empty AI_README files in your project.\\n\\n' +\n 'Scans the project for empty or missing AI_README files and guides you through populating them with project conventions.\\n\\n' +\n '**When to use:**\\n' +\n '- First time setting up AI_README in a project\\n' +\n '- When get_context_for_file detects empty AI_README files\\n' +\n '- After creating new empty AI_README.md files manually\\n' +\n '- To populate multiple AI_README files at once\\n\\n' +\n '**What it does:**\\n' +\n '1. Scans project for empty AI_README files\\n' +\n '2. Creates root-level AI_README if none exist\\n' +\n '3. Provides step-by-step instructions to populate each file\\n' +\n '4. Guides analysis of tech stack, patterns, and conventions\\n\\n' +\n '**Workflow:**\\n' +\n '1. Call init_ai_readme\\n' +\n '2. Follow the instructions to explore directories\\n' +\n '3. Use update_ai_readme to populate each file\\n' +\n '4. Call get_context_for_file to verify and use conventions\\n\\n' +\n '**Example:** `init_ai_readme({ projectRoot: \"/path/to/project\" })`',\n inputSchema: zodToJsonSchema(initSchema),\n },\n ],\n };\n});\n\n// Register tool: call_tool\nserver.setRequestHandler(CallToolRequestSchema, async (request) => {\n const { name, arguments: args } = request.params;\n\n try {\n if (name === 'discover_ai_readmes') {\n const input = discoverSchema.parse(args) as DiscoverInput;\n const result = await discoverAIReadmes(input);\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(result, null, 2),\n },\n ],\n };\n }\n\n if (name === 'get_context_for_file') {\n const input = getContextSchema.parse(args) as GetContextInput;\n const result = await getContextForFile(input);\n return {\n content: [\n {\n type: 'text',\n text: result.formattedPrompt,\n },\n ],\n };\n }\n\n if (name === 'update_ai_readme') {\n const input = updateSchema.parse(args) as UpdateInput;\n const result = await updateAIReadme(input);\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(result, null, 2),\n },\n ],\n };\n }\n\n if (name === 'validate_ai_readmes') {\n const input = validateSchema.parse(args) as ValidateInput;\n const result = await validateAIReadmes(input);\n return {\n content: [\n {\n type: 'text',\n text: JSON.stringify(result, null, 2),\n },\n ],\n };\n }\n\n if (name === 'init_ai_readme') {\n const input = initSchema.parse(args) as InitInput;\n const result = await initAIReadme(input);\n return {\n content: [\n {\n type: 'text',\n text: result.instructions || JSON.stringify(result, null, 2),\n },\n ],\n };\n }\n\n throw new Error(`Unknown tool: ${name}`);\n } catch (error) {\n const errorMessage = error instanceof Error ? error.message : String(error);\n return {\n content: [\n {\n type: 'text',\n text: `Error: ${errorMessage}`,\n },\n ],\n isError: true,\n };\n }\n});\n\nasync function main() {\n const transport = new StdioServerTransport();\n await server.connect(transport);\n\n console.error('AI_README MCP Server started');\n console.error('Available tools: discover_ai_readmes, get_context_for_file, update_ai_readme, validate_ai_readmes, init_ai_readme');\n}\n\nmain().catch((error) => {\n console.error('Fatal error:', error);\n process.exit(1);\n});\n","/**\n * MCP Tool: discover_ai_readmes\n * Scans the project and discovers all AI_README.md files\n */\n\nimport { z } from 'zod';\nimport { AIReadmeScanner } from '../core/scanner.js';\n\nexport const discoverSchema = z.object({\n projectRoot: z.string().describe('The root directory of the project'),\n excludePatterns: z\n .array(z.string())\n .optional()\n .describe(\"Glob patterns to exclude (e.g., ['node_modules/**', '.git/**'])\"),\n});\n\nexport type DiscoverInput = z.infer<typeof discoverSchema>;\n\nexport async function discoverAIReadmes(input: DiscoverInput) {\n const { projectRoot, excludePatterns } = input;\n\n // Create scanner with options\n const scanner = new AIReadmeScanner(projectRoot, {\n excludePatterns,\n cacheContent: false, // Don't cache content in discovery phase\n });\n\n // Scan the project\n const index = await scanner.scan();\n\n // Format the response\n return {\n projectRoot: index.projectRoot,\n totalFound: index.readmes.length,\n readmeFiles: index.readmes.map((readme) => ({\n path: readme.path,\n scope: readme.scope,\n level: readme.level,\n patterns: readme.patterns,\n })),\n lastUpdated: index.lastUpdated.toISOString(),\n };\n}\n","/**\n * AIReadmeScanner - Scans project directories for AI_README.md files\n */\n\nimport { glob } from 'glob';\nimport { readFile } from 'fs/promises';\nimport { dirname, join } from 'path';\nimport type { ReadmeEntry, ReadmeIndex, ScannerOptions } from '../types/index.js';\n\nexport class AIReadmeScanner {\n private projectRoot: string;\n private options: Required<ScannerOptions>;\n\n constructor(projectRoot: string, options?: ScannerOptions) {\n this.projectRoot = projectRoot;\n this.options = {\n excludePatterns: options?.excludePatterns || [\n '**/node_modules/**',\n '**/.git/**',\n '**/dist/**',\n '**/build/**',\n '**/.next/**',\n '**/coverage/**',\n ],\n cacheContent: options?.cacheContent ?? true,\n readmeFilename: options?.readmeFilename || 'AI_README.md',\n };\n }\n\n /**\n * Scan the project directory for AI_README.md files\n */\n async scan(): Promise<ReadmeIndex> {\n const pattern = `**/${this.options.readmeFilename}`;\n const ignore = this.options.excludePatterns;\n\n // Find all AI_README.md files\n const files = await glob(pattern, {\n cwd: this.projectRoot,\n ignore,\n absolute: false,\n nodir: true,\n });\n\n // Build ReadmeEntry objects\n const readmes: ReadmeEntry[] = [];\n\n for (const file of files) {\n const entry = await this.createReadmeEntry(file);\n readmes.push(entry);\n }\n\n // Sort by level (root first, then deeper levels)\n readmes.sort((a, b) => a.level - b.level);\n\n return {\n projectRoot: this.projectRoot,\n readmes,\n lastUpdated: new Date(),\n };\n }\n\n /**\n * Create a ReadmeEntry from a file path\n */\n private async createReadmeEntry(filePath: string): Promise<ReadmeEntry> {\n // Normalize path to use forward slashes (Unix-style) for consistency\n const normalizedPath = filePath.replace(/\\\\/g, '/');\n const dir = dirname(normalizedPath);\n const level = dir === '.' ? 0 : dir.split('/').length;\n const scope = dir === '.' ? 'root' : dir.replace(/\\//g, '-');\n\n // Generate glob patterns this README covers\n const patterns = this.generatePatterns(dir);\n\n // Optionally cache content\n let content: string | undefined;\n if (this.options.cacheContent) {\n try {\n const fullPath = join(this.projectRoot, filePath);\n content = await readFile(fullPath, 'utf-8');\n } catch (error) {\n console.error(`Failed to read ${filePath}:`, error);\n }\n }\n\n return {\n path: normalizedPath,\n scope,\n level,\n patterns,\n content,\n };\n }\n\n /**\n * Generate glob patterns that this README covers\n */\n private generatePatterns(dir: string): string[] {\n if (dir === '.') {\n return ['**/*']; // Root level covers everything\n }\n\n return [\n `${dir}/**/*`, // All files in this directory and subdirectories\n `${dir}/*`, // Direct children\n ];\n }\n\n /**\n * Refresh the scan (re-scan the project)\n */\n async refresh(): Promise<ReadmeIndex> {\n return this.scan();\n }\n\n /**\n * Get the project root directory\n */\n getProjectRoot(): string {\n return this.projectRoot;\n }\n\n /**\n * Get current scanner options\n */\n getOptions(): Required<ScannerOptions> {\n return { ...this.options };\n }\n}\n","/**\r\n * MCP Tool: get_context_for_file\r\n * Gets relevant AI_README context for a specific file path\r\n */\r\n\r\nimport { z } from 'zod';\r\nimport { AIReadmeScanner } from '../core/scanner.js';\r\nimport { ContextRouter } from '../core/router.js';\r\n\r\nexport const getContextSchema = z.object({\r\n projectRoot: z.string().describe('The root directory of the project'),\r\n filePath: z\r\n .string()\r\n .describe('The file path to get context for (relative to project root)'),\r\n includeRoot: z\r\n .boolean()\r\n .optional()\r\n .default(true)\r\n .describe('Whether to include root-level AI_README (default: true)'),\r\n excludePatterns: z\r\n .array(z.string())\r\n .optional()\r\n .describe('Glob patterns to exclude when scanning'),\r\n});\r\n\r\nexport type GetContextInput = z.infer<typeof getContextSchema>;\r\n\r\nexport async function getContextForFile(input: GetContextInput) {\r\n const { projectRoot, filePath, includeRoot, excludePatterns } = input;\r\n\r\n // First, scan the project to build the index\r\n const scanner = new AIReadmeScanner(projectRoot, {\r\n excludePatterns,\r\n cacheContent: true, // Cache content for context retrieval\r\n });\r\n\r\n const index = await scanner.scan();\r\n\r\n // Create router and get context\r\n const router = new ContextRouter(index);\r\n const contexts = await router.getContextForFile(filePath, includeRoot);\r\n\r\n // Check for empty AI_README files\r\n const hasEmptyReadmes = contexts.some(ctx => !ctx.content || ctx.content.trim().length === 0);\r\n const hasNoReadmes = contexts.length === 0;\r\n\r\n // Format the response with a helpful prompt template\r\n const formattedContexts = contexts.map((ctx) => ({\r\n path: ctx.path,\r\n relevance: ctx.relevance,\r\n distance: ctx.distance,\r\n content: ctx.content,\r\n }));\r\n\r\n // Generate a formatted prompt for the AI\r\n let promptText = `## š Project Context for: ${filePath}\\n\\n`;\r\n\r\n // Simple check: if empty or no READMEs, suggest using init tool\r\n if (hasNoReadmes || hasEmptyReadmes) {\r\n promptText += `ā ļø **Empty or missing AI_README files detected.**\\n\\n`;\r\n promptText += `**Recommended Action:** Use the \\`init_ai_readme\\` tool to automatically populate AI_README files.\\n\\n`;\r\n promptText += `\\`\\`\\`\\n`;\r\n promptText += `init_ai_readme({ projectRoot: \"${projectRoot.replace(/\\\\/g, '/')}\" })\\n`;\r\n promptText += `\\`\\`\\`\\n\\n`;\r\n promptText += `This tool will:\\n`;\r\n promptText += `- Scan the project for empty AI_README files\\n`;\r\n promptText += `- Guide you through populating them with conventions\\n`;\r\n promptText += `- Ensure consistent documentation across your project\\n\\n`;\r\n promptText += `š” Alternatively, you can manually create and populate AI_README.md files, then call this tool again.\\n\\n`;\r\n promptText += `---\\n\\n`;\r\n }\r\n\r\n // Filter out empty contexts (content with only whitespace)\r\n const nonEmptyContexts = contexts.filter(ctx => ctx.content && ctx.content.trim().length > 0);\r\n\r\n if (nonEmptyContexts.length > 0) {\r\n // Show non-empty contexts\r\n for (const ctx of nonEmptyContexts) {\r\n if (ctx.relevance === 'root') {\r\n promptText += `### Root Conventions (${ctx.path})\\n\\n`;\r\n } else if (ctx.relevance === 'direct') {\r\n promptText += `### Direct Module Conventions (${ctx.path})\\n\\n`;\r\n } else {\r\n promptText += `### Parent Module Conventions (${ctx.path})\\n\\n`;\r\n }\r\n\r\n promptText += ctx.content + '\\n\\n';\r\n }\r\n\r\n promptText += `---\\n`;\r\n promptText += `**Important:**\\n`;\r\n promptText += `- Follow the above conventions when making changes\\n`;\r\n promptText += `- When establishing NEW conventions: update AI_README first ā get context ā write code\\n`;\r\n promptText += `- When discovering patterns in existing code: document them in AI_README afterward\\n`;\r\n }\r\n\r\n return {\r\n filePath,\r\n totalContexts: contexts.length,\r\n contexts: formattedContexts,\r\n formattedPrompt: promptText,\r\n };\r\n}\r\n","/**\n * ContextRouter - Routes file paths to relevant AI_README contexts\n */\n\nimport { minimatch } from 'minimatch';\nimport { dirname, join } from 'path';\nimport { readFile } from 'fs/promises';\nimport type { ReadmeContext, ReadmeIndex, ReadmeEntry } from '../types/index.js';\n\nexport class ContextRouter {\n private index: ReadmeIndex;\n\n constructor(index: ReadmeIndex) {\n this.index = index;\n }\n\n /**\n * Get relevant AI_README contexts for a specific file path\n * @param filePath - The file path relative to project root\n * @param includeRoot - Whether to include root-level README (default: true)\n */\n async getContextForFile(\n filePath: string,\n includeRoot: boolean = true\n ): Promise<ReadmeContext[]> {\n const contexts: ReadmeContext[] = [];\n\n // Find matching READMEs\n for (const readme of this.index.readmes) {\n const match = this.matchesPath(filePath, readme);\n\n if (!match) continue;\n\n // Skip root if not requested\n if (!includeRoot && readme.level === 0) continue;\n\n // Calculate distance from file to README\n const distance = this.calculateDistance(filePath, readme);\n\n // Get content\n const content = await this.getReadmeContent(readme);\n\n // Determine relevance\n const relevance = this.determineRelevance(filePath, readme);\n\n contexts.push({\n path: readme.path,\n content,\n relevance,\n distance,\n });\n }\n\n // Sort by distance (closest first) and then by relevance\n contexts.sort((a, b) => {\n if (a.distance !== b.distance) {\n return a.distance - b.distance;\n }\n // If same distance, prioritize direct > parent > root\n const relevanceOrder = { direct: 0, parent: 1, root: 2 };\n return relevanceOrder[a.relevance] - relevanceOrder[b.relevance];\n });\n\n return contexts;\n }\n\n /**\n * Get contexts for multiple files\n */\n async getContextForFiles(filePaths: string[]): Promise<Map<string, ReadmeContext[]>> {\n const results = new Map<string, ReadmeContext[]>();\n\n for (const filePath of filePaths) {\n const contexts = await this.getContextForFile(filePath);\n results.set(filePath, contexts);\n }\n\n return results;\n }\n\n /**\n * Check if a file path matches a README's patterns\n */\n private matchesPath(filePath: string, readme: ReadmeEntry): boolean {\n return readme.patterns.some(pattern => {\n return minimatch(filePath, pattern, { dot: true });\n });\n }\n\n /**\n * Calculate the directory distance between a file and a README\n */\n private calculateDistance(filePath: string, readme: ReadmeEntry): number {\n const fileDir = dirname(filePath);\n const readmeDir = dirname(readme.path);\n\n // If README is at root\n if (readmeDir === '.') {\n return fileDir === '.' ? 0 : fileDir.split('/').length;\n }\n\n // If file is in the same directory as README\n if (fileDir === readmeDir) {\n return 0;\n }\n\n // If file is in a subdirectory of README's directory\n if (fileDir.startsWith(readmeDir + '/')) {\n const subPath = fileDir.slice(readmeDir.length + 1);\n return subPath.split('/').length;\n }\n\n // Calculate levels up\n const fileParts = fileDir.split('/');\n const readmeParts = readmeDir.split('/');\n\n // Find common ancestor\n let commonDepth = 0;\n for (let i = 0; i < Math.min(fileParts.length, readmeParts.length); i++) {\n if (fileParts[i] === readmeParts[i]) {\n commonDepth++;\n } else {\n break;\n }\n }\n\n // Distance is the sum of levels to go up and down\n return (fileParts.length - commonDepth) + (readmeParts.length - commonDepth);\n }\n\n /**\n * Determine the relevance type of a README for a file\n */\n private determineRelevance(\n filePath: string,\n readme: ReadmeEntry\n ): 'root' | 'direct' | 'parent' {\n const fileDir = dirname(filePath);\n const readmeDir = dirname(readme.path);\n\n // Root level README\n if (readmeDir === '.') {\n return 'root';\n }\n\n // Direct match (same directory)\n if (fileDir === readmeDir) {\n return 'direct';\n }\n\n // Parent directory\n if (fileDir.startsWith(readmeDir + '/')) {\n return 'parent';\n }\n\n return 'parent';\n }\n\n /**\n * Get the content of a README (from cache or file system)\n */\n private async getReadmeContent(readme: ReadmeEntry): Promise<string> {\n // Return cached content if available\n if (readme.content) {\n return readme.content;\n }\n\n // Otherwise read from file system\n try {\n const fullPath = join(this.index.projectRoot, readme.path);\n return await readFile(fullPath, 'utf-8');\n } catch (error) {\n console.error(`Failed to read ${readme.path}:`, error);\n return `[Error: Could not read ${readme.path}]`;\n }\n }\n\n /**\n * Update the index (useful after re-scanning)\n */\n updateIndex(index: ReadmeIndex): void {\n this.index = index;\n }\n\n /**\n * Get the current index\n */\n getIndex(): ReadmeIndex {\n return this.index;\n }\n}\n","import { z } from 'zod';\nimport { dirname } from 'path';\nimport { ReadmeUpdater, UpdateOperation } from '../core/updater.js';\nimport { ReadmeValidator } from '../core/validator.js';\n\n/**\n * Zod schema for update operation\n */\nconst updateOperationSchema = z.object({\n type: z.enum(['replace', 'append', 'prepend', 'insert-after', 'insert-before']).describe(\n 'Type of update operation'\n ),\n section: z.string().optional().describe(\n 'Section heading to target (e.g., \"## Coding Conventions\")'\n ),\n searchText: z.string().optional().describe(\n 'Text to search for (required for replace operation)'\n ),\n content: z.string().describe('Content to add or replace'),\n});\n\n/**\n * Zod schema for update_ai_readme tool\n */\nexport const updateSchema = z.object({\n readmePath: z.string().describe('Path to the AI_README.md file to update'),\n operations: z.array(updateOperationSchema).describe(\n 'List of update operations to perform'\n ),\n});\n\nexport type UpdateInput = z.infer<typeof updateSchema>;\n\n/**\n * Update an AI_README.md file with specified operations\n *\n * @param input - Update parameters\n * @returns Update result with changes\n *\n * @example\n * ```typescript\n * await updateAIReadme({\n * readmePath: 'apps/frontend/AI_README.md',\n * operations: [{\n * type: 'insert-after',\n * section: '## Directory Structure',\n * content: 'āāā src/hooks/ # Custom React hooks'\n * }]\n * });\n * ```\n *\n * Note: Changes are immediately written to the file.\n * Use git to track changes and rollback if needed.\n */\nexport async function updateAIReadme(input: UpdateInput) {\n const { readmePath, operations } = input;\n\n const updater = new ReadmeUpdater();\n\n // Perform update\n const result = await updater.update(readmePath, operations as UpdateOperation[]);\n\n if (!result.success) {\n return {\n success: false,\n readmePath,\n error: result.error,\n summary: `Failed to update ${readmePath}: ${result.error}`,\n };\n }\n\n // Auto-validate after update\n try {\n const projectRoot = dirname(dirname(readmePath)); // Approximate project root\n const config = await ReadmeValidator.loadConfig(projectRoot);\n const validator = new ReadmeValidator(config || undefined);\n const validation = await validator.validate(readmePath);\n\n // Collect validation warnings\n const warnings = validation.issues\n .filter(i => i.type === 'warning' || i.type === 'error')\n .map(i => `[${i.type.toUpperCase()}] ${i.message}`);\n\n const workflowTip = result.changes.some(c =>\n c.operation === 'replace' || c.operation === 'append' || c.operation === 'prepend'\n )\n ? '\\n\\nš” NEXT STEP: Use get_context_for_file before writing code to ensure you\\'re following the updated conventions.'\n : '';\n\n return {\n success: true,\n readmePath,\n changes: result.changes,\n summary: `Successfully updated ${readmePath} with ${result.changes.length} operation(s). Use 'git diff' to review changes.${workflowTip}`,\n validation: {\n valid: validation.valid,\n score: validation.score,\n warnings: warnings.length > 0 ? warnings : undefined,\n stats: validation.stats,\n },\n };\n } catch (validationError) {\n // If validation fails, still return success for the update\n return {\n success: true,\n readmePath,\n changes: result.changes,\n summary: `Successfully updated ${readmePath} with ${result.changes.length} operation(s). Use 'git diff' to review changes.`,\n validation: {\n valid: false,\n error: validationError instanceof Error ? validationError.message : 'Validation failed',\n },\n };\n }\n}\n","import { readFile, writeFile } from 'fs/promises';\nimport { existsSync } from 'fs';\n\nexport interface UpdateOperation {\n type: 'replace' | 'append' | 'prepend' | 'insert-after' | 'insert-before';\n section?: string; // Section heading to target (e.g., \"## Coding Conventions\")\n searchText?: string; // Text to search for (for replace operations)\n content: string; // Content to add/replace\n}\n\nexport interface UpdateResult {\n success: boolean;\n changes: {\n operation: string;\n section?: string;\n linesAdded: number;\n linesRemoved: number;\n }[];\n error?: string;\n}\n\n/**\n * ReadmeUpdater - Handles updating AI_README.md files\n *\n * Features:\n * - Section-based updates\n * - Multiple operation types (append, prepend, replace, insert-after, insert-before)\n * - Detailed change tracking\n *\n * Note: Version control is handled by Git. Use git diff/revert for rollback.\n */\nexport class ReadmeUpdater {\n constructor() {\n // No configuration needed\n }\n\n /**\n * Update a README file with given operations\n *\n * @param readmePath - Path to the AI_README.md file\n * @param operations - List of update operations\n * @returns Update result with changes\n */\n async update(\n readmePath: string,\n operations: UpdateOperation[]\n ): Promise<UpdateResult> {\n try {\n // Read current content (empty string if file doesn't exist or is empty)\n let content = '';\n if (existsSync(readmePath)) {\n content = await readFile(readmePath, 'utf-8');\n }\n\n // Apply operations\n let updatedContent = content;\n const changes: UpdateResult['changes'] = [];\n\n for (const operation of operations) {\n const result = await this.applyOperation(updatedContent, operation);\n updatedContent = result.content;\n changes.push(result.change);\n }\n\n // Write updated content\n await writeFile(readmePath, updatedContent, 'utf-8');\n\n return {\n success: true,\n changes,\n };\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n changes: [],\n };\n }\n }\n\n /**\n * Apply a single update operation to content\n *\n * @param content - Current content\n * @param operation - Operation to apply\n * @returns Updated content and change info\n */\n private async applyOperation(\n content: string,\n operation: UpdateOperation\n ): Promise<{ content: string; change: UpdateResult['changes'][0] }> {\n const lines = content.split('\\n');\n let updatedLines = [...lines];\n let linesAdded = 0;\n let linesRemoved = 0;\n\n switch (operation.type) {\n case 'append': {\n // Add content to the end\n updatedLines.push('', operation.content);\n linesAdded = operation.content.split('\\n').length + 1;\n break;\n }\n\n case 'prepend': {\n // Add content to the beginning\n updatedLines.unshift(operation.content, '');\n linesAdded = operation.content.split('\\n').length + 1;\n break;\n }\n\n case 'replace': {\n // Replace specific text\n if (!operation.searchText) {\n throw new Error('searchText is required for replace operation');\n }\n\n const originalContent = updatedLines.join('\\n');\n const newContent = originalContent.replace(\n operation.searchText,\n operation.content\n );\n\n if (originalContent === newContent) {\n throw new Error(`Text not found: ${operation.searchText}`);\n }\n\n updatedLines = newContent.split('\\n');\n linesRemoved = operation.searchText.split('\\n').length;\n linesAdded = operation.content.split('\\n').length;\n break;\n }\n\n case 'insert-after': {\n // Insert content after a section\n if (!operation.section) {\n throw new Error('section is required for insert-after operation');\n }\n\n const sectionIndex = this.findSectionIndex(updatedLines, operation.section);\n if (sectionIndex === -1) {\n throw new Error(`Section not found: ${operation.section}`);\n }\n\n // Find the end of the section content\n const insertIndex = this.findSectionEnd(updatedLines, sectionIndex);\n updatedLines.splice(insertIndex, 0, '', operation.content);\n linesAdded = operation.content.split('\\n').length + 1;\n break;\n }\n\n case 'insert-before': {\n // Insert content before a section\n if (!operation.section) {\n throw new Error('section is required for insert-before operation');\n }\n\n const sectionIndex = this.findSectionIndex(updatedLines, operation.section);\n if (sectionIndex === -1) {\n throw new Error(`Section not found: ${operation.section}`);\n }\n\n updatedLines.splice(sectionIndex, 0, operation.content, '');\n linesAdded = operation.content.split('\\n').length + 1;\n break;\n }\n\n default:\n throw new Error(`Unknown operation type: ${operation.type}`);\n }\n\n return {\n content: updatedLines.join('\\n'),\n change: {\n operation: operation.type,\n section: operation.section,\n linesAdded,\n linesRemoved,\n },\n };\n }\n\n /**\n * Find the index of a section heading\n *\n * @param lines - Content lines\n * @param section - Section heading (e.g., \"## Coding Conventions\")\n * @returns Index of the section, or -1 if not found\n */\n private findSectionIndex(lines: string[], section: string): number {\n return lines.findIndex((line) => line.trim() === section.trim());\n }\n\n /**\n * Find the end of a section (before the next section of same or higher level)\n *\n * @param lines - Content lines\n * @param startIndex - Index of the section heading\n * @returns Index where the section ends\n */\n private findSectionEnd(lines: string[], startIndex: number): number {\n const startLine = lines[startIndex];\n if (!startLine) {\n return lines.length;\n }\n\n const sectionLevel = this.getSectionLevel(startLine);\n\n for (let i = startIndex + 1; i < lines.length; i++) {\n const line = lines[i];\n if (!line) continue;\n\n const trimmedLine = line.trim();\n if (trimmedLine.startsWith('#')) {\n const level = this.getSectionLevel(trimmedLine);\n if (level <= sectionLevel) {\n return i;\n }\n }\n }\n\n return lines.length;\n }\n\n /**\n * Get the heading level of a markdown section\n *\n * @param line - Line containing a heading\n * @returns Heading level (1-6)\n */\n private getSectionLevel(line: string): number {\n const match = line.match(/^(#{1,6})\\s/);\n return match && match[1] ? match[1].length : 0;\n }\n}\n","import { readFile } from 'fs/promises';\nimport { existsSync } from 'fs';\nimport { join } from 'path';\nimport type {\n ValidationConfig,\n ResolvedValidationConfig,\n ValidationResult,\n ValidationIssue,\n} from '../types/index.js';\nimport { DEFAULT_VALIDATION_CONFIG } from '../types/index.js';\n\n/**\n * ReadmeValidator - Validates AI_README.md files\n *\n * Features:\n * - Token count validation\n * - Structure validation\n * - Content quality checks\n * - Configurable rules via constructor parameter\n */\nexport class ReadmeValidator {\n private config: ResolvedValidationConfig;\n\n constructor(config?: Partial<ValidationConfig>) {\n // Merge user config with defaults\n this.config = this.mergeConfig(config || {});\n }\n\n /**\n * Merge user config with default config\n */\n private mergeConfig(userConfig: Partial<ValidationConfig>): ResolvedValidationConfig {\n return {\n maxTokens: userConfig.maxTokens ?? DEFAULT_VALIDATION_CONFIG.maxTokens,\n rules: {\n ...DEFAULT_VALIDATION_CONFIG.rules,\n ...(userConfig.rules || {}),\n },\n tokenLimits: {\n ...DEFAULT_VALIDATION_CONFIG.tokenLimits,\n ...(userConfig.tokenLimits || {}),\n },\n };\n }\n\n /**\n * Validate a single AI_README.md file\n *\n * @param readmePath - Path to the README file\n * @returns Validation result\n */\n async validate(readmePath: string): Promise<ValidationResult> {\n const issues: ValidationIssue[] = [];\n\n // Check file exists\n if (!existsSync(readmePath)) {\n return {\n valid: false,\n filePath: readmePath,\n issues: [\n {\n type: 'error',\n rule: 'structure',\n message: `File not found: ${readmePath}`,\n },\n ],\n };\n }\n\n // Read content\n const content = await readFile(readmePath, 'utf-8');\n\n // Check if file is empty\n if (content.trim().length === 0) {\n issues.push({\n type: 'error',\n rule: 'empty-content',\n message: 'README file is empty',\n suggestion: 'Add content to the README file',\n });\n }\n\n // Calculate statistics\n const lines = content.split('\\n');\n const tokens = this.estimateTokens(content);\n const characters = content.length;\n\n // Validate token count\n this.validateTokenCount(tokens, issues);\n\n // Validate structure\n this.validateStructure(content, lines, issues);\n\n // Validate line length\n this.validateLineLength(lines, issues);\n\n // Validate code blocks\n this.validateCodeBlocks(content, issues);\n\n // Calculate score\n const score = this.calculateScore(issues, tokens);\n\n return {\n valid: !issues.some((i) => i.type === 'error'),\n filePath: readmePath,\n issues,\n score,\n stats: {\n tokens,\n lines: lines.length,\n characters,\n },\n };\n }\n\n /**\n * Estimate token count (simple word-based estimation)\n * Formula: words * 1.3 (approximate token-to-word ratio)\n */\n private estimateTokens(content: string): number {\n const words = content.split(/\\s+/).filter((w) => w.length > 0).length;\n return Math.round(words * 1.3);\n }\n\n /**\n * Validate token count against limits\n */\n private validateTokenCount(tokens: number, issues: ValidationIssue[]): void {\n const { tokenLimits, maxTokens } = this.config;\n\n if (tokens > tokenLimits.error) {\n issues.push({\n type: 'error',\n rule: 'token-count',\n message: `README is too long (${tokens} tokens). Maximum recommended: ${maxTokens} tokens.`,\n suggestion: 'Remove unnecessary content, use bullet points instead of paragraphs, and avoid code examples.',\n });\n } else if (tokens > tokenLimits.warning) {\n issues.push({\n type: 'warning',\n rule: 'token-count',\n message: `README is quite long (${tokens} tokens). Consider keeping it under ${tokenLimits.good} tokens.`,\n suggestion: 'Simplify content and remove redundant information.',\n });\n } else if (tokens > tokenLimits.good) {\n issues.push({\n type: 'info',\n rule: 'token-count',\n message: `README length is acceptable (${tokens} tokens).`,\n });\n }\n }\n\n /**\n * Validate README structure\n */\n private validateStructure(_content: string, lines: string[], issues: ValidationIssue[]): void {\n // Check for H1 heading\n if (this.config.rules.requireH1) {\n const hasH1 = lines.some((line) => line.trim().match(/^#\\s+[^#]/));\n if (!hasH1) {\n issues.push({\n type: 'error',\n rule: 'require-h1',\n message: 'README must have a H1 heading (# Title)',\n suggestion: 'Add a title at the beginning of the file: # Project Name',\n });\n }\n }\n\n // Check for required sections\n if (this.config.rules.requireSections && this.config.rules.requireSections.length > 0) {\n for (const section of this.config.rules.requireSections) {\n const hasSection = lines.some((line) => line.trim() === section);\n if (!hasSection) {\n issues.push({\n type: 'warning',\n rule: 'require-sections',\n message: `Missing required section: ${section}`,\n suggestion: `Add section: ${section}`,\n });\n }\n }\n }\n }\n\n /**\n * Validate line length\n */\n private validateLineLength(lines: string[], issues: ValidationIssue[]): void {\n const { maxLineLength } = this.config.rules;\n const longLines = lines\n .map((line, index) => ({ line, index }))\n .filter(({ line }) => line.length > maxLineLength);\n\n if (longLines.length > 3) {\n // Only warn if there are many long lines\n issues.push({\n type: 'info',\n rule: 'line-length',\n message: `${longLines.length} lines exceed ${maxLineLength} characters`,\n suggestion: 'Consider breaking long lines for better readability',\n });\n }\n }\n\n /**\n * Validate code blocks\n */\n private validateCodeBlocks(content: string, issues: ValidationIssue[]): void {\n if (!this.config.rules.allowCodeBlocks) {\n const codeBlockCount = (content.match(/```/g) || []).length / 2;\n if (codeBlockCount > 0) {\n issues.push({\n type: 'warning',\n rule: 'code-blocks',\n message: `Found ${codeBlockCount} code blocks. Code examples consume many tokens.`,\n suggestion: 'Remove code examples or move them to separate documentation.',\n });\n }\n }\n }\n\n /**\n * Calculate quality score (0-100)\n */\n private calculateScore(issues: ValidationIssue[], tokens: number): number {\n let score = 100;\n\n // Deduct points for issues\n for (const issue of issues) {\n if (issue.type === 'error') score -= 20;\n else if (issue.type === 'warning') score -= 10;\n else if (issue.type === 'info') score -= 2;\n }\n\n // Deduct points for excessive length\n const { tokenLimits } = this.config;\n if (tokens > tokenLimits.error) score -= 30;\n else if (tokens > tokenLimits.warning) score -= 15;\n else if (tokens < tokenLimits.excellent) score += 10; // Bonus for concise READMEs\n\n return Math.max(0, Math.min(100, score));\n }\n\n /**\n * Load validation config from .aireadme.config.json\n *\n * @param projectRoot - Project root directory\n * @returns Validation config or null if not found\n */\n static async loadConfig(projectRoot: string): Promise<Partial<ValidationConfig> | null> {\n const configPath = join(projectRoot, '.aireadme.config.json');\n\n if (!existsSync(configPath)) {\n return null;\n }\n\n try {\n const content = await readFile(configPath, 'utf-8');\n const config = JSON.parse(content);\n return config.validation || config; // Support both formats\n } catch (error) {\n console.error(`Failed to load config from ${configPath}:`, error);\n return null;\n }\n }\n}\n","/**\n * Core type definitions for AI_README MCP Server\n */\n\n/**\n * Represents a single AI_README.md file entry in the index\n */\nexport interface ReadmeEntry {\n /** Absolute or relative path to the AI_README.md file */\n path: string;\n /** Scope identifier (e.g., 'root', 'frontend', 'backend') */\n scope: string;\n /** Directory level depth (0 for root) */\n level: number;\n /** Glob patterns this README covers */\n patterns: string[];\n /** Cached content of the README (optional) */\n content?: string;\n}\n\n/**\n * Index of all AI_README files in the project\n */\nexport interface ReadmeIndex {\n /** Root directory of the project */\n projectRoot: string;\n /** List of discovered README entries */\n readmes: ReadmeEntry[];\n /** Timestamp of last index update */\n lastUpdated: Date;\n}\n\n/**\n * Context information for a specific file\n */\nexport interface ReadmeContext {\n /** Path to the AI_README.md file */\n path: string;\n /** Content of the README */\n content: string;\n /** Relevance type */\n relevance: 'root' | 'direct' | 'parent';\n /** Distance in directory levels from target file */\n distance: number;\n}\n\n/**\n * Options for the scanner\n */\nexport interface ScannerOptions {\n /** Patterns to exclude from scanning */\n excludePatterns?: string[];\n /** Whether to cache README contents */\n cacheContent?: boolean;\n /** Custom README filename (default: 'AI_README.md') */\n readmeFilename?: string;\n}\n\n/**\n * Update action types for AI_README modifications\n */\nexport type UpdateAction = 'append' | 'replace' | 'delete';\n\n/**\n * Result of an update operation\n */\nexport interface UpdateResult {\n /** Whether the update succeeded */\n success: boolean;\n /** Path to the updated file */\n updatedPath: string;\n /** Path to backup file (if created) */\n backupPath?: string;\n /** Diff of changes made */\n diff: string;\n}\n\n/**\n * Options for the updater\n */\nexport interface UpdaterOptions {\n /** Directory for backups (default: '.ai_readme_history') */\n backupDir?: string;\n /** Whether to create backups */\n createBackup?: boolean;\n}\n\n/**\n * Validation configuration\n */\nexport interface ValidationConfig {\n /** Maximum tokens allowed (default: 500) */\n maxTokens?: number;\n /** Validation rules */\n rules?: {\n /** Require H1 heading (default: true) */\n requireH1?: boolean;\n /** Required sections (default: []) */\n requireSections?: string[];\n /** Allow code blocks (default: true) */\n allowCodeBlocks?: boolean;\n /** Maximum line length (default: 120) */\n maxLineLength?: number;\n };\n /** Token limit thresholds */\n tokenLimits?: {\n /** Excellent: under this many tokens (default: 300) */\n excellent?: number;\n /** Good: under this many tokens (default: 500) */\n good?: number;\n /** Warning: under this many tokens (default: 800) */\n warning?: number;\n /** Error: over this many tokens (default: 1200) */\n error?: number;\n };\n}\n\n/**\n * Fully resolved validation configuration (all properties required)\n */\nexport interface ResolvedValidationConfig {\n maxTokens: number;\n rules: {\n requireH1: boolean;\n requireSections: string[];\n allowCodeBlocks: boolean;\n maxLineLength: number;\n };\n tokenLimits: {\n excellent: number;\n good: number;\n warning: number;\n error: number;\n };\n}\n\n/**\n * Default validation configuration\n */\nexport const DEFAULT_VALIDATION_CONFIG: ResolvedValidationConfig = {\n maxTokens: 400,\n rules: {\n requireH1: true,\n requireSections: [],\n allowCodeBlocks: false,\n maxLineLength: 100,\n },\n tokenLimits: {\n excellent: 200,\n good: 400,\n warning: 600,\n error: 1000,\n },\n};\n\n/**\n * Validation issue severity\n */\nexport type ValidationSeverity = 'error' | 'warning' | 'info';\n\n/**\n * Validation rule type\n */\nexport type ValidationRule =\n | 'token-count'\n | 'require-h1'\n | 'require-sections'\n | 'code-blocks'\n | 'line-length'\n | 'empty-content'\n | 'structure';\n\n/**\n * Validation issue\n */\nexport interface ValidationIssue {\n /** Issue severity */\n type: ValidationSeverity;\n /** Rule that triggered this issue */\n rule: ValidationRule;\n /** Issue message */\n message: string;\n /** Line number (if applicable) */\n line?: number;\n /** Suggestion for fixing */\n suggestion?: string;\n}\n\n/**\n * Validation result for a single README\n */\nexport interface ValidationResult {\n /** Whether validation passed */\n valid: boolean;\n /** Path to the README file */\n filePath: string;\n /** List of issues found */\n issues: ValidationIssue[];\n /** Quality score (0-100) */\n score?: number;\n /** Token count statistics */\n stats?: {\n tokens: number;\n lines: number;\n characters: number;\n };\n}\n","import { z } from 'zod';\nimport { ReadmeValidator } from '../core/validator.js';\nimport { AIReadmeScanner } from '../core/scanner.js';\nimport type { ValidationConfig } from '../types/index.js';\n\n/**\n * Zod schema for validate_ai_readmes tool\n */\nexport const validateSchema = z.object({\n projectRoot: z.string().describe('The root directory of the project'),\n excludePatterns: z.array(z.string()).optional().describe(\n 'Glob patterns to exclude (e.g., [\"node_modules/**\", \".git/**\"])'\n ),\n config: z.object({\n maxTokens: z.number().optional(),\n rules: z.object({\n requireH1: z.boolean().optional(),\n requireSections: z.array(z.string()).optional(),\n allowCodeBlocks: z.boolean().optional(),\n maxLineLength: z.number().optional(),\n }).optional(),\n tokenLimits: z.object({\n excellent: z.number().optional(),\n good: z.number().optional(),\n warning: z.number().optional(),\n error: z.number().optional(),\n }).optional(),\n }).optional().describe('Custom validation configuration (optional, uses defaults if not provided)'),\n});\n\nexport type ValidateInput = z.infer<typeof validateSchema>;\n\n/**\n * Validate all AI_README.md files in a project\n *\n * @param input - Validation parameters\n * @returns Validation results for all README files\n *\n * @example\n * ```typescript\n * await validateAIReadmes({\n * projectRoot: '/path/to/project',\n * excludePatterns: ['node_modules/**'],\n * config: {\n * maxTokens: 500,\n * rules: {\n * requireH1: true,\n * requireSections: ['## Architecture', '## Conventions']\n * }\n * }\n * });\n * ```\n */\nexport async function validateAIReadmes(input: ValidateInput) {\n const { projectRoot, excludePatterns, config: userConfig } = input;\n\n try {\n // Use provided config, fallback to file config if available, then defaults\n let config: Partial<ValidationConfig> | undefined = userConfig;\n\n if (!config) {\n // Optionally load from .aireadme.config.json if it exists\n const fileConfig = await ReadmeValidator.loadConfig(projectRoot);\n if (fileConfig) {\n config = fileConfig;\n }\n }\n\n // Create validator with config\n const validator = new ReadmeValidator(config);\n\n // Scan for all README files\n const scanner = new AIReadmeScanner(projectRoot, {\n excludePatterns: excludePatterns || [],\n cacheContent: false,\n });\n const index = await scanner.scan();\n\n // Validate each README\n const results = [];\n for (const readme of index.readmes) {\n const result = await validator.validate(readme.path);\n results.push(result);\n }\n\n // Calculate overall statistics\n const totalFiles = results.length;\n const validFiles = results.filter(r => r.valid).length;\n const totalIssues = results.reduce((sum, r) => sum + r.issues.length, 0);\n const averageScore = totalFiles > 0\n ? Math.round(results.reduce((sum, r) => sum + (r.score || 0), 0) / totalFiles)\n : 0;\n\n // Group issues by severity\n const issuesBySeverity = {\n error: 0,\n warning: 0,\n info: 0,\n };\n for (const result of results) {\n for (const issue of result.issues) {\n issuesBySeverity[issue.type]++;\n }\n }\n\n return {\n success: true,\n projectRoot,\n summary: {\n totalFiles,\n validFiles,\n invalidFiles: totalFiles - validFiles,\n totalIssues,\n averageScore,\n issuesBySeverity,\n },\n results,\n message: totalIssues === 0\n ? `All ${totalFiles} README files passed validation! Average score: ${averageScore}/100`\n : `Found ${totalIssues} issues across ${totalFiles} README files. ${validFiles} files passed validation.`,\n };\n } catch (error) {\n return {\n success: false,\n projectRoot,\n error: error instanceof Error ? error.message : String(error),\n message: `Validation failed: ${error instanceof Error ? error.message : String(error)}`,\n };\n }\n}\n","/**\n * MCP Tool: init_ai_readme\n * Scans project for empty AI_README files and populates them with AI-generated content\n */\n\nimport { z } from 'zod';\nimport { writeFile, readFile } from 'fs/promises';\nimport { join } from 'path';\nimport { existsSync } from 'fs';\nimport { AIReadmeScanner } from '../core/scanner.js';\n\nexport const initSchema = z.object({\n projectRoot: z.string().describe('The root directory of the project'),\n excludePatterns: z\n .array(z.string())\n .optional()\n .describe('Glob patterns to exclude when scanning'),\n targetPath: z\n .string()\n .optional()\n .describe('Specific directory to initialize (optional, defaults to scanning entire project)'),\n});\n\nexport type InitInput = z.infer<typeof initSchema>;\n\ninterface EmptyReadmeInfo {\n path: string;\n dirPath: string;\n needsCreation: boolean;\n}\n\n/**\n * Initialize AI_README files in the project\n * - Scans for empty or missing AI_README files\n * - Prompts AI to populate them with project conventions\n */\nexport async function initAIReadme(input: InitInput) {\n const { projectRoot, excludePatterns, targetPath } = input;\n\n // Scan the project\n const scanner = new AIReadmeScanner(projectRoot, {\n excludePatterns,\n cacheContent: true,\n });\n\n const index = await scanner.scan();\n\n // Find empty AI_README files\n const emptyReadmes: EmptyReadmeInfo[] = [];\n\n // Check existing AI_READMEs\n for (const readme of index.readmes) {\n if (!readme.content || readme.content.trim().length === 0) {\n emptyReadmes.push({\n path: readme.path,\n dirPath: readme.path.replace(/[\\/\\\\]AI_README\\.md$/, ''),\n needsCreation: false,\n });\n }\n }\n\n // If no AI_README exists at all, create one at project root\n if (index.readmes.length === 0) {\n const rootReadmePath = join(projectRoot, 'AI_README.md');\n\n // Create empty file if it doesn't exist\n if (!existsSync(rootReadmePath)) {\n await writeFile(rootReadmePath, '', 'utf-8');\n }\n\n emptyReadmes.push({\n path: rootReadmePath,\n dirPath: projectRoot,\n needsCreation: true,\n });\n }\n\n // If targetPath is specified, filter to only that path\n let targetReadmes = emptyReadmes;\n if (targetPath) {\n const normalizedTarget = targetPath.replace(/\\\\/g, '/');\n targetReadmes = emptyReadmes.filter(r =>\n r.dirPath.replace(/\\\\/g, '/').includes(normalizedTarget)\n );\n }\n\n // If no empty READMEs found\n if (targetReadmes.length === 0) {\n return {\n success: true,\n message: 'ā
All AI_README files are already populated!',\n initialized: [],\n };\n }\n\n // Generate initialization instructions for AI\n let promptText = `# š AI_README Initialization\\n\\n`;\n promptText += `Found ${targetReadmes.length} AI_README file(s) that need population.\\n\\n`;\n\n if (targetReadmes.some(r => r.needsCreation)) {\n promptText += `ā
Created empty AI_README file(s) at:\\n`;\n for (const readme of targetReadmes.filter(r => r.needsCreation)) {\n promptText += `- ${readme.path.replace(/\\\\/g, '/')}\\n`;\n }\n promptText += `\\n`;\n }\n\n promptText += `## š Required Actions\\n\\n`;\n promptText += `You must populate the following AI_README files by analyzing their respective directories:\\n\\n`;\n\n for (let i = 0; i < targetReadmes.length; i++) {\n const readme = targetReadmes[i];\n const readmePath = readme.path.replace(/\\\\/g, '/');\n const dirPath = readme.dirPath.replace(/\\\\/g, '/');\n\n promptText += `### ${i + 1}. ${readmePath}\\n\\n`;\n promptText += `**Directory to analyze:** \\`${dirPath}\\`\\n\\n`;\n promptText += `**Steps:**\\n\\n`;\n promptText += `1. **Scan directory contents:**\\n`;\n promptText += ` \\`\\`\\`\\n`;\n promptText += ` Use Glob: pattern=\"**/*\", path=\"${dirPath}\"\\n`;\n promptText += ` \\`\\`\\`\\n\\n`;\n promptText += `2. **Read key source files** (pick 2-5 representative files):\\n`;\n promptText += ` - Configuration files (package.json, tsconfig.json, etc.)\\n`;\n promptText += ` - Main source files\\n`;\n promptText += ` - Important modules/components\\n\\n`;\n promptText += `3. **Analyze and identify:**\\n`;\n promptText += ` - š¦ **Tech Stack**: Frameworks, libraries, languages, tools\\n`;\n promptText += ` - šļø **Architecture**: Project structure, design patterns\\n`;\n promptText += ` - š **Coding Conventions**: Naming, formatting, patterns\\n`;\n promptText += ` - šļø **File Structure**: Directory organization, module boundaries\\n\\n`;\n promptText += `4. **Populate AI_README:**\\n`;\n promptText += ` \\`\\`\\`\\n`;\n promptText += ` Use update_ai_readme:\\n`;\n promptText += ` {\\n`;\n promptText += ` readmePath: \"${readmePath}\",\\n`;\n promptText += ` operations: [{\\n`;\n promptText += ` type: \"append\",\\n`;\n promptText += ` content: \"<your analysis in markdown format>\"\\n`;\n promptText += ` }]\\n`;\n promptText += ` }\\n`;\n promptText += ` \\`\\`\\`\\n\\n`;\n promptText += ` **Include these sections:**\\n`;\n promptText += ` - \\`## Tech Stack\\` - List frameworks, libraries, tools\\n`;\n promptText += ` - \\`## Architecture Patterns\\` - Design patterns, project structure\\n`;\n promptText += ` - \\`## Coding Conventions\\` - Naming, formatting, best practices\\n`;\n promptText += ` - \\`## File Structure\\` - Directory organization (brief)\\n\\n`;\n promptText += ` **Keep it concise:** AI_READMEs should be <400 tokens. Focus on actionable conventions.\\n\\n`;\n }\n\n promptText += `---\\n\\n`;\n promptText += `š” **Tips:**\\n`;\n promptText += `- Work through each README sequentially\\n`;\n promptText += `- Be concise - every token counts!\\n`;\n promptText += `- Focus on conventions that help generate better code\\n`;\n promptText += `- After completing all, you can verify with \\`validate_ai_readmes\\`\\n\\n`;\n promptText += `**Start with the first AI_README now!**\\n`;\n\n return {\n success: true,\n message: `Found ${targetReadmes.length} AI_README file(s) to initialize`,\n readmesToInitialize: targetReadmes.map(r => r.path.replace(/\\\\/g, '/')),\n instructions: promptText,\n };\n}\n"],"mappings":";;;AAKA,SAAS,cAAc;AACvB,SAAS,4BAA4B;AACrC;AAAA,EACE;AAAA,EACA;AAAA,OACK;AACP,SAAS,uBAAuB;;;ACNhC,SAAS,SAAS;;;ACDlB,SAAS,YAAY;AACrB,SAAS,gBAAgB;AACzB,SAAS,SAAS,YAAY;AAGvB,IAAM,kBAAN,MAAsB;AAAA,EACnB;AAAA,EACA;AAAA,EAER,YAAY,aAAqB,SAA0B;AACzD,SAAK,cAAc;AACnB,SAAK,UAAU;AAAA,MACb,iBAAiB,SAAS,mBAAmB;AAAA,QAC3C;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACA,cAAc,SAAS,gBAAgB;AAAA,MACvC,gBAAgB,SAAS,kBAAkB;AAAA,IAC7C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,OAA6B;AACjC,UAAM,UAAU,MAAM,KAAK,QAAQ,cAAc;AACjD,UAAM,SAAS,KAAK,QAAQ;AAG5B,UAAM,QAAQ,MAAM,KAAK,SAAS;AAAA,MAChC,KAAK,KAAK;AAAA,MACV;AAAA,MACA,UAAU;AAAA,MACV,OAAO;AAAA,IACT,CAAC;AAGD,UAAM,UAAyB,CAAC;AAEhC,eAAW,QAAQ,OAAO;AACxB,YAAM,QAAQ,MAAM,KAAK,kBAAkB,IAAI;AAC/C,cAAQ,KAAK,KAAK;AAAA,IACpB;AAGA,YAAQ,KAAK,CAAC,GAAG,MAAM,EAAE,QAAQ,EAAE,KAAK;AAExC,WAAO;AAAA,MACL,aAAa,KAAK;AAAA,MAClB;AAAA,MACA,aAAa,oBAAI,KAAK;AAAA,IACxB;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,kBAAkB,UAAwC;AAEtE,UAAM,iBAAiB,SAAS,QAAQ,OAAO,GAAG;AAClD,UAAM,MAAM,QAAQ,cAAc;AAClC,UAAM,QAAQ,QAAQ,MAAM,IAAI,IAAI,MAAM,GAAG,EAAE;AAC/C,UAAM,QAAQ,QAAQ,MAAM,SAAS,IAAI,QAAQ,OAAO,GAAG;AAG3D,UAAM,WAAW,KAAK,iBAAiB,GAAG;AAG1C,QAAI;AACJ,QAAI,KAAK,QAAQ,cAAc;AAC7B,UAAI;AACF,cAAM,WAAW,KAAK,KAAK,aAAa,QAAQ;AAChD,kBAAU,MAAM,SAAS,UAAU,OAAO;AAAA,MAC5C,SAAS,OAAO;AACd,gBAAQ,MAAM,kBAAkB,QAAQ,KAAK,KAAK;AAAA,MACpD;AAAA,IACF;AAEA,WAAO;AAAA,MACL,MAAM;AAAA,MACN;AAAA,MACA;AAAA,MACA;AAAA,MACA;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,iBAAiB,KAAuB;AAC9C,QAAI,QAAQ,KAAK;AACf,aAAO,CAAC,MAAM;AAAA,IAChB;AAEA,WAAO;AAAA,MACL,GAAG,GAAG;AAAA;AAAA,MACN,GAAG,GAAG;AAAA;AAAA,IACR;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,UAAgC;AACpC,WAAO,KAAK,KAAK;AAAA,EACnB;AAAA;AAAA;AAAA;AAAA,EAKA,iBAAyB;AACvB,WAAO,KAAK;AAAA,EACd;AAAA;AAAA;AAAA;AAAA,EAKA,aAAuC;AACrC,WAAO,EAAE,GAAG,KAAK,QAAQ;AAAA,EAC3B;AACF;;;ADzHO,IAAM,iBAAiB,EAAE,OAAO;AAAA,EACrC,aAAa,EAAE,OAAO,EAAE,SAAS,mCAAmC;AAAA,EACpE,iBAAiB,EACd,MAAM,EAAE,OAAO,CAAC,EAChB,SAAS,EACT,SAAS,iEAAiE;AAC/E,CAAC;AAID,eAAsB,kBAAkB,OAAsB;AAC5D,QAAM,EAAE,aAAa,gBAAgB,IAAI;AAGzC,QAAM,UAAU,IAAI,gBAAgB,aAAa;AAAA,IAC/C;AAAA,IACA,cAAc;AAAA;AAAA,EAChB,CAAC;AAGD,QAAM,QAAQ,MAAM,QAAQ,KAAK;AAGjC,SAAO;AAAA,IACL,aAAa,MAAM;AAAA,IACnB,YAAY,MAAM,QAAQ;AAAA,IAC1B,aAAa,MAAM,QAAQ,IAAI,CAAC,YAAY;AAAA,MAC1C,MAAM,OAAO;AAAA,MACb,OAAO,OAAO;AAAA,MACd,OAAO,OAAO;AAAA,MACd,UAAU,OAAO;AAAA,IACnB,EAAE;AAAA,IACF,aAAa,MAAM,YAAY,YAAY;AAAA,EAC7C;AACF;;;AErCA,SAAS,KAAAA,UAAS;;;ACDlB,SAAS,iBAAiB;AAC1B,SAAS,WAAAC,UAAS,QAAAC,aAAY;AAC9B,SAAS,YAAAC,iBAAgB;AAGlB,IAAM,gBAAN,MAAoB;AAAA,EACjB;AAAA,EAER,YAAY,OAAoB;AAC9B,SAAK,QAAQ;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAOA,MAAM,kBACJ,UACA,cAAuB,MACG;AAC1B,UAAM,WAA4B,CAAC;AAGnC,eAAW,UAAU,KAAK,MAAM,SAAS;AACvC,YAAM,QAAQ,KAAK,YAAY,UAAU,MAAM;AAE/C,UAAI,CAAC,MAAO;AAGZ,UAAI,CAAC,eAAe,OAAO,UAAU,EAAG;AAGxC,YAAM,WAAW,KAAK,kBAAkB,UAAU,MAAM;AAGxD,YAAM,UAAU,MAAM,KAAK,iBAAiB,MAAM;AAGlD,YAAM,YAAY,KAAK,mBAAmB,UAAU,MAAM;AAE1D,eAAS,KAAK;AAAA,QACZ,MAAM,OAAO;AAAA,QACb;AAAA,QACA;AAAA,QACA;AAAA,MACF,CAAC;AAAA,IACH;AAGA,aAAS,KAAK,CAAC,GAAG,MAAM;AACtB,UAAI,EAAE,aAAa,EAAE,UAAU;AAC7B,eAAO,EAAE,WAAW,EAAE;AAAA,MACxB;AAEA,YAAM,iBAAiB,EAAE,QAAQ,GAAG,QAAQ,GAAG,MAAM,EAAE;AACvD,aAAO,eAAe,EAAE,SAAS,IAAI,eAAe,EAAE,SAAS;AAAA,IACjE,CAAC;AAED,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAM,mBAAmB,WAA4D;AACnF,UAAM,UAAU,oBAAI,IAA6B;AAEjD,eAAW,YAAY,WAAW;AAChC,YAAM,WAAW,MAAM,KAAK,kBAAkB,QAAQ;AACtD,cAAQ,IAAI,UAAU,QAAQ;AAAA,IAChC;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKQ,YAAY,UAAkB,QAA8B;AAClE,WAAO,OAAO,SAAS,KAAK,aAAW;AACrC,aAAO,UAAU,UAAU,SAAS,EAAE,KAAK,KAAK,CAAC;AAAA,IACnD,CAAC;AAAA,EACH;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAAkB,UAAkB,QAA6B;AACvE,UAAM,UAAUF,SAAQ,QAAQ;AAChC,UAAM,YAAYA,SAAQ,OAAO,IAAI;AAGrC,QAAI,cAAc,KAAK;AACrB,aAAO,YAAY,MAAM,IAAI,QAAQ,MAAM,GAAG,EAAE;AAAA,IAClD;AAGA,QAAI,YAAY,WAAW;AACzB,aAAO;AAAA,IACT;AAGA,QAAI,QAAQ,WAAW,YAAY,GAAG,GAAG;AACvC,YAAM,UAAU,QAAQ,MAAM,UAAU,SAAS,CAAC;AAClD,aAAO,QAAQ,MAAM,GAAG,EAAE;AAAA,IAC5B;AAGA,UAAM,YAAY,QAAQ,MAAM,GAAG;AACnC,UAAM,cAAc,UAAU,MAAM,GAAG;AAGvC,QAAI,cAAc;AAClB,aAAS,IAAI,GAAG,IAAI,KAAK,IAAI,UAAU,QAAQ,YAAY,MAAM,GAAG,KAAK;AACvE,UAAI,UAAU,CAAC,MAAM,YAAY,CAAC,GAAG;AACnC;AAAA,MACF,OAAO;AACL;AAAA,MACF;AAAA,IACF;AAGA,WAAQ,UAAU,SAAS,eAAgB,YAAY,SAAS;AAAA,EAClE;AAAA;AAAA;AAAA;AAAA,EAKQ,mBACN,UACA,QAC8B;AAC9B,UAAM,UAAUA,SAAQ,QAAQ;AAChC,UAAM,YAAYA,SAAQ,OAAO,IAAI;AAGrC,QAAI,cAAc,KAAK;AACrB,aAAO;AAAA,IACT;AAGA,QAAI,YAAY,WAAW;AACzB,aAAO;AAAA,IACT;AAGA,QAAI,QAAQ,WAAW,YAAY,GAAG,GAAG;AACvC,aAAO;AAAA,IACT;AAEA,WAAO;AAAA,EACT;AAAA;AAAA;AAAA;AAAA,EAKA,MAAc,iBAAiB,QAAsC;AAEnE,QAAI,OAAO,SAAS;AAClB,aAAO,OAAO;AAAA,IAChB;AAGA,QAAI;AACF,YAAM,WAAWC,MAAK,KAAK,MAAM,aAAa,OAAO,IAAI;AACzD,aAAO,MAAMC,UAAS,UAAU,OAAO;AAAA,IACzC,SAAS,OAAO;AACd,cAAQ,MAAM,kBAAkB,OAAO,IAAI,KAAK,KAAK;AACrD,aAAO,0BAA0B,OAAO,IAAI;AAAA,IAC9C;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKA,YAAY,OAA0B;AACpC,SAAK,QAAQ;AAAA,EACf;AAAA;AAAA;AAAA;AAAA,EAKA,WAAwB;AACtB,WAAO,KAAK;AAAA,EACd;AACF;;;ADrLO,IAAM,mBAAmBC,GAAE,OAAO;AAAA,EACvC,aAAaA,GAAE,OAAO,EAAE,SAAS,mCAAmC;AAAA,EACpE,UAAUA,GACP,OAAO,EACP,SAAS,6DAA6D;AAAA,EACzE,aAAaA,GACV,QAAQ,EACR,SAAS,EACT,QAAQ,IAAI,EACZ,SAAS,yDAAyD;AAAA,EACrE,iBAAiBA,GACd,MAAMA,GAAE,OAAO,CAAC,EAChB,SAAS,EACT,SAAS,wCAAwC;AACtD,CAAC;AAID,eAAsB,kBAAkB,OAAwB;AAC9D,QAAM,EAAE,aAAa,UAAU,aAAa,gBAAgB,IAAI;AAGhE,QAAM,UAAU,IAAI,gBAAgB,aAAa;AAAA,IAC/C;AAAA,IACA,cAAc;AAAA;AAAA,EAChB,CAAC;AAED,QAAM,QAAQ,MAAM,QAAQ,KAAK;AAGjC,QAAM,SAAS,IAAI,cAAc,KAAK;AACtC,QAAM,WAAW,MAAM,OAAO,kBAAkB,UAAU,WAAW;AAGrE,QAAM,kBAAkB,SAAS,KAAK,SAAO,CAAC,IAAI,WAAW,IAAI,QAAQ,KAAK,EAAE,WAAW,CAAC;AAC5F,QAAM,eAAe,SAAS,WAAW;AAGzC,QAAM,oBAAoB,SAAS,IAAI,CAAC,SAAS;AAAA,IAC/C,MAAM,IAAI;AAAA,IACV,WAAW,IAAI;AAAA,IACf,UAAU,IAAI;AAAA,IACd,SAAS,IAAI;AAAA,EACf,EAAE;AAGF,MAAI,aAAa,qCAA8B,QAAQ;AAAA;AAAA;AAGvD,MAAI,gBAAgB,iBAAiB;AACnC,kBAAc;AAAA;AAAA;AACd,kBAAc;AAAA;AAAA;AACd,kBAAc;AAAA;AACd,kBAAc,kCAAkC,YAAY,QAAQ,OAAO,GAAG,CAAC;AAAA;AAC/E,kBAAc;AAAA;AAAA;AACd,kBAAc;AAAA;AACd,kBAAc;AAAA;AACd,kBAAc;AAAA;AACd,kBAAc;AAAA;AAAA;AACd,kBAAc;AAAA;AAAA;AACd,kBAAc;AAAA;AAAA;AAAA,EAChB;AAGA,QAAM,mBAAmB,SAAS,OAAO,SAAO,IAAI,WAAW,IAAI,QAAQ,KAAK,EAAE,SAAS,CAAC;AAE5F,MAAI,iBAAiB,SAAS,GAAG;AAE/B,eAAW,OAAO,kBAAkB;AAClC,UAAI,IAAI,cAAc,QAAQ;AAC5B,sBAAc,yBAAyB,IAAI,IAAI;AAAA;AAAA;AAAA,MACjD,WAAW,IAAI,cAAc,UAAU;AACrC,sBAAc,kCAAkC,IAAI,IAAI;AAAA;AAAA;AAAA,MAC1D,OAAO;AACL,sBAAc,kCAAkC,IAAI,IAAI;AAAA;AAAA;AAAA,MAC1D;AAEA,oBAAc,IAAI,UAAU;AAAA,IAC9B;AAEA,kBAAc;AAAA;AACd,kBAAc;AAAA;AACd,kBAAc;AAAA;AACd,kBAAc;AAAA;AACd,kBAAc;AAAA;AAAA,EAChB;AAEA,SAAO;AAAA,IACL;AAAA,IACA,eAAe,SAAS;AAAA,IACxB,UAAU;AAAA,IACV,iBAAiB;AAAA,EACnB;AACF;;;AEtGA,SAAS,KAAAC,UAAS;AAClB,SAAS,WAAAC,gBAAe;;;ACDxB,SAAS,YAAAC,WAAU,iBAAiB;AACpC,SAAS,kBAAkB;AA8BpB,IAAM,gBAAN,MAAoB;AAAA,EACzB,cAAc;AAAA,EAEd;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAM,OACJ,YACA,YACuB;AACvB,QAAI;AAEF,UAAI,UAAU;AACd,UAAI,WAAW,UAAU,GAAG;AAC1B,kBAAU,MAAMA,UAAS,YAAY,OAAO;AAAA,MAC9C;AAGA,UAAI,iBAAiB;AACrB,YAAM,UAAmC,CAAC;AAE1C,iBAAW,aAAa,YAAY;AAClC,cAAM,SAAS,MAAM,KAAK,eAAe,gBAAgB,SAAS;AAClE,yBAAiB,OAAO;AACxB,gBAAQ,KAAK,OAAO,MAAM;AAAA,MAC5B;AAGA,YAAM,UAAU,YAAY,gBAAgB,OAAO;AAEnD,aAAO;AAAA,QACL,SAAS;AAAA,QACT;AAAA,MACF;AAAA,IACF,SAAS,OAAO;AACd,aAAO;AAAA,QACL,SAAS;AAAA,QACT,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,QAC5D,SAAS,CAAC;AAAA,MACZ;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASA,MAAc,eACZ,SACA,WACkE;AAClE,UAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,QAAI,eAAe,CAAC,GAAG,KAAK;AAC5B,QAAI,aAAa;AACjB,QAAI,eAAe;AAEnB,YAAQ,UAAU,MAAM;AAAA,MACtB,KAAK,UAAU;AAEb,qBAAa,KAAK,IAAI,UAAU,OAAO;AACvC,qBAAa,UAAU,QAAQ,MAAM,IAAI,EAAE,SAAS;AACpD;AAAA,MACF;AAAA,MAEA,KAAK,WAAW;AAEd,qBAAa,QAAQ,UAAU,SAAS,EAAE;AAC1C,qBAAa,UAAU,QAAQ,MAAM,IAAI,EAAE,SAAS;AACpD;AAAA,MACF;AAAA,MAEA,KAAK,WAAW;AAEd,YAAI,CAAC,UAAU,YAAY;AACzB,gBAAM,IAAI,MAAM,8CAA8C;AAAA,QAChE;AAEA,cAAM,kBAAkB,aAAa,KAAK,IAAI;AAC9C,cAAM,aAAa,gBAAgB;AAAA,UACjC,UAAU;AAAA,UACV,UAAU;AAAA,QACZ;AAEA,YAAI,oBAAoB,YAAY;AAClC,gBAAM,IAAI,MAAM,mBAAmB,UAAU,UAAU,EAAE;AAAA,QAC3D;AAEA,uBAAe,WAAW,MAAM,IAAI;AACpC,uBAAe,UAAU,WAAW,MAAM,IAAI,EAAE;AAChD,qBAAa,UAAU,QAAQ,MAAM,IAAI,EAAE;AAC3C;AAAA,MACF;AAAA,MAEA,KAAK,gBAAgB;AAEnB,YAAI,CAAC,UAAU,SAAS;AACtB,gBAAM,IAAI,MAAM,gDAAgD;AAAA,QAClE;AAEA,cAAM,eAAe,KAAK,iBAAiB,cAAc,UAAU,OAAO;AAC1E,YAAI,iBAAiB,IAAI;AACvB,gBAAM,IAAI,MAAM,sBAAsB,UAAU,OAAO,EAAE;AAAA,QAC3D;AAGA,cAAM,cAAc,KAAK,eAAe,cAAc,YAAY;AAClE,qBAAa,OAAO,aAAa,GAAG,IAAI,UAAU,OAAO;AACzD,qBAAa,UAAU,QAAQ,MAAM,IAAI,EAAE,SAAS;AACpD;AAAA,MACF;AAAA,MAEA,KAAK,iBAAiB;AAEpB,YAAI,CAAC,UAAU,SAAS;AACtB,gBAAM,IAAI,MAAM,iDAAiD;AAAA,QACnE;AAEA,cAAM,eAAe,KAAK,iBAAiB,cAAc,UAAU,OAAO;AAC1E,YAAI,iBAAiB,IAAI;AACvB,gBAAM,IAAI,MAAM,sBAAsB,UAAU,OAAO,EAAE;AAAA,QAC3D;AAEA,qBAAa,OAAO,cAAc,GAAG,UAAU,SAAS,EAAE;AAC1D,qBAAa,UAAU,QAAQ,MAAM,IAAI,EAAE,SAAS;AACpD;AAAA,MACF;AAAA,MAEA;AACE,cAAM,IAAI,MAAM,2BAA2B,UAAU,IAAI,EAAE;AAAA,IAC/D;AAEA,WAAO;AAAA,MACL,SAAS,aAAa,KAAK,IAAI;AAAA,MAC/B,QAAQ;AAAA,QACN,WAAW,UAAU;AAAA,QACrB,SAAS,UAAU;AAAA,QACnB;AAAA,QACA;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,iBAAiB,OAAiB,SAAyB;AACjE,WAAO,MAAM,UAAU,CAAC,SAAS,KAAK,KAAK,MAAM,QAAQ,KAAK,CAAC;AAAA,EACjE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EASQ,eAAe,OAAiB,YAA4B;AAClE,UAAM,YAAY,MAAM,UAAU;AAClC,QAAI,CAAC,WAAW;AACd,aAAO,MAAM;AAAA,IACf;AAEA,UAAM,eAAe,KAAK,gBAAgB,SAAS;AAEnD,aAAS,IAAI,aAAa,GAAG,IAAI,MAAM,QAAQ,KAAK;AAClD,YAAM,OAAO,MAAM,CAAC;AACpB,UAAI,CAAC,KAAM;AAEX,YAAM,cAAc,KAAK,KAAK;AAC9B,UAAI,YAAY,WAAW,GAAG,GAAG;AAC/B,cAAM,QAAQ,KAAK,gBAAgB,WAAW;AAC9C,YAAI,SAAS,cAAc;AACzB,iBAAO;AAAA,QACT;AAAA,MACF;AAAA,IACF;AAEA,WAAO,MAAM;AAAA,EACf;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQQ,gBAAgB,MAAsB;AAC5C,UAAM,QAAQ,KAAK,MAAM,aAAa;AACtC,WAAO,SAAS,MAAM,CAAC,IAAI,MAAM,CAAC,EAAE,SAAS;AAAA,EAC/C;AACF;;;AC1OA,SAAS,YAAAC,iBAAgB;AACzB,SAAS,cAAAC,mBAAkB;AAC3B,SAAS,QAAAC,aAAY;;;ACyId,IAAM,4BAAsD;AAAA,EACjE,WAAW;AAAA,EACX,OAAO;AAAA,IACL,WAAW;AAAA,IACX,iBAAiB,CAAC;AAAA,IAClB,iBAAiB;AAAA,IACjB,eAAe;AAAA,EACjB;AAAA,EACA,aAAa;AAAA,IACX,WAAW;AAAA,IACX,MAAM;AAAA,IACN,SAAS;AAAA,IACT,OAAO;AAAA,EACT;AACF;;;ADrIO,IAAM,kBAAN,MAAsB;AAAA,EACnB;AAAA,EAER,YAAY,QAAoC;AAE9C,SAAK,SAAS,KAAK,YAAY,UAAU,CAAC,CAAC;AAAA,EAC7C;AAAA;AAAA;AAAA;AAAA,EAKQ,YAAY,YAAiE;AACnF,WAAO;AAAA,MACL,WAAW,WAAW,aAAa,0BAA0B;AAAA,MAC7D,OAAO;AAAA,QACL,GAAG,0BAA0B;AAAA,QAC7B,GAAI,WAAW,SAAS,CAAC;AAAA,MAC3B;AAAA,MACA,aAAa;AAAA,QACX,GAAG,0BAA0B;AAAA,QAC7B,GAAI,WAAW,eAAe,CAAC;AAAA,MACjC;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,MAAM,SAAS,YAA+C;AAC5D,UAAM,SAA4B,CAAC;AAGnC,QAAI,CAACC,YAAW,UAAU,GAAG;AAC3B,aAAO;AAAA,QACL,OAAO;AAAA,QACP,UAAU;AAAA,QACV,QAAQ;AAAA,UACN;AAAA,YACE,MAAM;AAAA,YACN,MAAM;AAAA,YACN,SAAS,mBAAmB,UAAU;AAAA,UACxC;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAGA,UAAM,UAAU,MAAMC,UAAS,YAAY,OAAO;AAGlD,QAAI,QAAQ,KAAK,EAAE,WAAW,GAAG;AAC/B,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,MAAM;AAAA,QACN,SAAS;AAAA,QACT,YAAY;AAAA,MACd,CAAC;AAAA,IACH;AAGA,UAAM,QAAQ,QAAQ,MAAM,IAAI;AAChC,UAAM,SAAS,KAAK,eAAe,OAAO;AAC1C,UAAM,aAAa,QAAQ;AAG3B,SAAK,mBAAmB,QAAQ,MAAM;AAGtC,SAAK,kBAAkB,SAAS,OAAO,MAAM;AAG7C,SAAK,mBAAmB,OAAO,MAAM;AAGrC,SAAK,mBAAmB,SAAS,MAAM;AAGvC,UAAM,QAAQ,KAAK,eAAe,QAAQ,MAAM;AAEhD,WAAO;AAAA,MACL,OAAO,CAAC,OAAO,KAAK,CAAC,MAAM,EAAE,SAAS,OAAO;AAAA,MAC7C,UAAU;AAAA,MACV;AAAA,MACA;AAAA,MACA,OAAO;AAAA,QACL;AAAA,QACA,OAAO,MAAM;AAAA,QACb;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA;AAAA,EAMQ,eAAe,SAAyB;AAC9C,UAAM,QAAQ,QAAQ,MAAM,KAAK,EAAE,OAAO,CAAC,MAAM,EAAE,SAAS,CAAC,EAAE;AAC/D,WAAO,KAAK,MAAM,QAAQ,GAAG;AAAA,EAC/B;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAAmB,QAAgB,QAAiC;AAC1E,UAAM,EAAE,aAAa,UAAU,IAAI,KAAK;AAExC,QAAI,SAAS,YAAY,OAAO;AAC9B,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,MAAM;AAAA,QACN,SAAS,uBAAuB,MAAM,kCAAkC,SAAS;AAAA,QACjF,YAAY;AAAA,MACd,CAAC;AAAA,IACH,WAAW,SAAS,YAAY,SAAS;AACvC,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,MAAM;AAAA,QACN,SAAS,yBAAyB,MAAM,uCAAuC,YAAY,IAAI;AAAA,QAC/F,YAAY;AAAA,MACd,CAAC;AAAA,IACH,WAAW,SAAS,YAAY,MAAM;AACpC,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,MAAM;AAAA,QACN,SAAS,gCAAgC,MAAM;AAAA,MACjD,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,kBAAkB,UAAkB,OAAiB,QAAiC;AAE5F,QAAI,KAAK,OAAO,MAAM,WAAW;AAC/B,YAAM,QAAQ,MAAM,KAAK,CAAC,SAAS,KAAK,KAAK,EAAE,MAAM,WAAW,CAAC;AACjE,UAAI,CAAC,OAAO;AACV,eAAO,KAAK;AAAA,UACV,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS;AAAA,UACT,YAAY;AAAA,QACd,CAAC;AAAA,MACH;AAAA,IACF;AAGA,QAAI,KAAK,OAAO,MAAM,mBAAmB,KAAK,OAAO,MAAM,gBAAgB,SAAS,GAAG;AACrF,iBAAW,WAAW,KAAK,OAAO,MAAM,iBAAiB;AACvD,cAAM,aAAa,MAAM,KAAK,CAAC,SAAS,KAAK,KAAK,MAAM,OAAO;AAC/D,YAAI,CAAC,YAAY;AACf,iBAAO,KAAK;AAAA,YACV,MAAM;AAAA,YACN,MAAM;AAAA,YACN,SAAS,6BAA6B,OAAO;AAAA,YAC7C,YAAY,gBAAgB,OAAO;AAAA,UACrC,CAAC;AAAA,QACH;AAAA,MACF;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAAmB,OAAiB,QAAiC;AAC3E,UAAM,EAAE,cAAc,IAAI,KAAK,OAAO;AACtC,UAAM,YAAY,MACf,IAAI,CAAC,MAAM,WAAW,EAAE,MAAM,MAAM,EAAE,EACtC,OAAO,CAAC,EAAE,KAAK,MAAM,KAAK,SAAS,aAAa;AAEnD,QAAI,UAAU,SAAS,GAAG;AAExB,aAAO,KAAK;AAAA,QACV,MAAM;AAAA,QACN,MAAM;AAAA,QACN,SAAS,GAAG,UAAU,MAAM,iBAAiB,aAAa;AAAA,QAC1D,YAAY;AAAA,MACd,CAAC;AAAA,IACH;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,mBAAmB,SAAiB,QAAiC;AAC3E,QAAI,CAAC,KAAK,OAAO,MAAM,iBAAiB;AACtC,YAAM,kBAAkB,QAAQ,MAAM,MAAM,KAAK,CAAC,GAAG,SAAS;AAC9D,UAAI,iBAAiB,GAAG;AACtB,eAAO,KAAK;AAAA,UACV,MAAM;AAAA,UACN,MAAM;AAAA,UACN,SAAS,SAAS,cAAc;AAAA,UAChC,YAAY;AAAA,QACd,CAAC;AAAA,MACH;AAAA,IACF;AAAA,EACF;AAAA;AAAA;AAAA;AAAA,EAKQ,eAAe,QAA2B,QAAwB;AACxE,QAAI,QAAQ;AAGZ,eAAW,SAAS,QAAQ;AAC1B,UAAI,MAAM,SAAS,QAAS,UAAS;AAAA,eAC5B,MAAM,SAAS,UAAW,UAAS;AAAA,eACnC,MAAM,SAAS,OAAQ,UAAS;AAAA,IAC3C;AAGA,UAAM,EAAE,YAAY,IAAI,KAAK;AAC7B,QAAI,SAAS,YAAY,MAAO,UAAS;AAAA,aAChC,SAAS,YAAY,QAAS,UAAS;AAAA,aACvC,SAAS,YAAY,UAAW,UAAS;AAElD,WAAO,KAAK,IAAI,GAAG,KAAK,IAAI,KAAK,KAAK,CAAC;AAAA,EACzC;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAQA,aAAa,WAAW,aAAgE;AACtF,UAAM,aAAaC,MAAK,aAAa,uBAAuB;AAE5D,QAAI,CAACF,YAAW,UAAU,GAAG;AAC3B,aAAO;AAAA,IACT;AAEA,QAAI;AACF,YAAM,UAAU,MAAMC,UAAS,YAAY,OAAO;AAClD,YAAM,SAAS,KAAK,MAAM,OAAO;AACjC,aAAO,OAAO,cAAc;AAAA,IAC9B,SAAS,OAAO;AACd,cAAQ,MAAM,8BAA8B,UAAU,KAAK,KAAK;AAChE,aAAO;AAAA,IACT;AAAA,EACF;AACF;;;AFnQA,IAAM,wBAAwBE,GAAE,OAAO;AAAA,EACrC,MAAMA,GAAE,KAAK,CAAC,WAAW,UAAU,WAAW,gBAAgB,eAAe,CAAC,EAAE;AAAA,IAC9E;AAAA,EACF;AAAA,EACA,SAASA,GAAE,OAAO,EAAE,SAAS,EAAE;AAAA,IAC7B;AAAA,EACF;AAAA,EACA,YAAYA,GAAE,OAAO,EAAE,SAAS,EAAE;AAAA,IAChC;AAAA,EACF;AAAA,EACA,SAASA,GAAE,OAAO,EAAE,SAAS,2BAA2B;AAC1D,CAAC;AAKM,IAAM,eAAeA,GAAE,OAAO;AAAA,EACnC,YAAYA,GAAE,OAAO,EAAE,SAAS,yCAAyC;AAAA,EACzE,YAAYA,GAAE,MAAM,qBAAqB,EAAE;AAAA,IACzC;AAAA,EACF;AACF,CAAC;AAyBD,eAAsB,eAAe,OAAoB;AACvD,QAAM,EAAE,YAAY,WAAW,IAAI;AAEnC,QAAM,UAAU,IAAI,cAAc;AAGlC,QAAM,SAAS,MAAM,QAAQ,OAAO,YAAY,UAA+B;AAE/E,MAAI,CAAC,OAAO,SAAS;AACnB,WAAO;AAAA,MACL,SAAS;AAAA,MACT;AAAA,MACA,OAAO,OAAO;AAAA,MACd,SAAS,oBAAoB,UAAU,KAAK,OAAO,KAAK;AAAA,IAC1D;AAAA,EACF;AAGA,MAAI;AACF,UAAM,cAAcC,SAAQA,SAAQ,UAAU,CAAC;AAC/C,UAAM,SAAS,MAAM,gBAAgB,WAAW,WAAW;AAC3D,UAAM,YAAY,IAAI,gBAAgB,UAAU,MAAS;AACzD,UAAM,aAAa,MAAM,UAAU,SAAS,UAAU;AAGtD,UAAM,WAAW,WAAW,OACzB,OAAO,OAAK,EAAE,SAAS,aAAa,EAAE,SAAS,OAAO,EACtD,IAAI,OAAK,IAAI,EAAE,KAAK,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE;AAEpD,UAAM,cAAc,OAAO,QAAQ;AAAA,MAAK,OACtC,EAAE,cAAc,aAAa,EAAE,cAAc,YAAY,EAAE,cAAc;AAAA,IAC3E,IACI,8HACA;AAEJ,WAAO;AAAA,MACL,SAAS;AAAA,MACT;AAAA,MACA,SAAS,OAAO;AAAA,MAChB,SAAS,wBAAwB,UAAU,SAAS,OAAO,QAAQ,MAAM,mDAAmD,WAAW;AAAA,MACvI,YAAY;AAAA,QACV,OAAO,WAAW;AAAA,QAClB,OAAO,WAAW;AAAA,QAClB,UAAU,SAAS,SAAS,IAAI,WAAW;AAAA,QAC3C,OAAO,WAAW;AAAA,MACpB;AAAA,IACF;AAAA,EACF,SAAS,iBAAiB;AAExB,WAAO;AAAA,MACL,SAAS;AAAA,MACT;AAAA,MACA,SAAS,OAAO;AAAA,MAChB,SAAS,wBAAwB,UAAU,SAAS,OAAO,QAAQ,MAAM;AAAA,MACzE,YAAY;AAAA,QACV,OAAO;AAAA,QACP,OAAO,2BAA2B,QAAQ,gBAAgB,UAAU;AAAA,MACtE;AAAA,IACF;AAAA,EACF;AACF;;;AIlHA,SAAS,KAAAC,UAAS;AAQX,IAAM,iBAAiBC,GAAE,OAAO;AAAA,EACrC,aAAaA,GAAE,OAAO,EAAE,SAAS,mCAAmC;AAAA,EACpE,iBAAiBA,GAAE,MAAMA,GAAE,OAAO,CAAC,EAAE,SAAS,EAAE;AAAA,IAC9C;AAAA,EACF;AAAA,EACA,QAAQA,GAAE,OAAO;AAAA,IACf,WAAWA,GAAE,OAAO,EAAE,SAAS;AAAA,IAC/B,OAAOA,GAAE,OAAO;AAAA,MACd,WAAWA,GAAE,QAAQ,EAAE,SAAS;AAAA,MAChC,iBAAiBA,GAAE,MAAMA,GAAE,OAAO,CAAC,EAAE,SAAS;AAAA,MAC9C,iBAAiBA,GAAE,QAAQ,EAAE,SAAS;AAAA,MACtC,eAAeA,GAAE,OAAO,EAAE,SAAS;AAAA,IACrC,CAAC,EAAE,SAAS;AAAA,IACZ,aAAaA,GAAE,OAAO;AAAA,MACpB,WAAWA,GAAE,OAAO,EAAE,SAAS;AAAA,MAC/B,MAAMA,GAAE,OAAO,EAAE,SAAS;AAAA,MAC1B,SAASA,GAAE,OAAO,EAAE,SAAS;AAAA,MAC7B,OAAOA,GAAE,OAAO,EAAE,SAAS;AAAA,IAC7B,CAAC,EAAE,SAAS;AAAA,EACd,CAAC,EAAE,SAAS,EAAE,SAAS,2EAA2E;AACpG,CAAC;AAyBD,eAAsB,kBAAkB,OAAsB;AAC5D,QAAM,EAAE,aAAa,iBAAiB,QAAQ,WAAW,IAAI;AAE7D,MAAI;AAEF,QAAI,SAAgD;AAEpD,QAAI,CAAC,QAAQ;AAEX,YAAM,aAAa,MAAM,gBAAgB,WAAW,WAAW;AAC/D,UAAI,YAAY;AACd,iBAAS;AAAA,MACX;AAAA,IACF;AAGA,UAAM,YAAY,IAAI,gBAAgB,MAAM;AAG5C,UAAM,UAAU,IAAI,gBAAgB,aAAa;AAAA,MAC/C,iBAAiB,mBAAmB,CAAC;AAAA,MACrC,cAAc;AAAA,IAChB,CAAC;AACD,UAAM,QAAQ,MAAM,QAAQ,KAAK;AAGjC,UAAM,UAAU,CAAC;AACjB,eAAW,UAAU,MAAM,SAAS;AAClC,YAAM,SAAS,MAAM,UAAU,SAAS,OAAO,IAAI;AACnD,cAAQ,KAAK,MAAM;AAAA,IACrB;AAGA,UAAM,aAAa,QAAQ;AAC3B,UAAM,aAAa,QAAQ,OAAO,OAAK,EAAE,KAAK,EAAE;AAChD,UAAM,cAAc,QAAQ,OAAO,CAAC,KAAK,MAAM,MAAM,EAAE,OAAO,QAAQ,CAAC;AACvE,UAAM,eAAe,aAAa,IAC9B,KAAK,MAAM,QAAQ,OAAO,CAAC,KAAK,MAAM,OAAO,EAAE,SAAS,IAAI,CAAC,IAAI,UAAU,IAC3E;AAGJ,UAAM,mBAAmB;AAAA,MACvB,OAAO;AAAA,MACP,SAAS;AAAA,MACT,MAAM;AAAA,IACR;AACA,eAAW,UAAU,SAAS;AAC5B,iBAAW,SAAS,OAAO,QAAQ;AACjC,yBAAiB,MAAM,IAAI;AAAA,MAC7B;AAAA,IACF;AAEA,WAAO;AAAA,MACL,SAAS;AAAA,MACT;AAAA,MACA,SAAS;AAAA,QACP;AAAA,QACA;AAAA,QACA,cAAc,aAAa;AAAA,QAC3B;AAAA,QACA;AAAA,QACA;AAAA,MACF;AAAA,MACA;AAAA,MACA,SAAS,gBAAgB,IACrB,OAAO,UAAU,mDAAmD,YAAY,SAChF,SAAS,WAAW,kBAAkB,UAAU,kBAAkB,UAAU;AAAA,IAClF;AAAA,EACF,SAAS,OAAO;AACd,WAAO;AAAA,MACL,SAAS;AAAA,MACT;AAAA,MACA,OAAO,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAAA,MAC5D,SAAS,sBAAsB,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,CAAC;AAAA,IACvF;AAAA,EACF;AACF;;;AC5HA,SAAS,KAAAC,UAAS;AAClB,SAAS,aAAAC,kBAA2B;AACpC,SAAS,QAAAC,aAAY;AACrB,SAAS,cAAAC,mBAAkB;AAGpB,IAAM,aAAaC,GAAE,OAAO;AAAA,EACjC,aAAaA,GAAE,OAAO,EAAE,SAAS,mCAAmC;AAAA,EACpE,iBAAiBA,GACd,MAAMA,GAAE,OAAO,CAAC,EAChB,SAAS,EACT,SAAS,wCAAwC;AAAA,EACpD,YAAYA,GACT,OAAO,EACP,SAAS,EACT,SAAS,kFAAkF;AAChG,CAAC;AAeD,eAAsB,aAAa,OAAkB;AACnD,QAAM,EAAE,aAAa,iBAAiB,WAAW,IAAI;AAGrD,QAAM,UAAU,IAAI,gBAAgB,aAAa;AAAA,IAC/C;AAAA,IACA,cAAc;AAAA,EAChB,CAAC;AAED,QAAM,QAAQ,MAAM,QAAQ,KAAK;AAGjC,QAAM,eAAkC,CAAC;AAGzC,aAAW,UAAU,MAAM,SAAS;AAClC,QAAI,CAAC,OAAO,WAAW,OAAO,QAAQ,KAAK,EAAE,WAAW,GAAG;AACzD,mBAAa,KAAK;AAAA,QAChB,MAAM,OAAO;AAAA,QACb,SAAS,OAAO,KAAK,QAAQ,wBAAwB,EAAE;AAAA,QACvD,eAAe;AAAA,MACjB,CAAC;AAAA,IACH;AAAA,EACF;AAGA,MAAI,MAAM,QAAQ,WAAW,GAAG;AAC9B,UAAM,iBAAiBC,MAAK,aAAa,cAAc;AAGvD,QAAI,CAACC,YAAW,cAAc,GAAG;AAC/B,YAAMC,WAAU,gBAAgB,IAAI,OAAO;AAAA,IAC7C;AAEA,iBAAa,KAAK;AAAA,MAChB,MAAM;AAAA,MACN,SAAS;AAAA,MACT,eAAe;AAAA,IACjB,CAAC;AAAA,EACH;AAGA,MAAI,gBAAgB;AACpB,MAAI,YAAY;AACd,UAAM,mBAAmB,WAAW,QAAQ,OAAO,GAAG;AACtD,oBAAgB,aAAa;AAAA,MAAO,OAClC,EAAE,QAAQ,QAAQ,OAAO,GAAG,EAAE,SAAS,gBAAgB;AAAA,IACzD;AAAA,EACF;AAGA,MAAI,cAAc,WAAW,GAAG;AAC9B,WAAO;AAAA,MACL,SAAS;AAAA,MACT,SAAS;AAAA,MACT,aAAa,CAAC;AAAA,IAChB;AAAA,EACF;AAGA,MAAI,aAAa;AAAA;AAAA;AACjB,gBAAc,SAAS,cAAc,MAAM;AAAA;AAAA;AAE3C,MAAI,cAAc,KAAK,OAAK,EAAE,aAAa,GAAG;AAC5C,kBAAc;AAAA;AACd,eAAW,UAAU,cAAc,OAAO,OAAK,EAAE,aAAa,GAAG;AAC/D,oBAAc,KAAK,OAAO,KAAK,QAAQ,OAAO,GAAG,CAAC;AAAA;AAAA,IACpD;AACA,kBAAc;AAAA;AAAA,EAChB;AAEA,gBAAc;AAAA;AAAA;AACd,gBAAc;AAAA;AAAA;AAEd,WAAS,IAAI,GAAG,IAAI,cAAc,QAAQ,KAAK;AAC7C,UAAM,SAAS,cAAc,CAAC;AAC9B,UAAM,aAAa,OAAO,KAAK,QAAQ,OAAO,GAAG;AACjD,UAAM,UAAU,OAAO,QAAQ,QAAQ,OAAO,GAAG;AAEjD,kBAAc,OAAO,IAAI,CAAC,KAAK,UAAU;AAAA;AAAA;AACzC,kBAAc,+BAA+B,OAAO;AAAA;AAAA;AACpD,kBAAc;AAAA;AAAA;AACd,kBAAc;AAAA;AACd,kBAAc;AAAA;AACd,kBAAc,sCAAsC,OAAO;AAAA;AAC3D,kBAAc;AAAA;AAAA;AACd,kBAAc;AAAA;AACd,kBAAc;AAAA;AACd,kBAAc;AAAA;AACd,kBAAc;AAAA;AAAA;AACd,kBAAc;AAAA;AACd,kBAAc;AAAA;AACd,kBAAc;AAAA;AACd,kBAAc;AAAA;AACd,kBAAc;AAAA;AAAA;AACd,kBAAc;AAAA;AACd,kBAAc;AAAA;AACd,kBAAc;AAAA;AACd,kBAAc;AAAA;AACd,kBAAc,qBAAqB,UAAU;AAAA;AAC7C,kBAAc;AAAA;AACd,kBAAc;AAAA;AACd,kBAAc;AAAA;AACd,kBAAc;AAAA;AACd,kBAAc;AAAA;AACd,kBAAc;AAAA;AAAA;AACd,kBAAc;AAAA;AACd,kBAAc;AAAA;AACd,kBAAc;AAAA;AACd,kBAAc;AAAA;AACd,kBAAc;AAAA;AAAA;AACd,kBAAc;AAAA;AAAA;AAAA,EAChB;AAEA,gBAAc;AAAA;AAAA;AACd,gBAAc;AAAA;AACd,gBAAc;AAAA;AACd,gBAAc;AAAA;AACd,gBAAc;AAAA;AACd,gBAAc;AAAA;AAAA;AACd,gBAAc;AAAA;AAEd,SAAO;AAAA,IACL,SAAS;AAAA,IACT,SAAS,SAAS,cAAc,MAAM;AAAA,IACtC,qBAAqB,cAAc,IAAI,OAAK,EAAE,KAAK,QAAQ,OAAO,GAAG,CAAC;AAAA,IACtE,cAAc;AAAA,EAChB;AACF;;;AV9HA,IAAM,SAAS,IAAI;AAAA,EACjB;AAAA,IACE,MAAM;AAAA,IACN,SAAS;AAAA,EACX;AAAA,EACA;AAAA,IACE,cAAc;AAAA,MACZ,OAAO,CAAC;AAAA,IACV;AAAA,EACF;AACF;AAGA,OAAO,kBAAkB,wBAAwB,YAAY;AAC3D,SAAO;AAAA,IACL,OAAO;AAAA,MACL;AAAA,QACE,MAAM;AAAA,QACN,aACE;AAAA,QACF,aAAa,gBAAgB,cAAc;AAAA,MAC7C;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,aACE;AAAA,QAaF,aAAa,gBAAgB,gBAAgB;AAAA,MAC/C;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,aACE;AAAA,QA6BF,aAAa,gBAAgB,YAAY;AAAA,MAC3C;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,aACE;AAAA,QACF,aAAa,gBAAgB,cAAc;AAAA,MAC7C;AAAA,MACA;AAAA,QACE,MAAM;AAAA,QACN,aACE;AAAA,QAkBF,aAAa,gBAAgB,UAAU;AAAA,MACzC;AAAA,IACF;AAAA,EACF;AACF,CAAC;AAGD,OAAO,kBAAkB,uBAAuB,OAAO,YAAY;AACjE,QAAM,EAAE,MAAM,WAAW,KAAK,IAAI,QAAQ;AAE1C,MAAI;AACF,QAAI,SAAS,uBAAuB;AAClC,YAAM,QAAQ,eAAe,MAAM,IAAI;AACvC,YAAM,SAAS,MAAM,kBAAkB,KAAK;AAC5C,aAAO;AAAA,QACL,SAAS;AAAA,UACP;AAAA,YACE,MAAM;AAAA,YACN,MAAM,KAAK,UAAU,QAAQ,MAAM,CAAC;AAAA,UACtC;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,SAAS,wBAAwB;AACnC,YAAM,QAAQ,iBAAiB,MAAM,IAAI;AACzC,YAAM,SAAS,MAAM,kBAAkB,KAAK;AAC5C,aAAO;AAAA,QACL,SAAS;AAAA,UACP;AAAA,YACE,MAAM;AAAA,YACN,MAAM,OAAO;AAAA,UACf;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,SAAS,oBAAoB;AAC/B,YAAM,QAAQ,aAAa,MAAM,IAAI;AACrC,YAAM,SAAS,MAAM,eAAe,KAAK;AACzC,aAAO;AAAA,QACL,SAAS;AAAA,UACP;AAAA,YACE,MAAM;AAAA,YACN,MAAM,KAAK,UAAU,QAAQ,MAAM,CAAC;AAAA,UACtC;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,SAAS,uBAAuB;AAClC,YAAM,QAAQ,eAAe,MAAM,IAAI;AACvC,YAAM,SAAS,MAAM,kBAAkB,KAAK;AAC5C,aAAO;AAAA,QACL,SAAS;AAAA,UACP;AAAA,YACE,MAAM;AAAA,YACN,MAAM,KAAK,UAAU,QAAQ,MAAM,CAAC;AAAA,UACtC;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,QAAI,SAAS,kBAAkB;AAC7B,YAAM,QAAQ,WAAW,MAAM,IAAI;AACnC,YAAM,SAAS,MAAM,aAAa,KAAK;AACvC,aAAO;AAAA,QACL,SAAS;AAAA,UACP;AAAA,YACE,MAAM;AAAA,YACN,MAAM,OAAO,gBAAgB,KAAK,UAAU,QAAQ,MAAM,CAAC;AAAA,UAC7D;AAAA,QACF;AAAA,MACF;AAAA,IACF;AAEA,UAAM,IAAI,MAAM,iBAAiB,IAAI,EAAE;AAAA,EACzC,SAAS,OAAO;AACd,UAAM,eAAe,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK;AAC1E,WAAO;AAAA,MACL,SAAS;AAAA,QACP;AAAA,UACE,MAAM;AAAA,UACN,MAAM,UAAU,YAAY;AAAA,QAC9B;AAAA,MACF;AAAA,MACA,SAAS;AAAA,IACX;AAAA,EACF;AACF,CAAC;AAED,eAAe,OAAO;AACpB,QAAM,YAAY,IAAI,qBAAqB;AAC3C,QAAM,OAAO,QAAQ,SAAS;AAE9B,UAAQ,MAAM,8BAA8B;AAC5C,UAAQ,MAAM,mHAAmH;AACnI;AAEA,KAAK,EAAE,MAAM,CAAC,UAAU;AACtB,UAAQ,MAAM,gBAAgB,KAAK;AACnC,UAAQ,KAAK,CAAC;AAChB,CAAC;","names":["z","dirname","join","readFile","z","z","dirname","readFile","readFile","existsSync","join","existsSync","readFile","join","z","dirname","z","z","z","writeFile","join","existsSync","z","join","existsSync","writeFile"]}
|