@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/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@gethmy/mcp",
|
|
3
|
-
"version": "2.2.
|
|
3
|
+
"version": "2.2.3",
|
|
4
4
|
"description": "MCP server for Harmony Kanban board - enables AI coding agents to manage your boards",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
@@ -69,6 +69,7 @@
|
|
|
69
69
|
"zod": "^4.3.6"
|
|
70
70
|
},
|
|
71
71
|
"devDependencies": {
|
|
72
|
+
"@harmony/memory": "workspace:*",
|
|
72
73
|
"@types/node": "^25.5.0",
|
|
73
74
|
"typescript": "^6.0.1"
|
|
74
75
|
}
|
package/src/auto-session.ts
CHANGED
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* after 10 minutes of inactivity or when a different card is worked on.
|
|
7
7
|
*
|
|
8
8
|
* Agent identity is resolved from the MCP client's `initialize` handshake
|
|
9
|
-
* (clientInfo.name), so "Claude Code", "Cursor", "
|
|
9
|
+
* (clientInfo.name), so "Claude Code", "Cursor", "Codex", etc. are
|
|
10
10
|
* detected automatically — no hardcoded fallback needed.
|
|
11
11
|
*/
|
|
12
12
|
|
package/src/cli.ts
CHANGED
|
@@ -29,6 +29,11 @@ program
|
|
|
29
29
|
.command("serve")
|
|
30
30
|
.description("Start the MCP server (stdio transport)")
|
|
31
31
|
.action(async () => {
|
|
32
|
+
if (!isConfigured()) {
|
|
33
|
+
console.error("No API key configured.");
|
|
34
|
+
console.error("Run: npx @gethmy/mcp setup");
|
|
35
|
+
process.exit(1);
|
|
36
|
+
}
|
|
32
37
|
await refreshSkills();
|
|
33
38
|
const server = new HarmonyMCPServer();
|
|
34
39
|
await server.run();
|
|
@@ -147,6 +152,8 @@ program
|
|
|
147
152
|
.option("-p, --project <id>", "Set project context")
|
|
148
153
|
.option("--skip-context", "Skip workspace/project selection")
|
|
149
154
|
.option("--skip-docs", "Skip project docs scaffold/verification")
|
|
155
|
+
.option("--new", "Create a new account (skip the choice prompt)")
|
|
156
|
+
.option("-n, --name <name>", "Full name (for account creation)")
|
|
150
157
|
.action(async (options) => {
|
|
151
158
|
await runSetup({
|
|
152
159
|
force: options.force,
|
|
@@ -162,6 +169,8 @@ program
|
|
|
162
169
|
projectId: options.project,
|
|
163
170
|
skipContext: options.skipContext,
|
|
164
171
|
skipDocs: options.skipDocs,
|
|
172
|
+
newAccount: options.new,
|
|
173
|
+
name: options.name,
|
|
165
174
|
});
|
|
166
175
|
});
|
|
167
176
|
|
package/src/onboard.ts
ADDED
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
import { requestWithBearer, signupUser } from "./api-client.js";
|
|
2
|
+
import { getApiUrl } from "./config.js";
|
|
3
|
+
|
|
4
|
+
export interface OnboardParams {
|
|
5
|
+
email: string;
|
|
6
|
+
password: string;
|
|
7
|
+
fullName: string;
|
|
8
|
+
workspaceName?: string;
|
|
9
|
+
projectName?: string;
|
|
10
|
+
template?: string;
|
|
11
|
+
keyName?: string;
|
|
12
|
+
apiUrl?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface OnboardResult {
|
|
16
|
+
user: { id: string; email: string; full_name: string };
|
|
17
|
+
workspace: { id: string; name: string; slug: string; created_at: string };
|
|
18
|
+
project: { id: string; name: string; slug: string };
|
|
19
|
+
columns: unknown[];
|
|
20
|
+
apiKey: {
|
|
21
|
+
rawKey: string;
|
|
22
|
+
prefix: string;
|
|
23
|
+
};
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export async function onboardNewUser(
|
|
27
|
+
params: OnboardParams,
|
|
28
|
+
): Promise<OnboardResult> {
|
|
29
|
+
const {
|
|
30
|
+
email,
|
|
31
|
+
password,
|
|
32
|
+
fullName,
|
|
33
|
+
workspaceName = `${fullName}'s Workspace`,
|
|
34
|
+
projectName = "My First Board",
|
|
35
|
+
template = "kanban",
|
|
36
|
+
keyName = "mcp-agent",
|
|
37
|
+
apiUrl = getApiUrl(),
|
|
38
|
+
} = params;
|
|
39
|
+
|
|
40
|
+
// 1. Signup
|
|
41
|
+
const signupResult = await signupUser(apiUrl, {
|
|
42
|
+
email,
|
|
43
|
+
password,
|
|
44
|
+
full_name: fullName,
|
|
45
|
+
});
|
|
46
|
+
const token = signupResult.session.access_token;
|
|
47
|
+
|
|
48
|
+
// 2. Create workspace
|
|
49
|
+
const workspaceResult = await requestWithBearer<{
|
|
50
|
+
workspace: {
|
|
51
|
+
id: string;
|
|
52
|
+
name: string;
|
|
53
|
+
slug: string;
|
|
54
|
+
created_at: string;
|
|
55
|
+
};
|
|
56
|
+
}>(apiUrl, token, "POST", "/workspaces", {
|
|
57
|
+
name: workspaceName,
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
// 3. Create project
|
|
61
|
+
const projectResult = await requestWithBearer<{
|
|
62
|
+
project: { id: string; name: string; slug: string };
|
|
63
|
+
columns: unknown[];
|
|
64
|
+
}>(apiUrl, token, "POST", "/projects", {
|
|
65
|
+
workspaceId: workspaceResult.workspace.id,
|
|
66
|
+
name: projectName,
|
|
67
|
+
template,
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
// 4. Generate API key
|
|
71
|
+
const keyResult = await requestWithBearer<{
|
|
72
|
+
apiKey: {
|
|
73
|
+
id: string;
|
|
74
|
+
name: string;
|
|
75
|
+
prefix: string;
|
|
76
|
+
created_at: string;
|
|
77
|
+
};
|
|
78
|
+
rawKey: string;
|
|
79
|
+
}>(apiUrl, token, "POST", "/api-keys", {
|
|
80
|
+
name: keyName,
|
|
81
|
+
});
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
user: signupResult.user,
|
|
85
|
+
workspace: workspaceResult.workspace,
|
|
86
|
+
project: projectResult.project,
|
|
87
|
+
columns: projectResult.columns,
|
|
88
|
+
apiKey: {
|
|
89
|
+
rawKey: keyResult.rawKey,
|
|
90
|
+
prefix: keyResult.apiKey.prefix,
|
|
91
|
+
},
|
|
92
|
+
};
|
|
93
|
+
}
|
package/src/server.ts
CHANGED
|
@@ -23,7 +23,6 @@ import {
|
|
|
23
23
|
import {
|
|
24
24
|
getClient,
|
|
25
25
|
type HarmonyApiClient,
|
|
26
|
-
requestWithBearer,
|
|
27
26
|
resetClient,
|
|
28
27
|
signupUser,
|
|
29
28
|
} from "./api-client.js";
|
|
@@ -59,6 +58,7 @@ import {
|
|
|
59
58
|
} from "./context-assembly.js";
|
|
60
59
|
import { autoExpandGraph } from "./graph-expansion.js";
|
|
61
60
|
import { runLifecycleMaintenance } from "./lifecycle-maintenance.js";
|
|
61
|
+
import { onboardNewUser } from "./onboard.js";
|
|
62
62
|
import type { PromptVariant } from "./prompt-builder.js";
|
|
63
63
|
|
|
64
64
|
/**
|
|
@@ -273,18 +273,24 @@ const TOOLS = {
|
|
|
273
273
|
},
|
|
274
274
|
},
|
|
275
275
|
harmony_move_card: {
|
|
276
|
-
description:
|
|
276
|
+
description:
|
|
277
|
+
"Move a card to a different column or position. Provide either columnId (UUID) or columnName (e.g. 'Review', 'Done').",
|
|
277
278
|
inputSchema: {
|
|
278
279
|
type: "object",
|
|
279
280
|
properties: {
|
|
280
281
|
cardId: { type: "string", description: "Card ID to move" },
|
|
281
|
-
columnId: { type: "string", description: "Target column ID" },
|
|
282
|
+
columnId: { type: "string", description: "Target column ID (UUID)" },
|
|
283
|
+
columnName: {
|
|
284
|
+
type: "string",
|
|
285
|
+
description:
|
|
286
|
+
"Target column name (e.g. 'To Do', 'In Progress', 'Review', 'Done'). Used if columnId is not provided.",
|
|
287
|
+
},
|
|
282
288
|
position: {
|
|
283
289
|
type: "number",
|
|
284
290
|
description: "Position in column (0-indexed)",
|
|
285
291
|
},
|
|
286
292
|
},
|
|
287
|
-
required: ["cardId"
|
|
293
|
+
required: ["cardId"],
|
|
288
294
|
},
|
|
289
295
|
},
|
|
290
296
|
harmony_archive_card: {
|
|
@@ -420,14 +426,22 @@ const TOOLS = {
|
|
|
420
426
|
},
|
|
421
427
|
},
|
|
422
428
|
harmony_add_label_to_card: {
|
|
423
|
-
description:
|
|
429
|
+
description:
|
|
430
|
+
"Add a label to a card. Provide labelId directly, or labelName to look up (or auto-create) the label by name.",
|
|
424
431
|
inputSchema: {
|
|
425
432
|
type: "object",
|
|
426
433
|
properties: {
|
|
427
434
|
cardId: { type: "string" },
|
|
428
|
-
labelId: {
|
|
435
|
+
labelId: {
|
|
436
|
+
type: "string",
|
|
437
|
+
description: "Label ID (optional if labelName provided)",
|
|
438
|
+
},
|
|
439
|
+
labelName: {
|
|
440
|
+
type: "string",
|
|
441
|
+
description: "Label name — will look up or create if not found",
|
|
442
|
+
},
|
|
429
443
|
},
|
|
430
|
-
required: ["cardId"
|
|
444
|
+
required: ["cardId"],
|
|
431
445
|
},
|
|
432
446
|
},
|
|
433
447
|
harmony_remove_label_from_card: {
|
|
@@ -1971,6 +1985,27 @@ export function registerHandlers(server: Server, deps: ToolDeps): void {
|
|
|
1971
1985
|
});
|
|
1972
1986
|
}
|
|
1973
1987
|
|
|
1988
|
+
/** Resolve a column name to its ID. Prefers exact match, falls back to substring. */
|
|
1989
|
+
async function resolveColumnByName(
|
|
1990
|
+
client: HarmonyApiClient,
|
|
1991
|
+
projectId: string,
|
|
1992
|
+
columnName: string,
|
|
1993
|
+
): Promise<{ id: string; name: string }> {
|
|
1994
|
+
const board = await client.getBoard(projectId, { summary: true });
|
|
1995
|
+
const columns = board.columns as Array<{ id: string; name: string }>;
|
|
1996
|
+
const lower = columnName.toLowerCase();
|
|
1997
|
+
const col =
|
|
1998
|
+
columns.find((c) => c.name.toLowerCase() === lower) ||
|
|
1999
|
+
columns.find((c) => c.name.toLowerCase().includes(lower));
|
|
2000
|
+
if (!col) {
|
|
2001
|
+
const available = columns.map((c) => c.name).join(", ");
|
|
2002
|
+
throw new Error(
|
|
2003
|
+
`Column "${columnName}" not found. Available columns: ${available}`,
|
|
2004
|
+
);
|
|
2005
|
+
}
|
|
2006
|
+
return col;
|
|
2007
|
+
}
|
|
2008
|
+
|
|
1974
2009
|
async function handleToolCall(
|
|
1975
2010
|
name: string,
|
|
1976
2011
|
args: Record<string, unknown>,
|
|
@@ -2041,21 +2076,41 @@ async function handleToolCall(
|
|
|
2041
2076
|
|
|
2042
2077
|
case "harmony_move_card": {
|
|
2043
2078
|
const cardId = z.string().uuid().parse(args.cardId);
|
|
2044
|
-
const columnId = z.string().uuid().parse(args.columnId);
|
|
2045
2079
|
const position =
|
|
2046
2080
|
args.position !== undefined
|
|
2047
2081
|
? z.number().int().min(0).parse(args.position)
|
|
2048
2082
|
: undefined;
|
|
2083
|
+
|
|
2084
|
+
// Resolve columnId — accept UUID directly or resolve from columnName
|
|
2085
|
+
let columnId: string;
|
|
2086
|
+
let resolvedProjectId: string | undefined;
|
|
2087
|
+
if (args.columnId) {
|
|
2088
|
+
columnId = z.string().uuid().parse(args.columnId);
|
|
2089
|
+
} else if (args.columnName) {
|
|
2090
|
+
const columnName = z.string().parse(args.columnName);
|
|
2091
|
+
const { card: cardForProject } = await client.getCard(cardId);
|
|
2092
|
+
resolvedProjectId = (cardForProject as { project_id?: string })
|
|
2093
|
+
?.project_id;
|
|
2094
|
+
if (!resolvedProjectId) throw new Error("Card has no project");
|
|
2095
|
+
const col = await resolveColumnByName(
|
|
2096
|
+
client,
|
|
2097
|
+
resolvedProjectId,
|
|
2098
|
+
columnName,
|
|
2099
|
+
);
|
|
2100
|
+
columnId = col.id;
|
|
2101
|
+
} else {
|
|
2102
|
+
throw new Error("Either columnId or columnName is required");
|
|
2103
|
+
}
|
|
2104
|
+
|
|
2049
2105
|
const result = await client.moveCard(cardId, columnId, position);
|
|
2050
2106
|
|
|
2051
2107
|
// Auto-end active agent session when moving to Review or Done
|
|
2052
2108
|
let sessionEnded = false;
|
|
2053
2109
|
try {
|
|
2054
2110
|
const { card } = result as { card: { project_id?: string } };
|
|
2055
|
-
|
|
2056
|
-
|
|
2057
|
-
|
|
2058
|
-
});
|
|
2111
|
+
const projectId = card?.project_id || resolvedProjectId;
|
|
2112
|
+
if (projectId) {
|
|
2113
|
+
const board = await client.getBoard(projectId, { summary: true });
|
|
2059
2114
|
const columns = board.columns as Array<{
|
|
2060
2115
|
id: string;
|
|
2061
2116
|
name: string;
|
|
@@ -2178,7 +2233,47 @@ async function handleToolCall(
|
|
|
2178
2233
|
|
|
2179
2234
|
case "harmony_add_label_to_card": {
|
|
2180
2235
|
const cardId = z.string().uuid().parse(args.cardId);
|
|
2181
|
-
|
|
2236
|
+
let labelId = args.labelId
|
|
2237
|
+
? z.string().uuid().parse(args.labelId)
|
|
2238
|
+
: undefined;
|
|
2239
|
+
const labelName = args.labelName
|
|
2240
|
+
? z.string().min(1).max(100).parse(args.labelName)
|
|
2241
|
+
: undefined;
|
|
2242
|
+
|
|
2243
|
+
// If labelName provided without labelId, look up or create the label
|
|
2244
|
+
if (!labelId && labelName) {
|
|
2245
|
+
const { card } = await client.getCard(cardId);
|
|
2246
|
+
const projectId = (card as { project_id?: string }).project_id;
|
|
2247
|
+
if (!projectId) {
|
|
2248
|
+
throw new Error(
|
|
2249
|
+
"Cannot resolve label by name: card has no project_id",
|
|
2250
|
+
);
|
|
2251
|
+
}
|
|
2252
|
+
if (projectId) {
|
|
2253
|
+
const board = await client.getBoard(projectId, { summary: true });
|
|
2254
|
+
const labels = board.labels as Array<{
|
|
2255
|
+
id: string;
|
|
2256
|
+
name: string;
|
|
2257
|
+
}>;
|
|
2258
|
+
const existing = labels.find(
|
|
2259
|
+
(l) => l.name.toLowerCase() === labelName.toLowerCase(),
|
|
2260
|
+
);
|
|
2261
|
+
if (existing) {
|
|
2262
|
+
labelId = existing.id;
|
|
2263
|
+
} else {
|
|
2264
|
+
const created = await client.createLabel(projectId, {
|
|
2265
|
+
name: labelName,
|
|
2266
|
+
color: "#57b8a5",
|
|
2267
|
+
});
|
|
2268
|
+
labelId = (created as { label: { id: string } }).label.id;
|
|
2269
|
+
}
|
|
2270
|
+
}
|
|
2271
|
+
}
|
|
2272
|
+
|
|
2273
|
+
if (!labelId) {
|
|
2274
|
+
throw new Error("Either labelId or labelName must be provided");
|
|
2275
|
+
}
|
|
2276
|
+
|
|
2182
2277
|
await client.addLabelToCard(cardId, labelId);
|
|
2183
2278
|
return { success: true };
|
|
2184
2279
|
}
|
|
@@ -2363,12 +2458,20 @@ async function handleToolCall(
|
|
|
2363
2458
|
|
|
2364
2459
|
if (addLabels?.length) {
|
|
2365
2460
|
for (const labelName of addLabels) {
|
|
2366
|
-
|
|
2461
|
+
let label = labels.find(
|
|
2367
2462
|
(l) => l.name.toLowerCase() === labelName.toLowerCase(),
|
|
2368
2463
|
);
|
|
2464
|
+
if (!label && projectId) {
|
|
2465
|
+
const created = await client.createLabel(projectId, {
|
|
2466
|
+
name: labelName,
|
|
2467
|
+
color: "#57b8a5",
|
|
2468
|
+
});
|
|
2469
|
+
label = (created as { label: { id: string; name: string } })
|
|
2470
|
+
.label;
|
|
2471
|
+
}
|
|
2369
2472
|
if (label) {
|
|
2370
2473
|
await client.addLabelToCard(cardId, label.id);
|
|
2371
|
-
labelsAdded.push(label.name);
|
|
2474
|
+
labelsAdded.push(label.name ?? labelName);
|
|
2372
2475
|
}
|
|
2373
2476
|
}
|
|
2374
2477
|
}
|
|
@@ -2571,38 +2674,30 @@ async function handleToolCall(
|
|
|
2571
2674
|
await flushMemoryActions(client, cardId);
|
|
2572
2675
|
cleanupMemorySession(cardId);
|
|
2573
2676
|
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
|
|
2577
|
-
|
|
2677
|
+
// End the session — tolerate failure (e.g., session already ended or not found)
|
|
2678
|
+
let result: { session: unknown } = { session: null };
|
|
2679
|
+
let sessionEndError: string | null = null;
|
|
2680
|
+
try {
|
|
2681
|
+
result = await client.endAgentSession(cardId, {
|
|
2682
|
+
status: sessionStatus,
|
|
2683
|
+
progressPercent: endProgressPercent,
|
|
2684
|
+
});
|
|
2685
|
+
} catch (err) {
|
|
2686
|
+
sessionEndError =
|
|
2687
|
+
err instanceof Error ? err.message : "Failed to end session";
|
|
2688
|
+
}
|
|
2578
2689
|
|
|
2579
|
-
// Remove from auto-session tracking
|
|
2690
|
+
// Remove from auto-session tracking regardless
|
|
2580
2691
|
untrack(cardId);
|
|
2581
2692
|
|
|
2582
2693
|
let movedTo: string | null = null;
|
|
2583
|
-
const _learningsExtracted = 0;
|
|
2584
2694
|
|
|
2585
|
-
// Get card info for move and learning extraction
|
|
2586
|
-
let _cardTitle = "";
|
|
2587
|
-
let _cardLabels: string[] = [];
|
|
2588
|
-
let _cardDescription = "";
|
|
2589
|
-
let _cardSubtasks: Array<{ title: string; done: boolean }> = [];
|
|
2590
2695
|
try {
|
|
2591
2696
|
const { card } = await client.getCard(cardId);
|
|
2592
2697
|
const typedCard = card as {
|
|
2593
2698
|
project_id?: string;
|
|
2594
|
-
title?: string;
|
|
2595
|
-
description?: string;
|
|
2596
2699
|
labels?: Array<{ id: string; name: string }>;
|
|
2597
|
-
subtasks?: Array<{ title: string; done: boolean }>;
|
|
2598
2700
|
};
|
|
2599
|
-
_cardTitle = typedCard.title || "";
|
|
2600
|
-
_cardLabels = (typedCard.labels || []).map((l) => l.name);
|
|
2601
|
-
_cardDescription = typedCard.description || "";
|
|
2602
|
-
_cardSubtasks = (typedCard.subtasks || []).map((s) => ({
|
|
2603
|
-
title: s.title,
|
|
2604
|
-
done: s.done,
|
|
2605
|
-
}));
|
|
2606
2701
|
const projectId = typedCard.project_id;
|
|
2607
2702
|
|
|
2608
2703
|
// Remove "agent" label when session is completed (not paused)
|
|
@@ -2616,20 +2711,13 @@ async function handleToolCall(
|
|
|
2616
2711
|
}
|
|
2617
2712
|
|
|
2618
2713
|
if (moveToColumn && projectId) {
|
|
2619
|
-
const
|
|
2620
|
-
|
|
2621
|
-
|
|
2622
|
-
|
|
2623
|
-
id: string;
|
|
2624
|
-
name: string;
|
|
2625
|
-
}>;
|
|
2626
|
-
const col = columns.find((c) =>
|
|
2627
|
-
c.name.toLowerCase().includes(moveToColumn.toLowerCase()),
|
|
2714
|
+
const col = await resolveColumnByName(
|
|
2715
|
+
client,
|
|
2716
|
+
projectId,
|
|
2717
|
+
moveToColumn,
|
|
2628
2718
|
);
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
movedTo = col.name;
|
|
2632
|
-
}
|
|
2719
|
+
await client.moveCard(cardId, col.id);
|
|
2720
|
+
movedTo = col.name;
|
|
2633
2721
|
}
|
|
2634
2722
|
} catch {
|
|
2635
2723
|
// Card fetch/move failed, continue
|
|
@@ -2655,6 +2743,7 @@ async function handleToolCall(
|
|
|
2655
2743
|
|
|
2656
2744
|
return {
|
|
2657
2745
|
success: true,
|
|
2746
|
+
...(sessionEndError && { sessionEndError }),
|
|
2658
2747
|
movedTo,
|
|
2659
2748
|
learningsExtracted: pipelineResult.learningsExtracted,
|
|
2660
2749
|
feedbackAdjusted: pipelineResult.feedbackAdjusted,
|
|
@@ -3773,69 +3862,31 @@ async function handleToolCall(
|
|
|
3773
3862
|
const projectName = (args.projectName as string) || "My First Project";
|
|
3774
3863
|
const template = (args.template as string) || "kanban";
|
|
3775
3864
|
const keyName = (args.keyName as string) || "mcp-agent";
|
|
3776
|
-
const apiUrl = deps.getApiUrl();
|
|
3777
3865
|
|
|
3778
|
-
|
|
3779
|
-
const signupResult = await signupUser(apiUrl, {
|
|
3866
|
+
const result = await onboardNewUser({
|
|
3780
3867
|
email,
|
|
3781
3868
|
password,
|
|
3782
|
-
|
|
3783
|
-
|
|
3784
|
-
|
|
3785
|
-
|
|
3786
|
-
// 2. Create workspace
|
|
3787
|
-
const workspaceResult = await requestWithBearer<{
|
|
3788
|
-
workspace: {
|
|
3789
|
-
id: string;
|
|
3790
|
-
name: string;
|
|
3791
|
-
slug: string;
|
|
3792
|
-
created_at: string;
|
|
3793
|
-
};
|
|
3794
|
-
}>(apiUrl, token, "POST", "/workspaces", {
|
|
3795
|
-
name: workspaceName,
|
|
3796
|
-
});
|
|
3797
|
-
|
|
3798
|
-
// 3. Create project
|
|
3799
|
-
const projectResult = await requestWithBearer<{
|
|
3800
|
-
project: { id: string; name: string; slug: string };
|
|
3801
|
-
columns: unknown[];
|
|
3802
|
-
}>(apiUrl, token, "POST", "/projects", {
|
|
3803
|
-
workspaceId: workspaceResult.workspace.id,
|
|
3804
|
-
name: projectName,
|
|
3869
|
+
fullName,
|
|
3870
|
+
workspaceName,
|
|
3871
|
+
projectName,
|
|
3805
3872
|
template,
|
|
3873
|
+
keyName,
|
|
3874
|
+
apiUrl: deps.getApiUrl(),
|
|
3806
3875
|
});
|
|
3807
3876
|
|
|
3808
|
-
//
|
|
3809
|
-
|
|
3810
|
-
|
|
3811
|
-
|
|
3812
|
-
name: string;
|
|
3813
|
-
prefix: string;
|
|
3814
|
-
created_at: string;
|
|
3815
|
-
};
|
|
3816
|
-
rawKey: string;
|
|
3817
|
-
}>(apiUrl, token, "POST", "/api-keys", {
|
|
3818
|
-
name: keyName,
|
|
3819
|
-
});
|
|
3820
|
-
|
|
3821
|
-
// 5. Save config
|
|
3822
|
-
deps.saveConfig({ apiKey: keyResult.rawKey });
|
|
3823
|
-
deps.setActiveWorkspace(workspaceResult.workspace.id);
|
|
3824
|
-
deps.setActiveProject(projectResult.project.id);
|
|
3825
|
-
|
|
3826
|
-
// 6. Reset client so singleton picks up new key
|
|
3877
|
+
// Save config and reset client
|
|
3878
|
+
deps.saveConfig({ apiKey: result.apiKey.rawKey });
|
|
3879
|
+
deps.setActiveWorkspace(result.workspace.id);
|
|
3880
|
+
deps.setActiveProject(result.project.id);
|
|
3827
3881
|
deps.resetClient();
|
|
3828
3882
|
|
|
3829
3883
|
return {
|
|
3830
3884
|
success: true,
|
|
3831
|
-
user:
|
|
3832
|
-
workspace:
|
|
3833
|
-
project:
|
|
3834
|
-
columns:
|
|
3835
|
-
apiKey:
|
|
3836
|
-
rawKey: keyResult.rawKey,
|
|
3837
|
-
prefix: keyResult.apiKey.prefix,
|
|
3838
|
-
},
|
|
3885
|
+
user: result.user,
|
|
3886
|
+
workspace: result.workspace,
|
|
3887
|
+
project: result.project,
|
|
3888
|
+
columns: result.columns,
|
|
3889
|
+
apiKey: result.apiKey,
|
|
3839
3890
|
message: `Onboarding complete! Account created for ${email}. Workspace "${workspaceName}" and project "${projectName}" are ready. API key saved to config.`,
|
|
3840
3891
|
};
|
|
3841
3892
|
}
|
package/src/skills.ts
CHANGED
|
@@ -7,7 +7,7 @@ export const SKILLS_VERSION = "3";
|
|
|
7
7
|
const VERSION_MARKER_PREFIX = "<!-- skills-version:";
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
|
-
* Legacy workflow prompt used by Codex, Cursor
|
|
10
|
+
* Legacy workflow prompt used by Codex, Cursor agents.
|
|
11
11
|
* Claude Code skills use the newer SKILL_DEFINITIONS content instead.
|
|
12
12
|
*/
|
|
13
13
|
export const HARMONY_WORKFLOW_PROMPT = `# Harmony Card Workflow
|