1
0
mirror of https://github.com/misskey-dev/misskey.git synced 2026-07-25 12:05:19 +02:00

Tighten user entity test types

This commit is contained in:
mattyatea
2026-06-29 22:23:26 +09:00
parent a4a3606435
commit 2debf09bf2
13 changed files with 1620 additions and 16 deletions

View File

@@ -0,0 +1,253 @@
---
name: add-api-endpoint
description: Misskey の REST API エンドポイント (/api/<category>/<name>) を NestJS DI + meta/paramDef 規約で追加する。バックエンドに新しい API ルートを足す時に必ず使う。endpoint-list.ts への手動登録、e2e テスト、misskey-js 再生成、CHANGELOG までの一連の手順を含む。
---
# Misskey API エンドポイント追加スキル
`packages/backend/src/server/api/endpoints/<category>/<name>.ts` に新規エンドポイントを追加するためのワークフロー。**手順 4 (endpoint-list.ts 登録) を忘れると 404 になる** 点に最大の注意を払う。
## 最重要事実 (見落とすと壊れる)
1. エンドポイントは **glob 自動収集されない**。[packages/backend/src/server/api/endpoint-list.ts](../../../packages/backend/src/server/api/endpoint-list.ts) への 1 行追加が必須。
2. `meta` / `paramDef` を変えたら **misskey-js の再生成が必須**`pnpm build-misskey-js-with-types` を忘れると CI の `check-misskey-js-autogen` で必ず落ちる。
3. `meta.errors` の各 `id`**UUID**。重複させない (既存全 UUID と衝突確認)。
## ステップ 1: ファイル配置と SPDX
`packages/backend/src/server/api/endpoints/<category>/<name>.ts` に新規作成する。`<category>` は機能領域 (例: `notes`, `users`, `admin/announcements`)。
冒頭に SPDX ヘッダーを必ず付ける:
```ts
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
```
## ステップ 2: 最小テンプレート (シンプル read 系)
[endpoints/ping.ts](../../../packages/backend/src/server/api/endpoints/ping.ts) をベースに書く。認証不要・パラメータなし・小さなレスポンスの例:
```ts
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
export const meta = {
tags: ['<tag>'],
requireCredential: false,
res: {
type: 'object',
optional: false, nullable: false,
properties: {
// ...
},
},
} as const;
export const paramDef = {
type: 'object',
properties: {},
required: [],
} as const;
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
) {
super(meta, paramDef, async (ps, me) => {
// 実装
});
}
}
```
## ステップ 3: 認証付き / DI / errors を含むテンプレート
[endpoints/notes/create.ts](../../../packages/backend/src/server/api/endpoints/notes/create.ts) を参照する。要点:
```ts
import { Inject, Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { ApiError } from '@/server/api/error.js';
import { DI } from '@/di-symbols.js';
// import ms from 'ms'; // limit.duration に ms('1hour') 等を渡すとき (default import)
export const meta = {
tags: ['notes'],
requireCredential: true, // 認証必須なら true
prohibitMoved: false, // moved user を拒否するか
kind: 'write:notes', // OAuth scope (requireCredential 時に必須)
limit: {
duration: 3600000, // ms('1hour')
max: 300,
},
errors: {
noSuchNote: {
message: 'No such note.',
code: 'NO_SUCH_NOTE',
id: 'xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx', // ★ UUID v4 を必ず生成 (`x`=hex, `y`=8/9/a/b)。下の「UUID 生成」を参照
},
},
res: {
type: 'object',
optional: false, nullable: false,
ref: 'Note', // packed entity に揃える場合
},
} as const;
export const paramDef = {
type: 'object',
properties: {
noteId: { type: 'string', format: 'misskey:id' },
},
required: ['noteId'],
} as const;
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
@Inject(DI.notesRepository)
private notesRepository: NotesRepository,
) {
super(meta, paramDef, async (ps, me) => {
const note = await this.notesRepository.findOneBy({ id: ps.noteId });
if (note == null) throw new ApiError(meta.errors.noSuchNote);
// 実装
});
}
}
```
### meta フィールド早見表
| フィールド | 用途 |
|---|---|
| `tags` | OpenAPI タグ (機能領域) |
| `requireCredential` | 認証必須か |
| `requireModerator` / `requireAdmin` | 権限制限 |
| `prohibitMoved` | アカウント移行済ユーザーを拒否 |
| `kind` | OAuth scope (`read:notes` / `write:notes` 等)。`requireCredential: true` 時必須 |
| `limit` | レート制限 (`{ duration, max, key?, minInterval? }`) |
| `errors` | エラー定義。各要素に `message` / `code` / `id` (UUID v4) 必須 |
| `res` | JSON Schema or `ref: '<EntityName>'` (packed entity 参照) |
| `requireFile` | ファイルアップロード必須 |
| `secure` | secure cookie 必要 |
| `allowGet` | GET メソッド許可 |
| `cacheSec` | レスポンスキャッシュ秒数 |
| `description` | OpenAPI 説明 |
詳細は [endpoints.ts](../../../packages/backend/src/server/api/endpoints.ts) の型定義 (lines 11-125) を参照。
### paramDef の特殊フォーマット
JSON Schema (AJV) ベースだが、Misskey 拡張を使える:
- `format: 'misskey:id'` — ID 文字列バリデーション
- `allOf` / `anyOf` / `oneOf` — 複合条件
- `default` — デフォルト値
詳細は [endpoint-base.ts](../../../packages/backend/src/server/api/endpoint-base.ts) を参照。
### エラー throw
**「公開 API エラーとして API クライアントに返したいもの」は必ず `throw new ApiError(meta.errors.<key>)` を使う**。`meta.errors` に列挙した上で `ApiError` でラップしないと、misskey-js 側の型情報に出ず、レスポンスも 500 になる。第 2 引数で追加情報を渡せる:
```ts
throw new ApiError(meta.errors.invalidParam, { reason: 'too short' });
```
一方で、**想定外の例外 (DB 不整合 / 下層サービスの bug など) を握り潰すために `try/catch``ApiError` に変換するのは避ける**。既存 endpoint も「期待される業務エラーは `ApiError` に変換し、それ以外は `throw err;` で再 throw する」という二段構えになっている。`packages/backend/src/server/api/endpoints/notes/create.ts``catch` 節 (末尾の `throw err;`) を参照。生の `throw` を全面禁止すると未知例外も 200 で潰れて debug が困難になるので、このバランスを保つ。
詳細は [error.ts](../../../packages/backend/src/server/api/error.ts) の `ApiError` クラスを参照。
### UUID 生成
```bash
node -e "console.log(crypto.randomUUID())"
```
その UUID が他のエンドポイントの `id` と衝突していないか必ず確認:
```bash
grep -r "id: '<生成した UUID>'" packages/backend/src/server/api/endpoints/
```
## ステップ 4: ★必須 — endpoint-list.ts に登録
[packages/backend/src/server/api/endpoint-list.ts](../../../packages/backend/src/server/api/endpoint-list.ts) の同カテゴリ末尾に 1 行追加する(既存の並びを崩さない):
```ts
export * as '<category>/<name>' from './endpoints/<category>/<name>.js';
```
ファイル冒頭のコメント (`When you add new endpoint, you should add it to this file.`) の通り、このリストが API ルーティングの単一の真実。**忘れると 404**。
`EndpointsModule.ts` がこのファイルの全エクスポートを `Object.entries()` で反復し、NestJS provider (`provide: 'ep:<path>'`) を生成する。
## ステップ 5: e2e テスト追加
[packages/backend/test/e2e/endpoints.ts](../../../packages/backend/test/e2e/endpoints.ts) に対応する `describe` / `test` を追加する。`api()` ヘルパーで叩く:
```ts
describe('<category>/<name>', () => {
test('正常系', async () => {
const res = await api('<category>/<name>', { /* params */ }, alice);
assert.strictEqual(res.status, 200);
});
});
```
実行: `pnpm --filter backend test:e2e`
## ステップ 6: misskey-js 再生成 (★必須)
`meta` / `paramDef` / `res` を変えたら必ず実行する:
```bash
pnpm build-misskey-js-with-types
```
これで以下が更新される:
- `packages/backend/built/api.json` (OpenAPI spec)
- `packages/misskey-js/generator/api.json`
- `packages/misskey-js/src/autogen/*.ts` (TypeScript 型)
PR に `packages/misskey-js/src/autogen/` 配下の差分が含まれていないと、CI の `check-misskey-js-autogen` で落ちる。
## ステップ 7: Lint と typecheck
```bash
pnpm --filter backend lint
```
(typecheck = `tsgo --noEmit` / ESLint = `eslint`)
## ステップ 8: CHANGELOG
ユーザー影響がある (新機能 / 既存挙動変更) なら、`CHANGELOG.md``## Unreleased``### Server` に 1 行追加する ([AGENTS.md §CHANGELOG](../../../AGENTS.md#changelog) 参照):
```
- Feat: /api/<category>/<name> を追加
```
純粋なリファクタや内部用なら不要。
## 参照ファイル
- [endpoints.ts (meta/paramDef 型定義)](../../../packages/backend/src/server/api/endpoints.ts)
- [endpoint-base.ts (Endpoint 基底クラス)](../../../packages/backend/src/server/api/endpoint-base.ts)
- [endpoint-list.ts (★ ここに登録)](../../../packages/backend/src/server/api/endpoint-list.ts)
- [error.ts (ApiError)](../../../packages/backend/src/server/api/error.ts)
- [endpoints/ping.ts (最小例)](../../../packages/backend/src/server/api/endpoints/ping.ts)
- [endpoints/notes/create.ts (DI + errors の典型)](../../../packages/backend/src/server/api/endpoints/notes/create.ts)
- [test/e2e/endpoints.ts (テスト例)](../../../packages/backend/test/e2e/endpoints.ts)
- [scripts/generate_api_json.js (misskey-js 生成元)](../../../packages/backend/scripts/generate_api_json.js)

View File

@@ -0,0 +1,117 @@
---
name: add-i18n-key
description: Misskey の i18n キーを追加・修正する。locales/ja-JP.yml のみ編集可能で、他言語ファイル (en-US.yml 等 39 言語) は Crowdin の自動配信先のため絶対に触らない。型は packages/i18n が ja-JP.yml から自動再生成する。frontend からは i18n.ts.<key> または i18n.tsx.<key>(...) で参照する。
---
# Misskey i18n キー追加スキル
UI 文言の追加・変更を行う際の規約。**手動編集して良いのは `locales/ja-JP.yml` のみ。**
## 大前提 (絶対 NG)
- **`locales/<lang>.yml` (ja-JP.yml 以外) の編集は禁止**。これらは Crowdin の自動配信先で、手動編集すると次の同期で上書き喪失する ([locales/README.md](../../../locales/README.md), [crowdin.yml](../../../crowdin.yml))。
- 文字列リテラルを SFC に直書きしない (`<span>こんにちは</span>` 等)。必ず `i18n.ts.<key>` を経由する。
- 既存キーの破壊的リネームは Crowdin 翻訳資産も道連れになるので慎重に。追加・改名併用 (新キー追加 → 移行 → 旧キー削除) を検討する。
## ステップ 1: ja-JP.yml にキーを追加
[locales/ja-JP.yml](../../../locales/ja-JP.yml) を編集する。YAML の階層構造を維持し、関連するセクションに配置する:
```yaml
# トップレベル単純キー
save: "保存"
# ネストしたカテゴリ (アンダースコア接頭辞は内部カテゴリ)
_settings:
general: "全般"
appearance: "外観"
# パラメータ付き (単純なプレースホルダ置換)
# ICU MessageFormat の plural / select / number / date などは非対応
# 使えるのは `{name}` のような単純な置換のみ
greeting: "こんにちは、{name}さん"
```
### 命名のお作法
- 単純キー: lowerCamelCase (例: `saveChanges`, `confirmDelete`)。
- カテゴリ: アンダースコア接頭辞 (例: `_settings`, `_abuseUserReport`)。
- 既存セクション内に置く場合はアルファベット順を維持する (新セクション全体を末尾に追加するのは可)。
## ステップ 2: 型定義の自動再生成
`packages/i18n/build.ts``ja-JP.yml` を解析し、TypeScript インターフェースを [packages/i18n/src/autogen/locale.ts](../../../packages/i18n/src/autogen/locale.ts) に出力する。
### 自動 (推奨)
`pnpm dev` 実行中なら、`packages/i18n` の watch スクリプトが yml の変更を検知して自動再生成する。
### 手動
```bash
pnpm --filter i18n generate
```
実体は `tsx scripts/generateLocaleInterface.ts`
### 失敗パターン
これを実行せずに frontend 側で `i18n.ts.<newKey>` を参照すると、`Locale` インターフェースに追加されていないため、typecheck で「Property '<newKey>' does not exist on type 'Locale'」というエラーになる。`pnpm --filter frontend lint` で発覚する。
## ステップ 3: frontend での参照
```ts
import { i18n } from '@/i18n.js';
```
| 用途 | 書き方 |
|---|---|
| 単純文字列 | `i18n.ts.save` |
| ネスト | `i18n.ts._settings.general` |
| パラメータ付き | `i18n.tsx.greeting({ name: userName })` |
| Vue テンプレート内 | `{{ i18n.ts.save }}` / `{{ i18n.tsx.greeting({ name }) }}` |
`i18n.ts` は型付き文字列、`i18n.tsx` は MessageFormat 関数。
## ステップ 4: 検証
```bash
# i18n パッケージの型再生成 + typecheck
pnpm --filter i18n lint
# frontend で新キー参照箇所の型チェック
pnpm --filter frontend lint
```
## 例: 「ノートを削除しますか?」確認ダイアログを追加する
1. `locales/ja-JP.yml`:
```yaml
_notes:
deleteConfirm: "このノートを削除しますか?"
```
2. `pnpm --filter i18n generate` (または `pnpm dev` で watch 中)
3. SFC:
```vue
<script setup lang="ts">
import { i18n } from '@/i18n.js';
import * as os from '@/os.js';
async function onDelete() {
const { canceled } = await os.confirm({
type: 'warning',
text: i18n.ts._notes.deleteConfirm,
});
if (canceled) return;
// 削除処理
}
</script>
```
## 参照ファイル
- [locales/README.md (★ 編集ポリシー根拠)](../../../locales/README.md)
- [locales/ja-JP.yml](../../../locales/ja-JP.yml)
- [packages/i18n/build.ts](../../../packages/i18n/build.ts)
- [packages/i18n/src/autogen/locale.ts (生成物)](../../../packages/i18n/src/autogen/locale.ts)
- [packages/frontend/src/i18n.ts](../../../packages/frontend/src/i18n.ts)

View File

@@ -0,0 +1,174 @@
---
name: add-mk-component
description: Misskey フロントエンドの新規 Vue 3 コンポーネントを追加する。Mk* 命名 / SPDX (HTML コメント) / <script setup lang="ts"> / <style lang="scss" module> / *.stories.impl.ts 併設の規約をまとめて適用する。新しい共有 UI コンポーネントを packages/frontend/src/components/ に作る時に使う。
---
# Misskey Vue コンポーネント追加スキル
`packages/frontend/src/components/` に新しい共有コンポーネントを追加するための規約。
## 大前提
- 共有 / 再利用コンポーネントは **必ず `Mk` プレフィックス** (例: `MkButton`, `MkInput`)。ページ固有部品など `Mk` プレフィックスでないものは原則 `pages/` 側に置く。
- 新規では `<style lang="scss" module>` (CSS Modules) を既定とする。古い `scoped` 形式が混在しているが、新規では使わない。
- 文字列リテラルの直書きは禁止。文言は必ず `i18n.ts.<key>` 経由で参照する (新キーは `add-i18n-key` スキルを参照)。
- `alert()` / `confirm()` / `window.prompt()` は使わない。`os.alert` / `os.confirm` / `os.popup` などを使う。
## ステップ 1: ファイル配置
`packages/frontend/src/components/Mk<Name>.vue` に新規作成する。
ストーリーが必要 (= ほぼ常に必要) なら、同階層に `Mk<Name>.stories.impl.ts` も作る。Storybook の規約は `*.stories.impl.ts` であって、`*.stories.ts` ではない。
## ステップ 2: SPDX ヘッダー (HTML コメント形式)
`.vue` ファイル冒頭に必須:
```html
<!--
SPDX-FileCopyrightText: syuilo and misskey-project
SPDX-License-Identifier: AGPL-3.0-only
-->
```
`/* ... */` (TS / JS 形式) ではなく **HTML コメント** で書くこと。既存の `.vue` ファイルがすべて HTML コメント形式を使っており、SFC の先頭として自然な形式に統一するため (CI の `spdx` ジョブはコメント形式ではなく SPDX 文字列の有無のみを検査する)。
## ステップ 3: 最小テンプレート
[MkInfo.vue](../../../packages/frontend/src/components/MkInfo.vue) をベースにする (シンプルな表示コンポーネント):
```vue
<!--
SPDX-FileCopyrightText: syuilo and misskey-project
SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
<div :class="$style.root">
<slot></slot>
</div>
</template>
<script lang="ts" setup>
const props = defineProps<{
variant?: 'primary' | 'secondary';
}>();
const emit = defineEmits<{
(ev: 'click'): void;
}>();
</script>
<style lang="scss" module>
.root {
padding: 12px 14px;
border-radius: var(--MI-radius);
background: var(--MI_THEME-panel);
}
</style>
```
### 規約ポイント
| 項目 | 規約 |
|---|---|
| `<script>` | `<script lang="ts" setup>`。型パラメータが必要なら `generic="T extends ..."` を付ける ([MkInput.vue 参照](../../../packages/frontend/src/components/MkInput.vue)) |
| `defineProps` / `defineEmits` | **type-only** (`<{ ... }>`) 形式。runtime の object 形式は使わない |
| `<style>` | `lang="scss" module` を既定。クラス参照は `:class="$style.foo"` |
| CSS 変数 | `var(--MI_THEME-...)` (テーマ) / `var(--MI-radius)` (UI 共通) — ハードコードしない |
| アイコン | Tabler icons のクラス (`<i class="ti ti-info-circle">`) を使う |
## ステップ 4: i18n と os の利用
```vue
<script lang="ts" setup>
import { i18n } from '@/i18n.js';
import * as os from '@/os.js';
async function onClick() {
const { canceled } = await os.confirm({
type: 'warning',
text: i18n.ts._notes.deleteConfirm,
});
if (canceled) return;
os.toast(i18n.ts.deleted);
}
</script>
```
### `os` の主なヘルパー (詳細は [os.ts](../../../packages/frontend/src/os.ts))
| 関数 | 用途 |
|---|---|
| `os.alert({ type, title?, text })` | 単方向アラート |
| `os.confirm({ type, title, text })` | yes/no 確認 (`{ canceled }` を返す) |
| `os.toast(message)` | 一時通知 |
| `os.popup(component, props, handlers)` | 任意コンポーネントの非同期ポップアップ |
| `os.popupMenu(items, anchor?)` | コンテキストメニュー |
| `os.form(title, fields)` | フォームダイアログ |
| `os.apiWithDialog(endpoint, data)` | API 呼出し + エラー時ダイアログ表示 |
## ステップ 5: Storybook ストーリー併設
[MkButton.stories.impl.ts](../../../packages/frontend/src/components/MkButton.stories.impl.ts) を雛形として参考にする。`.stories.impl.ts``packages/frontend/src/` 配下の `.ts` ファイルなので [AGENTS.md §1 SPDX ヘッダー必須](../../../AGENTS.md#1-spdx-ヘッダー必須) の対象であり、冒頭に SPDX ヘッダーを必ず付ける (HTML コメント形式ではなく `/* */` 形式)。形式 (以下の `MkXxx` は実際のコンポーネント名に置換する):
```ts
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
/* eslint-disable @typescript-eslint/explicit-function-return-type */
/* eslint-disable import/no-default-export */
import type { StoryObj } from '@storybook/vue3';
import MkXxx from './MkXxx.vue';
export const Default = {
render(args) {
return {
components: { MkXxx },
setup() {
return { args };
},
template: '<MkXxx v-bind="args">slot content</MkXxx>',
};
},
args: {
variant: 'primary',
},
parameters: {
layout: 'centered',
},
} satisfies StoryObj<typeof MkXxx>;
```
`Vue` SFC は default export なので、`import MkXxx from './MkXxx.vue';` のように名前付き import ではなく default import で書く。実行確認は `pnpm --filter frontend storybook-dev`
## ステップ 6: Lint と typecheck
```bash
pnpm --filter frontend lint
```
(typecheck = vue-tsc 等、ESLint = `@misskey-dev/eslint-plugin` 含む)
ESLint --fix をピンポイントで:
```bash
pnpm exec eslint --fix packages/frontend/src/components/Mk<Name>.vue
```
## ステップ 7: 既存コンポーネントとの整合性確認
- 似た用途の既存 `Mk*` コンポーネントを参考に、スタイルやプロップ命名を揃える。
- `_button` / `_panel` / `_selectable` などの **共通 utility class** (グローバルスタイルにある) を活用できるか確認する。
- 大きな機能なら、Storybook stories で各バリエーションを網羅する。
## 参照ファイル
- [MkInfo.vue (シンプル例)](../../../packages/frontend/src/components/MkInfo.vue)
- [MkButton.vue (汎用ボタン例)](../../../packages/frontend/src/components/MkButton.vue)
- [MkInput.vue (generics + 多機能例)](../../../packages/frontend/src/components/MkInput.vue)
- [MkButton.stories.impl.ts (Storybook 雛形)](../../../packages/frontend/src/components/MkButton.stories.impl.ts)
- [packages/frontend/src/os.ts](../../../packages/frontend/src/os.ts)
- [packages/frontend/src/i18n.ts](../../../packages/frontend/src/i18n.ts)

View File

@@ -0,0 +1,148 @@
---
name: context-budget
description: Codex セッションのコンテキスト窓消費を agents/skills/MCP/rules/AGENTS.md ごとに見える化し、肥大化と冗長コンポーネントを検出して節約候補を提示する。"コンテキスト消費を見せて"、"context budget"、"context audit"、"トークン内訳"、"これ以上 MCP 入る?" 等の発話で起動する。
---
<!--
SPDX-License-Identifier: MIT
SPDX-FileCopyrightText: 2026 Affaan Mustafa and everything-Codex contributors
出典 (upstream): https://github.com/affaan-m/everything-Codex (v2.0.0-rc.1)
upstream path: skills/context-budget/SKILL.md
upstream origin frontmatter: ECC
upstream license: MIT — https://github.com/affaan-m/everything-Codex/blob/main/LICENSE
project-level notice: see .Codex/THIRD_PARTY_LICENSES.md (Misskey 内サードパーティ一覧 + MIT 全文)
Imported into Misskey .Codex/ on 2026-05-10 as a standalone copy (no dependency on the ECC plugin runtime). description was rewritten in Japanese and a "Misskey 固有メモ" section was appended; body content remains MIT-licensed.
note: Misskey の skills/agents 数は少ないので、MCP / AGENTS.md / プラグイン由来の overhead が支配的になりやすい点に留意。
-->
# Context Budget
セッション内に読み込まれるコンポーネント (agents / skills / rules / MCP servers / AGENTS.md) の token overhead を分析し、空き context を回復する具体策を提示する。
## 使う場面
- セッションが重い・出力品質が落ちてきた感覚がある
- 直近で skills / agents / MCP server を多数追加した
- 残りの context headroom を知りたい
- 追加コンポーネントを入れる前に空きを確認したい
- 「context-budget」「token 内訳」等のキーワードでユーザーが明示的に要請した時 (Misskey リポジトリにはこの名前のスラッシュコマンドは登録していない — 本 skill は名前 / description マッチで auto-invoke される想定。実装済の slash command 一覧は [.Codex/commands/](../../commands/) を参照)
## 仕組み
### Phase 1: Inventory
各コンポーネントを走査して token を推定する。
**Agents** (`.Codex/agents/*.md`)
- 行数とトークン数 (`words × 1.3`) を計算
- frontmatter `description` の長さを抽出
- フラグ: 200 行超 (重い)、description 30 word 超 (frontmatter 肥大)
**Skills** (`.Codex/skills/*/SKILL.md`)
- SKILL.md ごとに token を計算
- フラグ: 400 行超
- `.agents/skills/` 等の重複コピーは除外
**Rules** (リポジトリルートの `AGENTS.md` + `.Codex/` から `@-import` されるファイル)
- ファイル単位で token 計算
- フラグ: 100 行超
- 同一言語モジュール内の内容重複を検出
**MCP Servers** (`.mcp.json` または有効 MCP 設定)
- server 数と総 tool 数
- schema overhead をツールあたり ~500 token で見積もる
- フラグ: 20 tool 超のサーバー、`gh` / `git` / `npm` 等の CLI を単純ラップしただけのサーバー
**AGENTS.md** (project + user-level)
- ファイルごとに token を計算
- フラグ: 合計 300 行超
### Phase 2: Classify
| バケット | 判定基準 | 行動 |
|--------------------|-------------------------------------------------------------|-----------------------------------|
| **Always needed** | AGENTS.md から参照されている / 有効コマンドの裏 / 現プロジェクトと一致 | 維持 |
| **Sometimes needed** | ドメイン依存 (例: 言語パターン)、AGENTS.md 参照なし | オンデマンド有効化を検討 |
| **Rarely needed** | コマンド参照なし、内容重複、明確な用途なし | 削除または lazy-load |
### Phase 3: Detect Issues
- **Bloated agent description** — frontmatter description が 30 word 超だと、Task ツール起動のたびに毎回ロードされる
- **Heavy agents** — 200 行超は Task ツールの context を毎回膨らませる
- **Redundant components** — agent ロジックを重複する skill、AGENTS.md と重複する rule
- **MCP over-subscription** — 10 server 超、または CLI 代用可能なサーバー
- **AGENTS.md bloat** — 冗長説明、古いセクション、rule に移すべき指示
### Phase 4: Report
```
Context Budget Report
═══════════════════════════════════════
Total estimated overhead: ~XX,XXX tokens
Context model: <現在モデル名> (<window>K window) ← 例: Codex Opus 4.7 (1M), Codex Sonnet (200K)
Effective available context: ~XXX,XXX tokens (XX%)
Component Breakdown:
┌─────────────────┬────────┬───────────┐
│ Component │ Count │ Tokens │
├─────────────────┼────────┼───────────┤
│ Agents │ N │ ~X,XXX │
│ Skills │ N │ ~X,XXX │
│ Rules │ N │ ~X,XXX │
│ MCP tools │ N │ ~XX,XXX │
│ AGENTS.md │ N │ ~X,XXX │
└─────────────────┴────────┴───────────┘
WARNING: Issues Found (N):
[token 節約量の降順]
Top 3 Optimizations:
1. [action] → save ~X,XXX tokens
2. [action] → save ~X,XXX tokens
3. [action] → save ~X,XXX tokens
Potential savings: ~XX,XXX tokens (XX% of current overhead)
```
verbose mode ではさらにファイルごとの token 内訳、最重ファイルの行単位ブレークダウン、重複行の対比、MCP tool 一覧 + tool ごとの schema サイズ推定を出す。
## 例
**基本監査**
```
User: コンテキスト消費を見せて
Skill: 16 agents (12,400 tokens), 28 skills (6,200), 87 MCP tools (43,500), 2 AGENTS.md (1,200)
Flags: 重い agent 3 個、CLI 代用可能な MCP 3 個
Top saving: MCP 3 個削除 → -27,500 tokens (overhead の 47% 削減)
```
**Verbose**
```
User: トークン内訳をファイル単位で
Skill: 上記レポートに加えて、planner.md (213 lines, 1,840 tokens) のような
per-file 行内訳、MCP tool ごとのサイズ、rule の重複行を side-by-side で表示
```
**追加前チェック**
```
User: MCP server を 5 個追加したいが、空きある?
Skill: 現状 33% → 5 server (≈ 50 tools) 追加で +25,000 tokens → 45% に到達
推奨: CLI 代用可能な server 2 個を先に外して 40% 以下を維持
```
## ベストプラクティス
- **トークン推定**: prose は `words × 1.3`、code 主体は `chars / 4`
- **MCP は最大のレバー**: tool あたり ~500 token、30-tool server ひとつで全 skill より大きい
- **agent description は常時ロード**: 呼ばれない agent でも description は毎 Task 投入
- **verbose は debug 用**: 普段は使わない
- **変更後は監査**: agent/skill/MCP 追加直後に走らせて creep を早期発見
## Misskey 固有メモ
- Misskey は MCP server をプロジェクトで明示登録していないため (`.mcp.json` 不在)、現状 overhead の支配項は AGENTS.md と公式プラグイン群の skills / agents description である。
- ECC プラグインがユーザースコープで `installed_plugins.json` に存在するため、プロジェクトで `enabledPlugins` に追加していなくても system reminder に 200+ skill が現れる。これらは description が短いので個別 overhead は小さいが、合計値の確認に本 skill を使う。

View File

@@ -0,0 +1,156 @@
---
name: create-migration
description: Misskey の TypeORM マイグレーションを公式 CLI (migration:generate / migration:create) で正しく生成し、SPDX ヘッダー付与・up/down 整合・check-migrations 確認まで誘導する。エンティティのスキーマ変更を含むあらゆる DB 変更、または手書き SQL によるデータ移行が必要な時に使用する。
---
# Misskey マイグレーション作成スキル
`packages/backend/migration/` に新規 TypeORM マイグレーションを追加するためのワークフロー。
## 大前提 (絶対 NG)
- **既にマージ済み (develop / master) のマイグレーションファイルを編集しない** ([AGENTS.md §3](../../../AGENTS.md#3-マージ済み-migration-を絶対に編集しない))。本番履歴の改変は深刻なデータ不整合を引き起こす。スキーマ変更は **常に新しいタイムスタンプで新規ファイル** を作る。
- ファイル名のタイムスタンプ部分を後から書き換えない (順序が壊れる)。
> 作り方は AGENTS.md §3 の「`Date.now()` で UNIX ms を取得 → `{ms}-{PascalName}.js` を手書き」が最低ライン。エンティティ差分から自動生成したい (= TypeORM の `migration:generate` を使う) 場合は本 skill の手順に従う。**どちらでも構わない**が、エンティティ変更を伴う時は CLI 経由のほうが取り漏れが減るので推奨。
## ステップ 1: どちらの方式を使うか決める
| 状況 | 方式 |
|---|---|
| エンティティ (`packages/backend/src/models/*.ts`) を `@Column` / `@Index` / `@Entity` 等で先に変更し、差分から自動生成したい | `typeorm migration:generate` (本 skill の手順) |
| 手書き SQL / データ移行 / `CREATE INDEX CONCURRENTLY` など、エンティティ差分では表現できない変更 | `typeorm migration:create` で空雛形を作るか、`migrate-new` command で手書き雛形を作る |
| 列追加 1 本のような小規模変更で、既存ファイルをコピーした方が速い | AGENTS.md §3 の手順 (`Date.now()` + 手書き) でよい |
迷ったら **まずエンティティを変更 → `migration:generate`** が原則。既存 342 ファイルのほぼすべてが `queryRunner.query(\`SQL...\`)` の raw SQL なので、CLI 出力でも手書きでもスタイルは揃う。
## ステップ 2: CLI 実行
ルートディレクトリから以下を実行する。`<PascalName>` は変更内容を表す PascalCase (例: `AddBirthdayIndex`, `AddCategoryToAvatarDecorations`)。
### 2-A. エンティティ差分から生成
[CONTRIBUTING.md §Migration作成方法](../../../CONTRIBUTING.md#migration作成方法) に記載の基本形:
```bash
# packages/backend ディレクトリで実行する場合 (CONTRIBUTING.md 記載形式)
pnpm dlx typeorm migration:generate -d ormconfig.js -o --esm <PascalName>
```
**リポジトリルートから実行する場合** (AI が使う推奨形式。`pnpm --filter backend exec` を使うと backend の TypeORM バージョンと一致するため確実):
```bash
pnpm --filter backend exec typeorm migration:generate -d ormconfig.js -o --esm migration/<PascalName>
```
> **`--esm` について**: `-o` / `--outputJs` は「TS ではなく JS を出力する」オプション、`--esm` は「ESM 形式 (`export class ...`) で出力する」オプション。Misskey の既存 migration はすべて ESM JS であるため **両方が必須**。`--esm` を省略すると CommonJS 形式の JS が生成されスタイルが揃わない。
事前準備:
- `pnpm build-pre` を実行して `built/meta.json` を生成する (`loadConfig()` が `built/meta.json` を必須とするため。`pnpm build` 済みであれば不要)。
- `.config/default.yml` が存在すること (なければ `.config/example.yml` を参考に作成する)。
- `pnpm --filter backend compile-config` を実行して `built/.config.json` を生成する (`ormconfig.js` が `loadConfig()` 経由で必須とする。未実行だと "Compiled configuration file not found." エラーになる)。
- `pnpm --filter backend build` でエンティティを最新ビルド (CLI は `built/` を読む)。
- ローカル DB を起動する (`docker compose -f compose.local-db.yml up -d`)。
### 2-B. 空の手書きマイグレーション
```bash
pnpm --filter backend exec typeorm migration:create -o --esm migration/<PascalName>
```
ローカル DB の起動とビルドは不要。空の `up` / `down` だけが生成される。
> `-o --esm` を **必ず付ける**。これが無いと `<UnixMs>-<PascalName>.ts` (CommonJS / TS 出力) が生成されるが、Misskey の `ormconfig.js` は `migration/*.js` だけを読み、既存の他 migration も全て `export class ... { async up(queryRunner) {...} }` の ESM JS 形式なので、後で手作業で `.ts → .js` リネーム + `import { MigrationInterface }` 削除 + `class ... implements MigrationInterface` 削除をしないと走らない。`-o --esm` を付ければそのまま `.js` ESM で出るので、後処理は SPDX ヘッダー付与 (ステップ 3) だけで済む。
## ステップ 3: SPDX ヘッダー付与
CLI 出力には SPDX ヘッダーが含まれない。**必ず冒頭に追加する** (CI の `spdx` ジョブが失敗するため)。
```js
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
```
## ステップ 4: up / down の整合確認
- `up()` の各ステートメントに対し、`down()` で完全に巻き戻せること。
- 列追加 (`ADD COLUMN`) ↔ 列削除 (`DROP COLUMN`)、テーブル作成 ↔ テーブル削除、
FK 追加 ↔ FK 削除、インデックス作成 ↔ インデックス削除 を必ずペアで書く。
- `down()` を空のまま残さない。本番ロールバック時に詰む。
### インデックス追加時の注意 (CREATE INDEX CONCURRENTLY)
大規模テーブルへの `CREATE INDEX` は本番で長時間ロックする恐れがある。`CONCURRENTLY` で発行するときは **migration 側にも対応が必要**: PostgreSQL は `CREATE INDEX CONCURRENTLY` を transaction 内で実行できないため、migration class に以下を仕込んで TypeORM に「この migration は transaction を張らない」と指示する。
参照実装: [packages/backend/migration/1745378064470-composite-note-index.js](../../../packages/backend/migration/1745378064470-composite-note-index.js)。
```js
const isConcurrentIndexMigrationEnabled = process.env.MISSKEY_MIGRATION_CREATE_INDEX_CONCURRENTLY === '1';
export class CompositeNoteIndex1745378064470 {
name = 'CompositeNoteIndex1745378064470';
transaction = isConcurrentIndexMigrationEnabled ? false : undefined;
async up(queryRunner) {
const concurrently = isConcurrentIndexMigrationEnabled;
if (concurrently) {
// CREATE INDEX CONCURRENTLY ...
} else {
// CREATE INDEX ...
}
}
async down(queryRunner) {
// 同様に環境変数で分岐
}
}
```
要点:
- **`transaction = isConcurrentIndexMigrationEnabled ? false : undefined;`** が必須。これがないと `CREATE INDEX CONCURRENTLY` が transaction 内で実行されて `ERROR: CREATE INDEX CONCURRENTLY cannot run inside a transaction block` で失敗する。
- 環境変数 `MISSKEY_MIGRATION_CREATE_INDEX_CONCURRENTLY=1` がデフォルト OFF。OFF のときは普通の `CREATE INDEX` (transaction 内) で動く必要がある。`up`/`down` 双方を環境変数で分岐させる。
- `ormconfig.js` の `migrationsTransactionMode` は **環境変数で切り替わる**: `MISSKEY_MIGRATION_CREATE_INDEX_CONCURRENTLY=1` のときだけ `'each'` (各 migration が個別 transaction)、未設定時は `'all'` (全 migration を 1 つの transaction でラップ) ([ormconfig.js:19](../../../packages/backend/ormconfig.js#L19))。普段は `'all'` 前提なので、CONCURRENTLY を使う migration を書く時だけこのフラグの存在を意識すれば良い。
### 関連エンティティとの一致
`migration:generate` を使った場合、エンティティ側の `@Column` / `@Entity` 修正と DB スキーマが食い違うとビルド全体がズレる。生成後に該当エンティティと SQL の対応を目視確認すること。
## ステップ 5: 検証
ルートから実行:
```bash
# 未反映の差分が無いか (新規 migration が生成すべき DDL を取り逃していないか)
pnpm --filter backend check-migrations
# ローカル DB に適用
pnpm migrate
# ロールバック (down が壊れていないか)
pnpm revert
# 再適用 (順方向にもう一度通す)
pnpm migrate
```
`check-migrations` の実体は [scripts/check_migrations_clean.js](../../../packages/backend/scripts/check_migrations_clean.js)。TypeORM の `dataSource.driver.createSchemaBuilder().log()` で pending DDL を取得し、`upQueries` / `downQueries` のいずれかが残っていれば非ゼロ終了する。**順序検査ではなく**「エンティティと migration が同期しているか」の検査。
## ステップ 6: 既存ファイル参照テンプレ
新規ファイルを書くときは、変更パターンが近い既存ファイルを **必ずひとつ開いて並べて書く**。スタイルが激しくズレた PR は差し戻されやすい。
| パターン | 参照ファイル |
|---|---|
| インデックス追加 + 関数定義 | [packages/backend/migration/1767169026317-birthday-index.js](../../../packages/backend/migration/1767169026317-birthday-index.js) |
| 列追加のみ | [packages/backend/migration/1766652173085-add-category-to-avatar-decorations.js](../../../packages/backend/migration/1766652173085-add-category-to-avatar-decorations.js) |
| テーブル新規作成 + FK | [packages/backend/migration/1761569941833-add-channel-muting.js](../../../packages/backend/migration/1761569941833-add-channel-muting.js) |
クラス命名規則は **PascalCase 名 + 13 桁タイムスタンプ** (例: `class BirthdayIndex1767169026317`)。`name` プロパティもクラス名と同一文字列にする。
## ステップ 7: CHANGELOG (ユーザー影響がある場合)
スキーマ変更がユーザーに見える挙動を生む場合のみ、`CHANGELOG.md` の `## Unreleased` → `### Server` または `### General` に 1 行追加する ([AGENTS.md §CHANGELOG](../../../AGENTS.md#changelog) 参照)。内部リファクタや純粋なインデックス追加は不要。

View File

@@ -0,0 +1,152 @@
---
name: "source-command-harness-audit"
description: "Misskey の .Codex/ ハーネス (skills/agents/commands) を 7 カテゴリで採点する確定的な監査。"
---
# source-command-harness-audit
Use this skill when the user asks to run the migrated source command `harness-audit`.
## Command Template
<!--
SPDX-License-Identifier: MIT
SPDX-FileCopyrightText: 2026 Affaan Mustafa and everything-Codex contributors
出典 (upstream): https://github.com/affaan-m/everything-Codex (v2.0.0-rc.1)
upstream path: commands/harness-audit.md
upstream license: MIT — https://github.com/affaan-m/everything-Codex/blob/main/LICENSE
project-level notice: see .Codex/THIRD_PARTY_LICENSES.md (Misskey 内サードパーティ一覧 + MIT 全文)
Imported into Misskey .Codex/ on 2026-05-10. The 7-category rubric and output contract are derived from the upstream ECC version (MIT). The runtime layer was substantially reimplemented for Misskey: the upstream relies on scripts/harness-audit.js to mechanically score, while this version asks Codex to score directly with pnpm/git/grep, and adds Misskey-specific evaluation axes (SPDX coverage / endpoint-list 登録漏れ / migration 順序 / ja-JP.yml 整合).
note: 元 ECC 版は scripts/harness-audit.js (専用 Node スクリプト) で機械採点していたが、Misskey は ECC plugin runtime に依存しない方針なので、Codex が直接ファイルを読んで採点する手動運用版に書き換えた。Misskey 固有の重要観点 (SPDX 適用率 / endpoint-list 登録漏れ / migration 順序 / ja-JP.yml 整合) を評価軸として明示的に組み込んでいる。
-->
# /harness-audit — Misskey ハーネス監査
Misskey リポジトリの `.Codex/` 構成を 7 カテゴリで採点し、改善優先度を提示する。
## Usage
`/harness-audit [scope]`
- `scope` (任意): `repo` (default) / `skills` / `commands` / `agents`
## 評価カテゴリ (各 0-10)
| # | カテゴリ | 評価軸 |
| --- | --- | --- |
| 1 | Tool Coverage | skill / agent / command の数、欠けているワークフロー段、重複なし |
| 2 | Context Efficiency | frontmatter description の冗長度、SKILL.md の長さ分布、重複情報、AGENTS.md の肥大化 |
| 3 | Quality Gates | Stop / PreToolUse / PostToolUse hook の整備、`/quality-gate` 等の完了前ゲートの有無、自動 lint/typecheck |
| 4 | Memory Persistence | docs/* の同期状態を評価。プロジェクト側 `.Codex/memory/` は未採用方針 (auto-memory はユーザーホーム側で自動運用) のため、ここを採点起点にせず既定 5/10 から開始する |
| 5 | Eval Coverage | testing.md の網羅、Misskey 固有の e2e/fed/Storybook/Cypress 適用ガイド |
| 6 | Security Guardrails | SPDX 規約適用、migration 不変性ルール、ja-JP.yml 限定編集ルール、secrets 検出 |
| 7 | Cost Efficiency | enabledPlugins の重複・過剰、context-budget の整備、MCP 過剰登録なし |
## Misskey 固有の確認項目 (採点根拠コマンド)
採点時に以下を実コマンドで確認する。各項目の **属するカテゴリ** は項目内に明記する (#1-#3 は Security Guardrails、#4 は Tool Coverage、#5 は Quality Gates):
```bash
# 1. [Security Guardrails] SPDX 適用率 (新規ファイル想定の汎用チェック)
# - node_modules を prune で除外
# - packages/misskey-js は MIT サブパッケージなので AGPL ヘッダーを持たない (AGENTS.md §1) → 除外
# - built/ なども除外
# 候補にはなお *.config.{ts,js} / *eslint* / *.d.ts のような CI 上 SPDX 対象外
# (.github/workflows/check-spdx-license-id.yml の exclude 参照) も混ざるため、
# 上位に出たファイルが「新規追加した実コード」かどうかは目視判定する。
find packages \
\( -type d \( -name node_modules -o -name built -o -name dist -o -path 'packages/misskey-js' \) -prune \) \
-o -type f \( -name '*.ts' -o -name '*.js' -o -name '*.vue' -o -name '*.scss' \) -print \
| xargs -r grep -L 'SPDX-License-Identifier: AGPL-3.0-only' | head -20
# → 上位に新規実コードが無ければ満点
# 2. [Security Guardrails] ja-JP.yml 以外の locales が直近で手動編集されていないか
# --pretty=format: でコミットヘッダ行を抑止し、ファイル名行のみを残してから grep する。
# Crowdin の自動同期 commit でも他言語 yml は更新されるため、出力が 0 行になることは少ない。
# 出力があった場合は、author / commit message を確認し Crowdin 由来か手動編集かを判定する:
# git log --since='30 days ago' --pretty=format:'%h %an %s' -- locales/<file>.yml
git log --since='30 days ago' --pretty=format: --name-only -- 'locales/*.yml' \
| grep -v '^$' | grep -v 'ja-JP.yml' | sort -u
# → 出力が無い、または全て Crowdin 由来 commit なら満点
# 3. [Security Guardrails] migration の pending DDL 検査 (TypeORM schema builder)
pnpm --filter backend check-migrations
# → 0 errors (= "All migrations are clean.") なら満点
# 4. [Tool Coverage] endpoint-list.ts 登録漏れ (新規 endpoint がリストにない場合)
# endpoints/ は再帰構造 (notes/create.ts, admin/announcements/create.ts 等) で 400+ ファイルあるため、
# endpoint-list.ts も `export * as '<category>/<name>' from './endpoints/<category>/<name>.js';` 形式で
# 1 ファイル 1 行登録される。両者の行数を「再帰 .ts 数」と「export * as 行数」で比較する。
# e2e / 単体テストは endpoint ではないので *.test.ts を除外する。
endpoint_files=$(find packages/backend/src/server/api/endpoints -type f -name '*.ts' ! -name '*.test.ts' | wc -l)
list_entries=$(grep -cE "^export \* as " packages/backend/src/server/api/endpoint-list.ts)
echo "endpoints (recursive): $endpoint_files / endpoint-list.ts entries: $list_entries"
# 差分が 0 なら満点。差分が出たら、登録漏れの具体特定:
comm -23 \
<(find packages/backend/src/server/api/endpoints -type f -name '*.ts' ! -name '*.test.ts' \
| sed -E 's|.*/endpoints/||;s|\.ts$||' | sort -u) \
<(grep -oE "^export \* as '[^']+'" packages/backend/src/server/api/endpoint-list.ts \
| sed -E "s/^export \* as '([^']+)'/\1/" | sort -u)
# 出力された行が登録漏れの endpoint。0 行なら満点。
# 5. [Quality Gates] console.log の混入
grep -rn 'console\.\(log\|debug\)' packages/backend/src packages/frontend/src 2>/dev/null \
| grep -v 'node_modules\|test\|.spec\.\|.test\.' | wc -l
# → 0 が理想
```
## 出力契約
以下を返す:
1. `overall_score` / `max_score` (repo は 70 点満点)
2. カテゴリごとのスコア + 具体的な根拠
3. 失敗チェック項目と該当ファイルパス
4. Top 3 改善アクション
5. 次に適用を推奨する skill / 手順
## サンプル出力
```text
Harness Audit (repo): 55/70
Tool Coverage: 9/10 (skills 5, agents 2, commands 5 — 偏りなし)
Context Efficiency: 8/10 (description 平均 3-5 行、肥大なし)
Quality Gates: 5/10 (Stop hook 共有設定に未登録 / `/quality-gate` あり)
Memory Persistence: 5/10 (プロジェクト側 memory/ 未採用方針 = 既定値)
Eval Coverage: 7/10 (testing.md 網羅、Storybook 一部抜け)
Security Guardrails: 10/10 (SPDX 100%, locales OK, migrations clean)
Cost Efficiency: 8/10 (context-budget 導入済 / MCP 0)
Failed Checks:
- packages/frontend/src/.../X.vue で SPDX 欠落 (Security Guardrails)
- console.log が backend に 3 件 (Quality Gates)
- 共有 Stop hook なし (Quality Gates) — 各 contributor が `.Codex/settings.local.json` で opt-in する方針なら減点しなくて良い
Top 3 Actions:
1) [Security Guardrails] SPDX 欠落 1 ファイルを修正:
packages/frontend/src/.../X.vue
2) [Quality Gates] backend の console.log 3 件を logger に置換。
git grep "console\.log" packages/backend/src
3) [Cost Efficiency] enabledPlugins から未使用のものを外す。
.Codex/docs/plugins.md と照合。
Suggested next skills to apply:
- /quality-gate で完了前に lint + unit test を回す
- context-budget で plugin 由来の overhead を確認
```
## 採点の信頼性
- 確定的: 同じ commit / 同じ `.Codex/` 構成なら同じスコア
- ヒューリスティクス: 「description の冗長度」のような主観項目は同一基準で機械的に判定
- スクリプト不要: `pnpm``git``grep`/`find` 等の標準ツールのみ
## 参考: ECC オリジナルとの差分
- ECC 版は `node scripts/harness-audit.js` を直叩きする運用で、ECC リポジトリ全体に閉じた採点だった。
- Misskey 版は **Misskey の規約 (SPDX/migration/locales/endpoint-list)** を Security 採点に組み込み、`pnpm` ベースの実コマンドで根拠を取る方式に再設計。
- 結果として ECC への依存はゼロ。

View File

@@ -0,0 +1,128 @@
---
name: "source-command-quality-gate"
description: "Misskey の lint / typecheck / 高速テストを順に実行して品質ゲートを通すコマンド。完了前の軽量検証用。"
---
# source-command-quality-gate
Use this skill when the user asks to run the migrated source command `quality-gate`.
## Command Template
<!--
SPDX-License-Identifier: MIT
SPDX-FileCopyrightText: 2026 Affaan Mustafa and everything-Codex contributors
出典 (upstream): https://github.com/affaan-m/everything-Codex (v2.0.0-rc.1)
upstream path: commands/quality-gate.md
upstream license: MIT — https://github.com/affaan-m/everything-Codex/blob/main/LICENSE
project-level notice: see .Codex/THIRD_PARTY_LICENSES.md (Misskey 内サードパーティ一覧 + MIT 全文)
Imported into Misskey .Codex/ on 2026-05-10. Pipeline 概念 (lint → typecheck → test) は upstream ECC 版から借用 (MIT)。実コマンド層は Misskey の pnpm + tsgo + ESLint + Vitest に固定し、formatter (Prettier/Biome) フェーズは削除した。
note: 元 ECC 版は言語自動判定 + format/lint/type のジェネリック版だったが、Misskey 専用に pnpm + tsgo + ESLint + Vitest の組み合わせに固定。重い test:e2e / test:fed は含まない (CI 側で実行される)。
-->
# /quality-gate — Misskey 軽量品質ゲート
`/quality-gate [scope]`
完了前の **軽量** 品質チェック。重い E2E / 連合テスト (test:e2e / test:fed / Cypress) は CI 側で実行されるため、本コマンドには含めない。
## Scope
- `repo` (default) — 全パッケージ
- `backend``packages/backend` のみ
- `frontend``packages/frontend` のみ
- `path/to/file.ts` — 単一ファイルへの ESLint --fix のみ
## Pipeline
### Repo scope (全部)
各パッケージの `lint` スクリプト実体は `pnpm typecheck && pnpm eslint` ([packages/backend/package.json](../../packages/backend/package.json), [packages/frontend/package.json](../../packages/frontend/package.json)) で、ルートの `pnpm lint``pnpm --no-bail -r lint` (= 全パッケージで lint を `--no-bail` で実行)。**typecheck は lint に含まれている**ため、通常はこの 2 コマンドで十分:
```bash
# 1. Lint (= typecheck + ESLint、全パッケージ。--no-bail で最初の失敗で止まらず全結果を集める)
pnpm lint
# 2. Unit test (高速、e2e は含まない)
pnpm --filter backend test
pnpm --filter frontend test
```
#### 詳細を分けて見たい時のみ (optional)
lint がまとめて失敗していて typecheck の結果だけ単独で見たい場合は、以下を個別に回す。**通常は不要** (lint の出力を読めば足りる):
```bash
pnpm --filter backend typecheck # tsgo 単体
pnpm --filter frontend typecheck # vue-tsc 単体 (Vue SFC の型を見るため)
```
### Backend scope
`pnpm --filter backend lint` は内部で `pnpm typecheck && pnpm eslint` を実行する ([packages/backend/package.json](../../packages/backend/package.json)) ので、`lint` を回せば typecheck も終わる。軽量ゲートでは typecheck の二重実行を避けるため `lint` + `test` のみ:
```bash
pnpm --filter backend lint
pnpm --filter backend test
```
`tsgo` の出力を単独で見たい時のみ optional で `pnpm --filter backend typecheck` を別途回す。
### Frontend scope
`pnpm --filter frontend lint` も内部で `pnpm typecheck && pnpm eslint` を実行する ([packages/frontend/package.json](../../packages/frontend/package.json)) ため、軽量ゲートでは Backend 同様に `lint` + `test` のみ:
```bash
pnpm --filter frontend lint
pnpm --filter frontend test
```
`vue-tsc` の出力を単独で見たい時のみ optional で `pnpm --filter frontend typecheck` を別途回す。
### Single file scope
```bash
pnpm exec eslint --fix <path>
```
## Output
実行したフェーズの pass/fail と件数を集計する。標準パイプラインは `pnpm lint` (typecheck 内包) と unit test のみなので、デフォルトの出力は以下のようになる:
```text
Quality Gate (repo):
Lint: PASS (0 errors, 2 warnings)
Backend ut: PASS (412/412)
Frontend ut: PASS (87/87)
→ 完了前の軽量チェック OK。重い e2e / 連合テストは CI 側で実行される。
```
`#### 詳細を分けて見たい時のみ (optional)` で個別 typecheck (`pnpm --filter backend typecheck` / `pnpm --filter frontend typecheck`) も回した場合のみ、その結果を追加行として表示する:
```text
Quality Gate (repo):
Lint: PASS (0 errors, 2 warnings)
Backend tc: PASS (0 errors) # optional 実行時のみ
Frontend tc: PASS (0 errors) # optional 実行時のみ
Backend ut: PASS (412/412)
Frontend ut: PASS (87/87)
```
失敗時は最初に落ちたフェーズで停止して詳細を見せる。
## 関連 skill / コマンド
- `/check-misskey-js` コマンド — API 変更時の misskey-js 再生成
- [AGENTS.md §必須コマンド](../../AGENTS.md#必須コマンド) — pnpm コマンド一覧の正典
## 元 ECC 版との差分
- ジェネリックな言語自動判定を排除し、Misskey 固定 pipeline に。
- formatter フェーズなし (Misskey は ESLint --fix のみ採用)。
- e2e / federation / Cypress は重いため除外し CI 側に委譲。

View File

@@ -0,0 +1,164 @@
name = "misskey-api-reviewer"
description = "Misskey の API エンドポイント (packages/backend/src/server/api/endpoints/) の追加・変更を専門レビューする。SPDX / meta / paramDef / UUID 重複 / endpoint-list.ts 登録 / ApiError throw / misskey-js 再生成 / e2e / CHANGELOG を機械的にチェック。バックエンド API を追加・変更した PR レビューで呼び出す。"
developer_instructions = '''
# Misskey API エンドポイントレビュアー
Misskey バックエンド (`packages/backend`) の REST API エンドポイント追加・変更 PR を機械的にレビューする専門エージェント。規約の根拠は [.Codex/skills/add-api-endpoint/SKILL.md](../skills/add-api-endpoint/SKILL.md)。
## 役割
`packages/backend/src/server/api/endpoints/` 配下の `.ts` 変更を対象に、規約逸脱・登録漏れ・型自動生成漏れ・テスト不足を抽出する。良い点には触れず、改善が必要な箇所のみ報告する。
## レビュー対象の特定
呼び出し元から明示的にファイルが渡されたらそれを優先する。渡されなかった場合は **PR / ブランチ全体の差分** を取得する (未コミット差分のみではないことに注意)。
```bash
BASE=$(git merge-base origin/develop HEAD)
{ git diff --name-only "$BASE"...HEAD; git diff --name-only HEAD; git ls-files --others --exclude-standard; } \
| sort -u \
| grep -E '^packages/backend/src/server/api/endpoints/.*\.ts$'
```
`origin/develop` が無い環境では `develop` または `master` にフォールバックする。
加えて以下も同じ baseline で差分対象に含める:
- `packages/backend/src/server/api/endpoint-list.ts`
- `packages/backend/test/e2e/**` (とくに `endpoints.ts` と `<area>.ts`)
- `packages/misskey-js/src/autogen/**`
- `CHANGELOG.md`
差分対象が空なら「レビュー対象の API エンドポイント変更なし」と短く報告して終了。
## チェックリスト
### 1. SPDX ヘッダー (Critical)
新規 `.ts` ファイル冒頭に以下があるか:
```
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
```
欠落すると CI の `spdx` ジョブが落ちる。
### 2. `meta` の必須・推奨フィールド (Major)
[endpoints.ts の型定義](../../packages/backend/src/server/api/endpoints.ts) を真とする。
- `tags`: OpenAPI タグ (機能領域)。
- `requireCredential`: 明示必須 (boolean)。
- `kind`: OAuth scope。`requireCredential: true` のとき必須 (`read:account` / `write:notes` 等)。
- `requireModerator` / `requireAdmin`: 権限制限が要るか。
- `prohibitMoved`: 移行済アカウントを拒否するか (write 系で要検討)。
- `limit`: レート制限 `{ duration, max, key?, minInterval? }`。書き込み系 / コスト高い処理で未指定なら指摘。
- `errors`: エラー定義。各要素に `message` / `code` / `id` (UUID v4) が揃っているか。
- `res`: JSON Schema または `ref: '<EntityName>'`。各プロパティに `optional` / `nullable` が **明示** されているか。
- `requireFile` / `secure` / `allowGet` / `cacheSec` / `description`: 該当するエンドポイントで使い分けているか。
### 3. `meta.errors` の UUID 検証 (Critical)
各 `errors[*].id` が:
1. UUID v4 形式 (`xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx`) か
2. 既存エンドポイントの `id` と重複していないか
重複検査:
```bash
grep -rn "id: '< UUID>'" packages/backend/src/server/api/endpoints/
```
新規エンドポイントの全 `id` を抽出して衝突を確認する。
### 4. `paramDef` (Major)
- JSON Schema 形式 (`type: 'object'`, `properties`, `required`)
- ID 文字列は `format: 'misskey:id'`
- `required` 配列で必須プロパティを明示
- `as const` または `as const satisfies Schema` で型推論を効かせる (既存実装は前者多数。`as const` 自体が無く `Schema` 型注釈もない場合のみ指摘)
### 5. エンドポイント実装本体 (Major)
- `Endpoint<typeof meta, typeof paramDef>` を継承しているか。
- `@Injectable()` デコレータ + `export default class` 形式か (`// eslint-disable-line import/no-default-export` が必要)。
- DI は `@Inject(DI.xxx)` 形式か。
- **クライアントに返すべき API エラーは `throw new ApiError(meta.errors.<key>)`** ([error.ts](../../packages/backend/src/server/api/error.ts) 参照)。`meta.errors` で定義したエラーケースを `throw new Error(...)` で投げているなら指摘する。
- 防御的アサーション・「起きるはずがない」内部不整合・テスト用 ENV ガード等の **想定外フェイルファスト** は `throw new Error('...')` で構わない。既存実装でも `admin/reset-password.ts` などが採用しているパターン (例: `cannot reset password of root`)。`meta.errors` に対応がない `throw new Error` を一律で指摘しない。
- 同期 `throw` は許容。非同期処理での例外伝搬を確認する。
### 6. ★ `endpoint-list.ts` への登録 (Critical)
最も忘れやすい。**忘れると 404**。[endpoint-list.ts](../../packages/backend/src/server/api/endpoint-list.ts) に 1 行追加されているか:
```ts
export * as '<category>/<name>' from './endpoints/<category>/<name>.js';
```
新規エンドポイントを抽出し、各々が `endpoint-list.ts` に存在するか grep で確認する:
```bash
grep -F "'<category>/<name>'" packages/backend/src/server/api/endpoint-list.ts
```
**並び順の補足**: ファイル全体は厳密なアルファベット順では並んでおらず、同カテゴリ内 (`admin/queue/*` など) でも追加された経緯どおりの順になっている箇所が多い。**順序逸脱は指摘根拠にしない** (誤検知の元)。「行が存在するか」のみを Critical 観点として扱う。
### 7. `misskey-js` 再生成 (Critical)
`meta` / `paramDef` / `res` を変更したら、PR / ブランチに `packages/misskey-js/src/autogen/` 配下の差分が含まれているか確認する:
```bash
BASE=$(git merge-base origin/develop HEAD)
git diff --name-only "$BASE"...HEAD -- packages/misskey-js/src/autogen/
```
差分ゼロなら `pnpm build-misskey-js-with-types` の実行漏れ。CI の `check-misskey-js-autogen` ジョブで必ず落ちるため Critical 扱い。
### 8. e2e テスト (Major)
[test/e2e/endpoints.ts](../../packages/backend/test/e2e/endpoints.ts) または `test/e2e/<area>.ts` (`note.ts`, `users.ts` 等) 配下に、対応する `api('<category>/<name>', ...)` 呼び出しを含む `test(...)` ケースが追加されているか確認する。複雑な分岐 (権限チェック・エラーケース) の網羅も確認する。
**describe ラベルの形式は問わない**: 既存テストは `describe('Note', () => { test('稿', ...) })` のように人間可読ラベルで構造化されており、`<category>/<name>` 形式の describe は使われていない。describe 名の規約違反としては指摘しない。
### 9. CHANGELOG エントリ (Minor)
ユーザー影響がある (新エンドポイント / 既存挙動変更) 場合、`CHANGELOG.md` の `## Unreleased` → `### Server` に 1 行追加されているか確認する。
```
- Feat: /api/<category>/<name> を追加
```
純粋な内部リファクタなら不要。
## 出力形式
優先度別に以下のフォーマットで出力する。
```
## 🔴 Critical
- packages/backend/src/server/api/endpoints/foo/bar.ts:23
meta.errors.fooError.id が UUID v4 形式ではない (実値: 'xxx-xxx')。
`node -e "console.log(crypto.randomUUID())"` で再生成すること。
## 🟡 Major
- ...
## 🔵 Minor
- ...
```
問題のないチェック項目には触れない。全項目クリアなら `✅ レビュー観点上の指摘なし` と短く返す。
## 参照
- [.Codex/skills/add-api-endpoint/SKILL.md](../skills/add-api-endpoint/SKILL.md) — 実装側の規約 (本エージェントの根拠)
- [endpoints.ts (meta/paramDef 型定義)](../../packages/backend/src/server/api/endpoints.ts)
- [endpoint-list.ts (★ 登録先)](../../packages/backend/src/server/api/endpoint-list.ts)
- [endpoint-base.ts (Endpoint 基底クラス)](../../packages/backend/src/server/api/endpoint-base.ts)
- [error.ts (ApiError)](../../packages/backend/src/server/api/error.ts)
- [test/e2e/endpoints.ts](../../packages/backend/test/e2e/endpoints.ts)
- [AGENTS.md](../../AGENTS.md) — SPDX / マイグレーション履歴 / CHANGELOG 書式などの最低限ルール (Codex / Copilot と共通)'''

View File

@@ -0,0 +1,173 @@
name = "vue-component-reviewer"
description = 'Misskey フロントエンド (packages/frontend/src/components/ / pages/) の Vue 3 SFC 変更を専門レビューする。SPDX (HTML コメント) / Mk* 命名 / <script lang="ts" setup> / type-only defineProps / <style lang="scss" module> / CSS 変数 / i18n.ts と i18n.tsx の使い分け / os.* 経由 / a11y / Storybook (*.stories.impl.ts) を機械的にチェック。フロントエンドの .vue 変更を含む PR レビューで呼び出す。'
developer_instructions = '''
# Misskey Vue コンポーネントレビュアー
Misskey フロントエンド (`packages/frontend`) の Vue 3 SFC 変更を機械的にレビューする専門エージェント。規約の根拠は [.Codex/skills/add-mk-component/SKILL.md](../skills/add-mk-component/SKILL.md)。
## 役割
`packages/frontend/src/components/` および `packages/frontend/src/pages/` 配下の `.vue` 変更を対象に、命名・i18n・スタイル・アクセシビリティ・Storybook 併設の規約逸脱を抽出する。良い点には触れず、改善が必要な箇所のみ報告する。
## レビュー対象の特定
呼び出し元から明示的にファイルが渡されたらそれを優先する。渡されなかった場合は **PR / ブランチ全体の差分** を取得する (未コミット差分のみではないことに注意)。
```bash
BASE=$(git merge-base origin/develop HEAD)
{ git diff --name-only "$BASE"...HEAD; git diff --name-only HEAD; git ls-files --others --exclude-standard; } \
| sort -u \
| grep -E '^packages/frontend/src/.*\.vue$'
```
`origin/develop` が無い環境では `develop` または `master` にフォールバックする。
`.ts` を一律で含めると本エージェントの守備範囲外 (composable / store / service 層) まで巻き込んで誤検知が増えるため、対象は `.vue` のみとし、Storybook 併設チェックのために以下を **別リスト** として追加する:
- `locales/*.yml` (とくに `ja-JP.yml` 以外の変更は即 Critical)
- `packages/frontend/src/components/**/*.stories.impl.ts`
- `CHANGELOG.md`
差分対象が空なら「レビュー対象の Vue コンポーネント変更なし」と短く報告して終了。
## チェックリスト
### 1. SPDX ヘッダー (Critical)
`.vue` ファイル冒頭は **HTML コメント形式** で必須:
```html
<!--
SPDX-FileCopyrightText: syuilo and misskey-project
SPDX-License-Identifier: AGPL-3.0-only
-->
```
`/* ... */` (TS 形式) は禁止。既存 SFC の慣習・SFC 先頭として自然な形式に統一するため (CI の `spdx` ジョブはコメント形式ではなく SPDX 文字列の有無のみを検査するため、形式が違っても CI は通るが、規約違反として指摘する)。
### 2. 命名規約 (Major)
- 共有 / 再利用コンポーネント (`packages/frontend/src/components/` 配下、サブディレクトリ含む) は `Mk` プレフィックス必須 (例: `MkButton.vue`, `global/MkAvatar.vue`, `grid/MkGrid.vue`)。
- ページ固有のものは `pages/` 配下に置き、`Mk` プレフィックスは不要。
> `<script setup>` SFC は named export を持たないため、「ファイル名と export 名の一致」を機械的に検査することはできない。SFC のデフォルトエクスポートはコンパイラ生成なので、ファイル名規約のみを基準にする。
### 3. `<script>` タグ (Major)
- `<script lang="ts" setup>` または `<script setup lang="ts">` のどちらでもよい (既存コードは多数派が前者だが、後者も `MkThemePreview.vue` 等で使われている)。属性順は指摘しない。`lang="ts"` が **無い** ものは指摘する。
- 型ジェネリックが必要なら `generic="T extends ..."` 属性を加える (順序問わず)。
- `defineProps<{ ... }>()` / `defineEmits<{ ... }>()` は **type-only** 形式。runtime の object 形式 (`defineProps({ ... })`) は使わない。
- Options API (`export default { data() { ... } }`) は禁止。
### 4. i18n の使い分け (Critical)
- 文字列リテラルの直書き禁止 (テンプレート / JS 両方)。
- 引数なし: `i18n.ts.<path>` (例: `i18n.ts.deleted`)。
- 引数あり: `i18n.tsx.<path>(...)` (関数呼び出し、例: `i18n.tsx.takeOverConfirm({ name })`)。
- 新規 i18n キーは `locales/ja-JP.yml` **のみ** に追加。
- **`locales/ja-JP.yml` 以外の `.yml` 変更があれば即 Critical** (`en-US.yml` 等は Crowdin 自動配信先で、手動編集すると上書き喪失する)。
差分検出:
```bash
BASE=$(git merge-base origin/develop HEAD)
git diff --name-only "$BASE"...HEAD -- 'locales/*.yml' | grep -v 'ja-JP.yml'
```
### 5. スタイル (Major)
- `<style lang="scss" module>` を既定とし、`:class="$style.foo"` で参照する。
- 新規で `<style scoped>` (module なし) は使わない (legacy)。
- **CSS 変数の使用必須** (色・余白・角丸など):
- テーマ色: `var(--MI_THEME-*)` (例: `var(--MI_THEME-panel)`)
- UI 共通: `var(--MI-*)` (例: `var(--MI-radius)`)
- 直接の `#fff` / `rgb(...)` / `rgba(...)` ハードコードは禁止
ハードコード検出:
```bash
BASE=$(git merge-base origin/develop HEAD)
git diff "$BASE"...HEAD -- 'packages/frontend/src/**/*.vue' \
| grep -E '^\+' | grep -E '#[0-9a-fA-F]{3,8}\b|rgba?\('
```
### 6. UI 操作は `os.*` 経由 (Critical)
- `alert()` / `confirm()` / `window.prompt()` / `window.alert()`
- `os.alert` / `os.confirm` / `os.popup` / `os.toast` / `os.popupMenu` / `os.contextMenu` / `os.form` / `os.apiWithDialog` 使 ([os.ts](../../packages/frontend/src/os.ts) )
:
```bash
BASE=$(git merge-base origin/develop HEAD)
git diff "$BASE"...HEAD -- 'packages/frontend/src/**/*.vue' \
| grep -E '^\+' | grep -E '\b(alert|confirm|prompt)\s*\('
```
### 7. アクセシビリティ (Major)
- `<button>` `role="button"` + `tabindex="0"` + (`@keydown.enter` )
- `<div @click>` a11y
- `<label>` `aria-label`
- `:disabled` `aria-disabled`
### 8. Storybook 併設 (Major)
- `Mk*` `Mk<Name>.stories.impl.ts` (: `components/global/MkAvatar.stories.impl.ts`, `components/grid/MkGrid.stories.impl.ts`)
- ** `.stories.impl.ts` ** (`.stories.ts` )
- [MkButton.stories.impl.ts](../../packages/frontend/src/components/MkButton.stories.impl.ts)
( `Mk*.vue` ):
```bash
BASE=$(git merge-base origin/develop HEAD)
git diff --name-only --diff-filter=A "$BASE"...HEAD -- \
'packages/frontend/src/components/**/Mk*.vue' \
| sed 's/\.vue$/.stories.impl.ts/' \
| xargs -I {} sh -c 'test -f {} || echo "missing: {}"'
```
### 9. アイコン (Minor)
- Tabler icons (`<i class="ti ti-info-circle">` ) 使
- SVG 使 ()
### 10. CHANGELOG エントリ (Minor)
`CHANGELOG.md` `## Unreleased` → `### Client` に 1 行追加されているか確認する。
```
- Enhance: <component> <>
- Fix: <component> <>
```
## 出力形式
```
## 🔴 Critical
- packages/frontend/src/components/MkFoo.vue:1
SPDX HTML TS
`<!-- ... -->`
## 🟡 Major
- ...
## 🔵 Minor
- ...
```
` `
## 参照
- [.Codex/skills/add-mk-component/SKILL.md](../skills/add-mk-component/SKILL.md) ()
- [.Codex/skills/add-i18n-key/SKILL.md](../skills/add-i18n-key/SKILL.md) i18n
- [os.ts](../../packages/frontend/src/os.ts) UI API
- [MkButton.vue](../../packages/frontend/src/components/MkButton.vue)
- [MkInput.vue](../../packages/frontend/src/components/MkInput.vue) generic SFC
- [MkButton.stories.impl.ts](../../packages/frontend/src/components/MkButton.stories.impl.ts) Storybook
- [AGENTS.md](../../AGENTS.md) SPDX / locales / CHANGELOG (Codex / Copilot )'''

2
.serena/.gitignore vendored Normal file
View File

@@ -0,0 +1,2 @@
/cache
/project.local.yml

133
.serena/project.yml Normal file
View File

@@ -0,0 +1,133 @@
# the name by which the project can be referenced within Serena
project_name: "misskey"
# list of languages for which language servers are started; choose from:
# al angular ansible bash clojure
# cpp cpp_ccls crystal csharp csharp_omnisharp
# dart elixir elm erlang fortran
# fsharp go groovy haskell haxe
# hlsl html java json julia
# kotlin lean4 lua luau markdown
# matlab msl nix ocaml pascal
# perl php php_phpactor powershell python
# python_jedi python_ty r rego ruby
# ruby_solargraph rust scala scss solidity
# svelte swift systemverilog terraform toml
# typescript typescript_vts vue yaml zig
# (This list may be outdated. For the current list, see values of Language enum here:
# https://github.com/oraios/serena/blob/main/src/solidlsp/ls_config.py
# For some languages, there are alternative language servers, e.g. csharp_omnisharp, ruby_solargraph.)
# Note:
# - For C, use cpp
# - For JavaScript, use typescript
# - For Angular projects, use angular (subsumes typescript+html; requires `npm install` in the project root)
# - For Svelte projects, use svelte (subsumes typescript/javascript for .svelte projects; requires npm)
# - For SCSS / Sass / plain CSS, use scss (some-sass-language-server handles all three)
# - For Free Pascal/Lazarus, use pascal
# Special requirements:
# Some languages require additional setup/installations.
# See here for details: https://oraios.github.io/serena/01-about/020_programming-languages.html#language-servers
# When using multiple languages, the first language server that supports a given file will be used for that file.
# The first language is the default language and the respective language server will be used as a fallback.
# Note that when using the JetBrains backend, language servers are not used and this list is correspondingly ignored.
languages:
- vue
# the encoding used by text files in the project
# For a list of possible encodings, see https://docs.python.org/3.11/library/codecs.html#standard-encodings
encoding: "utf-8"
# line ending convention to use when writing source files.
# Possible values: unset (use global setting), "lf", "crlf", or "native" (platform default)
# This does not affect Serena's own files (e.g. memories and configuration files), which always use native line endings.
line_ending:
# The language backend to use for this project.
# If not set, the global setting from serena_config.yml is used.
# Valid values: LSP, JetBrains
# Note: the backend is fixed at startup. If a project with a different backend
# is activated post-init, an error will be returned.
language_backend:
# whether to use project's .gitignore files to ignore files
ignore_all_files_in_gitignore: true
# advanced configuration option allowing to configure language server-specific options.
# Maps the language key to the options.
# The settings are considered only if the project is trusted (see global configuration to define trusted projects).
# See https://oraios.github.io/serena/02-usage/050_configuration.html#language-server-specific-settings
ls_specific_settings: {}
# list of additional workspace folder paths for cross-package reference support (e.g. in monorepos).
# Paths can be absolute or relative to the project root.
# Each folder is registered as an LSP workspace folder, enabling language servers to discover
# symbols and references across package boundaries.
# Currently supported for: TypeScript.
# Example:
# additional_workspace_folders:
# - ../sibling-package
# - ../shared-lib
additional_workspace_folders: []
# list of additional paths to ignore in this project.
# Same syntax as gitignore, so you can use * and **.
# Note: global ignored_paths from serena_config.yml are also applied additively.
ignored_paths: []
# whether the project is in read-only mode
# If set to true, all editing tools will be disabled and attempts to use them will result in an error
# Added on 2025-04-18
read_only: false
# list of tool names to exclude.
# This extends the existing exclusions (e.g. from the global configuration)
# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html
excluded_tools: []
# list of tools to include that would otherwise be disabled (particularly optional tools that are disabled by default).
# This extends the existing inclusions (e.g. from the global configuration).
# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html
included_optional_tools: []
# fixed set of tools to use as the base tool set (if non-empty), replacing Serena's default set of tools.
# This cannot be combined with non-empty excluded_tools or included_optional_tools.
# Find the list of tools here: https://oraios.github.io/serena/01-about/035_tools.html
fixed_tools: []
# list of mode names that are to be activated by default, overriding the setting in the global configuration.
# The full set of modes to be activated is base_modes (from global config) + default_modes + added_modes.
# If the setting is undefined/empty, the default_modes from the global configuration (serena_config.yml) apply.
# Otherwise, this overrides the setting from the global configuration (serena_config.yml).
# Therefore, you can set this to [] if you do not want the default modes defined in the global config to apply
# for this project.
# This setting can, in turn, be overridden by CLI parameters (--mode).
# See https://oraios.github.io/serena/02-usage/050_configuration.html#modes
default_modes:
# list of mode names to be activated additionally for this project, e.g. ["query-projects"]
# The full set of modes to be activated is base_modes (from global config) + default_modes + added_modes.
# See https://oraios.github.io/serena/02-usage/050_configuration.html#modes
added_modes:
# initial prompt for the project. It will always be given to the LLM upon activating the project
# (contrary to the memories, which are loaded on demand).
initial_prompt: ""
# time budget (seconds) per tool call for the retrieval of additional symbol information
# such as docstrings or parameter information.
# This overrides the corresponding setting in the global configuration; see the documentation there.
# If null or missing, use the setting from the global configuration.
symbol_info_budget:
# list of regex patterns which, when matched, mark a memory entry as readonly.
# Extends the list from the global configuration, merging the two lists.
read_only_memory_patterns: []
# list of regex patterns for memories to completely ignore.
# Matching memories will not appear in list_memories or activate_project output
# and cannot be accessed via read_memory or write_memory.
# To access ignored memory files, use the read_file tool on the raw file path.
# Extends the list from the global configuration, merging the two lists.
# Example: ["_archive/.*", "_episodes/.*"]
ignored_memory_patterns: []

View File

@@ -128,7 +128,7 @@ describe('ユーザー', () => {
preventAiLearning: user.preventAiLearning,
isExplorable: user.isExplorable,
isDeleted: user.isDeleted,
hiddenRoleIds: (user as any).hiddenRoleIds ?? [],
hiddenRoleIds: user.hiddenRoleIds ?? [],
twoFactorBackupCodesStock: user.twoFactorBackupCodesStock,
hideOnlineStatus: user.hideOnlineStatus,
hasUnreadSpecifiedNotes: user.hasUnreadSpecifiedNotes,
@@ -662,7 +662,7 @@ describe('ユーザー', () => {
isModerator: rolePublic.isModerator,
isAdministrator: rolePublic.isAdministrator,
asBadge: rolePublic.asBadge,
isPublicDisplayRequired: (rolePublic as any).isPublicDisplayRequired,
isPublicDisplayRequired: rolePublic.isPublicDisplayRequired,
displayOrder: rolePublic.displayOrder,
}]);
});
@@ -683,7 +683,7 @@ describe('ユーザー', () => {
isModerator: roleBadge.isModerator,
isAdministrator: roleBadge.isAdministrator,
asBadge: roleBadge.asBadge,
isPublicDisplayRequired: (roleBadge as any).isPublicDisplayRequired,
isPublicDisplayRequired: roleBadge.isPublicDisplayRequired,
displayOrder: roleBadge.displayOrder,
}]);
});
@@ -692,7 +692,7 @@ describe('ユーザー', () => {
const visibleRole = await role(root, { isPublic: true, name: 'Hideable Role' });
const secondVisibleRole = await role(root, { isPublic: true, name: 'Second Hideable Role' });
const privateRole = await role(root, { isPublic: false, name: 'Private Role' });
const forcedRole = await role(root, { isPublic: true, isPublicDisplayRequired: true, name: 'Forced Role' } as any);
const forcedRole = await role(root, { isPublic: true, isPublicDisplayRequired: true, name: 'Forced Role' });
const unassignedRole = await role(root, { isPublic: true, name: 'Unassigned Role' });
await api('admin/roles/assign', { userId: user.id, roleId: visibleRole.id }, root);
@@ -711,11 +711,11 @@ describe('ユーザー', () => {
forcedRole.id,
visibleRole.id,
],
} as any,
},
user,
});
assert.deepStrictEqual((response as any).hiddenRoleIds, [secondVisibleRole.id, visibleRole.id]);
assert.deepStrictEqual(response.hiddenRoleIds, [secondVisibleRole.id, visibleRole.id]);
});
test('をID指定のリスト形式で取得することができる', async () => {
const parameters = { userIds: [] };

View File

@@ -73,6 +73,10 @@ describe('UserEntityService', () => {
let rolesRepository: RolesRepository;
let roleAssignmentsRepository: RoleAssignmentsRepository;
let roleService: RoleService;
type RoleServiceCacheController = {
rolesCache: { delete(): void };
roleAssignmentByUserIdCache: { delete(userId: MiUser['id']): void };
};
async function createUser(userData: Partial<MiUser> = {}, profileData: Partial<MiUserProfile> = {}) {
const un = secureRndstr(16);
@@ -105,7 +109,7 @@ describe('UserEntityService', () => {
})
.then(x => rolesRepository.findOneByOrFail(x.identifiers[0]));
(roleService as any).rolesCache.delete();
(roleService as unknown as RoleServiceCacheController).rolesCache.delete();
return role;
}
@@ -116,7 +120,7 @@ describe('UserEntityService', () => {
roleId: role.id,
});
(roleService as any).roleAssignmentByUserIdCache.delete(user.id);
(roleService as unknown as RoleServiceCacheController).roleAssignmentByUserIdCache.delete(user.id);
}
async function memo(writer: MiUser, target: MiUser, memo: string) {
@@ -309,7 +313,7 @@ describe('UserEntityService', () => {
});
const updatedMe = await usersRepository.findOneByOrFail({ id: me.id });
const actual = await service.pack(updatedMe, updatedMe, { schema: 'MeDetailed' }) as any;
const actual = await service.pack(updatedMe, updatedMe, { schema: 'MeDetailed' });
expect(actual.hiddenRoleIds).toEqual([secondVisibleRole.id, visibleRole.id]);
});
@@ -328,14 +332,14 @@ describe('UserEntityService', () => {
await usersRepository.update(target.id, { hiddenRoleIds: [hiddenRole.id, forcedRole.id] });
const updatedTarget = await usersRepository.findOneByOrFail({ id: target.id });
const normalView = await service.pack(updatedTarget, viewer, { schema: 'UserDetailed' }) as any;
const moderatorView = await service.pack(updatedTarget, moderator, { schema: 'UserDetailed' }) as any;
const normalView = await service.pack(updatedTarget, viewer, { schema: 'UserDetailed' });
const moderatorView = await service.pack(updatedTarget, moderator, { schema: 'UserDetailed' });
expect(normalView.roles.map((role: any) => role.id)).toEqual([visibleRole.id, forcedRole.id]);
expect(normalView.badgeRoles.map((role: any) => role.id)).toEqual([visibleRole.id, forcedRole.id]);
expect(normalView.badgeRoles.every((role: any) => typeof role.id === 'string')).toBe(true);
expect(moderatorView.roles.map((role: any) => role.id)).toEqual([hiddenRole.id, visibleRole.id, forcedRole.id]);
expect(moderatorView.badgeRoles.map((role: any) => role.id)).toEqual([hiddenRole.id, visibleRole.id, forcedRole.id]);
expect(normalView.roles.map(role => role.id)).toEqual([visibleRole.id, forcedRole.id]);
expect(normalView.badgeRoles?.map(role => role.id)).toEqual([visibleRole.id, forcedRole.id]);
expect(normalView.badgeRoles?.every(role => typeof role.id === 'string')).toBe(true);
expect(moderatorView.roles.map(role => role.id)).toEqual([hiddenRole.id, visibleRole.id, forcedRole.id]);
expect(moderatorView.badgeRoles?.map(role => role.id)).toEqual([hiddenRole.id, visibleRole.id, forcedRole.id]);
});
test('alsoKnownAs as string does not throw', async () => {