# `pi-news-api` maintainer guide

This file is the operating manual for agents and maintainers working on
`pi-news-api`.

## Mission

Keep this package readable, honest, and dependable inside the Pi TUI.

The package exists to give Pi users a small, trustworthy general-news
briefing surface backed by the Currents News API, with predictable widget
behavior, accurate documentation, and conservative release practices.

## Repository map

- `index.ts` — TypeScript source of truth for the extension
- `dist/index.js` — compiled default-exported Pi extension entrypoint
- `dist/index.d.ts` — published type declarations
- `tests/news.test.ts` — Currents response parsing, category matching, and
  widget-width formatting checks
- `tests/package.test.ts` — package metadata and trust-signal structure checks
- `docs/COMPATIBILITY.md` — maintained compatibility notes
- `README.md` — user-facing package documentation
- `CONTRIBUTING.md` — contributor workflow
- `SECURITY.md` — security and disclosure policy
- `CHANGELOG.md` — release history

## Project facts

- The project is **TypeScript**.
- `dist/` is **committed directly**.
- The package is intended for **Pi package installation via npm**.
- The extension entrypoint must remain registered in `package.json` under
  `pi.extensions`.
- The project registers the documented `news_get_latest`, `news_search`, and
  `news_configure` LLM tools alongside commands, a widget, and lifecycle
  behavior.
- The current Node.js floor is **20+**.
- The extension talks to exactly one upstream service: the Currents News API
  (`api.currentsapi.services`).

## Currents API integration model

- Authentication is a single API key read from the `CURRENTS_API_KEY`
  environment variable and sent as `Authorization: Bearer <key>`. The key is
  never written to `~/.config/pi-news-api/config.json`, logs, or prompts.
- The "source" concept from RSS-based designs does not apply here. The toggle
  list is Currents' own canonical `/v2` category taxonomy (16 categories),
  because Currents already returns structured `category` metadata per
  article — headline-keyword categorization would be redundant and less
  accurate.
- The widget refresh path uses a **single** `/v2/latest-news` request with all
  enabled categories passed as repeated `category` params, not one request
  per category. This is a deliberate quota-conservation choice: the free
  Currents tier allows 1,000 requests/day.
- `/news_search` is a distinct code path against `/v2/search`, independent of
  the enabled-category list, because keyword search is a first-class Currents
  capability with no RSS equivalent.
- Rate-limit headers (`X-RateLimit-Remaining`, `X-RateLimit-Limit`) should be
  surfaced to the user (e.g. in `/news_refresh` output) rather than silently
  discarded.
- HTTP/auth failures must produce actionable, specific messages (missing key,
  invalid key, quota exceeded, upstream error) instead of a generic failure.

## Pi package conventions

Follow current Pi package guidance:

- Keep the `pi-package` keyword in `package.json`.
- Preserve `pi.extensions` so Pi can load the package root directly.
- If package metadata changes, make sure `npm pack --dry-run` still includes
  the intended runtime files and top-level docs.
- Category/language/interest state is persisted in
  `~/.config/pi-news-api/config.json`; keep the JSON schema simple and
  documented, and retain Pi custom session entries only as a
  compatibility/migration mirror.
- Render widgets against Pi's provided widget width, not guessed terminal
  width.

## Coding standards

- Prefer small, explicit helpers over clever abstractions.
- Keep command descriptions concise and agent-readable.
- Preserve stable command names; all commands must remain prefixed with
  `news_`.
- Do not present inferred story details as confirmed facts.
- Never fabricate Currents API behavior, quota limits, or Pi runtime behavior.
- Keep the package narrow in scope; resist turning it into a general-purpose
  news aggregation platform unless the repo is intentionally redesigned for
  that.

## Runtime guarantees

Maintain these behavioral guarantees:

- `/news_menu`, `/news_refresh`, `/news_categories`, `/news_enable`,
  `/news_disable`, `/news_search`, `/news_interests`, and `/news_language`
  remain the public command surface.
- `news_get_latest`, `news_search`, and `news_configure` remain the public
  agent-tool surface; tool names stay prefixed with `news_`.
- News tools return source URLs and structured article metadata, and their
  output must identify upstream article content as untrusted data rather than
  executable instructions.
- `/news_enable` and `/news_disable` continue to support exact or partial
  category-name matching (against both the Currents slug and the
  human-readable label).
- `/news_categories` remains interactive in TUI mode and falls back to plain
  status output outside TUI mode.
- Widget rendering stays width-aware and must not depend on
  `process.stdout.columns`.
- Dated articles older than the freshness window should not pin the widget or
  menu when fresher headlines are available.
- The widget is an ephemeral banner: when its countdown expires, or when Pi
  starts active agent work, it should clear itself instead of auto-refreshing
  forever or lingering over long tool/subagent runs.
- Category/language/interest state must persist across Pi sessions through
  `~/.config/pi-news-api/config.json`, with Pi custom session entries retained
  only as a compatibility/migration mirror.
- Successful command and agent-tool searches update a bounded local interest
  profile. Persist topic/count/timestamp and inferred-category aggregates only;
  never persist raw article history.
- Default latest-news requests merge at most two learned categories with the
  explicitly enabled categories and remain a single Currents request.
- Interest tracking must be inspectable, disableable, and clearable through
  `/news_interests` and `news_configure`.
- Raw topic history, counts, timestamps, and interest weights remain local and
  must never be included in model prompts, telemetry, logs, or external
  payloads. Explicit search queries and resulting category filters still go to
  Currents as documented.
- Detailed interest history is available only through local `/news_interests`
  output; agent tools may expose tracking state and personalized category names
  but not raw topic history.
- Selecting a story for deep-dive research should continue to inject a hidden
  Pi message rather than exposing raw internal prompt text to the user by
  default.
- Timers and widget state must be cleaned up on `session_shutdown`.
- The widget must never fail silently when `CURRENTS_API_KEY` is unset; it
  must show a setup notice.

## Documentation policy

Documentation must match code.

Whenever you change a command, default, widget behavior, category list, icon
mapping, or persistence behavior, update all affected docs:

1. Command `description` strings in `index.ts`
2. `README.md`
3. `AGENTS.md` if maintainer or agent expectations changed
4. `CHANGELOG.md` when shipped behavior changed
5. `CONTRIBUTING.md` or `SECURITY.md` if contributor or trust processes
   changed

Before finishing, grep for stale references:

- old command descriptions
- outdated category counts or taxonomy names
- outdated Node.js requirements
- outdated widget behavior
- removed or renamed files

## Testing policy

Every code change should be backed by tests appropriate to the behavior being
touched.

Current suites:

- `tests/news.test.ts` — Currents response parsing, category matching, and
  widget formatting behavior
- `tests/package.test.ts` — package metadata, published file expectations,
  and README command coverage

Expectations:

- Run `npm test` before considering work complete.
- Run `npm pack --dry-run` when packaging or metadata changes.
- New parsing or formatting behavior should include both success-path and
  edge-case coverage where practical.
- User-facing packaging or documentation changes should include a structural
  or metadata check when feasible.
- Keep the test suite non-network by default; tests exercise pure functions
  (`normalizeArticle`, `dedupeArticles`, `parseQuota`, `describeHttpError`,
  etc.), never live Currents API calls.

## Security and trust posture

Treat this package as user-facing runtime software, not a throwaway demo.

- Do not add hidden telemetry.
- Never write `CURRENTS_API_KEY` (or any credential) to disk, logs, prompts,
  or the config file.
- Do not log secrets, cookies, or private tokens in code, docs, or tests.
- Prefer explicit limitations over vague marketing language.
- If a behavior is uncertain, say so and inspect the code or the Currents API
  docs (https://currentsapi.services/en/docs/) instead of guessing.

## Release discipline

- Never commit without explicit user approval.
- Never push or publish without explicit user approval.
- Do not skip npm versions.
- Dry-run with `npm pack --dry-run` before publish.
- Keep tags aligned with `package.json` versions.
- If npm browser auth / 2FA interrupts publish flow, tell the user plainly and
  let them complete the auth flow rather than pretending publish succeeded.
- If the user wants to finish the release manually after prep is done,
  provide the one-liner `npm publish --ignore-scripts && git push origin master --tags`.

## Release checklist

When asked to prepare a release:

1. Run `npm test`
2. Run `npm pack --dry-run`
3. Verify `package.json` metadata is current
4. Verify `README.md` and `AGENTS.md` reflect shipped behavior
5. Check whether the current version is already published before bumping
6. Only commit, tag, push, or publish with explicit user approval
7. If the user prefers to finish the release manually, provide the
   publish/push one-liner instead of repeatedly retrying around browser-auth
   prompts
