Skip to content

refactor#29

Merged
abechan1 merged 10 commits into
mainfrom
refactor
Jun 29, 2026
Merged

refactor#29
abechan1 merged 10 commits into
mainfrom
refactor

Conversation

@abechan1

@abechan1 abechan1 commented Jun 29, 2026

Copy link
Copy Markdown
Contributor
  • fix: support sequential slide filenames
  • refactor: consolidate output framework metadata into Output::Format
  • refactor: group CLI-facing classes under Slidict::Cli namespace
  • refactor: group slidict.io API clients under Slidict::External namespace
  • refactor: move MarkdownRenderer under Slidict::Output
  • fix: require sinatra/base in build_app, not just run

Summary by CodeRabbit

  • New Features

    • slidict serve で生成済みスライドをローカル配信できるようになりました。
    • slidict slides で一覧表示・詳細表示・作成・編集ができるようになりました。
    • 出力先の既定が public/ 配下に変更され、フレームワーク別の出力形式も整理されました。
  • Bug Fixes

    • 生成ファイルの保存先ルールを見直し、既存ファイルとの競合を避けやすくしました。
  • Chores

    • バージョンを 0.3.1 に更新しました。

abechan1 and others added 6 commits June 29, 2026 05:35
Extension, body_format, and frontmatter for each slide framework were
scattered across cli.rb and markdown_renderer.rb. Move them into a
single Slidict::Output::Format registry so adding a new framework only
requires one new entry.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Slidict::CLI, SlidesCommand, and Server were flat siblings of domain
classes (Deck, Config, LLMClient, etc.), making it unclear which
classes are CLI entry points versus reusable domain logic. Move them
into lib/slidict/cli/ as Slidict::Cli::App, Slidict::Cli::Slides, and
Slidict::Cli::Serve. Also drop Server's unused class-level .run, since
the instance method was already the single call path.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AuthClient and SlidesClient both talk directly to the slidict.io HTTP
API, but lived as flat siblings of unrelated domain classes. Move them
into Slidict::External (auth_client.rb, slides_client.rb) so the
boundary between "talks to slidict.io" and local-only logic
(Credentials, Cli::*) is explicit in the namespace.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
MarkdownRenderer turns a Deck into the rendered output, so it belongs
next to Output::Format rather than as a flat sibling of CLI/auth code.
Renamed to Slidict::Output::Renderer to match the namespace. Deck and
Slide stay at the Slidict top level since they're the domain model fed
into both the LLM client and the renderer, not output themselves.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
build_app referenced Sinatra::Base directly, relying on run to have
required "sinatra/base" first. Calling build_app on its own (as
serve_spec.rb does to test the Rack app without starting a server)
raised NameError. Move the require into build_app so it works either way.
@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: e9acf211-46f9-497f-9e64-2cc2ba4cc553

📥 Commits

Reviewing files that changed from the base of the PR and between 1c14efd and d1ce3ad.

📒 Files selected for processing (18)
  • .rubocop.yml
  • lib/slidict.rb
  • lib/slidict/cli/app.rb
  • lib/slidict/cli/serve.rb
  • lib/slidict/cli/slides.rb
  • lib/slidict/credentials.rb
  • lib/slidict/external/slidict_io/auth.rb
  • lib/slidict/external/slidict_io/client.rb
  • lib/slidict/external/slidict_io/credentials.rb
  • lib/slidict/llm/client.rb
  • lib/slidict/llm_client.rb
  • spec/slidict/cli/app_spec.rb
  • spec/slidict/cli/serve_spec.rb
  • spec/slidict/cli/slides_spec.rb
  • spec/slidict/external/slidict_io/auth_spec.rb
  • spec/slidict/external/slidict_io/client_spec.rb
  • spec/slidict/external/slidict_io/credentials_spec.rb
  • spec/slidict/llm/client_spec.rb

📝 Walkthrough

ウォークスルー

旧来の単一ファイル実装(Slidict::CLIAuthClientSlidesClientMarkdownRendererSlidesCommandCredentials)を削除し、cli/external/slidict_io/llm/output/ 配下の名前空間に再実装した。デフォルト出力先を public/ 配下の連番ファイルへ変更し、slidict serve サブコマンド(Sinatra)を新設した。

変更点

CLI・外部クライアント・出力レンダラのモジュール再編と serve サブコマンド追加

