@kb-labs/mind-core 1.5.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.
- package/README.md +187 -0
- package/dist/index.d.ts +402 -0
- package/dist/index.js +574 -0
- package/dist/index.js.map +1 -0
- package/package.json +49 -0
package/README.md
ADDED
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
# @kb-labs/mind-core
|
|
2
|
+
|
|
3
|
+
Core contracts, errors, and utilities for KB Labs Mind.
|
|
4
|
+
|
|
5
|
+
## Vision & Purpose
|
|
6
|
+
|
|
7
|
+
**@kb-labs/mind-core** provides core contracts, errors, and utilities for KB Labs Mind. It includes error handling, token utilities, hash utilities, path utilities, and default configurations.
|
|
8
|
+
|
|
9
|
+
### Core Goals
|
|
10
|
+
|
|
11
|
+
- **Error Handling**: Unified error handling for Mind
|
|
12
|
+
- **Token Utilities**: Token estimation and truncation utilities
|
|
13
|
+
- **Hash Utilities**: Hashing utilities
|
|
14
|
+
- **Path Utilities**: Path manipulation utilities
|
|
15
|
+
- **Defaults**: Default configurations
|
|
16
|
+
|
|
17
|
+
## Package Status
|
|
18
|
+
|
|
19
|
+
- **Version**: 0.1.0
|
|
20
|
+
- **Stage**: Stable
|
|
21
|
+
- **Status**: Production Ready ✅
|
|
22
|
+
|
|
23
|
+
## Architecture
|
|
24
|
+
|
|
25
|
+
### High-Level Overview
|
|
26
|
+
|
|
27
|
+
```
|
|
28
|
+
Mind Core
|
|
29
|
+
│
|
|
30
|
+
├──► Error Handling
|
|
31
|
+
├──► Token Utilities
|
|
32
|
+
├──► Hash Utilities
|
|
33
|
+
├──► Path Utilities
|
|
34
|
+
└──► Defaults
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Key Components
|
|
38
|
+
|
|
39
|
+
1. **Error** (`error/`): Error handling
|
|
40
|
+
2. **Utils** (`utils/`): Utilities (token, hash, paths)
|
|
41
|
+
3. **Defaults** (`defaults.ts`): Default configurations
|
|
42
|
+
|
|
43
|
+
## ✨ Features
|
|
44
|
+
|
|
45
|
+
- **Error Handling**: Unified error handling for Mind
|
|
46
|
+
- **Token Utilities**: Token estimation and truncation utilities
|
|
47
|
+
- **Hash Utilities**: Hashing utilities
|
|
48
|
+
- **Path Utilities**: Path manipulation utilities
|
|
49
|
+
- **Defaults**: Default configurations
|
|
50
|
+
|
|
51
|
+
## 📦 API Reference
|
|
52
|
+
|
|
53
|
+
### Main Exports
|
|
54
|
+
|
|
55
|
+
#### Error Handling
|
|
56
|
+
|
|
57
|
+
- `MindError`: Mind error class
|
|
58
|
+
- `createMindError`: Create Mind error
|
|
59
|
+
|
|
60
|
+
#### Token Utilities
|
|
61
|
+
|
|
62
|
+
- `estimateTokens`: Estimate tokens in text
|
|
63
|
+
- `truncateTokens`: Truncate text by tokens
|
|
64
|
+
|
|
65
|
+
#### Hash Utilities
|
|
66
|
+
|
|
67
|
+
- `hashString`: Hash string
|
|
68
|
+
- `hashFile`: Hash file
|
|
69
|
+
|
|
70
|
+
#### Path Utilities
|
|
71
|
+
|
|
72
|
+
- `normalizePath`: Normalize path
|
|
73
|
+
- `resolvePath`: Resolve path
|
|
74
|
+
|
|
75
|
+
#### Defaults
|
|
76
|
+
|
|
77
|
+
- `DEFAULT_CONFIG`: Default configuration
|
|
78
|
+
|
|
79
|
+
## 🔧 Configuration
|
|
80
|
+
|
|
81
|
+
### Configuration Options
|
|
82
|
+
|
|
83
|
+
All configuration via function parameters.
|
|
84
|
+
|
|
85
|
+
## 🔗 Dependencies
|
|
86
|
+
|
|
87
|
+
### Runtime Dependencies
|
|
88
|
+
|
|
89
|
+
- `@kb-labs/mind-types` (`link:../mind-types`): Mind types
|
|
90
|
+
|
|
91
|
+
### Development Dependencies
|
|
92
|
+
|
|
93
|
+
- `@kb-labs/devkit` (`link:../../../kb-labs-devkit`): DevKit presets
|
|
94
|
+
- `@types/node` (`^24.7.0`): Node.js types
|
|
95
|
+
- `tsup` (`^8.5.0`): TypeScript bundler
|
|
96
|
+
- `typescript` (`^5.6.3`): TypeScript compiler
|
|
97
|
+
- `vitest` (`^3.2.4`): Test runner
|
|
98
|
+
|
|
99
|
+
## 🧪 Testing
|
|
100
|
+
|
|
101
|
+
### Test Structure
|
|
102
|
+
|
|
103
|
+
No tests currently.
|
|
104
|
+
|
|
105
|
+
### Test Coverage
|
|
106
|
+
|
|
107
|
+
- **Current Coverage**: ~50%
|
|
108
|
+
- **Target Coverage**: 90%
|
|
109
|
+
|
|
110
|
+
## 📈 Performance
|
|
111
|
+
|
|
112
|
+
### Performance Characteristics
|
|
113
|
+
|
|
114
|
+
- **Time Complexity**: O(1) for utilities, O(n) for token estimation
|
|
115
|
+
- **Space Complexity**: O(1)
|
|
116
|
+
- **Bottlenecks**: Token estimation for large texts
|
|
117
|
+
|
|
118
|
+
## 🔒 Security
|
|
119
|
+
|
|
120
|
+
### Security Considerations
|
|
121
|
+
|
|
122
|
+
- **Hash Utilities**: Secure hashing utilities
|
|
123
|
+
- **Path Validation**: Path validation for file operations
|
|
124
|
+
|
|
125
|
+
### Known Vulnerabilities
|
|
126
|
+
|
|
127
|
+
- None
|
|
128
|
+
|
|
129
|
+
## 🐛 Known Issues & Limitations
|
|
130
|
+
|
|
131
|
+
### Known Issues
|
|
132
|
+
|
|
133
|
+
- None currently
|
|
134
|
+
|
|
135
|
+
### Limitations
|
|
136
|
+
|
|
137
|
+
- **Token Estimation**: Basic token estimation
|
|
138
|
+
|
|
139
|
+
### Future Improvements
|
|
140
|
+
|
|
141
|
+
- **Better Token Estimation**: More accurate token estimation
|
|
142
|
+
|
|
143
|
+
## 🔄 Migration & Breaking Changes
|
|
144
|
+
|
|
145
|
+
### Migration from Previous Versions
|
|
146
|
+
|
|
147
|
+
No breaking changes in current version (0.1.0).
|
|
148
|
+
|
|
149
|
+
### Breaking Changes in Future Versions
|
|
150
|
+
|
|
151
|
+
- None planned
|
|
152
|
+
|
|
153
|
+
## 📚 Examples
|
|
154
|
+
|
|
155
|
+
### Example 1: Use Error Handling
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
import { createMindError } from '@kb-labs/mind-core';
|
|
159
|
+
|
|
160
|
+
const error = createMindError('MIND_PARSE_ERROR', 'Failed to parse file');
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
### Example 2: Use Token Utilities
|
|
164
|
+
|
|
165
|
+
```typescript
|
|
166
|
+
import { estimateTokens, truncateTokens } from '@kb-labs/mind-core';
|
|
167
|
+
|
|
168
|
+
const tokens = estimateTokens('Hello world');
|
|
169
|
+
const truncated = truncateTokens('Long text...', 100);
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
### Example 3: Use Hash Utilities
|
|
173
|
+
|
|
174
|
+
```typescript
|
|
175
|
+
import { hashString } from '@kb-labs/mind-core';
|
|
176
|
+
|
|
177
|
+
const hash = hashString('Hello world');
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
## 🤝 Contributing
|
|
181
|
+
|
|
182
|
+
See [CONTRIBUTING.md](../../CONTRIBUTING.md) for development guidelines.
|
|
183
|
+
|
|
184
|
+
## 📄 License
|
|
185
|
+
|
|
186
|
+
MIT © KB Labs
|
|
187
|
+
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,402 @@
|
|
|
1
|
+
export * from '@kb-labs/mind-types';
|
|
2
|
+
import { IStorage } from '@kb-labs/sdk';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* @module @kb-labs/mind-core/error
|
|
6
|
+
* Standardized error class for KB Labs Mind
|
|
7
|
+
*/
|
|
8
|
+
declare class MindError extends Error {
|
|
9
|
+
code: string;
|
|
10
|
+
hint?: string | undefined;
|
|
11
|
+
meta?: any | undefined;
|
|
12
|
+
constructor(code: string, message: string, hint?: string | undefined, meta?: any | undefined);
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* Maps MindError codes to CLI exit codes
|
|
16
|
+
*/
|
|
17
|
+
declare function getExitCode(err: MindError): number;
|
|
18
|
+
/**
|
|
19
|
+
* Error codes with their standard hints
|
|
20
|
+
*/
|
|
21
|
+
declare const ERROR_HINTS: {
|
|
22
|
+
readonly MIND_NO_GIT: "Initialize git repository or run from a git repository";
|
|
23
|
+
readonly MIND_FS_TIMEOUT: "File system operation timed out - try increasing time budget";
|
|
24
|
+
readonly MIND_PARSE_ERROR: "Failed to parse file - check syntax and try again";
|
|
25
|
+
readonly MIND_PACK_BUDGET_EXCEEDED: "Context pack exceeds token budget - reduce content or increase budget";
|
|
26
|
+
readonly MIND_FORBIDDEN: "Operation not permitted - check file permissions";
|
|
27
|
+
readonly MIND_TIME_BUDGET: "Time budget exceeded - operation completed partially";
|
|
28
|
+
readonly MIND_BAD_FLAGS: "Invalid command line flags - check values and try again";
|
|
29
|
+
readonly MIND_INVALID_FLAG: "Invalid flag value - check format and try again";
|
|
30
|
+
readonly MIND_BUNDLE_TIMEOUT: "Bundle operation timed out - skipped bundle information";
|
|
31
|
+
readonly MIND_FEED_ERROR: "Mind feed operation failed - check logs for details";
|
|
32
|
+
readonly MIND_INIT_ERROR: "Mind initialization failed - check permissions and try again";
|
|
33
|
+
readonly MIND_UPDATE_ERROR: "Mind update operation failed - check logs for details";
|
|
34
|
+
readonly MIND_PACK_ERROR: "Mind pack operation failed - check logs for details";
|
|
35
|
+
readonly MIND_GIT_ERROR: "Git operation failed - check git repository status";
|
|
36
|
+
readonly MIND_INDEX_NOT_FOUND: "Mind indexes not found - run \"kb mind init\" first";
|
|
37
|
+
readonly MIND_INVALID_PATH: "Invalid file or directory path - check path exists and is accessible";
|
|
38
|
+
readonly MIND_DEPENDENCY_ERROR: "Dependency resolution failed - check package configuration";
|
|
39
|
+
readonly MIND_BUILD_ERROR: "Build operation failed - check configuration and try again";
|
|
40
|
+
};
|
|
41
|
+
type ErrorCode = keyof typeof ERROR_HINTS;
|
|
42
|
+
/**
|
|
43
|
+
* Create a MindError with standardized code and hint
|
|
44
|
+
*/
|
|
45
|
+
declare function createMindError(code: ErrorCode, message: string, meta?: any): MindError;
|
|
46
|
+
/**
|
|
47
|
+
* Create a MindError from a generic error
|
|
48
|
+
*/
|
|
49
|
+
declare function wrapError(error: unknown, code?: ErrorCode): MindError;
|
|
50
|
+
/**
|
|
51
|
+
* Check if an error is a MindError
|
|
52
|
+
*/
|
|
53
|
+
declare function isMindError(error: unknown): error is MindError;
|
|
54
|
+
|
|
55
|
+
/**
|
|
56
|
+
* Pack and context types for KB Labs Mind
|
|
57
|
+
*/
|
|
58
|
+
|
|
59
|
+
type ContextSection = "intent_summary" | "product_overview" | "project_meta" | "api_signatures" | "recent_diffs" | "docs_overview" | "impl_snippets" | "configs_profiles";
|
|
60
|
+
interface ContextBudget {
|
|
61
|
+
totalTokens: number;
|
|
62
|
+
caps: Partial<Record<ContextSection, number>>;
|
|
63
|
+
truncation: "start" | "middle" | "end";
|
|
64
|
+
}
|
|
65
|
+
type ContextSlice = "overview" | "api" | "diffs" | "snippets" | "configs" | "meta" | "docs";
|
|
66
|
+
interface ContextPreset {
|
|
67
|
+
name: string;
|
|
68
|
+
weight: Partial<Record<ContextSlice, number>>;
|
|
69
|
+
}
|
|
70
|
+
interface ITokenEstimator {
|
|
71
|
+
estimate(text: string): number;
|
|
72
|
+
truncate(text: string, maxTokens: number, mode: "start" | "middle" | "end"): string;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* Token estimation utilities for KB Labs Mind
|
|
77
|
+
*/
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Default whitespace-aware token estimator
|
|
81
|
+
* Algorithm: ~3.8-4.2 chars/token based on whitespace and code patterns
|
|
82
|
+
*/
|
|
83
|
+
declare class DefaultTokenEstimator implements ITokenEstimator {
|
|
84
|
+
private readonly charsPerToken;
|
|
85
|
+
private readonly codeBonus;
|
|
86
|
+
private readonly punctuationWeight;
|
|
87
|
+
estimate(text: string): number;
|
|
88
|
+
truncate(text: string, maxTokens: number, mode: "start" | "middle" | "end"): string;
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Default token estimator instance
|
|
92
|
+
*/
|
|
93
|
+
declare const defaultTokenEstimator: DefaultTokenEstimator;
|
|
94
|
+
/**
|
|
95
|
+
* Estimate tokens using default strategy
|
|
96
|
+
*/
|
|
97
|
+
declare function estimateTokens(text: string): number;
|
|
98
|
+
/**
|
|
99
|
+
* Truncate text to token limit using default strategy
|
|
100
|
+
*/
|
|
101
|
+
declare function truncateToTokens(text: string, maxTokens: number, mode?: "start" | "middle" | "end"): string;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* Hashing utilities for KB Labs Mind
|
|
105
|
+
*/
|
|
106
|
+
/**
|
|
107
|
+
* Compute SHA256 hash for string content
|
|
108
|
+
*/
|
|
109
|
+
declare function sha256(content: string): string;
|
|
110
|
+
/**
|
|
111
|
+
* Compute SHA256 hash for Buffer content
|
|
112
|
+
*/
|
|
113
|
+
declare function sha256Buffer(buffer: Buffer): string;
|
|
114
|
+
/**
|
|
115
|
+
* Compute SHA256 hash for file content (streaming for large files)
|
|
116
|
+
*/
|
|
117
|
+
declare function sha256File(filePath: string): Promise<string>;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Path utilities for KB Labs Mind
|
|
121
|
+
*/
|
|
122
|
+
/**
|
|
123
|
+
* Convert path to POSIX format (forward slashes)
|
|
124
|
+
*/
|
|
125
|
+
declare function toPosix(filePath: string): string;
|
|
126
|
+
/**
|
|
127
|
+
* Convert POSIX path back to platform-specific format
|
|
128
|
+
*/
|
|
129
|
+
declare function fromPosix(posixPath: string): string;
|
|
130
|
+
/**
|
|
131
|
+
* Find workspace root by looking for git repository or monorepo indicators
|
|
132
|
+
* Searches up the directory tree from cwd
|
|
133
|
+
*/
|
|
134
|
+
declare function findWorkspaceRoot(cwd: string): Promise<string>;
|
|
135
|
+
/**
|
|
136
|
+
* Make path relative to workspace root
|
|
137
|
+
*/
|
|
138
|
+
declare function makeRelativeToRoot(absolutePath: string, root: string): string;
|
|
139
|
+
/**
|
|
140
|
+
* Check if path should be ignored based on common patterns
|
|
141
|
+
*/
|
|
142
|
+
declare function shouldIgnorePath(filePath: string): boolean;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Mathematical utility functions for KB Labs Mind
|
|
146
|
+
*/
|
|
147
|
+
/**
|
|
148
|
+
* Calculate cosine similarity between two vectors
|
|
149
|
+
*
|
|
150
|
+
* Cosine similarity measures the cosine of the angle between two vectors,
|
|
151
|
+
* producing a value between -1 and 1, where:
|
|
152
|
+
* - 1 means vectors point in the same direction (identical)
|
|
153
|
+
* - 0 means vectors are orthogonal (no similarity)
|
|
154
|
+
* - -1 means vectors point in opposite directions
|
|
155
|
+
*
|
|
156
|
+
* @param a - First vector (array of numbers)
|
|
157
|
+
* @param b - Second vector (array of numbers)
|
|
158
|
+
* @returns Similarity score [0-1], or 0 if vectors have different lengths or zero magnitudes
|
|
159
|
+
*
|
|
160
|
+
* @example
|
|
161
|
+
* ```typescript
|
|
162
|
+
* const similarity = cosineSimilarity([1, 2, 3], [4, 5, 6]);
|
|
163
|
+
* console.log(similarity); // ~0.974
|
|
164
|
+
* ```
|
|
165
|
+
*/
|
|
166
|
+
declare function cosineSimilarity(a: number[], b: number[]): number;
|
|
167
|
+
/**
|
|
168
|
+
* Calculate dot product of two vectors
|
|
169
|
+
*
|
|
170
|
+
* @param a - First vector
|
|
171
|
+
* @param b - Second vector
|
|
172
|
+
* @returns Dot product, or 0 if vectors have different lengths
|
|
173
|
+
*
|
|
174
|
+
* @example
|
|
175
|
+
* ```typescript
|
|
176
|
+
* const dot = dotProduct([1, 2, 3], [4, 5, 6]);
|
|
177
|
+
* console.log(dot); // 32
|
|
178
|
+
* ```
|
|
179
|
+
*/
|
|
180
|
+
declare function dotProduct(a: number[], b: number[]): number;
|
|
181
|
+
/**
|
|
182
|
+
* Calculate magnitude (L2 norm) of a vector
|
|
183
|
+
*
|
|
184
|
+
* @param vec - Input vector
|
|
185
|
+
* @returns Magnitude (Euclidean length)
|
|
186
|
+
*
|
|
187
|
+
* @example
|
|
188
|
+
* ```typescript
|
|
189
|
+
* const mag = magnitude([3, 4]);
|
|
190
|
+
* console.log(mag); // 5
|
|
191
|
+
* ```
|
|
192
|
+
*/
|
|
193
|
+
declare function magnitude(vec: number[]): number;
|
|
194
|
+
/**
|
|
195
|
+
* Normalize a vector to unit length
|
|
196
|
+
*
|
|
197
|
+
* @param vec - Input vector
|
|
198
|
+
* @returns Normalized vector (magnitude = 1), or original if magnitude is 0
|
|
199
|
+
*
|
|
200
|
+
* @example
|
|
201
|
+
* ```typescript
|
|
202
|
+
* const normalized = normalize([3, 4]);
|
|
203
|
+
* console.log(normalized); // [0.6, 0.8]
|
|
204
|
+
* ```
|
|
205
|
+
*/
|
|
206
|
+
declare function normalize(vec: number[]): number[];
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Base class for file-based stores with rotation support
|
|
210
|
+
*
|
|
211
|
+
* Provides JSONL file rotation, segmentation by date, and automatic cleanup.
|
|
212
|
+
* Used by history stores, feedback stores, and other persistent logging mechanisms.
|
|
213
|
+
*/
|
|
214
|
+
|
|
215
|
+
interface FileRotationOptions {
|
|
216
|
+
/**
|
|
217
|
+
* Base directory path for storing files
|
|
218
|
+
* @default '.kb/mind/store/'
|
|
219
|
+
*/
|
|
220
|
+
basePath?: string;
|
|
221
|
+
/**
|
|
222
|
+
* Prefix for generated filenames
|
|
223
|
+
* @default 'store-'
|
|
224
|
+
*/
|
|
225
|
+
filePrefix?: string;
|
|
226
|
+
/**
|
|
227
|
+
* Maximum number of records per file before rotation
|
|
228
|
+
* @default 1000
|
|
229
|
+
*/
|
|
230
|
+
maxRecordsPerFile?: number;
|
|
231
|
+
/**
|
|
232
|
+
* Maximum number of files to keep (oldest deleted first)
|
|
233
|
+
* @default 30
|
|
234
|
+
*/
|
|
235
|
+
maxFiles?: number;
|
|
236
|
+
}
|
|
237
|
+
/**
|
|
238
|
+
* Abstract base class for file-based stores with automatic rotation
|
|
239
|
+
*
|
|
240
|
+
* Features:
|
|
241
|
+
* - JSONL format (one JSON object per line)
|
|
242
|
+
* - Date-based file segmentation (YYYYMMDD-timestamp.jsonl)
|
|
243
|
+
* - Automatic rotation when maxRecordsPerFile reached
|
|
244
|
+
* - Automatic cleanup when maxFiles exceeded
|
|
245
|
+
* - Sorted file iteration (oldest to newest)
|
|
246
|
+
*
|
|
247
|
+
* @example
|
|
248
|
+
* ```typescript
|
|
249
|
+
* class MyStore extends FileRotationStore<MyRecord> {
|
|
250
|
+
* async save(record: MyRecord): Promise<void> {
|
|
251
|
+
* return this.appendRecord(record);
|
|
252
|
+
* }
|
|
253
|
+
*
|
|
254
|
+
* async find(criteria: any): Promise<MyRecord[]> {
|
|
255
|
+
* return this.readRecords((rec) => rec.id === criteria.id);
|
|
256
|
+
* }
|
|
257
|
+
* }
|
|
258
|
+
* ```
|
|
259
|
+
*/
|
|
260
|
+
declare abstract class FileRotationStore<TRecord> {
|
|
261
|
+
protected readonly storage: IStorage;
|
|
262
|
+
protected readonly basePath: string;
|
|
263
|
+
protected readonly filePrefix: string;
|
|
264
|
+
protected readonly maxRecordsPerFile: number;
|
|
265
|
+
protected readonly maxFiles: number;
|
|
266
|
+
constructor(storage: IStorage, options?: FileRotationOptions);
|
|
267
|
+
/**
|
|
268
|
+
* Append a record to the current writable file
|
|
269
|
+
*
|
|
270
|
+
* Automatically handles:
|
|
271
|
+
* - File rotation when maxRecordsPerFile exceeded
|
|
272
|
+
* - Cleanup when maxFiles exceeded
|
|
273
|
+
* - JSONL formatting
|
|
274
|
+
*
|
|
275
|
+
* @param record - Record to append
|
|
276
|
+
*/
|
|
277
|
+
protected appendRecord(record: TRecord): Promise<void>;
|
|
278
|
+
/**
|
|
279
|
+
* Read records from all files, optionally filtering
|
|
280
|
+
*
|
|
281
|
+
* @param filter - Optional filter function
|
|
282
|
+
* @param limit - Maximum number of records to return
|
|
283
|
+
* @returns Array of records matching filter
|
|
284
|
+
*/
|
|
285
|
+
protected readRecords(filter?: (record: TRecord) => boolean, limit?: number): Promise<TRecord[]>;
|
|
286
|
+
/**
|
|
287
|
+
* Get the current writable file path
|
|
288
|
+
*
|
|
289
|
+
* Creates a new segment if:
|
|
290
|
+
* - No files exist
|
|
291
|
+
* - Latest file has >= maxRecordsPerFile records
|
|
292
|
+
*
|
|
293
|
+
* @returns Path to writable file
|
|
294
|
+
*/
|
|
295
|
+
protected getWritableFile(): Promise<string>;
|
|
296
|
+
/**
|
|
297
|
+
* Get all store files sorted by timestamp (oldest to newest)
|
|
298
|
+
*
|
|
299
|
+
* @returns Sorted array of file paths
|
|
300
|
+
*/
|
|
301
|
+
protected getFilesSorted(): Promise<string[]>;
|
|
302
|
+
/**
|
|
303
|
+
* Generate a segment file path from timestamp
|
|
304
|
+
*
|
|
305
|
+
* Format: {filePrefix}YYYYMMDD-{timestamp}.jsonl
|
|
306
|
+
* Example: history-20251209-1733769000123.jsonl
|
|
307
|
+
*
|
|
308
|
+
* @param ts - Unix timestamp in milliseconds
|
|
309
|
+
* @returns Full file path
|
|
310
|
+
*/
|
|
311
|
+
protected segmentPath(ts: number): string;
|
|
312
|
+
/**
|
|
313
|
+
* Enforce file rotation by deleting oldest files if maxFiles exceeded
|
|
314
|
+
*/
|
|
315
|
+
protected enforceRotation(): Promise<void>;
|
|
316
|
+
/**
|
|
317
|
+
* Ensure path ends with trailing slash
|
|
318
|
+
*/
|
|
319
|
+
protected ensureTrailingSlash(p: string): string;
|
|
320
|
+
}
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* JSON file operations for KB Labs Mind
|
|
324
|
+
*/
|
|
325
|
+
/**
|
|
326
|
+
* Read JSON file with error handling
|
|
327
|
+
*/
|
|
328
|
+
declare function readJson<T = any>(filePath: string): Promise<T | null>;
|
|
329
|
+
/**
|
|
330
|
+
* Write JSON file atomically with sorted keys
|
|
331
|
+
*/
|
|
332
|
+
declare function writeJson<T>(filePath: string, data: T): Promise<void>;
|
|
333
|
+
/**
|
|
334
|
+
* Compute hash of JSON content
|
|
335
|
+
*/
|
|
336
|
+
declare function computeJsonHash(data: any): string;
|
|
337
|
+
|
|
338
|
+
/**
|
|
339
|
+
* @module @kb-labs/mind-core/verification
|
|
340
|
+
* Mind index verification utilities
|
|
341
|
+
*
|
|
342
|
+
* Moved from mind-gateway to break circular dependency (TASK-004)
|
|
343
|
+
*/
|
|
344
|
+
interface VerifyResult {
|
|
345
|
+
ok: boolean;
|
|
346
|
+
code: string | null;
|
|
347
|
+
inconsistencies: string[];
|
|
348
|
+
hint: string;
|
|
349
|
+
}
|
|
350
|
+
/**
|
|
351
|
+
* Verify Mind index integrity
|
|
352
|
+
*
|
|
353
|
+
* Checks:
|
|
354
|
+
* 1. Main index file exists (.kb/mind/index.json)
|
|
355
|
+
* 2. Individual file hashes match (api-index, deps, recent-diff)
|
|
356
|
+
* 3. Combined index checksum is valid
|
|
357
|
+
* 4. Required files are present
|
|
358
|
+
*
|
|
359
|
+
* @param cwd - Workspace root directory
|
|
360
|
+
* @returns Verification result with inconsistencies list
|
|
361
|
+
*
|
|
362
|
+
* @example
|
|
363
|
+
* ```typescript
|
|
364
|
+
* const result = await verifyIndexes('/path/to/workspace');
|
|
365
|
+
* if (!result.ok) {
|
|
366
|
+
* console.error('Inconsistencies:', result.inconsistencies);
|
|
367
|
+
* console.log('Hint:', result.hint);
|
|
368
|
+
* }
|
|
369
|
+
* ```
|
|
370
|
+
*/
|
|
371
|
+
declare function verifyIndexes(cwd: string): Promise<VerifyResult>;
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* Default configurations for KB Labs Mind
|
|
375
|
+
*/
|
|
376
|
+
|
|
377
|
+
/**
|
|
378
|
+
* Default context budget
|
|
379
|
+
*/
|
|
380
|
+
declare const DEFAULT_BUDGET: ContextBudget;
|
|
381
|
+
/**
|
|
382
|
+
* Default context preset
|
|
383
|
+
*/
|
|
384
|
+
declare const DEFAULT_PRESET: ContextPreset;
|
|
385
|
+
/**
|
|
386
|
+
* Default time budget for indexing operations (ms)
|
|
387
|
+
*/
|
|
388
|
+
declare const DEFAULT_TIME_BUDGET_MS = 800;
|
|
389
|
+
/**
|
|
390
|
+
* Maximum file size to process (bytes)
|
|
391
|
+
*/
|
|
392
|
+
declare const MAX_FILE_SIZE_BYTES: number;
|
|
393
|
+
/**
|
|
394
|
+
* Maximum lines per snippet
|
|
395
|
+
*/
|
|
396
|
+
declare const MAX_SNIPPET_LINES = 60;
|
|
397
|
+
/**
|
|
398
|
+
* Generator string for artifacts
|
|
399
|
+
*/
|
|
400
|
+
declare function getGenerator(): string;
|
|
401
|
+
|
|
402
|
+
export { DEFAULT_BUDGET, DEFAULT_PRESET, DEFAULT_TIME_BUDGET_MS, DefaultTokenEstimator, ERROR_HINTS, type ErrorCode, type FileRotationOptions, FileRotationStore, MAX_FILE_SIZE_BYTES, MAX_SNIPPET_LINES, MindError, type VerifyResult, computeJsonHash, cosineSimilarity, createMindError, defaultTokenEstimator, dotProduct, estimateTokens, findWorkspaceRoot, fromPosix, getExitCode, getGenerator, isMindError, magnitude, makeRelativeToRoot, normalize, readJson, sha256, sha256Buffer, sha256File, shouldIgnorePath, toPosix, truncateToTokens, verifyIndexes, wrapError, writeJson };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,574 @@
|
|
|
1
|
+
export * from '@kb-labs/mind-types';
|
|
2
|
+
import { createHash } from 'crypto';
|
|
3
|
+
import path, { dirname } from 'path';
|
|
4
|
+
import { existsSync, promises } from 'fs';
|
|
5
|
+
import { readFile } from 'fs/promises';
|
|
6
|
+
|
|
7
|
+
// src/index.ts
|
|
8
|
+
|
|
9
|
+
// src/error/mind-error.ts
|
|
10
|
+
var MindError = class extends Error {
|
|
11
|
+
constructor(code, message, hint, meta) {
|
|
12
|
+
super(message);
|
|
13
|
+
this.code = code;
|
|
14
|
+
this.hint = hint;
|
|
15
|
+
this.meta = meta;
|
|
16
|
+
this.name = "MindError";
|
|
17
|
+
}
|
|
18
|
+
};
|
|
19
|
+
function getExitCode(err) {
|
|
20
|
+
if (err.code === "MIND_FORBIDDEN") {
|
|
21
|
+
return 3;
|
|
22
|
+
}
|
|
23
|
+
if (err.code === "MIND_NO_GIT") {
|
|
24
|
+
return 2;
|
|
25
|
+
}
|
|
26
|
+
if (err.code === "MIND_FS_TIMEOUT") {
|
|
27
|
+
return 2;
|
|
28
|
+
}
|
|
29
|
+
if (err.code === "MIND_PARSE_ERROR") {
|
|
30
|
+
return 1;
|
|
31
|
+
}
|
|
32
|
+
if (err.code === "MIND_PACK_BUDGET_EXCEEDED") {
|
|
33
|
+
return 1;
|
|
34
|
+
}
|
|
35
|
+
if (err.code.startsWith("MIND_")) {
|
|
36
|
+
return 1;
|
|
37
|
+
}
|
|
38
|
+
return 1;
|
|
39
|
+
}
|
|
40
|
+
var ERROR_HINTS = {
|
|
41
|
+
MIND_NO_GIT: "Initialize git repository or run from a git repository",
|
|
42
|
+
MIND_FS_TIMEOUT: "File system operation timed out - try increasing time budget",
|
|
43
|
+
MIND_PARSE_ERROR: "Failed to parse file - check syntax and try again",
|
|
44
|
+
MIND_PACK_BUDGET_EXCEEDED: "Context pack exceeds token budget - reduce content or increase budget",
|
|
45
|
+
MIND_FORBIDDEN: "Operation not permitted - check file permissions",
|
|
46
|
+
MIND_TIME_BUDGET: "Time budget exceeded - operation completed partially",
|
|
47
|
+
MIND_BAD_FLAGS: "Invalid command line flags - check values and try again",
|
|
48
|
+
MIND_INVALID_FLAG: "Invalid flag value - check format and try again",
|
|
49
|
+
MIND_BUNDLE_TIMEOUT: "Bundle operation timed out - skipped bundle information",
|
|
50
|
+
MIND_FEED_ERROR: "Mind feed operation failed - check logs for details",
|
|
51
|
+
MIND_INIT_ERROR: "Mind initialization failed - check permissions and try again",
|
|
52
|
+
MIND_UPDATE_ERROR: "Mind update operation failed - check logs for details",
|
|
53
|
+
MIND_PACK_ERROR: "Mind pack operation failed - check logs for details",
|
|
54
|
+
MIND_GIT_ERROR: "Git operation failed - check git repository status",
|
|
55
|
+
MIND_INDEX_NOT_FOUND: 'Mind indexes not found - run "kb mind init" first',
|
|
56
|
+
MIND_INVALID_PATH: "Invalid file or directory path - check path exists and is accessible",
|
|
57
|
+
MIND_DEPENDENCY_ERROR: "Dependency resolution failed - check package configuration",
|
|
58
|
+
MIND_BUILD_ERROR: "Build operation failed - check configuration and try again"
|
|
59
|
+
};
|
|
60
|
+
function createMindError(code, message, meta) {
|
|
61
|
+
return new MindError(code, message, ERROR_HINTS[code], meta);
|
|
62
|
+
}
|
|
63
|
+
function wrapError(error, code = "MIND_FEED_ERROR") {
|
|
64
|
+
if (error instanceof MindError) {
|
|
65
|
+
return error;
|
|
66
|
+
}
|
|
67
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
68
|
+
return createMindError(code, message, { originalError: error });
|
|
69
|
+
}
|
|
70
|
+
function isMindError(error) {
|
|
71
|
+
return error instanceof MindError;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// src/utils/token.ts
|
|
75
|
+
var DefaultTokenEstimator = class {
|
|
76
|
+
charsPerToken = 4;
|
|
77
|
+
codeBonus = 0.1;
|
|
78
|
+
// 10% bonus for code-like content
|
|
79
|
+
punctuationWeight = 0.8;
|
|
80
|
+
estimate(text) {
|
|
81
|
+
if (!text || text.length === 0) {
|
|
82
|
+
return 0;
|
|
83
|
+
}
|
|
84
|
+
const words = text.match(/\b\w+\b/g) || [];
|
|
85
|
+
const punctuation = text.match(/[^\w\s]/g) || [];
|
|
86
|
+
const whitespace = text.match(/\s/g) || [];
|
|
87
|
+
let tokens = words.length;
|
|
88
|
+
tokens += punctuation.length * this.punctuationWeight;
|
|
89
|
+
tokens += whitespace.length * 0.3;
|
|
90
|
+
const codeIndicators = text.match(/[{}();=<>]/g) || [];
|
|
91
|
+
if (codeIndicators.length > words.length * 0.1) {
|
|
92
|
+
tokens *= 1 + this.codeBonus;
|
|
93
|
+
}
|
|
94
|
+
const charBasedEstimate = text.length / this.charsPerToken;
|
|
95
|
+
return Math.ceil(Math.max(tokens, charBasedEstimate));
|
|
96
|
+
}
|
|
97
|
+
truncate(text, maxTokens, mode) {
|
|
98
|
+
if (this.estimate(text) <= maxTokens) {
|
|
99
|
+
return text;
|
|
100
|
+
}
|
|
101
|
+
const lines = text.split("\n");
|
|
102
|
+
const estimatedTokens = this.estimate(text);
|
|
103
|
+
const ratio = maxTokens / estimatedTokens;
|
|
104
|
+
const targetLines = Math.max(1, Math.floor(lines.length * ratio));
|
|
105
|
+
if (targetLines >= lines.length) {
|
|
106
|
+
return text;
|
|
107
|
+
}
|
|
108
|
+
switch (mode) {
|
|
109
|
+
case "start":
|
|
110
|
+
return lines.slice(0, targetLines).join("\n");
|
|
111
|
+
case "end":
|
|
112
|
+
return lines.slice(-targetLines).join("\n");
|
|
113
|
+
case "middle":
|
|
114
|
+
default: {
|
|
115
|
+
const startLines = Math.max(1, Math.floor(targetLines / 2));
|
|
116
|
+
const endLines = Math.max(1, targetLines - startLines);
|
|
117
|
+
const start = lines.slice(0, startLines);
|
|
118
|
+
const end = lines.slice(-endLines);
|
|
119
|
+
return [...start, "...", ...end].join("\n");
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
};
|
|
124
|
+
var defaultTokenEstimator = new DefaultTokenEstimator();
|
|
125
|
+
function estimateTokens(text) {
|
|
126
|
+
return defaultTokenEstimator.estimate(text);
|
|
127
|
+
}
|
|
128
|
+
function truncateToTokens(text, maxTokens, mode = "middle") {
|
|
129
|
+
return defaultTokenEstimator.truncate(text, maxTokens, mode);
|
|
130
|
+
}
|
|
131
|
+
function sha256(content) {
|
|
132
|
+
return createHash("sha256").update(content, "utf8").digest("hex");
|
|
133
|
+
}
|
|
134
|
+
function sha256Buffer(buffer) {
|
|
135
|
+
return createHash("sha256").update(buffer).digest("hex");
|
|
136
|
+
}
|
|
137
|
+
async function sha256File(filePath) {
|
|
138
|
+
const { readFile: readFile2 } = await import('fs/promises');
|
|
139
|
+
const content = await readFile2(filePath);
|
|
140
|
+
return sha256Buffer(content);
|
|
141
|
+
}
|
|
142
|
+
function toPosix(filePath) {
|
|
143
|
+
return filePath.replace(/\\/g, "/");
|
|
144
|
+
}
|
|
145
|
+
function fromPosix(posixPath) {
|
|
146
|
+
return posixPath.split("/").join(path.sep);
|
|
147
|
+
}
|
|
148
|
+
async function findWorkspaceRoot(cwd) {
|
|
149
|
+
let current = path.resolve(cwd);
|
|
150
|
+
const root = path.parse(current).root;
|
|
151
|
+
while (current !== root) {
|
|
152
|
+
if (existsSync(path.join(current, ".git"))) {
|
|
153
|
+
return toPosix(current);
|
|
154
|
+
}
|
|
155
|
+
const packageJsonPath = path.join(current, "package.json");
|
|
156
|
+
if (existsSync(packageJsonPath)) {
|
|
157
|
+
try {
|
|
158
|
+
const packageJsonContent = await readFile(packageJsonPath, "utf8");
|
|
159
|
+
const packageJson = JSON.parse(packageJsonContent);
|
|
160
|
+
if (packageJson.workspaces || packageJson.pnpm?.workspace) {
|
|
161
|
+
return toPosix(current);
|
|
162
|
+
}
|
|
163
|
+
} catch {
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
if (existsSync(path.join(current, "pnpm-workspace.yaml"))) {
|
|
167
|
+
return toPosix(current);
|
|
168
|
+
}
|
|
169
|
+
current = path.dirname(current);
|
|
170
|
+
}
|
|
171
|
+
return toPosix(cwd);
|
|
172
|
+
}
|
|
173
|
+
function makeRelativeToRoot(absolutePath, root) {
|
|
174
|
+
const relative = path.relative(root, absolutePath);
|
|
175
|
+
return toPosix(relative);
|
|
176
|
+
}
|
|
177
|
+
function shouldIgnorePath(filePath) {
|
|
178
|
+
const posixPath = toPosix(filePath);
|
|
179
|
+
const ignorePatterns = [
|
|
180
|
+
"node_modules/**",
|
|
181
|
+
".git/**",
|
|
182
|
+
".kb/**",
|
|
183
|
+
// except .kb/mind/**
|
|
184
|
+
"dist/**",
|
|
185
|
+
"coverage/**",
|
|
186
|
+
".turbo/**",
|
|
187
|
+
".vite/**",
|
|
188
|
+
"**/*.log",
|
|
189
|
+
"**/*.tmp",
|
|
190
|
+
"**/*.temp"
|
|
191
|
+
];
|
|
192
|
+
const extensionPatterns = [".log", ".tmp", ".temp"];
|
|
193
|
+
if (posixPath.startsWith(".kb/") && !posixPath.startsWith(".kb/mind/")) {
|
|
194
|
+
return true;
|
|
195
|
+
}
|
|
196
|
+
if (posixPath.startsWith(".kb/mind/")) {
|
|
197
|
+
return false;
|
|
198
|
+
}
|
|
199
|
+
if (extensionPatterns.some((ext) => posixPath.endsWith(ext))) {
|
|
200
|
+
return true;
|
|
201
|
+
}
|
|
202
|
+
return ignorePatterns.some((pattern) => {
|
|
203
|
+
if (pattern.endsWith("/**")) {
|
|
204
|
+
const prefix = pattern.slice(0, -3);
|
|
205
|
+
return posixPath.startsWith(prefix + "/") || posixPath === prefix;
|
|
206
|
+
}
|
|
207
|
+
if (pattern.endsWith("**")) {
|
|
208
|
+
const prefix = pattern.slice(0, -2);
|
|
209
|
+
return posixPath.startsWith(prefix);
|
|
210
|
+
}
|
|
211
|
+
if (pattern.startsWith("**/")) {
|
|
212
|
+
const suffix = pattern.slice(3);
|
|
213
|
+
return posixPath.endsWith(suffix);
|
|
214
|
+
}
|
|
215
|
+
if (pattern.includes("*")) {
|
|
216
|
+
let regexPattern = pattern.replace(/\*\*/g, ".*").replace(/\*/g, "[^/]*");
|
|
217
|
+
if (pattern.startsWith("**/")) {
|
|
218
|
+
regexPattern = ".*" + regexPattern.slice(3);
|
|
219
|
+
}
|
|
220
|
+
const regex = new RegExp("^" + regexPattern + "$");
|
|
221
|
+
return regex.test(posixPath);
|
|
222
|
+
}
|
|
223
|
+
return posixPath.includes(pattern);
|
|
224
|
+
});
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// src/utils/math.ts
|
|
228
|
+
function cosineSimilarity(a, b) {
|
|
229
|
+
if (a.length !== b.length) {
|
|
230
|
+
return 0;
|
|
231
|
+
}
|
|
232
|
+
let dotProduct2 = 0;
|
|
233
|
+
let normA = 0;
|
|
234
|
+
let normB = 0;
|
|
235
|
+
for (let i = 0; i < a.length; i++) {
|
|
236
|
+
const av = a[i] ?? 0;
|
|
237
|
+
const bv = b[i] ?? 0;
|
|
238
|
+
dotProduct2 += av * bv;
|
|
239
|
+
normA += av * av;
|
|
240
|
+
normB += bv * bv;
|
|
241
|
+
}
|
|
242
|
+
if (normA === 0 || normB === 0) {
|
|
243
|
+
return 0;
|
|
244
|
+
}
|
|
245
|
+
return dotProduct2 / Math.sqrt(normA * normB);
|
|
246
|
+
}
|
|
247
|
+
function dotProduct(a, b) {
|
|
248
|
+
if (a.length !== b.length) {
|
|
249
|
+
return 0;
|
|
250
|
+
}
|
|
251
|
+
let result = 0;
|
|
252
|
+
for (let i = 0; i < a.length; i++) {
|
|
253
|
+
result += (a[i] ?? 0) * (b[i] ?? 0);
|
|
254
|
+
}
|
|
255
|
+
return result;
|
|
256
|
+
}
|
|
257
|
+
function magnitude(vec) {
|
|
258
|
+
let sum = 0;
|
|
259
|
+
for (let i = 0; i < vec.length; i++) {
|
|
260
|
+
const v = vec[i] ?? 0;
|
|
261
|
+
sum += v * v;
|
|
262
|
+
}
|
|
263
|
+
return Math.sqrt(sum);
|
|
264
|
+
}
|
|
265
|
+
function normalize(vec) {
|
|
266
|
+
const mag = magnitude(vec);
|
|
267
|
+
if (mag === 0) {
|
|
268
|
+
return vec;
|
|
269
|
+
}
|
|
270
|
+
return vec.map((v) => (v ?? 0) / mag);
|
|
271
|
+
}
|
|
272
|
+
var FileRotationStore = class {
|
|
273
|
+
constructor(storage, options = {}) {
|
|
274
|
+
this.storage = storage;
|
|
275
|
+
this.basePath = options.basePath ? this.ensureTrailingSlash(options.basePath) : ".kb/mind/store/";
|
|
276
|
+
this.filePrefix = options.filePrefix ?? "store-";
|
|
277
|
+
this.maxRecordsPerFile = options.maxRecordsPerFile ?? 1e3;
|
|
278
|
+
this.maxFiles = options.maxFiles ?? 30;
|
|
279
|
+
}
|
|
280
|
+
basePath;
|
|
281
|
+
filePrefix;
|
|
282
|
+
maxRecordsPerFile;
|
|
283
|
+
maxFiles;
|
|
284
|
+
/**
|
|
285
|
+
* Append a record to the current writable file
|
|
286
|
+
*
|
|
287
|
+
* Automatically handles:
|
|
288
|
+
* - File rotation when maxRecordsPerFile exceeded
|
|
289
|
+
* - Cleanup when maxFiles exceeded
|
|
290
|
+
* - JSONL formatting
|
|
291
|
+
*
|
|
292
|
+
* @param record - Record to append
|
|
293
|
+
*/
|
|
294
|
+
async appendRecord(record) {
|
|
295
|
+
const target = await this.getWritableFile();
|
|
296
|
+
const line = JSON.stringify({ v: 1, record }) + "\n";
|
|
297
|
+
const existing = await this.storage.read(target);
|
|
298
|
+
const buffer = existing ? Buffer.concat([existing, Buffer.from(line, "utf8")]) : Buffer.from(line, "utf8");
|
|
299
|
+
await this.storage.write(target, buffer);
|
|
300
|
+
await this.enforceRotation();
|
|
301
|
+
}
|
|
302
|
+
/**
|
|
303
|
+
* Read records from all files, optionally filtering
|
|
304
|
+
*
|
|
305
|
+
* @param filter - Optional filter function
|
|
306
|
+
* @param limit - Maximum number of records to return
|
|
307
|
+
* @returns Array of records matching filter
|
|
308
|
+
*/
|
|
309
|
+
async readRecords(filter, limit) {
|
|
310
|
+
const files = await this.getFilesSorted();
|
|
311
|
+
const results = [];
|
|
312
|
+
for (const file of files) {
|
|
313
|
+
if (limit && results.length >= limit) {
|
|
314
|
+
break;
|
|
315
|
+
}
|
|
316
|
+
const buf = await this.storage.read(file);
|
|
317
|
+
if (!buf) {
|
|
318
|
+
continue;
|
|
319
|
+
}
|
|
320
|
+
const lines = buf.toString("utf8").split("\n").filter(Boolean);
|
|
321
|
+
for (const line of lines) {
|
|
322
|
+
if (limit && results.length >= limit) {
|
|
323
|
+
break;
|
|
324
|
+
}
|
|
325
|
+
try {
|
|
326
|
+
const parsed = JSON.parse(line);
|
|
327
|
+
const rec = parsed.record;
|
|
328
|
+
if (!filter || filter(rec)) {
|
|
329
|
+
results.push(rec);
|
|
330
|
+
}
|
|
331
|
+
} catch {
|
|
332
|
+
continue;
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
return limit ? results.slice(0, limit) : results;
|
|
337
|
+
}
|
|
338
|
+
/**
|
|
339
|
+
* Get the current writable file path
|
|
340
|
+
*
|
|
341
|
+
* Creates a new segment if:
|
|
342
|
+
* - No files exist
|
|
343
|
+
* - Latest file has >= maxRecordsPerFile records
|
|
344
|
+
*
|
|
345
|
+
* @returns Path to writable file
|
|
346
|
+
*/
|
|
347
|
+
async getWritableFile() {
|
|
348
|
+
const files = await this.getFilesSorted();
|
|
349
|
+
if (files.length === 0) {
|
|
350
|
+
return this.segmentPath(Date.now());
|
|
351
|
+
}
|
|
352
|
+
const latest = files[files.length - 1];
|
|
353
|
+
const buf = await this.storage.read(latest);
|
|
354
|
+
if (!buf) {
|
|
355
|
+
return latest;
|
|
356
|
+
}
|
|
357
|
+
const count = buf.toString("utf8").split("\n").filter(Boolean).length;
|
|
358
|
+
if (count >= this.maxRecordsPerFile) {
|
|
359
|
+
return this.segmentPath(Date.now());
|
|
360
|
+
}
|
|
361
|
+
return latest;
|
|
362
|
+
}
|
|
363
|
+
/**
|
|
364
|
+
* Get all store files sorted by timestamp (oldest to newest)
|
|
365
|
+
*
|
|
366
|
+
* @returns Sorted array of file paths
|
|
367
|
+
*/
|
|
368
|
+
async getFilesSorted() {
|
|
369
|
+
const files = await this.storage.list(this.basePath);
|
|
370
|
+
return files.filter((f) => f.startsWith(this.basePath + this.filePrefix) && f.endsWith(".jsonl")).sort();
|
|
371
|
+
}
|
|
372
|
+
/**
|
|
373
|
+
* Generate a segment file path from timestamp
|
|
374
|
+
*
|
|
375
|
+
* Format: {filePrefix}YYYYMMDD-{timestamp}.jsonl
|
|
376
|
+
* Example: history-20251209-1733769000123.jsonl
|
|
377
|
+
*
|
|
378
|
+
* @param ts - Unix timestamp in milliseconds
|
|
379
|
+
* @returns Full file path
|
|
380
|
+
*/
|
|
381
|
+
segmentPath(ts) {
|
|
382
|
+
const date = new Date(ts);
|
|
383
|
+
const day = String(date.getDate()).padStart(2, "0");
|
|
384
|
+
const month = String(date.getMonth() + 1).padStart(2, "0");
|
|
385
|
+
const year = date.getFullYear();
|
|
386
|
+
const filename = `${this.filePrefix}${year}${month}${day}-${ts}.jsonl`;
|
|
387
|
+
return path.posix.join(this.basePath, filename);
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Enforce file rotation by deleting oldest files if maxFiles exceeded
|
|
391
|
+
*/
|
|
392
|
+
async enforceRotation() {
|
|
393
|
+
const files = await this.getFilesSorted();
|
|
394
|
+
if (files.length <= this.maxFiles) {
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
397
|
+
const excess = files.length - this.maxFiles;
|
|
398
|
+
const toDelete = files.slice(0, excess);
|
|
399
|
+
await Promise.all(toDelete.map((f) => this.storage.delete(f)));
|
|
400
|
+
}
|
|
401
|
+
/**
|
|
402
|
+
* Ensure path ends with trailing slash
|
|
403
|
+
*/
|
|
404
|
+
ensureTrailingSlash(p) {
|
|
405
|
+
return p.endsWith("/") ? p : `${p}/`;
|
|
406
|
+
}
|
|
407
|
+
};
|
|
408
|
+
function sortKeysRecursively(obj) {
|
|
409
|
+
if (obj === null || typeof obj !== "object") {
|
|
410
|
+
return obj;
|
|
411
|
+
}
|
|
412
|
+
if (Array.isArray(obj)) {
|
|
413
|
+
return obj.map(sortKeysRecursively);
|
|
414
|
+
}
|
|
415
|
+
const sorted = {};
|
|
416
|
+
const keys = Object.keys(obj).sort();
|
|
417
|
+
for (const key of keys) {
|
|
418
|
+
sorted[key] = sortKeysRecursively(obj[key]);
|
|
419
|
+
}
|
|
420
|
+
return sorted;
|
|
421
|
+
}
|
|
422
|
+
async function readJson(filePath) {
|
|
423
|
+
try {
|
|
424
|
+
const content = await promises.readFile(filePath, "utf8");
|
|
425
|
+
return JSON.parse(content);
|
|
426
|
+
} catch (error) {
|
|
427
|
+
if (error.code === "ENOENT") {
|
|
428
|
+
return null;
|
|
429
|
+
}
|
|
430
|
+
throw error;
|
|
431
|
+
}
|
|
432
|
+
}
|
|
433
|
+
async function writeJson(filePath, data) {
|
|
434
|
+
const tmp = `${filePath}.tmp`;
|
|
435
|
+
const sorted = sortKeysRecursively(data);
|
|
436
|
+
const content = JSON.stringify(sorted, null, 2) + "\n";
|
|
437
|
+
await promises.mkdir(dirname(filePath), { recursive: true });
|
|
438
|
+
await promises.writeFile(tmp, content, "utf8");
|
|
439
|
+
if (process.platform === "win32") {
|
|
440
|
+
try {
|
|
441
|
+
await promises.unlink(filePath);
|
|
442
|
+
} catch (err) {
|
|
443
|
+
if (err.code !== "ENOENT") {
|
|
444
|
+
throw err;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
await promises.rename(tmp, filePath);
|
|
449
|
+
}
|
|
450
|
+
function computeJsonHash(data) {
|
|
451
|
+
const content = JSON.stringify(sortKeysRecursively(data));
|
|
452
|
+
return sha256(content);
|
|
453
|
+
}
|
|
454
|
+
async function verifyIndexes(cwd) {
|
|
455
|
+
const inconsistencies = [];
|
|
456
|
+
try {
|
|
457
|
+
const index = await readJson(`${cwd}/.kb/mind/index.json`);
|
|
458
|
+
if (!index) {
|
|
459
|
+
return {
|
|
460
|
+
ok: false,
|
|
461
|
+
code: "MIND_NO_INDEX",
|
|
462
|
+
inconsistencies: ["Main index file not found"],
|
|
463
|
+
hint: 'Run "kb mind rag-index" to initialize indexes'
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
const [apiIndex, depsGraph, recentDiff, meta, docs] = await Promise.all([
|
|
467
|
+
readJson(`${cwd}/.kb/mind/api-index.json`),
|
|
468
|
+
readJson(`${cwd}/.kb/mind/deps.json`),
|
|
469
|
+
readJson(`${cwd}/.kb/mind/recent-diff.json`),
|
|
470
|
+
readJson(`${cwd}/.kb/mind/meta.json`),
|
|
471
|
+
readJson(`${cwd}/.kb/mind/docs.json`)
|
|
472
|
+
]);
|
|
473
|
+
if (apiIndex) {
|
|
474
|
+
const computedHash = computeJsonHash(apiIndex);
|
|
475
|
+
if (computedHash !== index.apiIndexHash) {
|
|
476
|
+
inconsistencies.push(
|
|
477
|
+
`API index hash mismatch: expected ${index.apiIndexHash}, got ${computedHash}`
|
|
478
|
+
);
|
|
479
|
+
}
|
|
480
|
+
} else if (index.apiIndexHash) {
|
|
481
|
+
inconsistencies.push("API index file missing but hash is present");
|
|
482
|
+
}
|
|
483
|
+
if (depsGraph) {
|
|
484
|
+
const computedHash = computeJsonHash(depsGraph);
|
|
485
|
+
if (computedHash !== index.depsHash) {
|
|
486
|
+
inconsistencies.push(
|
|
487
|
+
`Dependencies hash mismatch: expected ${index.depsHash}, got ${computedHash}`
|
|
488
|
+
);
|
|
489
|
+
}
|
|
490
|
+
} else if (index.depsHash) {
|
|
491
|
+
inconsistencies.push("Dependencies file missing but hash is present");
|
|
492
|
+
}
|
|
493
|
+
if (recentDiff) {
|
|
494
|
+
const computedHash = computeJsonHash(recentDiff);
|
|
495
|
+
if (computedHash !== index.recentDiffHash) {
|
|
496
|
+
inconsistencies.push(
|
|
497
|
+
`Recent diff hash mismatch: expected ${index.recentDiffHash}, got ${computedHash}`
|
|
498
|
+
);
|
|
499
|
+
}
|
|
500
|
+
} else if (index.recentDiffHash) {
|
|
501
|
+
inconsistencies.push("Recent diff file missing but hash is present");
|
|
502
|
+
}
|
|
503
|
+
const combinedContent = JSON.stringify({
|
|
504
|
+
apiIndex: apiIndex || {},
|
|
505
|
+
deps: depsGraph || {},
|
|
506
|
+
recentDiff: recentDiff || {},
|
|
507
|
+
meta: meta || {},
|
|
508
|
+
docs: docs || {}
|
|
509
|
+
});
|
|
510
|
+
const computedChecksum = sha256(combinedContent);
|
|
511
|
+
if (computedChecksum !== index.indexChecksum) {
|
|
512
|
+
inconsistencies.push(
|
|
513
|
+
`Index checksum mismatch: expected ${index.indexChecksum}, got ${computedChecksum}`
|
|
514
|
+
);
|
|
515
|
+
}
|
|
516
|
+
const expectedFiles = ["api-index.json", "deps.json", "recent-diff.json"];
|
|
517
|
+
for (const file of expectedFiles) {
|
|
518
|
+
try {
|
|
519
|
+
await promises.access(`${cwd}/.kb/mind/${file}`);
|
|
520
|
+
} catch {
|
|
521
|
+
inconsistencies.push(`Required index file missing: ${file}`);
|
|
522
|
+
}
|
|
523
|
+
}
|
|
524
|
+
const ok = inconsistencies.length === 0;
|
|
525
|
+
const code = ok ? null : "MIND_INDEX_INCONSISTENT";
|
|
526
|
+
const hint = ok ? "All indexes are consistent and up to date" : 'Run "kb mind rag-index" to rebuild indexes';
|
|
527
|
+
return { ok, code, inconsistencies, hint };
|
|
528
|
+
} catch (error) {
|
|
529
|
+
return {
|
|
530
|
+
ok: false,
|
|
531
|
+
code: "MIND_VERIFY_ERROR",
|
|
532
|
+
inconsistencies: [`Verification failed: ${error.message}`],
|
|
533
|
+
hint: "Check file permissions and workspace structure"
|
|
534
|
+
};
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
|
|
538
|
+
// src/defaults.ts
|
|
539
|
+
var DEFAULT_BUDGET = {
|
|
540
|
+
totalTokens: 9e3,
|
|
541
|
+
caps: {
|
|
542
|
+
intent_summary: 300,
|
|
543
|
+
product_overview: 600,
|
|
544
|
+
project_meta: 500,
|
|
545
|
+
api_signatures: 2200,
|
|
546
|
+
recent_diffs: 1200,
|
|
547
|
+
docs_overview: 600,
|
|
548
|
+
impl_snippets: 3e3,
|
|
549
|
+
configs_profiles: 700
|
|
550
|
+
},
|
|
551
|
+
truncation: "middle"
|
|
552
|
+
};
|
|
553
|
+
var DEFAULT_PRESET = {
|
|
554
|
+
name: "balanced",
|
|
555
|
+
weight: {
|
|
556
|
+
overview: 1,
|
|
557
|
+
api: 1.2,
|
|
558
|
+
diffs: 1,
|
|
559
|
+
snippets: 1.4,
|
|
560
|
+
configs: 0.6,
|
|
561
|
+
meta: 0.8,
|
|
562
|
+
docs: 0.9
|
|
563
|
+
}
|
|
564
|
+
};
|
|
565
|
+
var DEFAULT_TIME_BUDGET_MS = 800;
|
|
566
|
+
var MAX_FILE_SIZE_BYTES = 1.5 * 1024 * 1024;
|
|
567
|
+
var MAX_SNIPPET_LINES = 60;
|
|
568
|
+
function getGenerator() {
|
|
569
|
+
return "kb-labs-mind@0.1.0";
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
export { DEFAULT_BUDGET, DEFAULT_PRESET, DEFAULT_TIME_BUDGET_MS, DefaultTokenEstimator, ERROR_HINTS, FileRotationStore, MAX_FILE_SIZE_BYTES, MAX_SNIPPET_LINES, MindError, computeJsonHash, cosineSimilarity, createMindError, defaultTokenEstimator, dotProduct, estimateTokens, findWorkspaceRoot, fromPosix, getExitCode, getGenerator, isMindError, magnitude, makeRelativeToRoot, normalize, readJson, sha256, sha256Buffer, sha256File, shouldIgnorePath, toPosix, truncateToTokens, verifyIndexes, wrapError, writeJson };
|
|
573
|
+
//# sourceMappingURL=index.js.map
|
|
574
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/error/mind-error.ts","../src/utils/token.ts","../src/utils/hash.ts","../src/utils/paths.ts","../src/utils/math.ts","../src/utils/file-rotation.ts","../src/utils/json.ts","../src/verification/verify-indexes.ts","../src/defaults.ts"],"names":["readFile","dotProduct","path","fsp"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * @module @kb-labs/mind-core/error\n * Standardized error class for KB Labs Mind\n */\n\nexport class MindError extends Error {\n constructor(\n public code: string,\n message: string,\n public hint?: string,\n public meta?: any\n ) {\n super(message);\n this.name = 'MindError';\n }\n}\n\n/**\n * Maps MindError codes to CLI exit codes\n */\nexport function getExitCode(err: MindError): number {\n if (err.code === 'MIND_FORBIDDEN') {return 3;}\n if (err.code === 'MIND_NO_GIT') {return 2;}\n if (err.code === 'MIND_FS_TIMEOUT') {return 2;}\n if (err.code === 'MIND_PARSE_ERROR') {return 1;}\n if (err.code === 'MIND_PACK_BUDGET_EXCEEDED') {return 1;}\n if (err.code.startsWith('MIND_')) {return 1;}\n return 1;\n}\n\n/**\n * Error codes with their standard hints\n */\nexport const ERROR_HINTS = {\n MIND_NO_GIT: 'Initialize git repository or run from a git repository',\n MIND_FS_TIMEOUT: 'File system operation timed out - try increasing time budget',\n MIND_PARSE_ERROR: 'Failed to parse file - check syntax and try again',\n MIND_PACK_BUDGET_EXCEEDED: 'Context pack exceeds token budget - reduce content or increase budget',\n MIND_FORBIDDEN: 'Operation not permitted - check file permissions',\n MIND_TIME_BUDGET: 'Time budget exceeded - operation completed partially',\n MIND_BAD_FLAGS: 'Invalid command line flags - check values and try again',\n MIND_INVALID_FLAG: 'Invalid flag value - check format and try again',\n MIND_BUNDLE_TIMEOUT: 'Bundle operation timed out - skipped bundle information',\n MIND_FEED_ERROR: 'Mind feed operation failed - check logs for details',\n MIND_INIT_ERROR: 'Mind initialization failed - check permissions and try again',\n MIND_UPDATE_ERROR: 'Mind update operation failed - check logs for details',\n MIND_PACK_ERROR: 'Mind pack operation failed - check logs for details',\n MIND_GIT_ERROR: 'Git operation failed - check git repository status',\n MIND_INDEX_NOT_FOUND: 'Mind indexes not found - run \"kb mind init\" first',\n MIND_INVALID_PATH: 'Invalid file or directory path - check path exists and is accessible',\n MIND_DEPENDENCY_ERROR: 'Dependency resolution failed - check package configuration',\n MIND_BUILD_ERROR: 'Build operation failed - check configuration and try again',\n} as const;\n\nexport type ErrorCode = keyof typeof ERROR_HINTS;\n\n/**\n * Create a MindError with standardized code and hint\n */\nexport function createMindError(\n code: ErrorCode,\n message: string,\n meta?: any\n): MindError {\n return new MindError(code, message, ERROR_HINTS[code], meta);\n}\n\n/**\n * Create a MindError from a generic error\n */\nexport function wrapError(error: unknown, code: ErrorCode = 'MIND_FEED_ERROR'): MindError {\n if (error instanceof MindError) {\n return error;\n }\n \n const message = error instanceof Error ? error.message : String(error);\n return createMindError(code, message, { originalError: error });\n}\n\n/**\n * Check if an error is a MindError\n */\nexport function isMindError(error: unknown): error is MindError {\n return error instanceof MindError;\n}\n","/**\n * Token estimation utilities for KB Labs Mind\n */\n\nimport type { ITokenEstimator } from \"../types/pack\";\n\n/**\n * Default whitespace-aware token estimator\n * Algorithm: ~3.8-4.2 chars/token based on whitespace and code patterns\n */\nexport class DefaultTokenEstimator implements ITokenEstimator {\n private readonly charsPerToken: number = 4.0;\n private readonly codeBonus: number = 0.1; // 10% bonus for code-like content\n private readonly punctuationWeight: number = 0.8;\n\n estimate(text: string): number {\n if (!text || text.length === 0) {return 0;}\n\n // Count words, punctuation, and whitespace\n const words = text.match(/\\b\\w+\\b/g) || [];\n const punctuation = text.match(/[^\\w\\s]/g) || [];\n const whitespace = text.match(/\\s/g) || [];\n \n // Base estimation\n let tokens = words.length;\n \n // Add punctuation with weight\n tokens += punctuation.length * this.punctuationWeight;\n \n // Add whitespace (spaces, newlines, tabs)\n tokens += whitespace.length * 0.3;\n \n // Apply code bonus if content looks like code\n const codeIndicators = text.match(/[{}();=<>]/g) || [];\n if (codeIndicators.length > words.length * 0.1) {\n tokens *= (1 + this.codeBonus);\n }\n \n // Apply character-based adjustment\n const charBasedEstimate = text.length / this.charsPerToken;\n \n // Use the higher of word-based or char-based estimate\n return Math.ceil(Math.max(tokens, charBasedEstimate));\n }\n\n truncate(text: string, maxTokens: number, mode: \"start\"|\"middle\"|\"end\"): string {\n if (this.estimate(text) <= maxTokens) {\n return text;\n }\n\n const lines = text.split('\\n');\n const estimatedTokens = this.estimate(text);\n const ratio = maxTokens / estimatedTokens;\n const targetLines = Math.max(1, Math.floor(lines.length * ratio));\n \n if (targetLines >= lines.length) {return text;}\n\n switch (mode) {\n case 'start':\n return lines.slice(0, targetLines).join('\\n');\n case 'end':\n return lines.slice(-targetLines).join('\\n');\n case 'middle':\n default: {\n const startLines = Math.max(1, Math.floor(targetLines / 2));\n const endLines = Math.max(1, targetLines - startLines);\n const start = lines.slice(0, startLines);\n const end = lines.slice(-endLines);\n return [...start, '...', ...end].join('\\n');\n }\n }\n }\n}\n\n/**\n * Default token estimator instance\n */\nexport const defaultTokenEstimator = new DefaultTokenEstimator();\n\n/**\n * Estimate tokens using default strategy\n */\nexport function estimateTokens(text: string): number {\n return defaultTokenEstimator.estimate(text);\n}\n\n/**\n * Truncate text to token limit using default strategy\n */\nexport function truncateToTokens(\n text: string, \n maxTokens: number, \n mode: \"start\"|\"middle\"|\"end\" = \"middle\"\n): string {\n return defaultTokenEstimator.truncate(text, maxTokens, mode);\n}\n","/**\n * Hashing utilities for KB Labs Mind\n */\n\nimport { createHash } from 'node:crypto';\n\n/**\n * Compute SHA256 hash for string content\n */\nexport function sha256(content: string): string {\n return createHash('sha256').update(content, 'utf8').digest('hex');\n}\n\n/**\n * Compute SHA256 hash for Buffer content\n */\nexport function sha256Buffer(buffer: Buffer): string {\n return createHash('sha256').update(buffer).digest('hex');\n}\n\n/**\n * Compute SHA256 hash for file content (streaming for large files)\n */\nexport async function sha256File(filePath: string): Promise<string> {\n const { readFile } = await import('node:fs/promises');\n const content = await readFile(filePath);\n return sha256Buffer(content);\n}\n","/**\n * Path utilities for KB Labs Mind\n */\n\nimport path from 'node:path';\nimport { existsSync } from 'node:fs';\nimport { readFile } from 'node:fs/promises';\n\n/**\n * Convert path to POSIX format (forward slashes)\n */\nexport function toPosix(filePath: string): string {\n return filePath.replace(/\\\\/g, '/');\n}\n\n/**\n * Convert POSIX path back to platform-specific format\n */\nexport function fromPosix(posixPath: string): string {\n return posixPath.split('/').join(path.sep);\n}\n\n/**\n * Find workspace root by looking for git repository or monorepo indicators\n * Searches up the directory tree from cwd\n */\nexport async function findWorkspaceRoot(cwd: string): Promise<string> {\n let current = path.resolve(cwd);\n const root = path.parse(current).root;\n\n while (current !== root) {\n // Check for git repository\n if (existsSync(path.join(current, '.git'))) {\n return toPosix(current);\n }\n\n // Check for monorepo indicators\n const packageJsonPath = path.join(current, 'package.json');\n if (existsSync(packageJsonPath)) {\n try {\n const packageJsonContent = await readFile(packageJsonPath, 'utf8');\n const packageJson = JSON.parse(packageJsonContent);\n // Check for workspace configuration\n if (packageJson.workspaces || packageJson.pnpm?.workspace) {\n return toPosix(current);\n }\n } catch {\n // Ignore JSON parse errors\n }\n }\n\n // Check for pnpm-workspace.yaml\n if (existsSync(path.join(current, 'pnpm-workspace.yaml'))) {\n return toPosix(current);\n }\n\n // Move up one directory\n current = path.dirname(current);\n }\n\n // If no workspace found, return the original cwd as POSIX\n return toPosix(cwd);\n}\n\n/**\n * Make path relative to workspace root\n */\nexport function makeRelativeToRoot(absolutePath: string, root: string): string {\n const relative = path.relative(root, absolutePath);\n return toPosix(relative);\n}\n\n/**\n * Check if path should be ignored based on common patterns\n */\nexport function shouldIgnorePath(filePath: string): boolean {\n const posixPath = toPosix(filePath);\n \n const ignorePatterns = [\n 'node_modules/**',\n '.git/**',\n '.kb/**', // except .kb/mind/**\n 'dist/**',\n 'coverage/**',\n '.turbo/**',\n '.vite/**',\n '**/*.log',\n '**/*.tmp',\n '**/*.temp'\n ];\n \n // Simple file extension patterns\n const extensionPatterns = ['.log', '.tmp', '.temp'];\n\n // Special case: allow .kb/mind/** but ignore other .kb/**\n if (posixPath.startsWith('.kb/') && !posixPath.startsWith('.kb/mind/')) {\n return true;\n }\n \n // Allow .kb/mind/** paths\n if (posixPath.startsWith('.kb/mind/')) {\n return false;\n }\n \n // Check simple extension patterns first\n if (extensionPatterns.some(ext => posixPath.endsWith(ext))) {\n return true;\n }\n\n return ignorePatterns.some(pattern => {\n if (pattern.endsWith('/**')) {\n const prefix = pattern.slice(0, -3);\n return posixPath.startsWith(prefix + '/') || posixPath === prefix;\n }\n if (pattern.endsWith('**')) {\n const prefix = pattern.slice(0, -2);\n return posixPath.startsWith(prefix);\n }\n if (pattern.startsWith('**/')) {\n const suffix = pattern.slice(3);\n return posixPath.endsWith(suffix);\n }\n if (pattern.includes('*')) {\n // Simple glob pattern matching\n let regexPattern = pattern.replace(/\\*\\*/g, '.*').replace(/\\*/g, '[^/]*');\n // Handle **/*.ext patterns that should match files in any directory\n if (pattern.startsWith('**/')) {\n regexPattern = '.*' + regexPattern.slice(3);\n }\n const regex = new RegExp('^' + regexPattern + '$');\n return regex.test(posixPath);\n }\n return posixPath.includes(pattern);\n });\n}\n","/**\n * Mathematical utility functions for KB Labs Mind\n */\n\n/**\n * Calculate cosine similarity between two vectors\n *\n * Cosine similarity measures the cosine of the angle between two vectors,\n * producing a value between -1 and 1, where:\n * - 1 means vectors point in the same direction (identical)\n * - 0 means vectors are orthogonal (no similarity)\n * - -1 means vectors point in opposite directions\n *\n * @param a - First vector (array of numbers)\n * @param b - Second vector (array of numbers)\n * @returns Similarity score [0-1], or 0 if vectors have different lengths or zero magnitudes\n *\n * @example\n * ```typescript\n * const similarity = cosineSimilarity([1, 2, 3], [4, 5, 6]);\n * console.log(similarity); // ~0.974\n * ```\n */\nexport function cosineSimilarity(a: number[], b: number[]): number {\n // Vectors must have same dimensionality\n if (a.length !== b.length) {\n return 0;\n }\n\n let dotProduct = 0;\n let normA = 0;\n let normB = 0;\n\n // Calculate dot product and norms in single pass\n for (let i = 0; i < a.length; i++) {\n const av = a[i] ?? 0;\n const bv = b[i] ?? 0;\n dotProduct += av * bv;\n normA += av * av;\n normB += bv * bv;\n }\n\n // Handle zero magnitude vectors (division by zero)\n if (normA === 0 || normB === 0) {\n return 0;\n }\n\n // cosine(θ) = (a · b) / (||a|| * ||b||)\n return dotProduct / Math.sqrt(normA * normB);\n}\n\n/**\n * Calculate dot product of two vectors\n *\n * @param a - First vector\n * @param b - Second vector\n * @returns Dot product, or 0 if vectors have different lengths\n *\n * @example\n * ```typescript\n * const dot = dotProduct([1, 2, 3], [4, 5, 6]);\n * console.log(dot); // 32\n * ```\n */\nexport function dotProduct(a: number[], b: number[]): number {\n if (a.length !== b.length) {\n return 0;\n }\n\n let result = 0;\n for (let i = 0; i < a.length; i++) {\n result += (a[i] ?? 0) * (b[i] ?? 0);\n }\n return result;\n}\n\n/**\n * Calculate magnitude (L2 norm) of a vector\n *\n * @param vec - Input vector\n * @returns Magnitude (Euclidean length)\n *\n * @example\n * ```typescript\n * const mag = magnitude([3, 4]);\n * console.log(mag); // 5\n * ```\n */\nexport function magnitude(vec: number[]): number {\n let sum = 0;\n for (let i = 0; i < vec.length; i++) {\n const v = vec[i] ?? 0;\n sum += v * v;\n }\n return Math.sqrt(sum);\n}\n\n/**\n * Normalize a vector to unit length\n *\n * @param vec - Input vector\n * @returns Normalized vector (magnitude = 1), or original if magnitude is 0\n *\n * @example\n * ```typescript\n * const normalized = normalize([3, 4]);\n * console.log(normalized); // [0.6, 0.8]\n * ```\n */\nexport function normalize(vec: number[]): number[] {\n const mag = magnitude(vec);\n if (mag === 0) {\n return vec;\n }\n return vec.map(v => (v ?? 0) / mag);\n}\n","/**\n * Base class for file-based stores with rotation support\n *\n * Provides JSONL file rotation, segmentation by date, and automatic cleanup.\n * Used by history stores, feedback stores, and other persistent logging mechanisms.\n */\n\nimport path from 'node:path';\nimport type { IStorage } from '@kb-labs/sdk';\n\nexport interface FileRotationOptions {\n /**\n * Base directory path for storing files\n * @default '.kb/mind/store/'\n */\n basePath?: string;\n\n /**\n * Prefix for generated filenames\n * @default 'store-'\n */\n filePrefix?: string;\n\n /**\n * Maximum number of records per file before rotation\n * @default 1000\n */\n maxRecordsPerFile?: number;\n\n /**\n * Maximum number of files to keep (oldest deleted first)\n * @default 30\n */\n maxFiles?: number;\n}\n\n/**\n * Abstract base class for file-based stores with automatic rotation\n *\n * Features:\n * - JSONL format (one JSON object per line)\n * - Date-based file segmentation (YYYYMMDD-timestamp.jsonl)\n * - Automatic rotation when maxRecordsPerFile reached\n * - Automatic cleanup when maxFiles exceeded\n * - Sorted file iteration (oldest to newest)\n *\n * @example\n * ```typescript\n * class MyStore extends FileRotationStore<MyRecord> {\n * async save(record: MyRecord): Promise<void> {\n * return this.appendRecord(record);\n * }\n *\n * async find(criteria: any): Promise<MyRecord[]> {\n * return this.readRecords((rec) => rec.id === criteria.id);\n * }\n * }\n * ```\n */\nexport abstract class FileRotationStore<TRecord> {\n protected readonly basePath: string;\n protected readonly filePrefix: string;\n protected readonly maxRecordsPerFile: number;\n protected readonly maxFiles: number;\n\n constructor(\n protected readonly storage: IStorage,\n options: FileRotationOptions = {}\n ) {\n this.basePath = options.basePath ? this.ensureTrailingSlash(options.basePath) : '.kb/mind/store/';\n this.filePrefix = options.filePrefix ?? 'store-';\n this.maxRecordsPerFile = options.maxRecordsPerFile ?? 1000;\n this.maxFiles = options.maxFiles ?? 30;\n }\n\n /**\n * Append a record to the current writable file\n *\n * Automatically handles:\n * - File rotation when maxRecordsPerFile exceeded\n * - Cleanup when maxFiles exceeded\n * - JSONL formatting\n *\n * @param record - Record to append\n */\n protected async appendRecord(record: TRecord): Promise<void> {\n const target = await this.getWritableFile();\n const line = JSON.stringify({ v: 1, record }) + '\\n';\n\n const existing = await this.storage.read(target);\n const buffer = existing\n ? Buffer.concat([existing, Buffer.from(line, 'utf8')])\n : Buffer.from(line, 'utf8');\n\n await this.storage.write(target, buffer);\n await this.enforceRotation();\n }\n\n /**\n * Read records from all files, optionally filtering\n *\n * @param filter - Optional filter function\n * @param limit - Maximum number of records to return\n * @returns Array of records matching filter\n */\n protected async readRecords(\n filter?: (record: TRecord) => boolean,\n limit?: number\n ): Promise<TRecord[]> {\n const files = await this.getFilesSorted();\n const results: TRecord[] = [];\n\n for (const file of files) {\n if (limit && results.length >= limit) {break;}\n\n const buf = await this.storage.read(file);\n if (!buf) {continue;}\n\n const lines = buf.toString('utf8').split('\\n').filter(Boolean);\n for (const line of lines) {\n if (limit && results.length >= limit) {break;}\n\n try {\n const parsed = JSON.parse(line) as { v: number; record: TRecord };\n const rec = parsed.record;\n\n if (!filter || filter(rec)) {\n results.push(rec);\n }\n } catch {\n // Skip malformed lines\n continue;\n }\n }\n }\n\n return limit ? results.slice(0, limit) : results;\n }\n\n /**\n * Get the current writable file path\n *\n * Creates a new segment if:\n * - No files exist\n * - Latest file has >= maxRecordsPerFile records\n *\n * @returns Path to writable file\n */\n protected async getWritableFile(): Promise<string> {\n const files = await this.getFilesSorted();\n\n if (files.length === 0) {\n return this.segmentPath(Date.now());\n }\n\n const latest = files[files.length - 1]!;\n const buf = await this.storage.read(latest);\n\n if (!buf) {return latest;}\n\n const count = buf.toString('utf8').split('\\n').filter(Boolean).length;\n if (count >= this.maxRecordsPerFile) {\n return this.segmentPath(Date.now());\n }\n\n return latest;\n }\n\n /**\n * Get all store files sorted by timestamp (oldest to newest)\n *\n * @returns Sorted array of file paths\n */\n protected async getFilesSorted(): Promise<string[]> {\n const files = await this.storage.list(this.basePath);\n return files\n .filter((f) => f.startsWith(this.basePath + this.filePrefix) && f.endsWith('.jsonl'))\n .sort();\n }\n\n /**\n * Generate a segment file path from timestamp\n *\n * Format: {filePrefix}YYYYMMDD-{timestamp}.jsonl\n * Example: history-20251209-1733769000123.jsonl\n *\n * @param ts - Unix timestamp in milliseconds\n * @returns Full file path\n */\n protected segmentPath(ts: number): string {\n const date = new Date(ts);\n const day = String(date.getDate()).padStart(2, '0');\n const month = String(date.getMonth() + 1).padStart(2, '0');\n const year = date.getFullYear();\n const filename = `${this.filePrefix}${year}${month}${day}-${ts}.jsonl`;\n return path.posix.join(this.basePath, filename);\n }\n\n /**\n * Enforce file rotation by deleting oldest files if maxFiles exceeded\n */\n protected async enforceRotation(): Promise<void> {\n const files = await this.getFilesSorted();\n if (files.length <= this.maxFiles) {return;}\n\n const excess = files.length - this.maxFiles;\n const toDelete = files.slice(0, excess);\n await Promise.all(toDelete.map((f: string) => this.storage.delete(f)));\n }\n\n /**\n * Ensure path ends with trailing slash\n */\n protected ensureTrailingSlash(p: string): string {\n return p.endsWith('/') ? p : `${p}/`;\n }\n}\n","/**\n * JSON file operations for KB Labs Mind\n */\n\nimport { promises as fsp } from \"node:fs\";\nimport { dirname } from \"node:path\";\nimport { sha256 } from \"./hash\";\n\n/**\n * Recursively sort object keys for deterministic output\n */\nfunction sortKeysRecursively(obj: any): any {\n if (obj === null || typeof obj !== 'object') {\n return obj;\n }\n\n if (Array.isArray(obj)) {\n return obj.map(sortKeysRecursively);\n }\n\n const sorted: any = {};\n const keys = Object.keys(obj).sort();\n\n for (const key of keys) {\n sorted[key] = sortKeysRecursively(obj[key]);\n }\n\n return sorted;\n}\n\n/**\n * Read JSON file with error handling\n */\nexport async function readJson<T = any>(filePath: string): Promise<T | null> {\n try {\n const content = await fsp.readFile(filePath, 'utf8');\n return JSON.parse(content) as T;\n } catch (error: any) {\n if (error.code === 'ENOENT') {\n return null;\n }\n throw error;\n }\n}\n\n/**\n * Write JSON file atomically with sorted keys\n */\nexport async function writeJson<T>(filePath: string, data: T): Promise<void> {\n const tmp = `${filePath}.tmp`;\n const sorted = sortKeysRecursively(data);\n const content = JSON.stringify(sorted, null, 2) + '\\n';\n\n // Ensure directory exists\n await fsp.mkdir(dirname(filePath), { recursive: true });\n\n // Write to temp file\n await fsp.writeFile(tmp, content, 'utf8');\n\n // Windows-safe atomic rename\n if (process.platform === \"win32\") {\n try {\n await fsp.unlink(filePath);\n } catch (err: any) {\n if (err.code !== \"ENOENT\") {throw err;}\n }\n }\n\n // Rename tmp to final location\n await fsp.rename(tmp, filePath);\n}\n\n/**\n * Compute hash of JSON content\n */\nexport function computeJsonHash(data: any): string {\n const content = JSON.stringify(sortKeysRecursively(data));\n return sha256(content);\n}\n","/**\n * @module @kb-labs/mind-core/verification\n * Mind index verification utilities\n *\n * Moved from mind-gateway to break circular dependency (TASK-004)\n */\n\nimport { readJson, computeJsonHash } from '../utils/json';\nimport { sha256 } from '../utils/hash';\nimport { promises as fsp } from 'node:fs';\n\nexport interface VerifyResult {\n ok: boolean;\n code: string | null;\n inconsistencies: string[];\n hint: string;\n}\n\n/**\n * Verify Mind index integrity\n *\n * Checks:\n * 1. Main index file exists (.kb/mind/index.json)\n * 2. Individual file hashes match (api-index, deps, recent-diff)\n * 3. Combined index checksum is valid\n * 4. Required files are present\n *\n * @param cwd - Workspace root directory\n * @returns Verification result with inconsistencies list\n *\n * @example\n * ```typescript\n * const result = await verifyIndexes('/path/to/workspace');\n * if (!result.ok) {\n * console.error('Inconsistencies:', result.inconsistencies);\n * console.log('Hint:', result.hint);\n * }\n * ```\n */\nexport async function verifyIndexes(cwd: string): Promise<VerifyResult> {\n const inconsistencies: string[] = [];\n\n try {\n // Load main index\n const index = await readJson(`${cwd}/.kb/mind/index.json`);\n if (!index) {\n return {\n ok: false,\n code: 'MIND_NO_INDEX',\n inconsistencies: ['Main index file not found'],\n hint: 'Run \"kb mind rag-index\" to initialize indexes',\n };\n }\n\n // Load all index files\n const [apiIndex, depsGraph, recentDiff, meta, docs] = await Promise.all([\n readJson(`${cwd}/.kb/mind/api-index.json`),\n readJson(`${cwd}/.kb/mind/deps.json`),\n readJson(`${cwd}/.kb/mind/recent-diff.json`),\n readJson(`${cwd}/.kb/mind/meta.json`),\n readJson(`${cwd}/.kb/mind/docs.json`),\n ]);\n\n // Verify individual file hashes\n if (apiIndex) {\n const computedHash = computeJsonHash(apiIndex);\n if (computedHash !== index.apiIndexHash) {\n inconsistencies.push(\n `API index hash mismatch: expected ${index.apiIndexHash}, got ${computedHash}`\n );\n }\n } else if (index.apiIndexHash) {\n inconsistencies.push('API index file missing but hash is present');\n }\n\n if (depsGraph) {\n const computedHash = computeJsonHash(depsGraph);\n if (computedHash !== index.depsHash) {\n inconsistencies.push(\n `Dependencies hash mismatch: expected ${index.depsHash}, got ${computedHash}`\n );\n }\n } else if (index.depsHash) {\n inconsistencies.push('Dependencies file missing but hash is present');\n }\n\n if (recentDiff) {\n const computedHash = computeJsonHash(recentDiff);\n if (computedHash !== index.recentDiffHash) {\n inconsistencies.push(\n `Recent diff hash mismatch: expected ${index.recentDiffHash}, got ${computedHash}`\n );\n }\n } else if (index.recentDiffHash) {\n inconsistencies.push('Recent diff file missing but hash is present');\n }\n\n // Verify combined index checksum\n const combinedContent = JSON.stringify({\n apiIndex: apiIndex || {},\n deps: depsGraph || {},\n recentDiff: recentDiff || {},\n meta: meta || {},\n docs: docs || {},\n });\n const computedChecksum = sha256(combinedContent);\n\n if (computedChecksum !== index.indexChecksum) {\n inconsistencies.push(\n `Index checksum mismatch: expected ${index.indexChecksum}, got ${computedChecksum}`\n );\n }\n\n // Check for missing files that should exist\n const expectedFiles = ['api-index.json', 'deps.json', 'recent-diff.json'];\n for (const file of expectedFiles) {\n try {\n await fsp.access(`${cwd}/.kb/mind/${file}`);\n } catch {\n inconsistencies.push(`Required index file missing: ${file}`);\n }\n }\n\n const ok = inconsistencies.length === 0;\n const code = ok ? null : 'MIND_INDEX_INCONSISTENT';\n const hint = ok\n ? 'All indexes are consistent and up to date'\n : 'Run \"kb mind rag-index\" to rebuild indexes';\n\n return { ok, code, inconsistencies, hint };\n } catch (error: any) {\n return {\n ok: false,\n code: 'MIND_VERIFY_ERROR',\n inconsistencies: [`Verification failed: ${error.message}`],\n hint: 'Check file permissions and workspace structure',\n };\n }\n}\n","/**\n * Default configurations for KB Labs Mind\n */\n\nimport type { ContextBudget, ContextPreset } from \"./types/pack\";\n\n/**\n * Default context budget\n */\nexport const DEFAULT_BUDGET: ContextBudget = {\n totalTokens: 9000,\n caps: {\n intent_summary: 300,\n product_overview: 600,\n project_meta: 500,\n api_signatures: 2200,\n recent_diffs: 1200,\n docs_overview: 600,\n impl_snippets: 3000,\n configs_profiles: 700,\n },\n truncation: \"middle\",\n};\n\n/**\n * Default context preset\n */\nexport const DEFAULT_PRESET: ContextPreset = {\n name: \"balanced\",\n weight: { \n overview: 1, \n api: 1.2, \n diffs: 1, \n snippets: 1.4, \n configs: 0.6,\n meta: 0.8,\n docs: 0.9\n }\n};\n\n/**\n * Default time budget for indexing operations (ms)\n */\nexport const DEFAULT_TIME_BUDGET_MS = 800;\n\n/**\n * Maximum file size to process (bytes)\n */\nexport const MAX_FILE_SIZE_BYTES = 1.5 * 1024 * 1024; // 1.5MB\n\n/**\n * Maximum lines per snippet\n */\nexport const MAX_SNIPPET_LINES = 60;\n\n/**\n * Generator string for artifacts\n */\nexport function getGenerator(): string {\n return \"kb-labs-mind@0.1.0\";\n}\n"]}
|
package/package.json
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@kb-labs/mind-core",
|
|
3
|
+
"version": "1.5.0",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"description": "Core contracts, errors, and utilities for KB Labs Mind",
|
|
6
|
+
"main": "./dist/index.js",
|
|
7
|
+
"types": "./dist/index.d.ts",
|
|
8
|
+
"exports": {
|
|
9
|
+
".": {
|
|
10
|
+
"types": "./dist/index.d.ts",
|
|
11
|
+
"import": "./dist/index.js",
|
|
12
|
+
"require": "./dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"./dist/*": "./dist/*"
|
|
15
|
+
},
|
|
16
|
+
"files": [
|
|
17
|
+
"dist",
|
|
18
|
+
"README.md",
|
|
19
|
+
"LICENSE"
|
|
20
|
+
],
|
|
21
|
+
"sideEffects": false,
|
|
22
|
+
"scripts": {
|
|
23
|
+
"clean": "rimraf dist",
|
|
24
|
+
"build": "tsup --config tsup.config.ts",
|
|
25
|
+
"dev": "tsup --config tsup.config.ts --watch",
|
|
26
|
+
"lint": "eslint src --ext .ts,.tsx,.js,.jsx",
|
|
27
|
+
"lint:fix": "eslint . --fix",
|
|
28
|
+
"type-check": "tsc --noEmit",
|
|
29
|
+
"test": "vitest run --passWithNoTests",
|
|
30
|
+
"test:watch": "vitest"
|
|
31
|
+
},
|
|
32
|
+
"devDependencies": {
|
|
33
|
+
"@kb-labs/devkit": "link:../../../../infra/kb-labs-devkit",
|
|
34
|
+
"@types/node": "^24.3.3",
|
|
35
|
+
"rimraf": "^6.0.1",
|
|
36
|
+
"tsup": "^8.5.0",
|
|
37
|
+
"typescript": "^5.6.3",
|
|
38
|
+
"vitest": "^3.2.4"
|
|
39
|
+
},
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@kb-labs/mind-types": "^1.5.0",
|
|
42
|
+
"@kb-labs/sdk": "^1.5.0"
|
|
43
|
+
},
|
|
44
|
+
"engines": {
|
|
45
|
+
"node": ">=20.0.0",
|
|
46
|
+
"pnpm": ">=9.0.0"
|
|
47
|
+
},
|
|
48
|
+
"packageManager": "pnpm@9.11.0"
|
|
49
|
+
}
|