draft-board 0.1.0-beta.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.
Files changed (250) hide show
  1. package/app/backend/.env.example +9 -0
  2. package/app/backend/.smartkanban/evidence/8b383839-cbec-45af-86ee-c7708d075cbe/bddf2ed5-2e21-4d46-a62b-10b87f1642a6_patch.txt +195 -0
  3. package/app/backend/.smartkanban/evidence/8b383839-cbec-45af-86ee-c7708d075cbe/bddf2ed5-2e21-4d46-a62b-10b87f1642a6_stat.txt +6 -0
  4. package/app/backend/CURL_EXAMPLES.md +335 -0
  5. package/app/backend/ENV_SETUP.md +65 -0
  6. package/app/backend/alembic/env.py +71 -0
  7. package/app/backend/alembic/script.py.mako +28 -0
  8. package/app/backend/alembic/versions/001_initial_schema.py +104 -0
  9. package/app/backend/alembic/versions/002_add_jobs_table.py +52 -0
  10. package/app/backend/alembic/versions/003_add_workspace_table.py +48 -0
  11. package/app/backend/alembic/versions/004_add_evidence_table.py +56 -0
  12. package/app/backend/alembic/versions/005_add_verification_commands.py +32 -0
  13. package/app/backend/alembic/versions/006_add_planner_lock_table.py +39 -0
  14. package/app/backend/alembic/versions/007_add_revision_review_tables.py +126 -0
  15. package/app/backend/alembic/versions/008_add_revision_idempotency_and_traceability.py +52 -0
  16. package/app/backend/alembic/versions/009_add_job_health_fields.py +46 -0
  17. package/app/backend/alembic/versions/010_add_review_comment_line_content.py +36 -0
  18. package/app/backend/alembic/versions/011_add_analysis_cache.py +47 -0
  19. package/app/backend/alembic/versions/012_add_boards_table.py +102 -0
  20. package/app/backend/alembic/versions/013_add_ticket_blocking.py +45 -0
  21. package/app/backend/alembic/versions/014_add_agent_sessions.py +220 -0
  22. package/app/backend/alembic/versions/015_add_ticket_sort_order.py +33 -0
  23. package/app/backend/alembic/versions/03220f0b93ae_add_pr_fields_to_ticket.py +49 -0
  24. package/app/backend/alembic/versions/0c2d89fff3b1_seed_board_configs_from_yaml.py +206 -0
  25. package/app/backend/alembic/versions/3348e5cf54c1_add_merge_checklist_table.py +67 -0
  26. package/app/backend/alembic/versions/357c780ee445_add_goal_status.py +34 -0
  27. package/app/backend/alembic/versions/553340b7e26c_add_autonomy_fields_to_goal.py +65 -0
  28. package/app/backend/alembic/versions/774dc335c679_merge_migration_heads.py +23 -0
  29. package/app/backend/alembic/versions/7b307e847cbd_merge_heads.py +23 -0
  30. package/app/backend/alembic/versions/82ecd978cc70_add_missing_indexes.py +48 -0
  31. package/app/backend/alembic/versions/8ef5054dc280_add_normalized_log_entries.py +173 -0
  32. package/app/backend/alembic/versions/8f3e2bd8ea3b_merge_migration_heads.py +23 -0
  33. package/app/backend/alembic/versions/9d17f0698d3b_add_config_column_to_boards_table.py +30 -0
  34. package/app/backend/alembic/versions/add_agent_conversation_history.py +72 -0
  35. package/app/backend/alembic/versions/add_job_variant.py +34 -0
  36. package/app/backend/alembic/versions/add_performance_indexes.py +95 -0
  37. package/app/backend/alembic/versions/add_repos_and_board_repos.py +174 -0
  38. package/app/backend/alembic/versions/add_session_id_to_jobs.py +27 -0
  39. package/app/backend/alembic/versions/add_sqlite_backend_tables.py +104 -0
  40. package/app/backend/alembic/versions/b10fb0b62240_add_diff_content_to_revisions.py +34 -0
  41. package/app/backend/alembic.ini +89 -0
  42. package/app/backend/app/__init__.py +3 -0
  43. package/app/backend/app/data_dir.py +85 -0
  44. package/app/backend/app/database.py +70 -0
  45. package/app/backend/app/database_sync.py +64 -0
  46. package/app/backend/app/dependencies/__init__.py +5 -0
  47. package/app/backend/app/dependencies/auth.py +80 -0
  48. package/app/backend/app/dependencies.py +43 -0
  49. package/app/backend/app/exceptions.py +178 -0
  50. package/app/backend/app/executors/__init__.py +1 -0
  51. package/app/backend/app/executors/adapters/__init__.py +1 -0
  52. package/app/backend/app/executors/adapters/aider.py +152 -0
  53. package/app/backend/app/executors/adapters/amazon_q.py +103 -0
  54. package/app/backend/app/executors/adapters/amp.py +123 -0
  55. package/app/backend/app/executors/adapters/claude.py +177 -0
  56. package/app/backend/app/executors/adapters/cline.py +127 -0
  57. package/app/backend/app/executors/adapters/codex.py +167 -0
  58. package/app/backend/app/executors/adapters/copilot.py +202 -0
  59. package/app/backend/app/executors/adapters/cursor.py +87 -0
  60. package/app/backend/app/executors/adapters/droid.py +123 -0
  61. package/app/backend/app/executors/adapters/gemini.py +132 -0
  62. package/app/backend/app/executors/adapters/goose.py +131 -0
  63. package/app/backend/app/executors/adapters/opencode.py +123 -0
  64. package/app/backend/app/executors/adapters/qwen.py +123 -0
  65. package/app/backend/app/executors/plugins/__init__.py +1 -0
  66. package/app/backend/app/executors/registry.py +202 -0
  67. package/app/backend/app/executors/spec.py +226 -0
  68. package/app/backend/app/main.py +486 -0
  69. package/app/backend/app/middleware/__init__.py +13 -0
  70. package/app/backend/app/middleware/idempotency.py +426 -0
  71. package/app/backend/app/middleware/rate_limit.py +312 -0
  72. package/app/backend/app/middleware/security_headers.py +43 -0
  73. package/app/backend/app/middleware/timeout.py +37 -0
  74. package/app/backend/app/models/__init__.py +56 -0
  75. package/app/backend/app/models/agent_conversation_history.py +56 -0
  76. package/app/backend/app/models/agent_session.py +127 -0
  77. package/app/backend/app/models/analysis_cache.py +49 -0
  78. package/app/backend/app/models/base.py +9 -0
  79. package/app/backend/app/models/board.py +79 -0
  80. package/app/backend/app/models/board_repo.py +68 -0
  81. package/app/backend/app/models/cost_budget.py +42 -0
  82. package/app/backend/app/models/enums.py +40 -0
  83. package/app/backend/app/models/evidence.py +132 -0
  84. package/app/backend/app/models/goal.py +102 -0
  85. package/app/backend/app/models/idempotency_entry.py +30 -0
  86. package/app/backend/app/models/job.py +163 -0
  87. package/app/backend/app/models/job_queue.py +39 -0
  88. package/app/backend/app/models/kv_store.py +28 -0
  89. package/app/backend/app/models/merge_checklist.py +87 -0
  90. package/app/backend/app/models/normalized_log.py +100 -0
  91. package/app/backend/app/models/planner_lock.py +43 -0
  92. package/app/backend/app/models/rate_limit_entry.py +25 -0
  93. package/app/backend/app/models/repo.py +66 -0
  94. package/app/backend/app/models/review_comment.py +91 -0
  95. package/app/backend/app/models/review_summary.py +69 -0
  96. package/app/backend/app/models/revision.py +130 -0
  97. package/app/backend/app/models/ticket.py +223 -0
  98. package/app/backend/app/models/ticket_event.py +83 -0
  99. package/app/backend/app/models/user.py +47 -0
  100. package/app/backend/app/models/workspace.py +71 -0
  101. package/app/backend/app/redis_client.py +119 -0
  102. package/app/backend/app/routers/__init__.py +29 -0
  103. package/app/backend/app/routers/agents.py +296 -0
  104. package/app/backend/app/routers/auth.py +94 -0
  105. package/app/backend/app/routers/board.py +885 -0
  106. package/app/backend/app/routers/dashboard.py +351 -0
  107. package/app/backend/app/routers/debug.py +528 -0
  108. package/app/backend/app/routers/evidence.py +96 -0
  109. package/app/backend/app/routers/executors.py +324 -0
  110. package/app/backend/app/routers/goals.py +574 -0
  111. package/app/backend/app/routers/jobs.py +448 -0
  112. package/app/backend/app/routers/maintenance.py +172 -0
  113. package/app/backend/app/routers/merge.py +360 -0
  114. package/app/backend/app/routers/planner.py +537 -0
  115. package/app/backend/app/routers/pull_requests.py +382 -0
  116. package/app/backend/app/routers/repos.py +263 -0
  117. package/app/backend/app/routers/revisions.py +939 -0
  118. package/app/backend/app/routers/settings.py +267 -0
  119. package/app/backend/app/routers/tickets.py +2003 -0
  120. package/app/backend/app/routers/webhooks.py +143 -0
  121. package/app/backend/app/routers/websocket.py +249 -0
  122. package/app/backend/app/schemas/__init__.py +109 -0
  123. package/app/backend/app/schemas/board.py +87 -0
  124. package/app/backend/app/schemas/common.py +33 -0
  125. package/app/backend/app/schemas/evidence.py +87 -0
  126. package/app/backend/app/schemas/goal.py +90 -0
  127. package/app/backend/app/schemas/job.py +97 -0
  128. package/app/backend/app/schemas/merge.py +139 -0
  129. package/app/backend/app/schemas/planner.py +500 -0
  130. package/app/backend/app/schemas/repo.py +187 -0
  131. package/app/backend/app/schemas/review.py +137 -0
  132. package/app/backend/app/schemas/revision.py +114 -0
  133. package/app/backend/app/schemas/ticket.py +238 -0
  134. package/app/backend/app/schemas/ticket_event.py +72 -0
  135. package/app/backend/app/schemas/workspace.py +19 -0
  136. package/app/backend/app/services/__init__.py +31 -0
  137. package/app/backend/app/services/agent_memory_service.py +223 -0
  138. package/app/backend/app/services/agent_registry.py +346 -0
  139. package/app/backend/app/services/agent_session_manager.py +318 -0
  140. package/app/backend/app/services/agent_session_service.py +219 -0
  141. package/app/backend/app/services/agent_tools.py +379 -0
  142. package/app/backend/app/services/auth_service.py +98 -0
  143. package/app/backend/app/services/autonomy_service.py +380 -0
  144. package/app/backend/app/services/board_repo_service.py +201 -0
  145. package/app/backend/app/services/board_service.py +326 -0
  146. package/app/backend/app/services/cleanup_service.py +1085 -0
  147. package/app/backend/app/services/config_service.py +908 -0
  148. package/app/backend/app/services/context_gatherer.py +557 -0
  149. package/app/backend/app/services/cost_tracking_service.py +293 -0
  150. package/app/backend/app/services/cursor_log_normalizer.py +536 -0
  151. package/app/backend/app/services/delivery_pipeline.py +440 -0
  152. package/app/backend/app/services/executor_service.py +634 -0
  153. package/app/backend/app/services/git_host/__init__.py +11 -0
  154. package/app/backend/app/services/git_host/factory.py +87 -0
  155. package/app/backend/app/services/git_host/github.py +270 -0
  156. package/app/backend/app/services/git_host/gitlab.py +194 -0
  157. package/app/backend/app/services/git_host/protocol.py +75 -0
  158. package/app/backend/app/services/git_merge_simple.py +346 -0
  159. package/app/backend/app/services/git_ops.py +384 -0
  160. package/app/backend/app/services/github_service.py +233 -0
  161. package/app/backend/app/services/goal_service.py +113 -0
  162. package/app/backend/app/services/job_service.py +423 -0
  163. package/app/backend/app/services/job_watchdog_service.py +424 -0
  164. package/app/backend/app/services/langchain_adapter.py +122 -0
  165. package/app/backend/app/services/llm_provider_clients.py +351 -0
  166. package/app/backend/app/services/llm_service.py +285 -0
  167. package/app/backend/app/services/log_normalizer.py +342 -0
  168. package/app/backend/app/services/log_stream_service.py +276 -0
  169. package/app/backend/app/services/merge_checklist_service.py +264 -0
  170. package/app/backend/app/services/merge_service.py +784 -0
  171. package/app/backend/app/services/orchestrator_log.py +84 -0
  172. package/app/backend/app/services/planner_service.py +1662 -0
  173. package/app/backend/app/services/planner_tick_sync.py +1040 -0
  174. package/app/backend/app/services/queued_message_service.py +156 -0
  175. package/app/backend/app/services/reliability_wrapper.py +389 -0
  176. package/app/backend/app/services/repo_discovery_service.py +318 -0
  177. package/app/backend/app/services/review_service.py +334 -0
  178. package/app/backend/app/services/revision_service.py +389 -0
  179. package/app/backend/app/services/safe_autopilot.py +510 -0
  180. package/app/backend/app/services/sqlite_worker.py +372 -0
  181. package/app/backend/app/services/task_dispatch.py +135 -0
  182. package/app/backend/app/services/ticket_generation_service.py +1781 -0
  183. package/app/backend/app/services/ticket_service.py +486 -0
  184. package/app/backend/app/services/udar_planner_service.py +1007 -0
  185. package/app/backend/app/services/webhook_service.py +126 -0
  186. package/app/backend/app/services/workspace_service.py +465 -0
  187. package/app/backend/app/services/worktree_file_service.py +92 -0
  188. package/app/backend/app/services/worktree_validator.py +213 -0
  189. package/app/backend/app/sqlite_kv.py +278 -0
  190. package/app/backend/app/state_machine.py +128 -0
  191. package/app/backend/app/templates/__init__.py +5 -0
  192. package/app/backend/app/templates/registry.py +243 -0
  193. package/app/backend/app/utils/__init__.py +5 -0
  194. package/app/backend/app/utils/artifact_reader.py +87 -0
  195. package/app/backend/app/utils/circuit_breaker.py +229 -0
  196. package/app/backend/app/utils/db_retry.py +136 -0
  197. package/app/backend/app/utils/ignored_fields.py +123 -0
  198. package/app/backend/app/utils/validators.py +54 -0
  199. package/app/backend/app/websocket/__init__.py +5 -0
  200. package/app/backend/app/websocket/manager.py +179 -0
  201. package/app/backend/app/websocket/state_tracker.py +113 -0
  202. package/app/backend/app/worker.py +3190 -0
  203. package/app/backend/calculator_tickets.json +40 -0
  204. package/app/backend/canary_tests.sh +591 -0
  205. package/app/backend/celerybeat-schedule +0 -0
  206. package/app/backend/celerybeat-schedule-shm +0 -0
  207. package/app/backend/celerybeat-schedule-wal +0 -0
  208. package/app/backend/logs/.gitkeep +3 -0
  209. package/app/backend/multiplication_division_implementation_tickets.json +55 -0
  210. package/app/backend/multiplication_division_tickets.json +42 -0
  211. package/app/backend/pyproject.toml +45 -0
  212. package/app/backend/requirements-dev.txt +8 -0
  213. package/app/backend/requirements.txt +20 -0
  214. package/app/backend/run.sh +30 -0
  215. package/app/backend/run_with_logs.sh +10 -0
  216. package/app/backend/scientific_calculator_tickets.json +40 -0
  217. package/app/backend/scripts/extract_openapi.py +21 -0
  218. package/app/backend/scripts/seed_demo.py +187 -0
  219. package/app/backend/setup_demo_review.py +302 -0
  220. package/app/backend/test_actual_parse.py +41 -0
  221. package/app/backend/test_agent_streaming.py +61 -0
  222. package/app/backend/test_parse.py +51 -0
  223. package/app/backend/test_streaming.py +51 -0
  224. package/app/backend/test_subprocess_streaming.py +50 -0
  225. package/app/backend/tests/__init__.py +1 -0
  226. package/app/backend/tests/conftest.py +46 -0
  227. package/app/backend/tests/test_auth.py +341 -0
  228. package/app/backend/tests/test_autonomy_service.py +391 -0
  229. package/app/backend/tests/test_cleanup_service_safety.py +417 -0
  230. package/app/backend/tests/test_middleware.py +279 -0
  231. package/app/backend/tests/test_planner_providers.py +290 -0
  232. package/app/backend/tests/test_planner_unblock.py +183 -0
  233. package/app/backend/tests/test_revision_invariants.py +618 -0
  234. package/app/backend/tests/test_sqlite_kv.py +290 -0
  235. package/app/backend/tests/test_sqlite_worker.py +353 -0
  236. package/app/backend/tests/test_task_dispatch.py +100 -0
  237. package/app/backend/tests/test_ticket_validation.py +304 -0
  238. package/app/backend/tests/test_udar_agent.py +693 -0
  239. package/app/backend/tests/test_webhook_service.py +184 -0
  240. package/app/backend/tickets_output.json +59 -0
  241. package/app/backend/user_management_tickets.json +50 -0
  242. package/app/backend/uvicorn.log +0 -0
  243. package/app/draft.yaml +313 -0
  244. package/app/frontend/dist/assets/index-LcjCczu5.js +155 -0
  245. package/app/frontend/dist/assets/index-_FP_279e.css +1 -0
  246. package/app/frontend/dist/index.html +14 -0
  247. package/app/frontend/dist/vite.svg +1 -0
  248. package/app/frontend/package.json +101 -0
  249. package/bin/cli.js +527 -0
  250. package/package.json +37 -0
