agentnet-cli 0.2.4__tar.gz → 0.3.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 (134) hide show
  1. agentnet_cli-0.3.0/CHANGELOG.md +9 -0
  2. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/CLAUDE.md +2 -1
  3. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/PKG-INFO +4 -1
  4. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/README.md +3 -0
  5. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/pyproject.toml +1 -1
  6. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/cli/core/setup_wizard.py +19 -0
  7. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/cli/main.py +53 -0
  8. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/connectors/claude.py +50 -20
  9. agentnet_cli-0.3.0/src/agentnet_cli/connectors/claude_search_hook.py +150 -0
  10. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/connectors/templates/SKILL.hosted.md +1 -1
  11. agentnet_cli-0.3.0/src/agentnet_cli/connectors/templates/codex/skill.md +10 -0
  12. agentnet_cli-0.3.0/src/agentnet_cli/connectors/templates/cursor/agentnet.mdc +6 -0
  13. agentnet_cli-0.3.0/src/agentnet_cli/connectors/templates/shared/context.md +48 -0
  14. agentnet_cli-0.3.0/src/agentnet_cli/connectors/templates/vscode/instructions.md +1 -0
  15. agentnet_cli-0.3.0/src/agentnet_cli/integrations/claude/plugin/agents/marketplace.md +23 -0
  16. agentnet_cli-0.3.0/src/agentnet_cli/integrations/claude/plugin/hooks/hooks.json +49 -0
  17. agentnet_cli-0.3.0/src/agentnet_cli/integrations/claude/plugin/skills/agentnet/SKILL.md +33 -0
  18. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/integrations/openclaw/openclaw.plugin.json +1 -1
  19. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/integrations/openclaw/package.json +1 -1
  20. agentnet_cli-0.3.0/src/agentnet_cli/integrations/openclaw/skills/agentnet/SKILL.md +32 -0
  21. agentnet_cli-0.3.0/src/agentnet_cli/integrations/shared/discovery-skill.base.md +48 -0
  22. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/marketplace/client.py +22 -0
  23. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/tools/hermes/plugin.yaml +1 -1
  24. agentnet_cli-0.3.0/src/agentnet_cli/tools/hermes/skills/agentnet/SKILL.md +39 -0
  25. agentnet_cli-0.3.0/src/agentnet_cli/tools/hook.py +674 -0
  26. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/tests/test_claude.py +28 -2
  27. agentnet_cli-0.3.0/tests/test_claude_search_hook.py +142 -0
  28. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/tests/test_discovery_skill.py +1 -2
  29. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/tests/test_platform_client.py +37 -0
  30. agentnet_cli-0.3.0/tests/test_skill_hook.py +583 -0
  31. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/uv.lock +1 -1
  32. agentnet_cli-0.2.4/src/agentnet_cli/connectors/templates/codex/skill.md +0 -12
  33. agentnet_cli-0.2.4/src/agentnet_cli/connectors/templates/cursor/agentnet.mdc +0 -8
  34. agentnet_cli-0.2.4/src/agentnet_cli/connectors/templates/shared/context.md +0 -82
  35. agentnet_cli-0.2.4/src/agentnet_cli/connectors/templates/vscode/instructions.md +0 -5
  36. agentnet_cli-0.2.4/src/agentnet_cli/integrations/claude/plugin/agents/marketplace.md +0 -23
  37. agentnet_cli-0.2.4/src/agentnet_cli/integrations/claude/plugin/hooks/hooks.json +0 -22
  38. agentnet_cli-0.2.4/src/agentnet_cli/integrations/claude/plugin/skills/agentnet/SKILL.md +0 -35
  39. agentnet_cli-0.2.4/src/agentnet_cli/integrations/openclaw/skills/agentnet/SKILL.md +0 -32
  40. agentnet_cli-0.2.4/src/agentnet_cli/integrations/shared/discovery-skill.base.md +0 -82
  41. agentnet_cli-0.2.4/src/agentnet_cli/tools/hermes/skills/agentnet/SKILL.md +0 -45
  42. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/.github/workflows/ci.yml +0 -0
  43. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/.github/workflows/publish.yml +0 -0
  44. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/.gitignore +0 -0
  45. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/ARCHITECTURE.md +0 -0
  46. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/docs/archive/superpowers/plans/2026-05-09-hermes-native-plugin.md +0 -0
  47. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/docs/archive/superpowers/plans/2026-05-09-marketplace-cli-and-skill.md +0 -0
  48. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/docs/archive/superpowers/plans/2026-05-10-claude-native-plugin.md +0 -0
  49. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/docs/archive/superpowers/plans/2026-05-10-openclaw-native-plugin.md +0 -0
  50. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/docs/archive/superpowers/specs/2026-05-09-hermes-native-plugin-design.md +0 -0
  51. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/docs/archive/superpowers/specs/2026-05-09-marketplace-cli-and-skill-design.md +0 -0
  52. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/docs/archive/superpowers/specs/2026-05-10-claude-native-plugin-design.md +0 -0
  53. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/docs/archive/superpowers/specs/2026-05-10-openclaw-native-plugin-design.md +0 -0
  54. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/__init__.py +0 -0
  55. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/cli/__init__.py +0 -0
  56. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/cli/core/__init__.py +0 -0
  57. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/cli/core/connect.py +0 -0
  58. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/cli/core/detect.py +0 -0
  59. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/cli/core/disconnect.py +0 -0
  60. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/cli/core/register.py +0 -0
  61. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/cli/core/status.py +0 -0
  62. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/cli/core/updater.py +0 -0
  63. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/cli/marketplace/__init__.py +0 -0
  64. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/cli/marketplace/agent.py +0 -0
  65. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/cli/marketplace/discover.py +0 -0
  66. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/connectors/README.md +0 -0
  67. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/connectors/__init__.py +0 -0
  68. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/connectors/base.py +0 -0
  69. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/connectors/codex.py +0 -0
  70. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/connectors/copilot.py +0 -0
  71. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/connectors/cursor.py +0 -0
  72. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/connectors/hermes.py +0 -0
  73. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/connectors/openclaw.py +0 -0
  74. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/connectors/registry.py +0 -0
  75. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/connectors/shims.py +0 -0
  76. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/connectors/templates/README.md +0 -0
  77. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/connectors/templates/copilot/agentnet.agent.md +0 -0
  78. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/connectors/templates/cursor/agent.md +0 -0
  79. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/connectors/templates/shared/default-chat.md +0 -0
  80. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/connectors/vscode.py +0 -0
  81. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/infra/__init__.py +0 -0
  82. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/infra/config.py +0 -0
  83. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/infra/manifest.py +0 -0
  84. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/infra/package_paths.py +0 -0
  85. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/infra/paths.py +0 -0
  86. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/infra/platform.py +0 -0
  87. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/integrations/claude/.claude-plugin/marketplace.json +0 -0
  88. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/integrations/claude/plugin/.claude-plugin/plugin.json +0 -0
  89. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/integrations/claude/plugin/.mcp.json +0 -0
  90. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/integrations/openclaw/.mcp.json +0 -0
  91. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/integrations/openclaw/index.js +0 -0
  92. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/marketplace/__init__.py +0 -0
  93. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/marketplace/auth.py +0 -0
  94. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/marketplace/catalogs/__init__.py +0 -0
  95. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/marketplace/catalogs/claude_marketplace.py +0 -0
  96. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/marketplace/catalogs/clawhub.py +0 -0
  97. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/marketplace/skills/__init__.py +0 -0
  98. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/marketplace/skills/client.py +0 -0
  99. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/marketplace/skills/discovery.py +0 -0
  100. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/marketplace/skills/skillsmp.py +0 -0
  101. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/tools/__init__.py +0 -0
  102. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/tools/handlers.py +0 -0
  103. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/tools/hermes/__init__.py +0 -0
  104. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/tools/hermes/handlers.py +0 -0
  105. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/tools/hermes/schemas.py +0 -0
  106. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/src/agentnet_cli/tools/mcp_server.py +0 -0
  107. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/tests/__init__.py +0 -0
  108. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/tests/conftest.py +0 -0
  109. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/tests/test_agent_cmd.py +0 -0
  110. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/tests/test_claude_marketplace.py +0 -0
  111. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/tests/test_clawhub_client.py +0 -0
  112. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/tests/test_cli.py +0 -0
  113. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/tests/test_codex.py +0 -0
  114. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/tests/test_config.py +0 -0
  115. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/tests/test_copilot.py +0 -0
  116. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/tests/test_cursor.py +0 -0
  117. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/tests/test_detect.py +0 -0
  118. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/tests/test_discover_cmd.py +0 -0
  119. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/tests/test_discovery.py +0 -0
  120. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/tests/test_e2e.py +0 -0
  121. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/tests/test_hermes.py +0 -0
  122. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/tests/test_hermes_plugin.py +0 -0
  123. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/tests/test_manifest.py +0 -0
  124. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/tests/test_marketplace.py +0 -0
  125. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/tests/test_mcp_tools.py +0 -0
  126. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/tests/test_openclaw.py +0 -0
  127. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/tests/test_paths.py +0 -0
  128. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/tests/test_platform_config.py +0 -0
  129. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/tests/test_register.py +0 -0
  130. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/tests/test_server.py +0 -0
  131. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/tests/test_skills_client.py +0 -0
  132. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/tests/test_skillsmp_client.py +0 -0
  133. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/tests/test_updater.py +0 -0
  134. {agentnet_cli-0.2.4 → agentnet_cli-0.3.0}/tests/test_vscode.py +0 -0
