dino-spec 13.6.0 → 13.6.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -82
- package/bin/dino-hud.js +1 -1
- package/bin/dino.js +1 -1
- package/dist/commands/hud.d.ts +8 -1
- package/dist/commands/hud.d.ts.map +1 -1
- package/dist/commands/hud.js +18 -4
- package/dist/commands/hud.js.map +1 -1
- package/dist/core/agents/context-isolation.js +26 -26
- package/dist/core/config/feature-flags.d.ts +1 -25
- package/dist/core/config/feature-flags.d.ts.map +1 -1
- package/dist/core/config/feature-flags.js +1 -7
- package/dist/core/config/feature-flags.js.map +1 -1
- package/dist/core/context/auto-injection-engine.d.ts +1 -10
- package/dist/core/context/auto-injection-engine.d.ts.map +1 -1
- package/dist/core/context/auto-injection-engine.js +2 -45
- package/dist/core/context/auto-injection-engine.js.map +1 -1
- package/dist/core/context/context-health.d.ts +2 -27
- package/dist/core/context/context-health.d.ts.map +1 -1
- package/dist/core/context/context-health.js +12 -98
- package/dist/core/context/context-health.js.map +1 -1
- package/dist/core/context/index.d.ts +2 -3
- package/dist/core/context/index.d.ts.map +1 -1
- package/dist/core/context/index.js +2 -18
- package/dist/core/context/index.js.map +1 -1
- package/dist/core/context/lazy-loader.d.ts +1 -44
- package/dist/core/context/lazy-loader.d.ts.map +1 -1
- package/dist/core/context/lazy-loader.js +1 -59
- package/dist/core/context/lazy-loader.js.map +1 -1
- package/dist/core/context-repl/index.d.ts +3 -8
- package/dist/core/context-repl/index.d.ts.map +1 -1
- package/dist/core/context-repl/index.js +3 -11
- package/dist/core/context-repl/index.js.map +1 -1
- package/dist/core/context-repl/types.d.ts +1 -277
- package/dist/core/context-repl/types.d.ts.map +1 -1
- package/dist/core/context-repl/types.js +1 -52
- package/dist/core/context-repl/types.js.map +1 -1
- package/dist/core/generator/claude-md.js +1 -1
- package/dist/core/provider/storage.d.ts.map +1 -1
- package/dist/core/provider/storage.js +2 -1
- package/dist/core/provider/storage.js.map +1 -1
- package/dist/hooks/post-edit.js +0 -73
- package/dist/hooks/post-edit.js.map +1 -1
- package/dist/hooks/session-start.js +0 -115
- package/dist/hooks/session-start.js.map +1 -1
- package/dist/hooks/types.js +1 -1
- package/dist/hooks/user-prompt-submit.js +0 -100
- package/dist/hooks/user-prompt-submit.js.map +1 -1
- package/dist/hud/config-tui.d.ts +25 -0
- package/dist/hud/config-tui.d.ts.map +1 -0
- package/dist/hud/config-tui.js +199 -0
- package/dist/hud/config-tui.js.map +1 -0
- package/dist/hud/config.d.ts +28 -3
- package/dist/hud/config.d.ts.map +1 -1
- package/dist/hud/config.js +60 -8
- package/dist/hud/config.js.map +1 -1
- package/dist/hud/index.d.ts.map +1 -1
- package/dist/hud/index.js +1 -0
- package/dist/hud/index.js.map +1 -1
- package/dist/hud/models.d.ts +58 -0
- package/dist/hud/models.d.ts.map +1 -0
- package/dist/hud/models.js +124 -0
- package/dist/hud/models.js.map +1 -0
- package/dist/hud/render/budget-bar.d.ts +2 -0
- package/dist/hud/render/budget-bar.d.ts.map +1 -1
- package/dist/hud/render/budget-bar.js +8 -5
- package/dist/hud/render/budget-bar.js.map +1 -1
- package/dist/hud/stdin.d.ts +3 -0
- package/dist/hud/stdin.d.ts.map +1 -1
- package/dist/hud/stdin.js +29 -2
- package/dist/hud/stdin.js.map +1 -1
- package/dist/hud/token-estimator.d.ts +79 -0
- package/dist/hud/token-estimator.d.ts.map +1 -0
- package/dist/hud/token-estimator.js +126 -0
- package/dist/hud/token-estimator.js.map +1 -0
- package/dist/hud/types.d.ts +2 -0
- package/dist/hud/types.d.ts.map +1 -1
- package/dist/mcp/server.d.ts +1 -2
- package/dist/mcp/server.d.ts.map +1 -1
- package/dist/mcp/server.js +3 -12
- package/dist/mcp/server.js.map +1 -1
- package/dist/mcp/tool-tiers.d.ts.map +1 -1
- package/dist/mcp/tool-tiers.js +0 -5
- package/dist/mcp/tool-tiers.js.map +1 -1
- package/dist/mcp/tools/index.d.ts +2 -7
- package/dist/mcp/tools/index.d.ts.map +1 -1
- package/dist/mcp/tools/index.js +2 -25
- package/dist/mcp/tools/index.js.map +1 -1
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +5 -9
- package/dist/mcp-server.js.map +1 -1
- package/dist/rules/index.js +2 -2
- package/dist/utils/exec.js +2 -2
- package/dist/utils/exec.js.map +1 -1
- package/dist/utils/gitignore.d.ts.map +1 -1
- package/dist/utils/gitignore.js +0 -5
- package/dist/utils/gitignore.js.map +1 -1
- package/package.json +77 -76
- package/dist/core/context/focus-resource-loader.d.ts +0 -143
- package/dist/core/context/focus-resource-loader.d.ts.map +0 -1
- package/dist/core/context/focus-resource-loader.js +0 -305
- package/dist/core/context/focus-resource-loader.js.map +0 -1
- package/dist/core/context-repl/__tests__/repl-environment.test.d.ts +0 -7
- package/dist/core/context-repl/__tests__/repl-environment.test.d.ts.map +0 -1
- package/dist/core/context-repl/__tests__/repl-environment.test.js +0 -335
- package/dist/core/context-repl/__tests__/repl-environment.test.js.map +0 -1
- package/dist/core/context-repl/context-window-monitor.d.ts +0 -181
- package/dist/core/context-repl/context-window-monitor.d.ts.map +0 -1
- package/dist/core/context-repl/context-window-monitor.js +0 -309
- package/dist/core/context-repl/context-window-monitor.js.map +0 -1
- package/dist/core/context-repl/repl-environment.d.ts +0 -66
- package/dist/core/context-repl/repl-environment.d.ts.map +0 -1
- package/dist/core/context-repl/repl-environment.js +0 -795
- package/dist/core/context-repl/repl-environment.js.map +0 -1
- package/dist/mcp/focus-filter.d.ts +0 -74
- package/dist/mcp/focus-filter.d.ts.map +0 -1
- package/dist/mcp/focus-filter.js +0 -229
- package/dist/mcp/focus-filter.js.map +0 -1
- package/dist/mcp/tools/auto-inject.d.ts +0 -36
- package/dist/mcp/tools/auto-inject.d.ts.map +0 -1
- package/dist/mcp/tools/auto-inject.js +0 -143
- package/dist/mcp/tools/auto-inject.js.map +0 -1
- package/dist/mcp/tools/auto-unload.d.ts +0 -29
- package/dist/mcp/tools/auto-unload.d.ts.map +0 -1
- package/dist/mcp/tools/auto-unload.js +0 -151
- package/dist/mcp/tools/auto-unload.js.map +0 -1
- package/dist/mcp/tools/context-repl.d.ts +0 -48
- package/dist/mcp/tools/context-repl.d.ts.map +0 -1
- package/dist/mcp/tools/context-repl.js +0 -290
- package/dist/mcp/tools/context-repl.js.map +0 -1
- package/dist/mcp/tools/health.d.ts +0 -29
- package/dist/mcp/tools/health.d.ts.map +0 -1
- package/dist/mcp/tools/health.js +0 -171
- package/dist/mcp/tools/health.js.map +0 -1
|
@@ -1,305 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Focus-Aware Resource Loader - v13.4.0
|
|
3
|
-
*
|
|
4
|
-
* Determines if files should be deferred based on focus proximity.
|
|
5
|
-
* Implements the "Moderate" aggressiveness strategy:
|
|
6
|
-
* - In-focus files: Default threshold (500 tokens)
|
|
7
|
-
* - Out-of-focus files: Lower threshold (300 tokens)
|
|
8
|
-
*
|
|
9
|
-
* Focus distance is calculated based on path hierarchy:
|
|
10
|
-
* - Same directory: in-focus
|
|
11
|
-
* - Parent/child directory: near-focus
|
|
12
|
-
* - Different top-level: out-of-focus
|
|
13
|
-
*
|
|
14
|
-
* Usage:
|
|
15
|
-
* ```typescript
|
|
16
|
-
* const config = getFocusAwareDeferConfig('src/mcp/', 'src/core/auth.ts');
|
|
17
|
-
* if (config.shouldDefer(tokenCount)) {
|
|
18
|
-
* // Defer this file
|
|
19
|
-
* }
|
|
20
|
-
* ```
|
|
21
|
-
*/
|
|
22
|
-
import { normalize } from 'path';
|
|
23
|
-
// =============================================================================
|
|
24
|
-
// Constants
|
|
25
|
-
// =============================================================================
|
|
26
|
-
/**
|
|
27
|
-
* Default thresholds for focus-aware deferring
|
|
28
|
-
* "Moderate" aggressiveness - balances context savings with availability
|
|
29
|
-
*/
|
|
30
|
-
export const DEFAULT_FOCUS_DEFER_THRESHOLDS = {
|
|
31
|
-
inFocus: 500, // Same as current default
|
|
32
|
-
nearFocus: 400, // Slightly more aggressive
|
|
33
|
-
outOfFocus: 300, // Most aggressive (user's choice)
|
|
34
|
-
};
|
|
35
|
-
/**
|
|
36
|
-
* Get focus-aware thresholds from environment or defaults
|
|
37
|
-
*/
|
|
38
|
-
export function getFocusDeferThresholds() {
|
|
39
|
-
const inFocus = parseInt(process.env.DINO_DEFER_IN_FOCUS || '', 10);
|
|
40
|
-
const nearFocus = parseInt(process.env.DINO_DEFER_NEAR_FOCUS || '', 10);
|
|
41
|
-
const outOfFocus = parseInt(process.env.DINO_DEFER_OUT_FOCUS || '', 10);
|
|
42
|
-
return {
|
|
43
|
-
inFocus: !isNaN(inFocus) && inFocus > 0 ? inFocus : DEFAULT_FOCUS_DEFER_THRESHOLDS.inFocus,
|
|
44
|
-
nearFocus: !isNaN(nearFocus) && nearFocus > 0 ? nearFocus : DEFAULT_FOCUS_DEFER_THRESHOLDS.nearFocus,
|
|
45
|
-
outOfFocus: !isNaN(outOfFocus) && outOfFocus > 0 ? outOfFocus : DEFAULT_FOCUS_DEFER_THRESHOLDS.outOfFocus,
|
|
46
|
-
};
|
|
47
|
-
}
|
|
48
|
-
// =============================================================================
|
|
49
|
-
// Focus Proximity Calculation
|
|
50
|
-
// =============================================================================
|
|
51
|
-
/**
|
|
52
|
-
* Normalize a path for comparison (handles both / and \)
|
|
53
|
-
*/
|
|
54
|
-
function normalizePath(path) {
|
|
55
|
-
return normalize(path).replace(/\\/g, '/').toLowerCase();
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Get path segments for comparison
|
|
59
|
-
*/
|
|
60
|
-
function getPathSegments(path) {
|
|
61
|
-
const normalized = normalizePath(path);
|
|
62
|
-
// Remove leading slash and trailing slash, then split
|
|
63
|
-
return normalized.replace(/^\/+|\/+$/g, '').split('/').filter(Boolean);
|
|
64
|
-
}
|
|
65
|
-
/**
|
|
66
|
-
* Calculate focus proximity between focus area and file path
|
|
67
|
-
*
|
|
68
|
-
* Proximity levels:
|
|
69
|
-
* - in-focus: File is within the focus directory or focus directory is parent
|
|
70
|
-
* - near-focus: File shares a common ancestor (1-2 levels up)
|
|
71
|
-
* - out-of-focus: File is in a completely different area
|
|
72
|
-
*
|
|
73
|
-
* @param focusArea - Current focus area (e.g., 'src/mcp/')
|
|
74
|
-
* @param filePath - File path to evaluate (e.g., 'src/core/auth.ts')
|
|
75
|
-
* @returns Focus proximity level
|
|
76
|
-
*/
|
|
77
|
-
export function calculateFocusProximity(focusArea, filePath) {
|
|
78
|
-
// If no focus area, everything is out-of-focus
|
|
79
|
-
if (!focusArea || focusArea.trim() === '') {
|
|
80
|
-
return 'out-of-focus';
|
|
81
|
-
}
|
|
82
|
-
const focusSegments = getPathSegments(focusArea);
|
|
83
|
-
const fileSegments = getPathSegments(filePath);
|
|
84
|
-
// Empty segments means root or invalid path
|
|
85
|
-
if (focusSegments.length === 0 || fileSegments.length === 0) {
|
|
86
|
-
return 'out-of-focus';
|
|
87
|
-
}
|
|
88
|
-
// Check if file is within focus directory
|
|
89
|
-
// e.g., focus='src/mcp/', file='src/mcp/server.ts' -> in-focus
|
|
90
|
-
if (fileSegments.length >= focusSegments.length) {
|
|
91
|
-
let matchCount = 0;
|
|
92
|
-
for (let i = 0; i < focusSegments.length; i++) {
|
|
93
|
-
if (focusSegments[i] === fileSegments[i]) {
|
|
94
|
-
matchCount++;
|
|
95
|
-
}
|
|
96
|
-
else {
|
|
97
|
-
break;
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
// All focus segments match -> file is in or under focus
|
|
101
|
-
if (matchCount === focusSegments.length) {
|
|
102
|
-
return 'in-focus';
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
// Check if focus is within file's directory (parent-child)
|
|
106
|
-
// e.g., focus='src/mcp/tools/', file='src/mcp/' -> in-focus (file is parent)
|
|
107
|
-
if (focusSegments.length > fileSegments.length) {
|
|
108
|
-
let matchCount = 0;
|
|
109
|
-
for (let i = 0; i < fileSegments.length; i++) {
|
|
110
|
-
if (focusSegments[i] === fileSegments[i]) {
|
|
111
|
-
matchCount++;
|
|
112
|
-
}
|
|
113
|
-
else {
|
|
114
|
-
break;
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
|
-
// All file segments match -> focus is under file
|
|
118
|
-
if (matchCount === fileSegments.length) {
|
|
119
|
-
return 'in-focus';
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
// Check for common ancestor (near-focus)
|
|
123
|
-
// Count matching segments from the start
|
|
124
|
-
let commonSegments = 0;
|
|
125
|
-
const minLength = Math.min(focusSegments.length, fileSegments.length);
|
|
126
|
-
for (let i = 0; i < minLength; i++) {
|
|
127
|
-
if (focusSegments[i] === fileSegments[i]) {
|
|
128
|
-
commonSegments++;
|
|
129
|
-
}
|
|
130
|
-
else {
|
|
131
|
-
break;
|
|
132
|
-
}
|
|
133
|
-
}
|
|
134
|
-
// Share at least 1-2 segments -> near-focus
|
|
135
|
-
// e.g., focus='src/mcp/', file='src/core/' -> common='src/' -> near-focus
|
|
136
|
-
if (commonSegments >= 1) {
|
|
137
|
-
// If they share the first 2+ segments, it's near-focus
|
|
138
|
-
// e.g., src/core/ and src/mcp/ share 'src' -> near-focus
|
|
139
|
-
if (commonSegments >= 2) {
|
|
140
|
-
return 'near-focus';
|
|
141
|
-
}
|
|
142
|
-
// If they only share 1 segment but it's a meaningful one (not 'src'), near-focus
|
|
143
|
-
// Otherwise, if only sharing 'src' or similar, check depth
|
|
144
|
-
const sharedSegment = focusSegments[0];
|
|
145
|
-
if (sharedSegment !== 'src' && sharedSegment !== 'lib' && sharedSegment !== '.') {
|
|
146
|
-
return 'near-focus';
|
|
147
|
-
}
|
|
148
|
-
// They share 'src' or similar - consider near-focus only if depths are similar
|
|
149
|
-
const depthDiff = Math.abs(focusSegments.length - fileSegments.length);
|
|
150
|
-
if (depthDiff <= 1) {
|
|
151
|
-
return 'near-focus';
|
|
152
|
-
}
|
|
153
|
-
}
|
|
154
|
-
// No significant common ancestor -> out-of-focus
|
|
155
|
-
return 'out-of-focus';
|
|
156
|
-
}
|
|
157
|
-
/**
|
|
158
|
-
* Get the token threshold for a given proximity level
|
|
159
|
-
*/
|
|
160
|
-
export function getThresholdForProximity(proximity, thresholds) {
|
|
161
|
-
const t = thresholds || getFocusDeferThresholds();
|
|
162
|
-
switch (proximity) {
|
|
163
|
-
case 'in-focus':
|
|
164
|
-
return t.inFocus;
|
|
165
|
-
case 'near-focus':
|
|
166
|
-
return t.nearFocus;
|
|
167
|
-
case 'out-of-focus':
|
|
168
|
-
return t.outOfFocus;
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
// =============================================================================
|
|
172
|
-
// Focus-Aware Defer Configuration
|
|
173
|
-
// =============================================================================
|
|
174
|
-
/**
|
|
175
|
-
* Get focus-aware defer configuration for a file
|
|
176
|
-
*
|
|
177
|
-
* @param focusArea - Current focus area (e.g., 'src/mcp/')
|
|
178
|
-
* @param filePath - File path to evaluate
|
|
179
|
-
* @param thresholds - Optional custom thresholds
|
|
180
|
-
* @returns Configuration object with threshold and shouldDefer function
|
|
181
|
-
*/
|
|
182
|
-
export function getFocusAwareDeferConfig(focusArea, filePath, thresholds) {
|
|
183
|
-
const proximity = calculateFocusProximity(focusArea, filePath);
|
|
184
|
-
const threshold = getThresholdForProximity(proximity, thresholds);
|
|
185
|
-
return {
|
|
186
|
-
focusArea,
|
|
187
|
-
filePath,
|
|
188
|
-
proximity,
|
|
189
|
-
threshold,
|
|
190
|
-
shouldDefer: (tokenCount) => tokenCount > threshold,
|
|
191
|
-
};
|
|
192
|
-
}
|
|
193
|
-
/**
|
|
194
|
-
* Check if focus-aware deferring is enabled
|
|
195
|
-
* Default: enabled (true)
|
|
196
|
-
* Opt-out: DINO_FOCUS_DEFER=false
|
|
197
|
-
*/
|
|
198
|
-
export function isFocusAwareDeferEnabled() {
|
|
199
|
-
return process.env.DINO_FOCUS_DEFER !== 'false';
|
|
200
|
-
}
|
|
201
|
-
// =============================================================================
|
|
202
|
-
// Batch Processing
|
|
203
|
-
// =============================================================================
|
|
204
|
-
/**
|
|
205
|
-
* Classify multiple files by focus proximity
|
|
206
|
-
*
|
|
207
|
-
* @param focusArea - Current focus area
|
|
208
|
-
* @param files - Array of file paths
|
|
209
|
-
* @returns Files grouped by proximity
|
|
210
|
-
*/
|
|
211
|
-
export function classifyFilesByFocus(focusArea, files) {
|
|
212
|
-
const result = {
|
|
213
|
-
inFocus: [],
|
|
214
|
-
nearFocus: [],
|
|
215
|
-
outOfFocus: [],
|
|
216
|
-
};
|
|
217
|
-
for (const file of files) {
|
|
218
|
-
const proximity = calculateFocusProximity(focusArea, file);
|
|
219
|
-
result[proximity === 'in-focus' ? 'inFocus' :
|
|
220
|
-
proximity === 'near-focus' ? 'nearFocus' : 'outOfFocus'].push(file);
|
|
221
|
-
}
|
|
222
|
-
return result;
|
|
223
|
-
}
|
|
224
|
-
/**
|
|
225
|
-
* Get defer recommendations for a batch of files
|
|
226
|
-
*
|
|
227
|
-
* @param focusArea - Current focus area
|
|
228
|
-
* @param files - Array of file paths with token estimates
|
|
229
|
-
* @returns Files that should be deferred
|
|
230
|
-
*/
|
|
231
|
-
export function getDeferRecommendations(focusArea, files) {
|
|
232
|
-
const shouldDefer = [];
|
|
233
|
-
const shouldLoad = [];
|
|
234
|
-
let tokensSaved = 0;
|
|
235
|
-
let tokensLoaded = 0;
|
|
236
|
-
for (const file of files) {
|
|
237
|
-
const config = getFocusAwareDeferConfig(focusArea, file.path);
|
|
238
|
-
if (config.shouldDefer(file.tokenEstimate)) {
|
|
239
|
-
shouldDefer.push({
|
|
240
|
-
path: file.path,
|
|
241
|
-
tokenEstimate: file.tokenEstimate,
|
|
242
|
-
proximity: config.proximity,
|
|
243
|
-
threshold: config.threshold,
|
|
244
|
-
});
|
|
245
|
-
tokensSaved += file.tokenEstimate;
|
|
246
|
-
}
|
|
247
|
-
else {
|
|
248
|
-
shouldLoad.push({
|
|
249
|
-
path: file.path,
|
|
250
|
-
tokenEstimate: file.tokenEstimate,
|
|
251
|
-
proximity: config.proximity,
|
|
252
|
-
});
|
|
253
|
-
tokensLoaded += file.tokenEstimate;
|
|
254
|
-
}
|
|
255
|
-
}
|
|
256
|
-
return {
|
|
257
|
-
shouldDefer,
|
|
258
|
-
shouldLoad,
|
|
259
|
-
stats: {
|
|
260
|
-
totalFiles: files.length,
|
|
261
|
-
deferredCount: shouldDefer.length,
|
|
262
|
-
loadedCount: shouldLoad.length,
|
|
263
|
-
tokensSaved,
|
|
264
|
-
tokensLoaded,
|
|
265
|
-
},
|
|
266
|
-
};
|
|
267
|
-
}
|
|
268
|
-
// =============================================================================
|
|
269
|
-
// Formatting
|
|
270
|
-
// =============================================================================
|
|
271
|
-
/**
|
|
272
|
-
* Format focus-aware defer stats for display
|
|
273
|
-
*/
|
|
274
|
-
export function formatFocusDeferStats(stats, focusArea) {
|
|
275
|
-
const lines = [
|
|
276
|
-
'## Focus-Aware Deferring',
|
|
277
|
-
'',
|
|
278
|
-
focusArea ? `**Focus:** ${focusArea}` : '**Focus:** none',
|
|
279
|
-
`**Total Files:** ${stats.totalFiles}`,
|
|
280
|
-
`**Deferred:** ${stats.deferredCount} (~${stats.tokensSaved} tokens saved)`,
|
|
281
|
-
`**Loaded:** ${stats.loadedCount} (~${stats.tokensLoaded} tokens)`,
|
|
282
|
-
];
|
|
283
|
-
if (stats.totalFiles > 0) {
|
|
284
|
-
const deferPercent = Math.round((stats.deferredCount / stats.totalFiles) * 100);
|
|
285
|
-
lines.push(`**Defer Rate:** ${deferPercent}%`);
|
|
286
|
-
}
|
|
287
|
-
return lines.join('\n');
|
|
288
|
-
}
|
|
289
|
-
/**
|
|
290
|
-
* Format threshold information for display
|
|
291
|
-
*/
|
|
292
|
-
export function formatThresholdInfo(thresholds) {
|
|
293
|
-
const t = thresholds || getFocusDeferThresholds();
|
|
294
|
-
const lines = [
|
|
295
|
-
'## Focus-Aware Thresholds',
|
|
296
|
-
'',
|
|
297
|
-
`- **In-focus:** ${t.inFocus} tokens`,
|
|
298
|
-
`- **Near-focus:** ${t.nearFocus} tokens`,
|
|
299
|
-
`- **Out-of-focus:** ${t.outOfFocus} tokens`,
|
|
300
|
-
'',
|
|
301
|
-
'Files exceeding their threshold are deferred for lazy loading.',
|
|
302
|
-
];
|
|
303
|
-
return lines.join('\n');
|
|
304
|
-
}
|
|
305
|
-
//# sourceMappingURL=focus-resource-loader.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"focus-resource-loader.js","sourceRoot":"","sources":["../../../src/core/context/focus-resource-loader.ts"],"names":[],"mappings":"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"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"repl-environment.test.d.ts","sourceRoot":"","sources":["../../../../src/core/context-repl/__tests__/repl-environment.test.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|
|
@@ -1,335 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* REPL Environment Tests - v13.6.0
|
|
3
|
-
*
|
|
4
|
-
* Tests for the RLM-style REPL environment.
|
|
5
|
-
*/
|
|
6
|
-
import { describe, it, expect, beforeEach, afterEach, vi } from 'vitest';
|
|
7
|
-
import { REPLEnvironment, createREPLEnvironment, formatREPLResult, formatREPLState } from '../repl-environment.js';
|
|
8
|
-
import * as featureFlags from '../../config/feature-flags.js';
|
|
9
|
-
import * as fs from 'fs/promises';
|
|
10
|
-
import * as path from 'path';
|
|
11
|
-
import { tmpdir } from 'os';
|
|
12
|
-
// Mock feature flags
|
|
13
|
-
vi.mock('../../config/feature-flags.js', () => ({
|
|
14
|
-
isFeatureEnabled: vi.fn(),
|
|
15
|
-
}));
|
|
16
|
-
describe('REPLEnvironment', () => {
|
|
17
|
-
let tempDir;
|
|
18
|
-
let repl;
|
|
19
|
-
beforeEach(async () => {
|
|
20
|
-
// Create temp directory
|
|
21
|
-
tempDir = path.join(tmpdir(), `repl-test-${Date.now()}`);
|
|
22
|
-
await fs.mkdir(tempDir, { recursive: true });
|
|
23
|
-
// Create .dino directory with session.md
|
|
24
|
-
const dinoDir = path.join(tempDir, '.dino');
|
|
25
|
-
await fs.mkdir(dinoDir, { recursive: true });
|
|
26
|
-
await fs.writeFile(path.join(dinoDir, 'session.md'), '# Session\n\n## Focus\n**src/core/** (high confidence, test)\n\n## Phase\n**implementation**');
|
|
27
|
-
await fs.writeFile(path.join(dinoDir, 'config.json'), JSON.stringify({ version: '13.6.0', features: { enableReplEnvironment: true } }));
|
|
28
|
-
// Enable REPL by default
|
|
29
|
-
vi.mocked(featureFlags.isFeatureEnabled).mockResolvedValue(true);
|
|
30
|
-
repl = new REPLEnvironment({ projectDir: tempDir, maxTokens: 2000 });
|
|
31
|
-
});
|
|
32
|
-
afterEach(async () => {
|
|
33
|
-
try {
|
|
34
|
-
await fs.rm(tempDir, { recursive: true, force: true });
|
|
35
|
-
}
|
|
36
|
-
catch {
|
|
37
|
-
// Ignore cleanup errors
|
|
38
|
-
}
|
|
39
|
-
vi.clearAllMocks();
|
|
40
|
-
});
|
|
41
|
-
describe('constructor', () => {
|
|
42
|
-
it('should create REPL with default options', () => {
|
|
43
|
-
const defaultRepl = new REPLEnvironment();
|
|
44
|
-
const state = defaultRepl.getState();
|
|
45
|
-
expect(state.variables.size).toBe(0);
|
|
46
|
-
expect(state.totalTokens).toBe(0);
|
|
47
|
-
expect(state.operationCount).toBe(0);
|
|
48
|
-
});
|
|
49
|
-
it('should create REPL with custom options', () => {
|
|
50
|
-
const customRepl = new REPLEnvironment({ projectDir: tempDir, maxTokens: 5000 });
|
|
51
|
-
const state = customRepl.getState();
|
|
52
|
-
expect(state.variables.size).toBe(0);
|
|
53
|
-
});
|
|
54
|
-
});
|
|
55
|
-
describe('createREPLEnvironment', () => {
|
|
56
|
-
it('should create REPL via factory function', () => {
|
|
57
|
-
const factoryRepl = createREPLEnvironment({ projectDir: tempDir });
|
|
58
|
-
expect(factoryRepl).toBeDefined();
|
|
59
|
-
expect(factoryRepl.getState).toBeDefined();
|
|
60
|
-
});
|
|
61
|
-
});
|
|
62
|
-
describe('getState', () => {
|
|
63
|
-
it('should return current state', () => {
|
|
64
|
-
const state = repl.getState();
|
|
65
|
-
expect(state).toHaveProperty('variables');
|
|
66
|
-
expect(state).toHaveProperty('totalTokens');
|
|
67
|
-
expect(state).toHaveProperty('startedAt');
|
|
68
|
-
expect(state).toHaveProperty('operationCount');
|
|
69
|
-
expect(state).toHaveProperty('history');
|
|
70
|
-
});
|
|
71
|
-
});
|
|
72
|
-
describe('clearState', () => {
|
|
73
|
-
it('should reset state to initial values', async () => {
|
|
74
|
-
// Perform some operations first
|
|
75
|
-
await repl.peek();
|
|
76
|
-
// Clear state
|
|
77
|
-
repl.clearState();
|
|
78
|
-
const state = repl.getState();
|
|
79
|
-
expect(state.variables.size).toBe(0);
|
|
80
|
-
expect(state.totalTokens).toBe(0);
|
|
81
|
-
// operationCount includes the clear operation history
|
|
82
|
-
expect(state.history.length).toBe(1);
|
|
83
|
-
expect(state.history[0].operation).toBe('clear');
|
|
84
|
-
});
|
|
85
|
-
});
|
|
86
|
-
describe('execute', () => {
|
|
87
|
-
it('should return error when REPL is disabled', async () => {
|
|
88
|
-
vi.mocked(featureFlags.isFeatureEnabled).mockResolvedValue(false);
|
|
89
|
-
const result = await repl.execute('get decisions');
|
|
90
|
-
expect(result.success).toBe(false);
|
|
91
|
-
expect(result.error).toContain('REPL environment is disabled');
|
|
92
|
-
});
|
|
93
|
-
it('should return error for invalid query', async () => {
|
|
94
|
-
const result = await repl.execute('this is not a valid query format!!!');
|
|
95
|
-
// Query parser may handle this gracefully or error
|
|
96
|
-
// Check that we get a result either way
|
|
97
|
-
expect(result).toHaveProperty('success');
|
|
98
|
-
expect(result).toHaveProperty('executionTimeMs');
|
|
99
|
-
});
|
|
100
|
-
it('should increment operation count', async () => {
|
|
101
|
-
const stateBefore = repl.getState();
|
|
102
|
-
await repl.execute('get decisions');
|
|
103
|
-
const stateAfter = repl.getState();
|
|
104
|
-
expect(stateAfter.operationCount).toBe(stateBefore.operationCount + 1);
|
|
105
|
-
});
|
|
106
|
-
it('should add to history', async () => {
|
|
107
|
-
await repl.execute('get decisions');
|
|
108
|
-
const state = repl.getState();
|
|
109
|
-
expect(state.history.length).toBeGreaterThan(0);
|
|
110
|
-
expect(state.history[state.history.length - 1].operation).toBe('execute');
|
|
111
|
-
});
|
|
112
|
-
});
|
|
113
|
-
describe('peek', () => {
|
|
114
|
-
it('should return error when REPL is disabled', async () => {
|
|
115
|
-
vi.mocked(featureFlags.isFeatureEnabled).mockResolvedValue(false);
|
|
116
|
-
const result = await repl.peek();
|
|
117
|
-
expect(result.success).toBe(false);
|
|
118
|
-
expect(result.error).toContain('REPL environment is disabled');
|
|
119
|
-
});
|
|
120
|
-
it('should return metadata without loading full content', async () => {
|
|
121
|
-
const result = await repl.peek();
|
|
122
|
-
expect(result.success).toBe(true);
|
|
123
|
-
expect(result.tokensUsed).toBe(0); // Peek is free
|
|
124
|
-
const metadata = result.data;
|
|
125
|
-
expect(metadata).toHaveProperty('totalFiles');
|
|
126
|
-
expect(metadata).toHaveProperty('totalTokens');
|
|
127
|
-
expect(metadata).toHaveProperty('categories');
|
|
128
|
-
expect(metadata).toHaveProperty('samples');
|
|
129
|
-
});
|
|
130
|
-
it('should support filtering by pattern', async () => {
|
|
131
|
-
// Create some test files
|
|
132
|
-
await fs.writeFile(path.join(tempDir, '.dino', 'test.json'), '{}');
|
|
133
|
-
await fs.writeFile(path.join(tempDir, '.dino', 'test.md'), '# Test');
|
|
134
|
-
const resultAll = await repl.peek();
|
|
135
|
-
const resultJson = await repl.peek('*.json');
|
|
136
|
-
expect(resultAll.success).toBe(true);
|
|
137
|
-
expect(resultJson.success).toBe(true);
|
|
138
|
-
const metaAll = resultAll.data;
|
|
139
|
-
const metaJson = resultJson.data;
|
|
140
|
-
// JSON filter should have equal or fewer files
|
|
141
|
-
expect(metaJson.totalFiles).toBeLessThanOrEqual(metaAll.totalFiles);
|
|
142
|
-
});
|
|
143
|
-
it('should add to history', async () => {
|
|
144
|
-
await repl.peek();
|
|
145
|
-
const state = repl.getState();
|
|
146
|
-
const lastEntry = state.history[state.history.length - 1];
|
|
147
|
-
expect(lastEntry.operation).toBe('peek');
|
|
148
|
-
expect(lastEntry.success).toBe(true);
|
|
149
|
-
});
|
|
150
|
-
});
|
|
151
|
-
describe('grep', () => {
|
|
152
|
-
beforeEach(async () => {
|
|
153
|
-
// Create test files with searchable content
|
|
154
|
-
await fs.writeFile(path.join(tempDir, '.dino', 'search-test.md'), '# Test File\n\nThis contains a searchable pattern.\nAnother line with pattern here.');
|
|
155
|
-
});
|
|
156
|
-
it('should return error when REPL is disabled', async () => {
|
|
157
|
-
vi.mocked(featureFlags.isFeatureEnabled).mockResolvedValue(false);
|
|
158
|
-
const result = await repl.grep('pattern');
|
|
159
|
-
expect(result.success).toBe(false);
|
|
160
|
-
expect(result.error).toContain('REPL environment is disabled');
|
|
161
|
-
});
|
|
162
|
-
it('should find matches in files', async () => {
|
|
163
|
-
const result = await repl.grep('pattern');
|
|
164
|
-
expect(result.success).toBe(true);
|
|
165
|
-
const grepResult = result.data;
|
|
166
|
-
expect(grepResult.pattern).toBe('pattern');
|
|
167
|
-
expect(grepResult.totalMatches).toBeGreaterThan(0);
|
|
168
|
-
expect(grepResult.filesWithMatches).toBeGreaterThan(0);
|
|
169
|
-
});
|
|
170
|
-
it('should support case insensitive search', async () => {
|
|
171
|
-
const resultSensitive = await repl.grep('Pattern', { ignoreCase: false });
|
|
172
|
-
const resultInsensitive = await repl.grep('Pattern', { ignoreCase: true });
|
|
173
|
-
expect(resultSensitive.success).toBe(true);
|
|
174
|
-
expect(resultInsensitive.success).toBe(true);
|
|
175
|
-
const sensitive = resultSensitive.data;
|
|
176
|
-
const insensitive = resultInsensitive.data;
|
|
177
|
-
// Case insensitive should find equal or more matches
|
|
178
|
-
expect(insensitive.totalMatches).toBeGreaterThanOrEqual(sensitive.totalMatches);
|
|
179
|
-
});
|
|
180
|
-
it('should respect maxMatches limit', async () => {
|
|
181
|
-
const result = await repl.grep('pattern', { maxMatches: 1 });
|
|
182
|
-
expect(result.success).toBe(true);
|
|
183
|
-
const grepResult = result.data;
|
|
184
|
-
expect(grepResult.matches.length).toBeLessThanOrEqual(1);
|
|
185
|
-
});
|
|
186
|
-
it('should support filesOnly option', async () => {
|
|
187
|
-
const result = await repl.grep('pattern', { filesOnly: true });
|
|
188
|
-
expect(result.success).toBe(true);
|
|
189
|
-
const grepResult = result.data;
|
|
190
|
-
expect(grepResult.matches).toEqual([]); // No detailed matches when filesOnly
|
|
191
|
-
expect(grepResult.filesWithMatches).toBeGreaterThan(0);
|
|
192
|
-
});
|
|
193
|
-
});
|
|
194
|
-
describe('partition', () => {
|
|
195
|
-
beforeEach(async () => {
|
|
196
|
-
// Create src directory with some files
|
|
197
|
-
const srcDir = path.join(tempDir, 'src');
|
|
198
|
-
await fs.mkdir(srcDir, { recursive: true });
|
|
199
|
-
await fs.writeFile(path.join(srcDir, 'index.ts'), 'export {};');
|
|
200
|
-
await fs.writeFile(path.join(srcDir, 'utils.ts'), 'export const x = 1;');
|
|
201
|
-
});
|
|
202
|
-
it('should return error when REPL is disabled', async () => {
|
|
203
|
-
vi.mocked(featureFlags.isFeatureEnabled).mockResolvedValue(false);
|
|
204
|
-
const result = await repl.partition('by-directory');
|
|
205
|
-
expect(result.success).toBe(false);
|
|
206
|
-
expect(result.error).toContain('REPL environment is disabled');
|
|
207
|
-
});
|
|
208
|
-
it('should return error for invalid strategy', async () => {
|
|
209
|
-
const result = await repl.partition('invalid-strategy');
|
|
210
|
-
expect(result.success).toBe(false);
|
|
211
|
-
expect(result.error).toContain('Invalid strategy');
|
|
212
|
-
});
|
|
213
|
-
it('should partition files by directory', async () => {
|
|
214
|
-
const result = await repl.partition('by-directory');
|
|
215
|
-
expect(result.success).toBe(true);
|
|
216
|
-
expect(result.data).toHaveProperty('partitions');
|
|
217
|
-
});
|
|
218
|
-
it('should support all valid strategies', async () => {
|
|
219
|
-
const strategies = ['by-directory', 'by-feature', 'by-dependency', 'by-time'];
|
|
220
|
-
for (const strategy of strategies) {
|
|
221
|
-
const result = await repl.partition(strategy);
|
|
222
|
-
expect(result).toHaveProperty('success');
|
|
223
|
-
}
|
|
224
|
-
});
|
|
225
|
-
});
|
|
226
|
-
describe('loadVariable', () => {
|
|
227
|
-
it('should return error when REPL is disabled', async () => {
|
|
228
|
-
vi.mocked(featureFlags.isFeatureEnabled).mockResolvedValue(false);
|
|
229
|
-
const result = await repl.loadVariable('memories.decisions');
|
|
230
|
-
expect(result.success).toBe(false);
|
|
231
|
-
expect(result.error).toContain('REPL environment is disabled');
|
|
232
|
-
});
|
|
233
|
-
it('should return error for invalid variable path', async () => {
|
|
234
|
-
const result = await repl.loadVariable('invalid.path');
|
|
235
|
-
expect(result.success).toBe(false);
|
|
236
|
-
expect(result.error).toContain('Invalid variable path');
|
|
237
|
-
});
|
|
238
|
-
it('should load valid variable', async () => {
|
|
239
|
-
// Create memories file
|
|
240
|
-
const memoriesDir = path.join(tempDir, '.dino', 'memories');
|
|
241
|
-
await fs.mkdir(memoriesDir, { recursive: true });
|
|
242
|
-
await fs.writeFile(path.join(memoriesDir, 'decisions.json'), JSON.stringify([{ id: 'D-001', topic: 'test', choice: 'option1' }]));
|
|
243
|
-
const result = await repl.loadVariable('memories.decisions');
|
|
244
|
-
// May or may not succeed depending on how loadContextVariable handles the path
|
|
245
|
-
expect(result).toHaveProperty('success');
|
|
246
|
-
expect(result).toHaveProperty('tokensUsed');
|
|
247
|
-
});
|
|
248
|
-
it('should support custom variable name', async () => {
|
|
249
|
-
const result = await repl.loadVariable('session.focus', 'myFocus');
|
|
250
|
-
expect(result).toHaveProperty('success');
|
|
251
|
-
if (result.success && result.data) {
|
|
252
|
-
expect(result.data.name).toBe('myFocus');
|
|
253
|
-
}
|
|
254
|
-
});
|
|
255
|
-
});
|
|
256
|
-
describe('unloadVariable', () => {
|
|
257
|
-
it('should return false for non-existent variable', () => {
|
|
258
|
-
const result = repl.unloadVariable('nonexistent');
|
|
259
|
-
expect(result).toBe(false);
|
|
260
|
-
});
|
|
261
|
-
});
|
|
262
|
-
describe('formatting', () => {
|
|
263
|
-
describe('formatREPLResult', () => {
|
|
264
|
-
it('should format successful result', () => {
|
|
265
|
-
const result = {
|
|
266
|
-
success: true,
|
|
267
|
-
data: { key: 'value' },
|
|
268
|
-
tokensUsed: 10,
|
|
269
|
-
executionTimeMs: 5,
|
|
270
|
-
};
|
|
271
|
-
const formatted = formatREPLResult(result);
|
|
272
|
-
expect(formatted).toContain('## REPL Result');
|
|
273
|
-
expect(formatted).toContain('**Success:** Yes');
|
|
274
|
-
expect(formatted).toContain('**Tokens Used:** ~10');
|
|
275
|
-
expect(formatted).toContain('key');
|
|
276
|
-
});
|
|
277
|
-
it('should format error result', () => {
|
|
278
|
-
const result = {
|
|
279
|
-
success: false,
|
|
280
|
-
data: null,
|
|
281
|
-
tokensUsed: 0,
|
|
282
|
-
executionTimeMs: 1,
|
|
283
|
-
error: 'Something went wrong',
|
|
284
|
-
};
|
|
285
|
-
const formatted = formatREPLResult(result);
|
|
286
|
-
expect(formatted).toContain('## REPL Error');
|
|
287
|
-
expect(formatted).toContain('Something went wrong');
|
|
288
|
-
});
|
|
289
|
-
it('should include warnings', () => {
|
|
290
|
-
const result = {
|
|
291
|
-
success: true,
|
|
292
|
-
data: [],
|
|
293
|
-
tokensUsed: 100,
|
|
294
|
-
executionTimeMs: 10,
|
|
295
|
-
warnings: ['Result truncated'],
|
|
296
|
-
};
|
|
297
|
-
const formatted = formatREPLResult(result);
|
|
298
|
-
expect(formatted).toContain('### Warnings');
|
|
299
|
-
expect(formatted).toContain('Result truncated');
|
|
300
|
-
});
|
|
301
|
-
});
|
|
302
|
-
describe('formatREPLState', () => {
|
|
303
|
-
it('should format state summary', () => {
|
|
304
|
-
const state = repl.getState();
|
|
305
|
-
const formatted = formatREPLState(state);
|
|
306
|
-
expect(formatted).toContain('## REPL State');
|
|
307
|
-
expect(formatted).toContain('**Variables Loaded:**');
|
|
308
|
-
expect(formatted).toContain('**Total Tokens:**');
|
|
309
|
-
});
|
|
310
|
-
});
|
|
311
|
-
});
|
|
312
|
-
describe('history tracking', () => {
|
|
313
|
-
it('should track all operations', async () => {
|
|
314
|
-
await repl.peek();
|
|
315
|
-
await repl.grep('test');
|
|
316
|
-
await repl.execute('get decisions');
|
|
317
|
-
const state = repl.getState();
|
|
318
|
-
expect(state.history.length).toBe(3);
|
|
319
|
-
});
|
|
320
|
-
it('should limit history to 100 entries', async () => {
|
|
321
|
-
// This would require many operations, skip for efficiency
|
|
322
|
-
// Just verify the logic exists in the code
|
|
323
|
-
const state = repl.getState();
|
|
324
|
-
expect(state.history.length).toBeLessThanOrEqual(100);
|
|
325
|
-
});
|
|
326
|
-
it('should track tokens before and after', async () => {
|
|
327
|
-
await repl.peek();
|
|
328
|
-
const state = repl.getState();
|
|
329
|
-
const lastEntry = state.history[state.history.length - 1];
|
|
330
|
-
expect(lastEntry).toHaveProperty('tokensBefore');
|
|
331
|
-
expect(lastEntry).toHaveProperty('tokensAfter');
|
|
332
|
-
});
|
|
333
|
-
});
|
|
334
|
-
});
|
|
335
|
-
//# sourceMappingURL=repl-environment.test.js.map
|