@@ -0,0 +1,9 @@
1
+ # Backend environment variables
2
+ APP_ENV=development
3
+ DATABASE_URL=sqlite:///./kanban.db
4
+ FRONTEND_URL=http://localhost:5173
5
+
6
+ # LLM API keys (at least one needed for ticket generation / planner)
7
+ # ANTHROPIC_API_KEY=sk-ant-...
8
+ # OPENAI_API_KEY=sk-...
9
+ # AWS credentials auto-detected for Bedrock
@@ -0,0 +1,195 @@
1
+ diff --git a/package.json b/package.json
2
+ index 1a2b3c4..5d6e7f8 100644
3
+ --- a/package.json
4
+ +++ b/package.json
5
+ @@ -10,7 +10,9 @@
6
+ "dependencies": {
7
+ "express": "^4.18.2",
8
+ "better-sqlite3": "^9.4.3",
9
+ - "cors": "^2.8.5"
10
+ + "cors": "^2.8.5",
11
+ + "jsonwebtoken": "^9.0.2",
12
+ + "bcryptjs": "^2.4.3"
13
+ },
14
+ "devDependencies": {
15
+ "jest": "^29.7.0",
16
+ diff --git a/src/models/db.js b/src/models/db.js
17
+ index 2b3c4d5..8e9f0a1 100644
18
+ --- a/src/models/db.js
19
+ +++ b/src/models/db.js
20
+ @@ -8,6 +8,16 @@ db.exec(`
21
+ )
22
+ `);
23
+
24
+ +db.exec(`
25
+ + CREATE TABLE IF NOT EXISTS users (
26
+ + id INTEGER PRIMARY KEY AUTOINCREMENT,
27
+ + username TEXT UNIQUE NOT NULL,
28
+ + password TEXT NOT NULL,
29
+ + email TEXT,
30
+ + created_at DATETIME DEFAULT CURRENT_TIMESTAMP
31
+ + )
32
+ +`);
33
+ +
34
+ module.exports = db;
35
+ diff --git a/src/middleware/auth.js b/src/middleware/auth.js
36
+ new file mode 100644
37
+ index 0000000..3f4a5b6
38
+ --- /dev/null
39
+ +++ b/src/middleware/auth.js
40
+ @@ -0,0 +1,45 @@
41
+ +const jwt = require("jsonwebtoken");
42
+ +
43
+ +const JWT_SECRET = process.env.JWT_SECRET || "dev-secret-key";
44
+ +
45
+ +function authenticateToken(req, res, next) {
46
+ + const authHeader = req.headers["authorization"];
47
+ + const token = authHeader && authHeader.split(" ")[1];
48
+ +
49
+ + if (!token) {
50
+ + return res.status(401).json({ error: "Access token required" });
51
+ + }
52
+ +
53
+ + try {
54
+ + const decoded = jwt.verify(token, JWT_SECRET);
55
+ + req.user = decoded;
56
+ + next();
57
+ + } catch (err) {
58
+ + return res.status(401).json({ error: "Invalid or expired token" });
59
+ + }
60
+ +}
61
+ +
62
+ +function generateToken(user) {
63
+ + return jwt.sign(
64
+ + { id: user.id, username: user.username },
65
+ + JWT_SECRET,
66
+ + { expiresIn: "24h" }
67
+ + );
68
+ +}
69
+ +
70
+ +function hashPassword(password) {
71
+ + const bcrypt = require("bcryptjs");
72
+ + return bcrypt.hashSync(password, 10);
73
+ +}
74
+ +
75
+ +function comparePassword(password, hash) {
76
+ + const bcrypt = require("bcryptjs");
77
+ + return bcrypt.compareSync(password, hash);
78
+ +}
79
+ +
80
+ +module.exports = {
81
+ + authenticateToken,
82
+ + generateToken,
83
+ + hashPassword,
84
+ + comparePassword,
85
+ + JWT_SECRET
86
+ +};
87
+ diff --git a/src/routes/auth.js b/src/routes/auth.js
88
+ new file mode 100644
89
+ index 0000000..7c8d9e0
90
+ --- /dev/null
91
+ +++ b/src/routes/auth.js
92
+ @@ -0,0 +1,78 @@
93
+ +const express = require("express");
94
+ +const router = express.Router();
95
+ +const db = require("../models/db");
96
+ +const { generateToken, hashPassword, comparePassword } = require("../middleware/auth");
97
+ +
98
+ +// POST /api/auth/register
99
+ +router.post("/register", (req, res) => {
100
+ + const { username, password, email } = req.body;
101
+ +
102
+ + if (!username || !password) {
103
+ + return res.status(400).json({ error: "Username and password are required" });
104
+ + }
105
+ +
106
+ + if (password.length < 6) {
107
+ + return res.status(400).json({ error: "Password must be at least 6 characters" });
108
+ + }
109
+ +
110
+ + try {
111
+ + const existing = db.prepare("SELECT id FROM users WHERE username = ?").get(username);
112
+ + if (existing) {
113
+ + return res.status(409).json({ error: "Username already exists" });
114
+ + }
115
+ +
116
+ + const hashedPassword = hashPassword(password);
117
+ + const result = db.prepare(
118
+ + "INSERT INTO users (username, password, email) VALUES (?, ?, ?)"
119
+ + ).run(username, hashedPassword, email || null);
120
+ +
121
+ + const user = { id: result.lastInsertRowid, username };
122
+ + const token = generateToken(user);
123
+ +
124
+ + res.status(201).json({ user: { id: user.id, username }, token });
125
+ + } catch (err) {
126
+ + res.status(500).json({ error: "Registration failed" });
127
+ + }
128
+ +});
129
+ +
130
+ +// POST /api/auth/login
131
+ +router.post("/login", (req, res) => {
132
+ + const { username, password } = req.body;
133
+ +
134
+ + if (!username || !password) {
135
+ + return res.status(400).json({ error: "Username and password are required" });
136
+ + }
137
+ +
138
+ + try {
139
+ + const user = db.prepare("SELECT * FROM users WHERE username = ?").get(username);
140
+ +
141
+ + if (!user) {
142
+ + return res.status(401).json({ error: "Invalid credentials" });
143
+ + }
144
+ +
145
+ + if (!comparePassword(password, user.password)) {
146
+ + return res.status(401).json({ error: "Invalid credentials" });
147
+ + }
148
+ +
149
+ + const token = generateToken({ id: user.id, username: user.username });
150
+ + res.json({ user: { id: user.id, username: user.username }, token });
151
+ + } catch (err) {
152
+ + res.status(500).json({ error: "Login failed" });
153
+ + }
154
+ +});
155
+ +
156
+ +// GET /api/auth/me - Get current user
157
+ +router.get("/me", (req, res) => {
158
+ + if (!req.user) {
159
+ + return res.status(401).json({ error: "Not authenticated" });
160
+ + }
161
+ +
162
+ + const user = db.prepare("SELECT id, username, email, created_at FROM users WHERE id = ?").get(req.user.id);
163
+ + if (!user) {
164
+ + return res.status(404).json({ error: "User not found" });
165
+ + }
166
+ +
167
+ + res.json({ user });
168
+ +});
169
+ +
170
+ +module.exports = router;
171
+ diff --git a/src/index.js b/src/index.js
172
+ index 4d5e6f7..9a0b1c2 100644
173
+ --- a/src/index.js
174
+ +++ b/src/index.js
175
+ @@ -2,6 +2,8 @@ const express = require("express");
176
+ const cors = require("cors");
177
+ const taskRoutes = require("./routes/tasks");
178
+ const projectRoutes = require("./routes/projects");
179
+ +const authRoutes = require("./routes/auth");
180
+ +const { authenticateToken } = require("./middleware/auth");
181
+ const { errorHandler } = require("./middleware/errorHandler");
182
+
183
+ const app = express();
184
+ @@ -12,8 +14,10 @@ app.use(express.json());
185
+
186
+ app.get("/health", (req, res) => res.json({ status: "ok" }));
187
+
188
+ -app.use("/api/tasks", taskRoutes);
189
+ -app.use("/api/projects", projectRoutes);
190
+ +app.use("/api/auth", authRoutes);
191
+ +
192
+ +app.use("/api/tasks", authenticateToken, taskRoutes);
193
+ +app.use("/api/projects", authenticateToken, projectRoutes);
194
+
195
+ app.use(errorHandler);
@@ -0,0 +1,6 @@
1
+ src/middleware/auth.js | 45 +++++++++++++++++++++++++++++++++++++++++++++
2
+ src/routes/auth.js | 78 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
3
+ src/models/db.js | 12 ++++++++++--
4
+ src/index.js | 8 ++++++--
5
+ package.json | 4 +++-
6
+ 5 files changed, 142 insertions(+), 5 deletions(-)
@@ -0,0 +1,335 @@
1
+ # Draft API - Example curl Commands
2
+
3
+ This document provides example curl commands to interact with the Draft API.
4
+
5
+ ## Prerequisites
6
+
7
+ Start the backend server:
8
+
9
+ ```bash
10
+ cd backend
11
+ source venv/bin/activate
12
+ uvicorn app.main:app --reload
13
+ ```
14
+
15
+ The API will be available at `http://localhost:8000`.
16
+
17
+ ## Health Check
18
+
19
+ ```bash
20
+ curl http://localhost:8000/health
21
+ ```
22
+
23
+ Expected response:
24
+ ```json
25
+ {"status": "ok"}
26
+ ```
27
+
28
+ ## Goals
29
+
30
+ ### Create a Goal
31
+
32
+ ```bash
33
+ curl -X POST http://localhost:8000/goals \
34
+ -H "Content-Type: application/json" \
35
+ -d '{
36
+ "title": "Implement User Authentication",
37
+ "description": "Add login, registration, and session management to the application"
38
+ }'
39
+ ```
40
+
41
+ Expected response:
42
+ ```json
43
+ {
44
+ "id": "550e8400-e29b-41d4-a716-446655440000",
45
+ "title": "Implement User Authentication",
46
+ "description": "Add login, registration, and session management to the application",
47
+ "created_at": "2026-01-05T10:00:00",
48
+ "updated_at": "2026-01-05T10:00:00"
49
+ }
50
+ ```
51
+
52
+ ### List All Goals
53
+
54
+ ```bash
55
+ curl http://localhost:8000/goals
56
+ ```
57
+
58
+ Expected response:
59
+ ```json
60
+ {
61
+ "goals": [
62
+ {
63
+ "id": "550e8400-e29b-41d4-a716-446655440000",
64
+ "title": "Implement User Authentication",
65
+ "description": "Add login, registration, and session management to the application",
66
+ "created_at": "2026-01-05T10:00:00",
67
+ "updated_at": "2026-01-05T10:00:00"
68
+ }
69
+ ],
70
+ "total": 1
71
+ }
72
+ ```
73
+
74
+ ### Get a Single Goal
75
+
76
+ ```bash
77
+ curl http://localhost:8000/goals/{goal_id}
78
+ ```
79
+
80
+ Replace `{goal_id}` with the actual goal UUID.
81
+
82
+ ## Tickets
83
+
84
+ ### Create a Ticket
85
+
86
+ ```bash
87
+ curl -X POST http://localhost:8000/tickets \
88
+ -H "Content-Type: application/json" \
89
+ -d '{
90
+ "goal_id": "550e8400-e29b-41d4-a716-446655440000",
91
+ "title": "Design login form UI",
92
+ "description": "Create a responsive login form with email and password fields",
93
+ "priority": 80,
94
+ "actor_type": "human",
95
+ "actor_id": "user-123"
96
+ }'
97
+ ```
98
+
99
+ Expected response:
100
+ ```json
101
+ {
102
+ "id": "660e8400-e29b-41d4-a716-446655440001",
103
+ "goal_id": "550e8400-e29b-41d4-a716-446655440000",
104
+ "title": "Design login form UI",
105
+ "description": "Create a responsive login form with email and password fields",
106
+ "state": "proposed",
107
+ "priority": 80,
108
+ "created_at": "2026-01-05T10:05:00",
109
+ "updated_at": "2026-01-05T10:05:00"
110
+ }
111
+ ```
112
+
113
+ ### Get a Ticket
114
+
115
+ ```bash
116
+ curl http://localhost:8000/tickets/{ticket_id}
117
+ ```
118
+
119
+ Replace `{ticket_id}` with the actual ticket UUID.
120
+
121
+ ### Transition a Ticket (Valid)
122
+
123
+ Move from `proposed` to `planned`:
124
+
125
+ ```bash
126
+ curl -X POST http://localhost:8000/tickets/{ticket_id}/transition \
127
+ -H "Content-Type: application/json" \
128
+ -d '{
129
+ "to_state": "planned",
130
+ "actor_type": "planner",
131
+ "actor_id": "ai-planner-1",
132
+ "reason": "Task has been reviewed and broken down"
133
+ }'
134
+ ```
135
+
136
+ Expected response (ticket with updated state):
137
+ ```json
138
+ {
139
+ "id": "660e8400-e29b-41d4-a716-446655440001",
140
+ "goal_id": "550e8400-e29b-41d4-a716-446655440000",
141
+ "title": "Design login form UI",
142
+ "description": "Create a responsive login form with email and password fields",
143
+ "state": "planned",
144
+ "priority": 80,
145
+ "created_at": "2026-01-05T10:05:00",
146
+ "updated_at": "2026-01-05T10:10:00"
147
+ }
148
+ ```
149
+
150
+ ### Complete Workflow: proposed → planned → executing → verifying → done
151
+
152
+ ```bash
153
+ # Start executing (planned → executing)
154
+ curl -X POST http://localhost:8000/tickets/{ticket_id}/transition \
155
+ -H "Content-Type: application/json" \
156
+ -d '{
157
+ "to_state": "executing",
158
+ "actor_type": "executor",
159
+ "actor_id": "ai-executor-1",
160
+ "reason": "Starting implementation"
161
+ }'
162
+
163
+ # Submit for verification (executing → verifying)
164
+ curl -X POST http://localhost:8000/tickets/{ticket_id}/transition \
165
+ -H "Content-Type: application/json" \
166
+ -d '{
167
+ "to_state": "verifying",
168
+ "actor_type": "executor",
169
+ "actor_id": "ai-executor-1",
170
+ "reason": "Implementation complete, ready for review"
171
+ }'
172
+
173
+ # Mark as done (verifying → done)
174
+ curl -X POST http://localhost:8000/tickets/{ticket_id}/transition \
175
+ -H "Content-Type: application/json" \
176
+ -d '{
177
+ "to_state": "done",
178
+ "actor_type": "human",
179
+ "actor_id": "user-123",
180
+ "reason": "Verified and approved"
181
+ }'
182
+ ```
183
+
184
+ ### Transition a Ticket (Invalid - Should Fail)
185
+
186
+ Try to go directly from `proposed` to `done`:
187
+
188
+ ```bash
189
+ curl -X POST http://localhost:8000/tickets/{ticket_id}/transition \
190
+ -H "Content-Type: application/json" \
191
+ -d '{
192
+ "to_state": "done",
193
+ "actor_type": "human",
194
+ "actor_id": "user-123",
195
+ "reason": "Trying to skip states"
196
+ }'
197
+ ```
198
+
199
+ Expected error response (HTTP 400):
200
+ ```json
201
+ {
202
+ "detail": "Invalid transition from 'proposed' to 'done'",
203
+ "error_type": "invalid_state_transition",
204
+ "from_state": "proposed",
205
+ "to_state": "done"
206
+ }
207
+ ```
208
+
209
+ ### Get Ticket Events (Audit Log)
210
+
211
+ ```bash
212
+ curl http://localhost:8000/tickets/{ticket_id}/events
213
+ ```
214
+
215
+ Expected response:
216
+ ```json
217
+ {
218
+ "events": [
219
+ {
220
+ "id": "770e8400-e29b-41d4-a716-446655440001",
221
+ "ticket_id": "660e8400-e29b-41d4-a716-446655440001",
222
+ "event_type": "created",
223
+ "from_state": null,
224
+ "to_state": "proposed",
225
+ "actor_type": "human",
226
+ "actor_id": "user-123",
227
+ "reason": "Ticket created",
228
+ "payload": {
229
+ "title": "Design login form UI",
230
+ "description": "Create a responsive login form with email and password fields",
231
+ "goal_id": "550e8400-e29b-41d4-a716-446655440000",
232
+ "priority": 80
233
+ },
234
+ "created_at": "2026-01-05T10:05:00"
235
+ },
236
+ {
237
+ "id": "770e8400-e29b-41d4-a716-446655440002",
238
+ "ticket_id": "660e8400-e29b-41d4-a716-446655440001",
239
+ "event_type": "transitioned",
240
+ "from_state": "proposed",
241
+ "to_state": "planned",
242
+ "actor_type": "planner",
243
+ "actor_id": "ai-planner-1",
244
+ "reason": "Task has been reviewed and broken down",
245
+ "payload": null,
246
+ "created_at": "2026-01-05T10:10:00"
247
+ }
248
+ ],
249
+ "total": 2
250
+ }
251
+ ```
252
+
253
+ ## Board View
254
+
255
+ ### Get Board (All Tickets Grouped by State)
256
+
257
+ ```bash
258
+ curl http://localhost:8000/board
259
+ ```
260
+
261
+ Expected response:
262
+ ```json
263
+ {
264
+ "columns": [
265
+ {
266
+ "state": "proposed",
267
+ "tickets": []
268
+ },
269
+ {
270
+ "state": "planned",
271
+ "tickets": [
272
+ {
273
+ "id": "660e8400-e29b-41d4-a716-446655440001",
274
+ "goal_id": "550e8400-e29b-41d4-a716-446655440000",
275
+ "title": "Design login form UI",
276
+ "description": "Create a responsive login form",
277
+ "state": "planned",
278
+ "priority": 80,
279
+ "created_at": "2026-01-05T10:05:00",
280
+ "updated_at": "2026-01-05T10:10:00"
281
+ }
282
+ ]
283
+ },
284
+ {
285
+ "state": "executing",
286
+ "tickets": []
287
+ },
288
+ {
289
+ "state": "verifying",
290
+ "tickets": []
291
+ },
292
+ {
293
+ "state": "needs_human",
294
+ "tickets": []
295
+ },
296
+ {
297
+ "state": "blocked",
298
+ "tickets": []
299
+ },
300
+ {
301
+ "state": "done",
302
+ "tickets": []
303
+ },
304
+ {
305
+ "state": "abandoned",
306
+ "tickets": []
307
+ }
308
+ ],
309
+ "total_tickets": 1
310
+ }
311
+ ```
312
+
313
+ ## State Machine Reference
314
+
315
+ ### Valid State Transitions
316
+
317
+ | From State | Valid Next States |
318
+ |---------------|------------------------------------------|
319
+ | proposed | planned, abandoned |
320
+ | planned | executing, blocked, abandoned |
321
+ | executing | verifying, needs_human, blocked |
322
+ | verifying | done, executing, needs_human |
323
+ | needs_human | executing, planned, abandoned |
324
+ | blocked | planned, abandoned |
325
+ | done | (terminal state - no transitions) |
326
+ | abandoned | (terminal state - no transitions) |
327
+
328
+ ### Actor Types
329
+
330
+ - `human` - Human user action
331
+ - `planner` - AI planner agent
332
+ - `executor` - AI executor agent
333
+ - `system` - Automated system action
334
+
335
+
@@ -0,0 +1,65 @@
1
+ # Environment Variables Setup
2
+
3
+ ## AWS Bedrock Configuration
4
+
5
+ To use AWS Bedrock models with the Draft planner, add these variables to your `.env` file in the `backend/` directory:
6
+
7
+ ```bash
8
+ # Required AWS Credentials
9
+ AWS_ACCESS_KEY_ID=your-access-key-id-here
10
+ AWS_SECRET_ACCESS_KEY=your-secret-access-key-here
11
+ AWS_REGION_NAME=us-east-1
12
+ ```
13
+
14
+ ### Notes:
15
+
16
+ 1. **Model Configuration**: The model is configured in `draft.yaml`:
17
+ ```yaml
18
+ planner_config:
19
+ model: "bedrock/anthropic.claude-sonnet-4-5-20250929-v1:0"
20
+ ```
21
+
22
+ 2. **AWS Region**: Make sure your `AWS_REGION_NAME` matches where your Bedrock model is available. Common regions:
23
+ - `us-east-1` (N. Virginia)
24
+ - `us-west-2` (Oregon)
25
+ - Check AWS Bedrock documentation for model availability by region
26
+
27
+ 3. **Bedrock Access**: Ensure your AWS account has:
28
+ - AWS Bedrock enabled
29
+ - Model access requested and approved for Claude Sonnet 4.5
30
+ - IAM permissions for `bedrock:InvokeModel`
31
+
32
+ 4. **Alternative: AWS Profile**: Instead of access keys, you can use an AWS CLI profile:
33
+ ```bash
34
+ AWS_PROFILE=your-profile-name
35
+ AWS_REGION_NAME=us-east-1
36
+ ```
37
+
38
+ ## Other Environment Variables
39
+
40
+ ```bash
41
+ # Frontend URL for CORS (optional, defaults to http://localhost:5173)
42
+ FRONTEND_URL=http://localhost:5173
43
+
44
+ # Git repository path (optional, defaults to current directory)
45
+ GIT_REPO_PATH=/path/to/your/repo
46
+
47
+ # Database URL (optional, defaults to sqlite:///kanban.db)
48
+ DATABASE_URL=sqlite:///kanban.db
49
+ ```
50
+
51
+ ## Testing the Configuration
52
+
53
+ 1. Make sure your `.env` file is in the `backend/` directory
54
+ 2. Restart the backend server to load the new environment variables
55
+ 3. Trigger a planner action (like proposing follow-ups for a blocked ticket) to test the LLM connection
56
+ 4. Check the logs for any AWS authentication or model access errors
57
+
58
+ ## Troubleshooting
59
+
60
+ - **Authentication errors**: Verify your AWS credentials are correct
61
+ - **Model not found**: Check if the model ID is correct and available in your region
62
+ - **Access denied**: Ensure your IAM user/role has Bedrock permissions
63
+ - **Region mismatch**: Verify the model is available in your configured region
64
+
65
+
@@ -0,0 +1,71 @@
1
+ """Alembic environment configuration for Draft."""
2
+
3
+ from logging.config import fileConfig
4
+
5
+ from sqlalchemy import engine_from_config, pool
6
+
7
+ from alembic import context
8
+
9
+ # Import models to ensure they are registered with SQLAlchemy
10
+ from app.models import Base # noqa: F401
11
+
12
+ # this is the Alembic Config object, which provides
13
+ # access to the values within the .ini file in use.
14
+ config = context.config
15
+
16
+ # Interpret the config file for Python logging.
17
+ # This line sets up loggers basically.
18
+ if config.config_file_name is not None:
19
+ fileConfig(config.config_file_name)
20
+
21
+ # add your model's MetaData object here
22
+ # for 'autogenerate' support
23
+ target_metadata = Base.metadata
24
+
25
+
26
+ def run_migrations_offline() -> None:
27
+ """Run migrations in 'offline' mode.
28
+
29
+ This configures the context with just a URL
30
+ and not an Engine, though an Engine is acceptable
31
+ here as well. By skipping the Engine creation
32
+ we don't even need a DBAPI to be available.
33
+
34
+ Calls to context.execute() here emit the given string to the
35
+ script output.
36
+ """
37
+ url = config.get_main_option("sqlalchemy.url")
38
+ context.configure(
39
+ url=url,
40
+ target_metadata=target_metadata,
41
+ literal_binds=True,
42
+ dialect_opts={"paramstyle": "named"},
43
+ )
44
+
45
+ with context.begin_transaction():
46
+ context.run_migrations()
47
+
48
+
49
+ def run_migrations_online() -> None:
50
+ """Run migrations in 'online' mode.
51
+
52
+ In this scenario we need to create an Engine
53
+ and associate a connection with the context.
54
+ """
55
+ connectable = engine_from_config(
56
+ config.get_section(config.config_ini_section, {}),
57
+ prefix="sqlalchemy.",
58
+ poolclass=pool.NullPool,
59
+ )
60
+
61
+ with connectable.connect() as connection:
62
+ context.configure(connection=connection, target_metadata=target_metadata)
63
+
64
+ with context.begin_transaction():
65
+ context.run_migrations()
66
+
67
+
68
+ if context.is_offline_mode():
69
+ run_migrations_offline()
70
+ else:
71
+ run_migrations_online()