レイヤー / ファイル 概要
Output::Format レジストリと Renderer の実装
lib/slidict/output/format.rb, lib/slidict/output/renderer.rb, spec/slidict/output/renderer_spec.rb
Output::Format がフレームワーク別の拡張子・body_format・frontmatter を REGISTRY に集約し、Output::Renderer が Format を参照してデッキを Markdown/Asciidoc 形式にレンダリングする。スペックの対象クラスも更新済み。
External::SlidictIo::Auth と Credentials の実装
lib/slidict/external/slidict_io/auth.rb, lib/slidict/external/slidict_io/credentials.rb, spec/slidict/external/slidict_io/auth_spec.rb, spec/slidict/external/slidict_io/credentials_spec.rb
デバイスコード認証フロー(Auth)と CLI アクセストークンのファイル保存・読み取り(Credentials)を新名前空間に実装。タイムアウト設定のテストも追加。
External::SlidictIo::Client の実装
lib/slidict/external/slidict_io/client.rb, spec/slidict/external/slidict_io/client_spec.rb
スライド CRUD HTTP クライアントを新名前空間に実装し、401/403/404/422/429 を個別例外クラスへマッピング。スペックの対象クラスと例外参照も更新。
Llm::Client の実装
lib/slidict/llm/client.rb, .rubocop.yml, spec/slidict/llm/client_spec.rb
OpenAI 互換 API への接続確認とスライド生成を Slidict::Llm::Client に再実装。JSON 配列抽出ロジックで思考文混在の応答に対応。rubocop の除外パスも更新。
Cli::Slides サブコマンド実装
lib/slidict/cli/slides.rb, spec/slidict/cli/slides_spec.rb
list/show/create/edit の引数解析・送信・エラー分類・Unauthorized 時の再認証リトライ・ヘルプ出力を Cli::Slides に実装。--body- で始まるケースや --file 不存在ケースのテストを追加。
Cli::Serve サブコマンドと Sinatra スライドサーバ
lib/slidict/cli/serve.rb, slidict.gemspec, spec/slidict/cli/serve_spec.rb
public/ 配下の .md/.adoc を列挙して Sinatra で配信する Cli::Serve を実装。puma/rackup/sinatra を gemspec に追加。パーセントエンコードのテストも含む。
Cli::App エントリポイントと出力パス決定ロジック
lib/slidict/cli/app.rb, spec/slidict/cli/app_spec.rb
Cli::App が引数を解析して auth/slides/serve/generate の各フローに分岐し、public/ 配下への逐次連番出力パスを決定してレンダリング結果を書き込む。フレームワーク別拡張子・--filename 正規化のテストを追加。
エントリポイント切り替え・ロード設定・バージョン・ドキュメント更新
bin/slidict, lib/slidict.rb, lib/slidict/version.rb, .gitignore, README.md
bin/slidict のエントリを Cli::App に切り替え、require_relative を新構成に更新、バージョンを 0.3.1 に更新。.gitignore と README を public/ ベースの出力に合わせて変更。

シーケンス図

sequenceDiagram
  participant User as ユーザー
  participant App as Cli::App
  participant Auth as External::SlidictIo::Auth
  participant Creds as External::SlidictIo::Credentials
  participant LLM as Llm::Client
  participant Renderer as Output::Renderer
  participant Slides as Cli::Slides
  participant Serve as Cli::Serve

  User->>App: slidict auth
  App->>Auth: request_device_code
  Auth-->>App: device_code, user_code
  App->>Auth: poll_token(device_code:)
  Auth-->>App: access_token
  App->>Creds: write_cli_token!

  User->>App: slidict --topic "..."
  App->>LLM: verify_connection!
  LLM-->>App: ok
  App->>LLM: generate_slides(deck)
  LLM-->>App: Slide[]
  App->>Renderer: render(deck)
  Renderer-->>App: content
  App-->>User: Created public/001.md

  User->>App: slidict serve
  App->>Serve: run(args)
  Serve-->>User: Sinatra GET /
Loading

推定コードレビュー工数

🎯 4 (Complex) | ⏱️ ~60 minutes

関連する可能性のある PR

  • slidict/slidict-cli#23: 本 PR が削除した旧 Slidict::AuthClientSlidict::Credentials・CLI 配線を導入した PR。
  • slidict/slidict-cli#26: 本 PR が External::SlidictIo::Auth/CredentialsCli::App/Cli::Slides に置き換えた GitHub デバイスログインフローと Unauthorized 再試行ロジックを導入した PR。
  • slidict/slidict-cli#28: Sinatra による public/ 配下スライド配信・連番ファイル出力・--filename など、本 PR と同一機能領域を扱う PR。

提案ラベル

codex

🐇 モジュールを分けて名前空間整え、
public/ に連番スライドが並ぶよ。
Sinatra が serve を受け取って、
デバイスコードで認証も新鮮!
バージョン 0.3.1、うさぎ跳ね! 🎉

