gemcode 0.3.50__tar.gz → 0.3.52__tar.gz

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.
Files changed (121) hide show
  1. {gemcode-0.3.50/src/gemcode.egg-info → gemcode-0.3.52}/PKG-INFO +1 -1
  2. {gemcode-0.3.50 → gemcode-0.3.52}/pyproject.toml +1 -1
  3. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/session_runtime.py +103 -70
  4. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/tui/input_handler.py +17 -3
  5. {gemcode-0.3.50 → gemcode-0.3.52/src/gemcode.egg-info}/PKG-INFO +1 -1
  6. {gemcode-0.3.50 → gemcode-0.3.52}/LICENSE +0 -0
  7. {gemcode-0.3.50 → gemcode-0.3.52}/MANIFEST.in +0 -0
  8. {gemcode-0.3.50 → gemcode-0.3.52}/README.md +0 -0
  9. {gemcode-0.3.50 → gemcode-0.3.52}/setup.cfg +0 -0
  10. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/__init__.py +0 -0
  11. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/__main__.py +0 -0
  12. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/agent.py +0 -0
  13. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/audit.py +0 -0
  14. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/autocompact.py +0 -0
  15. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/callbacks.py +0 -0
  16. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/capability_routing.py +0 -0
  17. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/cli.py +0 -0
  18. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/compaction.py +0 -0
  19. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/computer_use/__init__.py +0 -0
  20. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/computer_use/browser_computer.py +0 -0
  21. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/config.py +0 -0
  22. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/context_budget.py +0 -0
  23. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/context_warning.py +0 -0
  24. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/credentials.py +0 -0
  25. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/hitl_session.py +0 -0
  26. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/hooks.py +0 -0
  27. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/intent_classifier.py +0 -0
  28. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/interactions.py +0 -0
  29. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/invoke.py +0 -0
  30. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/kairos_daemon.py +0 -0
  31. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/limits.py +0 -0
  32. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/live_audio_engine.py +0 -0
  33. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/logging_config.py +0 -0
  34. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/mcp_loader.py +0 -0
  35. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/memory/__init__.py +0 -0
  36. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/memory/embedding_memory_service.py +0 -0
  37. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/memory/file_memory_service.py +0 -0
  38. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/modality_tools.py +0 -0
  39. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/model_errors.py +0 -0
  40. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/model_routing.py +0 -0
  41. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/openapi_loader.py +0 -0
  42. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/paths.py +0 -0
  43. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/permissions.py +0 -0
  44. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/plugins/__init__.py +0 -0
  45. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/plugins/terminal_hooks_plugin.py +0 -0
  46. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/plugins/tool_recovery_plugin.py +0 -0
  47. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/pricing.py +0 -0
  48. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/prompt_suggestions.py +0 -0
  49. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/query/__init__.py +0 -0
  50. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/query/config.py +0 -0
  51. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/query/deps.py +0 -0
  52. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/query/engine.py +0 -0
  53. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/query/stop_hooks.py +0 -0
  54. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/query/token_budget.py +0 -0
  55. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/query/transitions.py +0 -0
  56. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/refine.py +0 -0
  57. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/repl_commands.py +0 -0
  58. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/repl_slash.py +0 -0
  59. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/review_agent.py +0 -0
  60. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/session_store.py +0 -0
  61. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/slash_commands.py +0 -0
  62. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/thinking.py +0 -0
  63. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/tool_prompt_manifest.py +0 -0
  64. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/tool_registry.py +0 -0
  65. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/tools/__init__.py +0 -0
  66. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/tools/bash.py +0 -0
  67. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/tools/browser.py +0 -0
  68. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/tools/edit.py +0 -0
  69. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/tools/filesystem.py +0 -0
  70. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/tools/notes.py +0 -0
  71. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/tools/search.py +0 -0
  72. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/tools/shell.py +0 -0
  73. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/tools/shell_gate.py +0 -0
  74. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/tools/subtask.py +0 -0
  75. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/tools/think.py +0 -0
  76. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/tools/todo.py +0 -0
  77. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/tools/web.py +0 -0
  78. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/tools_inspector.py +0 -0
  79. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/trust.py +0 -0
  80. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/tui/scrollback.py +0 -0
  81. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/tui/spinner.py +0 -0
  82. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/tui/welcome_banner.py +0 -0
  83. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/tui/welcome_rich.py +0 -0
  84. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/version.py +0 -0
  85. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/vertex.py +0 -0
  86. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/web/__init__.py +0 -0
  87. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/web/claude_sse_adapter.py +0 -0
  88. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/web/terminal_repl.py +0 -0
  89. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode/workspace_hints.py +0 -0
  90. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode.egg-info/SOURCES.txt +0 -0
  91. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode.egg-info/dependency_links.txt +0 -0
  92. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode.egg-info/entry_points.txt +0 -0
  93. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode.egg-info/requires.txt +0 -0
  94. {gemcode-0.3.50 → gemcode-0.3.52}/src/gemcode.egg-info/top_level.txt +0 -0
  95. {gemcode-0.3.50 → gemcode-0.3.52}/tests/test_agent_instruction.py +0 -0
  96. {gemcode-0.3.50 → gemcode-0.3.52}/tests/test_autocompact.py +0 -0
  97. {gemcode-0.3.50 → gemcode-0.3.52}/tests/test_capability_routing.py +0 -0
  98. {gemcode-0.3.50 → gemcode-0.3.52}/tests/test_claude_web_adapter_sse.py +0 -0
  99. {gemcode-0.3.50 → gemcode-0.3.52}/tests/test_cli_init.py +0 -0
  100. {gemcode-0.3.50 → gemcode-0.3.52}/tests/test_computer_use_permissions.py +0 -0
  101. {gemcode-0.3.50 → gemcode-0.3.52}/tests/test_context_budget.py +0 -0
  102. {gemcode-0.3.50 → gemcode-0.3.52}/tests/test_context_warning.py +0 -0
  103. {gemcode-0.3.50 → gemcode-0.3.52}/tests/test_credentials.py +0 -0
  104. {gemcode-0.3.50 → gemcode-0.3.52}/tests/test_interactive_permission_ask.py +0 -0
  105. {gemcode-0.3.50 → gemcode-0.3.52}/tests/test_kairos_scheduler.py +0 -0
  106. {gemcode-0.3.50 → gemcode-0.3.52}/tests/test_modality_tools.py +0 -0
  107. {gemcode-0.3.50 → gemcode-0.3.52}/tests/test_model_error_retry.py +0 -0
  108. {gemcode-0.3.50 → gemcode-0.3.52}/tests/test_model_errors.py +0 -0
  109. {gemcode-0.3.50 → gemcode-0.3.52}/tests/test_model_routing.py +0 -0
  110. {gemcode-0.3.50 → gemcode-0.3.52}/tests/test_paths.py +0 -0
  111. {gemcode-0.3.50 → gemcode-0.3.52}/tests/test_permissions.py +0 -0
  112. {gemcode-0.3.50 → gemcode-0.3.52}/tests/test_prompt_suggestions.py +0 -0
  113. {gemcode-0.3.50 → gemcode-0.3.52}/tests/test_repl_commands.py +0 -0
  114. {gemcode-0.3.50 → gemcode-0.3.52}/tests/test_repl_slash.py +0 -0
  115. {gemcode-0.3.50 → gemcode-0.3.52}/tests/test_slash_commands.py +0 -0
  116. {gemcode-0.3.50 → gemcode-0.3.52}/tests/test_thinking_config.py +0 -0
  117. {gemcode-0.3.50 → gemcode-0.3.52}/tests/test_token_budget.py +0 -0
  118. {gemcode-0.3.50 → gemcode-0.3.52}/tests/test_tool_context_circulation.py +0 -0
  119. {gemcode-0.3.50 → gemcode-0.3.52}/tests/test_tools.py +0 -0
  120. {gemcode-0.3.50 → gemcode-0.3.52}/tests/test_tools_inspector.py +0 -0
  121. {gemcode-0.3.50 → gemcode-0.3.52}/tests/test_workspace_hints.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gemcode
