smart-context-mcp 1.3.1 → 1.4.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 CHANGED
@@ -42,6 +42,62 @@ npx smart-context-init --target .
42
42
  ```
43
43
  Restart your AI client. Done.
44
44
 
45
+ ---
46
+
47
+ ## šŸš€ How to Invoke the MCP
48
+
49
+ The MCP doesn't intercept prompts automatically. **You need to tell the agent to use it.**
50
+
51
+ ### Option 1: Use MCP Prompts (Easiest)
52
+
53
+ In Cursor, type in the chat:
54
+
55
+ ```
56
+ /prompt use-devctx
57
+
58
+ [Your task here]
59
+ ```
60
+
61
+ **Available prompts:**
62
+ - `/prompt use-devctx` - Force devctx tools for current task
63
+ - `/prompt devctx-workflow` - Full workflow (start → context → work → end)
64
+ - `/prompt devctx-preflight` - Preflight only (build_index + smart_turn start)
65
+
66
+ ### Option 2: Explicit Instruction
67
+
68
+ Just tell the agent directly:
69
+
70
+ ```
71
+ Use smart_turn(start) to recover context, then [your task]
72
+ ```
73
+
74
+ Or:
75
+
76
+ ```
77
+ Use the MCP to review this code
78
+ ```
79
+
80
+ ### Option 3: Automatic (via Rules)
81
+
82
+ The agent *should* use devctx automatically for complex tasks because:
83
+ - āœ… `.cursorrules` is active in Cursor
84
+ - āœ… `CLAUDE.md` is active in Claude Desktop (if you created it)
85
+ - āœ… `AGENTS.md` is active in other clients (if you created it)
86
+
87
+ **But it's not guaranteed** - the agent decides based on task complexity.
88
+
89
+ ### ⚔ Quick Reference
90
+
91
+ | Scenario | Command |
92
+ |----------|---------|
93
+ | Start new task | `/prompt devctx-workflow` |
94
+ | Continue previous task | `smart_turn(start) and continue` |
95
+ | Force MCP usage | `/prompt use-devctx` |
96
+ | First time in project | `/prompt devctx-preflight` |
97
+ | Trust automatic rules | Just describe your task normally |
98
+
99
+ ---
100
+
45
101
  ## How it Works in Practice
46
102
 
47
103
  **The reality:** This MCP does not intercept prompts automatically. Here's the actual flow:
@@ -267,19 +323,50 @@ Get everything for a task in one call:
267
323
 
268
324
  Returns: relevant files + compressed content + symbol details + graph relationships
269
325
 
326
+ **Smart pattern detection:** Automatically detects literal patterns (TODO, FIXME, /**, console.log, debugger) and prioritizes them in search.
327
+
270
328
  ### smart_summary
271
329
 
272
330
  Maintain task checkpoint:
273
331
 
274
332
  ```javascript
275
- // Save checkpoint
333
+ // Save checkpoint (flat API - recommended)
334
+ { action: 'update', goal: 'Implement OAuth', status: 'in_progress', nextStep: '...' }
335
+
336
+ // Or nested format (backward compatible)
276
337
  { action: 'update', update: { goal: 'Implement OAuth', status: 'in_progress', nextStep: '...' }}
277
338
 
278
339
  // Resume task
279
340
  { action: 'get' }
