askaipods 0.1.1 → 0.2.1

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.
package/README.md CHANGED
@@ -7,7 +7,7 @@ $ askaipods "what are people saying about test-time compute"
7
7
 
8
8
  # askaipods · "what are people saying about test-time compute"
9
9
 
10
- *Tier: anonymous · Results: 10 · Quota: 1/10 daily*
10
+ *Tier: anonymous · Results: 20 · Quota: 1/10 daily*
11
11
 
12
12
  ## Results — newest first
13
13
 
@@ -23,7 +23,7 @@ $ askaipods "what are people saying about test-time compute"
23
23
  > Test-time compute as a paradigm pushes toward smaller base models because
24
24
  > the cost of solving a prob...
25
25
 
26
- (...8 more results, newest-first...)
26
+ (...18 more results, newest-first...)
27
27
  ```
28
28
 
29
29
  ## Why this exists
@@ -67,7 +67,7 @@ Then copy or symlink the `skill/askaipods/` directory into your agent's skills f
67
67
 
68
68
  ✨ **Two-for-one tip**: Codex CLI and OpenClaw both read from `~/.agents/skills/`, so a single install at `~/.agents/skills/askaipods/` covers both runtimes simultaneously.
69
69
 
70
- The skill folder is self-contained: it tells the host agent how to invoke `askaipods` (via `npx`), how to parse the JSON, and how to render the response with an **Insights** section. The section layout is tier-dependent — member tier renders **Latest 5** + **Top 5 Most Relevant** + **Insights**; anonymous tier renders **Recent Quotes** + **Insights** (the "Top Relevant" section is suppressed for anonymous because results are a randomized subset and the rank is not a true semantic-relevance signal).
70
+ The skill folder is self-contained: it tells the host agent how to invoke `askaipods` (via `npx`), how to parse the JSON, and how to render the response with an **Insights** section. The section layout is tier-dependent — member tier renders **Latest 5** + **Top 5 Most Relevant** + **Insights**; anonymous tier renders **Recent Quotes** + **Insights** (the "Top Relevant" section is suppressed for anonymous because the API returns results sorted by date, not by semantic relevance).
71
71
 
72
72
  ## Usage
73
73
 
@@ -80,8 +80,8 @@ askaipods "what are VCs saying about reasoning models"
80
80
  # JSON output (for scripts and agents)
81
81
  askaipods "Anthropic safety research" --format json
82
82
 
83
- # Restrict to recent episodes only (max 7 days for anonymous tier; member tier accepts any value)
84
- askaipods "GPU shortage" --days 7
83
+ # Restrict to recent episodes only (anonymous tier caps --days at 90; member tier accepts any value)
84
+ askaipods "GPU shortage" --days 90
85
85
 
86
86
  # Use a member-tier API key for 50/day instead of 10/day
87
87
  ASKAIPODS_API_KEY=pk_xxx askaipods "your query"
@@ -94,27 +94,28 @@ Once the skill is installed in your agent's skills directory, simply ask:
94
94
 
95
95
  > What are people saying about test-time compute on AI podcasts?
96
96
 
97
- Your agent will recognize the trigger phrase, invoke `askaipods`, and present the results with an AI-generated Insights summary. The exact layout is tier-dependent: **member tier** renders dual sections (Latest 5 + Top 5 Most Relevant + Insights); **anonymous tier** renders a single section (Recent Quotes + Insights), because anonymous results are a randomized subset of the top 20 and showing a "Top Relevant" view would be misleading. No CLI knowledge required from the user either way.
97
+ Your agent will recognize the trigger phrase, invoke `askaipods`, and present the results with an AI-generated Insights summary. The exact layout is tier-dependent: **member tier** renders dual sections (Latest 5 + Top 5 Most Relevant + Insights); **anonymous tier** renders a single section (Recent Quotes + Insights), because anonymous results are sorted by date (not semantic relevance) and showing a "Top Relevant" view would be misleading. No CLI knowledge required from the user either way.
98
98
 
99
99
  ## Tier comparison
100
100
 
101
101
  | | Anonymous (default) | Member |
102
102
  |---|---|---|
103
103
  | **Daily quota** | 10 searches per IP | 50 searches per user |
104
- | **Results returned** | 10 (randomized from top 20) | 20 (fixed top 20) |
105
- | **Text length** | Truncated by rank (150/100/60 chars) | Full text |
104
+ | **Results returned** | 20 (deterministic top 20, sorted newest-first) | 20 (deterministic top 20, sorted by relevance) |
105
+ | **Text length** | Full text | Full text |
106
106
  | **Date precision** | Month only (`2025-10`) | Full date (`2025-10-15`) |
107
+ | **`--days` cap (when specified)** | 90 days | Unlimited |
107
108
  | **Setup** | Nothing | `ASKAIPODS_API_KEY` env var |
108
109
  | **Sign up** | n/a | https://podlens.net |
109
110
 
110
- The anonymous tier exists so you can try the skill end-to-end with zero setup. Sign up for member access only when you outgrow the 10/day quota or need full text and exact dates.
111
+ The anonymous tier exists so you can try the skill end-to-end with zero setup. Sign up for member access only when you outgrow the 10/day quota or need full dates and unlimited lookback.
111
112
 
112
113
  ## Honest limitations
113
114
 
114
115
  - **No speaker attribution.** The corpus indexes quotes at the episode level but does not attempt to identify *which guest* said each quote. The upstream pipeline avoids speaker labeling because automatic diarization is unreliable, and a wrong attribution is worse than no attribution.
115
116
  - **No episode URLs.** The public API does not expose direct podcast or episode links. You will need to search the podcast and episode title in your podcast app of choice.
116
117
  - **AI-focused corpus.** Coverage is dense for AI research, ML engineering, AI investing, and AI policy. Off-topic queries return sparse, noisy results.
117
- - **Short quote excerpts.** Each result is typically 1-3 sentences (anonymous tier truncates further). For long-form context, listen to the episode.
118
+ - **Short quote excerpts.** Each result is typically 1-3 sentences. For long-form context, listen to the episode.
118
119
 
119
120
  These are not bugs. The skill surfaces them honestly so neither you nor your agent fabricate things the API does not provide.
120
121
 
@@ -142,7 +143,7 @@ For member tier (`render_hint: dual_view`), the host agent renders two sections
142
143
  (3-5 bullets synthesizing patterns across the quotes)
143
144
  ```
