@elitedcs/ghl-mcp 3.54.0 → 3.55.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.
Files changed (27) hide show
  1. package/CHANGELOG.md +18 -0
  2. package/README.md +8 -6
  3. package/dist/index.js +307 -145
  4. package/package.json +4 -3
  5. package/skills/blueprint/README.md +27 -0
  6. package/skills/blueprint/SKILL.md +149 -0
  7. package/skills/blueprint/examples/medspa-approval-view.md +92 -0
  8. package/skills/blueprint/examples/medspa-brief.json +52 -0
  9. package/skills/blueprint/examples/medspa-build-plan.json +265 -0
  10. package/skills/blueprint/examples/medspa-dry-run-report.md +67 -0
  11. package/skills/blueprint/examples/sample-approval-view.md +82 -0
  12. package/skills/blueprint/examples/sample-brief.json +13 -0
  13. package/skills/blueprint/examples/sample-build-plan.json +227 -0
  14. package/skills/blueprint/examples/validate-plan.cjs +127 -0
  15. package/skills/blueprint/presets/clinic-launch-a2p.md +39 -0
  16. package/skills/blueprint/presets/clinic-launch-a2p.preset.json +337 -0
  17. package/skills/blueprint/presets/generic-client.md +36 -0
  18. package/skills/blueprint/presets/generic-client.preset.json +255 -0
  19. package/skills/blueprint/presets/med-spa.md +59 -0
  20. package/skills/blueprint/presets/med-spa.preset.json +264 -0
  21. package/skills/blueprint/references/agency-os-detection.md +80 -0
  22. package/skills/blueprint/references/approval-view.md +83 -0
  23. package/skills/blueprint/references/brief-schema.md +45 -0
  24. package/skills/blueprint/references/build-plan-schema.md +52 -0
  25. package/skills/blueprint/references/external-funnel.md +310 -0
  26. package/skills/blueprint/references/intake-question-set.md +141 -0
  27. package/skills/blueprint/references/preset-format.md +130 -0
package/dist/index.js CHANGED
@@ -114,8 +114,8 @@ var init_ghl_client = __esm({
114
114
  Version: version || GHL_API_VERSION
115
115
  };
116
116
  }
