Compare commits

..

No commits in common. "b739ef1fb7288a1cac453ee3ee5a9b23fa0baf5d" and "510b75c0bfcaafe4e5a4326867c1291712160ac6" have entirely different histories.

4 changed files with 37 additions and 355 deletions

View File

@ -1,114 +1,55 @@
# KAOS — Master Status
**Verzija:** v0.3.4
**Poslednje ažuriranje:** 2026-02-20 18:00
**Verzija:** 0.3.0
**Poslednje ažuriranje:** 2026-02-20
---
## Stanje fajlova na Windows-u
## Fajl indeks
| Folder | Taskovi |
|--------|---------|
| backlog/ | T17, T18, T23 |
| ready/ | T25 |
| active/ | — |
| review/ | — |
| done/ | (server ima T01-T22, T24) |
| 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 |
---
## v0.1 Supervisor — ZAVRŠENO ✅ (67 testova)
## Task folderi
| 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 ✅
| 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 |
---
## Taskovi za kreiranje (dogovoreni, nemaju .md)
## v0.1 Taskovi — ZAVRŠENO ✅
| # | Š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 |
| 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** |
---
## Poznati bugovi
## Sledeće — v0.2 Dashboard
| 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/` |
| Task | Naslov | Folder | Zavisi od |
|------|--------|--------|-----------|
| T08 | HTTP server + API | backlog | T07 ✅ |
| T09 | Dashboard kanban board | backlog | T08 |
| T10 | Drag & Drop | backlog | T09 |

View File

@ -1,45 +0,0 @@
# 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 |

View File

@ -1,53 +0,0 @@
# 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 |

View File

@ -1,161 +0,0 @@
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])
}
}