popilot 0.5.0 → 0.7.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 (171) hide show
  1. package/adapters/codex/.codex/commands/_domain.md.hbs +33 -0
  2. package/adapters/codex/.codex/commands/analytics.md.hbs +55 -0
  3. package/adapters/codex/.codex/commands/daily.md.hbs +301 -0
  4. package/adapters/codex/.codex/commands/dev.md.hbs +62 -0
  5. package/adapters/codex/.codex/commands/gtm.md +82 -0
  6. package/adapters/codex/.codex/commands/handoff.md +259 -0
  7. package/adapters/codex/.codex/commands/market.md +120 -0
  8. package/adapters/codex/.codex/commands/metrics.md +123 -0
  9. package/adapters/codex/.codex/commands/oscar-loop.md +436 -0
  10. package/adapters/codex/.codex/commands/party.md +85 -0
  11. package/adapters/codex/.codex/commands/plan.md +43 -0
  12. package/adapters/codex/.codex/commands/research.md +203 -0
  13. package/adapters/codex/.codex/commands/retro.md +68 -0
  14. package/adapters/codex/.codex/commands/save.md +440 -0
  15. package/adapters/codex/.codex/commands/sessions.md +139 -0
  16. package/adapters/codex/.codex/commands/sprint.md +106 -0
  17. package/adapters/codex/.codex/commands/start.md +396 -0
  18. package/adapters/codex/.codex/commands/strategy.md +41 -0
  19. package/adapters/codex/.codex/commands/task.md +220 -0
  20. package/adapters/codex/.codex/commands/tracking.md +116 -0
  21. package/adapters/codex/.codex/commands/validate.md +58 -0
  22. package/adapters/codex/AGENTS.md.hbs +210 -0
  23. package/adapters/codex/manifest.yaml +36 -0
  24. package/adapters/gemini/.gemini/commands/_domain.md.hbs +33 -0
  25. package/adapters/gemini/.gemini/commands/analytics.md.hbs +55 -0
  26. package/adapters/gemini/.gemini/commands/daily.md.hbs +301 -0
  27. package/adapters/gemini/.gemini/commands/dev.md.hbs +62 -0
  28. package/adapters/gemini/.gemini/commands/gtm.md +82 -0
  29. package/adapters/gemini/.gemini/commands/handoff.md +259 -0
  30. package/adapters/gemini/.gemini/commands/market.md +120 -0
  31. package/adapters/gemini/.gemini/commands/metrics.md +123 -0
  32. package/adapters/gemini/.gemini/commands/oscar-loop.md +436 -0
  33. package/adapters/gemini/.gemini/commands/party.md +85 -0
  34. package/adapters/gemini/.gemini/commands/plan.md +43 -0
  35. package/adapters/gemini/.gemini/commands/research.md +203 -0
  36. package/adapters/gemini/.gemini/commands/retro.md +68 -0
  37. package/adapters/gemini/.gemini/commands/save.md +440 -0
  38. package/adapters/gemini/.gemini/commands/sessions.md +139 -0
  39. package/adapters/gemini/.gemini/commands/sprint.md +106 -0
  40. package/adapters/gemini/.gemini/commands/start.md +396 -0
  41. package/adapters/gemini/.gemini/commands/strategy.md +41 -0
  42. package/adapters/gemini/.gemini/commands/task.md +220 -0
  43. package/adapters/gemini/.gemini/commands/tracking.md +116 -0
  44. package/adapters/gemini/.gemini/commands/validate.md +58 -0
  45. package/adapters/gemini/GEMINI.md.hbs +210 -0
  46. package/adapters/gemini/manifest.yaml +36 -0
  47. package/bin/cli.mjs +215 -4
  48. package/lib/doctor.mjs +38 -1
  49. package/lib/hydrate.mjs +15 -0
  50. package/lib/industry-presets.mjs +135 -0
  51. package/lib/scaffold.mjs +5 -0
  52. package/lib/setup-wizard.mjs +71 -2
  53. package/package.json +1 -1
  54. package/scaffold/.context/agents/TEMPLATE.md +14 -0
  55. package/scaffold/.context/agents/analyst.md.hbs +3 -3
  56. package/scaffold/.context/agents/developer.md.hbs +5 -5
  57. package/scaffold/.context/agents/gtm-strategist.md.hbs +3 -3
  58. package/scaffold/.context/agents/handoff-specialist.md.hbs +18 -18
  59. package/scaffold/.context/agents/market-researcher.md.hbs +6 -6
  60. package/scaffold/.context/agents/orchestrator.md.hbs +8 -8
  61. package/scaffold/.context/agents/planner.md.hbs +6 -6
  62. package/scaffold/.context/agents/qa.md.hbs +5 -5
  63. package/scaffold/.context/agents/researcher.md.hbs +33 -6
  64. package/scaffold/.context/agents/strategist.md.hbs +8 -8
  65. package/scaffold/.context/agents/tracking-governor.md.hbs +2 -2
  66. package/scaffold/.context/project.yaml.example +25 -0
  67. package/scaffold/mcp-pm/package.json +19 -0
  68. package/scaffold/mcp-pm/src/api-client.ts +69 -0
  69. package/scaffold/mcp-pm/src/index.ts +660 -0
  70. package/scaffold/mcp-pm/tsconfig.json +14 -0
  71. package/scaffold/pm-api/package.json +21 -0
  72. package/scaffold/pm-api/sql/schema-core.sql +331 -0
  73. package/scaffold/pm-api/sql/schema-docs.sql +25 -0
  74. package/scaffold/pm-api/sql/schema-meetings.sql +17 -0
  75. package/scaffold/pm-api/sql/schema-rewards.sql +16 -0
  76. package/scaffold/pm-api/src/auth.ts +28 -0
  77. package/scaffold/pm-api/src/blockchain/adapter.ts +20 -0
  78. package/scaffold/pm-api/src/blockchain/tron.ts +62 -0
  79. package/scaffold/pm-api/src/db/adapter.ts +36 -0
  80. package/scaffold/pm-api/src/db/turso.ts +147 -0
  81. package/scaffold/pm-api/src/index.ts +114 -0
  82. package/scaffold/pm-api/src/mcp-tools/dashboard.ts +40 -0
  83. package/scaffold/pm-api/src/mcp-tools/epic.ts +67 -0
  84. package/scaffold/pm-api/src/mcp-tools/event.ts +89 -0
  85. package/scaffold/pm-api/src/mcp-tools/index.ts +11 -0
  86. package/scaffold/pm-api/src/mcp-tools/initiative.ts +51 -0
  87. package/scaffold/pm-api/src/mcp-tools/memo.ts +164 -0
  88. package/scaffold/pm-api/src/mcp-tools/notification.ts +37 -0
  89. package/scaffold/pm-api/src/mcp-tools/retro.ts +183 -0
  90. package/scaffold/pm-api/src/mcp-tools/sprint.ts +204 -0
  91. package/scaffold/pm-api/src/mcp-tools/standup.ts +136 -0
  92. package/scaffold/pm-api/src/mcp-tools/story.ts +230 -0
  93. package/scaffold/pm-api/src/mcp-tools/task.ts +187 -0
  94. package/scaffold/pm-api/src/mcp-tools/utils.ts +83 -0
  95. package/scaffold/pm-api/src/mcp.ts +871 -0
  96. package/scaffold/pm-api/src/nudge.ts +283 -0
  97. package/scaffold/pm-api/src/routes/auth.ts +32 -0
  98. package/scaffold/pm-api/src/routes/v2-activity.ts +27 -0
  99. package/scaffold/pm-api/src/routes/v2-admin.ts +165 -0
  100. package/scaffold/pm-api/src/routes/v2-dashboard.ts +189 -0
  101. package/scaffold/pm-api/src/routes/v2-docs.ts +34 -0
  102. package/scaffold/pm-api/src/routes/v2-initiatives.ts +118 -0
  103. package/scaffold/pm-api/src/routes/v2-kickoff.ts +265 -0
  104. package/scaffold/pm-api/src/routes/v2-meetings.ts +324 -0
  105. package/scaffold/pm-api/src/routes/v2-memos.ts +257 -0
  106. package/scaffold/pm-api/src/routes/v2-nav.ts +260 -0
  107. package/scaffold/pm-api/src/routes/v2-notifications.ts +79 -0
  108. package/scaffold/pm-api/src/routes/v2-page-content.ts +35 -0
  109. package/scaffold/pm-api/src/routes/v2-pm.ts +380 -0
  110. package/scaffold/pm-api/src/routes/v2-policy.ts +58 -0
  111. package/scaffold/pm-api/src/routes/v2-retro.ts +221 -0
  112. package/scaffold/pm-api/src/routes/v2-rewards.ts +132 -0
  113. package/scaffold/pm-api/src/routes/v2-scenarios.ts +48 -0
  114. package/scaffold/pm-api/src/routes/v2-search.ts +32 -0
  115. package/scaffold/pm-api/src/routes/v2-standup.ts +127 -0
  116. package/scaffold/pm-api/src/routes/v2-user.ts +38 -0
  117. package/scaffold/pm-api/src/types.ts +11 -0
  118. package/scaffold/pm-api/src/utils/activity.ts +22 -0
  119. package/scaffold/pm-api/src/utils/admin.ts +9 -0
  120. package/scaffold/pm-api/src/utils/agent-notify.ts +62 -0
  121. package/scaffold/pm-api/src/utils/assignee.ts +69 -0
  122. package/scaffold/pm-api/src/utils/db.ts +45 -0
  123. package/scaffold/pm-api/src/utils/initiative.ts +23 -0
  124. package/scaffold/pm-api/src/utils/sprint-lifecycle.ts +96 -0
  125. package/scaffold/pm-api/tsconfig.json +15 -0
  126. package/scaffold/pm-api/wrangler.toml.hbs +11 -0
  127. package/scaffold/spec-site/package-lock.json +40 -0
  128. package/scaffold/spec-site/package.json +4 -1
  129. package/scaffold/spec-site/src/api/types.ts +6 -0
  130. package/scaffold/spec-site/src/components/AppHeader.vue +429 -55
  131. package/scaffold/spec-site/src/components/MemberSelect.vue +48 -0
  132. package/scaffold/spec-site/src/components/NotificationDropdown.vue +116 -0
  133. package/scaffold/spec-site/src/components/SearchModal.vue +102 -0
  134. package/scaffold/spec-site/src/components/VelocityChart.vue +77 -0
  135. package/scaffold/spec-site/src/composables/pmTypes.ts +15 -2
  136. package/scaffold/spec-site/src/composables/useDashboard.ts +221 -0
  137. package/scaffold/spec-site/src/composables/useMediaQuery.ts +28 -0
  138. package/scaffold/spec-site/src/composables/useNotification.ts +200 -0
  139. package/scaffold/spec-site/src/composables/usePmStore.ts +48 -1
  140. package/scaffold/spec-site/src/composables/useRetro.ts +6 -0
  141. package/scaffold/spec-site/src/composables/useStandup.ts +201 -0
  142. package/scaffold/spec-site/src/composables/useTheme.ts +37 -0
  143. package/scaffold/spec-site/src/composables/useUser.ts +19 -1
  144. package/scaffold/spec-site/src/features.ts +108 -0
  145. package/scaffold/spec-site/src/pages/AdminPage.vue +299 -0
  146. package/scaffold/spec-site/src/pages/DashboardPage.vue +650 -0
  147. package/scaffold/spec-site/src/pages/DocsHub.vue +157 -0
  148. package/scaffold/spec-site/src/pages/InboxPage.vue +156 -0
  149. package/scaffold/spec-site/src/pages/MeetingsPage.vue +294 -0
  150. package/scaffold/spec-site/src/pages/MyPage.vue +343 -0
  151. package/scaffold/spec-site/src/pages/RewardsPage.vue +266 -0
  152. package/scaffold/spec-site/src/pages/board/BoardAdmin.vue +422 -0
  153. package/scaffold/spec-site/src/pages/board/BoardEpicSection.vue +54 -0
  154. package/scaffold/spec-site/src/pages/board/BoardPage.vue +884 -0
  155. package/scaffold/spec-site/src/pages/board/BoardStoryCard.vue +67 -0
  156. package/scaffold/spec-site/src/pages/board/BoardTaskItem.vue +52 -0
  157. package/scaffold/spec-site/src/pages/board/MyTasksPage.vue +202 -0
  158. package/scaffold/spec-site/src/pages/board/SprintClose.vue +167 -0
  159. package/scaffold/spec-site/src/pages/board/SprintColumn.vue +49 -0
  160. package/scaffold/spec-site/src/pages/board/SprintKickoff.vue +389 -0
  161. package/scaffold/spec-site/src/pages/board/StatusBadge.vue +52 -0
  162. package/scaffold/spec-site/src/pages/board/StoryDetailPanel.vue +495 -0
  163. package/scaffold/spec-site/src/pages/board/TaskCard.vue +42 -0
  164. package/scaffold/spec-site/src/pages/retro/RetroCard.vue +36 -2
  165. package/scaffold/spec-site/src/pages/retro/RetroHeader.vue +82 -66
  166. package/scaffold/spec-site/src/pages/retro/RetroPage.vue +47 -18
  167. package/scaffold/spec-site/src/pages/standup/StandupEntryCard.vue +551 -0
  168. package/scaffold/spec-site/src/pages/standup/StandupForm.vue +68 -0
  169. package/scaffold/spec-site/src/pages/standup/StandupList.vue +71 -0
  170. package/scaffold/spec-site/src/pages/standup/StandupPage.vue +225 -0
  171. package/scaffold/spec-site/src/router.ts +141 -0
