agent-first-data 0.13.2__tar.gz → 0.14.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 (18) hide show
  1. {agent_first_data-0.13.2 → agent_first_data-0.14.0}/PKG-INFO +5 -5
  2. {agent_first_data-0.13.2 → agent_first_data-0.14.0}/README.md +4 -4
  3. {agent_first_data-0.13.2 → agent_first_data-0.14.0}/agent_first_data/skill.py +24 -16
  4. {agent_first_data-0.13.2 → agent_first_data-0.14.0}/agent_first_data.egg-info/PKG-INFO +5 -5
  5. {agent_first_data-0.13.2 → agent_first_data-0.14.0}/pyproject.toml +1 -1
  6. {agent_first_data-0.13.2 → agent_first_data-0.14.0}/tests/test_skill.py +36 -9
  7. {agent_first_data-0.13.2 → agent_first_data-0.14.0}/agent_first_data/__init__.py +0 -0
  8. {agent_first_data-0.13.2 → agent_first_data-0.14.0}/agent_first_data/afdata_logging.py +0 -0
  9. {agent_first_data-0.13.2 → agent_first_data-0.14.0}/agent_first_data/cli.py +0 -0
  10. {agent_first_data-0.13.2 → agent_first_data-0.14.0}/agent_first_data/format.py +0 -0
  11. {agent_first_data-0.13.2 → agent_first_data-0.14.0}/agent_first_data.egg-info/SOURCES.txt +0 -0
  12. {agent_first_data-0.13.2 → agent_first_data-0.14.0}/agent_first_data.egg-info/dependency_links.txt +0 -0
  13. {agent_first_data-0.13.2 → agent_first_data-0.14.0}/agent_first_data.egg-info/top_level.txt +0 -0
  14. {agent_first_data-0.13.2 → agent_first_data-0.14.0}/setup.cfg +0 -0
  15. {agent_first_data-0.13.2 → agent_first_data-0.14.0}/tests/test_afdata_logging.py +0 -0
  16. {agent_first_data-0.13.2 → agent_first_data-0.14.0}/tests/test_cli.py +0 -0
  17. {agent_first_data-0.13.2 → agent_first_data-0.14.0}/tests/test_format.py +0 -0
  18. {agent_first_data-0.13.2 → agent_first_data-0.14.0}/tests/test_no_stderr_policy.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-first-data
3
- Version: 0.13.2
3
+ Version: 0.14.0
4
4
  Summary: A naming convention that lets AI agents understand your data without being told what it means.
5
5
  License-Expression: MIT
6
6
  Project-URL: Repository, https://github.com/agentfirstkit/agent-first-data
@@ -50,7 +50,7 @@ init_logging_json("INFO", secret_names=("authorization",))
50
50
 
51
51
  ## Reference
52
52
 
