chore: fix .gitignore inline comments + add untracked code files
All checks were successful
Build & Deploy / build-and-deploy (push) Successful in 1m32s
G12 Leak-Guard / leak-guard (push) Successful in 5s
Lint — undefined names / undefined-names (push) Successful in 11s

Inline # comments in gitignore are not supported — they were silently
breaking three patterns (data/checkpoints/, data/adapter-migration-state.json,
.claude/agents/.generated/). Moved comments to their own lines and added
missing entries for runtime dirs (data/audit/, data/logs/, etc.) and
temp files (.interaction_tmp.json, .design-build/, .taskmaster bak files).

Also tracks previously untracked legitimate files: scripts, tests, docs,
skills references, .env.example, taskmaster templates.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-06-27 12:46:32 +00:00
parent b4a68cf5da
commit 4de555367d
17 changed files with 3108 additions and 3 deletions

View File

@@ -0,0 +1,348 @@
# Hooks: Case Status Webhooks Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** כאשר status של תיק משתנה ב-legal-ai (e.g. `qa_failed`, `exported`), ה-plugin מקבל webhook, מעדכן את ה-issue ב-Paperclip, ומעיר את ה-CEO במקרה הצורך.
**Architecture:** Legal-ai REST API קורא `pc_request("POST", "/api/plugins/marcusgroup.legal-ai/webhooks/case-status", ...)` אחרי כל שינוי status. ה-plugin מטפל ב-`onWebhook()` ומגיב: מוסיף תגובה לissue, מעיר CEO אם QA נכשל.
**Tech Stack:** TypeScript (plugin-legal-ai), Python/FastAPI (legal-ai web), `@paperclipai/plugin-sdk@2026.325.0`, `httpx` (Python), `pc_request` helper.
---
## File Map
| Action | File |
|--------|------|
| Modify | `plugin-legal-ai/src/worker.ts` — add `onWebhook()` to `definePlugin({})` |
| Modify | `plugin-legal-ai/plugin.json` — add `"webhooks.receive"` capability |
| Modify | `plugin-legal-ai/src/manifest.ts` — add webhook capability |
| Modify | `legal-ai/web/app.py` — emit webhook after `PUT /api/cases/{case_number}` |
| Modify | `legal-ai/web/paperclip_api.py` — add `emit_webhook()` helper |
---
## Task 1: Add `emit_webhook` helper ב-Python
**Files:**
- Modify: `legal-ai/web/paperclip_api.py`
- [ ] **Step 1: קרא את הקובץ הקיים**
```bash
head -90 /home/chaim/legal-ai/web/paperclip_api.py
```
- [ ] **Step 2: הוסף את ה-helper בסוף הקובץ**
פתח `/home/chaim/legal-ai/web/paperclip_api.py` והוסף אחרי הפונקציה `pc_request`:
```python
async def emit_case_status_webhook(
case_number: str,
old_status: str,
new_status: str,
company_id: str | None = None,
run_id: str | None = None,
) -> None:
"""Notify the Paperclip plugin that a case status changed.
Fire-and-forget: logs errors but never raises, so callers aren't blocked.
"""
try:
await pc_request(
"POST",
"/api/plugins/marcusgroup.legal-ai/webhooks/case-status",
json={
"caseNumber": case_number,
"oldStatus": old_status,
"newStatus": new_status,
"companyId": company_id,
"timestamp": datetime.utcnow().isoformat() + "Z",
},
run_id=run_id,
timeout=5.0,
)
except Exception as exc:
logger.warning("emit_case_status_webhook failed: %s", exc)
```
> **הערה:** `datetime` ו-`logger` כבר מיובאים ב-`app.py`. בדוק שהם מיובאים גם ב-`paperclip_api.py` — אם לא, הוסף `from datetime import datetime` ו-`import logging; logger = logging.getLogger(__name__)` בראש הקובץ.
- [ ] **Step 3: Commit**
```bash
cd /home/chaim/legal-ai
git add web/paperclip_api.py
git commit -m "feat: add emit_case_status_webhook helper"
```
---
## Task 2: צרף webhook לendpoint `PUT /api/cases/{case_number}`
**Files:**
- Modify: `legal-ai/web/app.py`
- [ ] **Step 1: מצא את ה-endpoint**
```bash
grep -n "PUT\|case_number\|update_case" /home/chaim/legal-ai/web/app.py | head -30
```
- [ ] **Step 2: קרא את הenable endpoint המלא**
זהה את הסקציה המלאה של ה-endpoint ואת הייבוא הקיים.
- [ ] **Step 3: הוסף import ל-emit_webhook**
בראש `app.py`, בסקציית ה-imports מ-`paperclip_api`:
```python
from .paperclip_api import pc_request, emit_case_status_webhook
```
- [ ] **Step 4: הוסף webhook emit בתוך הendpoint**
אחרי שהקוד מעדכן את ה-case (לפני ה-`return`), הוסף:
```python
# Notify plugin about status change (fire-and-forget)
if updates.get("status") and old_status != updates["status"]:
background_tasks.add_task(
emit_case_status_webhook,
case_number=case_number,
old_status=old_status,
new_status=updates["status"],
company_id=str(case.get("company_id")),
)
```
> אם ה-endpoint כבר מקבל `background_tasks: BackgroundTasks` — השתמש בו. אם לא, הוסף `background_tasks: BackgroundTasks` לחתימת הפונקציה. הוסף `from fastapi import BackgroundTasks` ל-imports.
- [ ] **Step 5: שמור את ה-`old_status` לפני ה-update**
בתחילת ה-endpoint handler, לפני קריאת ה-DB update:
```python
old_status = (await db.get_case(case_number) or {}).get("status", "")
```
- [ ] **Step 6: בדיקה בסיסית — שלח PUT ידני**
```bash
curl -s -X PUT https://legal-ai.nautilus.marcusgroup.org/api/cases/1130-25 \
-H "Content-Type: application/json" \
-d '{"status": "in_progress"}' | jq .status
```
בדוק ב-Paperclip logs שהwebhook נשלח (עדיין לא מטופל בצד ה-plugin):
```bash
pm2 logs paperclip --lines 20
```
- [ ] **Step 7: Commit**
```bash
cd /home/chaim/legal-ai
git add web/app.py
git commit -m "feat: emit case-status webhook on PUT /api/cases/:case"
```
---
## Task 3: הוסף `onWebhook()` ל-plugin
**Files:**
- Modify: `plugin-legal-ai/src/worker.ts`
- [ ] **Step 1: קרא את `definePlugin({})` הקיים**
```bash
grep -n "definePlugin\|onWebhook\|onHealth\|onShutdown" /home/chaim/plugin-legal-ai/src/worker.ts
```
- [ ] **Step 2: הוסף את `onWebhook` handler**
בתוך הobject שמועבר ל-`definePlugin({})`, אחרי `setup(ctx)`:
```typescript
onWebhook: async (input) => {
const { endpointKey, payload, companyId } = input as {
endpointKey: string;
payload: {
caseNumber: string;
oldStatus: string;
newStatus: string;
companyId: string;
timestamp: string;
};
companyId: string;
};
if (endpointKey !== "case-status") return;
const { caseNumber, oldStatus, newStatus } = payload;
ctx.logger.info(`Webhook: ${caseNumber} ${oldStatus}${newStatus}`);
// Find the Paperclip issue linked to this case
const stateKey = `case:${caseNumber}`;
const issueId = await ctx.state.get({ companyId }, stateKey);
if (!issueId) {
ctx.logger.warn(`No issue found for case ${caseNumber}`);
return;
}
const statusLabels: Record<string, string> = {
in_progress: "🔄 בעבודה",
drafted: "✍️ טיוטה מוכנה",
qa_failed: "❌ QA נכשל",
exported: "📄 יוצא ל-DOCX",
reviewed: "✅ נבדק",
final: "🎯 סופי",
};
const label = statusLabels[newStatus] ?? newStatus;
// Post a status comment on the issue
await ctx.issues.createComment({
issueId: issueId as string,
body: `**עדכון סטטוס:** ${label} (היה: ${oldStatus})`,
});
// Wake CEO if QA failed
if (newStatus === "qa_failed") {
const companies = await ctx.companies.list();
const company = companies.find((c) => c.id === companyId);
if (!company) return;
const CEO_IDS: Record<string, string> = {
"42a7acd0-30c5-4cbd-ac97-7424f65df294": "752cebdd-6748-4a04-aacd-c7ab0294ef33",
"8639e837-4c9d-47fa-a76b-95788d651896": "cdbfa8bc-3d61-41a4-a2e7-677ec7d34562",
};
const ceoId = CEO_IDS[companyId];
if (ceoId) {
await ctx.agents.invoke(ceoId, companyId, {
prompt: `תיק ${caseNumber} נכשל בבדיקת QA. עיין בתוצאות QA ותקן את הבעיות.`,
reason: "qa_failed webhook",
});
}
}
},
```
> **הערה:** `ctx` חייב להיות נגיש ב-`onWebhook`. אם `ctx` מוגדר בתוך `setup()` בלבד — הוצא אותו ל-closure חיצוני של ה-plugin object (ראה את הדפוס הקיים ב-`worker.ts`).
- [ ] **Step 3: בדק TypeScript**
```bash
cd /home/chaim/plugin-legal-ai && npx tsc --noEmit
```
Expected: 0 errors.
- [ ] **Step 4: Build**
```bash
cd /home/chaim/plugin-legal-ai && npm run build
```
Expected: `dist/worker.js` נוצר ללא שגיאות.
- [ ] **Step 5: Commit**
```bash
cd /home/chaim/plugin-legal-ai
git add src/worker.ts
git commit -m "feat: add onWebhook handler for case-status events"
```
---
## Task 4: הוסף capability ל-`plugin.json` ול-`manifest.ts`
**Files:**
- Modify: `plugin-legal-ai/plugin.json`
- Modify: `plugin-legal-ai/src/manifest.ts`
- [ ] **Step 1: הוסף `"webhooks.receive"` ל-capabilities**
ב-`plugin-legal-ai/plugin.json`, בarray `"capabilities"`, הוסף:
```json
"webhooks.receive"
```
- [ ] **Step 2: הוסף גם ב-`manifest.ts`**
```bash
grep -n "capabilities\|webhooks" /home/chaim/plugin-legal-ai/src/manifest.ts
```
הוסף `"webhooks.receive"` לarray שם.
- [ ] **Step 3: Re-install plugin**
```bash
cd /home/chaim/plugin-legal-ai && npm run build
npx paperclipai plugin uninstall marcusgroup.legal-ai \
--api-base http://localhost:3100 --api-key pcapi_legal_install_key_2026
npx paperclipai plugin install /home/chaim/plugin-legal-ai \
--api-base http://localhost:3100 --api-key pcapi_legal_install_key_2026
pm2 restart paperclip
```
- [ ] **Step 4: Commit**
```bash
cd /home/chaim/plugin-legal-ai
git add plugin.json src/manifest.ts
git commit -m "feat: add webhooks.receive capability to plugin manifest"
```
---
## Task 5: בדיקה end-to-end
- [ ] **Step 1: Deploy legal-ai**
```bash
cd /home/chaim/legal-ai
git push origin main
# המתן לבנייה (~2-4 דקות)
```
- [ ] **Step 2: שנה סטטוס תיק**
```bash
curl -s -X PUT https://legal-ai.nautilus.marcusgroup.org/api/cases/1130-25 \
-H "Content-Type: application/json" \
-d '{"status": "qa_failed"}' | jq .status
```
- [ ] **Step 3: בדוק שהתגובה הוספה ל-Paperclip issue**
```bash
# מצא את ה-issue הקשור לתיק 1130-25
pm2 logs paperclip --lines 30 | grep "1130-25\|webhook\|qa_failed"
```
Expected: תגובה "❌ QA נכשל" הוספה לissue. CEO הועיר.
- [ ] **Step 4: בדוק שינוי סטטוס שגרתי (לא QA)**
```bash
curl -s -X PUT https://legal-ai.nautilus.marcusgroup.org/api/cases/1130-25 \
-H "Content-Type: application/json" \
-d '{"status": "drafted"}' | jq .status
```
Expected: תגובה "✍️ טיוטה מוכנה" בissue. CEO **לא** הועיר.
---
## אימות סופי
| בדיקה | פקודה | תוצאה מצופה |
|-------|-------|-------------|
| QA נכשל → CEO מועיר | `PUT status=qa_failed` | תגובה + agent invocation |
| exported → תגובה בלבד | `PUT status=exported` | תגובה בלבד |
| שינוי ללא status | `PUT title=...` | שום webhook |
| תיק ללא issue | webhook לתיק חדש | לוג warning, ללא crash |

View File

@@ -0,0 +1,306 @@
# Per-Agent CLAUDE.md Versioning & Validation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** הוסף validation ו-version tracking לקבצי הוראות הסוכנים. כעת, לפני שה-sync script מחיל שינויים, הוא מאמת שכל `instructionsFilePath` קיים. בנוסף, metadata של הסוכן ב-DB יכיל `claude_md_mtime` — השינוי האחרון בקובץ — כדי לזהות drift.
**Architecture:** `sync_agents_across_companies.py` מקבל `--check-instructions` flag שסורק את כל הסוכנים ומדווח על קבצים חסרים/ישנים. ב-`--apply`, מתווספת בדיקת pre-flight שמבטלת את הsync אם קובץ חסר. `agents.metadata` מקבל `claude_md_mtime` עם ה-mtime בפועל של הקובץ.
**Tech Stack:** Python 3.10+, asyncpg, httpx, `os.path.getmtime()`, Paperclip REST API (`PATCH /api/agents/{id}`).
---
## File Map
| Action | File |
|--------|------|
| Modify | `legal-ai/scripts/sync_agents_across_companies.py``--check-instructions` flag, pre-flight, metadata update |
זה הקובץ היחיד שצריך לגעת בו. כל שאר הלוגיקה קיימת כבר.
---
## Task 1: הוסף `--check-instructions` flag
**Files:**
- Modify: `legal-ai/scripts/sync_agents_across_companies.py`
- [ ] **Step 1: קרא את החלק של `argparse` בסקריפט**
```bash
grep -n "argparse\|add_argument\|--verify\|--dry-run\|--apply" \
/home/chaim/legal-ai/scripts/sync_agents_across_companies.py | head -20
```
- [ ] **Step 2: הוסף את הargument**
מצא את הסקציה שמגדירה `args` והוסף:
```python
parser.add_argument(
"--check-instructions",
action="store_true",
help="Scan all agents' instructionsFilePath and report missing/outdated files",
)
```
- [ ] **Step 3: הוסף את הפונקציה `check_instructions()`**
הוסף לפני `async def main()`:
```python
async def check_instructions(agents: list[dict]) -> bool:
"""Print a report of all agents' instruction files. Returns True if all OK."""
all_ok = True
print(f"\n{'Agent':<30} {'File':<60} {'Status':<15} {'Size':>8} {'Modified'}")
print("-" * 120)
for agent in agents:
adapter_cfg = agent.get("adapter_config") or {}
if isinstance(adapter_cfg, str):
import json as _json
adapter_cfg = _json.loads(adapter_cfg)
file_path = adapter_cfg.get("instructionsFilePath", "")
name = agent.get("name", agent.get("id", "?"))[:29]
if not file_path:
print(f"{name:<30} {'(none)':<60} {'⚠️ NOT SET':<15}")
continue
if not os.path.exists(file_path):
print(f"{name:<30} {file_path[-59:]:<60} {'❌ MISSING':<15}")
all_ok = False
continue
stat = os.stat(file_path)
size_kb = stat.st_size // 1024
mtime = datetime.fromtimestamp(stat.st_mtime).strftime("%Y-%m-%d %H:%M")
# Compare with DB metadata
metadata = agent.get("metadata") or {}
if isinstance(metadata, str):
import json as _json
metadata = _json.loads(metadata)
db_mtime = metadata.get("claude_md_mtime", "")
actual_mtime = str(int(stat.st_mtime))
drift = " ⚠️ DRIFT" if db_mtime and db_mtime != actual_mtime else ""
print(f"{name:<30} {file_path[-59:]:<60} {'✅ OK':<15} {size_kb:>6}KB {mtime}{drift}")
print()
return all_ok
```
> `from datetime import datetime` ו-`import os` — בדוק שמיובאים בראש הסקריפט. אם לא, הוסף.
- [ ] **Step 4: הוסף קריאה ל-`check_instructions()` ב-`main()`**
בתוך `async def main()`, אחרי שloading הagents מה-DB:
```python
if args.check_instructions:
all_ok = await check_instructions(master_agents + mirror_agents)
sys.exit(0 if all_ok else 1)
```
- [ ] **Step 5: בדיקה**
```bash
cd /home/chaim/legal-ai
python scripts/sync_agents_across_companies.py --check-instructions
```
Expected: טבלה עם כל הסוכנים, paths, סטטוס ✅/❌.
- [ ] **Step 6: Commit**
```bash
cd /home/chaim/legal-ai
git add scripts/sync_agents_across_companies.py
git commit -m "feat: add --check-instructions flag to sync script"
```
---
## Task 2: הוסף pre-flight validation לפני `--apply`
**Files:**
- Modify: `legal-ai/scripts/sync_agents_across_companies.py`
- [ ] **Step 1: מצא את נקודת הכניסה של `--apply`**
```bash
grep -n "args.apply\|if.*apply\|apply.*mode" \
/home/chaim/legal-ai/scripts/sync_agents_across_companies.py | head -10
```
- [ ] **Step 2: הוסף pre-flight לפני apply**
בתחילת בלוק `--apply`, לפני כל שינוי:
```python
if args.apply:
# Pre-flight: abort if any agent is missing its instructions file
print("🔍 Pre-flight: checking instruction files...")
all_ok = await check_instructions(master_agents + mirror_agents)
if not all_ok:
print("❌ Abort: one or more instruction files are missing. Fix paths before --apply.")
sys.exit(1)
print("✅ Pre-flight passed.\n")
# ... rest of apply logic ...
```
- [ ] **Step 3: בדיקה — הפעל עם קובץ חסר (סימולציה)**
```bash
# שנה זמנית path לקובץ שלא קיים
cd /home/chaim/legal-ai
python scripts/sync_agents_across_companies.py --dry-run 2>&1 | head -5
```
Expected: dry-run עובר. אם תנסה `--apply` עם agent שhis file חסר — הsync יבוטל.
- [ ] **Step 4: Commit**
```bash
cd /home/chaim/legal-ai
git add scripts/sync_agents_across_companies.py
git commit -m "feat: add pre-flight instruction file validation before --apply"
```
---
## Task 3: עדכן `agents.metadata` עם `claude_md_mtime`
**Files:**
- Modify: `legal-ai/scripts/sync_agents_across_companies.py`
- [ ] **Step 1: מצא את `compute_diff()` או `apply_diff()`**
```bash
grep -n "def compute_diff\|def apply_diff\|def build_patch\|metadata" \
/home/chaim/legal-ai/scripts/sync_agents_across_companies.py | head -20
```
- [ ] **Step 2: הוסף פונקציה `get_claude_md_mtime()`**
```python
def get_claude_md_mtime(adapter_config: dict) -> str | None:
"""Return the Unix mtime of the agent's instructionsFilePath, or None if missing."""
path = adapter_config.get("instructionsFilePath", "")
if not path or not os.path.exists(path):
return None
return str(int(os.path.getmtime(path)))
```
- [ ] **Step 3: שלב mtime בbuild של metadata patch**
מצא את המקום שמכין את ה-`metadata` לsync. הוסף:
```python
# Build metadata patch with claude_md_mtime
current_metadata = master_agent.get("metadata") or {}
if isinstance(current_metadata, str):
import json as _json
current_metadata = _json.loads(current_metadata)
adapter_cfg = master_agent.get("adapter_config") or {}
if isinstance(adapter_cfg, str):
import json as _json
adapter_cfg = _json.loads(adapter_cfg)
mtime = get_claude_md_mtime(adapter_cfg)
if mtime:
current_metadata["claude_md_mtime"] = mtime
current_metadata["claude_md_last_synced"] = datetime.utcnow().isoformat() + "Z"
```
כלול את ה-`current_metadata` המעודכן ב-PATCH לAPI.
- [ ] **Step 4: בדוק שה-metadata מתעדכן**
הפעל `--dry-run` וחפש `claude_md_mtime` בoutput:
```bash
python scripts/sync_agents_across_companies.py --dry-run 2>&1 | grep -i "mtime\|metadata" | head -10
```
לאחר `--apply`, בדוק ב-DB:
```bash
psql -h localhost -p 54329 -U paperclip -c \
"SELECT name, metadata->>'claude_md_mtime' AS mtime FROM agents WHERE metadata->>'claude_md_mtime' IS NOT NULL LIMIT 5" \
paperclip
```
- [ ] **Step 5: Commit**
```bash
cd /home/chaim/legal-ai
git add scripts/sync_agents_across_companies.py
git commit -m "feat: track claude_md_mtime in agents.metadata during sync"
```
---
## Task 4: הוסף `make check-agents` shortcut
**Files:**
- Modify: `legal-ai/Makefile` (אם קיים) אחרת הוסף alias
- [ ] **Step 1: בדוק אם Makefile קיים**
```bash
ls /home/chaim/legal-ai/Makefile 2>/dev/null && echo "EXISTS" || echo "MISSING"
```
- [ ] **Step 2א: אם Makefile קיים — הוסף target**
```makefile
check-agents:
python scripts/sync_agents_across_companies.py --check-instructions
sync-agents-dry:
python scripts/sync_agents_across_companies.py --dry-run
sync-agents:
python scripts/sync_agents_across_companies.py --apply
```
- [ ] **Step 2ב: אם Makefile לא קיים — הוסף alias ל-`~/.bashrc`**
```bash
echo "alias check-agents='cd /home/chaim/legal-ai && python scripts/sync_agents_across_companies.py --check-instructions'" >> ~/.bashrc
source ~/.bashrc
```
- [ ] **Step 3: בדוק**
```bash
# אם Makefile:
make -C /home/chaim/legal-ai check-agents
# אם alias:
check-agents
```
Expected: טבלת סוכנים מוצגת.
- [ ] **Step 4: Commit (אם Makefile)**
```bash
cd /home/chaim/legal-ai
git add Makefile
git commit -m "feat: add check-agents and sync-agents make targets"
```
---
## אימות סופי
| בדיקה | פקודה | תוצאה מצופה |
|-------|-------|-------------|
| `--check-instructions` | `python sync_agents... --check-instructions` | טבלה עם ✅ לכל agent |
| Pre-flight בולם apply | מחק קובץ זמנית + `--apply` | Abort עם הודעה ברורה |
| mtime ב-DB | `SELECT metadata->>'claude_md_mtime' FROM agents` | timestamp לכל agent |
| DRIFT זוהה | שנה קובץ + `--check-instructions` | ⚠️ DRIFT מוצג |
| shortcut | `check-agents` או `make check-agents` | עובד |

View File

@@ -0,0 +1,412 @@
# Scheduled Background Agents Implementation Plan
> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
**Goal:** הוסף 2 cron jobs לplugin: (1) תזכורת על תיקים תקועים 3+ ימים, (2) ניתוח פידבק יו"ר שבועי עם עדכון `decision-lessons.md` אוטומטי.
**Architecture:** שני jobs חדשים נרשמים ב-`ctx.jobs.register()`. Job 1 קורא `/api/cases/stale?days=3` (endpoint חדש ב-legal-ai) ומוסיף תגובה לissues תקועים. Job 2 קורא `/api/chair-feedback/weekly-summary` (endpoint חדש), שולח לCEO agent שמעדכן את `decision-lessons.md`.
**Tech Stack:** TypeScript (plugin-legal-ai jobs), Python/FastAPI (legal-ai web), asyncpg, Paperclip SDK `ctx.jobs.register()`, `ctx.agents.invoke()`.
---
## File Map
| Action | File |
|--------|------|
| Modify | `plugin-legal-ai/src/worker.ts` — register 2 new jobs |
| Modify | `plugin-legal-ai/plugin.json` — declare 2 new job entries |
| Modify | `plugin-legal-ai/src/manifest.ts` — add to jobs array |
| Modify | `legal-ai/web/app.py` — add `GET /api/cases/stale` + `GET /api/chair-feedback/weekly-summary` |
| Modify | `legal-ai/web/database.py` (or equivalent DB module) — add `get_stale_cases()` + `get_weekly_chair_feedback()` |
---
## Task 1: הוסף endpoint `GET /api/cases/stale`
**Files:**
- Modify: `legal-ai/web/app.py`
- Modify: `legal-ai/web/database.py` (שם DB queries מנוהלים — בדוק עם `grep -n "async def get_cases\|from.*database\|import.*db" /home/chaim/legal-ai/web/app.py | head -10`)
- [ ] **Step 1: מצא את module ה-DB**
```bash
grep -n "^from\|^import\|db\." /home/chaim/legal-ai/web/app.py | head -20
```
זהה את שם הmodule שמכיל את DB queries (בד"כ `database.py` או `db.py`).
- [ ] **Step 2: הוסף `get_stale_cases()` לmodule ה-DB**
```python
async def get_stale_cases(days: int = 3) -> list[dict]:
"""Return cases whose status is not 'final' and haven't been updated in `days` days."""
async with get_db_connection() as conn:
rows = await conn.fetch(
"""
SELECT case_number, title, status, company_id,
updated_at,
now() - updated_at AS age
FROM cases
WHERE status NOT IN ('final', 'new')
AND updated_at < now() - ($1 || ' days')::interval
ORDER BY updated_at ASC
""",
str(days),
)
return [dict(r) for r in rows]
```
> `get_db_connection()` — השתמש בדפוס הקיים בקובץ. אם זה `asyncpg.connect()` ישיר, `asyncpg.create_pool()`, או context manager — העתק את הדפוס.
- [ ] **Step 3: הוסף endpoint ב-`app.py`**
```python
@app.get("/api/cases/stale")
async def list_stale_cases(days: int = 3):
"""Cases stuck in non-final status for more than `days` days."""
cases = await db.get_stale_cases(days=days)
return {
"cases": [
{
"case_number": c["case_number"],
"title": c["title"],
"status": c["status"],
"company_id": str(c["company_id"]),
"days_stale": c["age"].days,
}
for c in cases
],
"total": len(cases),
}
```
- [ ] **Step 4: בדיקה**
```bash
curl -s "https://legal-ai.nautilus.marcusgroup.org/api/cases/stale?days=1" | jq .total
```
Expected: JSON עם רשימת תיקים.
- [ ] **Step 5: Commit**
```bash
cd /home/chaim/legal-ai
git add web/app.py web/database.py # או השם הנכון
git commit -m "feat: add GET /api/cases/stale endpoint"
```
---
## Task 2: הוסף endpoint `GET /api/chair-feedback/weekly-summary`
**Files:**
- Modify: `legal-ai/web/app.py`
- Modify: DB module
- [ ] **Step 1: בדוק את מבנה טבלת `chair_feedback`**
```bash
sqlite3 /home/chaim/.paperclip/instances/default/data/app.db \
".schema chair_feedback" 2>/dev/null || \
psql -h localhost -p 5433 -U legal_ai -c "\d chair_feedback" legal_ai 2>/dev/null || \
grep -rn "chair_feedback" /home/chaim/legal-ai/mcp-server/src/ | head -10
```
- [ ] **Step 2: הוסף `get_weekly_chair_feedback()` לDB module**
```python
async def get_weekly_chair_feedback(days: int = 7) -> list[dict]:
"""Return chair feedback entries from the last `days` days."""
async with get_db_connection() as conn:
rows = await conn.fetch(
"""
SELECT cf.case_number, cf.feedback_text, cf.created_at,
cf.feedback_type, c.title
FROM chair_feedback cf
JOIN cases c ON c.case_number = cf.case_number
WHERE cf.created_at > now() - ($1 || ' days')::interval
ORDER BY cf.created_at DESC
""",
str(days),
)
return [dict(r) for r in rows]
```
> אם שמות השדות שונים (בדוק ב-Step 1) — התאם.
- [ ] **Step 3: הוסף endpoint**
```python
@app.get("/api/chair-feedback/weekly-summary")
async def get_chair_feedback_weekly(days: int = 7):
"""Feedback entries from the past week, formatted for the learning agent."""
entries = await db.get_weekly_chair_feedback(days=days)
if not entries:
return {"summary": "", "entry_count": 0}
lines = [
f"- תיק {e['case_number']} ({e['title']}): {e['feedback_text']}"
for e in entries
]
summary = "\n".join(lines)
return {"summary": summary, "entry_count": len(entries), "entries": entries}
```
- [ ] **Step 4: בדיקה**
```bash
curl -s "https://legal-ai.nautilus.marcusgroup.org/api/chair-feedback/weekly-summary" | jq .entry_count
```
- [ ] **Step 5: Commit**
```bash
cd /home/chaim/legal-ai
git add web/app.py web/database.py
git commit -m "feat: add GET /api/chair-feedback/weekly-summary endpoint"
```
---
## Task 3: הוסף jobs לplugin
**Files:**
- Modify: `plugin-legal-ai/plugin.json`
- Modify: `plugin-legal-ai/src/manifest.ts`
- [ ] **Step 1: קרא את ה-`jobs` הקיים ב-`plugin.json`**
```bash
cat /home/chaim/plugin-legal-ai/plugin.json | python3 -c "import json,sys; d=json.load(sys.stdin); print(json.dumps(d['jobs'], indent=2))"
```
- [ ] **Step 2: הוסף 2 jobs חדשים לarray `"jobs"` ב-`plugin.json`**
```json
{
"jobKey": "stale-case-reminder",
"displayName": "תזכורת תיקים תקועים",
"description": "מזהה תיקים שלא עודכנו 3+ ימים ומוסיף תגובה לissue",
"schedule": "0 8 * * *"
},
{
"jobKey": "weekly-feedback-analysis",
"displayName": "ניתוח פידבק שבועי",
"description": "מסכם פידבק יו\"ר מהשבוע האחרון ומעדכן את decision-lessons.md",
"schedule": "0 19 * * 0"
}
```
> `"0 8 * * *"` = כל יום בשעה 08:00. `"0 19 * * 0"` = כל ראשון ב-19:00.
- [ ] **Step 3: עדכן `manifest.ts`**
```bash
grep -n "jobs\|jobKey\|schedule" /home/chaim/plugin-legal-ai/src/manifest.ts
```
הוסף את אותם 2 objects לarray `jobs` ב-`manifest.ts`.
- [ ] **Step 4: Commit**
```bash
cd /home/chaim/plugin-legal-ai
git add plugin.json src/manifest.ts
git commit -m "feat: declare stale-case-reminder and weekly-feedback-analysis jobs"
```
---
## Task 4: Implement job handlers ב-`worker.ts`
**Files:**
- Modify: `plugin-legal-ai/src/worker.ts`
- [ ] **Step 1: קרא את handler של `sync-case-status` הקיים**
```bash
grep -n "sync-case-status\|jobs.register\|jobKey" /home/chaim/plugin-legal-ai/src/worker.ts
```
העתק את הדפוס.
- [ ] **Step 2: הוסף את `stale-case-reminder` handler**
בתוך `setup(ctx)`, אחרי רישום ה-job הקיים:
```typescript
ctx.jobs.register("stale-case-reminder", async (job) => {
ctx.logger.info("stale-case-reminder: starting");
const config = await ctx.config.get();
const apiBase = (config.legalApiBaseUrl as string) ?? "http://localhost:8085";
const resp = await ctx.http.fetch(`${apiBase}/api/cases/stale?days=3`);
if (!resp.ok) {
ctx.logger.error(`stale-case-reminder: API error ${resp.status}`);
return;
}
const data = (await resp.json()) as {
cases: Array<{
case_number: string;
title: string;
status: string;
company_id: string;
days_stale: number;
}>;
};
for (const staleCase of data.cases) {
const issueId = await ctx.state.get(
{ companyId: staleCase.company_id },
`case:${staleCase.case_number}`
);
if (!issueId) continue;
await ctx.issues.createComment({
issueId: issueId as string,
body: `⚠️ **תיק תקוע** — ${staleCase.days_stale} ימים ללא עדכון (סטטוס: ${staleCase.status}). האם יש צורך בפעולה?`,
});
ctx.logger.info(
`stale-case-reminder: posted reminder for ${staleCase.case_number} (${staleCase.days_stale}d stale)`
);
}
ctx.logger.info(`stale-case-reminder: done. ${data.cases.length} cases reminded`);
});
```
- [ ] **Step 3: הוסף את `weekly-feedback-analysis` handler**
```typescript
ctx.jobs.register("weekly-feedback-analysis", async (job) => {
ctx.logger.info("weekly-feedback-analysis: starting");
const config = await ctx.config.get();
const apiBase = (config.legalApiBaseUrl as string) ?? "http://localhost:8085";
const resp = await ctx.http.fetch(`${apiBase}/api/chair-feedback/weekly-summary`);
if (!resp.ok) {
ctx.logger.error(`weekly-feedback-analysis: API error ${resp.status}`);
return;
}
const data = (await resp.json()) as {
summary: string;
entry_count: number;
};
if (data.entry_count === 0) {
ctx.logger.info("weekly-feedback-analysis: no feedback this week, skipping");
return;
}
// Invoke the CEO agent to process feedback and update decision-lessons.md
const companies = await ctx.companies.list();
for (const company of companies) {
// CEO IDs per company
const CEO_IDS: Record<string, string> = {
"42a7acd0-30c5-4cbd-ac97-7424f65df294": "752cebdd-6748-4a04-aacd-c7ab0294ef33",
"8639e837-4c9d-47fa-a76b-95788d651896": "cdbfa8bc-3d61-41a4-a2e7-677ec7d34562",
};
const ceoId = CEO_IDS[company.id];
if (!ceoId) continue;
await ctx.agents.invoke(ceoId, company.id, {
prompt: `ניתוח פידבק שבועי יו"ר (${data.entry_count} פריטים):
${data.summary}
המשימה: עדכן את /home/chaim/legal-ai/docs/legal-decision-lessons.md עם הלקחים החדשים שעולים מהפידבק. הוסף רק לקחים חדשים שלא קיימים כבר. קבץ לפי נושא.`,
reason: "weekly-feedback-analysis scheduled job",
});
ctx.logger.info(
`weekly-feedback-analysis: invoked CEO for company ${company.id} (${data.entry_count} feedback entries)`
);
break; // One CEO is enough — lessons file is shared
}
});
```
- [ ] **Step 4: TypeScript check**
```bash
cd /home/chaim/plugin-legal-ai && npx tsc --noEmit
```
Expected: 0 errors.
- [ ] **Step 5: Build**
```bash
cd /home/chaim/plugin-legal-ai && npm run build
```
- [ ] **Step 6: Commit**
```bash
cd /home/chaim/plugin-legal-ai
git add src/worker.ts
git commit -m "feat: implement stale-case-reminder and weekly-feedback-analysis jobs"
```
---
## Task 5: Re-install plugin + בדיקה
- [ ] **Step 1: Deploy legal-ai**
```bash
cd /home/chaim/legal-ai && git push origin main
# המתן ~3 דקות
curl -s https://legal-ai.nautilus.marcusgroup.org/api/health | jq .status
```
- [ ] **Step 2: Re-install plugin**
```bash
cd /home/chaim/plugin-legal-ai && npm run build
npx paperclipai plugin uninstall marcusgroup.legal-ai \
--api-base http://localhost:3100 --api-key pcapi_legal_install_key_2026
npx paperclipai plugin install /home/chaim/plugin-legal-ai \
--api-base http://localhost:3100 --api-key pcapi_legal_install_key_2026
pm2 restart paperclip
```
- [ ] **Step 3: בדוק שה-jobs רשומים**
```bash
curl -s -H "Authorization: Bearer pcapi_legal_install_key_2026" \
http://localhost:3100/api/plugins/marcusgroup.legal-ai/jobs | jq .[].jobKey
```
Expected: `"sync-case-status"`, `"stale-case-reminder"`, `"weekly-feedback-analysis"`.
- [ ] **Step 4: הפעל job ידנית לבדיקה**
```bash
curl -s -X POST -H "Authorization: Bearer pcapi_legal_install_key_2026" \
http://localhost:3100/api/plugins/marcusgroup.legal-ai/jobs/stale-case-reminder/run | jq .
```
Expected: Job הופעל. בדוק logs:
```bash
pm2 logs paperclip --lines 30 | grep "stale-case-reminder"
```
---
## אימות סופי
| בדיקה | פקודה | תוצאה מצופה |
|-------|-------|-------------|
| API stale endpoint | `curl .../api/cases/stale?days=1` | JSON עם cases |
| API feedback endpoint | `curl .../api/chair-feedback/weekly-summary` | JSON עם summary |
| Jobs רשומים | `GET .../api/plugins/.../jobs` | 3 jobs רשומים |
| Stale reminder ידני | `POST .../jobs/stale-case-reminder/run` | תגובות בissues |
| Feedback analysis ידני | `POST .../jobs/weekly-feedback-analysis/run` | CEO מועיר |