agentshim 0.8.0__tar.gz → 0.9.0__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.
Files changed (62) hide show
  1. {agentshim-0.8.0 → agentshim-0.9.0}/CHANGELOG.md +21 -0
  2. {agentshim-0.8.0 → agentshim-0.9.0}/PKG-INFO +1 -1
  3. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/__init__.py +3 -1
  4. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/agent.py +22 -5
  5. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/core/__init__.py +2 -0
  6. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/core/profile.py +18 -0
  7. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/core/provider.py +4 -0
  8. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/claude/provider.py +15 -0
  9. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/codex/provider.py +5 -0
  10. agentshim-0.9.0/agentshim/providers/codex/skills.py +86 -0
  11. {agentshim-0.8.0 → agentshim-0.9.0}/pyproject.toml +1 -1
  12. agentshim-0.8.0/agentshim/providers/codex/skills.py +0 -27
  13. {agentshim-0.8.0 → agentshim-0.9.0}/.gitignore +0 -0
  14. {agentshim-0.8.0 → agentshim-0.9.0}/README.md +0 -0
  15. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/core/_files.py +0 -0
  16. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/core/env.py +0 -0
  17. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/core/errors.py +0 -0
  18. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/core/events.py +0 -0
  19. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/core/mcp.py +0 -0
  20. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/core/pricing.py +0 -0
  21. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/core/schema.py +0 -0
  22. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/core/skills.py +0 -0
  23. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/core/stream.py +0 -0
  24. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/core/turn.py +0 -0
  25. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/core/usage.py +0 -0
  26. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/execution/__init__.py +0 -0
  27. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/execution/executor.py +0 -0
  28. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/execution/host.py +0 -0
  29. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/execution/transform.py +0 -0
  30. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/__init__.py +0 -0
  31. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/claude/__init__.py +0 -0
  32. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/claude/events.py +0 -0
  33. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/claude/hooks/__init__.py +0 -0
  34. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/claude/hooks/confine_reads.py +0 -0
  35. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/claude/parser.py +0 -0
  36. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/claude/sandbox.py +0 -0
  37. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/claude/scripted.py +0 -0
  38. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/claude/user_hooks.py +0 -0
  39. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/codex/__init__.py +0 -0
  40. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/codex/_toml.py +0 -0
  41. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/codex/events.py +0 -0
  42. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/codex/parser.py +0 -0
  43. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/codex/rules.py +0 -0
  44. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/codex/sandbox.py +0 -0
  45. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/codex/scripted.py +0 -0
  46. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/copilot/__init__.py +0 -0
  47. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/copilot/events.py +0 -0
  48. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/copilot/parser.py +0 -0
  49. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/copilot/provider.py +0 -0
  50. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/copilot/scripted.py +0 -0
  51. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/gemini/__init__.py +0 -0
  52. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/gemini/events.py +0 -0
  53. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/gemini/parser.py +0 -0
  54. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/gemini/provider.py +0 -0
  55. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/gemini/scripted.py +0 -0
  56. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/opencode/__init__.py +0 -0
  57. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/opencode/events.py +0 -0
  58. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/opencode/parser.py +0 -0
  59. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/opencode/provider.py +0 -0
  60. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/providers/opencode/scripted.py +0 -0
  61. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/py.typed +0 -0
  62. {agentshim-0.8.0 → agentshim-0.9.0}/agentshim/testing/__init__.py +0 -0
@@ -1,5 +1,26 @@
1
1
  # Changelog
2
2
 
