@lovelybunch/api 1.0.75-alpha.9 → 1.0.75

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 (163) hide show
  1. package/dist/lib/jobs/job-runner.js +10 -2
  2. package/dist/lib/jobs/job-scheduler.js +21 -0
  3. package/dist/lib/mail/mail-runner.d.ts +51 -0
  4. package/dist/lib/mail/mail-runner.js +342 -0
  5. package/dist/lib/slack/slack-service.d.ts +2 -0
  6. package/dist/lib/slack/slack-service.js +3 -0
  7. package/dist/lib/storage/file-storage.d.ts +16 -16
  8. package/dist/lib/storage/file-storage.js +59 -64
  9. package/dist/lib/terminal/terminal-manager.d.ts +3 -3
  10. package/dist/lib/terminal/terminal-manager.js +10 -10
  11. package/dist/routes/api/v1/ai/route.js +39 -19
  12. package/dist/routes/api/v1/git/index.js +23 -0
  13. package/dist/routes/api/v1/mail/index.d.ts +3 -0
  14. package/dist/routes/api/v1/mail/index.js +23 -0
  15. package/dist/routes/api/v1/mail/route.d.ts +294 -0
  16. package/dist/routes/api/v1/mail/route.js +344 -0
  17. package/dist/routes/api/v1/mcp/index.js +32 -32
  18. package/dist/routes/api/v1/slack/index.d.ts +3 -0
  19. package/dist/routes/api/v1/slack/index.js +15 -0
  20. package/dist/routes/api/v1/slack/route.d.ts +124 -0
  21. package/dist/routes/api/v1/slack/route.js +192 -0
  22. package/dist/routes/api/v1/tasks/[id]/route.d.ts +117 -0
  23. package/dist/routes/api/v1/tasks/[id]/route.js +166 -0
  24. package/dist/routes/api/v1/tasks/index.d.ts +3 -0
  25. package/dist/routes/api/v1/tasks/index.js +10 -0
  26. package/dist/routes/api/v1/tasks/route.d.ts +96 -0
  27. package/dist/routes/api/v1/tasks/route.js +136 -0
  28. package/dist/routes/api/v1/terminal/[taskId]/create/index.d.ts +3 -0
  29. package/dist/routes/api/v1/terminal/[taskId]/create/index.js +5 -0
  30. package/dist/routes/api/v1/terminal/[taskId]/create/route.d.ts +10 -0
  31. package/dist/routes/api/v1/terminal/[taskId]/create/route.js +27 -0
  32. package/dist/routes/api/v1/terminal/[taskId]/destroy/index.d.ts +3 -0
  33. package/dist/routes/api/v1/terminal/[taskId]/destroy/index.js +5 -0
  34. package/dist/routes/api/v1/terminal/[taskId]/destroy/route.d.ts +10 -0
  35. package/dist/routes/api/v1/terminal/[taskId]/destroy/route.js +21 -0
  36. package/dist/routes/api/v1/terminal/[taskId]/resize/index.d.ts +3 -0
  37. package/dist/routes/api/v1/terminal/[taskId]/resize/index.js +5 -0
  38. package/dist/routes/api/v1/terminal/[taskId]/resize/route.d.ts +10 -0
  39. package/dist/routes/api/v1/terminal/[taskId]/resize/route.js +21 -0
  40. package/dist/routes/api/v1/terminal/sessions/route.js +4 -4
  41. package/dist/server-with-static.js +12 -8
  42. package/dist/server.js +12 -8
  43. package/package.json +4 -4
  44. package/static/assets/{ActivityPage-OxRci_V2.js → ActivityPage-k4I7Q53O.js} +1 -1
  45. package/static/assets/ApiKeysSettingsPage-B1YvVdmg.js +2 -0
  46. package/static/assets/{ArchitectureEditPage-D7xcH6dY.js → ArchitectureEditPage-CpowsIx2.js} +4 -4
  47. package/static/assets/{ArchitecturePage-pvnlX-NW.js → ArchitecturePage-DYxC_aMR.js} +1 -1
  48. package/static/assets/{AuthSettingsPage-Bu0CZ1rY.js → AuthSettingsPage-DtSo78Y_.js} +2 -2
  49. package/static/assets/{CallbackPage-D0lkjxCT.js → CallbackPage-bROCGapx.js} +1 -1
  50. package/static/assets/CodePage-CPCj64rX.js +2 -0
  51. package/static/assets/{CollapsibleSection-Bt_ZLnJc.js → CollapsibleSection-M5cXbl92.js} +1 -1
  52. package/static/assets/DashboardPage-B9BZZfw6.js +51 -0
  53. package/static/assets/{GitPage-TrTxZ27J.js → GitPage-BiDtdSK1.js} +2 -2
  54. package/static/assets/GitSettingsPage-THm6wDjs.js +6 -0
  55. package/static/assets/IdentityPage-BC16skg6.js +6 -0
  56. package/static/assets/{ImplementationStepsEditor-Ctx0CvbU.js → ImplementationStepsEditor-HliLQav5.js} +2 -2
  57. package/static/assets/{IntegrationsSettingsPage-C2wJVdM7.js → IntegrationsSettingsPage-CC_VKIQa.js} +1 -1
  58. package/static/assets/JobDetailPage-z1QQYvmU.js +1 -0
  59. package/static/assets/{KnowledgeDetailPage-BdTUfWqj.js → KnowledgeDetailPage-DzHXBS7Q.js} +1 -1
  60. package/static/assets/{KnowledgeEditPage-D8XK4IUf.js → KnowledgeEditPage-BwGnUH_m.js} +1 -1
  61. package/static/assets/KnowledgePage-CGIVMS02.js +3 -0
  62. package/static/assets/{LoginPage-Dqxd7cTa.js → LoginPage-VQ3lcfLV.js} +1 -1
  63. package/static/assets/MailInboxPage-DiZKqwdU.js +1 -0
  64. package/static/assets/MailProcessingModal-DIeSQBoR.js +6 -0
  65. package/static/assets/MailReadPage-C8AACmZQ.js +1 -0
  66. package/static/assets/MailSentPage-C_5yFly_.js +1 -0
  67. package/static/assets/{McpSettingsPage-10n35zXi.js → McpSettingsPage-i9YHcu1s.js} +1 -1
  68. package/static/assets/{NewKnowledgePage-BlJzzuh7.js → NewKnowledgePage-BnVY7WUD.js} +1 -1
  69. package/static/assets/{NewSkillPage-ByqN--mH.js → NewSkillPage-DwniHD6D.js} +1 -1
  70. package/static/assets/NewTaskPage-F5UX2WMc.js +90 -0
  71. package/static/assets/NotFoundPage-BbSZX_4L.js +6 -0
  72. package/static/assets/NotificationsSettingsPage-C8kjcift.js +1 -0
  73. package/static/assets/{ProjectEditPage-DKJTY2uc.js → ProjectEditPage-DUUlIEqI.js} +1 -1
  74. package/static/assets/{ProjectPage-2VblKCWz.js → ProjectPage-Unz9PQpA.js} +1 -1
  75. package/static/assets/{PromptsSettingsPage-B4mOhXuo.js → PromptsSettingsPage-DVpIuRKI.js} +1 -1
  76. package/static/assets/ResourceDetailPage-DqHZ2KYD.js +1 -0
  77. package/static/assets/{ResourcesPage-2BbjIWfF.js → ResourcesPage-BP5tuAi-.js} +1 -1
  78. package/static/assets/RoleEditPage-BgKu8S0-.js +13 -0
  79. package/static/assets/{RolePage-qXWXZ2FZ.js → RolePage-Fed52Ov5.js} +1 -1
  80. package/static/assets/{RulesSettingsPage-BtM7p8F6.js → RulesSettingsPage-BQ2O0u66.js} +3 -3
  81. package/static/assets/SchedulePage-jkxjuzBx.js +4 -0
  82. package/static/assets/SkillDetailPage-k3Q2-NFd.js +1 -0
  83. package/static/assets/{SkillEditPage-Czlo8WWT.js → SkillEditPage-urF4snjo.js} +1 -1
  84. package/static/assets/SkillsPage-DlWDhEjR.js +8 -0
  85. package/static/assets/{SkillsSettingsPage-DKtpy7qk.js → SkillsSettingsPage-BViFgckG.js} +1 -1
  86. package/static/assets/{SourceInput-BITn1Y15.js → SourceInput-CAFKTHw-.js} +1 -1
  87. package/static/assets/{TagInput-BK91_M1N.js → TagInput-C6lI-ePr.js} +1 -1
  88. package/static/assets/TaskDetailPage-DpbRHnW_.js +16 -0
  89. package/static/assets/TaskEditPage-DssRbW0h.js +1 -0
  90. package/static/assets/TasksPage-CD_eo0Bj.js +17 -0
  91. package/static/assets/TerminalPage-BG_wlccr.js +1 -0
  92. package/static/assets/TerminalSessionPage-CsK-LznK.js +8 -0
  93. package/static/assets/{UserPreferencesPage-DrgYEcxO.js → UserPreferencesPage-CWUq3efu.js} +1 -1
  94. package/static/assets/UserSettingsPage-CduI_MGS.js +1 -0
  95. package/static/assets/{UtilitiesPage-Djr4qT5L.js → UtilitiesPage-BAxokhLh.js} +1 -1
  96. package/static/assets/{alert-CsMvyYoX.js → alert-BXsc6_qu.js} +1 -1
  97. package/static/assets/{arrow-down-BZnfbld8.js → arrow-down-DmW_3gE8.js} +1 -1
  98. package/static/assets/{arrow-left-WGBYWq3h.js → arrow-left-1S-835kP.js} +1 -1
  99. package/static/assets/{arrow-up-BByVUPE7.js → arrow-up-BYism_o1.js} +1 -1
  100. package/static/assets/arrow-up-down-Dw3J0a4i.js +6 -0
  101. package/static/assets/{badge-AwLOflf5.js → badge-BUEY53dV.js} +1 -1
  102. package/static/assets/{browser-modal-BzGNFfTG.js → browser-modal-DCNdI4NT.js} +2 -2
  103. package/static/assets/{card-SN5gKnu7.js → card-BcPlIAH5.js} +1 -1
  104. package/static/assets/{chevron-left-C7uNq9l_.js → chevron-left-FMmNe7yP.js} +1 -1
  105. package/static/assets/{chevron-up-CHdIiLxL.js → chevron-up-CqM3won3.js} +1 -1
  106. package/static/assets/{chevrons-up-TXwQuoUN.js → chevrons-up-DTvCkIHc.js} +1 -1
  107. package/static/assets/{circle-alert-37E5gU9K.js → circle-alert-dseM-Ib7.js} +1 -1
  108. package/static/assets/{circle-check-big-nY4PntB5.js → circle-check-big-jKg34xC-.js} +1 -1
  109. package/static/assets/{circle-check-D02pWDME.js → circle-check-eyo6pBP1.js} +1 -1
  110. package/static/assets/{circle-play-7EXFLo4F.js → circle-play-BrY_lNiH.js} +1 -1
  111. package/static/assets/{circle-x-By4JoTHB.js → circle-x-uqmzEce1.js} +1 -1
  112. package/static/assets/{clipboard-BdymjxLO.js → clipboard-tzPFoieb.js} +1 -1
  113. package/static/assets/{clock-HDu44KTo.js → clock-Bjc06QBM.js} +1 -1
  114. package/static/assets/code-DrYqPukx.js +6 -0
  115. package/static/assets/{download-Cv2G2Eg9.js → download-Bg__QCLT.js} +1 -1
  116. package/static/assets/{external-link-DwMXcCCj.js → external-link-CNDy2UUo.js} +1 -1
  117. package/static/assets/{eye-DYnjJzdb.js → eye-DLFBnC8t.js} +1 -1
  118. package/static/assets/{folder-git-2-COeWFPHS.js → folder-git-2-DUqd0WRi.js} +1 -1
  119. package/static/assets/index-CHdBxVyk.css +2 -0
  120. package/static/assets/{index-9Tv-j_Ga.js → index-DFcWlnzl.js} +118 -103
  121. package/static/assets/{info-BmtuPMhv.js → info-D6jxZC5X.js} +1 -1
  122. package/static/assets/kiro-CX1mOsRO.js +17 -0
  123. package/static/assets/{label-TGqbNfMO.js → label-DBuh-ke5.js} +1 -1
  124. package/static/assets/{markdown-editor-ls1JPK_e.js → markdown-editor-B4YNQFT2.js} +1 -1
  125. package/static/assets/message-square-B5RWz_ff.js +6 -0
  126. package/static/assets/paperclip-4A_3MaPx.js +6 -0
  127. package/static/assets/{pause-CAWbvTiL.js → pause-BzhKXHtR.js} +1 -1
  128. package/static/assets/{play-DF_Qeu0H.js → play-CHIf-Rcz.js} +1 -1
  129. package/static/assets/{radio-group-DYTbywtK.js → radio-group-C1ct-VsJ.js} +1 -1
  130. package/static/assets/{refresh-cw-BFZxHqbC.js → refresh-cw-B3OwrDUf.js} +1 -1
  131. package/static/assets/{search-Dr90tbch.js → search-Cq1ksEdp.js} +1 -1
  132. package/static/assets/{select-Cs5qtMYV.js → select-44mcS2_G.js} +1 -1
  133. package/static/assets/{status-utils-BDOyevaX.js → status-utils-CDkPeVfP.js} +1 -1
  134. package/static/assets/{switch-4TDb6YiQ.js → switch-CIwjYvCt.js} +1 -1
  135. package/static/assets/{tabs-BrbEvF4V.js → tabs-DTV6Su-h.js} +1 -1
  136. package/static/assets/{tag-DrQkepeD.js → tag-p6yeowCW.js} +1 -1
  137. package/static/assets/{terminal-preview-uuKF9_x4.js → terminal-preview-DN38x9Jm.js} +1 -1
  138. package/static/assets/use-terminal-BXJqOeJe.js +1 -0
  139. package/static/assets/{video-DYA2WfbA.js → video-BH5ChaoS.js} +1 -1
  140. package/static/index.html +2 -2
  141. package/static/assets/ApiKeysSettingsPage-C0evI19e.js +0 -2
  142. package/static/assets/CodePage-BJ4PC5nb.js +0 -2
  143. package/static/assets/DashboardPage-BiffPdmj.js +0 -41
  144. package/static/assets/GitSettingsPage-D7q5xQd_.js +0 -6
  145. package/static/assets/IdentityPage-CY0Ak2j0.js +0 -11
  146. package/static/assets/JobDetailPage-Phx_IlKX.js +0 -1
  147. package/static/assets/KnowledgePage-Ci9G7Br-.js +0 -8
  148. package/static/assets/NewProposalPage-BP7Ttoxk.js +0 -90
  149. package/static/assets/ProposalDetailPage-m3ysyzpj.js +0 -1
  150. package/static/assets/ProposalEditPage-3XVg_paW.js +0 -1
  151. package/static/assets/ProposalsPage-B3u0aFFz.js +0 -17
  152. package/static/assets/ResourceDetailPage-somBLUpC.js +0 -1
  153. package/static/assets/RoleEditPage-CLzX7Xhi.js +0 -13
  154. package/static/assets/SchedulePage-4tFcIBSs.js +0 -4
  155. package/static/assets/SkillDetailPage-CroSdaju.js +0 -1
  156. package/static/assets/SkillsPage-CgULbcI-.js +0 -8
  157. package/static/assets/TerminalPage-8fwvnOo2.js +0 -1
  158. package/static/assets/TerminalSessionPage-BhO5U48p.js +0 -13
  159. package/static/assets/UserSettingsPage-Dj6lKLi8.js +0 -1
  160. package/static/assets/droid-CPteN3f9.js +0 -17
  161. package/static/assets/index-GFQ5RqVh.css +0 -2
  162. package/static/assets/use-terminal-BG5UXuVE.js +0 -1
  163. package/static/assets/zap-h9QOsasv.js +0 -6
