carrick 0.3.57 → 0.3.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +117 -26
- package/bin/carrick.mjs +13 -2
- package/dist/auth/credentials.d.ts +14 -0
- package/dist/auth/credentials.js +86 -0
- package/dist/auth/credentials.js.map +1 -0
- package/dist/auth/oauth.d.ts +11 -0
- package/dist/auth/oauth.js +119 -0
- package/dist/auth/oauth.js.map +1 -0
- package/dist/auth/read.d.ts +139 -0
- package/dist/auth/read.js +44 -0
- package/dist/auth/read.js.map +1 -0
- package/dist/auth/run.d.ts +12 -0
- package/dist/auth/run.js +68 -0
- package/dist/auth/run.js.map +1 -0
- package/dist/contract.d.ts +10 -0
- package/dist/contract.js +3 -0
- package/dist/contract.js.map +1 -1
- package/dist/definition.d.ts +33 -0
- package/dist/definition.js +107 -0
- package/dist/definition.js.map +1 -0
- package/dist/diagnostics.d.ts +72 -3
- package/dist/diagnostics.js +275 -55
- package/dist/diagnostics.js.map +1 -1
- package/dist/hook/refresh.d.ts +32 -0
- package/dist/hook/refresh.js +138 -0
- package/dist/hook/refresh.js.map +1 -0
- package/dist/hook/session-start.js +13 -0
- package/dist/hook/session-start.js.map +1 -1
- package/dist/init/connect.d.ts +17 -0
- package/dist/init/connect.js +120 -0
- package/dist/init/connect.js.map +1 -0
- package/dist/init/mcp.d.ts +50 -0
- package/dist/init/mcp.js +235 -0
- package/dist/init/mcp.js.map +1 -0
- package/dist/init/projects.d.ts +46 -0
- package/dist/init/projects.js +137 -0
- package/dist/init/projects.js.map +1 -0
- package/dist/init/repos.d.ts +113 -19
- package/dist/init/repos.js +60 -39
- package/dist/init/repos.js.map +1 -1
- package/dist/init/run.d.ts +16 -0
- package/dist/init/run.js +243 -61
- package/dist/init/run.js.map +1 -1
- package/dist/init/settings.d.ts +1 -1
- package/dist/init/settings.js +1 -1
- package/dist/init/settings.js.map +1 -1
- package/dist/lens.d.ts +41 -0
- package/dist/lens.js +116 -0
- package/dist/lens.js.map +1 -0
- package/dist/server.js +327 -24
- package/dist/server.js.map +1 -1
- package/dist/surfaces.d.ts +28 -0
- package/dist/surfaces.js +70 -0
- package/dist/surfaces.js.map +1 -0
- package/package.json +6 -6
- package/sidecar/dist/src/capture/deno-project.d.ts +41 -0
- package/sidecar/dist/src/capture/deno-project.js +513 -0
- package/sidecar/dist/src/capture/index.d.ts +1 -0
- package/sidecar/dist/src/capture/index.js +60 -19
- package/sidecar/dist/src/capture/self-check.d.ts +2 -0
- package/sidecar/dist/src/capture/self-check.js +3 -2
- package/sidecar/dist/src/project-loader.js +22 -1
- package/dist/init/identity.d.ts +0 -20
- package/dist/init/identity.js +0 -60
- package/dist/init/identity.js.map +0 -1
|
@@ -16,6 +16,7 @@ import { resolveChannel } from "../channel.js";
|
|
|
16
16
|
import { createLogger } from "../log.js";
|
|
17
17
|
import { renderSessionStart } from "../render.js";
|
|
18
18
|
import { resolveRoot, rootNote } from "../root.js";
|
|
19
|
+
import { refreshInBackground } from "./refresh.js";
|
|
19
20
|
const log = createLogger("carrick-session");
|
|
20
21
|
async function main() {
|
|
21
22
|
if (resolveChannel({ hooksInstalled: true }).channel === "off") {
|
|
@@ -38,6 +39,18 @@ async function main() {
|
|
|
38
39
|
// Ends with a newline: this is a line a developer also runs by hand and
|
|
39
40
|
// compares against `carrick status`, and that one ends its output properly.
|
|
40
41
|
process.stdout.write(`${renderSessionStart(outcome.result)}\n`);
|
|
42
|
+
// The hosted index the first CI scan writes is picked up here rather than by
|
|
43
|
+
// a command the developer has to remember (carrick#955). Detached and
|
|
44
|
+
// unwaited: a refresh is minutes of work and a hook is not.
|
|
45
|
+
let started = null;
|
|
46
|
+
try {
|
|
47
|
+
started = refreshInBackground(choice.root, outcome.result);
|
|
48
|
+
}
|
|
49
|
+
catch (error) {
|
|
50
|
+
log("background refresh not started", String(error));
|
|
51
|
+
}
|
|
52
|
+
if (started)
|
|
53
|
+
process.stdout.write(`${started}\n`);
|
|
41
54
|
}
|
|
42
55
|
await main();
|
|
43
56
|
process.exit(0);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-start.js","sourceRoot":"","sources":["../../src/hook/session-start.ts"],"names":[],"mappings":";AACA,8EAA8E;AAC9E,EAAE;AACF,gFAAgF;AAChF,8EAA8E;AAC9E,gFAAgF;AAChF,4EAA4E;AAC5E,mDAAmD;AACnD,EAAE;AACF,+EAA+E;AAC/E,6EAA6E;AAC7E,0EAA0E;AAC1E,gEAAgE;AAEhE,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AAEnD,MAAM,GAAG,GAAG,YAAY,CAAC,iBAAiB,CAAC,CAAC;AAE5C,KAAK,UAAU,IAAI;IACjB,IAAI,cAAc,CAAC,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;QAC/D,GAAG,CAAC,uCAAuC,CAAC,CAAC;QAC7C,OAAO;IACT,CAAC;IACD,MAAM,MAAM,GAAG,WAAW,CAAC;QACzB,UAAU,EAAE,OAAO,CAAC,GAAG,EAAE;QACzB,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,IAAI,IAAI;QACrD,QAAQ,EAAE,OAAO,CAAC,GAAG,EAAE;KACxB,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC9B,IAAI,IAAI;QAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IAEpB,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACvG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QACpB,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;QACxC,OAAO;IACT,CAAC;IACD,wEAAwE;IACxE,4EAA4E;IAC5E,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,kBAAkB,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;
|
|
1
|
+
{"version":3,"file":"session-start.js","sourceRoot":"","sources":["../../src/hook/session-start.ts"],"names":[],"mappings":";AACA,8EAA8E;AAC9E,EAAE;AACF,gFAAgF;AAChF,8EAA8E;AAC9E,gFAAgF;AAChF,4EAA4E;AAC5E,mDAAmD;AACnD,EAAE;AACF,+EAA+E;AAC/E,6EAA6E;AAC7E,0EAA0E;AAC1E,gEAAgE;AAEhE,OAAO,EAAE,MAAM,EAAE,MAAM,WAAW,CAAC;AACnC,OAAO,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAE,MAAM,WAAW,CAAC;AACzC,OAAO,EAAE,kBAAkB,EAAE,MAAM,cAAc,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,YAAY,CAAC;AACnD,OAAO,EAAE,mBAAmB,EAAE,MAAM,cAAc,CAAC;AAEnD,MAAM,GAAG,GAAG,YAAY,CAAC,iBAAiB,CAAC,CAAC;AAE5C,KAAK,UAAU,IAAI;IACjB,IAAI,cAAc,CAAC,EAAE,cAAc,EAAE,IAAI,EAAE,CAAC,CAAC,OAAO,KAAK,KAAK,EAAE,CAAC;QAC/D,GAAG,CAAC,uCAAuC,CAAC,CAAC;QAC7C,OAAO;IACT,CAAC;IACD,MAAM,MAAM,GAAG,WAAW,CAAC;QACzB,UAAU,EAAE,OAAO,CAAC,GAAG,EAAE;QACzB,UAAU,EAAE,OAAO,CAAC,GAAG,CAAC,oBAAoB,CAAC,IAAI,IAAI;QACrD,QAAQ,EAAE,OAAO,CAAC,GAAG,EAAE;KACxB,CAAC,CAAC;IACH,MAAM,IAAI,GAAG,QAAQ,CAAC,MAAM,CAAC,CAAC;IAC9B,IAAI,IAAI;QAAE,GAAG,CAAC,IAAI,CAAC,CAAC;IAEpB,MAAM,OAAO,GAAG,MAAM,MAAM,CAAC,EAAE,GAAG,EAAE,MAAM,CAAC,IAAI,EAAE,SAAS,EAAE,MAAM,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,EAAE,CAAC,CAAC;IACvG,IAAI,CAAC,OAAO,CAAC,MAAM,EAAE,CAAC;QACpB,GAAG,CAAC,WAAW,EAAE,OAAO,CAAC,OAAO,IAAI,EAAE,CAAC,CAAC;QACxC,OAAO;IACT,CAAC;IACD,wEAAwE;IACxE,4EAA4E;IAC5E,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,kBAAkB,CAAC,OAAO,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC;IAEhE,6EAA6E;IAC7E,sEAAsE;IACtE,4DAA4D;IAC5D,IAAI,OAAO,GAAkB,IAAI,CAAC;IAClC,IAAI,CAAC;QACH,OAAO,GAAG,mBAAmB,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC;IAC7D,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,GAAG,CAAC,gCAAgC,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACvD,CAAC;IACD,IAAI,OAAO;QAAE,OAAO,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,OAAO,IAAI,CAAC,CAAC;AACpD,CAAC;AAED,MAAM,IAAI,EAAE,CAAC;AACb,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import { type ResolvedRepos } from "../auth/read.ts";
|
|
2
|
+
type ConnectOptions = {
|
|
3
|
+
interactive: boolean;
|
|
4
|
+
say: (message: string) => void;
|
|
5
|
+
project?: string;
|
|
6
|
+
/** True when this run has already seen or created the project (carrick#955). */
|
|
7
|
+
projectExists?: boolean;
|
|
8
|
+
open?: (url: string) => Promise<boolean>;
|
|
9
|
+
poll?: (signal: AbortSignal) => Promise<ResolvedRepos>;
|
|
10
|
+
wait?: (signal: AbortSignal) => Promise<void>;
|
|
11
|
+
signal?: AbortSignal;
|
|
12
|
+
};
|
|
13
|
+
/** Every requested repo must be present, connected, and assigned to this project. */
|
|
14
|
+
export declare function reposAreInProject(identity: ResolvedRepos, repos: string[], project: string): boolean;
|
|
15
|
+
/** The browser owns installation and assignment; Ctrl-C stops verification. */
|
|
16
|
+
export declare function connectRepos(token: string, repos: string[], initial: ResolvedRepos, options: ConnectOptions): Promise<ResolvedRepos>;
|
|
17
|
+
export {};
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
import { setTimeout } from "node:timers/promises";
|
|
2
|
+
import { APP_BASE } from "../auth/credentials.js";
|
|
3
|
+
import { openBrowser } from "../auth/oauth.js";
|
|
4
|
+
import { resolveRepos } from "../auth/read.js";
|
|
5
|
+
/** Every requested repo must be present, connected, and assigned to this project. */
|
|
6
|
+
export function reposAreInProject(identity, repos, project) {
|
|
7
|
+
if (!identity.workspace.installed || repos.length === 0)
|
|
8
|
+
return false;
|
|
9
|
+
return repos.every((name) => {
|
|
10
|
+
const repo = identity.repos.find((candidate) => candidate.full_name.toLowerCase() === name.toLowerCase());
|
|
11
|
+
return repo?.connected === true && repo.project_slug === project;
|
|
12
|
+
});
|
|
13
|
+
}
|
|
14
|
+
function assignment(identity, name) {
|
|
15
|
+
const repo = identity.repos.find((candidate) => candidate.full_name.toLowerCase() === name.toLowerCase());
|
|
16
|
+
return repo?.connected === true ? repo.project_slug : "not connected";
|
|
17
|
+
}
|
|
18
|
+
function reportAssignments(identity, repos, previous, say) {
|
|
19
|
+
for (const name of repos) {
|
|
20
|
+
const current = assignment(identity, name);
|
|
21
|
+
if (previous.get(name.toLowerCase()) === current)
|
|
22
|
+
continue;
|
|
23
|
+
const repo = identity.repos.find((candidate) => candidate.full_name.toLowerCase() === name.toLowerCase());
|
|
24
|
+
const actualName = repo?.full_name ?? name;
|
|
25
|
+
say(current === "not connected"
|
|
26
|
+
? `${actualName} is not connected to this workspace.`
|
|
27
|
+
: `${actualName} is currently in project "${current}".`);
|
|
28
|
+
previous.set(name.toLowerCase(), current);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
function verifiedLine(repos, project) {
|
|
32
|
+
return repos.length === 1
|
|
33
|
+
? `Verified 1 repo in project "${project}".`
|
|
34
|
+
: `Verified ${repos.length} repos in project "${project}".`;
|
|
35
|
+
}
|
|
36
|
+
/** The browser owns installation and assignment; Ctrl-C stops verification. */
|
|
37
|
+
export async function connectRepos(token, repos, initial, options) {
|
|
38
|
+
const workspaceUrl = `${APP_BASE}/w/${encodeURIComponent(initial.workspace.slug)}`;
|
|
39
|
+
const connectUrl = `${workspaceUrl}/connect`;
|
|
40
|
+
const projectUrl = `${workspaceUrl}/projects`;
|
|
41
|
+
const reposUrl = `${workspaceUrl}/repos`;
|
|
42
|
+
const assignments = new Map();
|
|
43
|
+
if (options.project) {
|
|
44
|
+
reportAssignments(initial, repos, assignments, options.say);
|
|
45
|
+
if (reposAreInProject(initial, repos, options.project)) {
|
|
46
|
+
options.say(verifiedLine(repos, options.project));
|
|
47
|
+
return initial;
|
|
48
|
+
}
|
|
49
|
+
// The create step is skipped only when this run has just seen the project
|
|
50
|
+
// in the workspace or created it; a browser is still where a repo is
|
|
51
|
+
// assigned to it.
|
|
52
|
+
if (!options.projectExists) {
|
|
53
|
+
options.say(`Create project "${options.project}" if needed: ${projectUrl}`);
|
|
54
|
+
options.say(` Choose Create project, enter a name, and set the slug to "${options.project}".`);
|
|
55
|
+
}
|
|
56
|
+
if (!initial.workspace.installed || initial.repos.some((repo) => !repo.connected)) {
|
|
57
|
+
options.say(`Connect any missing repos: ${connectUrl}`);
|
|
58
|
+
}
|
|
59
|
+
options.say(`Assign the requested repos: ${reposUrl}`);
|
|
60
|
+
options.say(` Select ${repos.join(", ")}, choose the target project in "Move selected to", then choose "Move selected".`);
|
|
61
|
+
}
|
|
62
|
+
else {
|
|
63
|
+
if (initial.workspace.installed && initial.repos.every((repo) => repo.connected))
|
|
64
|
+
return initial;
|
|
65
|
+
options.say(`Connect repositories in your browser: ${connectUrl}`);
|
|
66
|
+
}
|
|
67
|
+
if (!options.interactive) {
|
|
68
|
+
if (options.project) {
|
|
69
|
+
options.say(`Project "${options.project}" is not verified for every requested repo.`);
|
|
70
|
+
}
|
|
71
|
+
return initial;
|
|
72
|
+
}
|
|
73
|
+
options.say(options.project
|
|
74
|
+
? `Waiting for every requested repo to reach project "${options.project}". Press Ctrl-C to stop.`
|
|
75
|
+
: "Waiting for repository connections. Press Ctrl-C to continue setup without waiting.");
|
|
76
|
+
const controller = new AbortController();
|
|
77
|
+
const cancel = () => controller.abort();
|
|
78
|
+
process.once("SIGINT", cancel);
|
|
79
|
+
const signal = AbortSignal.any([controller.signal, AbortSignal.timeout(30 * 60_000), ...(options.signal ? [options.signal] : [])]);
|
|
80
|
+
let latest = initial;
|
|
81
|
+
const seen = new Set(initial.repos.filter((repo) => repo.connected).map((repo) => repo.full_name));
|
|
82
|
+
try {
|
|
83
|
+
const target = options.project ? (options.projectExists ? reposUrl : projectUrl) : connectUrl;
|
|
84
|
+
void (options.open ?? openBrowser)(target).catch(() => false);
|
|
85
|
+
while (!signal.aborted) {
|
|
86
|
+
await (options.wait ?? (async (signal) => { await setTimeout(5000, undefined, { signal }); }))(signal);
|
|
87
|
+
signal.throwIfAborted();
|
|
88
|
+
latest = await (options.poll ?? ((signal) => resolveRepos(token, repos, fetch, signal)))(signal);
|
|
89
|
+
if (options.project) {
|
|
90
|
+
reportAssignments(latest, repos, assignments, options.say);
|
|
91
|
+
if (reposAreInProject(latest, repos, options.project)) {
|
|
92
|
+
options.say(verifiedLine(repos, options.project));
|
|
93
|
+
return latest;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
else {
|
|
97
|
+
for (const repo of latest.repos) {
|
|
98
|
+
if (repo.connected && !seen.has(repo.full_name)) {
|
|
99
|
+
options.say(`Connected ${repo.full_name}.`);
|
|
100
|
+
seen.add(repo.full_name);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
if (latest.workspace.installed && latest.repos.every((repo) => repo.connected))
|
|
104
|
+
return latest;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
catch (error) {
|
|
109
|
+
if (!signal.aborted)
|
|
110
|
+
throw error;
|
|
111
|
+
}
|
|
112
|
+
finally {
|
|
113
|
+
process.removeListener("SIGINT", cancel);
|
|
114
|
+
}
|
|
115
|
+
options.say(options.project
|
|
116
|
+
? `Stopped waiting; project "${options.project}" is not verified for every requested repo.`
|
|
117
|
+
: "Stopped waiting for repository connections; continuing local setup.");
|
|
118
|
+
return latest;
|
|
119
|
+
}
|
|
120
|
+
//# sourceMappingURL=connect.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"connect.js","sourceRoot":"","sources":["../../src/init/connect.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,sBAAsB,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,wBAAwB,CAAC;AAClD,OAAO,EAAE,WAAW,EAAE,MAAM,kBAAkB,CAAC;AAC/C,OAAO,EAAE,YAAY,EAAsB,MAAM,iBAAiB,CAAC;AAcnE,qFAAqF;AACrF,MAAM,UAAU,iBAAiB,CAC/B,QAAuB,EACvB,KAAe,EACf,OAAe;IAEf,IAAI,CAAC,QAAQ,CAAC,SAAS,CAAC,SAAS,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IACtE,OAAO,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE;QAC1B,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAC9B,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,SAAS,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,WAAW,EAAE,CACxE,CAAC;QACF,OAAO,IAAI,EAAE,SAAS,KAAK,IAAI,IAAI,IAAI,CAAC,YAAY,KAAK,OAAO,CAAC;IACnE,CAAC,CAAC,CAAC;AACL,CAAC;AAED,SAAS,UAAU,CAAC,QAAuB,EAAE,IAAY;IACvD,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAC9B,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,SAAS,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,WAAW,EAAE,CACxE,CAAC;IACF,OAAO,IAAI,EAAE,SAAS,KAAK,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,YAAY,CAAC,CAAC,CAAC,eAAe,CAAC;AACxE,CAAC;AAED,SAAS,iBAAiB,CACxB,QAAuB,EACvB,KAAe,EACf,QAA6B,EAC7B,GAA8B;IAE9B,KAAK,MAAM,IAAI,IAAI,KAAK,EAAE,CAAC;QACzB,MAAM,OAAO,GAAG,UAAU,CAAC,QAAQ,EAAE,IAAI,CAAC,CAAC;QAC3C,IAAI,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,OAAO;YAAE,SAAS;QAC3D,MAAM,IAAI,GAAG,QAAQ,CAAC,KAAK,CAAC,IAAI,CAC9B,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,SAAS,CAAC,WAAW,EAAE,KAAK,IAAI,CAAC,WAAW,EAAE,CACxE,CAAC;QACF,MAAM,UAAU,GAAG,IAAI,EAAE,SAAS,IAAI,IAAI,CAAC;QAC3C,GAAG,CACD,OAAO,KAAK,eAAe;YACzB,CAAC,CAAC,GAAG,UAAU,sCAAsC;YACrD,CAAC,CAAC,GAAG,UAAU,6BAA6B,OAAO,IAAI,CAC1D,CAAC;QACF,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,EAAE,EAAE,OAAO,CAAC,CAAC;IAC5C,CAAC;AACH,CAAC;AAED,SAAS,YAAY,CAAC,KAAe,EAAE,OAAe;IACpD,OAAO,KAAK,CAAC,MAAM,KAAK,CAAC;QACvB,CAAC,CAAC,+BAA+B,OAAO,IAAI;QAC5C,CAAC,CAAC,YAAY,KAAK,CAAC,MAAM,sBAAsB,OAAO,IAAI,CAAC;AAChE,CAAC;AAED,+EAA+E;AAC/E,MAAM,CAAC,KAAK,UAAU,YAAY,CAAC,KAAa,EAAE,KAAe,EAAE,OAAsB,EAAE,OAAuB;IAChH,MAAM,YAAY,GAAG,GAAG,QAAQ,MAAM,kBAAkB,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,EAAE,CAAC;IACnF,MAAM,UAAU,GAAG,GAAG,YAAY,UAAU,CAAC;IAC7C,MAAM,UAAU,GAAG,GAAG,YAAY,WAAW,CAAC;IAC9C,MAAM,QAAQ,GAAG,GAAG,YAAY,QAAQ,CAAC;IACzC,MAAM,WAAW,GAAG,IAAI,GAAG,EAAkB,CAAC;IAE9C,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;QACpB,iBAAiB,CAAC,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;QAC5D,IAAI,iBAAiB,CAAC,OAAO,EAAE,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YACvD,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;YAClD,OAAO,OAAO,CAAC;QACjB,CAAC;QACD,0EAA0E;QAC1E,qEAAqE;QACrE,kBAAkB;QAClB,IAAI,CAAC,OAAO,CAAC,aAAa,EAAE,CAAC;YAC3B,OAAO,CAAC,GAAG,CAAC,mBAAmB,OAAO,CAAC,OAAO,gBAAgB,UAAU,EAAE,CAAC,CAAC;YAC5E,OAAO,CAAC,GAAG,CAAC,+DAA+D,OAAO,CAAC,OAAO,IAAI,CAAC,CAAC;QAClG,CAAC;QACD,IAAI,CAAC,OAAO,CAAC,SAAS,CAAC,SAAS,IAAI,OAAO,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;YAClF,OAAO,CAAC,GAAG,CAAC,8BAA8B,UAAU,EAAE,CAAC,CAAC;QAC1D,CAAC;QACD,OAAO,CAAC,GAAG,CAAC,+BAA+B,QAAQ,EAAE,CAAC,CAAC;QACvD,OAAO,CAAC,GAAG,CAAC,YAAY,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,iFAAiF,CAAC,CAAC;IAC7H,CAAC;SAAM,CAAC;QACN,IAAI,OAAO,CAAC,SAAS,CAAC,SAAS,IAAI,OAAO,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC;YAAE,OAAO,OAAO,CAAC;QACjG,OAAO,CAAC,GAAG,CAAC,yCAAyC,UAAU,EAAE,CAAC,CAAC;IACrE,CAAC;IACD,IAAI,CAAC,OAAO,CAAC,WAAW,EAAE,CAAC;QACzB,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;YACpB,OAAO,CAAC,GAAG,CAAC,YAAY,OAAO,CAAC,OAAO,6CAA6C,CAAC,CAAC;QACxF,CAAC;QACD,OAAO,OAAO,CAAC;IACjB,CAAC;IACD,OAAO,CAAC,GAAG,CACT,OAAO,CAAC,OAAO;QACb,CAAC,CAAC,sDAAsD,OAAO,CAAC,OAAO,0BAA0B;QACjG,CAAC,CAAC,qFAAqF,CAC1F,CAAC;IACF,MAAM,UAAU,GAAG,IAAI,eAAe,EAAE,CAAC;IACzC,MAAM,MAAM,GAAG,GAAS,EAAE,CAAC,UAAU,CAAC,KAAK,EAAE,CAAC;IAC9C,OAAO,CAAC,IAAI,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAC/B,MAAM,MAAM,GAAG,WAAW,CAAC,GAAG,CAAC,CAAC,UAAU,CAAC,MAAM,EAAE,WAAW,CAAC,OAAO,CAAC,EAAE,GAAG,MAAM,CAAC,EAAE,GAAG,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC;IACnI,IAAI,MAAM,GAAG,OAAO,CAAC;IACrB,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,OAAO,CAAC,KAAK,CAAC,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC;IACnG,IAAI,CAAC;QACH,MAAM,MAAM,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,OAAO,CAAC,aAAa,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC;QAC9F,KAAK,CAAC,OAAO,CAAC,IAAI,IAAI,WAAW,CAAC,CAAC,MAAM,CAAC,CAAC,KAAK,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,CAAC;QAC9D,OAAO,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;YACvB,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,KAAK,EAAE,MAAM,EAAE,EAAE,GAAG,MAAM,UAAU,CAAC,IAAI,EAAE,SAAS,EAAE,EAAE,MAAM,EAAE,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YACvG,MAAM,CAAC,cAAc,EAAE,CAAC;YACxB,MAAM,GAAG,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,CAAC,CAAC,MAAM,EAAE,EAAE,CAAC,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC;YACjG,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;gBACpB,iBAAiB,CAAC,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,OAAO,CAAC,GAAG,CAAC,CAAC;gBAC3D,IAAI,iBAAiB,CAAC,MAAM,EAAE,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;oBACtD,OAAO,CAAC,GAAG,CAAC,YAAY,CAAC,KAAK,EAAE,OAAO,CAAC,OAAO,CAAC,CAAC,CAAC;oBAClD,OAAO,MAAM,CAAC;gBAChB,CAAC;YACH,CAAC;iBAAM,CAAC;gBACN,KAAK,MAAM,IAAI,IAAI,MAAM,CAAC,KAAK,EAAE,CAAC;oBAChC,IAAI,IAAI,CAAC,SAAS,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,EAAE,CAAC;wBAChD,OAAO,CAAC,GAAG,CAAC,aAAa,IAAI,CAAC,SAAS,GAAG,CAAC,CAAC;wBAC5C,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;oBAC3B,CAAC;gBACH,CAAC;gBACD,IAAI,MAAM,CAAC,SAAS,CAAC,SAAS,IAAI,MAAM,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,SAAS,CAAC;oBAAE,OAAO,MAAM,CAAC;YAChG,CAAC;QACH,CAAC;IACH,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QAAC,IAAI,CAAC,MAAM,CAAC,OAAO;YAAE,MAAM,KAAK,CAAC;IAAC,CAAC;YAC7C,CAAC;QAAC,OAAO,CAAC,cAAc,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAAC,CAAC;IACrD,OAAO,CAAC,GAAG,CACT,OAAO,CAAC,OAAO;QACb,CAAC,CAAC,6BAA6B,OAAO,CAAC,OAAO,6CAA6C;QAC3F,CAAC,CAAC,qEAAqE,CAC1E,CAAC;IACF,OAAO,MAAM,CAAC;AAChB,CAAC"}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
export declare const MCP_URL = "https://api.carrick.tools/mcp";
|
|
2
|
+
export declare const MCP_NAME = "carrick";
|
|
3
|
+
export declare const MCP_LINE = "claude mcp add --scope user --transport http carrick https://api.carrick.tools/mcp";
|
|
4
|
+
/** Everything this module touches outside itself, so a test can state a machine. */
|
|
5
|
+
export type McpEnvironment = {
|
|
6
|
+
onPath: (command: string) => boolean;
|
|
7
|
+
/** Exit status of a command, or null when it could not be started. */
|
|
8
|
+
run: (command: string, args: string[]) => number | null;
|
|
9
|
+
home: string;
|
|
10
|
+
platform: NodeJS.Platform;
|
|
11
|
+
appData: string | null;
|
|
12
|
+
readFile: (file: string) => string | null;
|
|
13
|
+
writeFile: (file: string, body: string) => void;
|
|
14
|
+
exists: (target: string) => boolean;
|
|
15
|
+
};
|
|
16
|
+
export declare function realEnvironment(): McpEnvironment;
|
|
17
|
+
/** A client configured by writing a file it owns. */
|
|
18
|
+
export type FileClient = {
|
|
19
|
+
name: string;
|
|
20
|
+
/** The client's own data directory: its existence is the detection. */
|
|
21
|
+
directory: (env: McpEnvironment) => string | null;
|
|
22
|
+
file: (env: McpEnvironment) => string | null;
|
|
23
|
+
/** Container key for a file this client has not written yet. */
|
|
24
|
+
container: "mcpServers" | "servers";
|
|
25
|
+
/** URL key for a file this client has not written yet. */
|
|
26
|
+
urlKey: "url" | "serverUrl";
|
|
27
|
+
/** Whether a fresh entry states `"type": "http"`. */
|
|
28
|
+
statesType: boolean;
|
|
29
|
+
};
|
|
30
|
+
/** What happened for one client, in the order init prints it. */
|
|
31
|
+
export type McpOutcome = {
|
|
32
|
+
client: string;
|
|
33
|
+
state: "written" | "present" | "failed";
|
|
34
|
+
/** The line to print: the path written, or what to do by hand. */
|
|
35
|
+
detail: string;
|
|
36
|
+
};
|
|
37
|
+
/**
|
|
38
|
+
* The entry to add, and where, for one client's existing file.
|
|
39
|
+
*
|
|
40
|
+
* Exported for the test that states each shape: this is the half that reads a
|
|
41
|
+
* file rather than remembering one.
|
|
42
|
+
*/
|
|
43
|
+
export declare function mergeServerEntry(client: FileClient, existing: string | null): {
|
|
44
|
+
body: string;
|
|
45
|
+
state: "written" | "present";
|
|
46
|
+
};
|
|
47
|
+
/** Configure every client this machine has. Empty when it has none. */
|
|
48
|
+
export declare function connectMcpClients(env?: McpEnvironment): McpOutcome[];
|
|
49
|
+
/** The lines init prints for the MCP step, whatever it could configure. */
|
|
50
|
+
export declare function mcpLines(outcomes: McpOutcome[]): string[];
|
package/dist/init/mcp.js
ADDED
|
@@ -0,0 +1,235 @@
|
|
|
1
|
+
// Connecting this machine's agent clients to the workspace MCP server.
|
|
2
|
+
//
|
|
3
|
+
// `carrick init` used to print one line for one client and leave the rest to
|
|
4
|
+
// the reader. It now configures every client it can see on this machine, and
|
|
5
|
+
// prints the line only for what it could not (carrick#955). One connection
|
|
6
|
+
// serves every project in the workspace, so this is a once-per-machine step.
|
|
7
|
+
//
|
|
8
|
+
// Two ways to configure a client, and the choice is not stylistic:
|
|
9
|
+
//
|
|
10
|
+
// 1. **Its own command**, where the client ships one. Claude Code's
|
|
11
|
+
// `claude mcp add` is the line this CLI already printed, so running it is
|
|
12
|
+
// the same act the reader was being asked to perform, and the file layout
|
|
13
|
+
// stays the client's business.
|
|
14
|
+
// 2. **Its config file**, for clients with no such command. The shape of an
|
|
15
|
+
// entry is read off the file wherever the file can state it — the
|
|
16
|
+
// container key it already uses, and the URL key its existing entries use
|
|
17
|
+
// — so extending a configured client follows that client rather than a
|
|
18
|
+
// remembered schema. Only a file that does not exist yet is written from
|
|
19
|
+
// the default below, and every path written is printed so a wrong guess is
|
|
20
|
+
// one visible line and one entry to delete.
|
|
21
|
+
//
|
|
22
|
+
// A client is "on this machine" when its own data directory exists; those
|
|
23
|
+
// directories are created by the client, never by us. Nothing here may throw:
|
|
24
|
+
// a client that cannot be configured falls back to a printed line, because a
|
|
25
|
+
// failed MCP write must not fail a setup that has already written files.
|
|
26
|
+
import fs from "node:fs";
|
|
27
|
+
import os from "node:os";
|
|
28
|
+
import path from "node:path";
|
|
29
|
+
import { spawnSync } from "node:child_process";
|
|
30
|
+
export const MCP_URL = "https://api.carrick.tools/mcp";
|
|
31
|
+
export const MCP_NAME = "carrick";
|
|
32
|
+
export const MCP_LINE = `claude mcp add --scope user --transport http ${MCP_NAME} ${MCP_URL}`;
|
|
33
|
+
export function realEnvironment() {
|
|
34
|
+
return {
|
|
35
|
+
onPath: (command) => spawnSync(process.platform === "win32" ? "where" : "which", [command], {
|
|
36
|
+
stdio: "ignore",
|
|
37
|
+
}).status === 0,
|
|
38
|
+
// Time-limited and with no stdin: a client's own command is someone
|
|
39
|
+
// else's code, and `init` has already written files by the time it runs.
|
|
40
|
+
// A command that hangs or asks a question reads as one that did not
|
|
41
|
+
// configure anything, and the line to run by hand is printed instead.
|
|
42
|
+
run: (command, args) => spawnSync(command, args, { stdio: "ignore", timeout: 15_000 }).status,
|
|
43
|
+
home: os.homedir(),
|
|
44
|
+
platform: process.platform,
|
|
45
|
+
appData: process.env["APPDATA"] ?? null,
|
|
46
|
+
readFile: (file) => {
|
|
47
|
+
try {
|
|
48
|
+
return fs.readFileSync(file, "utf8");
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
writeFile: (file, body) => {
|
|
55
|
+
fs.mkdirSync(path.dirname(file), { recursive: true });
|
|
56
|
+
fs.writeFileSync(file, body);
|
|
57
|
+
},
|
|
58
|
+
exists: (target) => fs.existsSync(target),
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
function vsCodeUserDirectory(env) {
|
|
62
|
+
if (env.platform === "darwin") {
|
|
63
|
+
return path.join(env.home, "Library", "Application Support", "Code", "User");
|
|
64
|
+
}
|
|
65
|
+
if (env.platform === "win32") {
|
|
66
|
+
return env.appData ? path.join(env.appData, "Code", "User") : null;
|
|
67
|
+
}
|
|
68
|
+
return path.join(env.home, ".config", "Code", "User");
|
|
69
|
+
}
|
|
70
|
+
const FILE_CLIENTS = [
|
|
71
|
+
{
|
|
72
|
+
name: "Cursor",
|
|
73
|
+
directory: (env) => path.join(env.home, ".cursor"),
|
|
74
|
+
file: (env) => path.join(env.home, ".cursor", "mcp.json"),
|
|
75
|
+
container: "mcpServers",
|
|
76
|
+
urlKey: "url",
|
|
77
|
+
statesType: false,
|
|
78
|
+
},
|
|
79
|
+
{
|
|
80
|
+
name: "Windsurf",
|
|
81
|
+
directory: (env) => path.join(env.home, ".codeium", "windsurf"),
|
|
82
|
+
file: (env) => path.join(env.home, ".codeium", "windsurf", "mcp_config.json"),
|
|
83
|
+
container: "mcpServers",
|
|
84
|
+
urlKey: "serverUrl",
|
|
85
|
+
statesType: false,
|
|
86
|
+
},
|
|
87
|
+
{
|
|
88
|
+
name: "VS Code",
|
|
89
|
+
directory: vsCodeUserDirectory,
|
|
90
|
+
file: (env) => {
|
|
91
|
+
const directory = vsCodeUserDirectory(env);
|
|
92
|
+
return directory === null ? null : path.join(directory, "mcp.json");
|
|
93
|
+
},
|
|
94
|
+
container: "servers",
|
|
95
|
+
urlKey: "url",
|
|
96
|
+
statesType: true,
|
|
97
|
+
},
|
|
98
|
+
];
|
|
99
|
+
function isRecord(value) {
|
|
100
|
+
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* The entry to add, and where, for one client's existing file.
|
|
104
|
+
*
|
|
105
|
+
* Exported for the test that states each shape: this is the half that reads a
|
|
106
|
+
* file rather than remembering one.
|
|
107
|
+
*/
|
|
108
|
+
export function mergeServerEntry(client, existing) {
|
|
109
|
+
const document = existing === null || existing.trim() === "" ? {} : JSON.parse(existing);
|
|
110
|
+
if (!isRecord(document))
|
|
111
|
+
throw new Error("the file is not a JSON object");
|
|
112
|
+
// The container the file already uses wins over the default: a client that
|
|
113
|
+
// renamed its key between versions has stated which one it reads.
|
|
114
|
+
const container = isRecord(document[client.container])
|
|
115
|
+
? client.container
|
|
116
|
+
: isRecord(document["mcpServers"])
|
|
117
|
+
? "mcpServers"
|
|
118
|
+
: isRecord(document["servers"])
|
|
119
|
+
? "servers"
|
|
120
|
+
: client.container;
|
|
121
|
+
const servers = isRecord(document[container]) ? { ...document[container] } : {};
|
|
122
|
+
if (MCP_NAME in servers)
|
|
123
|
+
return { body: `${JSON.stringify(document, null, 2)}\n`, state: "present" };
|
|
124
|
+
// The URL key and the type field are read off a sibling where there is one.
|
|
125
|
+
const siblings = Object.values(servers).filter(isRecord);
|
|
126
|
+
const urlKey = siblings.some((entry) => "serverUrl" in entry)
|
|
127
|
+
? "serverUrl"
|
|
128
|
+
: siblings.some((entry) => "url" in entry)
|
|
129
|
+
? "url"
|
|
130
|
+
: client.urlKey;
|
|
131
|
+
const statesType = siblings.some((entry) => typeof entry["type"] === "string")
|
|
132
|
+
? true
|
|
133
|
+
: siblings.length > 0
|
|
134
|
+
? false
|
|
135
|
+
: client.statesType;
|
|
136
|
+
const entry = statesType
|
|
137
|
+
? { type: "http", [urlKey]: MCP_URL }
|
|
138
|
+
: { [urlKey]: MCP_URL };
|
|
139
|
+
servers[MCP_NAME] = entry;
|
|
140
|
+
const merged = {};
|
|
141
|
+
for (const [key, value] of Object.entries(document)) {
|
|
142
|
+
merged[key] = key === container ? servers : value;
|
|
143
|
+
}
|
|
144
|
+
if (!(container in merged))
|
|
145
|
+
merged[container] = servers;
|
|
146
|
+
return { body: `${JSON.stringify(merged, null, 2)}\n`, state: "written" };
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Claude Code, through the command it ships.
|
|
150
|
+
*
|
|
151
|
+
* Detected the same way as the others — its own data directory — and not by
|
|
152
|
+
* the command alone: a machine where `claude` resolves but nothing has ever
|
|
153
|
+
* run it has no user configuration to add a server to, and the detection rule
|
|
154
|
+
* stays one rule.
|
|
155
|
+
*/
|
|
156
|
+
function configureClaudeCode(env) {
|
|
157
|
+
if (!env.onPath("claude") || !env.exists(path.join(env.home, ".claude")))
|
|
158
|
+
return null;
|
|
159
|
+
// `claude mcp add` refuses a name it already holds, so the read comes first
|
|
160
|
+
// and a client already connected is left exactly as it is.
|
|
161
|
+
if (env.run("claude", ["mcp", "get", MCP_NAME]) === 0) {
|
|
162
|
+
return { client: "Claude Code", state: "present", detail: `already connected as "${MCP_NAME}"` };
|
|
163
|
+
}
|
|
164
|
+
const status = env.run("claude", [
|
|
165
|
+
"mcp",
|
|
166
|
+
"add",
|
|
167
|
+
"--scope",
|
|
168
|
+
"user",
|
|
169
|
+
"--transport",
|
|
170
|
+
"http",
|
|
171
|
+
MCP_NAME,
|
|
172
|
+
MCP_URL,
|
|
173
|
+
]);
|
|
174
|
+
return status === 0
|
|
175
|
+
? { client: "Claude Code", state: "written", detail: "connected for this user" }
|
|
176
|
+
: { client: "Claude Code", state: "failed", detail: MCP_LINE };
|
|
177
|
+
}
|
|
178
|
+
function configureFileClient(env, client) {
|
|
179
|
+
const directory = client.directory(env);
|
|
180
|
+
const file = client.file(env);
|
|
181
|
+
if (directory === null || file === null)
|
|
182
|
+
return null;
|
|
183
|
+
if (!env.exists(directory) && !env.exists(file))
|
|
184
|
+
return null;
|
|
185
|
+
const existing = env.readFile(file);
|
|
186
|
+
try {
|
|
187
|
+
const { body, state } = mergeServerEntry(client, existing);
|
|
188
|
+
if (state === "present") {
|
|
189
|
+
return { client: client.name, state: "present", detail: `already in ${file}` };
|
|
190
|
+
}
|
|
191
|
+
env.writeFile(file, body);
|
|
192
|
+
return { client: client.name, state: "written", detail: file };
|
|
193
|
+
}
|
|
194
|
+
catch {
|
|
195
|
+
// A hand-edited file that no longer parses is a thing to report, never a
|
|
196
|
+
// thing to replace.
|
|
197
|
+
return {
|
|
198
|
+
client: client.name,
|
|
199
|
+
state: "failed",
|
|
200
|
+
detail: `${file} is not valid JSON. Add "${MCP_NAME}" (${MCP_URL}) there by hand.`,
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
/** Configure every client this machine has. Empty when it has none. */
|
|
205
|
+
export function connectMcpClients(env = realEnvironment()) {
|
|
206
|
+
const outcomes = [];
|
|
207
|
+
const claude = configureClaudeCode(env);
|
|
208
|
+
if (claude)
|
|
209
|
+
outcomes.push(claude);
|
|
210
|
+
for (const client of FILE_CLIENTS) {
|
|
211
|
+
const outcome = configureFileClient(env, client);
|
|
212
|
+
if (outcome)
|
|
213
|
+
outcomes.push(outcome);
|
|
214
|
+
}
|
|
215
|
+
return outcomes;
|
|
216
|
+
}
|
|
217
|
+
/** The lines init prints for the MCP step, whatever it could configure. */
|
|
218
|
+
export function mcpLines(outcomes) {
|
|
219
|
+
if (outcomes.length === 0) {
|
|
220
|
+
return [
|
|
221
|
+
" The org index, for work that crosses repos you do not have on disk.",
|
|
222
|
+
" No agent client was found on this machine; in Claude Code that is:",
|
|
223
|
+
` ${MCP_LINE}`,
|
|
224
|
+
` Any other client takes the same server over HTTP: ${MCP_URL}`,
|
|
225
|
+
];
|
|
226
|
+
}
|
|
227
|
+
const lines = [` The org index (${MCP_URL}), once per machine:`];
|
|
228
|
+
for (const outcome of outcomes) {
|
|
229
|
+
const verb = outcome.state === "written" ? "wrote" : outcome.state === "present" ? "unchanged" : "could not";
|
|
230
|
+
lines.push(` ${verb} ${outcome.client}: ${outcome.detail}`);
|
|
231
|
+
}
|
|
232
|
+
lines.push(" Restart a running client to pick the server up.");
|
|
233
|
+
return lines;
|
|
234
|
+
}
|
|
235
|
+
//# sourceMappingURL=mcp.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mcp.js","sourceRoot":"","sources":["../../src/init/mcp.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { z } from "zod";
|
|
2
|
+
declare const project: z.ZodObject<{
|
|
3
|
+
slug: z.ZodString;
|
|
4
|
+
name: z.ZodString;
|
|
5
|
+
archived: z.ZodBoolean;
|
|
6
|
+
repo_count: z.ZodNullable<z.ZodNumber>;
|
|
7
|
+
}, "strip", z.ZodTypeAny, {
|
|
8
|
+
name: string;
|
|
9
|
+
slug: string;
|
|
10
|
+
archived: boolean;
|
|
11
|
+
repo_count: number | null;
|
|
12
|
+
}, {
|
|
13
|
+
name: string;
|
|
14
|
+
slug: string;
|
|
15
|
+
archived: boolean;
|
|
16
|
+
repo_count: number | null;
|
|
17
|
+
}>;
|
|
18
|
+
/** One project in the signed-in workspace. */
|
|
19
|
+
export type Project = z.infer<typeof project>;
|
|
20
|
+
/** What a create attempt did. `absent` is "this server has no such action". */
|
|
21
|
+
export type CreateOutcome = {
|
|
22
|
+
kind: "created";
|
|
23
|
+
project: Project;
|
|
24
|
+
} | {
|
|
25
|
+
kind: "refused";
|
|
26
|
+
message: string;
|
|
27
|
+
} | {
|
|
28
|
+
kind: "absent";
|
|
29
|
+
};
|
|
30
|
+
/**
|
|
31
|
+
* Every project in the signed-in workspace, or null when this API has no such
|
|
32
|
+
* action. Throws only on 401.
|
|
33
|
+
*/
|
|
34
|
+
export declare function listProjects(token: string, request?: typeof fetch, signal?: AbortSignal): Promise<Project[] | null>;
|
|
35
|
+
/**
|
|
36
|
+
* Create one project in the signed-in workspace.
|
|
37
|
+
*
|
|
38
|
+
* The slug is sent as given: `carrick init` validates it against the same
|
|
39
|
+
* shape the dashboard enforces before it ever reaches here, so a slug this
|
|
40
|
+
* client sends is one the server can accept or refuse on its own rules, never
|
|
41
|
+
* one it has to normalise silently into something the user did not ask for.
|
|
42
|
+
*/
|
|
43
|
+
export declare function createProject(token: string, slug: string, name?: string, request?: typeof fetch, signal?: AbortSignal): Promise<CreateOutcome>;
|
|
44
|
+
/** The list as init prints it: one line per project, active ones first. */
|
|
45
|
+
export declare function projectLines(projects: Project[]): string[];
|
|
46
|
+
export {};
|