@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.
Files changed (105) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.yml +82 -0
  2. package/.github/ISSUE_TEMPLATE/feature_request.yml +61 -0
  3. package/.github/workflows/auto-release.yml +78 -0
  4. package/.github/workflows/ci.yml +78 -0
  5. package/.github/workflows/publish.yml +43 -0
  6. package/.github/workflows/test.yml +37 -0
  7. package/CHANGELOG.md +128 -0
  8. package/LICENSE +21 -0
  9. package/README.md +251 -0
  10. package/dist/cli/commands.d.ts +3 -0
  11. package/dist/cli/commands.d.ts.map +1 -0
  12. package/dist/cli/commands.js +19 -0
  13. package/dist/cli/commands.js.map +1 -0
  14. package/dist/cli/rename.d.ts +2 -0
  15. package/dist/cli/rename.d.ts.map +1 -0
  16. package/dist/cli/rename.js +136 -0
  17. package/dist/cli/rename.js.map +1 -0
  18. package/dist/index.d.ts +3 -0
  19. package/dist/index.d.ts.map +1 -0
  20. package/dist/index.js +13 -0
  21. package/dist/index.js.map +1 -0
  22. package/dist/parsers/excel-parser.d.ts +6 -0
  23. package/dist/parsers/excel-parser.d.ts.map +1 -0
  24. package/dist/parsers/excel-parser.js +42 -0
  25. package/dist/parsers/excel-parser.js.map +1 -0
  26. package/dist/parsers/factory.d.ts +7 -0
  27. package/dist/parsers/factory.d.ts.map +1 -0
  28. package/dist/parsers/factory.js +29 -0
  29. package/dist/parsers/factory.js.map +1 -0
  30. package/dist/parsers/pdf-parser.d.ts +7 -0
  31. package/dist/parsers/pdf-parser.d.ts.map +1 -0
  32. package/dist/parsers/pdf-parser.js +67 -0
  33. package/dist/parsers/pdf-parser.js.map +1 -0
  34. package/dist/parsers/text-parser.d.ts +6 -0
  35. package/dist/parsers/text-parser.d.ts.map +1 -0
  36. package/dist/parsers/text-parser.js +39 -0
  37. package/dist/parsers/text-parser.js.map +1 -0
  38. package/dist/parsers/word-parser.d.ts +6 -0
  39. package/dist/parsers/word-parser.d.ts.map +1 -0
  40. package/dist/parsers/word-parser.js +44 -0
  41. package/dist/parsers/word-parser.js.map +1 -0
  42. package/dist/services/ai-factory.d.ts +5 -0
  43. package/dist/services/ai-factory.d.ts.map +1 -0
  44. package/dist/services/ai-factory.js +15 -0
  45. package/dist/services/ai-factory.js.map +1 -0
  46. package/dist/services/claude-service.d.ts +9 -0
  47. package/dist/services/claude-service.d.ts.map +1 -0
  48. package/dist/services/claude-service.js +113 -0
  49. package/dist/services/claude-service.js.map +1 -0
  50. package/dist/services/file-renamer.d.ts +12 -0
  51. package/dist/services/file-renamer.d.ts.map +1 -0
  52. package/dist/services/file-renamer.js +99 -0
  53. package/dist/services/file-renamer.js.map +1 -0
  54. package/dist/services/openai-service.d.ts +9 -0
  55. package/dist/services/openai-service.d.ts.map +1 -0
  56. package/dist/services/openai-service.js +112 -0
  57. package/dist/services/openai-service.js.map +1 -0
  58. package/dist/types/index.d.ts +61 -0
  59. package/dist/types/index.d.ts.map +1 -0
  60. package/dist/types/index.js +2 -0
  61. package/dist/types/index.js.map +1 -0
  62. package/dist/utils/file-templates.d.ts +18 -0
  63. package/dist/utils/file-templates.d.ts.map +1 -0
  64. package/dist/utils/file-templates.js +232 -0
  65. package/dist/utils/file-templates.js.map +1 -0
  66. package/dist/utils/naming-conventions.d.ts +4 -0
  67. package/dist/utils/naming-conventions.d.ts.map +1 -0
  68. package/dist/utils/naming-conventions.js +55 -0
  69. package/dist/utils/naming-conventions.js.map +1 -0
  70. package/package.json +75 -0
  71. package/src/cli/commands.ts +20 -0
  72. package/src/cli/rename.ts +157 -0
  73. package/src/index.ts +17 -0
  74. package/src/parsers/excel-parser.ts +49 -0
  75. package/src/parsers/factory.ts +34 -0
  76. package/src/parsers/pdf-parser.ts +78 -0
  77. package/src/parsers/text-parser.ts +43 -0
  78. package/src/parsers/word-parser.ts +50 -0
  79. package/src/services/ai-factory.ts +16 -0
  80. package/src/services/claude-service.ts +114 -0
  81. package/src/services/file-renamer.ts +123 -0
  82. package/src/services/openai-service.ts +113 -0
  83. package/src/types/index.ts +71 -0
  84. package/src/types/pdf-extraction.d.ts +7 -0
  85. package/src/utils/file-templates.ts +275 -0
  86. package/src/utils/naming-conventions.ts +67 -0
  87. package/tests/data/empty-file.txt +0 -0
  88. package/tests/data/sample-markdown.md +9 -0
  89. package/tests/data/sample-pdf.pdf +0 -0
  90. package/tests/data/sample-text.txt +25 -0
  91. package/tests/integration/end-to-end.test.ts +209 -0
  92. package/tests/integration/workflow.test.ts +336 -0
  93. package/tests/mocks/mock-ai-service.ts +58 -0
  94. package/tests/unit/cli/commands.test.ts +163 -0
  95. package/tests/unit/parsers/factory.test.ts +100 -0
  96. package/tests/unit/parsers/pdf-parser.test.ts +63 -0
  97. package/tests/unit/parsers/text-parser.test.ts +85 -0
  98. package/tests/unit/services/ai-factory.test.ts +37 -0
  99. package/tests/unit/services/claude-service.test.ts +188 -0
  100. package/tests/unit/services/file-renamer.test.ts +299 -0
  101. package/tests/unit/services/openai-service.test.ts +196 -0
  102. package/tests/unit/utils/file-templates.test.ts +199 -0
  103. package/tests/unit/utils/naming-conventions.test.ts +88 -0
  104. package/tsconfig.json +20 -0
  105. 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":"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"}
@@ -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
+ }