3
+ ## 0.9.0 (2026-10-03)
4
+
5
+ Skill isolation. Additive: the default scope keeps today's behaviour.
6
+
7
+ ### Added
8
+
9
+ - `SkillScope` (`ALL`, `PROJECT`) and `start_session(skill_scope=...)` /
10
+ `run(skill_scope=...)`. `PROJECT` offers only the workspace's skills and
11
+ the CLI's built-in ones, not the user's personal skills or plugins.
12
+ - `ProviderProfile.skill_scopes` declares the scopes a provider enforces;
13
+ asking for another raises `ProviderCapabilityError` from `start_session`.
14
+ - `ArgvContext.skill_scope` carries the session's scope to `build_argv`.
15
+ - Claude Code: `PROJECT` passes `--setting-sources project,local`, which also
16
+ skips user settings and `~/.claude/CLAUDE.md`; credentials still work.
17
+ - Codex: `PROJECT` sets `features.plugins=false` and disables every user
18
+ `SKILL.md` (`$CODEX_HOME/skills` except `.system`, `~/.agents/skills`)
19
+ through `skills.config`.
20
+ - Gemini, opencode and Copilot support `ALL` only.
21
+ - Live e2e: `SkillScope.PROJECT` hides a skill seeded in a relocated user
22
+ home while the workspace skill stays offered (`tests/e2e/test_skills_e2e.py`).
23
+
3
24
  ## 0.8.0 (2026-10-03)
4
25
 
5
26
  Skill observability. Additive: a caller that ignores the new events and the
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: agentshim
3
- Version: 0.8.0
3
+ Version: 0.9.0
4
4
  Summary: Provider-agnostic coding-agent CLI shims
5
5
  Requires-Python: >=3.10
6
6
  Provides-Extra: test
@@ -50,6 +50,7 @@ from .core import (
50
50
  SessionResumeError,
51
51
  SessionStarted,
52
52
  SkillInvoked,
53
+ SkillScope,
53
54
  SkillsDiscovered,
54
55
  SkillSignal,
55
56
  SkillSummary,
@@ -96,7 +97,7 @@ from .providers.copilot import CopilotProvider
96
97
  from .providers.gemini import GeminiProvider
97
98
  from .providers.opencode import OpencodeProvider
98
99
 
99
- __version__ = "0.8.0"
100
+ __version__ = "0.9.0"
100
101
 
101
102
  __all__ = [
102
103
  "AgentEvent",
@@ -159,6 +160,7 @@ __all__ = [
159
160
  "SessionResumeError",
160
161
  "SessionStarted",
161
162
  "SkillInvoked",
163
+ "SkillScope",
162
164
  "SkillSignal",
163
165
  "SkillSummary",
164
166
  "SkillTracker",
@@ -23,7 +23,7 @@ from agentshim.core.errors import (
23
23
  SessionResumeError,
24
24
  )
25
25
  from agentshim.core.events import RunFinished, RunStarted, compose_event_handlers
26
- from agentshim.core.profile import McpMechanism, OutputSchemaStyle, SchemaDialect
26
+ from agentshim.core.profile import McpMechanism, OutputSchemaStyle, SchemaDialect, SkillScope
27
27
  from agentshim.core.provider import ArgvContext
28
28
  from agentshim.core.schema import compact_json, dialect_problems, materialize
29
29
  from agentshim.core.skills import SkillTracker
@@ -114,9 +114,15 @@ class CliAgent:
114
114
  cwd: str | None = None,
115
115
  timeout: float | None = None,
116
116
  session_id: str | None = None,
117
+ skill_scope: SkillScope = SkillScope.ALL,
117
118
  ) -> AgentSession:
118
- """Open a conversation whose turns resume one another."""
119
- return AgentSession(self, cwd=cwd, timeout=timeout, session_id=session_id)
119
+ """Open a conversation whose turns resume one another.
120
+
121
+ ``skill_scope`` is fixed for the conversation (see ``AgentSession``).
122
+ """
123
+ return AgentSession(
124
+ self, cwd=cwd, timeout=timeout, session_id=session_id, skill_scope=skill_scope
125
+ )
120
126
 
121
127
  def run(
122
128
  self,
@@ -124,9 +130,11 @@ class CliAgent:
124
130
  *,
125
131
  cwd: str | None = None,
126
132
  timeout: float | None = None,
133
+ skill_scope: SkillScope = SkillScope.ALL,
127
134
  ) -> TurnResult:
128
135
  """Run one turn in a throwaway session."""
129
- return self.start_session(cwd=cwd, timeout=timeout).turn(request)
136
+ session = self.start_session(cwd=cwd, timeout=timeout, skill_scope=skill_scope)
137
+ return session.turn(request)
130
138
 
131
139
 
132
140
  def _discard(message: str) -> None:
@@ -143,6 +151,7 @@ class AgentSession:
143
151
  cwd: str | None = None,
144
152
  timeout: float | None = None,
145
153
  session_id: str | None = None,
154
+ skill_scope: SkillScope = SkillScope.ALL,
146
155
  ) -> None:
147
156
  """Bind a conversation to one agent, with per-turn defaults.
148
157
 
@@ -150,8 +159,15 @@ class AgentSession:
150
159
  successive turns resume one another. ``cwd`` and ``timeout`` are
151
160
  defaults an individual ``TurnRequest`` may override. Passing
152
161
  ``session_id`` adopts a conversation the provider already has, so the
153
- first turn resumes rather than starts fresh.
162
+ first turn resumes rather than starts fresh. ``skill_scope`` limits
163
+ which skills every turn's CLI may discover; a scope the provider's
164
+ ``profile.skill_scopes`` does not list raises
165
+ ``ProviderCapabilityError`` here, before any turn runs.
154
166
  """
167
+ if skill_scope not in agent.profile.skill_scopes:
168
+ msg = f"{agent.profile.name} cannot limit skills to scope {skill_scope.value!r}"
169
+ raise ProviderCapabilityError(msg)
170
+ self.skill_scope = skill_scope
155
171
  self._agent = agent
156
172
  self._cwd = cwd
157
173
  self._timeout = timeout
@@ -260,6 +276,7 @@ class AgentSession:
260
276
  mcp_argv=installation.argv,
261
277
  extra_args=req.extra_args,
262
278
  cwd=cwd,
279
+ skill_scope=self.skill_scope,
263
280
  )