53
- - Full convention and API groups: [../docs/overview.md](../docs/overview.md)
54
- - Formal cross-language contract: [../spec/agent-first-data.md](../spec/agent-first-data.md)
55
- - Conformance fixtures: [../spec/fixtures](../spec/fixtures)
56
- - Agent skill: [../skills/agent-first-data.md](../skills/agent-first-data.md)
53
+ - Full convention and API groups: [docs/overview.md](https://github.com/agentfirstkit/agent-first-data/blob/main/docs/overview.md)
54
+ - Formal cross-language contract: [spec/agent-first-data.md](https://github.com/agentfirstkit/agent-first-data/blob/main/spec/agent-first-data.md)
55
+ - Conformance fixtures: [spec/fixtures](https://github.com/agentfirstkit/agent-first-data/tree/main/spec/fixtures)
56
+ - Agent skill: [skills/agent-first-data.md](https://github.com/agentfirstkit/agent-first-data/blob/main/skills/agent-first-data.md)
@@ -41,7 +41,7 @@ init_logging_json("INFO", secret_names=("authorization",))
41
41
 
42
42
  ## Reference
43
43
 
44
- - Full convention and API groups: [../docs/overview.md](../docs/overview.md)
45
- - Formal cross-language contract: [../spec/agent-first-data.md](../spec/agent-first-data.md)
46
- - Conformance fixtures: [../spec/fixtures](../spec/fixtures)
47
- - Agent skill: [../skills/agent-first-data.md](../skills/agent-first-data.md)
44
+ - Full convention and API groups: [docs/overview.md](https://github.com/agentfirstkit/agent-first-data/blob/main/docs/overview.md)
45
+ - Formal cross-language contract: [spec/agent-first-data.md](https://github.com/agentfirstkit/agent-first-data/blob/main/spec/agent-first-data.md)
46
+ - Conformance fixtures: [spec/fixtures](https://github.com/agentfirstkit/agent-first-data/tree/main/spec/fixtures)
47
+ - Agent skill: [skills/agent-first-data.md](https://github.com/agentfirstkit/agent-first-data/blob/main/skills/agent-first-data.md)
@@ -2,7 +2,7 @@
2
2
 
3
3
  A spore that embeds its ``SKILL.md`` describes itself with a :class:`SkillSpec` and
4
4
  calls :func:`run_skill_admin` to install, uninstall, or report status of that skill
5
- across supported coding agents (Codex, Claude Code, opencode).
5
+ across supported coding agents (Codex, Claude Code, opencode, Hermes).
6
6
 
7
7
  The function performs the filesystem work and returns the protocol ``dict`` (rendered
8
8
  by the caller with ``cli_output``) or raises :class:`SkillError`. It never writes to
@@ -52,7 +52,7 @@ class SkillScope(enum.Enum):
52
52
  """Where to install the skill."""
53
53
 
54
54
  PERSONAL = "personal"
55
- PROJECT = "project"
55
+ WORKSPACE = "workspace"
56
56
 
57
57
 
58
58
  class SkillAgentSelection(enum.Enum):
@@ -62,6 +62,7 @@ class SkillAgentSelection(enum.Enum):
62
62
  CODEX = "codex"
63
63
  CLAUDE_CODE = "claude-code"
64
64
  OPENCODE = "opencode"
65
+ HERMES = "hermes"
65
66
 
66
67
 
67
68
  class SkillAgent(enum.Enum):
@@ -70,6 +71,7 @@ class SkillAgent(enum.Enum):
70
71
  CODEX = "codex"
71
72
  CLAUDE_CODE = "claude-code"
72
73
  OPENCODE = "opencode"
74
+ HERMES = "hermes"
73
75
 
74
76
 
75
77
  @dataclass
@@ -310,24 +312,23 @@ def _resolve_targets(spec: SkillSpec, options: SkillOptions) -> list[_SkillTarge
310
312
  _resolve_target(spec, SkillAgent.CODEX, SkillScope.PERSONAL, None),
311
313
  _resolve_target(spec, SkillAgent.CLAUDE_CODE, SkillScope.PERSONAL, None),
312
314
  _resolve_target(spec, SkillAgent.OPENCODE, SkillScope.PERSONAL, None),
315
+ _resolve_target(spec, SkillAgent.HERMES, SkillScope.PERSONAL, None),
313
316
  ]
314
- # Codex has no project scope, so --agent all --scope project skips it.
315
- if agent is SkillAgentSelection.ALL and scope is SkillScope.PROJECT:
317
+ if agent is SkillAgentSelection.ALL and scope is SkillScope.WORKSPACE:
316
318
  return [
317
- _resolve_target(spec, SkillAgent.CLAUDE_CODE, SkillScope.PROJECT, None),
318
- _resolve_target(spec, SkillAgent.OPENCODE, SkillScope.PROJECT, None),
319
+ _resolve_target(spec, SkillAgent.CODEX, SkillScope.WORKSPACE, None),
320
+ _resolve_target(spec, SkillAgent.CLAUDE_CODE, SkillScope.WORKSPACE, None),
321
+ _resolve_target(spec, SkillAgent.OPENCODE, SkillScope.WORKSPACE, None),
322
+ _resolve_target(spec, SkillAgent.HERMES, SkillScope.WORKSPACE, None),
319
323
  ]
320
- if agent is SkillAgentSelection.CODEX and scope is SkillScope.PROJECT:
321
- raise SkillError(
322
- "Codex project skill scope is not supported",
323
- "use personal scope for Codex, or --agent claude-code/opencode --scope project",
324
- )
325
324
  if agent is SkillAgentSelection.CODEX:
326
- return [_resolve_target(spec, SkillAgent.CODEX, SkillScope.PERSONAL, options.skills_dir)]
325
+ return [_resolve_target(spec, SkillAgent.CODEX, scope, options.skills_dir)]
327
326
  if agent is SkillAgentSelection.CLAUDE_CODE:
328
327
  return [_resolve_target(spec, SkillAgent.CLAUDE_CODE, scope, options.skills_dir)]
329
328
  if agent is SkillAgentSelection.OPENCODE:
330
329
  return [_resolve_target(spec, SkillAgent.OPENCODE, scope, options.skills_dir)]
330
+ if agent is SkillAgentSelection.HERMES:
331
+ return [_resolve_target(spec, SkillAgent.HERMES, scope, options.skills_dir)]
331
332
  raise SkillError(f"invalid --agent {agent!r}")
332
333
 
333
334
 
@@ -350,23 +351,30 @@ def _resolve_target(
350
351
 
351
352
  def _default_skills_dir(agent: SkillAgent, scope: SkillScope) -> Path:
352
353
  if agent is SkillAgent.CODEX:
353
- if scope is SkillScope.PROJECT:
354
- raise SkillError("Codex project skill scope is not supported")
354
+ if scope is not SkillScope.PERSONAL:
355
+ return Path.cwd() / ".codex" / "skills"
355
356
  codex_home = os.environ.get("CODEX_HOME")
356
357
  if codex_home is not None:
357
358
  return Path(codex_home) / "skills"
358
359
  return _home_dir() / ".codex" / "skills"
359
360
  if agent is SkillAgent.CLAUDE_CODE:
360
- if scope is SkillScope.PROJECT:
361
+ if scope is not SkillScope.PERSONAL:
361
362
  return Path.cwd() / ".claude" / "skills"
362
363
  return _home_dir() / ".claude" / "skills"
363
364
  if agent is SkillAgent.OPENCODE:
364
- if scope is SkillScope.PROJECT:
365
+ if scope is not SkillScope.PERSONAL:
365
366
  return Path.cwd() / ".opencode" / "skills"
366
367
  xdg = os.environ.get("XDG_CONFIG_HOME")
367
368
  if xdg is not None:
368
369
  return Path(xdg) / "opencode" / "skills"
369
370
  return _home_dir() / ".config" / "opencode" / "skills"
371
+ if agent is SkillAgent.HERMES:
372
+ if scope is not SkillScope.PERSONAL:
373
+ return Path.cwd() / ".hermes" / "skills"
374
+ hermes_home = os.environ.get("HERMES_HOME")
375
+ if hermes_home is not None:
376
+ return Path(hermes_home) / "skills"
377
+ return _home_dir() / ".hermes" / "skills"
370
378
  raise SkillError(f"unknown agent {agent!r}")
371
379
 
372
380
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agent-first-data
3
- Version: 0.13.2
3
+ Version: 0.14.0
4
4
  Summary: A naming convention that lets AI agents understand your data without being told what it means.
5
5
  License-Expression: MIT
6
6
  Project-URL: Repository, https://github.com/agentfirstkit/agent-first-data
@@ -50,7 +50,7 @@ init_logging_json("INFO", secret_names=("authorization",))
50
50
 
51
51
  ## Reference
52
52
 
53
- - Full convention and API groups: [../docs/overview.md](../docs/overview.md)
54
- - Formal cross-language contract: [../spec/agent-first-data.md](../spec/agent-first-data.md)
55
- - Conformance fixtures: [../spec/fixtures](../spec/fixtures)
56
- - Agent skill: [../skills/agent-first-data.md](../skills/agent-first-data.md)
53
+ - Full convention and API groups: [docs/overview.md](https://github.com/agentfirstkit/agent-first-data/blob/main/docs/overview.md)
54
+ - Formal cross-language contract: [spec/agent-first-data.md](https://github.com/agentfirstkit/agent-first-data/blob/main/spec/agent-first-data.md)
55
+ - Conformance fixtures: [spec/fixtures](https://github.com/agentfirstkit/agent-first-data/tree/main/spec/fixtures)
56
+ - Agent skill: [skills/agent-first-data.md](https://github.com/agentfirstkit/agent-first-data/blob/main/skills/agent-first-data.md)
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agent-first-data"
3
- version = "0.13.2"
3
+ version = "0.14.0"
4
4
  description = "A naming convention that lets AI agents understand your data without being told what it means."
5
5
  license = "MIT"
6
6
  readme = "README.md"
@@ -62,6 +62,7 @@ def test_rejects_unquoted_colon_space():
62
62
  (SkillAgentSelection.CODEX, "codex"),
63
63
  (SkillAgentSelection.CLAUDE_CODE, "claude-code"),
64
64
  (SkillAgentSelection.OPENCODE, "opencode"),
65
+ (SkillAgentSelection.HERMES, "hermes"),
65
66
  ],
66
67
  )
67
68
  def test_install_status_uninstall(agent, expect, tmp_path):
@@ -218,22 +219,48 @@ def test_force_uninstall_removes_symlink_without_following(tmp_path):
218
219
  assert external.read_text(encoding="utf-8") == "external"
219
220
 
220
221
 
221
- def test_all_personal_resolves_three_targets():
222
+ def test_all_personal_resolves_four_targets():
222
223
  opts = SkillOptions(agent=SkillAgentSelection.ALL, scope=SkillScope.PERSONAL)
223
224
  targets = _resolve_targets(spec(), opts)
224
- assert [t.agent for t in targets] == [SkillAgent.CODEX, SkillAgent.CLAUDE_CODE, SkillAgent.OPENCODE]
225
+ assert [t.agent for t in targets] == [
226
+ SkillAgent.CODEX,
227
+ SkillAgent.CLAUDE_CODE,
228
+ SkillAgent.OPENCODE,
229
+ SkillAgent.HERMES,
230
+ ]
225
231
 
226
232
 
227
- def test_all_project_skips_codex():
228
- opts = SkillOptions(agent=SkillAgentSelection.ALL, scope=SkillScope.PROJECT)
233
+ def test_all_workspace_resolves_four_targets():
234
+ opts = SkillOptions(agent=SkillAgentSelection.ALL, scope=SkillScope.WORKSPACE)
229
235
  targets = _resolve_targets(spec(), opts)
230
- assert [t.agent for t in targets] == [SkillAgent.CLAUDE_CODE, SkillAgent.OPENCODE]
236
+ assert [t.agent for t in targets] == [
237
+ SkillAgent.CODEX,
238
+ SkillAgent.CLAUDE_CODE,
239
+ SkillAgent.OPENCODE,
240
+ SkillAgent.HERMES,
241
+ ]
242
+ assert [t.scope for t in targets] == [
243
+ SkillScope.WORKSPACE,
244
+ SkillScope.WORKSPACE,
245
+ SkillScope.WORKSPACE,
246
+ SkillScope.WORKSPACE,
247
+ ]
248
+
249
+
250
+ def test_codex_workspace_scope_uses_codex_skills_dir():
251
+ opts = SkillOptions(agent=SkillAgentSelection.CODEX, scope=SkillScope.WORKSPACE)
252
+ targets = _resolve_targets(spec(), opts)
253
+ assert [t.agent for t in targets] == [SkillAgent.CODEX]
254
+ assert targets[0].scope is SkillScope.WORKSPACE
255
+ assert str(targets[0].skills_dir).endswith(".codex/skills")
231
256
 
232
257
 
233
- def test_codex_project_scope_rejected():
234
- opts = SkillOptions(agent=SkillAgentSelection.CODEX, scope=SkillScope.PROJECT)
235
- with pytest.raises(SkillError):
236
- _resolve_targets(spec(), opts)
258
+ def test_hermes_workspace_scope_uses_hermes_skills_dir():
259
+ opts = SkillOptions(agent=SkillAgentSelection.HERMES, scope=SkillScope.WORKSPACE)
260
+ targets = _resolve_targets(spec(), opts)
261
+ assert [t.agent for t in targets] == [SkillAgent.HERMES]
262
+ assert targets[0].scope is SkillScope.WORKSPACE
263
+ assert str(targets[0].skills_dir).endswith(".hermes/skills")
237
264
 
238
265
 
239
266
  def test_skills_dir_requires_single_agent():