ak5 1.0.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- ak5-1.0.0/.gitignore +64 -0
- ak5-1.0.0/Dockerfile +14 -0
- ak5-1.0.0/PKG-INFO +163 -0
- ak5-1.0.0/README.md +133 -0
- ak5-1.0.0/pyproject.toml +46 -0
- ak5-1.0.0/src/ak5/__init__.py +3 -0
- ak5-1.0.0/src/ak5/cli/__init__.py +3 -0
- ak5-1.0.0/src/ak5/cli/commands/agents.py +55 -0
- ak5-1.0.0/src/ak5/cli/commands/board.py +122 -0
- ak5-1.0.0/src/ak5/cli/commands/delegate.py +66 -0
- ak5-1.0.0/src/ak5/cli/commands/demo.py +230 -0
- ak5-1.0.0/src/ak5/cli/commands/login.py +47 -0
- ak5-1.0.0/src/ak5/cli/commands/mcp.py +8 -0
- ak5-1.0.0/src/ak5/cli/commands/serve.py +18 -0
- ak5-1.0.0/src/ak5/cli/config.py +35 -0
- ak5-1.0.0/src/ak5/cli/main.py +31 -0
- ak5-1.0.0/src/ak5/config.py +29 -0
- ak5-1.0.0/src/ak5/database.py +52 -0
- ak5-1.0.0/src/ak5/main.py +148 -0
- ak5-1.0.0/src/ak5/mcp/__init__.py +17 -0
- ak5-1.0.0/src/ak5/mcp/client.py +164 -0
- ak5-1.0.0/src/ak5/mcp/server.py +10 -0
- ak5-1.0.0/src/ak5/mcp/tools.py +149 -0
- ak5-1.0.0/src/ak5/models/__init__.py +16 -0
- ak5-1.0.0/src/ak5/models/actor.py +50 -0
- ak5-1.0.0/src/ak5/models/audit.py +38 -0
- ak5-1.0.0/src/ak5/models/base.py +24 -0
- ak5-1.0.0/src/ak5/models/board.py +40 -0
- ak5-1.0.0/src/ak5/models/column.py +46 -0
- ak5-1.0.0/src/ak5/models/ticket.py +157 -0
- ak5-1.0.0/src/ak5/routers/__init__.py +13 -0
- ak5-1.0.0/src/ak5/routers/actors.py +101 -0
- ak5-1.0.0/src/ak5/routers/auth.py +137 -0
- ak5-1.0.0/src/ak5/routers/boards.py +211 -0
- ak5-1.0.0/src/ak5/routers/events.py +37 -0
- ak5-1.0.0/src/ak5/routers/tickets.py +488 -0
- ak5-1.0.0/src/ak5/schemas/__init__.py +57 -0
- ak5-1.0.0/src/ak5/schemas/actor.py +47 -0
- ak5-1.0.0/src/ak5/schemas/audit.py +16 -0
- ak5-1.0.0/src/ak5/schemas/board.py +31 -0
- ak5-1.0.0/src/ak5/schemas/column.py +23 -0
- ak5-1.0.0/src/ak5/schemas/event.py +10 -0
- ak5-1.0.0/src/ak5/schemas/ticket.py +93 -0
- ak5-1.0.0/src/ak5/services/__init__.py +12 -0
- ak5-1.0.0/src/ak5/services/discovery.py +79 -0
- ak5-1.0.0/src/ak5/services/event_bus.py +72 -0
- ak5-1.0.0/src/ak5/services/lexorank.py +149 -0
- ak5-1.0.0/tests/conftest.py +109 -0
- ak5-1.0.0/tests/test_boards_api.py +94 -0
- ak5-1.0.0/tests/test_cli.py +31 -0
- ak5-1.0.0/tests/test_discovery.py +43 -0
- ak5-1.0.0/tests/test_lexorank.py +57 -0
- ak5-1.0.0/tests/test_mcp_tools.py +73 -0
- ak5-1.0.0/tests/test_tickets_api.py +199 -0
ak5-1.0.0/.gitignore
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
# Python
|
|
2
|
+
__pycache__/
|
|
3
|
+
*.py[cod]
|
|
4
|
+
*$py.class
|
|
5
|
+
*.so
|
|
6
|
+
.Python
|
|
7
|
+
build/
|
|
8
|
+
develop-eggs/
|
|
9
|
+
dist/
|
|
10
|
+
downloads/
|
|
11
|
+
eggs/
|
|
12
|
+
.eggs/
|
|
13
|
+
/lib/
|
|
14
|
+
/lib64/
|
|
15
|
+
parts/
|
|
16
|
+
sdist/
|
|
17
|
+
var/
|
|
18
|
+
wheels/
|
|
19
|
+
share/python-wheels/
|
|
20
|
+
*.egg-info/
|
|
21
|
+
.installed.cfg
|
|
22
|
+
*.egg
|
|
23
|
+
MANIFEST
|
|
24
|
+
|
|
25
|
+
# Virtual environments
|
|
26
|
+
.venv/
|
|
27
|
+
env/
|
|
28
|
+
venv/
|
|
29
|
+
ENV/
|
|
30
|
+
|
|
31
|
+
# Database
|
|
32
|
+
*.db
|
|
33
|
+
*.db-wal
|
|
34
|
+
*.db-shm
|
|
35
|
+
*.sqlite
|
|
36
|
+
*.sqlite3
|
|
37
|
+
|
|
38
|
+
# Node / Frontend
|
|
39
|
+
node_modules/
|
|
40
|
+
.next/
|
|
41
|
+
out/
|
|
42
|
+
build/
|
|
43
|
+
dist/
|
|
44
|
+
.npm
|
|
45
|
+
*.tsbuildinfo
|
|
46
|
+
|
|
47
|
+
# Testing & Coverage
|
|
48
|
+
.pytest_cache/
|
|
49
|
+
.coverage
|
|
50
|
+
htmlcov/
|
|
51
|
+
|
|
52
|
+
# OS / IDE
|
|
53
|
+
.DS_Store
|
|
54
|
+
.idea/
|
|
55
|
+
.vscode/
|
|
56
|
+
*.swp
|
|
57
|
+
*.swo
|
|
58
|
+
|
|
59
|
+
# Session
|
|
60
|
+
.ak5_session.json
|
|
61
|
+
|
|
62
|
+
# Local agent / scratch
|
|
63
|
+
.libragent/
|
|
64
|
+
tmp/
|
ak5-1.0.0/Dockerfile
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
FROM python:3.13-slim
|
|
2
|
+
|
|
3
|
+
WORKDIR /app
|
|
4
|
+
|
|
5
|
+
RUN pip install uv
|
|
6
|
+
|
|
7
|
+
COPY pyproject.toml README.md ./
|
|
8
|
+
COPY backend ./backend
|
|
9
|
+
|
|
10
|
+
RUN uv pip install --system -e backend
|
|
11
|
+
|
|
12
|
+
EXPOSE 8000
|
|
13
|
+
|
|
14
|
+
CMD ["uvicorn", "ak5.main:app", "--host", "0.0.0.0", "--port", "8000"]
|
ak5-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: ak5
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: AK5: Agent-Orchestrated Kanban System Gateway, MCP Server & CLI
|
|
5
|
+
Author: AK5 Team
|
|
6
|
+
License: MIT
|
|
7
|
+
Keywords: ai-agents,fastapi,kanban,mcp,multi-agent,orchestration
|
|
8
|
+
Classifier: Development Status :: 4 - Beta
|
|
9
|
+
Classifier: Intended Audience :: Developers
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Programming Language :: Python :: 3
|
|
12
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
15
|
+
Classifier: Topic :: Software Development :: Libraries :: Application Frameworks
|
|
16
|
+
Requires-Python: >=3.11
|
|
17
|
+
Requires-Dist: aiosqlite>=0.20.0
|
|
18
|
+
Requires-Dist: click>=8.1.0
|
|
19
|
+
Requires-Dist: fastapi>=0.115.0
|
|
20
|
+
Requires-Dist: httpx>=0.27.0
|
|
21
|
+
Requires-Dist: mcp>=1.0.0
|
|
22
|
+
Requires-Dist: pydantic-settings>=2.4.0
|
|
23
|
+
Requires-Dist: pydantic>=2.8.0
|
|
24
|
+
Requires-Dist: pyjwt>=2.9.0
|
|
25
|
+
Requires-Dist: rich>=13.7.0
|
|
26
|
+
Requires-Dist: sqlalchemy>=2.0.30
|
|
27
|
+
Requires-Dist: sse-starlette>=2.1.0
|
|
28
|
+
Requires-Dist: uvicorn[standard]>=0.30.0
|
|
29
|
+
Description-Content-Type: text/markdown
|
|
30
|
+
|
|
31
|
+
# AK5 (Agent K5) — Agent-Orchestrated Kanban System
|
|
32
|
+
|
|
33
|
+
AK5는 인간 사용자(PM, 개발자)와 자율 AI 에이전트(LLM Agents)가 단일 칸반(K5) 인터페이스 위에서 실시간으로 협업하고 작업을 위임(Delegation) 및 추적하는 **Agent-Orchestrated Kanban 시스템**입니다.
|
|
34
|
+
|
|
35
|
+
---
|
|
36
|
+
|
|
37
|
+
## 1. 주요 특징
|
|
38
|
+
|
|
39
|
+
* **Actor 추상화 일원화 (Unified Actor Model):** 인간과 AI 에이전트를 동일한 `Actor` 인터페이스로 취급 (`user_pm`, `agent_image_worker` 등).
|
|
40
|
+
* **역량 기반 발견 및 위임 (Capability-driven Discovery & Delegation):** 에이전트 역량 태그(`image-resize`, `code-review`, `security` 등) 및 자연어 검색 기반으로 작업 위임.
|
|
41
|
+
* **듀얼 프로토콜 (REST + SSE + MCP):**
|
|
42
|
+
* **Web UI (Next.js 15):** 드래그 앤 드롭(`@dnd-kit`), SSE 실시간 보드 자동 동기화, 계층형 서브태스크 진행률 바 및 아코디언 토글, 에이전트 활동 펄스(Activity Glow).
|
|
43
|
+
* **AI Agents (MCP):** Model Context Protocol 표준 툴셋(`ak5_list_available_agents`, `ak5_delegate_subtask`, `ak5_get_ticket_context`, `ak5_update_ticket_status`, `ak5_report_block`).
|
|
44
|
+
* **CLI (`ak5`):** 터미널 환경에서 액터 로그인, 에이전트 검색, 서브태스크 위임, 실시간 터미널 칸반 뷰(`ak5 board --watch`), 협업 시뮬레이션 데모(`ak5 demo`).
|
|
45
|
+
* **동시성 및 순서 정렬 보장:** Lexorank 알고리즘(Base36)을 통한 충돌 없는 임의 순서 삽입, SQLite WAL 모드 + Busy Timeout 설정.
|
|
46
|
+
|
|
47
|
+
---
|
|
48
|
+
|
|
49
|
+
## 2. 프로젝트 디렉토리 레이아웃
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
ak5/
|
|
53
|
+
├── backend/ # FastAPI + MCP Server
|
|
54
|
+
│ ├── src/ak5/
|
|
55
|
+
│ │ ├── main.py # FastAPI 진입점 & Lifespan & MCP 마운트
|
|
56
|
+
│ │ ├── config.py # Pydantic Settings
|
|
57
|
+
│ │ ├── database.py # SQLite WAL 비동기 세션
|
|
58
|
+
│ │ ├── models/ # SQLAlchemy 2.0 모델 (Actor, Board, Column, Ticket, Audit)
|
|
59
|
+
│ │ ├── schemas/ # Pydantic v2 DTO 스키마
|
|
60
|
+
│ │ ├── services/ # Lexorank, Discovery, EventBus (SSE)
|
|
61
|
+
│ │ ├── routers/ # REST API (auth, actors, boards, tickets, events)
|
|
62
|
+
│ │ └── mcp/ # MCP Server (MCPServer, tools, client)
|
|
63
|
+
│ └── tests/ # Pytest 종합 단위/통합 테스트 (19개)
|
|
64
|
+
│
|
|
65
|
+
├── frontend/ # Next.js 15 Web UI (React 18, Tailwind, @dnd-kit)
|
|
66
|
+
│ ├── src/
|
|
67
|
+
│ │ ├── app/ # App Router (layout.tsx, page.tsx, globals.css)
|
|
68
|
+
│ │ ├── components/
|
|
69
|
+
│ │ │ ├── board/ # KanbanBoard, KanbanColumn, TicketCard
|
|
70
|
+
│ │ │ └── actor/ # ActorBadge
|
|
71
|
+
│ │ └── lib/ # SSE 구독 및 API 클라이언트
|
|
72
|
+
│ └── package.json
|
|
73
|
+
│
|
|
74
|
+
├── cli/ # ak5 터미널 도구
|
|
75
|
+
│ └── src/ak5_cli/
|
|
76
|
+
│ ├── main.py # Click CLI 엔트리포인트
|
|
77
|
+
│ ├── config.py # 세션 및 API URL 관리
|
|
78
|
+
│ └── commands/ # login, agents, delegate, board, demo
|
|
79
|
+
│
|
|
80
|
+
├── docker-compose.yml # 컨테이너 오케스트레이션
|
|
81
|
+
└── pyproject.toml # uv 기반 워크스페이스 설정
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
---
|
|
85
|
+
|
|
86
|
+
## 3. 실행 방법
|
|
87
|
+
|
|
88
|
+
### 3.1 사전 요구사항
|
|
89
|
+
* Python 3.11+
|
|
90
|
+
* [uv](https://github.com/astral-sh/uv)
|
|
91
|
+
* Node.js 18+ 및 npm
|
|
92
|
+
|
|
93
|
+
### 3.2 백엔드 (FastAPI & MCP Gateway) 실행
|
|
94
|
+
```bash
|
|
95
|
+
# 가상환경 생성 및 의존성 설치
|
|
96
|
+
uv sync
|
|
97
|
+
uv pip install -e backend -e cli
|
|
98
|
+
|
|
99
|
+
# 백엔드 서버 기동 (기본 포트 8000)
|
|
100
|
+
uv run uvicorn ak5.main:app --host 127.0.0.1 --port 8000 --reload
|
|
101
|
+
```
|
|
102
|
+
* **Swagger API 문서:** `http://127.0.0.1:8000/docs`
|
|
103
|
+
* **MCP SSE 엔드포인트:** `http://127.0.0.1:8000/mcp/sse`
|
|
104
|
+
|
|
105
|
+
### 3.3 프론트엔드 (Next.js 15 Web Dashboard) 실행
|
|
106
|
+
```bash
|
|
107
|
+
cd frontend
|
|
108
|
+
npm install
|
|
109
|
+
npm run dev
|
|
110
|
+
```
|
|
111
|
+
* **웹 대시보드:** `http://localhost:3000`
|
|
112
|
+
|
|
113
|
+
### 3.4 CLI (`ak5`) 도구 사용법
|
|
114
|
+
```bash
|
|
115
|
+
# 1. 액터 등록 및 로그인
|
|
116
|
+
uv run ak5 login --id "agent_code_reviewer" --role "Senior Reviewer" --caps "python,rust,security"
|
|
117
|
+
|
|
118
|
+
# 2. 역량 기반 에이전트 검색
|
|
119
|
+
uv run ak5 agents --cap "image-resize"
|
|
120
|
+
|
|
121
|
+
# 3. 작업 위임 실행
|
|
122
|
+
uv run ak5 delegate TK-001 \
|
|
123
|
+
--to agent_image_worker \
|
|
124
|
+
--title "WebP 썸네일 변환기 구현" \
|
|
125
|
+
--desc "150x150 WebP 포맷 변환 함수 작성"
|
|
126
|
+
|
|
127
|
+
# 4. 실시간 터미널 칸반 뷰
|
|
128
|
+
uv run ak5 board --watch
|
|
129
|
+
|
|
130
|
+
# 5. 자율 멀티 에이전트 협업 데모 시뮬레이션 실행
|
|
131
|
+
uv run ak5 demo
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
---
|
|
135
|
+
|
|
136
|
+
## 4. MCP (Model Context Protocol) 툴셋
|
|
137
|
+
|
|
138
|
+
외부 에이전트(Claude Desktop, Cursor, LibrAgent 등)에서 다음 5가지 표준 툴을 호출하여 AK5 칸반을 직접 조작할 수 있습니다:
|
|
139
|
+
|
|
140
|
+
1. `ak5_list_available_agents(capability, search_query)`: 가용 에이전트 역량 검색
|
|
141
|
+
2. `ak5_delegate_subtask(parent_ticket_id, target_agent_id, title, description, priority)`: 하위 티켓 발급 및 에이전트 위임
|
|
142
|
+
3. `ak5_get_ticket_context(ticket_id)`: 티켓 세부사항, 서브태스크 진척도, 최근 코멘트, 실행 맥락 조회
|
|
143
|
+
4. `ak5_update_ticket_status(ticket_id, column_name, status_note, execution_context)`: 티켓 상태 전이 및 산출물 기록
|
|
144
|
+
5. `ak5_report_block(ticket_id, blocking_reason, required_actor_id)`: 티켓 블록 처리 및 PM/담당자 멘션
|
|
145
|
+
|
|
146
|
+
---
|
|
147
|
+
|
|
148
|
+
## 5. 테스트 검증
|
|
149
|
+
|
|
150
|
+
```bash
|
|
151
|
+
# 백엔드 및 CLI 전체 단위/통합 테스트 실행
|
|
152
|
+
uv run pytest backend/tests
|
|
153
|
+
```
|
|
154
|
+
19개의 모든 테스트 케이스(Lexorank 보간/리밸런싱, Actor 식별 및 검색, 티켓 수명주기, 계층형 서브태스크 위임, SSE 이벤트 버스, MCP 툴셋, CLI 명령어)가 통과합니다.
|
|
155
|
+
|
|
156
|
+
---
|
|
157
|
+
|
|
158
|
+
## 6. 사용방법 매뉴얼 & Agent Skill 안내
|
|
159
|
+
|
|
160
|
+
* **상세 사용자 및 운영 매뉴얼:** [MANUAL.md](../MANUAL.md) (또는 [docs/MANUAL.md](../docs/MANUAL.md))
|
|
161
|
+
* **에이전트 하네스용 Skill 정의:** [skills/ak5/SKILL.md](../skills/ak5/SKILL.md)
|
|
162
|
+
* Antigravity, Claude, Cursor 등의 자율 에이전트 하네스에서 `ak5` CLI 및 MCP 툴을 직접 호출하여 자율 분업을 수행할 수 있도록 절차와 러너 스크립트([harness_setup.sh](../skills/ak5/scripts/harness_setup.sh))가 포함되어 있습니다.
|
|
163
|
+
|
ak5-1.0.0/README.md
ADDED
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
# AK5 (Agent K5) — Agent-Orchestrated Kanban System
|
|
2
|
+
|
|
3
|
+
AK5는 인간 사용자(PM, 개발자)와 자율 AI 에이전트(LLM Agents)가 단일 칸반(K5) 인터페이스 위에서 실시간으로 협업하고 작업을 위임(Delegation) 및 추적하는 **Agent-Orchestrated Kanban 시스템**입니다.
|
|
4
|
+
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
## 1. 주요 특징
|
|
8
|
+
|
|
9
|
+
* **Actor 추상화 일원화 (Unified Actor Model):** 인간과 AI 에이전트를 동일한 `Actor` 인터페이스로 취급 (`user_pm`, `agent_image_worker` 등).
|
|
10
|
+
* **역량 기반 발견 및 위임 (Capability-driven Discovery & Delegation):** 에이전트 역량 태그(`image-resize`, `code-review`, `security` 등) 및 자연어 검색 기반으로 작업 위임.
|
|
11
|
+
* **듀얼 프로토콜 (REST + SSE + MCP):**
|
|
12
|
+
* **Web UI (Next.js 15):** 드래그 앤 드롭(`@dnd-kit`), SSE 실시간 보드 자동 동기화, 계층형 서브태스크 진행률 바 및 아코디언 토글, 에이전트 활동 펄스(Activity Glow).
|
|
13
|
+
* **AI Agents (MCP):** Model Context Protocol 표준 툴셋(`ak5_list_available_agents`, `ak5_delegate_subtask`, `ak5_get_ticket_context`, `ak5_update_ticket_status`, `ak5_report_block`).
|
|
14
|
+
* **CLI (`ak5`):** 터미널 환경에서 액터 로그인, 에이전트 검색, 서브태스크 위임, 실시간 터미널 칸반 뷰(`ak5 board --watch`), 협업 시뮬레이션 데모(`ak5 demo`).
|
|
15
|
+
* **동시성 및 순서 정렬 보장:** Lexorank 알고리즘(Base36)을 통한 충돌 없는 임의 순서 삽입, SQLite WAL 모드 + Busy Timeout 설정.
|
|
16
|
+
|
|
17
|
+
---
|
|
18
|
+
|
|
19
|
+
## 2. 프로젝트 디렉토리 레이아웃
|
|
20
|
+
|
|
21
|
+
```
|
|
22
|
+
ak5/
|
|
23
|
+
├── backend/ # FastAPI + MCP Server
|
|
24
|
+
│ ├── src/ak5/
|
|
25
|
+
│ │ ├── main.py # FastAPI 진입점 & Lifespan & MCP 마운트
|
|
26
|
+
│ │ ├── config.py # Pydantic Settings
|
|
27
|
+
│ │ ├── database.py # SQLite WAL 비동기 세션
|
|
28
|
+
│ │ ├── models/ # SQLAlchemy 2.0 모델 (Actor, Board, Column, Ticket, Audit)
|
|
29
|
+
│ │ ├── schemas/ # Pydantic v2 DTO 스키마
|
|
30
|
+
│ │ ├── services/ # Lexorank, Discovery, EventBus (SSE)
|
|
31
|
+
│ │ ├── routers/ # REST API (auth, actors, boards, tickets, events)
|
|
32
|
+
│ │ └── mcp/ # MCP Server (MCPServer, tools, client)
|
|
33
|
+
│ └── tests/ # Pytest 종합 단위/통합 테스트 (19개)
|
|
34
|
+
│
|
|
35
|
+
├── frontend/ # Next.js 15 Web UI (React 18, Tailwind, @dnd-kit)
|
|
36
|
+
│ ├── src/
|
|
37
|
+
│ │ ├── app/ # App Router (layout.tsx, page.tsx, globals.css)
|
|
38
|
+
│ │ ├── components/
|
|
39
|
+
│ │ │ ├── board/ # KanbanBoard, KanbanColumn, TicketCard
|
|
40
|
+
│ │ │ └── actor/ # ActorBadge
|
|
41
|
+
│ │ └── lib/ # SSE 구독 및 API 클라이언트
|
|
42
|
+
│ └── package.json
|
|
43
|
+
│
|
|
44
|
+
├── cli/ # ak5 터미널 도구
|
|
45
|
+
│ └── src/ak5_cli/
|
|
46
|
+
│ ├── main.py # Click CLI 엔트리포인트
|
|
47
|
+
│ ├── config.py # 세션 및 API URL 관리
|
|
48
|
+
│ └── commands/ # login, agents, delegate, board, demo
|
|
49
|
+
│
|
|
50
|
+
├── docker-compose.yml # 컨테이너 오케스트레이션
|
|
51
|
+
└── pyproject.toml # uv 기반 워크스페이스 설정
|
|
52
|
+
```
|
|
53
|
+
|
|
54
|
+
---
|
|
55
|
+
|
|
56
|
+
## 3. 실행 방법
|
|
57
|
+
|
|
58
|
+
### 3.1 사전 요구사항
|
|
59
|
+
* Python 3.11+
|
|
60
|
+
* [uv](https://github.com/astral-sh/uv)
|
|
61
|
+
* Node.js 18+ 및 npm
|
|
62
|
+
|
|
63
|
+
### 3.2 백엔드 (FastAPI & MCP Gateway) 실행
|
|
64
|
+
```bash
|
|
65
|
+
# 가상환경 생성 및 의존성 설치
|
|
66
|
+
uv sync
|
|
67
|
+
uv pip install -e backend -e cli
|
|
68
|
+
|
|
69
|
+
# 백엔드 서버 기동 (기본 포트 8000)
|
|
70
|
+
uv run uvicorn ak5.main:app --host 127.0.0.1 --port 8000 --reload
|
|
71
|
+
```
|
|
72
|
+
* **Swagger API 문서:** `http://127.0.0.1:8000/docs`
|
|
73
|
+
* **MCP SSE 엔드포인트:** `http://127.0.0.1:8000/mcp/sse`
|
|
74
|
+
|
|
75
|
+
### 3.3 프론트엔드 (Next.js 15 Web Dashboard) 실행
|
|
76
|
+
```bash
|
|
77
|
+
cd frontend
|
|
78
|
+
npm install
|
|
79
|
+
npm run dev
|
|
80
|
+
```
|
|
81
|
+
* **웹 대시보드:** `http://localhost:3000`
|
|
82
|
+
|
|
83
|
+
### 3.4 CLI (`ak5`) 도구 사용법
|
|
84
|
+
```bash
|
|
85
|
+
# 1. 액터 등록 및 로그인
|
|
86
|
+
uv run ak5 login --id "agent_code_reviewer" --role "Senior Reviewer" --caps "python,rust,security"
|
|
87
|
+
|
|
88
|
+
# 2. 역량 기반 에이전트 검색
|
|
89
|
+
uv run ak5 agents --cap "image-resize"
|
|
90
|
+
|
|
91
|
+
# 3. 작업 위임 실행
|
|
92
|
+
uv run ak5 delegate TK-001 \
|
|
93
|
+
--to agent_image_worker \
|
|
94
|
+
--title "WebP 썸네일 변환기 구현" \
|
|
95
|
+
--desc "150x150 WebP 포맷 변환 함수 작성"
|
|
96
|
+
|
|
97
|
+
# 4. 실시간 터미널 칸반 뷰
|
|
98
|
+
uv run ak5 board --watch
|
|
99
|
+
|
|
100
|
+
# 5. 자율 멀티 에이전트 협업 데모 시뮬레이션 실행
|
|
101
|
+
uv run ak5 demo
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
---
|
|
105
|
+
|
|
106
|
+
## 4. MCP (Model Context Protocol) 툴셋
|
|
107
|
+
|
|
108
|
+
외부 에이전트(Claude Desktop, Cursor, LibrAgent 등)에서 다음 5가지 표준 툴을 호출하여 AK5 칸반을 직접 조작할 수 있습니다:
|
|
109
|
+
|
|
110
|
+
1. `ak5_list_available_agents(capability, search_query)`: 가용 에이전트 역량 검색
|
|
111
|
+
2. `ak5_delegate_subtask(parent_ticket_id, target_agent_id, title, description, priority)`: 하위 티켓 발급 및 에이전트 위임
|
|
112
|
+
3. `ak5_get_ticket_context(ticket_id)`: 티켓 세부사항, 서브태스크 진척도, 최근 코멘트, 실행 맥락 조회
|
|
113
|
+
4. `ak5_update_ticket_status(ticket_id, column_name, status_note, execution_context)`: 티켓 상태 전이 및 산출물 기록
|
|
114
|
+
5. `ak5_report_block(ticket_id, blocking_reason, required_actor_id)`: 티켓 블록 처리 및 PM/담당자 멘션
|
|
115
|
+
|
|
116
|
+
---
|
|
117
|
+
|
|
118
|
+
## 5. 테스트 검증
|
|
119
|
+
|
|
120
|
+
```bash
|
|
121
|
+
# 백엔드 및 CLI 전체 단위/통합 테스트 실행
|
|
122
|
+
uv run pytest backend/tests
|
|
123
|
+
```
|
|
124
|
+
19개의 모든 테스트 케이스(Lexorank 보간/리밸런싱, Actor 식별 및 검색, 티켓 수명주기, 계층형 서브태스크 위임, SSE 이벤트 버스, MCP 툴셋, CLI 명령어)가 통과합니다.
|
|
125
|
+
|
|
126
|
+
---
|
|
127
|
+
|
|
128
|
+
## 6. 사용방법 매뉴얼 & Agent Skill 안내
|
|
129
|
+
|
|
130
|
+
* **상세 사용자 및 운영 매뉴얼:** [MANUAL.md](../MANUAL.md) (또는 [docs/MANUAL.md](../docs/MANUAL.md))
|
|
131
|
+
* **에이전트 하네스용 Skill 정의:** [skills/ak5/SKILL.md](../skills/ak5/SKILL.md)
|
|
132
|
+
* Antigravity, Claude, Cursor 등의 자율 에이전트 하네스에서 `ak5` CLI 및 MCP 툴을 직접 호출하여 자율 분업을 수행할 수 있도록 절차와 러너 스크립트([harness_setup.sh](../skills/ak5/scripts/harness_setup.sh))가 포함되어 있습니다.
|
|
133
|
+
|
ak5-1.0.0/pyproject.toml
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "ak5"
|
|
3
|
+
version = "1.0.0"
|
|
4
|
+
description = "AK5: Agent-Orchestrated Kanban System Gateway, MCP Server & CLI"
|
|
5
|
+
readme = "README.md"
|
|
6
|
+
license = { text = "MIT" }
|
|
7
|
+
requires-python = ">=3.11"
|
|
8
|
+
authors = [
|
|
9
|
+
{ name = "AK5 Team" }
|
|
10
|
+
]
|
|
11
|
+
keywords = ["kanban", "ai-agents", "mcp", "orchestration", "multi-agent", "fastapi"]
|
|
12
|
+
classifiers = [
|
|
13
|
+
"Development Status :: 4 - Beta",
|
|
14
|
+
"Intended Audience :: Developers",
|
|
15
|
+
"License :: OSI Approved :: MIT License",
|
|
16
|
+
"Programming Language :: Python :: 3",
|
|
17
|
+
"Programming Language :: Python :: 3.11",
|
|
18
|
+
"Programming Language :: Python :: 3.12",
|
|
19
|
+
"Programming Language :: Python :: 3.13",
|
|
20
|
+
"Topic :: Software Development :: Libraries :: Application Frameworks",
|
|
21
|
+
]
|
|
22
|
+
dependencies = [
|
|
23
|
+
"fastapi>=0.115.0",
|
|
24
|
+
"uvicorn[standard]>=0.30.0",
|
|
25
|
+
"sqlalchemy>=2.0.30",
|
|
26
|
+
"aiosqlite>=0.20.0",
|
|
27
|
+
"pydantic>=2.8.0",
|
|
28
|
+
"pydantic-settings>=2.4.0",
|
|
29
|
+
"pyjwt>=2.9.0",
|
|
30
|
+
"httpx>=0.27.0",
|
|
31
|
+
"sse-starlette>=2.1.0",
|
|
32
|
+
"mcp>=1.0.0",
|
|
33
|
+
"click>=8.1.0",
|
|
34
|
+
"rich>=13.7.0",
|
|
35
|
+
]
|
|
36
|
+
|
|
37
|
+
[project.scripts]
|
|
38
|
+
ak5 = "ak5.cli.main:cli"
|
|
39
|
+
ak5-mcp = "ak5.mcp.server:main"
|
|
40
|
+
|
|
41
|
+
[build-system]
|
|
42
|
+
requires = ["hatchling"]
|
|
43
|
+
build-backend = "hatchling.build"
|
|
44
|
+
|
|
45
|
+
[tool.hatch.build.targets.wheel]
|
|
46
|
+
packages = ["src/ak5"]
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import httpx
|
|
3
|
+
from ak5.cli.config import get_api_url
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
from rich.table import Table
|
|
6
|
+
|
|
7
|
+
console = Console()
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
@click.command("agents")
|
|
11
|
+
@click.option("--cap", default=None, help="Filter by capability tag (e.g. 'image-resize')")
|
|
12
|
+
@click.option("--status", default=None, help="Filter by status ('idle', 'busy', 'offline')")
|
|
13
|
+
@click.option("--query", default=None, help="Semantic search query")
|
|
14
|
+
def agents_command(cap: str | None, status: str | None, query: str | None) -> None:
|
|
15
|
+
"""Discover agents matching capability tags or search queries."""
|
|
16
|
+
api_url = get_api_url()
|
|
17
|
+
params = {}
|
|
18
|
+
if cap:
|
|
19
|
+
params["capability"] = cap
|
|
20
|
+
if status:
|
|
21
|
+
params["status"] = status
|
|
22
|
+
if query:
|
|
23
|
+
params["query"] = query
|
|
24
|
+
|
|
25
|
+
try:
|
|
26
|
+
with httpx.Client(timeout=10.0) as client:
|
|
27
|
+
resp = client.get(f"{api_url}/actors/discovery", params=params)
|
|
28
|
+
resp.raise_for_status()
|
|
29
|
+
agents = resp.json()
|
|
30
|
+
|
|
31
|
+
if not agents:
|
|
32
|
+
console.print("[yellow]No matching agents found.[/yellow]")
|
|
33
|
+
return
|
|
34
|
+
|
|
35
|
+
table = Table(title="Available Autonomous AI Agents", show_lines=True)
|
|
36
|
+
table.add_column("Agent ID", style="cyan", no_wrap=True)
|
|
37
|
+
table.add_column("Role", style="magenta")
|
|
38
|
+
table.add_column("Status", style="green")
|
|
39
|
+
table.add_column("Capabilities", style="yellow")
|
|
40
|
+
|
|
41
|
+
for a in agents:
|
|
42
|
+
caps_str = ", ".join(a.get("capabilities", []))
|
|
43
|
+
status_color = "green" if a["status"] == "idle" else ("yellow" if a["status"] == "busy" else "red")
|
|
44
|
+
table.add_row(
|
|
45
|
+
f"@{a['actor_id']}",
|
|
46
|
+
a["role"],
|
|
47
|
+
f"[{status_color}]{a['status']}[/{status_color}]",
|
|
48
|
+
caps_str,
|
|
49
|
+
)
|
|
50
|
+
|
|
51
|
+
console.print(table)
|
|
52
|
+
except httpx.ConnectError:
|
|
53
|
+
console.print(f"[bold red]✗ Failed to connect to AK5 Gateway at {api_url}[/bold red]")
|
|
54
|
+
except Exception as e:
|
|
55
|
+
console.print(f"[bold red]✗ Error querying agents:[/bold red] {e}")
|
|
@@ -0,0 +1,122 @@
|
|
|
1
|
+
import asyncio
|
|
2
|
+
|
|
3
|
+
import click
|
|
4
|
+
import httpx
|
|
5
|
+
from ak5.cli.config import get_api_url
|
|
6
|
+
from rich.console import Console
|
|
7
|
+
from rich.live import Live
|
|
8
|
+
from rich.panel import Panel
|
|
9
|
+
from rich.table import Table
|
|
10
|
+
from rich.text import Text
|
|
11
|
+
|
|
12
|
+
console = Console()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def render_board_view(board_data: dict) -> Table:
|
|
16
|
+
"""Render 4-column Kanban board as a Rich Table."""
|
|
17
|
+
main_table = Table(
|
|
18
|
+
title=f"📋 Kanban Board: {board_data['name']} ({board_data['board_id']})",
|
|
19
|
+
show_header=True,
|
|
20
|
+
header_style="bold cyan",
|
|
21
|
+
expand=True,
|
|
22
|
+
)
|
|
23
|
+
|
|
24
|
+
stage_colors = {
|
|
25
|
+
"open": "blue",
|
|
26
|
+
"in_progress": "yellow",
|
|
27
|
+
"review": "magenta",
|
|
28
|
+
"done": "green",
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
# Add column headers
|
|
32
|
+
for col in board_data.get("columns", []):
|
|
33
|
+
color = stage_colors.get(col.get("stage"), "white")
|
|
34
|
+
count = len(col.get("tickets", []))
|
|
35
|
+
wip_info = f" [WIP: {count}/{col['wip_limit']}]" if col.get("wip_limit", 0) > 0 else f" ({count})"
|
|
36
|
+
main_table.add_column(f"[{color}]{col['name']}{wip_info}[/{color}]")
|
|
37
|
+
|
|
38
|
+
# Find maximum number of rows among columns
|
|
39
|
+
max_tickets = max(len(col.get("tickets", [])) for col in board_data.get("columns", [])) if board_data.get("columns") else 0
|
|
40
|
+
|
|
41
|
+
if max_tickets == 0:
|
|
42
|
+
empty_row = [Panel("[dim](Empty)[/dim]", expand=True) for _ in board_data.get("columns", [])]
|
|
43
|
+
main_table.add_row(*empty_row)
|
|
44
|
+
return main_table
|
|
45
|
+
|
|
46
|
+
for row_idx in range(max_tickets):
|
|
47
|
+
row_cells = []
|
|
48
|
+
for col in board_data.get("columns", []):
|
|
49
|
+
tickets = col.get("tickets", [])
|
|
50
|
+
if row_idx < len(tickets):
|
|
51
|
+
t = tickets[row_idx]
|
|
52
|
+
p_color = "red" if t["priority"] in ("urgent", "high") else ("yellow" if t["priority"] == "medium" else "dim")
|
|
53
|
+
status_badge = f"[{p_color}][{t['priority'].upper()}][/{p_color}]"
|
|
54
|
+
|
|
55
|
+
assignee = f"@{t['assigned_to']}" if t.get("assigned_to") else "[dim]Unassigned[/dim]"
|
|
56
|
+
|
|
57
|
+
subtask_badge = ""
|
|
58
|
+
if t.get("subtask_count", 0) > 0:
|
|
59
|
+
subtask_badge = f"\n[bold cyan]Subtasks: {t['subtask_done_count']}/{t['subtask_count']} Done[/bold cyan]"
|
|
60
|
+
|
|
61
|
+
content = (
|
|
62
|
+
f"[bold white]{t['title']}[/bold white]\n"
|
|
63
|
+
f"{status_badge} [dim]{t['ticket_id']}[/dim] | {assignee}"
|
|
64
|
+
f"{subtask_badge}"
|
|
65
|
+
)
|
|
66
|
+
|
|
67
|
+
border_color = "yellow" if col.get("stage") == "in_progress" else "white"
|
|
68
|
+
row_cells.append(Panel(content, border_style=border_color, expand=True))
|
|
69
|
+
else:
|
|
70
|
+
row_cells.append(Text(""))
|
|
71
|
+
main_table.add_row(*row_cells)
|
|
72
|
+
|
|
73
|
+
return main_table
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def fetch_board(api_url: str, board_id: str) -> dict:
|
|
77
|
+
with httpx.Client(timeout=10.0) as client:
|
|
78
|
+
resp = client.get(f"{api_url}/boards/{board_id}")
|
|
79
|
+
resp.raise_for_status()
|
|
80
|
+
return resp.json()
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
async def watch_board_live(api_url: str, board_id: str) -> None:
|
|
84
|
+
"""Stream SSE events and re-render board on changes."""
|
|
85
|
+
console.print(f"[bold cyan]Connecting to real-time event stream for board '{board_id}'...[/bold cyan]")
|
|
86
|
+
board_data = fetch_board(api_url, board_id)
|
|
87
|
+
|
|
88
|
+
with Live(render_board_view(board_data), console=console, refresh_per_second=4) as live:
|
|
89
|
+
async with (
|
|
90
|
+
httpx.AsyncClient(timeout=None) as client,
|
|
91
|
+
client.stream("GET", f"{api_url}/events/stream") as stream,
|
|
92
|
+
):
|
|
93
|
+
async for line in stream.aiter_lines():
|
|
94
|
+
if line.startswith("event:"):
|
|
95
|
+
event_type = line.split(":", 1)[1].strip()
|
|
96
|
+
if event_type in ("TICKET_CREATED", "TICKET_MOVED", "TICKET_DELEGATED", "TICKET_UPDATED", "COMMENT_ADDED"):
|
|
97
|
+
# Refresh board
|
|
98
|
+
try:
|
|
99
|
+
board_data = fetch_board(api_url, board_id)
|
|
100
|
+
live.update(render_board_view(board_data))
|
|
101
|
+
except (httpx.HTTPError, OSError):
|
|
102
|
+
pass
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
@click.command("board")
|
|
106
|
+
@click.option("--board-id", default="proj-core-engine", help="Target Board ID")
|
|
107
|
+
@click.option("--watch", is_flag=True, help="Watch board with live real-time SSE updates")
|
|
108
|
+
def board_command(board_id: str, watch: bool) -> None:
|
|
109
|
+
"""View Kanban board in terminal."""
|
|
110
|
+
api_url = get_api_url()
|
|
111
|
+
try:
|
|
112
|
+
if watch:
|
|
113
|
+
asyncio.run(watch_board_live(api_url, board_id))
|
|
114
|
+
else:
|
|
115
|
+
board_data = fetch_board(api_url, board_id)
|
|
116
|
+
console.print(render_board_view(board_data))
|
|
117
|
+
except httpx.ConnectError:
|
|
118
|
+
console.print(f"[bold red]✗ Failed to connect to AK5 Gateway at {api_url}[/bold red]")
|
|
119
|
+
except httpx.HTTPStatusError as e:
|
|
120
|
+
console.print(f"[bold red]✗ Board query failed ({e.response.status_code}):[/bold red] {e.response.text}")
|
|
121
|
+
except KeyboardInterrupt:
|
|
122
|
+
console.print("\n[yellow]Watch stopped.[/yellow]")
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
import click
|
|
2
|
+
import httpx
|
|
3
|
+
from ak5.cli.config import get_api_url, get_token
|
|
4
|
+
from rich.console import Console
|
|
5
|
+
|
|
6
|
+
console = Console()
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@click.command("delegate")
|
|
10
|
+
@click.argument("ticket_id")
|
|
11
|
+
@click.option("--to", "target_actor_id", required=True, help="Target Agent ID to delegate task to")
|
|
12
|
+
@click.option("--title", required=True, help="Subtask title")
|
|
13
|
+
@click.option("--desc", "description", default="", help="Subtask detailed description")
|
|
14
|
+
@click.option("--priority", type=click.Choice(["low", "medium", "high", "urgent"]), default="medium")
|
|
15
|
+
def delegate_command(
|
|
16
|
+
ticket_id: str,
|
|
17
|
+
target_actor_id: str,
|
|
18
|
+
title: str,
|
|
19
|
+
description: str,
|
|
20
|
+
priority: str,
|
|
21
|
+
) -> None:
|
|
22
|
+
"""Delegate a subtask to an agent under an existing parent ticket."""
|
|
23
|
+
api_url = get_api_url()
|
|
24
|
+
token = get_token()
|
|
25
|
+
|
|
26
|
+
headers = {}
|
|
27
|
+
if token:
|
|
28
|
+
headers["Authorization"] = f"Bearer {token}"
|
|
29
|
+
else:
|
|
30
|
+
# Auto-login as orchestrator if not logged in
|
|
31
|
+
with httpx.Client(timeout=5.0) as client:
|
|
32
|
+
resp = client.post(
|
|
33
|
+
f"{api_url}/auth/identify",
|
|
34
|
+
json={"actor_id": "cli_user", "actor_type": "human", "name": "CLI User", "role": "PM"},
|
|
35
|
+
)
|
|
36
|
+
if resp.is_success:
|
|
37
|
+
headers["Authorization"] = f"Bearer {resp.json()['access_token']}"
|
|
38
|
+
|
|
39
|
+
payload = {
|
|
40
|
+
"target_actor_id": target_actor_id,
|
|
41
|
+
"subtask_title": title,
|
|
42
|
+
"subtask_description": description,
|
|
43
|
+
"priority": priority,
|
|
44
|
+
"labels": ["delegated", "cli"],
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
try:
|
|
48
|
+
with httpx.Client(timeout=10.0) as client:
|
|
49
|
+
resp = client.post(
|
|
50
|
+
f"{api_url}/tickets/{ticket_id}/delegate",
|
|
51
|
+
json=payload,
|
|
52
|
+
headers=headers,
|
|
53
|
+
)
|
|
54
|
+
resp.raise_for_status()
|
|
55
|
+
subtask = resp.json()
|
|
56
|
+
|
|
57
|
+
console.print(
|
|
58
|
+
f"[bold green]✓[/bold green] Subtask [cyan]{subtask['ticket_id']}[/cyan] created and assigned to "
|
|
59
|
+
f"[magenta]@{target_actor_id}[/magenta] (Parent: [yellow]{ticket_id}[/yellow])"
|
|
60
|
+
)
|
|
61
|
+
except httpx.ConnectError:
|
|
62
|
+
console.print(f"[bold red]✗ Failed to connect to AK5 Gateway at {api_url}[/bold red]")
|
|
63
|
+
except httpx.HTTPStatusError as e:
|
|
64
|
+
console.print(f"[bold red]✗ Delegation failed ({e.response.status_code}):[/bold red] {e.response.text}")
|
|
65
|
+
except Exception as e:
|
|
66
|
+
console.print(f"[bold red]✗ Error:[/bold red] {e}")
|