117
- buildUrl(path11, params) {
118
- const url = new URL(path11, GHL_BASE_URL);
117
+ buildUrl(path12, params) {
118
+ const url = new URL(path12, GHL_BASE_URL);
119
119
  if (params) {
120
120
  for (const [key, value] of Object.entries(params)) {
121
121
  if (value !== void 0 && value !== null) {
@@ -125,8 +125,8 @@ var init_ghl_client = __esm({
125
125
  }
126
126
  return url.toString();
127
127
  }
128
- async request(method, path11, options = {}, attempt = 0) {
129
- const url = this.buildUrl(path11, options.params);
128
+ async request(method, path12, options = {}, attempt = 0) {
129
+ const url = this.buildUrl(path12, options.params);
130
130
  const headers = this.buildHeaders(options.version);
131
131
  const fetchOptions = {
132
132
  method,
@@ -144,14 +144,14 @@ var init_ghl_client = __esm({
144
144
  } catch (error) {
145
145
  clearTimeout(timeout);
146
146
  if (error instanceof Error && error.name === "AbortError") {
147
- throw new Error(`Request timeout (30s): ${method} ${path11}`);
147
+ throw new Error(`Request timeout (30s): ${method} ${path12}`);
148
148
  }
149
149
  if (!options.noRetry && attempt < MAX_RETRIES) {
150
150
  const delay4 = computeRetryDelay(null, attempt, BASE_DELAY_MS);
151
- process.stderr.write(`[ghl-mcp] Network error on ${method} ${path11}, retry ${attempt + 1}/${MAX_RETRIES} in ${delay4}ms
151
+ process.stderr.write(`[ghl-mcp] Network error on ${method} ${path12}, retry ${attempt + 1}/${MAX_RETRIES} in ${delay4}ms
152
152
  `);
153
153
  await new Promise((r) => setTimeout(r, delay4));
154
- return this.request(method, path11, options, attempt + 1);
154
+ return this.request(method, path12, options, attempt + 1);
155
155
  }
156
156
  throw error;
157
157
  } finally {
@@ -159,10 +159,10 @@ var init_ghl_client = __esm({
159
159
  }
160
160
  if (!options.noRetry && (response.status === 429 || response.status >= 500) && attempt < MAX_RETRIES) {
161
161
  const delay4 = computeRetryDelay(response.headers.get("Retry-After"), attempt, BASE_DELAY_MS);
162
- process.stderr.write(`[ghl-mcp] ${response.status} on ${method} ${path11}, retry ${attempt + 1}/${MAX_RETRIES} in ${delay4}ms
162
+ process.stderr.write(`[ghl-mcp] ${response.status} on ${method} ${path12}, retry ${attempt + 1}/${MAX_RETRIES} in ${delay4}ms
163
163
  `);
164
164
  await new Promise((r) => setTimeout(r, delay4));
165
- return this.request(method, path11, options, attempt + 1);
165
+ return this.request(method, path12, options, attempt + 1);
166
166
  }
167
167
  if (!response.ok) {
168
168
  let errorBody = "";
@@ -171,7 +171,7 @@ var init_ghl_client = __esm({
171
171
  } catch {
172
172
  }
173
173
  throw new Error(
174
- `GHL API Error ${response.status} ${response.statusText}: ${method} ${path11}
174
+ `GHL API Error ${response.status} ${response.statusText}: ${method} ${path12}
175
175
  ${errorBody}`
176
176
  );
177
177
  }
@@ -183,20 +183,20 @@ ${errorBody}`
183
183
  return { message: text };
184
184
  }
185
185
  }
186
- async get(path11, options) {
187
- return this.request("GET", path11, options);
186
+ async get(path12, options) {
187
+ return this.request("GET", path12, options);
188
188
  }
189
- async post(path11, options) {
190
- return this.request("POST", path11, options);
189
+ async post(path12, options) {
190
+ return this.request("POST", path12, options);
191
191
  }
192
- async put(path11, options) {
193
- return this.request("PUT", path11, options);
192
+ async put(path12, options) {
193
+ return this.request("PUT", path12, options);
194
194
  }
195
- async patch(path11, options) {
196
- return this.request("PATCH", path11, options);
195
+ async patch(path12, options) {
196
+ return this.request("PATCH", path12, options);
197
197
  }
198
- async delete(path11, options) {
199
- return this.request("DELETE", path11, options);
198
+ async delete(path12, options) {
199
+ return this.request("DELETE", path12, options);
200
200
  }
201
201
  /**
202
202
  * Helper: resolves locationId from args or falls back to default
@@ -998,9 +998,9 @@ function planFromPayload(payload) {
998
998
  return payload?.plan === "command-os" ? "command-os" : "mcp";
999
999
  }
1000
1000
  function legacyDeviceFingerprint() {
1001
- const os5 = require("node:os");
1001
+ const os6 = require("node:os");
1002
1002
  const crypto4 = require("node:crypto");
1003
- const raw = `${os5.hostname()}:${os5.userInfo().username}:${os5.platform()}:${os5.arch()}`;
1003
+ const raw = `${os6.hostname()}:${os6.userInfo().username}:${os6.platform()}:${os6.arch()}`;
1004
1004
  return crypto4.createHash("sha256").update(raw).digest("hex").slice(0, 16);
1005
1005
  }
1006
1006
  function deviceFingerprint(opts) {
@@ -1235,7 +1235,7 @@ async function validateFirebase(firebaseKey, refreshToken) {
1235
1235
  function registerSetupTool(server2) {
1236
1236
  server2.tool(
1237
1237
  "setup_ghl_mcp",
1238
- "First-run setup for GHL Command MCP. Validates your license and GHL credentials, then writes them to a per-user credentials file. Restart Claude after this completes to load all 234 tools (180 if you skip the optional Firebase fields; add Firebase later with enable_workflow_builder).",
1238
+ "First-run setup for GHL Command MCP. Validates your license and GHL credentials, then writes them to a per-user credentials file. Restart Claude after this completes to load all 235 tools (181 if you skip the optional Firebase fields; add Firebase later with enable_workflow_builder).",
1239
1239
  {
1240
1240
  email: import_zod40.z.string().email().describe("Email used at purchase."),
1241
1241
  license_key: import_zod40.z.string().min(20).describe("License key from your purchase email."),
@@ -1317,11 +1317,11 @@ Note: Firebase credentials rejected (${fb.error}). Saved without Workflow Builde
1317
1317
  signed_attestation: lic.signedAttestation
1318
1318
  });
1319
1319
  const isFree = lic.tier === "free";
1320
- const toolCount = isFree ? workflowBuilderEnabled ? "106" : "88" : workflowBuilderEnabled ? "234" : "180";
1320
+ const toolCount = isFree ? workflowBuilderEnabled ? "107" : "89" : workflowBuilderEnabled ? "235" : "181";
1321
1321
  const wfLine = workflowBuilderEnabled ? "Workflow Builder: enabled." : "Workflow Builder: not configured (optional).";
1322
1322
  const wfTip = workflowBuilderEnabled ? "" : isFree ? `
1323
1323
  Unlock the account auditor next (the free tier's best tool): say "Unlock the Workflow Builder" \u2014 one browser login enables audit_workflows, validate_workflow, and the full-detail reads.` : "\nTo enable Workflow Builder later (52 extra Firebase-gated tools): run enable_workflow_builder with your three Firebase values. No need to re-enter license/API key/location ID.";
1324
- const freeTip = isFree ? "\n\nFree tier: 106 read-only tools (88 before the one-login auditor unlock). Write tools stay visible but answer with upgrade info instead of acting. Full version ($97/mo founding rate, same key upgrades in place): https://ghlcommand.com" : "";
1324
+ const freeTip = isFree ? "\n\nFree tier: 107 read-only tools (89 before the one-login auditor unlock). Write tools stay visible but answer with upgrade info instead of acting. Full version ($97/mo founding rate, same key upgrades in place): https://ghlcommand.com" : "";
1325
1325
  return {
1326
1326
  content: [{
1327
1327
  type: "text",
@@ -1347,7 +1347,7 @@ Unlock the account auditor next (the free tier's best tool): say "Unlock the Wor
1347
1347
  function registerEnableWorkflowBuilderTool(server2) {
1348
1348
  server2.tool(
1349
1349
  "enable_workflow_builder",
1350
- "Add Firebase credentials to an existing GHL Command install to unlock 54 additional tools across the internal-API modules: workflow builder (create/edit/clone/delete/publish/validate workflows, build_if_else_branch, build_goal_event, get_trigger_registry), funnel + page builder, form builder, pipeline builder, workflow cloner, smart lists, reputation, email campaigns, email templates, and memberships, plus the pre-deploy validator. On the FREE tier this same login unlocks the read-only auditor suite (audit_workflows, validate_workflow, full-detail workflow/funnel/pipeline reads). Requires you've already run setup_ghl_mcp. EASIEST PATH: run `capture_firebase_interactive` instead \u2014 a Chrome window opens, you log into GHL, zero pasting. Use THIS tool when you have JSON from `auto_capture_firebase_script` (console-paste path) to put in `firebase_paste`, or the three manual DevTools fields. Tool count goes from 180 to 234 after the next Claude restart.",
1350
+ "Add Firebase credentials to an existing GHL Command install to unlock 54 additional tools across the internal-API modules: workflow builder (create/edit/clone/delete/publish/validate workflows, build_if_else_branch, build_goal_event, get_trigger_registry), funnel + page builder, form builder, pipeline builder, workflow cloner, smart lists, reputation, email campaigns, email templates, and memberships, plus the pre-deploy validator. On the FREE tier this same login unlocks the read-only auditor suite (audit_workflows, validate_workflow, full-detail workflow/funnel/pipeline reads). Requires you've already run setup_ghl_mcp. EASIEST PATH: run `capture_firebase_interactive` instead \u2014 a Chrome window opens, you log into GHL, zero pasting. Use THIS tool when you have JSON from `auto_capture_firebase_script` (console-paste path) to put in `firebase_paste`, or the three manual DevTools fields. Tool count goes from 181 to 235 after the next Claude restart.",
1351
1351
  {
1352
1352
  // v3.25.0: one-paste path. Tool runs `auto_capture_firebase_script` to
1353
1353
  // get the console script; the script returns a JSON object that pastes
@@ -1428,7 +1428,7 @@ DevTools steps: https://elitedcs.com/ghl-mcp-firebase`
1428
1428
  "",
1429
1429
  "**You MUST restart Claude before using any workflow-builder tool.** Quit Claude completely (Cmd+Q on Mac, full exit on Windows) and reopen. Without a restart, the workflow builder tools will keep using the OLD Firebase auth from before this call and fail with 401 errors \u2014 even though this tool reported success.",
1430
1430
  "",
1431
- 'After restart, all 234 tools load. Try: "List my workflows in full detail" or "Validate workflow <id>".',
1431
+ 'After restart, all 235 tools load. Try: "List my workflows in full detail" or "Validate workflow <id>".',
1432
1432
  "",
1433
1433
  "Note: Firebase refresh tokens rotate every few weeks. If workflow tools stop working in a few weeks (run `health_check` to confirm Firebase auth: FAIL), run `auto_capture_firebase_script` for fresh values and re-run this tool with the new firebase_paste."
1434
1434
  ].join("\n")
@@ -1524,7 +1524,7 @@ The login in the capture window may belong to the wrong GHL account, or the sess
1524
1524
  "",
1525
1525
  "**You MUST restart Claude before using any workflow-builder tool.** Quit Claude completely (Cmd+Q on Mac, full exit on Windows) and reopen.",
1526
1526
  "",
1527
- 'After restart, all 234 tools load. Try: "List my workflows in full detail".',
1527
+ 'After restart, all 235 tools load. Try: "List my workflows in full detail".',
1528
1528
  "",
1529
1529
  "Future token rotations re-capture silently \u2014 if workflow tools ever 401, just run capture_firebase_interactive again; no window should appear."
1530
1530
  ].join("\n")
@@ -2102,9 +2102,9 @@ var require_package = __commonJS({
2102
2102
  "package.json"(exports2, module2) {
2103
2103
  module2.exports = {
2104
2104
  name: "@elitedcs/ghl-mcp",
2105
- version: "3.54.0",
2105
+ version: "3.55.0",
2106
2106
  mcpName: "io.github.drjerryrelth/ghl-command",
2107
- description: "GoHighLevel MCP Server for Claude. 234 tools \u2014 full CRM, automation, marketing control, account-wide workflow audit, live funnel-capture verification, and the only programmatic GHL workflow builder, now multi-tenant across client accounts.",
2107
+ description: "GoHighLevel MCP Server for Claude. 235 tools \u2014 full CRM, automation, marketing control, account-wide workflow audit, live funnel-capture verification, and the only programmatic GHL workflow builder, now multi-tenant across client accounts.",
2108
2108
  main: "dist/index.js",
2109
2109
  bin: {
2110
2110
  "ghl-mcp": "dist/index.js"
@@ -2118,7 +2118,8 @@ var require_package = __commonJS({
2118
2118
  "templates/external-funnel/cloudflare-worker.js",
2119
2119
  "templates/external-funnel/README.md",
2120
2120
  "README.md",
2121
- "CHANGELOG.md"
2121
+ "CHANGELOG.md",
2122
+ "skills"
2122
2123
  ],
2123
2124
  scripts: {
2124
2125
  build: "esbuild src/index.ts --bundle --platform=node --target=node20 --format=cjs --outfile=dist/index.js --packages=external && esbuild src/capture-helper.ts --bundle --platform=node --target=node20 --format=cjs --outfile=dist/capture-helper.js --packages=external",
@@ -2191,7 +2192,7 @@ function launchCommand(port = 7300) {
2191
2192
  return `npx -y @elitedcs/ghl-mcp@latest dashboard --port=${port}`;
2192
2193
  }
2193
2194
  function installingEntry() {
2194
- return path7.join(__dirname, "index.js");
2195
+ return path8.join(__dirname, "index.js");
2195
2196
  }
2196
2197
  function planLauncher(platform2, home, port = 7300, entry = installingEntry()) {
2197
2198
  const cmd = launchCommand(port);
@@ -2199,12 +2200,12 @@ function planLauncher(platform2, home, port = 7300, entry = installingEntry()) {
2199
2200
  const systemApps = "/Applications";
2200
2201
  let base = home;
2201
2202
  try {
2202
- fs7.accessSync(systemApps, fs7.constants.W_OK);
2203
+ fs8.accessSync(systemApps, fs8.constants.W_OK);
2203
2204
  base = "";
2204
2205
  } catch {
2205
2206
  }
2206
- const appDir = base ? path7.join(home, "Applications", "Command OS.app") : path7.join(systemApps, "Command OS.app");
2207
- const macOSDir = path7.join(appDir, "Contents", "MacOS");
2207
+ const appDir = base ? path8.join(home, "Applications", "Command OS.app") : path8.join(systemApps, "Command OS.app");
2208
+ const macOSDir = path8.join(appDir, "Contents", "MacOS");
2208
2209
  const script = [
2209
2210
  "#!/bin/bash",
2210
2211
  "# GHL Command \u2014 Command OS launcher (regenerate: ghl-mcp install-launcher)",
@@ -2233,13 +2234,13 @@ function planLauncher(platform2, home, port = 7300, entry = installingEntry()) {
2233
2234
  targetPath: appDir,
2234
2235
  humanLocation: base ? "your personal Applications folder at ~/Applications (Finder \u2192 Go \u2192 Home \u2192 Applications)" : "your Applications folder \u2014 open Finder \u2192 Applications and look for \u201CCommand OS\u201D",
2235
2236
  files: [
2236
- { path: path7.join(macOSDir, "command-os"), contents: script, executable: true },
2237
- { path: path7.join(appDir, "Contents", "Info.plist"), contents: plist, executable: false }
2237
+ { path: path8.join(macOSDir, "command-os"), contents: script, executable: true },
2238
+ { path: path8.join(appDir, "Contents", "Info.plist"), contents: plist, executable: false }
2238
2239
  ]
2239
2240
  };
2240
2241
  }
2241
2242
  if (platform2 === "win32") {
2242
- const target2 = path7.join(home, "Desktop", "Command OS.cmd");
2243
+ const target2 = path8.join(home, "Desktop", "Command OS.cmd");
2243
2244
  return {
2244
2245
  platform: platform2,
2245
2246
  targetPath: target2,
@@ -2252,7 +2253,7 @@ function planLauncher(platform2, home, port = 7300, entry = installingEntry()) {
2252
2253
  ].join("\r\n"), executable: false }]
2253
2254
  };
2254
2255
  }
2255
- const target = path7.join(home, ".local", "share", "applications", "command-os.desktop");
2256
+ const target = path8.join(home, ".local", "share", "applications", "command-os.desktop");
2256
2257
  return {
2257
2258
  platform: platform2,
2258
2259
  targetPath: target,
@@ -2276,11 +2277,11 @@ function planLauncher(platform2, home, port = 7300, entry = installingEntry()) {
2276
2277
  function installLauncher(argv = []) {
2277
2278
  const portArg = argv.find((a) => a.startsWith("--port="));
2278
2279
  const port = portArg ? Number(portArg.split("=")[1]) : 7300;
2279
- const plan = planLauncher(process.platform, os3.homedir(), port, installingEntry());
2280
+ const plan = planLauncher(process.platform, os4.homedir(), port, installingEntry());
2280
2281
  try {
2281
2282
  for (const f of plan.files) {
2282
- fs7.mkdirSync(path7.dirname(f.path), { recursive: true });
2283
- fs7.writeFileSync(f.path, f.contents, { mode: f.executable ? 493 : 420 });
2283
+ fs8.mkdirSync(path8.dirname(f.path), { recursive: true });
2284
+ fs8.writeFileSync(f.path, f.contents, { mode: f.executable ? 493 : 420 });
2284
2285
  }
2285
2286
  } catch (e) {
2286
2287
  process.stderr.write(`
@@ -2300,13 +2301,13 @@ function installLauncher(argv = []) {
2300
2301
  ].join("\n"));
2301
2302
  return 0;
2302
2303
  }
2303
- var fs7, os3, path7;
2304
+ var fs8, os4, path8;
2304
2305
  var init_launcher = __esm({
2305
2306
  "src/launcher.ts"() {
2306
2307
  "use strict";
2307
- fs7 = __toESM(require("fs"));
2308
- os3 = __toESM(require("os"));
2309
- path7 = __toESM(require("path"));
2308
+ fs8 = __toESM(require("fs"));
2309
+ os4 = __toESM(require("os"));
2310
+ path8 = __toESM(require("path"));
2310
2311
  }
2311
2312
  });
2312
2313
 
@@ -2335,10 +2336,10 @@ function stageBlockedBy(stages, stage) {
2335
2336
  return null;
2336
2337
  }
2337
2338
  function claudeBin() {
2338
- const fallback = path8.join(os4.homedir(), ".local", "bin", "claude");
2339
- const fromPath = (process.env.PATH || "").split(path8.delimiter).map((d) => path8.join(d, "claude")).find((p) => {
2339
+ const fallback = path9.join(os5.homedir(), ".local", "bin", "claude");
2340
+ const fromPath = (process.env.PATH || "").split(path9.delimiter).map((d) => path9.join(d, "claude")).find((p) => {
2340
2341
  try {
2341
- fs8.accessSync(p, fs8.constants.X_OK);
2342
+ fs9.accessSync(p, fs9.constants.X_OK);
2342
2343
  return true;
2343
2344
  } catch {
2344
2345
  return false;
@@ -2346,7 +2347,7 @@ function claudeBin() {
2346
2347
  });
2347
2348
  if (fromPath) return fromPath;
2348
2349
  try {
2349
- fs8.accessSync(fallback, fs8.constants.X_OK);
2350
+ fs9.accessSync(fallback, fs9.constants.X_OK);
2350
2351
  return fallback;
2351
2352
  } catch {
2352
2353
  }
@@ -2395,7 +2396,7 @@ function runStage(locationId2, stage, onEvent, opts = {}) {
2395
2396
  const locationName = SANDBOX_ALLOWLIST[locationId2] || opts.name || locationId2;
2396
2397
  const spec = STAGE_SPECS[stage];
2397
2398
  if (!spec) return Promise.reject(new Error(`Stage ${stage} is not powered yet.`));
2398
- return new Promise((resolve5) => {
2399
+ return new Promise((resolve6) => {
2399
2400
  const bin = claudeBin();
2400
2401
  onEvent({ kind: "status", line: `Starting ${spec.name} on ${locationName} (headless Claude, ${spec.allowedTools.length} tools allowed)\u2026` });
2401
2402
  const child = (0, import_child_process2.spawn)(bin, buildClaudeArgs(spec, locationId2, locationName, opts.context), {
@@ -2441,31 +2442,31 @@ function runStage(locationId2, stage, onEvent, opts = {}) {
2441
2442
  if (code !== 0 && !lastText) {
2442
2443
  const outcome2 = { ok: false, error: `claude exited ${code}: ${stderrTail.trim().slice(-200) || "no output"}` };
2443
2444
  onEvent({ kind: "error", line: outcome2.error });
2444
- resolve5(outcome2);
2445
+ resolve6(outcome2);
2445
2446
  return;
2446
2447
  }
2447
2448
  const outcome = parseResultLine(lastText);
2448
2449
  const detail = outcome.error || (outcome.issues?.length ? outcome.issues.join(" \xB7 ") : "") || outcome.summary || "no detail reported";
2449
2450
  const success = outcome.formId ? `Done: form "${outcome.formName}" (${outcome.formId})` : `Done: ${outcome.summary ?? "stage complete"}`;
2450
2451
  onEvent({ kind: "result", line: outcome.ok ? success : `Not passed: ${detail}` });
2451
- resolve5(outcome);
2452
+ resolve6(outcome);
2452
2453
  });
2453
2454
  child.on("error", (err) => {
2454
2455
  clearTimeout(timeout);
2455
2456
  const outcome = { ok: false, error: `could not start claude: ${err.message}` };
2456
2457
  onEvent({ kind: "error", line: outcome.error });
2457
- resolve5(outcome);
2458
+ resolve6(outcome);
2458
2459
  });
2459
2460
  });
2460
2461
  }
2461
- var import_child_process2, fs8, os4, path8, SANDBOX_ALLOWLIST, PROTECTED_LOCATIONS, STAGE_SPECS, HUMAN_GATE_STAGES;
2462
+ var import_child_process2, fs9, os5, path9, SANDBOX_ALLOWLIST, PROTECTED_LOCATIONS, STAGE_SPECS, HUMAN_GATE_STAGES;
2462
2463
  var init_stage_runner = __esm({
2463
2464
  "src/stage-runner.ts"() {
2464
2465
  "use strict";
2465
2466
  import_child_process2 = require("child_process");
2466
- fs8 = __toESM(require("fs"));
2467
- os4 = __toESM(require("os"));
2468
- path8 = __toESM(require("path"));
2467
+ fs9 = __toESM(require("fs"));
2468
+ os5 = __toESM(require("os"));
2469
+ path9 = __toESM(require("path"));
2469
2470
  SANDBOX_ALLOWLIST = {
2470
2471
  JrV2p35O3hY2wqhr2c0T: "MCP Testing",
2471
2472
  jHP5wkYRineXDlzAOEbW: "Blueprint Demo"
@@ -3185,11 +3186,11 @@ __export(dashboard_exports, {
3185
3186
  writeOverlay: () => writeOverlay
3186
3187
  });
3187
3188
  function overlayPath() {
3188
- return path9.join(appDataDir(), "intake-overlay.json");
3189
+ return path10.join(appDataDir(), "intake-overlay.json");
3189
3190
  }
3190
3191
  function readOverlay() {
3191
3192
  try {
3192
- const raw = JSON.parse(fs9.readFileSync(overlayPath(), "utf8"));
3193
+ const raw = JSON.parse(fs10.readFileSync(overlayPath(), "utf8"));
3193
3194
  if (raw && typeof raw === "object") return raw;
3194
3195
  } catch {
3195
3196
  }
@@ -3197,7 +3198,7 @@ function readOverlay() {
3197
3198
  }
3198
3199
  function writeOverlay(layer) {
3199
3200
  ensureAppDataDir();
3200
- fs9.writeFileSync(overlayPath(), JSON.stringify(layer, null, 2), { mode: 384 });
3201
+ fs10.writeFileSync(overlayPath(), JSON.stringify(layer, null, 2), { mode: 384 });
3201
3202
  }
3202
3203
  function recoverStuckStages(state) {
3203
3204
  let recovered = 0;
@@ -3209,11 +3210,11 @@ function recoverStuckStages(state) {
3209
3210
  return { state: { ...state, clients }, recovered };
3210
3211
  }
3211
3212
  function cockpitStatePath() {
3212
- return path9.join(appDataDir(), "cockpit-state.json");
3213
+ return path10.join(appDataDir(), "cockpit-state.json");
3213
3214
  }
3214
3215
  function readCockpitState() {
3215
3216
  try {
3216
- const raw = JSON.parse(fs9.readFileSync(cockpitStatePath(), "utf8"));
3217
+ const raw = JSON.parse(fs10.readFileSync(cockpitStatePath(), "utf8"));
3217
3218
  if (raw && raw.v === 1 && raw.clients && typeof raw.clients === "object") return raw;
3218
3219
  } catch {
3219
3220
  }
@@ -3221,7 +3222,7 @@ function readCockpitState() {
3221
3222
  }
3222
3223
  function writeCockpitState(state) {
3223
3224
  ensureAppDataDir();
3224
- fs9.writeFileSync(cockpitStatePath(), JSON.stringify(state, null, 2), { mode: 384 });
3225
+ fs10.writeFileSync(cockpitStatePath(), JSON.stringify(state, null, 2), { mode: 384 });
3225
3226
  }
3226
3227
  function setStage(state, locationId2, stageIndex, status) {
3227
3228
  if (!Number.isInteger(stageIndex) || stageIndex < 0 || stageIndex >= STAGES.length) throw new Error("bad stage index");
@@ -3313,7 +3314,7 @@ function notify(message) {
3313
3314
  `);
3314
3315
  try {
3315
3316
  ensureAppDataDir();
3316
- fs9.appendFileSync(path9.join(appDataDir(), "cockpit.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] ${message}
3317
+ fs10.appendFileSync(path10.join(appDataDir(), "cockpit.log"), `[${(/* @__PURE__ */ new Date()).toISOString()}] ${message}
3317
3318
  `);
3318
3319
  } catch {
3319
3320
  }
@@ -4014,17 +4015,17 @@ async function runDashboard(argv) {
4014
4015
  const source = args.url ? { kind: "url", url: String(args.url) } : args.instruction ? { kind: "recorder", instruction: String(args.instruction) } : { kind: "text", text: String(args.transcript ?? "") };
4015
4016
  let recorderPatterns = [];
4016
4017
  if (source.kind === "recorder") {
4017
- recorderPatterns = await new Promise((resolve5) => {
4018
+ recorderPatterns = await new Promise((resolve6) => {
4018
4019
  const ls = (0, import_child_process4.spawn)(claudeBin(), ["mcp", "list"], { stdio: ["ignore", "pipe", "ignore"] });
4019
4020
  let buf = "";
4020
4021
  ls.stdout.on("data", (d) => {
4021
4022
  buf += d.toString();
4022
4023
  });
4023
- ls.on("close", () => resolve5(recorderAllowPatterns(buf)));
4024
- ls.on("error", () => resolve5([]));
4024
+ ls.on("close", () => resolve6(recorderAllowPatterns(buf)));
4025
+ ls.on("error", () => resolve6([]));
4025
4026
  setTimeout(() => {
4026
4027
  ls.kill("SIGKILL");
4027
- resolve5(recorderAllowPatterns(buf));
4028
+ resolve6(recorderAllowPatterns(buf));
4028
4029
  }, 9e4).unref?.();
4029
4030
  });
4030
4031
  if (!recorderPatterns.length) {
@@ -4034,7 +4035,7 @@ async function runDashboard(argv) {
4034
4035
  }
4035
4036
  }
4036
4037
  const out = await prefillFromSource(
4037
- (prompt, tools) => new Promise((resolve5, reject) => {
4038
+ (prompt, tools) => new Promise((resolve6, reject) => {
4038
4039
  const argv2 = ["-p", prompt, "--max-turns", "25", "--disallowedTools", tools.deny];
4039
4040
  if (tools.allow) argv2.push("--allowedTools", tools.allow);
4040
4041
  else argv2.push("--allowedTools", "");
@@ -4043,7 +4044,7 @@ async function runDashboard(argv) {
4043
4044
  child.stdout.on("data", (d) => {
4044
4045
  out2 += d.toString();
4045
4046
  });
4046
- child.on("close", () => resolve5(out2));
4047
+ child.on("close", () => resolve6(out2));
4047
4048
  child.on("error", reject);
4048
4049
  setTimeout(() => child.kill("SIGKILL"), 5 * 60 * 1e3).unref?.();
4049
4050
  }),
@@ -4175,13 +4176,13 @@ async function runDashboard(argv) {
4175
4176
  }
4176
4177
  }
4177
4178
  const review = await generateReview(
4178
- (prompt) => new Promise((resolve5, reject) => {
4179
+ (prompt) => new Promise((resolve6, reject) => {
4179
4180
  const child = (0, import_child_process4.spawn)(claudeBin(), ["-p", prompt, "--allowedTools", "", "--disallowedTools", "Bash,Write,Edit,WebFetch,WebSearch,Task,Read,Glob,Grep", "--max-turns", "6"], { stdio: ["ignore", "pipe", "pipe"] });
4180
4181
  let out = "";
4181
4182
  child.stdout.on("data", (d) => {
4182
4183
  out += d.toString();
4183
4184
  });
4184
- child.on("close", () => resolve5(out));
4185
+ child.on("close", () => resolve6(out));
4185
4186
  child.on("error", reject);
4186
4187
  setTimeout(() => child.kill("SIGKILL"), 6 * 60 * 1e3).unref?.();
4187
4188
  }),
@@ -4354,9 +4355,9 @@ async function runDashboard(argv) {
4354
4355
  res.end();
4355
4356
  });
4356
4357
  try {
4357
- await new Promise((resolve5, reject) => {
4358
+ await new Promise((resolve6, reject) => {
4358
4359
  server2.once("error", reject);
4359
- server2.listen(port, "127.0.0.1", resolve5);
4360
+ server2.listen(port, "127.0.0.1", resolve6);
4360
4361
  });
4361
4362
  } catch (e) {
4362
4363
  const msg2 = e?.code === "EADDRINUSE" ? `Port ${port} is busy with another program. Close it, or start Command OS on a different port.` : `Command OS could not start: ${e instanceof Error ? e.message : String(e)}`;
@@ -4382,18 +4383,18 @@ async function runDashboard(argv) {
4382
4383
  if (adopted) process.stderr.write(` Synced ${adopted} client${adopted === 1 ? "" : "s"} from your GHL (teammate updates).
4383
4384
  `);
4384
4385
  });
4385
- return await new Promise((resolve5) => {
4386
- const stop = () => server2.close(() => resolve5(0));
4386
+ return await new Promise((resolve6) => {
4387
+ const stop = () => server2.close(() => resolve6(0));
4387
4388
  process.on("SIGINT", stop);
4388
4389
  process.on("SIGTERM", stop);
4389
4390
  });
4390
4391
  }
4391
- var fs9, path9, http, import_child_process3, import_child_process4, osmod, STAGES, ACCOUNT_TYPES, activeRun, UPGRADE_MSG;
4392
+ var fs10, path10, http, import_child_process3, import_child_process4, osmod, STAGES, ACCOUNT_TYPES, activeRun, UPGRADE_MSG;
4392
4393
  var init_dashboard = __esm({
4393
4394
  "src/dashboard.ts"() {
4394
4395
  "use strict";
4395
- fs9 = __toESM(require("fs"));
4396
- path9 = __toESM(require("path"));
4396
+ fs10 = __toESM(require("fs"));
4397
+ path10 = __toESM(require("path"));
4397
4398
  http = __toESM(require("http"));
4398
4399
  import_child_process3 = require("child_process");
4399
4400
  init_credentials_store();
@@ -4428,8 +4429,8 @@ var init_dashboard = __esm({
4428
4429
 
4429
4430
  // src/index.ts
4430
4431
  var dotenv2 = __toESM(require("dotenv"));
4431
- var path10 = __toESM(require("path"));
4432
- var fs10 = __toESM(require("fs"));
4432
+ var path11 = __toESM(require("path"));
4433
+ var fs11 = __toESM(require("fs"));
4433
4434
  var import_mcp = require("@modelcontextprotocol/sdk/server/mcp.js");
4434
4435
  var import_stdio = require("@modelcontextprotocol/sdk/server/stdio.js");
4435
4436
  init_ghl_client();
@@ -8376,16 +8377,16 @@ function registerEmailTools(server2, client) {
8376
8377
  function registerEmailBuilderInternalTools(server2, builderClient) {
8377
8378
  const client = builderClient;
8378
8379
  if (!client) return;
8379
- async function builderRequest(method, path11, body) {
8380
+ async function builderRequest(method, path12, body) {
8380
8381
  const headers = await client.buildHeaders();
8381
- const response = await fetch(`${EMAIL_BUILDER_BASE}${path11}`, {
8382
+ const response = await fetch(`${EMAIL_BUILDER_BASE}${path12}`, {
8382
8383
  method,
8383
8384
  headers,
8384
8385
  body: body ? JSON.stringify(body) : void 0
8385
8386
  });
8386
8387
  if (!response.ok) {
8387
8388
  const text2 = await response.text();
8388
- throw new Error(`Email Builder API Error ${response.status}: ${method} /emails/builder${path11}
8389
+ throw new Error(`Email Builder API Error ${response.status}: ${method} /emails/builder${path12}
8389
8390
  ${text2}`);
8390
8391
  }
8391
8392
  const text = await response.text();
@@ -9642,23 +9643,23 @@ var import_zod34 = require("zod");
9642
9643
  function registerFunnelBuilderTools(server2, builderClient) {
9643
9644
  const client = builderClient;
9644
9645
  if (!client) return;
9645
- async function internalGet(path11) {
9646
- return client.request("GET", path11);
9646
+ async function internalGet(path12) {
9647
+ return client.request("GET", path12);
9647
9648
  }
9648
- async function internalPost(path11, body) {
9649
- return client.request("POST", path11, body);
9649
+ async function internalPost(path12, body) {
9650
+ return client.request("POST", path12, body);
9650
9651
  }
9651
- async function internalPut(path11, body) {
9652
- return client.request("PUT", path11, body);
9652
+ async function internalPut(path12, body) {
9653
+ return client.request("PUT", path12, body);
9653
9654
  }
9654
- async function internalDelete(path11) {
9655
- return client.request("DELETE", path11);
9655
+ async function internalDelete(path12) {
9656
+ return client.request("DELETE", path12);
9656
9657
  }
9657
- async function funnelRequest(method, path11, body) {
9658
+ async function funnelRequest(method, path12, body) {
9658
9659
  const headers = await client.buildHeaders();
9659
9660
  headers.Origin = "https://app.gohighlevel.com";
9660
9661
  headers.Referer = "https://app.gohighlevel.com/";
9661
- const url = `https://backend.leadconnectorhq.com/funnels${path11}`;
9662
+ const url = `https://backend.leadconnectorhq.com/funnels${path12}`;
9662
9663
  const options = { method, headers };
9663
9664
  if (body && (method === "POST" || method === "PUT")) {
9664
9665
  options.body = JSON.stringify(body);
@@ -9666,7 +9667,7 @@ function registerFunnelBuilderTools(server2, builderClient) {
9666
9667
  const response = await fetch(url, options);
9667
9668
  if (!response.ok) {
9668
9669
  const text2 = await response.text();
9669
- throw new Error(`Funnel API Error ${response.status}: ${method} ${path11}
9670
+ throw new Error(`Funnel API Error ${response.status}: ${method} ${path12}
9670
9671
  ${text2}`);
9671
9672
  }
9672
9673
  const text = await response.text();
@@ -10611,12 +10612,12 @@ var valueCardSchema = import_zod35.z.object({
10611
10612
  function registerPageStudioTools(server2, builderClient) {
10612
10613
  const client = builderClient;
10613
10614
  if (!client) return;
10614
- async function funnelRequest(method, path11) {
10615
+ async function funnelRequest(method, path12) {
10615
10616
  const headers = await client.buildHeaders();
10616
10617
  headers.Origin = "https://app.gohighlevel.com";
10617
10618
  headers.Referer = "https://app.gohighlevel.com/";
10618
- const response = await fetch(`https://backend.leadconnectorhq.com/funnels${path11}`, { method, headers });
10619
- if (!response.ok) throw new Error(`Funnel API Error ${response.status}: ${method} ${path11}
10619
+ const response = await fetch(`https://backend.leadconnectorhq.com/funnels${path12}`, { method, headers });
10620
+ if (!response.ok) throw new Error(`Funnel API Error ${response.status}: ${method} ${path12}
10620
10621
  ${await response.text()}`);
10621
10622
  const text = await response.text();
10622
10623
  return text ? JSON.parse(text) : {};
@@ -10935,9 +10936,9 @@ function buildUpdateFormPath(formId, locationId2) {
10935
10936
  function buildUpdateFormBody(name, formData) {
10936
10937
  return { name, formData };
10937
10938
  }
10938
- async function formApiRequest(client, method, path11, body) {
10939
+ async function formApiRequest(client, method, path12, body) {
10939
10940
  const headers = await client.buildHeaders();
10940
- const url = `https://backend.leadconnectorhq.com/forms${path11}`;
10941
+ const url = `https://backend.leadconnectorhq.com/forms${path12}`;
10941
10942
  const options = { method, headers };
10942
10943
  if (body && (method === "POST" || method === "PUT")) {
10943
10944
  options.body = JSON.stringify(body);
@@ -10945,7 +10946,7 @@ async function formApiRequest(client, method, path11, body) {
10945
10946
  const response = await fetch(url, options);
10946
10947
  if (!response.ok) {
10947
10948
  const text2 = await response.text();
10948
- throw new Error(`Form API Error ${response.status}: ${method} ${path11}
10949
+ throw new Error(`Form API Error ${response.status}: ${method} ${path12}
10949
10950
  ${text2}`);
10950
10951
  }
10951
10952
  const text = await response.text();
@@ -10959,7 +10960,7 @@ ${text2}`);
10959
10960
  function registerFormBuilderTools(server2, builderClient, publicClient) {
10960
10961
  const client = builderClient;
10961
10962
  if (!client) return;
10962
- const formRequest = (method, path11, body) => formApiRequest(client, method, path11, body);
10963
+ const formRequest = (method, path12, body) => formApiRequest(client, method, path12, body);
10963
10964
  server2.tool(
10964
10965
  "get_form_full",
10965
10966
  "Get a form with full builder data: all fields (labels, types, IDs, validation), conditional logic, auto-responder config, email notification settings, styling, and version history. This is the internal API \u2014 it returns everything the form builder UI shows.",
@@ -11080,10 +11081,10 @@ function registerFormBuilderTools(server2, builderClient, publicClient) {
11080
11081
  },
11081
11082
  async ({ formId, limit, skip }) => {
11082
11083
  try {
11083
- let path11 = `/submissions?locationId=${client.locationId}&limit=${limit ?? 20}`;
11084
- if (formId) path11 += `&formId=${formId}`;
11085
- if (skip) path11 += `&skip=${skip}`;
11086
- const result = await formRequest("GET", path11);
11084
+ let path12 = `/submissions?locationId=${client.locationId}&limit=${limit ?? 20}`;
11085
+ if (formId) path12 += `&formId=${formId}`;
11086
+ if (skip) path12 += `&skip=${skip}`;
11087
+ const result = await formRequest("GET", path12);
11087
11088
  return {
11088
11089
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }]
11089
11090
  };
@@ -11476,9 +11477,9 @@ var import_zod38 = require("zod");
11476
11477
  function registerPipelineBuilderTools(server2, builderClient) {
11477
11478
  const client = builderClient;
11478
11479
  if (!client) return;
11479
- async function pipelineRequest(method, path11, body) {
11480
+ async function pipelineRequest(method, path12, body) {
11480
11481
  const headers = await client.buildHeaders();
11481
- const url = `https://backend.leadconnectorhq.com/opportunities/pipelines${path11}`;
11482
+ const url = `https://backend.leadconnectorhq.com/opportunities/pipelines${path12}`;
11482
11483
  const options = { method, headers };
11483
11484
  if (body && (method === "POST" || method === "PUT" || method === "PATCH")) {
11484
11485
  options.body = JSON.stringify(body);
@@ -11486,7 +11487,7 @@ function registerPipelineBuilderTools(server2, builderClient) {
11486
11487
  const response = await fetch(url, options);
11487
11488
  if (!response.ok) {
11488
11489
  const text2 = await response.text();
11489
- throw new Error(`Pipeline API Error ${response.status}: ${method} ${path11}
11490
+ throw new Error(`Pipeline API Error ${response.status}: ${method} ${path12}
11490
11491
  ${text2}`);
11491
11492
  }
11492
11493
  const text = await response.text();
@@ -12231,7 +12232,7 @@ ${lines.join("\n")}
12231
12232
  // src/tools/bulk-operations.ts
12232
12233
  var import_zod42 = require("zod");
12233
12234
  function delay(ms) {
12234
- return new Promise((resolve5) => setTimeout(resolve5, ms));
12235
+ return new Promise((resolve6) => setTimeout(resolve6, ms));
12235
12236
  }
12236
12237
  function formatResults(op, results, total) {
12237
12238
  return `${op}: ${results.success} success, ${results.failed} failed out of ${total}.${results.errors.length ? "\nErrors:\n" + results.errors.join("\n") : ""}`;
@@ -12356,7 +12357,7 @@ function registerBulkOperationTools(server2, client) {
12356
12357
  // src/tools/account-export.ts
12357
12358
  var import_zod43 = require("zod");
12358
12359
  function delay2(ms) {
12359
- return new Promise((resolve5) => setTimeout(resolve5, ms));
12360
+ return new Promise((resolve6) => setTimeout(resolve6, ms));
12360
12361
  }
12361
12362
  function registerAccountExportTools(server2, client) {
12362
12363
  const builderClient = WorkflowBuilderClient.fromEnv();
@@ -12677,9 +12678,9 @@ var OBJECT_KEYS = ["contacts", "opportunity"];
12677
12678
  function registerSmartListTools(server2, builderClient) {
12678
12679
  const client = builderClient;
12679
12680
  if (!client) return;
12680
- async function smartListRequest(method, path11, body) {
12681
+ async function smartListRequest(method, path12, body) {
12681
12682
  const headers = await client.buildHeaders();
12682
- const url = `${SMARTLIST_BASE}${path11}`;
12683
+ const url = `${SMARTLIST_BASE}${path12}`;
12683
12684
  const options = { method, headers };
12684
12685
  if (body && (method === "POST" || method === "PUT")) {
12685
12686
  options.body = JSON.stringify(body);
@@ -12687,7 +12688,7 @@ function registerSmartListTools(server2, builderClient) {
12687
12688
  const response = await fetch(url, options);
12688
12689
  if (!response.ok) {
12689
12690
  const text2 = await response.text();
12690
- throw new Error(`Smart Lists API Error ${response.status}: ${method} ${path11}
12691
+ throw new Error(`Smart Lists API Error ${response.status}: ${method} ${path12}
12691
12692
  ${text2}`);
12692
12693
  }
12693
12694
  const text = await response.text();
@@ -12819,12 +12820,12 @@ var REPUTATION_BASE = "https://backend.leadconnectorhq.com/reputation";
12819
12820
  function registerReputationTools(server2, builderClient) {
12820
12821
  const client = builderClient;
12821
12822
  if (!client) return;
12822
- async function reputationRequest(method, path11) {
12823
+ async function reputationRequest(method, path12) {
12823
12824
  const headers = await client.buildHeaders();
12824
- const response = await fetch(`${REPUTATION_BASE}${path11}`, { method, headers });
12825
+ const response = await fetch(`${REPUTATION_BASE}${path12}`, { method, headers });
12825
12826
  if (!response.ok) {
12826
12827
  const text2 = await response.text();
12827
- throw new Error(`Reputation API Error ${response.status}: ${method} ${path11}
12828
+ throw new Error(`Reputation API Error ${response.status}: ${method} ${path12}
12828
12829
  ${text2}`);
12829
12830
  }
12830
12831
  const text = await response.text();
@@ -12939,16 +12940,16 @@ var MEMBERSHIP_BASE = "https://backend.leadconnectorhq.com/membership";
12939
12940
  function registerMembershipTools(server2, builderClient) {
12940
12941
  const client = builderClient;
12941
12942
  if (!client) return;
12942
- async function membershipRequest(path11, method = "GET", body) {
12943
+ async function membershipRequest(path12, method = "GET", body) {
12943
12944
  const headers = await client.buildHeaders();
12944
- const response = await fetch(`${MEMBERSHIP_BASE}${path11}`, {
12945
+ const response = await fetch(`${MEMBERSHIP_BASE}${path12}`, {
12945
12946
  method,
12946
12947
  headers,
12947
12948
  body: body ? JSON.stringify(body) : void 0
12948
12949
  });
12949
12950
  if (!response.ok) {
12950
12951
  const text2 = await response.text();
12951
- throw new Error(`Membership API Error ${response.status}: ${method} ${path11}
12952
+ throw new Error(`Membership API Error ${response.status}: ${method} ${path12}
12952
12953
  ${text2}`);
12953
12954
  }
12954
12955
  const text = await response.text();
@@ -13117,7 +13118,7 @@ var import_zod49 = require("zod");
13117
13118
  var fs5 = __toESM(require("fs"));
13118
13119
  var path5 = __toESM(require("path"));
13119
13120
  function delay3(ms) {
13120
- return new Promise((resolve5) => setTimeout(resolve5, ms));
13121
+ return new Promise((resolve6) => setTimeout(resolve6, ms));
13121
13122
  }
13122
13123
  var TemplateSchema = import_zod49.z.object({
13123
13124
  templateName: import_zod49.z.string(),
@@ -13261,7 +13262,7 @@ function registerTemplateDeployerTools(server2, client) {
13261
13262
  const locId = client.resolveLocationId(locationId2);
13262
13263
  const safePath = validateTemplatePath(templateFile);
13263
13264
  const template = TemplateSchema.parse(JSON.parse(fs5.readFileSync(safePath, "utf-8")));
13264
- const resolve5 = (text) => {
13265
+ const resolve6 = (text) => {
13265
13266
  if (typeof text !== "string") return text;
13266
13267
  let result = text;
13267
13268
  for (const [key, value] of Object.entries(answers)) {
@@ -13274,7 +13275,7 @@ function registerTemplateDeployerTools(server2, client) {
13274
13275
  return result;
13275
13276
  };
13276
13277
  const resolveObj = (obj) => {
13277
- if (typeof obj === "string") return resolve5(obj);
13278
+ if (typeof obj === "string") return resolve6(obj);
13278
13279
  if (Array.isArray(obj)) return obj.map(resolveObj);
13279
13280
  if (obj && typeof obj === "object") {
13280
13281
  const result = {};
@@ -14897,9 +14898,9 @@ function presetForBusinessType(type) {
14897
14898
  return "generic";
14898
14899
  }
14899
14900
  }
14900
- function setPath(target, path11, value) {
14901
+ function setPath(target, path12, value) {
14901
14902
  if (value === void 0) return;
14902
- const parts = path11.split(".");
14903
+ const parts = path12.split(".");
14903
14904
  let node = target;
14904
14905
  for (let i = 0; i < parts.length - 1; i++) {
14905
14906
  const k = parts[i];
@@ -16985,15 +16986,15 @@ function extractFunnelId(result) {
16985
16986
  return void 0;
16986
16987
  }
16987
16988
  function makeExecuteDeps(client, builderClient, locationId2) {
16988
- const pipelineApi = async (method, path11, body) => {
16989
+ const pipelineApi = async (method, path12, body) => {
16989
16990
  const headers = await builderClient.buildHeaders();
16990
- const url = `https://backend.leadconnectorhq.com/opportunities/pipelines${path11}`;
16991
+ const url = `https://backend.leadconnectorhq.com/opportunities/pipelines${path12}`;
16991
16992
  const options = { method, headers };
16992
16993
  if (body && (method === "POST" || method === "PUT")) options.body = JSON.stringify(body);
16993
16994
  const response = await fetch(url, options);
16994
16995
  if (!response.ok) {
16995
16996
  const text2 = await response.text();
16996
- throw new Error(`Pipeline API ${response.status}: ${method} ${path11}
16997
+ throw new Error(`Pipeline API ${response.status}: ${method} ${path12}
16997
16998
  ${text2.slice(0, 300)}`);
16998
16999
  }
16999
17000
  const text = await response.text();
@@ -17004,17 +17005,17 @@ ${text2.slice(0, 300)}`);
17004
17005
  return JSON.parse(text.replace(/[\x00-\x1F\x7F]/g, ""));
17005
17006
  }
17006
17007
  };
17007
- const funnelApi = async (method, path11, body) => {
17008
+ const funnelApi = async (method, path12, body) => {
17008
17009
  const headers = await builderClient.buildHeaders();
17009
17010
  headers.Origin = "https://app.gohighlevel.com";
17010
17011
  headers.Referer = "https://app.gohighlevel.com/";
17011
- const url = `https://backend.leadconnectorhq.com/funnels${path11}`;
17012
+ const url = `https://backend.leadconnectorhq.com/funnels${path12}`;
17012
17013
  const options = { method, headers };
17013
17014
  if (body && (method === "POST" || method === "PUT")) options.body = JSON.stringify(body);
17014
17015
  const response = await fetch(url, options);
17015
17016
  if (!response.ok) {
17016
17017
  const text2 = await response.text();
17017
- throw new Error(`Funnel API ${response.status}: ${method} ${path11}
17018
+ throw new Error(`Funnel API ${response.status}: ${method} ${path12}
17018
17019
  ${text2.slice(0, 300)}`);
17019
17020
  }
17020
17021
  const text = await response.text();
@@ -17723,6 +17724,157 @@ function registerAllTools(server2, client, registry2, mcpVersion, env = process.
17723
17724
  // src/index.ts
17724
17725
  init_credentials_store();
17725
17726
  init_setup_tool();
17727
+
17728
+ // src/tools/skills.ts
17729
+ var import_zod57 = require("zod");
17730
+
17731
+ // src/skill-installer.ts
17732
+ var import_node_crypto2 = require("node:crypto");
17733
+ var fs6 = __toESM(require("node:fs"));
17734
+ var path6 = __toESM(require("node:path"));
17735
+ var os3 = __toESM(require("node:os"));
17736
+ function sha256(buf) {
17737
+ return (0, import_node_crypto2.createHash)("sha256").update(buf).digest("hex");
17738
+ }
17739
+ function bundledSkillsDir(baseDir) {
17740
+ const candidate = path6.resolve(baseDir, "..", "skills");
17741
+ try {
17742
+ return fs6.statSync(candidate).isDirectory() ? candidate : null;
17743
+ } catch {
17744
+ return null;
17745
+ }
17746
+ }
17747
+ function listFiles(root) {
17748
+ const out = [];
17749
+ const walk = (dir) => {
17750
+ for (const entry of fs6.readdirSync(dir, { withFileTypes: true })) {
17751
+ const full = path6.join(dir, entry.name);
17752
+ if (entry.isDirectory()) walk(full);
17753
+ else if (entry.isFile()) out.push(path6.relative(root, full));
17754
+ }
17755
+ };
17756
+ walk(root);
17757
+ return out.sort();
17758
+ }
17759
+ function readMarker(markerPath) {
17760
+ try {
17761
+ const parsed = JSON.parse(fs6.readFileSync(markerPath, "utf8"));
17762
+ return parsed && typeof parsed === "object" && parsed.files ? parsed : null;
17763
+ } catch {
17764
+ return null;
17765
+ }
17766
+ }
17767
+ function installBundledSkills(opts) {
17768
+ const result = {
17769
+ installed: [],
17770
+ updated: [],
17771
+ skippedUserModified: [],
17772
+ unchanged: [],
17773
+ errors: [],
17774
+ targetDir: opts.targetDir ?? path6.join(os3.homedir(), ".claude", "skills"),
17775
+ bundledDir: null
17776
+ };
17777
+ const bundled = bundledSkillsDir(opts.baseDir);
17778
+ result.bundledDir = bundled;
17779
+ if (!bundled) return result;
17780
+ try {
17781
+ fs6.mkdirSync(result.targetDir, { recursive: true });
17782
+ } catch (e) {
17783
+ result.errors.push(`cannot create ${result.targetDir}: ${String(e)}`);
17784
+ return result;
17785
+ }
17786
+ const markerPath = path6.join(result.targetDir, ".ghl-command-skills.json");
17787
+ const marker = readMarker(markerPath) ?? { packageVersion: "", files: {} };
17788
+ const newMarker = { packageVersion: opts.packageVersion, files: { ...marker.files } };
17789
+ for (const rel of listFiles(bundled)) {
17790
+ try {
17791
+ const src = fs6.readFileSync(path6.join(bundled, rel));
17792
+ const dest = path6.join(result.targetDir, rel);
17793
+ const srcHash = sha256(src);
17794
+ let destBuf = null;
17795
+ try {
17796
+ destBuf = fs6.readFileSync(dest);
17797
+ } catch {
17798
+ destBuf = null;
17799
+ }
17800
+ if (destBuf === null) {
17801
+ fs6.mkdirSync(path6.dirname(dest), { recursive: true });
17802
+ fs6.writeFileSync(dest, src);
17803
+ newMarker.files[rel] = srcHash;
17804
+ result.installed.push(rel);
17805
+ continue;
17806
+ }
17807
+ const destHash = sha256(destBuf);
17808
+ if (destHash === srcHash) {
17809
+ newMarker.files[rel] = srcHash;
17810
+ result.unchanged.push(rel);
17811
+ continue;
17812
+ }
17813
+ const lastWritten = marker.files[rel];
17814
+ if (lastWritten && destHash === lastWritten) {
17815
+ fs6.writeFileSync(dest, src);
17816
+ newMarker.files[rel] = srcHash;
17817
+ result.updated.push(rel);
17818
+ } else {
17819
+ result.skippedUserModified.push(rel);
17820
+ }
17821
+ } catch (e) {
17822
+ result.errors.push(`${rel}: ${String(e)}`);
17823
+ }
17824
+ }
17825
+ try {
17826
+ fs6.writeFileSync(markerPath, JSON.stringify(newMarker, null, 2));
17827
+ } catch (e) {
17828
+ result.errors.push(`marker write failed: ${String(e)}`);
17829
+ }
17830
+ return result;
17831
+ }
17832
+ function summarizeInstall(r) {
17833
+ if (!r.bundledDir) return "";
17834
+ const bits = [];
17835
+ if (r.installed.length) bits.push(`${r.installed.length} installed`);
17836
+ if (r.updated.length) bits.push(`${r.updated.length} updated`);
17837
+ if (r.skippedUserModified.length) bits.push(`${r.skippedUserModified.length} kept (your edits)`);
17838
+ if (r.errors.length) bits.push(`${r.errors.length} errors`);
17839
+ if (bits.length === 0) return "";
17840
+ return `Skills: ${bits.join(", ")} \u2192 ${r.targetDir}`;
17841
+ }
17842
+
17843
+ // src/tools/skills.ts
17844
+ function registerSkillsTool(server2, packageVersion, baseDir) {
17845
+ server2.tool(
17846
+ "install_skills",
17847
+ "Install (or repair) the guided skills bundled with GHL Command \u2014 currently the Blueprint skill (build a whole client account from one intake) \u2014 into your ~/.claude/skills/ so Claude can use them. Runs automatically on startup; call this to verify what is installed, or to re-install after deleting a skill. NEVER overwrites files you have edited (your version is kept and reported). Restart Claude after install for new skills to load.",
17848
+ {
17849
+ targetDir: import_zod57.z.string().optional().describe("Override the install directory. Default: ~/.claude/skills")
17850
+ },
17851
+ async ({ targetDir }) => {
17852
+ try {
17853
+ const result = installBundledSkills({ packageVersion, baseDir, targetDir });
17854
+ if (!result.bundledDir) {
17855
+ return jsonResponse({
17856
+ ok: false,
17857
+ note: "No bundled skills directory found in this install (development checkout without skills/, or a pre-3.55.0 package)."
17858
+ });
17859
+ }
17860
+ return jsonResponse({
17861
+ ok: result.errors.length === 0,
17862
+ targetDir: result.targetDir,
17863
+ installed: result.installed,
17864
+ updated: result.updated,
17865
+ keptYourEdits: result.skippedUserModified,
17866
+ unchanged: result.unchanged.length,
17867
+ errors: result.errors,
17868
+ next: result.installed.length > 0 || result.updated.length > 0 ? "Fully restart Claude (quit and reopen) so the new/updated skills load." : "Everything already current."
17869
+ });
17870
+ } catch (error) {
17871
+ return errorResponse(error);
17872
+ }
17873
+ }
17874
+ );
17875
+ }
17876
+
17877
+ // src/index.ts
17726
17878
  init_attestation();
17727
17879
 
17728
17880
  // src/tools/meta.ts
@@ -17756,8 +17908,8 @@ function registerMetaTools(server2, installedVersion) {
17756
17908
 
17757
17909
  // src/cli.ts
17758
17910
  var import_node_util = require("node:util");
17759
- var fs6 = __toESM(require("fs"));
17760
- var path6 = __toESM(require("path"));
17911
+ var fs7 = __toESM(require("fs"));
17912
+ var path7 = __toESM(require("path"));
17761
17913
  var import_crypto2 = require("crypto");
17762
17914
  init_ghl_client();
17763
17915
  init_token_registry();
@@ -17800,9 +17952,9 @@ function errLine(msg2) {
17800
17952
  function preflightWritable() {
17801
17953
  try {
17802
17954
  const dir = ensureAppDataDir();
17803
- const probe = path6.join(dir, `.write-probe.${process.pid}.${(0, import_crypto2.randomBytes)(4).toString("hex")}`);
17804
- fs6.writeFileSync(probe, "ok");
17805
- fs6.unlinkSync(probe);
17955
+ const probe = path7.join(dir, `.write-probe.${process.pid}.${(0, import_crypto2.randomBytes)(4).toString("hex")}`);
17956
+ fs7.writeFileSync(probe, "ok");
17957
+ fs7.unlinkSync(probe);
17806
17958
  return true;
17807
17959
  } catch (error) {
17808
17960
  errLine(`Config dir is not writable: ${error instanceof Error ? error.message : String(error)}`);
@@ -18052,7 +18204,7 @@ var bundledPkg = require_package();
18052
18204
  var pkg = (() => {
18053
18205
  try {
18054
18206
  const onDisk = JSON.parse(
18055
- fs10.readFileSync(path10.resolve(__dirname, "..", "package.json"), "utf8")
18207
+ fs11.readFileSync(path11.resolve(__dirname, "..", "package.json"), "utf8")
18056
18208
  );
18057
18209
  if (typeof onDisk.version === "string" && onDisk.version.length > 0) {
18058
18210
  return { version: onDisk.version };
@@ -18064,7 +18216,7 @@ var pkg = (() => {
18064
18216
  dotenv2.config();
18065
18217
  {
18066
18218
  const configDirOverride = process.env.GHL_MCP_CONFIG_DIR?.trim();
18067
- if (configDirOverride && !path10.isAbsolute(configDirOverride)) {
18219
+ if (configDirOverride && !path11.isAbsolute(configDirOverride)) {
18068
18220
  process.stderr.write(
18069
18221
  `[ghl-mcp] GHL_MCP_CONFIG_DIR must be an absolute path (got "${configDirOverride}"). Use e.g. /data/ghl-mcp in a container, with a volume mounted at /data.
18070
18222
  `
@@ -18077,20 +18229,20 @@ process.on("unhandledRejection", (reason) => {
18077
18229
  `);
18078
18230
  });
18079
18231
  function hardenSecretFilePerms() {
18080
- const repoDir = path10.resolve(__dirname, "..");
18232
+ const repoDir = path11.resolve(__dirname, "..");
18081
18233
  const candidates = [
18082
- { file: path10.join(repoDir, "start-mcp.sh"), mode: 448 },
18234
+ { file: path11.join(repoDir, "start-mcp.sh"), mode: 448 },
18083
18235
  // Legacy registry location (pre-migration); new location lives in app-data.
18084
- { file: path10.join(repoDir, ".ghl-tokens.json"), mode: 384 },
18236
+ { file: path11.join(repoDir, ".ghl-tokens.json"), mode: 384 },
18085
18237
  { file: tokenRegistryPath(), mode: 384 }
18086
18238
  ];
18087
18239
  for (const { file, mode } of candidates) {
18088
18240
  let current;
18089
18241
  try {
18090
- if (!fs10.existsSync(file)) continue;
18091
- current = fs10.statSync(file).mode & 511;
18242
+ if (!fs11.existsSync(file)) continue;
18243
+ current = fs11.statSync(file).mode & 511;
18092
18244
  if (current !== mode) {
18093
- fs10.chmodSync(file, mode);
18245
+ fs11.chmodSync(file, mode);
18094
18246
  }
18095
18247
  } catch (error) {
18096
18248
  const message = error instanceof Error ? error.message : String(error);
@@ -18272,6 +18424,16 @@ async function resolveAccessAndRegister() {
18272
18424
  registerEnableWorkflowBuilderTool(server);
18273
18425
  registerFirebaseCaptureScriptTool(server);
18274
18426
  registerInteractiveCaptureTool(server);
18427
+ registerSkillsTool(server, pkg.version, __dirname);
18428
+ try {
18429
+ const skillsResult = installBundledSkills({ packageVersion: pkg.version, baseDir: __dirname });
18430
+ const line = summarizeInstall(skillsResult);
18431
+ if (line) process.stderr.write(`[ghl-mcp] ${line}
18432
+ `);
18433
+ } catch (e) {
18434
+ process.stderr.write(`[ghl-mcp] Skills install skipped: ${String(e).slice(0, 120)}
18435
+ `);
18436
+ }
18275
18437
  if (fileCreds && !process.env.GHL_API_KEY) {
18276
18438
  process.stderr.write(`[ghl-mcp] Loaded credentials from ${credentialsPath()}
18277
18439
  `);