bone-agent 1.4.0 → 2.0.1

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 (126) hide show
  1. package/bin/bone.js +39 -0
  2. package/package.json +25 -39
  3. package/LICENSE +0 -21
  4. package/README.md +0 -201
  5. package/bin/npm-wrapper.js +0 -235
  6. package/bin/rg +0 -0
  7. package/bin/rg.exe +0 -0
  8. package/config.yaml.example +0 -144
  9. package/prompts/main/ask_questions.md +0 -31
  10. package/prompts/main/batch_independent_calls.md +0 -5
  11. package/prompts/main/casual_interactions.md +0 -11
  12. package/prompts/main/code_references.md +0 -8
  13. package/prompts/main/communication_style.md +0 -12
  14. package/prompts/main/context_reliability.md +0 -12
  15. package/prompts/main/conversational_tool_calling.md +0 -15
  16. package/prompts/main/dream.md +0 -50
  17. package/prompts/main/editing_pattern.md +0 -13
  18. package/prompts/main/error_handling.md +0 -6
  19. package/prompts/main/exploration_pattern.md +0 -21
  20. package/prompts/main/intro.md +0 -1
  21. package/prompts/main/obsidian.md +0 -16
  22. package/prompts/main/obsidian_project.md +0 -79
  23. package/prompts/main/professional_objectivity.md +0 -3
  24. package/prompts/main/skills.md +0 -3
  25. package/prompts/main/targeted_searching.md +0 -10
  26. package/prompts/main/task_lists_pattern.md +0 -8
  27. package/prompts/main/temp_folder.md +0 -9
  28. package/prompts/main/think_before_acting.md +0 -10
  29. package/prompts/main/tone_and_style.md +0 -4
  30. package/prompts/main/tool_preferences.md +0 -24
  31. package/prompts/main/trust_subagent_context.md +0 -21
  32. package/prompts/main/when_to_use_sub_agent.md +0 -7
  33. package/prompts/micro/ask_questions.md +0 -1
  34. package/prompts/micro/batch_independent_calls.md +0 -1
  35. package/prompts/micro/casual_interactions.md +0 -1
  36. package/prompts/micro/code_references.md +0 -1
  37. package/prompts/micro/communication_style.md +0 -1
  38. package/prompts/micro/context_reliability.md +0 -1
  39. package/prompts/micro/conversational_tool_calling.md +0 -1
  40. package/prompts/micro/editing_pattern.md +0 -1
  41. package/prompts/micro/error_handling.md +0 -1
  42. package/prompts/micro/exploration_pattern.md +0 -1
  43. package/prompts/micro/intro.md +0 -1
  44. package/prompts/micro/obsidian.md +0 -4
  45. package/prompts/micro/obsidian_project.md +0 -5
  46. package/prompts/micro/professional_objectivity.md +0 -1
  47. package/prompts/micro/skills.md +0 -1
  48. package/prompts/micro/targeted_searching.md +0 -1
  49. package/prompts/micro/task_lists_pattern.md +0 -1
  50. package/prompts/micro/temp_folder.md +0 -1
  51. package/prompts/micro/think_before_acting.md +0 -5
  52. package/prompts/micro/tone_and_style.md +0 -1
  53. package/prompts/micro/tool_preferences.md +0 -1
  54. package/prompts/micro/trust_subagent_context.md +0 -1
  55. package/prompts/micro/when_to_use_sub_agent.md +0 -1
  56. package/requirements.txt +0 -9
  57. package/src/__init__.py +0 -11
  58. package/src/core/__init__.py +0 -1
  59. package/src/core/agentic.py +0 -1085
  60. package/src/core/chat_manager.py +0 -1577
  61. package/src/core/config_manager.py +0 -260
  62. package/src/core/cron.py +0 -578
  63. package/src/core/cron_allowlist.py +0 -118
  64. package/src/core/memory.py +0 -145
  65. package/src/core/metadata.py +0 -75
  66. package/src/core/retry.py +0 -71
  67. package/src/core/skills.py +0 -463
  68. package/src/core/sub_agent.py +0 -376
  69. package/src/core/tool_approval.py +0 -220
  70. package/src/core/tool_feedback.py +0 -789
  71. package/src/exceptions.py +0 -79
  72. package/src/llm/__init__.py +0 -1
  73. package/src/llm/client.py +0 -176
  74. package/src/llm/codex_provider.py +0 -350
  75. package/src/llm/config.py +0 -536
  76. package/src/llm/prompts.py +0 -494
  77. package/src/llm/providers.py +0 -438
  78. package/src/llm/streaming.py +0 -163
  79. package/src/llm/token_tracker.py +0 -399
  80. package/src/tools/__init__.py +0 -151
  81. package/src/tools/constants.py +0 -59
  82. package/src/tools/create_file.py +0 -136
  83. package/src/tools/directory.py +0 -389
  84. package/src/tools/edit.py +0 -549
  85. package/src/tools/file_reader.py +0 -322
  86. package/src/tools/helpers/__init__.py +0 -99
  87. package/src/tools/helpers/base.py +0 -599
  88. package/src/tools/helpers/converters.py +0 -44
  89. package/src/tools/helpers/file_helpers.py +0 -189
  90. package/src/tools/helpers/formatters.py +0 -411
  91. package/src/tools/helpers/loader.py +0 -145
  92. package/src/tools/helpers/parallel_executor.py +0 -231
  93. package/src/tools/helpers/path_resolver.py +0 -283
  94. package/src/tools/helpers/plugin_manifest.py +0 -185
  95. package/src/tools/obsidian.py +0 -96
  96. package/src/tools/review_sub_agent.py +0 -190
  97. package/src/tools/rg_search.py +0 -477
  98. package/src/tools/search_plugins.py +0 -177
  99. package/src/tools/select_option.py +0 -600
  100. package/src/tools/shell.py +0 -302
  101. package/src/tools/sub_agent.py +0 -139
  102. package/src/tools/task_list.py +0 -269
  103. package/src/tools/web_search.py +0 -61
  104. package/src/ui/__init__.py +0 -1
  105. package/src/ui/banner.py +0 -87
  106. package/src/ui/commands.py +0 -3131
  107. package/src/ui/displays.py +0 -239
  108. package/src/ui/loader.py +0 -284
  109. package/src/ui/main.py +0 -643
  110. package/src/ui/prompt_utils.py +0 -113
  111. package/src/ui/setting_selector.py +0 -590
  112. package/src/ui/setup_wizard.py +0 -294
  113. package/src/ui/sub_agent_panel.py +0 -234
  114. package/src/ui/tool_confirmation.py +0 -226
  115. package/src/utils/__init__.py +0 -1
  116. package/src/utils/citation_parser.py +0 -199
  117. package/src/utils/editor.py +0 -207
  118. package/src/utils/gitignore_filter.py +0 -149
  119. package/src/utils/logger.py +0 -254
  120. package/src/utils/paths.py +0 -30
  121. package/src/utils/result_parsers.py +0 -108
  122. package/src/utils/safe_commands.py +0 -243
  123. package/src/utils/settings.py +0 -195
  124. package/src/utils/user_message_logger.py +0 -120
  125. package/src/utils/validation.py +0 -201
  126. package/src/utils/web_search.py +0 -173
