webbee 0.1.19__tar.gz → 0.2.0__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 (73) hide show
  1. {webbee-0.1.19 → webbee-0.2.0}/CHANGELOG.md +10 -0
  2. {webbee-0.1.19 → webbee-0.2.0}/PKG-INFO +1 -1
  3. {webbee-0.1.19 → webbee-0.2.0}/pyproject.toml +1 -1
  4. webbee-0.2.0/src/webbee/__init__.py +1 -0
  5. {webbee-0.1.19 → webbee-0.2.0}/src/webbee/cli.py +6 -1
  6. {webbee-0.1.19 → webbee-0.2.0}/src/webbee/render.py +3 -0
  7. {webbee-0.1.19 → webbee-0.2.0}/src/webbee/repl.py +2 -2
  8. {webbee-0.1.19 → webbee-0.2.0}/tests/test_repl.py +22 -6
  9. webbee-0.1.19/src/webbee/__init__.py +0 -1
  10. {webbee-0.1.19 → webbee-0.2.0}/.github/workflows/publish.yml +0 -0
  11. {webbee-0.1.19 → webbee-0.2.0}/.gitignore +0 -0
  12. {webbee-0.1.19 → webbee-0.2.0}/LICENSE +0 -0
  13. {webbee-0.1.19 → webbee-0.2.0}/README.md +0 -0
  14. {webbee-0.1.19 → webbee-0.2.0}/install.sh +0 -0
  15. {webbee-0.1.19 → webbee-0.2.0}/src/webbee/account.py +0 -0
  16. {webbee-0.1.19 → webbee-0.2.0}/src/webbee/banner_art.py +0 -0
  17. {webbee-0.1.19 → webbee-0.2.0}/src/webbee/clipboard.py +0 -0
  18. {webbee-0.1.19 → webbee-0.2.0}/src/webbee/commands.py +0 -0
  19. {webbee-0.1.19 → webbee-0.2.0}/src/webbee/config.py +0 -0
  20. {webbee-0.1.19 → webbee-0.2.0}/src/webbee/details.py +0 -0
  21. {webbee-0.1.19 → webbee-0.2.0}/src/webbee/events.py +0 -0
  22. {webbee-0.1.19 → webbee-0.2.0}/src/webbee/frames.py +0 -0
  23. {webbee-0.1.19 → webbee-0.2.0}/src/webbee/intel/__init__.py +0 -0
  24. {webbee-0.1.19 → webbee-0.2.0}/src/webbee/intel/chunker.py +0 -0
  25. {webbee-0.1.19 → webbee-0.2.0}/src/webbee/intel/embed.py +0 -0
  26. {webbee-0.1.19 → webbee-0.2.0}/src/webbee/intel/graph.py +0 -0
  27. {webbee-0.1.19 → webbee-0.2.0}/src/webbee/intel/indexer.py +0 -0
  28. {webbee-0.1.19 → webbee-0.2.0}/src/webbee/intel/models.py +0 -0
  29. {webbee-0.1.19 → webbee-0.2.0}/src/webbee/intel/query.py +0 -0
  30. {webbee-0.1.19 → webbee-0.2.0}/src/webbee/intel/service.py +0 -0
  31. {webbee-0.1.19 → webbee-0.2.0}/src/webbee/intel/store.py +0 -0
  32. {webbee-0.1.19 → webbee-0.2.0}/src/webbee/intel/vectors.py +0 -0
  33. {webbee-0.1.19 → webbee-0.2.0}/src/webbee/intel/watch.py +0 -0
  34. {webbee-0.1.19 → webbee-0.2.0}/src/webbee/output_pane.py +0 -0
  35. {webbee-0.1.19 → webbee-0.2.0}/src/webbee/repo.py +0 -0
  36. {webbee-0.1.19 → webbee-0.2.0}/src/webbee/session.py +0 -0
  37. {webbee-0.1.19 → webbee-0.2.0}/src/webbee/sessions.py +0 -0
  38. {webbee-0.1.19 → webbee-0.2.0}/src/webbee/stream.py +0 -0
  39. {webbee-0.1.19 → webbee-0.2.0}/src/webbee/tools.py +0 -0
  40. {webbee-0.1.19 → webbee-0.2.0}/src/webbee/tui.py +0 -0
  41. {webbee-0.1.19 → webbee-0.2.0}/src/webbee/update.py +0 -0
  42. {webbee-0.1.19 → webbee-0.2.0}/tests/__init__.py +0 -0
  43. {webbee-0.1.19 → webbee-0.2.0}/tests/test_account.py +0 -0
  44. {webbee-0.1.19 → webbee-0.2.0}/tests/test_cli.py +0 -0
  45. {webbee-0.1.19 → webbee-0.2.0}/tests/test_clipboard.py +0 -0
  46. {webbee-0.1.19 → webbee-0.2.0}/tests/test_commands.py +0 -0
  47. {webbee-0.1.19 → webbee-0.2.0}/tests/test_config.py +0 -0
  48. {webbee-0.1.19 → webbee-0.2.0}/tests/test_cpc_contract_stable.py +0 -0
  49. {webbee-0.1.19 → webbee-0.2.0}/tests/test_details.py +0 -0
  50. {webbee-0.1.19 → webbee-0.2.0}/tests/test_events.py +0 -0
  51. {webbee-0.1.19 → webbee-0.2.0}/tests/test_freeze_fix.py +0 -0
  52. {webbee-0.1.19 → webbee-0.2.0}/tests/test_intel_chunker.py +0 -0
  53. {webbee-0.1.19 → webbee-0.2.0}/tests/test_intel_embed.py +0 -0
  54. {webbee-0.1.19 → webbee-0.2.0}/tests/test_intel_graph.py +0 -0
  55. {webbee-0.1.19 → webbee-0.2.0}/tests/test_intel_indexer.py +0 -0
  56. {webbee-0.1.19 → webbee-0.2.0}/tests/test_intel_no_numpy.py +0 -0
  57. {webbee-0.1.19 → webbee-0.2.0}/tests/test_intel_query.py +0 -0
  58. {webbee-0.1.19 → webbee-0.2.0}/tests/test_intel_service.py +0 -0
  59. {webbee-0.1.19 → webbee-0.2.0}/tests/test_intel_store.py +0 -0
  60. {webbee-0.1.19 → webbee-0.2.0}/tests/test_intel_vectors.py +0 -0
  61. {webbee-0.1.19 → webbee-0.2.0}/tests/test_intel_watch.py +0 -0
  62. {webbee-0.1.19 → webbee-0.2.0}/tests/test_marathon.py +0 -0
  63. {webbee-0.1.19 → webbee-0.2.0}/tests/test_packaging.py +0 -0
  64. {webbee-0.1.19 → webbee-0.2.0}/tests/test_render.py +0 -0
  65. {webbee-0.1.19 → webbee-0.2.0}/tests/test_repo.py +0 -0
  66. {webbee-0.1.19 → webbee-0.2.0}/tests/test_session.py +0 -0
  67. {webbee-0.1.19 → webbee-0.2.0}/tests/test_sessions.py +0 -0
  68. {webbee-0.1.19 → webbee-0.2.0}/tests/test_stream.py +0 -0
  69. {webbee-0.1.19 → webbee-0.2.0}/tests/test_tools.py +0 -0
  70. {webbee-0.1.19 → webbee-0.2.0}/tests/test_tui.py +0 -0
  71. {webbee-0.1.19 → webbee-0.2.0}/tests/test_tui_hardening.py +0 -0
  72. {webbee-0.1.19 → webbee-0.2.0}/tests/test_update.py +0 -0
  73. {webbee-0.1.19 → webbee-0.2.0}/tests/test_version.py +0 -0
