@amirdaraee/namewise 0.4.0 → 0.4.1

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 (31) hide show
  1. package/.github/workflows/build.yml +6 -0
  2. package/.github/workflows/publish.yml +59 -11
  3. package/.github/workflows/test.yml +1 -0
  4. package/RELEASE.md +15 -3
  5. package/dist/services/claude-service.d.ts.map +1 -1
  6. package/dist/services/claude-service.js +9 -55
  7. package/dist/services/claude-service.js.map +1 -1
  8. package/dist/services/lmstudio-service.d.ts.map +1 -1
  9. package/dist/services/lmstudio-service.js +9 -19
  10. package/dist/services/lmstudio-service.js.map +1 -1
  11. package/dist/services/ollama-service.d.ts.map +1 -1
  12. package/dist/services/ollama-service.js +9 -19
  13. package/dist/services/ollama-service.js.map +1 -1
  14. package/dist/services/openai-service.d.ts.map +1 -1
  15. package/dist/services/openai-service.js +9 -55
  16. package/dist/services/openai-service.js.map +1 -1
  17. package/dist/utils/ai-prompts.d.ts +20 -0
  18. package/dist/utils/ai-prompts.d.ts.map +1 -0
  19. package/dist/utils/ai-prompts.js +71 -0
  20. package/dist/utils/ai-prompts.js.map +1 -0
  21. package/package.json +1 -1
  22. package/src/services/claude-service.ts +10 -48
  23. package/src/services/lmstudio-service.ts +11 -20
  24. package/src/services/ollama-service.ts +11 -20
  25. package/src/services/openai-service.ts +10 -48
  26. package/src/utils/ai-prompts.ts +76 -0
  27. package/tests/integration/ai-prompting.test.ts +386 -0
  28. package/tests/integration/person-name-extraction.test.ts +440 -0
  29. package/tests/unit/services/lmstudio-service.test.ts +4 -4
  30. package/tests/unit/services/ollama-service.test.ts +4 -4
  31. package/tests/unit/utils/ai-prompts.test.ts +213 -0
@@ -1,7 +1,8 @@
1
1
  import Anthropic from '@anthropic-ai/sdk';
2
2
  import { AIProvider, FileInfo } from '../types/index.js';
3
- import { applyNamingConvention, getNamingInstructions, NamingConvention } from '../utils/naming-conventions.js';
4
- import { getTemplateInstructions, FileCategory } from '../utils/file-templates.js';
3
+ import { applyNamingConvention, NamingConvention } from '../utils/naming-conventions.js';
4
+ import { FileCategory } from '../utils/file-templates.js';
5
+ import { buildFileNamePrompt } from '../utils/ai-prompts.js';
5
6
 
