xcoding 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.
xcode/ui.py ADDED
@@ -0,0 +1,349 @@
1
+ """Look & feel: themes, the ghost+trees logo, the welcome box, and the
2
+ input header. Kept separate from cli.py so the chrome is easy to tweak.
3
+ """
4
+
5
+ from __future__ import annotations
6
+
7
+ import json
8
+ import random
9
+ import time
10
+ from pathlib import Path
11
+ from typing import Callable
12
+
13
+ from rich.box import ROUNDED
14
+ from rich.console import Group
15
+ from rich.panel import Panel
16
+ from rich.table import Table
17
+ from rich.text import Text
18
+
19
+ PREFS = Path(".xcode") / "ui.json"
20
+ # The source logo we render into the terminal (lives at the project root).
21
+ LOGO = Path(__file__).resolve().parent.parent / "logo.png"
22
+
23
+ # The little glyph that spins while we think, and the words it cycles through.
24
+ SPIN_GLYPHS = "✻✷✶✳✺✸"
25
+ THINKING_WORDS = [
26
+ "Envisioning", "Thinking", "Pondering", "Cooking", "Conjuring",
27
+ "Crunching", "Noodling", "Percolating", "Scheming", "Manifesting",
28
+ "Summoning", "Brewing",
29
+ ]
30
+ TIPS = [
31
+ "Use /btw to ask a quick side question without interrupting",
32
+ "Press shift+tab to cycle normal → auto → plan mode",
33
+ "Run /theme matrix to go full hacker mode",
34
+ "Type /clear to wipe the conversation and start fresh",
35
+ "Hit ↑/↓ or w/s to move through any menu, enter to pick",
36
+ "Try 'fix typecheck errors'",
37
+ "Ask me to 'make it more compact'",
38
+ "Use @file to attach files to your message",
39
+ "Press ctrl+z to undo your last edit in the input",
40
+ "Type / to see every slash command with descriptions",
41
+ "Use /model to switch the active model on the fly",
42
+ "Run /init to explore the project and write an XCODE.md",
43
+ "Use /compact to summarize and free up context",
44
+ "Try 'explain this stack trace'",
45
+ "Try 'write a commit message for my changes'",
46
+ "Try 'find where we handle login'",
47
+ "Ask 'how does <filepath> work?'",
48
+ "Use /resume to pick up a previous session",
49
+ "Use /memory to see what the project memory holds",
50
+ "Use /perms reset to clear saved permissions",
51
+ "Try 'add tests for the auth module'",
52
+ "Press ctrl+c to cancel the current input",
53
+ ]
54
+
55
+ # Each theme picks colors for the border, the ghost, the trees, accents, etc.
56
+ THEMES: dict[str, dict] = {
57
+ "ghost": { # default — clean white ghost on black
58
+ "border": "grey93", "ghost": "grey93", "eyes": "grey42",
59
+ "tree": "grey58", "accent": "white", "title": "bold white",
60
+ "user": "bold white", "tool": "grey50", "mode": "white",
61
+ },
62
+ "matrix": {
63
+ "border": "green", "ghost": "green1", "eyes": "bright_green",
64
+ "tree": "green3", "accent": "green1", "title": "bold green",
65
+ "user": "bold green", "tool": "green4", "mode": "green",
66
+ },
67
+ "dracula": {
68
+ "border": "purple", "ghost": "grey93", "eyes": "bright_magenta",
69
+ "tree": "spring_green3", "accent": "orchid", "title": "bold purple",
70
+ "user": "bold orchid", "tool": "grey50", "mode": "orchid",
71
+ },
72
+ "ember": {
73
+ "border": "dark_orange3", "ghost": "wheat1", "eyes": "orange1",
74
+ "tree": "dark_olive_green3", "accent": "orange1", "title": "bold orange1",
75
+ "user": "bold orange1", "tool": "grey50", "mode": "dark_orange",
76
+ },
77
+ "mono": {
78
+ "border": "grey50", "ghost": "grey93", "eyes": "grey70",
79
+ "tree": "grey58", "accent": "grey85", "title": "bold white",
80
+ "user": "bold white", "tool": "grey42", "mode": "grey70",
81
+ },
82
+ }
83
+
84
+ DEFAULT_THEME = "ghost"
85
+
86
+ def _ghost_text(theme: dict) -> Text:
87
+ """Tiny compact ghost facing right."""
88
+ g = theme["ghost"]
89
+ e = theme["eyes"]
90
+
91
+ # Ghost facing RIGHT
92
+ logo_lines = [
93
+ " ▓▓▓▓▓",
94
+ "▓▓▓▓▓▓▓",
95
+ " ▓▒▓▓▒▓",
96
+ "▓▓▓▓▓▓▓",
97
+ " ▓ ▓",
98
+ ]
99
+
100
+ t = Text()
101
+ for line in logo_lines:
102
+ # Color the eyes (▒) differently
103
+ if "▒" in line:
104
+ parts = line.split("▒")
105
+ for i, part in enumerate(parts):
106
+ if i > 0:
107
+ t.append("▒", style=e)
108
+ t.append(part, style=g)
109
+ else:
110
+ t.append(line, style=g)
111
+ t.append("\n")
112
+ t.rstrip()
113
+ return t
114
+
115
+
116
+ def _neighbors(x, y, w, h):
117
+ if x > 0: yield x - 1, y
118
+ if x < w - 1: yield x + 1, y
119
+ if y > 0: yield x, y - 1
120
+ if y < h - 1: yield x, y + 1
121
+
122
+
123
+ def _largest_blob(mask, w, h):
124
+ """(bounding box, mask) of the biggest connected True region — used to crop
125
+ to the ghost and to drop the little floating sparkles around it."""
126
+ from collections import deque
127
+ seen = [[False] * w for _ in range(h)]
128
+ best, best_box, best_cells = 0, (0, 0, w - 1, h - 1), None
129
+ for sy in range(h):
130
+ for sx in range(w):
131
+ if not mask[sy][sx] or seen[sy][sx]:
132
+ continue
133
+ dq = deque([(sx, sy)]); seen[sy][sx] = True
134
+ cells, l, t, r, b = [], sx, sy, sx, sy
135
+ while dq:
136
+ x, y = dq.popleft(); cells.append((x, y))
137
+ l, t, r, b = min(l, x), min(t, y), max(r, x), max(b, y)
138
+ for nx, ny in _neighbors(x, y, w, h):
139
+ if mask[ny][nx] and not seen[ny][nx]:
140
+ seen[ny][nx] = True; dq.append((nx, ny))
141
+ if len(cells) > best:
142
+ best, best_box, best_cells = len(cells), (l, t, r, b), cells
143
+ keep = [[False] * w for _ in range(h)]
144
+ for x, y in (best_cells or []):
145
+ keep[y][x] = True
146
+ return best_box, keep
147
+
148
+
149
+ def _logo_segments(width: int, rows: int):
150
+ """Read logo.png and label every pixel as background / body / feature.
151
+
152
+ The logo is RGBA with a *transparent* field, so the alpha channel tells us
153
+ the ghost from the background directly. We: (1) sample at high res, (2) take
154
+ the largest opaque blob as the ghost (dropping the floating sparkles),
155
+ (3) crop + transparent-pad to it, (4) at display size, split the ghost into
156
+ its white *body* and its enclosed black *features* (the eyes + </> mouth).
157
+ The black outline rim is folded into the body so the silhouette stays solid.
158
+ Result codes: 0=bg, 1=body, 2=feature.
159
+ """
160
+ from collections import deque
161
+
162
+ from PIL import Image, ImageOps
163
+
164
+ base = 220
165
+ im = Image.open(LOGO).convert("RGBA").resize((base, base))
166
+ apx = im.load()
167
+ opaque = [[apx[x, y][3] >= 128 for x in range(base)] for y in range(base)]
168
+ (l, t, r, b), _ = _largest_blob(opaque, base, base)
169
+ im = im.crop((l, t, r + 1, b + 1))
170
+ im = ImageOps.expand(im, border=max(2, (r - l) // 20), fill=(0, 0, 0, 0))
171
+ im = im.resize((width, rows))
172
+
173
+ px = im.load()
174
+ opq = [[px[x, y][3] >= 128 for x in range(width)] for y in range(rows)]
175
+ _, ghost = _largest_blob(opq, width, rows) # drop resized sparkle bits
176
+
177
+ def lum(x, y):
178
+ p = px[x, y]
179
+ return (p[0] * 299 + p[1] * 587 + p[2] * 114) // 1000
180
+
181
+ dark = [[ghost[y][x] and lum(x, y) < 128 for x in range(width)] for y in range(rows)]
182
+
183
+ # outline = dark rim touching the background; flood it so only the enclosed
184
+ # dark (eyes + mouth) stays a 'feature'.
185
+ outline = [[False] * width for _ in range(rows)]
186
+ dq = deque()
187
+ for y in range(rows):
188
+ for x in range(width):
189
+ if dark[y][x] and any(not ghost[ny][nx]
190
+ for nx, ny in _neighbors(x, y, width, rows)):
191
+ outline[y][x] = True; dq.append((x, y))
192
+ while dq:
193
+ x, y = dq.popleft()
194
+ for nx, ny in _neighbors(x, y, width, rows):
195
+ if dark[ny][nx] and not outline[ny][nx]:
196
+ outline[ny][nx] = True; dq.append((nx, ny))
197
+
198
+ out = [[0] * width for _ in range(rows)]
199
+ for y in range(rows):
200
+ for x in range(width):
201
+ if not ghost[y][x]:
202
+ out[y][x] = 0 # background → transparent
203
+ elif dark[y][x] and not outline[y][x]:
204
+ out[y][x] = 2 # enclosed feature → black
205
+ else:
206
+ out[y][x] = 1 # body (fill + outline rim)
207
+ return out
208
+
209
+
210
+ def logo(theme: dict, width: int = 26) -> Text:
211
+ """Render logo.png into the terminal with truecolor half-blocks.
212
+
213
+ Each character is the 'upper half block' ▀ whose *foreground* paints the
214
+ top pixel and *background* paints the bottom pixel — so one cell carries
215
+ two stacked pixels. The body fills with the theme's ghost colour; the eyes
216
+ and </> mouth fill black; the field stays transparent.
217
+ """
218
+ from rich.style import Style
219
+
220
+ rows = width + (width & 1) # even → 2 pixels per cell
221
+ try:
222
+ seg = _logo_segments(width, rows)
223
+ except Exception:
224
+ return _ghost_text(theme) # no Pillow / no file → ASCII
225
+
226
+ fill = {0: None, 1: theme["ghost"], 2: "grey3"}
227
+ t = Text()
228
+ for cr in range(rows // 2):
229
+ for c in range(width):
230
+ top = fill[seg[2 * cr][c]]
231
+ bot = fill[seg[2 * cr + 1][c]]
232
+ if top and bot:
233
+ t.append("▀", style=Style(color=top, bgcolor=bot))
234
+ elif top:
235
+ t.append("▀", style=Style(color=top))
236
+ elif bot:
237
+ t.append("▄", style=Style(color=bot))
238
+ else:
239
+ t.append(" ")
240
+ t.append("\n")
241
+ t.rstrip()
242
+ return t
243
+
244
+
245
+ def welcome(theme: dict, model: str, cwd: str, notes: str = "") -> Panel:
246
+ """Landscape welcome card: ghost on the left, info on the right."""
247
+ info = Group(
248
+ Text("xcode", style=theme["title"]),
249
+ Text("local-model coding agent", style=theme["tool"]),
250
+ Text(""),
251
+ Text(model, style=theme["accent"]),
252
+ Text(cwd + (f" ·{notes}" if notes else ""), style=theme["tool"]),
253
+ )
254
+ grid = Table.grid(padding=(0, 2))
255
+ grid.add_column(justify="left")
256
+ grid.add_column(justify="left")
257
+ grid.add_row(_ghost_text(theme), info)
258
+
259
+ return Panel(grid, box=ROUNDED, border_style=theme["border"],
260
+ padding=(0, 2), expand=False)
261
+
262
+
263
+ def status_line(theme: dict, mode: str, tokens: int, budget: int,
264
+ model: str) -> Text:
265
+ t = Text(" ")
266
+ if mode == "auto":
267
+ t.append("⏵⏵ auto mode on ", style=f"bold {theme['mode']}")
268
+ t.append("(shift+tab to cycle)", style="dim")
269
+ elif mode == "plan":
270
+ t.append("◷ plan mode ", style=f"bold {theme['mode']}")
271
+ t.append("(read-only · shift+tab to cycle)", style="dim")
272
+ else:
273
+ t.append("·· normal mode ", style=theme["mode"])
274
+ t.append("(shift+tab to cycle)", style="dim")
275
+ pct = min(100, int(100 * tokens / budget)) if budget else 0
276
+ tok_color = "green" if pct < 60 else "yellow" if pct < 85 else "red"
277
+ t.append(" · ")
278
+ t.append(f"~{tokens/1000:.1f}k/{budget//1000}k", style=tok_color)
279
+ t.append(" · ")
280
+ t.append(f"● {model}", style=theme["accent"])
281
+ return t
282
+
283
+
284
+ # ------------------------------------------------------------- thinking
285
+
286
+ class ThinkingStatus:
287
+ """A live, self-animating status line shown while the model works.
288
+
289
+ Renders like Claude Code's:
290
+
291
+ ✻ Envisioning… (48s · ↓ 1.7k tokens · 2 shells running) ⎿ Tip: Use /btw to ask a quick side question
292
+
293
+ Drive it with a rich.live.Live; the glyph spins, the timer ticks and the
294
+ token counter climbs on every refresh — no extra work needed from caller.
295
+ Mutate `.tokens` and `.preview` from the stream loop to update them.
296
+ """
297
+
298
+ def __init__(self, theme: dict, get_shells: Callable[[], int] | None = None):
299
+ self.theme = theme
300
+ self.get_shells = get_shells or (lambda: 0)
301
+ self.start = time.monotonic()
302
+ self.word = random.choice(THINKING_WORDS)
303
+ self.tip = random.choice(TIPS)
304
+ self.tokens = 0
305
+ self.preview = ""
306
+
307
+ def __rich__(self) -> Group:
308
+ elapsed = time.monotonic() - self.start
309
+ glyph = SPIN_GLYPHS[int(elapsed * 6) % len(SPIN_GLYPHS)]
310
+ shells = self.get_shells()
311
+
312
+ head = Text()
313
+ head.append(f"{glyph} ", style=self.theme["accent"])
314
+ head.append(f"{self.word}… ", style=self.theme["title"])
315
+ meta = f"({int(elapsed)}s · ↓ {self.tokens / 1000:.1f}k tokens"
316
+ if shells:
317
+ meta += f" · {shells} shell{'s' if shells != 1 else ''} running"
318
+ meta += ")"
319
+ head.append(meta, style="dim")
320
+
321
+ lines = [head]
322
+ if self.preview:
323
+ snippet = self.preview.replace("\n", " ").strip()[-80:]
324
+ lines.append(Text(f" {snippet}", style="dim"))
325
+ lines.append(Text(f" ⎿ Tip: {self.tip}", style="dim"))
326
+ return Group(*lines)
327
+
328
+
329
+ # ----------------------------------------------------------------- prefs
330
+
331
+ def load_prefs() -> dict:
332
+ if PREFS.exists():
333
+ try:
334
+ return json.loads(PREFS.read_text(encoding="utf-8"))
335
+ except Exception:
336
+ pass
337
+ return {"theme": DEFAULT_THEME, "mode": "normal"}
338
+
339
+
340
+ def save_prefs(prefs: dict) -> None:
341
+ try:
342
+ PREFS.parent.mkdir(parents=True, exist_ok=True)
343
+ PREFS.write_text(json.dumps(prefs, indent=2), encoding="utf-8")
344
+ except Exception:
345
+ pass
346
+
347
+
348
+ def get_theme(name: str) -> dict:
349
+ return THEMES.get(name, THEMES[DEFAULT_THEME])
@@ -0,0 +1,190 @@
1
+ Metadata-Version: 2.4
2
+ Name: xcoding
3
+ Version: 0.1.0
4
+ Summary: A local-model coding agent — Claude Code, but powered by Ollama or llama.cpp.
5
+ Author: c7s89r nzv
6
+ Maintainer: c7s89r nzv
7
+ License: MIT
8
+ Project-URL: Homepage, https://github.com/c7s89r/xcode
9
+ Project-URL: Repository, https://github.com/c7s89r/xcode
10
+ Project-URL: Issues, https://github.com/c7s89r/xcode/issues
11
+ Keywords: cli,coding-agent,ollama,llama.cpp,ai,local-llm
12
+ Classifier: Development Status :: 4 - Beta
13
+ Classifier: Environment :: Console
14
+ Classifier: Intended Audience :: Developers
15
+ Classifier: License :: OSI Approved :: MIT License
16
+ Classifier: Programming Language :: Python :: 3
17
+ Classifier: Topic :: Software Development
18
+ Requires-Python: >=3.9
19
+ Description-Content-Type: text/markdown
20
+ License-File: LICENSE
21
+ Requires-Dist: openai>=1.30.0
22
+ Requires-Dist: httpx>=0.27.0
23
+ Requires-Dist: rich>=13.7.0
24
+ Requires-Dist: prompt_toolkit>=3.0.0
25
+ Requires-Dist: discord.py>=2.3.0
26
+ Dynamic: license-file
27
+
28
+ # xcode
29
+
30
+ A local-model coding agent — like Claude Code, but it talks to a model
31
+ running on your own machine instead of a cloud API.
32
+
33
+ > **✅ Works with [Ollama](https://ollama.com) for now.** Just install Ollama,
34
+ > pull a tool-capable model, then `pip install xcode` and run `xcode`.
35
+ > (llama.cpp support is in too, but Ollama is the tested path.)
36
+
37
+ It auto-detects whichever backend is running, gives the model tools to read/write
38
+ files and run shell commands, and loops until your task is done. Every file write
39
+ and every shell command asks for your approval first.
40
+
41
+ ### Quick start (Ollama)
42
+
43
+ ```bash
44
+ ollama serve
45
+ ollama pull qwen2.5-coder # a model that's good at tool use
46
+ pip install xcode
47
+ xcode
48
+ ```
49
+
50
+ ## Install
51
+
52
+ ```bash
53
+ pip install xcode
54
+ ```
55
+
56
+ Then just run `xcode` from any project folder.
57
+
58
+ Or from source:
59
+
60
+ ```bash
61
+ pip install -e .
62
+ ```
63
+
64
+ (Python 3.9+. Pulls in `openai`, `httpx`, `rich`.)
65
+
66
+ ## Run a backend
67
+
68
+ **Ollama** (easiest — supports tool-calling natively):
69
+
70
+ ```bash
71
+ ollama serve
72
+ ollama pull qwen2.5-coder # a model that's good at tool use
73
+ ```
74
+
75
+ **llama.cpp** (raw GGUF files):
76
+
77
+ ```bash
78
+ llama-server -m your-model.gguf # listens on :8080, OpenAI-compatible
79
+ ```
80
+
81
+ > Tool-calling quality depends heavily on the model. Use a model trained for it
82
+ > (e.g. `qwen2.5-coder`, `llama3.1`, `mistral-nemo`). Tiny models will struggle.
83
+
84
+ ## Use it
85
+
86
+ ```bash
87
+ xcode
88
+ # or: python -m xcode
89
+ ```
90
+
91
+ Then just talk to it:
92
+
93
+ ```
94
+ › add a /health endpoint to app.py that returns {"ok": true}
95
+ ```
96
+
97
+ In-REPL commands: `/help`, `/models`, `/model`, `/init`, `/todos`, `/perms`,
98
+ `/compact`, `/sessions`, `/resume`, `/reset`, `/exit`.
99
+
100
+ - Replies **stream** live; the prompt shows a **context meter** (`~3.2k/8k`).
101
+ - Writes/commands ask `y / n / a`; **a** ("always") is saved to
102
+ `.xcode/permissions.json`. Edits show a **colored diff** preview.
103
+ - Attach files inline with `@path` (e.g. `explain @xcode/agent.py`).
104
+ - The agent tracks a **todo list** for multi-step work (`/todos` to view).
105
+ - Old turns are **auto-compacted** when the context meter fills; `/compact`
106
+ forces it. Conversations are **saved** per project — `xcode --resume` or
107
+ `/resume` to pick up where you left off.
108
+ - Drop an **XCODE.md** at the repo root (or run `/init`) and it's auto-loaded
109
+ as project memory.
110
+
111
+ ### Modes (shift+tab to cycle)
112
+
113
+ - **·· normal** — asks before writes/commands
114
+ - **⏵⏵ auto** — runs & writes without asking
115
+ - **◷ plan** — read-only; explores but makes no changes
116
+
117
+ ### Sub-agents, web, MCP, hooks
118
+
119
+ - `spawn_agent` lets the model delegate an isolated subtask to a fresh context.
120
+ - `web_search` (DuckDuckGo) and `web_fetch` give it internet access.
121
+ - Drop a `.xcode/settings.json` to add **hooks** (run a formatter after every
122
+ edit), **env** vars, seed **permissions**, and declare **MCP servers**:
123
+
124
+ ```json
125
+ {
126
+ "hooks": { "after_edit": ["ruff format {path}"] },
127
+ "permissions": { "commands": ["git", "ls", "python"] },
128
+ "mcpServers": {
129
+ "fs": { "command": "npx",
130
+ "args": ["-y", "@modelcontextprotocol/server-filesystem", "."] }
131
+ }
132
+ }
133
+ ```
134
+
135
+ MCP tools show up to the model as `mcp__<server>__<tool>`.
136
+
137
+ ### Headless / scripting
138
+
139
+ ```bash
140
+ xcode -p "summarize what this repo does" # read-only, prints, exits
141
+ xcode -p "bump the version to 0.2.0" --yes # auto-approve writes
142
+ xcode -p "what changed?" --resume # continue last session
143
+ ```
144
+
145
+ ## Configuration (env vars)
146
+
147
+ | var | meaning |
148
+ |------------------|------------------------------------------------------|
149
+ | `XCODE_BASE_URL` | point straight at any OpenAI-compatible `/v1` URL |
150
+ | `XCODE_MODEL` | force a specific model name |
151
+ | `XCODE_API_KEY` | token if your endpoint needs one (default `local`) |
152
+ | `XCODE_MAX_STEPS`| max tool round-trips per turn (default 25) |
153
+
154
+ ## How it works
155
+
156
+ ```
157
+ cli.py REPL + permission prompts (the only UI code)
158
+ agent.py the loop: model ⇄ tools until it stops calling tools
159
+ backends.py auto-detect Ollama (:11434) / llama.cpp (:8080)
160
+ tools.py read_file, write_file, list_dir, run_command + JSON schemas
161
+ config.py system prompt + knobs
162
+ ```
163
+
164
+ ## Roadmap
165
+
166
+ - [x] Streaming token output
167
+ - [x] `edit_file` (targeted edits instead of full rewrites)
168
+ - [x] `grep` / `glob_files` search tools
169
+ - [x] Persistent permission rules ("always allow `git …`")
170
+ - [x] `/model` picker + smart default-model selection
171
+ - [x] Context compaction for long sessions + context meter
172
+ - [x] Diff-style preview when confirming edits
173
+ - [x] Project memory (XCODE.md) + `/init`
174
+ - [x] Todo/task tracking
175
+ - [x] Session save + `--resume`
176
+ - [x] Headless mode (`-p`) + `@file` mentions
177
+ - [x] Web fetch / web search tools
178
+ - [x] Sub-agents (delegate a subtask to a fresh context)
179
+ - [x] MCP server support
180
+ - [x] Hooks + settings.json
181
+ - [x] Themes + ghost logo, shift+tab mode cycling (normal/auto/plan)
182
+
183
+ ## Made by
184
+
185
+ Built by **@c7s89r** (nzv).
186
+
187
+ - GitHub: [@c7s89r](https://github.com/c7s89r)
188
+ - Discord: `c7s89r`
189
+
190
+ MIT licensed — see [LICENSE](LICENSE).
@@ -0,0 +1,20 @@
1
+ xcode/__init__.py,sha256=bfuPcvsqB0fFWkasX5F3XKEtGuwBM6djqJiXbUHLgcI,108
2
+ xcode/__main__.py,sha256=MSmt_5Xg84uHqzTN38JwgseJK8rsJn_11A8WD99VtEo,61
3
+ xcode/agent.py,sha256=UUK78OArJNd0OGC_aCJhQcEl-klXik-_CJX417aN7XU,14149
4
+ xcode/backends.py,sha256=CEfkyzjGn-1psVr4GZKiE--Ggprqd82MMjaJsYrN3yk,4636
5
+ xcode/cli.py,sha256=ciBSoB0JBGI3fqNQVXpC4K70xD6cfi5LyignSVmbVEg,24391
6
+ xcode/config.py,sha256=eLuOCJsZkvzk4bpiJRMHyi1tijIslNVejciiA74cwa8,3883
7
+ xcode/hooks.py,sha256=zqWfe5dLZ_H09M1IPqGaTR5c8b_qA5mTyPPlIO3614k,2402
8
+ xcode/input_bar.py,sha256=dzCxTe9t2SnKhpph8oNKsoBZgMBpHoaqkO2AIzp2Xjs,13839
9
+ xcode/mcp.py,sha256=U4OQYwgaTEmF7cXM1rl8U58AotjbMGU1F8PXAU_TrLs,5725
10
+ xcode/memory.py,sha256=PNAr4bbzB5yjAntFjHckoWCfUH0X2VpazkCqmrQ2JcA,1217
11
+ xcode/permissions.py,sha256=EeggrynUfP_Yjm0oqMoLrP44IN9u9egN1WeA9HWNyiE,2661
12
+ xcode/session.py,sha256=KcjBR-PmVEJF5JDTMk7WphPi8YcpYofg-NTisv9V588,1757
13
+ xcode/tools.py,sha256=-eB4A5lnHo9hPIX-xSsxYhufsBkqD62jHaPFbKskQYk,17564
14
+ xcode/ui.py,sha256=qJ6_21hLxF_qdOEISxQdYC8JRnHEIFH1zDS_2eGTk9w,13477
15
+ xcoding-0.1.0.dist-info/licenses/LICENSE,sha256=E2ul7pgp6YEzKrl_f3mwu6X4P02Ap82BqMW0dyO7r20,1069
16
+ xcoding-0.1.0.dist-info/METADATA,sha256=OgjdcmLzf0Slew0VuJkuOoxFsK6J1vS3_UH-Kghp3l4,6210
17
+ xcoding-0.1.0.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
18
+ xcoding-0.1.0.dist-info/entry_points.txt,sha256=qvTz6nsCXyjnhJwuI6OQ7At8x3YlxWUTo1KhacIoXWk,41
19
+ xcoding-0.1.0.dist-info/top_level.txt,sha256=81BFRvNclHTxdaBmzzIyfb_JaVM_fwWEGSRkMhsoRBs,6
20
+ xcoding-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ xcode = xcode.cli:main
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 c7s89r (nzv)
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -0,0 +1 @@
1
+ xcode