@@ -1,5 +1,15 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.2.0
4
+
5
+ - **Marathon is now the default.** A task you type runs to completion on its own
6
+ — it plans, edits, runs, and verifies across as many steps as it needs, and
7
+ only stops when the goal is done, you stop it, or your credits run low (it
8
+ pauses, it doesn't fail — top up and it resumes). No more fixed per-run step
9
+ limit on the default path.
10
+ - **`--once` (alias `--no-marathon`)** runs a single bounded coding turn instead,
11
+ for a quick one-shot.
12
+
3
13
  ## 0.1.14
4
14
 
5
15
  - **Esc/Ctrl-C now actually stops the turn — server-side, not just locally.**
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: webbee
3
- Version: 0.1.19
3
+ Version: 0.2.0
4
4
  Summary: Webbee 🐝 — the Imperal Cloud coding agent in your terminal
5
5
  Project-URL: Homepage, https://imperal.io
6
6
  Project-URL: Documentation, https://docs.imperal.io
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "webbee"
3
- version = "0.1.19"
3
+ version = "0.2.0"
4
4
  description = "Webbee 🐝 — the Imperal Cloud coding agent in your terminal"
5
5
  readme = "README.md"
6
6
  license = "MIT"
@@ -0,0 +1 @@
1
+ __version__ = "0.2.0"
@@ -15,6 +15,11 @@ def build_parser() -> argparse.ArgumentParser:
15
15
  help="Launch a long-horizon marathon toward GOAL (runs autonomously "
16
16
  "until the goal is met, verified by the project's own test command).",
17
17
  )
