zrb 1.15.3__py3-none-any.whl → 2.0.0a4__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.

Potentially problematic release.


This version of zrb might be problematic. Click here for more details.

Files changed (204) hide show
  1. zrb/__init__.py +118 -133
  2. zrb/attr/type.py +10 -7
  3. zrb/builtin/__init__.py +55 -1
  4. zrb/builtin/git.py +12 -1
  5. zrb/builtin/group.py +31 -15
  6. zrb/builtin/llm/chat.py +147 -0
  7. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/entity/add_entity_util.py +7 -7
  8. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/add_module_util.py +5 -5
  9. zrb/builtin/project/add/fastapp/fastapp_util.py +1 -1
  10. zrb/builtin/searxng/config/settings.yml +5671 -0
  11. zrb/builtin/searxng/start.py +21 -0
  12. zrb/builtin/shell/autocomplete/bash.py +4 -3
  13. zrb/builtin/shell/autocomplete/zsh.py +4 -3
  14. zrb/callback/callback.py +8 -1
  15. zrb/cmd/cmd_result.py +2 -1
  16. zrb/config/config.py +555 -169
  17. zrb/config/helper.py +84 -0
  18. zrb/config/web_auth_config.py +50 -35
  19. zrb/context/any_shared_context.py +20 -3
  20. zrb/context/context.py +39 -5
  21. zrb/context/print_fn.py +13 -0
  22. zrb/context/shared_context.py +17 -8
  23. zrb/group/any_group.py +3 -3
  24. zrb/group/group.py +3 -3
  25. zrb/input/any_input.py +5 -1
  26. zrb/input/base_input.py +18 -6
  27. zrb/input/option_input.py +41 -1
  28. zrb/input/text_input.py +7 -24
  29. zrb/llm/agent/__init__.py +9 -0
  30. zrb/llm/agent/agent.py +215 -0
  31. zrb/llm/agent/summarizer.py +20 -0
  32. zrb/llm/app/__init__.py +10 -0
  33. zrb/llm/app/completion.py +281 -0
  34. zrb/llm/app/confirmation/allow_tool.py +66 -0
  35. zrb/llm/app/confirmation/handler.py +178 -0
  36. zrb/llm/app/confirmation/replace_confirmation.py +77 -0
  37. zrb/llm/app/keybinding.py +34 -0
  38. zrb/llm/app/layout.py +117 -0
  39. zrb/llm/app/lexer.py +155 -0
  40. zrb/llm/app/redirection.py +28 -0
  41. zrb/llm/app/style.py +16 -0
  42. zrb/llm/app/ui.py +733 -0
  43. zrb/llm/config/__init__.py +4 -0
  44. zrb/llm/config/config.py +122 -0
  45. zrb/llm/config/limiter.py +247 -0
  46. zrb/llm/history_manager/__init__.py +4 -0
  47. zrb/llm/history_manager/any_history_manager.py +23 -0
  48. zrb/llm/history_manager/file_history_manager.py +91 -0
  49. zrb/llm/history_processor/summarizer.py +108 -0
  50. zrb/llm/note/__init__.py +3 -0
  51. zrb/llm/note/manager.py +122 -0
  52. zrb/llm/prompt/__init__.py +29 -0
  53. zrb/llm/prompt/claude_compatibility.py +92 -0
  54. zrb/llm/prompt/compose.py +55 -0
  55. zrb/llm/prompt/default.py +51 -0
  56. zrb/llm/prompt/markdown/file_extractor.md +112 -0
  57. zrb/llm/prompt/markdown/mandate.md +23 -0
  58. zrb/llm/prompt/markdown/persona.md +3 -0
  59. zrb/llm/prompt/markdown/repo_extractor.md +112 -0
  60. zrb/llm/prompt/markdown/repo_summarizer.md +29 -0
  61. zrb/llm/prompt/markdown/summarizer.md +21 -0
  62. zrb/llm/prompt/note.py +41 -0
  63. zrb/llm/prompt/system_context.py +46 -0
  64. zrb/llm/prompt/zrb.py +41 -0
  65. zrb/llm/skill/__init__.py +3 -0
  66. zrb/llm/skill/manager.py +86 -0
  67. zrb/llm/task/__init__.py +4 -0
  68. zrb/llm/task/llm_chat_task.py +316 -0
  69. zrb/llm/task/llm_task.py +245 -0
  70. zrb/llm/tool/__init__.py +39 -0
  71. zrb/llm/tool/bash.py +75 -0
  72. zrb/llm/tool/code.py +266 -0
  73. zrb/llm/tool/file.py +419 -0
  74. zrb/llm/tool/note.py +70 -0
  75. zrb/{builtin/llm → llm}/tool/rag.py +33 -37
  76. zrb/llm/tool/search/brave.py +53 -0
  77. zrb/llm/tool/search/searxng.py +47 -0
  78. zrb/llm/tool/search/serpapi.py +47 -0
  79. zrb/llm/tool/skill.py +19 -0
  80. zrb/llm/tool/sub_agent.py +70 -0
  81. zrb/llm/tool/web.py +97 -0
  82. zrb/llm/tool/zrb_task.py +66 -0
  83. zrb/llm/util/attachment.py +101 -0
  84. zrb/llm/util/prompt.py +104 -0
  85. zrb/llm/util/stream_response.py +178 -0
  86. zrb/runner/cli.py +21 -20
  87. zrb/runner/common_util.py +24 -19
  88. zrb/runner/web_route/task_input_api_route.py +5 -5
  89. zrb/runner/web_util/user.py +7 -3
  90. zrb/session/any_session.py +12 -9
  91. zrb/session/session.py +38 -17
  92. zrb/task/any_task.py +24 -3
  93. zrb/task/base/context.py +42 -22
  94. zrb/task/base/execution.py +67 -55
  95. zrb/task/base/lifecycle.py +14 -7
  96. zrb/task/base/monitoring.py +12 -7
  97. zrb/task/base_task.py +113 -50
  98. zrb/task/base_trigger.py +16 -6
  99. zrb/task/cmd_task.py +6 -0
  100. zrb/task/http_check.py +11 -5
  101. zrb/task/make_task.py +5 -3
  102. zrb/task/rsync_task.py +30 -10
  103. zrb/task/scaffolder.py +7 -4
  104. zrb/task/scheduler.py +7 -4
  105. zrb/task/tcp_check.py +6 -4
  106. zrb/util/ascii_art/art/bee.txt +17 -0
  107. zrb/util/ascii_art/art/cat.txt +9 -0
  108. zrb/util/ascii_art/art/ghost.txt +16 -0
  109. zrb/util/ascii_art/art/panda.txt +17 -0
  110. zrb/util/ascii_art/art/rose.txt +14 -0
  111. zrb/util/ascii_art/art/unicorn.txt +15 -0
  112. zrb/util/ascii_art/banner.py +92 -0
  113. zrb/util/attr.py +54 -39
  114. zrb/util/cli/markdown.py +32 -0
  115. zrb/util/cli/text.py +30 -0
  116. zrb/util/cmd/command.py +33 -10
  117. zrb/util/file.py +61 -33
  118. zrb/util/git.py +2 -2
  119. zrb/util/{llm/prompt.py → markdown.py} +2 -3
  120. zrb/util/match.py +78 -0
  121. zrb/util/run.py +3 -3
  122. zrb/util/string/conversion.py +1 -1
  123. zrb/util/truncate.py +23 -0
  124. zrb/util/yaml.py +204 -0
  125. zrb/xcom/xcom.py +10 -0
  126. {zrb-1.15.3.dist-info → zrb-2.0.0a4.dist-info}/METADATA +41 -27
  127. {zrb-1.15.3.dist-info → zrb-2.0.0a4.dist-info}/RECORD +129 -131
  128. {zrb-1.15.3.dist-info → zrb-2.0.0a4.dist-info}/WHEEL +1 -1
  129. zrb/attr/__init__.py +0 -0
  130. zrb/builtin/llm/chat_session.py +0 -311
  131. zrb/builtin/llm/history.py +0 -71
  132. zrb/builtin/llm/input.py +0 -27
  133. zrb/builtin/llm/llm_ask.py +0 -187
  134. zrb/builtin/llm/previous-session.js +0 -21
  135. zrb/builtin/llm/tool/__init__.py +0 -0
  136. zrb/builtin/llm/tool/api.py +0 -71
  137. zrb/builtin/llm/tool/cli.py +0 -38
  138. zrb/builtin/llm/tool/code.py +0 -254
  139. zrb/builtin/llm/tool/file.py +0 -626
  140. zrb/builtin/llm/tool/sub_agent.py +0 -137
  141. zrb/builtin/llm/tool/web.py +0 -195
  142. zrb/builtin/project/__init__.py +0 -0
  143. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/__init__.py +0 -0
  144. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/_zrb/module/template/app_template/module/my_module/service/__init__.py +0 -0
  145. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/common/__init__.py +0 -0
  146. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/__init__.py +0 -0
  147. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/__init__.py +0 -0
  148. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/permission/__init__.py +0 -0
  149. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/role/__init__.py +0 -0
  150. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/module/auth/service/user/__init__.py +0 -0
  151. zrb/builtin/project/add/fastapp/fastapp_template/my_app_name/schema/__init__.py +0 -0
  152. zrb/builtin/project/create/__init__.py +0 -0
  153. zrb/builtin/shell/__init__.py +0 -0
  154. zrb/builtin/shell/autocomplete/__init__.py +0 -0
  155. zrb/callback/__init__.py +0 -0
  156. zrb/cmd/__init__.py +0 -0
  157. zrb/config/default_prompt/file_extractor_system_prompt.md +0 -12
  158. zrb/config/default_prompt/interactive_system_prompt.md +0 -35
  159. zrb/config/default_prompt/persona.md +0 -1
  160. zrb/config/default_prompt/repo_extractor_system_prompt.md +0 -112
  161. zrb/config/default_prompt/repo_summarizer_system_prompt.md +0 -10
  162. zrb/config/default_prompt/summarization_prompt.md +0 -16
  163. zrb/config/default_prompt/system_prompt.md +0 -32
  164. zrb/config/llm_config.py +0 -243
  165. zrb/config/llm_context/config.py +0 -129
  166. zrb/config/llm_context/config_parser.py +0 -46
  167. zrb/config/llm_rate_limitter.py +0 -137
  168. zrb/content_transformer/__init__.py +0 -0
  169. zrb/context/__init__.py +0 -0
  170. zrb/dot_dict/__init__.py +0 -0
  171. zrb/env/__init__.py +0 -0
  172. zrb/group/__init__.py +0 -0
  173. zrb/input/__init__.py +0 -0
  174. zrb/runner/__init__.py +0 -0
  175. zrb/runner/web_route/__init__.py +0 -0
  176. zrb/runner/web_route/home_page/__init__.py +0 -0
  177. zrb/session/__init__.py +0 -0
  178. zrb/session_state_log/__init__.py +0 -0
  179. zrb/session_state_logger/__init__.py +0 -0
  180. zrb/task/__init__.py +0 -0
  181. zrb/task/base/__init__.py +0 -0
  182. zrb/task/llm/__init__.py +0 -0
  183. zrb/task/llm/agent.py +0 -243
  184. zrb/task/llm/config.py +0 -103
  185. zrb/task/llm/conversation_history.py +0 -128
  186. zrb/task/llm/conversation_history_model.py +0 -242
  187. zrb/task/llm/default_workflow/coding.md +0 -24
  188. zrb/task/llm/default_workflow/copywriting.md +0 -17
  189. zrb/task/llm/default_workflow/researching.md +0 -18
  190. zrb/task/llm/error.py +0 -95
  191. zrb/task/llm/history_summarization.py +0 -216
  192. zrb/task/llm/print_node.py +0 -101
  193. zrb/task/llm/prompt.py +0 -325
  194. zrb/task/llm/tool_wrapper.py +0 -220
  195. zrb/task/llm/typing.py +0 -3
  196. zrb/task/llm_task.py +0 -341
  197. zrb/task_status/__init__.py +0 -0
  198. zrb/util/__init__.py +0 -0
  199. zrb/util/cli/__init__.py +0 -0
  200. zrb/util/cmd/__init__.py +0 -0
  201. zrb/util/codemod/__init__.py +0 -0
  202. zrb/util/string/__init__.py +0 -0
  203. zrb/xcom/__init__.py +0 -0
  204. {zrb-1.15.3.dist-info → zrb-2.0.0a4.dist-info}/entry_points.txt +0 -0
