crabcode 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.
Files changed (64) hide show
  1. crabcode-0.1.0.dist-info/METADATA +23 -0
  2. crabcode-0.1.0.dist-info/RECORD +64 -0
  3. crabcode-0.1.0.dist-info/WHEEL +4 -0
  4. crabcode-0.1.0.dist-info/entry_points.txt +2 -0
  5. crabcode_cli/__init__.py +3 -0
  6. crabcode_cli/app.py +142 -0
  7. crabcode_cli/banner.py +42 -0
  8. crabcode_cli/pipe.py +58 -0
  9. crabcode_cli/render/__init__.py +0 -0
  10. crabcode_cli/render/messages.py +19 -0
  11. crabcode_cli/repl.py +1263 -0
  12. crabcode_core/__init__.py +3 -0
  13. crabcode_core/api/__init__.py +6 -0
  14. crabcode_core/api/anthropic_adapter.py +250 -0
  15. crabcode_core/api/base.py +66 -0
  16. crabcode_core/api/codex_adapter.py +390 -0
  17. crabcode_core/api/openai_adapter.py +221 -0
  18. crabcode_core/api/registry.py +40 -0
  19. crabcode_core/compact/__init__.py +7 -0
  20. crabcode_core/compact/compact.py +150 -0
  21. crabcode_core/config/__init__.py +3 -0
  22. crabcode_core/config/claudemd.py +53 -0
  23. crabcode_core/config/manager.py +152 -0
  24. crabcode_core/events.py +392 -0
  25. crabcode_core/mcp/__init__.py +4 -0
  26. crabcode_core/mcp/client.py +163 -0
  27. crabcode_core/mcp/config.py +52 -0
  28. crabcode_core/permissions/__init__.py +3 -0
  29. crabcode_core/permissions/manager.py +106 -0
  30. crabcode_core/prompts/__init__.py +0 -0
  31. crabcode_core/prompts/context.py +209 -0
  32. crabcode_core/prompts/profile.py +66 -0
  33. crabcode_core/prompts/system.py +405 -0
  34. crabcode_core/prompts/templates.py +60 -0
  35. crabcode_core/query/__init__.py +0 -0
  36. crabcode_core/query/loop.py +442 -0
  37. crabcode_core/session/__init__.py +3 -0
  38. crabcode_core/session/storage.py +153 -0
  39. crabcode_core/skills/__init__.py +5 -0
  40. crabcode_core/skills/loader.py +147 -0
  41. crabcode_core/tools/__init__.py +29 -0
  42. crabcode_core/tools/agent.py +160 -0
  43. crabcode_core/tools/bash.py +127 -0
  44. crabcode_core/tools/diff_utils.py +120 -0
  45. crabcode_core/tools/file_edit.py +145 -0
  46. crabcode_core/tools/file_read.py +123 -0
  47. crabcode_core/tools/file_write.py +102 -0
  48. crabcode_core/tools/glob.py +109 -0
  49. crabcode_core/tools/grep.py +130 -0
  50. crabcode_core/tools/lint.py +368 -0
  51. crabcode_core/tools/memory.py +236 -0
  52. crabcode_core/tools/skill.py +112 -0
  53. crabcode_core/types/__init__.py +73 -0
  54. crabcode_core/types/config.py +103 -0
  55. crabcode_core/types/event.py +105 -0
  56. crabcode_core/types/message.py +177 -0
  57. crabcode_core/types/tool.py +138 -0
  58. crabcode_search/__init__.py +11 -0
  59. crabcode_search/background.py +371 -0
  60. crabcode_search/chunker.py +517 -0
  61. crabcode_search/embedder.py +447 -0
  62. crabcode_search/indexer.py +212 -0
  63. crabcode_search/store.py +222 -0
  64. crabcode_search/tool.py +232 -0
