@gethmy/mcp 2.23.0 → 2.24.0
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/dist/cli.js +207 -67
- package/dist/index.js +174 -44
- package/dist/lib/api-client.js +78 -26
- package/dist/lib/config.js +79 -24
- package/dist/lib/oauth-refresh.js +76 -24
- package/package.json +1 -1
- package/src/api-client.ts +6 -0
- package/src/cli.ts +21 -12
- package/src/config.ts +201 -27
- package/src/remote.ts +34 -6
- package/src/server.ts +154 -12
- package/src/tui/setup.ts +21 -6
package/dist/cli.js
CHANGED
|
@@ -20,7 +20,7 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
|
20
20
|
// src/config.ts
|
|
21
21
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
22
22
|
import { homedir } from "node:os";
|
|
23
|
-
import { join } from "node:path";
|
|
23
|
+
import { dirname, join, parse, resolve } from "node:path";
|
|
24
24
|
function getConfigDir() {
|
|
25
25
|
return join(homedir(), ".harmony-mcp");
|
|
26
26
|
}
|
|
@@ -30,6 +30,22 @@ function getConfigPath() {
|
|
|
30
30
|
function getLocalConfigPath(cwd) {
|
|
31
31
|
return join(cwd || process.cwd(), LOCAL_CONFIG_FILENAME);
|
|
32
32
|
}
|
|
33
|
+
function findLocalConfigPath(cwd) {
|
|
34
|
+
const home = resolve(homedir());
|
|
35
|
+
let dir = resolve(cwd || process.cwd());
|
|
36
|
+
const { root } = parse(dir);
|
|
37
|
+
for (;; ) {
|
|
38
|
+
if (dir !== home && dir !== root) {
|
|
39
|
+
const candidate = join(dir, LOCAL_CONFIG_FILENAME);
|
|
40
|
+
if (existsSync(candidate))
|
|
41
|
+
return candidate;
|
|
42
|
+
}
|
|
43
|
+
const parent = dirname(dir);
|
|
44
|
+
if (parent === dir)
|
|
45
|
+
return null;
|
|
46
|
+
dir = parent;
|
|
47
|
+
}
|
|
48
|
+
}
|
|
33
49
|
function emptyConfig() {
|
|
34
50
|
return {
|
|
35
51
|
apiKey: null,
|
|
@@ -81,8 +97,8 @@ function saveConfig(config) {
|
|
|
81
97
|
});
|
|
82
98
|
}
|
|
83
99
|
function loadLocalConfig(cwd) {
|
|
84
|
-
const localConfigPath =
|
|
85
|
-
if (
|
|
100
|
+
const localConfigPath = findLocalConfigPath(cwd);
|
|
101
|
+
if (localConfigPath === null) {
|
|
86
102
|
return null;
|
|
87
103
|
}
|
|
88
104
|
try {
|
|
@@ -97,7 +113,7 @@ function loadLocalConfig(cwd) {
|
|
|
97
113
|
}
|
|
98
114
|
}
|
|
99
115
|
function saveLocalConfig(config, cwd) {
|
|
100
|
-
const localConfigPath = getLocalConfigPath(cwd);
|
|
116
|
+
const localConfigPath = findLocalConfigPath(cwd) ?? getLocalConfigPath(cwd);
|
|
101
117
|
const existingConfig = loadLocalConfig(cwd) || {
|
|
102
118
|
workspaceId: null,
|
|
103
119
|
projectId: null
|
|
@@ -111,7 +127,7 @@ function saveLocalConfig(config, cwd) {
|
|
|
111
127
|
writeFileSync(localConfigPath, JSON.stringify(cleanConfig, null, 2));
|
|
112
128
|
}
|
|
113
129
|
function hasLocalConfig(cwd) {
|
|
114
|
-
return
|
|
130
|
+
return findLocalConfigPath(cwd) !== null;
|
|
115
131
|
}
|
|
116
132
|
function getActiveCredential() {
|
|
117
133
|
const config = loadConfig();
|
|
@@ -133,33 +149,69 @@ function getUserEmail() {
|
|
|
133
149
|
const config = loadConfig();
|
|
134
150
|
return config.userEmail;
|
|
135
151
|
}
|
|
136
|
-
function
|
|
137
|
-
if (options?.
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
152
|
+
function setActiveContext(context, options) {
|
|
153
|
+
if (options?.global) {
|
|
154
|
+
saveConfig({
|
|
155
|
+
activeWorkspaceId: context.workspaceId,
|
|
156
|
+
activeProjectId: context.projectId
|
|
157
|
+
});
|
|
158
|
+
return;
|
|
141
159
|
}
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
saveLocalConfig({ projectId }, options.cwd);
|
|
160
|
+
const localPath = findLocalConfigPath(options?.cwd);
|
|
161
|
+
if (options?.local || localPath !== null) {
|
|
162
|
+
saveLocalConfig({ workspaceId: context.workspaceId, projectId: context.projectId }, options?.cwd);
|
|
146
163
|
} else {
|
|
147
|
-
saveConfig({
|
|
164
|
+
saveConfig({
|
|
165
|
+
activeWorkspaceId: context.workspaceId,
|
|
166
|
+
activeProjectId: context.projectId
|
|
167
|
+
});
|
|
148
168
|
}
|
|
149
169
|
}
|
|
150
|
-
function
|
|
170
|
+
function setActiveWorkspace(workspaceId, options) {
|
|
171
|
+
const currentWorkspaceId = getActiveWorkspaceId(options?.cwd);
|
|
172
|
+
const keepProject = currentWorkspaceId === workspaceId;
|
|
173
|
+
setActiveContext({
|
|
174
|
+
workspaceId,
|
|
175
|
+
projectId: keepProject ? getActiveProjectId(options?.cwd) : null
|
|
176
|
+
}, options);
|
|
177
|
+
}
|
|
178
|
+
function readActiveContext(cwd) {
|
|
151
179
|
const localConfig = loadLocalConfig(cwd);
|
|
152
|
-
if (localConfig
|
|
153
|
-
return
|
|
180
|
+
if (localConfig) {
|
|
181
|
+
return {
|
|
182
|
+
workspaceId: localConfig.workspaceId ?? null,
|
|
183
|
+
projectId: localConfig.projectId ?? null
|
|
184
|
+
};
|
|
154
185
|
}
|
|
155
|
-
|
|
186
|
+
const globalConfig = loadConfig();
|
|
187
|
+
return {
|
|
188
|
+
workspaceId: globalConfig.activeWorkspaceId,
|
|
189
|
+
projectId: globalConfig.activeProjectId
|
|
190
|
+
};
|
|
191
|
+
}
|
|
192
|
+
function getActiveWorkspaceId(cwd) {
|
|
193
|
+
return readActiveContext(cwd).workspaceId;
|
|
156
194
|
}
|
|
157
195
|
function getActiveProjectId(cwd) {
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
196
|
+
return readActiveContext(cwd).projectId;
|
|
197
|
+
}
|
|
198
|
+
function getActiveContext(cwd) {
|
|
199
|
+
return describeActiveContext({
|
|
200
|
+
projectId: getActiveProjectId(cwd),
|
|
201
|
+
workspaceId: getActiveWorkspaceId(cwd)
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
function describeActiveContext(context) {
|
|
205
|
+
const { projectId, workspaceId } = context;
|
|
206
|
+
if (projectId && !workspaceId) {
|
|
207
|
+
return {
|
|
208
|
+
projectId,
|
|
209
|
+
workspaceId,
|
|
210
|
+
consistent: false,
|
|
211
|
+
note: `An active project (${projectId}) is set with no active workspace, so ` + "workspace-scoped tools cannot resolve one from it. Re-set it with " + "harmony_set_project_context, or pass workspaceId explicitly."
|
|
212
|
+
};
|
|
161
213
|
}
|
|
162
|
-
return
|
|
214
|
+
return { projectId, workspaceId, consistent: true, note: null };
|
|
163
215
|
}
|
|
164
216
|
function isConfigured() {
|
|
165
217
|
const config = loadConfig();
|
|
@@ -878,7 +930,7 @@ function lockPath() {
|
|
|
878
930
|
return join3(getConfigDir(), LOCK_FILENAME);
|
|
879
931
|
}
|
|
880
932
|
function sleep(ms) {
|
|
881
|
-
return new Promise((
|
|
933
|
+
return new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
882
934
|
}
|
|
883
935
|
async function withRefreshLock(fn) {
|
|
884
936
|
const path = lockPath();
|
|
@@ -1546,7 +1598,7 @@ function getRetryDelay(attempt) {
|
|
|
1546
1598
|
const delay = Math.min(RETRY_CONFIG.baseDelayMs * 2 ** attempt, RETRY_CONFIG.maxDelayMs);
|
|
1547
1599
|
return Math.round(delay + delay * 0.25 * (Math.random() * 2 - 1));
|
|
1548
1600
|
}
|
|
1549
|
-
var sleep2 = (ms) => new Promise((
|
|
1601
|
+
var sleep2 = (ms) => new Promise((resolve2) => setTimeout(resolve2, ms));
|
|
1550
1602
|
|
|
1551
1603
|
class Semaphore {
|
|
1552
1604
|
permits;
|
|
@@ -1559,7 +1611,7 @@ class Semaphore {
|
|
|
1559
1611
|
this.permits--;
|
|
1560
1612
|
return;
|
|
1561
1613
|
}
|
|
1562
|
-
return new Promise((
|
|
1614
|
+
return new Promise((resolve2) => this.queue.push(resolve2));
|
|
1563
1615
|
}
|
|
1564
1616
|
release() {
|
|
1565
1617
|
const next = this.queue.shift();
|
|
@@ -2705,7 +2757,7 @@ async function autoExpandGraph(client3, entityId, title, content, _tags, workspa
|
|
|
2705
2757
|
});
|
|
2706
2758
|
candidates = entities.filter((e) => e.id !== entityId && (e.confidence ?? 1) >= 0.4).slice(0, maxRelations);
|
|
2707
2759
|
if (candidates.length === 0) {
|
|
2708
|
-
await new Promise((
|
|
2760
|
+
await new Promise((resolve2) => setTimeout(resolve2, 2000));
|
|
2709
2761
|
const retry = await client3.searchMemoryEntities(workspaceId, query, {
|
|
2710
2762
|
project_id: projectId,
|
|
2711
2763
|
limit: 20
|
|
@@ -3297,7 +3349,7 @@ import {
|
|
|
3297
3349
|
writeFileSync as writeFileSync3
|
|
3298
3350
|
} from "node:fs";
|
|
3299
3351
|
import { homedir as homedir3 } from "node:os";
|
|
3300
|
-
import { dirname, join as join5 } from "node:path";
|
|
3352
|
+
import { dirname as dirname2, join as join5 } from "node:path";
|
|
3301
3353
|
init_config();
|
|
3302
3354
|
|
|
3303
3355
|
// src/hmy-config.ts
|
|
@@ -3444,7 +3496,7 @@ function stripSkillPreamble(content) {
|
|
|
3444
3496
|
`;
|
|
3445
3497
|
}
|
|
3446
3498
|
function atomicWrite(filePath, content) {
|
|
3447
|
-
const dir =
|
|
3499
|
+
const dir = dirname2(filePath);
|
|
3448
3500
|
if (!existsSync4(dir))
|
|
3449
3501
|
mkdirSync3(dir, { recursive: true });
|
|
3450
3502
|
const tmp = `${filePath}.tmp-${process.pid}-${Date.now()}`;
|
|
@@ -3535,10 +3587,10 @@ async function refreshSkills(opts = {}) {
|
|
|
3535
3587
|
continue;
|
|
3536
3588
|
let siblingPath;
|
|
3537
3589
|
if (samplePath.endsWith("SKILL.md")) {
|
|
3538
|
-
const parentDir =
|
|
3590
|
+
const parentDir = dirname2(dirname2(samplePath));
|
|
3539
3591
|
siblingPath = `${parentDir}/${name}/SKILL.md`;
|
|
3540
3592
|
} else {
|
|
3541
|
-
const parentDir =
|
|
3593
|
+
const parentDir = dirname2(samplePath);
|
|
3542
3594
|
siblingPath = `${parentDir}/${name}.md`;
|
|
3543
3595
|
}
|
|
3544
3596
|
if (existsSync4(siblingPath)) {
|
|
@@ -4586,11 +4638,15 @@ var TOOLS = {
|
|
|
4586
4638
|
}
|
|
4587
4639
|
},
|
|
4588
4640
|
harmony_set_project_context: {
|
|
4589
|
-
description: "Set the active project context for subsequent operations",
|
|
4641
|
+
description: "Set the active project context for subsequent operations. The project's workspace is set with it, so the two can never point at different places; pass workspaceId to skip the lookup.",
|
|
4590
4642
|
inputSchema: {
|
|
4591
4643
|
type: "object",
|
|
4592
4644
|
properties: {
|
|
4593
|
-
projectId: { type: "string" }
|
|
4645
|
+
projectId: { type: "string" },
|
|
4646
|
+
workspaceId: {
|
|
4647
|
+
type: "string",
|
|
4648
|
+
description: "The workspace this project belongs to. Optional — looked up when omitted."
|
|
4649
|
+
}
|
|
4594
4650
|
},
|
|
4595
4651
|
required: ["projectId"]
|
|
4596
4652
|
}
|
|
@@ -5394,13 +5450,26 @@ var TOOLS = {
|
|
|
5394
5450
|
type: "array",
|
|
5395
5451
|
description: "The playbook's ordered stage objects.",
|
|
5396
5452
|
items: { type: "object" }
|
|
5453
|
+
},
|
|
5454
|
+
triggerType: {
|
|
5455
|
+
type: "string",
|
|
5456
|
+
enum: ["manual", "auto"],
|
|
5457
|
+
description: "'manual' (default) — the playbook is applied by a person. 'auto' — it claims matching cards itself, and requires autoBind."
|
|
5458
|
+
},
|
|
5459
|
+
autoBind: {
|
|
5460
|
+
type: "object",
|
|
5461
|
+
description: "Auto-bind rule: {priority?: number, mode?: 'all'|'any', when: [{path, op, value}]}. Conditions are evaluated against the card's labels (lowercased), intent, complexity_score and priority with the gate operators eq/neq/gte/gt/lte/lt/contains/exists; 'contains' on labels is membership. Stored even while triggerType is 'manual', so a rule can be armed later without re-authoring it."
|
|
5462
|
+
},
|
|
5463
|
+
catalogId: {
|
|
5464
|
+
type: "string",
|
|
5465
|
+
description: "Slug of the built-in template this came from (provenance only; never used to match)."
|
|
5397
5466
|
}
|
|
5398
5467
|
},
|
|
5399
5468
|
required: ["name"]
|
|
5400
5469
|
}
|
|
5401
5470
|
},
|
|
5402
5471
|
harmony_update_playbook: {
|
|
5403
|
-
description: "Update a playbook's name, description, steps/stages, enabled flag,
|
|
5472
|
+
description: "Update a playbook's name, description, steps/stages, enabled flag, lifecycle state ('active'|'deprecated'), or its auto-bind rule and arming.",
|
|
5404
5473
|
inputSchema: {
|
|
5405
5474
|
type: "object",
|
|
5406
5475
|
properties: {
|
|
@@ -5423,6 +5492,15 @@ var TOOLS = {
|
|
|
5423
5492
|
type: "string",
|
|
5424
5493
|
enum: ["active", "deprecated"],
|
|
5425
5494
|
description: "Lifecycle state"
|
|
5495
|
+
},
|
|
5496
|
+
triggerType: {
|
|
5497
|
+
type: "string",
|
|
5498
|
+
enum: ["manual", "auto"],
|
|
5499
|
+
description: "Arm ('auto') or disarm ('manual') automatic application. Arming requires a rule to be present or supplied in the same call."
|
|
5500
|
+
},
|
|
5501
|
+
autoBind: {
|
|
5502
|
+
type: "object",
|
|
5503
|
+
description: "Replace the auto-bind rule: {priority?, mode?: 'all'|'any', when: [{path, op, value}]}. Pass null to remove it."
|
|
5426
5504
|
}
|
|
5427
5505
|
},
|
|
5428
5506
|
required: ["playbookId"]
|
|
@@ -5887,7 +5965,10 @@ async function handleToolCall(name, args, deps) {
|
|
|
5887
5965
|
const where = resolved.project.workspaceName ? `project "${resolved.project.name ?? resolved.project.id}" (workspace "${resolved.project.workspaceName}")` : `project "${resolved.project.name ?? resolved.project.id}"`;
|
|
5888
5966
|
const established = activeProjectId == null;
|
|
5889
5967
|
if (established) {
|
|
5890
|
-
deps.
|
|
5968
|
+
deps.setActiveContext({
|
|
5969
|
+
projectId: resolved.project.id,
|
|
5970
|
+
workspaceId: resolved.project.workspaceId
|
|
5971
|
+
});
|
|
5891
5972
|
}
|
|
5892
5973
|
return {
|
|
5893
5974
|
success: true,
|
|
@@ -6326,15 +6407,57 @@ ${options}
|
|
|
6326
6407
|
}
|
|
6327
6408
|
case "harmony_set_project_context": {
|
|
6328
6409
|
const projectId = z.string().uuid().parse(args.projectId);
|
|
6329
|
-
|
|
6330
|
-
|
|
6410
|
+
const explicitWorkspaceId = args.workspaceId ? z.string().uuid().parse(args.workspaceId) : null;
|
|
6411
|
+
let owningWorkspaceId = explicitWorkspaceId;
|
|
6412
|
+
if (!owningWorkspaceId) {
|
|
6413
|
+
try {
|
|
6414
|
+
const { workspaces } = await client3.listWorkspaces();
|
|
6415
|
+
for (const workspace of workspaces) {
|
|
6416
|
+
if (!workspace?.id)
|
|
6417
|
+
continue;
|
|
6418
|
+
const { projects } = await client3.listProjects(workspace.id);
|
|
6419
|
+
if (projects.some((p) => p?.id === projectId)) {
|
|
6420
|
+
owningWorkspaceId = workspace.id;
|
|
6421
|
+
break;
|
|
6422
|
+
}
|
|
6423
|
+
}
|
|
6424
|
+
} catch (error) {
|
|
6425
|
+
const reason = error instanceof Error ? error.message : String(error);
|
|
6426
|
+
return {
|
|
6427
|
+
success: false,
|
|
6428
|
+
activeProjectId: deps.getActiveProjectId(),
|
|
6429
|
+
activeWorkspaceId: deps.getActiveWorkspaceId(),
|
|
6430
|
+
note: `Could not resolve this project's workspace (${reason}), so the ` + `active context was left unchanged rather than half-written. ` + `Retry, or pass workspaceId explicitly to skip the lookup.`
|
|
6431
|
+
};
|
|
6432
|
+
}
|
|
6433
|
+
if (!owningWorkspaceId) {
|
|
6434
|
+
return {
|
|
6435
|
+
success: false,
|
|
6436
|
+
activeProjectId: deps.getActiveProjectId(),
|
|
6437
|
+
activeWorkspaceId: deps.getActiveWorkspaceId(),
|
|
6438
|
+
note: `Project ${projectId} is not in any workspace this connection ` + `can reach, so the active context was left unchanged. Check ` + `harmony_list_projects, pass workspaceId explicitly, or ` + `reconnect with /mcp if it lives in another workspace.`
|
|
6439
|
+
};
|
|
6440
|
+
}
|
|
6441
|
+
}
|
|
6442
|
+
deps.setActiveContext({ projectId, workspaceId: owningWorkspaceId });
|
|
6443
|
+
return {
|
|
6444
|
+
success: true,
|
|
6445
|
+
activeProjectId: projectId,
|
|
6446
|
+
activeWorkspaceId: owningWorkspaceId
|
|
6447
|
+
};
|
|
6331
6448
|
}
|
|
6332
6449
|
case "harmony_get_context": {
|
|
6450
|
+
const report = describeActiveContext({
|
|
6451
|
+
projectId: deps.getActiveProjectId(),
|
|
6452
|
+
workspaceId: deps.getActiveWorkspaceId()
|
|
6453
|
+
});
|
|
6333
6454
|
return {
|
|
6334
6455
|
success: true,
|
|
6335
6456
|
context: {
|
|
6336
|
-
activeWorkspaceId:
|
|
6337
|
-
activeProjectId:
|
|
6457
|
+
activeWorkspaceId: report.workspaceId,
|
|
6458
|
+
activeProjectId: report.projectId,
|
|
6459
|
+
consistent: report.consistent,
|
|
6460
|
+
...report.note ? { note: report.note } : {}
|
|
6338
6461
|
}
|
|
6339
6462
|
};
|
|
6340
6463
|
}
|
|
@@ -7183,7 +7306,10 @@ ${options}
|
|
|
7183
7306
|
workspaceId,
|
|
7184
7307
|
name: name2,
|
|
7185
7308
|
description: args.description,
|
|
7186
|
-
steps: args.steps
|
|
7309
|
+
steps: args.steps,
|
|
7310
|
+
triggerType: args.triggerType,
|
|
7311
|
+
autoBind: args.autoBind,
|
|
7312
|
+
catalogId: args.catalogId
|
|
7187
7313
|
});
|
|
7188
7314
|
return { success: true, playbook: result.playbook };
|
|
7189
7315
|
}
|
|
@@ -7194,7 +7320,9 @@ ${options}
|
|
|
7194
7320
|
description: args.description,
|
|
7195
7321
|
steps: args.steps,
|
|
7196
7322
|
enabled: args.enabled,
|
|
7197
|
-
state: args.state
|
|
7323
|
+
state: args.state,
|
|
7324
|
+
triggerType: args.triggerType,
|
|
7325
|
+
..."autoBind" in args ? { autoBind: args.autoBind } : {}
|
|
7198
7326
|
});
|
|
7199
7327
|
return { success: true, playbook: result.playbook };
|
|
7200
7328
|
}
|
|
@@ -7271,8 +7399,10 @@ ${options}
|
|
|
7271
7399
|
apiUrl: deps.getApiUrl()
|
|
7272
7400
|
});
|
|
7273
7401
|
deps.saveConfig({ apiKey: result.apiKey.rawKey });
|
|
7274
|
-
deps.
|
|
7275
|
-
|
|
7402
|
+
deps.setActiveContext({
|
|
7403
|
+
projectId: result.project.id,
|
|
7404
|
+
workspaceId: result.workspace.id
|
|
7405
|
+
});
|
|
7276
7406
|
deps.resetClient();
|
|
7277
7407
|
return {
|
|
7278
7408
|
success: true,
|
|
@@ -7294,7 +7424,7 @@ function createConfigDeps() {
|
|
|
7294
7424
|
isConfigured,
|
|
7295
7425
|
getActiveProjectId: () => getActiveProjectId(),
|
|
7296
7426
|
getActiveWorkspaceId: () => getActiveWorkspaceId(),
|
|
7297
|
-
|
|
7427
|
+
setActiveContext: (context) => setActiveContext(context),
|
|
7298
7428
|
setActiveWorkspace: (id) => setActiveWorkspace(id),
|
|
7299
7429
|
getApiUrl,
|
|
7300
7430
|
getMemoryDir: () => getMemoryDir(),
|
|
@@ -7373,7 +7503,7 @@ import {
|
|
|
7373
7503
|
unlinkSync
|
|
7374
7504
|
} from "node:fs";
|
|
7375
7505
|
import { homedir as homedir6 } from "node:os";
|
|
7376
|
-
import { dirname as
|
|
7506
|
+
import { dirname as dirname4, join as join8 } from "node:path";
|
|
7377
7507
|
import * as p4 from "@clack/prompts";
|
|
7378
7508
|
init_config();
|
|
7379
7509
|
init_oauth_login();
|
|
@@ -7445,7 +7575,7 @@ async function confirmOrDefault(assumeYes, opts) {
|
|
|
7445
7575
|
|
|
7446
7576
|
// src/tui/docs.ts
|
|
7447
7577
|
import { existsSync as existsSync6, readdirSync as readdirSync2, readFileSync as readFileSync5, statSync as statSync2 } from "node:fs";
|
|
7448
|
-
import { isAbsolute, join as join7, resolve, sep as sep2 } from "node:path";
|
|
7578
|
+
import { isAbsolute, join as join7, resolve as resolve2, sep as sep2 } from "node:path";
|
|
7449
7579
|
import * as p2 from "@clack/prompts";
|
|
7450
7580
|
|
|
7451
7581
|
// src/tui/theme.ts
|
|
@@ -7869,7 +7999,7 @@ function verifyDocs(cwd) {
|
|
|
7869
7999
|
const agentsMd = readText(join7(cwd, "AGENTS.md"));
|
|
7870
8000
|
const pkg = readJson(join7(cwd, "package.json"));
|
|
7871
8001
|
const pkgScripts = pkg && typeof pkg.scripts === "object" && pkg.scripts !== null ? pkg.scripts : {};
|
|
7872
|
-
const projectRoot =
|
|
8002
|
+
const projectRoot = resolve2(cwd);
|
|
7873
8003
|
if (claudeMd) {
|
|
7874
8004
|
const importedFiles = [];
|
|
7875
8005
|
for (const line of claudeMd.split(`
|
|
@@ -7886,7 +8016,7 @@ function verifyDocs(cwd) {
|
|
|
7886
8016
|
});
|
|
7887
8017
|
continue;
|
|
7888
8018
|
}
|
|
7889
|
-
const resolvedPath =
|
|
8019
|
+
const resolvedPath = resolve2(projectRoot, refPath);
|
|
7890
8020
|
if (resolvedPath !== projectRoot && !resolvedPath.startsWith(projectRoot + sep2)) {
|
|
7891
8021
|
issues.push({
|
|
7892
8022
|
severity: "error",
|
|
@@ -8071,13 +8201,13 @@ function checkBacktickPaths(content, file, cwd, issues) {
|
|
|
8071
8201
|
const pathRe = /`((?:src\/|packages\/|apps\/|supabase\/|docs\/)[^`]+)`/g;
|
|
8072
8202
|
let match;
|
|
8073
8203
|
const checked = new Set;
|
|
8074
|
-
const root =
|
|
8204
|
+
const root = resolve2(cwd);
|
|
8075
8205
|
while ((match = pathRe.exec(content)) !== null) {
|
|
8076
8206
|
const refPath = match[1].replace(/\/$/, "");
|
|
8077
8207
|
if (checked.has(refPath))
|
|
8078
8208
|
continue;
|
|
8079
8209
|
checked.add(refPath);
|
|
8080
|
-
const resolvedRef =
|
|
8210
|
+
const resolvedRef = resolve2(root, refPath);
|
|
8081
8211
|
if (resolvedRef !== root && !resolvedRef.startsWith(root + sep2))
|
|
8082
8212
|
continue;
|
|
8083
8213
|
if (!existsSync6(resolvedRef)) {
|
|
@@ -8158,7 +8288,7 @@ import {
|
|
|
8158
8288
|
writeFileSync as writeFileSync4
|
|
8159
8289
|
} from "node:fs";
|
|
8160
8290
|
import { homedir as homedir5 } from "node:os";
|
|
8161
|
-
import { dirname as
|
|
8291
|
+
import { dirname as dirname3 } from "node:path";
|
|
8162
8292
|
import * as p3 from "@clack/prompts";
|
|
8163
8293
|
function ensureDir(dirPath) {
|
|
8164
8294
|
if (!existsSync7(dirPath)) {
|
|
@@ -8171,7 +8301,7 @@ function writeFile(filePath, content, options = {}) {
|
|
|
8171
8301
|
return { path: filePath, action: "skip" };
|
|
8172
8302
|
}
|
|
8173
8303
|
try {
|
|
8174
|
-
ensureDir(
|
|
8304
|
+
ensureDir(dirname3(filePath));
|
|
8175
8305
|
const defaultMode = filePath.includes(".harmony-mcp") ? 384 : 420;
|
|
8176
8306
|
const mode = options.mode ?? defaultMode;
|
|
8177
8307
|
writeFileSync4(filePath, content, { mode });
|
|
@@ -8191,7 +8321,7 @@ function mergeJsonFile(filePath, updates, options = {}) {
|
|
|
8191
8321
|
const exists = existsSync7(filePath);
|
|
8192
8322
|
if (!exists) {
|
|
8193
8323
|
try {
|
|
8194
|
-
ensureDir(
|
|
8324
|
+
ensureDir(dirname3(filePath));
|
|
8195
8325
|
writeFileSync4(filePath, JSON.stringify(updates, null, 2), {
|
|
8196
8326
|
mode: 420
|
|
8197
8327
|
});
|
|
@@ -8241,7 +8371,7 @@ function appendToToml(filePath, section, content, options = {}) {
|
|
|
8241
8371
|
const exists = existsSync7(filePath);
|
|
8242
8372
|
if (!exists) {
|
|
8243
8373
|
try {
|
|
8244
|
-
ensureDir(
|
|
8374
|
+
ensureDir(dirname3(filePath));
|
|
8245
8375
|
writeFileSync4(filePath, content, { mode: 420 });
|
|
8246
8376
|
return { path: filePath, action: "create" };
|
|
8247
8377
|
} catch (error) {
|
|
@@ -8295,7 +8425,7 @@ async function writeFilesWithProgress(files, options = {}) {
|
|
|
8295
8425
|
});
|
|
8296
8426
|
}
|
|
8297
8427
|
results.push(result);
|
|
8298
|
-
await new Promise((
|
|
8428
|
+
await new Promise((resolve3) => setTimeout(resolve3, 50));
|
|
8299
8429
|
}
|
|
8300
8430
|
spinner2.stop("Files written");
|
|
8301
8431
|
for (const result of results) {
|
|
@@ -8395,7 +8525,7 @@ async function registerMcpServer() {
|
|
|
8395
8525
|
async function writeMcpConfigFallback(home) {
|
|
8396
8526
|
const { readFileSync: readFileSync7, writeFileSync: writeFileSync5, mkdirSync: mkdirSync6, existsSync: existsSync9 } = await import("node:fs");
|
|
8397
8527
|
const settingsPath = join8(home, ".claude", "settings.json");
|
|
8398
|
-
const settingsDir =
|
|
8528
|
+
const settingsDir = dirname4(settingsPath);
|
|
8399
8529
|
if (!existsSync9(settingsDir)) {
|
|
8400
8530
|
mkdirSync6(settingsDir, { recursive: true });
|
|
8401
8531
|
}
|
|
@@ -8414,7 +8544,7 @@ async function writeMcpConfigFallback(home) {
|
|
|
8414
8544
|
async function allowlistHarmonyTools(home, allowAll) {
|
|
8415
8545
|
const { readFileSync: readFileSync7, writeFileSync: writeFileSync5, mkdirSync: mkdirSync6, existsSync: existsSync9 } = await import("node:fs");
|
|
8416
8546
|
const settingsPath = join8(home, ".claude", "settings.json");
|
|
8417
|
-
const settingsDir =
|
|
8547
|
+
const settingsDir = dirname4(settingsPath);
|
|
8418
8548
|
if (!existsSync9(settingsDir)) {
|
|
8419
8549
|
mkdirSync6(settingsDir, { recursive: true });
|
|
8420
8550
|
}
|
|
@@ -8946,8 +9076,10 @@ ${colors.dim(url)}`);
|
|
|
8946
9076
|
createdNewAccount = true;
|
|
8947
9077
|
needsApiKey = true;
|
|
8948
9078
|
saveConfig({ apiKey, userEmail, apiUrl: API_URL });
|
|
8949
|
-
|
|
8950
|
-
|
|
9079
|
+
setActiveContext({
|
|
9080
|
+
workspaceId: selectedWorkspaceIdFromSignup,
|
|
9081
|
+
projectId: selectedProjectIdFromSignup
|
|
9082
|
+
});
|
|
8951
9083
|
p4.log.success("Workspace and board created");
|
|
8952
9084
|
} catch (error) {
|
|
8953
9085
|
spinner4.stop(colors.error("Account creation failed"));
|
|
@@ -9279,7 +9411,7 @@ Specify the workspace with --workspace <id>, or select one below.`);
|
|
|
9279
9411
|
if (allSymlinks.length > 0) {
|
|
9280
9412
|
for (const symlink of allSymlinks) {
|
|
9281
9413
|
try {
|
|
9282
|
-
const linkDir =
|
|
9414
|
+
const linkDir = dirname4(symlink.link);
|
|
9283
9415
|
if (!existsSync8(linkDir)) {
|
|
9284
9416
|
mkdirSync5(linkDir, { recursive: true });
|
|
9285
9417
|
}
|
|
@@ -9346,10 +9478,12 @@ Specify the workspace with --workspace <id>, or select one below.`);
|
|
|
9346
9478
|
localConfig.projectId = selectedProjectId;
|
|
9347
9479
|
saveLocalConfig(localConfig, cwd);
|
|
9348
9480
|
console.log(` ${colors.success("✓")} ${colors.dim(formatPath(getLocalConfigPath(cwd), home))} ${colors.dim("(created)")}`);
|
|
9349
|
-
if (selectedWorkspaceId)
|
|
9350
|
-
|
|
9351
|
-
|
|
9352
|
-
|
|
9481
|
+
if (selectedWorkspaceId || selectedProjectId) {
|
|
9482
|
+
setActiveContext({
|
|
9483
|
+
workspaceId: selectedWorkspaceId ?? null,
|
|
9484
|
+
projectId: selectedProjectId ?? null
|
|
9485
|
+
}, { global: true });
|
|
9486
|
+
}
|
|
9353
9487
|
}
|
|
9354
9488
|
console.log("");
|
|
9355
9489
|
p4.outro(colors.success("Setup complete!"));
|
|
@@ -9448,7 +9582,7 @@ Skills:`);
|
|
|
9448
9582
|
console.log(`
|
|
9449
9583
|
Context:`);
|
|
9450
9584
|
if (hasLocal) {
|
|
9451
|
-
console.log(` Local config: ${
|
|
9585
|
+
console.log(` Local config: ${findLocalConfigPath()}`);
|
|
9452
9586
|
console.log(` Workspace: ${localConfig?.workspaceId || "(not set)"}`);
|
|
9453
9587
|
console.log(` Project: ${localConfig?.projectId || "(not set)"}`);
|
|
9454
9588
|
}
|
|
@@ -9457,12 +9591,18 @@ Context:`);
|
|
|
9457
9591
|
console.log(` Project: ${globalConfig.activeProjectId || "(not set)"}`);
|
|
9458
9592
|
const effectiveWorkspace = getActiveWorkspaceId();
|
|
9459
9593
|
const effectiveProject = getActiveProjectId();
|
|
9460
|
-
const
|
|
9461
|
-
const
|
|
9594
|
+
const contextSource = hasLocal ? "local" : "global";
|
|
9595
|
+
const wsSource = effectiveWorkspace ? contextSource : "";
|
|
9596
|
+
const projSource = effectiveProject ? contextSource : "";
|
|
9462
9597
|
console.log(`
|
|
9463
9598
|
Active (effective):`);
|
|
9464
9599
|
console.log(` Workspace: ${effectiveWorkspace || "(not set)"}${wsSource ? ` ← ${wsSource}` : ""}`);
|
|
9465
9600
|
console.log(` Project: ${effectiveProject || "(not set)"}${projSource ? ` ← ${projSource}` : ""}`);
|
|
9601
|
+
const report = getActiveContext();
|
|
9602
|
+
if (!report.consistent && report.note) {
|
|
9603
|
+
console.log(`
|
|
9604
|
+
⚠ ${report.note}`);
|
|
9605
|
+
}
|
|
9466
9606
|
} else {
|
|
9467
9607
|
console.log(`Status: Not configured
|
|
9468
9608
|
`);
|