144
145
 
145
- For anonymous tier (`render_hint: single_view`), only Latest and Insights — the Top Relevant section is intentionally suppressed because anonymous results are randomized within the top 20, so api_rank does not represent true semantic relevance.
146
+ For anonymous tier (`render_hint: single_view`), only Recent Quotes and Insights — the Top Relevant section is intentionally suppressed because anonymous results are sorted by date (newest-first), so api_rank reflects temporal order, not semantic relevance.
146
147
 
147
148
  See [`skill/askaipods/SKILL.md`](skill/askaipods/SKILL.md) for the full skill specification.
148
149
 
@@ -39,12 +39,12 @@ Or trigger it organically:
39
39
 
40
40
  > What are people saying about test-time compute on AI podcasts?
41
41
 
42
- Claude Code should recognize the trigger phrase, run `npx askaipods search "..." --format json`, parse the response, and render the Latest / Top Relevant / Insights sections.
42
+ Claude Code should recognize the trigger phrase, run `npx askaipods search "..." --format json`, parse the response, and render the structured results per the SKILL.md template (layout is tier-dependent — member tier shows Latest + Top Relevant + Insights; anonymous tier shows Recent Quotes + Insights).
43
43
 
44
44
  ## Troubleshooting
45
45
 
46
46
  - **Skill not appearing**: Make sure the parent directory name matches the `name` field in `SKILL.md` (both must be `askaipods`).
47
- - **`npx askaipods` fails**: Check that Node.js 18+ is installed: `node --version`. The CLI uses zero dependencies so there are no other prereqs.
47
+ - **`npx askaipods` fails**: Check that Node.js 18.3.0+ is installed: `node --version`. The CLI uses zero dependencies so there are no other prereqs.
48
48
  - **Anonymous quota exhausted**: Sign up at https://podlens.net for 50/day, then `export ASKAIPODS_API_KEY=pk_xxx`.
