@apdesign/cursor-roi-tracker 0.5.3 → 0.5.4
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 +4 -0
- package/extension/animus-cursor-roi-tracker.vsix +0 -0
- package/extension/extension.js +40 -5
- package/extension/package.json +1 -1
- package/package.json +1 -1
- package/src/cli-install-hooks.js +2 -0
- package/src/config.js +10 -0
- package/src/mutex-lock.js +1 -0
package/README.md
CHANGED
|
@@ -89,6 +89,8 @@ npx cursor-roi-install-hooks
|
|
|
89
89
|
```json
|
|
90
90
|
{
|
|
91
91
|
"highThreshold": 0.85,
|
|
92
|
+
"bulkInsertThreshold": 8,
|
|
93
|
+
"pureDeleteThreshold": 8,
|
|
92
94
|
"sourceExtensions": [".ts", ".tsx", ".js", ".jsx", ".vue"],
|
|
93
95
|
"excludeRegexes": ["(^|/)dist/", "(^|/)build/", "\\.min\\."],
|
|
94
96
|
"eventsDirectory": [".cursor/animus-ai-events.jsonl", ".cursor/local-ai-events"],
|
|
@@ -103,6 +105,8 @@ npx cursor-roi-install-hooks
|
|
|
103
105
|
### 关键字段说明
|
|
104
106
|
|
|
105
107
|
- `highThreshold`:判定 AI 归因命中阈值
|
|
108
|
+
- `bulkInsertThreshold`:扩展记录 `agent_bulk_insert` 的最小插入行数(默认 8)
|
|
109
|
+
- `pureDeleteThreshold`:扩展记录 `agent_pure_delete` 的最小删除行数(默认 8)
|
|
106
110
|
- `sourceExtensions`:参与统计的代码文件后缀
|
|
107
111
|
- `excludeRegexes`:排除路径/文件规则(正则字符串)
|
|
108
112
|
- `eventsDirectory`:AI 事件文件路径(支持多个)
|
|
Binary file
|
package/extension/extension.js
CHANGED
|
@@ -2,11 +2,12 @@ const fs = require("fs");
|
|
|
2
2
|
const path = require("path");
|
|
3
3
|
const vscode = require("vscode");
|
|
4
4
|
const crypto = require("crypto");
|
|
5
|
+
const { loadConfig } = require("../src/config");
|
|
5
6
|
const { acquireMutexLock, releaseMutexLock, resolveEventLockPath } = require("../src/mutex-lock");
|
|
6
7
|
|
|
7
8
|
const EVENT_FILE_RELATIVE_PATH = path.join(".cursor", "animus-ai-events.jsonl");
|
|
8
|
-
const
|
|
9
|
-
const
|
|
9
|
+
const DEFAULT_BULK_INSERT_THRESHOLD = 8;
|
|
10
|
+
const DEFAULT_PURE_DELETE_THRESHOLD = 8;
|
|
10
11
|
const DEBOUNCE_WINDOW_MS = 200;
|
|
11
12
|
const NEARBY_LINE_DISTANCE = 8;
|
|
12
13
|
const writeChainsByFile = new Map();
|
|
@@ -68,11 +69,13 @@ function handleDocumentChanged(event) {
|
|
|
68
69
|
}
|
|
69
70
|
|
|
70
71
|
const eventFilePath = path.join(workspaceFolder.uri.fsPath, EVENT_FILE_RELATIVE_PATH);
|
|
72
|
+
const thresholds = resolveThresholds(workspaceFolder.uri.fsPath);
|
|
71
73
|
const payloads = buildEventPayloads({
|
|
72
74
|
change,
|
|
73
75
|
relativePath,
|
|
74
76
|
insertLines,
|
|
75
77
|
deletedLines,
|
|
78
|
+
thresholds,
|
|
76
79
|
});
|
|
77
80
|
for (const payload of payloads) {
|
|
78
81
|
enqueueDebouncedEvent(eventFilePath, payload);
|
|
@@ -127,7 +130,7 @@ function enqueueJsonlAppend(filePath, payload) {
|
|
|
127
130
|
writeChainsByFile.set(filePath, next);
|
|
128
131
|
}
|
|
129
132
|
|
|
130
|
-
function buildEventPayloads({ change, relativePath, insertLines, deletedLines }) {
|
|
133
|
+
function buildEventPayloads({ change, relativePath, insertLines, deletedLines, thresholds }) {
|
|
131
134
|
const payloads = [];
|
|
132
135
|
const now = Date.now();
|
|
133
136
|
const insertStartLine = change.range.start.line + 1;
|
|
@@ -135,7 +138,16 @@ function buildEventPayloads({ change, relativePath, insertLines, deletedLines })
|
|
|
135
138
|
const deleteStartLine = change.range.start.line + 1;
|
|
136
139
|
const deleteEndLine = deletedLines > 0 ? deleteStartLine + deletedLines - 1 : null;
|
|
137
140
|
|
|
138
|
-
|
|
141
|
+
const bulkInsertThreshold = positiveIntegerOrFallback(
|
|
142
|
+
thresholds?.bulkInsertThreshold,
|
|
143
|
+
DEFAULT_BULK_INSERT_THRESHOLD
|
|
144
|
+
);
|
|
145
|
+
const pureDeleteThreshold = positiveIntegerOrFallback(
|
|
146
|
+
thresholds?.pureDeleteThreshold,
|
|
147
|
+
DEFAULT_PURE_DELETE_THRESHOLD
|
|
148
|
+
);
|
|
149
|
+
|
|
150
|
+
if (insertLines >= bulkInsertThreshold) {
|
|
139
151
|
payloads.push({
|
|
140
152
|
eventId: crypto.randomUUID(),
|
|
141
153
|
type: "agent_bulk_insert",
|
|
@@ -150,7 +162,7 @@ function buildEventPayloads({ change, relativePath, insertLines, deletedLines })
|
|
|
150
162
|
});
|
|
151
163
|
}
|
|
152
164
|
|
|
153
|
-
if (insertLines === 0 && deletedLines
|
|
165
|
+
if (insertLines === 0 && deletedLines >= pureDeleteThreshold) {
|
|
154
166
|
payloads.push({
|
|
155
167
|
eventId: crypto.randomUUID(),
|
|
156
168
|
type: "agent_pure_delete",
|
|
@@ -166,6 +178,29 @@ function buildEventPayloads({ change, relativePath, insertLines, deletedLines })
|
|
|
166
178
|
return payloads;
|
|
167
179
|
}
|
|
168
180
|
|
|
181
|
+
function resolveThresholds(repoRoot) {
|
|
182
|
+
try {
|
|
183
|
+
const config = loadConfig(repoRoot);
|
|
184
|
+
return {
|
|
185
|
+
bulkInsertThreshold: config.bulkInsertThreshold,
|
|
186
|
+
pureDeleteThreshold: config.pureDeleteThreshold,
|
|
187
|
+
};
|
|
188
|
+
} catch (_error) {
|
|
189
|
+
return {
|
|
190
|
+
bulkInsertThreshold: DEFAULT_BULK_INSERT_THRESHOLD,
|
|
191
|
+
pureDeleteThreshold: DEFAULT_PURE_DELETE_THRESHOLD,
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
function positiveIntegerOrFallback(value, fallback) {
|
|
197
|
+
const num = Number(value);
|
|
198
|
+
if (Number.isFinite(num) && num > 0) {
|
|
199
|
+
return Math.floor(num);
|
|
200
|
+
}
|
|
201
|
+
return fallback;
|
|
202
|
+
}
|
|
203
|
+
|
|
169
204
|
function resolveRepoRootFromEventFile(filePath) {
|
|
170
205
|
return path.dirname(path.dirname(filePath));
|
|
171
206
|
}
|
package/extension/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "animus-cursor-roi-tracker",
|
|
3
3
|
"displayName": "Animus Cursor ROI Tracker",
|
|
4
4
|
"description": "Silently captures bulk agent insert events for ROI attribution.",
|
|
5
|
-
"version": "0.1.
|
|
5
|
+
"version": "0.1.2",
|
|
6
6
|
"publisher": "animus",
|
|
7
7
|
"engines": {
|
|
8
8
|
"vscode": "^1.74.0"
|
package/package.json
CHANGED
package/src/cli-install-hooks.js
CHANGED
|
@@ -161,6 +161,8 @@ function ensureDefaultConfig(repoRoot) {
|
|
|
161
161
|
}
|
|
162
162
|
const payload = {
|
|
163
163
|
highThreshold: 0.85,
|
|
164
|
+
bulkInsertThreshold: 8,
|
|
165
|
+
pureDeleteThreshold: 8,
|
|
164
166
|
sourceExtensions: [".ts", ".tsx", ".js", ".jsx", ".vue", ".css", ".scss", ".html"],
|
|
165
167
|
excludeRegexes: [
|
|
166
168
|
"(^|/)dist/",
|
package/src/config.js
CHANGED
|
@@ -9,6 +9,8 @@ const {
|
|
|
9
9
|
function loadConfig(repoRoot) {
|
|
10
10
|
const defaults = {
|
|
11
11
|
highThreshold: 0.85,
|
|
12
|
+
bulkInsertThreshold: 8,
|
|
13
|
+
pureDeleteThreshold: 8,
|
|
12
14
|
sourceExtensions: DEFAULT_SOURCE_EXTENSIONS,
|
|
13
15
|
excludeRegexes: DEFAULT_EXCLUDE_PATTERNS.map((item) => item.source),
|
|
14
16
|
eventsDirectory: [".cursor/animus-ai-events.jsonl", ".cursor/local-ai-events"],
|
|
@@ -31,6 +33,14 @@ function loadConfig(repoRoot) {
|
|
|
31
33
|
typeof parsed.highThreshold === "number"
|
|
32
34
|
? parsed.highThreshold
|
|
33
35
|
: defaults.highThreshold,
|
|
36
|
+
bulkInsertThreshold:
|
|
37
|
+
Number.isFinite(Number(parsed.bulkInsertThreshold)) && Number(parsed.bulkInsertThreshold) > 0
|
|
38
|
+
? Number(parsed.bulkInsertThreshold)
|
|
39
|
+
: defaults.bulkInsertThreshold,
|
|
40
|
+
pureDeleteThreshold:
|
|
41
|
+
Number.isFinite(Number(parsed.pureDeleteThreshold)) && Number(parsed.pureDeleteThreshold) > 0
|
|
42
|
+
? Number(parsed.pureDeleteThreshold)
|
|
43
|
+
: defaults.pureDeleteThreshold,
|
|
34
44
|
sourceExtensions: Array.isArray(parsed.sourceExtensions)
|
|
35
45
|
? parsed.sourceExtensions
|
|
36
46
|
: defaults.sourceExtensions,
|
package/src/mutex-lock.js
CHANGED
|
@@ -21,6 +21,7 @@ async function acquireMutexLock(lockDirPath, options = {}) {
|
|
|
21
21
|
startMs: Date.now(),
|
|
22
22
|
};
|
|
23
23
|
const ownerPath = path.join(lockDirPath, LOCK_OWNER_FILE);
|
|
24
|
+
await fs.promises.mkdir(path.dirname(lockDirPath), { recursive: true });
|
|
24
25
|
const startedAt = Date.now();
|
|
25
26
|
let attempt = 0;
|
|
26
27
|
|