@@ -0,0 +1,9 @@
1
+ # Changelog
2
+
3
+ ## 0.3.0 - 2026-07-18
4
+
5
+ - Add Claude Code search-fire hook support so AgentNet can surface relevant marketplace skills during search workflows.
6
+ - Add `agentnet enable-search-fire` for one-command Claude hook installation.
7
+ - Preserve malformed Claude settings files instead of overwriting them during connector setup.
8
+ - Add setup telemetry hooks and a platform client telemetry helper.
9
+ - Bump bundled Hermes, hosted skill, and OpenClaw integration metadata to match the CLI release.
@@ -8,7 +8,7 @@ CLI tool that detects AI coding agents on your system and connects them to the [
8
8
  - **Package manager:** uv
9
9
  - **CLI framework:** Typer + Rich
10
10
  - **HTTP client:** httpx
11
- - **Testing:** pytest (354 tests), pytest-cov
11
+ - **Testing:** pytest (406 tests), pytest-cov
12
12
  - **CI:** GitHub Actions (lint + test matrix on 3.11/3.12/3.13)
13
13
  - **Publish:** PyPI via trusted publisher (tag `v*`)
14
14
 
@@ -44,6 +44,7 @@ uv run agentnet --help # Run locally
44
44
  - **Hermes Plugin:** `agentnet connect hermes` copies the plugin to `~/.hermes/plugins/agentnet/` and skills to `~/.hermes/skills/agentnet/`, using Hermes's native plugin system.
45
45
  - **OpenClaw Plugin:** `agentnet connect openclaw` delegates to `openclaw plugins install` + `openclaw plugins uninstall` instead of writing files directly. The plugin at `openclaw-plugin/` is a native OpenClaw plugin with `openclaw.plugin.json` manifest, publishable to ClawHub.
46
46
  - **Plugin hint:** The CLI emits a `<claude-code-hint>` tag on stderr when `CLAUDECODE=1` is set, prompting Claude Code users to install the plugin.
47
+ - **Every-prompt skill hook (Claude Code):** `agentnet enable-skill-fire` (and `connect claude`) install three hooks in `~/.claude/settings.json` (also bundled in the plugin's `hooks/hooks.json`). `UserPromptSubmit` → `agentnet skill-hook --pre` spawns a detached worker (0 latency). The worker discovers **installable skill candidates** (`discover_skills` → skills.sh, each carrying a `<repo>@<slug>`) + runs a cheap no-tool `claude -p` **classifier** (Haiku) as the reliable relevance **gate** (a bare yes/no gate isn't — the model just answers). Two-phase so the outcome reaches the hooks fast: **Phase 1** caches the **recommendation list** (relevant skills by name + why + skills.sh link) the moment the gate opens (~12s); **Phase 2** *appends* the top match fetched via `npx skills use <repo>@<slug>` (which downloads the skill — SKILL.md + references — to a temp dir; single-file skills are materialized to a temp `SKILL.md`) as a **concise header (name + description) + the on-disk path**, not a full SKILL.md dump. The injected outcome is thus the list *then* "Applying the top match now:" + that block — the agent surfaces the list and reads/applies the methodology from disk, which is what makes it *act* on the skill rather than just mention it. No files are written to the repo. If content is unavailable (no `npx`/all fetches miss), Phase 2 falls back — behind the same open gate — to the live **Skills Agent** over **brokered A2A**: `PlatformClient.use_agent(agent_id="agentnet-skills-agent", task=…)` with the user's `setup` identity; the platform relays A2A and returns `{status:"settled", agent_response}` in one call. **No skills-agent token is ever held client-side.** Only a `settled` response is used. Injection is layered: `PostToolUse` (matcher `*`) → `agentnet skill-hook --peek` force-steers the agent **mid-flight** once the outcome is ready (`{"decision":"block","reason":…}`, inject-once via an `injected` flag); `Stop` → `agentnet skill-hook --post` is the guaranteed fallback (`{"decision":"block", …additionalContext}`) for no-tool answers. Because the hooks are registered in **both** `settings.json` (via `connect`/`enable-skill-fire`) and the plugin's `hooks.json`, Claude Code may run each twice in parallel — so idempotence is enforced with **atomic `O_EXCL` once-claims**: a per-`(session,prompt)` **spawn marker** so duplicate `--pre` hooks spawn exactly one worker (not 2× classifier cost), and a per-prompt **emit marker** shared by peek + post so exactly one steer fires (and a re-fired `Stop` no-ops). Session-keyed JSON cache `$TMPDIR/agentnet-skill/<session>.json` (`{outcome}`) + sibling `.emitted`/`.<hash>.spawn` markers; `AGENTNET_SKILL_SUBAGENT=1` guards recursion. All best-effort — no token/binary/candidates/unreachable-platform/timeout injects nothing. `agent_id` overridable via `AGENTNET_SKILLS_AGENT_ID`/config. Implemented in `tools/hook.py` + `connectors/claude_search_hook.py`.
47
48
 
48
49
  ## Testing Patterns
49
50
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentnet-cli
3
- Version: 0.2.4
3
+ Version: 0.3.0
4
4
  Summary: Detect AI agents and connect them to the Agent-net marketplace
5
5
  Project-URL: Homepage, https://agentnet.market
6
6
  Project-URL: Repository, https://github.com/TheAgent-net/agentnet-cli
@@ -65,6 +65,9 @@ Requires Python 3.11+.
65
65
  # Install from PyPI
66
66
  pip install agentnet-cli
67
67
 
68
+ # Install this release exactly
69
+ pip install agentnet-cli==0.3.0
70
+
68
71
  # Or run without installing
69
72
  uvx agentnet
70
73
 
@@ -38,6 +38,9 @@ Requires Python 3.11+.
38
38
  # Install from PyPI
39
39
  pip install agentnet-cli
40
40
 
41
+ # Install this release exactly
42
+ pip install agentnet-cli==0.3.0
43
+
41
44
  # Or run without installing
42
45
  uvx agentnet
43
46
 
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agentnet-cli"
3
- version = "0.2.4"
3
+ version = "0.3.0"
4
4
  description = "Detect AI agents and connect them to the Agent-net marketplace"
5
5
  readme = "README.md"
6
6
  requires-python = ">=3.11"
@@ -11,6 +11,8 @@ from rich.console import Console
11
11
  from rich.table import Table
12
12
 
13
13
  from ...infra.config import load_config
14
+ from ...infra.platform import resolve_platform_url
15
+ from ...marketplace.client import PlatformClient
14
16
  from .connect import connect_command
15
17
  from .detect import detect_all
16
18
  from ...infra.paths import AgentName, agent_display_name
@@ -19,7 +21,23 @@ from .register import default_agent_name, register_command
19
21
  console = Console()
20
22
 
21
23
 
24
+ def _send_telemetry(event_type: str, **metadata: str) -> None:
25
+ """Best-effort telemetry to platform; never blocks setup."""
26
+ try:
27
+ import httpx
28
+
29
+ config = load_config() or {}
30
+ base = resolve_platform_url(config=config)
31
+ token = config.get("api_token", "")
32
+ http_client = httpx.Client(timeout=5.0)
33
+ with PlatformClient(base_url=base, api_token=token, http_client=http_client) as client:
34
+ client.send_telemetry(event_type=event_type, metadata=metadata or None)
35
+ except Exception:
36
+ pass
37
+
38
+
22
39
  def setup_command(platform_url: str | None = None, *, choose: bool = False) -> None:
40
+ _send_telemetry("cli_setup")
23
41
  config = load_config()
24
42
  if not config or not config.get("api_token"):
25
43
  console.print()
@@ -62,6 +80,7 @@ def setup_command(platform_url: str | None = None, *, choose: bool = False) -> N
62
80
  f" [bold]Step 3:[/bold] Configuring all detected agents ({len(targets)})"
63
81
  )
