@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 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 = getLocalConfigPath(cwd);
85
- if (!existsSync(localConfigPath)) {
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 existsSync(getLocalConfigPath(cwd));
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 setActiveWorkspace(workspaceId, options) {
137
- if (options?.local) {
138
- saveLocalConfig({ workspaceId }, options.cwd);
139
- } else {
140
- saveConfig({ activeWorkspaceId: workspaceId });
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
- function setActiveProject(projectId, options) {
144
- if (options?.local) {
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({ activeProjectId: projectId });
164
+ saveConfig({
165
+ activeWorkspaceId: context.workspaceId,
166
+ activeProjectId: context.projectId
167
+ });
148
168
  }
149
169
  }
150
- function getActiveWorkspaceId(cwd) {
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?.workspaceId) {
153
- return localConfig.workspaceId;
180
+ if (localConfig) {
181
+ return {
182
+ workspaceId: localConfig.workspaceId ?? null,
183
+ projectId: localConfig.projectId ?? null
184
+ };
154
185
  }
155
- return loadConfig().activeWorkspaceId;
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
- const localConfig = loadLocalConfig(cwd);
159
- if (localConfig?.projectId) {
160
- return localConfig.projectId;
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 loadConfig().activeProjectId;
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((resolve) => setTimeout(resolve, ms));
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((resolve) => setTimeout(resolve, ms));
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((resolve) => this.queue.push(resolve));
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((resolve) => setTimeout(resolve, 2000));
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 = dirname(filePath);
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 = dirname(dirname(samplePath));
3590
+ const parentDir = dirname2(dirname2(samplePath));
3539
3591
  siblingPath = `${parentDir}/${name}/SKILL.md`;
3540
3592
  } else {
3541
- const parentDir = dirname(samplePath);
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, or lifecycle state ('active'|'deprecated').",
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.setActiveProject(resolved.project.id);
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
- deps.setActiveProject(projectId);
6330
- return { success: true, activeProjectId: projectId };
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: deps.getActiveWorkspaceId(),
6337
- activeProjectId: deps.getActiveProjectId()
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.setActiveWorkspace(result.workspace.id);
7275
- deps.setActiveProject(result.project.id);
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
- setActiveProject: (id) => setActiveProject(id),
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 dirname3, join as join8 } from "node:path";
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 = resolve(cwd);
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 = resolve(projectRoot, refPath);
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 = resolve(cwd);
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 = resolve(root, refPath);
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 dirname2 } from "node:path";
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(dirname2(filePath));
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(dirname2(filePath));
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(dirname2(filePath));
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((resolve2) => setTimeout(resolve2, 50));
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 = dirname3(settingsPath);
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 = dirname3(settingsPath);
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
- setActiveWorkspace(selectedWorkspaceIdFromSignup);
8950
- setActiveProject(selectedProjectIdFromSignup);
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 = dirname3(symlink.link);
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
- setActiveWorkspace(selectedWorkspaceId);
9351
- if (selectedProjectId)
9352
- setActiveProject(selectedProjectId);
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: ${getLocalConfigPath()}`);
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 wsSource = localConfig?.workspaceId ? "local" : globalConfig.activeWorkspaceId ? "global" : "";
9461
- const projSource = localConfig?.projectId ? "local" : globalConfig.activeProjectId ? "global" : "";
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
  `);