gemcode 0.3.40__tar.gz → 0.3.42__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.40/src/gemcode.egg-info → gemcode-0.3.42}/PKG-INFO +1 -1
  2. {gemcode-0.3.40 → gemcode-0.3.42}/pyproject.toml +1 -1
  3. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/agent.py +1 -1
  4. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/intent_classifier.py +42 -17
  5. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/pricing.py +1 -0
  6. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/tui/scrollback.py +14 -12
  7. {gemcode-0.3.40 → gemcode-0.3.42/src/gemcode.egg-info}/PKG-INFO +1 -1
  8. {gemcode-0.3.40 → gemcode-0.3.42}/LICENSE +0 -0
  9. {gemcode-0.3.40 → gemcode-0.3.42}/MANIFEST.in +0 -0
  10. {gemcode-0.3.40 → gemcode-0.3.42}/README.md +0 -0
  11. {gemcode-0.3.40 → gemcode-0.3.42}/setup.cfg +0 -0
  12. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/__init__.py +0 -0
  13. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/__main__.py +0 -0
  14. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/audit.py +0 -0
  15. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/autocompact.py +0 -0
  16. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/callbacks.py +0 -0
  17. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/capability_routing.py +0 -0
  18. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/cli.py +0 -0
  19. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/compaction.py +0 -0
  20. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/computer_use/__init__.py +0 -0
  21. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/computer_use/browser_computer.py +0 -0
  22. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/config.py +0 -0
  23. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/context_budget.py +0 -0
  24. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/context_warning.py +0 -0
  25. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/credentials.py +0 -0
  26. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/hitl_session.py +0 -0
  27. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/hooks.py +0 -0
  28. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/interactions.py +0 -0
  29. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/invoke.py +0 -0
  30. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/kairos_daemon.py +0 -0
  31. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/limits.py +0 -0
  32. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/live_audio_engine.py +0 -0
  33. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/logging_config.py +0 -0
  34. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/mcp_loader.py +0 -0
  35. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/memory/__init__.py +0 -0
  36. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/memory/embedding_memory_service.py +0 -0
  37. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/memory/file_memory_service.py +0 -0
  38. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/modality_tools.py +0 -0
  39. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/model_errors.py +0 -0
  40. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/model_routing.py +0 -0
  41. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/openapi_loader.py +0 -0
  42. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/paths.py +0 -0
  43. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/permissions.py +0 -0
  44. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/plugins/__init__.py +0 -0
  45. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/plugins/terminal_hooks_plugin.py +0 -0
  46. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/plugins/tool_recovery_plugin.py +0 -0
  47. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/prompt_suggestions.py +0 -0
  48. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/query/__init__.py +0 -0
  49. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/query/config.py +0 -0
  50. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/query/deps.py +0 -0
  51. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/query/engine.py +0 -0
  52. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/query/stop_hooks.py +0 -0
  53. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/query/token_budget.py +0 -0
  54. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/query/transitions.py +0 -0
  55. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/refine.py +0 -0
  56. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/repl_commands.py +0 -0
  57. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/repl_slash.py +0 -0
  58. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/review_agent.py +0 -0
  59. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/session_runtime.py +0 -0
  60. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/session_store.py +0 -0
  61. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/slash_commands.py +0 -0
  62. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/thinking.py +0 -0
  63. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/tool_prompt_manifest.py +0 -0
  64. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/tool_registry.py +0 -0
  65. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/tools/__init__.py +0 -0
  66. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/tools/bash.py +0 -0
  67. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/tools/browser.py +0 -0
  68. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/tools/edit.py +0 -0
  69. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/tools/filesystem.py +0 -0
  70. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/tools/notes.py +0 -0
  71. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/tools/search.py +0 -0
  72. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/tools/shell.py +0 -0
  73. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/tools/shell_gate.py +0 -0
  74. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/tools/subtask.py +0 -0
  75. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/tools/think.py +0 -0
  76. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/tools/todo.py +0 -0
  77. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/tools/web.py +0 -0
  78. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/tools_inspector.py +0 -0
  79. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/trust.py +0 -0
  80. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/tui/input_handler.py +0 -0
  81. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/tui/spinner.py +0 -0
  82. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/tui/welcome_banner.py +0 -0
  83. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/tui/welcome_rich.py +0 -0
  84. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/version.py +0 -0
  85. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/vertex.py +0 -0
  86. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/web/__init__.py +0 -0
  87. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/web/claude_sse_adapter.py +0 -0
  88. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/web/terminal_repl.py +0 -0
  89. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode/workspace_hints.py +0 -0
  90. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode.egg-info/SOURCES.txt +0 -0
  91. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode.egg-info/dependency_links.txt +0 -0
  92. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode.egg-info/entry_points.txt +0 -0
  93. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode.egg-info/requires.txt +0 -0
  94. {gemcode-0.3.40 → gemcode-0.3.42}/src/gemcode.egg-info/top_level.txt +0 -0
  95. {gemcode-0.3.40 → gemcode-0.3.42}/tests/test_agent_instruction.py +0 -0
  96. {gemcode-0.3.40 → gemcode-0.3.42}/tests/test_autocompact.py +0 -0
  97. {gemcode-0.3.40 → gemcode-0.3.42}/tests/test_capability_routing.py +0 -0
  98. {gemcode-0.3.40 → gemcode-0.3.42}/tests/test_claude_web_adapter_sse.py +0 -0
  99. {gemcode-0.3.40 → gemcode-0.3.42}/tests/test_cli_init.py +0 -0
  100. {gemcode-0.3.40 → gemcode-0.3.42}/tests/test_computer_use_permissions.py +0 -0
  101. {gemcode-0.3.40 → gemcode-0.3.42}/tests/test_context_budget.py +0 -0
  102. {gemcode-0.3.40 → gemcode-0.3.42}/tests/test_context_warning.py +0 -0
  103. {gemcode-0.3.40 → gemcode-0.3.42}/tests/test_credentials.py +0 -0
  104. {gemcode-0.3.40 → gemcode-0.3.42}/tests/test_interactive_permission_ask.py +0 -0
  105. {gemcode-0.3.40 → gemcode-0.3.42}/tests/test_kairos_scheduler.py +0 -0
  106. {gemcode-0.3.40 → gemcode-0.3.42}/tests/test_modality_tools.py +0 -0
  107. {gemcode-0.3.40 → gemcode-0.3.42}/tests/test_model_error_retry.py +0 -0
  108. {gemcode-0.3.40 → gemcode-0.3.42}/tests/test_model_errors.py +0 -0
  109. {gemcode-0.3.40 → gemcode-0.3.42}/tests/test_model_routing.py +0 -0
  110. {gemcode-0.3.40 → gemcode-0.3.42}/tests/test_paths.py +0 -0
  111. {gemcode-0.3.40 → gemcode-0.3.42}/tests/test_permissions.py +0 -0
  112. {gemcode-0.3.40 → gemcode-0.3.42}/tests/test_prompt_suggestions.py +0 -0
  113. {gemcode-0.3.40 → gemcode-0.3.42}/tests/test_repl_commands.py +0 -0
  114. {gemcode-0.3.40 → gemcode-0.3.42}/tests/test_repl_slash.py +0 -0
  115. {gemcode-0.3.40 → gemcode-0.3.42}/tests/test_slash_commands.py +0 -0
  116. {gemcode-0.3.40 → gemcode-0.3.42}/tests/test_thinking_config.py +0 -0
  117. {gemcode-0.3.40 → gemcode-0.3.42}/tests/test_token_budget.py +0 -0
  118. {gemcode-0.3.40 → gemcode-0.3.42}/tests/test_tool_context_circulation.py +0 -0
  119. {gemcode-0.3.40 → gemcode-0.3.42}/tests/test_tools.py +0 -0
  120. {gemcode-0.3.40 → gemcode-0.3.42}/tests/test_tools_inspector.py +0 -0
  121. {gemcode-0.3.40 → gemcode-0.3.42}/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.40
