@aiready/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.
@@ -0,0 +1,61 @@
1
+
2
+ 
3
+ > @aiready/core@0.1.0 build /Users/pengcao/projects/aiready/packages/core
4
+ > tsup src/index.ts --format cjs,esm --dts
5
+
6
+ CLI Building entry: src/index.ts
7
+ CLI Using tsconfig: tsconfig.json
8
+ CLI tsup v8.5.1
9
+ CLI Target: es2020
10
+ CJS Build start
11
+ ESM Build start
12
+ DTS Build start
13
+
14
+ [12:59:20 AM]  WARN  ▲ [WARNING] The condition "types" here will never be used as it comes after both "import" and "require" [package.json]
15
+
16
+ package.json:12:6:
17
+  12 │ "types": "./dist/index.d.ts"
18
+ ╵ ~~~~~~~
19
+
20
+ The "import" condition comes earlier and will be used for all "import" statements:
21
+
22
+ package.json:11:6:
23
+  11 │ "import": "./dist/index.mjs",
24
+ ╵ ~~~~~~~~
25
+
26
+ The "require" condition comes earlier and will be used for all "require" calls:
27
+
28
+ package.json:10:6:
29
+  10 │ "require": "./dist/index.js",
30
+ ╵ ~~~~~~~~~
31
+
32
+
33
+
34
+
35
+ [12:59:20 AM]  WARN  ▲ [WARNING] The condition "types" here will never be used as it comes after both "import" and "require" [package.json]
36
+
37
+ package.json:12:6:
38
+  12 │ "types": "./dist/index.d.ts"
39
+ ╵ ~~~~~~~
40
+
41
+ The "import" condition comes earlier and will be used for all "import" statements:
42
+
43
+ package.json:11:6:
44
+  11 │ "import": "./dist/index.mjs",
45
+ ╵ ~~~~~~~~
46
+
47
+ The "require" condition comes earlier and will be used for all "require" calls:
48
+
49
+ package.json:10:6:
50
+  10 │ "require": "./dist/index.js",
51
+ ╵ ~~~~~~~~~
52
+
53
+
54
+
55
+ ESM dist/index.mjs 2.02 KB
56
+ ESM ⚡️ Build success in 273ms
57
+ CJS dist/index.js 3.42 KB
58
+ CJS ⚡️ Build success in 274ms
59
+ DTS ⚡️ Build success in 435ms
60
+ DTS dist/index.d.ts 2.48 KB
61
+ DTS dist/index.d.mts 2.48 KB
@@ -0,0 +1,13 @@
1
+
2
+ 
3
+ > @aiready/core@0.1.0 test /Users/pengcao/projects/aiready/packages/core
4
+ > vitest run
5
+
6
+
7
+  RUN  v2.1.9 /Users/pengcao/projects/aiready/packages/core
8
+
9
+ include: **/*.{test,spec}.?(c|m)[jt]s?(x)
10
+ exclude: **/node_modules/**, **/dist/**, **/cypress/**, **/.{idea,git,cache,output,temp}/**, **/{karma,rollup,webpack,vite,vitest,jest,ava,babel,nyc,cypress,tsup,build,eslint,prettier}.config.*
11
+ 
12
+ No test files found, exiting with code 1
13
+ [?25h ELIFECYCLE  Test failed. See above for more details.
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 AIReady Team
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1,85 @@
1
+ interface AnalysisResult {
2
+ fileName: string;
3
+ issues: Issue[];
4
+ metrics: Metrics;
5
+ }
6
+ interface Issue {
7
+ type: IssueType;
8
+ severity: 'critical' | 'major' | 'minor' | 'info';
9
+ message: string;
10
+ location: Location;
11
+ suggestion?: string;
12
+ }
13
+ type IssueType = 'duplicate-pattern' | 'context-fragmentation' | 'doc-drift' | 'naming-inconsistency' | 'dead-code' | 'circular-dependency' | 'missing-types';
14
+ interface Location {
15
+ file: string;
16
+ line: number;
17
+ column?: number;
18
+ endLine?: number;
19
+ endColumn?: number;
20
+ }
21
+ interface Metrics {
22
+ tokenCost?: number;
23
+ complexityScore?: number;
24
+ consistencyScore?: number;
25
+ docFreshnessScore?: number;
26
+ }
27
+ interface ScanOptions {
28
+ rootDir: string;
29
+ include?: string[];
30
+ exclude?: string[];
31
+ maxDepth?: number;
32
+ }
33
+ interface Report {
34
+ summary: {
35
+ totalFiles: number;
36
+ totalIssues: number;
37
+ criticalIssues: number;
38
+ majorIssues: number;
39
+ };
40
+ results: AnalysisResult[];
41
+ metrics: {
42
+ overallScore: number;
43
+ tokenCostTotal: number;
44
+ avgConsistency: number;
45
+ };
46
+ }
47
+
48
+ declare function scanFiles(options: ScanOptions): Promise<string[]>;
49
+ declare function readFileContent(filePath: string): Promise<string>;
50
+ declare function getFileExtension(filePath: string): string;
51
+ declare function isSourceFile(filePath: string): boolean;
52
+
53
+ interface ASTNode {
54
+ type: string;
55
+ loc?: {
56
+ start: {
57
+ line: number;
58
+ column: number;
59
+ };
60
+ end: {
61
+ line: number;
62
+ column: number;
63
+ };
64
+ };
65
+ }
66
+ declare function parseCode(code: string, language: string): ASTNode | null;
67
+ declare function extractFunctions(ast: ASTNode): ASTNode[];
68
+ declare function extractImports(ast: ASTNode): string[];
69
+
70
+ /**
71
+ * Estimate token count for text (rough approximation)
72
+ * ~1 token ≈ 4 characters for code
73
+ */
74
+ declare function estimateTokens(text: string): number;
75
+ /**
76
+ * Calculate Levenshtein distance between two strings
77
+ * Useful for similarity detection
78
+ */
79
+ declare function levenshteinDistance(str1: string, str2: string): number;
80
+ /**
81
+ * Calculate similarity score (0-1) between two strings
82
+ */
83
+ declare function similarityScore(str1: string, str2: string): number;
84
+
85
+ export { type ASTNode, type AnalysisResult, type Issue, type IssueType, type Location, type Metrics, type Report, type ScanOptions, estimateTokens, extractFunctions, extractImports, getFileExtension, isSourceFile, levenshteinDistance, parseCode, readFileContent, scanFiles, similarityScore };
@@ -0,0 +1,85 @@
1
+ interface AnalysisResult {
2
+ fileName: string;
3
+ issues: Issue[];
4
+ metrics: Metrics;
5
+ }
6
+ interface Issue {
7
+ type: IssueType;
8
+ severity: 'critical' | 'major' | 'minor' | 'info';
9
+ message: string;
10
+ location: Location;
11
+ suggestion?: string;
12
+ }
13
+ type IssueType = 'duplicate-pattern' | 'context-fragmentation' | 'doc-drift' | 'naming-inconsistency' | 'dead-code' | 'circular-dependency' | 'missing-types';
14
+ interface Location {
15
+ file: string;
16
+ line: number;
17
+ column?: number;
18
+ endLine?: number;
19
+ endColumn?: number;
20
+ }
21
+ interface Metrics {
22
+ tokenCost?: number;
23
+ complexityScore?: number;
24
+ consistencyScore?: number;
25
+ docFreshnessScore?: number;
26
+ }
27
+ interface ScanOptions {
28
+ rootDir: string;
29
+ include?: string[];
30
+ exclude?: string[];
31
+ maxDepth?: number;
32
+ }
33
+ interface Report {
34
+ summary: {
35
+ totalFiles: number;
36
+ totalIssues: number;
37
+ criticalIssues: number;
38
+ majorIssues: number;
39
+ };
40
+ results: AnalysisResult[];
41
+ metrics: {
42
+ overallScore: number;
43
+ tokenCostTotal: number;
44
+ avgConsistency: number;
45
+ };
46
+ }
47
+
48
+ declare function scanFiles(options: ScanOptions): Promise<string[]>;
49
+ declare function readFileContent(filePath: string): Promise<string>;
50
+ declare function getFileExtension(filePath: string): string;
51
+ declare function isSourceFile(filePath: string): boolean;
52
+
53
+ interface ASTNode {
54
+ type: string;
55
+ loc?: {
56
+ start: {
57
+ line: number;
58
+ column: number;
59
+ };
60
+ end: {
61
+ line: number;
62
+ column: number;
63
+ };
64
+ };
65
+ }
66
+ declare function parseCode(code: string, language: string): ASTNode | null;
67
+ declare function extractFunctions(ast: ASTNode): ASTNode[];
68
+ declare function extractImports(ast: ASTNode): string[];
69
+
70
+ /**
71
+ * Estimate token count for text (rough approximation)
72
+ * ~1 token ≈ 4 characters for code
73
+ */
74
+ declare function estimateTokens(text: string): number;
75
+ /**
76
+ * Calculate Levenshtein distance between two strings
77
+ * Useful for similarity detection
78
+ */
79
+ declare function levenshteinDistance(str1: string, str2: string): number;
80
+ /**
81
+ * Calculate similarity score (0-1) between two strings
82
+ */
83
+ declare function similarityScore(str1: string, str2: string): number;
84
+
85
+ export { type ASTNode, type AnalysisResult, type Issue, type IssueType, type Location, type Metrics, type Report, type ScanOptions, estimateTokens, extractFunctions, extractImports, getFileExtension, isSourceFile, levenshteinDistance, parseCode, readFileContent, scanFiles, similarityScore };
package/dist/index.js ADDED
@@ -0,0 +1,126 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/index.ts
21
+ var index_exports = {};
22
+ __export(index_exports, {
23
+ estimateTokens: () => estimateTokens,
24
+ extractFunctions: () => extractFunctions,
25
+ extractImports: () => extractImports,
26
+ getFileExtension: () => getFileExtension,
27
+ isSourceFile: () => isSourceFile,
28
+ levenshteinDistance: () => levenshteinDistance,
29
+ parseCode: () => parseCode,
30
+ readFileContent: () => readFileContent,
31
+ scanFiles: () => scanFiles,
32
+ similarityScore: () => similarityScore
33
+ });
34
+ module.exports = __toCommonJS(index_exports);
35
+
36
+ // src/utils/file-scanner.ts
37
+ var import_glob = require("glob");
38
+ var import_promises = require("fs/promises");
39
+ var DEFAULT_EXCLUDE = [
40
+ "**/node_modules/**",
41
+ "**/dist/**",
42
+ "**/build/**",
43
+ "**/.git/**",
44
+ "**/coverage/**",
45
+ "**/*.min.js",
46
+ "**/*.bundle.js"
47
+ ];
48
+ async function scanFiles(options) {
49
+ const {
50
+ rootDir,
51
+ include = ["**/*.{ts,tsx,js,jsx,py,java}"],
52
+ exclude = DEFAULT_EXCLUDE
53
+ } = options;
54
+ const files = await (0, import_glob.glob)(include, {
55
+ cwd: rootDir,
56
+ ignore: exclude,
57
+ absolute: true
58
+ });
59
+ return files;
60
+ }
61
+ async function readFileContent(filePath) {
62
+ return (0, import_promises.readFile)(filePath, "utf-8");
63
+ }
64
+ function getFileExtension(filePath) {
65
+ return filePath.split(".").pop() || "";
66
+ }
67
+ function isSourceFile(filePath) {
68
+ const ext = getFileExtension(filePath);
69
+ return ["ts", "tsx", "js", "jsx", "py", "java", "go", "rs"].includes(ext);
70
+ }
71
+
72
+ // src/utils/ast-parser.ts
73
+ function parseCode(code, language) {
74
+ return null;
75
+ }
76
+ function extractFunctions(ast) {
77
+ return [];
78
+ }
79
+ function extractImports(ast) {
80
+ return [];
81
+ }
82
+
83
+ // src/utils/metrics.ts
84
+ function estimateTokens(text) {
85
+ return Math.ceil(text.length / 4);
86
+ }
87
+ function levenshteinDistance(str1, str2) {
88
+ const len1 = str1.length;
89
+ const len2 = str2.length;
90
+ const matrix = [];
91
+ for (let i = 0; i <= len1; i++) {
92
+ matrix[i] = [i];
93
+ }
94
+ for (let j = 0; j <= len2; j++) {
95
+ matrix[0][j] = j;
96
+ }
97
+ for (let i = 1; i <= len1; i++) {
98
+ for (let j = 1; j <= len2; j++) {
99
+ const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;
100
+ matrix[i][j] = Math.min(
101
+ matrix[i - 1][j] + 1,
102
+ matrix[i][j - 1] + 1,
103
+ matrix[i - 1][j - 1] + cost
104
+ );
105
+ }
106
+ }
107
+ return matrix[len1][len2];
108
+ }
109
+ function similarityScore(str1, str2) {
110
+ const distance = levenshteinDistance(str1, str2);
111
+ const maxLength = Math.max(str1.length, str2.length);
112
+ return maxLength === 0 ? 1 : 1 - distance / maxLength;
113
+ }
114
+ // Annotate the CommonJS export names for ESM import in node:
115
+ 0 && (module.exports = {
116
+ estimateTokens,
117
+ extractFunctions,
118
+ extractImports,
119
+ getFileExtension,
120
+ isSourceFile,
121
+ levenshteinDistance,
122
+ parseCode,
123
+ readFileContent,
124
+ scanFiles,
125
+ similarityScore
126
+ });
package/dist/index.mjs ADDED
@@ -0,0 +1,90 @@
1
+ // src/utils/file-scanner.ts
2
+ import { glob } from "glob";
3
+ import { readFile } from "fs/promises";
4
+ var DEFAULT_EXCLUDE = [
5
+ "**/node_modules/**",
6
+ "**/dist/**",
7
+ "**/build/**",
8
+ "**/.git/**",
9
+ "**/coverage/**",
10
+ "**/*.min.js",
11
+ "**/*.bundle.js"
12
+ ];
13
+ async function scanFiles(options) {
14
+ const {
15
+ rootDir,
16
+ include = ["**/*.{ts,tsx,js,jsx,py,java}"],
17
+ exclude = DEFAULT_EXCLUDE
18
+ } = options;
19
+ const files = await glob(include, {
20
+ cwd: rootDir,
21
+ ignore: exclude,
22
+ absolute: true
23
+ });
24
+ return files;
25
+ }
26
+ async function readFileContent(filePath) {
27
+ return readFile(filePath, "utf-8");
28
+ }
29
+ function getFileExtension(filePath) {
30
+ return filePath.split(".").pop() || "";
31
+ }
32
+ function isSourceFile(filePath) {
33
+ const ext = getFileExtension(filePath);
34
+ return ["ts", "tsx", "js", "jsx", "py", "java", "go", "rs"].includes(ext);
35
+ }
36
+
37
+ // src/utils/ast-parser.ts
38
+ function parseCode(code, language) {
39
+ return null;
40
+ }
41
+ function extractFunctions(ast) {
42
+ return [];
43
+ }
44
+ function extractImports(ast) {
45
+ return [];
46
+ }
47
+
48
+ // src/utils/metrics.ts
49
+ function estimateTokens(text) {
50
+ return Math.ceil(text.length / 4);
51
+ }
52
+ function levenshteinDistance(str1, str2) {
53
+ const len1 = str1.length;
54
+ const len2 = str2.length;
55
+ const matrix = [];
56
+ for (let i = 0; i <= len1; i++) {
57
+ matrix[i] = [i];
58
+ }
59
+ for (let j = 0; j <= len2; j++) {
60
+ matrix[0][j] = j;
61
+ }
62
+ for (let i = 1; i <= len1; i++) {
63
+ for (let j = 1; j <= len2; j++) {
64
+ const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;
65
+ matrix[i][j] = Math.min(
66
+ matrix[i - 1][j] + 1,
67
+ matrix[i][j - 1] + 1,
68
+ matrix[i - 1][j - 1] + cost
69
+ );
70
+ }
71
+ }
72
+ return matrix[len1][len2];
73
+ }
74
+ function similarityScore(str1, str2) {
75
+ const distance = levenshteinDistance(str1, str2);
76
+ const maxLength = Math.max(str1.length, str2.length);
77
+ return maxLength === 0 ? 1 : 1 - distance / maxLength;
78
+ }
79
+ export {
80
+ estimateTokens,
81
+ extractFunctions,
82
+ extractImports,
83
+ getFileExtension,
84
+ isSourceFile,
85
+ levenshteinDistance,
86
+ parseCode,
87
+ readFileContent,
88
+ scanFiles,
89
+ similarityScore
90
+ };
package/package.json ADDED
@@ -0,0 +1,45 @@
1
+ {
2
+ "name": "@aiready/core",
3
+ "version": "0.1.0",
4
+ "description": "Shared utilities for AIReady analysis tools",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.mjs",
7
+ "types": "./dist/index.d.ts",
8
+ "exports": {
9
+ ".": {
10
+ "require": "./dist/index.js",
11
+ "import": "./dist/index.mjs",
12
+ "types": "./dist/index.d.ts"
13
+ }
14
+ },
15
+ "keywords": [
16
+ "aiready",
17
+ "code-analysis",
18
+ "utilities"
19
+ ],
20
+ "author": "AIReady Team",
21
+ "license": "MIT",
22
+ "repository": {
23
+ "type": "git",
24
+ "url": "https://github.com/caopengau/aiready-core.git"
25
+ },
26
+ "homepage": "https://github.com/caopengau/aiready-core",
27
+ "bugs": {
28
+ "url": "https://github.com/caopengau/aiready-core/issues"
29
+ },
30
+ "devDependencies": {
31
+ "tsup": "^8.3.5",
32
+ "eslint": "^9.17.0"
33
+ },
34
+ "dependencies": {
35
+ "glob": "^11.0.0",
36
+ "chalk": "^5.4.1"
37
+ },
38
+ "scripts": {
39
+ "build": "tsup src/index.ts --format cjs,esm --dts",
40
+ "dev": "tsup src/index.ts --format cjs,esm --dts --watch",
41
+ "test": "vitest run",
42
+ "lint": "eslint src",
43
+ "clean": "rm -rf dist"
44
+ }
45
+ }
package/src/index.ts ADDED
@@ -0,0 +1,4 @@
1
+ export * from './types';
2
+ export * from './utils/file-scanner';
3
+ export * from './utils/ast-parser';
4
+ export * from './utils/metrics';
package/src/types.ts ADDED
@@ -0,0 +1,59 @@
1
+ export interface AnalysisResult {
2
+ fileName: string;
3
+ issues: Issue[];
4
+ metrics: Metrics;
5
+ }
6
+
7
+ export interface Issue {
8
+ type: IssueType;
9
+ severity: 'critical' | 'major' | 'minor' | 'info';
10
+ message: string;
11
+ location: Location;
12
+ suggestion?: string;
13
+ }
14
+
15
+ export type IssueType =
16
+ | 'duplicate-pattern'
17
+ | 'context-fragmentation'
18
+ | 'doc-drift'
19
+ | 'naming-inconsistency'
20
+ | 'dead-code'
21
+ | 'circular-dependency'
22
+ | 'missing-types';
23
+
24
+ export interface Location {
25
+ file: string;
26
+ line: number;
27
+ column?: number;
28
+ endLine?: number;
29
+ endColumn?: number;
30
+ }
31
+
32
+ export interface Metrics {
33
+ tokenCost?: number;
34
+ complexityScore?: number;
35
+ consistencyScore?: number;
36
+ docFreshnessScore?: number;
37
+ }
38
+
39
+ export interface ScanOptions {
40
+ rootDir: string;
41
+ include?: string[];
42
+ exclude?: string[];
43
+ maxDepth?: number;
44
+ }
45
+
46
+ export interface Report {
47
+ summary: {
48
+ totalFiles: number;
49
+ totalIssues: number;
50
+ criticalIssues: number;
51
+ majorIssues: number;
52
+ };
53
+ results: AnalysisResult[];
54
+ metrics: {
55
+ overallScore: number;
56
+ tokenCostTotal: number;
57
+ avgConsistency: number;
58
+ };
59
+ }
@@ -0,0 +1,28 @@
1
+ // Placeholder for AST parsing utilities
2
+ // Will be implemented with specific parsers (TypeScript, Babel, etc.)
3
+
4
+ export interface ASTNode {
5
+ type: string;
6
+ loc?: {
7
+ start: { line: number; column: number };
8
+ end: { line: number; column: number };
9
+ };
10
+ }
11
+
12
+ export function parseCode(code: string, language: string): ASTNode | null {
13
+ // TODO: Implement language-specific parsing
14
+ // TypeScript: @typescript-eslint/parser
15
+ // JavaScript: @babel/parser
16
+ // Python: tree-sitter-python
17
+ return null;
18
+ }
19
+
20
+ export function extractFunctions(ast: ASTNode): ASTNode[] {
21
+ // TODO: Extract function declarations
22
+ return [];
23
+ }
24
+
25
+ export function extractImports(ast: ASTNode): string[] {
26
+ // TODO: Extract import statements
27
+ return [];
28
+ }
@@ -0,0 +1,42 @@
1
+ import { glob } from 'glob';
2
+ import { readFile } from 'fs/promises';
3
+ import { ScanOptions } from '../types';
4
+
5
+ const DEFAULT_EXCLUDE = [
6
+ '**/node_modules/**',
7
+ '**/dist/**',
8
+ '**/build/**',
9
+ '**/.git/**',
10
+ '**/coverage/**',
11
+ '**/*.min.js',
12
+ '**/*.bundle.js',
13
+ ];
14
+
15
+ export async function scanFiles(options: ScanOptions): Promise<string[]> {
16
+ const {
17
+ rootDir,
18
+ include = ['**/*.{ts,tsx,js,jsx,py,java}'],
19
+ exclude = DEFAULT_EXCLUDE,
20
+ } = options;
21
+
22
+ const files = await glob(include, {
23
+ cwd: rootDir,
24
+ ignore: exclude,
25
+ absolute: true,
26
+ });
27
+
28
+ return files;
29
+ }
30
+
31
+ export async function readFileContent(filePath: string): Promise<string> {
32
+ return readFile(filePath, 'utf-8');
33
+ }
34
+
35
+ export function getFileExtension(filePath: string): string {
36
+ return filePath.split('.').pop() || '';
37
+ }
38
+
39
+ export function isSourceFile(filePath: string): boolean {
40
+ const ext = getFileExtension(filePath);
41
+ return ['ts', 'tsx', 'js', 'jsx', 'py', 'java', 'go', 'rs'].includes(ext);
42
+ }
@@ -0,0 +1,47 @@
1
+ /**
2
+ * Estimate token count for text (rough approximation)
3
+ * ~1 token ≈ 4 characters for code
4
+ */
5
+ export function estimateTokens(text: string): number {
6
+ return Math.ceil(text.length / 4);
7
+ }
8
+
9
+ /**
10
+ * Calculate Levenshtein distance between two strings
11
+ * Useful for similarity detection
12
+ */
13
+ export function levenshteinDistance(str1: string, str2: string): number {
14
+ const len1 = str1.length;
15
+ const len2 = str2.length;
16
+ const matrix: number[][] = [];
17
+
18
+ for (let i = 0; i <= len1; i++) {
19
+ matrix[i] = [i];
20
+ }
21
+
22
+ for (let j = 0; j <= len2; j++) {
23
+ matrix[0][j] = j;
24
+ }
25
+
26
+ for (let i = 1; i <= len1; i++) {
27
+ for (let j = 1; j <= len2; j++) {
28
+ const cost = str1[i - 1] === str2[j - 1] ? 0 : 1;
29
+ matrix[i][j] = Math.min(
30
+ matrix[i - 1][j] + 1,
31
+ matrix[i][j - 1] + 1,
32
+ matrix[i - 1][j - 1] + cost
33
+ );
34
+ }
35
+ }
36
+
37
+ return matrix[len1][len2];
38
+ }
39
+
40
+ /**
41
+ * Calculate similarity score (0-1) between two strings
42
+ */
43
+ export function similarityScore(str1: string, str2: string): number {
44
+ const distance = levenshteinDistance(str1, str2);
45
+ const maxLength = Math.max(str1.length, str2.length);
46
+ return maxLength === 0 ? 1 : 1 - distance / maxLength;
47
+ }
package/tsconfig.json ADDED
@@ -0,0 +1,20 @@
1
+ {
2
+ "compilerOptions": {
3
+ "target": "ES2020",
4
+ "module": "ESNext",
5
+ "lib": ["ES2020"],
6
+ "moduleResolution": "bundler",
7
+ "declaration": true,
8
+ "declarationMap": true,
9
+ "sourceMap": true,
10
+ "outDir": "./dist",
11
+ "rootDir": "./src",
12
+ "strict": true,
13
+ "esModuleInterop": true,
14
+ "skipLibCheck": true,
15
+ "forceConsistentCasingInFileNames": true,
16
+ "resolveJsonModule": true
17
+ },
18
+ "include": ["src/**/*"],
19
+ "exclude": ["node_modules", "dist"]
20
+ }