smart-context-mcp 1.4.0 → 1.6.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 +129 -1
- package/package.json +12 -3
- package/scripts/doctor-state.js +89 -0
- package/scripts/init-clients.js +48 -7
- package/scripts/report-adoption-metrics.js +228 -0
- package/scripts/report-metrics.js +12 -1
- package/scripts/report-workflow-metrics.js +35 -2
- package/scripts/task-runner.js +238 -0
- package/src/analytics/product-quality.js +206 -0
- package/src/client-contract.js +153 -0
- package/src/context-patterns.js +11 -0
- package/src/hooks/claude-hooks.js +48 -49
- package/src/metrics.js +19 -16
- package/src/orchestration/headless-wrapper.js +20 -26
- package/src/repo-safety.js +21 -0
- package/src/server.js +14 -4
- package/src/storage/sqlite.js +241 -12
- package/src/task-runner/policy.js +354 -0
- package/src/task-runner.js +631 -0
- package/src/tools/smart-doctor.js +418 -0
- package/src/tools/smart-metrics.js +54 -30
- package/src/tools/smart-status.js +79 -7
- package/src/tools/smart-summary.js +29 -29
- package/src/tools/smart-turn.js +455 -14
- package/src/utils/mutation-safety.js +85 -0
- package/src/workflow-tracker.js +261 -39
package/README.md
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# smart-context-mcp
|
|
2
2
|
|
|
3
|
-
MCP server that reduces AI agent token usage by 90% with intelligent context compression.
|
|
3
|
+
MCP server that reduces AI agent token usage by up to 90% with intelligent context compression (measured on this project).
|
|
4
4
|
|
|
5
5
|
[](https://www.npmjs.com/package/smart-context-mcp)
|
|
6
6
|
[](https://opensource.org/licenses/MIT)
|
|
@@ -14,6 +14,14 @@ npx smart-context-init --target . --clients cursor
|
|
|
14
14
|
```
|
|
15
15
|
Restart Cursor. Done.
|
|
16
16
|
|
|
17
|
+
Optional assisted mode for long tasks:
|
|
18
|
+
```bash
|
|
19
|
+
./.devctx/bin/cursor-devctx task --prompt "your task" -- <agent-command> [args...]
|
|
20
|
+
./.devctx/bin/cursor-devctx implement --prompt "implement the auth guard" -- <agent-command> [args...]
|
|
21
|
+
./.devctx/bin/cursor-devctx review --prompt "review the latest diff" -- <agent-command> [args...]
|
|
22
|
+
./.devctx/bin/cursor-devctx doctor
|
|
23
|
+
```
|
|
24
|
+
|
|
17
25
|
### Codex CLI
|
|
18
26
|
```bash
|
|
19
27
|
npm install -g smart-context-mcp
|
|
@@ -44,6 +52,62 @@ Restart your AI client. Done.
|
|
|
44
52
|
|
|
45
53
|
---
|
|
46
54
|
|
|
55
|
+
## `1.6.0` Task Runner
|
|
56
|
+
|
|
57
|
+
`1.6.0` adds `smart-context-task`, a workflow-oriented CLI on top of the raw MCP tools.
|
|
58
|
+
|
|
59
|
+
Use it when you want a more repeatable path than “agent reads rules and hopefully picks the right flow”.
|
|
60
|
+
|
|
61
|
+
```bash
|
|
62
|
+
smart-context-task task --prompt "inspect the auth flow and continue the bugfix"
|
|
63
|
+
smart-context-task implement --prompt "add a token guard to loginHandler"
|
|
64
|
+
smart-context-task continue --session-id my-session-id
|
|
65
|
+
smart-context-task review --prompt "review the latest diff"
|
|
66
|
+
smart-context-task doctor
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
The runner now covers:
|
|
70
|
+
|
|
71
|
+
- `task`
|
|
72
|
+
- `implement`
|
|
73
|
+
- `continue`
|
|
74
|
+
- `resume`
|
|
75
|
+
- `review`
|
|
76
|
+
- `debug`
|
|
77
|
+
- `refactor`
|
|
78
|
+
- `test`
|
|
79
|
+
- `doctor`
|
|
80
|
+
- `status`
|
|
81
|
+
- `checkpoint`
|
|
82
|
+
- `cleanup`
|
|
83
|
+
|
|
84
|
+
For Cursor projects, `smart-context-init` also generates `./.devctx/bin/cursor-devctx`, which routes through the same runner/policy stack.
|
|
85
|
+
|
|
86
|
+
See [Task Runner Workflows](../../docs/task-runner.md) for the full behavior and command guidance.
|
|
87
|
+
|
|
88
|
+
---
|
|
89
|
+
|
|
90
|
+
## 📊 Real Metrics
|
|
91
|
+
|
|
92
|
+
**Production use on this project:**
|
|
93
|
+
- ~7M tokens → ~800K tokens (approximately 89% reduction)
|
|
94
|
+
- 1,500+ operations tracked
|
|
95
|
+
- Compression ratios: 3x to 46x
|
|
96
|
+
|
|
97
|
+
**Workflow savings:**
|
|
98
|
+
- Debugging: ~85-90% reduction
|
|
99
|
+
- Code Review: ~85-90% reduction
|
|
100
|
+
- Refactoring: ~85-90% reduction
|
|
101
|
+
- Testing: ~85-90% reduction
|
|
102
|
+
- Architecture: ~85-90% reduction
|
|
103
|
+
|
|
104
|
+
**Real adoption:**
|
|
105
|
+
- Approximately 70-75% of complex tasks use devctx
|
|
106
|
+
- Top tools: `smart_read` (850+), `smart_search` (280+), `smart_shell` (220+)
|
|
107
|
+
- Non-usage: task too simple, no index built, native tools preferred
|
|
108
|
+
|
|
109
|
+
---
|
|
110
|
+
|
|
47
111
|
## 🚀 How to Invoke the MCP
|
|
48
112
|
|
|
49
113
|
The MCP doesn't intercept prompts automatically. **You need to tell the agent to use it.**
|
|
@@ -98,6 +162,36 @@ The agent *should* use devctx automatically for complex tasks because:
|
|
|
98
162
|
|
|
99
163
|
---
|
|
100
164
|
|
|
165
|
+
## 🚨 Agent Ignored devctx? → Paste This Next
|
|
166
|
+
|
|
167
|
+
<table>
|
|
168
|
+
<tr>
|
|
169
|
+
<td width="100%" bgcolor="#FFF3CD">
|
|
170
|
+
|
|
171
|
+
### 📋 Official Prompt (Copy & Paste)
|
|
172
|
+
|
|
173
|
+
```
|
|
174
|
+
Use smart-context-mcp for this task.
|
|
175
|
+
Start with smart_turn(start), then use smart_context or smart_search before reading full files.
|
|
176
|
+
End with smart_turn(end) if you make progress.
|
|
177
|
+
```
|
|
178
|
+
|
|
179
|
+
### ⚡ Ultra-Short
|
|
180
|
+
|
|
181
|
+
```
|
|
182
|
+
Use devctx: smart_turn(start) → smart_context → smart_turn(end)
|
|
183
|
+
```
|
|
184
|
+
|
|
185
|
+
</td>
|
|
186
|
+
</tr>
|
|
187
|
+
</table>
|
|
188
|
+
|
|
189
|
+
**When:** Agent read large files with `Read`, used `Grep` repeatedly, or no devctx tools in complex task.
|
|
190
|
+
|
|
191
|
+
**Why:** Task seemed simple, no index built, native tools appeared more direct, or rules weren't strong enough.
|
|
192
|
+
|
|
193
|
+
---
|
|
194
|
+
|
|
101
195
|
## How it Works in Practice
|
|
102
196
|
|
|
103
197
|
**The reality:** This MCP does not intercept prompts automatically. Here's the actual flow:
|
|
@@ -116,11 +210,17 @@ The agent *should* use devctx automatically for complex tasks because:
|
|
|
116
210
|
- ✅ Rules **guide** the agent (not enforce)
|
|
117
211
|
- ✅ Agent can use built-in tools when appropriate
|
|
118
212
|
- ✅ Token savings: 85-90% on complex tasks
|
|
213
|
+
- ✅ Reports can show both gross savings and net savings after context overhead
|
|
214
|
+
- ✅ Workflow JSON/reporting now exposes net-metrics coverage, so historical rows without persisted overhead are explicit
|
|
215
|
+
- ✅ `smart_metrics` now exposes measured orchestration-quality signals from `smart_turn` (continuity recovery, blocked-state remediation coverage, context-refresh signals)
|
|
216
|
+
- ✅ If `.devctx/state.sqlite` is tracked or staged, runtime SQLite mutations pause across checkpoints, workflow tracking, hook state, and pattern learning
|
|
119
217
|
|
|
120
218
|
Check actual usage:
|
|
121
219
|
- **Real-time feedback** - Enabled by default (disable with `export DEVCTX_SHOW_USAGE=false`)
|
|
122
220
|
- `npm run report:metrics` - Tool-level savings + adoption analysis
|
|
123
221
|
- `npm run report:workflows` - Workflow-level savings (requires `DEVCTX_WORKFLOW_TRACKING=true`)
|
|
222
|
+
- `npm run benchmark:orchestration` - Repeatable orchestration regression suite for continuity, refresh, blocked-state remediation, and checkpoint quality
|
|
223
|
+
- `npm run benchmark:orchestration:release` - Same suite with a checked-in release baseline, used by CI and `prepublishOnly`
|
|
124
224
|
|
|
125
225
|
## What it does
|
|
126
226
|
|
|
@@ -341,6 +441,23 @@ Maintain task checkpoint:
|
|
|
341
441
|
```
|
|
342
442
|
|
|
343
443
|
Stores compressed task state (~100 tokens: goal, status, decisions, blockers), not full conversation. Supports both flat and nested parameter formats.
|
|
444
|
+
When git hygiene or SQLite storage health affects persisted state, responses expose `mutationSafety`, `repoSafety`, `degradedMode`, and `storageHealth` so clients can remediate consistently.
|
|
445
|
+
|
|
446
|
+
### smart_doctor
|
|
447
|
+
|
|
448
|
+
Run a single operational health check across repo hygiene, SQLite state, compaction hygiene, and legacy cleanup:
|
|
449
|
+
|
|
450
|
+
```javascript
|
|
451
|
+
smart_doctor({})
|
|
452
|
+
smart_doctor({ verifyIntegrity: false })
|
|
453
|
+
```
|
|
454
|
+
|
|
455
|
+
CLI:
|
|
456
|
+
|
|
457
|
+
```bash
|
|
458
|
+
smart-context-doctor --json
|
|
459
|
+
smart-context-doctor --no-integrity
|
|
460
|
+
```
|
|
344
461
|
|
|
345
462
|
### smart_status
|
|
346
463
|
|
|
@@ -352,6 +469,17 @@ Display current session context:
|
|
|
352
469
|
```
|
|
353
470
|
|
|
354
471
|
Shows goal, status, recent decisions, touched files, and progress. Updates automatically with each MCP operation.
|
|
472
|
+
When repo safety or SQLite health blocks normal state access, `smart_status` still exposes the same safety contract plus `storageHealth`.
|
|
473
|
+
|
|
474
|
+
### SQLite Recovery
|
|
475
|
+
|
|
476
|
+
If `.devctx/state.sqlite` is unhealthy, use the surfaced `storageHealth.issue`:
|
|
477
|
+
|
|
478
|
+
- `missing`: initialize local state with a persisted action
|
|
479
|
+
- `oversized`: run `smart_summary compact`
|
|
480
|
+
- `locked`: stop competing devctx writers, then retry
|
|
481
|
+
- `corrupted`: back up and remove the file so devctx can recreate it
|
|
482
|
+
- broader inspection: run `smart_doctor` / `smart-context-doctor`
|
|
355
483
|
|
|
356
484
|
### smart_edit
|
|
357
485
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "smart-context-mcp",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.6.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",
|
|
@@ -14,9 +14,11 @@
|
|
|
14
14
|
},
|
|
15
15
|
"bin": {
|
|
16
16
|
"smart-context-headless": "scripts/headless-wrapper.js",
|
|
17
|
+
"smart-context-task": "scripts/task-runner.js",
|
|
17
18
|
"smart-context-server": "scripts/devctx-server.js",
|
|
18
19
|
"smart-context-init": "scripts/init-clients.js",
|
|
19
20
|
"smart-context-report": "scripts/report-metrics.js",
|
|
21
|
+
"smart-context-doctor": "scripts/doctor-state.js",
|
|
20
22
|
"smart-context-protect": "scripts/check-repo-safety.js"
|
|
21
23
|
},
|
|
22
24
|
"exports": {
|
|
@@ -27,11 +29,14 @@
|
|
|
27
29
|
"src/",
|
|
28
30
|
"scripts/claude-hook.js",
|
|
29
31
|
"scripts/check-repo-safety.js",
|
|
32
|
+
"scripts/doctor-state.js",
|
|
30
33
|
"scripts/devctx-server.js",
|
|
31
34
|
"scripts/headless-wrapper.js",
|
|
32
35
|
"scripts/init-clients.js",
|
|
36
|
+
"scripts/task-runner.js",
|
|
33
37
|
"scripts/report-metrics.js",
|
|
34
|
-
"scripts/report-workflow-metrics.js"
|
|
38
|
+
"scripts/report-workflow-metrics.js",
|
|
39
|
+
"scripts/report-adoption-metrics.js"
|
|
35
40
|
],
|
|
36
41
|
"engines": {
|
|
37
42
|
"node": ">=18"
|
|
@@ -54,13 +59,17 @@
|
|
|
54
59
|
"test": "node --test --test-concurrency=1 ./tests/*.test.js",
|
|
55
60
|
"verify": "node ./scripts/verify-features-direct.js",
|
|
56
61
|
"benchmark": "node ./scripts/run-benchmark.js",
|
|
62
|
+
"benchmark:orchestration": "node ./evals/orchestration-benchmark.js",
|
|
63
|
+
"benchmark:orchestration:release": "node ./evals/orchestration-benchmark.js --baseline=./evals/orchestration-release-baseline.json",
|
|
57
64
|
"eval": "node ./evals/harness.js",
|
|
58
65
|
"eval:context": "node ./evals/harness.js --tool=context",
|
|
59
66
|
"eval:both": "node ./evals/harness.js --tool=both",
|
|
60
67
|
"eval:self": "node ./evals/harness.js --root=../.. --corpus=./evals/corpus/self-tasks.json",
|
|
61
68
|
"eval:report": "node ./evals/report.js",
|
|
62
69
|
"report:metrics": "node ./scripts/report-metrics.js",
|
|
63
|
-
"report:workflows": "node ./scripts/report-workflow-metrics.js"
|
|
70
|
+
"report:workflows": "node ./scripts/report-workflow-metrics.js",
|
|
71
|
+
"report:adoption": "node ./scripts/report-adoption-metrics.js",
|
|
72
|
+
"prepublishOnly": "npm test && npm run benchmark:orchestration:release"
|
|
64
73
|
},
|
|
65
74
|
"dependencies": {
|
|
66
75
|
"@modelcontextprotocol/sdk": "^1.13.0",
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import fs from 'node:fs';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import { smartDoctor } from '../src/tools/smart-doctor.js';
|
|
5
|
+
import { setProjectRoot } from '../src/utils/runtime-config.js';
|
|
6
|
+
|
|
7
|
+
const writeStdout = (text) => {
|
|
8
|
+
fs.writeSync(process.stdout.fd, text);
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
const writeStderr = (text) => {
|
|
12
|
+
fs.writeSync(process.stderr.fd, text);
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
const parseArgs = (argv) => {
|
|
16
|
+
const options = {
|
|
17
|
+
projectRoot: process.cwd(),
|
|
18
|
+
json: false,
|
|
19
|
+
verifyIntegrity: true,
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
for (let index = 0; index < argv.length; index += 1) {
|
|
23
|
+
const token = argv[index];
|
|
24
|
+
|
|
25
|
+
if (token === '--project-root') {
|
|
26
|
+
const next = argv[index + 1];
|
|
27
|
+
if (!next || next.startsWith('--')) {
|
|
28
|
+
throw new Error('Missing value for --project-root');
|
|
29
|
+
}
|
|
30
|
+
options.projectRoot = path.resolve(next);
|
|
31
|
+
index += 1;
|
|
32
|
+
continue;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
if (token === '--json') {
|
|
36
|
+
options.json = true;
|
|
37
|
+
continue;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
if (token === '--no-integrity') {
|
|
41
|
+
options.verifyIntegrity = false;
|
|
42
|
+
continue;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
throw new Error(`Unknown argument: ${token}`);
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return options;
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
const printHuman = (result) => {
|
|
52
|
+
const writer = result.overall === 'error' ? writeStderr : writeStdout;
|
|
53
|
+
writer(`devctx doctor: ${result.overall}\n`);
|
|
54
|
+
writer(`${result.message}\n`);
|
|
55
|
+
|
|
56
|
+
for (const check of result.checks ?? []) {
|
|
57
|
+
writer(`\n[${check.status}] ${check.id}: ${check.message}\n`);
|
|
58
|
+
for (const action of check.recommendedActions ?? []) {
|
|
59
|
+
writer(`- ${action}\n`);
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
};
|
|
63
|
+
|
|
64
|
+
const main = async () => {
|
|
65
|
+
const options = parseArgs(process.argv.slice(2));
|
|
66
|
+
setProjectRoot(options.projectRoot);
|
|
67
|
+
|
|
68
|
+
const result = await smartDoctor({
|
|
69
|
+
verifyIntegrity: options.verifyIntegrity,
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
if (options.json) {
|
|
73
|
+
const output = `${JSON.stringify(result, null, 2)}\n`;
|
|
74
|
+
if (result.overall === 'error') {
|
|
75
|
+
writeStderr(output);
|
|
76
|
+
} else {
|
|
77
|
+
writeStdout(output);
|
|
78
|
+
}
|
|
79
|
+
} else {
|
|
80
|
+
printHuman(result);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
process.exitCode = result.overall === 'error' ? 1 : 0;
|
|
84
|
+
};
|
|
85
|
+
|
|
86
|
+
main().catch((error) => {
|
|
87
|
+
writeStderr(`${error.message}\n`);
|
|
88
|
+
process.exitCode = 1;
|
|
89
|
+
});
|
package/scripts/init-clients.js
CHANGED
|
@@ -3,6 +3,7 @@ import { execFileSync } from 'node:child_process';
|
|
|
3
3
|
import fs from 'node:fs';
|
|
4
4
|
import path from 'node:path';
|
|
5
5
|
import { fileURLToPath } from 'node:url';
|
|
6
|
+
import { CLIENT_CONTRACT_RULE_LINES } from '../src/client-contract.js';
|
|
6
7
|
|
|
7
8
|
const currentFilePath = fileURLToPath(import.meta.url);
|
|
8
9
|
const scriptsDir = path.dirname(currentFilePath);
|
|
@@ -164,6 +165,35 @@ const updateCursorConfig = (targetDir, serverConfig, dryRun) => {
|
|
|
164
165
|
writeFile(filePath, `${JSON.stringify(current, null, 2)}\n`, dryRun);
|
|
165
166
|
};
|
|
166
167
|
|
|
168
|
+
const buildCursorAssistedLauncher = (targetDir) => {
|
|
169
|
+
const runnerScript = normalizeCommandPath(path.relative(targetDir, path.join(devctxDir, 'scripts', 'task-runner.js')));
|
|
170
|
+
return `#!/bin/sh
|
|
171
|
+
set -eu
|
|
172
|
+
|
|
173
|
+
script_dir="$(CDPATH= cd -- "$(dirname "$0")" && pwd)"
|
|
174
|
+
project_root="$(CDPATH= cd -- "$script_dir/../.." && pwd)"
|
|
175
|
+
|
|
176
|
+
export DEVCTX_PROJECT_ROOT="$project_root"
|
|
177
|
+
|
|
178
|
+
if [ "$#" -gt 0 ] && [ "\${1#-}" = "$1" ]; then
|
|
179
|
+
subcommand="$1"
|
|
180
|
+
shift
|
|
181
|
+
exec "${process.execPath}" "$project_root/${runnerScript}" "$subcommand" --client cursor "$@"
|
|
182
|
+
fi
|
|
183
|
+
|
|
184
|
+
exec "${process.execPath}" "$project_root/${runnerScript}" task --client cursor "$@"
|
|
185
|
+
`;
|
|
186
|
+
};
|
|
187
|
+
|
|
188
|
+
const updateCursorAssistedLauncher = (targetDir, dryRun) => {
|
|
189
|
+
const filePath = path.join(targetDir, '.devctx', 'bin', 'cursor-devctx');
|
|
190
|
+
writeFile(filePath, buildCursorAssistedLauncher(targetDir), dryRun);
|
|
191
|
+
|
|
192
|
+
if (!dryRun) {
|
|
193
|
+
fs.chmodSync(filePath, 0o755);
|
|
194
|
+
}
|
|
195
|
+
};
|
|
196
|
+
|
|
167
197
|
const updateClaudeConfig = (targetDir, serverConfig, dryRun) => {
|
|
168
198
|
const filePath = path.join(targetDir, '.mcp.json');
|
|
169
199
|
const current = readJson(filePath, { mcpServers: {} });
|
|
@@ -375,15 +405,25 @@ const updatePreCommitHook = (targetDir, dryRun) => {
|
|
|
375
405
|
// Agent rules — instruct agents to prefer devctx tools over built-in ones
|
|
376
406
|
// ---------------------------------------------------------------------------
|
|
377
407
|
|
|
378
|
-
const agentRuleBody = `**First time in project?** Run build_index to enable search/context quality.
|
|
408
|
+
const agentRuleBody = `**First time in project?** Run build_index(incremental=true) to enable search/context quality.
|
|
379
409
|
|
|
380
410
|
Prefer devctx MCP for non-trivial tasks:
|
|
381
|
-
-
|
|
382
|
-
- smart_search(intent=...)
|
|
383
|
-
-
|
|
384
|
-
- smart_shell instead of Shell
|
|
411
|
+
- smart_turn(start, userPrompt, ensureSession=true) before multi-step work
|
|
412
|
+
- smart_context(...) or smart_search(intent=...) to build context cheaply
|
|
413
|
+
- smart_read(outline|signatures|symbol) before full reads
|
|
414
|
+
- smart_shell instead of Shell for safe diagnostics
|
|
415
|
+
- smart_turn(end, event=milestone) after meaningful progress
|
|
416
|
+
|
|
417
|
+
If devctx MCP is installed and enabled, default to devctx on every non-trivial multi-file or multi-step task. Skip it only for genuinely trivial work, and then state why it was skipped.
|
|
418
|
+
|
|
419
|
+
Client contract:
|
|
420
|
+
- ${CLIENT_CONTRACT_RULE_LINES.join('\n- ')}
|
|
385
421
|
|
|
386
|
-
|
|
422
|
+
Cursor assisted mode:
|
|
423
|
+
- For long or continuity-sensitive tasks, prefer the local launcher \`./.devctx/bin/cursor-devctx\`
|
|
424
|
+
- Usage: \`./.devctx/bin/cursor-devctx task --prompt "your task" -- <agent-command> [args...]\`
|
|
425
|
+
- Specialized flows: \`review\`, \`debug\`, \`refactor\`, \`test\`, \`doctor\`, \`status\`, \`checkpoint\`, \`cleanup\`
|
|
426
|
+
- This launcher wraps the prompt with smart_turn(start/end) orchestration and the shared operational contract
|
|
387
427
|
|
|
388
428
|
Reading cascade: outline → signatures → symbol → full (last resort)
|
|
389
429
|
|
|
@@ -393,7 +433,7 @@ Detailed workflows: .cursor/rules/profiles-compact/ (debugging, code-review, ref
|
|
|
393
433
|
|
|
394
434
|
If you didn't use devctx tools in a non-trivial programming task, add at the end:
|
|
395
435
|
|
|
396
|
-
**Note:** devctx not used because: [task too simple | MCP unavailable | index not built | already had sufficient context | native tool more direct for this case]. To use devctx next time: "Use smart-context-mcp: smart_turn(start) → smart_context/smart_search → smart_read → smart_turn(end)"`;
|
|
436
|
+
**Note:** devctx not used because: [task too simple | MCP unavailable | index not built | already had sufficient context | native tool more direct for this case]. To use devctx next time: "Use smart-context-mcp: smart_turn(start, userPrompt, ensureSession=true) → smart_context/smart_search → smart_read → smart_turn(end, event=milestone)"`;
|
|
397
437
|
|
|
398
438
|
const cursorRuleContent = `---
|
|
399
439
|
description: Prefer devctx MCP tools for non-trivial tasks
|
|
@@ -515,6 +555,7 @@ const main = () => {
|
|
|
515
555
|
if (clientSet.has('cursor')) {
|
|
516
556
|
updateCursorConfig(targetDir, serverConfig, options.dryRun);
|
|
517
557
|
updateCursorRule(targetDir, options.dryRun);
|
|
558
|
+
updateCursorAssistedLauncher(targetDir, options.dryRun);
|
|
518
559
|
}
|
|
519
560
|
|
|
520
561
|
if (clientSet.has('codex')) {
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Report adoption metrics - measures real MCP usage in non-trivial tasks
|
|
5
|
+
*
|
|
6
|
+
* Usage:
|
|
7
|
+
* npm run report:adoption
|
|
8
|
+
* npm run report:adoption -- --days 7
|
|
9
|
+
* npm run report:adoption -- --json
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import { withStateDb } from '../src/storage/sqlite.js';
|
|
13
|
+
import { WORKFLOW_DEFINITIONS } from '../src/workflow-tracker.js';
|
|
14
|
+
|
|
15
|
+
const parseArgs = (argv) => {
|
|
16
|
+
const args = {};
|
|
17
|
+
for (let i = 2; i < argv.length; i++) {
|
|
18
|
+
if (argv[i] === '--days' && argv[i + 1]) {
|
|
19
|
+
args.days = parseInt(argv[i + 1], 10);
|
|
20
|
+
i++;
|
|
21
|
+
} else if (argv[i] === '--json') {
|
|
22
|
+
args.json = true;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
return args;
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
const formatPct = (value) => `${value.toFixed(1)}%`;
|
|
29
|
+
const formatNumber = (value) => new Intl.NumberFormat('en-US').format(value);
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Classify if a session represents a non-trivial task
|
|
33
|
+
*/
|
|
34
|
+
const isNonTrivialTask = (sessionEvents, metricsEvents) => {
|
|
35
|
+
// Criteria 1: Multiple operations (≥5)
|
|
36
|
+
if (sessionEvents.length + metricsEvents.length >= 5) return true;
|
|
37
|
+
|
|
38
|
+
// Criteria 2: Large file reads (any file >500 lines)
|
|
39
|
+
const hasLargeFileRead = metricsEvents.some(
|
|
40
|
+
(m) => m.tool === 'Read' && m.raw_tokens > 1500 // ~500 lines
|
|
41
|
+
);
|
|
42
|
+
if (hasLargeFileRead) return true;
|
|
43
|
+
|
|
44
|
+
// Criteria 3: Multiple file reads (≥3)
|
|
45
|
+
const fileReads = metricsEvents.filter((m) => m.tool === 'Read' || m.tool === 'smart_read');
|
|
46
|
+
if (fileReads.length >= 3) return true;
|
|
47
|
+
|
|
48
|
+
// Criteria 4: Repeated searches (≥2)
|
|
49
|
+
const searches = metricsEvents.filter((m) => m.tool === 'Grep' || m.tool === 'smart_search');
|
|
50
|
+
if (searches.length >= 2) return true;
|
|
51
|
+
|
|
52
|
+
// Criteria 5: Workflow classification
|
|
53
|
+
const devctxTools = metricsEvents.filter((m) =>
|
|
54
|
+
['smart_turn', 'smart_context', 'smart_search', 'smart_read', 'smart_shell'].includes(m.tool)
|
|
55
|
+
);
|
|
56
|
+
if (devctxTools.length > 0) return true;
|
|
57
|
+
|
|
58
|
+
return false;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Check if session used devctx tools
|
|
63
|
+
*/
|
|
64
|
+
const usedDevctx = (metricsEvents) => {
|
|
65
|
+
const devctxTools = ['smart_turn', 'smart_context', 'smart_search', 'smart_read', 'smart_shell', 'smart_read_batch'];
|
|
66
|
+
return metricsEvents.some((m) => devctxTools.includes(m.tool));
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
/**
|
|
70
|
+
* Calculate adoption metrics
|
|
71
|
+
*/
|
|
72
|
+
const calculateAdoptionMetrics = (days = 30) => {
|
|
73
|
+
return withStateDb((db) => {
|
|
74
|
+
const cutoff = new Date(Date.now() - days * 24 * 60 * 60 * 1000).toISOString();
|
|
75
|
+
|
|
76
|
+
// Get all sessions since cutoff
|
|
77
|
+
const sessions = db
|
|
78
|
+
.prepare(
|
|
79
|
+
`
|
|
80
|
+
SELECT session_id, snapshot_json, created_at
|
|
81
|
+
FROM sessions
|
|
82
|
+
WHERE created_at >= ?
|
|
83
|
+
ORDER BY created_at DESC
|
|
84
|
+
`
|
|
85
|
+
)
|
|
86
|
+
.all(cutoff);
|
|
87
|
+
|
|
88
|
+
const results = {
|
|
89
|
+
totalSessions: sessions.length,
|
|
90
|
+
nonTrivialTasks: 0,
|
|
91
|
+
tasksWithDevctx: 0,
|
|
92
|
+
adoptionRate: 0,
|
|
93
|
+
byWorkflow: {},
|
|
94
|
+
toolUsage: {},
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
// Initialize workflow stats
|
|
98
|
+
Object.keys(WORKFLOW_DEFINITIONS).forEach((type) => {
|
|
99
|
+
results.byWorkflow[type] = {
|
|
100
|
+
total: 0,
|
|
101
|
+
withDevctx: 0,
|
|
102
|
+
adoptionRate: 0,
|
|
103
|
+
};
|
|
104
|
+
});
|
|
105
|
+
|
|
106
|
+
// Analyze each session
|
|
107
|
+
sessions.forEach((session) => {
|
|
108
|
+
const snapshot = JSON.parse(session.snapshot_json || '{}');
|
|
109
|
+
const sessionId = session.session_id;
|
|
110
|
+
|
|
111
|
+
// Get events for this session
|
|
112
|
+
const sessionEvents = db
|
|
113
|
+
.prepare('SELECT * FROM session_events WHERE session_id = ?')
|
|
114
|
+
.all(sessionId);
|
|
115
|
+
|
|
116
|
+
const metricsEvents = db
|
|
117
|
+
.prepare('SELECT * FROM metrics_events WHERE session_id = ?')
|
|
118
|
+
.all(sessionId);
|
|
119
|
+
|
|
120
|
+
// Check if non-trivial
|
|
121
|
+
if (!isNonTrivialTask(sessionEvents, metricsEvents)) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
results.nonTrivialTasks++;
|
|
126
|
+
|
|
127
|
+
// Check if used devctx
|
|
128
|
+
const hasDevctx = usedDevctx(metricsEvents);
|
|
129
|
+
if (hasDevctx) {
|
|
130
|
+
results.tasksWithDevctx++;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
// Track tool usage
|
|
134
|
+
metricsEvents.forEach((m) => {
|
|
135
|
+
results.toolUsage[m.tool] = (results.toolUsage[m.tool] || 0) + 1;
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
// Classify by workflow if possible
|
|
139
|
+
const goal = snapshot.goal || '';
|
|
140
|
+
let workflowType = null;
|
|
141
|
+
|
|
142
|
+
for (const [type, def] of Object.entries(WORKFLOW_DEFINITIONS)) {
|
|
143
|
+
if (def.pattern.test(goal)) {
|
|
144
|
+
workflowType = type;
|
|
145
|
+
break;
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
if (workflowType) {
|
|
150
|
+
results.byWorkflow[workflowType].total++;
|
|
151
|
+
if (hasDevctx) {
|
|
152
|
+
results.byWorkflow[workflowType].withDevctx++;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
});
|
|
156
|
+
|
|
157
|
+
// Calculate rates
|
|
158
|
+
if (results.nonTrivialTasks > 0) {
|
|
159
|
+
results.adoptionRate = (results.tasksWithDevctx / results.nonTrivialTasks) * 100;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
Object.keys(results.byWorkflow).forEach((type) => {
|
|
163
|
+
const stats = results.byWorkflow[type];
|
|
164
|
+
if (stats.total > 0) {
|
|
165
|
+
stats.adoptionRate = (stats.withDevctx / stats.total) * 100;
|
|
166
|
+
}
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
return results;
|
|
170
|
+
});
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Format and print report
|
|
175
|
+
*/
|
|
176
|
+
const printReport = (metrics, days) => {
|
|
177
|
+
console.log(`\nAdoption Metrics (Last ${days} Days)`);
|
|
178
|
+
console.log('='.repeat(50));
|
|
179
|
+
console.log();
|
|
180
|
+
|
|
181
|
+
console.log(`Total Sessions: ${formatNumber(metrics.totalSessions)}`);
|
|
182
|
+
console.log(`Non-Trivial Tasks: ${formatNumber(metrics.nonTrivialTasks)}`);
|
|
183
|
+
console.log(`Tasks with devctx: ${formatNumber(metrics.tasksWithDevctx)}`);
|
|
184
|
+
console.log();
|
|
185
|
+
|
|
186
|
+
console.log(`Overall Adoption: ${formatPct(metrics.adoptionRate)}`);
|
|
187
|
+
console.log();
|
|
188
|
+
|
|
189
|
+
console.log('By Workflow:');
|
|
190
|
+
Object.entries(metrics.byWorkflow)
|
|
191
|
+
.filter(([, stats]) => stats.total > 0)
|
|
192
|
+
.sort((a, b) => b[1].adoptionRate - a[1].adoptionRate)
|
|
193
|
+
.forEach(([type, stats]) => {
|
|
194
|
+
const def = WORKFLOW_DEFINITIONS[type];
|
|
195
|
+
console.log(
|
|
196
|
+
` ${def.name.padEnd(25)} ${formatPct(stats.adoptionRate).padStart(7)} (${stats.withDevctx}/${stats.total})`
|
|
197
|
+
);
|
|
198
|
+
});
|
|
199
|
+
console.log();
|
|
200
|
+
|
|
201
|
+
console.log('Top devctx Tools:');
|
|
202
|
+
const devctxTools = ['smart_turn', 'smart_context', 'smart_search', 'smart_read', 'smart_shell'];
|
|
203
|
+
Object.entries(metrics.toolUsage)
|
|
204
|
+
.filter(([tool]) => devctxTools.includes(tool))
|
|
205
|
+
.sort((a, b) => b[1] - a[1])
|
|
206
|
+
.slice(0, 5)
|
|
207
|
+
.forEach(([tool, count]) => {
|
|
208
|
+
console.log(` ${tool.padEnd(20)} ${formatNumber(count)} uses`);
|
|
209
|
+
});
|
|
210
|
+
console.log();
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
// Main
|
|
214
|
+
const args = parseArgs(process.argv);
|
|
215
|
+
const days = args.days || 30;
|
|
216
|
+
|
|
217
|
+
try {
|
|
218
|
+
const metrics = calculateAdoptionMetrics(days);
|
|
219
|
+
|
|
220
|
+
if (args.json) {
|
|
221
|
+
console.log(JSON.stringify(metrics, null, 2));
|
|
222
|
+
} else {
|
|
223
|
+
printReport(metrics, days);
|
|
224
|
+
}
|
|
225
|
+
} catch (error) {
|
|
226
|
+
console.error('Error calculating adoption metrics:', error.message);
|
|
227
|
+
process.exit(1);
|
|
228
|
+
}
|
|
@@ -3,6 +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
7
|
|
|
7
8
|
const requireValue = (argv, index, flag) => {
|
|
8
9
|
const value = argv[index + 1];
|
|
@@ -60,6 +61,8 @@ export const createReport = async (options) => {
|
|
|
60
61
|
toolFilter: options.tool,
|
|
61
62
|
invalidLines: result.invalidLines,
|
|
62
63
|
summary: result.summary,
|
|
64
|
+
adoption: result.adoption,
|
|
65
|
+
productQuality: result.productQuality,
|
|
63
66
|
};
|
|
64
67
|
};
|
|
65
68
|
|
|
@@ -73,6 +76,10 @@ const printHuman = (report) => {
|
|
|
73
76
|
console.log(`Raw tokens: ${formatNumber(report.summary.rawTokens)}`);
|
|
74
77
|
console.log(`Final tokens: ${formatNumber(report.summary.compressedTokens)}`);
|
|
75
78
|
console.log(`Saved tokens: ${formatNumber(report.summary.savedTokens)} (${report.summary.savingsPct}%)`);
|
|
79
|
+
console.log(`Net saved: ${formatNumber(report.summary.netSavedTokens)} (${report.summary.netSavingsPct}%)`);
|
|
80
|
+
if (report.summary.overheadTokens > 0) {
|
|
81
|
+
console.log(`Overhead: ${formatNumber(report.summary.overheadTokens)} (${report.summary.overheadPctOfRaw}% of raw)`);
|
|
82
|
+
}
|
|
76
83
|
if (report.invalidLines.length > 0) {
|
|
77
84
|
console.log(`Invalid JSONL: ${report.invalidLines.join(', ')}`);
|
|
78
85
|
}
|
|
@@ -86,13 +93,17 @@ const printHuman = (report) => {
|
|
|
86
93
|
|
|
87
94
|
for (const tool of report.summary.tools) {
|
|
88
95
|
console.log(
|
|
89
|
-
` ${tool.tool.padEnd(14)} count=${formatNumber(tool.count)} raw=${formatNumber(tool.rawTokens)} final=${formatNumber(tool.compressedTokens)} saved=${formatNumber(tool.savedTokens)} (${tool.savingsPct}%)`
|
|
96
|
+
` ${tool.tool.padEnd(14)} count=${formatNumber(tool.count)} raw=${formatNumber(tool.rawTokens)} final=${formatNumber(tool.compressedTokens)} saved=${formatNumber(tool.savedTokens)} (${tool.savingsPct}%) net=${formatNumber(tool.netSavedTokens)} (${tool.netSavingsPct}%)`
|
|
90
97
|
);
|
|
91
98
|
}
|
|
92
99
|
|
|
93
100
|
if (report.adoption) {
|
|
94
101
|
console.log(formatAdoptionReport(report.adoption));
|
|
95
102
|
}
|
|
103
|
+
|
|
104
|
+
if (report.productQuality?.turnsMeasured > 0) {
|
|
105
|
+
console.log(formatProductQualityReport(report.productQuality));
|
|
106
|
+
}
|
|
96
107
|
};
|
|
97
108
|
|
|
98
109
|
export const main = async () => {
|