3
- Version: 0.3.50
3
+ Version: 0.3.52
4
4
  Summary: Local-first coding agent on Google Gemini + ADK
5
5
  Author: GemCode Contributors
6
6
  License: Apache License
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "gemcode"
7
- version = "0.3.50"
7
+ version = "0.3.52"
8
8
  description = "Local-first coding agent on Google Gemini + ADK"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -30,76 +30,109 @@ def session_db_path(cfg: GemCodeConfig) -> Path:
30
30
  return cfg.project_root / ".gemcode" / "sessions.sqlite"
31
31
 
32
32
 
33
- class _SafeComputerUseToolset:
33
+ def _make_safe_computer_toolset(computer):
34
34
  """
35
- Drop-in wrapper around ComputerUseToolset that catches Playwright startup
36
- failures (e.g. missing browser binary) and disables computer-use gracefully
37
- for the current session instead of crashing every turn with an unhandled error.
38
-
39
- When the underlying toolset fails for the *first* time a one-time warning is
40
- printed to stderr so the user knows they need to run ``playwright install``.
41
- Subsequent calls are silently no-ops so the session keeps working without
42
- browser tools — the agent can still use all other tools normally.
43
- """
44
-
45
- def __init__(self, computer) -> None:
46
- try:
47
- from google.adk.tools.computer_use.computer_use_toolset import ComputerUseToolset
48
- self._inner = ComputerUseToolset(computer=computer)
49
- except Exception:
50
- self._inner = None
51
- self._broken = False
52
- self._warned = False
53
-
54
- def _warn_once(self, error: Exception) -> None:
55
- if self._warned:
56
- return
57
- self._warned = True
58
- import sys
59
- msg = str(error)
60
- if "playwright install" in msg.lower() or "executable doesn't exist" in msg.lower():
61
- print(
62
- "\n[gemcode] Browser (computer-use) is unavailable — Playwright browsers are not installed.\n"
63
- " Run: playwright install chromium\n"
64
- " Then restart GemCode with /computer on (or --computer flag).\n"
65
- " Continuing without browser tools for this session.\n",
66
- file=sys.stderr,
67
- )
68
- else:
69
- print(
70
- f"\n[gemcode] Browser (computer-use) failed to start: {msg!s:.200}\n"
71
- " Continuing without browser tools for this session.\n",
72
- file=sys.stderr,
73
- )
35
+ Build a BaseToolset-compatible wrapper around ComputerUseToolset that catches
36
+ Playwright startup failures gracefully instead of crashing LlmAgent validation.
74
37
 