49
49
  - **Skill triggers too rarely**: Front-load your prompt with the trigger phrases in `SKILL.md` description, or invoke directly with `/askaipods <query>`.
50
50
 
@@ -39,7 +39,7 @@ Codex should detect the trigger, run `npx askaipods search "..." --format json`,
39
39
 
40
40
  - **Skill not detected**: Restart Codex (per the official docs, "If an update doesn't appear, restart Codex").
41
41
  - **Multiple skills with the same name across scopes**: Codex shows both in the selector — repository-scoped wins by default.
42
- - **`npx askaipods` fails**: Check Node.js 18+: `node --version`.
42
+ - **`npx askaipods` fails**: Check Node.js 18.3.0+: `node --version`.
43
43
 
44
44
  ## Reference
45
45
 
@@ -26,7 +26,7 @@ Hermes should pick up the skill from `~/.hermes/skills/askaipods/`, shell out to
26
26
 
27
27
  ## Troubleshooting
28
28
 
29
- - **`npx askaipods` fails**: Hermes is Python-based but the askaipods CLI is Node. Make sure Node.js 18+ is on PATH alongside Python: `node --version`.
29
+ - **`npx askaipods` fails**: Hermes is Python-based but the askaipods CLI is Node. Make sure Node.js 18.3.0+ is on PATH alongside Python: `node --version`.
30
30
  - **Skill not picked up**: Hermes documentation indicates skills are loaded from `~/.hermes/skills/`. Restart the agent after install if needed.
31
31
  - **Quota exhausted**: Set `ASKAIPODS_API_KEY` in your shell environment before launching Hermes so the variable propagates to subprocess calls.
32
32
 
@@ -52,7 +52,7 @@ OpenClaw should recognize the trigger phrase, shell out to `npx askaipods search
52
52
  ## Troubleshooting
53
53
 
54
54
  - **Skill not detected**: Run `openclaw skills update --all` to refresh, or restart the OpenClaw session. Check that the directory name `askaipods` matches the `name` field in `SKILL.md`.
55
- - **`npx askaipods` fails**: Make sure Node.js 18+ is on PATH: `node --version`.
55
+ - **`npx askaipods` fails**: Make sure Node.js 18.3.0+ is on PATH: `node --version`.
56
56
  - **Conflicting copies across precedence levels**: Only the highest-precedence one wins. If you have askaipods in both `~/.agents/skills/` and `<workspace>/skills/`, the workspace one takes effect.
57
57
 
58
58
  ## Reference
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "askaipods",
3
- "version": "0.1.1",
3
+ "version": "0.2.1",
4
4
  "description": "Search AI podcast quotes by topic — recent episode excerpts from Lex Fridman, Dwarkesh Patel, No Priors, Latent Space, and dozens more. Universal agentskills.io skill compatible with Claude Code, OpenAI Codex, Hermes Agent, OpenClaw, and any other agent that supports the open skill standard. Powered by podlens.net.",
5
5
  "type": "module",
6
6
  "bin": {
@@ -2,7 +2,7 @@
2
2
  name: askaipods
3
3
  description: Search AI podcast quotes about a topic. Use whenever the user asks "what are people saying about X", "latest takes on Y", "find AI podcast quotes about Z", "who is discussing <model/concept>", or wants to know how AI researchers, founders, or VCs are publicly discussing any AI topic — even when they don't say "podcast". Returns recent excerpts from real episodes of Lex Fridman, Dwarkesh Patel, No Priors, Latent Space, and dozens more, sorted newest-first via the podlens.net semantic search API. Trigger eagerly on AI-research, ML-engineering, AI-investing, or AI-policy questions where real-human commentary beats a web search summary. Do not use for general web search, full transcript reading, or non-AI topics.
4
4
  license: MIT
5
- requirements: Node.js 18.3.0+ on PATH (the CLI uses `node:util.parseArgs`, which was added in 18.3.0), internet access to podlens.net. Optional ASKAIPODS_API_KEY env var unlocks the 50/day member tier; without it the skill works on the 10/day anonymous tier (per-IP).
5
+ requirements: Node.js 18.3.0+ on PATH (the CLI uses `node:util.parseArgs`, which was added in 18.3.0), internet access to podlens.net. Optional ASKAIPODS_API_KEY env var unlocks the 50/day member tier with full dates and unlimited lookback; without it the skill works on the 10/day anonymous tier (per-IP, month-precision dates, `--days` capped at 90 when specified).
6
6
  ---
7
7
 
8
8
  # askaipods — AI podcast quote search
@@ -42,12 +42,30 @@ npx askaipods search "<USER QUERY>" --format json
42
42
 
43
43
  The package is published on npm as `askaipods`, so `npx` will resolve it regardless of whether the user has it installed globally. If `npx` is unavailable in the host environment, the user can install globally once with `npm install -g askaipods` and the skill will run the same command.
44
44
 
45
- To restrict to recent episodes only, add `--days N` (the API caps anonymous tier at 7 days; member tier accepts any value):
45
+ To restrict to recent episodes only, add `--days N`. When `--days` is passed, the API clamps the value to a maximum of 90 for anonymous tier (member tier accepts any value). When `--days` is omitted entirely, there is no time filter — the API returns all-time results.
46
46
 
47
47
  ```bash
48
- npx askaipods search "<USER QUERY>" --days 30 --format json
48
+ npx askaipods search "<USER QUERY>" --days 90 --format json
49
49
  ```
50
50
 
51
+ ### Time-intent mapping (important)
52
+
53
+ When the user's query implies a time window, you MUST pass the appropriate `--days` value. Without it, the API returns all-time results regardless of recency words in the query.
54
+
55
+ | User intent | `--days` value |
56
+ |---|---|
57
+ | "recent", "latest", "最近", "current" | `90` |
58
+ | "this month", "这个月" | `30` |
59
+ | "this week", "这周", "last week" | `7` |
60
+ | "today", "yesterday", "last few days" | `3` |
61
+ | "last N days/weeks/months" | Convert N to days |
62
+ | "this quarter", "这个季度" | `90` |
63
+ | "this year", "今年" | `365` (member only; anonymous capped to 90) |
64
+ | Explicit date range (e.g. "since January") | Convert to days from today |
65
+ | No time intent (broad research) | Omit `--days` (all time — no cap applied) |
66
+
67
+ Do NOT silently default every query to `--days 90` — omitting `--days` on broad research queries preserves valuable historical context that the user did not ask to exclude.
68
+
51
69
  ## JSON shape returned by the CLI
52
70
 
53
71
  ```json
@@ -77,12 +95,12 @@ npx askaipods search "<USER QUERY>" --days 30 --format json
77
95
  Field notes that affect how you render:
78
96
 
79
97
  - **`tier`** — `member` if the user has a valid API key, `anonymous` otherwise. Drives the rendering branch below. On exit `0`, `tier` is always one of these two values — there is no third "unknown" path to handle (the CLI validates the upstream response and exits `3` if the value is missing or unexpected).
80
- - **`render_hint`** — `dual_view` for member, `single_view` for anonymous. Honor this. The reason: anonymous results are a randomized 10-of-20 subset, so `api_rank` only describes order *within that random subset*, not true semantic relevance against the corpus. Showing a "Top Most Relevant" section for anonymous tier would mislead the user.
98
+ - **`render_hint`** — `dual_view` for member, `single_view` for anonymous. Honor this. The reason: anonymous results are sorted by `published_at` desc (newest-first) by the API, so `api_rank` reflects temporal order, not semantic relevance. Showing a "Top Most Relevant" section for anonymous tier would mislead the user. Member results arrive in similarity order, so `api_rank` is meaningful for relevance-based views.
81
99
  - **`results[]`** — already sorted **newest first** by the CLI. Each result carries `api_rank` (1 = most semantically relevant in API order) so you can derive a "Top Relevant" sub-view without re-querying.
82
100
  - **`results[].podcast` / `episode` / `date`** — any of these may be `null` if the upstream record is incomplete. Render `Unknown podcast` / `Untitled episode` / `date unknown` rather than dropping the result. The CLI's own markdown renderer falls back the same way.
