@amirdaraee/namewise 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/ISSUE_TEMPLATE/bug_report.yml +82 -0
- package/.github/ISSUE_TEMPLATE/feature_request.yml +61 -0
- package/.github/workflows/auto-release.yml +78 -0
- package/.github/workflows/ci.yml +78 -0
- package/.github/workflows/publish.yml +43 -0
- package/.github/workflows/test.yml +37 -0
- package/CHANGELOG.md +128 -0
- package/LICENSE +21 -0
- package/README.md +251 -0
- package/dist/cli/commands.d.ts +3 -0
- package/dist/cli/commands.d.ts.map +1 -0
- package/dist/cli/commands.js +19 -0
- package/dist/cli/commands.js.map +1 -0
- package/dist/cli/rename.d.ts +2 -0
- package/dist/cli/rename.d.ts.map +1 -0
- package/dist/cli/rename.js +136 -0
- package/dist/cli/rename.js.map +1 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/parsers/excel-parser.d.ts +6 -0
- package/dist/parsers/excel-parser.d.ts.map +1 -0
- package/dist/parsers/excel-parser.js +42 -0
- package/dist/parsers/excel-parser.js.map +1 -0
- package/dist/parsers/factory.d.ts +7 -0
- package/dist/parsers/factory.d.ts.map +1 -0
- package/dist/parsers/factory.js +29 -0
- package/dist/parsers/factory.js.map +1 -0
- package/dist/parsers/pdf-parser.d.ts +7 -0
- package/dist/parsers/pdf-parser.d.ts.map +1 -0
- package/dist/parsers/pdf-parser.js +67 -0
- package/dist/parsers/pdf-parser.js.map +1 -0
- package/dist/parsers/text-parser.d.ts +6 -0
- package/dist/parsers/text-parser.d.ts.map +1 -0
- package/dist/parsers/text-parser.js +39 -0
- package/dist/parsers/text-parser.js.map +1 -0
- package/dist/parsers/word-parser.d.ts +6 -0
- package/dist/parsers/word-parser.d.ts.map +1 -0
- package/dist/parsers/word-parser.js +44 -0
- package/dist/parsers/word-parser.js.map +1 -0
- package/dist/services/ai-factory.d.ts +5 -0
- package/dist/services/ai-factory.d.ts.map +1 -0
- package/dist/services/ai-factory.js +15 -0
- package/dist/services/ai-factory.js.map +1 -0
- package/dist/services/claude-service.d.ts +9 -0
- package/dist/services/claude-service.d.ts.map +1 -0
- package/dist/services/claude-service.js +113 -0
- package/dist/services/claude-service.js.map +1 -0
- package/dist/services/file-renamer.d.ts +12 -0
- package/dist/services/file-renamer.d.ts.map +1 -0
- package/dist/services/file-renamer.js +99 -0
- package/dist/services/file-renamer.js.map +1 -0
- package/dist/services/openai-service.d.ts +9 -0
- package/dist/services/openai-service.d.ts.map +1 -0
- package/dist/services/openai-service.js +112 -0
- package/dist/services/openai-service.js.map +1 -0
- package/dist/types/index.d.ts +61 -0
- package/dist/types/index.d.ts.map +1 -0
- package/dist/types/index.js +2 -0
- package/dist/types/index.js.map +1 -0
- package/dist/utils/file-templates.d.ts +18 -0
- package/dist/utils/file-templates.d.ts.map +1 -0
- package/dist/utils/file-templates.js +232 -0
- package/dist/utils/file-templates.js.map +1 -0
- package/dist/utils/naming-conventions.d.ts +4 -0
- package/dist/utils/naming-conventions.d.ts.map +1 -0
- package/dist/utils/naming-conventions.js +55 -0
- package/dist/utils/naming-conventions.js.map +1 -0
- package/package.json +75 -0
- package/src/cli/commands.ts +20 -0
- package/src/cli/rename.ts +157 -0
- package/src/index.ts +17 -0
- package/src/parsers/excel-parser.ts +49 -0
- package/src/parsers/factory.ts +34 -0
- package/src/parsers/pdf-parser.ts +78 -0
- package/src/parsers/text-parser.ts +43 -0
- package/src/parsers/word-parser.ts +50 -0
- package/src/services/ai-factory.ts +16 -0
- package/src/services/claude-service.ts +114 -0
- package/src/services/file-renamer.ts +123 -0
- package/src/services/openai-service.ts +113 -0
- package/src/types/index.ts +71 -0
- package/src/types/pdf-extraction.d.ts +7 -0
- package/src/utils/file-templates.ts +275 -0
- package/src/utils/naming-conventions.ts +67 -0
- package/tests/data/empty-file.txt +0 -0
- package/tests/data/sample-markdown.md +9 -0
- package/tests/data/sample-pdf.pdf +0 -0
- package/tests/data/sample-text.txt +25 -0
- package/tests/integration/end-to-end.test.ts +209 -0
- package/tests/integration/workflow.test.ts +336 -0
- package/tests/mocks/mock-ai-service.ts +58 -0
- package/tests/unit/cli/commands.test.ts +163 -0
- package/tests/unit/parsers/factory.test.ts +100 -0
- package/tests/unit/parsers/pdf-parser.test.ts +63 -0
- package/tests/unit/parsers/text-parser.test.ts +85 -0
- package/tests/unit/services/ai-factory.test.ts +37 -0
- package/tests/unit/services/claude-service.test.ts +188 -0
- package/tests/unit/services/file-renamer.test.ts +299 -0
- package/tests/unit/services/openai-service.test.ts +196 -0
- package/tests/unit/utils/file-templates.test.ts +199 -0
- package/tests/unit/utils/naming-conventions.test.ts +88 -0
- package/tsconfig.json +20 -0
- package/vitest.config.ts +30 -0
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { NamingConvention, FileInfo } from '../types/index.js';
|
|
2
|
+
export type FileCategory = 'document' | 'movie' | 'music' | 'series' | 'photo' | 'book' | 'general' | 'auto';
|
|
3
|
+
export interface TemplateOptions {
|
|
4
|
+
personalName?: string;
|
|
5
|
+
dateFormat?: 'YYYY-MM-DD' | 'YYYY' | 'YYYYMMDD' | 'none';
|
|
6
|
+
category?: FileCategory;
|
|
7
|
+
}
|
|
8
|
+
export interface FileTemplate {
|
|
9
|
+
category: FileCategory;
|
|
10
|
+
pattern: string;
|
|
11
|
+
description: string;
|
|
12
|
+
examples: string[];
|
|
13
|
+
}
|
|
14
|
+
export declare const FILE_TEMPLATES: Record<Exclude<FileCategory, 'auto'>, FileTemplate>;
|
|
15
|
+
export declare function categorizeFile(filePath: string, content?: string, fileInfo?: FileInfo): FileCategory;
|
|
16
|
+
export declare function applyTemplate(aiGeneratedName: string, category: FileCategory, templateOptions: TemplateOptions, namingConvention: NamingConvention): string;
|
|
17
|
+
export declare function getTemplateInstructions(category: FileCategory): string;
|
|
18
|
+
//# sourceMappingURL=file-templates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-templates.d.ts","sourceRoot":"","sources":["../../src/utils/file-templates.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAG/D,MAAM,MAAM,YAAY,GAAG,UAAU,GAAG,OAAO,GAAG,OAAO,GAAG,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,MAAM,CAAC;AAE7G,MAAM,WAAW,eAAe;IAC9B,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,UAAU,CAAC,EAAE,YAAY,GAAG,MAAM,GAAG,UAAU,GAAG,MAAM,CAAC;IACzD,QAAQ,CAAC,EAAE,YAAY,CAAC;CACzB;AAED,MAAM,WAAW,YAAY;IAC3B,QAAQ,EAAE,YAAY,CAAC;IACvB,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,eAAO,MAAM,cAAc,EAAE,MAAM,CAAC,OAAO,CAAC,YAAY,EAAE,MAAM,CAAC,EAAE,YAAY,CAuE9E,CAAC;AAEF,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,QAAQ,CAAC,EAAE,QAAQ,GAAG,YAAY,CAiHpG;AAED,wBAAgB,aAAa,CAC3B,eAAe,EAAE,MAAM,EACvB,QAAQ,EAAE,YAAY,EACtB,eAAe,EAAE,eAAe,EAChC,gBAAgB,EAAE,gBAAgB,GACjC,MAAM,CA2BR;AA8BD,wBAAgB,uBAAuB,CAAC,QAAQ,EAAE,YAAY,GAAG,MAAM,CAMtE"}
|
|
@@ -0,0 +1,232 @@
|
|
|
1
|
+
import { applyNamingConvention } from './naming-conventions.js';
|
|
2
|
+
export const FILE_TEMPLATES = {
|
|
3
|
+
document: {
|
|
4
|
+
category: 'document',
|
|
5
|
+
pattern: '{content}-{personalName}-{date}',
|
|
6
|
+
description: 'Personal documents with name and date',
|
|
7
|
+
examples: [
|
|
8
|
+
'driving-license-amirhossein-20250213.pdf',
|
|
9
|
+
'dennemeyer-working-contract-amirhossein-20240314.pdf',
|
|
10
|
+
'university-diploma-sarah-20220615.pdf'
|
|
11
|
+
]
|
|
12
|
+
},
|
|
13
|
+
movie: {
|
|
14
|
+
category: 'movie',
|
|
15
|
+
pattern: '{content}-{year}',
|
|
16
|
+
description: 'Movies with release year',
|
|
17
|
+
examples: [
|
|
18
|
+
'the-dark-knight-2008.mkv',
|
|
19
|
+
'inception-2010.mp4',
|
|
20
|
+
'pulp-fiction-1994.avi'
|
|
21
|
+
]
|
|
22
|
+
},
|
|
23
|
+
music: {
|
|
24
|
+
category: 'music',
|
|
25
|
+
pattern: '{artist}-{content}',
|
|
26
|
+
description: 'Music files with artist name',
|
|
27
|
+
examples: [
|
|
28
|
+
'the-beatles-hey-jude.mp3',
|
|
29
|
+
'queen-bohemian-rhapsody.flac',
|
|
30
|
+
'pink-floyd-wish-you-were-here.wav'
|
|
31
|
+
]
|
|
32
|
+
},
|
|
33
|
+
series: {
|
|
34
|
+
category: 'series',
|
|
35
|
+
pattern: '{content}-s{season}e{episode}',
|
|
36
|
+
description: 'TV series with season and episode',
|
|
37
|
+
examples: [
|
|
38
|
+
'breaking-bad-s01e01.mkv',
|
|
39
|
+
'game-of-thrones-s04e09.mp4',
|
|
40
|
+
'the-office-s02e01.avi'
|
|
41
|
+
]
|
|
42
|
+
},
|
|
43
|
+
photo: {
|
|
44
|
+
category: 'photo',
|
|
45
|
+
pattern: '{content}-{personalName}-{date}',
|
|
46
|
+
description: 'Photos with personal name and date',
|
|
47
|
+
examples: [
|
|
48
|
+
'vacation-paris-john-20240715.jpg',
|
|
49
|
+
'wedding-ceremony-maria-20231009.png',
|
|
50
|
+
'birthday-party-alex-20240320.heic'
|
|
51
|
+
]
|
|
52
|
+
},
|
|
53
|
+
book: {
|
|
54
|
+
category: 'book',
|
|
55
|
+
pattern: '{author}-{content}',
|
|
56
|
+
description: 'Books with author name',
|
|
57
|
+
examples: [
|
|
58
|
+
'george-orwell-1984.pdf',
|
|
59
|
+
'j-k-rowling-harry-potter-philosophers-stone.epub',
|
|
60
|
+
'stephen-king-the-shining.mobi'
|
|
61
|
+
]
|
|
62
|
+
},
|
|
63
|
+
general: {
|
|
64
|
+
category: 'general',
|
|
65
|
+
pattern: '{content}',
|
|
66
|
+
description: 'General files without special formatting',
|
|
67
|
+
examples: [
|
|
68
|
+
'meeting-notes-q4-2024.txt',
|
|
69
|
+
'project-requirements.docx',
|
|
70
|
+
'financial-report.xlsx'
|
|
71
|
+
]
|
|
72
|
+
}
|
|
73
|
+
};
|
|
74
|
+
export function categorizeFile(filePath, content, fileInfo) {
|
|
75
|
+
const extension = getFileExtension(filePath).toLowerCase();
|
|
76
|
+
const fileName = getFileName(filePath).toLowerCase();
|
|
77
|
+
const contentLower = content?.toLowerCase() || '';
|
|
78
|
+
// Use metadata for enhanced categorization
|
|
79
|
+
let metadataHints = [];
|
|
80
|
+
if (fileInfo?.documentMetadata) {
|
|
81
|
+
const meta = fileInfo.documentMetadata;
|
|
82
|
+
if (meta.title)
|
|
83
|
+
metadataHints.push(meta.title.toLowerCase());
|
|
84
|
+
if (meta.author)
|
|
85
|
+
metadataHints.push(meta.author.toLowerCase());
|
|
86
|
+
if (meta.creator)
|
|
87
|
+
metadataHints.push(meta.creator.toLowerCase());
|
|
88
|
+
if (meta.subject)
|
|
89
|
+
metadataHints.push(meta.subject.toLowerCase());
|
|
90
|
+
if (meta.keywords)
|
|
91
|
+
metadataHints.push(...meta.keywords.map(k => k.toLowerCase()));
|
|
92
|
+
}
|
|
93
|
+
// Use folder context for better categorization
|
|
94
|
+
let folderHints = [];
|
|
95
|
+
if (fileInfo?.folderPath) {
|
|
96
|
+
folderHints = fileInfo.folderPath.map(f => f.toLowerCase());
|
|
97
|
+
}
|
|
98
|
+
if (fileInfo?.parentFolder) {
|
|
99
|
+
folderHints.push(fileInfo.parentFolder.toLowerCase());
|
|
100
|
+
}
|
|
101
|
+
const allHints = [...metadataHints, ...folderHints, contentLower, fileName].join(' ');
|
|
102
|
+
// Document types
|
|
103
|
+
const documentExtensions = ['.pdf', '.docx', '.doc', '.txt', '.rtf'];
|
|
104
|
+
const documentKeywords = ['contract', 'agreement', 'license', 'certificate', 'diploma', 'invoice', 'receipt', 'report', 'application', 'form', 'resume', 'cv', 'letter'];
|
|
105
|
+
// Media types
|
|
106
|
+
const movieExtensions = ['.mp4', '.mkv', '.avi', '.mov', '.wmv', '.flv', '.webm'];
|
|
107
|
+
const musicExtensions = ['.mp3', '.flac', '.wav', '.aac', '.ogg', '.m4a'];
|
|
108
|
+
const photoExtensions = ['.jpg', '.jpeg', '.png', '.gif', '.bmp', '.tiff', '.heic', '.webp'];
|
|
109
|
+
const bookExtensions = ['.epub', '.mobi', '.azw', '.azw3'];
|
|
110
|
+
// Enhanced series detection
|
|
111
|
+
const seriesKeywords = ['s01', 's02', 's03', 's04', 's05', 'season', 'episode', 'e01', 'e02', 'e03', 'series', 'show', 'tv'];
|
|
112
|
+
// Enhanced movie keywords
|
|
113
|
+
const movieKeywords = ['movie', 'film', 'cinema', '1080p', '720p', '4k', 'bluray', 'dvdrip', 'webrip'];
|
|
114
|
+
// Book keywords
|
|
115
|
+
const bookKeywords = ['chapter', 'author', 'book', 'novel', 'ebook', 'isbn', 'publisher', 'edition'];
|
|
116
|
+
// Music keywords
|
|
117
|
+
const musicKeywords = ['album', 'track', 'artist', 'band', 'singer', 'song', 'music'];
|
|
118
|
+
// Photo keywords
|
|
119
|
+
const photoKeywords = ['photo', 'image', 'picture', 'vacation', 'wedding', 'birthday', 'selfie', 'portrait'];
|
|
120
|
+
// Folder-based hints
|
|
121
|
+
const folderMovieHints = ['movies', 'films', 'cinema', 'video'];
|
|
122
|
+
const folderSeriesHints = ['series', 'shows', 'tv', 'television'];
|
|
123
|
+
const folderMusicHints = ['music', 'audio', 'songs', 'albums'];
|
|
124
|
+
const folderPhotoHints = ['photos', 'images', 'pictures', 'gallery'];
|
|
125
|
+
const folderBookHints = ['books', 'ebooks', 'library', 'reading'];
|
|
126
|
+
const folderDocumentHints = ['documents', 'docs', 'papers', 'files'];
|
|
127
|
+
// Check folder context first for strong hints
|
|
128
|
+
if (folderHints.some(hint => folderSeriesHints.includes(hint)))
|
|
129
|
+
return 'series';
|
|
130
|
+
if (folderHints.some(hint => folderMovieHints.includes(hint)))
|
|
131
|
+
return 'movie';
|
|
132
|
+
if (folderHints.some(hint => folderMusicHints.includes(hint)))
|
|
133
|
+
return 'music';
|
|
134
|
+
if (folderHints.some(hint => folderPhotoHints.includes(hint)))
|
|
135
|
+
return 'photo';
|
|
136
|
+
if (folderHints.some(hint => folderBookHints.includes(hint)))
|
|
137
|
+
return 'book';
|
|
138
|
+
if (folderHints.some(hint => folderDocumentHints.includes(hint)))
|
|
139
|
+
return 'document';
|
|
140
|
+
// Check for series first (before movies)
|
|
141
|
+
if (movieExtensions.includes(extension) && (seriesKeywords.some(keyword => allHints.includes(keyword)))) {
|
|
142
|
+
return 'series';
|
|
143
|
+
}
|
|
144
|
+
// Check by extension with enhanced keyword matching
|
|
145
|
+
if (documentExtensions.includes(extension)) {
|
|
146
|
+
// Check if it's a book
|
|
147
|
+
if (bookExtensions.includes(extension) || bookKeywords.some(keyword => allHints.includes(keyword))) {
|
|
148
|
+
return 'book';
|
|
149
|
+
}
|
|
150
|
+
// Check if it's likely a personal document
|
|
151
|
+
if (documentKeywords.some(keyword => allHints.includes(keyword))) {
|
|
152
|
+
return 'document';
|
|
153
|
+
}
|
|
154
|
+
return 'document'; // Default for document extensions
|
|
155
|
+
}
|
|
156
|
+
// Enhanced media type detection
|
|
157
|
+
if (movieExtensions.includes(extension)) {
|
|
158
|
+
if (movieKeywords.some(keyword => allHints.includes(keyword))) {
|
|
159
|
+
return 'movie';
|
|
160
|
+
}
|
|
161
|
+
return 'movie'; // Default for movie extensions
|
|
162
|
+
}
|
|
163
|
+
if (musicExtensions.includes(extension)) {
|
|
164
|
+
if (musicKeywords.some(keyword => allHints.includes(keyword))) {
|
|
165
|
+
return 'music';
|
|
166
|
+
}
|
|
167
|
+
return 'music';
|
|
168
|
+
}
|
|
169
|
+
if (photoExtensions.includes(extension)) {
|
|
170
|
+
if (photoKeywords.some(keyword => allHints.includes(keyword))) {
|
|
171
|
+
return 'photo';
|
|
172
|
+
}
|
|
173
|
+
return 'photo';
|
|
174
|
+
}
|
|
175
|
+
if (bookExtensions.includes(extension))
|
|
176
|
+
return 'book';
|
|
177
|
+
return 'general';
|
|
178
|
+
}
|
|
179
|
+
export function applyTemplate(aiGeneratedName, category, templateOptions, namingConvention) {
|
|
180
|
+
if (category === 'auto') {
|
|
181
|
+
throw new Error('Cannot apply template for "auto" category. Category should be resolved before calling applyTemplate.');
|
|
182
|
+
}
|
|
183
|
+
const template = FILE_TEMPLATES[category];
|
|
184
|
+
let result = template.pattern;
|
|
185
|
+
// Replace template variables
|
|
186
|
+
result = result.replace('{content}', aiGeneratedName);
|
|
187
|
+
if (templateOptions.personalName) {
|
|
188
|
+
result = result.replace('{personalName}', templateOptions.personalName);
|
|
189
|
+
}
|
|
190
|
+
if (templateOptions.dateFormat && templateOptions.dateFormat !== 'none') {
|
|
191
|
+
const date = formatDate(new Date(), templateOptions.dateFormat);
|
|
192
|
+
result = result.replace('{date}', date);
|
|
193
|
+
}
|
|
194
|
+
// Clean up any remaining unreplaced variables
|
|
195
|
+
result = result.replace(/\{[^}]+\}/g, '');
|
|
196
|
+
// Clean up multiple hyphens or other separators
|
|
197
|
+
result = result.replace(/-+/g, '-').replace(/^-|-$/g, '');
|
|
198
|
+
// Apply naming convention
|
|
199
|
+
return applyNamingConvention(result, namingConvention);
|
|
200
|
+
}
|
|
201
|
+
function formatDate(date, format) {
|
|
202
|
+
const year = date.getFullYear();
|
|
203
|
+
const month = String(date.getMonth() + 1).padStart(2, '0');
|
|
204
|
+
const day = String(date.getDate()).padStart(2, '0');
|
|
205
|
+
switch (format) {
|
|
206
|
+
case 'YYYY-MM-DD':
|
|
207
|
+
return `${year}-${month}-${day}`;
|
|
208
|
+
case 'YYYY':
|
|
209
|
+
return `${year}`;
|
|
210
|
+
case 'YYYYMMDD':
|
|
211
|
+
return `${year}${month}${day}`;
|
|
212
|
+
default:
|
|
213
|
+
return `${year}${month}${day}`;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
function getFileExtension(filePath) {
|
|
217
|
+
const parts = filePath.split('.');
|
|
218
|
+
return parts.length > 1 ? '.' + parts[parts.length - 1] : '';
|
|
219
|
+
}
|
|
220
|
+
function getFileName(filePath) {
|
|
221
|
+
const pathParts = filePath.split(/[/\\]/);
|
|
222
|
+
const fileName = pathParts[pathParts.length - 1];
|
|
223
|
+
return fileName.replace(/\.[^.]*$/, ''); // Remove extension
|
|
224
|
+
}
|
|
225
|
+
export function getTemplateInstructions(category) {
|
|
226
|
+
if (category === 'auto') {
|
|
227
|
+
return 'Generate appropriate filename based on detected file type and content.';
|
|
228
|
+
}
|
|
229
|
+
const template = FILE_TEMPLATES[category];
|
|
230
|
+
return `Generate filename for ${category} type files. ${template.description}. Examples: ${template.examples.join(', ')}`;
|
|
231
|
+
}
|
|
232
|
+
//# sourceMappingURL=file-templates.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"file-templates.js","sourceRoot":"","sources":["../../src/utils/file-templates.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,qBAAqB,EAAE,MAAM,yBAAyB,CAAC;AAiBhE,MAAM,CAAC,MAAM,cAAc,GAAwD;IACjF,QAAQ,EAAE;QACR,QAAQ,EAAE,UAAU;QACpB,OAAO,EAAE,iCAAiC;QAC1C,WAAW,EAAE,uCAAuC;QACpD,QAAQ,EAAE;YACR,0CAA0C;YAC1C,sDAAsD;YACtD,uCAAuC;SACxC;KACF;IACD,KAAK,EAAE;QACL,QAAQ,EAAE,OAAO;QACjB,OAAO,EAAE,kBAAkB;QAC3B,WAAW,EAAE,0BAA0B;QACvC,QAAQ,EAAE;YACR,0BAA0B;YAC1B,oBAAoB;YACpB,uBAAuB;SACxB;KACF;IACD,KAAK,EAAE;QACL,QAAQ,EAAE,OAAO;QACjB,OAAO,EAAE,oBAAoB;QAC7B,WAAW,EAAE,8BAA8B;QAC3C,QAAQ,EAAE;YACR,0BAA0B;YAC1B,8BAA8B;YAC9B,mCAAmC;SACpC;KACF;IACD,MAAM,EAAE;QACN,QAAQ,EAAE,QAAQ;QAClB,OAAO,EAAE,+BAA+B;QACxC,WAAW,EAAE,mCAAmC;QAChD,QAAQ,EAAE;YACR,yBAAyB;YACzB,4BAA4B;YAC5B,uBAAuB;SACxB;KACF;IACD,KAAK,EAAE;QACL,QAAQ,EAAE,OAAO;QACjB,OAAO,EAAE,iCAAiC;QAC1C,WAAW,EAAE,oCAAoC;QACjD,QAAQ,EAAE;YACR,kCAAkC;YAClC,qCAAqC;YACrC,mCAAmC;SACpC;KACF;IACD,IAAI,EAAE;QACJ,QAAQ,EAAE,MAAM;QAChB,OAAO,EAAE,oBAAoB;QAC7B,WAAW,EAAE,wBAAwB;QACrC,QAAQ,EAAE;YACR,wBAAwB;YACxB,kDAAkD;YAClD,+BAA+B;SAChC;KACF;IACD,OAAO,EAAE;QACP,QAAQ,EAAE,SAAS;QACnB,OAAO,EAAE,WAAW;QACpB,WAAW,EAAE,0CAA0C;QACvD,QAAQ,EAAE;YACR,2BAA2B;YAC3B,2BAA2B;YAC3B,uBAAuB;SACxB;KACF;CACF,CAAC;AAEF,MAAM,UAAU,cAAc,CAAC,QAAgB,EAAE,OAAgB,EAAE,QAAmB;IACpF,MAAM,SAAS,GAAG,gBAAgB,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;IAC3D,MAAM,QAAQ,GAAG,WAAW,CAAC,QAAQ,CAAC,CAAC,WAAW,EAAE,CAAC;IACrD,MAAM,YAAY,GAAG,OAAO,EAAE,WAAW,EAAE,IAAI,EAAE,CAAC;IAElD,2CAA2C;IAC3C,IAAI,aAAa,GAAa,EAAE,CAAC;IACjC,IAAI,QAAQ,EAAE,gBAAgB,EAAE,CAAC;QAC/B,MAAM,IAAI,GAAG,QAAQ,CAAC,gBAAgB,CAAC;QACvC,IAAI,IAAI,CAAC,KAAK;YAAE,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,EAAE,CAAC,CAAC;QAC7D,IAAI,IAAI,CAAC,MAAM;YAAE,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,MAAM,CAAC,WAAW,EAAE,CAAC,CAAC;QAC/D,IAAI,IAAI,CAAC,OAAO;YAAE,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;QACjE,IAAI,IAAI,CAAC,OAAO;YAAE,aAAa,CAAC,IAAI,CAAC,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC,CAAC;QACjE,IAAI,IAAI,CAAC,QAAQ;YAAE,aAAa,CAAC,IAAI,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC;IACpF,CAAC;IAED,+CAA+C;IAC/C,IAAI,WAAW,GAAa,EAAE,CAAC;IAC/B,IAAI,QAAQ,EAAE,UAAU,EAAE,CAAC;QACzB,WAAW,GAAG,QAAQ,CAAC,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC;IAC9D,CAAC;IACD,IAAI,QAAQ,EAAE,YAAY,EAAE,CAAC;QAC3B,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,YAAY,CAAC,WAAW,EAAE,CAAC,CAAC;IACxD,CAAC;IAED,MAAM,QAAQ,GAAG,CAAC,GAAG,aAAa,EAAE,GAAG,WAAW,EAAE,YAAY,EAAE,QAAQ,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IAEtF,iBAAiB;IACjB,MAAM,kBAAkB,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IACrE,MAAM,gBAAgB,GAAG,CAAC,UAAU,EAAE,WAAW,EAAE,SAAS,EAAE,aAAa,EAAE,SAAS,EAAE,SAAS,EAAE,SAAS,EAAE,QAAQ,EAAE,aAAa,EAAE,MAAM,EAAE,QAAQ,EAAE,IAAI,EAAE,QAAQ,CAAC,CAAC;IAEzK,cAAc;IACd,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAClF,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;IAC1E,MAAM,eAAe,GAAG,CAAC,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,OAAO,CAAC,CAAC;IAC7F,MAAM,cAAc,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAE3D,4BAA4B;IAC5B,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,SAAS,EAAE,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,QAAQ,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;IAE7H,0BAA0B;IAC1B,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,QAAQ,CAAC,CAAC;IAEvG,gBAAgB;IAChB,MAAM,YAAY,GAAG,CAAC,SAAS,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,SAAS,CAAC,CAAC;IAErG,mBAAmB;IACnB,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,CAAC;IAEtF,iBAAiB;IACjB,MAAM,aAAa,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,UAAU,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,UAAU,CAAC,CAAC;IAE7G,qBAAqB;IACrB,MAAM,gBAAgB,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAChE,MAAM,iBAAiB,GAAG,CAAC,QAAQ,EAAE,OAAO,EAAE,IAAI,EAAE,YAAY,CAAC,CAAC;IAClE,MAAM,gBAAgB,GAAG,CAAC,OAAO,EAAE,OAAO,EAAE,OAAO,EAAE,QAAQ,CAAC,CAAC;IAC/D,MAAM,gBAAgB,GAAG,CAAC,QAAQ,EAAE,QAAQ,EAAE,UAAU,EAAE,SAAS,CAAC,CAAC;IACrE,MAAM,eAAe,GAAG,CAAC,OAAO,EAAE,QAAQ,EAAE,SAAS,EAAE,SAAS,CAAC,CAAC;IAClE,MAAM,mBAAmB,GAAG,CAAC,WAAW,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,CAAC,CAAC;IAErE,8CAA8C;IAC9C,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,iBAAiB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAAE,OAAO,QAAQ,CAAC;IAChF,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAAE,OAAO,OAAO,CAAC;IAC9E,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAAE,OAAO,OAAO,CAAC;IAC9E,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,gBAAgB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAAE,OAAO,OAAO,CAAC;IAC9E,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,eAAe,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAAE,OAAO,MAAM,CAAC;IAC5E,IAAI,WAAW,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC,mBAAmB,CAAC,QAAQ,CAAC,IAAI,CAAC,CAAC;QAAE,OAAO,UAAU,CAAC;IAEpF,yCAAyC;IACzC,IAAI,eAAe,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,CACzC,cAAc,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,CAC3D,EAAE,CAAC;QACF,OAAO,QAAQ,CAAC;IAClB,CAAC;IAED,oDAAoD;IACpD,IAAI,kBAAkB,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QAC3C,uBAAuB;QACvB,IAAI,cAAc,CAAC,QAAQ,CAAC,SAAS,CAAC,IAAI,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;YACnG,OAAO,MAAM,CAAC;QAChB,CAAC;QACD,2CAA2C;QAC3C,IAAI,gBAAgB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;YACjE,OAAO,UAAU,CAAC;QACpB,CAAC;QACD,OAAO,UAAU,CAAC,CAAC,kCAAkC;IACvD,CAAC;IAED,gCAAgC;IAChC,IAAI,eAAe,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QACxC,IAAI,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;YAC9D,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,OAAO,OAAO,CAAC,CAAC,+BAA+B;IACjD,CAAC;IAED,IAAI,eAAe,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QACxC,IAAI,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;YAC9D,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,IAAI,eAAe,CAAC,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC;QACxC,IAAI,aAAa,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC,QAAQ,CAAC,QAAQ,CAAC,OAAO,CAAC,CAAC,EAAE,CAAC;YAC9D,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IAED,IAAI,cAAc,CAAC,QAAQ,CAAC,SAAS,CAAC;QAAE,OAAO,MAAM,CAAC;IAEtD,OAAO,SAAS,CAAC;AACnB,CAAC;AAED,MAAM,UAAU,aAAa,CAC3B,eAAuB,EACvB,QAAsB,EACtB,eAAgC,EAChC,gBAAkC;IAElC,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;QACxB,MAAM,IAAI,KAAK,CAAC,sGAAsG,CAAC,CAAC;IAC1H,CAAC;IACD,MAAM,QAAQ,GAAG,cAAc,CAAC,QAAyC,CAAC,CAAC;IAC3E,IAAI,MAAM,GAAG,QAAQ,CAAC,OAAO,CAAC;IAE9B,6BAA6B;IAC7B,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,WAAW,EAAE,eAAe,CAAC,CAAC;IAEtD,IAAI,eAAe,CAAC,YAAY,EAAE,CAAC;QACjC,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,gBAAgB,EAAE,eAAe,CAAC,YAAY,CAAC,CAAC;IAC1E,CAAC;IAED,IAAI,eAAe,CAAC,UAAU,IAAI,eAAe,CAAC,UAAU,KAAK,MAAM,EAAE,CAAC;QACxE,MAAM,IAAI,GAAG,UAAU,CAAC,IAAI,IAAI,EAAE,EAAE,eAAe,CAAC,UAAU,CAAC,CAAC;QAChE,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;IAC1C,CAAC;IAED,8CAA8C;IAC9C,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,YAAY,EAAE,EAAE,CAAC,CAAC;IAE1C,gDAAgD;IAChD,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IAE1D,0BAA0B;IAC1B,OAAO,qBAAqB,CAAC,MAAM,EAAE,gBAAgB,CAAC,CAAC;AACzD,CAAC;AAED,SAAS,UAAU,CAAC,IAAU,EAAE,MAA0C;IACxE,MAAM,IAAI,GAAG,IAAI,CAAC,WAAW,EAAE,CAAC;IAChC,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAC3D,MAAM,GAAG,GAAG,MAAM,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,CAAC,QAAQ,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;IAEpD,QAAQ,MAAM,EAAE,CAAC;QACf,KAAK,YAAY;YACf,OAAO,GAAG,IAAI,IAAI,KAAK,IAAI,GAAG,EAAE,CAAC;QACnC,KAAK,MAAM;YACT,OAAO,GAAG,IAAI,EAAE,CAAC;QACnB,KAAK,UAAU;YACb,OAAO,GAAG,IAAI,GAAG,KAAK,GAAG,GAAG,EAAE,CAAC;QACjC;YACE,OAAO,GAAG,IAAI,GAAG,KAAK,GAAG,GAAG,EAAE,CAAC;IACnC,CAAC;AACH,CAAC;AAED,SAAS,gBAAgB,CAAC,QAAgB;IACxC,MAAM,KAAK,GAAG,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IAClC,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,GAAG,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;AAC/D,CAAC;AAED,SAAS,WAAW,CAAC,QAAgB;IACnC,MAAM,SAAS,GAAG,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC1C,MAAM,QAAQ,GAAG,SAAS,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACjD,OAAO,QAAQ,CAAC,OAAO,CAAC,UAAU,EAAE,EAAE,CAAC,CAAC,CAAC,mBAAmB;AAC9D,CAAC;AAED,MAAM,UAAU,uBAAuB,CAAC,QAAsB;IAC5D,IAAI,QAAQ,KAAK,MAAM,EAAE,CAAC;QACxB,OAAO,wEAAwE,CAAC;IAClF,CAAC;IACD,MAAM,QAAQ,GAAG,cAAc,CAAC,QAAyC,CAAC,CAAC;IAC3E,OAAO,yBAAyB,QAAQ,gBAAgB,QAAQ,CAAC,WAAW,eAAe,QAAQ,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;AAC5H,CAAC"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
export type NamingConvention = 'kebab-case' | 'snake_case' | 'camelCase' | 'PascalCase' | 'lowercase' | 'UPPERCASE';
|
|
2
|
+
export declare function applyNamingConvention(text: string, convention: NamingConvention): string;
|
|
3
|
+
export declare function getNamingInstructions(convention: NamingConvention): string;
|
|
4
|
+
//# sourceMappingURL=naming-conventions.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"naming-conventions.d.ts","sourceRoot":"","sources":["../../src/utils/naming-conventions.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,gBAAgB,GAAG,YAAY,GAAG,YAAY,GAAG,WAAW,GAAG,YAAY,GAAG,WAAW,GAAG,WAAW,CAAC;AAEpH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,gBAAgB,GAAG,MAAM,CAmDxF;AAED,wBAAgB,qBAAqB,CAAC,UAAU,EAAE,gBAAgB,GAAG,MAAM,CAW1E"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export function applyNamingConvention(text, convention) {
|
|
2
|
+
// First, normalize the text by removing special characters and extra spaces
|
|
3
|
+
const normalized = text
|
|
4
|
+
.replace(/[^\w\s-]/g, '') // Remove special characters except hyphens
|
|
5
|
+
.replace(/\s+/g, ' ') // Normalize spaces
|
|
6
|
+
.trim();
|
|
7
|
+
switch (convention) {
|
|
8
|
+
case 'kebab-case':
|
|
9
|
+
return normalized
|
|
10
|
+
.toLowerCase()
|
|
11
|
+
.replace(/\s+/g, '-')
|
|
12
|
+
.replace(/[_]/g, '-');
|
|
13
|
+
case 'snake_case':
|
|
14
|
+
return normalized
|
|
15
|
+
.toLowerCase()
|
|
16
|
+
.replace(/\s+/g, '_')
|
|
17
|
+
.replace(/[-]/g, '_');
|
|
18
|
+
case 'camelCase':
|
|
19
|
+
return normalized
|
|
20
|
+
.split(/[\s\-_]+/)
|
|
21
|
+
.map((word, index) => index === 0
|
|
22
|
+
? word.toLowerCase()
|
|
23
|
+
: word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
24
|
+
.join('');
|
|
25
|
+
case 'PascalCase':
|
|
26
|
+
return normalized
|
|
27
|
+
.split(/[\s\-_]+/)
|
|
28
|
+
.map(word => word.charAt(0).toUpperCase() + word.slice(1).toLowerCase())
|
|
29
|
+
.join('');
|
|
30
|
+
case 'lowercase':
|
|
31
|
+
return normalized
|
|
32
|
+
.toLowerCase()
|
|
33
|
+
.replace(/\s+/g, '')
|
|
34
|
+
.replace(/[-_]/g, '');
|
|
35
|
+
case 'UPPERCASE':
|
|
36
|
+
return normalized
|
|
37
|
+
.toUpperCase()
|
|
38
|
+
.replace(/\s+/g, '')
|
|
39
|
+
.replace(/[-_]/g, '');
|
|
40
|
+
default:
|
|
41
|
+
return normalized.replace(/\s+/g, '-').toLowerCase(); // Default to kebab-case
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
export function getNamingInstructions(convention) {
|
|
45
|
+
const instructions = {
|
|
46
|
+
'kebab-case': 'Use lowercase with hyphens between words (e.g., "meeting-notes-2024")',
|
|
47
|
+
'snake_case': 'Use lowercase with underscores between words (e.g., "meeting_notes_2024")',
|
|
48
|
+
'camelCase': 'Use camelCase format starting with lowercase (e.g., "meetingNotes2024")',
|
|
49
|
+
'PascalCase': 'Use PascalCase format starting with uppercase (e.g., "MeetingNotes2024")',
|
|
50
|
+
'lowercase': 'Use single lowercase word with no separators (e.g., "meetingnotes2024")',
|
|
51
|
+
'UPPERCASE': 'Use single uppercase word with no separators (e.g., "MEETINGNOTES2024")'
|
|
52
|
+
};
|
|
53
|
+
return instructions[convention];
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=naming-conventions.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"naming-conventions.js","sourceRoot":"","sources":["../../src/utils/naming-conventions.ts"],"names":[],"mappings":"AAEA,MAAM,UAAU,qBAAqB,CAAC,IAAY,EAAE,UAA4B;IAC9E,4EAA4E;IAC5E,MAAM,UAAU,GAAG,IAAI;SACpB,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC,2CAA2C;SACpE,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAM,mBAAmB;SAC7C,IAAI,EAAE,CAAC;IAEV,QAAQ,UAAU,EAAE,CAAC;QACnB,KAAK,YAAY;YACf,OAAO,UAAU;iBACd,WAAW,EAAE;iBACb,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;iBACpB,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAE1B,KAAK,YAAY;YACf,OAAO,UAAU;iBACd,WAAW,EAAE;iBACb,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;iBACpB,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;QAE1B,KAAK,WAAW;YACd,OAAO,UAAU;iBACd,KAAK,CAAC,UAAU,CAAC;iBACjB,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CACnB,KAAK,KAAK,CAAC;gBACT,CAAC,CAAC,IAAI,CAAC,WAAW,EAAE;gBACpB,CAAC,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAC/D;iBACA,IAAI,CAAC,EAAE,CAAC,CAAC;QAEd,KAAK,YAAY;YACf,OAAO,UAAU;iBACd,KAAK,CAAC,UAAU,CAAC;iBACjB,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE,CAAC;iBACvE,IAAI,CAAC,EAAE,CAAC,CAAC;QAEd,KAAK,WAAW;YACd,OAAO,UAAU;iBACd,WAAW,EAAE;iBACb,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;iBACnB,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAE1B,KAAK,WAAW;YACd,OAAO,UAAU;iBACd,WAAW,EAAE;iBACb,OAAO,CAAC,MAAM,EAAE,EAAE,CAAC;iBACnB,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC;QAE1B;YACE,OAAO,UAAU,CAAC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,WAAW,EAAE,CAAC,CAAC,wBAAwB;IAClF,CAAC;AACH,CAAC;AAED,MAAM,UAAU,qBAAqB,CAAC,UAA4B;IAChE,MAAM,YAAY,GAAG;QACnB,YAAY,EAAE,uEAAuE;QACrF,YAAY,EAAE,2EAA2E;QACzF,WAAW,EAAE,yEAAyE;QACtF,YAAY,EAAE,0EAA0E;QACxF,WAAW,EAAE,yEAAyE;QACtF,WAAW,EAAE,yEAAyE;KACvF,CAAC;IAEF,OAAO,YAAY,CAAC,UAAU,CAAC,CAAC;AAClC,CAAC"}
|
package/package.json
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@amirdaraee/namewise",
|
|
3
|
+
"version": "0.3.0",
|
|
4
|
+
"main": "dist/index.js",
|
|
5
|
+
"type": "module",
|
|
6
|
+
"bin": {
|
|
7
|
+
"namewise": "./dist/index.js"
|
|
8
|
+
},
|
|
9
|
+
"scripts": {
|
|
10
|
+
"build": "tsc",
|
|
11
|
+
"dev": "tsx src/index.ts",
|
|
12
|
+
"start": "node dist/index.js",
|
|
13
|
+
"test": "vitest",
|
|
14
|
+
"test:ui": "vitest --ui",
|
|
15
|
+
"test:run": "vitest run",
|
|
16
|
+
"test:coverage": "vitest run --coverage",
|
|
17
|
+
"test:watch": "vitest --watch",
|
|
18
|
+
"test:unit": "vitest run tests/unit",
|
|
19
|
+
"test:integration": "vitest run tests/integration",
|
|
20
|
+
"version:patch": "npm version patch",
|
|
21
|
+
"version:minor": "npm version minor",
|
|
22
|
+
"version:major": "npm version major",
|
|
23
|
+
"release": "npm run build && npm run test:run && npm version patch",
|
|
24
|
+
"release:minor": "npm run build && npm run test:run && npm version minor",
|
|
25
|
+
"release:major": "npm run build && npm run test:run && npm version major",
|
|
26
|
+
"prepare": "npm run build",
|
|
27
|
+
"prepublishOnly": "npm run test:run"
|
|
28
|
+
},
|
|
29
|
+
"engines": {
|
|
30
|
+
"node": ">=18.0.0"
|
|
31
|
+
},
|
|
32
|
+
"keywords": [
|
|
33
|
+
"ai",
|
|
34
|
+
"rename",
|
|
35
|
+
"files",
|
|
36
|
+
"smart",
|
|
37
|
+
"content",
|
|
38
|
+
"cli",
|
|
39
|
+
"automation",
|
|
40
|
+
"organization",
|
|
41
|
+
"claude",
|
|
42
|
+
"openai"
|
|
43
|
+
],
|
|
44
|
+
"author": "ai-rename contributors",
|
|
45
|
+
"repository": {
|
|
46
|
+
"type": "git",
|
|
47
|
+
"url": "https://github.com/amirdaraee/namewise.git"
|
|
48
|
+
},
|
|
49
|
+
"bugs": {
|
|
50
|
+
"url": "https://github.com/amirdaraee/namewise/issues"
|
|
51
|
+
},
|
|
52
|
+
"homepage": "https://github.com/amirdaraee/namewise#readme",
|
|
53
|
+
"license": "MIT",
|
|
54
|
+
"description": "AI-powered CLI tool that intelligently renames files based on their content using Claude or OpenAI",
|
|
55
|
+
"devDependencies": {
|
|
56
|
+
"@types/fs-extra": "^11.0.4",
|
|
57
|
+
"@types/inquirer": "^9.0.9",
|
|
58
|
+
"@types/node": "^24.3.1",
|
|
59
|
+
"@vitest/coverage-v8": "^3.2.4",
|
|
60
|
+
"@vitest/ui": "^3.2.4",
|
|
61
|
+
"tsx": "^4.20.5",
|
|
62
|
+
"typescript": "^5.9.2",
|
|
63
|
+
"vitest": "^3.2.4"
|
|
64
|
+
},
|
|
65
|
+
"dependencies": {
|
|
66
|
+
"@anthropic-ai/sdk": "^0.61.0",
|
|
67
|
+
"commander": "^14.0.0",
|
|
68
|
+
"fs-extra": "^11.3.1",
|
|
69
|
+
"inquirer": "^12.9.4",
|
|
70
|
+
"mammoth": "^1.10.0",
|
|
71
|
+
"openai": "^5.19.1",
|
|
72
|
+
"pdf-extraction": "^1.0.2",
|
|
73
|
+
"xlsx": "^0.18.5"
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { Command } from 'commander';
|
|
2
|
+
import { renameFiles } from './rename.js';
|
|
3
|
+
|
|
4
|
+
export function setupCommands(program: Command): void {
|
|
5
|
+
program
|
|
6
|
+
.command('rename')
|
|
7
|
+
.description('Rename files in a directory based on their content')
|
|
8
|
+
.argument('<directory>', 'Directory containing files to rename')
|
|
9
|
+
.option('-p, --provider <provider>', 'AI provider (claude|openai)', 'claude')
|
|
10
|
+
.option('-k, --api-key <key>', 'API key for the AI provider')
|
|
11
|
+
.option('-c, --case <convention>', 'Naming convention (kebab-case|snake_case|camelCase|PascalCase|lowercase|UPPERCASE)', 'kebab-case')
|
|
12
|
+
.option('-t, --template <category>', 'File category template (document|movie|music|series|photo|book|general|auto)', 'general')
|
|
13
|
+
.option('-n, --name <personalName>', 'Personal name to include in filenames')
|
|
14
|
+
.option('-d, --date <format>', 'Date format (YYYY-MM-DD|YYYY|YYYYMMDD|none)', 'none')
|
|
15
|
+
.option('--dry-run', 'Preview changes without renaming files', false)
|
|
16
|
+
.option('--max-size <size>', 'Maximum file size in MB', '10')
|
|
17
|
+
.action(async (directory, options) => {
|
|
18
|
+
await renameFiles(directory, options);
|
|
19
|
+
});
|
|
20
|
+
}
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
import { promises as fs } from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import inquirer from 'inquirer';
|
|
4
|
+
import { FileInfo, Config, RenameResult, FileCategory, DateFormat } from '../types/index.js';
|
|
5
|
+
import { DocumentParserFactory } from '../parsers/factory.js';
|
|
6
|
+
import { AIServiceFactory } from '../services/ai-factory.js';
|
|
7
|
+
import { FileRenamer } from '../services/file-renamer.js';
|
|
8
|
+
|
|
9
|
+
export async function renameFiles(directory: string, options: any): Promise<void> {
|
|
10
|
+
try {
|
|
11
|
+
// Validate directory exists
|
|
12
|
+
const stats = await fs.stat(directory);
|
|
13
|
+
if (!stats.isDirectory()) {
|
|
14
|
+
throw new Error(`${directory} is not a directory`);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Get API key
|
|
18
|
+
let apiKey = options.apiKey;
|
|
19
|
+
if (!apiKey) {
|
|
20
|
+
const keyPrompt = await inquirer.prompt([
|
|
21
|
+
{
|
|
22
|
+
type: 'password',
|
|
23
|
+
name: 'apiKey',
|
|
24
|
+
message: `Enter your ${options.provider} API key:`,
|
|
25
|
+
mask: '*'
|
|
26
|
+
}
|
|
27
|
+
]);
|
|
28
|
+
apiKey = keyPrompt.apiKey;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Create config
|
|
32
|
+
const config: Config = {
|
|
33
|
+
aiProvider: options.provider,
|
|
34
|
+
apiKey,
|
|
35
|
+
maxFileSize: parseInt(options.maxSize) * 1024 * 1024, // Convert MB to bytes
|
|
36
|
+
supportedExtensions: ['.pdf', '.docx', '.doc', '.xlsx', '.xls', '.txt'],
|
|
37
|
+
dryRun: options.dryRun,
|
|
38
|
+
namingConvention: options.case,
|
|
39
|
+
templateOptions: {
|
|
40
|
+
category: options.template as FileCategory,
|
|
41
|
+
personalName: options.name,
|
|
42
|
+
dateFormat: options.date as DateFormat
|
|
43
|
+
}
|
|
44
|
+
};
|
|
45
|
+
|
|
46
|
+
// Initialize services
|
|
47
|
+
const parserFactory = new DocumentParserFactory();
|
|
48
|
+
const aiService = AIServiceFactory.create(config.aiProvider, apiKey);
|
|
49
|
+
const fileRenamer = new FileRenamer(parserFactory, aiService, config);
|
|
50
|
+
|
|
51
|
+
// Get files to process
|
|
52
|
+
const files = await getFilesToProcess(directory, config.supportedExtensions);
|
|
53
|
+
|
|
54
|
+
if (files.length === 0) {
|
|
55
|
+
console.log('No supported files found in the directory.');
|
|
56
|
+
return;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
console.log(`Found ${files.length} files to process:`);
|
|
60
|
+
files.forEach(file => console.log(` - ${file.name}`));
|
|
61
|
+
|
|
62
|
+
// Confirm before processing
|
|
63
|
+
if (!config.dryRun) {
|
|
64
|
+
const confirm = await inquirer.prompt([
|
|
65
|
+
{
|
|
66
|
+
type: 'confirm',
|
|
67
|
+
name: 'proceed',
|
|
68
|
+
message: 'Do you want to proceed with renaming these files?',
|
|
69
|
+
default: false
|
|
70
|
+
}
|
|
71
|
+
]);
|
|
72
|
+
|
|
73
|
+
if (!confirm.proceed) {
|
|
74
|
+
console.log('Operation cancelled.');
|
|
75
|
+
return;
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Process files
|
|
80
|
+
console.log('\nProcessing files...');
|
|
81
|
+
const results = await fileRenamer.renameFiles(files);
|
|
82
|
+
|
|
83
|
+
// Display results
|
|
84
|
+
displayResults(results, config.dryRun);
|
|
85
|
+
|
|
86
|
+
} catch (error) {
|
|
87
|
+
console.error('Error:', error instanceof Error ? error.message : 'Unknown error');
|
|
88
|
+
process.exit(1);
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
async function getFilesToProcess(directory: string, supportedExtensions: string[]): Promise<FileInfo[]> {
|
|
93
|
+
const files: FileInfo[] = [];
|
|
94
|
+
const entries = await fs.readdir(directory, { withFileTypes: true });
|
|
95
|
+
|
|
96
|
+
for (const entry of entries) {
|
|
97
|
+
if (entry.isFile()) {
|
|
98
|
+
const filePath = path.join(directory, entry.name);
|
|
99
|
+
const extension = path.extname(entry.name).toLowerCase();
|
|
100
|
+
|
|
101
|
+
if (supportedExtensions.includes(extension)) {
|
|
102
|
+
const stats = await fs.stat(filePath);
|
|
103
|
+
|
|
104
|
+
// Extract folder context
|
|
105
|
+
const parentFolder = path.basename(directory);
|
|
106
|
+
const fullPath = path.resolve(filePath);
|
|
107
|
+
const folderPath = path.dirname(fullPath).split(path.sep).filter(p => p);
|
|
108
|
+
|
|
109
|
+
files.push({
|
|
110
|
+
path: filePath,
|
|
111
|
+
name: entry.name,
|
|
112
|
+
extension,
|
|
113
|
+
size: stats.size,
|
|
114
|
+
// File system metadata
|
|
115
|
+
createdAt: stats.birthtime,
|
|
116
|
+
modifiedAt: stats.mtime,
|
|
117
|
+
accessedAt: stats.atime,
|
|
118
|
+
// Context metadata
|
|
119
|
+
parentFolder,
|
|
120
|
+
folderPath: folderPath.slice(-3), // Last 3 folder levels for context
|
|
121
|
+
// Document metadata will be populated by parsers
|
|
122
|
+
documentMetadata: undefined
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
return files;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function displayResults(results: RenameResult[], dryRun: boolean): void {
|
|
132
|
+
const successful = results.filter(r => r.success);
|
|
133
|
+
const failed = results.filter(r => !r.success);
|
|
134
|
+
|
|
135
|
+
console.log(`\n${dryRun ? 'Preview' : 'Results'}:`);
|
|
136
|
+
console.log(`✅ ${successful.length} files ${dryRun ? 'would be' : 'successfully'} renamed`);
|
|
137
|
+
|
|
138
|
+
if (failed.length > 0) {
|
|
139
|
+
console.log(`❌ ${failed.length} files failed`);
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
console.log('\nDetails:');
|
|
143
|
+
results.forEach(result => {
|
|
144
|
+
const status = result.success ? '✅' : '❌';
|
|
145
|
+
const originalName = path.basename(result.originalPath);
|
|
146
|
+
const newName = path.basename(result.newPath);
|
|
147
|
+
|
|
148
|
+
if (result.success) {
|
|
149
|
+
console.log(`${status} ${originalName} → ${newName}`);
|
|
150
|
+
} else {
|
|
151
|
+
console.log(`${status} ${originalName} (failed)`);
|
|
152
|
+
if (result.error) {
|
|
153
|
+
console.log(` Error: ${result.error}`);
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
}
|
package/src/index.ts
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
import { program } from 'commander';
|
|
4
|
+
import { setupCommands } from './cli/commands.js';
|
|
5
|
+
|
|
6
|
+
async function main() {
|
|
7
|
+
program
|
|
8
|
+
.name('namewise')
|
|
9
|
+
.description('AI-powered tool to intelligently rename files based on their content')
|
|
10
|
+
.version('0.3.0');
|
|
11
|
+
|
|
12
|
+
setupCommands(program);
|
|
13
|
+
|
|
14
|
+
await program.parseAsync(process.argv);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import fs from 'fs';
|
|
2
|
+
import path from 'path';
|
|
3
|
+
import * as XLSX from 'xlsx';
|
|
4
|
+
import { DocumentParser, ParseResult, DocumentMetadata } from '../types/index.js';
|
|
5
|
+
|
|
6
|
+
export class ExcelParser implements DocumentParser {
|
|
7
|
+
supports(filePath: string): boolean {
|
|
8
|
+
const ext = path.extname(filePath).toLowerCase();
|
|
9
|
+
return ext === '.xlsx' || ext === '.xls';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async parse(filePath: string): Promise<ParseResult> {
|
|
13
|
+
try {
|
|
14
|
+
const workbook = XLSX.readFile(filePath);
|
|
15
|
+
const sheets: string[] = [];
|
|
16
|
+
const metadata: DocumentMetadata = {};
|
|
17
|
+
|
|
18
|
+
workbook.SheetNames.forEach(sheetName => {
|
|
19
|
+
const sheet = workbook.Sheets[sheetName];
|
|
20
|
+
const csvData = XLSX.utils.sheet_to_csv(sheet);
|
|
21
|
+
if (csvData.trim()) {
|
|
22
|
+
sheets.push(`Sheet: ${sheetName}\\n${csvData}`);
|
|
23
|
+
}
|
|
24
|
+
});
|
|
25
|
+
|
|
26
|
+
const content = sheets.join('\\n\\n').trim();
|
|
27
|
+
|
|
28
|
+
// Extract metadata from workbook properties
|
|
29
|
+
if (workbook.Props) {
|
|
30
|
+
const props = workbook.Props as any;
|
|
31
|
+
metadata.title = props.Title;
|
|
32
|
+
metadata.author = props.Author;
|
|
33
|
+
metadata.subject = props.Subject;
|
|
34
|
+
metadata.keywords = props.Keywords ? [props.Keywords] : undefined;
|
|
35
|
+
metadata.creationDate = props.CreatedDate;
|
|
36
|
+
metadata.modificationDate = props.ModifiedDate;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Estimate word count from content
|
|
40
|
+
if (content) {
|
|
41
|
+
metadata.wordCount = content.split(/\\s+/).filter(word => word.length > 0).length;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
return { content, metadata };
|
|
45
|
+
} catch (error) {
|
|
46
|
+
throw new Error(`Failed to parse Excel file: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
}
|