Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 7 additions & 6 deletions .github/agents/impl.agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ tools:
"todo",
"ms-vscode.vscode-websearchforcopilot/websearch",
]
model: "GPT-5.3-Codex"
model: "GPT-5.4"
---

与えられた実行計画に従って、実装を行ってください。TDD に倣って、以下のステップで実施します。
Expand All @@ -20,11 +20,12 @@ model: "GPT-5.3-Codex"
1. 関連するドキュメントやコード、Issueの内容を確認する
2. 網羅的なテストコードを使い作成する
3. 開発ポリシーに従って #tool:edit などを使い実装する。変更はツールを利用し、 #tool:execute を使ったsedなどは使用しない。
4. テストを #tool:execute などを使い実行し、成功を確認する
5. 成功したらリファクタリングを行う
6. リファクタリング後もテストが成功することを確認する
7. 必要に応じてドキュメントを更新する
8. 実装内容を説明する
4. ある程度の編集粒度で、Gitにコミットする
5. テストを #tool:execute などを使い実行し、成功を確認する
6. 成功したらリファクタリングを行う
7. リファクタリング後もテストが成功することを確認する
8. 内容について、特筆すべき情報がある場合ドキュメントを作成・更新する
9. 実装内容を説明する

## 注意事項

Expand Down
17 changes: 8 additions & 9 deletions .github/agents/issue.agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ tools:
"web",
"ms-vscode.vscode-websearchforcopilot/websearch",
]
model: "GPT-5.3-Codex"
model: "GPT-5.4"
---

あなたは、ユーザーが入力する要望 (issue, bug report, feature request など) をもとに、イシューを管理するエージェントです。以下のステップに基づき、要件と仕様の解像度を高めながら、イシューを管理してください。
Expand All @@ -22,18 +22,17 @@ model: "GPT-5.3-Codex"
3. 現在のローカル レポジトリ状況を確認する
4. 現在の GitHub Issues の状況を確認する
5. #tool:ms-vscode.vscode-websearchforcopilot/websearch でウェブ検索を行い、要件および要件に必要な周辺知識の理解を深める
6. 要件と調査結果に基づき、十分な情報を含めたIssue を作成/更新する
6. 要件と調査結果に基づき、十分な情報を含めたIssue を1つ以上作成/更新する
7. 作成された Issue に対して批判的にレビューを行う
8. レビュー内容に基づき、Issue を改善する
9. `gh`を使用して Issue を作成し、ユーザーに作成した Issue とその内容を報告する
9. `gh`を使用して Issue を作成し、ユーザーに作成したIssueリストとその内容を報告する

## 注意事項

- Issue の作成においては、巨大な要件で1つのIssueを作成するのではなく、必要に応じて複数の Issue に分割することを検討してください
- 既存の Issue と重複する内容がないか確認してください。重複する内容がある場合は、既存の Issue を更新する形で対応してください

## ツール

- #tool:ms-vscode.vscode-websearchforcopilot/websearch: ウェブ検索
- `gh`: GitHub リポジトリの操作

## ドキュメント

- `docs/`
- `README.md`
- `CONTRIBUTING.md`
17 changes: 8 additions & 9 deletions .github/agents/orchestrator.agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,14 @@ model: "GPT-5.3-Codex"

## 手順 (#tool:todo)

要件に応じて、以下のステップを要件を満たすまで繰り返してください。

1. #tool:agent/runSubagent で issue エージェントを呼び出し、イシューを作成する
2. #tool:agent/runSubagent で plan エージェントを呼び出し、実装計画を立てる
3. #tool:agent/runSubagent で impl エージェントを呼び出し、実装を行う
4. #tool:agent/runSubagent で review エージェントを呼び出し、コードレビューと修正を行う
5. 残っている要件に応じてステップ 3 と 4 を繰り返す
6. #tool:agent/runSubagent で pr エージェントを呼び出し、プルリクエストを作成する
7. 実装内容とプルリクエストのリンクをユーザーに通知する
1. #tool:agent/runSubagent で issue エージェントを呼び出し、イシューを1つ以上作成する
2. 作成したイシュー1つずつについて、以下を行い、実装を進める。エージェントにはissueのIDを渡して、イシューの内容を伝える。
- #tool:agent/runSubagent で plan エージェントを呼び出し、実装計画を立てる
- #tool:agent/runSubagent で impl エージェントを呼び出し、実装を行う
- #tool:agent/runSubagent で review エージェントを呼び出し、コードレビューと修正を行う
- レビューにてプルリクエストが作成OKとなるまで、上記の実装とレビューのサイクルを回す
- #tool:agent/runSubagent で pr エージェントを呼び出し、プルリクエストを作成する
3. 実装内容とプルリクエストのリンクをユーザーに通知する

