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 +2 -2
- package/package.json +1 -1
- package/scripts/headless-wrapper.js +2 -1
- package/scripts/report-metrics.js +2 -2
- package/scripts/task-runner.js +2 -1
- package/src/analytics/product-quality.js +176 -1
- package/src/hooks/claude-hooks.js +1 -423
- package/src/hooks/cursor-hooks.js +1 -0
- package/src/orchestration/adapters/claude-adapter.js +426 -0
- package/src/orchestration/adapters/cursor-adapter.js +429 -0
- package/src/orchestration/base-orchestrator.js +242 -0
- package/src/orchestration/headless-wrapper.js +39 -195
- package/src/orchestration/policy/event-policy.js +297 -0
- package/src/task-runner.js +33 -247
- package/src/utils/client-detection.js +33 -0
package/README.md
CHANGED
|
@@ -52,9 +52,9 @@ Restart your AI client. Done.
|
|
|
52
52
|
|
|
53
53
|
---
|
|
54
54
|
|
|
55
|
-
##
|
|
55
|
+
## Task Runner
|
|
56
56
|
|
|
57
|
-
`
|
|
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.
|
|
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:
|
|
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
|
|
104
|
+
if (hasProductQualitySignals(report.productQuality)) {
|
|
105
105
|
console.log(formatProductQualityReport(report.productQuality));
|
|
106
106
|
}
|
|
107
107
|
};
|
package/scripts/task-runner.js
CHANGED
|
@@ -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:
|
|
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
|
|
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.');
|