smart-context-mcp 1.7.7 → 1.8.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
@@ -105,6 +105,29 @@ See [Task Runner Workflows](https://github.com/Arrayo/smart-context-mcp/blob/mai
105
105
 
106
106
  ---
107
107
 
108
+ ## When to Use (and When Not To)
109
+
110
+ **Use devctx when:**
111
+ - You're exploring an unfamiliar codebase
112
+ - The task spans multiple sessions (checkpoints save context)
113
+ - You need to understand how files relate to each other (graph/imports)
114
+ - The context is too large to manage manually
115
+ - You're doing complex multi-file refactors or debugging across layers
116
+
117
+ **Skip devctx when:**
118
+ - You already know exactly which files to touch
119
+ - It's a single-file or surgical change (2-3 edits max)
120
+ - You have the full mental map from a recent exploration
121
+ - Native tools (Grep, Read, StrReplace) are more direct for the task
122
+
123
+ **Honest verdict from real users:**
124
+
125
+ > "The MCP shines in long, multi-session tasks or when you don't know the codebase. For contained refactors where you already know what to touch, native tools are just as fast or faster. The real value was `smart_read(outline)` for the initial analysis and checkpoints to not lose the thread between sessions."
126
+
127
+ The 90% token savings are real, but they require the right task type to materialize.
128
+
129
+ ---
130
+
108
131
  ## 📊 Real Metrics
109
132
 
110
133
  **Production use on this project:**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smart-context-mcp",
3
- "version": "1.7.7",
3
+ "version": "1.8.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",
@@ -19,3 +19,9 @@ export const IGNORED_FILE_NAMES = [
19
19
  'bun.lockb',
20
20
  'npm-shrinkwrap.json',
21
21
  ];
22
+
23
+ export const IGNORED_FILE_PATTERNS = [
24
+ /\.min\.(js|css)$/,
25
+ /\.(map|snap)$/,
26
+ /^(questions|answers|fixtures|seed|dump|data)\.(json|jsonl|ndjson)$/i,
27
+ ];
@@ -33,7 +33,7 @@ const saveIndexMetadata = (meta, root = projectRoot) => {
33
33
  }
34
34
  fs.writeFileSync(metaPath, JSON.stringify(meta, null, 2), 'utf8');
35
35
  } catch (error) {
36
- console.warn('Failed to save index metadata:', error.message);
36
+ process.stderr.write(`[devctx] Failed to save index metadata: ${error.message}\n`);
37
37
  }
38
38
  };
39
39
 
@@ -65,31 +65,12 @@ const timeout = (ms, message) => {
65
65
  });
66
66
  };
67
67
 
68
- const isTestEnvironment = () => {
69
- return process.env.NODE_ENV === 'test' ||
70
- typeof process.env.NODE_TEST_CONTEXT !== 'undefined' ||
71
- process.argv.some(arg => arg.includes('--test'));
72
- };
73
-
74
- const isMcpEnvironment = () => {
75
- return process.env.MCP_SERVER === 'true' ||
76
- process.argv.some(arg => arg.includes('devctx-server'));
77
- };
78
-
79
- const log = (message, level = 'info') => {
80
- if (isTestEnvironment() || isMcpEnvironment()) {
81
- return;
82
- }
83
-
84
- if (level === 'warn') {
85
- console.warn(message);
86
- } else {
87
- console.log(message);
88
- }
68
+ const log = (message) => {
69
+ process.stderr.write(`[devctx] ${message}\n`);
89
70
  };
90
71
 
91
72
  export const ensureIndexReady = async (options = {}) => {
92
- const { force = false, timeoutMs = INDEX_BUILD_TIMEOUT_MS, root = projectRoot, silent = false } = options;
73
+ const { force = false, timeoutMs = INDEX_BUILD_TIMEOUT_MS, root = projectRoot } = options;
93
74
 
94
75
  if (!force) {
95
76
  const existingIndex = loadIndex(root);
@@ -101,9 +82,7 @@ export const ensureIndexReady = async (options = {}) => {
101
82
  }
102
83
  }
103
84
 
104
- if (!silent) {
105
- log('📦 Building search index (this may take 30-60s)...');
106
- }
85
+ log('Building search index...');
107
86
 
108
87
  try {
109
88
  const buildPromise = buildIndexCore({ root, incremental: true });
@@ -119,14 +98,10 @@ export const ensureIndexReady = async (options = {}) => {
119
98
  version: result?.version
120
99
  }, root);
121
100
 
122
- if (!silent) {
123
- log('✅ Index ready');
124
- }
101
+ log('Index ready');
125
102
  return { status: 'built', cached: false, fileCount: result?.files?.length || 0 };
126
103
  } catch (error) {
127
- if (!silent) {
128
- log('⚠️ Index build failed, search will use fallback mode', 'warn');
129
- }
104
+ log(`Index build failed: ${error.message}`);
130
105
  return { status: 'fallback', error: error.message };
131
106
  }
132
107
  };
@@ -11,7 +11,7 @@ import { truncate } from '../utils/text.js';
11
11
  import { recordToolUsage } from '../usage-feedback.js';
12
12
  import { recordDecision, DECISION_REASONS, EXPECTED_BENEFITS } from '../decision-explainer.js';
13
13
  import { recordDevctxOperation } from '../missed-opportunities.js';
14
- import { IGNORED_DIRS, IGNORED_FILE_NAMES } from '../config/ignored-paths.js';
14
+ import { IGNORED_DIRS, IGNORED_FILE_NAMES, IGNORED_FILE_PATTERNS } from '../config/ignored-paths.js';
15
15
  import { buildMetricsDisplay } from '../utils/metrics-display.js';
16
16
  import { createProgressReporter } from '../streaming.js';
17
17
  import { ensureIndexReady } from '../index-manager.js';
@@ -44,7 +44,12 @@ export const intentWeights = {
44
44
 
45
45
  const defaultWeights = intentWeights.explore;
46
46
 
47
- const shouldIgnoreFile = (filePath) => ignoredFileNames.has(path.basename(filePath));
47
+ const shouldIgnoreFile = (filePath) => {
48
+ const base = path.basename(filePath);
49
+ if (ignoredFileNames.has(base)) return true;
50
+ if (IGNORED_FILE_PATTERNS.some((p) => p.test(base))) return true;
51
+ return false;
52
+ };
48
53
 
49
54
  const isSearchableFile = (entryName, fullPath) => fallbackExtensions.has(path.extname(entryName)) || isDockerfile(fullPath);
50
55
 
@@ -92,6 +97,8 @@ const parseRgLine = (line, root) => {
92
97
  };
93
98
  };
94
99
 
100
+ const MAX_FILE_SIZE = '1M';
101
+
95
102
  const searchWithRipgrep = async (root, query) => {
96
103
  const args = [
97
104
  '--line-number',
@@ -100,10 +107,12 @@ const searchWithRipgrep = async (root, query) => {
100
107
  'never',
101
108
  '--smart-case',
102
109
  '--fixed-strings',
110
+ '--max-filesize', MAX_FILE_SIZE,
103
111
  ];
104
112
 
105
113
  for (const dir of ignoredDirs) {
106
114
  args.push('--glob', `!${dir}/**`);
115
+ args.push('--glob', `!**/${dir}/**`);
107
116
  }
108
117
 
109
118
  for (const fileName of ignoredFileNames) {