@super-hands/connect 0.1.16 → 0.1.18
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 +15 -6
- package/client.mjs +78 -17
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -3,12 +3,20 @@
|
|
|
3
3
|
Connect the coding agents on this machine to your team's
|
|
4
4
|
[Superhands](https://app.superhands.ai) MCP server, in one command.
|
|
5
5
|
|
|
6
|
-
Superhands
|
|
6
|
+
Superhands generates the command for you, with a short-lived setup code on
|
|
7
|
+
the end of it:
|
|
7
8
|
|
|
8
9
|
```
|
|
9
|
-
|
|
10
|
+
npx -y @super-hands/connect@latest --code shsetup_…
|
|
10
11
|
```
|
|
11
12
|
|
|
13
|
+
The client exchanges that code for the credential over one request to your
|
|
14
|
+
deployment and writes the credential into the clients below. The code is
|
|
15
|
+
worth nothing twenty minutes later; the token it becomes never appears in the
|
|
16
|
+
command, the prompt you pasted it from, or your shell history. A command
|
|
17
|
+
written by hand around a token from the Agents page still works:
|
|
18
|
+
`SUPERHANDS_MCP_TOKEN="…" npx -y @super-hands/connect@latest`.
|
|
19
|
+
|
|
12
20
|
It connects to `https://app.superhands.ai/api/mcp` unless `SUPERHANDS_MCP_URL`
|
|
13
21
|
says otherwise — a command from a preview or local deployment carries that
|
|
14
22
|
variable too.
|
|
@@ -71,10 +79,11 @@ remove one client and leave the rest.
|
|
|
71
79
|
|
|
72
80
|
## What it does not do
|
|
73
81
|
|
|
74
|
-
It reads no repository, runs none of your code, and uploads nothing
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
82
|
+
It reads no repository, runs none of your code, and uploads nothing but the
|
|
83
|
+
setup code it was given, once, to the deployment that issued it. The one
|
|
84
|
+
secret it then holds is the token that came back, and it is written only into
|
|
85
|
+
the client configs above. The token can be revoked at any time from the
|
|
86
|
+
Superhands Agents page.
|
|
78
87
|
|
|
79
88
|
This file is generated from
|
|
80
89
|
[`lib/connect-client-entry.ts`](https://github.com/superhandsai/superhands/blob/main/lib/connect-client-entry.ts)
|
package/client.mjs
CHANGED
|
@@ -21,6 +21,7 @@ var MCP_ACCESS_TOKEN_TTL_SECONDS = 30 * 24 * 60 * 60;
|
|
|
21
21
|
// lib/mcp-clients.ts
|
|
22
22
|
var CONNECT_TOKEN_ENV = "SUPERHANDS_MCP_TOKEN";
|
|
23
23
|
var CONNECT_URL_ENV = "SUPERHANDS_MCP_URL";
|
|
24
|
+
var CONNECT_CODE_FLAG = "--code";
|
|
24
25
|
var CONNECT_DEFAULT_MCP_URL = "https://app.superhands.ai/api/mcp";
|
|
25
26
|
var CONNECT_CLIENT_PACKAGE = "@super-hands/connect";
|
|
26
27
|
var CONNECT_CLIENT_SPEC = `${CONNECT_CLIENT_PACKAGE}@latest`;
|
|
@@ -41,7 +42,7 @@ function codexConfigBlock(args) {
|
|
|
41
42
|
}
|
|
42
43
|
|
|
43
44
|
// lib/connect-skill.ts
|
|
44
|
-
var CONNECT_SKILL_VERSION =
|
|
45
|
+
var CONNECT_SKILL_VERSION = 12;
|
|
45
46
|
var CONNECT_SKILL_DIR = MCP_SERVER_KEY;
|
|
46
47
|
var CONNECT_SKILL_FILENAME = "SKILL.md";
|
|
47
48
|
var VERSION_MARKER = `[//]: # (superhands-skill-version: ${CONNECT_SKILL_VERSION})`;
|
|
@@ -75,9 +76,6 @@ Before you build, change, or restyle any UI:
|
|
|
75
76
|
example \`["app/marketing/pricing.tsx"]\`. The team writes down where
|
|
76
77
|
Instructions apply, and a path answers that far better than a description
|
|
77
78
|
does. Leave it out if you do not know yet; it narrows nothing away.
|
|
78
|
-
- If that tool is not available on this server, it is an older deployment:
|
|
79
|
-
call \`superhands_get_guidance\` and \`superhands_get_skill\` instead,
|
|
80
|
-
with the same \`intent\` \u2014 those two are older and take no \`paths\`.
|
|
81
79
|
2. Plan against what comes back, and note which parts of your plan each set of
|
|
82
80
|
keys covers. The first answer renders what to read first, then names
|
|
83
81
|
under **Reachable** the modules judged to matter to parts you have not
|
|
@@ -87,7 +85,7 @@ Before you build, change, or restyle any UI:
|
|
|
87
85
|
the team has written down; say so rather than quietly using your own
|
|
88
86
|
defaults.
|
|
89
87
|
3. As you start each part \u2014 the sidebar, then the table, then the form \u2014 call
|
|
90
|
-
\`
|
|
88
|
+
\`superhands_get_instruction_modules\` with the reachable keys covering the
|
|
91
89
|
part you are starting now and \`part\` set to that part in a few words,
|
|
92
90
|
for example \`"the form's validation errors"\`. A key ending in \`*\` takes
|
|
93
91
|
everything under it, so \`better-ui/*\` is a whole skill and
|
|
@@ -101,27 +99,32 @@ Before you build, change, or restyle any UI:
|
|
|
101
99
|
the start. These are instructions to follow, not facts to know, so modules
|
|
102
100
|
for a part you are not building are a page of directions about the wrong
|
|
103
101
|
thing sitting in your context while you work.
|
|
104
|
-
-
|
|
102
|
+
- Review-enforced decision entries from step 1 need no such loop. They are
|
|
105
103
|
concise enough to stay in the opening brief, so one read at the start is
|
|
106
104
|
the right read.
|
|
107
|
-
- If that tool is not available on this server,
|
|
105
|
+
- If that tool is not available on this server,
|
|
108
106
|
build from what step 1 already returned and skip this step. Do not ask
|
|
109
107
|
for the whole library instead \u2014 that is the wall of text step 1 avoided.
|
|
110
108
|
4. Build to what comes back. Use the selected Instructions together. When two
|
|
111
109
|
entries pull in different directions, prefer the more specific instruction,
|
|
112
110
|
preserve explicit exceptions, and call out any conflict you cannot honestly
|
|
113
111
|
resolve.
|
|
114
|
-
5.
|
|
112
|
+
5. As your work reaches files your opening intent never mentioned, call
|
|
113
|
+
\`superhands_possible_misses\` with the \`paths\` you are actually in. It is
|
|
114
|
+
a free, instant, advisory list of checks those files usually need \u2014 not a
|
|
115
|
+
review, not a gate \u2014 so call it as the work moves and once before your
|
|
116
|
+
summary, passing \`handled\` for anything you already covered.
|
|
117
|
+
6. When you have finished \u2014 not part-way through \u2014 send the files you wrote or
|
|
115
118
|
changed with \`superhands_report_completed_build\`, naming the \`route\` if
|
|
116
119
|
you know it. The team looks at what their agents build there; it records
|
|
117
120
|
and does not review, so a failed report costs the work nothing.
|
|
118
|
-
|
|
121
|
+
7. When you finish, say you followed the team's Instructions \u2014 never that your
|
|
119
122
|
work was checked. Reading Instructions is not a check, reporting a build is
|
|
120
123
|
not a review, and nothing has looked at what you built.
|
|
121
124
|
|
|
122
|
-
If the tool answers \`no-instructions
|
|
123
|
-
|
|
124
|
-
|
|
125
|
+
If the tool answers \`no-instructions\`, carry on with what you were asked for
|
|
126
|
+
and mention that the team adds its Instructions on the Superhands Instructions
|
|
127
|
+
page.
|
|
125
128
|
|
|
126
129
|
If the \`${MCP_SERVER_KEY}\` MCP server is not reachable in this session, say
|
|
127
130
|
so rather than guessing at the team's decisions \u2014 it is configured on this
|
|
@@ -326,6 +329,59 @@ function writeSkill(clientDir) {
|
|
|
326
329
|
function connectReportUrl(endpoint) {
|
|
327
330
|
return `${endpoint.replace(/\/+$/, "")}/connect`;
|
|
328
331
|
}
|
|
332
|
+
function connectExchangeUrl(endpoint) {
|
|
333
|
+
return `${connectReportUrl(endpoint)}/exchange`;
|
|
334
|
+
}
|
|
335
|
+
function takeCodeArgument(argv) {
|
|
336
|
+
const rest = [];
|
|
337
|
+
let code = null;
|
|
338
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
339
|
+
const arg = argv[i];
|
|
340
|
+
if (arg === CONNECT_CODE_FLAG) {
|
|
341
|
+
const next = argv[i + 1];
|
|
342
|
+
if (next !== void 0 && !next.startsWith("--")) {
|
|
343
|
+
code = next;
|
|
344
|
+
i += 1;
|
|
345
|
+
} else {
|
|
346
|
+
code = "";
|
|
347
|
+
}
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
if (arg.startsWith(`${CONNECT_CODE_FLAG}=`)) {
|
|
351
|
+
code = arg.slice(CONNECT_CODE_FLAG.length + 1);
|
|
352
|
+
continue;
|
|
353
|
+
}
|
|
354
|
+
rest.push(arg);
|
|
355
|
+
}
|
|
356
|
+
return { code, rest };
|
|
357
|
+
}
|
|
358
|
+
async function exchangeSetupCode(args) {
|
|
359
|
+
let response;
|
|
360
|
+
try {
|
|
361
|
+
response = await fetch(connectExchangeUrl(args.endpoint), {
|
|
362
|
+
method: "POST",
|
|
363
|
+
headers: { "content-type": "application/json" },
|
|
364
|
+
body: JSON.stringify({ code: args.code }),
|
|
365
|
+
signal: AbortSignal.timeout(1e4)
|
|
366
|
+
});
|
|
367
|
+
} catch {
|
|
368
|
+
return stop(
|
|
369
|
+
`Superhands at ${args.endpoint} could not be reached to turn the setup code into a credential. Check the connection and run this command again.`
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
const body = await response.json().catch(() => null);
|
|
373
|
+
if (response.ok && body?.ok && typeof body.token === "string" && body.token) {
|
|
374
|
+
return body.token;
|
|
375
|
+
}
|
|
376
|
+
if (response.status === 401 || response.status === 400) {
|
|
377
|
+
return stop(
|
|
378
|
+
"this setup code has expired or has been used up. Get a fresh command from Superhands and run it unchanged."
|
|
379
|
+
);
|
|
380
|
+
}
|
|
381
|
+
return stop(
|
|
382
|
+
`Superhands answered ${response.status} to the setup code. Try again in a moment, or get a fresh command from Superhands.`
|
|
383
|
+
);
|
|
384
|
+
}
|
|
329
385
|
async function reportSkillVersion(args) {
|
|
330
386
|
try {
|
|
331
387
|
await fetch(connectReportUrl(args.endpoint), {
|
|
@@ -425,7 +481,7 @@ function chosenClients(flags) {
|
|
|
425
481
|
return explicit ? { cursor, claude, codex, explicit } : { cursor: true, claude: true, codex: true, explicit };
|
|
426
482
|
}
|
|
427
483
|
async function main() {
|
|
428
|
-
const argv = process.argv.slice(2);
|
|
484
|
+
const { code, rest: argv } = takeCodeArgument(process.argv.slice(2));
|
|
429
485
|
const flags = new Set(argv.filter((arg) => arg.startsWith("--")));
|
|
430
486
|
const command = argv.find((arg) => !arg.startsWith("--")) ?? "install";
|
|
431
487
|
if (command === "uninstall") return uninstall(flags);
|
|
@@ -435,14 +491,19 @@ async function main() {
|
|
|
435
491
|
`unknown command "${command}". This tool takes "install" (the default), "update" or "uninstall".`
|
|
436
492
|
);
|
|
437
493
|
}
|
|
438
|
-
return install(flags);
|
|
494
|
+
return install(flags, code);
|
|
439
495
|
}
|
|
440
|
-
async function install(flags) {
|
|
441
|
-
const token = process.env[CONNECT_TOKEN_ENV]?.trim();
|
|
496
|
+
async function install(flags, code) {
|
|
442
497
|
const endpoint = process.env[CONNECT_URL_ENV]?.trim() || CONNECT_DEFAULT_MCP_URL;
|
|
498
|
+
if (code === "") {
|
|
499
|
+
stop(
|
|
500
|
+
`${CONNECT_CODE_FLAG} needs the setup code after it. Copy the whole command from Superhands and run it unchanged.`
|
|
501
|
+
);
|
|
502
|
+
}
|
|
503
|
+
const token = code ? await exchangeSetupCode({ endpoint, code }) : process.env[CONNECT_TOKEN_ENV]?.trim();
|
|
443
504
|
if (!token) {
|
|
444
505
|
stop(
|
|
445
|
-
`this command needs ${CONNECT_TOKEN_ENV} set on the same line. Copy the whole command from Superhands
|
|
506
|
+
`this command needs a setup code after ${CONNECT_CODE_FLAG}, or ${CONNECT_TOKEN_ENV} set on the same line. Copy the whole command from Superhands and run it unchanged.`
|
|
446
507
|
);
|
|
447
508
|
}
|
|
448
509
|
const wanted = chosenClients(flags);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@super-hands/connect",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.18",
|
|
4
4
|
"description": "Connect the coding agents on this machine to your team's Superhands MCP server, refresh it later with `update` (no token needed), and take it back off again with `uninstall`. Writes each client's own config; reads no repository, uploads nothing.",
|
|
5
5
|
"bin": {
|
|
6
6
|
"superhands-connect": "client.mjs"
|