@gethmy/mcp 3.5.0 → 3.7.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/README.md +5 -5
- package/dist/cli.js +258 -12
- package/dist/index.js +252 -8
- package/dist/lib/api-client.js +117 -8
- package/dist/lib/config.js +56 -9
- package/dist/lib/oauth-refresh.js +49 -8
- package/dist/run-hook-cli.js +264 -8
- package/package.json +3 -2
- package/src/config.ts +162 -18
- package/src/hook-install.ts +1 -1
- package/src/oauth-refresh.ts +1 -1
- package/src/run-hook.ts +1 -1
- package/src/run-state.ts +1 -1
- package/src/server.ts +1 -1
- package/src/tui/setup.ts +12 -3
- package/src/tui/writer.ts +21 -1
- package/src/run-redaction.ts +0 -461
package/README.md
CHANGED
|
@@ -434,7 +434,7 @@ curl -X GET "https://gethmy.com/api/v1/workspaces" \
|
|
|
434
434
|
|
|
435
435
|
### Global Configuration
|
|
436
436
|
|
|
437
|
-
Stored in `~/.
|
|
437
|
+
Stored in `~/.hmy/agent/config.json`. Browser sign-in writes OAuth tokens (and nulls `apiKey`); API-key setup writes `apiKey` instead. The server prefers a live OAuth token and refreshes it automatically:
|
|
438
438
|
|
|
439
439
|
```json
|
|
440
440
|
{
|
|
@@ -452,7 +452,7 @@ Stored in `~/.harmony-mcp/config.json`. Browser sign-in writes OAuth tokens (and
|
|
|
452
452
|
|
|
453
453
|
### Local Project Configuration
|
|
454
454
|
|
|
455
|
-
Stored in `.
|
|
455
|
+
Stored in `.hmy.json` in your project root:
|
|
456
456
|
|
|
457
457
|
```json
|
|
458
458
|
{
|
|
@@ -498,10 +498,10 @@ Skills:
|
|
|
498
498
|
~/.agents/skills/hmy/SKILL.md
|
|
499
499
|
|
|
500
500
|
Context:
|
|
501
|
-
Local config: .
|
|
501
|
+
Local config: .hmy.json
|
|
502
502
|
Workspace: my-team-id
|
|
503
503
|
Project: my-project-id
|
|
504
|
-
Global config: ~/.
|
|
504
|
+
Global config: ~/.hmy/agent/config.json
|
|
505
505
|
Workspace: (not set)
|
|
506
506
|
Project: (not set)
|
|
507
507
|
|
|
@@ -515,7 +515,7 @@ Context:
|
|
|
515
515
|
Add this to prevent accidentally committing local config:
|
|
516
516
|
|
|
517
517
|
```
|
|
518
|
-
.
|
|
518
|
+
.hmy.json
|
|
519
519
|
```
|
|
520
520
|
|
|
521
521
|
## Architecture
|
package/dist/cli.js
CHANGED
|
@@ -21,12 +21,36 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
|
|
|
21
21
|
import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
22
22
|
import { homedir } from "node:os";
|
|
23
23
|
import { dirname, join, parse, resolve } from "node:path";
|
|
24
|
+
function noteLegacyConfigDir(path) {
|
|
25
|
+
if (warnedLegacyConfigDir)
|
|
26
|
+
return;
|
|
27
|
+
warnedLegacyConfigDir = true;
|
|
28
|
+
console.error(`Harmony: reading the pre-#1082 config at ${path}. ` + `The current location is ${getConfigPath()}; ` + `run the agent daemon once to migrate, or move the file yourself.`);
|
|
29
|
+
}
|
|
30
|
+
function noteLegacyLocalPin(path) {
|
|
31
|
+
if (warnedLegacyLocalPin)
|
|
32
|
+
return;
|
|
33
|
+
warnedLegacyLocalPin = true;
|
|
34
|
+
console.error(`Harmony: this repo is pinned by ${path}, the pre-#1082 name. ` + `Rename it to ${LOCAL_CONFIG_FILENAME} — the fallback that finds it is temporary.`);
|
|
35
|
+
}
|
|
36
|
+
function noteLocalPinRename(from, to) {
|
|
37
|
+
console.error(`Harmony: wrote the repo pin to ${to} (the pre-#1082 ${from} is now ignored and can be deleted).`);
|
|
38
|
+
}
|
|
39
|
+
function getHmyRootDir() {
|
|
40
|
+
return join(homedir(), CONFIG_DIR_NAME);
|
|
41
|
+
}
|
|
24
42
|
function getConfigDir() {
|
|
25
|
-
return join(
|
|
43
|
+
return join(getHmyRootDir(), CONFIG_DIR_SUBDIR);
|
|
44
|
+
}
|
|
45
|
+
function getLegacyConfigDir() {
|
|
46
|
+
return join(homedir(), LEGACY_CONFIG_DIR_NAME);
|
|
26
47
|
}
|
|
27
48
|
function getConfigPath() {
|
|
28
49
|
return join(getConfigDir(), "config.json");
|
|
29
50
|
}
|
|
51
|
+
function getLegacyConfigPath() {
|
|
52
|
+
return join(getLegacyConfigDir(), "config.json");
|
|
53
|
+
}
|
|
30
54
|
function getLocalConfigPath(cwd) {
|
|
31
55
|
return join(cwd || process.cwd(), LOCAL_CONFIG_FILENAME);
|
|
32
56
|
}
|
|
@@ -36,9 +60,14 @@ function findLocalConfigPath(cwd) {
|
|
|
36
60
|
const { root } = parse(dir);
|
|
37
61
|
for (;; ) {
|
|
38
62
|
if (dir !== home && dir !== root) {
|
|
39
|
-
const
|
|
40
|
-
if (existsSync(
|
|
41
|
-
return
|
|
63
|
+
const current = join(dir, LOCAL_CONFIG_FILENAME);
|
|
64
|
+
if (existsSync(current))
|
|
65
|
+
return current;
|
|
66
|
+
const legacy = join(dir, LEGACY_LOCAL_CONFIG_FILENAME);
|
|
67
|
+
if (existsSync(legacy)) {
|
|
68
|
+
noteLegacyLocalPin(legacy);
|
|
69
|
+
return legacy;
|
|
70
|
+
}
|
|
42
71
|
}
|
|
43
72
|
const parent = dirname(dir);
|
|
44
73
|
if (parent === dir)
|
|
@@ -61,9 +90,12 @@ function emptyConfig() {
|
|
|
61
90
|
};
|
|
62
91
|
}
|
|
63
92
|
function loadConfig() {
|
|
64
|
-
|
|
93
|
+
let configPath = getConfigPath();
|
|
65
94
|
if (!existsSync(configPath)) {
|
|
66
|
-
|
|
95
|
+
configPath = getLegacyConfigPath();
|
|
96
|
+
if (!existsSync(configPath))
|
|
97
|
+
return emptyConfig();
|
|
98
|
+
noteLegacyConfigDir(configPath);
|
|
67
99
|
}
|
|
68
100
|
try {
|
|
69
101
|
const data = readFileSync(configPath, "utf-8");
|
|
@@ -113,7 +145,11 @@ function loadLocalConfig(cwd) {
|
|
|
113
145
|
}
|
|
114
146
|
}
|
|
115
147
|
function saveLocalConfig(config, cwd) {
|
|
116
|
-
const
|
|
148
|
+
const foundPath = findLocalConfigPath(cwd);
|
|
149
|
+
const localConfigPath = foundPath ? join(dirname(foundPath), LOCAL_CONFIG_FILENAME) : getLocalConfigPath(cwd);
|
|
150
|
+
if (foundPath !== null && foundPath !== localConfigPath) {
|
|
151
|
+
noteLocalPinRename(foundPath, localConfigPath);
|
|
152
|
+
}
|
|
117
153
|
const existingConfig = loadLocalConfig(cwd) || {
|
|
118
154
|
workspaceId: null,
|
|
119
155
|
projectId: null
|
|
@@ -125,6 +161,7 @@ function saveLocalConfig(config, cwd) {
|
|
|
125
161
|
if (newConfig.projectId)
|
|
126
162
|
cleanConfig.projectId = newConfig.projectId;
|
|
127
163
|
writeFileSync(localConfigPath, JSON.stringify(cleanConfig, null, 2));
|
|
164
|
+
return localConfigPath;
|
|
128
165
|
}
|
|
129
166
|
function hasLocalConfig(cwd) {
|
|
130
167
|
return findLocalConfigPath(cwd) !== null;
|
|
@@ -259,7 +296,7 @@ function getMemoryDir() {
|
|
|
259
296
|
return config.memoryDir;
|
|
260
297
|
return join(homedir(), ".harmony", "memory");
|
|
261
298
|
}
|
|
262
|
-
var DEFAULT_API_URL = "https://app.gethmy.com/api", LOCAL_CONFIG_FILENAME = ".harmony-mcp.json";
|
|
299
|
+
var DEFAULT_API_URL = "https://app.gethmy.com/api", LOCAL_CONFIG_FILENAME = ".hmy.json", LEGACY_LOCAL_CONFIG_FILENAME = ".harmony-mcp.json", CONFIG_DIR_NAME = ".hmy", CONFIG_DIR_SUBDIR = "agent", LEGACY_CONFIG_DIR_NAME = ".harmony-mcp", warnedLegacyConfigDir = false, warnedLegacyLocalPin = false;
|
|
263
300
|
var init_config = () => {};
|
|
264
301
|
|
|
265
302
|
// src/prompt-builder.ts
|
|
@@ -2167,6 +2204,213 @@ var REVIEW_DISALLOWED_TOOLS = [
|
|
|
2167
2204
|
"mcp__harmony__harmony_delete_subtask",
|
|
2168
2205
|
"mcp__harmony__harmony_toggle_subtask"
|
|
2169
2206
|
];
|
|
2207
|
+
// ../harmony-shared/dist/runRedaction.js
|
|
2208
|
+
var MAX_INPUT_CHARS = 2000;
|
|
2209
|
+
var MAX_OUTPUT_CHARS = 4000;
|
|
2210
|
+
var MAX_INPUT_STRING_CHARS = 600;
|
|
2211
|
+
var REDACTION_MARK = "«redacted»";
|
|
2212
|
+
var SENSITIVE_SEGMENTS = [
|
|
2213
|
+
".ssh",
|
|
2214
|
+
".gnupg",
|
|
2215
|
+
".aws",
|
|
2216
|
+
".codex",
|
|
2217
|
+
".gemini",
|
|
2218
|
+
".docker",
|
|
2219
|
+
".kube",
|
|
2220
|
+
".hmy",
|
|
2221
|
+
".harmony-mcp",
|
|
2222
|
+
".password-store",
|
|
2223
|
+
".claude",
|
|
2224
|
+
"gh",
|
|
2225
|
+
"gcloud",
|
|
2226
|
+
"op",
|
|
2227
|
+
"anthropic"
|
|
2228
|
+
];
|
|
2229
|
+
var CONFIG_SCOPED_SEGMENTS = new Set([
|
|
2230
|
+
"gh",
|
|
2231
|
+
"gcloud",
|
|
2232
|
+
"op",
|
|
2233
|
+
"anthropic"
|
|
2234
|
+
]);
|
|
2235
|
+
var SENSITIVE_BASENAMES = new Set([
|
|
2236
|
+
".netrc",
|
|
2237
|
+
"_netrc",
|
|
2238
|
+
".npmrc",
|
|
2239
|
+
".pgpass",
|
|
2240
|
+
".git-credentials",
|
|
2241
|
+
".htpasswd",
|
|
2242
|
+
".claude.json",
|
|
2243
|
+
"credentials",
|
|
2244
|
+
".credentials",
|
|
2245
|
+
"credentials.json",
|
|
2246
|
+
".credentials.json",
|
|
2247
|
+
"credentials.yml",
|
|
2248
|
+
"credentials.yaml",
|
|
2249
|
+
"auth.json",
|
|
2250
|
+
".auth.json",
|
|
2251
|
+
"secrets",
|
|
2252
|
+
"secrets.json",
|
|
2253
|
+
"secrets.yaml",
|
|
2254
|
+
"secrets.yml",
|
|
2255
|
+
"id_rsa",
|
|
2256
|
+
"id_dsa",
|
|
2257
|
+
"id_ecdsa",
|
|
2258
|
+
"id_ed25519",
|
|
2259
|
+
"known_hosts"
|
|
2260
|
+
]);
|
|
2261
|
+
var SENSITIVE_EXTENSIONS = [
|
|
2262
|
+
".pem",
|
|
2263
|
+
".key",
|
|
2264
|
+
".p12",
|
|
2265
|
+
".pfx",
|
|
2266
|
+
".keystore",
|
|
2267
|
+
".jks",
|
|
2268
|
+
".asc",
|
|
2269
|
+
".gpg"
|
|
2270
|
+
];
|
|
2271
|
+
function isSensitivePath(rawPath) {
|
|
2272
|
+
if (typeof rawPath !== "string" || rawPath.length === 0)
|
|
2273
|
+
return false;
|
|
2274
|
+
const path = rawPath.trim().toLowerCase();
|
|
2275
|
+
const segments = path.split(/[\\/]+/).filter((s) => s.length > 0);
|
|
2276
|
+
if (segments.length === 0)
|
|
2277
|
+
return false;
|
|
2278
|
+
for (let i = 0;i < segments.length; i++) {
|
|
2279
|
+
const segment = segments[i];
|
|
2280
|
+
if (!SENSITIVE_SEGMENTS.includes(segment))
|
|
2281
|
+
continue;
|
|
2282
|
+
if (CONFIG_SCOPED_SEGMENTS.has(segment)) {
|
|
2283
|
+
if (i > 0 && segments[i - 1] === ".config")
|
|
2284
|
+
return true;
|
|
2285
|
+
continue;
|
|
2286
|
+
}
|
|
2287
|
+
return true;
|
|
2288
|
+
}
|
|
2289
|
+
const basename = segments[segments.length - 1];
|
|
2290
|
+
if (SENSITIVE_BASENAMES.has(basename))
|
|
2291
|
+
return true;
|
|
2292
|
+
if (basename === ".env" || basename.startsWith(".env."))
|
|
2293
|
+
return true;
|
|
2294
|
+
if (basename.endsWith(".env"))
|
|
2295
|
+
return true;
|
|
2296
|
+
if (SENSITIVE_EXTENSIONS.some((ext) => basename.endsWith(ext)))
|
|
2297
|
+
return true;
|
|
2298
|
+
if (/service[-_]?account.*\.json$/.test(basename))
|
|
2299
|
+
return true;
|
|
2300
|
+
return false;
|
|
2301
|
+
}
|
|
2302
|
+
function sensitivePathsIn(input, depth = 0) {
|
|
2303
|
+
if (depth > 6)
|
|
2304
|
+
return [];
|
|
2305
|
+
if (typeof input === "string") {
|
|
2306
|
+
return isSensitivePath(input) ? [input] : [];
|
|
2307
|
+
}
|
|
2308
|
+
if (Array.isArray(input)) {
|
|
2309
|
+
return input.flatMap((item) => sensitivePathsIn(item, depth + 1));
|
|
2310
|
+
}
|
|
2311
|
+
if (input !== null && typeof input === "object") {
|
|
2312
|
+
return Object.values(input).flatMap((value) => sensitivePathsIn(value, depth + 1));
|
|
2313
|
+
}
|
|
2314
|
+
return [];
|
|
2315
|
+
}
|
|
2316
|
+
var SECRET_PATTERNS = [
|
|
2317
|
+
{
|
|
2318
|
+
pattern: /-----BEGIN[^-]*PRIVATE KEY-----[\s\S]*?-----END[^-]*-----/g,
|
|
2319
|
+
replace: REDACTION_MARK
|
|
2320
|
+
},
|
|
2321
|
+
{ pattern: /\bhmy_at_[A-Za-z0-9_-]{8,}/g, replace: REDACTION_MARK },
|
|
2322
|
+
{ pattern: /\bhmy_[A-Za-z0-9_-]{8,}/g, replace: REDACTION_MARK },
|
|
2323
|
+
{ pattern: /\bsk-(?:ant-)?[A-Za-z0-9_-]{16,}/g, replace: REDACTION_MARK },
|
|
2324
|
+
{ pattern: /\bgh[pousr]_[A-Za-z0-9]{16,}/g, replace: REDACTION_MARK },
|
|
2325
|
+
{ pattern: /\bgithub_pat_[A-Za-z0-9_]{20,}/g, replace: REDACTION_MARK },
|
|
2326
|
+
{ pattern: /\bxox[abprs]-[A-Za-z0-9-]{10,}/g, replace: REDACTION_MARK },
|
|
2327
|
+
{ pattern: /\bAKIA[0-9A-Z]{16}\b/g, replace: REDACTION_MARK },
|
|
2328
|
+
{ pattern: /\bAIza[0-9A-Za-z_-]{20,}/g, replace: REDACTION_MARK },
|
|
2329
|
+
{
|
|
2330
|
+
pattern: /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g,
|
|
2331
|
+
replace: REDACTION_MARK
|
|
2332
|
+
},
|
|
2333
|
+
{
|
|
2334
|
+
pattern: /\b(Bearer|Basic|Token)\s+[A-Za-z0-9._~+/=-]{12,}/gi,
|
|
2335
|
+
replace: `$1 ${REDACTION_MARK}`
|
|
2336
|
+
},
|
|
2337
|
+
{
|
|
2338
|
+
pattern: /(\w{1,32}:\/\/)[^/\s:@]+:[^/\s@]+@/g,
|
|
2339
|
+
replace: `$1${REDACTION_MARK}@`
|
|
2340
|
+
},
|
|
2341
|
+
{
|
|
2342
|
+
pattern: /\b([A-Za-z0-9_]{0,40}(?:TOKEN|SECRET|PASSWORD|PASSWD|APIKEY|API_KEY|ACCESS_KEY|PRIVATE_KEY|CREDENTIAL|AUTH)[A-Za-z0-9_]{0,40})\s*[=:]\s*(?:"[^"]*"|'[^']*'|`[^`]*`|[^\s,;)}\]]+)/gi,
|
|
2343
|
+
replace: `$1=${REDACTION_MARK}`
|
|
2344
|
+
},
|
|
2345
|
+
{
|
|
2346
|
+
pattern: /(--?(?:password|passwd|token|api-?key|secret|auth)(?:=|\s+))(?:"[^"]*"|'[^']*'|[^\s]+)/gi,
|
|
2347
|
+
replace: `$1${REDACTION_MARK}`
|
|
2348
|
+
}
|
|
2349
|
+
];
|
|
2350
|
+
function redactSecrets(text) {
|
|
2351
|
+
if (typeof text !== "string" || text.length === 0)
|
|
2352
|
+
return text;
|
|
2353
|
+
let out = text;
|
|
2354
|
+
for (const { pattern, replace } of SECRET_PATTERNS) {
|
|
2355
|
+
pattern.lastIndex = 0;
|
|
2356
|
+
out = out.replace(pattern, replace);
|
|
2357
|
+
}
|
|
2358
|
+
return out;
|
|
2359
|
+
}
|
|
2360
|
+
function truncate(text, max, originalLength) {
|
|
2361
|
+
const total = originalLength ?? text.length;
|
|
2362
|
+
if (total <= max)
|
|
2363
|
+
return text;
|
|
2364
|
+
return `${text.slice(0, max)}… [+${total - max} chars]`;
|
|
2365
|
+
}
|
|
2366
|
+
function redactThenTruncate(text, max) {
|
|
2367
|
+
const preCap = max * 4 + 64;
|
|
2368
|
+
const scanned = text.length > preCap ? text.slice(0, preCap) : text;
|
|
2369
|
+
return truncate(redactSecrets(scanned), max, text.length);
|
|
2370
|
+
}
|
|
2371
|
+
function redactStructure(value, depth = 0) {
|
|
2372
|
+
if (depth > 6)
|
|
2373
|
+
return REDACTION_MARK;
|
|
2374
|
+
if (typeof value === "string") {
|
|
2375
|
+
return redactThenTruncate(value, MAX_INPUT_STRING_CHARS);
|
|
2376
|
+
}
|
|
2377
|
+
if (Array.isArray(value)) {
|
|
2378
|
+
return value.slice(0, 20).map((item) => redactStructure(item, depth + 1));
|
|
2379
|
+
}
|
|
2380
|
+
if (value !== null && typeof value === "object") {
|
|
2381
|
+
const out = {};
|
|
2382
|
+
for (const [key, item] of Object.entries(value)) {
|
|
2383
|
+
out[key] = redactStructure(item, depth + 1);
|
|
2384
|
+
}
|
|
2385
|
+
return out;
|
|
2386
|
+
}
|
|
2387
|
+
return value;
|
|
2388
|
+
}
|
|
2389
|
+
function redactToolCall(args) {
|
|
2390
|
+
const sensitive = sensitivePathsIn(args.input);
|
|
2391
|
+
if (sensitive.length > 0) {
|
|
2392
|
+
return { withheld: "sensitive-path" };
|
|
2393
|
+
}
|
|
2394
|
+
const result = {};
|
|
2395
|
+
if (args.input !== undefined) {
|
|
2396
|
+
let input = redactStructure(args.input);
|
|
2397
|
+
let serialized;
|
|
2398
|
+
try {
|
|
2399
|
+
serialized = JSON.stringify(input) ?? "";
|
|
2400
|
+
} catch {
|
|
2401
|
+
serialized = "";
|
|
2402
|
+
input = REDACTION_MARK;
|
|
2403
|
+
}
|
|
2404
|
+
if (serialized.length > MAX_INPUT_CHARS) {
|
|
2405
|
+
input = truncate(serialized, MAX_INPUT_CHARS);
|
|
2406
|
+
}
|
|
2407
|
+
result.input = input;
|
|
2408
|
+
}
|
|
2409
|
+
if (typeof args.output === "string" && args.output.length > 0) {
|
|
2410
|
+
result.output = redactThenTruncate(args.output, MAX_OUTPUT_CHARS);
|
|
2411
|
+
}
|
|
2412
|
+
return result;
|
|
2413
|
+
}
|
|
2170
2414
|
// ../harmony-shared/dist/stageHandoff.js
|
|
2171
2415
|
var HANDOFF_MARKER = "harmony:stage-handoff";
|
|
2172
2416
|
var HANDOFF_BLOCK_RE = new RegExp("```json\\s*\\n//\\s*" + HANDOFF_MARKER + "\\s*\\n([\\s\\S]*?)\\n```", "m");
|
|
@@ -9436,6 +9680,7 @@ function ensureDir(dirPath) {
|
|
|
9436
9680
|
mkdirSync5(dirPath, { recursive: true, mode: 493 });
|
|
9437
9681
|
}
|
|
9438
9682
|
}
|
|
9683
|
+
var CONFIG_DIR_MARKERS = [".hmy", ".harmony-mcp"];
|
|
9439
9684
|
function writeFile(filePath, content, options = {}) {
|
|
9440
9685
|
const exists = existsSync8(filePath);
|
|
9441
9686
|
if (exists && !options.force) {
|
|
@@ -9443,7 +9688,7 @@ function writeFile(filePath, content, options = {}) {
|
|
|
9443
9688
|
}
|
|
9444
9689
|
try {
|
|
9445
9690
|
ensureDir(dirname3(filePath));
|
|
9446
|
-
const defaultMode = filePath.includes(
|
|
9691
|
+
const defaultMode = CONFIG_DIR_MARKERS.some((marker) => filePath.includes(marker)) ? 384 : 420;
|
|
9447
9692
|
const mode = options.mode ?? defaultMode;
|
|
9448
9693
|
writeFileSync5(filePath, content, { mode });
|
|
9449
9694
|
if (options.mode !== undefined) {
|
|
@@ -10612,14 +10857,15 @@ Specify the workspace with --workspace <id>, or select one below.`);
|
|
|
10612
10857
|
console.log(` ${colors.dim("Skipped tool allowlist — you'll be prompted per tool, or run /permissions in Claude Code later.")}`);
|
|
10613
10858
|
}
|
|
10614
10859
|
}
|
|
10860
|
+
let writtenLocalConfigPath = null;
|
|
10615
10861
|
if (selectedWorkspaceId || selectedProjectId) {
|
|
10616
10862
|
const localConfig = {};
|
|
10617
10863
|
if (selectedWorkspaceId)
|
|
10618
10864
|
localConfig.workspaceId = selectedWorkspaceId;
|
|
10619
10865
|
if (selectedProjectId)
|
|
10620
10866
|
localConfig.projectId = selectedProjectId;
|
|
10621
|
-
saveLocalConfig(localConfig, cwd);
|
|
10622
|
-
console.log(` ${colors.success("✓")} ${colors.dim(formatPath(
|
|
10867
|
+
writtenLocalConfigPath = saveLocalConfig(localConfig, cwd);
|
|
10868
|
+
console.log(` ${colors.success("✓")} ${colors.dim(formatPath(writtenLocalConfigPath, home))} ${colors.dim("(created)")}`);
|
|
10623
10869
|
if (selectedWorkspaceId || selectedProjectId) {
|
|
10624
10870
|
setActiveContext({
|
|
10625
10871
|
workspaceId: selectedWorkspaceId ?? null,
|
|
@@ -10649,7 +10895,7 @@ Specify the workspace with --workspace <id>, or select one below.`);
|
|
|
10649
10895
|
console.log(` Skills: ${installMode === "global" ? "~/.agents/skills/ (global)" : ".claude/skills/ (local)"}`);
|
|
10650
10896
|
}
|
|
10651
10897
|
if (selectedWorkspaceId || selectedProjectId) {
|
|
10652
|
-
console.log(` Context: ${formatPath(getLocalConfigPath(cwd), home)}`);
|
|
10898
|
+
console.log(` Context: ${formatPath(writtenLocalConfigPath ?? getLocalConfigPath(cwd), home)}`);
|
|
10653
10899
|
}
|
|
10654
10900
|
console.log("");
|
|
10655
10901
|
console.log(` ${colors.bold("Usage:")}`);
|