## 注意事項

Expand Down
11 changes: 5 additions & 6 deletions .github/agents/plan.agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ tools:
"web",
"ms-vscode.vscode-websearchforcopilot/websearch",
]
model: "GPT-5.3-Codex"
model: "GPT-5.4"
---

与えられたイシューの実装計画を立ててください。
Expand All @@ -18,9 +18,10 @@ model: "GPT-5.3-Codex"

1. 現在のレポジトリ状況を確認し、リモートとの同期を行う
2. 指定されたイシューの内容を確認する。イシューが存在しない場合は、処理を中止しユーザーに通知する。
3. レポジトリ (コード、ドキュメント) を確認する
3. レポジトリ (仕様、コード、ドキュメント) を確認する
4. ウェブ検索で情報を収集する
5. 実装計画をユーザーに提示する
5. 技術的制約により仕様を変更する場合、仕様ファイル(`spec-*.md`)に反映する
6. 実装計画をユーザーに提示する

## ツール

Expand All @@ -29,9 +30,7 @@ model: "GPT-5.3-Codex"

## ドキュメント

- `docs/`
- `README.md`
- `CONTRIBUTING.md`
- `spec-*.md`

## ブランチ戦略

Expand Down
2 changes: 1 addition & 1 deletion .github/agents/pr.agent.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ tools:
"web",
"ms-vscode.vscode-websearchforcopilot/websearch",
]
model: "GPT-5.3-Codex"
model: "Claude Sonnet 4.6"
---

与えられたイシューと実装に対する、プルリクエストを作成してください。
Expand Down
10 changes: 6 additions & 4 deletions .github/agents/review.agent.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
---
description: 実装内容をレビューし、建設的なフィードバックを提供します。
description: Issueベースで実装内容をレビューし、建設的なフィードバックを提供します。
tools:
[
"execute",
Expand All @@ -9,20 +9,22 @@ tools:
"web",
"ms-vscode.vscode-websearchforcopilot/websearch",
]
model: "GPT-5.3-Codex"
model: "GPT-5.4"
---

実装内容をレビューしてください。批判的に評価を行い、発言についての中立的なレビューを提供してください。新たな情報を検索、分析することを推奨します。あくまでレビューの提供までがあなたの役割です。

## 手順 (#tool:todo)

