simplify(compat): tool_search/skills_sync_client — drop 22 re-exports, repoint 5 callers + 5 test files; drop stale re-export prose in web_tools_truncate/file_operations_lint
This commit is contained in:
@@ -3761,7 +3761,8 @@ class HermesCLI(CLIAgentSetupMixin, CLICommandsMixin, CLIBillingMixin, CLITuiMix
|
||||
# the token (only issued for multi-member orgs), so a solo account never hits
|
||||
# the network here. Both fail-quiet.
|
||||
try:
|
||||
from tools.skills_sync_client import maybe_pull_org_skills, maybe_pull_skills
|
||||
from tools.skills_sync_client import maybe_pull_skills
|
||||
from tools.skills_sync_client_org import maybe_pull_org_skills
|
||||
except Exception:
|
||||
return
|
||||
for pull in (maybe_pull_skills, maybe_pull_org_skills):
|
||||
|
||||
+1
-1
@@ -4439,7 +4439,7 @@ def _housekeeping_skill_sync() -> None:
|
||||
|
||||
def _housekeeping_org_skill_sync() -> None:
|
||||
"""Gated on real org membership (the token must carry an org role): solo accounts never reach the network."""
|
||||
from tools.skills_sync_client import maybe_pull_org_skills
|
||||
from tools.skills_sync_client_org import maybe_pull_org_skills
|
||||
maybe_pull_org_skills()
|
||||
|
||||
|
||||
|
||||
@@ -1,8 +1,7 @@
|
||||
"""Interactive messaging-platform setup wizards: WhatsApp (bridge + Cloud API), Slack manifest,
|
||||
Skill Sync.
|
||||
|
||||
Split out of ``hermes_cli/main.py``, which re-imports every name (``hermes_cli.main.<name>`` keeps
|
||||
resolving and monkeypatching). Names that stay in main are imported lazily at call time.
|
||||
Split out of ``hermes_cli/main.py``. Names that still live in main are imported lazily at call time.
|
||||
"""
|
||||
|
||||
import contextlib
|
||||
@@ -246,9 +245,10 @@ def _sync_device(args, ssc) -> int:
|
||||
|
||||
|
||||
def _sync_propose(args, ssc) -> int:
|
||||
from tools.skills_sync_client_org import propose_skill
|
||||
name = args.name
|
||||
try:
|
||||
result = ssc.propose_skill(name, message=args.message)
|
||||
result = propose_skill(name, message=args.message)
|
||||
except ssc.SyncInertError as e:
|
||||
_err(f"cannot share this skill: {e}")
|
||||
return 1
|
||||
@@ -310,7 +310,8 @@ def _sync_pull(ssc, identity):
|
||||
result = ssc.pull_skills(identity=identity)
|
||||
# Refresh the org mirror too when this account belongs to an organisation (no-op
|
||||
# otherwise), so one pull covers both.
|
||||
org_result = ssc.maybe_pull_org_skills()
|
||||
from tools.skills_sync_client_org import maybe_pull_org_skills
|
||||
org_result = maybe_pull_org_skills()
|
||||
if org_result:
|
||||
n = len(org_result.get("updated") or [])
|
||||
_err(f"org: refreshed {n} shared skill(s) from your organisation.")
|
||||
|
||||
@@ -334,42 +334,43 @@ class TestLocalEditsSurviveOrgUpdates:
|
||||
monkeypatch.setattr(
|
||||
ssc, "_org_dir", lambda: skills / sku.ORG_MIRROR_DIR_NAME
|
||||
)
|
||||
return ssc, skills, d
|
||||
from tools import skills_sync_client_org as org
|
||||
return org, skills, d
|
||||
|
||||
def test_unmodified_skill_is_not_flagged(self, tmp_path, monkeypatch):
|
||||
ssc, _skills, d = self._mirror(tmp_path, monkeypatch)
|
||||
ssc._write_org_baseline(
|
||||
org, _skills, d = self._mirror(tmp_path, monkeypatch)
|
||||
org._write_org_baseline(
|
||||
"org-1",
|
||||
{"shared-x": {"fingerprint": ssc._skill_dir_fingerprint(d), "tree": "t1"}},
|
||||
{"shared-x": {"fingerprint": org._skill_dir_fingerprint(d), "tree": "t1"}},
|
||||
)
|
||||
assert ssc.org_skill_is_locally_modified("shared-x", "org-1") is False
|
||||
assert ssc.list_locally_modified_org_skills("org-1") == []
|
||||
assert org.org_skill_is_locally_modified("shared-x", "org-1") is False
|
||||
assert org.list_locally_modified_org_skills("org-1") == []
|
||||
|
||||
def test_edited_skill_is_detected(self, tmp_path, monkeypatch):
|
||||
ssc, _skills, d = self._mirror(tmp_path, monkeypatch)
|
||||
ssc._write_org_baseline(
|
||||
org, _skills, d = self._mirror(tmp_path, monkeypatch)
|
||||
org._write_org_baseline(
|
||||
"org-1",
|
||||
{"shared-x": {"fingerprint": ssc._skill_dir_fingerprint(d), "tree": "t1"}},
|
||||
{"shared-x": {"fingerprint": org._skill_dir_fingerprint(d), "tree": "t1"}},
|
||||
)
|
||||
(d / "SKILL.md").write_text("---\nname: shared-x\n---\nEDITED\n", encoding="utf-8")
|
||||
assert ssc.org_skill_is_locally_modified("shared-x", "org-1") is True
|
||||
assert ssc.list_locally_modified_org_skills("org-1") == ["shared-x"]
|
||||
assert org.org_skill_is_locally_modified("shared-x", "org-1") is True
|
||||
assert org.list_locally_modified_org_skills("org-1") == ["shared-x"]
|
||||
|
||||
def test_missing_baseline_does_not_cry_wolf(self, tmp_path, monkeypatch):
|
||||
ssc, _skills, _d = self._mirror(tmp_path, monkeypatch)
|
||||
org, _skills, _d = self._mirror(tmp_path, monkeypatch)
|
||||
# Mirror pulled before baselines existed — must not be reported as
|
||||
# modified (that would block every update with a phantom conflict).
|
||||
assert ssc.org_skill_is_locally_modified("shared-x", "org-1") is False
|
||||
assert org.org_skill_is_locally_modified("shared-x", "org-1") is False
|
||||
|
||||
def test_fingerprint_is_content_based_not_mtime(self, tmp_path, monkeypatch):
|
||||
import os
|
||||
import time
|
||||
|
||||
ssc, _skills, d = self._mirror(tmp_path, monkeypatch)
|
||||
before = ssc._skill_dir_fingerprint(d)
|
||||
org, _skills, d = self._mirror(tmp_path, monkeypatch)
|
||||
before = org._skill_dir_fingerprint(d)
|
||||
time.sleep(0.01)
|
||||
os.utime(d / "SKILL.md", None) # touch: mtime changes, content doesn't
|
||||
assert ssc._skill_dir_fingerprint(d) == before
|
||||
assert org._skill_dir_fingerprint(d) == before
|
||||
|
||||
def test_auto_propose_defaults_off(self, monkeypatch):
|
||||
from tools import skills_sync_client as ssc
|
||||
|
||||
@@ -28,7 +28,8 @@ from types import SimpleNamespace
|
||||
import pytest
|
||||
|
||||
from agent.tool_dispatch_helpers import _plan_tool_batch_segments
|
||||
from tools.tool_search import _short_desc, build_catalog, search_catalog
|
||||
from tools.tool_search import build_catalog, search_catalog
|
||||
from tools.tool_search_catalog import _short_desc
|
||||
|
||||
|
||||
def _tc(name, arguments="{}", call_id=None):
|
||||
@@ -300,7 +301,7 @@ class TestSourceNameIndexing:
|
||||
# Compare in token space: the tokenizer may stem (e.g.
|
||||
# "catalogsource" -> "catalogsourc"), and the contract is that
|
||||
# the label lands in the document exactly once either way.
|
||||
from tools.tool_search import _tokenize
|
||||
from tools.tool_search_catalog import _tokenize
|
||||
label_token = _tokenize(source_label)[0]
|
||||
tokens_by_name = {entry.name: entry._tokens for entry in catalog}
|
||||
assert tokens_by_name[names[0]].count(label_token) == 1
|
||||
|
||||
@@ -22,6 +22,8 @@ from pathlib import Path
|
||||
import pytest
|
||||
|
||||
import tools.skills_sync_client as ssc
|
||||
import tools.skills_sync_client_org as org
|
||||
import tools.skills_sync_client_wire as wire
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -268,7 +270,7 @@ def _jwt(claims: dict) -> str:
|
||||
|
||||
class TestAddressing:
|
||||
def test_full_64_hex_address(self):
|
||||
addr = ssc.wire_address(b"")
|
||||
addr = wire.wire_address(b"")
|
||||
# sha256 of empty is the well-known e3b0... digest, full 64 hex.
|
||||
assert addr == (
|
||||
"sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"
|
||||
@@ -278,21 +280,21 @@ class TestAddressing:
|
||||
def test_address_differs_from_local_truncated_namespace(self):
|
||||
# The wire full-64-hex must NOT equal the local truncated 16-hex form.
|
||||
data = b"hello world"
|
||||
full = ssc.wire_address(data)
|
||||
full = wire.wire_address(data)
|
||||
truncated = "sha256:" + hashlib.sha256(data).hexdigest()[:16]
|
||||
assert full != truncated
|
||||
assert len(full.split(":")[1]) == 64
|
||||
assert len(truncated.split(":")[1]) == 16
|
||||
|
||||
def test_canonical_json_sorted_no_whitespace(self):
|
||||
out = ssc.canonical_json_bytes({"b": 1, "a": 2})
|
||||
out = wire.canonical_json_bytes({"b": 1, "a": 2})
|
||||
assert out == b'{"a":2,"b":1}'
|
||||
assert b" " not in out
|
||||
assert not out.endswith(b"\n")
|
||||
|
||||
def test_canonical_json_stable(self):
|
||||
obj = {"type": "tree", "entries": [{"name": "x", "hash": "sha256:aa"}]}
|
||||
assert ssc.canonical_json_bytes(obj) == ssc.canonical_json_bytes(dict(obj))
|
||||
assert wire.canonical_json_bytes(obj) == wire.canonical_json_bytes(dict(obj))
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -366,11 +368,11 @@ class TestObjectBuilding:
|
||||
assert tree_hash.startswith("sha256:")
|
||||
# tree object present and canonical
|
||||
kind, data = objects.objects[tree_hash]
|
||||
assert kind == ssc.KIND_TREE
|
||||
assert kind == wire.KIND_TREE
|
||||
tree = json.loads(data)
|
||||
entries = {e["name"]: e for e in tree["entries"]}
|
||||
assert entries["SKILL.md"]["mode"] == ssc.MODE_FILE
|
||||
assert entries["run.sh"]["mode"] == ssc.MODE_EXEC
|
||||
assert entries["SKILL.md"]["mode"] == wire.MODE_FILE
|
||||
assert entries["run.sh"]["mode"] == wire.MODE_EXEC
|
||||
# entries sorted by name (byte order)
|
||||
names = [e["name"] for e in tree["entries"]]
|
||||
assert names == sorted(names)
|
||||
@@ -494,14 +496,14 @@ class TestEndToEnd:
|
||||
client = ssc.SyncClient(base, "tok")
|
||||
caps = client.capabilities()
|
||||
assert caps["hsp_version"] == "1"
|
||||
ssc._check_version(caps) # no raise
|
||||
wire._check_version(caps) # no raise
|
||||
|
||||
def test_version_mismatch_raises(self, mock_server):
|
||||
base, state = mock_server
|
||||
state.hsp_version = "2"
|
||||
client = ssc.SyncClient(base, "tok")
|
||||
with pytest.raises(ssc.SyncError):
|
||||
ssc._check_version(client.capabilities())
|
||||
wire._check_version(client.capabilities())
|
||||
|
||||
def test_push_uploads_and_cas(self, mock_server, synced_env):
|
||||
base, state = mock_server
|
||||
@@ -514,7 +516,7 @@ class TestEndToEnd:
|
||||
assert head == result["head"]
|
||||
# commit object is present and well-formed
|
||||
kind, data = state.objects[head]
|
||||
assert kind == ssc.KIND_COMMIT
|
||||
assert kind == wire.KIND_COMMIT
|
||||
commit = json.loads(data)
|
||||
assert commit["author"]["owner"] == "owner1"
|
||||
assert commit["parents"] == [] # first commit
|
||||
@@ -636,7 +638,7 @@ class TestOptInFlag:
|
||||
class TestSyncManifest:
|
||||
def test_build_parse_roundtrip(self):
|
||||
data = ssc.build_sync_manifest_bytes({"beta": True, "alpha": False})
|
||||
parsed = ssc.parse_sync_manifest(data)
|
||||
parsed = wire.parse_sync_manifest(data)
|
||||
assert parsed == {"alpha": False, "beta": True}
|
||||
|
||||
def test_manifest_wire_shape(self):
|
||||
@@ -654,18 +656,18 @@ class TestSyncManifest:
|
||||
|
||||
def test_parse_rejects_malformed(self):
|
||||
# Strict: unknown type, bad version, non-array skills, malformed entry.
|
||||
assert ssc.parse_sync_manifest(b"not json") is None
|
||||
assert ssc.parse_sync_manifest(b'{"type":"nope","version":1,"skills":[]}') is None
|
||||
assert ssc.parse_sync_manifest(b'{"type":"sync-manifest","version":2,"skills":[]}') is None
|
||||
assert ssc.parse_sync_manifest(b'{"type":"sync-manifest","version":1,"skills":{}}') is None
|
||||
assert wire.parse_sync_manifest(b"not json") is None
|
||||
assert wire.parse_sync_manifest(b'{"type":"nope","version":1,"skills":[]}') is None
|
||||
assert wire.parse_sync_manifest(b'{"type":"sync-manifest","version":2,"skills":[]}') is None
|
||||
assert wire.parse_sync_manifest(b'{"type":"sync-manifest","version":1,"skills":{}}') is None
|
||||
assert (
|
||||
ssc.parse_sync_manifest(
|
||||
wire.parse_sync_manifest(
|
||||
b'{"type":"sync-manifest","version":1,"skills":[{"name":"x"}]}'
|
||||
)
|
||||
is None
|
||||
)
|
||||
# A malformed manifest must NOT be mistaken for "no skills opted in".
|
||||
assert ssc.parse_sync_manifest(b'{"type":"sync-manifest","version":1,"skills":[]}') == {}
|
||||
assert wire.parse_sync_manifest(b'{"type":"sync-manifest","version":1,"skills":[]}') == {}
|
||||
|
||||
def test_snapshot_embeds_manifest_root_blob(self, mock_server, synced_env):
|
||||
# snapshot_profile must add a root-level `sync-manifest` blob recording
|
||||
@@ -914,7 +916,7 @@ class TestOrgEndToEnd:
|
||||
home, skills, identity = synced_env
|
||||
identity = {**identity, "org_id": "org-1", "org_role": "ADMIN"}
|
||||
client = ssc.SyncClient(base, identity["api_key"])
|
||||
result = ssc.propose_skill("alpha", client, identity=identity)
|
||||
result = org.propose_skill("alpha", client, identity=identity)
|
||||
assert result["ok"] is True
|
||||
assert result.get("merged") is True
|
||||
head = state.refs["refs/org/org-1/HEAD"]
|
||||
@@ -930,7 +932,7 @@ class TestOrgEndToEnd:
|
||||
# Seed an org HEAD as admin first.
|
||||
admin_ident = {**identity, "org_id": "org-1", "org_role": "ADMIN"}
|
||||
client = ssc.SyncClient(base, identity["api_key"])
|
||||
seeded = ssc.propose_skill("alpha", client, identity=admin_ident)
|
||||
seeded = org.propose_skill("alpha", client, identity=admin_ident)
|
||||
|
||||
# Member edits beta and proposes: server converts to 202.
|
||||
state.org_role_admin = False
|
||||
@@ -938,7 +940,7 @@ class TestOrgEndToEnd:
|
||||
"---\nname: beta\n---\nbeta v2 member edit\n", encoding="utf-8"
|
||||
)
|
||||
member_ident = {**identity, "org_id": "org-1", "org_role": "MEMBER"}
|
||||
result = ssc.propose_skill("beta", client, identity=member_ident)
|
||||
result = org.propose_skill("beta", client, identity=member_ident)
|
||||
assert result["ok"] is True
|
||||
assert result.get("proposal_pending") is True
|
||||
assert result["proposal_id"] == 1
|
||||
@@ -955,12 +957,12 @@ class TestOrgEndToEnd:
|
||||
home, skills, identity = synced_env
|
||||
admin_ident = {**identity, "org_id": "org-1", "org_role": "ADMIN"}
|
||||
client = ssc.SyncClient(base, identity["api_key"])
|
||||
ssc.propose_skill("alpha", client, identity=admin_ident)
|
||||
ssc.propose_skill("beta", client, identity=admin_ident)
|
||||
org.propose_skill("alpha", client, identity=admin_ident)
|
||||
org.propose_skill("beta", client, identity=admin_ident)
|
||||
|
||||
state.org_role_admin = False
|
||||
member_ident = {**identity, "org_id": "org-1", "org_role": "MEMBER"}
|
||||
result = ssc.propose_skill("alpha", client, identity=member_ident)
|
||||
result = org.propose_skill("alpha", client, identity=member_ident)
|
||||
# Walk the proposed commit's root: both skills present.
|
||||
commit = json.loads(state.org_objects[result["commit"]][1])
|
||||
root = json.loads(state.org_objects[commit["tree"]][1])
|
||||
@@ -972,9 +974,9 @@ class TestOrgEndToEnd:
|
||||
home, skills, identity = synced_env
|
||||
admin_ident = {**identity, "org_id": "org-1", "org_role": "ADMIN"}
|
||||
client = ssc.SyncClient(base, identity["api_key"])
|
||||
ssc.propose_skill("alpha", client, identity=admin_ident)
|
||||
org.propose_skill("alpha", client, identity=admin_ident)
|
||||
|
||||
result = ssc.pull_org_skills(client, identity=admin_ident)
|
||||
result = org.pull_org_skills(client, identity=admin_ident)
|
||||
assert result["ok"] is True
|
||||
assert "alpha" in result["updated"]
|
||||
mirrored = skills / "_org" / "org-1" / "alpha" / "SKILL.md"
|
||||
@@ -986,7 +988,7 @@ class TestOrgEndToEnd:
|
||||
home, skills, identity = synced_env
|
||||
ident = {**identity, "org_id": "org-1", "org_role": "MEMBER"}
|
||||
client = ssc.SyncClient(base, identity["api_key"])
|
||||
result = ssc.pull_org_skills(client, identity=ident)
|
||||
result = org.pull_org_skills(client, identity=ident)
|
||||
assert result["ok"] is True
|
||||
assert result["head"] is None
|
||||
assert result["updated"] == []
|
||||
@@ -998,7 +1000,7 @@ class TestOrgEndToEnd:
|
||||
ident = {**identity, "org_id": "org-1", "org_role": "ADMIN"}
|
||||
client = ssc.SyncClient(base, identity["api_key"])
|
||||
with pytest.raises(ssc.SyncInertError):
|
||||
ssc.propose_skill("alpha", client, identity=ident)
|
||||
org.propose_skill("alpha", client, identity=ident)
|
||||
|
||||
def test_maybe_pull_org_inert_without_role(self, monkeypatch):
|
||||
# Personal org: no org_role claim -> None, never raises.
|
||||
@@ -1006,7 +1008,7 @@ class TestOrgEndToEnd:
|
||||
import hermes_cli.auth as auth_mod
|
||||
monkeypatch.setattr(auth_mod, "resolve_nous_runtime_credentials",
|
||||
lambda **kw: {"api_key": token})
|
||||
assert ssc.maybe_pull_org_skills() is None
|
||||
assert org.maybe_pull_org_skills() is None
|
||||
|
||||
|
||||
class TestOrgEndpointScoping:
|
||||
@@ -1048,12 +1050,12 @@ class TestOrgEndpointScoping:
|
||||
client = ssc.SyncClient(base, identity["api_key"])
|
||||
|
||||
# `synced_env` already seeds alpha and beta (beta under devops/).
|
||||
first = ssc.propose_skill("alpha", client, identity=admin)
|
||||
first = org.propose_skill("alpha", client, identity=admin)
|
||||
assert first["ok"] is True
|
||||
|
||||
# Previously: base_head read as None -> CAS from None -> 409 ->
|
||||
# SyncConflict escaped to the caller.
|
||||
second = ssc.propose_skill("beta", client, identity=admin)
|
||||
second = org.propose_skill("beta", client, identity=admin)
|
||||
assert second["ok"] is True
|
||||
|
||||
# And the splice preserved the first skill rather than replacing it.
|
||||
@@ -1074,9 +1076,9 @@ class TestOrgEndpointScoping:
|
||||
home, skills, identity = synced_env
|
||||
admin = self._admin(identity)
|
||||
client = ssc.SyncClient(base, identity["api_key"])
|
||||
ssc.propose_skill("alpha", client, identity=admin)
|
||||
org.propose_skill("alpha", client, identity=admin)
|
||||
|
||||
result = ssc.pull_org_skills(client=client, identity=admin)
|
||||
result = org.pull_org_skills(client=client, identity=admin)
|
||||
assert result["ok"] is True
|
||||
assert result["head"] == state.refs["refs/org/org-1/HEAD"], (
|
||||
"pull must resolve the real org HEAD, not None"
|
||||
|
||||
@@ -254,7 +254,8 @@ class TestThresholdGate:
|
||||
class TestRetrieval:
|
||||
def _fake_catalog(self):
|
||||
"""Build a catalog directly without touching the registry."""
|
||||
from tools.tool_search import CatalogEntry, _tokenize, _entry_search_text
|
||||
from tools.tool_search import CatalogEntry
|
||||
from tools.tool_search_catalog import _tokenize, _entry_search_text
|
||||
defs = [
|
||||
_td("github_create_issue", "Open a new issue in a GitHub repository",
|
||||
{"title": {"type": "string"}, "body": {"type": "string"}}),
|
||||
@@ -644,7 +645,7 @@ class TestCatalogListing:
|
||||
assert result.listing_form in {"names", "groups", "mixed"}
|
||||
|
||||
def test_short_desc_first_sentence_and_clip(self):
|
||||
from tools.tool_search import _short_desc
|
||||
from tools.tool_search_catalog import _short_desc
|
||||
assert _short_desc("Open an issue. Second sentence dropped.") == "Open an issue."
|
||||
long = "word " * 40
|
||||
s = _short_desc(long)
|
||||
|
||||
@@ -64,7 +64,7 @@ def issue_defs():
|
||||
|
||||
class TestStemming:
|
||||
def test_tokenize_stems_index_and_query_identically(self):
|
||||
from tools.tool_search import _tokenize
|
||||
from tools.tool_search_catalog import _tokenize
|
||||
# Same stem on both sides is the whole contract.
|
||||
assert _tokenize("issues") == _tokenize("issue")
|
||||
assert _tokenize("creating messages") == _tokenize("create message")
|
||||
@@ -87,7 +87,7 @@ class TestStemming:
|
||||
assert names == ["mq_slack_post_message"]
|
||||
|
||||
def test_single_token_stems_are_cached(self):
|
||||
from tools.tool_search import _stem, _tokenize
|
||||
from tools.tool_search_catalog import _stem, _tokenize
|
||||
|
||||
_stem.cache_clear()
|
||||
corpus = "issues creating issues creating"
|
||||
@@ -102,12 +102,11 @@ class TestStemming:
|
||||
def test_parallel_tokenize_search_and_dispatch_are_deterministic(self, issue_defs):
|
||||
from tools.tool_search import (
|
||||
ToolSearchConfig,
|
||||
_stem,
|
||||
_tokenize,
|
||||
build_catalog,
|
||||
dispatch_tool_search,
|
||||
search_catalog,
|
||||
)
|
||||
from tools.tool_search_catalog import _stem, _tokenize
|
||||
|
||||
corpus = (
|
||||
"issues",
|
||||
@@ -174,7 +173,7 @@ class TestStemming:
|
||||
concurrently on the underlying per-thread instances. A shared
|
||||
stemmer's mutable parse state produces wrong stems or raises here.
|
||||
"""
|
||||
from tools.tool_search import _stem
|
||||
from tools.tool_search_catalog import _stem
|
||||
|
||||
words = ["issues", "creating", "meetings", "categories", "searching"]
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
"""Syntax-lint and LSP-diagnostics tier for ``tools.file_operations``.
|
||||
|
||||
``ShellFileOperations`` inherits ``LintMixin``; module constants and in-process
|
||||
linters are pure and re-imported into ``tools.file_operations`` for back-compat.
|
||||
linters are pure functions importable from this module.
|
||||
"""
|
||||
|
||||
import ast
|
||||
|
||||
@@ -279,7 +279,8 @@ def _maybe_auto_propose_org_edit(name: str, skill_path: Path) -> Optional[str]:
|
||||
f"This skill is shared by your organisation. Your edit is "
|
||||
f"saved locally and will not be overwritten by org updates. "
|
||||
f"Run `hermes sync propose {name}` to share it back.")
|
||||
result = ssc.propose_skill(name)
|
||||
from tools.skills_sync_client_org import propose_skill
|
||||
result = propose_skill(name)
|
||||
if result.get("proposal_pending"):
|
||||
return (
|
||||
f"Auto-proposed to your organisation as proposal "
|
||||
|
||||
@@ -17,12 +17,13 @@ from contextlib import suppress
|
||||
from pathlib import Path, PurePosixPath
|
||||
from typing import Any, Callable, Dict, List, Optional, Tuple
|
||||
|
||||
from tools.skills_sync_client_wire import ( # noqa: F401 (re-exports)
|
||||
DEFAULT_MAX_OBJECT_BYTES, KIND_BLOB, KIND_COMMIT, KIND_TREE, MODE_EXEC, MODE_FILE, ObjectSet,
|
||||
SyncClient, SyncConflict, SyncError, _check_version, assemble_root_from_skill_trees, build_commit,
|
||||
build_root_tree, build_sync_manifest_bytes, build_tree, canonical_json_bytes, checked_capabilities,
|
||||
materialize_tree, merge_skill, nest_skill_tree, parse_sync_manifest, read_manifest_of_root,
|
||||
read_ref_hash, root_tree_of_commit, skill_trees_of_root, wire_address)
|
||||
from tools.skills_sync_client_wire import (
|
||||
DEFAULT_MAX_OBJECT_BYTES, KIND_BLOB, ObjectSet, SyncClient, SyncConflict, SyncError,
|
||||
assemble_root_from_skill_trees, build_commit, build_root_tree, build_sync_manifest_bytes, build_tree,
|
||||
checked_capabilities, materialize_tree, merge_skill, nest_skill_tree, read_manifest_of_root,
|
||||
read_ref_hash, root_tree_of_commit, skill_trees_of_root)
|
||||
from tools.skills_sync_client_org import (
|
||||
ORG_DIR_NAME, list_locally_modified_org_skills, list_org_skill_names, resolve_org_identity)
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
# Gate claim (NAS's wire name; means "Nous admin" / Permissions.ADMIN_ACCESS). The bearer comes
|
||||
@@ -495,10 +496,3 @@ def sync_status() -> Dict[str, Any]:
|
||||
except Exception as e:
|
||||
logger.debug("skills_sync_client: sync_status org lookup failed: %s", e)
|
||||
return status
|
||||
|
||||
|
||||
# Imported last: skills_sync_client_org reads this module's state lazily.
|
||||
from tools.skills_sync_client_org import ( # noqa: E402,F401 (re-exports)
|
||||
ORG_DIR_NAME, _skill_dir_fingerprint, _write_org_baseline, list_locally_modified_org_skills,
|
||||
list_org_skill_names, maybe_pull_org_skills, org_skill_is_locally_modified, propose_skill,
|
||||
pull_org_skills, resolve_org_identity)
|
||||
|
||||
@@ -17,12 +17,11 @@ from dataclasses import dataclass
|
||||
from typing import Any, Dict, Iterable, List, Optional, Tuple
|
||||
|
||||
from tools.registry import tool_error
|
||||
from tools.tool_search_catalog import ( # noqa: F401 — re-exported public/test names
|
||||
from tools.tool_search_catalog import (
|
||||
BRIDGE_TOOL_NAMES, CHARS_PER_TOKEN, TOOL_CALL_NAME, TOOL_DESCRIBE_NAME, TOOL_SEARCH_NAME,
|
||||
CatalogEntry, _corpus_stats, _entry_search_text, _fn, _listing_group_label,
|
||||
_registry_entry, _registry_toolset, _short_desc, _stem, _tokenize, build_catalog,
|
||||
build_catalog_listing_with_form, search_catalog)
|
||||
from tools.tool_search_validation import validate_deferred_call_args # noqa: F401
|
||||
CatalogEntry, _corpus_stats, _fn, _listing_group_label, _registry_entry, _registry_toolset,
|
||||
build_catalog, build_catalog_listing_with_form, search_catalog)
|
||||
from tools.tool_search_validation import validate_deferred_call_args
|
||||
|
||||
logger = logging.getLogger("tools.tool_search")
|
||||
_MAX_QUERIES_PER_CALL = _MAX_DESCRIBE_NAMES_PER_CALL = 10 # bound the work one bridge call requests
|
||||
|
||||
@@ -14,13 +14,8 @@ logger = logging.getLogger("tools.web_tools")
|
||||
|
||||
# Per-page char budget sent to the model (override: web.extract_char_limit); larger pages are head+tail
|
||||
# truncated, full text stored on disk.
|
||||
# ─── Parallel / Tavily / Firecrawl helpers — moved into plugins ────────────── After PR #25182, the
|
||||
# per-vendor client construction, request helpers, and response normalizers all live in
|
||||
# plugins.web.<vendor>.provider: - parallel: plugins/web/parallel/provider.py - tavily:
|
||||
# plugins/web/tavily/provider.py - firecrawl: plugins/web/firecrawl/provider.py The names from the firecrawl
|
||||
# plugin (Firecrawl proxy, _get_firecrawl_client, _to_plain_object, _normalize_result_list,
|
||||
# _extract_web_search_results, _extract_scrape_payload, _is_tool_gateway_ready, etc.) are re-exported at the
|
||||
# top of this module for backward-compat with integration tests and unit-test patches.
|
||||
# Per-vendor client construction, request helpers and response normalizers live in
|
||||
# plugins.web.<vendor>.provider (parallel / tavily / firecrawl) since PR #25182.
|
||||
DEFAULT_EXTRACT_CHAR_LIMIT = 15000
|
||||
# Ceiling on the full-text file written to cache/web so a multi-MB page can't write unbounded bytes on
|
||||
# every extract; the model only ever sees char_limit.
|
||||
|
||||
Reference in New Issue
Block a user