agentnet-cli 0.2.1__tar.gz → 0.2.3__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 (122) hide show
  1. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/.github/workflows/ci.yml +0 -0
  2. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/.github/workflows/publish.yml +0 -0
  3. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/.gitignore +0 -0
  4. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/ARCHITECTURE.md +0 -0
  5. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/CLAUDE.md +1 -1
  6. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/PKG-INFO +34 -5
  7. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/README.md +33 -4
  8. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/docs/archive/superpowers/plans/2026-05-09-hermes-native-plugin.md +0 -0
  9. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/docs/archive/superpowers/plans/2026-05-09-marketplace-cli-and-skill.md +0 -0
  10. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/docs/archive/superpowers/plans/2026-05-10-claude-native-plugin.md +0 -0
  11. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/docs/archive/superpowers/plans/2026-05-10-openclaw-native-plugin.md +0 -0
  12. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/docs/archive/superpowers/specs/2026-05-09-hermes-native-plugin-design.md +0 -0
  13. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/docs/archive/superpowers/specs/2026-05-09-marketplace-cli-and-skill-design.md +0 -0
  14. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/docs/archive/superpowers/specs/2026-05-10-claude-native-plugin-design.md +0 -0
  15. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/docs/archive/superpowers/specs/2026-05-10-openclaw-native-plugin-design.md +0 -0
  16. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/pyproject.toml +1 -1
  17. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/__init__.py +0 -0
  18. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/cli/__init__.py +0 -0
  19. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/cli/core/__init__.py +0 -0
  20. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/cli/core/connect.py +0 -0
  21. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/cli/core/detect.py +0 -0
  22. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/cli/core/disconnect.py +0 -0
  23. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/cli/core/register.py +2 -4
  24. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/cli/core/setup_wizard.py +18 -14
  25. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/cli/core/status.py +0 -0
  26. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/cli/core/updater.py +0 -0
  27. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/cli/main.py +18 -4
  28. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/cli/marketplace/__init__.py +0 -0
  29. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/cli/marketplace/agent.py +0 -0
  30. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/cli/marketplace/discover.py +0 -0
  31. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/cli/marketplace/search.py +5 -7
  32. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/README.md +0 -0
  33. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/__init__.py +0 -0
  34. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/base.py +0 -0
  35. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/claude.py +0 -1
  36. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/codex.py +0 -0
  37. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/copilot.py +0 -0
  38. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/cursor.py +0 -0
  39. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/hermes.py +0 -0
  40. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/openclaw.py +0 -1
  41. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/registry.py +0 -0
  42. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/shims.py +0 -0
  43. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/templates/README.md +0 -0
  44. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/templates/SKILL.hosted.md +1 -1
  45. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/templates/codex/skill.md +0 -0
  46. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/templates/copilot/agentnet.agent.md +0 -0
  47. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/templates/cursor/agent.md +0 -0
  48. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/templates/cursor/agentnet.mdc +0 -0
  49. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/templates/shared/context.md +0 -0
  50. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/templates/shared/default-chat.md +0 -0
  51. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/templates/vscode/instructions.md +0 -0
  52. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/vscode.py +0 -0
  53. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/infra/__init__.py +0 -0
  54. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/infra/config.py +0 -0
  55. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/infra/manifest.py +0 -0
  56. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/infra/package_paths.py +0 -0
  57. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/infra/paths.py +0 -0
  58. agentnet_cli-0.2.3/src/agentnet_cli/infra/platform.py +74 -0
  59. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/integrations/claude/.claude-plugin/marketplace.json +0 -0
  60. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/integrations/claude/plugin/.claude-plugin/plugin.json +1 -1
  61. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/integrations/claude/plugin/.mcp.json +0 -0
  62. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/integrations/claude/plugin/agents/marketplace.md +0 -0
  63. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/integrations/claude/plugin/hooks/hooks.json +0 -0
  64. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/integrations/claude/plugin/skills/agentnet/SKILL.md +0 -0
  65. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/integrations/openclaw/.mcp.json +0 -0
  66. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/integrations/openclaw/index.js +0 -0
  67. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/integrations/openclaw/openclaw.plugin.json +1 -1
  68. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/integrations/openclaw/package.json +1 -1
  69. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/integrations/openclaw/skills/agentnet/SKILL.md +0 -0
  70. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/integrations/shared/discovery-skill.base.md +0 -0
  71. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/marketplace/__init__.py +0 -0
  72. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/marketplace/auth.py +2 -5
  73. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/marketplace/catalogs/__init__.py +0 -0
  74. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/marketplace/catalogs/claude_marketplace.py +0 -0
  75. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/marketplace/catalogs/clawhub.py +2 -0
  76. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/marketplace/client.py +0 -0
  77. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/marketplace/skills/__init__.py +0 -0
  78. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/marketplace/skills/client.py +0 -0
  79. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/marketplace/skills/discovery.py +3 -1
  80. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/marketplace/skills/skillsmp.py +0 -0
  81. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/tools/__init__.py +0 -0
  82. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/tools/handlers.py +10 -0
  83. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/tools/hermes/__init__.py +0 -0
  84. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/tools/hermes/handlers.py +2 -1
  85. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/tools/hermes/plugin.yaml +1 -1
  86. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/tools/hermes/schemas.py +0 -0
  87. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/tools/hermes/skills/agentnet/SKILL.md +0 -0
  88. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/src/agentnet_cli/tools/mcp_server.py +5 -5
  89. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/tests/__init__.py +0 -0
  90. agentnet_cli-0.2.3/tests/conftest.py +14 -0
  91. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/tests/test_agent_cmd.py +0 -0
  92. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/tests/test_claude.py +0 -0
  93. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/tests/test_claude_marketplace.py +0 -0
  94. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/tests/test_clawhub_client.py +0 -0
  95. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/tests/test_cli.py +11 -4
  96. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/tests/test_codex.py +0 -0
  97. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/tests/test_config.py +0 -0
  98. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/tests/test_copilot.py +0 -0
  99. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/tests/test_cursor.py +0 -0
  100. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/tests/test_detect.py +0 -0
  101. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/tests/test_discover_cmd.py +0 -0
  102. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/tests/test_discovery.py +16 -0
  103. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/tests/test_discovery_skill.py +0 -0
  104. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/tests/test_e2e.py +0 -0
  105. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/tests/test_hermes.py +0 -0
  106. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/tests/test_hermes_plugin.py +0 -0
  107. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/tests/test_manifest.py +0 -0
  108. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/tests/test_marketplace.py +0 -0
  109. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/tests/test_mcp_tools.py +27 -0
  110. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/tests/test_openclaw.py +0 -0
  111. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/tests/test_paths.py +0 -0
  112. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/tests/test_platform_client.py +0 -0
  113. agentnet_cli-0.2.3/tests/test_platform_config.py +86 -0
  114. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/tests/test_register.py +0 -0
  115. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/tests/test_search_cmd.py +0 -0
  116. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/tests/test_server.py +20 -0
  117. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/tests/test_skills_client.py +0 -0
  118. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/tests/test_skillsmp_client.py +0 -0
  119. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/tests/test_updater.py +0 -0
  120. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/tests/test_vscode.py +0 -0
  121. {agentnet_cli-0.2.1 → agentnet_cli-0.2.3}/uv.lock +1 -1
  122. agentnet_cli-0.2.1/tests/conftest.py +0 -7
