@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/dist/index.js CHANGED
@@ -21,12 +21,36 @@ var __require = /* @__PURE__ */ createRequire(import.meta.url);
21
21
  import { existsSync as existsSync2, mkdirSync as mkdirSync2, readFileSync as readFileSync2, writeFileSync as writeFileSync2 } from "node:fs";
22
22
  import { homedir } from "node:os";
23
23
  import { dirname, join as join2, 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 join2(homedir(), CONFIG_DIR_NAME);
41
+ }
24
42
  function getConfigDir() {
25
- return join2(homedir(), ".harmony-mcp");
43
+ return join2(getHmyRootDir(), CONFIG_DIR_SUBDIR);
44
+ }
45
+ function getLegacyConfigDir() {
46
+ return join2(homedir(), LEGACY_CONFIG_DIR_NAME);
26
47
  }
27
48
  function getConfigPath() {
28
49
  return join2(getConfigDir(), "config.json");
29
50
  }
51
+ function getLegacyConfigPath() {
52
+ return join2(getLegacyConfigDir(), "config.json");
53
+ }
30
54
  function getLocalConfigPath(cwd) {
31
55
  return join2(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 candidate = join2(dir, LOCAL_CONFIG_FILENAME);
40
- if (existsSync2(candidate))
41
- return candidate;
63
+ const current = join2(dir, LOCAL_CONFIG_FILENAME);
64
+ if (existsSync2(current))
65
+ return current;
66
+ const legacy = join2(dir, LEGACY_LOCAL_CONFIG_FILENAME);
67
+ if (existsSync2(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
- const configPath = getConfigPath();
93
+ let configPath = getConfigPath();
65
94
  if (!existsSync2(configPath)) {
66
- return emptyConfig();
95
+ configPath = getLegacyConfigPath();
96
+ if (!existsSync2(configPath))
97
+ return emptyConfig();
98
+ noteLegacyConfigDir(configPath);
67
99
  }
68
100
  try {
69
101
  const data = readFileSync2(configPath, "utf-8");
@@ -113,7 +145,11 @@ function loadLocalConfig(cwd) {
113
145
  }
114
146
  }
115
147
  function saveLocalConfig(config, cwd) {
116
- const localConfigPath = findLocalConfigPath(cwd) ?? getLocalConfigPath(cwd);
148
+ const foundPath = findLocalConfigPath(cwd);
149
+ const localConfigPath = foundPath ? join2(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
  writeFileSync2(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 join2(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
@@ -1950,6 +1987,213 @@ var REVIEW_DISALLOWED_TOOLS = [
1950
1987
  "mcp__harmony__harmony_delete_subtask",
1951
1988
  "mcp__harmony__harmony_toggle_subtask"
1952
1989
  ];
1990
+ // ../harmony-shared/dist/runRedaction.js
1991
+ var MAX_INPUT_CHARS = 2000;
1992
+ var MAX_OUTPUT_CHARS = 4000;
1993
+ var MAX_INPUT_STRING_CHARS = 600;
1994
+ var REDACTION_MARK = "«redacted»";
1995
+ var SENSITIVE_SEGMENTS = [
1996
+ ".ssh",
1997
+ ".gnupg",
1998
+ ".aws",
1999
+ ".codex",
2000
+ ".gemini",
2001
+ ".docker",
2002
+ ".kube",
2003
+ ".hmy",
2004
+ ".harmony-mcp",
2005
+ ".password-store",
2006
+ ".claude",
2007
+ "gh",
2008
+ "gcloud",
2009
+ "op",
2010
+ "anthropic"
2011
+ ];
2012
+ var CONFIG_SCOPED_SEGMENTS = new Set([
2013
+ "gh",
2014
+ "gcloud",
2015
+ "op",
2016
+ "anthropic"
2017
+ ]);
2018
+ var SENSITIVE_BASENAMES = new Set([
2019
+ ".netrc",
2020
+ "_netrc",
2021
+ ".npmrc",
2022
+ ".pgpass",
2023
+ ".git-credentials",
2024
+ ".htpasswd",
2025
+ ".claude.json",
2026
+ "credentials",
2027
+ ".credentials",
2028
+ "credentials.json",
2029
+ ".credentials.json",
2030
+ "credentials.yml",
2031
+ "credentials.yaml",
2032
+ "auth.json",
2033
+ ".auth.json",
2034
+ "secrets",
2035
+ "secrets.json",
2036
+ "secrets.yaml",
2037
+ "secrets.yml",
2038
+ "id_rsa",
2039
+ "id_dsa",
2040
+ "id_ecdsa",
2041
+ "id_ed25519",
2042
+ "known_hosts"
2043
+ ]);
2044
+ var SENSITIVE_EXTENSIONS = [
2045
+ ".pem",
2046
+ ".key",
2047
+ ".p12",
2048
+ ".pfx",
2049
+ ".keystore",
2050
+ ".jks",
2051
+ ".asc",
2052
+ ".gpg"
2053
+ ];
2054
+ function isSensitivePath(rawPath) {
2055
+ if (typeof rawPath !== "string" || rawPath.length === 0)
2056
+ return false;
2057
+ const path = rawPath.trim().toLowerCase();
2058
+ const segments = path.split(/[\\/]+/).filter((s) => s.length > 0);
2059
+ if (segments.length === 0)
2060
+ return false;
2061
+ for (let i = 0;i < segments.length; i++) {
2062
+ const segment = segments[i];
2063
+ if (!SENSITIVE_SEGMENTS.includes(segment))
2064
+ continue;
2065
+ if (CONFIG_SCOPED_SEGMENTS.has(segment)) {
2066
+ if (i > 0 && segments[i - 1] === ".config")
2067
+ return true;
2068
+ continue;
2069
+ }
2070
+ return true;
2071
+ }
2072
+ const basename = segments[segments.length - 1];
2073
+ if (SENSITIVE_BASENAMES.has(basename))
2074
+ return true;
2075
+ if (basename === ".env" || basename.startsWith(".env."))
2076
+ return true;
2077
+ if (basename.endsWith(".env"))
2078
+ return true;
2079
+ if (SENSITIVE_EXTENSIONS.some((ext) => basename.endsWith(ext)))
2080
+ return true;
2081
+ if (/service[-_]?account.*\.json$/.test(basename))
2082
+ return true;
2083
+ return false;
2084
+ }
2085
+ function sensitivePathsIn(input, depth = 0) {
2086
+ if (depth > 6)
2087
+ return [];
2088
+ if (typeof input === "string") {
2089
+ return isSensitivePath(input) ? [input] : [];
2090
+ }
2091
+ if (Array.isArray(input)) {
2092
+ return input.flatMap((item) => sensitivePathsIn(item, depth + 1));
2093
+ }
2094
+ if (input !== null && typeof input === "object") {
2095
+ return Object.values(input).flatMap((value) => sensitivePathsIn(value, depth + 1));
2096
+ }
2097
+ return [];
2098
+ }
2099
+ var SECRET_PATTERNS = [
2100
+ {
2101
+ pattern: /-----BEGIN[^-]*PRIVATE KEY-----[\s\S]*?-----END[^-]*-----/g,
2102
+ replace: REDACTION_MARK
2103
+ },
2104
+ { pattern: /\bhmy_at_[A-Za-z0-9_-]{8,}/g, replace: REDACTION_MARK },
2105
+ { pattern: /\bhmy_[A-Za-z0-9_-]{8,}/g, replace: REDACTION_MARK },
2106
+ { pattern: /\bsk-(?:ant-)?[A-Za-z0-9_-]{16,}/g, replace: REDACTION_MARK },
2107
+ { pattern: /\bgh[pousr]_[A-Za-z0-9]{16,}/g, replace: REDACTION_MARK },
2108
+ { pattern: /\bgithub_pat_[A-Za-z0-9_]{20,}/g, replace: REDACTION_MARK },
2109
+ { pattern: /\bxox[abprs]-[A-Za-z0-9-]{10,}/g, replace: REDACTION_MARK },
2110
+ { pattern: /\bAKIA[0-9A-Z]{16}\b/g, replace: REDACTION_MARK },
2111
+ { pattern: /\bAIza[0-9A-Za-z_-]{20,}/g, replace: REDACTION_MARK },
2112
+ {
2113
+ pattern: /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g,
2114
+ replace: REDACTION_MARK
2115
+ },
2116
+ {
2117
+ pattern: /\b(Bearer|Basic|Token)\s+[A-Za-z0-9._~+/=-]{12,}/gi,
2118
+ replace: `$1 ${REDACTION_MARK}`
2119
+ },
2120
+ {
2121
+ pattern: /(\w{1,32}:\/\/)[^/\s:@]+:[^/\s@]+@/g,
2122
+ replace: `$1${REDACTION_MARK}@`
2123
+ },
2124
+ {
2125
+ 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,
2126
+ replace: `$1=${REDACTION_MARK}`
2127
+ },
2128
+ {
2129
+ pattern: /(--?(?:password|passwd|token|api-?key|secret|auth)(?:=|\s+))(?:"[^"]*"|'[^']*'|[^\s]+)/gi,
2130
+ replace: `$1${REDACTION_MARK}`
2131
+ }
2132
+ ];
2133
+ function redactSecrets(text) {
2134
+ if (typeof text !== "string" || text.length === 0)
2135
+ return text;
2136
+ let out = text;
2137
+ for (const { pattern, replace } of SECRET_PATTERNS) {
2138
+ pattern.lastIndex = 0;
2139
+ out = out.replace(pattern, replace);
2140
+ }
2141
+ return out;
2142
+ }
2143
+ function truncate(text, max, originalLength) {
2144
+ const total = originalLength ?? text.length;
2145
+ if (total <= max)
2146
+ return text;
2147
+ return `${text.slice(0, max)}… [+${total - max} chars]`;
2148
+ }
2149
+ function redactThenTruncate(text, max) {
2150
+ const preCap = max * 4 + 64;
2151
+ const scanned = text.length > preCap ? text.slice(0, preCap) : text;
2152
+ return truncate(redactSecrets(scanned), max, text.length);
2153
+ }
2154
+ function redactStructure(value, depth = 0) {
2155
+ if (depth > 6)
2156
+ return REDACTION_MARK;
2157
+ if (typeof value === "string") {
2158
+ return redactThenTruncate(value, MAX_INPUT_STRING_CHARS);
2159
+ }
2160
+ if (Array.isArray(value)) {
2161
+ return value.slice(0, 20).map((item) => redactStructure(item, depth + 1));
2162
+ }
2163
+ if (value !== null && typeof value === "object") {
2164
+ const out = {};
2165
+ for (const [key, item] of Object.entries(value)) {
2166
+ out[key] = redactStructure(item, depth + 1);
2167
+ }
2168
+ return out;
2169
+ }
2170
+ return value;
2171
+ }
2172
+ function redactToolCall(args) {
2173
+ const sensitive = sensitivePathsIn(args.input);
2174
+ if (sensitive.length > 0) {
2175
+ return { withheld: "sensitive-path" };
2176
+ }
2177
+ const result = {};
2178
+ if (args.input !== undefined) {
2179
+ let input = redactStructure(args.input);
2180
+ let serialized;
2181
+ try {
2182
+ serialized = JSON.stringify(input) ?? "";
2183
+ } catch {
2184
+ serialized = "";
2185
+ input = REDACTION_MARK;
2186
+ }
2187
+ if (serialized.length > MAX_INPUT_CHARS) {
2188
+ input = truncate(serialized, MAX_INPUT_CHARS);
2189
+ }
2190
+ result.input = input;
2191
+ }
2192
+ if (typeof args.output === "string" && args.output.length > 0) {
2193
+ result.output = redactThenTruncate(args.output, MAX_OUTPUT_CHARS);
2194
+ }
2195
+ return result;
2196
+ }
1953
2197
  // ../harmony-shared/dist/stageHandoff.js
1954
2198
  var HANDOFF_MARKER = "harmony:stage-handoff";
1955
2199
  var HANDOFF_BLOCK_RE = new RegExp("```json\\s*\\n//\\s*" + HANDOFF_MARKER + "\\s*\\n([\\s\\S]*?)\\n```", "m");
@@ -18,12 +18,40 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
18
18
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
19
19
  import { homedir } from "node:os";
20
20
  import { dirname, join, parse, resolve } from "node:path";
21
+ function resetLegacyNoticesForTest() {
22
+ warnedLegacyConfigDir = false;
23
+ warnedLegacyLocalPin = false;
24
+ }
25
+ function noteLegacyConfigDir(path) {
26
+ if (warnedLegacyConfigDir)
27
+ return;
28
+ warnedLegacyConfigDir = true;
29
+ 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.`);
30
+ }
31
+ function noteLegacyLocalPin(path) {
32
+ if (warnedLegacyLocalPin)
33
+ return;
34
+ warnedLegacyLocalPin = true;
35
+ 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.`);
36
+ }
37
+ function noteLocalPinRename(from, to) {
38
+ console.error(`Harmony: wrote the repo pin to ${to} (the pre-#1082 ${from} is now ignored and can be deleted).`);
39
+ }
40
+ function getHmyRootDir() {
41
+ return join(homedir(), CONFIG_DIR_NAME);
42
+ }
21
43
  function getConfigDir() {
22
- return join(homedir(), ".harmony-mcp");
44
+ return join(getHmyRootDir(), CONFIG_DIR_SUBDIR);
45
+ }
46
+ function getLegacyConfigDir() {
47
+ return join(homedir(), LEGACY_CONFIG_DIR_NAME);
23
48
  }
24
49
  function getConfigPath() {
25
50
  return join(getConfigDir(), "config.json");
26
51
  }
52
+ function getLegacyConfigPath() {
53
+ return join(getLegacyConfigDir(), "config.json");
54
+ }
27
55
  function getLocalConfigPath(cwd) {
28
56
  return join(cwd || process.cwd(), LOCAL_CONFIG_FILENAME);
29
57
  }
@@ -33,9 +61,14 @@ function findLocalConfigPath(cwd) {
33
61
  const { root } = parse(dir);
34
62
  for (;; ) {
35
63
  if (dir !== home && dir !== root) {
36
- const candidate = join(dir, LOCAL_CONFIG_FILENAME);
37
- if (existsSync(candidate))
38
- return candidate;
64
+ const current = join(dir, LOCAL_CONFIG_FILENAME);
65
+ if (existsSync(current))
66
+ return current;
67
+ const legacy = join(dir, LEGACY_LOCAL_CONFIG_FILENAME);
68
+ if (existsSync(legacy)) {
69
+ noteLegacyLocalPin(legacy);
70
+ return legacy;
71
+ }
39
72
  }
40
73
  const parent = dirname(dir);
41
74
  if (parent === dir)
@@ -58,9 +91,12 @@ function emptyConfig() {
58
91
  };
59
92
  }
60
93
  function loadConfig() {
61
- const configPath = getConfigPath();
94
+ let configPath = getConfigPath();
62
95
  if (!existsSync(configPath)) {
63
- return emptyConfig();
96
+ configPath = getLegacyConfigPath();
97
+ if (!existsSync(configPath))
98
+ return emptyConfig();
99
+ noteLegacyConfigDir(configPath);
64
100
  }
65
101
  try {
66
102
  const data = readFileSync(configPath, "utf-8");
@@ -110,7 +146,11 @@ function loadLocalConfig(cwd) {
110
146
  }
111
147
  }
112
148
  function saveLocalConfig(config, cwd) {
113
- const localConfigPath = findLocalConfigPath(cwd) ?? getLocalConfigPath(cwd);
149
+ const foundPath = findLocalConfigPath(cwd);
150
+ const localConfigPath = foundPath ? join(dirname(foundPath), LOCAL_CONFIG_FILENAME) : getLocalConfigPath(cwd);
151
+ if (foundPath !== null && foundPath !== localConfigPath) {
152
+ noteLocalPinRename(foundPath, localConfigPath);
153
+ }
114
154
  const existingConfig = loadLocalConfig(cwd) || {
115
155
  workspaceId: null,
116
156
  projectId: null
@@ -122,6 +162,7 @@ function saveLocalConfig(config, cwd) {
122
162
  if (newConfig.projectId)
123
163
  cleanConfig.projectId = newConfig.projectId;
124
164
  writeFileSync(localConfigPath, JSON.stringify(cleanConfig, null, 2));
165
+ return localConfigPath;
125
166
  }
126
167
  function hasLocalConfig(cwd) {
127
168
  return findLocalConfigPath(cwd) !== null;
@@ -259,7 +300,7 @@ function getMemoryDir() {
259
300
  return config.memoryDir;
260
301
  return join(homedir(), ".harmony", "memory");
261
302
  }
262
- var DEFAULT_API_URL = "https://app.gethmy.com/api", LOCAL_CONFIG_FILENAME = ".harmony-mcp.json";
303
+ 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
304
  var init_config = () => {};
264
305
 
265
306
  // src/oauth-login.ts
@@ -973,6 +1014,74 @@ var REVIEW_DISALLOWED_TOOLS = [
973
1014
  "mcp__harmony__harmony_delete_subtask",
974
1015
  "mcp__harmony__harmony_toggle_subtask"
975
1016
  ];
1017
+ // ../harmony-shared/dist/runRedaction.js
1018
+ var REDACTION_MARK = "«redacted»";
1019
+ var CONFIG_SCOPED_SEGMENTS = new Set([
1020
+ "gh",
1021
+ "gcloud",
1022
+ "op",
1023
+ "anthropic"
1024
+ ]);
1025
+ var SENSITIVE_BASENAMES = new Set([
1026
+ ".netrc",
1027
+ "_netrc",
1028
+ ".npmrc",
1029
+ ".pgpass",
1030
+ ".git-credentials",
1031
+ ".htpasswd",
1032
+ ".claude.json",
1033
+ "credentials",
1034
+ ".credentials",
1035
+ "credentials.json",
1036
+ ".credentials.json",
1037
+ "credentials.yml",
1038
+ "credentials.yaml",
1039
+ "auth.json",
1040
+ ".auth.json",
1041
+ "secrets",
1042
+ "secrets.json",
1043
+ "secrets.yaml",
1044
+ "secrets.yml",
1045
+ "id_rsa",
1046
+ "id_dsa",
1047
+ "id_ecdsa",
1048
+ "id_ed25519",
1049
+ "known_hosts"
1050
+ ]);
1051
+ var SECRET_PATTERNS = [
1052
+ {
1053
+ pattern: /-----BEGIN[^-]*PRIVATE KEY-----[\s\S]*?-----END[^-]*-----/g,
1054
+ replace: REDACTION_MARK
1055
+ },
1056
+ { pattern: /\bhmy_at_[A-Za-z0-9_-]{8,}/g, replace: REDACTION_MARK },
1057
+ { pattern: /\bhmy_[A-Za-z0-9_-]{8,}/g, replace: REDACTION_MARK },
1058
+ { pattern: /\bsk-(?:ant-)?[A-Za-z0-9_-]{16,}/g, replace: REDACTION_MARK },
1059
+ { pattern: /\bgh[pousr]_[A-Za-z0-9]{16,}/g, replace: REDACTION_MARK },
1060
+ { pattern: /\bgithub_pat_[A-Za-z0-9_]{20,}/g, replace: REDACTION_MARK },
1061
+ { pattern: /\bxox[abprs]-[A-Za-z0-9-]{10,}/g, replace: REDACTION_MARK },
1062
+ { pattern: /\bAKIA[0-9A-Z]{16}\b/g, replace: REDACTION_MARK },
1063
+ { pattern: /\bAIza[0-9A-Za-z_-]{20,}/g, replace: REDACTION_MARK },
1064
+ {
1065
+ pattern: /\beyJ[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}/g,
1066
+ replace: REDACTION_MARK
1067
+ },
1068
+ {
1069
+ pattern: /\b(Bearer|Basic|Token)\s+[A-Za-z0-9._~+/=-]{12,}/gi,
1070
+ replace: `$1 ${REDACTION_MARK}`
1071
+ },
1072
+ {
1073
+ pattern: /(\w{1,32}:\/\/)[^/\s:@]+:[^/\s@]+@/g,
1074
+ replace: `$1${REDACTION_MARK}@`
1075
+ },
1076
+ {
1077
+ 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,
1078
+ replace: `$1=${REDACTION_MARK}`
1079
+ },
1080
+ {
1081
+ pattern: /(--?(?:password|passwd|token|api-?key|secret|auth)(?:=|\s+))(?:"[^"]*"|'[^']*'|[^\s]+)/gi,
1082
+ replace: `$1${REDACTION_MARK}`
1083
+ }
1084
+ ];
976
1085
  // ../harmony-shared/dist/stageHandoff.js
977
1086
  var HANDOFF_MARKER = "harmony:stage-handoff";
978
1087
  var HANDOFF_BLOCK_RE = new RegExp("```json\\s*\\n//\\s*" + HANDOFF_MARKER + "\\s*\\n([\\s\\S]*?)\\n```", "m");
@@ -18,12 +18,40 @@ var __esm = (fn, res) => () => (fn && (res = fn(fn = 0)), res);
18
18
  import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
19
19
  import { homedir } from "node:os";
20
20
  import { dirname, join, parse, resolve } from "node:path";
21
+ function resetLegacyNoticesForTest() {
22
+ warnedLegacyConfigDir = false;
23
+ warnedLegacyLocalPin = false;
24
+ }
25
+ function noteLegacyConfigDir(path) {
26
+ if (warnedLegacyConfigDir)
27
+ return;
28
+ warnedLegacyConfigDir = true;
29
+ 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.`);
30
+ }
31
+ function noteLegacyLocalPin(path) {
32
+ if (warnedLegacyLocalPin)
33
+ return;
34
+ warnedLegacyLocalPin = true;
35
+ 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.`);
36
+ }
37
+ function noteLocalPinRename(from, to) {
38
+ console.error(`Harmony: wrote the repo pin to ${to} (the pre-#1082 ${from} is now ignored and can be deleted).`);
39
+ }
40
+ function getHmyRootDir() {
41
+ return join(homedir(), CONFIG_DIR_NAME);
42
+ }
21
43
  function getConfigDir() {
22
- return join(homedir(), ".harmony-mcp");
44
+ return join(getHmyRootDir(), CONFIG_DIR_SUBDIR);
45
+ }
46
+ function getLegacyConfigDir() {
47
+ return join(homedir(), LEGACY_CONFIG_DIR_NAME);
23
48
  }
24
49
  function getConfigPath() {
25
50
  return join(getConfigDir(), "config.json");
26
51
  }
52
+ function getLegacyConfigPath() {
53
+ return join(getLegacyConfigDir(), "config.json");
54
+ }
27
55
  function getLocalConfigPath(cwd) {
28
56
  return join(cwd || process.cwd(), LOCAL_CONFIG_FILENAME);
29
57
  }
@@ -33,9 +61,14 @@ function findLocalConfigPath(cwd) {
33
61
  const { root } = parse(dir);
34
62
  for (;; ) {
35
63
  if (dir !== home && dir !== root) {
36
- const candidate = join(dir, LOCAL_CONFIG_FILENAME);
37
- if (existsSync(candidate))
38
- return candidate;
64
+ const current = join(dir, LOCAL_CONFIG_FILENAME);
65
+ if (existsSync(current))
66
+ return current;
67
+ const legacy = join(dir, LEGACY_LOCAL_CONFIG_FILENAME);
68
+ if (existsSync(legacy)) {
69
+ noteLegacyLocalPin(legacy);
70
+ return legacy;
71
+ }
39
72
  }
40
73
  const parent = dirname(dir);
41
74
  if (parent === dir)
@@ -58,9 +91,12 @@ function emptyConfig() {
58
91
  };
59
92
  }
60
93
  function loadConfig() {
61
- const configPath = getConfigPath();
94
+ let configPath = getConfigPath();
62
95
  if (!existsSync(configPath)) {
63
- return emptyConfig();
96
+ configPath = getLegacyConfigPath();
97
+ if (!existsSync(configPath))
98
+ return emptyConfig();
99
+ noteLegacyConfigDir(configPath);
64
100
  }
65
101
  try {
66
102
  const data = readFileSync(configPath, "utf-8");
@@ -110,7 +146,11 @@ function loadLocalConfig(cwd) {
110
146
  }
111
147
  }
112
148
  function saveLocalConfig(config, cwd) {
113
- const localConfigPath = findLocalConfigPath(cwd) ?? getLocalConfigPath(cwd);
149
+ const foundPath = findLocalConfigPath(cwd);
150
+ const localConfigPath = foundPath ? join(dirname(foundPath), LOCAL_CONFIG_FILENAME) : getLocalConfigPath(cwd);
151
+ if (foundPath !== null && foundPath !== localConfigPath) {
152
+ noteLocalPinRename(foundPath, localConfigPath);
153
+ }
114
154
  const existingConfig = loadLocalConfig(cwd) || {
115
155
  workspaceId: null,
116
156
  projectId: null
@@ -122,6 +162,7 @@ function saveLocalConfig(config, cwd) {
122
162
  if (newConfig.projectId)
123
163
  cleanConfig.projectId = newConfig.projectId;
124
164
  writeFileSync(localConfigPath, JSON.stringify(cleanConfig, null, 2));
165
+ return localConfigPath;
125
166
  }
126
167
  function hasLocalConfig(cwd) {
127
168
  return findLocalConfigPath(cwd) !== null;
@@ -259,7 +300,7 @@ function getMemoryDir() {
259
300
  return config.memoryDir;
260
301
  return join(homedir(), ".harmony", "memory");
261
302
  }
262
- var DEFAULT_API_URL = "https://app.gethmy.com/api", LOCAL_CONFIG_FILENAME = ".harmony-mcp.json";
303
+ 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
304
  var init_config = () => {};
264
305
  init_config();
265
306
 
@@ -269,6 +310,8 @@ export {
269
310
  setActiveContext,
270
311
  saveLocalConfig,
271
312
  saveConfig,
313
+ resetLegacyNoticesForTest,
314
+ noteLegacyLocalPin,
272
315
  loadLocalConfig,
273
316
  loadConfig,
274
317
  isConfigured,
@@ -277,6 +320,9 @@ export {
277
320
  getUserEmail,
278
321
  getMemoryDir,
279
322
  getLocalConfigPath,
323
+ getLegacyConfigPath,
324
+ getLegacyConfigDir,
325
+ getHmyRootDir,
280
326
  getConfigPath,
281
327
  getConfigDir,
282
328
  getApiUrl,
@@ -287,5 +333,6 @@ export {
287
333
  getActiveContext,
288
334
  findLocalConfigPath,
289
335
  describeActiveContext,
290
- areSkillsInstalled
336
+ areSkillsInstalled,
337
+ LEGACY_LOCAL_CONFIG_FILENAME
291
338
  };