264
281
  )
265
282
  command = CommandRequest(argv=argv, stdin=req.prompt, cwd=cwd, env=env, timeout=timeout)
@@ -58,6 +58,7 @@ from .profile import (
58
58
  OutputSchemaStyle,
59
59
  ProviderProfile,
60
60
  SchemaDialect,
61
+ SkillScope,
61
62
  SkillSignal,
62
63
  )
63
64
  from .provider import ArgvContext, McpInstallation, ParsedTurn, Provider, StreamParser
@@ -110,6 +111,7 @@ __all__ = [
110
111
  "SessionResumeError",
111
112
  "SessionStarted",
112
113
  "SkillInvoked",
114
+ "SkillScope",
113
115
  "SkillSignal",
114
116
  "SkillSummary",
115
117
  "SkillTracker",
@@ -61,6 +61,22 @@ class SkillSignal(Enum):
61
61
  INFERRED = "inferred"
62
62
 
63
63
 
64
+ class SkillScope(Enum):
65
+ """Which skills a session's CLI may discover.
66
+
67
+ ``ALL``: whatever the CLI finds by default, including the user's own
68
+ skills and installed plugins. ``PROJECT``: only the skills under the
69
+ session's working directory (the profile's ``skill_dirs``) plus the
70
+ CLI's built-in ones, so the user running agentshim does not change what
71
+ the agent is offered. A provider declares the scopes it can enforce in
72
+ ``ProviderProfile.skill_scopes``; asking for another one is an error,
73
+ never a silent ``ALL``.
74
+ """
75
+
76
+ ALL = "all"
77
+ PROJECT = "project"
78
+
79
+
64
80
  def _no_container_env() -> Mapping[str, str]:
65
81
  """Empty read-only default: a frozen spec must not carry a mutable one.
66
82
 
@@ -109,3 +125,5 @@ class ProviderProfile:
109
125
  skill_discovery: SkillSignal = SkillSignal.NONE
110
126
  #: Whether the stream reveals skill loads (``SkillInvoked``).
111
127
  skill_invocation: SkillSignal = SkillSignal.NONE
128
+ #: The ``SkillScope`` values a session on this provider may request.
129
+ skill_scopes: frozenset[SkillScope] = frozenset({SkillScope.ALL})
@@ -5,6 +5,7 @@ from __future__ import annotations
5
5
  from dataclasses import dataclass, field
6
6
  from typing import TYPE_CHECKING, Any, Protocol
7
7
 
8
+ from .profile import SkillScope
8
9
  from .usage import ProviderUsage
9
10
 
10
11
  if TYPE_CHECKING:
@@ -37,6 +38,9 @@ class ArgvContext:
37
38
  #: The directory the CLI will run in, or ``None`` for the executor's own.
38
39
  #: Argv never carries it; a provider reads it to validate paths against it.
39
40
  cwd: str | None = None
41
+ #: Which skills the CLI may discover; the session has already checked
42
+ #: that the provider's profile supports it.
43
+ skill_scope: SkillScope = SkillScope.ALL
40
44
 
41
45
 
42
46
  @dataclass(frozen=True)
@@ -12,6 +12,7 @@ from agentshim.core.profile import (
12
12
  OutputSchemaStyle,
13
13
  ProviderProfile,
14
14
  SchemaDialect,
15
+ SkillScope,
15
16
  SkillSignal,
16
17
  )
17
18
 
@@ -31,6 +32,17 @@ if TYPE_CHECKING:
31
32
  MCP_CONFIG_FILENAME = ".mcp.json"
32
33
  MCP_SERVER_KEY = "mcpServers"
33
34
 
35
+ #: ``SkillScope.PROJECT``: load settings only from the workspace's
36
+ #: ``.claude/settings.json`` and ``.claude/settings.local.json`` (plus
37
+ #: ``--settings`` and managed policy, which always apply). Leaving out the
38
+ #: ``user`` source drops ``~/.claude/skills``, the user's plugins (with their
39
+ #: skills, hooks and MCP servers), ``~/.claude/CLAUDE.md`` and the user's
40
+ #: settings (default model, permissions, hooks, ``env``, ``apiKeyHelper``).
41
+ #: Credentials are not settings: OAuth in ``.credentials.json`` and the
42
+ #: ``auth_env_vars`` keep working. Claude Code's built-in skills and claude.ai
43
+ #: account connectors are not user settings and stay.
44
+ PROJECT_SETTING_SOURCES = ("--setting-sources", "project,local")
45
+
34
46
  PROFILE = ProviderProfile(
35
47
  name="claude",
36
48
  display_name="Claude Code",
@@ -54,6 +66,7 @@ PROFILE = ProviderProfile(
54
66
  # ``system/init`` lists ``skills``; the ``Skill`` tool call names one.
55
67
  skill_discovery=SkillSignal.STRUCTURED,
56
68
  skill_invocation=SkillSignal.STRUCTURED,
69
+ skill_scopes=frozenset({SkillScope.ALL, SkillScope.PROJECT}),
57
70
  container_install=(
58
71
  "apt-get update && apt-get install -y --no-install-recommends curl ca-certificates",
59
72
  "curl -fsSL https://claude.ai/install.sh | bash",
@@ -117,6 +130,8 @@ class ClaudeProvider:
117
130
  "stream-json",
118
131
  "--verbose",
119
132
  ]
133
+ if ctx.skill_scope is SkillScope.PROJECT:
134
+ argv += PROJECT_SETTING_SOURCES
120
135
  if ctx.model:
121
136
  argv += ["--model", ctx.model]
122
137
  if ctx.reasoning_effort:
@@ -13,6 +13,7 @@ from agentshim.core.profile import (
13
13
  OutputSchemaStyle,
14
14
  ProviderProfile,
15
15
  SchemaDialect,
16
+ SkillScope,
16
17
  SkillSignal,
17
18
  )
18
19
 
@@ -20,6 +21,7 @@ from ._toml import toml_array, toml_str, unescape_toml
20
21
  from .parser import CodexStreamParser
21
22
  from .rules import RULES_FILENAME
22
23
  from .sandbox import CodexSandboxConfig, resolve_sandbox, sandbox_overrides
24
+ from .skills import project_scope_overrides
23
25
 
24
26
  if TYPE_CHECKING:
25
27
  from collections.abc import Callable, Mapping, Sequence
@@ -79,6 +81,7 @@ PROFILE = ProviderProfile(
79
81
  # ``exec --json`` lists no skills; a load is inferred from a shell read
80
82
  # of a ``SKILL.md`` (``skills.py``).
81
83
  skill_invocation=SkillSignal.INFERRED,
84
+ skill_scopes=frozenset({SkillScope.ALL, SkillScope.PROJECT}),
82
85
  container_install=(_NODE_INSTALL, _CODEX_INSTALL),
83
86
  # Documented Codex CLI variable that relocates ~/.codex (``codex --help``:
84
87
  # "Layer $CODEX_HOME/<name>.config.toml on top of the base user config";
@@ -130,6 +133,8 @@ class CodexProvider:
130
133
  if ctx.model:
131
134
  argv += ["--model", ctx.model]
132
135
  argv += _shell_path_config(ctx.env)
136
+ if ctx.skill_scope is SkillScope.PROJECT:
137
+ argv += project_scope_overrides(ctx.env)
133
138
  if ctx.reasoning_effort:
134
139
  argv += ["--config", f"model_reasoning_effort={toml_str(ctx.reasoning_effort)}"]
135
140
  argv += list(ctx.mcp_argv)
@@ -0,0 +1,86 @@
1
+ """Codex skills: recognize a load in a shell command, and hide the user's.
2
+
3
+ Codex has no skill event in ``codex exec --json``: it injects a list of
4
+ skills into the prompt and the model loads one by reading its ``SKILL.md``
5
+ with a shell command (``sed -n '1,240p' .../.agents/skills/<name>/SKILL.md``).
6
+ That read is the only trace of the load in the stream, so it is inferred
7
+ here and nowhere else.
8
+ """
9
+
10
+ from __future__ import annotations
11
+
12
+ import os
13
+ import re
14
+ from typing import TYPE_CHECKING
15
+
16
+ from agentshim.core.events import SkillInvoked
17
+
18
+ from ._toml import toml_str
19
+
20
+ if TYPE_CHECKING:
21
+ from collections.abc import Mapping
22
+
23
+ #: A path ending in ``skills/[<group>/...]<name>/SKILL.md``, as one shell word.
24
+ _SKILL_FILE = re.compile(
25
+ r"(?<![^\s'\"=])(?P<path>(?:[^\s'\"]*/)?skills/(?:[^\s'\"/]+/)*?(?P<name>[^\s'\"/]+)/SKILL\.md)(?![^\s'\";|&)])"
26
+ )
27
+
28
+
29
+ def skill_reads(command: str, tool_id: str | None) -> list[SkillInvoked]:
30
+ """Return one ``SkillInvoked`` per ``SKILL.md`` the command names, in order."""
31
+ return [
32
+ SkillInvoked(name=match["name"], source_path=match["path"], tool_id=tool_id)
33
+ for match in _SKILL_FILE.finditer(command)
34
+ ]
35
+
36
+
37
+ #: Codex's own bundled skills, which it re-extracts into every home.
38
+ _SYSTEM_SKILLS = ".system"
39
+
40
+
41
+ def user_skill_files(env: Mapping[str, str]) -> list[str]:
42
+ """Every user-installed ``SKILL.md`` the CLI run with *env* would offer.
43
+
44
+ Codex reads user skills from ``$CODEX_HOME/skills`` (default
45
+ ``~/.codex/skills``) and ``~/.agents/skills``, beside the workspace's own
46
+ ``.agents/skills``. ``$CODEX_HOME/skills/.system`` holds Codex's bundled
47
+ skills and is not the user's. Paths are listed as found and, when a
48
+ symlink is involved, also resolved, since either spelling may be the one
49
+ Codex matches against. The scan runs where agentshim runs, so a CLI in a
50
+ container is only covered when the container shares this home.
51
+ """
52
+ home = env.get("HOME") or os.path.expanduser("~") # noqa: PTH111 - a str contract, not a Path
53
+ codex_home = env.get("CODEX_HOME") or os.path.join(home, ".codex") # noqa: PTH118
54
+ roots = [os.path.join(codex_home, "skills"), os.path.join(home, ".agents", "skills")] # noqa: PTH118
55
+ found: list[str] = []
56
+ for root in roots:
57
+ for directory, subdirs, files in os.walk(root, followlinks=True):
58
+ if directory == root and _SYSTEM_SKILLS in subdirs:
59
+ subdirs.remove(_SYSTEM_SKILLS)
60
+ subdirs.sort()
61
+ if "SKILL.md" in files:
62
+ path = os.path.join(directory, "SKILL.md") # noqa: PTH118
63
+ found.append(path)
64
+ resolved = os.path.realpath(path)
65
+ if resolved != path:
66
+ found.append(resolved)
67
+ return list(dict.fromkeys(found))
68
+
69
+
70
+ def project_scope_overrides(env: Mapping[str, str]) -> list[str]:
71
+ """``--config`` flags that leave Codex only the workspace's skills.
72
+
73
+ Plugins are switched off as a feature (their skills, MCP servers and
74
+ apps go with them), and each user skill is disabled by path through
75
+ ``skills.config``. The override replaces any ``skills.config`` in the
76
+ user's ``config.toml``, which is user policy this scope excludes anyway.
77
+ """
78
+ entries = ", ".join(
79
+ f"{{path = {toml_str(path)}, enabled = false}}" for path in user_skill_files(env)
80
+ )
81
+ return [
82
+ "--config",
83
+ "features.plugins=false",
84
+ "--config",
85
+ f"skills.config=[{entries}]",
86
+ ]
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agentshim"
3
- version = "0.8.0"
3
+ version = "0.9.0"
4
4
  description = "Provider-agnostic coding-agent CLI shims"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.10"
@@ -1,27 +0,0 @@
1
- """Recognize a Codex skill load in a shell command.
2
-
3
- Codex has no skill event in ``codex exec --json``: it injects a list of
4
- skills into the prompt and the model loads one by reading its ``SKILL.md``
5
- with a shell command (``sed -n '1,240p' .../.agents/skills/<name>/SKILL.md``).
6
- That read is the only trace of the load in the stream, so it is inferred
7
- here and nowhere else.
8
- """
9
-
10
- from __future__ import annotations
11
-
12
- import re
13
-
14
- from agentshim.core.events import SkillInvoked
15
-
16
- #: A path ending in ``skills/[<group>/...]<name>/SKILL.md``, as one shell word.
17
- _SKILL_FILE = re.compile(
18
- r"(?<![^\s'\"=])(?P<path>(?:[^\s'\"]*/)?skills/(?:[^\s'\"/]+/)*?(?P<name>[^\s'\"/]+)/SKILL\.md)(?![^\s'\";|&)])"
19
- )
20
-
21
-
22
- def skill_reads(command: str, tool_id: str | None) -> list[SkillInvoked]:
23
- """Return one ``SkillInvoked`` per ``SKILL.md`` the command names, in order."""
24
- return [
25
- SkillInvoked(name=match["name"], source_path=match["path"], tool_id=tool_id)
26
- for match in _SKILL_FILE.finditer(command)
27
- ]
File without changes
File without changes
File without changes