75
- # ── ADK toolset protocol ────────────────────────────────────────────────────
38
+ Must be a proper BaseToolset subclass (not a plain class) because ADK's Pydantic
39
+ model for LlmAgent validates each entry in `tools` against BaseTool | BaseToolset.
40
+ Returns a real BaseToolset subclass instance, or None if BaseToolset is unavailable.
41
+ """
42
+ try:
43
+ from google.adk.tools.base_toolset import BaseToolset
44
+ except ImportError:
45
+ return None
76
46
 
77
- async def process_llm_request(self, *, tool_context, llm_request) -> None:
78
- if self._broken or self._inner is None:
79
- return
80
- try:
81
- await self._inner.process_llm_request(
82
- tool_context=tool_context, llm_request=llm_request
83
- )
84
- except Exception as exc:
85
- self._broken = True
86
- self._warn_once(exc)
87
-
88
- async def get_tools(self, readonly_context=None):
89
- if self._broken or self._inner is None:
90
- return []
91
- try:
92
- return await self._inner.get_tools(readonly_context)
93
- except Exception as exc:
94
- self._broken = True
95
- self._warn_once(exc)
96
- return []
97
-
98
- def __getattr__(self, name: str):
99
- """Proxy all other attribute accesses to the inner toolset."""
100
- if self._inner is not None:
101
- return getattr(self._inner, name)
102
- raise AttributeError(name)
47
+ class _SafeComputerUseToolset(BaseToolset):
48
+ """Wraps ComputerUseToolset; degrades to a no-op if Playwright is missing."""
49
+
50
+ def __init__(self) -> None:
51
+ try:
52
+ from google.adk.tools.computer_use.computer_use_toolset import ComputerUseToolset
53
+ self._inner = ComputerUseToolset(computer=computer)
54
+ except Exception:
55
+ self._inner = None
56
+ self._broken = False
57
+ self._warned = False
58
+
59
+ def _warn_once(self, error: Exception) -> None:
60
+ if self._warned:
61
+ return
62
+ self._warned = True
63
+ import sys
64
+ msg = str(error)
65
+ if "playwright install" in msg.lower() or "executable doesn't exist" in msg.lower():
66
+ print(
67
+ "\n[gemcode] Browser (computer-use) is unavailable — Playwright browsers are not installed.\n"
68
+ " Run: playwright install chromium\n"
69
+ " Then restart GemCode with /computer on (or --computer flag).\n"
70
+ " Continuing without browser tools for this session.\n",
71
+ file=sys.stderr,
72
+ )
73
+ else:
74
+ print(
75
+ f"\n[gemcode] Browser (computer-use) failed to start: {msg!s:.200}\n"
76
+ " Continuing without browser tools for this session.\n",
77
+ file=sys.stderr,
78
+ )
79
+
80
+ # ── BaseToolset required attribute ──────────────────────────────────────
81
+ @property
82
+ def tool_name_prefix(self) -> str:
83
+ if self._inner is not None:
84
+ try:
85
+ return self._inner.tool_name_prefix
86
+ except Exception:
87
+ pass
88
+ return ""
89
+
90
+ # ── Core protocol methods ────────────────────────────────────────────────
91
+
92
+ async def process_llm_request(self, *, tool_context, llm_request) -> None:
93
+ if self._broken or self._inner is None:
94
+ return
95
+ try:
96
+ await self._inner.process_llm_request(
97
+ tool_context=tool_context, llm_request=llm_request
98
+ )
99
+ except Exception as exc:
100
+ if not self._broken:
101
+ self._broken = True
102
+ self._warn_once(exc)
103
+
104
+ async def get_tools(self, readonly_context=None):
105
+ if self._broken or self._inner is None:
106
+ return []
107
+ try:
108
+ return await self._inner.get_tools(readonly_context)
109
+ except Exception as exc:
110
+ if not self._broken:
111
+ self._broken = True
112
+ self._warn_once(exc)
113
+ return []
114
+
115
+ async def close(self) -> None:
116
+ if self._inner is not None:
117
+ try:
118
+ await self._inner.close()
119
+ except Exception:
120
+ pass
121
+
122
+ def __getattr__(self, name: str):
123
+ """Proxy any other BaseToolset attributes ADK needs to the inner toolset."""
124
+ # Guard against infinite recursion for our own private attrs.
125
+ if name.startswith("_"):
126
+ raise AttributeError(name)
127
+ inner = object.__getattribute__(self, "_inner")
128
+ if inner is not None:
129
+ try:
130
+ return getattr(inner, name)
131
+ except AttributeError:
132
+ pass
133
+ raise AttributeError(f"'_SafeComputerUseToolset' has no attribute '{name}'")
134
+
135
+ return _SafeComputerUseToolset()
103
136
 
104
137
 
105
138
  def _build_artifact_service(cfg: GemCodeConfig):
@@ -158,15 +191,15 @@ def create_runner(cfg: GemCodeConfig, extra_tools: list | None = None) -> Runner
158
191
  viewport_w = int(os.environ.get("GEMCODE_BROWSER_WIDTH", "1280"))
159
192
  viewport_h = int(os.environ.get("GEMCODE_BROWSER_HEIGHT", "720"))
160
193
  from gemcode.computer_use.browser_computer import BrowserComputer
161
- from google.adk.tools.computer_use.computer_use_toolset import ComputerUseToolset
162
194
 
163
195
  computer = BrowserComputer(
164
196
  headless=headless,
165
197
  viewport_size=(viewport_w, viewport_h),
166
198
  )
167
- computer_toolset = _SafeComputerUseToolset(computer=computer)
199
+ computer_toolset = _make_safe_computer_toolset(computer)
168
200
  merged_extra_tools = list(merged_extra_tools or [])
169
- merged_extra_tools.append(computer_toolset)
201
+ if computer_toolset is not None:
202
+ merged_extra_tools.append(computer_toolset)
170
203
 
171
204
  # Standalone read-only browser tools (browser_screenshot, browser_get_text, etc.)
172
205
  from gemcode.tools.browser import build_browser_inspection_tools
@@ -184,8 +184,18 @@ class GemCodeInputHandler:
184
184
  """Ctrl+C clears current line (like a real shell)."""
185
185
  event.app.current_buffer.reset()
186
186
 
187
- # Rows reserved for the / command popup. Too high (e.g. 20) leaves a huge
188
- # empty band above the prompt; 10–12 is enough for most slash lists.
187
+ @kb.add("enter")
188
+ def _submit(event):
189
+ """Enter always submits — even in multiline mode (for pasted code)."""
190
+ event.current_buffer.validate_and_handle()
191
+
192
+ @kb.add("escape", "enter", eager=True)
193
+ @kb.add("c-j") # Ctrl+J = manual newline inside a message
194
+ def _newline(event):
195
+ """Meta+Enter or Ctrl+J inserts a real newline without submitting."""
196
+ event.current_buffer.insert_text("\n")
197
+
198
+ # Rows reserved for the / command popup.
189
199
  try:
190
200
  _menu_rows = int(os.environ.get("GEMCODE_TUI_RESERVE_MENU_LINES", "12"))
191
201
  except ValueError:
@@ -202,8 +212,12 @@ class GemCodeInputHandler:
202
212
  mouse_support=False,
203
213
  complete_in_thread=True,
204
214
  reserve_space_for_menu=_menu_rows,
205
- # Single-column popup with description column (like VS Code)
206
215
  complete_style="COLUMN",
216
+ # Multiline=True so pasted code (with \n) lands in the buffer
217
+ # as one block rather than submitting line-by-line.
218
+ # Our Enter binding above overrides the default "add newline"
219
+ # behaviour so single-line prompts work exactly as before.
220
+ multiline=True,
207
221
  )
208
222
 
209
223
  def is_interactive(self) -> bool:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gemcode
3
- Version: 0.3.50
3
+ Version: 0.3.52
4
4
  Summary: Local-first coding agent on Google Gemini + ADK
5
5
  Author: GemCode Contributors
6
6
  License: Apache License
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes