commit c41279f305718c203a3237875064eb49fd790801 Author: Zhongwei Li Date: Sun Nov 30 08:46:35 2025 +0800 Initial commit diff --git a/.claude-plugin/plugin.json b/.claude-plugin/plugin.json new file mode 100644 index 0000000..16c0a71 --- /dev/null +++ b/.claude-plugin/plugin.json @@ -0,0 +1,14 @@ +{ + "name": "agent-prompts", + "description": "AI Agent基本原則・動作モード・カスタムコマンド", + "version": "1.0.0", + "author": { + "name": "otolab" + }, + "commands": [ + "./commands" + ], + "hooks": [ + "./hooks" + ] +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..b9addd4 --- /dev/null +++ b/README.md @@ -0,0 +1,3 @@ +# agent-prompts + +AI Agent基本原則・動作モード・カスタムコマンド diff --git a/commands/README.md b/commands/README.md new file mode 100644 index 0000000..aef6174 --- /dev/null +++ b/commands/README.md @@ -0,0 +1,38 @@ +# Claude Code カスタムコマンド集 + +このディレクトリには、Claude Code用のカスタムコマンド定義を管理しています。 + +## 利用可能なコマンド + +### `/advisory` - アドバイザリーモード +前提を疑い、作業の問題点を客観的に発見するコマンド。 +- 基本: `/advisory` - 基本的な前提チェック +- 詳細: `/advisory deep` - Toulminモデルによる論理検証を含む + +## セットアップ方法 + +このリポジトリはClaude Codeプラグインとして構成されています。 + +### プラグインとしてインストール +```bash +# Claude Codeのプラグインディレクトリにクローンまたはリンク +ln -sfn $(pwd) ~/.claude/plugins/ai-agent-prompts + +# または直接クローン +git clone https://github.com/otolab/ai-agent-prompts ~/.claude/plugins/ai-agent-prompts +``` + +### レガシー方法(プラグイン非対応環境) +```bash +# コマンドディレクトリへのシンボリックリンク +ln -sfn $(pwd)/commands ~/.claude/commands +``` + +## コマンドの追加方法 +1. `commands/` ディレクトリに新しい `.md` ファイルを作成 +2. コマンドの実行内容を記述 +3. `.claude/commands/` にリンクまたはコピー +4. このREADMEに説明を追加 + +--- +**作成**: 2025年10月15日 \ No newline at end of file diff --git a/commands/advisory.md b/commands/advisory.md new file mode 100644 index 0000000..5a8b266 --- /dev/null +++ b/commands/advisory.md @@ -0,0 +1,256 @@ +# アドバイザリレポートの作成 - 前提を疑う + +## コアコンセプト +立場を変えることで、2つの視点から支援を提供する: +1. **作業プロセス**:無意識の前提を発見し、作業の問題点を客観的に見つける +2. **技術的課題**:複雑な問題を構造的に分析し、解決策を評価する + +## 実行方法 +- `/advisory` - 基本的な前提チェック +- `/advisory deep` - 詳細な論理検証を含む + +--- + +## 実行指示 + +### 1. 立場と目的の転換 +作業者からレビュアー/監査役へ立場を変更する。 +「作業を進める」から「問題を発見する」へ目的を転換する。 +優先度を速さから正確性・完全性へシフトする。 + +**既存TODO管理:** +現在のTODOリストを以下の形式で記録してから、アドバイザリー用の新しいTODOリストを作成する: +``` +【待避したTODO】 +- [タスク1]: [状態] +- [タスク2]: [状態] +``` +(ステップ12で復元時に参照) + +### 2. 原則の確認と動作モードの理解 +FOUNDATION_MODEが有効でなければ有効にする。 +作業の検証だけでなく、アドバイザリ自身もこの原則に従う。 +mode_listツールで利用可能なモードを確認し、mode_statusとmode_showで現在アクティブなモードを確認する(アドバイザリーは従わなくてよい)。 + +### 3. コミュニケーションの事実確認 +ユーザーが実際に言ったことを原文のまま確認する。 +アシスタントがどう返答したか、実際の応答を確認する。 +やり取りの時系列を追跡し、指示の変化を記録する。 +質問に対して回答したか、スルーした箇所はないか確認する。 + +### 4. 認識のズレを発見 +ユーザーの本当の意図は何だったか分析する。 +アシスタントはそれをどう理解したか検証する。 +どこで・なぜ認識のズレが生じたか特定する。 +「噛み合っていない」箇所を明確に発見する。 + +### 5. 状況まとめ(中間チェックポイント) +状況のまとめを文章化する。 +まとめた文章を確認し、理解できていないこと、追加で調査すべき観点を発見する。 + +**進捗通知:** +> 【中間チェックポイント1 - 状況確認】 +> ✅ 完了した調査:ステップ1-4 +> 📝 状況まとめ:[簡潔な要約] +> 🔍 追加調査項目:[ある場合は件数と概要] +> +> → 判定:[次のステップ6へ進みます/ステップ3に戻って再調査します] + +判定に基づき、次のステップに進むか、ステップ3に戻って再調査する。 + +### 6. 独立した視点で資料を探して読み込む +ドキュメントの一覧を作成。 +作業を行う上で重要な資料が存在しないか、十分に参照されているかを調べる。往々にして問題はコンテキストの不足から発生するため、「探す」ことは重要な作業である。 +作業者が明文化されていない情報に基づいて作業していないかを検証する。 +批判的な視点で読み、何が欠けているか、矛盾はないかを探す。 + +### 7. 現在のコンテキストを外部視点で調査 +作業手順は妥当か、目的に対して適切か確認する。 +TODOリストに書かれていない重要事項を探す。 +作業者が見落としている全体像を把握する。 +本来の目的から逸れていないか検証する。 + +### 8. 検証まとめ(中間チェックポイント) +検証のまとめを文章化する。 +作成した文章について批判的に検討し、論理の飛躍、情報の不足の可能性を洗い出す。 + +**進捗通知:** +> 【中間チェックポイント2 - 検証結果】 +> ✅ 完了した調査:ステップ6-7 +> 📊 検証結果:[良好/問題あり - 簡潔な要約] +> ⚠️ 論理の飛躍・情報不足:[ある場合は件数と概要] +> +> → 判定:[次のステップへ進みます/ステップ6に戻って再調査します] + +判定に基づき、次のステップに進むか、ステップ6に戻って再調査する。 + +### 9. 論理的検証(deepモード時のみ) +`/advisory deep` 実行時は、技術的な誤解・理解不足・ミスを体系的に発見するため、Toulminモデルによる論理検証を実施する。詳細は「deepモード詳細」セクションを参照。 +最後に「状況まとめ」「検証まとめ」自体を批判的に検証する。 + +**進捗通知(deepモード時):** +> 【論理検証完了】 +> 🔬 Toulminモデル8項目:[合格数/8] +> 📝 メタレベル検証:[まとめ自体の問題点] +> +> → 判定:[次のステップへ進みます/該当ステップに戻って再実行します] + +### 9.5. 技術的課題の分析(deepモード時) +技術的な課題が存在する場合、構造的に分析して解決策を評価する: + +**課題の特定** +- 何を実現しようとしているか(目標) +- どこで詰まっているか(障壁) +- 既知の制約・前提条件は何か +- エラーメッセージや症状の記録 + +**5 Whysによる根本原因分析** +1. なぜこの課題が発生しているか? +2. それはなぜか?(原因の原因) +3. さらになぜか?(より深い原因) +4. さらになぜか?(さらに深く) +5. 根本原因は何か? + +**課題の分類** +- 仕様の誤解・理解不足 +- 実装のミス・バグ +- 環境・設定の問題 +- 外部依存・ライブラリの問題 +- アーキテクチャ・設計の課題 + +**解決策の評価(複数案がある場合)** +各解決アプローチについて: +- アプローチの概要 +- メリット(利点・強み) +- デメリット(欠点・弱み) +- リスク(潜在的な問題) +- 実装工数・複雑度 + +**推奨アプローチの選定** +- 最も適切な解決策とその理由 +- トレードオフの明示 +- 代替案の提示 + +### 10. レポート作成 +> 【アドバイザリーからの報告書】 +> +> ## 作業プロセスの評価 +> 検出された問題: +> - 原則違反 +> - ユーザー意図との乖離 +> - 動作モードの不適切・未適用(mode_status/mode_showで確認) +> - 未読ファイル +> - 疑わしい実装 +> +> 推奨事項: +> [具体的な改善提案] +> +> ## 技術的課題の分析(deepモード時、課題がある場合) +> **根本原因**: +> [5 Whysの結果] +> +> **課題の分類**: +> [仕様誤解/実装ミス/環境問題/設計課題] +> +> **推奨する解決策**: +> [具体的なアプローチとその理由] +> +> **トレードオフと代替案**: +> [考慮すべきリスクと他の選択肢] + +### 11. アドバイザリとしての作業終了 +レポートを提出し、作業者モードに戻る。 + +### 12. 作業者として対応 +レポートを読んで問題を認識する。 +技術的な課題があれば詳細検証の依頼を提案する。 +発見された問題についてユーザーに報告する。 +ステップ1で待避した元のTODOリストを必要に応じて復元する。 +作業再開の許可をユーザーから得る。 + +--- + +## 調査項目詳細 + +### 作業手順の妥当性 +- 目的の理解 → 調査 → 実装の順序で進めているか +- 必要なドキュメントを読んでから作業しているか +- テスト → 実装 → ドキュメント更新の流れか +- ユーザーの確認を待ってから進めているか + +### 原則との整合性 +- FOUNDATION_MODEの5つの原則に違反していないか +- 特に「効率とは速さではなく確実性」を理解しているか +- 「結果と過程」の原則に従っているか + +### 知識と理解の検証 +- 仕様の根拠があるか +- 名前からの推測で進めていないか +- ハルシネーションしていないか +- 公式ドキュメントを読んだか + +--- + +## よくあるミスパターン + +### 品質の妥協 +- `any`型:正しい型定義を避けている +- `it.skip`:テストを無効化している +- `--no-verify`:検証を回避している +- lint警告の無視:「警告だから」という理由 + +### プロセスの問題 +- ドキュメント未読での実装 +- エラーメッセージを読まない対処 +- 「後で直す」「たぶん動く」という前提 +- 完了の勝手な宣言 + +### コミュニケーションの問題 +- ユーザーの質問に答えていない +- 確認を待たずに進めている +- 意図を勝手に解釈している +- エラーや問題を報告していない +- 適切な動作モードが有効になっていない(mode_statusで確認) +- モードの実行仕様を適用していない(mode_showで確認) + +--- + +## deepモード詳細 + +`/advisory deep` 実行時、または詳しい調査を求められた場合は、ステップ9で以下の構造的検証を実施: + +### Toulminモデルによる8つの質問 + +技術的な誤解、理解不足、ミスを体系的に発見するための論理検証: + +1. **推論は明確な前提から始まっているか?** + 前提を明示的に述べているか + +2. **前提は証拠や事実で裏付けられているか?** + ドキュメント、テスト結果、実行結果など + +3. **前提と結論の間に論理的なつながりがあるか?** + 飛躍していないか + +4. **その論理的つながりは妥当か?** + 他の解釈の可能性は? + +5. **推論は論理的誤謬を避けているか?** + 早まった一般化、誤った二分法など + +6. **結論は前提から論理的に導かれているか?** + 前提から必然的に導かれるか + +7. **推論は既存の知識や原則と整合しているか?** + ドキュメントや技術的ベストプラクティスと矛盾しないか + +8. **推論の結論は妥当で合理的か?** + 現実的で実装可能か + +--- + +## アドバイザリーの心構え + +アドバイザリーは作業者の敵ではなく、より良い成果のための協力者である。 +批判は建設的であるべきで、問題の指摘だけでなく改善策も提示する。良い手順について評価することも重要である。 +最終的な目的はユーザーの満足と、高品質な成果物の実現である。 \ No newline at end of file diff --git a/hooks/README.md b/hooks/README.md new file mode 100644 index 0000000..32d09dd --- /dev/null +++ b/hooks/README.md @@ -0,0 +1,117 @@ +# Hooks - Claude Codeフック集 + +Claude Codeのセッションやツール実行時に自動的に実行されるフックスクリプト集です。 + +## 利用可能なフック + +### start-session.js +**イベント**: SessionStart +**目的**: セッション開始時に動作モードの設定を促す + +セッション開始時にmode_listの実行と適切なモードの有効化を促します。 +セッションのソースタイプ(startup/resume/clear/compact)に応じて適切な指示を提供します。 +これにより、コンテキストリセット後も一貫した作業スタイルを維持できます。 + +### post-tool-use.sh +**イベント**: PostToolUse (Bashツール限定) +**目的**: cdコマンド実行後の現在ディレクトリをコンテキストに追加 + +Bashツールで`cd`コマンドが実行された際に、移動後の現在ディレクトリを`[cwd: /path/to/dir]`形式で +コンテキストに追加します。これにより、ディレクトリ移動の追跡が容易になります。 + +## フックの仕組み + +### 実行フロー +1. Claude Codeが特定のイベント(SessionStart、PostToolUse等)を検出 +2. `~/.claude/settings.json`のmatcherパターンに一致するか確認 +3. 一致した場合、指定されたコマンドを実行 +4. フックスクリプトがJSON形式で追加コンテキストを出力 +5. Claude Codeがコンテキストを会話に追加 + +### JSON出力形式 +```json +{ + "hookSpecificOutput": { + "hookEventName": "EventName", + "additionalContext": "追加するテキスト" + } +} +``` + +## 新規フックの作成方法 + +### 1. スクリプトの作成 +```bash +cat > hooks/my-hook.sh << 'EOF' +#!/bin/bash +echo '{ + "hookSpecificOutput": { + "hookEventName": "MyEvent", + "additionalContext": "カスタムメッセージ" + } +}' +EOF +``` + +### 2. 実行権限の付与 +```bash +chmod +x hooks/my-hook.sh +``` + +### 3. settings.jsonへの登録 +```json +"EventName": [ + { + "matcher": "パターン", + "hooks": [ + { + "type": "command", + "command": "~/Develop/otolab/ai-agent-prompts/hooks/my-hook.sh" + } + ] + } +] +``` + +## 環境変数 + +フックスクリプト内で利用可能な環境変数(イベントによって異なる): + +### PostToolUse +- `CLAUDE_CODE_TOOL_NAME` - 実行されたツール名(例: "Bash", "Read", "Write") +- `CLAUDE_CODE_TOOL_PARAMS` - ツールパラメータ(JSON文字列) +- `CLAUDE_CODE_TOOL_RESULT` - ツール実行結果(利用可能な場合) + +## デバッグ方法 + +### ログファイルへの出力 +```bash +#!/bin/bash +# デバッグ情報をログファイルに記録 +echo "$(date): Hook executed" >> /tmp/hook-debug.log +echo "Tool: $CLAUDE_CODE_TOOL_NAME" >> /tmp/hook-debug.log +``` + +### 環境変数の確認 +```bash +#!/bin/bash +# すべての環境変数を記録 +env > /tmp/hook-env.txt +``` + +## ベストプラクティス + +1. **軽量に保つ**: フックの実行時間を最小限に +2. **エラーハンドリング**: 失敗してもセッションを妨げない +3. **条件分岐**: 必要な場合のみ出力を生成 +4. **ログ記録**: デバッグ用のログは別ファイルに + +## 注意事項 + +- フックスクリプトの出力はJSON形式である必要があります +- 不正なJSON出力はエラーになる可能性があります +- matcherパターンは正確に設定してください +- 設定変更後はClaude Codeの再起動が必要です + +--- +**作成**: 2025年10月6日 \ No newline at end of file diff --git a/hooks/hooks.json b/hooks/hooks.json new file mode 100644 index 0000000..1d2713a --- /dev/null +++ b/hooks/hooks.json @@ -0,0 +1,15 @@ +{ + "hooks": { + "SessionStart": [ + { + "matcher": "startup|compact|resume|clear", + "hooks": [ + { + "type": "command", + "command": "${CLAUDE_PLUGIN_ROOT}/hooks/start-session.js" + } + ] + } + ] + } +} \ No newline at end of file diff --git a/hooks/post-tool-use.sh b/hooks/post-tool-use.sh new file mode 100755 index 0000000..6fb2823 --- /dev/null +++ b/hooks/post-tool-use.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +# PostToolUse hook for Bash tool +# 1. Adds current working directory to context when cd command is executed +# 2. Warns about git add -A/. without git status in the same command +# +# NOTE: このhookは一時的に無効化されています (2025-10-22) +# 理由: Bashツール実行時にエラーが発生してツールが異常終了する問題が確認された +# 問題が解決次第、hooks/hooks.jsonで再度有効化する予定 + +# Function to output JSON response +output_json() { + local additional_context="$1" + if [ -n "$additional_context" ]; then + cat < /dev/null 2>&1; then + MODIFIED_COUNT=$(git status --porcelain 2>/dev/null | wc -l | tr -d ' ') + WARNING="[git] ⚠️ ${MODIFIED_COUNT}個のファイルが対象 (git add -A/.) - git statusで内容を確認してください" + if [ -z "$MESSAGES" ]; then + MESSAGES="$WARNING" + else + MESSAGES="${MESSAGES} +${WARNING}" + fi + fi + fi + fi + + # Output combined messages + if [ -n "$MESSAGES" ]; then + output_json "$MESSAGES" + fi +fi \ No newline at end of file diff --git a/hooks/pre-compact.sh b/hooks/pre-compact.sh new file mode 100755 index 0000000..72c5f0c --- /dev/null +++ b/hooks/pre-compact.sh @@ -0,0 +1,24 @@ +#!/bin/bash + +# コンテキスト圧縮前のモード状態復元 + +MESSAGE="⚠️ コンテキスト圧縮前の処理\n\n" +MESSAGE="${MESSAGE}以下の手順を実行してください:\n" +MESSAGE="${MESSAGE}1. コンテキストから最新のアクティブなモードリストを検索\n" +MESSAGE="${MESSAGE}2. mode_setツールで状態を保存(MCPオンメモリに保存)\n\n" +MESSAGE="${MESSAGE}※ この処理により、compact後もMCPサーバーのモード状態が保持されます\n\n" + +MESSAGE="${MESSAGE}⚠️ コンテキスト圧縮完了後の処理\n\n" +MESSAGE="${MESSAGE}以下の手順をTODOの最初に追加してください:\n" +MESSAGE="${MESSAGE}1. **mode_show()** を実行してモード文書を再読み込み\n\n" +MESSAGE="${MESSAGE}※ 保存されたモードの状態を復元します。" + +# Use jq to properly encode JSON with multi-line strings +jq -n \ + --arg message "$MESSAGE" \ + '{ + hookSpecificOutput: { + hookEventName: "PreCompact", + additionalContext: $message + } + }' diff --git a/hooks/start-session.js b/hooks/start-session.js new file mode 100755 index 0000000..278314e --- /dev/null +++ b/hooks/start-session.js @@ -0,0 +1,113 @@ +#!/usr/bin/env node + +const fs = require('fs'); +const path = require('path'); + +// 標準入力からJSONを読み取る +let inputData = ''; + +process.stdin.on('data', (chunk) => { + inputData += chunk; +}); + +process.stdin.on('end', () => { + try { + const hookData = JSON.parse(inputData); + const source = hookData.source || 'unknown'; + + // sourceに応じたメッセージを構築 + const lines = [ + '═══════════════════════════════════════════' + ]; + + switch (source) { + case 'startup': + lines.push([ + '📋 **Session Setup - 新規セッション開始**', + '', + '作業環境をセットアップします。', + 'TodoWriteツールで以下のタスクをリストの最初に追加してください:', + '', + '1. mode_list()を実行して利用可能なモードを確認する', + '2. 適切なモードを有効化する', + '', + 'これらは作業開始前の準備ステップです。', + ].join('\n')); + break; + + case 'resume': + lines.push([ + '📋 **Session Setup - セッション再開**', + '', + '中断していたセッションを再開します。', + 'TodoWriteツールで以下のタスクをリストの最初に追加してください:', + '', + '1. コンテキストから最新のアクティブモードリストを検索する', + '2. mode_set()を実行してモードをMCPメモリに復元する', + '', + 'これらは作業再開前の準備ステップです。', + ].join('\n')); + break; + + case 'clear': + lines.push([ + '📋 **Session Setup - モードクリア後**', + '', + 'アシスタント動作モードがクリアされました。', + 'TodoWriteツールで以下のタスクをリストの最初に追加してください:', + '', + '1. mode_show()を実行してモードドキュメントを再読み込みする', + '', + 'これは作業継続前の準備ステップです。', + ].join('\n')); + break; + + case 'compact': + lines.push([ + '📋 **Session Setup - コンテキスト圧縮後の再開**', + '', + 'コンテキストが圧縮されました。モード状態を復元します。', + 'TodoWriteツールで以下のタスクをリストの最初に追加してください:', + '', + '1. mode_show()を実行してモード状態を復元する', + '', + 'これは作業継続前の準備ステップです。', + '', + '💡 ヒント: mode_show()は「サマリが存在する場合でも原文を読み直す」指示を含んでいます。', + ].join('\n')); + break; + + default: + lines.push([ + '📋 **Session Setup**', + '', + `セッションが開始されました (source: ${source})。`, + 'TodoWriteツールで以下のタスクをリストの最初に追加してください:', + '', + '1. mode_list()を実行する', + ].join('\n')); + break; + } + + // .serenaディレクトリのチェック + const serenaPath = path.join(process.cwd(), '.serena'); + if (fs.existsSync(serenaPath) && fs.statSync(serenaPath).isDirectory()) { + lines.push('* Serenaのアクティベートを行ってください'); + } + + lines.push('═══════════════════════════════════════════'); + + // JSON出力 + const output = { + hookSpecificOutput: { + hookEventName: 'SessionStart', + additionalContext: lines.join('\n') + } + }; + + console.log(JSON.stringify(output, null, 2)); + } catch (error) { + console.error('Error processing hook input:', error.message); + process.exit(1); + } +}); diff --git a/plugin.lock.json b/plugin.lock.json new file mode 100644 index 0000000..feac258 --- /dev/null +++ b/plugin.lock.json @@ -0,0 +1,69 @@ +{ + "$schema": "internal://schemas/plugin.lock.v1.json", + "pluginId": "gh:otolab/ai-agent-prompts:agent-prompts", + "normalized": { + "repo": null, + "ref": "refs/tags/v20251128.0", + "commit": "2d88a2184b3fa984549d6767c3122a09e5ebd2a0", + "treeHash": "8d2d7392f49ea6c83f403d3da29f55325b40112b254fe0a2b1a364af8faf1622", + "generatedAt": "2025-11-28T10:27:32.617683Z", + "toolVersion": "publish_plugins.py@0.2.0" + }, + "origin": { + "remote": "git@github.com:zhongweili/42plugin-data.git", + "branch": "master", + "commit": "aa1497ed0949fd50e99e70d6324a29c5b34f9390", + "repoRoot": "/Users/zhongweili/projects/openmind/42plugin-data" + }, + "manifest": { + "name": "agent-prompts", + "description": "AI Agent基本原則・動作モード・カスタムコマンド", + "version": "1.0.0" + }, + "content": { + "files": [ + { + "path": "README.md", + "sha256": "0d783408f4b2c0a0592942de1e38752ad78b238738fc31f57f0bc53f904c9d39" + }, + { + "path": "hooks/pre-compact.sh", + "sha256": "3974efbcaa5694f6c1fa6215ab66d12edc502fda03f097f4c3b305b66bea7792" + }, + { + "path": "hooks/README.md", + "sha256": "6286bef1210333bc6c2bd4bbd33908df0eacb946090f200a229101f8ac98cae6" + }, + { + "path": "hooks/hooks.json", + "sha256": "1eb4ebb770f2ab5b583bb98a236deef69b12df89507b3315c8a3faa314aa8d17" + }, + { + "path": "hooks/post-tool-use.sh", + "sha256": "0c6ef48e11c82e44bc6247ce6399ed027e5895e193a54265502c130cf58e5fbf" + }, + { + "path": "hooks/start-session.js", + "sha256": "d4f79000894603770f2a265644a35645807ea2fb3436b7f9695cdc4be8d3bc54" + }, + { + "path": ".claude-plugin/plugin.json", + "sha256": "c81dbb81d4e6e9d58dda469a0cad4f43bbabc088b12cde0d51cf10509bea2c01" + }, + { + "path": "commands/README.md", + "sha256": "e383eef2de4b4063a8376d95518109fad1cb39d17705b7bd24632593d69dc117" + }, + { + "path": "commands/advisory.md", + "sha256": "b76f788bfd81d12885601bfd452a9b9ca69d42823ad67baea50b5103ca2d21d7" + } + ], + "dirSha256": "8d2d7392f49ea6c83f403d3da29f55325b40112b254fe0a2b1a364af8faf1622" + }, + "security": { + "scannedAt": null, + "scannerVersion": null, + "flags": [] + } +} \ No newline at end of file