@lovelybunch/api 1.0.76-alpha.10 → 1.0.76-alpha.12

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 (142) hide show
  1. package/dist/lib/git.d.ts +3 -0
  2. package/dist/lib/git.js +35 -0
  3. package/dist/routes/api/v1/ai/route.js +56 -149
  4. package/dist/routes/api/v1/git/index.js +27 -1
  5. package/dist/routes/api/v1/jobs/[id]/route.d.ts +12 -18
  6. package/dist/routes/api/v1/jobs/[id]/route.js +3 -3
  7. package/dist/routes/api/v1/jobs/route.d.ts +19 -19
  8. package/dist/routes/api/v1/jobs/route.js +17 -2
  9. package/dist/routes/api/v1/mcp/index.js +27 -19
  10. package/dist/routes/api/v1/resources/generate/route.js +10 -3
  11. package/dist/routes/api/v1/slack/route.d.ts +6 -6
  12. package/package.json +4 -4
  13. package/static/assets/{ActivityPage-B3rsYt_4.js → ActivityPage-BJJTUKh9.js} +1 -1
  14. package/static/assets/{AgentsContextEditPage-4D7qO4S-.js → AgentsContextEditPage-DdBAbiaZ.js} +1 -1
  15. package/static/assets/{AgentsContextPage-NmvLRo0k.js → AgentsContextPage-C49ImhHO.js} +1 -1
  16. package/static/assets/{ApiKeysSettingsPage-BGj5p9zw.js → ApiKeysSettingsPage-CYl8BKyY.js} +1 -1
  17. package/static/assets/{ArchitectureEditPage-DI2jNmc9.js → ArchitectureEditPage-Bj3Bg_pV.js} +1 -1
  18. package/static/assets/{ArchitecturePage-DiIuThUM.js → ArchitecturePage-B4REANhO.js} +1 -1
  19. package/static/assets/{AuthSettingsPage-DWuwxAS0.js → AuthSettingsPage-CvobAtkW.js} +1 -1
  20. package/static/assets/{CallbackPage-BCmkYhEr.js → CallbackPage-Cg68HItO.js} +1 -1
  21. package/static/assets/{CodePage-BZhIkymg.js → CodePage-BgIQAm3x.js} +1 -1
  22. package/static/assets/{CollapsibleSection-BJzLVd-x.js → CollapsibleSection-B80EDHZL.js} +1 -1
  23. package/static/assets/{DashboardPage-DtwE553F.js → DashboardPage-KtwwIk4u.js} +1 -1
  24. package/static/assets/{GitPage-qG7VYL9b.js → GitPage-U50MBUc2.js} +1 -1
  25. package/static/assets/{GitSettingsPage-C8Bgpj-X.js → GitSettingsPage-CVJFc5Cz.js} +1 -1
  26. package/static/assets/{IdentityPage-Cf8Ruu-g.js → IdentityPage-B_prxEfP.js} +1 -1
  27. package/static/assets/{ImplementationStepsEditor-BXIRT7o1.js → ImplementationStepsEditor-7737WSoi.js} +1 -1
  28. package/static/assets/{IntegrationsSettingsPage-th56kUgi.js → IntegrationsSettingsPage-CzmVKesF.js} +1 -1
  29. package/static/assets/JobDetailPage-61LKyh2y.js +1 -0
  30. package/static/assets/{KnowledgeDetailPage-BklEOfs0.js → KnowledgeDetailPage-DF8VINWX.js} +1 -1
  31. package/static/assets/{KnowledgeEditPage-BGqzVqHi.js → KnowledgeEditPage-BPqv8dm5.js} +1 -1
  32. package/static/assets/{KnowledgePage-Dmm7vzAj.js → KnowledgePage-DK2ZmTOW.js} +1 -1
  33. package/static/assets/{LoginPage-PDafaEnq.js → LoginPage-BJoMiSzz.js} +1 -1
  34. package/static/assets/MailInboxPage-D7kppimo.js +1 -0
  35. package/static/assets/MailProcessingModal-CiU2tPrl.js +1 -0
  36. package/static/assets/MailReadPage-C6Uk8fuE.js +1 -0
  37. package/static/assets/{MailSentPage-CjBrbezf.js → MailSentPage-BC08nLUD.js} +1 -1
  38. package/static/assets/{McpSettingsPage-DxE-4YPc.js → McpSettingsPage-CdNU_m5E.js} +1 -1
  39. package/static/assets/{MemoryEditPage-BlQ5EN1w.js → MemoryEditPage-CrMnRGDo.js} +1 -1
  40. package/static/assets/{MemoryPage-B8fsgW7z.js → MemoryPage-BXzS3NzY.js} +1 -1
  41. package/static/assets/{NewKnowledgePage-CuH201xQ.js → NewKnowledgePage-Ia0GhoF_.js} +1 -1
  42. package/static/assets/{NewSkillPage-BdX7eNoi.js → NewSkillPage-B-lKmYIT.js} +1 -1
  43. package/static/assets/{NewTaskPage-D9W2Yi0r.js → NewTaskPage-B9sYSebW.js} +1 -1
  44. package/static/assets/{NotFoundPage-D9tmTVho.js → NotFoundPage-CFh8ZpUq.js} +1 -1
  45. package/static/assets/{NotificationsSettingsPage-B4lBxS8D.js → NotificationsSettingsPage-CkW0hhZp.js} +1 -1
  46. package/static/assets/{ProjectEditPage-DdqUI91K.js → ProjectEditPage-CERVEYfa.js} +1 -1
  47. package/static/assets/{ProjectPage-DB7ugw8l.js → ProjectPage-smlgt2WA.js} +1 -1
  48. package/static/assets/{PromptsSettingsPage-DFXoKUjj.js → PromptsSettingsPage-rmH7tCbI.js} +1 -1
  49. package/static/assets/{ResourceDetailPage-CWc4YIMc.js → ResourceDetailPage-JYYH_eYN.js} +1 -1
  50. package/static/assets/ResourcesPage-Bxo0L-YJ.js +41 -0
  51. package/static/assets/{RoleEditPage-B20UAC2r.js → RoleEditPage-DUxyu-Pf.js} +1 -1
  52. package/static/assets/{RolePage-C5Jf7wxY.js → RolePage-Bxtbnhzq.js} +1 -1
  53. package/static/assets/{RulesSettingsPage-91WP1FQf.js → RulesSettingsPage-Bq85g3dZ.js} +1 -1
  54. package/static/assets/RunDetailPage-Bmr-OpEH.js +1 -0
  55. package/static/assets/SchedulePage-DVFrtK_u.js +4 -0
  56. package/static/assets/{SkillDetailPage-Cpxz_wmk.js → SkillDetailPage-DLVeAMHh.js} +1 -1
  57. package/static/assets/{SkillEditPage-Cz7VRg0r.js → SkillEditPage-L8qO7aXB.js} +1 -1
  58. package/static/assets/{SkillsPage-Bxmsi6Xd.js → SkillsPage-Dbk0ZdCj.js} +1 -1
  59. package/static/assets/{SkillsSettingsPage-Cysx53Au.js → SkillsSettingsPage-D1LF96aM.js} +1 -1
  60. package/static/assets/{SourceInput-D59F7hff.js → SourceInput-s6cvxCld.js} +1 -1
  61. package/static/assets/{TagInput-BH6wDXWJ.js → TagInput-B6t8mWa1.js} +1 -1
  62. package/static/assets/{TaskDetailPage-5ktdWaTr.js → TaskDetailPage-CFKiBp4N.js} +1 -1
  63. package/static/assets/{TaskEditPage-CIZWp_s4.js → TaskEditPage-CU1Nh5Wt.js} +1 -1
  64. package/static/assets/{TasksPage-BNXDm-Er.js → TasksPage-ChyoBFb1.js} +1 -1
  65. package/static/assets/{TeamEditPage-C10siqme.js → TeamEditPage-BiH1cvW_.js} +1 -1
  66. package/static/assets/{TeamPage-CXpZVAfy.js → TeamPage-CZgCevCv.js} +1 -1
  67. package/static/assets/{TerminalPage-DJ-Tu_hG.js → TerminalPage-CnMHrceg.js} +1 -1
  68. package/static/assets/{TerminalSessionPage-BLryJlkL.js → TerminalSessionPage-CWRmxO5F.js} +1 -1
  69. package/static/assets/{UserPreferencesPage-BvAcv5tt.js → UserPreferencesPage-DBAMcnVw.js} +1 -1
  70. package/static/assets/{UserSettingsPage-NQfoOvHf.js → UserSettingsPage-DaJ5ULZ6.js} +1 -1
  71. package/static/assets/{UtilitiesPage-NnBCWVn8.js → UtilitiesPage-DK2tA2dg.js} +1 -1
  72. package/static/assets/{alert-CMfexl-V.js → alert-BLA1YxSF.js} +1 -1
  73. package/static/assets/{arrow-down-7VoAlw8h.js → arrow-down-BS_gkmFX.js} +1 -1
  74. package/static/assets/{arrow-left-CkQCXG-O.js → arrow-left-BMmwy6t5.js} +1 -1
  75. package/static/assets/{arrow-up-Bozm6eto.js → arrow-up-DEdQz7h4.js} +1 -1
  76. package/static/assets/{arrow-up-down-BgZelEPF.js → arrow-up-down-BfLyyRID.js} +1 -1
  77. package/static/assets/{badge-CKqDVhJv.js → badge-DXtacKLi.js} +1 -1
  78. package/static/assets/{browser-modal-CIOZi1Jw.js → browser-modal-C-EJH5-l.js} +1 -1
  79. package/static/assets/{card-jBf8qQSy.js → card-T1Zl_t-O.js} +1 -1
  80. package/static/assets/{chevron-left-D1qHCYRd.js → chevron-left-CHIuYPig.js} +1 -1
  81. package/static/assets/{chevron-up-CcwaoTsZ.js → chevron-up-C1j8XcMS.js} +1 -1
  82. package/static/assets/{chevrons-up-BtlNQbrw.js → chevrons-up-CJo8Mkrm.js} +1 -1
  83. package/static/assets/{circle-alert-DqK0nUqF.js → circle-alert-C-TQDAZH.js} +1 -1
  84. package/static/assets/{circle-check-qo4cPVA1.js → circle-check-D4Eo7NeP.js} +1 -1
  85. package/static/assets/{circle-check-big-DDQIqE0G.js → circle-check-big-Dw0wgLkY.js} +1 -1
  86. package/static/assets/{circle-play-CQ-UItmT.js → circle-play-XYouMl6A.js} +1 -1
  87. package/static/assets/{circle-x-Bd57ohC5.js → circle-x-i-p09ZTc.js} +1 -1
  88. package/static/assets/{clipboard-JQLlAwqS.js → clipboard-CAJMRUqv.js} +1 -1
  89. package/static/assets/{clock-DTp411N-.js → clock-t6EXtqFH.js} +1 -1
  90. package/static/assets/{code-Bi-U-NxE.js → code-CQveJCBf.js} +1 -1
  91. package/static/assets/{download-DNaE6rtO.js → download-gURwTJ4j.js} +1 -1
  92. package/static/assets/{external-link-DRsIhdWR.js → external-link-B6lRGdDG.js} +1 -1
  93. package/static/assets/{eye-jqpFU8Rl.js → eye-BmQvWYqM.js} +1 -1
  94. package/static/assets/{folder-git-2-ixHmuqNj.js → folder-git-2-Y7dQhpIc.js} +1 -1
  95. package/static/assets/{globe-BZEWER4V.js → globe-DZ1sD-ad.js} +1 -1
  96. package/static/assets/index-6zt5f3rT.js +502 -0
  97. package/static/assets/{index-BGKQ2Csq.js → index-B-EQGkgp.js} +1 -1
  98. package/static/assets/{index-CtWTFIFI.js → index-BDAqiMeO.js} +1 -1
  99. package/static/assets/{index-2_Tb936f.js → index-BKsuLLw3.js} +1 -1
  100. package/static/assets/{index-BD47WGC6.js → index-BQ6h7obM.js} +1 -1
  101. package/static/assets/index-BcCuKdbf.css +1 -0
  102. package/static/assets/{index-htJaaOEZ.js → index-C7-vaKc0.js} +1 -1
  103. package/static/assets/{index-M-XcYp88.js → index-CEVCklYW.js} +1 -1
  104. package/static/assets/{index-B8B_4B2l.js → index-CdbMr7EO.js} +1 -1
  105. package/static/assets/{index-DHAvHhw0.js → index-Cop1vyXt.js} +1 -1
  106. package/static/assets/{index-CK5AHHmM.js → index-Ct1TqN9g.js} +1 -1
  107. package/static/assets/{index-D-5sddKG.js → index-CvaJ85WT.js} +1 -1
  108. package/static/assets/{index-BLjvGp1F.js → index-DHyZkKDt.js} +1 -1
  109. package/static/assets/{index-C3ao8yMv.js → index-DeFIoxHB.js} +1 -1
  110. package/static/assets/{index-CoeRBZRl.js → index-DiWum-Qk.js} +1 -1
  111. package/static/assets/{index-DffngCzC.js → index-DkcxAJRd.js} +1 -1
  112. package/static/assets/{index-UDlexUv4.js → index-JMlWoOlR.js} +1 -1
  113. package/static/assets/{index-Bft1WzL9.js → index-d2atEU4g.js} +1 -1
  114. package/static/assets/{index-CdmUQF6-.js → index-p7Qpxih7.js} +1 -1
  115. package/static/assets/{index-CiS46Fde.js → index-q465oP8l.js} +1 -1
  116. package/static/assets/{info-Na9GqbtC.js → info-gGFJAYNn.js} +1 -1
  117. package/static/assets/{label-CHVVHrlr.js → label-CSt-eWXG.js} +1 -1
  118. package/static/assets/{markdown-editor-CT9CPLw5.js → markdown-editor-C6ZusrL3.js} +3 -3
  119. package/static/assets/{message-square-B5NR6hzj.js → message-square-B-XWYk3x.js} +1 -1
  120. package/static/assets/{paperclip-SI0kWfGf.js → paperclip-Drtxq0HD.js} +1 -1
  121. package/static/assets/{pause-DKI9nsrI.js → pause-fdtoI1yQ.js} +1 -1
  122. package/static/assets/{play-BTA0gBuh.js → play-BjJrEDlO.js} +1 -1
  123. package/static/assets/{radio-group-BO2__pxZ.js → radio-group-CLFgSQkD.js} +1 -1
  124. package/static/assets/{refresh-cw-DLJpfa8_.js → refresh-cw-DF2wDnWT.js} +1 -1
  125. package/static/assets/{search-SRuczbr5.js → search-CYS9xEgy.js} +1 -1
  126. package/static/assets/{select-D8HyxWyg.js → select-C7W_IjpH.js} +1 -1
  127. package/static/assets/{switch-Ck0OeSLk.js → switch-CeUexKh7.js} +1 -1
  128. package/static/assets/{tabs-CmFe8eVW.js → tabs-CqVmSyiJ.js} +1 -1
  129. package/static/assets/{tag-BTjGQC_2.js → tag-Mob815Ck.js} +1 -1
  130. package/static/assets/{terminal-preview-BkQIuk__.js → terminal-preview-Byv2z2nW.js} +1 -1
  131. package/static/assets/triangle-alert-DW7a6_v9.js +6 -0
  132. package/static/assets/{use-terminal-CrqBojgD.js → use-terminal-CQe1dgj2.js} +1 -1
  133. package/static/assets/{video-BlwRNgSo.js → video-Ovebr1Iq.js} +1 -1
  134. package/static/index.html +2 -2
  135. package/static/assets/JobDetailPage-ZyEcbPAe.js +0 -1
  136. package/static/assets/MailInboxPage-M5_2gGkB.js +0 -1
  137. package/static/assets/MailProcessingModal-C4PotIFc.js +0 -6
  138. package/static/assets/MailReadPage-D32FUx5o.js +0 -1
  139. package/static/assets/ResourcesPage-BK-EXoIA.js +0 -41
  140. package/static/assets/SchedulePage-BijprHGn.js +0 -4
  141. package/static/assets/index-BWhM9fZn.js +0 -502
  142. package/static/assets/index-oRAlpULD.css +0 -1
