agent-discogs 0.1.0__py3-none-any.whl

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.
@@ -0,0 +1,151 @@
1
+ """agent-discogs: Token-efficient Discogs CLI for AI agents."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import sys
6
+ from typing import Any
7
+
8
+ import click
9
+
10
+ from agent_discogs.commands.cache import cache
11
+ from agent_discogs.commands.get import get, price, tracks
12
+ from agent_discogs.commands.search import search
13
+ from agent_discogs.commands.skills import skills
14
+ from agent_discogs.commands.status import status
15
+
16
+ ALIASES: dict[str, str] = {
17
+ "find": "search",
18
+ "query": "search",
19
+ "fetch": "get",
20
+ "show": "get",
21
+ }
22
+
23
+
24
+ _HELP_TEXT = """\
25
+ agent-discogs - token-efficient Discogs CLI for AI agents
26
+
27
+ Usage: agent-discogs <command> [args] [options]
28
+
29
+ Commands:
30
+ search [type] <query> Search database (aliases: find, query)
31
+ get <noun> <ref>... Get entity details, 1-10 refs (aliases: fetch, show)
32
+ tracks <ref>... Shortcut: get tracklist <ref>...
33
+ price <ref>... Shortcut: get price <ref>...
34
+ cache clear Clear HTTP cache
35
+ skills [get <name>] Print the bundled agent guide (start: skills get core)
36
+ status Show session and auth info
37
+
38
+ Search Types: artist, label, master, release
39
+ Get Nouns: artist, credits, identifiers (ids), label, master, price, release,
40
+ releases, tracklist, versions
41
+
42
+ Refs:
43
+ Search results return typed refs: @a3857 (artist), @r847868 (release),
44
+ @m3719 (master), @l647 (label). Use refs with get commands.
45
+
46
+ Options:
47
+ --json JSON output: a compact projection of the text view (search, get)
48
+ --full With --json: the raw SDK record instead of the projection
49
+ --limit N Results per page (search, get)
50
+ --page N Page number for server-side pages (search, get)
51
+ --after CURSOR Continue a client-side filtered list; copy from Next page /
52
+ Continue scan (search with --release-type official|unofficial,
53
+ get releases --role, get releases @l --year)
54
+ -v, --verbose get release: also print notes, credits, identifiers
55
+ -c, --compact get release: one-line "Tracks: N (mm:ss)" instead of the tracklist
56
+ (total omitted if any track lacks a duration)
57
+ --sort KEY Server-side order. get releases @a: year|title|format;
58
+ get versions: released|title|format|label|catno|country
59
+ --desc With --sort: descending
60
+ --year YEAR get versions: filter by year (server-side); get releases @l:
61
+ filter by year (client-side scan, continues via --after)
62
+ --version Show version and exit
63
+ --help Show this message and exit
64
+
65
+ Environment:
66
+ DISCOGS_TOKEN Personal access token (higher rate limit, required for price data)
67
+
68
+ Price data also needs seller settings filled out on the Discogs account the
69
+ token belongs to (discogs.com/settings/seller). Without them the API answers
70
+ 404 and only 'price' is affected.
71
+
72
+ Examples:
73
+ agent-discogs search "The Downward Spiral"
74
+ agent-discogs search artist "Nine Inch Nails"
75
+ agent-discogs get release @r847868
76
+ agent-discogs get versions @m3719 --country US --format Vinyl
77
+ agent-discogs tracks @r847868
78
+ agent-discogs price @r847868
79
+ agent-discogs get releases @a3857 --role Main --limit 5
80
+ """
81
+
82
+
83
+ class AliasGroup(click.Group):
84
+ """Click group that supports command aliases."""
85
+
86
+ def get_command(self, ctx: click.Context, cmd_name: str) -> click.Command | None:
87
+ cmd = super().get_command(ctx, cmd_name)
88
+ if cmd is not None:
89
+ return cmd
90
+ canonical = ALIASES.get(cmd_name)
91
+ if canonical is not None:
92
+ return super().get_command(ctx, canonical)
93
+ return None
94
+
95
+ def resolve_command(
96
+ self, ctx: click.Context, args: list[str]
97
+ ) -> tuple[str | None, click.Command | None, list[str]]:
98
+ # Ensure the canonical name is used so help text is consistent
99
+ _, cmd, remaining = super().resolve_command(ctx, args)
100
+ if cmd is not None:
101
+ return cmd.name, cmd, remaining
102
+ return None, None, args
103
+
104
+ def format_help(
105
+ self,
106
+ ctx: click.Context, # noqa: ARG002 # part of click's Command API
107
+ formatter: click.HelpFormatter,
108
+ ) -> None:
109
+ formatter.write(_HELP_TEXT)
110
+
111
+ def invoke(self, ctx: click.Context) -> Any:
112
+ try:
113
+ return super().invoke(ctx)
114
+ except KeyboardInterrupt:
115
+ sys.exit(130)
116
+ except click.exceptions.Exit:
117
+ raise
118
+ except click.exceptions.Abort:
119
+ raise
120
+ except click.exceptions.UsageError:
121
+ raise
122
+ # Last-resort boundary: any escaped exception becomes agent-readable text
123
+ # instead of a traceback.
124
+ except Exception as e: # noqa: BLE001
125
+ from agent_discogs.errors import format_error
126
+
127
+ print(format_error(e), file=sys.stderr)
128
+ sys.exit(1)
129
+
130
+
131
+ @click.group(cls=AliasGroup, invoke_without_command=True)
132
+ @click.version_option(package_name="agent-discogs", prog_name="agent-discogs")
133
+ @click.pass_context
134
+ def cli(ctx: click.Context) -> None:
135
+ """Token-efficient Discogs CLI for AI agents."""
136
+ if ctx.invoked_subcommand is None:
137
+ click.echo(ctx.get_help())
138
+
139
+
140
+ cli.add_command(cache)
141
+ cli.add_command(get)
142
+ cli.add_command(price)
143
+ cli.add_command(search)
144
+ cli.add_command(skills)
145
+ cli.add_command(status)
146
+ cli.add_command(tracks)
147
+
148
+
149
+ def main() -> None:
150
+ """Entry point for the CLI."""
151
+ cli()
@@ -0,0 +1,5 @@
1
+ """Allow running as `python -m agent_discogs`."""
2
+
3
+ from agent_discogs import main
4
+
5
+ main()
@@ -0,0 +1,30 @@
1
+ """SDK client initialization."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import os
6
+ from pathlib import Path
7
+
8
+ from discogs_sdk import Discogs
9
+
10
+ _client: Discogs | None = None
11
+
12
+ CACHE_DIR = (
13
+ Path(os.environ.get("XDG_CACHE_HOME", Path.home() / ".cache")) / "agent-discogs"
14
+ )
15
+
16
+
17
+ def get_client() -> Discogs:
18
+ """Get or create the Discogs SDK client."""
19
+ global _client # noqa: PLW0603 # module-level singleton is the point
20
+ if _client is not None:
21
+ return _client
22
+
23
+ token = os.environ.get("DISCOGS_TOKEN")
24
+ _client = Discogs(token=token, cache=True, cache_dir=CACHE_DIR)
25
+ return _client
26
+
27
+
28
+ def has_token() -> bool:
29
+ """Check if a DISCOGS_TOKEN is configured."""
30
+ return bool(os.environ.get("DISCOGS_TOKEN"))
File without changes
@@ -0,0 +1,16 @@
1
+ """Cache command handler."""
2
+
3
+ from __future__ import annotations
4
+
5
+ import click
6
+
7
+ from agent_discogs.client import get_client
8
+
9
+
10
+ @click.command()
11
+ @click.argument("action", type=click.Choice(["clear"]))
12
+ def cache(action: str) -> None:
13
+ """Manage cache."""
14
+ if action == "clear":
15
+ get_client().clear_cache()
16
+ print("Cache cleared.")