1. 与えられた課題が何であるかをIssueから理解する
1. 網羅的に情報を収集する
- レポジトリの分析
- ドキュメント群の分析
- ウェブ検索 (#tool:ms-vscode.vscode-websearchforcopilot/websearch) によるベストプラクティス、pitfalls、代替案の調査
- 要件の確認と理解
2. 収集した情報をもとに、実装内容を批判的に評価する (正確性、完全性、一貫性、正当性、妥当性、関連性、明確性、客観性、バイアスの有無、可読性、保守性、テストが十分であるかなどの観点)
3. 改善点や懸念点があれば指摘し、アクションプランを示す
- spec-*.mdに含まれる仕様の確認
1. 収集した情報をもとに、実装内容を批判的に評価する (正確性、完全性、一貫性、正当性、妥当性、関連性、明確性、客観性、バイアスの有無、可読性、保守性、テストが十分であるかなどの観点)
1. 改善点や懸念点があれば指摘し、アクションプランを示す

## ツール

Expand Down
11 changes: 7 additions & 4 deletions apps/backend/src/__tests__/app.issue11.integration.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1211,10 +1211,13 @@ describe('issue #11 api integration', () => {
expect(approveRes.status).toBe(200);
expect(mockSendEmail).toHaveBeenCalledWith({
to: 'owner@approve.example',
subject: 'Approve University の代表者招待',
html: expect.stringMatching(
/^招待リンク: invitation:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/,
),
template: 'university-owner-invitation-link',
payload: {
universityName: 'Approve University',
invitationLink: expect.stringMatching(
/^invitation:[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/,
),
},
});
const approveJson = (await approveRes.json()) as {
data: { status: string; createdOrganizationId: string };
Expand Down
8 changes: 6 additions & 2 deletions apps/backend/src/auth.ts
Original file line number Diff line number Diff line change
Expand Up @@ -37,8 +37,12 @@ export const auth = betterAuth({
const inviteLink = `${env.APP_URL}/invite/${data.id}`;
await emailService.sendEmail({
to: data.email,
subject: `${data.organization.name} への招待`,
html: `${data.inviter.user.name} さんが ${data.organization.name} へ招待しました: ${inviteLink}`,
template: 'organization-invitation',
payload: {
organizationName: data.organization.name,
inviterName: data.inviter.user.name,
inviteLink,
},
});
},
}),
Expand Down
7 changes: 5 additions & 2 deletions apps/backend/src/routes/admin/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -613,8 +613,11 @@ adminRequestRoutes.openapi(approveUniversityRequestRoute, async (c) => {

await emailService.sendEmail({
to: request.representativeEmail,
subject: `${request.universityName} の代表者招待`,
html: `招待リンク: invitation:${invitationId}`,
template: 'university-owner-invitation-link',
payload: {
universityName: request.universityName,
invitationLink: `invitation:${invitationId}`,
},
});

await db
Expand Down
7 changes: 5 additions & 2 deletions apps/backend/src/routes/admin/universities.ts
Original file line number Diff line number Diff line change
Expand Up @@ -134,8 +134,11 @@ adminUniversityRoutes.openapi(createUniversityRoute, async (c) => {

await emailService.sendEmail({
to: body.data.ownerEmail,
subject: `${inserted[0].name} の代表者招待`,
html: `招待ID: ${invitationId}`,
template: 'university-owner-invitation-id',
payload: {
universityName: inserted[0].name,
invitationId,
},
});
}

Expand Down
7 changes: 5 additions & 2 deletions apps/backend/src/routes/university.ts
Original file line number Diff line number Diff line change
Expand Up @@ -381,8 +381,11 @@ universityRoutes.openapi(inviteUniversityRoute, async (c) => {

await emailService.sendEmail({
to: body.data.email,
subject: `${inviter[0]?.orgName ?? organizationId} への招待`,
html: `招待リンク: invitation:${invitationId}`,
template: 'organization-member-invitation-link',
payload: {
organizationName: inviter[0]?.orgName ?? organizationId,
invitationLink: `invitation:${invitationId}`,
},
});

return c.json({ data: inserted[0] }, 201);
Expand Down
35 changes: 34 additions & 1 deletion apps/backend/src/services/email/console.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { describe, expect, it } from 'vitest';
import { describe, expect, it, vi } from 'vitest';
import { ConsoleEmailService } from './console.js';

describe('ConsoleEmailService', () => {
Expand All @@ -12,4 +12,37 @@ describe('ConsoleEmailService', () => {

expect(result.success).toBe(true);
});

it('resolves template-based emails before logging', async () => {
const service = new ConsoleEmailService();
const consoleLogSpy = vi.spyOn(console, 'log').mockImplementation(() => undefined);

const result = await service.sendEmail({
to: 'owner@example.com',
template: 'organization-invitation',
payload: {
organizationName: 'DocShare University',
inviterName: 'Admin User',
inviteLink: 'https://app.example.test/invite/inv-1',
},
});

expect(result.success).toBe(true);
expect(consoleLogSpy).toHaveBeenCalledWith(
'[EMAIL]',
JSON.stringify({
to: 'owner@example.com',
template: 'organization-invitation',
payload: {
organizationName: 'DocShare University',
inviterName: 'Admin User',
inviteLink: 'https://app.example.test/invite/inv-1',
},
subject: 'DocShare University への招待',
html: 'Admin User さんが DocShare University へ招待しました: https://app.example.test/invite/inv-1',
}),
);

consoleLogSpy.mockRestore();
});
});
4 changes: 3 additions & 1 deletion apps/backend/src/services/email/console.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
import type { EmailService, SendEmailParams, SendEmailResult } from './interface.js';
import { resolveSendEmailParams } from './templates.js';

export class ConsoleEmailService implements EmailService {
async sendEmail(params: SendEmailParams): Promise<SendEmailResult> {
console.log('[EMAIL]', JSON.stringify(params));
const resolved = resolveSendEmailParams(params);
console.log('[EMAIL]', JSON.stringify({ ...params, ...resolved }));
return { success: true, messageId: 'console' };
}
}
39 changes: 38 additions & 1 deletion apps/backend/src/services/email/interface.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,47 @@
export type SendEmailParams = {
export type EmailTemplateMap = {
'organization-invitation': {
organizationName: string;
inviterName: string;
inviteLink: string;
};
'organization-member-invitation-link': {
organizationName: string;
invitationLink: string;
};
'university-owner-invitation-link': {
universityName: string;
invitationLink: string;
};
'university-owner-invitation-id': {
universityName: string;
invitationId: string;
};
};

export type EmailTemplateId = keyof EmailTemplateMap;

export type SendEmailContentParams = {
to: string;
subject: string;
html: string;
text?: string;
template?: never;
payload?: never;
};

export type SendEmailTemplateParams = {
[TemplateId in EmailTemplateId]: {
to: string;
template: TemplateId;
payload: EmailTemplateMap[TemplateId];
text?: string;
subject?: never;
html?: never;
};
}[EmailTemplateId];

export type SendEmailParams = SendEmailContentParams | SendEmailTemplateParams;

export type SendEmailResult = {
success: boolean;
messageId?: string;
Expand Down
Loading
Loading