3
+ Version: 0.3.42
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.40"
7
+ version = "0.3.42"
8
8
  description = "Local-first coding agent on Google Gemini + ADK"
9
9
  readme = "README.md"
10
10
  requires-python = ">=3.11"
@@ -400,7 +400,7 @@ You run locally via the GemCode CLI. You are the same agent the user launched
400
400
 
401
401
  ### Intent-aware workflow
402
402
 
403
- Before you see this message, a lightweight LLM classifier already determined the user's intent
403
+ Before you see this message, a **gemini-2.5-flash-lite** intent classifier already determined the user's intent
404
404
  and stored it in session state as `_gemcode_intent`. Read it and adapt your behaviour:
405
405
 
406
406
  | `_gemcode_intent` | Meaning | What to do |
@@ -1,7 +1,7 @@
1
1
  """
2
2
  LLM-based intent pre-classifier.
3
3
 
4
- Before each user turn a lightweight Gemini call (gemini-2.0-flash-lite by
4
+ Before each user turn a lightweight Gemini call (gemini-2.5-flash-lite by
5
5
  default) classifies the message into one of five intents. The TUI / CLI
6
6
  can then:
7
7
 
@@ -10,7 +10,7 @@ can then:
10
10
  workflow automatically without re-classifying
11
11
 
12
12
  Configure:
13
- GEMCODE_INTENT_MODEL Override the classifier model (default: gemini-2.0-flash-lite)
13
+ GEMCODE_INTENT_MODEL Override the classifier model (default: gemini-2.5-flash-lite)
14
14
  GEMCODE_INTENT_CLASSIFY_ENABLED Set "0" to disable (falls back to main agent)
15
15
  """
