codecortex 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.
Potentially problematic release.
This version of codecortex might be problematic. Click here for more details.
- codecortex-0.2.0.dist-info/METADATA +206 -0
- codecortex-0.2.0.dist-info/RECORD +31 -0
- codecortex-0.2.0.dist-info/WHEEL +5 -0
- codecortex-0.2.0.dist-info/entry_points.txt +2 -0
- codecortex-0.2.0.dist-info/licenses/LICENSE +21 -0
- codecortex-0.2.0.dist-info/top_level.txt +1 -0
- codeintel/__init__.py +1 -0
- codeintel/__main__.py +361 -0
- codeintel/cache.py +66 -0
- codeintel/config.py +42 -0
- codeintel/doctor.py +161 -0
- codeintel/gateway.py +228 -0
- codeintel/http_server.py +93 -0
- codeintel/indexer.py +250 -0
- codeintel/injector.py +81 -0
- codeintel/installer.py +103 -0
- codeintel/mapper.py +192 -0
- codeintel/onboarding.py +197 -0
- codeintel/policy.py +30 -0
- codeintel/provider.py +52 -0
- codeintel/providers/__init__.py +0 -0
- codeintel/providers/graph.py +415 -0
- codeintel/providers/lsp.py +407 -0
- codeintel/providers/none.py +30 -0
- codeintel/providers/semantic.py +139 -0
- codeintel/reindexer.py +112 -0
- codeintel/reset.py +100 -0
- codeintel/searcher.py +143 -0
- codeintel/semantic_db.py +78 -0
- codeintel/server.py +216 -0
- codeintel/term.py +162 -0
codeintel/term.py
ADDED
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
"""Terminal output system — one small, dependency-free styling layer shared by every
|
|
2
|
+
human-facing command (doctor, status, query, setup) so they read as one tool.
|
|
3
|
+
|
|
4
|
+
Two independent axes, each auto-detected and overridable:
|
|
5
|
+
* color — raw ANSI SGR, ON only for a TTY, disabled by NO_COLOR / --no-color / TERM=dumb.
|
|
6
|
+
* glyphs — unicode by default, ASCII fallback when the stream can't encode them / --ascii.
|
|
7
|
+
|
|
8
|
+
Alignment note: status glyphs are drawn only from Unicode blocks that render as exactly one
|
|
9
|
+
column in monospace fonts. `⚠` (U+26A0, Misc Symbols) is deliberately AVOIDED — many coding
|
|
10
|
+
fonts draw it ~2 cells wide even though `unicodedata` calls it narrow, which is what made the
|
|
11
|
+
doctor table drift. `▲` (U+25B2, Geometric Shapes) is width-stable, so naive centering is correct.
|
|
12
|
+
"""
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import os
|
|
16
|
+
import sys
|
|
17
|
+
from typing import Optional
|
|
18
|
+
|
|
19
|
+
_CODES = {"bold": "1", "dim": "2", "red": "31", "green": "32", "yellow": "33", "cyan": "36"}
|
|
20
|
+
|
|
21
|
+
# state -> (unicode glyph, ascii glyph, color role). Glyphs are single display-column safe.
|
|
22
|
+
GLYPHS = {
|
|
23
|
+
"ok": {"unicode": "✓", "ascii": "[ OK ]", "color": "green"}, # ✓
|
|
24
|
+
"fail": {"unicode": "✗", "ascii": "[FAIL]", "color": "red"}, # ✗
|
|
25
|
+
"warn": {"unicode": "▲", "ascii": "[WARN]", "color": "yellow"}, # ▲ (NOT ⚠)
|
|
26
|
+
"na": {"unicode": "n/a", "ascii": "[ N/A]", "color": "dim"},
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
# Blocks that render as exactly one terminal column in virtually every monospace font.
|
|
30
|
+
# EXCLUDES Miscellaneous Symbols (0x2600-0x26FF) — the width-ambiguous emoji-adjacent range.
|
|
31
|
+
_SAFE_GLYPH_BLOCKS = (
|
|
32
|
+
(0x0000, 0x007F), # Basic Latin
|
|
33
|
+
(0x2500, 0x257F), # Box Drawing ─ │ └
|
|
34
|
+
(0x25A0, 0x25FF), # Geometric Shapes ▲ ● ○
|
|
35
|
+
(0x2700, 0x27BF), # Dingbats ✓ ✗
|
|
36
|
+
)
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def is_display_width_safe(ch: str) -> bool:
|
|
40
|
+
"""True if every char in ``ch`` is from a block that draws as one monospace column.
|
|
41
|
+
Column-aligned glyphs (table/list cells) must satisfy this; prose banners need not."""
|
|
42
|
+
return all(any(lo <= ord(c) <= hi for lo, hi in _SAFE_GLYPH_BLOCKS) for c in ch)
|
|
43
|
+
|
|
44
|
+
|
|
45
|
+
class Console:
|
|
46
|
+
def __init__(
|
|
47
|
+
self,
|
|
48
|
+
*,
|
|
49
|
+
stream=None,
|
|
50
|
+
no_color: bool = False,
|
|
51
|
+
ascii_mode: Optional[bool] = None,
|
|
52
|
+
) -> None:
|
|
53
|
+
self.stream = stream if stream is not None else sys.stdout
|
|
54
|
+
self.enabled = self._detect_color(no_color, self.stream)
|
|
55
|
+
self.ascii = self._detect_ascii(ascii_mode, self.stream)
|
|
56
|
+
|
|
57
|
+
@staticmethod
|
|
58
|
+
def _detect_color(no_color_flag: bool, stream) -> bool:
|
|
59
|
+
if no_color_flag or "NO_COLOR" in os.environ: # presence, not truthiness (no-color.org)
|
|
60
|
+
return False
|
|
61
|
+
if os.environ.get("TERM") == "dumb":
|
|
62
|
+
return False
|
|
63
|
+
if os.environ.get("FORCE_COLOR"):
|
|
64
|
+
return True
|
|
65
|
+
try:
|
|
66
|
+
return bool(stream.isatty())
|
|
67
|
+
except Exception:
|
|
68
|
+
return False
|
|
69
|
+
|
|
70
|
+
@staticmethod
|
|
71
|
+
def _detect_ascii(flag: Optional[bool], stream) -> bool:
|
|
72
|
+
if flag is not None:
|
|
73
|
+
return flag
|
|
74
|
+
enc = getattr(stream, "encoding", None) or ""
|
|
75
|
+
try:
|
|
76
|
+
"✓✗▲".encode(enc or "utf-8")
|
|
77
|
+
return False
|
|
78
|
+
except (LookupError, UnicodeEncodeError):
|
|
79
|
+
return True
|
|
80
|
+
|
|
81
|
+
def _wrap(self, code: str, s: str) -> str:
|
|
82
|
+
return f"\x1b[{code}m{s}\x1b[0m" if self.enabled else s
|
|
83
|
+
|
|
84
|
+
def bold(self, s: str) -> str: return self._wrap(_CODES["bold"], s)
|
|
85
|
+
def dim(self, s: str) -> str: return self._wrap(_CODES["dim"], s)
|
|
86
|
+
def red(self, s: str) -> str: return self._wrap(_CODES["red"], s)
|
|
87
|
+
def green(self, s: str) -> str: return self._wrap(_CODES["green"], s)
|
|
88
|
+
def yellow(self, s: str) -> str: return self._wrap(_CODES["yellow"], s)
|
|
89
|
+
def cyan(self, s: str) -> str: return self._wrap(_CODES["cyan"], s)
|
|
90
|
+
|
|
91
|
+
def glyph(self, state: str) -> str:
|
|
92
|
+
"""A colored, width-safe status token for ``state`` in {ok, fail, warn, na}."""
|
|
93
|
+
g = GLYPHS.get(state, GLYPHS["na"])
|
|
94
|
+
text = g["ascii"] if self.ascii else g["unicode"]
|
|
95
|
+
color = g["color"]
|
|
96
|
+
return self.dim(text) if color == "dim" else getattr(self, color)(text)
|
|
97
|
+
|
|
98
|
+
def raw_glyph(self, state: str) -> str:
|
|
99
|
+
"""The uncolored glyph text (for width math), still respecting ascii mode."""
|
|
100
|
+
g = GLYPHS.get(state, GLYPHS["na"])
|
|
101
|
+
return g["ascii"] if self.ascii else g["unicode"]
|
|
102
|
+
|
|
103
|
+
def status_cell(self, state: str, width: int) -> str:
|
|
104
|
+
"""A glyph centered in ``width`` columns THEN colored — so the (zero-width on screen but
|
|
105
|
+
len()-counted) ANSI codes never corrupt the centering. This is the table-alignment fix."""
|
|
106
|
+
g = GLYPHS.get(state, GLYPHS["na"])
|
|
107
|
+
padded = self.raw_glyph(state).center(width)
|
|
108
|
+
color = g["color"]
|
|
109
|
+
return self.dim(padded) if color == "dim" else getattr(self, color)(padded)
|
|
110
|
+
|
|
111
|
+
def rule(self, n: int) -> str:
|
|
112
|
+
return self.dim(("-" if self.ascii else "─") * n)
|
|
113
|
+
|
|
114
|
+
def header(self, subcommand: str, context: str = "") -> str:
|
|
115
|
+
line = self.bold(f"codeintel {subcommand}")
|
|
116
|
+
if context:
|
|
117
|
+
line += f" {self.dim('—')} {self.dim(context)}"
|
|
118
|
+
return line
|
|
119
|
+
|
|
120
|
+
|
|
121
|
+
# Auto-detected singletons; the CLI calls configure() once after parsing --no-color/--ascii.
|
|
122
|
+
c = Console(stream=sys.stdout)
|
|
123
|
+
c_err = Console(stream=sys.stderr)
|
|
124
|
+
|
|
125
|
+
|
|
126
|
+
def configure(*, no_color: bool = False, ascii_mode: Optional[bool] = None) -> None:
|
|
127
|
+
"""Re-detect both consoles honoring CLI flags (called once from __main__)."""
|
|
128
|
+
global c, c_err
|
|
129
|
+
c = Console(stream=sys.stdout, no_color=no_color, ascii_mode=ascii_mode)
|
|
130
|
+
c_err = Console(stream=sys.stderr, no_color=no_color, ascii_mode=ascii_mode)
|
|
131
|
+
|
|
132
|
+
|
|
133
|
+
class LiveStep:
|
|
134
|
+
"""One status line for a slow op. On a TTY it redraws in place; on a pipe/CI/log it prints
|
|
135
|
+
exactly one clean line when done — never leaks carriage-return/cursor bytes to a non-TTY."""
|
|
136
|
+
|
|
137
|
+
def __init__(self, console: Console, label: str) -> None:
|
|
138
|
+
self.c = console
|
|
139
|
+
self.label = label
|
|
140
|
+
try:
|
|
141
|
+
self.live = console.enabled and console.stream.isatty()
|
|
142
|
+
except Exception:
|
|
143
|
+
self.live = False
|
|
144
|
+
if self.live:
|
|
145
|
+
try:
|
|
146
|
+
console.stream.write(f" {console.dim('…')} {label}\r")
|
|
147
|
+
console.stream.flush()
|
|
148
|
+
except Exception:
|
|
149
|
+
self.live = False
|
|
150
|
+
|
|
151
|
+
def done(self, state: str, detail: str = "") -> None:
|
|
152
|
+
text = f" {self.c.glyph(state)} {self.label}"
|
|
153
|
+
if detail:
|
|
154
|
+
text += f" {self.c.dim(detail)}"
|
|
155
|
+
try:
|
|
156
|
+
if self.live:
|
|
157
|
+
self.c.stream.write("\x1b[2K\r" + text + "\n") # erase line, redraw
|
|
158
|
+
else:
|
|
159
|
+
self.c.stream.write(text + "\n")
|
|
160
|
+
self.c.stream.flush()
|
|
161
|
+
except Exception:
|
|
162
|
+
pass
|