Files
awoooi/apps/api/src/hermes/agent_loader.py
Your Name 13e51802fe feat(awooop): Phase 0 全 ADR + Phase 1 control plane schema(含 critic 四項修正)
## Phase 0(文件層,全部 Accepted)
- ADR-106/107:AwoooP 平台架構 + 儲存策略
- ADR-111~118:Bootstrap → RLS 七項核心 ADR
- ADR-119~124:SAGA → Singleton Decomposition 六項 ADR
- ADR-UI-01~04:Operator Console 四個 UI ADR

## Phase 1(DB schema + migration)
- awooop_phase1_control_plane_2026-05-04.sql:7 張新表 + trigger + RLS
  - Step 1:三角色(platform_admin/migration BYPASSRLS,awooop_app 受 RLS)
  - Step 13:GRANT awooop_app 最小權限(7 條)
  - Step 14:RLS fail-closed,移除 __platform__ 後門
- awooop_phase1_batch1_rls_2026-05-04.sql:高流量四表三步式 ADD COLUMN
- awooop_phase1_batch1_backfill.py:SKIP LOCKED 分批回填腳本
- awooop_models.py:7 個 SQLAlchemy 2.x models

## Critic 修正(4 Critical + 3 Major)
- C-1:ADD CONSTRAINT IF NOT EXISTS → DO 塊 + pg_constraint 查詢
- C-2:__mapper_args__ 字串 list → primary_key=True on mapped_column
- C-3:__platform__ RLS 後門 → 全移除,改用 BYPASSRLS role
- C-4:awooop_app role 從未建立 → Step 1 + 7 條 GRANT
- M-1:active_pointer_guard SECURITY DEFINER(FORCE RLS 跨租戶保護)
- M-2:pg_partman create_parent 加冪等防護
- M-3:immutability trigger 新增身份欄位保護(project_id/family/contract_id)

## Task 1.2 修補
- agent_loader.py:硬編碼 Mac 路徑 → AGENTS_DIR 環境變數
- Dockerfile:補 COPY .claude/agents/

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-04 13:37:11 +08:00

42 lines
1.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"""載入 .claude/agents/*.md 並解析 system promptADR-095
2026-04-24 Claude Sonnet 4.6 (WS4 Hermes NL)
2026-05-04 Claude Sonnet 4.6 (Task 1.2): 移除本機絕對路徑,改用 AGENTS_DIR 環境變數
"""
from __future__ import annotations
import os
import pathlib
from functools import lru_cache
# 本機預設: /Users/ogt/awoooi/.claude/agents由 AGENTS_DIR 覆蓋)
# K8s 容器預設: /app/.claude/agentsDockerfile COPY .claude/agents/ ./.claude/agents/
_AGENTS_DIR = pathlib.Path(os.getenv("AGENTS_DIR", "/app/.claude/agents"))
def _parse_agent_md(path: pathlib.Path) -> str:
"""去除 YAML frontmatter回傳 body 作為 system prompt"""
text = path.read_text(encoding="utf-8")
# --- frontmatter ---
if text.startswith("---"):
end = text.find("---", 3)
if end != -1:
return text[end + 3:].strip()
return text.strip()
@lru_cache(maxsize=None)
def get_agent_system_prompt(agent_name: str) -> str | None:
"""
回傳指定 agent 的 system prompt。
若 .md 不存在回傳 Nonecaller 決定是否 fallback
"""
path = _AGENTS_DIR / f"{agent_name}.md"
if not path.exists():
return None
return _parse_agent_md(path)
def list_available_agents() -> list[str]:
"""回傳目前存在的 agent 名稱列表(無副檔名)"""
return [p.stem for p in sorted(_AGENTS_DIR.glob("*.md"))]