@@ -0,0 +1,23 @@
1
+ Metadata-Version: 2.4
2
+ Name: crabcode
3
+ Version: 0.1.0
4
+ Summary: CrabCode - AI coding assistant in the terminal
5
+ Requires-Python: >=3.10
6
+ Requires-Dist: anthropic>=0.40.0
7
+ Requires-Dist: httpx>=0.27.0
8
+ Requires-Dist: mcp>=1.0.0
9
+ Requires-Dist: openai>=1.50.0
10
+ Requires-Dist: prompt-toolkit>=3.0.0
11
+ Requires-Dist: pydantic>=2.0.0
12
+ Requires-Dist: rich>=13.0.0
13
+ Requires-Dist: tiktoken>=0.7.0
14
+ Requires-Dist: typer>=0.12.0
15
+ Provides-Extra: bedrock
16
+ Requires-Dist: anthropic[bedrock]>=0.40.0; extra == 'bedrock'
17
+ Provides-Extra: search
18
+ Requires-Dist: modelscope>=1.10.0; extra == 'search'
19
+ Requires-Dist: sentence-transformers>=2.0.0; extra == 'search'
20
+ Requires-Dist: tree-sitter-language-pack>=1.0.0; extra == 'search'
21
+ Requires-Dist: usearch>=2.12.0; extra == 'search'
22
+ Provides-Extra: vertex
23
+ Requires-Dist: anthropic[vertex]>=0.40.0; extra == 'vertex'
@@ -0,0 +1,64 @@
1
+ crabcode_cli/__init__.py,sha256=EmiIl6vNhF_COIsCBw4ND_rmu8EFVVGvWoMh1uIVenA,63
2
+ crabcode_cli/app.py,sha256=kYmRDq3RUFxHU5FGMEqa6K5DzCn6Vd1FHmaC1R9HWEw,5212
3
+ crabcode_cli/banner.py,sha256=K0ak6Ng4l85C3TWnh64ulvdF4ReJSRsaWe8DOjse3Zg,1403
4
+ crabcode_cli/pipe.py,sha256=oAF_dPsXOOhJveV5AzuyH7QukBqDhx3m6i7qFMfvlLw,1810
5
+ crabcode_cli/repl.py,sha256=cVGkbdHTmFnpLUzAuZ9j7-3kutv5BshdhOxneXGp-7o,45532
6
+ crabcode_cli/render/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
7
+ crabcode_cli/render/messages.py,sha256=n9OsbLIAU3FyO9gwGMMJld7zq-71N0caz9ecmE2kIjo,568
8
+ crabcode_core/__init__.py,sha256=-MK4aDYG9K0Ndpfd03Fit34RBWzvNQgQpeYk8zqBHAg,73
9
+ crabcode_core/events.py,sha256=3CjajpVUlL3Aye48QVAyduUpNWlaUkH_LWUv-6Cf_aI,15148
10
+ crabcode_core/api/__init__.py,sha256=66G1xF1GKYMmfpmbyE9PigFx-_1Me3vIteI-1Xw8iAE,260
11
+ crabcode_core/api/anthropic_adapter.py,sha256=ir3iaXMyuZX2bz6sas7xz3JFNTmtUdFqJk_XAStk9cI,9077
12
+ crabcode_core/api/base.py,sha256=BZin6-FQo3OIugocA8Y3Hd7z0B3_gXwC9SXoQnoh2dk,1864
13
+ crabcode_core/api/codex_adapter.py,sha256=kollddHCR_1_Lz_NVSUCn5eGHHjj5csbeJqosuP8TC0,14780
14
+ crabcode_core/api/openai_adapter.py,sha256=DnIoQF9L0Qg9yXt9hfwS6F148B69QmmJucvabGkIL7o,7464
15
+ crabcode_core/api/registry.py,sha256=EAZxKTIv2VNHWsgbMTb95V0xf15ct36PIRJj4E9ZYcE,1566
16
+ crabcode_core/compact/__init__.py,sha256=P5C6D0MilT3FAnmaAxc8NeMi2CZLEMB-CwEzKUGqSlY,206
17
+ crabcode_core/compact/compact.py,sha256=mA_6gfZvMq6U-WvEKr8tsu2jnDy9psYBRbkvbqy_oPA,4514
18
+ crabcode_core/config/__init__.py,sha256=wbSnNVCqj673phu2pvIFz-f5HM5bLvH-FdD9b01HNRA,84
19
+ crabcode_core/config/claudemd.py,sha256=cg1Uzg_n5UWN5m528c59VbYkqYp4tb8kfLHWGqjFGVY,1778
20
+ crabcode_core/config/manager.py,sha256=BkjJcU5JUfYCQJF3aTU5nOOET0TwieIv-Db_yabiUL4,4837
21
+ crabcode_core/mcp/__init__.py,sha256=axvOgOWHjpKs7WFEkf8Za_4mm_oNe0tYyD94dAMnWTE,182
22
+ crabcode_core/mcp/client.py,sha256=Rmm1Twgruv34WkEXBfcn7ofeltCyP5ykzwAir6D-6Ng,4885
23
+ crabcode_core/mcp/config.py,sha256=z0iz-8Lgt2M7Ko9r6yMue_T1_AcE-37ldcd8kCvQkRM,1714
24
+ crabcode_core/permissions/__init__.py,sha256=YfNm9B3NPQnVxDvRukkW787XbOfJBh3ck47lwyblQgk,131
25
+ crabcode_core/permissions/manager.py,sha256=U6Tw0QpZVmYBtXzGcHAy110ge9U-_7Jdjt3-t7rhxOk,3540
26
+ crabcode_core/prompts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
27
+ crabcode_core/prompts/context.py,sha256=skhtywNIfFQcdiTBEF5QU-6zixW08DfjqXHiU2OCCKU,6652
28
+ crabcode_core/prompts/profile.py,sha256=MWxdrC0vVc6QfaRa5sgKEC8Gi_s61F9ZaxjoCTiiEPc,2088
29
+ crabcode_core/prompts/system.py,sha256=88ePx8eYaKsHOUbjlmLRuJH488n61QjVneDY_fAAt6Y,27416
30
+ crabcode_core/prompts/templates.py,sha256=yJiQDphi8ilx0nrS7TW8ysxmiWukcmek-S5unAdvqQQ,2128
31
+ crabcode_core/query/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
32
+ crabcode_core/query/loop.py,sha256=HbmJv4VXkfhUm7xNdAI9A0wmz_nD5bU1EQnuhLWdYgo,15352
33
+ crabcode_core/session/__init__.py,sha256=NdWh1WvqPExjzwOdj9sBT5nUGD-Us_GiBNKucjoIr3k,131
34
+ crabcode_core/session/storage.py,sha256=wKqEx4USsNnxoeZV4aA9oJNBdUagryQqK6fhVpSe5sc,5101
35
+ crabcode_core/skills/__init__.py,sha256=BXXoMXXwBUOmkbEBHCSHbHazOa6XzvhdCUkBckrMuEA,174
36
+ crabcode_core/skills/loader.py,sha256=IhU7Gm8zCbQyPmUFWVbgYeJZuxdp8i1qfSfxsyF2qDc,4718
37
+ crabcode_core/tools/__init__.py,sha256=_B1pQrmyiexctwpDpZt2nJlBNe0rCh7LVwiknZBy1_4,914
38
+ crabcode_core/tools/agent.py,sha256=mxQn_GgPYv-wMQEzvktdc3e71_OahSCnYl6heslUshA,5915
39
+ crabcode_core/tools/bash.py,sha256=jEwbCg9lWuU3u3gOwZ8DyD0gcjUAM8llIx0zQpBppKE,4695
40
+ crabcode_core/tools/diff_utils.py,sha256=IYuEe53zyvb1FAEIU0vcsEPQGCz1TZCyekMhJC5r0AE,3480
41
+ crabcode_core/tools/file_edit.py,sha256=s0Y95EOEN2_1VKnGZHSbOyYODo7rsqQtY5EYKNJC2ww,5091
42
+ crabcode_core/tools/file_read.py,sha256=D5ausRFj9wRCI7E28KWZ07XtPjoN4Jp7MnEBzQTOwFY,3990
43
+ crabcode_core/tools/file_write.py,sha256=hCgiSuFAlfGsUgZXLjYUJxgv85zuDmj04qvDtidVTx0,3528
44
+ crabcode_core/tools/glob.py,sha256=tSoKOgNSX-hmq0ftQRTD7KzfQBHH56lJgc2XJqpuhnA,3472
45
+ crabcode_core/tools/grep.py,sha256=f8t2zaggB4hBwODQW-mnEVl9LFIBvC2ADIza294yFUo,4249
46
+ crabcode_core/tools/lint.py,sha256=GueRqdh3_qFsQRLFZbOpTFn-tTME8YRrvbe5mZ4wWDw,12514
47
+ crabcode_core/tools/memory.py,sha256=SR3FQ6zhRkTvOqGaIYL5fnorMmsLHRwRDDqzM_uukLc,8690
48
+ crabcode_core/tools/skill.py,sha256=g0NlvHLlCCeJStgNBvRwTBEUeygcKjvHtG8hOVJOnW8,3980
49
+ crabcode_core/types/__init__.py,sha256=h-uuoClhnMDxPKiahMIB9KXo4y2giYO-WNdB_LSXsh0,1446
50
+ crabcode_core/types/config.py,sha256=WLBf2mcY8BjvcyE4_CVXl9HyvWYfDC15S9imjG5jnIE,3606
51
+ crabcode_core/types/event.py,sha256=Fu-JvcmkpNp5IGTYKnif3fZMVR0zREpeLARE7nfoccU,2181
52
+ crabcode_core/types/message.py,sha256=wg5urxbhDMcjBwzE-2VqpJCaxzIr_cZh1UsT7KlrBI0,4593
53
+ crabcode_core/types/tool.py,sha256=QOTXf9h0GkG5Dk5Cx58OCxOHK_xnIEgkIOLeRqWl8DU,4132
54
+ crabcode_search/__init__.py,sha256=Fw3-cySOi83Y6WSduXUTC62ANXA5lf7PX3kSnQoXEUs,272
55
+ crabcode_search/background.py,sha256=urkJ6RV-aQvCRlaQr2n08S7NiY2sCDEjC-vOZw4vRl0,11098
56
+ crabcode_search/chunker.py,sha256=gtpMWAjNBEAmr9cfhU5uOcuXZFDhEpNRubVbttaxT14,16148
57
+ crabcode_search/embedder.py,sha256=suPEUgzfSfy4YwFmhHVisB0ii_7GLIdOQ4T6EI1D230,14569
58
+ crabcode_search/indexer.py,sha256=wNHrzB9oW8eRcKek6ZtXJdRB_HgFLj_1LhqqtG_tiDw,7522
59
+ crabcode_search/store.py,sha256=7E7fkQ25CgX0LlIp6wrgQNkVS9epUaqLpfoDA_V-BWg,8002
60
+ crabcode_search/tool.py,sha256=s3Tn8zUOYKAjbAL_HE18nwzX9zxLttvyy9Lc-v7nb5o,8828
61
+ crabcode-0.1.0.dist-info/METADATA,sha256=dDLzEjUtB0lQnpEdJI83Yx35E3ERpuwHvke0YjbEXd4,831
62
+ crabcode-0.1.0.dist-info/WHEEL,sha256=QccIxa26bgl1E6uMy58deGWi-0aeIkkangHcxk2kWfw,87
63
+ crabcode-0.1.0.dist-info/entry_points.txt,sha256=tpYKME-yDLFb2eqwryWJG-d-HW4dkvDbzwogRl2jays,52
64
+ crabcode-0.1.0.dist-info/RECORD,,
@@ -0,0 +1,4 @@
1
+ Wheel-Version: 1.0
2
+ Generator: hatchling 1.29.0
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ crabcode = crabcode_cli.app:entry
@@ -0,0 +1,3 @@
1
+ """CrabCode CLI - terminal frontend."""
2
+
3
+ __version__ = "0.1.0"
crabcode_cli/app.py ADDED
@@ -0,0 +1,142 @@
1
+ """CrabCode CLI entry point."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import os
7
+ import sys
8
+ from typing import Optional
9
+
10
+ import typer
11
+
12
+ app = typer.Typer(
13
+ name="crabcode",
14
+ help="CrabCode — AI coding assistant in the terminal",
15
+ add_completion=False,
16
+ )
17
+
18
+
19
+ @app.command()
20
+ def main(
21
+ prompt: Optional[str] = typer.Argument(None, help="Prompt to send (pipe mode)"),
22
+ pipe: bool = typer.Option(False, "-p", "--pipe", help="Run in pipe mode (non-interactive)"),
23
+ model: Optional[str] = typer.Option(None, "-m", "--model", help="Model to use"),
24
+ provider: Optional[str] = typer.Option(None, "--provider", help="API provider (anthropic/openai/codex/router)"),
25
+ base_url: Optional[str] = typer.Option(None, "--base-url", help="API base URL (for routers)"),
26
+ api_format: Optional[str] = typer.Option(None, "--api-format", help="API format for router (anthropic/openai)"),
27
+ model_profile: Optional[str] = typer.Option(None, "--model-profile", "-M", help="Use a named model from settings.models"),
28
+ cwd: Optional[str] = typer.Option(None, "--cwd", help="Working directory"),
29
+ resume: Optional[str] = typer.Option(None, "-r", "--resume", help="Resume a session by ID"),
30
+ continue_last: bool = typer.Option(False, "-c", "--continue", help="Continue most recent session"),
31
+ ) -> None:
32
+ """CrabCode — AI coding assistant in the terminal."""
33
+ from crabcode_core.types.config import ApiConfig, CrabCodeSettings
34
+
35
+ work_dir = cwd or os.getcwd()
36
+
37
+ settings = CrabCodeSettings()
38
+
39
+ from crabcode_core.config.manager import ConfigManager
40
+ file_settings = ConfigManager(cwd=work_dir).load()
41
+ for key, val in file_settings.env.items():
42
+ os.environ.setdefault(key, val)
43
+ if file_settings.api.provider:
44
+ settings.api.provider = file_settings.api.provider
45
+ if file_settings.api.model:
46
+ settings.api.model = file_settings.api.model
47
+ if file_settings.api.base_url:
48
+ settings.api.base_url = file_settings.api.base_url
49
+ if file_settings.api.api_key_env:
50
+ settings.api.api_key_env = file_settings.api.api_key_env
51
+ if file_settings.api.format:
52
+ settings.api.format = file_settings.api.format
53
+ if not file_settings.api.thinking_enabled:
54
+ settings.api.thinking_enabled = file_settings.api.thinking_enabled
55
+ if file_settings.api.max_tokens != 16384:
56
+ settings.api.max_tokens = file_settings.api.max_tokens
57
+
58
+ if file_settings.models:
59
+ settings.models = {**file_settings.models, **settings.models}
60
+ if file_settings.default_model and not settings.default_model:
61
+ settings.default_model = file_settings.default_model
62
+
63
+ if file_settings.permissions.run_everything:
64
+ settings.permissions.run_everything = True
65
+
66
+ if model:
67
+ settings.api.model = model
68
+ if provider:
69
+ settings.api.provider = provider
70
+ if base_url:
71
+ settings.api.base_url = base_url
72
+ if api_format:
73
+ settings.api.format = api_format
74
+ if model_profile:
75
+ if model_profile in file_settings.models:
76
+ settings.models = file_settings.models
77
+ settings.default_model = model_profile
78
+ else:
79
+ typer.echo(
80
+ f"Warning: model profile '{model_profile}' not found in settings.models. "
81
+ f"Available: {list(file_settings.models.keys()) or '(none configured)'}",
82
+ err=True,
83
+ )
84
+
85
+ stdin_text = ""
86
+ if not sys.stdin.isatty():
87
+ stdin_text = sys.stdin.read().strip()
88
+
89
+ if pipe or stdin_text:
90
+ text = prompt or stdin_text
91
+ if not text:
92
+ typer.echo("Error: no prompt provided", err=True)
93
+ raise typer.Exit(1)
94
+
95
+ from crabcode_cli.pipe import run_pipe
96
+ asyncio.run(run_pipe(text, settings=settings, cwd=work_dir))
97
+ return
98
+
99
+ if prompt:
100
+ from crabcode_cli.pipe import run_pipe
101
+ asyncio.run(run_pipe(prompt, settings=settings, cwd=work_dir))
102
+ return
103
+
104
+ resume_id: str | None = None
105
+ if resume:
106
+ resume_id = resume
107
+ elif continue_last:
108
+ from crabcode_core.session.storage import SessionStorage
109
+ sessions = SessionStorage.list_sessions(work_dir)
110
+ if sessions:
111
+ resume_id = sessions[0]["session_id"]
112
+ else:
113
+ typer.echo("No previous sessions found.", err=True)
114
+
115
+ if file_settings.extra_tools:
116
+ try:
117
+ from crabcode_search.background import (
118
+ is_codebase_search_enabled,
119
+ maybe_spawn_background_indexer,
120
+ )
121
+
122
+ if is_codebase_search_enabled(file_settings.extra_tools):
123
+ maybe_spawn_background_indexer(
124
+ cwd=work_dir,
125
+ tool_config=file_settings.tool_settings.get("CodebaseSearch", {}),
126
+ )
127
+ except Exception:
128
+ pass
129
+
130
+ from crabcode_cli.repl import run_repl
131
+ asyncio.run(run_repl(settings=settings, cwd=work_dir, resume_session_id=resume_id))
132
+ # Native library threads (PyTorch, FAISS) may keep the process alive
133
+ # after the asyncio loop shuts down; force-exit to avoid hanging.
134
+ os._exit(0)
135
+
136
+
137
+ def entry() -> None:
138
+ app()
139
+
140
+
141
+ if __name__ == "__main__":
142
+ entry()
crabcode_cli/banner.py ADDED
@@ -0,0 +1,42 @@
1
+ """CrabCode ASCII banner."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from rich.console import Console
6
+ from rich.table import Table
7
+ from rich.text import Text
8
+
9
+ # ASCII crab art — hand-crafted
10
+ _CRAB = (
11
+ " ▐▛██▜▌ ▐▛██▜▌ \n"
12
+ " ▝▜██████▛▘ ▝▜██████▛▘ \n"
13
+ " ▐██████████▌ \n"
14
+ " ▐████████████▌ \n"
15
+ " ▐████████████▌ \n"
16
+ " ▝▜████████▛▘ \n"
17
+ " ▐▛██▛▘ ▝▜██▜▌ \n"
18
+ " ▐▛▘ ▝▜▌ \n"
19
+ "▝▘ ▝▘ \n"
20
+ " "
21
+ )
22
+
23
+ # "CrabCode" in figlet slant font
24
+ _CRABCODE = r""" ______ __ ______ __
25
+ / ____/________ _/ /_ / ____/___ ____/ /__
26
+ / / / ___/ __ `/ __ \/ / / __ \/ __ / _ \
27
+ / /___/ / / /_/ / /_/ / /___/ /_/ / /_/ / __/
28
+ \____/_/ \__,_/_.___/\____/\____/\__,_/\___/ """
29
+
30
+
31
+ def print_banner(console: Console) -> None:
32
+ """Print the CrabCode banner with ASCII crab art on the left."""
33
+ grid = Table.grid(padding=(0, 3))
34
+ grid.add_column(no_wrap=True)
35
+ grid.add_column(no_wrap=True)
36
+ grid.add_row(
37
+ Text(_CRAB, style="bold red"),
38
+ Text(_CRABCODE, style="bold cyan"),
39
+ )
40
+ console.print()
41
+ console.print(grid)
42
+ console.print()
crabcode_cli/pipe.py ADDED
@@ -0,0 +1,58 @@
1
+ """Pipe mode — reads from stdin, sends to Core, writes to stdout."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import asyncio
6
+ import sys
7
+
8
+ from crabcode_core.events import CoreSession
9
+ from crabcode_core.types.config import CrabCodeSettings
10
+ from crabcode_core.types.event import (
11
+ ErrorEvent,
12
+ PermissionRequestEvent,
13
+ PermissionResponseEvent,
14
+ StreamTextEvent,
15
+ ThinkingEvent,
16
+ ToolResultEvent,
17
+ ToolUseEvent,
18
+ TurnCompleteEvent,
19
+ )
20
+
21
+
22
+ async def run_pipe(
23
+ prompt: str,
24
+ settings: CrabCodeSettings | None = None,
25
+ cwd: str = ".",
26
+ ) -> None:
27
+ """Run a single prompt through the core and print the response."""
28
+ session = CoreSession(cwd=cwd, settings=settings)
29
+
30
+ async for event in session.send_message(prompt):
31
+ if isinstance(event, StreamTextEvent):
32
+ sys.stdout.write(event.text)
33
+ sys.stdout.flush()
34
+ elif isinstance(event, ThinkingEvent):
35
+ pass
36
+ elif isinstance(event, ToolUseEvent):
37
+ sys.stderr.write(f"\n[Tool: {event.tool_name}]\n")
38
+ sys.stderr.flush()
39
+ elif isinstance(event, PermissionRequestEvent):
40
+ await session.respond_permission(
41
+ PermissionResponseEvent(
42
+ tool_use_id=event.tool_use_id, allowed=True
43
+ )
44
+ )
45
+ elif isinstance(event, ToolResultEvent):
46
+ if event.is_error:
47
+ sys.stderr.write(f"\n[Error: {event.result}]\n")
48
+ sys.stderr.flush()
49
+ elif isinstance(event, ErrorEvent):
50
+ sys.stderr.write(f"\nError: {event.message}\n")
51
+ sys.stderr.flush()
52
+ if not event.recoverable:
53
+ sys.exit(1)
54
+ elif isinstance(event, TurnCompleteEvent):
55
+ pass
56
+
57
+ sys.stdout.write("\n")
58
+ sys.stdout.flush()
File without changes
@@ -0,0 +1,19 @@
1
+ """Message rendering utilities for the CLI."""
2
+
3
+ from __future__ import annotations
4
+
5
+ from rich.console import Console
6
+ from rich.markdown import Markdown
7
+ from rich.panel import Panel
8
+
9
+
10
+ def render_assistant_markdown(text: str, console: Console | None = None) -> None:
11
+ """Render assistant text as Markdown."""
12
+ c = console or Console()
13
+ c.print(Markdown(text))
14
+
15
+
16
+ def render_error(message: str, console: Console | None = None) -> None:
17
+ """Render an error message."""
18
+ c = console or Console()
19
+ c.print(Panel(message, title="Error", border_style="red"))