@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.
Files changed (3) hide show
  1. package/README.md +15 -6
  2. package/client.mjs +78 -17
  3. 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 setup generates the command for you, credential included:
6
+ Superhands generates the command for you, with a short-lived setup code on
7
+ the end of it:
7
8
 
8
9
  ```
9
- SUPERHANDS_MCP_TOKEN="…" npx -y @super-hands/connect@latest
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. The one
75
- secret it holds is the token in its own environment, and it is written only
76
- into the client configs above. The token can be revoked at any time from the
77
- Superhands MCP page.
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 = 11;
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
- \`superhands_get_playbook_modules\` with the reachable keys covering the
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
- - Team-authored decision entries from step 1 need no such loop. They are
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, it is an older deployment:
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. When you have finished \u2014 not part-way through \u2014 send the files you wrote or
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
- 6. When you finish, say you followed the team's Instructions \u2014 never that your
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\` (or \`no-guidance\` from an older
123
- server), carry on with what you were asked for and mention that the team
124
- adds its Instructions on the Superhands Instructions page.
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 setup and run it unchanged.`
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.16",
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"