ai-readme-mcp 0.3.3 → 0.4.1
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 +124 -80
- package/dist/index.js +141 -328
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/docs/templates/basic.md +0 -55
package/dist/index.js
CHANGED
|
@@ -296,6 +296,8 @@ async function getContextForFile(input) {
|
|
|
296
296
|
const index = await scanner.scan();
|
|
297
297
|
const router = new ContextRouter(index);
|
|
298
298
|
const contexts = await router.getContextForFile(filePath, includeRoot);
|
|
299
|
+
const hasEmptyReadmes = contexts.some((ctx) => !ctx.content || ctx.content.trim().length === 0);
|
|
300
|
+
const hasNoReadmes = contexts.length === 0;
|
|
299
301
|
const formattedContexts = contexts.map((ctx) => ({
|
|
300
302
|
path: ctx.path,
|
|
301
303
|
relevance: ctx.relevance,
|
|
@@ -305,33 +307,39 @@ async function getContextForFile(input) {
|
|
|
305
307
|
let promptText = `## \u{1F4DA} Project Context for: ${filePath}
|
|
306
308
|
|
|
307
309
|
`;
|
|
308
|
-
if (
|
|
309
|
-
promptText += `\u26A0\uFE0F **
|
|
310
|
+
if (hasNoReadmes || hasEmptyReadmes) {
|
|
311
|
+
promptText += `\u26A0\uFE0F **Empty or missing AI_README files detected.**
|
|
310
312
|
|
|
311
313
|
`;
|
|
312
|
-
promptText +=
|
|
313
|
-
|
|
314
|
-
promptText += `- Project architecture and conventions
|
|
314
|
+
promptText += `**Recommended Action:** Use the \`init_ai_readme\` tool to automatically populate AI_README files.
|
|
315
|
+
|
|
315
316
|
`;
|
|
316
|
-
promptText +=
|
|
317
|
+
promptText += `\`\`\`
|
|
317
318
|
`;
|
|
318
|
-
promptText +=
|
|
319
|
+
promptText += `init_ai_readme({ projectRoot: "${projectRoot.replace(/\\/g, "/")}" })
|
|
319
320
|
`;
|
|
320
|
-
promptText +=
|
|
321
|
+
promptText += `\`\`\`
|
|
321
322
|
|
|
322
323
|
`;
|
|
323
|
-
promptText +=
|
|
324
|
+
promptText += `This tool will:
|
|
324
325
|
`;
|
|
325
|
-
promptText +=
|
|
326
|
+
promptText += `- Scan the project for empty AI_README files
|
|
326
327
|
`;
|
|
327
|
-
promptText += `-
|
|
328
|
+
promptText += `- Guide you through populating them with conventions
|
|
328
329
|
`;
|
|
329
|
-
promptText += `-
|
|
330
|
+
promptText += `- Ensure consistent documentation across your project
|
|
331
|
+
|
|
330
332
|
`;
|
|
331
|
-
promptText +=
|
|
333
|
+
promptText += `\u{1F4A1} Alternatively, you can manually create and populate AI_README.md files, then call this tool again.
|
|
334
|
+
|
|
335
|
+
`;
|
|
336
|
+
promptText += `---
|
|
337
|
+
|
|
332
338
|
`;
|
|
333
|
-
}
|
|
334
|
-
|
|
339
|
+
}
|
|
340
|
+
const nonEmptyContexts = contexts.filter((ctx) => ctx.content && ctx.content.trim().length > 0);
|
|
341
|
+
if (nonEmptyContexts.length > 0) {
|
|
342
|
+
for (const ctx of nonEmptyContexts) {
|
|
335
343
|
if (ctx.relevance === "root") {
|
|
336
344
|
promptText += `### Root Conventions (${ctx.path})
|
|
337
345
|
|
|
@@ -385,14 +393,10 @@ var ReadmeUpdater = class {
|
|
|
385
393
|
*/
|
|
386
394
|
async update(readmePath, operations) {
|
|
387
395
|
try {
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
error: `File not found: ${readmePath}`,
|
|
392
|
-
changes: []
|
|
393
|
-
};
|
|
396
|
+
let content = "";
|
|
397
|
+
if (existsSync(readmePath)) {
|
|
398
|
+
content = await readFile3(readmePath, "utf-8");
|
|
394
399
|
}
|
|
395
|
-
const content = await readFile3(readmePath, "utf-8");
|
|
396
400
|
let updatedContent = content;
|
|
397
401
|
const changes = [];
|
|
398
402
|
for (const operation of operations) {
|
|
@@ -916,353 +920,180 @@ async function validateAIReadmes(input) {
|
|
|
916
920
|
|
|
917
921
|
// src/tools/init.ts
|
|
918
922
|
import { z as z5 } from "zod";
|
|
919
|
-
import {
|
|
920
|
-
import { join as join5, dirname as dirname4, relative } from "path";
|
|
921
|
-
import { existsSync as existsSync4 } from "fs";
|
|
922
|
-
import { fileURLToPath } from "url";
|
|
923
|
-
|
|
924
|
-
// src/core/detector.ts
|
|
925
|
-
import { readFile as readFile5, readdir, stat } from "fs/promises";
|
|
923
|
+
import { writeFile as writeFile2 } from "fs/promises";
|
|
926
924
|
import { join as join4 } from "path";
|
|
927
925
|
import { existsSync as existsSync3 } from "fs";
|
|
928
|
-
var ProjectDetector = class {
|
|
929
|
-
constructor(targetPath) {
|
|
930
|
-
this.targetPath = targetPath;
|
|
931
|
-
}
|
|
932
|
-
/**
|
|
933
|
-
* Detect project information by analyzing files and structure
|
|
934
|
-
*/
|
|
935
|
-
async detect() {
|
|
936
|
-
const info = {
|
|
937
|
-
projectName: "Project",
|
|
938
|
-
projectType: "unknown",
|
|
939
|
-
language: "JavaScript",
|
|
940
|
-
hasTests: false,
|
|
941
|
-
mainDirs: []
|
|
942
|
-
};
|
|
943
|
-
const packageJsonPath = join4(this.targetPath, "package.json");
|
|
944
|
-
if (existsSync3(packageJsonPath)) {
|
|
945
|
-
await this.analyzePackageJson(packageJsonPath, info);
|
|
946
|
-
}
|
|
947
|
-
if (existsSync3(join4(this.targetPath, "requirements.txt")) || existsSync3(join4(this.targetPath, "setup.py")) || existsSync3(join4(this.targetPath, "pyproject.toml"))) {
|
|
948
|
-
info.language = "Python";
|
|
949
|
-
}
|
|
950
|
-
if (existsSync3(join4(this.targetPath, "go.mod"))) {
|
|
951
|
-
info.language = "Go";
|
|
952
|
-
}
|
|
953
|
-
if (existsSync3(join4(this.targetPath, "Cargo.toml"))) {
|
|
954
|
-
info.language = "Rust";
|
|
955
|
-
}
|
|
956
|
-
if (existsSync3(join4(this.targetPath, "pom.xml")) || existsSync3(join4(this.targetPath, "build.gradle"))) {
|
|
957
|
-
info.language = "Java";
|
|
958
|
-
}
|
|
959
|
-
await this.analyzeStructure(info);
|
|
960
|
-
return info;
|
|
961
|
-
}
|
|
962
|
-
/**
|
|
963
|
-
* Analyze package.json to extract project information
|
|
964
|
-
*/
|
|
965
|
-
async analyzePackageJson(path, info) {
|
|
966
|
-
try {
|
|
967
|
-
const content = await readFile5(path, "utf-8");
|
|
968
|
-
const pkg = JSON.parse(content);
|
|
969
|
-
if (pkg.name) {
|
|
970
|
-
info.projectName = pkg.name;
|
|
971
|
-
}
|
|
972
|
-
if (pkg.devDependencies?.typescript || pkg.dependencies?.typescript) {
|
|
973
|
-
info.language = "TypeScript";
|
|
974
|
-
}
|
|
975
|
-
if (pkg.dependencies?.react || pkg.devDependencies?.react) {
|
|
976
|
-
info.framework = "React";
|
|
977
|
-
} else if (pkg.dependencies?.vue || pkg.devDependencies?.vue) {
|
|
978
|
-
info.framework = "Vue";
|
|
979
|
-
} else if (pkg.dependencies?.["@angular/core"]) {
|
|
980
|
-
info.framework = "Angular";
|
|
981
|
-
} else if (pkg.dependencies?.next) {
|
|
982
|
-
info.framework = "Next.js";
|
|
983
|
-
} else if (pkg.dependencies?.express) {
|
|
984
|
-
info.framework = "Express";
|
|
985
|
-
} else if (pkg.dependencies?.nestjs || pkg.dependencies?.["@nestjs/core"]) {
|
|
986
|
-
info.framework = "NestJS";
|
|
987
|
-
}
|
|
988
|
-
const lockFiles = await readdir(this.targetPath);
|
|
989
|
-
if (lockFiles.includes("pnpm-lock.yaml")) {
|
|
990
|
-
info.packageManager = "pnpm";
|
|
991
|
-
} else if (lockFiles.includes("yarn.lock")) {
|
|
992
|
-
info.packageManager = "yarn";
|
|
993
|
-
} else if (lockFiles.includes("bun.lockb")) {
|
|
994
|
-
info.packageManager = "bun";
|
|
995
|
-
} else if (lockFiles.includes("package-lock.json")) {
|
|
996
|
-
info.packageManager = "npm";
|
|
997
|
-
}
|
|
998
|
-
if (pkg.workspaces || existsSync3(join4(this.targetPath, "pnpm-workspace.yaml"))) {
|
|
999
|
-
info.projectType = "monorepo";
|
|
1000
|
-
}
|
|
1001
|
-
if (!info.projectType || info.projectType === "unknown") {
|
|
1002
|
-
if (pkg.main || pkg.exports) {
|
|
1003
|
-
info.projectType = "library";
|
|
1004
|
-
} else {
|
|
1005
|
-
info.projectType = "application";
|
|
1006
|
-
}
|
|
1007
|
-
}
|
|
1008
|
-
if (pkg.dependencies) {
|
|
1009
|
-
info.dependencies = Object.keys(pkg.dependencies).slice(0, 5);
|
|
1010
|
-
}
|
|
1011
|
-
} catch (error) {
|
|
1012
|
-
}
|
|
1013
|
-
}
|
|
1014
|
-
/**
|
|
1015
|
-
* Analyze directory structure
|
|
1016
|
-
*/
|
|
1017
|
-
async analyzeStructure(info) {
|
|
1018
|
-
try {
|
|
1019
|
-
const entries = await readdir(this.targetPath);
|
|
1020
|
-
const dirs = [];
|
|
1021
|
-
for (const entry of entries) {
|
|
1022
|
-
try {
|
|
1023
|
-
const entryPath = join4(this.targetPath, entry);
|
|
1024
|
-
const stats = await stat(entryPath);
|
|
1025
|
-
if (stats.isDirectory()) {
|
|
1026
|
-
if (["src", "lib", "app", "pages", "components", "api", "server", "client"].includes(entry)) {
|
|
1027
|
-
dirs.push(entry);
|
|
1028
|
-
}
|
|
1029
|
-
if (["test", "tests", "__tests__", "spec"].includes(entry)) {
|
|
1030
|
-
info.hasTests = true;
|
|
1031
|
-
}
|
|
1032
|
-
if (["apps", "packages", "modules"].includes(entry)) {
|
|
1033
|
-
info.projectType = "monorepo";
|
|
1034
|
-
dirs.push(entry);
|
|
1035
|
-
}
|
|
1036
|
-
}
|
|
1037
|
-
} catch {
|
|
1038
|
-
}
|
|
1039
|
-
}
|
|
1040
|
-
info.mainDirs = dirs;
|
|
1041
|
-
} catch (error) {
|
|
1042
|
-
}
|
|
1043
|
-
}
|
|
1044
|
-
};
|
|
1045
|
-
|
|
1046
|
-
// src/tools/init.ts
|
|
1047
926
|
var initSchema = z5.object({
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
smart: z5.boolean().optional().describe("Enable smart content generation based on project analysis (default: true)")
|
|
927
|
+
projectRoot: z5.string().describe("The root directory of the project"),
|
|
928
|
+
excludePatterns: z5.array(z5.string()).optional().describe("Glob patterns to exclude when scanning"),
|
|
929
|
+
targetPath: z5.string().optional().describe("Specific directory to initialize (optional, defaults to scanning entire project)")
|
|
1052
930
|
});
|
|
1053
931
|
async function initAIReadme(input) {
|
|
1054
|
-
const {
|
|
1055
|
-
const
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
if (fileExists) {
|
|
1069
|
-
const existingContent = await readFile6(readmePath, "utf-8");
|
|
1070
|
-
isEmpty = existingContent.trim().length < 50;
|
|
1071
|
-
if (!isEmpty && !overwrite) {
|
|
1072
|
-
return {
|
|
1073
|
-
success: false,
|
|
1074
|
-
error: "AI_README.md already exists with content",
|
|
1075
|
-
message: `AI_README.md already exists at ${readmePath}. Use overwrite: true to replace it.`,
|
|
1076
|
-
existingPath: readmePath
|
|
1077
|
-
};
|
|
1078
|
-
}
|
|
932
|
+
const { projectRoot, excludePatterns, targetPath } = input;
|
|
933
|
+
const scanner = new AIReadmeScanner(projectRoot, {
|
|
934
|
+
excludePatterns,
|
|
935
|
+
cacheContent: true
|
|
936
|
+
});
|
|
937
|
+
const index = await scanner.scan();
|
|
938
|
+
const emptyReadmes = [];
|
|
939
|
+
for (const readme of index.readmes) {
|
|
940
|
+
if (!readme.content || readme.content.trim().length === 0) {
|
|
941
|
+
emptyReadmes.push({
|
|
942
|
+
path: readme.path,
|
|
943
|
+
dirPath: readme.path.replace(/[\/\\]AI_README\.md$/, ""),
|
|
944
|
+
needsCreation: false
|
|
945
|
+
});
|
|
1079
946
|
}
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
detectedInfo = projectInfo;
|
|
1086
|
-
const parentReadme = await findParentReadme(targetPath);
|
|
1087
|
-
const isSubdirectory = parentReadme !== null;
|
|
1088
|
-
content = await generateSmartContent(projectInfo, targetPath, isSubdirectory, parentReadme);
|
|
1089
|
-
} else {
|
|
1090
|
-
const __filename2 = fileURLToPath(import.meta.url);
|
|
1091
|
-
const __dirname2 = dirname4(__filename2);
|
|
1092
|
-
const templatePath = join5(__dirname2, "..", "..", "docs", "templates", "basic.md");
|
|
1093
|
-
if (!existsSync4(templatePath)) {
|
|
1094
|
-
return {
|
|
1095
|
-
success: false,
|
|
1096
|
-
error: `Template file not found: ${templatePath}`,
|
|
1097
|
-
message: "Template file is missing. Please check installation."
|
|
1098
|
-
};
|
|
1099
|
-
}
|
|
1100
|
-
content = await readFile6(templatePath, "utf-8");
|
|
1101
|
-
const finalProjectName = projectName || "Project Name";
|
|
1102
|
-
content = content.replace(/\{\{PROJECT_NAME\}\}/g, finalProjectName);
|
|
947
|
+
}
|
|
948
|
+
if (index.readmes.length === 0) {
|
|
949
|
+
const rootReadmePath = join4(projectRoot, "AI_README.md");
|
|
950
|
+
if (!existsSync3(rootReadmePath)) {
|
|
951
|
+
await writeFile2(rootReadmePath, "", "utf-8");
|
|
1103
952
|
}
|
|
1104
|
-
|
|
1105
|
-
|
|
953
|
+
emptyReadmes.push({
|
|
954
|
+
path: rootReadmePath,
|
|
955
|
+
dirPath: projectRoot,
|
|
956
|
+
needsCreation: true
|
|
957
|
+
});
|
|
958
|
+
}
|
|
959
|
+
let targetReadmes = emptyReadmes;
|
|
960
|
+
if (targetPath) {
|
|
961
|
+
const normalizedTarget = targetPath.replace(/\\/g, "/");
|
|
962
|
+
targetReadmes = emptyReadmes.filter(
|
|
963
|
+
(r) => r.dirPath.replace(/\\/g, "/").includes(normalizedTarget)
|
|
964
|
+
);
|
|
965
|
+
}
|
|
966
|
+
if (targetReadmes.length === 0) {
|
|
1106
967
|
return {
|
|
1107
968
|
success: true,
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
projectInfo: smart ? detectedInfo : void 0,
|
|
1111
|
-
message: `Successfully ${action} AI_README.md at ${readmePath}. ${smart ? "Content generated based on project analysis." : "Edit the file to customize it for your project."}`
|
|
1112
|
-
};
|
|
1113
|
-
} catch (error) {
|
|
1114
|
-
return {
|
|
1115
|
-
success: false,
|
|
1116
|
-
error: error instanceof Error ? error.message : String(error),
|
|
1117
|
-
message: `Failed to create AI_README.md: ${error instanceof Error ? error.message : String(error)}`
|
|
969
|
+
message: "\u2705 All AI_README files are already populated!",
|
|
970
|
+
initialized: []
|
|
1118
971
|
};
|
|
1119
972
|
}
|
|
1120
|
-
}
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
}
|
|
973
|
+
let promptText = `# \u{1F680} AI_README Initialization
|
|
974
|
+
|
|
975
|
+
`;
|
|
976
|
+
promptText += `Found ${targetReadmes.length} AI_README file(s) that need population.
|
|
977
|
+
|
|
978
|
+
`;
|
|
979
|
+
if (targetReadmes.some((r) => r.needsCreation)) {
|
|
980
|
+
promptText += `\u2705 Created empty AI_README file(s) at:
|
|
981
|
+
`;
|
|
982
|
+
for (const readme of targetReadmes.filter((r) => r.needsCreation)) {
|
|
983
|
+
promptText += `- ${readme.path.replace(/\\/g, "/")}
|
|
984
|
+
`;
|
|
1131
985
|
}
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
currentPath = parentPath;
|
|
986
|
+
promptText += `
|
|
987
|
+
`;
|
|
1135
988
|
}
|
|
1136
|
-
|
|
1137
|
-
}
|
|
1138
|
-
async function generateSmartContent(projectInfo, targetPath, isSubdirectory, parentPath) {
|
|
1139
|
-
let content = `# ${projectInfo.projectName}
|
|
989
|
+
promptText += `## \u{1F4CB} Required Actions
|
|
1140
990
|
|
|
1141
991
|
`;
|
|
1142
|
-
|
|
1143
|
-
const relativePath = relative(parentPath, targetPath);
|
|
1144
|
-
content += `> This README extends the root AI_README.md with ${relativePath}-specific conventions.
|
|
992
|
+
promptText += `You must populate the following AI_README files by analyzing their respective directories:
|
|
1145
993
|
|
|
1146
994
|
`;
|
|
1147
|
-
|
|
1148
|
-
|
|
995
|
+
for (let i = 0; i < targetReadmes.length; i++) {
|
|
996
|
+
const readme = targetReadmes[i];
|
|
997
|
+
const readmePath = readme.path.replace(/\\/g, "/");
|
|
998
|
+
const dirPath = readme.dirPath.replace(/\\/g, "/");
|
|
999
|
+
promptText += `### ${i + 1}. ${readmePath}
|
|
1149
1000
|
|
|
1150
1001
|
`;
|
|
1151
|
-
|
|
1002
|
+
promptText += `**Directory to analyze:** \`${dirPath}\`
|
|
1003
|
+
|
|
1152
1004
|
`;
|
|
1153
|
-
|
|
1005
|
+
promptText += `**Steps:**
|
|
1006
|
+
|
|
1154
1007
|
`;
|
|
1155
|
-
|
|
1156
|
-
content += `- **Framework:** ${projectInfo.framework}
|
|
1008
|
+
promptText += `1. **Scan directory contents:**
|
|
1157
1009
|
`;
|
|
1158
|
-
|
|
1159
|
-
|
|
1010
|
+
promptText += ` \`\`\`
|
|
1011
|
+
`;
|
|
1012
|
+
promptText += ` Use Glob: pattern="**/*", path="${dirPath}"
|
|
1160
1013
|
`;
|
|
1161
|
-
|
|
1162
|
-
content += `## Directory Structure
|
|
1014
|
+
promptText += ` \`\`\`
|
|
1163
1015
|
|
|
1164
1016
|
`;
|
|
1165
|
-
|
|
1166
|
-
content += `- ${dir}/ - [Add description]
|
|
1017
|
+
promptText += `2. **Read key source files** (pick 2-5 representative files):
|
|
1167
1018
|
`;
|
|
1168
|
-
|
|
1169
|
-
content += `
|
|
1019
|
+
promptText += ` - Configuration files (package.json, tsconfig.json, etc.)
|
|
1170
1020
|
`;
|
|
1171
|
-
|
|
1172
|
-
|
|
1021
|
+
promptText += ` - Main source files
|
|
1022
|
+
`;
|
|
1023
|
+
promptText += ` - Important modules/components
|
|
1173
1024
|
|
|
1174
1025
|
`;
|
|
1175
|
-
|
|
1026
|
+
promptText += `3. **Analyze and identify:**
|
|
1176
1027
|
`;
|
|
1177
|
-
|
|
1178
|
-
|
|
1028
|
+
promptText += ` - \u{1F4E6} **Tech Stack**: Frameworks, libraries, languages, tools
|
|
1179
1029
|
`;
|
|
1180
|
-
|
|
1030
|
+
promptText += ` - \u{1F3D7}\uFE0F **Architecture**: Project structure, design patterns
|
|
1181
1031
|
`;
|
|
1182
|
-
|
|
1183
|
-
content += `- Use TypeScript strict mode
|
|
1032
|
+
promptText += ` - \u{1F4DD} **Coding Conventions**: Naming, formatting, patterns
|
|
1184
1033
|
`;
|
|
1185
|
-
|
|
1034
|
+
promptText += ` - \u{1F5C2}\uFE0F **File Structure**: Directory organization, module boundaries
|
|
1035
|
+
|
|
1186
1036
|
`;
|
|
1187
|
-
|
|
1188
|
-
content += `- Follow PEP 8 style guide
|
|
1037
|
+
promptText += `4. **Populate AI_README:**
|
|
1189
1038
|
`;
|
|
1190
|
-
|
|
1039
|
+
promptText += ` \`\`\`
|
|
1191
1040
|
`;
|
|
1192
|
-
|
|
1193
|
-
content += `- [Add more style guidelines]
|
|
1194
|
-
|
|
1041
|
+
promptText += ` Use update_ai_readme:
|
|
1195
1042
|
`;
|
|
1196
|
-
|
|
1197
|
-
content += `### ${projectInfo.framework} Conventions
|
|
1043
|
+
promptText += ` {
|
|
1198
1044
|
`;
|
|
1199
|
-
|
|
1200
|
-
content += `- Component naming: PascalCase
|
|
1045
|
+
promptText += ` readmePath: "${readmePath}",
|
|
1201
1046
|
`;
|
|
1202
|
-
|
|
1047
|
+
promptText += ` operations: [{
|
|
1203
1048
|
`;
|
|
1204
|
-
|
|
1049
|
+
promptText += ` type: "append",
|
|
1205
1050
|
`;
|
|
1206
|
-
|
|
1207
|
-
content += `- Component naming: PascalCase or kebab-case
|
|
1051
|
+
promptText += ` content: "<your analysis in markdown format>"
|
|
1208
1052
|
`;
|
|
1209
|
-
|
|
1053
|
+
promptText += ` }]
|
|
1210
1054
|
`;
|
|
1211
|
-
}
|
|
1212
|
-
content += `
|
|
1055
|
+
promptText += ` }
|
|
1213
1056
|
`;
|
|
1214
|
-
|
|
1215
|
-
content += `## Testing
|
|
1057
|
+
promptText += ` \`\`\`
|
|
1216
1058
|
|
|
1217
1059
|
`;
|
|
1218
|
-
|
|
1219
|
-
content += `- Tests are located in test directories
|
|
1060
|
+
promptText += ` **Include these sections:**
|
|
1220
1061
|
`;
|
|
1221
|
-
|
|
1222
|
-
if (projectInfo.packageManager) {
|
|
1223
|
-
content += `- Run: \`${projectInfo.packageManager} test\`
|
|
1062
|
+
promptText += ` - \`## Tech Stack\` - List frameworks, libraries, tools
|
|
1224
1063
|
`;
|
|
1225
|
-
|
|
1226
|
-
content += `- Run: [Add test command]
|
|
1064
|
+
promptText += ` - \`## Architecture Patterns\` - Design patterns, project structure
|
|
1227
1065
|
`;
|
|
1228
|
-
|
|
1229
|
-
content += `- Coverage target: [Add target or "not enforced"]
|
|
1230
|
-
|
|
1066
|
+
promptText += ` - \`## Coding Conventions\` - Naming, formatting, best practices
|
|
1231
1067
|
`;
|
|
1232
|
-
|
|
1233
|
-
content += `## Key Dependencies
|
|
1068
|
+
promptText += ` - \`## File Structure\` - Directory organization (brief)
|
|
1234
1069
|
|
|
1235
1070
|
`;
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
`;
|
|
1239
|
-
}
|
|
1240
|
-
content += `
|
|
1071
|
+
promptText += ` **Keep it concise:** AI_READMEs should be <400 tokens. Focus on actionable conventions.
|
|
1072
|
+
|
|
1241
1073
|
`;
|
|
1242
1074
|
}
|
|
1243
|
-
|
|
1075
|
+
promptText += `---
|
|
1244
1076
|
|
|
1245
1077
|
`;
|
|
1246
|
-
|
|
1247
|
-
content += `- Install: \`${projectInfo.packageManager} install\`
|
|
1078
|
+
promptText += `\u{1F4A1} **Tips:**
|
|
1248
1079
|
`;
|
|
1249
|
-
|
|
1080
|
+
promptText += `- Work through each README sequentially
|
|
1250
1081
|
`;
|
|
1251
|
-
|
|
1082
|
+
promptText += `- Be concise - every token counts!
|
|
1252
1083
|
`;
|
|
1253
|
-
|
|
1254
|
-
content += `
|
|
1084
|
+
promptText += `- Focus on conventions that help generate better code
|
|
1255
1085
|
`;
|
|
1256
|
-
|
|
1086
|
+
promptText += `- After completing all, you can verify with \`validate_ai_readmes\`
|
|
1257
1087
|
|
|
1258
1088
|
`;
|
|
1259
|
-
|
|
1089
|
+
promptText += `**Start with the first AI_README now!**
|
|
1260
1090
|
`;
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1091
|
+
return {
|
|
1092
|
+
success: true,
|
|
1093
|
+
message: `Found ${targetReadmes.length} AI_README file(s) to initialize`,
|
|
1094
|
+
readmesToInitialize: targetReadmes.map((r) => r.path.replace(/\\/g, "/")),
|
|
1095
|
+
instructions: promptText
|
|
1096
|
+
};
|
|
1266
1097
|
}
|
|
1267
1098
|
|
|
1268
1099
|
// src/index.ts
|
|
@@ -1287,30 +1118,12 @@ server.setRequestHandler(ListToolsRequestSchema, async () => {
|
|
|
1287
1118
|
},
|
|
1288
1119
|
{
|
|
1289
1120
|
name: "get_context_for_file",
|
|
1290
|
-
description: '\u26A0\uFE0F REQUIRED: Call this tool BEFORE creating or editing ANY file to get project conventions.\n\nGets relevant AI_README context for a specific file path. Returns formatted guidelines that MUST be followed when writing code.\n\n**When to call
|
|
1121
|
+
description: '\u26A0\uFE0F REQUIRED: Call this tool BEFORE creating or editing ANY file to get project conventions.\n\nGets relevant AI_README context for a specific file path. Returns formatted guidelines that MUST be followed when writing code.\n\n**When to call:**\n- BEFORE using Write tool (creating new files)\n- BEFORE using Edit tool (modifying existing files)\n- AFTER updating AI_README (to get fresh context)\n- Works even if the target file does not exist yet\n\n**Workflows:**\n1. Following conventions: get_context_for_file \u2192 Write/Edit\n2. Establishing NEW conventions: update_ai_readme \u2192 get_context_for_file \u2192 Write/Edit\n3. Empty AI_README detected: Use `init_ai_readme` tool (tool will suggest this)\n4. Document discovered patterns: Write/Edit \u2192 update_ai_readme\n\n**Example:** Before creating Button.tsx, call get_context_for_file with filePath="src/components/Button.tsx" to learn styling preferences (CSS Modules vs Tailwind), naming conventions, component patterns, etc.',
|
|
1291
1122
|
inputSchema: zodToJsonSchema(getContextSchema)
|
|
1292
1123
|
},
|
|
1293
1124
|
{
|
|
1294
1125
|
name: "update_ai_readme",
|
|
1295
|
-
description:
|
|
1296
|
-
|
|
1297
|
-
**When to use this tool:**
|
|
1298
|
-
1. BEFORE code changes: When establishing NEW conventions
|
|
1299
|
-
- User requests a style/approach change (e.g., "use CSS Modules instead of Tailwind")
|
|
1300
|
-
- Choosing between multiple approaches (e.g., state management libraries)
|
|
1301
|
-
- Setting up new architectural patterns or folder structures
|
|
1302
|
-
- Workflow: update_ai_readme \u2192 get_context_for_file \u2192 Write/Edit
|
|
1303
|
-
|
|
1304
|
-
2. AFTER code changes: When documenting discovered patterns
|
|
1305
|
-
- Found consistent patterns in existing code
|
|
1306
|
-
- Identified team conventions from code review
|
|
1307
|
-
- Workflow: Write/Edit \u2192 update_ai_readme
|
|
1308
|
-
|
|
1309
|
-
**Example scenarios:**
|
|
1310
|
-
- User: "Use CSS Modules instead of Tailwind" \u2192 Update AI_README first, then modify code
|
|
1311
|
-
- User: "Add error handling to API calls" \u2192 Code first, document pattern after if it's new
|
|
1312
|
-
|
|
1313
|
-
Note: Only update when documenting NEW conventions - avoid duplicates.`,
|
|
1126
|
+
description: 'Update an AI_README.md file to document conventions, patterns, or architectural decisions. Supports append, prepend, replace, insert-after, insert-before operations.\n\n**CRITICAL: Token Efficiency Rules**\n- Keep content EXTREMELY concise (< 400 tokens ideal, < 600 warning, > 1000 error)\n- Only document ACTIONABLE conventions that affect code generation\n- NO explanations, NO examples, NO verbose descriptions\n- Use bullet points, avoid complete sentences when possible\n- Focus on: tech stack, naming rules, patterns, architectural decisions\n- AVOID: project background, how-to guides, documentation, obvious practices\n\n**When to use:**\n1. BEFORE code changes: Establishing NEW conventions\n - User requests style/approach change (e.g., "use CSS Modules")\n - Choosing between approaches (e.g., state management)\n - Setting up new architectural patterns\n - Workflow: update_ai_readme \u2192 get_context_for_file \u2192 Write/Edit\n\n2. AFTER code changes: Documenting discovered patterns\n - Found consistent patterns in existing code\n - Workflow: Write/Edit \u2192 update_ai_readme\n\n**Quality checklist before updating:**\nIs this a CONVENTION or PATTERN (not documentation)?\nWill this help AI generate better code?\nIs it concise (<3 words per bullet)?\nDoes it avoid obvious/general practices?\nReject: project descriptions, tutorials, general best practices\n\n**Example (GOOD):**\n- Use CSS Modules\n- Components in PascalCase\n- Test coverage: 80%+\n\n**Example (BAD - too verbose):**\n- We use CSS Modules for styling because it provides better type safety and scoping compared to Tailwind...',
|
|
1314
1127
|
inputSchema: zodToJsonSchema(updateSchema)
|
|
1315
1128
|
},
|
|
1316
1129
|
{
|
|
@@ -1320,7 +1133,7 @@ Note: Only update when documenting NEW conventions - avoid duplicates.`,
|
|
|
1320
1133
|
},
|
|
1321
1134
|
{
|
|
1322
1135
|
name: "init_ai_readme",
|
|
1323
|
-
description:
|
|
1136
|
+
description: '\u{1F680} Initialize and populate empty AI_README files in your project.\n\nScans the project for empty or missing AI_README files and guides you through populating them with project conventions.\n\n**When to use:**\n- First time setting up AI_README in a project\n- When get_context_for_file detects empty AI_README files\n- After creating new empty AI_README.md files manually\n- To populate multiple AI_README files at once\n\n**What it does:**\n1. Scans project for empty AI_README files\n2. Creates root-level AI_README if none exist\n3. Provides step-by-step instructions to populate each file\n4. Guides analysis of tech stack, patterns, and conventions\n\n**Workflow:**\n1. Call init_ai_readme\n2. Follow the instructions to explore directories\n3. Use update_ai_readme to populate each file\n4. Call get_context_for_file to verify and use conventions\n\n**Example:** `init_ai_readme({ projectRoot: "/path/to/project" })`',
|
|
1324
1137
|
inputSchema: zodToJsonSchema(initSchema)
|
|
1325
1138
|
}
|
|
1326
1139
|
]
|
|
@@ -1384,7 +1197,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
1384
1197
|
content: [
|
|
1385
1198
|
{
|
|
1386
1199
|
type: "text",
|
|
1387
|
-
text: JSON.stringify(result, null, 2)
|
|
1200
|
+
text: result.instructions || JSON.stringify(result, null, 2)
|
|
1388
1201
|
}
|
|
1389
1202
|
]
|
|
1390
1203
|
};
|