ai-readme-mcp 0.2.0 → 0.3.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 +182 -20
- package/dist/index.js +361 -42
- package/dist/index.js.map +1 -1
- package/docs/templates/basic.md +55 -0
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -28,7 +28,8 @@ Think of it as a **"style guide and context manager for AI"** - ensuring every A
|
|
|
28
28
|
- 🎯 **Smart Context Routing** - Find relevant README content based on file paths
|
|
29
29
|
- 🔄 **Update & Sync** - AI can both read and update AI_README files
|
|
30
30
|
- ✅ **Validation & Quality** - Ensure README consistency with token limits and structure checks
|
|
31
|
-
-
|
|
31
|
+
- 🤖 **Smart Initialization** - Auto-generate AI_README files based on project analysis
|
|
32
|
+
- 🏗️ **Monorepo Support** - Place AI_README.md files at different folder levels; the tool automatically finds and uses the most relevant one
|
|
32
33
|
- 📦 **Easy Integration** - Works seamlessly with Cursor, Claude Code, and other MCP clients
|
|
33
34
|
|
|
34
35
|
|
|
@@ -36,7 +37,7 @@ Think of it as a **"style guide and context manager for AI"** - ensuring every A
|
|
|
36
37
|
|
|
37
38
|
### Installation
|
|
38
39
|
|
|
39
|
-
**Option 1: Using npx (Recommended
|
|
40
|
+
**Option 1: Using npx (Recommended)**
|
|
40
41
|
|
|
41
42
|
No installation needed! Just configure and use via npx:
|
|
42
43
|
|
|
@@ -53,7 +54,7 @@ No installation needed! Just configure and use via npx:
|
|
|
53
54
|
|
|
54
55
|
The `-y` flag automatically accepts the npx prompt.
|
|
55
56
|
|
|
56
|
-
**Option 2: Global Installation
|
|
57
|
+
**Option 2: Global Installation**
|
|
57
58
|
|
|
58
59
|
```bash
|
|
59
60
|
npm install -g ai-readme-mcp
|
|
@@ -71,9 +72,9 @@ Then configure:
|
|
|
71
72
|
}
|
|
72
73
|
```
|
|
73
74
|
|
|
74
|
-
**Option 3: Local Development
|
|
75
|
+
**Option 3: Local Development**
|
|
75
76
|
|
|
76
|
-
> **Note:** Use this method
|
|
77
|
+
> **Note:** Use this method if you want to modify or contribute to the source code.
|
|
77
78
|
|
|
78
79
|
```bash
|
|
79
80
|
# Clone this repository to a permanent location
|
|
@@ -142,7 +143,7 @@ Add to Cursor's MCP configuration file:
|
|
|
142
143
|
**Windows:** `%APPDATA%\Cursor\User\mcp.json`
|
|
143
144
|
**macOS/Linux:** `~/.cursor/mcp.json`
|
|
144
145
|
|
|
145
|
-
**
|
|
146
|
+
**Using npx (Recommended):**
|
|
146
147
|
|
|
147
148
|
```json
|
|
148
149
|
{
|
|
@@ -155,7 +156,7 @@ Add to Cursor's MCP configuration file:
|
|
|
155
156
|
}
|
|
156
157
|
```
|
|
157
158
|
|
|
158
|
-
**
|
|
159
|
+
**If globally installed:**
|
|
159
160
|
|
|
160
161
|
```json
|
|
161
162
|
{
|
|
@@ -167,16 +168,14 @@ Add to Cursor's MCP configuration file:
|
|
|
167
168
|
}
|
|
168
169
|
```
|
|
169
170
|
|
|
170
|
-
**
|
|
171
|
+
**For local development:**
|
|
171
172
|
|
|
172
173
|
```json
|
|
173
174
|
{
|
|
174
175
|
"mcpServers": {
|
|
175
176
|
"ai-readme-manager": {
|
|
176
177
|
"command": "node",
|
|
177
|
-
"args": [
|
|
178
|
-
"D:\\Home\\WorkSpace\\playground\\ai-readme-mcp\\dist\\index.js"
|
|
179
|
-
]
|
|
178
|
+
"args": ["/path/to/ai-readme-mcp/dist/index.js"]
|
|
180
179
|
}
|
|
181
180
|
}
|
|
182
181
|
}
|
|
@@ -193,7 +192,7 @@ Add to `claude_desktop_config.json`:
|
|
|
193
192
|
**Windows:** `%APPDATA%\claude\claude_desktop_config.json`
|
|
194
193
|
**macOS/Linux:** `~/.config/claude/config.json` or `~/Library/Application Support/Claude/config.json`
|
|
195
194
|
|
|
196
|
-
**
|
|
195
|
+
**Using npx (Recommended):**
|
|
197
196
|
|
|
198
197
|
```json
|
|
199
198
|
{
|
|
@@ -206,7 +205,7 @@ Add to `claude_desktop_config.json`:
|
|
|
206
205
|
}
|
|
207
206
|
```
|
|
208
207
|
|
|
209
|
-
**
|
|
208
|
+
**If globally installed:**
|
|
210
209
|
|
|
211
210
|
```json
|
|
212
211
|
{
|
|
@@ -218,14 +217,14 @@ Add to `claude_desktop_config.json`:
|
|
|
218
217
|
}
|
|
219
218
|
```
|
|
220
219
|
|
|
221
|
-
**
|
|
220
|
+
**For local development:**
|
|
222
221
|
|
|
223
222
|
```json
|
|
224
223
|
{
|
|
225
224
|
"mcpServers": {
|
|
226
225
|
"ai-readme-manager": {
|
|
227
226
|
"command": "node",
|
|
228
|
-
"args": ["/
|
|
227
|
+
"args": ["/path/to/ai-readme-mcp/dist/index.js"]
|
|
229
228
|
}
|
|
230
229
|
}
|
|
231
230
|
}
|
|
@@ -233,7 +232,20 @@ Add to `claude_desktop_config.json`:
|
|
|
233
232
|
|
|
234
233
|
### Create Your First AI_README
|
|
235
234
|
|
|
236
|
-
|
|
235
|
+
You can use the `init_ai_readme` MCP tool to automatically generate a customized AI_README.md based on your project:
|
|
236
|
+
|
|
237
|
+
**Smart Mode (Default)**:
|
|
238
|
+
The tool automatically detects your project and generates relevant content:
|
|
239
|
+
- Detects project type (library/application/monorepo)
|
|
240
|
+
- Identifies language (TypeScript, Python, Go, Rust, Java, etc.)
|
|
241
|
+
- Recognizes framework (React, Vue, Next.js, Express, NestJS, etc.)
|
|
242
|
+
- Analyzes directory structure
|
|
243
|
+
- Generates framework-specific conventions
|
|
244
|
+
|
|
245
|
+
Ask your AI assistant:
|
|
246
|
+
> "Please use init_ai_readme to create an AI_README.md for this project"
|
|
247
|
+
|
|
248
|
+
Or create manually:
|
|
237
249
|
|
|
238
250
|
```markdown
|
|
239
251
|
# My Project
|
|
@@ -251,13 +263,54 @@ Create `AI_README.md` in your project root:
|
|
|
251
263
|
Run tests with: `npm test`
|
|
252
264
|
```
|
|
253
265
|
|
|
266
|
+
### Multi-Level AI_README (Not Just for Monorepos!)
|
|
267
|
+
|
|
268
|
+
**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.
|
|
269
|
+
|
|
270
|
+
**Why multi-level?**
|
|
271
|
+
- 🎯 **Avoid bloated root README** - Keep each README focused and concise
|
|
272
|
+
- 📍 **Precise context** - AI gets only the relevant conventions for the code it's working on
|
|
273
|
+
- 🔧 **Flexible organization** - Organize by feature, module, or any structure that makes sense
|
|
274
|
+
|
|
275
|
+
Simply place `AI_README.md` files at different folder levels:
|
|
276
|
+
|
|
277
|
+
```
|
|
278
|
+
my-monorepo/
|
|
279
|
+
├── AI_README.md # Root-level conventions (applies to all)
|
|
280
|
+
├── apps/
|
|
281
|
+
│ ├── frontend/
|
|
282
|
+
│ │ ├── AI_README.md # Frontend-specific conventions
|
|
283
|
+
│ │ └── src/components/Button.tsx
|
|
284
|
+
│ └── backend/
|
|
285
|
+
│ ├── AI_README.md # Backend-specific conventions
|
|
286
|
+
│ └── src/api/users.ts
|
|
287
|
+
└── packages/
|
|
288
|
+
└── shared/
|
|
289
|
+
├── AI_README.md # Shared library conventions
|
|
290
|
+
└── src/utils.ts
|
|
291
|
+
```
|
|
292
|
+
|
|
293
|
+
**Smart Empty README Handling:**
|
|
294
|
+
- 📝 If you create an empty `AI_README.md` in a subdirectory, the tool can auto-fill it with relevant conventions
|
|
295
|
+
- 🔗 For subdirectories with parent READMEs, generates differential content (only module-specific conventions)
|
|
296
|
+
- 📋 For root directories, generates full project analysis
|
|
297
|
+
- Ask your AI: "This AI_README.md is empty, can you help fill it?"
|
|
298
|
+
|
|
299
|
+
When AI works on a file, it automatically gets:
|
|
300
|
+
- The **most relevant** AI_README (closest parent directory)
|
|
301
|
+
- Plus the **root-level** AI_README (for project-wide standards)
|
|
302
|
+
|
|
303
|
+
For example, when editing `apps/frontend/src/components/Button.tsx`:
|
|
304
|
+
- ✅ Gets `apps/frontend/AI_README.md` (React component standards)
|
|
305
|
+
- ✅ Gets root `AI_README.md` (project-wide Git, testing conventions)
|
|
306
|
+
|
|
254
307
|
### Test the Integration
|
|
255
308
|
|
|
256
|
-
Restart
|
|
309
|
+
Restart your IDE, then ask your AI assistant:
|
|
257
310
|
|
|
258
311
|
> "I'm about to create a new component. What conventions should I follow?"
|
|
259
312
|
|
|
260
|
-
|
|
313
|
+
The AI will automatically retrieve your AI_README context!
|
|
261
314
|
|
|
262
315
|
For detailed setup instructions, see [Quick Start Guide](./docs/QUICK_START.md).
|
|
263
316
|
|
|
@@ -314,10 +367,64 @@ npm run dev
|
|
|
314
367
|
- **[Quick Start Guide](./docs/QUICK_START.md)** - Get started in 10 minutes
|
|
315
368
|
- **[Contributing Guide](./CONTRIBUTING.md)** - How to contribute
|
|
316
369
|
- [Project Specification](./docs/SPEC.md) - Complete technical specification
|
|
317
|
-
- [AI_README Templates](./docs/templates/) - Example templates for your projects
|
|
318
370
|
|
|
319
371
|
## 🛠️ Available MCP Tools
|
|
320
372
|
|
|
373
|
+
### `init_ai_readme`
|
|
374
|
+
|
|
375
|
+
Initialize a new AI_README.md file with smart content generation based on project analysis.
|
|
376
|
+
|
|
377
|
+
```typescript
|
|
378
|
+
// Parameters
|
|
379
|
+
{
|
|
380
|
+
targetPath: string; // Required: Directory where AI_README.md will be created
|
|
381
|
+
projectName?: string; // Optional: Project name (auto-detected if not provided)
|
|
382
|
+
overwrite?: boolean; // Optional: Overwrite existing file (default: false)
|
|
383
|
+
smart?: boolean; // Optional: Enable smart content generation (default: true)
|
|
384
|
+
}
|
|
385
|
+
|
|
386
|
+
// Returns
|
|
387
|
+
{
|
|
388
|
+
success: boolean;
|
|
389
|
+
readmePath: string;
|
|
390
|
+
action: 'created' | 'filled' | 'overwritten';
|
|
391
|
+
projectInfo?: {
|
|
392
|
+
projectName: string;
|
|
393
|
+
projectType: 'library' | 'application' | 'monorepo' | 'unknown';
|
|
394
|
+
language: string;
|
|
395
|
+
framework?: string;
|
|
396
|
+
packageManager?: string;
|
|
397
|
+
hasTests: boolean;
|
|
398
|
+
mainDirs: string[];
|
|
399
|
+
};
|
|
400
|
+
message: string;
|
|
401
|
+
}
|
|
402
|
+
```
|
|
403
|
+
|
|
404
|
+
**Smart Mode Features**:
|
|
405
|
+
- Auto-detects project type, language, and framework
|
|
406
|
+
- Generates framework-specific conventions (React, Vue, Python, etc.)
|
|
407
|
+
- For subdirectories with parent READMEs, generates differential content
|
|
408
|
+
- Handles empty files (< 50 chars) by auto-filling them
|
|
409
|
+
- Analyzes directory structure and dependencies
|
|
410
|
+
|
|
411
|
+
**Example Usage**:
|
|
412
|
+
|
|
413
|
+
```typescript
|
|
414
|
+
// Create smart README for React project
|
|
415
|
+
{
|
|
416
|
+
targetPath: "/path/to/react-app",
|
|
417
|
+
smart: true
|
|
418
|
+
}
|
|
419
|
+
// Result: Auto-detects React, generates component naming conventions, etc.
|
|
420
|
+
|
|
421
|
+
// Fill empty subdirectory README
|
|
422
|
+
{
|
|
423
|
+
targetPath: "/path/to/monorepo/apps/frontend"
|
|
424
|
+
}
|
|
425
|
+
// Result: Generates differential content extending root README
|
|
426
|
+
```
|
|
427
|
+
|
|
321
428
|
### `discover_ai_readmes`
|
|
322
429
|
|
|
323
430
|
Scans your project and discovers all AI_README.md files.
|
|
@@ -440,6 +547,61 @@ Update an AI_README.md file with specified operations.
|
|
|
440
547
|
}
|
|
441
548
|
```
|
|
442
549
|
|
|
550
|
+
### `validate_ai_readmes`
|
|
551
|
+
|
|
552
|
+
Validate all AI_README.md files in your project for quality and token efficiency.
|
|
553
|
+
|
|
554
|
+
```typescript
|
|
555
|
+
// Parameters
|
|
556
|
+
{
|
|
557
|
+
projectRoot: string; // Required: Project root directory
|
|
558
|
+
excludePatterns?: string[]; // Optional: Glob patterns to exclude
|
|
559
|
+
config?: { // Optional: Custom validation config
|
|
560
|
+
maxTokens?: number;
|
|
561
|
+
rules?: {
|
|
562
|
+
requireH1?: boolean;
|
|
563
|
+
requireSections?: string[];
|
|
564
|
+
allowCodeBlocks?: boolean;
|
|
565
|
+
maxLineLength?: number;
|
|
566
|
+
};
|
|
567
|
+
tokenLimits?: {
|
|
568
|
+
excellent?: number; // Default: 200
|
|
569
|
+
good?: number; // Default: 400
|
|
570
|
+
warning?: number; // Default: 600
|
|
571
|
+
error?: number; // Default: 1000
|
|
572
|
+
};
|
|
573
|
+
};
|
|
574
|
+
}
|
|
575
|
+
|
|
576
|
+
// Returns
|
|
577
|
+
{
|
|
578
|
+
valid: boolean;
|
|
579
|
+
totalFiles: number;
|
|
580
|
+
results: Array<{
|
|
581
|
+
path: string;
|
|
582
|
+
valid: boolean;
|
|
583
|
+
tokens: number;
|
|
584
|
+
rating: 'excellent' | 'good' | 'needs-improvement' | 'too-long';
|
|
585
|
+
issues: string[];
|
|
586
|
+
suggestions: string[];
|
|
587
|
+
}>;
|
|
588
|
+
summary: string;
|
|
589
|
+
}
|
|
590
|
+
```
|
|
591
|
+
|
|
592
|
+
**Validation Features**:
|
|
593
|
+
- Token counting for AI consumption optimization
|
|
594
|
+
- Structure validation (H1 heading, sections)
|
|
595
|
+
- Line length checks (default: 100 chars)
|
|
596
|
+
- Code block detection (disabled by default for strict mode)
|
|
597
|
+
- Quality ratings based on token count
|
|
598
|
+
|
|
599
|
+
**Default Token Limits (Strict Mode)**:
|
|
600
|
+
- 🌟 Excellent: < 200 tokens
|
|
601
|
+
- ✅ Good: < 400 tokens
|
|
602
|
+
- ⚠️ Needs improvement: < 600 tokens
|
|
603
|
+
- ❌ Too long: > 1000 tokens
|
|
604
|
+
|
|
443
605
|
## 🚀 What's Next
|
|
444
606
|
|
|
445
607
|
We're actively working on new features:
|
|
@@ -472,4 +634,4 @@ This project is licensed under the MIT License - see the [LICENSE](LICENSE) file
|
|
|
472
634
|
|
|
473
635
|
---
|
|
474
636
|
|
|
475
|
-
**Status:** ✅ Published | **Version:** 0.
|
|
637
|
+
**Status:** ✅ Published | **Version:** 0.3.0 | **Last Updated:** 2025-11-08
|
package/dist/index.js
CHANGED
|
@@ -305,29 +305,56 @@ async function getContextForFile(input) {
|
|
|
305
305
|
let promptText = `## \u{1F4DA} Project Context for: ${filePath}
|
|
306
306
|
|
|
307
307
|
`;
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
promptText += `### Root Conventions (${ctx.path})
|
|
308
|
+
if (contexts.length === 0) {
|
|
309
|
+
promptText += `\u26A0\uFE0F **No AI_README.md files found in this project.**
|
|
311
310
|
|
|
312
311
|
`;
|
|
313
|
-
|
|
314
|
-
|
|
312
|
+
promptText += `To get the most out of AI assistance, consider creating an AI_README.md file to document:
|
|
313
|
+
`;
|
|
314
|
+
promptText += `- Project architecture and conventions
|
|
315
|
+
`;
|
|
316
|
+
promptText += `- Coding standards and best practices
|
|
317
|
+
`;
|
|
318
|
+
promptText += `- Testing requirements
|
|
319
|
+
`;
|
|
320
|
+
promptText += `- Common patterns to follow
|
|
315
321
|
|
|
316
322
|
`;
|
|
317
|
-
|
|
318
|
-
|
|
323
|
+
promptText += `**Quick Start:**
|
|
324
|
+
`;
|
|
325
|
+
promptText += `Use the \`init_ai_readme\` tool to create a template:
|
|
326
|
+
`;
|
|
327
|
+
promptText += `- Creates AI_README.md from a customizable template
|
|
328
|
+
`;
|
|
329
|
+
promptText += `- Helps maintain consistency across your team
|
|
330
|
+
`;
|
|
331
|
+
promptText += `- Improves AI output quality
|
|
332
|
+
`;
|
|
333
|
+
} else {
|
|
334
|
+
for (const ctx of formattedContexts) {
|
|
335
|
+
if (ctx.relevance === "root") {
|
|
336
|
+
promptText += `### Root Conventions (${ctx.path})
|
|
319
337
|
|
|
320
338
|
`;
|
|
339
|
+
} else if (ctx.relevance === "direct") {
|
|
340
|
+
promptText += `### Direct Module Conventions (${ctx.path})
|
|
341
|
+
|
|
342
|
+
`;
|
|
343
|
+
} else {
|
|
344
|
+
promptText += `### Parent Module Conventions (${ctx.path})
|
|
345
|
+
|
|
346
|
+
`;
|
|
347
|
+
}
|
|
348
|
+
promptText += ctx.content + "\n\n";
|
|
321
349
|
}
|
|
322
|
-
promptText +=
|
|
323
|
-
}
|
|
324
|
-
promptText += `---
|
|
350
|
+
promptText += `---
|
|
325
351
|
**Important Reminders:**
|
|
326
352
|
`;
|
|
327
|
-
|
|
353
|
+
promptText += `- Follow the above conventions when making changes
|
|
328
354
|
`;
|
|
329
|
-
|
|
355
|
+
promptText += `- If your changes affect architecture or conventions, consider updating the relevant AI_README
|
|
330
356
|
`;
|
|
357
|
+
}
|
|
331
358
|
return {
|
|
332
359
|
filePath,
|
|
333
360
|
totalContexts: contexts.length,
|
|
@@ -883,53 +910,199 @@ async function validateAIReadmes(input) {
|
|
|
883
910
|
|
|
884
911
|
// src/tools/init.ts
|
|
885
912
|
import { z as z5 } from "zod";
|
|
886
|
-
import { readFile as
|
|
887
|
-
import { join as
|
|
888
|
-
import { existsSync as
|
|
913
|
+
import { readFile as readFile6, writeFile as writeFile2 } from "fs/promises";
|
|
914
|
+
import { join as join5, dirname as dirname4, relative } from "path";
|
|
915
|
+
import { existsSync as existsSync4 } from "fs";
|
|
889
916
|
import { fileURLToPath } from "url";
|
|
917
|
+
|
|
918
|
+
// src/core/detector.ts
|
|
919
|
+
import { readFile as readFile5, readdir, stat } from "fs/promises";
|
|
920
|
+
import { join as join4 } from "path";
|
|
921
|
+
import { existsSync as existsSync3 } from "fs";
|
|
922
|
+
var ProjectDetector = class {
|
|
923
|
+
constructor(targetPath) {
|
|
924
|
+
this.targetPath = targetPath;
|
|
925
|
+
}
|
|
926
|
+
/**
|
|
927
|
+
* Detect project information by analyzing files and structure
|
|
928
|
+
*/
|
|
929
|
+
async detect() {
|
|
930
|
+
const info = {
|
|
931
|
+
projectName: "Project",
|
|
932
|
+
projectType: "unknown",
|
|
933
|
+
language: "JavaScript",
|
|
934
|
+
hasTests: false,
|
|
935
|
+
mainDirs: []
|
|
936
|
+
};
|
|
937
|
+
const packageJsonPath = join4(this.targetPath, "package.json");
|
|
938
|
+
if (existsSync3(packageJsonPath)) {
|
|
939
|
+
await this.analyzePackageJson(packageJsonPath, info);
|
|
940
|
+
}
|
|
941
|
+
if (existsSync3(join4(this.targetPath, "requirements.txt")) || existsSync3(join4(this.targetPath, "setup.py")) || existsSync3(join4(this.targetPath, "pyproject.toml"))) {
|
|
942
|
+
info.language = "Python";
|
|
943
|
+
}
|
|
944
|
+
if (existsSync3(join4(this.targetPath, "go.mod"))) {
|
|
945
|
+
info.language = "Go";
|
|
946
|
+
}
|
|
947
|
+
if (existsSync3(join4(this.targetPath, "Cargo.toml"))) {
|
|
948
|
+
info.language = "Rust";
|
|
949
|
+
}
|
|
950
|
+
if (existsSync3(join4(this.targetPath, "pom.xml")) || existsSync3(join4(this.targetPath, "build.gradle"))) {
|
|
951
|
+
info.language = "Java";
|
|
952
|
+
}
|
|
953
|
+
await this.analyzeStructure(info);
|
|
954
|
+
return info;
|
|
955
|
+
}
|
|
956
|
+
/**
|
|
957
|
+
* Analyze package.json to extract project information
|
|
958
|
+
*/
|
|
959
|
+
async analyzePackageJson(path, info) {
|
|
960
|
+
try {
|
|
961
|
+
const content = await readFile5(path, "utf-8");
|
|
962
|
+
const pkg = JSON.parse(content);
|
|
963
|
+
if (pkg.name) {
|
|
964
|
+
info.projectName = pkg.name;
|
|
965
|
+
}
|
|
966
|
+
if (pkg.devDependencies?.typescript || pkg.dependencies?.typescript) {
|
|
967
|
+
info.language = "TypeScript";
|
|
968
|
+
}
|
|
969
|
+
if (pkg.dependencies?.react || pkg.devDependencies?.react) {
|
|
970
|
+
info.framework = "React";
|
|
971
|
+
} else if (pkg.dependencies?.vue || pkg.devDependencies?.vue) {
|
|
972
|
+
info.framework = "Vue";
|
|
973
|
+
} else if (pkg.dependencies?.["@angular/core"]) {
|
|
974
|
+
info.framework = "Angular";
|
|
975
|
+
} else if (pkg.dependencies?.next) {
|
|
976
|
+
info.framework = "Next.js";
|
|
977
|
+
} else if (pkg.dependencies?.express) {
|
|
978
|
+
info.framework = "Express";
|
|
979
|
+
} else if (pkg.dependencies?.nestjs || pkg.dependencies?.["@nestjs/core"]) {
|
|
980
|
+
info.framework = "NestJS";
|
|
981
|
+
}
|
|
982
|
+
const lockFiles = await readdir(this.targetPath);
|
|
983
|
+
if (lockFiles.includes("pnpm-lock.yaml")) {
|
|
984
|
+
info.packageManager = "pnpm";
|
|
985
|
+
} else if (lockFiles.includes("yarn.lock")) {
|
|
986
|
+
info.packageManager = "yarn";
|
|
987
|
+
} else if (lockFiles.includes("bun.lockb")) {
|
|
988
|
+
info.packageManager = "bun";
|
|
989
|
+
} else if (lockFiles.includes("package-lock.json")) {
|
|
990
|
+
info.packageManager = "npm";
|
|
991
|
+
}
|
|
992
|
+
if (pkg.workspaces || existsSync3(join4(this.targetPath, "pnpm-workspace.yaml"))) {
|
|
993
|
+
info.projectType = "monorepo";
|
|
994
|
+
}
|
|
995
|
+
if (!info.projectType || info.projectType === "unknown") {
|
|
996
|
+
if (pkg.main || pkg.exports) {
|
|
997
|
+
info.projectType = "library";
|
|
998
|
+
} else {
|
|
999
|
+
info.projectType = "application";
|
|
1000
|
+
}
|
|
1001
|
+
}
|
|
1002
|
+
if (pkg.dependencies) {
|
|
1003
|
+
info.dependencies = Object.keys(pkg.dependencies).slice(0, 5);
|
|
1004
|
+
}
|
|
1005
|
+
} catch (error) {
|
|
1006
|
+
}
|
|
1007
|
+
}
|
|
1008
|
+
/**
|
|
1009
|
+
* Analyze directory structure
|
|
1010
|
+
*/
|
|
1011
|
+
async analyzeStructure(info) {
|
|
1012
|
+
try {
|
|
1013
|
+
const entries = await readdir(this.targetPath);
|
|
1014
|
+
const dirs = [];
|
|
1015
|
+
for (const entry of entries) {
|
|
1016
|
+
try {
|
|
1017
|
+
const entryPath = join4(this.targetPath, entry);
|
|
1018
|
+
const stats = await stat(entryPath);
|
|
1019
|
+
if (stats.isDirectory()) {
|
|
1020
|
+
if (["src", "lib", "app", "pages", "components", "api", "server", "client"].includes(entry)) {
|
|
1021
|
+
dirs.push(entry);
|
|
1022
|
+
}
|
|
1023
|
+
if (["test", "tests", "__tests__", "spec"].includes(entry)) {
|
|
1024
|
+
info.hasTests = true;
|
|
1025
|
+
}
|
|
1026
|
+
if (["apps", "packages", "modules"].includes(entry)) {
|
|
1027
|
+
info.projectType = "monorepo";
|
|
1028
|
+
dirs.push(entry);
|
|
1029
|
+
}
|
|
1030
|
+
}
|
|
1031
|
+
} catch {
|
|
1032
|
+
}
|
|
1033
|
+
}
|
|
1034
|
+
info.mainDirs = dirs;
|
|
1035
|
+
} catch (error) {
|
|
1036
|
+
}
|
|
1037
|
+
}
|
|
1038
|
+
};
|
|
1039
|
+
|
|
1040
|
+
// src/tools/init.ts
|
|
890
1041
|
var initSchema = z5.object({
|
|
891
1042
|
targetPath: z5.string().describe("Directory where AI_README.md will be created"),
|
|
892
1043
|
projectName: z5.string().optional().describe("Project name to use in the template (optional)"),
|
|
893
|
-
overwrite: z5.boolean().optional().describe("Whether to overwrite existing AI_README.md (default: false)")
|
|
1044
|
+
overwrite: z5.boolean().optional().describe("Whether to overwrite existing AI_README.md (default: false)"),
|
|
1045
|
+
smart: z5.boolean().optional().describe("Enable smart content generation based on project analysis (default: true)")
|
|
894
1046
|
});
|
|
895
1047
|
async function initAIReadme(input) {
|
|
896
1048
|
const { targetPath, projectName, overwrite = false } = input;
|
|
1049
|
+
const smart = input.smart !== false;
|
|
1050
|
+
console.error(`[DEBUG] init_ai_readme: smart=${smart}, input.smart=${input.smart}`);
|
|
897
1051
|
try {
|
|
898
|
-
if (!
|
|
1052
|
+
if (!existsSync4(targetPath)) {
|
|
899
1053
|
return {
|
|
900
1054
|
success: false,
|
|
901
1055
|
error: `Target directory does not exist: ${targetPath}`,
|
|
902
1056
|
message: `Failed to create AI_README.md: Directory not found`
|
|
903
1057
|
};
|
|
904
1058
|
}
|
|
905
|
-
const readmePath =
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
1059
|
+
const readmePath = join5(targetPath, "AI_README.md");
|
|
1060
|
+
const fileExists = existsSync4(readmePath);
|
|
1061
|
+
let isEmpty = false;
|
|
1062
|
+
if (fileExists) {
|
|
1063
|
+
const existingContent = await readFile6(readmePath, "utf-8");
|
|
1064
|
+
isEmpty = existingContent.trim().length < 50;
|
|
1065
|
+
if (!isEmpty && !overwrite) {
|
|
1066
|
+
return {
|
|
1067
|
+
success: false,
|
|
1068
|
+
error: "AI_README.md already exists with content",
|
|
1069
|
+
message: `AI_README.md already exists at ${readmePath}. Use overwrite: true to replace it.`,
|
|
1070
|
+
existingPath: readmePath
|
|
1071
|
+
};
|
|
1072
|
+
}
|
|
913
1073
|
}
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
1074
|
+
let content;
|
|
1075
|
+
let detectedInfo = {};
|
|
1076
|
+
if (smart) {
|
|
1077
|
+
const detector = new ProjectDetector(targetPath);
|
|
1078
|
+
const projectInfo = await detector.detect();
|
|
1079
|
+
detectedInfo = projectInfo;
|
|
1080
|
+
const parentReadme = await findParentReadme(targetPath);
|
|
1081
|
+
const isSubdirectory = parentReadme !== null;
|
|
1082
|
+
content = await generateSmartContent(projectInfo, targetPath, isSubdirectory, parentReadme);
|
|
1083
|
+
} else {
|
|
1084
|
+
const __filename2 = fileURLToPath(import.meta.url);
|
|
1085
|
+
const __dirname2 = dirname4(__filename2);
|
|
1086
|
+
const templatePath = join5(__dirname2, "..", "..", "docs", "templates", "basic.md");
|
|
1087
|
+
if (!existsSync4(templatePath)) {
|
|
1088
|
+
return {
|
|
1089
|
+
success: false,
|
|
1090
|
+
error: `Template file not found: ${templatePath}`,
|
|
1091
|
+
message: "Template file is missing. Please check installation."
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
content = await readFile6(templatePath, "utf-8");
|
|
1095
|
+
const finalProjectName = projectName || "Project Name";
|
|
1096
|
+
content = content.replace(/\{\{PROJECT_NAME\}\}/g, finalProjectName);
|
|
923
1097
|
}
|
|
924
|
-
let content = await readFile5(templatePath, "utf-8");
|
|
925
|
-
const finalProjectName = projectName || "Project Name";
|
|
926
|
-
content = content.replace(/\{\{PROJECT_NAME\}\}/g, finalProjectName);
|
|
927
1098
|
await writeFile2(readmePath, content, "utf-8");
|
|
1099
|
+
const action = fileExists ? isEmpty ? "filled" : "overwritten" : "created";
|
|
928
1100
|
return {
|
|
929
1101
|
success: true,
|
|
930
1102
|
readmePath,
|
|
931
|
-
|
|
932
|
-
|
|
1103
|
+
action,
|
|
1104
|
+
projectInfo: smart ? detectedInfo : void 0,
|
|
1105
|
+
message: `Successfully ${action} AI_README.md at ${readmePath}. ${smart ? "Content generated based on project analysis." : "Edit the file to customize it for your project."}`
|
|
933
1106
|
};
|
|
934
1107
|
} catch (error) {
|
|
935
1108
|
return {
|
|
@@ -939,6 +1112,152 @@ async function initAIReadme(input) {
|
|
|
939
1112
|
};
|
|
940
1113
|
}
|
|
941
1114
|
}
|
|
1115
|
+
async function findParentReadme(targetPath) {
|
|
1116
|
+
let currentPath = dirname4(targetPath);
|
|
1117
|
+
const root = "/";
|
|
1118
|
+
while (currentPath !== root) {
|
|
1119
|
+
const readmePath = join5(currentPath, "AI_README.md");
|
|
1120
|
+
if (existsSync4(readmePath)) {
|
|
1121
|
+
const content = await readFile6(readmePath, "utf-8");
|
|
1122
|
+
if (content.trim().length > 50) {
|
|
1123
|
+
return currentPath;
|
|
1124
|
+
}
|
|
1125
|
+
}
|
|
1126
|
+
const parentPath = dirname4(currentPath);
|
|
1127
|
+
if (parentPath === currentPath) break;
|
|
1128
|
+
currentPath = parentPath;
|
|
1129
|
+
}
|
|
1130
|
+
return null;
|
|
1131
|
+
}
|
|
1132
|
+
async function generateSmartContent(projectInfo, targetPath, isSubdirectory, parentPath) {
|
|
1133
|
+
let content = `# ${projectInfo.projectName}
|
|
1134
|
+
|
|
1135
|
+
`;
|
|
1136
|
+
if (isSubdirectory && parentPath) {
|
|
1137
|
+
const relativePath = relative(parentPath, targetPath);
|
|
1138
|
+
content += `> This README extends the root AI_README.md with ${relativePath}-specific conventions.
|
|
1139
|
+
|
|
1140
|
+
`;
|
|
1141
|
+
}
|
|
1142
|
+
content += `## Architecture
|
|
1143
|
+
|
|
1144
|
+
`;
|
|
1145
|
+
content += `- **Type:** ${projectInfo.projectType}
|
|
1146
|
+
`;
|
|
1147
|
+
content += `- **Language:** ${projectInfo.language}
|
|
1148
|
+
`;
|
|
1149
|
+
if (projectInfo.framework) {
|
|
1150
|
+
content += `- **Framework:** ${projectInfo.framework}
|
|
1151
|
+
`;
|
|
1152
|
+
}
|
|
1153
|
+
content += `
|
|
1154
|
+
`;
|
|
1155
|
+
if (projectInfo.mainDirs && projectInfo.mainDirs.length > 0) {
|
|
1156
|
+
content += `## Directory Structure
|
|
1157
|
+
|
|
1158
|
+
`;
|
|
1159
|
+
for (const dir of projectInfo.mainDirs) {
|
|
1160
|
+
content += `- ${dir}/ - [Add description]
|
|
1161
|
+
`;
|
|
1162
|
+
}
|
|
1163
|
+
content += `
|
|
1164
|
+
`;
|
|
1165
|
+
}
|
|
1166
|
+
content += `## Coding Conventions
|
|
1167
|
+
|
|
1168
|
+
`;
|
|
1169
|
+
content += `### File Naming
|
|
1170
|
+
`;
|
|
1171
|
+
content += `- [Add your file naming conventions here]
|
|
1172
|
+
|
|
1173
|
+
`;
|
|
1174
|
+
content += `### Code Style
|
|
1175
|
+
`;
|
|
1176
|
+
if (projectInfo.language === "TypeScript") {
|
|
1177
|
+
content += `- Use TypeScript strict mode
|
|
1178
|
+
`;
|
|
1179
|
+
content += `- Prefer interfaces over types for object shapes
|
|
1180
|
+
`;
|
|
1181
|
+
} else if (projectInfo.language === "Python") {
|
|
1182
|
+
content += `- Follow PEP 8 style guide
|
|
1183
|
+
`;
|
|
1184
|
+
content += `- Use type hints
|
|
1185
|
+
`;
|
|
1186
|
+
}
|
|
1187
|
+
content += `- [Add more style guidelines]
|
|
1188
|
+
|
|
1189
|
+
`;
|
|
1190
|
+
if (projectInfo.framework) {
|
|
1191
|
+
content += `### ${projectInfo.framework} Conventions
|
|
1192
|
+
`;
|
|
1193
|
+
if (projectInfo.framework === "React") {
|
|
1194
|
+
content += `- Component naming: PascalCase
|
|
1195
|
+
`;
|
|
1196
|
+
content += `- Hooks naming: use prefix 'use'
|
|
1197
|
+
`;
|
|
1198
|
+
content += `- Prefer functional components
|
|
1199
|
+
`;
|
|
1200
|
+
} else if (projectInfo.framework === "Vue") {
|
|
1201
|
+
content += `- Component naming: PascalCase or kebab-case
|
|
1202
|
+
`;
|
|
1203
|
+
content += `- Use Composition API
|
|
1204
|
+
`;
|
|
1205
|
+
}
|
|
1206
|
+
content += `
|
|
1207
|
+
`;
|
|
1208
|
+
}
|
|
1209
|
+
content += `## Testing
|
|
1210
|
+
|
|
1211
|
+
`;
|
|
1212
|
+
if (projectInfo.hasTests) {
|
|
1213
|
+
content += `- Tests are located in test directories
|
|
1214
|
+
`;
|
|
1215
|
+
}
|
|
1216
|
+
if (projectInfo.packageManager) {
|
|
1217
|
+
content += `- Run: \`${projectInfo.packageManager} test\`
|
|
1218
|
+
`;
|
|
1219
|
+
} else {
|
|
1220
|
+
content += `- Run: [Add test command]
|
|
1221
|
+
`;
|
|
1222
|
+
}
|
|
1223
|
+
content += `- Coverage target: [Add target or "not enforced"]
|
|
1224
|
+
|
|
1225
|
+
`;
|
|
1226
|
+
if (projectInfo.dependencies && projectInfo.dependencies.length > 0) {
|
|
1227
|
+
content += `## Key Dependencies
|
|
1228
|
+
|
|
1229
|
+
`;
|
|
1230
|
+
for (const dep of projectInfo.dependencies) {
|
|
1231
|
+
content += `- ${dep} - [Add purpose]
|
|
1232
|
+
`;
|
|
1233
|
+
}
|
|
1234
|
+
content += `
|
|
1235
|
+
`;
|
|
1236
|
+
}
|
|
1237
|
+
content += `## Development
|
|
1238
|
+
|
|
1239
|
+
`;
|
|
1240
|
+
if (projectInfo.packageManager) {
|
|
1241
|
+
content += `- Install: \`${projectInfo.packageManager} install\`
|
|
1242
|
+
`;
|
|
1243
|
+
content += `- Dev: \`${projectInfo.packageManager} run dev\`
|
|
1244
|
+
`;
|
|
1245
|
+
content += `- Build: \`${projectInfo.packageManager} run build\`
|
|
1246
|
+
`;
|
|
1247
|
+
}
|
|
1248
|
+
content += `
|
|
1249
|
+
`;
|
|
1250
|
+
content += `## Important Notes
|
|
1251
|
+
|
|
1252
|
+
`;
|
|
1253
|
+
content += `- [Add critical information that AI should know]
|
|
1254
|
+
`;
|
|
1255
|
+
content += `- [Security considerations]
|
|
1256
|
+
`;
|
|
1257
|
+
content += `- [Performance considerations]
|
|
1258
|
+
`;
|
|
1259
|
+
return content;
|
|
1260
|
+
}
|
|
942
1261
|
|
|
943
1262
|
// src/index.ts
|
|
944
1263
|
var server = new Server(
|
|
@@ -962,12 +1281,12 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
962
1281
|
},
|
|
963
1282
|
{
|
|
964
1283
|
name: "get_context_for_file",
|
|
965
|
-
description: "Get relevant AI_README context for a specific file path. Returns formatted context from relevant README files to help understand project conventions.",
|
|
1284
|
+
description: "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 editing files to understand conventions.",
|
|
966
1285
|
inputSchema: zodToJsonSchema(getContextSchema)
|
|
967
1286
|
},
|
|
968
1287
|
{
|
|
969
1288
|
name: "update_ai_readme",
|
|
970
|
-
description: "Update an AI_README.md file with
|
|
1289
|
+
description: "Update an AI_README.md file when you discover important conventions, patterns, or rules while editing code. Use this to keep documentation in sync with code changes. Supports append, prepend, replace, insert-after, insert-before operations. Auto-validates after update.",
|
|
971
1290
|
inputSchema: zodToJsonSchema(updateSchema)
|
|
972
1291
|
},
|
|
973
1292
|
{
|
|
@@ -977,7 +1296,7 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
977
1296
|
},
|
|
978
1297
|
{
|
|
979
1298
|
name: "init_ai_readme",
|
|
980
|
-
description: "Initialize
|
|
1299
|
+
description: "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.",
|
|
981
1300
|
inputSchema: zodToJsonSchema(initSchema)
|
|
982
1301
|
}
|
|
983
1302
|
]
|
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"],"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.1.0',\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.',\n inputSchema: zodToJsonSchema(getContextSchema),\n },\n {\n name: 'update_ai_readme',\n description:\n 'Update an AI_README.md file with specified operations (append, prepend, replace, insert-after, insert-before). Auto-validates after update. Changes are written directly; use Git for version control.',\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 a new AI_README.md file from a template. Creates a basic structure with common sections to help you get started quickly.',\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 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**Important Reminders:**\\n`;\n promptText += `- Follow the above conventions when making changes\\n`;\n promptText += `- If your changes affect architecture or conventions, consider updating the relevant AI_README\\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 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: 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 } from 'path';\nimport { existsSync } from 'fs';\nimport { fileURLToPath } from 'url';\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});\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\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 already exists\n if (existsSync(readmePath) && !overwrite) {\n return {\n success: false,\n error: 'AI_README.md already exists',\n message: `AI_README.md already exists at ${readmePath}. Use overwrite: true to replace it.`,\n existingPath: readmePath,\n };\n }\n\n // Get template path\n // In production (dist/), template is at ../../docs/templates/basic.md\n // In development (src/), template is at ../../docs/templates/basic.md\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 // Read template\n let content = await readFile(templatePath, 'utf-8');\n\n // Replace placeholders\n const finalProjectName = projectName || 'Project Name';\n content = content.replace(/\\{\\{PROJECT_NAME\\}\\}/g, finalProjectName);\n\n // Write to target\n await writeFile(readmePath, content, 'utf-8');\n\n return {\n success: true,\n readmePath,\n projectName: finalProjectName,\n message: `Successfully created AI_README.md at ${readmePath}. 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"],"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","__filename","__dirname"]}
|
|
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.1.0',\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 editing files to understand conventions.',\n inputSchema: zodToJsonSchema(getContextSchema),\n },\n {\n name: 'update_ai_readme',\n description:\n 'Update an AI_README.md file when you discover important conventions, patterns, or rules while editing code. Use this to keep documentation in sync with code changes. Supports append, prepend, replace, insert-after, insert-before operations. Auto-validates after update.',\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**Important Reminders:**\\n`;\n promptText += `- Follow the above conventions when making changes\\n`;\n promptText += `- If your changes affect architecture or conventions, consider updating the relevant AI_README\\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 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: 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"]}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
# {{PROJECT_NAME}}
|
|
2
|
+
|
|
3
|
+
Brief description of this module or component.
|
|
4
|
+
|
|
5
|
+
## Architecture
|
|
6
|
+
|
|
7
|
+
- **Type:** [Library/Service/Application/Component]
|
|
8
|
+
- **Language:** [TypeScript/JavaScript/Python/Java/Go/Rust/etc]
|
|
9
|
+
- **Framework:** [React/Vue/Django/Spring/Gin/etc]
|
|
10
|
+
|
|
11
|
+
## Directory Structure
|
|
12
|
+
|
|
13
|
+
- Main entry: [path]
|
|
14
|
+
- Core logic: [path]
|
|
15
|
+
- Utilities: [path]
|
|
16
|
+
|
|
17
|
+
## Coding Conventions
|
|
18
|
+
|
|
19
|
+
### File Naming
|
|
20
|
+
- [snake_case/camelCase/PascalCase/kebab-case]
|
|
21
|
+
|
|
22
|
+
### Code Style
|
|
23
|
+
- Formatting: [Prettier/Black/gofmt/rustfmt/etc]
|
|
24
|
+
- Linting: [ESLint/Pylint/golangci-lint/Clippy/etc]
|
|
25
|
+
|
|
26
|
+
### Important Patterns
|
|
27
|
+
- Key architectural patterns or design decisions
|
|
28
|
+
- Project-specific best practices
|
|
29
|
+
- Common patterns to follow
|
|
30
|
+
|
|
31
|
+
## Testing
|
|
32
|
+
|
|
33
|
+
- **Framework:** [Jest/Pytest/JUnit/Go testing/etc]
|
|
34
|
+
- **Run Command:** [command]
|
|
35
|
+
- **Coverage Target:** [percentage or "not enforced"]
|
|
36
|
+
|
|
37
|
+
## Dependencies
|
|
38
|
+
|
|
39
|
+
### Key Libraries
|
|
40
|
+
- [Library name] - Purpose and why used
|
|
41
|
+
- [Library name] - Purpose and why used
|
|
42
|
+
|
|
43
|
+
## Development Workflow
|
|
44
|
+
|
|
45
|
+
- Installation: [command]
|
|
46
|
+
- Dev mode: [command]
|
|
47
|
+
- Build: [command]
|
|
48
|
+
- Run: [command]
|
|
49
|
+
|
|
50
|
+
## Important Notes
|
|
51
|
+
|
|
52
|
+
- Critical information AI should know
|
|
53
|
+
- Security considerations
|
|
54
|
+
- Performance considerations
|
|
55
|
+
- Known limitations or gotchas
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "ai-readme-mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.3.0",
|
|
4
4
|
"description": "MCP server for managing AI_README.md files in projects",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"bin": {
|
|
@@ -9,7 +9,8 @@
|
|
|
9
9
|
"main": "./dist/index.js",
|
|
10
10
|
"types": "./dist/index.d.ts",
|
|
11
11
|
"files": [
|
|
12
|
-
"dist"
|
|
12
|
+
"dist",
|
|
13
|
+
"docs/templates"
|
|
13
14
|
],
|
|
14
15
|
"scripts": {
|
|
15
16
|
"dev": "tsup --watch",
|