stashai 0.1.332__tar.gz → 0.1.334__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.
- {stashai-0.1.332/stashai.egg-info → stashai-0.1.334}/PKG-INFO +1 -1
- {stashai-0.1.332 → stashai-0.1.334}/cli/tests/test_app_vfs.py +11 -10
- {stashai-0.1.332 → stashai-0.1.334}/pyproject.toml +1 -1
- {stashai-0.1.332 → stashai-0.1.334/stashai.egg-info}/PKG-INFO +1 -1
- {stashai-0.1.332 → stashai-0.1.334}/stashvfs/__init__.py +2 -1
- {stashai-0.1.332 → stashai-0.1.334}/stashvfs/client.py +10 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashvfs/model.py +5 -5
- {stashai-0.1.332 → stashai-0.1.334}/stashvfs/shell.py +52 -25
- {stashai-0.1.332 → stashai-0.1.334}/LICENSE +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/README.md +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/__init__.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/client.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/config.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/formatting.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/import_history.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/main.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/mcp_server.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/telemetry.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/tests/__init__.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/tests/test_agent_cli.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/tests/test_agent_detection.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/tests/test_client_retry.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/tests/test_history_pagination.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/tests/test_hook_cli.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/tests/test_import_history.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/tests/test_install_codex.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/tests/test_install_gemini.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/tests/test_install_hermes.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/tests/test_install_hooks.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/tests/test_install_openclaw.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/tests/test_ls_cmd.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/tests/test_marketplace_autoupdate.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/tests/test_memory_recompute_poll.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/tests/test_mount_vfs.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/tests/test_root_help.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/tests/test_scope_config.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/tests/test_setup_complete_splash.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/tests/test_share_and_upload_helpers.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/tests/test_sharing_cli.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/tests/test_skill_client_permissions.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/tests/test_skills_install.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/tests/test_skills_installed.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/tests/test_skills_sync.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/tests/test_sources_cli.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/tests/test_streaming_toggle.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/tests/test_tools_cli.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/tests/test_update_file_and_folder.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/cli/tests/test_upload_status_helpers.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/setup.cfg +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/__init__.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/__init__.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/_do_session_upload.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/_do_upload.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/_session_watcher.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/agent_config.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/__init__.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/claude/scripts/adapt.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/claude/scripts/config.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/claude/scripts/on_prompt.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/claude/scripts/on_session_end.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/claude/scripts/on_session_start.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/claude/scripts/on_stop.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/claude/scripts/on_tool_use.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/codex/AGENTS.md +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/codex/README.md +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/codex/config.toml.snippet +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/codex/hooks.json +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/codex/scripts/adapt.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/codex/scripts/config.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/codex/scripts/on_prompt.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/codex/scripts/on_session_start.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/codex/scripts/on_stop.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/codex/scripts/on_tool_use.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/cursor/README.md +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/cursor/hooks.json +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/cursor/scripts/adapt.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/cursor/scripts/config.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/cursor/scripts/on_agent_response.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/cursor/scripts/on_prompt.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/cursor/scripts/on_session_end.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/cursor/scripts/on_session_start.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/cursor/scripts/on_tool_use.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/cursor/stash.mdc +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/gemini/GEMINI.md +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/gemini/README.md +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/gemini/scripts/adapt.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/gemini/scripts/config.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/gemini/scripts/on_prompt.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/gemini/scripts/on_session_end.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/gemini/scripts/on_session_start.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/gemini/scripts/on_stop.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/gemini/scripts/on_tool_use.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/gemini/settings.snippet.json +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/hermes/HERMES.md +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/hermes/README.md +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/hermes/config.snippet.yaml +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/hermes/scripts/adapt.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/hermes/scripts/config.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/hermes/scripts/on_prompt.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/hermes/scripts/on_session_end.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/hermes/scripts/on_session_start.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/hermes/scripts/on_stop.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/hermes/scripts/on_tool_use.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/openclaw/README.md +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/openclaw/dist/index.js +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/openclaw/index.ts +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/openclaw/openclaw.plugin.json +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/openclaw/package.json +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/openclaw/scripts/adapt.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/openclaw/scripts/config.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/openclaw/scripts/on_prompt.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/openclaw/scripts/on_session_end.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/openclaw/scripts/on_session_start.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/openclaw/scripts/on_stop.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/opencode/AGENTS.md +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/opencode/README.md +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/opencode/package.json +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/opencode/plugin.ts +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/opencode/scripts/adapt.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/opencode/scripts/config.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/opencode/scripts/on_prompt.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/opencode/scripts/on_session_end.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/opencode/scripts/on_session_start.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/opencode/scripts/on_tool_use.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/doctor.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/event.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/hooks.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/scope.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/session_upload.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/stash_client.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/state.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/summarize.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/transcript_upload.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/upload_status.py +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai.egg-info/SOURCES.txt +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai.egg-info/dependency_links.txt +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai.egg-info/entry_points.txt +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai.egg-info/requires.txt +0 -0
- {stashai-0.1.332 → stashai-0.1.334}/stashai.egg-info/top_level.txt +0 -0
|
@@ -190,21 +190,22 @@ def test_app_vfs_grep_no_match_stops_and_chain():
|
|
|
190
190
|
assert result.stderr == ""
|
|
191
191
|
|
|
192
192
|
|
|
193
|
-
def
|
|
194
|
-
"""Patterns compile as extended regex,
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
193
|
+
def test_app_vfs_grep_accepts_bre_operator_escapes():
|
|
194
|
+
"""Patterns compile as extended regex, but default-grep callers write BRE —
|
|
195
|
+
`foo\\|bar` (this burned Heavi's agent in prod, twice: first as a silent
|
|
196
|
+
no-match, then as a hard refusal that cost the agent its search). Both
|
|
197
|
+
dialects intend an operator there, so the escape must just work; `-F` stays
|
|
198
|
+
the literal-match path."""
|
|
198
199
|
shell, _client = _shell()
|
|
199
200
|
|
|
200
|
-
|
|
201
|
+
assert shell.run("grep -ri 'hello\\|missing' /").exit_code == 0
|
|
201
202
|
|
|
202
|
-
|
|
203
|
-
assert "extended" in result.stderr
|
|
204
|
-
|
|
205
|
-
# The same intent written as extended regex works.
|
|
203
|
+
# The same intent written as extended regex still works.
|
|
206
204
|
assert shell.run("grep -ri 'hello|missing' /").exit_code == 0
|
|
207
205
|
|
|
206
|
+
# A literal backslash escape that is not a BRE operator is left alone.
|
|
207
|
+
assert shell.run("grep -ri 'hello\\d' /").exit_code == 1
|
|
208
|
+
|
|
208
209
|
|
|
209
210
|
def test_app_vfs_grep_accepts_extended_and_fixed_string_flags():
|
|
210
211
|
shell, _client = _shell()
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
contract that backs them. Shared by the `stash vfs` CLI command and the
|
|
3
3
|
`/api/v1/me/vfs` endpoint."""
|
|
4
4
|
|
|
5
|
-
from .client import MachineVfsClient, VfsClient, VfsClientError
|
|
5
|
+
from .client import MachineVfsClient, VfsClient, VfsClientError, VfsScanBudget
|
|
6
6
|
from .model import MountError, StashVfsModel
|
|
7
7
|
from .shell import SkillAppVfsShell, VfsCommandResult
|
|
8
8
|
|
|
@@ -14,4 +14,5 @@ __all__ = [
|
|
|
14
14
|
"VfsClient",
|
|
15
15
|
"VfsClientError",
|
|
16
16
|
"VfsCommandResult",
|
|
17
|
+
"VfsScanBudget",
|
|
17
18
|
]
|
|
@@ -25,6 +25,16 @@ class VfsClientError(Exception):
|
|
|
25
25
|
super().__init__(str(detail))
|
|
26
26
|
|
|
27
27
|
|
|
28
|
+
class VfsScanBudget(Exception):
|
|
29
|
+
"""A document read was refused because the command's scan budget ran out.
|
|
30
|
+
|
|
31
|
+
Raised by clients that meter reads (the server-side VFS) for reads issued
|
|
32
|
+
inside `scan_calls`. The shell stops the grep sweep at this point and
|
|
33
|
+
reports the results so far with a loud truncation warning — an org-wide
|
|
34
|
+
sweep degrades to a partial answer instead of an aborted command.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
|
|
28
38
|
class VfsClient(Protocol):
|
|
29
39
|
"""Everything `StashVfsModel` reads. Listing calls run during `refresh()`;
|
|
30
40
|
the rest are lazy loaders fired when a file's bytes are first read."""
|
|
@@ -15,7 +15,7 @@ from concurrent.futures import ThreadPoolExecutor
|
|
|
15
15
|
from dataclasses import dataclass, field
|
|
16
16
|
from datetime import datetime
|
|
17
17
|
|
|
18
|
-
from .client import MachineVfsClient, VfsClient, VfsClientError
|
|
18
|
+
from .client import MachineVfsClient, VfsClient, VfsClientError, VfsScanBudget
|
|
19
19
|
|
|
20
20
|
BytesLoader = Callable[[], bytes]
|
|
21
21
|
|
|
@@ -57,7 +57,7 @@ class VfsNode:
|
|
|
57
57
|
# Set when `prefetch` already tried this loader and it failed. `read_file`
|
|
58
58
|
# re-raises it rather than fetching again — a second fetch would hit the
|
|
59
59
|
# provider twice and, server-side, spend the document budget twice.
|
|
60
|
-
error: VfsClientError | None = None
|
|
60
|
+
error: VfsClientError | VfsScanBudget | None = None
|
|
61
61
|
|
|
62
62
|
@property
|
|
63
63
|
def is_dir(self) -> bool:
|
|
@@ -180,16 +180,16 @@ class StashVfsModel:
|
|
|
180
180
|
if len(pending) < 2:
|
|
181
181
|
return
|
|
182
182
|
|
|
183
|
-
def load(node: VfsNode) -> tuple[VfsNode, bytes | VfsClientError]:
|
|
183
|
+
def load(node: VfsNode) -> tuple[VfsNode, bytes | VfsClientError | VfsScanBudget]:
|
|
184
184
|
try:
|
|
185
185
|
return node, node.loader()
|
|
186
|
-
except VfsClientError as e:
|
|
186
|
+
except (VfsClientError, VfsScanBudget) as e:
|
|
187
187
|
return node, e
|
|
188
188
|
|
|
189
189
|
workers = min(PREFETCH_CONCURRENCY, len(pending))
|
|
190
190
|
with ThreadPoolExecutor(max_workers=workers) as pool:
|
|
191
191
|
for node, result in pool.map(load, pending):
|
|
192
|
-
if isinstance(result, VfsClientError):
|
|
192
|
+
if isinstance(result, (VfsClientError, VfsScanBudget)):
|
|
193
193
|
node.error = result
|
|
194
194
|
continue
|
|
195
195
|
node.content = result
|
|
@@ -10,7 +10,7 @@ import time
|
|
|
10
10
|
from dataclasses import dataclass
|
|
11
11
|
from datetime import datetime
|
|
12
12
|
|
|
13
|
-
from .client import VfsClientError
|
|
13
|
+
from .client import VfsClientError, VfsScanBudget
|
|
14
14
|
from .model import MountError, StashVfsModel
|
|
15
15
|
|
|
16
16
|
|
|
@@ -435,9 +435,8 @@ class SkillAppVfsShell:
|
|
|
435
435
|
if fixed_strings:
|
|
436
436
|
regex = re.compile(re.escape(pattern), flags)
|
|
437
437
|
else:
|
|
438
|
-
_reject_bre_escapes(pattern)
|
|
439
438
|
try:
|
|
440
|
-
regex = re.compile(pattern, flags)
|
|
439
|
+
regex = re.compile(_translate_bre_escapes(pattern), flags)
|
|
441
440
|
except re.error as e:
|
|
442
441
|
raise VfsShellError(str(e)) from e
|
|
443
442
|
|
|
@@ -459,27 +458,44 @@ class SkillAppVfsShell:
|
|
|
459
458
|
paths = ["."]
|
|
460
459
|
matches = []
|
|
461
460
|
roots = []
|
|
461
|
+
# Resolve every root before reading anything (listings are unbudgeted),
|
|
462
|
+
# so a budget stop can say how much of the sweep went unsearched.
|
|
463
|
+
sweep: list[list[str]] = []
|
|
464
|
+
total_files = 0
|
|
465
|
+
for raw_path in paths:
|
|
466
|
+
path = self._resolve_path(raw_path)
|
|
467
|
+
roots.append(path)
|
|
468
|
+
node = self.model._get_node(path)
|
|
469
|
+
file_paths = [path] if node.is_file else self._file_paths(path) if recursive else []
|
|
470
|
+
if node.is_dir and not recursive:
|
|
471
|
+
raise VfsShellError(f"{raw_path}: is a directory")
|
|
472
|
+
sweep.append(file_paths)
|
|
473
|
+
total_files += len(file_paths)
|
|
462
474
|
docs_scanned = 0
|
|
475
|
+
budget_hit = False
|
|
463
476
|
# The per-document reads below are the mechanics of one search, not
|
|
464
477
|
# documents the user asked to see — scan_calls tags them so analytics
|
|
465
478
|
# skip them, and record_search writes the single search event that
|
|
466
479
|
# stands in for the whole sweep.
|
|
467
480
|
with self.model.client.scan_calls():
|
|
468
|
-
for
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
node = self.model._get_node(path)
|
|
472
|
-
file_paths = [path] if node.is_file else self._file_paths(path) if recursive else []
|
|
473
|
-
if node.is_dir and not recursive:
|
|
474
|
-
raise VfsShellError(f"{raw_path}: is a directory")
|
|
481
|
+
for file_paths in sweep:
|
|
482
|
+
if budget_hit:
|
|
483
|
+
break
|
|
475
484
|
self.model.prefetch(file_paths)
|
|
476
485
|
for file_path in file_paths:
|
|
477
|
-
docs_scanned += 1
|
|
478
486
|
try:
|
|
479
487
|
text = self._read_text(file_path)
|
|
488
|
+
except VfsScanBudget:
|
|
489
|
+
# Out of reads mid-sweep: report what was found rather
|
|
490
|
+
# than aborting, but never silently — the warning below
|
|
491
|
+
# marks the results partial.
|
|
492
|
+
budget_hit = True
|
|
493
|
+
break
|
|
480
494
|
except VfsClientError as e:
|
|
495
|
+
docs_scanned += 1
|
|
481
496
|
self._warn(f"{name}: {file_path}: {e.detail}")
|
|
482
497
|
continue
|
|
498
|
+
docs_scanned += 1
|
|
483
499
|
matches.append(
|
|
484
500
|
_grep_text(
|
|
485
501
|
regex,
|
|
@@ -492,6 +508,12 @@ class SkillAppVfsShell:
|
|
|
492
508
|
)
|
|
493
509
|
)
|
|
494
510
|
self.model.client.record_search(pattern, roots, docs_scanned)
|
|
511
|
+
if budget_hit:
|
|
512
|
+
self._warn(
|
|
513
|
+
f"{name}: stopped after reading {docs_scanned} of {total_files} files "
|
|
514
|
+
"(server scan budget); matches shown are partial — narrow the paths to "
|
|
515
|
+
"search the rest"
|
|
516
|
+
)
|
|
495
517
|
output = "".join(matches)
|
|
496
518
|
if not output:
|
|
497
519
|
raise VfsShellExit(1)
|
|
@@ -860,23 +882,28 @@ def _render_printf_template(template: str, values: list[str]) -> tuple[str, int]
|
|
|
860
882
|
return "".join(output), used
|
|
861
883
|
|
|
862
884
|
|
|
863
|
-
def
|
|
885
|
+
def _translate_bre_escapes(pattern: str) -> str:
|
|
864
886
|
"""Patterns compile as Python (extended) regex, where BRE's operator escapes
|
|
865
|
-
`\\|` `\\(` `\\)` mean the *literal* character — so a caller writing
|
|
866
|
-
syntax like `foo\\|bar` would silently match nothing.
|
|
887
|
+
`\\|` `\\(` `\\)` would mean the *literal* character — so a caller writing
|
|
888
|
+
default-grep syntax like `foo\\|bar` would silently match nothing. Both
|
|
889
|
+
dialects intend an operator there, so treat the escape as the operator;
|
|
890
|
+
`-F` remains the way to match the character literally."""
|
|
891
|
+
output: list[str] = []
|
|
867
892
|
index = 0
|
|
868
|
-
while index < len(pattern)
|
|
869
|
-
|
|
870
|
-
|
|
893
|
+
while index < len(pattern):
|
|
894
|
+
char = pattern[index]
|
|
895
|
+
if char == "\\" and index + 1 < len(pattern):
|
|
896
|
+
escaped = pattern[index + 1]
|
|
897
|
+
if escaped in "|()":
|
|
898
|
+
output.append(escaped)
|
|
899
|
+
else:
|
|
900
|
+
output.append(char)
|
|
901
|
+
output.append(escaped)
|
|
902
|
+
index += 2
|
|
871
903
|
continue
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
f"pattern escape \\{escaped} is BRE syntax; patterns here are extended "
|
|
876
|
-
f"regex — use {escaped} as the operator, or -F to match text literally",
|
|
877
|
-
exit_code=2,
|
|
878
|
-
)
|
|
879
|
-
index += 2
|
|
904
|
+
output.append(char)
|
|
905
|
+
index += 1
|
|
906
|
+
return "".join(output)
|
|
880
907
|
|
|
881
908
|
|
|
882
909
|
def _grep_text(
|
|
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
|
|
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
|
{stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/claude/scripts/on_session_start.py
RENAMED
|
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
|
{stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/cursor/scripts/on_agent_response.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
{stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/cursor/scripts/on_session_start.py
RENAMED
|
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
|
{stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/gemini/scripts/on_session_start.py
RENAMED
|
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
|
{stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/hermes/scripts/on_session_start.py
RENAMED
|
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
|
{stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/openclaw/scripts/on_session_end.py
RENAMED
|
File without changes
|
{stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/openclaw/scripts/on_session_start.py
RENAMED
|
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
|
{stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/opencode/scripts/on_session_end.py
RENAMED
|
File without changes
|
{stashai-0.1.332 → stashai-0.1.334}/stashai/plugin/assets/opencode/scripts/on_session_start.py
RENAMED
|
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
|