16
16
  from __future__ import annotations
@@ -41,6 +41,20 @@ INTENT_DESCRIPTIONS: dict[str, str] = {
41
41
  INTENT_ANALYSIS: "Systematic audit or summarisation — thorough tool sweep, then synthesise.",
42
42
  }
43
43
 
44
+ # How the intent was determined (for TUI suffix)
45
+ SOURCE_LOCAL = "local" # obvious greeting / heuristic, no classifier API call
46
+ SOURCE_LLM = "llm" # gemini-2.5-flash-lite classifier
47
+ SOURCE_OFF = "off" # classifier disabled
48
+
49
+
50
+ def show_intentifying_line(source: str) -> bool:
51
+ """
52
+ Whether to show the minimal ``∴ Intentifying…`` line in the TUI (same lane
53
+ as ``∴ Thinking``). Hidden only when intent classification is disabled
54
+ (``SOURCE_OFF``).
55
+ """
56
+ return source != SOURCE_OFF
57
+
44
58
  # ── Prompts ───────────────────────────────────────────────────────────────────
45
59
  _CLASSIFY_PROMPT = """\
46
60
  Classify the user message into exactly ONE intent label from the list below.
@@ -67,7 +81,7 @@ _GREETING_SYSTEM = (
67
81
  )
68
82
 
69
83
  _CLASSIFIER_MODEL_ENV = "GEMCODE_INTENT_MODEL"
70
- _DEFAULT_CLASSIFIER_MODEL = "gemini-2.0-flash-lite"
84
+ _DEFAULT_CLASSIFIER_MODEL = "gemini-2.5-flash-lite"
71
85
 
72
86
  # Single-word / very-short messages that are unambiguously greetings —
73
87
  # checked locally before spending an API call on the classifier.
@@ -98,27 +112,24 @@ def _get_api_key() -> str:
98
112
  )
99
113
 
100
114
 
101
- async def classify_intent(message: str) -> str:
115
+ async def classify_intent_with_source(message: str) -> tuple[str, str]:
102
116
  """
103
- Classify a user message using a lightweight Gemini call.
104
-
105
- Returns one of: GREETING, CONCEPT, PROJECT_QUESTION, ENGINEERING_TASK, ANALYSIS.
106
- Falls back to ENGINEERING_TASK on any error (the main agent handles all real
107
- tasks safely with that default).
117
+ Classify a user message; return (intent, source).
108
118
 
