codemelt-core 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.
Files changed (82) hide show
  1. package/dist/analyzer/compressionAnalyzer.d.ts +4 -0
  2. package/dist/analyzer/compressionAnalyzer.d.ts.map +1 -0
  3. package/dist/analyzer/compressionAnalyzer.js +21 -0
  4. package/dist/analyzer/index.d.ts +3 -0
  5. package/dist/analyzer/index.d.ts.map +1 -0
  6. package/dist/analyzer/index.js +2 -0
  7. package/dist/analyzer/prompts/onboarding.d.ts +3 -0
  8. package/dist/analyzer/prompts/onboarding.d.ts.map +1 -0
  9. package/dist/analyzer/prompts/onboarding.js +14 -0
  10. package/dist/analyzer/prompts/performance.d.ts +3 -0
  11. package/dist/analyzer/prompts/performance.d.ts.map +1 -0
  12. package/dist/analyzer/prompts/performance.js +18 -0
  13. package/dist/analyzer/prompts/refactor.d.ts +3 -0
  14. package/dist/analyzer/prompts/refactor.d.ts.map +1 -0
  15. package/dist/analyzer/prompts/refactor.js +19 -0
  16. package/dist/analyzer/purposeDetector.d.ts +4 -0
  17. package/dist/analyzer/purposeDetector.d.ts.map +1 -0
  18. package/dist/analyzer/purposeDetector.js +141 -0
  19. package/dist/analyzer/readinessAnalyzer.d.ts +4 -0
  20. package/dist/analyzer/readinessAnalyzer.d.ts.map +1 -0
  21. package/dist/analyzer/readinessAnalyzer.js +111 -0
  22. package/dist/analyzer/repositoryAnalyzer.d.ts +4 -0
  23. package/dist/analyzer/repositoryAnalyzer.d.ts.map +1 -0
  24. package/dist/analyzer/repositoryAnalyzer.js +231 -0
  25. package/dist/analyzer/rules.d.ts +3 -0
  26. package/dist/analyzer/rules.d.ts.map +1 -0
  27. package/dist/analyzer/rules.js +156 -0
  28. package/dist/analyzer/types.d.ts +70 -0
  29. package/dist/analyzer/types.d.ts.map +1 -0
  30. package/dist/analyzer/types.js +1 -0
  31. package/dist/analyzer/workflowGenerator.d.ts +3 -0
  32. package/dist/analyzer/workflowGenerator.d.ts.map +1 -0
  33. package/dist/analyzer/workflowGenerator.js +22 -0
  34. package/dist/filters/shouldIgnore.d.ts +2 -0
  35. package/dist/filters/shouldIgnore.d.ts.map +1 -0
  36. package/dist/filters/shouldIgnore.js +10 -0
  37. package/dist/formatter/generateRepositoryContext.d.ts +10 -0
  38. package/dist/formatter/generateRepositoryContext.d.ts.map +1 -0
  39. package/dist/formatter/generateRepositoryContext.js +311 -0
  40. package/dist/index.d.ts +13 -0
  41. package/dist/index.d.ts.map +1 -0
  42. package/dist/index.js +12 -0
  43. package/dist/prioritizer/scoreFile.d.ts +3 -0
  44. package/dist/prioritizer/scoreFile.d.ts.map +1 -0
  45. package/dist/prioritizer/scoreFile.js +25 -0
  46. package/dist/prioritizer/sortFiles.d.ts +3 -0
  47. package/dist/prioritizer/sortFiles.d.ts.map +1 -0
  48. package/dist/prioritizer/sortFiles.js +9 -0
  49. package/dist/scanner/importanceDetector.d.ts +3 -0
  50. package/dist/scanner/importanceDetector.d.ts.map +1 -0
  51. package/dist/scanner/importanceDetector.js +70 -0
  52. package/dist/scanner/scanFiles.d.ts +3 -0
  53. package/dist/scanner/scanFiles.d.ts.map +1 -0
  54. package/dist/scanner/scanFiles.js +127 -0
  55. package/dist/summarizer/extractPatterns.d.ts +12 -0
  56. package/dist/summarizer/extractPatterns.d.ts.map +1 -0
  57. package/dist/summarizer/extractPatterns.js +83 -0
  58. package/dist/summarizer/flowDetector.d.ts +6 -0
  59. package/dist/summarizer/flowDetector.d.ts.map +1 -0
  60. package/dist/summarizer/flowDetector.js +29 -0
  61. package/dist/summarizer/index.d.ts +5 -0
  62. package/dist/summarizer/index.d.ts.map +1 -0
  63. package/dist/summarizer/index.js +25 -0
  64. package/dist/summarizer/inferPurpose.d.ts +10 -0
  65. package/dist/summarizer/inferPurpose.d.ts.map +1 -0
  66. package/dist/summarizer/inferPurpose.js +162 -0
  67. package/dist/summarizer/summarizeDirectory.d.ts +3 -0
  68. package/dist/summarizer/summarizeDirectory.d.ts.map +1 -0
  69. package/dist/summarizer/summarizeDirectory.js +87 -0
  70. package/dist/summarizer/summarizeFile.d.ts +4 -0
  71. package/dist/summarizer/summarizeFile.d.ts.map +1 -0
  72. package/dist/summarizer/summarizeFile.js +45 -0
  73. package/dist/summarizer/types.d.ts +35 -0
  74. package/dist/summarizer/types.d.ts.map +1 -0
  75. package/dist/summarizer/types.js +1 -0
  76. package/dist/tokenizer/estimateTokens.d.ts +2 -0
  77. package/dist/tokenizer/estimateTokens.d.ts.map +1 -0
  78. package/dist/tokenizer/estimateTokens.js +3 -0
  79. package/dist/tokenizer/estimateWords.d.ts +2 -0
  80. package/dist/tokenizer/estimateWords.d.ts.map +1 -0
  81. package/dist/tokenizer/estimateWords.js +6 -0
  82. package/package.json +28 -0
@@ -0,0 +1,4 @@
1
+ import { ScannedFile } from "@codemelt/shared";
2
+ import { CompressionStats } from "./types.js";
3
+ export declare function calculateCompression(files: ScannedFile[], totalFilesCount: number, ignoredCount: number, ignoredBytes?: number): CompressionStats;
4
+ //# sourceMappingURL=compressionAnalyzer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"compressionAnalyzer.d.ts","sourceRoot":"","sources":["../../src/analyzer/compressionAnalyzer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,WAAW,EAAE,EACpB,eAAe,EAAE,MAAM,EACvB,YAAY,EAAE,MAAM,EACpB,YAAY,GAAE,MAAU,GACvB,gBAAgB,CAyBlB"}
@@ -0,0 +1,21 @@
1
+ export function calculateCompression(files, totalFilesCount, ignoredCount, ignoredBytes = 0) {
2
+ const originalFilesCount = totalFilesCount;
3
+ // Filters active included files
4
+ const includedFiles = files.filter((f) => f.included);
5
+ const compressedFilesCount = includedFiles.length;
6
+ const totalScannedBytes = files.reduce((acc, f) => acc + f.size, 0);
7
+ const totalIncludedBytes = includedFiles.reduce((acc, f) => acc + f.size, 0);
8
+ // Strictly use real ignoredBytes
9
+ const originalBytes = totalScannedBytes + ignoredBytes;
10
+ const compressedBytes = totalIncludedBytes;
11
+ const savingsPercentage = originalBytes > 0 && ignoredBytes > 0
12
+ ? Math.max(0, Math.min(99.9, Number(((ignoredBytes / originalBytes) * 100).toFixed(1))))
13
+ : 0;
14
+ return {
15
+ originalBytes,
16
+ compressedBytes,
17
+ originalFilesCount,
18
+ compressedFilesCount,
19
+ savingsPercentage,
20
+ };
21
+ }
@@ -0,0 +1,3 @@
1
+ export * from "./repositoryAnalyzer.js";
2
+ export * from "./types.js";
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/analyzer/index.ts"],"names":[],"mappings":"AAAA,cAAc,yBAAyB,CAAC;AACxC,cAAc,YAAY,CAAC"}
@@ -0,0 +1,2 @@
1
+ export * from "./repositoryAnalyzer.js";
2
+ export * from "./types.js";
@@ -0,0 +1,3 @@
1
+ import { ArchitectureType, DetectionResult } from "../types.js";
2
+ export declare function getOnboardingPrompt(architecture: ArchitectureType, technologies: DetectionResult[]): string;
3
+ //# sourceMappingURL=onboarding.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"onboarding.d.ts","sourceRoot":"","sources":["../../../src/analyzer/prompts/onboarding.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEhE,wBAAgB,mBAAmB,CACjC,YAAY,EAAE,gBAAgB,EAC9B,YAAY,EAAE,eAAe,EAAE,GAC9B,MAAM,CAgBR"}
@@ -0,0 +1,14 @@
1
+ export function getOnboardingPrompt(architecture, technologies) {
2
+ const techNames = technologies.map(t => t.name);
3
+ const archLabel = architecture === "unknown" ? "software codebase" : `${architecture} repository`;
4
+ let prompt = `You are a staff engineer onboarding onto this ${archLabel}.\n`;
5
+ if (techNames.length > 0) {
6
+ prompt += `The technology stack includes: ${techNames.join(", ")}.\n`;
7
+ }
8
+ prompt += `\nReview the provided repository context and compile an onboarding document containing:
9
+ 1. **Architectural Overview**: Explain the high-level data flow and structure.
10
+ 2. **Key Directory Maps**: Explain what each folder is responsible for.
11
+ 3. **Local Setup Guide**: Map out the steps I need to take to compile, configure, and boot the application locally based on the configurations and dependencies present.
12
+ 4. **Initial Code Tracing**: Identify where the critical entrypoints, schemas, or main routes are located to start reading code.`;
13
+ return prompt;
14
+ }
@@ -0,0 +1,3 @@
1
+ import { ArchitectureType, DetectionResult } from "../types.js";
2
+ export declare function getPerformancePrompt(architecture: ArchitectureType, technologies: DetectionResult[]): string;
3
+ //# sourceMappingURL=performance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"performance.d.ts","sourceRoot":"","sources":["../../../src/analyzer/prompts/performance.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEhE,wBAAgB,oBAAoB,CAClC,YAAY,EAAE,gBAAgB,EAC9B,YAAY,EAAE,eAAe,EAAE,GAC9B,MAAM,CAqBR"}
@@ -0,0 +1,18 @@
1
+ export function getPerformancePrompt(architecture, technologies) {
2
+ const techNames = technologies.map(t => t.name);
3
+ let prompt = `You are a performance optimization expert auditing a repository built on a ${architecture} architecture.\n`;
4
+ if (techNames.includes("React")) {
5
+ prompt += `- React: Evaluate component render cycles, state dependencies, hook dependencies (useEffect, useMemo, useCallback), and virtualized rendering of long lists.\n`;
6
+ }
7
+ if (techNames.includes("Tailwind CSS")) {
8
+ prompt += `- Styling: Identify redundant Tailwind classes, class composition overhead, or layout thrashing.\n`;
9
+ }
10
+ if (techNames.includes("PostgreSQL") || techNames.includes("Prisma (ORM)")) {
11
+ prompt += `- DB Performance: Assess query join patterns, missing indexes on schemas, transaction sizes, or N+1 query scenarios.\n`;
12
+ }
13
+ prompt += `\nReview the provided files and supply a Performance Optimization Plan:
14
+ 1. **Bottleneck Audit**: Point out specific lines, functions, or queries that will trigger performance bottlenecks.
15
+ 2. **Computational Load**: Flag any CPU-heavy work running on the browser main-thread.
16
+ 3. **Optimized Snippets**: Provide exact code upgrades showing how to rewrite the code (e.g. implementing React memoization, caching filters, or indexing database models) to ensure near-zero latency.`;
17
+ return prompt;
18
+ }
@@ -0,0 +1,3 @@
1
+ import { ArchitectureType, DetectionResult } from "../types.js";
2
+ export declare function getRefactorPrompt(architecture: ArchitectureType, technologies: DetectionResult[]): string;
3
+ //# sourceMappingURL=refactor.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"refactor.d.ts","sourceRoot":"","sources":["../../../src/analyzer/prompts/refactor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,eAAe,EAAE,MAAM,aAAa,CAAC;AAEhE,wBAAgB,iBAAiB,CAC/B,YAAY,EAAE,gBAAgB,EAC9B,YAAY,EAAE,eAAe,EAAE,GAC9B,MAAM,CAsBR"}
@@ -0,0 +1,19 @@
1
+ export function getRefactorPrompt(architecture, technologies) {
2
+ const techNames = technologies.map(t => t.name);
3
+ const archLabel = architecture === "unknown" ? "codebase" : `${architecture}`;
4
+ let prompt = `You are a senior software architect conducting a code quality and refactoring audit of this ${archLabel} repository.\n`;
5
+ if (techNames.includes("Prisma (ORM)")) {
6
+ prompt += `- Database: Highlight database schema optimizations, schema models, raw query optimizations, and connection-pool management strategies for Prisma ORM.\n`;
7
+ }
8
+ if (techNames.includes("Next.js")) {
9
+ prompt += `- Next.js: Check usage of Server vs Client Components, layout nesting logic, routing architecture, and caching strategies.\n`;
10
+ }
11
+ if (techNames.includes("Zustand") || techNames.includes("Redux")) {
12
+ prompt += `- State Management: Assess how state stores are partitioned, how selectors are declared, and if state transitions are optimized and atomic.\n`;
13
+ }
14
+ prompt += `\nThoroughly review the provided context and deliver:
15
+ 1. **Design Pattern Violations**: Note any major anti-patterns (e.g., tight coupling, god objects, logic in views).
16
+ 2. **Top 3 Refactoring Candidates**: Identify three specific files or modules that should be decoupled or updated, detailing the precise rationale.
17
+ 3. **Refactored Code Blueprints**: Provide drop-in, type-safe replacement code illustrating how the refactored modules should be written.`;
18
+ return prompt;
19
+ }
@@ -0,0 +1,4 @@
1
+ import { ScannedFile } from "@codemelt/shared";
2
+ import { PurposeResult } from "./types.js";
3
+ export declare function detectPurpose(files: ScannedFile[], dependencies: Set<string>): PurposeResult;
4
+ //# sourceMappingURL=purposeDetector.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"purposeDetector.d.ts","sourceRoot":"","sources":["../../src/analyzer/purposeDetector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,aAAa,EAAqB,MAAM,YAAY,CAAC;AAE9D,wBAAgB,aAAa,CAC3B,KAAK,EAAE,WAAW,EAAE,EACpB,YAAY,EAAE,GAAG,CAAC,MAAM,CAAC,GACxB,aAAa,CA6Jf"}
@@ -0,0 +1,141 @@
1
+ export function detectPurpose(files, dependencies) {
2
+ const scores = {
3
+ "developer-tooling": 0.0,
4
+ "saas-dashboard": 0.0,
5
+ "chat-application": 0.0,
6
+ "ecommerce-platform": 0.0,
7
+ "cms": 0.0,
8
+ "portfolio": 0.0,
9
+ "api-platform": 0.0,
10
+ "unknown": 0.0,
11
+ };
12
+ const signals = {
13
+ "developer-tooling": [],
14
+ "saas-dashboard": [],
15
+ "chat-application": [],
16
+ "ecommerce-platform": [],
17
+ "cms": [],
18
+ "portfolio": [],
19
+ "api-platform": [],
20
+ "unknown": [],
21
+ };
22
+ const hasFile = (name) => files.some((f) => f.name.toLowerCase() === name.toLowerCase());
23
+ const hasDirectory = (dirName) => files.some((f) => f.path.includes(`/${dirName}/`) || f.path.startsWith(`${dirName}/`));
24
+ // --- Heuristic Signals Mapping ---
25
+ // 1. Developer Tooling Signals
26
+ const genericDevDeps = ["typescript", "eslint", "prettier"];
27
+ const specializedDevDeps = ["commander", "yargs", "tsup", "esbuild"];
28
+ for (const dep of genericDevDeps) {
29
+ if (dependencies.has(dep)) {
30
+ scores["developer-tooling"] += 0.05;
31
+ signals["developer-tooling"].push(`dependency:${dep}`);
32
+ }
33
+ }
34
+ for (const dep of specializedDevDeps) {
35
+ if (dependencies.has(dep)) {
36
+ scores["developer-tooling"] += 0.25;
37
+ signals["developer-tooling"].push(`dependency:${dep}`);
38
+ }
39
+ }
40
+ const hasCliFolder = files.some((f) => f.path.includes("/cli/") ||
41
+ f.path.startsWith("cli/") ||
42
+ f.path.includes("/bin/") ||
43
+ f.path.startsWith("bin/"));
44
+ if (hasDirectory("scripts") || hasDirectory("bin") || hasDirectory("cli") || hasCliFolder || hasFile("eslint.config.js") || hasFile("cli.ts")) {
45
+ scores["developer-tooling"] += 0.35;
46
+ signals["developer-tooling"].push("file:tooling-configs");
47
+ }
48
+ // 2. SaaS Dashboard Signals
49
+ const saasDeps = ["recharts", "chart.js", "d3", "stripe", "@stripe/stripe-js"];
50
+ for (const dep of saasDeps) {
51
+ if (dependencies.has(dep)) {
52
+ scores["saas-dashboard"] += 0.35;
53
+ signals["saas-dashboard"].push(`dependency:${dep}`);
54
+ }
55
+ }
56
+ if (hasDirectory("dashboard") || hasDirectory("admin") || hasDirectory("billing") || hasDirectory("payments")) {
57
+ scores["saas-dashboard"] += 0.40;
58
+ signals["saas-dashboard"].push("folder:management-routes");
59
+ }
60
+ // 3. Chat Application Signals
61
+ const chatDeps = ["socket.io", "socket.io-client", "pusher", "pusher-js", "ws"];
62
+ for (const dep of chatDeps) {
63
+ if (dependencies.has(dep)) {
64
+ scores["chat-application"] += 0.45;
65
+ signals["chat-application"].push(`dependency:${dep}`);
66
+ }
67
+ }
68
+ if (hasDirectory("chat") || hasDirectory("messages") || hasDirectory("rooms")) {
69
+ scores["chat-application"] += 0.45;
70
+ signals["chat-application"].push("folder:communications");
71
+ }
72
+ // 4. Ecommerce Platform Signals
73
+ if (dependencies.has("stripe") || dependencies.has("@stripe/stripe-js") || dependencies.has("@shopify/shopify-api")) {
74
+ scores["ecommerce-platform"] += 0.45;
75
+ signals["ecommerce-platform"].push("dependency:billing-client");
76
+ }
77
+ if (hasDirectory("cart") || hasDirectory("checkout") || hasDirectory("products") || hasDirectory("orders")) {
78
+ scores["ecommerce-platform"] += 0.45;
79
+ signals["ecommerce-platform"].push("folder:checkout-funnel");
80
+ }
81
+ // 5. CMS / Static Blog Signals
82
+ const cmsDeps = ["strapi", "contentful", "@sanity/client", "ghost", "wordpress"];
83
+ for (const dep of cmsDeps) {
84
+ if (dependencies.has(dep)) {
85
+ scores["cms"] += 0.50;
86
+ signals["cms"].push(`dependency:${dep}`);
87
+ }
88
+ }
89
+ if (hasDirectory("posts") || hasDirectory("content") || hasDirectory("blog") || hasDirectory("articles")) {
90
+ scores["cms"] += 0.35;
91
+ signals["cms"].push("folder:content-data");
92
+ }
93
+ // 6. Portfolio Signals
94
+ if (hasFile("portfolio") || hasFile("resume.pdf") || hasDirectory("portfolio")) {
95
+ scores["portfolio"] += 0.60;
96
+ signals["portfolio"].push("file:portfolio-assets");
97
+ }
98
+ // 7. API Platform Signals
99
+ const apiDeps = ["express", "@nestjs/core", "fastify", "koa", "swagger-ui-express", "@tuner/trpc"];
100
+ for (const dep of apiDeps) {
101
+ if (dependencies.has(dep)) {
102
+ scores["api-platform"] += 0.45;
103
+ signals["api-platform"].push(`dependency:${dep}`);
104
+ }
105
+ }
106
+ if (hasDirectory("routes") || hasDirectory("controllers") || hasDirectory("api") || hasDirectory("endpoints")) {
107
+ scores["api-platform"] += 0.40;
108
+ signals["api-platform"].push("folder:routing-handlers");
109
+ }
110
+ // --- Resolve highest scoring purpose ---
111
+ let bestPurpose = "unknown";
112
+ let maxScore = 0.0;
113
+ for (const [purpose, score] of Object.entries(scores)) {
114
+ if (score > maxScore) {
115
+ maxScore = score;
116
+ bestPurpose = purpose;
117
+ }
118
+ }
119
+ const finalConfidence = Math.min(maxScore, 1.0);
120
+ const getConfidenceTier = (score) => {
121
+ if (score >= 0.8)
122
+ return "Strong";
123
+ if (score >= 0.5)
124
+ return "Moderate";
125
+ return "Low";
126
+ };
127
+ if (finalConfidence < 0.25) {
128
+ return {
129
+ name: "unknown",
130
+ confidence: 0.0,
131
+ confidenceTier: "Low",
132
+ matchedSignals: [],
133
+ };
134
+ }
135
+ return {
136
+ name: bestPurpose,
137
+ confidence: finalConfidence,
138
+ confidenceTier: getConfidenceTier(finalConfidence),
139
+ matchedSignals: signals[bestPurpose] || [],
140
+ };
141
+ }
@@ -0,0 +1,4 @@
1
+ import { ScannedFile } from "@codemelt/shared";
2
+ import { AIReadinessScore } from "./types.js";
3
+ export declare function analyzeReadiness(files: ScannedFile[], ignoredCount: number): AIReadinessScore;
4
+ //# sourceMappingURL=readinessAnalyzer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"readinessAnalyzer.d.ts","sourceRoot":"","sources":["../../src/analyzer/readinessAnalyzer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,gBAAgB,EAAE,MAAM,YAAY,CAAC;AAE9C,wBAAgB,gBAAgB,CAC9B,KAAK,EAAE,WAAW,EAAE,EACpB,YAAY,EAAE,MAAM,GACnB,gBAAgB,CAiHlB"}
@@ -0,0 +1,111 @@
1
+ export function analyzeReadiness(files, ignoredCount) {
2
+ let documentation = 0;
3
+ let typingQuality = 0;
4
+ let structureClarity = 0;
5
+ let configCompleteness = 0;
6
+ let contextOptimization = 0;
7
+ const recommendations = [];
8
+ // Helper check methods
9
+ const hasFile = (name) => files.some((f) => f.name.toLowerCase() === name.toLowerCase());
10
+ const hasExtension = (ext) => files.some((f) => f.extension.toLowerCase() === ext.toLowerCase());
11
+ const hasDirectory = (dirName) => files.some((f) => f.path.includes(`/${dirName}/`) || f.path.startsWith(`${dirName}/`));
12
+ // 1. Documentation Scoring (Max 20)
13
+ const hasReadme = files.some((f) => f.name.toLowerCase() === "readme.md" ||
14
+ f.name.toLowerCase() === "readme.mdx" ||
15
+ f.name.toLowerCase() === "readme.txt" ||
16
+ f.name.toLowerCase() === "readme");
17
+ if (hasReadme) {
18
+ documentation += 10;
19
+ }
20
+ else {
21
+ recommendations.push("Add a README.md file at the repository root to give AI models structural context.");
22
+ }
23
+ if (hasFile("contributing.md") || hasFile("api.md") || hasDirectory("docs")) {
24
+ documentation += 10;
25
+ }
26
+ else {
27
+ recommendations.push("Provide secondary onboarding guides (docs/ or CONTRIBUTING.md) to explain local builds.");
28
+ }
29
+ // 2. Typing Quality Scoring (Max 20)
30
+ if (hasFile("tsconfig.json")) {
31
+ typingQuality += 15;
32
+ }
33
+ else {
34
+ recommendations.push("Transition to TypeScript or add a tsconfig.json to supply compile-time types for AI reasoning.");
35
+ }
36
+ if (hasExtension("ts") || hasExtension("tsx") || hasExtension("d.ts")) {
37
+ typingQuality += 5;
38
+ }
39
+ else {
40
+ recommendations.push("Write structured typings for data entities instead of relying on loose JS any types.");
41
+ }
42
+ // 3. Structure Clarity Scoring (Max 20)
43
+ const standardizedDirs = ["src", "components", "lib", "app", "pages", "routes", "controllers", "services"];
44
+ const matchedDirsCount = standardizedDirs.filter(dir => hasDirectory(dir)).length;
45
+ if (matchedDirsCount >= 2) {
46
+ structureClarity += 15;
47
+ }
48
+ else if (matchedDirsCount === 1) {
49
+ structureClarity += 8;
50
+ recommendations.push("Introduce conventional directory subdivisions (e.g. src/lib, src/components) to separate concerns.");
51
+ }
52
+ else {
53
+ recommendations.push("Organize files into specialized directories instead of keeping flat structures in the root folder.");
54
+ }
55
+ // File organization balance check
56
+ const rootFilesCount = files.filter(f => !f.path.includes("/")).length;
57
+ if (files.length > 5 && rootFilesCount / files.length < 0.4) {
58
+ structureClarity += 5;
59
+ }
60
+ else if (files.length > 5) {
61
+ recommendations.push("Avoid cluttering the root folder with massive amounts of flat source files.");
62
+ }
63
+ else {
64
+ structureClarity += 5;
65
+ }
66
+ // 4. Config Completeness Scoring (Max 20)
67
+ if (hasFile(".gitignore")) {
68
+ configCompleteness += 10;
69
+ }
70
+ else {
71
+ recommendations.push("Include a .gitignore to inform tools which pathways are safe to ignore by default.");
72
+ }
73
+ if (hasFile(".env.example") || hasFile(".env.template") || hasFile(".env.local.example")) {
74
+ configCompleteness += 5;
75
+ }
76
+ else {
77
+ recommendations.push("Add a .env.example file detailing active environment variable structures.");
78
+ }
79
+ if (hasFile(".eslintrc.json") || hasFile("eslint.config.js") || hasFile(".prettierrc") || hasFile("tsconfig.json")) {
80
+ configCompleteness += 5;
81
+ }
82
+ // 5. Context Optimization Scoring (Max 20)
83
+ // Ensure huge lockfiles are not inside included scanned files list
84
+ const hasLockfilesInScan = files.some(f => f.name === "package-lock.json" ||
85
+ f.name === "yarn.lock" ||
86
+ f.name === "pnpm-lock.yaml");
87
+ if (!hasLockfilesInScan) {
88
+ contextOptimization += 10;
89
+ }
90
+ else {
91
+ recommendations.push("Ensure large package lockfiles are excluded from scans to save 80%+ context space.");
92
+ }
93
+ if (ignoredCount > 0) {
94
+ contextOptimization += 10;
95
+ }
96
+ else {
97
+ recommendations.push("Set active filter/ignore configs to prune compile and distribution noise folders.");
98
+ }
99
+ const score = documentation + typingQuality + structureClarity + configCompleteness + contextOptimization;
100
+ return {
101
+ score,
102
+ breakdown: {
103
+ documentation,
104
+ typingQuality,
105
+ structureClarity,
106
+ configCompleteness,
107
+ contextOptimization,
108
+ },
109
+ recommendations: recommendations.slice(0, 4), // Cap at 4 actionable items to avoid UI clutter
110
+ };
111
+ }
@@ -0,0 +1,4 @@
1
+ import { ScannedFile } from "@codemelt/shared";
2
+ import { ProjectAnalysis } from "./types.js";
3
+ export declare function analyzeRepository(files: ScannedFile[], totalFilesCount?: number, ignoredCount?: number, ignoredBytes?: number): ProjectAnalysis;
4
+ //# sourceMappingURL=repositoryAnalyzer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"repositoryAnalyzer.d.ts","sourceRoot":"","sources":["../../src/analyzer/repositoryAnalyzer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAE/C,OAAO,EAAE,eAAe,EAAwD,MAAM,YAAY,CAAC;AAoBnG,wBAAgB,iBAAiB,CAC/B,KAAK,EAAE,WAAW,EAAE,EACpB,eAAe,GAAE,MAAqB,EACtC,YAAY,GAAE,MAAU,EACxB,YAAY,GAAE,MAAU,GACvB,eAAe,CA2IjB"}
@@ -0,0 +1,231 @@
1
+ import { TECHNOLOGY_RULES } from "./rules.js";
2
+ import { calculateCompression } from "./compressionAnalyzer.js";
3
+ import { analyzeReadiness } from "./readinessAnalyzer.js";
4
+ import { generateWorkflows } from "./workflowGenerator.js";
5
+ import { detectPurpose } from "./purposeDetector.js";
6
+ import { analyzeSemanticRepository } from "../summarizer/index.js";
7
+ function getConfidenceTier(score) {
8
+ if (score >= 0.8)
9
+ return "Strong";
10
+ if (score >= 0.5)
11
+ return "Moderate";
12
+ return "Low";
13
+ }
14
+ export function analyzeRepository(files, totalFilesCount = files.length, ignoredCount = 0, ignoredBytes = 0) {
15
+ const detections = new Map();
16
+ const parsedPackages = [];
17
+ // 1. Locate and parse EVERY package.json in the scanned files list
18
+ for (const file of files) {
19
+ if (file.name === "package.json" && file.type === "text") {
20
+ try {
21
+ const parsed = JSON.parse(file.content);
22
+ parsedPackages.push({
23
+ path: file.path,
24
+ dependencies: parsed.dependencies || {},
25
+ devDependencies: parsed.devDependencies || {},
26
+ });
27
+ }
28
+ catch {
29
+ // Safe fallback for broken or partially uploaded package.json files
30
+ }
31
+ }
32
+ }
33
+ // Helper: check if dependency exists across any package.json
34
+ const findDependency = (depName) => {
35
+ const matches = [];
36
+ for (const pkg of parsedPackages) {
37
+ if (pkg.dependencies[depName]) {
38
+ matches.push({ version: pkg.dependencies[depName], pkgPath: pkg.path });
39
+ }
40
+ else if (pkg.devDependencies[depName]) {
41
+ matches.push({ version: pkg.devDependencies[depName], pkgPath: pkg.path });
42
+ }
43
+ }
44
+ return matches;
45
+ };
46
+ // Helper: find any files matching pattern
47
+ const findMatchingFiles = (pattern) => {
48
+ return files
49
+ .filter((f) => f.path.endsWith(pattern) || f.path.includes(pattern))
50
+ .map((f) => f.path);
51
+ };
52
+ // 2. Rules Pipeline Evaluator
53
+ for (const rule of TECHNOLOGY_RULES) {
54
+ let score = 0.0;
55
+ const matchedDeps = [];
56
+ const matchedFiles = [];
57
+ const matchedBy = [];
58
+ let detectedVersion = undefined;
59
+ // Check Dependency Signatures
60
+ if (rule.dependencies) {
61
+ for (const dep of rule.dependencies) {
62
+ const matches = findDependency(dep);
63
+ if (matches.length > 0) {
64
+ matchedDeps.push(dep);
65
+ if (!detectedVersion) {
66
+ detectedVersion = matches[0].version; // Grab first resolved version
67
+ }
68
+ }
69
+ }
70
+ if (matchedDeps.length > 0) {
71
+ score += 0.70; // Dependency matches provide 0.70 confidence
72
+ matchedBy.push("dependency");
73
+ }
74
+ }
75
+ // Check File Configuration Signatures
76
+ if (rule.filePatterns) {
77
+ for (const pattern of rule.filePatterns) {
78
+ const found = findMatchingFiles(pattern);
79
+ if (found.length > 0) {
80
+ matchedFiles.push(...found);
81
+ }
82
+ }
83
+ if (matchedFiles.length > 0) {
84
+ score += 0.30; // File patterns provide a 0.30 confidence boost
85
+ matchedBy.push("file-pattern");
86
+ }
87
+ }
88
+ // If we have any active matches, register the detection
89
+ if (score > 0) {
90
+ const finalScore = Math.min(score, 1.0); // Cap at 1.0 (absolute confidence)
91
+ detections.set(rule.name, {
92
+ name: rule.name,
93
+ category: rule.category,
94
+ version: detectedVersion,
95
+ confidence: finalScore,
96
+ confidenceTier: getConfidenceTier(finalScore),
97
+ matchedBy,
98
+ explainability: {
99
+ matchedDependencies: matchedDeps,
100
+ matchedFiles: matchedFiles,
101
+ },
102
+ });
103
+ }
104
+ }
105
+ // 3. Sub-modules Orchestration
106
+ const technologies = Array.from(detections.values());
107
+ const architecture = detectArchitecture(files, parsedPackages, detections);
108
+ // Extract parsed dependencies set for purpose detection
109
+ const dependenciesSet = new Set();
110
+ for (const pkg of parsedPackages) {
111
+ Object.keys(pkg.dependencies).forEach(dep => dependenciesSet.add(dep));
112
+ Object.keys(pkg.devDependencies).forEach(dep => dependenciesSet.add(dep));
113
+ }
114
+ const purpose = detectPurpose(files, dependenciesSet);
115
+ const readinessScore = analyzeReadiness(files, ignoredCount);
116
+ const prompts = generateWorkflows(architecture, technologies);
117
+ const compression = calculateCompression(files, totalFilesCount, ignoredCount, ignoredBytes);
118
+ // Compute file count per importance level cleanly
119
+ const importanceStats = {
120
+ critical: files.filter(f => f.importance === "critical").length,
121
+ high: files.filter(f => f.importance === "high").length,
122
+ normal: files.filter(f => f.importance === "normal").length,
123
+ low: files.filter(f => f.importance === "low").length,
124
+ };
125
+ const semanticAnalysis = analyzeSemanticRepository(files);
126
+ const totalSize = files.reduce((acc, f) => acc + f.size, 0);
127
+ const summary = generateSummaryText(technologies, architecture, purpose.name, files.length, totalSize);
128
+ return {
129
+ technologies,
130
+ architecture,
131
+ purpose,
132
+ readinessScore,
133
+ prompts,
134
+ compression,
135
+ summary,
136
+ fileCount: files.length,
137
+ totalSize,
138
+ importanceStats,
139
+ semanticAnalysis,
140
+ };
141
+ }
142
+ function detectArchitecture(files, parsedPackages, detections) {
143
+ // 1. Monorepo Detection with strict evidence
144
+ const hasWorkspaceConfig = files.some((f) => f.name === "turbo.json" ||
145
+ f.name === "nx.json" ||
146
+ f.name === "lerna.json" ||
147
+ f.name === "pnpm-workspace.yaml");
148
+ let hasRootWorkspaces = false;
149
+ for (const file of files) {
150
+ if (file.name === "package.json" && (file.path === "package.json" || file.path === "./package.json") && file.type === "text") {
151
+ try {
152
+ const parsed = JSON.parse(file.content);
153
+ if (parsed.workspaces) {
154
+ hasRootWorkspaces = true;
155
+ }
156
+ }
157
+ catch { }
158
+ }
159
+ }
160
+ const hasSubpackageJson = parsedPackages.some((pkg) => pkg.path.includes("packages/") ||
161
+ pkg.path.includes("apps/") ||
162
+ pkg.path.includes("libs/"));
163
+ if (parsedPackages.length > 1 && (hasWorkspaceConfig || hasRootWorkspaces || hasSubpackageJson)) {
164
+ return "monorepo";
165
+ }
166
+ const hasFramework = (name) => detections.has(name);
167
+ const hasCategory = (cat) => Array.from(detections.values()).some((d) => d.category === cat);
168
+ // 2. Fullstack Monolith (Next.js, Nuxt/Vue, SvelteKit + Database layers)
169
+ const isFullstackFramework = hasFramework("Next.js") || hasFramework("Svelte / SvelteKit") || hasFramework("Vue.js");
170
+ const hasDb = hasCategory("database");
171
+ if (isFullstackFramework && hasDb) {
172
+ return "fullstack-monolith";
173
+ }
174
+ // 3. Backend API
175
+ const isBackendFramework = hasFramework("Express.js") || hasFramework("NestJS");
176
+ if (isBackendFramework && hasDb && !hasFramework("React")) {
177
+ return "backend-api";
178
+ }
179
+ // 4. Realtime Systems
180
+ if (hasCategory("realtime")) {
181
+ return "realtime-system";
182
+ }
183
+ // 5. Frontend Only
184
+ if ((hasFramework("React") || hasFramework("Vue.js") || hasFramework("Angular")) && !hasDb) {
185
+ return "frontend-only";
186
+ }
187
+ return "unknown";
188
+ }
189
+ function generateSummaryText(technologies, architecture, purpose, fileCount, totalSize) {
190
+ const frameworks = technologies.filter((t) => t.category === "framework").map((t) => t.name);
191
+ const databases = technologies.filter((t) => t.category === "database").map((t) => t.name);
192
+ const styling = technologies.filter((t) => t.category === "styling").map((t) => t.name);
193
+ const archLabels = {
194
+ "monorepo": "Monorepo Workspace",
195
+ "fullstack-monolith": "Fullstack Monolith",
196
+ "frontend-only": "Frontend Application",
197
+ "backend-api": "Backend API Service",
198
+ "realtime-system": "Realtime Application",
199
+ "unknown": "Software Repository"
200
+ };
201
+ const purposeLabels = {
202
+ "developer-tooling": "Developer Tooling Project",
203
+ "saas-dashboard": "SaaS Dashboard Portal",
204
+ "chat-application": "Chat Application",
205
+ "ecommerce-platform": "Ecommerce Platform",
206
+ "cms": "Content Management System",
207
+ "portfolio": "Portfolio Website",
208
+ "api-platform": "API Platform Service",
209
+ "unknown": "Application"
210
+ };
211
+ let summary = `Scanned ${fileCount} files (${(totalSize / 1024 / 1024).toFixed(2)} MB). `;
212
+ if (purpose !== "unknown") {
213
+ summary += `This project is classified as a ${purposeLabels[purpose]} configured on a ${archLabels[architecture]} layout. `;
214
+ }
215
+ else {
216
+ summary += `The codebase is classified as a ${archLabels[architecture]}. `;
217
+ }
218
+ if (frameworks.length > 0) {
219
+ summary += `Project is built using ${frameworks.join(" / ")}. `;
220
+ }
221
+ else {
222
+ summary += `Project is structured as a basic JavaScript/TypeScript application. `;
223
+ }
224
+ if (databases.length > 0) {
225
+ summary += `Uses ${databases.join(" & ")} as the data layers. `;
226
+ }
227
+ if (styling.length > 0) {
228
+ summary += `Styling is implemented with ${styling.join(", ")}. `;
229
+ }
230
+ return summary;
231
+ }
@@ -0,0 +1,3 @@
1
+ import { HeuristicRule } from "./types.js";
2
+ export declare const TECHNOLOGY_RULES: HeuristicRule[];
3
+ //# sourceMappingURL=rules.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rules.d.ts","sourceRoot":"","sources":["../../src/analyzer/rules.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,YAAY,CAAC;AAE3C,eAAO,MAAM,gBAAgB,EAAE,aAAa,EAgK3C,CAAC"}