@insitue/claude-plugin 0.4.6 → 0.5.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/chunk-5APYM634.js +31 -0
- package/dist/chunk-IRPBZWNQ.js +66 -0
- package/dist/{chunk-U4C5CDNQ.js → chunk-SGLSPTHD.js} +62 -87
- package/dist/chunk-UNMH2DN4.js +72 -0
- package/dist/cloud/api.js +14 -0
- package/dist/cloud/config.js +10 -0
- package/dist/diagnose.js +6 -0
- package/dist/mcp-server.js +209 -2
- package/dist/setup-cli.js +4 -2
- package/package.json +4 -3
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
// src/cloud/config.ts
|
|
2
|
+
import { homedir } from "os";
|
|
3
|
+
import { join } from "path";
|
|
4
|
+
import { existsSync, readFileSync } from "fs";
|
|
5
|
+
function loadAuth() {
|
|
6
|
+
const p = join(homedir(), ".insitue", "auth.json");
|
|
7
|
+
if (!existsSync(p)) return {};
|
|
8
|
+
try {
|
|
9
|
+
return JSON.parse(readFileSync(p, "utf8"));
|
|
10
|
+
} catch {
|
|
11
|
+
return {};
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
function loadProjectId(projectDir) {
|
|
15
|
+
const p = join(projectDir, ".insitue", "project.json");
|
|
16
|
+
if (!existsSync(p)) return null;
|
|
17
|
+
try {
|
|
18
|
+
return JSON.parse(readFileSync(p, "utf8")).projectId ?? null;
|
|
19
|
+
} catch {
|
|
20
|
+
return null;
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
function resolveHost(cfg) {
|
|
24
|
+
return process.env["INSITUE_API_HOST"] ?? cfg.host ?? "https://app.insitue.com";
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export {
|
|
28
|
+
loadAuth,
|
|
29
|
+
loadProjectId,
|
|
30
|
+
resolveHost
|
|
31
|
+
};
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
// src/cloud/api.ts
|
|
2
|
+
var CloudApiError = class extends Error {
|
|
3
|
+
constructor(status, code, message) {
|
|
4
|
+
super(message);
|
|
5
|
+
this.status = status;
|
|
6
|
+
this.code = code;
|
|
7
|
+
this.name = "CloudApiError";
|
|
8
|
+
}
|
|
9
|
+
status;
|
|
10
|
+
code;
|
|
11
|
+
};
|
|
12
|
+
async function call(host, token, method, path, body) {
|
|
13
|
+
const res = await fetch(`${host}${path}`, {
|
|
14
|
+
method,
|
|
15
|
+
headers: {
|
|
16
|
+
authorization: `Bearer ${token}`,
|
|
17
|
+
...body ? { "content-type": "application/json" } : {}
|
|
18
|
+
},
|
|
19
|
+
...body ? { body: JSON.stringify(body) } : {}
|
|
20
|
+
});
|
|
21
|
+
const text = await res.text();
|
|
22
|
+
let json = {};
|
|
23
|
+
try {
|
|
24
|
+
json = text ? JSON.parse(text) : {};
|
|
25
|
+
} catch {
|
|
26
|
+
}
|
|
27
|
+
const errMsg = json?.error;
|
|
28
|
+
if (!res.ok) {
|
|
29
|
+
const code = res.status === 401 ? "unauthorized" : res.status === 402 ? "not_paid" : res.status === 404 ? "not_found" : res.status === 409 ? "conflict" : "error";
|
|
30
|
+
throw new CloudApiError(res.status, code, errMsg ?? `HTTP ${res.status}`);
|
|
31
|
+
}
|
|
32
|
+
return json;
|
|
33
|
+
}
|
|
34
|
+
var listIssues = (host, token, projectId) => call(
|
|
35
|
+
host,
|
|
36
|
+
token,
|
|
37
|
+
"GET",
|
|
38
|
+
`/api/v1/dev/issues?projectId=${encodeURIComponent(projectId)}`
|
|
39
|
+
);
|
|
40
|
+
var claimIssue = (host, token, id) => call(
|
|
41
|
+
host,
|
|
42
|
+
token,
|
|
43
|
+
"POST",
|
|
44
|
+
`/api/v1/dev/issues/${encodeURIComponent(id)}/claim`
|
|
45
|
+
);
|
|
46
|
+
var resolveIssue = (host, token, id, prUrl, branch) => call(
|
|
47
|
+
host,
|
|
48
|
+
token,
|
|
49
|
+
"POST",
|
|
50
|
+
`/api/v1/dev/issues/${encodeURIComponent(id)}/resolve`,
|
|
51
|
+
{ prUrl, branch }
|
|
52
|
+
);
|
|
53
|
+
var releaseIssue = (host, token, id) => call(
|
|
54
|
+
host,
|
|
55
|
+
token,
|
|
56
|
+
"POST",
|
|
57
|
+
`/api/v1/dev/issues/${encodeURIComponent(id)}/release`
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
export {
|
|
61
|
+
CloudApiError,
|
|
62
|
+
listIssues,
|
|
63
|
+
claimIssue,
|
|
64
|
+
resolveIssue,
|
|
65
|
+
releaseIssue
|
|
66
|
+
};
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
// src/diagnose.ts
|
|
2
|
-
import { existsSync, readFileSync } from "fs";
|
|
2
|
+
import { existsSync, readdirSync, readFileSync, statSync } from "fs";
|
|
3
3
|
import { request as httpRequest } from "http";
|
|
4
4
|
import { join } from "path";
|
|
5
|
-
function
|
|
6
|
-
const pkgJson = join(
|
|
5
|
+
function readPkgVersionAt(dir, pkgName) {
|
|
6
|
+
const pkgJson = join(dir, "node_modules", pkgName, "package.json");
|
|
7
7
|
if (!existsSync(pkgJson)) return null;
|
|
8
8
|
try {
|
|
9
9
|
return JSON.parse(readFileSync(pkgJson, "utf8")).version ?? null;
|
|
@@ -11,6 +11,38 @@ function readPkgVersion(projectDir, pkgName) {
|
|
|
11
11
|
return null;
|
|
12
12
|
}
|
|
13
13
|
}
|
|
14
|
+
function listWorkspaceMembers(projectDir) {
|
|
15
|
+
const out = [];
|
|
16
|
+
for (const parent of ["apps", "packages"]) {
|
|
17
|
+
const parentDir = join(projectDir, parent);
|
|
18
|
+
if (!existsSync(parentDir)) continue;
|
|
19
|
+
let entries;
|
|
20
|
+
try {
|
|
21
|
+
entries = readdirSync(parentDir);
|
|
22
|
+
} catch {
|
|
23
|
+
continue;
|
|
24
|
+
}
|
|
25
|
+
for (const name of entries) {
|
|
26
|
+
const child = join(parentDir, name);
|
|
27
|
+
try {
|
|
28
|
+
if (statSync(child).isDirectory() && existsSync(join(child, "package.json"))) {
|
|
29
|
+
out.push(child);
|
|
30
|
+
}
|
|
31
|
+
} catch {
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
function readPkgVersion(projectDir, pkgName) {
|
|
38
|
+
const v = readPkgVersionAt(projectDir, pkgName);
|
|
39
|
+
if (v) return v;
|
|
40
|
+
for (const ws of listWorkspaceMembers(projectDir)) {
|
|
41
|
+
const v2 = readPkgVersionAt(ws, pkgName);
|
|
42
|
+
if (v2) return v2;
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
14
46
|
function readSession(projectDir) {
|
|
15
47
|
const file = join(projectDir, ".insitue", "session.json");
|
|
16
48
|
if (!existsSync(file)) return null;
|
|
@@ -21,7 +53,7 @@ function readSession(projectDir) {
|
|
|
21
53
|
}
|
|
22
54
|
}
|
|
23
55
|
async function pokeCompanion(port) {
|
|
24
|
-
return new Promise((
|
|
56
|
+
return new Promise((resolve) => {
|
|
25
57
|
const req = httpRequest(
|
|
26
58
|
{
|
|
27
59
|
host: "127.0.0.1",
|
|
@@ -32,26 +64,27 @@ async function pokeCompanion(port) {
|
|
|
32
64
|
},
|
|
33
65
|
(res) => {
|
|
34
66
|
res.resume();
|
|
35
|
-
|
|
67
|
+
resolve({ alive: true, subscribers: null });
|
|
36
68
|
}
|
|
37
69
|
);
|
|
38
|
-
req.on("error", () =>
|
|
70
|
+
req.on("error", () => resolve({ alive: false, subscribers: null }));
|
|
39
71
|
req.on("timeout", () => {
|
|
40
72
|
req.destroy();
|
|
41
|
-
|
|
73
|
+
resolve({ alive: false, subscribers: null });
|
|
42
74
|
});
|
|
43
75
|
req.end();
|
|
44
76
|
});
|
|
45
77
|
}
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
78
|
+
var FRAMEWORK_CONFIGS = [
|
|
79
|
+
"next.config.mjs",
|
|
80
|
+
"next.config.js",
|
|
81
|
+
"next.config.ts",
|
|
82
|
+
"vite.config.ts",
|
|
83
|
+
"vite.config.js"
|
|
84
|
+
];
|
|
85
|
+
function detectSwcPluginConfiguredAt(dir) {
|
|
86
|
+
for (const f of FRAMEWORK_CONFIGS) {
|
|
87
|
+
const p = join(dir, f);
|
|
55
88
|
if (existsSync(p)) {
|
|
56
89
|
try {
|
|
57
90
|
const c = readFileSync(p, "utf8");
|
|
@@ -63,6 +96,18 @@ function detectSwcPluginConfigured(projectDir) {
|
|
|
63
96
|
}
|
|
64
97
|
return null;
|
|
65
98
|
}
|
|
99
|
+
function detectSwcPluginConfigured(projectDir) {
|
|
100
|
+
let foundAnyConfig = false;
|
|
101
|
+
const direct = detectSwcPluginConfiguredAt(projectDir);
|
|
102
|
+
if (direct === true) return true;
|
|
103
|
+
if (direct === false) foundAnyConfig = true;
|
|
104
|
+
for (const ws of listWorkspaceMembers(projectDir)) {
|
|
105
|
+
const r = detectSwcPluginConfiguredAt(ws);
|
|
106
|
+
if (r === true) return true;
|
|
107
|
+
if (r === false) foundAnyConfig = true;
|
|
108
|
+
}
|
|
109
|
+
return foundAnyConfig ? false : null;
|
|
110
|
+
}
|
|
66
111
|
async function diagnose(projectDir) {
|
|
67
112
|
const session = readSession(projectDir.dir);
|
|
68
113
|
const hasSessionFile = session !== null;
|
|
@@ -119,76 +164,6 @@ async function diagnose(projectDir) {
|
|
|
119
164
|
};
|
|
120
165
|
}
|
|
121
166
|
|
|
122
|
-
// src/project-dir.ts
|
|
123
|
-
import { existsSync as existsSync2, realpathSync } from "fs";
|
|
124
|
-
import { dirname, isAbsolute, join as join2, resolve, sep } from "path";
|
|
125
|
-
function readProjectDirArg(argv) {
|
|
126
|
-
for (let i = 0; i < argv.length; i++) {
|
|
127
|
-
const a = argv[i];
|
|
128
|
-
if (a === "--project-dir" && i + 1 < argv.length) return argv[i + 1];
|
|
129
|
-
if (a.startsWith("--project-dir=")) return a.slice("--project-dir=".length);
|
|
130
|
-
}
|
|
131
|
-
return null;
|
|
132
|
-
}
|
|
133
|
-
function walkUpFor(start, marker) {
|
|
134
|
-
let dir = resolve(start);
|
|
135
|
-
while (true) {
|
|
136
|
-
if (existsSync2(join2(dir, marker))) return dir;
|
|
137
|
-
const parent = dirname(dir);
|
|
138
|
-
if (parent === dir) return null;
|
|
139
|
-
dir = parent;
|
|
140
|
-
}
|
|
141
|
-
}
|
|
142
|
-
function realpathSafe(p) {
|
|
143
|
-
try {
|
|
144
|
-
return realpathSync(p);
|
|
145
|
-
} catch {
|
|
146
|
-
return resolve(p);
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
function resolveProjectDir(argv = process.argv.slice(2), env = process.env) {
|
|
150
|
-
const fromArg = readProjectDirArg(argv);
|
|
151
|
-
if (fromArg) {
|
|
152
|
-
return { dir: realpathSafe(fromArg), source: "argv" };
|
|
153
|
-
}
|
|
154
|
-
if (env.INSITUE_PROJECT_DIR) {
|
|
155
|
-
return {
|
|
156
|
-
dir: realpathSafe(env.INSITUE_PROJECT_DIR),
|
|
157
|
-
source: "INSITUE_PROJECT_DIR"
|
|
158
|
-
};
|
|
159
|
-
}
|
|
160
|
-
if (env.CLAUDE_PROJECT_DIR) {
|
|
161
|
-
return {
|
|
162
|
-
dir: realpathSafe(env.CLAUDE_PROJECT_DIR),
|
|
163
|
-
source: "CLAUDE_PROJECT_DIR"
|
|
164
|
-
};
|
|
165
|
-
}
|
|
166
|
-
const cwd = process.cwd();
|
|
167
|
-
const sessionDir = walkUpFor(cwd, ".insitue");
|
|
168
|
-
if (sessionDir && existsSync2(join2(sessionDir, ".insitue", "session.json"))) {
|
|
169
|
-
return { dir: realpathSafe(sessionDir), source: "session-walk-up" };
|
|
170
|
-
}
|
|
171
|
-
const pkgDir = walkUpFor(cwd, "package.json");
|
|
172
|
-
if (pkgDir) {
|
|
173
|
-
return { dir: realpathSafe(pkgDir), source: "package-walk-up" };
|
|
174
|
-
}
|
|
175
|
-
return { dir: realpathSafe(cwd), source: "cwd" };
|
|
176
|
-
}
|
|
177
|
-
function isInsideProject(root, target) {
|
|
178
|
-
const r = realpathSafe(root);
|
|
179
|
-
let t;
|
|
180
|
-
try {
|
|
181
|
-
t = realpathSync(target);
|
|
182
|
-
} catch {
|
|
183
|
-
t = resolve(target);
|
|
184
|
-
}
|
|
185
|
-
if (!isAbsolute(r) || !isAbsolute(t)) return false;
|
|
186
|
-
const rWithSep = r.endsWith(sep) ? r : r + sep;
|
|
187
|
-
return t === r || t.startsWith(rWithSep);
|
|
188
|
-
}
|
|
189
|
-
|
|
190
167
|
export {
|
|
191
|
-
diagnose
|
|
192
|
-
resolveProjectDir,
|
|
193
|
-
isInsideProject
|
|
168
|
+
diagnose
|
|
194
169
|
};
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
// src/project-dir.ts
|
|
2
|
+
import { existsSync, realpathSync } from "fs";
|
|
3
|
+
import { dirname, isAbsolute, join, resolve, sep } from "path";
|
|
4
|
+
function readProjectDirArg(argv) {
|
|
5
|
+
for (let i = 0; i < argv.length; i++) {
|
|
6
|
+
const a = argv[i];
|
|
7
|
+
if (a === "--project-dir" && i + 1 < argv.length) return argv[i + 1];
|
|
8
|
+
if (a.startsWith("--project-dir=")) return a.slice("--project-dir=".length);
|
|
9
|
+
}
|
|
10
|
+
return null;
|
|
11
|
+
}
|
|
12
|
+
function walkUpFor(start, marker) {
|
|
13
|
+
let dir = resolve(start);
|
|
14
|
+
while (true) {
|
|
15
|
+
if (existsSync(join(dir, marker))) return dir;
|
|
16
|
+
const parent = dirname(dir);
|
|
17
|
+
if (parent === dir) return null;
|
|
18
|
+
dir = parent;
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
function realpathSafe(p) {
|
|
22
|
+
try {
|
|
23
|
+
return realpathSync(p);
|
|
24
|
+
} catch {
|
|
25
|
+
return resolve(p);
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
function resolveProjectDir(argv = process.argv.slice(2), env = process.env) {
|
|
29
|
+
const fromArg = readProjectDirArg(argv);
|
|
30
|
+
if (fromArg) {
|
|
31
|
+
return { dir: realpathSafe(fromArg), source: "argv" };
|
|
32
|
+
}
|
|
33
|
+
if (env.INSITUE_PROJECT_DIR) {
|
|
34
|
+
return {
|
|
35
|
+
dir: realpathSafe(env.INSITUE_PROJECT_DIR),
|
|
36
|
+
source: "INSITUE_PROJECT_DIR"
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
if (env.CLAUDE_PROJECT_DIR) {
|
|
40
|
+
return {
|
|
41
|
+
dir: realpathSafe(env.CLAUDE_PROJECT_DIR),
|
|
42
|
+
source: "CLAUDE_PROJECT_DIR"
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
const cwd = process.cwd();
|
|
46
|
+
const sessionDir = walkUpFor(cwd, ".insitue");
|
|
47
|
+
if (sessionDir && existsSync(join(sessionDir, ".insitue", "session.json"))) {
|
|
48
|
+
return { dir: realpathSafe(sessionDir), source: "session-walk-up" };
|
|
49
|
+
}
|
|
50
|
+
const pkgDir = walkUpFor(cwd, "package.json");
|
|
51
|
+
if (pkgDir) {
|
|
52
|
+
return { dir: realpathSafe(pkgDir), source: "package-walk-up" };
|
|
53
|
+
}
|
|
54
|
+
return { dir: realpathSafe(cwd), source: "cwd" };
|
|
55
|
+
}
|
|
56
|
+
function isInsideProject(root, target) {
|
|
57
|
+
const r = realpathSafe(root);
|
|
58
|
+
let t;
|
|
59
|
+
try {
|
|
60
|
+
t = realpathSync(target);
|
|
61
|
+
} catch {
|
|
62
|
+
t = resolve(target);
|
|
63
|
+
}
|
|
64
|
+
if (!isAbsolute(r) || !isAbsolute(t)) return false;
|
|
65
|
+
const rWithSep = r.endsWith(sep) ? r : r + sep;
|
|
66
|
+
return t === r || t.startsWith(rWithSep);
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export {
|
|
70
|
+
resolveProjectDir,
|
|
71
|
+
isInsideProject
|
|
72
|
+
};
|
package/dist/diagnose.js
ADDED
package/dist/mcp-server.js
CHANGED
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import {
|
|
3
|
-
diagnose,
|
|
4
3
|
isInsideProject,
|
|
5
4
|
resolveProjectDir
|
|
6
|
-
} from "./chunk-
|
|
5
|
+
} from "./chunk-UNMH2DN4.js";
|
|
6
|
+
import {
|
|
7
|
+
diagnose
|
|
8
|
+
} from "./chunk-SGLSPTHD.js";
|
|
9
|
+
import {
|
|
10
|
+
CloudApiError,
|
|
11
|
+
claimIssue,
|
|
12
|
+
listIssues,
|
|
13
|
+
releaseIssue,
|
|
14
|
+
resolveIssue
|
|
15
|
+
} from "./chunk-IRPBZWNQ.js";
|
|
16
|
+
import {
|
|
17
|
+
loadAuth,
|
|
18
|
+
loadProjectId,
|
|
19
|
+
resolveHost
|
|
20
|
+
} from "./chunk-5APYM634.js";
|
|
7
21
|
|
|
8
22
|
// src/mcp-server.ts
|
|
9
23
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
@@ -699,6 +713,199 @@ server.registerTool(
|
|
|
699
713
|
};
|
|
700
714
|
}
|
|
701
715
|
);
|
|
716
|
+
function cloudSetup() {
|
|
717
|
+
const auth = loadAuth();
|
|
718
|
+
if (!auth.token) {
|
|
719
|
+
return {
|
|
720
|
+
error: "not_logged_in",
|
|
721
|
+
message: "Run `insitue login` (create a token at https://app.insitue.com/app/settings/developer) so InSitue can read this account's issues."
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
const projectId = loadProjectId(projectDir.dir);
|
|
725
|
+
if (!projectId) {
|
|
726
|
+
return {
|
|
727
|
+
error: "not_linked",
|
|
728
|
+
message: "Link this repo to a cloud project: `insitue link <projectId>` (find the id in your InSitue dashboard project settings)."
|
|
729
|
+
};
|
|
730
|
+
}
|
|
731
|
+
return { token: auth.token, host: resolveHost(auth), projectId };
|
|
732
|
+
}
|
|
733
|
+
function cloudErrPayload(e) {
|
|
734
|
+
if (e instanceof CloudApiError) {
|
|
735
|
+
return {
|
|
736
|
+
error: e.code,
|
|
737
|
+
message: e.code === "not_paid" ? `${e.message} \u2014 upgrade to a paid InSitue plan to use cloud issues.` : e.message
|
|
738
|
+
};
|
|
739
|
+
}
|
|
740
|
+
return null;
|
|
741
|
+
}
|
|
742
|
+
server.registerTool(
|
|
743
|
+
"list_cloud_issues",
|
|
744
|
+
{
|
|
745
|
+
description: "List open InSitue cloud issues (captured bug reports) for the linked project, so you can fix them locally. Returns id, description (note), status, and source file:line where known. Then call `claim_cloud_issue` to start one.",
|
|
746
|
+
inputSchema: {}
|
|
747
|
+
},
|
|
748
|
+
async () => {
|
|
749
|
+
const setup = cloudSetup();
|
|
750
|
+
if ("error" in setup) {
|
|
751
|
+
return {
|
|
752
|
+
content: [{ type: "text", text: JSON.stringify(setup) }]
|
|
753
|
+
};
|
|
754
|
+
}
|
|
755
|
+
const { token, host, projectId } = setup;
|
|
756
|
+
try {
|
|
757
|
+
const result = await listIssues(host, token, projectId);
|
|
758
|
+
return {
|
|
759
|
+
content: [
|
|
760
|
+
{
|
|
761
|
+
type: "text",
|
|
762
|
+
text: JSON.stringify({ status: "ok", projectId, issues: result.issues })
|
|
763
|
+
}
|
|
764
|
+
]
|
|
765
|
+
};
|
|
766
|
+
} catch (e) {
|
|
767
|
+
const payload = cloudErrPayload(e) ?? {
|
|
768
|
+
error: "error",
|
|
769
|
+
message: String(e)
|
|
770
|
+
};
|
|
771
|
+
return {
|
|
772
|
+
content: [{ type: "text", text: JSON.stringify(payload) }]
|
|
773
|
+
};
|
|
774
|
+
}
|
|
775
|
+
}
|
|
776
|
+
);
|
|
777
|
+
server.registerTool(
|
|
778
|
+
"claim_cloud_issue",
|
|
779
|
+
{
|
|
780
|
+
description: "Claim a cloud issue into local work (marks it 'fixing locally' in the dashboard) and return its repro: description, source file:line, page URL, console errors. Read the file, fix it, open a PR, then call resolve_cloud_issue.",
|
|
781
|
+
inputSchema: {
|
|
782
|
+
id: z.string().describe("The issue id from list_cloud_issues.")
|
|
783
|
+
}
|
|
784
|
+
},
|
|
785
|
+
async ({ id }) => {
|
|
786
|
+
const setup = cloudSetup();
|
|
787
|
+
if ("error" in setup) {
|
|
788
|
+
return {
|
|
789
|
+
content: [{ type: "text", text: JSON.stringify(setup) }]
|
|
790
|
+
};
|
|
791
|
+
}
|
|
792
|
+
const { token, host } = setup;
|
|
793
|
+
try {
|
|
794
|
+
const result = await claimIssue(host, token, id);
|
|
795
|
+
const bundle = result.bundle;
|
|
796
|
+
const targetObj = bundle?.["target"];
|
|
797
|
+
const sourceObj = targetObj?.["source"];
|
|
798
|
+
const runtimeObj = bundle?.["runtime"];
|
|
799
|
+
const errorsArr = (runtimeObj?.["errors"] ?? runtimeObj?.["console"])?.slice(0, 3);
|
|
800
|
+
const consoleErrors = Array.isArray(errorsArr) ? errorsArr.map((e) => typeof e === "string" ? e : JSON.stringify(e)).filter(Boolean) : [];
|
|
801
|
+
const source = typeof sourceObj?.["file"] === "string" ? {
|
|
802
|
+
file: sourceObj["file"],
|
|
803
|
+
...typeof sourceObj["line"] === "number" ? { line: sourceObj["line"] } : {}
|
|
804
|
+
} : null;
|
|
805
|
+
const url = typeof runtimeObj?.["url"] === "string" ? runtimeObj["url"] : null;
|
|
806
|
+
return {
|
|
807
|
+
content: [
|
|
808
|
+
{
|
|
809
|
+
type: "text",
|
|
810
|
+
text: JSON.stringify({
|
|
811
|
+
status: "ok",
|
|
812
|
+
id,
|
|
813
|
+
note: result.note,
|
|
814
|
+
source,
|
|
815
|
+
url,
|
|
816
|
+
consoleErrors,
|
|
817
|
+
instructions: "Read the source file around the line, reproduce the issue from the description, make the fix, open a PR, then call resolve_cloud_issue with the PR url."
|
|
818
|
+
})
|
|
819
|
+
}
|
|
820
|
+
]
|
|
821
|
+
};
|
|
822
|
+
} catch (e) {
|
|
823
|
+
const payload = cloudErrPayload(e) ?? {
|
|
824
|
+
error: "error",
|
|
825
|
+
message: String(e)
|
|
826
|
+
};
|
|
827
|
+
return {
|
|
828
|
+
content: [{ type: "text", text: JSON.stringify(payload) }]
|
|
829
|
+
};
|
|
830
|
+
}
|
|
831
|
+
}
|
|
832
|
+
);
|
|
833
|
+
server.registerTool(
|
|
834
|
+
"resolve_cloud_issue",
|
|
835
|
+
{
|
|
836
|
+
description: "Mark a claimed cloud issue resolved and attach the GitHub PR URL of your local fix. The dashboard shows it as resolved_local with the PR linked. Call after you've opened the PR.",
|
|
837
|
+
inputSchema: {
|
|
838
|
+
id: z.string().describe("The issue id."),
|
|
839
|
+
prUrl: z.string().describe("The GitHub PR URL for the fix."),
|
|
840
|
+
branch: z.string().optional().describe("The branch name (optional, inferred from the PR if omitted).")
|
|
841
|
+
}
|
|
842
|
+
},
|
|
843
|
+
async ({ id, prUrl, branch }) => {
|
|
844
|
+
const setup = cloudSetup();
|
|
845
|
+
if ("error" in setup) {
|
|
846
|
+
return {
|
|
847
|
+
content: [{ type: "text", text: JSON.stringify(setup) }]
|
|
848
|
+
};
|
|
849
|
+
}
|
|
850
|
+
const { token, host } = setup;
|
|
851
|
+
try {
|
|
852
|
+
const result = await resolveIssue(host, token, id, prUrl, branch);
|
|
853
|
+
return {
|
|
854
|
+
content: [
|
|
855
|
+
{
|
|
856
|
+
type: "text",
|
|
857
|
+
text: JSON.stringify({ status: "ok", runId: result.runId })
|
|
858
|
+
}
|
|
859
|
+
]
|
|
860
|
+
};
|
|
861
|
+
} catch (e) {
|
|
862
|
+
const payload = cloudErrPayload(e) ?? {
|
|
863
|
+
error: "error",
|
|
864
|
+
message: String(e)
|
|
865
|
+
};
|
|
866
|
+
return {
|
|
867
|
+
content: [{ type: "text", text: JSON.stringify(payload) }]
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
}
|
|
871
|
+
);
|
|
872
|
+
server.registerTool(
|
|
873
|
+
"release_cloud_issue",
|
|
874
|
+
{
|
|
875
|
+
description: "Release a previously-claimed cloud issue back to the open queue (you decided not to fix it locally).",
|
|
876
|
+
inputSchema: {
|
|
877
|
+
id: z.string().describe("The issue id to release.")
|
|
878
|
+
}
|
|
879
|
+
},
|
|
880
|
+
async ({ id }) => {
|
|
881
|
+
const setup = cloudSetup();
|
|
882
|
+
if ("error" in setup) {
|
|
883
|
+
return {
|
|
884
|
+
content: [{ type: "text", text: JSON.stringify(setup) }]
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
const { token, host } = setup;
|
|
888
|
+
try {
|
|
889
|
+
await releaseIssue(host, token, id);
|
|
890
|
+
return {
|
|
891
|
+
content: [
|
|
892
|
+
{
|
|
893
|
+
type: "text",
|
|
894
|
+
text: JSON.stringify({ status: "ok", released: true })
|
|
895
|
+
}
|
|
896
|
+
]
|
|
897
|
+
};
|
|
898
|
+
} catch (e) {
|
|
899
|
+
const payload = cloudErrPayload(e) ?? {
|
|
900
|
+
error: "error",
|
|
901
|
+
message: String(e)
|
|
902
|
+
};
|
|
903
|
+
return {
|
|
904
|
+
content: [{ type: "text", text: JSON.stringify(payload) }]
|
|
905
|
+
};
|
|
906
|
+
}
|
|
907
|
+
}
|
|
908
|
+
);
|
|
702
909
|
server.registerPrompt(
|
|
703
910
|
"connect",
|
|
704
911
|
{
|
package/dist/setup-cli.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@insitue/claude-plugin",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Drive Claude (Code AND Desktop) from the InSitue browser overlay — pick an element in your app, claude reads the file and proposes the edit.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"insitue",
|
|
@@ -52,8 +52,9 @@
|
|
|
52
52
|
"node": ">=24"
|
|
53
53
|
},
|
|
54
54
|
"scripts": {
|
|
55
|
-
"build": "tsup src/dispatcher.ts src/mcp-server.ts src/setup-cli.ts --format esm --clean --external @modelcontextprotocol/sdk --external ws --external zod",
|
|
56
|
-
"dev": "tsup src/dispatcher.ts src/mcp-server.ts src/setup-cli.ts --format esm --watch --external @modelcontextprotocol/sdk --external ws --external zod",
|
|
55
|
+
"build": "tsup src/dispatcher.ts src/mcp-server.ts src/setup-cli.ts src/diagnose.ts src/cloud/api.ts src/cloud/config.ts --format esm --clean --external @modelcontextprotocol/sdk --external ws --external zod",
|
|
56
|
+
"dev": "tsup src/dispatcher.ts src/mcp-server.ts src/setup-cli.ts src/diagnose.ts src/cloud/api.ts src/cloud/config.ts --format esm --watch --external @modelcontextprotocol/sdk --external ws --external zod",
|
|
57
|
+
"test": "node --test \"test/*.test.mjs\"",
|
|
57
58
|
"typecheck": "tsc --noEmit",
|
|
58
59
|
"lint": "tsc --noEmit"
|
|
59
60
|
}
|