loki-mode 6.13.0 → 6.14.0

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.
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  [![Agent Types](https://img.shields.io/badge/Agent%20Types-41-blue)]()
10
10
  [![Autonomi](https://img.shields.io/badge/Autonomi-autonomi.dev-5B4EEA)](https://www.autonomi.dev/)
11
11
 
12
- **Current Version: v6.13.0**
12
+ **Current Version: v6.14.0**
13
13
 
14
14
  ---
15
15
 
package/SKILL.md CHANGED
@@ -3,7 +3,7 @@ name: loki-mode
3
3
  description: Multi-agent autonomous startup system. Triggers on "Loki Mode". Takes PRD to deployed product with minimal human intervention. Requires --dangerously-skip-permissions flag.
4
4
  ---
5
5
 
6
- # Loki Mode v6.13.0
6
+ # Loki Mode v6.14.0
7
7
 
8
8
  **You are an autonomous agent. You make decisions. You do not ask questions. You do not stop.**
9
9
 
@@ -267,4 +267,4 @@ The following features are documented in skill modules but not yet fully automat
267
267
  | Quality gates 3-reviewer system | Implemented (v5.35.0) | 5 specialist reviewers in `skills/quality-gates.md`; execution in run.sh |
268
268
  | Benchmarks (HumanEval, SWE-bench) | Infrastructure only | Runner scripts and datasets exist in `benchmarks/`; no published results |
269
269
 
270
- **v6.13.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
270
+ **v6.14.0 | [Autonomi](https://www.autonomi.dev/) flagship product | ~260 lines core**
package/VERSION CHANGED
@@ -1 +1 @@
1
- 6.13.0
1
+ 6.14.0
package/autonomy/loki CHANGED
@@ -419,6 +419,7 @@ show_help() {
419
419
  echo " checkpoint|cp Save/restore session checkpoints"
420
420
  echo " projects Multi-project registry management"
421
421
  echo " audit [cmd] Agent audit log and quality scanning (log|scan)"
422
+ echo " review [dir] Run quality gates on any project (standalone, no AI needed)"
422
423
  echo " optimize Optimize prompts based on session history"
423
424
  echo " enterprise Enterprise feature management (tokens, OIDC)"
424
425
  echo " metrics Prometheus/OpenMetrics metrics from dashboard"
@@ -7848,6 +7849,9 @@ main() {
7848
7849
  audit)
7849
7850
  cmd_audit "$@"
7850
7851
  ;;
7852
+ review)
7853
+ cmd_review "$@"
7854
+ ;;
7851
7855
  optimize)
7852
7856
  cmd_optimize "$@"
7853
7857
  ;;
@@ -7895,6 +7899,223 @@ main() {
7895
7899
  esac
7896
7900
  }
7897
7901
 
7902
+ # Standalone project review - run quality gates on any codebase (v6.14.0)
7903
+ cmd_review() {
7904
+ local target_dir="."
7905
+ local json_output=""
7906
+ local verbose=""
7907
+
7908
+ while [[ $# -gt 0 ]]; do
7909
+ case "$1" in
7910
+ --json) json_output="true"; shift ;;
7911
+ --verbose|-v) verbose="true"; shift ;;
7912
+ --help|-h)
7913
+ echo -e "${BOLD}loki review${NC} - Run quality gates on any project (standalone)"
7914
+ echo ""
7915
+ echo "Usage: loki review [directory] [options]"
7916
+ echo ""
7917
+ echo "Arguments:"
7918
+ echo " directory Path to project (default: current directory)"
7919
+ echo ""
7920
+ echo "Options:"
7921
+ echo " --json Machine-readable JSON output"
7922
+ echo " --verbose, -v Show detailed output per gate"
7923
+ echo " --help, -h Show this help"
7924
+ echo ""
7925
+ echo "Gates: project-type, lint, tests, security, dependencies, structure"
7926
+ echo "Exit code: 0 if all pass, 1 if any fail"
7927
+ return 0
7928
+ ;;
7929
+ -*) echo -e "${RED}Unknown option: $1${NC}"; return 1 ;;
7930
+ *) target_dir="$1"; shift ;;
7931
+ esac
7932
+ done
7933
+
7934
+ # Resolve and validate directory
7935
+ target_dir="$(cd "$target_dir" 2>/dev/null && pwd)" || {
7936
+ echo -e "${RED}Error: Directory not found: $target_dir${NC}"; return 1
7937
+ }
7938
+
7939
+ local project_type="unknown"
7940
+ local results=() # gate:status pairs
7941
+ local total_pass=0 total_warn=0 total_fail=0
7942
+ local gate_details=()
7943
+
7944
+ # Helper: record gate result
7945
+ _review_gate() {
7946
+ local gate="$1" status="$2" detail="${3:-}"
7947
+ results+=("$gate:$status")
7948
+ gate_details+=("$gate:$detail")
7949
+ case "$status" in
7950
+ PASS) total_pass=$((total_pass + 1)) ;;
7951
+ WARN) total_warn=$((total_warn + 1)) ;;
7952
+ FAIL) total_fail=$((total_fail + 1)) ;;
7953
+ esac
7954
+ }
7955
+
7956
+ # Gate 1: Detect project type
7957
+ if [ -f "$target_dir/package.json" ]; then
7958
+ project_type="node"
7959
+ elif [ -f "$target_dir/pyproject.toml" ] || [ -f "$target_dir/setup.py" ] || [ -f "$target_dir/requirements.txt" ]; then
7960
+ project_type="python"
7961
+ elif [ -f "$target_dir/go.mod" ]; then
7962
+ project_type="go"
7963
+ elif [ -f "$target_dir/Cargo.toml" ]; then
7964
+ project_type="rust"
7965
+ fi
7966
+ if [ "$project_type" != "unknown" ]; then
7967
+ _review_gate "project-type" "PASS" "Detected: $project_type"
7968
+ else
7969
+ _review_gate "project-type" "WARN" "Could not detect project type"
7970
+ fi
7971
+
7972
+ # Gate 2: Lint check
7973
+ local lint_output="" lint_status="PASS"
7974
+ case "$project_type" in
7975
+ node)
7976
+ if [ -f "$target_dir/.eslintrc.js" ] || [ -f "$target_dir/.eslintrc.json" ] || [ -f "$target_dir/.eslintrc.yml" ] || [ -f "$target_dir/eslint.config.js" ] || [ -f "$target_dir/eslint.config.mjs" ]; then
7977
+ lint_output=$(cd "$target_dir" && npx eslint . --max-warnings=0 2>&1) || lint_status="FAIL"
7978
+ else
7979
+ lint_output="No ESLint config found"; lint_status="WARN"
7980
+ fi ;;
7981
+ python)
7982
+ if command -v ruff &>/dev/null; then
7983
+ lint_output=$(cd "$target_dir" && ruff check . 2>&1) || lint_status="FAIL"
7984
+ elif command -v pylint &>/dev/null; then
7985
+ lint_output=$(cd "$target_dir" && pylint --recursive=y . 2>&1) || lint_status="FAIL"
7986
+ else
7987
+ lint_output="No linter available (install ruff or pylint)"; lint_status="WARN"
7988
+ fi ;;
7989
+ go)
7990
+ if command -v golangci-lint &>/dev/null; then
7991
+ lint_output=$(cd "$target_dir" && golangci-lint run 2>&1) || lint_status="FAIL"
7992
+ else
7993
+ lint_output=$(cd "$target_dir" && go vet ./... 2>&1) || lint_status="FAIL"
7994
+ fi ;;
7995
+ rust)
7996
+ lint_output=$(cd "$target_dir" && cargo clippy -- -D warnings 2>&1) || lint_status="FAIL"
7997
+ ;;
7998
+ *) lint_output="Skipped (unknown project type)"; lint_status="WARN" ;;
7999
+ esac
8000
+ _review_gate "lint" "$lint_status" "$lint_output"
8001
+
8002
+ # Gate 3: Tests
8003
+ local test_output="" test_status="PASS"
8004
+ case "$project_type" in
8005
+ node)
8006
+ if grep -q '"test"' "$target_dir/package.json" 2>/dev/null; then
8007
+ test_output=$(cd "$target_dir" && npm test 2>&1) || test_status="FAIL"
8008
+ else
8009
+ test_output="No test script in package.json"; test_status="WARN"
8010
+ fi ;;
8011
+ python)
8012
+ if command -v pytest &>/dev/null; then
8013
+ test_output=$(cd "$target_dir" && pytest --tb=short 2>&1) || test_status="FAIL"
8014
+ else
8015
+ test_output="pytest not available"; test_status="WARN"
8016
+ fi ;;
8017
+ go) test_output=$(cd "$target_dir" && go test ./... 2>&1) || test_status="FAIL" ;;
8018
+ rust) test_output=$(cd "$target_dir" && cargo test 2>&1) || test_status="FAIL" ;;
8019
+ *) test_output="Skipped (unknown project type)"; test_status="WARN" ;;
8020
+ esac
8021
+ _review_gate "tests" "$test_status" "$test_output"
8022
+
8023
+ # Gate 4: Security - grep for hardcoded secrets
8024
+ local secret_output="" secret_status="PASS"
8025
+ local secret_patterns='(API_KEY|SECRET_KEY|PASSWORD|TOKEN|PRIVATE_KEY)\s*[=:]\s*["\x27][A-Za-z0-9+/=_-]{8,}'
8026
+ secret_output=$(grep -rEn "$secret_patterns" "$target_dir" \
8027
+ --include="*.js" --include="*.ts" --include="*.py" --include="*.go" --include="*.rs" \
8028
+ --include="*.jsx" --include="*.tsx" --include="*.java" --include="*.rb" \
8029
+ --exclude-dir=node_modules --exclude-dir=.git --exclude-dir=vendor \
8030
+ --exclude-dir=__pycache__ --exclude-dir=.venv --exclude-dir=target 2>/dev/null) || true
8031
+ if [ -n "$secret_output" ]; then
8032
+ secret_status="FAIL"
8033
+ secret_output="Potential hardcoded secrets found:
8034
+ $secret_output"
8035
+ else
8036
+ secret_output="No hardcoded secrets detected"
8037
+ fi
8038
+ _review_gate "security" "$secret_status" "$secret_output"
8039
+
8040
+ # Gate 5: Dependency audit
8041
+ local dep_output="" dep_status="PASS"
8042
+ case "$project_type" in
8043
+ node)
8044
+ if [ -f "$target_dir/package-lock.json" ] || [ -f "$target_dir/yarn.lock" ]; then
8045
+ dep_output=$(cd "$target_dir" && npm audit --production 2>&1) || dep_status="WARN"
8046
+ else
8047
+ dep_output="No lockfile found"; dep_status="WARN"
8048
+ fi ;;
8049
+ python)
8050
+ if command -v pip-audit &>/dev/null; then
8051
+ dep_output=$(cd "$target_dir" && pip-audit 2>&1) || dep_status="WARN"
8052
+ else
8053
+ dep_output="pip-audit not available"; dep_status="WARN"
8054
+ fi ;;
8055
+ *) dep_output="No dependency audit available for $project_type"; dep_status="WARN" ;;
8056
+ esac
8057
+ _review_gate "dependencies" "$dep_status" "$dep_output"
8058
+
8059
+ # Gate 6: Structure check
8060
+ local struct_output="" struct_status="PASS" struct_issues=()
8061
+ [ ! -f "$target_dir/README.md" ] && [ ! -f "$target_dir/README.rst" ] && [ ! -f "$target_dir/README" ] && struct_issues+=("Missing README")
8062
+ local file_count
8063
+ file_count=$(find "$target_dir" -type f -not -path '*/.git/*' -not -path '*/node_modules/*' -not -path '*/vendor/*' -not -path '*/__pycache__/*' -not -path '*/.venv/*' -not -path '*/target/*' 2>/dev/null | wc -l | tr -d ' ')
8064
+ [ "$file_count" -gt 5000 ] && struct_issues+=("Large project: $file_count files")
8065
+ local huge_files
8066
+ huge_files=$(find "$target_dir" -type f -size +1M -not -path '*/.git/*' -not -path '*/node_modules/*' -not -path '*/vendor/*' -not -path '*/target/*' 2>/dev/null | head -5)
8067
+ [ -n "$huge_files" ] && struct_issues+=("Files >1MB found: $(echo "$huge_files" | wc -l | tr -d ' ')")
8068
+ if [ ${#struct_issues[@]} -gt 0 ]; then
8069
+ struct_status="WARN"
8070
+ struct_output=$(printf '%s\n' "${struct_issues[@]}")
8071
+ else
8072
+ struct_output="README present, $file_count files, no oversized files"
8073
+ fi
8074
+ _review_gate "structure" "$struct_status" "$struct_output"
8075
+
8076
+ # Output results
8077
+ if [ -n "$json_output" ]; then
8078
+ local gates_json="["
8079
+ local first="true"
8080
+ for r in "${results[@]}"; do
8081
+ local gate="${r%%:*}" status="${r#*:}"
8082
+ [ "$first" = "true" ] && first="" || gates_json+=","
8083
+ gates_json+="{\"gate\":\"$gate\",\"status\":\"$status\"}"
8084
+ done
8085
+ gates_json+="]"
8086
+ printf '{"directory":"%s","project_type":"%s","pass":%d,"warn":%d,"fail":%d,"gates":%s}\n' \
8087
+ "$target_dir" "$project_type" "$total_pass" "$total_warn" "$total_fail" "$gates_json"
8088
+ else
8089
+ echo -e "${BOLD}Loki Review: $target_dir${NC}"
8090
+ echo -e "Project type: ${CYAN}$project_type${NC}"
8091
+ echo "---"
8092
+ for r in "${results[@]}"; do
8093
+ local gate="${r%%:*}" status="${r#*:}"
8094
+ case "$status" in
8095
+ PASS) echo -e " ${GREEN}[PASS]${NC} $gate" ;;
8096
+ WARN) echo -e " ${YELLOW}[WARN]${NC} $gate" ;;
8097
+ FAIL) echo -e " ${RED}[FAIL]${NC} $gate" ;;
8098
+ esac
8099
+ done
8100
+ echo "---"
8101
+ echo -e "Results: ${GREEN}$total_pass passed${NC}, ${YELLOW}$total_warn warnings${NC}, ${RED}$total_fail failed${NC}"
8102
+
8103
+ if [ -n "$verbose" ]; then
8104
+ echo ""
8105
+ echo -e "${BOLD}Details:${NC}"
8106
+ for d in "${gate_details[@]}"; do
8107
+ local gate="${d%%:*}" detail="${d#*:}"
8108
+ echo -e "\n${CYAN}[$gate]${NC}"
8109
+ echo "$detail" | head -30
8110
+ done
8111
+ fi
8112
+ fi
8113
+
8114
+ # Exit code: 1 if any failures
8115
+ [ "$total_fail" -gt 0 ] && return 1
8116
+ return 0
8117
+ }
8118
+
7898
8119
  # Worktree management (v6.7.0)
