spect8-mcp 0.2.3 → 0.2.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/INSTALLATION_PLAN.md +126 -0
- package/README.md +45 -12
- package/dist/auth.d.ts +1 -1
- package/dist/auth.d.ts.map +1 -1
- package/dist/auth.js +12 -4
- package/dist/auth.js.map +1 -1
- package/dist/cli.js +166 -121
- package/dist/cli.js.map +1 -1
- package/dist/collectors/cursor_sqlite.d.ts +8 -4
- package/dist/collectors/cursor_sqlite.d.ts.map +1 -1
- package/dist/collectors/cursor_sqlite.js +91 -23
- package/dist/collectors/cursor_sqlite.js.map +1 -1
- package/dist/config.d.ts +4 -4
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +3 -3
- package/dist/config.js.map +1 -1
- package/dist/hooks/shared.d.ts +1 -1
- package/dist/hooks/shared.d.ts.map +1 -1
- package/dist/install.d.ts +41 -0
- package/dist/install.d.ts.map +1 -0
- package/dist/install.js +279 -0
- package/dist/install.js.map +1 -0
- package/dist/server.js +3 -2
- package/dist/server.js.map +1 -1
- package/dist/tools/report_activity.d.ts +2 -2
- package/dist/types.d.ts +2 -2
- package/examples/claude/hooks.json +3 -3
- package/examples/cursor/mcp.json +1 -4
- package/package.json +3 -2
|
@@ -9,17 +9,21 @@ export interface CursorSqliteTailerOptions {
|
|
|
9
9
|
pollIntervalMs?: number;
|
|
10
10
|
}
|
|
11
11
|
/**
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* Polls Cursor's state database for new `bubbleId:*` rows and emits token
|
|
13
|
+
* usage to the ingest batcher. The implementation shells out to the system
|
|
14
|
+
* `sqlite3` binary to avoid native Node dependencies in the published package.
|
|
14
15
|
*/
|
|
15
16
|
export declare class CursorSqliteTailer {
|
|
16
17
|
private readonly options;
|
|
17
18
|
private timer;
|
|
18
|
-
private
|
|
19
|
+
private watermarkRowId;
|
|
19
20
|
constructor(options: CursorSqliteTailerOptions);
|
|
20
21
|
start(): void;
|
|
21
22
|
stop(): Promise<void>;
|
|
22
23
|
private poll;
|
|
23
|
-
private
|
|
24
|
+
private collectLatestRows;
|
|
25
|
+
private readMaxRowId;
|
|
26
|
+
private queryJson;
|
|
27
|
+
private toTokenEvent;
|
|
24
28
|
}
|
|
25
29
|
//# sourceMappingURL=cursor_sqlite.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cursor_sqlite.d.ts","sourceRoot":"","sources":["../../src/collectors/cursor_sqlite.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;
|
|
1
|
+
{"version":3,"file":"cursor_sqlite.d.ts","sourceRoot":"","sources":["../../src/collectors/cursor_sqlite.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AAC3C,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAGvD,MAAM,WAAW,yBAAyB;IACxC,UAAU,EAAE,MAAM,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,MAAM,CAAC;IACf,OAAO,EAAE,OAAO,CAAC;IACjB,YAAY,EAAE,MAAM,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAqBD;;;;GAIG;AACH,qBAAa,kBAAkB;IAIjB,OAAO,CAAC,QAAQ,CAAC,OAAO;IAHpC,OAAO,CAAC,KAAK,CAA+B;IAC5C,OAAO,CAAC,cAAc,CAAK;gBAEE,OAAO,EAAE,yBAAyB;IAE/D,KAAK,IAAI,IAAI;IAUP,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC;YAOb,IAAI;IAwBlB,OAAO,CAAC,iBAAiB;IAsBzB,OAAO,CAAC,YAAY;IAepB,OAAO,CAAC,SAAS;IAiBjB,OAAO,CAAC,YAAY;CAsBrB"}
|
|
@@ -1,21 +1,24 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { execFileSync } from "node:child_process";
|
|
2
2
|
import { existsSync } from "node:fs";
|
|
3
3
|
/**
|
|
4
|
-
*
|
|
5
|
-
*
|
|
4
|
+
* Polls Cursor's state database for new `bubbleId:*` rows and emits token
|
|
5
|
+
* usage to the ingest batcher. The implementation shells out to the system
|
|
6
|
+
* `sqlite3` binary to avoid native Node dependencies in the published package.
|
|
6
7
|
*/
|
|
7
8
|
export class CursorSqliteTailer {
|
|
8
9
|
options;
|
|
9
10
|
timer = null;
|
|
10
|
-
|
|
11
|
+
watermarkRowId = 0;
|
|
11
12
|
constructor(options) {
|
|
12
13
|
this.options = options;
|
|
13
14
|
}
|
|
14
15
|
start() {
|
|
15
16
|
if (this.timer)
|
|
16
17
|
return;
|
|
17
|
-
|
|
18
|
+
this.watermarkRowId = this.readMaxRowId();
|
|
19
|
+
const interval = this.options.pollIntervalMs ?? 5_000;
|
|
18
20
|
this.timer = setInterval(() => void this.poll(), interval);
|
|
21
|
+
this.timer.unref?.();
|
|
19
22
|
void this.poll();
|
|
20
23
|
this.options.logger.info("Cursor SQLite tailer started (polling mode)");
|
|
21
24
|
}
|
|
@@ -26,36 +29,101 @@ export class CursorSqliteTailer {
|
|
|
26
29
|
}
|
|
27
30
|
}
|
|
28
31
|
async poll() {
|
|
29
|
-
const
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
32
|
+
const rows = this.collectLatestRows();
|
|
33
|
+
if (rows.length === 0)
|
|
34
|
+
return;
|
|
35
|
+
let queued = 0;
|
|
36
|
+
for (const row of rows) {
|
|
37
|
+
try {
|
|
38
|
+
const event = this.toTokenEvent(JSON.parse(row.value));
|
|
39
|
+
if (event) {
|
|
40
|
+
this.options.batcher.enqueueTokenEvent(event);
|
|
41
|
+
queued++;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
catch {
|
|
45
|
+
// Cursor stores mixed JSON payloads in this table; ignore malformed rows.
|
|
46
|
+
}
|
|
47
|
+
finally {
|
|
48
|
+
this.watermarkRowId = Math.max(this.watermarkRowId, row.rowid);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
if (queued > 0) {
|
|
52
|
+
this.options.logger.debug(`cursor_sqlite: queued ${queued} token events`);
|
|
34
53
|
}
|
|
35
54
|
}
|
|
36
|
-
|
|
55
|
+
collectLatestRows() {
|
|
37
56
|
if (!existsSync(this.options.sqlitePath)) {
|
|
38
57
|
return [];
|
|
39
58
|
}
|
|
40
59
|
try {
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
const output =
|
|
44
|
-
encoding: "utf8",
|
|
45
|
-
timeout: 2000
|
|
46
|
-
});
|
|
60
|
+
const query = "SELECT rowid, key, value FROM cursorDiskKV " +
|
|
61
|
+
"WHERE key LIKE 'bubbleId:%' AND rowid > ? ORDER BY rowid";
|
|
62
|
+
const output = this.queryJson(query, String(this.watermarkRowId));
|
|
47
63
|
if (!output || output.trim() === "")
|
|
48
64
|
return [];
|
|
49
|
-
|
|
50
|
-
return rows.map((r) => ({
|
|
51
|
-
id: r.key,
|
|
52
|
-
payload: JSON.parse(r.value)
|
|
53
|
-
}));
|
|
65
|
+
return JSON.parse(output);
|
|
54
66
|
}
|
|
55
67
|
catch (err) {
|
|
56
|
-
this.options.logger.debug("
|
|
68
|
+
this.options.logger.debug("cursor_sqlite: poll failed", err.message);
|
|
57
69
|
return [];
|
|
58
70
|
}
|
|
59
71
|
}
|
|
72
|
+
readMaxRowId() {
|
|
73
|
+
if (!existsSync(this.options.sqlitePath)) {
|
|
74
|
+
return 0;
|
|
75
|
+
}
|
|
76
|
+
try {
|
|
77
|
+
const output = this.queryJson("SELECT MAX(rowid) AS max_rowid FROM cursorDiskKV WHERE key LIKE 'bubbleId:%'");
|
|
78
|
+
const [row] = JSON.parse(output || "[]");
|
|
79
|
+
return row?.max_rowid ?? 0;
|
|
80
|
+
}
|
|
81
|
+
catch {
|
|
82
|
+
return 0;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
queryJson(query, ...params) {
|
|
86
|
+
const escapedParams = params.map((p) => p.replaceAll("'", "''"));
|
|
87
|
+
const parameterized = escapedParams.reduce((sql, param) => sql.replace("?", `'${param}'`), query);
|
|
88
|
+
return execFileSync("sqlite3", [
|
|
89
|
+
"-readonly",
|
|
90
|
+
"-json",
|
|
91
|
+
this.options.sqlitePath,
|
|
92
|
+
parameterized,
|
|
93
|
+
], {
|
|
94
|
+
encoding: "utf8",
|
|
95
|
+
timeout: 2_000,
|
|
96
|
+
});
|
|
97
|
+
}
|
|
98
|
+
toTokenEvent(bubble) {
|
|
99
|
+
const requestId = bubble.requestId ?? bubble.bubbleId;
|
|
100
|
+
if (!requestId)
|
|
101
|
+
return null;
|
|
102
|
+
const tokenCount = bubble.tokenCount;
|
|
103
|
+
const input = tokenCount?.inputTokens ?? null;
|
|
104
|
+
const output = tokenCount?.outputTokens ?? null;
|
|
105
|
+
if ((input ?? 0) + (output ?? 0) === 0)
|
|
106
|
+
return null;
|
|
107
|
+
return {
|
|
108
|
+
request_id: requestId,
|
|
109
|
+
session_id: this.options.getSessionId(),
|
|
110
|
+
developer_id: this.options.developerId,
|
|
111
|
+
timestamp_ms: toMs(bubble.createdAt) ?? Date.now(),
|
|
112
|
+
ide_name: "cursor",
|
|
113
|
+
model_name: bubble.modelInfo?.modelName ?? bubble.modelInfo?.model ?? null,
|
|
114
|
+
input_tokens: input,
|
|
115
|
+
output_tokens: output,
|
|
116
|
+
cache_read_input_tokens: tokenCount?.cacheReadInputTokens ?? null,
|
|
117
|
+
cache_creation_input_tokens: tokenCount?.cacheCreationInputTokens ?? null,
|
|
118
|
+
};
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
function toMs(value) {
|
|
122
|
+
if (value == null)
|
|
123
|
+
return null;
|
|
124
|
+
if (typeof value === "number")
|
|
125
|
+
return value > 1e12 ? value : value * 1000;
|
|
126
|
+
const parsed = Date.parse(value);
|
|
127
|
+
return Number.isFinite(parsed) ? parsed : null;
|
|
60
128
|
}
|
|
61
129
|
//# sourceMappingURL=cursor_sqlite.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cursor_sqlite.js","sourceRoot":"","sources":["../../src/collectors/cursor_sqlite.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"cursor_sqlite.js","sourceRoot":"","sources":["../../src/collectors/cursor_sqlite.ts"],"names":[],"mappings":"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"}
|
package/dist/config.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ declare const ConfigFileSchema: z.ZodObject<{
|
|
|
3
3
|
ingest_url: z.ZodOptional<z.ZodString>;
|
|
4
4
|
ingest_hmac_key: z.ZodOptional<z.ZodString>;
|
|
5
5
|
developer_id: z.ZodOptional<z.ZodString>;
|
|
6
|
-
ide_name: z.ZodOptional<z.
|
|
6
|
+
ide_name: z.ZodOptional<z.ZodString>;
|
|
7
7
|
cursor_sqlite_path: z.ZodOptional<z.ZodString>;
|
|
8
8
|
claude_projects_root: z.ZodOptional<z.ZodString>;
|
|
9
9
|
batch_interval_ms: z.ZodOptional<z.ZodNumber>;
|
|
@@ -17,7 +17,7 @@ declare const ConfigFileSchema: z.ZodObject<{
|
|
|
17
17
|
ingest_url: z.ZodOptional<z.ZodString>;
|
|
18
18
|
ingest_hmac_key: z.ZodOptional<z.ZodString>;
|
|
19
19
|
developer_id: z.ZodOptional<z.ZodString>;
|
|
20
|
-
ide_name: z.ZodOptional<z.
|
|
20
|
+
ide_name: z.ZodOptional<z.ZodString>;
|
|
21
21
|
cursor_sqlite_path: z.ZodOptional<z.ZodString>;
|
|
22
22
|
claude_projects_root: z.ZodOptional<z.ZodString>;
|
|
23
23
|
batch_interval_ms: z.ZodOptional<z.ZodNumber>;
|
|
@@ -31,7 +31,7 @@ declare const ConfigFileSchema: z.ZodObject<{
|
|
|
31
31
|
ingest_url: z.ZodOptional<z.ZodString>;
|
|
32
32
|
ingest_hmac_key: z.ZodOptional<z.ZodString>;
|
|
33
33
|
developer_id: z.ZodOptional<z.ZodString>;
|
|
34
|
-
ide_name: z.ZodOptional<z.
|
|
34
|
+
ide_name: z.ZodOptional<z.ZodString>;
|
|
35
35
|
cursor_sqlite_path: z.ZodOptional<z.ZodString>;
|
|
36
36
|
claude_projects_root: z.ZodOptional<z.ZodString>;
|
|
37
37
|
batch_interval_ms: z.ZodOptional<z.ZodNumber>;
|
|
@@ -46,7 +46,7 @@ export interface ResolvedConfig {
|
|
|
46
46
|
ingestUrl: string;
|
|
47
47
|
ingestHmacKey: string;
|
|
48
48
|
developerId: string;
|
|
49
|
-
ideName:
|
|
49
|
+
ideName: string;
|
|
50
50
|
cursorSqlitePath: string;
|
|
51
51
|
claudeProjectsRoot: string;
|
|
52
52
|
batchIntervalMs: number;
|
package/dist/config.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,QAAA,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCAgBN,CAAC;AAEjB,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"config.d.ts","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,QAAA,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gCAgBN,CAAC;AAEjB,MAAM,WAAW,cAAc;IAC7B,SAAS,EAAE,MAAM,CAAC;IAClB,aAAa,EAAE,MAAM,CAAC;IACtB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,gBAAgB,EAAE,MAAM,CAAC;IACzB,kBAAkB,EAAE,MAAM,CAAC;IAC3B,eAAe,EAAE,MAAM,CAAC;IACxB,cAAc,EAAE,MAAM,CAAC;IACvB,gBAAgB,EAAE,MAAM,CAAC;IACzB,eAAe,EAAE,MAAM,CAAC;IACxB,kBAAkB,EAAE,OAAO,CAAC;IAC5B,kBAAkB,EAAE,OAAO,CAAC;IAC5B,QAAQ,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,MAAM,GAAG,OAAO,CAAC;CAC1D;AA+DD,wBAAgB,UAAU,CAAC,OAAO,EAAE,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC,GAAG,IAAI,CAOnF;AAED,wBAAgB,UAAU,IAAI,cAAc,CAwD3C"}
|
package/dist/config.js
CHANGED
|
@@ -7,7 +7,7 @@ const ConfigFileSchema = z
|
|
|
7
7
|
ingest_url: z.string().url().optional(),
|
|
8
8
|
ingest_hmac_key: z.string().optional(),
|
|
9
9
|
developer_id: z.string().optional(),
|
|
10
|
-
ide_name: z.
|
|
10
|
+
ide_name: z.string().min(1).optional(),
|
|
11
11
|
cursor_sqlite_path: z.string().optional(),
|
|
12
12
|
claude_projects_root: z.string().optional(),
|
|
13
13
|
batch_interval_ms: z.number().int().positive().optional(),
|
|
@@ -66,7 +66,7 @@ export function loadConfig() {
|
|
|
66
66
|
if (!existsSync(CONFIG_DIR)) {
|
|
67
67
|
mkdirSync(CONFIG_DIR, { recursive: true });
|
|
68
68
|
}
|
|
69
|
-
const ingestUrl = process.env.SPECT8_INGEST_URL ?? file.ingest_url ?? "https://
|
|
69
|
+
const ingestUrl = process.env.SPECT8_INGEST_URL ?? file.ingest_url ?? "https://cooperative-presence-production-d9a8.up.railway.app";
|
|
70
70
|
const ingestHmacKey = process.env.SPECT8_INGEST_HMAC_KEY ?? file.ingest_hmac_key ?? "";
|
|
71
71
|
const developerId = process.env.SPECT8_DEVELOPER_ID ?? file.developer_id ?? "";
|
|
72
72
|
return {
|
|
@@ -90,7 +90,7 @@ export function loadConfig() {
|
|
|
90
90
|
50,
|
|
91
91
|
offlineQueuePath: process.env.SPECT8_OFFLINE_QUEUE_PATH ??
|
|
92
92
|
file.offline_queue_path ??
|
|
93
|
-
join(CONFIG_DIR, "offline_queue.
|
|
93
|
+
join(CONFIG_DIR, "offline_queue.json"),
|
|
94
94
|
sessionFilePath: process.env.SPECT8_SESSION_FILE ??
|
|
95
95
|
file.session_file_path ??
|
|
96
96
|
join(CONFIG_DIR, "session"),
|
package/dist/config.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,SAAS,EAAE,YAAY,EAAE,aAAa,EAAE,MAAM,SAAS,CAAC;AAC7E,OAAO,EAAE,OAAO,EAAE,MAAM,SAAS,CAAC;AAClC,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AACjC,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,MAAM,gBAAgB,GAAG,CAAC;KACvB,MAAM,CAAC;IACN,UAAU,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,GAAG,EAAE,CAAC,QAAQ,EAAE;IACvC,eAAe,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACtC,YAAY,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,QAAQ,EAAE;IACnC,QAAQ,EAAE,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"config.js","sourceRoot":"","sources":["../src/config.ts"],"names":[],"mappings":"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"}
|
package/dist/hooks/shared.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ import type { ToolEvent, TokenEvent } from "../types.js";
|
|
|
5
5
|
export interface HookBundle {
|
|
6
6
|
sessionId: string;
|
|
7
7
|
developerId: string;
|
|
8
|
-
ideName:
|
|
8
|
+
ideName: string;
|
|
9
9
|
batcher: Batcher;
|
|
10
10
|
client: IngestClient;
|
|
11
11
|
queue: OfflineQueue;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/hooks/shared.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAGlD,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzD,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"shared.d.ts","sourceRoot":"","sources":["../../src/hooks/shared.ts"],"names":[],"mappings":"AAMA,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAC7D,OAAO,EAAE,OAAO,EAAE,MAAM,yBAAyB,CAAC;AAGlD,OAAO,KAAK,EAAE,SAAS,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AAEzD,MAAM,WAAW,UAAU;IACzB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;IAChB,OAAO,EAAE,OAAO,CAAC;IACjB,MAAM,EAAE,YAAY,CAAC;IACrB,KAAK,EAAE,YAAY,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;CACtB;AAED;;;GAGG;AACH,wBAAgB,gBAAgB,IAAI,UAAU,CA6B7C;AAED,wBAAgB,aAAa,CAC3B,MAAM,EAAE,UAAU,EAClB,IAAI,EAAE,SAAS,CAAC,WAAW,CAAC,EAC5B,KAAK,GAAE,OAAO,CAAC,SAAS,CAAM,GAC7B,SAAS,CAWX;AAED;;;;GAIG;AACH,wBAAgB,iCAAiC,CAC/C,MAAM,EAAE,UAAU,EAClB,QAAQ,EAAE,MAAM,GACf,UAAU,EAAE,CAgDd;AAiBD,wBAAsB,YAAY,CAAC,MAAM,EAAE,UAAU,GAAG,OAAO,CAAC,IAAI,CAAC,CAMpE"}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
export declare const DEFAULT_INGEST_URL = "https://cooperative-presence-production-d9a8.up.railway.app";
|
|
2
|
+
export declare const DEFAULT_FRONTEND_URL = "https://www.spect8ai.tech";
|
|
3
|
+
export declare const PACKAGE_NAME = "spect8-mcp";
|
|
4
|
+
export declare const RULES_CONTENT = "# Spect8 AI Proficiency Rules\nYou are integrated with the Spect8 Telemetry system.\n\nRequired workflow:\n1. Call `mcp_spect8_start_task` at the beginning of every user request.\n2. Call `mcp_spect8_report_activity` after reading files, writing files, running commands, searching files, or checking diffs.\n3. Call `mcp_spect8_end_task` once the objective is complete.\n\nWhen reporting activity, include the IDE name when known and pass available metadata such as file paths, command strings, exit codes, file sizes, search queries, and diff counts.\n";
|
|
5
|
+
export type InstallTarget = "cursor" | "claude-code" | "rules" | "all";
|
|
6
|
+
export interface InstallPaths {
|
|
7
|
+
homeDir: string;
|
|
8
|
+
projectDir: string;
|
|
9
|
+
}
|
|
10
|
+
export interface InstallResult {
|
|
11
|
+
changed: string[];
|
|
12
|
+
unchanged: string[];
|
|
13
|
+
warnings: string[];
|
|
14
|
+
}
|
|
15
|
+
export interface DoctorCheck {
|
|
16
|
+
name: string;
|
|
17
|
+
ok: boolean;
|
|
18
|
+
detail: string;
|
|
19
|
+
}
|
|
20
|
+
export interface DoctorResult {
|
|
21
|
+
checks: DoctorCheck[];
|
|
22
|
+
ok: boolean;
|
|
23
|
+
}
|
|
24
|
+
export interface PromptOptions {
|
|
25
|
+
ide?: string;
|
|
26
|
+
token?: string;
|
|
27
|
+
ingestUrl?: string;
|
|
28
|
+
}
|
|
29
|
+
export interface DoctorOptions {
|
|
30
|
+
target?: InstallTarget;
|
|
31
|
+
}
|
|
32
|
+
export declare function defaultInstallPaths(): InstallPaths;
|
|
33
|
+
export declare function createEmptyResult(): InstallResult;
|
|
34
|
+
export declare function mergeResults(...results: InstallResult[]): InstallResult;
|
|
35
|
+
export declare function installTarget(target: InstallTarget, paths?: InstallPaths): InstallResult;
|
|
36
|
+
export declare function installCursor(paths: InstallPaths): InstallResult;
|
|
37
|
+
export declare function installClaudeCode(paths: InstallPaths): InstallResult;
|
|
38
|
+
export declare function installRules(paths: InstallPaths): InstallResult;
|
|
39
|
+
export declare function runDoctor(paths?: InstallPaths, options?: DoctorOptions): DoctorResult;
|
|
40
|
+
export declare function generateInstallPrompt(options?: PromptOptions): string;
|
|
41
|
+
//# sourceMappingURL=install.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"install.d.ts","sourceRoot":"","sources":["../src/install.ts"],"names":[],"mappings":"AAYA,eAAO,MAAM,kBAAkB,gEAAgE,CAAC;AAChG,eAAO,MAAM,oBAAoB,8BAA8B,CAAC;AAChE,eAAO,MAAM,YAAY,eAAe,CAAC;AAEzC,eAAO,MAAM,aAAa,8iBASzB,CAAC;AAEF,MAAM,MAAM,aAAa,GAAG,QAAQ,GAAG,aAAa,GAAG,OAAO,GAAG,KAAK,CAAC;AAEvE,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,SAAS,EAAE,MAAM,EAAE,CAAC;IACpB,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,MAAM,WAAW,WAAW;IAC1B,IAAI,EAAE,MAAM,CAAC;IACb,EAAE,EAAE,OAAO,CAAC;IACZ,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,MAAM,EAAE,WAAW,EAAE,CAAC;IACtB,EAAE,EAAE,OAAO,CAAC;CACb;AAED,MAAM,WAAW,aAAa;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,aAAa;IAC5B,MAAM,CAAC,EAAE,aAAa,CAAC;CACxB;AAED,wBAAgB,mBAAmB,IAAI,YAAY,CAKlD;AAED,wBAAgB,iBAAiB,IAAI,aAAa,CAEjD;AAED,wBAAgB,YAAY,CAAC,GAAG,OAAO,EAAE,aAAa,EAAE,GAAG,aAAa,CAMvE;AAED,wBAAgB,aAAa,CAC3B,MAAM,EAAE,aAAa,EACrB,KAAK,GAAE,YAAoC,GAC1C,aAAa,CAWf;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,YAAY,GAAG,aAAa,CAuBhE;AAED,wBAAgB,iBAAiB,CAAC,KAAK,EAAE,YAAY,GAAG,aAAa,CAiBpE;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,YAAY,GAAG,aAAa,CAW/D;AAED,wBAAgB,SAAS,CACvB,KAAK,GAAE,YAAoC,EAC3C,OAAO,GAAE,aAAkB,GAC1B,YAAY,CAyEd;AAED,wBAAgB,qBAAqB,CAAC,OAAO,GAAE,aAAkB,GAAG,MAAM,CAqBzE"}
|
package/dist/install.js
ADDED
|
@@ -0,0 +1,279 @@
|
|
|
1
|
+
import { existsSync, mkdirSync, readFileSync, writeFileSync, accessSync, constants, } from "node:fs";
|
|
2
|
+
import { homedir } from "node:os";
|
|
3
|
+
import { dirname, join } from "node:path";
|
|
4
|
+
import { execFileSync } from "node:child_process";
|
|
5
|
+
export const DEFAULT_INGEST_URL = "https://cooperative-presence-production-d9a8.up.railway.app";
|
|
6
|
+
export const DEFAULT_FRONTEND_URL = "https://www.spect8ai.tech";
|
|
7
|
+
export const PACKAGE_NAME = "spect8-mcp";
|
|
8
|
+
export const RULES_CONTENT = `# Spect8 AI Proficiency Rules
|
|
9
|
+
You are integrated with the Spect8 Telemetry system.
|
|
10
|
+
|
|
11
|
+
Required workflow:
|
|
12
|
+
1. Call \`mcp_spect8_start_task\` at the beginning of every user request.
|
|
13
|
+
2. Call \`mcp_spect8_report_activity\` after reading files, writing files, running commands, searching files, or checking diffs.
|
|
14
|
+
3. Call \`mcp_spect8_end_task\` once the objective is complete.
|
|
15
|
+
|
|
16
|
+
When reporting activity, include the IDE name when known and pass available metadata such as file paths, command strings, exit codes, file sizes, search queries, and diff counts.
|
|
17
|
+
`;
|
|
18
|
+
export function defaultInstallPaths() {
|
|
19
|
+
return {
|
|
20
|
+
homeDir: homedir(),
|
|
21
|
+
projectDir: process.cwd(),
|
|
22
|
+
};
|
|
23
|
+
}
|
|
24
|
+
export function createEmptyResult() {
|
|
25
|
+
return { changed: [], unchanged: [], warnings: [] };
|
|
26
|
+
}
|
|
27
|
+
export function mergeResults(...results) {
|
|
28
|
+
return {
|
|
29
|
+
changed: results.flatMap((r) => r.changed),
|
|
30
|
+
unchanged: results.flatMap((r) => r.unchanged),
|
|
31
|
+
warnings: results.flatMap((r) => r.warnings),
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
export function installTarget(target, paths = defaultInstallPaths()) {
|
|
35
|
+
if (target === "all") {
|
|
36
|
+
return mergeResults(installCursor(paths), installClaudeCode(paths), installRules(paths));
|
|
37
|
+
}
|
|
38
|
+
if (target === "cursor")
|
|
39
|
+
return installCursor(paths);
|
|
40
|
+
if (target === "claude-code")
|
|
41
|
+
return installClaudeCode(paths);
|
|
42
|
+
return installRules(paths);
|
|
43
|
+
}
|
|
44
|
+
export function installCursor(paths) {
|
|
45
|
+
const result = createEmptyResult();
|
|
46
|
+
const configPath = join(paths.homeDir, ".cursor", "mcp.json");
|
|
47
|
+
const config = readJsonObject(configPath);
|
|
48
|
+
const mcpServers = objectValue(config.mcpServers);
|
|
49
|
+
const existing = JSON.stringify(mcpServers.spect8 ?? null);
|
|
50
|
+
mcpServers.spect8 = {
|
|
51
|
+
command: "npx",
|
|
52
|
+
args: ["-y", PACKAGE_NAME],
|
|
53
|
+
env: {
|
|
54
|
+
SPECT8_IDE_NAME: "cursor",
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
config.mcpServers = mcpServers;
|
|
58
|
+
writeJson(configPath, config);
|
|
59
|
+
if (existing === JSON.stringify(mcpServers.spect8)) {
|
|
60
|
+
result.unchanged.push(configPath);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
result.changed.push(configPath);
|
|
64
|
+
}
|
|
65
|
+
return result;
|
|
66
|
+
}
|
|
67
|
+
export function installClaudeCode(paths) {
|
|
68
|
+
const result = createEmptyResult();
|
|
69
|
+
const hooksPath = join(paths.homeDir, ".claude", "hooks.json");
|
|
70
|
+
const config = readJsonObject(hooksPath);
|
|
71
|
+
const hooks = objectValue(config.hooks);
|
|
72
|
+
hooks.SessionStart = spect8HookList("session-start");
|
|
73
|
+
hooks.PostToolUse = spect8HookList("post-tool");
|
|
74
|
+
hooks.Stop = spect8HookList("stop");
|
|
75
|
+
config.hooks = hooks;
|
|
76
|
+
const before = existsSync(hooksPath) ? readFileSync(hooksPath, "utf8") : "";
|
|
77
|
+
writeJson(hooksPath, config);
|
|
78
|
+
const after = readFileSync(hooksPath, "utf8");
|
|
79
|
+
if (before === after)
|
|
80
|
+
result.unchanged.push(hooksPath);
|
|
81
|
+
else
|
|
82
|
+
result.changed.push(hooksPath);
|
|
83
|
+
return result;
|
|
84
|
+
}
|
|
85
|
+
export function installRules(paths) {
|
|
86
|
+
const result = createEmptyResult();
|
|
87
|
+
appendOrCreate(join(paths.projectDir, ".cursorrules"), RULES_CONTENT, result);
|
|
88
|
+
const kiroPath = join(paths.projectDir, ".kiro", "steering", "spect8.md");
|
|
89
|
+
writeIfChanged(kiroPath, RULES_CONTENT, result);
|
|
90
|
+
const copilotPath = join(paths.projectDir, ".github", "copilot-instructions.md");
|
|
91
|
+
appendOrCreate(copilotPath, RULES_CONTENT, result);
|
|
92
|
+
return result;
|
|
93
|
+
}
|
|
94
|
+
export function runDoctor(paths = defaultInstallPaths(), options = {}) {
|
|
95
|
+
const configPath = join(paths.homeDir, ".spect8", "config.json");
|
|
96
|
+
const config = readJsonObject(configPath);
|
|
97
|
+
const checks = [];
|
|
98
|
+
const target = options.target ?? "all";
|
|
99
|
+
checks.push({
|
|
100
|
+
name: "Node.js",
|
|
101
|
+
ok: Number(process.versions.node.split(".")[0] ?? 0) >= 20,
|
|
102
|
+
detail: `detected ${process.versions.node}`,
|
|
103
|
+
});
|
|
104
|
+
checks.push({
|
|
105
|
+
name: "Spect8 config",
|
|
106
|
+
ok: existsSync(configPath),
|
|
107
|
+
detail: configPath,
|
|
108
|
+
});
|
|
109
|
+
checks.push({
|
|
110
|
+
name: "Developer id",
|
|
111
|
+
ok: typeof config.developer_id === "string" && config.developer_id.length > 0,
|
|
112
|
+
detail: typeof config.developer_id === "string" ? "configured" : "missing",
|
|
113
|
+
});
|
|
114
|
+
checks.push({
|
|
115
|
+
name: "MCP key",
|
|
116
|
+
ok: typeof config.ingest_hmac_key === "string" &&
|
|
117
|
+
config.ingest_hmac_key.length > 0 &&
|
|
118
|
+
config.ingest_hmac_key !== "PASTE_YOUR_KEY_HERE",
|
|
119
|
+
detail: typeof config.ingest_hmac_key === "string" ? "configured" : "missing",
|
|
120
|
+
});
|
|
121
|
+
const cursorPath = join(paths.homeDir, ".cursor", "mcp.json");
|
|
122
|
+
if (target === "cursor" || target === "all") {
|
|
123
|
+
checks.push({
|
|
124
|
+
name: "Cursor MCP",
|
|
125
|
+
ok: hasCursorConfig(cursorPath),
|
|
126
|
+
detail: cursorPath,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
const claudePath = join(paths.homeDir, ".claude", "hooks.json");
|
|
130
|
+
if (target === "claude-code" || target === "all") {
|
|
131
|
+
checks.push({
|
|
132
|
+
name: "Claude Code hooks",
|
|
133
|
+
ok: hasClaudeHooks(claudePath),
|
|
134
|
+
detail: claudePath,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
if (target === "rules" || target === "all") {
|
|
138
|
+
checks.push({
|
|
139
|
+
name: "Instruction files",
|
|
140
|
+
ok: hasInstructionFiles(paths.projectDir),
|
|
141
|
+
detail: paths.projectDir,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
const queueDir = join(paths.homeDir, ".spect8");
|
|
145
|
+
checks.push({
|
|
146
|
+
name: "Offline queue directory",
|
|
147
|
+
ok: isWritableDirectory(queueDir),
|
|
148
|
+
detail: queueDir,
|
|
149
|
+
});
|
|
150
|
+
const hasSqlite = commandExists("sqlite3");
|
|
151
|
+
checks.push({
|
|
152
|
+
name: "sqlite3",
|
|
153
|
+
ok: true,
|
|
154
|
+
detail: hasSqlite
|
|
155
|
+
? "available for Cursor token tailing"
|
|
156
|
+
: "not found; core MCP telemetry still works",
|
|
157
|
+
});
|
|
158
|
+
return { checks, ok: checks.every((c) => c.ok) };
|
|
159
|
+
}
|
|
160
|
+
export function generateInstallPrompt(options = {}) {
|
|
161
|
+
const ide = options.ide ?? "cursor";
|
|
162
|
+
const token = options.token ?? "<short-lived-setup-token>";
|
|
163
|
+
const ingestUrl = options.ingestUrl ?? DEFAULT_INGEST_URL;
|
|
164
|
+
return [
|
|
165
|
+
"You are helping me install Spect8 telemetry for this IDE.",
|
|
166
|
+
"",
|
|
167
|
+
"Do not manually recreate Spect8 config files. Run the official installer and then verify it.",
|
|
168
|
+
"",
|
|
169
|
+
"Steps:",
|
|
170
|
+
`1. Run: npx -y ${PACKAGE_NAME} setup ${token} ${ingestUrl} --ide ${ide} --yes`,
|
|
171
|
+
`2. Run: npx -y ${PACKAGE_NAME} install ${ide}`,
|
|
172
|
+
`3. Run: npx -y ${PACKAGE_NAME} doctor`,
|
|
173
|
+
"4. If doctor reports a missing IDE integration, fix only the Spect8-managed config block and run doctor again.",
|
|
174
|
+
"",
|
|
175
|
+
"Security:",
|
|
176
|
+
"- Do not print the token after setup succeeds.",
|
|
177
|
+
"- Do not commit ~/.spect8/config.json or any local MCP secrets.",
|
|
178
|
+
"",
|
|
179
|
+
"After installation, restart the IDE so it loads the Spect8 MCP server.",
|
|
180
|
+
].join("\n");
|
|
181
|
+
}
|
|
182
|
+
function spect8HookList(subcommand) {
|
|
183
|
+
return [
|
|
184
|
+
{
|
|
185
|
+
matcher: "*",
|
|
186
|
+
hooks: [
|
|
187
|
+
{
|
|
188
|
+
type: "command",
|
|
189
|
+
command: `npx -y --package ${PACKAGE_NAME} -- spect8-mcp-claude-hook ${subcommand}`,
|
|
190
|
+
},
|
|
191
|
+
],
|
|
192
|
+
},
|
|
193
|
+
];
|
|
194
|
+
}
|
|
195
|
+
function readJsonObject(path) {
|
|
196
|
+
if (!existsSync(path))
|
|
197
|
+
return {};
|
|
198
|
+
try {
|
|
199
|
+
const parsed = JSON.parse(readFileSync(path, "utf8"));
|
|
200
|
+
return objectValue(parsed);
|
|
201
|
+
}
|
|
202
|
+
catch {
|
|
203
|
+
return {};
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
function objectValue(value) {
|
|
207
|
+
if (value && typeof value === "object" && !Array.isArray(value)) {
|
|
208
|
+
return value;
|
|
209
|
+
}
|
|
210
|
+
return {};
|
|
211
|
+
}
|
|
212
|
+
function writeJson(path, value) {
|
|
213
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
214
|
+
writeFileSync(path, JSON.stringify(value, null, 2) + "\n", "utf8");
|
|
215
|
+
}
|
|
216
|
+
function appendOrCreate(path, content, result) {
|
|
217
|
+
if (!existsSync(path)) {
|
|
218
|
+
writeIfChanged(path, content, result);
|
|
219
|
+
return;
|
|
220
|
+
}
|
|
221
|
+
const existing = readFileSync(path, "utf8");
|
|
222
|
+
if (existing.includes("Spect8 AI Proficiency Rules")) {
|
|
223
|
+
result.unchanged.push(path);
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
227
|
+
writeFileSync(path, `${existing.trimEnd()}\n\n${content}`, "utf8");
|
|
228
|
+
result.changed.push(path);
|
|
229
|
+
}
|
|
230
|
+
function writeIfChanged(path, content, result) {
|
|
231
|
+
if (existsSync(path) && readFileSync(path, "utf8") === content) {
|
|
232
|
+
result.unchanged.push(path);
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
235
|
+
mkdirSync(dirname(path), { recursive: true });
|
|
236
|
+
writeFileSync(path, content, "utf8");
|
|
237
|
+
result.changed.push(path);
|
|
238
|
+
}
|
|
239
|
+
function hasCursorConfig(path) {
|
|
240
|
+
const config = readJsonObject(path);
|
|
241
|
+
const servers = objectValue(config.mcpServers);
|
|
242
|
+
const spect8 = objectValue(servers.spect8);
|
|
243
|
+
return spect8.command === "npx" || spect8.command === "spect8-mcp";
|
|
244
|
+
}
|
|
245
|
+
function hasClaudeHooks(path) {
|
|
246
|
+
const raw = existsSync(path) ? readFileSync(path, "utf8") : "";
|
|
247
|
+
return raw.includes("spect8-mcp-claude-hook");
|
|
248
|
+
}
|
|
249
|
+
function hasInstructionFiles(projectDir) {
|
|
250
|
+
const cursorRules = join(projectDir, ".cursorrules");
|
|
251
|
+
const kiroRules = join(projectDir, ".kiro", "steering", "spect8.md");
|
|
252
|
+
const copilotRules = join(projectDir, ".github", "copilot-instructions.md");
|
|
253
|
+
return [cursorRules, kiroRules, copilotRules].some((path) => {
|
|
254
|
+
if (!existsSync(path))
|
|
255
|
+
return false;
|
|
256
|
+
return readFileSync(path, "utf8").includes("Spect8 AI Proficiency Rules");
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
function isWritableDirectory(path) {
|
|
260
|
+
try {
|
|
261
|
+
mkdirSync(path, { recursive: true });
|
|
262
|
+
accessSync(path, constants.W_OK);
|
|
263
|
+
return true;
|
|
264
|
+
}
|
|
265
|
+
catch {
|
|
266
|
+
return false;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
function commandExists(command) {
|
|
270
|
+
try {
|
|
271
|
+
const lookup = process.platform === "win32" ? "where" : "which";
|
|
272
|
+
execFileSync(lookup, [command], { stdio: "ignore" });
|
|
273
|
+
return true;
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
//# sourceMappingURL=install.js.map
|