lint fixes
This commit is contained in:
@@ -8,7 +8,6 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
if TYPE_CHECKING:
|
||||
|
||||
@@ -1338,8 +1338,8 @@ class AgentRunner:
|
||||
skills_catalog_prompt = ""
|
||||
protocols_prompt = ""
|
||||
try:
|
||||
from framework.skills.config import SkillsConfig
|
||||
from framework.skills.catalog import SkillCatalog
|
||||
from framework.skills.config import SkillsConfig
|
||||
from framework.skills.defaults import DefaultSkillManager
|
||||
from framework.skills.discovery import DiscoveryConfig, SkillDiscovery
|
||||
|
||||
@@ -1355,6 +1355,7 @@ class AgentRunner:
|
||||
|
||||
# Trust-gate project-scope skills (AS-13)
|
||||
from framework.skills.trust import TrustGate
|
||||
|
||||
discovered = TrustGate(interactive=self._interactive).filter_and_gate(
|
||||
discovered, project_dir=self.agent_path
|
||||
)
|
||||
|
||||
@@ -11,7 +11,7 @@ from framework.skills.defaults import DefaultSkillManager
|
||||
from framework.skills.discovery import DiscoveryConfig, SkillDiscovery
|
||||
from framework.skills.models import TrustStatus
|
||||
from framework.skills.parser import ParsedSkill, parse_skill_md
|
||||
from framework.skills.trust import TrustGate, TrustedRepoStore
|
||||
from framework.skills.trust import TrustedRepoStore, TrustGate
|
||||
|
||||
__all__ = [
|
||||
"DefaultSkillConfig",
|
||||
|
||||
@@ -65,9 +65,7 @@ class SkillCatalog:
|
||||
"""
|
||||
# Filter out framework-scope skills (default skills) — they're
|
||||
# injected via the protocols prompt, not the catalog
|
||||
community_skills = [
|
||||
s for s in self._skills.values() if s.source_scope != "framework"
|
||||
]
|
||||
community_skills = [s for s in self._skills.values() if s.source_scope != "framework"]
|
||||
|
||||
if not community_skills:
|
||||
return ""
|
||||
|
||||
@@ -20,9 +20,7 @@ def register_skill_commands(subparsers) -> None:
|
||||
skill_sub = skill_parser.add_subparsers(dest="skill_command", required=True)
|
||||
|
||||
# hive skill list
|
||||
list_parser = skill_sub.add_parser(
|
||||
"list", help="List discovered skills across all scopes"
|
||||
)
|
||||
list_parser = skill_sub.add_parser("list", help="List discovered skills across all scopes")
|
||||
list_parser.add_argument(
|
||||
"--project-dir",
|
||||
default=None,
|
||||
@@ -117,6 +115,6 @@ def cmd_skill_trust(args) -> int:
|
||||
store.trust(repo_key, project_path=str(project_path))
|
||||
|
||||
print(f"✓ Trusted: {repo_key}")
|
||||
print(f" Stored in ~/.hive/trusted_repos.json")
|
||||
print(f" Skills from this repository will load without prompting in future runs.")
|
||||
print(" Stored in ~/.hive/trusted_repos.json")
|
||||
print(" Skills from this repository will load without prompting in future runs.")
|
||||
return 0
|
||||
|
||||
@@ -75,7 +75,8 @@ class SkillsConfig:
|
||||
"""Build config from agent module-level variables.
|
||||
|
||||
Args:
|
||||
default_skills: Dict from agent module (e.g. ``{"hive.note-taking": {"enabled": True}}``)
|
||||
default_skills: Dict from agent module
|
||||
(e.g. ``{"hive.note-taking": {"enabled": True}}``)
|
||||
skills: List of pre-activated skill names from agent module
|
||||
"""
|
||||
all_disabled = False
|
||||
|
||||
@@ -7,25 +7,26 @@ locations. Resolves name collisions deterministically.
|
||||
from __future__ import annotations
|
||||
|
||||
import logging
|
||||
from dataclasses import dataclass, field
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
from framework.skills.parser import ParsedSkill, parse_skill_md
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Directories to skip during scanning
|
||||
_SKIP_DIRS = frozenset({
|
||||
".git",
|
||||
"node_modules",
|
||||
"__pycache__",
|
||||
".venv",
|
||||
"venv",
|
||||
".mypy_cache",
|
||||
".pytest_cache",
|
||||
".ruff_cache",
|
||||
})
|
||||
_SKIP_DIRS = frozenset(
|
||||
{
|
||||
".git",
|
||||
"node_modules",
|
||||
"__pycache__",
|
||||
".venv",
|
||||
"venv",
|
||||
".mypy_cache",
|
||||
".pytest_cache",
|
||||
".ruff_cache",
|
||||
}
|
||||
)
|
||||
|
||||
# Scope priority (higher = takes precedence)
|
||||
_SCOPE_PRIORITY = {
|
||||
|
||||
@@ -9,7 +9,7 @@ from __future__ import annotations
|
||||
|
||||
import logging
|
||||
import re
|
||||
from dataclasses import dataclass, field
|
||||
from dataclasses import dataclass
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
|
||||
@@ -52,9 +52,7 @@ def _try_fix_yaml(raw: str) -> str:
|
||||
if m:
|
||||
key_part, value_part = m.group(1), m.group(2)
|
||||
# If value contains a colon and isn't already quoted
|
||||
if ":" in value_part and not (
|
||||
value_part.startswith('"') or value_part.startswith("'")
|
||||
):
|
||||
if ":" in value_part and not (value_part.startswith('"') or value_part.startswith("'")):
|
||||
value_part = f'"{value_part}"'
|
||||
fixed.append(f"{key_part}{value_part}")
|
||||
else:
|
||||
|
||||
@@ -14,7 +14,7 @@ import logging
|
||||
import subprocess
|
||||
import sys
|
||||
from collections.abc import Callable
|
||||
from dataclasses import dataclass, field
|
||||
from dataclasses import dataclass
|
||||
from datetime import UTC, datetime
|
||||
from enum import StrEnum
|
||||
from pathlib import Path
|
||||
@@ -158,9 +158,7 @@ class ProjectTrustDetector:
|
||||
def __init__(self, store: TrustedRepoStore | None = None) -> None:
|
||||
self._store = store or TrustedRepoStore()
|
||||
|
||||
def classify(
|
||||
self, project_dir: Path | None
|
||||
) -> tuple[ProjectTrustClassification, str]:
|
||||
def classify(self, project_dir: Path | None) -> tuple[ProjectTrustClassification, str]:
|
||||
"""Return (classification, repo_key).
|
||||
|
||||
repo_key is empty string for ALWAYS_TRUSTED cases without a remote.
|
||||
@@ -398,7 +396,8 @@ class TrustGate:
|
||||
project_dir: Path | None,
|
||||
repo_key: str,
|
||||
) -> str:
|
||||
"""Show the security notice (once) and consent prompt. Return 'session' | 'permanent' | 'denied'."""
|
||||
"""Show the security notice (once) and consent prompt.
|
||||
Return 'session' | 'permanent' | 'denied'."""
|
||||
from framework.credentials.setup import Colors
|
||||
|
||||
if not sys.stdout.isatty():
|
||||
@@ -459,7 +458,10 @@ class TrustGate:
|
||||
p(" Options:")
|
||||
p(f" {Colors.CYAN}1){Colors.NC} Trust this session only")
|
||||
p(f" {Colors.CYAN}2){Colors.NC} Trust permanently — remember for future runs")
|
||||
p(f" {Colors.DIM}3) Deny — skip all project-scope skills from this repo{Colors.NC}")
|
||||
p(
|
||||
f" {Colors.DIM}3) Deny"
|
||||
f" — skip all project-scope skills from this repo{Colors.NC}"
|
||||
)
|
||||
p(f"{Colors.YELLOW}{'─' * 60}{Colors.NC}")
|
||||
|
||||
def _prompt_consent(self, Colors) -> str: # noqa: N803
|
||||
@@ -472,6 +474,4 @@ class TrustGate:
|
||||
return mapping[choice]
|
||||
except (KeyboardInterrupt, EOFError):
|
||||
return "denied"
|
||||
self._print(
|
||||
f"{Colors.RED}Invalid choice. Enter 1, 2, or 3.{Colors.NC}"
|
||||
)
|
||||
self._print(f"{Colors.RED}Invalid choice. Enter 1, 2, or 3.{Colors.NC}")
|
||||
|
||||
@@ -1,18 +1,20 @@
|
||||
"""Tests for default skills — parsing, token budget, and configuration."""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
import pytest
|
||||
|
||||
from framework.skills.config import DefaultSkillConfig, SkillsConfig
|
||||
from framework.skills.defaults import (
|
||||
SKILL_REGISTRY,
|
||||
SHARED_MEMORY_KEYS,
|
||||
SKILL_REGISTRY,
|
||||
DefaultSkillManager,
|
||||
)
|
||||
from framework.skills.parser import parse_skill_md
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
_DEFAULT_SKILLS_DIR = Path(__file__).resolve().parent.parent / "framework" / "skills" / "_default_skills"
|
||||
_DEFAULT_SKILLS_DIR = (
|
||||
Path(__file__).resolve().parent.parent / "framework" / "skills" / "_default_skills"
|
||||
)
|
||||
|
||||
|
||||
class TestDefaultSkillFiles:
|
||||
@@ -101,9 +103,7 @@ class TestDefaultSkillManager:
|
||||
assert len(manager.active_skill_names) == 5
|
||||
|
||||
def test_disable_all_via_convention(self):
|
||||
config = SkillsConfig.from_agent_vars(
|
||||
default_skills={"_all": {"enabled": False}}
|
||||
)
|
||||
config = SkillsConfig.from_agent_vars(default_skills={"_all": {"enabled": False}})
|
||||
manager = DefaultSkillManager(config)
|
||||
manager.load()
|
||||
|
||||
@@ -111,6 +111,7 @@ class TestDefaultSkillManager:
|
||||
|
||||
def test_log_active_skills(self, caplog):
|
||||
import logging
|
||||
|
||||
with caplog.at_level(logging.INFO, logger="framework.skills.defaults"):
|
||||
manager = DefaultSkillManager()
|
||||
manager.load()
|
||||
@@ -120,6 +121,7 @@ class TestDefaultSkillManager:
|
||||
|
||||
def test_log_all_disabled(self, caplog):
|
||||
import logging
|
||||
|
||||
config = SkillsConfig(all_defaults_disabled=True)
|
||||
with caplog.at_level(logging.INFO, logger="framework.skills.defaults"):
|
||||
manager = DefaultSkillManager(config)
|
||||
@@ -159,15 +161,11 @@ class TestSkillsConfig:
|
||||
assert config.skills == ["deep-research"]
|
||||
|
||||
def test_from_agent_vars_bool_shorthand(self):
|
||||
config = SkillsConfig.from_agent_vars(
|
||||
default_skills={"hive.note-taking": False}
|
||||
)
|
||||
config = SkillsConfig.from_agent_vars(default_skills={"hive.note-taking": False})
|
||||
assert config.is_default_enabled("hive.note-taking") is False
|
||||
|
||||
def test_from_agent_vars_all_disabled(self):
|
||||
config = SkillsConfig.from_agent_vars(
|
||||
default_skills={"_all": {"enabled": False}}
|
||||
)
|
||||
config = SkillsConfig.from_agent_vars(default_skills={"_all": {"enabled": False}})
|
||||
assert config.all_defaults_disabled is True
|
||||
|
||||
def test_get_default_overrides(self):
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
"""Tests for the skill catalog and prompt generation."""
|
||||
|
||||
import pytest
|
||||
|
||||
from framework.skills.catalog import SkillCatalog
|
||||
from framework.skills.parser import ParsedSkill
|
||||
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
"""Tests for skill discovery."""
|
||||
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
|
||||
from framework.skills.discovery import SkillDiscovery, DiscoveryConfig
|
||||
from framework.skills.discovery import DiscoveryConfig, SkillDiscovery
|
||||
|
||||
|
||||
def _write_skill(base: Path, name: str, description: str = "A test skill.") -> Path:
|
||||
@@ -24,11 +23,13 @@ class TestSkillDiscovery:
|
||||
_write_skill(agents_skills, "skill-a")
|
||||
_write_skill(agents_skills, "skill-b")
|
||||
|
||||
discovery = SkillDiscovery(DiscoveryConfig(
|
||||
project_root=tmp_path,
|
||||
skip_user_scope=True,
|
||||
skip_framework_scope=True,
|
||||
))
|
||||
discovery = SkillDiscovery(
|
||||
DiscoveryConfig(
|
||||
project_root=tmp_path,
|
||||
skip_user_scope=True,
|
||||
skip_framework_scope=True,
|
||||
)
|
||||
)
|
||||
skills = discovery.discover()
|
||||
|
||||
names = {s.name for s in skills}
|
||||
@@ -40,11 +41,13 @@ class TestSkillDiscovery:
|
||||
hive_skills = tmp_path / ".hive" / "skills"
|
||||
_write_skill(hive_skills, "hive-skill")
|
||||
|
||||
discovery = SkillDiscovery(DiscoveryConfig(
|
||||
project_root=tmp_path,
|
||||
skip_user_scope=True,
|
||||
skip_framework_scope=True,
|
||||
))
|
||||
discovery = SkillDiscovery(
|
||||
DiscoveryConfig(
|
||||
project_root=tmp_path,
|
||||
skip_user_scope=True,
|
||||
skip_framework_scope=True,
|
||||
)
|
||||
)
|
||||
skills = discovery.discover()
|
||||
|
||||
assert len(skills) == 1
|
||||
@@ -61,10 +64,12 @@ class TestSkillDiscovery:
|
||||
|
||||
monkeypatch.setattr(Path, "home", lambda: tmp_path / "home")
|
||||
|
||||
discovery = SkillDiscovery(DiscoveryConfig(
|
||||
project_root=tmp_path / "project",
|
||||
skip_framework_scope=True,
|
||||
))
|
||||
discovery = SkillDiscovery(
|
||||
DiscoveryConfig(
|
||||
project_root=tmp_path / "project",
|
||||
skip_framework_scope=True,
|
||||
)
|
||||
)
|
||||
skills = discovery.discover()
|
||||
|
||||
matching = [s for s in skills if s.name == "shared-skill"]
|
||||
@@ -80,11 +85,13 @@ class TestSkillDiscovery:
|
||||
hive_skills = tmp_path / ".hive" / "skills"
|
||||
_write_skill(hive_skills, "override-test", "Hive version")
|
||||
|
||||
discovery = SkillDiscovery(DiscoveryConfig(
|
||||
project_root=tmp_path,
|
||||
skip_user_scope=True,
|
||||
skip_framework_scope=True,
|
||||
))
|
||||
discovery = SkillDiscovery(
|
||||
DiscoveryConfig(
|
||||
project_root=tmp_path,
|
||||
skip_user_scope=True,
|
||||
skip_framework_scope=True,
|
||||
)
|
||||
)
|
||||
skills = discovery.discover()
|
||||
|
||||
matching = [s for s in skills if s.name == "override-test"]
|
||||
@@ -97,11 +104,13 @@ class TestSkillDiscovery:
|
||||
_write_skill(skills_dir / "node_modules", "npm-skill")
|
||||
_write_skill(skills_dir, "real-skill")
|
||||
|
||||
discovery = SkillDiscovery(DiscoveryConfig(
|
||||
project_root=tmp_path,
|
||||
skip_user_scope=True,
|
||||
skip_framework_scope=True,
|
||||
))
|
||||
discovery = SkillDiscovery(
|
||||
DiscoveryConfig(
|
||||
project_root=tmp_path,
|
||||
skip_user_scope=True,
|
||||
skip_framework_scope=True,
|
||||
)
|
||||
)
|
||||
skills = discovery.discover()
|
||||
|
||||
names = {s.name for s in skills}
|
||||
@@ -110,19 +119,23 @@ class TestSkillDiscovery:
|
||||
assert "npm-skill" not in names
|
||||
|
||||
def test_empty_scan(self, tmp_path):
|
||||
discovery = SkillDiscovery(DiscoveryConfig(
|
||||
project_root=tmp_path,
|
||||
skip_user_scope=True,
|
||||
skip_framework_scope=True,
|
||||
))
|
||||
discovery = SkillDiscovery(
|
||||
DiscoveryConfig(
|
||||
project_root=tmp_path,
|
||||
skip_user_scope=True,
|
||||
skip_framework_scope=True,
|
||||
)
|
||||
)
|
||||
skills = discovery.discover()
|
||||
assert skills == []
|
||||
|
||||
def test_framework_scope_loads_defaults(self):
|
||||
"""Framework scope should find the built-in default skills."""
|
||||
discovery = SkillDiscovery(DiscoveryConfig(
|
||||
skip_user_scope=True,
|
||||
))
|
||||
discovery = SkillDiscovery(
|
||||
DiscoveryConfig(
|
||||
skip_user_scope=True,
|
||||
)
|
||||
)
|
||||
skills = discovery.discover()
|
||||
|
||||
framework_skills = [s for s in skills if s.source_scope == "framework"]
|
||||
@@ -135,11 +148,13 @@ class TestSkillDiscovery:
|
||||
deep = tmp_path / ".agents" / "skills" / "a" / "b" / "c" / "d" / "e"
|
||||
_write_skill(deep, "too-deep")
|
||||
|
||||
discovery = SkillDiscovery(DiscoveryConfig(
|
||||
project_root=tmp_path,
|
||||
skip_user_scope=True,
|
||||
skip_framework_scope=True,
|
||||
max_depth=2,
|
||||
))
|
||||
discovery = SkillDiscovery(
|
||||
DiscoveryConfig(
|
||||
project_root=tmp_path,
|
||||
skip_user_scope=True,
|
||||
skip_framework_scope=True,
|
||||
max_depth=2,
|
||||
)
|
||||
)
|
||||
skills = discovery.discover()
|
||||
assert not any(s.name == "too-deep" for s in skills)
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
"""Integration tests for the skill system — prompt composition and backward compatibility."""
|
||||
|
||||
import pytest
|
||||
|
||||
from framework.graph.prompt_composer import compose_system_prompt
|
||||
from framework.skills.catalog import SkillCatalog
|
||||
from framework.skills.config import SkillsConfig
|
||||
@@ -129,11 +127,13 @@ class TestEndToEndPipeline:
|
||||
)
|
||||
|
||||
# Discovery
|
||||
discovery = SkillDiscovery(DiscoveryConfig(
|
||||
project_root=tmp_path,
|
||||
skip_user_scope=True,
|
||||
skip_framework_scope=True,
|
||||
))
|
||||
discovery = SkillDiscovery(
|
||||
DiscoveryConfig(
|
||||
project_root=tmp_path,
|
||||
skip_user_scope=True,
|
||||
skip_framework_scope=True,
|
||||
)
|
||||
)
|
||||
skills = discovery.discover()
|
||||
assert len(skills) == 1
|
||||
|
||||
@@ -162,11 +162,13 @@ class TestEndToEndPipeline:
|
||||
)
|
||||
|
||||
# Discover community skills
|
||||
discovery = SkillDiscovery(DiscoveryConfig(
|
||||
project_root=tmp_path,
|
||||
skip_user_scope=True,
|
||||
skip_framework_scope=True,
|
||||
))
|
||||
discovery = SkillDiscovery(
|
||||
DiscoveryConfig(
|
||||
project_root=tmp_path,
|
||||
skip_user_scope=True,
|
||||
skip_framework_scope=True,
|
||||
)
|
||||
)
|
||||
community_skills = discovery.discover()
|
||||
catalog = SkillCatalog(community_skills)
|
||||
catalog_prompt = catalog.to_prompt()
|
||||
@@ -199,11 +201,13 @@ class TestEndToEndPipeline:
|
||||
)
|
||||
|
||||
# Community skills
|
||||
discovery = SkillDiscovery(DiscoveryConfig(
|
||||
project_root=tmp_path,
|
||||
skip_user_scope=True,
|
||||
skip_framework_scope=True,
|
||||
))
|
||||
discovery = SkillDiscovery(
|
||||
DiscoveryConfig(
|
||||
project_root=tmp_path,
|
||||
skip_user_scope=True,
|
||||
skip_framework_scope=True,
|
||||
)
|
||||
)
|
||||
catalog = SkillCatalog(discovery.discover())
|
||||
|
||||
# Disabled defaults
|
||||
|
||||
@@ -1,20 +1,23 @@
|
||||
"""Tests for SKILL.md parser."""
|
||||
|
||||
import pytest
|
||||
from pathlib import Path
|
||||
|
||||
from framework.skills.parser import parse_skill_md, ParsedSkill
|
||||
import pytest
|
||||
|
||||
from framework.skills.parser import parse_skill_md
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def tmp_skill(tmp_path):
|
||||
"""Helper to create a SKILL.md file and return its path."""
|
||||
|
||||
def _create(content: str, dir_name: str = "my-skill") -> Path:
|
||||
skill_dir = tmp_path / dir_name
|
||||
skill_dir.mkdir(parents=True, exist_ok=True)
|
||||
skill_md = skill_dir / "SKILL.md"
|
||||
skill_md.write_text(content, encoding="utf-8")
|
||||
return skill_md
|
||||
|
||||
return _create
|
||||
|
||||
|
||||
|
||||
@@ -3,24 +3,18 @@
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from datetime import UTC, datetime
|
||||
from pathlib import Path
|
||||
from unittest.mock import MagicMock, patch
|
||||
|
||||
import pytest
|
||||
|
||||
from framework.skills.parser import ParsedSkill
|
||||
from framework.skills.trust import (
|
||||
ProjectTrustClassification,
|
||||
ProjectTrustDetector,
|
||||
TrustGate,
|
||||
TrustedRepoEntry,
|
||||
TrustedRepoStore,
|
||||
TrustGate,
|
||||
_is_localhost_remote,
|
||||
_normalize_remote_url,
|
||||
)
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
# Helpers
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -53,10 +47,7 @@ class TestNormalizeRemoteUrl:
|
||||
assert _normalize_remote_url("https://github.com/org/repo") == "github.com/org/repo"
|
||||
|
||||
def test_ssh_url_format(self):
|
||||
assert (
|
||||
_normalize_remote_url("ssh://git@github.com/org/repo.git")
|
||||
== "github.com/org/repo"
|
||||
)
|
||||
assert _normalize_remote_url("ssh://git@github.com/org/repo.git") == "github.com/org/repo"
|
||||
|
||||
def test_lowercased(self):
|
||||
assert _normalize_remote_url("git@GitHub.COM:Org/Repo.git") == "github.com/org/repo"
|
||||
@@ -65,10 +56,7 @@ class TestNormalizeRemoteUrl:
|
||||
assert _normalize_remote_url("https://github.com/org/repo/") == "github.com/org/repo"
|
||||
|
||||
def test_gitlab(self):
|
||||
assert (
|
||||
_normalize_remote_url("git@gitlab.com:team/project.git")
|
||||
== "gitlab.com/team/project"
|
||||
)
|
||||
assert _normalize_remote_url("git@gitlab.com:team/project.git") == "gitlab.com/team/project"
|
||||
|
||||
|
||||
# ---------------------------------------------------------------------------
|
||||
@@ -238,6 +226,7 @@ class TestProjectTrustDetector:
|
||||
|
||||
def test_git_timeout_treated_as_trusted(self, tmp_path):
|
||||
import subprocess
|
||||
|
||||
(tmp_path / ".git").mkdir()
|
||||
store = TrustedRepoStore(tmp_path / "t.json")
|
||||
det = ProjectTrustDetector(store)
|
||||
@@ -287,9 +276,7 @@ class TestTrustGate:
|
||||
skill = make_skill("proj-skill", "project")
|
||||
gate = TrustGate(store=store, interactive=False)
|
||||
with patch("subprocess.run") as m:
|
||||
m.return_value = MagicMock(
|
||||
returncode=0, stdout="git@github.com:trusted/repo.git\n"
|
||||
)
|
||||
m.return_value = MagicMock(returncode=0, stdout="git@github.com:trusted/repo.git\n")
|
||||
result = gate.filter_and_gate([skill], project_dir=tmp_path)
|
||||
assert any(s.name == "proj-skill" for s in result)
|
||||
|
||||
@@ -322,9 +309,11 @@ class TestTrustGate:
|
||||
print_fn=outputs.append,
|
||||
input_fn=lambda _: "1", # trust this session
|
||||
)
|
||||
with patch("sys.stdin.isatty", return_value=True), patch(
|
||||
"sys.stdout.isatty", return_value=True
|
||||
), patch("subprocess.run") as m:
|
||||
with (
|
||||
patch("sys.stdin.isatty", return_value=True),
|
||||
patch("sys.stdout.isatty", return_value=True),
|
||||
patch("subprocess.run") as m,
|
||||
):
|
||||
m.return_value = MagicMock(
|
||||
returncode=0, stdout="https://github.com/stranger/repo.git\n"
|
||||
)
|
||||
@@ -344,9 +333,11 @@ class TestTrustGate:
|
||||
print_fn=lambda _: None,
|
||||
input_fn=lambda _: "2", # trust permanently
|
||||
)
|
||||
with patch("sys.stdin.isatty", return_value=True), patch(
|
||||
"sys.stdout.isatty", return_value=True
|
||||
), patch("subprocess.run") as m:
|
||||
with (
|
||||
patch("sys.stdin.isatty", return_value=True),
|
||||
patch("sys.stdout.isatty", return_value=True),
|
||||
patch("subprocess.run") as m,
|
||||
):
|
||||
m.return_value = MagicMock(
|
||||
returncode=0, stdout="https://github.com/stranger/repo.git\n"
|
||||
)
|
||||
@@ -365,9 +356,11 @@ class TestTrustGate:
|
||||
print_fn=lambda _: None,
|
||||
input_fn=lambda _: "3", # deny
|
||||
)
|
||||
with patch("sys.stdin.isatty", return_value=True), patch(
|
||||
"sys.stdout.isatty", return_value=True
|
||||
), patch("subprocess.run") as m:
|
||||
with (
|
||||
patch("sys.stdin.isatty", return_value=True),
|
||||
patch("sys.stdout.isatty", return_value=True),
|
||||
patch("subprocess.run") as m,
|
||||
):
|
||||
m.return_value = MagicMock(
|
||||
returncode=0, stdout="https://github.com/stranger/repo.git\n"
|
||||
)
|
||||
@@ -394,9 +387,11 @@ class TestTrustGate:
|
||||
print_fn=lambda _: None,
|
||||
input_fn=lambda _: (_ for _ in ()).throw(KeyboardInterrupt()),
|
||||
)
|
||||
with patch("sys.stdin.isatty", return_value=True), patch(
|
||||
"sys.stdout.isatty", return_value=True
|
||||
), patch("subprocess.run") as m:
|
||||
with (
|
||||
patch("sys.stdin.isatty", return_value=True),
|
||||
patch("sys.stdout.isatty", return_value=True),
|
||||
patch("subprocess.run") as m,
|
||||
):
|
||||
m.return_value = MagicMock(
|
||||
returncode=0, stdout="https://github.com/stranger/repo.git\n"
|
||||
)
|
||||
@@ -407,9 +402,7 @@ class TestTrustGate:
|
||||
"""Security notice (NFR-5) should be shown the first time only."""
|
||||
# Use a temp sentinel path
|
||||
sentinel = tmp_path / ".skill_trust_notice_shown"
|
||||
monkeypatch.setattr(
|
||||
"framework.skills.trust._NOTICE_SENTINEL_PATH", sentinel
|
||||
)
|
||||
monkeypatch.setattr("framework.skills.trust._NOTICE_SENTINEL_PATH", sentinel)
|
||||
assert not sentinel.exists()
|
||||
|
||||
(tmp_path / ".git").mkdir()
|
||||
@@ -422,9 +415,11 @@ class TestTrustGate:
|
||||
print_fn=output_lines.append,
|
||||
input_fn=lambda _: "3",
|
||||
)
|
||||
with patch("sys.stdin.isatty", return_value=True), patch(
|
||||
"sys.stdout.isatty", return_value=True
|
||||
), patch("subprocess.run") as m:
|
||||
with (
|
||||
patch("sys.stdin.isatty", return_value=True),
|
||||
patch("sys.stdout.isatty", return_value=True),
|
||||
patch("subprocess.run") as m,
|
||||
):
|
||||
m.return_value = MagicMock(
|
||||
returncode=0, stdout="https://github.com/stranger/repo.git\n"
|
||||
)
|
||||
@@ -436,9 +431,11 @@ class TestTrustGate:
|
||||
# Second run should NOT show the notice again
|
||||
output_lines.clear()
|
||||
skill2 = make_skill("notice-skill-2", "project")
|
||||
with patch("sys.stdin.isatty", return_value=True), patch(
|
||||
"sys.stdout.isatty", return_value=True
|
||||
), patch("subprocess.run") as m:
|
||||
with (
|
||||
patch("sys.stdin.isatty", return_value=True),
|
||||
patch("sys.stdout.isatty", return_value=True),
|
||||
patch("subprocess.run") as m,
|
||||
):
|
||||
m.return_value = MagicMock(
|
||||
returncode=0, stdout="https://github.com/stranger/repo.git\n"
|
||||
)
|
||||
@@ -459,15 +456,15 @@ class TestTrustGate:
|
||||
print_fn=lambda _: None,
|
||||
input_fn=lambda _: "3", # deny project skills
|
||||
)
|
||||
with patch("sys.stdin.isatty", return_value=True), patch(
|
||||
"sys.stdout.isatty", return_value=True
|
||||
), patch("subprocess.run") as m:
|
||||
with (
|
||||
patch("sys.stdin.isatty", return_value=True),
|
||||
patch("sys.stdout.isatty", return_value=True),
|
||||
patch("subprocess.run") as m,
|
||||
):
|
||||
m.return_value = MagicMock(
|
||||
returncode=0, stdout="https://github.com/stranger/repo.git\n"
|
||||
)
|
||||
result = gate.filter_and_gate(
|
||||
[fw_skill, user_skill, proj_skill], project_dir=tmp_path
|
||||
)
|
||||
result = gate.filter_and_gate([fw_skill, user_skill, proj_skill], project_dir=tmp_path)
|
||||
names = {s.name for s in result}
|
||||
assert "fw" in names
|
||||
assert "usr" in names
|
||||
|
||||
Reference in New Issue
Block a user