64
82
  connect_command(connect_all=True)
83
+ _send_telemetry("cli_setup_complete", connectors=",".join(targets))
65
84
 
66
85
 
67
86
  def _available_targets(results) -> list[str]:
@@ -301,6 +301,59 @@ def mcp_serve() -> None:
301
301
  serve()
302
302
 
303
303
 
304
+ @app.command(name="skill-hook", hidden=True)
305
+ def skill_hook(
306
+ pre: bool = typer.Option(False, "--pre", help="UserPromptSubmit: spawn the discovery worker"),
307
+ peek: bool = typer.Option(False, "--peek", help="PostToolUse: steer the agent mid-flight"),
308
+ post: bool = typer.Option(False, "--post", help="Stop: fold in relevant AgentNet skills"),
309
+ fetch: bool = typer.Option(False, "--fetch", help="Detached worker: discover + cache (internal)"),
310
+ session: str = typer.Option("", "--session", help="Session id (worker)"),
311
+ query: str = typer.Option("", "--query", help="Prompt text (worker)"),
312
+ limit: int = typer.Option(5, "--limit", help="Max skills to suggest"),
313
+ hook_timeout: float = typer.Option(
314
+ 3.0, "--timeout", help="Max seconds a hook waits for the subagent's result",
315
+ ),
316
+ ) -> None:
317
+ """Claude Code every-prompt hooks — surface relevant AgentNet skills (internal).
318
+
319
+ ``--pre`` (UserPromptSubmit) spawns a detached skill-scout worker; ``--peek`` (PostToolUse)
320
+ steers the agent mid-flight once the outcome is ready; ``--post`` (Stop) is the guaranteed
321
+ fallback. Best-effort: nothing/exit 0 on error.
322
+ """
323
+ from ..tools.hook import run_fetch, run_peek, run_post, run_pre
324
+
325
+ if fetch:
326
+ run_fetch(session=session, query=query, limit=limit, timeout=hook_timeout)
327
+ elif pre:
328
+ run_pre(limit=limit, timeout=hook_timeout)
329
+ elif peek:
330
+ run_peek(limit=limit, timeout=hook_timeout)
331
+ else: # default and --post
332
+ run_post(limit=limit, timeout=hook_timeout)
333
+
334
+
335
+ @app.command(name="enable-skill-fire")
336
+ def enable_skill_fire(
337
+ remove: bool = typer.Option(False, "--remove", help="Remove the hook instead of installing"),
338
+ ) -> None:
339
+ """Fire AgentNet on every Claude Code prompt (writes ~/.claude/settings.json)."""
340
+ from ..connectors.claude_search_hook import SettingsHookError, install, uninstall
341
+
342
+ try:
343
+ changed, path = uninstall() if remove else install()
344
+ except SettingsHookError as exc:
345
+ console.print(f"[red]✗[/red] {exc}")
346
+ raise typer.Exit(1) from exc
347
+ action = "removed" if remove else "installed"
348
+ if changed:
349
+ console.print(f"[green]✓[/green] AgentNet skill hook {action} in [bold]{path}[/bold]")
350
+ if not remove:
351
+ console.print(" [dim]Restart Claude Code — every prompt now fires AgentNet.[/dim]")
352
+ else:
353
+ state = "not present" if remove else "already installed"
354
+ console.print(f"[dim]No change ({state}): {path}[/dim]")
355
+
356
+
304
357
  # -- Marketplace commands --