@@ -0,0 +1,122 @@
1
+ import json
2
+ import os
3
+ from typing import Dict
4
+
5
+ from zrb.config.config import CFG
6
+
7
+
8
+ class NoteManager:
9
+ def __init__(self, context_file: str = CFG.LLM_NOTE_FILE):
10
+ self.context_file = os.path.abspath(os.path.expanduser(context_file))
11
+
12
+ def _get_normalized_path(self, path: str) -> str:
13
+ """
14
+ Convert path to absolute path, then try to make it relative to home.
15
+ If it's inside home, return '~/rel/path'.
16
+ Otherwise return absolute path.
17
+ """
18
+ abs_path = os.path.abspath(os.path.expanduser(path))
19
+ home = os.path.expanduser("~")
20
+
21
+ if abs_path.startswith(home):
22
+ rel_path = os.path.relpath(abs_path, home)
23
+ if rel_path == ".":
24
+ return "~"
25
+ return os.path.join("~", rel_path)
26
+
27
+ return abs_path
28
+
29
+ def _load_data(self) -> Dict[str, str]:
30
+ if not os.path.exists(self.context_file):
31
+ return {}
32
+ try:
33
+ with open(self.context_file, "r", encoding="utf-8") as f:
34
+ return json.load(f)
35
+ except (json.JSONDecodeError, OSError):
36
+ return {}
37
+
38
+ def _save_data(self, data: Dict[str, str]):
39
+ os.makedirs(os.path.dirname(self.context_file), exist_ok=True)
40
+ with open(self.context_file, "w", encoding="utf-8") as f:
41
+ json.dump(data, f, indent=2, sort_keys=True)
42
+
43
+ def write(self, context_path: str, content: str):
44
+ """
45
+ Write content to context file for a specific path.
46
+ Turn context path to be relative to home directory (unless it is outside home directory).
47
+ """
48
+ key = self._get_normalized_path(context_path)
49
+ data = self._load_data()
50
+ data[key] = content
51
+ self._save_data(data)
52
+
53
+ def read(self, context_path: str) -> str:
54
+ """
55
+ Seek for any key in context file that match context path.
56
+ """
57
+ key = self._get_normalized_path(context_path)
58
+ data = self._load_data()
59
+ return data.get(key, "")
60
+
61
+ def read_all(self, context_path: str) -> Dict[str, str]:
62
+ """
63
+ Seek for all key in context file that match context path or any of its parent, return as key-value.
64
+ """
65
+ target_path = self._get_normalized_path(context_path)
66
+ data = self._load_data()
67
+
68
+ result = {}
69
+
70
+ # Check for root/home notes (~)
71
+ if "~" in data:
72
+ result["~"] = data["~"]
73
+
74
+ # If target_path is just "~", we are done
75
+ if target_path == "~":
76
+ return result
77
+
78
+ # We need to find all parents of target_path that are in data
79
+ # Example target: ~/zrb/src
80
+ # Parents: ~, ~/zrb
81
+
82
+ # Split the path parts
83
+ # If path starts with "~", we treat it specially
84
+
85
+ parts = []
86
+ if target_path.startswith("~" + os.sep) or target_path == "~":
87
+ # Remove ~
88
+ rel_part = target_path[2:] # "zrb/src"
89
+ parts = rel_part.split(os.sep)
90
+ current = "~"
91
+ else:
92
+ # Absolute path like /opt/project
93
+ parts = target_path.split(os.sep)
94
+ # parts for /opt/project -> ['', 'opt', 'project']
95
+ current = parts[0] # '' (root)
96
+ if current == "":
97
+ current = "/"
98
+ parts = parts[1:]
99
+
100
+ # Iterate and build up path
101
+ for part in parts:
102
+ if current == "~":
103
+ current = os.path.join("~", part)
104
+ elif current == "/":
105
+ current = os.path.join("/", part)
106
+ else:
107
+ current = os.path.join(current, part)
108
+
109
+ # Normalize just in case os.path.join changes things
110
+ # But wait, our keys in JSON are stored with separators.
111
+ # We should probably reconstruct the key carefully.
112
+
113
+ # Let's simplify:
114
+ # Check if 'current' exists in data
115
+ if current in data:
116
+ result[current] = data[current]
117
+
118
+ # Finally check the target_path itself if not covered
119
+ if target_path in data and target_path not in result:
120
+ result[target_path] = data[target_path]
121
+
122
+ return result
@@ -0,0 +1,29 @@
1
+ from zrb.llm.prompt.claude_compatibility import create_claude_compatibility_prompt
2
+ from zrb.llm.prompt.compose import PromptManager, PromptMiddleware, new_prompt
3
+ from zrb.llm.prompt.default import (
4
+ get_file_extractor_system_prompt,
5
+ get_mandate_prompt,
6
+ get_persona_prompt,
7
+ get_repo_extractor_system_prompt,
8
+ get_repo_summarizer_system_prompt,
9
+ get_summarizer_system_prompt,
10
+ )
11
+ from zrb.llm.prompt.note import create_note_prompt
12
+ from zrb.llm.prompt.system_context import system_context
13
+ from zrb.llm.prompt.zrb import create_zrb_prompt
14
+
15
+ __all__ = [
16
+ "create_claude_compatibility_prompt",
17
+ "PromptManager",
18
+ "PromptMiddleware",
19
+ "new_prompt",
20
+ "get_file_extractor_system_prompt",
21
+ "get_mandate_prompt",
22
+ "get_persona_prompt",
23
+ "get_repo_extractor_system_prompt",
24
+ "get_repo_summarizer_system_prompt",
25
+ "get_summarizer_system_prompt",
26
+ "create_note_prompt",
27
+ "system_context",
28
+ "create_zrb_prompt",
29
+ ]
@@ -0,0 +1,92 @@
1
+ from pathlib import Path
2
+ from typing import Callable, List, Optional
3
+
4
+ from zrb.context.any_context import AnyContext
5
+ from zrb.llm.skill.manager import SkillManager
6
+ from zrb.util.markdown import make_markdown_section
7
+
8
+
9
+ def create_claude_compatibility_prompt(skill_manager: SkillManager):
10
+ def claude_compatibility(
11
+ ctx: AnyContext,
12
+ current_prompt: str,
13
+ next_handler: Callable[[AnyContext, str], str],
14
+ ) -> str:
15
+ search_dirs = _get_search_directories()
16
+ additional_context = []
17
+
18
+ # 1. CLAUDE.md
19
+ claude_content = _get_combined_content("CLAUDE.md", search_dirs)
20
+ if claude_content:
21
+ additional_context.append(
22
+ make_markdown_section(
23
+ "Project Instructions (CLAUDE.md)", claude_content
24
+ )
25
+ )
26
+
27
+ # 2. AGENTS.md
28
+ agents_content = _get_combined_content("AGENTS.md", search_dirs)
29
+ if agents_content:
30
+ additional_context.append(
31
+ make_markdown_section("Agent Definitions (AGENTS.md)", agents_content)
32
+ )
33
+
34
+ # 3. Available Claude Skills
35
+ skills_section = _get_skills_section(skill_manager)
36
+ if skills_section:
37
+ additional_context.append(skills_section)
38
+
39
+ new_section = "\n\n".join(additional_context)
40
+ return next_handler(ctx, f"{current_prompt}\n\n{new_section}")
41
+
42
+ return claude_compatibility
43
+
44
+
45
+ def _get_search_directories() -> List[Path]:
46
+ search_dirs: List[Path] = []
47
+ # 1. User global config (~/.claude)
48
+ try:
49
+ home = Path.home()
50
+ search_dirs.append(home / ".claude")
51
+ except Exception:
52
+ pass
53
+
54
+ # 2. Project directories (Root -> ... -> CWD)
55
+ try:
56
+ cwd = Path.cwd()
57
+ # Parents returns [parent, grandparent...]. We want reversed (Root first)
58
+ # This allows specific configs (closer to CWD) to override general ones
59
+ project_dirs = list(cwd.parents)[::-1] + [cwd]
60
+ search_dirs.extend(project_dirs)
61
+ except Exception:
62
+ pass
63
+ return search_dirs
64
+
65
+
66
+ def _get_combined_content(filename: str, search_dirs: List[Path]) -> str:
67
+ contents = []
68
+ for directory in search_dirs:
69
+ file_path = directory / filename
70
+ if file_path.exists() and file_path.is_file():
71
+ try:
72
+ with open(file_path, "r", encoding="utf-8") as f:
73
+ content = f.read().strip()
74
+ if content:
75
+ contents.append(content)
76
+ except Exception:
77
+ pass
78
+ return "\n\n".join(contents)
79
+
80
+
81
+ def _get_skills_section(skill_manager: SkillManager) -> Optional[str]:
82
+ skills = skill_manager.scan()
83
+ if not skills:
84
+ return None
85
+
86
+ skills_context = ["Use 'activate_skill' to load instructions for a skill."]
87
+ for skill in skills:
88
+ skills_context.append(f"- {skill.name}")
89
+
90
+ return make_markdown_section(
91
+ "Available Skills (Claude Skills)", "\n".join(skills_context)
92
+ )
@@ -0,0 +1,55 @@
1
+ from typing import Callable
2
+
3
+ from zrb.context.any_context import AnyContext
4
+ from zrb.util.attr import get_str_attr
5
+
6
+ PromptMiddleware = Callable[[AnyContext, str, Callable[[AnyContext, str], str]], str]
7
+
8
+
9
+ class PromptManager:
10
+ def __init__(self, middlewares: list[PromptMiddleware] = []):
11
+ self._middlewares = middlewares
12
+
13
+ def add_middleware(self, *middleware: PromptMiddleware):
14
+ self.append_middlewear(*middleware)
15
+
16
+ def append_middlewear(self, *middleware: PromptMiddleware):
17
+ self._middlewares += middleware
18
+
19
+ def compose_prompt(self) -> Callable[[AnyContext], str]:
20
+ """
21
+ Composes a list of prompt middlewares into a single prompt factory function.
22
+
23
+ Each middleware should have the signature:
24
+ (ctx: AnyContext, prompt: str, next: Callable[[AnyContext, str], str]) -> str
25
+
26
+ The resulting function takes an AnyContext and returns the final prompt string.
27
+ """
28
+
29
+ def composed_prompt_factory(ctx: AnyContext) -> str:
30
+ def dispatch(index: int, current_prompt: str) -> str:
31
+ if index >= len(self._middlewares):
32
+ return current_prompt
33
+
34
+ middleware = self._middlewares[index]
35
+
36
+ def next_handler(c: AnyContext, p: str) -> str:
37
+ return dispatch(index + 1, p)
38
+
39
+ return middleware(ctx, current_prompt, next_handler)
40
+
41
+ return dispatch(0, "")
42
+
43
+ return composed_prompt_factory
44
+
45
+
46
+ def new_prompt(new_prompt: str, render: bool = False):
47
+ def new_prompt_middleware(
48
+ ctx: AnyContext, current_prompt: str, next: Callable[[AnyContext, str], str]
49
+ ):
50
+ effective_new_prompt = new_prompt
51
+ if render:
52
+ effective_new_prompt = get_str_attr(ctx, new_prompt, auto_render=True)
53
+ return next(ctx, f"{current_prompt}\n{effective_new_prompt}")
54
+
55
+ return new_prompt_middleware
@@ -0,0 +1,51 @@
1
+ import os
2
+
3
+ from zrb.config.config import CFG
4
+
5
+
6
+ def get_default_prompt(name: str) -> str:
7
+ # 1. Check for local project override (configured via LLM_PROMPT_DIR)
8
+ prompt_dir = getattr(CFG, "LLM_PROMPT_DIR", ".zrb/llm/prompt")
9
+ local_prompt_path = os.path.abspath(
10
+ os.path.join(os.getcwd(), prompt_dir, f"{name}.md")
11
+ )
12
+
13
+ if os.path.exists(local_prompt_path):
14
+ try:
15
+ with open(local_prompt_path, "r", encoding="utf-8") as f:
16
+ return f.read()
17
+ except Exception:
18
+ pass
19
+
20
+ # 2. Fallback to package default
21
+ cwd = os.path.dirname(__file__)
22
+ with open(os.path.join(cwd, "markdown", f"{name}.md"), "r", encoding="utf-8") as f:
23
+ return f.read()
24
+
25
+
26
+ def get_persona_prompt(assistant_name: str | None = None) -> str:
27
+ effective_assistant_name = (
28
+ assistant_name if assistant_name else CFG.LLM_ASSISTANT_NAME
29
+ )
30
+ prompt = get_default_prompt("persona")
31
+ return prompt.replace("{ASSISTANT_NAME}", effective_assistant_name)
32
+
33
+
34
+ def get_mandate_prompt() -> str:
35
+ return get_default_prompt("mandate")
36
+
37
+
38
+ def get_summarizer_system_prompt() -> str:
39
+ return get_default_prompt("summarizer")
40
+
41
+
42
+ def get_file_extractor_system_prompt() -> str:
43
+ return get_default_prompt("file_extractor")
44
+
45
+
46
+ def get_repo_extractor_system_prompt() -> str:
47
+ return get_default_prompt("repo_extractor")
48
+
49
+
50
+ def get_repo_summarizer_system_prompt() -> str:
51
+ return get_default_prompt("repo_summarizer")
@@ -0,0 +1,112 @@
1
+ You are an expert code and configuration analysis agent. Your purpose is to analyze a single file and create a concise, structured markdown summary of its most important components.
2
+
3
+ ### Instructions
4
+
5
+ 1. **Analyze File Content**: Determine the file's type (e.g., Python, Dockerfile, YAML, Markdown).
6
+ 2. **Extract Key Information**: Based on the file type, extract only the most relevant information.
7
+ * **Source Code** (`.py`, `.js`, `.go`): Extract classes, functions, key variables, and their purpose.
8
+ * **Configuration** (`.yaml`, `.toml`, `.json`): Extract main sections, keys, and values.
9
+ * **Infrastructure** (`Dockerfile`, `.tf`): Extract resources, settings, and commands.
10
+ * **Documentation** (`.md`): Extract headings, summaries, and code blocks.
11
+ 3. **Format Output**: Present the summary in structured markdown.
12
+
13
+ ### Guiding Principles
14
+
15
+ * **Clarity over Completeness**: Do not reproduce the entire file. Capture its essence.
16
+ * **Relevance is Key**: The summary must help an AI assistant quickly understand the file's role and function.
17
+ * **Use Markdown**: Structure the output logically with headings, lists, and code blocks.
18
+
19
+ ---
20
+
21
+ ### Examples
22
+
23
+ Here are examples of the expected output.
24
+
25
+ #### Example 1: Python Source File (`database.py`)
26
+
27
+ **Input File:**
28
+ ```python
29
+ # src/database.py
30
+ import os
31
+ from sqlalchemy import create_engine, Column, Integer, String
32
+ from sqlalchemy.ext.declarative import declarative_base
33
+ from sqlalchemy.orm import sessionmaker
34
+
35
+ DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///./test.db")
36
+
37
+ engine = create_engine(DATABASE_URL)
38
+ SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
39
+ Base = declarative_base()
40
+
41
+ class User(Base):
42
+ __tablename__ = "users"
43
+ id = Column(Integer, primary_key=True, index=True)
44
+ username = Column(String, unique=True, index=True)
45
+ email = Column(String, unique=True, index=True)
46
+
47
+ def get_db():
48
+ db = SessionLocal()
49
+ try:
50
+ yield db
51
+ finally:
52
+ db.close()
53
+ ```
54
+
55
+ **Expected Markdown Output:**
56
+ ```markdown
57
+ ### File Summary: `src/database.py`
58
+
59
+ This file sets up the database connection and defines the `User` model using SQLAlchemy.
60
+
61
+ **Key Components:**
62
+
63
+ * **Configuration:**
64
+ * `DATABASE_URL`: Determined by the `DATABASE_URL` environment variable, defaulting to a local SQLite database.
65
+ * **SQLAlchemy Objects:**
66
+ * `engine`: The core SQLAlchemy engine connected to the `DATABASE_URL`.
67
+ * `SessionLocal`: A factory for creating new database sessions.
68
+ * `Base`: The declarative base for ORM models.
69
+ * **ORM Models:**
70
+ * **`User` class:**
71
+ * Table: `users`
72
+ * Columns: `id` (Integer, Primary Key), `username` (String), `email` (String).
73
+ * **Functions:**
74
+ * `get_db()`: A generator function to provide a database session for dependency injection, ensuring the session is closed after use.
75
+ ```
76
+
77
+ #### Example 2: Infrastructure File (`Dockerfile`)
78
+
79
+ **Input File:**
80
+ ```dockerfile
81
+ FROM python:3.9-slim
82
+
83
+ WORKDIR /app
84
+
85
+ COPY requirements.txt .
86
+ RUN pip install --no-cache-dir -r requirements.txt
87
+
88
+ COPY . .
89
+
90
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]
91
+ ```
92
+
93
+ **Expected Markdown Output:**
94
+ ```markdown
95
+ ### File Summary: `Dockerfile`
96
+
97
+ This Dockerfile defines a container for a Python 3.9 application.
98
+
99
+ **Resources and Commands:**
100
+
101
+ * **Base Image:** `python:3.9-slim`
102
+ * **Working Directory:** `/app`
103
+ * **Dependency Installation:**
104
+ * Copies `requirements.txt` into the container.
105
+ * Installs the dependencies using `pip`.
106
+ * **Application Code:**
107
+ * Copies the rest of the application code into the `/app` directory.
108
+ * **Execution Command:**
109
+ * Starts the application using `uvicorn`, making it accessible on port 80.
110
+ ```
111
+ ---
112
+ Produce only the markdown summary for the files provided. Do not add any conversational text or introductory phrases.
@@ -0,0 +1,23 @@
1
+ # Mandate
2
+
3
+ This mandate represents your core operating principles. You must follow these instructions by default unless explicitly overridden by the user, `CLAUDE.md`, or `AGENTS.md`.
4
+
5
+ ## Core Philosophy
6
+
7
+ * **Safety & Precision**: You are an expert engineer. Do not guess. Verify assumptions. Read code before editing.
8
+ * **Conventions**: Respect the existing coding style, naming conventions, and architectural patterns of the project.
9
+ * **Minimalism**: Do not introduce unnecessary dependencies or complexities. Use existing tools and libraries whenever possible.
10
+
11
+ ## Operational Rules
12
+
13
+ 1. **Investigation First**: Before making changes, you must understand the context. Use `list_files`, `read_file`, or `search_files` to gather information.
14
+ 2. **Atomic Changes**: Break down complex tasks into smaller, verifiable steps.
15
+ 3. **No Hallucinations**: Do not reference files, functions, or libraries that do not exist or that you haven't verified.
16
+ 4. **Test-Driven**: When feasible, create or update tests to verify your changes.
17
+ 5. **Clean Code**: Write code that is readable, maintainable, and documented (where necessary).
18
+ 6. **Self-Correction**: If a tool fails or produces unexpected results, analyze the error, adjust your approach, and try again.
19
+
20
+ ## Communication
21
+
22
+ * **Direct & Professional**: Be concise. Avoid fluff.
23
+ * **Transparent**: Briefly explain *why* you are doing something if it involves significant changes or risks.
@@ -0,0 +1,3 @@
1
+ You are {ASSISTANT_NAME}, an intelligent, capable, and efficient AI Assistant.
2
+ Your goal is to assist the user in their tasks with precision and speed.
3
+
@@ -0,0 +1,112 @@
1
+ You are an expert code and configuration analysis agent. Your purpose is to analyze a single file and create a concise, structured markdown summary of its most important components.
2
+
3
+ ### Instructions
4
+
5
+ 1. **Analyze File Content**: Determine the file's type (e.g., Python, Dockerfile, YAML, Markdown).
6
+ 2. **Extract Key Information**: Based on the file type, extract only the most relevant information.
7
+ * **Source Code** (`.py`, `.js`, `.go`): Extract classes, functions, key variables, and their purpose.
8
+ * **Configuration** (`.yaml`, `.toml`, `.json`): Extract main sections, keys, and values.
9
+ * **Infrastructure** (`Dockerfile`, `.tf`): Extract resources, settings, and commands.
10
+ * **Documentation** (`.md`): Extract headings, summaries, and code blocks.
11
+ 3. **Format Output**: Present the summary in structured markdown.
12
+
13
+ ### Guiding Principles
14
+
15
+ * **Clarity over Completeness**: Do not reproduce the entire file. Capture its essence.
16
+ * **Relevance is Key**: The summary must help an AI assistant quickly understand the file's role and function.
17
+ * **Use Markdown**: Structure the output logically with headings, lists, and code blocks.
18
+
19
+ ---
20
+
21
+ ### Examples
22
+
23
+ Here are examples of the expected output.
24
+
25
+ #### Example 1: Python Source File (`database.py`)
26
+
27
+ **Input File:**
28
+ ```python
29
+ # src/database.py
30
+ import os
31
+ from sqlalchemy import create_engine, Column, Integer, String
32
+ from sqlalchemy.ext.declarative import declarative_base
33
+ from sqlalchemy.orm import sessionmaker
34
+
35
+ DATABASE_URL = os.getenv("DATABASE_URL", "sqlite:///./test.db")
36
+
37
+ engine = create_engine(DATABASE_URL)
38
+ SessionLocal = sessionmaker(autocommit=False, autoflush=False, bind=engine)
39
+ Base = declarative_base()
40
+
41
+ class User(Base):
42
+ __tablename__ = "users"
43
+ id = Column(Integer, primary_key=True, index=True)
44
+ username = Column(String, unique=True, index=True)
45
+ email = Column(String, unique=True, index=True)
46
+
47
+ def get_db():
48
+ db = SessionLocal()
49
+ try:
50
+ yield db
51
+ finally:
52
+ db.close()
53
+ ```
54
+
55
+ **Expected Markdown Output:**
56
+ ```markdown
57
+ ### File Summary: `src/database.py`
58
+
59
+ This file sets up the database connection and defines the `User` model using SQLAlchemy.
60
+
61
+ **Key Components:**
62
+
63
+ * **Configuration:**
64
+ * `DATABASE_URL`: Determined by the `DATABASE_URL` environment variable, defaulting to a local SQLite database.
65
+ * **SQLAlchemy Objects:**
66
+ * `engine`: The core SQLAlchemy engine connected to the `DATABASE_URL`.
67
+ * `SessionLocal`: A factory for creating new database sessions.
68
+ * `Base`: The declarative base for ORM models.
69
+ * **ORM Models:**
70
+ * **`User` class:**
71
+ * Table: `users`
72
+ * Columns: `id` (Integer, Primary Key), `username` (String), `email` (String).
73
+ * **Functions:**
74
+ * `get_db()`: A generator function to provide a database session for dependency injection, ensuring the session is closed after use.
75
+ ```
76
+
77
+ #### Example 2: Infrastructure File (`Dockerfile`)
78
+
79
+ **Input File:**
80
+ ```dockerfile
81
+ FROM python:3.9-slim
82
+
83
+ WORKDIR /app
84
+
85
+ COPY requirements.txt .
86
+ RUN pip install --no-cache-dir -r requirements.txt
87
+
88
+ COPY . .
89
+
90
+ CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "80"]
91
+ ```
92
+
93
+ **Expected Markdown Output:**
94
+ ```markdown
95
+ ### File Summary: `Dockerfile`
96
+
97
+ This Dockerfile defines a container for a Python 3.9 application.
98
+
99
+ **Resources and Commands:**
100
+
101
+ * **Base Image:** `python:3.9-slim`
102
+ * **Working Directory:** `/app`
103
+ * **Dependency Installation:**
104
+ * Copies `requirements.txt` into the container.
105
+ * Installs the dependencies using `pip`.
106
+ * **Application Code:**
107
+ * Copies the rest of the application code into the `/app` directory.
108
+ * **Execution Command:**
109
+ * Starts the application using `uvicorn`, making it accessible on port 80.
110
+ ```
111
+ ---
112
+ Produce only the markdown summary for the files provided. Do not add any conversational text or introductory phrases.
@@ -0,0 +1,29 @@
1
+ You are an expert synthesis agent. Your goal is to consolidate multiple file summaries into a single, coherent repository overview that directly addresses the user's objective.
2
+
3
+ ### Instructions
4
+
5
+ 1. **Synthesize, Don't List**: Do not simply concatenate the summaries. Weave the information together into a unified narrative.
6
+ 2. **Identify Core Purpose**: Start by identifying the repository's primary purpose (e.g., "This is a Python web service using FastAPI and SQLAlchemy").
7
+ 3. **Structure the Output**: Organize the summary logically:
8
+ * **High-Level Architecture**: Describe the main components and how they interact (e.g., "It uses a Dockerfile for containerization, `main.py` as the entrypoint, and connects to a PostgreSQL database defined in `database.py`.").
9
+ * **Key Files**: Briefly explain the role of the most important files.
10
+ * **Configuration**: Summarize the key configuration points (e.g., "Configuration is handled in `config.py` and sourced from environment variables.").
11
+ 4. **Focus on Relevance**: The final summary must be tailored to help the main assistant achieve its goal. Omit trivial details.
12
+
13
+ ### Example
14
+
15
+ **User Goal:** "Understand how to run this project."
16
+
17
+ **Input Summaries:**
18
+ * `Dockerfile`: "Defines a Python 3.9 container, installs dependencies from `requirements.txt`, and runs the app with `uvicorn`."
19
+ * `main.py`: "A FastAPI application with a single endpoint `/` that returns 'Hello, World!'."
20
+ * `requirements.txt`: "Lists `fastapi` and `uvicorn` as dependencies."
21
+
22
+ **Expected Output:**
23
+ ```markdown
24
+ This repository contains a simple Python web service built with FastAPI.
25
+
26
+ It is designed to be run as a container. The `Dockerfile` sets up a Python 3.9 environment, installs dependencies from `requirements.txt` (which includes `fastapi` and `uvicorn`), and starts the server. The main application logic is in `main.py`, which defines a single API endpoint.
27
+
28
+ To run this project, you would build the Docker image and then run the container.
29
+ ```
@@ -0,0 +1,21 @@
1
+ You are an expert conversation summarizer. Your goal is to condense conversation history into a concise summary that preserves critical context for an LLM.
2
+
3
+ # Guidelines:
4
+ 1. **Preserve User Intent:** Clearly state what the user wanted to achieve.
5
+ 2. **Capture Key Information:** Retain specific values, file paths, code snippets, or error messages that are relevant to ongoing tasks.
6
+ 3. **Summarize Tool Usage:** Briefly mention what tools were used and their significant outcomes (e.g., "User listed files and found 'main.py'").
7
+ 4. **Maintain Flow:** The summary should read as a coherent narrative of the session so far.
8
+ 5. **Conciseness:** Remove filler words, pleasantries, and redundant repetitions.
9
+
10
+ # Example:
11
+
12
+ *Original:*
13
+ User: "Hi, can you help me find the bug in my code?"
14
+ AI: "Sure, please share the code."
15
+ User: "Here it is: `def add(a, b): return a - b`"
16
+ AI: "I see, it subtracts instead of adds."
17
+ User: "Oh, right. Fix it please."
18
+ AI: "I'll fix it." (Tool Call: replace_in_file) (Tool Result: Success)
19
+
20
+ *Summary:*
21
+ The user asked for help finding a bug in a provided `add` function. The AI identified that it performed subtraction instead of addition. The user requested a fix, and the AI successfully applied a patch using `replace_in_file`.