83
- - **`results[].date` format** — `YYYY-MM-DD` (or full ISO timestamp) for member tier; `YYYY-MM` only for anonymous tier (deliberately fuzzed by the API for query privacy). Display whatever you got — don't guess a day.
101
+ - **`results[].date` format** — `YYYY-MM-DD` (or full ISO timestamp) for member tier; `YYYY-MM` only for anonymous tier (deliberately fuzzed by the API). Display whatever you got — don't guess a day.
84
102
  - **`meta.quota`** — passed through from the podlens.net API. Sub-fields like `used`, `limit`, `period` are reliably present; other sub-fields (e.g., a reset timestamp) may or may not appear depending on the server version. Treat all sub-fields as optional and degrade gracefully.
85
- - **`meta.restrictions`** — `null` for member tier; for anonymous tier, an object describing the cap (e.g., `{ max_results: 10, text_truncated: true, results_randomized: true }`). If non-null, the closing anonymous-tier note (templated below) is the right way to surface it; do not parse the object field-by-field.
103
+ - **`meta.restrictions`** — `null` for member tier; for anonymous tier, an object describing the cap (e.g., `{ max_results: 20, text_truncated: false, results_randomized: false, date_precision: "month", max_days: 90, order: "published_at_desc" }`). If non-null, the closing anonymous-tier note (templated below) is the right way to surface it; do not parse the object field-by-field.
86
104
  - **No speaker name and no episode URL.** The corpus is indexed at the key-point level without per-speaker attribution (the upstream pipeline intentionally avoids attributing quotes to individuals because automatic speaker diarization is unreliable). Episode URLs are also not exposed by the public API. Render `Podcast — Episode` only; do not fabricate "Dario said" if the text doesn't already attribute itself.
87
105
 
88
106
  ## How to render the response
@@ -132,7 +150,7 @@ If the same result appears in both Latest and Top Relevant sections, that's fine
132
150
 
133
151
  2. ...
134
152
 
135
- (all returned results, in `results` array order which is already newest-first; expect up to 10)
153
+ (all returned results, in `results` array order which is already newest-first; expect up to 20)
136
154
 
137
155
  ## 💡 Insights
138
156
 
@@ -142,14 +160,14 @@ If the same result appears in both Latest and Top Relevant sections, that's fine
142
160
 
143
161
  ---
144
162
 
145
- *Anonymous tier: 10 randomized results from top 20, text truncated by rank, dates fuzzed to month. Set `ASKAIPODS_API_KEY` for 50 searches/day with full text and full dates — sign up at https://podlens.net.*
163
+ *Anonymous tier: 20 results sorted newest-first, dates fuzzed to month, `--days` capped at 90 when specified. Set `ASKAIPODS_API_KEY` for 50 searches/day with full dates and unlimited lookback — sign up at https://podlens.net.*
146
164
  ```
147
165
 
148
- The closing note about the anonymous tier matters because it tells the user (a) why the text looks chopped, (b) why the dates are coarse, and (c) what the upgrade path is. Skipping it leaves the user wondering if the skill is broken.
166
+ The closing note about the anonymous tier matters because it tells the user (a) why the dates are coarse, (b) what the lookback cap is, and (c) what the upgrade path is. Skipping it leaves the user wondering why dates lack day precision.
149
167
 
150
168
  ## Insights guidelines
151
169
 
152
- The Insights section is the most valuable part of your response — it is what differentiates this skill from a raw API call. The user could read 10 quotes themselves; what they cannot easily do is *spot the patterns across the 10*. That is your job.
170
+ The Insights section is the most valuable part of your response — it is what differentiates this skill from a raw API call. The user could read 20 quotes themselves; what they cannot easily do is *spot the patterns across the 20*. That is your job.
153
171
 
154
172
  Write 3-5 bullets, each one concrete and one sentence long. Cover at least three of these dimensions:
155
173
 
@@ -186,6 +204,6 @@ Never silently swallow an error. Never fabricate quotes when the API returns not
186
204
  - **No speaker attribution.** The API returns "podcast + episode + quote text" but not "who said it". The upstream pipeline avoids per-speaker attribution because automatic speaker diarization is unreliable — surfacing wrong attribution would be worse than no attribution.
187
205
  - **No episode URLs.** The public API does not expose direct links to episodes. Users who want to listen will need to search the podcast and episode title in their podcast app of choice.
188
206
  - **AI-focused corpus.** Coverage is dense for AI research, ML engineering, AI investing, and AI policy. Coverage for unrelated topics is sparse and noisy.
189
- - **Short quote excerpts, not transcripts.** Each result is one extracted "key point" from an episode, typically 1-3 sentences (anonymous tier truncates further). For long-form context, the user will need to listen.
207
+ - **Short quote excerpts, not transcripts.** Each result is one extracted "key point" from an episode, typically 1-3 sentences. For long-form context, the user will need to listen.
190
208
 
191
209
  These limitations are not bugs — surfacing them honestly is better than the user discovering them mid-task and losing trust.
package/src/cli.js CHANGED
@@ -14,7 +14,7 @@ import { parseArgs } from "node:util";
14
14
  import { search, AskaipodsError } from "./client.js";
15
15
  import { renderJson, renderMarkdown } from "./format.js";
16
16
 
17
- const VERSION = "0.1.1";
17
+ const VERSION = "0.2.0";
18
18
 
19
19
  const HELP_TEXT = `askaipods ${VERSION} — search AI podcast quotes by topic