280
341
  ```
281
342
 
282
- Stores compressed task state (~100 tokens: goal, status, decisions, blockers), not full conversation.
343
+ Stores compressed task state (~100 tokens: goal, status, decisions, blockers), not full conversation. Supports both flat and nested parameter formats.
344
+
345
+ ### smart_status
346
+
347
+ Display current session context:
348
+
349
+ ```javascript
350
+ { format: 'detailed' } // Full output with progress stats
351
+ { format: 'compact' } // Minimal JSON
352
+ ```
353
+
354
+ Shows goal, status, recent decisions, touched files, and progress. Updates automatically with each MCP operation.
355
+
356
+ ### smart_edit
357
+
358
+ Batch edit multiple files:
359
+
360
+ ```javascript
361
+ {
362
+ pattern: 'console.log',
363
+ replacement: 'logger.info',
364
+ files: ['src/a.js', 'src/b.js'],
365
+ mode: 'literal' // or 'regex'
366
+ }
367
+ ```
368
+
369
+ Use `dryRun: true` for preview. Max 50 files per call.
283
370
 
284
371
  ## New Features
285
372
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smart-context-mcp",
3
- "version": "1.3.1",
3
+ "version": "1.4.0",
4
4
  "description": "MCP server that reduces agent token usage by 90% with intelligent context compression, task checkpoint persistence, and workflow-aware agent guidance.",
5
5
  "author": "Francisco Caballero Portero <fcp1978@hotmail.com>",
6
6
  "type": "module",
@@ -0,0 +1,21 @@
1
+ export const IGNORED_DIRS = [
2
+ 'node_modules',
3
+ '.git',
4
+ '.next',
5
+ 'dist',
6
+ 'build',
7
+ 'coverage',
8
+ '.venv',
9
+ 'venv',
10
+ '__pycache__',
11
+ '.terraform',
12
+ '.devctx',
13
+ ];
14
+
15
+ export const IGNORED_FILE_NAMES = [
16
+ 'pnpm-lock.yaml',
17
+ 'package-lock.json',
18
+ 'yarn.lock',
19
+ 'bun.lockb',
20
+ 'npm-shrinkwrap.json',
21
+ ];
@@ -1,24 +1,12 @@
1
- /**
2
- * Cross-project context for monorepos and related projects.
3
- *
4
- * Enables context sharing across multiple related codebases.
5
- */
6
-
7
1
  import fs from 'node:fs';
8
2
  import path from 'node:path';
9
3
  import { loadIndex } from './index.js';
10
4
  import { smartSearch } from './tools/smart-search.js';
11
5
  import { smartRead } from './tools/smart-read.js';
12
- import { projectRoot, setProjectRoot } from './utils/paths.js';
6
+ import { projectRoot } from './utils/paths.js';
13
7
 
14
8
  const CROSS_PROJECT_CONFIG_FILE = '.devctx-projects.json';
15
9
 
16
- /**
17
- * Load cross-project configuration.
18
- *
19
- * @param {string} root - Project root
20
- * @returns {object|null} Configuration or null if not found
21
- */
22
10
  export const loadCrossProjectConfig = (root = projectRoot) => {
23
11
  const configPath = path.join(root, CROSS_PROJECT_CONFIG_FILE);
24
12
 
@@ -34,12 +22,6 @@ export const loadCrossProjectConfig = (root = projectRoot) => {
34
22
  }
35
23
  };
36
24
 
37
- /**
38
- * Discover related projects from config.
39
- *
40
- * @param {string} root - Project root
41
- * @returns {Array<object>} Array of { name, path, type, description }
42
- */
43
25
  export const discoverRelatedProjects = (root = projectRoot) => {
44
26
  const config = loadCrossProjectConfig(root);
45
27
  if (!config?.projects) return [];
@@ -68,13 +50,6 @@ export const discoverRelatedProjects = (root = projectRoot) => {
68
50
  return projects;
69
51
  };
70
52
 
71
- /**
72
- * Search across multiple related projects.
73
- *
74
- * @param {string} query - Search query
75
- * @param {object} options - Search options
76
- * @returns {Promise<Array>} Results from all projects
77
- */
78
53
  export const searchAcrossProjects = async (query, options = {}) => {
79
54
  const {
80
55
  root = projectRoot,
@@ -125,18 +100,10 @@ export const searchAcrossProjects = async (query, options = {}) => {
125
100
  return results;
126
101
  };
127
102
 
128
- /**
129
- * Read files from multiple projects.
130
- *
131
- * @param {Array<object>} fileRefs - Array of { project, file, mode }
132
- * @param {string} root - Project root
133
- * @returns {Promise<Array>} Read results
134
- */
135
103
  export const readAcrossProjects = async (fileRefs, root = projectRoot) => {
136
104
  const relatedProjects = discoverRelatedProjects(root);
137
105
  const projectMap = new Map(relatedProjects.map(p => [p.name, p]));
138
106
 
139
- const originalRoot = projectRoot;
140
107
  const results = [];
141
108
 
142
109
  for (const ref of fileRefs) {
@@ -151,11 +118,10 @@ export const readAcrossProjects = async (fileRefs, root = projectRoot) => {
151
118
  }
152
119
 
153
120
  try {
154
- setProjectRoot(project.path);
155
-
156
121
  const readResult = await smartRead({
157
122
  filePath: ref.file,
158
123
  mode: ref.mode || 'outline',
124
+ cwd: project.path,
159
125
  });
160
126
 
161
127
  results.push({
@@ -175,17 +141,9 @@ export const readAcrossProjects = async (fileRefs, root = projectRoot) => {
175
141
  }
176
142
  }
177
143
 
178
- setProjectRoot(originalRoot);
179
144
  return results;
180
145
  };
181
146
 
182
- /**
183
- * Find symbol definitions across projects.
184
- *
185
- * @param {string} symbolName - Symbol to find
186
- * @param {string} root - Project root
187
- * @returns {Promise<Array>} Symbol locations across projects
188
- */
189
147
  export const findSymbolAcrossProjects = async (symbolName, root = projectRoot) => {
190
148
  const relatedProjects = discoverRelatedProjects(root).filter(p => p.hasIndex);
191
149
  const results = [];
@@ -223,12 +181,6 @@ export const findSymbolAcrossProjects = async (symbolName, root = projectRoot) =
223
181
  return results;
224
182
  };
225
183
 
226
- /**
227
- * Get dependency graph across projects.
228
- *
229
- * @param {string} root - Project root
230
- * @returns {object} Cross-project dependency graph
231
- */
232
184
  export const getCrossProjectDependencies = (root = projectRoot) => {
233
185
  const relatedProjects = discoverRelatedProjects(root).filter(p => p.hasIndex);
234
186
  const dependencies = {
@@ -276,12 +228,6 @@ export const getCrossProjectDependencies = (root = projectRoot) => {
276
228
  return dependencies;
277
229
  };
278
230
 
279
- /**
280
- * Get statistics about cross-project usage.
281
- *
282
- * @param {string} root - Project root
283
- * @returns {object} Usage statistics
284
- */
285
231
  export const getCrossProjectStats = (root = projectRoot) => {
286
232
  const relatedProjects = discoverRelatedProjects(root);
287
233
  const deps = getCrossProjectDependencies(root);
@@ -306,12 +252,6 @@ export const getCrossProjectStats = (root = projectRoot) => {
306
252
  return stats;
307
253
  };
308
254
 
309
- /**
310
- * Create a sample cross-project configuration.
311
- *
312
- * @param {string} root - Project root
313
- * @returns {object} Sample configuration
314
- */
315
255
  export const createSampleConfig = (root = projectRoot) => {
316
256
  return {
317
257
  version: '1.0',
@@ -1,52 +1,25 @@
1
- /**
2
- * Decision explainer - tracks and explains why devctx tools were used or not used
3
- *
4
- * ENABLED BY DEFAULT - disable with: DEVCTX_EXPLAIN=false
5
- *
6
- * Provides transparency into agent decision-making:
7
- * - Why was smart_read used instead of Read?
8
- * - Why was smart_search chosen?
9
- * - What are the expected benefits?
10
- */
11
-
12
1
  const sessionDecisions = {
13
2
  decisions: [],
14
- enabled: true, // Changed: enabled by default
3
+ enabled: true,
15
4
  };
16
5
 
17
- /**
18
- * Check if explanations are enabled
19
- */
20
- /**
21
- * Check if decision explanations are enabled
22
- *
23
- * Priority:
24
- * 1. Explicit env var (DEVCTX_EXPLAIN=true/false)
25
- * 2. Default: ENABLED (changed from disabled)
26
- */
27
6
  export const isExplainEnabled = () => {
28
7
  const envValue = process.env.DEVCTX_EXPLAIN?.toLowerCase();
29
8
 
30
- // Explicit enable
31
9
  if (envValue === 'true' || envValue === '1' || envValue === 'yes') {
32
10
  sessionDecisions.enabled = true;
33
11
  return true;
34
12
  }
35
13
 
36
- // Explicit disable
37
14
  if (envValue === 'false' || envValue === '0' || envValue === 'no') {
38
15
  sessionDecisions.enabled = false;
39
16
  return false;
40
17
  }
41
18
 
42
- // Default: ENABLED (changed)
43
19
  sessionDecisions.enabled = true;
44
20
  return true;
45
21
  };
46
22
 
47
- /**
48
- * Record a decision with explanation
49
- */
50
23
  export const recordDecision = ({
51
24
  tool,
52
25
  action,
@@ -68,16 +41,10 @@ export const recordDecision = ({
68
41
  });
69
42
  };
70
43
 
71
- /**
72
- * Get all decisions for current session
73
- */
74
44
  export const getSessionDecisions = () => {
75
45
  return sessionDecisions.decisions;
76
46
  };
77
47
 
78
- /**
79
- * Format decisions as markdown for display
80
- */
81
48
  export const formatDecisionExplanations = () => {
82
49
  if (!isExplainEnabled()) return '';
83
50
 
@@ -115,47 +82,30 @@ export const formatDecisionExplanations = () => {
115
82
  return lines.join('\n');
116
83
  };
117
84
 
118
- /**
119
- * Reset session decisions (for testing or manual reset)
120
- */
121
85
  export const resetSessionDecisions = () => {
122
86
  sessionDecisions.decisions = [];
123
- sessionDecisions.enabled = true; // Reset to default (enabled)
87
+ sessionDecisions.enabled = true;
124
88
  };
125
89
 
126
- /**
127
- * Common decision reasons (for consistency)
128
- */
129
90
  export const DECISION_REASONS = {
130
- // smart_read reasons
131
91
  LARGE_FILE: 'File is large (>500 lines), outline mode extracts structure only',
132
92
  SYMBOL_EXTRACTION: 'Extracting specific symbol, smart_read can locate and extract it efficiently',
133
93
  TOKEN_BUDGET: 'Token budget constraint, cascading to more compressed mode',
134
94
  MULTIPLE_SYMBOLS: 'Reading multiple symbols, smart_read can batch them',
135
-
136
- // smart_search reasons
137
95
  MULTIPLE_FILES: 'Query spans 50+ files, smart_search ranks by relevance',
138
96
  INTENT_AWARE: 'Intent-aware search prioritizes relevant results (debug/implementation/tests)',
139
97
  INDEX_BOOST: 'Symbol index available, boosting relevant matches',
140
98
  PATTERN_SEARCH: 'Complex pattern search, smart_search handles regex efficiently',
141
-
142
- // smart_context reasons
143
99
  TASK_CONTEXT: 'Building complete context for task, smart_context orchestrates multiple reads',
144
100
  RELATED_FILES: 'Need related files (callers, tests, types), smart_context finds them',
145
101
  ONE_CALL: 'Single call to get all context, more efficient than multiple reads',
146
102
  DIFF_ANALYSIS: 'Analyzing git diff, smart_context expands changed symbols',
147
-
148
- // smart_shell reasons
149
103
  COMMAND_OUTPUT: 'Command output needs compression (git log, npm test, etc.)',
150
104
  RELEVANT_LINES: 'Extracting relevant lines from command output',
151
105
  SAFE_EXECUTION: 'Using allowlist-validated command execution',
152
-
153
- // smart_summary reasons
154
106
  CHECKPOINT: 'Saving task checkpoint for session recovery',
155
107
  RESUME: 'Recovering previous task context',
156
108
  PERSISTENCE: 'Maintaining task state across agent restarts',
157
-
158
- // Native tool reasons
159
109
  SIMPLE_TASK: 'Task is simple, native tool is more direct',
160
110
  ALREADY_CACHED: 'Content already in context, no need for compression',
161
111
  SINGLE_LINE: 'Reading single line, native Read is sufficient',
@@ -163,9 +113,6 @@ export const DECISION_REASONS = {
163
113
  NO_INDEX: 'No symbol index available, native search is equivalent',
164
114
  };
165
115
 
166
- /**
167
- * Common expected benefits (for consistency)
168
- */
169
116
  export const EXPECTED_BENEFITS = {
170
117
  TOKEN_SAVINGS: (tokens) => `~${formatTokens(tokens)} saved`,
171
118
  FASTER_RESPONSE: 'Faster response due to less data to process',
@@ -175,9 +122,6 @@ export const EXPECTED_BENEFITS = {
175
122
  FOCUSED_RESULTS: 'Focused on relevant code only',
176
123
  };
177
124
 
178
- /**
179
- * Format token count for display
180
- */
181
125
  const formatTokens = (tokens) => {
182
126
  if (tokens >= 1000000) {
183
127
  return `${(tokens / 1000000).toFixed(1)}M tokens`;
package/src/index.js CHANGED
@@ -3,6 +3,7 @@ import fsp from 'node:fs/promises';
3
3
  import path from 'node:path';
4
4
  import ts from 'typescript';
5
5
  import { isBinaryBuffer } from './utils/fs.js';
6
+ import { IGNORED_DIRS } from './config/ignored-paths.js';
6
7
 
7
8
  const INDEX_VERSION = 4;
8
9
 
@@ -61,10 +62,7 @@ const indexableExtensions = new Set([
61
62
  '.cs', '.kt', '.php', '.swift',
62
63
  ]);
63
64
 
64
- const ignoredDirs = new Set([
65
- 'node_modules', '.git', '.next', 'dist', 'build', 'coverage',
66
- '.venv', 'venv', '__pycache__', '.terraform', '.devctx',
67
- ]);
65
+ const ignoredDirs = new Set(IGNORED_DIRS);
68
66
 
69
67
  const scriptKindByExtension = {
70
68
  '.js': ts.ScriptKind.JS,
@@ -1,25 +1,9 @@
1
- /**
2
- * Missed opportunities detector - identifies when devctx should have been used but wasn't
3
- *
4
- * Analyzes session metrics to detect patterns where devctx would have helped.
5
- *
6
- * ENABLED BY DEFAULT - disable with: DEVCTX_DETECT_MISSED=false
7
- *
8
- * Detection heuristics (based on session metrics):
9
- * - Low devctx adoption in complex sessions (many operations, few devctx calls)
10
- * - Sessions with 0 devctx usage but high operation count
11
- * - Inferred complexity vs actual devctx usage
12
- *
13
- * Note: We can't intercept native tool calls in real-time, so we analyze
14
- * patterns from metrics after the fact.
15
- */
16
-
17
1
  const sessionActivity = {
18
2
  devctxOperations: 0,
19
- totalOperations: 0, // Estimated from devctx calls + time-based heuristic
3
+ totalOperations: 0,
20
4
  lastDevctxCall: 0,
21
5
  sessionStart: Date.now(),
22
- enabled: true, // Changed: enabled by default
6
+ enabled: true,
23
7
  warnings: [],
24
8
  };
25
9
 
@@ -34,36 +18,23 @@ const DEVCTX_TOOLS = new Set([
34
18
  'build_index',
35
19
  ]);
36
20
 
37
- /**
38
- * Check if missed opportunity detection is enabled
39
- *
40
- * Priority:
41
- * 1. Explicit env var (DEVCTX_DETECT_MISSED=true/false)
42
- * 2. Default: ENABLED (changed from disabled)
43
- */
44
21
  export const isMissedDetectionEnabled = () => {
45
22
  const envValue = process.env.DEVCTX_DETECT_MISSED?.toLowerCase();
46
23
 
47
- // Explicit enable
48
24
  if (envValue === 'true' || envValue === '1' || envValue === 'yes') {
49
25
  sessionActivity.enabled = true;
50
26
  return true;
51
27
  }
52
28
 
53
- // Explicit disable
54
29
  if (envValue === 'false' || envValue === '0' || envValue === 'no') {
55
30
  sessionActivity.enabled = false;
56
31
  return false;
57
32
  }
58
33
 
59
- // Default: ENABLED (changed)
60
34
  sessionActivity.enabled = true;
61
35
  return true;
62
36
  };
63
37
 
64
- /**
65
- * Record devctx tool usage
66
- */
67
38
  export const recordDevctxOperation = () => {
68
39
  if (!isMissedDetectionEnabled()) return;
69
40
 
@@ -72,29 +43,19 @@ export const recordDevctxOperation = () => {
72
43
  sessionActivity.lastDevctxCall = Date.now();
73
44
  };
74
45
 
75
- /**
76
- * Estimate total operations based on time and activity
77
- * Heuristic: If no devctx calls for >2 minutes, likely agent is using native tools
78
- */
79
46
  const estimateTotalOperations = () => {
80
47
  const now = Date.now();
81
48
  const sessionDuration = now - sessionActivity.sessionStart;
82
49
  const timeSinceLastDevctx = now - sessionActivity.lastDevctxCall;
83
50
 
84
- // If session is active (recent devctx calls), estimate conservatively
85
51
  if (timeSinceLastDevctx < 2 * 60 * 1000) {
86
52
  return sessionActivity.totalOperations;
87
53
  }
88
54
 
89
- // If long gap without devctx, estimate agent is using native tools
90
- // Heuristic: ~1 operation per 10 seconds of activity
91
55
  const estimatedNativeOps = Math.floor(timeSinceLastDevctx / 10000);
92
56
  return sessionActivity.totalOperations + estimatedNativeOps;
93
57
  };
94
58
 
95
- /**
96
- * Analyze session and detect missed opportunities
97
- */
98
59
  export const analyzeMissedOpportunities = () => {
99
60
  if (!isMissedDetectionEnabled()) return null;
100
61
 
@@ -102,21 +63,18 @@ export const analyzeMissedOpportunities = () => {
102
63
  const sessionDuration = now - sessionActivity.sessionStart;
103
64
  const timeSinceLastDevctx = now - sessionActivity.lastDevctxCall;
104
65
  const estimatedTotal = estimateTotalOperations();
105
-
106
66
  const opportunities = [];
107
67
 
108
- // Detection 1: Long session with no devctx usage
109
68
  if (sessionDuration > 5 * 60 * 1000 && sessionActivity.devctxOperations === 0) {
110
69
  opportunities.push({
111
70
  type: 'no_devctx_usage',
112
71
  severity: 'high',
113
72
  reason: 'Session active for >5 minutes with 0 devctx calls. Agent may not be using devctx.',
114
73
  suggestion: 'Use forcing prompt or check if MCP is active',
115
- estimatedSavings: estimatedTotal * 10000, // Estimate ~10K tokens per operation
74
+ estimatedSavings: estimatedTotal * 10000,
116
75
  });
117
76
  }
118
77
 
119
- // Detection 2: Low devctx adoption in active session
120
78
  const devctxRatio = estimatedTotal > 0 ? sessionActivity.devctxOperations / estimatedTotal : 0;
121
79
  if (estimatedTotal >= 10 && devctxRatio < 0.3) {
122
80
  opportunities.push({
@@ -128,7 +86,6 @@ export const analyzeMissedOpportunities = () => {
128
86
  });
129
87
  }
130
88
 
131
- // Detection 3: Long gap without devctx (agent switched to native tools)
132
89
  if (sessionActivity.devctxOperations > 0 && timeSinceLastDevctx > 3 * 60 * 1000) {
133
90
  const minutesSince = Math.round(timeSinceLastDevctx / 60000);
134
91
  opportunities.push({
@@ -140,7 +97,6 @@ export const analyzeMissedOpportunities = () => {
140
97
  });
141
98
  }
142
99
 
143
- // Detection 4: Session too short to analyze
144
100
  if (sessionDuration < 60 * 1000 && opportunities.length === 0) {
145
101
  return {
146
102
  opportunities: [],
@@ -160,26 +116,20 @@ export const analyzeMissedOpportunities = () => {
160
116
  };
161
117
  };
162
118
 
163
- /**
164
- * Format missed opportunities as markdown
165
- */
166
119
  export const formatMissedOpportunities = () => {
167
120
  if (!isMissedDetectionEnabled()) return '';
168
121
 
169
122
  const analysis = analyzeMissedOpportunities();
170
123
  if (!analysis) return '';
171
124
 
172
- // Show session too short message (but don't show full warning)
173
125
  if (analysis.message) {
174
126
  return '';
175
127
  }
176
128
 
177
- // If no opportunities but session is active, show positive feedback
178
129
  if (analysis.opportunities.length === 0 && analysis.devctxOperations > 0) {
179
130
  return `\n\nāœ… **devctx adoption: ${analysis.devctxRatio}%** (${analysis.devctxOperations}/${analysis.estimatedTotal} operations)\n`;
180
131
  }
181
132
 
182
- // No opportunities and no devctx usage - don't show yet (wait for longer session)
183
133
  if (analysis.opportunities.length === 0) {
184
134
  return '';
185
135
  }
@@ -227,9 +177,6 @@ export const formatMissedOpportunities = () => {
227
177
  return lines.join('\n');
228
178
  };
229
179
 
230
- /**
231
- * Get session activity summary
232
- */
233
180
  export const getSessionActivity = () => {
234
181
  return {
235
182
  devctxOperations: sessionActivity.devctxOperations,
@@ -240,21 +187,15 @@ export const getSessionActivity = () => {
240
187
  };
241
188
  };
242
189
 
243
- /**
244
- * Reset session activity (for testing or manual reset)
245
- */
246
190
  export const resetSessionActivity = () => {
247
191
  sessionActivity.devctxOperations = 0;
248
192
  sessionActivity.totalOperations = 0;
249
193
  sessionActivity.lastDevctxCall = 0;
250
194
  sessionActivity.sessionStart = Date.now();
251
195
  sessionActivity.warnings = [];
252
- sessionActivity.enabled = true; // Reset to default (enabled)
196
+ sessionActivity.enabled = true;
253
197
  };
254
198
 
255
- /**
256
- * Testing helpers - expose internal state for test scenarios
257
- */
258
199
  export const __testing__ = {
259
200
  setSessionStart: (timestamp) => {
260
201
  sessionActivity.sessionStart = timestamp;
@@ -268,9 +209,6 @@ export const __testing__ = {
268
209
  getSessionActivity: () => sessionActivity,
269
210
  };
270
211
 
271
- /**
272
- * Format token count for display
273
- */
274
212
  const formatTokens = (tokens) => {
275
213
  if (tokens >= 1000000) {
276
214
  return `${(tokens / 1000000).toFixed(1)}M tokens`;