305
358
  from .marketplace.agent import agent as _agent_fn # noqa: E402
306
359
  from .marketplace.discover import discover as _discover_fn # noqa: E402
@@ -45,34 +45,64 @@ class ClaudeConnector(AgentConnector):
45
45
  errors=["Claude Code not found. Install it from https://code.claude.com"],
46
46
  )
47
47
 
48
- marketplace_src = _marketplace_source()
49
-
50
- proc = subprocess.run(
51
- ["claude", "plugin", "marketplace", "add", marketplace_src, "--scope", "user"],
52
- capture_output=True,
53
- timeout=_SUBPROCESS_TIMEOUT,
54
- )
55
- if proc.returncode != 0:
56
- msg = proc.stderr.decode(errors="replace").strip()
57
- return ConnectionResult(success=False, errors=[f"marketplace add failed: {msg}"])
58
-
59
- proc = subprocess.run(
60
- ["claude", "plugin", "install", _PLUGIN_ID, "--scope", "user"],
61
- capture_output=True,
62
- timeout=_SUBPROCESS_TIMEOUT,
63
- )
64
- if proc.returncode != 0:
65
- msg = proc.stderr.decode(errors="replace").strip()
66
- return ConnectionResult(success=False, errors=[f"plugin install failed: {msg}"])
48
+ # 1. Install the AgentNet every-prompt hook straight into settings.json.
49
+ # This is the reliable path: every prompt fires AgentNet (discover +
50
+ # fold in relevant skills). It does NOT depend on the plugin marketplace
51
+ # flow (which errors on some Claude Code versions), so connect succeeds
52
+ # even if that fails.
53
+ from .claude_search_hook import SettingsHookError
54
+ from .claude_search_hook import install as install_search_hook
55
+
56
+ errors: list[str] = []
57
+ try:
58
+ install_search_hook()
59
+ except SettingsHookError as exc:
60
+ # A malformed settings.json must not be overwritten; report and preserve it, but let
61
+ # the rest of connect (MCP + plugin) still run.
62
+ errors.append(str(exc))
63
+
64
+ # 2. Best-effort: install the plugin for the discovery MCP tools and
65
+ # session hooks. `marketplace add` takes only <source> (no --scope).
66
+ # Failures here are non-fatal the prompt hook above is already live.
67
+ try:
68
+ marketplace_src = _marketplace_source()
69
+ proc = subprocess.run(
70
+ ["claude", "plugin", "marketplace", "add", marketplace_src],
71
+ capture_output=True,
72
+ timeout=_SUBPROCESS_TIMEOUT,
73
+ )
74
+ if proc.returncode == 0:
75
+ proc = subprocess.run(
76
+ ["claude", "plugin", "install", _PLUGIN_ID, "--scope", "user"],
77
+ capture_output=True,
78
+ timeout=_SUBPROCESS_TIMEOUT,
79
+ )
80
+ if proc.returncode != 0:
81
+ errors.append(
82
+ "plugin install (discovery tools) failed: "
83
+ + proc.stderr.decode(errors="replace").strip()
84
+ )
85
+ else:
86
+ errors.append(
87
+ "plugin marketplace add (discovery tools) failed: "
88
+ + proc.stderr.decode(errors="replace").strip()
89
+ )
90
+ except Exception as exc: # noqa: BLE001 — plugin step is best-effort
91
+ errors.append(f"plugin step skipped: {exc}")
67
92
 
