spect8-mcp 0.2.1 → 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 +12 -20
- package/dist/collectors/cursor_sqlite.d.ts.map +1 -1
- package/dist/collectors/cursor_sqlite.js +81 -116
- 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
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
# Spect8 Installation Plan
|
|
2
|
+
|
|
3
|
+
## Goal
|
|
4
|
+
|
|
5
|
+
Make a new Spect8 install require one command, work across MCP-capable and
|
|
6
|
+
hook-capable IDEs, and provide a safe AI-assisted fallback prompt for users who
|
|
7
|
+
want their IDE agent to run setup for them.
|
|
8
|
+
|
|
9
|
+
## Primary User Flow
|
|
10
|
+
|
|
11
|
+
```bash
|
|
12
|
+
npx -y spect8-mcp setup --ide cursor
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
The setup command should:
|
|
16
|
+
|
|
17
|
+
1. Authenticate the user through Spect8 browser auth or accept a setup token.
|
|
18
|
+
2. Store credentials in `~/.spect8/config.json`.
|
|
19
|
+
3. Install the requested IDE integration.
|
|
20
|
+
4. Install project instruction files when requested.
|
|
21
|
+
5. Run `spect8 doctor` checks and print next steps.
|
|
22
|
+
|
|
23
|
+
For already-authenticated users:
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
spect8 install cursor
|
|
27
|
+
spect8 install claude-code
|
|
28
|
+
spect8 install rules
|
|
29
|
+
spect8 install all
|
|
30
|
+
spect8 doctor
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## How The AI Knows To Use Spect8
|
|
34
|
+
|
|
35
|
+
Spect8 setup must combine two mechanisms:
|
|
36
|
+
|
|
37
|
+
1. **Tool availability:** MCP config exposes `start_task`, `report_activity`,
|
|
38
|
+
`end_task`, and `get_score` to the IDE.
|
|
39
|
+
2. **Behavior instruction or hook:** IDE rules tell the AI when to call those
|
|
40
|
+
tools, while hook-capable IDEs emit telemetry automatically after real tool
|
|
41
|
+
calls.
|
|
42
|
+
|
|
43
|
+
MCP alone is not enough; it makes tools available but does not guarantee the AI
|
|
44
|
+
will call them. Hooks are preferred when available because they are not dependent
|
|
45
|
+
on prompt compliance.
|
|
46
|
+
|
|
47
|
+
## Compatibility Tiers
|
|
48
|
+
|
|
49
|
+
### Tier 1: MCP-Native IDEs
|
|
50
|
+
|
|
51
|
+
Examples: Cursor, Windsurf, VS Code MCP clients.
|
|
52
|
+
|
|
53
|
+
Install strategy:
|
|
54
|
+
|
|
55
|
+
- Write or merge the IDE MCP JSON.
|
|
56
|
+
- Point the server at `npx -y spect8-mcp`.
|
|
57
|
+
- Set only IDE-specific environment such as `SPECT8_IDE_NAME`; credentials come
|
|
58
|
+
from `~/.spect8/config.json`.
|
|
59
|
+
- Add instruction files where the IDE supports them.
|
|
60
|
+
|
|
61
|
+
### Tier 2: Hook-Capable IDEs
|
|
62
|
+
|
|
63
|
+
Example: Claude Code.
|
|
64
|
+
|
|
65
|
+
Install strategy:
|
|
66
|
+
|
|
67
|
+
- Write or merge hook config.
|
|
68
|
+
- Use platform-safe commands that do not rely on shell-specific env expansion.
|
|
69
|
+
- Let hooks record observed tool calls and token usage automatically.
|
|
70
|
+
- Use instructions only for task boundaries and optional score checks.
|
|
71
|
+
|
|
72
|
+
### Tier 3: Instruction-Only IDEs
|
|
73
|
+
|
|
74
|
+
Examples: Kiro, GitHub Copilot, Antigravity-style agents without MCP/hook
|
|
75
|
+
support.
|
|
76
|
+
|
|
77
|
+
Install strategy:
|
|
78
|
+
|
|
79
|
+
- Install IDE-specific instruction files.
|
|
80
|
+
- Ask the AI to call `report_activity` when the IDE exposes Spect8 tools.
|
|
81
|
+
- Mark this mode as lower-fidelity if the IDE cannot expose MCP tools.
|
|
82
|
+
|
|
83
|
+
## Secondary AI-Assisted Install
|
|
84
|
+
|
|
85
|
+
Some users will prefer to paste a custom prompt into their IDE agent. This is
|
|
86
|
+
viable as long as the prompt tells the agent to run the official installer
|
|
87
|
+
instead of manually recreating config files.
|
|
88
|
+
|
|
89
|
+
Generate the prompt:
|
|
90
|
+
|
|
91
|
+
```bash
|
|
92
|
+
spect8 prompt --ide cursor --token <short-lived-setup-token>
|
|
93
|
+
```
|
|
94
|
+
|
|
95
|
+
The prompt should instruct the IDE agent to:
|
|
96
|
+
|
|
97
|
+
1. Run `npx -y spect8-mcp setup <token> <ingest_url> --ide <ide> --yes`.
|
|
98
|
+
2. Run `npx -y spect8-mcp install <ide>`.
|
|
99
|
+
3. Run `npx -y spect8-mcp doctor`.
|
|
100
|
+
4. Avoid printing or committing secrets.
|
|
101
|
+
|
|
102
|
+
Long-lived MCP keys should not be embedded in prompts when avoidable. The
|
|
103
|
+
product should eventually issue short-lived setup tokens that the CLI exchanges
|
|
104
|
+
for the local MCP key.
|
|
105
|
+
|
|
106
|
+
## Test Coverage Required
|
|
107
|
+
|
|
108
|
+
Installation coverage should include:
|
|
109
|
+
|
|
110
|
+
- Config generation and preservation of existing config fields.
|
|
111
|
+
- Cursor MCP JSON creation and merge behavior.
|
|
112
|
+
- Claude Code hook creation without shell-specific syntax.
|
|
113
|
+
- Instruction file creation and idempotent append behavior.
|
|
114
|
+
- Doctor checks for auth, IDE config, offline queue directory, and optional
|
|
115
|
+
Cursor `sqlite3` support.
|
|
116
|
+
- Personalized prompt generation.
|
|
117
|
+
- End-to-end telemetry flow from installed MCP tools through `start_task`,
|
|
118
|
+
`report_activity`, and `end_task` into a batcher/offline queue test double.
|
|
119
|
+
|
|
120
|
+
## Known Follow-Ups
|
|
121
|
+
|
|
122
|
+
- Add backend support for short-lived setup tokens.
|
|
123
|
+
- Add installers for additional MCP client config paths as their schemas are
|
|
124
|
+
confirmed.
|
|
125
|
+
- Add an uninstall command that removes only Spect8-managed blocks.
|
|
126
|
+
- Add a real backend connectivity check to `spect8 doctor`.
|
package/README.md
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
#
|
|
1
|
+
# spect8-mcp
|
|
2
2
|
|
|
3
3
|
Local MCP server + Claude Code hooks that capture Cursor / Claude Code tool
|
|
4
4
|
calls and token usage, and forward them to a Spect8 ingest API.
|
|
@@ -6,9 +6,14 @@ calls and token usage, and forward them to a Spect8 ingest API.
|
|
|
6
6
|
## Install
|
|
7
7
|
|
|
8
8
|
```bash
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
9
|
+
npx -y spect8-mcp setup --ide cursor
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
You can also install globally:
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
npm install -g spect8-mcp
|
|
16
|
+
spect8 setup --ide cursor
|
|
12
17
|
```
|
|
13
18
|
|
|
14
19
|
### One-time config
|
|
@@ -17,7 +22,7 @@ Create `~/.spect8/config.json`:
|
|
|
17
22
|
|
|
18
23
|
```json
|
|
19
24
|
{
|
|
20
|
-
"ingest_url": "https://
|
|
25
|
+
"ingest_url": "https://cooperative-presence-production-d9a8.up.railway.app",
|
|
21
26
|
"ingest_hmac_key": "<32-byte hex shared secret>",
|
|
22
27
|
"developer_id": "dev-your-id",
|
|
23
28
|
"ide_name": "cursor"
|
|
@@ -27,16 +32,44 @@ Create `~/.spect8/config.json`:
|
|
|
27
32
|
All fields can also be supplied via env vars (same uppercase names prefixed
|
|
28
33
|
with `SPECT8_`, e.g. `SPECT8_INGEST_URL`).
|
|
29
34
|
|
|
35
|
+
For manual key-based setup:
|
|
36
|
+
|
|
37
|
+
```bash
|
|
38
|
+
spect8 setup <mcp_key> [ingest_url] --ide cursor
|
|
39
|
+
```
|
|
40
|
+
|
|
30
41
|
## Cursor wiring
|
|
31
42
|
|
|
32
|
-
|
|
33
|
-
|
|
43
|
+
```bash
|
|
44
|
+
spect8 install cursor
|
|
45
|
+
spect8 doctor cursor
|
|
46
|
+
```
|
|
34
47
|
|
|
35
48
|
## Claude Code wiring
|
|
36
49
|
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
50
|
+
```bash
|
|
51
|
+
spect8 install claude-code
|
|
52
|
+
spect8 doctor claude-code
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
## Instruction files
|
|
56
|
+
|
|
57
|
+
For IDEs that rely on project instructions:
|
|
58
|
+
|
|
59
|
+
```bash
|
|
60
|
+
spect8 install rules
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
This writes Spect8 instructions to `.cursorrules`, `.kiro/steering/spect8.md`,
|
|
64
|
+
and `.github/copilot-instructions.md`.
|
|
65
|
+
|
|
66
|
+
## AI-assisted setup prompt
|
|
67
|
+
|
|
68
|
+
To generate a prompt a user can paste into their IDE agent:
|
|
69
|
+
|
|
70
|
+
```bash
|
|
71
|
+
spect8 prompt --ide cursor --token <short-lived-setup-token>
|
|
72
|
+
```
|
|
40
73
|
|
|
41
74
|
## Provided tools
|
|
42
75
|
|
|
@@ -49,8 +82,8 @@ binary runs via npx so global install is not required.
|
|
|
49
82
|
|
|
50
83
|
- HMAC-SHA256 signed POSTs (`X-Spect8-Signature` + `X-Spect8-Timestamp`)
|
|
51
84
|
reaching `/api/v1/events` and `/api/v1/token_events`.
|
|
52
|
-
- 500 ms / 50 event batcher with
|
|
53
|
-
offline_queue.
|
|
85
|
+
- 500 ms / 50 event batcher with a durable JSON offline queue
|
|
86
|
+
(`~/.spect8/offline_queue.json`) so no event is lost across network blips.
|
|
54
87
|
|
|
55
88
|
## Local data we read
|
|
56
89
|
|
package/dist/auth.d.ts
CHANGED
|
@@ -3,5 +3,5 @@ import type { Logger } from "./logger.js";
|
|
|
3
3
|
* Starts a temporary local server to receive the MCP key from the browser.
|
|
4
4
|
* Returns a promise that resolves when the key is received and saved.
|
|
5
5
|
*/
|
|
6
|
-
export declare function runInteractiveAuth(logger: Logger, frontendUrl
|
|
6
|
+
export declare function runInteractiveAuth(logger: Logger, frontendUrl?: string): Promise<void>;
|
|
7
7
|
//# sourceMappingURL=auth.d.ts.map
|
package/dist/auth.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"auth.d.ts","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAE1C;;;GAGG;AACH,wBAAsB,kBAAkB,CACtC,MAAM,EAAE,MAAM,EACd,WAAW,GAAE,MAA6B,GACzC,OAAO,CAAC,IAAI,CAAC,CA8Df"}
|
package/dist/auth.js
CHANGED
|
@@ -1,11 +1,12 @@
|
|
|
1
1
|
import http from "node:http";
|
|
2
|
-
import {
|
|
2
|
+
import { execFile } from "node:child_process";
|
|
3
3
|
import { saveConfig } from "./config.js";
|
|
4
|
+
import { DEFAULT_FRONTEND_URL } from "./install.js";
|
|
4
5
|
/**
|
|
5
6
|
* Starts a temporary local server to receive the MCP key from the browser.
|
|
6
7
|
* Returns a promise that resolves when the key is received and saved.
|
|
7
8
|
*/
|
|
8
|
-
export async function runInteractiveAuth(logger, frontendUrl) {
|
|
9
|
+
export async function runInteractiveAuth(logger, frontendUrl = DEFAULT_FRONTEND_URL) {
|
|
9
10
|
return new Promise((resolve) => {
|
|
10
11
|
const port = 9999;
|
|
11
12
|
const callbackUrl = `http://localhost:${port}/callback`;
|
|
@@ -52,8 +53,15 @@ export async function runInteractiveAuth(logger, frontendUrl) {
|
|
|
52
53
|
logger.info(`Starting interactive auth...`);
|
|
53
54
|
logger.info(`Please visit: ${connectUrl}`);
|
|
54
55
|
// Attempt to open the browser automatically
|
|
55
|
-
|
|
56
|
-
|
|
56
|
+
if (process.platform === "darwin") {
|
|
57
|
+
execFile("open", [connectUrl]);
|
|
58
|
+
}
|
|
59
|
+
else if (process.platform === "win32") {
|
|
60
|
+
execFile("cmd", ["/c", "start", "", connectUrl]);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
execFile("xdg-open", [connectUrl]);
|
|
64
|
+
}
|
|
57
65
|
});
|
|
58
66
|
});
|
|
59
67
|
}
|
package/dist/auth.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"auth.js","sourceRoot":"","sources":["../src/auth.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,WAAW,CAAC;AAC7B,OAAO,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAC;AAC9C,OAAO,EAAE,UAAU,EAAE,MAAM,aAAa,CAAC;AACzC,OAAO,EAAE,oBAAoB,EAAE,MAAM,cAAc,CAAC;AAGpD;;;GAGG;AACH,MAAM,CAAC,KAAK,UAAU,kBAAkB,CACtC,MAAc,EACd,cAAsB,oBAAoB;IAE1C,OAAO,IAAI,OAAO,CAAC,CAAC,OAAO,EAAE,EAAE;QAC7B,MAAM,IAAI,GAAG,IAAI,CAAC;QAClB,MAAM,WAAW,GAAG,oBAAoB,IAAI,WAAW,CAAC;QACxD,MAAM,UAAU,GAAG,GAAG,WAAW,qBAAqB,kBAAkB,CAAC,WAAW,CAAC,EAAE,CAAC;QAExF,MAAM,MAAM,GAAG,IAAI,CAAC,YAAY,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,EAAE;YAC5C,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,EAAE,oBAAoB,IAAI,EAAE,CAAC,CAAC;YAE/D,IAAI,GAAG,CAAC,QAAQ,KAAK,WAAW,EAAE,CAAC;gBACjC,MAAM,GAAG,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC;gBACxC,MAAM,WAAW,GAAG,GAAG,CAAC,YAAY,CAAC,GAAG,CAAC,aAAa,CAAC,CAAC;gBAExD,IAAI,GAAG,IAAI,WAAW,EAAE,CAAC;oBACvB,UAAU,CAAC;wBACT,eAAe,EAAE,GAAG;wBACpB,YAAY,EAAE,WAAW;qBAC1B,CAAC,CAAC;oBAEH,GAAG,CAAC,SAAS,CAAC,GAAG,EAAE,EAAE,cAAc,EAAE,WAAW,EAAE,CAAC,CAAC;oBACpD,GAAG,CAAC,GAAG,CAAC;;;;;;;;;WASP,CAAC,CAAC;oBAEH,MAAM,CAAC,IAAI,CAAC,iDAAiD,CAAC,CAAC;oBAE/D,wDAAwD;oBACxD,UAAU,CAAC,GAAG,EAAE;wBACd,MAAM,CAAC,KAAK,EAAE,CAAC;wBACf,OAAO,EAAE,CAAC;oBACZ,CAAC,EAAE,IAAI,CAAC,CAAC;gBACX,CAAC;qBAAM,CAAC;oBACN,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;oBACnB,GAAG,CAAC,GAAG,CAAC,4BAA4B,CAAC,CAAC;gBACxC,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,GAAG,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC;gBACnB,GAAG,CAAC,GAAG,EAAE,CAAC;YACZ,CAAC;QACH,CAAC,CAAC,CAAC;QAEH,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,GAAG,EAAE;YACvB,MAAM,CAAC,IAAI,CAAC,8BAA8B,CAAC,CAAC;YAC5C,MAAM,CAAC,IAAI,CAAC,iBAAiB,UAAU,EAAE,CAAC,CAAC;YAE3C,4CAA4C;YAC5C,IAAI,OAAO,CAAC,QAAQ,KAAK,QAAQ,EAAE,CAAC;gBAClC,QAAQ,CAAC,MAAM,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;YACjC,CAAC;iBAAM,IAAI,OAAO,CAAC,QAAQ,KAAK,OAAO,EAAE,CAAC;gBACxC,QAAQ,CAAC,KAAK,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,UAAU,CAAC,CAAC,CAAC;YACnD,CAAC;iBAAM,CAAC;gBACN,QAAQ,CAAC,UAAU,EAAE,CAAC,UAAU,CAAC,CAAC,CAAC;YACrC,CAAC;QACH,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC"}
|
package/dist/cli.js
CHANGED
|
@@ -1,147 +1,192 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import { URL } from "url";
|
|
8
|
-
const RULES_CONTENT = `# Spect8 AI Proficiency Rules
|
|
9
|
-
You are integrated with the Spect8 Telemetry system. To maintain logical density tracking, you MUST:
|
|
10
|
-
1. Call \`mcp_spect8_start_task\` at the beginning of every user request.
|
|
11
|
-
2. Call \`mcp_spect8_end_task\` once the objective is met.
|
|
12
|
-
3. Identify yourself in the \`ide_name\` parameter (e.g., 'cursor', 'kiro', 'antigravity').
|
|
13
|
-
4. After EVERY file read, file write, command execution, search, or git diff, call \`mcp_spect8_report_activity\` with:
|
|
14
|
-
- activity_type: the type of action ('read_file', 'write_file', 'run_command', 'git_diff', 'search_files')
|
|
15
|
-
- file_path: the path of the file (for reads/writes)
|
|
16
|
-
- file_size_bytes: size of content written (for writes)
|
|
17
|
-
- command: the command string (for run_command)
|
|
18
|
-
- exit_code: the result code (for run_command)
|
|
19
|
-
`;
|
|
2
|
+
import { execFile } from "node:child_process";
|
|
3
|
+
import { createServer } from "node:http";
|
|
4
|
+
import { URL } from "node:url";
|
|
5
|
+
import { saveConfig } from "./config.js";
|
|
6
|
+
import { DEFAULT_FRONTEND_URL, DEFAULT_INGEST_URL, generateInstallPrompt, installTarget, runDoctor, } from "./install.js";
|
|
20
7
|
function log(msg) {
|
|
21
8
|
console.log(`[Spect8] ${msg}`);
|
|
22
9
|
}
|
|
23
|
-
function
|
|
24
|
-
const
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
if (!content.includes("Spect8")) {
|
|
32
|
-
writeFileSync(cursorRulesPath, content + "\n\n" + RULES_CONTENT);
|
|
33
|
-
log("✅ Appended Spect8 rules to .cursorrules");
|
|
10
|
+
function parseArgs(args) {
|
|
11
|
+
const positionals = [];
|
|
12
|
+
const options = {};
|
|
13
|
+
for (let i = 0; i < args.length; i++) {
|
|
14
|
+
const arg = args[i] ?? "";
|
|
15
|
+
if (!arg.startsWith("--")) {
|
|
16
|
+
positionals.push(arg);
|
|
17
|
+
continue;
|
|
34
18
|
}
|
|
35
|
-
|
|
36
|
-
|
|
19
|
+
const withoutPrefix = arg.slice(2);
|
|
20
|
+
const [rawKey = "", inlineValue] = withoutPrefix.split("=", 2);
|
|
21
|
+
const key = rawKey.replaceAll("-", "_");
|
|
22
|
+
if (inlineValue !== undefined) {
|
|
23
|
+
options[key] = inlineValue;
|
|
24
|
+
continue;
|
|
37
25
|
}
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
mkdirSync(kiroDir, { recursive: true });
|
|
43
|
-
const kiroPath = join(kiroDir, "spect8.md");
|
|
44
|
-
writeFileSync(kiroPath, RULES_CONTENT);
|
|
45
|
-
log("✅ Created .kiro/steering/spect8.md");
|
|
46
|
-
}
|
|
47
|
-
function enforceCopilot(targetDir) {
|
|
48
|
-
const copilotDir = join(targetDir, ".github");
|
|
49
|
-
mkdirSync(copilotDir, { recursive: true });
|
|
50
|
-
const copilotPath = join(copilotDir, "copilot-instructions.md");
|
|
51
|
-
if (!existsSync(copilotPath)) {
|
|
52
|
-
writeFileSync(copilotPath, RULES_CONTENT);
|
|
53
|
-
log("✅ Created .github/copilot-instructions.md");
|
|
54
|
-
}
|
|
55
|
-
else {
|
|
56
|
-
const content = readFileSync(copilotPath, "utf-8");
|
|
57
|
-
if (!content.includes("Spect8")) {
|
|
58
|
-
writeFileSync(copilotPath, content + "\n\n" + RULES_CONTENT);
|
|
59
|
-
log("✅ Appended Spect8 rules to copilot-instructions.md");
|
|
26
|
+
const next = args[i + 1];
|
|
27
|
+
if (next && !next.startsWith("--")) {
|
|
28
|
+
options[key] = next;
|
|
29
|
+
i++;
|
|
60
30
|
}
|
|
61
31
|
else {
|
|
62
|
-
|
|
32
|
+
options[key] = true;
|
|
63
33
|
}
|
|
64
34
|
}
|
|
35
|
+
return { positionals, options };
|
|
36
|
+
}
|
|
37
|
+
function optionString(options, key) {
|
|
38
|
+
const value = options[key];
|
|
39
|
+
return typeof value === "string" ? value : undefined;
|
|
40
|
+
}
|
|
41
|
+
function normalizeTarget(raw) {
|
|
42
|
+
const value = (raw ?? "all").toLowerCase();
|
|
43
|
+
if (value === "claude" || value === "claude_code")
|
|
44
|
+
return "claude-code";
|
|
45
|
+
if (value === "cursor")
|
|
46
|
+
return "cursor";
|
|
47
|
+
if (value === "rules" || value === "instructions")
|
|
48
|
+
return "rules";
|
|
49
|
+
return "all";
|
|
50
|
+
}
|
|
51
|
+
function printInstallResult(target) {
|
|
52
|
+
const result = installTarget(target);
|
|
53
|
+
for (const file of result.changed)
|
|
54
|
+
log(`Updated ${file}`);
|
|
55
|
+
for (const file of result.unchanged)
|
|
56
|
+
log(`Already configured ${file}`);
|
|
57
|
+
for (const warning of result.warnings)
|
|
58
|
+
log(`Warning: ${warning}`);
|
|
59
|
+
log(`Install target '${target}' complete.`);
|
|
65
60
|
}
|
|
66
61
|
function setupGlobalMcpConfig(apiKey, devId, ingestUrl) {
|
|
67
|
-
const mcpConfigDir = join(homedir(), ".spect8");
|
|
68
|
-
mkdirSync(mcpConfigDir, { recursive: true });
|
|
69
|
-
const configPath = join(mcpConfigDir, "config.json");
|
|
70
62
|
const config = {
|
|
71
63
|
ingest_hmac_key: apiKey || "PASTE_YOUR_KEY_HERE",
|
|
72
64
|
developer_id: devId || "your-email@example.com",
|
|
73
|
-
ingest_url: ingestUrl ||
|
|
74
|
-
ide_name: "
|
|
65
|
+
ingest_url: ingestUrl || DEFAULT_INGEST_URL,
|
|
66
|
+
ide_name: "other",
|
|
75
67
|
};
|
|
76
|
-
|
|
77
|
-
log(
|
|
68
|
+
saveConfig(config);
|
|
69
|
+
log(`Configured ~/.spect8/config.json with ${config.ingest_url}`);
|
|
78
70
|
}
|
|
79
|
-
function
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
const targetDir = process.cwd();
|
|
84
|
-
log(`Initializing Spect8 rules in ${targetDir}...`);
|
|
85
|
-
enforceCursor(targetDir);
|
|
86
|
-
enforceKiro(targetDir);
|
|
87
|
-
enforceCopilot(targetDir);
|
|
88
|
-
log("✨ Project is now Spect8-compliant.");
|
|
71
|
+
function openBrowser(url) {
|
|
72
|
+
if (process.platform === "darwin") {
|
|
73
|
+
execFile("open", [url]);
|
|
74
|
+
return;
|
|
89
75
|
}
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
76
|
+
if (process.platform === "win32") {
|
|
77
|
+
execFile("cmd", ["/c", "start", "", url]);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
execFile("xdg-open", [url]);
|
|
81
|
+
}
|
|
82
|
+
async function interactiveSetup(frontendUrl) {
|
|
83
|
+
const port = 9090;
|
|
84
|
+
const callbackUrl = `http://localhost:${port}/callback`;
|
|
85
|
+
const authUrl = `${frontendUrl.replace(/\/+$/, "")}/connect?callback=${encodeURIComponent(callbackUrl)}`;
|
|
86
|
+
await new Promise((resolve, reject) => {
|
|
87
|
+
const server = createServer((req, res) => {
|
|
88
|
+
res.setHeader("Access-Control-Allow-Origin", "*");
|
|
89
|
+
res.setHeader("Access-Control-Allow-Methods", "GET, OPTIONS");
|
|
90
|
+
const url = new URL(req.url || "", callbackUrl);
|
|
91
|
+
if (url.pathname === "/callback") {
|
|
92
|
+
const key = url.searchParams.get("key");
|
|
93
|
+
const devId = url.searchParams.get("developerId");
|
|
94
|
+
if (key) {
|
|
95
|
+
setupGlobalMcpConfig(key, devId || undefined);
|
|
96
|
+
res.writeHead(302, { Location: `${frontendUrl}/dashboard` });
|
|
97
|
+
res.end();
|
|
98
|
+
log("Authentication successful. API key saved.");
|
|
99
|
+
setTimeout(() => {
|
|
100
|
+
server.close();
|
|
101
|
+
resolve();
|
|
102
|
+
}, 500);
|
|
103
|
+
return;
|
|
118
104
|
}
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
105
|
+
}
|
|
106
|
+
res.writeHead(400);
|
|
107
|
+
res.end("Missing Spect8 setup key");
|
|
108
|
+
});
|
|
109
|
+
server.listen(port, () => {
|
|
110
|
+
log("Logging you in via browser...");
|
|
111
|
+
log(`If it does not open automatically, visit: ${authUrl}`);
|
|
112
|
+
openBrowser(authUrl);
|
|
113
|
+
});
|
|
114
|
+
setTimeout(() => {
|
|
115
|
+
server.close();
|
|
116
|
+
reject(new Error("Setup timed out. Please try again."));
|
|
117
|
+
}, 5 * 60 * 1000).unref?.();
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
async function runSetup(parsed) {
|
|
121
|
+
const token = parsed.positionals[0];
|
|
122
|
+
const ingestUrl = parsed.positionals[1] ?? optionString(parsed.options, "ingest_url");
|
|
123
|
+
const frontendUrl = optionString(parsed.options, "frontend_url") ??
|
|
124
|
+
process.env.SPECT8_FRONTEND_URL ??
|
|
125
|
+
DEFAULT_FRONTEND_URL;
|
|
126
|
+
if (token) {
|
|
127
|
+
setupGlobalMcpConfig(token, optionString(parsed.options, "developer_id"), ingestUrl);
|
|
136
128
|
}
|
|
137
129
|
else {
|
|
138
|
-
|
|
130
|
+
await interactiveSetup(frontendUrl);
|
|
131
|
+
}
|
|
132
|
+
const ide = optionString(parsed.options, "ide");
|
|
133
|
+
if (ide) {
|
|
134
|
+
printInstallResult(normalizeTarget(ide));
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
function runPrompt(parsed) {
|
|
138
|
+
process.stdout.write(generateInstallPrompt({
|
|
139
|
+
ide: optionString(parsed.options, "ide") ?? parsed.positionals[0],
|
|
140
|
+
token: optionString(parsed.options, "token"),
|
|
141
|
+
ingestUrl: optionString(parsed.options, "ingest_url"),
|
|
142
|
+
}) + "\n");
|
|
143
|
+
}
|
|
144
|
+
function printDoctor(parsed) {
|
|
145
|
+
const result = runDoctor(undefined, {
|
|
146
|
+
target: normalizeTarget(optionString(parsed.options, "ide") ?? parsed.positionals[0]),
|
|
147
|
+
});
|
|
148
|
+
for (const check of result.checks) {
|
|
149
|
+
const status = check.ok ? "ok" : "fail";
|
|
150
|
+
log(`${status} ${check.name}: ${check.detail}`);
|
|
151
|
+
}
|
|
152
|
+
if (!result.ok) {
|
|
153
|
+
process.exitCode = 1;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
function printUsage() {
|
|
157
|
+
console.log(`
|
|
139
158
|
Spect8 CLI
|
|
140
|
-
Usage:
|
|
141
|
-
spect8 setup
|
|
142
|
-
spect8
|
|
159
|
+
Usage:
|
|
160
|
+
spect8 setup [mcp_key] [ingest_url] [--ide cursor|claude-code|rules|all]
|
|
161
|
+
spect8 install cursor|claude-code|rules|all
|
|
162
|
+
spect8 doctor [cursor|claude-code|rules|all] [--ide cursor|claude-code|rules|all]
|
|
163
|
+
spect8 prompt [--ide cursor] [--token <setup-token>] [--ingest-url <url>]
|
|
143
164
|
`);
|
|
165
|
+
}
|
|
166
|
+
async function main() {
|
|
167
|
+
const args = process.argv.slice(2);
|
|
168
|
+
const command = (args[0] ?? "").toLowerCase();
|
|
169
|
+
const parsed = parseArgs(args.slice(1));
|
|
170
|
+
if (command === "setup") {
|
|
171
|
+
await runSetup(parsed);
|
|
172
|
+
return;
|
|
173
|
+
}
|
|
174
|
+
if (command === "install" || command === "init") {
|
|
175
|
+
printInstallResult(normalizeTarget(parsed.positionals[0]));
|
|
176
|
+
return;
|
|
177
|
+
}
|
|
178
|
+
if (command === "doctor") {
|
|
179
|
+
printDoctor(parsed);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
if (command === "prompt") {
|
|
183
|
+
runPrompt(parsed);
|
|
184
|
+
return;
|
|
144
185
|
}
|
|
186
|
+
printUsage();
|
|
145
187
|
}
|
|
146
|
-
main()
|
|
188
|
+
main().catch((err) => {
|
|
189
|
+
process.stderr.write(`[Spect8] ${err.message}\n`);
|
|
190
|
+
process.exit(1);
|
|
191
|
+
});
|
|
147
192
|
//# sourceMappingURL=cli.js.map
|
package/dist/cli.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":";AAEA,OAAO,EAAE,
|
|
1
|
+
{"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"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"}
|