109
- Classification is disabled when GEMCODE_INTENT_CLASSIFY_ENABLED=0.
119
+ ``source`` is one of: ``SOURCE_LOCAL`` (heuristic, no API call),
120
+ ``SOURCE_LLM`` (classifier model), ``SOURCE_OFF`` (classifier disabled).
110
121
  """
111
122
  if not _classifier_enabled():
112
- return INTENT_ENGINEERING_TASK
123
+ return INTENT_ENGINEERING_TASK, SOURCE_OFF
113
124
 
114
125
  stripped = (message or "").strip()
115
126
  if not stripped:
116
- return INTENT_GREETING
127
+ return INTENT_GREETING, SOURCE_LOCAL
117
128
 
118
129
  # Fast local check for unambiguously short greetings — saves an API round-trip.
119
130
  lower = stripped.lower()
120
131
  if lower in _OBVIOUS_GREETINGS or (len(lower) <= 3 and lower.isalpha()):
121
- return INTENT_GREETING
132
+ return INTENT_GREETING, SOURCE_LOCAL
122
133
 
123
134
  try:
124
135
  import google.genai as genai
@@ -136,14 +147,28 @@ async def classify_intent(message: str) -> str:
136
147
  label = (resp.text or "").strip().upper()
137
148
  # Exact match first
138
149
  if label in _VALID_INTENTS:
139
- return label
150
+ return label, SOURCE_LLM
140
151
  # Partial match (model may return extra punctuation or lowercase)
141
152
  for key in _VALID_INTENTS:
142
153
  if key in label:
143
- return key
144
- return INTENT_ENGINEERING_TASK
154
+ return key, SOURCE_LLM
155
+ return INTENT_ENGINEERING_TASK, SOURCE_LLM
145
156
  except Exception:
146
- return INTENT_ENGINEERING_TASK
157
+ return INTENT_ENGINEERING_TASK, SOURCE_LLM
158
+
159
+
160
+ async def classify_intent(message: str) -> str:
161
+ """
162
+ Classify a user message using a lightweight Gemini call.
163
+
164
+ Returns one of: GREETING, CONCEPT, PROJECT_QUESTION, ENGINEERING_TASK, ANALYSIS.
165
+ Falls back to ENGINEERING_TASK on any error (the main agent handles all real
166
+ tasks safely with that default).
167
+
168
+ Classification is disabled when GEMCODE_INTENT_CLASSIFY_ENABLED=0.
169
+ """
170
+ intent, _ = await classify_intent_with_source(message)
171
+ return intent
147
172
 
148
173
 
149
174
  async def generate_greeting_reply(message: str) -> str:
@@ -16,6 +16,7 @@ from __future__ import annotations
16
16
  _PRICING_TABLE: dict[str, tuple[float, float]] = {
17
17
  # ── Gemini 2.5 ──────────────────────────────────────────────────────────
18
18
  "gemini-2.5-pro": (3.50, 10.50), # standard context ≤200k
19
+ "gemini-2.5-flash-lite": (0.10, 0.40), # lowest-cost 2.5 (must precede flash)
19
20
  "gemini-2.5-flash": (0.15, 0.60), # standard context
20
21
  "gemini-2.5-flash-8b": (0.037, 0.15), # 8B lite variant
21
22
  # ── Gemini 2.0 ──────────────────────────────────────────────────────────
@@ -516,28 +516,30 @@ async def run_gemcode_scrollback_tui(
516
516
  prompt = slash.model_prompt or prompt
517
517
 
518
518
  # ── LLM intent pre-classifier ────────────────────────────────────────────
519
- # A lightweight Gemini call (gemini-2.0-flash-lite) classifies the message
520
- # before the main agent runs. Greetings are short-circuited entirely:
521
- # we generate a natural reply directly and skip the main agent, so the
522
- # user gets an instant response with zero unnecessary tool calls.
523
- # For all other intents the classified label is stored in session state
524
- # so the main agent can adapt its workflow without re-classifying.
519
+ # gemini-2.5-flash-lite classifies the message (same lane as Thinking)
525
520
  try:
526
521
  from gemcode.intent_classifier import (
527
- classify_intent,
522
+ classify_intent_with_source,
528
523
  generate_greeting_reply,
524
+ show_intentifying_line,
529
525
  INTENT_GREETING,
530
526
  INTENT_DESCRIPTIONS,
531
527
  )
532
- _intent = await classify_intent(prompt)
528
+ _intent, _intent_src = await classify_intent_with_source(prompt)
529
+ if show_intentifying_line(_intent_src):
530
+ print(
531
+ f" \u23bf {ansi.dim}\u2234 Intentifying\u2026{ansi.reset}"
532
+ )
533
+ print("")
533
534
 
534
535
  if _intent == INTENT_GREETING:
535
- # Fast path: generate a warm reply with the lightweight model and skip
536
- # the main agent entirely — no tool calls, no spinner, instant UX.
537
- _reply = await generate_greeting_reply(prompt)
536
+ _start_anim("Replying\u2026")
537
+ try:
538
+ _reply = await generate_greeting_reply(prompt)
539
+ finally:
540
+ _stop_anim()
538
541
  print(f" \u23bf {ansi.bold}GemCode{ansi.reset}:")
539
542
  console.print(_RichPadding(_RichMarkdown(_reply), (0, 0, 0, 4)))
540
- # Print minimal footer (no token stats since we bypassed the main model)
541
543
  print("")
542
544
  if os.environ.get("GEMCODE_TUI_TURN_RULE", "1").lower() in (
543
545
  "1", "true", "yes", "on"
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: gemcode
3
- Version: 0.3.40
3
+ Version: 0.3.42
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