gemcode 0.3.74__py3-none-any.whl → 0.3.75__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.
gemcode/cli.py CHANGED
@@ -19,6 +19,7 @@ from gemcode.capability_routing import apply_capability_routing
19
19
  from gemcode.session_runtime import create_runner
20
20
  from gemcode.trust import is_trusted_root, trust_root
21
21
  from gemcode.repl_slash import process_repl_slash
22
+ from gemcode.ide_stdio import main as ide_stdio_main
22
23
 
23
24
 
24
25
  def _events_to_text(events) -> str:
@@ -339,6 +340,20 @@ def main() -> None:
339
340
  "enable Terminal for Desktop Folder (or grant Full Disk Access)."
340
341
  )
341
342
 
343
+ # Hidden IDE engine mode: `gemcode ide --stdio`
344
+ if len(sys.argv) >= 2 and sys.argv[1] == "ide":
345
+ ide_parser = argparse.ArgumentParser(prog="gemcode ide")
346
+ ide_parser.add_argument(
347
+ "--stdio",
348
+ action="store_true",
349
+ help="Run IDE engine over stdin/stdout (JSONL)",
350
+ )
351
+ ide_args = ide_parser.parse_args(sys.argv[2:])
352
+ if ide_args.stdio:
353
+ ide_stdio_main()
354
+ return
355
+ raise SystemExit("Usage: gemcode ide --stdio")
356
+
342
357
  # Persist or rotate API key (Claude Code–style `claude login`).
343
358
  if len(sys.argv) > 1 and sys.argv[1] == "login":
344
359
  load_cli_environment()
gemcode/config.py CHANGED
@@ -340,6 +340,12 @@ class GemCodeConfig:
340
340
  default_factory=lambda: _truthy_env("GEMCODE_ENABLE_WEB_SEARCH", default=False)
341
341
  )
342
342
 
343
+ # IDE mode (VS Code extension): the engine should *propose* writes/commands,
344
+ # and the IDE applies them (WorkspaceEdit / terminal task) after user approval.
345
+ ide_proposal_mode: bool = False
346
+ ide_allow_write: bool = False
347
+ ide_allow_shell: bool = False
348
+
343
349
  def __post_init__(self) -> None:
344
350
  self.project_root = self.project_root.resolve()
345
351
  # Default agentic depth when env omits GEMCODE_MAX_LLM_CALLS (was: None → SDK default).