@@ -1,322 +0,0 @@
1
- """File reading operations."""
2
-
3
- import os
4
- from pathlib import Path
5
- from typing import Optional, Dict, Tuple
6
-
7
- from .helpers.base import tool
8
- from .helpers.path_resolver import PathResolver
9
- from .helpers.formatters import format_file_result
10
- from . import constants
11
-
12
-
13
- def _validate_read_path(
14
- path_str: str,
15
- repo_root: Path,
16
- gitignore_spec,
17
- vault_root: str = None,
18
- ) -> Tuple[Optional[Path], Optional[str]]:
19
- """Validate and resolve path for reading.
20
-
21
- Args:
22
- path_str: Path string to validate
23
- repo_root: Repository root directory
24
- gitignore_spec: Optional PathSpec for .gitignore filtering
25
- vault_root: Optional Obsidian vault root path
26
-
27
- Returns:
28
- (resolved_path, error_message) - error_message is None if valid
29
- """
30
- vault_path = Path(vault_root) if vault_root else None
31
- resolver = PathResolver(repo_root=repo_root, gitignore_spec=gitignore_spec, vault_path=vault_path)
32
- return resolver.resolve_and_validate(
33
- path_str,
34
- check_gitignore=True,
35
- must_exist=True,
36
- must_be_file=True,
37
- enforce_boundary=True,
38
- )
39
-
40
-
41
- def _validate_start_line(start_line: Optional[int]) -> int:
42
- """Validate and normalize start_line parameter.
43
-
44
- Args:
45
- start_line: Optional 1-based starting line number
46
-
47
- Returns:
48
- Normalized start_line (1 or greater)
49
- """
50
- if start_line is None:
51
- return 1
52
- try:
53
- start_line = int(start_line)
54
- except (TypeError, ValueError):
55
- raise ValueError("start_line must be an integer (1-based).")
56
- if start_line < 1:
57
- start_line = 1
58
- return start_line
59
-
60
-
61
- def _skip_lines(file_obj, lines_to_skip: int) -> bool:
62
- """Advance file_obj by lines_to_skip lines.
63
-
64
- Args:
65
- file_obj: File object to advance
66
- lines_to_skip: Number of lines to skip
67
-
68
- Returns:
69
- True if EOF reached early
70
- """
71
- if lines_to_skip <= 0:
72
- return False
73
- remaining = lines_to_skip
74
- while remaining > 0:
75
- if file_obj.readline() == "":
76
- return True
77
- remaining -= 1
78
- return False
79
-
80
-
81
- def _read_full_file(file_path: Path, start_line: int) -> Dict[str, any]:
82
- """Read entire file, optionally starting from specific line.
83
-
84
- Args:
85
- file_path: Path to file to read
86
- start_line: 1-based starting line number
87
-
88
- Returns:
89
- dict with keys: content, lines_read, truncated=False
90
- """
91
- if start_line == 1:
92
- # Use newline=None (universal newlines) to normalize \r\n → \n,
93
- # matching the behavior of all other read paths and edit_file's matcher.
94
- with file_path.open("r", encoding="utf-8", errors="replace", newline=None) as f:
95
- content = f.read()
96
- lines_read = len(content.splitlines())
97
- else:
98
- lines = []
99
- with file_path.open("r", encoding="utf-8", errors="replace", newline=None) as f:
100
- eof_early = _skip_lines(f, start_line - 1)
101
- if not eof_early:
102
- lines = f.readlines()
103
- content = "".join(lines)
104
- lines_read = len(content.splitlines())
105
-
106
- return {"content": content, "lines_read": lines_read, "truncated": False}
107
-
108
-
109
- def _read_partial_file(file_path: Path, start_line: int, max_lines: int) -> Dict[str, any]:
110
- """Read partial file content with streaming for large files.
111
-
112
- Args:
113
- file_path: Path to file to read
114
- start_line: 1-based starting line number
115
- max_lines: Maximum number of lines to read
116
-
117
- Returns:
118
- dict with keys: content, lines_read, truncated
119
-
120
- Strategy:
121
- - Stream in 8KB chunks
122
- - Extract complete lines as we go
123
- - Stop at max_lines
124
- - Handle pathological long lines (>10MB buffer)
125
- """
126
- lines = []
127
- truncated = False
128
- lines_read = 0
129
- chunk_size = constants.FILE_READ_CHUNK_SIZE
130
- max_buffer_size = constants.FILE_READ_MAX_BUFFER_SIZE
131
-
132
- # Use universal newlines so all newline types normalize to '\n' for parsing.
133
- with file_path.open("r", encoding="utf-8", errors="replace", newline=None) as f:
134
- eof_early = _skip_lines(f, start_line - 1)
135
- if eof_early:
136
- return {"content": "", "lines_read": 0, "truncated": False}
137
-
138
- if max_lines == 0:
139
- # Check if file has any content without loading it all
140
- if f.read(1):
141
- truncated = True
142
- else:
143
- # Streaming read: read in chunks, stop when we have enough lines
144
- buffer = ""
145
- eof_reached = False
146
- while lines_read < max_lines:
147
- chunk = f.read(chunk_size)
148
- if not chunk: # EOF reached
149
- eof_reached = True
150
- break
151
-
152
- buffer += chunk
153
-
154
- parts = buffer.split("\n")
155
- complete_lines = len(parts) - 1
156
- remaining_capacity = max_lines - lines_read
157
-
158
- if complete_lines:
159
- to_take = min(remaining_capacity, complete_lines)
160
- for i in range(to_take):
161
- lines.append(parts[i] + "\n")
162
- lines_read += to_take
163
-
164
- if to_take < complete_lines:
165
- truncated = True
166
- buffer = ""
167
- break
168
-
169
- buffer = parts[-1]
170
-
171
- # If we've read enough lines and have leftover content, mark as truncated
172
- if lines_read >= max_lines:
173
- if buffer:
174
- truncated = True
175
- break
176
-
177
- # Safeguard against extremely long single lines (pathological case)
178
- if len(buffer) > max_buffer_size:
179
- lines.append(buffer[:max_buffer_size])
180
- lines_read += 1
181
- truncated = True
182
- buffer = ""
183
- break
184
-
185
- if eof_reached and not truncated and buffer and lines_read < max_lines:
186
- lines.append(buffer)
187
- lines_read += 1
188
- buffer = ""
189
-
190
- if lines_read >= max_lines and not truncated:
191
- # We may have stopped exactly at a chunk boundary; peek for more content.
192
- if f.read(1):
193
- truncated = True
194
-
195
- content = "".join(lines)
196
- return {"content": content, "lines_read": lines_read, "truncated": truncated}
197
-
198
-
199
- def _read_file_content(
200
- file_path: Path,
201
- start_line: int,
202
- max_lines: Optional[int]
203
- ) -> Dict[str, any]:
204
- """Read file content with optional line range.
205
-
206
- Args:
207
- file_path: Path to file to read
208
- start_line: 1-based starting line number
209
- max_lines: Optional maximum number of lines to read
210
-
211
- Returns:
212
- dict with keys: content, lines_read, truncated
213
-
214
- Logic:
215
- - If max_lines is None: call _read_full_file()
216
- - Else: call _read_partial_file()
217
- """
218
- if max_lines is None:
219
- return _read_full_file(file_path, start_line)
220
- return _read_partial_file(file_path, start_line, max_lines)
221
-
222
-
223
- @tool(
224
- name="read_file",
225
- description="Read file contents. Prefer over rg when you know the file path.",
226
- parameters={
227
- "type": "object",
228
- "properties": {
229
- "path_str": {"type": "string", "description": "Path to read"},
230
- "max_lines": {"type": "integer", "description": "Max lines to read (omit for full file)"},
231
- "start_line": {"type": "integer", "description": "1-based start line (default: 1)"}
232
- },
233
- "required": ["path_str"]
234
- },
235
- )
236
- def read_file(
237
- path_str: str,
238
- repo_root: Path,
239
- max_lines: Optional[int] = None,
240
- start_line: Optional[int] = None,
241
- gitignore_spec = None,
242
- vault_root: str = None,
243
- ) -> str:
244
- """Read a file's contents.
245
-
246
- Fast file reader that respects .gitignore, supports partial reads via
247
- max_lines/start_line, and provides consistent output format.
248
-
249
- Args:
250
- path_str: Path string to the file to read
251
- repo_root: Repository root directory (for path resolution)
252
- max_lines: Optional limit on number of lines to read
253
- start_line: Optional 1-based starting line number (default: 1)
254
- gitignore_spec: Optional PathSpec for .gitignore filtering
255
- vault_root: Optional Obsidian vault root path
256
-
257
- Returns:
258
- str: Formatted result with exit_code, lines_read, and file content
259
- """
260
- try:
261
- # Validate path
262
- resolved, error = _validate_read_path(path_str, repo_root, gitignore_spec, vault_root=vault_root)
263
- if error:
264
- return format_file_result(
265
- exit_code=1,
266
- error=error,
267
- path=path_str
268
- )
269
-
270
- # Validate start_line
271
- try:
272
- start_line = _validate_start_line(start_line)
273
- except ValueError as e:
274
- try:
275
- rel_path = resolved.relative_to(repo_root)
276
- except ValueError:
277
- rel_path = resolved
278
- return format_file_result(
279
- exit_code=1,
280
- error=str(e),
281
- path=str(rel_path)
282
- )
283
-
284
- # Normalize max_lines
285
- if max_lines is not None and max_lines < 0:
286
- max_lines = 0
287
-
288
- # Read file content
289
- result = _read_file_content(resolved, start_line, max_lines)
290
-
291
- try:
292
- rel_path = resolved.relative_to(repo_root)
293
- except ValueError:
294
- rel_path = resolved
295
-
296
- return format_file_result(
297
- exit_code=0,
298
- content=result["content"],
299
- path=str(rel_path),
300
- lines_read=result["lines_read"],
301
- start_line=start_line,
302
- truncated=result["truncated"]
303
- )
304
-
305
- except FileNotFoundError:
306
- return format_file_result(
307
- exit_code=1,
308
- error="File not found",
309
- path=path_str
310
- )
311
- except PermissionError:
312
- return format_file_result(
313
- exit_code=1,
314
- error="Permission denied",
315
- path=path_str
316
- )
317
- except Exception as e:
318
- return format_file_result(
319
- exit_code=1,
320
- error=str(e),
321
- path=path_str
322
- )
@@ -1,99 +0,0 @@
1
- """Tool infrastructure and helper utilities.
2
-
3
- This subpackage provides the core infrastructure for tool registration,
4
- execution, and supporting utilities. It is not intended to be imported
5
- directly by end users - import from tools/ instead for backward compatibility.
6
-
7
- For creating custom tools, use:
8
- from tools import tool # or from tools.helpers.base import tool
9
- """
10
-
11
- # Core infrastructure
12
- from .base import (
13
- ToolDefinition,
14
- ToolRegistry,
15
- tool,
16
- build_context,
17
- get_tool_schemas,
18
- get_terminal_policy,
19
- TERMINAL_NONE,
20
- TERMINAL_YIELD,
21
- TERMINAL_STOP,
22
- TOOLS,
23
- )
24
-
25
- # File operation helpers
26
- from .file_helpers import (
27
- _is_reserved_windows_name,
28
- GitignoreFilter,
29
- )
30
-
31
- # Path resolution helpers
32
- from .path_resolver import PathResolver
33
-
34
- # Result formatting helpers
35
- from .formatters import (
36
- format_tool_result,
37
- format_file_result,
38
- _build_diff,
39
- _detect_newline,
40
- )
41
-
42
- # Type conversion helpers
43
- from .converters import (
44
- coerce_int,
45
- coerce_bool,
46
- )
47
-
48
- # Tool loading and discovery
49
- from .loader import (
50
- discover_tools,
51
- list_registered_tools,
52
- )
53
-
54
- # Plugin manifest for on-demand tool discovery
55
- from .plugin_manifest import PluginManifest, plugin_manifest
56
-
57
- # Parallel execution
58
- from .parallel_executor import (
59
- ToolCall,
60
- ToolResult,
61
- ParallelToolExecutor,
62
- )
63
-
64
- __all__ = [
65
- # Core infrastructure
66
- 'ToolDefinition',
67
- 'ToolRegistry',
68
- 'tool',
69
- 'build_context',
70
- 'get_tool_schemas',
71
- 'get_terminal_policy',
72
- 'TERMINAL_NONE',
73
- 'TERMINAL_YIELD',
74
- 'TERMINAL_STOP',
75
- 'TOOLS',
76
- # File operation helpers
77
- '_is_reserved_windows_name',
78
- 'GitignoreFilter',
79
- # Path resolution helpers
80
- 'PathResolver',
81
- # Result formatting helpers
82
- 'format_tool_result',
83
- 'format_file_result',
84
- '_build_diff',
85
- '_detect_newline',
86
- # Type conversion helpers
87
- 'coerce_int',
88
- 'coerce_bool',
89
- # Tool loading and discovery
90
- 'discover_tools',
91
- 'list_registered_tools',
92
- # Plugin manifest
93
- 'PluginManifest',
94
- 'plugin_manifest',
95
- # Parallel execution
96
- 'ToolCall',
97
- 'ToolResult',
98
- 'ParallelToolExecutor',
99
- ]