codesummary 1.2.0 → 1.2.2

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/features.md CHANGED
@@ -2,48 +2,13 @@
2
2
 
3
3
  ## 1. Overview
4
4
 
5
- **CodeSummary** is a **Node.js-based, cross-platform CLI tool** (distributed via **npm**) that automatically scans a project's source code and generates output in three formats:
5
+ **CodeSummary** is a Node.js-based, cross-platform CLI tool that automatically scans project source code to generate documentation optimized for both Large Language Models (LLMs) and human auditors.
6
6
 
7
- - **PDF**: clean, professional A4 documentation for code reviews, audits, and archival snapshots
8
- - **RAG JSON**: structured output with semantic chunks, byte offsets, and token estimates — built for vector databases and programmatic LLM integration
9
- - **LLM Markdown**: a single token-optimised Markdown file for pasting directly into any chat-based LLM (any LLM chat interface)
10
-
11
- > **Repository**: [https://github.com/skamoll/CodeSummary](https://github.com/skamoll/CodeSummary)
12
- > **npm Package Name**: `codesummary`
13
-
14
- ---
15
-
16
- ### 1.1 Target Audience
17
-
18
- - **Developers** who need quick, complete overviews of large projects
19
- - **Auditors / Consultants** requiring traceable documentation snapshots
20
- - **Educators / Students** preparing comprehensive code handovers
21
- - **AI Engineers** building RAG pipelines or feeding code into vector databases
22
- - **Anyone** who wants to work with their codebase inside a chat-based LLM efficiently
23
-
24
- ---
25
-
26
- ### 1.2 Core Objectives
27
-
28
- 1. **Three output modes** — PDF for humans, RAG JSON for machines, LLM Markdown for chat
29
- 2. **Cross-platform reliability** — identical behaviour on Windows, macOS, and Linux
30
- 3. **Lossless content optimisation** — reduce token count without altering code meaning
31
- 4. **Smart config migration** — new defaults merge into existing config without data loss
32
- 5. **Versioned output** — `-v1`, `-v2` suffixes prevent overwrites and timestamp clutter
33
- 6. **Non-interactive operation** — `--no-interactive` for CI/CD pipelines
34
-
35
- ---
36
-
37
- ### 1.3 Technology Stack
38
-
39
- - **Node.js** ≥ 18 (native ES modules)
40
- - **PDFKit** for PDF generation with streaming support
41
- - **Inquirer.js** for interactive prompts
42
- - **Chalk** for terminal styling
43
- - **Ora** for progress indicators
44
- - **fs-extra** for enhanced file system operations
45
- - **js-yaml** for YAML config loading
46
- - **ajv** for JSON schema validation
7
+ ### 1.1 Core Objectives
8
+ 1. **LLM-First Context**: Minimize token usage while preserving semantic meaning for AI consumption.
9
+ 2. **Professional Documentation**: Provide high-fidelity PDF snapshots for audits and code reviews.
10
+ 3. **Cross-Platform Reliability**: Consistent behavior across Windows, macOS, and Linux.
11
+ 4. **Quality Guaranteed**: Built-in regression testing suite to ensure reliable code analysis.
47
12
 
48
13
  ---
49
14
 
@@ -52,367 +17,41 @@
52
17
  ### 2.1 Command-Line Interface
53
18
 
54
19
  #### 2.1.1 Primary Commands
55
-
56
20
  | Command | Description |
57
- |---------|-------------|
58
- | `codesummary` | Scan current directory, generate PDF |
59
- | `codesummary --format rag` | Generate RAG-optimised JSON |
60
- | `codesummary --format llm` | Generate LLM-optimised Markdown |
61
- | `codesummary --format both` | Generate PDF + RAG JSON (single scan) |
62
- | `codesummary config` | Launch interactive configuration editor |
63
- | `codesummary --show-config` | Display current configuration |
64
- | `codesummary --reset-config` | Reset to defaults and run setup wizard |
65
- | `codesummary --help` | Show help |
66
- | `codesummary --version` | Show version |
67
-
68
- #### 2.1.2 Command-Line Options
69
-
70
- | Option | Short | Description |
71
- |--------|-------|-------------|
72
- | `--format <format>` | `-f` | `pdf` (default), `rag`, `llm`, or `both` |
73
- | `--output <path>` | `-o` | Override output directory for this run |
74
- | `--no-interactive` | | Skip all prompts; auto-select all extensions |
75
- | `--show-config` | | Display current configuration |
76
- | `--reset-config` | | Reset configuration to defaults |
77
- | `--help` | `-h` | Show help message |
78
- | `--version` | `-v` | Show version |
79
-
80
- #### 2.1.3 Interactive Workflow
81
-
82
- 1. **First-run setup** — detects missing config, launches setup wizard, creates output directory
83
- 2. **Directory scanning** — recursive scan with whitelist filtering and exclusion rules
84
- 3. **Extension selection** — checkbox prompt with file counts; skipped with `--no-interactive`
85
- 4. **Generation** — selected format(s) generated, versioned filenames used if target exists
21
+ | :--- | :--- |
22
+ | `codesummary` | Scan current directory and generate PDF (default). |
23
+ | `codesummary --format llm` | Generate LLM-optimized Markdown. |
24
+ | `codesummary --format both` | Generate both PDF and LLM Markdown. |
25
+ | `codesummary config` | Launch the interactive configuration editor. |
26
+ | `npm test` | Execute the automated regression test suite. |
86
27
 
87
28
  ---
88
29
 
89
30
  ### 2.2 Output Formats
90
-
91
- #### 2.2.1 PDF (`--format pdf`)
92
-
93
- Generates a professional A4 PDF with three sections:
94
-
95
- 1. **Project overview**: title, project name, timestamp, included file types
96
- 2. **File structure**: sorted complete file listing
97
- 3. **File content**: full source of every selected file, monospace font, no truncation
98
-
99
- File naming: `PROJECTNAME_code.pdf` → `PROJECTNAME_code-v1.pdf` → `PROJECTNAME_code-v2.pdf` ...
100
-
101
- #### 2.2.2 RAG JSON (`--format rag`)
102
-
103
- Generates a structured JSON file built for embedding and retrieval in AI/ML pipelines.
104
-
105
- **When to use RAG:**
106
- - Loading code into a vector database (Pinecone, Qdrant, Chroma, etc.)
107
- - Building a retrieval-augmented generation pipeline
108
- - Programmatic LLM integration where you control chunking and retrieval
109
- - Rapid chunk seeking via byte offsets without re-parsing the full JSON
110
-
111
- **JSON structure:**
112
- ```json
113
- {
114
- "metadata": { "projectName": "...", "generatedAt": "...", "version": "..." },
115
- "files": [
116
- {
117
- "id": "abc123",
118
- "path": "src/component.js",
119
- "language": "JavaScript",
120
- "hash": "sha256-...",
121
- "chunks": [
122
- {
123
- "id": "chunk_abc123_0",
124
- "content": "function myFn() { ... }",
125
- "tokenEstimate": 45,
126
- "lineStart": 1,
127
- "lineEnd": 15,
128
- "chunkingMethod": "semantic-function",
129
- "context": "function_myFn",
130
- "imports": ["react"],
131
- "calls": ["useState"]
132
- }
133
- ]
134
- }
135
- ],
136
- "index": {
137
- "chunkOffsets": {
138
- "chunk_abc123_0": { "contentStart": 12123, "contentEnd": 12356 }
139
- },
140
- "statistics": { "processingTimeMs": 245, "chunksWithValidOffsets": 387 }
141
- }
142
- }
143
- ```
144
-
145
- **Key RAG features:**
146
- - Semantic chunking by function, class, or logical block
147
- - Byte-accurate content offsets for fast random access
148
- - SHA-256 file hashes for deduplication
149
- - Language-aware token estimation (±20% accuracy)
150
- - Import and call graph extraction
151
- - YAML-configurable via `raggen.config.yaml`
152
-
153
- File naming: `PROJECTNAME_rag.json` → `PROJECTNAME_rag-v1.json` → ...
154
-
155
- #### 2.2.3 LLM Markdown (`--format llm`)
156
-
157
- Generates a single Markdown file optimised for direct consumption by chat-based LLMs.
158
-
159
- **When to use LLM Markdown:**
160
- - Asking any LLM chat interface to review or explain your codebase
161
- - One-off questions that don't justify setting up a RAG pipeline
162
- - Sharing project context in a conversation without a file upload feature
163
-
164
- **File structure:**
165
- ```markdown
166
- # ProjectName — Code Summary
167
-
168
- **Generated:** 2026-04-05 | **Files:** 42 | **Total size:** 1.2 MB
31
+ ... (unchanged) ...
169
32
 
170
33
  ---
171
34
 
172
- ## File Tree
35
+ ### 2.3 Quality Control
173
36
 
174
- ```
175
- src/cli.js
176
- src/scanner.js
177
- ...
178
- ```
179
-
180
- ---
181
-
182
- ## src/cli.js
183
-
184
- ```js
185
- // full file content
186
- ```
187
- ```
188
-
189
- **Lossless optimisations applied automatically:**
190
-
191
- | Optimisation | Applies to | Notes |
192
- |---|---|---|
193
- | Normalise line endings (`\r\n` → `\n`) | All files | Safe for all languages |
194
- | Strip trailing whitespace per line | All files | Never has semantic meaning |
195
- | Remove leading/trailing blank lines | All files | Per-file trimming |
196
- | Compact JSON | `.json` files | Re-serialise without indentation |
197
- | Max 2 consecutive blank lines | `.md` / `.mdx` | Preserves paragraph semantics |
198
- | Max 1 consecutive blank line | All other files | Removes relleno without touching indentation |
199
-
200
- **What is never modified:**
201
- - Indentation (critical for Python, YAML, Makefiles)
202
- - Multiple spaces within a line (may be in string literals)
203
- - Comments
204
- - Code logic
205
-
206
- File naming: `PROJECTNAME_llm.md` → `PROJECTNAME_llm-v1.md` → ...
207
-
208
- #### 2.2.4 Both (`--format both`)
209
-
210
- Runs PDF and RAG generation in sequence using a single scan pass. Uses continue-on-error: if one format fails, the other still completes. Exit code 1 if either failed.
211
-
212
- ---
213
-
214
- ### 2.3 Configuration Management
215
-
216
- #### 2.3.1 Storage Locations
217
-
218
- - **Linux/macOS**: `~/.codesummary/config.json`
219
- - **Windows**: `%APPDATA%\CodeSummary\config.json`
220
-
221
- #### 2.3.2 Configuration Structure
222
-
223
- ```json
224
- {
225
- "configVersion": "1.1.0",
226
- "output": {
227
- "mode": "fixed | relative",
228
- "fixedPath": "absolute path"
229
- },
230
- "allowedExtensions": ["array of extensions"],
231
- "excludeDirs": ["array of directory names"],
232
- "excludeFiles": ["array of glob patterns"],
233
- "styles": { "colors": {}, "layout": {}, "fonts": {} },
234
- "settings": {
235
- "documentTitle": "Project Code Summary",
236
- "maxFilesBeforePrompt": 500
237
- }
238
- }
239
- ```
240
-
241
- #### 2.3.3 Smart Migration
242
-
243
- On every run, new defaults are merged into the existing config using `smartMergeArrays`:
244
- - Items already present are kept in place
245
- - New items are appended at the end
246
- - User removals are respected (removed items are not re-added)
247
- - Changes are saved automatically and the user is notified
248
-
249
- #### 2.3.4 Interactive Editor
250
-
251
- Sections available via `codesummary config`:
252
- - Output settings (mode, fixed path)
253
- - Allowed extensions
254
- - Excluded directories
255
- - Excluded file patterns
256
- - General settings (document title, file warning threshold)
37
+ #### 2.3.1 Automated Testing
38
+ The project includes a comprehensive suite of automated tests using the native Node.js test runner:
39
+ - **Scanner Validation**: Ensures `.csignore` rules and directory exclusions are applied correctly.
40
+ - **Graph Engine Accuracy**: Verifies that file-to-file dependencies are correctly resolved across multiple languages.
41
+ - **Config Integrity**: Tests the stability of the global configuration manager and its migration paths.
257
42
 
258
43
  ---
259
44
 
260
45
  ### 2.4 File System Scanning
261
-
262
- #### 2.4.1 Algorithm
263
-
264
- 1. Recursive directory traversal from `process.cwd()`
265
- 2. Whitelist filtering by allowed extensions
266
- 3. Directory exclusion by exact name match + built-in common-skip list
267
- 4. File exclusion by glob pattern matching
268
- 5. Symlink detection (skipped to avoid loops)
269
- 6. File size limit: 100 MB per file
270
- 7. Duplicate detection via absolute path tracking
271
-
272
- #### 2.4.2 Supported Extensions (defaults)
273
-
274
- **Web & JavaScript ecosystem:**
275
- `.js`, `.jsx`, `.ts`, `.tsx`, `.vue`, `.svelte`, `.astro`, `.mdx`
276
-
277
- **Backend languages:**
278
- `.py`, `.java`, `.cs`, `.cpp`, `.c`, `.h`, `.go`, `.rs`, `.swift`, `.kt`, `.scala`, `.rb`, `.php`, `.dart`, `.lua`, `.r`, `.ex`, `.exs`, `.pl`
279
-
280
- **Web & markup:**
281
- `.html`, `.css`, `.scss`, `.xml`
282
-
283
- **Data & config:**
284
- `.json`, `.yaml`, `.yml`, `.toml`, `.ini`, `.properties`, `.tf`, `.tfvars`, `.env`, `.local`, `.cfg`, `.conf`
285
-
286
- **Schema & query:**
287
- `.sql`, `.graphql`, `.gql`, `.proto`, `.prisma`
288
-
289
- **Scripts:**
290
- `.sh`, `.bat`, `.ps1`, `.mk`, `.cmake`
291
-
292
- **Documentation:**
293
- `.md`, `.mdx`, `.txt`
294
-
295
- **Specialised:**
296
- `.ino` (Arduino), `.j2` (Jinja2), `.service`, `.timer` (systemd), `.crt` (certificates), `.csv`, `.tsv`
297
-
298
- #### 2.4.3 Default Excluded Directories
299
-
300
- Build output: `dist`, `build`, `out`, `target`
301
- Dependencies: `node_modules`, `vendor`, `bower_components`
302
- Caches: `.cache`, `.turbo`, `.gradle`, `.yarn`, `.pnpm-store`, `.pytest_cache`, `.mypy_cache`, `.tox`, `htmlcov`
303
- IDE: `.git`, `.vscode`, `.idea`
304
- Framework: `.next`, `.nuxt`, `.angular`, `.svelte-kit`, `.expo`, `.dart_tool`, `storybook-static`
305
- Python: `__pycache__`, `venv`, `.venv`
306
- Infrastructure: `.terraform`
307
-
308
- #### 2.4.4 Default Excluded File Patterns
309
-
310
- Lock files: `*-lock.json`, `*.lock`, `composer.lock`, `Pipfile.lock`, `*-lock.yaml`
311
- Minified: `*.min.js`, `*.min.css`, `*.map`
312
- Compiled: `*.pyc`, `*.pyo`, `*.class`
313
- Temporary: `*.log`, `*.tmp`, `*.temp`, `*.swp`, `*.bak`, `*.orig`
314
- OS metadata: `.DS_Store`, `Thumbs.db`, `desktop.ini`, `ehthumbs.db`
315
-
316
- ---
317
-
318
- ### 2.5 Versioned Output Files
319
-
320
- When a target file already exists, a `-vN` suffix is added instead of overwriting:
321
-
322
- ```
323
- PROJECTNAME_llm.md ← exists
324
- PROJECTNAME_llm-v1.md ← created
325
- ← next run: v1 exists
326
- PROJECTNAME_llm-v2.md ← created
327
- ```
328
-
329
- Applies to all three formats. Existing `-vN` suffixes are stripped before re-versioning to avoid `name-v1-v1.md`.
330
-
331
- ---
332
-
333
- ### 2.6 Non-Interactive Mode
334
-
335
- `--no-interactive` (or non-TTY stdin) skips:
336
- - Extension selection checkbox → all detected extensions selected
337
- - File count confirmation prompt → proceeds automatically
338
-
339
- Designed for use in CI/CD pipelines and scripted environments.
340
-
341
- ---
342
-
343
- ### 2.7 Error Handling
344
-
345
- - **Path traversal prevention**: blocks `..`, null bytes, and Windows reserved names
346
- - **Non-ASCII path support**: Unicode characters in paths (e.g. `C:\Users\Andrés\...`) are preserved correctly
347
- - **Graceful scan errors**: permission denied and missing files are logged but don't abort the scan
348
- - **PDF stream errors**: file-in-use (EBUSY/EACCES) triggers versioned filename fallback
349
- - **LLM/RAG errors**: unreadable files emit a warning block in output instead of crashing
350
- - **`--format both` failures**: continue-on-error; both outputs attempted, all errors reported together
46
+ - **Recursive Traversal**: Deep scan of the current directory.
47
+ - **Smart Filtering**: Respects `allowedExtensions` and `excludeDirs` from global config.
48
+ - **.csignore Support**: High-priority exclusion rules using standard gitignore syntax.
351
49
 
352
50
  ---
353
51
 
354
52
  ## 3. Technical Architecture
355
-
356
- ### 3.1 Module Structure
357
-
358
- ```
359
- src/
360
- ├── cli.js # Argument parsing, orchestration, user interaction
361
- ├── scanner.js # Recursive directory scanning and filtering
362
- ├── pdfGenerator.js # PDF generation (PDFKit, streaming)
363
- ├── ragGenerator.js # RAG JSON generation with semantic chunking
364
- ├── llmGenerator.js # LLM Markdown generation with content optimisations
365
- ├── configManager.js # Global config load, save, migrate, edit
366
- ├── ragConfig.js # RAG-specific config (YAML loading, defaults)
367
- ├── errorHandler.js # Path validation, sanitisation, global error handlers
368
- └── utils.js # Shared: formatFileSize, getExtensionDescription,
369
- # matchesGlobPattern, resolveVersionedPath
370
- ```
371
-
372
- ### 3.2 Data Flow
373
-
374
- ```
375
- bin/codesummary.js
376
- └─ src/index.js (bootstrap)
377
- └─ src/cli.js (parse args → executeMainFlow)
378
- ├─ scanner.js (scan → filesByExtension)
379
- ├─ pdfGenerator.js (format: pdf)
380
- ├─ ragGenerator.js (format: rag) ← uses ragConfig.js
381
- └─ llmGenerator.js (format: llm)
382
- ```
383
-
384
- ### 3.3 Key Design Decisions
385
-
386
- - **ESM modules** throughout (`"type": "module"`)
387
- - **No singleton exports** — all modules export classes, instantiated at call site
388
- - **Shared utilities** in `utils.js` — single source of truth, no duplication
389
- - **Streaming writes** for PDF and LLM output — memory-efficient on large projects
390
- - **Static imports** only — dynamic `import()` avoided for consistency
391
-
392
- ---
393
-
394
- ## 4. Security
395
-
396
- - Path traversal (`..`) blocked via pattern matching before any file operation
397
- - User-supplied paths sanitised: control characters and injection sequences removed
398
- - Unicode characters in paths preserved (non-ASCII allowed)
399
- - Windows reserved device names (CON, NUL, COM1, etc.) rejected
400
- - No external network calls at runtime — fully offline operation
401
- - Config validated against schema before use; corrupt config prompts reset
402
-
403
- ---
404
-
405
- ## 5. Future Enhancements
406
-
407
- - `--format all`: PDF + RAG + LLM in a single pass
408
- - Syntax highlighting in PDF output
409
- - Clickable table of contents with bookmarks in PDF
410
- - Git integration: document only changed files since last commit
411
- - CI/CD plugin for automated documentation on push
412
- - Custom PDF themes and styling
53
+ ... (unchanged) ...
413
54
 
414
55
  ---
415
-
416
- **Document Version**: 3.0
417
- **Last Updated**: 2026-04-05
418
- **Status**: Implementation Complete
56
+ **Status**: Feature Complete (v1.2.1)
57
+ **Last Updated**: 2026-04-18
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "codesummary",
3
- "version": "1.2.0",
4
- "description": "Cross-platform CLI tool that generates PDF documentation, RAG-optimized JSON for vector databases, and LLM-optimized Markdown for direct use with any chat-based LLM. Perfect for code reviews, audits, RAG pipelines, and AI-assisted development.",
3
+ "version": "1.2.2",
4
+ "description": "Cross-platform CLI tool that generates PDF documentation and LLM-optimized Markdown for direct use with any chat-based LLM. Perfect for code reviews, audits, and AI-assisted development.",
5
5
  "main": "src/index.js",
6
6
  "type": "module",
7
7
  "bin": {
@@ -10,7 +10,7 @@
10
10
  "scripts": {
11
11
  "start": "node bin/codesummary.js",
12
12
  "dev": "node bin/codesummary.js",
13
- "test": "node bin/codesummary.js --help",
13
+ "test": "node --test test/**/*.test.js",
14
14
  "lint": "eslint src/ bin/",
15
15
  "format": "prettier --write .",
16
16
  "prepublishOnly": "npm run test"
@@ -18,10 +18,7 @@
18
18
  "keywords": [
19
19
  "code-documentation",
20
20
  "pdf-generator",
21
- "rag",
22
- "vector-database",
23
21
  "ai-ml",
24
- "semantic-chunking",
25
22
  "source-code",
26
23
  "cli-tool",
27
24
  "code-review",
@@ -30,7 +27,6 @@
30
27
  "cross-platform",
31
28
  "documentation-generator",
32
29
  "pdf-export",
33
- "json-export",
34
30
  "code-analysis",
35
31
  "file-scanner",
36
32
  "project-summary",
@@ -40,10 +36,7 @@
40
36
  "automatic-documentation",
41
37
  "professional-pdf",
42
38
  "terminal-compatible",
43
- "llm-integration",
44
- "token-estimation",
45
- "byte-offsets",
46
- "precision-indexing"
39
+ "llm-integration"
47
40
  ],
48
41
  "author": {
49
42
  "name": "CodeSummary Contributors",
@@ -68,21 +61,24 @@
68
61
  "README.md",
69
62
  "LICENSE",
70
63
  "CHANGELOG.md",
71
- "rag-schema.json",
72
64
  "features.md"
73
65
  ],
74
66
  "preferGlobal": true,
75
67
  "dependencies": {
76
- "pdfkit": "^0.15.0",
77
- "inquirer": "^9.2.15",
78
- "fs-extra": "^11.2.0",
68
+ "ajv": "^8.12.0",
79
69
  "chalk": "^5.3.0",
80
- "ora": "^8.0.1",
70
+ "fs-extra": "^11.2.0",
71
+ "ignore": "^5.3.2",
72
+ "inquirer": "^9.2.15",
81
73
  "js-yaml": "^4.1.0",
82
- "ajv": "^8.12.0"
74
+ "ora": "^8.0.1",
75
+ "pdfkit": "^0.15.0",
76
+ "pdfkit-table": "^0.1.99"
83
77
  },
84
78
  "devDependencies": {
79
+ "@playwright/test": "^1.59.1",
85
80
  "eslint": "^8.57.0",
81
+ "pdfjs-dist": "^5.6.205",
86
82
  "prettier": "^3.2.5"
87
83
  },
88
84
  "funding": {
@@ -0,0 +1,85 @@
1
+ export class AiProviderError extends Error {
2
+ constructor(message, options = {}) {
3
+ super(message);
4
+ this.name = 'AiProviderError';
5
+ this.code = options.code || 'ai_error';
6
+ this.status = options.status ?? null;
7
+ this.retryable = Boolean(options.retryable);
8
+ this.provider = options.provider || null;
9
+ this.scope = options.scope || 'provider';
10
+ this.details = options.details || null;
11
+ this.cause = options.cause || null;
12
+ }
13
+ }
14
+
15
+ function statusToCode(status) {
16
+ if (status === 400) return 'bad_request';
17
+ if (status === 401 || status === 403) return 'auth_error';
18
+ if (status === 404) return 'not_found';
19
+ if (status === 408) return 'timeout';
20
+ if (status === 409) return 'conflict';
21
+ if (status === 413) return 'payload_too_large';
22
+ if (status === 422) return 'unprocessable';
23
+ if (status === 425) return 'too_early';
24
+ if (status === 429) return 'rate_limited';
25
+ if (status >= 500) return 'server_error';
26
+ return 'http_error';
27
+ }
28
+
29
+ export function isRetryableStatus(status) {
30
+ return [408, 409, 425, 429, 500, 502, 503, 504].includes(status);
31
+ }
32
+
33
+ export function normalizeAiError(error, context = {}) {
34
+ if (error instanceof AiProviderError) {
35
+ return error;
36
+ }
37
+
38
+ const provider = context.provider || null;
39
+ const scope = context.scope || 'provider';
40
+ const message = error?.message || 'Unknown AI error';
41
+ const lower = message.toLowerCase();
42
+ const isTimeout = lower.includes('aborted') || lower.includes('timeout');
43
+ const isNetwork = lower.includes('fetch failed') || lower.includes('econnrefused') || lower.includes('enotfound');
44
+
45
+ if (isTimeout) {
46
+ return new AiProviderError('AI request timed out', {
47
+ code: 'timeout',
48
+ retryable: true,
49
+ provider,
50
+ scope,
51
+ cause: error
52
+ });
53
+ }
54
+
55
+ if (isNetwork) {
56
+ return new AiProviderError('AI provider network unavailable', {
57
+ code: 'network_unavailable',
58
+ retryable: true,
59
+ provider,
60
+ scope,
61
+ cause: error
62
+ });
63
+ }
64
+
65
+ return new AiProviderError(message, {
66
+ code: 'ai_error',
67
+ retryable: false,
68
+ provider,
69
+ scope,
70
+ cause: error
71
+ });
72
+ }
73
+
74
+ export function createHttpAiError(provider, status, text = '') {
75
+ const code = statusToCode(status);
76
+ const retryable = isRetryableStatus(status);
77
+ const suffix = text ? `: ${text}` : '';
78
+ return new AiProviderError(`${provider} request failed (${status})${suffix}`, {
79
+ code,
80
+ status,
81
+ retryable,
82
+ provider,
83
+ scope: 'provider'
84
+ });
85
+ }
@@ -0,0 +1,8 @@
1
+ export function isAiSemanticEnabled(aiOptions = {}) {
2
+ return Boolean(aiOptions.enabled && aiOptions.semantic);
3
+ }
4
+
5
+ export default {
6
+ isAiSemanticEnabled
7
+ };
8
+