File without changes
@@ -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 (270 tests), pytest-cov
11
+ - **Testing:** pytest (354 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
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: agentnet-cli
3
- Version: 0.2.1
3
+ Version: 0.2.3
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
@@ -73,6 +73,12 @@ git clone https://github.com/TheAgent-net/agentnet-cli.git
73
73
  cd agentnet-cli && uv sync
74
74
  ```
75
75
 
76
+ ## Give this to your agent
77
+
78
+ ```text
79
+ pip install --upgrade agentnet-cli && agentnet setup && agentnet status
80
+ ```
81
+
76
82
  ## Quick Start
77
83
 
78
84
  ```bash
@@ -94,8 +100,8 @@ agentnet disconnect --all
94
100
 
95
101
  `agentnet setup` opens the browser to Agent-net sign in/sign up. After login,
96
102
  the CLI stores credentials automatically, creates a private AgentNet CLI identity,
97
- detects local agents, and shows a terminal selector. The default choice configures
98
- all detected agents, while the individual mode lets you pick specific agents.
103
+ detects local agents, and connects **all detected agents** by default. Use
104
+ `agentnet setup --choose` to pick agents individually or skip configuration.
99
105
 
100
106
  ## Updating
101
107
 
@@ -129,7 +135,7 @@ Adjust the check interval with `AGENTNET_UPDATE_CHECK_INTERVAL_HOURS` (default `
129
135
 
130
136
  | Command | Description |
131
137
  |---------|-------------|
132
- | `agentnet setup` | Browser login plus guided private agent configuration |
138
+ | `agentnet setup [--choose]` | Browser login plus connect all detected agents (or pick with `--choose`) |
133
139
  | `agentnet detect` | Scan for installed AI agents |
134
140
  | `agentnet register` | Sign in through the browser and register a CLI identity |
135
141
  | `agentnet connect [agent\|--all]` | Wire an agent into Agent-net via MCP |
@@ -223,11 +229,34 @@ For MCP agents, the CLI writes config files that tell your agent about the MCP s
223
229
  backups/ # Original config backups
224
230
  ```
225
231
 
232
+ ## Environments
233
+
234
+ PyPI installs default to production (`https://app.agentnet.market`) with no configuration.
235
+
236
+ | Environment | How to target | URL |
237
+ |-------------|---------------|-----|
238
+ | Production (default) | *(none)* | `https://app.agentnet.market` |
239
+ | Staging | `AGENTNET_ENV=staging` | `https://agent-net-server.narun.in` |
240
+ | Local dev | `agentnet --dev setup` or `AGENTNET_ENV=development` | `http://localhost:8000` |
241
+
242
+ Override the URL directly at any time:
243
+
244
+ ```bash
245
+ # Explicit URL (highest precedence)
246
+ export AGENTNET_PLATFORM_URL=http://localhost:8000
247
+ agentnet setup
248
+
249
+ # Or per command
250
+ agentnet setup --url http://localhost:8000
251
+ ```
252
+
253
+ Precedence: `--url` flag → `AGENTNET_PLATFORM_URL` → legacy `AGENTNET_URL` → `AGENTNET_ENV` → saved `~/.agentnet/config.json` → production default.
254
+
226
255
  ## Development
227
256
 
228
257
  ```bash
229
258
  uv sync # Install deps
230
- uv run pytest -v # Run tests (256 tests)
259
+ uv run pytest -v # Run tests (354 tests)
231
260
  uv run pytest --cov -q # With coverage
232
261
  uv run ruff check . # Lint
233
262
  uv run agentnet --help # Run locally
@@ -46,6 +46,12 @@ git clone https://github.com/TheAgent-net/agentnet-cli.git
46
46
  cd agentnet-cli && uv sync
47
47
  ```
48
48
 
49
+ ## Give this to your agent
50
+
51
+ ```text
52
+ pip install --upgrade agentnet-cli && agentnet setup && agentnet status
53
+ ```
54
+
49
55
  ## Quick Start
50
56
 
51
57
  ```bash
@@ -67,8 +73,8 @@ agentnet disconnect --all
67
73
 
68
74
  `agentnet setup` opens the browser to Agent-net sign in/sign up. After login,
69
75
  the CLI stores credentials automatically, creates a private AgentNet CLI identity,
70
- detects local agents, and shows a terminal selector. The default choice configures
71
- all detected agents, while the individual mode lets you pick specific agents.
76
+ detects local agents, and connects **all detected agents** by default. Use
77
+ `agentnet setup --choose` to pick agents individually or skip configuration.
72
78
 
73
79
  ## Updating
74
80
 
@@ -102,7 +108,7 @@ Adjust the check interval with `AGENTNET_UPDATE_CHECK_INTERVAL_HOURS` (default `
102
108
 
103
109
  | Command | Description |
104
110
  |---------|-------------|
105
- | `agentnet setup` | Browser login plus guided private agent configuration |
111
+ | `agentnet setup [--choose]` | Browser login plus connect all detected agents (or pick with `--choose`) |
106
112
  | `agentnet detect` | Scan for installed AI agents |
107
113
  | `agentnet register` | Sign in through the browser and register a CLI identity |
108
114
  | `agentnet connect [agent\|--all]` | Wire an agent into Agent-net via MCP |
@@ -196,11 +202,34 @@ For MCP agents, the CLI writes config files that tell your agent about the MCP s
196
202
  backups/ # Original config backups
197
203
  ```
198
204
 
205
+ ## Environments
206
+
207
+ PyPI installs default to production (`https://app.agentnet.market`) with no configuration.
208
+
209
+ | Environment | How to target | URL |
210
+ |-------------|---------------|-----|
211
+ | Production (default) | *(none)* | `https://app.agentnet.market` |
212
+ | Staging | `AGENTNET_ENV=staging` | `https://agent-net-server.narun.in` |
213
+ | Local dev | `agentnet --dev setup` or `AGENTNET_ENV=development` | `http://localhost:8000` |
214
+
215
+ Override the URL directly at any time:
216
+
217
+ ```bash
218
+ # Explicit URL (highest precedence)
219
+ export AGENTNET_PLATFORM_URL=http://localhost:8000
220
+ agentnet setup
221
+
222
+ # Or per command
223
+ agentnet setup --url http://localhost:8000
224
+ ```
225
+
226
+ Precedence: `--url` flag → `AGENTNET_PLATFORM_URL` → legacy `AGENTNET_URL` → `AGENTNET_ENV` → saved `~/.agentnet/config.json` → production default.
227
+
199
228
  ## Development
200
229
 
201
230
  ```bash
202
231
  uv sync # Install deps
203
- uv run pytest -v # Run tests (256 tests)
232
+ uv run pytest -v # Run tests (354 tests)
204
233
  uv run pytest --cov -q # With coverage
205
234
  uv run ruff check . # Lint
206
235
  uv run agentnet --help # Run locally
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agentnet-cli"
3
- version = "0.2.1"
3
+ version = "0.2.3"
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"
@@ -1,7 +1,6 @@
1
1
  from __future__ import annotations
2
2
 
3
3
  import getpass
4
- import os
5
4
  import socket
6
5
  import time
7
6
  import webbrowser
@@ -11,11 +10,10 @@ from rich.console import Console
11
10
  from rich.table import Table
12
11
 
13
12
  from ...infra.config import load_config, save_config
13
+ from ...infra.platform import resolve_platform_url
14
14
  from ...marketplace.client import PlatformClient
15
15
 
16
16
  console = Console()
17
-
18
- DEFAULT_PLATFORM_URL = "https://app.agentnet.market"
19
17
  DEFAULT_LOGIN_TIMEOUT_SECONDS = 10 * 60
20
18
 
21
19
 
@@ -31,7 +29,7 @@ def register_command(
31
29
  if not typer.confirm(" Re-register?"):
32
30
  return
33
31
 
34
- url = platform_url or os.environ.get("AGENTNET_URL") or DEFAULT_PLATFORM_URL
32
+ url = resolve_platform_url(explicit_url=platform_url, use_config=False)
35
33
 
36
34
  client = PlatformClient(base_url=url)
37
35
  info = _browser_login(client)
@@ -19,7 +19,7 @@ from .register import default_agent_name, register_command
19
19
  console = Console()
20
20
 
21
21
 
22
- def setup_command(platform_url: str | None = None) -> None:
22
+ def setup_command(platform_url: str | None = None, *, choose: bool = False) -> None:
23
23
  config = load_config()
24
24
  if not config or not config.get("api_token"):
25
25
  console.print()
@@ -43,21 +43,25 @@ def setup_command(platform_url: str | None = None) -> None:
43
43
  return
44
44
 
45
45
  console.print()
46
- console.print(
47
- f" [bold]Step 3:[/bold] Choose agents to configure "
48
- f"([green]all recommended[/green], {len(targets)} available)"
49
- )
50
- selected, connect_all = _select_targets(targets)
51
- if not selected:
52
- console.print("\n [dim]No agents configured.[/dim]\n")
53
- return
54
-
55
- if connect_all:
56
- connect_command(connect_all=True)
46
+ if choose:
47
+ console.print(
48
+ f" [bold]Step 3:[/bold] Choose agents to configure ({len(targets)} available)"
49
+ )
50
+ selected, connect_all = _select_targets(targets)
51
+ if not selected:
52
+ console.print("\n [dim]No agents configured.[/dim]\n")
53
+ return
54
+ if connect_all:
55
+ connect_command(connect_all=True)
56
+ return
57
+ for agent_name in selected:
58
+ connect_command(agent_name=agent_name)
57
59
  return
58
60
 
59
- for agent_name in selected:
60
- connect_command(agent_name=agent_name)
61
+ console.print(
62
+ f" [bold]Step 3:[/bold] Configuring all detected agents ({len(targets)})"
63
+ )
64
+ connect_command(connect_all=True)
61
65
 
62
66
 
63
67
  def _available_targets(results) -> list[str]:
@@ -9,6 +9,7 @@ from rich.console import Console
9
9
  from rich.table import Table
10
10
 
11
11
  from agentnet_cli import __version__
12
+ from agentnet_cli.infra.platform import LOCAL_DEV_PLATFORM_URL, PRODUCTION_PLATFORM_URL
12
13
 
13
14
  app = typer.Typer(
14
15
  name="agentnet",
@@ -29,8 +30,16 @@ def main(
29
30
  version: bool = typer.Option(
30
31
  None, "--version", "-V", callback=_version_callback, is_eager=True, help="Show version",
31
32
  ),
33
+ dev: bool = typer.Option(
34
+ False,
35
+ "--dev",
36
+ help=f"Use local platform ({LOCAL_DEV_PLATFORM_URL}) for this session",
37
+ ),
32
38
  ) -> None:
33
39
  """Discover AI coding agents on your system and connect them to the Agent-net marketplace."""
40
+ if dev:
41
+ os.environ.setdefault("AGENTNET_ENV", "development")
42
+
34
43
  try:
35
44
  from .core.updater import maybe_auto_update # noqa: PLC0415
36
45
 
@@ -113,7 +122,7 @@ def detect() -> None:
113
122
  @app.command()
114
123
  def register(
115
124
  url: Optional[str] = typer.Option(
116
- None, "--url", help="Platform URL (default: https://app.agentnet.market)",
125
+ None, "--url", help=f"Platform URL (default: {PRODUCTION_PLATFORM_URL})",
117
126
  ),
118
127
  ) -> None:
119
128
  """Sign in through the browser and register a CLI identity."""
@@ -125,13 +134,18 @@ def register(
125
134
  @app.command()
126
135
  def setup(
127
136
  url: Optional[str] = typer.Option(
128
- None, "--url", help="Platform URL (default: https://app.agentnet.market)",
137
+ None, "--url", help=f"Platform URL (default: {PRODUCTION_PLATFORM_URL})",
138
+ ),
139
+ choose: bool = typer.Option(
140
+ False,
141
+ "--choose",
142
+ help="Interactively choose which detected agents to configure",
129
143
  ),
130
144
  ) -> None:
131
- """Sign in and choose which detected agents to configure."""
145
+ """Sign in and configure all detected agents (use --choose to pick individually)."""
132
146
  from .core.setup_wizard import setup_command
133
147
 
134
- setup_command(platform_url=url)
148
+ setup_command(platform_url=url, choose=choose)
135
149
 
136
150
 
137
151
  @app.command()
@@ -1,12 +1,12 @@
1
1
  from __future__ import annotations
2
2
 
3
- import json
4
3
  import os
5
4
  from typing import Any
6
5
 
7
6
  import typer
8
7
 
9
8
  from ...infra.config import load_config
9
+ from ...infra.platform import resolve_platform_url
10
10
  from ...marketplace.auth import die, output
11
11
  from ...marketplace.client import PlatformClient, PlatformError
12
12
  from ...marketplace.catalogs.claude_marketplace import ClaudeMarketplaceClient, ClaudeMarketplaceError
@@ -25,17 +25,15 @@ def _platform_client() -> PlatformClient | None:
25
25
  if not token:
26
26
  return None
27
27
 
28
- platform_url = os.environ.get("AGENTNET_PLATFORM_URL", "") or config.get(
29
- "platform_url", "https://app.agentnet.market",
30
- )
28
+ platform_url = resolve_platform_url(config=config)
31
29
  return PlatformClient(base_url=platform_url, api_token=token)
32
30
 
33
31
 
34
32
  def _skill_search(query: str, limit: int) -> dict[str, Any]:
35
33
  config = load_config() or {}
36
34
  with SkillDiscovery(
37
- platform_url=config.get("platform_url"),
38
- api_token=config.get("api_token"),
35
+ platform_url=resolve_platform_url(config=config),
36
+ api_token=config.get("api_token") or os.environ.get("AGENTNET_TOKEN", ""),
39
37
  openai_api_key=os.environ.get("OPENAI_API_KEY") or None,
40
38
  anthropic_api_key=os.environ.get("ANTHROPIC_API_KEY") or None,
41
39
  ) as disco:
@@ -118,7 +116,7 @@ def search(
118
116
  "skills": _skill_search(query, limit),
119
117
  "plugins": _plugin_search(query, limit, category),
120
118
  }
121
- print(json.dumps(result, indent=2))
119
+ output(result)
122
120
  return
123
121
 
124
122
  die("Not authenticated. Run 'agentnet setup' or set AGENTNET_TOKEN.")
@@ -3,7 +3,6 @@ from __future__ import annotations
3
3
  import json
4
4
  import shutil
5
5
  import subprocess
6
- from pathlib import Path
7
6
  from typing import Any
8
7
 
9
8
  from ..infra.package_paths import bundled_claude_marketplace
@@ -3,7 +3,6 @@ from __future__ import annotations
3
3
  import json
4
4
  import shutil
5
5
  import subprocess
6
- from pathlib import Path
7
6
  from typing import Any
8
7
 
9
8
  from ..infra.package_paths import bundled_openclaw_plugin
@@ -1,6 +1,6 @@
1
1
  ---
2
2
  name: agentnet
3
- version: 0.2.1
3
+ version: 0.2.3
4
4
  description: >-
5
5
  Discover agents, listings, skills, and plugins for any task. Run `agentnet search`
6
6
  when the user needs help with anything — weather, translation, code review, design,
@@ -0,0 +1,74 @@
1
+ from __future__ import annotations
2
+
3
+ import os
4
+ from typing import Any
5
+
6
+ PRODUCTION_PLATFORM_URL = "https://app.agentnet.market"
7
+ STAGING_PLATFORM_URL = "https://agent-net-server.narun.in"
8
+ LOCAL_DEV_PLATFORM_URL = "http://localhost:8000"
9
+
10
+ _ENV_PLATFORM_URLS: dict[str, str] = {
11
+ "production": PRODUCTION_PLATFORM_URL,
12
+ "prod": PRODUCTION_PLATFORM_URL,
13
+ "staging": STAGING_PLATFORM_URL,
14
+ "stage": STAGING_PLATFORM_URL,
15
+ "development": LOCAL_DEV_PLATFORM_URL,
16
+ "dev": LOCAL_DEV_PLATFORM_URL,
17
+ "local": LOCAL_DEV_PLATFORM_URL,
18
+ }
19
+
20
+ # Backward-compatible alias used by register and docs.
21
+ DEFAULT_PLATFORM_URL = PRODUCTION_PLATFORM_URL
22
+
23
+
24
+ def _normalize_url(url: str) -> str:
25
+ return url.strip().rstrip("/")
26
+
27
+
28
+ def _env_platform_url() -> str | None:
29
+ for key in ("AGENTNET_PLATFORM_URL", "AGENTNET_URL"):
30
+ raw = os.environ.get(key, "").strip()
31
+ if raw:
32
+ return _normalize_url(raw)
33
+ return None
34
+
35
+
36
+ def _env_named_platform_url() -> str | None:
37
+ env_name = os.environ.get("AGENTNET_ENV", "").strip().lower()
38
+ if not env_name:
39
+ return None
40
+ return _ENV_PLATFORM_URLS.get(env_name)
41
+
42
+
43
+ def resolve_platform_url(
44
+ *,
45
+ explicit_url: str | None = None,
46
+ config: dict[str, Any] | None = None,
47
+ use_config: bool = True,
48
+ ) -> str:
49
+ """Resolve the Agent-net platform base URL.
50
+
51
+ Precedence (highest first):
52
+ 1. ``explicit_url`` — CLI ``--url`` flag
53
+ 2. ``AGENTNET_PLATFORM_URL`` or legacy ``AGENTNET_URL`` env vars
54
+ 3. ``AGENTNET_ENV`` mapping (``development``/``staging``/``production``)
55
+ 4. ``platform_url`` from ``~/.agentnet/config.json`` (when ``use_config=True``)
56
+ 5. Production default (``https://app.agentnet.market``)
57
+ """
58
+ if explicit_url and explicit_url.strip():
59
+ return _normalize_url(explicit_url)
60
+
61
+ env_url = _env_platform_url()
62
+ if env_url:
63
+ return env_url
64
+
65
+ named_url = _env_named_platform_url()
66
+ if named_url:
67
+ return named_url
68
+
69
+ if use_config and config:
70
+ saved = config.get("platform_url")
71
+ if isinstance(saved, str) and saved.strip():
72
+ return _normalize_url(saved)
73
+
74
+ return PRODUCTION_PLATFORM_URL
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agentnet",
3
- "version": "0.2.1",
3
+ "version": "0.2.3",
4
4
  "description": "Discover, hire, and pay AI agents on the Agent-net marketplace",
5
5
  "author": {
6
6
  "name": "Agent-net",
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "id": "agentnet",
3
3
  "name": "AgentNet Marketplace",
4
- "version": "0.2.1",
4
+ "version": "0.2.3",
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.1",
3
+ "version": "0.2.3",
4
4
  "description": "AgentNet marketplace plugin for OpenClaw",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -5,6 +5,7 @@ import os
5
5
  from typing import Any, NoReturn
6
6
 
7
7
  from ..infra.config import load_config
8
+ from ..infra.platform import resolve_platform_url
8
9
  from .client import PlatformClient
9
10
 
10
11
 
@@ -15,11 +16,7 @@ def get_client() -> PlatformClient:
15
16
  token = config.get("api_token", "")
16
17
  if not token:
17
18
  die("Not authenticated. Run 'agentnet setup' or set AGENTNET_TOKEN.")
18
- platform_url = os.environ.get("AGENTNET_PLATFORM_URL", "")
19
- if not platform_url and config:
20
- platform_url = config.get("platform_url", "https://app.agentnet.market")
21
- if not platform_url:
22
- platform_url = "https://app.agentnet.market"
19
+ platform_url = resolve_platform_url(config=config)
23
20
  return PlatformClient(base_url=platform_url, api_token=token)
24
21
 
25
22
 
@@ -56,6 +56,8 @@ class ClawHubClient:
56
56
  family: str | None = None,
57
57
  ) -> dict[str, Any]:
58
58
  params: dict[str, Any] = {"q": query, "limit": limit}
59
+ if sort:
60
+ params["sort"] = sort
59
61
  if category:
60
62
  params["category"] = category
61
63
  if family:
@@ -389,7 +389,9 @@ class SkillDiscovery:
389
389
  # Prefer richer description
390
390
  if len(r.get("description", "")) < len(existing.get("description", "")):
391
391
  r["description"] = existing["description"]
392
- seen[key] = r
392
+ for alias, value in list(seen.items()):
393
+ if value is existing:
394
+ seen[alias] = r
393
395
  else:
394
396
  existing["_sources"] = existing.get("_sources", {existing.get("source", "")})
395
397
  existing["_sources"].add(r.get("source", ""))
@@ -51,6 +51,16 @@ class ToolHandlers:
51
51
  )
52
52
  self._agent_id = agent_id
53
53
 
54
+ def close(self) -> None:
55
+ self._client.close()
56
+ self._discovery.close()
57
+
58
+ def __enter__(self) -> "ToolHandlers":
59
+ return self
60
+
61
+ def __exit__(self, *args: Any) -> None:
62
+ self.close()
63
+
54
64
  def discover(
55
65
  self,
56
66
  *,
@@ -5,6 +5,7 @@ import os
5
5
  from typing import Any
6
6
 
7
7
  from agentnet_cli.infra.config import load_config
8
+ from agentnet_cli.infra.platform import resolve_platform_url
8
9
  from agentnet_cli.tools.handlers import ToolHandlers
9
10
 
10
11
  _NO_TOKEN_ERROR = json.dumps({"error": "Not registered. Run 'agentnet setup' first."})
@@ -15,7 +16,7 @@ def _get_handlers() -> ToolHandlers | None:
15
16
  config = load_config()
16
17
  if not token and config:
17
18
  token = config.get("api_token", "")
18
- platform_url = (config or {}).get("platform_url", "https://app.agentnet.market")
19
+ platform_url = resolve_platform_url(config=config)
19
20
  agent_id = (config or {}).get("agent_id", "")
20
21
  if not token:
21
22
  return None
@@ -1,5 +1,5 @@
1
1
  name: agentnet
2
- version: "0.2.1"
2
+ version: "0.2.3"
3
3
  description: >-
4
4
  Agent-net marketplace discovery. Use whenever the user needs an external
5
5
  product, service, agent, skill, or plugin — always call agentnet_search first.
@@ -7,6 +7,7 @@ from typing import Any
7
7
 
8
8
  from .. import __version__
9
9
  from ..infra.config import load_config
10
+ from ..infra.platform import resolve_platform_url
10
11
  from .handlers import ToolHandlers
11
12
 
12
13
  _CORE_TOOL_NAMES = frozenset({
@@ -211,11 +212,8 @@ def serve() -> None:
211
212
  if not token and config:
212
213
  token = config.get("api_token", "")
213
214
 
214
- platform_url = ""
215
- agent_id = ""
216
- if config:
217
- platform_url = config.get("platform_url", "https://app.agentnet.market")
218
- agent_id = config.get("agent_id", "")
215
+ platform_url = resolve_platform_url(config=config)
216
+ agent_id = config.get("agent_id", "") if config else ""
219
217
 
220
218
  if not token:
221
219
  sys.stderr.write("AGENTNET_TOKEN not set and no config found\n")
@@ -318,3 +316,5 @@ def serve() -> None:
318
316
  _write_response(_error_response(err_id, -32603, "Internal error"))
319
317
  except Exception:
320
318
  pass
319
+
320
+ handlers.close()
@@ -0,0 +1,14 @@
1
+ import pytest
2
+
3
+
4
+ @pytest.fixture(autouse=True)
5
+ def _isolate_platform_env(monkeypatch):
6
+ """Prevent host/shell AGENTNET_* vars from affecting test expectations."""
7
+ for key in ("AGENTNET_ENV", "AGENTNET_PLATFORM_URL", "AGENTNET_URL"):
8
+ monkeypatch.delenv(key, raising=False)
9
+
10
+
11
+ @pytest.fixture()
12
+ def fake_home(tmp_path, monkeypatch):
13
+ monkeypatch.setattr("agentnet_cli.infra.paths.Path.home", lambda: tmp_path)
14
+ return tmp_path
@@ -1,5 +1,6 @@
1
- from unittest.mock import patch
1
+ import os
2
2
  from contextlib import nullcontext
3
+ from unittest.mock import patch
3
4
 
4
5
  from typer.testing import CliRunner
5
6
  from agentnet_cli.connectors.base import DetectionResult
@@ -179,6 +180,12 @@ def test_connect_not_registered(fake_home):
179
180
  assert result.exit_code != 0 or "not registered" in result.stdout.lower()
180
181
 
181
182
 
183
+ def test_dev_flag_sets_development_env(fake_home):
184
+ with patch("agentnet_cli.cli.core.updater.maybe_auto_update"):
185
+ runner.invoke(app, ["--dev", "detect"])
186
+ assert os.environ.get("AGENTNET_ENV") == "development"
187
+
188
+
182
189
  def test_setup_registers_when_missing_config(fake_home):
183
190
  with patch("agentnet_cli.cli.core.setup_wizard.register_command") as register, \
184
191
  patch("agentnet_cli.cli.core.setup_wizard.detect_all", return_value=[]), \
@@ -223,7 +230,7 @@ def test_setup_can_select_individual_detected_agent(fake_home):
223
230
 
224
231
  with patch("agentnet_cli.cli.core.setup_wizard.detect_all", return_value=detections), \
225
232
  patch("agentnet_cli.cli.core.setup_wizard.connect_command") as connect:
226
- result = runner.invoke(app, ["setup"], input="2\n1\n")
233
+ result = runner.invoke(app, ["setup", "--choose"], input="2\n1\n")
227
234
 
228
235
  assert result.exit_code == 0
229
236
  connect.assert_called_once_with(agent_name="claude")
@@ -240,7 +247,7 @@ def test_setup_individual_mode_defaults_to_no_agents(fake_home):
240
247
 
241
248
  with patch("agentnet_cli.cli.core.setup_wizard.detect_all", return_value=detections), \
242
249
  patch("agentnet_cli.cli.core.setup_wizard.connect_command") as connect:
243
- result = runner.invoke(app, ["setup"], input="2\n\n")
250
+ result = runner.invoke(app, ["setup", "--choose"], input="2\n\n")
244
251
 
245
252
  assert result.exit_code == 0
246
253
  assert "No agents configured" in result.stdout
@@ -255,7 +262,7 @@ def test_setup_can_skip_agent_configuration(fake_home):
255
262
 
256
263
  with patch("agentnet_cli.cli.core.setup_wizard.detect_all", return_value=detections), \
257
264
  patch("agentnet_cli.cli.core.setup_wizard.connect_command") as connect:
258
- result = runner.invoke(app, ["setup"], input="3\n")
265
+ result = runner.invoke(app, ["setup", "--choose"], input="3\n")
259
266
 
260
267
  assert result.exit_code == 0
261
268
  assert "No agents configured" in result.stdout
@@ -112,6 +112,22 @@ class TestDeduplicate:
112
112
  unique = disco._deduplicate(results)
113
113
  assert unique[0]["installs"] == 1000
114
114
 
115
+ def test_replacement_updates_registered_aliases(self):
116
+ disco = SkillDiscovery(
117
+ skills_client=MagicMock(),
118
+ skillsmp_client=MagicMock(),
119
+ clawhub_client=MagicMock(),
120
+ claude_marketplace=MagicMock(),
121
+ )
122
+ results = [
123
+ {"name": "code reviews", "source": "clawhub", "installs": 100},
124
+ {"name": "code review", "source": "skills.sh", "installs": 500},
125
+ ]
126
+ unique = disco._deduplicate(results)
127
+ assert len(unique) == 1
128
+ assert unique[0]["name"] == "code review"
129
+ assert unique[0]["source_count"] == 2
130
+
115
131
 
116
132
  class TestRankDeterministic:
117
133
  def test_composite_score_blends_signals(self):
@@ -29,6 +29,33 @@ def _make_handlers(handler_fn, agent_id="agent_123"):
29
29
  )
30
30
 
31
31
 
32
+ def test_handlers_context_manager_closes_owned_clients():
33
+ transport = httpx.MockTransport(lambda req: httpx.Response(200, json={}))
34
+ platform_http = httpx.Client(transport=transport)
35
+ skills_http = httpx.Client(transport=transport)
36
+ skillsmp_http = httpx.Client(transport=transport)
37
+ claude_http = httpx.Client(transport=transport)
38
+ clawhub_http = httpx.Client(transport=transport)
39
+
40
+ with ToolHandlers(
41
+ platform_url="https://test.agentnet.market",
42
+ api_token="agn_test",
43
+ agent_id="agent_123",
44
+ http_client=platform_http,
45
+ skills_http_client=skills_http,
46
+ skillsmp_http_client=skillsmp_http,
47
+ claude_marketplace_http_client=claude_http,
48
+ clawhub_http_client=clawhub_http,
49
+ ) as handlers:
50
+ assert isinstance(handlers, ToolHandlers)
51
+
52
+ assert platform_http.is_closed
53
+ assert skills_http.is_closed
54
+ assert skillsmp_http.is_closed
55
+ assert claude_http.is_closed
56
+ assert clawhub_http.is_closed
57
+
58
+
32
59
  def test_discover(handlers):
33
60
  result = handlers.discover(query="translation")
34
61
  assert isinstance(result, dict)
@@ -0,0 +1,86 @@
1
+ import pytest
2
+
3
+ from agentnet_cli.infra.platform import (
4
+ LOCAL_DEV_PLATFORM_URL,
5
+ PRODUCTION_PLATFORM_URL,
6
+ STAGING_PLATFORM_URL,
7
+ resolve_platform_url,
8
+ )
9
+
10
+
11
+ def test_production_default(monkeypatch):
12
+ monkeypatch.delenv("AGENTNET_PLATFORM_URL", raising=False)
13
+ monkeypatch.delenv("AGENTNET_URL", raising=False)
14
+ monkeypatch.delenv("AGENTNET_ENV", raising=False)
15
+ assert resolve_platform_url() == PRODUCTION_PLATFORM_URL
16
+
17
+
18
+ def test_explicit_url_wins(monkeypatch):
19
+ monkeypatch.setenv("AGENTNET_PLATFORM_URL", "https://env.example.com")
20
+ assert resolve_platform_url(explicit_url="https://flag.example.com") == "https://flag.example.com"
21
+
22
+
23
+ def test_platform_url_env_override(monkeypatch):
24
+ monkeypatch.setenv("AGENTNET_PLATFORM_URL", "https://env.example.com")
25
+ assert resolve_platform_url() == "https://env.example.com"
26
+
27
+
28
+ def test_legacy_agentnet_url_env(monkeypatch):
29
+ monkeypatch.delenv("AGENTNET_PLATFORM_URL", raising=False)
30
+ monkeypatch.setenv("AGENTNET_URL", "https://legacy.example.com")
31
+ assert resolve_platform_url() == "https://legacy.example.com"
32
+
33
+
34
+ def test_platform_url_env_beats_legacy_url(monkeypatch):
35
+ monkeypatch.setenv("AGENTNET_PLATFORM_URL", "https://primary.example.com")
36
+ monkeypatch.setenv("AGENTNET_URL", "https://legacy.example.com")
37
+ assert resolve_platform_url() == "https://primary.example.com"
38
+
39
+
40
+ @pytest.mark.parametrize(
41
+ ("env_value", "expected"),
42
+ [
43
+ ("production", PRODUCTION_PLATFORM_URL),
44
+ ("staging", STAGING_PLATFORM_URL),
45
+ ("development", LOCAL_DEV_PLATFORM_URL),
46
+ ("dev", LOCAL_DEV_PLATFORM_URL),
47
+ ],
48
+ )
49
+ def test_agentnet_env_mapping(monkeypatch, env_value, expected):
50
+ monkeypatch.delenv("AGENTNET_PLATFORM_URL", raising=False)
51
+ monkeypatch.delenv("AGENTNET_URL", raising=False)
52
+ monkeypatch.setenv("AGENTNET_ENV", env_value)
53
+ assert resolve_platform_url() == expected
54
+
55
+
56
+ def test_env_url_beats_agentnet_env(monkeypatch):
57
+ monkeypatch.setenv("AGENTNET_PLATFORM_URL", "https://custom.example.com")
58
+ monkeypatch.setenv("AGENTNET_ENV", "development")
59
+ assert resolve_platform_url() == "https://custom.example.com"
60
+
61
+
62
+ def test_config_platform_url(monkeypatch):
63
+ monkeypatch.delenv("AGENTNET_PLATFORM_URL", raising=False)
64
+ monkeypatch.delenv("AGENTNET_URL", raising=False)
65
+ monkeypatch.delenv("AGENTNET_ENV", raising=False)
66
+ assert resolve_platform_url(config={"platform_url": "https://cfg.example.com"}) == "https://cfg.example.com"
67
+
68
+
69
+ def test_env_overrides_config(monkeypatch):
70
+ monkeypatch.setenv("AGENTNET_PLATFORM_URL", "https://env.example.com")
71
+ assert resolve_platform_url(config={"platform_url": "https://cfg.example.com"}) == "https://env.example.com"
72
+
73
+
74
+ def test_register_skips_saved_config(monkeypatch):
75
+ monkeypatch.delenv("AGENTNET_PLATFORM_URL", raising=False)
76
+ monkeypatch.delenv("AGENTNET_URL", raising=False)
77
+ monkeypatch.delenv("AGENTNET_ENV", raising=False)
78
+ assert (
79
+ resolve_platform_url(config={"platform_url": "https://cfg.example.com"}, use_config=False)
80
+ == PRODUCTION_PLATFORM_URL
81
+ )
82
+
83
+
84
+ def test_trailing_slash_stripped(monkeypatch):
85
+ monkeypatch.setenv("AGENTNET_PLATFORM_URL", "https://example.com/")
86
+ assert resolve_platform_url() == "https://example.com"
@@ -388,6 +388,26 @@ class TestEOFHandling:
388
388
  responses = _run_server([])
389
389
  assert responses == []
390
390
 
391
+ def test_empty_stdin_closes_handlers(self):
392
+ stdin = io.StringIO("")
393
+ stdout = io.StringIO()
394
+ config = {
395
+ "api_token": "test_token",
396
+ "platform_url": "https://test.agentnet.market",
397
+ "agent_id": "agent_test_1",
398
+ }
399
+
400
+ with (
401
+ patch("agentnet_cli.tools.mcp_server.sys.stdin", stdin),
402
+ patch("agentnet_cli.tools.mcp_server.sys.stdout", stdout),
403
+ patch("agentnet_cli.tools.mcp_server.load_config", return_value=config),
404
+ patch("agentnet_cli.tools.mcp_server.os.environ", {}),
405
+ patch("agentnet_cli.tools.mcp_server.ToolHandlers") as MockHandlers,
406
+ ):
407
+ serve()
408
+
409
+ MockHandlers.return_value.close.assert_called_once()
410
+
391
411
 
392
412
  class TestNoTokenConfigured:
393
413
  """16. No token anywhere causes ``sys.exit(1)``."""
@@ -4,7 +4,7 @@ requires-python = ">=3.11"
4
4
 
5
5
  [[package]]
6
6
  name = "agentnet-cli"
7
- version = "0.2.1"
7
+ version = "0.2.3"
8
8
  source = { editable = "." }
9
9
  dependencies = [
10
10
  { name = "click" },
@@ -1,7 +0,0 @@
1
- import pytest
2
-
3
-
4
- @pytest.fixture()
5
- def fake_home(tmp_path, monkeypatch):
6
- monkeypatch.setattr("agentnet_cli.infra.paths.Path.home", lambda: tmp_path)
7
- return tmp_path