@@ -0,0 +1,58 @@
1
+ """
2
+ GemCode IDE stdio protocol (JSON Lines).
3
+
4
+ This module defines the *wire format* for `gemcode ide --stdio`, used by IDE
5
+ extensions (VS Code) to talk to a long-lived GemCode engine process.
6
+
7
+ Design goals:
8
+ - Human-readable JSONL (easy to debug)
9
+ - Streaming (token deltas + progress)
10
+ - Safe editing (engine proposes; IDE applies via WorkspaceEdit)
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import json
16
+ import sys
17
+ import time
18
+ from dataclasses import dataclass
19
+ from typing import Any
20
+
21
+
22
+ PROTOCOL_VERSION = 1
23
+
24
+
25
+ def _now_ms() -> int:
26
+ return int(time.time() * 1000)
27
+
28
+
29
+ def dumps(obj: Any) -> str:
30
+ return json.dumps(obj, ensure_ascii=False, separators=(",", ":"))
31
+
32
+
33
+ @dataclass
34
+ class IdeEmitter:
35
+ """Writes JSONL messages to stdout (flushes each line)."""
36
+
37
+ stream: Any = sys.stdout
38
+
39
+ def send(self, msg: dict) -> None:
40
+ msg = dict(msg or {})
41
+ msg.setdefault("v", PROTOCOL_VERSION)
42
+ msg.setdefault("ts_ms", _now_ms())
43
+ self.stream.write(dumps(msg) + "\n")
44
+ try:
45
+ self.stream.flush()
46
+ except Exception:
47
+ pass
48
+
49
+
50
+ def parse_json_line(line: str) -> dict[str, Any]:
51
+ try:
52
+ obj = json.loads(line)
53
+ except Exception as e:
54
+ return {"type": "invalid", "error": f"invalid_json: {e}"}
55
+ if not isinstance(obj, dict):
56
+ return {"type": "invalid", "error": "message must be a JSON object"}
57
+ return obj
58
+
gemcode/ide_stdio.py ADDED
@@ -0,0 +1,164 @@
1
+ """
2
+ `gemcode ide --stdio`
3
+
4
+ Long-lived engine process that communicates over stdin/stdout using JSONL.
5
+
6
+ The IDE is responsible for:
7
+ - presenting UI
8
+ - previewing diffs
9
+ - applying changes (WorkspaceEdit)
10
+
11
+ GemCode is responsible for:
12
+ - planning + tool calls
13
+ - proposing edits/commands (when in proposal mode)
14
+ """
15
+
16
+ from __future__ import annotations
17
+
18
+ import asyncio
19
+ import os
20
+ import sys
21
+ from typing import Any
22
+
23
+ from gemcode.config import GemCodeConfig, load_cli_environment
24
+ from gemcode.ide_protocol import IdeEmitter, parse_json_line
25
+ from gemcode.invoke import run_turn
26
+ from gemcode.session_runtime import create_runner
27
+
28
+
29
+ def _truthy(v: Any, default: bool = False) -> bool:
30
+ if v is None:
31
+ return default
32
+ if isinstance(v, bool):
33
+ return v
34
+ if isinstance(v, (int, float)):
35
+ return bool(v)
36
+ if isinstance(v, str):
37
+ return v.strip().lower() in ("1", "true", "yes", "on")
38
+ return default
39
+
40
+
41
+ def _build_prompt(prompt: str, attachments: list[dict] | None) -> str:
42
+ # Keep it simple: attachments are appended as fenced blocks.
43
+ if not attachments:
44
+ return prompt
45
+ parts = [prompt.rstrip()]
46
+ for a in attachments:
47
+ if not isinstance(a, dict):
48
+ continue
49
+ at = (a.get("type") or "").strip().lower()
50
+ if at == "selection":
51
+ txt = a.get("text") or ""
52
+ path = a.get("path") or ""
53
+ rng = a.get("range") or ""
54
+ header = f"Selection from {path}{(' ' + rng) if rng else ''}".strip()
55
+ parts.append(f"\n\n```text\n{header}\n{txt}\n```")
56
+ elif at == "file":
57
+ path = a.get("path") or ""
58
+ snippet = a.get("text") or ""
59
+ header = f"File context: {path}".strip()
60
+ parts.append(f"\n\n```text\n{header}\n{snippet}\n```")
61
+ return "\n".join(parts).strip() + "\n"
62
+
63
+
64
+ async def run_stdio_loop() -> int:
65
+ load_cli_environment()
66
+ # Keep stdout reserved for protocol JSONL. Redirect accidental prints to stderr.
67
+ proto_out = sys.stdout
68
+ try:
69
+ sys.stdout = sys.stderr # type: ignore[assignment]
70
+ except Exception:
71
+ pass
72
+ emitter = IdeEmitter(stream=proto_out)
73
+ emitter.send({"type": "hello", "protocol": 1})
74
+
75
+ runner = None
76
+ cfg: GemCodeConfig | None = None
77
+ session_id: str | None = None
78
+
79
+ try:
80
+ for raw in sys.stdin:
81
+ msg = parse_json_line(raw)
82
+ mtype = msg.get("type")
83
+ if mtype in ("invalid", None):
84
+ emitter.send({"type": "error", "error": msg.get("error") or "invalid"})
85
+ continue
86
+
87
+ if mtype == "shutdown":
88
+ emitter.send({"type": "bye"})
89
+ return 0
90
+
91
+ if mtype != "turn":
92
+ emitter.send({"type": "error", "error": f"unknown_type:{mtype}"})
93
+ continue
94
+
95
+ # Lazily initialize runner on first turn (needs project root).
96
+ if cfg is None:
97
+ root = msg.get("project_root") or os.getcwd()
98
+ model = msg.get("model") or os.environ.get("GEMCODE_MODEL") or ""
99
+ from pathlib import Path
100
+ cfg = GemCodeConfig(project_root=Path(str(root)), model=str(model))
101
+ # Attach emitter + proposal mode flags (used by tool wrappers).
102
+ object.__setattr__(cfg, "_ide_emitter", emitter)
103
+ object.__setattr__(cfg, "ide_proposal_mode", True)
104
+ runner = create_runner(cfg, extra_tools=None)
105
+
106
+ if session_id is None:
107
+ session_id = str(msg.get("session") or "vscode")
108
+
109
+ prompt = str(msg.get("prompt") or "")
110
+ attachments = msg.get("attachments") if isinstance(msg.get("attachments"), list) else None
111
+ full_prompt = _build_prompt(prompt, attachments)
112
+
113
+ # Per-turn allow flags (the engine still only proposes in IDE mode; the IDE applies).
114
+ allow_write = _truthy(msg.get("allowWrite"), default=False)
115
+ allow_shell = _truthy(msg.get("allowShell"), default=False)
116
+ object.__setattr__(cfg, "ide_allow_write", bool(allow_write))
117
+ object.__setattr__(cfg, "ide_allow_shell", bool(allow_shell))
118
+
119
+ emitter.send({"type": "turn_start", "session": session_id})
120
+ try:
121
+ events = await run_turn(
122
+ runner,
123
+ user_id="local",
124
+ session_id=session_id,
125
+ prompt=full_prompt,
126
+ max_llm_calls=cfg.max_llm_calls,
127
+ cfg=cfg,
128
+ )
129
+ except Exception as e:
130
+ emitter.send({"type": "error", "error": f"{type(e).__name__}: {e}"})
131
+ emitter.send({"type": "turn_done", "session": session_id, "ok": False})
132
+ continue
133
+
134
+ # Emit assistant text as a single message for now (delta streaming can be added later).
135
+ txt_parts: list[str] = []
136
+ for ev in events:
137
+ try:
138
+ if not getattr(ev, "content", None) or not ev.content.parts:
139
+ continue
140
+ if getattr(ev, "author", None) == "user":
141
+ continue
142
+ for p in ev.content.parts:
143
+ t = getattr(p, "text", None)
144
+ if t:
145
+ txt_parts.append(t)
146
+ except Exception:
147
+ continue
148
+ out_text = "".join(txt_parts).strip()
149
+ if out_text:
150
+ emitter.send({"type": "text", "text": out_text})
151
+ emitter.send({"type": "turn_done", "session": session_id, "ok": True})
152
+
153
+ finally:
154
+ if runner is not None:
155
+ try:
156
+ await runner.close()
157
+ except Exception:
158
+ pass
159
+ return 0
160
+
161
+
162
+ def main() -> None:
163
+ raise SystemExit(asyncio.run(run_stdio_loop()))
164
+
gemcode/tools/bash.py CHANGED
@@ -30,6 +30,14 @@ def make_bash_tool(cfg: GemCodeConfig):
30
30
  root = cfg.project_root
31
31
  trusted = is_trusted_root(root)
32
32
 
33
+ def _emit(msg: dict) -> None:
34
+ em = getattr(cfg, "_ide_emitter", None)
35
+ if em is not None:
36
+ try:
37
+ em.send(msg)
38
+ except Exception:
39
+ pass
40
+
33
41
  def bash(
34
42
  command: str,
35
43
  timeout_seconds: int = 120,
@@ -95,6 +103,20 @@ def make_bash_tool(cfg: GemCodeConfig):
95
103
  explicit user approval. Quote file paths that contain spaces.
96
104
  cwd_subdir is relative to the project root.
97
105
  """
