@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.
- package/dist/lib/jobs/job-runner.js +10 -2
- package/dist/lib/jobs/job-scheduler.js +21 -0
- package/dist/lib/mail/mail-runner.d.ts +51 -0
- package/dist/lib/mail/mail-runner.js +342 -0
- package/dist/lib/slack/slack-service.d.ts +2 -0
- package/dist/lib/slack/slack-service.js +3 -0
- package/dist/lib/storage/file-storage.d.ts +16 -16
- package/dist/lib/storage/file-storage.js +59 -64
- package/dist/lib/terminal/terminal-manager.d.ts +3 -3
- package/dist/lib/terminal/terminal-manager.js +10 -10
- package/dist/routes/api/v1/ai/route.js +39 -19
- package/dist/routes/api/v1/git/index.js +23 -0
- package/dist/routes/api/v1/mail/index.d.ts +3 -0
- package/dist/routes/api/v1/mail/index.js +23 -0
- package/dist/routes/api/v1/mail/route.d.ts +294 -0
- package/dist/routes/api/v1/mail/route.js +344 -0
- package/dist/routes/api/v1/mcp/index.js +32 -32
- package/dist/routes/api/v1/slack/index.d.ts +3 -0
- package/dist/routes/api/v1/slack/index.js +15 -0
- package/dist/routes/api/v1/slack/route.d.ts +124 -0
- package/dist/routes/api/v1/slack/route.js +192 -0
- package/dist/routes/api/v1/tasks/[id]/route.d.ts +117 -0
- package/dist/routes/api/v1/tasks/[id]/route.js +166 -0
- package/dist/routes/api/v1/tasks/index.d.ts +3 -0
- package/dist/routes/api/v1/tasks/index.js +10 -0
- package/dist/routes/api/v1/tasks/route.d.ts +96 -0
- package/dist/routes/api/v1/tasks/route.js +136 -0
- package/dist/routes/api/v1/terminal/[taskId]/create/index.d.ts +3 -0
- package/dist/routes/api/v1/terminal/[taskId]/create/index.js +5 -0
- package/dist/routes/api/v1/terminal/[taskId]/create/route.d.ts +10 -0
- package/dist/routes/api/v1/terminal/[taskId]/create/route.js +27 -0
- package/dist/routes/api/v1/terminal/[taskId]/destroy/index.d.ts +3 -0
- package/dist/routes/api/v1/terminal/[taskId]/destroy/index.js +5 -0
- package/dist/routes/api/v1/terminal/[taskId]/destroy/route.d.ts +10 -0
- package/dist/routes/api/v1/terminal/[taskId]/destroy/route.js +21 -0
- package/dist/routes/api/v1/terminal/[taskId]/resize/index.d.ts +3 -0
- package/dist/routes/api/v1/terminal/[taskId]/resize/index.js +5 -0
- package/dist/routes/api/v1/terminal/[taskId]/resize/route.d.ts +10 -0
- package/dist/routes/api/v1/terminal/[taskId]/resize/route.js +21 -0
- package/dist/routes/api/v1/terminal/sessions/route.js +4 -4
- package/dist/server-with-static.js +12 -8
- package/dist/server.js +12 -8
- package/package.json +4 -4
- package/static/assets/{ActivityPage-OxRci_V2.js → ActivityPage-k4I7Q53O.js} +1 -1
- package/static/assets/ApiKeysSettingsPage-B1YvVdmg.js +2 -0
- package/static/assets/{ArchitectureEditPage-D7xcH6dY.js → ArchitectureEditPage-CpowsIx2.js} +4 -4
- package/static/assets/{ArchitecturePage-pvnlX-NW.js → ArchitecturePage-DYxC_aMR.js} +1 -1
- package/static/assets/{AuthSettingsPage-Bu0CZ1rY.js → AuthSettingsPage-DtSo78Y_.js} +2 -2
- package/static/assets/{CallbackPage-D0lkjxCT.js → CallbackPage-bROCGapx.js} +1 -1
- package/static/assets/CodePage-CPCj64rX.js +2 -0
- package/static/assets/{CollapsibleSection-Bt_ZLnJc.js → CollapsibleSection-M5cXbl92.js} +1 -1
- package/static/assets/DashboardPage-B9BZZfw6.js +51 -0
- package/static/assets/{GitPage-TrTxZ27J.js → GitPage-BiDtdSK1.js} +2 -2
- package/static/assets/GitSettingsPage-THm6wDjs.js +6 -0
- package/static/assets/IdentityPage-BC16skg6.js +6 -0
- package/static/assets/{ImplementationStepsEditor-Ctx0CvbU.js → ImplementationStepsEditor-HliLQav5.js} +2 -2
- package/static/assets/{IntegrationsSettingsPage-C2wJVdM7.js → IntegrationsSettingsPage-CC_VKIQa.js} +1 -1
- package/static/assets/JobDetailPage-z1QQYvmU.js +1 -0
- package/static/assets/{KnowledgeDetailPage-BdTUfWqj.js → KnowledgeDetailPage-DzHXBS7Q.js} +1 -1
- package/static/assets/{KnowledgeEditPage-D8XK4IUf.js → KnowledgeEditPage-BwGnUH_m.js} +1 -1
- package/static/assets/KnowledgePage-CGIVMS02.js +3 -0
- package/static/assets/{LoginPage-Dqxd7cTa.js → LoginPage-VQ3lcfLV.js} +1 -1
- package/static/assets/MailInboxPage-DiZKqwdU.js +1 -0
- package/static/assets/MailProcessingModal-DIeSQBoR.js +6 -0
- package/static/assets/MailReadPage-C8AACmZQ.js +1 -0
- package/static/assets/MailSentPage-C_5yFly_.js +1 -0
- package/static/assets/{McpSettingsPage-10n35zXi.js → McpSettingsPage-i9YHcu1s.js} +1 -1
- package/static/assets/{NewKnowledgePage-BlJzzuh7.js → NewKnowledgePage-BnVY7WUD.js} +1 -1
- package/static/assets/{NewSkillPage-ByqN--mH.js → NewSkillPage-DwniHD6D.js} +1 -1
- package/static/assets/NewTaskPage-F5UX2WMc.js +90 -0
- package/static/assets/NotFoundPage-BbSZX_4L.js +6 -0
- package/static/assets/NotificationsSettingsPage-C8kjcift.js +1 -0
- package/static/assets/{ProjectEditPage-DKJTY2uc.js → ProjectEditPage-DUUlIEqI.js} +1 -1
- package/static/assets/{ProjectPage-2VblKCWz.js → ProjectPage-Unz9PQpA.js} +1 -1
- package/static/assets/{PromptsSettingsPage-B4mOhXuo.js → PromptsSettingsPage-DVpIuRKI.js} +1 -1
- package/static/assets/ResourceDetailPage-DqHZ2KYD.js +1 -0
- package/static/assets/{ResourcesPage-2BbjIWfF.js → ResourcesPage-BP5tuAi-.js} +1 -1
- package/static/assets/RoleEditPage-BgKu8S0-.js +13 -0
- package/static/assets/{RolePage-qXWXZ2FZ.js → RolePage-Fed52Ov5.js} +1 -1
- package/static/assets/{RulesSettingsPage-BtM7p8F6.js → RulesSettingsPage-BQ2O0u66.js} +3 -3
- package/static/assets/SchedulePage-jkxjuzBx.js +4 -0
- package/static/assets/SkillDetailPage-k3Q2-NFd.js +1 -0
- package/static/assets/{SkillEditPage-Czlo8WWT.js → SkillEditPage-urF4snjo.js} +1 -1
- package/static/assets/SkillsPage-DlWDhEjR.js +8 -0
- package/static/assets/{SkillsSettingsPage-DKtpy7qk.js → SkillsSettingsPage-BViFgckG.js} +1 -1
- package/static/assets/{SourceInput-BITn1Y15.js → SourceInput-CAFKTHw-.js} +1 -1
- package/static/assets/{TagInput-BK91_M1N.js → TagInput-C6lI-ePr.js} +1 -1
- package/static/assets/TaskDetailPage-DpbRHnW_.js +16 -0
- package/static/assets/TaskEditPage-DssRbW0h.js +1 -0
- package/static/assets/TasksPage-CD_eo0Bj.js +17 -0
- package/static/assets/TerminalPage-BG_wlccr.js +1 -0
- package/static/assets/TerminalSessionPage-CsK-LznK.js +8 -0
- package/static/assets/{UserPreferencesPage-DrgYEcxO.js → UserPreferencesPage-CWUq3efu.js} +1 -1
- package/static/assets/UserSettingsPage-CduI_MGS.js +1 -0
- package/static/assets/{UtilitiesPage-Djr4qT5L.js → UtilitiesPage-BAxokhLh.js} +1 -1
- package/static/assets/{alert-CsMvyYoX.js → alert-BXsc6_qu.js} +1 -1
- package/static/assets/{arrow-down-BZnfbld8.js → arrow-down-DmW_3gE8.js} +1 -1
- package/static/assets/{arrow-left-WGBYWq3h.js → arrow-left-1S-835kP.js} +1 -1
- package/static/assets/{arrow-up-BByVUPE7.js → arrow-up-BYism_o1.js} +1 -1
- package/static/assets/arrow-up-down-Dw3J0a4i.js +6 -0
- package/static/assets/{badge-AwLOflf5.js → badge-BUEY53dV.js} +1 -1
- package/static/assets/{browser-modal-BzGNFfTG.js → browser-modal-DCNdI4NT.js} +2 -2
- package/static/assets/{card-SN5gKnu7.js → card-BcPlIAH5.js} +1 -1
- package/static/assets/{chevron-left-C7uNq9l_.js → chevron-left-FMmNe7yP.js} +1 -1
- package/static/assets/{chevron-up-CHdIiLxL.js → chevron-up-CqM3won3.js} +1 -1
- package/static/assets/{chevrons-up-TXwQuoUN.js → chevrons-up-DTvCkIHc.js} +1 -1
- package/static/assets/{circle-alert-37E5gU9K.js → circle-alert-dseM-Ib7.js} +1 -1
- package/static/assets/{circle-check-big-nY4PntB5.js → circle-check-big-jKg34xC-.js} +1 -1
- package/static/assets/{circle-check-D02pWDME.js → circle-check-eyo6pBP1.js} +1 -1
- package/static/assets/{circle-play-7EXFLo4F.js → circle-play-BrY_lNiH.js} +1 -1
- package/static/assets/{circle-x-By4JoTHB.js → circle-x-uqmzEce1.js} +1 -1
- package/static/assets/{clipboard-BdymjxLO.js → clipboard-tzPFoieb.js} +1 -1
- package/static/assets/{clock-HDu44KTo.js → clock-Bjc06QBM.js} +1 -1
- package/static/assets/code-DrYqPukx.js +6 -0
- package/static/assets/{download-Cv2G2Eg9.js → download-Bg__QCLT.js} +1 -1
- package/static/assets/{external-link-DwMXcCCj.js → external-link-CNDy2UUo.js} +1 -1
- package/static/assets/{eye-DYnjJzdb.js → eye-DLFBnC8t.js} +1 -1
- package/static/assets/{folder-git-2-COeWFPHS.js → folder-git-2-DUqd0WRi.js} +1 -1
- package/static/assets/index-CHdBxVyk.css +2 -0
- package/static/assets/{index-9Tv-j_Ga.js → index-DFcWlnzl.js} +118 -103
- package/static/assets/{info-BmtuPMhv.js → info-D6jxZC5X.js} +1 -1
- package/static/assets/kiro-CX1mOsRO.js +17 -0
- package/static/assets/{label-TGqbNfMO.js → label-DBuh-ke5.js} +1 -1
- package/static/assets/{markdown-editor-ls1JPK_e.js → markdown-editor-B4YNQFT2.js} +1 -1
- package/static/assets/message-square-B5RWz_ff.js +6 -0
- package/static/assets/paperclip-4A_3MaPx.js +6 -0
- package/static/assets/{pause-CAWbvTiL.js → pause-BzhKXHtR.js} +1 -1
- package/static/assets/{play-DF_Qeu0H.js → play-CHIf-Rcz.js} +1 -1
- package/static/assets/{radio-group-DYTbywtK.js → radio-group-C1ct-VsJ.js} +1 -1
- package/static/assets/{refresh-cw-BFZxHqbC.js → refresh-cw-B3OwrDUf.js} +1 -1
- package/static/assets/{search-Dr90tbch.js → search-Cq1ksEdp.js} +1 -1
- package/static/assets/{select-Cs5qtMYV.js → select-44mcS2_G.js} +1 -1
- package/static/assets/{status-utils-BDOyevaX.js → status-utils-CDkPeVfP.js} +1 -1
- package/static/assets/{switch-4TDb6YiQ.js → switch-CIwjYvCt.js} +1 -1
- package/static/assets/{tabs-BrbEvF4V.js → tabs-DTV6Su-h.js} +1 -1
- package/static/assets/{tag-DrQkepeD.js → tag-p6yeowCW.js} +1 -1
- package/static/assets/{terminal-preview-uuKF9_x4.js → terminal-preview-DN38x9Jm.js} +1 -1
- package/static/assets/use-terminal-BXJqOeJe.js +1 -0
- package/static/assets/{video-DYA2WfbA.js → video-BH5ChaoS.js} +1 -1
- package/static/index.html +2 -2
- package/static/assets/ApiKeysSettingsPage-C0evI19e.js +0 -2
- package/static/assets/CodePage-BJ4PC5nb.js +0 -2
- package/static/assets/DashboardPage-BiffPdmj.js +0 -41
- package/static/assets/GitSettingsPage-D7q5xQd_.js +0 -6
- package/static/assets/IdentityPage-CY0Ak2j0.js +0 -11
- package/static/assets/JobDetailPage-Phx_IlKX.js +0 -1
- package/static/assets/KnowledgePage-Ci9G7Br-.js +0 -8
- package/static/assets/NewProposalPage-BP7Ttoxk.js +0 -90
- package/static/assets/ProposalDetailPage-m3ysyzpj.js +0 -1
- package/static/assets/ProposalEditPage-3XVg_paW.js +0 -1
- package/static/assets/ProposalsPage-B3u0aFFz.js +0 -17
- package/static/assets/ResourceDetailPage-somBLUpC.js +0 -1
- package/static/assets/RoleEditPage-CLzX7Xhi.js +0 -13
- package/static/assets/SchedulePage-4tFcIBSs.js +0 -4
- package/static/assets/SkillDetailPage-CroSdaju.js +0 -1
- package/static/assets/SkillsPage-CgULbcI-.js +0 -8
- package/static/assets/TerminalPage-8fwvnOo2.js +0 -1
- package/static/assets/TerminalSessionPage-BhO5U48p.js +0 -13
- package/static/assets/UserSettingsPage-Dj6lKLi8.js +0 -1
- package/static/assets/droid-CPteN3f9.js +0 -17
- package/static/assets/index-GFQ5RqVh.css +0 -2
- package/static/assets/use-terminal-BG5UXuVE.js +0 -1
- 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 = ['
|
|
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
|
|
57
|
+
async createTask(task) {
|
|
58
58
|
await this.ensureDirectories();
|
|
59
|
-
// Extract content from the
|
|
60
|
-
const { content, ...frontmatter } =
|
|
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 =
|
|
63
|
-
?
|
|
64
|
-
: new Date(
|
|
65
|
-
const updatedAt =
|
|
66
|
-
?
|
|
67
|
-
: new Date(
|
|
68
|
-
// Convert the
|
|
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:
|
|
72
|
-
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
|
-
...(
|
|
75
|
+
...(task.intent && { intent: task.intent }),
|
|
76
76
|
createdAt: createdAt.toISOString(),
|
|
77
77
|
updatedAt: updatedAt.toISOString(),
|
|
78
|
-
status:
|
|
79
|
-
priority:
|
|
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:
|
|
83
|
-
name:
|
|
84
|
-
email:
|
|
83
|
+
id: task.author.id,
|
|
84
|
+
name: task.author.name,
|
|
85
|
+
email: task.author.email || '',
|
|
85
86
|
role: 'engineer', // Default role
|
|
86
|
-
type:
|
|
87
|
+
type: task.author.type
|
|
87
88
|
},
|
|
88
89
|
// Steps (top-level for consistency with CLI storage format)
|
|
89
|
-
steps:
|
|
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:
|
|
100
|
+
tags: task.metadata.tags || [],
|
|
100
101
|
labels: [],
|
|
101
102
|
// Comments use consistent field names: id, author, content, createdAt
|
|
102
|
-
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(
|
|
112
|
-
const filePath = path.join(this.basePath, '
|
|
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
|
|
116
|
+
async getTask(id) {
|
|
116
117
|
try {
|
|
117
|
-
const filePath = path.join(this.basePath, '
|
|
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
|
|
121
|
-
return this.
|
|
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
|
|
130
|
-
const existing = await this.
|
|
130
|
+
async updateTask(id, updates) {
|
|
131
|
+
const existing = await this.getTask(id);
|
|
131
132
|
if (!existing)
|
|
132
|
-
throw new Error(`
|
|
133
|
-
// Never allow id to be updated to prevent overwriting other
|
|
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
|
|
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.
|
|
155
|
+
await this.createTask(updated);
|
|
155
156
|
}
|
|
156
|
-
async
|
|
157
|
-
const filePath = path.join(this.basePath, '
|
|
157
|
+
async deleteTask(id) {
|
|
158
|
+
const filePath = path.join(this.basePath, 'tasks', `${id}.md`);
|
|
158
159
|
await fs.unlink(filePath);
|
|
159
160
|
}
|
|
160
|
-
async
|
|
161
|
-
const
|
|
161
|
+
async listTasks(filter) {
|
|
162
|
+
const tasksDir = path.join(this.basePath, 'tasks');
|
|
162
163
|
try {
|
|
163
|
-
const files = await fs.readdir(
|
|
164
|
-
const
|
|
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(
|
|
168
|
+
const content = await fs.readFile(path.join(tasksDir, file), 'utf-8');
|
|
168
169
|
const { data, content: body } = matter(content);
|
|
169
|
-
return this.
|
|
170
|
+
return this.frontmatterToTask(data, body);
|
|
170
171
|
}));
|
|
171
172
|
// Apply filters
|
|
172
|
-
let filtered =
|
|
173
|
-
if (!
|
|
173
|
+
let filtered = tasks.filter(task => {
|
|
174
|
+
if (!task)
|
|
174
175
|
return false;
|
|
175
|
-
if (filter?.status &&
|
|
176
|
+
if (filter?.status && task.status !== filter.status)
|
|
176
177
|
return false;
|
|
177
|
-
if (filter?.author &&
|
|
178
|
+
if (filter?.author && task.author.id !== filter.author)
|
|
178
179
|
return false;
|
|
179
|
-
if (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
|
|
183
|
-
if (!filter.tags.some(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
|
|
212
|
-
const
|
|
213
|
-
const fuse = new Fuse(
|
|
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
|
-
|
|
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(
|
|
281
|
-
return `# ${
|
|
275
|
+
getDefaultContent(task) {
|
|
276
|
+
return `# ${task.title}
|
|
282
277
|
|
|
283
278
|
## Problem Statement
|
|
284
|
-
Describe the problem this
|
|
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
|
-
|
|
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(
|
|
22
|
+
createSession(taskId: string, shellPreference?: ShellType, startupCommand?: string): Promise<TerminalSession>;
|
|
23
23
|
getSession(sessionId: string): TerminalSession | undefined;
|
|
24
|
-
|
|
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(
|
|
20
|
-
const sessionId = `${
|
|
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', '
|
|
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
|
-
|
|
79
|
+
COCONUT_TASK_ID: taskId,
|
|
80
80
|
COCONUT_CONTEXT_PATH: path.join(projectRoot, '.nut', 'context'),
|
|
81
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
213
|
-
return Array.from(this.sessions.values()).filter(session => session.
|
|
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,
|
|
10
|
-
import {
|
|
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
|
-
|
|
100
|
-
description:
|
|
101
|
-
inputSchema: jsonSchema(
|
|
99
|
+
tasks: tool({
|
|
100
|
+
description: tasksFullTool.description,
|
|
101
|
+
inputSchema: jsonSchema(tasksFullTool.parameters),
|
|
102
102
|
execute: async (args) => {
|
|
103
|
-
const result = await
|
|
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
|
|
187
|
-
const { operation, id, filters,
|
|
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
|
|
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:
|
|
195
|
-
count:
|
|
196
|
-
|
|
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
|
|
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 (!
|
|
234
|
+
if (!task) {
|
|
215
235
|
return { success: false, error: 'Task data is required for create operation' };
|
|
216
236
|
}
|
|
217
|
-
const created = await
|
|
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 ||
|
|
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
|
|
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
|
|
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
|
|
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,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 };
|