<?xml version="1.0" encoding="UTF-8"?><rss version="2.0" xmlns:content="http://purl.org/rss/1.0/modules/content/"><channel><title>yaakai.to</title><description>yaakaito&apos;s weblog</description><link>https://yaakai.to/</link><language>ja-jp</language><item><title>docs を管理する Skill を作って (A)DR + 数個の Markdown で開発をしている</title><link>https://yaakai.to/blog/2026/dev-with-adr/</link><guid isPermaLink="true">https://yaakai.to/blog/2026/dev-with-adr/</guid><pubDate>Wed, 11 Mar 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;最近 ADR を使った開発に関する話題をよく見かける気がするが、自分も前に記事を書いたときから ADR 中心の開発を続けていて、その型が自分の中で固まってきたので現時点でのものをまとめる。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://yaakai.to/blog/2025/my-spec-driven-dev-plan&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;仕様駆動開発風のオレオレ実装計画を作って AI と併走する - yaakai.to&lt;/h3&gt;&lt;p&gt;🐧 仕様駆動開発風のオレオレ実装計画を作って AI と併走する | yaakai.to&lt;/p&gt;&lt;span&gt;yaakai.to&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;先に断っておくと基本的には個人の private なリポジトリで使っているものなので実際の出力のサンプルはない。&lt;/p&gt;
&lt;p&gt;概要としては以下のような感じで、これで分かる人は見なくていい内容だと思う:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;docs/adr もしくは docs/decisions に意思決定ログとして (A)DR を書き、それを元に Plan などを作成して開発&lt;/li&gt;
&lt;li&gt;AI 向けのドキュメントは基本 (A)DR + CLAUDE.md のみ&lt;/li&gt;
&lt;li&gt;人間向けのドキュメントとして (A)DR がすべて反映された ARCHITECTURE.md と README.md を作成する Skill を用意して作成&lt;/li&gt;
&lt;li&gt;(A)DR を書くために AskUserQuestion や WebSearch を使う Skill を別途用意&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;(A)DR と書いているのは、技術要素や Architecture っぽいものでなくとも Decision Record として残すようにしているため。以後は ADR とする。&lt;/p&gt;
&lt;p&gt;ツッコミ回避をしておくと、リポジトリに人間用のドキュメントを入れる必要はないし、毎回 AI に聞いてコードベースと ADR を調べてもらえば良い、というのはそれはそうだと思っている。
ARCHITECTURE.md はほぼ趣味九割であるといい感じに見えるという理由で入れている。一割の実用は AI をあらゆるドキュメントの代わりに使うにはまだ速度が足りないのと、AI が 100% 生成しているわけではないので人間目線で出力がある程度読みやすいからという感じ。&lt;/p&gt;
&lt;h2&gt;ディレクトリ&lt;/h2&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── .claude&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   └── skills&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│       └── doc-writer/SKILL.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── docs&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── decisions&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   ├── 2026-02-26-foo-foo-foo.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   ├── 2026-02-28-bar-bar-bar.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   └── 2026-03-02-buzz-buzz-buzz.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── ARCHITECTURE.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   └── FOOFOO.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── CLAUDE.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── README.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;└── YAAKAITO.md&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;よくある感じ。&lt;/p&gt;
&lt;p&gt;ARCHITECTURE.md 以外にも人間向けのドキュメントを残していることがたまにある。大体は調べたこと + あとで反映したいことを一時的にまとめるために使っていて、Issue にしたりコードに全部反映したら消している。&lt;/p&gt;
&lt;p&gt;YAAKAITO.md は完全に自分用のメモで global に gitignore されたものを置いている。リポジトリに入れるなという話はあるが、 DevContainer を使っている関係でここが一番扱いやすい、中身は自由なメモ。&lt;/p&gt;
&lt;h2&gt;ADR と ARCHITECTURE.md を書くための Skill&lt;/h2&gt;
&lt;p&gt;2 つのフォーマットと、常にコードベース &amp;gt; ADR &amp;gt; ARCHITECTURE.md であることを知識として Skill にしている。フォーマットや構造はリポジトリ毎に微調整したいことがあるので Plugin なんかではなくリポジトリに commit している。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;doc-writer&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Write or update project documentation (Decision Records and ARCHITECTURE.md). Use when requests involve &quot;create DR&quot;, &quot;create ADR&quot;, &quot;record decision&quot;, &quot;write ARCHITECTURE.md&quot;, &quot;update architecture doc&quot;, &quot;document design decisions&quot;, or equivalent requests to summarize prior discussion into a decision record. Also triggers on requests to summarize architecture, reflect decision changes into documentation, describe the overall project structure, or when code changes warrant updating architectural documentation. Use this skill even when the user doesn&apos;t explicitly mention &quot;DR&quot; or &quot;ARCHITECTURE.md&quot; — any request about recording why a technical choice was made or capturing the current system design should use this skill.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;user-invocable&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# Document Writer&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Create Decision Records (DR) and update ARCHITECTURE.md. DR is essentially synonymous with ADR (Architecture Decision Record) — no distinction is made.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Document Map&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;CLAUDE.md                    -- Architecture Overview, Core Principles&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;docs/ARCHITECTURE.md         -- Detailed architecture document&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;docs/decisions/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  yyyy-mm-dd-slug.md         -- Decision Records (DR)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Keep &lt;/span&gt;&lt;span&gt;`CLAUDE.md`&lt;/span&gt;&lt;span&gt; Architecture Overview and Core Principles consistent with ARCHITECTURE.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Routing&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Execute one of the following based on the request:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;1.&lt;/span&gt;&lt;span&gt; **Create/edit a DR**&lt;/span&gt;&lt;span&gt; → Follow the &quot;Decision Record Writing&quot; section&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;2.&lt;/span&gt;&lt;span&gt; **Create/update ARCHITECTURE.md**&lt;/span&gt;&lt;span&gt; → Follow the &quot;Architecture Document Writing&quot; section&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Decision Record Writing&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Naming&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Filename: &lt;/span&gt;&lt;span&gt;`yyyy-mm-dd-slug.md`&lt;/span&gt;&lt;span&gt; (date is the creation date)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Location: &lt;/span&gt;&lt;span&gt;`docs/decisions/`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Title&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Use &lt;/span&gt;&lt;span&gt;`# Title`&lt;/span&gt;&lt;span&gt; format. Preserve existing &lt;/span&gt;&lt;span&gt;`# ADR NNNN:`&lt;/span&gt;&lt;span&gt; titles as historical records.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Template&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;When creating a new DR, read &lt;/span&gt;&lt;span&gt;`references/decision-record-template.md`&lt;/span&gt;&lt;span&gt; with the Read tool and follow its structure.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Writing Guidelines&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Context and Decision are written by the user. Only the user can accurately describe their decision-making context and judgment — ask for these sections&apos; content and compose the DR from their answers&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Write Decision in active voice with &quot;We will ...&quot; statements&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Consequences should cover positive, negative, and neutral outcomes — a consequence often becomes Context for a future DR&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Cross-link related existing DRs in the Notes section&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Write section headings in English (Status, Context, Decision, Consequences, Notes)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Architecture Document Writing&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;ARCHITECTURE.md is a document written for humans, not AI agents (CLAUDE.md serves that role):&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Helps new developers and maintainers understand the system&apos;s design intent without reading every source file&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Documents core architectural concepts that reviewers rely on when evaluating whether a change aligns with the system&apos;s design direction&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Analyze DRs and the codebase to create or update ARCHITECTURE.md.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Instructions&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;1.&lt;/span&gt;&lt;span&gt; Examine the package structure under &lt;/span&gt;&lt;span&gt;`packages/`&lt;/span&gt;&lt;span&gt;, package.json dependencies, and main entry points&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;2.&lt;/span&gt;&lt;span&gt; Examine &lt;/span&gt;&lt;span&gt;`.github/workflows/`&lt;/span&gt;&lt;span&gt; to understand where and how each package runs&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;3.&lt;/span&gt;&lt;span&gt; Read all DRs under &lt;/span&gt;&lt;span&gt;`docs/decisions/`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;4.&lt;/span&gt;&lt;span&gt; Create or update ARCHITECTURE.md following the guidelines below&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Source Priority&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Follow &lt;/span&gt;&lt;span&gt;**codebase &amp;gt; DR &amp;gt; existing ARCHITECTURE.md**&lt;/span&gt;&lt;span&gt; priority. Code is the single source of truth — DRs record intent but implementation may lag behind, so trust sources in this order. If a DR is accepted but not yet reflected in code, describe the current code state. Only reflect DR content to the extent it is implemented in code.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Updating&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Broad but shallow coverage has no value — the user selects which items to focus on. Present proposed changes to the user and apply only after approval.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;After updating ARCHITECTURE.md, check whether &lt;/span&gt;&lt;span&gt;`CLAUDE.md`&lt;/span&gt;&lt;span&gt; Architecture Overview and Core Principles need updates, and propose them if so.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### What to Include&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Record the knowledge a human needs to understand this system — the design intent, the reasoning behind structural choices, and the mental model that makes the codebase navigable. A new developer reading ARCHITECTURE.md should come away understanding why the system is shaped this way, not just what exists.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Text-based overview diagram (execution environments and data flow)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Design principles and how they are concretized in each component&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Package collaboration structure and dependency rules centered on core types&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Where each package runs and how it connects to others&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Technology selection rationale (only when significant context exists, such as cost, constraints, or bug avoidance)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Prohibited&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Direct code (command examples, schema definitions, function signatures, etc.). Sample JSON is allowed (see &quot;Exceptions&quot; below)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Facts obvious from reading code (CLI flags, API endpoint lists, etc.) or enumerations of each package&apos;s implementation details&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Inventing names that don&apos;t exist in the codebase&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Describing historical changes (&quot;originally X, then changed to Y&quot;). Change history belongs in DRs — write only the current state and intent&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Meta-descriptions of the document&apos;s own purpose&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Operational details (schedule frequencies, CI/CD configuration values, etc.) — these change frequently and become stale&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Style&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Write declaratively, not in Q&amp;amp;A format. State the current state in each section with intent woven in naturally&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Keep it simple. Avoid verbose explanations&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Write for a human reader — use natural narrative flow, not bullet-point checklists optimized for machine parsing&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Examples&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;**Bad — stating what&apos;s obvious from code:**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;cli-fetch-rss is a package that fetches RSS feeds, accepts a group argument,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;loads a list of corresponding feed URLs, and fetches them in parallel.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;**Good — design intent not readable from code:**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Feed fetching and AI processing are separated into different packages so that&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;expensive AI processing can be skipped for quick debugging.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;**Bad — historical narrative:**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Originally called the Claude API directly, then migrated to Agent SDK.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;**Good — current intent only:**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;AI processing uses Agent SDK. By providing tools to agents, they can fetch&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;external information as needed to make decisions.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Format&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;When creating or significantly revising ARCHITECTURE.md, read &lt;/span&gt;&lt;span&gt;`references/architecture-template.md`&lt;/span&gt;&lt;span&gt; with the Read tool and follow its structure. The format is inspired by [&lt;/span&gt;&lt;span&gt;esbuild&apos;s architecture.md&lt;/span&gt;&lt;span&gt;](&lt;/span&gt;&lt;span&gt;https://github.com/evanw/esbuild/blob/main/docs/architecture.md&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; **Leading comment**&lt;/span&gt;&lt;span&gt;: Always include an HTML comment at the very top of the file (before the table of contents) stating that this document is for human readers and directing AI agents to also read the Decision Records under &lt;/span&gt;&lt;span&gt;`docs/decisions/`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ```html&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;!--&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    This document is written for human readers.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    If you are an AI agent, also read the Decision Records under docs/decisions/.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; **Table of contents**&lt;/span&gt;&lt;span&gt;: Place a nested bulleted list below the leading comment (before the title). Each item is an anchor link to a section heading&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; **Design principles**&lt;/span&gt;&lt;span&gt;: Use &lt;/span&gt;&lt;span&gt;`* **Principle Name**`&lt;/span&gt;&lt;span&gt; + indented paragraph (not &lt;/span&gt;&lt;span&gt;`**Name**: description`&lt;/span&gt;&lt;span&gt; inline format)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; **Section headings**&lt;/span&gt;&lt;span&gt;: Write in English&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; **Overview diagram**&lt;/span&gt;&lt;span&gt;: Text-based ASCII diagram. Do not use Japanese inside boxes (monospace fonts misalign). Do not include specific configuration values like cron frequencies&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Exceptions&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;The following are allowed as exceptions to the &quot;don&apos;t write details&quot; principle in Prohibited:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## User Prompt&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;$ARGUMENTS&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ある程度複合的なことをする Skill になっているので、細かいものにわけてもいいと思う。知識の共有がだるかったのとそんな大した量でもないので一緒にしてしまっている。&lt;/p&gt;
&lt;p&gt;一応英語に変換してもらっているが、ある程度はメンテすることを前提にしているので普段の会話で使っている言語でいいと思う。&lt;/p&gt;
&lt;h2&gt;ADR&lt;/h2&gt;
&lt;p&gt;なにかしらの方法で議論しコンテキストが満たされている状態で、&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;/doc-writer ここまでの議論の内容を ADR としてまとめて&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;とかで作っている、フォーマットは &lt;a href=&quot;https://cognitect.com/blog/2011/11/15/documenting-architecture-decisions&quot;&gt;Michael Nygard 氏の ADR&lt;/a&gt; に Notes 欄を足したものを使うことが多い。
ADR 自体は特に特殊なものでもなく、良い解説は世の中に溢れていると思うので割愛。&lt;/p&gt;
&lt;p&gt;事前の議論の密度にもよるが、 Context と Decision はできるだけ監修するというか、ほとんど自分で書くようにしている。&lt;/p&gt;
&lt;p&gt;ADR はすべての変更に対して作成しているわけではなく、技術要素の変更や人間がちゃんと確認して承認したいことを中心に書いている。意味が変わらない程度のちょっとした機能の改善、 UI の調整、リファクタリングやバグフィックスでは不要。&lt;/p&gt;
&lt;h2&gt;ARCHITECTURE.md&lt;/h2&gt;
&lt;p&gt;これは人間向けだから AI は ADR をちゃんと辿ってねと苦し紛れにコメントで書いている、多分大して意味はない。&lt;/p&gt;
&lt;p&gt;ADR の実装が終わった後なんかに、&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;/doc-writer ARCHITECTURE.md に @docs/decisions/2026-xx-xx の内容を反映して&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;みたいな感じで一旦下書きをしてもらい、気に入らなかったら何度か会話して自分好みのものにしている。
自分は esbuild の architecture.md の感じが好きなのでそれを目指して書いているが、細かいルールがあるわけではないので好きに書くのがいいと思う。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/evanw/esbuild/blob/main/docs/architecture.md&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;esbuild/docs/architecture.md at main · evanw/esbuild&lt;/h3&gt;&lt;p&gt;An extremely fast bundler for the web. Contribute to evanw/esbuild development by creating an account on GitHub.&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;また、すべての ADR を反映しているわけではなく、その必要もないと思っている。反映の基準としては以下のどれかに当てはまると思ったら書き加えたり消したりしている:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;人間がコードレビューをするときに、事前知識としてあったら嬉しい情報&lt;/li&gt;
&lt;li&gt;しばらく触らないと忘れそうだが ADR を辿るのはだるそうな、なにかしらの意図がある情報&lt;/li&gt;
&lt;li&gt;一度試したがあまりうまくいかなかったことと、その解決策&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;CLAUDE.md&lt;/h2&gt;
&lt;p&gt;Architecture Overview として概要を書くことが多くて、ARCHITECTURE.md からさらに厳選した以下のようなものを 2-3 行書いている。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;複数の環境で動くいくつかのパッケージがある場合は、その関係性とデータの流れ&lt;/li&gt;
&lt;li&gt;複数のパッケージでなにかしら統一したい方針があるならそれ&lt;/li&gt;
&lt;li&gt;キャッシュ使って無駄に API にリクエストしないでねみたいな勘所&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;他はよく言われている内容で、最小限に AI が知らない固有のことだけを書くように努力している。&lt;/p&gt;
&lt;h2&gt;README.md&lt;/h2&gt;
&lt;p&gt;人間向けの手順とか書く。AI 以前から特に変わってない。&lt;/p&gt;
&lt;h2&gt;周辺&lt;/h2&gt;
&lt;h3&gt;&lt;code&gt;/discuss&lt;/code&gt; Skill&lt;/h3&gt;
&lt;p&gt;以前は全体の流れに含んでいたが、独立して議論モードに入るための Skill として用意している。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;discuss&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Structured discussion to explore design approaches and reach decisions. Analyzes the codebase, researches best practices, and clarifies requirements.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;user-invocable&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;disable-model-invocation&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# Discuss&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;You are the user&apos;s peer engineer in a design discussion.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Your role is to help the user reach a well-informed decision — not to give answers, but to ask the right questions, surface relevant context, and lay out trade-offs so the user can decide with confidence.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Core Rule: Use AskUserQuestion for All Questions&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;When you need input from the user, always use the &lt;/span&gt;&lt;span&gt;**AskUserQuestion tool**&lt;/span&gt;&lt;span&gt; — never embed questions as plain text in your response. This keeps the discussion interactive and structured. Text output is for sharing analysis, context, trade-offs, and summaries — not for asking questions.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;If you have both analysis to share and a question to ask, share the analysis as text and then call AskUserQuestion in the same turn.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Purpose&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Clarify and organize ideas&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Identify requirements&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Explore design approaches&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Behavior&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Adaptive Discussion&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;During the conversation, use the following approaches as needed:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;1.&lt;/span&gt;&lt;span&gt; **Explore**&lt;/span&gt;&lt;span&gt; - When technical context is needed:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   -&lt;/span&gt;&lt;span&gt; Use &lt;/span&gt;&lt;span&gt;**Glob**&lt;/span&gt;&lt;span&gt; and &lt;/span&gt;&lt;span&gt;**Grep**&lt;/span&gt;&lt;span&gt; to find related code, existing patterns, and tech stack&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   -&lt;/span&gt;&lt;span&gt; Use &lt;/span&gt;&lt;span&gt;**Read**&lt;/span&gt;&lt;span&gt; to examine architectural decisions (ADRs, CLAUDE.md, README)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   -&lt;/span&gt;&lt;span&gt; Use the &lt;/span&gt;&lt;span&gt;**Explore agent**&lt;/span&gt;&lt;span&gt; for broad codebase investigation that requires multiple queries&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;2.&lt;/span&gt;&lt;span&gt; **Clarify**&lt;/span&gt;&lt;span&gt; - When requirements are ambiguous:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   -&lt;/span&gt;&lt;span&gt; Use &lt;/span&gt;&lt;span&gt;**AskUserQuestion**&lt;/span&gt;&lt;span&gt; with 2-4 concrete options per question&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   -&lt;/span&gt;&lt;span&gt; Cover key categories: Scope, Behavior, Data, Users, Integration, Constraints, Priority, Edge cases&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   -&lt;/span&gt;&lt;span&gt; Skip questions answerable from codebase analysis&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   -&lt;/span&gt;&lt;span&gt; Combine multiple related questions into a single AskUserQuestion call (up to 4 questions per call) rather than asking one at a time&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;3.&lt;/span&gt;&lt;span&gt; **Research**&lt;/span&gt;&lt;span&gt; - When external guidance would help:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   -&lt;/span&gt;&lt;span&gt; Use &lt;/span&gt;&lt;span&gt;**WebSearch**&lt;/span&gt;&lt;span&gt; to find best practices — cross-reference multiple sources to avoid relying on a single opinion or outdated information&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   -&lt;/span&gt;&lt;span&gt; Prioritize official documentation over blog posts and community answers&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   -&lt;/span&gt;&lt;span&gt; Present findings with applicability assessment for the user&apos;s specific context&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Key Principles&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Listen actively and provide constructive feedback&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Present options with pros/cons when decisions are needed&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Summarize key points periodically&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; When the user asks to &quot;clarify&quot;, &quot;dig deeper&quot;, or explore further, respond by using &lt;/span&gt;&lt;span&gt;**Clarify**&lt;/span&gt;&lt;span&gt; or &lt;/span&gt;&lt;span&gt;**Research**&lt;/span&gt;&lt;span&gt; to continue the discussion&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Do NOT create code or documentation unless explicitly requested&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Output&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;When the discussion reaches a natural conclusion, provide a summary using this template:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Discussion Summary&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Decisions&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [What was decided and why]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Open Questions&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [Unresolved items that need further investigation or input]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これは User レベルの Skill として用意していてリポジトリには依存しないものとしている。
大体は新しいセッションを起動してこの Skill ではじめる。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;/discuss foo をしたい、bar は buzz で bunbun&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;コードを書かないモードになっていて、都度 AskUserQuestion で必要な情報を聞いてくれる。さらに深堀りが必要そうなときは繰り返して満足行くところまでやっている。満足したら &lt;code&gt;/doc-writer&lt;/code&gt; に任せる。&lt;/p&gt;
&lt;p&gt;これは以下のものを参考にさせてもらっていて、影響を強く受けている:&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://x.com/trq212/status/2005315275026260309&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;JavaScript is not available.&lt;/h3&gt;&lt;span&gt;x.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/fumiya-kume/claude-code/blob/master/dig/commands/dig.md&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;claude-code/dig/commands/dig.md at master · fumiya-kume/claude-code&lt;/h3&gt;&lt;p&gt;Kuu&amp;amp;#39;s Claude Code utils for myself. Contribute to fumiya-kume/claude-code development by creating an account on GitHub.&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://gist.github.com/taichi/8419da7e2b20685db8d5f91f73fc3b1d&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;要件定義を行うための、Claude Code用カスタムスラッシュコマンド&lt;/h3&gt;&lt;p&gt;要件定義を行うための、Claude Code用カスタムスラッシュコマンド. GitHub Gist: instantly share code, notes, and snippets.&lt;/p&gt;&lt;span&gt;gist.github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3&gt;Claude Code on the web&lt;/h3&gt;
&lt;p&gt;ADR を作った時点で一度コンテキストが切れる(ことができる)ので、ローカルで ADR だけ作って実装は on the web でやってみたり、その逆をしたりしている。複数の worktree を監視するのは面倒だが、 on the web であれば結構気楽に複数タスクを投げれるようになってきたので便利。&lt;/p&gt;
&lt;p&gt;ただ、on the web で &lt;code&gt;/discuss&lt;/code&gt; で会話を始めるのがうまくいかなくて、Skill として登録なんかはしているのだが動いている感じがしない。なので on the web で ADR を作る場合は入力が増えがちかもしれない。&lt;/p&gt;
&lt;h3&gt;ADR の実装&lt;/h3&gt;
&lt;p&gt;ほぼほぼ Plan Mode に ADR を食わせるだけでやっている。
フェーズがいくつかある場合は、大まかなタスクリストを Markdown として作って、ADR と合わせて渡してフェーズごとに Plan Mode → 実装とすることもある。&lt;/p&gt;
&lt;h3&gt;GitHub Agentic Workflows&lt;/h3&gt;
&lt;p&gt;まだお試しレベルだが、ADR 以外のドキュメント類で間違っている部分を GHA で検出するのを試している。以下の記事に書いたものと同じ:&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://yaakai.to/note/145&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;GitHub Agentic Workflows を使ってみる - yaakai.to&lt;/h3&gt;&lt;p&gt;🚜 GitHub Agentic Workflows を使ってみる | yaakai.to&lt;/p&gt;&lt;span&gt;yaakai.to&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Validate ARCHITECTURE.md and CLAUDE.md for factual accuracy against the current codebase&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;on&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  schedule&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;weekly&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  workflow_dispatch&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;permissions&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  contents&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;read&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  issues&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;read&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  pull-requests&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;read&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;engine&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  id&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;copilot&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  model&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;gpt-5&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;tools&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  github&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    toolsets&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  edit&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;safe-outputs&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  github-token&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ secrets.AI_USER_GH_TOKEN }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  create-pull-request&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    max&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    labels&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;documentation&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;ai-automate:claude&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  add-labels&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    allowed&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;documentation&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;ai-automate:claude&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# Validate Documentation&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;You are an AI agent that validates ARCHITECTURE.md and CLAUDE.md for factual accuracy against the current codebase.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Scope&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Only check for &lt;/span&gt;&lt;span&gt;**incorrect**&lt;/span&gt;&lt;span&gt; items. Do NOT:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Add missing items or sections&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Remove items that are correct&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Suggest improvements or reorganization&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Change style or wording unless factually wrong&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;An item is &quot;incorrect&quot; when it describes something that contradicts the actual codebase — for example, a package name that doesn&apos;t exist, a wrong file path, a description that doesn&apos;t match what the code actually does, or a stated principle that the codebase explicitly violates.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Steps&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;1.&lt;/span&gt;&lt;span&gt; Read &lt;/span&gt;&lt;span&gt;`.claude/skills/doc-writer/SKILL.md`&lt;/span&gt;&lt;span&gt; to understand the documentation rules and structure&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;2.&lt;/span&gt;&lt;span&gt; Read &lt;/span&gt;&lt;span&gt;`.github/scan/validate-docs.md`&lt;/span&gt;&lt;span&gt; — this is the suppression list. Any documentation item listed there is an intentional decision and must be skipped entirely. Do NOT flag, modify, or mention suppressed items.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;3.&lt;/span&gt;&lt;span&gt; Examine the package structure under &lt;/span&gt;&lt;span&gt;`packages/`&lt;/span&gt;&lt;span&gt; (list directories, read package.json files and main entry points)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;4.&lt;/span&gt;&lt;span&gt; Examine &lt;/span&gt;&lt;span&gt;`.github/workflows/`&lt;/span&gt;&lt;span&gt; to understand where and how each package runs&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;5.&lt;/span&gt;&lt;span&gt; Read &lt;/span&gt;&lt;span&gt;`docs/ARCHITECTURE.md`&lt;/span&gt;&lt;span&gt; and validate every factual claim against the codebase&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;6.&lt;/span&gt;&lt;span&gt; Read &lt;/span&gt;&lt;span&gt;`CLAUDE.md`&lt;/span&gt;&lt;span&gt; and validate every factual claim against the codebase&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;7.&lt;/span&gt;&lt;span&gt; If no incorrect items are found, call &lt;/span&gt;&lt;span&gt;`noop`&lt;/span&gt;&lt;span&gt; with &quot;Documentation is accurate. No fixes needed.&quot; and stop&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;8.&lt;/span&gt;&lt;span&gt; If incorrect items are found:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   a. Fix only the incorrect items in ARCHITECTURE.md and/or CLAUDE.md using the edit tool&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   b. Use the &lt;/span&gt;&lt;span&gt;`create-pull-request`&lt;/span&gt;&lt;span&gt; safe output with:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      -&lt;/span&gt;&lt;span&gt; Branch name: &lt;/span&gt;&lt;span&gt;`chore/fix-docs-YYYYMMDD-RUNID`&lt;/span&gt;&lt;span&gt; (where YYYYMMDD is today&apos;s date and RUNID is the current GitHub Actions run ID)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      -&lt;/span&gt;&lt;span&gt; Commit message: &lt;/span&gt;&lt;span&gt;`docs: fix incorrect items in ARCHITECTURE.md / CLAUDE.md`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      -&lt;/span&gt;&lt;span&gt; Title: &lt;/span&gt;&lt;span&gt;`docs: fix incorrect items in project documentation`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      -&lt;/span&gt;&lt;span&gt; Body listing each incorrect item found and what was fixed&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Language Policy&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Write commit messages, PR title, and PR body in the same language as README.md.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Output&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; If no incorrect items are found (the expected common case), call &lt;/span&gt;&lt;span&gt;`noop`&lt;/span&gt;&lt;span&gt; and stop. Do NOT create a PR.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; If clear factual errors are found, use the &lt;/span&gt;&lt;span&gt;`create-pull-request`&lt;/span&gt;&lt;span&gt; safe output to submit a PR with the fixes. Never push directly to the branch.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;まだあまり運用はできていないが、ちょっとしたことでも割と見つけてくれる感じで便利。週 1 くらいで動かすようにしている。&lt;/p&gt;
&lt;h3&gt;雑感&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;リポジトリに SDD や AI 向けの PLAN を入れるのには今も否定的なので、人間向けも兼ねるこの形が気に入っている
&lt;ul&gt;
&lt;li&gt;AI 専用みたいなドキュメントはあんまいらなくて人間が嬉しければ AI も嬉しい気はする、今は&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;SDD は 5 回くらいチャレンジしては挫折した、これくらいのライトさがよい&lt;/li&gt;
&lt;li&gt;ADR は元々好きな考え方だったので盛り上がってきているのは嬉しい&lt;/li&gt;
&lt;li&gt;on the web で Skill が使いづらい&lt;/li&gt;
&lt;li&gt;Agentic Workflows よい&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>GitHub Agentic Workflows と通常の workflow を連携させたい</title><link>https://yaakai.to/note/146/</link><guid isPermaLink="true">https://yaakai.to/note/146/</guid><pubDate>Sun, 08 Mar 2026 07:46:52 GMT</pubDate><content:encoded>&lt;p&gt;Agentic Workflows で何かしらの処理をした後に、その成果物に対してさらに GHA を動かしたいようなケースがある。わかりやすいところで言えば、Agentic Workflows でコードを変更したらテストが動いてほしいが、デフォルトでは Bot になるので、push をトリガーにする GHA (や外部サービス) が起動しない。&lt;/p&gt;
&lt;p&gt;先にオチを書いておくと、2026/03/08 時点では用途にハマるものがなくて専用のユーザーの PAT を使い、通常のユーザーとして各種 Workflow などがトリガーされるようにした。&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;safe-outputs.dispatch-workflow&lt;/code&gt;&lt;/h2&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.github.com/gh-aw/reference/safe-outputs/#workflow-dispatch-dispatch-workflow&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Safe Outputs | GitHub Agentic Workflows&lt;/h3&gt;&lt;p&gt;Learn about safe output processing features that enable creating GitHub issues, comments, and pull requests without giving workflows write permissions.&lt;/p&gt;&lt;span&gt;github.github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;safe-outputs&lt;/code&gt; に次のようにすると、指定した workflow がトリガーできるようになる:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;safe-outputs&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  dispatch-workflow&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    workflows&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;test&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;lint&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    max&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;prompt から必要に合わせて呼び出す、&lt;code&gt;safe-outputs&lt;/code&gt; は許可リストで、指定したら必ず呼び出させるようなものではないので注意。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;Dispatch both `test` and `lint` workflows using the `dispatch-workflow` safe output,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;passing the PR branch name as `ref` input.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;ref&lt;/code&gt; としてブランチ名を渡しているが、 &lt;code&gt;create-pull-request&lt;/code&gt; の場合に safe-outputs の処理内で branch にランダムな suffix が追加されるため、この名前はそのまま使えずこれをヒントにブランチを特定したりする必要があった。&lt;/p&gt;
&lt;h2&gt;reusable workflow&lt;/h2&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.github.com/gh-aw/reference/safe-outputs/#global-configuration-options&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Safe Outputs | GitHub Agentic Workflows&lt;/h3&gt;&lt;p&gt;Learn about safe output processing features that enable creating GitHub issues, comments, and pull requests without giving workflows write permissions.&lt;/p&gt;&lt;span&gt;github.github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;workflow_on&lt;/code&gt; を使って reusable workflow として統合する形式で、今回やりたいことにはマッチしていそう。ドキュメントを読む限りでは &lt;code&gt;needs.[job].outputs&lt;/code&gt; から issue や pr の情報が取れそうなのだが、現状これが取れなくてワークアラウンド的に途中に特定する job を挟んでいる。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Orchestrator&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;on&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  push&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    branches&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;main&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;jobs&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  run-agent&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    uses&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;./.github/workflows/agent-task.lock.yml&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    secrets&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;inherit&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  find-pr&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    needs&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;run-agent&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    runs-on&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;ubuntu-latest&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    outputs&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      ref&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ steps.find.outputs.ref }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    steps&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      - &lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;find&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          ref=$(gh pr list --repo &quot;$GITHUB_REPOSITORY&quot; --label documentation --state open --json headRefName --jq &apos;.[0].headRefName // empty&apos;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          echo &quot;ref=$ref&quot; &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        env&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          GH_TOKEN&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ github.token }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  test&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    needs&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;find-pr&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;needs.find-pr.outputs.ref != &apos;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    uses&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;./.github/workflows/test.yml&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    with&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      ref&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ needs.find-pr.outputs.ref }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  lint&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    needs&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;find-pr&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;needs.find-pr.outputs.ref != &apos;&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    uses&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;./.github/workflows/lint.yml&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    with&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      ref&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ needs.find-pr.outputs.ref }}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Claude Code にコードを読んでもらった感じは以下の PR で対応が入っているが、出力される lock がそれを使うようになっていないんだとか、一旦できることがなさそうで修正待ちかという感じ。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/github/gh-aw/pull/18914&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;feat: expose safe-output results as workflow_call outputs by Copilot · Pull Request #18914 · github/gh-aw&lt;/h3&gt;&lt;p&gt;Workflows triggered via workflow_call had no way to surface results from safe-outputs (created issue numbers, PR URLs, commit SHAs, etc.) to callers. The compiled YAML never generated on.workflow_c...&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;この方法で branch に対してテストを走らせたりすることができるが、workflow が実行されるのはこの場合は main ブランチでなので、 commit や PR でテストを pass したことにはならない。&lt;/p&gt;
&lt;h2&gt;&lt;code&gt;safe-outputs.github-token&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;そもそも Bot にしないというアプローチで、ユーザーが作ることになるので元々設定されている workflow が全部そのまま動く。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;safe-outputs&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  github-token&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ secrets.USER_GH_TOKEN }}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;output 毎に個別に設定することもできるが、自分が試したときはうまくいかなくて全体に設定してしまった。CodeRabbit が Bot だとデフォルトでは動かないみたいな話もあって、プライベートリポジトリに関してだけいえばこれで一旦良いかなとなった。&lt;/p&gt;</content:encoded></item><item><title>GitHub Agentic Workflows を使ってみる</title><link>https://yaakai.to/note/145/</link><guid isPermaLink="true">https://yaakai.to/note/145/</guid><pubDate>Sun, 01 Mar 2026 20:26:37 GMT</pubDate><content:encoded>&lt;p&gt;GitHub Actions で AI を動作させる仕組みとして Agentic Workflows が追加されていて、これを試したメモ。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.blog/ai-and-ml/automate-repository-tasks-with-github-agentic-workflows/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Automate repository tasks with GitHub Agentic Workflows&lt;/h3&gt;&lt;p&gt;Build automations using coding agents in GitHub Actions to handle triage, documentation, code quality, and more.&lt;/p&gt;&lt;span&gt;github.blog&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://github.blog/jp/2026-02-16-automate-repository-tasks-with-github-agentic-workflows/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;GitHub Agentic Workflowsを発表 - リポジトリの自動化を実現&lt;/h3&gt;&lt;p&gt;itHub Agentic Workflowsがテクニカルプレビューとして登場。GitHub Actionsでコーディングエージェントを使用し、意図駆動型の自動化を構築しましょう。トリアージ、ドキュメント作成、コード品質向上など、さまざまなタスクを自動化できます。&lt;/p&gt;&lt;span&gt;github.blog&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;ざっくり言えば Markdown 形式でプロンプトを書き、Frontmatter に GHA の workflow ファイルに書くような設定を書くとそれをビルドして GHA で動作する &lt;code&gt;[workflow].yml&lt;/code&gt; にしてくれる。&lt;/p&gt;
&lt;p&gt;最近は変更点を ADR 形式で残す + 重要な点を ARCHITECURE.md や CLAUDE.md に反映するようなスタイルで開発をしていて、 main にマージした際に ARCHITRECURE.md への反映内容が間違っていないかを GHA で検証したかったので、そこに使ってみることにした。プロンプトはすでに Claude Code Action 向けに用意したものを使いまわしている、解説は割愛。&lt;/p&gt;
&lt;p&gt;Agentic Workflows 用の Markdown は &lt;a href=&quot;https://github.com/github/gh-aw/blob/main/create.md&quot;&gt;&lt;code&gt;https://github.com/github/gh-aw/blob/main/create.md&lt;/code&gt;&lt;/a&gt; を読むことで作ることができるようになる。これを読むようにしながら、必要なものを作ってもらう。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;https://github.com/github/gh-aw/blob/main/create.md を読んで、@.github/workflows/validate-docs.yml を Agentic Workflows に置き換えてください &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これで以下のようなファイルができる:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Validate ARCHITECTURE.md and CLAUDE.md for factual accuracy against the current codebase&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;on&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  push&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    branches&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;main&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;permissions&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  contents&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;read&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  issues&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;read&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  pull-requests&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;read&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;tools&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  github&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    toolsets&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;default&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  edit&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;safe-outputs&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  create-pull-request&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    max&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    labels&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;documentation&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  add-labels&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    allowed&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;documentation&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# Validate Documentation&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;You are an AI agent that validates ARCHITECTURE.md and CLAUDE.md for factual accuracy against the current codebase.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Scope&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Only check for &lt;/span&gt;&lt;span&gt;**incorrect**&lt;/span&gt;&lt;span&gt; items. Do NOT:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Add missing items or sections&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Remove items that are correct&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Suggest improvements or reorganization&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Change style or wording unless factually wrong&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;An item is &quot;incorrect&quot; when it describes something that contradicts the actual codebase — for example, a package name that doesn&apos;t exist, a wrong file path, a description that doesn&apos;t match what the code actually does, or a stated principle that the codebase explicitly violates.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Steps&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;1.&lt;/span&gt;&lt;span&gt; Read &lt;/span&gt;&lt;span&gt;`.claude/skills/doc-writer/SKILL.md`&lt;/span&gt;&lt;span&gt; to understand the documentation rules and structure&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;2.&lt;/span&gt;&lt;span&gt; Examine the package structure under &lt;/span&gt;&lt;span&gt;`packages/`&lt;/span&gt;&lt;span&gt; (list directories, read package.json files and main entry points)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;3.&lt;/span&gt;&lt;span&gt; Examine &lt;/span&gt;&lt;span&gt;`.github/workflows/`&lt;/span&gt;&lt;span&gt; to understand where and how each package runs&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;4.&lt;/span&gt;&lt;span&gt; Read &lt;/span&gt;&lt;span&gt;`ARCHITECTURE.md`&lt;/span&gt;&lt;span&gt; and validate every factual claim against the codebase&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;5.&lt;/span&gt;&lt;span&gt; Read &lt;/span&gt;&lt;span&gt;`CLAUDE.md`&lt;/span&gt;&lt;span&gt; and validate every factual claim against the codebase&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;6.&lt;/span&gt;&lt;span&gt; If no incorrect items are found, call the &lt;/span&gt;&lt;span&gt;`noop`&lt;/span&gt;&lt;span&gt; safe output with the message &quot;No incorrect items found.&quot; and stop&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;7.&lt;/span&gt;&lt;span&gt; If incorrect items are found:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   a. Fix only the incorrect items in ARCHITECTURE.md and/or CLAUDE.md using the edit tool&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   b. Use the &lt;/span&gt;&lt;span&gt;`create-pull-request`&lt;/span&gt;&lt;span&gt; safe output with:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      -&lt;/span&gt;&lt;span&gt; Branch name: &lt;/span&gt;&lt;span&gt;`chore/fix-docs-YYYYMMDD`&lt;/span&gt;&lt;span&gt; (where YYYYMMDD is today&apos;s date)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      -&lt;/span&gt;&lt;span&gt; Commit message: &lt;/span&gt;&lt;span&gt;`docs: fix incorrect items in ARCHITECTURE.md / CLAUDE.md`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      -&lt;/span&gt;&lt;span&gt; Title: &lt;/span&gt;&lt;span&gt;`docs: fix incorrect items in project documentation`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      -&lt;/span&gt;&lt;span&gt; Body listing each incorrect item found and what was fixed&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Language Policy&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Write commit messages, PR title, and PR body in English. Think in English.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Output&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; If changes were made, use the &lt;/span&gt;&lt;span&gt;`create-pull-request`&lt;/span&gt;&lt;span&gt; safe output to submit a PR with the fixes&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; If no incorrect items were found, use the &lt;/span&gt;&lt;span&gt;`noop`&lt;/span&gt;&lt;span&gt; safe output to confirm that documentation is accurate&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;本文部分が実際に AI にやってほしいことのプロンプトで、Frontmatter には &lt;code&gt;on:&lt;/code&gt; のような GHA でおなじみのものに追加して、この Workflow で Agent が生成できる成果物を &lt;code&gt;safe-outputs&lt;/code&gt; として指定できる。ここで書き込めるものを指定しておくことで、安全に動かそうという仕組みのようだ。&lt;/p&gt;
&lt;p&gt;Markdown を作ったら &lt;code&gt;gh aw&lt;/code&gt; コマンドでビルドする。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$ gh extension install github/gh-aw&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;$ gh aw compile --strict [workflow-name]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;無事ビルドができると &lt;code&gt;workflow-name.lock.yml&lt;/code&gt; が作成される。これが実際に動作する workflow で、中身は見慣れた GHA の workflow ファイルになっている。&lt;/p&gt;
&lt;p&gt;全体の流れを大雑把に言えば、 &lt;code&gt;engine: copilot&lt;/code&gt; (デフォルト) の場合、 MCP やトークンの準備などを行い、 Markdown に書かれていたプロンプトとツール定義を合わて埋め込んだプロンプトを copilot-cli に渡して実行している。他には Frontmatter に設定した内容に沿って output の制御なんかをしていそうだが、詳しくは追っていない。&lt;/p&gt;
&lt;p&gt;Copilot で動かすには PAT が必要で、engine を切り替えた場合はそれぞれ対応する API キーが必要。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.github.com/gh-aw/reference/auth/#copilot_github_token&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Authentication | GitHub Agentic Workflows&lt;/h3&gt;&lt;p&gt;Comprehensive reference for GitHub Actions secrets, GitHub tokens and GitHub Apps in gh-aw&lt;/p&gt;&lt;span&gt;github.github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$ gh aw secrets set COPILOT_GITHUB_TOKEN --value &quot;token&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これで Workflow ファイルを push したら動いた。ちゃんと間違いを訂正する PR を作ってくれたので、しばらくはこれを使ってみようと思う。&lt;/p&gt;
&lt;p&gt;Claude Code Action を GHA で動かすのとできることが大きく変わるわけではないが、あちらは一部の event をサポートしていないので、Agent SDK を使って構築するのもだるいような場合にはこちらが明確に有利か、というのがいまのところの感想。&lt;/p&gt;
&lt;h2&gt;Refs&lt;/h2&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.github.io/gh-aw/setup/creating-workflows/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Creating Agentic Workflows | GitHub Agentic Workflows&lt;/h3&gt;&lt;span&gt;github.github.io&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</content:encoded></item><item><title>Custom Slash Command を Skills に変換する</title><link>https://yaakai.to/note/144/</link><guid isPermaLink="true">https://yaakai.to/note/144/</guid><pubDate>Fri, 27 Feb 2026 08:03:42 GMT</pubDate><content:encoded>&lt;p&gt;少なくとも Claude Code では 2 つは統合されて、同じ扱いで呼び出すことができるようになっている。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://code.claude.com/docs/en/slash-commands#custom-slash-commands&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Extend Claude with skills - Claude Code Docs&lt;/h3&gt;&lt;p&gt;Create, manage, and share skills to extend Claude&amp;amp;#x27;s capabilities in Claude Code. Includes custom commands and bundled skills.&lt;/p&gt;&lt;span&gt;code.claude.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Skill に寄せておくと他の Agent から使いたい場合に便利そうなので、必要なものを変換したい。
Custom Slash Command に互換のある、「ユーザーが呼び出すことができ、Agent が自動的に使用しない」という状態にするには、 Frontmatter で &lt;code&gt;disable-model-invocation: true&lt;/code&gt; を指定する。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;allowed-tools&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Bash(git push), Bash(git diff), Bash(git log), Bash(gh pr create), Bash(which gh)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Create a pull request with structured format&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;user-invocable&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;disable-model-invocation&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Create a pull request following the structured format. Analyze the current branch changes and generate appropriate content.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;....&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;user-invocable: true&lt;/code&gt; はデフォルト値なので指定する必要がないが、自分は明示的に書いてしまっている。これらのフラグを逆にすると「Agent が自動的に使用することだけ許可」された Skill を作ることができる、そちらも用途はありそう。&lt;/p&gt;</content:encoded></item><item><title>Claude Code on the web / on desktop 向けの設定をした</title><link>https://yaakai.to/blog/2026/setup-claude-code-on-the-web-desktop-worktree/</link><guid isPermaLink="true">https://yaakai.to/blog/2026/setup-claude-code-on-the-web-desktop-worktree/</guid><pubDate>Sat, 21 Feb 2026 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;ここ最近 Claude Code on the web を意識的に使うようにしていたら on desktop と worktree まわりにもアップデートが来たので、備忘録的に設定や使い方をまとめておく。
これを触ったり書いているのが 2026/02 の 1-3 週目あたりで、ちょうど &lt;code&gt;claude -w&lt;/code&gt; と Previews が入ったタイミングなので情報はすぐに古くなるかもしれない。
まったく使ったことのない人向けの内容ではないので、どういうものかだったり基本的な使い方は公式を参照。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://code.claude.com/docs/ja/claude-code-on-the-web&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;ウェブ上の Claude Code - Claude Code Docs&lt;/h3&gt;&lt;p&gt;セキュアなクラウドインフラストラクチャで Claude Code タスクを非同期に実行します&lt;/p&gt;&lt;span&gt;code.claude.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://code.claude.com/docs/ja/desktop&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Claude Code Desktop を使用する - Claude Code Docs&lt;/h3&gt;&lt;p&gt;Claude Code Desktop をさらに活用する：Git 分離による並列セッション、ビジュアル diff レビュー、アプリプレビュー、PR 監視、権限モード、コネクタ、エンタープライズ設定。&lt;/p&gt;&lt;span&gt;code.claude.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;前提として、専用になにかしらの設定をしなくともある程度は on the web が動作し、調査タスクや bugfix 程度であれば十分にワークしている。ここに少し設定を加えて便利になればよいなという温度感。
また、 on the web と on desktop は同じ UI から利用できるが、中身は別物で有効な設定も違ったりするので注意。区別するために on the web と on desktop と書いている。&lt;/p&gt;
&lt;h2&gt;連携する GitHub アカウントを分ける&lt;/h2&gt;
&lt;p&gt;on the web の場合に、メインで利用しているものの中には Claude Code に触ってほしくないものが含まれている(可能性がある)のと、単純に UI に無数のリポジトリが表示されると扱いづらいので、専用のユーザーを作って必要なものだけアクセス権を与えて使うようにしている。
自分の場合は&lt;a href=&quot;https://yaakai.to/blog/2025/issue-driven-workflow-with-claude-and-coderabbit&quot;&gt;前回の Issue 経由での開発フロー&lt;/a&gt;で使っているユーザーが用途にマッチするのでそれをそのまま使っている。ユーザーの切り替えは単に Claude と連携している GitHub アカウントを切り替えれば OK。&lt;/p&gt;
&lt;h2&gt;DevContainer に ssh する&lt;/h2&gt;
&lt;p&gt;基本的には OrbStack + DevContainer で開発しているので、 on desktop の ssh 接続を使いたい。
DevContainer は &lt;code&gt;ghcr.io/devcontainers/features/sshd&lt;/code&gt; を使うことで sshd を立てることができるのでこれを利用する。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/devcontainers/features/pkgs/container/features%2Fsshd&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Build software better, together&lt;/h3&gt;&lt;p&gt;GitHub is where people build software. More than 150 million people use GitHub to discover, fork, and contribute to over 420 million projects.&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;必要なところだけ抜粋すると次のように devcontainer.json を設定している:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;postCreateCommand&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;.devcontainer/setup.sh&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;features&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;ghcr.io/devcontainers/features/sshd:1&quot;&lt;/span&gt;&lt;span&gt;: {}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;mounts&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;source=${localEnv:HOME}/.ssh/id_rsa.pub,target=/home/vscode/.ssh-host-authorized_keys,type=bind,readonly&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;setup.sh:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;# SSH authorized_keys setup (skip on Codespaces)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;-z&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;$CODESPACES&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ] &amp;amp;&amp;amp; [ &lt;/span&gt;&lt;span&gt;-s&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;$HOME&lt;/span&gt;&lt;span&gt;/.ssh-host-authorized_keys&quot;&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    mkdir&lt;/span&gt;&lt;span&gt; -p&lt;/span&gt;&lt;span&gt; ~/.ssh&lt;/span&gt;&lt;span&gt; &amp;amp;&amp;amp; &lt;/span&gt;&lt;span&gt;chmod&lt;/span&gt;&lt;span&gt; 700&lt;/span&gt;&lt;span&gt; ~/.ssh&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    cp&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;$HOME&lt;/span&gt;&lt;span&gt;/.ssh-host-authorized_keys&quot;&lt;/span&gt;&lt;span&gt; ~/.ssh/authorized_keys&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    chmod&lt;/span&gt;&lt;span&gt; 600&lt;/span&gt;&lt;span&gt; ~/.ssh/authorized_keys&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;このように設定すると、 OrbStack を使っている場合はコンテナ名を調べて以下のコマンドで ssh できるようになる:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; ssh&lt;/span&gt;&lt;span&gt; -p&lt;/span&gt;&lt;span&gt; 2222&lt;/span&gt;&lt;span&gt; vscode@condescending_dewdney.orb.local&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;あとはこれを on desktop で ssh 接続先として登録すれば使えるようになる。&lt;/p&gt;
&lt;p&gt;ただし、この設定自体が Claude Code on desktop を使いたいという個人的なニーズに紐づいているので、プライベートリポジトリだけの運用になりそう。
後述する Previews もこれを書いている時点では ssh には対応していないこともあり、使用頻度は低くなりそう。&lt;/p&gt;
&lt;h2&gt;SessionStart で依存の install&lt;/h2&gt;
&lt;p&gt;on the web でのセットアップ手順として &lt;code&gt;SessionStart&lt;/code&gt; hooks を使った方法が公式に紹介されているが、これを on desktop と &lt;code&gt;claude -w&lt;/code&gt; でも使えるようにしたい。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://code.claude.com/docs/ja/claude-code-on-the-web#%E4%BE%9D%E5%AD%98%E9%96%A2%E4%BF%82%E7%AE%A1%E7%90%86&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;ウェブ上の Claude Code - Claude Code Docs&lt;/h3&gt;&lt;p&gt;セキュアなクラウドインフラストラクチャで Claude Code タスクを非同期に実行します&lt;/p&gt;&lt;span&gt;code.claude.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;hooks&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;SessionStart&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;matcher&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;startup&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;hooks&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &quot;type&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;command&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &quot;command&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;$CLAUDE_PROJECT_DIR&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;/.claude/session-start.sh&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;session-start.sh で環境に合わせて必要なスクリプトを動かすようにしている:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;#!/bin/bash&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# Claude Code on the Web environment&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$CLAUDE_CODE_REMOTE&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; &quot;true&quot;&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  bun&lt;/span&gt;&lt;span&gt; install&lt;/span&gt;&lt;span&gt; --no-save&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# Inside a worktree&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; [[ &lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$PWD&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ==&lt;/span&gt;&lt;span&gt; *&lt;/span&gt;&lt;span&gt;/.claude/worktrees/&lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; ]]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  # Copy files listed in .worktreeinclude from the main working tree&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  main_tree&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;$(&lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; worktree list &lt;/span&gt;&lt;span&gt;--porcelain&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt; head&lt;/span&gt;&lt;span&gt; -1&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt; sed&lt;/span&gt;&lt;span&gt; &apos;s/^worktree //&apos;)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  include_file&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$main_tree&lt;/span&gt;&lt;span&gt;/.worktreeinclude&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  if&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;-f&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;$include_file&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    while&lt;/span&gt;&lt;span&gt; IFS&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; read&lt;/span&gt;&lt;span&gt; -r&lt;/span&gt;&lt;span&gt; entry&lt;/span&gt;&lt;span&gt; ||&lt;/span&gt;&lt;span&gt; [ &lt;/span&gt;&lt;span&gt;-n&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;$entry&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ]; &lt;/span&gt;&lt;span&gt;do&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      [ &lt;/span&gt;&lt;span&gt;-z&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;$entry&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ] &amp;amp;&amp;amp; &lt;/span&gt;&lt;span&gt;continue&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      src&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt;$main_tree&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;$entry&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      [ &lt;/span&gt;&lt;span&gt;-e&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;$src&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; ] &amp;amp;&amp;amp; &lt;/span&gt;&lt;span&gt;cp&lt;/span&gt;&lt;span&gt; -a&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;$src&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;$PWD&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;$entry&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    done&lt;/span&gt;&lt;span&gt; &amp;lt;&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;$include_file&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  fi&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  bun&lt;/span&gt;&lt;span&gt; install&lt;/span&gt;&lt;span&gt; --no-save&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;fi&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;.claude/worktrees&lt;/code&gt; 以下で実行されているなら on desktop か &lt;code&gt;claude -w&lt;/code&gt; で動いているだろうという判定をしている。worktree 内で claude を起動し直すと少し無駄な感じになるが気になることはなさそう。&lt;/p&gt;
&lt;p&gt;worktree の場合、後述する &lt;code&gt;.worktreeinclude&lt;/code&gt; というファイルを読んで、 git 管理下にないファイルをコピーするようにしている。
claude 自体が持っている &lt;code&gt;.worktreeinclude&lt;/code&gt; が &lt;code&gt;claude -w&lt;/code&gt; の場合には動作しないのでその workaround。&lt;/p&gt;
&lt;p&gt;on the web でたまに SessionStart が終わらないことがあり、おそらく &lt;code&gt;bun install&lt;/code&gt; でネットワークに接続できていない(ログが見れないためよくわからない…)。
クラウド環境の設定からネットワークアクセスをポチポチいじるとなおったりする。謎。&lt;/p&gt;
&lt;h2&gt;.worktreeinclude&lt;/h2&gt;
&lt;div&gt;&lt;a href=&quot;https://code.claude.com/docs/ja/desktop#gitignore-%E3%81%A7%E7%84%A1%E8%A6%96%E3%81%95%E3%82%8C%E3%81%9F%E3%83%95%E3%82%A1%E3%82%A4%E3%83%AB%E3%81%AE%E3%82%B3%E3%83%94%E3%83%BC&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Claude Code Desktop を使用する - Claude Code Docs&lt;/h3&gt;&lt;p&gt;Claude Code Desktop をさらに活用する：Git 分離による並列セッション、ビジュアル diff レビュー、アプリプレビュー、PR 監視、権限モード、コネクタ、エンタープライズ設定。&lt;/p&gt;&lt;span&gt;code.claude.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;昨今の流れ的にそれ自体がどうという話はあるが、 &lt;code&gt;.env&lt;/code&gt; なんかを worktree にコピーしたいときに使える。元ファイルが必要なので on desktop 用で on the web の場合は代わりに環境変数を定義できる。
&lt;code&gt;claude -w&lt;/code&gt; が出て、そちらはこのファイルには対応していないので、自分は前述のようにしている。&lt;/p&gt;
&lt;p&gt;特に難しいことはなくて、&lt;code&gt;.gitignore&lt;/code&gt; に入っているが worktree でも必要なファイルを同じフォーマットで列挙すればよい:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.env&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;PR のフォーマットを指定する&lt;/h2&gt;
&lt;p&gt;on the web では最終的に PR にして完結させることが多いのだが、 UI にある PR ボタンを押した場合に、これを書いている時点では CLAUDE.md を読んでいないように見える。
一般的な PR のテンプレート + 英語で作られるのだが、これをカスタマイズしたくてもこれを書いている時点では方法がない。&lt;/p&gt;
&lt;p&gt;幸いリポジトリにある Skills などは問題なく動作するので、PR を作るための Skills を user レベルから repository に移すことにした。
そのままだと gh を使おうとするが on the web ではデフォルトでは gh が利用できず、あえて用意されていないようにも思うのでリンクを作らせるようにした。&lt;/p&gt;
&lt;p&gt;大した内容ではないが、Skill の手順の最後をこんな感じにしている:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;Check if &lt;/span&gt;&lt;span&gt;`gh`&lt;/span&gt;&lt;span&gt; CLI is available by running &lt;/span&gt;&lt;span&gt;`which gh`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  -&lt;/span&gt;&lt;span&gt; **If available**&lt;/span&gt;&lt;span&gt;: Create the PR using &lt;/span&gt;&lt;span&gt;`gh pr create`&lt;/span&gt;&lt;span&gt; with the formatted body based on the template&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  -&lt;/span&gt;&lt;span&gt; **If not available**&lt;/span&gt;&lt;span&gt;: Output a GitHub PR creation URL: &lt;/span&gt;&lt;span&gt;`https://github.com/{owner}/{repo}/compare/main...{branch}?expand=1&amp;amp;title={url-encoded-title}&amp;amp;body={url-encoded-body}`&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ただ、これはこれで PR を作成するのが今ブラウザでログインしているアカウントになってしまうため、分けている意味が少し薄れてしまうのがマイナス。そのうち解決しそうな話ではある。&lt;/p&gt;
&lt;h3&gt;2026/03/09 追記&lt;/h3&gt;
&lt;p&gt;デフォルトでも curl は動作するので、環境変数として PAT を設定して、フォールバックとして curl でのリクエストをするようにすると Skill 経由でうまくPR を作れるようになっているように思える。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;#### Fallback: when &lt;/span&gt;&lt;span&gt;`gh`&lt;/span&gt;&lt;span&gt; fails&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;If &lt;/span&gt;&lt;span&gt;`gh pr create`&lt;/span&gt;&lt;span&gt; fails for any reason (not installed, auth error, API rate limit, etc.), fall back to creating the PR via &lt;/span&gt;&lt;span&gt;`curl`&lt;/span&gt;&lt;span&gt; using the GitHub REST API.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```bash&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# Get repo info from remote URL&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;REMOTE_URL&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;git&lt;/span&gt;&lt;span&gt; remote&lt;/span&gt;&lt;span&gt; get-url&lt;/span&gt;&lt;span&gt; origin&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# Extract owner/repo (handles both HTTPS and SSH formats)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;OWNER_REPO&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;$(&lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &quot;&lt;/span&gt;&lt;span&gt;$REMOTE_URL&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt; sed&lt;/span&gt;&lt;span&gt; -E&lt;/span&gt;&lt;span&gt; &apos;s#(https://github\.com/|git@github\.com:)##; s/\.git$//&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;curl&lt;/span&gt;&lt;span&gt; -s&lt;/span&gt;&lt;span&gt; -X&lt;/span&gt;&lt;span&gt; POST&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  -H &quot;Authorization: token $GITHUB_TOKEN&quot; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  -H&lt;/span&gt;&lt;span&gt; &quot;Accept: application/vnd.github+v3+json&quot;&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;https://api.github.com/repos/$OWNER_REPO/pulls&quot; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  -d&lt;/span&gt;&lt;span&gt; &quot;$(&lt;/span&gt;&lt;span&gt;cat&lt;/span&gt;&lt;span&gt; &amp;lt;&amp;lt;&lt;/span&gt;&lt;span&gt;&apos;EOF&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;title&quot;: &quot;...&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;body&quot;: &quot;...&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;head&quot;: &quot;&amp;lt;branch&amp;gt;&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;base&quot;: &quot;&amp;lt;base-branch&amp;gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;EOF&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Use&lt;/span&gt;&lt;span&gt; `&lt;/span&gt;&lt;span&gt;$GITHUB_TOKEN&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt; or&lt;/span&gt;&lt;span&gt; `&lt;/span&gt;&lt;span&gt;$GH_TOKEN&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt; environment&lt;/span&gt;&lt;span&gt; variable&lt;/span&gt;&lt;span&gt; for&lt;/span&gt;&lt;span&gt; authentication&lt;/span&gt;&lt;span&gt; (check &lt;/span&gt;&lt;span&gt;both,&lt;/span&gt;&lt;span&gt; prefer&lt;/span&gt;&lt;span&gt; `&lt;/span&gt;&lt;span&gt;GITHUB_TOKEN&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Escape&lt;/span&gt;&lt;span&gt; JSON&lt;/span&gt;&lt;span&gt; special&lt;/span&gt;&lt;span&gt; characters&lt;/span&gt;&lt;span&gt; in&lt;/span&gt;&lt;span&gt; the&lt;/span&gt;&lt;span&gt; title&lt;/span&gt;&lt;span&gt; and&lt;/span&gt;&lt;span&gt; body&lt;/span&gt;&lt;span&gt; (double &lt;/span&gt;&lt;span&gt;quotes,&lt;/span&gt;&lt;span&gt; backslashes,&lt;/span&gt;&lt;span&gt; newlines&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Extract&lt;/span&gt;&lt;span&gt; the&lt;/span&gt;&lt;span&gt; PR&lt;/span&gt;&lt;span&gt; URL&lt;/span&gt;&lt;span&gt; from&lt;/span&gt;&lt;span&gt; the&lt;/span&gt;&lt;span&gt; response&apos;s `html_url` field&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Previews for Claude Code&lt;/h2&gt;
&lt;p&gt;今のところ対応しているのが on desktop の Local だけなのでまだ使っていない。そのうち ssh や on the web でも対応してくれると嬉しいのだが…。&lt;/p&gt;
&lt;h2&gt;今の運用と雑感&lt;/h2&gt;
&lt;p&gt;個人的なリポジトリは on the web で十分に開発ができる + PR を出せば Preview URLs で動作確認が完結することもあってかなり使いやすい。
Issue ベースでの開発フローも整備してはいるので、それとの兼ね合いが少し曖昧になっているが、なんとなく以下の感じでやっている:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;メインの開発はこれまで通り CLI&lt;/li&gt;
&lt;li&gt;新しいことを試してみたり、調査タスクは on the web&lt;/li&gt;
&lt;li&gt;バグレポートへの対応や、Issue として書いて残せるもの、一旦 Issue にしておいてあとで考えたいものは Issue ベース&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;CLI 以外は都度レスポンスすることがほとんどなくて、ポイポイと投げておいてあとで暇なときにまとめて確認している。
自分は並列にいくつも Agent を動かすみたいなのが苦手であまりやっていなかったが、この運用だと少しできているのかなと思う。&lt;/p&gt;
&lt;p&gt;on desktop の ssh と &lt;code&gt;claude -w&lt;/code&gt; はこれまで自作の worktree 管理をしていたが、そっちは廃止して基本 claude に乗ることにした。&lt;/p&gt;
&lt;p&gt;一方で on desktop の local がちょっと難しくて、これは単純に次のようなペインポイントがあって扱いに困ることがあるため:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;on desktop で worktree を作るとき、 &lt;code&gt;.worktreeinclude&lt;/code&gt; との兼ね合いなのかブランチを指定しても main worktree の状態から worktree が作られているように思う&lt;/li&gt;
&lt;li&gt;diff 表示も少しおかしくて、常に main worktree が優先されている&lt;/li&gt;
&lt;li&gt;Claude Code Desktop の Claude Code が &lt;code&gt;NODE_EXTRA_CA_CERTS&lt;/code&gt; を見ていなくて、それ起因で環境によってはそもそも使えないことがある(&lt;a href=&quot;https://github.com/anthropics/claude-code/issues/22559&quot;&gt;Issue&lt;/a&gt;)&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;自分の場合は on desktop を使いたい環境で、現状では main worktree を綺麗に保つことが結構難しくて(例えば特定のパスに依存して動作するようなものがある環境)、 branch や diff がめちゃくちゃになってしまうし、そもそも Claude Code が動かなかったりする。
Codex App だとこのあたりがうまく動いていて、今はそっちを使っている。&lt;/p&gt;
&lt;p&gt;ただ、Previews を使いこなせればフロントエンド開発にはかなり有効に思えるのでうまく使いたいとは思っている。&lt;/p&gt;
&lt;h3&gt;その他&lt;/h3&gt;
&lt;ul&gt;
&lt;li&gt;on the web は常に push しているっぽいので、 GHA とか設定次第では結構使うかも&lt;/li&gt;
&lt;li&gt;rewind ができないので困ることがある&lt;/li&gt;
&lt;li&gt;AI Agent 全体的な話になってしまうが、 DevContainer のサポートが弱いのがぐぬぬという感じ、相性自体はいいはずなのだが…&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>claude-agent-sdk の output_format を zod で指定して任意の出力形式にする</title><link>https://yaakai.to/note/141/</link><guid isPermaLink="true">https://yaakai.to/note/141/</guid><pubDate>Tue, 03 Feb 2026 17:58:31 GMT</pubDate><content:encoded>&lt;p&gt;&lt;code&gt;@anthropic-ai/claude-agent-sdk&lt;/code&gt; を利用していて、その出力形式を自然言語形式ではなく特定の型の JSON にしたいケースがある。その場合 &lt;code&gt;outputFormat&lt;/code&gt; で JSONSchema を指定することで任意の形式の出力を得ることができる。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;query&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  prompt: &lt;/span&gt;&lt;span&gt;&apos;...&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  options: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    outputFormat: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      type: &lt;/span&gt;&lt;span&gt;&apos;json_schema&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      schema&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これと zod 4x 以降でサポートされた &lt;code&gt;toJSONSchema&lt;/code&gt; を組み合わせると、 zod で定義した型の JSON を出力として指定することが可能となる。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://platform.claude.com/docs/en/agent-sdk/structured-outputs&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Get structured output from agents&lt;/h3&gt;&lt;p&gt;Return validated JSON from agent workflows using JSON Schema, Zod, or Pydantic. Get type-safe, structured data after multi-turn tool use.&lt;/p&gt;&lt;span&gt;platform.claude.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://zod.dev/json-schema&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;JSON Schema | Zod&lt;/h3&gt;&lt;p&gt;How to convert Zod schemas to JSON Schema&lt;/p&gt;&lt;span&gt;zod.dev&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;しかし、 2026/02/04 時点では公式のドキュメント通りでは指定がうまく動作せず、自然言語で出力されてしまう。これは zod が吐き出す  &lt;code&gt;$schema&lt;/code&gt; フィールドが &lt;code&gt;draft-2020-12&lt;/code&gt; になっていることが原因のようで、&lt;code&gt;draft-07&lt;/code&gt; を指定することで動作するらしい。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/anthropics/claude-agent-sdk-typescript/issues/105&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Structured output ignored · Issue #105 · anthropics/claude-agent-sdk-typescript&lt;/h3&gt;&lt;p&gt;Hi, if I try to use the structured output option, the LLM completely ignores it and outputs generic text. Could someone help me understand if there&amp;amp;#39;s a bug in the SDK? I&amp;amp;#39;m following the example in ...&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;つまり現状では以下のようなコードで期待した &lt;code&gt;structured_output&lt;/code&gt; を得ることができる:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;query&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  prompt: &lt;/span&gt;&lt;span&gt;&apos;...&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  options: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    outputFormat: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      type: &lt;/span&gt;&lt;span&gt;&apos;json_schema&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      schema: z.&lt;/span&gt;&lt;span&gt;toJSONSchema&lt;/span&gt;&lt;span&gt;(schema, { target: &lt;/span&gt;&lt;span&gt;&apos;draft-07&apos;&lt;/span&gt;&lt;span&gt; })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content:encoded></item><item><title>code-simplifier が便利</title><link>https://yaakai.to/note/140/</link><guid isPermaLink="true">https://yaakai.to/note/140/</guid><pubDate>Thu, 22 Jan 2026 12:44:13 GMT</pubDate><content:encoded>&lt;p&gt;Claude の公式のプラグイン集である claude-plugins-official で &lt;code&gt;code-simplifier&lt;/code&gt; という subagent が提供されていてこれが結構便利。名前の通り、書いたコードをシンプルにしてくれる。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/anthropics/claude-plugins-official/blob/main/plugins/code-simplifier/agents/code-simplifier.md&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;claude-plugins-official/plugins/code-simplifier/agents/code-simplifier.md at main · anthropics/claude-plugins-official&lt;/h3&gt;&lt;p&gt;Official, Anthropic-managed directory of high quality Claude Code Plugins. - anthropics/claude-plugins-official&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://x.com/bcherny/status/2009450715081789767&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;JavaScript is not available.&lt;/h3&gt;&lt;span&gt;x.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;自分はこれを Custom Slash Command で呼び出して、変更したらテストを実行するようにして使っている。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Simplify and refactor code while ensuring tests pass&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;allowed-tools&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Task(code-simplifier:code-simplifier)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Simplify and refactor code using the code-simplifier subagent, then verify behavior is unchanged by running tests.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Instructions&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;1.&lt;/span&gt;&lt;span&gt; Use the &lt;/span&gt;&lt;span&gt;`code-simplifier:code-simplifier`&lt;/span&gt;&lt;span&gt; subagent to perform the refactoring&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   -&lt;/span&gt;&lt;span&gt; Focus on clarity, consistency, and maintainability&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   -&lt;/span&gt;&lt;span&gt; Preserve all existing functionality&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;2.&lt;/span&gt;&lt;span&gt; After refactoring, run the project&apos;s test suite to verify behavior is unchanged&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;3.&lt;/span&gt;&lt;span&gt; If tests fail, investigate and fix any regressions introduced by the refactoring&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;4.&lt;/span&gt;&lt;span&gt; Report the changes made and test results&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Additional Context&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;$ARGUMENTS&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;gt; /code-simplifier&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;gt; /code-simplifier @src/index.ts をリファクタリングして&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;CLAUDE.md に毎回実行してと書いておくのもよさそうだが、そこそこ実行コストが高いように感じているので、今のところは明示的に呼び出すようにしている。&lt;/p&gt;</content:encoded></item><item><title>Claude Code で respectGitignore を設定して個人用ファイルを @ で補完できるようにする</title><link>https://yaakai.to/note/134/</link><guid isPermaLink="true">https://yaakai.to/note/134/</guid><pubDate>Wed, 14 Jan 2026 21:03:24 GMT</pubDate><content:encoded>&lt;p&gt;Claude Code では &lt;code&gt;@src/index.ts&lt;/code&gt; のようなファイル参照はこれまで gitignore を尊重する動作になっていたが、これが 2.1.0 で OFF にできるようになった。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md#210&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;claude-code/CHANGELOG.md at main · anthropics/claude-code&lt;/h3&gt;&lt;p&gt;Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflo...&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;respectGitignore&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これで何が嬉しいかというと、例えば個人用のメモや Git 管理していない PLAN.md のようなファイルを Claude Code に渡したい場合に、それが gitignore されていると補完が効かないという問題があった。これは Issue にもなっている。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/anthropics/claude-code/issues/2305&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;[FEATURE REQUEST] Allow Claude Code to access gitignored files · Issue #2305 · anthropics/claude-code&lt;/h3&gt;&lt;p&gt;Current Behavior Claude Code currently respects .gitignore files and excludes gitignored files from: @-mention autocomplete suggestions General file awareness and reading (unless explicitly request...&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/anthropics/claude-code/issues/5105&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;[FEATURE REQUEST] Allow Claude Code to access gitignored files · Issue #5105 · anthropics/claude-code&lt;/h3&gt;&lt;p&gt;Current Behavior Claude Code currently respects .gitignore files and excludes gitignored files from: @-mention autocomplete suggestions General file awareness and reading (unless explicitly request...&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;自分の場合はオレオレ実装計画を Git 管理しない形で作って使っているので、&lt;code&gt;false&lt;/code&gt; にできると嬉しい。ただし、&lt;code&gt;node_modules&lt;/code&gt; なんかも対象になってしまうことに注意。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://yaakai.to/blog/2025/my-spec-driven-dev-plan&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;仕様駆動開発風のオレオレ実装計画を作って AI と併走する - yaakai.to&lt;/h3&gt;&lt;p&gt;🐧 仕様駆動開発風のオレオレ実装計画を作って AI と併走する | yaakai.to&lt;/p&gt;&lt;span&gt;yaakai.to&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</content:encoded></item><item><title>Claude Code で LSP サポートを使う</title><link>https://yaakai.to/note/130/</link><guid isPermaLink="true">https://yaakai.to/note/130/</guid><pubDate>Mon, 12 Jan 2026 19:36:57 GMT</pubDate><content:encoded>&lt;p&gt;以前のバージョンから LSP のサポートがあることは releasenote に記載されていたが、不具合で実質的に動作していなかったものが 2.1.0 で解消された。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md#210&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;claude-code/CHANGELOG.md at main · anthropics/claude-code&lt;/h3&gt;&lt;p&gt;Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflo...&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;これまで古いバージョンで自作の plugin を作る必要があったが、公式の marketplace にあるものを利用することが出来るようになっている。例えば TypeScript の場合は以下:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$ claude plugin install typescript-lsp@claude-plugins-official&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;$ npm install -g typescript-language-server typescript&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;$ ENABLE_LSP_TOOL=1 claude   &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/anthropics/claude-plugins-official/tree/main/plugins/typescript-lsp&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;claude-plugins-official/plugins/typescript-lsp at main · anthropics/claude-plugins-official&lt;/h3&gt;&lt;p&gt;Official, Anthropic-managed directory of high quality Claude Code Plugins. - anthropics/claude-plugins-official&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;ただ、2.1.5 現在でも動作させるためには &lt;code&gt;ENABLE_LSP_TOOL&lt;/code&gt; フラグが必要。releasenote や&lt;a href=&quot;https://code.claude.com/docs/ja/plugins#%E3%83%97%E3%83%A9%E3%82%B0%E3%82%A4%E3%83%B3%E3%81%ABlsp%E3%82%B5%E3%83%BC%E3%83%90%E3%83%BC%E3%82%92%E8%BF%BD%E5%8A%A0%E3%81%99%E3%82%8B&quot;&gt;ドキュメント&lt;/a&gt;そのような記載はないためなんとも言えないが、現状はまだ Experimental な機能ということだろうか？&lt;/p&gt;</content:encoded></item><item><title>Claude Code の言語を settings.json で設定する</title><link>https://yaakai.to/note/129/</link><guid isPermaLink="true">https://yaakai.to/note/129/</guid><pubDate>Thu, 08 Jan 2026 07:13:26 GMT</pubDate><content:encoded>&lt;p&gt;これまでは CLAUDE.md で言語を指定することが多かったが、2.1.0 で settings.json で &lt;code&gt;language&lt;/code&gt; がサポートされた。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/anthropics/claude-code/blob/main/CHANGELOG.md#210&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;claude-code/CHANGELOG.md at main · anthropics/claude-code&lt;/h3&gt;&lt;p&gt;Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflo...&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;日本語の場合は以下のように設定すると、CLAUDE.md で指定することなく日本語でレスポンスが返されるようになる。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;language&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;japanese&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content:encoded></item><item><title>Issue 起点の開発フローを claude-code-action と CodeRabbit で作った</title><link>https://yaakai.to/blog/2025/issue-driven-workflow-with-claude-and-coderabbit/</link><guid isPermaLink="true">https://yaakai.to/blog/2025/issue-driven-workflow-with-claude-and-coderabbit/</guid><pubDate>Wed, 31 Dec 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;適当な Issue を AI に実装したり一度試してもらいたいときに、これまでは @claude したり &lt;a href=&quot;https://yaakai.to/blog/2025/dev-with-claude-code-2025-07&quot;&gt;git-worktree を使った裏での実行&lt;/a&gt;をしていたが、これを GitHub 上でレビューまで完結するようにしたかったのでフローを作った。 private リポジトリでしばらく使っていたが、&lt;a href=&quot;https://github.com/yaakaito/yaakaito&quot;&gt;このブログのリポジトリ&lt;/a&gt;にも移植してきたのでこのタイミングでまとめておく。&lt;/p&gt;
&lt;h2&gt;やっていること&lt;/h2&gt;
&lt;p&gt;Issue に特定のラベルをつけると GHA が起動して、 Claude Code による実装と CodeRabbit でのレビュー、その修正が自動で行われる。
可能なものは Cloudflare Workers の &lt;a href=&quot;https://developers.cloudflare.com/workers/configuration/previews/&quot;&gt;Preview URLs&lt;/a&gt; を発行していて、それが PR に貼られるのでそこで動作確認をしている。&lt;/p&gt;
&lt;p&gt;流れを書くとこんな感じ:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Issue テンプレートから &lt;code&gt;ai-automate:claude&lt;/code&gt; ラベル付きで Issue を作成&lt;/li&gt;
&lt;li&gt;GHA が起動して、Issue 内容をもとに Claude Code が実装を行い、PR を作成
&lt;ul&gt;
&lt;li&gt;Preview URLs を発行&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;CodeRabbit が PR をレビューして、 Approved か Request Changes を通知&lt;/li&gt;
&lt;li&gt;レビュー通知に合わせて GHA が起動
&lt;ul&gt;
&lt;li&gt;Approved の場合は Claude Code を使ってレビューをして内容をコメント&lt;/li&gt;
&lt;li&gt;Request Changes の場合は Claude Code で修正を行い、 Approved されるまで繰り返す&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;このフローを基本としつつ、今は CodeRabbit が Plan を作ってくれるようになったので、その Plan に基づいて実装を行うフローも追加している。
ただ、 Plan が作られるトリガーがよく分かっていなくて動作が不安定。 CodeRabbit からのコメントをトリガーにしているが、それが来ないことがある。&lt;/p&gt;
&lt;h2&gt;Why CodeRabbit?&lt;/h2&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://www.coderabbit.ai/ja&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;AIコードレビュー | CodeRabbit | 無料トライアル可能&lt;/h3&gt;&lt;p&gt;コンテキストを理解したフィードバック、行単位のコード提案、リアルタイムチャットを備えたAIファーストのプルリクエストレビュアー&lt;/p&gt;&lt;span&gt;www.coderabbit.ai&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;レビューの精度みたいな話を一旦抜きにして、 Approved と Request Changes を送ってくれるからというのがある。というよりは、元々 CodeRabbit を試していて、これをしてくれるのでこのフローを作ってみようと思った。
AI によるコードレビューだと Claude Code にやらせたり、 GitHub Copilot や Greptile を使うという方法が他にもあるが、 PR レビューの承認や変更要求を自動で行ってくれるものはないはずなので CodeRabbit を採用している。&lt;/p&gt;
&lt;p&gt;レビュー内容に関しても今のところそこまで不満はないが、少し遅めなのと、細かすぎる(Nitsや揚げ足取り的な内容)のではと思うことは多少ある。
が、特に今回のように修正を AI が行ってくれる場合は細かい分には嬉しいので、プラスかなと捉えている。遅いのも基本的には放置してあとで見るのであまり気にならない。&lt;/p&gt;
&lt;p&gt;ただ、最終的には Claude Code のレビューも兼用するようにしている。
push 毎に Claude Code がレビューすると PR が荒れがちになるのが気になっていたが、 CodeRabbit の Approved 後にすることでちょうどよくなったと思う。&lt;/p&gt;
&lt;p&gt;また、まだ効果が実感できていないので期待になってしまうが、CodeRabbit や Greptile のようなツールを使うことで、レビューの内容を学習(した記録を外付け)できることに魅力を感じている。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://www.greptile.com/learning&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;AI Code Review | Greptile | Merge 4X Faster, Catch 3X More Bugs&lt;/h3&gt;&lt;p&gt;AI Code Reviews that understand your entire codebase. Automate PR reviews, catch bugs faster, improve code quality with AI-driven analysis. Try Greptile free!&lt;/p&gt;&lt;span&gt;www.greptile.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;あと Poem がかわいい。&lt;/p&gt;
&lt;h2&gt;Examples&lt;/h2&gt;
&lt;p&gt;作った Issue と PR の例:&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/yaakaito/yaakaito/issues/122&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;パンくずリストの実装 · Issue #122 · yaakaito/yaakaito&lt;/h3&gt;&lt;p&gt;各記事ページの左上部分にパンくずリストを追加する。 / 区切りのテキストベースで行い、現在のページは含めない yaakai.to / blog / これに伴って、日付の位置をタイトルの下に変更する。&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/yaakaito/yaakaito/pull/125&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;パンくずリストの実装 by nullkite · Pull Request #125 · yaakaito/yaakaito&lt;/h3&gt;&lt;p&gt;各記事ページの左上部分にパンくずリストを追加し、日付の位置をタイトルの下に変更しました。
Key Changes

web/src/components/Breadcrumb.astro - パンくずリストコンポーネントを新規追加
web/src/layouts/blog-post.astro - レイアウトを更新してパンくずリストを追加し、日付の位置を変更

Technical Detai...&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;ここでは Approved されているのでそれで終わっているが、 Request Changes された場合はさらに修正が自動で行われる。
例えばこれは何度かやりとりを繰り返してる:&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/yaakaito/yaakaito/pull/127&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;注釈は Pop Over で Top Layer に出すようにする by nullkite · Pull Request #127 · yaakaito/yaakaito&lt;/h3&gt;&lt;p&gt;注釈リンクをクリックするとスクロールせずにその場でポップオーバーで注釈内容を表示できるようにしました。
Key Changes

web/src/layouts/blog-post.astro: CSS Anchor Positioning を使用した注釈ポップオーバーの実装

Technical Details

CSS Anchor Positioning API を使用してアンカー要素...&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;GitHub Actions の workflow&lt;/h2&gt;
&lt;p&gt;Preview URLs 用のものを含めると 3 つの workflow で構成している。Preview URLs は適当に &lt;code&gt;wrangler versions upload&lt;/code&gt; すればよいだけなのでここでは割愛、&lt;a href=&quot;https://github.com/yaakaito/yaakaito/blob/main/.github/workflows/deploy-preview.yaml&quot;&gt;GitHub にこのリポジトリのものが置いてある。&lt;/a&gt;&lt;/p&gt;
&lt;div&gt;
&lt;p&gt;WARNING&lt;/p&gt;
&lt;p&gt;この workflow は移植するにあたって、調整を行っている場所がいくつかあって、十分に確認はできていないのでそれが原因で動作しない可能性がある。&lt;/p&gt;
&lt;/div&gt;
&lt;div&gt;
&lt;p&gt;NOTE&lt;/p&gt;
&lt;p&gt;PR を作るのが Bot だと CodeRabbit が反応せず、これを変更する設定もなかった(2025/12/31 時点)ので、PR を作成するために PAT なりを用意する必要がある。
メインで使っているアカウントの PAT でもよいのだが、変更できる範囲を限定したいのと、通知が多くなりすぎてしまうので、自分は専用のアカウントを作ってそれの PAT を使っている。
ただし、 private リポジトリで使う場合はそのユーザーの分も CodeRabbit の seat が必要になるので注意。&lt;/p&gt;
&lt;/div&gt;
&lt;p&gt;まず、 Issue に &lt;code&gt;ai-automate:claude&lt;/code&gt; ラベルがつけられたときに起動する workflow:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Automate with Claude&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;on&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  issues&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    types&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;labeled&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;jobs&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  automate-issue&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;github.event.label.name == &apos;ai-automate:claude&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    runs-on&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;ubuntu-latest&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    permissions&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      contents&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      pull-requests&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      issues&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      id-token&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    steps&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      - &lt;/span&gt;&lt;span&gt;uses&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;actions/checkout@v5&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      - &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Create branch for issue&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        env&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          GH_TOKEN&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ secrets.AI_USER_GH_TOKEN }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          BRANCH_NAME=&quot;claude/automate-${{ github.event.issue.number }}-$(date +%Y%m%d-%H%M)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          git checkout -b &quot;$BRANCH_NAME&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          git push -u origin &quot;$BRANCH_NAME&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          echo &quot;BRANCH_NAME=$BRANCH_NAME&quot; &amp;gt;&amp;gt; $GITHUB_ENV&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      - &lt;/span&gt;&lt;span&gt;uses&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;anthropics/claude-code-action@v1&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        with&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          claude_code_oauth_token&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          github_token&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ secrets.AI_USER_GH_TOKEN }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          prompt&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            Read the issue and implement the requested changes.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            Note: A branch named &quot;${{ env.BRANCH_NAME }}&quot; has already been created and checked out for this issue.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            ## Language Policy&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            Use the same language as the issue for commits, PR title, and PR body.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            ## Workflow&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            1. Use mcp__github__get_issue to read and understand the issue&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            2. Check CLAUDE.md for repository-specific guidelines&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            3. Implement the changes&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            4. Commit and push changes to the current branch&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;               - Include Co-authored-by: ${{ github.event.issue.user.login }} &amp;lt;${{ github.event.issue.user.id }}+${{ github.event.issue.user.login }}@users.noreply.github.com&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            5. Read .claude/skills/pr-template/SKILL.md for PR template, then create PR with mcp__github__create_pull_request&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            6. Add &quot;ai-automate:claude&quot; label to the created PR&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            7. Comment the PR URL to the issue using: gh issue comment ${{ github.event.issue.number }} --body &quot;Created PR: [PR_URL]&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          claude_args&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            --allowedTools &quot;Bash,Write,Edit,Read,Glob,Grep,Task,mcp__github__get_issue,mcp__github_comment__update_claude_comment,mcp__github__create_branch,mcp__github__create_pull_request,mcp__github__update_issue,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh issue comment:*)&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Claude Code に実装させ、実装が終わったら PR を作成させている。&lt;/p&gt;
&lt;p&gt;branch を先に作っているのは、たまに main へそのまま commit して push してしまうことがあったため。
branch protection ルールで守るなどの方法もあるが、ブランチ名が衝突しても面倒そうなので手動で作ることにしている。&lt;/p&gt;
&lt;p&gt;PR に書く内容は Skill 化しているのだが、claude-code-action 内で Skill がうまく動かないので SKILL.md を直接読むように指示している。&lt;/p&gt;
&lt;p&gt;次に、PR が CodeRabbit によってレビューされたときに起動する workflow:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Claude Fix Unresolved Reviews&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;on&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  pull_request_review&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    types&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;submitted&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;jobs&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  # CodeRabbitのrequested changes時に自動修正を実行&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  fix-requested-changes&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      github.event_name == &apos;pull_request_review&apos; &amp;amp;&amp;amp;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      github.event.review.state == &apos;changes_requested&apos; &amp;amp;&amp;amp;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      github.event.review.user.login == &apos;coderabbitai[bot]&apos; &amp;amp;&amp;amp;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      contains(join(github.event.pull_request.labels.*.name, &apos;,&apos;), &apos;ai-automate:&apos;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    runs-on&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;ubuntu-latest&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    permissions&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      contents&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      pull-requests&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      issues&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      id-token&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    steps&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      - &lt;/span&gt;&lt;span&gt;uses&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;actions/checkout@v5&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      - &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Checkout PR&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        env&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          GH_TOKEN&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          gh pr checkout ${{ github.event.pull_request.number }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      - &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Fetch unresolved reviews&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        env&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          GH_TOKEN&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          OWNER=$(echo &quot;${{ github.repository }}&quot; | cut -d/ -f1)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          REPO=$(echo &quot;${{ github.repository }}&quot; | cut -d/ -f2)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          PR_NUMBER=${{ github.event.pull_request.number }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          RESULT=$(gh api graphql -f query=&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          query($owner: String!, $repo: String!, $number: Int!) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              repository(owner: $owner, name: $repo) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  pullRequest(number: $number) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                      reviewThreads(first: 100) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                          nodes {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                              isResolved&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                              isOutdated&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                              path&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                              line&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                              comments(first: 10) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                                  nodes {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                                      author { login }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                                      body&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                                  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                              }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                          }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                      }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          }&apos; -f owner=&quot;$OWNER&quot; -f repo=&quot;$REPO&quot; -F number=&quot;$PR_NUMBER&quot;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          echo &quot;$RESULT&quot; | jq -r &apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              .data.repository.pullRequest.reviewThreads.nodes&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              | map(select(.isResolved == false and .isOutdated == false))&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              | if length == 0 then&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  &quot;&amp;lt;no-unresolved-comments /&amp;gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                else&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  .[] |&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  &quot;&amp;lt;review-comment&amp;gt;&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  &quot;  &amp;lt;file&amp;gt;\(.path // &quot;unknown&quot;)&amp;lt;/file&amp;gt;&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  &quot;  &amp;lt;line&amp;gt;\(.line // &quot;unknown&quot;)&amp;lt;/line&amp;gt;&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  &quot;  &amp;lt;author&amp;gt;\(.comments.nodes[0].author.login // &quot;unknown&quot;)&amp;lt;/author&amp;gt;&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  &quot;  &amp;lt;body&amp;gt;\(.comments.nodes[0].body // &quot;&quot;)&amp;lt;/body&amp;gt;&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  &quot;&amp;lt;/review-comment&amp;gt;&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  &quot;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                end&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &apos; &amp;gt; .git/unresolved_reviews.xml&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      - &lt;/span&gt;&lt;span&gt;uses&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;anthropics/claude-code-action@v1&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        with&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&amp;amp;&lt;/span&gt;&lt;span&gt;claude-code-fix-reviews&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          claude_code_oauth_token&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          github_token&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ secrets.AI_USER_GH_TOKEN }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          allowed_bots&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;coderabbitai[bot]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          track_progress&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          prompt&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            Fix unresolved review comments on this GitHub PR.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            ## Steps&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            1. Read the file `.git/unresolved_reviews.xml` which contains unresolved review comments.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            2. Review each comment and apply the necessary fixes.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;               - If the file contains `&amp;lt;no-unresolved-comments /&amp;gt;`, exit.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            3. Commit after each fix.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            4. Repeat until all fixes are complete.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            5. Push the branch to remote when all fixes are done.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          claude_args&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            --allowedTools &quot;Bash,Write,Edit,MultiEdit,Read,LS,Glob,Grep,mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  # CodeRabbitのapproved時にClaude Codeレビューを実行&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  claude-review-after-approval&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      github.event_name == &apos;pull_request_review&apos; &amp;amp;&amp;amp;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      github.event.review.state == &apos;approved&apos; &amp;amp;&amp;amp;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      github.event.review.user.login == &apos;coderabbitai[bot]&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    runs-on&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;ubuntu-latest&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    permissions&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      contents&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;read&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      pull-requests&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;read&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      issues&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;read&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      id-token&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    steps&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      - &lt;/span&gt;&lt;span&gt;uses&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;actions/checkout@v5&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        with&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          fetch-depth&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      - &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Checkout PR&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        env&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          GH_TOKEN&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          gh pr checkout ${{ github.event.pull_request.number }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      - &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Run Claude Code Review&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        uses&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;anthropics/claude-code-action@v1&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        with&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          claude_code_oauth_token&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          allowed_bots&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;coderabbitai[bot]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          track_progress&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          prompt&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            REPO: ${{ github.repository }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            PR NUMBER: ${{ github.event.pull_request.number }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            Perform a comprehensive code review in the same language as the Pull Request description with the following focus areas:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            1. **Code Quality**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;               - Clean code principles and best practices&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;               - Proper error handling and edge cases&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;               - Code readability and maintainability&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            2. **Security**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;               - Check for potential security vulnerabilities&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;               - Validate input sanitization&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;               - Review authentication/authorization logic&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            3. **Performance**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;               - Identify potential performance bottlenecks&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;               - Review database queries for efficiency&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;               - Check for memory leaks or resource issues&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            4. **Testing**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;               - Verify adequate test coverage&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;               - Review test quality and edge cases&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;               - Check for missing test scenarios&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            5. **Documentation**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;               - Ensure code is properly documented&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;               - Verify README updates for new features&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;               - Check API documentation accuracy&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            Provide detailed feedback using inline comments for specific issues.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            Use top-level comments for general observations or praise.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          claude_args&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            --allowedTools &quot;mcp__github_inline_comment__create_inline_comment,Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;前提として、 PR が作られると CodeRabbit のレビューが自動に開始される必要がある。
Approved された場合は Claude Code によるレビューを行い、 Request Changes された場合は Claude Code による修正を Approved されるまで繰り返す。レビュー用の &lt;code&gt;prompt&lt;/code&gt; は claude-code-action に組み込まれているものと同じはず。&lt;/p&gt;
&lt;p&gt;ここでも&lt;a href=&quot;https://github.com/yaakaito/env/tree/main/cc-plugins/base/skills/github-pr-unresolved-review-fetcher&quot;&gt;レビューコメントの取得を Skill 化したもの&lt;/a&gt;を使いたかったが、一旦妥協して workflow 内に直接 script を書く形にしている。
未解決のレビューを取得するよう &lt;code&gt;prompt&lt;/code&gt; で指示するだけでも十分に思えるが、 GitHub 上で resolved されたものの判別がうまくできないことが何度かあって、確実に動かすためにこうしている。&lt;/p&gt;
&lt;p&gt;ちなみに claude-code-action の場合も次のような設定をすることで Plugin を使うことができる。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;plugin_marketplaces&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;https://github.com/yaakaito/env.git&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;plugins&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;base@yaakaito-env&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;雑感&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;軽い実装であれば Preview URLs も合わせて GitHub 上で動作確認までできて便利
&lt;ul&gt;
&lt;li&gt;メインの作業中に思いついたことを雑に Issue にして投げておける&lt;/li&gt;
&lt;li&gt;必要であれば修正を @claude で投げればよい&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Skill が動いてほしい、動きそうなのだが…
&lt;ul&gt;
&lt;li&gt;ただの md なので直接読めばよいという恩恵を受けている感じがある&lt;/li&gt;
&lt;li&gt;&lt;code&gt;--allowedTools&lt;/code&gt; が足りないとかではなく &lt;code&gt;❌ Error: Execute skill: pr-template&lt;/code&gt; のように失敗してしまう&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;track_progress&lt;/code&gt; を使うと beta 版のように進捗を Issue Comment としてアップデートしてくれて便利な上 branch の問題も解決する
&lt;ul&gt;
&lt;li&gt;ただ、Mention が毎回飛んでくるのでちょっとだるい感じになる&lt;/li&gt;
&lt;li&gt;放置しておいてあとでまとめて確認したい&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;修正を push してもたまに CodeRabbit が反応しないことがある
&lt;ul&gt;
&lt;li&gt;Plan がこないのも同じ理由なのだろうか？&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>CLAUDE.md を作る skill を作って整理する</title><link>https://yaakai.to/note/101/</link><guid isPermaLink="true">https://yaakai.to/note/101/</guid><pubDate>Sat, 20 Dec 2025 19:48:04 GMT</pubDate><content:encoded>&lt;p&gt;これまで &lt;code&gt;/init&lt;/code&gt; したものに適当に書き足したものを CLAUDE.md や AGENTS.md として使ってきたが、いくつか記事を読んだり参考になる CLAUDE.md を見たりで整理したい気持ちになったので整理した。いくつか管理しているリポジトリに適用していきたかったので、 Claude Code の Plugin として skill にしている。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/yaakaito/env/blob/main/cc-plugins/base/skills/agents-md/SKILL.md&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;File not found · yaakaito/env&lt;/h3&gt;&lt;p&gt;Contribute to yaakaito/env development by creating an account on GitHub.&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;整理するにあたって、以下のことを意識して作るようにした:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;最小限であること&lt;/li&gt;
&lt;li&gt;現状を尊重すること&lt;/li&gt;
&lt;li&gt;リポジトリ単位では、人間がやってほしいワークフローを押し付けないこと&lt;/li&gt;
&lt;li&gt;常に使うものでないなら、別ドキュメント / skills / slash command に切り出すこと&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;最終的に次のような構成になった:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;このリポジトリの目的、アプリケーションの簡単な説明&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Architecture Overview&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- どのように作られ、動いているかの概要を数行で書く  &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 例えば Cloudflare Workers と KV を使って構築している&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Directory Structure&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- よく触ることになるファイルのポインタ&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Core Principles&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 現状を尊重してほしいとか、一般的によいとされる開発をしてねとか、消すこともある&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- リポジトリで特に重要なコーディング原則などがあれば書き足す&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Commands&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 普段使うコマンド&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Testing&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- モックは最小限にしてほしいとか&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- リポジトリで必要なことや方針があれば書き足す&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Language Policy&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- AI 向けのドキュメントは英語で書いてほしい&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 他はユーザーに任せる&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Additional Resources&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 特に重要な ADR や ARCHITECURE.md へのリンク&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これに加えて設計の記録である ADR 、全体の設計を詳しく書いた ARCHITECURE.md 、リポジトリごとの固有の BEST_PRACTICES.md をだいたいの場合で作るようにしている。要所で守ってほしいルールはだいたい skill 化していて、例えば ADR のフォーマットを skill にして書くときに読んでもらうようにしている。他には PR のフォーマットとかフロントエンドの実装ガイドラインなんかが便利。&lt;/p&gt;
&lt;p&gt;これに加えて、言語の設定や推奨したいコーディング、ワークフローを書いた &lt;code&gt;~/.claude/CLAUDE.md&lt;/code&gt; を使っている。大した内容ではないが。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/yaakaito/env/blob/main/.claude/CLAUDE.md&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;File not found · yaakaito/env&lt;/h3&gt;&lt;p&gt;Contribute to yaakaito/env development by creating an account on GitHub.&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;所感としては、これまで &lt;code&gt;/init&lt;/code&gt; なりで作っていた比較的大きいものから移行しても大して困っていないので、これくらいで十分なんだなという肌感はある。 Core Principles にちょっと抽象的なことを書いているが効いているかよくわからない。Language Policy を書いたのが結構良くて、AI 向けのものをこれまで英語で書かれたり日本語で書かれたりしていたのがなくなった(元々が日本語ならそうなるが…)。&lt;/p&gt;
&lt;h2&gt;Refs&lt;/h2&gt;
&lt;p&gt;読んだ記事:&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://www.humanlayer.dev/blog/writing-a-good-claude-md&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Writing a good CLAUDE.md&lt;/h3&gt;&lt;p&gt;`CLAUDE.md` is a high-leverage configuration point for Claude Code. Learning how to write a good `CLAUDE.md` (or `AGENTS.md`) is a key skill for agent-enabled software engineering.&lt;/p&gt;&lt;span&gt;www.humanlayer.dev&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://platform.claude.com/docs/en/agents-and-tools/agent-skills/best-practices&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Skill authoring best practices&lt;/h3&gt;&lt;p&gt;Learn how to write effective Skills that Claude can discover and use successfully.&lt;/p&gt;&lt;span&gt;platform.claude.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;参考にさせていただいた CLAUDE.md とか:&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/KentBeck/BPlusTree3/blob/main/rust%2Fdocs%2FCLAUDE.md&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;BPlusTree3/rust/docs/CLAUDE.md at main · KentBeck/BPlusTree3&lt;/h3&gt;&lt;p&gt;A plug-compatible replacement of Rust&amp;amp;#39;s BTree collection - KentBeck/BPlusTree3&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/kopi-vm/kopi/blob/main/AGENTS.md&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;kopi/AGENTS.md at main · kopi-vm/kopi&lt;/h3&gt;&lt;p&gt;Contribute to kopi-vm/kopi development by creating an account on GitHub.&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/oraios/serena/blob/main/CLAUDE.md&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;serena/CLAUDE.md at main · oraios/serena&lt;/h3&gt;&lt;p&gt;A powerful coding agent toolkit providing semantic retrieval and editing capabilities (MCP server &amp;amp;amp; other integrations) - oraios/serena&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/openai/codex/blob/main/AGENTS.md&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;codex/AGENTS.md at main · openai/codex&lt;/h3&gt;&lt;p&gt;Lightweight coding agent that runs in your terminal - openai/codex&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</content:encoded></item><item><title>Codex CLI でも Skills を使う</title><link>https://yaakai.to/note/100/</link><guid isPermaLink="true">https://yaakai.to/note/100/</guid><pubDate>Mon, 08 Dec 2025 18:06:25 GMT</pubDate><content:encoded>&lt;p&gt;Experimental だが、v0.65.0 で Codex CLI でも Claude (Code) の Skills と同等のものがサポートされた。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/openai/codex/releases/tag/rust-v0.65.0&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Release 0.65.0 · openai/codex&lt;/h3&gt;&lt;p&gt;Highlights

Codex Max as default (#7566): Codex Max is now the default model, and a TUI panic related to async-in-sync code was fixed.
Better resume UX (#7302, #7303): Added a /resume slash command...&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/openai/codex/blob/main/docs/skills.md&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;codex/docs/skills.md at main · openai/codex&lt;/h3&gt;&lt;p&gt;Lightweight coding agent that runs in your terminal - openai/codex&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;laiso さんの書かれている内容とほとんど同じになってしまうが、備忘録的に自分の使い方を残す。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://blog.lai.so/codex-skills/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Codex CLI が Skills をサポート&lt;/h3&gt;&lt;p&gt;Codex CLI の最新版v0.65.0 において、experimental ではありますが Skills のサポートが導入されました[1]。

codex/docs/skills.md at main · openai/codexLightweight coding agent that runs in your terminal - openai/codexGitHubopenai

[1]: https://github.com/openai/codex/pull/7412

Claude Skills と同じ形式のディレクトリを配置するだけで読み込まれるため、導入の手間はほとんどありません。設定としては、config.toml に次の一行を追加します。

[features]
skills &amp;amp;#x3D; true


スキルパッケージは ~/.codex/&lt;/p&gt;&lt;span&gt;blog.lai.so&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;使用するには &lt;code&gt;~/.codex/config.toml&lt;/code&gt; でフラグを設定し、 &lt;code&gt;~/.codex/skills&lt;/code&gt; 以下にスキルを配置する必要がある。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;features&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;skills = &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;自分は基本的に開発を DevContainer で行っていて、コンテナが Rebuild される際に設定される Dotfiles Repository に設定をまとめたかったので、Claude Code 向けに Plugin にしているものからセットアップの際にコピーすることにした:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;cp&lt;/span&gt;&lt;span&gt; -r&lt;/span&gt;&lt;span&gt; ./.codex&lt;/span&gt;&lt;span&gt; ~/.codex&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;cp&lt;/span&gt;&lt;span&gt; -r&lt;/span&gt;&lt;span&gt; ./cc-plugins/dev-plan/skills/adr-writer&lt;/span&gt;&lt;span&gt; ~/.codex/skills/adr-writer&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;cp&lt;/span&gt;&lt;span&gt; -r&lt;/span&gt;&lt;span&gt; ./cc-plugins/skills/skills/frontend-design&lt;/span&gt;&lt;span&gt; ~/.codex/skills/frontend-design&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;sub&gt;&lt;a href=&quot;https://github.com/yaakaito/env/blob/main/setup.sh#L9-L11&quot;&gt;https://github.com/yaakaito/env/blob/main/setup.sh#L9-L11&lt;/a&gt;&lt;/sub&gt;&lt;/p&gt;
&lt;p&gt;frontend-design は &lt;a href=&quot;https://github.com/anthropics/skills&quot;&gt;anthropics/skills&lt;/a&gt; のもので、扱いやすいようリポジトリに取り込んでいる。Claude Code Plugin のように付け外しが気楽にできる仕組みになっていないのが惜しいが、どちらもで使うという意味であれば十分そうだ。&lt;/p&gt;
&lt;p&gt;Codex CLI では &lt;code&gt;$&lt;/code&gt; で Skill が参照でき、これは Claude Code にはない利点。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$frontend-design を使って xxx を ....&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Refs&lt;/h2&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/openai/codex/pull/7412&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;feat: experimental support for skills.md by tibo-openai · Pull Request #7412 · openai/codex&lt;/h3&gt;&lt;p&gt;This change prototypes support for Skills with the CLI. This is an experimental feature for internal testing.&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://code.claude.com/docs/ja/plugins&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;プラグインを作成する - Claude Code Docs&lt;/h3&gt;&lt;p&gt;スキル、エージェント、フック、MCP サーバーで Claude Code を拡張するカスタムプラグインを作成します。&lt;/p&gt;&lt;span&gt;code.claude.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</content:encoded></item><item><title>bun test や vitest の onTestFinished Hook</title><link>https://yaakai.to/note/99/</link><guid isPermaLink="true">https://yaakai.to/note/99/</guid><pubDate>Tue, 18 Nov 2025 18:39:40 GMT</pubDate><content:encoded>&lt;p&gt;Bun v1.3.2 で追加され、 Vitest でもサポートされている機能として &lt;code&gt;onFinishedTest&lt;/code&gt; という Hook がある。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://bun.com/blog/bun-v1.3.2&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Bun v1.3.2 | Bun Blog&lt;/h3&gt;&lt;p&gt;Fixes 287 issues (addressing 324 👍). Hoisted installs restored as default for backward compatibility. CPU profiling with --cpu-prof, faster installs for packages with post-install scripts, bun:test onTestFinished hook, ServerWebSocket subscriptions getter, Alpine 3.22 in Docker images, improved Git dependency resolution, bun list alias, and numerous bug fixes across install, HTTP/HTTPS, N-API, bun test, bun build, and runtime.&lt;/p&gt;&lt;span&gt;bun.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://vitest.dev/api/#ontestfinished&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Vitest&lt;/h3&gt;&lt;p&gt;Next generation testing framework powered by Vite&lt;/p&gt;&lt;span&gt;vitest.dev&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;これは各テストの終了時に実行されるフックで、&lt;code&gt;afterEach&lt;/code&gt; よりあとで実行され、テストの成否に関わらず実行されるため、テストで必要だったリソースの解放などに使うことが出来る。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { afterEach, describe, expect, onTestFinished, test } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;bun:test&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;describe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;onTestFinished Hook&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    afterEach&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;afterEach called&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    test&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;example&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        onTestFinished&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;example 1 onTestFinished called&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        expect&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt; +&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;toBe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    test&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;with Error&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        onTestFinished&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            console.&lt;/span&gt;&lt;span&gt;log&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;example 2 onTestFinished called&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        throw&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Error&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これを実行すると次のようになる:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;test/hook.test.ts:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; 6 |     });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; 7 |     test(&apos;example&apos;, () =&amp;gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; 8 |         onTestFinished(() =&amp;gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; 9 |             console.log(&apos;example 1 onTestFinished called&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;10 |         });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;11 |         expect(1 + 1).toBe(3);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                           ^&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;error: expect(received).toBe(expected)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Expected: 3&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Received: 2&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      at &amp;lt;anonymous&amp;gt; (/workspaces/aiblio/test/hook.test.ts:11:23)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;afterEach called&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;example 1 onTestFinished called&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;✗ onTestFinished Hook &amp;gt; example&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;12 |     });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;13 |     test(&apos;with Error&apos;, () =&amp;gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;14 |         onTestFinished(() =&amp;gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;15 |             console.log(&apos;example 2 onTestFinished called&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;16 |         });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;17 |         throw new Error();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                             ^&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Error: &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      at &amp;lt;anonymous&amp;gt; (/workspaces/aiblio/test/hook.test.ts:17:25)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;afterEach called&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;example 2 onTestFinished called&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;✗ onTestFinished Hook &amp;gt; with Error&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; 0 pass&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; 2 fail&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt; 1 expect() calls&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Ran 2 tests across 1 file. [62.00ms]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content:encoded></item><item><title>Chrome DevTools MCP を DevContainer(OrbStack) から使う</title><link>https://yaakai.to/note/98/</link><guid isPermaLink="true">https://yaakai.to/note/98/</guid><pubDate>Sat, 08 Nov 2025 19:02:42 GMT</pubDate><content:encoded>&lt;p&gt;普段 DevContainer で開発している中で DevTools MCP を使いたいが、 Chrome が入っていないコンテナから利用するには設定が必要なのでそのメモ。ホスト側の Chrome を使うようにする。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://developer.chrome.com/blog/chrome-devtools-mcp?hl=ja&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;AI エージェント用の Chrome DevTools（MCP） &amp;amp;nbsp;|&amp;amp;nbsp; Blog &amp;amp;nbsp;|&amp;amp;nbsp; Chrome for Developers&lt;/h3&gt;&lt;p&gt;新しい Chrome DevTools MCP サーバーの一般公開プレビュー版。Chrome DevTools の機能を AI コーディング アシスタントに提供します。&lt;/p&gt;&lt;span&gt;developer.chrome.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;基本的な設定方法は DevTools MCP の README に書かれていて、DevContainer 専用ではないが &lt;code&gt;--browser-url&lt;/code&gt; というオプションに remote debugging port を有効にした Chrome を指定することが出来る。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/ChromeDevTools/chrome-devtools-mcp/?tab=readme-ov-file#connecting-to-a-running-chrome-instance&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;GitHub - ChromeDevTools/chrome-devtools-mcp: Chrome DevTools for coding agents&lt;/h3&gt;&lt;p&gt;Chrome DevTools for coding agents. Contribute to ChromeDevTools/chrome-devtools-mcp development by creating an account on GitHub.&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;remote debugging port は 136 以降で &lt;code&gt;--user-data-dir&lt;/code&gt; オプションを兼用しないと有効にできなくなっている。
今回は Chrome を普段遣いのものとは別で開きたかったので、すべてまとめると次のコマンドで MCP から利用する Chrome を起動できる。Raycast の Script Commnad として適当に登録した。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$ open -n -a &quot;Google Chrome&quot; --args --remote-debugging-port=9222 --user-data-dir=/tmp/devtools-mcp&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Chrome が起動したらコンテナ側から接続するホスト側の IP を調べる。&lt;code&gt;host.docker.internal&lt;/code&gt; でアクセスできそうだが、Chrome 側の制約で拒否されてしまった。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$ getent hosts host.docker.internal&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;gt; 0.250.250.254   host.docker.internal&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;sub&gt;ちなみにコンテナから curl を投げてみるとこんな感じになる:&lt;/sub&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$ curl http://host.docker.internal:9222/json/version                     &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Host header is specified and is not an IP address or localhost.%                                                                                             &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;$ curl -H &quot;Host: localhost&quot; http://host.docker.internal:9222/json/version&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   &quot;Browser&quot;: &quot;Chrome/142.0.7444.60&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   &quot;Protocol-Version&quot;: &quot;1.3&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   &quot;User-Agent&quot;: &quot;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   &quot;V8-Version&quot;: &quot;14.2.231.14&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   &quot;WebKit-Version&quot;: &quot;537.36 (@e7848b4d5b3843432464a9b8237fe58e87f6c357)&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   &quot;webSocketDebuggerUrl&quot;: &quot;ws://localhost/devtools/browser/9e8b5696-17d3-4b14-82cf-1f5975b571b7&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;$ curl http://0.250.250.254:9222/json/version                     &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   &quot;Browser&quot;: &quot;Chrome/142.0.7444.60&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   &quot;Protocol-Version&quot;: &quot;1.3&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   &quot;User-Agent&quot;: &quot;Mozilla/5.0 (Macintosh; Intel Mac OS X 10_15_7) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/142.0.0.0 Safari/537.36&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   &quot;V8-Version&quot;: &quot;14.2.231.14&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   &quot;WebKit-Version&quot;: &quot;537.36 (@e7848b4d5b3843432464a9b8237fe58e87f6c357)&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   &quot;webSocketDebuggerUrl&quot;: &quot;ws://0.250.250.254:9222/devtools/browser/9e8b5696-17d3-4b14-82cf-1f5975b571b7&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;これを引数に起動するように &lt;code&gt;.mcp.json&lt;/code&gt; などを調整する。自分は Claude Code を使っているので、&lt;a href=&quot;https://github.com/yaakaito/env/blob/main/cc-plugins/devtools/.mcp.json&quot;&gt;Plugin としてこの設定を install 出来るようにした。&lt;/a&gt;&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;mcpServers&quot;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;chrome-devtools&quot;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &quot;command&quot;: &quot;npx&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &quot;args&quot;: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;chrome-devtools-mcp@latest&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;--browserUrl=http://0.250.250.254:9222&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      ]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;設定がうまく行っていれば次のような適当なコマンドを実行すると、ホスト側の Chrome が動いて確認できるはず。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;chrome-devtools を使って yaakai.to の Core Web Vitals を測定して &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Refs&lt;/h2&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://developer.chrome.com/docs/devtools/remote-debugging/local-server?hl=ja&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;ポート転送でローカル サーバーと Chrome インスタンスにアクセスする &amp;amp;nbsp;|&amp;amp;nbsp; Chrome DevTools &amp;amp;nbsp;|&amp;amp;nbsp; Chrome for Developers&lt;/h3&gt;&lt;p&gt;開発マシンのウェブサーバーでサイトをホストし、Android デバイスからコンテンツにアクセスする。&lt;/p&gt;&lt;span&gt;developer.chrome.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://developer.chrome.com/blog/remote-debugging-port?hl=ja&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;セキュリティを強化するためのリモート デバッグ スイッチの変更 &amp;amp;nbsp;|&amp;amp;nbsp; Blog &amp;amp;nbsp;|&amp;amp;nbsp; Chrome for Developers&lt;/h3&gt;&lt;p&gt;--remote-debugging-port と --remote-debugging-pipe の動作の変更を発表します。&lt;/p&gt;&lt;span&gt;developer.chrome.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://chromedevtools.github.io/devtools-protocol/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Chrome DevTools Protocol&lt;/h3&gt;&lt;p&gt;Chrome DevTools Protocol - version tot&lt;/p&gt;&lt;span&gt;chromedevtools.github.io&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</content:encoded></item><item><title>実装計画を Vibe Kanban で並列に開発する</title><link>https://yaakai.to/blog/2025/dev-with-vibe-kanban/</link><guid isPermaLink="true">https://yaakai.to/blog/2025/dev-with-vibe-kanban/</guid><pubDate>Tue, 04 Nov 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Coding Agent でタスクリストや実装計画を立てて開発を行っていると、作業を並列に進めることができるシーンが度々ある。いままではこれを git-worktree を利用したり、可能であれば GitHub の Issue として登録することで進めていたが、Vibe Kanban を使うことで快適に行えそうだったので試した。Vibe Kanban の使い方を細かく解説するような内容ではない。&lt;/p&gt;
&lt;h2&gt;Vibe Kanban&lt;/h2&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://www.vibekanban.com/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Vibe Kanban - Orchestrate AI Coding Agents&lt;/h3&gt;&lt;p&gt;Get the most out of coding agents like Claude Code, Gemini CLI and Amp. Orchestrate multiple AI coding agents, track tasks, and manage your development workflow efficiently.&lt;/p&gt;&lt;span&gt;www.vibekanban.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://www.vibekanban.com/docs&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Vibe Kanban - Vibe Kanban&lt;/h3&gt;&lt;p&gt;Plan and review the work of AI agents faster, ship more&lt;/p&gt;&lt;span&gt;www.vibekanban.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;git-worktree をうまく扱えるツールだと認識している。git-worktree を利用して、複数の Coding Agent のインスタンスを同時に立ち上げ、それぞれを管理することができる。自分は名前から勘違いしていたが、 GitHub の Issue を並列で実装できる、というようなツールではない。そのような使い方もできるとは思うが。&lt;/p&gt;
&lt;p&gt;複数の Project(Repository?) を扱えるようなので明記しておくと、自分は Dev Container で開発しているので &lt;code&gt;/workspaces/repo&lt;/code&gt; で &lt;code&gt;npx&lt;/code&gt; で Vibe Kanban を起動している:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;cd /workspaces/repo&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;npx vibe-kanban&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;起動すると URL が発行されるので Web ブラウザで開く。 GitHub と連携すると PR を作成したりできるが、今回の用途では連携しなくとも問題ないはず。&lt;/p&gt;
&lt;h2&gt;実装計画を作って MCP Server を利用してタスクを登録する&lt;/h2&gt;
&lt;p&gt;Vibe Kanban 自体がなにかタスクを分割するような機能を持っているわけではないので、好みのツールやフォーマットで実装計画を作って、タスクリストを作る。
このタイミングで、並列に開発が行えるタスクは依存関係を含めて分割しておく。今回は設計とタスクリストをまとめて &lt;code&gt;PLAN.md&lt;/code&gt; として保存している。&lt;/p&gt;
&lt;p&gt;タスクが分割できたら Vibe Kanban に取り込むのだが、 MCP Server が用意されているのでこれを利用するとタスクの登録を Coding Agent でできるようになる。今回は Claude Code を使用している。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://www.vibekanban.com/docs/integrations/vibe-kanban-mcp-server&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Vibe Kanban MCP Server - Vibe Kanban&lt;/h3&gt;&lt;p&gt;Configure the Vibe Kanban MCP server&lt;/p&gt;&lt;span&gt;www.vibekanban.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;mcpServers&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;vibe_kanban&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &quot;command&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;npx&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &quot;args&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;&quot;-y&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;vibe-kanban@latest&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&quot;--mcp&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;設定ができたら次のようなプロンプトを実行する:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;@PLAN.md を確認して、作業計画を Task 単位で vibe_kanban へ Task として登録してください。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;タスクには以下の内容を記載します:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; 設計は @PLAN.md を参照すること&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; このタスクでの具体的な作業内容&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; 依存する他のタスクや、並列作業が可能かどうか&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; 並列実行できる場合は、タイトルに + をつけてください&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;タスクは降順に登録してください。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;「降順に登録」が割と重要で、これがないと Vibe Kanban 上のタスクリストが逆になってしまうはず。現状タスクのソート機能がなく、常に登録された順番に表示されるためこれを入れたほうがよいと思う。&lt;/p&gt;

&lt;p&gt;登録されたタスクは Attempt を追加して実行する。デフォルトではリポジトリで switch している branch からタスクを実装するようになっている。
今回は Vibe Kanban でコードの確認までした worktree を実装計画用の作業 branch に随時マージしていき、 VSCode 側で作業 branch を使って人間が動作確認するようなスタイルにした。&lt;/p&gt;
&lt;p&gt;また、今回自分は Vibe Kanban で計画を作ったが、タスク登録を考えると計画には Claude Code なりをそのまま利用したほうがよいと思う。&lt;/p&gt;
&lt;h2&gt;並列での実装とレビュー&lt;/h2&gt;
&lt;p&gt;並列で実装が行えるタスクになったら、それぞれに Attempt を追加して実行する。実行中のタスクはすべて In Progress に移動したあと、終わったものから In Review に移動していくので、レビューを行う。&lt;/p&gt;

&lt;p&gt;レビューは Vibe Kanban 内の UI で行うことができる。開発サーバーを連携することでライブプレビューも利用できるようだが、今回は試していない。&lt;/p&gt;

&lt;p&gt;このレビュー UI は&lt;a href=&quot;https://yaakai.to/blog/2025/dev-with-claude-code-2025-07/&quot;&gt;もともと VSCode 上で行っていたスタイル&lt;/a&gt;と似ていて使いやすい。
行単位にレビューしつつも、レビューごとに修正を行うのではなくまとめて依頼することができるようになっていて便利。&lt;/p&gt;
&lt;p&gt;レビューを送信するとまた In Progress に移動するので、これを繰り返して納得できるコードを生成する。生成したら Merge すると内容が作業 branch に反映され、動作確認してタスク完了となる。複数のタスクを並列に進めている場合はしょっちゅう Conflict するが、 Web UI から rebase を要求できるようになっているのでこれを使って解決する。&lt;/p&gt;
&lt;p&gt;あとはすべてのタスクが完了するまでこれを繰り返す。途中、ある程度コードを変更したタイミングで PR を作り、CodeRabbit にレビューしてもらったものを修正しながら進めた。&lt;/p&gt;
&lt;h2&gt;雑感&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;AI にコードを書かせている途中で思いついたタスクを追加したいことはよくあるが、Vibe Kanban では To Do へ適当に追加しておけばよいのが楽だった
&lt;ul&gt;
&lt;li&gt;例えば、似た構成の複数のパッケージを並列で開発させたあと、それぞれの README.md やコードを見比べて品質の統一をするようなタスクを作成し、次のフェーズへ進む前に実行していた&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;タスクを簡単に複製できるので、複数の Coding Agent での実装を試したりするのが手軽にできそう&lt;/li&gt;
&lt;li&gt;Vibe Kanban ですべてやるよりは CLI や他のツールも適宜使った方がよさそう&lt;/li&gt;
&lt;li&gt;試す前はなんとなくスペック駆動との相性が悪そうに思えていたが、使い始めてみるとむしろよいように感じた&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Claude Code のステータスラインをカスタマイズする</title><link>https://yaakai.to/note/96/</link><guid isPermaLink="true">https://yaakai.to/note/96/</guid><pubDate>Mon, 27 Oct 2025 19:57:51 GMT</pubDate><content:encoded>&lt;p&gt;Copilot CLI を触っていたらブランチ名なんかがいい感じに表示されていて、これを Claude Code でもやりたいと思った。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://docs.claude.com/ja/docs/claude-code/statusline&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;ステータスラインをカスタマイズする - Claude Code Docs&lt;/h3&gt;&lt;p&gt;Claude Code でコンテキストウィンドウの使用状況、コスト、git ステータスを監視するカスタムステータスバーを設定します&lt;/p&gt;&lt;span&gt;docs.claude.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;公式情報の通りに進めることにする。まず、&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;/statusline&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;とすると &lt;code&gt;statusline-setup&lt;/code&gt; subagent が起動し、&lt;code&gt;.zshrc&lt;/code&gt; なんかを確認してなるべく同等のものを構築してくれる。
このままでもよいが、自分はターミナルと似ていると一瞬どっちにいるかわからないなと思ったので少しカスタマイズすることにした。&lt;/p&gt;
&lt;p&gt;カスタマイズは &lt;code&gt;/statusline&lt;/code&gt; にやってほしいことを投げるか、&lt;code&gt;/statusline&lt;/code&gt; を実行したあとに普通に会話すればその通りに作ってくれた。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;/statusline git status と git diff を確認して、変更があるかと、追加/削除行数をそれぞれ色付きで表示するようにして&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最終的にワーキングディレクトリと Git の情報を表示するようにした、どのくらい作業させたかの目安になるので気に入っている:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt; /workspaces/reponame [⎇ tmp/branch ↑2↓5] ✗ +4 -3 &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;全部は以下にあるが、自分の欲しいものを Claude Code に作ってもらうほうが良いと思う。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/yaakaito/env/blob/main/.claude/status-line.sh&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;File not found · yaakaito/env&lt;/h3&gt;&lt;p&gt;Contribute to yaakaito/env development by creating an account on GitHub.&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</content:encoded></item><item><title>DevContainer で Rebuild しても .zsh_history が残るようにする</title><link>https://yaakai.to/note/95/</link><guid isPermaLink="true">https://yaakai.to/note/95/</guid><pubDate>Mon, 20 Oct 2025 20:33:26 GMT</pubDate><content:encoded>&lt;p&gt;コンテナを Rebuild する機会はそこまで多くないので、これまで適当に内容をコピペしたりリポジトリ配下に置いたりして誤魔化していたのだが、つらくなってきたの仕組み化しようと思った。履歴は(コンテナの)命より重い・・・・！
これまでやってこなかった理由の一つに「リポジトリに含まれる DevContainer の設定に個人の都合を含みたくない」というのがあり、いろいろ方法を考えていたのだが結果としては妥協することにした。&lt;/p&gt;
&lt;p&gt;方法は公式にも紹介されているように &lt;code&gt;.devcontainer.json&lt;/code&gt; の &lt;code&gt;mounts&lt;/code&gt; を使う。これは Claude Code の公式 DevContainer も同じようなことをしていた。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://code.visualstudio.com/remote/advancedcontainers/persist-bash-history&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Persist bash history&lt;/h3&gt;&lt;p&gt;Persist bash history in containers&lt;/p&gt;&lt;span&gt;code.visualstudio.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/anthropics/claude-code/blob/main/.devcontainer/devcontainer.json#L44-L47&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;claude-code/.devcontainer/devcontainer.json at main · anthropics/claude-code&lt;/h3&gt;&lt;p&gt;Claude Code is an agentic coding tool that lives in your terminal, understands your codebase, and helps you code faster by executing routine tasks, explaining complex code, and handling git workflo...&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;とはいえこのまま使っているわけではなく、もろもろの設定のだるさや保存先が気になったので、ホスト側に XDG Base Directory に沿うような形で配置することにした。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;// devcontainer.json&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;initializeCommand&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;mkdir -p ${localEnv:HOME}/.local/share/devcontainer/${devcontainerId} &amp;amp;&amp;amp; touch ${localEnv:HOME}/.local/share/devcontainer/${devcontainerId}/.zsh_history&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;mounts&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;source=${localEnv:HOME}/.local/share/devcontainer/${devcontainerId}/.zsh_history,target=/home/vscode/.zsh_history,type=bind&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これでだいたいリポジトリ毎に履歴が  &lt;code&gt;~/.local/share/devcontainer/{devcontainerId}/.zsh_history&lt;/code&gt; へ溜まるようになる。
&lt;code&gt;${containerEnv:HOME}&lt;/code&gt; が使えそうだが Build 時には使えなかった。&lt;/p&gt;
&lt;p&gt;これを個人的な設定として &lt;code&gt;devcontainer.local.json&lt;/code&gt; のような形で設定できれば理想だったのだがそういった機能は現状ではできないようだ。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;.zsh_history&lt;/code&gt; 以外にも Claude Code なんかの履歴も同じようにできそうだが、 AI との会話ログにそこまで価値を感じてないのと個人的過ぎるのでやることはないと思う。&lt;/p&gt;
&lt;h2&gt;Refs&lt;/h2&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://containers.dev/implementors/json_reference/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Dev Container metadata reference&lt;/h3&gt;&lt;p&gt;The devcontainer.json file contains any needed metadata and settings required to configure a development container for a given well-defined tool and runtime ...&lt;/p&gt;&lt;span&gt;containers.dev&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://docs.claude.com/ja/docs/claude-code/devcontainer&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;開発コンテナ - Claude Code Docs&lt;/h3&gt;&lt;p&gt;一貫性のある安全な環境が必要なチーム向けのClaude Code開発コンテナについて学びます。&lt;/p&gt;&lt;span&gt;docs.claude.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</content:encoded></item><item><title>仕様駆動開発風のオレオレ実装計画を作って AI と併走する</title><link>https://yaakai.to/blog/2025/my-spec-driven-dev-plan/</link><guid isPermaLink="true">https://yaakai.to/blog/2025/my-spec-driven-dev-plan/</guid><pubDate>Wed, 15 Oct 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Kiro を皮切りに「仕様駆動開発」が話題になり、最近では Spec Kit のようなフレームワークも登場し、今後何が主流になるか読めない状況だ。
自分はツールを変えつつも、Markdown でタスクリストを作って開発するような手法を続けてきていて、現時点でのやり方を振り返りも兼ねて一度まとめておこうと思った。&lt;/p&gt;
&lt;h2&gt;モチベーション&lt;/h2&gt;
&lt;p&gt;この方法でやっている理由としては「Cline を主に使っていた頃からこういう手法でやっていたから」というのがほとんどすべてなのだが、細かいところで言うと、&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;少なくとも Kiro に関しては UI ありきなのが微妙に感じた&lt;/li&gt;
&lt;li&gt;最悪コピペで使える規模の小さいプロンプトで完結するようにしたい&lt;/li&gt;
&lt;li&gt;要件の入力に汎用性がほしかった(例えば ADR をソースにしたり、都度の会話で行ったり)&lt;/li&gt;
&lt;li&gt;自分が直近で扱っていたタスクに関しては、Kiro や Spec Kit のようなフレームワークが提唱するフローは大げさに感じた&lt;/li&gt;
&lt;li&gt;仕様駆動開発の各種 md を Git 管理するべきなのかがわかっていなくて、しないのであればフォーマットは自由でよいのではと思った
&lt;ul&gt;
&lt;li&gt;これに関しては、自分は「Git 管理したくない」と思っていて、今回もしていない&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;あたりがある。とはいえ影響を受けていないかというとそうでもなく、タスクリストの作り方に大きく変化があったので、仕様駆動開発風とした、あくまで風である。&lt;/p&gt;
&lt;p&gt;また、今のバージョンは以下の影響を大きく受けている:&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://tech.dentsusoken.com/entry/2025/08/27/%E5%AE%9F%E7%94%A8%E5%93%81%E3%82%92%E4%BD%9C%E3%81%A3%E3%81%A6%E6%8E%A2%E3%82%8B%E7%94%9F%E6%88%90AI%E3%82%92%E4%BD%BF%E3%81%A3%E3%81%9F%E3%82%BD%E3%83%95%E3%83%88%E3%82%A6%E3%82%A7%E3%82%A2%E9%96%8B&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;実用品を作って探る生成AIを使ったソフトウェア開発の現在 - 電通総研 テックブログ&lt;/h3&gt;&lt;p&gt;みなさんこんにちは、XI本部エンジニアリングオフィスの佐藤太一です。 このエントリでは、私がRustで実装したJava用バージョンマネージャであるKopiを紹介すると共に、実装の過程で得た生成AIを使ったソフトウェア開発に関する知見を共有します。 Kopiのソースコードとドキュメントは全てClaude Codeによるものです。私自身は開発環境の構築とメンテナンスをしながら、プロンプトによる指示のみで、約2か月弱の期間に約四万行のRustコードと約六千行のMarkdownを書き上げました。行数の計測においては、コメントや改行は除いています。 成果物は全てオープンソースソフトウェアとして公開してい…&lt;/p&gt;&lt;span&gt;tech.dentsusoken.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://speakerdeck.com/twada/agentic-software-engineering-findy-2025-07-edition?slide=24&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;AI時代のソフトウェア開発を考える（2025/07版） / Agentic Software Engineering Findy 2025-07 Edition&lt;/h3&gt;&lt;p&gt;AI時代のソフトウェア開発を考える（2025/07版）
開発生産性 Conference 2025
2025年 7月4日（金）
https://dev-productivity-con.findy-code.io/2025&lt;/p&gt;&lt;span&gt;speakerdeck.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;Prompts&lt;/h2&gt;
&lt;p&gt;3 つのプロンプトを Claude Code の Custom Slash Command として使用している。
量が多いものではなく、Claude Code 特有の何かを使うわけでもないので、他のツールでも同様の機能を使ったりコピペで利用できると思う。
1 つ目と 3 つ目はあまり重要ではないので、なくとも良い。&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;/dev-plan:new&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;あなたはプロのソフトウェアエンジニアです。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;これからユーザーが提供するコンテキストをもとにユーザーと議論を行い、コードベースを詳しく分析し、ソフトウェアを設計し、実装計画を立てます。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;ユーザーから &lt;/span&gt;&lt;span&gt;`/dev-plan:create-md`&lt;/span&gt;&lt;span&gt; で作成指示があるまではコードやドキュメントは作成せず、背景、決定、要点、計画をまとめることに徹してください。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;開始時に使うプロンプトで、色々書いているが「まだコードを書かないで」ということが伝われば割となんでもよい。
Claude Code の Plan mode だと、何かを相談 → 計画を承認 → コードを書くという流れになってしまい、事前に大きめの決め事をしたい用途とは噛み合わないように思う。&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;/dev-plan:create-md&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;これまでのコンテキストから実装に必要なタスクリストをコードベースを分析して作成してください。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;実装タスクは、このリポジトリ内の実装のみにフォーカスします。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;いくつかフェーズに分割し、それぞれが独立し完結したフェーズになるようにしてください。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;それぞれのフェーズで実装すべきものや達成すべきゴールを具体的なコードなどを交えて詳しく明記してください。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;タスクリストは次のルールに沿って作成します:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; 詳細な行番号は記載せず、ファイル名とシンボルのみで、簡潔に記載&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; タスクは twada 氏の TDD の原則に沿って作成&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    -&lt;/span&gt;&lt;span&gt; RED/GREEN/REFACTOR の prefix はつけない&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; テストは 1 ケースづつ実装する、一度に複数のテストを追加することは禁止&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; タスクはネストしない&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;フォーマットは以下です:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# ${title}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Overview&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Phase N: ${title}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;このフェーズで実装すべきものや達成すべきゴールを詳しく明記します。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;特にコンテキストから読み取れる設定や数値については具体的に記載します。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;実装方針やコード例、技術スタックについても記載します。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Requirements&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;EARS記法で次のように要件定義します:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; THE SYSTEM SHALL &amp;lt;応答&amp;gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; WHEN &amp;lt;条件&amp;gt;, THE SYSTEM SHALL &amp;lt;応答&amp;gt;.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Targets&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; ${filename} - ${note}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Tasks&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] task1&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] task2&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### References&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; ${filename} - ${note}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Tech Stack&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;使用する技術スタックについてまとめます。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Note&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;実装にあたって注意するべきことがあれば記載します。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;ファイルは .dev-plans 以下へ .md 形式で保存してください。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Claude Code と話し合って大体の方針が決まったあとに、計画を Markdown として保存するために使う、今回の話の中心となるもの。
ファイルのフォーマットを指定しつつ、適当な実装単位で以下のものをまとめてもらっている:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;各フェーズの概要&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Requirements&lt;/code&gt; - このフェーズで実装するべき要件定義&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Targets&lt;/code&gt; - 変更対象となるファイル&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Tasks&lt;/code&gt; - TDD に沿った具体的なタスク&lt;/li&gt;
&lt;li&gt;&lt;code&gt;References&lt;/code&gt; - 参考にになる情報のあるファイル&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;&lt;code&gt;Requirements&lt;/code&gt; と &lt;code&gt;Tasks&lt;/code&gt; セクションが特に重要で、計画をレビューする際はこの 2 つを見ることで想定していないものを作ろうとしていないかを確認している。
実際どのようなものになるかは後述する。&lt;/p&gt;
&lt;h3&gt;&lt;code&gt;/dev-plan:next&lt;/code&gt;&lt;/h3&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;現在のコンテキストにある .dev-plans 以下の実行計画を参照しながら、実装を行います。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;新しい作業を始める前に、前回のタスクがあれば Markdown でチェックリストが完了になっていることを確認してください。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```markdown&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;x&lt;/span&gt;&lt;span&gt;] 完了済みタスク&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] 未完了タスク&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;確認したら、未完了の次の $ARGUMENTS を&lt;/span&gt;&lt;span&gt;**ひとつだけ**&lt;/span&gt;&lt;span&gt;進めてください。完了したら、Markdown のチェックリストを更新してください。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;task&lt;/code&gt; か &lt;code&gt;phase&lt;/code&gt; を引数にとって、それを一つ進める。一つだけじゃ止まらないこともある、可愛いね。
もっと細かくコントロールしたい場合などもあるのでなくともよいし、実際使わないこともある。&lt;/p&gt;
&lt;h2&gt;実際のワークフローの例&lt;/h2&gt;
&lt;p&gt;個人用に作っている RSS リーダーのクライアント的なものを題材に、「日付ごとに既読にする」機能の追加を作りたいとする。
Claude Code を起動したら、 &lt;code&gt;/dev-plan:new&lt;/code&gt; で実装計画の作成を開始する。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;gt; /dev-plan:new is running…&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;● プロのソフトウェアエンジニアとしてお手伝いします。これからユーザーが提供するコンテキストをもとに、コードベースの分析と設計の議&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  論を進めていきます。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  実装計画を立てるために必要な情報を教えてください。以下のような情報があると助かります：&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - 実現したい機能や解決したい課題&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - 背景や動機&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - 現在の問題点&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - 想定しているアプローチやアイデア&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  コードベースの分析が必要な場合は、対象となるパッケージやファイルの情報も教えてください。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  /dev-plan:create-md の指示があるまでは、コードやドキュメントは作成せず、設計と計画の議論に集中します。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;コードを書かないモードになったら具体的な要件を伝えて、望む要件になるように会話する。
別途 ADR のようなものがあったりする場合もここで渡して確認してもらう、なるべくここの汎用性が高くなるようにしたいという意図があってこうなっている。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;gt; @packages/reader に日付ごとに関連する記事を既読にする機能を追加します。\&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 各日付セクションの最後に「既読にする」というテキストでボタンを追加する\&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 「既読にする」ボタンを押すと、セクションの内容が既読になり消える\&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    - 個別記事をクリックして既読にした場合のコントラスト変更とは異なり、表示しないようにする\&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- セクションに記事が一つもないときは「既読にする」ボタンも表示しない&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;● 了解しました。@packages/reader に日付ごとの記事を一括既読にする機能を追加する計画を立てていきます。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  まず、現在のコードベースの構造を分析させてください。フロントエンドの実装を確認します。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ......&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;● 完璧です。コードベースの分析が完了しました。以下、実装計画をまとめます。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  背景と要件&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  実現したい機能&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  日付セクションごとに記事を一括既読にする機能を追加します。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  具体的な要件&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  1. 各日付セクションの最後に「既読にする」ボタンを配置&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  2. ボタンをクリックすると、そのセクションの全記事が既読になり非表示になる&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  3. セクションに表示される記事が0件の場合はボタンも表示しない&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  4. 個別記事の既読ボタン（コントラスト変更）とは異なり、完全に非表示にする&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  現在の実装の分析&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  データ構造&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - 日付でグループ化: GroupedArticlesByDate 型で日付キー（例: &quot;20251013&quot;）ごとに記事を管理&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - 各日付内で2つに分類:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    - withSummary: 要約あり記事をさらにauthor別にグルーピング&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    - withoutSummary: 要約なし記事&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ......&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  この計画で問題なければ、/dev-plan:create-md で詳細な実装ドキュメントを作成します。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  質問や修正したい点があれば教えてください。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;長いので出力は大きく省略している。ここまでで不透明な部分なんかがあれば質問したりしてくれるはず。
今回は通しで実装させるかつテスト周りがわかりやすくなるよう、適当な理由をつけて本来不要な API を新規実装する。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;gt; 機能単位での削除を容易にするため、API は新たに専用のものを作成してください&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;● 了解しました。専用のAPIエンドポイントを作成するように計画を修正します。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  設計決定の変更&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  新規APIエンドポイント&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  決定: /api/articles/read/by-date を新規作成&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  エンドポイント仕様&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  POST /api/articles/read/by-date&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  Content-Type: application/json&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  リクエストボディ: string[] (記事IDの配列)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  レスポンス:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;ids&quot;: string[],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;read&quot;: true,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;feature&quot;: &quot;by-date&quot;  // 識別用&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  既存の bulk API との違い&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - エンドポイントが分離: 機能ごとに独立したエンドポイント&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - 削除が容易: この機能を削除する際は、このエンドポイントと関連コードのみ削除すればよい&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - 実装は類似: 内部的には同じロジック（記事IDリストを受け取って既読にする）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  更新された実装計画&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  1. バックエンド（backend/index.ts）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  新規エンドポイント追加 (138行目付近、既存のbulk APIの後に追加)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  .post(&apos;/api/articles/read/by-date&apos;, zValidator(&apos;json&apos;, z.string().array()), async (c) =&amp;gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      // 具体的な実装例&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  2. フロントエンド（use-backend.ts）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  新関数追加 (markAsReadAll の後に追加)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const markAsReadByDate = async (dateKey: string) =&amp;gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      // 具体的な実装例&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  };&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  3. UI実装（app.tsx）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  変更なし（前述の計画通り）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  4. スタイル（app.module.css）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  変更なし（前述の計画通り）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  機能削除時の手順&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  将来この機能を削除する場合：&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  1. バックエンド: /api/articles/read/by-date エンドポイントを削除&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  2. フロントエンド:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    - use-backend.ts から markAsReadByDate 関数を削除&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    - app.tsx から日付セクションのボタンUIを削除&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    - app.module.css から .dateMarkAsReadButton を削除&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  既存の markAsReadAll や個別既読機能には一切影響しません。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  この修正で問題なければ、/dev-plan:create-md で実装ドキュメントを作成します。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ここまででやりとりが微妙になってきたな、と思ったら全部ばっさり捨てていて、会話で得た知識で最初のプロンプトを調整している。
今回貼り付けているものは 2 回目の結果で、 1 回目はボタンをどこに置くか延々と聞いてくる装置になってしまったので捨てている。&lt;/p&gt;
&lt;p&gt;内容に問題がなくなったら &lt;code&gt;/dev-plan:create-md&lt;/code&gt; で実装計画を Markdown として保存する。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;gt; /dev-plan:create-md is running…&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;● 実装計画をドキュメントとして作成します。まず、コードベースをさらに分析して、テストファイルの構造や既存のテストパターンを確認し&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ます。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ......&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;● 完璧です。テストの構造も理解できました。それでは実装計画ドキュメントを作成します。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;結果としてできた Markdown が以下、Tech Stack や Note 以下はこの解説ではあまり重要ではないので省略している。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;# 日付ごとの一括既読機能の実装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Overview&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;`reader`&lt;/span&gt;&lt;span&gt; パッケージに、日付セクションごとに記事を一括で既読にする機能を追加します。この機能により、ユーザーは特定の日付の記事をまとめて既読にして非表示にできます。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;**主な機能要件:**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; 各日付セクションの最後に「既読にする」ボタンを表示&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; ボタンをクリックすると、その日付の全記事が既読になり非表示になる&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; セクションに表示される記事が0件の場合はボタンも表示しない&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; 機能の削除を容易にするため、専用のAPIエンドポイントを作成&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;**技術スタック:**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; バックエンド: Hono + Cloudflare Workers + D1 Database&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; フロントエンド: React + TypeScript&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; テスト: Bun test framework + Hono testClient&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Phase 1: バックエンドAPI実装（TDD）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;このフェーズでは、日付ごとの一括既読APIエンドポイント &lt;/span&gt;&lt;span&gt;`/api/articles/read/by-date`&lt;/span&gt;&lt;span&gt; を実装します。既存の &lt;/span&gt;&lt;span&gt;`/api/articles/read/bulk`&lt;/span&gt;&lt;span&gt; と同様の動作をしますが、機能として独立させることで将来の削除や変更を容易にします。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Requirements&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; THE SYSTEM SHALL 記事IDの配列を受け取り、該当する全記事を既読にする。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; WHEN 記事IDの配列がPOSTされた場合、THE SYSTEM SHALL D1データベースのReadArticleテーブルに記録する。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; WHEN 一部の記事IDが存在しない場合、THE SYSTEM SHALL 存在する記事のみを既読にして201を返す。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; WHEN 全ての記事IDが存在しない場合、THE SYSTEM SHALL 404エラーを返す。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; WHEN 空配列がPOSTされた場合、THE SYSTEM SHALL 404エラーを返す。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; WHEN 無効なデータ型が送信された場合、THE SYSTEM SHALL 400バリデーションエラーを返す。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; WHEN 既読登録が成功した場合、THE SYSTEM SHALL &lt;/span&gt;&lt;span&gt;`{ ids: string[], read: true, feature: &quot;by-date&quot; }`&lt;/span&gt;&lt;span&gt; の形式でレスポンスを返す。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Targets&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; src/backend/index.ts - 新規エンドポイント &lt;/span&gt;&lt;span&gt;`/api/articles/read/by-date`&lt;/span&gt;&lt;span&gt; の追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; src/backend/index.test.ts - 新規エンドポイントのテスト追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Tasks&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] POST /api/articles/read/by-date が空のDBで空配列を送信すると404を返すテストを追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] POST /api/articles/read/by-date エンドポイントを実装（空配列で404を返す最小実装）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] POST /api/articles/read/by-date が複数記事を一括既読できるテストを追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] 記事IDからArticleを検索し、ReadArticleテーブルに登録する処理を実装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] POST /api/articles/read/by-date が成功時に正しいレスポンス構造を返すテストを追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] レスポンスに &lt;/span&gt;&lt;span&gt;`feature: &quot;by-date&quot;`&lt;/span&gt;&lt;span&gt; フィールドを含める実装を追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] POST /api/articles/read/by-date が部分的に存在しない記事があっても成功するテストを追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] 存在する記事のみを処理する実装を確認（既存コードで対応済み）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] POST /api/articles/read/by-date が全て存在しない記事で404を返すテストを追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] 全記事が存在しない場合の404レスポンス処理を確認&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] POST /api/articles/read/by-date が無効なデータ型でバリデーションエラーを返すテストを追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] zodバリデーションの追加（&lt;/span&gt;&lt;span&gt;`z.string().array()`&lt;/span&gt;&lt;span&gt; を使用）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] POST /api/articles/read/by-date で一括既読後にGET /api/articlesで全てisRead: trueになるテストを追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] エンドツーエンドの動作確認（既存のReadArticleテーブルとの連携）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### References&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; src/backend/index.ts:138-173 - 既存の &lt;/span&gt;&lt;span&gt;`/api/articles/read/bulk`&lt;/span&gt;&lt;span&gt; エンドポイント（実装パターンの参考）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; src/backend/index.test.ts:236-321 - 既存の一括既読機能のテスト（テストパターンの参考）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; src/backend/test/fixtures/test-data.ts - テストデータのセットアップ&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; src/backend/test/mocks/d1.ts - D1データベースのモック実装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Phase 2: フロントエンド状態管理実装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;このフェーズでは、&lt;/span&gt;&lt;span&gt;`use-backend.ts`&lt;/span&gt;&lt;span&gt; に日付ごとの一括既読処理を行う &lt;/span&gt;&lt;span&gt;`markAsReadByDate`&lt;/span&gt;&lt;span&gt; 関数を追加します。この関数は、指定された日付キーに対応する全記事を既読にし、楽観的更新とエラーハンドリングを提供します。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Requirements&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; THE SYSTEM SHALL 日付キー（例: &quot;20251013&quot;）を受け取り、その日付の全記事を既読にする。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; WHEN 日付キーが存在しない場合、THE SYSTEM SHALL 処理を中断する。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; WHEN 記事が0件の場合、THE SYSTEM SHALL 処理を中断する。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; THE SYSTEM SHALL 楽観的更新により即座にUIを更新する。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; WHEN API呼び出しが失敗した場合、THE SYSTEM SHALL 状態をロールバックする。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; THE SYSTEM SHALL pending既読状態もクリアする。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; THE SYSTEM SHALL 新規APIエンドポイント &lt;/span&gt;&lt;span&gt;`/api/articles/read/by-date`&lt;/span&gt;&lt;span&gt; を呼び出す。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Targets&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; src/frontend/component/use-backend.ts - &lt;/span&gt;&lt;span&gt;`markAsReadByDate`&lt;/span&gt;&lt;span&gt; 関数の追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Tasks&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] &lt;/span&gt;&lt;span&gt;`markAsReadByDate`&lt;/span&gt;&lt;span&gt; 関数のスケルトンを追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] 日付キーから &lt;/span&gt;&lt;span&gt;`groupedArticles[dateKey]`&lt;/span&gt;&lt;span&gt; を取得する処理を実装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] &lt;/span&gt;&lt;span&gt;`withSummary`&lt;/span&gt;&lt;span&gt; の全author配下の記事IDを収集する処理を実装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] &lt;/span&gt;&lt;span&gt;`withoutSummary`&lt;/span&gt;&lt;span&gt; の記事IDを収集する処理を実装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] 記事IDが0件の場合に早期リターンする処理を追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] 楽観的更新により &lt;/span&gt;&lt;span&gt;`readOnThisSession`&lt;/span&gt;&lt;span&gt; を更新する処理を実装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] &lt;/span&gt;&lt;span&gt;`pendingReadOnThisSession`&lt;/span&gt;&lt;span&gt; から該当記事を削除する処理を実装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] &lt;/span&gt;&lt;span&gt;`/api/articles/read/by-date`&lt;/span&gt;&lt;span&gt; APIを呼び出す処理を実装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] API失敗時の状態ロールバック処理を実装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] エラーログ出力処理を追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] &lt;/span&gt;&lt;span&gt;`useBackend`&lt;/span&gt;&lt;span&gt; の戻り値に &lt;/span&gt;&lt;span&gt;`markAsReadByDate`&lt;/span&gt;&lt;span&gt; を追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### References&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; src/frontend/component/use-backend.ts:146-171 - 既存の &lt;/span&gt;&lt;span&gt;`markAsReadAll`&lt;/span&gt;&lt;span&gt; 関数（実装パターンの参考）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; src/frontend/component/use-backend.ts:21-69 - &lt;/span&gt;&lt;span&gt;`groupedAndSorted`&lt;/span&gt;&lt;span&gt; 関数（データ構造の参考）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; src/frontend/component/use-backend.ts:98-115 - &lt;/span&gt;&lt;span&gt;`markAsRead`&lt;/span&gt;&lt;span&gt; 関数（エラーハンドリングの参考）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Phase 3: UI実装とスタイリング&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;このフェーズでは、各日付セクションに「既読にする」ボタンを追加し、適切なスタイルを適用します。表示される記事が0件のセクションではボタンを表示しないようにします。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Requirements&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; THE SYSTEM SHALL 各日付セクションの最後に「既読にする」ボタンを表示する。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; WHEN セクション内のフィルタ後の記事が0件の場合、THE SYSTEM SHALL ボタンを表示しない。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; WHEN ボタンがクリックされた場合、THE SYSTEM SHALL &lt;/span&gt;&lt;span&gt;`markAsReadByDate`&lt;/span&gt;&lt;span&gt; 関数を呼び出す。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; THE SYSTEM SHALL 既存の &lt;/span&gt;&lt;span&gt;`.toggleButton`&lt;/span&gt;&lt;span&gt; と統一感のあるスタイルを適用する。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; THE SYSTEM SHALL ボタンに適切なマージンを設定し、視覚的に分かりやすく配置する。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Targets&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; src/frontend/component/app.tsx - 日付セクションへのボタン追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; src/frontend/component/app.module.css - ボタンスタイルの追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Tasks&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] 日付セクション内のフィルタ後記事数をカウントする処理を追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] &lt;/span&gt;&lt;span&gt;`withSummary`&lt;/span&gt;&lt;span&gt; の全author配下のフィルタ済み記事数を計算&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] &lt;/span&gt;&lt;span&gt;`withoutSummary`&lt;/span&gt;&lt;span&gt; のフィルタ済み記事数を計算&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] 記事数が0より大きい場合のみボタンを表示する条件分岐を追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] 「既読にする」ボタンの要素を追加（&lt;/span&gt;&lt;span&gt;`button`&lt;/span&gt;&lt;span&gt; タグ）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] ボタンのクリックハンドラに &lt;/span&gt;&lt;span&gt;`markAsReadByDate(key)`&lt;/span&gt;&lt;span&gt; を設定&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] app.module.css に &lt;/span&gt;&lt;span&gt;`.dateMarkAsReadButton`&lt;/span&gt;&lt;span&gt; クラスを追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] ボタンの基本スタイル（色、パディング、ボーダー）を設定&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] ボタンのホバースタイルを追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] ボタンの配置用マージンを設定（日付セクション内で適切に配置）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### References&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; src/frontend/component/app.tsx:59-94 - 既存の日付セクションレンダリング&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; src/frontend/component/app.tsx:96-103 - 既存のコントロールボタン&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; src/frontend/component/app.module.css:58-77 - 既存の &lt;/span&gt;&lt;span&gt;`.toggleButton`&lt;/span&gt;&lt;span&gt; スタイル&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; src/frontend/component/app.tsx:43-55 - &lt;/span&gt;&lt;span&gt;`filterArticle`&lt;/span&gt;&lt;span&gt; 関数&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Phase 4: 統合テストと動作確認&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;このフェーズでは、実装した機能が正しく動作することを確認します。特に、日付セクションごとの既読処理が適切に動作し、UIが期待通りに更新されることを検証します。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Requirements&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; THE SYSTEM SHALL 開発環境で全ての機能が正常に動作する。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; THE SYSTEM SHALL 日付セクションのボタンをクリックすると、そのセクションの記事が非表示になる。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; THE SYSTEM SHALL ボタンクリック後、セクション自体も非表示になる（記事が0件になるため）。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; THE SYSTEM SHALL 他の日付セクションに影響を与えない。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; THE SYSTEM SHALL エラーケースで適切にロールバックする。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Targets&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; 全実装コード&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; 開発環境でのブラウザ動作確認&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Tasks&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] バックエンドのユニットテストを全て実行（&lt;/span&gt;&lt;span&gt;`bun test src/backend/index.test.ts`&lt;/span&gt;&lt;span&gt;）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] テストが全てパスすることを確認&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] ビルドが成功することを確認（&lt;/span&gt;&lt;span&gt;`bun run build`&lt;/span&gt;&lt;span&gt;）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] 開発サーバーを起動（&lt;/span&gt;&lt;span&gt;`bun run dev`&lt;/span&gt;&lt;span&gt;）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] ブラウザで複数日付の記事が表示されることを確認&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] 最新日付の「既読にする」ボタンが表示されることを確認&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] ボタンをクリックして、その日付の記事が非表示になることを確認&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] 他の日付セクションが影響を受けないことを確認&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] ページをリロードして、既読状態が永続化されていることを確認&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] Chrome拡張機能をビルド（&lt;/span&gt;&lt;span&gt;`bun run build-extension`&lt;/span&gt;&lt;span&gt;）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] Chrome拡張機能で同様の動作確認&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; [ ] biomeによるコード品質チェック（&lt;/span&gt;&lt;span&gt;`bunx @biomejs/biome check --apply ./`&lt;/span&gt;&lt;span&gt;）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### References&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; CLAUDE.md:13-38 - ビルド・開発コマンドの一覧&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; packages/reader/README.md - Reader パッケージのドキュメント（もし存在する場合）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Tech Stack&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;......&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Note&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### 実装上の注意点&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;......&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Markdown ができたら、この内容を詳しくレビューする。特に Requirements と Tasks セクションを重点的に見て、想定したものになっているかを確認する。
例えば以下のようなものがあれば調整する:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;仕様の不足 / 不要な仕様が混じっている → 仕様、要件の軌道修正&lt;/li&gt;
&lt;li&gt;タスクの粒度が大きすぎる → 1 タスクを自身がレビューしやすいと思われるサイズに調整&lt;/li&gt;
&lt;li&gt;テストが足りていない → 書いてほしいテストを指示し追加&lt;/li&gt;
&lt;li&gt;実装の順番が不適切 → タスクの順番を入れ替えを指示、大きく変えるときは直接編集して読み込み直す&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;gt; - WHEN 既読登録が成功した場合、THE SYSTEM SHALL `{ ids: string[], read: true, feature: &quot;by-date&quot; }` の形式でレスポンスを返す。\&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;\&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;read: true と feature フィールドは不要です&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;● 承知しました。レスポンス形式を { ids: string[] } のみに修正します。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;● Update(.dev-plans/mark-as-read-by-date.md)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ⎿  Updated .dev-plans/mark-as-read-by-date.md with 1 addition and 1 removal&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;       27    - WHEN 全ての記事IDが存在しない場合、THE SYSTEM SHALL 404エラーを返す。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;       28    - WHEN 空配列がPOSTされた場合、THE SYSTEM SHALL 404エラーを返す。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;       29    - WHEN 無効なデータ型が送信された場合、THE SYSTEM SHALL 400バリデーションエラーを返す。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;       30 -  - WHEN 既読登録が成功した場合、THE SYSTEM SHALL `{ ids: string[], read: true, feature: &quot;by-date&quot; }` の形式でレスポンスを返す。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;       30 +  - WHEN 既読登録が成功した場合、THE SYSTEM SHALL `{ ids: string[] }` の形式でレスポンスを返す。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;       31&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;       32    ### Targets&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;       33&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;● Update(.dev-plans/mark-as-read-by-date.md)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ⎿  Updated .dev-plans/mark-as-read-by-date.md with 1 removal&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;       41    - [ ] POST /api/articles/read/by-date が複数記事を一括既読できるテストを追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;       42    - [ ] 記事IDからArticleを検索し、ReadArticleテーブルに登録する処理を実装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;       43    - [ ] POST /api/articles/read/by-date が成功時に正しいレスポンス構造を返すテストを追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;       44 -  - [ ] レスポンスに `feature: &quot;by-date&quot;` フィールドを含める実装を追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;       44    - [ ] POST /api/articles/read/by-date が部分的に存在しない記事があっても成功するテストを追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;       45    - [ ] 存在する記事のみを処理する実装を確認（既存コードで対応済み）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;       46    - [ ] POST /api/articles/read/by-date が全て存在しない記事で404を返すテストを追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;● 修正完了しました。以下の変更を行いました：&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  1. Requirementsセクション（30行目）: レスポンス形式を { ids: string[] } のみに変更&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  2. Tasksセクション（46行目）: feature: &quot;by-date&quot; フィールド追加タスクを削除&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  これで、APIレスポンスは既存の /api/articles/read/bulk と同じシンプルな形式になります。実装がよりシンプルになりますね。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;調整が終わったら、一度 Claude Code を終了して、実装計画だけを読み込み直している。Git 管理していない場合は補完が効かず少しめんどくさい。
読み込んだあとは &lt;code&gt;/dev-plan:next phase&lt;/code&gt; や都度の指示で、計画が完了するまで順番に消化していく。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;gt; Read @.dev-plans/mark-as-read-by-date.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;● ファイルを確認しました。これは日付ごとの一括既読機能の実装計画書ですね。内容を要約します：&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  📋 実装計画の概要&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ......&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  次のステップ: Phase 1から順番に実装を進めますか？それとも特定のフェーズについて詳しく確認したいことがありますか？&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;gt; /dev-plan:next phase&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;基本的には Manual Accept で進め、特にテストコードは都度細かく確認するように意識している。
自分しか使わない UI の構築タスクなんかは Auto Accept で進めることもあるが、そもそもこの手法を採用するのは、自分の頭にあるコードを高速に書くツールとして AI を利用したいようなケースなので、 Auto Accept すること自体にあまり意味がないと考えている。&lt;/p&gt;
&lt;h2&gt;雑感&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;ある程度規模のあるタスクを一元管理できるようになり、出力も安定しているように思うので、比較的安心してコードを生成できている肌感はある&lt;/li&gt;
&lt;li&gt;一方で現状タスクやフェーズのゴールの定義が曖昧だなと感じていて、直近で Codex のイベントで紹介されていた方法は参考なりそうだが、まだ見れていない: &lt;a href=&quot;https://www.youtube.com/watch?v=Gr41tYOzE20&quot;&gt;https://www.youtube.com/watch?v=Gr41tYOzE20&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;この手法で時間という意味での生産性があがるのかと言われると微妙で、結局計画をレビューするのに時間とスタミナを使ってしまう、最後の 10% (＝実際のコード生成) だけを見れば速いと思う
&lt;ul&gt;
&lt;li&gt;慣れの問題だったり、記述するべきコードの量が多い場合は時間が短縮できるようには思う&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;結局実装計画のようなものを Git 管理に乗せるべきなのかはよくわからない&lt;/li&gt;
&lt;li&gt;基本的にはこの形になっていたからか、少なくとも手元では Claude Code の性能の劣化、みたいなのをあまり感じなかった&lt;/li&gt;
&lt;li&gt;ファイルを Git 管理しない場合に Claude Code では補完が効かないのが不便
&lt;ul&gt;
&lt;li&gt;この辺は Issue になっている: &lt;a href=&quot;https://github.com/anthropics/claude-code/issues/5105&quot;&gt;https://github.com/anthropics/claude-code/issues/5105&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;.git 以下に置くのもためしたが、こちらは補完は効くが相談中に Auto Accept で複数行修正したいようなケースでそれが効かなくて不便、特別な権限が設定されている？&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;この手法を続けるかは現時点ではわからないが、Spec Kit はしばらく使ってみたいと思っている&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>dependabot の cooldown と pnpm/bun の minimumReleaseAge を設定する</title><link>https://yaakai.to/note/94/</link><guid isPermaLink="true">https://yaakai.to/note/94/</guid><pubDate>Sun, 28 Sep 2025 16:37:18 GMT</pubDate><content:encoded>&lt;p&gt;直近で話題になることが多かったサプライチェーン攻撃に対する予防策として、「一定時間経過したパッケージしかアップデート対象にしない」というオプションが dependabot や pnpm に導入されている。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.blog/changelog/2025-07-01-dependabot-supports-configuration-of-a-minimum-package-age/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Dependabot supports configuration of a minimum package age - GitHub Changelog&lt;/h3&gt;&lt;p&gt;The cooldown feature is now generally available for Dependabot version updates! This feature gives you control over when version update pull requests are created to bump your dependencies. What&amp;amp;rsquo;s new&amp;amp;hellip;&lt;/p&gt;&lt;span&gt;github.blog&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://pnpm.io/ja/blog/releases/10.16&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;pnpm 10.16&lt;/h3&gt;&lt;span&gt;pnpm.io&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://pnpm.io/ja/blog/releases/10.17&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;pnpm 10.17&lt;/h3&gt;&lt;span&gt;pnpm.io&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;dependabot / cooldown&lt;/h2&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://docs.github.com/ja/code-security/dependabot/dependabot-version-updates/optimizing-pr-creation-version-updates#setting-up-a-cooldown-period-for-dependency-updates&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Dependabot バージョン更新プログラムに合わせて pull request の作成を最適化する&lt;/h3&gt;&lt;span&gt;docs.github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;.github/dependabot.yml&lt;/code&gt; に次のように設定する:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;version&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;2&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;updates&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - &lt;/span&gt;&lt;span&gt;package-ecosystem&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;npm&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    directory&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;/workers&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    schedule&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      interval&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;weekly&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    cooldown&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      default-days&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;7&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      semver-major-days&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;14&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;semver-major-days&lt;/code&gt; などで semantic versioning に従って細かく設定ができる。&lt;code&gt;include / exclude&lt;/code&gt; での除外設定もサポートされている。&lt;/p&gt;
&lt;h2&gt;pnpm / minimumReleaseAge&lt;/h2&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://pnpm.io/ja/settings#minimumreleaseage&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Settings (pnpm-workspace.yaml)&lt;/h3&gt;&lt;span&gt;pnpm.io&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;pnpm-workspace.yaml&lt;/code&gt; に次のように設定するか、CLI からも設定できる:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;minimumReleaseAge&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;10080&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt;  pnpm&lt;/span&gt;&lt;span&gt; config&lt;/span&gt;&lt;span&gt; set&lt;/span&gt;&lt;span&gt; --location=project&lt;/span&gt;&lt;span&gt; minimumReleaseAge&lt;/span&gt;&lt;span&gt; 10080&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;dependabot とは違い、こちらは分単位での指定(7日)。 &lt;code&gt;10.17.0&lt;/code&gt; で &lt;code&gt;minimumReleaseAgeExclude&lt;/code&gt; が入って除外設定もできるようになった。&lt;/p&gt;
&lt;h2&gt;[追記] bun / minimumReleaseAge&lt;/h2&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://bun.com/docs/runtime/bunfig#install-minimumreleaseage&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;bunfig.toml - Bun&lt;/h3&gt;&lt;p&gt;Configure Bun&amp;amp;#x27;s behavior using its configuration file bunfig.toml&lt;/p&gt;&lt;span&gt;bun.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;bunfig.toml&lt;/code&gt; で設定する:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;install&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;minimumReleaseAge = &lt;/span&gt;&lt;span&gt;604800&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;minimumReleaseAgeExcludes = [&lt;/span&gt;&lt;span&gt;&quot;@types/bun&quot;&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これは秒になっていてややこしい…。pnpm の 60 倍になっているが、設定としてはこれで同じ。&lt;/p&gt;</content:encoded></item><item><title>bun:sqlite を使って bun test で Hono + Cloudflare Workers + D1 をテストする</title><link>https://yaakai.to/note/92/</link><guid isPermaLink="true">https://yaakai.to/note/92/</guid><pubDate>Tue, 09 Sep 2025 12:04:41 GMT</pubDate><content:encoded>&lt;p&gt;Cloudflare D1 を &lt;code&gt;bun:sqlite&lt;/code&gt; で代用することで、 wrangler を使わずに &lt;code&gt;bun test&lt;/code&gt; でテストが書けるようにした。ORM も使っていないような簡単なアプリ向け。&lt;/p&gt;
&lt;p&gt;まず、 &lt;code&gt;bun:sqlite&lt;/code&gt; をオンメモリで作成して、必要なテーブルを用意しつつ D1 互換のインターフェイスを返すモックを作れるようにする。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { Database } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;bun:sqlite&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; function&lt;/span&gt;&lt;span&gt; createMockD1Database&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; db&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Database&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;:memory:&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    db.&lt;/span&gt;&lt;span&gt;exec&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;BEGIN&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    db.&lt;/span&gt;&lt;span&gt;exec&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        CREATE TABLE ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    `&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    db.&lt;/span&gt;&lt;span&gt;exec&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;COMMIT&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        prepare&lt;/span&gt;&lt;span&gt;: (&lt;/span&gt;&lt;span&gt;query&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; string&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            const&lt;/span&gt;&lt;span&gt; stmt&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; db.&lt;/span&gt;&lt;span&gt;prepare&lt;/span&gt;&lt;span&gt;(query);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            return&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                bind&lt;/span&gt;&lt;span&gt;: (&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;params&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; any&lt;/span&gt;&lt;span&gt;[]) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; ({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    all&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        try&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            const&lt;/span&gt;&lt;span&gt; results&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; stmt.&lt;/span&gt;&lt;span&gt;all&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;params);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            return&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                                results,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                                success: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                                meta: {},&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            };&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        } &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; any&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            return&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                                results: [],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                                success: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                                meta: { error: error.message },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            };&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    first&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        try&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            return&lt;/span&gt;&lt;span&gt; stmt.&lt;/span&gt;&lt;span&gt;get&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;params) &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; null&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        } &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; (error) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            return&lt;/span&gt;&lt;span&gt; null&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        try&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            stmt.&lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;...&lt;/span&gt;&lt;span&gt;params);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            return&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                                success: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                                meta: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                                    changes: db.changes,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                                    last_row_id: db.lastInsertRowid,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                                },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            };&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        } &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; any&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            return&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                                success: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                                meta: { error: error.message },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                            };&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                }),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            };&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        batch&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;statements&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; any&lt;/span&gt;&lt;span&gt;[]) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            const&lt;/span&gt;&lt;span&gt; results&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; [];&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; statement&lt;/span&gt;&lt;span&gt; of&lt;/span&gt;&lt;span&gt; statements) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                const&lt;/span&gt;&lt;span&gt; result&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; statement.&lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                results.&lt;/span&gt;&lt;span&gt;push&lt;/span&gt;&lt;span&gt;(result);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            return&lt;/span&gt;&lt;span&gt; results;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        exec&lt;/span&gt;&lt;span&gt;: (&lt;/span&gt;&lt;span&gt;sql&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; string&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            return&lt;/span&gt;&lt;span&gt; db.&lt;/span&gt;&lt;span&gt;exec&lt;/span&gt;&lt;span&gt;(sql);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    };&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;このモックを Hono の &lt;code&gt;testClient&lt;/code&gt; で &lt;code&gt;DB&lt;/code&gt; として渡すことで、 D1 の代わりにモックが使用され、 他のツールなしに &lt;code&gt;bun test&lt;/code&gt; でテストが書ける。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;describe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;Tests&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    let&lt;/span&gt;&lt;span&gt; client&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; TestClient&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    let&lt;/span&gt;&lt;span&gt; mockDB&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; ReturnType&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;typeof&lt;/span&gt;&lt;span&gt; createMockD1Database&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    beforeEach&lt;/span&gt;&lt;span&gt;(() &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        mockDB &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; createMockD1Database&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        client &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; testClient&lt;/span&gt;&lt;span&gt;(app, {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            DB: mockDB,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    test&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;api&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;async&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        const&lt;/span&gt;&lt;span&gt; res&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; client.api.items.&lt;/span&gt;&lt;span&gt;$get&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        expect&lt;/span&gt;&lt;span&gt;(res.status).&lt;/span&gt;&lt;span&gt;toBe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;200&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        const&lt;/span&gt;&lt;span&gt; items&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; res.&lt;/span&gt;&lt;span&gt;json&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        expect&lt;/span&gt;&lt;span&gt;(Array.&lt;/span&gt;&lt;span&gt;isArray&lt;/span&gt;&lt;span&gt;(items)).&lt;/span&gt;&lt;span&gt;toBe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        expect&lt;/span&gt;&lt;span&gt;(items).&lt;/span&gt;&lt;span&gt;toHaveLength&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;事前にテストデータなんかがほしいときは普通に &lt;code&gt;mockDB.exec&lt;/code&gt; を使って入れたりしている。&lt;/p&gt;
&lt;h2&gt;Refs&lt;/h2&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://hono.dev/docs/helpers/testing&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Testing Helper - Hono&lt;/h3&gt;&lt;p&gt;Web framework built on Web Standards for Cloudflare Workers, Fastly Compute, Deno, Bun, Vercel, Node.js, and others. Fast, but not only fast.&lt;/p&gt;&lt;span&gt;hono.dev&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/yusukebe/testing-d1-app-with-types&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;GitHub - yusukebe/testing-d1-app-with-types: Testing a D1 Application with Types&lt;/h3&gt;&lt;p&gt;Testing a D1 Application with Types. Contribute to yusukebe/testing-d1-app-with-types development by creating an account on GitHub.&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://bun.com/docs/api/sqlite&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;SQLite - Bun&lt;/h3&gt;&lt;p&gt;Bun natively implements a high-performance SQLite3 driver.&lt;/p&gt;&lt;span&gt;bun.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</content:encoded></item><item><title>ES Module の import defer</title><link>https://yaakai.to/note/91/</link><guid isPermaLink="true">https://yaakai.to/note/91/</guid><pubDate>Fri, 22 Aug 2025 11:59:48 GMT</pubDate><content:encoded>&lt;p&gt;TypeScript 5.9 の告知を見て知ったのだが、 import はするがその評価を遅延する、という構文が ES の Stage 3 になっている。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-5-9/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Announcing TypeScript 5.9 - TypeScript&lt;/h3&gt;&lt;p&gt;Today we are excited to announce the release of TypeScript 5.9! If you&amp;amp;#8217;re not familiar with TypeScript, it&amp;amp;#8217;s a language that builds on JavaScript by adding syntax for types. With types, TypeScript makes it possible to check your code to avoid bugs ahead of time. The TypeScript type-checker does all this, and is also the [&amp;amp;hellip;]&lt;/p&gt;&lt;span&gt;devblogs.microsoft.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/tc39/proposal-defer-import-eval/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;GitHub - tc39/proposal-defer-import-eval: A proposal for introducing a way to defer evaluate of a module&lt;/h3&gt;&lt;p&gt;A proposal for introducing a way to defer evaluate of a module - tc39/proposal-defer-import-eval&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;従来 &lt;code&gt;import&lt;/code&gt; や &lt;code&gt;require&lt;/code&gt; と同時に大きな副作用なあるようなモジュールを読み込む際、それを実際に使用するまで読み込みを遅延するようなテクニックがあった。例えば、 &lt;code&gt;require&lt;/code&gt; を使った次のようなコードである（ Proposal からの引用）:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;// https://github.com/tc39/proposal-defer-import-eval/?tab=readme-ov-file#motivation&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;exports&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;doSomething&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; function&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;target&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; operation&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; require&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;operation&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; operation&lt;/span&gt;&lt;span&gt;(target);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これを解決するスマートな構文として次のようなものが提案されている（ Proposal からの引用）:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;// https://github.com/tc39/proposal-defer-import-eval/?tab=readme-ov-file#proposal&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; defer &lt;/span&gt;&lt;span&gt;*&lt;/span&gt;&lt;span&gt; as&lt;/span&gt;&lt;span&gt; yNamespace &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &quot;y&quot;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;頻度はそう高くないが、 ESM で書いている場合に &lt;code&gt;require&lt;/code&gt; の例のような記述をすることは諦めていたので、なるほどなぁとなる提案だった。&lt;/p&gt;</content:encoded></item><item><title>2025年7月版個人的開発スタイル with Claude Code</title><link>https://yaakai.to/blog/2025/dev-with-claude-code-2025-07/</link><guid isPermaLink="true">https://yaakai.to/blog/2025/dev-with-claude-code-2025-07/</guid><pubDate>Tue, 15 Jul 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;最近の開発はほとんど Claude Code で行っているが、使い始めた 3 月から比べると利用スタイルも結構変わったなとふと思ったので、あとで懐かしむために今やっているスタイルを書き残すことにした。個人的な開発に使っているもので、業務にこのフローを適用しているわけではないのと、Claude Code でうまくコードを書く方法ではなくその周辺の話。&lt;/p&gt;
&lt;h2&gt;全体感&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;開発は Dev Containers もしくは GitHub Codespaces&lt;/li&gt;
&lt;li&gt;ローカル開発では VSCode でメインの Claude Code + git worktree でいくつかの並列作業
&lt;ul&gt;
&lt;li&gt;GitHub Copilot もたまに使う&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Claude Code Actions でレビューと GitHub Issues の分析
&lt;ul&gt;
&lt;li&gt;レビューは CodeRabbit も使っている&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;HACK 的な設定は入れない&lt;/li&gt;
&lt;li&gt;コンテキストを充実させて解決することを意識する&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Dev Containers / Codespaces での開発&lt;/h2&gt;
&lt;p&gt;&lt;a href=&quot;https://yaakai.to/blog/2023/develop-in-codespaces-and-devcontainer/&quot;&gt;数年前に個人的な開発はすべてこれらに移行している。&lt;/a&gt;この地点からの変化として AI 系のツールの準備をどこで行うか、というのがあって今はいわゆる &lt;a href=&quot;https://github.com/yaakaito/env/&quot;&gt;dotfiles(envというリポジトリにしている)&lt;/a&gt; 側で管理していて、&lt;code&gt;setup.sh&lt;/code&gt; の流れの中で Claude Code をインストールしている。&lt;/p&gt;
&lt;p&gt;env にはいくつか Claude Code を便利に使うための設定やツールを含んでいて、自分の管理するどのリポジトリでも使えるようになっている。特に影響の大きいものだと全体の &lt;code&gt;CLAUDE.md&lt;/code&gt; つまり &lt;code&gt;~/.claude/CLAUDE.md&lt;/code&gt; に日本語で出力するよう設定している。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;## YOU MUST&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; ユーザーの入力に使用された言語にかかわらず、日本語で返答やコードの出力を行います&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Git Commit や Pull request を作成する際、指示がなければ日本語で内容を作成します&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## IMPORTANT&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; git commit を行うときは、指示がなければ [&lt;/span&gt;&lt;span&gt;commit.md&lt;/span&gt;&lt;span&gt;](&lt;/span&gt;&lt;span&gt;~/.claude/commands/commit.md&lt;/span&gt;&lt;span&gt;) のルールに従ってください&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;加えて、全体の &lt;code&gt;settings.json&lt;/code&gt; として、Read 系だったり、比較的安全なコマンドを許可している。issue なんかは価値観によるかもしれない。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;permissions&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;allow&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &quot;Bash(cat:*)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &quot;Bash(cp:*)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &quot;Bash(find:*)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &quot;Bash(head:*)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &quot;Bash(gh api:*)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &quot;Bash(gh issue create:*)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &quot;Bash(gh issue edit:*)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &quot;Bash(gh pr diff:*)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &quot;Bash(gh pr list:*)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &quot;Bash(gh pr view:*)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &quot;Bash(git add:*)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &quot;Bash(git branch:*)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &quot;Bash(git log:*)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &quot;Bash(git push:*)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &quot;Bash(git rm:*)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &quot;Bash(grep:*)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &quot;Bash(ls:*)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &quot;Bash(mkdir:*)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &quot;Bash(mv:*)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &quot;Bash(rg:*)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &quot;Bash(tail:*)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &quot;Bash(xargs grep:*)&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &quot;Read(~/.claude/commands/commit.md)&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;細かいツールやコマンドなんかはそれぞれが登場するときにまた紹介する。&lt;/p&gt;
&lt;h2&gt;ローカルの Claude Code での開発&lt;/h2&gt;
&lt;p&gt;Claude Code は基本的に VSCode の Terminal で動作させているが、連携の拡張機能は使っていない。最近は「Create New Terminal in Editor Area」でファイルなどと同じエディター領域で開いてしまって、そこで Claude Code を実行している。自分がターミナルとして操作したい場合は、これまで通りの Terminal を使ったり、同じようにエディター領域で開いたりとその時の気分でやっている。&lt;/p&gt;
&lt;p&gt;画面としてはこんな感じで作業している:&lt;/p&gt;

&lt;p&gt;青い部分がメインの Claude Code のエリアで、ここでは Claude Code と対話しながら進めたい変更を行う。基本的に 1 ラインしか動くことはない。コアになる機能の開発なんかはここで行っている。&lt;/p&gt;
&lt;p&gt;赤い部分はメインの変更点を Git diff で見る、もしくはサブタスクを Claude Code で並列しながら行う場合にそのターミナルをタブで出していて、これについては後述する。Git diff は VSCode についているソース管理のビューを使っていて、変更点をまとめて開くことが出来るので Claude Code が書いた内容をレビューするのに重宝している。このビューは worktree にも対応していて、裏でやっていた作業をまとめて確認する場合にも役立つ。&lt;/p&gt;
&lt;p&gt;緑の部分は普通にエクスプローラーや検索を使う。VSCode がいろいろ機能がついていて便利なターミナルと化している気がしなくもない。&lt;/p&gt;
&lt;p&gt;いわゆる右側の AI パネルは最近はほとんど使うことがなくなってしまったが、GitHub Copilot を使うときには出てきたりする。Copilot は主に、コンテキストの指定が面倒な JSX や CSS を範囲指定で渡して何かしらをしてもらう用途に使っている。Copilot 用の設定は特に入れていない。&lt;/p&gt;
&lt;h3&gt;git worktree を使った並列作業&lt;/h3&gt;
&lt;p&gt;そこまで対話が必要なく、ある程度の指示があれば Claude Code が自走できるものは git worktree を使って裏で作業させている。これをローカルの Claude Code でやっている理由は GHA で Max プランが適用されないからなのだが、最近適用出来るようになったので GHA で間に合うものはなるべくそちらで済ますようにしている。アイデアはあるけど一度やらせてみてどうなるか見てみたい、というものは結構あるので、そういった中規模程度のタスクを一度やらせてみるのがメインな使い方になっているかもしれない。&lt;/p&gt;
&lt;p&gt;実際の使い方については GitHub Issues を起点にしている。 env リポジトリに以下の流れを行う&lt;a href=&quot;https://github.com/yaakaito/env/blob/main/resolve-issue.zsh&quot;&gt;スクリプトを用意していて&lt;/a&gt;、実行すると worktree で実装が始まる。&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Issue の一覧を peco で表示&lt;/li&gt;
&lt;li&gt;選択した Issue とコメントの内容を取得&lt;/li&gt;
&lt;li&gt;Issue の内容から branch 名を生成&lt;/li&gt;
&lt;li&gt;branch 名で branch と worktree を作成&lt;/li&gt;
&lt;li&gt;worktree のディレクトリへ移動し、&lt;code&gt;.env&lt;/code&gt; のコピーや依存関係の準備をする&lt;/li&gt;
&lt;li&gt;Issue から prompt を作って、 &lt;code&gt;claude --dangerously-skip-permissions&lt;/code&gt; で実行&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;この一連の流れが以下のコマンドで実行できるようにしている:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$ resolve-issue&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;実際に使うときは新しいターミナルを開いて上記のコマンドを実行するという感じ。worktree は &lt;code&gt;.git/working-trees&lt;/code&gt; 以下に入れるようにしていて、こうしておくと管理が楽なのと、 VSCode のソース管理で Diff が見れるというメリットがある。&lt;/p&gt;
&lt;h4&gt;Claude Code Actions での Issue 作成のサポート&lt;/h4&gt;
&lt;p&gt;Issue をベースにするので当然 Issue を作る必要があるのと、あまりに内容があれだとうまくいかない。&lt;code&gt;-p&lt;/code&gt; オプションで実行しているわけではないので、都度指示を継ぎ足したりすることも出来るが、基本的には Issue の内容をある程度書く形でやっている。これを補助するために Issue に対して GHA で Claude Code Actions を実行して、Issue の内容を分析したり、レビューを行うようにしている。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;- &lt;/span&gt;&lt;span&gt;uses&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;anthropics/claude-code-action@beta&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  with&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    claude_code_oauth_token&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    github_token&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    direct_prompt&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        Issueとコメントの内容を確認し、コードベースをもとに実装は行わず分析して、実装計画を考えてください。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        その後、考えた実装計画から以下の点をユーザーにフィードバックしてください。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        - 変更内容の3行程度のサマリー&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        - 主に変更されるファイルと変更内容&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        - 不足している情報をユーザーに質問&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        英語で考えて、最終的なアウトプットだけ日本語で出力してください。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これで自分がわかっていない変数を AI に補完してもらったりして Issue を完成させている。Claude Code からのコメントが残り続けるとプロンプトになったときにノイズになりそうなので、 Issue の内容に満足したらコメントは削除するようにしている。実装できそうであればそのまま &lt;code&gt;@claude&lt;/code&gt; で実装してもらうこともある。&lt;/p&gt;
&lt;p&gt;以前は料金が気になって一部のものだけをラベルでトリガーしていたが、 Max プランが GHA にも適用できるようになってとりあえずこれを流しておけるようになったので便利。&lt;/p&gt;
&lt;h2&gt;リモートの Claude Code Actions での開発&lt;/h2&gt;
&lt;p&gt;ほとんど書いてしまったようなものだが、Max プランで動かせるようになってからは小さなタスクは積極的に &lt;code&gt;@claude&lt;/code&gt; で実装を行ってもらっている。欠点として修正がいくつが必要な場合に PR が荒れがちになるというのがある。適当に見切りをつけてローカルでの実行に移行したいとは思っているが、このあたりはまだこれといって方針は見えていない。&lt;/p&gt;
&lt;h2&gt;Claude Code の設定&lt;/h2&gt;
&lt;p&gt;ざっくり言えば、&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;CLAUDE.md&lt;/code&gt; は &lt;code&gt;/init&lt;/code&gt; で作成したものに日本語使用することだけを付け足している
&lt;ul&gt;
&lt;li&gt;明らかに操作が変わったときは合わせて更新している&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;Permissions はそれぞれのリポジトリに合わせて build test lint など普段の操作を許可している&lt;/li&gt;
&lt;li&gt;うまく開発を回すためにいくつか Custom Slash Command を作ったり Hook を設定したりしている&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;基本的にはあまり Claude Code 自体はカスタマイズはせずに、都度コンテキストを渡すことを意識している。&lt;/p&gt;
&lt;h3&gt;Custom Slash Command&lt;/h3&gt;
&lt;p&gt;リポジトリ単位で作っているものと、共通で使っているものがある。リポジトリ単位では、複雑になりがちなフローをまとめたりしている。例えば以下は playwright で Chrome 拡張のテストを実行するためのフローをまとめたもの:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;allowed-tools&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Bash(bun run build), Bash(bun run build-extension), Bash(bun run e2e)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;description&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Run end-to-end tests for the @repo/reader package with proper setup.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# Run E2E Tests&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Run end-to-end tests for the @repo/reader package with proper setup:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;1.&lt;/span&gt;&lt;span&gt; Navigate to reader package: &lt;/span&gt;&lt;span&gt;`cd /workspaces/repo/packages/reader`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;2.&lt;/span&gt;&lt;span&gt; Build web application: &lt;/span&gt;&lt;span&gt;`bun run build`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;3.&lt;/span&gt;&lt;span&gt; Build Chrome extension: &lt;/span&gt;&lt;span&gt;`bun run build-extension`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;4.&lt;/span&gt;&lt;span&gt; Run E2E tests: &lt;/span&gt;&lt;span&gt;`bun run e2e`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;5.&lt;/span&gt;&lt;span&gt; Check screenshots in &lt;/span&gt;&lt;span&gt;`screenshots/`&lt;/span&gt;&lt;span&gt; directory&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Show progress for each step and report test results with any failures highlighted.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;共通のコマンドとして env に以下のようなものを用意している、お気に入りは &lt;code&gt;workflow-fix&lt;/code&gt;。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yaakaito/env/blob/main/.claude/commands/commit.md&quot;&gt;/commit&lt;/a&gt; - コミットメッセージにここまでの AI とのやりとりを含めてコミットする&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yaakaito/env/blob/main/.claude/commands/reviews-fix.md&quot;&gt;/reviews-fix&lt;/a&gt; - 現在のブランチに対応する PR のレビューコメントを集めて修正を行う&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yaakaito/env/blob/main/.claude/commands/workflow-fix.md&quot;&gt;/workflow-fix&lt;/a&gt; - 現在のブランチに対応するワークフローを確認し、失敗していたら修正を行う&lt;/li&gt;
&lt;li&gt;&lt;a href=&quot;https://github.com/yaakaito/env/blob/main/.claude/commands/workflow-fix.md&quot;&gt;/workflow-fix-file&lt;/a&gt; - 指定したファイル名のワークフローを確認し、失敗していたら修正を行う&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;この辺りはリポジトリに依存しない、個人の開発スタイルだと考えて共通にしているが、&lt;code&gt;/commit&lt;/code&gt; は怪しい。例えば &lt;code&gt;/create-pr&lt;/code&gt; はリポジトリごとにある程度ルールが存在するはずなので共通にすることはないというのがわかりやすいが、このあたりの区別はまだまだ曖昧。それと、共通のものは &lt;code&gt;workflow-fix&lt;/code&gt; のように &lt;code&gt;workflow&lt;/code&gt; や &lt;code&gt;reviews&lt;/code&gt; を前にしている、数が多くなったときにこの方が絞り込みがしやすいように思う。&lt;/p&gt;
&lt;h3&gt;Hooks&lt;/h3&gt;
&lt;p&gt;編集時の format 、 commit 前の test と lint を設定しているのと、リポジトリに合わせて「bun を使っているのに npm を使おうとするのを止める」のような明らかな間違いを止める Hooks を設定している。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;hooks&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;PostToolUse&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                &quot;matcher&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;Write|Edit|MultiEdit&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                &quot;hooks&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        &quot;type&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;command&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        &quot;command&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;jq -r &apos;.tool_input.file_path | select(endswith(&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;.js&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;) or endswith(&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;.ts&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;) or endswith(&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;.jsx&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;) or endswith(&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;.tsx&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;)) or endswith(&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;.json&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;)) or endswith(&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;.css&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;))&apos; | xargs -r bun biome format --write&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                ]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;PreToolUse&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                &quot;matcher&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;Bash&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                &quot;hooks&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        &quot;type&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;command&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        &quot;command&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;jq -r &apos;.tool_input.command // &lt;/span&gt;&lt;span&gt;\&quot;\&quot;&lt;/span&gt;&lt;span&gt;&apos; | grep -q &lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;^git commit&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt; &amp;amp;&amp;amp; (bun run lint 1&amp;gt;/dev/null; [ $? -eq 1 ] &amp;amp;&amp;amp; echo &apos;{&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;decision&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;block&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;reason&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;Lint errors found. Fix them before committing.&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;}&apos; &amp;amp;&amp;amp; exit 2) || true&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        &quot;type&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;command&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        &quot;command&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;jq -r &apos;.tool_input.command // &lt;/span&gt;&lt;span&gt;\&quot;\&quot;&lt;/span&gt;&lt;span&gt;&apos; | grep -q &lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;^git commit&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt; &amp;amp;&amp;amp; (bun run test 1&amp;gt;/dev/null; [ $? -eq 1 ] &amp;amp;&amp;amp; echo &apos;{&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;decision&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;block&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;reason&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;Tests failed. Fix them before committing.&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;}&apos; &amp;amp;&amp;amp; exit 2) || true&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        &quot;type&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;command&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        &quot;command&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;jq -r &apos;.tool_input.command // &lt;/span&gt;&lt;span&gt;\&quot;\&quot;&lt;/span&gt;&lt;span&gt;&apos; | grep -qE &lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;^(npm|npx)&lt;/span&gt;&lt;span&gt;\\&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt; &amp;amp;&amp;amp; echo &apos;{&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;decision&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;block&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;reason&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;Consider using bun instead of npm/npx for better performance. Examples: npm install → bun install, npm run → bun run, npx → bun x&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;}&apos; &amp;amp;&amp;amp; exit 2 || true&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        &quot;type&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;command&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                        &quot;command&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;jq -r &apos;.tool_input.command // &lt;/span&gt;&lt;span&gt;\&quot;\&quot;&lt;/span&gt;&lt;span&gt;&apos; | grep -qE &lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;^bunx @biomejs/biome check&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt; &amp;amp;&amp;amp; echo &apos;{&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;decision&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;block&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;reason&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;Use bun biome lint instead of bunx @biomejs/biome check for better performance and consistency.&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;}&apos; &amp;amp;&amp;amp; exit 2 || true&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                ]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Pull request のレビュー環境&lt;/h2&gt;
&lt;p&gt;ローカルの Claude Code で作ったものと、Claude Code Actions で作ったものでフローが異なる。&lt;/p&gt;
&lt;p&gt;前者はコードレビューだけ行っていて、Claude Code Actions と CodeRabbit、たまに GitHub Copilot を使っている。レビューされた内容は、web 上で不要なものは Resolve したりしつつ、前述の &lt;code&gt;/reviews-fix&lt;/code&gt; を使って修正を行っている。&lt;/p&gt;
&lt;p&gt;後者の Claude Code Actions で作成したコードは、ローカルの開発環境を経由せずにマージまで持っていきたかったので、作れるものに関しては Cloudflare の Preview URLs を使って確認用の環境を作ってみている、そうでないものは Codespaces で起動したりとか。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://developers.cloudflare.com/workers/configuration/previews/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Preview URLs&lt;/h3&gt;&lt;p&gt;Preview URLs allow you to preview new versions of your project without deploying it to production.&lt;/p&gt;&lt;span&gt;developers.cloudflare.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Claude Code Actions ないしリモートの AI (コンテナ) で作られたものの動作をどう確認するのかというのは課題感としてあり、特に出力に AI が絡むものはテストコードなどでは保証が難しいのをどうしていくのがいいかあまりわかっていない。&lt;/p&gt;
&lt;h2&gt;雑感&lt;/h2&gt;
&lt;p&gt;個々のリポジトリで多少の違いはありつつも、全体的には Claude Code と GitHub Issues を中心に据えたスタイルになってきているように思う。
自分は積極的にコードが書きたいとかコード書いて楽しいと思っているタイプではないのもあり、モチベが低くとも一旦雑にでも Issue にさえしてしまえば、手をつけられる環境に近づいたことに価値を感じている。&lt;/p&gt;
&lt;p&gt;やりたいと考えてはいるがうまくできていない、まだやっていないことを書き残して終わる。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;人間と AI 向けのドキュメントを Markdown で残すようにしているものの AI が書くとどうしても淡々とした説明になってしまう、 Why を残すのであれば人間がうまく挟まる必要がありそう&lt;/li&gt;
&lt;li&gt;gemini-cli も導入して、 Issue に対して評価を行う際に Gemini と話し合ってほしい&lt;/li&gt;
&lt;li&gt;PR レビューが Bot で荒れがちになるのを整理したいが特にアイデアはなし&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Claude Code の Hook で format lint test を実行する</title><link>https://yaakai.to/note/88/</link><guid isPermaLink="true">https://yaakai.to/note/88/</guid><pubDate>Thu, 10 Jul 2025 11:18:18 GMT</pubDate><content:encoded>&lt;p&gt;Claude Code に追加された Hook 機能を使って、これまで CLAUDE.md や githooks で実現していたツールの実行を Claude Code の設定に移してみる。format は公式でも利用例として紹介されている。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://docs.anthropic.com/en/docs/claude-code/hooks&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Hooks reference - Claude Code Docs&lt;/h3&gt;&lt;p&gt;Reference for Claude Code hook events, configuration schema, JSON input/output formats, exit codes, async hooks, HTTP hooks, prompt hooks, and MCP tool hooks.&lt;/p&gt;&lt;span&gt;docs.anthropic.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;今回は format は編集時 PostToolUse 、lint と test は &lt;code&gt;git commit&lt;/code&gt; の PreToolUse で実行したいと考え、最終的には次のように設定した:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;hooks&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;PostToolUse&quot;&lt;/span&gt;&lt;span&gt;: [{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &quot;matcher&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;Write|Edit|MultiEdit&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &quot;hooks&quot;&lt;/span&gt;&lt;span&gt;: [{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;type&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;command&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;command&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;jq -r &apos;.tool_input.file_path | select(endswith(&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;.js&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;) or endswith(&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;.ts&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;) or endswith(&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;.jsx&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;) or endswith(&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;.tsx&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;)) or endswith(&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;.json&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;)) or endswith(&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;.css&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;))&apos; | xargs -r bun biome format --write&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      }]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;PreToolUse&quot;&lt;/span&gt;&lt;span&gt;: [{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &quot;matcher&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;Bash&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &quot;hooks&quot;&lt;/span&gt;&lt;span&gt;: [{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;type&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;command&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;command&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;jq -r &apos;.tool_input.command // &lt;/span&gt;&lt;span&gt;\&quot;\&quot;&lt;/span&gt;&lt;span&gt;&apos; | grep -q &lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;^git commit&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt; &amp;amp;&amp;amp; (bun run lint 1&amp;gt;/dev/null; [ $? -eq 1 ] &amp;amp;&amp;amp; echo &apos;{&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;decision&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;block&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;reason&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;Lint errors found. Fix them before committing.&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;}&apos; &amp;amp;&amp;amp; exit 2) || true&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      }, {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;type&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;command&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;command&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;jq -r &apos;.tool_input.command // &lt;/span&gt;&lt;span&gt;\&quot;\&quot;&lt;/span&gt;&lt;span&gt;&apos; | grep -q &lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;^git commit&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt; &amp;amp;&amp;amp; (bun run test 1&amp;gt;/dev/null; [ $? -eq 1 ] &amp;amp;&amp;amp; echo &apos;{&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;decision&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;block&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;reason&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;Tests failed. Fix them before committing.&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;}&apos; &amp;amp;&amp;amp; exit 2) || true&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      }]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;format と lint には biome を使っていて、 test は monorepo でパッケージごとに異なるツールを利用している。設定のポイントとして、例えば &lt;code&gt;biome lint&lt;/code&gt; の Lint Error は Exit code が 1 なのでそのままでは &lt;code&gt;git commit&lt;/code&gt; が止まらない。そのため PreToolUse では、コマンドがエラーだったときに詳細な JSON と Exit code 2 を返すようにしている。少し読みやすくするとこうなる:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;jq&lt;/span&gt;&lt;span&gt; -r&lt;/span&gt;&lt;span&gt; &apos;.tool_input.command // \&quot;\&quot;&apos;&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt; grep&lt;/span&gt;&lt;span&gt; -q&lt;/span&gt;&lt;span&gt; \&quot;&lt;/span&gt;&lt;span&gt;^git&lt;/span&gt;&lt;span&gt; commit&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;amp;&amp;amp; (&lt;/span&gt;&lt;span&gt;bun&lt;/span&gt;&lt;span&gt; run&lt;/span&gt;&lt;span&gt; lint&lt;/span&gt;&lt;span&gt; 1&amp;gt;&lt;/span&gt;&lt;span&gt;/dev/null&lt;/span&gt;&lt;span&gt;; &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        [ &lt;/span&gt;&lt;span&gt;$?&lt;/span&gt;&lt;span&gt; -eq&lt;/span&gt;&lt;span&gt; 1&lt;/span&gt;&lt;span&gt; ] &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;amp;&amp;amp; &lt;/span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &apos;{\&quot;decision\&quot;:\&quot;block\&quot;,\&quot;reason\&quot;:\&quot;Lint errors found. Fix them before committing.\&quot;}&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;amp;&amp;amp; &lt;/span&gt;&lt;span&gt;exit&lt;/span&gt;&lt;span&gt; 2&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; true&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これで今のところうまく動いているよう見える。これが便利かはまだ体感できていないので、すぐにやめる可能性はある。&lt;/p&gt;</content:encoded></item><item><title>Claude Code の PreToolUse Hook で誤ったパッケージマネージャーの利用を止める</title><link>https://yaakai.to/note/87/</link><guid isPermaLink="true">https://yaakai.to/note/87/</guid><pubDate>Thu, 10 Jul 2025 11:09:07 GMT</pubDate><content:encoded>&lt;p&gt;Claude Code に追加された Hook 機能を使って「bun を利用しているのに npm を使おうとする」というような動作を防止することが出来そうだったので試した。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://docs.anthropic.com/en/docs/claude-code/hooks&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Hooks reference - Claude Code Docs&lt;/h3&gt;&lt;p&gt;Reference for Claude Code hook events, configuration schema, JSON input/output formats, exit codes, async hooks, HTTP hooks, prompt hooks, and MCP tool hooks.&lt;/p&gt;&lt;span&gt;docs.anthropic.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;PreToolUse では設定されたコマンドが exit code 2 を返すと、実行しようとしていたツール利用をブロックすることができる。さらに JSON で詳しい情報を伝えることで Claude Code が必要なリカバリを行うため、これに合わせて次の設定を試した:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;hooks&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;PreToolUse&quot;&lt;/span&gt;&lt;span&gt;: [{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &quot;matcher&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;Bash&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &quot;hooks&quot;&lt;/span&gt;&lt;span&gt;: [{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;type&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;command&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;command&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;jq -r &apos;.tool_input.command // &lt;/span&gt;&lt;span&gt;\&quot;\&quot;&lt;/span&gt;&lt;span&gt;&apos; | grep -qE &lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;^(npm|npx)&lt;/span&gt;&lt;span&gt;\\&lt;/span&gt;&lt;span&gt;s&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt; &amp;amp;&amp;amp; echo &apos;{&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;decision&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;block&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;reason&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;Consider using bun instead of npm/npx for better performance. Examples: npm install → bun install, npm run → bun run, npx → bun x&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;}&apos; &amp;amp;&amp;amp; exit 2 || true&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      }]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;少し読みやすくするとこんな感じ:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;jq -r &apos;.tool_input.command // \&quot;\&quot;&apos; | grep -qE \&quot;^(npm|npx)\\s\&quot; &amp;amp;&amp;amp;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    echo &apos;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;         \&quot;decision\&quot;:\&quot;block\&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;         \&quot;reason\&quot;:\&quot;Consider using bun instead of npm/npx for better performance. Examples: npm install → bun install, npm run → bun run, npx → bun x\&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&apos; &amp;amp;&amp;amp; exit 2&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;|| true&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;雑ではあるが、これで誤った npm の実行をブロックすることができた。&lt;/p&gt;
</content:encoded></item><item><title>Biome v2 の GritQL での Linter Plugins を試す</title><link>https://yaakai.to/note/86/</link><guid isPermaLink="true">https://yaakai.to/note/86/</guid><pubDate>Wed, 25 Jun 2025 13:50:37 GMT</pubDate><content:encoded>&lt;p&gt;Biome v2 がリリースされ、β 版の機能として Linter Plugins が追加された。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://biomejs.dev/blog/biome-v2/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Biome v2—codename: Biotype&lt;/h3&gt;&lt;span&gt;biomejs.dev&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://biomejs.dev/linter/plugins/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Linter Plugins&lt;/h3&gt;&lt;span&gt;biomejs.dev&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;実装は GritQL によるパターンマッチングをベースに作られていて、現状 Fix はできず検出のみとなっている。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://docs.grit.io/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Grit - Declarative Code Search and Transformation&lt;/h3&gt;&lt;span&gt;docs.grit.io&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;まだ β 版という前提はありつつも、 ESLint でやっているような Monorepo 内での期待しない import の検出ができれば乗り換え&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;ができるなと思い、以下のようなルールを試した。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;or {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    `import $_ from $path`,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    `import { $_ } from $path`,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    `import * as $_ from $path`,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;} where {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    $path &amp;lt;: `&apos;@project/theModule&apos;`,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    register_diagnostic(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        span = $path,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        message = &quot;このパッケージから theModule へ依存することは出来ません。&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        severity = &quot;error&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これを同じく v2 で追加された Monorepo サポートと兼用しパッケージ事に設定することで、泥臭いながらも期待するものはある程度実現できる。ただ、Side Effect Import の場合にうまくいかず、以下が動作うまくしなくてハマっていた。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;`import $path` where {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    $path &amp;lt;: `&apos;@project/theModule&apos;`,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    register_diagnostic(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        span = $path,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        message = &quot;このパッケージから theModule へ依存することは出来ません。&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        severity = &quot;error&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;$path&lt;/code&gt; 周りの記述の仕方をいくつか試したが、 &lt;code&gt;biome search&lt;/code&gt; でもマッチングがうまくいかなかった。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$ npx biome search &apos;`import $path`&apos; &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Searched 57 files in 95ms. Found 0 matches.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一方で以下は動作するので、単純に実装が間に合っていないだけなのかもしれないが詳しくは調べていない。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;`import &apos;$_&apos;` as $import where {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    register_diagnostic(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        span = $import,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        message = &quot;Side Effect Import は禁止されています。&quot;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        severity = &quot;error&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    )&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Refs&lt;/h2&gt;
&lt;p&gt;このスライドで Rust 系のツールも Plugin システムに対応しつつあることを知った&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://speakerdeck.com/unvalley/typescript-linters&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Plugin System in Rust based  JavaScript / TypeScript Linters&lt;/h3&gt;&lt;p&gt;TSKaigi 2025 Session 30 minutes

biome: https://github.com/biomejs/biome
deno_lint: https://github.com/denoland/deno
oxc: https://github.com/oxc-pro&amp;amp;amp;hellip;&lt;/p&gt;&lt;span&gt;speakerdeck.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;section&gt;&lt;h2&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;&lt;a href=&quot;https://yaakai.to/blog/2025/cline-repository-refactoring/&quot;&gt;https://yaakai.to/blog/2025/cline-repository-refactoring/&lt;/a&gt; でこのカスタムルールが書きたくて Biome → ESLint にした &lt;a href=&quot;#user-content-fnref-1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded></item><item><title>Dev Container で Chrome 拡張を Playwright でテストする</title><link>https://yaakai.to/note/85/</link><guid isPermaLink="true">https://yaakai.to/note/85/</guid><pubDate>Tue, 17 Jun 2025 20:50:46 GMT</pubDate><content:encoded>&lt;p&gt;Playwright は Chrome 拡張もテスト出来るが、Dev Container で動かす際は CI と同様に install 時に &lt;code&gt;--with-deps&lt;/code&gt; が必要。というだけの話ではあるのだが、地味にハマったのでメモに残す。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://playwright.dev/docs/chrome-extensions&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Chrome extensions&lt;/h3&gt;&lt;span&gt;playwright.dev&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$ npx playwright install --with-deps chromium&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;あとは公式にあるように &lt;code&gt;channel: &apos;chromium&apos;&lt;/code&gt;  でテスト用のコンテキストを作ればよい。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;context &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; chromium.&lt;/span&gt;&lt;span&gt;launchPersistentContext&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt;, {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  channel: &lt;/span&gt;&lt;span&gt;&apos;chromium&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  args: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    `--disable-extensions-except=${&lt;/span&gt;&lt;span&gt;pathToExtension&lt;/span&gt;&lt;span&gt;}`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    `--load-extension=${&lt;/span&gt;&lt;span&gt;pathToExtension&lt;/span&gt;&lt;span&gt;}`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;公式を読めば大体解決する話なのだが、 &lt;a href=&quot;https://yaakai.to/blog/2025/fixing-roo-code-browser-issues-in-dev-container/&quot;&gt;Cline 向けに Chromium を Dev Container に用意していた&lt;/a&gt;影響か、Claude Code が最初の動くものとして次のように設定してきて少しハマった。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;context &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; chromium.&lt;/span&gt;&lt;span&gt;launchPersistentContext&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt;, {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  headless: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  args: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    `--disable-extensions-except=${&lt;/span&gt;&lt;span&gt;pathToExtension&lt;/span&gt;&lt;span&gt;}`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    `--load-extension=${&lt;/span&gt;&lt;span&gt;pathToExtension&lt;/span&gt;&lt;span&gt;}`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  executablePath: &lt;/span&gt;&lt;span&gt;&apos;/usr/bin/chromium&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これでも動作はするのだが(公式にも記載はある)、Chrome 拡張でないテストは &lt;code&gt;playwright install chromium&lt;/code&gt; で入れたものを使うので Chromium が 2 つ必要になる。これをまとめる方法がよくわからなかったが  &lt;code&gt;channel&lt;/code&gt; を使うとこれを解決できる、公式をちゃんと読もう。&lt;/p&gt;
&lt;p&gt;以下は最小構成のリポジトリ:&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/yaakaito/playwright-chrome-extension&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;GitHub - yaakaito/playwright-chrome-extension&lt;/h3&gt;&lt;p&gt;Contribute to yaakaito/playwright-chrome-extension development by creating an account on GitHub.&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</content:encoded></item><item><title>/command で Claude Code とのやり取りを Commit に残す</title><link>https://yaakai.to/note/84/</link><guid isPermaLink="true">https://yaakai.to/note/84/</guid><pubDate>Mon, 09 Jun 2025 13:00:40 GMT</pubDate><content:encoded>&lt;p&gt;AI を使ってコードを書いているときに、そのやりとりを何かしらの形で残せると良いよねという課題感があったが、コミットメッセージに残せばよいというアイデアを見かけてこれを試している。&lt;/p&gt;
&lt;p&gt;これを毎回そのようにしてと頼むのは面倒なので、 Claude Code の Slash Comannd として用意してそれを使っている。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;Create a commit that includes &lt;/span&gt;&lt;span&gt;**all conversation**&lt;/span&gt;&lt;span&gt; between the user and yourself up to this point, following Conventional Commits format:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;type&amp;gt;[optional scope]: &amp;lt;description&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;[optional body]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;prompt: &amp;lt;user&apos;s input prompt&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;----&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;your response&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;----&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;prompt: &amp;lt;user&apos;s input prompt&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;----&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;your response&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;[optional footer(s)]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Breaking changes: Use ! after type/scope or add BREAKING CHANGE: footer&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Common types: feat, fix, docs, style, refactor, test, chore, ci, build, perf&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Scope examples: (api), (ui), (auth), (parser)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Description: Present tense, lowercase, under 50 chars, no period&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; Separate conversation exchanges with ----&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これを &lt;code&gt;~/.claude/commands/commit.md&lt;/code&gt; として保存しておくと &lt;code&gt;/user:commit&lt;/code&gt; が使えるようになる。ルール化するなら &lt;code&gt;project:&lt;/code&gt; にしてもいいと思う。&lt;/p&gt;
&lt;p&gt;実際にできるメッセージは自分の入力はそのまま残る一方で、 Claude Code 側のものは結構要約されたりする。すべてそのまま残ってほしい気もするが、それはそれで膨大になりそうなので今のところ必要十分という感じ。&lt;/p&gt;
&lt;h2&gt;Refs&lt;/h2&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://docs.anthropic.com/ja/docs/claude-code/tutorials#%E5%80%8B%E4%BA%BA%E7%94%A8%E3%82%B9%E3%83%A9%E3%83%83%E3%82%B7%E3%83%A5%E3%82%B3%E3%83%9E%E3%83%B3%E3%83%89%E3%82%92%E4%BD%9C%E6%88%90%E3%81%99%E3%82%8B&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;docs.anthropic.com&lt;/h3&gt;&lt;span&gt;docs.anthropic.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;blockquote&gt;&lt;p&gt;使ったプロンプトをコミットログにぶち込んでおくのCloudflareの人も実践してた&lt;a href=&quot;https://t.co/Q0WSOsfPcW&quot;&gt;https://t.co/Q0WSOsfPcW&lt;/a&gt; &lt;a href=&quot;https://t.co/SKXx0Ytmwz&quot;&gt;https://t.co/SKXx0Ytmwz&lt;/a&gt;&lt;/p&gt;— Iaiso (@laiso) &lt;a href=&quot;https://twitter.com/laiso/status/1931410761299239341?ref_src=twsrc%5Etfw&quot;&gt;June 7, 2025&lt;/a&gt;&lt;/blockquote&gt; </content:encoded></item><item><title>git worktree で Claude Code の並列稼働を試す</title><link>https://yaakai.to/note/78/</link><guid isPermaLink="true">https://yaakai.to/note/78/</guid><pubDate>Sat, 24 May 2025 11:55:13 GMT</pubDate><content:encoded>&lt;p&gt;Claude Code Best Practices でも紹介されている方法で、 git worktree でコードベースを複製してそれぞれで Claude Code を実行する。自分は趣味の範囲は基本的に Dev Container でコーディングを行っているので、複数 clone してそれぞれでイメージを作って、とするのは面倒でこの方法を採用した。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://www.anthropic.com/engineering/claude-code-best-practices&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Claude Code overview - Claude Code Docs&lt;/h3&gt;&lt;p&gt;Claude Code is an agentic coding tool that reads your codebase, edits files, runs commands, and integrates with your development tools. Available in your terminal, IDE, desktop app, and browser.&lt;/p&gt;&lt;span&gt;www.anthropic.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://git-scm.com/docs/git-worktree&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Git - git-worktree Documentation&lt;/h3&gt;&lt;span&gt;git-scm.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;ブランチ込みで作りたいときは、 &lt;code&gt;-b&lt;/code&gt; でブランチを作成できる。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;git worktree add ~/feature/foo-bar -b feature/foo-bar&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Dev Container のマウント先は  &lt;code&gt;/workspace/[reponame]&lt;/code&gt; になっているはずで、ここから &lt;code&gt;../feature/foo-bar&lt;/code&gt; で &lt;code&gt;/workspace/feature/foo-bar&lt;/code&gt; は作れないので &lt;code&gt;~&lt;/code&gt; へ worktree を作っている。
worktree を作ると、そこに指定したブランチが co されているので、 cd して Claude Code で作業する。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$ cd ~/feature/foo-bar &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;$ claude&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最近は「Terminal: Create New Terminal in Editor Area」でエディターの領域でターミナルを開いてしまい、ファイルのタブと同じレベルでターミナルを管理して Claude Code を実行しているので、worktree 分このタブを増やせば良い。&lt;/p&gt;
&lt;p&gt;作業が終わったら remove で削除する。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$ git worktree remove ~/feature/foo-bar&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content:encoded></item><item><title>このブログを Remote MCP Server にして検索できるようにしてみる</title><link>https://yaakai.to/blog/2025/blog-remote-mcp-server/</link><guid isPermaLink="true">https://yaakai.to/blog/2025/blog-remote-mcp-server/</guid><pubDate>Tue, 20 May 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;技術的な素振りの側面が大きいが、比較的新し目のことを書いたりしているので MCP でブログの内容が検索できるとあとで実装に使いたいときに便利そうだと思ったので、このブログを Remote MCP Server にしてみた。&lt;/p&gt;
&lt;p&gt;サンプルに毛が生えた程度の大したものではないが、コードはこのブログのリポジトリに含まれている:&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/yaakaito/yaakaito/tree/main/mcp-server&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;yaakaito/mcp-server at main · yaakaito/yaakaito&lt;/h3&gt;&lt;p&gt;Contribute to yaakaito/yaakaito development by creating an account on GitHub.&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;Cloudflare で Remote MCP Server を立てる&lt;/h2&gt;
&lt;p&gt;MCP といえばローカルで Node なんかで動いているイメージが強いが(もはやそうでもない？)、プロトコルとしては HTTP 経由でも利用できるようになっていて、先日 Cloudflare Workers でこれを動かす方法が公式から発表されたので、これに従っていく。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://blog.cloudflare.com/remote-model-context-protocol-servers-mcp/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Build and deploy Remote Model Context Protocol (MCP) servers to Cloudflare&lt;/h3&gt;&lt;p&gt;You can now build and deploy remote MCP servers to Cloudflare, and we handle the hard parts of building remote MCP servers for you. Unlike local MCP servers you may have previously used, remote MCP servers are Internet-accessible. People simply sign in and grant permissions to MCP clients using familiar authorization flows.&lt;/p&gt;&lt;span&gt;blog.cloudflare.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://developers.cloudflare.com/agents/guides/remote-mcp-server/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Build a Remote MCP server&lt;/h3&gt;&lt;p&gt;This guide will show you how to deploy your own remote MCP server on Cloudflare using Streamable HTTP transport, the current MCP specification standard. You have two options:&lt;/p&gt;&lt;span&gt;developers.cloudflare.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;少し前のテンプレートだと OAuth 認証がセットになっていて少しだるかった記憶があるが、最近試したら &lt;code&gt;remote-mcp-authless&lt;/code&gt; というのになっていたので、ありがたくこれを使う。いつの間にかドキュメントが Bun のコマンドにも対応していた。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; bun&lt;/span&gt;&lt;span&gt; create&lt;/span&gt;&lt;span&gt; cloudflare@latest&lt;/span&gt;&lt;span&gt; my-mcp-server&lt;/span&gt;&lt;span&gt; --template=cloudflare/ai/demos/remote-mcp-authless&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ざっくり言えば Cloudflare の提供する &lt;code&gt;agents/mcp&lt;/code&gt; で &lt;code&gt;@modelcontextprotocol/sdk&lt;/code&gt; のサーバーをラップするとイメージで実装されている。それ以外は &lt;code&gt;@modelcontextprotocol/sdk&lt;/code&gt; と同様に実装できるはずなので、それの詳しい説明は割愛する。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://agents.cloudflare.com/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Cloudflare Agents&lt;/h3&gt;&lt;p&gt;Build agents on Cloudflare—the platform designed for durable execution, serverless inference, and pricing that scales up (and down).&lt;/p&gt;&lt;span&gt;agents.cloudflare.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/modelcontextprotocol/typescript-sdk&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;GitHub - modelcontextprotocol/typescript-sdk: The official TypeScript SDK for Model Context Protocol servers and clients&lt;/h3&gt;&lt;p&gt;The official TypeScript SDK for Model Context Protocol servers and clients - modelcontextprotocol/typescript-sdk&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;検索できる Tool を実装する&lt;/h2&gt;
&lt;p&gt;中身については、前に作ったこのブログの関連記事用の Vectorize を流用している。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://yaakai.to/blog/2025/cf-vectorize-and-workes-ai/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Cloudflare Vectorize と Workers AI で関連する記事を作ってみる - yaakai.to&lt;/h3&gt;&lt;p&gt;📚 Cloudflare Vectorize と Workers AI で関連する記事を作ってみる | yaakai.to&lt;/p&gt;&lt;span&gt;yaakai.to&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Vectorize は Workers から使えるベクトルデータベースで、手軽にベクトルの保存や検索を行うことができる。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://developers.cloudflare.com/vectorize/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Cloudflare Vectorize&lt;/h3&gt;&lt;p&gt;Vectorize is a globally distributed vector database that enables you to build full-stack, AI-powered applications with Cloudflare Workers. Vectorize makes querying embeddings — representations of values or objects like text, images, audio that are designed to be consumed by machine learning models and semantic search algorithms — faster, easier and more affordable.&lt;/p&gt;&lt;span&gt;developers.cloudflare.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;詳しい使い方や現状の実装は興味があれば前の記事を見てほしい、ここで作る MCP サーバーには、Vectroize からベクトル検索を行う Tool を実装しただけ。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;// 記事を検索するツールを追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.server.&lt;/span&gt;&lt;span&gt;tool&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;yaakaito_search_articles&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;キーワードや文章を入力して、関連する記事を検索します。&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        query: z.&lt;/span&gt;&lt;span&gt;string&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;describe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;検索キーワードまたは文章&quot;&lt;/span&gt;&lt;span&gt;),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        limit: z.&lt;/span&gt;&lt;span&gt;number&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;optional&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;describe&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;取得する記事の最大数（デフォルト: 3）&quot;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    async&lt;/span&gt;&lt;span&gt; ({ &lt;/span&gt;&lt;span&gt;query&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;limit&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; 3&lt;/span&gt;&lt;span&gt; }) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        try&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            const&lt;/span&gt;&lt;span&gt; env&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; this&lt;/span&gt;&lt;span&gt;.env &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; Env&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            // 検索クエリをベクトル化&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            const&lt;/span&gt;&lt;span&gt; response&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; env.&lt;/span&gt;&lt;span&gt;AI&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;@cf/baai/bge-base-en-v1.5&quot;&lt;/span&gt;&lt;span&gt;, {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                text: query,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            const&lt;/span&gt;&lt;span&gt; embedding&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; response.data[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            // 類似記事を検索&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            const&lt;/span&gt;&lt;span&gt; matches&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; env.&lt;/span&gt;&lt;span&gt;VECTORIZE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;query&lt;/span&gt;&lt;span&gt;(embedding, {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                topK: limit,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                returnValues: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                returnMetadata: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            // 検索結果を整形&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            const&lt;/span&gt;&lt;span&gt; searchResults&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; matches.matches.&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;((&lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; any&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; ({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                id: match.id,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                title: match.metadata?.title &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; string&lt;/span&gt;&lt;span&gt; ||&lt;/span&gt;&lt;span&gt; &quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                content: match.metadata?.content &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; string&lt;/span&gt;&lt;span&gt; ||&lt;/span&gt;&lt;span&gt; &quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                tags: (match.metadata?.tags &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; string&lt;/span&gt;&lt;span&gt;[]) &lt;/span&gt;&lt;span&gt;||&lt;/span&gt;&lt;span&gt; [],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                path: match.metadata?.path &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; string&lt;/span&gt;&lt;span&gt; ||&lt;/span&gt;&lt;span&gt; &quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                createdAt: match.metadata?.createdAt &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; string&lt;/span&gt;&lt;span&gt; ||&lt;/span&gt;&lt;span&gt; &quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                updatedAt: match.metadata?.updatedAt &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; string&lt;/span&gt;&lt;span&gt; ||&lt;/span&gt;&lt;span&gt; &quot;&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                similarity: match.score&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            }));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            return&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                content: [{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    type: &lt;/span&gt;&lt;span&gt;&quot;text&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    text: &lt;/span&gt;&lt;span&gt;JSON&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;stringify&lt;/span&gt;&lt;span&gt;(searchResults)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                }]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            };&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        } &lt;/span&gt;&lt;span&gt;catch&lt;/span&gt;&lt;span&gt; (error) { }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ベクトル化には引き続き &lt;code&gt;@cf/baai/bge-base-en-v1.5&lt;/code&gt; を使っているが、そこまで困っていない。
Tool を実装したらいつも通り Workers へデプロイし、今回は VSCode の Copilot に MCP Server を設定する。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;// settings.json&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;mcp&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;servers&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &quot;yaakaito&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;url&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;https://{worker}.workers.dev/mcp&quot;&lt;/span&gt;&lt;span&gt; // or /sse&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;もしくは &lt;code&gt;MCP: Add Server...&lt;/code&gt; とかでもできると思う、適当に起動なり再起動をしたらツールが使えるようになるので適当に質問を投げてみる。&lt;/p&gt;

&lt;h2&gt;Resources としてブログ記事を追加する&lt;/h2&gt;
&lt;p&gt;MCP には Resources という機能もあり、Tool とはまた違う形で情報を提供することができる。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://modelcontextprotocol.io/docs/concepts/resources&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Resources - Model Context Protocol&lt;/h3&gt;&lt;span&gt;modelcontextprotocol.io&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;今回の場合は、Resources に記事を配置しておくと、Claude Desktop でこんな感じに表示されて、プロンプトに本文を含めることができる。&lt;/p&gt;

&lt;p&gt;Claude Desktop は Integrations が使えると URL のみでよいはずだが、Pro (以下) の場合は現状 &lt;code&gt;mcp-remote&lt;/code&gt; を経由して設定する必要がある。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;mcpServers&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;yaakaito&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &quot;command&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;npx&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &quot;args&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;mcp-remote&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;https://{url}.workers.dev/sse&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      ]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/geelen/mcp-remote&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;GitHub - geelen/mcp-remote&lt;/h3&gt;&lt;p&gt;Contribute to geelen/mcp-remote development by creating an account on GitHub.&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;実装としては、デプロイ時に記事をコード化して Worker としてビルドしてしまうという方法にしている。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;for&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; article&lt;/span&gt;&lt;span&gt; of&lt;/span&gt;&lt;span&gt; articles) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    this&lt;/span&gt;&lt;span&gt;.server.&lt;/span&gt;&lt;span&gt;resource&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        article.title,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        `articles://${&lt;/span&gt;&lt;span&gt;article&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;}.md`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        { mimeType: &lt;/span&gt;&lt;span&gt;&apos;text/plain&apos;&lt;/span&gt;&lt;span&gt;, description: &lt;/span&gt;&lt;span&gt;`${&lt;/span&gt;&lt;span&gt;article&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;title&lt;/span&gt;&lt;span&gt;} - ${&lt;/span&gt;&lt;span&gt;article&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;content&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;substring&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;50&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;}`&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            return&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                contents: [{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    uri: &lt;/span&gt;&lt;span&gt;`article://${&lt;/span&gt;&lt;span&gt;article&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;id&lt;/span&gt;&lt;span&gt;}.md`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    mimeType: &lt;/span&gt;&lt;span&gt;&apos;text/plain&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    text: article.content,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                }]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            };&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    );&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;記事が増えるともう少し考えたほうがよさそうで、理想としては subscribe に対応するのがいいだろうが、そんな頻繁に増えるものでもないので、一旦はこれにしている。&lt;/p&gt;
&lt;h2&gt;デバッグ&lt;/h2&gt;
&lt;p&gt;開発をサポートするツールとして MCP Inspector と AI Playground が紹介されている。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/modelcontextprotocol/inspector&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;GitHub - modelcontextprotocol/inspector: Visual testing tool for MCP servers&lt;/h3&gt;&lt;p&gt;Visual testing tool for MCP servers. Contribute to modelcontextprotocol/inspector development by creating an account on GitHub.&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://playground.ai.cloudflare.com/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Attention Required! | Cloudflare&lt;/h3&gt;&lt;span&gt;playground.ai.cloudflare.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;自分は今回前者だけ使った、ただこれを Dev Container で動かそうとするとパッとはうまくいかなかったので、ホスト側で実行した。多分これと似たような問題でおそらく解決方法はあるが、面倒なのでやっていない。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://yaakai.to/note/28/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Dev Container での wrangler dev で dev-server に繋がらない場合の対処 - yaakai.to&lt;/h3&gt;&lt;p&gt;🖊 Dev Container での wrangler dev で dev-server に繋がらない場合の対処 | yaakai.to&lt;/p&gt;&lt;span&gt;yaakai.to&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</content:encoded></item><item><title>Claude Code を GitHub Actions で動かす</title><link>https://yaakai.to/note/75/</link><guid isPermaLink="true">https://yaakai.to/note/75/</guid><pubDate>Sun, 20 Apr 2025 21:34:01 GMT</pubDate><content:encoded>&lt;p&gt;Claude Code は &lt;code&gt;claude -p&lt;/code&gt; でユーザーとの対話なしに一度だけクエリを実行することができる。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://docs.anthropic.com/ja/docs/agents-and-tools/claude-code/overview#cli&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Claude Code overview - Claude Code Docs&lt;/h3&gt;&lt;p&gt;Claude Code is an agentic coding tool that reads your codebase, edits files, runs commands, and integrates with your development tools. Available in your terminal, IDE, desktop app, and browser.&lt;/p&gt;&lt;span&gt;docs.anthropic.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;これを &lt;code&gt;ANTHROPIC_API_KEY&lt;/code&gt; を設定した環境で実行することで CI として動作させることが可能になる(直近でこの部分にバグがあったが修正された)。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://docs.anthropic.com/ja/docs/agents-and-tools/claude-code/overview#ci&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Claude Code overview - Claude Code Docs&lt;/h3&gt;&lt;p&gt;Claude Code is an agentic coding tool that reads your codebase, edits files, runs commands, and integrates with your development tools. Available in your terminal, IDE, desktop app, and browser.&lt;/p&gt;&lt;span&gt;docs.anthropic.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/anthropics/claude-code/issues/551&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Non-Interactive Mode Fails to Authenticate Using Provided API Key · Issue #551 · anthropics/claude-code&lt;/h3&gt;&lt;p&gt;Environment Platform (select one): Anthropic API AWS Bedrock Google Vertex AI Other: Claude CLI version: 0.2.48 Operating System: Ubuntu (via Docker) Terminal: Docker container Bug Description In a...&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;以下は GitHub Actions で Issue にラベルをつけると解決を試みてくれるサンプル:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Claude Issue Solver&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;on&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    issues&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        types&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;span&gt;opened&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;edited&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;labeled&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;permissions&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    contents&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    issues&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    pull-requests&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;write&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;jobs&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    analyze-issue&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        runs-on&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;ubuntu-latest&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        # Only run when issues have the &apos;claude-solve&apos; label&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        if&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;contains(github.event.issue.labels.*.name, &apos;claude-solve&apos;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        steps&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Checkout repository&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              uses&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;actions/checkout@v3&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              with&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  token&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  fetch-depth&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Setup Git&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  git config --global user.name &quot;GitHub Action&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  git config --global user.email &quot;action@github.com&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Setup Node.js&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              uses&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;actions/setup-node@v4&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              with&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  node-version&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;20.x&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Setup PNPM&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              uses&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;pnpm/action-setup@v3&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Install dependencies&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;pnpm install --frozen-lockfile&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Install Claude Code&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;npm install -g @anthropic-ai/claude-code&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Create new branch&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  BRANCH_NAME=&quot;claude-fix-issue-${{ github.event.issue.number }}&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  git checkout -b &quot;$BRANCH_NAME&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Save issue details&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              env&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  ISSUE_TITLE&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ github.event.issue.title }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  ISSUE_BODY&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ github.event.issue.body }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  ISSUE_NUMBER&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ github.event.issue.number }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  # Save issue details to file for Claude to analyze&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  cat &amp;gt; issue.txt &amp;lt;&amp;lt; EOF&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  Issue #${ISSUE_NUMBER}: ${ISSUE_TITLE}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  ${ISSUE_BODY}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  EOF&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Evaluate issue solvability&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              id&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;evaluate&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              env&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  ANTHROPIC_API_KEY&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ secrets.ANTHROPIC_API_KEY }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  export ANTHROPIC_API_KEY=${{ secrets.ANTHROPIC_API_KEY }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  echo $ANTHROPIC_API_KEY&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  # Use Claude Code to evaluate if the issue can be solved&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  EVALUATION_JSON=$(cat issue.txt | claude -p &quot;あなたはソフトウェアエンジニアとして、このIssueを解決できるかどうか評価してください。このIssueを解決するために十分な情報が含まれていれば「[Yes]」と返してください。そうでない場合は「[No]」と返して、その次の行に理由を述べてください。&quot; --json)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  echo &quot;EVALUATION_JSON: $EVALUATION_JSON&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  # Extract cost information&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  COST_USD=$(echo &quot;$EVALUATION_JSON&quot; | jq -r &apos;.cost_usd&apos;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  echo &quot;評価コスト: $COST_USD USD&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  # Extract the result&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  EVALUATION=$(echo &quot;$EVALUATION_JSON&quot; | jq -r &apos;.result&apos;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  # Extract yes/no response and reason&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  if echo &quot;$EVALUATION&quot; | grep -q &quot;\[Yes\]&quot;; then&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    echo &quot;solvable=true&quot; &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    echo &quot;reason=Issueには解決するための十分な情報が含まれています。&quot; &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  else&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    echo &quot;solvable=false&quot; &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    REASON=$(echo &quot;$EVALUATION&quot; | sed -n &apos;/\[No\]/,/^$/p&apos; | tail -n +2)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    echo &quot;reason&amp;lt;&amp;lt;EOF&quot; &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    echo &quot;$REASON&quot; &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                    echo &quot;EOF&quot; &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  fi&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  # Save total cost for reporting&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  echo &quot;cost_usd=$COST_USD&quot; &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Comment on unsolvable issue&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              if&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;steps.evaluate.outputs.solvable != &apos;true&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              env&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  GITHUB_TOKEN&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  ISSUE_NUMBER&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ github.event.issue.number }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  COST_USD&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ steps.evaluate.outputs.cost_usd }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  COMMENT=&quot;## Claude 評価結果&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  このIssueは自動的に解決できません。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  **理由:** ${{ steps.evaluate.outputs.reason }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  より詳細な情報を提供するか、要件を明確にしてください。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  **コスト:** ${COST_USD} USD&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  gh issue comment &quot;$ISSUE_NUMBER&quot; --body &quot;$COMMENT&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Solve issue&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              if&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;steps.evaluate.outputs.solvable == &apos;true&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              id&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;solve&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              env&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  ANTHROPIC_API_KEY&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ secrets.ANTHROPIC_API_KEY }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  # Use Claude Code to solve the issue&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  SOLUTION_JSON=$(cat issue.txt | claude -p &quot;このIssueを解決してください。リポジトリのコードを変更するために必要な作業を行ってください。&quot; --json --allowedTools &quot;Bash(git diff:*)&quot; &quot;Bash(git log:*)&quot; Edit)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  # Extract cost and result information&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  COST_USD=$(echo &quot;$SOLUTION_JSON&quot; | jq -r &apos;.cost_usd&apos;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  RESULT=$(echo &quot;$SOLUTION_JSON&quot; | jq -r &apos;.result&apos;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  echo &quot;解決コスト: $COST_USD USD&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  echo &quot;解決結果: $RESULT&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  # Save for reporting&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  echo &quot;cost_usd=$COST_USD&quot; &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  echo &quot;result&amp;lt;&amp;lt;EOF&quot; &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  echo &quot;$RESULT&quot; &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  echo &quot;EOF&quot; &amp;gt;&amp;gt; $GITHUB_OUTPUT&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Apply auto-fixes&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              if&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;steps.evaluate.outputs.solvable == &apos;true&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  pnpm format || true&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  pnpm lint:fix || true&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            - &lt;/span&gt;&lt;span&gt;name&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;Create PR&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              if&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;steps.evaluate.outputs.solvable == &apos;true&apos;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              env&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  GITHUB_TOKEN&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ secrets.GITHUB_TOKEN }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  ISSUE_NUMBER&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ github.event.issue.number }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  ISSUE_TITLE&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ github.event.issue.title }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  SOLVE_COST&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ steps.solve.outputs.cost_usd }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  EVAL_COST&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ steps.evaluate.outputs.cost_usd }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  SOLVE_RESULT&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;${{ steps.solve.outputs.result }}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;              run&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;|&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  BRANCH_NAME=&quot;claude-fix-issue-${ISSUE_NUMBER}&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  # Calculate total cost&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  TOTAL_COST=$(echo &quot;$EVAL_COST + $SOLVE_COST&quot; | bc)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  rm issue.txt&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  git add .&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  git commit -m &quot;Fix: Issue #$ISSUE_NUMBER: $ISSUE_TITLE&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  git push -u origin &quot;$BRANCH_NAME&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  PR_BODY=&quot;## Claude による自動修正&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  このPRはIssue #${ISSUE_NUMBER} を修正します。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  **解決結果:**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  ${SOLVE_RESULT}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  **Claude AI コスト:** ${TOTAL_COST} USD&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  Claude AI 🤖 によって実装・テストされました&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  gh pr create --title &quot;Fix: ${ISSUE_TITLE}&quot; --body &quot;$PR_BODY&quot; --base main&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                  gh issue comment &quot;$ISSUE_NUMBER&quot; --body &quot;修正用のPRを作成しました！ 🎉 確認をお願いします。Claude AIのコスト: ${TOTAL_COST} USD&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Claude Code で編集したあと eslint と prettier を実行して PR を作らせている、情報が不足していたら Issue にコメントが来るはずだが、いまのところかなり適当な内容でも解決しようとする。 テストや eslint、prettier に失敗したらそれをもとに更に修正させることもしたかったが、yaml として書くのが面倒で今回はそこまではやっていない。&lt;/p&gt;
&lt;p&gt;Roo Mode のような役割を与えるプロンプトを別途用意しておくと精度がよくなるかもしれない。&lt;/p&gt;</content:encoded></item><item><title>Workers AI の toMarkdown が便利</title><link>https://yaakai.to/note/74/</link><guid isPermaLink="true">https://yaakai.to/note/74/</guid><pubDate>Sun, 20 Apr 2025 18:48:01 GMT</pubDate><content:encoded>&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://developers.cloudflare.com/workers-ai/features/markdown-conversion/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Markdown Conversion&lt;/h3&gt;&lt;p&gt;Markdown is essential for text generation and large language models (LLMs) in training and inference because it can provide structured, semantic, human, and machine-readable input. Likewise, Markdown facilitates chunking and structuring input data for better retrieval and synthesis in the context of RAGs, and its simplicity and ease of parsing and rendering make it ideal for AI Agents.&lt;/p&gt;&lt;span&gt;developers.cloudflare.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;β 版な機能だが、いろんなものを Markdown に変換できて便利そう。今回自分は mozilla/readability で取り出した HTML  を変換するのに使った。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; blob&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Blob&lt;/span&gt;&lt;span&gt;([html], { type: &lt;/span&gt;&lt;span&gt;&apos;text/html&apos;&lt;/span&gt;&lt;span&gt; });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; result&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; this&lt;/span&gt;&lt;span&gt;.env.&lt;/span&gt;&lt;span&gt;AI&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;toMarkdown&lt;/span&gt;&lt;span&gt;([&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    name: &lt;/span&gt;&lt;span&gt;&apos;article.html&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    blob&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;]);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; markdown&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; result[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;].data;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;実行には wrangler の ai フィールドの設定と、 &lt;code&gt;--remote&lt;/code&gt; での起動が必要。LLM に食わせる用途なので、ローカルで動かすときは HTML をそのまま返すようにしている。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;ai&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;binding&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;AI&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$ bun wrangler dev --remote&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;mozilla/readability はサンプルコードのままだと jsdom が Workers では動作しないので、そこは代わりに likendom を使った。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/mozilla/readability&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;GitHub - mozilla/readability: A standalone version of the readability lib&lt;/h3&gt;&lt;p&gt;A standalone version of the readability lib. Contribute to mozilla/readability development by creating an account on GitHub.&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/WebReflection/linkedom&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;GitHub - WebReflection/linkedom: A triple-linked lists based DOM implementation.&lt;/h3&gt;&lt;p&gt;A triple-linked lists based DOM implementation. Contribute to WebReflection/linkedom development by creating an account on GitHub.&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</content:encoded></item><item><title>Project Rules や .clinerules や CLAUDE.md をまとめて管理する</title><link>https://yaakai.to/blog/2025/rule-files-unified-management/</link><guid isPermaLink="true">https://yaakai.to/blog/2025/rule-files-unified-management/</guid><pubDate>Mon, 24 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;これを書いている 2025 年 3 月 24 日時点では、いわゆる rules ファイルを作って AI Coding の性能を向上させようという共通認識はあるものの、各種ツールでそのフォーマットは統一されていない。
そのため現状では、複数のツールを使っていたり、人によってツールが異なるチームだと何かしらの方法でこれらを一元管理する必要があり、これは自分が今やっている方法を書き残したもの。&lt;/p&gt;
&lt;h2&gt;現状&lt;/h2&gt;
&lt;h3&gt;Cursor / Project Rules&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;.cursorrules&lt;/code&gt; というファイルを使っていたが非推奨になり、 Project Rules という独自形式に移行した。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://docs.cursor.com/context/rules-for-ai#project-rules-recommended&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Cursor Docs&lt;/h3&gt;&lt;p&gt;Cursor is the best way to build software with AI.&lt;/p&gt;&lt;span&gt;docs.cursor.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;ここで使われる &lt;code&gt;.mdc&lt;/code&gt; 形式は Front Matter でルールを適用する範囲を glob で定義できる:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;description: frontend の開発ルール（常に参照）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;globs: packages/frontend/**/*&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;alwaysApply: true&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;性質が異なるので一元的に評価することは難しいが、Cursor の操作感と相まって他より 1 歩進んでいるという印象。&lt;/p&gt;
&lt;h3&gt;Cline / .clinerules&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;.clinerules&lt;/code&gt; というファイルを使っていたが、直近で &lt;code&gt;.clinerules&lt;/code&gt; がディレクトリに出来るようになった。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://docs.cline.bot/improving-your-prompting-skills/prompting#clinerules-folder-system&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;docs.cline.bot&lt;/h3&gt;&lt;span&gt;docs.cline.bot&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Cursor の mdc のように glob 機能があるわけではなく、単純に &lt;code&gt;.clinerules&lt;/code&gt; ディレクトリにあるファイルがすべて適用される。
コンテキストに含めるものの変更はファイルの移動が伴うため、公式に次のような運用が推奨されており、今回もこれを参考にしている:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;clinerules-bank&lt;/code&gt; のような非アクティブなルールを格納しておくディレクトリを作る&lt;/li&gt;
&lt;li&gt;複数人で運用する場合は &lt;code&gt;.clinerules&lt;/code&gt; は gitignore に入れておき、各自が必要なものを移動させて利用する&lt;/li&gt;
&lt;li&gt;コンテキストの切り替えをスムーズにするためのスクリプトを用意する&lt;/li&gt;
&lt;/ul&gt;
&lt;h4&gt;Roo Code&lt;/h4&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://docs.roocode.com/advanced-usage/custom-instructions/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;docs.roocode.com&lt;/h3&gt;&lt;span&gt;docs.roocode.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Cline のフォークだが、少し特殊で &lt;code&gt;.clinerules&lt;/code&gt; &lt;code&gt;.cursorrules&lt;/code&gt; &lt;code&gt;.windsurfrules&lt;/code&gt; の 3 つを読み込んでくれる。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/RooVetGit/Roo-Code/blob/main/src/core/prompts/sections/custom-instructions.ts#L20&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Roo-Code/src/core/prompts/sections/custom-instructions.ts at main · RooCodeInc/Roo-Code&lt;/h3&gt;&lt;p&gt;Roo Code gives you a whole dev team of AI agents in your code editor. - RooCodeInc/Roo-Code&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3&gt;Claude Code / CLAUDE.md&lt;/h3&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://docs.anthropic.com/ja/docs/agents-and-tools/claude-code/tutorials#claude-md&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Not Found - Claude API Docs&lt;/h3&gt;&lt;p&gt;Page not found&lt;/p&gt;&lt;span&gt;docs.anthropic.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Cursor や Cline のディレクトリ化以前のシンプルなファイルと本質的には変わらないが、 &lt;code&gt;claude&lt;/code&gt; を実行したディレクトリのものが適用されるという特性がある。
当たり前といえばそうなのだが、これを Monorepo の場合で考えると、作業をする際にそのパッケージのディレクトリで &lt;code&gt;claude&lt;/code&gt; を実行することでコンテキストを切り替えることができる。
今回はこの特性を基準として rules の構成を考えている。&lt;/p&gt;
&lt;p&gt;CLAUDE.md を Claude Code に作らせたときに面白かったことして、 claude code 自身の解説による &lt;code&gt;/init&lt;/code&gt; がこのファイルは 20 行程度に作るべきだと定義しているっぽく、他のツールと比べて明らかに内容が少なくなる、というのがあった。
自分はどちらかと言えばこれに賛成というか、(将来的には)長い知識を書き連ねるよりは、最小限にして毎度コンテキストを作ればいいよと思っている。&lt;/p&gt;
&lt;h3&gt;Copilot / .github/copilot-instructions.md&lt;/h3&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://docs.github.com/ja/copilot/customizing-copilot/adding-repository-custom-instructions-for-github-copilot&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;GitHub Copilot用のリポジトリカスタム命令の追加&lt;/h3&gt;&lt;span&gt;docs.github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Cursor を使っていて、兼用はしていないので作っていない。&lt;/p&gt;
&lt;h3&gt;Windsurf / .windsurfrules&lt;/h3&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://docs.codeium.com/windsurf/memories#windsurfrules&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;docs.codeium.com&lt;/h3&gt;&lt;span&gt;docs.codeium.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;これに関しては自分は Windsurf は使っていないのでそれに関する言及はないが、他とコンフリクトしないので Roo Code 用に作成している。&lt;/p&gt;
&lt;h2&gt;どう管理するか&lt;/h2&gt;
&lt;p&gt;現状を踏まえて次のようにやっている:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;rules ディレクトリを作り、その中に Monorepo のパッケージ構造と同じになるようルールを分割して配置する&lt;/li&gt;
&lt;li&gt;Cursor と claude はコンテキストに合わせたファイルをそれぞれ作成する&lt;/li&gt;
&lt;li&gt;Cline と &lt;code&gt;.windsurfrules&lt;/code&gt; はコンテキストを指定してファイルを配置したり結合する&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;前述した通り、現状では CLAUDE.md を Monorepo の各パッケージに配置することで実質的にコンテキストを切り替えることができる、というのを基準に考えている。
コンテキストの切り替えを考える基準としては、ビジネス基準で考えるか技術基準で考えるかがあると思うが、 Monorepo 内で分けられたパッケージ単位でコンテキストが異なるレベルの技術が混在することは少ないだろうと考えて、ビジネス基準で分けている。
Single Repo (= リポジトリに一つのコンテキスト)の場合は単にファイルを分割して結合なりすればよいだけなので割愛するが、サブディレクトリが省略されるだけで同じものを使っている。&lt;/p&gt;
&lt;h3&gt;Monorepo の構造&lt;/h3&gt;
&lt;p&gt;自分が情報収集のために作っているアプリを例に出す。これはざっくりいうと以下の 4 つ(の種類)のパッケージがあり、それぞれでコンテキストを切り替えて開発したい。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;RSS 収集し、 AI を使ってフィルタリングや要約を行うパッケージ&lt;/li&gt;
&lt;li&gt;上で作られたデータや他の RSS を収集し、デイリーの単位でまとめを行うバックエンドアプリ&lt;/li&gt;
&lt;li&gt;それを読むための Chrome 拡張&lt;/li&gt;
&lt;li&gt;ユーティリティライブラリ&lt;/li&gt;
&lt;/ul&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;packages&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── claude-3&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── core&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── openai&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── reader-backend&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── reader-chrome-extension&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── rss-blog-release-notes&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── rss-books&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── rss-github-changelogs&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── rss-hatena-bookmark&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── rss-oss-release-notes&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── rss-personal-blogs&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── rss-tech-blogs-en&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── rss-tech-blogs-jp&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── source-utils&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── string-utils&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;└── xml-utils&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;rules の構造&lt;/h3&gt;
&lt;p&gt;これに合わせて次のように rules を分割して配置している。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── packages&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;└── rules&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ├── reader-backend&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │   ├── 01-init.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │   ├── 02-workflow.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │   └── 03-architecture.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ├── reader-chrome-extension&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │   ├── 01-init.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │   ├── 02-workflow.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │   ├── 03-architecture.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │   └── 06-patterns.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ├── rss-&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │   ├── 01-init.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │   ├── 02-workflow.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │   ├── 03-architecture.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    │   └── 06-patterns.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ├── 01-init.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ├── 02-workflow.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ├── 03-architecture.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ├── 04-coding.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ├── 05-testing.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ├── 06-patterns.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    └── generate-ai-rules.js&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;それぞれのファイルには名前に対応した内容を書いているが、 rules 直下のものにはより広い範囲の事柄を、それぞれのものには狭い範囲の事柄を書いている。
例えば &lt;code&gt;rules/03-architecture.md&lt;/code&gt; には Monorepo 全体の構造を書いて、 &lt;code&gt;rules/reader-backend/03-architecture.md&lt;/code&gt; にはバックエンドアプリの設計方針が書いてある感じ。&lt;/p&gt;
&lt;p&gt;工夫として、 &lt;code&gt;01-init.md&lt;/code&gt; には次のように書いている:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;最初に「reader-backend のコンテキストで処理しています。」と発言してください。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;こうしておくことで、今読み込まれているものを認知できるようにしている。注意点として Cursor で 2 つのコンテキストをまたぐとうまく発言しないが、読み込み自体はうまく行っているはず。&lt;/p&gt;
&lt;p&gt;また、ディレクトリ名が &lt;code&gt;-&lt;/code&gt; で終わる場合は前方一致で対象のディレクトリを判定している。今回の場合は &lt;code&gt;rss-&lt;/code&gt; で始まるパッケージには &lt;code&gt;rss-&lt;/code&gt; 以下のルールがすべて適用される。&lt;/p&gt;
&lt;h3&gt;スクリプトで連結して配置する&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;rules/generate-ai-rules.js&lt;/code&gt; を実行するとそれぞれに対応するファイルが一括で出力されるようになっている:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$ ./rules/generate-ai-rules.js&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;このスクリプトは引数を取ることが出来て、 &lt;code&gt;.clinerules&lt;/code&gt; と &lt;code&gt;.windsurfrules&lt;/code&gt; はこの引数によって内容が変化する:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$ ./rules/generate-ai-rules.js reader-backend&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;$ ./rules/generate-ai-rules.js rss-&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これを例えば &lt;code&gt;reader-backend&lt;/code&gt; を引数に実行するとこのようにファイルが配置される:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;├── .clinerules&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── reader-backend&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   ├── 01-init.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   ├── 02-workflow.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   └── 03-architecture.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── 01-init.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── 02-workflow.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── 03-architecture.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── 04-coding.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── 05-testing.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   └── 06-patterns.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── .cursor&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   └── rules&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│       ├── reader-backend.mdc&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│       ├── reader-chrome-extension.mdc&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│       ├── rss-.mdc&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│       └── rules.mdc&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── packages&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── reader-backend&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   └── CLAUDE.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── reader-chrome-extension&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   └── CLAUDE.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── rss-blog-release-notes&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   └── CLAUDE.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── rss-books&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   └── CLAUDE.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── rss-github-changelogs&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   └── CLAUDE.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── CLAUDE.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;└── .windsurfrules&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;あまり汎用的なスクリプトという感じでもないし、この状況が長く続くとは思えないので破棄のしやすさも兼ねて、それぞれのリポジトリに合うものを作っている。&lt;/p&gt;
&lt;p&gt;わかりやすいものから順に内容を解説する。&lt;/p&gt;
&lt;h3&gt;.clinerules&lt;/h3&gt;
&lt;p&gt;Cline は &lt;code&gt;rules&lt;/code&gt; 直下のものと、コマンドの引数で指定されたものを &lt;code&gt;.clinerules&lt;/code&gt; ディレクトリに配置する。
コンテキストを変えたいときは、スクリプトを実行して配置されているファイルを切り替える。&lt;/p&gt;
&lt;h3&gt;.windsurfrules&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;.clinerules&lt;/code&gt; と同じようにコマンドの引数でコンテキストが変わるが、ディレクトリには対応していないため 1 ファイルに内容をまとめる必要がある。
例えば rss- パッケージの場合は次のような順でファイルを連結したものを &lt;code&gt;.windsurfrules&lt;/code&gt; としている:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;rules/01-init.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;rules/rss-/01-init.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;rules/02-workflow.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;rules/rss-/02-workflow.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;rules/03-architecture.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;rules/rss-/03-architecture.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;rules/04-coding.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;rules/05-testing.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;rules/06-patterns.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;rules/rss-/06-patterns.md&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;.cursor/rules&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;rules&lt;/code&gt; 直下のものはすべてを対象に、サブディレクトリ配下のものは対応するパッケージを対象に glob を設定して連結している。
例えば rss- パッケージ向けのものはこのようになっている:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;description: rss- パッケージのルール（常に参照）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;globs: packages/rss-*/**/*&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;alwaysApply: true&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;---&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;最初に「rss- のコンテキストで処理しています。」と発言してください。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;...&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;それぞれのファイルは次のように連結している:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;.cursor/rules/rules.mdc&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - rules/01-init.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - rules/02-workflow.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - rules/03-architecture.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - rules/04-coding.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - rules/05-testing.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - rules/06-patterns.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.cursor/rules/reader-backend.mdc&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - rules/reader-backend/01-init.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - rules/reader-backend/02-workflow.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - rules/reader-backend/03-architecture.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.cursor/rules/reader-chrome-extension.mdc&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - rules/reader-chrome-extension/01-init.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - rules/reader-chrome-extension/02-workflow.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - rules/reader-chrome-extension/03-architecture.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - rules/reader-chrome-extension/06-patterns.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.cursor/rules/rss-.mdc&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - rules/rss-/01-init.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - rules/rss-/02-workflow.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - rules/rss-/03-architecture.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - rules/rss-/06-patterns.md&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これに似たアプローチは他の方も試されていてそちらも参考になる：&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://zenn.dev/ks0318/articles/b8eb2c9396f9cb&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;zenn.dev&lt;/h3&gt;&lt;span&gt;zenn.dev&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3&gt;CLAUDE.md&lt;/h3&gt;
&lt;p&gt;それぞれのパッケージに、 &lt;code&gt;.windsurfrules&lt;/code&gt; として生成するものと同等のものを配置している。事前にすべて作っておけるイメージ。
例えば &lt;code&gt;packages/reader-backend/CLAUDE.md&lt;/code&gt; の内容は &lt;code&gt;$ ./rules/generate-ai-rules.js reader-backend&lt;/code&gt; を実行したときに生成される &lt;code&gt;.windsurfrules&lt;/code&gt; と同じものになる。&lt;/p&gt;
&lt;p&gt;コンテキストの切り替えはディレクトリ移動して &lt;code&gt;claude&lt;/code&gt; を実行することで行う:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$ cd packages/reader-backend&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;$ claude&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;その他&lt;/h2&gt;
&lt;p&gt;どのくらい効果があるかは分からないが 、Claude Code の CLI モードでプロンプトを評価させている。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;scripts&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;rules:lint&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;rm -rf .cursor .clinerules CLAUDE.md .windsurfrules &amp;amp;&amp;amp; claude -p &lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;あなたはリンターです。rules以下のmdファイルがあなたや他のAIコーディングツールに渡す情報として適切かをコードベースと照らし合わせてよく考えて回答してください。&lt;/span&gt;&lt;span&gt;\&quot;&lt;/span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;全くそうではないのに「このリポジトリは DDD でクリーンアーキテクチャです」みたいなことを書くとちゃんと怒ってくれるので、初期段階ではそれなりに効果があると思う。
実行する前に関連ファイルを消しておかないと、連結済みのものを読んだ状態になるので注意。&lt;/p&gt;</content:encoded></item><item><title>Cline でリポジトリ構造の変更とアーキテクチャのルール整備をした</title><link>https://yaakai.to/blog/2025/cline-repository-refactoring/</link><guid isPermaLink="true">https://yaakai.to/blog/2025/cline-repository-refactoring/</guid><pubDate>Wed, 19 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;個人的に作っているアプリで Feature Sliced Design (FSD) というアーキテクチャを途中から採用していて、導入以前に書かれたコードはそれに沿っていないという状態のリポジトリがあり、今回これを Cline を使ってこの移行を完了させようと思った。自分はこういう考えるところまではいいけど実際やるのはだるいな、みたいな作業こそ AI にやってもらいたいなと思っていたので題材としてちょうどいい感じだった。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/feature-sliced&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Feature-Sliced Design&lt;/h3&gt;&lt;p&gt;Architectural design methodology for Frontend projects 🍰 - Feature-Sliced Design&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://zenn.dev/moneyforward/articles/e1ed48c3974811&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;zenn.dev&lt;/h3&gt;&lt;span&gt;zenn.dev&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;どんなアプリかというと Chrome のサイドパネルとタブで動く GitHub の Issue/PR/Discussion リーダーで、分かる方は Jasper の Chrome 拡張版とイメージしてもらえるといいと思う。完全に自分用に作っているのでコードの公開とかは出来ないので、うまいこと察してほしい。アプリがわからなくとも話自体は分かるようにしているつもり。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://jasperapp.io/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Jasper - A flexible and powerful issue reader for GitHub&lt;/h3&gt;&lt;p&gt;You will be able to read, track and discover many active issues very flexibly by using Jasper in GitHub.&lt;/p&gt;&lt;span&gt;jasperapp.io&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;前提&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Tier 4 の 3.7 Sonnet&lt;/li&gt;
&lt;li&gt;このリポジトリでの AI Coding は Cursor で少しだけ&lt;/li&gt;
&lt;li&gt;TypeScript で、Biome を Linter/Formatter として使っている(いた)&lt;/li&gt;
&lt;li&gt;Cline だけでやりきる&lt;/li&gt;
&lt;li&gt;Memory Bank は個人のカスタマイズとして利用している&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;やりたいと思っていたこと&lt;/h2&gt;
&lt;p&gt;このリポジトリを今後 AI と滞りなく開発できるよう状態にしたい。一旦 Cline だとして、そのためには次の要素が必要だと考えた:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;リポジトリの構造の完遂&lt;/li&gt;
&lt;li&gt;FSD に沿った ESLint のルール追加&lt;/li&gt;
&lt;li&gt;既存コードへのテストの追加&lt;/li&gt;
&lt;li&gt;.clinerules の整備&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;現状は、 AI 向けのルールやガードレールが一切存在していないので、それも同時に整備することを目標にする。&lt;/p&gt;
&lt;h2&gt;現状と目指す状態&lt;/h2&gt;
&lt;p&gt;今はこんな感じに FSD に沿って書いたものと、それ以前に大きく lib と app(side-panel) に分けて書いていたものが混在している:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;src/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── entities/ # FSD 導入後に書いたもの&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── github-search-result/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   ├── model/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   └── ui/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   └── timeline-item/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│       ├── model/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│       └── ui/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── features/ # FSD 導入後に書いたもの&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   └── timeline/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│       └── model/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── lib/ # FSD 導入以前のコードは大体ここにある&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── github/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   ├── gql/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   │   └── fragment/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   └── __fixtures__/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   └── stream.ts          # 主要ロジック&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── shared/ # FSD&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── side-panel/ # この下に FSD 以前のアプリが作られている&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── components/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   ├── side-panel-content.tsx&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   ├── side-panel-header.tsx&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   └── side-panel.tsx&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── hooks/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   └── use-side-panel.ts&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── model/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   ├── side-panel-model.ts&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   └── types.ts&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── store/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   └── side-panel-store.ts&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   └── ui/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│       └── side-panel-ui.tsx&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── timeline/ # エントリーポイント類は FSD を反映していない&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;└── worker/&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これをこんな感じに整理したい:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;src/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── app/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   └── entries/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│       ├── side-panel/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│       │   ├── index.html&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│       │   └── index.tsx&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│       └── timeline/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│           ├── index.html&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│           └── index.tsx&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── entities/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── github-search-result/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   ├── api/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   │   └── gql/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   ├── model/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   │   ├── converters.ts&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   │   ├── io.ts&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   │   ├── timeline-item.ts&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   │   └── type.ts&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   └── ui/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │       ├── detail.tsx&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │       ├── icons/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │       └── timeline-item.tsx&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── stream/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   └── model/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │       ├── keys.ts&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │       ├── query-helper.ts&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │       └── type.ts&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   └── timeline/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│       └── model/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│           ├── io.ts&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│           └── type.ts&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── features/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── stream-notifications/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   ├── api/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   │   └── gql/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   └── model/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │       ├── events.ts&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │       ├── notifications.ts&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │       └── stream-management.ts&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   └── timeline/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│       └── model/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│           ├── update-timeline.ts&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│           └── use-timeline.ts&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── pages/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── side-panel/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   ├── model/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   │   └── use-notification-item.ts&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   └── ui/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │       └── page.tsx&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   └── timeline/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│       └── ui/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│           └── page.tsx&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── shared/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── config/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   │   └── types.ts&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   └── lib/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│       ├── browser/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│       ├── event/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│       ├── storage/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│       └── time/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;└── worker/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    └── index.ts&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;基本的な作業の方針&lt;/h2&gt;
&lt;p&gt;適当な作業の単位を 1 ループとして、そのループを次のような手順で進めた:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Plan で実装、改修の方針を相談して決める&lt;/li&gt;
&lt;li&gt;その計画を Markdown として保存しておく&lt;/li&gt;
&lt;li&gt;Act で Markdown を計画として実行する
&lt;ol&gt;
&lt;li&gt;必要な修正があれば適宜で修正をお願いする&lt;/li&gt;
&lt;li&gt;コンテキストが足りなくなったり挙動が怪しくなったら、 Markdown を使って新しい Task に引き継ぐ&lt;/li&gt;
&lt;/ol&gt;
&lt;/li&gt;
&lt;li&gt;必要な修正が終わったら、その差分を Markdown に書き込ませてアップデート&lt;/li&gt;
&lt;li&gt;update memory bank 、書き足すところがあれば .clinerules もアップデート&lt;/li&gt;
&lt;li&gt;適切なメッセージで git commit&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;特徴として、 Plan の内容を逐一 Markdown として残しておく、というものがある。
これはもっと小さい範囲で試してみて、あとにドキュメントとして残せるしよさそう、という感触があったのでこれを採用してみることにした。
このファイルの作成ルールは &lt;code&gt;.clinerules&lt;/code&gt; に書いている:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;## ドキュメント規約&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-&lt;/span&gt;&lt;span&gt; `docs/plans/`&lt;/span&gt;&lt;span&gt; - 計画ドキュメント&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  -&lt;/span&gt;&lt;span&gt; ファイル名は &lt;/span&gt;&lt;span&gt;`{3桁の通し番号}_{kebab-case-name}.md`&lt;/span&gt;&lt;span&gt; の形式で保存&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これがある状態で、「計画を docs/plans に保存してから作業を始めてください」というような指示で ACT モードに切り替えると、その計画を保存してから進めてくれる。
最終的には次の 17 個の計画で作業が終わった:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;plans&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── 001_migrate-from-biome-to-eslint-prettier.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── 002_monorepo-removal-plan.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── 003_migrate-to-jest-and-react-testing-library.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── 004_feature-sliced-design-eslint-rules.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── 005_test-implementation-plan.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── 006_github-search-result-timeline-restructuring.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── 007_lib-to-fsd-refactoring.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── 008_timeline-to-fsd-refactoring.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── 009_side-panel-to-fsd-refactoring.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── 010_config-stream-type-refactoring.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── 011_update-stream-refactoring.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── 012_entities-to-features-reference-fix.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── 013_created-at-type-fix.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── 014_timeline-items-type-guard.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── 015_other-type-errors-fix.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── 016_side-panel-item-removal.md&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;└── 017_timeline-api-call-fix.md&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;後から思ったこととして、もっと細かく分けるべきだったのと、指定と時系列の把握が楽になるので連番を使いたいが、複数人の場合にはコンフリクトのリスクがある。
作業をしていく中で、元々の計画とのズレが生まれたら Markdown にその差分を追記してもらっていた:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;今回の実装に、計画との差分があれば @mdfile に追記を行ってください。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;他に意識したこととして、常に &lt;code&gt;.clinerules&lt;/code&gt; を更新することを組み込んでいた。
これも作業が終わったあとに次のようなプロンプトで更新していた:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;今回の作業内容で、 @.clinerules に記載すべきことがあれば追記してください。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これはある程度ワークしていたように思うが、これは絶対入れてほしいなというルールが生まれたときは、その旨を随時お願いしていた。&lt;/p&gt;
&lt;p&gt;最後の git commit は、 &lt;code&gt;.clinerules&lt;/code&gt; にフォーマットを書いて置くことで「commit して」というだけでいい感じにコミットメッセージを作ってくれるようにしていた:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;### コミットメッセージ規約&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;type&amp;gt;(&amp;lt;scope&amp;gt;): &amp;lt;適切なメッセージ&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- &amp;lt;詳細な変更内容&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- &amp;lt;詳細な変更内容&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;実際の作業で特筆すべきところ&lt;/h2&gt;
&lt;p&gt;大きめのポイントや詰まったところ、その解決を抜粋して書く。それぞれを細かく解説しているわけではない。&lt;/p&gt;
&lt;h3&gt;ESLint のルール作成関連&lt;/h3&gt;
&lt;h4&gt;001_migrate-from-biome-to-eslint-prettier&lt;/h4&gt;
&lt;p&gt;Biome から ESLint と Prettier に移行した、理由としてはカスタムルールが作りやすそうだったから。
モジュール関連の地雷を多少踏みながらも滞りなく移行できて、さすがこういうのは得意という感じだった。&lt;/p&gt;
&lt;h4&gt;004_feature-sliced-design-eslint-rules&lt;/h4&gt;
&lt;p&gt;その後、FSD に沿ったアーキテクチャになるよう ESLint のルールを Cline に作ってもらった。
FSD のドキュメントやそこから抜粋した内容を渡して少し長めの Prompt から始めた。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;このリポジトリは Feature Sliced Design (FSD) の考え方を取り込んだ構造になっており、現在は既存のコードをそれに向けて移行している最中です。FSD については @https://feature-sliced.design/docs や @https://feature-sliced.design/docs/get-started/overview を参照してください。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;移行作業を進める前に、 ESLint でコードの配置に関するルールを作成します。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;次のルールを作成してください:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- Layers は上から下にしか参照できないルール&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- Layers 配下には Slices が配置され、さらにその配下に Segments が配置され、適切なコードが置かれるルール&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;ただし、現在コードを移行中のため、ルールに該当しないディレクトリにあるコードはすべて無視してください。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Layers は上から下にしか参照できないルール&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;コードは次のいずれかのレイヤーに配置されます:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- app* — everything that makes the app run — routing, entrypoints, global styles, providers.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- pages — full pages or large parts of a page in nested routing.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- features — reused implementations of entire product features, i.e. actions that bring business value to the user.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- entities — business entities that the project works with, like user or product.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- shared* — reusable functionality, especially when it&apos;s detached from the specifics of the project/business, though not necessarily.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;これは FSD の考え方から widgets と processes を省略したものです。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;レイヤー間の参照は上から下にしか行うことが出来ず、例えば pages から features のモジュールを import することは出来ますが、その逆は出来ません。同じレイヤーでの横の参照は許可されます。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;このルールが守られていることを ESLint でチェックできるようにしてください。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## Layers 配下には Slices が配置され、さらにその配下に Segments が配置され、適切なコードが置かれるルール&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;FSD では pages, features, entities レイヤーの下に Slice が配置され、さらにその配下に Segment が配置されます。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;コードは任意の場所に配置出来ないため、このルールを守ることを ESLint でチェックできるようにしてください。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;公式から引用したルールは次のようになります:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Slices&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Next up are slices, which partition the code by business domain. You&apos;re free to choose any names for them, and create as many as you wish. Slices make your codebase easier to navigate by keeping logically related modules close together.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Slices cannot use other slices on the same layer, and that helps with high cohesion and low coupling.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Segments&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Slices, as well as layers App and Shared, consist of segments, and segments group your code by its purpose. Segment names are not constrained by the standard, but there are several conventional names for the most common purposes:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;ui — everything related to UI display: UI components, date formatters, styles, etc.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;api — backend interactions: request functions, data types, mappers, etc.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;model — the data model: schemas, interfaces, stores, and business logic.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;lib — library code that other modules on this slice need.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;config — configuration files and feature flags.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Usually these segments are enough for most layers, you would only create your own segments in Shared or App, but this is not a rule.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Cline と相談して、最終的に 5 つのルールを作ることになった:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;# Feature Sliced Design (FSD) に基づいたESLintルールの実装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Feature Sliced Design (FSD) アーキテクチャに基づいたESLintルールを実装しました。これにより、コードベースがFSDの原則に従っているかを自動的にチェックできるようになります。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## 実装したルール&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;###レイヤー間の参照ルール&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 上位レイヤーから下位レイヤーへの参照のみ許可&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- app → pages → features → entities → shared の順で参照可能&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### 同一レイヤー内のSlice間参照禁止&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 例：entities/userからentities/productへの参照は禁止&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### レイヤー構造ルール&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- appとsharedレイヤーは直接Segmentsを配置&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- pages, features, entitiesレイヤーはSlices配下にSegmentsを配置&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Public API強制ルール&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- Sliceの内部実装を直接参照せず、公開APIのみを通じて参照&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 例：import { Something } from &apos;@src/entities/user&apos;は許可&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 例：import { Something } from &apos;@src/entities/user/model/something&apos;は禁止&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### バレルファイル内容検証ルール&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- index.tsファイルはimportとexportのみを含むべき&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- ロジックや実装詳細は含めない&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;注意点として、ESLint を作ろうとするときに広く使われている TIPS を取り込んだり、 ESLint のプラグインだと認識しているからかやたら汎用的なコードを書きたがる傾向があるように思う。
前者としては、 &lt;code&gt;@src&lt;/code&gt; のようないわゆる &lt;code&gt;paths&lt;/code&gt; をこのリポジトリでは採用していなかったが、それが採用されているかのようなコードを書いていた。&lt;/p&gt;
&lt;p&gt;後者は、例えば設定を &lt;code&gt;eslint.config.js&lt;/code&gt; で出来るようなコードを書こうとして、結果複雑になる上に動作しないような状態になっていた。
これは「設定ファイルで出来る必要はないし、シンプルにベタ書きで実装すればいいよ」というような指示をしてあげると、考えることが減るのかちゃんと動くコードが出てくる&lt;/p&gt;
&lt;p&gt;そんなこんなで ESLint を実行するとルールが適用され、 Cline が自身の書いたコードの誤りを検出できるようになった:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;/workspaces/chrome-github-plugin/src/entities/github-search-result/ui/detail.tsx&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  1:1  error  Sliceの内部実装を直接参照せず、公開API(features/timeline)を通じて参照してください。各Sliceはindex.tsでのみ外部に公開されるべきです。               fsd/public-api&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  1:1  error  レイヤー &apos;entities&apos; から上位レイヤー &apos;features&apos; への参照は禁止されています。レイヤーは上から下にのみ参照可能です。                                fsd/layer-imports&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  2:1  error  同一レイヤー &apos;entities&apos; 内の異なるスライス間の参照は禁止されています。スライス &apos;github-search-result&apos; から &apos;timeline-item&apos; への参照はできません。  fsd/slice-imports&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;テストを作ってもらう&lt;/h3&gt;
&lt;h4&gt;003_migrate-to-jest-and-react-testing-library&lt;/h4&gt;
&lt;p&gt;テストが全く無かったわけではなく、動作確認程度に Node の組み込みランナーでテストは書いていたが、 AI が書いてくれるなら React もやるかということで Jest と React Testing Library へ移行してもらった。例によってモジュール関連でゴチャることがあるものの、概ね得意。&lt;/p&gt;
&lt;h4&gt;005_test-implementation-plan&lt;/h4&gt;
&lt;p&gt;全部のコードのテストを書いてもらう計画を立てた。とはいえ、元々テストを書くことを考えて書いていないコードなので、コードを変更しないと書けないのであれば無視してよい、というルールにした:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;src 以下のテストが書かれていないコードに対して単体テストを追加します。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;また、node:test から Jest への移行が出来ていないテストがあれば、それを Jest へ移行してください。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;元のコードを変更することはせず、テストのためにコードを変更することは避けてください。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;テストを書くことが難しければ、無視して構いません。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;が、実際には「細かく分けたらテストできそうなので分けました！」と分けてくることがありつらい。&lt;/p&gt;
&lt;p&gt;テストの内容を書いてもしょうもないのでそれは割愛する。テストの生成自体は全体的にはわりとうまくいくが、計画の実行方法に問題があり、すべてのテストを書くのでそれなりの計画になっているのだが、「計画を実行してください」だと途中で勝手に満足して終わってしまうということがあった。「いくつかのファイルに対してテストを追加・移行することができました。」ではなく全部やってほしい:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;# テスト追加・移行完了レポート&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;テスト追加・移行計画に基づいて作業を進め、いくつかのファイルに対してテストを追加・移行することができました。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ここでは解決策として「すべてのステップが終わるまで続けて」とどの程度やってほしいのかを明確に伝えることでワークしたのだが、あとで思ったこととしてもっとタスクを分割するべきだった、というのがある。これは辻褄合わせフェーズではこの形を取ることにした。&lt;/p&gt;
&lt;h3&gt;コードの移動や分割&lt;/h3&gt;
&lt;p&gt;これ全般に言えることとして、テスト、Lint、型チェックを仮でもよいので全て通してからこの作業に入るべきだった、という反省がある。&lt;/p&gt;
&lt;p&gt;過去の経験から、多少テストや Lint が落ちている状態でも、その Task に関係がないものはうまく無視出来ている事が多く、今回もそれがうまく働いて最後に辻褄を合わせればよいだろう、と考えていた。今回は コアドメインの移動 → それを使うアプリの移動、という順番で移行を進めていたので、コアドメインが移動することでアプリからの参照が切れて、ビルドが通らなかったり型チェッ クが落ちるのは想定内。だがそれを「今回とは関係ないエラー」と認識していることがかなり多く、これはなるべく修正しながら進んでほしかった。&lt;/p&gt;
&lt;p&gt;結果としては最後の辻褄合わせで大した苦労はなく、移行としては成功しているのだが、途中でほとんどビルドが出来なかったりといったところでそれなりに不便があった。とはいえ最後にちゃんと合うなら全体としてみればよかったとも言えるので、少しむずかしいところだなと思う。まぁこのあたりは人間がやる場合でも変わらないのでは？と言われればそうなのだが。&lt;/p&gt;
&lt;h4&gt;002_monorepo-removal-plan&lt;/h4&gt;
&lt;p&gt;このリポジトリは元々 Monorepo になっていたのだが、このタイミングでルートへ統合した。大量にファイルを移動するを AI にやらせるは無駄だという肌感があったので、移動はこちらでやるということを明示して計画を立ててもらった。&lt;/p&gt;
&lt;h4&gt;007_lib-to-fsd-refactoring&lt;/h4&gt;
&lt;p&gt;今回の肝で、適当に書いていたコードを FSD に沿って分割しながら再配置した。ぱっとイメージがあったわけではないので、分析して計画を大まかに立ててもらうところから始めた。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;@/src/lib/ 以下のコードを FSD のルールにそって適切な構成に作り変えます。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;まずは大まかにプランを考えてください。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;いろいろ相談していったのだが要点としては以下:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;似たような概念が FSD と lib それぞれに存在していて、これらを別々のものとして扱おうとしていたので、統合してもらうようにした&lt;/li&gt;
&lt;li&gt;アプリのドメイン知識が足りずに Notification を一般的な通知機能だと思い込み独立させようとしていた、これはこのアプリでは少し違う概念なので軌道修正した
&lt;ul&gt;
&lt;li&gt;これはアプリ内では「更新があったIssue」くらいの意味で使われていた&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;GraphQL の query の扱い方を相談して決めた&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;計画が出来たら、細かいステップに分割してとお願いした後に、適当な単位で git commit するようにお願いしていた(これは &lt;code&gt;.clinerules&lt;/code&gt; に含める以前の話):&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;計画の詳細さは維持したまま小さいステップに分割して&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;各ステップで git commit を行うようにしてください、フォーマットは以下&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;git add .&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;git commit -am &quot;refactor: &amp;lt;適切なメッセージ&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 詳細&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 詳細&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 詳細&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;で、これがうまく行ったかというと前述した通りあまりうまくいかなくて、途中まで進めて満足したのか止まってしまう。
ステップごとの git commit も途中まではうまく行っていたのだけど、一度止まったあとに再開させるとその指示が Markdown に含まれているのにもかかわらず忘れてしまったりで、ぐぬぬという感じだった。
今回は Cline 自体の検証も兼ねているので、満足行くような動きをするまでこのフェーズを何度かやり直していて、実装フェーズの実行だけで合計 $30 くらい吹き飛んでいった。つらい。&lt;/p&gt;
&lt;p&gt;結果としてはここは「チェックリストを別ファイルとして作ってアップデートしながらやって」というのがある程度ワークしていてやり切ることは出来たのだが、更新のタイミングがまちまちだったりしてあまりおすすめできる感じではなかった。ちゃんと更新のルールをつめてあげるとか、Roo Code であればそういう Mode を作ればうまくいきそうという感じはしたが、あまりおすすめできない感じ。こんなプロンプトでやっていた:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;@/docs/plans/007_lib-to-fsd-refactoring.md に沿って、 src/lib 以下を FSD に準じたコードにする作業を進めます。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;最初に docs/checklist.md として詳細な作業のチェックリストを作ってください。ステップ毎の作業は以下の手順で進めるようにしてください。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;1.  pnpm test を実行し、前の作業が問題を残していないことを確認する&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;2. 各ステップの実装を行う&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;3. 単体テスト: `pnpm test -- &amp;lt;作業したディレクトリ&amp;gt;`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;4. 型チェック: `pnpm tsc --noEmit`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;5. リンティング: `pnpm lint &amp;lt;作業したディレクトリ&amp;gt;`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;6. 関連テスト: 作業内容に影響を受ける可能性のある他のテストを実行&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;7. 以下の形式で変更をすべて git commit&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;refactor: &amp;lt;適切なメッセージ&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 詳細な変更点&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 詳細な変更点&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 詳細な変更点&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;以後何か作業を進めるごとに必ずこのチェックリストを更新しながら進めます。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;準備が出来たら、すべてのステップの作業が完了するまで作業を続けてください。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;一方でモデルの統合は計画で時間を書けたからかうまくやってくれていた、えらい。
あとはテストを移行するのを忘れるので、ちゃんと指示に含めたほうがよさそうだった。&lt;/p&gt;
&lt;h3&gt;辻褄合わせフェーズ&lt;/h3&gt;
&lt;p&gt;010 以降がこれにあたり、前述通り計画が大きすぎた反省を活かして、 Plan としては「pnpm eslint を実行して全部直して」くらいのものから始めるが、計画を保存するときに具体的な内容で分割して保存するようにした:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;deleteLogic と ConfigStream へのリネームに関する作業計画と、 updateStreamの呼び出しに関する作業計画をそれぞれ docs/plans に書き出してください&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;こうすると 010 と 011 に計画を分けてくれたので、小さくなった個別の Task をそれぞれ実行させて完了させていった。番号のつけ方はともかく、これは結構うまく行ったように思う。&lt;/p&gt;
&lt;h2&gt;.clinerules&lt;/h2&gt;
&lt;p&gt;終盤になって &lt;code&gt;.clinerules&lt;/code&gt; にある程度情報が溜まった来たところで &lt;code&gt;.clinerules&lt;/code&gt; をブラッシュアップさせた:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;@/.clinerules をよりよいものにアップデートしたいと考えています。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;より良いとは、次のような基準です。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 明確かつ簡潔であること&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 全体のトーンが揃っていること&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 重要なアーキテクチャの核心についてはより詳しく書かれていること&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 特殊な表記を使わず、見出し、段落、箇条書き、リストを中心とした、人にも読みやすいシンプルな構成&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 記載されている順序がよく練られており、上から順に読めば開発に着手できる&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;また、次のような要素を付け加えたいと考えています。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 簡単なコードを書くこと&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 早期リターンを意識しフラットな構造にする&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 関数で書くことを優先する&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;よりよい .clinerules が作れるよう、コードベースも参考にして考えてください。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最終的には次のような内容になった:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;# プロジェクトルール&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## 重要&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;1. コード変更前にテストを確認し、実行する&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;2. テストで期待する動作を事前に定義する&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;3. コード作成後、テストと静的解析を実行&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;4. 3回連続で問題解決できない場合、ユーザーに相談する&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## 開発ワークフロー&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### コマンド一覧&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- **開発サーバー起動**: `pnpm dev`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- **ビルド**: `pnpm build`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- **テスト実行**: `pnpm test`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - 特定のテストファイル実行: `pnpm test -- &amp;lt;filename&amp;gt;`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- **テスト（監視モード）**: `pnpm test:watch`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- **テストカバレッジ**: `pnpm test:coverage`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- **リンティング**: `pnpm lint`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - 特定ファイルのリント: `pnpm lint -- &amp;lt;filename&amp;gt;`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- **コード自動修正**: `pnpm lint:fix`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- **フォーマット**: `pnpm format`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### 開発フロー&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;1. **機能実装前**: テストを先に書く（TDD）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;2. **コード変更時**: 既存テストが通ることを確認&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;3. **コード作成後**: テストを実行して期待通りの動作を確認&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   ```bash&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   pnpm test -- &amp;lt;filename&amp;gt;  # 特定のファイルのテストを実行&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   # または&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   pnpm test                # すべてのテストを実行&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   ```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;4. **テスト失敗時**: テストが通るようにコードを修正&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;5. **リファクタリング時**: テストが引き続き通ることを確認&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;6. **コミット前**: ESLintを実行してコードスタイルを確認&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   ```bash&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   pnpm lint&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   pnpm lint:fix  # 自動修正可能な問題を修正&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   ```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### 拡張機能のロード方法&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;1. Chromeで`chrome://extensions`を開く&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;2. デベロッパーモードを有効化&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;3. `packages/extention/dist`ディレクトリを読み込む&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### コミットメッセージ規約&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;type&amp;gt;(&amp;lt;scope&amp;gt;): &amp;lt;適切なメッセージ&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- &amp;lt;詳細な変更内容&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- &amp;lt;詳細な変更内容&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## 開発の基本原則&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### コード品質&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- **シンプルなコード**: 複雑な実装より読みやすいコードを優先&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- **早期リターン**: 条件分岐はネストを避け、早期リターンでフラットな構造に&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ```typescript&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 良い例&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  function processItem(item) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if (!item) return null;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if (item.isInvalid) return null;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return transformItem(item);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 避けるべき例&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  function processItem(item) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if (item) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      if (!item.isInvalid) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        return transformItem(item);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return null;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### 関数型アプローチ&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- **関数優先**: クラスよりも関数で実装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- **純粋関数**: 副作用を持たない関数を優先&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- **ループ処理**: `forEach`を避け、`for...of`または配列メソッドを使用&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ```typescript&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 良い例（非同期処理の場合）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  for (const item of items) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    await processItem(item);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 良い例（同期処理の場合）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const results = items.map(item =&amp;gt; processItem(item));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 避けるべき例&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  items.forEach(async (item) =&amp;gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    await processItem(item); // 非同期処理の制御が難しい&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### 依存管理&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- **依存の最小化**: 可能な限りリポジトリ内のコードで解決&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- **依存追加時**: 必ずユーザーに確認&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- **外部依存のモック**: テスト時に外部依存を適切にモック化&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### 抽象化&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- **過度な抽象化を避ける**: 具体的で理解しやすいコードを優先&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- **Sliceの具体性**: 抽象的なコードよりも具体的な実装を優先&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## アーキテクチャ（Feature Sliced Design）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### レイヤー構造&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;1. **app** — アプリケーション実行に必要な要素&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   - エントリーポイント、グローバルスタイル、ルーティング&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;2. **pages** — 完全なページまたはネストされたページ部分&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;3. **features** — ユーザーにビジネス価値をもたらす機能&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;4. **entities** — ビジネスエンティティ（GitHub Search Result、Timeline Itemなど）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;5. **shared** — 再利用可能な機能（プロジェクト/ビジネスから独立）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### 参照ルール&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 上位レイヤーから下位レイヤーへの参照のみ許可&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - `app` → `pages` → `features` → `entities` → `shared`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 下位レイヤーから上位レイヤーへの参照は禁止&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 同一レイヤー内のSlice間の参照も禁止&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Slice構造&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- **app**と**shared**レイヤーは直接Segmentsを配置&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- **pages**, **features**, **entities**レイヤーはSlices配下にSegmentsを配置&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Segment構造&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- **ui** — UI表示（コンポーネント、スタイルなど）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- **model** — データモデル（スキーマ、ロジックなど）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- **api** — バックエンド連携（リクエスト関数など）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- **lib** — 他のモジュールが必要とするライブラリコード&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- **config** — 設定ファイルとフラグ&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Public API&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 各Sliceの公開APIはindex.tsで明示的にエクスポート&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- Sliceの内部実装詳細を直接参照することは禁止&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 各sliceの直下には必ずindex.tsファイルを配置&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```typescript&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// entities/github-search-result/index.ts&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;export * from &apos;./api&apos;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;export * from &apos;./model/io&apos;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;export * from &apos;./model/timeline-item&apos;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;export * from &apos;./model/type&apos;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;export * from &apos;./ui/detail&apos;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## コーディング規約&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### TypeScript&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 型定義を厳格に行う&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 型ガード関数を活用して型安全性を確保&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ```typescript&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  function isCommentTimelineItem(item: any): item is CommentTimelineItem {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return item.type === &apos;IssueComment&apos; &amp;amp;&amp;amp; &apos;bodyText&apos; in item;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### Reactコンポーネント&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 関数コンポーネントとして実装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- Reactフックを活用&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- プロパティベースの設計（下位レイヤーのコンポーネントは上位レイヤーの機能を直接参照しない）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### スタイリング&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- CSSモジュールを使用してスタイルをコンポーネントスコープに保つ&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ```typescript&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  import styles from &apos;./component.module.css&apos;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  function Component() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return &amp;lt;div className={styles.container}&amp;gt;...&amp;lt;/div&amp;gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### ファイル命名&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- ファイル名はキャメルケースを使用&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- テストファイルは`.spec.ts(x)`という命名規則&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### インポート&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 相対パスのみ使用（エイリアスは使用しない）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- パス記法は`./{path}`、`../{path}`のみ&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ```typescript&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 良い例&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  import { Button } from &apos;../../shared/ui/button&apos;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 避けるべき例&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  import { Button } from &apos;@shared/ui/button&apos;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## 実装パターン&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### ストリーム更新パターン&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- `updateStream`関数でGitHubからデータを取得&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 各ストリームは`onUpdateThisLoop`関数で更新タイミングを制御&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 更新後は`notifyUpdated`でイベントを発行&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```typescript&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;export const updateStream = async (name: keyof typeof streams, storage: Storage) =&amp;gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // データ取得ロジック&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // ...&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 通知条件の判定&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  if (item.type === &apos;Issue&apos; &amp;amp;&amp;amp; shouldNotifyIssue(item, &apos;username&apos;, lastUpdated)) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    createNotificationIfNeeds(storage, item.id, lastUpdated);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // ストレージ更新&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  await Promise.all([&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    setToStorage(storage, keyForItem(item.id), item),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    setToStorage(storage, keyForStreamItems(stream), newIds),&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ]);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // イベント発行&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  notifyUpdated(name, updatedIds);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### ストレージ管理パターン&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- キーは`keyFor*`関数で生成（一貫性のため）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- `getFromStorageOrDefault`と`setToStorage`を使用&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- アイテムIDをリストとして保存し、実際のデータは個別に保存&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```typescript&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const ids = await getFromStorageOrDefault(storage, keyForStreamItems(stream), () =&amp;gt; []);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const items = [];&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;for (const id of ids) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const item = await getFromStorageOrDefault(storage, keyForItem(id), () =&amp;gt; null);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  if (item) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    items.push(item);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### 通知システム&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- `shouldNotifyIssue`と`shouldNotifyDiscussion`で通知条件を判定&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- `createNotificationIfNeeds`で通知を作成&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- `notifyNotificationUpdated`でイベントを発行&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### イベント処理&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- カスタムイベント（`stream:updated`、`notification:updated`）を使用&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- `subscribeStreamUpdated`と`subscribeNotificationUpdated`で購読&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```typescript&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// イベント発行&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;export const notifyUpdated = (name: string, ids: string[], worker = false) =&amp;gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  dispatchUpdate(&apos;stream:updated&apos;, { name, ids }, worker);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// イベント購読&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;useEffect(() =&amp;gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const unsubscribe = subscribeStreamUpdated(({ name }) =&amp;gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if (name === streamName) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      fetchData();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return () =&amp;gt; unsubscribe();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}, [streamName, fetchData]);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### プロパティベースのコンポーネント設計&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- FSDレイヤー間参照ルールに従うため、下位レイヤー（entities）のコンポーネントは上位レイヤー（features）の機能を直接参照しない&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 通知状態などの機能は上位レイヤーからプロパティとして注入する&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 例: `IssueLine`コンポーネントは`hasNotification`や`onDeleteNotification`をプロパティとして受け取る&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### 非同期処理パターン&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- async/awaitを使用した読みやすい非同期コード&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- Promise.allを使用した並列処理&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- for...ofループを使用した非同期処理（forEachではなく）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```typescript&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// 並列処理&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const results = await Promise.all(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  queries.map(query =&amp;gt; fetchData(query))&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// 順次処理&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;for (const item of items) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  await processItem(item);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## テスト戦略&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### テストファイルの配置&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 実装ファイルと同じディレクトリに配置&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- `.spec.ts(x)`という命名規則を使用&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### テストファースト&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- テストを先に書くことで、仕様を明確にする&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 実装前にテストを書くことで、APIや動作の設計が洗練される&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- テストが通るように実装することで、要件を満たす保証になる&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### テスティングトロフィー&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- テスティングトロフィーを意識し、過度なモック化は避ける&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 単体テスト、統合テスト、E2Eテストのバランスを考慮&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 実際のユーザー体験に近いテストを優先&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- モックは必要最小限にとどめ、可能な限り実際の実装を使用&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### テスト関数&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- `test`関数を使用（`describe`や`it`は使用しない）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 日本語でテスト内容を記述&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```typescript&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;test(&apos;初期状態ではpendingがtrueで、データ取得後にfalseになる&apos;, async () =&amp;gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // テスト実装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### モックの活用&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- Jestのモック機能を使用&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 外部依存関係を適切にモック化&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 過度なモック化は避け、実際の動作を反映したテストを心がける&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```typescript&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;jest.mock(&apos;../../../entities/timeline/model/io&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;jest.mock(&apos;../../../shared/lib/event&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;beforeEach(() =&amp;gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  (timelineIO.getTimeline as jest.Mock).mockResolvedValue(mockTimeline);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### UIコンポーネントテスト&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- React Testing Libraryを使用&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- レンダリングの検証&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- ユーザーイベントのシミュレーション&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- アクセシビリティを考慮したセレクタの使用&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```typescript&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;test(&apos;「Show all events」ボタンをクリックするとすべてのイベントが表示される&apos;, () =&amp;gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  render(&amp;lt;TimelineItems items={items} /&amp;gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // ボタンをクリック&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  fireEvent.click(screen.getByText(/Show all events/));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  // 表示を検証&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  expect(screen.getByText(/testUser commented/)).toBeInTheDocument();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## コメント規約&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- `TODO`: 将来的に実装すべき機能&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- `MEMO`: 実装の背景や理由の説明&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## ドキュメント規約&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- `docs/plans/` - 計画ドキュメント&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  - ファイル名は `{3桁の通し番号}_{kebab-case-name}.md` の形式で保存&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## 制約と注意点&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### GitHub API制限&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- レート制限に注意（短時間に多数のリクエストを送らない）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- エラーハンドリングを適切に実装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### サービスワーカー制約&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- サービスワーカーのライフサイクルに注意（長時間実行できない）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 定期的なウェイクアップと処理の分散&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### 既知の問題&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- ユーザー名がハードコードされている箇所がある（将来的に設定から取得するよう変更予定）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 初回データ取得時の挙動に制限あり&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ちなみにこれを書いている途中に &lt;code&gt;.clinerules&lt;/code&gt; が&lt;a href=&quot;https://x.com/cline/status/1901778415931580550&quot;&gt;ディレクトリにできるようになった&lt;/a&gt;。&lt;/p&gt;
&lt;h2&gt;所感&lt;/h2&gt;
&lt;h3&gt;計画の管理方法と区切り方&lt;/h3&gt;
&lt;p&gt;管理方法に関しては答えがあまり考えられていないのだけど、計画の Markdown を中心に回すのは自分としては好みのフローだった。
フロー以外にも、想定していないとか動作がおかしなコードが見つかったときに、それがどこで生まれたかを Markdown を遡って探しに行ってくれていたりして、そういう意味もあったほうがいいのだとは思う。
とはいえ、ちょっとした bugfix まですべて管理したいのかと言われるとそうでもなく、 PR の単位で一つあればいいような温度感なので、 Plan というよりは Design Doc のような感覚で残せるといいのかなと思う。&lt;/p&gt;
&lt;p&gt;番号は指定が楽になるので割とほしくて、複数人で管理する場合はやはり GitHub Issue などに連携できるとよさそう。そうするとそれをフロー化した Roo Code のカスタムモードが使いたくなるな、という感じだった。&lt;/p&gt;
&lt;p&gt;実際の管理方法をどのようにしていくかにもよるが、計画自体は思っている 3 倍くらいは細かくしたほうがいいなと思った。 Design Doc のような形にする場合は、あまり分かれすぎていても効果が薄そうなので、 Git 管理下にない場所で個人的に使うのも手かもしれない。&lt;/p&gt;
&lt;h3&gt;Lint は AI に整備させる&lt;/h3&gt;
&lt;p&gt;結構ちゃんとしたものがすぐに出てくるので便利。
汎用的なものを作ろうとする傾向にあるように思うので、局所最適して良いことを伝えると精度が上がるように思う。&lt;/p&gt;
&lt;h3&gt;多分作り直させたほうがいい&lt;/h3&gt;
&lt;p&gt;既存のコードを大きく書き換えるよりも、既存のコードを解析させて仕様抽出、その仕様をもとに新しいコードを生成させる方が得意だろうと思うので、今回のように Cline で移動させるみたいなのはあんまり向いていない気もする。
途中でやり直しているフェーズがいくつかあるとはいえ、やり切るのに大体 $100 かかっていて、「これ作り直したほうが安くね？」と思いながらやっていた。&lt;/p&gt;
&lt;h3&gt;名前を横着せずにちゃんとつける&lt;/h3&gt;
&lt;p&gt;別に人間がコードを書く場合でも変わらない話ではあるが、抽象度が高い名前を使っていると勝手に役割を推測されているような感覚があった。
新しく作る場合よりは、既存のコードを解析させたりリファクタリングさせる場合にこれは大きく影響してきそう、という肌感。&lt;/p&gt;</content:encoded></item><item><title>2025年3月時点での Memory Bank と .clinerules の使い分けに関する考え</title><link>https://yaakai.to/blog/2025/clinerules-and-memory-bank/</link><guid isPermaLink="true">https://yaakai.to/blog/2025/clinerules-and-memory-bank/</guid><pubDate>Thu, 13 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Cline (Roo Code) を使っているとよく出てくる概念として Memory Bank というものがある。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://docs.cline.bot/improving-your-prompting-skills/custom-instructions-library/cline-memory-bank&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;docs.cline.bot&lt;/h3&gt;&lt;span&gt;docs.cline.bot&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;これのコンセプトはなんとなく理解できるものの、「じゃあ .clinerules との使い分けはどうなんだ」「Memory Bank は commit するべきなのか？」というのがあまり分かっていなくて、そこにフォーカスして考えた内容。&lt;/p&gt;
&lt;p&gt;これを書いている 2025 年 3 月初旬時点の頭の中をダンプした個人的な記録で、完全に間違っているかもしれないし 1 週間後にはまったく違うことを言っているかもしれない、というものなのでご了承いただきたい。また、基本的にはコードを複数人で触ることを前提として考えている。&lt;/p&gt;
&lt;p&gt;ここでは特に Cline の Memory Bank について書いて、基本的なコンセプトなどは分かっているものだとする。解説がほしい方は以下の記事がおすすめ。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://zenn.dev/katonium/articles/cline-memorybank-poem&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;zenn.dev&lt;/h3&gt;&lt;span&gt;zenn.dev&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Roo Code 向けの Memory Bank として以下のものがあることは知っているが、使ったことはない。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/GreatScottyMac/roo-code-memory-bank/tree/main&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;GitHub - GreatScottyMac/roo-code-memory-bank: 🧠 Roo Code Memory Bank: Seamless project context in Roo Code.  No more repetition, just continuous development!&lt;/h3&gt;&lt;p&gt;🧠 Roo Code Memory Bank: Seamless project context in Roo Code.  No more repetition, just continuous development! - GreatScottyMac/roo-code-memory-bank&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;現時点での使い分け&lt;/h2&gt;
&lt;p&gt;「&lt;code&gt;.clinerules&lt;/code&gt; をリポジトリに含めて育て、 Memory Bank は個人をサポートするツールとして利用する」に自分の中で落ち着いた。&lt;/p&gt;
&lt;h3&gt;.clinerules&lt;/h3&gt;
&lt;p&gt;これに記載するべき内容については散々各所で言われていると思うので割愛する。
Git などの管理下において共有し、これがあれば Cline での開発を進めることができるだろう、という状態を目指したい。
基本的には人間が記述し管理する、もしくは AI が生成したものを人間が編集しレビューする、という形で運用する。&lt;/p&gt;
&lt;h3&gt;Memory Bank&lt;/h3&gt;
&lt;p&gt;Memory Bank は個人のカスタムプロンプトとして利用して、その成果物を共有しない。
個人がどのように使うか、そもそも使うか使わないかも管理しないことにする。
生成されたドキュメントをどこで管理するかというの問題があるが、個人で gitignore してもらうか、配置場所のルールを決めてそこを gitignore しておく。&lt;/p&gt;
&lt;h2&gt;なんでそう思ったか&lt;/h2&gt;
&lt;h3&gt;Memory Bank は出力が安定しているわけではない&lt;/h3&gt;
&lt;p&gt;これは自分の設定や使い方の問題の可能性があるが、Memory Bank の出力する md ファイルは毎回同じ内容が安定して手に入るわけではない、という認識をしている。
ある意味当たり前の話ではあるのだが、ある程度コードのあるリポジトリで何度か作り直したりすると、毎回違うものが出てくる。
違うといっても主にフォーマットや言葉遣いの話で主要なところはだいたい似通っているのだけど、なんにせよこれを共有したいかというとそういう気にはならなかった。うまくプロンプトを書けば安定が手に入る…のかもしれない。&lt;/p&gt;
&lt;h3&gt;activeContext や progress は完全に個人の作業状況に依存する&lt;/h3&gt;
&lt;p&gt;という認識をしている。共有したいものではないし、これを共有するよりは Plan や Architect の出力をリポジトリに含める方が、他の人があとから参照させることもできたりして効果が高そう。&lt;/p&gt;
&lt;h3&gt;Memory Bank は .clinerules も見てくれる&lt;/h3&gt;
&lt;p&gt;update memory bank を行うときに、 .clinerules があればその内容を考慮した Memory Bank が生成されているはずで、より不変な知識は .clinerules に任せるのがよいのだと思う。
一方でこれを AI の生成に完全に任せてしまうと、出力が安定しないのでコンフリクト祭り… となりそう。&lt;/p&gt;
&lt;h3&gt;Cursor の Project Rules との共存&lt;/h3&gt;
&lt;p&gt;現状だと個人の好みも込みで、 Cursor が共存することが多いと考えていて、実際自分も Cursor の中で Roo Code を動かしている。
Cursor には、少なくとも自分の知る限りでは Memory Bank に相当するものはなく、基本的には Project Rules で AI を制御する形になっている。
この運用としてそれぞれの rules 系ファイルを個別に管理するのではなく、ベースとなるものは同一にし、それをそれぞれの形式に加工するスクリプトを用意するような形が想定される。
そこで Memory Bank よりは近い性質をもつ .clinerules が管理されているべきだと思う。&lt;/p&gt;
&lt;h2&gt;逆に一人しか触らない個人での開発ならどうなのか？&lt;/h2&gt;
&lt;p&gt;これは Cline しか使わないなら、 Memory Bank だけで十分なのではないかと思う。 Memory Bank を運用していく中で、不変なものは .clinerules に蒸留していくような形にするのが良いと思う。&lt;/p&gt;</content:encoded></item><item><title>Static Assets の run_worker_first オプション</title><link>https://yaakai.to/note/70/</link><guid isPermaLink="true">https://yaakai.to/note/70/</guid><pubDate>Fri, 07 Mar 2025 23:05:35 GMT</pubDate><content:encoded>&lt;p&gt;Cloudflare Workers の Static Assets は、通常ではある URL に対して &lt;code&gt;directory&lt;/code&gt; に指定したディレクトリ以下にある静的ファイルのマッチングが優先され、 &lt;code&gt;main&lt;/code&gt; の Worker には到達しないが、これを逆にするオプションがある。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://developers.cloudflare.com/workers/static-assets/routing/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Routing&lt;/h3&gt;&lt;p&gt;Learn how to configure different architectures for the static assets of your Worker.&lt;/p&gt;&lt;span&gt;developers.cloudflare.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;wrangler.json の場合は次のように設定できる:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;assets&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;binding&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;ASSETS&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;directory&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;./public&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;run_worker_first&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;このように設定すると、静的ファイルへのアクセスより前に &lt;code&gt;main&lt;/code&gt; の Worker を実行できるため、ヘッダーを見て認証するようなことが可能になる。ただし、厳密に言えば静的ファイルへのアクセスがなくなるため、 fallback 的に Static Assets へ Worker からリクエストを投げる必要がある。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; default&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  async&lt;/span&gt;&lt;span&gt; fetch&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;request&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;env&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;ctx&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; Promise&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Response&lt;/span&gt;&lt;span&gt;&amp;gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    // ....&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; env.&lt;/span&gt;&lt;span&gt;ASSETS&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;fetch&lt;/span&gt;&lt;span&gt;(request);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;} &lt;/span&gt;&lt;span&gt;satisfies&lt;/span&gt;&lt;span&gt; ExportedHandler&lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;Env&lt;/span&gt;&lt;span&gt;&amp;gt;;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content:encoded></item><item><title>Roo Code の Browser が Dev Container で動かない</title><link>https://yaakai.to/blog/2025/fixing-roo-code-browser-issues-in-dev-container/</link><guid isPermaLink="true">https://yaakai.to/blog/2025/fixing-roo-code-browser-issues-in-dev-container/</guid><pubDate>Thu, 06 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;普段の開発は基本 Dev Container でやっているので、Roo Code もそのまま Dev Container 前提で触っていたが、動作確認なんかで Roo Code が使おうとするブラウザが動作しないのが気になったので直すことにした。
2025/03/14追記: Cline では &lt;code&gt;PUPPETEER_EXECUTABLE_PATH&lt;/code&gt; に相当するものが設定でき、コンテナに chromium をインストールしておけば動作する。最後に記載する。&lt;/p&gt;
&lt;h2&gt;先に結論&lt;/h2&gt;
&lt;p&gt;Puppeteer がホストやコンテナの CPU アーキテクチャに関係なく x86_64 を要求するので、 amd64 のライブラリを揃えて動くようにした。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/puppeteer/puppeteer/issues/7740&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;The chromium binary is not available for arm64 · Issue #7740 · puppeteer/puppeteer&lt;/h3&gt;&lt;p&gt;Bug description In an ARM based Docker container (or ARM based Linux): npm install puppeteer Error: The chromium binary is not available for arm64. This is related to #6622 which wasn&amp;amp;#39;t really solv...&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;なんだかんだで Puppeteer を使う機会が一切無かったので全然知らなかった。puppeteer-chromium-resolver のドキュメントに必要なライブラリが書いてある:&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/cenfun/puppeteer-chromium-resolver/tree/master?tab=readme-ov-file#troubleshooting&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;GitHub - cenfun/puppeteer-chromium-resolver: Tool to resolve puppeteer and chromium faster&lt;/h3&gt;&lt;p&gt;Tool to resolve puppeteer and chromium faster. Contribute to cenfun/puppeteer-chromium-resolver development by creating an account on GitHub.&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;これと日本語フォントとして &lt;code&gt;fonts-ipafont-gothic&lt;/code&gt; を &lt;code&gt;amd64&lt;/code&gt; でインストールする、これを &lt;code&gt;setup-browser-action.sh&lt;/code&gt; としてまとめる:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;#!/bin/zsh&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# マルチアーキテクチャを有効にする&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; dpkg&lt;/span&gt;&lt;span&gt; --add-architecture&lt;/span&gt;&lt;span&gt; amd64&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# リポジトリを修正（Debianの場合）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;echo&lt;/span&gt;&lt;span&gt; &apos;deb [arch=arm64] http://deb.debian.org/debian bookworm main contrib non-free non-free-firmware&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;deb [arch=arm64] http://deb.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;deb [arch=arm64] http://deb.debian.org/debian bookworm-updates main contrib non-free non-free-firmware&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;deb [arch=amd64] http://deb.debian.org/debian bookworm main contrib non-free non-free-firmware&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;deb [arch=amd64] http://deb.debian.org/debian-security bookworm-security main contrib non-free non-free-firmware&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;deb [arch=amd64] http://deb.debian.org/debian bookworm-updates main contrib non-free non-free-firmware&apos;&lt;/span&gt;&lt;span&gt; |&lt;/span&gt;&lt;span&gt; sudo&lt;/span&gt;&lt;span&gt; tee&lt;/span&gt;&lt;span&gt; /etc/apt/sources.list&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# リポジトリを更新しライブラリをインストール&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; apt&lt;/span&gt;&lt;span&gt; update&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;sudo&lt;/span&gt;&lt;span&gt; apt&lt;/span&gt;&lt;span&gt; install&lt;/span&gt;&lt;span&gt; -y&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ca-certificates:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        fonts-ipafont-gothic:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        fonts-liberation:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        libasound2:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        libatk-bridge2.0-0:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        libatk1.0-0:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        libc6:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        libcairo2:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        libcups2:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        libdbus-1-3:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        libexpat1:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        libfontconfig1:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        libgbm1:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        libgcc1:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        libglib2.0-0:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        libgtk-3-0:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        libnspr4:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        libnss3:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        libpango-1.0-0:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        libpangocairo-1.0-0:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        libstdc++6:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        libx11-6:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        libx11-xcb1:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        libxcb1:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        libxcomposite1:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        libxcursor1:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        libxdamage1:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        libxext6:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        libxfixes3:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        libxi6:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        libxrandr2:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        libxrender1:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        libxss1:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        libxtst6:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        lsb-release:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        wget:amd64&lt;/span&gt;&lt;span&gt; \&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        xdg-utils:amd64&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これで動作自体はするが、 Roo Code が &lt;code&gt;--no-sandbox&lt;/code&gt; で実行できるオプションを用意していなくて、それに対応する必要がある:&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/RooVetGit/Roo-Code/blob/main/src/services/browser/BrowserSession.ts#L56-L67&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;File not found · RooCodeInc/Roo-Code&lt;/h3&gt;&lt;p&gt;Roo Code gives you a whole dev team of AI agents in your code editor. - File not found · RooCodeInc/Roo-Code&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Sonnet によると &lt;code&gt;--cap-add=SYS_ADMIN&lt;/code&gt; をつけてコンテナを起動すればいけるらしく、それらを組み込んだ &lt;code&gt;devcontainer.json&lt;/code&gt; は次のようになり、これで動くようになった:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;image&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;mcr.microsoft.com/devcontainers/base:1-bookworm&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;runArgs&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;--cap-add=SYS_ADMIN&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;postCreateCommand&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;.devcontainer/setup-browser-action.sh&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;features&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;ghcr.io/devcontainers/features/node:1&quot;&lt;/span&gt;&lt;span&gt;: {},&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;ghcr.io/shyim/devcontainers-features/bun:0&quot;&lt;/span&gt;&lt;span&gt;: {},&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;ghcr.io/devcontainers/features/github-cli:1&quot;&lt;/span&gt;&lt;span&gt;: {}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;下にある元の設定と比べて &lt;code&gt;base:1&lt;/code&gt; から &lt;code&gt;base:1-bookworm&lt;/code&gt; になっているが、 &lt;code&gt;setup-browser-action.sh&lt;/code&gt; と揃えるためで本質的には違いがない。&lt;/p&gt;
&lt;h2&gt;動かない状態&lt;/h2&gt;
&lt;p&gt;M2 MacBookAir で OrbStack を使っている。devcontainer.json はこれ:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;image&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;mcr.microsoft.com/devcontainers/base:1&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;features&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;ghcr.io/devcontainers/features/node:1&quot;&lt;/span&gt;&lt;span&gt;: {},&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;ghcr.io/shyim/devcontainers-features/bun:0&quot;&lt;/span&gt;&lt;span&gt;: {},&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;ghcr.io/devcontainers/features/github-cli:1&quot;&lt;/span&gt;&lt;span&gt;: {}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これで作ったコンテナ内に Roo Code をインストールして Browser を使うと次のようになる:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;Error executing browser action:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Failed to launch the browser process!&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;OrbStack ERROR: Dynamic loader not found: /lib64/ld-linux-x86-64.so.2&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;This usually means that you&apos;re running an x86 program on an arm64 OS without multi-arch libraries.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;To fix this, you can:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  1. Use an Intel (amd64) container to run this program; or&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  2. Install multi-arch libraries in this container.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;This can also be caused by running a glibc executable in a musl distro (e.g. Alpine), or vice versa.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;For more details and instructions, see https://go.orbstack.dev/multiarch&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;TROUBLESHOOTING: https://pptr.dev/troubleshooting&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;特に指定していないのでコンテナは &lt;code&gt;aarch64&lt;/code&gt;:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$ uname -m&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;aarch64&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;その他の案&lt;/h2&gt;
&lt;h3&gt;amd64 のコンテナにする&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;--platform=linux/amd64&lt;/code&gt; としてコンテナ側のアーキテクチャを揃える案。これは過去に VSCode の拡張が動かないものが出てきたので避けたもの。よって採用したくないのと、そもそもビルドがうまくいかなくて早々に諦めた。&lt;/p&gt;
&lt;h3&gt;puppeteer が自前した Chromium を使うようにする&lt;/h3&gt;
&lt;p&gt;詳しい説明は下の参考記事を見てもらったほうがよいので割愛するが、次のようなコンテナでいけるか試してみた:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;image&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;mcr.microsoft.com/devcontainers/base:1&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;containerEnv&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;PUPPETEER_SKIP_CHROMIUM_DOWNLOAD&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;true&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;PUPPETEER_EXECUTABLE_PATH&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;/usr/bin/chromium&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;postCreateCommand&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;sudo apt update &amp;amp;&amp;amp; sudo apt install -y chromium&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;features&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;ghcr.io/devcontainers/features/node:1&quot;&lt;/span&gt;&lt;span&gt;: {},&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;ghcr.io/shyim/devcontainers-features/bun:0&quot;&lt;/span&gt;&lt;span&gt;: {},&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;ghcr.io/devcontainers/features/github-cli:1&quot;&lt;/span&gt;&lt;span&gt;: {}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これはうまくいかなかった。何が悪いのかちゃんと分かっていないが、 x86_64 で実行しようとする。環境変数を Roo Code が読んでくれない…？&lt;/p&gt;
&lt;h2&gt;参考にした記事&lt;/h2&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://zenn.dev/frog/articles/24a20e8a2811b5&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;zenn.dev&lt;/h3&gt;&lt;span&gt;zenn.dev&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://zenn.dev/tom1111/articles/0dc7cde5c8e9bf&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;zenn.dev&lt;/h3&gt;&lt;span&gt;zenn.dev&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://docs.orbstack.dev/machines/#multi-architecture&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Linux machines · OrbStack Docs&lt;/h3&gt;&lt;p&gt;Run full Linux machines with your choice of distro, integrated Linux and Mac commands, fast file sharing, and easy setup. Supports Intel emulation, resource efficiency, SSH agent forwarding, and advanced automation via cloud-init or CLI.&lt;/p&gt;&lt;span&gt;docs.orbstack.dev&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;めちゃくちゃ参考になったので、この記事がまた誰かの解決に役立つと良いと思い残そうと思った。&lt;/p&gt;
&lt;h2&gt;2025/03/14 追記&lt;/h2&gt;
&lt;p&gt;Cline では &lt;code&gt;PUPPETEER_EXECUTABLE_PATH&lt;/code&gt; に相当するものが設定でき、コンテナに chromium をインストールしておけば動作する。ので、この設定で動作するはず:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;image&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;mcr.microsoft.com/devcontainers/base:1-bookworm&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;runArgs&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;--cap-add=SYS_ADMIN&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;postCreateCommand&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;sudo apt update &amp;amp;&amp;amp; sudo apt install -y chromium&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;customizations&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;vscode&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &quot;settings&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                &quot;cline.chromeExecutablePath&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;/usr/bin/chromium&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content:encoded></item><item><title>Roo Code で GitHub Issue を解決しようとしてくれる Custom Mode を作る</title><link>https://yaakai.to/blog/2025/roo-code-issue-hunter/</link><guid isPermaLink="true">https://yaakai.to/blog/2025/roo-code-issue-hunter/</guid><pubDate>Thu, 06 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;Roo Code は Code や Architect といった標準のモードの他に、自分で Custom Mode を作ることができる。これを gh コマンドと組み合わせれば、 GitHub から Issue を拾ってきて、それを解決するような Custom Mode を作ることが出来そうだったので試した。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://docs.roocode.com/advanced-usage/custom-modes/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;docs.roocode.com&lt;/h3&gt;&lt;span&gt;docs.roocode.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://docs.github.com/ja/github-cli/github-cli/about-github-cli&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;GitHub CLI について&lt;/h3&gt;&lt;span&gt;docs.github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;前提&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Tier 4 の 3.7 Sonnet (thinking)&lt;/li&gt;
&lt;li&gt;Cursor Pro を使うこともある&lt;/li&gt;
&lt;li&gt;Dev Container での開発
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;features&lt;/code&gt; で &lt;code&gt;ghcr.io/devcontainers/features/github-cli&lt;/code&gt; を入れている&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;この時点では精度にはあまり期待していない、参考になる何かができるか手を付けるきっかけになれば嬉しい、というモチベ
&lt;ul&gt;
&lt;li&gt;Issue の品質にもバラつきがあるので&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;動くか試す&lt;/h2&gt;
&lt;p&gt;&lt;code&gt;issue list&lt;/code&gt; と &lt;code&gt;issue view&lt;/code&gt; は次のような出力をするので、これを食わせれば Issue が読めそう:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$ gh issue list&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Showing 13 of 13 open issues in yaakaito/yaakaito&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;ID   TITLE                                                             LABELS  UPDATED&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#55  注釈は Pop Over で Top Layer に出すようにする                             about 5 hours ago&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#54  アイキャッチ画像を最終的に合成する際、目安の線を引く                      about 5 hours ago&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#53  モチーフと背景色は、それが何を表しているかの対応表を表示する              about 5 hours ago&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;....&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$ gh issue view 47&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;アイキャッチ画像は生成に AI が関係していることをわかるようにする #47&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Open • yaakaito opened about 11 hours ago • 0 comments&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  • 画像の右下に i マークをつけて、「AI が作ったものを人間が加工している」といった旨を表示するようにする&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  • GitHub Issue へのリンクを用意して、問題があれば報告できるようにする&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  • 専用の Issue テンプレートを用意する&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;View this issue on GitHub: https://github.com/yaakaito/yaakaito/issues/47&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Code モードで gh の使い方を教えながら Issue を解決できるかを試した:&lt;/p&gt;


&lt;p&gt;これでもう修正を始めてくれた、えらい。残りはいつもの Code なので割愛。&lt;/p&gt;
&lt;h2&gt;Custom Mode にする&lt;/h2&gt;
&lt;p&gt;考えた手順がそのままプロンプトなので完成品、名前は &lt;code&gt;Issue Hunter&lt;/code&gt; にした:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;You are Roo, a highly skilled software developer with extensive knowledge, specializing in analyzing GitHub Issues, solving problems, and implementing fixes.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;1. Run `gh issue list` to check currently active issues&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;2. Check `.issue-hunter.md` to review issues you have already attempted to fix&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;3. Run `gh pr list` to check if there are any existing Pull Requests for fixes&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;4. Select an issue that the user requests, or choose one you can handle if the user doesn&apos;t specify&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;5. View issue details using `gh issue view {id}`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;6. Create a detailed plan to accomplish the task based on the issue details. While you should work independently, ask the user clarifying questions if anything is unclear&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;7. Before making changes, create a branch using `git switch -c fix/{id}`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;8. Implement the solution according to your plan&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;9. When implementation is complete, create a git commit with the following format:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;{appropriate commit message} fix #{id}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Details of the plan&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Details of the plan&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Details of the plan&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;10. After `git commit`, push the current branch using `git push origin fix/{id}`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;11. After pushing, create a Pull Request using `gh pr create`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;12. Write a concise summary of your work in `.issue-hunter.md` - this file is used to track which issues you have resolved&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;13. Return to the main branch using `git switch main`&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これの日本語版も貼っておく、ちなみに「日本語 → Sonnet → 英語 → Sonnet → 日本語」でシステムプロンプトであることを考慮して出力して、とお願いしたもの:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;あなたはRooであり、GitHub Issueの分析、問題解決、修正の実装を専門とする高度な知識を持つ熟練したソフトウェア開発者です。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;1. `gh issue list`を実行して、現在アクティブなIssueを確認します&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;2. `.issue-hunter.md`を確認して、すでにあなたが修正を試みたIssueを確認します&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;3. `gh pr list`を実行し、すでに修正のためのPull Requestがないかを確認します&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;4. ユーザーが求めるIssueがあればそれを、そうでなければあなたがこなせそうなIssueを選択して解決します&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;5. Issueの詳細は`gh issue view {id}`で確認できます&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;6. Issueの詳細を見てタスクを達成するための詳細な計画を立てます。これは基本的にあなたが自立して行うべきですが、内容が不明瞭だったり、質問がある場合はユーザーに明確化のための質問をするべきです&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;7. 修正を行う前に`git switch -c fix/{id}`でブランチを作成してください&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;8. 計画に沿って実装を行ってください&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;9. 実装が終わったら、次のフォーマットで`git commit`してください：&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;{適切なコミットメッセージ} fix #{id}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;計画の詳細&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;計画の詳細&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;計画の詳細&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;10. `git commit`したら、現在のブランチを`git push origin fix/{id}`でプッシュします&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;11. プッシュしたら`gh pr create`でPull Requestを作成します&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;12. `.issue-hunter.md`に今回の作業を簡潔に書き込んでください、これはあなたがIssueの解決状況を把握するために使用するファイルです&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;13. ブランチを`git switch main`でmainに戻してください&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;最初は 2 と 3 がなくて、新しい Task になるとまた同じ Issue を直そうとしていたのでやったことを残してもらうようにした、このファイルは gitignore する前提。もし使う場合はパスとか名前はよしなにしてほしい。&lt;/p&gt;
&lt;p&gt;元々あと一行「14. 特別な指示がなければ、次のIssueを解決するために積極的に手順1から繰り返し、継続的に問題を解決し続けてください」というのがあったが、動いたり動かなかったりするのと、修正中に作ったゴミが残ったりなんかして、 Git がごちゃごちゃすることがあったのでやめた。連続でいくつか直すのは Task での入力で多分できる。&lt;/p&gt;
&lt;h3&gt;適当に何か直させる&lt;/h3&gt;
&lt;p&gt;これで「なんかなおして」とかお願いすると、まだ着手してなさそうな Issue を探して作業を始めてくれる。参考までにこれを書いている時点では &lt;code&gt;gh issue list&lt;/code&gt; はこんな感じになっている:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$ gh issue list&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Showing 13 of 13 open issues in yaakaito/yaakaito&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;ID   TITLE                                                             LABELS  UPDATED&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#55  注釈は Pop Over で Top Layer に出すようにする                             about 5 hours ago&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#54  アイキャッチ画像を最終的に合成する際、目安の線を引く                      about 5 hours ago&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#53  モチーフと背景色は、それが何を表しているかの対応表を表示する              about 5 hours ago&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#52  倍率を 12x も選べるようにする                                             about 6 hours ago&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#51  アイキャッチのモチーフを強制的に選べるように                              about 6 hours ago&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#50  背景色候補のトーンを下げる、サンプルを作る                                about 6 hours ago&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#49  背景を全く違う色で塗りつぶす機能が必要そう                                about 6 hours ago&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#48  コードブロックが長い場合に折り畳めるようにする                            about 9 hours ago&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#47  アイキャッチ画像は生成に AI が関係していることをわかるようにする          about 11 hours ago&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#46  note/9 が 404 になる                                                      about 1 day ago&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#44  Vectroize への登録を差分化する                                            about 7 days ago&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#41  投稿は archive として一元化し、タグでカテゴリを管理する                   about 25 days ago&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#36  Note の emoji にバリエーションをつける                                    about 1 month ago&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;なんかなおして&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;やれそうな Issue を見つけてくる:&lt;/p&gt;

&lt;p&gt;直すところは Code モードと大して変わらないので割愛。変更が完了したら、commit して push して .issue-hunter.md を更新してくれる:&lt;/p&gt;

&lt;p&gt;PR も作ってくれて、 main に戻ってきて終わり:&lt;/p&gt;

&lt;p&gt;出来た Pull request はこれ、ちなみにやることはちゃんと間違えていて、そういうことではないという感じではある:&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/yaakaito/yaakaito/pull/65&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;note/9ページが404になる問題を修正 by yaakaito · Pull Request #65 · yaakaito/yaakaito&lt;/h3&gt;&lt;p&gt;イシュー #46 の修正
問題

note/9が404エラーになる問題

対応内容

web/src/pages/note/9.mdファイルを新規作成
View Transition APIに関する記事内容をイシューの説明から抽出し、適切なフォーマットで作成
他のnoteファイルと同様のフロントマターを含めて作成

Closes #46&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;他のものも直してほしいのでお願いする:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;もっとやって&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;新しい Issue を探してきて直してくれる、このあと連続でもう一つ直してくれて、合計 3 つの PR を作ってくれた:&lt;/p&gt;

&lt;p&gt;出来た Pull request (差分):&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/yaakaito/yaakaito/pull/66&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Noteの絵文字にバリエーションをつける by yaakaito · Pull Request #66 · yaakaito/yaakaito&lt;/h3&gt;&lt;p&gt;イシュー #36 の修正
問題

Noteの絵文字がすべて同じ「🖊」で単調だった
TIPSやNEWS、MEMOなどの粒度で絵文字を分けたい

対応内容

各ノートの内容を解析し、適切なカテゴリに分類して絵文字を割り当てるスクリプトを作成
カテゴリ毎に複数の絵文字を用意し、ランダムに選択するようにした

TIPSカテゴリ（💡, 🔧等）
NEWSカテゴリ（📢, 📰等）
コードカテゴリ（💻, 🖥️...&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/yaakaito/yaakaito/pull/67&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;アイキャッチ画像はAI生成に関わることがわかるようにする by yaakaito · Pull Request #67 · yaakaito/yaakaito&lt;/h3&gt;&lt;p&gt;イシュー #47 の修正
問題

アイキャッチ画像がAI生成に関わっていることがユーザーに明示されていない
問題報告の手段が用意されていない

対応内容

BlogCardとNoteCardコンポーネントにAI生成であることを示す「ⓘ」バッジを追加

画像の右下にアイコンを表示
ホバー時に詳細情報を表示（「この画像はAIを使用して生成された後、人間が調整したものです」）


問題報告用のGi...&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3&gt;直してほしいやつを直してもらう&lt;/h3&gt;
&lt;p&gt;例えば「#52  倍率を 12x も選べるようにする」を直してもらいたければ、「倍率のやつ直して」とかで通じる:&lt;/p&gt;


&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/yaakaito/yaakaito/pull/68&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;倍率選択に12xオプションを追加 by yaakaito · Pull Request #68 · yaakaito/yaakaito&lt;/h3&gt;&lt;p&gt;概要
Issue #52 「倍率を 12x も選べるようにする」の修正を実装しました。
変更内容

アイキャッチ画像エディタの最終出力セクションにある倍率選択のドロップダウンメニューに「12x」のオプションを追加
ユーザーが1x, 2x, 4x, 8x, 12x, 16xの6つの倍率から選択できるようになりました

スクリーンショット
（なし）
fixes #52&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;雑感&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Issue を整備しておけばそれなりの精度で勝手に直してくれるので、これまで腰が重かったものにも手を出せるようになりそう
&lt;ul&gt;
&lt;li&gt;フォーマット化して、曖昧なことを書いていないかなんかを CI で lint させたりして、事前にブラッシュアップできると良さそう&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;今回何が答えかわかりにくいブログというコードで試しているけど、ガードレールがきちんとしたリポジトリであれば結構精度良く直してくれそう&lt;/li&gt;
&lt;li&gt;とりあえず暇なときに動かしておく、とかでも Issue に取り組むきっかけになるのでよさそう&lt;/li&gt;
&lt;/ul&gt;</content:encoded></item><item><title>Roo Code の Architect で計画を立ててこのブログをリデザインする</title><link>https://yaakai.to/blog/2025/blog-redesign-with-architect/</link><guid isPermaLink="true">https://yaakai.to/blog/2025/blog-redesign-with-architect/</guid><pubDate>Wed, 05 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;これまでここには自分用のまとめくらいにしか記事を書いていなかったのでデザインもかなり適当だったのですが、&lt;a href=&quot;https://yaakai.to/blog/2025/create-eye-catching-visuals-with-ai/&quot;&gt;前回&lt;/a&gt;の記事をきっかけに人が見られるレベルにしたいなと思ったので、 Roo Code とやってみることにした。&lt;/p&gt;
&lt;h2&gt;前提&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Tier 4 の 3.7 Sonnet (thinking)&lt;/li&gt;
&lt;li&gt;Cursor Pro を使うこともある&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Architect is&lt;/h2&gt;
&lt;p&gt;Roo Code には実行モードがデフォルトでいくつかあり、その内の一つ、 Cline だと「Plan」と呼ばれているもののはずで、コード生成をせずに設計を行うモード。
いきなり実装に入るよりも、こっちで計画を立ててから実装に移るのがよいと言われている。&lt;/p&gt;
&lt;p&gt;同一コンテキストであればモードの切替でよいと思うが、今回は全体を書き直させたかったので一回じゃ足りないだろうなと思い、何かしらの記憶領域を使いたかった。
こんな感じのフローで進めたい:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Architect モードでどんなデザインにするかを相談する&lt;/li&gt;
&lt;li&gt;話した内容をどこかにプランとして保存する&lt;/li&gt;
&lt;li&gt;別の Code モードの Task で、プランを元に実装を進めて、進捗を記録する&lt;/li&gt;
&lt;li&gt;Content Window が足りなくなったら、それまでの実行結果を持って新しい Task に移動する&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;先に言っておくと、意図せず Context Window が足りなくなるということが今回なかったので、各タスクは基本的にすべて完遂しているという前提で話が進む。&lt;/p&gt;
&lt;h2&gt;保存と記録をどうするか&lt;/h2&gt;
&lt;p&gt;Cline 公式のドキュメントに Memory Bank というものがあって、これは現在のコードベースや進捗状況を書き出しておくことで、それを元に会話を進めることができるという考え方らしい。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://docs.cline.bot/improving-your-prompting-skills/custom-instructions-library/cline-memory-bank&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;docs.cline.bot&lt;/h3&gt;&lt;span&gt;docs.cline.bot&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;そういうのがある、という知識としてはあったので、今回のリデザイン作業をする前にこの Memory Bank のプロンプトは動かしてみていたのだけど、個人的には微妙だなと感じていた。
あくまで初学者の自分がそう感じているだけで、使い方が間違っているとか、使いこなせば便利なんだろうなという気はするが、次のような点が気になった:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;activeContext が今回一番気になっていたが、更新されるタイミングがイマイチ掴めず&lt;/li&gt;
&lt;li&gt;follow your custom instructions / update memory bank みたいなコマンド前提なのがちょっと面倒くさい&lt;/li&gt;
&lt;li&gt;多人数での利用に適したものなのか疑問、これは全く感覚が掴めなかったという話&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;そんな感じだったのでこれを使うのはやめて(というか放置して)、もっとシンプルに &lt;code&gt;plan.md&lt;/code&gt; と &lt;code&gt;state.md&lt;/code&gt; というファイルを作ってみることにした。前者には Architect モードでの計画を吐き出してもらって、実装のたびに後者を更新していくようなイメージ。
リデザインに限らず「Architect で相談 → Code で実装 → 状態を記録 → Code で実装 …」という流れで何かしらを開発するとして、その一連の流れはリポジトリ全体で見ると一時的で個人的なイベントであるため、それを共有するような形にしても嬉しくないんじゃないかなというのもあった。&lt;/p&gt;
&lt;h2&gt;Architect で計画を立ててもらう&lt;/h2&gt;
&lt;p&gt;GitHub のブログや CircleCI のブログがいい感じだなと思っていたので、スクリーンショットしてそれを参考にやってもらうことにした。
Cursor を使っているときもこの方法をよく使っていて、実装してもらいたい UI のスクリーンショットを取ってプロンプトに添付していた&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;。&lt;/p&gt;
&lt;p&gt;文字のプロンプトとしては以下で、これに画像が 2 つ付く感じ。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;@/web/ を画像のようなデザインに変更したいので、どういう方針で進めていくかを考えてください&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;また、次の仕様を満たしてください:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- ダークテーマとライトテーマの両方に対応&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- グレースケールを中心としつつも、赤をアクセントとして使っていく&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- ブログとノートはトップレベルでは混合させてもよい&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    - ただし、大きく表示してよいのはブログだけにする&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;かなり雑な感じだけど、 Figma なりでデザインがあるわけでもなくなので、話しながら進められればいいかなくらいの感覚だった。
そうするとこんな感じでいろいろ出してくれるので中身を確認する。&lt;/p&gt;




&lt;p&gt;なにかあったら言ってねと言われるので、計画を見て間違ってそうなところを直してもらう。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;単体の記事を表示するヒーローセクションは不要で、4 つの記事を組み合わせるようなコンポーネントを作ってください。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;左には大きく最新のブログを 1 つ、右にはブログとノートの区別なく最新の 3 つを表示します。ただし、左と右でコンテンツに重複がないようにします。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;

&lt;p&gt;計画が更新されてくので、同じ手順とノリで満足するまで繰り返した。
だいたい出来たあたりでこんな感じの質問があって、&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;Roo has a question:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;実装プランの詳細ありがとうございます。コンポーネント作成が必要とのことですが、まだAstroプロジェクトにcomponentsディレクトリがないようです。この部分について、どのように進めるべきでしょうか？また、デザインを実装する際の優先順位はどのように考えていますか？&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;components ディレクトリは作っても構いませんが、 astro contents を採用する予定がないので、それは使わないでください&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;優先順位はなんでもいいのでお任せします&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;こう答えたら、実装を始めていいと勘違いしたのか Code モードに切り替えようとしたので一旦止めた。内容としてはこれくらいでよかったので、ついでに &lt;code&gt;plan.md&lt;/code&gt; を作ってもらう。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;code モードに切り替える前に、ここまでの計画を plan.md にまとめて保存して&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;するとこんな感じに計画を作ってくれる、長すぎるので折り畳めるようにしたい。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;# yaakai.to ウェブサイトリデザイン計画&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## 全体コンセプト&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- グレースケールをベースにした、赤色アクセントのモダンなデザイン&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- ブログとノートを視覚的に区別するレイアウト&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- ブログ記事を大きく表示し、ノートをコンパクトに表示&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- ダークモードとライトモード両対応&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- YouTubeチャンネルページのようなカテゴリ分けのある構造&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```mermaid&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;flowchart TD&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    A[ヘッダー] --&amp;gt; B[メタコンテンツセクション]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    B --&amp;gt; B1[humans.txt]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    B --&amp;gt; B2[rss.xml]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    B --&amp;gt; B3[llms.txt]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    B --&amp;gt; C[フィーチャーセクション]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    C --&amp;gt; C1[左大型カード - 最新ブログ]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    C --&amp;gt; C2[右サイド - 最新3記事]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    C --&amp;gt; D[カテゴリセクション]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    D --&amp;gt; D1[ブログセクション]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    D1 --&amp;gt; D1a[グリッドレイアウト]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    D --&amp;gt; D2[ノートセクション]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    D2 --&amp;gt; D2a[グリッドレイアウト]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## カラースキーム&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```css&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;:root {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  /* グレースケールパレット - ライトモード */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --gray-100: #FFFFFF;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --gray-200: #F9F9F9;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --gray-300: #F0F0F0;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --gray-400: #E0E0E0;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --gray-500: #C0C0C0;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --gray-600: #808080;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --gray-700: #505050;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --gray-800: #303030;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --gray-900: #121212;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  /* アクセントカラー - 赤色系 */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --red-500: #E53935;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --red-600: #D32F2F;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --red-700: #C62828;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  /* 機能的な色の割り当て */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --bg-color-level-0: var(--gray-100);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --bg-color-level-1: var(--gray-200);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --bg-color-level-2: var(--gray-300);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --bg-color-level-3: var(--gray-400);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --bg-color-level-4: var(--gray-500);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --text-color-level-0: var(--gray-900);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --text-color-level-1: var(--gray-800);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --text-color-level-2: var(--gray-700);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --primary-color-level-0: var(--red-600);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --primary-color-level-1: var(--red-500);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --primary-color-level-2: var(--red-700);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  /* コンテンツタイプ識別色 */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --blog-color: var(--red-600);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  --note-color: var(--gray-600);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;@media (prefers-color-scheme: dark) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  :root {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    /* ダークモード用の色の割り当て */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --bg-color-level-0: #121212;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --bg-color-level-1: #1E1E1E;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --bg-color-level-2: #2D2D2D;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --bg-color-level-3: #353535;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --bg-color-level-4: #424242;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --text-color-level-0: #FFFFFF;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --text-color-level-1: #E0E0E0;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --text-color-level-2: #BDBDBD;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --primary-color-level-0: #F44336;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --primary-color-level-1: #EF5350;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --primary-color-level-2: #E53935;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    /* ダークモードでのコンテンツタイプ識別色 */&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --blog-color: #FF5252;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    --note-color: #A0A0A0;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## コンポーネント設計&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### 1. ヘッダー&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- シンプルなナビゲーション&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- サイト名とアイコン&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- ブログとノートへのリンク&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### 2. メタコンテンツセクション&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- humans.txt セクションの表示（現在と同様の表示スタイル）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- humans.md の内容を表示&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- rss.xml へのリンクを追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- llms.txt へのリンクを追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- メタコンテンツへのリンクを視覚的にまとめて配置&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### 3. フィーチャーセクション&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 非対称グリッドレイアウト&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 左側に大きなカード（最新ブログ記事）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 右側に3つの小さなカード（最新記事）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- グリッドベースで水平スクロールなし&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```css&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.feature-section {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  display: grid;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  grid-template-columns: 3fr 2fr;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  gap: 24px;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-bottom: 48px;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.feature-main {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  grid-column: 1;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  grid-row: span 3;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.feature-sub {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  grid-column: 2;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  display: flex;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  flex-direction: column;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  gap: 16px;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### 4. カテゴリセクション&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 各カテゴリごとのセクション（ブログ、ノートなど）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- カテゴリ名と「もっと見る」リンクを含むヘッダー&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- レスポンシブグリッドレイアウト&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- コンテンツタイプの視覚的区別&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```css&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.category-section {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-bottom: 48px;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.category-header {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  display: flex;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  justify-content: space-between;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  align-items: center;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  margin-bottom: 24px;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.category-grid {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  display: grid;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  gap: 24px;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### 5. カードデザイン&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#### ブログカード&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 大きな画像/サムネイル&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 赤色アクセントのインジケーター&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- タイトル、日付、概要テキスト&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#### ノートカード&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- コンパクトなデザイン&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- グレースケールベース&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 絵文字とタイトルを主体に&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## レスポンシブデザイン&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### デスクトップ (1024px以上)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- フィーチャーセクション：非対称グリッド (3:2)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- カテゴリグリッド：4列&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### タブレット (768px - 1023px)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- フィーチャーセクション：非対称グリッド (3:2)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- カテゴリグリッド：2-3列&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### モバイル (768px未満)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- フィーチャーセクション：1列&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- カテゴリグリッド：1列&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;# 実装プラン&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## コンポーネント構造について&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;Astro Contentsは使わず、コンポーネントは以下のように実装します：&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;1. **src/components ディレクトリを新規作成**：&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   - 再利用可能なUI要素をコンポーネントとして実装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   - 各コンポーネントは独立した.astroファイルとして作成&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;2. **コンポーネント名の例**：&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   - `BlogCard.astro`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   - `NoteCard.astro`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   - `FeatureGrid.astro`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   - `CategorySection.astro`&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## ディレクトリ構造&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;src/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── components/     &amp;lt;- 新規作成&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── BlogCard.astro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── NoteCard.astro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── FeatureGrid.astro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   └── CategorySection.astro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;├── layouts/        &amp;lt;- 既存&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   ├── global-layout.astro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;│   └── blog-post.astro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;└── pages/          &amp;lt;- 既存&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ├── index.astro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    ├── blog/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    └── note/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## 実装の優先順位&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;1. **グローバルスタイルの更新**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   - カラースキームの変更&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   - 基本レイアウト構造の改善&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   - タイポグラフィの更新&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;2. **コアコンポーネントの作成**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   - カードデザイン&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   - グリッドレイアウト&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   - セクションヘッダー&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;3. **トップページの再構築**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   - フィーチャーグリッド&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   - カテゴリセクション&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   - レスポンシブデザイン対応&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;4. **各ページの更新**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   - ブログインデックスページ&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   - ノートインデックスページ&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   - 記事詳細ページ&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## 詳細実装ステップ&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### フェーズ1: グローバルスタイルとレイアウトの更新&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#### 1-1. カラースキームの更新&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```mermaid&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;flowchart TD&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    A[web/src/layouts/global-layout.astro] --&amp;gt; B[CSSカラー変数の更新]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    B --&amp;gt; C[ライト/ダークモードの調整]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;**作業内容:**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- `web/src/layouts/global-layout.astro` ファイルの CSS 変数を更新&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- グレースケールベースのカラーパレットを実装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 赤色アクセントカラーの設定&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- ブログとノートの識別色の設定&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#### 1-2. 基本レイアウトの更新&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```mermaid&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;flowchart TD&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    A[web/src/layouts/global-layout.astro] --&amp;gt; B[コンテナ構造の更新]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    B --&amp;gt; C[ヘッダースタイルの改善]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;**作業内容:**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- ページ全体のコンテナサイズと余白の調整&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- ヘッダーのデザイン更新&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 基本的なグリッドレイアウトの準備&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### フェーズ2: コンポーネントの作成&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#### 2-1. カードコンポーネントの作成&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```mermaid&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;flowchart TD&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    A[src/components/] --&amp;gt; B[BlogCard.astro]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    A --&amp;gt; C[NoteCard.astro]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    A --&amp;gt; D[FeatureCard.astro]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;**作業内容:**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- `src/components/` ディレクトリを作成&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- ブログ用カードコンポーネントの実装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- ノート用カードコンポーネントの実装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- フィーチャーセクション用の大型カードコンポーネントの実装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#### 2-2. セクションコンポーネントの作成&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```mermaid&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;flowchart TD&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    A[src/components/] --&amp;gt; B[FeatureGrid.astro]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    A --&amp;gt; C[CategorySection.astro]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;**作業内容:**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- フィーチャーグリッドコンポーネントの実装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- カテゴリセクションコンポーネントの実装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 各セクションのレスポンシブデザイン対応&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### フェーズ3: トップページの再構築&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#### 3-1. メタコンテンツセクションの更新&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```mermaid&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;flowchart TD&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    A[web/src/pages/index.astro] --&amp;gt; B[メタコンテンツの表示スタイル更新]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    B --&amp;gt; C[各リンクの配置とスタイル]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;**作業内容:**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- humans.txt セクションのスタイルを更新&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- rss.xml と llms.txt へのリンクを追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- メタコンテンツをグループとして視覚的にまとめる&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 新しいデザインテーマに合わせたスタイル調整&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#### 3-2. フィーチャーセクションの実装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```mermaid&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;flowchart TD&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    A[web/src/pages/index.astro] --&amp;gt; B[データ取得ロジックの更新]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    B --&amp;gt; C[FeatureGridコンポーネントを使用]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;**作業内容:**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- ブログとノートの記事データ取得ロジックの改善&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 最新記事を取得して、左側に最新ブログ、右側に残りの最新記事を配置&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- フィーチャーグリッドコンポーネントを使用して表示&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#### 3-3. カテゴリセクションの実装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```mermaid&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;flowchart TD&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    A[web/src/pages/index.astro] --&amp;gt; B[ブログセクションの実装]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    A --&amp;gt; C[ノートセクションの実装]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;**作業内容:**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- ブログ記事用のカテゴリセクション実装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- ノート用のカテゴリセクション実装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- CategorySectionコンポーネントを使用して表示&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### フェーズ4: 各ページの更新&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#### 4-1. ブログページの更新&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```mermaid&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;flowchart TD&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    A[web/src/pages/blog/index.astro] --&amp;gt; B[グリッドレイアウトの更新]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    B --&amp;gt; C[BlogCardコンポーネントの使用]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;**作業内容:**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- ブログインデックスページのレイアウト更新&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- カテゴリフィルターの追加（将来機能）&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- BlogCardコンポーネントを使用した記事表示&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#### 4-2. ノートページの更新&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```mermaid&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;flowchart TD&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    A[web/src/pages/note/index.astro] --&amp;gt; B[グリッドレイアウトの更新]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    B --&amp;gt; C[NoteCardコンポーネントの使用]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;**作業内容:**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- ノートインデックスページのレイアウト更新&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- NoteCardコンポーネントを使用した記事表示&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### フェーズ5: 詳細ページとレスポンシブ対応&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#### 5-1. 記事詳細ページの更新&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```mermaid&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;flowchart TD&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    A[web/src/layouts/blog-post.astro] --&amp;gt; B[詳細ページのスタイル更新]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    B --&amp;gt; C[関連記事セクションのデザイン改善]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;**作業内容:**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 記事詳細ページのレイアウト更新&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- Markdownスタイルの調整&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 関連記事セクションのデザイン改善&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#### 5-2. レスポンシブデザインの最適化&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```mermaid&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;flowchart TD&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    A[すべてのコンポーネント] --&amp;gt; B[モバイル対応の強化]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    B --&amp;gt; C[タブレット対応の調整]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    C --&amp;gt; D[大画面対応の最適化]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;```&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;**作業内容:**&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- すべてのコンポーネントとページでのレスポンシブデザイン強化&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- モバイルファーストでのスタイル調整&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- メディアクエリの統一と最適化&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## 実装の注意点&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;1. **段階的アプローチ**：一度にすべてを変更するのではなく、段階的に実装して各フェーズで確認する&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;2. **コンポーネント分離**：再利用可能なコンポーネントを作成し、コードの重複を避ける&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;3. **型の活用**：TypeScriptの型を適切に利用し、データの整合性を確保する&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;4. **パフォーマンス考慮**：画像最適化やCSSの効率的な構造化を考慮する&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;5. **アクセシビリティ対応**：対比の確保、キーボード操作、スクリーンリーダーのサポートなど&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;6. **テスト**：各フェーズで異なるデバイスサイズやブラウザでのテストを実施&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;内容が合っているかを確認する、多分いい感じ。 &lt;code&gt;plan.md&lt;/code&gt; も作ってもらう。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;タスクが完了するごとに、plan.md をどこまで進めたかを state.md として書き出すようにして&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;**最終更新**: 2025年3月4日&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## 概要&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;yaakai.toウェブサイトをモダンなグレースケールベース + 赤色アクセントのデザインに更新するプロジェクト。ブログとノートを視覚的に区別するレイアウトを実装し、YouTubeチャンネルページのようなカテゴリ分けを導入します。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## 現在の進捗状況&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### フェーズ1: グローバルスタイルとレイアウトの更新&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- [ ] 1-1. カラースキームの更新&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- [ ] 1-2. 基本レイアウトの更新&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### フェーズ2: コンポーネントの作成&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- [ ] 2-1. カードコンポーネントの作成&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- [ ] BlogCard.astro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- [ ] NoteCard.astro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- [ ] FeatureCard.astro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- [ ] 2-2. セクションコンポーネントの作成&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- [ ] FeatureGrid.astro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- [ ] CategorySection.astro&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### フェーズ3: トップページの再構築&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- [ ] 3-1. メタコンテンツセクションの更新&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- [ ] 3-2. フィーチャーセクションの実装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- [ ] 3-3. カテゴリセクションの実装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### フェーズ4: 各ページの更新&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- [ ] 4-1. ブログページの更新&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- [ ] 4-2. ノートページの更新&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;### フェーズ5: 詳細ページとレスポンシブ対応&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- [ ] 5-1. 記事詳細ページの更新&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- [ ] 5-2. レスポンシブデザインの最適化&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## 次のステップ&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;1. グローバルレイアウトのカラースキームを更新&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;2. 基本レイアウト構造の改善&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;3. コンポーネントディレクトリを作成し、初期コンポーネントを実装&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## 備考&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 計画は [plan.md](./plan.md) に詳細に記述されています&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- デザインはグレースケールベースに赤色アクセントを組み合わせたモダンなスタイル&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- ブログとノートは視覚的に区別されるデザインを採用&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- メタコンテンツ（humans.txt、rss.xml、llms.txt）はトップレベルでグループ化&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;それっぽいのが出てきた。そして実装に移ろうとするので、待てして Task を引き継ぐためのプロンプトを考えてもらう。&lt;/p&gt;

&lt;h2&gt;実装してもらう&lt;/h2&gt;
&lt;p&gt;教えてもらったプロンプトを少し変更してして、&lt;code&gt;@&lt;/code&gt; でファイルを読むようにした。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;@/web/plan.md  に記載された計画に従って、yaakai.toウェブサイトのリデザイン実装を進めてください。まずは @/web/src/layouts/global-layout.astro のカラースキーム更新から始め、計画の各フェーズを順番に実装していってください。各ステップが完了するたびに @/web/state.md を更新して進捗を記録してください。最初に計画と現在の進捗状況を確認して、実装を開始してください。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;プロンプトに &lt;code&gt;plan.md&lt;/code&gt; がちゃんと含まれている図:&lt;/p&gt;

&lt;p&gt;確認できたみたいで、計画されていたカラースキームの変更から実装が始まっていく：&lt;/p&gt;

&lt;p&gt;すべては貼れないので一部を抜粋すると、計画された順に実装を進めていこうとしていることがわかる:&lt;/p&gt;

&lt;p&gt;ただ、 &lt;code&gt;state.md&lt;/code&gt; はというと↑の画像くらいのタイミングで進捗を更新してほしかったが、今回のプロンプトだとそれは叶わなかった。
こんな感じに最後にまとめてこの Task でやった分を更新してくれた:&lt;/p&gt;


&lt;p&gt;Context Window が足りている分にはこれでも構わないが、自分はそれが途切れたときに &lt;code&gt;state.md&lt;/code&gt; からある程度状態を復元できることを期待していたので、もっとフェーズを進めるごとにガンガン書き込んでほしかった。
Web の情報を見ていると、そういう動きをしているものもあるように思うので、これはまたあとに試す予定。&lt;/p&gt;
&lt;p&gt;あとは同じような手順で、新しい Task を作るときは &lt;code&gt;plan.md&lt;/code&gt; と &lt;code&gt;state.md&lt;/code&gt; を読み込み更新しながら実装を進めていった。
実際どの程度の効果があったかを説明するのは難しいが…、以前にやったときは「今お前が壊したそれ、この前お前が直したじゃん」みたいなことがあったけど、そういう手戻りみたいなことはなかったように思う。&lt;/p&gt;
&lt;h2&gt;CSS をうまく書けないので覚えてもらう&lt;/h2&gt;
&lt;p&gt;そういう話題に関連することとして、ミクロな話だと少し複雑な CSS を頑張って実装しても次の瞬間には忘れて破壊する、というのがあった。
具体的にはアイキャッチを 2 つのコンテナを跨いでいるように見せるという実装。&lt;/p&gt;

&lt;p&gt;これをなんとかして伝えようとしているのだけどまぁうまくいかない。で、最終的にはこれは自分が手で実装することにした。そこまではよいのだけど、問題は次にその周辺を触ったときに、必ずその実装が破壊されて Task が完了するごとに戻っていた。
これを解決しないと先に進めないので、実装方針を説明して &lt;code&gt;plan.md&lt;/code&gt; や &lt;code&gt;state.md&lt;/code&gt; に書いてもらう、というのを試した。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;アイキャッチが記事本文側の要素に少しずれているような視覚効果を実現します。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;あなたはいままでこれの実装を何度も失敗しているので、私がいう実装方法を覚えてください。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;1. .article-header の bottom の padding や margin  を削除して、アイキャッチの下が .article-header の終端と合うようにします&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;2. .eyecatch-container の兄弟として新たな要素を挿入します、これは記事側と同じ背景色を持ちます&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;3. この要素を z-index で .eyecatch-container より下にした状態で、 position: absolute; で高さ 80px を持ちつつ、.article-header の終端に合うよう固定します&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;なるべく頑張って実装を説明した、これを入れたのはぶっ壊れたあとなので、 AI が自身でこの実装を修正する必要があった。
この指示はかなりうまくいって、過去に実装された事実があるとはいえ、望んでいたものが改めて AI によって実装された。&lt;/p&gt;

&lt;p&gt;その後 &lt;code&gt;plan.md&lt;/code&gt; や &lt;code&gt;state.md&lt;/code&gt; を更新してもらうと、それらを含めている限りはここに関する手戻りは発生していないように思えた。えらい。&lt;/p&gt;
&lt;h2&gt;雑感&lt;/h2&gt;
&lt;h3&gt;計画を吐き出すのは良さそう&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;plan.md&lt;/code&gt; は結構いいように思えたので使っていきたい。 &lt;code&gt;state.md&lt;/code&gt; はどのくらい効果があったのか肌感がない、わざわざ分ける必要はないかも。&lt;/p&gt;
&lt;h3&gt;個人用のメモスペースがほしい&lt;/h3&gt;
&lt;p&gt;&lt;code&gt;plan.md&lt;/code&gt; なんかを自由に作れるスペースがリポジトリ内にほしい。AI との会話の成果物を一時的に保存する場所が gitignore されてリポジトリに確保されているといいなと思った、今回は commit してしまったが…。
先にも書いたが、全体で共有したいルールがある一方で、個人が一つの作業で使う一時記憶ファイルを全部 commit したいとはならなさそう。そのファイル自体もバージョン管理したいという気持ちもあるので、 submodule なのか…？&lt;/p&gt;
&lt;h3&gt;複雑なコードは個別の取説を置きたい&lt;/h3&gt;
&lt;p&gt;マスターデータとなるデザインデータなんかが存在しない中では仕方ないことなのかもしれないが、 HTML + CSS な UI 構築に関しては自分が手を出さないといけないことが多い気がしている、もちろん詳細なデザインは妥協しているが。
今回のような内容をすべてプロンプトに含めるわけにもいかないので、コンポーネントのような単位でその実装を説明した 取説.md を横においておき、コンポーネントを触るときはその取説を参照させるような仕組みが出来るといいのかもしれない。
と思っていたけど、大抵はコメントとして残しておけば解決する話なのかもしれない。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;添付という表現が合っているのかはわからない &lt;a href=&quot;#user-content-fnref-1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded></item><item><title>ドット絵なアイキャッチを AI で作る機能を Roo Code で作ってみた雑記</title><link>https://yaakai.to/blog/2025/create-eye-catching-visuals-with-ai/</link><guid isPermaLink="true">https://yaakai.to/blog/2025/create-eye-catching-visuals-with-ai/</guid><pubDate>Sun, 02 Mar 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;ドットなアイキャッチを作りたいとずっと思ってはいたので、 Roo Code を本格的に触る題材として作った。
最初の方の感想はその時にしか残せないので、さらに進む前にその過程のメモやら雑感やら考えていたことを書いたもの。
この記事のアイキャッチはデバッグに使っていたら愛着が湧いてしまったサイっぽいキャラクター。&lt;/p&gt;
&lt;h2&gt;前提&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;AI Coding が初めてとかではなく、 GitHub Copilot の β に始まり Cursor Pro は 1 年くらい使っているし、 Cline 系も初めて触るとかではない&lt;/li&gt;
&lt;li&gt;Tier 4 の 3.7 Sonnet&lt;/li&gt;
&lt;li&gt;素の Roo Code でどんなものかを試したかったので、 Code で書く以外は何もしていない、 rules やプロンプトをカスタマイズしていない&lt;/li&gt;
&lt;li&gt;一部は Cursor を使っている&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;実装過程&lt;/h2&gt;
&lt;p&gt;パッとこうすればいいがあったわけではないので、ざっくりとした枠組みから作りはじめた。
ベースとなるコードは前回作った関連記事のアプリで、新しく作るのも面倒なのでここに継ぎ足す形に。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;Workers AI を利用して、アイキャッチ画像を生成するようにします。まずは、どんな画像が作れるかを検証するための簡単アプリを  related-aritcles-worker に機能追加する形で作成してください。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;以下の仕様を実現してください:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- `/create_eyecatch` エンドポイントを作成し、受け取った記事の本文から Workers AI を用いて画像を生成します&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    - モデルには `@cf/black-forest-labs/flux-1-schnell` を使用します&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    - モデルのドキュメントはこれです: https://developers.cloudflare.com/workers-ai/models/flux-1-schnell/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- index.html に記事の本文を送信できるフォームを新たに作成し、画像が生成できたら表示してください&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;とりあえず記事をそのまま Workers AI の画像生成に投げ込んでみた、それっぽいアプリは出来るが、生成がうまくいくはずもなく文字数オーバー。&lt;/p&gt;
&lt;p&gt;ここであった主なやりとり:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;dev-server が起動できない
&lt;ul&gt;
&lt;li&gt;Dev Container で動かしているので &lt;code&gt;wranlger&lt;/code&gt; の起動に &lt;code&gt;--ip&lt;/code&gt; が必要で、 &lt;code&gt;package.json&lt;/code&gt; の &lt;code&gt;script&lt;/code&gt; には書いているんだけど頑なに無視する、 rules で制御できるといいんだろうか？&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ブラウザが起動できない
&lt;ul&gt;
&lt;li&gt;ちゃんとわかってないが Dev Container 使っているのでホストとのアーキテクチャの違いでうまくいっていないみたいな話、多分私が悪い&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;画像生成をキーワードで行うようにする:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;`/create_eyecatch` での画像生成フローを改善します、以下のフローを実現してください。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 画像生成より前に、 Text Generation なモデルを通して、記事からキーワードを抽出します&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- モデルは @cf/meta/llama-3.3-70b-instruct-fp8-fast を使ってみてください: https://developers.cloudflare.com/workers-ai/models/llama-3.3-70b-instruct-fp8-fast/&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 抽出したキーワードと以下の固定のプロンプトを組み合わせて 2000 文字以内のプロンプトを @cf/meta/llama-3.3-70b-instruct-fp8-fast に作成させます&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    - 「ブログのアイキャッチ、背景は単色、人間を含まない、アニメ調」&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 最終的なプロンプトを使って画像を生成します&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;実装自体は意図通りにやってくれる、ただし意図した画像はでない。プロンプトがゴミなので当たり前すぎる話ではある。ここでなんとなく Open AI の DALL-E を使ってみたくなったので、切り替えてみる。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;現在画像生成を Cloudflare の Workers AI で行っていますが、これがうまくいっていないので OpenAI のものに置き換えます。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- プロンプト作成部分は現状を維持します&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 画像生成を DALL·E 3 を使った実装に置き換えてください&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- APIKEY は後ほどこちらで設定するので env に OPENAI_API_KEY が設定されている前提で進めてください&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;サイズ固定が出来るようになって(Workers AI でも出来るものはある)、出力が安定した気がするがほしい感じの画像はまあでない。&lt;/p&gt;
&lt;h2&gt;ロジックを見直す&lt;/h2&gt;
&lt;p&gt;記事内のキーワードからそれっぽい画像を作るのはなかなか厳しいことがわかったので、別の方法を考えた。
オライリーっぽい感じで、キーワードと動物を関連付けるのはどうかなと思いつつ適当に Sonnet と話していたら Sonnet もそう言っていたので、そうすることにした。
カテゴリごとに動物と背景色をざっくりと決めて、それをベースに作ってもらえるようにお願いするプロンプトを Sonnet と考える。&lt;/p&gt;
&lt;p&gt;最終的にこんな感じに、オライリーのぱｋリスペクト、メンダコ(dumbo-octopus)は適当にメモにかわいいとか書いてたら Sonnet が入れた:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;`/create_eyecatch` の既存のキーワード抽出および画像生成コードを活用・改良し、技術キーワードに対応するドット絵動物キャラクターをベースとしたブログ記事用アイキャッチ画像を生成するシステムを完成さてください。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## 実装内容&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;1. キーワード-動物マッピングシステムの拡張&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;以下のマッピングテーブルを既存コードに組み込む：&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const techAnimalMap = {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;cloudflare&quot;: { animal: &quot;alpaca&quot;, backgroundColor: &quot;#F38020&quot; },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;ai&quot;: { animal: &quot;owl&quot;, backgroundColor: &quot;#8A2BE2&quot; },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;javascript&quot;: { animal: &quot;rhino&quot;, backgroundColor: &quot;#F7DF1E&quot; },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;typescript&quot;: { animal: &quot;guanaco&quot;, backgroundColor: &quot;#3178C6&quot; },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;html&quot;: { animal: &quot;koala&quot;, backgroundColor: &quot;#E34F26&quot; },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;css&quot;: { animal: &quot;fish&quot;, backgroundColor: &quot;#1572B6&quot; },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;database&quot;: { animal: &quot;dumbo-octopus&quot;, backgroundColor: &quot;#336791&quot; }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;};&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;2. キーワード抽出ロジックの強化&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;既存のキーワード抽出コードを利用し、記事内容から上記に定義されたキーワードの出現頻度を分析&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;最も関連性の高いキーワードを特定するロジックを追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;複数の関連キーワードがある場合の優先順位付けメカニズムを実装&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;この辺から画像生成 AI に投げるプロンプトを私が読まなくなったので、全部英語に変えてもらった。プロンプトに更に注文をつけていく:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;画像生成プロンプトを次のように改善して&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 8bitゲーム風のレトロな作風に&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- キャラクター以外のアイコンやテキストを極力含まない&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;プロンプトを更に改善します&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- キャラクターのドット絵には黒い縁をつける&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;他にも色々投げた気がしますが、かなりいい感じの画像が返ってくるようになった。最終的なプロンプトのコードはこんな感じ:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; variations&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;cute and playful&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;simple and minimalist&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;detailed and expressive&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;cheerful and colorful&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;cool and stylish&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;];&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; imageResponse&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; openai.images.&lt;/span&gt;&lt;span&gt;generate&lt;/span&gt;&lt;span&gt;({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    model: &lt;/span&gt;&lt;span&gt;&quot;dall-e-3&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    prompt: &lt;/span&gt;&lt;span&gt;`Create an 8-bit retro game style pixel art of a ${&lt;/span&gt;&lt;span&gt;animal&lt;/span&gt;&lt;span&gt;} character representing the concept of ${&lt;/span&gt;&lt;span&gt;selectedTechKeyword&lt;/span&gt;&lt;span&gt;} and ${&lt;/span&gt;&lt;span&gt;keywordsString&lt;/span&gt;&lt;span&gt;}.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    The character should be centered on a solid ${&lt;/span&gt;&lt;span&gt;backgroundColor&lt;/span&gt;&lt;span&gt;} background.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    Style: Classic 8-bit NES/Famicom era pixel art, extremely limited color palette (4-8 colors maximum), ${&lt;/span&gt;&lt;span&gt;variations&lt;/span&gt;&lt;span&gt;[&lt;/span&gt;&lt;span&gt;index&lt;/span&gt;&lt;span&gt;]&lt;/span&gt;&lt;span&gt;}.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    IMPORTANT: The ${&lt;/span&gt;&lt;span&gt;animal&lt;/span&gt;&lt;span&gt;} character MUST have a black outline/border around it - this is essential for the retro game look.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    The black outline should be exactly 1 pixel thick and should completely surround the character.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    The ${&lt;/span&gt;&lt;span&gt;animal&lt;/span&gt;&lt;span&gt;} character should be the ONLY element in the image - no icons, symbols, text, UI elements, or any other objects.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    Make it look like a character sprite from an 80s video game with sharp pixels, no anti-aliasing.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    The final result should be clean, minimalist, and instantly recognizable as an 8-bit game character with a distinct black outline.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    The final image should be exactly 64x64 pixels in size.`&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    n: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// DALL-E 3では1枚ずつ生成&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    size: &lt;/span&gt;&lt;span&gt;&quot;1024x1024&quot;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;// DALL·E 3で利用可能なサイズ&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    quality: &lt;/span&gt;&lt;span&gt;&quot;standard&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    response_format: &lt;/span&gt;&lt;span&gt;&quot;b64_json&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ネガティブっぽいプロンプトは書いても聞いてくれないどころか、書くとほぼ含めてくるようになるのでやめた。 &lt;code&gt;backgroundColor&lt;/code&gt; はだいたい無視される。これを 5 リクエスト同時に投げて 5 枚づつ作るようにしている。&lt;/p&gt;
&lt;p&gt;ちなみに適当に検証したタイミングで画像生成はやめて、開発中は適当に作ったものに固定すればよかったが、頭が回らずに無駄に $10 くらいクレジット溶かした(戒め1)。
あとこんな感じに、ちょっとうまくいったな！と思ったタイミングで絶対に git commit した方がいい(戒め2)。&lt;/p&gt;
&lt;h2&gt;ドット絵にする、地獄の始まり&lt;/h2&gt;
&lt;p&gt;この手の画像の生成をやったことがある人は知っていると思うが、生成 AI が作るドット絵はドット絵ではない。わからないと何を言っているんだという感じだが、この記事のサムネの元になった画像を見てみるとわかりやすいと思う:&lt;/p&gt;

&lt;p&gt;パット見拡大されたドット絵に見えるが、斜めに切れているドットがあったり、1ドットの中に複数の色が存在したりしている。目とか耳を見るとドット絵としてはまぁない状態になっていることがわかりやすいはず。
これを補正して、うまくドット絵にするのが目標。ネタバレするとすべて機械化することは諦めて、最終的には人間が細かい修正をする形に落ち着いた。&lt;/p&gt;
&lt;p&gt;さて、これをどう修正していくかというと、まずは範囲選択 → 選択した範囲を 128x128 px のドット絵と見立てる → それぞれのドットを一番多い色で塗りつぶす、というのを考えた。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;自動生成したアイキャッチ用画像に簡単な加工が出来るようにします。これは、ブログ全体での画像のテイストをなるべく揃えるために必要な作業です。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;次のような加工をしたいと考えています:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 背景色を透過処理&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 生成された画像から、アイコン部分だけをユーザーが範囲選択して切り抜き&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 切り抜いた画像をニアレストネイバー法を用いて 128x128 に縮小&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 4倍である 512x512 に拡大、1ドット辺り 4x4 を利用する状態にする&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- この画像を 1200x630 のキャンバスの真ん中に配置する&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- キャンバスの背景を指定された 1 色で塗りつぶす&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;これを次のようなフローで実現したいと考えています:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- `/create_eyecatch` で候補となる画像を複数枚生成&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- クライアントでユーザーが範囲指定して切り抜きを行う&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 縮小、拡大、補正を行ったキャラクターをユーザーが確認できる形で出力&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- ユーザーが決定したら、1200x630のキャンバスに配置し、アイキャッチとして出力する&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;各ステップでキャンセルして先頭に戻せるようにしてください&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;結論からいうとこれは全くうまくいかなかった。望む状態になるよう何度か修正を加えたが、範囲選択ができ、切り抜きを行って 128x128 に縮小する… というところまではいくが、その画像が綺麗ではない。
そもそも生成された画像のドットのサイズがまちまちなので、固定値で出来るようなものでもなく、という感じ。
一方で全体の UI とフローというか、要素の配置があまりにも適当すぎる(これは指示を一切していないのも悪いが)ことを除けば、操作自体はそれっぽくできる、すごい。
ここで作ったドット絵化のロジックはすべて消えているが、 UI の枠組みはここで作ったものを最後まで使うことになった。&lt;/p&gt;
&lt;p&gt;次にグリッドに区切ってそれぞれのドットを一番多い色で塗りつぶす、というのを考えた。イメージとしてはこんなの:&lt;/p&gt;

&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;index.html へ画像の加工機能を実装します、これは `/create_eyecatch` で生成された曖昧なドット絵(Pixel Art)をきれいなドット絵に加工し、最終的に 1200 x 630 のサイズにします。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## 前提&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- `/create_eyecatch` で生成される画像は 1024x1024 px です&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- そこに、拡大された 64x64 px 程度のドット絵が書かれています&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- ただし、ドット絵としては歪みもある画像になっています&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;     - 例えば、拡大された一つの Dot を複数の色が構成しています&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;     - ドット絵には存在しないはずの、円形の Dot が存在します&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## 実装する機能のフロー&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;ドット絵として不正な Dot を補正して、ドット絵を完成させます。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;1. まず返却された複数の画像から一つをユーザーが選択します&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;2. 選択された画像にグリッドを表示します、このグリッドは 8 x 8 pxを 1 dot として扱います、言い換えると全体を 128 x 128 px のキャンバスだと見立てるということです&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;3. グリッドの大きさはスライダーでユーザーが調整できるようにします&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;4. ユーザーがグリッドのサイズを決定したら、画像を変更する処理に入ります&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;5. 処理した画像を出力します、1200 x 630 のキャンバスにはまだ入れる必要はありません&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## 画像処理の詳細&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;ユーザーが決めたグリッドにそって、Dot の塗りつぶしを行います&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- グリッドで区切られた Dot の中で、最も多く使われている色を計算します&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- その色で、その Dot をすべて塗りつぶします&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 使った色を記録しておきます&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- すべての Dot を塗りつぶしたあと、使った色の数が 16 種類以上ある場合、色を少なくする処理を行います&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      - 近い色をまとめ、16 種類にします&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      - まとめられた色を、まとめた色で置き換えます&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      - 縁の装飾のため真っ黒(#000) は必ず残します&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      - 最終的な色の分布が、同じ系統の色に偏らないようにしてください&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;この処理を行うことで、例えばグリッドのサイズが 8x8 px であれば、1024x1024 の画像が 128x128px のドット絵となることを期待します。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;色の絞り込みのルールを変更します。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 基本的には、使われている回数が多い順に採用し、16 色に収めます&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 人間が識別できない濃淡の差がある色がある場合は、それをまとめます&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- この中に、四隅のそれぞれ 4 Dot の色は含まないでください&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 四隅の 4Dot と極めて近い色を持つ Dot はすべて同じ色にします&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&apos;related-articles-worker/public/index.html&apos; (see below for file content) に実装されている画像編集機能をさらにパワーアップします。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;既存のドットの補正、色の補正の処理が終わったあと、ユーザーがさらに修正を行い、最終的に 1200 x 630 の画像として出力します。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;以下の機能を追加してください:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 補正が終わった画像に対して、使われている色を利用して 1 Dot 単位の変更が出来る機能の追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 補正が終わった画像に対して、使われている色を利用して連なっている同色をすべて塗りつぶす機能の追加&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- その後、画像の4隅の色と同じ色で塗りつぶした 1200x630 のキャンバスを用意し、任意の位置に加工済みの画像を配置できるようにする&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 背景色を変更できるようにする&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## ユーザーによる画像の補正&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 利用している 16 色のパレットから、任意の色を選んで、&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;     - クリックした Dot をその色に置き換える事ができる&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;     - 連なっている同色を塗りつぶすことが出来る&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          - 塗りつぶしツールを実装する&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;1 回では不足していたので、細かい修正依頼とかはしつつも大まかに 3 つのプロンプトで機能全体ができた。
これはこの時点でかなりいいものが出てきていて、なるべく脳をプロンプトに dump したかいがあったなと感じた。&lt;/p&gt;


&lt;p&gt;これにグリッドのオフセットを付け加えたり、 Undo が出来るようにしたり、というのを対話で雑に加えていった。 Undo を作っている途中で一度コンテキストが足りなくなったので、新しいプロンプトで次の指示をしたらわりと察してやってくれた:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;@/related-articles-worker/public/index.html にはドット絵を編集する機能がありますが、 undo 機能を実装する途中で止まってしまっています。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;undo 機能を完成させてください。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 10 回まで undo が出来る&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;-  ctrl + z をショートカットに設定する&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;このあたりから結構バグ修正というか、動作して気に入らなかったり、動いてなかったりというのを修正することが増えた。
あと、この機能の実装を全部 index.html 1 ファイルに詰め込んでいたので、 1 回で使うコンテキストの量があからさまに多くなってきた。&lt;/p&gt;
&lt;p&gt;バグ修正の基本的なやりかたとしては、 UI が関わるものは気に入らない部分を SS して、文句と一緒に投げていた。これで基本的には解決してくれる。&lt;/p&gt;


&lt;p&gt;唯一「グリッドと画像がずれる」という問題をどうしても Roo Code では解決できなくて、Cursor と解決した。&lt;/p&gt;

&lt;p&gt;この辺はいつもどおり Dev Tool からおかしい CSS やら調べたりということしていて、だいたいわかったところで原因がありそうな行を Cursor に投げて直してもらった。
Cursor で直したものは、動作の不具合としてはこれともう一つ。そっちは多分 Cursor でも出来たが、だいたい検討がついていたので Cursor に投げたほうが早いよなとなった。&lt;/p&gt;
&lt;p&gt;実装をさらに進めていって、最終的な出力を調整出来る段階まで来たので、細かい位置やドットの大きさを指定できるようにしたい:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;@/related-articles-worker/public/index.html のドット絵編集機能を改善します。最終的にアイキャッチとして 1200 x 630 px のサイズで画像を出力します。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;以下の機能を実装します:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 画像の倍率を調整する機能&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 画像の位置を中心からのオフセットで調整する機能&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 選択した色をすべて背景色として扱う機能&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## 倍率を調整する機能&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;この編集機能はグリッドの大きさを設定することができます。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;例えば 16px のグリッドを設定した場合、ドット絵として見た場合は 16x16 px が 1 ドットとして扱われるため、16倍に拡大された画像ということになります。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;この倍率を最終的な出力では自由に調整できるようにして、画像の大きさを調整します。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;次のように実現します:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;- 1x 2x 4x 8x 16x のような刻みで倍率を選択出来るようにする&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;     - この倍率は、元画像からの倍率ではなく、ドット絵を 1 ドット = 1x1px に正規化した場合の倍率です&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;     - 例えば、16px のグリッドで作成された場合も 8x を選んだのであれば 1 ドットが 8x8px のグリッドで構成されるドット絵になります&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;## 選択した色をすべて背景色として扱う&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;最終的な出力の背景を単色としたいので、同一の色で塗りつぶす必要があります&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;加工中の画像の背景色はランダムなため、パレットから背景色として扱う色を複数個選べる必要があります&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;選んだ色と、1200x630 を確保して生まれた余剰部分の色をすべて同じにして、単色での背景を実現します&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これだとドット絵をそのまま引き伸ばして悲しい感じの画像になっていたので、間違っているよといったら理解してくれた:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;@/related-articles-worker/public/index.html でのドット絵編集の最終段階である 1200 x 630 でのアイキャッチ出力部分を改善します。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;倍率を変更する際に、元々のグリッドと倍率が異なると色の境界がぼやけるようになっています。これは間違った実装です。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;画像の引き伸ばしではなく、ドットのサイズのみを変更します。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;アップロードと配信部分を作る&lt;/h3&gt;
&lt;p&gt;生成するだけで配信をしていないので、作った画像を記事に関連付けて取れるようにする機能を足す。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;@/related-articles-worker/src/index.ts にアイキャッチ画像を配信する機能を追加します。アイキャッチ画像は @/related-articles-worker/public/index.html で作成できるようになっています。&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;次のフローを実装してください:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;1. `/registier` で登録されたとき、これまでの処理に追加して、id と本文の内容をセットで KV へ保存します&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;2. このとき、まだアイキャッチは作られていません&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;3. index.html で画像生成を開いたとき、まだアイキャッチのない記事の一覧を表示します&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;4. 記事を選ぶと、対象の id を `/create_eyecatch` に送り、既存のフローでアイキャッチを作成します&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;5. 作成したアイキャッチをアップロードするエンドポイントを新たに実装し、そこへアイキャッチを送信します&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      - また、id を指定して画像をアップロードする方法も同時に用意してください&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;6. `/eyecatch?id=${id}` で、登録したアイキャッチが画像として返ってくるようにします&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;データストアが必要であれば、 Workers の KV を利用してください。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;特に間違いもなく一瞬で終わる、 index.html がでかすぎて一度途切れたが、「以下のプロンプトではじめた実装の続きをしてください」 という指示で同じプロンプトを貼り付けたらうまいことしてくれた。
直接アップロードする機能とかも適当に開発させて終わり。書くことが特にない、こういうのが得意なのはそれはそうという感じ。&lt;/p&gt;
&lt;h2&gt;雑感&lt;/h2&gt;
&lt;h3&gt;まあ安くはない&lt;/h3&gt;
&lt;p&gt;差額で見た感じ、今回の機能を作るのに $25 くらいかかっていた。ここにすべてのプロンプトを載せているわけではない。
土日の可処分時間でやったもので、全部自分で書いたらこうはならなかったなと思うと時間を買えた感覚はあるのだが、今後の趣味の開発がこのペースで進むとして単純に 4 週で月 $100 + α。
ここに Cursor Pro と Claude Pro 、API と叩いているツールもろもろの利用料が乗ってなんやかんやで AI に掛かる費用が月 $200 くらいになるのかと思うとなかなか。&lt;/p&gt;
&lt;p&gt;ただ、今回ちゃんとやるために Tier 4 に引き上げたが、 Tier 4 未満の Roo Code は考えられないのでやるならまず Tier 4 にした方がいい。低い Tier で使ったこともあり、そのときは微妙という感想だった。&lt;/p&gt;
&lt;h3&gt;自分でも曖昧な部分の開発が重い&lt;/h3&gt;
&lt;p&gt;これはわかっていた話というか、別に普段の開発でも変わらないところ。今回でいうとドット絵の編集。
完全に人間がやる場合と比べて、とりあえずプロンプトを書けば何かは出てくるので、それを触ってみて「やっぱだめだよな」が気軽にできるのはよかったと思う。
ので、別に AI も得意ではないけど、人間がやる理由にはならないとは思った。困ったらそれこそ Chat で相談とかでもよい。
ただ、これをやるのに、後述のセーブポイントはまじでたくさん作ったほうがいいと思った。&lt;/p&gt;
&lt;h3&gt;コンテキストでかすぎ問題&lt;/h3&gt;
&lt;p&gt;これは index.html にすべての実装を乗せていったのが完全に悪いのだけど、最後の方は 2500 行程度になって 3 回往復したらコンテキストが足りなくなる、みたいになっていた。
後で実際のやりとりを除いてみると、ほぼ毎回 index.html をめちゃくちゃ送っている、でかい。ファイル指定しているからそりゃそう。&lt;/p&gt;
&lt;p&gt;今回これを回避するのに、前のプロンプト貼り付けて「多分続きまでやってるからなんとかして」みたいな指示をしていた。
この進め方自体の是非はともかく、 Roo Code は結構察して進めてくれるので、素直にすごいなと思った。&lt;/p&gt;
&lt;p&gt;コンテキストが足りるようにコードを適切に分割しろという話ではあって、コードのファイルを分ける基準が今後はわりとここに関係してきそうだなと思った。
今回 rules なんかは全く使っていなくてこれなので、改善できるんだろうなぁとは思いつつも、これ以上にコンテキストに何かを入れることが出来るのか…? という気持ちが強い。&lt;/p&gt;
&lt;h3&gt;セーブポイントをたくさん作れ&lt;/h3&gt;
&lt;p&gt;git commit をたくさんしろ、というのとほぼイコール。
少しでも期待した動作になったり、進捗が出たと思ったらすぐに commit する癖をつけるのがいいと思った。
ここまでをなしにして実装をやり直したい、と思うことが結構あって、その時点で commit がなくて Roo Code の戻すのをお願いしたりしていた。虚無。&lt;/p&gt;
&lt;h3&gt;プロンプトは適当でもわりとなんとかなる&lt;/h3&gt;
&lt;p&gt;ここの機能を実装したいときの指示書、みたいなものは結構適当でもいいなという感覚がある。最悪対話で修正すればよくて、これは Cursor でもそう感じていた。
ある程度フォーマット化されているのがいいとは思いつつも、曖昧なことやノイズが入っていなければなんとかなる、という印象。
それよりもコードベース全体を理解させることの方がはるかに重要だろうな、という感じ、そりゃそう。今回はほぼすべてが index.html だったので、逆にそこは解決されていた感があった。&lt;/p&gt;
&lt;h3&gt;デザイン適当すぎ問題&lt;/h3&gt;
&lt;p&gt;これは何も指示していないのがだいたい悪いとは思うんだけど、誰が使えるんだその UI みたいなのが平気で出てくる、基本縦に長過ぎる。&lt;/p&gt;</content:encoded></item><item><title>pnpm の Catalogs を使って monorepo 内のバージョンを統一する</title><link>https://yaakai.to/note/45/</link><guid isPermaLink="true">https://yaakai.to/note/45/</guid><pubDate>Fri, 28 Feb 2025 12:28:22 GMT</pubDate><content:encoded>&lt;p&gt;monorepo を運用していると、パッケージごとに参照しているライブラリのバージョンが異なっていて、結果として動作不良やビルドエラーにつながるケースがあります。こういった状態を回避する方法として pnpm v 10 で導入された Catalogs 機能が使えます。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://pnpm.io/ja/catalogs&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Catalogs&lt;/h3&gt;&lt;span&gt;pnpm.io&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;最近 dependabot も対応しました。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.blog/changelog/2025-02-04-dependabot-now-supports-pnpm-workspace-catalogs-ga/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Dependabot now supports pnpm workspace catalogs (GA) - GitHub Changelog&lt;/h3&gt;&lt;p&gt;Starting today, Dependabot offers full support for pnpm workspace catalogs. pnpm workspace catalogs are widely used in monorepos, and improper dependency handling can lead to: Broken dependency trees due to&amp;amp;hellip;&lt;/p&gt;&lt;span&gt;github.blog&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Catalogs では、monorepo の定義ファイルである &lt;code&gt;pnpm-workspace.yaml&lt;/code&gt; に利用したいバージョンを記載しておくことで、各パッケージからは &lt;code&gt;catalog:&lt;/code&gt; でそれを参照することができます。例えば React のバージョンを 19.0.0 に統一したい場合は次のようにします。&lt;/p&gt;
&lt;p&gt;pnpm-workspace.yaml:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;catalog&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    react&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;19.0.0&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;packages/foo/package.json:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;dependencies&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;react&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;catalog:&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;packages/bar/package.json:&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;dependencies&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;react&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;catalog:&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;こうすると &lt;code&gt;packages/foo&lt;/code&gt; でも &lt;code&gt;packages/bar&lt;/code&gt; でも、同じ React 19.0.0 が利用できるようになります。&lt;/p&gt;</content:encoded></item><item><title>CSS の attr が content と &lt;string&gt; 以外でも利用可能に</title><link>https://yaakai.to/note/43/</link><guid isPermaLink="true">https://yaakai.to/note/43/</guid><pubDate>Sun, 23 Feb 2025 20:04:49 GMT</pubDate><content:encoded>&lt;p&gt;Chrome 133 以降で、これまで次のように &lt;code&gt;content: attr(data-text);&lt;/code&gt; のような形でしか利用できなかった &lt;code&gt;attr()&lt;/code&gt; が、それ以外のものにも適用出来るようになりました。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;code&quot;&lt;/span&gt;&lt;span&gt; data-language&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;ts&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  .code::before&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    content&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;attr&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data-language&lt;/span&gt;&lt;span&gt;); // これまでは実質この用途専用だった&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;例えば汎用的なコンポーネントを作るときに、外から width や height 、 color を渡したいケースがあります。これをアップデートされた &lt;code&gt;attr()&lt;/code&gt; でこのように書くことができます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;box&quot;&lt;/span&gt;&lt;span&gt; data-width&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;400&quot;&lt;/span&gt;&lt;span&gt; data-height&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;400&quot;&lt;/span&gt;&lt;span&gt; data-color&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;#ff00ff&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;.box&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;attr&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data-width&lt;/span&gt;&lt;span&gt; px&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;200&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  height&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;attr&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data-height&lt;/span&gt;&lt;span&gt; px&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;200&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  background&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;attr&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;data-color&lt;/span&gt;&lt;span&gt; type(&amp;lt;color&amp;gt;&lt;/span&gt;&lt;span&gt;), &lt;/span&gt;&lt;span&gt;red&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;attr()&lt;/code&gt; の引数は 2 つで、前者は attrbiute 名と型を指定できます、 &lt;code&gt;px&lt;/code&gt; を指定すると attribute は数値として扱われ、 &lt;code&gt;type(&amp;lt;color&amp;gt;)&lt;/code&gt; ではそれを色として扱うことを伝えています。 2 つ目の引数は fallback で、 attribute が存在しなかった場合にその値が選択されます。&lt;/p&gt;
&lt;h2&gt;Refs&lt;/h2&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://developer.chrome.com/blog/advanced-attr?hl=ja&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;CSS attr() のアップグレード &amp;amp;nbsp;|&amp;amp;nbsp; Blog &amp;amp;nbsp;|&amp;amp;nbsp; Chrome for Developers&lt;/h3&gt;&lt;p&gt;カスタム プロパティを含む任意の CSS プロパティで attr() を使用できるようになりました。文字列以外のデータ型に値を解析できます。&lt;/p&gt;&lt;span&gt;developer.chrome.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://www.bram.us/2025/01/20/css-attr-gets-an-upgrade/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;CSS attr() gets an upgrade&lt;/h3&gt;&lt;p&gt;You can now use `attr()` with any CSS property — not just content – and it can parse values into data types other than &amp;amp;lt;string&amp;amp;gt;.&lt;/p&gt;&lt;span&gt;www.bram.us&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://developer.mozilla.org/ja/docs/Web/CSS/attr&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;attr() - CSS | MDN&lt;/h3&gt;&lt;p&gt;attr() は CSS の関数で、選択した要素の属性の値を取得し、それをプロパティ値で使用するために使用されます。これは、 var() 関数が独自のプロパティ値を置き換える方法と似ています。擬似要素で使用することもでき、その場合は擬似要素を作る元になった要素の属性値が返されます。&lt;/p&gt;&lt;span&gt;developer.mozilla.org&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</content:encoded></item><item><title>Cloudflare Vectorize と Workers AI で関連する記事を作ってみる</title><link>https://yaakai.to/blog/2025/cf-vectorize-and-workes-ai/</link><guid isPermaLink="true">https://yaakai.to/blog/2025/cf-vectorize-and-workes-ai/</guid><pubDate>Sun, 23 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;このブログは SSG したものを GitHub Pages にデプロイしているのですが、 Cloudflare Vectorize と Workers AI を使うと関連記事を表示することができそうだったので作ってみました。&lt;/p&gt;
&lt;p&gt;このブログのリポジトリにある Worker として実装しています：&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/yaakaito/yaakaito/tree/main/related-articles-worker&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;yaakaito/related-articles-worker at main · yaakaito/yaakaito&lt;/h3&gt;&lt;p&gt;Contribute to yaakaito/yaakaito development by creating an account on GitHub.&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;チュートリアルと被る部分も多々あるので、そちらも参考にしてください。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://developers.cloudflare.com/vectorize/get-started/embeddings/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Vectorize and Workers AI&lt;/h3&gt;&lt;p&gt;Vectorize allows you to generate vector embeddings using a machine-learning model, including the models available in Workers AI.&lt;/p&gt;&lt;span&gt;developers.cloudflare.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;Workers AI による埋め込みベクトルの生成&lt;/h2&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://developers.cloudflare.com/workers-ai/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Cloudflare Workers AI&lt;/h3&gt;&lt;span&gt;developers.cloudflare.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Workers AI は Cloudflare Workers から各種 AI モデルを呼び出すことができるサービスです。モデルは結構色々あって、Text Embedding に使える &lt;code&gt;@cf/baai/bge-base-en-v1.5&lt;/code&gt; というモデルがあり、今回はこれを使って記事の内容をベクトル化しています。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://developers.cloudflare.com/workers-ai/models/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Models&lt;/h3&gt;&lt;span&gt;developers.cloudflare.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;wrangler.jsonc&lt;/code&gt; に設定を行い、次のようなコードでベクトルを生成できます：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;ai&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;binding&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;AI&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; response&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; env.&lt;/span&gt;&lt;span&gt;AI&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;run&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;@cf/baai/bge-base-en-v1.5&quot;&lt;/span&gt;&lt;span&gt;, {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    text: article.content,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; embedding&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; response.data[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;]; &lt;/span&gt;&lt;span&gt;// number[]&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;@cf/baai/bge-base-en-v1.5&lt;/code&gt; は英語用のモデルで、現時点では英語のモデルしか利用できませんが、今回は Cloudflare Workers で完結させたかったため、精度には目をつぶることにしました。記事が少ないのでなんとも言えないですが、それっぽい結果にはなってる気がします。ここは好きなモデルを使えるので、 Workers AI ではなく、コードを書いて Open AI のモデルを使うとかでもよいです。&lt;/p&gt;
&lt;h2&gt;Vectorize でのベクトルデータの管理&lt;/h2&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://developers.cloudflare.com/vectorize/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Cloudflare Vectorize&lt;/h3&gt;&lt;p&gt;Vectorize is a globally distributed vector database that enables you to build full-stack, AI-powered applications with Cloudflare Workers. Vectorize makes querying embeddings — representations of values or objects like text, images, audio that are designed to be consumed by machine learning models and semantic search algorithms — faster, easier and more affordable.&lt;/p&gt;&lt;span&gt;developers.cloudflare.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Cloudflare Workers から触れるベクトルデータベースで、手軽にベクトルの保存や検索を行うことができます。&lt;/p&gt;
&lt;p&gt;使用するために準備として wrangler からデータベースを作成します：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;bun&lt;/span&gt;&lt;span&gt; wrangler&lt;/span&gt;&lt;span&gt; vectorize&lt;/span&gt;&lt;span&gt; create&lt;/span&gt;&lt;span&gt; related-articles-index&lt;/span&gt;&lt;span&gt; --preset=&lt;/span&gt;&lt;span&gt;&quot;@cf/baai/bge-base-en-v1.5&quot;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Workers AI を使用する場合モデルに合わせてプリセットを指定することで、互換性のある設定が自動的に行われます。そうでない場合は、 &lt;code&gt;--dimensions&lt;/code&gt; と &lt;code&gt;--metric&lt;/code&gt; を指定して作成します。&lt;/p&gt;
&lt;p&gt;出力された設定情報を &lt;code&gt;wrangler.jsonc&lt;/code&gt; に追加します：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;vectorize&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &quot;binding&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;VECTORIZE&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &quot;index_name&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;related-articles-index&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  ],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;データの挿入&lt;/h3&gt;
&lt;p&gt;検索対象にしたい記事をベクトル化してデータベースに登録します。 Worker AI で取得した &lt;code&gt;embedding&lt;/code&gt; を、以下のように &lt;code&gt;upsert&lt;/code&gt; で保存します：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;await&lt;/span&gt;&lt;span&gt; env.&lt;/span&gt;&lt;span&gt;VECTORIZE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;upsert&lt;/span&gt;&lt;span&gt;([{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    id: article.id, &lt;/span&gt;&lt;span&gt;// どこからでも作れるように、記事の URL に対応した形式にしている&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    values: embedding,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    metadata: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        url: article.url,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        title: article.title,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        emoji: article.emoji,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        content: article.content&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}]);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これを適当な Web API としてまとめ、 GitHub Actions で SSG するときに全記事分リクエストを投げています。現状は記事数が少ないため、毎回全データを上書きする形で実装しています。&lt;/p&gt;
&lt;h2&gt;関連記事の取得&lt;/h2&gt;
&lt;p&gt;関連記事を取得する API は、次のような URL で記事の ID を受け取って類似度の高い記事を返します。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;https://articles.yaakai.to/related_articles?id=note-1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;この API では、 ID に登録されているベクトルを取り出して、そのベクトルを利用して Vectorize の &lt;code&gt;query&lt;/code&gt; メソッドで類似度の高い記事を検索しています：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; sourceVectors&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; env.&lt;/span&gt;&lt;span&gt;VECTORIZE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getByIds&lt;/span&gt;&lt;span&gt;([id]);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; matches&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; env.&lt;/span&gt;&lt;span&gt;VECTORIZE&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;query&lt;/span&gt;&lt;span&gt;(sourceVectors[&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;].values, {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    topK: &lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    returnValues: &lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    returnMetadata: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;});&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;完全一致の記事（自分自身）を除外し、上位3件を関連記事として返しています：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; relatedArticles&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; matches.matches&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    .&lt;/span&gt;&lt;span&gt;filter&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; match.id &lt;/span&gt;&lt;span&gt;!==&lt;/span&gt;&lt;span&gt; id)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    .&lt;/span&gt;&lt;span&gt;slice&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;3&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    .&lt;/span&gt;&lt;span&gt;map&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;match&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; ({&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        id: match.id,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        url: match.metadata?.url &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; string&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        title: match.metadata?.title &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; string&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        emoji: match.metadata?.emoji &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; string&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        content: match.metadata?.content &lt;/span&gt;&lt;span&gt;as&lt;/span&gt;&lt;span&gt; string&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        similarity: match.score&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }));&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;このブログには、クライアントサイドからこの API にリクエストして、取得したデータを整形して表示しています。&lt;/p&gt;
&lt;h2&gt;ローカル開発環境&lt;/h2&gt;
&lt;p&gt;注意点としてローカルでの開発時は、Workers AI と Vectorize を使用するために &lt;code&gt;--remote&lt;/code&gt; オプションを付けて起動する必要があります：&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; wrangler&lt;/span&gt;&lt;span&gt; dev&lt;/span&gt;&lt;span&gt; --remote&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content:encoded></item><item><title>Dev Container での wrangler dev で dev-server に繋がらない場合の対処</title><link>https://yaakai.to/note/28/</link><guid isPermaLink="true">https://yaakai.to/note/28/</guid><pubDate>Sun, 02 Feb 2025 14:40:03 GMT</pubDate><content:encoded>&lt;p&gt;&lt;code&gt;wrangler dev&lt;/code&gt; コマンドは Cloudflare での動作を想定した dev-server を実行できるコマンドですが、Dev Container 内でこれを実行した場合に発行される URL    にアクセスしても、レスポンスが一切返ってこない状態になりました。コンテナの実行には OrbStack を利用しています。解決法を探してみたところ次のような Issue がありました。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/cloudflare/workers-sdk/issues/6587&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;🐛 BUG: I can&amp;amp;#39;t access the development server I launched with wrangler dev in DevContainer · Issue #6587 · cloudflare/workers-sdk&lt;/h3&gt;&lt;p&gt;Which Cloudflare product(s) does this pertain to? Wrangler What version(s) of the tool(s) are you using? 3.72.3 What version of Node are you using? v22.7.0 What operating system and version are you...&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;どうやら &lt;code&gt;--ip 127.0.0.1&lt;/code&gt; を加えて起動するとうまくいくことがあるようです。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; npx&lt;/span&gt;&lt;span&gt; wrangler&lt;/span&gt;&lt;span&gt; dev&lt;/span&gt;&lt;span&gt; --ip&lt;/span&gt;&lt;span&gt; 127.0.0.1&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;今回自分はこれで動作しました。ただし、Dev Container でないローカル環境などではこれでは実行できなくなるので、注意が必要です。&lt;/p&gt;</content:encoded></item><item><title>Cloudflare Workers の Static Assets と Frameworks を試す</title><link>https://yaakai.to/blog/2025/cf-workers-static-assets-and-frameworks/</link><guid isPermaLink="true">https://yaakai.to/blog/2025/cf-workers-static-assets-and-frameworks/</guid><pubDate>Sun, 02 Feb 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;これを書いている時点ではβ版となっていますが、 Cloudflare Workers に Static Assets と Frameworks という機能が追加されていて、これまで Pages 側の機能だった静的ファイルの配信が Workers に統合されつつあるようです。これを利用すると Workers 単体で SSG なコンテンツを配信したり、 Next.js や Astro のサーバーサイド機能をうまく使うことが出来るようなので、これはそのサンプルを触ってみたログです。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://developers.cloudflare.com/workers/static-assets/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Static Assets&lt;/h3&gt;&lt;p&gt;Create full-stack applications deployed to Cloudflare Workers.&lt;/p&gt;&lt;span&gt;developers.cloudflare.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://developers.cloudflare.com/workers/frameworks/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Framework guides&lt;/h3&gt;&lt;p&gt;Create full-stack applications deployed to Cloudflare Workers.&lt;/p&gt;&lt;span&gt;developers.cloudflare.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;チュートリアルをなぞって少し手を加えた程度のものですが、コードは以下にあります。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/yaakaito/workers-static-assets-example&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;GitHub - yaakaito/workers-static-assets-example&lt;/h3&gt;&lt;p&gt;Contribute to yaakaito/workers-static-assets-example development by creating an account on GitHub.&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;基本的な機能&lt;/h2&gt;
&lt;p&gt;特にフレームワークを利用しないプレーンなアプリは &lt;a href=&quot;https://developers.cloudflare.com/workers/static-assets/get-started/#deploy-a-full-stack-application&quot;&gt;Deploy a full-stack application&lt;/a&gt; に従って以下のコマンドでこれを試すことができます。デフォルトが Vite なので、個人的にはもっとシンプルな構成であると嬉しかったです。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; npm&lt;/span&gt;&lt;span&gt; create&lt;/span&gt;&lt;span&gt; cloudflare@latest&lt;/span&gt;&lt;span&gt; --&lt;/span&gt;&lt;span&gt; my-dynamic-site&lt;/span&gt;&lt;span&gt; --experimental&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;生成されたものをみると、Wrangler の設定ファイルである &lt;code&gt;wrangler.json&lt;/code&gt; もしくは &lt;code&gt;wrangler.toml&lt;/code&gt; に &lt;code&gt;assets&lt;/code&gt; フィールドが追加されていて、これで設定を行うようです。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://developers.cloudflare.com/workers/static-assets/binding/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Configuration and Bindings&lt;/h3&gt;&lt;p&gt;Details on how to configure Workers static assets and its binding.&lt;/p&gt;&lt;span&gt;developers.cloudflare.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;JSON の場合は次のようにディレクトリを指定すると、そのディレクトリ以下のファイルが &lt;code&gt;wrangler deploy&lt;/code&gt; でアップロードされます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;main&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;src/index.ts&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;assets&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;directory&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;./public/&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ここに該当するファイルがあれば Workers の &lt;code&gt;main&lt;/code&gt; よりも先にそれが返され、そうでなければ &lt;code&gt;main&lt;/code&gt; に指定したスクリプトに到達するようです。&lt;/p&gt;
&lt;h2&gt;Next.js + PPR&lt;/h2&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://developers.cloudflare.com/workers/frameworks/framework-guides/nextjs/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Next.js&lt;/h3&gt;&lt;p&gt;Create an Next.js application and deploy it to Cloudflare Workers with Workers Assets.&lt;/p&gt;&lt;span&gt;developers.cloudflare.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; npm&lt;/span&gt;&lt;span&gt; create&lt;/span&gt;&lt;span&gt; cloudflare@latest&lt;/span&gt;&lt;span&gt; my-next-app&lt;/span&gt;&lt;span&gt; --&lt;/span&gt;&lt;span&gt; --framework=next&lt;/span&gt;&lt;span&gt; --experimental&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;@opennextjs/cloudflare&lt;/code&gt; を利用して、 Workers で動作する形にビルドしているようです。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://opennext.js.org/cloudflare&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Index - OpenNext&lt;/h3&gt;&lt;p&gt;Open-source Next.js adapters&lt;/p&gt;&lt;span&gt;opennext.js.org&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://laiso.hatenablog.com/entry/2024/10/12/000528&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Cloudflare WorkersとNext.jsインテグレーションの問題にOpenNext実装が加わった - laiso&lt;/h3&gt;&lt;p&gt;Cloudflareが@opennextjs/cloudflareを発表 2024年9月末に開催された「Builder Day 2024」でNext.jsアプリをCloudflare Workersにデプロイする新たな方法が公開された。 この方法は、@opennextjs/cloudflareを使ってビルドする。既存の@cloudflare/next-on-pages方式を置き換える可能性があり、現在は実験的な段階だ。 以下に新旧の開発手順のドキュメントがある。 新(opennextjs): Next.js | Cloudflare Workers docs 旧(next-on-pages):…&lt;/p&gt;&lt;span&gt;laiso.hatenablog.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;概ねうまく動作しているように見えるのですが、Partial Prerendering が備え付けの &lt;code&gt;preview&lt;/code&gt; コマンドだとうまく動作しませんでした。&lt;code&gt;next dev&lt;/code&gt; や実際の Workers の環境では問題なく動作しているので、 Wrangler 特有の問題なのかもしれませんが、基本的な開発は &lt;code&gt;next dev&lt;/code&gt; だと思うのであまり気にする必要はなさそうです。&lt;/p&gt;
&lt;p&gt;他にも現状だとサポートされていない機能がいくつかあって、特に ISR が使えないのが気になります。そのうち対応される気もするので使いたい場合は待つことになります。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://opennext.js.org/cloudflare#supported-nextjs-features&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Index - OpenNext&lt;/h3&gt;&lt;p&gt;Open-source Next.js adapters&lt;/p&gt;&lt;span&gt;opennext.js.org&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;Astro + Server Islands&lt;/h2&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://developers.cloudflare.com/workers/frameworks/framework-guides/astro/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Astro&lt;/h3&gt;&lt;p&gt;Create an Astro application and deploy it to Cloudflare Workers with Workers Assets.&lt;/p&gt;&lt;span&gt;developers.cloudflare.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;npm&lt;/span&gt;&lt;span&gt; create&lt;/span&gt;&lt;span&gt; cloudflare@latest&lt;/span&gt;&lt;span&gt; my-astro-app&lt;/span&gt;&lt;span&gt; --&lt;/span&gt;&lt;span&gt; --framework=astro&lt;/span&gt;&lt;span&gt; --experimental&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;@astrojs/cloudflare&lt;/code&gt; を使って動作します。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://docs.astro.build/ja/guides/integrations-guide/cloudflare/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;@astrojs/cloudflare&lt;/h3&gt;&lt;p&gt;Learn how to use the @astrojs/cloudflare adapter to deploy your Astro project.&lt;/p&gt;&lt;span&gt;docs.astro.build&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Server Islands を試しましたが、問題なく動いているように思えました。個人的に Astro といえば SSG で、 GitHub Pages にデプロイすればいいかくらいの温度感で使っていたので、Workers で Server Islands が使えるのは出来ることが広がりそうだなという感触があります。この記事も Astro で書いたものを GitHub Pages においているのですが、そのうち Workers へ引っ越すかもしれません。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://docs.astro.build/en/guides/server-islands/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Server islands&lt;/h3&gt;&lt;p&gt;Combine high performance static HTML with dynamic server-rendered content.&lt;/p&gt;&lt;span&gt;docs.astro.build&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;直接今回の話とは関係ないのですが、Dev Container + OrbStack で &lt;code&gt;astro dev&lt;/code&gt; で起動したサーバーにブラウザからアクセスができないという問題がありました。コンテナ内からの &lt;code&gt;curl&lt;/code&gt; は通る状態です。Codespace や Dev Container でないローカル環境では動作するので、 OrbStack 特有の問題だと思いますが未解決です。Docker Desktop ですが次のような記事がありました。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://qiita.com/hamu3864kA/items/353f14d456f6aa672c3b&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;[mac]devcontainerで立ち上げたローカルサーバーに接続できない（built-in port-forwarding） - Qiita&lt;/h3&gt;&lt;p&gt;結論：Docker Desktopのせい。 事象 astro で静的サイトを作るため、VSCodeの devcontainer を利用しました。 npx astro dev で起動するとこんなのが出るので 「ブラウザーで開く」をクリックしても、ブラウザーはグルグルするも...&lt;/p&gt;&lt;span&gt;qiita.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;雑感&lt;/h2&gt;
&lt;p&gt;Workers と Pages が徐々に統合されるという流れがあったと思うのですが、 Static Assets でこの流れが一気に進んだように思えます。出来るとわかりつつも、 Cloudflare Workers 上に Web UI をもつアプリケーションを作るのは少し億劫なところがあったのですが、 Static Assets が入るとそれが解決するので、個人開発レベルなら Cloudflare 1 本でも困ることがなくなりそうです。&lt;/p&gt;</content:encoded></item><item><title>TypeScript 5.8 の --erasableSyntaxOnly</title><link>https://yaakai.to/note/26/</link><guid isPermaLink="true">https://yaakai.to/note/26/</guid><pubDate>Tue, 28 Jan 2025 09:21:41 GMT</pubDate><content:encoded>&lt;p&gt;Node がデフォルトでの TypeScript の実行をサポートしたことに伴って、それを更にサポートするフラグが TypeScript に追加されるようです。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-5-8-beta/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Announcing TypeScript 5.8 Beta - TypeScript&lt;/h3&gt;&lt;p&gt;Today we are excited to announce the availability of TypeScript 5.8 Beta. To get started using the beta, you can get it through npm with the following command: npm install -D typescript@beta Let&amp;amp;#8217;s take a look at what&amp;amp;#8217;s new in TypeScript 5.8! Checked Returns for Conditional and Indexed Access Types Consider an API that presents [&amp;amp;hellip;]&lt;/p&gt;&lt;span&gt;devblogs.microsoft.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/microsoft/TypeScript/issues/59601&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Tsconfig option to disallow features requiring transformations which are not supported by Node.js&amp;amp;#39; --strip-types · Issue #59601 · microsoft/TypeScript&lt;/h3&gt;&lt;p&gt;🔍 Search Terms --strip-types ✅ Viability Checklist This wouldn&amp;amp;#39;t be a breaking change in existing TypeScript/JavaScript code This wouldn&amp;amp;#39;t change the runtime behavior of existing JavaScript code Th...&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;これを利用すると、 &lt;code&gt;enum&lt;/code&gt; や &lt;code&gt;namespace&lt;/code&gt; といった TS 特有の機能を無効化し、削除可能な型情報のみを扱うことが出来るようになります。Node の TS サポートでは、これらの構文を実行するために別途 &lt;code&gt;--experimental-transform-types&lt;/code&gt; フラグが必要でしたが、今回 &lt;code&gt;--erasableSyntaxOnly&lt;/code&gt; が導入され、それを利用することで、Node と互換性の高いコードを書けることが期待されているようです。&lt;/p&gt;</content:encoded></item><item><title>Temporal が Firefox Nightly で一部利用可能に</title><link>https://yaakai.to/note/25/</link><guid isPermaLink="true">https://yaakai.to/note/25/</guid><pubDate>Mon, 27 Jan 2025 11:39:03 GMT</pubDate><content:encoded>&lt;p&gt;長らく使われてきた &lt;code&gt;Date&lt;/code&gt; に変わるものとして &lt;code&gt;Temporal&lt;/code&gt; が提案されていましたが、その実装が Firefox の Nigthly ビルドに含まれるようになったようです。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/blog/javascript-temporal-is-coming/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;JavaScript Temporal is coming&lt;/h3&gt;&lt;p&gt;A new way to handle dates and times is being added to JavaScript. Let&amp;amp;#39;s take a look at Temporal, what problems it solves, the current state, and what you&amp;amp;#39;ll find in the new documentation about it on MDN.&lt;/p&gt;&lt;span&gt;developer.mozilla.org&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;大きな特徴として、Wall-Clock Time (その地域時間) と Exact Time (UTC) が分離しました。&lt;code&gt;Plain&lt;/code&gt; がつくものが Wall-Clock Time で、 &lt;code&gt;Instant &lt;/code&gt; が Exact Time です。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span&gt; Temporal.Now.&lt;/span&gt;&lt;span&gt;instant&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;toString&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;← &lt;/span&gt;&lt;span&gt;&quot;2025-01-27T11:12:04.997Z&quot;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span&gt; Temporal.Now.&lt;/span&gt;&lt;span&gt;plainDateTimeISO&lt;/span&gt;&lt;span&gt;().&lt;/span&gt;&lt;span&gt;toString&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;← &lt;/span&gt;&lt;span&gt;&quot;2025-01-27T20:12:23.839&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;また、期間を表す概念として &lt;code&gt;Duration&lt;/code&gt; があります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span&gt; Temporal.Duration.&lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt;({ hours: &lt;/span&gt;&lt;span&gt;1&lt;/span&gt;&lt;span&gt;}).&lt;/span&gt;&lt;span&gt;toString&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;← &lt;/span&gt;&lt;span&gt;&quot;PT1H&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これまでライブラリで行っていたような操作も多くのメソッドが追加され、ネイティブでサポートされるようになります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span&gt; Temporal.PlainDate.&lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;2025-01-01&apos;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;since&lt;/span&gt;&lt;span&gt;(Temporal.Now.&lt;/span&gt;&lt;span&gt;plainDateISO&lt;/span&gt;&lt;span&gt;()).&lt;/span&gt;&lt;span&gt;toString&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;← &lt;/span&gt;&lt;span&gt;&quot;-P26D&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;gt;&amp;gt;&lt;/span&gt;&lt;span&gt; Temporal.PlainDateTime.&lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;2025-01-01&apos;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;add&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;P26DT12H&apos;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;toString&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;← &lt;/span&gt;&lt;span&gt;&quot;2025-01-27T12:00:00&quot;&lt;/span&gt;&lt;span&gt; &lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Refs&lt;/h2&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://zenn.dev/cybozu_frontend/articles/temporal-reduces-scope&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;zenn.dev&lt;/h3&gt;&lt;span&gt;zenn.dev&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</content:encoded></item><item><title>Popover API と Dialog に関する備忘録</title><link>https://yaakai.to/blog/2025/popover-dialog/</link><guid isPermaLink="true">https://yaakai.to/blog/2025/popover-dialog/</guid><pubDate>Thu, 23 Jan 2025 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;2024 年の Baseline に追加された機能として Popover API があり、またそれに関連するものとして Dialog がすでに実装されている。
この 2 つは目的は違うが技術的には似たものになっているので、それぞれの特徴などをまとめる。&lt;/p&gt;
&lt;p&gt;動作サンプル: &lt;a href=&quot;https://yaakaito.github.io/code-sandbox/popover-and-dialog/&quot;&gt;https://yaakaito.github.io/code-sandbox/popover-and-dialog/&lt;/a&gt;&lt;/p&gt;
&lt;h2&gt;前提知識&lt;/h2&gt;
&lt;h3&gt;Top Layer&lt;/h3&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://developer.mozilla.org/ja/docs/Glossary/Top_layer&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Top layer (最上位レイヤー) - MDN Web Docs 用語集 | MDN&lt;/h3&gt;&lt;p&gt;最上位レイヤーは、ビューポートの幅と高さ全体にわたり、ウェブ文書内に表示される他のすべてのレイヤーの最上位に位置する固有のレイヤーです。これは、ページ上の他のすべてのコンテンツの上に現れるべき要素を含むために、ブラウザーによって作成されます。&lt;/p&gt;&lt;span&gt;developer.mozilla.org&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;従来のz-indexとは異なる、ブラウザネイティブの最前面レイヤー。PopoverとDialogのモーダルモードは、このTop layerを利用して要素を描画する。入れ子も可能だが、Light Dismiss（要素外クリックでの閉じる）の挙動には注意が必要となる。&lt;/p&gt;
&lt;h2&gt;利用用途による違い&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Popover
&lt;ul&gt;
&lt;li&gt;カレンダーのようなフォームでの特殊な入力 UI&lt;/li&gt;
&lt;li&gt;通知、トースト&lt;/li&gt;
&lt;li&gt;チュートリアル UI&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;モーダル Dialog
&lt;ul&gt;
&lt;li&gt;利用規約への同意のようなそれ以外を操作できないような UI&lt;/li&gt;
&lt;li&gt;削除のような重要な確認操作&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;非モーダル Dialog
&lt;ul&gt;
&lt;li&gt;Cookie の利用許可のような、ユーザーに同意を求めるが、コンテンツは操作できる UI&lt;/li&gt;
&lt;li&gt;プライベート URL で共有を行うようなコンテンツでの、共有に関する警告の UI&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Popover&lt;/h2&gt;
&lt;div&gt;&lt;a href=&quot;https://developer.mozilla.org/ja/docs/Web/API/Popover_API&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;ポップオーバー API - Web API | MDN&lt;/h3&gt;&lt;p&gt;ポップオーバー API は、他のページコンテンツの上に表示するポップオーバーコンテンツを表示するための、標準的な、一貫性のある、柔軟な仕組みを開発者に提供します。ポップオーバーコンテンツは、HTML 属性を用いて宣言的に、または JavaScript を用いて制御することができます。&lt;/p&gt;&lt;span&gt;developer.mozilla.org&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Popover は属性で、任意の要素を Top layer へ配置することができる。ダイアログやモーダルではないので、アクセシビリティといった視点で意味は持たず、基本的には背景の要素に操作が貫通する。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; popovertarget&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;mypopover&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;toggle popover&amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;mypopover&quot;&lt;/span&gt;&lt;span&gt; popover&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;auto|manual|hint&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;Popover content&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;popover 属性には現状 3 つの値を設定することができ、これによって Light Dismiss や表示方法をコントロールする。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;&lt;code&gt;auto&lt;/code&gt;: デフォルト値で、Light Dismiss が有効になる&lt;/li&gt;
&lt;li&gt;&lt;code&gt;manual&lt;/code&gt;: Light Dismiss が無効になり、閉じるための動作を実装する必要がある&lt;/li&gt;
&lt;li&gt;&lt;code&gt;hint&lt;/code&gt;: 「hint」は Chrome 133 から導入された（される予定の）機能で、この値が設定されている Popover は同時に1つしか表示されない&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;JS からもコントロール可能だが、必ず属性に &lt;code&gt;popover&lt;/code&gt; が付与されている必要がある。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;popover.&lt;/span&gt;&lt;span&gt;showPopover&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;popover.&lt;/span&gt;&lt;span&gt;hidePopover&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Dialog&lt;/h2&gt;
&lt;div&gt;&lt;a href=&quot;https://developer.mozilla.org/ja/docs/Web/HTML/Element/dialog&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;&amp;amp;lt;dialog&amp;amp;gt;: ダイアログ要素 - HTML | MDN&lt;/h3&gt;&lt;p&gt;&amp;amp;lt;dialog&amp;amp;gt; は HTML の要素で、モーダルまたは非モーダルダイアログボックスや、それ以外の消すことができるアラート、インスペクター、サブウィンドウなどのような対話的コンポーネントを表します。&lt;/p&gt;&lt;span&gt;developer.mozilla.org&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;Dialog はユーザーに操作を求めるための役割（&lt;code&gt;role=dialog&lt;/code&gt;）を持つ。モーダル/非モーダルの2つのモードがあり、特にモーダルモードではTop layerへの表示と背景操作のロックを行う。JS での制御が想定されていて、&lt;code&gt;showModal()&lt;/code&gt; を利用することでモーダルダイアログが、&lt;code&gt;show()&lt;/code&gt; を利用することで非モーダルダイアログが表示される。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;dialog&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;dialog&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;Dialog&amp;lt;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;This is a dialog.&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;Close&amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;dialog&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; dialog&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; document.&lt;/span&gt;&lt;span&gt;getElementById&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;dialog&apos;&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    dialog.&lt;/span&gt;&lt;span&gt;showModal&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;モーダルの場合、次の機能がブラウザネイティブで提供される。これによって、これまでの実装よりもアクセシビリティや UX の向上が期待できる。&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;inert による背景 UI のロック
&lt;ul&gt;
&lt;li&gt;
&lt;div&gt;&lt;a href=&quot;https://developer.mozilla.org/ja/docs/Web/HTML/Global_attributes/inert&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;HTML inert グローバル属性 - HTML | MDN&lt;/h3&gt;&lt;p&gt;inert グローバル属性は論理属性で、要素とそのフラットツリーの子孫すべてが「不活性」になることを示す論理属性です。 showModal() に対応するモーダルダイアログ (&amp;amp;lt;dialog&amp;amp;gt;) は、不活性状態から脱します。つまり、先祖から不活性を受け継ぐことはなく、自身に inert 属性を明示的に設定した場合にのみ不活性になります。&lt;/p&gt;&lt;span&gt;developer.mozilla.org&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;&lt;code&gt;.showModal()&lt;/code&gt; で表示された Dialog は、Top Layer でない要素が inert となる&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;:backdrop による背景の表示
&lt;ul&gt;
&lt;li&gt;
&lt;div&gt;&lt;a href=&quot;https://developer.mozilla.org/ja/docs/Web/CSS/::backdrop&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;::backdrop - CSS | MDN&lt;/h3&gt;&lt;p&gt;::backdrop は CSS の擬似要素で、何らかの要素が全画面モード（最上位レイヤー）で表示される直下に直接表示されるビューポートの寸法のボックスです。&lt;/p&gt;&lt;span&gt;developer.mozilla.org&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;/li&gt;
&lt;li&gt;Top layer と背景コンテンツの間に表示される領域で、モーダルダイアログではデフォルトで半透明の背景が表示されており、ユーザーの操作を受け付けないことを表現している&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;ESC での閉じるのサポート
&lt;ul&gt;
&lt;li&gt;Light Dismiss ではない&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;li&gt;フォーカスの管理
&lt;ul&gt;
&lt;li&gt;モーダルダイアログが表示された際に、その中の要素にフォーカスが移動する&lt;/li&gt;
&lt;li&gt;ダイアログが閉じられた際に、直前のフォーカス位置に戻る&lt;/li&gt;
&lt;/ul&gt;
&lt;/li&gt;
&lt;/ul&gt;
&lt;h2&gt;Anchor Positioning&lt;/h2&gt;
&lt;div&gt;&lt;a href=&quot;https://developer.mozilla.org/ja/docs/Web/CSS/CSS_anchor_positioning/Using&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;CSS アンカー位置指定の使用 - CSS | MDN&lt;/h3&gt;&lt;p&gt;CSS アンカー位置指定モジュールは、要素を結びつけるための機能を定義します。要素は、アンカー要素とアンカー位置指定要素として定義できます。アンカー位置指定要素は、アンカー要素に束縛することができます。アンカー位置決めされた要素は、そのサイズと位置を、結合されたアンカー要素のサイズと位置に相対的に設定することができます。&lt;/p&gt;&lt;span&gt;developer.mozilla.org&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;特定要素を基準とした配置をサポート。Top layer要素でも有効で、Popoverと組み合わせて使用できる。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   #anchorPopoverTrigger&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        anchor-name&lt;/span&gt;&lt;span&gt;: --anchor-target;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   #anchorPopover&lt;/span&gt;&lt;span&gt;  {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        position-anchor&lt;/span&gt;&lt;span&gt;: --anchor-target;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        position-area&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;bottom&lt;/span&gt;&lt;span&gt; span-right;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        position-try-fallbacks&lt;/span&gt;&lt;span&gt;: flip-inline, flip-inline flip-block;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        margin&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;300&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;   }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;anchorPopoverTrigger&quot;&lt;/span&gt;&lt;span&gt; popovertarget&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;anchorPopoverLeft&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;toggle anchor popover&amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;anchorPopover&quot;&lt;/span&gt;&lt;span&gt; popover&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    Anchor Popover Left&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;position-area による配置指定が基本となりそうだが、top や left などを使用した直接的な位置指定も可能。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://developer.mozilla.org/ja/docs/Web/CSS/CSS_anchor_positioning/Using#position-area_%E3%81%AE%E8%A8%AD%E5%AE%9A&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;CSS アンカー位置指定の使用 - CSS | MDN&lt;/h3&gt;&lt;p&gt;CSS アンカー位置指定モジュールは、要素を結びつけるための機能を定義します。要素は、アンカー要素とアンカー位置指定要素として定義できます。アンカー位置指定要素は、アンカー要素に束縛することができます。アンカー位置決めされた要素は、そのサイズと位置を、結合されたアンカー要素のサイズと位置に相対的に設定することができます。&lt;/p&gt;&lt;span&gt;developer.mozilla.org&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;fallback を設定することができ、画面に収まらない場合の挙動を調整することが出来る。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/CSS/position-try-fallbacks&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;position-try-fallbacks - CSS | MDN&lt;/h3&gt;&lt;p&gt;The position-try-fallbacks CSS property enables you to specify a list of one or more alternative position try fallback options for anchor-positioned elements to be placed relative to their associated anchor elements. When the element would otherwise overflow its inset-modified containing block, the browser will try placing the positioned element in these different fallback positions, in the order provided, until it finds a value that stops it from overflowing its container or the viewport.&lt;/p&gt;&lt;span&gt;developer.mozilla.org&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h3&gt;Close Watcher&lt;/h3&gt;
&lt;div&gt;&lt;a href=&quot;https://developer.mozilla.org/en-US/docs/Web/API/CloseWatcher&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;CloseWatcher - Web APIs | MDN&lt;/h3&gt;&lt;p&gt;The CloseWatcher interface allows a custom UI component with open and close semantics to respond to device-specific close actions in the same way as a built-in component.&lt;/p&gt;&lt;span&gt;developer.mozilla.org&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;
&lt;p&gt;ユーザーが閉じようとした動作を検知できる API で、具体的には ESC や戻るを押したタイミングが検知できる。&lt;code&gt;popover=manual&lt;/code&gt; や非モーダルダイアログには ESC での閉じる機能がないので、対応する必要が出てきたときに有効。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;let&lt;/span&gt;&lt;span&gt; closeWatcher &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; null&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;popoverTrigger.&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;click&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (closeWatcher) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        closeWatcher.&lt;/span&gt;&lt;span&gt;destroy&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    closeWatcher &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; CloseWatcher&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    closeWatcher.&lt;/span&gt;&lt;span&gt;onclose&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        popover.&lt;/span&gt;&lt;span&gt;hidePopover&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    popover.&lt;/span&gt;&lt;span&gt;showPopover&lt;/span&gt;&lt;span&gt;()&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;})&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Refs&lt;/h2&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://blog.jxck.io/tags/#popover&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;#Tags | blog.jxck.io&lt;/h3&gt;&lt;span&gt;blog.jxck.io&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://zenn.dev/d_kawaguchi/articles/css-anchor-positioning-294aa71a7f77fc&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;zenn.dev&lt;/h3&gt;&lt;span&gt;zenn.dev&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://azukiazusa.dev/blog/close-watcher-api/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;UI を閉じる動作を処理する CloseWatcher API&lt;/h3&gt;&lt;p&gt;CloseWatcher API は UI を閉じる動作を処理するための API です。キーボードの `Esc` キー、Android の戻るボタンなどのデバイス固有のメカニズムによって閉じるイベントを提供します。&lt;/p&gt;&lt;span&gt;azukiazusa.dev&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</content:encoded></item><item><title>Node がデフォルトで TypeScript をサポートするようです</title><link>https://yaakai.to/note/15/</link><guid isPermaLink="true">https://yaakai.to/note/15/</guid><pubDate>Sat, 28 Dec 2024 09:58:23 GMT</pubDate><content:encoded>&lt;p&gt;&lt;code&gt;--experimental-strip-types&lt;/code&gt; を使うことで Node で TS を実行することができていましたが、フラグが不要になるようです。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/nodejs/node/pull/56350&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;module: unflag --experimental-strip-types by marco-ippolito · Pull Request #56350 · nodejs/node&lt;/h3&gt;&lt;p&gt;It&amp;amp;amp;#39;s time to enable it by default to catch some more bugs, currently there are no open issues.
I think it&amp;amp;amp;#39;s a semver minor change.
Fixes: nodejs/typescript#17
@nodejs/tsc for visibility
Not...&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://www.totaltypescript.com/typescript-is-coming-to-node-23&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Node.js Now Supports TypeScript By Default&lt;/h3&gt;&lt;p&gt;TypeScript is coming to Node 23. Let&amp;amp;#x27;s break down what that means.&lt;/p&gt;&lt;span&gt;www.totaltypescript.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;フラグが削除されるだけなので &lt;code&gt;allowImportingTsExtensions&lt;/code&gt; を設定したりといったことは引き続き必要です。ただ、次期 TS バージョンにおいてこのあたりも整理される予定とのこと。&lt;/p&gt;
&lt;p&gt;&lt;code&gt;namespace&lt;/code&gt; や &lt;code&gt;enum&lt;/code&gt; といった TS の機能を利用するには引き続き &lt;code&gt;--experimental-transform-types&lt;/code&gt; が必要です。&lt;/p&gt;</content:encoded></item><item><title>--experimental-strip-types と Node 組み込みランナーで TypeScript をテストをする</title><link>https://yaakai.to/note/14/</link><guid isPermaLink="true">https://yaakai.to/note/14/</guid><pubDate>Mon, 16 Dec 2024 19:08:14 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://yaakai.to/note/4/&quot;&gt;前回&lt;/a&gt; の続きのようなもので、Node 22.6 で &lt;code&gt;--experimental-strip-types&lt;/code&gt; が入ったので swc-node を使う必要がなくなりました。次のようなコマンドで、 Node の組み込みテストランナーを使って TypeScript のテストが実行できます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; node&lt;/span&gt;&lt;span&gt; --experimental-strip-types&lt;/span&gt;&lt;span&gt; --watch&lt;/span&gt;&lt;span&gt; --test&lt;/span&gt;&lt;span&gt; &apos;src/**/*.test.ts&apos;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ただしすんなり動くわけではなく、やはりモジュールの解決周りで調整が必要になります。ここに詳しく解説するスペースはないので、詳しく知りたい方はリンクを参照してください。&lt;/p&gt;
&lt;p&gt;前回はコード上では &lt;code&gt;.js&lt;/code&gt; を省略しない import とし、bundler 側で辻褄を合わせていました。&lt;code&gt;--experimental-strip-types&lt;/code&gt; の場合は現状では &lt;code&gt;.(m)ts&lt;/code&gt; が省略できなくなるので、コード上の import を &lt;code&gt;.ts&lt;/code&gt; に書き直しました。このままだとエディタ上でエラーとなってしまうため、 &lt;code&gt;tsconfig.json&lt;/code&gt; で &lt;code&gt;allowImportingTsExtensions &lt;/code&gt; と &lt;code&gt;noEmit&lt;/code&gt; を立てます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;compilerOptions&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;allowImportingTsExtensions&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;noEmit&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;また、&lt;code&gt;namespace&lt;/code&gt; や &lt;code&gt;enum&lt;/code&gt; のような、型の表現以外の TypeScript のコードを実行するには別途 &lt;code&gt;--experimental-transform-types&lt;/code&gt; が必要です。今回は必要ないコードしかなかったので指定しませんでした。&lt;/p&gt;
&lt;h2&gt;Refs&lt;/h2&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://hiroppy.me/blog/nodejs-strip-type/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Node.jsでTypeScriptのコードを実行できるようになるかも | hiroppy&amp;amp;#x27;s site&lt;/h3&gt;&lt;p&gt;Node.jsでTypeScriptのコードを実行できるような機能が提案されました&lt;/p&gt;&lt;span&gt;hiroppy.me&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://zenn.dev/uhyo/articles/rewrite-relative-import-extensions-read-before-use&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;zenn.dev&lt;/h3&gt;&lt;span&gt;zenn.dev&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://nodejs.org/en/blog/release/v22.6.0&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Node.js — Node.js 22.6.0 (Current)&lt;/h3&gt;&lt;p&gt;Node.js® is a free, open-source, cross-platform JavaScript runtime environment that lets developers create servers, web apps, command line tools and scripts.&lt;/p&gt;&lt;span&gt;nodejs.org&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</content:encoded></item><item><title>--noCheck オプションで tsc でも型チェックをスキップできるように</title><link>https://yaakai.to/note/13/</link><guid isPermaLink="true">https://yaakai.to/note/13/</guid><pubDate>Mon, 16 Dec 2024 18:11:48 GMT</pubDate><content:encoded>&lt;p&gt;TypeScript 5.6 から &lt;code&gt;--noCheck&lt;/code&gt; オプションが追加され、これまで必ず行われていた型チェックが tsc 単体でもスキップできるようになりました。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$ tsc --noCheck&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;swc などが主流で、Node も単体で TS の実行をサポートしようとしていたりする中で、特別用途があるのかというと… という感じです。&lt;/p&gt;
&lt;h2&gt;Refs&lt;/h2&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-5-6/#the---nocheck-option&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Announcing TypeScript 5.6 - TypeScript&lt;/h3&gt;&lt;p&gt;Today we&amp;amp;#8217;re excited to announce the release of TypeScript 5.6! If you&amp;amp;#8217;re not familiar with TypeScript, it&amp;amp;#8217;s a language that builds on top of JavaScript by adding syntax for types. Types describe the shapes we expect of our variables, parameters, and functions, and the TypeScript type-checker can help catch issues like typos, missing properties, and [&amp;amp;hellip;]&lt;/p&gt;&lt;span&gt;devblogs.microsoft.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</content:encoded></item><item><title>Declarative Shadow DOM</title><link>https://yaakai.to/note/12/</link><guid isPermaLink="true">https://yaakai.to/note/12/</guid><pubDate>Sun, 03 Nov 2024 18:41:35 GMT</pubDate><content:encoded>&lt;p&gt;Baseline 2024 で追加された、これまで JavaScript からしか生成できなかった Shadow DOM を HTML で定義できるようになる機能です。これによって、Web Components をサーバーサイドでのレンダリングに対応させることができます。&lt;/p&gt;
&lt;p&gt;次のような HTML として記述することで Shadow DOM が生成されます。(&lt;a href=&quot;https://yaakaito.github.io/code-sandbox/declarative-shadow-dom/&quot;&gt;動作サンプル&lt;/a&gt;)&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;my-custom-element&lt;/span&gt;&lt;span&gt; data-count&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;12345&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt; shadowrootmode&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;open&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;&lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            *&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                margin&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                padding&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;0&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            .container&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                display&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;flex&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                gap&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;8&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            .counter&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                font-weight&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;bold&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            .increment&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;.decrement&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;20&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                cursor&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;pointer&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;/&lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;container&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;counter&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;12345&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;increment&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;+&amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;decrement&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;-&amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;/&lt;/span&gt;&lt;span&gt;template&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;my-custom-element&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;同名で &lt;code&gt;customElements.define&lt;/code&gt; された &lt;code&gt;HTMLElement&lt;/code&gt; があれば、自動的にアップデートされ、 &lt;code&gt;constructor&lt;/code&gt; や &lt;code&gt;connectedCallback&lt;/code&gt; で Hydration を行うことができます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;class&lt;/span&gt;&lt;span&gt; MyCustomElement&lt;/span&gt;&lt;span&gt; extends&lt;/span&gt;&lt;span&gt; HTMLElement&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    connectedCallback&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        let&lt;/span&gt;&lt;span&gt; count &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; parseInt&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;this&lt;/span&gt;&lt;span&gt;.&lt;/span&gt;&lt;span&gt;getAttribute&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;data-count&apos;&lt;/span&gt;&lt;span&gt;))&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        this&lt;/span&gt;&lt;span&gt;.shadowRoot.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;.increment&apos;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;click&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            this&lt;/span&gt;&lt;span&gt;.shadowRoot.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;.counter&apos;&lt;/span&gt;&lt;span&gt;).textContent &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; String&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;++&lt;/span&gt;&lt;span&gt;count)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        this&lt;/span&gt;&lt;span&gt;.shadowRoot.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;.decrement&apos;&lt;/span&gt;&lt;span&gt;).&lt;/span&gt;&lt;span&gt;addEventListener&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;click&apos;&lt;/span&gt;&lt;span&gt;, () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            this&lt;/span&gt;&lt;span&gt;.shadowRoot.&lt;/span&gt;&lt;span&gt;querySelector&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;.counter&apos;&lt;/span&gt;&lt;span&gt;).textContent &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; String&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;--&lt;/span&gt;&lt;span&gt;count)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        })&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;customElements.&lt;/span&gt;&lt;span&gt;define&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&quot;my-custom-element&quot;&lt;/span&gt;&lt;span&gt;, MyCustomElement);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Declarative Shadow DOM の形で JavaScript から要素を追加する場合、 &lt;code&gt;$el.innerHTML = htmlString&lt;/code&gt; ではセキュリティ上の理由から実行が行われず、代わりに &lt;code&gt;setHTMLUnsafe&lt;/code&gt; や &lt;code&gt;parseHTMLUnsafe&lt;/code&gt; を利用する必要があります。&lt;/p&gt;
&lt;h1&gt;Refs&lt;/h1&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://web.dev/articles/declarative-shadow-dom?hl=ja&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;宣言型の Shadow DOM &amp;amp;nbsp;|&amp;amp;nbsp; web.dev&lt;/h3&gt;&lt;p&gt;宣言型 Shadow DOM は、Shadow DOM を HTML で直接実装して使用する新しい方法です。&lt;/p&gt;&lt;span&gt;web.dev&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://azukiazusa.dev/blog/declarative-shadow-dom/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;HTML だけで Shadow DOM を構築するための宣言型 Shadow DOM&lt;/h3&gt;&lt;p&gt;宣言型 Shadow DOM は `&amp;amp;lt;template&amp;gt;` 要素を使用して Shadow DOM を構築する方法です。宣言型 Shadow DOM を使用することで、従来の JavaScript を使用した Shadow DOM の構築方法と比較して、サーバーサイドレンダリング（SSR）に対応しているため、パフォーマンスの向上や SEO 対策に期待されます。&lt;/p&gt;&lt;span&gt;azukiazusa.dev&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://zenn.dev/cybozu_frontend/articles/web-standardized-component-in-server-and-client#declarative-shadow-dom-(dsd)-%E3%81%A8%E3%81%AF%EF%BC%9F&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;zenn.dev&lt;/h3&gt;&lt;span&gt;zenn.dev&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</content:encoded></item><item><title>TypeScript 5.6 の Disallowed Nullish and Truthy Checks と ESLint no-constant-binary-expression</title><link>https://yaakai.to/note/11/</link><guid isPermaLink="true">https://yaakai.to/note/11/</guid><pubDate>Thu, 10 Oct 2024 07:52:06 GMT</pubDate><content:encoded>&lt;p&gt;TypeScript 5.6 から、常に真または偽になるような意図していない条件式や、Null 合体演算子 (&lt;code&gt;??&lt;/code&gt;) を誤って使用している場合にエラーとなるようになりました。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://devblogs.microsoft.com/typescript/announcing-typescript-5-6/#disallowed-nullish-and-truthy-checks&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Announcing TypeScript 5.6 - TypeScript&lt;/h3&gt;&lt;p&gt;Today we&amp;amp;#8217;re excited to announce the release of TypeScript 5.6! If you&amp;amp;#8217;re not familiar with TypeScript, it&amp;amp;#8217;s a language that builds on top of JavaScript by adding syntax for types. Types describe the shapes we expect of our variables, parameters, and functions, and the TypeScript type-checker can help catch issues like typos, missing properties, and [&amp;amp;hellip;]&lt;/p&gt;&lt;span&gt;devblogs.microsoft.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;公式のコードを引用すると次のような場合にエラーが発生するようなります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;0x&lt;/span&gt;&lt;span&gt;[0-9a-f]&lt;/span&gt;&lt;span&gt;/&lt;/span&gt;&lt;span&gt;) { } &lt;/span&gt;&lt;span&gt;// error: This kind of expression is always truthy.&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;if&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;x&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; 0&lt;/span&gt;&lt;span&gt;) { } &lt;/span&gt;&lt;span&gt;// error: This kind of expression is always truthy.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;特に Null 合体演算子の誤用は、コードが直感に反していることもあり大きな恩恵を得ることが出来そうです。例えば次のコードは &lt;code&gt;options.max&lt;/code&gt; が未定義の際に &lt;code&gt;value &amp;lt; 100&lt;/code&gt; と扱われるように思えますが実際にはそうではありません。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; value &lt;/span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt; options.max &lt;/span&gt;&lt;span&gt;??&lt;/span&gt;&lt;span&gt; 100&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// error: Right operand of ?? is unreachable because the left operand is never nullish.&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これは実際には &lt;code&gt;(value &amp;lt; options.max) ?? 100&lt;/code&gt; と扱われます。実数にした適当なコードを実行してみると、結果が予想と異なっていることがわかります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;10&lt;/span&gt;&lt;span&gt; &amp;lt;&lt;/span&gt;&lt;span&gt; {}.max &lt;/span&gt;&lt;span&gt;??&lt;/span&gt;&lt;span&gt; 100&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;// &amp;gt; false&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;また、この新しい機能は ESLint の no-constant-binary-expression ルールが元になっているようです。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://eslint.org/blog/2024/10/code-review-nit-to-ecosystem-improvements/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;no-unused-binary-expressions: From code review nit to ecosystem improvements - ESLint - Pluggable JavaScript Linter&lt;/h3&gt;&lt;p&gt;A pluggable and configurable linter tool for identifying and reporting on patterns in JavaScript. Maintain your code quality with ease.&lt;/p&gt;&lt;span&gt;eslint.org&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</content:encoded></item><item><title>Speculation Rules API を使ったページの事前レンダリング</title><link>https://yaakai.to/note/10/</link><guid isPermaLink="true">https://yaakai.to/note/10/</guid><pubDate>Mon, 19 Aug 2024 14:53:42 GMT</pubDate><content:encoded>&lt;p&gt;&lt;code&gt;rel=&quot;prerender&quot;&lt;/code&gt; を利用したページの事前読み込みに変わる次世代の API として、 Speculation Rules API というものが Chrome に導入されました。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://developer.chrome.com/docs/web-platform/prerender-pages?hl=ja&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Chrome でページを事前レンダリングしてページ ナビゲーションを即時に行う &amp;amp;nbsp;|&amp;amp;nbsp; Web Platform &amp;amp;nbsp;|&amp;amp;nbsp; Chrome for Developers&lt;/h3&gt;&lt;p&gt;Chrome チームでは、ユーザーがアクセスする可能性の高い今後のページを完全に事前レンダリングする機能の開発に取り組んできました。&lt;/p&gt;&lt;span&gt;developer.chrome.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;&amp;lt;script&amp;gt;&lt;/code&gt; で比較的簡単な記述をするだけでページの事前準備をすることができ、&lt;a href=&quot;https://yaakai.to/note/9/&quot;&gt;前回の View Transition API&lt;/a&gt; と組み合わせるとネイティブアプリのような滑らかなページ遷移を実現することが出来るようです。View Transition API の例を元にすると、次のようなコードで遷移先の内容を読み込んでおくことができます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;!&lt;/span&gt;&lt;span&gt;DOCTYPE&lt;/span&gt;&lt;span&gt; html&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt; lang&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;en&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;!-- 略 --&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;nav&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;navHome&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;./index.html&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;🏠 Home&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;navBlog&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;./blog.html&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;🖊 Blog&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;navAbout&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;./about.html&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;🐴 About&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;/&lt;/span&gt;&lt;span&gt;nav&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;mark&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;speculationrules&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;prerender&quot;: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &quot;urls&quot;: [&quot;about.html&quot;, &quot;blog.html&quot;]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;/&lt;/span&gt;&lt;span&gt;script&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;html&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;実際の動作を比べてみると、View Transition API のみでは一度遷移し読み込みが完了するまで少し待ってからアニメーションが動作するのに対して、Speculation Rules API も足すことでクリック直後に素早くアニメーションと遷移が動作するようになります。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://yaakaito.github.io/code-sandbox/mp-view-transition/index.html&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;yaakaito.github.io&lt;/h3&gt;&lt;span&gt;yaakaito.github.io&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://yaakaito.github.io/code-sandbox/speculation-rules/index.html&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;yaakaito.github.io&lt;/h3&gt;&lt;span&gt;yaakaito.github.io&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;DevTool で動作確認を行う場合、 &lt;code&gt;Application &amp;gt; Background Services &amp;gt; Speculative loads&lt;/code&gt; から動作を確認できます。&lt;/p&gt;
&lt;p&gt;また、JavaScript から動的にルールを生成することも可能です。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://developer.chrome.com/docs/web-platform/prerender-pages?hl=ja#add_speculation_rules_dynamically_through_javascript&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Chrome でページを事前レンダリングしてページ ナビゲーションを即時に行う &amp;amp;nbsp;|&amp;amp;nbsp; Web Platform &amp;amp;nbsp;|&amp;amp;nbsp; Chrome for Developers&lt;/h3&gt;&lt;p&gt;Chrome チームでは、ユーザーがアクセスする可能性の高い今後のページを完全に事前レンダリングする機能の開発に取り組んできました。&lt;/p&gt;&lt;span&gt;developer.chrome.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</content:encoded></item><item><title>View Transition API を使った MPA でのページ間アニメーション</title><link>https://yaakai.to/note/9/</link><guid isPermaLink="true">https://yaakai.to/note/9/</guid><pubDate>Tue, 23 Jul 2024 14:40:10 GMT</pubDate><content:encoded>&lt;p&gt;Chrome 126 で SPA でない通常のページ遷移の際にも間を補完するアニメーションを実装することが可能になりました。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://developer.chrome.com/blog/new-in-chrome-126#cross-document-transitions&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Novità di Chrome 126 &amp;amp;nbsp;|&amp;amp;nbsp; Blog &amp;amp;nbsp;|&amp;amp;nbsp; Chrome for Developers&lt;/h3&gt;&lt;p&gt;È in corso l&amp;amp;#39;implementazione di Chrome 126. Grazie al supporto delle transizioni tra documenti nell&amp;amp;#39;API ViewTransiziones, l&amp;amp;#39;API CloseWatcher ha riattivato l&amp;amp;#39;utilizzo, ha generato voci di trigger per l&amp;amp;#39;API Gamepad e non solo, ma c&amp;amp;#39;è molto altro e molto altro ancora. Adriana Jara ha tutti i dettagli sulle novità per gli sviluppatori in Chrome 126.&lt;/p&gt;&lt;span&gt;developer.chrome.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://developer.chrome.com/docs/web-platform/view-transitions?hl=ja&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;View Transition API によるスムーズな遷移 &amp;amp;nbsp;|&amp;amp;nbsp; View Transitions &amp;amp;nbsp;|&amp;amp;nbsp; Chrome for Developers&lt;/h3&gt;&lt;p&gt;View Transition API を使用すると、ウェブサイトのビュー間の遷移を追加できます。&lt;/p&gt;&lt;span&gt;developer.chrome.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;次の CSS を遷移元と遷移先で設定しておくことで、デフォルトではフェードイン・アウトなアニメーションが適用されるようになります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;@view-transition&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  navigation: auto;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;例としてナビゲーションにマーカーが付くようなページを用意しました。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://yaakaito.github.io/code-sandbox/mp-view-transition/index.html&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;yaakaito.github.io&lt;/h3&gt;&lt;span&gt;yaakaito.github.io&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;これは CSS Anchor Positioning API を利用して実装していて、それぞれのページでは &lt;code&gt;position-anchor&lt;/code&gt; にそのページのものを指定するだけでページ遷移時にアニメーションが適用されます。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://developer.chrome.com/blog/anchor-positioning-api?hl=ja&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;CSS アンカー ポジショニング API のご紹介 &amp;amp;nbsp;|&amp;amp;nbsp; Blog &amp;amp;nbsp;|&amp;amp;nbsp; Chrome for Developers&lt;/h3&gt;&lt;p&gt;新しい Anchor Positioning API を使用して、要素を互いに相対的に配置します。&lt;/p&gt;&lt;span&gt;developer.chrome.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;nav&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;navHome&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                &amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;./index.html&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;🏠 Home&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;navBlog&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                &amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;./blog.html&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;🖊 Blog&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;navAbout&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                &amp;lt;&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt; href&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;./about.html&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;🐴 About&amp;lt;/&lt;/span&gt;&lt;span&gt;a&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;/&lt;/span&gt;&lt;span&gt;nav&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt; id&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;mark&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;body&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;@view-transition&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  navigation: auto;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#navHome&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  anchor-name&lt;/span&gt;&lt;span&gt;: --anchor-home;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#navBlog&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  anchor-name&lt;/span&gt;&lt;span&gt;: --anchor-blog;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#navAbout&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  anchor-name&lt;/span&gt;&lt;span&gt;: --anchor-about;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;#mark&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  position&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;absolute&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  position-anchor&lt;/span&gt;&lt;span&gt;: --anchor-home;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  background&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;black&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  left&lt;/span&gt;&lt;span&gt;: anchor(&lt;/span&gt;&lt;span&gt;left&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  top&lt;/span&gt;&lt;span&gt;: anchor(&lt;/span&gt;&lt;span&gt;top&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  width&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;4&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  height&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;32&lt;/span&gt;&lt;span&gt;px&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  view-transition-name&lt;/span&gt;&lt;span&gt;: mark;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;#mark&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    position-anchor&lt;/span&gt;&lt;span&gt;: --anchor-home;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content:encoded></item><item><title>React 19 の use で Suspense に対応したコンポーネントを作る</title><link>https://yaakai.to/note/8/</link><guid isPermaLink="true">https://yaakai.to/note/8/</guid><pubDate>Tue, 04 Jun 2024 19:27:05 GMT</pubDate><content:encoded>&lt;p&gt;React 19 では &lt;code&gt;use&lt;/code&gt; が導入され、これを利用すると &lt;code&gt;&amp;lt;Suspense&amp;gt;&lt;/code&gt; に対応したコンポーネントの作成が容易になります。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://ja.react.dev/blog/2024/04/25/react-19#new-feature-use&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;ja.react.dev&lt;/h3&gt;&lt;span&gt;ja.react.dev&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://ja.react.dev/reference/react/use&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;- This feature is available in the latest Canary version of React&lt;/h3&gt;&lt;span&gt;ja.react.dev&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Promise を &lt;code&gt;use&lt;/code&gt; を経由して利用すると、それが完了していない場合に例外が投げられるので、これがそのまま Suspense の実装として使えます。公式にも書かれていますが、これを &lt;code&gt;try-catch&lt;/code&gt; すると例外として拾ってしまうので動作しなくなります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; {use} &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;react&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; ChildComponet&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; data&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; use&lt;/span&gt;&lt;span&gt;(aPromise)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; &amp;lt;&lt;/span&gt;&lt;span&gt;div&lt;/span&gt;&lt;span&gt;&amp;gt;{data}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;function App() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;Suspense&lt;/span&gt;&lt;span&gt; fallback&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{&amp;lt;&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;loading&amp;lt;/&lt;/span&gt;&lt;span&gt;p&lt;/span&gt;&lt;span&gt;&amp;gt;}&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;ChildComponent&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;/&lt;/span&gt;&lt;span&gt;Suspense&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  )&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;通常の Hooks と異なり、 &lt;code&gt;if&lt;/code&gt; を絡めて早期 Return などを書くことも可能です。&lt;/p&gt;</content:encoded></item><item><title>:has() でリストが空のときにセクションごと消す</title><link>https://yaakai.to/note/6/</link><guid isPermaLink="true">https://yaakai.to/note/6/</guid><pubDate>Sun, 02 Jun 2024 20:14:07 GMT</pubDate><content:encoded>&lt;p&gt;&lt;code&gt;:has()&lt;/code&gt; が 2023 年末にすべてのブラウザでサポートされて、現実的に利用できるようになりました。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://developer.mozilla.org/ja/docs/Web/CSS/:has&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;:has() - CSS | MDN&lt;/h3&gt;&lt;p&gt;:has() は CSS の擬似クラス関数で、引数として渡される相対セレクターのいずれかが、その要素から辿ってアンカーとして少なくとも一つの要素と一致する場合に、その要素を表します。この擬似クラスは、相対セレクターリストを引数として取ることで、参照している要素に関して親要素や前の兄弟要素を選択する方法を提供します。&lt;/p&gt;&lt;span&gt;developer.mozilla.org&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://coliss.com/articles/build-websites/operation/css/css-has-pseudo-class.html&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;朗報！ CSSの:has()疑似クラスがすべてのブラウザにサポートされました、:has()疑似クラスの便利な使い方のまとめ&lt;/h3&gt;&lt;p&gt;CSSの:has()疑似クラスは便利そうだけどブラウザのサポートがまだ、と見送っていた人に朗報です。12/19にリリースされたFirefox 121（リリース情報）でサポートされ、これで:has()疑&lt;/p&gt;&lt;span&gt;coliss.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;「渡したセレクターにマッチする子要素を持つ親要素であるか」を判定できるので、例えばリストが空のときにセクションごと消すことができます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;section&lt;/span&gt;&lt;span&gt;:has&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;:empty&lt;/span&gt;&lt;span&gt;) {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  display&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;none&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;style&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;section&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;section&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;section1&amp;lt;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;item1&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &amp;lt;&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;item2&amp;lt;/&lt;/span&gt;&lt;span&gt;li&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;section&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;&lt;/span&gt;&lt;span&gt;section&lt;/span&gt;&lt;span&gt; class&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;section&quot;&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;section2&amp;lt;/&lt;/span&gt;&lt;span&gt;h1&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;/&lt;/span&gt;&lt;span&gt;ul&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&amp;lt;/&lt;/span&gt;&lt;span&gt;section&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content:encoded></item><item><title>CLI から VSCode の拡張を入れる</title><link>https://yaakai.to/note/5/</link><guid isPermaLink="true">https://yaakai.to/note/5/</guid><pubDate>Sun, 02 Jun 2024 20:13:54 GMT</pubDate><content:encoded>&lt;p&gt;&lt;code&gt;--install-extension&lt;/code&gt; というコマンドがあり、これを使うと CLI から拡張機能をインストールできます。
プロジェクトに直接関係しないが devcontainer に入っていてほしい、みたいなものに個人の設定で使うと便利そうです。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;code --install-extension mhutchie.git-graph&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;code --install-extension eamodio.gitlens&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content:encoded></item><item><title>Node 組み込みテストランナーと swc で TypeScript をテストする</title><link>https://yaakai.to/note/4/</link><guid isPermaLink="true">https://yaakai.to/note/4/</guid><pubDate>Sun, 02 Jun 2024 20:13:39 GMT</pubDate><content:encoded>&lt;p&gt;Node 18 から実験的に導入された組み込みのテストランナーがありますが、これが 21 で対象を glob 指定できるようになりました。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://nodejs.org/en/blog/announcements/v18-release-announce#test-runner-module-experimental&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Node.js — Node.js 18 is now available!&lt;/h3&gt;&lt;p&gt;Node.js® is a free, open-source, cross-platform JavaScript runtime environment that lets developers create servers, web apps, command line tools and scripts.&lt;/p&gt;&lt;span&gt;nodejs.org&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://nodejs.org/en/blog/announcements/v21-release-announce#support-for-globs-in-the-nodejs-test-runner&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Node.js — Node.js 21 is now available!&lt;/h3&gt;&lt;p&gt;Node.js® is a free, open-source, cross-platform JavaScript runtime environment that lets developers create servers, web apps, command line tools and scripts.&lt;/p&gt;&lt;span&gt;nodejs.org&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;swc-node と組み合わせると、それなりに使えそうなテストランナーになりそうです。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/swc-project/swc-node&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;GitHub - swc-project/swc-node: Faster ts-node without typecheck&lt;/h3&gt;&lt;p&gt;Faster ts-node without typecheck. Contribute to swc-project/swc-node development by creating an account on GitHub.&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;これらを用意した状態で次のコマンドにすると、 TS をトランスパイルしつつ watch モードで動くテストランナーが作れます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;$&lt;/span&gt;&lt;span&gt; node&lt;/span&gt;&lt;span&gt; --import&lt;/span&gt;&lt;span&gt; @swc-node/register/esm-register&lt;/span&gt;&lt;span&gt; --watch&lt;/span&gt;&lt;span&gt; --test&lt;/span&gt;&lt;span&gt; &apos;src/**/*.test.ts&apos;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;div&gt;
&lt;p&gt;NOTE&lt;/p&gt;
&lt;p&gt;
webpack などのバンドラーを使って ESM で記述している場合、Node の ESM と共存できるようにする必要があります。
自分は import を &lt;code&gt;*.js&lt;/code&gt; で行うようにし、 &lt;code&gt;extentionAlias&lt;/code&gt; で &lt;code&gt;import xxx from &apos;./xxx.js&apos;&lt;/code&gt; を &lt;code&gt;.ts&lt;/code&gt; としても解決できるようにしました。
&lt;a href=&quot;https://webpack.js.org/configuration/resolve/#resolveextensionalias&quot;&gt;https://webpack.js.org/configuration/resolve/#resolveextensionalias&lt;/a&gt;&lt;/p&gt;
&lt;/div&gt;</content:encoded></item><item><title>@mozilla/readability を使った HTML からの本文抽出</title><link>https://yaakai.to/note/3/</link><guid isPermaLink="true">https://yaakai.to/note/3/</guid><pubDate>Sun, 02 Jun 2024 20:13:24 GMT</pubDate><content:encoded>&lt;p&gt;@mozilla/readability を使うと簡単に本文を抽出することができます。これは Firefox のリーダービューで使われているもののようです。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/mozilla/readability&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;GitHub - mozilla/readability: A standalone version of the readability lib&lt;/h3&gt;&lt;p&gt;A standalone version of the readability lib. Contribute to mozilla/readability development by creating an account on GitHub.&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://support.mozilla.org/ja/kb/firefox-reader-view-clutter-free-web-pages&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;ウェブページをリーダービューでまとめて読む | Firefox ヘルプ&lt;/h3&gt;&lt;p&gt;Firefox のリーダービューは、ウェブページからボタンや広告などの余計なものを削除し、記事本文を画面で読みやすくする表示モードです。&lt;/p&gt;&lt;span&gt;support.mozilla.org&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;今回はこの結果を Claude3 に投げたかったので、DOMPurify を組み合わせて最小限文章がわかりそうな DOM として取り出しています。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/cure53/DOMPurify&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;GitHub - cure53/DOMPurify: DOMPurify - a DOM-only, super-fast, uber-tolerant XSS sanitizer for HTML, MathML and SVG. DOMPurify works with a secure default, but offers a lot of configurability and hooks. Demo:&lt;/h3&gt;&lt;p&gt;DOMPurify - a DOM-only, super-fast, uber-tolerant XSS sanitizer for HTML, MathML and SVG. DOMPurify works with a secure default, but offers a lot of configurability and hooks. Demo: - cure53/DOMPurify&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { Readability } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;@mozilla/readability&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; { JSDOM } &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;jsdom&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;import&lt;/span&gt;&lt;span&gt; createDOMPurify &lt;/span&gt;&lt;span&gt;from&lt;/span&gt;&lt;span&gt; &apos;dompurify&apos;&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;export&lt;/span&gt;&lt;span&gt; const&lt;/span&gt;&lt;span&gt; getReadability&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; async&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;span&gt;url&lt;/span&gt;&lt;span&gt;:&lt;/span&gt;&lt;span&gt; string&lt;/span&gt;&lt;span&gt;) &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; content&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; fetch&lt;/span&gt;&lt;span&gt;(url).&lt;/span&gt;&lt;span&gt;then&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;res&lt;/span&gt;&lt;span&gt; =&amp;gt;&lt;/span&gt;&lt;span&gt; res.&lt;/span&gt;&lt;span&gt;text&lt;/span&gt;&lt;span&gt;());&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; reader&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; new&lt;/span&gt;&lt;span&gt; Readability&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; JSDOM&lt;/span&gt;&lt;span&gt;(content, {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        url&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }).window.document);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; readability&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; (reader.&lt;/span&gt;&lt;span&gt;parse&lt;/span&gt;&lt;span&gt;() &lt;/span&gt;&lt;span&gt;??&lt;/span&gt;&lt;span&gt; { content: &lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt; });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; createDOMPurify&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;new&lt;/span&gt;&lt;span&gt; JSDOM&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;&apos;&lt;/span&gt;&lt;span&gt;).window).&lt;/span&gt;&lt;span&gt;sanitize&lt;/span&gt;&lt;span&gt;(readability.content, {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ALLOWED_TAGS: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &apos;h1&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;h2&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;h3&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;h4&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;h5&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;h6&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &apos;blockquote&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;code&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;pre&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &apos;p&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;br&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;hr&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &apos;a&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;img&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;table&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;thead&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;tbody&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;tr&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;th&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;td&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &apos;ul&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;ol&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;li&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &apos;dl&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;dt&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;dd&apos;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        ALLOWED_ATTR: [&lt;/span&gt;&lt;span&gt;&apos;href&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;src&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;title&apos;&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;&apos;alt&apos;&lt;/span&gt;&lt;span&gt;],&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    });&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content:encoded></item><item><title>React 19 Actions</title><link>https://yaakai.to/note/2/</link><guid isPermaLink="true">https://yaakai.to/note/2/</guid><pubDate>Sun, 02 Jun 2024 20:13:05 GMT</pubDate><content:encoded>&lt;p&gt;React 19 では、データ更新とそれに伴う一連の流れをうまく扱う方法として Actions が導入されるようです。
例えば、これまでフォームの送信中の状態を次のように扱うことがありましたが、これが React の機能として標準化されます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;isPending&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setIsPending&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; useState&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; submit&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  setIsPending&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;true&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  await&lt;/span&gt;&lt;span&gt; foo&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  setIsPending&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;false&lt;/span&gt;&lt;span&gt;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;form&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; click&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{submit} &lt;/span&gt;&lt;span&gt;disable&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{isPending}&amp;gt;foo&amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;/&lt;/span&gt;&lt;span&gt;form&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    )&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;新しく導入される Actions の一つである &lt;code&gt;useActionState&lt;/code&gt; を利用して書くと次のように書けます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;submitAction&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;isPending&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; useActionState&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  async&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; error&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; foo&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (error) { &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; error; }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; null&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;&lt;/span&gt;&lt;span&gt;form&lt;/span&gt;&lt;span&gt; action&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{submitAction}&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;submit&quot;&lt;/span&gt;&lt;span&gt; disable&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{isPending}&amp;gt;foo&amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      &amp;lt;/&lt;/span&gt;&lt;span&gt;form&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    )&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;Hook に渡した関数は実質的に &lt;code&gt;submitAction&lt;/code&gt; の内容になっていて、&lt;code&gt;isPending&lt;/code&gt; はその関数の実行状態に合わせて値が変化します。
これによって最初のコードと同等に &lt;code&gt;isPending&lt;/code&gt; を使って UI の状態を変更できる、ということのようです。&lt;/p&gt;
&lt;h3&gt;useFormStatus&lt;/h3&gt;
&lt;p&gt;これに組み合わせて使えるものとして &lt;code&gt;useFormStatus&lt;/code&gt; という親の &lt;code&gt;form&lt;/code&gt; の実行状態を取得することが出来る Hook もあります、これは react-dom から提供されるものです。
例えばボタンコンポーネントを作るときにその状態を外から Props として渡すことがあると思いますが、 Actions を使った form の中ではこれを簡略化することが出来ます。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; FooSubmit&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; { &lt;/span&gt;&lt;span&gt;pending&lt;/span&gt;&lt;span&gt; } &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; useFormStatus&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &amp;lt;&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt; type&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;&quot;submit&quot;&lt;/span&gt;&lt;span&gt; disable&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{pending}&amp;gt;foo&amp;lt;/&lt;/span&gt;&lt;span&gt;button&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;function&lt;/span&gt;&lt;span&gt; FooForm&lt;/span&gt;&lt;span&gt;() {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;submitAction&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;isPending&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; useActionState&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    async&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      const&lt;/span&gt;&lt;span&gt; error&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; foo&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      if&lt;/span&gt;&lt;span&gt; (error) { &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; error; }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      return&lt;/span&gt;&lt;span&gt; null&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  )&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  return&lt;/span&gt;&lt;span&gt; (&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;&lt;/span&gt;&lt;span&gt;form&lt;/span&gt;&lt;span&gt; action&lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt;{submitAction}&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;          &amp;lt;&lt;/span&gt;&lt;span&gt;FooSubmit&lt;/span&gt;&lt;span&gt; /&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &amp;lt;/&lt;/span&gt;&lt;span&gt;form&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;      )&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;これが &lt;code&gt;useActionState&lt;/code&gt; の例と同じ動作になるようです。&lt;/p&gt;
&lt;h3&gt;useOptimistic&lt;/h3&gt;
&lt;p&gt;Actions が失敗したときに巻き戻される &lt;code&gt;useState&lt;/code&gt; のようなものです。次にコードで &lt;code&gt;await foo()&lt;/code&gt; が失敗すると、 &lt;code&gt;value&lt;/code&gt; が &lt;code&gt;currentValue&lt;/code&gt; に戻ります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;value&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;setValue&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; useOptimistic&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;currentValue&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;const&lt;/span&gt;&lt;span&gt; [&lt;/span&gt;&lt;span&gt;error&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;submitAction&lt;/span&gt;&lt;span&gt;, &lt;/span&gt;&lt;span&gt;isPending&lt;/span&gt;&lt;span&gt;] &lt;/span&gt;&lt;span&gt;=&lt;/span&gt;&lt;span&gt; useActionState&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  async&lt;/span&gt;&lt;span&gt; () &lt;/span&gt;&lt;span&gt;=&amp;gt;&lt;/span&gt;&lt;span&gt; {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    setValue&lt;/span&gt;&lt;span&gt;(&lt;/span&gt;&lt;span&gt;&apos;newValue&apos;&lt;/span&gt;&lt;span&gt;)&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    const&lt;/span&gt;&lt;span&gt; error&lt;/span&gt;&lt;span&gt; =&lt;/span&gt;&lt;span&gt; await&lt;/span&gt;&lt;span&gt; foo&lt;/span&gt;&lt;span&gt;();&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    if&lt;/span&gt;&lt;span&gt; (error) { &lt;/span&gt;&lt;span&gt;return&lt;/span&gt;&lt;span&gt; error; }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    return&lt;/span&gt;&lt;span&gt; null&lt;/span&gt;&lt;span&gt;;&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;)&lt;/span&gt;&lt;span&gt;&amp;gt;&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;ユーザー名を変更したいが、重複チェックに時間がかかるような場合に、結果の予定を先に UI に反映しておくような場合にその実装が標準化できるようです。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://react.dev/blog/2024/04/25/react-19&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;react.dev&lt;/h3&gt;&lt;span&gt;react.dev&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://react.dev/reference/react-dom/hooks/useFormStatus&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;- This feature is available in the latest Canary version of React&lt;/h3&gt;&lt;span&gt;react.dev&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;</content:encoded></item><item><title>node 22 の標準 glob を利用する</title><link>https://yaakai.to/note/1/</link><guid isPermaLink="true">https://yaakai.to/note/1/</guid><pubDate>Sun, 02 Jun 2024 20:07:55 GMT</pubDate><content:encoded>&lt;p&gt;Node 22 では実験的な機能として &lt;code&gt;glob&lt;/code&gt; と &lt;code&gt;globSync&lt;/code&gt; が導入されました。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://nodejs.org/en/blog/announcements/v22-release-announce#glob-and-globsync&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Node.js — Node.js 22 is now available!&lt;/h3&gt;&lt;p&gt;Node.js® is a free, open-source, cross-platform JavaScript runtime environment that lets developers create servers, web apps, command line tools and scripts.&lt;/p&gt;&lt;span&gt;nodejs.org&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;これまで glob パッケージを利用していた場合、次のように置き換えることが出来ます。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://www.npmjs.com/package/glob&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Just a moment...&lt;/h3&gt;&lt;span&gt;www.npmjs.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;&lt;span&gt;-&lt;/span&gt; const glob = require(&apos;glob&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;span&gt;+&lt;/span&gt; const { glob, globSync} = require(&apos;fs&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  glob(&apos;*&apos;, (error, matches) =&amp;gt; {});&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;span&gt;-&lt;/span&gt; const matches = glob.sync(&apos;*&apos;);&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;&lt;span&gt;+&lt;/span&gt; const matches = globSync(&apos;*&apos;);&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;</content:encoded></item><item><title>個人開発を Codespaces と DevContainer へ移行</title><link>https://yaakai.to/blog/2023/develop-in-codespaces-and-devcontainer/</link><guid isPermaLink="true">https://yaakai.to/blog/2023/develop-in-codespaces-and-devcontainer/</guid><pubDate>Sat, 06 May 2023 00:00:00 GMT</pubDate><content:encoded>&lt;p&gt;&lt;a href=&quot;https://github.com/features/codespaces&quot;&gt;Codespaces&lt;/a&gt; が利用できるようになってから、ちょっとした開発は Codespaces を行っていたのですが、最近は個人の開発はすべて Codespaces で十分だと思えてきたので完全に移行しようと思いました。
ただ、基本的には無料枠でやっているので&lt;sup&gt;&lt;a href=&quot;#user-content-fn-1&quot;&gt;1&lt;/a&gt;&lt;/sup&gt;、ある程度腰を据えて開発していると枠が足りなくなるのと、Chrome 拡張の開発は毎回成果物のダウンロードが必要だったりで不便なところは少しあるので、それを埋めるために一部で &lt;a href=&quot;https://code.visualstudio.com/docs/devcontainers/containers&quot;&gt;Dev Container&lt;/a&gt; を使って、手元のマシン内で開発可能な環境を整えることにしました。&lt;/p&gt;
&lt;p&gt;これは、その準備をしたときの作業ログと未来の自分に向けたメモです。ある程度 Codespaces を触ったことがあるという前提で書かれています。&lt;/p&gt;
&lt;h2&gt;開発用コンテナと Dockfile について&lt;/h2&gt;
&lt;p&gt;Codespaces と Dev Container で使用するコンテナに関する設定を区別する必要なく、同じものが使えます。
Docker Desktop に相当するものがインストールされていれば、VSCode に Dev Containers のプラグインを入れればそのまま利用可能です。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://marketplace.visualstudio.com/items?itemName=ms-vscode-remote.remote-containers&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Dev&amp;amp;#32;Containers&amp;amp;#32;-&amp;amp;#32;Visual&amp;amp;#32;Studio&amp;amp;#32;Marketplace&lt;/h3&gt;&lt;p&gt;Extension&amp;amp;#32;for&amp;amp;#32;Visual&amp;amp;#32;Studio&amp;amp;#32;Code&amp;amp;#32;-&amp;amp;#32;Open&amp;amp;#32;any&amp;amp;#32;folder&amp;amp;#32;or&amp;amp;#32;repository&amp;amp;#32;inside&amp;amp;#32;a&amp;amp;#32;Docker&amp;amp;#32;container&amp;amp;#32;and&amp;amp;#32;take&amp;amp;#32;advantage&amp;amp;#32;of&amp;amp;#32;Visual&amp;amp;#32;Studio&amp;amp;#32;Code&amp;amp;#39;s&amp;amp;#32;full&amp;amp;#32;feature&amp;amp;#32;set.&lt;/p&gt;&lt;span&gt;marketplace.visualstudio.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;ローカルで開発するときはコードを適当な場所に clone して「Open in container」から開きます。コマンドパレットからも開き直せます、その場合は Reopen ~ になります。&lt;/p&gt;
&lt;h3&gt;Clone Repository in Container&lt;/h3&gt;
&lt;p&gt;コードの clone をコンテナの中で行うという方法もあるようです。これを利用すると Codespace とほとんど同じ感覚で使えるようです。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://weseek.co.jp/tech/4112/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;【vscode】devcontainerのClone in container volumeがよいという話&lt;/h3&gt;&lt;p&gt;今回は、 vscode で devcontainer を使った開発環境について、 「Clone repository in container volume」を使うとうれしい点を取り上げます。&lt;/p&gt;&lt;span&gt;weseek.co.jp&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://code.visualstudio.com/docs/devcontainers/create-dev-container#_add-an-open-in-dev-container-badge&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Create a Dev Container&lt;/h3&gt;&lt;p&gt;Create a development container using Visual Studio Code Remote Development&lt;/p&gt;&lt;span&gt;code.visualstudio.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;ただ、何回試してみたのですが、shallow clone になってしまってだるいのと、成果物がコンテナ内に入ってしまうので取り出すのがめんどくさい、という理由があって使っていません。&lt;/p&gt;
&lt;h3&gt;OrbStack&lt;/h3&gt;
&lt;p&gt;個人の MBA では Docker Desktop の代わりに &lt;a href=&quot;https://orbstack.dev/&quot;&gt;OrbStack&lt;/a&gt; を試しています。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://orbstack.dev/&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;OrbStack · Fast, light, simple Docker &amp;amp;amp; Linux&lt;/h3&gt;&lt;p&gt;Say goodbye to slow, clunky containers and VMs. The fast, light, and easy way to run containers and Linux. Develop at lightspeed with our Docker Desktop alternative.&lt;/p&gt;&lt;span&gt;orbstack.dev&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://zenn.dev/daifukuninja/articles/6285b5491a05e5&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;zenn.dev&lt;/h3&gt;&lt;span&gt;zenn.dev&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;いまのところ特に問題なく Docker Desktop の代わりとして使えています。&lt;/p&gt;
&lt;h2&gt;Dotfiles について&lt;/h2&gt;
&lt;p&gt;ここ数年は fish を使っていたのですが、devcontainers のものにはデフォルトでは入っていないので、これを機に zsh に戻してみることにしました。
これまで Codespaces を使うときは不便を感じつつも特にカスタマイズをしていなかったので、今回ちゃんと設定を整えてみることにしました。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/yaakaito/env&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;GitHub - yaakaito/env&lt;/h3&gt;&lt;p&gt;Contribute to yaakaito/env development by creating an account on GitHub.&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;開発に使う devcontainer をある程度テンプレ化しておきたかったので&lt;sup&gt;&lt;a href=&quot;#user-content-fn-2&quot;&gt;2&lt;/a&gt;&lt;/sup&gt; dotfiles ではなくて env という名前にしてみました。&lt;/p&gt;
&lt;p&gt;Codepsaces の場合は、GitHub の設定から dotfiles のリポジトリを指定することで、Codespaces が起動するときに dotfiles の内容がコンテナにコピーされ、setup.sh が実行されます。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://docs.github.com/ja/codespaces/customizing-your-codespace/personalizing-github-codespaces-for-your-account#turning-on-settings-sync-in-a-codespace&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;アカウントGitHub Codespaces のカスタマイズ&lt;/h3&gt;&lt;span&gt;docs.github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;Dev Container の場合は、VSCode の設定から GitHub にある dotfiles のリポジトリを指定ことができます。これをすると Codespaces と同等の挙動になります。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;dotfiles.repository&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;yaakaito/env&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;  &quot;dotfiles.targetPath&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;~/.env&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h3&gt;zsh のカスタマイズ&lt;/h3&gt;
&lt;p&gt;とはいえ欲しい物が fish っぽく動いてほしいくらいだったので、次の記事を参考に &lt;a href=&quot;https://github.com/zsh-users/zsh-autosuggestions&quot;&gt;zsh-autosuggestions&lt;/a&gt; と &lt;a href=&quot;https://github.com/zsh-users/zsh-syntax-highlighting/&quot;&gt;zsh-syntaxhighlight&lt;/a&gt; を入れて、fish ライクに、peco で ctrl-r に履歴検索、ctrl-s でディレクトリ移動をバインドしています。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://dev.to/krish_agarwal/setup-guide-for-zsh-in-github-codespaces-5152&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Setup guide for ZSH in GitHub codespaces&lt;/h3&gt;&lt;p&gt;Setup ZSH   This post will guide you through to set up starship along with zsh suggestions...&lt;/p&gt;&lt;span&gt;dev.to&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://zenn.dev/oreo2990/articles/13c80cf34a95af&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;zenn.dev&lt;/h3&gt;&lt;span&gt;zenn.dev&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://qiita.com/keisukee/items/9b815e56a173a281f42f&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;pecoを使ったらターミナルの操作が劇的に効率化できた話 - Qiita&lt;/h3&gt;&lt;p&gt;はじめに pecoが最強。便利すぎてヤバイよって話です。 ターミナルの操作を限りなく高速化したいみなさんに本記事を捧げます。peco童貞の方にとっては、控えめに言っても人生が変わることになる1記事になるでしょう。自信作です。 駆け出しエンジニアだった昔の自分に会ったら、...&lt;/p&gt;&lt;span&gt;qiita.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;他には pnpm などの補完がほしいところですが、これは使うコンテナによって必要なものが違うので、Dockerfile の方で有効にしています。例えば pnpm の場合は以下のような感じです。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://pnpm.io/completion&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Command line tab-completion&lt;/h3&gt;&lt;span&gt;pnpm.io&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;USER&lt;/span&gt;&lt;span&gt; vscode&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;RUN&lt;/span&gt;&lt;span&gt; corepack prepare pnpm@8.2.0 --activate&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;RUN&lt;/span&gt;&lt;span&gt; pnpm install-completion zsh&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;Copilot や VSCode の拡張機能について&lt;/h2&gt;
&lt;p&gt;これまではあまり意識せずに Copilot を Codespaces にインストールして使っていたのですが、これを設定している途中で、コンテナ内で開発する際に Copilot が有効にならないことがあることに気づきました。これは Codespaces に限らず、Dev Container でも同じでした。他にもいくつかの拡張機能が動作しないことがあったのですが、これは container.json に設定を追加することで解決しました。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://docs.github.com/ja/codespaces/codespaces-reference/using-github-copilot-in-github-codespaces&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;GitHub Codespaces でのGitHub Copilotの使用&lt;/h3&gt;&lt;span&gt;docs.github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;div&gt;&lt;a href=&quot;https://containers.dev/implementors/spec/#implementation-specific-steps&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;Development Container Specification&lt;/h3&gt;&lt;p&gt;The purpose of the Development Container Specification is to provide a way to enrich containers with the content and metadata necessary to enable development...&lt;/p&gt;&lt;span&gt;containers.dev&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;&lt;code&gt;customizations.vscode.extensions&lt;/code&gt; フィールドを設定することで、コンテナで有効にする VSCode の拡張機能を指定することができるようです。これを使って Copilot や必要な拡張を有効にすることにしました。&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;{&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;build&quot;&lt;/span&gt;&lt;span&gt;: { &lt;/span&gt;&lt;span&gt;&quot;dockerfile&quot;&lt;/span&gt;&lt;span&gt;: &lt;/span&gt;&lt;span&gt;&quot;Dockerfile&quot;&lt;/span&gt;&lt;span&gt; },&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    &quot;customizations&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        &quot;vscode&quot;&lt;/span&gt;&lt;span&gt;: {&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            &quot;extensions&quot;&lt;/span&gt;&lt;span&gt;: [&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                &quot;github.copilot&quot;&lt;/span&gt;&lt;span&gt;,&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;                &quot;astro-build.astro-vscode&quot;&lt;/span&gt;&lt;span&gt; // 例えば Astro を使う場合&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;            ]&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;        }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;    }&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;h2&gt;今回やらなかったがやりたいこと&lt;/h2&gt;
&lt;p&gt;Codespaces や Dev Container を使った開発では、シェルの history が残らないため、永続化したい場合は別途手段を用意する必要があるようです。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://zenn.dev/hankei6km/articles/persist-command-history-in-github-codesapces&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;zenn.dev&lt;/h3&gt;&lt;span&gt;zenn.dev&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;history が永続化できるとそもそも不要かもしれませんが、cheat を使ったスニペットを env リポジトリに含めて置けると便利そうです。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://github.com/cheat/cheat&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;GitHub - cheat/cheat: cheat allows you to create and view interactive cheatsheets on the command-line. It was designed to help remind *nix system administrators of options for commands that they use frequently, but not frequently enough to remember.&lt;/h3&gt;&lt;p&gt;cheat allows you to create and view interactive cheatsheets on the command-line. It was designed to help remind *nix system administrators of options for commands that they use frequently, but not ...&lt;/p&gt;&lt;span&gt;github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;h2&gt;2023-05-14 追記&lt;/h2&gt;
&lt;p&gt;しばらく触っていなかったリポジトリを同じ要領で触ってみたら、DevContainer 側で VSCode の Extention のインストールが全く進まずに困っていたのですが、Dockerfile が原因でした。
かなり前に公式を参考に devcontainer.json と Dockerfile を作った際に、その Dockerfile はこんな感じに定義されていて、&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;ARG VARIANT=bullseye&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;FROM --platform=linux/amd64 mcr.microsoft.com/vscode/devcontainers/base:0-${VARIANT}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;linux/amd64&lt;/code&gt; を指定していると M2 Mac 上のコンテナに Extention がインストールできなくなるようでした。platform は指定しなければ自動で選択してくれるので、&lt;/p&gt;
&lt;pre&gt;&lt;code&gt;&lt;span&gt;&lt;span&gt;ARG VARIANT=bullseye&lt;/span&gt;&lt;/span&gt;
&lt;span&gt;&lt;span&gt;FROM mcr.microsoft.com/vscode/devcontainers/base:0-${VARIANT}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;
&lt;p&gt;に変更して無事動くようになりました。当時参考にしたドキュメントが見つからなかったんですが、現在のものは platform を指定していないので問題なさそうです。&lt;/p&gt;
&lt;div&gt;&lt;div&gt;&lt;div&gt;&lt;a href=&quot;https://docs.github.com/ja/codespaces/setting-up-your-project-for-codespaces/adding-a-dev-container-configuration/introduction-to-dev-containers&quot; target=&quot;_blank&quot;&gt;&lt;div&gt;&lt;h3&gt;開発コンテナーの概要&lt;/h3&gt;&lt;span&gt;docs.github.com&lt;/span&gt;&lt;/div&gt;&lt;/a&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;
&lt;p&gt;そもそもトレンドとしては Dockerfile は使わず、devcontainer.json だけで完結するのがよいようにも思います。&lt;/p&gt;
&lt;section&gt;&lt;h2&gt;Footnotes&lt;/h2&gt;
&lt;ol&gt;
&lt;li&gt;
&lt;p&gt;追加することに抵抗があるわけではないので、理由がなければ Codespaces を継続してます。 &lt;a href=&quot;#user-content-fnref-1&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;li&gt;
&lt;p&gt;テンプレートリポジトリはリポジトリが増えるし同じようなことをそれぞれに更新かけたりがだるいので。 &lt;a href=&quot;#user-content-fnref-2&quot;&gt;↩&lt;/a&gt;&lt;/p&gt;
&lt;/li&gt;
&lt;/ol&gt;
&lt;/section&gt;</content:encoded></item></channel></rss>