@@ -0,0 +1,331 @@
1
+ -- PM API Core Schema
2
+ -- Run this SQL to initialize the database
3
+
4
+ -- Authentication
5
+ CREATE TABLE IF NOT EXISTS auth_tokens (
6
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
7
+ user_name TEXT NOT NULL,
8
+ token TEXT NOT NULL UNIQUE,
9
+ is_active INTEGER DEFAULT 1,
10
+ expires_at TEXT,
11
+ created_at TEXT DEFAULT (datetime('now')),
12
+ updated_at TEXT DEFAULT (datetime('now'))
13
+ );
14
+
15
+ -- Team Members
16
+ CREATE TABLE IF NOT EXISTS members (
17
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
18
+ display_name TEXT NOT NULL UNIQUE,
19
+ role TEXT DEFAULT 'member',
20
+ is_active INTEGER DEFAULT 1,
21
+ webhook_url TEXT,
22
+ wallet_address TEXT,
23
+ created_at TEXT DEFAULT (datetime('now')),
24
+ updated_at TEXT DEFAULT (datetime('now'))
25
+ );
26
+
27
+ -- Sprints
28
+ CREATE TABLE IF NOT EXISTS nav_sprints (
29
+ id TEXT PRIMARY KEY,
30
+ title TEXT NOT NULL,
31
+ theme TEXT,
32
+ status TEXT DEFAULT 'planning',
33
+ active INTEGER DEFAULT 0,
34
+ start_date TEXT,
35
+ end_date TEXT,
36
+ velocity INTEGER,
37
+ team_size INTEGER,
38
+ sort_order INTEGER DEFAULT 0,
39
+ created_at TEXT DEFAULT (datetime('now')),
40
+ updated_at TEXT DEFAULT (datetime('now'))
41
+ );
42
+
43
+ -- Sprint Members
44
+ CREATE TABLE IF NOT EXISTS sprint_members (
45
+ sprint_id TEXT NOT NULL,
46
+ member_id INTEGER NOT NULL,
47
+ working_days INTEGER DEFAULT 0,
48
+ PRIMARY KEY (sprint_id, member_id),
49
+ FOREIGN KEY (sprint_id) REFERENCES nav_sprints(id),
50
+ FOREIGN KEY (member_id) REFERENCES members(id)
51
+ );
52
+
53
+ -- Member Absences
54
+ CREATE TABLE IF NOT EXISTS member_absences (
55
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
56
+ sprint_id TEXT NOT NULL,
57
+ member_id INTEGER NOT NULL,
58
+ absence_date TEXT NOT NULL,
59
+ reason TEXT,
60
+ UNIQUE(sprint_id, member_id, absence_date),
61
+ FOREIGN KEY (sprint_id) REFERENCES nav_sprints(id),
62
+ FOREIGN KEY (member_id) REFERENCES members(id)
63
+ );
64
+
65
+ -- Epics
66
+ CREATE TABLE IF NOT EXISTS pm_epics (
67
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
68
+ title TEXT NOT NULL,
69
+ description TEXT,
70
+ owner TEXT,
71
+ status TEXT DEFAULT 'active',
72
+ created_at TEXT DEFAULT (datetime('now')),
73
+ updated_at TEXT DEFAULT (datetime('now'))
74
+ );
75
+
76
+ -- Stories
77
+ CREATE TABLE IF NOT EXISTS pm_stories (
78
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
79
+ epic_id INTEGER,
80
+ epic_uid TEXT,
81
+ sprint TEXT,
82
+ title TEXT NOT NULL,
83
+ description TEXT,
84
+ acceptance_criteria TEXT,
85
+ assignee TEXT,
86
+ assignee_id INTEGER,
87
+ status TEXT DEFAULT 'backlog',
88
+ priority TEXT DEFAULT 'medium',
89
+ area TEXT DEFAULT 'FE',
90
+ story_points INTEGER,
91
+ figma_url TEXT,
92
+ sort_order INTEGER DEFAULT 0,
93
+ start_date TEXT,
94
+ due_date TEXT,
95
+ created_at TEXT DEFAULT (datetime('now')),
96
+ updated_at TEXT DEFAULT (datetime('now')),
97
+ FOREIGN KEY (epic_id) REFERENCES pm_epics(id),
98
+ FOREIGN KEY (sprint) REFERENCES nav_sprints(id),
99
+ FOREIGN KEY (assignee_id) REFERENCES members(id)
100
+ );
101
+
102
+ -- Tasks
103
+ CREATE TABLE IF NOT EXISTS pm_tasks (
104
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
105
+ story_id INTEGER NOT NULL,
106
+ title TEXT NOT NULL,
107
+ assignee TEXT,
108
+ assignee_id INTEGER,
109
+ status TEXT DEFAULT 'todo',
110
+ description TEXT,
111
+ story_points INTEGER,
112
+ sort_order INTEGER DEFAULT 0,
113
+ created_at TEXT DEFAULT (datetime('now')),
114
+ updated_at TEXT DEFAULT (datetime('now')),
115
+ FOREIGN KEY (story_id) REFERENCES pm_stories(id),
116
+ FOREIGN KEY (assignee_id) REFERENCES members(id)
117
+ );
118
+
119
+ -- Standup Entries
120
+ CREATE TABLE IF NOT EXISTS pm_standup_entries (
121
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
122
+ sprint TEXT NOT NULL,
123
+ user_name TEXT NOT NULL,
124
+ entry_date TEXT NOT NULL,
125
+ done_text TEXT,
126
+ plan_text TEXT,
127
+ plan_story_ids TEXT,
128
+ blockers TEXT,
129
+ created_at TEXT DEFAULT (datetime('now')),
130
+ updated_at TEXT DEFAULT (datetime('now')),
131
+ UNIQUE(sprint, user_name, entry_date)
132
+ );
133
+
134
+ -- Standup Feedback
135
+ CREATE TABLE IF NOT EXISTS pm_standup_feedback (
136
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
137
+ standup_entry_id INTEGER NOT NULL,
138
+ sprint TEXT NOT NULL,
139
+ target_user TEXT NOT NULL,
140
+ feedback_by TEXT NOT NULL,
141
+ feedback_text TEXT NOT NULL,
142
+ review_type TEXT DEFAULT 'comment',
143
+ created_at TEXT DEFAULT (datetime('now')),
144
+ FOREIGN KEY (standup_entry_id) REFERENCES pm_standup_entries(id)
145
+ );
146
+
147
+ -- Memos v2
148
+ CREATE TABLE IF NOT EXISTS memos_v2 (
149
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
150
+ page_id TEXT DEFAULT 'home',
151
+ content TEXT NOT NULL,
152
+ memo_type TEXT DEFAULT 'memo',
153
+ title TEXT,
154
+ created_by TEXT NOT NULL,
155
+ created_by_id INTEGER,
156
+ assigned_to TEXT,
157
+ assigned_to_id INTEGER,
158
+ status TEXT DEFAULT 'open',
159
+ resolved_by TEXT,
160
+ resolved_at TEXT,
161
+ related_decisions TEXT,
162
+ review_required INTEGER DEFAULT 0,
163
+ supersedes_id INTEGER,
164
+ created_at TEXT DEFAULT (datetime('now')),
165
+ updated_at TEXT DEFAULT (datetime('now'))
166
+ );
167
+
168
+ -- Memo Replies
169
+ CREATE TABLE IF NOT EXISTS memo_replies (
170
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
171
+ memo_id INTEGER NOT NULL,
172
+ content TEXT NOT NULL,
173
+ created_by TEXT NOT NULL,
174
+ review_type TEXT DEFAULT 'comment',
175
+ created_at TEXT DEFAULT (datetime('now')),
176
+ FOREIGN KEY (memo_id) REFERENCES memos_v2(id)
177
+ );
178
+
179
+ -- Notifications
180
+ CREATE TABLE IF NOT EXISTS notifications (
181
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
182
+ user_name TEXT NOT NULL,
183
+ type TEXT NOT NULL,
184
+ title TEXT NOT NULL,
185
+ body TEXT,
186
+ source_type TEXT,
187
+ source_id TEXT,
188
+ page_id TEXT,
189
+ actor TEXT,
190
+ is_read INTEGER DEFAULT 0,
191
+ created_at TEXT DEFAULT (datetime('now'))
192
+ );
193
+
194
+ -- Initiatives
195
+ CREATE TABLE IF NOT EXISTS initiatives (
196
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
197
+ title TEXT NOT NULL,
198
+ content TEXT NOT NULL,
199
+ author TEXT NOT NULL,
200
+ decider TEXT,
201
+ status TEXT DEFAULT 'pending',
202
+ decision_note TEXT,
203
+ decided_at TEXT,
204
+ source_context TEXT,
205
+ created_at TEXT DEFAULT (datetime('now')),
206
+ updated_at TEXT DEFAULT (datetime('now'))
207
+ );
208
+
209
+ -- Retro Sessions
210
+ CREATE TABLE IF NOT EXISTS retro_sessions (
211
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
212
+ sprint TEXT NOT NULL UNIQUE,
213
+ title TEXT DEFAULT 'Sprint Retrospective',
214
+ phase TEXT DEFAULT 'collect',
215
+ created_at TEXT DEFAULT (datetime('now')),
216
+ updated_at TEXT DEFAULT (datetime('now'))
217
+ );
218
+
219
+ -- Retro Items
220
+ CREATE TABLE IF NOT EXISTS retro_items (
221
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
222
+ session_id INTEGER NOT NULL,
223
+ category TEXT NOT NULL,
224
+ content TEXT NOT NULL,
225
+ author TEXT NOT NULL,
226
+ created_at TEXT DEFAULT (datetime('now')),
227
+ FOREIGN KEY (session_id) REFERENCES retro_sessions(id)
228
+ );
229
+
230
+ -- Retro Votes
231
+ CREATE TABLE IF NOT EXISTS retro_votes (
232
+ item_id INTEGER NOT NULL,
233
+ voter TEXT NOT NULL,
234
+ PRIMARY KEY (item_id, voter),
235
+ FOREIGN KEY (item_id) REFERENCES retro_items(id)
236
+ );
237
+
238
+ -- Retro Actions
239
+ CREATE TABLE IF NOT EXISTS retro_actions (
240
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
241
+ session_id INTEGER NOT NULL,
242
+ content TEXT NOT NULL,
243
+ assignee TEXT,
244
+ status TEXT DEFAULT 'todo',
245
+ created_at TEXT DEFAULT (datetime('now')),
246
+ updated_at TEXT DEFAULT (datetime('now')),
247
+ FOREIGN KEY (session_id) REFERENCES retro_sessions(id)
248
+ );
249
+
250
+ -- Agent Events (Push-Native)
251
+ CREATE TABLE IF NOT EXISTS agent_events (
252
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
253
+ event_type TEXT NOT NULL,
254
+ source_agent TEXT NOT NULL,
255
+ target_agent TEXT NOT NULL,
256
+ target_user TEXT NOT NULL,
257
+ payload TEXT NOT NULL,
258
+ status TEXT DEFAULT 'pending',
259
+ expires_at TEXT,
260
+ delivered_at TEXT,
261
+ acked_at TEXT,
262
+ created_at TEXT DEFAULT (datetime('now'))
263
+ );
264
+
265
+ -- Activity Log
266
+ CREATE TABLE IF NOT EXISTS activity_log (
267
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
268
+ actor TEXT NOT NULL,
269
+ action_type TEXT NOT NULL,
270
+ target_type TEXT NOT NULL,
271
+ target_id TEXT NOT NULL,
272
+ target_title TEXT,
273
+ metadata TEXT,
274
+ created_at TEXT DEFAULT (datetime('now'))
275
+ );
276
+
277
+ -- Nudge Log
278
+ CREATE TABLE IF NOT EXISTS nudge_log (
279
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
280
+ rule_id TEXT NOT NULL,
281
+ title TEXT NOT NULL,
282
+ body TEXT,
283
+ created_at TEXT DEFAULT (datetime('now'))
284
+ );
285
+
286
+ -- Navigation Pages (for spec-site wireframes/screens)
287
+ CREATE TABLE IF NOT EXISTS nav_pages (
288
+ id TEXT PRIMARY KEY,
289
+ title TEXT NOT NULL,
290
+ icon TEXT,
291
+ sort_order INTEGER DEFAULT 0,
292
+ parent_id TEXT,
293
+ is_visible INTEGER DEFAULT 1,
294
+ created_at TEXT DEFAULT (datetime('now'))
295
+ );
296
+
297
+ -- Page Content (spec-site page sections)
298
+ CREATE TABLE IF NOT EXISTS page_content (
299
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
300
+ page_id TEXT NOT NULL,
301
+ sprint TEXT,
302
+ section_type TEXT DEFAULT 'content',
303
+ content TEXT,
304
+ sort_order INTEGER DEFAULT 0,
305
+ created_at TEXT DEFAULT (datetime('now')),
306
+ updated_at TEXT DEFAULT (datetime('now')),
307
+ FOREIGN KEY (page_id) REFERENCES nav_pages(id)
308
+ );
309
+
310
+ -- Scenarios (spec-site scenario definitions)
311
+ CREATE TABLE IF NOT EXISTS scenarios (
312
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
313
+ page_id TEXT NOT NULL,
314
+ sprint TEXT,
315
+ name TEXT NOT NULL,
316
+ description TEXT,
317
+ is_default INTEGER DEFAULT 0,
318
+ sort_order INTEGER DEFAULT 0,
319
+ created_at TEXT DEFAULT (datetime('now')),
320
+ FOREIGN KEY (page_id) REFERENCES nav_pages(id)
321
+ );
322
+
323
+ -- Story Comments
324
+ CREATE TABLE IF NOT EXISTS story_comments (
325
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
326
+ story_id INTEGER NOT NULL,
327
+ content TEXT NOT NULL,
328
+ created_by TEXT NOT NULL,
329
+ created_at TEXT DEFAULT (datetime('now')),
330
+ FOREIGN KEY (story_id) REFERENCES pm_stories(id)
331
+ );
@@ -0,0 +1,25 @@
1
+ -- Docs Module (Optional)
2
+ -- Enable via project.yaml: operations.pm_api.features.docs = true
3
+
4
+ CREATE TABLE IF NOT EXISTS docs (
5
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
6
+ title TEXT NOT NULL,
7
+ slug TEXT UNIQUE,
8
+ content TEXT,
9
+ doc_type TEXT DEFAULT 'general',
10
+ category TEXT,
11
+ author TEXT NOT NULL,
12
+ is_published INTEGER DEFAULT 0,
13
+ created_at TEXT DEFAULT (datetime('now')),
14
+ updated_at TEXT DEFAULT (datetime('now'))
15
+ );
16
+
17
+ CREATE TABLE IF NOT EXISTS doc_versions (
18
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
19
+ doc_id INTEGER NOT NULL,
20
+ content TEXT NOT NULL,
21
+ version INTEGER NOT NULL,
22
+ created_by TEXT NOT NULL,
23
+ created_at TEXT DEFAULT (datetime('now')),
24
+ FOREIGN KEY (doc_id) REFERENCES docs(id)
25
+ );
@@ -0,0 +1,17 @@
1
+ -- Meetings Module (Optional)
2
+ -- Enable via project.yaml: operations.pm_api.features.meetings = true
3
+
4
+ CREATE TABLE IF NOT EXISTS meetings (
5
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
6
+ sprint TEXT,
7
+ title TEXT NOT NULL,
8
+ meeting_type TEXT DEFAULT 'general',
9
+ meeting_date TEXT NOT NULL,
10
+ attendees TEXT,
11
+ agenda TEXT,
12
+ notes TEXT,
13
+ action_items TEXT,
14
+ created_by TEXT NOT NULL,
15
+ created_at TEXT DEFAULT (datetime('now')),
16
+ updated_at TEXT DEFAULT (datetime('now'))
17
+ );
@@ -0,0 +1,16 @@
1
+ -- Rewards Module (Optional)
2
+ -- Enable via project.yaml: operations.pm_api.features.rewards = true
3
+
4
+ CREATE TABLE IF NOT EXISTS rewards (
5
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
6
+ member_name TEXT NOT NULL,
7
+ type TEXT NOT NULL CHECK (type IN ('reward', 'penalty')),
8
+ amount REAL NOT NULL,
9
+ reason TEXT NOT NULL,
10
+ status TEXT DEFAULT 'pending' CHECK (status IN ('pending', 'paid', 'cancelled')),
11
+ issued_by TEXT NOT NULL,
12
+ tx_hash TEXT,
13
+ paid_at TEXT,
14
+ created_at TEXT DEFAULT (datetime('now')),
15
+ updated_at TEXT DEFAULT (datetime('now'))
16
+ );
@@ -0,0 +1,28 @@
1
+ import type { Context, Next } from 'hono'
2
+ import type { AppEnv } from './types.js'
3
+ import { query } from './db/adapter.js'
4
+
5
+ export async function authMiddleware(c: Context<AppEnv>, next: Next) {
6
+ const header = c.req.header('Authorization')
7
+ if (!header?.startsWith('Bearer ')) {
8
+ return c.json({ error: 'Missing or invalid Authorization header' }, 401)
9
+ }
10
+
11
+ const token = header.slice(7)
12
+ const result = await query<{ user_name: string }>(
13
+ `SELECT user_name FROM auth_tokens
14
+ WHERE token = ? AND is_active = 1
15
+ AND (expires_at IS NULL OR expires_at > datetime('now'))`,
16
+ [token],
17
+ )
18
+
19
+ if (result.error) {
20
+ return c.json({ error: 'Auth verification failed' }, 500)
21
+ }
22
+ if (result.rows.length === 0) {
23
+ return c.json({ error: 'Invalid or expired token' }, 401)
24
+ }
25
+
26
+ c.set('userName', result.rows[0].user_name)
27
+ await next()
28
+ }
@@ -0,0 +1,20 @@
1
+ /**
2
+ * Blockchain adapter interface — provider-agnostic balance query layer.
3
+ * Implementations: TronAdapter, (future: EthAdapter, SolanaAdapter)
4
+ */
5
+
6
+ export interface BlockchainAdapter {
7
+ getBalance(address: string): Promise<{ native: number; token: number }>
8
+ getTokenName(): string
9
+ getNativeName(): string
10
+ }
11
+
12
+ let _adapter: BlockchainAdapter | null = null
13
+
14
+ export function setBlockchainAdapter(adapter: BlockchainAdapter) {
15
+ _adapter = adapter
16
+ }
17
+
18
+ export function getBlockchainAdapter(): BlockchainAdapter | null {
19
+ return _adapter
20
+ }
@@ -0,0 +1,62 @@
1
+ import type { BlockchainAdapter } from './adapter.js'
2
+
3
+ const ALPHABET = '123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz'
4
+
5
+ function base58ToHex(addr: string): string {
6
+ let num = 0n
7
+ for (const c of addr) {
8
+ const idx = ALPHABET.indexOf(c)
9
+ if (idx < 0) throw new Error(`Invalid base58 char: ${c}`)
10
+ num = num * 58n + BigInt(idx)
11
+ }
12
+ const hex = num.toString(16).padStart(50, '0')
13
+ return hex.slice(0, 42)
14
+ }
15
+
16
+ export class TronAdapter implements BlockchainAdapter {
17
+ private contractAddress: string
18
+ private tokenName: string
19
+ private tokenDecimals: number
20
+
21
+ constructor(opts: { contractAddress: string; tokenName: string; tokenDecimals?: number }) {
22
+ this.contractAddress = opts.contractAddress
23
+ this.tokenName = opts.tokenName
24
+ this.tokenDecimals = opts.tokenDecimals ?? 8
25
+ }
26
+
27
+ getTokenName(): string { return this.tokenName }
28
+ getNativeName(): string { return 'TRX' }
29
+
30
+ async getBalance(address: string): Promise<{ native: number; token: number }> {
31
+ try {
32
+ const addrHex = base58ToHex(address)
33
+ const contractHex = base58ToHex(this.contractAddress)
34
+
35
+ const [trxRes, balanceRes] = await Promise.all([
36
+ fetch(`https://api.trongrid.io/v1/accounts/${address}`),
37
+ fetch('https://api.trongrid.io/wallet/triggerconstantcontract', {
38
+ method: 'POST',
39
+ headers: { 'Content-Type': 'application/json' },
40
+ body: JSON.stringify({
41
+ owner_address: addrHex,
42
+ contract_address: contractHex,
43
+ function_selector: 'balanceOf(address)',
44
+ parameter: addrHex.slice(2).padStart(64, '0'),
45
+ }),
46
+ }),
47
+ ])
48
+
49
+ const trxData = await trxRes.json() as any
50
+ const balanceData = await balanceRes.json() as any
51
+
52
+ const native = (trxData.data?.[0]?.balance ?? 0) / 1_000_000
53
+ const token = balanceData.constant_result?.[0]
54
+ ? parseInt(balanceData.constant_result[0], 16) / 10 ** this.tokenDecimals
55
+ : 0
56
+
57
+ return { native, token }
58
+ } catch {
59
+ return { native: 0, token: 0 }
60
+ }
61
+ }
62
+ }
@@ -0,0 +1,36 @@
1
+ /**
2
+ * Database adapter interface — provider-agnostic query layer.
3
+ * Implementations: TursoAdapter, (future: SupabaseAdapter, SqliteAdapter)
4
+ */
5
+
6
+ export type ArgValue = string | number | null
7
+
8
+ export interface DbAdapter {
9
+ query<T = Record<string, unknown>>(sql: string, args?: ArgValue[]): Promise<{ rows: T[]; error?: string }>
10
+ execute(sql: string, args?: ArgValue[]): Promise<{ rowsAffected: number; error?: string }>
11
+ }
12
+
13
+ let _adapter: DbAdapter | null = null
14
+
15
+ export function setAdapter(adapter: DbAdapter) {
16
+ _adapter = adapter
17
+ }
18
+
19
+ export function getAdapter(): DbAdapter {
20
+ if (!_adapter) throw new Error('DB adapter not initialized. Call setAdapter() first.')
21
+ return _adapter
22
+ }
23
+
24
+ export async function query<T = Record<string, unknown>>(
25
+ sql: string,
26
+ args: ArgValue[] = [],
27
+ ): Promise<{ rows: T[]; error?: string }> {
28
+ return getAdapter().query<T>(sql, args)
29
+ }
30
+
31
+ export async function execute(
32
+ sql: string,
33
+ args: ArgValue[] = [],
34
+ ): Promise<{ rowsAffected: number; error?: string }> {
35
+ return getAdapter().execute(sql, args)
36
+ }
@@ -0,0 +1,147 @@
1
+ /**
2
+ * Turso HTTP API v2 (/v2/pipeline) adapter
3
+ * Workers environment: setTursoEnv() injects bindings per request
4
+ */
5
+
6
+ import type { DbAdapter, ArgValue } from './adapter.js'
7
+
8
+ interface HranaValue {
9
+ type: 'text' | 'integer' | 'float' | 'null'
10
+ value?: string
11
+ }
12
+
13
+ function toHranaArg(v: ArgValue): HranaValue {
14
+ if (v === null) return { type: 'null' }
15
+ if (typeof v === 'number') {
16
+ return Number.isInteger(v)
17
+ ? { type: 'integer', value: String(v) }
18
+ : { type: 'float', value: String(v) }
19
+ }
20
+ return { type: 'text', value: String(v) }
21
+ }
22
+
23
+ interface PipelineResult {
24
+ results: Array<{
25
+ type: 'ok' | 'error'
26
+ response?: {
27
+ type: string
28
+ result?: {
29
+ cols: Array<{ name: string }>
30
+ rows: Array<Array<HranaValue>>
31
+ affected_row_count: number
32
+ last_insert_rowid: string | null
33
+ }
34
+ }
35
+ error?: { message: string }
36
+ }>
37
+ }
38
+
39
+ export class TursoAdapter implements DbAdapter {
40
+ private url: string
41
+ private token: string
42
+
43
+ constructor(url: string, token: string) {
44
+ this.url = url
45
+ this.token = token
46
+ }
47
+
48
+ async query<T = Record<string, unknown>>(
49
+ sql: string,
50
+ args: ArgValue[] = [],
51
+ ): Promise<{ rows: T[]; error?: string }> {
52
+ if (!this.url || !this.token) {
53
+ return { rows: [], error: 'Missing TURSO_URL or TURSO_AUTH_TOKEN' }
54
+ }
55
+
56
+ try {
57
+ const resp = await fetch(this.url + '/v2/pipeline', {
58
+ method: 'POST',
59
+ headers: {
60
+ Authorization: 'Bearer ' + this.token,
61
+ 'Content-Type': 'application/json',
62
+ },
63
+ body: JSON.stringify({
64
+ requests: [
65
+ { type: 'execute', stmt: { sql, args: args.map(toHranaArg) } },
66
+ { type: 'close' },
67
+ ],
68
+ }),
69
+ })
70
+
71
+ if (!resp.ok) {
72
+ const text = await resp.text().catch(() => '')
73
+ return { rows: [], error: `HTTP ${resp.status}: ${text}` }
74
+ }
75
+
76
+ const data: PipelineResult = await resp.json()
77
+ const first = data.results[0]
78
+
79
+ if (first.type === 'error') {
80
+ return { rows: [], error: first.error?.message ?? 'Unknown error' }
81
+ }
82
+
83
+ const result = first.response?.result
84
+ if (!result) return { rows: [] }
85
+
86
+ const colNames = result.cols.map(c => c.name)
87
+ const rows = result.rows.map(row => {
88
+ const obj: Record<string, unknown> = {}
89
+ colNames.forEach((col, i) => {
90
+ const cell = row[i]
91
+ if (cell.type === 'null') obj[col] = null
92
+ else if (cell.type === 'integer') obj[col] = Number(cell.value)
93
+ else if (cell.type === 'float') obj[col] = Number(cell.value)
94
+ else obj[col] = cell.value
95
+ })
96
+ return obj as T
97
+ })
98
+
99
+ return { rows }
100
+ } catch (err: unknown) {
101
+ const message = err instanceof Error ? err.message : 'Unknown error'
102
+ return { rows: [], error: message }
103
+ }
104
+ }
105
+
106
+ async execute(
107
+ sql: string,
108
+ args: ArgValue[] = [],
109
+ ): Promise<{ rowsAffected: number; error?: string }> {
110
+ if (!this.url || !this.token) {
111
+ return { rowsAffected: 0, error: 'Missing TURSO_URL or TURSO_AUTH_TOKEN' }
112
+ }
113
+
114
+ try {
115
+ const resp = await fetch(this.url + '/v2/pipeline', {
116
+ method: 'POST',
117
+ headers: {
118
+ Authorization: 'Bearer ' + this.token,
119
+ 'Content-Type': 'application/json',
120
+ },
121
+ body: JSON.stringify({
122
+ requests: [
123
+ { type: 'execute', stmt: { sql, args: args.map(toHranaArg) } },
124
+ { type: 'close' },
125
+ ],
126
+ }),
127
+ })
128
+
129
+ if (!resp.ok) {
130
+ const text = await resp.text().catch(() => '')
131
+ return { rowsAffected: 0, error: `HTTP ${resp.status}: ${text}` }
132
+ }
133
+
134
+ const data: PipelineResult = await resp.json()
135
+ const first = data.results[0]
136
+
137
+ if (first.type === 'error') {
138
+ return { rowsAffected: 0, error: first.error?.message ?? 'Unknown error' }
139
+ }
140
+
141
+ return { rowsAffected: first.response?.result?.affected_row_count ?? 0 }
142
+ } catch (err: unknown) {
143
+ const message = err instanceof Error ? err.message : 'Unknown error'
144
+ return { rowsAffected: 0, error: message }
145
+ }
146
+ }
147
+ }