68
93
  self._cleanup_legacy()
69
94
 
70
95
  return ConnectionResult(
71
96
  success=True,
72
- mcp_entry={"scope": "plugin", "plugin_name": _PLUGIN_ID},
97
+ mcp_entry={"scope": "settings-hook", "search_fire": True},
98
+ errors=errors,
73
99
  )
74
100
 
75
101
  def disconnect(self, connection_manifest: dict[str, Any]) -> bool:
102
+ from .claude_search_hook import uninstall as uninstall_search_hook
103
+
104
+ uninstall_search_hook()
105
+
76
106
  claude_bin = shutil.which("claude")
77
107
  if not claude_bin:
78
108
  return True
@@ -0,0 +1,150 @@
1
+ """Install the AgentNet every-prompt hooks directly into Claude Code's settings.json.
2
+
3
+ Registers three hooks: ``UserPromptSubmit`` (spawn a background discovery worker),
4
+ ``PostToolUse`` (steer the agent mid-flight once the outcome is ready), and ``Stop``
5
+ (guaranteed fallback that folds the outcome in). Same effect as the bundled plugin, but
6
+ written straight into ``~/.claude/settings.json`` so it works in one command regardless of
7
+ Claude Code version.
8
+
9
+ ``PostToolUse`` is tool-scoped so its block carries a ``*`` matcher (all tools);
10
+ ``UserPromptSubmit`` and ``Stop`` are not tool-scoped, so their blocks carry no matcher.
11
+ """
12
+
13
+ from __future__ import annotations
14
+
15
+ import json
16
+ from typing import Any
17
+
18
+ from ..infra.paths import AgentName, agent_config_root
19
+
20
+
21
+ class SettingsHookError(Exception):
22
+ """Raised when ~/.claude/settings.json exists but is malformed, so it must not be overwritten."""
23
+
24
+
25
+ # event -> (command, matcher). matcher=None for non-tool-scoped events.
26
+ _HOOKS: dict[str, tuple[str, str | None]] = {
27
+ "UserPromptSubmit": ("agentnet skill-hook --pre", None),
28
+ "PostToolUse": ("agentnet skill-hook --peek", "*"),
29
+ "Stop": ("agentnet skill-hook --post", None),
30
+ }
31
+
32
+
33
+ def _settings_path():
34
+ return agent_config_root(AgentName.CLAUDE) / "settings.json"
35
+
36
+
37
+ def _load(path) -> dict[str, Any]:
38
+ """Parse an existing settings.json to a dict.
39
+
40
+ Absent file -> ``{}`` (a fresh config). A file that *exists* but is unparseable or not a JSON
41
+ object is NOT the same as empty — overwriting it would erase the user's real config — so raise
42
+ ``SettingsHookError`` and let the caller preserve the file untouched.
43
+ """
44
+ if not path.exists():
45
+ return {}
46
+ try:
47
+ data = json.loads(path.read_text())
48
+ except (json.JSONDecodeError, OSError) as exc:
49
+ raise SettingsHookError(f"{path} is not valid JSON — fix it and re-run") from exc
50
+ if not isinstance(data, dict):
51
+ raise SettingsHookError(f"{path} is not a JSON object — fix it and re-run")
52
+ return data
53
+
54
+
55
+ def _is_agentnet_cmd(cmd: Any) -> bool:
56
+ return isinstance(cmd, str) and cmd.startswith("agentnet skill-hook")
57
+
58
+
59
+ def _block(command: str, matcher: str | None) -> dict[str, Any]:
60
+ block: dict[str, Any] = {}
61
+ if matcher is not None:
62
+ block["matcher"] = matcher
63
+ block["hooks"] = [{"type": "command", "command": command}]
64
+ return block
65
+
66
+
67
+ def _event_has_agentnet(blocks: list[Any]) -> bool:
68
+ for b in blocks:
69
+ if isinstance(b, dict) and any(
70
+ isinstance(h, dict) and _is_agentnet_cmd(h.get("command")) for h in b.get("hooks", [])
71
+ ):
72
+ return True
73
+ return False
74
+
75
+
76
+ def install() -> tuple[bool, str]:
77
+ """Add the UserPromptSubmit/Stop hooks to settings.json. Returns (changed, path)."""
78
+ path = _settings_path()
79
+ path.parent.mkdir(parents=True, exist_ok=True)
80
+ data = _load(path)
81
+ hooks = data.setdefault("hooks", {})
82
+ if not isinstance(hooks, dict):
83
+ hooks = {}
84
+ data["hooks"] = hooks
85
+
86
+ changed = False
87
+ for event, (command, matcher) in _HOOKS.items():
88
+ blocks = hooks.get(event)
89
+ if blocks is None:
90
+ blocks = []
91
+ hooks[event] = blocks
92
+ elif isinstance(blocks, dict):
93
+ # A single hook block stored as an object — preserve it by wrapping, don't discard it.
94
+ blocks = [blocks]
95
+ hooks[event] = blocks
96
+ elif not isinstance(blocks, list):
97
+ # Some other scalar we don't understand — leave the user's value untouched.
98
+ continue
99
+ if not _event_has_agentnet(blocks):
100
+ blocks.append(_block(command, matcher))
101
+ changed = True
102
+
103
+ if changed:
104
+ path.write_text(json.dumps(data, indent=2) + "\n")
105
+ return changed, str(path)
106
+
107
+
108
+ def uninstall() -> tuple[bool, str]:
109
+ """Remove the AgentNet every-prompt hooks from settings.json. Returns (changed, path).
110
+
111
+ A malformed settings file has nothing removable and must not be overwritten, so treat it as
112
+ a no-op rather than raising into the (best-effort) disconnect path.
113
+ """
114
+ path = _settings_path()
115
+ try:
116
+ data = _load(path)
117
+ except SettingsHookError:
118
+ return False, str(path)
119
+ hooks = data.get("hooks")
120
+ if not isinstance(hooks, dict):
121
+ return False, str(path)
122
+
123
+ changed = False
124
+ for event in list(_HOOKS):
125
+ blocks = hooks.get(event)
126
+ if not isinstance(blocks, list):
127
+ continue
128
+ kept = [
129
+ b
130
+ for b in blocks
131
+ if not (
132
+ isinstance(b, dict)
133
+ and any(
134
+ isinstance(h, dict) and _is_agentnet_cmd(h.get("command"))
135
+ for h in b.get("hooks", [])
136
+ )
137
+ )
138
+ ]
139
+ if len(kept) != len(blocks):
140
+ changed = True
141
+ if kept:
142
+ hooks[event] = kept
143
+ else:
144
+ hooks.pop(event, None)
145
+
146
+ if changed:
147
+ if not hooks:
148
+ data.pop("hooks", None)
149
+ path.write_text(json.dumps(data, indent=2) + "\n")
150
+ return changed, str(path)
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: agentnet
3
- version: 0.2.3
3
+ version: 0.3.0
4
4
  description: >-
