@amirdaraee/namewise 1.0.1 → 1.2.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 (122) hide show
  1. package/CHANGELOG.md +94 -0
  2. package/README.md +8 -8
  3. package/dist/cli/apply.d.ts.map +1 -1
  4. package/dist/cli/apply.js +33 -6
  5. package/dist/cli/apply.js.map +1 -1
  6. package/dist/cli/clean-empty.d.ts.map +1 -1
  7. package/dist/cli/clean-empty.js +2 -3
  8. package/dist/cli/clean-empty.js.map +1 -1
  9. package/dist/cli/commands.d.ts.map +1 -1
  10. package/dist/cli/commands.js +18 -37
  11. package/dist/cli/commands.js.map +1 -1
  12. package/dist/cli/config-cmd.d.ts.map +1 -1
  13. package/dist/cli/config-cmd.js +7 -4
  14. package/dist/cli/config-cmd.js.map +1 -1
  15. package/dist/cli/dedup.d.ts.map +1 -1
  16. package/dist/cli/dedup.js +3 -11
  17. package/dist/cli/dedup.js.map +1 -1
  18. package/dist/cli/diff.d.ts.map +1 -1
  19. package/dist/cli/diff.js +3 -7
  20. package/dist/cli/diff.js.map +1 -1
  21. package/dist/cli/find.d.ts.map +1 -1
  22. package/dist/cli/find.js +2 -3
  23. package/dist/cli/find.js.map +1 -1
  24. package/dist/cli/flatten.d.ts.map +1 -1
  25. package/dist/cli/flatten.js +4 -11
  26. package/dist/cli/flatten.js.map +1 -1
  27. package/dist/cli/handle-cli-error.d.ts +7 -0
  28. package/dist/cli/handle-cli-error.d.ts.map +1 -0
  29. package/dist/cli/handle-cli-error.js +27 -0
  30. package/dist/cli/handle-cli-error.js.map +1 -0
  31. package/dist/cli/init.d.ts.map +1 -1
  32. package/dist/cli/init.js +8 -6
  33. package/dist/cli/init.js.map +1 -1
  34. package/dist/cli/organize.d.ts.map +1 -1
  35. package/dist/cli/organize.js +4 -11
  36. package/dist/cli/organize.js.map +1 -1
  37. package/dist/cli/rename.d.ts +4 -2
  38. package/dist/cli/rename.d.ts.map +1 -1
  39. package/dist/cli/rename.js +171 -227
  40. package/dist/cli/rename.js.map +1 -1
  41. package/dist/cli/sanitize.d.ts.map +1 -1
  42. package/dist/cli/sanitize.js +4 -12
  43. package/dist/cli/sanitize.js.map +1 -1
  44. package/dist/cli/shared-config.d.ts +60 -0
  45. package/dist/cli/shared-config.d.ts.map +1 -0
  46. package/dist/cli/shared-config.js +64 -0
  47. package/dist/cli/shared-config.js.map +1 -0
  48. package/dist/cli/stats.d.ts.map +1 -1
  49. package/dist/cli/stats.js +2 -4
  50. package/dist/cli/stats.js.map +1 -1
  51. package/dist/cli/tree.d.ts.map +1 -1
  52. package/dist/cli/tree.js +2 -3
  53. package/dist/cli/tree.js.map +1 -1
  54. package/dist/cli/watch.d.ts +3 -1
  55. package/dist/cli/watch.d.ts.map +1 -1
  56. package/dist/cli/watch.js +19 -63
  57. package/dist/cli/watch.js.map +1 -1
  58. package/dist/parsers/pdf-parser.d.ts.map +1 -1
  59. package/dist/parsers/pdf-parser.js +44 -27
  60. package/dist/parsers/pdf-parser.js.map +1 -1
  61. package/dist/services/base-cloud-service.d.ts +32 -0
  62. package/dist/services/base-cloud-service.d.ts.map +1 -0
  63. package/dist/services/base-cloud-service.js +73 -0
  64. package/dist/services/base-cloud-service.js.map +1 -0
  65. package/dist/services/base-local-service.d.ts +18 -0
  66. package/dist/services/base-local-service.d.ts.map +1 -0
  67. package/dist/services/base-local-service.js +81 -0
  68. package/dist/services/base-local-service.js.map +1 -0
  69. package/dist/services/claude-service.d.ts +8 -6
  70. package/dist/services/claude-service.d.ts.map +1 -1
  71. package/dist/services/claude-service.js +40 -98
  72. package/dist/services/claude-service.js.map +1 -1
  73. package/dist/services/file-renamer.d.ts +7 -0
  74. package/dist/services/file-renamer.d.ts.map +1 -1
  75. package/dist/services/file-renamer.js +63 -12
  76. package/dist/services/file-renamer.js.map +1 -1
  77. package/dist/services/lmstudio-service.d.ts +35 -9
  78. package/dist/services/lmstudio-service.d.ts.map +1 -1
  79. package/dist/services/lmstudio-service.js +23 -101
  80. package/dist/services/lmstudio-service.js.map +1 -1
  81. package/dist/services/ollama-service.d.ts +23 -9
  82. package/dist/services/ollama-service.d.ts.map +1 -1
  83. package/dist/services/ollama-service.js +16 -87
  84. package/dist/services/ollama-service.js.map +1 -1
  85. package/dist/services/openai-service.d.ts +8 -6
  86. package/dist/services/openai-service.d.ts.map +1 -1
  87. package/dist/services/openai-service.js +36 -94
  88. package/dist/services/openai-service.js.map +1 -1
  89. package/dist/utils/ai-name-sanitizer.d.ts +14 -0
  90. package/dist/utils/ai-name-sanitizer.d.ts.map +1 -0
  91. package/dist/utils/ai-name-sanitizer.js +55 -0
  92. package/dist/utils/ai-name-sanitizer.js.map +1 -0
  93. package/dist/utils/ai-prompts.d.ts.map +1 -1
  94. package/dist/utils/ai-prompts.js +2 -1
  95. package/dist/utils/ai-prompts.js.map +1 -1
  96. package/dist/utils/assert-directory.d.ts +9 -0
  97. package/dist/utils/assert-directory.d.ts.map +1 -0
  98. package/dist/utils/assert-directory.js +15 -0
  99. package/dist/utils/assert-directory.js.map +1 -0
  100. package/dist/utils/file-info.d.ts +8 -0
  101. package/dist/utils/file-info.d.ts.map +1 -0
  102. package/dist/utils/file-info.js +25 -0
  103. package/dist/utils/file-info.js.map +1 -0
  104. package/dist/utils/file-templates.d.ts.map +1 -1
  105. package/dist/utils/file-templates.js +31 -1
  106. package/dist/utils/file-templates.js.map +1 -1
  107. package/dist/utils/image-compressor.d.ts +1 -1
  108. package/dist/utils/image-compressor.d.ts.map +1 -1
  109. package/dist/utils/image-compressor.js +4 -2
  110. package/dist/utils/image-compressor.js.map +1 -1
  111. package/dist/utils/logger.d.ts +2 -0
  112. package/dist/utils/logger.d.ts.map +1 -1
  113. package/dist/utils/logger.js +4 -0
  114. package/dist/utils/logger.js.map +1 -1
  115. package/dist/utils/record-session.d.ts +18 -0
  116. package/dist/utils/record-session.d.ts.map +1 -0
  117. package/dist/utils/record-session.js +24 -0
  118. package/dist/utils/record-session.js.map +1 -0
  119. package/dist/utils/ui.d.ts.map +1 -1
  120. package/dist/utils/ui.js +1 -7
  121. package/dist/utils/ui.js.map +1 -1
  122. package/package.json +17 -18
