{"owner":"stringer-rss","repo":"stringer","hasSkills":true,"totalSkillsCount":1,"totalTokensCount":1214,"categories":["root-instruction"],"hasMcp":false,"mcpConfig":null,"found":["AGENTS.md"],"skills":{"AGENTS.md":"# AGENTS.md\n\nThis file provides guidance to coding agents when working with code in this repository.\n\n## What this is\n\nStringer is a self-hosted, anti-social RSS reader: a Rails 8.1 app (Ruby 4.0) backed by PostgreSQL, with feed fetching run as background jobs via GoodJob. The README's mention of Backbone.js is partly outdated — see the frontend notes below.\n\n## Commands\n\nToolchain versions (Ruby, Node, Postgres, pnpm) are pinned in `.tool-versions`; check that file rather than relying on numbers quoted here.\n\n- **Setup:** `bin/setup` (runs `bundle install`, `db:prepare`, clears logs/tmp, then launches the dev server). Pass `--skip-server` to stop before launching.\n- **Run dev server:** `bin/dev` (foreman against `Procfile.dev` — boots Puma on port 3000 plus `js`/`css` esbuild watchers). The app is at `http://localhost:3000`.\n- **Ruby tests:** `bundle exec rspec`. Run a single file/example with `bundle exec rspec spec/path/to/file_spec.rb:LINE`.\n- **JS tests:** `pnpm test` (vitest + jsdom with coverage). Specs live in `spec/javascript/**/*_spec.ts`. `pnpm test` runs `pretest` first, which runs `tscheck` and `eslint`.\n- **Type check:** `pnpm tscheck` (`tsc --noEmit`).\n- **Lint (Ruby):** `bundle exec rubocop`. **Lint (JS):** `pnpm eslint`. **Lint (CSS):** `pnpm stylelint`.\n- **Security scan:** `bundle exec brakeman`.\n- **Interactive console:** `rake console` (or `bin/rails console`).\n- **Asset build (one-off):** `pnpm build` (JS via esbuild) and `pnpm build:css` (CSS via esbuild).\n\nFeed operations are exposed as rake tasks: `rake fetch_feeds`, `rake fetch_feed[ID]`, `rake lazy_fetch`, `rake cleanup_old_stories[DAYS]` (default 30 days; removes read, unstarred stories).\n\n## Architecture\n\n**Command objects (`app/commands/`)** hold the business logic. Each is a module or class with a single `.call` entry point (e.g. `Feed::FetchOne.call(feed)`), grouped by domain (`feed/`, `story/`, `user/`, `fever_api/`). Controllers and jobs stay thin and delegate to commands. When adding behavior, prefer a new command over fattening a controller or model.\n\n**Repositories (`app/repositories/`)** wrap all ActiveRecord query/persistence logic (`FeedRepository`, `StoryRepository`, `GroupRepository`, `UserRepository`). Commands and controllers go through repositories rather than calling `Feed.where(...)` etc. directly. Keep DB access in the repository layer.\n\n**Background jobs** funnel through a single `CallableJob`, which just calls any callable: `CallableJob.perform_later(Feed::FetchOne, feed)` enqueues `Feed::FetchOne.call(feed)`. There is rarely a need to write a new `ApplicationJob` subclass — enqueue a command via `CallableJob` instead. Jobs run on GoodJob (Postgres-backed).\n\n**Feed fetching flow:** `Feed::FetchAll` → enqueues `Feed::FetchOne` per feed → `HTTParty` fetches, `Feedjira` parses → `Feed::FindNewStories` diffs against the latest stored entry → `StoryRepository.add`. Fetch failures set the feed status to `:red` (vs `:green`) and log rather than raise.\n\n**Admin routes** under `/admin` (GoodJob dashboard, settings, debug) are gated by `AdminConstraint` (`lib/admin_constraint.rb`). Stringer is single-user; auth is password-based (`bcrypt`).\n\n**Fever API:** Stringer implements a clone of the Fever API (`fever_controller`, `app/commands/fever_api/`) so third-party mobile clients can connect.\n\n## Frontend\n\nAssets are bundled with **esbuild**, not Rails' asset pipeline compilation (Propshaft serves the built files). Two distinct JS worlds coexist:\n\n- **Stimulus controllers** (`app/javascript/controllers/`) — the current approach for new interactive behavior (dialogs, hotkeys, toggles).\n- **Backbone** (`app/javascript/application.ts`) — the legacy story-list view (`Story`/`StoryView`/`StoryList`/`AppView`), which talks to `/stories`. Note this file is `@ts-nocheck`. Underscore templates use custom `{{ }}` delimiters (set in `application.ts`), not the default `<% %>`.\n\nTurbo Drive is explicitly **disabled** (`Turbo.session.drive = false`).\n\n## Linting baselines\n\nESLint, Stylelint, and RuboCop each use a generated \"todo\" baseline file (`.eslint_todo.ts`, `.stylelint_todo.yml`, `.rubocop_todo.yml`) that suppresses pre-existing offenses. The intent is to ratchet these down over time — fix offenses and remove their entries rather than adding new suppressions. Regenerate via `bundle exec exe/eslint_autogen` / `bundle exec exe/stylelint_autogen` (CI checks that the committed baseline matches). A custom RuboCop cop lives in `linters/rubocop/cop/`.\n\n## CI\n\nCircleCI (`.circleci/config.yml`) runs, in order: `tscheck`, eslint (incl. unused-rule check and autogen drift check), vitest, brakeman, stylelint (+ autogen drift), rubocop, then rspec (with `xvfb-run` for system specs). Match this locally before pushing.\n"},"files":{"AGENTS.md":"# AGENTS.md\n\nThis file provides guidance to coding agents when working with code in this repository.\n\n## What this is\n\nStringer is a self-hosted, anti-social RSS reader: a Rails 8.1 app (Ruby 4.0) backed by PostgreSQL, with feed fetching run as background jobs via GoodJob. The README's mention of Backbone.js is partly outdated — see the frontend notes below.\n\n## Commands\n\nToolchain versions (Ruby, Node, Postgres, pnpm) are pinned in `.tool-versions`; check that file rather than relying on numbers quoted here.\n\n- **Setup:** `bin/setup` (runs `bundle install`, `db:prepare`, clears logs/tmp, then launches the dev server). Pass `--skip-server` to stop before launching.\n- **Run dev server:** `bin/dev` (foreman against `Procfile.dev` — boots Puma on port 3000 plus `js`/`css` esbuild watchers). The app is at `http://localhost:3000`.\n- **Ruby tests:** `bundle exec rspec`. Run a single file/example with `bundle exec rspec spec/path/to/file_spec.rb:LINE`.\n- **JS tests:** `pnpm test` (vitest + jsdom with coverage). Specs live in `spec/javascript/**/*_spec.ts`. `pnpm test` runs `pretest` first, which runs `tscheck` and `eslint`.\n- **Type check:** `pnpm tscheck` (`tsc --noEmit`).\n- **Lint (Ruby):** `bundle exec rubocop`. **Lint (JS):** `pnpm eslint`. **Lint (CSS):** `pnpm stylelint`.\n- **Security scan:** `bundle exec brakeman`.\n- **Interactive console:** `rake console` (or `bin/rails console`).\n- **Asset build (one-off):** `pnpm build` (JS via esbuild) and `pnpm build:css` (CSS via esbuild).\n\nFeed operations are exposed as rake tasks: `rake fetch_feeds`, `rake fetch_feed[ID]`, `rake lazy_fetch`, `rake cleanup_old_stories[DAYS]` (default 30 days; removes read, unstarred stories).\n\n## Architecture\n\n**Command objects (`app/commands/`)** hold the business logic. Each is a module or class with a single `.call` entry point (e.g. `Feed::FetchOne.call(feed)`), grouped by domain (`feed/`, `story/`, `user/`, `fever_api/`). Controllers and jobs stay thin and delegate to commands. When adding behavior, prefer a new command over fattening a controller or model.\n\n**Repositories (`app/repositories/`)** wrap all ActiveRecord query/persistence logic (`FeedRepository`, `StoryRepository`, `GroupRepository`, `UserRepository`). Commands and controllers go through repositories rather than calling `Feed.where(...)` etc. directly. Keep DB access in the repository layer.\n\n**Background jobs** funnel through a single `CallableJob`, which just calls any callable: `CallableJob.perform_later(Feed::FetchOne, feed)` enqueues `Feed::FetchOne.call(feed)`. There is rarely a need to write a new `ApplicationJob` subclass — enqueue a command via `CallableJob` instead. Jobs run on GoodJob (Postgres-backed).\n\n**Feed fetching flow:** `Feed::FetchAll` → enqueues `Feed::FetchOne` per feed → `HTTParty` fetches, `Feedjira` parses → `Feed::FindNewStories` diffs against the latest stored entry → `StoryRepository.add`. Fetch failures set the feed status to `:red` (vs `:green`) and log rather than raise.\n\n**Admin routes** under `/admin` (GoodJob dashboard, settings, debug) are gated by `AdminConstraint` (`lib/admin_constraint.rb`). Stringer is single-user; auth is password-based (`bcrypt`).\n\n**Fever API:** Stringer implements a clone of the Fever API (`fever_controller`, `app/commands/fever_api/`) so third-party mobile clients can connect.\n\n## Frontend\n\nAssets are bundled with **esbuild**, not Rails' asset pipeline compilation (Propshaft serves the built files). Two distinct JS worlds coexist:\n\n- **Stimulus controllers** (`app/javascript/controllers/`) — the current approach for new interactive behavior (dialogs, hotkeys, toggles).\n- **Backbone** (`app/javascript/application.ts`) — the legacy story-list view (`Story`/`StoryView`/`StoryList`/`AppView`), which talks to `/stories`. Note this file is `@ts-nocheck`. Underscore templates use custom `{{ }}` delimiters (set in `application.ts`), not the default `<% %>`.\n\nTurbo Drive is explicitly **disabled** (`Turbo.session.drive = false`).\n\n## Linting baselines\n\nESLint, Stylelint, and RuboCop each use a generated \"todo\" baseline file (`.eslint_todo.ts`, `.stylelint_todo.yml`, `.rubocop_todo.yml`) that suppresses pre-existing offenses. The intent is to ratchet these down over time — fix offenses and remove their entries rather than adding new suppressions. Regenerate via `bundle exec exe/eslint_autogen` / `bundle exec exe/stylelint_autogen` (CI checks that the committed baseline matches). A custom RuboCop cop lives in `linters/rubocop/cop/`.\n\n## CI\n\nCircleCI (`.circleci/config.yml`) runs, in order: `tscheck`, eslint (incl. unused-rule check and autogen drift check), vitest, brakeman, stylelint (+ autogen drift), rubocop, then rspec (with `xvfb-run` for system specs). Match this locally before pushing.\n"},"items":[{"name":"AGENTS.md","path":"AGENTS.md","rawUrl":"https://raw.githubusercontent.com/stringer-rss/stringer/HEAD/AGENTS.md","title":"AI Agent Protocol & Instructions","category":"root-instruction","format":"markdown","content":"# AGENTS.md\n\nThis file provides guidance to coding agents when working with code in this repository.\n\n## What this is\n\nStringer is a self-hosted, anti-social RSS reader: a Rails 8.1 app (Ruby 4.0) backed by PostgreSQL, with feed fetching run as background jobs via GoodJob. The README's mention of Backbone.js is partly outdated — see the frontend notes below.\n\n## Commands\n\nToolchain versions (Ruby, Node, Postgres, pnpm) are pinned in `.tool-versions`; check that file rather than relying on numbers quoted here.\n\n- **Setup:** `bin/setup` (runs `bundle install`, `db:prepare`, clears logs/tmp, then launches the dev server). Pass `--skip-server` to stop before launching.\n- **Run dev server:** `bin/dev` (foreman against `Procfile.dev` — boots Puma on port 3000 plus `js`/`css` esbuild watchers). The app is at `http://localhost:3000`.\n- **Ruby tests:** `bundle exec rspec`. Run a single file/example with `bundle exec rspec spec/path/to/file_spec.rb:LINE`.\n- **JS tests:** `pnpm test` (vitest + jsdom with coverage). Specs live in `spec/javascript/**/*_spec.ts`. `pnpm test` runs `pretest` first, which runs `tscheck` and `eslint`.\n- **Type check:** `pnpm tscheck` (`tsc --noEmit`).\n- **Lint (Ruby):** `bundle exec rubocop`. **Lint (JS):** `pnpm eslint`. **Lint (CSS):** `pnpm stylelint`.\n- **Security scan:** `bundle exec brakeman`.\n- **Interactive console:** `rake console` (or `bin/rails console`).\n- **Asset build (one-off):** `pnpm build` (JS via esbuild) and `pnpm build:css` (CSS via esbuild).\n\nFeed operations are exposed as rake tasks: `rake fetch_feeds`, `rake fetch_feed[ID]`, `rake lazy_fetch`, `rake cleanup_old_stories[DAYS]` (default 30 days; removes read, unstarred stories).\n\n## Architecture\n\n**Command objects (`app/commands/`)** hold the business logic. Each is a module or class with a single `.call` entry point (e.g. `Feed::FetchOne.call(feed)`), grouped by domain (`feed/`, `story/`, `user/`, `fever_api/`). Controllers and jobs stay thin and delegate to commands. When adding behavior, prefer a new command over fattening a controller or model.\n\n**Repositories (`app/repositories/`)** wrap all ActiveRecord query/persistence logic (`FeedRepository`, `StoryRepository`, `GroupRepository`, `UserRepository`). Commands and controllers go through repositories rather than calling `Feed.where(...)` etc. directly. Keep DB access in the repository layer.\n\n**Background jobs** funnel through a single `CallableJob`, which just calls any callable: `CallableJob.perform_later(Feed::FetchOne, feed)` enqueues `Feed::FetchOne.call(feed)`. There is rarely a need to write a new `ApplicationJob` subclass — enqueue a command via `CallableJob` instead. Jobs run on GoodJob (Postgres-backed).\n\n**Feed fetching flow:** `Feed::FetchAll` → enqueues `Feed::FetchOne` per feed → `HTTParty` fetches, `Feedjira` parses → `Feed::FindNewStories` diffs against the latest stored entry → `StoryRepository.add`. Fetch failures set the feed status to `:red` (vs `:green`) and log rather than raise.\n\n**Admin routes** under `/admin` (GoodJob dashboard, settings, debug) are gated by `AdminConstraint` (`lib/admin_constraint.rb`). Stringer is single-user; auth is password-based (`bcrypt`).\n\n**Fever API:** Stringer implements a clone of the Fever API (`fever_controller`, `app/commands/fever_api/`) so third-party mobile clients can connect.\n\n## Frontend\n\nAssets are bundled with **esbuild**, not Rails' asset pipeline compilation (Propshaft serves the built files). Two distinct JS worlds coexist:\n\n- **Stimulus controllers** (`app/javascript/controllers/`) — the current approach for new interactive behavior (dialogs, hotkeys, toggles).\n- **Backbone** (`app/javascript/application.ts`) — the legacy story-list view (`Story`/`StoryView`/`StoryList`/`AppView`), which talks to `/stories`. Note this file is `@ts-nocheck`. Underscore templates use custom `{{ }}` delimiters (set in `application.ts`), not the default `<% %>`.\n\nTurbo Drive is explicitly **disabled** (`Turbo.session.drive = false`).\n\n## Linting baselines\n\nESLint, Stylelint, and RuboCop each use a generated \"todo\" baseline file (`.eslint_todo.ts`, `.stylelint_todo.yml`, `.rubocop_todo.yml`) that suppresses pre-existing offenses. The intent is to ratchet these down over time — fix offenses and remove their entries rather than adding new suppressions. Regenerate via `bundle exec exe/eslint_autogen` / `bundle exec exe/stylelint_autogen` (CI checks that the committed baseline matches). A custom RuboCop cop lives in `linters/rubocop/cop/`.\n\n## CI\n\nCircleCI (`.circleci/config.yml`) runs, in order: `tscheck`, eslint (incl. unused-rule check and autogen drift check), vitest, brakeman, stylelint (+ autogen drift), rubocop, then rspec (with `xvfb-run` for system specs). Match this locally before pushing.\n","isInternal":false,"tokens":1214,"sizeBytes":4793}],"systemPromptSnippet":"<yakaai_skills repo=\"stringer-rss/stringer\">\n<!-- File: AGENTS.md (Tokens: ~1214 | Category: root-instruction) -->\n# AGENTS.md\n\nThis file provides guidance to coding agents when working with code in this repository.\n\n## What this is\n\nStringer is a self-hosted, anti-social RSS reader: a Rails 8.1 app (Ruby 4.0) backed by PostgreSQL, with feed fetching run as background jobs via GoodJob. The README's mention of Backbone.js is partly outdated — see the frontend notes below.\n\n## Commands\n\nToolchain versions (Ruby, Node, Postgres, pnpm) are pinned in `.tool-versions`; check that file rather than relying on numbers quoted here.\n\n- **Setup:** `bin/setup` (runs `bundle install`, `db:prepare`, clears logs/tmp, then launches the dev server). Pass `--skip-server` to stop before launching.\n- **Run dev server:** `bin/dev` (foreman against `Procfile.dev` — boots Puma on port 3000 plus `js`/`css` esbuild watchers). The app is at `http://localhost:3000`.\n- **Ruby tests:** `bundle exec rspec`. Run a single file/example with `bundle exec rspec spec/path/to/file_spec.rb:LINE`.\n- **JS tests:** `pnpm test` (vitest + jsdom with coverage). Specs live in `spec/javascript/**/*_spec.ts`. `pnpm test` runs `pretest` first, which runs `tscheck` and `eslint`.\n- **Type check:** `pnpm tscheck` (`tsc --noEmit`).\n- **Lint (Ruby):** `bundle exec rubocop`. **Lint (JS):** `pnpm eslint`. **Lint (CSS):** `pnpm stylelint`.\n- **Security scan:** `bundle exec brakeman`.\n- **Interactive console:** `rake console` (or `bin/rails console`).\n- **Asset build (one-off):** `pnpm build` (JS via esbuild) and `pnpm build:css` (CSS via esbuild).\n\nFeed operations are exposed as rake tasks: `rake fetch_feeds`, `rake fetch_feed[ID]`, `rake lazy_fetch`, `rake cleanup_old_stories[DAYS]` (default 30 days; removes read, unstarred stories).\n\n## Architecture\n\n**Command objects (`app/commands/`)** hold the business logic. Each is a module or class with a single `.call` entry point (e.g. `Feed::FetchOne.call(feed)`), grouped by domain (`feed/`, `story/`, `user/`, `fever_api/`). Controllers and jobs stay thin and delegate to commands. When adding behavior, prefer a new command over fattening a controller or model.\n\n**Repositories (`app/repositories/`)** wrap all ActiveRecord query/persistence logic (`FeedRepository`, `StoryRepository`, `GroupRepository`, `UserRepository`). Commands and controllers go through repositories rather than calling `Feed.where(...)` etc. directly. Keep DB access in the repository layer.\n\n**Background jobs** funnel through a single `CallableJob`, which just calls any callable: `CallableJob.perform_later(Feed::FetchOne, feed)` enqueues `Feed::FetchOne.call(feed)`. There is rarely a need to write a new `ApplicationJob` subclass — enqueue a command via `CallableJob` instead. Jobs run on GoodJob (Postgres-backed).\n\n**Feed fetching flow:** `Feed::FetchAll` → enqueues `Feed::FetchOne` per feed → `HTTParty` fetches, `Feedjira` parses → `Feed::FindNewStories` diffs against the latest stored entry → `StoryRepository.add`. Fetch failures set the feed status to `:red` (vs `:green`) and log rather than raise.\n\n**Admin routes** under `/admin` (GoodJob dashboard, settings, debug) are gated by `AdminConstraint` (`lib/admin_constraint.rb`). Stringer is single-user; auth is password-based (`bcrypt`).\n\n**Fever API:** Stringer implements a clone of the Fever API (`fever_controller`, `app/commands/fever_api/`) so third-party mobile clients can connect.\n\n## Frontend\n\nAssets are bundled with **esbuild**, not Rails' asset pipeline compilation (Propshaft serves the built files). Two distinct JS worlds coexist:\n\n- **Stimulus controllers** (`app/javascript/controllers/`) — the current approach for new interactive behavior (dialogs, hotkeys, toggles).\n- **Backbone** (`app/javascript/application.ts`) — the legacy story-list view (`Story`/`StoryView`/`StoryList`/`AppView`), which talks to `/stories`. Note this file is `@ts-nocheck`. Underscore templates use custom `{{ }}` delimiters (set in `application.ts`), not the default `<% %>`.\n\nTurbo Drive is explicitly **disabled** (`Turbo.session.drive = false`).\n\n## Linting baselines\n\nESLint, Stylelint, and RuboCop each use a generated \"todo\" baseline file (`.eslint_todo.ts`, `.stylelint_todo.yml`, `.rubocop_todo.yml`) that suppresses pre-existing offenses. The intent is to ratchet these down over time — fix offenses and remove their entries rather than adding new suppressions. Regenerate via `bundle exec exe/eslint_autogen` / `bundle exec exe/stylelint_autogen` (CI checks that the committed baseline matches). A custom RuboCop cop lives in `linters/rubocop/cop/`.\n\n## CI\n\nCircleCI (`.circleci/config.yml`) runs, in order: `tscheck`, eslint (incl. unused-rule check and autogen drift check), vitest, brakeman, stylelint (+ autogen drift), rubocop, then rspec (with `xvfb-run` for system specs). Match this locally before pushing.\n</yakaai_skills>"}