package/dist/lib/git.d.ts CHANGED
@@ -1,3 +1,4 @@
1
+ import type { InstructionFileKind, InstructionFiles } from '@lovelybunch/types';
1
2
  export declare function getRepoRoot(): Promise<string>;
2
3
  export declare function getWorktreesBase(): Promise<string>;
3
4
  export declare function sanitizeBranchName(name: string): string;
@@ -9,6 +10,8 @@ export declare function runGit(args: string[], opts?: {
9
10
  stdout: string;
10
11
  stderr: string;
11
12
  }>;
13
+ export declare function lsTree(ref?: string): Promise<string[]>;
14
+ export declare function getInstructionFiles(filter?: InstructionFileKind | 'all'): Promise<InstructionFiles>;
12
15
  export declare function getRepoStatus(): Promise<{
13
16
  branch: string;
14
17
  ahead: number;
package/dist/lib/git.js CHANGED
@@ -5,6 +5,7 @@ import { promises as fs } from 'fs';
5
5
  import { readGithubToken, isGithubTokenValid } from './github-token.js';
6
6
  import { findGaitDirectory } from './gait-path.js';
7
7
  import { loadGitSettings } from './git-settings.js';
8
+ import { INSTRUCTION_FILE_KINDS } from '@lovelybunch/types';
8
9
  const execFile = promisify(_execFile);
9
10
  // Base directory for worktrees under the repository root
10
11
  export async function getRepoRoot() {
@@ -52,6 +53,40 @@ export async function runGit(args, opts) {
52
53
  };
53
54
  return execFile('git', args, { cwd, maxBuffer: 10 * 1024 * 1024, timeout, env });
54
55
  }
56
+ // --- ls-tree ---
57
+ export async function lsTree(ref = 'HEAD') {
58
+ const { stdout } = await runGit(['ls-tree', '-r', '--name-only', ref]);
59
+ return stdout.trim().split('\n').filter(l => l && !l.startsWith('.nut/'));
60
+ }
61
+ // --- Instruction files ---
62
+ const FILENAME_TO_KIND = new Map(INSTRUCTION_FILE_KINDS.map(k => [`${k}.md`, k]));
63
+ const KIND_TO_FILENAME = new Map(INSTRUCTION_FILE_KINDS.map(k => [k, `${k}.md`]));
64
+ export async function getInstructionFiles(filter) {
65
+ const filterFilename = filter && filter !== 'all' ? KIND_TO_FILENAME.get(filter) : undefined;
66
+ const allPaths = await lsTree();
67
+ const wanted = allPaths.filter(p => {
68
+ const name = path.basename(p);
69
+ if (!FILENAME_TO_KIND.has(name))
70
+ return false;
71
+ if (filterFilename)
72
+ return name === filterFilename;
73
+ return true;
74
+ });
75
+ const entries = await Promise.all(wanted.map(async (filePath) => {
76
+ const { stdout } = await runGit(['show', `HEAD:${filePath}`]);
77
+ return { filePath, content: stdout };
78
+ }));
79
+ const result = {};
80
+ for (const { filePath, content } of entries) {
81
+ const dir = path.dirname(filePath);
82
+ const key = dir === '.' ? '.' : dir;
83
+ const kind = FILENAME_TO_KIND.get(path.basename(filePath));
84
+ if (!result[key])
85
+ result[key] = {};
86
+ result[key][kind] = content;
87
+ }
88
+ return result;
89
+ }
55
90
  // --- Status ---
56
91
  export async function getRepoStatus() {
57
92
  const { stdout } = await runGit(['status', '--porcelain=v1', '-b']);
@@ -7,7 +7,7 @@ import { ZodError } from 'zod';
7
7
  import { streamText, tool, jsonSchema, stepCountIs } from 'ai';
8
8
  import { createAnthropic } from '@ai-sdk/anthropic';
9
9
  import { getLogsDir, listTasks, getTask, createTask, updateTask, deleteTask, } from '@lovelybunch/core';
10
- import { tasksFullTool, knowledgeTool, normalizeKnowledgeMetadata, eventsTool, projectContextTool, architectureContextTool, roleContextTool, resourcesTool, listConnectorsTool, connectorRequestTool, resolveConnectorUrl } from '@lovelybunch/mcp';
10
+ import { tasksFullTool, knowledgeTool, normalizeKnowledgeMetadata, eventsTool, roleContextTool, agentsContextTool, teamContextTool, memoryContextTool, resourcesTool, listConnectorsTool, connectorRequestTool, resolveConnectorUrl } 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';
@@ -120,27 +120,35 @@ export async function POST(c) {
120
120
  return JSON.stringify(result);
121
121
  },
122
122
  }),
123
- project_context: tool({
124
- description: projectContextTool.description,
125
- inputSchema: jsonSchema(projectContextTool.parameters),
123
+ role_context: tool({
124
+ description: roleContextTool.description,
125
+ inputSchema: jsonSchema(roleContextTool.parameters),
126
126
  execute: async (args) => {
127
- const result = await executeProjectContextToolDirect(args);
127
+ const result = await executeRoleContextToolDirect(args);
128
128
  return JSON.stringify(result);
129
129
  },
130
130
  }),
131
- architecture_context: tool({
132
- description: architectureContextTool.description,
133
- inputSchema: jsonSchema(architectureContextTool.parameters),
131
+ agents_context: tool({
132
+ description: agentsContextTool.description,
133
+ inputSchema: jsonSchema(agentsContextTool.parameters),
134
134
  execute: async (args) => {
135
- const result = await executeArchitectureContextToolDirect(args);
135
+ const result = await executeAgentsContextToolDirect(args);
136
136
  return JSON.stringify(result);
137
137
  },
138
138
  }),
139
- role_context: tool({
140
- description: roleContextTool.description,
141
- inputSchema: jsonSchema(roleContextTool.parameters),
139
+ team_context: tool({
140
+ description: teamContextTool.description,
141
+ inputSchema: jsonSchema(teamContextTool.parameters),
142
142
  execute: async (args) => {
143
- const result = await executeRoleContextToolDirect(args);
143
+ const result = await executeTeamContextToolDirect(args);
144
+ return JSON.stringify(result);
145
+ },
146
+ }),
147
+ memory_context: tool({
148
+ description: memoryContextTool.description,
149
+ inputSchema: jsonSchema(memoryContextTool.parameters),
150
+ execute: async (args) => {
151
+ const result = await executeMemoryContextToolDirect(args);
144
152
  return JSON.stringify(result);
145
153
  },
146
154
  }),
@@ -537,12 +545,12 @@ async function executeEventsToolDirect(args) {
537
545
  return { success: false, error: error.message || 'Events tool execution failed' };
538
546
  }
539
547
  }
540
- // Project context tool - read/write the project definition document
541
- async function executeProjectContextToolDirect(args) {
548
+ // Role context tool - read/write the role definition document
549
+ async function executeRoleContextToolDirect(args) {
542
550
  const { operation, content, old_text, new_text } = args;
543
551
  try {
544
552
  const contextPath = getContextBasePath();
545
- const filePath = join(contextPath, 'project.md');
553
+ const filePath = join(contextPath, 'role.md');
546
554
  switch (operation) {
547
555
  case 'get': {
548
556
  try {
@@ -555,7 +563,7 @@ async function executeProjectContextToolDirect(args) {
555
563
  frontmatter: parsed.data,
556
564
  raw: fileContent
557
565
  },
558
- message: 'Retrieved project context'
566
+ message: 'Retrieved role context'
559
567
  };
560
568
  }
561
569
  catch (err) {
@@ -563,7 +571,7 @@ async function executeProjectContextToolDirect(args) {
563
571
  return {
564
572
  success: true,
565
573
  data: { content: '', frontmatter: {}, raw: '' },
566
- message: 'Project context document does not exist yet. You can create it with an update operation.'
574
+ message: 'Role context document does not exist yet. You can create it with an update operation.'
567
575
  };
568
576
  }
569
577
  throw err;
@@ -590,7 +598,7 @@ async function executeProjectContextToolDirect(args) {
590
598
  await fs.writeFile(filePath, newContent, 'utf-8');
591
599
  return {
592
600
  success: true,
593
- message: 'Appended content to project context document'
601
+ message: 'Appended content to role context document'
594
602
  };
595
603
  }
596
604
  case 'replace_section': {
@@ -604,7 +612,7 @@ async function executeProjectContextToolDirect(args) {
604
612
  }
605
613
  catch (err) {
606
614
  if (err.code === 'ENOENT') {
607
- return { success: false, error: 'Project context document does not exist. Use append or update to create it first.' };
615
+ return { success: false, error: 'Role context document does not exist. Use append or update to create it first.' };
608
616
  }
609
617
  throw err;
610
618
  }
@@ -614,7 +622,7 @@ async function executeProjectContextToolDirect(args) {
614
622
  success: false,
615
623
  error: 'Could not find the specified text in the document. The text may have been paraphrased or changed.',
616
624
  fallback_markdown: new_text,
617
- suggestion: 'Here is the replacement text. You can copy it and manually edit the document at /context/project'
625
+ suggestion: 'Here is the replacement text. You can copy it and manually edit the document at /context/role'
618
626
  };
619
627
  }
620
628
  // Replace the text
@@ -622,7 +630,7 @@ async function executeProjectContextToolDirect(args) {
622
630
  await fs.writeFile(filePath, updatedContent, 'utf-8');
623
631
  return {
624
632
  success: true,
625
- message: 'Replaced section in project context document'
633
+ message: 'Replaced section in role context document'
626
634
  };
627
635
  }
628
636
  case 'update': {
@@ -633,7 +641,7 @@ async function executeProjectContextToolDirect(args) {
633
641
  await fs.writeFile(filePath, content, 'utf-8');
634
642
  return {
635
643
  success: true,
636
- message: 'Updated project context document'
644
+ message: 'Updated role context document'
637
645
  };
638
646
  }
639
647
  default:
@@ -641,124 +649,28 @@ async function executeProjectContextToolDirect(args) {
641
649
  }
642
650
  }
643
651
  catch (error) {
644
- console.error('Error executing project context tool:', error);
645
- return { success: false, error: error.message || 'Project context tool execution failed' };
652
+ console.error('Error executing role context tool:', error);
653
+ return { success: false, error: error.message || 'Role context tool execution failed' };
646
654
  }
647
655
  }
648
- // Architecture context tool - read/write the architecture document
649
- async function executeArchitectureContextToolDirect(args) {
650
- const { operation, content, old_text, new_text } = args;
651
- try {
652
- const contextPath = getContextBasePath();
653
- const filePath = join(contextPath, 'architecture.md');
654
- switch (operation) {
655
- case 'get': {
656
- try {
657
- const fileContent = await fs.readFile(filePath, 'utf-8');
658
- const parsed = matter(fileContent);
659
- return {
660
- success: true,
661
- data: {
662
- content: parsed.content,
663
- frontmatter: parsed.data,
664
- raw: fileContent
665
- },
666
- message: 'Retrieved architecture context'
667
- };
668
- }
669
- catch (err) {
670
- if (err.code === 'ENOENT') {
671
- return {
672
- success: true,
673
- data: { content: '', frontmatter: {}, raw: '' },
674
- message: 'Architecture context document does not exist yet. You can create it with an update operation.'
675
- };
676
- }
677
- throw err;
678
- }
679
- }
680
- case 'append': {
681
- if (!content) {
682
- return { success: false, error: 'Content is required for append operation' };
683
- }
684
- await fs.mkdir(contextPath, { recursive: true });
685
- // Read existing content if file exists
686
- let existingContent = '';
687
- try {
688
- existingContent = await fs.readFile(filePath, 'utf-8');
689
- }
690
- catch (err) {
691
- if (err.code !== 'ENOENT')
692
- throw err;
693
- }
694
- // Append new content with a newline separator
695
- const newContent = existingContent
696
- ? existingContent.trimEnd() + '\n\n' + content
697
- : content;
698
- await fs.writeFile(filePath, newContent, 'utf-8');
699
- return {
700
- success: true,
701
- message: 'Appended content to architecture context document'
702
- };
703
- }
704
- case 'replace_section': {
705
- if (!old_text || !new_text) {
706
- return { success: false, error: 'Both old_text and new_text are required for replace_section operation' };
707
- }
708
- // Read existing content
709
- let existingContent = '';
710
- try {
711
- existingContent = await fs.readFile(filePath, 'utf-8');
712
- }
713
- catch (err) {
714
- if (err.code === 'ENOENT') {
715
- return { success: false, error: 'Architecture context document does not exist. Use append or update to create it first.' };
716
- }
717
- throw err;
718
- }
719
- // Check if old_text exists in the document
720
- if (!existingContent.includes(old_text)) {
721
- return {
722
- success: false,
723
- error: 'Could not find the specified text in the document. The text may have been paraphrased or changed.',
724
- fallback_markdown: new_text,
725
- suggestion: 'Here is the replacement text. You can copy it and manually edit the document at /context/architecture'
726
- };
727
- }
728
- // Replace the text
729
- const updatedContent = existingContent.replace(old_text, new_text);
730
- await fs.writeFile(filePath, updatedContent, 'utf-8');
731
- return {
732
- success: true,
733
- message: 'Replaced section in architecture context document'
734
- };
735
- }
736
- case 'update': {
737
- if (!content) {
738
- return { success: false, error: 'Content is required for update operation' };
739
- }
740
- await fs.mkdir(contextPath, { recursive: true });
741
- await fs.writeFile(filePath, content, 'utf-8');
742
- return {
743
- success: true,
744
- message: 'Updated architecture context document'
745
- };
746
- }
747
- default:
748
- return { success: false, error: `Unknown operation: ${operation}. Use 'get', 'append', 'replace_section', or 'update'.` };
749
- }
750
- }
751
- catch (error) {
752
- console.error('Error executing architecture context tool:', error);
753
- return { success: false, error: error.message || 'Architecture context tool execution failed' };
754
- }
656
+ // Agents context tool - read/write the agents definition document
657
+ async function executeAgentsContextToolDirect(args) {
658
+ return executeGenericContextToolDirect('agents', args);
755
659
  }
756
- // Role context tool - read/write the role definition document
757
- async function executeRoleContextToolDirect(args) {
660
+ // Team context tool - read/write the team definition document
661
+ async function executeTeamContextToolDirect(args) {
662
+ return executeGenericContextToolDirect('team', args);
663
+ }
664
+ // Memory context tool - read/write the memory document
665
+ async function executeMemoryContextToolDirect(args) {
666
+ return executeGenericContextToolDirect('memory', args);
667
+ }
668
+ async function executeGenericContextToolDirect(contextType, args) {
758
669
  const { operation, content, old_text, new_text } = args;
670
+ const label = contextType.charAt(0).toUpperCase() + contextType.slice(1);
759
671
  try {
760
672
  const contextPath = getContextBasePath();
761
- const filePath = join(contextPath, 'role.md');
673
+ const filePath = join(contextPath, `${contextType}.md`);
762
674
  switch (operation) {
763
675
  case 'get': {
764
676
  try {
@@ -771,7 +683,7 @@ async function executeRoleContextToolDirect(args) {
771
683
  frontmatter: parsed.data,
772
684
  raw: fileContent
773
685
  },
774
- message: 'Retrieved role context'
686
+ message: `Retrieved ${contextType} context`
775
687
  };
776
688
  }
777
689
  catch (err) {
@@ -779,7 +691,7 @@ async function executeRoleContextToolDirect(args) {
779
691
  return {
780
692
  success: true,
781
693
  data: { content: '', frontmatter: {}, raw: '' },
782
- message: 'Role context document does not exist yet. You can create it with an update operation.'
694
+ message: `${label} context document does not exist yet. You can create it with an update operation.`
783
695
  };
784
696
  }
785
697
  throw err;
@@ -790,7 +702,6 @@ async function executeRoleContextToolDirect(args) {
790
702
  return { success: false, error: 'Content is required for append operation' };
791
703
  }
792
704
  await fs.mkdir(contextPath, { recursive: true });
793
- // Read existing content if file exists
794
705
  let existingContent = '';
795
706
  try {
796
707
  existingContent = await fs.readFile(filePath, 'utf-8');
@@ -799,46 +710,42 @@ async function executeRoleContextToolDirect(args) {
799
710
  if (err.code !== 'ENOENT')
800
711
  throw err;
801
712
  }
802
- // Append new content with a newline separator
803
713
  const newContent = existingContent
804
714
  ? existingContent.trimEnd() + '\n\n' + content
805
715
  : content;
806
716
  await fs.writeFile(filePath, newContent, 'utf-8');
807
717
  return {
808
718
  success: true,
809
- message: 'Appended content to role context document'
719
+ message: `Appended content to ${contextType} context document`
810
720
  };
811
721
  }
812
722
  case 'replace_section': {
813
723
  if (!old_text || !new_text) {
814
724
  return { success: false, error: 'Both old_text and new_text are required for replace_section operation' };
815
725
  }
816
- // Read existing content
817
726
  let existingContent = '';
818
727
  try {
819
728
  existingContent = await fs.readFile(filePath, 'utf-8');
820
729
  }
821
730
  catch (err) {
822
731
  if (err.code === 'ENOENT') {
823
- return { success: false, error: 'Role context document does not exist. Use append or update to create it first.' };
732
+ return { success: false, error: `${label} context document does not exist. Use append or update to create it first.` };
824
733
  }
825
734
  throw err;
826
735
  }
827
- // Check if old_text exists in the document
828
736
  if (!existingContent.includes(old_text)) {
829
737
  return {
830
738
  success: false,
831
739
  error: 'Could not find the specified text in the document. The text may have been paraphrased or changed.',
832
740
  fallback_markdown: new_text,
833
- suggestion: 'Here is the replacement text. You can copy it and manually edit the document at /context/role'
741
+ suggestion: `Here is the replacement text. You can copy it and manually edit the document at /context/${contextType}`
834
742
  };
835
743
  }
836
- // Replace the text
837
744
  const updatedContent = existingContent.replace(old_text, new_text);
838
745
  await fs.writeFile(filePath, updatedContent, 'utf-8');
839
746
  return {
840
747
  success: true,
841
- message: 'Replaced section in role context document'
748
+ message: `Replaced section in ${contextType} context document`
842
749
  };
843
750
  }
844
751
  case 'update': {
@@ -849,7 +756,7 @@ async function executeRoleContextToolDirect(args) {
849
756
  await fs.writeFile(filePath, content, 'utf-8');
850
757
  return {
851
758
  success: true,
852
- message: 'Updated role context document'
759
+ message: `Updated ${contextType} context document`
853
760
  };
854
761
  }
855
762
  default:
@@ -857,8 +764,8 @@ async function executeRoleContextToolDirect(args) {
857
764
  }
858
765
  }
859
766
  catch (error) {
860
- console.error('Error executing role context tool:', error);
861
- return { success: false, error: error.message || 'Role context tool execution failed' };
767
+ console.error(`Error executing ${contextType} context tool:`, error);
768
+ return { success: false, error: error.message || `${label} context tool execution failed` };
862
769
  }
863
770
  }
864
771
  // ─── Resources Tool ─────────────────────────────────────────────────────────
@@ -1,6 +1,7 @@
1
1
  import { Hono } from 'hono';
2
2
  import crypto from 'crypto';
3
- import { getRepoStatus, listBranches, createBranch, deleteBranch, switchBranch, mergeBranch, pushCurrent, pullCurrent, listWorktrees, addWorktree, removeWorktree, commitInWorktree, pushWorktree, pullWorktree, checkRemoteAuth, getCredentialConfig, storeCredentials, getCommitDetails, getFileDiff, setRemoteUrl, } from '../../../../lib/git.js';
3
+ import { getRepoStatus, listBranches, createBranch, deleteBranch, switchBranch, mergeBranch, pushCurrent, pullCurrent, listWorktrees, addWorktree, removeWorktree, commitInWorktree, pushWorktree, pullWorktree, checkRemoteAuth, getCredentialConfig, storeCredentials, getCommitDetails, getFileDiff, setRemoteUrl, lsTree, getInstructionFiles, } from '../../../../lib/git.js';
4
+ import { INSTRUCTION_FILE_KINDS } from '@lovelybunch/types';
4
5
  import { saveGithubToken, clearGithubToken, readGithubToken, isGithubTokenValid } from '../../../../lib/github-token.js';
5
6
  import { createGithubAuthState, consumeGithubAuthState } from '../../../../lib/github-auth-state.js';
6
7
  import { resolveCoconutId, resolveControlPlaneUrl } from '../../../../lib/coconut-context.js';
@@ -674,4 +675,29 @@ app.get('/commits/:sha/files/:filepath{.+}', async (c) => {
674
675
  return c.json({ success: false, error: { message: e.message || 'Failed to get file diff' } }, 500);
675
676
  }
676
677
  });
678
+ // ls-tree
679
+ app.get('/ls-tree', async (c) => {
680
+ try {
681
+ const files = await lsTree();
682
+ return c.json({ success: true, data: files });
683
+ }
684
+ catch (e) {
685
+ return c.json({ success: false, error: { message: e.message || 'Failed to list tree' } }, 500);
686
+ }
687
+ });
688
+ // instruction-files
689
+ const VALID_FILTERS = new Set([...INSTRUCTION_FILE_KINDS, 'all']);
690
+ app.get('/instruction-files', async (c) => {
691
+ try {
692
+ const kind = (c.req.query('kind') || 'all');
693
+ if (!VALID_FILTERS.has(kind)) {
694
+ return c.json({ success: false, error: { message: `Invalid kind. Must be one of: ${[...VALID_FILTERS].join(', ')}` } }, 400);
695
+ }
696
+ const data = await getInstructionFiles(kind);
697
+ return c.json({ success: true, data });
698
+ }
699
+ catch (e) {
700
+ return c.json({ success: false, error: { message: e.message || 'Failed to retrieve instruction files' } }, 500);
701
+ }
702
+ });
677
703
  export default app;
@@ -33,23 +33,20 @@ export declare function GET(c: Context): Promise<(Response & import("hono").Type
33
33
  lastRunAt?: string;
34
34
  nextRunAt?: string;
35
35
  };
36
+ tags?: string[];
37
+ contextPaths?: string[];
38
+ agentId?: string;
39
+ agentIds?: string[];
40
+ mcpServers?: string[];
36
41
  runs: {
37
42
  id: string;
43
+ status: import("@lovelybunch/types").ScheduledJobRunStatus;
38
44
  jobId: string;
39
45
  trigger: import("@lovelybunch/types").ScheduledJobTrigger;
40
- status: import("@lovelybunch/types").ScheduledJobRunStatus;
41
46
  startedAt: string;
42
47
  finishedAt?: string;
43
- outputPath?: string;
44
- summary?: string;
45
48
  error?: string;
46
- cliCommand?: string;
47
49
  }[];
48
- tags?: string[];
49
- contextPaths?: string[];
50
- agentId?: string;
51
- agentIds?: string[];
52
- mcpServers?: string[];
53
50
  };
54
51
  }, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
55
52
  success: false;
@@ -97,23 +94,20 @@ export declare function PATCH(c: Context): Promise<(Response & import("hono").Ty
97
94
  lastRunAt?: string;
98
95
  nextRunAt?: string;
99
96
  };
97
+ tags?: string[];
98
+ contextPaths?: string[];
99
+ agentId?: string;
100
+ agentIds?: string[];
101
+ mcpServers?: string[];
100
102
  runs: {
101
103
  id: string;
104
+ status: import("@lovelybunch/types").ScheduledJobRunStatus;
102
105
  jobId: string;
103
106
  trigger: import("@lovelybunch/types").ScheduledJobTrigger;
104
- status: import("@lovelybunch/types").ScheduledJobRunStatus;
105
107
  startedAt: string;
106
108
  finishedAt?: string;
107
- outputPath?: string;
108
- summary?: string;
109
109
  error?: string;
110
- cliCommand?: string;
111
110
  }[];
112
- tags?: string[];
113
- contextPaths?: string[];
114
- agentId?: string;
115
- agentIds?: string[];
116
- mcpServers?: string[];
117
111
  };
118
112
  }, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
119
113
  success: false;
@@ -1,6 +1,6 @@
1
1
  import { JobStore } from '../../../../../lib/jobs/job-store.js';
2
2
  import { getGlobalJobScheduler } from '../../../../../lib/jobs/global-job-scheduler.js';
3
- import { normalizeSchedule, normalizeStatus, } from '../route.js';
3
+ import { normalizeSchedule, normalizeStatus, withLightweightRuns, } from '../route.js';
4
4
  const store = new JobStore();
5
5
  const scheduler = getGlobalJobScheduler();
6
6
  function normalizeScheduleUpdate(current, incoming) {
@@ -32,7 +32,7 @@ export async function GET(c) {
32
32
  }
33
33
  }, 404);
34
34
  }
35
- return c.json({ success: true, data: job });
35
+ return c.json({ success: true, data: withLightweightRuns(job) });
36
36
  }
37
37
  catch (error) {
38
38
  console.error('Failed to fetch job:', error);
@@ -101,7 +101,7 @@ export async function PATCH(c) {
101
101
  const reloaded = await store.getJob(updated.id);
102
102
  return c.json({
103
103
  success: true,
104
- data: reloaded ?? updated
104
+ data: withLightweightRuns(reloaded ?? updated)
105
105
  });
106
106
  }
107
107
  catch (error) {
@@ -1,7 +1,13 @@
1
1
  import { Context } from 'hono';
2
- import { DayOfWeek, ScheduledJobSchedule, ScheduledJobStatus } from '@lovelybunch/types';
2
+ import { DayOfWeek, ScheduledJobRun, ScheduledJobRunMeta, ScheduledJobSchedule, ScheduledJobStatus } from '@lovelybunch/types';
3
3
  export declare function normalizeSchedule(schedule?: Partial<ScheduledJobSchedule>): ScheduledJobSchedule;
4
4
  export declare function normalizeStatus(status?: ScheduledJobStatus): ScheduledJobStatus;
5
+ export declare function toRunMeta(run: ScheduledJobRun): ScheduledJobRunMeta;
6
+ export declare function withLightweightRuns<T extends {
7
+ runs: ScheduledJobRun[];
8
+ }>(job: T): Omit<T, 'runs'> & {
9
+ runs: ScheduledJobRunMeta[];
10
+ };
5
11
  export declare function GET(c: Context): Promise<(Response & import("hono").TypedResponse<{
6
12
  success: true;
7
13
  data: {
@@ -29,23 +35,20 @@ export declare function GET(c: Context): Promise<(Response & import("hono").Type
29
35
  lastRunAt?: string;
30
36
  nextRunAt?: string;
31
37
  };
38
+ tags?: string[];
39
+ contextPaths?: string[];
40
+ agentId?: string;
41
+ agentIds?: string[];
42
+ mcpServers?: string[];
32
43
  runs: {
33
44
  id: string;
45
+ status: import("@lovelybunch/types").ScheduledJobRunStatus;
34
46
  jobId: string;
35
47
  trigger: import("@lovelybunch/types").ScheduledJobTrigger;
36
- status: import("@lovelybunch/types").ScheduledJobRunStatus;
37
48
  startedAt: string;
38
49
  finishedAt?: string;
39
- outputPath?: string;
40
- summary?: string;
41
50
  error?: string;
42
- cliCommand?: string;
43
51
  }[];
44
- tags?: string[];
45
- contextPaths?: string[];
46
- agentId?: string;
47
- agentIds?: string[];
48
- mcpServers?: string[];
49
52
  }[];
50
53
  }, import("hono/utils/http-status").ContentfulStatusCode, "json">) | (Response & import("hono").TypedResponse<{
51
54
  success: false;
@@ -87,23 +90,20 @@ export declare function POST(c: Context): Promise<(Response & import("hono").Typ
87
90
  lastRunAt?: string;
88
91
  nextRunAt?: string;
89
92
  };
93
+ tags?: string[];
94
+ contextPaths?: string[];
95
+ agentId?: string;
96
+ agentIds?: string[];
97
+ mcpServers?: string[];
90
98
  runs: {
91
99
  id: string;
100
+ status: import("@lovelybunch/types").ScheduledJobRunStatus;
92
101
  jobId: string;
93
102
  trigger: import("@lovelybunch/types").ScheduledJobTrigger;
94
- status: import("@lovelybunch/types").ScheduledJobRunStatus;
95
103
  startedAt: string;
96
104
  finishedAt?: string;
97
- outputPath?: string;
98
- summary?: string;
99
105
  error?: string;
100
- cliCommand?: string;
101
106
  }[];
102
- tags?: string[];
103
- contextPaths?: string[];
104
- agentId?: string;
105
- agentIds?: string[];
106
- mcpServers?: string[];
107
107
  };
108
108
  }, 201, "json">) | (Response & import("hono").TypedResponse<{
109
109
  success: false;