5
5
  Discover agents and community skills for any task. Run `agentnet discover`
6
6
  when the user needs help with anything — weather, translation, code review, design,
@@ -0,0 +1,10 @@
1
+ ---
2
+ name: agentnet
3
+ description: >-
4
+ Before building a non-trivial capability from scratch (auth, tests, security review,
5
+ deployment, data/PDF parsing, scraping, CI/CD, integrations), find an existing AgentNet
6
+ agent or skill that already does it. Search with agentnet_discover_agents first; skip for
7
+ trivial edits or conversational turns.
8
+ ---
9
+
10
+ {{CONTEXT}}
@@ -0,0 +1,6 @@
1
+ ---
2
+ description: "AgentNet — before building a non-trivial capability (auth, tests, security review, deployment, parsing, scraping, CI/CD, integrations), find an existing agent/skill for it via agentnet_discover_agents."
3
+ alwaysApply: true
4
+ ---
5
+
6
+ {{CONTEXT}}
@@ -0,0 +1,48 @@
1
+ # AgentNet — discovery for the agent network
2
+
3
+ You are connected to **AgentNet**, the discovery/routing layer for AI agents and skills
4
+ ("Google for agents"). When you are about to **build a non-trivial capability**, an existing
5
+ agent or skill often already does it — surface that instead of reinventing it.
6
+
7
+ ## When to use
8
+ Before you implement something substantive — authentication, tests, a security review, a
9
+ deployment pipeline, PDF/data parsing, web scraping, CI/CD, a third-party integration — call
10
+ `agentnet_discover_agents` (or `agentnet_search`) with the capability you're about to build.
11
+ Skip it for trivial edits, questions, or purely conversational turns — no noise.
12
+
13
+ ## How
14
+ 1. **Discover** — `agentnet_discover_agents` (agents + skills by capability) or `agentnet_search`
15
+ (unified) with a concrete description of *what you're building*.
16
+ 2. **Surface** — briefly present the best match (name, what it does, how to use/install it),
17
+ then continue your work. Let the user choose; don't force it.
18
+ 3. **Inspect** — `agentnet_get_agent` only if the user wants detail on a specific agent.
19
+
20
+ ## Tools
21
+
22
+ ### agentnet_discover_agents
23
+ Agents + skills by capability. **Start here** when you're about to build something.
24
+ - **query** (string, required): the capability you're about to build
25
+ - **limit** (int, default 20): max results
26
+
27
+ ### agentnet_search
28
+ Unified search across agents, skills, listings, and plugins. Use for a broad look.
29
+ - **query** (string, required): what you need
30
+ - **type** (string, default "all"): all, agents, skills, plugins, listings, marketplace
31
+ - **category** (string, optional) · **limit** (int, default 20) · **max_price** (int, optional)
32
+
33
+ ### agentnet_get_agent
34
+ Full detail on a specific agent (skills, trust score).
35
+ - **agent_id** (string, required): from discovery results
36
+
37
+ ### agentnet_discover_skills
38
+ AI-ranked skill discovery by natural-language use case. Use when narrowing to skills.
39
+ - **use_case** (string, required) · **limit** (int, default 10)
40
+
41
+ Other catalogs (only when you need a specific source): `agentnet_discover`,
42
+ `agentnet_search_skills`, `agentnet_search_skillsmp`, `agentnet_search_claude_plugins`,
43
+ `agentnet_search_clawhub`.
44
+
45
+ ## Guidelines
46
+ - **Capability-first** — match on *what you're about to build*, not stray keywords.
47
+ - **Present, don't transact** — you surface options; you never hire, pay, or settle.
48
+ - **Stay quiet** when nothing relevant fits.
@@ -0,0 +1,23 @@
1
+ ---
2
+ name: marketplace
3
+ description: >-
4
+ AgentNet discovery specialist. Use when the user wants to find an existing agent or skill
5
+ for a capability they're about to build.
6
+ model: sonnet
7
+ tools: mcp__agentnet__*
8
+ ---
9
+
10
+ You are an AgentNet discovery specialist connected to the agent network ("Google for agents").
11
+ Your job: when the user is about to build a capability, find an existing agent or skill for it.
12
+
13
+ ## Workflow
14
+ 1. **Discover** → `agentnet_discover_agents` (agents + skills by capability) or `agentnet_search`
15
+ (unified) with a concrete description of what they're building
16
+ 2. **Present** → show the best matches with names, what they do, and how to use/install them
17
+ 3. **Inspect** → `agentnet_get_agent` only if the user wants more detail on a specific agent
18
+
19
+ ## Guidelines
20
+ - **Capability-first** — match on *what the user is about to build*, not stray keywords
21
+ - Present a short ranked list — do not dump raw JSON
22
+ - **Present, don't transact** — you surface options; you never hire, pay, or settle
23
+ - Stay quiet when nothing relevant fits
@@ -0,0 +1,49 @@
1
+ {
2
+ "hooks": {
3
+ "SessionStart": [
4
+ {
5
+ "hooks": [
6
+ {
7
+ "type": "command",
8
+ "command": "test -f ~/.agentnet/config.json || echo '[AgentNet] Not authenticated. Run: agentnet setup' >&2"
9
+ },
10
+ {
11
+ "type": "command",
12
+ "command": "agentnet update --quiet 2>/dev/null || true"
13
+ }
14
+ ]
15
+ }
16
+ ],
17
+ "UserPromptSubmit": [
18
+ {
19
+ "hooks": [
20
+ {
21
+ "type": "command",
22
+ "command": "agentnet skill-hook --pre"
23
+ }
24
+ ]
25
+ }
26
+ ],
27
+ "PostToolUse": [
28
+ {
29
+ "matcher": "*",
30
+ "hooks": [
31
+ {
32
+ "type": "command",
33
+ "command": "agentnet skill-hook --peek"
34
+ }
35
+ ]
36
+ }
37
+ ],
38
+ "Stop": [
39
+ {
40
+ "hooks": [
41
+ {
42
+ "type": "command",
43
+ "command": "agentnet skill-hook --post"
44
+ }
45
+ ]
46
+ }
47
+ ]
48
+ }
49
+ }
@@ -0,0 +1,33 @@
1
+ ---
2
+ name: agentnet
3
+ description: >-
4
+ Before building a non-trivial capability from scratch (auth, tests, security review,
5
+ deployment, data/PDF parsing, scraping, CI/CD, integrations, etc.), find an existing AgentNet
6
+ agent or skill that already does it. Triggers when the user asks to implement / build / set up
7
+ / add a capability a reusable skill or agent might already cover.
8
+ allowed-tools: mcp__agentnet__*
9
+ ---
10
+
11
+ # AgentNet — discovery for the agent network
12
+
13
+ You are connected to **AgentNet**, the discovery/routing layer for AI agents and skills
14
+ ("Google for agents"). When you are about to **build a non-trivial capability**, an existing
15
+ agent or skill often already does it — surface that instead of reinventing it.
16
+
17
+ ## When to use
18
+ Before you implement something substantive — authentication, tests, a security review, a
19
+ deployment pipeline, PDF/data parsing, web scraping, CI/CD, a third-party integration — call
20
+ `agentnet_discover_agents` (or `agentnet_search`) with the capability you're about to build.
21
+ Skip it for trivial edits, questions, or conversational turns.
22
+
23
+ ## How
24
+ 1. **Discover** — `agentnet_discover_agents` (agents + skills by capability) or `agentnet_search`
25
+ (unified) with a concrete description of what you're building.
26
+ 2. **Surface** — briefly present the best match (name, what it does, how to use/install it),
27
+ then continue your work. Let the user choose; don't force it.
28
+ 3. **Inspect** — `agentnet_get_agent` only if the user wants detail on a specific agent.
29
+
30
+ ## Guidelines
31
+ - **Capability-first** — match on *what you're about to build*, not stray keywords.
32
+ - **Present, don't transact** — you surface options; you never hire, pay, or settle.
33
+ - **Stay quiet** when nothing relevant fits — no noise on trivial turns.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "agentnet",
3
3
  "name": "AgentNet Marketplace",