20
20
 
@@ -24,7 +24,7 @@ USAGE:
24
24
 
25
25
  OPTIONS:
26
26
  --format <json|markdown> Output format (default: markdown if TTY, json if piped)
27
- --days <N> Only return results from the last N days (max 7 for anonymous tier; member tier accepts any value)
27
+ --days <N> Only return results from the last N days (anonymous tier caps at 90; member tier accepts any value)
28
28
  --api-key <key> PodLens API key (overrides ASKAIPODS_API_KEY env var)
29
29
  -h, --help Show this message
30
30
  -v, --version Show version
package/src/client.js CHANGED
@@ -136,7 +136,7 @@ export async function search({ query, days, apiKey, endpoint = PODLENS_ENDPOINT
136
136
 
137
137
  const headers = {
138
138
  "Content-Type": "application/json",
139
- "User-Agent": "askaipods/0.1.1 (+https://github.com/Delibread0601/askaipods)",
139
+ "User-Agent": "askaipods/0.2.0 (+https://github.com/Delibread0601/askaipods)",
140
140
  };
141
141
  if (apiKey) {
142
142
  headers["X-PodLens-API-Key"] = apiKey;
package/src/format.js CHANGED
@@ -12,8 +12,8 @@
12
12
  // Relevant" sub-views — see SKILL.md.
13
13
 
14
14
  const ANONYMOUS_NOTE =
15
- "Anonymous tier: 10 randomized results from top 20, text truncated by rank, " +
16
- "dates fuzzed to month. Set ASKAIPODS_API_KEY for 50 searches/day with full text and dates.";
15
+ "Anonymous tier: 20 results sorted newest-first, dates fuzzed to month, " +
16
+ "--days capped at 90 when specified. Set ASKAIPODS_API_KEY for 50 searches/day with full dates and unlimited lookback.";
17
17
 
18
18
  // Sort results newest-first by parsing each `published_at` to a UTC
19
19
  // millisecond timestamp and comparing numerically. Pure lexical compare
@@ -51,8 +51,8 @@ export function sortByDateDesc(items) {
51
51
  // its `api_rank` (1 = most semantically relevant in API order) so the
52
52
  // SKILL.md can tell the agent to derive a "Top Relevant" sub-view for
53
53
  // member tier without re-querying. For anonymous tier api_rank reflects
54
- // only the relative order within a randomized subset, not the corpus
55
- // rank — `render_hint` flags that distinction.
54
+ // temporal order (newest-first from the API), not semantic relevance —
55
+ // `render_hint` flags that distinction.
56
56
  //
57
57
  // Preconditions: `response` must be a validated success envelope from
58
58
  // `client.search()`. `client.js` validates `meta.tier` is a non-empty