agentnet-cli 0.2.2__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.2 → agentnet_cli-0.2.3}/.github/workflows/ci.yml +0 -0
  2. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/.github/workflows/publish.yml +0 -0
  3. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/.gitignore +0 -0
  4. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/ARCHITECTURE.md +0 -0
  5. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/CLAUDE.md +1 -1
  6. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/PKG-INFO +25 -2
  7. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/README.md +24 -1
  8. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/docs/archive/superpowers/plans/2026-05-09-hermes-native-plugin.md +0 -0
  9. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/docs/archive/superpowers/plans/2026-05-09-marketplace-cli-and-skill.md +0 -0
  10. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/docs/archive/superpowers/plans/2026-05-10-claude-native-plugin.md +0 -0
  11. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/docs/archive/superpowers/plans/2026-05-10-openclaw-native-plugin.md +0 -0
  12. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/docs/archive/superpowers/specs/2026-05-09-hermes-native-plugin-design.md +0 -0
  13. {agentnet_cli-0.2.2 → 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.2 → agentnet_cli-0.2.3}/docs/archive/superpowers/specs/2026-05-10-claude-native-plugin-design.md +0 -0
  15. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/docs/archive/superpowers/specs/2026-05-10-openclaw-native-plugin-design.md +0 -0
  16. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/pyproject.toml +1 -1
  17. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/__init__.py +0 -0
  18. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/cli/__init__.py +0 -0
  19. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/cli/core/__init__.py +0 -0
  20. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/cli/core/connect.py +0 -0
  21. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/cli/core/detect.py +0 -0
  22. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/cli/core/disconnect.py +0 -0
  23. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/cli/core/register.py +2 -4
  24. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/cli/core/setup_wizard.py +0 -0
  25. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/cli/core/status.py +0 -0
  26. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/cli/core/updater.py +0 -0
  27. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/cli/main.py +11 -2
  28. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/cli/marketplace/__init__.py +0 -0
  29. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/cli/marketplace/agent.py +0 -0
  30. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/cli/marketplace/discover.py +0 -0
  31. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/cli/marketplace/search.py +5 -7
  32. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/README.md +0 -0
  33. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/__init__.py +0 -0
  34. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/base.py +0 -0
  35. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/claude.py +0 -1
  36. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/codex.py +0 -0
  37. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/copilot.py +0 -0
  38. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/cursor.py +0 -0
  39. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/hermes.py +0 -0
  40. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/openclaw.py +0 -1
  41. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/registry.py +0 -0
  42. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/shims.py +0 -0
  43. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/templates/README.md +0 -0
  44. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/templates/SKILL.hosted.md +1 -1
  45. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/templates/codex/skill.md +0 -0
  46. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/templates/copilot/agentnet.agent.md +0 -0
  47. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/templates/cursor/agent.md +0 -0
  48. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/templates/cursor/agentnet.mdc +0 -0
  49. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/templates/shared/context.md +0 -0
  50. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/templates/shared/default-chat.md +0 -0
  51. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/templates/vscode/instructions.md +0 -0
  52. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/connectors/vscode.py +0 -0
  53. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/infra/__init__.py +0 -0
  54. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/infra/config.py +0 -0
  55. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/infra/manifest.py +0 -0
  56. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/infra/package_paths.py +0 -0
  57. {agentnet_cli-0.2.2 → 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.2 → agentnet_cli-0.2.3}/src/agentnet_cli/integrations/claude/.claude-plugin/marketplace.json +0 -0
  60. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/integrations/claude/plugin/.claude-plugin/plugin.json +1 -1
  61. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/integrations/claude/plugin/.mcp.json +0 -0
  62. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/integrations/claude/plugin/agents/marketplace.md +0 -0
  63. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/integrations/claude/plugin/hooks/hooks.json +0 -0
  64. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/integrations/claude/plugin/skills/agentnet/SKILL.md +0 -0
  65. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/integrations/openclaw/.mcp.json +0 -0
  66. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/integrations/openclaw/index.js +0 -0
  67. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/integrations/openclaw/openclaw.plugin.json +1 -1
  68. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/integrations/openclaw/package.json +1 -1
  69. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/integrations/openclaw/skills/agentnet/SKILL.md +0 -0
  70. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/integrations/shared/discovery-skill.base.md +0 -0
  71. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/marketplace/__init__.py +0 -0
  72. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/marketplace/auth.py +2 -5
  73. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/marketplace/catalogs/__init__.py +0 -0
  74. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/marketplace/catalogs/claude_marketplace.py +0 -0
  75. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/marketplace/catalogs/clawhub.py +2 -0
  76. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/marketplace/client.py +0 -0
  77. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/marketplace/skills/__init__.py +0 -0
  78. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/marketplace/skills/client.py +0 -0
  79. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/marketplace/skills/discovery.py +3 -1
  80. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/marketplace/skills/skillsmp.py +0 -0
  81. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/tools/__init__.py +0 -0
  82. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/tools/handlers.py +10 -0
  83. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/tools/hermes/__init__.py +0 -0
  84. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/tools/hermes/handlers.py +2 -1
  85. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/tools/hermes/plugin.yaml +1 -1
  86. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/tools/hermes/schemas.py +0 -0
  87. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/tools/hermes/skills/agentnet/SKILL.md +0 -0
  88. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/src/agentnet_cli/tools/mcp_server.py +5 -5
  89. {agentnet_cli-0.2.2 → 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.2 → agentnet_cli-0.2.3}/tests/test_agent_cmd.py +0 -0
  92. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/tests/test_claude.py +0 -0
  93. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/tests/test_claude_marketplace.py +0 -0
  94. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/tests/test_clawhub_client.py +0 -0
  95. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/tests/test_cli.py +8 -1
  96. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/tests/test_codex.py +0 -0
  97. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/tests/test_config.py +0 -0
  98. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/tests/test_copilot.py +0 -0
  99. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/tests/test_cursor.py +0 -0
  100. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/tests/test_detect.py +0 -0
  101. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/tests/test_discover_cmd.py +0 -0
  102. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/tests/test_discovery.py +16 -0
  103. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/tests/test_discovery_skill.py +0 -0
  104. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/tests/test_e2e.py +0 -0
  105. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/tests/test_hermes.py +0 -0
  106. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/tests/test_hermes_plugin.py +0 -0
  107. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/tests/test_manifest.py +0 -0
  108. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/tests/test_marketplace.py +0 -0
  109. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/tests/test_mcp_tools.py +27 -0
  110. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/tests/test_openclaw.py +0 -0
  111. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/tests/test_paths.py +0 -0
  112. {agentnet_cli-0.2.2 → 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.2 → agentnet_cli-0.2.3}/tests/test_register.py +0 -0
  115. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/tests/test_search_cmd.py +0 -0
  116. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/tests/test_server.py +20 -0
  117. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/tests/test_skills_client.py +0 -0
  118. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/tests/test_skillsmp_client.py +0 -0
  119. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/tests/test_updater.py +0 -0
  120. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/tests/test_vscode.py +0 -0
  121. {agentnet_cli-0.2.2 → agentnet_cli-0.2.3}/uv.lock +1 -1
  122. agentnet_cli-0.2.2/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.2
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
@@ -229,11 +229,34 @@ For MCP agents, the CLI writes config files that tell your agent about the MCP s
229
229
  backups/ # Original config backups
230
230
  ```
231
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
+
232
255
  ## Development
233
256
 
234
257
  ```bash
235
258
  uv sync # Install deps
236
- uv run pytest -v # Run tests (256 tests)
259
+ uv run pytest -v # Run tests (354 tests)
237
260
  uv run pytest --cov -q # With coverage
238
261
  uv run ruff check . # Lint
239
262
  uv run agentnet --help # Run locally
@@ -202,11 +202,34 @@ For MCP agents, the CLI writes config files that tell your agent about the MCP s
202
202
  backups/ # Original config backups
203
203
  ```
204
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
+
205
228
  ## Development
206
229
 
207
230
  ```bash
208
231
  uv sync # Install deps
209
- uv run pytest -v # Run tests (256 tests)
232
+ uv run pytest -v # Run tests (354 tests)
210
233
  uv run pytest --cov -q # With coverage
211
234
  uv run ruff check . # Lint
212
235
  uv run agentnet --help # Run locally
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "agentnet-cli"
3
- version = "0.2.2"
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)
@@ -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,7 +134,7 @@ 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})",
129
138
  ),
130
139
  choose: bool = typer.Option(
131
140
  False,
@@ -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.2
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.2",
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.2",
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.2",
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.2"
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=[]), \
@@ -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.2"
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