leadcode 0.1.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/LICENSE +21 -0
- package/README.md +186 -0
- package/bin/leadcode.js +2 -0
- package/dist/analyzers/constants.d.ts +3 -0
- package/dist/analyzers/constants.d.ts.map +1 -0
- package/dist/analyzers/constants.js +14 -0
- package/dist/analyzers/constants.js.map +1 -0
- package/dist/analyzers/dependencies.d.ts +8 -0
- package/dist/analyzers/dependencies.d.ts.map +1 -0
- package/dist/analyzers/dependencies.js +13 -0
- package/dist/analyzers/dependencies.js.map +1 -0
- package/dist/analyzers/detection.d.ts +6 -0
- package/dist/analyzers/detection.d.ts.map +1 -0
- package/dist/analyzers/detection.js +334 -0
- package/dist/analyzers/detection.js.map +1 -0
- package/dist/analyzers/patterns.d.ts +12 -0
- package/dist/analyzers/patterns.d.ts.map +1 -0
- package/dist/analyzers/patterns.js +84 -0
- package/dist/analyzers/patterns.js.map +1 -0
- package/dist/analyzers/structure.d.ts +3 -0
- package/dist/analyzers/structure.d.ts.map +1 -0
- package/dist/analyzers/structure.js +217 -0
- package/dist/analyzers/structure.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +64 -0
- package/dist/index.js.map +1 -0
- package/dist/rules/auth.d.ts +3 -0
- package/dist/rules/auth.d.ts.map +1 -0
- package/dist/rules/auth.js +48 -0
- package/dist/rules/auth.js.map +1 -0
- package/dist/rules/cross-stack.d.ts +3 -0
- package/dist/rules/cross-stack.d.ts.map +1 -0
- package/dist/rules/cross-stack.js +320 -0
- package/dist/rules/cross-stack.js.map +1 -0
- package/dist/rules/drizzle.d.ts +3 -0
- package/dist/rules/drizzle.d.ts.map +1 -0
- package/dist/rules/drizzle.js +43 -0
- package/dist/rules/drizzle.js.map +1 -0
- package/dist/rules/index.d.ts +7 -0
- package/dist/rules/index.d.ts.map +1 -0
- package/dist/rules/index.js +104 -0
- package/dist/rules/index.js.map +1 -0
- package/dist/rules/nextjs.d.ts +4 -0
- package/dist/rules/nextjs.d.ts.map +1 -0
- package/dist/rules/nextjs.js +86 -0
- package/dist/rules/nextjs.js.map +1 -0
- package/dist/rules/node.d.ts +3 -0
- package/dist/rules/node.d.ts.map +1 -0
- package/dist/rules/node.js +61 -0
- package/dist/rules/node.js.map +1 -0
- package/dist/rules/prisma.d.ts +3 -0
- package/dist/rules/prisma.d.ts.map +1 -0
- package/dist/rules/prisma.js +44 -0
- package/dist/rules/prisma.js.map +1 -0
- package/dist/rules/react.d.ts +3 -0
- package/dist/rules/react.d.ts.map +1 -0
- package/dist/rules/react.js +58 -0
- package/dist/rules/react.js.map +1 -0
- package/dist/rules/state.d.ts +3 -0
- package/dist/rules/state.d.ts.map +1 -0
- package/dist/rules/state.js +54 -0
- package/dist/rules/state.js.map +1 -0
- package/dist/rules/tailwind.d.ts +3 -0
- package/dist/rules/tailwind.d.ts.map +1 -0
- package/dist/rules/tailwind.js +41 -0
- package/dist/rules/tailwind.js.map +1 -0
- package/dist/rules/trpc.d.ts +3 -0
- package/dist/rules/trpc.d.ts.map +1 -0
- package/dist/rules/trpc.js +35 -0
- package/dist/rules/trpc.js.map +1 -0
- package/dist/rules/typescript.d.ts +3 -0
- package/dist/rules/typescript.d.ts.map +1 -0
- package/dist/rules/typescript.js +54 -0
- package/dist/rules/typescript.js.map +1 -0
- package/dist/rules/validation.d.ts +3 -0
- package/dist/rules/validation.d.ts.map +1 -0
- package/dist/rules/validation.js +38 -0
- package/dist/rules/validation.js.map +1 -0
- package/dist/templates/claude-md.d.ts +4 -0
- package/dist/templates/claude-md.d.ts.map +1 -0
- package/dist/templates/claude-md.js +309 -0
- package/dist/templates/claude-md.js.map +1 -0
- package/dist/tools/analyze-repo.d.ts +3 -0
- package/dist/tools/analyze-repo.d.ts.map +1 -0
- package/dist/tools/analyze-repo.js +68 -0
- package/dist/tools/analyze-repo.js.map +1 -0
- package/dist/tools/detect-gaps.d.ts +3 -0
- package/dist/tools/detect-gaps.d.ts.map +1 -0
- package/dist/tools/detect-gaps.js +34 -0
- package/dist/tools/detect-gaps.js.map +1 -0
- package/dist/tools/generate-claude-md.d.ts +3 -0
- package/dist/tools/generate-claude-md.d.ts.map +1 -0
- package/dist/tools/generate-claude-md.js +70 -0
- package/dist/tools/generate-claude-md.js.map +1 -0
- package/dist/tools/suggest.d.ts +3 -0
- package/dist/tools/suggest.d.ts.map +1 -0
- package/dist/tools/suggest.js +340 -0
- package/dist/tools/suggest.js.map +1 -0
- package/dist/tools/update-claude-md.d.ts +3 -0
- package/dist/tools/update-claude-md.d.ts.map +1 -0
- package/dist/tools/update-claude-md.js +108 -0
- package/dist/tools/update-claude-md.js.map +1 -0
- package/dist/tools/validate-claude-md.d.ts +3 -0
- package/dist/tools/validate-claude-md.d.ts.map +1 -0
- package/dist/tools/validate-claude-md.js +137 -0
- package/dist/tools/validate-claude-md.js.map +1 -0
- package/dist/types.d.ts +116 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +2 -0
- package/dist/types.js.map +1 -0
- package/package.json +55 -0
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { readdir, readFile } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { IGNORE_DIRS as IGNORE, SOURCE_EXTS } from "./constants.js";
|
|
4
|
+
async function scanDir(dir, patterns, maxDepth = 6) {
|
|
5
|
+
if (maxDepth <= 0)
|
|
6
|
+
return;
|
|
7
|
+
let entries;
|
|
8
|
+
try {
|
|
9
|
+
entries = await readdir(dir, { withFileTypes: true });
|
|
10
|
+
}
|
|
11
|
+
catch {
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
for (const entry of entries) {
|
|
15
|
+
if (IGNORE.has(entry.name) || entry.name.startsWith("."))
|
|
16
|
+
continue;
|
|
17
|
+
if (entry.isDirectory()) {
|
|
18
|
+
await scanDir(join(dir, entry.name), patterns, maxDepth - 1);
|
|
19
|
+
continue;
|
|
20
|
+
}
|
|
21
|
+
const ext = entry.name.split(".").pop() ?? "";
|
|
22
|
+
if (!SOURCE_EXTS.has(ext))
|
|
23
|
+
continue;
|
|
24
|
+
const filePath = join(dir, entry.name);
|
|
25
|
+
let content;
|
|
26
|
+
try {
|
|
27
|
+
content = await readFile(filePath, "utf-8");
|
|
28
|
+
}
|
|
29
|
+
catch {
|
|
30
|
+
continue;
|
|
31
|
+
}
|
|
32
|
+
const lines = content.split("\n");
|
|
33
|
+
if (ext === "tsx" || ext === "jsx") {
|
|
34
|
+
patterns.totalComponents++;
|
|
35
|
+
}
|
|
36
|
+
// Check for 'use client' / 'use server'
|
|
37
|
+
const firstLines = content.slice(0, 200);
|
|
38
|
+
if (firstLines.includes("'use client'") || firstLines.includes('"use client"')) {
|
|
39
|
+
patterns.useClientCount++;
|
|
40
|
+
}
|
|
41
|
+
if (firstLines.includes("'use server'") || firstLines.includes('"use server"')) {
|
|
42
|
+
patterns.useServerCount++;
|
|
43
|
+
}
|
|
44
|
+
// Barrel files
|
|
45
|
+
if (entry.name === "index.ts" || entry.name === "index.js") {
|
|
46
|
+
if (content.includes("export {") || content.includes("export *")) {
|
|
47
|
+
patterns.hasBarrelFiles = true;
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
// Path alias
|
|
51
|
+
if (content.includes("from '@/") || content.includes("from \"@/") ||
|
|
52
|
+
content.includes("from '~/") || content.includes("from \"~/")) {
|
|
53
|
+
patterns.usesPathAlias = true;
|
|
54
|
+
}
|
|
55
|
+
// Large files
|
|
56
|
+
if (lines.length > 300) {
|
|
57
|
+
patterns.largeFiles.push(filePath);
|
|
58
|
+
}
|
|
59
|
+
// Console.log count
|
|
60
|
+
const matches = content.match(/console\.log\(/g);
|
|
61
|
+
if (matches) {
|
|
62
|
+
patterns.consoleLogCount += matches.length;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
export async function analyzePatterns(projectPath) {
|
|
67
|
+
const patterns = {
|
|
68
|
+
useClientCount: 0,
|
|
69
|
+
useServerCount: 0,
|
|
70
|
+
totalComponents: 0,
|
|
71
|
+
clientRatio: 0,
|
|
72
|
+
hasBarrelFiles: false,
|
|
73
|
+
usesPathAlias: false,
|
|
74
|
+
largeFiles: [],
|
|
75
|
+
consoleLogCount: 0,
|
|
76
|
+
};
|
|
77
|
+
await scanDir(projectPath, patterns);
|
|
78
|
+
patterns.clientRatio =
|
|
79
|
+
patterns.totalComponents > 0
|
|
80
|
+
? patterns.useClientCount / patterns.totalComponents
|
|
81
|
+
: 0;
|
|
82
|
+
return patterns;
|
|
83
|
+
}
|
|
84
|
+
//# sourceMappingURL=patterns.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"patterns.js","sourceRoot":"","sources":["../../src/analyzers/patterns.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structure.d.ts","sourceRoot":"","sources":["../../src/analyzers/structure.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAgIjD,wBAAsB,gBAAgB,CACpC,WAAW,EAAE,MAAM,GAClB,OAAO,CAAC,aAAa,CAAC,CA+HxB"}
|
|
@@ -0,0 +1,217 @@
|
|
|
1
|
+
import { readdir, readFile, stat } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { IGNORE_DIRS as IGNORE } from "./constants.js";
|
|
4
|
+
async function exists(path) {
|
|
5
|
+
try {
|
|
6
|
+
await stat(path);
|
|
7
|
+
return true;
|
|
8
|
+
}
|
|
9
|
+
catch {
|
|
10
|
+
return false;
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
async function getTopLevelDirs(projectPath) {
|
|
14
|
+
const entries = await readdir(projectPath, { withFileTypes: true });
|
|
15
|
+
return entries
|
|
16
|
+
.filter((e) => e.isDirectory() && !IGNORE.has(e.name) && !e.name.startsWith("."))
|
|
17
|
+
.map((e) => e.name);
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* Recursively search for files matching given names within a directory.
|
|
21
|
+
* Returns true as soon as any match is found (early exit).
|
|
22
|
+
*/
|
|
23
|
+
async function hasFileRecursive(dir, fileNames, maxDepth = 5) {
|
|
24
|
+
if (maxDepth <= 0)
|
|
25
|
+
return false;
|
|
26
|
+
try {
|
|
27
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
28
|
+
for (const entry of entries) {
|
|
29
|
+
if (IGNORE.has(entry.name) || entry.name.startsWith("."))
|
|
30
|
+
continue;
|
|
31
|
+
if (entry.isFile() && fileNames.includes(entry.name))
|
|
32
|
+
return true;
|
|
33
|
+
if (entry.isDirectory()) {
|
|
34
|
+
const found = await hasFileRecursive(join(dir, entry.name), fileNames, maxDepth - 1);
|
|
35
|
+
if (found)
|
|
36
|
+
return true;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
catch {
|
|
41
|
+
// Permission errors, etc.
|
|
42
|
+
}
|
|
43
|
+
return false;
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* Search for a string pattern in files within a directory.
|
|
47
|
+
* Returns true if any file contains the pattern.
|
|
48
|
+
*/
|
|
49
|
+
async function hasContentRecursive(dir, pattern, extensions, maxDepth = 4) {
|
|
50
|
+
if (maxDepth <= 0)
|
|
51
|
+
return false;
|
|
52
|
+
try {
|
|
53
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
54
|
+
for (const entry of entries) {
|
|
55
|
+
if (IGNORE.has(entry.name) || entry.name.startsWith("."))
|
|
56
|
+
continue;
|
|
57
|
+
if (entry.isFile()) {
|
|
58
|
+
const ext = entry.name.split(".").pop() ?? "";
|
|
59
|
+
if (extensions.includes(ext)) {
|
|
60
|
+
try {
|
|
61
|
+
const content = await readFile(join(dir, entry.name), "utf-8");
|
|
62
|
+
if (pattern.test(content))
|
|
63
|
+
return true;
|
|
64
|
+
}
|
|
65
|
+
catch {
|
|
66
|
+
// Skip unreadable files
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
if (entry.isDirectory()) {
|
|
71
|
+
const found = await hasContentRecursive(join(dir, entry.name), pattern, extensions, maxDepth - 1);
|
|
72
|
+
if (found)
|
|
73
|
+
return true;
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
catch {
|
|
78
|
+
// Permission errors
|
|
79
|
+
}
|
|
80
|
+
return false;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Count source files recursively (ts, tsx, js, jsx).
|
|
84
|
+
*/
|
|
85
|
+
async function countSourceFiles(dir, maxDepth = 6) {
|
|
86
|
+
if (maxDepth <= 0)
|
|
87
|
+
return 0;
|
|
88
|
+
let count = 0;
|
|
89
|
+
try {
|
|
90
|
+
const entries = await readdir(dir, { withFileTypes: true });
|
|
91
|
+
for (const entry of entries) {
|
|
92
|
+
if (IGNORE.has(entry.name) || entry.name.startsWith("."))
|
|
93
|
+
continue;
|
|
94
|
+
if (entry.isFile()) {
|
|
95
|
+
const ext = entry.name.split(".").pop() ?? "";
|
|
96
|
+
if (["ts", "tsx", "js", "jsx"].includes(ext))
|
|
97
|
+
count++;
|
|
98
|
+
}
|
|
99
|
+
if (entry.isDirectory()) {
|
|
100
|
+
count += await countSourceFiles(join(dir, entry.name), maxDepth - 1);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
catch {
|
|
105
|
+
// Permission errors
|
|
106
|
+
}
|
|
107
|
+
return count;
|
|
108
|
+
}
|
|
109
|
+
function anyDirExists(topLevelDirs, names) {
|
|
110
|
+
return names.some((n) => topLevelDirs.includes(n));
|
|
111
|
+
}
|
|
112
|
+
export async function analyzeStructure(projectPath) {
|
|
113
|
+
const topLevelDirs = await getTopLevelDirs(projectPath);
|
|
114
|
+
const resolve = (segments) => join(projectPath, ...segments);
|
|
115
|
+
const hasSrcDir = await exists(join(projectPath, "src"));
|
|
116
|
+
const prefix = hasSrcDir ? ["src"] : [];
|
|
117
|
+
// Resolve with and without src/
|
|
118
|
+
const checkPath = async (segments) => (await exists(resolve([...prefix, ...segments]))) ||
|
|
119
|
+
(await exists(resolve(segments)));
|
|
120
|
+
// Parallel basic checks
|
|
121
|
+
const [hasAppDir, hasPagesDir, hasMiddleware] = await Promise.all([
|
|
122
|
+
checkPath(["app"]),
|
|
123
|
+
checkPath(["pages"]),
|
|
124
|
+
Promise.all([
|
|
125
|
+
exists(join(projectPath, "middleware.ts")),
|
|
126
|
+
exists(join(projectPath, "middleware.js")),
|
|
127
|
+
exists(join(projectPath, "src", "middleware.ts")),
|
|
128
|
+
exists(join(projectPath, "src", "middleware.js")),
|
|
129
|
+
]).then((r) => r.some(Boolean)),
|
|
130
|
+
]);
|
|
131
|
+
const hasApiRoutes = (await checkPath(["app", "api"])) || (await checkPath(["pages", "api"]));
|
|
132
|
+
// App Router specific file detection (parallel)
|
|
133
|
+
const appDir = hasSrcDir
|
|
134
|
+
? join(projectPath, "src", "app")
|
|
135
|
+
: join(projectPath, "app");
|
|
136
|
+
const [hasErrorBoundary, hasLoadingStates, hasNotFound, hasMetadata] = hasAppDir
|
|
137
|
+
? await Promise.all([
|
|
138
|
+
hasFileRecursive(appDir, ["error.tsx", "error.ts", "error.js", "error.jsx"]),
|
|
139
|
+
hasFileRecursive(appDir, ["loading.tsx", "loading.ts", "loading.js", "loading.jsx"]),
|
|
140
|
+
hasFileRecursive(appDir, ["not-found.tsx", "not-found.ts", "not-found.js", "not-found.jsx"]),
|
|
141
|
+
hasContentRecursive(appDir, /export\s+(const\s+metadata|async\s+function\s+generateMetadata|function\s+generateMetadata)/, ["ts", "tsx", "js", "jsx"]),
|
|
142
|
+
])
|
|
143
|
+
: [false, false, false, false];
|
|
144
|
+
// Parallel infra + directory checks
|
|
145
|
+
const [hasPublicDir, hasLibDir, hasUtilsDir, hasServicesDir, hasComponentsDir, hasHooksDir, hasPrismaSchema, hasTsConfig, hasEnvExample, hasEnvValidation, hasDockerfile, hasRuntime, approximateFileCount,] = await Promise.all([
|
|
146
|
+
exists(join(projectPath, "public")),
|
|
147
|
+
checkPath(["lib"]),
|
|
148
|
+
checkPath(["utils"]),
|
|
149
|
+
checkPath(["services"]),
|
|
150
|
+
checkPath(["components"]),
|
|
151
|
+
checkPath(["hooks"]),
|
|
152
|
+
exists(join(projectPath, "prisma", "schema.prisma")),
|
|
153
|
+
exists(join(projectPath, "tsconfig.json")),
|
|
154
|
+
Promise.all([
|
|
155
|
+
exists(join(projectPath, ".env.example")),
|
|
156
|
+
exists(join(projectPath, ".env.local.example")),
|
|
157
|
+
]).then((r) => r.some(Boolean)),
|
|
158
|
+
Promise.all([
|
|
159
|
+
exists(join(projectPath, "env.ts")),
|
|
160
|
+
exists(join(projectPath, "env.mjs")),
|
|
161
|
+
exists(join(projectPath, "src", "env.ts")),
|
|
162
|
+
exists(join(projectPath, "src", "env.mjs")),
|
|
163
|
+
exists(join(projectPath, "lib", "env.ts")),
|
|
164
|
+
exists(join(projectPath, "src", "lib", "env.ts")),
|
|
165
|
+
]).then((r) => r.some(Boolean)),
|
|
166
|
+
Promise.all([
|
|
167
|
+
exists(join(projectPath, "Dockerfile")),
|
|
168
|
+
exists(join(projectPath, "docker-compose.yml")),
|
|
169
|
+
exists(join(projectPath, "docker-compose.yaml")),
|
|
170
|
+
]).then((r) => r.some(Boolean)),
|
|
171
|
+
Promise.all([
|
|
172
|
+
exists(join(projectPath, "bun.lockb")),
|
|
173
|
+
exists(join(projectPath, "bun.lock")),
|
|
174
|
+
exists(join(projectPath, "deno.json")),
|
|
175
|
+
exists(join(projectPath, "deno.jsonc")),
|
|
176
|
+
]).then(([bunLockb, bunLock, denoJson, denoJsonc]) => (bunLockb || bunLock) ? "bun" : (denoJson || denoJsonc) ? "deno" : "node"),
|
|
177
|
+
countSourceFiles(projectPath),
|
|
178
|
+
]);
|
|
179
|
+
// All directory checks
|
|
180
|
+
const allDirs = hasSrcDir
|
|
181
|
+
? [...topLevelDirs, ...(await getTopLevelDirs(join(projectPath, "src")).catch(() => []))]
|
|
182
|
+
: topLevelDirs;
|
|
183
|
+
return {
|
|
184
|
+
hasSrcDir,
|
|
185
|
+
hasAppDir,
|
|
186
|
+
hasPagesDir,
|
|
187
|
+
hasApiRoutes,
|
|
188
|
+
hasMiddleware,
|
|
189
|
+
hasPublicDir,
|
|
190
|
+
hasLibDir,
|
|
191
|
+
hasUtilsDir,
|
|
192
|
+
hasServicesDir,
|
|
193
|
+
hasComponentsDir,
|
|
194
|
+
hasHooksDir,
|
|
195
|
+
topLevelDirs,
|
|
196
|
+
hasErrorBoundary,
|
|
197
|
+
hasLoadingStates,
|
|
198
|
+
hasNotFound,
|
|
199
|
+
hasMetadata,
|
|
200
|
+
hasPrismaSchema,
|
|
201
|
+
hasDockerfile,
|
|
202
|
+
hasEnvExample,
|
|
203
|
+
hasEnvValidation,
|
|
204
|
+
hasTsConfig,
|
|
205
|
+
hasTestsDir: anyDirExists(allDirs, ["__tests__", "tests", "test", "e2e"]),
|
|
206
|
+
hasSchemasDir: anyDirExists(allDirs, ["schemas", "validators", "validations"]),
|
|
207
|
+
hasActionsDir: anyDirExists(allDirs, ["actions"]),
|
|
208
|
+
hasQueriesDir: anyDirExists(allDirs, ["queries", "data"]),
|
|
209
|
+
hasConfigDir: anyDirExists(allDirs, ["config", "constants"]),
|
|
210
|
+
hasProvidersDir: anyDirExists(allDirs, ["providers", "context"]),
|
|
211
|
+
hasStoreDir: anyDirExists(allDirs, ["store", "stores"]),
|
|
212
|
+
hasTypesDir: anyDirExists(allDirs, ["types"]),
|
|
213
|
+
detectedRuntime: hasRuntime,
|
|
214
|
+
approximateFileCount,
|
|
215
|
+
};
|
|
216
|
+
}
|
|
217
|
+
//# sourceMappingURL=structure.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"structure.js","sourceRoot":"","sources":["../../src/analyzers/structure.ts"],"names":[],"mappings":"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"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":""}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
2
|
+
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
3
|
+
import * as z from "zod";
|
|
4
|
+
import { registerAnalyzeRepo } from "./tools/analyze-repo.js";
|
|
5
|
+
import { registerDetectGaps } from "./tools/detect-gaps.js";
|
|
6
|
+
import { registerSuggest } from "./tools/suggest.js";
|
|
7
|
+
import { registerGenerateClaudeMd } from "./tools/generate-claude-md.js";
|
|
8
|
+
import { registerValidateClaudeMd } from "./tools/validate-claude-md.js";
|
|
9
|
+
import { registerUpdateClaudeMd } from "./tools/update-claude-md.js";
|
|
10
|
+
const server = new McpServer({
|
|
11
|
+
name: "leadcode",
|
|
12
|
+
version: "0.1.0",
|
|
13
|
+
});
|
|
14
|
+
// Register tools
|
|
15
|
+
registerAnalyzeRepo(server);
|
|
16
|
+
registerDetectGaps(server);
|
|
17
|
+
registerSuggest(server);
|
|
18
|
+
registerGenerateClaudeMd(server);
|
|
19
|
+
registerValidateClaudeMd(server);
|
|
20
|
+
registerUpdateClaudeMd(server);
|
|
21
|
+
// Register prompts
|
|
22
|
+
server.prompt("setup-project", "Full LeadCode workflow: analyze repo → detect gaps → suggest conventions → generate CLAUDE.md", { projectPath: z.string().describe("Absolute path to the project root") }, ({ projectPath }) => ({
|
|
23
|
+
messages: [
|
|
24
|
+
{
|
|
25
|
+
role: "user",
|
|
26
|
+
content: {
|
|
27
|
+
type: "text",
|
|
28
|
+
text: [
|
|
29
|
+
`Please set up LeadCode for the project at ${projectPath}. Follow these steps:`,
|
|
30
|
+
"",
|
|
31
|
+
"1. Call analyze-repo to scan the project and get a full technical analysis.",
|
|
32
|
+
"2. Call detect-gaps with the analysis to identify structural gaps.",
|
|
33
|
+
"3. Call suggest-conventions with the analysis and gaps to get improvement options.",
|
|
34
|
+
"4. Present the analysis, gaps, and suggestions to me in a clear summary.",
|
|
35
|
+
"5. Ask me which options I prefer for each gap (simple / clean / scalable).",
|
|
36
|
+
"6. Once I've chosen, call generate-claude-md with my choices to create the CLAUDE.md file.",
|
|
37
|
+
"",
|
|
38
|
+
"Be thorough and explain each gap and suggestion clearly.",
|
|
39
|
+
].join("\n"),
|
|
40
|
+
},
|
|
41
|
+
},
|
|
42
|
+
],
|
|
43
|
+
}));
|
|
44
|
+
server.prompt("validate-project", "Check if an existing CLAUDE.md is still in sync with the project", { projectPath: z.string().describe("Absolute path to the project root") }, ({ projectPath }) => ({
|
|
45
|
+
messages: [
|
|
46
|
+
{
|
|
47
|
+
role: "user",
|
|
48
|
+
content: {
|
|
49
|
+
type: "text",
|
|
50
|
+
text: [
|
|
51
|
+
`Please validate the CLAUDE.md for the project at ${projectPath}.`,
|
|
52
|
+
"",
|
|
53
|
+
"1. Call validate-claude-md to check for drifts between the CLAUDE.md and actual project state.",
|
|
54
|
+
"2. Present any drifts found with clear explanations.",
|
|
55
|
+
"3. If drifts are found, suggest whether to regenerate or manually fix the CLAUDE.md.",
|
|
56
|
+
].join("\n"),
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
],
|
|
60
|
+
}));
|
|
61
|
+
// Start server
|
|
62
|
+
const transport = new StdioServerTransport();
|
|
63
|
+
await server.connect(transport);
|
|
64
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;AACzB,OAAO,EAAE,mBAAmB,EAAE,MAAM,yBAAyB,CAAC;AAC9D,OAAO,EAAE,kBAAkB,EAAE,MAAM,wBAAwB,CAAC;AAC5D,OAAO,EAAE,eAAe,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,EAAE,wBAAwB,EAAE,MAAM,+BAA+B,CAAC;AACzE,OAAO,EAAE,sBAAsB,EAAE,MAAM,6BAA6B,CAAC;AAErE,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC;IAC3B,IAAI,EAAE,UAAU;IAChB,OAAO,EAAE,OAAO;CACjB,CAAC,CAAC;AAEH,iBAAiB;AACjB,mBAAmB,CAAC,MAAM,CAAC,CAAC;AAC5B,kBAAkB,CAAC,MAAM,CAAC,CAAC;AAC3B,eAAe,CAAC,MAAM,CAAC,CAAC;AACxB,wBAAwB,CAAC,MAAM,CAAC,CAAC;AACjC,wBAAwB,CAAC,MAAM,CAAC,CAAC;AACjC,sBAAsB,CAAC,MAAM,CAAC,CAAC;AAE/B,mBAAmB;AACnB,MAAM,CAAC,MAAM,CACX,eAAe,EACf,+FAA+F,EAC/F,EAAE,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,mCAAmC,CAAC,EAAE,EACzE,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,CAAC;IACpB,QAAQ,EAAE;QACR;YACE,IAAI,EAAE,MAAe;YACrB,OAAO,EAAE;gBACP,IAAI,EAAE,MAAe;gBACrB,IAAI,EAAE;oBACJ,6CAA6C,WAAW,uBAAuB;oBAC/E,EAAE;oBACF,6EAA6E;oBAC7E,oEAAoE;oBACpE,oFAAoF;oBACpF,0EAA0E;oBAC1E,4EAA4E;oBAC5E,4FAA4F;oBAC5F,EAAE;oBACF,0DAA0D;iBAC3D,CAAC,IAAI,CAAC,IAAI,CAAC;aACb;SACF;KACF;CACF,CAAC,CACH,CAAC;AAEF,MAAM,CAAC,MAAM,CACX,kBAAkB,EAClB,kEAAkE,EAClE,EAAE,WAAW,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,CAAC,mCAAmC,CAAC,EAAE,EACzE,CAAC,EAAE,WAAW,EAAE,EAAE,EAAE,CAAC,CAAC;IACpB,QAAQ,EAAE;QACR;YACE,IAAI,EAAE,MAAe;YACrB,OAAO,EAAE;gBACP,IAAI,EAAE,MAAe;gBACrB,IAAI,EAAE;oBACJ,oDAAoD,WAAW,GAAG;oBAClE,EAAE;oBACF,gGAAgG;oBAChG,sDAAsD;oBACtD,sFAAsF;iBACvF,CAAC,IAAI,CAAC,IAAI,CAAC;aACb;SACF;KACF;CACF,CAAC,CACH,CAAC;AAEF,eAAe;AACf,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;AAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../../src/rules/auth.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAExC,eAAO,MAAM,SAAS,EAAE,IAiDvB,CAAC"}
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
export const authRules = {
|
|
2
|
+
id: "auth",
|
|
3
|
+
name: "Authentication",
|
|
4
|
+
applies: (a) => a.detected.auth !== null,
|
|
5
|
+
gaps: [
|
|
6
|
+
{
|
|
7
|
+
category: "auth-middleware",
|
|
8
|
+
severity: "high",
|
|
9
|
+
check: (a) => a.framework?.name === "next" &&
|
|
10
|
+
a.framework.variant === "app-router" &&
|
|
11
|
+
!a.structure.hasMiddleware,
|
|
12
|
+
message: "No middleware.ts detected for route protection",
|
|
13
|
+
details: "Next.js App Router should use middleware.ts for authentication checks on protected routes.",
|
|
14
|
+
},
|
|
15
|
+
{
|
|
16
|
+
category: "auth-session",
|
|
17
|
+
severity: "medium",
|
|
18
|
+
check: (a) => !a.structure.hasLibDir && !a.structure.hasUtilsDir,
|
|
19
|
+
message: "No centralized auth utility detected (e.g., lib/auth.ts)",
|
|
20
|
+
details: "Every protected Server Component and API route must validate the session server-side. Never rely on client-side auth state alone.",
|
|
21
|
+
},
|
|
22
|
+
],
|
|
23
|
+
conventions: [
|
|
24
|
+
{
|
|
25
|
+
id: "auth-server-check",
|
|
26
|
+
description: "Server-side auth checks",
|
|
27
|
+
rule: "Always validate authentication on the server (middleware, Server Components, API routes). Client-side checks are for UX only, not security.",
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: "auth-centralized",
|
|
31
|
+
description: "Centralized auth utilities",
|
|
32
|
+
rule: "Create a single auth utility (e.g., lib/auth.ts) that exports getCurrentUser(), requireAuth(), etc. All auth checks go through this module.",
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
id: "auth-protect-api",
|
|
36
|
+
description: "Protect API routes",
|
|
37
|
+
rule: "Every API route that requires auth must call requireAuth() at the top. No exceptions.",
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
interdictions: [
|
|
41
|
+
"NEVER store auth tokens in localStorage — use httpOnly cookies.",
|
|
42
|
+
"NEVER trust client-sent user IDs — always derive user identity from the session.",
|
|
43
|
+
"NEVER expose user passwords, hashes, or internal IDs in API responses.",
|
|
44
|
+
"NEVER skip auth checks on API routes because 'the UI prevents access'.",
|
|
45
|
+
],
|
|
46
|
+
crossRefs: [],
|
|
47
|
+
};
|
|
48
|
+
//# sourceMappingURL=auth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../../src/rules/auth.ts"],"names":[],"mappings":"AAEA,MAAM,CAAC,MAAM,SAAS,GAAS;IAC7B,EAAE,EAAE,MAAM;IACV,IAAI,EAAE,gBAAgB;IACtB,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC,IAAI,KAAK,IAAI;IACxC,IAAI,EAAE;QACJ;YACE,QAAQ,EAAE,iBAAiB;YAC3B,QAAQ,EAAE,MAAM;YAChB,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CACX,CAAC,CAAC,SAAS,EAAE,IAAI,KAAK,MAAM;gBAC5B,CAAC,CAAC,SAAS,CAAC,OAAO,KAAK,YAAY;gBACpC,CAAC,CAAC,CAAC,SAAS,CAAC,aAAa;YAC5B,OAAO,EAAE,gDAAgD;YACzD,OAAO,EACL,4FAA4F;SAC/F;QACD;YACE,QAAQ,EAAE,cAAc;YACxB,QAAQ,EAAE,QAAQ;YAClB,KAAK,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC,SAAS,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,WAAW;YAChE,OAAO,EAAE,0DAA0D;YACnE,OAAO,EACL,mIAAmI;SACtI;KACF;IACD,WAAW,EAAE;QACX;YACE,EAAE,EAAE,mBAAmB;YACvB,WAAW,EAAE,yBAAyB;YACtC,IAAI,EAAE,6IAA6I;SACpJ;QACD;YACE,EAAE,EAAE,kBAAkB;YACtB,WAAW,EAAE,4BAA4B;YACzC,IAAI,EAAE,6IAA6I;SACpJ;QACD;YACE,EAAE,EAAE,kBAAkB;YACtB,WAAW,EAAE,oBAAoB;YACjC,IAAI,EAAE,uFAAuF;SAC9F;KACF;IACD,aAAa,EAAE;QACb,iEAAiE;QACjE,kFAAkF;QAClF,wEAAwE;QACxE,wEAAwE;KACzE;IACD,SAAS,EAAE,EAAE;CACd,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cross-stack.d.ts","sourceRoot":"","sources":["../../src/rules/cross-stack.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,IAAI,EAAE,MAAM,aAAa,CAAC;AAExC,eAAO,MAAM,eAAe,EAAE,IA8T7B,CAAC"}
|