106
+ if getattr(cfg, "ide_proposal_mode", False):
107
+ if not getattr(cfg, "ide_allow_shell", False):
108
+ _emit({"type": "permission_request", "kind": "shell", "detail": "bash(...)"} )
109
+ return {"error": "shell_not_allowed"}
110
+ _emit(
111
+ {
112
+ "type": "command_suggestion",
113
+ "cmd": command,
114
+ "cwd_subdir": cwd_subdir,
115
+ "background": bool(background),
116
+ "via": "bash",
117
+ }
118
+ )
119
+ return {"suggested": True, "via": "bash", "cmd": command, "cwd_subdir": cwd_subdir, "background": bool(background)}
98
120
  if not trusted:
99
121
  return {"error": "Project folder is not trusted. Re-run GemCode and approve folder trust."}
100
122
 
gemcode/tools/edit.py CHANGED
@@ -2,6 +2,8 @@
2
2
 
3
3
  from __future__ import annotations
4
4
 
5
+ import hashlib
6
+ import time
5
7
  from pathlib import Path
6
8
 
7
9
  from gemcode.config import GemCodeConfig
@@ -11,6 +13,20 @@ from gemcode.paths import PathEscapeError, resolve_under_root
11
13
  def make_edit_tools(cfg: GemCodeConfig):
