smart-context-mcp 1.6.2 → 1.7.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 CHANGED
@@ -52,9 +52,9 @@ Restart your AI client. Done.
52
52
 
53
53
  ---
54
54
 
55
- ## `1.6.0` Task Runner
55
+ ## Task Runner
56
56
 
57
- `1.6.0` adds `smart-context-task`, a workflow-oriented CLI on top of the raw MCP tools.
57
+ `smart-context-task` is a workflow-oriented CLI on top of the raw MCP tools.
58
58
 
59
59
  Use it when you want a more repeatable path than “agent reads rules and hopefully picks the right flow”.
60
60
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "smart-context-mcp",
3
- "version": "1.6.2",
3
+ "version": "1.7.1",
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",
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { runHeadlessWrapper } from '../src/orchestration/headless-wrapper.js';
3
+ import { detectClient } from '../src/utils/client-detection.js';
3
4
 
4
5
  const requireValue = (argv, index, flag) => {
5
6
  const value = argv[index + 1];
@@ -11,7 +12,7 @@ const requireValue = (argv, index, flag) => {
11
12
 
12
13
  const parseArgs = (argv) => {
13
14
  const options = {
14
- client: 'generic',
15
+ client: null,
15
16
  prompt: '',
16
17
  sessionId: undefined,
17
18
  event: undefined,
@@ -3,7 +3,7 @@ import path from 'node:path';
3
3
  import { fileURLToPath } from 'node:url';
4
4
  import { smartMetrics } from '../src/tools/smart-metrics.js';
5
5
  import { formatAdoptionReport } from '../src/analytics/adoption.js';
6
- import { formatProductQualityReport } from '../src/analytics/product-quality.js';
6
+ import { formatProductQualityReport, hasProductQualitySignals } from '../src/analytics/product-quality.js';
7
7
 
8
8
  const requireValue = (argv, index, flag) => {
9
9
  const value = argv[index + 1];
@@ -101,7 +101,7 @@ const printHuman = (report) => {
101
101
  console.log(formatAdoptionReport(report.adoption));
102
102
  }
103
103
 
104
- if (report.productQuality?.turnsMeasured > 0) {
104
+ if (hasProductQualitySignals(report.productQuality)) {
105
105
  console.log(formatProductQualityReport(report.productQuality));
106
106
  }
107
107
  };
@@ -1,6 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { runTaskRunner } from '../src/task-runner.js';
3
3
  import { checkNodeVersion } from '../src/utils/runtime-check.js';
4
+ import { detectClient } from '../src/utils/client-detection.js';
4
5
 
5
6
  const runtimeCheck = checkNodeVersion();
6
7
  if (!runtimeCheck.ok) {
@@ -28,7 +29,7 @@ const parseArgs = (argv) => {
28
29
  const rest = argv[0] && !argv[0].startsWith('--') ? argv.slice(1) : argv;
29
30
  const options = {
30
31
  commandName: subcommand,
31
- client: 'generic',
32
+ client: null,
32
33
  prompt: '',
33
34
  sessionId: undefined,
34
35
  event: undefined,
@@ -12,6 +12,139 @@ const isTaskRunnerQualityEntry = (entry) =>
12
12
  entry?.tool === 'task_runner'
13
13
  && entry?.metadata?.analyticsKind === TASK_RUNNER_QUALITY_ANALYTICS_KIND;
14
14
 
15
+ const roundAverage = (total, count) =>
16
+ count > 0 ? Number((total / count).toFixed(1)) : 0;
17
+
18
+ const getMetricsClient = (entry) =>
19
+ entry?.metadata?.adapterClient
20
+ ?? entry?.metadata?.client
21
+ ?? null;
22
+
23
+ const hasMeasuredClientAdapters = (stats) =>
24
+ Number(stats?.clientAdapters?.clientsMeasured ?? 0) > 0;
25
+
26
+ const appendClientAdapterSignals = (lines, stats) => {
27
+ if (!hasMeasuredClientAdapters(stats)) {
28
+ return;
29
+ }
30
+
31
+ const clients = stats.clientAdapters.byClient;
32
+ const lowestOverheadClient = clients.reduce((best, current) => {
33
+ if (!best) {
34
+ return current;
35
+ }
36
+
37
+ if (current.averageContextOverheadTokens < best.averageContextOverheadTokens) {
38
+ return current;
39
+ }
40
+
41
+ if (current.averageContextOverheadTokens === best.averageContextOverheadTokens
42
+ && current.client.localeCompare(best.client) < 0) {
43
+ return current;
44
+ }
45
+
46
+ return best;
47
+ }, null);
48
+ const highestAutoStartClient = clients.reduce((best, current) => {
49
+ if (!best) {
50
+ return current;
51
+ }
52
+
53
+ if (current.autoStartCoveragePct > best.autoStartCoveragePct) {
54
+ return current;
55
+ }
56
+
57
+ if (current.autoStartCoveragePct === best.autoStartCoveragePct
58
+ && current.client.localeCompare(best.client) < 0) {
59
+ return current;
60
+ }
61
+
62
+ return best;
63
+ }, null);
64
+
65
+ lines.push('Client Adapter Signals:');
66
+ lines.push(`Clients measured: ${stats.clientAdapters.clientsMeasured}`);
67
+ lines.push(`Adapter events: ${clients.reduce((total, client) => total + client.adapterEvents, 0)}`);
68
+ lines.push(`Overhead total: ${stats.clientAdapters.totalContextOverheadTokens} tokens`);
69
+ if (lowestOverheadClient) {
70
+ lines.push(`Lowest avg overhead: ${lowestOverheadClient.client} (${lowestOverheadClient.averageContextOverheadTokens} tokens)`);
71
+ }
72
+ if (highestAutoStartClient) {
73
+ lines.push(`Best auto-start rate: ${highestAutoStartClient.client} (${highestAutoStartClient.autoStartCoveragePct}%)`);
74
+ }
75
+ lines.push('');
76
+
77
+ for (const client of clients) {
78
+ lines.push(`${client.client}:`);
79
+ lines.push(` Entries measured: ${client.entriesMeasured}`);
80
+ lines.push(` Adapter coverage: ${client.adapterEvents}/${client.entriesMeasured} (${client.adapterCoveragePct}%)`);
81
+ lines.push(` Base orchestrated: ${client.baseOrchestratedEvents}/${client.entriesMeasured} (${client.baseOrchestratorCoveragePct}%)`);
82
+ lines.push(` Auto-started: ${client.autoStartedEvents}/${client.entriesMeasured} (${client.autoStartCoveragePct}%)`);
83
+ lines.push(` Auto-preflighted: ${client.autoPreflightedEvents}/${client.entriesMeasured} (${client.autoPreflightCoveragePct}%)`);
84
+ lines.push(` Auto-checkpointed: ${client.autoCheckpointedEvents}/${client.entriesMeasured} (${client.autoCheckpointCoveragePct}%)`);
85
+ lines.push(` Context overhead: ${client.contextOverheadTokens} tokens total (${client.averageContextOverheadTokens} avg)`);
86
+ if (client.blockedEvents > 0) {
87
+ lines.push(` Blocked events: ${client.blockedEvents}`);
88
+ }
89
+ lines.push('');
90
+ }
91
+ };
92
+
93
+ const analyzeClientAdapterQuality = (entries = []) => {
94
+ const clientEntries = entries.filter((entry) => Boolean(getMetricsClient(entry)));
95
+
96
+ const byClient = [...clientEntries.reduce((acc, entry) => {
97
+ const client = getMetricsClient(entry);
98
+ const current = acc.get(client) ?? {
99
+ client,
100
+ entriesMeasured: 0,
101
+ adapterEvents: 0,
102
+ wrapperEvents: 0,
103
+ taskRunnerEvents: 0,
104
+ baseOrchestratedEvents: 0,
105
+ autoStartedEvents: 0,
106
+ autoPreflightedEvents: 0,
107
+ autoCheckpointedEvents: 0,
108
+ blockedEvents: 0,
109
+ contextOverheadEntries: 0,
110
+ contextOverheadTokens: 0,
111
+ };
112
+
113
+ const overheadTokens = Math.max(0, Number(entry.metadata?.overheadTokens ?? 0));
114
+ current.entriesMeasured += 1;
115
+ current.adapterEvents += entry.metadata?.managedByClientAdapter ? 1 : 0;
116
+ current.wrapperEvents += entry.tool === 'agent_wrapper' ? 1 : 0;
117
+ current.taskRunnerEvents += entry.tool === 'task_runner' ? 1 : 0;
118
+ current.baseOrchestratedEvents += entry.metadata?.managedByBaseOrchestrator ? 1 : 0;
119
+ current.autoStartedEvents += (entry.metadata?.autoStartTriggered || entry.metadata?.autoStarted) ? 1 : 0;
120
+ current.autoPreflightedEvents += entry.metadata?.autoPreflightTriggered ? 1 : 0;
121
+ current.autoCheckpointedEvents += (entry.metadata?.autoCheckpointTriggered || entry.metadata?.autoAppended) ? 1 : 0;
122
+ current.blockedEvents += entry.metadata?.blocked ? 1 : 0;
123
+ current.contextOverheadEntries += overheadTokens > 0 ? 1 : 0;
124
+ current.contextOverheadTokens += overheadTokens;
125
+
126
+ acc.set(client, current);
127
+ return acc;
128
+ }, new Map()).values()]
129
+ .map((entry) => ({
130
+ ...entry,
131
+ averageContextOverheadTokens: roundAverage(entry.contextOverheadTokens, entry.contextOverheadEntries),
132
+ adapterCoveragePct: roundPct(entry.adapterEvents, entry.entriesMeasured),
133
+ baseOrchestratorCoveragePct: roundPct(entry.baseOrchestratedEvents, entry.entriesMeasured),
134
+ autoStartCoveragePct: roundPct(entry.autoStartedEvents, entry.entriesMeasured),
135
+ autoPreflightCoveragePct: roundPct(entry.autoPreflightedEvents, entry.entriesMeasured),
136
+ autoCheckpointCoveragePct: roundPct(entry.autoCheckpointedEvents, entry.entriesMeasured),
137
+ }))
138
+ .sort((a, b) => a.client.localeCompare(b.client));
139
+
140
+ return {
141
+ clientsMeasured: byClient.length,
142
+ entriesMeasured: clientEntries.length,
143
+ totalContextOverheadTokens: byClient.reduce((total, entry) => total + entry.contextOverheadTokens, 0),
144
+ byClient,
145
+ };
146
+ };
147
+
15
148
  const analyzeTaskRunnerQuality = (entries = []) => {
16
149
  const runnerEntries = entries.filter(isTaskRunnerQualityEntry);
17
150
  const workflowEntries = runnerEntries.filter((entry) => entry.metadata?.isWorkflowCommand);
@@ -25,6 +158,16 @@ const analyzeTaskRunnerQuality = (entries = []) => {
25
158
  const blockedWithDoctor = blockedEntries.filter((entry) => entry.metadata?.doctorIssued);
26
159
  const checkpointEntries = runnerEntries.filter((entry) => entry.action === 'checkpoint');
27
160
  const persistedCheckpointEntries = checkpointEntries.filter((entry) => entry.metadata?.checkpointPersisted);
161
+ const baseOrchestratedEntries = workflowEntries.filter((entry) => entry.metadata?.managedByBaseOrchestrator);
162
+ const autoStartedEntries = workflowEntries.filter((entry) => entry.metadata?.autoStartTriggered);
163
+ const autoPreflightEntries = workflowEntries.filter((entry) => entry.metadata?.autoPreflightTriggered);
164
+ const autoCheckpointEntries = runnerEntries.filter((entry) => entry.metadata?.autoCheckpointTriggered);
165
+ const isolatedWorkflowEntries = workflowEntries.filter((entry) => entry.metadata?.isolatedSession);
166
+ const contextOverheadEntries = runnerEntries.filter((entry) => Number(entry.metadata?.contextOverheadTokens ?? 0) > 0);
167
+ const contextOverheadTokens = runnerEntries.reduce(
168
+ (total, entry) => total + Number(entry.metadata?.contextOverheadTokens ?? 0),
169
+ 0,
170
+ );
28
171
 
29
172
  const commandBreakdown = [...runnerEntries.reduce((acc, entry) => {
30
173
  const key = entry.action ?? 'unknown';
@@ -71,6 +214,20 @@ const analyzeTaskRunnerQuality = (entries = []) => {
71
214
  persistedCommands: persistedCheckpointEntries.length,
72
215
  persistenceRatePct: roundPct(persistedCheckpointEntries.length, checkpointEntries.length),
73
216
  },
217
+ automaticity: {
218
+ baseOrchestratedCommands: baseOrchestratedEntries.length,
219
+ autoStartedCommands: autoStartedEntries.length,
220
+ autoPreflightedCommands: autoPreflightEntries.length,
221
+ autoCheckpointedCommands: autoCheckpointEntries.length,
222
+ isolatedWorkflowCommands: isolatedWorkflowEntries.length,
223
+ contextOverheadTokens,
224
+ averageContextOverheadTokens: contextOverheadEntries.length > 0
225
+ ? Number((contextOverheadTokens / contextOverheadEntries.length).toFixed(1))
226
+ : 0,
227
+ baseOrchestratorCoveragePct: roundPct(baseOrchestratedEntries.length, workflowEntries.length),
228
+ autoStartCoveragePct: roundPct(autoStartedEntries.length, workflowEntries.length),
229
+ autoPreflightCoveragePct: roundPct(autoPreflightEntries.length, workflowEntries.length),
230
+ },
74
231
  };
75
232
  };
76
233
 
@@ -132,15 +289,24 @@ export const analyzeProductQuality = (entries = []) => {
132
289
  blockedEnds,
133
290
  persistenceRatePct: roundPct(persistedEnds, endEntries.length),
134
291
  },
292
+ clientAdapters: analyzeClientAdapterQuality(entries),
135
293
  taskRunner: analyzeTaskRunnerQuality(entries),
136
294
  };
137
295
  };
138
296
 
297
+ export const hasProductQualitySignals = (stats) =>
298
+ Boolean(stats)
299
+ && (
300
+ Number(stats.turnsMeasured ?? 0) > 0
301
+ || Number(stats?.taskRunner?.commandsMeasured ?? 0) > 0
302
+ || hasMeasuredClientAdapters(stats)
303
+ );
304
+
139
305
  export const formatProductQualityReport = (stats) => {
140
306
  const hasSmartTurn = Number(stats?.turnsMeasured ?? 0) > 0;
141
307
  const hasTaskRunner = Number(stats?.taskRunner?.commandsMeasured ?? 0) > 0;
142
308
 
143
- if (!stats || (!hasSmartTurn && !hasTaskRunner)) {
309
+ if (!hasProductQualitySignals(stats)) {
144
310
  return '';
145
311
  }
146
312
 
@@ -193,11 +359,20 @@ export const formatProductQualityReport = (stats) => {
193
359
  lines.push('Blocked-State Routing:');
194
360
  lines.push(`Blocked with doctor: ${stats.taskRunner.blockedState.blockedWithDoctor}/${stats.taskRunner.blockedState.blockedCommands} (${stats.taskRunner.blockedState.doctorCoveragePct}%)`);
195
361
  lines.push('');
362
+ lines.push('Automaticity:');
363
+ lines.push(`Base orchestrated: ${stats.taskRunner.automaticity.baseOrchestratedCommands}/${stats.taskRunner.workflowCommands} (${stats.taskRunner.automaticity.baseOrchestratorCoveragePct}%)`);
364
+ lines.push(`Auto-started: ${stats.taskRunner.automaticity.autoStartedCommands}/${stats.taskRunner.workflowCommands} (${stats.taskRunner.automaticity.autoStartCoveragePct}%)`);
365
+ lines.push(`Auto-preflighted: ${stats.taskRunner.automaticity.autoPreflightedCommands}/${stats.taskRunner.workflowCommands} (${stats.taskRunner.automaticity.autoPreflightCoveragePct}%)`);
366
+ lines.push(`Auto-checkpointed: ${stats.taskRunner.automaticity.autoCheckpointedCommands}`);
367
+ lines.push(`Context overhead: ${stats.taskRunner.automaticity.contextOverheadTokens} tokens total (${stats.taskRunner.automaticity.averageContextOverheadTokens} avg)`);
368
+ lines.push('');
196
369
  lines.push('Checkpoint Commands:');
197
370
  lines.push(`Persisted checkpoints: ${stats.taskRunner.checkpointing.persistedCommands}/${stats.taskRunner.checkpointing.commandsMeasured} (${stats.taskRunner.checkpointing.persistenceRatePct}%)`);
198
371
  lines.push('');
199
372
  }
200
373
 
374
+ appendClientAdapterSignals(lines, stats);
375
+
201
376
  lines.push('Notes:');
202
377
  lines.push('- These are measured orchestration signals, not direct answer-quality scores.');
203
378
  lines.push('- Context refresh usefulness is proxied by whether refreshed turns surfaced top-file signals.');