7899
8120
  cmd_worktree() {
7900
8121
  local subcommand="${1:-list}"
@@ -7,7 +7,7 @@ Modules:
7
7
  control: Session control API (start/stop/pause/resume)
8
8
  """
9
9
 
10
- __version__ = "6.13.0"
10
+ __version__ = "6.14.0"
11
11
 
12
12
  # Expose the control app for easy import
13
13
  try:
@@ -2,7 +2,7 @@
2
2
 
3
3
  The flagship product of [Autonomi](https://www.autonomi.dev/). Complete installation instructions for all platforms and use cases.
4
4
 
5
- **Version:** v6.13.0
5
+ **Version:** v6.14.0
6
6
 
7
7
  ---
8
8
 
package/mcp/__init__.py CHANGED
@@ -57,4 +57,4 @@ try:
57
57
  except ImportError:
58
58
  __all__ = ['mcp']
59
59
 
60
- __version__ = '6.13.0'
60
+ __version__ = '6.14.0'
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "loki-mode",
3
- "version": "6.13.0",
3
+ "version": "6.14.0",
4
4
  "description": "Loki Mode by Autonomi - Multi-agent autonomous startup system for Claude Code, Codex CLI, and Gemini CLI",
5
5
  "keywords": [
6
6
  "agent",
@@ -1,10 +1,34 @@
1
1
  # PRD: REST API Service
2
2
 
3
3
  ## Overview
4
- A simple REST API for managing notes. Tests Loki Mode's backend-only capabilities.
4
+ A simple REST API for managing notes. Tests Loki Mode's backend-only capabilities with proper validation, error handling, and test coverage.
5
5
 
6
6
  ## Target Users
7
- Developers who need a notes API.
7
+ - Developers who need a lightweight notes API for prototyping
8
+ - Teams evaluating backend code generation quality
9
+ - Frontend developers needing a mock API to build against
10
+
11
+ ## Features
12
+
13
+ ### MVP Features
14
+ 1. **Create Note** - Add a new note with title and content
15
+ 2. **List Notes** - Retrieve all notes
16
+ 3. **Get Note** - Retrieve a single note by ID
17
+ 4. **Update Note** - Edit an existing note's title or content
18
+ 5. **Delete Note** - Remove a note
19
+ 6. **Health Check** - Service health endpoint
20
+
21
+ ### Data Model
22
+
23
+ ```typescript
24
+ interface Note {
25
+ id: string;
26
+ title: string;
27
+ content: string;
28
+ createdAt: string; // ISO 8601 timestamp
29
+ updatedAt: string; // ISO 8601 timestamp
30
+ }
31
+ ```
8
32
 
9
33
  ## API Endpoints
10
34
 
@@ -12,23 +36,23 @@ Developers who need a notes API.
12
36
 
13
37
  #### GET /api/notes
14
38
  - Returns list of all notes
15
- - Response: `[{ id, title, content, createdAt }]`
39
+ - Response: `[{ id, title, content, createdAt, updatedAt }]`
16
40
 
17
41
  #### GET /api/notes/:id
18
42
  - Returns single note
19
- - Response: `{ id, title, content, createdAt }`
43
+ - Response: `{ id, title, content, createdAt, updatedAt }`
20
44
  - Error: 404 if not found
21
45
 
22
46
  #### POST /api/notes
23
47
  - Creates new note
24
48
  - Body: `{ title, content }`
25
- - Response: `{ id, title, content, createdAt }`
26
- - Error: 400 if validation fails
49
+ - Response: `{ id, title, content, createdAt, updatedAt }` (201)
50
+ - Error: 400 if validation fails (title required, content required)
27
51
 
28
52
  #### PUT /api/notes/:id
29
53
  - Updates existing note
30
- - Body: `{ title?, content? }`
31
- - Response: `{ id, title, content, updatedAt }`
54
+ - Body: `{ title?, content? }` (partial update)
55
+ - Response: `{ id, title, content, createdAt, updatedAt }`
32
56
  - Error: 404 if not found
33
57
 
34
58
  #### DELETE /api/notes/:id
@@ -44,36 +68,70 @@ Developers who need a notes API.
44
68
  ## Tech Stack
45
69
  - Runtime: Node.js 18+
46
70
  - Framework: Express.js
71
+ - Language: TypeScript
47
72
  - Database: In-memory (array) for simplicity
48
- - Validation: zod or joi
49
- - Testing: Jest + supertest
73
+ - Validation: zod
74
+ - Testing: Vitest + supertest
75
+
76
+ ### Structure
77
+ ```
78
+ /
79
+ ├── src/
80
+ │ ├── index.ts # Express server setup, middleware
81
+ │ ├── routes/
82
+ │ │ ├── notes.ts # Notes CRUD handlers
83
+ │ │ └── health.ts # Health check endpoint
84
+ │ ├── middleware/
85
+ │ │ └── errorHandler.ts # Global error handler
86
+ │ ├── schemas/
87
+ │ │ └── note.ts # Zod validation schemas
88
+ │ └── types/
89
+ │ └── index.ts # TypeScript type definitions
90
+ ├── tests/
91
+ │ ├── notes.test.ts # Notes endpoint tests
92
+ │ └── health.test.ts # Health check test
93
+ ├── package.json
94
+ ├── tsconfig.json
95
+ └── README.md
96
+ ```
50
97
 
51
98
  ## Requirements
52
- - Input validation on all endpoints
53
- - Proper HTTP status codes
54
- - JSON error responses
55
- - Request logging
56
- - Unit tests for each endpoint
99
+ - TypeScript throughout
100
+ - Input validation on all endpoints using zod
101
+ - Proper HTTP status codes (200, 201, 204, 400, 404)
102
+ - JSON error responses: `{ error: "message" }`
103
+ - Request logging (method, path, status code)
104
+ - CORS enabled for development
105
+
106
+ ## Testing
107
+ - API tests: All endpoints with valid input, invalid input, and edge cases (Vitest + supertest)
108
+ - Minimum test cases:
109
+ - `POST /api/notes` with valid data -> 201 + note object
110
+ - `POST /api/notes` with missing title -> 400 + error
111
+ - `POST /api/notes` with missing content -> 400 + error
112
+ - `GET /api/notes` -> 200 + array
113
+ - `GET /api/notes/:id` with valid id -> 200 + note
114
+ - `GET /api/notes/:id` with invalid id -> 404
115
+ - `PUT /api/notes/:id` with valid data -> 200 + updated note
116
+ - `PUT /api/notes/:id` with invalid id -> 404
117
+ - `DELETE /api/notes/:id` -> 204
118
+ - `DELETE /api/notes/:id` with invalid id -> 404
119
+ - `GET /health` -> 200 + status object
57
120
 
58
121
  ## Out of Scope
59
122
  - Authentication
60
- - Database persistence
123
+ - Database persistence (file or SQL)
61
124
  - Rate limiting
62
125
  - API documentation (OpenAPI)
63
126
  - Deployment
64
127
 
65
- ## Test Cases
66
- ```
67
- POST /api/notes with valid data 201 + note object
68
- POST /api/notes with missing title → 400 + error
69
- GET /api/notes 200 + array
70
- GET /api/notes/:id with valid id → 200 + note
71
- GET /api/notes/:id with invalid id → 404
72
- PUT /api/notes/:id with valid data → 200 + updated note
73
- DELETE /api/notes/:id → 204
74
- GET /health → 200 + status object
75
- ```
128
+ ## Success Criteria
129
+ - All 6 endpoints return correct status codes and response bodies
130
+ - Validation rejects invalid input with descriptive error messages
131
+ - All tests pass
132
+ - Server starts without errors on `npm start`
133
+ - Health check returns valid response
76
134
 
77
135
  ---
78
136
 
79
- **Purpose:** Tests backend agent capabilities, code review, and QA without frontend complexity.
137
+ **Purpose:** Tests backend agent capabilities, code review, and QA without frontend complexity. Expect ~15-20 minutes for full execution.
@@ -12,7 +12,7 @@ A simple e-commerce storefront called "ShopBase" with a product catalog, shoppin
12
12
 
13
13
  ### MVP Features
14
14
  1. **Product Catalog** - Browse products with images, prices, descriptions, and categories
15
- 2. **Product Detail** - Full product page with image gallery, variants (size/color), and reviews
15
+ 2. **Product Detail** - Full product page with image gallery and variants (size/color)
16
16
  3. **Shopping Cart** - Add/remove items, update quantities, persistent cart (survives refresh)
17
17
  4. **Checkout** - Shipping address form, order summary, Stripe payment
18
18
  5. **Order Confirmation** - Confirmation page and email receipt
@@ -1,27 +1,34 @@
1
1
  # PRD: Full-Stack Demo App
2
2
 
3
3
  ## Overview
4
- A complete full-stack application demonstrating Loki Mode's end-to-end capabilities. A simple bookmark manager with tags.
4
+ A complete full-stack application demonstrating Loki Mode's end-to-end capabilities. A bookmark manager called "Stash" with tags, search, and a clean UI.
5
5
 
6
6
  ## Target Users
7
- Users who want to save and organize bookmarks.
7
+ - Users who want to save and organize bookmarks with tags
8
+ - Developers testing Loki Mode's full-stack generation pipeline
8
9
 
9
10
  ## Features
10
11
 
11
12
  ### Core Features
12
13
  1. **Add Bookmark** - Save URL with title and optional tags
13
- 2. **View Bookmarks** - List all bookmarks with search/filter
14
+ - Acceptance: Form validates URL format, title is required, tags are comma-separated, form clears on submit
15
+ 2. **View Bookmarks** - List all bookmarks with search and tag filter
16
+ - Acceptance: Shows URL, title, tags, and creation date; search filters by title with 300ms debounce; tag chips are clickable for filtering
14
17
  3. **Edit Bookmark** - Update title, URL, or tags
15
- 4. **Delete Bookmark** - Remove bookmark
18
+ - Acceptance: Inline edit or modal form, pre-populated with current values, saves on submit
19
+ 4. **Delete Bookmark** - Remove bookmark with confirmation
20
+ - Acceptance: Confirmation dialog before delete, bookmark removed from list and database
16
21
  5. **Tag Management** - Create, view, and filter by tags
22
+ - Acceptance: Tag sidebar shows all tags with bookmark counts, clicking a tag filters the list, unused tags cleaned up on bookmark delete
17
23
 
18
24
  ### User Flow
19
- 1. User opens app sees bookmark list
20
- 2. Clicks "Add Bookmark" form appears
21
- 3. Enters URL, title, tags submits
22
- 4. Bookmark appears in list
23
- 5. Can filter by tag or search by title
25
+ 1. User opens app -> sees bookmark list (or empty state if none)
26
+ 2. Clicks "Add Bookmark" -> form appears
27
+ 3. Enters URL, title, tags -> submits
28
+ 4. Bookmark appears in list with tag chips
29
+ 5. Can filter by tag (click tag) or search by title (search bar)
24
30
  6. Can edit or delete any bookmark
31
+ 7. Refreshes page -> all state persists from database
25
32
 
26
33
  ## Tech Stack
27
34
 
@@ -42,17 +49,41 @@ Users who want to save and organize bookmarks.
42
49
  /
43
50
  ├── frontend/
44
51
  │ ├── src/
52
+ │ │ ├── App.tsx # Main app with layout
45
53
  │ │ ├── components/
54
+ │ │ │ ├── BookmarkList.tsx # List of bookmark cards
55
+ │ │ │ ├── BookmarkCard.tsx # Single bookmark display
56
+ │ │ │ ├── BookmarkForm.tsx # Add/edit form
57
+ │ │ │ ├── SearchBar.tsx # Search input with debounce
58
+ │ │ │ ├── TagSidebar.tsx # Tag list with counts
59
+ │ │ │ ├── TagChip.tsx # Clickable tag badge
60
+ │ │ │ ├── ConfirmDialog.tsx # Delete confirmation
61
+ │ │ │ └── EmptyState.tsx # Shown when no bookmarks
46
62
  │ │ ├── hooks/
63
+ │ │ │ ├── useBookmarks.ts # CRUD operations via React Query
64
+ │ │ │ └── useTags.ts # Tag fetching hook
47
65
  │ │ ├── types/
48
- │ │ └── App.tsx
66
+ │ │ └── index.ts # Bookmark and Tag types
67
+ │ │ └── main.tsx
68
+ │ ├── tests/
69
+ │ │ ├── BookmarkCard.test.tsx # Card rendering and actions
70
+ │ │ ├── BookmarkForm.test.tsx # Form validation and submit
71
+ │ │ └── SearchBar.test.tsx # Debounce and filter behavior
49
72
  │ ├── package.json
50
73
  │ └── vite.config.ts
51
74
  ├── backend/
52
75
  │ ├── src/
76
+ │ │ ├── index.ts # Express server setup
53
77
  │ │ ├── routes/
78
+ │ │ │ ├── bookmarks.ts # Bookmark CRUD handlers
79
+ │ │ │ └── tags.ts # Tag list handler
54
80
  │ │ ├── db/
55
- │ │ └── index.ts
81
+ │ │ └── index.ts # SQLite connection + schema init
82
+ │ │ └── schemas/
83
+ │ │ └── bookmark.ts # Zod validation schemas
84
+ │ ├── tests/
85
+ │ │ ├── bookmarks.test.ts # Bookmark API tests
86
+ │ │ └── tags.test.ts # Tag API tests
56
87
  │ ├── package.json
57
88
  │ └── tsconfig.json
58
89
  └── README.md
@@ -62,17 +93,20 @@ Users who want to save and organize bookmarks.
62
93
 
63
94
  ### Bookmarks
64
95
  - `GET /api/bookmarks` - List all (query: `?tag=`, `?search=`)
65
- - `POST /api/bookmarks` - Create new
66
- - `PUT /api/bookmarks/:id` - Update
67
- - `DELETE /api/bookmarks/:id` - Delete
96
+ - `POST /api/bookmarks` - Create new (body: `{ url, title, tags? }`)
97
+ - `PUT /api/bookmarks/:id` - Update (body: `{ url?, title?, tags? }`)
98
+ - `DELETE /api/bookmarks/:id` - Delete (returns 204)
68
99
 
69
100
  ### Tags
70
- - `GET /api/tags` - List all tags with counts
101
+ - `GET /api/tags` - List all tags with bookmark counts
102
+
103
+ ### Health
104
+ - `GET /health` - Returns `{ status: "ok" }`
71
105
 
72
106
  ## Database Schema
73
107
  ```sql
74
108
  CREATE TABLE bookmarks (
75
- id INTEGER PRIMARY KEY,
109
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
76
110
  url TEXT NOT NULL,
77
111
  title TEXT NOT NULL,
78
112
  created_at DATETIME DEFAULT CURRENT_TIMESTAMP,
@@ -80,29 +114,31 @@ CREATE TABLE bookmarks (
80
114
  );
81
115
 
82
116
  CREATE TABLE tags (
83
- id INTEGER PRIMARY KEY,
117
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
84
118
  name TEXT UNIQUE NOT NULL
85
119
  );
86
120
 
87
121
  CREATE TABLE bookmark_tags (
88
- bookmark_id INTEGER REFERENCES bookmarks(id),
89
- tag_id INTEGER REFERENCES tags(id),
122
+ bookmark_id INTEGER REFERENCES bookmarks(id) ON DELETE CASCADE,
123
+ tag_id INTEGER REFERENCES tags(id) ON DELETE CASCADE,
90
124
  PRIMARY KEY (bookmark_id, tag_id)
91
125
  );
92
126
  ```
93
127
 
94
128
  ## Requirements
95
129
  - TypeScript throughout
96
- - Input validation (frontend + backend)
97
- - Error handling with user feedback
98
- - Loading states
99
- - Empty states
100
- - Responsive design
130
+ - Input validation (frontend + backend): URL format, title required
131
+ - Error handling with user-visible feedback (toast or inline messages)
132
+ - Loading states during API calls
133
+ - Empty states for no bookmarks and no search results
134
+ - Search debounce: 300ms delay before API call
135
+ - Responsive design (single-column on mobile, sidebar on desktop)
101
136
 
102
137
  ## Testing
103
- - Backend: Jest + supertest for API tests
104
- - Frontend: Basic component tests (optional)
105
- - E2E: Manual testing checklist
138
+ - Backend API tests: Bookmark CRUD, tag listing, search/filter queries (Vitest + supertest)
139
+ - Frontend component tests: BookmarkCard rendering, BookmarkForm validation, SearchBar debounce (Vitest + React Testing Library)
140
+ - Minimum 10 test cases across frontend and backend
141
+ - All tests required to pass (no optional tests)
106
142
 
107
143
  ## Out of Scope
108
144
  - User authentication
@@ -112,10 +148,13 @@ CREATE TABLE bookmark_tags (
112
148
  - Real-time sync
113
149
 
114
150
  ## Success Criteria
115
- - All CRUD operations work
116
- - Search and filter work
151
+ - All CRUD operations work end-to-end (create, read, update, delete)
152
+ - Search filters bookmarks by title with debounce
153
+ - Tag filter shows only bookmarks with selected tag
154
+ - Tag counts are accurate
155
+ - Data persists across page refresh
117
156
  - No console errors
118
- - Tests pass
157
+ - All tests pass
119
158
  - Code review passes (all 3 reviewers)
120
159
 
121
160
  ---
@@ -1,48 +1,133 @@
1
1
  # PRD: Simple Todo App
2
2
 
3
3
  ## Overview
4
- A minimal todo application for testing Loki Mode with a simple, well-defined scope.
4
+ A minimal todo application for testing Loki Mode with a simple, well-defined scope. A single-page app called "Todos" with a React frontend, Express API, and SQLite persistence.
5
5
 
6
6
  ## Target Users
7
- Individual users who want a simple way to track tasks.
7
+ - Individual users who want a simple way to track tasks
8
+ - Developers testing Loki Mode's core generation pipeline
8
9
 
9
10
  ## Features
10
11
 
11
12
  ### MVP Features
12
13
  1. **Add Todo** - Users can add a new todo item with a title
13
- 2. **View Todos** - Display list of all todos
14
- 3. **Complete Todo** - Mark a todo as done
15
- 4. **Delete Todo** - Remove a todo from the list
14
+ 2. **View Todos** - Display list of all todos with completion status
15
+ 3. **Complete Todo** - Mark a todo as done (toggle)
16
+ 4. **Delete Todo** - Remove a todo from the list with confirmation
16
17
 
17
- ### Tech Stack (Suggested)
18
- - Frontend: React + TypeScript
19
- - Backend: Node.js + Express
20
- - Database: SQLite (local file)
21
- - No deployment (local testing only)
18
+ ### User Flow
19
+ 1. User opens app -> sees todo list (or empty state if none)
20
+ 2. Types a title in the input field -> presses Enter or clicks Add
21
+ 3. New todo appears at the top of the list, input clears
22
+ 4. Clicks checkbox to toggle complete -> visual strikethrough
23
+ 5. Clicks delete icon -> confirmation prompt -> todo removed
24
+ 6. Refreshes page -> all state persists from database
25
+
26
+ ## Tech Stack
27
+
28
+ ### Frontend
29
+ - React 18 with TypeScript
30
+ - Vite for bundling
31
+ - TailwindCSS for styling
32
+
33
+ ### Backend
34
+ - Node.js 18+
35
+ - Express.js
36
+ - SQLite via better-sqlite3
37
+ - zod for input validation
38
+
39
+ ### Structure
40
+ ```
41
+ /
42
+ ├── frontend/
43
+ │ ├── src/
44
+ │ │ ├── App.tsx # Main app component
45
+ │ │ ├── components/
46
+ │ │ │ ├── TodoList.tsx # List of todo items
47
+ │ │ │ ├── TodoItem.tsx # Single todo with checkbox/delete
48
+ │ │ │ ├── AddTodo.tsx # Input form for new todos
49
+ │ │ │ └── EmptyState.tsx # Shown when no todos exist
50
+ │ │ ├── hooks/
51
+ │ │ │ └── useTodos.ts # API fetch/mutate hook
52
+ │ │ ├── types/
53
+ │ │ │ └── index.ts # Todo type definition
54
+ │ │ └── main.tsx
55
+ │ ├── package.json
56
+ │ └── vite.config.ts
57
+ ├── backend/
58
+ │ ├── src/
59
+ │ │ ├── index.ts # Express server setup
60
+ │ │ ├── routes/
61
+ │ │ │ └── todos.ts # CRUD route handlers
62
+ │ │ └── db/
63
+ │ │ └── index.ts # SQLite connection + init
64
+ │ ├── package.json
65
+ │ └── tsconfig.json
66
+ ├── tests/
67
+ │ ├── todos.test.ts # API endpoint tests
68
+ │ └── components/
69
+ │ └── TodoItem.test.tsx # Component tests
70
+ └── README.md
71
+ ```
72
+
73
+ ## Database Schema
74
+
75
+ ```sql
76
+ CREATE TABLE todos (
77
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
78
+ title TEXT NOT NULL,
79
+ completed INTEGER DEFAULT 0, -- 0 = false, 1 = true
80
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
81
+ );
82
+ ```
83
+
84
+ ## API Endpoints
85
+
86
+ ### Todos
87
+ - `GET /api/todos` - List all todos (ordered by created_at DESC)
88
+ - `POST /api/todos` - Create todo (body: `{ title }`, returns created todo)
89
+ - `PATCH /api/todos/:id` - Toggle completion (body: `{ completed }`)
90
+ - `DELETE /api/todos/:id` - Delete todo (returns 204)
91
+
92
+ ### Health
93
+ - `GET /health` - Returns `{ status: "ok" }`
22
94
 
23
95
  ## Acceptance Criteria
24
96
 
25
97
  ### Add Todo
26
98
  - [ ] Input field for todo title
27
- - [ ] Submit button
99
+ - [ ] Submit on Enter key or button click
28
100
  - [ ] New todo appears in list
29
101
  - [ ] Input clears after submit
102
+ - [ ] Empty title is rejected (frontend + backend validation)
30
103
 
31
104
  ### View Todos
32
105
  - [ ] Shows all todos in a list
33
- - [ ] Shows completion status
34
- - [ ] Empty state when no todos
106
+ - [ ] Shows completion status (checkbox)
107
+ - [ ] Empty state message when no todos exist
35
108
 
36
109
  ### Complete Todo
37
- - [ ] Checkbox or button to mark complete
38
- - [ ] Visual indicator for completed items
39
- - [ ] Persists after refresh
110
+ - [ ] Checkbox toggles complete/incomplete
111
+ - [ ] Visual strikethrough for completed items
112
+ - [ ] Persists after page refresh
40
113
 
41
114
  ### Delete Todo
42
115
  - [ ] Delete button on each todo
43
116
  - [ ] Confirmation before delete
44
117
  - [ ] Removes from list and database
45
118
 
119
+ ## Requirements
120
+ - TypeScript throughout
121
+ - Input validation on both frontend and backend
122
+ - Proper HTTP status codes (201 for create, 204 for delete, 400 for validation errors)
123
+ - Loading states during API calls
124
+ - Responsive design (usable on mobile)
125
+
126
+ ## Testing
127
+ - API tests: All 4 CRUD endpoints with valid and invalid input (Vitest + supertest)
128
+ - Component tests: TodoItem renders correctly, AddTodo form submission works
129
+ - Minimum 6 test cases covering happy path and error cases
130
+
46
131
  ## Out of Scope
47
132
  - User authentication
48
133
  - Due dates
@@ -50,11 +135,14 @@ Individual users who want a simple way to track tasks.
50
135
  - Mobile app
51
136
  - Cloud deployment
52
137
 
53
- ## Success Metrics
54
- - All features functional
55
- - Tests passing
138
+ ## Success Criteria
139
+ - All 4 CRUD features functional end-to-end
140
+ - All tests pass
56
141
  - No console errors
142
+ - Empty state displays correctly
143
+ - Data persists across page refresh
144
+ - Input validation rejects empty titles
57
145
 
58
146
  ---
59
147
 
60
- **Purpose:** This PRD is intentionally simple to allow quick testing of Loki Mode's core functionality without waiting for complex builds or deployments.
148
+ **Purpose:** This PRD is intentionally simple to allow quick testing of Loki Mode's core functionality without waiting for complex builds or deployments. Expect ~15-25 minutes for full execution.