@gethmy/mcp 2.2.1 → 2.2.3
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 +5 -7
- package/dist/cli.js +405 -143
- package/dist/http.js +6 -4
- package/dist/index.js +164 -85
- package/dist/lib/auto-session.js +1 -1
- package/dist/lib/cli.js +9 -0
- package/dist/lib/onboard.js +36 -0
- package/dist/lib/server.js +129 -73
- package/dist/lib/skills.js +1 -1
- package/dist/lib/tui/setup.js +212 -59
- package/dist/remote.js +7132 -10614
- package/package.json +2 -1
- package/src/auto-session.ts +1 -1
- package/src/cli.ts +9 -0
- package/src/onboard.ts +93 -0
- package/src/server.ts +153 -102
- package/src/skills.ts +1 -1
- package/src/tui/setup.ts +249 -67
package/src/tui/setup.ts
CHANGED
|
@@ -15,8 +15,12 @@ import {
|
|
|
15
15
|
hasProjectContext,
|
|
16
16
|
isConfigured,
|
|
17
17
|
loadConfig,
|
|
18
|
+
saveConfig,
|
|
18
19
|
saveLocalConfig,
|
|
20
|
+
setActiveProject,
|
|
21
|
+
setActiveWorkspace,
|
|
19
22
|
} from "../config.js";
|
|
23
|
+
import { onboardNewUser } from "../onboard.js";
|
|
20
24
|
import { buildSkillFile, HARMONY_WORKFLOW_PROMPT } from "../skills.js";
|
|
21
25
|
import { type AgentId, detectAgents } from "./agents.js";
|
|
22
26
|
import { runDocsStep } from "./docs.js";
|
|
@@ -35,6 +39,8 @@ export interface SetupOptions {
|
|
|
35
39
|
projectId?: string;
|
|
36
40
|
skipContext?: boolean;
|
|
37
41
|
skipDocs?: boolean;
|
|
42
|
+
newAccount?: boolean;
|
|
43
|
+
name?: string;
|
|
38
44
|
}
|
|
39
45
|
|
|
40
46
|
// Central skills directory for global installation
|
|
@@ -499,55 +505,191 @@ export async function runSetup(options: SetupOptions = {}): Promise<void> {
|
|
|
499
505
|
needsContext = true;
|
|
500
506
|
}
|
|
501
507
|
|
|
502
|
-
// Step 1: API Key
|
|
508
|
+
// Step 1: API Key (or create account)
|
|
503
509
|
let apiKey = options.apiKey || existingConfig.apiKey;
|
|
504
510
|
let userEmail: string | undefined =
|
|
505
511
|
options.userEmail || existingConfig.userEmail || undefined;
|
|
512
|
+
let selectedWorkspaceIdFromSignup: string | undefined;
|
|
513
|
+
let selectedProjectIdFromSignup: string | undefined;
|
|
514
|
+
let selectedWorkspaceNameFromSignup: string | undefined;
|
|
515
|
+
let selectedProjectNameFromSignup: string | undefined;
|
|
516
|
+
let createdNewAccount = false;
|
|
506
517
|
|
|
507
518
|
if (needsApiKey || !apiKey || !apiKey.startsWith("hmy_")) {
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
519
|
+
// Determine path: create account or enter API key
|
|
520
|
+
let useNewAccount = options.newAccount === true;
|
|
521
|
+
|
|
522
|
+
if (!useNewAccount && options.apiKey) {
|
|
523
|
+
// API key passed via flag — skip the choice prompt
|
|
524
|
+
useNewAccount = false;
|
|
525
|
+
} else if (!useNewAccount && !options.apiKey) {
|
|
526
|
+
const getStarted = await p.select({
|
|
527
|
+
message: "How would you like to get started?",
|
|
528
|
+
options: [
|
|
529
|
+
{
|
|
530
|
+
value: "create",
|
|
531
|
+
label: "Create a free account",
|
|
532
|
+
hint: "recommended for new users",
|
|
533
|
+
},
|
|
534
|
+
{
|
|
535
|
+
value: "apikey",
|
|
536
|
+
label: "I already have an API key",
|
|
537
|
+
},
|
|
538
|
+
],
|
|
539
|
+
});
|
|
518
540
|
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
541
|
+
if (p.isCancel(getStarted)) {
|
|
542
|
+
p.cancel("Setup cancelled");
|
|
543
|
+
process.exit(0);
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
useNewAccount = getStarted === "create";
|
|
522
547
|
}
|
|
523
548
|
|
|
524
|
-
|
|
525
|
-
|
|
549
|
+
if (useNewAccount) {
|
|
550
|
+
// --- Create account flow ---
|
|
551
|
+
const fullName =
|
|
552
|
+
options.name ||
|
|
553
|
+
((await p.text({
|
|
554
|
+
message: "Full name",
|
|
555
|
+
placeholder: "Jane Smith",
|
|
556
|
+
validate: (v) => {
|
|
557
|
+
if (!v || v.trim().length === 0) return "Name is required";
|
|
558
|
+
if (v.length > 100) return "Name must be 100 characters or less";
|
|
559
|
+
return undefined;
|
|
560
|
+
},
|
|
561
|
+
})) as string);
|
|
562
|
+
|
|
563
|
+
if (p.isCancel(fullName)) {
|
|
564
|
+
p.cancel("Setup cancelled");
|
|
565
|
+
process.exit(0);
|
|
566
|
+
}
|
|
567
|
+
|
|
568
|
+
const email =
|
|
569
|
+
options.userEmail ||
|
|
570
|
+
((await p.text({
|
|
571
|
+
message: "Email",
|
|
572
|
+
placeholder: "you@example.com",
|
|
573
|
+
validate: (v) => {
|
|
574
|
+
if (!v) return "Email is required";
|
|
575
|
+
if (v.length > 254) return "Email is too long";
|
|
576
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(v))
|
|
577
|
+
return "Invalid email format";
|
|
578
|
+
return undefined;
|
|
579
|
+
},
|
|
580
|
+
})) as string);
|
|
581
|
+
|
|
582
|
+
if (p.isCancel(email)) {
|
|
583
|
+
p.cancel("Setup cancelled");
|
|
584
|
+
process.exit(0);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
const password = (await p.password({
|
|
588
|
+
message: "Password",
|
|
589
|
+
validate: (v) => {
|
|
590
|
+
if (!v) return "Password is required";
|
|
591
|
+
if (v.length < 8) return "Password must be at least 8 characters";
|
|
592
|
+
if (v.length > 128) return "Password must be 128 characters or less";
|
|
593
|
+
return undefined;
|
|
594
|
+
},
|
|
595
|
+
})) as string;
|
|
596
|
+
|
|
597
|
+
if (p.isCancel(password)) {
|
|
598
|
+
p.cancel("Setup cancelled");
|
|
599
|
+
process.exit(0);
|
|
600
|
+
}
|
|
601
|
+
|
|
602
|
+
const spinner = p.spinner();
|
|
603
|
+
spinner.start("Creating your account...");
|
|
604
|
+
|
|
605
|
+
try {
|
|
606
|
+
const result = await onboardNewUser({
|
|
607
|
+
email: email as string,
|
|
608
|
+
password: password as string,
|
|
609
|
+
fullName: fullName as string,
|
|
610
|
+
});
|
|
611
|
+
|
|
612
|
+
spinner.stop(
|
|
613
|
+
colors.success(`Account created for ${result.user.email}`),
|
|
614
|
+
);
|
|
615
|
+
|
|
616
|
+
apiKey = result.apiKey.rawKey;
|
|
617
|
+
userEmail = result.user.email;
|
|
618
|
+
selectedWorkspaceIdFromSignup = result.workspace.id;
|
|
619
|
+
selectedProjectIdFromSignup = result.project.id;
|
|
620
|
+
selectedWorkspaceNameFromSignup = result.workspace.name;
|
|
621
|
+
selectedProjectNameFromSignup = result.project.name;
|
|
622
|
+
createdNewAccount = true;
|
|
623
|
+
needsApiKey = true;
|
|
624
|
+
|
|
625
|
+
// Save config immediately
|
|
626
|
+
saveConfig({ apiKey, userEmail, apiUrl: API_URL });
|
|
627
|
+
setActiveWorkspace(selectedWorkspaceIdFromSignup);
|
|
628
|
+
setActiveProject(selectedProjectIdFromSignup);
|
|
629
|
+
|
|
630
|
+
p.log.success("Workspace and board created");
|
|
631
|
+
} catch (error) {
|
|
632
|
+
spinner.stop(colors.error("Account creation failed"));
|
|
633
|
+
const msg = error instanceof Error ? error.message : "Unknown error";
|
|
634
|
+
if (msg.includes("already") || msg.includes("409")) {
|
|
635
|
+
p.log.error(
|
|
636
|
+
"Account already exists. Sign in at app.gethmy.com to get your API key, or re-run setup and choose 'I already have an API key'.",
|
|
637
|
+
);
|
|
638
|
+
} else {
|
|
639
|
+
p.log.error(msg);
|
|
640
|
+
p.log.info("Please try again or visit https://app.gethmy.com");
|
|
641
|
+
}
|
|
642
|
+
process.exit(1);
|
|
643
|
+
}
|
|
644
|
+
} else {
|
|
645
|
+
// --- Existing API key flow ---
|
|
646
|
+
const keyInput = await p.text({
|
|
647
|
+
message: "Enter your Harmony API key",
|
|
648
|
+
placeholder: "hmy_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
|
|
649
|
+
validate: (value) => {
|
|
650
|
+
if (!value) return "API key is required";
|
|
651
|
+
if (!value.startsWith("hmy_"))
|
|
652
|
+
return 'API key must start with "hmy_"';
|
|
653
|
+
if (value.length < 20) return "API key is too short";
|
|
654
|
+
return undefined;
|
|
655
|
+
},
|
|
656
|
+
});
|
|
657
|
+
|
|
658
|
+
if (p.isCancel(keyInput)) {
|
|
659
|
+
p.cancel("Setup cancelled");
|
|
660
|
+
process.exit(0);
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
apiKey = keyInput as string;
|
|
664
|
+
needsApiKey = true;
|
|
665
|
+
}
|
|
526
666
|
} else {
|
|
527
667
|
p.log.success(`Using existing API key: ${apiKey.slice(0, 8)}...`);
|
|
528
668
|
}
|
|
529
669
|
|
|
530
|
-
// Validate API key
|
|
670
|
+
// Validate API key (skip if we just created account — key is guaranteed valid)
|
|
531
671
|
const spinner = p.spinner();
|
|
532
|
-
|
|
672
|
+
if (!createdNewAccount) {
|
|
673
|
+
spinner.start("Validating API key...");
|
|
533
674
|
|
|
534
|
-
|
|
675
|
+
const validation = await validateApiKey(apiKey);
|
|
535
676
|
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
677
|
+
if (!validation.valid) {
|
|
678
|
+
spinner.stop(colors.error("API key validation failed"));
|
|
679
|
+
p.log.error(validation.error || "Could not connect to Harmony API");
|
|
680
|
+
p.log.info("Get an API key at: https://app.gethmy.com/user/keys");
|
|
681
|
+
process.exit(1);
|
|
682
|
+
}
|
|
542
683
|
|
|
543
|
-
|
|
544
|
-
|
|
684
|
+
if (!userEmail) {
|
|
685
|
+
userEmail = validation.email;
|
|
686
|
+
}
|
|
687
|
+
spinner.stop(
|
|
688
|
+
colors.success(
|
|
689
|
+
userEmail ? `Connected as ${userEmail}` : "API key validated",
|
|
690
|
+
),
|
|
691
|
+
);
|
|
545
692
|
}
|
|
546
|
-
spinner.stop(
|
|
547
|
-
colors.success(
|
|
548
|
-
userEmail ? `Connected as ${userEmail}` : "API key validated",
|
|
549
|
-
),
|
|
550
|
-
);
|
|
551
693
|
|
|
552
694
|
// Step 2: Check if skills are already installed
|
|
553
695
|
let selectedAgents: AgentId[] = [];
|
|
@@ -633,10 +775,17 @@ export async function runSetup(options: SetupOptions = {}): Promise<void> {
|
|
|
633
775
|
}
|
|
634
776
|
|
|
635
777
|
// Step 3: Workspace and Project Selection
|
|
636
|
-
let selectedWorkspaceId =
|
|
637
|
-
|
|
638
|
-
let
|
|
639
|
-
let
|
|
778
|
+
let selectedWorkspaceId =
|
|
779
|
+
selectedWorkspaceIdFromSignup || options.workspaceId;
|
|
780
|
+
let selectedProjectId = selectedProjectIdFromSignup || options.projectId;
|
|
781
|
+
let selectedWorkspaceName: string | undefined =
|
|
782
|
+
selectedWorkspaceNameFromSignup;
|
|
783
|
+
let selectedProjectName: string | undefined = selectedProjectNameFromSignup;
|
|
784
|
+
|
|
785
|
+
// Skip context selection if we just created a new account
|
|
786
|
+
if (createdNewAccount) {
|
|
787
|
+
needsContext = false;
|
|
788
|
+
}
|
|
640
789
|
|
|
641
790
|
if (needsContext && !options.skipContext) {
|
|
642
791
|
// Fetch workspaces
|
|
@@ -935,46 +1084,79 @@ export async function runSetup(options: SetupOptions = {}): Promise<void> {
|
|
|
935
1084
|
console.log("");
|
|
936
1085
|
p.outro(colors.success("Setup complete!"));
|
|
937
1086
|
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
1087
|
+
if (createdNewAccount && selectedWorkspaceNameFromSignup) {
|
|
1088
|
+
// New account: show board URL and next steps
|
|
1089
|
+
const wsSlug = selectedWorkspaceNameFromSignup
|
|
1090
|
+
.toLowerCase()
|
|
1091
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
1092
|
+
.replace(/(^-|-$)/g, "");
|
|
1093
|
+
const projSlug = (selectedProjectNameFromSignup || "my-first-board")
|
|
1094
|
+
.toLowerCase()
|
|
1095
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
1096
|
+
.replace(/(^-|-$)/g, "");
|
|
1097
|
+
|
|
1098
|
+
console.log("");
|
|
942
1099
|
console.log(
|
|
943
|
-
`
|
|
1100
|
+
` ${colors.bold("Your board:")} ${colors.highlight(`https://app.gethmy.com/${wsSlug}/${projSlug}`)}`,
|
|
944
1101
|
);
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
console.log(` Context: ${formatPath(getLocalConfigPath(cwd), home)}`);
|
|
948
|
-
}
|
|
949
|
-
|
|
950
|
-
console.log("");
|
|
951
|
-
console.log(` ${colors.bold("Usage:")}`);
|
|
952
|
-
|
|
953
|
-
if (!needsSkills || selectedAgents.includes("claude")) {
|
|
1102
|
+
console.log("");
|
|
1103
|
+
console.log(` ${colors.bold("Next steps:")}`);
|
|
954
1104
|
console.log(
|
|
955
|
-
`
|
|
1105
|
+
` 1. Open Claude Code and say: ${colors.highlight('"Show me my board"')}`,
|
|
956
1106
|
);
|
|
957
|
-
}
|
|
958
|
-
|
|
959
|
-
if (selectedAgents.includes("codex")) {
|
|
960
1107
|
console.log(
|
|
961
|
-
`
|
|
1108
|
+
` 2. Create a card: ${colors.highlight('"Create a card called Auth token refresh"')}`,
|
|
962
1109
|
);
|
|
963
|
-
|
|
1110
|
+
console.log(
|
|
1111
|
+
` 3. Start the daemon: ${colors.highlight("npx @gethmy/agent")}`,
|
|
1112
|
+
);
|
|
1113
|
+
console.log("");
|
|
1114
|
+
console.log(` ${colors.dim("Happy shipping!")}`);
|
|
1115
|
+
} else {
|
|
1116
|
+
// Existing user: show config paths and usage
|
|
1117
|
+
console.log("");
|
|
1118
|
+
console.log(` ${colors.bold("Configuration:")}`);
|
|
1119
|
+
console.log(` API key: ${formatPath(getConfigPath(), home)}`);
|
|
1120
|
+
if (needsSkills && selectedAgents.length > 0) {
|
|
1121
|
+
console.log(
|
|
1122
|
+
` Skills: ${installMode === "global" ? "~/.agents/skills/ (global)" : ".claude/skills/ (local)"}`,
|
|
1123
|
+
);
|
|
1124
|
+
}
|
|
1125
|
+
if (selectedWorkspaceId || selectedProjectId) {
|
|
1126
|
+
console.log(
|
|
1127
|
+
` Context: ${formatPath(getLocalConfigPath(cwd), home)}`,
|
|
1128
|
+
);
|
|
1129
|
+
}
|
|
1130
|
+
|
|
1131
|
+
console.log("");
|
|
1132
|
+
console.log(` ${colors.bold("Usage:")}`);
|
|
1133
|
+
|
|
1134
|
+
if (!needsSkills || selectedAgents.includes("claude")) {
|
|
1135
|
+
console.log(
|
|
1136
|
+
` ${colors.brand("Claude Code:")} ${colors.highlight("/hmy #42")} or ${colors.highlight("/hmy-plan")} ${colors.dim("(create or execute plans)")}`,
|
|
1137
|
+
);
|
|
1138
|
+
}
|
|
964
1139
|
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
1140
|
+
if (selectedAgents.includes("codex")) {
|
|
1141
|
+
console.log(
|
|
1142
|
+
` ${colors.brand("Codex:")} ${colors.highlight("/prompts:hmy #42")}`,
|
|
1143
|
+
);
|
|
1144
|
+
}
|
|
1145
|
+
|
|
1146
|
+
if (
|
|
1147
|
+
selectedAgents.includes("cursor") ||
|
|
1148
|
+
selectedAgents.includes("windsurf")
|
|
1149
|
+
) {
|
|
1150
|
+
console.log(
|
|
1151
|
+
` ${colors.brand("Cursor:")} MCP tools available automatically`,
|
|
1152
|
+
);
|
|
1153
|
+
}
|
|
1154
|
+
|
|
1155
|
+
console.log("");
|
|
1156
|
+
console.log(` ${colors.dim("Add to new project: npx @gethmy/mcp setup")}`);
|
|
969
1157
|
console.log(
|
|
970
|
-
`
|
|
1158
|
+
` ${colors.dim("Need help? Visit https://app.gethmy.com/docs/mcp")}`,
|
|
971
1159
|
);
|
|
972
1160
|
}
|
|
973
|
-
|
|
974
|
-
console.log("");
|
|
975
|
-
console.log(` ${colors.dim("Add to new project: npx @gethmy/mcp setup")}`);
|
|
976
|
-
console.log(
|
|
977
|
-
` ${colors.dim("Need help? Visit https://app.gethmy.com/docs/mcp")}`,
|
|
978
|
-
);
|
|
979
1161
|
console.log("");
|
|
980
1162
|
}
|