🚥 Pre-merge checks | ✅ 3 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 2.53% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive タイトルが単語1つの 'refactor' だけで、変更内容の要点を特定できません。 主要な変更点が分かる具体的なタイトルにしてください。例: CLI/出力処理のリファクタリング。
✅ Passed checks (3 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch refactor

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Matches the Cli/External/Output namespace pattern already used
elsewhere. Updated the .rubocop.yml ClassLength exclude path to follow
the file move.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/slidict/cli/app.rb`:
- Around line 282-295: `output_path_for` currently always prefixes `public/`, so
passing `--filename public/...` in `normalize_filename` produces a duplicated
`public/public/...` path. Update the path handling in `output_path_for` and/or
`normalize_filename` to either strip an initial `public/` segment before joining
or reject such input explicitly, and keep the validation consistent with the
existing `ArgumentError` checks for relative paths and `..`.

In `@lib/slidict/cli/serve.rb`:
- Around line 69-70: The escape_path method in serve.rb is using CGI.escape on
each path segment, which turns spaces into plus signs and breaks static file
href matching. Update escape_path to use a path-segment-safe encoder that
preserves spaces as percent-encoding, and ensure the Serve CLI path handling
still joins segments correctly. Add a regression spec around the escape_path
behavior or the serving path for a filename like my slide.md to verify the href
becomes the expected encoded path.

In `@lib/slidict/cli/slides.rb`:
- Around line 100-103: `fetch_value!` is rejecting any argument that starts with
`-`, which breaks valid CLI values like `--title "-draft"` or `--body "---..."`.
Update the option parsing in `Slides::CLI#fetch_value!` so it only treats a
leading dash as missing when the token is actually another known option or a
true absent value, and allow legitimate string values that begin with `-` to
pass through for `slidict slides create/edit` as well as `publish`.
- Around line 169-170: `read_body` で `options[:file]` を `File.read`
している箇所は、存在しないパスや権限不足の `Errno::*` がそのまま未処理で落ちるため、`Slides::CLI#read_body`
内で読み込み失敗を捕捉して `ArgumentError` に変換し、`run` 側の既存エラーハンドリングに流すようにしてください。`read_body` と
`run` の責務を崩さず、`--file` 指定時の失敗が CLI 全体の例外にならないようにしてください。
- Around line 107-122: The `list` and `show` methods in `SlidesCLI` currently
skip the 401 re-auth retry path that `submit` uses, so expired tokens fall
straight into `print_client_error`. Update these command handlers to catch
`External::SlidesClient::Unauthorized`, trigger the same
re-authentication-and-retry flow used by the create/edit path, and only fall
back to `External::SlidesClient::Error` handling after retry fails. Keep the
retry logic aligned with the existing client calls in `list`, `show`, and the
submit flow so behavior is consistent across subcommands.

In `@lib/slidict/external/auth_client.rb`:
- Around line 57-59: The Net::HTTP call in auth_client’s request path has no
timeout settings, so the CLI can hang during authentication. Update the
Net::HTTP.start usage in the auth client flow (including the methods that drive
request_device_code and poll_token) to set explicit open_timeout and
read_timeout on the HTTP object before making the request. Keep the change
localized to the HTTP request helper so all auth API calls inherit the same
timeout behavior.

In `@lib/slidict/external/slides_client.rb`:
- Around line 90-92: The Net::HTTP request in SlidesClient currently has no
timeout, so a stalled API can hang the CLI indefinitely. Update the SlidesClient
request path that uses Net::HTTP.start and http.request to set both open_timeout
and read_timeout on the HTTP object before making the call. Keep the change
localized to the slide API client so the failure mode for slidict slides stays
bounded.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 55af38b0-144b-421f-be15-0742f6a4acf6

📥 Commits

Reviewing files that changed from the base of the PR and between 6ac8306 and 1c14efd.

📒 Files selected for processing (23)
  • .gitignore
  • README.md
  • bin/slidict
  • lib/slidict.rb
  • lib/slidict/auth_client.rb
  • lib/slidict/cli.rb
  • lib/slidict/cli/app.rb
  • lib/slidict/cli/serve.rb
  • lib/slidict/cli/slides.rb
  • lib/slidict/external/auth_client.rb
  • lib/slidict/external/slides_client.rb
  • lib/slidict/markdown_renderer.rb
  • lib/slidict/output/format.rb
  • lib/slidict/output/renderer.rb
  • lib/slidict/slides_client.rb
  • lib/slidict/slides_command.rb
  • lib/slidict/version.rb
  • slidict.gemspec
  • spec/slidict/cli/app_spec.rb
  • spec/slidict/cli/serve_spec.rb
  • spec/slidict/cli/slides_spec.rb
  • spec/slidict/external/slides_client_spec.rb
  • spec/slidict/output/renderer_spec.rb
💤 Files with no reviewable changes (5)
  • lib/slidict/cli.rb
  • lib/slidict/auth_client.rb
  • lib/slidict/slides_command.rb
  • lib/slidict/markdown_renderer.rb
  • lib/slidict/slides_client.rb

Comment thread lib/slidict/cli/app.rb
Comment thread lib/slidict/cli/serve.rb Outdated
Comment thread lib/slidict/cli/slides.rb
Comment thread lib/slidict/cli/slides.rb
Comment thread lib/slidict/cli/slides.rb Outdated
Comment thread lib/slidict/external/auth_client.rb Outdated
Comment on lines +57 to +59
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
http.request(request)
end

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

認証 API 呼び出しにタイムアウトがなく、CLI が張り付く可能性があります。

接続先のハングやネットワーク断で request_device_code / poll_token が返らなくなります。認証フローは対話コマンドなので、open_timeout / read_timeout はここで明示した方が安全です。

修正案
-        response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
+        response = Net::HTTP.start(
+          uri.hostname,
+          uri.port,
+          use_ssl: uri.scheme == "https",
+          open_timeout: 5,
+          read_timeout: 30
+        ) do |http|
           http.request(request)
         end
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
http.request(request)
end
response = Net::HTTP.start(
uri.hostname,
uri.port,
use_ssl: uri.scheme == "https",
open_timeout: 5,
read_timeout: 30
) do |http|
http.request(request)
end
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/slidict/external/auth_client.rb` around lines 57 - 59, The Net::HTTP call
in auth_client’s request path has no timeout settings, so the CLI can hang
during authentication. Update the Net::HTTP.start usage in the auth client flow
(including the methods that drive request_device_code and poll_token) to set
explicit open_timeout and read_timeout on the HTTP object before making the
request. Keep the change localized to the HTTP request helper so all auth API
calls inherit the same timeout behavior.

Comment thread lib/slidict/external/slides_client.rb Outdated
Comment on lines +90 to +92
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
http.request(request)
end

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

slides API 呼び出しにもタイムアウトがありません。

このままだと API 側の停止や TCP ハングで slidict slides が戻らなくなります。少なくとも open_timeout / read_timeout を入れて、対話 CLI の失敗モードを有限にしてください。

修正案
-        response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
+        response = Net::HTTP.start(
+          uri.hostname,
+          uri.port,
+          use_ssl: uri.scheme == "https",
+          open_timeout: 5,
+          read_timeout: 30
+        ) do |http|
           http.request(request)
         end
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: uri.scheme == "https") do |http|
http.request(request)
end
response = Net::HTTP.start(
uri.hostname,
uri.port,
use_ssl: uri.scheme == "https",
open_timeout: 5,
read_timeout: 30
) do |http|
http.request(request)
end
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/slidict/external/slides_client.rb` around lines 90 - 92, The Net::HTTP
request in SlidesClient currently has no timeout, so a stalled API can hang the
CLI indefinitely. Update the SlidesClient request path that uses Net::HTTP.start
and http.request to set both open_timeout and read_timeout on the HTTP object
before making the call. Keep the change localized to the slide API client so the
failure mode for slidict slides stays bounded.

abechan1 and others added 3 commits June 29, 2026 21:34
Credentials exists to support authenticating against the slidict.io
API (it stores/reads the CLI access token used by AuthClient and
SlidesClient), so it belongs alongside them in Slidict::External
rather than as a flat sibling of CLI-only code.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
AuthClient, SlidesClient, and Credentials all exist specifically to
talk to (or hold credentials for) the slidict.io API, so nest them
under Slidict::External::SlidictIo as Auth, Client, and Credentials
rather than flat siblings under External. Leaves External free for
any other third-party integration added later.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- cli/app.rb: --filename public/x no longer duplicates into
  public/public/x.md
- cli/serve.rb: percent-encode hrefs instead of CGI.escape, which
  turned spaces into "+" and broke static file lookups
- cli/slides.rb: fetch_value! no longer rejects legitimate values that
  start with "-" (e.g. YAML frontmatter bodies) for create/edit
- cli/slides.rb: list/show now share the same 401 reauthenticate-and-
  retry behavior as create/edit, instead of failing immediately
- cli/slides.rb: read_body converts Errno::ENOENT/EACCES from a
  missing/unreadable --file into a clean ArgumentError instead of
  crashing the CLI
- external/slidict_io/auth.rb, client.rb: set open_timeout/read_timeout
  on Net::HTTP.start so a hung slidict.io endpoint can't block the CLI
  forever

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@abechan1 abechan1 merged commit 38f7675 into main Jun 29, 2026
4 of 5 checks passed
@abechan1 abechan1 deleted the refactor branch June 29, 2026 14:10
@abechan1 abechan1 mentioned this pull request Jun 29, 2026
Merged
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant