fix: .gitignore에서 .agent/ 트래킹 + 테스트 절대경로 제거 (이식성 보장)
This commit is contained in:
38
.agent/workflows/check-gitea.md
Normal file
38
.agent/workflows/check-gitea.md
Normal file
@@ -0,0 +1,38 @@
|
||||
---
|
||||
description: Gitea API로 variet-agent 저장소 커밋/이슈/PR 현황을 조회하는 워크플로우
|
||||
---
|
||||
|
||||
# Gitea 저장소 현황 조회
|
||||
|
||||
서비스 정보는 `.agent/workflows/services.md` 참조.
|
||||
|
||||
// turbo-all
|
||||
|
||||
## 절차
|
||||
|
||||
1. 최근 커밋 조회 (최신 10개):
|
||||
```powershell
|
||||
$h = @{Authorization="token 3a01b4b15a39921572e64c413353e870d4d2161b"}
|
||||
$commits = Invoke-RestMethod -Uri "https://git.variet.net/api/v1/repos/Variet/variet-agent/commits?limit=10&sha=main" -Headers $h
|
||||
$commits | ForEach-Object { Write-Host "$($_.sha.Substring(0,7)) $($_.commit.message.Split("`n")[0])" }
|
||||
```
|
||||
|
||||
2. 열린 이슈 조회:
|
||||
```powershell
|
||||
$h = @{Authorization="token 3a01b4b15a39921572e64c413353e870d4d2161b"}
|
||||
$issues = Invoke-RestMethod -Uri "https://git.variet.net/api/v1/repos/Variet/variet-agent/issues?state=open&type=issues" -Headers $h
|
||||
$issues | ForEach-Object { Write-Host "#$($_.number) $($_.title)" }
|
||||
```
|
||||
|
||||
3. PR 조회:
|
||||
```powershell
|
||||
$h = @{Authorization="token 3a01b4b15a39921572e64c413353e870d4d2161b"}
|
||||
Invoke-RestMethod -Uri "https://git.variet.net/api/v1/repos/Variet/variet-agent/pulls?state=open" -Headers $h
|
||||
```
|
||||
|
||||
4. Wiki 페이지 목록:
|
||||
```powershell
|
||||
$h = @{Authorization="token 3a01b4b15a39921572e64c413353e870d4d2161b"}
|
||||
$pages = Invoke-RestMethod -Uri "https://git.variet.net/api/v1/repos/Variet/variet-agent/wiki/pages" -Headers $h
|
||||
$pages | ForEach-Object { Write-Host $_.title }
|
||||
```
|
||||
43
.agent/workflows/check-status.md
Normal file
43
.agent/workflows/check-status.md
Normal file
@@ -0,0 +1,43 @@
|
||||
---
|
||||
description: 프로젝트 전체 작업 현황을 종합 체크하는 워크플로우 (Git + Vikunja + 로컬)
|
||||
---
|
||||
|
||||
# 프로젝트 현황 종합 체크
|
||||
|
||||
"작업상황 체크", "현황 확인", "status check" 등 요청 시 이 워크플로우를 실행합니다.
|
||||
|
||||
// turbo-all
|
||||
|
||||
## 절차
|
||||
|
||||
1. Git 로컬 상태 확인:
|
||||
```powershell
|
||||
git -C "c:\Users\CafeVariet-GL552VW\Desktop\source_diff\variet-agent" status --short
|
||||
```
|
||||
```powershell
|
||||
git -C "c:\Users\CafeVariet-GL552VW\Desktop\source_diff\variet-agent" log --oneline -5
|
||||
```
|
||||
|
||||
2. Vikunja 태스크 현황 조회:
|
||||
```powershell
|
||||
$h = @{Authorization="Bearer tk_070f8e0b715e818bb7178c3815ed5389040eddca"}
|
||||
$tasks = Invoke-RestMethod -Uri "https://plan.variet.net/api/v1/projects/7/tasks?per_page=50" -Headers $h
|
||||
$todo = $tasks | Where-Object { -not $_.done }
|
||||
$done = $tasks | Where-Object { $_.done }
|
||||
Write-Host "=== Vikunja: TODO $($todo.Count), DONE $($done.Count) ==="
|
||||
$todo | ForEach-Object { Write-Host " #$($_.id) $($_.title)" }
|
||||
```
|
||||
|
||||
3. Gitea 최근 커밋 확인 (리모트):
|
||||
```powershell
|
||||
$h = @{Authorization="token 3a01b4b15a39921572e64c413353e870d4d2161b"}
|
||||
$commits = Invoke-RestMethod -Uri "https://git.variet.net/api/v1/repos/Variet/variet-agent/commits?limit=5&sha=main" -Headers $h
|
||||
Write-Host "=== Gitea: Recent Commits ==="
|
||||
$commits | ForEach-Object { Write-Host " $($_.sha.Substring(0,7)) $($_.commit.message.Split("`n")[0])" }
|
||||
```
|
||||
|
||||
4. 결과를 종합하여 사용자에게 보고:
|
||||
- 로컬 uncommitted 변경 여부
|
||||
- 로컬 vs 리모트 커밋 차이
|
||||
- TODO 태스크 목록 + 우선순위
|
||||
- 다음 작업 제안
|
||||
50
.agent/workflows/check-vikunja.md
Normal file
50
.agent/workflows/check-vikunja.md
Normal file
@@ -0,0 +1,50 @@
|
||||
---
|
||||
description: Vikunja API로 Variet Agent 프로젝트 태스크 현황을 조회하는 워크플로우
|
||||
---
|
||||
|
||||
# Vikunja 태스크 현황 조회
|
||||
|
||||
서비스 정보는 `.agent/workflows/services.md` 참조.
|
||||
|
||||
// turbo-all
|
||||
|
||||
## 절차
|
||||
|
||||
1. 간편 조회 (TODO/DONE 리스트):
|
||||
```powershell
|
||||
C:\ProgramData\miniforge3\envs\quant\python.exe .agent\workflows\vikunja_helper.py list
|
||||
```
|
||||
|
||||
2. TODO만 조회:
|
||||
```powershell
|
||||
C:\ProgramData\miniforge3\envs\quant\python.exe .agent\workflows\vikunja_helper.py list todo
|
||||
```
|
||||
|
||||
3. DONE만 조회:
|
||||
```powershell
|
||||
C:\ProgramData\miniforge3\envs\quant\python.exe .agent\workflows\vikunja_helper.py list done
|
||||
```
|
||||
|
||||
4. 태스크 완료 처리 (**⚠️ 반드시 이 방법 사용 — 직접 API 호출 금지**):
|
||||
```powershell
|
||||
C:\ProgramData\miniforge3\envs\quant\python.exe .agent\workflows\vikunja_helper.py done {TASK_ID}
|
||||
```
|
||||
여러 태스크 동시:
|
||||
```powershell
|
||||
C:\ProgramData\miniforge3\envs\quant\python.exe .agent\workflows\vikunja_helper.py done 71 77 78
|
||||
```
|
||||
|
||||
5. 태스크 코멘트 추가:
|
||||
```powershell
|
||||
C:\ProgramData\miniforge3\envs\quant\python.exe .agent\workflows\vikunja_helper.py comment {TASK_ID} "내용"
|
||||
```
|
||||
|
||||
6. 새 태스크 생성:
|
||||
```powershell
|
||||
C:\ProgramData\miniforge3\envs\quant\python.exe .agent\workflows\vikunja_helper.py create "제목" "설명"
|
||||
```
|
||||
|
||||
> [!CAUTION]
|
||||
> **절대로** `Invoke-RestMethod -Method Post -Body '{"done": true}'` 같은 직접 API 호출을 사용하지 마세요.
|
||||
> Vikunja API는 POST 시 body에 포함되지 않은 필드를 빈값으로 덮어씁니다.
|
||||
> `vikunja_helper.py`는 항상 GET → 기존 필드 보존 → POST 패턴을 사용하여 title/description 보존을 보장합니다.
|
||||
21
.agent/workflows/dev.md
Normal file
21
.agent/workflows/dev.md
Normal file
@@ -0,0 +1,21 @@
|
||||
---
|
||||
description: 개발 서버 실행 방법
|
||||
---
|
||||
|
||||
## 환경 설정
|
||||
|
||||
1. Python 환경 활성화
|
||||
// turbo
|
||||
```
|
||||
C:\ProgramData\miniforge3\envs\quant\python.exe -m pip install -r requirements.txt
|
||||
```
|
||||
|
||||
2. API 서버 실행
|
||||
```
|
||||
C:\ProgramData\miniforge3\envs\quant\python.exe -m uvicorn api.server:app --reload --host 0.0.0.0 --port 8100
|
||||
```
|
||||
|
||||
3. Discord Bot 실행 (별도 터미널)
|
||||
```
|
||||
C:\ProgramData\miniforge3\envs\quant\python.exe -m api.discord_bot
|
||||
```
|
||||
47
.agent/workflows/git.md
Normal file
47
.agent/workflows/git.md
Normal file
@@ -0,0 +1,47 @@
|
||||
---
|
||||
description: Git 및 Gitea 워크플로우
|
||||
---
|
||||
|
||||
## 저장소 정보
|
||||
- **Remote**: https://git.variet.net/Variet/variet-agent.git
|
||||
- **기본 브랜치**: main
|
||||
- **Vikunja 프로젝트**: https://plan.variet.net/projects/7
|
||||
|
||||
## 커밋 컨벤션
|
||||
```
|
||||
feat: 새 기능
|
||||
fix: 버그 수정
|
||||
docs: 문서 변경
|
||||
refactor: 리팩토링
|
||||
test: 테스트 추가/수정
|
||||
chore: 빌드/설정 변경
|
||||
```
|
||||
|
||||
## 작업 흐름
|
||||
|
||||
1. 브랜치 생성
|
||||
// turbo
|
||||
```
|
||||
git checkout -b feat/feature-name
|
||||
```
|
||||
|
||||
2. 커밋
|
||||
// turbo
|
||||
```
|
||||
git add -A && git commit -m "feat: description"
|
||||
```
|
||||
|
||||
3. 푸시
|
||||
```
|
||||
git push origin feat/feature-name
|
||||
```
|
||||
|
||||
4. Gitea에서 PR 생성 또는 API로 자동 생성
|
||||
|
||||
## Wiki 업데이트
|
||||
작업 완료 시 Gitea Wiki에 관련 내용 업데이트.
|
||||
Wiki 구조:
|
||||
- Home: 프로젝트 개요
|
||||
- Architecture: 아키텍처 설명
|
||||
- Design-Decisions: 설계 결정 이유
|
||||
- Changelog: 버전별 변경 이력
|
||||
18
.agent/workflows/sync.md
Normal file
18
.agent/workflows/sync.md
Normal file
@@ -0,0 +1,18 @@
|
||||
---
|
||||
description: 작업 동기화 및 진행 보고 절차
|
||||
---
|
||||
|
||||
## 작업 시작 시
|
||||
1. Vikunja (plan.variet.net/projects/7) 에서 해당 태스크를 "진행 중"으로 변경
|
||||
2. 관련 브랜치 생성 (feat/ 또는 fix/)
|
||||
|
||||
## 작업 중간 동기화
|
||||
1. 의미 있는 단위로 커밋 + 푸시
|
||||
2. 필요 시 Gitea Wiki 업데이트 (Architecture, Changelog 등)
|
||||
3. 사용자에게 디스코드 또는 Vikunja 코멘트로 진행 상황 보고
|
||||
|
||||
## 작업 완료 시
|
||||
1. Gitea PR 생성
|
||||
2. Vikunja 태스크 "완료"로 변경
|
||||
3. Changelog 업데이트
|
||||
4. Wiki 관련 페이지 업데이트
|
||||
24
.agent/workflows/test.md
Normal file
24
.agent/workflows/test.md
Normal file
@@ -0,0 +1,24 @@
|
||||
---
|
||||
description: 테스트 실행 방법
|
||||
---
|
||||
|
||||
## 단위 테스트
|
||||
// turbo
|
||||
```
|
||||
C:\ProgramData\miniforge3\envs\quant\python.exe -m pytest tests/ -v
|
||||
```
|
||||
|
||||
## Context Manager 효과 테스트
|
||||
```
|
||||
C:\ProgramData\miniforge3\envs\quant\python.exe -m tests.test_context_manager
|
||||
```
|
||||
|
||||
## Task Pipeline E2E
|
||||
```
|
||||
C:\ProgramData\miniforge3\envs\quant\python.exe -m tests.test_pipeline_e2e
|
||||
```
|
||||
|
||||
## Gemini CLI 연동 테스트
|
||||
```
|
||||
gemini -p "Hello, respond with 'OK'" --approval-mode yolo -o json
|
||||
```
|
||||
171
.agent/workflows/vikunja_helper.py
Normal file
171
.agent/workflows/vikunja_helper.py
Normal file
@@ -0,0 +1,171 @@
|
||||
"""Vikunja safe task updater — preserves existing fields when updating tasks.
|
||||
|
||||
Usage:
|
||||
python vikunja_helper.py done 75 # Mark task #75 as done
|
||||
python vikunja_helper.py done 71 77 78 # Mark multiple tasks done
|
||||
python vikunja_helper.py undone 75 # Mark task #75 as not done
|
||||
python vikunja_helper.py comment 75 "text" # Add comment to task #75
|
||||
python vikunja_helper.py desc 75 "text" # Set description (appends if exists)
|
||||
python vikunja_helper.py create "title" "description" # Create a new task
|
||||
python vikunja_helper.py create "title" "description" --done # Create and mark done
|
||||
python vikunja_helper.py list # List all tasks
|
||||
python vikunja_helper.py list todo # List TODO only
|
||||
python vikunja_helper.py list done # List DONE only
|
||||
"""
|
||||
|
||||
import sys
|
||||
import json
|
||||
import urllib.request
|
||||
import urllib.error
|
||||
import io
|
||||
|
||||
# Fix Windows console encoding (cp949 → utf-8)
|
||||
if sys.stdout.encoding != "utf-8":
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
|
||||
API_BASE = "https://plan.variet.net/api/v1"
|
||||
TOKEN = "tk_070f8e0b715e818bb7178c3815ed5389040eddca"
|
||||
PROJECT_ID = 7
|
||||
|
||||
HEADERS = {
|
||||
"Authorization": f"Bearer {TOKEN}",
|
||||
"Content-Type": "application/json",
|
||||
}
|
||||
|
||||
|
||||
def api_get(path: str):
|
||||
req = urllib.request.Request(f"{API_BASE}{path}", headers=HEADERS)
|
||||
with urllib.request.urlopen(req) as resp:
|
||||
return json.loads(resp.read().decode("utf-8"))
|
||||
|
||||
|
||||
def api_post(path: str, data: dict):
|
||||
body = json.dumps(data).encode("utf-8")
|
||||
req = urllib.request.Request(f"{API_BASE}{path}", data=body, headers=HEADERS, method="POST")
|
||||
with urllib.request.urlopen(req) as resp:
|
||||
return json.loads(resp.read().decode("utf-8"))
|
||||
|
||||
|
||||
def api_put(path: str, data: dict):
|
||||
body = json.dumps(data).encode("utf-8")
|
||||
req = urllib.request.Request(f"{API_BASE}{path}", data=body, headers=HEADERS, method="PUT")
|
||||
with urllib.request.urlopen(req) as resp:
|
||||
return json.loads(resp.read().decode("utf-8"))
|
||||
|
||||
|
||||
def get_task(task_id: int) -> dict:
|
||||
"""GET full task object — preserves all fields."""
|
||||
return api_get(f"/tasks/{task_id}")
|
||||
|
||||
|
||||
def safe_update_task(task_id: int, updates: dict) -> dict:
|
||||
"""Safely update task: GET first, then POST with preserved fields."""
|
||||
task = get_task(task_id)
|
||||
safe_body = {
|
||||
"title": task.get("title", ""),
|
||||
"description": task.get("description", ""),
|
||||
"priority": task.get("priority", 0),
|
||||
"done": task.get("done", False),
|
||||
}
|
||||
safe_body.update(updates)
|
||||
return api_post(f"/tasks/{task_id}", safe_body)
|
||||
|
||||
|
||||
def mark_done(task_ids: list[int]):
|
||||
for tid in task_ids:
|
||||
result = safe_update_task(tid, {"done": True})
|
||||
title = result.get("title", "?")
|
||||
print(f" ✅ #{tid} → done=True [{title}]")
|
||||
|
||||
|
||||
def mark_undone(task_ids: list[int]):
|
||||
for tid in task_ids:
|
||||
result = safe_update_task(tid, {"done": False})
|
||||
title = result.get("title", "?")
|
||||
print(f" ⬜ #{tid} → done=False [{title}]")
|
||||
|
||||
|
||||
def add_comment(task_id: int, comment: str):
|
||||
result = api_put(f"/tasks/{task_id}/comments", {"comment": comment})
|
||||
print(f" 💬 #{task_id} comment added (id={result.get('id', '?')})")
|
||||
|
||||
|
||||
def set_description(task_id: int, desc: str, append: bool = True):
|
||||
task = get_task(task_id)
|
||||
existing = task.get("description", "") or ""
|
||||
if append and existing:
|
||||
new_desc = existing.rstrip() + "\n\n" + desc
|
||||
else:
|
||||
new_desc = desc
|
||||
result = safe_update_task(task_id, {"description": new_desc})
|
||||
print(f" 📝 #{task_id} description updated [{result.get('title', '?')}]")
|
||||
|
||||
|
||||
def list_tasks(filter_: str = "all"):
|
||||
tasks = api_get(f"/projects/{PROJECT_ID}/tasks?per_page=100")
|
||||
if filter_ == "todo":
|
||||
tasks = [t for t in tasks if not t["done"]]
|
||||
elif filter_ == "done":
|
||||
tasks = [t for t in tasks if t["done"]]
|
||||
|
||||
tasks.sort(key=lambda t: t["id"])
|
||||
for t in tasks:
|
||||
status = "✅" if t["done"] else "⬜"
|
||||
desc = (t.get("description") or "")[:50].replace("\n", " ")
|
||||
labels = ", ".join(l["title"] for l in (t.get("labels") or []))
|
||||
print(f" {status} #{t['id']:3d} {t['title'][:40]:<40} [{labels}] {desc}")
|
||||
print(f"\n Total: {len(tasks)} tasks")
|
||||
|
||||
|
||||
def create_task(title: str, description: str = "", done: bool = False):
|
||||
"""Create a new task in the project."""
|
||||
payload = {"title": title, "description": description}
|
||||
result = api_put(f"/projects/{PROJECT_ID}/tasks", payload)
|
||||
task_id = result["id"]
|
||||
print(f" ✨ #{task_id} created: {result.get('title', '?')}")
|
||||
if done:
|
||||
result = safe_update_task(task_id, {"done": True})
|
||||
print(f" ✅ #{task_id} → done=True")
|
||||
return result
|
||||
|
||||
|
||||
def main():
|
||||
if len(sys.argv) < 2:
|
||||
print(__doc__)
|
||||
return
|
||||
|
||||
cmd = sys.argv[1].lower()
|
||||
|
||||
if cmd == "done":
|
||||
ids = [int(x) for x in sys.argv[2:]]
|
||||
mark_done(ids)
|
||||
elif cmd == "undone":
|
||||
ids = [int(x) for x in sys.argv[2:]]
|
||||
mark_undone(ids)
|
||||
elif cmd == "comment":
|
||||
tid = int(sys.argv[2])
|
||||
comment = sys.argv[3]
|
||||
add_comment(tid, comment)
|
||||
elif cmd == "desc":
|
||||
tid = int(sys.argv[2])
|
||||
desc = sys.argv[3]
|
||||
set_description(tid, desc)
|
||||
elif cmd == "list":
|
||||
f = sys.argv[2] if len(sys.argv) > 2 else "all"
|
||||
list_tasks(f)
|
||||
elif cmd == "create":
|
||||
title = sys.argv[2] if len(sys.argv) > 2 else ""
|
||||
desc = sys.argv[3] if len(sys.argv) > 3 else ""
|
||||
is_done = "--done" in sys.argv
|
||||
if not title:
|
||||
print("Error: title is required")
|
||||
print(' Usage: vikunja_helper.py create "title" "description" [--done]')
|
||||
return
|
||||
create_task(title, desc, done=is_done)
|
||||
else:
|
||||
print(f"Unknown command: {cmd}")
|
||||
print(__doc__)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@@ -1,4 +1,5 @@
|
||||
.agent/
|
||||
# .agent/workflows/services.md 는 토큰이 포함되어 있으므로 제외
|
||||
.agent/workflows/services.md
|
||||
sessions/
|
||||
__pycache__/
|
||||
*.pyc
|
||||
|
||||
@@ -5,15 +5,18 @@ Tests against the variet-agent project itself.
|
||||
|
||||
import sys
|
||||
import io
|
||||
import os
|
||||
|
||||
if sys.stdout.encoding != "utf-8":
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
sys.path.insert(0, r"C:\Users\CafeVariet-GL552VW\Desktop\source_diff\variet-agent")
|
||||
|
||||
# 프로젝트 루트를 동적으로 결정 (tests/ 상위 디렉토리)
|
||||
PROJECT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
sys.path.insert(0, PROJECT)
|
||||
|
||||
from core.project_indexer import ProjectIndex
|
||||
from core.context_manager import ContextManager
|
||||
|
||||
PROJECT = r"C:\Users\CafeVariet-GL552VW\Desktop\source_diff\variet-agent"
|
||||
|
||||
|
||||
def test_indexer():
|
||||
print("=" * 60)
|
||||
|
||||
@@ -8,15 +8,17 @@ import io
|
||||
import asyncio
|
||||
import json
|
||||
import time
|
||||
import os
|
||||
|
||||
if sys.stdout.encoding != "utf-8":
|
||||
sys.stdout = io.TextIOWrapper(sys.stdout.buffer, encoding="utf-8", errors="replace")
|
||||
sys.path.insert(0, r"C:\Users\CafeVariet-GL552VW\Desktop\source_diff\variet-agent")
|
||||
|
||||
# 프로젝트 루트를 동적으로 결정 (tests/ 상위 디렉토리)
|
||||
PROJECT = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
sys.path.insert(0, PROJECT)
|
||||
|
||||
from core.task_pipeline import TaskPipeline
|
||||
|
||||
PROJECT = r"C:\Users\CafeVariet-GL552VW\Desktop\source_diff\variet-agent"
|
||||
|
||||
USER_REQUEST = (
|
||||
"project_indexer.py의 find_relevant 함수가 공백이 포함된 쿼리를 처리하지 못합니다. "
|
||||
"'gemini caller'로 검색하면 gemini_caller.py를 찾지 못합니다. "
|
||||
|
||||
Reference in New Issue
Block a user