18
+ p.add_argument(
19
+ "--once", "--no-marathon", dest="once", action="store_true",
20
+ help="Run a single bounded coding turn (stops at the step limit) instead "
21
+ "of the default self-driving marathon.",
22
+ )
18
23
  sub = p.add_subparsers(dest="cmd")
19
24
  sub.add_parser("login", help="Log in to your Imperal account in the browser")
20
25
  sub.add_parser("logout", help="Log out and remove local credentials")
@@ -63,7 +68,7 @@ def main(argv=None) -> None:
63
68
  from webbee.repl import run_repl
64
69
  try:
65
70
  _maybe_print_update_notice()
66
- asyncio.run(run_repl(cfg, args.mode))
71
+ asyncio.run(run_repl(cfg, args.mode, once=args.once))
67
72
  except KeyboardInterrupt:
68
73
  # Ctrl-C during the update-check fetch, or at the read_line() prompt,
69
74
  # unwinds here — exit clean, no traceback. (repl.py itself now cancels
@@ -140,6 +140,9 @@ class RichSink:
140
140
  for label, value in rows:
141
141
  self.console.print(Text.assemble((pad + label.ljust(14), "dim"), (value, "white")))
142
142
  self.console.print()
143
+ tip = "Type a task and Webbee runs it to completion · pauses if credits run low · --once = single turn"
144
+ self.console.print(Text(tip.center(w), style="dim"))
145
+ self.console.print()
143
146
  self.console.print(Text("/help · Ctrl-D to exit".center(w), style="dim"))
144
147
  self.console.print()
145
148
  self._nudge()
@@ -91,7 +91,7 @@ def _default_intel_factory(cfg, workspace: str):
91
91
  return IntelService(root, compute_repo_key(root), cache_dir=cfg.cache_dir)
92
92
 
93
93
 
