navcode 0.1.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.
- navcode/__init__.py +22 -0
- navcode/_bootstrap.py +122 -0
- navcode/adapters/__init__.py +36 -0
- navcode/adapters/antigravity.py +31 -0
- navcode/adapters/base.py +100 -0
- navcode/adapters/bob.py +33 -0
- navcode/adapters/claude.py +33 -0
- navcode/adapters/codex.py +33 -0
- navcode/adapters/continue_.py +31 -0
- navcode/adapters/copilot.py +24 -0
- navcode/adapters/cursor.py +33 -0
- navcode/adapters/kimi.py +31 -0
- navcode/adapters/windsurf.py +29 -0
- navcode/classifier.py +320 -0
- navcode/cli.py +359 -0
- navcode/embeddings.py +184 -0
- navcode/graph.py +394 -0
- navcode/indexer.py +646 -0
- navcode/mcp_server.py +271 -0
- navcode/parser.py +755 -0
- navcode/retriever.py +471 -0
- navcode/watcher.py +242 -0
- navcode-0.1.0.dist-info/METADATA +81 -0
- navcode-0.1.0.dist-info/RECORD +28 -0
- navcode-0.1.0.dist-info/WHEEL +5 -0
- navcode-0.1.0.dist-info/entry_points.txt +2 -0
- navcode-0.1.0.dist-info/licenses/LICENSE +21 -0
- navcode-0.1.0.dist-info/top_level.txt +1 -0
navcode/__init__.py
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
from navcode._bootstrap import ensure_model
|
|
2
|
+
from loguru import logger
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
__version__ = "0.1.0"
|
|
6
|
+
|
|
7
|
+
ensure_model()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
def _setup_logging() -> None:
|
|
11
|
+
log_dir = Path.cwd() / ".codenav"
|
|
12
|
+
if log_dir.exists():
|
|
13
|
+
logger.add(
|
|
14
|
+
log_dir / "navcode.log",
|
|
15
|
+
rotation="5 MB",
|
|
16
|
+
retention="7 days",
|
|
17
|
+
level="INFO",
|
|
18
|
+
format="{time:YYYY-MM-DD HH:mm:ss} | {level} | {message}",
|
|
19
|
+
)
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
_setup_logging()
|
navcode/_bootstrap.py
ADDED
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
"""Bootstrap utilities — ensures the ONNX embedding model is present on disk."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
import httpx
|
|
6
|
+
from loguru import logger
|
|
7
|
+
from rich.console import Console
|
|
8
|
+
from rich.panel import Panel
|
|
9
|
+
from rich.progress import (
|
|
10
|
+
BarColumn,
|
|
11
|
+
DownloadColumn,
|
|
12
|
+
Progress,
|
|
13
|
+
TextColumn,
|
|
14
|
+
TimeRemainingColumn,
|
|
15
|
+
TransferSpeedColumn,
|
|
16
|
+
)
|
|
17
|
+
|
|
18
|
+
_HF_BASE: str = (
|
|
19
|
+
"https://huggingface.co/sentence-transformers/all-MiniLM-L6-v2/resolve/main"
|
|
20
|
+
)
|
|
21
|
+
MODEL_URL: str = f"{_HF_BASE}/onnx/model_quantized.onnx"
|
|
22
|
+
MODEL_PATH: Path = Path.home() / ".navcode" / "models" / "model_quantized.onnx"
|
|
23
|
+
|
|
24
|
+
# Tokenizer files required by EmbeddingsEngine
|
|
25
|
+
_TOKENIZER_FILES: list[str] = [
|
|
26
|
+
"tokenizer.json",
|
|
27
|
+
"tokenizer_config.json",
|
|
28
|
+
"vocab.txt",
|
|
29
|
+
"special_tokens_map.json",
|
|
30
|
+
]
|
|
31
|
+
|
|
32
|
+
_console = Console()
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def _download_file(url: str, dest: Path, label: str) -> None:
|
|
36
|
+
"""Stream *url* to *dest* with a Rich progress bar.
|
|
37
|
+
|
|
38
|
+
Raises:
|
|
39
|
+
httpx.HTTPError: on any HTTP-level failure.
|
|
40
|
+
"""
|
|
41
|
+
with httpx.stream("GET", url, follow_redirects=True, timeout=60) as response:
|
|
42
|
+
response.raise_for_status()
|
|
43
|
+
total = int(response.headers.get("content-length", 0)) or None
|
|
44
|
+
|
|
45
|
+
with Progress(
|
|
46
|
+
TextColumn(f"[bold blue]{label}[/bold blue]"),
|
|
47
|
+
BarColumn(),
|
|
48
|
+
DownloadColumn(),
|
|
49
|
+
TransferSpeedColumn(),
|
|
50
|
+
TimeRemainingColumn(),
|
|
51
|
+
console=_console,
|
|
52
|
+
transient=True,
|
|
53
|
+
) as progress:
|
|
54
|
+
task = progress.add_task("download", total=total)
|
|
55
|
+
with dest.open("wb") as fh:
|
|
56
|
+
for chunk in response.iter_bytes(chunk_size=8192):
|
|
57
|
+
fh.write(chunk)
|
|
58
|
+
progress.update(task, advance=len(chunk))
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
def ensure_model() -> None:
|
|
62
|
+
"""Ensure the quantised ONNX embedding model exists locally.
|
|
63
|
+
|
|
64
|
+
If the model file is already present at ``~/.navcode/models/model_quantized.onnx``
|
|
65
|
+
this function returns immediately. On first run it streams the file from
|
|
66
|
+
HuggingFace, displaying a Rich progress bar. Network failures are caught,
|
|
67
|
+
shown as a Rich warning panel, and silently swallowed so the rest of navcode
|
|
68
|
+
can still load (the model will be re-attempted on ``navcode init``).
|
|
69
|
+
"""
|
|
70
|
+
if MODEL_PATH.exists():
|
|
71
|
+
logger.debug("ONNX model already present at {}", MODEL_PATH)
|
|
72
|
+
else:
|
|
73
|
+
MODEL_PATH.parent.mkdir(parents=True, exist_ok=True)
|
|
74
|
+
logger.info("ONNX model not found — downloading from HuggingFace…")
|
|
75
|
+
|
|
76
|
+
try:
|
|
77
|
+
_download_file(MODEL_URL, MODEL_PATH, "Downloading model")
|
|
78
|
+
logger.info("Model saved to {}", MODEL_PATH)
|
|
79
|
+
except httpx.HTTPError as exc:
|
|
80
|
+
_console.print(
|
|
81
|
+
Panel(
|
|
82
|
+
f"[yellow]navcode[/yellow] could not download the embedding model.\n"
|
|
83
|
+
f"[dim]{exc}[/dim]\n\n"
|
|
84
|
+
"navcode will still load — the model will be downloaded on first "
|
|
85
|
+
"[cyan]navcode init[/cyan].",
|
|
86
|
+
title="[bold yellow]⚠ Model download skipped[/bold yellow]",
|
|
87
|
+
border_style="yellow",
|
|
88
|
+
)
|
|
89
|
+
)
|
|
90
|
+
logger.warning("Model download failed: {}", exc)
|
|
91
|
+
|
|
92
|
+
# Ensure tokenizer files are present alongside the model
|
|
93
|
+
_ensure_tokenizer_files()
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
def _ensure_tokenizer_files() -> None:
|
|
97
|
+
"""Download any missing tokenizer files from HuggingFace."""
|
|
98
|
+
models_dir = MODEL_PATH.parent
|
|
99
|
+
models_dir.mkdir(parents=True, exist_ok=True)
|
|
100
|
+
|
|
101
|
+
for filename in _TOKENIZER_FILES:
|
|
102
|
+
dest = models_dir / filename
|
|
103
|
+
if dest.exists():
|
|
104
|
+
logger.debug("Tokenizer file already present: {}", filename)
|
|
105
|
+
continue
|
|
106
|
+
|
|
107
|
+
url = f"{_HF_BASE}/{filename}"
|
|
108
|
+
logger.info("Tokenizer file not found — downloading: {}", filename)
|
|
109
|
+
try:
|
|
110
|
+
_download_file(url, dest, f"Downloading {filename}")
|
|
111
|
+
logger.info("Saved {}", dest)
|
|
112
|
+
except httpx.HTTPError as exc:
|
|
113
|
+
_console.print(
|
|
114
|
+
Panel(
|
|
115
|
+
f"[yellow]navcode[/yellow] could not download [cyan]{filename}[/cyan].\n"
|
|
116
|
+
f"[dim]{exc}[/dim]\n\n"
|
|
117
|
+
"Semantic search may be unavailable until this file is present.",
|
|
118
|
+
title="[bold yellow]⚠ Tokenizer file download skipped[/bold yellow]",
|
|
119
|
+
border_style="yellow",
|
|
120
|
+
)
|
|
121
|
+
)
|
|
122
|
+
logger.warning("Tokenizer file download failed ({}): {}", filename, exc)
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
"""navcode agent adapters — one per supported AI coding agent."""
|
|
2
|
+
|
|
3
|
+
from navcode.adapters.claude import ClaudeAdapter
|
|
4
|
+
from navcode.adapters.cursor import CursorAdapter
|
|
5
|
+
from navcode.adapters.codex import CodexAdapter
|
|
6
|
+
from navcode.adapters.copilot import CopilotAdapter
|
|
7
|
+
from navcode.adapters.bob import BobAdapter
|
|
8
|
+
from navcode.adapters.kimi import KimiAdapter
|
|
9
|
+
from navcode.adapters.antigravity import AntigravityAdapter
|
|
10
|
+
from navcode.adapters.windsurf import WindsurfAdapter
|
|
11
|
+
from navcode.adapters.continue_ import ContinueAdapter
|
|
12
|
+
|
|
13
|
+
ALL_ADAPTERS = {
|
|
14
|
+
"claude": ClaudeAdapter,
|
|
15
|
+
"cursor": CursorAdapter,
|
|
16
|
+
"codex": CodexAdapter,
|
|
17
|
+
"copilot": CopilotAdapter,
|
|
18
|
+
"bob": BobAdapter,
|
|
19
|
+
"kimi": KimiAdapter,
|
|
20
|
+
"antigravity": AntigravityAdapter,
|
|
21
|
+
"windsurf": WindsurfAdapter,
|
|
22
|
+
"continue": ContinueAdapter,
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
__all__ = [
|
|
26
|
+
"ClaudeAdapter",
|
|
27
|
+
"CursorAdapter",
|
|
28
|
+
"CodexAdapter",
|
|
29
|
+
"CopilotAdapter",
|
|
30
|
+
"BobAdapter",
|
|
31
|
+
"KimiAdapter",
|
|
32
|
+
"AntigravityAdapter",
|
|
33
|
+
"WindsurfAdapter",
|
|
34
|
+
"ContinueAdapter",
|
|
35
|
+
"ALL_ADAPTERS",
|
|
36
|
+
]
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Antigravity adapter."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from navcode.adapters.base import BaseAdapter
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class AntigravityAdapter(BaseAdapter):
|
|
9
|
+
|
|
10
|
+
NAME = "Antigravity"
|
|
11
|
+
BINARY = "antigravity"
|
|
12
|
+
CONFIG_DIR = "~/.antigravity"
|
|
13
|
+
PROJECT_DIR = ".antigravity"
|
|
14
|
+
PROCESS_NAME = "antigravity"
|
|
15
|
+
INSTRUCTION_FILE = ".antigravity/instructions.md"
|
|
16
|
+
|
|
17
|
+
def install(self) -> None:
|
|
18
|
+
self._write_instruction_file(
|
|
19
|
+
self.project_root / ".antigravity" / "instructions.md",
|
|
20
|
+
self._instruction_content(),
|
|
21
|
+
)
|
|
22
|
+
self._inject_mcp_config(
|
|
23
|
+
Path.home() / ".antigravity" / "mcp.json"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
def is_detected(self) -> bool:
|
|
27
|
+
return (
|
|
28
|
+
self._detect_binary()
|
|
29
|
+
or self._detect_config_dir()
|
|
30
|
+
or self._detect_project_dir()
|
|
31
|
+
)
|
navcode/adapters/base.py
ADDED
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
"""Abstract base class for all navcode agent adapters."""
|
|
2
|
+
|
|
3
|
+
from abc import ABC, abstractmethod
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class BaseAdapter(ABC):
|
|
8
|
+
|
|
9
|
+
# Override these in every adapter
|
|
10
|
+
NAME: str = ""
|
|
11
|
+
BINARY: str = ""
|
|
12
|
+
CONFIG_DIR: str = ""
|
|
13
|
+
PROJECT_DIR: str = ""
|
|
14
|
+
PROCESS_NAME: str | None = None
|
|
15
|
+
INSTRUCTION_FILE: str = ""
|
|
16
|
+
|
|
17
|
+
def __init__(self, project_root: Path):
|
|
18
|
+
self.project_root = project_root
|
|
19
|
+
self.codenav_dir = project_root / ".codenav"
|
|
20
|
+
|
|
21
|
+
@abstractmethod
|
|
22
|
+
def install(self) -> None:
|
|
23
|
+
"""Create all required files for this agent."""
|
|
24
|
+
pass
|
|
25
|
+
|
|
26
|
+
@abstractmethod
|
|
27
|
+
def is_detected(self) -> bool:
|
|
28
|
+
"""Return True if this agent is installed on the system."""
|
|
29
|
+
pass
|
|
30
|
+
|
|
31
|
+
def _write_instruction_file(self, path: Path, content: str) -> None:
|
|
32
|
+
"""Write instruction file, create parent dirs if needed."""
|
|
33
|
+
path.parent.mkdir(parents=True, exist_ok=True)
|
|
34
|
+
path.write_text(content, encoding="utf-8")
|
|
35
|
+
|
|
36
|
+
def _inject_mcp_config(self, config_path: Path) -> None:
|
|
37
|
+
"""Inject navcode MCP entry into agent JSON config."""
|
|
38
|
+
import json
|
|
39
|
+
|
|
40
|
+
config_path.parent.mkdir(parents=True, exist_ok=True)
|
|
41
|
+
|
|
42
|
+
existing = {}
|
|
43
|
+
if config_path.exists():
|
|
44
|
+
try:
|
|
45
|
+
existing = json.loads(config_path.read_text())
|
|
46
|
+
except json.JSONDecodeError:
|
|
47
|
+
pass
|
|
48
|
+
|
|
49
|
+
existing.setdefault("mcpServers", {})
|
|
50
|
+
existing["mcpServers"]["navcode"] = {
|
|
51
|
+
"command": "navcode",
|
|
52
|
+
"args": ["mcp-serve"],
|
|
53
|
+
"cwd": str(self.project_root),
|
|
54
|
+
}
|
|
55
|
+
config_path.write_text(
|
|
56
|
+
json.dumps(existing, indent=2),
|
|
57
|
+
encoding="utf-8",
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
def _detect_binary(self) -> bool:
|
|
61
|
+
import shutil
|
|
62
|
+
|
|
63
|
+
return shutil.which(self.BINARY) is not None
|
|
64
|
+
|
|
65
|
+
def _detect_config_dir(self) -> bool:
|
|
66
|
+
return Path.home().joinpath(
|
|
67
|
+
self.CONFIG_DIR.lstrip("~/")
|
|
68
|
+
).exists()
|
|
69
|
+
|
|
70
|
+
def _detect_project_dir(self) -> bool:
|
|
71
|
+
return (self.project_root / self.PROJECT_DIR).exists()
|
|
72
|
+
|
|
73
|
+
def _instruction_content(self) -> str:
|
|
74
|
+
return f"""# navcode — Active
|
|
75
|
+
|
|
76
|
+
## Rules for {self.NAME}
|
|
77
|
+
|
|
78
|
+
NEVER use read_file or list_directory to explore the codebase.
|
|
79
|
+
ALWAYS start every task with navcode_get_context(task=<your task>).
|
|
80
|
+
Work ONLY within files and symbols navcode returns.
|
|
81
|
+
If you need more: call navcode_expand_context(file_path, reason).
|
|
82
|
+
|
|
83
|
+
## Available MCP Tools
|
|
84
|
+
|
|
85
|
+
| Tool | When to use |
|
|
86
|
+
|---|---|
|
|
87
|
+
| navcode_get_context(task) | FIRST call — always |
|
|
88
|
+
| navcode_expand_context(file, reason) | Need more from one file |
|
|
89
|
+
| navcode_get_callers(function) | Understand blast radius |
|
|
90
|
+
| navcode_get_deps(file) | See what a file imports |
|
|
91
|
+
| navcode_search(query) | Find where something is defined |
|
|
92
|
+
| navcode_get_structure() | Architectural overview |
|
|
93
|
+
| navcode_stats() | Index health check |
|
|
94
|
+
|
|
95
|
+
## Why
|
|
96
|
+
navcode has pre-indexed this codebase.
|
|
97
|
+
Using it saves 80-90% tokens vs raw file reads.
|
|
98
|
+
|
|
99
|
+
Built by Nythris Studio
|
|
100
|
+
"""
|
navcode/adapters/bob.py
ADDED
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""IBM Bob adapter."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from navcode.adapters.base import BaseAdapter
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class BobAdapter(BaseAdapter):
|
|
9
|
+
|
|
10
|
+
NAME = "IBM Bob"
|
|
11
|
+
BINARY = "bob"
|
|
12
|
+
CONFIG_DIR = "~/.bob"
|
|
13
|
+
PROJECT_DIR = ".bob"
|
|
14
|
+
PROCESS_NAME = "bob-agent"
|
|
15
|
+
INSTRUCTION_FILE = ".bob/instructions.md"
|
|
16
|
+
|
|
17
|
+
def install(self) -> None:
|
|
18
|
+
# 1. Write .bob/instructions.md
|
|
19
|
+
self._write_instruction_file(
|
|
20
|
+
self.project_root / ".bob" / "instructions.md",
|
|
21
|
+
self._instruction_content(),
|
|
22
|
+
)
|
|
23
|
+
# 2. Inject MCP into ~/.bob/mcp.json
|
|
24
|
+
self._inject_mcp_config(
|
|
25
|
+
Path.home() / ".bob" / "mcp.json"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
def is_detected(self) -> bool:
|
|
29
|
+
return (
|
|
30
|
+
self._detect_binary()
|
|
31
|
+
or self._detect_config_dir()
|
|
32
|
+
or self._detect_project_dir()
|
|
33
|
+
)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Claude Code adapter."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from navcode.adapters.base import BaseAdapter
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ClaudeAdapter(BaseAdapter):
|
|
9
|
+
|
|
10
|
+
NAME = "Claude Code"
|
|
11
|
+
BINARY = "claude"
|
|
12
|
+
CONFIG_DIR = "~/.claude"
|
|
13
|
+
PROJECT_DIR = "CLAUDE.md"
|
|
14
|
+
PROCESS_NAME = None
|
|
15
|
+
INSTRUCTION_FILE = "CLAUDE.md"
|
|
16
|
+
|
|
17
|
+
def install(self) -> None:
|
|
18
|
+
# 1. Write CLAUDE.md to project root
|
|
19
|
+
self._write_instruction_file(
|
|
20
|
+
self.project_root / "CLAUDE.md",
|
|
21
|
+
self._instruction_content(),
|
|
22
|
+
)
|
|
23
|
+
# 2. Inject MCP into ~/.claude/mcp.json
|
|
24
|
+
self._inject_mcp_config(
|
|
25
|
+
Path.home() / ".claude" / "mcp.json"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
def is_detected(self) -> bool:
|
|
29
|
+
return (
|
|
30
|
+
self._detect_binary()
|
|
31
|
+
or self._detect_config_dir()
|
|
32
|
+
or self._detect_project_dir()
|
|
33
|
+
)
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Codex CLI adapter."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from navcode.adapters.base import BaseAdapter
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CodexAdapter(BaseAdapter):
|
|
9
|
+
|
|
10
|
+
NAME = "Codex CLI"
|
|
11
|
+
BINARY = "codex"
|
|
12
|
+
CONFIG_DIR = "~/.codex"
|
|
13
|
+
PROJECT_DIR = "AGENTS.md"
|
|
14
|
+
PROCESS_NAME = None
|
|
15
|
+
INSTRUCTION_FILE = "AGENTS.md"
|
|
16
|
+
|
|
17
|
+
def install(self) -> None:
|
|
18
|
+
# 1. Write AGENTS.md
|
|
19
|
+
self._write_instruction_file(
|
|
20
|
+
self.project_root / "AGENTS.md",
|
|
21
|
+
self._instruction_content(),
|
|
22
|
+
)
|
|
23
|
+
# 2. Inject MCP into ~/.codex/config.json
|
|
24
|
+
self._inject_mcp_config(
|
|
25
|
+
Path.home() / ".codex" / "config.json"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
def is_detected(self) -> bool:
|
|
29
|
+
return (
|
|
30
|
+
self._detect_binary()
|
|
31
|
+
or self._detect_config_dir()
|
|
32
|
+
or self._detect_project_dir()
|
|
33
|
+
)
|
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Continue adapter."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from navcode.adapters.base import BaseAdapter
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class ContinueAdapter(BaseAdapter):
|
|
9
|
+
|
|
10
|
+
NAME = "Continue"
|
|
11
|
+
BINARY = "continue"
|
|
12
|
+
CONFIG_DIR = "~/.continue"
|
|
13
|
+
PROJECT_DIR = ".continue"
|
|
14
|
+
PROCESS_NAME = None
|
|
15
|
+
INSTRUCTION_FILE = ".continue/navcode.md"
|
|
16
|
+
|
|
17
|
+
def install(self) -> None:
|
|
18
|
+
self._write_instruction_file(
|
|
19
|
+
self.project_root / ".continue" / "navcode.md",
|
|
20
|
+
self._instruction_content(),
|
|
21
|
+
)
|
|
22
|
+
self._inject_mcp_config(
|
|
23
|
+
Path.home() / ".continue" / "config.json"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
def is_detected(self) -> bool:
|
|
27
|
+
return (
|
|
28
|
+
self._detect_binary()
|
|
29
|
+
or self._detect_config_dir()
|
|
30
|
+
or self._detect_project_dir()
|
|
31
|
+
)
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
"""GitHub Copilot adapter."""
|
|
2
|
+
|
|
3
|
+
from navcode.adapters.base import BaseAdapter
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class CopilotAdapter(BaseAdapter):
|
|
7
|
+
|
|
8
|
+
NAME = "GitHub Copilot"
|
|
9
|
+
BINARY = "gh"
|
|
10
|
+
CONFIG_DIR = "~/.config/gh"
|
|
11
|
+
PROJECT_DIR = ".github"
|
|
12
|
+
PROCESS_NAME = None
|
|
13
|
+
INSTRUCTION_FILE = ".github/copilot-instructions.md"
|
|
14
|
+
|
|
15
|
+
def install(self) -> None:
|
|
16
|
+
# Write .github/copilot-instructions.md
|
|
17
|
+
self._write_instruction_file(
|
|
18
|
+
self.project_root / ".github" / "copilot-instructions.md",
|
|
19
|
+
self._instruction_content(),
|
|
20
|
+
)
|
|
21
|
+
# No MCP config for Copilot yet
|
|
22
|
+
|
|
23
|
+
def is_detected(self) -> bool:
|
|
24
|
+
return self._detect_binary() or self._detect_project_dir()
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Cursor adapter."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from navcode.adapters.base import BaseAdapter
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class CursorAdapter(BaseAdapter):
|
|
9
|
+
|
|
10
|
+
NAME = "Cursor"
|
|
11
|
+
BINARY = "cursor"
|
|
12
|
+
CONFIG_DIR = "~/.cursor"
|
|
13
|
+
PROJECT_DIR = ".cursor"
|
|
14
|
+
PROCESS_NAME = "cursor"
|
|
15
|
+
INSTRUCTION_FILE = ".cursor/rules/navcode.md"
|
|
16
|
+
|
|
17
|
+
def install(self) -> None:
|
|
18
|
+
# 1. Write .cursor/rules/navcode.md
|
|
19
|
+
self._write_instruction_file(
|
|
20
|
+
self.project_root / ".cursor" / "rules" / "navcode.md",
|
|
21
|
+
self._instruction_content(),
|
|
22
|
+
)
|
|
23
|
+
# 2. Inject MCP into .cursor/mcp.json
|
|
24
|
+
self._inject_mcp_config(
|
|
25
|
+
self.project_root / ".cursor" / "mcp.json"
|
|
26
|
+
)
|
|
27
|
+
|
|
28
|
+
def is_detected(self) -> bool:
|
|
29
|
+
return (
|
|
30
|
+
self._detect_binary()
|
|
31
|
+
or self._detect_config_dir()
|
|
32
|
+
or self._detect_project_dir()
|
|
33
|
+
)
|
navcode/adapters/kimi.py
ADDED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
"""Kimi Code adapter."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
|
|
5
|
+
from navcode.adapters.base import BaseAdapter
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class KimiAdapter(BaseAdapter):
|
|
9
|
+
|
|
10
|
+
NAME = "Kimi Code"
|
|
11
|
+
BINARY = "kimi"
|
|
12
|
+
CONFIG_DIR = "~/.kimi"
|
|
13
|
+
PROJECT_DIR = "KIMI.md"
|
|
14
|
+
PROCESS_NAME = None
|
|
15
|
+
INSTRUCTION_FILE = "KIMI.md"
|
|
16
|
+
|
|
17
|
+
def install(self) -> None:
|
|
18
|
+
self._write_instruction_file(
|
|
19
|
+
self.project_root / "KIMI.md",
|
|
20
|
+
self._instruction_content(),
|
|
21
|
+
)
|
|
22
|
+
self._inject_mcp_config(
|
|
23
|
+
Path.home() / ".kimi" / "mcp.json"
|
|
24
|
+
)
|
|
25
|
+
|
|
26
|
+
def is_detected(self) -> bool:
|
|
27
|
+
return (
|
|
28
|
+
self._detect_binary()
|
|
29
|
+
or self._detect_config_dir()
|
|
30
|
+
or self._detect_project_dir()
|
|
31
|
+
)
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
"""Windsurf adapter."""
|
|
2
|
+
|
|
3
|
+
from navcode.adapters.base import BaseAdapter
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
class WindsurfAdapter(BaseAdapter):
|
|
7
|
+
|
|
8
|
+
NAME = "Windsurf"
|
|
9
|
+
BINARY = "windsurf"
|
|
10
|
+
CONFIG_DIR = "~/.codeium/windsurf"
|
|
11
|
+
PROJECT_DIR = ".windsurf"
|
|
12
|
+
PROCESS_NAME = "windsurf"
|
|
13
|
+
INSTRUCTION_FILE = ".windsurf/rules/navcode.md"
|
|
14
|
+
|
|
15
|
+
def install(self) -> None:
|
|
16
|
+
self._write_instruction_file(
|
|
17
|
+
self.project_root / ".windsurf" / "rules" / "navcode.md",
|
|
18
|
+
self._instruction_content(),
|
|
19
|
+
)
|
|
20
|
+
self._inject_mcp_config(
|
|
21
|
+
self.project_root / ".windsurf" / "mcp.json"
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
def is_detected(self) -> bool:
|
|
25
|
+
return (
|
|
26
|
+
self._detect_binary()
|
|
27
|
+
or self._detect_config_dir()
|
|
28
|
+
or self._detect_project_dir()
|
|
29
|
+
)
|