@@ -1,108 +1,30 @@
1
- import { buildFileNamePrompt, AI_SYSTEM_PROMPT } from '../utils/ai-prompts.js';
2
- import { AuthError, NetworkError, RateLimitError, ConfigError } from '../errors.js';
3
- export class LMStudioService {
4
- name = 'LMStudio';
5
- baseUrl;
6
- model;
1
+ import { AI_SYSTEM_PROMPT } from '../utils/ai-prompts.js';
2
+ import { BaseLocalService } from './base-local-service.js';
3
+ export class LMStudioService extends BaseLocalService {
7
4
  constructor(baseUrl = 'http://localhost:1234', model = 'local-model') {
8
- this.baseUrl = this.validateLocalUrl(baseUrl);
9
- this.model = model;
5
+ super('LMStudio', baseUrl, model);
10
6
  }
11
- validateLocalUrl(url) {
12
- let parsed;
13
- try {
14
- parsed = new URL(url);
15
- }
16
- catch {
17
- throw new ConfigError(`Invalid LMStudio base URL: ${url}`);
18
- }
19
- const hostname = parsed.hostname.toLowerCase();
20
- const allowed = hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '::1' || hostname === '[::1]';
21
- if (!allowed) {
22
- throw new ConfigError(`LMStudio base URL must point to localhost (got: ${hostname})`);
23
- }
24
- return url;
25
- }
26
- async generateFileName(content, originalName, namingConvention = 'kebab-case', category = 'general', fileInfo, language, context, imageData) {
27
- try {
28
- if (imageData && (!imageData.startsWith('data:image/') || !imageData.includes(','))) {
29
- throw new NetworkError('Invalid image data format');
30
- }
31
- const prompt = buildFileNamePrompt({
32
- content: imageData
33
- ? 'Analyze this image and generate an appropriate filename based on what you see.'
34
- : content,
35
- originalName,
36
- namingConvention: namingConvention,
37
- category: category,
38
- fileInfo,
39
- language,
40
- context
41
- });
42
- const userMessage = imageData
43
- ? {
44
- role: 'user',
45
- content: [
46
- { type: 'text', text: prompt },
47
- { type: 'image_url', image_url: { url: imageData } }
48
- ]
49
- }
50
- : { role: 'user', content: prompt };
51
- const response = await this.makeRequest('/v1/chat/completions', {
52
- model: this.model,
53
- messages: [
54
- { role: 'system', content: AI_SYSTEM_PROMPT },
55
- userMessage
56
- ],
57
- temperature: 0.3,
58
- max_tokens: 100,
59
- stream: false
60
- });
61
- if (response.choices?.[0]?.message?.content) {
62
- return {
63
- name: this.sanitizeFilename(response.choices[0].message.content),
64
- inputTokens: undefined,
65
- outputTokens: undefined
66
- };
7
+ async requestCompletion(prompt, imageData) {
8
+ const userMessage = imageData
9
+ ? {
10
+ role: 'user',
11
+ content: [
12
+ { type: 'text', text: prompt },
13
+ { type: 'image_url', image_url: { url: imageData } }
14
+ ]
67
15
  }
68
- throw new NetworkError('No response content from LMStudio');
69
- }
70
- catch (error) {
71
- if (error instanceof AuthError || error instanceof NetworkError || error instanceof RateLimitError || error instanceof ConfigError)
72
- throw error;
73
- throw new NetworkError(`LMStudio service failed: ${error instanceof Error ? error.message : 'Unknown error'}`, { cause: error });
74
- }
75
- }
76
- sanitizeFilename(filename) {
77
- return filename
78
- .trim()
79
- .replace(/^["']|["']$/g, '') // Remove quotes
80
- .replace(/\.(txt|pdf|docx?|xlsx?|md|rtf)$/i, '') // Remove extensions
81
- .replace(/[<>:"/\\|?*]/g, '-') // Replace invalid characters
82
- .replace(/\s+/g, '-') // Replace spaces with hyphens
83
- .toLowerCase();
84
- }
85
- async makeRequest(endpoint, payload) {
86
- const url = `${this.baseUrl}${endpoint}`;
87
- const response = await fetch(url, {
88
- method: 'POST',
89
- headers: {
90
- 'Content-Type': 'application/json',
91
- },
92
- body: JSON.stringify(payload),
16
+ : { role: 'user', content: prompt };
17
+ const response = await this.makeRequest('/v1/chat/completions', {
18
+ model: this.model,
19
+ messages: [
20
+ { role: 'system', content: AI_SYSTEM_PROMPT },
21
+ userMessage
22
+ ],
23
+ temperature: 0.3,
24
+ max_tokens: 100,
25
+ stream: false
93
26
  });
94
- if (!response.ok) {
95
- const errorText = await response.text();
96
- if (response.status === 401 || response.status === 403) {
97
- throw new AuthError(`LMStudio authentication failed`, { details: { status: response.status, body: errorText } });
98
- }
99
- if (response.status === 429) {
100
- throw new RateLimitError(`LMStudio rate limit exceeded`, { details: { status: response.status } });
101
- }
102
- throw new NetworkError(`LMStudio API request failed: ${response.status} ${response.statusText}`, { details: { status: response.status, body: errorText } });
103
- }
104
- const data = await response.json();
105
- return data;
27
+ return response.choices?.[0]?.message?.content;
106
28
  }
107
29
  // Method to check if LMStudio service is available
108
30
  async isAvailable() {
@@ -1 +1 @@
1
- {"version":3,"file":"lmstudio-service.js","sourceRoot":"","sources":["../../src/services/lmstudio-service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"lmstudio-service.js","sourceRoot":"","sources":["../../src/services/lmstudio-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAqC3D,MAAM,OAAO,eAAgB,SAAQ,gBAA+D;IAClG,YACE,OAAO,GAAG,uBAAuB,EACjC,KAAK,GAAG,aAAa;QAErB,KAAK,CAAC,UAAU,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;IACpC,CAAC;IAES,KAAK,CAAC,iBAAiB,CAAC,MAAc,EAAE,SAAkB;QAClE,MAAM,WAAW,GAAkB,SAAS;YAC1C,CAAC,CAAC;gBACE,IAAI,EAAE,MAAM;gBACZ,OAAO,EAAE;oBACP,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE;oBAC9B,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE;iBACrD;aACF;YACH,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAEtC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,sBAAsB,EAAE;YAC9D,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,QAAQ,EAAE;gBACR,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,gBAAgB,EAAE;gBAC7C,WAAW;aACZ;YACD,WAAW,EAAE,GAAG;YAChB,UAAU,EAAE,GAAG;YACf,MAAM,EAAE,KAAK;SACd,CAAC,CAAC;QAEH,OAAO,QAAQ,CAAC,OAAO,EAAE,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,CAAC;IACjD,CAAC;IAED,mDAAmD;IACnD,KAAK,CAAC,WAAW;QACf,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,YAAY,CAAC,CAAC;YAC1D,OAAO,QAAQ,CAAC,EAAE,CAAC;QACrB,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,kCAAkC;IAClC,KAAK,CAAC,UAAU;QACd,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,YAAY,CAAC,CAAC;YAC1D,IAAI,CAAC,QAAQ,CAAC,EAAE;gBAAE,OAAO,EAAE,CAAC;YAE5B,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACnC,OAAO,IAAI,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC,KAAgB,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;QAC9D,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;CACF"}
@@ -1,14 +1,28 @@
1
- import { AIProvider, FileInfo, AINameResult } from '../types/index.js';
2
- export declare class OllamaService implements AIProvider {
3
- name: string;
4
- private baseUrl;
5
- private model;
1
+ import { BaseLocalService } from './base-local-service.js';
2
+ interface OllamaResponse {
3
+ model: string;
4
+ response?: string;
5
+ message?: {
6
+ content: string;
7
+ role: string;
8
+ };
9
+ done: boolean;
10
+ }
11
+ interface OllamaChatMessage {
12
+ role: 'system' | 'user' | 'assistant';
13
+ content: string;
14
+ images?: string[];
15
+ }
16
+ interface OllamaChatRequest {
17
+ model: string;
18
+ messages: OllamaChatMessage[];
19
+ stream: boolean;
20
+ }
21
+ export declare class OllamaService extends BaseLocalService<OllamaChatRequest, OllamaResponse> {
6
22
  constructor(baseUrl?: string, model?: string);
7
- private validateLocalUrl;
8
- generateFileName(content: string, originalName: string, namingConvention?: string, category?: string, fileInfo?: FileInfo, language?: string, context?: string, imageData?: string): Promise<AINameResult>;
9
- private sanitizeFilename;
10
- private makeRequest;
23
+ protected requestCompletion(prompt: string, imageData?: string): Promise<string | undefined>;
11
24
  isAvailable(): Promise<boolean>;
12
25
  listModels(): Promise<string[]>;
13
26
  }
27
+ export {};
14
28
  //# sourceMappingURL=ollama-service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"ollama-service.d.ts","sourceRoot":"","sources":["../../src/services/ollama-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAmBvE,qBAAa,aAAc,YAAW,UAAU;IAC9C,IAAI,SAAY;IAChB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,KAAK,CAAS;gBAEV,OAAO,SAA2B,EAAE,KAAK,SAAa;IAKlE,OAAO,CAAC,gBAAgB;IAelB,gBAAgB,CACpB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,EACpB,gBAAgB,SAAe,EAC/B,QAAQ,SAAY,EACpB,QAAQ,CAAC,EAAE,QAAQ,EACnB,QAAQ,CAAC,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,YAAY,CAAC;IAyCxB,OAAO,CAAC,gBAAgB;YAUV,WAAW;IAsBnB,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAS/B,UAAU,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;CAUtC"}
1
+ {"version":3,"file":"ollama-service.d.ts","sourceRoot":"","sources":["../../src/services/ollama-service.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAE3D,UAAU,cAAc;IACtB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,IAAI,EAAE,OAAO,CAAC;CACf;AAED,UAAU,iBAAiB;IACzB,IAAI,EAAE,QAAQ,GAAG,MAAM,GAAG,WAAW,CAAC;IACtC,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,UAAU,iBAAiB;IACzB,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,iBAAiB,EAAE,CAAC;IAC9B,MAAM,EAAE,OAAO,CAAC;CACjB;AAMD,qBAAa,aAAc,SAAQ,gBAAgB,CAAC,iBAAiB,EAAE,cAAc,CAAC;gBACxE,OAAO,SAA2B,EAAE,KAAK,SAAa;cAIlD,iBAAiB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAiB5F,WAAW,IAAI,OAAO,CAAC,OAAO,CAAC;IAS/B,UAAU,IAAI,OAAO,CAAC,MAAM,EAAE,CAAC;CAUtC"}
@@ -1,93 +1,22 @@
1
- import { buildFileNamePrompt, AI_SYSTEM_PROMPT } from '../utils/ai-prompts.js';
2
- import { AuthError, NetworkError, RateLimitError, ConfigError } from '../errors.js';
3
- export class OllamaService {
4
- name = 'Ollama';
5
- baseUrl;
6
- model;
1
+ import { AI_SYSTEM_PROMPT } from '../utils/ai-prompts.js';
2
+ import { BaseLocalService } from './base-local-service.js';
3
+ export class OllamaService extends BaseLocalService {
7
4
  constructor(baseUrl = 'http://localhost:11434', model = 'llama3.1') {
8
- this.baseUrl = this.validateLocalUrl(baseUrl);
9
- this.model = model;
5
+ super('Ollama', baseUrl, model);
10
6
  }
11
- validateLocalUrl(url) {
12
- let parsed;
13
- try {
14
- parsed = new URL(url);
15
- }
16
- catch {
17
- throw new ConfigError(`Invalid Ollama base URL: ${url}`);
18
- }
19
- const hostname = parsed.hostname.toLowerCase();
20
- const allowed = hostname === 'localhost' || hostname === '127.0.0.1' || hostname === '::1' || hostname === '[::1]';
21
- if (!allowed) {
22
- throw new ConfigError(`Ollama base URL must point to localhost (got: ${hostname})`);
23
- }
24
- return url;
25
- }
26
- async generateFileName(content, originalName, namingConvention = 'kebab-case', category = 'general', fileInfo, language, context, imageData) {
27
- try {
28
- if (imageData && (!imageData.startsWith('data:image/') || !imageData.includes(','))) {
29
- throw new NetworkError('Invalid image data format');
30
- }
31
- const prompt = buildFileNamePrompt({
32
- content: imageData
33
- ? 'Analyze this image and generate an appropriate filename based on what you see.'
34
- : content,
35
- originalName,
36
- namingConvention: namingConvention,
37
- category: category,
38
- fileInfo,
39
- language,
40
- context
41
- });
42
- const userMessage = imageData
43
- ? { role: 'user', content: prompt, images: [imageData.split(',')[1]] }
44
- : { role: 'user', content: prompt };
45
- const response = await this.makeRequest('/api/chat', {
46
- model: this.model,
47
- messages: [
48
- { role: 'system', content: AI_SYSTEM_PROMPT },
49
- userMessage
50
- ],
51
- stream: false
52
- });
53
- if (response.message?.content) {
54
- return { name: this.sanitizeFilename(response.message.content), inputTokens: undefined, outputTokens: undefined };
55
- }
56
- throw new NetworkError('No response content from Ollama');
57
- }
58
- catch (error) {
59
- if (error instanceof AuthError || error instanceof NetworkError || error instanceof RateLimitError || error instanceof ConfigError)
60
- throw error;
61
- throw new NetworkError(`Ollama service failed: ${error instanceof Error ? error.message : 'Unknown error'}`, { cause: error });
62
- }
63
- }
64
- sanitizeFilename(filename) {
65
- return filename
66
- .trim()
67
- .replace(/^["']|["']$/g, '')
68
- .replace(/\.(txt|pdf|docx?|xlsx?|md|rtf)$/i, '')
69
- .replace(/[<>:"/\\|?*]/g, '-')
70
- .replace(/\s+/g, '-')
71
- .toLowerCase();
72
- }
73
- async makeRequest(endpoint, payload) {
74
- const url = `${this.baseUrl}${endpoint}`;
75
- const response = await fetch(url, {
76
- method: 'POST',
77
- headers: { 'Content-Type': 'application/json' },
78
- body: JSON.stringify(payload)
7
+ async requestCompletion(prompt, imageData) {
8
+ const userMessage = imageData
9
+ ? { role: 'user', content: prompt, images: [imageData.split(',')[1]] }
10
+ : { role: 'user', content: prompt };
11
+ const response = await this.makeRequest('/api/chat', {
12
+ model: this.model,
13
+ messages: [
14
+ { role: 'system', content: AI_SYSTEM_PROMPT },
15
+ userMessage
16
+ ],
17
+ stream: false
79
18
  });
80
- if (!response.ok) {
81
- const errorText = await response.text();
82
- if (response.status === 401 || response.status === 403) {
83
- throw new AuthError(`Ollama authentication failed`, { details: { status: response.status, body: errorText } });
84
- }
85
- if (response.status === 429) {
86
- throw new RateLimitError(`Ollama rate limit exceeded`, { details: { status: response.status } });
87
- }
88
- throw new NetworkError(`Ollama API request failed: ${response.status} ${response.statusText}`, { details: { status: response.status, body: errorText } });
89
- }
90
- return response.json();
19
+ return response.message?.content;
91
20
  }
92
21
  async isAvailable() {
93
22
  try {
@@ -1 +1 @@
1
- {"version":3,"file":"ollama-service.js","sourceRoot":"","sources":["../../src/services/ollama-service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"ollama-service.js","sourceRoot":"","sources":["../../src/services/ollama-service.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAC;AAC1D,OAAO,EAAE,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAyB3D,MAAM,OAAO,aAAc,SAAQ,gBAAmD;IACpF,YAAY,OAAO,GAAG,wBAAwB,EAAE,KAAK,GAAG,UAAU;QAChE,KAAK,CAAC,QAAQ,EAAE,OAAO,EAAE,KAAK,CAAC,CAAC;IAClC,CAAC;IAES,KAAK,CAAC,iBAAiB,CAAC,MAAc,EAAE,SAAkB;QAClE,MAAM,WAAW,GAAsB,SAAS;YAC9C,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,CAAC,SAAS,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,EAAE;YACtE,CAAC,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;QAEtC,MAAM,QAAQ,GAAG,MAAM,IAAI,CAAC,WAAW,CAAC,WAAW,EAAE;YACnD,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,QAAQ,EAAE;gBACR,EAAE,IAAI,EAAE,QAAQ,EAAE,OAAO,EAAE,gBAAgB,EAAE;gBAC7C,WAAW;aACZ;YACD,MAAM,EAAE,KAAK;SACd,CAAC,CAAC;QAEH,OAAO,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IACnC,CAAC;IAED,KAAK,CAAC,WAAW;QACf,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,WAAW,CAAC,CAAC;YACzD,OAAO,QAAQ,CAAC,EAAE,CAAC;QACrB,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,KAAK,CAAC;QACf,CAAC;IACH,CAAC;IAED,KAAK,CAAC,UAAU;QACd,IAAI,CAAC;YACH,MAAM,QAAQ,GAAG,MAAM,KAAK,CAAC,GAAG,IAAI,CAAC,OAAO,WAAW,CAAC,CAAC;YACzD,IAAI,CAAC,QAAQ,CAAC,EAAE;gBAAE,OAAO,EAAE,CAAC;YAC5B,MAAM,IAAI,GAAG,MAAM,QAAQ,CAAC,IAAI,EAAE,CAAC;YACnC,OAAO,IAAI,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,KAAsB,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,IAAI,EAAE,CAAC;QACxE,CAAC;QAAC,MAAM,CAAC;YACP,OAAO,EAAE,CAAC;QACZ,CAAC;IACH,CAAC;CACF"}
@@ -1,10 +1,12 @@
1
- import { AIProvider, FileInfo, AINameResult } from '../types/index.js';
2
- export declare class OpenAIService implements AIProvider {
3
- name: string;
1
+ import OpenAI from 'openai';
2
+ import { BaseCloudService, CloudApiError, TokenUsage } from './base-cloud-service.js';
3
+ export declare class OpenAIService extends BaseCloudService<OpenAI.Chat.Completions.ChatCompletion> {
4
4
  private client;
5
- private model;
6
5
  constructor(apiKey: string, model?: string);
7
- generateFileName(content: string, originalName: string, namingConvention?: string, category?: string, fileInfo?: FileInfo, language?: string, context?: string, imageData?: string): Promise<AINameResult>;
8
- private sanitizeFileName;
6
+ protected createTextCompletion(prompt: string): Promise<OpenAI.Chat.Completions.ChatCompletion>;
7
+ protected createImageCompletion(prompt: string, imageData: string): Promise<OpenAI.Chat.Completions.ChatCompletion>;
8
+ protected extractSuggestedName(response: OpenAI.Chat.Completions.ChatCompletion): string;
9
+ protected extractTokenUsage(response: OpenAI.Chat.Completions.ChatCompletion): TokenUsage;
10
+ protected asApiError(error: unknown): CloudApiError | undefined;
9
11
  }
10
12
  //# sourceMappingURL=openai-service.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"openai-service.d.ts","sourceRoot":"","sources":["../../src/services/openai-service.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,mBAAmB,CAAC;AAKvE,qBAAa,aAAc,YAAW,UAAU;IAC9C,IAAI,SAAY;IAChB,OAAO,CAAC,MAAM,CAAS;IACvB,OAAO,CAAC,KAAK,CAAS;gBAEV,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM;IAKpC,gBAAgB,CACpB,OAAO,EAAE,MAAM,EACf,YAAY,EAAE,MAAM,EACpB,gBAAgB,GAAE,MAAqB,EACvC,QAAQ,GAAE,MAAkB,EAC5B,QAAQ,CAAC,EAAE,QAAQ,EACnB,QAAQ,CAAC,EAAE,MAAM,EACjB,OAAO,CAAC,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,YAAY,CAAC;IA6ExB,OAAO,CAAC,gBAAgB;CAkBzB"}
1
+ {"version":3,"file":"openai-service.d.ts","sourceRoot":"","sources":["../../src/services/openai-service.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAC;AAEtF,qBAAa,aAAc,SAAQ,gBAAgB,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC;IACzF,OAAO,CAAC,MAAM,CAAS;gBAEX,MAAM,EAAE,MAAM,EAAE,KAAK,CAAC,EAAE,MAAM;IAK1C,SAAS,CAAC,oBAAoB,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC;IAS/F,SAAS,CAAC,qBAAqB,CAAC,MAAM,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,cAAc,CAAC;IAiBnH,SAAS,CAAC,oBAAoB,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,cAAc,GAAG,MAAM;IAIxF,SAAS,CAAC,iBAAiB,CAAC,QAAQ,EAAE,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,cAAc,GAAG,UAAU;IAOzF,SAAS,CAAC,UAAU,CAAC,KAAK,EAAE,OAAO,GAAG,aAAa,GAAG,SAAS;CAGhE"}
@@ -1,104 +1,46 @@
1
1
  import OpenAI from 'openai';
2
- import { AuthError, NetworkError, RateLimitError } from '../errors.js';
3
- import { applyNamingConvention, stripWindowsIllegalChars } from '../utils/naming-conventions.js';
4
- import { buildFileNamePrompt } from '../utils/ai-prompts.js';
5
- export class OpenAIService {
6
- name = 'OpenAI';
2
+ import { BaseCloudService } from './base-cloud-service.js';
3
+ export class OpenAIService extends BaseCloudService {
7
4
  client;
8
- model;
9
5
  constructor(apiKey, model) {
6
+ super('OpenAI', model ?? 'gpt-5.5');
10
7
  this.client = new OpenAI({ apiKey });
11
- this.model = model ?? 'gpt-4o';
12
8
  }
13
- async generateFileName(content, originalName, namingConvention = 'kebab-case', category = 'general', fileInfo, language, context, imageData) {
14
- const convention = namingConvention;
15
- const fileCategory = category;
16
- try {
17
- let response;
18
- if (imageData) {
19
- if (!imageData.startsWith('data:image/') || !imageData.includes(',')) {
20
- throw new NetworkError('Invalid image data format');
21
- }
22
- const prompt = buildFileNamePrompt({
23
- content: 'Analyze this image and generate an appropriate filename based on what you see.',
24
- originalName,
25
- namingConvention: convention,
26
- category: fileCategory,
27
- fileInfo,
28
- language,
29
- context
30
- });
31
- response = await this.client.chat.completions.create({
32
- model: this.model,
33
- messages: [
34
- {
35
- role: 'user',
36
- content: [
37
- { type: 'text', text: prompt },
38
- { type: 'image_url', image_url: { url: imageData } }
39
- ]
40
- }
41
- ],
42
- max_tokens: 100,
43
- temperature: 0.3
44
- });
45
- }
46
- else {
47
- const prompt = buildFileNamePrompt({
48
- content,
49
- originalName,
50
- namingConvention: convention,
51
- category: fileCategory,
52
- fileInfo,
53
- language,
54
- context
55
- });
56
- response = await this.client.chat.completions.create({
57
- model: this.model,
58
- messages: [{ role: 'user', content: prompt }],
59
- max_tokens: 100,
60
- temperature: 0.3
61
- });
62
- }
63
- const suggestedName = response.choices[0]?.message?.content?.trim() || 'untitled-document';
64
- return {
65
- name: this.sanitizeFileName(suggestedName, convention),
66
- inputTokens: response.usage?.prompt_tokens,
67
- outputTokens: response.usage?.completion_tokens
68
- };
69
- }
70
- catch (error) {
71
- if (error instanceof AuthError || error instanceof NetworkError || error instanceof RateLimitError)
72
- throw error;
73
- if (error instanceof OpenAI.APIError) {
74
- if (error.status === 401 || error.status === 403) {
75
- throw new AuthError(`OpenAI authentication failed: ${error.message}`, { details: { status: error.status } });
76
- }
77
- if (error.status === 429) {
78
- throw new RateLimitError(`OpenAI rate limit exceeded: ${error.message}`, { details: { status: error.status } });
9
+ createTextCompletion(prompt) {
10
+ return this.client.chat.completions.create({
11
+ model: this.model,
12
+ messages: [{ role: 'user', content: prompt }],
13
+ max_tokens: 100,
14
+ temperature: 0.3
15
+ });
16
+ }
17
+ createImageCompletion(prompt, imageData) {
18
+ return this.client.chat.completions.create({
19
+ model: this.model,
20
+ messages: [
21
+ {
22
+ role: 'user',
23
+ content: [
24
+ { type: 'text', text: prompt },
25
+ { type: 'image_url', image_url: { url: imageData } }
26
+ ]
79
27
  }
80
- throw new NetworkError(`OpenAI API error (${error.status}): ${error.message}`, { details: { status: error.status } });
81
- }
82
- throw new NetworkError(`Failed to generate filename with OpenAI: ${error instanceof Error ? error.message : 'Unknown error'}`, { cause: error });
83
- }
28
+ ],
29
+ max_tokens: 100,
30
+ temperature: 0.3
31
+ });
32
+ }
33
+ extractSuggestedName(response) {
34
+ return response.choices[0]?.message?.content?.trim() || 'untitled-document';
35
+ }
36
+ extractTokenUsage(response) {
37
+ return {
38
+ inputTokens: response.usage?.prompt_tokens,
39
+ outputTokens: response.usage?.completion_tokens
40
+ };
84
41
  }
85
- sanitizeFileName(name, convention) {
86
- const nameWithoutExt = name.replace(/\.[^/.]+$/, '');
87
- const safeForWindows = stripWindowsIllegalChars(nameWithoutExt);
88
- let cleaned = applyNamingConvention(safeForWindows, convention);
89
- if (!cleaned) {
90
- cleaned = applyNamingConvention('untitled document', convention);
91
- }
92
- else if (cleaned.length > 100) {
93
- cleaned = cleaned.substring(0, 100);
94
- if (convention === 'kebab-case') {
95
- cleaned = cleaned.replace(/-[^-]*$/, '');
96
- }
97
- else if (convention === 'snake_case') {
98
- cleaned = cleaned.replace(/_[^_]*$/, '');
99
- }
100
- }
101
- return cleaned;
42
+ asApiError(error) {
43
+ return error instanceof OpenAI.APIError ? error : undefined;
102
44
  }
103
45
  }
104
46
  //# sourceMappingURL=openai-service.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"openai-service.js","sourceRoot":"","sources":["../../src/services/openai-service.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"openai-service.js","sourceRoot":"","sources":["../../src/services/openai-service.ts"],"names":[],"mappings":"AAAA,OAAO,MAAM,MAAM,QAAQ,CAAC;AAC5B,OAAO,EAAE,gBAAgB,EAA6B,MAAM,yBAAyB,CAAC;AAEtF,MAAM,OAAO,aAAc,SAAQ,gBAAwD;IACjF,MAAM,CAAS;IAEvB,YAAY,MAAc,EAAE,KAAc;QACxC,KAAK,CAAC,QAAQ,EAAE,KAAK,IAAI,SAAS,CAAC,CAAC;QACpC,IAAI,CAAC,MAAM,GAAG,IAAI,MAAM,CAAC,EAAE,MAAM,EAAE,CAAC,CAAC;IACvC,CAAC;IAES,oBAAoB,CAAC,MAAc;QAC3C,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;YACzC,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,CAAC;YAC7C,UAAU,EAAE,GAAG;YACf,WAAW,EAAE,GAAG;SACjB,CAAC,CAAC;IACL,CAAC;IAES,qBAAqB,CAAC,MAAc,EAAE,SAAiB;QAC/D,OAAO,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,WAAW,CAAC,MAAM,CAAC;YACzC,KAAK,EAAE,IAAI,CAAC,KAAK;YACjB,QAAQ,EAAE;gBACR;oBACE,IAAI,EAAE,MAAM;oBACZ,OAAO,EAAE;wBACP,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE;wBAC9B,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,EAAE,GAAG,EAAE,SAAS,EAAE,EAAE;qBACrD;iBACF;aACF;YACD,UAAU,EAAE,GAAG;YACf,WAAW,EAAE,GAAG;SACjB,CAAC,CAAC;IACL,CAAC;IAES,oBAAoB,CAAC,QAAgD;QAC7E,OAAO,QAAQ,CAAC,OAAO,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,IAAI,mBAAmB,CAAC;IAC9E,CAAC;IAES,iBAAiB,CAAC,QAAgD;QAC1E,OAAO;YACL,WAAW,EAAE,QAAQ,CAAC,KAAK,EAAE,aAAa;YAC1C,YAAY,EAAE,QAAQ,CAAC,KAAK,EAAE,iBAAiB;SAChD,CAAC;IACJ,CAAC;IAES,UAAU,CAAC,KAAc;QACjC,OAAO,KAAK,YAAY,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,SAAS,CAAC;IAC9D,CAAC;CACF"}
@@ -0,0 +1,14 @@
1
+ import { NamingConvention } from './naming-conventions.js';
2
+ /**
3
+ * Sanitizes AI-suggested filenames from cloud providers (Claude, OpenAI):
4
+ * strips the extension and Windows-illegal characters, applies the naming
5
+ * convention, and truncates overly long names at a word boundary.
6
+ */
7
+ export declare function sanitizeCloudFileName(name: string, convention: NamingConvention): string;
8
+ /**
9
+ * Sanitizes AI-suggested filenames from local providers (Ollama, LMStudio):
10
+ * strips surrounding quotes and known extensions, replaces invalid characters
11
+ * and whitespace with hyphens, and lowercases the result.
12
+ */
13
+ export declare function sanitizeLocalFileName(filename: string): string;
14
+ //# sourceMappingURL=ai-name-sanitizer.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-name-sanitizer.d.ts","sourceRoot":"","sources":["../../src/utils/ai-name-sanitizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAmD,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAmB5G;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,IAAI,EAAE,MAAM,EAAE,UAAU,EAAE,gBAAgB,GAAG,MAAM,CAkBxF;AAED;;;;GAIG;AACH,wBAAgB,qBAAqB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAQ9D"}
@@ -0,0 +1,55 @@
1
+ import { applyNamingConvention, stripWindowsIllegalChars } from './naming-conventions.js';
2
+ import { ParseError } from '../errors.js';
3
+ const MAX_NAME_WORDS = 12;
4
+ const PROSE_PREFIX = /^(based on|i can|i cannot|i am|i'm|this is|this appears|the document|the image|unable to|sorry|it appears|here is|here's)\b/i;
5
+ /**
6
+ * Reject AI output that is an explanation rather than a filename ("Based on
7
+ * the document content, I can only identify…"). Truncating prose to 100 chars
8
+ * produces garbage names — failing the file is the honest outcome.
9
+ */
10
+ function assertLooksLikeFileName(raw) {
11
+ const asWords = raw.replace(/[-_]+/g, ' ').trim();
12
+ const wordCount = asWords.split(/\s+/).filter(Boolean).length;
13
+ if (wordCount > MAX_NAME_WORDS || PROSE_PREFIX.test(asWords)) {
14
+ throw new ParseError(`AI returned an explanation instead of a filename: "${raw.slice(0, 80)}…"`);
15
+ }
16
+ }
17
+ /**
18
+ * Sanitizes AI-suggested filenames from cloud providers (Claude, OpenAI):
19
+ * strips the extension and Windows-illegal characters, applies the naming
20
+ * convention, and truncates overly long names at a word boundary.
21
+ */
22
+ export function sanitizeCloudFileName(name, convention) {
23
+ const nameWithoutExt = name.replace(/\.[^/.]+$/, '');
24
+ assertLooksLikeFileName(nameWithoutExt);
25
+ const safeForWindows = stripWindowsIllegalChars(nameWithoutExt);
26
+ let cleaned = applyNamingConvention(safeForWindows, convention);
27
+ if (!cleaned) {
28
+ cleaned = applyNamingConvention('untitled document', convention);
29
+ }
30
+ else if (cleaned.length > 100) {
31
+ cleaned = cleaned.substring(0, 100);
32
+ if (convention === 'kebab-case') {
33
+ cleaned = cleaned.replace(/-[^-]*$/, '');
34
+ }
35
+ else if (convention === 'snake_case') {
36
+ cleaned = cleaned.replace(/_[^_]*$/, '');
37
+ }
38
+ }
39
+ return cleaned;
40
+ }
41
+ /**
42
+ * Sanitizes AI-suggested filenames from local providers (Ollama, LMStudio):
43
+ * strips surrounding quotes and known extensions, replaces invalid characters
44
+ * and whitespace with hyphens, and lowercases the result.
45
+ */
46
+ export function sanitizeLocalFileName(filename) {
47
+ const unquoted = filename.trim().replace(/^["']|["']$/g, '');
48
+ assertLooksLikeFileName(unquoted);
49
+ return unquoted
50
+ .replace(/\.(txt|pdf|docx?|xlsx?|md|rtf)$/i, '') // Remove extensions
51
+ .replace(/[<>:"/\\|?*]/g, '-') // Replace invalid characters
52
+ .replace(/\s+/g, '-') // Replace spaces with hyphens
53
+ .toLowerCase();
54
+ }
55
+ //# sourceMappingURL=ai-name-sanitizer.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ai-name-sanitizer.js","sourceRoot":"","sources":["../../src/utils/ai-name-sanitizer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,qBAAqB,EAAE,wBAAwB,EAAoB,MAAM,yBAAyB,CAAC;AAC5G,OAAO,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAE1C,MAAM,cAAc,GAAG,EAAE,CAAC;AAC1B,MAAM,YAAY,GAAG,8HAA8H,CAAC;AAEpJ;;;;GAIG;AACH,SAAS,uBAAuB,CAAC,GAAW;IAC1C,MAAM,OAAO,GAAG,GAAG,CAAC,OAAO,CAAC,QAAQ,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAAC;IAClD,MAAM,SAAS,GAAG,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,MAAM,CAAC;IAC9D,IAAI,SAAS,GAAG,cAAc,IAAI,YAAY,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QAC7D,MAAM,IAAI,UAAU,CAAC,sDAAsD,GAAG,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,IAAI,CAAC,CAAC;IACnG,CAAC;AACH,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,IAAY,EAAE,UAA4B;IAC9E,MAAM,cAAc,GAAG,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;IACrD,uBAAuB,CAAC,cAAc,CAAC,CAAC;IACxC,MAAM,cAAc,GAAG,wBAAwB,CAAC,cAAc,CAAC,CAAC;IAChE,IAAI,OAAO,GAAG,qBAAqB,CAAC,cAAc,EAAE,UAAU,CAAC,CAAC;IAEhE,IAAI,CAAC,OAAO,EAAE,CAAC;QACb,OAAO,GAAG,qBAAqB,CAAC,mBAAmB,EAAE,UAAU,CAAC,CAAC;IACnE,CAAC;SAAM,IAAI,OAAO,CAAC,MAAM,GAAG,GAAG,EAAE,CAAC;QAChC,OAAO,GAAG,OAAO,CAAC,SAAS,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QACpC,IAAI,UAAU,KAAK,YAAY,EAAE,CAAC;YAChC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAC3C,CAAC;aAAM,IAAI,UAAU,KAAK,YAAY,EAAE,CAAC;YACvC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,SAAS,EAAE,EAAE,CAAC,CAAC;QAC3C,CAAC;IACH,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC;AAED;;;;GAIG;AACH,MAAM,UAAU,qBAAqB,CAAC,QAAgB;IACpD,MAAM,QAAQ,GAAG,QAAQ,CAAC,IAAI,EAAE,CAAC,OAAO,CAAC,cAAc,EAAE,EAAE,CAAC,CAAC;IAC7D,uBAAuB,CAAC,QAAQ,CAAC,CAAC;IAClC,OAAO,QAAQ;SACZ,OAAO,CAAC,kCAAkC,EAAE,EAAE,CAAC,CAAC,oBAAoB;SACpE,OAAO,CAAC,eAAe,EAAE,GAAG,CAAC,CAAC,6BAA6B;SAC3D,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC,8BAA8B;SACnD,WAAW,EAAE,CAAC;AACnB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"ai-prompts.d.ts","sourceRoot":"","sources":["../../src/utils/ai-prompts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAyB,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAClF,OAAO,EAA2B,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAE5E,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,QAAQ,EAAE,YAAY,CAAC;IACvB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CA2DlE;AAED;;GAEG;AACH,eAAO,MAAM,gBAAgB,0KAA0K,CAAC"}
1
+ {"version":3,"file":"ai-prompts.d.ts","sourceRoot":"","sources":["../../src/utils/ai-prompts.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAC7C,OAAO,EAAyB,gBAAgB,EAAE,MAAM,yBAAyB,CAAC;AAClF,OAAO,EAA2B,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAE5E,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,CAAC;IAChB,YAAY,EAAE,MAAM,CAAC;IACrB,gBAAgB,EAAE,gBAAgB,CAAC;IACnC,QAAQ,EAAE,YAAY,CAAC;IACvB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,OAAO,EAAE,aAAa,GAAG,MAAM,CA4DlE;AAED;;GAEG;AACH,eAAO,MAAM,gBAAgB,0KAA0K,CAAC"}
@@ -57,7 +57,8 @@ Document Properties:`;
57
57
  - Ignore irrelevant folder names that don't describe the document content
58
58
  - Only use letters, numbers, and appropriate separators for the naming convention
59
59
  - Focus on the document's actual content and purpose, not just metadata
60
- - Current filename stem (without extension): "${currentStem}" if the name you would generate is very similar or equivalent to this (same words, minor formatting differences), return this exact stem unchanged
60
+ - Prefer distinctive identifying details from the content (issuer/company/organization, document type, reference or invoice number, billing period) over generic terms avoid vague names like "scanned-document", "bill-invoice", or "document-scan" unless the content truly offers nothing specific
61
+ - Current filename stem (without extension): "${currentStem}" — if the name you would generate is very similar or equivalent to this (same words, minor formatting differences), return this exact stem unchanged. Never do this when the stem is a meaningless camera/scanner/export name (e.g. IMG_1234, scan-001, DSC_0042); in that case describe whatever is identifiable in the content instead
61
62
 
62
63
  ${metadataContext}
63
64
  ${contextSection}