diff --git a/.claude/agents/hermes-curator.md b/.claude/agents/hermes-curator.md index ce61e98..8d7a1d2 100644 --- a/.claude/agents/hermes-curator.md +++ b/.claude/agents/hermes-curator.md @@ -19,9 +19,15 @@ profiles: - **CMP** (תיקים 1xxx): רישוי ובניה. profile=`curator-cmp`. UUID `60dce831-...` - **CMPA** (תיקים 8xxx + 9xxx): היטלי השבחה ופיצויים. profile=`curator-cmpa`. UUID `d6f7c55d-...` -ה-CEO (`עוזר משפטי`, `claude_local`) הוא ה-orchestrator הראשי בכל חברה. -אני סוכן-משנה שמופעל אחרי שלב F (ייצוא DOCX) של ה-CEO. אני לא מחליף -אף סוכן קיים — מוסיף שכבת ניתוח חדשה. +**איך אני מופעל:** דפנה לוחצת "סמן כסופי" בקובץ ב-UI של legal-ai → +`POST /api/cases/{case_number}/exports/{filename}/mark-final` רץ ב-`web/app.py` → +הוא קורא ל-`pc_wake_curator_for_final()` ב-`web/paperclip_client.py` שיוצר +לי sub-issue ומעיר אותי. **לא דרך CEO** — חיבור ישיר מהאירוע ב-UI לסוכן. +זה מבטיח שאני מנתח את הגרסה האמיתית של דפנה, לא טיוטה אינטרמדיאטית. + +ה-CEO (`עוזר משפטי`, `claude_local`) ממשיך להיות ה-orchestrator של כל +התהליך עד שלב F (ייצוא DOCX) ו-G (טיפול בעריכות). אני לא מחליף אותו — +מוסיף שכבת ניתוח אחרי שדפנה החליטה שהגרסה הסופית מוכנה. ## תפקיד diff --git a/.claude/agents/legal-ceo.md b/.claude/agents/legal-ceo.md index 0ef318e..c79aca7 100644 --- a/.claude/agents/legal-ceo.md +++ b/.claude/agents/legal-ceo.md @@ -87,7 +87,7 @@ tools: | כותב החלטה | 7ed8686f-24bc-49a3-bc02-67ca15b895a9 | כתיבת בלוקים ה-יב (Opus) | | בודק איכות | 1a5b229e-9220-4b13-940c-f8eb7285fc29 | QA לפני ייצוא | | מייצא טיוטה | d0dc703b-ca83-4883-bca7-c9449e8713cd | בדיקה סופית + ייצוא DOCX מגורסת | -| מנהל ידע (Hermes) | CMP: 60dce831-5c5b-4bae-bda9-5282d506f0dc · CMPA: d6f7c55d-570a-46b8-8d72-1286d07da0d8 | סקירת החלטות סופיות, הצעות לעדכון style guide / lessons (POC — Hermes Agent). | +| מנהל ידע (Hermes) | CMP: 60dce831-5c5b-4bae-bda9-5282d506f0dc · CMPA: d6f7c55d-570a-46b8-8d72-1286d07da0d8 | סקירת החלטות סופיות, הצעות לעדכון style guide / lessons. **לא קורא ישירות מ-CEO** — מופעל אוטומטית מ-`web/app.py:api_mark_final` כשדפנה לוחצת "סמן כסופי" ב-UI. | ## כלל: כל issue חדש = תת-משימה @@ -452,61 +452,6 @@ Paperclip חוסם אוטומטית כל issue ב-`in_progress` שאין לו ru **מתי לחזור אחורה:** אם דוח QA מצביע על בעיה מתודולוגית (סילוגיזם חסר, כיוון לא תואם chair_directions) — חזור לשלב C/D ולא רק לכותב. -### שלב F2: סקירת ידע (Knowledge Curator) - -**מתי:** רק אחרי ש-F הושלם בהצלחה (ייצוא הצליח, comment "החלטה מוכנה לביקורת" פורסם). - -**מטרה:** להפעיל את **מנהל הידע** (Hermes) שיסקור את ההחלטה הסופית ויציע -עדכוני style guide / lessons. read-only על תוכן, write רק על comments. -בלאסט-רדיוס אפס — אם נכשל לא משנה את זרימת F. - -**זמין בשתי החברות (CMP + CMPA)** — כל אחת עם profile והחלטות-מקור משלה. -ה-lookup לפי שם מוצא את ה-curator הנכון בחברה הנוכחית. - -**תהליך:** - -```bash -# 1. בדוק אם קיים curator בחברה שלך -CURATOR_ID=$(PGPASSWORD=paperclip psql -h localhost -p 54329 -U paperclip -d paperclip -tA -c \ - "SELECT id FROM agents WHERE name='מנהל ידע' AND company_id='$PAPERCLIP_COMPANY_ID' LIMIT 1;") -if [ -z "$CURATOR_ID" ]; then - echo "[F2] No Knowledge Curator in company — skipping" -else - # 2. מצא את ה-issue הראשי של התיק - MAIN_ISSUE_ID=$(curl -s -H "Authorization: Bearer $PAPERCLIP_API_KEY" \ - "$PAPERCLIP_API_URL/issues/$PAPERCLIP_TASK_ID" \ - | python3 -c "import sys,json; d=json.load(sys.stdin); print(d.get('parentId') or d['id'])") - - # 3. צור sub-issue ל-curator (ללא המתנה לתשובה!) - SUB_ISSUE_ID=$(~/legal-ai/scripts/pc.sh POST "/api/issues/$MAIN_ISSUE_ID/children" "$(cat < dict: + """Wake the Knowledge Curator (Hermes) when a case is marked final. + + Creates a child issue under the main case issue, assigns it to the + curator, and triggers wakeup. Best-effort — silently skips if no + curator is configured for the company or no main issue is found. + + Returns ``{"status": "ok"|"skipped", ...}``. + """ + if not PAPERCLIP_BOARD_API_KEY: + logger.warning("PAPERCLIP_BOARD_API_KEY not set — skipping curator wakeup") + return {"status": "skipped", "reason": "no_api_key"} + + curator_id = CURATOR_AGENTS.get(company_id) + if not curator_id: + logger.info("No curator configured for company %s — skipping", company_id) + return {"status": "skipped", "reason": "no_curator", "company_id": company_id} + + issues = await get_case_issues(case_number) + if not issues: + logger.warning("No Paperclip issues found for case %s — skipping curator", case_number) + return {"status": "skipped", "reason": "no_issue"} + + main_issue = next((i for i in issues if i.get("status") == "in_progress"), None) or issues[0] + main_issue_id = main_issue["id"] + + description = ( + f"דפנה סימנה את ההחלטה הסופית של תיק {case_number} כסופית.\n" + f"קובץ סופי: `{final_filename}`\n\n" + f"סקור את ההחלטה מול skills/decision/SKILL.md ו-docs/legal-decision-lessons.md.\n" + f"חפש 3-5 דפוסי סגנון/דיון שלא תועדו. כתוב comment בעברית, ניטרלי, " + f"ממוספר. עדכן את MEMORY.md שלך. סגור את ה-issue (status=done)." + ) + child_resp = await pc_request( + "POST", + f"/api/issues/{main_issue_id}/children", + json={ + "title": f"[ערר {case_number}] סקירת ידע — Knowledge Curator", + "description": description, + "status": "in_progress", + "priority": "low", + "assigneeAgentId": curator_id, + }, + raise_on_error=True, + ) + sub_issue = child_resp.json() + sub_issue_id = sub_issue["id"] + + # Tag plugin_state for case-number visibility on the case page + try: + conn = await asyncpg.connect(PAPERCLIP_DB_URL) + try: + await _link_case_to_issue(conn, sub_issue_id, case_number) + finally: + await conn.close() + except Exception as e: + logger.warning("plugin_state link failed for sub_issue=%s: %s", sub_issue_id, e) + + # Trigger wakeup (use API per Paperclip rule — never DB insert) + wake_resp = await pc_request( + "POST", + f"/api/agents/{curator_id}/wakeup", + json={ + "source": "on_demand", + "triggerDetail": "manual", + "reason": f"final_marked_{case_number}", + "payload": { + "issueId": sub_issue_id, + "mutation": "assignment", + }, + }, + raise_on_error=True, + ) + logger.info( + "Curator wakeup for case %s: sub_issue=%s curator=%s wake=%s", + case_number, sub_issue_id, curator_id, wake_resp.status_code, + ) + return { + "status": "ok", + "sub_issue_id": sub_issue_id, + "curator_id": curator_id, + "main_issue_id": main_issue_id, + }