6
7
  export class ClaudeService implements AIProvider {
7
8
  name = 'Claude';
@@ -16,53 +17,14 @@ export class ClaudeService implements AIProvider {
16
17
  async generateFileName(content: string, originalName: string, namingConvention: string = 'kebab-case', category: string = 'general', fileInfo?: FileInfo): Promise<string> {
17
18
  const convention = namingConvention as NamingConvention;
18
19
  const fileCategory = category as FileCategory;
19
- const namingInstructions = getNamingInstructions(convention);
20
- const templateInstructions = getTemplateInstructions(fileCategory);
21
20
 
22
- // Build comprehensive context from all metadata
23
- let metadataContext = '';
24
- if (fileInfo) {
25
- metadataContext += `File Information:
26
- - Original filename: ${originalName}
27
- - File size: ${Math.round(fileInfo.size / 1024)}KB
28
- - Created: ${fileInfo.createdAt.toLocaleDateString()}
29
- - Modified: ${fileInfo.modifiedAt.toLocaleDateString()}
30
- - Parent folder: ${fileInfo.parentFolder}
31
- - Folder path: ${fileInfo.folderPath.join(' > ')}`;
32
-
33
- if (fileInfo.documentMetadata) {
34
- const meta = fileInfo.documentMetadata;
35
- metadataContext += `
36
- Document Properties:`;
37
- if (meta.title) metadataContext += `\n- Title: ${meta.title}`;
38
- if (meta.author) metadataContext += `\n- Author: ${meta.author}`;
39
- if (meta.creator) metadataContext += `\n- Creator: ${meta.creator}`;
40
- if (meta.subject) metadataContext += `\n- Subject: ${meta.subject}`;
41
- if (meta.keywords?.length) metadataContext += `\n- Keywords: ${meta.keywords.join(', ')}`;
42
- if (meta.creationDate) metadataContext += `\n- Created: ${meta.creationDate.toLocaleDateString()}`;
43
- if (meta.modificationDate) metadataContext += `\n- Modified: ${meta.modificationDate.toLocaleDateString()}`;
44
- if (meta.pages) metadataContext += `\n- Pages: ${meta.pages}`;
45
- if (meta.wordCount) metadataContext += `\n- Word count: ${meta.wordCount}`;
46
- }
47
- }
48
-
49
- const prompt = `Based on the following document information, generate a descriptive filename that captures the main topic/purpose of the document. The filename should be:
50
- - Descriptive and meaningful
51
- - Professional and clean
52
- - Between 3-8 words
53
- - ${namingInstructions}
54
- - ${templateInstructions}
55
- - Do not include file extension
56
- - Do not include personal names, dates, or template variables - just the core content description
57
- - Only use letters, numbers, and appropriate separators for the naming convention
58
- - Use all available context (metadata, folder context, document properties) to create the most accurate filename
59
-
60
- ${metadataContext}
61
-
62
- Document content (first 2000 characters):
63
- ${content.substring(0, 2000)}
64
-
65
- Respond with only the core filename (without personal info or dates) using the specified naming convention, no explanation.`;
21
+ const prompt = buildFileNamePrompt({
22
+ content,
23
+ originalName,
24
+ namingConvention: convention,
25
+ category: fileCategory,
26
+ fileInfo
27
+ });
66
28
 
67
29
  try {
68
30
  const response = await this.client.messages.create({
@@ -1,4 +1,7 @@
1
1
  import { AIProvider, FileInfo } from '../types/index.js';
2
+ import { buildFileNamePrompt, AI_SYSTEM_PROMPT } from '../utils/ai-prompts.js';
3
+ import { NamingConvention } from '../utils/naming-conventions.js';
4
+ import { FileCategory } from '../utils/file-templates.js';
2
5
 
3
6
  interface OpenAICompatibleResponse {
4
7
  choices: Array<{
@@ -55,7 +58,7 @@ export class LMStudioService implements AIProvider {
55
58
  messages: [
56
59
  {
57
60
  role: 'system',
58
- content: 'You are a helpful assistant that generates descriptive filenames based on document content. Always respond with just the filename, no explanation or additional text.'
61
+ content: AI_SYSTEM_PROMPT
59
62
  },
60
63
  {
61
64
  role: 'user',
@@ -85,25 +88,13 @@ export class LMStudioService implements AIProvider {
85
88
  category: string,
86
89
  fileInfo?: FileInfo
87
90
  ): string {
88
- let prompt = `Based on the following document content, generate a descriptive filename using ${namingConvention} naming convention. `;
89
- prompt += `The file category is: ${category}. `;
90
-
91
- if (fileInfo?.documentMetadata) {
92
- const meta = fileInfo.documentMetadata;
93
- if (meta.title) prompt += `Document title: "${meta.title}". `;
94
- if (meta.author) prompt += `Author: "${meta.author}". `;
95
- if (meta.subject) prompt += `Subject: "${meta.subject}". `;
96
- }
97
-
98
- if (fileInfo?.parentFolder && fileInfo.parentFolder !== '.') {
99
- prompt += `Located in folder: "${fileInfo.parentFolder}". `;
100
- }
101
-
102
- prompt += `\nOriginal filename: ${originalName}\n`;
103
- prompt += `\nDocument content (first 2000 characters):\n${content.substring(0, 2000)}`;
104
- prompt += `\n\nGenerate ONLY a descriptive filename (without extension) using ${namingConvention} format. Do not include any explanation or additional text.`;
105
-
106
- return prompt;
91
+ return buildFileNamePrompt({
92
+ content,
93
+ originalName,
94
+ namingConvention: namingConvention as NamingConvention,
95
+ category: category as FileCategory,
96
+ fileInfo
97
+ });
107
98
  }
108
99
 
109
100
  private sanitizeFilename(filename: string): string {
@@ -1,4 +1,7 @@
1
1
  import { AIProvider, FileInfo } from '../types/index.js';
2
+ import { buildFileNamePrompt, AI_SYSTEM_PROMPT } from '../utils/ai-prompts.js';
3
+ import { NamingConvention } from '../utils/naming-conventions.js';
4
+ import { FileCategory } from '../utils/file-templates.js';
2
5
 
3
6
  interface OllamaResponse {
4
7
  model: string;
@@ -43,7 +46,7 @@ export class OllamaService implements AIProvider {
43
46
  messages: [
44
47
  {
45
48
  role: 'system',
46
- content: 'You are a helpful assistant that generates descriptive filenames based on document content. Always respond with just the filename, no explanation or additional text.'
49
+ content: AI_SYSTEM_PROMPT
47
50
  },
48
51
  {
49
52
  role: 'user',
@@ -71,25 +74,13 @@ export class OllamaService implements AIProvider {
71
74
  category: string,
72
75
  fileInfo?: FileInfo
73
76
  ): string {
74
- let prompt = `Based on the following document content, generate a descriptive filename using ${namingConvention} naming convention. `;
75
- prompt += `The file category is: ${category}. `;
76
-
77
- if (fileInfo?.documentMetadata) {
78
- const meta = fileInfo.documentMetadata;
79
- if (meta.title) prompt += `Document title: "${meta.title}". `;
80
- if (meta.author) prompt += `Author: "${meta.author}". `;
81
- if (meta.subject) prompt += `Subject: "${meta.subject}". `;
82
- }
83
-
84
- if (fileInfo?.parentFolder && fileInfo.parentFolder !== '.') {
85
- prompt += `Located in folder: "${fileInfo.parentFolder}". `;
86
- }
87
-
88
- prompt += `\nOriginal filename: ${originalName}\n`;
89
- prompt += `\nDocument content (first 2000 characters):\n${content.substring(0, 2000)}`;
90
- prompt += `\n\nGenerate ONLY a descriptive filename (without extension) using ${namingConvention} format. Do not include any explanation or additional text.`;
91
-
92
- return prompt;
77
+ return buildFileNamePrompt({
78
+ content,
79
+ originalName,
80
+ namingConvention: namingConvention as NamingConvention,
81
+ category: category as FileCategory,
82
+ fileInfo
83
+ });
93
84
  }
94
85
 
95
86
  private sanitizeFilename(filename: string): string {
@@ -1,7 +1,8 @@
1
1
  import OpenAI from 'openai';
2
2
  import { AIProvider, FileInfo } from '../types/index.js';
3
- import { applyNamingConvention, getNamingInstructions, NamingConvention } from '../utils/naming-conventions.js';
4
- import { getTemplateInstructions, FileCategory } from '../utils/file-templates.js';
3
+ import { applyNamingConvention, NamingConvention } from '../utils/naming-conventions.js';
4
+ import { FileCategory } from '../utils/file-templates.js';
5
+ import { buildFileNamePrompt } from '../utils/ai-prompts.js';
5
6
 
6
7
  export class OpenAIService implements AIProvider {
7
8
  name = 'OpenAI';
@@ -16,53 +17,14 @@ export class OpenAIService implements AIProvider {
16
17
  async generateFileName(content: string, originalName: string, namingConvention: string = 'kebab-case', category: string = 'general', fileInfo?: FileInfo): Promise<string> {
17
18
  const convention = namingConvention as NamingConvention;
18
19
  const fileCategory = category as FileCategory;
19
- const namingInstructions = getNamingInstructions(convention);
20
- const templateInstructions = getTemplateInstructions(fileCategory);
21
20
 
22
- // Build comprehensive context from all metadata
23
- let metadataContext = '';
24
- if (fileInfo) {
25
- metadataContext += `File Information:
26
- - Original filename: ${originalName}
27
- - File size: ${Math.round(fileInfo.size / 1024)}KB
28
- - Created: ${fileInfo.createdAt.toLocaleDateString()}
29
- - Modified: ${fileInfo.modifiedAt.toLocaleDateString()}
30
- - Parent folder: ${fileInfo.parentFolder}
31
- - Folder path: ${fileInfo.folderPath.join(' > ')}`;
32
-
33
- if (fileInfo.documentMetadata) {
34
- const meta = fileInfo.documentMetadata;
35
- metadataContext += `
36
- Document Properties:`;
37
- if (meta.title) metadataContext += `\n- Title: ${meta.title}`;
38
- if (meta.author) metadataContext += `\n- Author: ${meta.author}`;
39
- if (meta.creator) metadataContext += `\n- Creator: ${meta.creator}`;
40
- if (meta.subject) metadataContext += `\n- Subject: ${meta.subject}`;
41
- if (meta.keywords?.length) metadataContext += `\n- Keywords: ${meta.keywords.join(', ')}`;
42
- if (meta.creationDate) metadataContext += `\n- Created: ${meta.creationDate.toLocaleDateString()}`;
43
- if (meta.modificationDate) metadataContext += `\n- Modified: ${meta.modificationDate.toLocaleDateString()}`;
44
- if (meta.pages) metadataContext += `\n- Pages: ${meta.pages}`;
45
- if (meta.wordCount) metadataContext += `\n- Word count: ${meta.wordCount}`;
46
- }
47
- }
48
-
49
- const prompt = `Based on the following document information, generate a descriptive filename that captures the main topic/purpose of the document. The filename should be:
50
- - Descriptive and meaningful
51
- - Professional and clean
52
- - Between 3-8 words
53
- - ${namingInstructions}
54
- - ${templateInstructions}
55
- - Do not include file extension
56
- - Do not include personal names, dates, or template variables - just the core content description
57
- - Only use letters, numbers, and appropriate separators for the naming convention
58
- - Use all available context (metadata, folder context, document properties) to create the most accurate filename
59
-
60
- ${metadataContext}
61
-
62
- Document content (first 2000 characters):
63
- ${content.substring(0, 2000)}
64
-
65
- Respond with only the core filename (without personal info or dates) using the specified naming convention, no explanation.`;
21
+ const prompt = buildFileNamePrompt({
22
+ content,
23
+ originalName,
24
+ namingConvention: convention,
25
+ category: fileCategory,
26
+ fileInfo
27
+ });
66
28
 
67
29
  try {
68
30
  const response = await this.client.chat.completions.create({
@@ -0,0 +1,76 @@
1
+ import { FileInfo } from '../types/index.js';
2
+ import { getNamingInstructions, NamingConvention } from './naming-conventions.js';
3
+ import { getTemplateInstructions, FileCategory } from './file-templates.js';
4
+
5
+ export interface PromptContext {
6
+ content: string;
7
+ originalName: string;
8
+ namingConvention: NamingConvention;
9
+ category: FileCategory;
10
+ fileInfo?: FileInfo;
11
+ }
12
+
13
+ /**
14
+ * Builds a standardized prompt for AI filename generation
15
+ * This prompt is used across all AI providers (Claude, OpenAI, LMStudio, Ollama)
16
+ */
17
+ export function buildFileNamePrompt(context: PromptContext): string {
18
+ const { content, originalName, namingConvention, category, fileInfo } = context;
19
+
20
+ const namingInstructions = getNamingInstructions(namingConvention);
21
+ const templateInstructions = getTemplateInstructions(category);
22
+
23
+ // Build comprehensive context from all metadata
24
+ let metadataContext = '';
25
+ if (fileInfo) {
26
+ metadataContext += `File Information:
27
+ - Original filename: ${originalName}
28
+ - File size: ${Math.round(fileInfo.size / 1024)}KB
29
+ - Created: ${fileInfo.createdAt.toLocaleDateString()}
30
+ - Modified: ${fileInfo.modifiedAt.toLocaleDateString()}
31
+ - Parent folder: ${fileInfo.parentFolder}
32
+ - Folder path: ${fileInfo.folderPath.join(' > ')}`;
33
+
34
+ if (fileInfo.documentMetadata) {
35
+ const meta = fileInfo.documentMetadata;
36
+ metadataContext += `
37
+ Document Properties:`;
38
+ if (meta.title) metadataContext += `\n- Title: ${meta.title}`;
39
+ if (meta.author) metadataContext += `\n- Author: ${meta.author}`;
40
+ if (meta.creator) metadataContext += `\n- Creator: ${meta.creator}`;
41
+ if (meta.subject) metadataContext += `\n- Subject: ${meta.subject}`;
42
+ if (meta.keywords?.length) metadataContext += `\n- Keywords: ${meta.keywords.join(', ')}`;
43
+ if (meta.creationDate) metadataContext += `\n- Created: ${meta.creationDate.toLocaleDateString()}`;
44
+ if (meta.modificationDate) metadataContext += `\n- Modified: ${meta.modificationDate.toLocaleDateString()}`;
45
+ if (meta.pages) metadataContext += `\n- Pages: ${meta.pages}`;
46
+ if (meta.wordCount) metadataContext += `\n- Word count: ${meta.wordCount}`;
47
+ }
48
+ }
49
+
50
+ return `Based on the following document information, generate a descriptive filename that captures the main topic/purpose of the document. The filename should be:
51
+ - Descriptive and meaningful
52
+ - Professional and clean
53
+ - Between 3-10 words
54
+ - ${namingInstructions}
55
+ - ${templateInstructions}
56
+ - Do not include file extension
57
+ - If the document is specifically for/about a person (based on content), include their name at the beginning
58
+ - Include dates only if they are essential to the document's identity (e.g., contracts, certificates)
59
+ - Ignore irrelevant folder names that don't describe the document content
60
+ - Only use letters, numbers, and appropriate separators for the naming convention
61
+ - Focus on the document's actual content and purpose, not just metadata
62
+
63
+ ${metadataContext}
64
+
65
+ Document content (first 2000 characters):
66
+ ${content.substring(0, 2000)}
67
+
68
+ Important: If this document is specifically for or about a particular person mentioned in the content, start the filename with their name. Otherwise, focus on the document's main purpose and content.
69
+
70
+ Respond with only the filename using the specified naming convention, no explanation.`;
71
+ }
72
+
73
+ /**
74
+ * System prompt for AI models that need a separate system message
75
+ */
76
+ export const AI_SYSTEM_PROMPT = 'You are a helpful assistant that generates descriptive filenames based on document content. Always respond with just the filename, no explanation or additional text.';