agestra 4.13.0 → 4.13.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -12,7 +12,7 @@
12
12
  "name": "agestra",
13
13
  "source": "./",
14
14
  "description": "Orchestrate Ollama, Gemini, and Codex for multi-AI debates, code review, and cross-validation",
15
- "version": "4.13.0",
15
+ "version": "4.13.1",
16
16
  "author": {
17
17
  "name": "mua-vtuber"
18
18
  },
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agestra",
3
- "version": "4.13.0",
3
+ "version": "4.13.1",
4
4
  "description": "Claude Code plugin — orchestrate Ollama, Gemini, and Codex for multi-AI debates, code review, and cross-validation",
5
5
  "mcpServers": {
6
6
  "agestra": {
package/README.ja.md CHANGED
@@ -50,7 +50,7 @@ npm install -g agestra
50
50
  agestra-install gemini --assets --scope user
51
51
  ```
52
52
 
53
- Gemini ではリポジトリ直下の [GEMINI.md](GEMINI.md) と、[`.gemini/commands/agestra/`](.gemini/commands/agestra) のプロジェクトコマンドを一緒に使います。user scope の `--assets` は Agestra Gemini native extension をインストールします。MCP 登録だけでよい場合は `npm run install:gemini` または `agestra-install gemini` を使ってください。
53
+ Gemini ではリポジトリ直下の [GEMINI.md](GEMINI.md)[`.gemini/commands/agestra/`](.gemini/commands/agestra)、生成された skills を一緒に使います。project scope の `--assets` は管理ファイルを書き込み、user scope の `--assets` は Agestra Gemini native extension をインストールします。MCP 登録だけでよい場合は `npm run install:gemini` または `agestra-install gemini` を使ってください。`npm run install:gemini:assets` はデフォルトで user scope を使うため、チェックアウトから project-scope の管理ファイルを入れる場合は `node scripts/install-host-mcp.mjs gemini --assets --scope project` を実行してください。
54
54
 
55
55
  セットアップ後に利用できる Gemini コマンド:
56
56
 
@@ -59,6 +59,8 @@ Gemini ではリポジトリ直下の [GEMINI.md](GEMINI.md) と、[`.gemini/com
59
59
  - `/agestra:design`
60
60
  - `/agestra:idea`
61
61
  - `/agestra:implement`
62
+ - `/agestra:qa`
63
+ - `/agestra:security`
62
64
 
63
65
  ### 前提条件
64
66
 
@@ -83,9 +85,9 @@ Gemini ではリポジトリ直下の [GEMINI.md](GEMINI.md) と、[`.gemini/com
83
85
 
84
86
  | ホスト | 自然な入口 |
85
87
  |--------|------------|
86
- | Claude Code | `/agestra review`, `/agestra design`, `/agestra idea`, `/agestra implement` |
88
+ | Claude Code | `/agestra setup`, `/agestra review`, `/agestra qa`, `/agestra security`, `/agestra design`, `/agestra idea`, `/agestra implement` |
87
89
  | Codex CLI | `AGENTS.md` に沿った自然言語リクエスト |
88
- | Gemini CLI | `/agestra:review`, `/agestra:design`, `/agestra:idea`, `/agestra:implement` |
90
+ | Gemini CLI | `/agestra:setup`, `/agestra:review`, `/agestra:qa`, `/agestra:security`, `/agestra:design`, `/agestra:idea`, `/agestra:implement` |
89
91
 
90
92
  3 つのホストはすべて同じ MCP サーバーと `commands/*.md` の共通ワークフロー仕様を利用します。
91
93
 
@@ -93,24 +95,29 @@ Gemini ではリポジトリ直下の [GEMINI.md](GEMINI.md) と、[`.gemini/com
93
95
 
94
96
  | コマンド | 説明 |
95
97
  |----------|------|
98
+ | `/agestra setup` | 初期 AI プロバイダー選択とセットアップ |
96
99
  | `/agestra review [target]` | コード品質、セキュリティ、統合の完成度をレビュー |
100
+ | `/agestra qa [target]` | 実装結果を検証し、PASS/FAIL の根拠を生成 |
101
+ | `/agestra security [target]` | 専用のセキュリティレビューを実行 |
97
102
  | `/agestra idea [topic]` | 類似プロジェクトとの比較から改善案を発見 |
98
103
  | `/agestra design [subject]` | 実装前にアーキテクチャと設計上のトレードオフを探索 |
99
- | `/agestra setup` | 初期 AI プロバイダー選択とセットアップ |
100
104
  | `/agestra implement [task]` | Claude only または Multi-AI モードで実装を進める |
101
105
 
102
- 外部プロバイダーが利用可能な場合、テキストコマンド(review、design、idea)は直接徹底討論モードに入り、マルチ AI クロスバリデーションを行います。プロバイダーが検出されない場合、Claude が自動的に単独で作業します。
106
+ 外部プロバイダーが利用可能な場合、review、QA、security、design、idea ワークフローは team-lead を通じてマルチ AI クロスバリデーションへルーティングされます。プロバイダーが検出されない場合、現在のホストのローカル specialist agent が自動的に処理します。
103
107
 
104
108
  ## エージェント
105
109
 
106
110
  | エージェント | モデル | 役割 |
107
111
  |--------------|--------|------|
108
112
  | `agestra-team-lead` | Sonnet | フルオーケストレーター — 環境チェック、品質ベースのプロバイダー選択、作業モード選定、CLI ワーカー監督、QA ループ |
113
+ | `agestra-implementer` | Sonnet | スコープ付き実装実行役 — コード変更、テスト更新、ローカル検証 |
114
+ | `agestra-e2e-writer` | Sonnet | 永続 E2E テスト作成役 — 承認済みブラウザフローテストのみ作成 |
109
115
  | `agestra-reviewer` | Opus | 厳格な品質検証役 — セキュリティ、孤立コード、仕様逸脱、テスト不足を確認 |
110
116
  | `agestra-designer` | Opus | アーキテクチャ探索役 — ソクラテス式質問、トレードオフ分析 |
111
117
  | `agestra-ideator` | Sonnet | 改善案発見役 — Web 調査、競合分析 |
112
118
  | `agestra-moderator` | Sonnet | マルチモード進行役 — 合意検出付きディベート、独立集約、ドキュメントレビュー、衝突解決 |
113
119
  | `agestra-qa` | Opus | QA 検証役 — 設計準拠の確認、PASS/FAIL 判定 |
120
+ | `agestra-security` | Opus | セキュリティレビュー役 — 脅威モデル、認証/データフローリスク、依存関係とシークレット衛生 |
114
121
 
115
122
  ## スキル
116
123
 
@@ -125,6 +132,9 @@ Gemini ではリポジトリ直下の [GEMINI.md](GEMINI.md) と、[`.gemini/com
125
132
  | `design` | Multi-AI モード選択を含む設計探索ワークフロー |
126
133
  | `idea` | Multi-AI モード選択を含む改善案発見ワークフロー |
127
134
  | `review` | Multi-AI モード選択を含むコード品質・セキュリティ・ハードコーディングレビューワークフロー |
135
+ | `qa` | 設計契約検証と PASS/FAIL 根拠生成ワークフロー |
136
+ | `security` | 専用セキュリティレビューワークフロー |
137
+ | `e2e` | 永続ブラウザ E2E テスト作成ワークフロー |
128
138
  | `leader` | マルチAI/プロバイダーオーケストレーションのエントリーポイント — 明示的なプロバイダー、ディベート、合意形成、相互検証シグナルを検知し、ドメイン分類後 `agestra-team-lead` へ委譲 |
129
139
 
130
140
  ---
@@ -147,7 +157,7 @@ Turborepo モノレポで、8 パッケージ構成です:
147
157
 
148
158
  - **Provider abstraction** — すべてのバックエンドは `AIProvider`(`chat`, `healthCheck`, `getCapabilities`)を実装します。新規プロバイダー追加は専用パッケージとファクトリ登録に分離されます。
149
159
  - **Zero-config** — プロバイダーは起動時に自動検出されます。手動設定は不要です。
150
- - **Host-native** — Claude はプラグインバンドル、Codex は `AGENTS.md`、Gemini は `GEMINI.md` とプロジェクトコマンドを使いますが、内部の MCP サーバーとワークフローコアは共通です。
160
+ - **Host-native** — Claude はプラグインバンドル、Codex は `AGENTS.md` と custom agents、Gemini は `GEMINI.md`、commands、skills、または native extension を使います。すべてのホストは同じ MCP サーバーとワークフローコアを共有します。
151
161
  - **Modular dispatch** — 各ツールカテゴリは `getTools()` + `handleTool()` を持つ独立モジュールです。サーバーが動的に収集してディスパッチします。
152
162
  - **Atomic writes** — すべてのファイル操作は一時ファイルへの書き込み後に rename する方式で、破損を防ぎます。
153
163
  - **Dead-end tracking** — 失敗したアプローチは記録され、今後のプロンプトに注入されます。
@@ -155,11 +165,11 @@ Turborepo モノレポで、8 パッケージ構成です:
155
165
 
156
166
  ### 作業モード
157
167
 
158
- **Text work**(レビュー、設計、アイデア): プロバイダーあり → 徹底討論モード; なし → Claude only
168
+ **Text work**(レビュー、QA、セキュリティ、設計、アイデア): プロバイダーあり → 徹底討論モード; なし → Claude only
159
169
 
160
170
  **Implementation work**(team-lead orchestration):
161
- - **Claude만으로** — Claude がプロジェクト/グローバルエージェントを使って直接実装します。
162
- - **다른 AI 함께** — CLI ワーカー(Codex/Gemini)が分離された git worktree で自律的にコーディングし、Ollama が簡単なタスクを担当し、Claude が監督・統合します。
171
+ - **Claude のみ** — Claude がプロジェクト/グローバルエージェントを使って直接実装します。
172
+ - **他の AI と併用** — CLI ワーカー(Codex/Gemini)が分離された git worktree で自律的にコーディングし、Ollama が簡単なタスクを担当し、Claude が監督・統合します。
163
173
 
164
174
  ---
165
175
 
@@ -237,7 +247,7 @@ Turborepo モノレポで、8 パッケージ構成です:
237
247
 
238
248
  | ツール | 説明 |
239
249
  |--------|------|
240
- | `host_assets_status` | Codex custom agents などの生成済みホストネイティブアセットを確認 |
250
+ | `host_assets_status` | Codex custom agents や Gemini assets などの生成済みホストネイティブアセットを確認 |
241
251
  | `host_assets_install` | 管理対象のホストネイティブアセットを明示的にインストールまたは更新 |
242
252
  | `host_assets_uninstall` | Agestra が追跡する管理対象ホストネイティブアセットを削除 |
243
253
 
@@ -326,21 +336,30 @@ agestra/
326
336
  ├── .gemini/
327
337
  │ └── commands/
328
338
  │ └── agestra/
339
+ │ ├── setup.toml # Gemini CLI の /agestra:setup
329
340
  │ ├── review.toml # Gemini CLI の /agestra:review
330
341
  │ ├── design.toml # Gemini CLI の /agestra:design
331
342
  │ ├── idea.toml # Gemini CLI の /agestra:idea
332
- └── implement.toml # Gemini CLI の /agestra:implement
343
+ ├── implement.toml # Gemini CLI の /agestra:implement
344
+ │ ├── qa.toml # Gemini CLI の /agestra:qa
345
+ │ └── security.toml # Gemini CLI の /agestra:security
333
346
  ├── commands/
347
+ │ ├── setup.md # /agestra setup — プロバイダー設定
334
348
  │ ├── review.md # /agestra review — 品質検証
349
+ │ ├── qa.md # /agestra qa — PASS/FAIL 検証
350
+ │ ├── security.md # /agestra security — セキュリティレビュー
335
351
  │ ├── idea.md # /agestra idea — 改善案探索
336
352
  │ ├── design.md # /agestra design — アーキテクチャ探索
337
353
  │ └── implement.md # /agestra implement — 実装ワークフロー
338
354
  ├── agents/
355
+ │ ├── agestra-implementer.md # スコープ付き実装実行役(Sonnet)
356
+ │ ├── agestra-e2e-writer.md # 永続 E2E テスト作成役(Sonnet)
339
357
  │ ├── agestra-reviewer.md # 厳格な品質検証役(Opus)
340
358
  │ ├── agestra-designer.md # アーキテクチャ探索役(Opus)
341
359
  │ ├── agestra-ideator.md # 改善案発見役(Sonnet)
342
360
  │ ├── agestra-moderator.md # マルチモード進行役(Sonnet)
343
361
  │ ├── agestra-qa.md # QA 検証役(Opus、コード書き込みなし)
362
+ │ ├── agestra-security.md # セキュリティレビュー役(Opus)
344
363
  │ └── agestra-team-lead.md # フルオーケストレーター(Sonnet、コード書き込みなし)
345
364
  ├── skills/
346
365
  │ ├── provider-guide.md # プロバイダー選択とモード参照
@@ -352,6 +371,9 @@ agestra/
352
371
  │ ├── design.md # 設計探索ワークフロー
353
372
  │ ├── idea.md # 改善案発見ワークフロー
354
373
  │ ├── review.md # コード品質レビューワークフロー
374
+ │ ├── qa.md # 設計契約 QA ワークフロー
375
+ │ ├── security.md # 専用セキュリティレビューワークフロー
376
+ │ ├── e2e.md # 永続 E2E テスト作成ワークフロー
355
377
  │ └── leader.md # マルチAIオーケストレーションルーター
356
378
  ├── hooks/
357
379
  │ └── user-prompt-submit.md # ツール推奨フック
@@ -403,7 +425,7 @@ npm run uninstall:gemini
403
425
  npm run uninstall:gemini:assets
404
426
  ```
405
427
 
406
- `*:assets` のアンインストールは、ホスト登録と未変更の生成済みホスト資産を一緒に削除します。ユーザーが生成済み資産を編集していた場合、Agestra はそのファイルを残して報告します。グローバル npm インストールでは `agestra-uninstall codex --assets` または `agestra-uninstall gemini --assets --scope user` を使ってください。
428
+ `*:assets` のアンインストールは、ホスト登録と未変更の生成済みホスト資産を一緒に削除します。Codex assets は custom-agent ファイルです。Gemini project-scope assets は管理ファイルで、Gemini user-scope assets は `gemini extensions uninstall agestra` で削除されます。ユーザーが生成済み資産を編集していた場合、Agestra はそのファイルを残して報告します。グローバル npm インストールでは `agestra-uninstall codex --assets` または `agestra-uninstall gemini --assets --scope user` を使ってください。
407
429
 
408
430
  生成済みのプロジェクトデータも削除したい場合は、`.agestra/` ディレクトリを手動で削除してください。
409
431
 
package/README.ko.md CHANGED
@@ -11,70 +11,34 @@ Agestra는 Claude 호스트, Ollama(로컬), Gemini CLI, Codex CLI를 플러그
11
11
 
12
12
  ## 빠른 시작
13
13
 
14
- ### Claude Code
14
+ 먼저 사용할 호스트를 고르세요. 호스트 네이티브 커맨드/에이전트까지 설치하려면 `--assets` 경로를 쓰고, 서버 연결만 필요하면 MCP-only 등록을 쓰면 됩니다.
15
15
 
16
- ```
17
- /plugin marketplace add mua-vtuber/Agestra
18
- /plugin install agestra@agestra
19
- ```
20
-
21
- Claude는 기존 플러그인 설치 UX를 그대로 유지합니다. Agestra가 첫 사용 시 `environment_check`로 사용 가능한 공급자(Claude 호스트, Ollama, Gemini CLI, Codex CLI)를 자동 감지합니다.
22
-
23
- ### Codex CLI
16
+ | 호스트 | 이 저장소에서 설치 | 전역 npm 패키지에서 설치 | `--assets`가 추가하는 것 |
17
+ |--------|--------------------|--------------------------|--------------------------|
18
+ | Claude Code | `/plugin marketplace add mua-vtuber/Agestra` 후 `/plugin install agestra@agestra` | 같은 플러그인 흐름 | 플러그인 번들, 커맨드, 에이전트, hook, MCP 서버 |
19
+ | Codex CLI | `npm run bundle` 후 `npm run install:codex:assets` | `npm install -g agestra` 후 `agestra-install codex --assets` | `.codex/agents/` 아래 생성형 custom agent |
20
+ | Gemini CLI | `npm run bundle` 후 `npm run install:gemini:assets` | `npm install -g agestra` 후 `agestra-install gemini --assets --scope user` | project scope에서는 관리 파일, user scope에서는 native `agestra` Gemini extension |
24
21
 
25
- 저장소 체크아웃 기준:
22
+ MCP-only 등록도 가능합니다:
26
23
 
27
- ```
28
- npm run bundle
29
- npm run install:codex:assets
30
- ```
24
+ | 호스트 | 저장소 패키지 | 체크아웃에서 전역 패키지 등록 |
25
+ |--------|---------------|-------------------------------|
26
+ | Codex CLI | `npm run install:codex` | `npm run install:codex:global` |
27
+ | Gemini CLI | `npm run install:gemini` | `npm run install:gemini:global` |
31
28
 
32
- 전역 npm 패키지 기준:
29
+ Claude는 네이티브 플러그인 UX를 그대로 사용합니다. Codex는 [AGENTS.md](AGENTS.md), 생성된 custom agent, 등록된 `agestra` MCP 서버를 함께 사용합니다. Gemini는 [GEMINI.md](GEMINI.md), `.gemini/commands/agestra/`, 생성된 skills, 그리고 project-scope 관리 파일 또는 user-scope native extension을 함께 사용합니다.
33
30
 
34
- ```
35
- npm install -g agestra
36
- agestra-install codex --assets
37
- ```
31
+ 참고: `npm run install:gemini:assets`는 기본적으로 user scope를 사용합니다. 체크아웃에서 project-scope Gemini 관리 파일을 설치하려면 `node scripts/install-host-mcp.mjs gemini --assets --scope project`를 실행하세요.
38
32
 
39
- 저장소에서 작업 중이지만 전역 npm 패키지를 MCP만 등록하고 싶다면:
40
-
41
- ```
42
- npm run install:codex:global
43
- ```
44
-
45
- Codex는 저장소 루트의 [AGENTS.md](AGENTS.md)와 등록된 `agestra` MCP 서버를 함께 사용합니다. 생성형 Codex custom agent에는 `--assets` 경로가 필요하며, 이 경로가 `.codex/agents/` 아래에 agent를 설치하고 Agestra host-asset manifest에 기록합니다. custom agent 없이 MCP만 등록하려면 `npm run install:codex` 또는 `agestra-install codex`를 사용하세요.
46
-
47
- ### Gemini CLI
48
-
49
- 저장소 체크아웃 기준:
50
-
51
- ```
52
- npm run bundle
53
- npm run install:gemini:assets
54
- ```
55
-
56
- 전역 npm 패키지 기준:
57
-
58
- ```
59
- npm install -g agestra
60
- agestra-install gemini --assets --scope user
61
- ```
62
-
63
- 저장소에서 작업 중이지만 전역 npm 패키지를 MCP만 등록하고 싶다면:
64
-
65
- ```
66
- npm run install:gemini:global
67
- ```
68
-
69
- Gemini는 저장소 루트의 [GEMINI.md](GEMINI.md)와 [`.gemini/commands/agestra/`](.gemini/commands/agestra) 프로젝트 커맨드를 함께 사용합니다. user scope의 `--assets` 경로는 Agestra Gemini native extension을 설치합니다. MCP만 등록하려면 `npm run install:gemini` 또는 `agestra-install gemini`를 사용하세요.
70
-
71
- 설치 후 Gemini에서 사용할 수 있는 명령:
33
+ Assets 설치 Gemini에서 사용할 있는 명령:
72
34
 
73
35
  - `/agestra:setup`
74
36
  - `/agestra:review`
75
37
  - `/agestra:design`
76
38
  - `/agestra:idea`
77
39
  - `/agestra:implement`
40
+ - `/agestra:qa`
41
+ - `/agestra:security`
78
42
 
79
43
  ### 사전 요구사항
80
44
 
@@ -110,9 +74,9 @@ winget install BurntSushi.ripgrep.MSVC
110
74
 
111
75
  | 호스트 | 자연스러운 진입 방식 |
112
76
  |--------|----------------------|
113
- | Claude Code | `/agestra review`, `/agestra design`, `/agestra idea`, `/agestra implement` |
77
+ | Claude Code | `/agestra setup`, `/agestra review`, `/agestra qa`, `/agestra security`, `/agestra design`, `/agestra idea`, `/agestra implement` |
114
78
  | Codex CLI | `AGENTS.md`에 맞춘 자연어 요청 |
115
- | Gemini CLI | `/agestra:review`, `/agestra:design`, `/agestra:idea`, `/agestra:implement` |
79
+ | Gemini CLI | `/agestra:setup`, `/agestra:review`, `/agestra:qa`, `/agestra:security`, `/agestra:design`, `/agestra:idea`, `/agestra:implement` |
116
80
 
117
81
  세 호스트 모두 같은 MCP 서버와 `commands/*.md` 공유 워크플로우를 사용합니다.
118
82
 
@@ -120,24 +84,29 @@ winget install BurntSushi.ripgrep.MSVC
120
84
 
121
85
  | 커맨드 | 설명 |
122
86
  |--------|------|
87
+ | `/agestra setup` | 초기 AI 공급자 선택 및 설정 |
123
88
  | `/agestra review [대상]` | 코드 품질, 보안, 통합 완성도 검증 |
89
+ | `/agestra qa [대상]` | 구현 결과를 검증하고 PASS/FAIL 근거 생성 |
90
+ | `/agestra security [대상]` | 전용 보안 리뷰 실행 |
124
91
  | `/agestra idea [주제]` | 유사 프로젝트 비교를 통한 개선점 발굴 |
125
92
  | `/agestra design [주제]` | 구현 전 아키텍처 및 설계 트레이드오프 탐색 |
126
- | `/agestra setup` | 초기 AI 공급자 선택 및 설정 |
127
93
  | `/agestra implement [작업]` | Claude only 또는 Multi-AI 모드로 실제 구현 진행 |
128
94
 
129
- 외부 공급자가 있으면 텍스트 커맨드(review, design, idea)는 곧바로 끝장토론 모드로 진입하여 멀티 AI 교차 검증을 수행합니다. 공급자가 없으면 자동으로 Claude 단독 모드로 작동합니다.
95
+ 외부 공급자가 있으면 review, QA, security, design, idea 워크플로우는 team-lead를 통해 멀티 AI 교차 검증으로 라우팅됩니다. 공급자가 없으면 현재 호스트의 로컬 specialist agent가 자동으로 처리합니다.
130
96
 
131
97
  ## 에이전트
132
98
 
133
99
  | 에이전트 | 모델 | 역할 |
134
100
  |----------|------|------|
135
101
  | `agestra-team-lead` | Sonnet | 풀 오케스트레이터 — 환경 체크, 품질 기반 공급자 라우팅, 작업 모드 선택, CLI 워커 감독, QA 루프 |
102
+ | `agestra-implementer` | Sonnet | 제한된 구현 실행자 — 코드 수정, 테스트 갱신, 로컬 검증 |
103
+ | `agestra-e2e-writer` | Sonnet | 지속 E2E 테스트 작성자 — 승인된 브라우저 플로우 테스트만 작성 |
136
104
  | `agestra-reviewer` | Opus | 엄격한 품질 검증 — 보안, 고아 시스템, 스펙 이탈, 테스트 공백 |
137
105
  | `agestra-designer` | Opus | 아키텍처 탐색 — 소크라테스식 질문, 트레이드오프 분석 |
138
106
  | `agestra-ideator` | Sonnet | 개선점 발굴 — 웹 리서치, 경쟁 분석 |
139
107
  | `agestra-moderator` | Sonnet | 다목적 진행자 — 합의 검출 토론, 독립 취합, 문서 라운드 리뷰, 충돌 해결 |
140
108
  | `agestra-qa` | Opus | QA 검증 — 설계 준수, PASS/FAIL 판정 |
109
+ | `agestra-security` | Opus | 보안 리뷰 — 위협 모델, 인증/데이터 흐름 위험, 의존성·시크릿 위생 |
141
110
 
142
111
  ## 스킬
143
112
 
@@ -152,6 +121,9 @@ winget install BurntSushi.ripgrep.MSVC
152
121
  | `design` | 멀티 AI 모드 선택이 포함된 아키텍처 탐색 워크플로우 |
153
122
  | `idea` | 멀티 AI 모드 선택이 포함된 개선점 발굴 워크플로우 |
154
123
  | `review` | 멀티 AI 모드 선택이 포함된 코드 품질·보안·하드코딩 리뷰 워크플로우 |
124
+ | `qa` | 설계 계약 검증 및 PASS/FAIL 근거 생성 워크플로우 |
125
+ | `security` | 전용 보안 리뷰 워크플로우 |
126
+ | `e2e` | 지속 브라우저 E2E 테스트 작성 워크플로우 |
155
127
  | `leader` | 멀티 AI/프로바이더 오케스트레이션 진입점 — 명시적인 프로바이더, 토론, 합의, 교차 검증 신호를 잡아 도메인을 분류한 뒤 `agestra-team-lead`에 위임 |
156
128
 
157
129
  ---
@@ -174,7 +146,7 @@ Turborepo 모노레포, 8개 패키지:
174
146
 
175
147
  - **공급자 추상화** — 모든 백엔드가 `AIProvider`(`chat`, `healthCheck`, `getCapabilities`)를 구현. 새 공급자 추가는 전용 패키지 구현과 팩토리 등록으로 분리됩니다.
176
148
  - **제로 설정** — 시작 시 공급자를 자동 감지. 수동 설정 불필요.
177
- - **호스트 네이티브** — Claude는 플러그인 번들을, Codex는 `AGENTS.md`를, Gemini는 `GEMINI.md`와 프로젝트 커맨드를 사용하지만 내부 MCP 서버와 워크플로우 코어는 공통으로 공유합니다.
149
+ - **호스트 네이티브** — Claude는 플러그인 번들을, Codex는 `AGENTS.md`와 custom agents를, Gemini는 `GEMINI.md`, commands, skills 또는 native extension을 사용합니다. 모든 호스트는 같은 MCP 서버와 워크플로우 코어를 공유합니다.
178
150
  - **모듈형 디스패치** — 각 도구 카테고리가 `getTools()` + `handleTool()`을 내보내는 독립 모듈. 서버가 동적으로 수집·디스패치.
179
151
  - **원자적 쓰기** — 모든 파일 연산이 임시 파일 → rename 방식. 크래시 시 손상 방지.
180
152
  - **실패 추적** — 실패한 접근법이 자동 기록, 이후 프롬프트에 주입.
@@ -182,7 +154,7 @@ Turborepo 모노레포, 8개 패키지:
182
154
 
183
155
  ### 작업 모드
184
156
 
185
- **텍스트 작업** (리뷰, 설계, 아이디어): 공급자 있으면 → 끝장토론; 없으면 → Claude only
157
+ **텍스트 작업** (리뷰, QA, 보안, 설계, 아이디어): 공급자 있으면 → 끝장토론; 없으면 → Claude only
186
158
 
187
159
  **구현 작업** (team-lead 오케스트레이션):
188
160
  - **Claude만으로** — Claude가 프로젝트/전역 에이전트를 활용해 직접 구현.
@@ -264,7 +236,7 @@ Turborepo 모노레포, 8개 패키지:
264
236
 
265
237
  | 도구 | 설명 |
266
238
  |------|------|
267
- | `host_assets_status` | Codex custom agents 같은 생성된 호스트 네이티브 자산 상태 확인 |
239
+ | `host_assets_status` | Codex custom agents, Gemini assets 같은 생성된 호스트 네이티브 자산 상태 확인 |
268
240
  | `host_assets_install` | 관리되는 호스트 네이티브 자산을 명시적으로 설치 또는 갱신 |
269
241
  | `host_assets_uninstall` | Agestra가 추적하는 관리형 호스트 네이티브 자산 제거 |
270
242
 
@@ -357,9 +329,14 @@ agestra/
357
329
  │ ├── review.toml # Gemini CLI의 /agestra:review
358
330
  │ ├── design.toml # Gemini CLI의 /agestra:design
359
331
  │ ├── idea.toml # Gemini CLI의 /agestra:idea
360
- └── implement.toml # Gemini CLI의 /agestra:implement
332
+ ├── implement.toml # Gemini CLI의 /agestra:implement
333
+ │ ├── qa.toml # Gemini CLI의 /agestra:qa
334
+ │ └── security.toml # Gemini CLI의 /agestra:security
361
335
  ├── commands/
336
+ │ ├── setup.md # /agestra setup — 공급자 설정
362
337
  │ ├── review.md # /agestra review — 품질 검증
338
+ │ ├── qa.md # /agestra qa — PASS/FAIL 검증
339
+ │ ├── security.md # /agestra security — 보안 리뷰
363
340
  │ ├── idea.md # /agestra idea — 개선점 발굴
364
341
  │ ├── design.md # /agestra design — 아키텍처 탐색
365
342
  │ └── implement.md # /agestra implement — 실제 구현 진행
@@ -367,8 +344,11 @@ agestra/
367
344
  │ ├── agestra-reviewer.md # 엄격한 품질 검증자 (Opus)
368
345
  │ ├── agestra-designer.md # 아키텍처 탐색자 (Opus)
369
346
  │ ├── agestra-ideator.md # 개선점 발굴자 (Sonnet)
347
+ │ ├── agestra-implementer.md # 제한된 구현 실행자 (Sonnet)
348
+ │ ├── agestra-e2e-writer.md # 지속 E2E 테스트 작성자 (Sonnet)
370
349
  │ ├── agestra-moderator.md # 다목적 진행자 (Sonnet)
371
350
  │ ├── agestra-qa.md # QA 검증자 (Opus, 코드 쓰기 불가)
351
+ │ ├── agestra-security.md # 보안 리뷰어 (Opus)
372
352
  │ └── agestra-team-lead.md # 풀 오케스트레이터 (Sonnet, 코드 쓰기 불가)
373
353
  ├── skills/
374
354
  │ ├── provider-guide.md # 공급자 라우팅 및 모드 참조
@@ -380,6 +360,9 @@ agestra/
380
360
  │ ├── design.md # 아키텍처 탐색 워크플로우
381
361
  │ ├── idea.md # 개선점 발굴 워크플로우
382
362
  │ ├── review.md # 코드 품질 리뷰 워크플로우
363
+ │ ├── qa.md # 설계 계약 QA 워크플로우
364
+ │ ├── security.md # 전용 보안 리뷰 워크플로우
365
+ │ ├── e2e.md # 지속 E2E 테스트 작성 워크플로우
383
366
  │ └── leader.md # 멀티 AI 오케스트레이션 라우터
384
367
  ├── hooks/
385
368
  │ └── user-prompt-submit.md # 도구 추천 hook
@@ -431,7 +414,7 @@ npm run uninstall:gemini
431
414
  npm run uninstall:gemini:assets
432
415
  ```
433
416
 
434
- `*:assets` 제거 명령은 호스트 등록과 변경되지 않은 생성형 호스트 자산을 함께 제거합니다. 사용자가 생성된 자산을 수정했다면 Agestra는 삭제하지 않고 남겨둔 파일을 보고합니다. 전역 npm 설치에서는 `agestra-uninstall codex --assets` 또는 `agestra-uninstall gemini --assets --scope user`를 사용하세요.
417
+ `*:assets` 제거 명령은 호스트 등록과 변경되지 않은 생성형 호스트 자산을 함께 제거합니다. Codex 자산은 custom-agent 파일입니다. Gemini project-scope 자산은 관리 파일이고, Gemini user-scope 자산은 `gemini extensions uninstall agestra`로 제거됩니다. 사용자가 생성된 자산을 수정했다면 Agestra는 삭제하지 않고 남겨둔 파일을 보고합니다. 전역 npm 설치에서는 `agestra-uninstall codex --assets` 또는 `agestra-uninstall gemini --assets --scope user`를 사용하세요.
435
418
 
436
419
  프로젝트에 생성된 데이터까지 지우려면 `.agestra/` 디렉터리를 수동으로 삭제하세요.
437
420
 
package/README.md CHANGED
@@ -11,70 +11,34 @@ Agestra connects the Claude host, Ollama (local), Gemini CLI, and Codex CLI as p
11
11
 
12
12
  ## Quick Start
13
13
 
14
- ### Claude Code
14
+ Pick the host you work in first. Use the `--assets` path when you want the host-native commands/agents installed too; use MCP-only registration when you only need the server connection.
15
15
 
16
- ```
17
- /plugin marketplace add mua-vtuber/Agestra
18
- /plugin install agestra@agestra
19
- ```
20
-
21
- Claude keeps the existing plugin UX. Agestra auto-detects available providers (Claude host, Ollama, Gemini CLI, Codex CLI) on first use via `environment_check`.
22
-
23
- ### Codex CLI
16
+ | Host | From this repository | From global npm | What `--assets` adds |
17
+ |------|----------------------|-----------------|----------------------|
18
+ | Claude Code | `/plugin marketplace add mua-vtuber/Agestra` then `/plugin install agestra@agestra` | same plugin flow | Plugin bundle, commands, agents, hooks, and MCP server together |
19
+ | Codex CLI | `npm run bundle` then `npm run install:codex:assets` | `npm install -g agestra` then `agestra-install codex --assets` | Generated custom agents under `.codex/agents/` |
20
+ | Gemini CLI | `npm run bundle` then `npm run install:gemini:assets` | `npm install -g agestra` then `agestra-install gemini --assets --scope user` | Project assets for project scope, or the native `agestra` Gemini extension for user scope |
24
21
 
25
- Repository checkout:
22
+ MCP-only registration is also available:
26
23
 
27
- ```
28
- npm run bundle
29
- npm run install:codex:assets
30
- ```
24
+ | Host | Repository package | Global package from a checkout |
25
+ |------|--------------------|--------------------------------|
26
+ | Codex CLI | `npm run install:codex` | `npm run install:codex:global` |
27
+ | Gemini CLI | `npm run install:gemini` | `npm run install:gemini:global` |
31
28
 
32
- Global npm package:
29
+ Claude keeps the native plugin UX. Codex combines [AGENTS.md](AGENTS.md), generated custom agents, and the registered `agestra` MCP server. Gemini combines [GEMINI.md](GEMINI.md), `.gemini/commands/agestra/`, generated skills, and either project-scope managed files or a user-scope native extension.
33
30
 
34
- ```
35
- npm install -g agestra
36
- agestra-install codex --assets
37
- ```
31
+ Note: `npm run install:gemini:assets` uses user scope by default. For project-scope managed Gemini files from a checkout, run `node scripts/install-host-mcp.mjs gemini --assets --scope project`.
38
32
 
39
- If you want MCP-only registration of the global package while working from a repo checkout, use:
40
-
41
- ```
42
- npm run install:codex:global
43
- ```
44
-
45
- Codex uses the repository-level [AGENTS.md](AGENTS.md) instructions plus the registered `agestra` MCP server. The `--assets` path is required for generated Codex custom agents; it installs them under `.codex/agents/` and tracks them in the Agestra host-asset manifest. For MCP-only registration without custom agents, use `npm run install:codex` or `agestra-install codex`.
46
-
47
- ### Gemini CLI
48
-
49
- Repository checkout:
50
-
51
- ```
52
- npm run bundle
53
- npm run install:gemini:assets
54
- ```
55
-
56
- Global npm package:
57
-
58
- ```
59
- npm install -g agestra
60
- agestra-install gemini --assets --scope user
61
- ```
62
-
63
- If you want MCP-only registration of the global package while working from a repo checkout, use:
64
-
65
- ```
66
- npm run install:gemini:global
67
- ```
68
-
69
- Gemini uses the repository-level [GEMINI.md](GEMINI.md) context file plus project commands under [`.gemini/commands/agestra/`](.gemini/commands/agestra). The user-scope `--assets` path installs the Agestra Gemini native extension. For MCP-only registration, use `npm run install:gemini` or `agestra-install gemini`.
70
-
71
- Available Gemini commands after setup:
33
+ Available Gemini commands after asset setup:
72
34
 
73
35
  - `/agestra:setup`
74
36
  - `/agestra:review`
75
37
  - `/agestra:design`
76
38
  - `/agestra:idea`
77
39
  - `/agestra:implement`
40
+ - `/agestra:qa`
41
+ - `/agestra:security`
78
42
 
79
43
  ### Prerequisites
80
44
 
@@ -110,9 +74,9 @@ winget install BurntSushi.ripgrep.MSVC
110
74
 
111
75
  | Host | Natural entrypoint |
112
76
  |------|--------------------|
113
- | Claude Code | `/agestra review`, `/agestra design`, `/agestra idea`, `/agestra implement` |
77
+ | Claude Code | `/agestra setup`, `/agestra review`, `/agestra qa`, `/agestra security`, `/agestra design`, `/agestra idea`, `/agestra implement` |
114
78
  | Codex CLI | Plain-language requests guided by `AGENTS.md` |
115
- | Gemini CLI | `/agestra:review`, `/agestra:design`, `/agestra:idea`, `/agestra:implement` |
79
+ | Gemini CLI | `/agestra:setup`, `/agestra:review`, `/agestra:qa`, `/agestra:security`, `/agestra:design`, `/agestra:idea`, `/agestra:implement` |
116
80
 
117
81
  All three hosts drive the same MCP server and shared workflow specs from `commands/*.md`.
118
82
 
@@ -120,13 +84,15 @@ All three hosts drive the same MCP server and shared workflow specs from `comman
120
84
 
121
85
  | Command | Description |
122
86
  |---------|-------------|
87
+ | `/agestra setup` | Initial AI provider selection and setup |
123
88
  | `/agestra review [target]` | Review code quality, security, and integration completeness |
89
+ | `/agestra qa [target]` | Verify implementation results and produce PASS/FAIL evidence |
90
+ | `/agestra security [target]` | Run a dedicated security review |
124
91
  | `/agestra idea [topic]` | Discover improvements by comparing with similar projects |
125
92
  | `/agestra design [subject]` | Explore architecture and design trade-offs before implementation |
126
- | `/agestra setup` | Initial AI provider selection and setup |
127
93
  | `/agestra implement [task]` | Execute implementation through Leader-host-only or suggested AI distribution mode |
128
94
 
129
- When external providers are available, text commands (review, design, idea) go directly to consensus debate mode for multi-AI cross-validation. When no providers are detected, the current leader host works with its local specialist agent automatically. Implementation requests first classify the task and can ask whether to propose an AI task distribution.
95
+ When external providers are available, review, QA, security, design, and idea workflows route through the team lead for multi-AI cross-validation. When no providers are detected, the current leader host works with its local specialist agent automatically. Implementation requests first classify the task and can ask whether to propose an AI task distribution.
130
96
 
131
97
  ## Agents
132
98
 
@@ -134,11 +100,13 @@ When external providers are available, text commands (review, design, idea) go d
134
100
  |-------|-------|------|
135
101
  | `agestra-team-lead` | Sonnet | Full orchestrator — environment check, quality-based provider routing, work mode selection, CLI worker supervision, QA loop |
136
102
  | `agestra-implementer` | Sonnet | Scoped implementation executor — code edits, test updates, local verification |
103
+ | `agestra-e2e-writer` | Sonnet | Persistent E2E test writer — creates approved browser-flow tests without changing product behavior |
137
104
  | `agestra-reviewer` | Opus | Strict quality verifier — security, orphans, spec drift, test gaps |
138
105
  | `agestra-designer` | Opus | Architecture explorer — Socratic questioning, trade-off analysis |
139
106
  | `agestra-ideator` | Sonnet | Improvement discoverer — web research, competitive analysis |
140
107
  | `agestra-moderator` | Sonnet | Multi-mode facilitator — debate with consensus detection, independent aggregation, document review, conflict resolution |
141
108
  | `agestra-qa` | Opus | QA verifier — design compliance, PASS/FAIL judgment |
109
+ | `agestra-security` | Opus | Security reviewer — threat model, auth/data-flow risk, dependency and secret hygiene |
142
110
 
143
111
  ## Skills
144
112
 
@@ -153,6 +121,9 @@ When external providers are available, text commands (review, design, idea) go d
153
121
  | `design` | Architecture exploration workflow with multi-AI mode selection |
154
122
  | `idea` | Improvement discovery workflow with multi-AI mode selection |
155
123
  | `review` | Code quality / security / hardcoding review workflow with multi-AI mode selection |
124
+ | `qa` | Design-contract verification and PASS/FAIL evidence workflow |
125
+ | `security` | Dedicated security review workflow |
126
+ | `e2e` | Persistent browser E2E test-authoring workflow |
156
127
  | `leader` | Multi-AI/provider orchestration entry — catches explicit provider, debate, consensus, or cross-validation signals, classifies the domain, and hands off to `agestra-team-lead` |
157
128
 
158
129
  ---
@@ -175,7 +146,7 @@ Turborepo monorepo with 8 packages:
175
146
 
176
147
  - **Provider abstraction** — All backends implement `AIProvider` (`chat`, `healthCheck`, `getCapabilities`). Adding a new provider is isolated to a provider package plus factory registration.
177
148
  - **Zero-config** — Providers are auto-detected at startup. No manual configuration required.
178
- - **Host-native** — Claude uses the plugin bundle, Codex uses `AGENTS.md`, and Gemini uses `GEMINI.md` plus project commands, while all three share the same MCP server and workflow core.
149
+ - **Host-native** — Claude uses the plugin bundle, Codex uses `AGENTS.md` plus custom agents, and Gemini uses `GEMINI.md`, commands, skills, or the native extension. All hosts share the same MCP server and workflow core.
179
150
  - **Modular dispatch** — Each tool category is an independent module with `getTools()` + `handleTool()`. The server collects and dispatches dynamically.
180
151
  - **Atomic writes** — All file operations use write-to-temp-then-rename to prevent corruption.
181
152
  - **Dead-end tracking** — Failed approaches are recorded and injected into future prompts.
@@ -183,7 +154,7 @@ Turborepo monorepo with 8 packages:
183
154
 
184
155
  ### Work Modes
185
156
 
186
- **Text work** (review, design, idea): providers available → consensus debate mode; no providers → Leader-host only
157
+ **Text work** (review, QA, security, design, idea): providers available → consensus debate mode; no providers → Leader-host only
187
158
 
188
159
  **Implementation work** (team-lead orchestration):
189
160
  - **Leader-host only** — `agestra-implementer` applies scoped code changes; reviewer/QA verify.
@@ -265,7 +236,7 @@ Turborepo monorepo with 8 packages:
265
236
 
266
237
  | Tool | Description |
267
238
  |------|-------------|
268
- | `host_assets_status` | Check generated host-native assets such as Codex custom agents |
239
+ | `host_assets_status` | Check generated host-native assets such as Codex custom agents and Gemini assets |
269
240
  | `host_assets_install` | Explicitly install or refresh managed host-native assets |
270
241
  | `host_assets_uninstall` | Remove managed host-native assets tracked by Agestra |
271
242
 
@@ -358,9 +329,14 @@ agestra/
358
329
  │ ├── review.toml # /agestra:review in Gemini CLI
359
330
  │ ├── design.toml # /agestra:design in Gemini CLI
360
331
  │ ├── idea.toml # /agestra:idea in Gemini CLI
361
- └── implement.toml # /agestra:implement in Gemini CLI
332
+ ├── implement.toml # /agestra:implement in Gemini CLI
333
+ │ ├── qa.toml # /agestra:qa in Gemini CLI
334
+ │ └── security.toml # /agestra:security in Gemini CLI
362
335
  ├── commands/
336
+ │ ├── setup.md # /agestra setup — provider setup
363
337
  │ ├── review.md # /agestra review — quality verification
338
+ │ ├── qa.md # /agestra qa — PASS/FAIL verification
339
+ │ ├── security.md # /agestra security — security review
364
340
  │ ├── idea.md # /agestra idea — improvement discovery
365
341
  │ ├── design.md # /agestra design — architecture exploration
366
342
  │ └── implement.md # /agestra implement — execution workflow
@@ -369,8 +345,10 @@ agestra/
369
345
  │ ├── agestra-designer.md # Architecture explorer (Opus)
370
346
  │ ├── agestra-ideator.md # Improvement discoverer (Sonnet)
371
347
  │ ├── agestra-implementer.md # Scoped implementation executor (Sonnet)
348
+ │ ├── agestra-e2e-writer.md # Persistent E2E test writer (Sonnet)
372
349
  │ ├── agestra-moderator.md # Multi-mode facilitator (Sonnet)
373
350
  │ ├── agestra-qa.md # QA verifier (Opus, no code writes)
351
+ │ ├── agestra-security.md # Security reviewer (Opus)
374
352
  │ └── agestra-team-lead.md # Full orchestrator (Sonnet, no code writes)
375
353
  ├── skills/
376
354
  │ ├── provider-guide.md # Provider routing and mode reference
@@ -382,6 +360,9 @@ agestra/
382
360
  │ ├── design.md # Architecture exploration workflow
383
361
  │ ├── idea.md # Improvement discovery workflow
384
362
  │ ├── review.md # Code quality review workflow
363
+ │ ├── qa.md # Design-contract QA workflow
364
+ │ ├── security.md # Dedicated security review workflow
365
+ │ ├── e2e.md # Persistent E2E test-writing workflow
385
366
  │ └── leader.md # Multi-AI orchestration entry router
386
367
  ├── hooks/
387
368
  │ └── user-prompt-submit.md # Tool recommendation hook
@@ -433,7 +414,7 @@ npm run uninstall:gemini
433
414
  npm run uninstall:gemini:assets
434
415
  ```
435
416
 
436
- The `*:assets` uninstall commands remove both the host registration and unchanged generated host assets. If a generated asset was edited by the user, Agestra leaves it in place and reports it. For a global npm install, use `agestra-uninstall codex --assets` or `agestra-uninstall gemini --assets --scope user`.
417
+ The `*:assets` uninstall commands remove both the host registration and unchanged generated host assets. Codex assets are custom-agent files. Gemini project-scope assets are managed files; Gemini user-scope assets are removed through `gemini extensions uninstall agestra`. If a generated asset was edited by the user, Agestra leaves it in place and reports it. For a global npm install, use `agestra-uninstall codex --assets` or `agestra-uninstall gemini --assets --scope user`.
437
418
 
438
419
  If you also want to delete generated project data, remove the `.agestra/` directory manually.
439
420
 
package/README.zh.md CHANGED
@@ -50,7 +50,7 @@ npm install -g agestra
50
50
  agestra-install gemini --assets --scope user
51
51
  ```
52
52
 
53
- Gemini 会结合仓库根目录下的 [GEMINI.md](GEMINI.md)[`.gemini/commands/agestra/`](.gemini/commands/agestra) 项目命令一起工作。user scope 的 `--assets` 会安装 Agestra Gemini native extension。只注册 MCP 时,请使用 `npm run install:gemini` 或 `agestra-install gemini`。
53
+ Gemini 会结合仓库根目录下的 [GEMINI.md](GEMINI.md)[`.gemini/commands/agestra/`](.gemini/commands/agestra) 和生成的 skills 一起工作。project scope 的 `--assets` 会写入受管文件,user scope 的 `--assets` 会安装 Agestra Gemini native extension。只注册 MCP 时,请使用 `npm run install:gemini` 或 `agestra-install gemini`。`npm run install:gemini:assets` 默认使用 user scope;如果要从 checkout 安装 project-scope 受管文件,请运行 `node scripts/install-host-mcp.mjs gemini --assets --scope project`。
54
54
 
55
55
  安装后可用的 Gemini 命令:
56
56
 
@@ -59,6 +59,8 @@ Gemini 会结合仓库根目录下的 [GEMINI.md](GEMINI.md) 与 [`.gemini/comma
59
59
  - `/agestra:design`
60
60
  - `/agestra:idea`
61
61
  - `/agestra:implement`
62
+ - `/agestra:qa`
63
+ - `/agestra:security`
62
64
 
63
65
  ### 前置条件
64
66
 
@@ -83,9 +85,9 @@ Gemini 会结合仓库根目录下的 [GEMINI.md](GEMINI.md) 与 [`.gemini/comma
83
85
 
84
86
  | 宿主 | 自然入口 |
85
87
  |------|----------|
86
- | Claude Code | `/agestra review`, `/agestra design`, `/agestra idea`, `/agestra implement` |
88
+ | Claude Code | `/agestra setup`, `/agestra review`, `/agestra qa`, `/agestra security`, `/agestra design`, `/agestra idea`, `/agestra implement` |
87
89
  | Codex CLI | 按 `AGENTS.md` 指引直接用自然语言发起请求 |
88
- | Gemini CLI | `/agestra:review`, `/agestra:design`, `/agestra:idea`, `/agestra:implement` |
90
+ | Gemini CLI | `/agestra:setup`, `/agestra:review`, `/agestra:qa`, `/agestra:security`, `/agestra:design`, `/agestra:idea`, `/agestra:implement` |
89
91
 
90
92
  三种宿主都会驱动同一个 MCP 服务,并共享 `commands/*.md` 中的工作流规范。
91
93
 
@@ -93,24 +95,29 @@ Gemini 会结合仓库根目录下的 [GEMINI.md](GEMINI.md) 与 [`.gemini/comma
93
95
 
94
96
  | 命令 | 说明 |
95
97
  |------|------|
98
+ | `/agestra setup` | 初始 AI 提供方选择与设置 |
96
99
  | `/agestra review [target]` | 审查代码质量、安全性和集成完成度 |
100
+ | `/agestra qa [target]` | 验证实现结果并生成 PASS/FAIL 证据 |
101
+ | `/agestra security [target]` | 执行专门的安全审查 |
97
102
  | `/agestra idea [topic]` | 通过与相似项目对比发掘改进点 |
98
103
  | `/agestra design [subject]` | 在实现前探索架构与设计取舍 |
99
- | `/agestra setup` | 初始 AI 提供方选择与设置 |
100
104
  | `/agestra implement [task]` | 以 Claude only 或 Multi-AI 模式执行实现 |
101
105
 
102
- 当外部提供方可用时,文本命令(review、design、idea)直接进入终极辩论模式,进行多 AI 交叉验证。当未检测到提供方时,Claude 自动独立工作。
106
+ 当外部提供方可用时,review、QA、security、design、idea 工作流会经由 team-lead 进入多 AI 交叉验证。当未检测到提供方时,当前宿主的本地 specialist agent 会自动处理。
103
107
 
104
108
  ## 代理
105
109
 
106
110
  | 代理 | 模型 | 角色 |
107
111
  |------|------|------|
108
112
  | `agestra-team-lead` | Sonnet | 全局编排者:环境检查、按质量路由提供方、选择工作模式、监督 CLI Worker、驱动 QA 循环 |
113
+ | `agestra-implementer` | Sonnet | 有范围的实现执行者:代码修改、测试更新、本地验证 |
114
+ | `agestra-e2e-writer` | Sonnet | 持久 E2E 测试作者:只编写已批准的浏览器流程测试 |
109
115
  | `agestra-reviewer` | Opus | 严格质量审查者:关注安全、孤立实现、规格漂移、测试缺口 |
110
116
  | `agestra-designer` | Opus | 架构探索者:苏格拉底式提问、权衡分析 |
111
117
  | `agestra-ideator` | Sonnet | 改进点发现者:Web 调研、竞品分析 |
112
118
  | `agestra-moderator` | Sonnet | 多模式主持者:带共识检测的辩论、独立汇总、文档审查、冲突解决 |
113
119
  | `agestra-qa` | Opus | QA 验证者:检查设计符合性并给出 PASS/FAIL 判断 |
120
+ | `agestra-security` | Opus | 安全审查者:威胁模型、认证/数据流风险、依赖与密钥卫生 |
114
121
 
115
122
  ## 技能
116
123
 
@@ -125,6 +132,9 @@ Gemini 会结合仓库根目录下的 [GEMINI.md](GEMINI.md) 与 [`.gemini/comma
125
132
  | `design` | 包含 Multi-AI 模式选择的架构探索工作流 |
126
133
  | `idea` | 包含 Multi-AI 模式选择的改进发现工作流 |
127
134
  | `review` | 包含 Multi-AI 模式选择的代码质量·安全·硬编码审查工作流 |
135
+ | `qa` | 设计契约验证与 PASS/FAIL 证据工作流 |
136
+ | `security` | 专门安全审查工作流 |
137
+ | `e2e` | 持久浏览器 E2E 测试编写工作流 |
128
138
  | `leader` | 多AI/提供方编排入口 — 捕获明确的提供方、辩论、共识或交叉验证信号,进行领域分类后委托给 `agestra-team-lead` |
129
139
 
130
140
  ---
@@ -147,7 +157,7 @@ Gemini 会结合仓库根目录下的 [GEMINI.md](GEMINI.md) 与 [`.gemini/comma
147
157
 
148
158
  - **Provider abstraction** — 所有后端都实现 `AIProvider`(`chat`、`healthCheck`、`getCapabilities`)。新增提供方只需新增一个 provider 包并注册工厂。
149
159
  - **Zero-config** — 启动时自动检测提供方,无需手动配置。
150
- - **Host-native** — Claude 使用插件包,Codex 使用 `AGENTS.md`,Gemini 使用 `GEMINI.md` 和项目命令,但三者共享同一套 MCP 服务与工作流核心。
160
+ - **Host-native** — Claude 使用插件包,Codex 使用 `AGENTS.md` 和 custom agents,Gemini 使用 `GEMINI.md`、commands、skills native extension。所有宿主共享同一套 MCP 服务与工作流核心。
151
161
  - **Modular dispatch** — 每类工具都是独立模块,对外提供 `getTools()` 和 `handleTool()`。服务端负责动态收集与分发。
152
162
  - **Atomic writes** — 所有文件操作都采用“写临时文件再重命名”的方式,避免损坏。
153
163
  - **Dead-end tracking** — 失败方案会被记录,并注入后续提示词。
@@ -155,11 +165,11 @@ Gemini 会结合仓库根目录下的 [GEMINI.md](GEMINI.md) 与 [`.gemini/comma
155
165
 
156
166
  ### 工作模式
157
167
 
158
- **文本工作**(review、design、idea):有提供方 → 终极辩论模式;无提供方 → Claude only
168
+ **文本工作**(review、QA、security、design、idea):有提供方 → 终极辩论模式;无提供方 → Claude only
159
169
 
160
170
  **实现工作**(team-lead orchestration):
161
- - **Claude만으로** — Claude 直接结合项目/全局代理完成实现。
162
- - **다른 AI 함께** — CLI Worker(Codex/Gemini)在隔离的 git worktree 中自主编码,Ollama 处理简单任务,Claude 负责监督与合并。
171
+ - **仅 Claude** — Claude 直接结合项目/全局代理完成实现。
172
+ - **与其他 AI 一起** — CLI Worker(Codex/Gemini)在隔离的 git worktree 中自主编码,Ollama 处理简单任务,Claude 负责监督与合并。
163
173
 
164
174
  ---
165
175
 
@@ -237,7 +247,7 @@ Gemini 会结合仓库根目录下的 [GEMINI.md](GEMINI.md) 与 [`.gemini/comma
237
247
 
238
248
  | 工具 | 说明 |
239
249
  |------|------|
240
- | `host_assets_status` | 检查 Codex custom agents 等生成的宿主原生资产 |
250
+ | `host_assets_status` | 检查 Codex custom agents、Gemini assets 等生成的宿主原生资产 |
241
251
  | `host_assets_install` | 显式安装或刷新受管宿主原生资产 |
242
252
  | `host_assets_uninstall` | 移除 Agestra 追踪的受管宿主原生资产 |
243
253
 
@@ -326,21 +336,30 @@ agestra/
326
336
  ├── .gemini/
327
337
  │ └── commands/
328
338
  │ └── agestra/
339
+ │ ├── setup.toml # Gemini CLI 的 /agestra:setup
329
340
  │ ├── review.toml # Gemini CLI 的 /agestra:review
330
341
  │ ├── design.toml # Gemini CLI 的 /agestra:design
331
342
  │ ├── idea.toml # Gemini CLI 的 /agestra:idea
332
- └── implement.toml # Gemini CLI 的 /agestra:implement
343
+ ├── implement.toml # Gemini CLI 的 /agestra:implement
344
+ │ ├── qa.toml # Gemini CLI 的 /agestra:qa
345
+ │ └── security.toml # Gemini CLI 的 /agestra:security
333
346
  ├── commands/
347
+ │ ├── setup.md # /agestra setup — 提供方设置
334
348
  │ ├── review.md # /agestra review — 质量验证
349
+ │ ├── qa.md # /agestra qa — PASS/FAIL 验证
350
+ │ ├── security.md # /agestra security — 安全审查
335
351
  │ ├── idea.md # /agestra idea — 改进点发现
336
352
  │ ├── design.md # /agestra design — 架构探索
337
353
  │ └── implement.md # /agestra implement — 实现工作流
338
354
  ├── agents/
355
+ │ ├── agestra-implementer.md # 有范围的实现执行者(Sonnet)
356
+ │ ├── agestra-e2e-writer.md # 持久 E2E 测试作者(Sonnet)
339
357
  │ ├── agestra-reviewer.md # 严格质量审查者(Opus)
340
358
  │ ├── agestra-designer.md # 架构探索者(Opus)
341
359
  │ ├── agestra-ideator.md # 改进点发现者(Sonnet)
342
360
  │ ├── agestra-moderator.md # 多模式主持者(Sonnet)
343
361
  │ ├── agestra-qa.md # QA 验证者(Opus,不写代码)
362
+ │ ├── agestra-security.md # 安全审查者(Opus)
344
363
  │ └── agestra-team-lead.md # 全局编排者(Sonnet,不写代码)
345
364
  ├── skills/
346
365
  │ ├── provider-guide.md # 提供方路由与模式说明
@@ -352,6 +371,9 @@ agestra/
352
371
  │ ├── design.md # 架构探索工作流
353
372
  │ ├── idea.md # 改进发现工作流
354
373
  │ ├── review.md # 代码质量审查工作流
374
+ │ ├── qa.md # 设计契约 QA 工作流
375
+ │ ├── security.md # 专门安全审查工作流
376
+ │ ├── e2e.md # 持久 E2E 测试编写工作流
355
377
  │ └── leader.md # 多AI 编排路由器
356
378
  ├── hooks/
357
379
  │ └── user-prompt-submit.md # 工具推荐 hook
@@ -403,7 +425,7 @@ npm run uninstall:gemini
403
425
  npm run uninstall:gemini:assets
404
426
  ```
405
427
 
406
- `*:assets` 卸载命令会同时移除宿主注册和未修改的生成宿主资产。如果用户编辑过生成资产,Agestra 会保留该文件并报告。使用全局 npm 安装时,请运行 `agestra-uninstall codex --assets` 或 `agestra-uninstall gemini --assets --scope user`。
428
+ `*:assets` 卸载命令会同时移除宿主注册和未修改的生成宿主资产。Codex assets 是 custom-agent 文件。Gemini project-scope assets 是受管文件,Gemini user-scope assets 通过 `gemini extensions uninstall agestra` 移除。如果用户编辑过生成资产,Agestra 会保留该文件并报告。使用全局 npm 安装时,请运行 `agestra-uninstall codex --assets` 或 `agestra-uninstall gemini --assets --scope user`。
407
429
 
408
430
  如果还想删除生成的项目数据,请手动删除 `.agestra/` 目录。
409
431
 
@@ -36,7 +36,7 @@ description: |
36
36
  model: opus
37
37
  color: blue
38
38
  codexSandboxMode: workspace-write
39
- disallowedTools: Edit, NotebookEdit
39
+ tools: Read, Glob, Grep, Bash, WebFetch, WebSearch, TodoWrite, AskUserQuestion, Skill, ToolSearch, CronCreate, CronList, CronDelete, Agent, Write
40
40
  ---
41
41
 
42
42
  <Role>
@@ -35,7 +35,7 @@ description: |
35
35
  model: sonnet
36
36
  color: green
37
37
  codexSandboxMode: workspace-write
38
- disallowedTools: Edit, NotebookEdit
38
+ tools: Read, Glob, Grep, Bash, WebFetch, WebSearch, TodoWrite, AskUserQuestion, Skill, ToolSearch, CronCreate, CronList, CronDelete, Agent, Write
39
39
  ---
40
40
 
41
41
  <Role>
@@ -61,7 +61,7 @@ description: |
61
61
  model: sonnet
62
62
  color: cyan
63
63
  codexSandboxMode: read-only
64
- disallowedTools: Edit, NotebookEdit
64
+ tools: Read, Glob, Grep, Bash, WebFetch, WebSearch, TodoWrite, AskUserQuestion, Skill, ToolSearch, CronCreate, CronList, CronDelete, Agent, mcp__plugin_agestra_agestra__provider_list, mcp__plugin_agestra_agestra__agent_debate_structured, mcp__plugin_agestra_agestra__agent_debate_status, mcp__plugin_agestra_agestra__agent_debate_approve, mcp__plugin_agestra_agestra__agent_debate_continue, mcp__plugin_agestra_agestra__agent_debate_reject, mcp__plugin_agestra_agestra__agent_debate_review, mcp__plugin_agestra_agestra__ai_chat, mcp__plugin_agestra_agestra__workspace_read, mcp__plugin_agestra_agestra__workspace_create_document
65
65
  ---
66
66
 
67
67
  <Role>
@@ -512,5 +512,4 @@ If `max_rounds` is hit with open proposals, the moderator surfaces the choice to
512
512
  - `ai_chat` — query individual providers for feedback (Independent Aggregation mode).
513
513
  - `workspace_create_document` — create analysis or aggregated documents (Independent Aggregation mode).
514
514
  - `workspace_read` — read individual provider documents by ID (Independent Aggregation mode).
515
- - `workspace_replace_document_content` — replace generated debate or synthesis markdown when the engine regenerates output from the ledger.
516
515
  </Tool_Usage>
@@ -39,7 +39,7 @@ description: |
39
39
  model: opus
40
40
  color: yellow
41
41
  codexSandboxMode: workspace-write
42
- disallowedTools: Edit, NotebookEdit
42
+ tools: Read, Glob, Grep, Bash, WebFetch, WebSearch, TodoWrite, AskUserQuestion, Skill, ToolSearch, CronCreate, CronList, CronDelete, Agent, Write
43
43
  ---
44
44
 
45
45
  <Role>
@@ -37,7 +37,7 @@ description: |
37
37
  model: opus
38
38
  color: red
39
39
  codexSandboxMode: workspace-write
40
- disallowedTools: Edit, NotebookEdit
40
+ tools: Read, Glob, Grep, Bash, WebFetch, WebSearch, TodoWrite, AskUserQuestion, Skill, ToolSearch, CronCreate, CronList, CronDelete, Agent, Write
41
41
  ---
42
42
 
43
43
  <Role>
@@ -18,7 +18,7 @@ description: |
18
18
  model: opus
19
19
  color: red
20
20
  codexSandboxMode: workspace-write
21
- disallowedTools: Edit, NotebookEdit
21
+ tools: Read, Glob, Grep, Bash, WebFetch, WebSearch, TodoWrite, AskUserQuestion, Skill, ToolSearch, CronCreate, CronList, CronDelete, Agent, Write
22
22
  ---
23
23
 
24
24
  <Role>
@@ -70,7 +70,7 @@ description: |
70
70
  model: sonnet
71
71
  color: magenta
72
72
  codexSandboxMode: read-only
73
- disallowedTools: Write, Edit, NotebookEdit
73
+ tools: Read, Glob, Grep, Bash, WebFetch, WebSearch, TodoWrite, AskUserQuestion, Skill, ToolSearch, CronCreate, CronList, CronDelete, Agent, mcp__plugin_agestra_agestra__environment_check, mcp__plugin_agestra_agestra__provider_list, mcp__plugin_agestra_agestra__provider_health, mcp__plugin_agestra_agestra__trace_query, mcp__plugin_agestra_agestra__trace_summary, mcp__plugin_agestra_agestra__trace_visualize, mcp__plugin_agestra_agestra__ai_chat, mcp__plugin_agestra_agestra__ai_analyze_files, mcp__plugin_agestra_agestra__ai_compare, mcp__plugin_agestra_agestra__agent_debate_structured, mcp__plugin_agestra_agestra__agent_debate_status, mcp__plugin_agestra_agestra__agent_debate_approve, mcp__plugin_agestra_agestra__agent_debate_continue, mcp__plugin_agestra_agestra__agent_debate_reject, mcp__plugin_agestra_agestra__agent_cross_validate, mcp__plugin_agestra_agestra__cli_worker_spawn, mcp__plugin_agestra_agestra__cli_worker_status, mcp__plugin_agestra_agestra__cli_worker_collect, mcp__plugin_agestra_agestra__cli_worker_stop, mcp__plugin_agestra_agestra__agent_changes_review, mcp__plugin_agestra_agestra__agent_changes_accept, mcp__plugin_agestra_agestra__agent_changes_reject
74
74
  ---
75
75
 
76
76
  <Role>
@@ -252,7 +252,7 @@ Execute approved tasks across available execution paths:
252
252
 
253
253
  **Result Integration:**
254
254
  - Leader-host implementation: changes are already applied on the main branch (no merge needed).
255
- - CLI workers: call `agent_changes_review` to see full diff, then `agent_changes_accept` or `agent_changes_reject`.
255
+ - CLI workers: call `agent_changes_review` to inspect the full diff. Do **not** accept here — Phase 4 step 7 owns the supervised/autonomous accept gate.
256
256
  - File overlap between tracks: detect conflicts between implementer-applied changes and CLI worker worktrees. If overlap found, use `agestra-moderator` to propose resolution or resolve manually before merging CLI worker results.
257
257
 
258
258
  ### Phase 4: Result Inspection
@@ -273,7 +273,9 @@ After each task completes:
273
273
  - Import/export chains are complete
274
274
  6. If issues found → craft a detailed correction prompt and re-assign to the same AI or send a scoped fix task to `agestra-implementer`.
275
275
  7. If all checks pass:
276
- - For CLI worker tasks: call `agent_changes_accept` to merge worktree changes
276
+ - For CLI worker tasks: gate `agent_changes_accept` by execution mode.
277
+ - **Supervised (default):** Summarize the diff (files touched, scope, risk highlights) and use `AskUserQuestion` to confirm the merge before calling `agent_changes_accept`. Call `agent_changes_reject` only after an explicit user rejection with a reason. If the user does not respond or `AskUserQuestion` is unavailable, leave the worker worktree pending, report the task ID, and wait for a later accept/reject decision.
278
+ - **Autonomous:** Record the review evidence in your status update (files, design alignment notes), then call `agent_changes_accept`. Escalate to the user instead of auto-accepting when the diff exceeds the worker's stated scope, adds unrequested files, or touches a file flagged as high-risk in Phase 2.
277
279
  - For rejected CLI worker tasks: call `agent_changes_reject` with reason
278
280
  - Proceed to verification:
279
281
  - **Multi-AI mode** → Phase 5M (Structured Debate) replaces the separate QA and post-implementation review phases.
package/dist/bundle.js CHANGED
@@ -1,6 +1,6 @@
1
1
  #!/usr/bin/env node
2
2
  import { createRequire as ___cr } from 'module';import { fileURLToPath as ___fu } from 'url';import { dirname as ___dn } from 'path';const __filename = ___fu(import.meta.url);const __dirname = ___dn(__filename);const require = ___cr(import.meta.url);
3
- var IR=Object.create;var Cd=Object.defineProperty;var RR=Object.getOwnPropertyDescriptor;var AR=Object.getOwnPropertyNames;var CR=Object.getPrototypeOf,DR=Object.prototype.hasOwnProperty;var Ar=(t,e)=>()=>(t&&(e=t(t=0)),e);var S=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports),rt=(t,e)=>{for(var r in e)Cd(t,r,{get:e[r],enumerable:!0})},OR=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of AR(e))!DR.call(t,s)&&s!==r&&Cd(t,s,{get:()=>e[s],enumerable:!(n=RR(e,s))||n.enumerable});return t};var Gy=(t,e,r)=>(r=t!=null?IR(CR(t)):{},OR(e||!t||!t.__esModule?Cd(r,"default",{value:t,enumerable:!0}):r,t));var Yy,si,Xy,Qy,ev,tv,rv,oi,nv,As,sv,ov,iv,av,cv,uv,In,Dd,Od,PK,kK,Md,lv,dv,ic,Rn,zd,pv,fv,Cs,Cr=Ar(()=>{"use strict";Yy="4.13.0",si="http://localhost:11434",Xy="llama3",Qy=".agestra/.jobs",ev=".agestra/traces",tv=".agestra/worktrees",rv=".agestra/sessions",oi=".agestra/workspace",nv="individual",As="debates",sv="synthesis",ov="issue",iv=".agestra/workers",av=Object.freeze(["PATH","HOME","USER","USERPROFILE","APPDATA","LOCALAPPDATA","TEMP","TMP","TMPDIR","SHELL","LANG","LC_ALL","NODE_PATH","SystemRoot","SystemDrive","COMSPEC","ComSpec","windir"]),cv=2,uv=6e4,In=20,Dd=5e4,Od=10,PK=Object.freeze([3,5,10]),kK=Object.freeze(["agree","disagree","abstain","revise"]),Md=2,lv=1440*60*1e3,dv=3600*1e3,ic=Object.freeze(["ko","zh","ja","en"]),Rn="ko",zd="en",pv="[REDACTED]",fv=["","--- REQUIRED RESPONSE FORMAT ---","Respond with JSON only, matching exactly this shape:",'{ "agrees": true | false, "feedback": "specific issues or why you agree", "suggestions": "concrete changes if you disagree (optional, may be omitted)" }',"No prose, markdown, or explanation outside the JSON object. The JSON must be the entire response or wrapped in a single ```json code fence.","-------------------------------"].join(`
3
+ var IR=Object.create;var Cd=Object.defineProperty;var RR=Object.getOwnPropertyDescriptor;var AR=Object.getOwnPropertyNames;var CR=Object.getPrototypeOf,DR=Object.prototype.hasOwnProperty;var Ar=(t,e)=>()=>(t&&(e=t(t=0)),e);var S=(t,e)=>()=>(e||t((e={exports:{}}).exports,e),e.exports),rt=(t,e)=>{for(var r in e)Cd(t,r,{get:e[r],enumerable:!0})},OR=(t,e,r,n)=>{if(e&&typeof e=="object"||typeof e=="function")for(let s of AR(e))!DR.call(t,s)&&s!==r&&Cd(t,s,{get:()=>e[s],enumerable:!(n=RR(e,s))||n.enumerable});return t};var Gy=(t,e,r)=>(r=t!=null?IR(CR(t)):{},OR(e||!t||!t.__esModule?Cd(r,"default",{value:t,enumerable:!0}):r,t));var Yy,si,Xy,Qy,ev,tv,rv,oi,nv,As,sv,ov,iv,av,cv,uv,In,Dd,Od,PK,kK,Md,lv,dv,ic,Rn,zd,pv,fv,Cs,Cr=Ar(()=>{"use strict";Yy="4.13.1",si="http://localhost:11434",Xy="llama3",Qy=".agestra/.jobs",ev=".agestra/traces",tv=".agestra/worktrees",rv=".agestra/sessions",oi=".agestra/workspace",nv="individual",As="debates",sv="synthesis",ov="issue",iv=".agestra/workers",av=Object.freeze(["PATH","HOME","USER","USERPROFILE","APPDATA","LOCALAPPDATA","TEMP","TMP","TMPDIR","SHELL","LANG","LC_ALL","NODE_PATH","SystemRoot","SystemDrive","COMSPEC","ComSpec","windir"]),cv=2,uv=6e4,In=20,Dd=5e4,Od=10,PK=Object.freeze([3,5,10]),kK=Object.freeze(["agree","disagree","abstain","revise"]),Md=2,lv=1440*60*1e3,dv=3600*1e3,ic=Object.freeze(["ko","zh","ja","en"]),Rn="ko",zd="en",pv="[REDACTED]",fv=["","--- REQUIRED RESPONSE FORMAT ---","Respond with JSON only, matching exactly this shape:",'{ "agrees": true | false, "feedback": "specific issues or why you agree", "suggestions": "concrete changes if you disagree (optional, may be omitted)" }',"No prose, markdown, or explanation outside the JSON object. The JSON must be the entire response or wrapped in a single ```json code fence.","-------------------------------"].join(`
4
4
  `),Cs=Object.freeze({auto:Object.freeze({name:"auto",description:"Auto-detect build profile from project structure (package.json scripts, tsconfig, vitest config, etc.).",command:"npx",args:Object.freeze([])}),tsc:Object.freeze({name:"tsc",description:"TypeScript project-references build via `npx tsc -b --noEmit`.",command:"npx",args:Object.freeze(["tsc","-b","--noEmit"])}),vitest:Object.freeze({name:"vitest",description:"Run the Vitest unit-test suite in CI mode.",command:"npx",args:Object.freeze(["vitest","run","--reporter=dot"])}),"npm-build":Object.freeze({name:"npm-build",description:"Run the project's `npm run build` script.",command:"npm",args:Object.freeze(["run","build"])}),"npm-test":Object.freeze({name:"npm-test",description:"Run the project's `npm test` script.",command:"npm",args:Object.freeze(["test","--","--run"])})})});import{execFileSync as MR}from"child_process";function Ds(t){if(!Number.isFinite(t)||t<=0)return{delivered:!1};if(process.platform==="win32")try{return MR("taskkill",["/PID",String(t),"/T","/F"],{stdio:"ignore",windowsHide:!0}),{delivered:!0}}catch{try{return process.kill(t),{delivered:!0}}catch{return{delivered:!1}}}try{return process.kill(-t,"SIGTERM"),{delivered:!0}}catch{try{return process.kill(t,"SIGTERM"),{delivered:!0}}catch{return{delivered:!1}}}}function Qt(t,e=3e3){let r=t.pid;if(r)Ds(r);else try{t.kill("SIGTERM")}catch{}setTimeout(()=>{try{t.kill("SIGKILL")}catch{}},e).unref?.()}var Os=Ar(()=>{"use strict"});import{execFileSync as zR,spawn as jR}from"child_process";import{existsSync as NR}from"fs";function mv(t){let e=t.replace(/\s+/g," ").trim();return e.length>200?`${e.slice(0,200)}...`:e}function Yr(t,e){if(t)try{t({...e,at:new Date().toISOString()})}catch{}}function qR(t){return process.platform!=="win32"||t.includes("/")||t.includes("\\")?!1:LR.has(t.toLowerCase())}function FR(t){if(t.includes("\0"))throw new Error(`Windows shim arg contains null byte: ${JSON.stringify(t)}`);if(t.includes("%"))throw new Error(`Windows shim arg contains '%' which would trigger cmd.exe env expansion: ${JSON.stringify(t)}`);return/[\s"&^<>|()!]/.test(t)?`"${t.replace(/"/g,'""')}"`:t}function UR(t){let e=t.toLowerCase();if(Ms.has(e))return Ms.get(e)??null;try{let n=zR("where.exe",[t],{encoding:"utf-8",stdio:["ignore","pipe","ignore"],windowsHide:!0,timeout:5e3}).split(/\r?\n/).map(a=>a.trim()).filter(Boolean);if(n.length===0)return Ms.set(e,null),null;let s=n.find(a=>/\.(cmd|bat)$/i.test(a));if(s){let a={type:"cmd",path:s};return Ms.set(e,a),a}let i=`${n[0]}.ps1`;if(NR(i)){let a={type:"powershell",path:i};return Ms.set(e,a),a}}catch{}return Ms.set(e,null),null}function An(t,e){if(!qR(t))return{command:t,args:e};let r=UR(t);return r?r.type==="cmd"?{command:"cmd.exe",args:["/d","/s","/c",r.path,...e.map(n=>FR(n))]}:{command:"powershell.exe",args:["-NoProfile","-ExecutionPolicy","RemoteSigned","-File",r.path,...e]}:{command:t,args:e}}function vt(t){let{command:e,args:r,timeout:n=12e4,overallTimeoutMs:s,cwd:o,env:i,stdin:a,maxBuffer:c=10485760,onStderrChunk:u,onActivity:l,signal:d}=t;return new Promise((p,f)=>{if(d?.aborted){Yr(l,{type:"abort",command:e,args:r,reason:String(d.reason??"aborted before spawn")}),f(new jt(d.reason));return}let m=An(e,r);Yr(l,{type:"start",command:m.command,args:m.args,timeoutMs:n});let h=jR(m.command,m.args,{cwd:o,env:i?{...process.env,...i}:void 0,stdio:[a?"pipe":"ignore","pipe","pipe"],windowsHide:!0}),y="",g="",b=0,_=0,P=!1,x=null,N=!1,k=null,O=()=>{k&&d&&d.removeEventListener("abort",k)},L=!1,q=()=>{!L&&b+_>c&&(P=!0,L=!0,Qt(h))},$e=null,We=()=>{$e&&(clearTimeout($e),$e=null)},Rt=()=>{N||(Qt(h),N=!0,We(),O(),Yr(l,{type:"timeout",command:e,args:r,timeoutMs:n}),f(new Error(`CLI timeout after ${n}ms of inactivity: ${e} ${r.join(" ")}`)))},zt=setTimeout(Rt,n);$e=s!==void 0?setTimeout(()=>{N||(Qt(h),N=!0,clearTimeout(zt),We(),O(),Yr(l,{type:"timeout",command:e,args:r,timeoutMs:s}),f(new Error(`CLI overall timeout after ${s}ms: ${e} ${r.join(" ")}`)))},s):null,d&&(k=()=>{N||(N=!0,clearTimeout(zt),We(),Qt(h),O(),Yr(l,{type:"abort",command:e,args:r,reason:String(d.reason??"aborted")}),f(new jt(d.reason)))},d.addEventListener("abort",k));let Ts=()=>{L||(clearTimeout(zt),zt=setTimeout(Rt,n))};if(h.stdout.on("data",At=>{b+=At.length,Yr(l,{type:"stdout",command:e,args:r,preview:mv(At.toString())}),b<=c&&(y+=At.toString()),q(),Ts()}),h.stderr.on("data",At=>{_+=At.length;let Tn=At.toString();if(Yr(l,{type:"stderr",command:e,args:r,preview:mv(Tn)}),_<=c&&(g+=Tn),q(),!L&&u){let ni=u(Tn);ni&&(x=ni,L=!0,Qt(h))}Ts()}),a&&h.stdin){h.stdin.on("error",()=>{});try{h.stdin.write(a),h.stdin.end()}catch{}}h.on("close",(At,Tn)=>{if(clearTimeout(zt),We(),!N){if(Yr(l,{type:"close",command:e,args:r,exitCode:At,signalCode:Tn}),x){N=!0,O(),f(x);return}N=!0,O(),p({stdout:y,stderr:g,exitCode:At??1,truncated:P})}}),h.on("error",At=>{clearTimeout(zt),We(),!N&&(N=!0,O(),f(new Error(`CLI spawn error: ${At.message}`)))})})}var jt,LR,Ms,ii=Ar(()=>{"use strict";Os();jt=class extends Error{constructor(e){let r=e instanceof Error?`: ${e.message}`:typeof e=="string"&&e?`: ${e}`:"";super(`CLI aborted${r}`),this.name="CliAbortedError"}};LR=new Set(["codex","gemini","npm","npx"]),Ms=new Map});import{writeFileSync as dC,mkdirSync as di,openSync as Fd,writeSync as Ud,fsyncSync as Ov,closeSync as Zd,renameSync as pC,unlinkSync as Dv,appendFileSync as fC,copyFileSync as mC}from"fs";import{dirname as pi,join as hC}from"path";import{randomUUID as gC}from"crypto";function ge(t,e){let r=pi(t);di(r,{recursive:!0});let n=hC(r,`.tmp-${gC().slice(0,8)}`);try{dC(n,e,"utf-8");try{pC(n,t)}catch(s){if(s.code!=="EXDEV")throw s;mC(n,t),Dv(n)}}catch(s){try{Dv(n)}catch{}throw s}}function Ze(t,e){ge(t,JSON.stringify(e,null,2))}function _c(t,e){let r=pi(t);di(r,{recursive:!0});let n=Fd(t,"a");try{Ud(n,e),Ov(n)}finally{Zd(n)}}var vc,nn=Ar(()=>{"use strict";vc=class{buffer=new Map;flushTimer=null;flushIntervalMs;maxBufferSize;auditFsync;destroyed=!1;constructor(e){this.flushIntervalMs=e?.flushIntervalMs??2e3,this.maxBufferSize=e?.maxBufferSize??100,this.auditFsync=e?.auditFsync??!0,this.flushIntervalMs>0&&(this.flushTimer=setInterval(()=>this.flush(),this.flushIntervalMs),this.flushTimer&&typeof this.flushTimer.unref=="function"&&this.flushTimer.unref())}append(e,r,n){if(this.destroyed)throw new Error("DurableAppendWriter has been destroyed");if(n==="audit"){if(this.auditFsync)_c(e,r);else{let o=pi(e);di(o,{recursive:!0}),fC(e,r)}return}let s=this.buffer.get(e);s||(s=[],this.buffer.set(e,s)),s.push(r),s.length>=this.maxBufferSize&&this.flushFile(e),this.flushIntervalMs===0&&this.flushFile(e)}flush(){for(let e of this.buffer.keys())this.flushFile(e)}panicFlush(){for(let[e,r]of this.buffer)if(r.length!==0)try{let n=pi(e);di(n,{recursive:!0});let s=Fd(e,"a");try{Ud(s,r.join("")),Ov(s)}finally{Zd(s)}}catch{}this.buffer.clear()}destroy(){this.destroyed||(this.destroyed=!0,this.flushTimer!==null&&(clearInterval(this.flushTimer),this.flushTimer=null),this.flush())}flushFile(e){let r=this.buffer.get(e);if(!r||r.length===0)return;let n=pi(e);di(n,{recursive:!0});let s=Fd(e,"a");try{Ud(s,r.join(""))}finally{Zd(s)}this.buffer.delete(e)}}});import{openSync as yC,writeSync as vC,closeSync as _C,unlinkSync as Mv,statSync as bC,mkdirSync as wC,existsSync as SC}from"fs";import{dirname as $C}from"path";function EC(t){if(t<=0)return;let e=new SharedArrayBuffer(4),r=new Int32Array(e);Atomics.wait(r,0,0,t)}function Kn(t,e,r={}){let n=r.staleMs??xC,s=r.acquireTimeoutMs??PC,o=r.pollMs??kC;wC($C(t),{recursive:!0});let i=Date.now(),a=null;for(;a===null;)try{a=yC(t,"wx")}catch(c){let u=c.code;if(!(u==="EEXIST"||process.platform==="win32"&&u==="EPERM"&&SC(t)))throw c;let d=!1;try{let f=bC(t);d=Date.now()-f.mtimeMs>n}catch{continue}if(d){try{Mv(t)}catch{}continue}let p=Date.now()-i;if(p>=s)throw new Vd(t,p);EC(o)}try{try{vC(a,`${process.pid}@${Date.now()}
5
5
  `)}catch{}return e()}finally{try{_C(a)}catch{}try{Mv(t)}catch{}}}var xC,PC,kC,Vd,bc=Ar(()=>{"use strict";xC=3e4,PC=5e3,kC=10,Vd=class extends Error{constructor(e,r){super(`withFileLock: timed out after ${r}ms waiting for ${e}`),this.name="FileLockTimeoutError"}}});import{existsSync as zv,realpathSync as TC}from"fs";import{basename as IC,dirname as RC,isAbsolute as Hd,join as AC,normalize as Nv,resolve as Wd,relative as CC}from"path";function DC(t){return t===""||!t.startsWith("..")&&!Hd(t)}function Bd(t){let e=[],r=Wd(t);for(;!zv(r);){let s=RC(r);if(s===r)break;e.unshift(IC(r)),r=s}let n=zv(r)?TC(r):r;return e.reduce((s,o)=>AC(s,o),n)}function Lv(t,e){let r=Bd(e),n=Bd(t),s=CC(r,n);return DC(s)}function Kd(t,e){let r=Wd(e,t);return Lv(r,e)}function Ft(t,e){let r=Wd(e,t);if(!Lv(r,e))throw new Error(`Path traversal blocked: ${t} escapes ${e}`);return r}function Je(t,e){if(t.length===0)throw new Error("Filename must not be empty");if(t.includes("\0"))throw new Error(`Filename contains null byte: ${JSON.stringify(t)}`);if(t.includes("/")||t.includes("\\"))throw new Error(`Filename must not contain path separators: ${JSON.stringify(t)}`);if(t.includes(".."))throw new Error(`Filename must not contain '..': ${JSON.stringify(t)}`);return Ft(t,e)}function sn(t,e){if(Hd(t)){let r=Nv(t);if(!Kd(r,e))throw new Error(`Read path outside base directory: ${t} is not within ${e}`);return r}return Ft(t,e)}function qv(t,e){return Hd(t)?Bd(Nv(t)):Ft(t,e)}function Nr(t){return OC.test(t)}function wc(t,e){if(!t||typeof t!="string")throw new Ct(String(t),"command must be a non-empty string");if(t.includes("\0"))throw new Ct(t,"command contains null byte");if(t.includes("/")||t.includes("\\"))throw new Ct(t,"command must not contain path separators");if(!jv.has(t))throw new Ct(t,`command is not in the allowlist (${[...jv].join(", ")})`);for(let r of e){if(typeof r!="string")throw new Ct(t,`args contain a non-string entry: ${JSON.stringify(r)}`);if(r.includes("\0"))throw new Ct(t,`arg contains null byte: ${JSON.stringify(r)}`);if(MC.has(r))throw new Ct(t,`arg ${JSON.stringify(r)} is a code-execution flag and is not permitted`)}}var OC,jv,MC,Ct,Vs=Ar(()=>{"use strict";OC=/^[a-zA-Z0-9_-]+-\d+-[a-f0-9]{6}$/;jv=new Set(["gemini","codex","claude","npx","node","npm","tsc"]),MC=new Set(["-e","--eval","-c","--command","-E","--execute","--exec"]),Ct=class extends Error{command;reason;constructor(e,r){super(`CLI command rejected: ${e} \u2014 ${r}`),this.command=e,this.reason=r,this.name="CliCommandRejectedError"}}});import{readFileSync as zC}from"fs";import{join as jC}from"path";function Ve(t,e){let r=jC(t,"status.json"),n=`${r}.lock`;Kn(n,()=>{let s=JSON.parse(zC(r,"utf-8"));Ze(r,{...s,...e})})}var fi=Ar(()=>{"use strict";nn();bc()});import{spawn as NC}from"child_process";import{join as Jd}from"path";function Fv(t){let e=t.replace(/\s+/g," ").trim();return e.length>200?`${e.slice(0,200)}...`:e}function Sc(t){return new Promise(e=>{let r=An(t.command,t.args),n={kind:"start",updatedAt:new Date().toISOString(),message:"CLI process started",command:r.command,timeoutMs:t.timeoutMs,stdoutBytes:0,stderrBytes:0},s=0,o,i=(y,g={})=>{Object.assign(n,y,{updatedAt:new Date().toISOString()});let b=Date.now(),_=n.kind!==o;if(!(!g.force&&!_&&b-s<LC))try{Ve(t.jobDir,{activity:{...n}}),s=b,o=n.kind}catch{}};i(n,{force:!0});let a=NC(r.command,r.args,{cwd:t.cwd,stdio:["ignore","pipe","pipe"],windowsHide:!0});if(t.recordPid&&typeof a.pid=="number"&&a.pid>0)try{Ve(t.jobDir,{pid:a.pid})}catch{}if(!a.stdout||!a.stderr){try{a.kill()}catch{}try{ge(Jd(t.jobDir,"error.txt"),"Worker stdout/stderr streams unavailable (pipe allocation failed)"),Ve(t.jobDir,{state:"error",completedAt:new Date().toISOString()})}catch{}e();return}let c="",u="",l=0,d=0,p=!1,f=!1;a.stdout.on("data",y=>{l+=y.length,i({kind:"stdout",message:"stdout received",stdoutBytes:l,stderrBytes:d,lastStdoutPreview:Fv(y.toString())}),l<=10485760?c+=y.toString():p||(p=!0,Qt(a))}),a.stderr.on("data",y=>{d+=y.length,i({kind:"stderr",message:"stderr received",stdoutBytes:l,stderrBytes:d,lastStderrPreview:Fv(y.toString())}),d<=10485760&&(u+=y.toString())});let m=(y,g)=>{if(!f){f=!0,clearTimeout(h);try{ge(Jd(t.jobDir,"output.txt"),c),ge(Jd(t.jobDir,"error.txt"),g!==void 0?g:u),Ve(t.jobDir,y)}catch{}e()}},h=setTimeout(()=>{Qt(a),i({kind:"timeout",message:`timed out after ${t.timeoutMs}ms`,timeoutMs:t.timeoutMs,stdoutBytes:l,stderrBytes:d},{force:!0}),m({state:"timed_out",completedAt:new Date().toISOString()})},t.timeoutMs);a.on("close",y=>{if(f)return;let g=c.trim().length>0,b=y===0||g?"completed":"error";i({kind:"close",message:"CLI process closed",exitCode:y??1,stdoutBytes:l,stderrBytes:d},{force:!0}),m({state:b,exitCode:y??1,completedAt:new Date().toISOString()})}),a.on("error",y=>{if(f)return;let g=y.code==="ENOENT"?"missing_cli":"error";i({kind:"close",message:`CLI spawn error: ${y.message}`,stdoutBytes:l,stderrBytes:d},{force:!0}),m({state:g,completedAt:new Date().toISOString()},y.message)})})}var LC,Gd=Ar(()=>{"use strict";nn();ii();Os();fi();LC=1e3});var Bv={};rt(Bv,{CLI_PROVIDERS:()=>Zv,resolveCliConfig:()=>Vv});import{readFileSync as Uv}from"fs";import{join as Jn}from"path";function Vv(t){let e=qC[t.provider]??t.provider;return Zv[e]??null}async function FC(){let t=process.argv[2];t||process.exit(1);let e;try{e=JSON.parse(Uv(Jn(t,"job.json"),"utf-8"))}catch{try{Ze(Jn(t,"status.json"),{id:"unknown",state:"error",provider:"unknown",completedAt:new Date().toISOString()}),ge(Jn(t,"error.txt"),"Failed to read job.json")}catch{}process.exit(1)}let r=Vv(e);r||(Ve(t,{state:"missing_cli",completedAt:new Date().toISOString()}),ge(Jn(t,"error.txt"),`No CLI mapping for provider: ${e.provider}`),process.exit(1));let{command:n,buildArgs:s}=r;Ve(t,{state:"running",startedAt:new Date().toISOString()});let o=Uv(Jn(t,"prompt.txt"),"utf-8"),i=s(o);try{wc(n,i)}catch(c){let u=c instanceof Ct?c.message:`Argv rejected: ${c.message}`;Ve(t,{state:"error",completedAt:new Date().toISOString()}),ge(Jn(t,"error.txt"),u);return}let a;if(e.cwd)try{a=Ft(e.cwd,process.cwd())}catch(c){Ve(t,{state:"error",completedAt:new Date().toISOString()}),ge(Jn(t,"error.txt"),`Rejected descriptor.cwd: ${c.message}`);return}await Sc({jobDir:t,command:n,args:i,cwd:a,timeoutMs:e.timeout,recordPid:!1})}var qC,Zv,UC,Hv=Ar(()=>{"use strict";nn();Vs();Gd();fi();qC={"gemini-cli":"gemini","codex-cli":"codex","claude-cli":"claude"},Zv={gemini:{command:"gemini",buildArgs:t=>["-p",t]},codex:{command:"codex",buildArgs:t=>["exec","--skip-git-repo-check","--full-auto","--ephemeral",t]},claude:{command:"claude",buildArgs:t=>["-p",t]}};UC=process.argv[1]?.endsWith("job-worker.js")||process.argv[1]?.endsWith("job-worker.ts");UC&&FC().catch(()=>process.exit(1))});var Qi=S(me=>{"use strict";Object.defineProperty(me,"__esModule",{value:!0});me.regexpCode=me.getEsmExportName=me.getProperty=me.safeStringify=me.stringify=me.strConcat=me.addCodeArg=me.str=me._=me.nil=me._Code=me.Name=me.IDENTIFIER=me._CodeOrName=void 0;var Yi=class{};me._CodeOrName=Yi;me.IDENTIFIER=/^[a-z$_][a-z$_0-9]*$/i;var ss=class extends Yi{constructor(e){if(super(),!me.IDENTIFIER.test(e))throw new Error("CodeGen: name must be a valid identifier");this.str=e}toString(){return this.str}emptyStr(){return!1}get names(){return{[this.str]:1}}};me.Name=ss;var Bt=class extends Yi{constructor(e){super(),this._items=typeof e=="string"?[e]:e}toString(){return this.str}emptyStr(){if(this._items.length>1)return!1;let e=this._items[0];return e===""||e==='""'}get str(){var e;return(e=this._str)!==null&&e!==void 0?e:this._str=this._items.reduce((r,n)=>`${r}${n}`,"")}get names(){var e;return(e=this._names)!==null&&e!==void 0?e:this._names=this._items.reduce((r,n)=>(n instanceof ss&&(r[n.str]=(r[n.str]||0)+1),r),{})}};me._Code=Bt;me.nil=new Bt("");function Q$(t,...e){let r=[t[0]],n=0;for(;n<e.length;)Df(r,e[n]),r.push(t[++n]);return new Bt(r)}me._=Q$;var Cf=new Bt("+");function ex(t,...e){let r=[Xi(t[0])],n=0;for(;n<e.length;)r.push(Cf),Df(r,e[n]),r.push(Cf,Xi(t[++n]));return Bj(r),new Bt(r)}me.str=ex;function Df(t,e){e instanceof Bt?t.push(...e._items):e instanceof ss?t.push(e):t.push(Kj(e))}me.addCodeArg=Df;function Bj(t){let e=1;for(;e<t.length-1;){if(t[e]===Cf){let r=Hj(t[e-1],t[e+1]);if(r!==void 0){t.splice(e-1,3,r);continue}t[e++]="+"}e++}}function Hj(t,e){if(e==='""')return t;if(t==='""')return e;if(typeof t=="string")return e instanceof ss||t[t.length-1]!=='"'?void 0:typeof e!="string"?`${t.slice(0,-1)}${e}"`:e[0]==='"'?t.slice(0,-1)+e.slice(1):void 0;if(typeof e=="string"&&e[0]==='"'&&!(t instanceof ss))return`"${t}${e.slice(1)}`}function Wj(t,e){return e.emptyStr()?t:t.emptyStr()?e:ex`${t}${e}`}me.strConcat=Wj;function Kj(t){return typeof t=="number"||typeof t=="boolean"||t===null?t:Xi(Array.isArray(t)?t.join(","):t)}function Jj(t){return new Bt(Xi(t))}me.stringify=Jj;function Xi(t){return JSON.stringify(t).replace(/\u2028/g,"\\u2028").replace(/\u2029/g,"\\u2029")}me.safeStringify=Xi;function Gj(t){return typeof t=="string"&&me.IDENTIFIER.test(t)?new Bt(`.${t}`):Q$`[${t}]`}me.getProperty=Gj;function Yj(t){if(typeof t=="string"&&me.IDENTIFIER.test(t))return new Bt(`${t}`);throw new Error(`CodeGen: invalid export name: ${t}, use explicit $id name mapping`)}me.getEsmExportName=Yj;function Xj(t){return new Bt(t.toString())}me.regexpCode=Xj});var zf=S($t=>{"use strict";Object.defineProperty($t,"__esModule",{value:!0});$t.ValueScope=$t.ValueScopeName=$t.Scope=$t.varKinds=$t.UsedValueState=void 0;var St=Qi(),Of=class extends Error{constructor(e){super(`CodeGen: "code" for ${e} not defined`),this.value=e.value}},Iu;(function(t){t[t.Started=0]="Started",t[t.Completed=1]="Completed"})(Iu||($t.UsedValueState=Iu={}));$t.varKinds={const:new St.Name("const"),let:new St.Name("let"),var:new St.Name("var")};var Ru=class{constructor({prefixes:e,parent:r}={}){this._names={},this._prefixes=e,this._parent=r}toName(e){return e instanceof St.Name?e:this.name(e)}name(e){return new St.Name(this._newName(e))}_newName(e){let r=this._names[e]||this._nameGroup(e);return`${e}${r.index++}`}_nameGroup(e){var r,n;if(!((n=(r=this._parent)===null||r===void 0?void 0:r._prefixes)===null||n===void 0)&&n.has(e)||this._prefixes&&!this._prefixes.has(e))throw new Error(`CodeGen: prefix "${e}" is not allowed in this scope`);return this._names[e]={prefix:e,index:0}}};$t.Scope=Ru;var Au=class extends St.Name{constructor(e,r){super(r),this.prefix=e}setValue(e,{property:r,itemIndex:n}){this.value=e,this.scopePath=(0,St._)`.${new St.Name(r)}[${n}]`}};$t.ValueScopeName=Au;var Qj=(0,St._)`\n`,Mf=class extends Ru{constructor(e){super(e),this._values={},this._scope=e.scope,this.opts={...e,_n:e.lines?Qj:St.nil}}get(){return this._scope}name(e){return new Au(e,this._newName(e))}value(e,r){var n;if(r.ref===void 0)throw new Error("CodeGen: ref must be passed in value");let s=this.toName(e),{prefix:o}=s,i=(n=r.key)!==null&&n!==void 0?n:r.ref,a=this._values[o];if(a){let l=a.get(i);if(l)return l}else a=this._values[o]=new Map;a.set(i,s);let c=this._scope[o]||(this._scope[o]=[]),u=c.length;return c[u]=r.ref,s.setValue(r,{property:o,itemIndex:u}),s}getValue(e,r){let n=this._values[e];if(n)return n.get(r)}scopeRefs(e,r=this._values){return this._reduceValues(r,n=>{if(n.scopePath===void 0)throw new Error(`CodeGen: name "${n}" has no value`);return(0,St._)`${e}${n.scopePath}`})}scopeCode(e=this._values,r,n){return this._reduceValues(e,s=>{if(s.value===void 0)throw new Error(`CodeGen: name "${s}" has no value`);return s.value.code},r,n)}_reduceValues(e,r,n={},s){let o=St.nil;for(let i in e){let a=e[i];if(!a)continue;let c=n[i]=n[i]||new Map;a.forEach(u=>{if(c.has(u))return;c.set(u,Iu.Started);let l=r(u);if(l){let d=this.opts.es5?$t.varKinds.var:$t.varKinds.const;o=(0,St._)`${o}${d} ${u} = ${l};${this.opts._n}`}else if(l=s?.(u))o=(0,St._)`${o}${l}${this.opts._n}`;else throw new Of(u);c.set(u,Iu.Completed)})}return o}};$t.ValueScope=Mf});var te=S(Q=>{"use strict";Object.defineProperty(Q,"__esModule",{value:!0});Q.or=Q.and=Q.not=Q.CodeGen=Q.operators=Q.varKinds=Q.ValueScopeName=Q.ValueScope=Q.Scope=Q.Name=Q.regexpCode=Q.stringify=Q.getProperty=Q.nil=Q.strConcat=Q.str=Q._=void 0;var ce=Qi(),sr=zf(),dn=Qi();Object.defineProperty(Q,"_",{enumerable:!0,get:function(){return dn._}});Object.defineProperty(Q,"str",{enumerable:!0,get:function(){return dn.str}});Object.defineProperty(Q,"strConcat",{enumerable:!0,get:function(){return dn.strConcat}});Object.defineProperty(Q,"nil",{enumerable:!0,get:function(){return dn.nil}});Object.defineProperty(Q,"getProperty",{enumerable:!0,get:function(){return dn.getProperty}});Object.defineProperty(Q,"stringify",{enumerable:!0,get:function(){return dn.stringify}});Object.defineProperty(Q,"regexpCode",{enumerable:!0,get:function(){return dn.regexpCode}});Object.defineProperty(Q,"Name",{enumerable:!0,get:function(){return dn.Name}});var Mu=zf();Object.defineProperty(Q,"Scope",{enumerable:!0,get:function(){return Mu.Scope}});Object.defineProperty(Q,"ValueScope",{enumerable:!0,get:function(){return Mu.ValueScope}});Object.defineProperty(Q,"ValueScopeName",{enumerable:!0,get:function(){return Mu.ValueScopeName}});Object.defineProperty(Q,"varKinds",{enumerable:!0,get:function(){return Mu.varKinds}});Q.operators={GT:new ce._Code(">"),GTE:new ce._Code(">="),LT:new ce._Code("<"),LTE:new ce._Code("<="),EQ:new ce._Code("==="),NEQ:new ce._Code("!=="),NOT:new ce._Code("!"),OR:new ce._Code("||"),AND:new ce._Code("&&"),ADD:new ce._Code("+")};var Fr=class{optimizeNodes(){return this}optimizeNames(e,r){return this}},jf=class extends Fr{constructor(e,r,n){super(),this.varKind=e,this.name=r,this.rhs=n}render({es5:e,_n:r}){let n=e?sr.varKinds.var:this.varKind,s=this.rhs===void 0?"":` = ${this.rhs}`;return`${n} ${this.name}${s};`+r}optimizeNames(e,r){if(e[this.name.str])return this.rhs&&(this.rhs=go(this.rhs,e,r)),this}get names(){return this.rhs instanceof ce._CodeOrName?this.rhs.names:{}}},Cu=class extends Fr{constructor(e,r,n){super(),this.lhs=e,this.rhs=r,this.sideEffects=n}render({_n:e}){return`${this.lhs} = ${this.rhs};`+e}optimizeNames(e,r){if(!(this.lhs instanceof ce.Name&&!e[this.lhs.str]&&!this.sideEffects))return this.rhs=go(this.rhs,e,r),this}get names(){let e=this.lhs instanceof ce.Name?{}:{...this.lhs.names};return Ou(e,this.rhs)}},Nf=class extends Cu{constructor(e,r,n,s){super(e,n,s),this.op=r}render({_n:e}){return`${this.lhs} ${this.op}= ${this.rhs};`+e}},Lf=class extends Fr{constructor(e){super(),this.label=e,this.names={}}render({_n:e}){return`${this.label}:`+e}},qf=class extends Fr{constructor(e){super(),this.label=e,this.names={}}render({_n:e}){return`break${this.label?` ${this.label}`:""};`+e}},Ff=class extends Fr{constructor(e){super(),this.error=e}render({_n:e}){return`throw ${this.error};`+e}get names(){return this.error.names}},Uf=class extends Fr{constructor(e){super(),this.code=e}render({_n:e}){return`${this.code};`+e}optimizeNodes(){return`${this.code}`?this:void 0}optimizeNames(e,r){return this.code=go(this.code,e,r),this}get names(){return this.code instanceof ce._CodeOrName?this.code.names:{}}},ea=class extends Fr{constructor(e=[]){super(),this.nodes=e}render(e){return this.nodes.reduce((r,n)=>r+n.render(e),"")}optimizeNodes(){let{nodes:e}=this,r=e.length;for(;r--;){let n=e[r].optimizeNodes();Array.isArray(n)?e.splice(r,1,...n):n?e[r]=n:e.splice(r,1)}return e.length>0?this:void 0}optimizeNames(e,r){let{nodes:n}=this,s=n.length;for(;s--;){let o=n[s];o.optimizeNames(e,r)||(eN(e,o.names),n.splice(s,1))}return n.length>0?this:void 0}get names(){return this.nodes.reduce((e,r)=>as(e,r.names),{})}},Ur=class extends ea{render(e){return"{"+e._n+super.render(e)+"}"+e._n}},Zf=class extends ea{},ho=class extends Ur{};ho.kind="else";var os=class t extends Ur{constructor(e,r){super(r),this.condition=e}render(e){let r=`if(${this.condition})`+super.render(e);return this.else&&(r+="else "+this.else.render(e)),r}optimizeNodes(){super.optimizeNodes();let e=this.condition;if(e===!0)return this.nodes;let r=this.else;if(r){let n=r.optimizeNodes();r=this.else=Array.isArray(n)?new ho(n):n}if(r)return e===!1?r instanceof t?r:r.nodes:this.nodes.length?this:new t(tx(e),r instanceof t?[r]:r.nodes);if(!(e===!1||!this.nodes.length))return this}optimizeNames(e,r){var n;if(this.else=(n=this.else)===null||n===void 0?void 0:n.optimizeNames(e,r),!!(super.optimizeNames(e,r)||this.else))return this.condition=go(this.condition,e,r),this}get names(){let e=super.names;return Ou(e,this.condition),this.else&&as(e,this.else.names),e}};os.kind="if";var is=class extends Ur{};is.kind="for";var Vf=class extends is{constructor(e){super(),this.iteration=e}render(e){return`for(${this.iteration})`+super.render(e)}optimizeNames(e,r){if(super.optimizeNames(e,r))return this.iteration=go(this.iteration,e,r),this}get names(){return as(super.names,this.iteration.names)}},Bf=class extends is{constructor(e,r,n,s){super(),this.varKind=e,this.name=r,this.from=n,this.to=s}render(e){let r=e.es5?sr.varKinds.var:this.varKind,{name:n,from:s,to:o}=this;return`for(${r} ${n}=${s}; ${n}<${o}; ${n}++)`+super.render(e)}get names(){let e=Ou(super.names,this.from);return Ou(e,this.to)}},Du=class extends is{constructor(e,r,n,s){super(),this.loop=e,this.varKind=r,this.name=n,this.iterable=s}render(e){return`for(${this.varKind} ${this.name} ${this.loop} ${this.iterable})`+super.render(e)}optimizeNames(e,r){if(super.optimizeNames(e,r))return this.iterable=go(this.iterable,e,r),this}get names(){return as(super.names,this.iterable.names)}},ta=class extends Ur{constructor(e,r,n){super(),this.name=e,this.args=r,this.async=n}render(e){return`${this.async?"async ":""}function ${this.name}(${this.args})`+super.render(e)}};ta.kind="func";var ra=class extends ea{render(e){return"return "+super.render(e)}};ra.kind="return";var Hf=class extends Ur{render(e){let r="try"+super.render(e);return this.catch&&(r+=this.catch.render(e)),this.finally&&(r+=this.finally.render(e)),r}optimizeNodes(){var e,r;return super.optimizeNodes(),(e=this.catch)===null||e===void 0||e.optimizeNodes(),(r=this.finally)===null||r===void 0||r.optimizeNodes(),this}optimizeNames(e,r){var n,s;return super.optimizeNames(e,r),(n=this.catch)===null||n===void 0||n.optimizeNames(e,r),(s=this.finally)===null||s===void 0||s.optimizeNames(e,r),this}get names(){let e=super.names;return this.catch&&as(e,this.catch.names),this.finally&&as(e,this.finally.names),e}},na=class extends Ur{constructor(e){super(),this.error=e}render(e){return`catch(${this.error})`+super.render(e)}};na.kind="catch";var sa=class extends Ur{render(e){return"finally"+super.render(e)}};sa.kind="finally";var Wf=class{constructor(e,r={}){this._values={},this._blockStarts=[],this._constants={},this.opts={...r,_n:r.lines?`
6
6
  `:""},this._extScope=e,this._scope=new sr.Scope({parent:e}),this._nodes=[new Zf]}toString(){return this._root.render(this.opts)}name(e){return this._scope.name(e)}scopeName(e){return this._extScope.name(e)}scopeValue(e,r){let n=this._extScope.value(e,r);return(this._values[n.prefix]||(this._values[n.prefix]=new Set)).add(n),n}getScopeValue(e,r){return this._extScope.getValue(e,r)}scopeRefs(e){return this._extScope.scopeRefs(e,this._values)}scopeCode(){return this._extScope.scopeCode(this._values)}_def(e,r,n,s){let o=this._scope.toName(r);return n!==void 0&&s&&(this._constants[o.str]=n),this._leafNode(new jf(e,o,n)),o}const(e,r,n){return this._def(sr.varKinds.const,e,r,n)}let(e,r,n){return this._def(sr.varKinds.let,e,r,n)}var(e,r,n){return this._def(sr.varKinds.var,e,r,n)}assign(e,r,n){return this._leafNode(new Cu(e,r,n))}add(e,r){return this._leafNode(new Nf(e,Q.operators.ADD,r))}code(e){return typeof e=="function"?e():e!==ce.nil&&this._leafNode(new Uf(e)),this}object(...e){let r=["{"];for(let[n,s]of e)r.length>1&&r.push(","),r.push(n),(n!==s||this.opts.es5)&&(r.push(":"),(0,ce.addCodeArg)(r,s));return r.push("}"),new ce._Code(r)}if(e,r,n){if(this._blockNode(new os(e)),r&&n)this.code(r).else().code(n).endIf();else if(r)this.code(r).endIf();else if(n)throw new Error('CodeGen: "else" body without "then" body');return this}elseIf(e){return this._elseNode(new os(e))}else(){return this._elseNode(new ho)}endIf(){return this._endBlockNode(os,ho)}_for(e,r){return this._blockNode(e),r&&this.code(r).endFor(),this}for(e,r){return this._for(new Vf(e),r)}forRange(e,r,n,s,o=this.opts.es5?sr.varKinds.var:sr.varKinds.let){let i=this._scope.toName(e);return this._for(new Bf(o,i,r,n),()=>s(i))}forOf(e,r,n,s=sr.varKinds.const){let o=this._scope.toName(e);if(this.opts.es5){let i=r instanceof ce.Name?r:this.var("_arr",r);return this.forRange("_i",0,(0,ce._)`${i}.length`,a=>{this.var(o,(0,ce._)`${i}[${a}]`),n(o)})}return this._for(new Du("of",s,o,r),()=>n(o))}forIn(e,r,n,s=this.opts.es5?sr.varKinds.var:sr.varKinds.const){if(this.opts.ownProperties)return this.forOf(e,(0,ce._)`Object.keys(${r})`,n);let o=this._scope.toName(e);return this._for(new Du("in",s,o,r),()=>n(o))}endFor(){return this._endBlockNode(is)}label(e){return this._leafNode(new Lf(e))}break(e){return this._leafNode(new qf(e))}return(e){let r=new ra;if(this._blockNode(r),this.code(e),r.nodes.length!==1)throw new Error('CodeGen: "return" should have one node');return this._endBlockNode(ra)}try(e,r,n){if(!r&&!n)throw new Error('CodeGen: "try" without "catch" and "finally"');let s=new Hf;if(this._blockNode(s),this.code(e),r){let o=this.name("e");this._currNode=s.catch=new na(o),r(o)}return n&&(this._currNode=s.finally=new sa,this.code(n)),this._endBlockNode(na,sa)}throw(e){return this._leafNode(new Ff(e))}block(e,r){return this._blockStarts.push(this._nodes.length),e&&this.code(e).endBlock(r),this}endBlock(e){let r=this._blockStarts.pop();if(r===void 0)throw new Error("CodeGen: not in self-balancing block");let n=this._nodes.length-r;if(n<0||e!==void 0&&n!==e)throw new Error(`CodeGen: wrong number of nodes: ${n} vs ${e} expected`);return this._nodes.length=r,this}func(e,r=ce.nil,n,s){return this._blockNode(new ta(e,r,n)),s&&this.code(s).endFunc(),this}endFunc(){return this._endBlockNode(ta)}optimize(e=1){for(;e-- >0;)this._root.optimizeNodes(),this._root.optimizeNames(this._root.names,this._constants)}_leafNode(e){return this._currNode.nodes.push(e),this}_blockNode(e){this._currNode.nodes.push(e),this._nodes.push(e)}_endBlockNode(e,r){let n=this._currNode;if(n instanceof e||r&&n instanceof r)return this._nodes.pop(),this;throw new Error(`CodeGen: not in block "${r?`${e.kind}/${r.kind}`:e.kind}"`)}_elseNode(e){let r=this._currNode;if(!(r instanceof os))throw new Error('CodeGen: "else" without "if"');return this._currNode=r.else=e,this}get _root(){return this._nodes[0]}get _currNode(){let e=this._nodes;return e[e.length-1]}set _currNode(e){let r=this._nodes;r[r.length-1]=e}};Q.CodeGen=Wf;function as(t,e){for(let r in e)t[r]=(t[r]||0)+(e[r]||0);return t}function Ou(t,e){return e instanceof ce._CodeOrName?as(t,e.names):t}function go(t,e,r){if(t instanceof ce.Name)return n(t);if(!s(t))return t;return new ce._Code(t._items.reduce((o,i)=>(i instanceof ce.Name&&(i=n(i)),i instanceof ce._Code?o.push(...i._items):o.push(i),o),[]));function n(o){let i=r[o.str];return i===void 0||e[o.str]!==1?o:(delete e[o.str],i)}function s(o){return o instanceof ce._Code&&o._items.some(i=>i instanceof ce.Name&&e[i.str]===1&&r[i.str]!==void 0)}}function eN(t,e){for(let r in e)t[r]=(t[r]||0)-(e[r]||0)}function tx(t){return typeof t=="boolean"||typeof t=="number"||t===null?!t:(0,ce._)`!${Kf(t)}`}Q.not=tx;var tN=rx(Q.operators.AND);function rN(...t){return t.reduce(tN)}Q.and=rN;var nN=rx(Q.operators.OR);function sN(...t){return t.reduce(nN)}Q.or=sN;function rx(t){return(e,r)=>e===ce.nil?r:r===ce.nil?e:(0,ce._)`${Kf(e)} ${t} ${Kf(r)}`}function Kf(t){return t instanceof ce.Name?t:(0,ce._)`(${t})`}});var le=S(re=>{"use strict";Object.defineProperty(re,"__esModule",{value:!0});re.checkStrictMode=re.getErrorPath=re.Type=re.useFunc=re.setEvaluated=re.evaluatedPropsToName=re.mergeEvaluated=re.eachItem=re.unescapeJsonPointer=re.escapeJsonPointer=re.escapeFragment=re.unescapeFragment=re.schemaRefOrVal=re.schemaHasRulesButRef=re.schemaHasRules=re.checkUnknownRules=re.alwaysValidSchema=re.toHash=void 0;var we=te(),oN=Qi();function iN(t){let e={};for(let r of t)e[r]=!0;return e}re.toHash=iN;function aN(t,e){return typeof e=="boolean"?e:Object.keys(e).length===0?!0:(ox(t,e),!ix(e,t.self.RULES.all))}re.alwaysValidSchema=aN;function ox(t,e=t.schema){let{opts:r,self:n}=t;if(!r.strictSchema||typeof e=="boolean")return;let s=n.RULES.keywords;for(let o in e)s[o]||ux(t,`unknown keyword: "${o}"`)}re.checkUnknownRules=ox;function ix(t,e){if(typeof t=="boolean")return!t;for(let r in t)if(e[r])return!0;return!1}re.schemaHasRules=ix;function cN(t,e){if(typeof t=="boolean")return!t;for(let r in t)if(r!=="$ref"&&e.all[r])return!0;return!1}re.schemaHasRulesButRef=cN;function uN({topSchemaRef:t,schemaPath:e},r,n,s){if(!s){if(typeof r=="number"||typeof r=="boolean")return r;if(typeof r=="string")return(0,we._)`${r}`}return(0,we._)`${t}${e}${(0,we.getProperty)(n)}`}re.schemaRefOrVal=uN;function lN(t){return ax(decodeURIComponent(t))}re.unescapeFragment=lN;function dN(t){return encodeURIComponent(Gf(t))}re.escapeFragment=dN;function Gf(t){return typeof t=="number"?`${t}`:t.replace(/~/g,"~0").replace(/\//g,"~1")}re.escapeJsonPointer=Gf;function ax(t){return t.replace(/~1/g,"/").replace(/~0/g,"~")}re.unescapeJsonPointer=ax;function pN(t,e){if(Array.isArray(t))for(let r of t)e(r);else e(t)}re.eachItem=pN;function nx({mergeNames:t,mergeToName:e,mergeValues:r,resultToName:n}){return(s,o,i,a)=>{let c=i===void 0?o:i instanceof we.Name?(o instanceof we.Name?t(s,o,i):e(s,o,i),i):o instanceof we.Name?(e(s,i,o),o):r(o,i);return a===we.Name&&!(c instanceof we.Name)?n(s,c):c}}re.mergeEvaluated={props:nx({mergeNames:(t,e,r)=>t.if((0,we._)`${r} !== true && ${e} !== undefined`,()=>{t.if((0,we._)`${e} === true`,()=>t.assign(r,!0),()=>t.assign(r,(0,we._)`${r} || {}`).code((0,we._)`Object.assign(${r}, ${e})`))}),mergeToName:(t,e,r)=>t.if((0,we._)`${r} !== true`,()=>{e===!0?t.assign(r,!0):(t.assign(r,(0,we._)`${r} || {}`),Yf(t,r,e))}),mergeValues:(t,e)=>t===!0?!0:{...t,...e},resultToName:cx}),items:nx({mergeNames:(t,e,r)=>t.if((0,we._)`${r} !== true && ${e} !== undefined`,()=>t.assign(r,(0,we._)`${e} === true ? true : ${r} > ${e} ? ${r} : ${e}`)),mergeToName:(t,e,r)=>t.if((0,we._)`${r} !== true`,()=>t.assign(r,e===!0?!0:(0,we._)`${r} > ${e} ? ${r} : ${e}`)),mergeValues:(t,e)=>t===!0?!0:Math.max(t,e),resultToName:(t,e)=>t.var("items",e)})};function cx(t,e){if(e===!0)return t.var("props",!0);let r=t.var("props",(0,we._)`{}`);return e!==void 0&&Yf(t,r,e),r}re.evaluatedPropsToName=cx;function Yf(t,e,r){Object.keys(r).forEach(n=>t.assign((0,we._)`${e}${(0,we.getProperty)(n)}`,!0))}re.setEvaluated=Yf;var sx={};function fN(t,e){return t.scopeValue("func",{ref:e,code:sx[e.code]||(sx[e.code]=new oN._Code(e.code))})}re.useFunc=fN;var Jf;(function(t){t[t.Num=0]="Num",t[t.Str=1]="Str"})(Jf||(re.Type=Jf={}));function mN(t,e,r){if(t instanceof we.Name){let n=e===Jf.Num;return r?n?(0,we._)`"[" + ${t} + "]"`:(0,we._)`"['" + ${t} + "']"`:n?(0,we._)`"/" + ${t}`:(0,we._)`"/" + ${t}.replace(/~/g, "~0").replace(/\\//g, "~1")`}return r?(0,we.getProperty)(t).toString():"/"+Gf(t)}re.getErrorPath=mN;function ux(t,e,r=t.opts.strictSchema){if(r){if(e=`strict mode: ${e}`,r===!0)throw new Error(e);t.self.logger.warn(e)}}re.checkStrictMode=ux});var Zr=S(Xf=>{"use strict";Object.defineProperty(Xf,"__esModule",{value:!0});var ot=te(),hN={data:new ot.Name("data"),valCxt:new ot.Name("valCxt"),instancePath:new ot.Name("instancePath"),parentData:new ot.Name("parentData"),parentDataProperty:new ot.Name("parentDataProperty"),rootData:new ot.Name("rootData"),dynamicAnchors:new ot.Name("dynamicAnchors"),vErrors:new ot.Name("vErrors"),errors:new ot.Name("errors"),this:new ot.Name("this"),self:new ot.Name("self"),scope:new ot.Name("scope"),json:new ot.Name("json"),jsonPos:new ot.Name("jsonPos"),jsonLen:new ot.Name("jsonLen"),jsonPart:new ot.Name("jsonPart")};Xf.default=hN});var oa=S(it=>{"use strict";Object.defineProperty(it,"__esModule",{value:!0});it.extendErrors=it.resetErrorsCount=it.reportExtraError=it.reportError=it.keyword$DataError=it.keywordError=void 0;var de=te(),zu=le(),mt=Zr();it.keywordError={message:({keyword:t})=>(0,de.str)`must pass "${t}" keyword validation`};it.keyword$DataError={message:({keyword:t,schemaType:e})=>e?(0,de.str)`"${t}" keyword must be ${e} ($data)`:(0,de.str)`"${t}" keyword is invalid ($data)`};function gN(t,e=it.keywordError,r,n){let{it:s}=t,{gen:o,compositeRule:i,allErrors:a}=s,c=px(t,e,r);n??(i||a)?lx(o,c):dx(s,(0,de._)`[${c}]`)}it.reportError=gN;function yN(t,e=it.keywordError,r){let{it:n}=t,{gen:s,compositeRule:o,allErrors:i}=n,a=px(t,e,r);lx(s,a),o||i||dx(n,mt.default.vErrors)}it.reportExtraError=yN;function vN(t,e){t.assign(mt.default.errors,e),t.if((0,de._)`${mt.default.vErrors} !== null`,()=>t.if(e,()=>t.assign((0,de._)`${mt.default.vErrors}.length`,e),()=>t.assign(mt.default.vErrors,null)))}it.resetErrorsCount=vN;function _N({gen:t,keyword:e,schemaValue:r,data:n,errsCount:s,it:o}){if(s===void 0)throw new Error("ajv implementation error");let i=t.name("err");t.forRange("i",s,mt.default.errors,a=>{t.const(i,(0,de._)`${mt.default.vErrors}[${a}]`),t.if((0,de._)`${i}.instancePath === undefined`,()=>t.assign((0,de._)`${i}.instancePath`,(0,de.strConcat)(mt.default.instancePath,o.errorPath))),t.assign((0,de._)`${i}.schemaPath`,(0,de.str)`${o.errSchemaPath}/${e}`),o.opts.verbose&&(t.assign((0,de._)`${i}.schema`,r),t.assign((0,de._)`${i}.data`,n))})}it.extendErrors=_N;function lx(t,e){let r=t.const("err",e);t.if((0,de._)`${mt.default.vErrors} === null`,()=>t.assign(mt.default.vErrors,(0,de._)`[${r}]`),(0,de._)`${mt.default.vErrors}.push(${r})`),t.code((0,de._)`${mt.default.errors}++`)}function dx(t,e){let{gen:r,validateName:n,schemaEnv:s}=t;s.$async?r.throw((0,de._)`new ${t.ValidationError}(${e})`):(r.assign((0,de._)`${n}.errors`,e),r.return(!1))}var cs={keyword:new de.Name("keyword"),schemaPath:new de.Name("schemaPath"),params:new de.Name("params"),propertyName:new de.Name("propertyName"),message:new de.Name("message"),schema:new de.Name("schema"),parentSchema:new de.Name("parentSchema")};function px(t,e,r){let{createErrors:n}=t.it;return n===!1?(0,de._)`{}`:bN(t,e,r)}function bN(t,e,r={}){let{gen:n,it:s}=t,o=[wN(s,r),SN(t,r)];return $N(t,e,o),n.object(...o)}function wN({errorPath:t},{instancePath:e}){let r=e?(0,de.str)`${t}${(0,zu.getErrorPath)(e,zu.Type.Str)}`:t;return[mt.default.instancePath,(0,de.strConcat)(mt.default.instancePath,r)]}function SN({keyword:t,it:{errSchemaPath:e}},{schemaPath:r,parentSchema:n}){let s=n?e:(0,de.str)`${e}/${t}`;return r&&(s=(0,de.str)`${s}${(0,zu.getErrorPath)(r,zu.Type.Str)}`),[cs.schemaPath,s]}function $N(t,{params:e,message:r},n){let{keyword:s,data:o,schemaValue:i,it:a}=t,{opts:c,propertyName:u,topSchemaRef:l,schemaPath:d}=a;n.push([cs.keyword,s],[cs.params,typeof e=="function"?e(t):e||(0,de._)`{}`]),c.messages&&n.push([cs.message,typeof r=="function"?r(t):r]),c.verbose&&n.push([cs.schema,i],[cs.parentSchema,(0,de._)`${l}${d}`],[mt.default.data,o]),u&&n.push([cs.propertyName,u])}});var mx=S(yo=>{"use strict";Object.defineProperty(yo,"__esModule",{value:!0});yo.boolOrEmptySchema=yo.topBoolOrEmptySchema=void 0;var xN=oa(),PN=te(),kN=Zr(),EN={message:"boolean schema is false"};function TN(t){let{gen:e,schema:r,validateName:n}=t;r===!1?fx(t,!1):typeof r=="object"&&r.$async===!0?e.return(kN.default.data):(e.assign((0,PN._)`${n}.errors`,null),e.return(!0))}yo.topBoolOrEmptySchema=TN;function IN(t,e){let{gen:r,schema:n}=t;n===!1?(r.var(e,!1),fx(t)):r.var(e,!0)}yo.boolOrEmptySchema=IN;function fx(t,e){let{gen:r,data:n}=t,s={gen:r,keyword:"false schema",data:n,schema:!1,schemaCode:!1,schemaValue:!1,params:{},it:t};(0,xN.reportError)(s,EN,void 0,e)}});var Qf=S(vo=>{"use strict";Object.defineProperty(vo,"__esModule",{value:!0});vo.getRules=vo.isJSONType=void 0;var RN=["string","number","integer","boolean","null","object","array"],AN=new Set(RN);function CN(t){return typeof t=="string"&&AN.has(t)}vo.isJSONType=CN;function DN(){let t={number:{type:"number",rules:[]},string:{type:"string",rules:[]},array:{type:"array",rules:[]},object:{type:"object",rules:[]}};return{types:{...t,integer:!0,boolean:!0,null:!0},rules:[{rules:[]},t.number,t.string,t.array,t.object],post:{rules:[]},all:{},keywords:{}}}vo.getRules=DN});var em=S(pn=>{"use strict";Object.defineProperty(pn,"__esModule",{value:!0});pn.shouldUseRule=pn.shouldUseGroup=pn.schemaHasRulesForType=void 0;function ON({schema:t,self:e},r){let n=e.RULES.types[r];return n&&n!==!0&&hx(t,n)}pn.schemaHasRulesForType=ON;function hx(t,e){return e.rules.some(r=>gx(t,r))}pn.shouldUseGroup=hx;function gx(t,e){var r;return t[e.keyword]!==void 0||((r=e.definition.implements)===null||r===void 0?void 0:r.some(n=>t[n]!==void 0))}pn.shouldUseRule=gx});var ia=S(at=>{"use strict";Object.defineProperty(at,"__esModule",{value:!0});at.reportTypeError=at.checkDataTypes=at.checkDataType=at.coerceAndCheckDataType=at.getJSONTypes=at.getSchemaTypes=at.DataType=void 0;var MN=Qf(),zN=em(),jN=oa(),K=te(),yx=le(),_o;(function(t){t[t.Correct=0]="Correct",t[t.Wrong=1]="Wrong"})(_o||(at.DataType=_o={}));function NN(t){let e=vx(t.type);if(e.includes("null")){if(t.nullable===!1)throw new Error("type: null contradicts nullable: false")}else{if(!e.length&&t.nullable!==void 0)throw new Error('"nullable" cannot be used without "type"');t.nullable===!0&&e.push("null")}return e}at.getSchemaTypes=NN;function vx(t){let e=Array.isArray(t)?t:t?[t]:[];if(e.every(MN.isJSONType))return e;throw new Error("type must be JSONType or JSONType[]: "+e.join(","))}at.getJSONTypes=vx;function LN(t,e){let{gen:r,data:n,opts:s}=t,o=qN(e,s.coerceTypes),i=e.length>0&&!(o.length===0&&e.length===1&&(0,zN.schemaHasRulesForType)(t,e[0]));if(i){let a=rm(e,n,s.strictNumbers,_o.Wrong);r.if(a,()=>{o.length?FN(t,e,o):nm(t)})}return i}at.coerceAndCheckDataType=LN;var _x=new Set(["string","number","integer","boolean","null"]);function qN(t,e){return e?t.filter(r=>_x.has(r)||e==="array"&&r==="array"):[]}function FN(t,e,r){let{gen:n,data:s,opts:o}=t,i=n.let("dataType",(0,K._)`typeof ${s}`),a=n.let("coerced",(0,K._)`undefined`);o.coerceTypes==="array"&&n.if((0,K._)`${i} == 'object' && Array.isArray(${s}) && ${s}.length == 1`,()=>n.assign(s,(0,K._)`${s}[0]`).assign(i,(0,K._)`typeof ${s}`).if(rm(e,s,o.strictNumbers),()=>n.assign(a,s))),n.if((0,K._)`${a} !== undefined`);for(let u of r)(_x.has(u)||u==="array"&&o.coerceTypes==="array")&&c(u);n.else(),nm(t),n.endIf(),n.if((0,K._)`${a} !== undefined`,()=>{n.assign(s,a),UN(t,a)});function c(u){switch(u){case"string":n.elseIf((0,K._)`${i} == "number" || ${i} == "boolean"`).assign(a,(0,K._)`"" + ${s}`).elseIf((0,K._)`${s} === null`).assign(a,(0,K._)`""`);return;case"number":n.elseIf((0,K._)`${i} == "boolean" || ${s} === null
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "agestra",
3
- "version": "4.13.0",
3
+ "version": "4.13.1",
4
4
  "description": "Multi-host AI orchestration toolkit for Claude Code, Codex CLI, and Gemini CLI",
5
5
  "type": "module",
6
6
  "packageManager": "npm@11.11.0",
@@ -0,0 +1,156 @@
1
+ // Single source of truth for agent permission categories.
2
+ //
3
+ // Five categories partition the nine Agestra agents by role.
4
+ // Each category fixes the exact `tools:` allowlist that the agent's
5
+ // frontmatter must declare, so Claude Code only loads the schemas the
6
+ // role legitimately needs and any drift is caught by a consistency test.
7
+
8
+ const MCP_PREFIX = "mcp__plugin_agestra_agestra__";
9
+
10
+ function withMcpPrefix(names) {
11
+ return names.map((name) => `${MCP_PREFIX}${name}`);
12
+ }
13
+
14
+ export const STANDARD_NON_WRITE_TOOLS = Object.freeze([
15
+ "Read",
16
+ "Glob",
17
+ "Grep",
18
+ "Bash",
19
+ "WebFetch",
20
+ "WebSearch",
21
+ "TodoWrite",
22
+ "AskUserQuestion",
23
+ "Skill",
24
+ "ToolSearch",
25
+ "CronCreate",
26
+ "CronList",
27
+ "CronDelete",
28
+ "Agent",
29
+ ]);
30
+
31
+ const ORCHESTRATOR_LEAD_MCP = Object.freeze(
32
+ withMcpPrefix([
33
+ "environment_check",
34
+ "provider_list",
35
+ "provider_health",
36
+ "trace_query",
37
+ "trace_summary",
38
+ "trace_visualize",
39
+ "ai_chat",
40
+ "ai_analyze_files",
41
+ "ai_compare",
42
+ "agent_debate_structured",
43
+ "agent_debate_status",
44
+ "agent_debate_approve",
45
+ "agent_debate_continue",
46
+ "agent_debate_reject",
47
+ "agent_cross_validate",
48
+ "cli_worker_spawn",
49
+ "cli_worker_status",
50
+ "cli_worker_collect",
51
+ "cli_worker_stop",
52
+ "agent_changes_review",
53
+ "agent_changes_accept",
54
+ "agent_changes_reject",
55
+ ]),
56
+ );
57
+
58
+ const ORCHESTRATOR_MODERATOR_MCP = Object.freeze(
59
+ withMcpPrefix([
60
+ "provider_list",
61
+ "agent_debate_structured",
62
+ "agent_debate_status",
63
+ "agent_debate_approve",
64
+ "agent_debate_continue",
65
+ "agent_debate_reject",
66
+ "agent_debate_review",
67
+ "ai_chat",
68
+ "workspace_read",
69
+ "workspace_create_document",
70
+ ]),
71
+ );
72
+
73
+ const ORCHESTRATOR_LEAD_TOOLS = Object.freeze([
74
+ ...STANDARD_NON_WRITE_TOOLS,
75
+ ...ORCHESTRATOR_LEAD_MCP,
76
+ ]);
77
+
78
+ const ORCHESTRATOR_MODERATOR_TOOLS = Object.freeze([
79
+ ...STANDARD_NON_WRITE_TOOLS,
80
+ ...ORCHESTRATOR_MODERATOR_MCP,
81
+ ]);
82
+
83
+ const WRITER_TOOLS = Object.freeze([...STANDARD_NON_WRITE_TOOLS, "Write"]);
84
+
85
+ export const CATEGORIES = Object.freeze({
86
+ "orchestrator-lead": Object.freeze({
87
+ members: Object.freeze(["agestra-team-lead"]),
88
+ policy: "mcp-allowlist",
89
+ tools: ORCHESTRATOR_LEAD_TOOLS,
90
+ description:
91
+ "Full-lifecycle orchestrator. Spawns workers, reviews and accepts worktree changes, runs structured debates. Does not write files directly.",
92
+ }),
93
+ "orchestrator-moderator": Object.freeze({
94
+ members: Object.freeze(["agestra-moderator"]),
95
+ policy: "mcp-allowlist",
96
+ tools: ORCHESTRATOR_MODERATOR_TOOLS,
97
+ description:
98
+ "Debate facilitator and result aggregator. Reads workspace and creates aggregation documents through MCP. Does not spawn workers, accept changes, or write files directly.",
99
+ }),
100
+ "artifact-writer": Object.freeze({
101
+ members: Object.freeze(["agestra-designer", "agestra-ideator"]),
102
+ policy: "tools-allowlist",
103
+ tools: WRITER_TOOLS,
104
+ description:
105
+ "Writes design or idea decision Markdown under docs/plans/ or docs/ideas/. Has Write but no MCP tools to prevent orchestration or change-acceptance leakage.",
106
+ }),
107
+ "report-writer": Object.freeze({
108
+ members: Object.freeze([
109
+ "agestra-qa",
110
+ "agestra-reviewer",
111
+ "agestra-security",
112
+ ]),
113
+ policy: "tools-allowlist",
114
+ tools: WRITER_TOOLS,
115
+ description:
116
+ "Writes QA, review, or security reports under docs/reports/. Has Write but no MCP tools to keep verification roles from accepting changes or spawning workers.",
117
+ }),
118
+ implementation: Object.freeze({
119
+ members: Object.freeze([
120
+ "agestra-implementer",
121
+ "agestra-e2e-writer",
122
+ ]),
123
+ policy: "open",
124
+ tools: null,
125
+ description:
126
+ "Applies scoped code or test changes. Tool surface is intentionally unconstrained at the frontmatter level so implementation can use whatever the task requires.",
127
+ }),
128
+ });
129
+
130
+ export const ALL_AGENTS = Object.freeze(
131
+ Object.values(CATEGORIES).flatMap((category) => [...category.members]),
132
+ );
133
+
134
+ export function categoryForAgent(agentName) {
135
+ for (const [name, category] of Object.entries(CATEGORIES)) {
136
+ if (category.members.includes(agentName)) {
137
+ return { name, ...category };
138
+ }
139
+ }
140
+ return null;
141
+ }
142
+
143
+ export function expectedToolsForAgent(agentName) {
144
+ const category = categoryForAgent(agentName);
145
+ if (!category) {
146
+ throw new Error(`Unknown agent: ${agentName}`);
147
+ }
148
+ return category.tools;
149
+ }
150
+
151
+ export function isOrchestratorCategory(categoryName) {
152
+ return (
153
+ categoryName === "orchestrator-lead" ||
154
+ categoryName === "orchestrator-moderator"
155
+ );
156
+ }