@cortex-context/cli 0.0.5 → 0.0.7

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
@@ -5617,7 +5617,7 @@ var require_dist = __commonJS({
5617
5617
  });
5618
5618
  };
5619
5619
  }
5620
- var prompts3 = require_prompts();
5620
+ var prompts5 = require_prompts();
5621
5621
  var passOn = ["suggest", "format", "onState", "validate", "onRender", "type"];
5622
5622
  var noop = () => {
5623
5623
  };
@@ -5668,7 +5668,7 @@ var require_dist = __commonJS({
5668
5668
  var _question2 = question;
5669
5669
  name = _question2.name;
5670
5670
  type = _question2.type;
5671
- if (prompts3[type] === void 0) {
5671
+ if (prompts5[type] === void 0) {
5672
5672
  throw new Error(`prompt type (${type}) is not defined`);
5673
5673
  }
5674
5674
  if (override2[question.name] !== void 0) {
@@ -5679,7 +5679,7 @@ var require_dist = __commonJS({
5679
5679
  }
5680
5680
  }
5681
5681
  try {
5682
- answer = prompt._injected ? getInjectedAnswer(prompt._injected, question.initial) : yield prompts3[type](question);
5682
+ answer = prompt._injected ? getInjectedAnswer(prompt._injected, question.initial) : yield prompts5[type](question);
5683
5683
  answers[name] = answer = yield getFormattedAnswer(question, answer, true);
5684
5684
  quit = yield onSubmit(question, answer, answers);
5685
5685
  } catch (err) {
@@ -5711,7 +5711,7 @@ var require_dist = __commonJS({
5711
5711
  }
5712
5712
  module2.exports = Object.assign(prompt, {
5713
5713
  prompt,
5714
- prompts: prompts3,
5714
+ prompts: prompts5,
5715
5715
  inject,
5716
5716
  override
5717
5717
  });
@@ -7798,7 +7798,7 @@ var require_prompts2 = __commonJS({
7798
7798
  var require_lib = __commonJS({
7799
7799
  "node_modules/prompts/lib/index.js"(exports2, module2) {
7800
7800
  "use strict";
7801
- var prompts3 = require_prompts2();
7801
+ var prompts5 = require_prompts2();
7802
7802
  var passOn = ["suggest", "format", "onState", "validate", "onRender", "type"];
7803
7803
  var noop = () => {
7804
7804
  };
@@ -7830,7 +7830,7 @@ var require_lib = __commonJS({
7830
7830
  throw new Error("prompt message is required");
7831
7831
  }
7832
7832
  ({ name, type } = question);
7833
- if (prompts3[type] === void 0) {
7833
+ if (prompts5[type] === void 0) {
7834
7834
  throw new Error(`prompt type (${type}) is not defined`);
7835
7835
  }
7836
7836
  if (override2[question.name] !== void 0) {
@@ -7841,7 +7841,7 @@ var require_lib = __commonJS({
7841
7841
  }
7842
7842
  }
7843
7843
  try {
7844
- answer = prompt._injected ? getInjectedAnswer(prompt._injected, question.initial) : await prompts3[type](question);
7844
+ answer = prompt._injected ? getInjectedAnswer(prompt._injected, question.initial) : await prompts5[type](question);
7845
7845
  answers[name] = answer = await getFormattedAnswer(question, answer, true);
7846
7846
  quit = await onSubmit(question, answer, answers);
7847
7847
  } catch (err) {
@@ -7864,7 +7864,7 @@ var require_lib = __commonJS({
7864
7864
  function override(answers) {
7865
7865
  prompt._override = Object.assign({}, answers);
7866
7866
  }
7867
- module2.exports = Object.assign(prompt, { prompt, prompts: prompts3, inject, override });
7867
+ module2.exports = Object.assign(prompt, { prompt, prompts: prompts5, inject, override });
7868
7868
  }
7869
7869
  });
7870
7870
 
@@ -16901,7 +16901,7 @@ var source_default = chalk;
16901
16901
  // src/commands/init.ts
16902
16902
  var import_path8 = require("path");
16903
16903
  var import_fs8 = require("fs");
16904
- var import_prompts = __toESM(require_prompts3());
16904
+ var import_prompts2 = __toESM(require_prompts3());
16905
16905
 
16906
16906
  // node_modules/ora/index.js
16907
16907
  var import_node_process7 = __toESM(require("process"), 1);
@@ -17840,7 +17840,10 @@ function writeConfig(config2) {
17840
17840
  (0, import_fs.mkdirSync)(CONFIG_DIR, { recursive: true });
17841
17841
  }
17842
17842
  const current = readConfig();
17843
- (0, import_fs.writeFileSync)(CONFIG_FILE, JSON.stringify({ ...current, ...config2 }, null, 2));
17843
+ (0, import_fs.writeFileSync)(
17844
+ CONFIG_FILE,
17845
+ JSON.stringify({ ...current, ...config2 }, null, 2)
17846
+ );
17844
17847
  }
17845
17848
  function deleteConfig() {
17846
17849
  if ((0, import_fs.existsSync)(CONFIG_FILE)) {
@@ -17944,11 +17947,56 @@ function printInstallResult(label, result, dryRun) {
17944
17947
  var import_fs3 = require("fs");
17945
17948
  var import_path3 = require("path");
17946
17949
  var import_os2 = require("os");
17947
- function getMcpConfigPath(workspacePath) {
17948
- return (0, import_path3.join)(workspacePath, ".vscode", "mcp.json");
17950
+ var import_prompts = __toESM(require_prompts3());
17951
+ async function detectWorkspaceTarget(workspacePath) {
17952
+ const hasVscode = (0, import_fs3.existsSync)((0, import_path3.join)(workspacePath, ".vscode"));
17953
+ const hasCursor = (0, import_fs3.existsSync)((0, import_path3.join)(workspacePath, ".cursor"));
17954
+ const hasClaude = (0, import_fs3.existsSync)((0, import_path3.join)(workspacePath, ".claude"));
17955
+ if (hasVscode) {
17956
+ return {
17957
+ target: "vscode",
17958
+ mcpPath: (0, import_path3.join)(workspacePath, ".vscode", "mcp.json"),
17959
+ isFallback: false
17960
+ };
17961
+ }
17962
+ if (hasCursor) {
17963
+ return {
17964
+ target: "cursor",
17965
+ mcpPath: (0, import_path3.join)(workspacePath, ".cursor", "mcp.json"),
17966
+ isFallback: false
17967
+ };
17968
+ }
17969
+ if (hasClaude) {
17970
+ return {
17971
+ target: "claude",
17972
+ mcpPath: (0, import_path3.join)(workspacePath, ".claude", "mcp.json"),
17973
+ isFallback: false
17974
+ };
17975
+ }
17976
+ console.log(
17977
+ source_default.yellow(
17978
+ " \u26A0 No IDE workspace detected (.vscode/, .cursor/, .claude/)"
17979
+ )
17980
+ );
17981
+ console.log(
17982
+ source_default.dim(
17983
+ " .agents/ is the emerging standard for IDE-agnostic AI config."
17984
+ )
17985
+ );
17986
+ const { confirm } = await (0, import_prompts.default)({
17987
+ type: "confirm",
17988
+ name: "confirm",
17989
+ message: "Install MCP config into .agents/mcp.json?",
17990
+ initial: true
17991
+ });
17992
+ if (!confirm) return null;
17993
+ return {
17994
+ target: "agents",
17995
+ mcpPath: (0, import_path3.join)(workspacePath, ".agents", "mcp.json"),
17996
+ isFallback: true
17997
+ };
17949
17998
  }
17950
- function readMcpConfig(workspacePath) {
17951
- const mcpPath = getMcpConfigPath(workspacePath);
17999
+ function readMcpConfig(mcpPath) {
17952
18000
  if (!(0, import_fs3.existsSync)(mcpPath)) return { servers: {} };
17953
18001
  try {
17954
18002
  return JSON.parse((0, import_fs3.readFileSync)(mcpPath, "utf-8"));
@@ -17956,12 +18004,12 @@ function readMcpConfig(workspacePath) {
17956
18004
  return { servers: {} };
17957
18005
  }
17958
18006
  }
17959
- function writeMcpConfig(workspacePath, config2) {
17960
- const mcpPath = getMcpConfigPath(workspacePath);
18007
+ function writeMcpConfig(mcpPath, config2) {
18008
+ (0, import_fs3.mkdirSync)((0, import_path3.dirname)(mcpPath), { recursive: true });
17961
18009
  (0, import_fs3.writeFileSync)(mcpPath, JSON.stringify(config2, null, 2) + "\n");
17962
18010
  }
17963
- function injectCortexMcpServer(workspacePath, cortexUrl, cortexToken) {
17964
- const config2 = readMcpConfig(workspacePath);
18011
+ function injectCortexMcpServer(mcpPath, cortexUrl, cortexToken) {
18012
+ const config2 = readMcpConfig(mcpPath);
17965
18013
  const existed = "cortex" in config2.servers;
17966
18014
  config2.servers["cortex"] = {
17967
18015
  type: "stdio",
@@ -17972,74 +18020,63 @@ function injectCortexMcpServer(workspacePath, cortexUrl, cortexToken) {
17972
18020
  ...cortexToken ? { CORTEX_API_TOKEN: cortexToken } : {}
17973
18021
  }
17974
18022
  };
17975
- writeMcpConfig(workspacePath, config2);
18023
+ writeMcpConfig(mcpPath, config2);
17976
18024
  return {
17977
18025
  added: !existed,
17978
18026
  updated: existed
17979
18027
  };
17980
18028
  }
17981
- function removeCortexMcpServer(workspacePath) {
17982
- const config2 = readMcpConfig(workspacePath);
18029
+ function removeCortexMcpServer(mcpPath) {
18030
+ const config2 = readMcpConfig(mcpPath);
17983
18031
  if (!("cortex" in config2.servers)) return { removed: false };
17984
18032
  delete config2.servers["cortex"];
17985
- writeMcpConfig(workspacePath, config2);
18033
+ writeMcpConfig(mcpPath, config2);
17986
18034
  return { removed: true };
17987
18035
  }
17988
- function hasCortexMcp(workspacePath) {
17989
- const config2 = readMcpConfig(workspacePath);
18036
+ function hasCortexMcp(mcpPath) {
18037
+ const config2 = readMcpConfig(mcpPath);
17990
18038
  return "cortex" in config2.servers;
17991
18039
  }
17992
18040
  function printMcpStatus(workspacePath) {
17993
- const mcpPath = getMcpConfigPath(workspacePath);
17994
- if (!(0, import_fs3.existsSync)(mcpPath)) {
17995
- console.log(source_default.yellow(" \u26A0 No .vscode/mcp.json found"));
17996
- return;
18041
+ const candidates = [
18042
+ {
18043
+ label: ".vscode/mcp.json",
18044
+ path: (0, import_path3.join)(workspacePath, ".vscode", "mcp.json")
18045
+ },
18046
+ {
18047
+ label: ".cursor/mcp.json",
18048
+ path: (0, import_path3.join)(workspacePath, ".cursor", "mcp.json")
18049
+ },
18050
+ {
18051
+ label: ".claude/mcp.json",
18052
+ path: (0, import_path3.join)(workspacePath, ".claude", "mcp.json")
18053
+ },
18054
+ {
18055
+ label: ".agents/mcp.json",
18056
+ path: (0, import_path3.join)(workspacePath, ".agents", "mcp.json")
18057
+ }
18058
+ ];
18059
+ let found = false;
18060
+ for (const { label, path } of candidates) {
18061
+ if (!(0, import_fs3.existsSync)(path)) continue;
18062
+ const config2 = readMcpConfig(path);
18063
+ if ("cortex" in config2.servers) {
18064
+ const url = config2.servers["cortex"]?.env?.["CORTEX_URL"] ?? "(not set)";
18065
+ console.log(
18066
+ source_default.green(` \u2713 MCP server "cortex" configured in ${label} \u2192 ${url}`)
18067
+ );
18068
+ found = true;
18069
+ }
17997
18070
  }
17998
- const config2 = readMcpConfig(workspacePath);
17999
- if ("cortex" in config2.servers) {
18000
- const entry = config2.servers["cortex"];
18001
- const url = entry.env?.["CORTEX_URL"] ?? "(not set)";
18002
- console.log(source_default.green(` \u2713 MCP server "cortex" configured \u2192 ${url}`));
18003
- } else {
18071
+ if (!found) {
18004
18072
  console.log(
18005
- source_default.yellow(' \u26A0 MCP server "cortex" not found in .vscode/mcp.json')
18073
+ source_default.yellow(' \u26A0 MCP server "cortex" not found in any known config')
18006
18074
  );
18007
18075
  }
18008
18076
  }
18009
18077
  function getCursorMcpPath(workspacePath) {
18010
18078
  return (0, import_path3.join)(workspacePath, ".cursor", "mcp.json");
18011
18079
  }
18012
- function configureCursorMcp(workspacePath, cortexUrl, cortexToken) {
18013
- const cursorDir = (0, import_path3.join)(workspacePath, ".cursor");
18014
- if (!(0, import_fs3.existsSync)(cursorDir)) {
18015
- return {
18016
- configured: false,
18017
- skipped: true,
18018
- reason: ".cursor/ not found (Cursor not configured)"
18019
- };
18020
- }
18021
- const mcpPath = getCursorMcpPath(workspacePath);
18022
- let config2 = { servers: {} };
18023
- if ((0, import_fs3.existsSync)(mcpPath)) {
18024
- try {
18025
- config2 = JSON.parse((0, import_fs3.readFileSync)(mcpPath, "utf-8"));
18026
- } catch {
18027
- config2 = { servers: {} };
18028
- }
18029
- }
18030
- config2.servers["cortex"] = {
18031
- type: "stdio",
18032
- command: "npx",
18033
- args: ["-y", "@cortex-context/cli", "mcp-serve"],
18034
- env: {
18035
- CORTEX_URL: cortexUrl,
18036
- ...cortexToken ? { CORTEX_API_TOKEN: cortexToken } : {}
18037
- }
18038
- };
18039
- (0, import_fs3.mkdirSync)(cursorDir, { recursive: true });
18040
- (0, import_fs3.writeFileSync)(mcpPath, JSON.stringify(config2, null, 2) + "\n");
18041
- return { configured: true, skipped: false };
18042
- }
18043
18080
  function removeCursorMcp(workspacePath) {
18044
18081
  const mcpPath = getCursorMcpPath(workspacePath);
18045
18082
  if (!(0, import_fs3.existsSync)(mcpPath)) return { removed: false };
@@ -18156,7 +18193,6 @@ function writeCortexConfig(workspacePath, force) {
18156
18193
  }
18157
18194
  const templatePath = (0, import_path4.join)(
18158
18195
  __dirname,
18159
- "..",
18160
18196
  "templates",
18161
18197
  "local",
18162
18198
  "cortex.config.template.yaml"
@@ -18173,10 +18209,10 @@ function writeCortexConfig(workspacePath, force) {
18173
18209
  var import_child_process = require("child_process");
18174
18210
  var import_fs5 = require("fs");
18175
18211
  var import_path5 = require("path");
18212
+ var import_os3 = require("os");
18176
18213
  async function deployLocalStack(workspacePath) {
18177
18214
  const templatePath = (0, import_path5.join)(
18178
18215
  __dirname,
18179
- "..",
18180
18216
  "templates",
18181
18217
  "local",
18182
18218
  "docker-compose.local.yml"
@@ -18189,10 +18225,13 @@ async function deployLocalStack(workspacePath) {
18189
18225
  (0, import_fs5.cpSync)(templatePath, destPath);
18190
18226
  }
18191
18227
  try {
18192
- (0, import_child_process.execSync)(`docker compose -f "${destPath}" up -d`, {
18193
- cwd: workspacePath,
18194
- stdio: "inherit"
18195
- });
18228
+ (0, import_child_process.execSync)(
18229
+ `docker compose --project-name cortex-local -f "${destPath}" up -d`,
18230
+ {
18231
+ cwd: workspacePath,
18232
+ stdio: "inherit"
18233
+ }
18234
+ );
18196
18235
  return { started: true };
18197
18236
  } catch (err) {
18198
18237
  return {
@@ -18209,6 +18248,58 @@ function isDockerAvailable() {
18209
18248
  return false;
18210
18249
  }
18211
18250
  }
18251
+ async function installDocker() {
18252
+ const os2 = (0, import_os3.platform)();
18253
+ try {
18254
+ if (os2 === "linux") {
18255
+ console.log(
18256
+ source_default.dim(" \u2192 Running Docker install script (curl | sh)...")
18257
+ );
18258
+ (0, import_child_process.execSync)("curl -fsSL https://get.docker.com | sh", { stdio: "inherit" });
18259
+ const user = process.env["USER"] ?? process.env["LOGNAME"];
18260
+ if (user) {
18261
+ (0, import_child_process.spawnSync)("usermod", ["-aG", "docker", user], { stdio: "inherit" });
18262
+ }
18263
+ return { installed: true };
18264
+ }
18265
+ if (os2 === "darwin") {
18266
+ console.log(source_default.dim(" \u2192 Installing Docker Desktop via Homebrew..."));
18267
+ (0, import_child_process.execSync)("brew install --cask docker", { stdio: "inherit" });
18268
+ return { installed: true };
18269
+ }
18270
+ if (os2 === "win32") {
18271
+ console.log(source_default.dim(" \u2192 Installing Docker Desktop via winget..."));
18272
+ (0, import_child_process.execSync)(
18273
+ "winget install --id Docker.DockerDesktop -e --silent --accept-package-agreements --accept-source-agreements",
18274
+ {
18275
+ stdio: "inherit",
18276
+ shell: "cmd.exe"
18277
+ }
18278
+ );
18279
+ return { installed: true };
18280
+ }
18281
+ return { installed: false, reason: `Unsupported OS: ${os2}` };
18282
+ } catch (err) {
18283
+ return {
18284
+ installed: false,
18285
+ reason: err instanceof Error ? err.message : String(err)
18286
+ };
18287
+ }
18288
+ }
18289
+ async function waitForCortexHealth(cortexUrl, maxWaitMs = 12e4) {
18290
+ const deadline = Date.now() + maxWaitMs;
18291
+ while (Date.now() < deadline) {
18292
+ try {
18293
+ const res = await fetch(`${cortexUrl}/health`, {
18294
+ signal: AbortSignal.timeout(4e3)
18295
+ });
18296
+ if (res.ok) return { healthy: true };
18297
+ } catch {
18298
+ }
18299
+ await new Promise((r) => setTimeout(r, 5e3));
18300
+ }
18301
+ return { healthy: false };
18302
+ }
18212
18303
 
18213
18304
  // src/lib/hooks.ts
18214
18305
  var import_fs6 = require("fs");
@@ -18388,7 +18479,7 @@ async function initCommand(options) {
18388
18479
  cortexToken = "dev-token-local";
18389
18480
  console.log(source_default.dim(" Mode: local Docker stack"));
18390
18481
  } else {
18391
- const responses = await (0, import_prompts.default)(
18482
+ const responses = await (0, import_prompts2.default)(
18392
18483
  [
18393
18484
  {
18394
18485
  type: options.url ? null : "text",
@@ -18518,23 +18609,34 @@ async function initCommand(options) {
18518
18609
  if (!options.skipMcp) {
18519
18610
  console.log("");
18520
18611
  console.log(source_default.bold(" Phase 2: MCP Server"));
18521
- const mcpStatus = injectCortexMcpServer(
18522
- workspacePath,
18523
- cortexUrl,
18524
- cortexToken
18525
- );
18526
- if (mcpStatus.added) {
18527
- console.log(source_default.green(" \u2713 .vscode/mcp.json \u2014 Cortex server added"));
18528
- } else {
18529
- console.log(source_default.cyan(" \u21BB .vscode/mcp.json \u2014 Cortex server updated"));
18530
- }
18531
- const cursorMcp = configureCursorMcp(workspacePath, cortexUrl, cortexToken);
18532
- if (cursorMcp.configured) {
18612
+ const detected = await detectWorkspaceTarget(workspacePath);
18613
+ if (!detected) {
18533
18614
  console.log(
18534
- source_default.green(" \u2713 .cursor/mcp.json \u2014 Cortex server configured")
18615
+ source_default.yellow(" \u26A0 MCP config skipped \u2014 no target directory confirmed")
18535
18616
  );
18536
- } else if (!cursorMcp.skipped) {
18537
- console.log(source_default.yellow(` \u26A0 .cursor/mcp.json \u2014 ${cursorMcp.reason}`));
18617
+ } else {
18618
+ const targetLabel = {
18619
+ vscode: ".vscode/mcp.json",
18620
+ cursor: ".cursor/mcp.json",
18621
+ claude: ".claude/mcp.json",
18622
+ agents: ".agents/mcp.json"
18623
+ };
18624
+ const label = targetLabel[detected.target] ?? detected.mcpPath;
18625
+ if (detected.isFallback) {
18626
+ console.log(
18627
+ source_default.dim(` \u2139 Installing into ${label} (IDE-agnostic standard)`)
18628
+ );
18629
+ }
18630
+ const mcpStatus = injectCortexMcpServer(
18631
+ detected.mcpPath,
18632
+ cortexUrl,
18633
+ cortexToken
18634
+ );
18635
+ if (mcpStatus.added) {
18636
+ console.log(source_default.green(` \u2713 ${label} \u2014 Cortex server added`));
18637
+ } else {
18638
+ console.log(source_default.cyan(` \u21BB ${label} \u2014 Cortex server updated`));
18639
+ }
18538
18640
  }
18539
18641
  const claudeMcp = configureClaudeDesktop(cortexUrl, cortexToken);
18540
18642
  if (claudeMcp.configured) {
@@ -27926,33 +28028,200 @@ async function serveCommand() {
27926
28028
  await runMcpServer();
27927
28029
  }
27928
28030
 
27929
- // src/commands/uninstall.ts
27930
- var import_path13 = require("path");
28031
+ // src/commands/server.ts
28032
+ var import_os4 = require("os");
27931
28033
  var import_fs12 = require("fs");
27932
- var import_prompts2 = __toESM(require_prompts3());
28034
+ var import_path13 = require("path");
28035
+ var import_prompts3 = __toESM(require_prompts3());
28036
+ var CORTEX_URL2 = "http://localhost:8082";
28037
+ var NEO4J_URL = "http://localhost:7474";
28038
+ async function serverCommand(options) {
28039
+ console.log("");
28040
+ console.log(source_default.bold.cyan(" \u25C6 Cortex Context \u2014 Server"));
28041
+ console.log(
28042
+ source_default.dim(
28043
+ " Installs and starts the Cortex stack (Neo4j + Cortex API) via Docker."
28044
+ )
28045
+ );
28046
+ console.log("");
28047
+ const defaultDir = (0, import_path13.join)((0, import_os4.homedir)(), ".cortex-context");
28048
+ const workDir = options.dir ?? defaultDir;
28049
+ (0, import_fs12.mkdirSync)(workDir, { recursive: true });
28050
+ console.log(source_default.bold(" Step 1: Docker"));
28051
+ const dockerReady = isDockerAvailable();
28052
+ if (dockerReady) {
28053
+ console.log(source_default.green(" \u2713 Docker is available"));
28054
+ } else {
28055
+ console.log(source_default.yellow(" \u26A0 Docker not found on PATH"));
28056
+ if (options.skipDockerInstall) {
28057
+ console.error(
28058
+ source_default.red(
28059
+ " \u2717 --skip-docker-install set but Docker is required. Install Docker manually and retry."
28060
+ )
28061
+ );
28062
+ process.exit(1);
28063
+ }
28064
+ const os2 = (0, import_os4.platform)();
28065
+ const installMethod = {
28066
+ linux: "curl | sh (official script, may require sudo)",
28067
+ darwin: "Homebrew (brew install --cask docker)",
28068
+ win32: "winget (winget install Docker.DockerDesktop)"
28069
+ };
28070
+ const method = installMethod[os2] ?? `unsupported OS (${os2})`;
28071
+ if (!installMethod[os2]) {
28072
+ console.error(
28073
+ source_default.red(` \u2717 Automatic Docker install is not supported on ${os2}.`)
28074
+ );
28075
+ console.error(
28076
+ source_default.dim(
28077
+ " Install Docker manually: https://docs.docker.com/get-docker/"
28078
+ )
28079
+ );
28080
+ process.exit(1);
28081
+ }
28082
+ console.log("");
28083
+ console.log(source_default.dim(` Install method: ${method}`));
28084
+ console.log(source_default.dim(" This will install Docker on your machine."));
28085
+ console.log("");
28086
+ const { confirm } = await (0, import_prompts3.default)({
28087
+ type: "confirm",
28088
+ name: "confirm",
28089
+ message: "Install Docker now?",
28090
+ initial: true
28091
+ });
28092
+ if (!confirm) {
28093
+ console.log(
28094
+ source_default.yellow("\n Aborted. Install Docker manually and run again.")
28095
+ );
28096
+ process.exit(0);
28097
+ }
28098
+ console.log("");
28099
+ const spinner = ora(" Installing Docker...").start();
28100
+ const result = await installDocker();
28101
+ if (!result.installed) {
28102
+ spinner.fail(source_default.red(` \u2717 Docker install failed: ${result.reason}`));
28103
+ console.log(
28104
+ source_default.dim(
28105
+ " Install Docker manually: https://docs.docker.com/get-docker/"
28106
+ )
28107
+ );
28108
+ process.exit(1);
28109
+ }
28110
+ spinner.succeed(source_default.green(" \u2713 Docker installed"));
28111
+ if (os2 === "win32") {
28112
+ console.log(
28113
+ source_default.yellow(
28114
+ " \u26A0 Docker Desktop was installed. Please start it manually, then re-run this command."
28115
+ )
28116
+ );
28117
+ process.exit(0);
28118
+ }
28119
+ if (os2 === "linux") {
28120
+ console.log(
28121
+ source_default.dim(
28122
+ " \u2139 You may need to log out and back in for the docker group to take effect."
28123
+ )
28124
+ );
28125
+ }
28126
+ console.log("");
28127
+ }
28128
+ console.log("");
28129
+ console.log(source_default.bold(" Step 2: Containers to start"));
28130
+ console.log(source_default.dim(" \u250C\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2510"));
28131
+ console.log(source_default.dim(" \u2502 neo4j \u2192 localhost:7474 (UI: 7474) \u2502"));
28132
+ console.log(source_default.dim(" \u2502 cortex-api \u2192 localhost:8082 \u2502"));
28133
+ console.log(source_default.dim(" \u2514\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2518"));
28134
+ console.log("");
28135
+ console.log(source_default.dim(` Working directory: ${workDir}`));
28136
+ console.log("");
28137
+ const { proceed } = await (0, import_prompts3.default)({
28138
+ type: "confirm",
28139
+ name: "proceed",
28140
+ message: "Start the Cortex stack now?",
28141
+ initial: true
28142
+ });
28143
+ if (!proceed) {
28144
+ console.log(source_default.yellow("\n Aborted."));
28145
+ process.exit(0);
28146
+ }
28147
+ console.log("");
28148
+ console.log(source_default.bold(" Step 3: Starting Cortex stack"));
28149
+ const startSpinner = ora(" Running docker compose up -d...").start();
28150
+ const startResult = await deployLocalStack(workDir);
28151
+ if (!startResult.started) {
28152
+ startSpinner.fail(
28153
+ source_default.red(` \u2717 Docker Compose failed: ${startResult.error}`)
28154
+ );
28155
+ process.exit(1);
28156
+ }
28157
+ startSpinner.succeed(source_default.green(" \u2713 Containers started"));
28158
+ console.log("");
28159
+ console.log(source_default.bold(" Step 4: Waiting for Cortex API..."));
28160
+ const healthSpinner = ora(
28161
+ " Polling http://localhost:8082/health (up to 2 min)..."
28162
+ ).start();
28163
+ const { healthy } = await waitForCortexHealth(CORTEX_URL2);
28164
+ if (!healthy) {
28165
+ healthSpinner.warn(
28166
+ source_default.yellow(
28167
+ " \u26A0 Cortex API did not respond in time \u2014 containers may still be starting."
28168
+ )
28169
+ );
28170
+ console.log(source_default.dim(` Check manually: curl ${CORTEX_URL2}/health`));
28171
+ console.log(
28172
+ source_default.dim(" Or: docker compose -f docker-compose.local.yml logs")
28173
+ );
28174
+ } else {
28175
+ healthSpinner.succeed(source_default.green(" \u2713 Cortex API is healthy"));
28176
+ }
28177
+ console.log("");
28178
+ console.log(source_default.bold.green(" \u2705 Cortex stack is running!"));
28179
+ console.log("");
28180
+ console.log(source_default.bold(" Services:"));
28181
+ console.log(source_default.cyan(` Cortex API \u2192 ${CORTEX_URL2}`));
28182
+ console.log(source_default.cyan(` Neo4j UI \u2192 ${NEO4J_URL}`));
28183
+ console.log(source_default.dim(" Neo4j login \u2192 neo4j / cortex-local"));
28184
+ console.log("");
28185
+ console.log(source_default.bold(" Next steps:"));
28186
+ console.log(source_default.dim(" 1. Initialize your workspace:"));
28187
+ console.log(source_default.cyan(` cortex-context init --url ${CORTEX_URL2}`));
28188
+ console.log(source_default.dim(" 2. Check connectivity:"));
28189
+ console.log(source_default.cyan(" cortex-context doctor"));
28190
+ console.log("");
28191
+ console.log(source_default.dim(" To stop the stack:"));
28192
+ console.log(source_default.dim(" docker compose -f docker-compose.local.yml down"));
28193
+ console.log("");
28194
+ }
28195
+
28196
+ // src/commands/uninstall.ts
28197
+ var import_path14 = require("path");
28198
+ var import_fs13 = require("fs");
28199
+ var import_prompts4 = __toESM(require_prompts3());
27933
28200
  var CORTEX_SKILLS = [
27934
28201
  "cortex-research",
27935
28202
  "cortex-dimensions",
27936
28203
  "cortex-ingest",
27937
- "generate-spec"
28204
+ "cortex-generate-spec"
27938
28205
  ];
27939
28206
  async function uninstallCommand(options) {
27940
28207
  console.log("");
27941
28208
  console.log(source_default.bold.cyan(" \u25C6 Cortex Context \u2014 Uninstall"));
27942
28209
  console.log(source_default.dim(" Removes Cortex Context from your workspace."));
27943
28210
  console.log("");
27944
- const workspacePath = (0, import_path13.resolve)(options.workspace ?? process.cwd());
28211
+ const workspacePath = (0, import_path14.resolve)(options.workspace ?? process.cwd());
27945
28212
  console.log(source_default.dim(` Workspace: ${workspacePath}`));
27946
28213
  console.log("");
27947
- const skillsPath = (0, import_path13.join)(workspacePath, ".github", "skills");
28214
+ const skillsPath = (0, import_path14.join)(workspacePath, ".github", "skills");
27948
28215
  const skillsToRemove = CORTEX_SKILLS.filter(
27949
- (s) => (0, import_fs12.existsSync)((0, import_path13.join)(skillsPath, s))
28216
+ (s) => (0, import_fs13.existsSync)((0, import_path14.join)(skillsPath, s))
28217
+ );
28218
+ const hasMcp = (0, import_fs13.existsSync)((0, import_path14.join)(workspacePath, ".vscode", "mcp.json"));
28219
+ const hasCursorMcp = (0, import_fs13.existsSync)((0, import_path14.join)(workspacePath, ".cursor", "mcp.json"));
28220
+ const hasHook = (0, import_fs13.existsSync)(
28221
+ (0, import_path14.join)(workspacePath, ".git", "hooks", "post-commit")
27950
28222
  );
27951
- const hasMcp = (0, import_fs12.existsSync)((0, import_path13.join)(workspacePath, ".vscode", "mcp.json"));
27952
- const hasCursorMcp = (0, import_fs12.existsSync)((0, import_path13.join)(workspacePath, ".cursor", "mcp.json"));
27953
- const hasHook = (0, import_fs12.existsSync)((0, import_path13.join)(workspacePath, ".git", "hooks", "post-commit"));
27954
- const hasConfig = (0, import_fs12.existsSync)(
27955
- (0, import_path13.join)(process.env["HOME"] ?? "~", ".cortex-context", "config.json")
28223
+ const hasConfig = (0, import_fs13.existsSync)(
28224
+ (0, import_path14.join)(process.env["HOME"] ?? "~", ".cortex-context", "config.json")
27956
28225
  );
27957
28226
  console.log(source_default.bold(" Will remove:"));
27958
28227
  if (hasMcp) {
@@ -27967,7 +28236,9 @@ async function uninstallCommand(options) {
27967
28236
  }
27968
28237
  }
27969
28238
  if (!options.keepHook && hasHook) {
27970
- console.log(source_default.dim(" \xB7 .git/hooks/post-commit (cortex auto-sync hook)"));
28239
+ console.log(
28240
+ source_default.dim(" \xB7 .git/hooks/post-commit (cortex auto-sync hook)")
28241
+ );
27971
28242
  }
27972
28243
  if (!options.keepConfig && hasConfig) {
27973
28244
  console.log(source_default.dim(" \xB7 ~/.cortex-context/config.json"));
@@ -27980,7 +28251,7 @@ async function uninstallCommand(options) {
27980
28251
  }
27981
28252
  console.log("");
27982
28253
  if (!options.yes) {
27983
- const { confirmed } = await (0, import_prompts2.default)({
28254
+ const { confirmed } = await (0, import_prompts4.default)({
27984
28255
  type: "confirm",
27985
28256
  name: "confirmed",
27986
28257
  message: "Proceed with uninstall?",
@@ -27999,13 +28270,21 @@ async function uninstallCommand(options) {
27999
28270
  try {
28000
28271
  const result = removeCortexMcpServer(workspacePath);
28001
28272
  if (result.removed) {
28002
- console.log(source_default.green(' \u2713 .vscode/mcp.json \u2014 "cortex" entry removed'));
28273
+ console.log(
28274
+ source_default.green(' \u2713 .vscode/mcp.json \u2014 "cortex" entry removed')
28275
+ );
28003
28276
  removed++;
28004
28277
  } else {
28005
- console.log(source_default.dim(' \xB7 .vscode/mcp.json \u2014 no "cortex" entry found'));
28278
+ console.log(
28279
+ source_default.dim(' \xB7 .vscode/mcp.json \u2014 no "cortex" entry found')
28280
+ );
28006
28281
  }
28007
28282
  } catch (err) {
28008
- console.log(source_default.red(` \u2717 .vscode/mcp.json \u2014 ${err instanceof Error ? err.message : err}`));
28283
+ console.log(
28284
+ source_default.red(
28285
+ ` \u2717 .vscode/mcp.json \u2014 ${err instanceof Error ? err.message : err}`
28286
+ )
28287
+ );
28009
28288
  errors++;
28010
28289
  }
28011
28290
  }
@@ -28013,46 +28292,62 @@ async function uninstallCommand(options) {
28013
28292
  try {
28014
28293
  const result = removeCursorMcp(workspacePath);
28015
28294
  if (result.removed) {
28016
- console.log(source_default.green(' \u2713 .cursor/mcp.json \u2014 "cortex" entry removed'));
28295
+ console.log(
28296
+ source_default.green(' \u2713 .cursor/mcp.json \u2014 "cortex" entry removed')
28297
+ );
28017
28298
  removed++;
28018
28299
  }
28019
28300
  } catch (err) {
28020
- console.log(source_default.red(` \u2717 .cursor/mcp.json \u2014 ${err instanceof Error ? err.message : err}`));
28301
+ console.log(
28302
+ source_default.red(
28303
+ ` \u2717 .cursor/mcp.json \u2014 ${err instanceof Error ? err.message : err}`
28304
+ )
28305
+ );
28021
28306
  errors++;
28022
28307
  }
28023
28308
  }
28024
28309
  try {
28025
28310
  const result = removeClaudeDesktopMcp();
28026
28311
  if (result.removed) {
28027
- console.log(source_default.green(` \u2713 Claude Desktop \u2014 "cortex" entry removed (${result.path})`));
28312
+ console.log(
28313
+ source_default.green(
28314
+ ` \u2713 Claude Desktop \u2014 "cortex" entry removed (${result.path})`
28315
+ )
28316
+ );
28028
28317
  removed++;
28029
28318
  }
28030
28319
  } catch {
28031
28320
  }
28032
28321
  if (!options.keepSkills) {
28033
28322
  for (const skill of skillsToRemove) {
28034
- const skillDir = (0, import_path13.join)(skillsPath, skill);
28323
+ const skillDir = (0, import_path14.join)(skillsPath, skill);
28035
28324
  try {
28036
- (0, import_fs12.rmSync)(skillDir, { recursive: true, force: true });
28325
+ (0, import_fs13.rmSync)(skillDir, { recursive: true, force: true });
28037
28326
  console.log(source_default.green(` \u2713 .github/skills/${skill}/ \u2014 removed`));
28038
28327
  removed++;
28039
28328
  } catch (err) {
28040
- console.log(source_default.red(` \u2717 .github/skills/${skill}/ \u2014 ${err instanceof Error ? err.message : err}`));
28329
+ console.log(
28330
+ source_default.red(
28331
+ ` \u2717 .github/skills/${skill}/ \u2014 ${err instanceof Error ? err.message : err}`
28332
+ )
28333
+ );
28041
28334
  errors++;
28042
28335
  }
28043
28336
  }
28044
28337
  }
28045
28338
  if (!options.keepHook && hasHook) {
28046
- const hookPath = (0, import_path13.join)(workspacePath, ".git", "hooks", "post-commit");
28339
+ const hookPath = (0, import_path14.join)(workspacePath, ".git", "hooks", "post-commit");
28047
28340
  try {
28048
28341
  const { readFileSync: readFileSync7 } = await import("fs");
28049
28342
  const hookContent = readFileSync7(hookPath, "utf-8");
28050
28343
  if (hookContent.includes("cortex-context")) {
28051
- (0, import_fs12.unlinkSync)(hookPath);
28344
+ (0, import_fs13.unlinkSync)(hookPath);
28052
28345
  console.log(source_default.green(" \u2713 .git/hooks/post-commit \u2014 removed"));
28053
28346
  removed++;
28054
28347
  } else {
28055
- console.log(source_default.dim(" \xB7 .git/hooks/post-commit \u2014 not ours, skipped"));
28348
+ console.log(
28349
+ source_default.dim(" \xB7 .git/hooks/post-commit \u2014 not ours, skipped")
28350
+ );
28056
28351
  }
28057
28352
  } catch {
28058
28353
  }
@@ -28063,26 +28358,42 @@ async function uninstallCommand(options) {
28063
28358
  console.log(source_default.green(" \u2713 ~/.cortex-context/config.json \u2014 removed"));
28064
28359
  removed++;
28065
28360
  } catch (err) {
28066
- console.log(source_default.red(` \u2717 ~/.cortex-context/config.json \u2014 ${err instanceof Error ? err.message : err}`));
28361
+ console.log(
28362
+ source_default.red(
28363
+ ` \u2717 ~/.cortex-context/config.json \u2014 ${err instanceof Error ? err.message : err}`
28364
+ )
28365
+ );
28067
28366
  errors++;
28068
28367
  }
28069
28368
  }
28070
28369
  console.log("");
28071
28370
  if (errors === 0) {
28072
- console.log(source_default.bold.green(` \u2705 Uninstall complete (${removed} item${removed !== 1 ? "s" : ""} removed)`));
28371
+ console.log(
28372
+ source_default.bold.green(
28373
+ ` \u2705 Uninstall complete (${removed} item${removed !== 1 ? "s" : ""} removed)`
28374
+ )
28375
+ );
28073
28376
  console.log("");
28074
- console.log(source_default.dim(' Reload VS Code (Cmd/Ctrl+Shift+P \u2192 "Reload Window") to deactivate the MCP server.'));
28377
+ console.log(
28378
+ source_default.dim(
28379
+ ' Reload VS Code (Cmd/Ctrl+Shift+P \u2192 "Reload Window") to deactivate the MCP server.'
28380
+ )
28381
+ );
28075
28382
  } else {
28076
- console.log(source_default.bold.yellow(` \u26A0 Uninstall finished with ${errors} error${errors !== 1 ? "s" : ""}`));
28383
+ console.log(
28384
+ source_default.bold.yellow(
28385
+ ` \u26A0 Uninstall finished with ${errors} error${errors !== 1 ? "s" : ""}`
28386
+ )
28387
+ );
28077
28388
  }
28078
28389
  console.log("");
28079
28390
  }
28080
28391
 
28081
28392
  // src/cli.ts
28082
- var import_fs13 = require("fs");
28083
- var import_path14 = require("path");
28393
+ var import_fs14 = require("fs");
28394
+ var import_path15 = require("path");
28084
28395
  var pkg = JSON.parse(
28085
- (0, import_fs13.readFileSync)((0, import_path14.join)(__dirname, "..", "package.json"), "utf-8")
28396
+ (0, import_fs14.readFileSync)((0, import_path15.join)(__dirname, "..", "package.json"), "utf-8")
28086
28397
  );
28087
28398
  function createCli() {
28088
28399
  const program3 = new Command();
@@ -28101,6 +28412,15 @@ function createCli() {
28101
28412
  "--local",
28102
28413
  "Deploy a local Cortex stack with Docker (mutually exclusive with --url)"
28103
28414
  ).option("--skip-mcp", "Skip MCP server configuration").option("--skip-skills", "Skip Skills installation").option("--skip-hooks", "Skip git hook installation").option("--skip-rules", "Skip Copilot/Cursor rules injection").option("--force", "Overwrite existing files without prompting").action(initCommand);
28415
+ program3.command("server").description(
28416
+ "Install Docker (if needed) and start the Cortex stack (Neo4j + Cortex API) locally"
28417
+ ).option(
28418
+ "--dir <path>",
28419
+ "Directory to place docker-compose.local.yml (defaults to current directory)"
28420
+ ).option(
28421
+ "--skip-docker-install",
28422
+ "Fail instead of auto-installing Docker when it is not found"
28423
+ ).action(serverCommand);
28104
28424
  program3.command("sync").description("Ingest latest git diff into the Cortex Knowledge Graph").option("--repo <path>", "Repository path (defaults to current directory)").option("--dry-run", "Show diff without sending to Cortex API").action(syncCommand);
28105
28425
  program3.command("update").description(
28106
28426
  "Update Skills and MCP Server files to the latest bundled version"