12
14
  root = cfg.project_root
13
15
 
16
+ def _sha256_text(s: str) -> str:
17
+ return hashlib.sha256(s.encode("utf-8", errors="strict")).hexdigest()
18
+
19
+ def _emit(msg: dict) -> None:
20
+ em = getattr(cfg, "_ide_emitter", None)
21
+ if em is not None:
22
+ try:
23
+ em.send(msg)
24
+ except Exception:
25
+ pass
26
+
27
+ def _proposal_id(prefix: str) -> str:
28
+ return f"{prefix}_{int(time.time()*1000)}"
29
+
14
30
  def write_file(path: str, content: str) -> dict:
15
31
  """
16
32
  Create or overwrite a file with the given content.
@@ -25,6 +41,47 @@ def make_edit_tools(cfg: GemCodeConfig):
25
41
  Never write non-textual content (binary, base64 blobs) — those belong in
26
42
  artifacts, not in source files.
27
43
  """
44
+ if getattr(cfg, "ide_proposal_mode", False):
45
+ if not getattr(cfg, "ide_allow_write", False):
46
+ _emit(
47
+ {
48
+ "type": "permission_request",
49
+ "kind": "write",
50
+ "detail": f"write_file({path})",
51
+ }
52
+ )
53
+ return {"error": "write_not_allowed"}
54
+ # Propose instead of executing.
55
+ try:
56
+ p = resolve_under_root(root, path)
57
+ except PathEscapeError as e:
58
+ return {"error": str(e)}
59
+ old_text = ""
60
+ existed = p.is_file()
61
+ if existed:
62
+ try:
63
+ old_text = p.read_text(encoding="utf-8", errors="strict")
64
+ except Exception:
65
+ old_text = ""
66
+ pid = _proposal_id("edit")
67
+ _emit(
68
+ {
69
+ "type": "edit_proposal",
70
+ "id": pid,
71
+ "files": [
72
+ {
73
+ "path": path,
74
+ "existed": existed,
75
+ "original_sha256": _sha256_text(old_text) if existed else None,
76
+ "new_sha256": _sha256_text(content),
77
+ "old_text": old_text,
78
+ "new_text": content,
79
+ }
80
+ ],
81
+ }
82
+ )
83
+ return {"proposal_id": pid, "path": path, "bytes": len(content.encode("utf-8"))}
84
+
28
85
  try:
29
86
  p = resolve_under_root(root, path)
30
87
  except PathEscapeError as e:
@@ -60,6 +117,16 @@ def make_edit_tools(cfg: GemCodeConfig):
60
117
  comments that explain non-obvious intent or trade-offs.
61
118
  - NEVER propose edits before reading. Read first. Edit second.