94
- async def run_repl(cfg, mode: str = "default", *, sink=None, read_line=input,
94
+ async def run_repl(cfg, mode: str = "default", *, once: bool = False, sink=None, read_line=input,
95
95
  agent_factory=None, auth=None, account_fetcher=None,
96
96
  sessions_client=None, intel_factory=None) -> None:
97
97
  """Interactive coding REPL. Production (a real tty, no injected sink) runs
@@ -226,7 +226,7 @@ async def run_repl(cfg, mode: str = "default", *, sink=None, read_line=input,
226
226
  _sink.user_echo(line)
227
227
  _sink.begin_turn()
228
228
  try:
229
- text = await agent.run(line, _sink)
229
+ text = await agent.run(line, _sink, marathon=not once, goal=(line if not once else ""))
230
230
  except (KeyboardInterrupt, asyncio.CancelledError):
231
231
  _sink.abort()
232
232
  _sink.note("Interrupted.")
@@ -30,9 +30,10 @@ class FakeSink:
30
30
 
31
31
 
32
32
  class FakeAgent:
33
- def __init__(self): self.tasks = []; self.mode = "default"
34
- async def run(self, task, sink):
33
+ def __init__(self): self.tasks = []; self.mode = "default"; self.runs = []
34
+ async def run(self, task, sink, *, marathon=False, goal=""):
35
35
  self.tasks.append(task)
36
+ self.runs.append({"task": task, "marathon": marathon, "goal": goal})
36
37
  return f"answer:{task}"
37
38
 
38
39
 
@@ -95,7 +96,8 @@ def _run(**kw):
95
96
  cfg = Config(api_url="http://x", panel_url="http://p")
96
97
  sink = kw.pop("sink", FakeSink())
97
98
  agent = kw.pop("agent", FakeAgent())
98
- asyncio.run(run_repl(cfg, "default", sink=sink, agent_factory=lambda c, tp, ws, m: agent,
99
+ once = kw.pop("once", False)
100
+ asyncio.run(run_repl(cfg, "default", once=once, sink=sink, agent_factory=lambda c, tp, ws, m: agent,
99
101
  read_line=kw.pop("read_line"), auth=kw.pop("auth", FakeAuth()),
100
102
  account_fetcher=kw.pop("account_fetcher", _fake_account_fetcher),
101
103
  sessions_client=kw.pop("sessions_client", FakeSessions()),
@@ -109,6 +111,20 @@ def test_task_is_sent_to_agent_and_answer_rendered():
109
111
  assert sink.turns == ["answer:исправь баг"]
110
112
 
111
113
 
114
+ def test_interactive_default_is_marathon():
115
+ # Marathon is the default: a typed task self-drives (marathon=True) with the
116
+ # task carried as the goal.
117
+ sink, agent = _run(read_line=_lines("build a thing", "/exit"))
118
+ assert agent.runs and agent.runs[0]["marathon"] is True
119
+ assert agent.runs[0]["goal"] == "build a thing"
120
+
121
+
122
+ def test_once_flag_uses_bounded_coding():
123
+ # --once opts back into the bounded, non-marathon coding turn.
124
+ sink, agent = _run(read_line=_lines("build a thing", "/exit"), once=True)
125
+ assert agent.runs and agent.runs[0]["marathon"] is False
126
+
127
+
112
128
  def test_exit_command_stops_loop():
113
129
  sink, agent = _run(read_line=_lines("/exit"))
114
130
  assert agent.tasks == []
@@ -133,7 +149,7 @@ def test_logout_command_calls_auth():
133
149
 
134
150
  def test_agent_error_is_swallowed_and_loop_continues():
135
151
  class RaisingAgent(FakeAgent):
136
- async def run(self, task, sink):
152
+ async def run(self, task, sink, *, marathon=False, goal=""):
137
153
  self.tasks.append(task)
138
154
  raise RuntimeError("boom")
139
155
 
@@ -204,7 +220,7 @@ def test_clear_command_clears_sink():
204
220
 
205
221
  def test_ctrl_c_mid_turn_aborts_and_returns_to_prompt():
206
222
  class InterruptingAgent(FakeAgent):
207
- async def run(self, task, sink):
223
+ async def run(self, task, sink, *, marathon=False, goal=""):
208
224
  self.tasks.append(task)
209
225
  raise KeyboardInterrupt
210
226
 
@@ -309,7 +325,7 @@ class _SpyAgent:
309
325
  self.mode = mode
310
326
  self.steps = []
311
327
 
312
- async def run(self, task, sink):
328
+ async def run(self, task, sink, *, marathon=False, goal=""):
313
329
  return f"answer:{task}"
314
330
 
315
331
  async def stop(self): ...
@@ -1 +0,0 @@
1
- __version__ = "0.1.19"
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
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
File without changes
File without changes
File without changes
File without changes
File without changes