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.
Files changed (133) hide show
  1. package/README.md +5 -82
  2. package/bin/dino-hud.js +1 -1
  3. package/bin/dino.js +1 -1
  4. package/dist/commands/hud.d.ts +8 -1
  5. package/dist/commands/hud.d.ts.map +1 -1
  6. package/dist/commands/hud.js +18 -4
  7. package/dist/commands/hud.js.map +1 -1
  8. package/dist/core/agents/context-isolation.js +26 -26
  9. package/dist/core/config/feature-flags.d.ts +1 -25
  10. package/dist/core/config/feature-flags.d.ts.map +1 -1
  11. package/dist/core/config/feature-flags.js +1 -7
  12. package/dist/core/config/feature-flags.js.map +1 -1
  13. package/dist/core/context/auto-injection-engine.d.ts +1 -10
  14. package/dist/core/context/auto-injection-engine.d.ts.map +1 -1
  15. package/dist/core/context/auto-injection-engine.js +2 -45
  16. package/dist/core/context/auto-injection-engine.js.map +1 -1
  17. package/dist/core/context/context-health.d.ts +2 -27
  18. package/dist/core/context/context-health.d.ts.map +1 -1
  19. package/dist/core/context/context-health.js +12 -98
  20. package/dist/core/context/context-health.js.map +1 -1
  21. package/dist/core/context/index.d.ts +2 -3
  22. package/dist/core/context/index.d.ts.map +1 -1
  23. package/dist/core/context/index.js +2 -18
  24. package/dist/core/context/index.js.map +1 -1
  25. package/dist/core/context/lazy-loader.d.ts +1 -44
  26. package/dist/core/context/lazy-loader.d.ts.map +1 -1
  27. package/dist/core/context/lazy-loader.js +1 -59
  28. package/dist/core/context/lazy-loader.js.map +1 -1
  29. package/dist/core/context-repl/index.d.ts +3 -8
  30. package/dist/core/context-repl/index.d.ts.map +1 -1
  31. package/dist/core/context-repl/index.js +3 -11
  32. package/dist/core/context-repl/index.js.map +1 -1
  33. package/dist/core/context-repl/types.d.ts +1 -277
  34. package/dist/core/context-repl/types.d.ts.map +1 -1
  35. package/dist/core/context-repl/types.js +1 -52
  36. package/dist/core/context-repl/types.js.map +1 -1
  37. package/dist/core/generator/claude-md.js +1 -1
  38. package/dist/core/provider/storage.d.ts.map +1 -1
  39. package/dist/core/provider/storage.js +2 -1
  40. package/dist/core/provider/storage.js.map +1 -1
  41. package/dist/hooks/post-edit.js +0 -73
  42. package/dist/hooks/post-edit.js.map +1 -1
  43. package/dist/hooks/session-start.js +0 -115
  44. package/dist/hooks/session-start.js.map +1 -1
  45. package/dist/hooks/types.js +1 -1
  46. package/dist/hooks/user-prompt-submit.js +0 -100
  47. package/dist/hooks/user-prompt-submit.js.map +1 -1
  48. package/dist/hud/config-tui.d.ts +25 -0
  49. package/dist/hud/config-tui.d.ts.map +1 -0
  50. package/dist/hud/config-tui.js +199 -0
  51. package/dist/hud/config-tui.js.map +1 -0
  52. package/dist/hud/config.d.ts +28 -3
  53. package/dist/hud/config.d.ts.map +1 -1
  54. package/dist/hud/config.js +60 -8
  55. package/dist/hud/config.js.map +1 -1
  56. package/dist/hud/index.d.ts.map +1 -1
  57. package/dist/hud/index.js +1 -0
  58. package/dist/hud/index.js.map +1 -1
  59. package/dist/hud/models.d.ts +58 -0
  60. package/dist/hud/models.d.ts.map +1 -0
  61. package/dist/hud/models.js +124 -0
  62. package/dist/hud/models.js.map +1 -0
  63. package/dist/hud/render/budget-bar.d.ts +2 -0
  64. package/dist/hud/render/budget-bar.d.ts.map +1 -1
  65. package/dist/hud/render/budget-bar.js +8 -5
  66. package/dist/hud/render/budget-bar.js.map +1 -1
  67. package/dist/hud/stdin.d.ts +3 -0
  68. package/dist/hud/stdin.d.ts.map +1 -1
  69. package/dist/hud/stdin.js +29 -2
  70. package/dist/hud/stdin.js.map +1 -1
  71. package/dist/hud/token-estimator.d.ts +79 -0
  72. package/dist/hud/token-estimator.d.ts.map +1 -0
  73. package/dist/hud/token-estimator.js +126 -0
  74. package/dist/hud/token-estimator.js.map +1 -0
  75. package/dist/hud/types.d.ts +2 -0
  76. package/dist/hud/types.d.ts.map +1 -1
  77. package/dist/mcp/server.d.ts +1 -2
  78. package/dist/mcp/server.d.ts.map +1 -1
  79. package/dist/mcp/server.js +3 -12
  80. package/dist/mcp/server.js.map +1 -1
  81. package/dist/mcp/tool-tiers.d.ts.map +1 -1
  82. package/dist/mcp/tool-tiers.js +0 -5
  83. package/dist/mcp/tool-tiers.js.map +1 -1
  84. package/dist/mcp/tools/index.d.ts +2 -7
  85. package/dist/mcp/tools/index.d.ts.map +1 -1
  86. package/dist/mcp/tools/index.js +2 -25
  87. package/dist/mcp/tools/index.js.map +1 -1
  88. package/dist/mcp-server.d.ts.map +1 -1
  89. package/dist/mcp-server.js +5 -9
  90. package/dist/mcp-server.js.map +1 -1
  91. package/dist/rules/index.js +2 -2
  92. package/dist/utils/exec.js +2 -2
  93. package/dist/utils/exec.js.map +1 -1
  94. package/dist/utils/gitignore.d.ts.map +1 -1
  95. package/dist/utils/gitignore.js +0 -5
  96. package/dist/utils/gitignore.js.map +1 -1
  97. package/package.json +77 -76
  98. package/dist/core/context/focus-resource-loader.d.ts +0 -143
  99. package/dist/core/context/focus-resource-loader.d.ts.map +0 -1
  100. package/dist/core/context/focus-resource-loader.js +0 -305
  101. package/dist/core/context/focus-resource-loader.js.map +0 -1
  102. package/dist/core/context-repl/__tests__/repl-environment.test.d.ts +0 -7
  103. package/dist/core/context-repl/__tests__/repl-environment.test.d.ts.map +0 -1
  104. package/dist/core/context-repl/__tests__/repl-environment.test.js +0 -335
  105. package/dist/core/context-repl/__tests__/repl-environment.test.js.map +0 -1
  106. package/dist/core/context-repl/context-window-monitor.d.ts +0 -181
  107. package/dist/core/context-repl/context-window-monitor.d.ts.map +0 -1
  108. package/dist/core/context-repl/context-window-monitor.js +0 -309
  109. package/dist/core/context-repl/context-window-monitor.js.map +0 -1
  110. package/dist/core/context-repl/repl-environment.d.ts +0 -66
  111. package/dist/core/context-repl/repl-environment.d.ts.map +0 -1
  112. package/dist/core/context-repl/repl-environment.js +0 -795
  113. package/dist/core/context-repl/repl-environment.js.map +0 -1
  114. package/dist/mcp/focus-filter.d.ts +0 -74
  115. package/dist/mcp/focus-filter.d.ts.map +0 -1
  116. package/dist/mcp/focus-filter.js +0 -229
  117. package/dist/mcp/focus-filter.js.map +0 -1
  118. package/dist/mcp/tools/auto-inject.d.ts +0 -36
  119. package/dist/mcp/tools/auto-inject.d.ts.map +0 -1
  120. package/dist/mcp/tools/auto-inject.js +0 -143
  121. package/dist/mcp/tools/auto-inject.js.map +0 -1
  122. package/dist/mcp/tools/auto-unload.d.ts +0 -29
  123. package/dist/mcp/tools/auto-unload.d.ts.map +0 -1
  124. package/dist/mcp/tools/auto-unload.js +0 -151
  125. package/dist/mcp/tools/auto-unload.js.map +0 -1
  126. package/dist/mcp/tools/context-repl.d.ts +0 -48
  127. package/dist/mcp/tools/context-repl.d.ts.map +0 -1
  128. package/dist/mcp/tools/context-repl.js +0 -290
  129. package/dist/mcp/tools/context-repl.js.map +0 -1
  130. package/dist/mcp/tools/health.d.ts +0 -29
  131. package/dist/mcp/tools/health.d.ts.map +0 -1
  132. package/dist/mcp/tools/health.js +0 -171
  133. 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,7 +0,0 @@
1
- /**
2
- * REPL Environment Tests - v13.6.0
3
- *
4
- * Tests for the RLM-style REPL environment.
5
- */
6
- export {};
7
- //# sourceMappingURL=repl-environment.test.d.ts.map
@@ -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