@fasaled/sailkari 0.1.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 (72) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +229 -0
  3. package/dist/application.d.ts +47 -0
  4. package/dist/application.js +100 -0
  5. package/dist/application.js.map +1 -0
  6. package/dist/autocomplete.d.ts +2 -0
  7. package/dist/autocomplete.js +62 -0
  8. package/dist/autocomplete.js.map +1 -0
  9. package/dist/classification-store.d.ts +20 -0
  10. package/dist/classification-store.js +83 -0
  11. package/dist/classification-store.js.map +1 -0
  12. package/dist/classifier.d.ts +6 -0
  13. package/dist/classifier.js +85 -0
  14. package/dist/classifier.js.map +1 -0
  15. package/dist/command-history.d.ts +12 -0
  16. package/dist/command-history.js +49 -0
  17. package/dist/command-history.js.map +1 -0
  18. package/dist/command-parser.d.ts +32 -0
  19. package/dist/command-parser.js +90 -0
  20. package/dist/command-parser.js.map +1 -0
  21. package/dist/command-queue.d.ts +21 -0
  22. package/dist/command-queue.js +33 -0
  23. package/dist/command-queue.js.map +1 -0
  24. package/dist/config.d.ts +8 -0
  25. package/dist/config.js +26 -0
  26. package/dist/config.js.map +1 -0
  27. package/dist/context.d.ts +16 -0
  28. package/dist/context.js +12 -0
  29. package/dist/context.js.map +1 -0
  30. package/dist/evaluation-metrics.d.ts +22 -0
  31. package/dist/evaluation-metrics.js +120 -0
  32. package/dist/evaluation-metrics.js.map +1 -0
  33. package/dist/file-scanner.d.ts +2 -0
  34. package/dist/file-scanner.js +37 -0
  35. package/dist/file-scanner.js.map +1 -0
  36. package/dist/index.d.ts +2 -0
  37. package/dist/index.js +15 -0
  38. package/dist/index.js.map +1 -0
  39. package/dist/llm-engine.d.ts +42 -0
  40. package/dist/llm-engine.js +181 -0
  41. package/dist/llm-engine.js.map +1 -0
  42. package/dist/logger.d.ts +15 -0
  43. package/dist/logger.js +48 -0
  44. package/dist/logger.js.map +1 -0
  45. package/dist/mcp.d.ts +8 -0
  46. package/dist/mcp.js +97 -0
  47. package/dist/mcp.js.map +1 -0
  48. package/dist/metadata.d.ts +4 -0
  49. package/dist/metadata.js +28 -0
  50. package/dist/metadata.js.map +1 -0
  51. package/dist/operation-worker.d.ts +1 -0
  52. package/dist/operation-worker.js +86 -0
  53. package/dist/operation-worker.js.map +1 -0
  54. package/dist/parser.d.ts +3 -0
  55. package/dist/parser.js +86 -0
  56. package/dist/parser.js.map +1 -0
  57. package/dist/progress.d.ts +4 -0
  58. package/dist/progress.js +56 -0
  59. package/dist/progress.js.map +1 -0
  60. package/dist/prompt.d.ts +19 -0
  61. package/dist/prompt.js +63 -0
  62. package/dist/prompt.js.map +1 -0
  63. package/dist/tui.d.ts +2 -0
  64. package/dist/tui.js +237 -0
  65. package/dist/tui.js.map +1 -0
  66. package/dist/types.d.ts +43 -0
  67. package/dist/types.js +2 -0
  68. package/dist/types.js.map +1 -0
  69. package/dist/watcher.d.ts +9 -0
  70. package/dist/watcher.js +94 -0
  71. package/dist/watcher.js.map +1 -0
  72. package/package.json +62 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Francisco Sanchez
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,229 @@
1
+ # Sailkari
2
+
3
+ **Sailkari** (Basque for “the one who classifies”) is a local benchmark TUI for comparing
4
+ instruction-tuned GGUF models on document classification. Give it a corpus of text files and
5
+ a YAML taxonomy, then run the same workload across models to compare classification output,
6
+ latency, throughput, chunking behavior, and context reuse.
7
+
8
+ Inference runs through [`node-llama-cpp`](https://github.com/withcatai/node-llama-cpp). The
9
+ model, corpus, prompts, and results remain on the local machine. Sailkari is intended for
10
+ evaluating model behavior and performance, not as a production classification service.
11
+
12
+ ## Requirements
13
+
14
+ - Node.js 20 or newer
15
+ - An instruction-tuned GGUF model downloaded separately
16
+
17
+ The npm package includes the compatible llama.cpp native addon and libraries for the current
18
+ platform. npm picks the matching prebuilt package for your OS and architecture, so no
19
+ compiler setup is required for supported platforms.
20
+
21
+ Sailkari works anywhere the native addon runs. It stores classifications in
22
+ `.sailkari/results.json` inside the evaluated folder, making repeated runs portable across
23
+ macOS, Linux, and Windows.
24
+
25
+ ## Install and run
26
+
27
+ Run the full-screen terminal interface without a global installation:
28
+
29
+ ```bash
30
+ npx sailkari
31
+ ```
32
+
33
+ Or install globally:
34
+
35
+ ```bash
36
+ npm install --global sailkari
37
+ sailkari help
38
+ ```
39
+
40
+ Sailkari has two presentation modes in the same executable:
41
+
42
+ ```bash
43
+ # Interactive terminal UI
44
+ sailkari
45
+
46
+ # MCP server over stdio for an external agent
47
+ sailkari --mcp
48
+ ```
49
+
50
+ Both modes use the same application engine, model lifecycle, prompt configuration,
51
+ classification pipeline, context-reuse modes, result store, and benchmark metrics. MCP only
52
+ changes how those operations are exposed to the client. It provides tools for loading a model,
53
+ setting a prompt, evaluating documents, and reading or removing stored classifications.
54
+
55
+ ## Architecture
56
+
57
+ Sailkari is a single executable with two presentation layers over the same core:
58
+
59
+ ```text
60
+ sailkari → TUI (Ink)
61
+ sailkari --mcp → MCP server over stdio
62
+
63
+ shared application core
64
+ ├─ model lifecycle
65
+ ├─ system prompt management
66
+ ├─ document classification
67
+ ├─ context reuse modes
68
+ ├─ results storage
69
+ └─ evaluation metrics
70
+ ```
71
+
72
+ The TUI and MCP adapters both call the same application service; they only differ in how they
73
+ render or return the result. This keeps the model logic, evaluation logic, and stored results
74
+ consistent regardless of the client interface.
75
+
76
+ The MCP resource `sailkari://system-prompt-contract` describes the system-prompt format, and
77
+ the `generate-system-prompt` MCP prompt helps an agent produce a compatible prompt.
78
+
79
+ Install from source:
80
+
81
+ ```bash
82
+ git clone https://github.com/fasaled/local-ai-classifier.git
83
+ cd local-ai-classifier
84
+ npm install
85
+ npm run build
86
+ node dist/index.js
87
+ ```
88
+
89
+ ## Run an evaluation
90
+
91
+ Load a model and evaluate the included example corpus:
92
+
93
+ ```text
94
+ model models/Ministral-3-3B-Instruct-2512-Q4_K_M.gguf
95
+ classify examples/documents examples/labels.yaml --force
96
+ ```
97
+
98
+ Use `--force` when repeating a benchmark to run inference again for files that already have a
99
+ stored result. Without it, Sailkari displays cached labels and excludes those files from
100
+ inference metrics.
101
+
102
+ ## Metrics
103
+
104
+ For every evaluated document, Sailkari shows the predicted label, total duration, inference
105
+ duration, source size, estimated input tokens, model calls, and estimated input throughput.
106
+ The final summary groups results into:
107
+
108
+ - file outcomes: labelled, no match, and cached;
109
+ - timing: setup, total inference, and mean inference time per evaluated file;
110
+ - workload: bytes, estimated input tokens, calls, chunks, and input tokens per second.
111
+
112
+ This keeps quality signals and performance signals together while comparing models.
113
+
114
+ ## Context modes
115
+
116
+ The default mode creates a fresh inference context for every model call, maximizing isolation.
117
+ Two optional modes measure the effect of reusing allocated context memory without sharing
118
+ inference history:
119
+
120
+ - `--reuse-context-file`: reuse a context between chunks of the same file, then dispose it.
121
+ - `--reuse-context-command`: reuse one context across the full `classify` command.
122
+
123
+ In both modes, Sailkari clears context history between chunks and documents before the next
124
+ inference request.
125
+
126
+ ## Commands
127
+
128
+ Enter these commands in the lower input panel:
129
+
130
+ ```text
131
+ model <path.gguf> Load and persist the inference model
132
+ prompt <path|default> Configure the system prompt
133
+ classify <folder> <labels.yaml> Classify a folder
134
+ classify <folder> <labels.yaml> --force
135
+ classify <folder> <labels.yaml> --reuse-context-file
136
+ classify <folder> <labels.yaml> --reuse-context-command
137
+ list-tags <folder> List stored classifications
138
+ remove-tags <folder> Remove stored classifications
139
+ cancel Cancel the active operation
140
+ queue List pending commands
141
+ queue remove <position> Remove a pending command
142
+ queue move <from> <to> Reorder pending commands
143
+ queue clear Remove all pending commands
144
+ help Show command help
145
+ quit Exit Sailkari
146
+ ```
147
+
148
+ The upper panel displays benchmark tables, summaries, warnings, and errors. Model and system
149
+ prompt configuration is saved in `~/.config/sailkari/config.json`. Use Tab for command and
150
+ path completion. Operations run in a worker thread, so the TUI remains available while a
151
+ model loads or files are classified. New commands are queued and can be inspected, reordered,
152
+ or removed with `queue`; use `cancel` to stop the active operation.
153
+
154
+ Use Up and Down to browse the persistent command history; it is stored with the rest of the
155
+ configuration in `~/.config/sailkari/config.json`. Use Shift+Up and Shift+Down to scroll the
156
+ activity panel; Page Up and Page Down are also supported.
157
+
158
+ ## Labels
159
+
160
+ Labels are a YAML map of names to natural-language descriptions:
161
+
162
+ ```yaml
163
+ family: "Family documents including personal letters and school documents"
164
+ banking: "Banking and financial documents including statements and tax returns"
165
+ technology: "Technology documents including architecture and security guides"
166
+ ```
167
+
168
+ See `examples/labels.yaml` for a larger example.
169
+
170
+ ## System prompts
171
+
172
+ Use `prompt default` to use the built-in classifier prompt. To use a custom prompt, run
173
+ `prompt examples/system-prompt.txt`.
174
+
175
+ The parser accepts:
176
+
177
+ - a bare label name, such as `banking`;
178
+ - JSON such as `{"labels":["banking"]}` or `[{"label":"banking"}]`.
179
+
180
+ The system prompt should request exactly one label and no explanation. Unknown or invalid
181
+ output is treated as no match.
182
+
183
+ ## Local execution
184
+
185
+ The model remains loaded for the TUI session and is released when the application exits.
186
+ Contexts follow the selected benchmark mode and are always disposed after their scope ends.
187
+ No model or document data is sent over the network; npm may access the network during
188
+ installation to fetch dependencies and the platform-specific native prebuilt.
189
+
190
+ ## Packaging
191
+
192
+ The published Sailkari package contains the compiled JavaScript CLI. `node-llama-cpp` is a
193
+ runtime dependency and provides the platform packages for each supported environment, such as
194
+ `@node-llama-cpp/mac-arm64-metal`, `@node-llama-cpp/linux-x64`, and
195
+ `@node-llama-cpp/win-x64`.
196
+
197
+ GGUF files are excluded from the package and are provided separately through the `model`
198
+ command.
199
+
200
+ ## Development
201
+
202
+ ```bash
203
+ npm install
204
+ npm run typecheck
205
+ npm test
206
+ npm run build
207
+ ```
208
+
209
+ Run model-dependent tests explicitly:
210
+
211
+ ```bash
212
+ CLASSIFIER_MODEL=/absolute/path/to/model.gguf npm run test:e2e
213
+ ```
214
+
215
+ Without `CLASSIFIER_MODEL` (and without a GGUF directly in `models/`), the e2e suite is
216
+ skipped.
217
+
218
+ ## Contributing
219
+
220
+ Development happens in the [Sailkari repository](https://github.com/fasaled/local-ai-classifier).
221
+ Open an [issue](https://github.com/fasaled/local-ai-classifier/issues) to report a bug, request
222
+ a feature, or discuss a proposed change. Contributions are welcome through pull requests.
223
+
224
+ ## Scope
225
+
226
+ - Plain text formats only: `.txt`, `.md`, `.csv`, `.json`, `.yaml`, `.yml`, `.xml`, `.log`,
227
+ `.conf`, `.config`, `.ini`, `.toml`, and `.properties`.
228
+ - Results are stored per evaluated folder in `.sailkari/results.json`.
229
+ - Models are not downloaded automatically.
@@ -0,0 +1,47 @@
1
+ import { type NativeLogCallback } from "./llm-engine.js";
2
+ import { summarizeEvaluation } from "./evaluation-metrics.js";
3
+ import type { ProcessingResult } from "./types.js";
4
+ export interface EvaluationOptions {
5
+ folder: string;
6
+ labels: string;
7
+ force: boolean;
8
+ contextReuse: "none" | "file" | "command";
9
+ signal?: AbortSignal;
10
+ onProgress?: (filePath: string, current: number, total: number, message: string) => void;
11
+ onResult?: (result: ProcessingResult) => void;
12
+ onStart?: (folder: string, fileCount: number) => void;
13
+ }
14
+ export interface EvaluationResult {
15
+ folder: string;
16
+ results: ProcessingResult[];
17
+ summary: ReturnType<typeof summarizeEvaluation>;
18
+ }
19
+ export declare class SailkariApplication {
20
+ private readonly nativeLogger?;
21
+ private engine;
22
+ private modelPath;
23
+ private systemPrompt;
24
+ constructor(nativeLogger?: NativeLogCallback | undefined);
25
+ get loadedModelPath(): string | undefined;
26
+ get configuredSystemPrompt(): string;
27
+ loadModel(modelPath: string): Promise<string>;
28
+ setSystemPrompt(prompt: string): Promise<{
29
+ warnings: string[];
30
+ }>;
31
+ loadSystemPrompt(path: string | null): Promise<{
32
+ path: string | null;
33
+ prompt: string;
34
+ warnings: string[];
35
+ }>;
36
+ evaluate(options: EvaluationOptions): Promise<EvaluationResult>;
37
+ listClassifications(folderPath: string): {
38
+ filePath: string;
39
+ labels: string[];
40
+ }[];
41
+ removeClassifications(folderPath: string): {
42
+ filePath: string;
43
+ labels: string[];
44
+ }[];
45
+ dispose(): Promise<void>;
46
+ static fileName(filePath: string): string;
47
+ }
@@ -0,0 +1,100 @@
1
+ import { basename, resolve } from "node:path";
2
+ import { ClassificationStore } from "./classification-store.js";
3
+ import { loadLabels, processFile } from "./classifier.js";
4
+ import { scanFolder } from "./file-scanner.js";
5
+ import { createLLMEngine } from "./llm-engine.js";
6
+ import { DEFAULT_SYSTEM_PROMPT, inspectSystemPrompt, loadSystemPrompt } from "./prompt.js";
7
+ import { summarizeEvaluation } from "./evaluation-metrics.js";
8
+ export class SailkariApplication {
9
+ nativeLogger;
10
+ engine;
11
+ modelPath;
12
+ systemPrompt = DEFAULT_SYSTEM_PROMPT;
13
+ constructor(nativeLogger) {
14
+ this.nativeLogger = nativeLogger;
15
+ this.engine = createLLMEngine(nativeLogger ?? (() => { }));
16
+ }
17
+ get loadedModelPath() {
18
+ return this.modelPath;
19
+ }
20
+ get configuredSystemPrompt() {
21
+ return this.systemPrompt;
22
+ }
23
+ async loadModel(modelPath) {
24
+ const absolutePath = resolve(modelPath);
25
+ await this.engine.dispose();
26
+ this.engine = createLLMEngine(this.nativeLogger ?? (() => { }));
27
+ await this.engine.loadModel(absolutePath);
28
+ this.modelPath = absolutePath;
29
+ return absolutePath;
30
+ }
31
+ async setSystemPrompt(prompt) {
32
+ this.systemPrompt = prompt.trim();
33
+ return { warnings: inspectSystemPrompt(this.systemPrompt) };
34
+ }
35
+ async loadSystemPrompt(path) {
36
+ if (path === null) {
37
+ this.systemPrompt = DEFAULT_SYSTEM_PROMPT;
38
+ return { path: null, prompt: this.systemPrompt, warnings: [] };
39
+ }
40
+ const absolutePath = resolve(path);
41
+ const prompt = await loadSystemPrompt(absolutePath);
42
+ this.systemPrompt = prompt;
43
+ return { path: absolutePath, prompt, warnings: inspectSystemPrompt(prompt) };
44
+ }
45
+ async evaluate(options) {
46
+ const startedAt = performance.now();
47
+ const labels = await loadLabels(resolve(options.labels));
48
+ const folder = resolve(options.folder);
49
+ const files = scanFolder(folder);
50
+ const preparationMs = performance.now() - startedAt;
51
+ options.onStart?.(folder, files.length);
52
+ const store = new ClassificationStore(folder);
53
+ const results = [];
54
+ const commandContext = options.contextReuse === "command" ? await this.engine.createContext() : undefined;
55
+ try {
56
+ for (const filePath of files) {
57
+ options.signal?.throwIfAborted();
58
+ if (commandContext && results.length > 0)
59
+ await commandContext.clearHistory();
60
+ const fileContext = options.contextReuse === "file"
61
+ ? await this.engine.createContext()
62
+ : commandContext;
63
+ try {
64
+ const result = await processFile(filePath, labels, options.force, this.engine, store, this.systemPrompt, options.signal, options.onProgress ? (current, total, message) => options.onProgress(filePath, current, total, message) : undefined, fileContext);
65
+ results.push(result);
66
+ options.onResult?.(result);
67
+ }
68
+ finally {
69
+ if (options.contextReuse === "file")
70
+ await fileContext?.dispose();
71
+ }
72
+ }
73
+ }
74
+ finally {
75
+ await commandContext?.dispose();
76
+ }
77
+ return {
78
+ folder,
79
+ results,
80
+ summary: summarizeEvaluation(results, performance.now() - startedAt, preparationMs),
81
+ };
82
+ }
83
+ listClassifications(folderPath) {
84
+ return new ClassificationStore(resolve(folderPath)).entries();
85
+ }
86
+ removeClassifications(folderPath) {
87
+ const store = new ClassificationStore(resolve(folderPath));
88
+ const entries = store.entries();
89
+ for (const entry of entries)
90
+ store.remove(entry.filePath);
91
+ return entries;
92
+ }
93
+ async dispose() {
94
+ await this.engine.dispose();
95
+ }
96
+ static fileName(filePath) {
97
+ return basename(filePath);
98
+ }
99
+ }
100
+ //# sourceMappingURL=application.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"application.js","sourceRoot":"","sources":["../src/application.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ export declare function completeInput(input: string): string[];
2
+ export declare function applyCompletion(input: string, completion: string): string;
@@ -0,0 +1,62 @@
1
+ import { readdirSync, statSync } from "node:fs";
2
+ import { basename, dirname, extname, join, resolve } from "node:path";
3
+ const COMMANDS = ["model", "prompt", "classify", "list-tags", "remove-tags", "help", "quit"];
4
+ function currentToken(input) {
5
+ const match = input.match(/(?:^|\s)([^\s]*)$/);
6
+ if (!match)
7
+ return { before: input, token: "" };
8
+ return { before: input.slice(0, input.length - match[1].length), token: match[1] };
9
+ }
10
+ function pathCandidates(token, predicate, defaultBase = ".") {
11
+ const hasTrailingSeparator = token.endsWith("/");
12
+ const base = !token ? defaultBase : hasTrailingSeparator ? token : token.includes("/") ? dirname(token) : defaultBase;
13
+ const prefix = token && !hasTrailingSeparator ? basename(token) : "";
14
+ const directory = resolve(base === "." && token.startsWith("/") ? "/" : base);
15
+ try {
16
+ return readdirSync(directory, { withFileTypes: true })
17
+ .filter((entry) => entry.name.startsWith(prefix))
18
+ .map((entry) => join(base, entry.name))
19
+ .filter((candidate) => {
20
+ try {
21
+ return statSync(resolve(candidate)).isDirectory() || predicate(resolve(candidate));
22
+ }
23
+ catch {
24
+ return false;
25
+ }
26
+ })
27
+ .map((candidate) => candidate + (statSync(resolve(candidate)).isDirectory() ? "/" : ""));
28
+ }
29
+ catch {
30
+ return [];
31
+ }
32
+ }
33
+ export function completeInput(input) {
34
+ const { before, token } = currentToken(input);
35
+ if (!before && !token.includes("/")) {
36
+ return COMMANDS.filter((command) => command.startsWith(token));
37
+ }
38
+ const command = input.trim().split(/\s+/)[0];
39
+ const tokens = input.trim().split(/\s+/);
40
+ const argumentIndex = tokens.length - 1 + (input.endsWith(" ") ? 1 : 0);
41
+ if (command === "model" && argumentIndex === 1) {
42
+ return pathCandidates(token, (path) => extname(path).toLowerCase() === ".gguf");
43
+ }
44
+ if (command === "prompt" && argumentIndex === 1) {
45
+ return pathCandidates(token, (path) => statSync(path).isFile());
46
+ }
47
+ if (command === "classify" && argumentIndex === 1) {
48
+ return pathCandidates(token, (path) => statSync(path).isDirectory());
49
+ }
50
+ if (command === "classify" && argumentIndex === 2) {
51
+ return pathCandidates(token, (path) => [".yaml", ".yml"].includes(extname(path).toLowerCase()));
52
+ }
53
+ if ((command === "list-tags" || command === "remove-tags") && argumentIndex === 1) {
54
+ return pathCandidates(token, (path) => statSync(path).isDirectory());
55
+ }
56
+ return [];
57
+ }
58
+ export function applyCompletion(input, completion) {
59
+ const { before } = currentToken(input);
60
+ return before + completion;
61
+ }
62
+ //# sourceMappingURL=autocomplete.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"autocomplete.js","sourceRoot":"","sources":["../src/autocomplete.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ export declare class ClassificationStore {
2
+ private readonly root;
3
+ private readonly filePath;
4
+ private results;
5
+ private loaded;
6
+ constructor(folderPath: string);
7
+ has(filePath: string): boolean;
8
+ getLabels(filePath: string): string[];
9
+ getTags(filePath: string): string[];
10
+ setLabels(filePath: string, labels: string[]): void;
11
+ remove(filePath: string): void;
12
+ entries(): {
13
+ filePath: string;
14
+ labels: string[];
15
+ }[];
16
+ private keyFor;
17
+ private load;
18
+ private save;
19
+ }
20
+ export declare function isClassificationStorePath(filePath: string): boolean;
@@ -0,0 +1,83 @@
1
+ import { mkdirSync, readFileSync, renameSync, writeFileSync } from "node:fs";
2
+ import { dirname, join, relative, resolve, sep } from "node:path";
3
+ const STORE_DIRECTORY = ".sailkari";
4
+ const STORE_FILE = "results.json";
5
+ const STORE_VERSION = 1;
6
+ function emptyResults() {
7
+ return { version: STORE_VERSION, files: {} };
8
+ }
9
+ export class ClassificationStore {
10
+ root;
11
+ filePath;
12
+ results;
13
+ loaded = false;
14
+ constructor(folderPath) {
15
+ this.root = resolve(folderPath);
16
+ this.filePath = join(this.root, STORE_DIRECTORY, STORE_FILE);
17
+ this.results = emptyResults();
18
+ }
19
+ has(filePath) {
20
+ this.load();
21
+ return this.keyFor(filePath) in this.results.files;
22
+ }
23
+ getLabels(filePath) {
24
+ this.load();
25
+ return [...(this.results.files[this.keyFor(filePath)]?.labels ?? [])];
26
+ }
27
+ getTags(filePath) {
28
+ const labels = this.getLabels(filePath);
29
+ if (this.has(filePath)) {
30
+ return [...labels, "ai-classified"];
31
+ }
32
+ return labels;
33
+ }
34
+ setLabels(filePath, labels) {
35
+ this.load();
36
+ this.results.files[this.keyFor(filePath)] = { labels: [...labels] };
37
+ this.save();
38
+ }
39
+ remove(filePath) {
40
+ this.load();
41
+ delete this.results.files[this.keyFor(filePath)];
42
+ this.save();
43
+ }
44
+ entries() {
45
+ this.load();
46
+ return Object.entries(this.results.files).map(([key, value]) => ({
47
+ filePath: join(this.root, key.split("/").join(sep)),
48
+ labels: [...value.labels],
49
+ }));
50
+ }
51
+ keyFor(filePath) {
52
+ return relative(this.root, resolve(filePath)).split(sep).join("/");
53
+ }
54
+ load() {
55
+ if (this.loaded)
56
+ return;
57
+ this.loaded = true;
58
+ try {
59
+ const parsed = JSON.parse(readFileSync(this.filePath, "utf8"));
60
+ if (parsed.version === STORE_VERSION && parsed.files && typeof parsed.files === "object") {
61
+ this.results = {
62
+ version: STORE_VERSION,
63
+ files: Object.fromEntries(Object.entries(parsed.files).filter(([, value]) => {
64
+ return value && Array.isArray(value.labels);
65
+ })),
66
+ };
67
+ }
68
+ }
69
+ catch {
70
+ this.results = emptyResults();
71
+ }
72
+ }
73
+ save() {
74
+ mkdirSync(dirname(this.filePath), { recursive: true });
75
+ const temporaryPath = `${this.filePath}.tmp-${process.pid}`;
76
+ writeFileSync(temporaryPath, JSON.stringify(this.results, null, 2) + "\n", "utf8");
77
+ renameSync(temporaryPath, this.filePath);
78
+ }
79
+ }
80
+ export function isClassificationStorePath(filePath) {
81
+ return filePath.split(sep).includes(STORE_DIRECTORY);
82
+ }
83
+ //# sourceMappingURL=classification-store.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classification-store.js","sourceRoot":"","sources":["../src/classification-store.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,6 @@
1
+ import type { EngineContext, LLMEngine } from "./llm-engine.js";
2
+ import type { Label, ProcessingResult } from "./types.js";
3
+ import type { ClassificationStore } from "./classification-store.js";
4
+ import type { ProgressCallback } from "./llm-engine.js";
5
+ export declare function processFile(filePath: string, labels: Label[], force: boolean, engine: LLMEngine, store: ClassificationStore, systemPrompt?: string, signal?: AbortSignal, onProgress?: ProgressCallback, context?: EngineContext): Promise<ProcessingResult>;
6
+ export declare function loadLabels(labelsPath: string): Promise<Label[]>;