@@ -49,44 +49,45 @@ export class FileStorageAdapter {
49
49
  }
50
50
  }
51
51
  async ensureDirectories() {
52
- const dirs = ['proposals', 'specs', 'flags', 'experiments', 'templates', 'jobs'];
52
+ const dirs = ['tasks', 'specs', 'flags', 'experiments', 'templates', 'jobs'];
53
53
  for (const dir of dirs) {
54
54
  await fs.mkdir(path.join(this.basePath, dir), { recursive: true });
55
55
  }
56
56
  }
57
- async createCP(cp) {
57
+ async createTask(task) {
58
58
  await this.ensureDirectories();
59
- // Extract content from the proposal if it exists
60
- const { content, ...frontmatter } = cp;
59
+ // Extract content from the task if it exists
60
+ const { content, ...frontmatter } = task;
61
61
  // Normalize date fields to Date instances in case callers provide ISO strings
62
- const createdAt = cp.metadata.createdAt instanceof Date
63
- ? cp.metadata.createdAt
64
- : new Date(cp.metadata.createdAt);
65
- const updatedAt = cp.metadata.updatedAt instanceof Date
66
- ? cp.metadata.updatedAt
67
- : new Date(cp.metadata.updatedAt);
68
- // Convert the proposal to markdown with YAML frontmatter
62
+ const createdAt = task.metadata.createdAt instanceof Date
63
+ ? task.metadata.createdAt
64
+ : new Date(task.metadata.createdAt);
65
+ const updatedAt = task.metadata.updatedAt instanceof Date
66
+ ? task.metadata.updatedAt
67
+ : new Date(task.metadata.updatedAt);
68
+ // Convert the task to markdown with YAML frontmatter
69
69
  const frontmatterData = this.sanitizeForYAML({
70
70
  // Required fields
71
- id: cp.id,
72
- title: cp.title,
71
+ id: task.id,
72
+ title: task.title,
73
73
  // Deprecated: intent is kept for backward compatibility during migration
74
74
  // TODO: Remove intent field in future version
75
- ...(cp.intent && { intent: cp.intent }),
75
+ ...(task.intent && { intent: task.intent }),
76
76
  createdAt: createdAt.toISOString(),
77
77
  updatedAt: updatedAt.toISOString(),
78
- status: cp.status,
79
- priority: cp.metadata.priority || 'medium',
78
+ status: task.status,
79
+ priority: task.metadata.priority || 'medium',
80
+ ...(task.metadata.readiness != null && { readiness: task.metadata.readiness }),
80
81
  // Author information
81
82
  author: {
82
- id: cp.author.id,
83
- name: cp.author.name,
84
- email: cp.author.email || '',
83
+ id: task.author.id,
84
+ name: task.author.name,
85
+ email: task.author.email || '',
85
86
  role: 'engineer', // Default role
86
- type: cp.author.type
87
+ type: task.author.type
87
88
  },
88
89
  // Steps (top-level for consistency with CLI storage format)
89
- steps: cp.planSteps.map(step => ({
90
+ steps: task.planSteps.map(step => ({
90
91
  id: step.id,
91
92
  description: step.description,
92
93
  status: step.status,
@@ -96,10 +97,10 @@ export class FileStorageAdapter {
96
97
  ...(step.executedAt && { executedAt: step.executedAt instanceof Date ? step.executedAt.toISOString() : step.executedAt }),
97
98
  })),
98
99
  // Metadata
99
- tags: cp.metadata.tags || [],
100
+ tags: task.metadata.tags || [],
100
101
  labels: [],
101
102
  // Comments use consistent field names: id, author, content, createdAt
102
- comments: (cp.comments || [])
103
+ comments: (task.comments || [])
103
104
  .filter((c) => c !== undefined)
104
105
  .map((c) => ({
105
106
  id: c.id,
@@ -108,17 +109,17 @@ export class FileStorageAdapter {
108
109
  createdAt: c.createdAt || c.timestamp || new Date().toISOString(),
109
110
  }))
110
111
  });
111
- const markdown = matter.stringify(content || this.getDefaultContent(cp), frontmatterData);
112
- const filePath = path.join(this.basePath, 'proposals', `${cp.id}.md`);
112
+ const markdown = matter.stringify(content || this.getDefaultContent(task), frontmatterData);
113
+ const filePath = path.join(this.basePath, 'tasks', `${task.id}.md`);
113
114
  await fs.writeFile(filePath, markdown, 'utf-8');
114
115
  }
115
- async getCP(id) {
116
+ async getTask(id) {
116
117
  try {
117
- const filePath = path.join(this.basePath, 'proposals', `${id}.md`);
118
+ const filePath = path.join(this.basePath, 'tasks', `${id}.md`);
118
119
  const fileContent = await fs.readFile(filePath, 'utf-8');
119
120
  const { data, content } = matter(fileContent);
120
- // Convert the frontmatter back to a ChangeProposal
121
- return this.frontmatterToCP(data, content);
121
+ // Convert the frontmatter back to a Task
122
+ return this.frontmatterToTask(data, content);
122
123
  }
123
124
  catch (error) {
124
125
  if (error.code === 'ENOENT')
@@ -126,13 +127,13 @@ export class FileStorageAdapter {
126
127
  throw error;
127
128
  }
128
129
  }
129
- async updateCP(id, updates) {
130
- const existing = await this.getCP(id);
130
+ async updateTask(id, updates) {
131
+ const existing = await this.getTask(id);
131
132
  if (!existing)
132
- throw new Error(`CP ${id} not found`);
133
- // Never allow id to be updated to prevent overwriting other proposals
133
+ throw new Error(`Task ${id} not found`);
134
+ // Never allow id to be updated to prevent overwriting other tasks
134
135
  const { id: _, ...safeUpdates } = updates;
135
- // Comments live at root level on ChangeProposal (not in metadata).
136
+ // Comments live at root level on Task (not in metadata).
136
137
  // Accept comments from root level or metadata.comments for backward compat.
137
138
  let commentsToStore = existing.comments || [];
138
139
  if (safeUpdates.comments) {
@@ -151,36 +152,36 @@ export class FileStorageAdapter {
151
152
  },
152
153
  comments: commentsToStore
153
154
  };
154
- await this.createCP(updated);
155
+ await this.createTask(updated);
155
156
  }
156
- async deleteCP(id) {
157
- const filePath = path.join(this.basePath, 'proposals', `${id}.md`);
157
+ async deleteTask(id) {
158
+ const filePath = path.join(this.basePath, 'tasks', `${id}.md`);
158
159
  await fs.unlink(filePath);
159
160
  }
160
- async listCPs(filter) {
161
- const proposalsDir = path.join(this.basePath, 'proposals');
161
+ async listTasks(filter) {
162
+ const tasksDir = path.join(this.basePath, 'tasks');
162
163
  try {
163
- const files = await fs.readdir(proposalsDir);
164
- const proposals = await Promise.all(files
164
+ const files = await fs.readdir(tasksDir);
165
+ const tasks = await Promise.all(files
165
166
  .filter(f => f.endsWith('.md'))
166
167
  .map(async (file) => {
167
- const content = await fs.readFile(path.join(proposalsDir, file), 'utf-8');
168
+ const content = await fs.readFile(path.join(tasksDir, file), 'utf-8');
168
169
  const { data, content: body } = matter(content);
169
- return this.frontmatterToCP(data, body);
170
+ return this.frontmatterToTask(data, body);
170
171
  }));
171
172
  // Apply filters
172
- let filtered = proposals.filter(cp => {
173
- if (!cp)
173
+ let filtered = tasks.filter(task => {
174
+ if (!task)
174
175
  return false;
175
- if (filter?.status && cp.status !== filter.status)
176
+ if (filter?.status && task.status !== filter.status)
176
177
  return false;
177
- if (filter?.author && cp.author.id !== filter.author)
178
+ if (filter?.author && task.author.id !== filter.author)
178
179
  return false;
179
- if (filter?.priority && cp.metadata.priority !== filter.priority)
180
+ if (filter?.priority && task.metadata.priority !== filter.priority)
180
181
  return false;
181
182
  if (filter?.tags && filter.tags.length > 0) {
182
- const cpTags = cp.metadata.tags || [];
183
- if (!filter.tags.some(tag => cpTags.includes(tag)))
183
+ const taskTags = task.metadata.tags || [];
184
+ if (!filter.tags.some(tag => taskTags.includes(tag)))
184
185
  return false;
185
186
  }
186
187
  return true;
@@ -208,9 +209,9 @@ export class FileStorageAdapter {
208
209
  throw error;
209
210
  }
210
211
  }
211
- async searchCPs(query) {
212
- const allCPs = await this.listCPs();
213
- const fuse = new Fuse(allCPs, {
212
+ async searchTasks(query) {
213
+ const allTasks = await this.listTasks();
214
+ const fuse = new Fuse(allTasks, {
214
215
  keys: [
215
216
  { name: 'title', weight: 2 },
216
217
  { name: 'intent', weight: 2 }, // Deprecated fallback
@@ -223,7 +224,7 @@ export class FileStorageAdapter {
223
224
  });
224
225
  return fuse.search(query).map(result => result.item);
225
226
  }
226
- frontmatterToCP(data, content) {
227
+ frontmatterToTask(data, content) {
227
228
  // Handle both old format (plan.steps) and new format (top-level steps)
228
229
  const steps = data.steps || data.plan?.steps || [];
229
230
  // Normalize comments using consistent field names (content/createdAt).
@@ -258,30 +259,24 @@ export class FileStorageAdapter {
258
259
  error: step.error,
259
260
  executedAt: step.executedAt ? new Date(step.executedAt) : undefined
260
261
  })),
261
- evidence: data.evidence || [],
262
- policies: data.policies || [],
263
- featureFlags: data.featureFlags || [],
264
- experiments: data.experiments || [],
265
- telemetryContracts: data.telemetryContracts || [],
266
- releasePlan: data.releasePlan || { strategy: 'immediate' },
267
262
  status: data.status || 'draft',
268
263
  comments: normalizedComments,
269
264
  metadata: {
270
265
  createdAt: new Date(data.createdAt || Date.now()),
271
266
  updatedAt: new Date(data.updatedAt || Date.now()),
272
267
  reviewers: data.reviewers || [],
273
- aiInteractions: data.aiInteractions || [],
274
268
  tags: data.tags || [],
275
269
  priority: data.priority || 'medium',
270
+ ...(data.readiness != null && { readiness: data.readiness }),
276
271
  },
277
272
  content // Store the markdown content
278
273
  };
279
274
  }
280
- getDefaultContent(cp) {
281
- return `# ${cp.title}
275
+ getDefaultContent(task) {
276
+ return `# ${task.title}
282
277
 
283
278
  ## Problem Statement
284
- Describe the problem this change proposal addresses.
279
+ Describe the problem this task addresses.
285
280
 
286
281
  ## Proposed Solution
287
282
  Describe the solution you're proposing.
@@ -2,7 +2,7 @@ import { WebSocket } from 'ws';
2
2
  import { ShellType } from './shell-utils.js';
3
3
  export interface TerminalSession {
4
4
  id: string;
5
- proposalId: string;
5
+ taskId: string;
6
6
  pty: any;
7
7
  websocket?: WebSocket;
8
8
  createdAt: Date;
@@ -19,9 +19,9 @@ export declare class TerminalManager {
19
19
  private cleanupInterval;
20
20
  private static readonly MAX_BACKLOG_BYTES;
21
21
  constructor();
22
- createSession(proposalId: string, shellPreference?: ShellType, startupCommand?: string): Promise<TerminalSession>;
22
+ createSession(taskId: string, shellPreference?: ShellType, startupCommand?: string): Promise<TerminalSession>;
23
23
  getSession(sessionId: string): TerminalSession | undefined;
24
- getSessionsByProposal(proposalId: string): TerminalSession[];
24
+ getSessionsByTask(taskId: string): TerminalSession[];
25
25
  attachWebSocket(sessionId: string, ws: WebSocket): boolean;
26
26
  destroySession(sessionId: string): boolean;
27
27
  resizeSession(sessionId: string, cols: number, rows: number): boolean;
@@ -16,8 +16,8 @@ export class TerminalManager {
16
16
  this.cleanupInactiveSessions();
17
17
  }, 5 * 60 * 1000);
18
18
  }
19
- async createSession(proposalId, shellPreference = 'bash', startupCommand) {
20
- const sessionId = `${proposalId}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
19
+ async createSession(taskId, shellPreference = 'bash', startupCommand) {
20
+ const sessionId = `${taskId}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
21
21
  // Get the project root directory
22
22
  let projectRoot;
23
23
  if (process.env.NODE_ENV === 'development' && process.env.GAIT_DEV_ROOT) {
@@ -32,8 +32,8 @@ export class TerminalManager {
32
32
  }
33
33
  // Prepare context information
34
34
  const contextInfo = {
35
- proposalId,
36
- proposalPath: path.join(projectRoot, '.nut', 'proposals', `${proposalId}.md`),
35
+ proposalId: taskId,
36
+ proposalPath: path.join(projectRoot, '.nut', 'tasks', `${taskId}.md`),
37
37
  contextPath: path.join(projectRoot, '.nut', 'context'),
38
38
  projectRoot,
39
39
  };
@@ -76,9 +76,9 @@ export class TerminalManager {
76
76
  delete injected.NODE_ENV;
77
77
  const env = {
78
78
  ...injected,
79
- COCONUT_PROPOSAL_ID: proposalId,
79
+ COCONUT_TASK_ID: taskId,
80
80
  COCONUT_CONTEXT_PATH: path.join(projectRoot, '.nut', 'context'),
81
- COCONUT_PROPOSAL_PATH: path.join(projectRoot, '.nut', 'proposals', `${proposalId}.md`),
81
+ COCONUT_TASK_PATH: path.join(projectRoot, '.nut', 'tasks', `${taskId}.md`),
82
82
  GAIT_DATA_PATH: projectRoot,
83
83
  TERM: 'xterm-256color',
84
84
  COLORTERM: 'truecolor',
@@ -100,7 +100,7 @@ export class TerminalManager {
100
100
  });
101
101
  const session = {
102
102
  id: sessionId,
103
- proposalId,
103
+ taskId,
104
104
  pty: ptyProcess,
105
105
  createdAt: new Date(),
106
106
  lastActivity: new Date(),
@@ -122,7 +122,7 @@ export class TerminalManager {
122
122
  tags: ["agent", "terminal", "session"],
123
123
  payload: {
124
124
  sessionId,
125
- proposalId,
125
+ taskId,
126
126
  cwd: projectRoot,
127
127
  shell: shellPath,
128
128
  }
@@ -209,8 +209,8 @@ export class TerminalManager {
209
209
  getSession(sessionId) {
210
210
  return this.sessions.get(sessionId);
211
211
  }
212
- getSessionsByProposal(proposalId) {
213
- return Array.from(this.sessions.values()).filter(session => session.proposalId === proposalId);
212
+ getSessionsByTask(taskId) {
213
+ return Array.from(this.sessions.values()).filter(session => session.taskId === taskId);
214
214
  }
215
215
  attachWebSocket(sessionId, ws) {
216
216
  const session = this.sessions.get(sessionId);
@@ -6,8 +6,8 @@ import readline from 'readline';
6
6
  import { ZodError } from 'zod';
7
7
  import { streamText, tool, jsonSchema, stepCountIs } from 'ai';
8
8
  import { createAnthropic } from '@ai-sdk/anthropic';
9
- import { getLogsDir, listProposals, getProposal, createProposal, updateProposal, deleteProposal, } from '@lovelybunch/core';
10
- import { proposalsFullTool, knowledgeTool, normalizeKnowledgeMetadata, eventsTool, projectContextTool, architectureContextTool, roleContextTool, resourcesTool } from '@lovelybunch/mcp';
9
+ import { getLogsDir, listTasks, getTask, createTask, updateTask, deleteTask, } from '@lovelybunch/core';
10
+ import { tasksFullTool, knowledgeTool, normalizeKnowledgeMetadata, eventsTool, projectContextTool, architectureContextTool, roleContextTool, resourcesTool } from '@lovelybunch/mcp';
11
11
  import matter from 'gray-matter';
12
12
  import Fuse from 'fuse.js';
13
13
  import { FileStorageAdapter } from '../../../../lib/storage/file-storage.js';
@@ -96,11 +96,11 @@ export async function POST(c) {
96
96
  // Using tool<any, string>() to properly type jsonSchema-based tools
97
97
  const storage = new FileStorageAdapter();
98
98
  const aiTools = {
99
- change_proposals: tool({
100
- description: proposalsFullTool.description,
101
- inputSchema: jsonSchema(proposalsFullTool.parameters),
99
+ tasks: tool({
100
+ description: tasksFullTool.description,
101
+ inputSchema: jsonSchema(tasksFullTool.parameters),
102
102
  execute: async (args) => {
103
- const result = await executeProposalsToolDirect(args, storage);
103
+ const result = await executeTasksToolDirect(args, storage);
104
104
  return JSON.stringify(result);
105
105
  },
106
106
  }),
@@ -183,24 +183,44 @@ export async function POST(c) {
183
183
  // These are unchanged from the previous implementation. They handle the actual
184
184
  // business logic for each tool, called automatically by the AI SDK when the
185
185
  // LLM makes tool calls.
186
- async function executeProposalsToolDirect(args, _storage) {
187
- const { operation, id, filters, proposal, updates } = args;
186
+ async function executeTasksToolDirect(args, _storage) {
187
+ const { operation, id, filters, task, updates } = args;
188
188
  try {
189
189
  switch (operation) {
190
190
  case 'list': {
191
- const proposals = await listProposals(filters || {});
191
+ const DEFAULT_LIST_LIMIT = 20;
192
+ const requestedLimit = filters?.limit;
193
+ const effectiveLimit = requestedLimit ?? DEFAULT_LIST_LIMIT;
194
+ const tasks = await listTasks(filters || {});
195
+ const totalCount = tasks.length;
196
+ const limited = tasks.slice(0, effectiveLimit);
197
+ // Return lightweight summaries to keep token usage manageable.
198
+ // The AI can use "get" with a specific task ID to retrieve full details.
199
+ const summaries = limited.map((t) => ({
200
+ id: t.id,
201
+ title: t.title,
202
+ status: t.status,
203
+ priority: t.metadata?.priority,
204
+ tags: t.metadata?.tags,
205
+ author: t.author ? { name: t.author.name, type: t.author.type } : undefined,
206
+ createdAt: t.metadata?.createdAt,
207
+ updatedAt: t.metadata?.updatedAt,
208
+ }));
192
209
  return {
193
210
  success: true,
194
- data: proposals,
195
- count: proposals.length,
196
- message: `Found ${proposals.length} tasks`
211
+ data: summaries,
212
+ count: summaries.length,
213
+ totalCount,
214
+ message: totalCount > effectiveLimit
215
+ ? `Showing ${summaries.length} of ${totalCount} tasks (limit: ${effectiveLimit}). Use filters or increase limit to see more. Use "get" with a task ID for full details.`
216
+ : `Found ${totalCount} tasks. Use "get" with a task ID for full details.`
197
217
  };
198
218
  }
199
219
  case 'get': {
200
220
  if (!id) {
201
221
  return { success: false, error: 'Task ID is required for get operation' };
202
222
  }
203
- const result = await getProposal(id);
223
+ const result = await getTask(id);
204
224
  if (!result) {
205
225
  return { success: false, error: 'Task not found' };
206
226
  }
@@ -211,10 +231,10 @@ async function executeProposalsToolDirect(args, _storage) {
211
231
  };
212
232
  }
213
233
  case 'create': {
214
- if (!proposal) {
234
+ if (!task) {
215
235
  return { success: false, error: 'Task data is required for create operation' };
216
236
  }
217
- const created = await createProposal(proposal);
237
+ const created = await createTask(task);
218
238
  return {
219
239
  success: true,
220
240
  data: created,
@@ -225,11 +245,11 @@ async function executeProposalsToolDirect(args, _storage) {
225
245
  if (!id) {
226
246
  return { success: false, error: 'Task ID is required for update operation' };
227
247
  }
228
- const updateData = updates || proposal;
248
+ const updateData = updates || task;
229
249
  if (!updateData) {
230
250
  return { success: false, error: 'Update data is required for update operation' };
231
251
  }
232
- const updated = await updateProposal(id, updateData);
252
+ const updated = await updateTask(id, updateData);
233
253
  return {
234
254
  success: true,
235
255
  data: updated,
@@ -240,7 +260,7 @@ async function executeProposalsToolDirect(args, _storage) {
240
260
  if (!id) {
241
261
  return { success: false, error: 'Task ID is required for delete operation' };
242
262
  }
243
- const deleted = await deleteProposal(id);
263
+ const deleted = await deleteTask(id);
244
264
  if (!deleted) {
245
265
  return { success: false, error: 'Task not found' };
246
266
  }
@@ -264,7 +284,7 @@ async function executeProposalsToolDirect(args, _storage) {
264
284
  }))
265
285
  };
266
286
  }
267
- console.error('Error executing proposals tool:', error);
287
+ console.error('Error executing tasks tool:', error);
268
288
  return { success: false, error: error.message || 'Tool execution failed' };
269
289
  }
270
290
  }
@@ -331,6 +331,22 @@ app.post('/branches/:branch/merge', async (c) => {
331
331
  catch { }
332
332
  const mergeStrategy = strategy?.strategy === 'squash' || strategy?.strategy === 'rebase' ? strategy.strategy : 'merge';
333
333
  const result = await mergeBranch(name, mergeStrategy);
334
+ // Determine target branch for notification
335
+ try {
336
+ const { runGit } = await import('../../../../lib/git.js');
337
+ const { stdout: branchOutput } = await runGit(['branch', '--show-current']);
338
+ const targetBranch = branchOutput.trim();
339
+ // Send Slack notification (non-blocking)
340
+ import('../../../../lib/slack/slack-service.js').then(({ getSlackService }) => {
341
+ getSlackService().sendNotification({
342
+ type: 'git.merge',
343
+ branch: name,
344
+ targetBranch,
345
+ message: `Merged ${name} into ${targetBranch} using ${mergeStrategy}`,
346
+ }).catch(err => console.warn('[git] Slack notification failed:', err));
347
+ }).catch(() => { });
348
+ }
349
+ catch { }
334
350
  return c.json({ success: true, data: { branch: name, strategy: mergeStrategy, result } });
335
351
  }
336
352
  catch (e) {
@@ -466,6 +482,13 @@ app.post('/push', async (c) => {
466
482
  remote: remoteName,
467
483
  }
468
484
  });
485
+ // Send Slack notification (non-blocking)
486
+ import('../../../../lib/slack/slack-service.js').then(({ getSlackService }) => {
487
+ getSlackService().sendNotification({
488
+ type: 'git.push',
489
+ branch: currentBranch,
490
+ }).catch(err => console.warn('[git] Slack notification failed:', err));
491
+ }).catch(() => { });
469
492
  }
470
493
  catch (logError) {
471
494
  console.error('Error logging push:', logError);
@@ -0,0 +1,3 @@
1
+ import { Hono } from 'hono';
2
+ declare const mailRoutes: Hono<import("hono/types").BlankEnv, import("hono/types").BlankSchema, "/">;
3
+ export { mailRoutes };
@@ -0,0 +1,23 @@
1
+ import { Hono } from 'hono';
2
+ import { listMailHandler, getMailHandler, setMailStatusHandler, setMailActionHandler, replyMailHandler, sendMailHandler, deleteMailHandler, inboundWebhookHandler, getMailProcessingHandler, stopMailProcessingHandler, } from './route.js';
3
+ const mailRoutes = new Hono();
4
+ // Resend inbound webhook
5
+ mailRoutes.post('/inbound', inboundWebhookHandler);
6
+ // Send email (coming soon)
7
+ mailRoutes.post('/send', sendMailHandler);
8
+ // Processing status and control
9
+ mailRoutes.get('/:id/processing', getMailProcessingHandler);
10
+ mailRoutes.post('/:id/processing/stop', stopMailProcessingHandler);
11
+ // Set email status (read/unread)
12
+ mailRoutes.put('/:id/status', setMailStatusHandler);
13
+ // Set agent action summary
14
+ mailRoutes.put('/:id/action', setMailActionHandler);
15
+ // Reply to email
16
+ mailRoutes.post('/:id/reply', replyMailHandler);
17
+ // List emails in folder
18
+ mailRoutes.get('/:folder', listMailHandler);
19
+ // Get specific email in folder
20
+ mailRoutes.get('/:folder/:id', getMailHandler);
21
+ // Delete email from folder
22
+ mailRoutes.delete('/:folder/:id', deleteMailHandler);
23
+ export { mailRoutes };