hipcortex 0.2.0__py3-none-any.whl
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.
- hipcortex/__init__.py +15 -0
- hipcortex/adapters/__init__.py +1 -0
- hipcortex/adapters/autogen.py +156 -0
- hipcortex/adapters/crewai.py +153 -0
- hipcortex/async_client.py +255 -0
- hipcortex/cli.py +480 -0
- hipcortex/client.py +223 -0
- hipcortex/install/SKILL.md +114 -0
- hipcortex/install/__init__.py +1 -0
- hipcortex/langchain_memory.py +238 -0
- hipcortex/llamaindex_storage.py +107 -0
- hipcortex-0.2.0.dist-info/METADATA +108 -0
- hipcortex-0.2.0.dist-info/RECORD +16 -0
- hipcortex-0.2.0.dist-info/WHEEL +5 -0
- hipcortex-0.2.0.dist-info/entry_points.txt +2 -0
- hipcortex-0.2.0.dist-info/top_level.txt +1 -0
hipcortex/cli.py
ADDED
|
@@ -0,0 +1,480 @@
|
|
|
1
|
+
"""HipCortex CLI — install command auto-configures AI coding assistants.
|
|
2
|
+
|
|
3
|
+
Usage:
|
|
4
|
+
hipcortex install # download binary + configure Claude Code + Cursor
|
|
5
|
+
hipcortex install --url URL # use existing server instead of local binary
|
|
6
|
+
hipcortex start # start the local server (downloads if needed)
|
|
7
|
+
hipcortex status # check server health
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import argparse
|
|
13
|
+
import json
|
|
14
|
+
import os
|
|
15
|
+
import platform
|
|
16
|
+
import shutil
|
|
17
|
+
import stat
|
|
18
|
+
import sys
|
|
19
|
+
import urllib.request
|
|
20
|
+
from pathlib import Path
|
|
21
|
+
from typing import Optional
|
|
22
|
+
|
|
23
|
+
# ─── Constants ───────────────────────────────────────────────────────────────
|
|
24
|
+
|
|
25
|
+
GITHUB_RELEASES = "https://github.com/farmountain/HipCortex/releases/latest/download"
|
|
26
|
+
INSTALL_DIR = Path.home() / ".hipcortex"
|
|
27
|
+
BINARY_NAME = "hipcortex-server"
|
|
28
|
+
DEFAULT_URL = "http://localhost:3030"
|
|
29
|
+
MANAGED_URL = "https://hipcortex.fly.dev"
|
|
30
|
+
|
|
31
|
+
# Claude Code registration line appended to ~/.claude/CLAUDE.md
|
|
32
|
+
_CLAUDE_REGISTRATION = """
|
|
33
|
+
# hipcortex
|
|
34
|
+
- **hipcortex** (`~/.claude/skills/hipcortex/SKILL.md`) - Persistent memory for AI agents. Store decisions, recall context, GDPR forget. Trigger: `/hipcortex`
|
|
35
|
+
When the user types `/hipcortex`, invoke the Skill tool with `skill: "hipcortex"` before doing anything else.
|
|
36
|
+
"""
|
|
37
|
+
|
|
38
|
+
# ─── Platform detection ───────────────────────────────────────────────────────
|
|
39
|
+
|
|
40
|
+
def _detect_platform() -> tuple[str, str]:
|
|
41
|
+
"""Return (os_name, arch) matching GitHub release asset names."""
|
|
42
|
+
system = platform.system().lower()
|
|
43
|
+
machine = platform.machine().lower()
|
|
44
|
+
|
|
45
|
+
if system == "darwin":
|
|
46
|
+
os_name = "macos"
|
|
47
|
+
elif system == "linux":
|
|
48
|
+
os_name = "linux"
|
|
49
|
+
elif system == "windows":
|
|
50
|
+
os_name = "windows"
|
|
51
|
+
else:
|
|
52
|
+
raise RuntimeError(f"Unsupported OS: {system}")
|
|
53
|
+
|
|
54
|
+
if machine in ("arm64", "aarch64"):
|
|
55
|
+
arch = "arm64"
|
|
56
|
+
elif machine in ("x86_64", "amd64"):
|
|
57
|
+
arch = "amd64"
|
|
58
|
+
else:
|
|
59
|
+
raise RuntimeError(f"Unsupported architecture: {machine}")
|
|
60
|
+
|
|
61
|
+
return os_name, arch
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def _binary_url(os_name: str, arch: str) -> str:
|
|
65
|
+
name = f"hipcortex-{os_name}-{arch}"
|
|
66
|
+
if os_name == "windows":
|
|
67
|
+
name += ".exe"
|
|
68
|
+
return f"{GITHUB_RELEASES}/{name}"
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
def _binary_path(os_name: str, arch: str) -> Path:
|
|
72
|
+
name = BINARY_NAME
|
|
73
|
+
if os_name == "windows":
|
|
74
|
+
name += ".exe"
|
|
75
|
+
return INSTALL_DIR / name
|
|
76
|
+
|
|
77
|
+
# ─── Download ────────────────────────────────────────────────────────────────
|
|
78
|
+
|
|
79
|
+
def _download_binary(url: str, dest: Path) -> None:
|
|
80
|
+
"""Download binary with progress indicator."""
|
|
81
|
+
INSTALL_DIR.mkdir(parents=True, exist_ok=True)
|
|
82
|
+
print(f" Downloading {url.split('/')[-1]} ...", end=" ", flush=True)
|
|
83
|
+
try:
|
|
84
|
+
urllib.request.urlretrieve(url, str(dest))
|
|
85
|
+
dest.chmod(dest.stat().st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)
|
|
86
|
+
print("✓")
|
|
87
|
+
except urllib.error.HTTPError as e:
|
|
88
|
+
print(f"✗ (HTTP {e.code})")
|
|
89
|
+
raise RuntimeError(
|
|
90
|
+
f"Could not download binary from {url}\n"
|
|
91
|
+
"Check https://github.com/farmountain/HipCortex/releases for available builds."
|
|
92
|
+
) from e
|
|
93
|
+
except Exception as e:
|
|
94
|
+
print(f"✗ ({e})")
|
|
95
|
+
raise
|
|
96
|
+
|
|
97
|
+
# ─── Skill registration (Claude Code) ────────────────────────────────────────
|
|
98
|
+
|
|
99
|
+
def _skill_dir() -> Path:
|
|
100
|
+
return Path.home() / ".claude" / "skills" / "hipcortex"
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
def _install_claude_code(server_url: str) -> bool:
|
|
104
|
+
"""Write SKILL.md + append to CLAUDE.md. Returns True on success."""
|
|
105
|
+
claude_dir = Path.home() / ".claude"
|
|
106
|
+
if not claude_dir.exists():
|
|
107
|
+
return False # Claude Code not installed
|
|
108
|
+
|
|
109
|
+
# Write skill file
|
|
110
|
+
skill_dir = _skill_dir()
|
|
111
|
+
skill_dir.mkdir(parents=True, exist_ok=True)
|
|
112
|
+
skill_md_src = Path(__file__).parent / "install" / "SKILL.md"
|
|
113
|
+
|
|
114
|
+
# Read template and substitute server URL
|
|
115
|
+
content = skill_md_src.read_text(encoding="utf-8")
|
|
116
|
+
content = content.replace("http://localhost:3030", server_url)
|
|
117
|
+
(skill_dir / "SKILL.md").write_text(content, encoding="utf-8")
|
|
118
|
+
|
|
119
|
+
# Append registration to CLAUDE.md if not already present
|
|
120
|
+
claude_md = claude_dir / "CLAUDE.md"
|
|
121
|
+
existing = claude_md.read_text(encoding="utf-8") if claude_md.exists() else ""
|
|
122
|
+
if "hipcortex" not in existing:
|
|
123
|
+
with claude_md.open("a", encoding="utf-8") as f:
|
|
124
|
+
f.write(_CLAUDE_REGISTRATION)
|
|
125
|
+
|
|
126
|
+
return True
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _uninstall_claude_code() -> None:
|
|
130
|
+
"""Remove HipCortex skill from Claude Code."""
|
|
131
|
+
skill_dir = _skill_dir()
|
|
132
|
+
if skill_dir.exists():
|
|
133
|
+
shutil.rmtree(skill_dir)
|
|
134
|
+
claude_md = Path.home() / ".claude" / "CLAUDE.md"
|
|
135
|
+
if claude_md.exists():
|
|
136
|
+
text = claude_md.read_text(encoding="utf-8")
|
|
137
|
+
# Remove the registration block
|
|
138
|
+
marker = "\n# hipcortex"
|
|
139
|
+
if marker in text:
|
|
140
|
+
idx = text.index(marker)
|
|
141
|
+
# Find end of registration block (next \n# or end of file)
|
|
142
|
+
end = text.find("\n# ", idx + 1)
|
|
143
|
+
claude_md.write_text(
|
|
144
|
+
text[:idx] + (text[end:] if end != -1 else ""),
|
|
145
|
+
encoding="utf-8",
|
|
146
|
+
)
|
|
147
|
+
|
|
148
|
+
# ─── MCP registration (Cursor / Windsurf) ────────────────────────────────────
|
|
149
|
+
|
|
150
|
+
def _cursor_mcp_path(global_: bool = False) -> Optional[Path]:
|
|
151
|
+
"""Return path to Cursor mcp.json — local (project) or global."""
|
|
152
|
+
if global_:
|
|
153
|
+
# Global Cursor config location by OS
|
|
154
|
+
if platform.system() == "Windows":
|
|
155
|
+
base = Path(os.environ.get("APPDATA", Path.home()))
|
|
156
|
+
elif platform.system() == "Darwin":
|
|
157
|
+
base = Path.home() / "Library" / "Application Support" / "Cursor"
|
|
158
|
+
else:
|
|
159
|
+
base = Path(os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config")) / "Cursor"
|
|
160
|
+
mcp_path = base / "mcp.json"
|
|
161
|
+
return mcp_path
|
|
162
|
+
else:
|
|
163
|
+
return Path.cwd() / ".cursor" / "mcp.json"
|
|
164
|
+
|
|
165
|
+
|
|
166
|
+
def _install_cursor(server_url: str, global_: bool = False) -> bool:
|
|
167
|
+
"""Write/update .cursor/mcp.json. Returns True on success."""
|
|
168
|
+
mcp_path = _cursor_mcp_path(global_=global_)
|
|
169
|
+
if mcp_path is None:
|
|
170
|
+
return False
|
|
171
|
+
|
|
172
|
+
mcp_path.parent.mkdir(parents=True, exist_ok=True)
|
|
173
|
+
|
|
174
|
+
# Read existing config or start fresh
|
|
175
|
+
existing: dict = {}
|
|
176
|
+
if mcp_path.exists():
|
|
177
|
+
try:
|
|
178
|
+
existing = json.loads(mcp_path.read_text(encoding="utf-8"))
|
|
179
|
+
except json.JSONDecodeError:
|
|
180
|
+
existing = {}
|
|
181
|
+
|
|
182
|
+
# Inject hipcortex MCP server entry
|
|
183
|
+
mcp_servers = existing.setdefault("mcpServers", {})
|
|
184
|
+
mcp_server_py = str(Path.home() / ".hipcortex-mcp" / "server.py")
|
|
185
|
+
mcp_servers["hipcortex"] = {
|
|
186
|
+
"command": sys.executable,
|
|
187
|
+
"args": [mcp_server_py],
|
|
188
|
+
"env": {"HIPCORTEX_URL": server_url},
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
mcp_path.write_text(json.dumps(existing, indent=2), encoding="utf-8")
|
|
192
|
+
return True
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def _install_vscode(server_url: str) -> bool:
|
|
196
|
+
"""Write mcpServers to VS Code settings.json. Returns True on success."""
|
|
197
|
+
if platform.system() == "Windows":
|
|
198
|
+
settings_path = Path(os.environ.get("APPDATA", "")) / "Code" / "User" / "settings.json"
|
|
199
|
+
elif platform.system() == "Darwin":
|
|
200
|
+
settings_path = Path.home() / "Library" / "Application Support" / "Code" / "User" / "settings.json"
|
|
201
|
+
else:
|
|
202
|
+
settings_path = Path(os.environ.get("XDG_CONFIG_HOME", Path.home() / ".config")) / "Code" / "User" / "settings.json"
|
|
203
|
+
|
|
204
|
+
if not settings_path.exists():
|
|
205
|
+
return False # VS Code not installed
|
|
206
|
+
|
|
207
|
+
try:
|
|
208
|
+
settings: dict = json.loads(settings_path.read_text(encoding="utf-8"))
|
|
209
|
+
except (json.JSONDecodeError, PermissionError):
|
|
210
|
+
return False
|
|
211
|
+
|
|
212
|
+
mcp_server_py = str(Path.home() / ".hipcortex-mcp" / "server.py")
|
|
213
|
+
mcp_servers = settings.setdefault("mcpServers", {})
|
|
214
|
+
mcp_servers["hipcortex"] = {
|
|
215
|
+
"command": sys.executable,
|
|
216
|
+
"args": [mcp_server_py],
|
|
217
|
+
"env": {"HIPCORTEX_URL": server_url},
|
|
218
|
+
}
|
|
219
|
+
settings_path.write_text(json.dumps(settings, indent=2), encoding="utf-8")
|
|
220
|
+
return True
|
|
221
|
+
|
|
222
|
+
# ─── MCP server script installer ─────────────────────────────────────────────
|
|
223
|
+
|
|
224
|
+
def _install_mcp_server() -> None:
|
|
225
|
+
"""Copy the MCP server script to ~/.hipcortex-mcp/server.py."""
|
|
226
|
+
mcp_dir = Path.home() / ".hipcortex-mcp"
|
|
227
|
+
mcp_dir.mkdir(parents=True, exist_ok=True)
|
|
228
|
+
src = Path(__file__).parent.parent.parent / "mcp" / "server.py" # sdk/mcp/server.py
|
|
229
|
+
# Try repo-relative path first, then PyPI-installed package
|
|
230
|
+
if not src.exists():
|
|
231
|
+
import importlib.resources
|
|
232
|
+
try:
|
|
233
|
+
# When installed as a package, look for bundled resource
|
|
234
|
+
with importlib.resources.path("hipcortex.install", "mcp_server.py") as p:
|
|
235
|
+
src = p
|
|
236
|
+
except Exception:
|
|
237
|
+
return # MCP server not bundled, skip
|
|
238
|
+
|
|
239
|
+
if src.exists():
|
|
240
|
+
shutil.copy2(str(src), str(mcp_dir / "server.py"))
|
|
241
|
+
|
|
242
|
+
# ─── Commands ────────────────────────────────────────────────────────────────
|
|
243
|
+
|
|
244
|
+
def cmd_install(args: argparse.Namespace) -> None:
|
|
245
|
+
"""Download binary + configure all detected AI coding assistants."""
|
|
246
|
+
print("\nHipCortex installer\n" + "=" * 40)
|
|
247
|
+
|
|
248
|
+
# Determine server URL
|
|
249
|
+
if args.url:
|
|
250
|
+
server_url = args.url.rstrip("/")
|
|
251
|
+
binary_path = None
|
|
252
|
+
print(f"Using existing server: {server_url}")
|
|
253
|
+
else:
|
|
254
|
+
os_name, arch = _detect_platform()
|
|
255
|
+
binary_path = _binary_path(os_name, arch)
|
|
256
|
+
server_url = DEFAULT_URL
|
|
257
|
+
|
|
258
|
+
if binary_path.exists() and not args.force:
|
|
259
|
+
print(f" Binary already at {binary_path} (use --force to re-download)")
|
|
260
|
+
else:
|
|
261
|
+
url = _binary_url(os_name, arch)
|
|
262
|
+
_download_binary(url, binary_path)
|
|
263
|
+
|
|
264
|
+
# Install MCP server script for Cursor/VS Code
|
|
265
|
+
_install_mcp_server()
|
|
266
|
+
|
|
267
|
+
# Register with AI coding assistants
|
|
268
|
+
print("\nRegistering with AI coding assistants:")
|
|
269
|
+
results = []
|
|
270
|
+
|
|
271
|
+
if _install_claude_code(server_url):
|
|
272
|
+
results.append(("Claude Code", "✓", f"~/.claude/skills/hipcortex/"))
|
|
273
|
+
else:
|
|
274
|
+
results.append(("Claude Code", "–", "not found (install from claude.ai/code)"))
|
|
275
|
+
|
|
276
|
+
if _install_cursor(server_url, global_=False):
|
|
277
|
+
results.append(("Cursor (project)", "✓", str(Path.cwd() / ".cursor" / "mcp.json")))
|
|
278
|
+
elif _install_cursor(server_url, global_=True):
|
|
279
|
+
mcp_path = _cursor_mcp_path(global_=True)
|
|
280
|
+
results.append(("Cursor (global)", "✓", str(mcp_path)))
|
|
281
|
+
else:
|
|
282
|
+
results.append(("Cursor", "–", "not found"))
|
|
283
|
+
|
|
284
|
+
if _install_vscode(server_url):
|
|
285
|
+
results.append(("VS Code", "✓", "settings.json"))
|
|
286
|
+
else:
|
|
287
|
+
results.append(("VS Code", "–", "not found"))
|
|
288
|
+
|
|
289
|
+
for name, status, detail in results:
|
|
290
|
+
print(f" {status} {name:<20} {detail}")
|
|
291
|
+
|
|
292
|
+
print()
|
|
293
|
+
|
|
294
|
+
if binary_path:
|
|
295
|
+
print(f"Binary: {binary_path}")
|
|
296
|
+
print(f"Start: hipcortex start")
|
|
297
|
+
print()
|
|
298
|
+
|
|
299
|
+
# Print usage instructions
|
|
300
|
+
claude_ok = any(s == "✓" and "Claude Code" in n for n, s, _ in results)
|
|
301
|
+
cursor_ok = any(s == "✓" and "Cursor" in n for n, s, _ in results)
|
|
302
|
+
|
|
303
|
+
if claude_ok:
|
|
304
|
+
print("Claude Code: type /hipcortex remember 'your note'")
|
|
305
|
+
if cursor_ok:
|
|
306
|
+
print("Cursor: restart and use the hipcortex MCP tools")
|
|
307
|
+
print()
|
|
308
|
+
print(f"Docs: https://github.com/farmountain/HipCortex")
|
|
309
|
+
|
|
310
|
+
# Auto-start the server if binary was downloaded and server isn't already running
|
|
311
|
+
if binary_path and binary_path.exists():
|
|
312
|
+
health_url = f"http://localhost:3030/health"
|
|
313
|
+
already_running = False
|
|
314
|
+
try:
|
|
315
|
+
with urllib.request.urlopen(health_url, timeout=1) as r:
|
|
316
|
+
already_running = r.status == 200
|
|
317
|
+
except Exception:
|
|
318
|
+
pass
|
|
319
|
+
|
|
320
|
+
if not already_running:
|
|
321
|
+
import subprocess as _sp
|
|
322
|
+
data_dir = str(INSTALL_DIR / "data")
|
|
323
|
+
import pathlib as _pl
|
|
324
|
+
_pl.Path(data_dir).mkdir(parents=True, exist_ok=True)
|
|
325
|
+
env = os.environ.copy()
|
|
326
|
+
env["PORT"] = "3030"
|
|
327
|
+
env["DATA_DIR"] = data_dir
|
|
328
|
+
env["RUST_LOG"] = "warn"
|
|
329
|
+
_sp.Popen(
|
|
330
|
+
[str(binary_path)],
|
|
331
|
+
env=env,
|
|
332
|
+
stdout=_sp.DEVNULL,
|
|
333
|
+
stderr=_sp.DEVNULL,
|
|
334
|
+
)
|
|
335
|
+
# Wait for startup
|
|
336
|
+
import time as _time
|
|
337
|
+
print("Starting HipCortex server...", end=" ", flush=True)
|
|
338
|
+
for _ in range(20):
|
|
339
|
+
_time.sleep(0.5)
|
|
340
|
+
try:
|
|
341
|
+
with urllib.request.urlopen(health_url, timeout=1) as r:
|
|
342
|
+
if r.status == 200:
|
|
343
|
+
print("✓ running on http://localhost:3030")
|
|
344
|
+
break
|
|
345
|
+
except Exception:
|
|
346
|
+
pass
|
|
347
|
+
else:
|
|
348
|
+
print("(starting in background)")
|
|
349
|
+
|
|
350
|
+
|
|
351
|
+
def cmd_start(args: argparse.Namespace) -> None:
|
|
352
|
+
"""Start the local HipCortex server."""
|
|
353
|
+
try:
|
|
354
|
+
os_name, arch = _detect_platform()
|
|
355
|
+
except RuntimeError as e:
|
|
356
|
+
print(f"Error: {e}", file=sys.stderr)
|
|
357
|
+
sys.exit(1)
|
|
358
|
+
|
|
359
|
+
binary = _binary_path(os_name, arch)
|
|
360
|
+
if not binary.exists():
|
|
361
|
+
print(f"Binary not found at {binary}")
|
|
362
|
+
print("Run: hipcortex install")
|
|
363
|
+
sys.exit(1)
|
|
364
|
+
|
|
365
|
+
port = args.port or int(os.environ.get("PORT", "3030"))
|
|
366
|
+
data_dir = args.data_dir or os.environ.get("DATA_DIR", str(Path.home() / ".hipcortex" / "data"))
|
|
367
|
+
Path(data_dir).mkdir(parents=True, exist_ok=True)
|
|
368
|
+
|
|
369
|
+
env = os.environ.copy()
|
|
370
|
+
env["PORT"] = str(port)
|
|
371
|
+
env["DATA_DIR"] = data_dir
|
|
372
|
+
|
|
373
|
+
print(f"Starting HipCortex on http://localhost:{port}")
|
|
374
|
+
print(f"Data: {data_dir}")
|
|
375
|
+
print("Ctrl+C to stop\n")
|
|
376
|
+
|
|
377
|
+
import subprocess
|
|
378
|
+
proc = subprocess.Popen([str(binary)], env=env)
|
|
379
|
+
# Poll /health until server is ready (max 10 seconds)
|
|
380
|
+
import time
|
|
381
|
+
health_url = f"http://localhost:{port}/health"
|
|
382
|
+
for _ in range(20):
|
|
383
|
+
time.sleep(0.5)
|
|
384
|
+
try:
|
|
385
|
+
with urllib.request.urlopen(health_url, timeout=1) as r:
|
|
386
|
+
if r.status == 200:
|
|
387
|
+
print(f"✓ HipCortex running on http://localhost:{port}")
|
|
388
|
+
print(f" /hipcortex remember 'your note' (Claude Code)")
|
|
389
|
+
print(f" curl {health_url}")
|
|
390
|
+
print()
|
|
391
|
+
break
|
|
392
|
+
except Exception:
|
|
393
|
+
pass
|
|
394
|
+
else:
|
|
395
|
+
print(" Server may still be starting... check health manually.")
|
|
396
|
+
print(f" curl {health_url}")
|
|
397
|
+
print()
|
|
398
|
+
try:
|
|
399
|
+
proc.wait()
|
|
400
|
+
except KeyboardInterrupt:
|
|
401
|
+
proc.terminate()
|
|
402
|
+
proc.wait()
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def cmd_status(args: argparse.Namespace) -> None:
|
|
406
|
+
"""Check server health."""
|
|
407
|
+
url = args.url or os.environ.get("HIPCORTEX_URL", DEFAULT_URL)
|
|
408
|
+
try:
|
|
409
|
+
import urllib.request
|
|
410
|
+
with urllib.request.urlopen(f"{url}/health", timeout=5) as r:
|
|
411
|
+
print(f"✓ HipCortex running at {url} ({r.read().decode().strip()})")
|
|
412
|
+
except Exception as e:
|
|
413
|
+
print(f"✗ Not reachable at {url}: {e}")
|
|
414
|
+
print(" Run: hipcortex start")
|
|
415
|
+
|
|
416
|
+
|
|
417
|
+
def cmd_uninstall(args: argparse.Namespace) -> None:
|
|
418
|
+
"""Remove HipCortex from AI coding assistants and optionally delete binary."""
|
|
419
|
+
print("Uninstalling HipCortex...")
|
|
420
|
+
_uninstall_claude_code()
|
|
421
|
+
print(" ✓ Removed from Claude Code")
|
|
422
|
+
|
|
423
|
+
if args.purge:
|
|
424
|
+
if INSTALL_DIR.exists():
|
|
425
|
+
shutil.rmtree(INSTALL_DIR)
|
|
426
|
+
print(f" ✓ Deleted {INSTALL_DIR}")
|
|
427
|
+
mcp_dir = Path.home() / ".hipcortex-mcp"
|
|
428
|
+
if mcp_dir.exists():
|
|
429
|
+
shutil.rmtree(mcp_dir)
|
|
430
|
+
print(f" ✓ Deleted {mcp_dir}")
|
|
431
|
+
|
|
432
|
+
# ─── Argument parser ──────────────────────────────────────────────────────────
|
|
433
|
+
|
|
434
|
+
def build_parser() -> argparse.ArgumentParser:
|
|
435
|
+
parser = argparse.ArgumentParser(
|
|
436
|
+
prog="hipcortex",
|
|
437
|
+
description="HipCortex memory engine — persistent causal memory for AI agents",
|
|
438
|
+
)
|
|
439
|
+
sub = parser.add_subparsers(dest="command")
|
|
440
|
+
|
|
441
|
+
# install
|
|
442
|
+
p_install = sub.add_parser("install", help="Download binary + configure AI coding assistants")
|
|
443
|
+
p_install.add_argument("--url", help=f"Use an existing server instead of local binary (e.g. {MANAGED_URL})")
|
|
444
|
+
p_install.add_argument("--force", action="store_true", help="Re-download binary even if it exists")
|
|
445
|
+
|
|
446
|
+
# start
|
|
447
|
+
p_start = sub.add_parser("start", help="Start the local HipCortex server")
|
|
448
|
+
p_start.add_argument("--port", type=int, help="Port (default: 3030)")
|
|
449
|
+
p_start.add_argument("--data-dir", help="Data directory (default: ~/.hipcortex/data)")
|
|
450
|
+
|
|
451
|
+
# status
|
|
452
|
+
p_status = sub.add_parser("status", help="Check server health")
|
|
453
|
+
p_status.add_argument("--url", help="Server URL to check")
|
|
454
|
+
|
|
455
|
+
# uninstall
|
|
456
|
+
p_uninstall = sub.add_parser("uninstall", help="Remove HipCortex configuration")
|
|
457
|
+
p_uninstall.add_argument("--purge", action="store_true", help="Also delete downloaded binary and data")
|
|
458
|
+
|
|
459
|
+
return parser
|
|
460
|
+
|
|
461
|
+
|
|
462
|
+
def main() -> None:
|
|
463
|
+
parser = build_parser()
|
|
464
|
+
args = parser.parse_args()
|
|
465
|
+
|
|
466
|
+
if args.command == "install":
|
|
467
|
+
cmd_install(args)
|
|
468
|
+
elif args.command == "start":
|
|
469
|
+
cmd_start(args)
|
|
470
|
+
elif args.command == "status":
|
|
471
|
+
cmd_status(args)
|
|
472
|
+
elif args.command == "uninstall":
|
|
473
|
+
cmd_uninstall(args)
|
|
474
|
+
else:
|
|
475
|
+
parser.print_help()
|
|
476
|
+
sys.exit(1)
|
|
477
|
+
|
|
478
|
+
|
|
479
|
+
if __name__ == "__main__":
|
|
480
|
+
main()
|