4
- "version": "0.2.3",
4
+ "version": "0.3.0",
5
5
  "description": "Discover agents, listings, skills, and plugins on the Agent-net marketplace",
6
6
  "skills": ["skills/agentnet"],
7
7
  "contracts": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentnet/openclaw-plugin",
3
- "version": "0.2.3",
3
+ "version": "0.3.0",
4
4
  "description": "AgentNet marketplace plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -0,0 +1,32 @@
1
+ ---
2
+ name: agentnet
3
+ description: >-
4
+ Before building a non-trivial capability from scratch (auth, tests, security review,
5
+ deployment, data/PDF parsing, scraping, CI/CD, integrations), find an existing AgentNet
6
+ agent or skill that already does it. Search with agentnet_discover_agents first; skip for
7
+ trivial edits or conversational turns.
8
+ ---
9
+
10
+ # AgentNet — discovery for the agent network
11
+
12
+ You are connected to **AgentNet**, the discovery/routing layer for AI agents and skills
13
+ ("Google for agents"). When you are about to **build a non-trivial capability**, an existing
14
+ agent or skill often already does it — surface that instead of reinventing it.
15
+
16
+ ## When to use
17
+ Before you implement something substantive — authentication, tests, a security review, a
18
+ deployment pipeline, PDF/data parsing, web scraping, CI/CD, a third-party integration — call
19
+ `agentnet_discover_agents` (or `agentnet_search`) with the capability you're about to build.
20
+ Skip it for trivial edits, questions, or conversational turns.
21
+
22
+ ## How
23
+ 1. **Discover** — `agentnet_discover_agents` (agents + skills by capability) or `agentnet_search`
24
+ (unified) with a concrete description of what you're building.
25
+ 2. **Surface** — briefly present the best match (name, what it does, how to use/install it),
26
+ then continue. Let the user choose; don't force it.
27
+ 3. **Inspect** — `agentnet_get_agent` only if the user wants detail on a specific agent.
28
+
29
+ ## Guidelines
30
+ - **Capability-first** — match on *what you're about to build*, not stray keywords.
31
+ - **Present, don't transact** — you surface options; you never hire, pay, or settle.
32
+ - **Stay quiet** when nothing relevant fits.