開発
English | 日本語
Yosegi 自体の開発について。monorepo の構成、開発中に叩くコマンド、公開前の検証方法を扱います。 コントリビューションの作法は CONTRIBUTING.md(英語)にあります。
パッケージ構成(Bun workspaces の monorepo)
packages/core—@yosegi/core。 フレームワーク非依存で、依存は zod のみ。.… domain(Screen JSON のスキーマ、Validator、合成プリミティブ、候補提示)./app… application(Composer / Service / Repository / ActorContext / 実装コンテキスト)./emit… Screen JSON → CSF(.stories.tsx)./registry… Storybook の index.json → Registry の正規化./testing… テスト用フィクスチャ
packages/server—@yosegi/yosegi。CLI / MCP / HTTP(Hono) アダプタと永続化。core の薄いラッパー。bin/yosegi.jsが唯一のコマンド入口。src/registry/source-registry.ts… TypeScript の型からの Registry 生成src/importer/story-importer.ts… Story(AST)→ Screen JSON
公開名が @yosegi/yosegi なのは、利用者がインストールするただ 1 つのパッケージであり yosegi の bin を持つためです。 ディレクトリ名が server のままなのは、配布物ではなくレイヤを表す名前だからです。 型抽出(react-docgen-typescript)と AST 解析はこちらに置き、core は zod のみに保ちます。
Agent Skill の置き場所
正となるのはリポジトリルートの skills/yosegi/(SKILL.md と、そこから開かれる references/)で、編集してよいのはここだけです。npx skills add <owner>/<repo> 系のインストーラはリポジトリ内の skills/<name>/SKILL.md を探すので、この場所から動かせません。
@yosegi/yosegi にも同梱します。node_modules からコピーしたい利用者向けです。files はパッケージディレクトリの外へ届かないため、packages/server/scripts/sync-skills.ts がルートの skills/ を packages/server/skills/ へコピーします。 これは生成物であり gitignore 対象で、bun run build とパッケージの prepack の両方が更新します。files は .gitignore に優先するので、追跡されていなくても tarball には入り、公開されたコピーが古くなることはありません。
bun run sync:skills # ミラーを更新する(リポジトリルートから)
bun --filter '@yosegi/yosegi' sync:skills:check # 書き換えずに差分だけ報告するpackages/server/skills/ は絶対に編集しません。 次の同期で捨てられます。
コマンド
bun install
bun test # 全パッケージ、その後 scripts/
bun typecheck
bun lint # 自動修正は bun lint:fix
bun run build # @yosegi/core → @yosegi/yosegi の依存順
bun run pack # リリースが公開する tarball を、検証付きで作る各パッケージは tsc で dist/(JS と .d.ts)を出し、package.json の exports はそこを指します。 開発中は各パッケージの tsconfig.json の paths が @yosegi/* をソースへ解決するので、ビルド無しでも bun test と tsc が通ります。
scripts/ は workspaces の外にあるため bun --filter は届きません。 ルートの bun test と bun typecheck が拾います。
CI(.github/workflows/ci.yml)は push・Pull Request・週次で lint / test / typecheck / build を回します。
依存バージョンの管理
複数パッケージが使うバージョンはルート package.json の catalog に一度だけ書き、各パッケージは "catalog:" で参照します。 現在は zod だけです(core と server)。1 パッケージしか使わない依存はそのパッケージに置きます。
公開パッケージが利用者へ露出する依存は、固定ではなくレンジにします。zod は core の .d.ts に構造的な形で現れ利用者側のコピーと単一化する必要があるため、typescript はホストが既に持っており固定すると 23MB のコピーがもう 1 つ入れ子になるためです。 ルートの typescript は devDependency として固定のままです。dist を作るコンパイラそのものだからです。
bunfig.toml は install.linker = "isolated" を設定しており、hoist されない node_modules になります。 各パッケージは自分が宣言したものしか見えません。hoist された配置では、他が引き込んでいる限り未宣言の依存も解決してしまい、その間違いは、利用者が公開された tarball を当の依存を持たないツリーへインストールした時点で初めて表面化します。
この linker は、ここでは import していない 5 つのパッケージをルートが宣言している理由でもあります。@braintree/sanitize-url・cytoscape・cytoscape-cose-bilkent・dayjs・debug の 5 つです。 いずれも mermaid の依存で、vitepress-plugin-mermaid がこれらを Vite の optimizeDeps.include に入れます。 その解決はルートから行われます。 これらが無いと docs:dev は起動しますが図がすべて空になります。docs:build は影響を受けないので、確認は図のあるページを開いて行います。
このリポジトリからホストに対して CLI を動かす
bun --filter '@yosegi/yosegi' cli <command>cwd が packages/server になるので相対パスはその分ずれます。bun run build && node packages/server/bin/yosegi.js <command> ならビルド成果物のほうを叩けます。 公開された yosegi コマンドが実際に動かすのはこちらです。
bin/yosegi.js はパッケージの exports を経由せず dist/adapters/cli/cli.js を直接 import します。 公開 API は HTTP アダプタと MCP サーバも再 export しているので、経由すると CLI を 1 回叩くたびに hono と MCP SDK まで読み込まれるためです。
shebang は node で、利用者に必要なのは Node.js 22 以上だけです。 これが成り立つのは、src/ の相対 import が明示的に .ts 拡張子を持ち、ビルド用 tsconfig が rewriteRelativeImportExtensions を設定しているからです。dist は Node の ESM リゾルバが要求する .js 拡張子を持つ形になります。 どちらか片方でも欠けると Bun でしか読めない成果物になり、それを捕まえるのが node-consumer の CI ジョブです。
公開前の検証
bun run build が通っても、公開されるものが実際に動く保証にはなりません。files の指定により tarball は各パッケージの一部しか含まないためです。workspace の外から検証します。
bun run pack <tmp> # tarball のパスを公開順に出力する
cd <a scratch project outside this repo>
npm install <tmp>/yosegi-core-0.1.0.tgz <tmp>/yosegi-yosegi-0.1.0.tgztarball を作る手段は bun run pack(scripts/pack.ts)だけで、CI とリリースワークフローもこれを通ります。 パッケージのディレクトリで npm publish してはいけません。npm は Bun の catalog: を解決できずリテラル文字列のまま固め、利用者の install がすべて EUNSUPPORTEDPROTOCOL で落ちます(npm publish --dry-run は警告しません)。 スクリプトは catalog: や workspace: が残った tarball、および自身の exports / bin / main / types が指すファイルを含まない tarball の出力を拒否します。
インストールは Bun ではなく npm で行います。 利用者に必要なのは Node だけなので、Bun で入ることは利用者が入れられることの証明になりません。 この経路は node-consumer の CI ジョブが毎 push で通しているため、手でやるのはパッケージング自体を変更したときだけでかまいません。
検証中のバージョンが npm に無いあいだ、この install は失敗します。server の tarball が @yosegi/core を厳密なバージョンで要求し、npm レジストリが 404 を返すためです。 検証のあいだだけローカルの tarball を指します。
"overrides": { "@yosegi/core": "file:<tmp>/yosegi-core-0.1.0.tgz" }そのうえで、作業用プロジェクト側で次を確認します。
@yosegi/coreとそのサブパス(/app・/emit・/registry)が import でき、型も解決します。node ./node_modules/.bin/yosegiが動き usage を出します(引数なしでは終了コード 1 になりますが、それは usage エラーです。重要なのは Node がdistを読めたことです)。node_modules/@yosegi/yosegi/skills/yosegi/にSKILL.mdとreferences/の両方があります。references が欠けた Skill は使い物になりません。node_modules/@yosegi/yosegi/package.jsonの@yosegi/core依存はworkspace:*でなく公開するバージョンになっています。zodもcatalog:でなく実際のバージョンになっています。
最後の 2 つは毎回確認する価値があります。bun pm pack は workspace:* と catalog: の両方を実際のバージョンへ置換します。 ただし、その値を取るのは package.json ではなく bun.lock からです。 バージョンを上げたりルートの catalog を書き換えたりしても bun install を回していなければ、古い値、あるいは存在しないバージョンがそのまま固められ、何の警告も出ません。 だから packages/server/package.json の @yosegi/core は明示的なバージョンに固定し、bun.lock はバージョンや catalog の変更と同じコミットで更新します。
バージョニング
1.0 未満のあいだは、マイナーバージョンにも破壊的変更が入り得ます。2 つのパッケージはバージョンを揃えて上げ、@yosegi/yosegi は厳密に一致するバージョンの @yosegi/core を要求します。
公開
.github/workflows/release.yml が v* タグで両パッケージを公開します。npm の認証は Trusted Publishing(OIDC)で行うため、このリポジトリに npm のトークンは一切無く、今後も置いてはいけません。publish ジョブの id-token: write 権限が認証手段のすべてです。 あわせて provenance の証明書も生成されます。 公開された tarball がこのリポジトリのそのコミットから来たことを誰でも検証できるのはこれによります。
リリースは npm への公開だけです。 ワークフローは GitHub Release を作らず、リリースノートも生成しません。 記録として残るのはタグとコミット履歴です。
初回のみのセットアップ(オーナーのみ)
以下はリポジトリからは実行できません。 スコープに対する権限を持つ npm アカウントが必要になります。
npm に
yosegiorganization(スコープ)を作ります。 スコープ付きパッケージは既定で restricted なので、両パッケージともpublishConfig.accessをpublicにしています。GitHub リポジトリを public にします。provenance が生成されるのは、public リポジトリが public パッケージを公開する場合だけです。
各パッケージごとに Trusted Publisher を設定します。
https://www.npmjs.com/package/@yosegi/core/accessと、@yosegi/yosegiの同じページ:- Organization or user:
yosegi-dev - Repository:
yosegi - Workflow filename:
release.yml - Allowed actions:
npm publish(2026-05-20 以降に作った設定では明示的に選ぶ必要があります。それ以前は既定で有効でした)
このページはパッケージ単位なので、パッケージが存在して初めて現れます。 一度も公開されていない名前に Publisher を設定できない場合は、
0.1.0だけ手で公開します(bun run packしてから、core・@yosegi/yosegiの順にnpm publish <tarball>)。 そのうえで Trusted Publisher を設定し、次のリリースからワークフローに任せます。 いずれにせよ core を先に公開します。- Organization or user:
各リリース
バージョンが記録されている箇所をまとめて上げ、1 つのコミットにします。
対象 場所 各パッケージ自身のバージョン 両方の package.jsonのversion両者のあいだの固定 packages/server/package.jsonの@yosegi/core依存bun pm packが置換に使う値bun install経由でbun.lockこれらがタグと食い違っているとワークフローは公開を拒否します。 ソースにバージョンを書いた箇所はありません。
yosegiVersion()(packages/server/src/config.ts)がpackage.jsonを読みます。CLI の--version、Registry のbuiltWith、MCP サーバのinitializeの応答は、すべてこれを通ります。 新しいリテラルが増えていないかがレビューで見る点です。skills/yosegi/SKILL.mdはバージョンではなく日付を持ちますが、これはリリースではなく Skill の内容を最後に変えた時点を指します。コミットし、タグを打って push します:
shgit tag v0.2.0 git push origin v0.2.0
ワークフローはまず lint / test / typecheck / build を回し、そのうえで core、続いて server を公開します。 順序は重要で、server は core を厳密なバージョンで要求するため、2 つの publish の間に入った install は解決に失敗します。