62
119
  """
120
+ if getattr(cfg, "ide_proposal_mode", False):
121
+ if not getattr(cfg, "ide_allow_write", False):
122
+ _emit(
123
+ {
124
+ "type": "permission_request",
125
+ "kind": "write",
126
+ "detail": f"search_replace({path})",
127
+ }
128
+ )
129
+ return {"error": "write_not_allowed"}
63
130
  try:
64
131
  p = resolve_under_root(root, path)
65
132
  except PathEscapeError as e:
@@ -76,6 +143,25 @@ def make_edit_tools(cfg: GemCodeConfig):
76
143
  new_text = text.replace(old_string, new_string)
77
144
  else:
78
145
  new_text = text.replace(old_string, new_string, 1)
146
+ if getattr(cfg, "ide_proposal_mode", False):
147
+ pid = _proposal_id("edit")
148
+ _emit(
149
+ {
150
+ "type": "edit_proposal",
151
+ "id": pid,
152
+ "files": [
153
+ {
154
+ "path": path,
155
+ "existed": True,
156
+ "original_sha256": _sha256_text(text),
157
+ "new_sha256": _sha256_text(new_text),
158
+ "old_text": text,
159
+ "new_text": new_text,
160
+ }
161
+ ],
162
+ }
163
+ )
164
+ return {"proposal_id": pid, "path": path, "replacements": count if replace_all else 1}
79
165
  p.write_text(new_text, encoding="utf-8")
80
166
  return {"path": path, "replacements": count if replace_all else 1}
81
167
 
gemcode/tools/shell.py CHANGED
@@ -37,6 +37,14 @@ def make_run_command(cfg: GemCodeConfig):
37
37
  root = cfg.project_root
38
38
  trusted = is_trusted_root(root)
39
39
 
40
+ def _emit(msg: dict) -> None:
41
+ em = getattr(cfg, "_ide_emitter", None)
42
+ if em is not None:
43
+ try:
44
+ em.send(msg)
45
+ except Exception:
46
+ pass
47
+
40
48
  def run_command(
41
49
  command: str,
42
50
  args: list[str] | None = None,
@@ -61,6 +69,22 @@ def make_run_command(cfg: GemCodeConfig):
61
69
  merged into the child environment (e.g. keys ["CI"], values ["1"] for
62
70
  non-interactive scaffolding tools). Omit both to use the default environment.
63
71
  """
72
+ if getattr(cfg, "ide_proposal_mode", False):
73
+ exe = str(command or "").strip()
74
+ args2 = list(args or [])
75
+ if not getattr(cfg, "ide_allow_shell", False):
76
+ _emit({"type": "permission_request", "kind": "shell", "detail": f"run_command({exe})"})
77
+ return {"error": "shell_not_allowed"}
78
+ _emit(
79
+ {
80
+ "type": "command_suggestion",
81
+ "cmd": " ".join([exe, *args2]).strip(),
82
+ "cwd_subdir": cwd_subdir,
83
+ "background": bool(background),
84
+ }
85
+ )
86
+ return {"suggested": True, "command": [exe, *args2], "cwd_subdir": cwd_subdir, "background": bool(background)}
87
+
64
88
  if not trusted:
65
89
  return {"error": "Project folder is not trusted. Re-run GemCode and approve folder trust."}
66
90
  if not (cwd_subdir or "").strip():
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gemcode
3
- Version: 0.3.74
3
+ Version: 0.3.75
4
4
  Summary: Local-first coding agent on Google Gemini + ADK
5
5
  Author: GemCode Contributors
6
6
  License: Apache License
@@ -5,15 +5,17 @@ gemcode/audit.py,sha256=bh9uhXaeh8wqxqoZtz3ZAowd8Ndk1ss-mw9993Vlrgo,469
5
5
  gemcode/autocompact.py,sha256=77h5tgFzJ2rjrhlCL2oIc28IHwLbP4Pqlo7cSNgDwiA,6727
6
6
  gemcode/callbacks.py,sha256=EYkJTRcHjCeqTgVZGat2Y3BYWg4tNtHl-cgl5cdUxpo,31076
7
7
  gemcode/capability_routing.py,sha256=yvQXwKtrfHXbgbNunU0Dxh9GCDN4cbySXIeccrdzr2o,3471
8
- gemcode/cli.py,sha256=kBXb4b4JCG3u0XewCJn8lCyOT62Y8bOvlVoDc2R-GKQ,25320
8
+ gemcode/cli.py,sha256=nm9AZoCW9buNnpkfwBAOUp07lU7ylekboFL7uWNZjTI,25830
9
9
  gemcode/compaction.py,sha256=9YtA_qa23_8dHWVHx7AJwUduuI7jJQtq-m6sT8jgPWI,1186
10
- gemcode/config.py,sha256=kXSpuGiEqMSyJRK8MKsk6o5Y_Kqwj_kAGhhlVkfWaaE,16727
10
+ gemcode/config.py,sha256=9fm5mxU3MqR95XeO4oumXjVYbkFS0Itc-Vi0g4XH5Mk,16987
11
11
  gemcode/context_budget.py,sha256=Nhox9vFBtLbb7jtO7cyGW1MxtN7SVjlIeQ7d-cgGyKM,10544
12
12
  gemcode/context_warning.py,sha256=Q8mg5Vojj7EglPhsGAVL7vb8ROLuHVPgdzw25yw-Q2c,4263
13
13
  gemcode/credentials.py,sha256=04v-rLD8_Ams69FQdof2FwcL3ZgsroGUnMcHNQFuBZo,1296
14
14
  gemcode/dynamic_policy.py,sha256=nWgBN6ffSn1Te4aDI1MURxRaQjTclzIuSf4KaAskE9U,4662
15
15
  gemcode/hitl_session.py,sha256=oNiI7odFJGUcmqPavjKLJOEumZKrgklLvwjjrIG9GPg,281
16
16
  gemcode/hooks.py,sha256=FHz175d_18j-4ByZZVdEIagmdOvLHcjDjo7HD2Cikf4,6339
17
+ gemcode/ide_protocol.py,sha256=benFpKBjXCBMfawYJauktD06JNgVH2WX0zfvJv2dusI,1325
18
+ gemcode/ide_stdio.py,sha256=dWsLj262s6WyFsizRUqx4IEZw0Jr3m9jsQnxR4kq8Gg,5200
17
19
  gemcode/intent_classifier.py,sha256=YfRVEe8gHeKlRkjuSWef1bZ0MPBwyYMp5jymP5Vig5U,8507
18
20
  gemcode/interactions.py,sha256=B0b3QNE_I2i5_HtiebX4ehhjlc4Nbqjf_XbvcTLyJT0,641
19
21
  gemcode/invoke.py,sha256=rHxV0_uFTjwvu6oOIpzNK98su1l3V1kQkqCtefxqhyA,10275
@@ -63,15 +65,15 @@ gemcode/query/stop_hooks.py,sha256=jaXMN2OptwHeGXF8o630zIVr62T8jVg-eIyREG0GyxA,1
63
65
  gemcode/query/token_budget.py,sha256=JTq2TrGkFY5t5KOs-P9XQPqahyjcdTzN3wctZ1JxFV0,2973
64
66
  gemcode/query/transitions.py,sha256=vJ77cv4cFwdvsxyGr7MxXz6uGVB5IDqOClqR1MkWvqw,933
65
67
  gemcode/tools/__init__.py,sha256=nSHcbts_cgAPGa2izrvczXJAHqtPjah8YhzbzERoGTI,4608
66
- gemcode/tools/bash.py,sha256=u9z7OI3iBGl5Df_GGipDsR8i3OcN59juw1hQHbO1PPk,12765
68
+ gemcode/tools/bash.py,sha256=WA6UmPy7OL1RVjLqw3Zii-3b_gS7nhdVTw5qIEwRE5E,13649
67
69
  gemcode/tools/browser.py,sha256=StWRttiyGkR4qaG5urRviJgdoj2hiFB2OuHPItaVzJY,7250
68
- gemcode/tools/edit.py,sha256=1fo-wlRT6fyuib0JnmUbPVG3cpjKNJsnmj9jxTgrZTs,3182
70
+ gemcode/tools/edit.py,sha256=cjf2UFKMWxgzVZlLqBbVt7X6S1UfUrLA-6afggPZmx4,5671
69
71
  gemcode/tools/filesystem.py,sha256=LfdPcGciW4z1-fKgfwjQC7cvMlfCNymCNtGh8-vEdKc,6466
70
72
  gemcode/tools/notebook.py,sha256=zR-bVU6mniJYI9u2RUgm0BmnHmTc-TmEETzLBsXzueA,9197
71
73
  gemcode/tools/notes.py,sha256=7Rp7eJPANELDBKfwNPzi4pco_uP3GP07vNun9YJxOCw,4544
72
74
  gemcode/tools/repo_map.py,sha256=hFjLcuZpJrlj08kJd2iXKIpTQRYAJQyaKPnuVNemZbQ,3650
73
75
  gemcode/tools/search.py,sha256=1I41K5galCa8hTHLZTc7OsFWGz0e92QsEyd7Jw6veUQ,5986
74
- gemcode/tools/shell.py,sha256=K9wstCKAasCxNTuu_s3w7yDZ_esXe-fgGH7UKcadOvc,5667
76
+ gemcode/tools/shell.py,sha256=88gsNvR_fsSySedO6gftXrbx-VoYICkfYpsPRTMymiQ,6477
75
77
  gemcode/tools/shell_gate.py,sha256=8wLfdjBsfgH8PnMaEIjdJWMJMtvLMGjgP93lFMcbFqE,929
76
78
  gemcode/tools/subtask.py,sha256=mmuVp7BazlZiJQk-sI72TmtSBq7omC4H43jQ-X8YJ3I,10070
77
79
  gemcode/tools/tasks.py,sha256=sPb9Ru0BBBcx_BMqLJBeOws6OR_-R0gyHAaotulsBBk,7657
@@ -87,9 +89,9 @@ gemcode/tui/welcome_rich.py,sha256=8FEZzLXrzqly5JWiDgV9ooRV1LNXDk-CXV1a7K6ua-U,4
87
89
  gemcode/web/__init__.py,sha256=EysmUAWs6g-lmMk4VFljKfaHVrEgb_FiIzwQmBdORJc,40
88
90
  gemcode/web/claude_sse_adapter.py,sha256=HcNp0Lh4DdBZBLOpstsqa-VzfqAUrRngZ6FSuJ-mIMg,8609
89
91
  gemcode/web/terminal_repl.py,sha256=k2irvFGbCY8gDm_pbirR7b_cakaeafcctoTIvnJkVXk,3902
90
- gemcode-0.3.74.dist-info/licenses/LICENSE,sha256=TD4524qn-W8Z07GTDnag-9jJPFutFZNB0a1WbMHPC54,8388
91
- gemcode-0.3.74.dist-info/METADATA,sha256=lCZr_kcUZ50jBLfwR2M94M6DBB-hE6rKICkAwBVb7jM,26402
92
- gemcode-0.3.74.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
93
- gemcode-0.3.74.dist-info/entry_points.txt,sha256=cZdLTLDiHbks7OSUCuxCh66dCWeQdpLR8BozoqfEjV4,45
94
- gemcode-0.3.74.dist-info/top_level.txt,sha256=UYrjULLBY2bcgK6KI6flomJWmsbDXu7n0rvW2SWFrbo,8
95
- gemcode-0.3.74.dist-info/RECORD,,
92
+ gemcode-0.3.75.dist-info/licenses/LICENSE,sha256=TD4524qn-W8Z07GTDnag-9jJPFutFZNB0a1WbMHPC54,8388
93
+ gemcode-0.3.75.dist-info/METADATA,sha256=RzP56-sQVecO_5QVBKmphZp7zws7mah8srMTtcZERSQ,26402
94
+ gemcode-0.3.75.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
95
+ gemcode-0.3.75.dist-info/entry_points.txt,sha256=cZdLTLDiHbks7OSUCuxCh66dCWeQdpLR8BozoqfEjV4,45
96
+ gemcode-0.3.75.dist-info/top_level.txt,sha256=UYrjULLBY2bcgK6KI6flomJWmsbDXu7n0rvW2SWFrbo,8
97
+ gemcode-0.3.75.dist-info/RECORD,,