Compare commits
2 Commits
510b75c0bf
...
b739ef1fb7
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b739ef1fb7 | ||
|
|
4031593ea8 |
@ -1,55 +1,114 @@
|
||||
# KAOS — Master Status
|
||||
|
||||
**Verzija:** 0.3.0
|
||||
**Poslednje ažuriranje:** 2026-02-20
|
||||
**Verzija:** v0.3.4
|
||||
**Poslednje ažuriranje:** 2026-02-20 18:00
|
||||
|
||||
---
|
||||
|
||||
## Fajl indeks
|
||||
## Stanje fajlova na Windows-u
|
||||
|
||||
| Fajl | Opis |
|
||||
|------|------|
|
||||
| `CLAUDE.md` | Mastermind (v0.3.0) |
|
||||
| `README.md` | Pregled projekta, arhitektura, odluke |
|
||||
| `agents/*/CLAUDE.md` | 8 agenata (v0.1.0) |
|
||||
| `TASKS/MASTER-STATUS.md` | Ovo — navigacija, status |
|
||||
| `TASKS/Implementation-Tasks.md` | Svi taskovi detaljno |
|
||||
| `TASKS/Workflow-Spec.md` | 10 koraka, odluke |
|
||||
| `TASKS/Multi-Agent-Spec.md` | Arhitektura agenata |
|
||||
| Folder | Taskovi |
|
||||
|--------|---------|
|
||||
| backlog/ | T17, T18, T23 |
|
||||
| ready/ | T25 |
|
||||
| active/ | — |
|
||||
| review/ | — |
|
||||
| done/ | (server ima T01-T22, T24) |
|
||||
|
||||
---
|
||||
|
||||
## Task folderi
|
||||
## v0.1 Supervisor — ZAVRŠENO ✅ (67 testova)
|
||||
|
||||
| Folder | Sadržaj | Taskovi |
|
||||
|--------|---------|---------|
|
||||
| backlog/ | Čeka odobrenje | T08, T09, T10 |
|
||||
| ready/ | Odobren za rad | — |
|
||||
| active/ | U izradi | — |
|
||||
| review/ | Čeka pregled/odgovor | — |
|
||||
| done/ | Završeno | T01, T02, T03, T04, T05, T06, T07 |
|
||||
| Task | Naslov | Tag |
|
||||
|------|--------|-----|
|
||||
| T01 | Inicijalizacija Go projekta | v0.1.1 |
|
||||
| T02 | Task loader (parsiranje MD) | v0.1.2 |
|
||||
| T03 | Runner (pokretanje Claude Code) | v0.1.4 |
|
||||
| T04 | Checker (build + test + vet) | v0.1.3 |
|
||||
| T05 | Reporter (pisanje izveštaja) | v0.1.5 |
|
||||
| T06 | CLI (komandni interfejs) | v0.1.6 |
|
||||
| T07 | Integracija (end-to-end) | v0.1.7 |
|
||||
|
||||
## v0.2 Dashboard — ZAVRŠENO ✅ (23 testova)
|
||||
|
||||
| Task | Naslov | Tag |
|
||||
|------|--------|-----|
|
||||
| T08 | HTTP server + API | v0.2.1 |
|
||||
| T09 | Dashboard Kanban + templates | v0.2.2 |
|
||||
| T10 | Drag & Drop + validacija | v0.2.3 |
|
||||
|
||||
## v0.3 UX + Operaterski interfejs — U TOKU
|
||||
|
||||
| Task | Naslov | Tag | Status |
|
||||
|------|--------|-----|--------|
|
||||
| T11 | Fix — svež stanje sa diska | v0.2.4 | done ✅ |
|
||||
| T12 | Prikaz dokumentacije | v0.2.5 | done ✅ |
|
||||
| T13 | Pretraga taskova i dokumentacije | v0.2.6 | done ✅ |
|
||||
| T14 | Konzola (2 sesije) | v0.2.7 | done ✅ |
|
||||
| T15 | Fix — docs širina | v0.2.8 | done ✅ |
|
||||
| T16 | SSE auto-refresh | v0.3.0 | done ✅ |
|
||||
| T19 | Dugme "Pusti" na taskovima | v0.2.9 | done ✅ |
|
||||
| T20 | Workflow dugmad po statusu | v0.3.1 | done ✅ |
|
||||
| T21 | UI fix — konzola, detalj panel, layout | v0.3.2 | done ✅ |
|
||||
| T22 | Prijava — dva moda + dontAsk fix | v0.3.3 | done ✅ |
|
||||
| T24 | PTY za real-time konzolu | v0.3.4 | done ✅ |
|
||||
| T25 | Timestampi + izveštaj prikaz | — | ready |
|
||||
| T26 | Test — prikaži zadnjih 20 linija loga | v0.3.5 | review |
|
||||
| T23 | Persistent logovi + log viewer tab | — | backlog |
|
||||
| T17 | Systemd servis | — | backlog |
|
||||
| T18 | End-to-end test uživo | — | backlog |
|
||||
|
||||
## PROJEKAT UKUPNO: 192 testova ✅
|
||||
|
||||
---
|
||||
|
||||
## v0.1 Taskovi — ZAVRŠENO ✅
|
||||
## Taskovi za kreiranje (dogovoreni, nemaju .md)
|
||||
|
||||
| Task | Naslov | Tag | Commit | Testova |
|
||||
|------|--------|-----|--------|---------|
|
||||
| T01 | Inicijalizacija Go projekta | v0.1.1 | f001c53 | 6 |
|
||||
| T02 | Task loader (parsiranje MD) | v0.1.2 | 79bcd52 | 17 |
|
||||
| T03 | Runner (pokretanje Claude Code) | v0.1.4 | 9d2c249 | 7 |
|
||||
| T04 | Checker (build + test + vet) | v0.1.3 | 5d869f5 | 10 |
|
||||
| T05 | Reporter (pisanje izveštaja) | v0.1.5 | 028872b | 10 |
|
||||
| T06 | CLI (komandni interfejs) | v0.1.6 | 38e1e10 | 9 |
|
||||
| T07 | Integracija (end-to-end) | v0.1.7 | b2ece98 | 8 |
|
||||
| **Ukupno** | | | | **67** |
|
||||
| # | Šta | Prioritet |
|
||||
|---|-----|-----------|
|
||||
| T26 | Tema: svetla/tamna/auto | nizak |
|
||||
| T27 | Fix: konzola se prazni posle vremena | srednji |
|
||||
| T28 | Notifikacije (zvuk/badge kad task završi) | srednji |
|
||||
| T29 | Task editor iz dashboarda | nizak |
|
||||
| T30 | Autentifikacija (login) | visok (pre produkcije) |
|
||||
| T31 | Deployer agent implementacija | visok |
|
||||
| T32 | Cost tracking (tokeni, cena, vreme po tasku) | srednji |
|
||||
|
||||
---
|
||||
|
||||
## Sledeće — v0.2 Dashboard
|
||||
## Poznati bugovi
|
||||
|
||||
| Task | Naslov | Folder | Zavisi od |
|
||||
|------|--------|--------|-----------|
|
||||
| T08 | HTTP server + API | backlog | T07 ✅ |
|
||||
| T09 | Dashboard kanban board | backlog | T08 |
|
||||
| T10 | Drag & Drop | backlog | T09 |
|
||||
| Bug | Task | Status |
|
||||
|-----|------|--------|
|
||||
| Konzola se prazni posle vremena | T27 | za kreiranje |
|
||||
| Sync vraća obrisane fajlove iz done/ | — | otvoreno |
|
||||
| "T01 Naslov" template u done/ | — | za brisanje |
|
||||
| Agent menjao CLAUDE.md | — | dodato pravilo "nikad ne menjaj" |
|
||||
|
||||
---
|
||||
|
||||
## Ključne odluke
|
||||
|
||||
| Odluka | Detalji |
|
||||
|--------|---------|
|
||||
| HTMX umesto React | Nema npm, jedan binary |
|
||||
| Folder-based state machine | Task stanje = folder lokacija |
|
||||
| Čist kontekst po tasku | Svaki task = nov claude proces |
|
||||
| dontAsk permission mode | Radi kao root bez problema |
|
||||
| Operater mod = claude CLI | Pro licenca, ne API |
|
||||
| Deployer jedini piše backlog/ | Auto-kreiranje taskova iz logova |
|
||||
| Agent nikad ne menja CLAUDE.md | Samo planer |
|
||||
| Max 2 paralelne sesije | Svaka = zaseban claude proces |
|
||||
| Semver tagging | Patch=task, Minor=milestone |
|
||||
|
||||
---
|
||||
|
||||
## Reference
|
||||
|
||||
| Šta | Gde |
|
||||
|-----|-----|
|
||||
| Mastermind pravila | `CLAUDE.md` v0.6.0 |
|
||||
| Workflow spec | `TASKS/Workflow-Spec.md` |
|
||||
| Multi-agent arhitektura | `TASKS/Multi-Agent-Spec.md` |
|
||||
| Agent pravila | `agents/*/CLAUDE.md` |
|
||||
| Template | `templates/new-project/` |
|
||||
|
||||
45
TASKS/reports/T26-report.md
Normal file
45
TASKS/reports/T26-report.md
Normal file
@ -0,0 +1,45 @@
|
||||
# T26 Izveštaj — Test: prikaži zadnjih 20 linija loga
|
||||
|
||||
## Rezultat: USPEŠNO ✅
|
||||
|
||||
## Šta je urađeno
|
||||
|
||||
Handler `handleLogsTail` i ruta `/api/logs/tail` su već postojali (iz ranijeg rada).
|
||||
UI link "Logovi" u layoutu je takođe već bio implementiran.
|
||||
|
||||
Ovaj task je dodao **7 novih testova** za logs endpoint:
|
||||
|
||||
| Test | Šta proverava |
|
||||
|------|---------------|
|
||||
| TestHandleLogsTail_OK | 200, vraća tačno 20 linija iz fajla sa 25 linija |
|
||||
| TestHandleLogsTail_LessThan20Lines | Vraća sve linije kad ih ima manje od 20 |
|
||||
| TestHandleLogsTail_NoLogFile | Poruka kad KAOS_LOG_FILE nije podešen |
|
||||
| TestHandleLogsTail_FileNotFound | Poruka kad fajl ne postoji |
|
||||
| TestHandleLogsTail_Max20Lines | Max 20 linija čak iz fajla sa 100 linija |
|
||||
| TestTailLines | Table-driven test (5 slučajeva) za helper funkciju |
|
||||
| TestTailLines_Content | Provera tačnog sadržaja poslednjih linija |
|
||||
|
||||
## Fajlovi
|
||||
|
||||
| Fajl | Akcija |
|
||||
|------|--------|
|
||||
| code/internal/server/logs_test.go | KREIRAN (161 linija) |
|
||||
|
||||
## Testovi
|
||||
|
||||
- Novih: 7 (12 sub-testova ukupno)
|
||||
- Svi prolaze ✅
|
||||
- `go build ./...` ✅
|
||||
- `go vet ./...` ✅
|
||||
- PROJEKAT UKUPNO: 192 testova ✅
|
||||
|
||||
## Commit
|
||||
|
||||
- `4031593` — T26: Testovi za endpoint zadnjih 20 linija loga
|
||||
|
||||
## Vremena
|
||||
|
||||
| Događaj | Vreme |
|
||||
|---------|-------|
|
||||
| Početak | 2026-02-21 04:33 |
|
||||
| Završetak | 2026-02-21 04:40 |
|
||||
53
TASKS/review/T26.md
Normal file
53
TASKS/review/T26.md
Normal file
@ -0,0 +1,53 @@
|
||||
# T26: Test — prikaži zadnjih 20 linija loga
|
||||
|
||||
**Kreirao:** planer
|
||||
**Datum:** 2026-02-20
|
||||
**Agent:** coder
|
||||
**Model:** Sonnet
|
||||
**Zavisi od:** —
|
||||
|
||||
---
|
||||
|
||||
## Opis
|
||||
|
||||
Jednostavan test task. Dodaj endpoint koji prikazuje zadnjih 20 linija
|
||||
server loga.
|
||||
|
||||
## Šta treba
|
||||
|
||||
1. Endpoint: GET /api/logs/tail
|
||||
2. Čita zadnjih 20 linija iz stdout/journalctl loga servera
|
||||
3. Vraća plain text
|
||||
|
||||
```go
|
||||
// Ako server piše u fajl:
|
||||
tail -20 /tmp/kaos-server.log
|
||||
|
||||
// Ili journalctl:
|
||||
journalctl -u kaos-server -n 20 --no-pager
|
||||
```
|
||||
|
||||
4. Na dashboardu: dodaj link "Poslednji logovi" negde vidljivo
|
||||
|
||||
## Testovi
|
||||
|
||||
- GET /api/logs/tail → 200, vraća tekst
|
||||
- Odgovor ima max 20 linija
|
||||
|
||||
---
|
||||
|
||||
## Pitanja
|
||||
|
||||
---
|
||||
|
||||
## Odgovori
|
||||
|
||||
## Vremena
|
||||
|
||||
| Događaj | Vreme |
|
||||
|---------|-------|
|
||||
| Pokrenut | 2026-02-20 15:52 |
|
||||
| Pokrenut | 2026-02-21 04:05 |
|
||||
| Pokrenut (→active) | 2026-02-21 04:11 |
|
||||
| Pokrenut (→active) | 2026-02-21 04:33 |
|
||||
| Završen (→review) | 2026-02-21 04:40 |
|
||||
161
code/internal/server/logs_test.go
Normal file
161
code/internal/server/logs_test.go
Normal file
@ -0,0 +1,161 @@
|
||||
package server
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestHandleLogsTail_OK(t *testing.T) {
|
||||
srv := setupTestServer(t)
|
||||
|
||||
logFile := filepath.Join(t.TempDir(), "test.log")
|
||||
lines := make([]string, 25)
|
||||
for i := range lines {
|
||||
lines[i] = "linija " + string(rune('A'+i))
|
||||
}
|
||||
os.WriteFile(logFile, []byte(strings.Join(lines, "\n")+"\n"), 0644)
|
||||
srv.Config.LogFile = logFile
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/logs/tail", nil)
|
||||
w := httptest.NewRecorder()
|
||||
srv.Router.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d", w.Code)
|
||||
}
|
||||
|
||||
got := strings.Split(strings.TrimRight(w.Body.String(), "\n"), "\n")
|
||||
if len(got) != maxLogLines {
|
||||
t.Fatalf("expected %d lines, got %d", maxLogLines, len(got))
|
||||
}
|
||||
// First returned line should be line 6 (index 5) since we have 25 lines, tail 20
|
||||
if got[0] != "linija F" {
|
||||
t.Errorf("expected first line 'linija F', got %q", got[0])
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleLogsTail_LessThan20Lines(t *testing.T) {
|
||||
srv := setupTestServer(t)
|
||||
|
||||
logFile := filepath.Join(t.TempDir(), "test.log")
|
||||
os.WriteFile(logFile, []byte("prva\ndruga\ntreca\n"), 0644)
|
||||
srv.Config.LogFile = logFile
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/logs/tail", nil)
|
||||
w := httptest.NewRecorder()
|
||||
srv.Router.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d", w.Code)
|
||||
}
|
||||
|
||||
got := strings.Split(strings.TrimRight(w.Body.String(), "\n"), "\n")
|
||||
if len(got) != 3 {
|
||||
t.Fatalf("expected 3 lines, got %d", len(got))
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleLogsTail_NoLogFile(t *testing.T) {
|
||||
srv := setupTestServer(t)
|
||||
// LogFile is empty string by default in test config
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/logs/tail", nil)
|
||||
w := httptest.NewRecorder()
|
||||
srv.Router.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d", w.Code)
|
||||
}
|
||||
|
||||
if !strings.Contains(w.Body.String(), "KAOS_LOG_FILE") {
|
||||
t.Error("expected message about KAOS_LOG_FILE not being set")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleLogsTail_FileNotFound(t *testing.T) {
|
||||
srv := setupTestServer(t)
|
||||
srv.Config.LogFile = "/tmp/nonexistent-kaos-test-log-12345.log"
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/logs/tail", nil)
|
||||
w := httptest.NewRecorder()
|
||||
srv.Router.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d", w.Code)
|
||||
}
|
||||
|
||||
if !strings.Contains(w.Body.String(), "ne postoji") {
|
||||
t.Error("expected message about file not existing")
|
||||
}
|
||||
}
|
||||
|
||||
func TestHandleLogsTail_Max20Lines(t *testing.T) {
|
||||
srv := setupTestServer(t)
|
||||
|
||||
logFile := filepath.Join(t.TempDir(), "test.log")
|
||||
lines := make([]string, 100)
|
||||
for i := range lines {
|
||||
lines[i] = "log line"
|
||||
}
|
||||
os.WriteFile(logFile, []byte(strings.Join(lines, "\n")+"\n"), 0644)
|
||||
srv.Config.LogFile = logFile
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/api/logs/tail", nil)
|
||||
w := httptest.NewRecorder()
|
||||
srv.Router.ServeHTTP(w, req)
|
||||
|
||||
if w.Code != http.StatusOK {
|
||||
t.Fatalf("expected 200, got %d", w.Code)
|
||||
}
|
||||
|
||||
got := strings.Split(strings.TrimRight(w.Body.String(), "\n"), "\n")
|
||||
if len(got) > maxLogLines {
|
||||
t.Errorf("expected max %d lines, got %d", maxLogLines, len(got))
|
||||
}
|
||||
}
|
||||
|
||||
func TestTailLines(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
text string
|
||||
n int
|
||||
expected int
|
||||
}{
|
||||
{"empty", "", 20, 1},
|
||||
{"less than n", "a\nb\nc", 20, 3},
|
||||
{"exact n", "a\nb\nc", 3, 3},
|
||||
{"more than n", "a\nb\nc\nd\ne", 3, 3},
|
||||
{"trailing newline", "a\nb\nc\n", 2, 2},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := tailLines(tt.text, tt.n)
|
||||
if len(got) != tt.expected {
|
||||
t.Errorf("tailLines(%q, %d) = %d lines, want %d", tt.text, tt.n, len(got), tt.expected)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestTailLines_Content(t *testing.T) {
|
||||
text := "first\nsecond\nthird\nfourth\nfifth\n"
|
||||
got := tailLines(text, 3)
|
||||
|
||||
if len(got) != 3 {
|
||||
t.Fatalf("expected 3 lines, got %d", len(got))
|
||||
}
|
||||
if got[0] != "third" {
|
||||
t.Errorf("expected 'third', got %q", got[0])
|
||||
}
|
||||
if got[1] != "fourth" {
|
||||
t.Errorf("expected 'fourth', got %q", got[1])
|
||||
}
|
||||
if got[2] != "fifth" {
|
||||
t.Errorf("expected 'fifth', got %q", got[2])
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user