@agents-tower/core 0.1.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/dist/adapters/claude.d.ts +2 -0
- package/dist/adapters/claude.js +43 -0
- package/dist/adapters/claude.js.map +1 -0
- package/dist/adapters/codex-cloud.d.ts +4 -0
- package/dist/adapters/codex-cloud.js +80 -0
- package/dist/adapters/codex-cloud.js.map +1 -0
- package/dist/adapters/codex-local.d.ts +17 -0
- package/dist/adapters/codex-local.js +180 -0
- package/dist/adapters/codex-local.js.map +1 -0
- package/dist/adapters/cursor-cloud.d.ts +2 -0
- package/dist/adapters/cursor-cloud.js +54 -0
- package/dist/adapters/cursor-cloud.js.map +1 -0
- package/dist/adapters/cursor-local.d.ts +2 -0
- package/dist/adapters/cursor-local.js +37 -0
- package/dist/adapters/cursor-local.js.map +1 -0
- package/dist/adapters/helpers.d.ts +15 -0
- package/dist/adapters/helpers.js +33 -0
- package/dist/adapters/helpers.js.map +1 -0
- package/dist/adapters/hermes.d.ts +2 -0
- package/dist/adapters/hermes.js +41 -0
- package/dist/adapters/hermes.js.map +1 -0
- package/dist/adapters/index.d.ts +2 -0
- package/dist/adapters/index.js +37 -0
- package/dist/adapters/index.js.map +1 -0
- package/dist/adapters/openclaw.d.ts +2 -0
- package/dist/adapters/openclaw.js +41 -0
- package/dist/adapters/openclaw.js.map +1 -0
- package/dist/adapters/presence.d.ts +2 -0
- package/dist/adapters/presence.js +36 -0
- package/dist/adapters/presence.js.map +1 -0
- package/dist/adapters/static-source.d.ts +15 -0
- package/dist/adapters/static-source.js +41 -0
- package/dist/adapters/static-source.js.map +1 -0
- package/dist/adapters/types.d.ts +44 -0
- package/dist/adapters/types.js +3 -0
- package/dist/adapters/types.js.map +1 -0
- package/dist/app-server.d.ts +94 -0
- package/dist/app-server.js +382 -0
- package/dist/app-server.js.map +1 -0
- package/dist/app-settings.d.ts +52 -0
- package/dist/app-settings.js +268 -0
- package/dist/app-settings.js.map +1 -0
- package/dist/appearance.d.ts +6 -0
- package/dist/appearance.js +84 -0
- package/dist/appearance.js.map +1 -0
- package/dist/aseprite.d.ts +4 -0
- package/dist/aseprite.js +42 -0
- package/dist/aseprite.js.map +1 -0
- package/dist/claude-agent-sdk.d.ts +44 -0
- package/dist/claude-agent-sdk.js +347 -0
- package/dist/claude-agent-sdk.js.map +1 -0
- package/dist/claude-home-cache.d.ts +61 -0
- package/dist/claude-home-cache.js +438 -0
- package/dist/claude-home-cache.js.map +1 -0
- package/dist/claude.d.ts +154 -0
- package/dist/claude.js +3495 -0
- package/dist/claude.js.map +1 -0
- package/dist/cloud.d.ts +2 -0
- package/dist/cloud.js +25 -0
- package/dist/cloud.js.map +1 -0
- package/dist/codex-command.d.ts +25 -0
- package/dist/codex-command.js +235 -0
- package/dist/codex-command.js.map +1 -0
- package/dist/codex-session-files.d.ts +8 -0
- package/dist/codex-session-files.js +509 -0
- package/dist/codex-session-files.js.map +1 -0
- package/dist/codex-thread-query.d.ts +14 -0
- package/dist/codex-thread-query.js +82 -0
- package/dist/codex-thread-query.js.map +1 -0
- package/dist/cursor-cloud-data.d.ts +34 -0
- package/dist/cursor-cloud-data.js +344 -0
- package/dist/cursor-cloud-data.js.map +1 -0
- package/dist/cursor-lib/local-discovery.d.ts +17 -0
- package/dist/cursor-lib/local-discovery.js +251 -0
- package/dist/cursor-lib/local-discovery.js.map +1 -0
- package/dist/cursor-lib/shared.d.ts +5 -0
- package/dist/cursor-lib/shared.js +88 -0
- package/dist/cursor-lib/shared.js.map +1 -0
- package/dist/cursor.d.ts +8 -0
- package/dist/cursor.js +1584 -0
- package/dist/cursor.js.map +1 -0
- package/dist/domain/codex-turn-semantics.d.ts +4 -0
- package/dist/domain/codex-turn-semantics.js +49 -0
- package/dist/domain/codex-turn-semantics.js.map +1 -0
- package/dist/domain/workload-policy.d.ts +8 -0
- package/dist/domain/workload-policy.js +268 -0
- package/dist/domain/workload-policy.js.map +1 -0
- package/dist/domain/workspace-activity.d.ts +10 -0
- package/dist/domain/workspace-activity.js +372 -0
- package/dist/domain/workspace-activity.js.map +1 -0
- package/dist/goal.d.ts +11 -0
- package/dist/goal.js +76 -0
- package/dist/goal.js.map +1 -0
- package/dist/hermes-hook-install.d.ts +12 -0
- package/dist/hermes-hook-install.js +335 -0
- package/dist/hermes-hook-install.js.map +1 -0
- package/dist/hermes.d.ts +97 -0
- package/dist/hermes.js +2446 -0
- package/dist/hermes.js.map +1 -0
- package/dist/index.d.ts +28 -0
- package/dist/index.js +47 -0
- package/dist/index.js.map +1 -0
- package/dist/live-monitor-lib/events.d.ts +43 -0
- package/dist/live-monitor-lib/events.js +1463 -0
- package/dist/live-monitor-lib/events.js.map +1 -0
- package/dist/live-monitor-lib/rollout-hooks.d.ts +13 -0
- package/dist/live-monitor-lib/rollout-hooks.js +253 -0
- package/dist/live-monitor-lib/rollout-hooks.js.map +1 -0
- package/dist/live-monitor.d.ts +102 -0
- package/dist/live-monitor.js +1461 -0
- package/dist/live-monitor.js.map +1 -0
- package/dist/local-thread-selection.d.ts +2 -0
- package/dist/local-thread-selection.js +58 -0
- package/dist/local-thread-selection.js.map +1 -0
- package/dist/openclaw.d.ts +41 -0
- package/dist/openclaw.js +715 -0
- package/dist/openclaw.js.map +1 -0
- package/dist/presence.d.ts +9 -0
- package/dist/presence.js +108 -0
- package/dist/presence.js.map +1 -0
- package/dist/project-identity.d.ts +7 -0
- package/dist/project-identity.js +162 -0
- package/dist/project-identity.js.map +1 -0
- package/dist/project-paths.d.ts +23 -0
- package/dist/project-paths.js +394 -0
- package/dist/project-paths.js.map +1 -0
- package/dist/project-storage.d.ts +7 -0
- package/dist/project-storage.js +79 -0
- package/dist/project-storage.js.map +1 -0
- package/dist/room-config.d.ts +21 -0
- package/dist/room-config.js +230 -0
- package/dist/room-config.js.map +1 -0
- package/dist/services/project-discovery.d.ts +1 -0
- package/dist/services/project-discovery.js +16 -0
- package/dist/services/project-discovery.js.map +1 -0
- package/dist/services/project-live-monitor.d.ts +1 -0
- package/dist/services/project-live-monitor.js +6 -0
- package/dist/services/project-live-monitor.js.map +1 -0
- package/dist/services/project-snapshot-coordinator.d.ts +49 -0
- package/dist/services/project-snapshot-coordinator.js +152 -0
- package/dist/services/project-snapshot-coordinator.js.map +1 -0
- package/dist/services/refresh-scheduler.d.ts +8 -0
- package/dist/services/refresh-scheduler.js +44 -0
- package/dist/services/refresh-scheduler.js.map +1 -0
- package/dist/services/snapshot-assembler.d.ts +8 -0
- package/dist/services/snapshot-assembler.js +167 -0
- package/dist/services/snapshot-assembler.js.map +1 -0
- package/dist/services/thread-store.d.ts +4 -0
- package/dist/services/thread-store.js +8 -0
- package/dist/services/thread-store.js.map +1 -0
- package/dist/snapshot-lib/activity-summary.d.ts +3 -0
- package/dist/snapshot-lib/activity-summary.js +220 -0
- package/dist/snapshot-lib/activity-summary.js.map +1 -0
- package/dist/snapshot-lib/dashboard-builder.d.ts +21 -0
- package/dist/snapshot-lib/dashboard-builder.js +146 -0
- package/dist/snapshot-lib/dashboard-builder.js.map +1 -0
- package/dist/snapshot-lib/thread-summary.d.ts +27 -0
- package/dist/snapshot-lib/thread-summary.js +953 -0
- package/dist/snapshot-lib/thread-summary.js.map +1 -0
- package/dist/snapshot.d.ts +2 -0
- package/dist/snapshot.js +21 -0
- package/dist/snapshot.js.map +1 -0
- package/dist/types.d.ts +368 -0
- package/dist/types.js +3 -0
- package/dist/types.js.map +1 -0
- package/dist/utils/json.d.ts +4 -0
- package/dist/utils/json.js +29 -0
- package/dist/utils/json.js.map +1 -0
- package/dist/utils/text.d.ts +3 -0
- package/dist/utils/text.js +26 -0
- package/dist/utils/text.js.map +1 -0
- package/dist/web-search.d.ts +1 -0
- package/dist/web-search.js +52 -0
- package/dist/web-search.js.map +1 -0
- package/dist/workload.d.ts +1 -0
- package/dist/workload.js +12 -0
- package/dist/workload.js.map +1 -0
- package/package.json +19 -0
package/dist/cursor.js
ADDED
|
@@ -0,0 +1,1584 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.loadCursorCloudProjectSnapshotData = exports.loadCursorAgents = exports.describeCursorAgentAvailability = exports.cursorApiKeyConfigured = exports.cursorAgentMatchesRepository = exports.sourceKindFromModel = exports.normalizeRepositoryUrl = exports.gitRemoteOriginUrl = exports.cursorStatusToActivityState = void 0;
|
|
4
|
+
exports.cursorHooksFilePath = cursorHooksFilePath;
|
|
5
|
+
exports.loadCursorLocalProjectSnapshotData = loadCursorLocalProjectSnapshotData;
|
|
6
|
+
const node_child_process_1 = require("node:child_process");
|
|
7
|
+
const promises_1 = require("node:fs/promises");
|
|
8
|
+
const node_path_1 = require("node:path");
|
|
9
|
+
const node_sqlite_1 = require("node:sqlite");
|
|
10
|
+
const node_util_1 = require("node:util");
|
|
11
|
+
const appearance_1 = require("./appearance");
|
|
12
|
+
const local_discovery_1 = require("./cursor-lib/local-discovery");
|
|
13
|
+
const shared_1 = require("./cursor-lib/shared");
|
|
14
|
+
var shared_2 = require("./cursor-lib/shared");
|
|
15
|
+
Object.defineProperty(exports, "cursorStatusToActivityState", { enumerable: true, get: function () { return shared_2.cursorStatusToActivityState; } });
|
|
16
|
+
Object.defineProperty(exports, "gitRemoteOriginUrl", { enumerable: true, get: function () { return shared_2.gitRemoteOriginUrl; } });
|
|
17
|
+
Object.defineProperty(exports, "normalizeRepositoryUrl", { enumerable: true, get: function () { return shared_2.normalizeRepositoryUrl; } });
|
|
18
|
+
Object.defineProperty(exports, "sourceKindFromModel", { enumerable: true, get: function () { return shared_2.sourceKindFromModel; } });
|
|
19
|
+
var cursor_cloud_data_1 = require("./cursor-cloud-data");
|
|
20
|
+
Object.defineProperty(exports, "cursorAgentMatchesRepository", { enumerable: true, get: function () { return cursor_cloud_data_1.cursorAgentMatchesRepository; } });
|
|
21
|
+
Object.defineProperty(exports, "cursorApiKeyConfigured", { enumerable: true, get: function () { return cursor_cloud_data_1.cursorApiKeyConfigured; } });
|
|
22
|
+
Object.defineProperty(exports, "describeCursorAgentAvailability", { enumerable: true, get: function () { return cursor_cloud_data_1.describeCursorAgentAvailability; } });
|
|
23
|
+
Object.defineProperty(exports, "loadCursorAgents", { enumerable: true, get: function () { return cursor_cloud_data_1.loadCursorAgents; } });
|
|
24
|
+
Object.defineProperty(exports, "loadCursorCloudProjectSnapshotData", { enumerable: true, get: function () { return cursor_cloud_data_1.loadCursorCloudProjectSnapshotData; } });
|
|
25
|
+
const project_storage_1 = require("./project-storage");
|
|
26
|
+
const project_paths_1 = require("./project-paths");
|
|
27
|
+
const execFileAsync = (0, node_util_1.promisify)(node_child_process_1.execFile);
|
|
28
|
+
const CURSOR_LOCAL_ACTIVE_WINDOW_MS = 2 * 60 * 1000;
|
|
29
|
+
const CURSOR_LOCAL_DONE_WINDOW_MS = 15 * 60 * 1000;
|
|
30
|
+
const CURSOR_LOCAL_EVENT_WINDOW_MS = 2 * 60 * 1000;
|
|
31
|
+
const CURSOR_HOOK_ACTIVE_WINDOW_MS = 2 * 60 * 1000;
|
|
32
|
+
const CURSOR_HOOK_DONE_WINDOW_MS = 15 * 60 * 1000;
|
|
33
|
+
const CURSOR_HOOK_EVENT_WINDOW_MS = 5 * 60 * 1000;
|
|
34
|
+
const CURSOR_HOOK_FUTURE_TOLERANCE_MS = 30 * 1000;
|
|
35
|
+
function pathExists(path) {
|
|
36
|
+
return (0, promises_1.stat)(path).then(() => true).catch(() => false);
|
|
37
|
+
}
|
|
38
|
+
function cursorComposerTimestamp(composer) {
|
|
39
|
+
return Math.max(composer.lastUpdatedAt ?? 0, composer.createdAt ?? 0, composer.activeBranch?.lastInteractionAt ?? 0, ...(Array.isArray(composer.branches)
|
|
40
|
+
? composer.branches.map((branch) => branch.lastInteractionAt ?? 0)
|
|
41
|
+
: [0]));
|
|
42
|
+
}
|
|
43
|
+
function extractBalancedJsonSegment(text, startIndex) {
|
|
44
|
+
const opening = text[startIndex];
|
|
45
|
+
const closing = opening === "[" ? "]" : opening === "{" ? "}" : null;
|
|
46
|
+
if (!closing) {
|
|
47
|
+
return null;
|
|
48
|
+
}
|
|
49
|
+
let depth = 0;
|
|
50
|
+
let inString = false;
|
|
51
|
+
let escaping = false;
|
|
52
|
+
for (let index = startIndex; index < text.length; index += 1) {
|
|
53
|
+
const char = text[index];
|
|
54
|
+
if (inString) {
|
|
55
|
+
if (escaping) {
|
|
56
|
+
escaping = false;
|
|
57
|
+
continue;
|
|
58
|
+
}
|
|
59
|
+
if (char === "\\") {
|
|
60
|
+
escaping = true;
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
if (char === "\"") {
|
|
64
|
+
inString = false;
|
|
65
|
+
}
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
if (char === "\"") {
|
|
69
|
+
inString = true;
|
|
70
|
+
continue;
|
|
71
|
+
}
|
|
72
|
+
if (char === opening) {
|
|
73
|
+
depth += 1;
|
|
74
|
+
continue;
|
|
75
|
+
}
|
|
76
|
+
if (char === closing) {
|
|
77
|
+
depth -= 1;
|
|
78
|
+
if (depth === 0) {
|
|
79
|
+
return text.slice(startIndex, index + 1);
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
return null;
|
|
84
|
+
}
|
|
85
|
+
function extractAsciiStrings(raw, minimumLength = 4) {
|
|
86
|
+
const strings = [];
|
|
87
|
+
let current = "";
|
|
88
|
+
for (const byte of raw.values()) {
|
|
89
|
+
if (byte >= 32 && byte <= 126) {
|
|
90
|
+
current += String.fromCharCode(byte);
|
|
91
|
+
continue;
|
|
92
|
+
}
|
|
93
|
+
if (current.length >= minimumLength) {
|
|
94
|
+
strings.push(current);
|
|
95
|
+
}
|
|
96
|
+
current = "";
|
|
97
|
+
}
|
|
98
|
+
if (current.length >= minimumLength) {
|
|
99
|
+
strings.push(current);
|
|
100
|
+
}
|
|
101
|
+
return strings;
|
|
102
|
+
}
|
|
103
|
+
function extractEmbeddedJsonTextCandidates(text, key, opening) {
|
|
104
|
+
const candidates = [];
|
|
105
|
+
let fromIndex = 0;
|
|
106
|
+
while (fromIndex < text.length) {
|
|
107
|
+
const keyIndex = text.indexOf(key, fromIndex);
|
|
108
|
+
if (keyIndex < 0) {
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
let jsonStart = keyIndex + key.length;
|
|
112
|
+
const maxLookahead = Math.min(text.length, jsonStart + 12);
|
|
113
|
+
while (jsonStart < maxLookahead && text[jsonStart] !== opening) {
|
|
114
|
+
jsonStart += 1;
|
|
115
|
+
}
|
|
116
|
+
fromIndex = keyIndex + key.length;
|
|
117
|
+
if (text[jsonStart] !== opening) {
|
|
118
|
+
continue;
|
|
119
|
+
}
|
|
120
|
+
const parsed = extractBalancedJsonSegment(text, jsonStart);
|
|
121
|
+
if (!parsed) {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
try {
|
|
125
|
+
candidates.push({
|
|
126
|
+
value: JSON.parse(parsed),
|
|
127
|
+
raw: parsed
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
catch {
|
|
131
|
+
continue;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
return candidates;
|
|
135
|
+
}
|
|
136
|
+
function extractEmbeddedJsonCandidates(raw, key, opening) {
|
|
137
|
+
const candidates = extractAsciiStrings(raw)
|
|
138
|
+
.flatMap((text) => extractEmbeddedJsonTextCandidates(text, key, opening));
|
|
139
|
+
if (candidates.length > 0) {
|
|
140
|
+
return candidates;
|
|
141
|
+
}
|
|
142
|
+
const rawCandidates = [];
|
|
143
|
+
const keyBytes = Buffer.from(key, "utf8");
|
|
144
|
+
const openingCode = opening.charCodeAt(0);
|
|
145
|
+
const closingCode = (opening === "[" ? "]" : "}").charCodeAt(0);
|
|
146
|
+
let fromIndex = 0;
|
|
147
|
+
while (fromIndex < raw.length) {
|
|
148
|
+
const keyIndex = raw.indexOf(keyBytes, fromIndex);
|
|
149
|
+
if (keyIndex < 0) {
|
|
150
|
+
break;
|
|
151
|
+
}
|
|
152
|
+
let jsonStart = keyIndex + keyBytes.length;
|
|
153
|
+
const maxLookahead = Math.min(raw.length, jsonStart + 64);
|
|
154
|
+
while (jsonStart < maxLookahead && raw[jsonStart] !== openingCode) {
|
|
155
|
+
jsonStart += 1;
|
|
156
|
+
}
|
|
157
|
+
fromIndex = keyIndex + keyBytes.length;
|
|
158
|
+
if (raw[jsonStart] !== openingCode) {
|
|
159
|
+
continue;
|
|
160
|
+
}
|
|
161
|
+
let depth = 0;
|
|
162
|
+
let inString = false;
|
|
163
|
+
let escaping = false;
|
|
164
|
+
let parsed = "";
|
|
165
|
+
let completed = false;
|
|
166
|
+
for (let index = jsonStart; index < raw.length; index += 1) {
|
|
167
|
+
const byte = raw[index];
|
|
168
|
+
if (byte < 32 || byte > 126) {
|
|
169
|
+
continue;
|
|
170
|
+
}
|
|
171
|
+
const char = String.fromCharCode(byte);
|
|
172
|
+
parsed += char;
|
|
173
|
+
if (inString) {
|
|
174
|
+
if (escaping) {
|
|
175
|
+
escaping = false;
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
if (char === "\\") {
|
|
179
|
+
escaping = true;
|
|
180
|
+
continue;
|
|
181
|
+
}
|
|
182
|
+
if (char === "\"") {
|
|
183
|
+
inString = false;
|
|
184
|
+
}
|
|
185
|
+
continue;
|
|
186
|
+
}
|
|
187
|
+
if (char === "\"") {
|
|
188
|
+
inString = true;
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (byte === openingCode) {
|
|
192
|
+
depth += 1;
|
|
193
|
+
continue;
|
|
194
|
+
}
|
|
195
|
+
if (byte === closingCode) {
|
|
196
|
+
depth -= 1;
|
|
197
|
+
if (depth === 0) {
|
|
198
|
+
completed = true;
|
|
199
|
+
break;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (!completed || parsed.length === 0) {
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
try {
|
|
207
|
+
rawCandidates.push({
|
|
208
|
+
value: JSON.parse(parsed),
|
|
209
|
+
raw: parsed
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
}
|
|
216
|
+
return rawCandidates;
|
|
217
|
+
}
|
|
218
|
+
function pickBestCandidate(candidates, score) {
|
|
219
|
+
return candidates
|
|
220
|
+
.sort((left, right) => {
|
|
221
|
+
const scoreDelta = score(right.value) - score(left.value);
|
|
222
|
+
if (scoreDelta !== 0) {
|
|
223
|
+
return scoreDelta;
|
|
224
|
+
}
|
|
225
|
+
return right.raw.length - left.raw.length;
|
|
226
|
+
})[0]?.value ?? null;
|
|
227
|
+
}
|
|
228
|
+
function parseCursorLocalWorkspaceState(raw) {
|
|
229
|
+
const composerData = pickBestCandidate(extractEmbeddedJsonCandidates(raw, "composer.composerData", "{"), (value) => Math.max(...(Array.isArray(value.allComposers) ? value.allComposers.map((composer) => cursorComposerTimestamp(composer)) : [0])));
|
|
230
|
+
const prompts = pickBestCandidate(extractEmbeddedJsonCandidates(raw, "aiService.prompts", "["), (value) => Array.isArray(value) ? value.length : 0) ?? [];
|
|
231
|
+
const generations = pickBestCandidate(extractEmbeddedJsonCandidates(raw, "aiService.generations", "["), (value) => Math.max(...(Array.isArray(value) ? value.map((entry) => entry.unixMs ?? 0) : [0]))) ?? [];
|
|
232
|
+
const backgroundComposer = pickBestCandidate(extractEmbeddedJsonCandidates(raw, "workbench.backgroundComposer.workspacePersistentData", "{"), (value) => `${value.cachedSelectedGitState?.ref ?? ""}${value.cachedSelectedGitState?.continueRef ?? ""}`.length);
|
|
233
|
+
return {
|
|
234
|
+
composerData,
|
|
235
|
+
prompts,
|
|
236
|
+
generations,
|
|
237
|
+
backgroundComposer
|
|
238
|
+
};
|
|
239
|
+
}
|
|
240
|
+
function parseCursorJsonValue(input) {
|
|
241
|
+
if (typeof input !== "string" || input.trim().length === 0) {
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
try {
|
|
245
|
+
return JSON.parse(input);
|
|
246
|
+
}
|
|
247
|
+
catch {
|
|
248
|
+
return null;
|
|
249
|
+
}
|
|
250
|
+
}
|
|
251
|
+
function loadCursorLocalWorkspaceStateFromSqlite(filePath) {
|
|
252
|
+
let database = null;
|
|
253
|
+
try {
|
|
254
|
+
database = new node_sqlite_1.DatabaseSync(filePath, { readOnly: true });
|
|
255
|
+
const readValue = (key) => {
|
|
256
|
+
const row = database
|
|
257
|
+
?.prepare("select value from ItemTable where key = ?")
|
|
258
|
+
.get(key);
|
|
259
|
+
return typeof row?.value === "string" ? row.value : null;
|
|
260
|
+
};
|
|
261
|
+
const composerData = parseCursorJsonValue(readValue("composer.composerData"));
|
|
262
|
+
const prompts = parseCursorJsonValue(readValue("aiService.prompts")) ?? [];
|
|
263
|
+
const generations = parseCursorJsonValue(readValue("aiService.generations")) ?? [];
|
|
264
|
+
const backgroundComposer = parseCursorJsonValue(readValue("workbench.backgroundComposer.workspacePersistentData"));
|
|
265
|
+
if (!composerData && prompts.length === 0 && generations.length === 0 && !backgroundComposer) {
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
return {
|
|
269
|
+
composerData,
|
|
270
|
+
prompts,
|
|
271
|
+
generations,
|
|
272
|
+
backgroundComposer
|
|
273
|
+
};
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
return null;
|
|
277
|
+
}
|
|
278
|
+
finally {
|
|
279
|
+
database?.close();
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
async function loadCursorLocalWorkspaceState(workspace) {
|
|
283
|
+
for (const filePath of [workspace.stateFilePath, workspace.backupFilePath]) {
|
|
284
|
+
if (!await pathExists(filePath)) {
|
|
285
|
+
continue;
|
|
286
|
+
}
|
|
287
|
+
try {
|
|
288
|
+
const fromSqlite = loadCursorLocalWorkspaceStateFromSqlite(filePath);
|
|
289
|
+
if (fromSqlite?.composerData || fromSqlite?.prompts.length || fromSqlite?.generations.length) {
|
|
290
|
+
return fromSqlite;
|
|
291
|
+
}
|
|
292
|
+
const raw = await (0, promises_1.readFile)(filePath);
|
|
293
|
+
const parsed = parseCursorLocalWorkspaceState(raw);
|
|
294
|
+
if (parsed.composerData || parsed.prompts.length > 0 || parsed.generations.length > 0) {
|
|
295
|
+
return parsed;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
298
|
+
catch {
|
|
299
|
+
continue;
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
return null;
|
|
303
|
+
}
|
|
304
|
+
function orderedCursorComposers(composerData) {
|
|
305
|
+
if (!composerData || !Array.isArray(composerData.allComposers)) {
|
|
306
|
+
return [];
|
|
307
|
+
}
|
|
308
|
+
const activeComposers = composerData.allComposers.filter((composer) => composer && composer.isArchived !== true);
|
|
309
|
+
const byId = new Map(activeComposers.map((composer) => [composer.composerId, composer]));
|
|
310
|
+
const orderedIds = [
|
|
311
|
+
...(Array.isArray(composerData.lastFocusedComposerIds) ? composerData.lastFocusedComposerIds : []),
|
|
312
|
+
...(Array.isArray(composerData.selectedComposerIds) ? composerData.selectedComposerIds : []),
|
|
313
|
+
...activeComposers
|
|
314
|
+
.slice()
|
|
315
|
+
.sort((left, right) => cursorComposerTimestamp(right) - cursorComposerTimestamp(left))
|
|
316
|
+
.map((composer) => composer.composerId)
|
|
317
|
+
];
|
|
318
|
+
const ordered = [];
|
|
319
|
+
const seen = new Set();
|
|
320
|
+
for (const composerId of orderedIds) {
|
|
321
|
+
if (typeof composerId !== "string" || seen.has(composerId)) {
|
|
322
|
+
continue;
|
|
323
|
+
}
|
|
324
|
+
const composer = byId.get(composerId);
|
|
325
|
+
if (!composer) {
|
|
326
|
+
continue;
|
|
327
|
+
}
|
|
328
|
+
seen.add(composerId);
|
|
329
|
+
ordered.push(composer);
|
|
330
|
+
}
|
|
331
|
+
return ordered;
|
|
332
|
+
}
|
|
333
|
+
function cursorPrimaryComposerId(composerData, latestGenerationMs) {
|
|
334
|
+
const composers = orderedCursorComposers(composerData);
|
|
335
|
+
if (composers.length === 0) {
|
|
336
|
+
return null;
|
|
337
|
+
}
|
|
338
|
+
if (Number.isFinite(latestGenerationMs) && latestGenerationMs > 0) {
|
|
339
|
+
const matchedComposer = composers
|
|
340
|
+
.map((composer) => ({
|
|
341
|
+
composer,
|
|
342
|
+
distanceMs: Math.abs(cursorComposerTimestamp(composer) - latestGenerationMs)
|
|
343
|
+
}))
|
|
344
|
+
.sort((left, right) => left.distanceMs - right.distanceMs)[0] ?? null;
|
|
345
|
+
if (matchedComposer && matchedComposer.distanceMs <= CURSOR_LOCAL_ACTIVE_WINDOW_MS) {
|
|
346
|
+
return matchedComposer.composer.composerId;
|
|
347
|
+
}
|
|
348
|
+
}
|
|
349
|
+
return composers[0]?.composerId ?? null;
|
|
350
|
+
}
|
|
351
|
+
async function cursorAgentLoopState() {
|
|
352
|
+
const logsDir = await (0, local_discovery_1.cursorLogsDir)();
|
|
353
|
+
if (!logsDir) {
|
|
354
|
+
return { active: false, updatedAtMs: null };
|
|
355
|
+
}
|
|
356
|
+
const directories = await (0, promises_1.readdir)(logsDir, { withFileTypes: true }).catch(() => []);
|
|
357
|
+
const recentDirs = directories
|
|
358
|
+
.filter((entry) => entry.isDirectory())
|
|
359
|
+
.map((entry) => entry.name)
|
|
360
|
+
.sort()
|
|
361
|
+
.slice(-5)
|
|
362
|
+
.reverse();
|
|
363
|
+
let lastEvent = null;
|
|
364
|
+
const pattern = /^(\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}\.\d{3}).*?\[PowerMainService\] (Started|Stopping) wakelock .*reason="agent-loop"/;
|
|
365
|
+
for (const dirName of recentDirs) {
|
|
366
|
+
const filePath = (0, node_path_1.join)(logsDir, dirName, "main.log");
|
|
367
|
+
if (!await pathExists(filePath)) {
|
|
368
|
+
continue;
|
|
369
|
+
}
|
|
370
|
+
const raw = await (0, promises_1.readFile)(filePath, "utf8").catch(() => "");
|
|
371
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
372
|
+
const match = line.match(pattern);
|
|
373
|
+
if (!match) {
|
|
374
|
+
continue;
|
|
375
|
+
}
|
|
376
|
+
const updatedAtMs = Date.parse(match[1].replace(" ", "T") + "Z");
|
|
377
|
+
if (!Number.isFinite(updatedAtMs)) {
|
|
378
|
+
continue;
|
|
379
|
+
}
|
|
380
|
+
lastEvent = {
|
|
381
|
+
active: match[2] === "Started",
|
|
382
|
+
updatedAtMs
|
|
383
|
+
};
|
|
384
|
+
}
|
|
385
|
+
if (lastEvent) {
|
|
386
|
+
break;
|
|
387
|
+
}
|
|
388
|
+
}
|
|
389
|
+
return lastEvent ?? { active: false, updatedAtMs: null };
|
|
390
|
+
}
|
|
391
|
+
function cursorLocalPromptDetail(prompt, fallback) {
|
|
392
|
+
if (prompt && prompt.trim().length > 0) {
|
|
393
|
+
const normalized = prompt.replace(/\s+/g, " ").trim();
|
|
394
|
+
return normalized.length <= 88 ? normalized : `${normalized.slice(0, 87)}…`;
|
|
395
|
+
}
|
|
396
|
+
return fallback;
|
|
397
|
+
}
|
|
398
|
+
function cleanCursorTranscriptUserText(text) {
|
|
399
|
+
const normalized = text
|
|
400
|
+
.replace(/<\/?user_query>/gi, " ")
|
|
401
|
+
.replace(/\s+/g, " ")
|
|
402
|
+
.trim();
|
|
403
|
+
return normalized.length > 0 ? normalized : null;
|
|
404
|
+
}
|
|
405
|
+
function cleanCursorTranscriptAssistantText(text) {
|
|
406
|
+
const normalized = text.replace(/\s+/g, " ").trim();
|
|
407
|
+
return normalized.length > 0 ? normalized : null;
|
|
408
|
+
}
|
|
409
|
+
function cursorTranscriptContentText(role, content) {
|
|
410
|
+
if (!Array.isArray(content)) {
|
|
411
|
+
return null;
|
|
412
|
+
}
|
|
413
|
+
const joined = content
|
|
414
|
+
.filter((part) => part && part.type === "text" && typeof part.text === "string")
|
|
415
|
+
.map((part) => part.text ?? "")
|
|
416
|
+
.join(" ");
|
|
417
|
+
if (!joined.trim()) {
|
|
418
|
+
return null;
|
|
419
|
+
}
|
|
420
|
+
return role === "user"
|
|
421
|
+
? cleanCursorTranscriptUserText(joined)
|
|
422
|
+
: cleanCursorTranscriptAssistantText(joined);
|
|
423
|
+
}
|
|
424
|
+
function cursorTranscriptToolState(toolName) {
|
|
425
|
+
const normalized = typeof toolName === "string" ? toolName.trim().toLowerCase() : "";
|
|
426
|
+
switch (normalized) {
|
|
427
|
+
case "edit":
|
|
428
|
+
case "editfile":
|
|
429
|
+
case "edit_file":
|
|
430
|
+
case "edit_file_v2":
|
|
431
|
+
case "strreplace":
|
|
432
|
+
case "write":
|
|
433
|
+
case "writefile":
|
|
434
|
+
case "applypatch":
|
|
435
|
+
case "apply_patch":
|
|
436
|
+
case "search_replace":
|
|
437
|
+
return "editing";
|
|
438
|
+
case "shell":
|
|
439
|
+
case "run_terminal_cmd":
|
|
440
|
+
case "run_terminal_command_v2":
|
|
441
|
+
return "running";
|
|
442
|
+
case "read":
|
|
443
|
+
case "readfile":
|
|
444
|
+
case "read_file":
|
|
445
|
+
case "read_file_v2":
|
|
446
|
+
case "grep":
|
|
447
|
+
case "glob":
|
|
448
|
+
case "list":
|
|
449
|
+
case "ls":
|
|
450
|
+
case "list_dir":
|
|
451
|
+
case "list_dir_v2":
|
|
452
|
+
case "ripgrep":
|
|
453
|
+
case "ripgrep_raw_search":
|
|
454
|
+
case "semantic_search_full":
|
|
455
|
+
case "codebase_search":
|
|
456
|
+
return "scanning";
|
|
457
|
+
case "task":
|
|
458
|
+
case "task_v2":
|
|
459
|
+
return "delegating";
|
|
460
|
+
case "todowrite":
|
|
461
|
+
case "create_plan":
|
|
462
|
+
return "planning";
|
|
463
|
+
default:
|
|
464
|
+
return null;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
function cursorTranscriptLatestToolState(content) {
|
|
468
|
+
if (!Array.isArray(content)) {
|
|
469
|
+
return null;
|
|
470
|
+
}
|
|
471
|
+
for (let index = content.length - 1; index >= 0; index -= 1) {
|
|
472
|
+
const state = cursorTranscriptToolState(content[index]?.name);
|
|
473
|
+
if (state) {
|
|
474
|
+
return state;
|
|
475
|
+
}
|
|
476
|
+
}
|
|
477
|
+
return null;
|
|
478
|
+
}
|
|
479
|
+
async function summariseCursorTranscriptSession(file) {
|
|
480
|
+
const raw = await (0, promises_1.readFile)(file.filePath, "utf8").catch(() => null);
|
|
481
|
+
if (!raw) {
|
|
482
|
+
return null;
|
|
483
|
+
}
|
|
484
|
+
let initialPrompt = null;
|
|
485
|
+
let latestUserText = null;
|
|
486
|
+
let latestAssistantText = null;
|
|
487
|
+
let latestRole = null;
|
|
488
|
+
let latestToolState = null;
|
|
489
|
+
let model = null;
|
|
490
|
+
for (const line of raw.split(/\r?\n/)) {
|
|
491
|
+
if (!line.trim()) {
|
|
492
|
+
continue;
|
|
493
|
+
}
|
|
494
|
+
let parsed = null;
|
|
495
|
+
try {
|
|
496
|
+
parsed = JSON.parse(line);
|
|
497
|
+
}
|
|
498
|
+
catch {
|
|
499
|
+
continue;
|
|
500
|
+
}
|
|
501
|
+
if (!parsed) {
|
|
502
|
+
continue;
|
|
503
|
+
}
|
|
504
|
+
const role = parsed.role === "user" || parsed.role === "assistant" ? parsed.role : null;
|
|
505
|
+
if (!role) {
|
|
506
|
+
continue;
|
|
507
|
+
}
|
|
508
|
+
const content = Array.isArray(parsed.message?.content) ? parsed.message.content : [];
|
|
509
|
+
const text = cursorTranscriptContentText(role, content);
|
|
510
|
+
if (!model) {
|
|
511
|
+
const candidateModel = parsed.model ?? parsed.message?.model ?? null;
|
|
512
|
+
model = typeof candidateModel === "string" && candidateModel.trim().length > 0
|
|
513
|
+
? candidateModel.trim()
|
|
514
|
+
: null;
|
|
515
|
+
}
|
|
516
|
+
if (role === "user") {
|
|
517
|
+
if (!initialPrompt && text) {
|
|
518
|
+
initialPrompt = text;
|
|
519
|
+
}
|
|
520
|
+
if (text) {
|
|
521
|
+
latestUserText = text;
|
|
522
|
+
latestRole = "user";
|
|
523
|
+
}
|
|
524
|
+
continue;
|
|
525
|
+
}
|
|
526
|
+
const toolState = cursorTranscriptLatestToolState(content);
|
|
527
|
+
if (text) {
|
|
528
|
+
latestAssistantText = text;
|
|
529
|
+
}
|
|
530
|
+
if (text || toolState) {
|
|
531
|
+
latestRole = "assistant";
|
|
532
|
+
}
|
|
533
|
+
if (toolState) {
|
|
534
|
+
latestToolState = toolState;
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
if (!initialPrompt && !latestUserText && !latestAssistantText) {
|
|
538
|
+
return null;
|
|
539
|
+
}
|
|
540
|
+
return {
|
|
541
|
+
sessionId: file.sessionId,
|
|
542
|
+
filePath: file.filePath,
|
|
543
|
+
createdAtMs: file.createdAtMs,
|
|
544
|
+
updatedAtMs: file.updatedAtMs,
|
|
545
|
+
initialPrompt,
|
|
546
|
+
latestUserText,
|
|
547
|
+
latestAssistantText,
|
|
548
|
+
latestRole,
|
|
549
|
+
latestToolState,
|
|
550
|
+
model
|
|
551
|
+
};
|
|
552
|
+
}
|
|
553
|
+
function cursorHooksDir(projectRoot) {
|
|
554
|
+
return (0, project_storage_1.getProjectStoragePath)(projectRoot, "cursor-hooks");
|
|
555
|
+
}
|
|
556
|
+
function cursorHooksFilePath(projectRoot, sessionId) {
|
|
557
|
+
return (0, node_path_1.join)(cursorHooksDir(projectRoot), `${sessionId}.jsonl`);
|
|
558
|
+
}
|
|
559
|
+
function cursorHookRecordTimestampMs(record, fallbackUpdatedAt) {
|
|
560
|
+
for (const key of ["timestamp", "created_at", "createdAt", "updated_at", "updatedAt"]) {
|
|
561
|
+
const value = record[key];
|
|
562
|
+
if (typeof value === "string") {
|
|
563
|
+
const parsed = Date.parse(value);
|
|
564
|
+
if (Number.isFinite(parsed)) {
|
|
565
|
+
if (parsed > fallbackUpdatedAt + CURSOR_HOOK_FUTURE_TOLERANCE_MS) {
|
|
566
|
+
return {
|
|
567
|
+
timestampMs: Math.max(0, fallbackUpdatedAt - CURSOR_HOOK_FUTURE_TOLERANCE_MS),
|
|
568
|
+
wasFutureClamped: true
|
|
569
|
+
};
|
|
570
|
+
}
|
|
571
|
+
return {
|
|
572
|
+
timestampMs: parsed,
|
|
573
|
+
wasFutureClamped: false
|
|
574
|
+
};
|
|
575
|
+
}
|
|
576
|
+
}
|
|
577
|
+
}
|
|
578
|
+
return {
|
|
579
|
+
timestampMs: fallbackUpdatedAt,
|
|
580
|
+
wasFutureClamped: false
|
|
581
|
+
};
|
|
582
|
+
}
|
|
583
|
+
function cursorHookSessionId(record) {
|
|
584
|
+
for (const key of ["conversation_id", "session_id", "parent_conversation_id"]) {
|
|
585
|
+
const value = record[key];
|
|
586
|
+
if (typeof value === "string" && value.trim().length > 0) {
|
|
587
|
+
return value.trim();
|
|
588
|
+
}
|
|
589
|
+
}
|
|
590
|
+
return null;
|
|
591
|
+
}
|
|
592
|
+
function cursorHookEventName(record) {
|
|
593
|
+
const value = record.hook_event_name;
|
|
594
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
595
|
+
}
|
|
596
|
+
function cursorHookModel(record) {
|
|
597
|
+
const value = record.model;
|
|
598
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
599
|
+
}
|
|
600
|
+
function cursorHookString(record, key) {
|
|
601
|
+
const value = record[key];
|
|
602
|
+
return typeof value === "string" && value.trim().length > 0 ? value.trim() : null;
|
|
603
|
+
}
|
|
604
|
+
function cursorHookStringList(record, key) {
|
|
605
|
+
const value = record[key];
|
|
606
|
+
if (!Array.isArray(value)) {
|
|
607
|
+
return [];
|
|
608
|
+
}
|
|
609
|
+
return value
|
|
610
|
+
.map((entry) => typeof entry === "string" ? entry.trim() : "")
|
|
611
|
+
.filter((entry) => entry.length > 0);
|
|
612
|
+
}
|
|
613
|
+
function cursorHookAttachmentsPaths(record) {
|
|
614
|
+
const value = record.attachments;
|
|
615
|
+
if (!Array.isArray(value)) {
|
|
616
|
+
return [];
|
|
617
|
+
}
|
|
618
|
+
return value
|
|
619
|
+
.map((entry) => {
|
|
620
|
+
if (!entry || typeof entry !== "object") {
|
|
621
|
+
return null;
|
|
622
|
+
}
|
|
623
|
+
const filePath = entry.file_path;
|
|
624
|
+
return typeof filePath === "string" && filePath.trim().length > 0 ? filePath.trim() : null;
|
|
625
|
+
})
|
|
626
|
+
.filter((entry) => entry !== null);
|
|
627
|
+
}
|
|
628
|
+
function cursorHookPaths(record, fallbackCwd) {
|
|
629
|
+
const values = new Set();
|
|
630
|
+
const cwd = cursorHookString(record, "cwd") ?? fallbackCwd;
|
|
631
|
+
if (cwd) {
|
|
632
|
+
values.add(cwd);
|
|
633
|
+
}
|
|
634
|
+
for (const value of [
|
|
635
|
+
cursorHookString(record, "file_path"),
|
|
636
|
+
cursorHookString(record, "transcript_path"),
|
|
637
|
+
cursorHookString(record, "agent_transcript_path")
|
|
638
|
+
]) {
|
|
639
|
+
if (value) {
|
|
640
|
+
values.add(value);
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
cursorHookStringList(record, "modified_files").forEach((value) => values.add(value));
|
|
644
|
+
cursorHookStringList(record, "workspace_roots").forEach((value) => values.add(value));
|
|
645
|
+
cursorHookAttachmentsPaths(record).forEach((value) => values.add(value));
|
|
646
|
+
return [...values];
|
|
647
|
+
}
|
|
648
|
+
function cursorHookActivityEventForTool(input) {
|
|
649
|
+
const normalizedTool = typeof input.toolName === "string" ? input.toolName.trim() : "";
|
|
650
|
+
const toolInput = input.record.tool_input;
|
|
651
|
+
const toolInputRecord = toolInput && typeof toolInput === "object" ? toolInput : null;
|
|
652
|
+
const command = cursorHookString(input.record, "command")
|
|
653
|
+
?? (toolInputRecord ? cursorHookString(toolInputRecord, "command") : null);
|
|
654
|
+
const workingDirectory = toolInputRecord
|
|
655
|
+
? cursorHookString(toolInputRecord, "working_directory")
|
|
656
|
+
: null;
|
|
657
|
+
const filePath = cursorHookString(input.record, "file_path")
|
|
658
|
+
?? (toolInputRecord ? cursorHookString(toolInputRecord, "file_path") : null);
|
|
659
|
+
const state = cursorTranscriptToolState(normalizedTool);
|
|
660
|
+
if (filePath || state === "editing") {
|
|
661
|
+
return {
|
|
662
|
+
type: "fileChange",
|
|
663
|
+
action: "edited",
|
|
664
|
+
path: filePath ?? input.fallbackCwd,
|
|
665
|
+
title: cursorLocalPromptDetail(filePath ?? "Edited files", "Edited files"),
|
|
666
|
+
isImage: false
|
|
667
|
+
};
|
|
668
|
+
}
|
|
669
|
+
if (normalizedTool.toLowerCase().startsWith("mcp:")) {
|
|
670
|
+
return {
|
|
671
|
+
type: "mcpToolCall",
|
|
672
|
+
action: "ran",
|
|
673
|
+
path: input.fallbackCwd,
|
|
674
|
+
title: cursorLocalPromptDetail(normalizedTool, "MCP tool"),
|
|
675
|
+
isImage: false
|
|
676
|
+
};
|
|
677
|
+
}
|
|
678
|
+
if (command || normalizedTool.toLowerCase() === "shell") {
|
|
679
|
+
return {
|
|
680
|
+
type: "commandExecution",
|
|
681
|
+
action: "ran",
|
|
682
|
+
path: workingDirectory ?? input.fallbackCwd,
|
|
683
|
+
title: cursorLocalPromptDetail(command ?? normalizedTool, input.failed ? "Command failed" : "Command ran"),
|
|
684
|
+
isImage: false
|
|
685
|
+
};
|
|
686
|
+
}
|
|
687
|
+
if (state === "scanning") {
|
|
688
|
+
return {
|
|
689
|
+
type: "other",
|
|
690
|
+
action: "updated",
|
|
691
|
+
path: input.fallbackCwd,
|
|
692
|
+
title: cursorLocalPromptDetail(normalizedTool || "Scanning", "Scanning"),
|
|
693
|
+
isImage: false
|
|
694
|
+
};
|
|
695
|
+
}
|
|
696
|
+
return null;
|
|
697
|
+
}
|
|
698
|
+
function cursorHookToolDetail(toolName, record) {
|
|
699
|
+
const toolInput = record.tool_input;
|
|
700
|
+
const toolInputRecord = toolInput && typeof toolInput === "object" ? toolInput : null;
|
|
701
|
+
const command = cursorHookString(record, "command")
|
|
702
|
+
?? (toolInputRecord ? cursorHookString(toolInputRecord, "command") : null);
|
|
703
|
+
const filePath = cursorHookString(record, "file_path")
|
|
704
|
+
?? (toolInputRecord ? cursorHookString(toolInputRecord, "file_path") : null);
|
|
705
|
+
const errorMessage = cursorHookString(record, "error_message");
|
|
706
|
+
if (errorMessage) {
|
|
707
|
+
return cursorLocalPromptDetail(errorMessage, "Tool failed");
|
|
708
|
+
}
|
|
709
|
+
if (command) {
|
|
710
|
+
return cursorLocalPromptDetail(command, "Shell command");
|
|
711
|
+
}
|
|
712
|
+
if (filePath) {
|
|
713
|
+
return cursorLocalPromptDetail(filePath, "Edited file");
|
|
714
|
+
}
|
|
715
|
+
if (toolName) {
|
|
716
|
+
return cursorLocalPromptDetail(toolName, "Tool activity");
|
|
717
|
+
}
|
|
718
|
+
return "Tool activity";
|
|
719
|
+
}
|
|
720
|
+
function cursorHookToolState(record, failed, fallbackCwd) {
|
|
721
|
+
const toolName = cursorHookString(record, "tool_name");
|
|
722
|
+
const detail = cursorHookToolDetail(toolName, record);
|
|
723
|
+
const activityEvent = cursorHookActivityEventForTool({
|
|
724
|
+
toolName,
|
|
725
|
+
record,
|
|
726
|
+
fallbackCwd,
|
|
727
|
+
failed
|
|
728
|
+
});
|
|
729
|
+
if (failed) {
|
|
730
|
+
return {
|
|
731
|
+
state: "blocked",
|
|
732
|
+
detail,
|
|
733
|
+
activityEvent,
|
|
734
|
+
latestMessage: cursorHookString(record, "error_message"),
|
|
735
|
+
isOngoing: false
|
|
736
|
+
};
|
|
737
|
+
}
|
|
738
|
+
const mappedState = cursorTranscriptToolState(toolName);
|
|
739
|
+
return {
|
|
740
|
+
state: mappedState ?? "planning",
|
|
741
|
+
detail,
|
|
742
|
+
activityEvent,
|
|
743
|
+
latestMessage: cursorHookString(record, "agent_message"),
|
|
744
|
+
isOngoing: true
|
|
745
|
+
};
|
|
746
|
+
}
|
|
747
|
+
function cursorHookPhaseSummary(input) {
|
|
748
|
+
const eventName = input.eventName.toLowerCase();
|
|
749
|
+
const { timestampMs: updatedAtMs, wasFutureClamped } = cursorHookRecordTimestampMs(input.record, input.fallbackUpdatedAt);
|
|
750
|
+
const sessionId = cursorHookSessionId(input.record) ?? `cursor-${updatedAtMs}`;
|
|
751
|
+
const model = cursorHookModel(input.record);
|
|
752
|
+
const paths = cursorHookPaths(input.record, input.fallbackCwd);
|
|
753
|
+
const sourceKind = model ? (0, shared_1.sourceKindFromModel)(model) : "cursor:local:hook";
|
|
754
|
+
const prompt = cursorHookString(input.record, "prompt");
|
|
755
|
+
const responseText = cursorHookString(input.record, "text");
|
|
756
|
+
const cwd = cursorHookString(input.record, "cwd") ?? input.fallbackCwd;
|
|
757
|
+
switch (eventName) {
|
|
758
|
+
case "beforesubmitprompt":
|
|
759
|
+
return {
|
|
760
|
+
sessionId,
|
|
761
|
+
createdAtMs: updatedAtMs,
|
|
762
|
+
updatedAtMs,
|
|
763
|
+
timestampWasClamped: wasFutureClamped,
|
|
764
|
+
label: cursorLocalPromptDetail(prompt, `Cursor ${sessionId.slice(0, 4)}`),
|
|
765
|
+
sourceKind,
|
|
766
|
+
state: "planning",
|
|
767
|
+
detail: cursorLocalPromptDetail(prompt, "Preparing prompt"),
|
|
768
|
+
statusText: model ?? "hook",
|
|
769
|
+
paths,
|
|
770
|
+
activityEvent: prompt
|
|
771
|
+
? {
|
|
772
|
+
type: "userMessage",
|
|
773
|
+
action: "said",
|
|
774
|
+
path: cwd,
|
|
775
|
+
title: cursorLocalPromptDetail(prompt, "Cursor prompt"),
|
|
776
|
+
isImage: false
|
|
777
|
+
}
|
|
778
|
+
: null,
|
|
779
|
+
latestMessage: null,
|
|
780
|
+
isOngoing: true
|
|
781
|
+
};
|
|
782
|
+
case "afteragentresponse":
|
|
783
|
+
return {
|
|
784
|
+
sessionId,
|
|
785
|
+
createdAtMs: updatedAtMs,
|
|
786
|
+
updatedAtMs,
|
|
787
|
+
timestampWasClamped: wasFutureClamped,
|
|
788
|
+
label: cursorLocalPromptDetail(responseText, `Cursor ${sessionId.slice(0, 4)}`),
|
|
789
|
+
sourceKind,
|
|
790
|
+
state: "thinking",
|
|
791
|
+
detail: cursorLocalPromptDetail(responseText, "Cursor response"),
|
|
792
|
+
statusText: model ?? "hook",
|
|
793
|
+
paths,
|
|
794
|
+
activityEvent: responseText
|
|
795
|
+
? {
|
|
796
|
+
type: "agentMessage",
|
|
797
|
+
action: "said",
|
|
798
|
+
path: cwd,
|
|
799
|
+
title: cursorLocalPromptDetail(responseText, "Cursor response"),
|
|
800
|
+
isImage: false
|
|
801
|
+
}
|
|
802
|
+
: null,
|
|
803
|
+
latestMessage: responseText,
|
|
804
|
+
isOngoing: true
|
|
805
|
+
};
|
|
806
|
+
case "afteragentthought":
|
|
807
|
+
return {
|
|
808
|
+
sessionId,
|
|
809
|
+
createdAtMs: updatedAtMs,
|
|
810
|
+
updatedAtMs,
|
|
811
|
+
timestampWasClamped: wasFutureClamped,
|
|
812
|
+
label: `Cursor ${sessionId.slice(0, 4)}`,
|
|
813
|
+
sourceKind,
|
|
814
|
+
state: "thinking",
|
|
815
|
+
detail: cursorLocalPromptDetail(responseText, "Thinking"),
|
|
816
|
+
statusText: model ?? "hook",
|
|
817
|
+
paths,
|
|
818
|
+
activityEvent: responseText
|
|
819
|
+
? {
|
|
820
|
+
type: "reasoning",
|
|
821
|
+
action: "updated",
|
|
822
|
+
path: cwd,
|
|
823
|
+
title: cursorLocalPromptDetail(responseText, "Thinking"),
|
|
824
|
+
isImage: false
|
|
825
|
+
}
|
|
826
|
+
: null,
|
|
827
|
+
latestMessage: null,
|
|
828
|
+
isOngoing: true
|
|
829
|
+
};
|
|
830
|
+
case "afterfileedit": {
|
|
831
|
+
const filePath = cursorHookString(input.record, "file_path");
|
|
832
|
+
return {
|
|
833
|
+
sessionId,
|
|
834
|
+
createdAtMs: updatedAtMs,
|
|
835
|
+
updatedAtMs,
|
|
836
|
+
timestampWasClamped: wasFutureClamped,
|
|
837
|
+
label: cursorLocalPromptDetail(filePath, `Cursor ${sessionId.slice(0, 4)}`),
|
|
838
|
+
sourceKind,
|
|
839
|
+
state: "editing",
|
|
840
|
+
detail: cursorLocalPromptDetail(filePath, "Edited file"),
|
|
841
|
+
statusText: model ?? "hook",
|
|
842
|
+
paths,
|
|
843
|
+
activityEvent: {
|
|
844
|
+
type: "fileChange",
|
|
845
|
+
action: "edited",
|
|
846
|
+
path: filePath ?? cwd,
|
|
847
|
+
title: cursorLocalPromptDetail(filePath, "Edited file"),
|
|
848
|
+
isImage: false
|
|
849
|
+
},
|
|
850
|
+
latestMessage: null,
|
|
851
|
+
isOngoing: true
|
|
852
|
+
};
|
|
853
|
+
}
|
|
854
|
+
case "beforetooluse":
|
|
855
|
+
case "pretooluse":
|
|
856
|
+
case "posttooluse":
|
|
857
|
+
return (() => {
|
|
858
|
+
const tool = cursorHookToolState(input.record, false, input.fallbackCwd);
|
|
859
|
+
return {
|
|
860
|
+
sessionId,
|
|
861
|
+
createdAtMs: updatedAtMs,
|
|
862
|
+
updatedAtMs,
|
|
863
|
+
timestampWasClamped: wasFutureClamped,
|
|
864
|
+
label: tool.detail,
|
|
865
|
+
sourceKind,
|
|
866
|
+
state: tool.state,
|
|
867
|
+
detail: tool.detail,
|
|
868
|
+
statusText: model ?? "hook",
|
|
869
|
+
paths,
|
|
870
|
+
activityEvent: tool.activityEvent,
|
|
871
|
+
latestMessage: tool.latestMessage,
|
|
872
|
+
isOngoing: true
|
|
873
|
+
};
|
|
874
|
+
})();
|
|
875
|
+
case "posttoolusefailure":
|
|
876
|
+
return (() => {
|
|
877
|
+
const tool = cursorHookToolState(input.record, true, input.fallbackCwd);
|
|
878
|
+
return {
|
|
879
|
+
sessionId,
|
|
880
|
+
createdAtMs: updatedAtMs,
|
|
881
|
+
updatedAtMs,
|
|
882
|
+
timestampWasClamped: wasFutureClamped,
|
|
883
|
+
label: tool.detail,
|
|
884
|
+
sourceKind,
|
|
885
|
+
state: tool.state,
|
|
886
|
+
detail: tool.detail,
|
|
887
|
+
statusText: model ?? "hook",
|
|
888
|
+
paths,
|
|
889
|
+
activityEvent: tool.activityEvent,
|
|
890
|
+
latestMessage: tool.latestMessage,
|
|
891
|
+
isOngoing: false
|
|
892
|
+
};
|
|
893
|
+
})();
|
|
894
|
+
case "beforeshellexecution":
|
|
895
|
+
case "aftershellexecution": {
|
|
896
|
+
const command = cursorHookString(input.record, "command");
|
|
897
|
+
return {
|
|
898
|
+
sessionId,
|
|
899
|
+
createdAtMs: updatedAtMs,
|
|
900
|
+
updatedAtMs,
|
|
901
|
+
timestampWasClamped: wasFutureClamped,
|
|
902
|
+
label: cursorLocalPromptDetail(command, `Cursor ${sessionId.slice(0, 4)}`),
|
|
903
|
+
sourceKind,
|
|
904
|
+
state: "running",
|
|
905
|
+
detail: cursorLocalPromptDetail(command, "Shell command"),
|
|
906
|
+
statusText: model ?? "hook",
|
|
907
|
+
paths,
|
|
908
|
+
activityEvent: {
|
|
909
|
+
type: "commandExecution",
|
|
910
|
+
action: "ran",
|
|
911
|
+
path: cwd,
|
|
912
|
+
title: cursorLocalPromptDetail(command, "Shell command"),
|
|
913
|
+
isImage: false
|
|
914
|
+
},
|
|
915
|
+
latestMessage: null,
|
|
916
|
+
isOngoing: true
|
|
917
|
+
};
|
|
918
|
+
}
|
|
919
|
+
case "beforemcpexecution":
|
|
920
|
+
case "aftermcpexecution": {
|
|
921
|
+
const toolName = cursorHookString(input.record, "tool_name");
|
|
922
|
+
return {
|
|
923
|
+
sessionId,
|
|
924
|
+
createdAtMs: updatedAtMs,
|
|
925
|
+
updatedAtMs,
|
|
926
|
+
timestampWasClamped: wasFutureClamped,
|
|
927
|
+
label: cursorLocalPromptDetail(toolName, `Cursor ${sessionId.slice(0, 4)}`),
|
|
928
|
+
sourceKind,
|
|
929
|
+
state: "running",
|
|
930
|
+
detail: cursorLocalPromptDetail(toolName, "MCP tool"),
|
|
931
|
+
statusText: model ?? "hook",
|
|
932
|
+
paths,
|
|
933
|
+
activityEvent: {
|
|
934
|
+
type: "mcpToolCall",
|
|
935
|
+
action: "ran",
|
|
936
|
+
path: cwd,
|
|
937
|
+
title: cursorLocalPromptDetail(toolName, "MCP tool"),
|
|
938
|
+
isImage: false
|
|
939
|
+
},
|
|
940
|
+
latestMessage: null,
|
|
941
|
+
isOngoing: true
|
|
942
|
+
};
|
|
943
|
+
}
|
|
944
|
+
case "subagentstart": {
|
|
945
|
+
const task = cursorHookString(input.record, "task") ?? cursorHookString(input.record, "description");
|
|
946
|
+
return {
|
|
947
|
+
sessionId,
|
|
948
|
+
createdAtMs: updatedAtMs,
|
|
949
|
+
updatedAtMs,
|
|
950
|
+
timestampWasClamped: wasFutureClamped,
|
|
951
|
+
label: cursorLocalPromptDetail(task, `Cursor ${sessionId.slice(0, 4)}`),
|
|
952
|
+
sourceKind,
|
|
953
|
+
state: "delegating",
|
|
954
|
+
detail: cursorLocalPromptDetail(task, "Delegating to subagent"),
|
|
955
|
+
statusText: model ?? "hook",
|
|
956
|
+
paths,
|
|
957
|
+
activityEvent: {
|
|
958
|
+
type: "other",
|
|
959
|
+
action: "updated",
|
|
960
|
+
path: cwd,
|
|
961
|
+
title: cursorLocalPromptDetail(task, "Delegating to subagent"),
|
|
962
|
+
isImage: false
|
|
963
|
+
},
|
|
964
|
+
latestMessage: null,
|
|
965
|
+
isOngoing: true
|
|
966
|
+
};
|
|
967
|
+
}
|
|
968
|
+
case "subagentstop": {
|
|
969
|
+
const status = cursorHookString(input.record, "status");
|
|
970
|
+
const summary = cursorHookString(input.record, "summary") ?? cursorHookString(input.record, "task");
|
|
971
|
+
const state = status === "error" ? "blocked"
|
|
972
|
+
: "done";
|
|
973
|
+
return {
|
|
974
|
+
sessionId,
|
|
975
|
+
createdAtMs: updatedAtMs,
|
|
976
|
+
updatedAtMs,
|
|
977
|
+
timestampWasClamped: wasFutureClamped,
|
|
978
|
+
label: cursorLocalPromptDetail(summary, `Cursor ${sessionId.slice(0, 4)}`),
|
|
979
|
+
sourceKind,
|
|
980
|
+
state,
|
|
981
|
+
detail: cursorLocalPromptDetail(summary, "Subagent completed"),
|
|
982
|
+
statusText: model ?? "hook",
|
|
983
|
+
paths,
|
|
984
|
+
activityEvent: {
|
|
985
|
+
type: "other",
|
|
986
|
+
action: "updated",
|
|
987
|
+
path: cwd,
|
|
988
|
+
title: cursorLocalPromptDetail(summary, "Subagent completed"),
|
|
989
|
+
isImage: false
|
|
990
|
+
},
|
|
991
|
+
latestMessage: null,
|
|
992
|
+
isOngoing: state !== "blocked"
|
|
993
|
+
};
|
|
994
|
+
}
|
|
995
|
+
case "stop": {
|
|
996
|
+
const status = cursorHookString(input.record, "status");
|
|
997
|
+
return {
|
|
998
|
+
sessionId,
|
|
999
|
+
createdAtMs: updatedAtMs,
|
|
1000
|
+
updatedAtMs,
|
|
1001
|
+
timestampWasClamped: wasFutureClamped,
|
|
1002
|
+
label: `Cursor ${sessionId.slice(0, 4)}`,
|
|
1003
|
+
sourceKind,
|
|
1004
|
+
state: status === "error" ? "blocked" : "done",
|
|
1005
|
+
detail: status === "error" ? "Cursor session failed"
|
|
1006
|
+
: status === "aborted" ? "Cursor session stopped"
|
|
1007
|
+
: "Cursor session completed",
|
|
1008
|
+
statusText: model ?? "hook",
|
|
1009
|
+
paths,
|
|
1010
|
+
activityEvent: null,
|
|
1011
|
+
latestMessage: null,
|
|
1012
|
+
isOngoing: false
|
|
1013
|
+
};
|
|
1014
|
+
}
|
|
1015
|
+
case "sessionstart": {
|
|
1016
|
+
const composerMode = cursorHookString(input.record, "composer_mode");
|
|
1017
|
+
const state = composerMode === "edit" ? "editing"
|
|
1018
|
+
: "planning";
|
|
1019
|
+
return {
|
|
1020
|
+
sessionId,
|
|
1021
|
+
createdAtMs: updatedAtMs,
|
|
1022
|
+
updatedAtMs,
|
|
1023
|
+
timestampWasClamped: wasFutureClamped,
|
|
1024
|
+
label: `Cursor ${sessionId.slice(0, 4)}`,
|
|
1025
|
+
sourceKind,
|
|
1026
|
+
state,
|
|
1027
|
+
detail: composerMode ? `Session started · ${composerMode}` : "Session started",
|
|
1028
|
+
statusText: model ?? "hook",
|
|
1029
|
+
paths,
|
|
1030
|
+
activityEvent: null,
|
|
1031
|
+
latestMessage: null,
|
|
1032
|
+
isOngoing: true
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
case "sessionend": {
|
|
1036
|
+
const reason = cursorHookString(input.record, "reason");
|
|
1037
|
+
return {
|
|
1038
|
+
sessionId,
|
|
1039
|
+
createdAtMs: updatedAtMs,
|
|
1040
|
+
updatedAtMs,
|
|
1041
|
+
timestampWasClamped: wasFutureClamped,
|
|
1042
|
+
label: `Cursor ${sessionId.slice(0, 4)}`,
|
|
1043
|
+
sourceKind,
|
|
1044
|
+
state: reason === "error" ? "blocked" : "done",
|
|
1045
|
+
detail: reason ? `Session ended · ${reason}` : "Session ended",
|
|
1046
|
+
statusText: model ?? "hook",
|
|
1047
|
+
paths,
|
|
1048
|
+
activityEvent: null,
|
|
1049
|
+
latestMessage: null,
|
|
1050
|
+
isOngoing: false
|
|
1051
|
+
};
|
|
1052
|
+
}
|
|
1053
|
+
case "precompact":
|
|
1054
|
+
return {
|
|
1055
|
+
sessionId,
|
|
1056
|
+
createdAtMs: updatedAtMs,
|
|
1057
|
+
updatedAtMs,
|
|
1058
|
+
timestampWasClamped: wasFutureClamped,
|
|
1059
|
+
label: `Cursor ${sessionId.slice(0, 4)}`,
|
|
1060
|
+
sourceKind,
|
|
1061
|
+
state: "thinking",
|
|
1062
|
+
detail: "Compacting context",
|
|
1063
|
+
statusText: model ?? "hook",
|
|
1064
|
+
paths,
|
|
1065
|
+
activityEvent: {
|
|
1066
|
+
type: "contextCompaction",
|
|
1067
|
+
action: "updated",
|
|
1068
|
+
path: cwd,
|
|
1069
|
+
title: "Compacting context",
|
|
1070
|
+
isImage: false
|
|
1071
|
+
},
|
|
1072
|
+
latestMessage: null,
|
|
1073
|
+
isOngoing: true
|
|
1074
|
+
};
|
|
1075
|
+
default:
|
|
1076
|
+
return null;
|
|
1077
|
+
}
|
|
1078
|
+
}
|
|
1079
|
+
function ageCursorHookSummary(summary, now = Date.now()) {
|
|
1080
|
+
if (!summary.isOngoing) {
|
|
1081
|
+
const ageMs = now - summary.updatedAtMs;
|
|
1082
|
+
if (ageMs <= CURSOR_HOOK_DONE_WINDOW_MS) {
|
|
1083
|
+
return summary;
|
|
1084
|
+
}
|
|
1085
|
+
return {
|
|
1086
|
+
...summary,
|
|
1087
|
+
state: "idle",
|
|
1088
|
+
detail: "Idle",
|
|
1089
|
+
activityEvent: null,
|
|
1090
|
+
latestMessage: null,
|
|
1091
|
+
timestampWasClamped: false
|
|
1092
|
+
};
|
|
1093
|
+
}
|
|
1094
|
+
const ageMs = now - summary.updatedAtMs;
|
|
1095
|
+
if (ageMs <= CURSOR_HOOK_ACTIVE_WINDOW_MS) {
|
|
1096
|
+
return summary;
|
|
1097
|
+
}
|
|
1098
|
+
if (ageMs <= CURSOR_HOOK_DONE_WINDOW_MS) {
|
|
1099
|
+
return {
|
|
1100
|
+
...summary,
|
|
1101
|
+
state: "done",
|
|
1102
|
+
detail: summary.detail === "Idle" ? "Done" : summary.detail,
|
|
1103
|
+
activityEvent: null,
|
|
1104
|
+
isOngoing: false,
|
|
1105
|
+
timestampWasClamped: false
|
|
1106
|
+
};
|
|
1107
|
+
}
|
|
1108
|
+
return {
|
|
1109
|
+
...summary,
|
|
1110
|
+
state: "idle",
|
|
1111
|
+
detail: "Idle",
|
|
1112
|
+
activityEvent: null,
|
|
1113
|
+
latestMessage: null,
|
|
1114
|
+
isOngoing: false,
|
|
1115
|
+
timestampWasClamped: false
|
|
1116
|
+
};
|
|
1117
|
+
}
|
|
1118
|
+
function cursorHookEventKind(event) {
|
|
1119
|
+
if (!event) {
|
|
1120
|
+
return "other";
|
|
1121
|
+
}
|
|
1122
|
+
if (event.type === "fileChange") {
|
|
1123
|
+
return "fileChange";
|
|
1124
|
+
}
|
|
1125
|
+
if (event.type === "commandExecution") {
|
|
1126
|
+
return "command";
|
|
1127
|
+
}
|
|
1128
|
+
if (event.type === "userMessage" || event.type === "agentMessage") {
|
|
1129
|
+
return "message";
|
|
1130
|
+
}
|
|
1131
|
+
if (event.type === "mcpToolCall") {
|
|
1132
|
+
return "tool";
|
|
1133
|
+
}
|
|
1134
|
+
return "other";
|
|
1135
|
+
}
|
|
1136
|
+
function cursorHookEventMethod(eventName, summary) {
|
|
1137
|
+
const normalized = typeof eventName === "string" ? eventName.trim() : "activity";
|
|
1138
|
+
switch (summary.activityEvent?.type) {
|
|
1139
|
+
case "userMessage":
|
|
1140
|
+
return "cursor/local/userMessage";
|
|
1141
|
+
case "agentMessage":
|
|
1142
|
+
return "cursor/local/agentMessage";
|
|
1143
|
+
case "fileChange":
|
|
1144
|
+
return "cursor/local/fileChange";
|
|
1145
|
+
case "commandExecution":
|
|
1146
|
+
return "cursor/local/commandExecution";
|
|
1147
|
+
case "mcpToolCall":
|
|
1148
|
+
return "cursor/local/mcpToolCall";
|
|
1149
|
+
case "reasoning":
|
|
1150
|
+
return "cursor/local/reasoning";
|
|
1151
|
+
case "contextCompaction":
|
|
1152
|
+
return "cursor/local/contextCompaction";
|
|
1153
|
+
default:
|
|
1154
|
+
return `cursor/local/hook/${normalized}`;
|
|
1155
|
+
}
|
|
1156
|
+
}
|
|
1157
|
+
function cursorHookEventFromSummary(input) {
|
|
1158
|
+
if (Date.now() - input.createdAtMs > CURSOR_HOOK_EVENT_WINDOW_MS) {
|
|
1159
|
+
return null;
|
|
1160
|
+
}
|
|
1161
|
+
if (!input.summary.activityEvent && !input.summary.latestMessage) {
|
|
1162
|
+
return null;
|
|
1163
|
+
}
|
|
1164
|
+
return {
|
|
1165
|
+
id: `${input.projectRoot}::cursor-hook::${input.sessionId}::${input.index}::${input.eventName ?? "activity"}`,
|
|
1166
|
+
source: "cursor",
|
|
1167
|
+
confidence: "typed",
|
|
1168
|
+
threadId: input.sessionId,
|
|
1169
|
+
createdAt: new Date(input.createdAtMs).toISOString(),
|
|
1170
|
+
method: cursorHookEventMethod(input.eventName, input.summary),
|
|
1171
|
+
kind: cursorHookEventKind(input.summary.activityEvent),
|
|
1172
|
+
phase: input.summary.state === "blocked" ? "failed"
|
|
1173
|
+
: input.summary.state === "done" ? "completed"
|
|
1174
|
+
: "updated",
|
|
1175
|
+
title: input.summary.detail,
|
|
1176
|
+
detail: input.summary.latestMessage ?? input.summary.detail,
|
|
1177
|
+
path: input.summary.activityEvent?.path ?? input.projectRoot,
|
|
1178
|
+
action: input.summary.activityEvent?.action,
|
|
1179
|
+
isImage: input.summary.activityEvent?.isImage
|
|
1180
|
+
};
|
|
1181
|
+
}
|
|
1182
|
+
async function cursorHookFiles(projectRoot) {
|
|
1183
|
+
const hooksDirs = await (0, project_storage_1.listExistingProjectStoragePaths)(projectRoot, "cursor-hooks");
|
|
1184
|
+
if (hooksDirs.length === 0) {
|
|
1185
|
+
return [];
|
|
1186
|
+
}
|
|
1187
|
+
const files = [];
|
|
1188
|
+
for (const hooksDir of hooksDirs) {
|
|
1189
|
+
const entries = await (0, promises_1.readdir)(hooksDir, { withFileTypes: true }).catch(() => []);
|
|
1190
|
+
for (const entry of entries) {
|
|
1191
|
+
if (!entry.isFile() || !entry.name.endsWith(".jsonl")) {
|
|
1192
|
+
continue;
|
|
1193
|
+
}
|
|
1194
|
+
const filePath = (0, node_path_1.join)(hooksDir, entry.name);
|
|
1195
|
+
const fileStats = await (0, promises_1.stat)(filePath).catch(() => null);
|
|
1196
|
+
if (!fileStats?.isFile()) {
|
|
1197
|
+
continue;
|
|
1198
|
+
}
|
|
1199
|
+
files.push({
|
|
1200
|
+
sessionId: entry.name.replace(/\.jsonl$/i, ""),
|
|
1201
|
+
filePath,
|
|
1202
|
+
updatedAtMs: fileStats.mtimeMs
|
|
1203
|
+
});
|
|
1204
|
+
}
|
|
1205
|
+
}
|
|
1206
|
+
return files.sort((left, right) => right.updatedAtMs - left.updatedAtMs);
|
|
1207
|
+
}
|
|
1208
|
+
async function summariseCursorHookSession(file, fallbackCwd) {
|
|
1209
|
+
const raw = await (0, promises_1.readFile)(file.filePath, "utf8").catch(() => null);
|
|
1210
|
+
if (!raw) {
|
|
1211
|
+
return null;
|
|
1212
|
+
}
|
|
1213
|
+
const rawRecords = raw
|
|
1214
|
+
.split(/\r?\n/)
|
|
1215
|
+
.map((line) => line.trim())
|
|
1216
|
+
.filter((line) => line.length > 0)
|
|
1217
|
+
.map((line) => {
|
|
1218
|
+
try {
|
|
1219
|
+
return JSON.parse(line);
|
|
1220
|
+
}
|
|
1221
|
+
catch {
|
|
1222
|
+
return null;
|
|
1223
|
+
}
|
|
1224
|
+
})
|
|
1225
|
+
.filter((record) => record !== null);
|
|
1226
|
+
if (rawRecords.length === 0) {
|
|
1227
|
+
return null;
|
|
1228
|
+
}
|
|
1229
|
+
const sortedRecords = rawRecords
|
|
1230
|
+
.slice()
|
|
1231
|
+
.sort((left, right) => cursorHookRecordTimestampMs(left, file.updatedAtMs).timestampMs
|
|
1232
|
+
- cursorHookRecordTimestampMs(right, file.updatedAtMs).timestampMs);
|
|
1233
|
+
let latestSummary = null;
|
|
1234
|
+
let latestPrompt = null;
|
|
1235
|
+
let latestAssistantText = null;
|
|
1236
|
+
const pathSet = new Set();
|
|
1237
|
+
const events = [];
|
|
1238
|
+
for (const [index, record] of sortedRecords.entries()) {
|
|
1239
|
+
const eventName = cursorHookEventName(record);
|
|
1240
|
+
const summary = cursorHookPhaseSummary({
|
|
1241
|
+
eventName: eventName ?? "activity",
|
|
1242
|
+
record,
|
|
1243
|
+
fallbackCwd,
|
|
1244
|
+
fallbackUpdatedAt: file.updatedAtMs
|
|
1245
|
+
});
|
|
1246
|
+
if (!summary) {
|
|
1247
|
+
continue;
|
|
1248
|
+
}
|
|
1249
|
+
summary.paths.forEach((path) => pathSet.add(path));
|
|
1250
|
+
if ((eventName ?? "").toLowerCase() === "beforesubmitprompt") {
|
|
1251
|
+
latestPrompt = cursorHookString(record, "prompt") ?? latestPrompt;
|
|
1252
|
+
}
|
|
1253
|
+
if ((eventName ?? "").toLowerCase() === "afteragentresponse") {
|
|
1254
|
+
latestAssistantText = cursorHookString(record, "text") ?? latestAssistantText;
|
|
1255
|
+
}
|
|
1256
|
+
const shouldReplaceLatest = !latestSummary
|
|
1257
|
+
|| summary.updatedAtMs >= latestSummary.updatedAtMs
|
|
1258
|
+
|| (latestSummary.timestampWasClamped
|
|
1259
|
+
&& !summary.timestampWasClamped
|
|
1260
|
+
&& summary.updatedAtMs >= latestSummary.updatedAtMs - CURSOR_HOOK_FUTURE_TOLERANCE_MS);
|
|
1261
|
+
if (shouldReplaceLatest) {
|
|
1262
|
+
latestSummary = summary;
|
|
1263
|
+
}
|
|
1264
|
+
const event = cursorHookEventFromSummary({
|
|
1265
|
+
projectRoot: fallbackCwd,
|
|
1266
|
+
sessionId: file.sessionId,
|
|
1267
|
+
eventName,
|
|
1268
|
+
summary,
|
|
1269
|
+
createdAtMs: summary.updatedAtMs,
|
|
1270
|
+
index
|
|
1271
|
+
});
|
|
1272
|
+
if (event) {
|
|
1273
|
+
events.push(event);
|
|
1274
|
+
}
|
|
1275
|
+
}
|
|
1276
|
+
if (!latestSummary) {
|
|
1277
|
+
return null;
|
|
1278
|
+
}
|
|
1279
|
+
const agedSummary = ageCursorHookSummary({
|
|
1280
|
+
...latestSummary,
|
|
1281
|
+
label: cursorLocalPromptDetail(latestPrompt ?? latestAssistantText ?? latestSummary.label, `Cursor ${file.sessionId.slice(0, 4)}`),
|
|
1282
|
+
latestMessage: latestAssistantText ?? latestSummary.latestMessage,
|
|
1283
|
+
paths: pathSet.size > 0 ? [...pathSet] : latestSummary.paths
|
|
1284
|
+
});
|
|
1285
|
+
return {
|
|
1286
|
+
summary: agedSummary,
|
|
1287
|
+
events: events.sort((left, right) => right.createdAt.localeCompare(left.createdAt))
|
|
1288
|
+
};
|
|
1289
|
+
}
|
|
1290
|
+
async function loadCursorHookProjectSnapshotData(projectRoot, limit = (0, local_discovery_1.cursorLocalSessionLimit)()) {
|
|
1291
|
+
const canonicalRoot = (0, project_paths_1.canonicalizeProjectPath)(projectRoot);
|
|
1292
|
+
if (!canonicalRoot) {
|
|
1293
|
+
return { agents: [], events: [] };
|
|
1294
|
+
}
|
|
1295
|
+
const files = await cursorHookFiles(canonicalRoot);
|
|
1296
|
+
if (files.length === 0) {
|
|
1297
|
+
return { agents: [], events: [] };
|
|
1298
|
+
}
|
|
1299
|
+
const [originUrl, branch] = await Promise.all([
|
|
1300
|
+
(0, shared_1.gitRemoteOriginUrl)(canonicalRoot),
|
|
1301
|
+
gitCurrentBranch(canonicalRoot)
|
|
1302
|
+
]);
|
|
1303
|
+
const summaries = (await Promise.all(files.slice(0, Math.max(limit, 24)).map((file) => summariseCursorHookSession(file, canonicalRoot))))
|
|
1304
|
+
.filter((entry) => entry !== null);
|
|
1305
|
+
const agents = [];
|
|
1306
|
+
const events = [];
|
|
1307
|
+
for (const entry of summaries.slice(0, limit)) {
|
|
1308
|
+
if (entry.summary.state === "idle" && !entry.summary.isOngoing) {
|
|
1309
|
+
continue;
|
|
1310
|
+
}
|
|
1311
|
+
const appearance = await (0, appearance_1.ensureAgentAppearance)(canonicalRoot, `cursor-local:${entry.summary.sessionId}`);
|
|
1312
|
+
agents.push({
|
|
1313
|
+
id: `cursor-local:${entry.summary.sessionId}`,
|
|
1314
|
+
label: entry.summary.label,
|
|
1315
|
+
source: "cursor",
|
|
1316
|
+
sourceKind: entry.summary.sourceKind,
|
|
1317
|
+
parentThreadId: null,
|
|
1318
|
+
depth: 0,
|
|
1319
|
+
isCurrent: false,
|
|
1320
|
+
isOngoing: entry.summary.isOngoing,
|
|
1321
|
+
statusText: entry.summary.statusText,
|
|
1322
|
+
role: "cursor",
|
|
1323
|
+
nickname: null,
|
|
1324
|
+
isSubagent: false,
|
|
1325
|
+
state: entry.summary.state,
|
|
1326
|
+
detail: entry.summary.detail,
|
|
1327
|
+
cwd: canonicalRoot,
|
|
1328
|
+
roomId: null,
|
|
1329
|
+
appearance,
|
|
1330
|
+
updatedAt: new Date(entry.summary.updatedAtMs).toISOString(),
|
|
1331
|
+
stoppedAt: entry.summary.isOngoing ? null : new Date(entry.summary.updatedAtMs).toISOString(),
|
|
1332
|
+
paths: entry.summary.paths.length > 0 ? entry.summary.paths : [canonicalRoot],
|
|
1333
|
+
activityEvent: entry.summary.activityEvent,
|
|
1334
|
+
latestMessage: entry.summary.latestMessage,
|
|
1335
|
+
threadId: entry.summary.sessionId,
|
|
1336
|
+
taskId: null,
|
|
1337
|
+
resumeCommand: null,
|
|
1338
|
+
url: null,
|
|
1339
|
+
git: {
|
|
1340
|
+
sha: null,
|
|
1341
|
+
branch,
|
|
1342
|
+
originUrl
|
|
1343
|
+
},
|
|
1344
|
+
provenance: "cursor",
|
|
1345
|
+
confidence: "typed",
|
|
1346
|
+
needsUser: null,
|
|
1347
|
+
liveSubscription: "readOnly",
|
|
1348
|
+
network: null
|
|
1349
|
+
});
|
|
1350
|
+
events.push(...entry.events);
|
|
1351
|
+
}
|
|
1352
|
+
return {
|
|
1353
|
+
agents: agents.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt)),
|
|
1354
|
+
events: events.sort((left, right) => right.createdAt.localeCompare(left.createdAt))
|
|
1355
|
+
};
|
|
1356
|
+
}
|
|
1357
|
+
function cursorLocalLabel(composer, prompt) {
|
|
1358
|
+
const name = typeof composer.name === "string" ? composer.name.trim() : "";
|
|
1359
|
+
if (name.length > 0) {
|
|
1360
|
+
return name;
|
|
1361
|
+
}
|
|
1362
|
+
const subtitle = typeof composer.subtitle === "string" ? composer.subtitle.trim() : "";
|
|
1363
|
+
if (subtitle.length > 0) {
|
|
1364
|
+
return cursorLocalPromptDetail(subtitle, "Cursor local");
|
|
1365
|
+
}
|
|
1366
|
+
if (prompt) {
|
|
1367
|
+
return cursorLocalPromptDetail(prompt, "Cursor local");
|
|
1368
|
+
}
|
|
1369
|
+
return `Cursor ${composer.composerId.slice(0, 4)}`;
|
|
1370
|
+
}
|
|
1371
|
+
function cursorLocalDetail(composer, prompt, state) {
|
|
1372
|
+
const subtitle = typeof composer.subtitle === "string" ? composer.subtitle.trim() : "";
|
|
1373
|
+
const changedFiles = composer.filesChangedCount ?? 0;
|
|
1374
|
+
const added = composer.totalLinesAdded ?? 0;
|
|
1375
|
+
const removed = composer.totalLinesRemoved ?? 0;
|
|
1376
|
+
if (state === "editing" && changedFiles > 0) {
|
|
1377
|
+
return `${changedFiles} file${changedFiles === 1 ? "" : "s"} changed · +${added} -${removed}`;
|
|
1378
|
+
}
|
|
1379
|
+
if (subtitle.length > 0) {
|
|
1380
|
+
return cursorLocalPromptDetail(subtitle, "Cursor local");
|
|
1381
|
+
}
|
|
1382
|
+
if (prompt) {
|
|
1383
|
+
return cursorLocalPromptDetail(prompt, "Cursor local");
|
|
1384
|
+
}
|
|
1385
|
+
return state === "idle" ? "Idle" : "Cursor local";
|
|
1386
|
+
}
|
|
1387
|
+
function cursorTranscriptActivityState(summary, now) {
|
|
1388
|
+
const ageMs = now - summary.updatedAtMs;
|
|
1389
|
+
if (ageMs <= CURSOR_LOCAL_ACTIVE_WINDOW_MS) {
|
|
1390
|
+
if (summary.latestToolState) {
|
|
1391
|
+
return { state: summary.latestToolState, isOngoing: true };
|
|
1392
|
+
}
|
|
1393
|
+
return { state: "planning", isOngoing: true };
|
|
1394
|
+
}
|
|
1395
|
+
if (ageMs <= CURSOR_LOCAL_DONE_WINDOW_MS) {
|
|
1396
|
+
return { state: "done", isOngoing: false };
|
|
1397
|
+
}
|
|
1398
|
+
return { state: "idle", isOngoing: false };
|
|
1399
|
+
}
|
|
1400
|
+
function cursorLocalActivityState(input) {
|
|
1401
|
+
const composerUpdatedAt = cursorComposerTimestamp(input.composer);
|
|
1402
|
+
const latestGenerationMs = input.isPrimaryComposer ? input.latestGenerationMs : 0;
|
|
1403
|
+
const agentLoopUpdatedAtMs = input.isPrimaryComposer ? (input.agentLoop.updatedAtMs ?? 0) : 0;
|
|
1404
|
+
const agentLoopActive = input.isPrimaryComposer ? input.agentLoop.active : false;
|
|
1405
|
+
const referenceUpdatedAt = Math.max(composerUpdatedAt, latestGenerationMs, agentLoopUpdatedAtMs);
|
|
1406
|
+
const ageMs = input.now - referenceUpdatedAt;
|
|
1407
|
+
const changedFiles = input.composer.filesChangedCount ?? 0;
|
|
1408
|
+
const changedLines = (input.composer.totalLinesAdded ?? 0) + (input.composer.totalLinesRemoved ?? 0);
|
|
1409
|
+
if (input.composer.hasBlockingPendingActions) {
|
|
1410
|
+
return { state: "blocked", isOngoing: true };
|
|
1411
|
+
}
|
|
1412
|
+
if (input.isPrimaryComposer && (agentLoopActive || ageMs <= CURSOR_LOCAL_ACTIVE_WINDOW_MS)) {
|
|
1413
|
+
if (changedFiles > 0 || changedLines > 0) {
|
|
1414
|
+
return { state: "editing", isOngoing: true };
|
|
1415
|
+
}
|
|
1416
|
+
return { state: "planning", isOngoing: true };
|
|
1417
|
+
}
|
|
1418
|
+
if (changedFiles > 0 || changedLines > 0) {
|
|
1419
|
+
if (ageMs <= CURSOR_LOCAL_DONE_WINDOW_MS) {
|
|
1420
|
+
return { state: "done", isOngoing: false };
|
|
1421
|
+
}
|
|
1422
|
+
return { state: "idle", isOngoing: false };
|
|
1423
|
+
}
|
|
1424
|
+
if (ageMs <= CURSOR_LOCAL_DONE_WINDOW_MS) {
|
|
1425
|
+
return { state: "done", isOngoing: false };
|
|
1426
|
+
}
|
|
1427
|
+
return { state: "idle", isOngoing: false };
|
|
1428
|
+
}
|
|
1429
|
+
function cursorTranscriptMessageEvent(input) {
|
|
1430
|
+
if (!input.role || !input.detail || Date.now() - input.createdAtMs > CURSOR_LOCAL_EVENT_WINDOW_MS) {
|
|
1431
|
+
return null;
|
|
1432
|
+
}
|
|
1433
|
+
return {
|
|
1434
|
+
id: `${input.projectRoot}::cursor-local::${input.sessionId}::${input.createdAtMs}`,
|
|
1435
|
+
source: "cursor",
|
|
1436
|
+
confidence: "inferred",
|
|
1437
|
+
threadId: input.sessionId,
|
|
1438
|
+
createdAt: new Date(input.createdAtMs).toISOString(),
|
|
1439
|
+
method: input.role === "user" ? "cursor/local/userMessage" : "cursor/local/agentMessage",
|
|
1440
|
+
kind: "message",
|
|
1441
|
+
phase: "updated",
|
|
1442
|
+
title: input.role === "user" ? "Cursor prompt" : "Cursor reply",
|
|
1443
|
+
detail: cursorLocalPromptDetail(input.detail, "Cursor local"),
|
|
1444
|
+
path: input.projectRoot,
|
|
1445
|
+
action: "said",
|
|
1446
|
+
isImage: false
|
|
1447
|
+
};
|
|
1448
|
+
}
|
|
1449
|
+
function cursorLocalPromptEvent(projectRoot, composerId, prompt, createdAtMs) {
|
|
1450
|
+
if (!prompt || !Number.isFinite(createdAtMs) || Date.now() - createdAtMs > CURSOR_LOCAL_EVENT_WINDOW_MS) {
|
|
1451
|
+
return null;
|
|
1452
|
+
}
|
|
1453
|
+
return {
|
|
1454
|
+
id: `${projectRoot}::cursor-local::${composerId}::${createdAtMs}`,
|
|
1455
|
+
source: "cursor",
|
|
1456
|
+
confidence: "inferred",
|
|
1457
|
+
threadId: composerId,
|
|
1458
|
+
createdAt: new Date(createdAtMs).toISOString(),
|
|
1459
|
+
method: "cursor/local/prompt",
|
|
1460
|
+
kind: "message",
|
|
1461
|
+
phase: "updated",
|
|
1462
|
+
title: "Cursor prompt",
|
|
1463
|
+
detail: cursorLocalPromptDetail(prompt, "Cursor local prompt"),
|
|
1464
|
+
path: projectRoot,
|
|
1465
|
+
action: "said",
|
|
1466
|
+
isImage: false
|
|
1467
|
+
};
|
|
1468
|
+
}
|
|
1469
|
+
async function gitCurrentBranch(projectRoot) {
|
|
1470
|
+
try {
|
|
1471
|
+
const { stdout } = await execFileAsync("git", ["-C", projectRoot, "rev-parse", "--abbrev-ref", "HEAD"]);
|
|
1472
|
+
const branch = stdout.trim();
|
|
1473
|
+
return branch.length > 0 && branch !== "HEAD" ? branch : null;
|
|
1474
|
+
}
|
|
1475
|
+
catch {
|
|
1476
|
+
return null;
|
|
1477
|
+
}
|
|
1478
|
+
}
|
|
1479
|
+
async function loadCursorTranscriptProjectSnapshotData(projectRoot, limit = (0, local_discovery_1.cursorLocalSessionLimit)()) {
|
|
1480
|
+
const canonicalRoot = (0, project_paths_1.canonicalizeProjectPath)(projectRoot);
|
|
1481
|
+
if (!canonicalRoot) {
|
|
1482
|
+
return { agents: [], events: [] };
|
|
1483
|
+
}
|
|
1484
|
+
const transcriptFiles = (await (0, local_discovery_1.cursorTranscriptFiles)(canonicalRoot)).slice(0, Math.max(limit, 24));
|
|
1485
|
+
if (transcriptFiles.length === 0) {
|
|
1486
|
+
return { agents: [], events: [] };
|
|
1487
|
+
}
|
|
1488
|
+
const summaries = (await Promise.all(transcriptFiles.map((file) => summariseCursorTranscriptSession(file))))
|
|
1489
|
+
.filter((summary) => summary !== null);
|
|
1490
|
+
if (summaries.length === 0) {
|
|
1491
|
+
return { agents: [], events: [] };
|
|
1492
|
+
}
|
|
1493
|
+
const now = Date.now();
|
|
1494
|
+
const [originUrl, branch] = await Promise.all([
|
|
1495
|
+
(0, shared_1.gitRemoteOriginUrl)(canonicalRoot),
|
|
1496
|
+
gitCurrentBranch(canonicalRoot)
|
|
1497
|
+
]);
|
|
1498
|
+
const agents = [];
|
|
1499
|
+
const events = [];
|
|
1500
|
+
for (const summary of summaries.slice(0, limit)) {
|
|
1501
|
+
const { state, isOngoing } = cursorTranscriptActivityState(summary, now);
|
|
1502
|
+
if (state === "idle" && !isOngoing) {
|
|
1503
|
+
continue;
|
|
1504
|
+
}
|
|
1505
|
+
const labelText = summary.initialPrompt
|
|
1506
|
+
?? summary.latestUserText
|
|
1507
|
+
?? summary.latestAssistantText
|
|
1508
|
+
?? `Cursor ${summary.sessionId.slice(0, 4)}`;
|
|
1509
|
+
const detailText = summary.latestAssistantText
|
|
1510
|
+
?? summary.latestUserText
|
|
1511
|
+
?? summary.initialPrompt
|
|
1512
|
+
?? "Cursor local";
|
|
1513
|
+
const activityText = summary.latestRole === "assistant"
|
|
1514
|
+
? (summary.latestAssistantText ?? detailText)
|
|
1515
|
+
: (summary.latestUserText ?? summary.initialPrompt ?? detailText);
|
|
1516
|
+
const updatedAt = new Date(summary.updatedAtMs || now).toISOString();
|
|
1517
|
+
const appearance = await (0, appearance_1.ensureAgentAppearance)(canonicalRoot, `cursor-local:${summary.sessionId}`);
|
|
1518
|
+
const activityEvent = activityText
|
|
1519
|
+
? {
|
|
1520
|
+
type: summary.latestRole === "user" ? "userMessage" : "agentMessage",
|
|
1521
|
+
action: "said",
|
|
1522
|
+
path: canonicalRoot,
|
|
1523
|
+
title: cursorLocalPromptDetail(activityText, "Cursor local"),
|
|
1524
|
+
isImage: false
|
|
1525
|
+
}
|
|
1526
|
+
: null;
|
|
1527
|
+
agents.push({
|
|
1528
|
+
id: `cursor-local:${summary.sessionId}`,
|
|
1529
|
+
label: cursorLocalPromptDetail(labelText, `Cursor ${summary.sessionId.slice(0, 4)}`),
|
|
1530
|
+
source: "cursor",
|
|
1531
|
+
sourceKind: summary.model ? `cursor:${summary.model}` : "cursor:local:agent-transcript",
|
|
1532
|
+
parentThreadId: null,
|
|
1533
|
+
depth: 0,
|
|
1534
|
+
isCurrent: false,
|
|
1535
|
+
isOngoing,
|
|
1536
|
+
statusText: summary.model ?? "agent transcript",
|
|
1537
|
+
role: "cursor",
|
|
1538
|
+
nickname: null,
|
|
1539
|
+
isSubagent: false,
|
|
1540
|
+
state,
|
|
1541
|
+
detail: cursorLocalPromptDetail(detailText, "Cursor local"),
|
|
1542
|
+
cwd: canonicalRoot,
|
|
1543
|
+
roomId: null,
|
|
1544
|
+
appearance,
|
|
1545
|
+
updatedAt,
|
|
1546
|
+
stoppedAt: isOngoing ? null : updatedAt,
|
|
1547
|
+
paths: [canonicalRoot],
|
|
1548
|
+
activityEvent,
|
|
1549
|
+
latestMessage: summary.latestAssistantText,
|
|
1550
|
+
threadId: summary.sessionId,
|
|
1551
|
+
taskId: null,
|
|
1552
|
+
resumeCommand: null,
|
|
1553
|
+
url: null,
|
|
1554
|
+
git: {
|
|
1555
|
+
sha: null,
|
|
1556
|
+
branch,
|
|
1557
|
+
originUrl
|
|
1558
|
+
},
|
|
1559
|
+
provenance: "cursor",
|
|
1560
|
+
confidence: "inferred",
|
|
1561
|
+
needsUser: null,
|
|
1562
|
+
liveSubscription: "readOnly",
|
|
1563
|
+
network: null
|
|
1564
|
+
});
|
|
1565
|
+
const event = cursorTranscriptMessageEvent({
|
|
1566
|
+
projectRoot: canonicalRoot,
|
|
1567
|
+
sessionId: summary.sessionId,
|
|
1568
|
+
role: summary.latestRole,
|
|
1569
|
+
detail: activityText,
|
|
1570
|
+
createdAtMs: summary.updatedAtMs
|
|
1571
|
+
});
|
|
1572
|
+
if (event) {
|
|
1573
|
+
events.push(event);
|
|
1574
|
+
}
|
|
1575
|
+
}
|
|
1576
|
+
return {
|
|
1577
|
+
agents: agents.sort((left, right) => right.updatedAt.localeCompare(left.updatedAt)),
|
|
1578
|
+
events: events.sort((left, right) => right.createdAt.localeCompare(left.createdAt))
|
|
1579
|
+
};
|
|
1580
|
+
}
|
|
1581
|
+
async function loadCursorLocalProjectSnapshotData(projectRoot, limit = (0, local_discovery_1.cursorLocalSessionLimit)()) {
|
|
1582
|
+
return loadCursorHookProjectSnapshotData(projectRoot, limit);
|
|
1583
|
+
}
|
|
1584
|
+
//# sourceMappingURL=cursor.js.map
|