pybao-cli 1.5.29 → 1.5.32

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 (158) hide show
  1. package/dist/REPL-KJ3N4IFV.js +51 -0
  2. package/dist/{acp-P5ZO4W4V.js → acp-UXL5XBSR.js} +31 -33
  3. package/dist/{acp-P5ZO4W4V.js.map → acp-UXL5XBSR.js.map} +1 -1
  4. package/dist/{agentsValidate-UORRIRNG.js → agentsValidate-RLWHCBOX.js} +7 -7
  5. package/dist/{ask-6PKW23F4.js → ask-C67K5D7C.js} +30 -30
  6. package/dist/{autoUpdater-REVFS3DJ.js → autoUpdater-X44IPDBE.js} +3 -3
  7. package/dist/chunk-2DKSGO53.js.map +7 -0
  8. package/dist/{chunk-VT2R63Q4.js → chunk-3Y5UWA23.js} +3 -3
  9. package/dist/{chunk-RRKAPFMQ.js → chunk-45LEZSMZ.js} +29 -1
  10. package/dist/chunk-45LEZSMZ.js.map +7 -0
  11. package/dist/{chunk-LDMNEBPV.js → chunk-6AV7PM4P.js} +1 -1
  12. package/dist/{chunk-RYFL7HVW.js → chunk-6FQ4JID2.js} +1 -1
  13. package/dist/{chunk-VKVOEWWH.js → chunk-AXIYIPYG.js} +1 -1
  14. package/dist/{chunk-X5B7NYPT.js → chunk-D6WY3ZR7.js} +3 -3
  15. package/dist/{chunk-EXFO5B6D.js → chunk-D7U4Q67X.js} +3 -3
  16. package/dist/{chunk-UVHZFFMY.js → chunk-DQ7NDNMF.js} +1 -1
  17. package/dist/{chunk-Z7VU67EA.js → chunk-GQYWLHCJ.js} +2 -2
  18. package/dist/{chunk-GZF3L435.js → chunk-HJWLDGVX.js} +1 -1
  19. package/dist/{chunk-VFVPMA6Q.js → chunk-ISDPU2GO.js} +3 -3
  20. package/dist/{chunk-6RLACN3L.js → chunk-IU4SQDR3.js} +4 -4
  21. package/dist/{chunk-VUSYHIJA.js → chunk-J3NNKXKE.js} +1 -1
  22. package/dist/{chunk-PHX4SQ7J.js → chunk-K4427JEK.js} +1 -1
  23. package/dist/{chunk-S2BNVKP7.js → chunk-MVVN7JLM.js} +4 -4
  24. package/dist/{chunk-ANFFZEQ4.js → chunk-NDOTVAEH.js} +19420 -13826
  25. package/dist/chunk-NDOTVAEH.js.map +7 -0
  26. package/dist/{chunk-3ZMHX5VJ.js → chunk-NLKNOEUD.js} +6 -6
  27. package/dist/{chunk-S6PNHDVA.js → chunk-OA35DSH2.js} +3 -3
  28. package/dist/{chunk-ZD4WAFPC.js → chunk-ONE5YOHE.js} +4 -4
  29. package/dist/{chunk-F3Z46DHI.js → chunk-PEGDADDZ.js} +1 -1
  30. package/dist/{chunk-2ATYG2IL.js → chunk-PLWFF7VP.js} +2 -2
  31. package/dist/{chunk-UKI67LME.js → chunk-PU3HW35O.js} +2 -2
  32. package/dist/{chunk-VTTBOI63.js → chunk-PUB5HDZO.js} +1 -1
  33. package/dist/{chunk-X475W4F6.js → chunk-Q5BDM5KT.js} +4 -4
  34. package/dist/{chunk-P2RR7GRN.js → chunk-QNM2GKTM.js} +1 -1
  35. package/dist/{chunk-DG6FK5XN.js → chunk-RDUARMFW.js} +3 -3
  36. package/dist/{chunk-QV6RSIUH.js → chunk-RGFN73DR.js} +2 -2
  37. package/dist/{chunk-VGAD66UR.js → chunk-V4SUJ2V2.js} +3 -3
  38. package/dist/{chunk-UIMY5WKT.js → chunk-VFWKWIQT.js} +2 -2
  39. package/dist/{chunk-3T43I32F.js → chunk-WLOWSFPM.js} +2 -2
  40. package/dist/{chunk-MOUPKWOG.js → chunk-Z6Y7EU6B.js} +1 -1
  41. package/dist/{cli-BZJPXPZE.js → cli-DZR5CAM4.js} +98 -100
  42. package/dist/{cli-BZJPXPZE.js.map → cli-DZR5CAM4.js.map} +1 -1
  43. package/dist/commands-NVO6FPF7.js +55 -0
  44. package/dist/{config-RP6BU5AE.js → config-RYWXMBP4.js} +4 -4
  45. package/dist/{context-GJ6HAJOG.js → context-SH7HCHJQ.js} +6 -6
  46. package/dist/{conversationPersistence-VV4QBHTG.js → conversationPersistence-7KLY4QHY.js} +3 -3
  47. package/dist/{conversationTracker-Q5PTDRTA.js → conversationTracker-QREF3WZI.js} +4 -4
  48. package/dist/{customCommands-CJVAFQXD.js → customCommands-RTGRJK2C.js} +4 -4
  49. package/dist/{env-XUAURZLQ.js → env-2U26GQKV.js} +2 -2
  50. package/dist/{file-ZSFDAM3N.js → file-GNARUL6B.js} +4 -4
  51. package/dist/index.js +3 -3
  52. package/dist/{llm-6JZTYBUT.js → llm-SCPITENC.js} +32 -32
  53. package/dist/{llmLazy-ZAUEBLRK.js → llmLazy-TBFX5PBQ.js} +1 -1
  54. package/dist/{loader-CNLAOYXD.js → loader-UII7EUHF.js} +4 -4
  55. package/dist/{lsp-SQBHGEL7.js → lsp-PYFU2XZV.js} +6 -6
  56. package/dist/{lspAnchor-JGLGRBDS.js → lspAnchor-FLLDVW2Y.js} +6 -6
  57. package/dist/{mcp-WB7F35XU.js → mcp-ZFR67MRU.js} +7 -7
  58. package/dist/{mentionProcessor-UMQ5CCMP.js → mentionProcessor-X77Y2FRT.js} +5 -5
  59. package/dist/{messages-LNHBEQAU.js → messages-WKBPEW2N.js} +1 -1
  60. package/dist/{model-5HD47QJV.js → model-4OWKZZE6.js} +5 -5
  61. package/dist/{openai-KANS5SWN.js → openai-GYV4LVYO.js} +5 -5
  62. package/dist/{outputStyles-PHJZQ2XP.js → outputStyles-BJLOS5C6.js} +4 -4
  63. package/dist/{pluginRuntime-5LCYFDSX.js → pluginRuntime-676XRWKB.js} +6 -6
  64. package/dist/{pluginValidation-WRU3V2ZI.js → pluginValidation-WAZLL2MX.js} +6 -6
  65. package/dist/prompts-NEANETDJ.js +57 -0
  66. package/dist/{pybAgentSessionLoad-PMGRB2CW.js → pybAgentSessionLoad-MOOILQTX.js} +4 -4
  67. package/dist/{pybAgentSessionResume-QVCNVEIE.js → pybAgentSessionResume-5GBSUAJH.js} +4 -4
  68. package/dist/{pybAgentStreamJsonSession-EDKNGJOK.js → pybAgentStreamJsonSession-VWJ6GCPV.js} +1 -1
  69. package/dist/{pybHooks-52SL7GAR.js → pybHooks-H4VS6TMX.js} +4 -4
  70. package/dist/query-7M27UWQO.js +55 -0
  71. package/dist/{registry-5722DCKT.js → registry-EDQK3LVJ.js} +5 -5
  72. package/dist/{ripgrep-UCBNNBWG.js → ripgrep-DKWSJ3O5.js} +3 -3
  73. package/dist/{skillMarketplace-WRXKQVZL.js → skillMarketplace-625WHVAK.js} +3 -3
  74. package/dist/{state-PQJJ3JAM.js → state-IQ3EEXFN.js} +2 -2
  75. package/dist/{theme-ATOMVV5D.js → theme-2YLGJWKG.js} +5 -5
  76. package/dist/{toolPermissionSettings-LESRBYOT.js → toolPermissionSettings-HJVDRS2G.js} +6 -6
  77. package/dist/tools-HECZM56A.js +55 -0
  78. package/dist/{userInput-2DHIVUIB.js → userInput-XLZJU666.js} +32 -32
  79. package/package.json +29 -1
  80. package/dist/REPL-CQPAOTYW.js +0 -51
  81. package/dist/chunk-ANFFZEQ4.js.map +0 -7
  82. package/dist/chunk-BKCAVW2G.js.map +0 -7
  83. package/dist/chunk-KULZBCG2.js +0 -4510
  84. package/dist/chunk-KULZBCG2.js.map +0 -7
  85. package/dist/chunk-RRKAPFMQ.js.map +0 -7
  86. package/dist/commands-EPTHVYFE.js +0 -55
  87. package/dist/prompts-HNG6Q4AE.js +0 -57
  88. package/dist/query-7VSQAEGO.js +0 -55
  89. package/dist/tools-YP6KOB7Y.js +0 -56
  90. /package/dist/{REPL-CQPAOTYW.js.map → REPL-KJ3N4IFV.js.map} +0 -0
  91. /package/dist/{agentsValidate-UORRIRNG.js.map → agentsValidate-RLWHCBOX.js.map} +0 -0
  92. /package/dist/{ask-6PKW23F4.js.map → ask-C67K5D7C.js.map} +0 -0
  93. /package/dist/{autoUpdater-REVFS3DJ.js.map → autoUpdater-X44IPDBE.js.map} +0 -0
  94. /package/dist/{chunk-BKCAVW2G.js → chunk-2DKSGO53.js} +0 -0
  95. /package/dist/{chunk-VT2R63Q4.js.map → chunk-3Y5UWA23.js.map} +0 -0
  96. /package/dist/{chunk-LDMNEBPV.js.map → chunk-6AV7PM4P.js.map} +0 -0
  97. /package/dist/{chunk-RYFL7HVW.js.map → chunk-6FQ4JID2.js.map} +0 -0
  98. /package/dist/{chunk-VKVOEWWH.js.map → chunk-AXIYIPYG.js.map} +0 -0
  99. /package/dist/{chunk-X5B7NYPT.js.map → chunk-D6WY3ZR7.js.map} +0 -0
  100. /package/dist/{chunk-EXFO5B6D.js.map → chunk-D7U4Q67X.js.map} +0 -0
  101. /package/dist/{chunk-UVHZFFMY.js.map → chunk-DQ7NDNMF.js.map} +0 -0
  102. /package/dist/{chunk-Z7VU67EA.js.map → chunk-GQYWLHCJ.js.map} +0 -0
  103. /package/dist/{chunk-GZF3L435.js.map → chunk-HJWLDGVX.js.map} +0 -0
  104. /package/dist/{chunk-VFVPMA6Q.js.map → chunk-ISDPU2GO.js.map} +0 -0
  105. /package/dist/{chunk-6RLACN3L.js.map → chunk-IU4SQDR3.js.map} +0 -0
  106. /package/dist/{chunk-VUSYHIJA.js.map → chunk-J3NNKXKE.js.map} +0 -0
  107. /package/dist/{chunk-PHX4SQ7J.js.map → chunk-K4427JEK.js.map} +0 -0
  108. /package/dist/{chunk-S2BNVKP7.js.map → chunk-MVVN7JLM.js.map} +0 -0
  109. /package/dist/{chunk-3ZMHX5VJ.js.map → chunk-NLKNOEUD.js.map} +0 -0
  110. /package/dist/{chunk-S6PNHDVA.js.map → chunk-OA35DSH2.js.map} +0 -0
  111. /package/dist/{chunk-ZD4WAFPC.js.map → chunk-ONE5YOHE.js.map} +0 -0
  112. /package/dist/{chunk-F3Z46DHI.js.map → chunk-PEGDADDZ.js.map} +0 -0
  113. /package/dist/{chunk-2ATYG2IL.js.map → chunk-PLWFF7VP.js.map} +0 -0
  114. /package/dist/{chunk-UKI67LME.js.map → chunk-PU3HW35O.js.map} +0 -0
  115. /package/dist/{chunk-VTTBOI63.js.map → chunk-PUB5HDZO.js.map} +0 -0
  116. /package/dist/{chunk-X475W4F6.js.map → chunk-Q5BDM5KT.js.map} +0 -0
  117. /package/dist/{chunk-P2RR7GRN.js.map → chunk-QNM2GKTM.js.map} +0 -0
  118. /package/dist/{chunk-DG6FK5XN.js.map → chunk-RDUARMFW.js.map} +0 -0
  119. /package/dist/{chunk-QV6RSIUH.js.map → chunk-RGFN73DR.js.map} +0 -0
  120. /package/dist/{chunk-VGAD66UR.js.map → chunk-V4SUJ2V2.js.map} +0 -0
  121. /package/dist/{chunk-UIMY5WKT.js.map → chunk-VFWKWIQT.js.map} +0 -0
  122. /package/dist/{chunk-3T43I32F.js.map → chunk-WLOWSFPM.js.map} +0 -0
  123. /package/dist/{chunk-MOUPKWOG.js.map → chunk-Z6Y7EU6B.js.map} +0 -0
  124. /package/dist/{commands-EPTHVYFE.js.map → commands-NVO6FPF7.js.map} +0 -0
  125. /package/dist/{config-RP6BU5AE.js.map → config-RYWXMBP4.js.map} +0 -0
  126. /package/dist/{context-GJ6HAJOG.js.map → context-SH7HCHJQ.js.map} +0 -0
  127. /package/dist/{conversationPersistence-VV4QBHTG.js.map → conversationPersistence-7KLY4QHY.js.map} +0 -0
  128. /package/dist/{conversationTracker-Q5PTDRTA.js.map → conversationTracker-QREF3WZI.js.map} +0 -0
  129. /package/dist/{customCommands-CJVAFQXD.js.map → customCommands-RTGRJK2C.js.map} +0 -0
  130. /package/dist/{env-XUAURZLQ.js.map → env-2U26GQKV.js.map} +0 -0
  131. /package/dist/{file-ZSFDAM3N.js.map → file-GNARUL6B.js.map} +0 -0
  132. /package/dist/{llm-6JZTYBUT.js.map → llm-SCPITENC.js.map} +0 -0
  133. /package/dist/{llmLazy-ZAUEBLRK.js.map → llmLazy-TBFX5PBQ.js.map} +0 -0
  134. /package/dist/{loader-CNLAOYXD.js.map → loader-UII7EUHF.js.map} +0 -0
  135. /package/dist/{lsp-SQBHGEL7.js.map → lsp-PYFU2XZV.js.map} +0 -0
  136. /package/dist/{lspAnchor-JGLGRBDS.js.map → lspAnchor-FLLDVW2Y.js.map} +0 -0
  137. /package/dist/{mcp-WB7F35XU.js.map → mcp-ZFR67MRU.js.map} +0 -0
  138. /package/dist/{mentionProcessor-UMQ5CCMP.js.map → mentionProcessor-X77Y2FRT.js.map} +0 -0
  139. /package/dist/{messages-LNHBEQAU.js.map → messages-WKBPEW2N.js.map} +0 -0
  140. /package/dist/{model-5HD47QJV.js.map → model-4OWKZZE6.js.map} +0 -0
  141. /package/dist/{openai-KANS5SWN.js.map → openai-GYV4LVYO.js.map} +0 -0
  142. /package/dist/{outputStyles-PHJZQ2XP.js.map → outputStyles-BJLOS5C6.js.map} +0 -0
  143. /package/dist/{pluginRuntime-5LCYFDSX.js.map → pluginRuntime-676XRWKB.js.map} +0 -0
  144. /package/dist/{pluginValidation-WRU3V2ZI.js.map → pluginValidation-WAZLL2MX.js.map} +0 -0
  145. /package/dist/{prompts-HNG6Q4AE.js.map → prompts-NEANETDJ.js.map} +0 -0
  146. /package/dist/{pybAgentSessionLoad-PMGRB2CW.js.map → pybAgentSessionLoad-MOOILQTX.js.map} +0 -0
  147. /package/dist/{pybAgentSessionResume-QVCNVEIE.js.map → pybAgentSessionResume-5GBSUAJH.js.map} +0 -0
  148. /package/dist/{pybAgentStreamJsonSession-EDKNGJOK.js.map → pybAgentStreamJsonSession-VWJ6GCPV.js.map} +0 -0
  149. /package/dist/{pybHooks-52SL7GAR.js.map → pybHooks-H4VS6TMX.js.map} +0 -0
  150. /package/dist/{query-7VSQAEGO.js.map → query-7M27UWQO.js.map} +0 -0
  151. /package/dist/{registry-5722DCKT.js.map → registry-EDQK3LVJ.js.map} +0 -0
  152. /package/dist/{ripgrep-UCBNNBWG.js.map → ripgrep-DKWSJ3O5.js.map} +0 -0
  153. /package/dist/{skillMarketplace-WRXKQVZL.js.map → skillMarketplace-625WHVAK.js.map} +0 -0
  154. /package/dist/{state-PQJJ3JAM.js.map → state-IQ3EEXFN.js.map} +0 -0
  155. /package/dist/{theme-ATOMVV5D.js.map → theme-2YLGJWKG.js.map} +0 -0
  156. /package/dist/{toolPermissionSettings-LESRBYOT.js.map → toolPermissionSettings-HJVDRS2G.js.map} +0 -0
  157. /package/dist/{tools-YP6KOB7Y.js.map → tools-HECZM56A.js.map} +0 -0
  158. /package/dist/{userInput-2DHIVUIB.js.map → userInput-XLZJU666.js.map} +0 -0
@@ -1,4510 +0,0 @@
1
- import { createRequire as __pybCreateRequire } from "node:module";
2
- const require = __pybCreateRequire(import.meta.url);
3
- import {
4
- AskUserQuestionTool,
5
- BashTool,
6
- Cost,
7
- FileEditTool,
8
- FileReadTool,
9
- FileWriteTool,
10
- GlobTool,
11
- GrepTool,
12
- KillShellTool,
13
- NotebookEditTool,
14
- SkillTool,
15
- SlashCommandTool,
16
- TASK_FORK_CONTEXT_POLICY,
17
- WebFetchTool,
18
- WebSearchTool,
19
- applyMarkdown,
20
- buildTaskAsyncPolicy,
21
- buildTaskGraph,
22
- countTokens,
23
- createTask,
24
- findTaskCycles,
25
- generateTaskListId,
26
- getAgentMemoryDir,
27
- getAgentPrompt,
28
- getMaxThinkingTokens,
29
- getProjectMemoryDir,
30
- getTask,
31
- getTaskListPaths,
32
- hasPermissionsToUseTool,
33
- hasReadPermission,
34
- hasWritePermission,
35
- listTasks,
36
- query,
37
- recordFileEdit,
38
- resolveAutoMemoryFlags,
39
- resolveMemoryPath,
40
- resolveProjectId,
41
- runWithTaskListEnv,
42
- runWithTaskListId,
43
- updateTask
44
- } from "./chunk-ANFFZEQ4.js";
45
- import {
46
- getCurrentSessionId
47
- } from "./chunk-XKYHFZEC.js";
48
- import {
49
- queryLLM
50
- } from "./chunk-6RLACN3L.js";
51
- import {
52
- FallbackToolUseRejectedMessage,
53
- MCPTool,
54
- getClients,
55
- getMCPTools
56
- } from "./chunk-S2BNVKP7.js";
57
- import {
58
- emitReminderEvent
59
- } from "./chunk-QV6RSIUH.js";
60
- import {
61
- getActiveAgents,
62
- getAgentByType,
63
- getAvailableAgentTypes
64
- } from "./chunk-UKI67LME.js";
65
- import {
66
- INTERRUPT_MESSAGE,
67
- createAssistantMessage,
68
- createUserMessageFromBlocks,
69
- createUserMessageFromText,
70
- getLastAssistantMessageId
71
- } from "./chunk-GZF3L435.js";
72
- import {
73
- formatDuration,
74
- formatNumber
75
- } from "./chunk-OUXHGDLH.js";
76
- import {
77
- getAbsolutePath
78
- } from "./chunk-3T43I32F.js";
79
- import {
80
- LspFacade,
81
- formatDiagnosticsPretty
82
- } from "./chunk-X475W4F6.js";
83
- import {
84
- getModelManager
85
- } from "./chunk-S6PNHDVA.js";
86
- import {
87
- getContext
88
- } from "./chunk-ZD4WAFPC.js";
89
- import {
90
- getTheme
91
- } from "./chunk-F3Z46DHI.js";
92
- import {
93
- debug
94
- } from "./chunk-VTTBOI63.js";
95
- import {
96
- BunShell,
97
- MEMORY_DIR,
98
- getCwd,
99
- getMessagesPath,
100
- getNextAvailableLogSidechainNumber,
101
- getSessionRoot,
102
- logError,
103
- overwriteLog,
104
- readTaskOutput,
105
- resolveXdgDataPath
106
- } from "./chunk-VUSYHIJA.js";
107
-
108
- // src/tools/index.ts
109
- import { memoize as memoize2 } from "lodash-es";
110
-
111
- // src/tools/ai/AskExpertModelTool/AskExpertModelTool.tsx
112
- import * as React from "react";
113
- import { Box, Text } from "ink";
114
- import { z } from "zod";
115
-
116
- // src/utils/session/expertChatStorage.ts
117
- import { existsSync, readFileSync, writeFileSync, mkdirSync } from "fs";
118
- import { join } from "path";
119
- import { randomUUID } from "crypto";
120
- function getExpertChatDirectory() {
121
- const override = process.env.ANYKODE_CONFIG_DIR?.trim();
122
- const expertChatDir = override ? join(override, "expert-chats") : resolveXdgDataPath("expert-chats");
123
- if (!existsSync(expertChatDir)) {
124
- mkdirSync(expertChatDir, { recursive: true });
125
- }
126
- return expertChatDir;
127
- }
128
- function getSessionFilePath(sessionId) {
129
- return join(getExpertChatDirectory(), `${sessionId}.json`);
130
- }
131
- function createExpertChatSession(expertModel) {
132
- const sessionId = randomUUID().slice(0, 5);
133
- const session = {
134
- sessionId,
135
- expertModel,
136
- messages: [],
137
- createdAt: Date.now(),
138
- lastUpdated: Date.now()
139
- };
140
- saveExpertChatSession(session);
141
- return session;
142
- }
143
- function loadExpertChatSession(sessionId) {
144
- const filePath = getSessionFilePath(sessionId);
145
- if (!existsSync(filePath)) {
146
- return null;
147
- }
148
- try {
149
- const content = readFileSync(filePath, "utf-8");
150
- return JSON.parse(content);
151
- } catch (error) {
152
- logError(error);
153
- debug.warn("EXPERT_CHAT_SESSION_LOAD_FAILED", {
154
- sessionId,
155
- error: error instanceof Error ? error.message : String(error)
156
- });
157
- return null;
158
- }
159
- }
160
- function saveExpertChatSession(session) {
161
- const filePath = getSessionFilePath(session.sessionId);
162
- try {
163
- session.lastUpdated = Date.now();
164
- writeFileSync(filePath, JSON.stringify(session, null, 2), "utf-8");
165
- } catch (error) {
166
- logError(error);
167
- debug.warn("EXPERT_CHAT_SESSION_SAVE_FAILED", {
168
- sessionId: session.sessionId,
169
- error: error instanceof Error ? error.message : String(error)
170
- });
171
- throw error;
172
- }
173
- }
174
- function addMessageToSession(sessionId, role, content) {
175
- const session = loadExpertChatSession(sessionId);
176
- if (!session) {
177
- return null;
178
- }
179
- session.messages.push({ role, content });
180
- saveExpertChatSession(session);
181
- return session;
182
- }
183
- function getSessionMessages(sessionId) {
184
- const session = loadExpertChatSession(sessionId);
185
- return session?.messages || [];
186
- }
187
-
188
- // src/tools/ai/AskExpertModelTool/AskExpertModelTool.tsx
189
- var inputSchema = z.strictObject({
190
- question: z.string().describe(
191
- "COMPLETE SELF-CONTAINED QUESTION: Must include full background context, relevant details, and a clear independent question. The expert model will receive ONLY this content with no access to previous conversation or external context. Structure as: 1) Background/Context 2) Specific situation/problem 3) Clear question. Ensure the expert can fully understand and respond without needing additional information."
192
- ),
193
- expert_model: z.string().describe(
194
- "The expert model to use (e.g., gpt-5, claude-3-5-sonnet-20241022)"
195
- ),
196
- chat_session_id: z.string().describe(
197
- 'Chat session ID: use "new" for new session or existing session ID'
198
- )
199
- });
200
- var AskExpertModelTool = {
201
- name: "AskExpertModel",
202
- async description() {
203
- return "Consult external AI models for expert opinions and analysis";
204
- },
205
- async prompt() {
206
- return `Ask a question to a specific external AI model for expert analysis.
207
-
208
- This tool allows you to consult different AI models for their unique perspectives and expertise.
209
-
210
- CRITICAL REQUIREMENT FOR QUESTION PARAMETER:
211
- The question MUST be completely self-contained and include:
212
- 1. FULL BACKGROUND CONTEXT - All relevant information the expert needs
213
- 2. SPECIFIC SITUATION - Clear description of the current scenario/problem
214
- 3. INDEPENDENT QUESTION - What exactly you want the expert to analyze/answer
215
-
216
- The expert model receives ONLY your question content with NO access to:
217
- - Previous conversation history (unless using existing session)
218
- - Current codebase or file context
219
- - User's current task or project details
220
-
221
- IMPORTANT: This tool is for asking questions to models, not for task execution.
222
- - Use when you need a specific model's opinion or analysis
223
- - Use when you want to compare different models' responses
224
- - Use the @ask-[model] format when available
225
-
226
- The expert_model parameter accepts:
227
- - OpenAI: gpt-4, gpt-5, o1-preview
228
- - Messages API: claude-3-5-sonnet, claude-3-opus
229
- - Others: kimi, gemini-pro, mixtral
230
-
231
- Example of well-structured question:
232
- "Background: I'm working on a React TypeScript application with performance issues. The app renders a large list of 10,000 items using a simple map() function, causing UI freezing.
233
-
234
- Current situation: Users report 3-5 second delays when scrolling through the list. The component re-renders the entire list on every state change.
235
-
236
- Question: What are the most effective React optimization techniques for handling large lists, and how should I prioritize implementing virtualization vs memoization vs other approaches?"`;
237
- },
238
- isReadOnly() {
239
- return true;
240
- },
241
- isConcurrencySafe() {
242
- return true;
243
- },
244
- inputSchema,
245
- userFacingName() {
246
- return "AskExpertModel";
247
- },
248
- async isEnabled() {
249
- return true;
250
- },
251
- needsPermissions() {
252
- return false;
253
- },
254
- async validateInput({ question, expert_model, chat_session_id }, context) {
255
- if (!question.trim()) {
256
- return { result: false, message: "Question cannot be empty" };
257
- }
258
- if (!expert_model.trim()) {
259
- return { result: false, message: "Expert model must be specified" };
260
- }
261
- if (!chat_session_id.trim()) {
262
- return {
263
- result: false,
264
- message: 'Chat session ID must be specified (use "new" for new session)'
265
- };
266
- }
267
- try {
268
- const modelManager = getModelManager();
269
- let currentModel;
270
- if (context?.agentId && context?.options?.model) {
271
- currentModel = context.options.model;
272
- } else {
273
- currentModel = modelManager.getModelName("main") || "";
274
- }
275
- const normalizedExpert = expert_model.toLowerCase().replace(/[^a-z0-9]/g, "");
276
- const normalizedCurrent = currentModel.toLowerCase().replace(/[^a-z0-9]/g, "");
277
- if (normalizedExpert === normalizedCurrent) {
278
- return {
279
- result: false,
280
- message: `You are already running as ${currentModel}. Consulting the same model would be redundant. Please choose a different model or handle the task directly.`
281
- };
282
- }
283
- } catch (e) {
284
- debug.error("AskExpertModel", {
285
- message: "Could not determine current model",
286
- error: e
287
- });
288
- }
289
- try {
290
- const modelManager = getModelManager();
291
- const modelResolution = modelManager.resolveModelWithInfo(expert_model);
292
- if (!modelResolution.success) {
293
- const availableModels = modelManager.getAllAvailableModelNames();
294
- if (availableModels.length > 0) {
295
- return {
296
- result: false,
297
- message: `Model '${expert_model}' is not configured. Available models: ${availableModels.join(", ")}. Check if any available model closely matches the user's request (e.g., 'kimi' matches 'kimi-k2-0711-preview'). If there's a strong match, auto retry using the correct model name. If no close match exists, inform the user that '${expert_model}' needs to be configured using /model command.`
298
- };
299
- } else {
300
- return {
301
- result: false,
302
- message: `Model '${expert_model}' not found and no models are currently configured in the system. Inform the user that models need to be configured first using the /model command.`
303
- };
304
- }
305
- }
306
- } catch (error) {
307
- logError(error);
308
- return {
309
- result: false,
310
- message: `Failed to validate expert model '${expert_model}'. Please check your model configuration.`
311
- };
312
- }
313
- return { result: true };
314
- },
315
- renderToolUseMessage({ question, expert_model, chat_session_id }, { verbose }) {
316
- if (!question || !expert_model) return null;
317
- const isNewSession = chat_session_id === "new";
318
- const sessionDisplay = isNewSession ? "new session" : `session ${chat_session_id.substring(0, 5)}...`;
319
- const theme = getTheme();
320
- if (verbose) {
321
- return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, { bold: true, color: "yellow" }, expert_model), /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, sessionDisplay), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: theme.text }, question.length > 300 ? question.substring(0, 300) + "..." : question)));
322
- }
323
- return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, { bold: true, color: "yellow" }, expert_model, " "), /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText, dimColor: true }, "(", sessionDisplay, ")"));
324
- },
325
- renderToolResultMessage(content) {
326
- const verbose = true;
327
- const theme = getTheme();
328
- if (typeof content === "object" && content && "expertAnswer" in content) {
329
- const expertResult = content;
330
- const isError = expertResult.expertAnswer.startsWith("Error") || expertResult.expertAnswer.includes("failed");
331
- const isInterrupted = expertResult.chatSessionId === "interrupted";
332
- if (isInterrupted) {
333
- return /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, "Consultation interrupted"));
334
- }
335
- const answerText = verbose ? expertResult.expertAnswer.trim() : expertResult.expertAnswer.length > 500 ? expertResult.expertAnswer.substring(0, 500) + "..." : expertResult.expertAnswer.trim();
336
- if (isError) {
337
- return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, { color: "red" }, answerText));
338
- }
339
- return /* @__PURE__ */ React.createElement(Box, { flexDirection: "column" }, /* @__PURE__ */ React.createElement(Text, { bold: true, color: theme.text }, "Response from ", expertResult.expertModelName, ":"), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: theme.text }, applyMarkdown(answerText))), /* @__PURE__ */ React.createElement(Box, { marginTop: 1 }, /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText, dimColor: true }, "Session: ", expertResult.chatSessionId.substring(0, 8))));
340
- }
341
- return /* @__PURE__ */ React.createElement(Box, { flexDirection: "row" }, /* @__PURE__ */ React.createElement(Text, { color: theme.secondaryText }, "Consultation completed"));
342
- },
343
- renderResultForAssistant(output) {
344
- return `[Expert consultation completed]
345
- Expert Model: ${output.expertModelName}
346
- Session ID: ${output.chatSessionId}
347
- To continue this conversation with context preservation, use this Session ID in your next AskExpertModel call to maintain the full conversation history and context.
348
-
349
- ${output.expertAnswer}`;
350
- },
351
- renderToolUseRejectedMessage() {
352
- return /* @__PURE__ */ React.createElement(FallbackToolUseRejectedMessage, null);
353
- },
354
- async *call({ question, expert_model, chat_session_id }, { abortController, readFileTimestamps }) {
355
- const expertModel = expert_model;
356
- let sessionId;
357
- let isInterrupted = false;
358
- const abortListener = () => {
359
- isInterrupted = true;
360
- };
361
- abortController.signal.addEventListener("abort", abortListener);
362
- try {
363
- if (abortController.signal.aborted) {
364
- return yield* this.handleInterrupt();
365
- }
366
- if (chat_session_id === "new") {
367
- try {
368
- const session = createExpertChatSession(expertModel);
369
- sessionId = session.sessionId;
370
- } catch (error) {
371
- logError(error);
372
- throw new Error("Failed to create new chat session");
373
- }
374
- } else {
375
- sessionId = chat_session_id;
376
- try {
377
- const session = loadExpertChatSession(sessionId);
378
- if (!session) {
379
- const newSession = createExpertChatSession(expertModel);
380
- sessionId = newSession.sessionId;
381
- }
382
- } catch (error) {
383
- logError(error);
384
- try {
385
- const newSession = createExpertChatSession(expertModel);
386
- sessionId = newSession.sessionId;
387
- } catch (createError) {
388
- logError(createError);
389
- throw new Error("Unable to create or load chat session");
390
- }
391
- }
392
- }
393
- if (isInterrupted || abortController.signal.aborted) {
394
- return yield* this.handleInterrupt();
395
- }
396
- let historyMessages;
397
- try {
398
- historyMessages = getSessionMessages(sessionId);
399
- } catch (error) {
400
- logError(error);
401
- historyMessages = [];
402
- }
403
- const messages = [...historyMessages, { role: "user", content: question }];
404
- let systemMessages;
405
- try {
406
- systemMessages = messages.map(
407
- (msg) => msg.role === "user" ? createUserMessageFromText(msg.content) : createAssistantMessage(msg.content)
408
- );
409
- } catch (error) {
410
- logError(error);
411
- throw new Error("Failed to prepare conversation messages");
412
- }
413
- if (isInterrupted || abortController.signal.aborted) {
414
- return yield* this.handleInterrupt();
415
- }
416
- yield {
417
- type: "progress",
418
- content: createAssistantMessage(
419
- `Connecting to ${expertModel}... (timeout: 5 minutes)`
420
- )
421
- };
422
- let response;
423
- try {
424
- const modelManager = getModelManager();
425
- const modelResolution = modelManager.resolveModelWithInfo(expertModel);
426
- debug.api("EXPERT_MODEL_RESOLUTION", {
427
- requestedModel: expertModel,
428
- success: modelResolution.success,
429
- profileName: modelResolution.profile?.name,
430
- profileModelName: modelResolution.profile?.modelName,
431
- provider: modelResolution.profile?.provider,
432
- isActive: modelResolution.profile?.isActive,
433
- error: modelResolution.error
434
- });
435
- const timeoutMs = 3e5;
436
- const timeoutPromise = new Promise((_, reject) => {
437
- setTimeout(() => {
438
- reject(
439
- new Error(
440
- `Expert model query timed out after ${timeoutMs / 1e3}s`
441
- )
442
- );
443
- }, timeoutMs);
444
- });
445
- response = await Promise.race([
446
- queryLLM(
447
- systemMessages,
448
- [],
449
- 0,
450
- [],
451
- abortController.signal,
452
- {
453
- safeMode: false,
454
- model: expertModel,
455
- prependCLISysprompt: false
456
- }
457
- ),
458
- timeoutPromise
459
- ]);
460
- } catch (error) {
461
- logError(error);
462
- if (error.name === "AbortError" || abortController.signal?.aborted || isInterrupted) {
463
- return yield* this.handleInterrupt();
464
- }
465
- if (error.message?.includes("timed out")) {
466
- throw new Error(
467
- `Expert model '${expertModel}' timed out after 5 minutes.
468
-
469
- Suggestions:
470
- - The model might be experiencing high load
471
- - Try a different model or retry later
472
- - Consider breaking down your question into smaller parts`
473
- );
474
- }
475
- if (error.message?.includes("rate limit")) {
476
- throw new Error(
477
- `Rate limit exceeded for ${expertModel}.
478
-
479
- Please wait a moment and try again, or use a different model.`
480
- );
481
- }
482
- if (error.message?.includes("invalid api key")) {
483
- throw new Error(
484
- `Invalid API key for ${expertModel}.
485
-
486
- Please check your model configuration with /model command.`
487
- );
488
- }
489
- if (error.message?.includes("model not found") || error.message?.includes("Failed to resolve model")) {
490
- try {
491
- const modelManager = getModelManager();
492
- const availableModels = modelManager.getAllAvailableModelNames();
493
- if (availableModels.length > 0) {
494
- throw new Error(
495
- `Model '${expertModel}' is not configured. Available models: ${availableModels.join(", ")}. Check if any available model closely matches the user's request (e.g., 'kimi' matches 'kimi-k2-0711-preview'). If there's a strong match, auto retry using the correct model name. If no close match exists, inform the user that '${expertModel}' needs to be configured using /model command.`
496
- );
497
- } else {
498
- throw new Error(
499
- `Model '${expertModel}' not found and no models are currently configured in the system. Inform the user that models need to be configured first using the /model command.`
500
- );
501
- }
502
- } catch (modelError) {
503
- throw new Error(
504
- `Model '${expertModel}' not found. Please check model configuration or inform user about the issue.`
505
- );
506
- }
507
- }
508
- throw new Error(
509
- `Expert model query failed: ${error.message || "Unknown error"}`
510
- );
511
- }
512
- let expertAnswer;
513
- try {
514
- if (!response?.message?.content) {
515
- throw new Error("No content in expert response");
516
- }
517
- expertAnswer = response.message.content.filter((block) => block.type === "text").map((block) => block.text).join("\n");
518
- if (!expertAnswer.trim()) {
519
- throw new Error("Expert response was empty");
520
- }
521
- } catch (error) {
522
- logError(error);
523
- throw new Error("Failed to process expert response");
524
- }
525
- try {
526
- addMessageToSession(sessionId, "user", question);
527
- addMessageToSession(sessionId, "assistant", expertAnswer);
528
- } catch (error) {
529
- logError(error);
530
- }
531
- const result = {
532
- chatSessionId: sessionId,
533
- expertModelName: expertModel,
534
- expertAnswer
535
- };
536
- yield {
537
- type: "result",
538
- data: result,
539
- resultForAssistant: this.renderResultForAssistant(result)
540
- };
541
- } catch (error) {
542
- if (error.name === "AbortError" || abortController.signal?.aborted || isInterrupted) {
543
- return yield* this.handleInterrupt();
544
- }
545
- logError(error);
546
- const errorSessionId = sessionId || "error-session";
547
- const errorMessage = error.message || "Expert consultation failed with unknown error";
548
- const result = {
549
- chatSessionId: errorSessionId,
550
- expertModelName: expertModel,
551
- expertAnswer: `\u274C ${errorMessage}`
552
- };
553
- yield {
554
- type: "result",
555
- data: result,
556
- resultForAssistant: this.renderResultForAssistant(result)
557
- };
558
- } finally {
559
- abortController.signal.removeEventListener("abort", abortListener);
560
- }
561
- },
562
- async *handleInterrupt() {
563
- yield {
564
- type: "result",
565
- data: {
566
- chatSessionId: "interrupted",
567
- expertModelName: "cancelled",
568
- expertAnswer: INTERRUPT_MESSAGE
569
- },
570
- resultForAssistant: INTERRUPT_MESSAGE
571
- };
572
- }
573
- };
574
-
575
- // src/tools/system/TaskOutputTool/TaskOutputTool.tsx
576
- import { Box as Box2, Text as Text2 } from "ink";
577
- import React2 from "react";
578
- import { z as z2 } from "zod";
579
-
580
- // src/utils/session/backgroundTasks.ts
581
- var backgroundTasks = /* @__PURE__ */ new Map();
582
- function getBackgroundAgentTaskSnapshot(agentId) {
583
- const task = backgroundTasks.get(agentId);
584
- if (!task) return void 0;
585
- const { abortController: _abortController, done: _done, ...snapshot } = task;
586
- return snapshot;
587
- }
588
- function upsertBackgroundAgentTask(task) {
589
- backgroundTasks.set(task.agentId, task);
590
- }
591
- async function waitForBackgroundAgentTask(agentId, waitUpToMs, signal) {
592
- const task = backgroundTasks.get(agentId);
593
- if (!task) return void 0;
594
- if (task.status !== "running") return task;
595
- const timeoutPromise = new Promise((_, reject) => {
596
- const timeoutId = setTimeout(() => {
597
- reject(new Error("Request timed out"));
598
- }, waitUpToMs);
599
- timeoutId.unref?.();
600
- });
601
- const abortPromise = new Promise((_, reject) => {
602
- if (signal.aborted) {
603
- reject(new Error("Request aborted"));
604
- return;
605
- }
606
- const onAbort = () => reject(new Error("Request aborted"));
607
- signal.addEventListener("abort", onAbort, { once: true });
608
- });
609
- await Promise.race([task.done, timeoutPromise, abortPromise]);
610
- return backgroundTasks.get(agentId);
611
- }
612
-
613
- // src/utils/tooling/toolOutputDisplay.ts
614
- function isTruthyEnv(value) {
615
- if (!value) return false;
616
- return ["1", "true", "yes", "on"].includes(value.trim().toLowerCase());
617
- }
618
- function isPackagedRuntime() {
619
- if (isTruthyEnv(process.env.PYB_PACKAGED)) return true;
620
- try {
621
- const exec = (process.execPath || "").split(/[\\/]/).pop()?.toLowerCase();
622
- if (!exec) return false;
623
- if (exec === "bun" || exec === "bun.exe") return false;
624
- if (exec === "node" || exec === "node.exe") return false;
625
- return true;
626
- } catch {
627
- return false;
628
- }
629
- }
630
- function truncateTextForDisplay(text, options) {
631
- const maxLines = options?.maxLines ?? 120;
632
- const maxChars = options?.maxChars ?? 12e3;
633
- const normalized = String(text ?? "");
634
- const lines = normalized.split(/\r?\n/);
635
- let workingLines = lines;
636
- let omittedLines = 0;
637
- if (maxLines > 0 && lines.length > maxLines) {
638
- workingLines = lines.slice(0, maxLines);
639
- omittedLines = lines.length - maxLines;
640
- }
641
- let workingText = workingLines.join("\n");
642
- let omittedChars = 0;
643
- if (maxChars > 0 && workingText.length > maxChars) {
644
- omittedChars = workingText.length - maxChars;
645
- workingText = workingText.slice(0, maxChars);
646
- }
647
- const truncated = omittedLines > 0 || omittedChars > 0;
648
- if (!truncated) {
649
- return {
650
- text: workingText,
651
- truncated: false,
652
- omittedLines: 0,
653
- omittedChars: 0
654
- };
655
- }
656
- const suffixParts = [];
657
- if (omittedLines > 0) {
658
- suffixParts.push(`${omittedLines} lines`);
659
- }
660
- if (omittedChars > 0) {
661
- suffixParts.push(`${omittedChars} chars`);
662
- }
663
- const suffix = `
664
-
665
- ... [truncated ${suffixParts.join(" \xB7 ")}] ...`;
666
- return {
667
- text: workingText + suffix,
668
- truncated: true,
669
- omittedLines,
670
- omittedChars
671
- };
672
- }
673
- function maybeTruncateVerboseToolOutput(text, options) {
674
- const maxLinesEnv = Number(process.env.PYB_TOOL_OUTPUT_MAX_LINES ?? "");
675
- const maxCharsEnv = Number(process.env.PYB_TOOL_OUTPUT_MAX_CHARS ?? "");
676
- const envOverrides = {
677
- maxLines: Number.isFinite(maxLinesEnv) && maxLinesEnv > 0 ? maxLinesEnv : void 0,
678
- maxChars: Number.isFinite(maxCharsEnv) && maxCharsEnv > 0 ? maxCharsEnv : void 0
679
- };
680
- const effective = {
681
- maxLines: envOverrides.maxLines ?? options?.maxLines,
682
- maxChars: envOverrides.maxChars ?? options?.maxChars
683
- };
684
- const fullAllowed = isTruthyEnv(process.env.PYB_TOOL_OUTPUT_FULL);
685
- if (!isPackagedRuntime() || fullAllowed) {
686
- return { text: String(text ?? ""), truncated: false };
687
- }
688
- const result = truncateTextForDisplay(String(text ?? ""), effective);
689
- return { text: result.text, truncated: result.truncated };
690
- }
691
-
692
- // src/tools/system/TaskOutputTool/prompt.ts
693
- var TOOL_NAME_FOR_PROMPT = "TaskOutput";
694
- var DESCRIPTION = "Retrieves output from a running or completed task";
695
- var PROMPT = `- Retrieves output from a running or completed task (background shell, agent, or remote session)
696
- - Takes a task_id parameter identifying the task
697
- - Returns the task output along with status information
698
- - Use block=true (default) to wait for task completion
699
- - Use block=false for non-blocking check of current status
700
- - Task IDs can be found using the /tasks command
701
- - Works with all task types: background shells, async agents, and remote sessions`;
702
-
703
- // src/tools/system/TaskOutputTool/TaskOutputTool.tsx
704
- var TOOL_PROGRESS_OPEN_TAG = ["<tool", "-progress>"].join("");
705
- var TOOL_PROGRESS_CLOSE_TAG = ["</tool", "-progress>"].join("");
706
- var inputSchema2 = z2.strictObject({
707
- task_id: z2.string().describe("The task ID to get output from"),
708
- block: z2.boolean().optional().default(true).describe("Whether to wait for completion"),
709
- timeout: z2.number().min(0).max(6e5).optional().default(3e4).describe("Max wait time in ms"),
710
- analyze: z2.boolean().optional().describe("Analyze the output for errors and warnings")
711
- });
712
- function isTaskOutputLspAnalysisEnabled() {
713
- const raw = String(process.env.PYB_TASKOUTPUT_LSP_ANALYSIS ?? "").trim().toLowerCase();
714
- if (!raw) return false;
715
- if (raw === "1" || raw === "true" || raw === "on" || raw === "yes")
716
- return true;
717
- if (raw === "0" || raw === "false" || raw === "off" || raw === "no")
718
- return false;
719
- return false;
720
- }
721
- function normalizeTaskOutputInput(input) {
722
- const task_id = typeof input.task_id === "string" && input.task_id || typeof input.agentId === "string" && String(input.agentId) || typeof input.bash_id === "string" && String(input.bash_id) || "";
723
- const block = typeof input.block === "boolean" ? input.block : true;
724
- const analyze = typeof input.analyze === "boolean" ? input.analyze : false;
725
- const timeout = typeof input.timeout === "number" ? input.timeout : typeof input.wait_up_to === "number" ? Number(input.wait_up_to) * 1e3 : 3e4;
726
- return { task_id, block, timeout, analyze };
727
- }
728
- function taskStatusFromBash(bg) {
729
- if (!bg) return "failed";
730
- if (bg.killed) return "killed";
731
- if (bg.code === null) return "running";
732
- return bg.code === 0 ? "completed" : "failed";
733
- }
734
- function buildTaskSummary(taskId) {
735
- const bg = BunShell.getInstance().getBackgroundOutput(taskId);
736
- if (bg) {
737
- return {
738
- task_id: taskId,
739
- task_type: "local_bash",
740
- status: taskStatusFromBash(bg),
741
- description: bg.command,
742
- output: readTaskOutput(taskId),
743
- exitCode: bg.code
744
- };
745
- }
746
- const agent = getBackgroundAgentTaskSnapshot(taskId);
747
- if (agent) {
748
- const output = readTaskOutput(taskId) || agent.resultText || "";
749
- return {
750
- task_id: taskId,
751
- task_type: "local_agent",
752
- status: agent.status,
753
- description: agent.description,
754
- output,
755
- prompt: agent.prompt,
756
- result: output,
757
- error: agent.error
758
- };
759
- }
760
- return null;
761
- }
762
- async function analyzeOutputWithLsp(output, exitCode) {
763
- const errorCount = (output.match(/Error:/gi) || []).length;
764
- const warningCount = (output.match(/Warning:/gi) || []).length;
765
- let summary = "";
766
- if (exitCode === 0) {
767
- summary = "Success";
768
- } else if (exitCode !== void 0 && exitCode !== null) {
769
- summary = `Failed (Exit Code: ${exitCode})`;
770
- } else {
771
- summary = "Completed";
772
- }
773
- const parts = [summary];
774
- if (errorCount > 0) parts.push(`${errorCount} Error${errorCount === 1 ? "" : "s"}`);
775
- if (warningCount > 0) parts.push(`${warningCount} Warning${warningCount === 1 ? "" : "s"}`);
776
- if (!isTaskOutputLspAnalysisEnabled()) {
777
- parts.push("LSP Analysis: disabled");
778
- return parts.join(", ");
779
- }
780
- if (exitCode === 0 && errorCount === 0 && warningCount === 0) {
781
- parts.push("LSP Analysis: skipped");
782
- return parts.join(", ");
783
- }
784
- if (exitCode !== 0 || errorCount > 0 || warningCount > 0) {
785
- try {
786
- const { isAbsolute: isAbsolute2, resolve: resolve3 } = await import("path");
787
- const { getSessionRoot: getSessionRoot2 } = await import("./state-PQJJ3JAM.js");
788
- const lines = output.split("\n");
789
- const uniqueFiles = /* @__PURE__ */ new Set();
790
- const lspSuggestions = [];
791
- const pathRegex = /(?:[a-zA-Z]:\\|(?:\/|\\))?[\w\-\.]+(?:(?:\/|\\)[\w\-\.]+)+\.(?:ts|tsx|js|jsx|py|java|cs|cpp|c|rs|go)(?::\d+|:\d+:\d+|\(\d+,\d+\))/g;
792
- for (const line of lines) {
793
- const matches = line.match(pathRegex);
794
- if (matches) {
795
- for (const match of matches) {
796
- let cleanPath = match;
797
- if (cleanPath.includes("(")) {
798
- cleanPath = cleanPath.split("(")[0];
799
- } else {
800
- cleanPath = cleanPath.replace(/:\d+(?::\d+)?$/, "");
801
- }
802
- uniqueFiles.add(cleanPath);
803
- }
804
- }
805
- }
806
- if (uniqueFiles.size === 0) {
807
- parts.push("LSP Analysis: no files detected");
808
- return parts.join(", ");
809
- }
810
- for (const file of uniqueFiles) {
811
- try {
812
- const absPath = isAbsolute2(file) ? file : resolve3(getSessionRoot2(), file);
813
- await LspFacade.run({
814
- operation: "diagnostics",
815
- filePath: absPath,
816
- waitForDiagnostics: true,
817
- timeoutMs: 2e3,
818
- rootPath: getSessionRoot2()
819
- });
820
- const diagnostics = await LspFacade.diagnostics(absPath, {
821
- rootPath: getSessionRoot2()
822
- });
823
- if (diagnostics && diagnostics.length > 0) {
824
- const pretty = formatDiagnosticsPretty(diagnostics, { maxItems: 3 });
825
- if (pretty) {
826
- lspSuggestions.push(`LSP Diagnostics for ${file}:`);
827
- lspSuggestions.push(pretty);
828
- }
829
- }
830
- } catch (e) {
831
- console.error("LSP Analysis Error:", e);
832
- }
833
- }
834
- if (lspSuggestions.length > 0) {
835
- parts.push("\nLSP Analysis:\n" + lspSuggestions.join("\n"));
836
- } else {
837
- parts.push("LSP Analysis: no diagnostics");
838
- }
839
- } catch (e) {
840
- console.error("LSP Analysis Setup Error:", e);
841
- }
842
- }
843
- return parts.join(", ");
844
- }
845
- async function waitForBashTaskCompletion(args) {
846
- const { taskId, timeoutMs, signal } = args;
847
- const startedAt = Date.now();
848
- while (Date.now() - startedAt < timeoutMs) {
849
- if (signal.aborted) return null;
850
- const summary = buildTaskSummary(taskId);
851
- if (!summary) return null;
852
- if (summary.status !== "running" && summary.status !== "pending")
853
- return summary;
854
- await new Promise((resolve3) => setTimeout(resolve3, 100));
855
- }
856
- return buildTaskSummary(taskId);
857
- }
858
- var TaskOutputTool = {
859
- name: TOOL_NAME_FOR_PROMPT,
860
- async description() {
861
- return DESCRIPTION;
862
- },
863
- userFacingName() {
864
- return "Task Output";
865
- },
866
- inputSchema: inputSchema2,
867
- isReadOnly() {
868
- return true;
869
- },
870
- isConcurrencySafe() {
871
- return true;
872
- },
873
- async isEnabled() {
874
- return true;
875
- },
876
- needsPermissions() {
877
- return false;
878
- },
879
- async prompt() {
880
- return PROMPT;
881
- },
882
- renderToolUseMessage(input) {
883
- const normalized = normalizeTaskOutputInput(input);
884
- if (!normalized.block) return "non-blocking";
885
- return "";
886
- },
887
- renderToolUseRejectedMessage() {
888
- return null;
889
- },
890
- renderToolResultMessage(output, { verbose }) {
891
- const theme = getTheme();
892
- if (output.retrieval_status === "timeout" || output.retrieval_status === "not_ready") {
893
- return /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(Text2, { color: theme.secondaryText }, "Task is still running\u2026"));
894
- }
895
- if (!output.task) {
896
- return /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(Text2, { color: theme.secondaryText }, "No task output available"));
897
- }
898
- if (output.task.task_type === "local_agent") {
899
- const lines = output.task.result ? output.task.result.split("\n").length : 0;
900
- if (!verbose) {
901
- return /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(Text2, { color: theme.secondaryText }, "Read output (ctrl+o to expand)"));
902
- }
903
- return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, /* @__PURE__ */ React2.createElement(Text2, null, output.task.description, " (", lines, " lines)"), output.task.prompt ? /* @__PURE__ */ React2.createElement(Box2, { paddingLeft: 2 }, /* @__PURE__ */ React2.createElement(Text2, { color: theme.secondaryText }, output.task.prompt)) : null, output.task.result ? /* @__PURE__ */ React2.createElement(Box2, { paddingLeft: 2, marginTop: 1 }, /* @__PURE__ */ React2.createElement(Text2, null, maybeTruncateVerboseToolOutput(output.task.result, {
904
- maxLines: 200,
905
- maxChars: 4e4
906
- }).text)) : null, output.task.error ? /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column", marginTop: 1, paddingLeft: 2 }, /* @__PURE__ */ React2.createElement(Text2, { color: theme.error, bold: true }, "Error:"), /* @__PURE__ */ React2.createElement(Text2, { color: theme.error }, output.task.error)) : null);
907
- }
908
- const content = output.task.output?.trimEnd() ?? "";
909
- if (!verbose) {
910
- return /* @__PURE__ */ React2.createElement(Box2, null, /* @__PURE__ */ React2.createElement(Text2, { color: theme.secondaryText }, content.length > 0 ? "Read output (ctrl+o to expand)" : "(No content)"));
911
- }
912
- return /* @__PURE__ */ React2.createElement(Box2, { flexDirection: "column" }, /* @__PURE__ */ React2.createElement(Text2, { color: theme.secondaryText }, output.task.description), content ? /* @__PURE__ */ React2.createElement(Box2, { paddingLeft: 2, marginTop: 1 }, /* @__PURE__ */ React2.createElement(Text2, null, maybeTruncateVerboseToolOutput(content, {
913
- maxLines: 200,
914
- maxChars: 4e4
915
- }).text)) : null);
916
- },
917
- renderResultForAssistant(output) {
918
- const parts = [];
919
- parts.push(
920
- `<retrieval_status>${output.retrieval_status}</retrieval_status>`
921
- );
922
- if (output.task) {
923
- parts.push(`<task_id>${output.task.task_id}</task_id>`);
924
- parts.push(`<task_type>${output.task.task_type}</task_type>`);
925
- parts.push(`<status>${output.task.status}</status>`);
926
- if (output.task.exitCode !== void 0 && output.task.exitCode !== null) {
927
- parts.push(`<exit_code>${output.task.exitCode}</exit_code>`);
928
- }
929
- if (output.task.output?.trim()) {
930
- parts.push(`<output>
931
- ${output.task.output.trimEnd()}
932
- </output>`);
933
- }
934
- if (output.task.error) {
935
- parts.push(`<error>${output.task.error}</error>`);
936
- }
937
- if (output.task.analysis) {
938
- parts.push(`<analysis>${output.task.analysis}</analysis>`);
939
- }
940
- }
941
- return parts.join("\n\n");
942
- },
943
- async validateInput(input) {
944
- if (!input.task_id) {
945
- return { result: false, message: "Task ID is required", errorCode: 1 };
946
- }
947
- const task = buildTaskSummary(input.task_id);
948
- if (!task) {
949
- return {
950
- result: false,
951
- message: `No task found with ID: ${input.task_id}`,
952
- errorCode: 2
953
- };
954
- }
955
- return { result: true };
956
- },
957
- async *call(input, context) {
958
- const normalized = normalizeTaskOutputInput(input);
959
- const taskId = normalized.task_id;
960
- const block = normalized.block;
961
- const timeoutMs = normalized.timeout;
962
- const analyze = normalized.analyze;
963
- const initial = buildTaskSummary(taskId);
964
- if (!initial) {
965
- throw new Error(`No task found with ID: ${taskId}`);
966
- }
967
- if (!block) {
968
- const isDone = initial.status !== "running" && initial.status !== "pending";
969
- if (analyze) {
970
- initial.analysis = await analyzeOutputWithLsp(initial.output || "", initial.exitCode);
971
- }
972
- const out2 = {
973
- retrieval_status: isDone ? "success" : "not_ready",
974
- task: initial
975
- };
976
- yield {
977
- type: "result",
978
- data: out2,
979
- resultForAssistant: this.renderResultForAssistant(out2)
980
- };
981
- return;
982
- }
983
- yield {
984
- type: "progress",
985
- content: createAssistantMessage(
986
- `${TOOL_PROGRESS_OPEN_TAG}${initial.description ? ` ${initial.description}
987
- ` : ""} Waiting for task (esc to give additional instructions)${TOOL_PROGRESS_CLOSE_TAG}`
988
- )
989
- };
990
- let finalTask = null;
991
- if (initial.task_type === "local_agent") {
992
- try {
993
- const task = await waitForBackgroundAgentTask(
994
- taskId,
995
- timeoutMs,
996
- context.abortController.signal
997
- );
998
- finalTask = task ? buildTaskSummary(taskId) : null;
999
- } catch {
1000
- finalTask = buildTaskSummary(taskId);
1001
- }
1002
- } else {
1003
- finalTask = await waitForBashTaskCompletion({
1004
- taskId,
1005
- timeoutMs,
1006
- signal: context.abortController.signal
1007
- });
1008
- }
1009
- if (!finalTask) {
1010
- const out2 = { retrieval_status: "timeout", task: null };
1011
- yield {
1012
- type: "result",
1013
- data: out2,
1014
- resultForAssistant: this.renderResultForAssistant(out2)
1015
- };
1016
- return;
1017
- }
1018
- if (finalTask.status === "running" || finalTask.status === "pending") {
1019
- const out2 = { retrieval_status: "timeout", task: finalTask };
1020
- yield {
1021
- type: "result",
1022
- data: out2,
1023
- resultForAssistant: this.renderResultForAssistant(out2)
1024
- };
1025
- return;
1026
- }
1027
- if (analyze) {
1028
- finalTask.analysis = await analyzeOutputWithLsp(finalTask.output || "", finalTask.exitCode);
1029
- }
1030
- const out = { retrieval_status: "success", task: finalTask };
1031
- yield {
1032
- type: "result",
1033
- data: out,
1034
- resultForAssistant: this.renderResultForAssistant(out)
1035
- };
1036
- }
1037
- };
1038
-
1039
- // src/tools/filesystem/DeleteTool/DeleteTool.tsx
1040
- import { rmSync, existsSync as existsSync2, statSync } from "fs";
1041
- import { Box as Box3, Text as Text3 } from "ink";
1042
- import * as React3 from "react";
1043
- import { z as z3 } from "zod";
1044
- import { isAbsolute, relative, resolve } from "path";
1045
-
1046
- // src/tools/filesystem/DeleteTool/prompt.ts
1047
- var PROMPT2 = `You can use this tool to delete files (regular file or directory), you can delete multi files in one toolcall, and you MUST make sure the files is exist before deleting.
1048
- When you need to delete file, you MUST use this tool to delete file instead of using shell.
1049
-
1050
- Usage:
1051
- - The file_paths parameter must be an array of absolute paths.
1052
- - Supports recursive deletion for directories (be careful!).
1053
- - Safe and cross-platform (works on Windows/Linux/Mac).
1054
- - Do NOT use BashTool/RunCommand with 'rm', 'del', or 'rmdir'.
1055
-
1056
- Examples:
1057
-
1058
- <example_single>
1059
- // Deleting a single file
1060
- Delete({
1061
- file_paths: ["/absolute/path/to/file.txt"]
1062
- })
1063
- </example_single>
1064
-
1065
- <example_force>
1066
- // Deleting a file that might be referenced
1067
- Delete({
1068
- file_paths: ["/absolute/path/to/shared.ts"],
1069
- force: true
1070
- })
1071
- </example_force>
1072
-
1073
- <example_directory>
1074
- // Deleting a directory recursively
1075
- Delete({
1076
- file_paths: ["/absolute/path/to/directory"]
1077
- })
1078
- </example_directory>
1079
-
1080
- <example_batch>
1081
- // Deleting multiple files and directories in one go
1082
- Delete({
1083
- file_paths: [
1084
- "/absolute/path/to/file1.txt",
1085
- "/absolute/path/to/file2.log",
1086
- "/absolute/path/to/temp_directory"
1087
- ]
1088
- })
1089
- </example_batch>
1090
- `.trim();
1091
-
1092
- // src/tools/filesystem/DeleteTool/DeleteTool.tsx
1093
- var inputSchema3 = z3.strictObject({
1094
- file_paths: z3.array(z3.string()).describe(
1095
- "The list of file paths you want to delete, you MUST set file path to absolute path."
1096
- ),
1097
- force: z3.boolean().optional().describe(
1098
- "Force deletion even if the file is referenced by other files (LSP check)."
1099
- )
1100
- });
1101
- var DeleteTool = {
1102
- name: "Delete",
1103
- async description() {
1104
- return "Use this tool to delete files or directories. ALWAYS use this tool instead of running shell commands (like 'rm', 'del') to ensure cross-platform compatibility and safety. Supports deleting multiple paths at once.";
1105
- },
1106
- userFacingName: () => "Delete",
1107
- async prompt() {
1108
- return PROMPT2;
1109
- },
1110
- inputSchema: inputSchema3,
1111
- async isEnabled() {
1112
- return true;
1113
- },
1114
- isReadOnly() {
1115
- return false;
1116
- },
1117
- isConcurrencySafe() {
1118
- return false;
1119
- },
1120
- needsPermissions({ file_paths }) {
1121
- return file_paths.some((path) => !hasWritePermission(path));
1122
- },
1123
- renderToolUseMessage(input, { verbose }) {
1124
- const paths = input.file_paths.map(
1125
- (p) => verbose ? p : relative(getCwd(), p)
1126
- ).join(", ");
1127
- return `Deleting: ${paths}`;
1128
- },
1129
- renderToolResultMessage(output) {
1130
- return /* @__PURE__ */ React3.createElement(Box3, { flexDirection: "column" }, output.deleted.length > 0 && /* @__PURE__ */ React3.createElement(Text3, null, "Deleted: ", output.deleted.join(", ")), output.failed.length > 0 && /* @__PURE__ */ React3.createElement(Text3, { color: getTheme().warning }, "Failed to delete: ", output.failed.join(", ")));
1131
- },
1132
- renderResultForAssistant(output) {
1133
- const parts = [];
1134
- if (output.deleted.length > 0) parts.push(`Deleted files: ${output.deleted.join(", ")}`);
1135
- if (output.failed.length > 0) parts.push(`Failed to delete files: ${output.failed.join(", ")}`);
1136
- return parts.join("\n");
1137
- },
1138
- renderToolUseRejectedMessage({ file_paths } = {}, { columns, verbose } = {}) {
1139
- if (!file_paths || file_paths.length === 0) {
1140
- return /* @__PURE__ */ React3.createElement(FallbackToolUseRejectedMessage, null);
1141
- }
1142
- return /* @__PURE__ */ React3.createElement(Box3, { flexDirection: "column" }, /* @__PURE__ */ React3.createElement(Text3, null, " ", "\u23BF", " ", /* @__PURE__ */ React3.createElement(Text3, { color: getTheme().error }, "User rejected deletion of ", file_paths.length, " items")));
1143
- },
1144
- async *call({ file_paths, force }, context) {
1145
- const verbose = context.options?.verbose || false;
1146
- const deletedItems = [];
1147
- const failedItems = [];
1148
- for (const rawPath of file_paths) {
1149
- const fullPath = isAbsolute(rawPath) ? rawPath : resolve(getCwd(), rawPath);
1150
- try {
1151
- if (!existsSync2(fullPath)) {
1152
- failedItems.push(`${rawPath} (not found)`);
1153
- continue;
1154
- }
1155
- if (!force) {
1156
- try {
1157
- const { LspFacade: LspFacade2 } = await import("./lsp-SQBHGEL7.js");
1158
- const referenceDetail = await LspFacade2.checkFileReferences(fullPath);
1159
- if (referenceDetail) {
1160
- failedItems.push(
1161
- `${rawPath} (Safety Check Failed: ${referenceDetail}. Use 'force: true' to override.)`
1162
- );
1163
- continue;
1164
- }
1165
- } catch (e) {
1166
- if (!force) {
1167
- failedItems.push(
1168
- `${rawPath} (Safety Check Failed: Internal LSP Error. Use 'force: true' to override.)`
1169
- );
1170
- continue;
1171
- }
1172
- }
1173
- }
1174
- const root = resolve(fullPath, "/");
1175
- if (fullPath === root) {
1176
- throw new Error("Cannot delete filesystem root");
1177
- }
1178
- const stats = statSync(fullPath);
1179
- const type = stats.isDirectory() ? "directory" : "file";
1180
- rmSync(fullPath, { recursive: true, force: true });
1181
- deletedItems.push(`${rawPath} (${type})`);
1182
- } catch (err) {
1183
- logError(err);
1184
- failedItems.push(`${rawPath} (${err.message})`);
1185
- }
1186
- }
1187
- const result = {
1188
- deleted: deletedItems,
1189
- failed: failedItems
1190
- };
1191
- if (deletedItems.length > 0 || failedItems.length > 0) {
1192
- yield {
1193
- type: "result",
1194
- data: result,
1195
- resultForAssistant: this.renderResultForAssistant(result)
1196
- };
1197
- }
1198
- return;
1199
- }
1200
- };
1201
-
1202
- // src/tools/mcp/ListMcpResourcesTool/ListMcpResourcesTool.tsx
1203
- import { Box as Box4, Text as Text4 } from "ink";
1204
- import React4 from "react";
1205
- import { z as z4 } from "zod";
1206
- import { ListResourcesResultSchema } from "@modelcontextprotocol/sdk/types.js";
1207
-
1208
- // src/tools/mcp/ListMcpResourcesTool/prompt.ts
1209
- var TOOL_NAME = "ListMcpResourcesTool";
1210
- var DESCRIPTION2 = `Lists available resources from configured MCP servers.
1211
- Each resource object includes a 'server' field indicating which server it's from.
1212
-
1213
- Usage examples:
1214
- - List all resources from all servers: \`listMcpResources\`
1215
- - List resources from a specific server: \`listMcpResources({ server: "myserver" })\``;
1216
- var PROMPT3 = `List available resources from configured MCP servers.
1217
- Each returned resource will include all standard MCP resource fields plus a 'server' field
1218
- indicating which server the resource belongs to.
1219
-
1220
- Parameters:
1221
- - server (optional): The name of a specific MCP server to get resources from. If not provided,
1222
- resources from all servers will be returned.`;
1223
-
1224
- // src/tools/mcp/ListMcpResourcesTool/ListMcpResourcesTool.tsx
1225
- var inputSchema4 = z4.strictObject({
1226
- server: z4.string().optional().describe("Optional server name to filter resources by")
1227
- });
1228
- var ListMcpResourcesTool = {
1229
- name: TOOL_NAME,
1230
- async description() {
1231
- return DESCRIPTION2;
1232
- },
1233
- async prompt() {
1234
- return PROMPT3;
1235
- },
1236
- inputSchema: inputSchema4,
1237
- userFacingName() {
1238
- return "listMcpResources";
1239
- },
1240
- async isEnabled() {
1241
- return true;
1242
- },
1243
- isReadOnly() {
1244
- return true;
1245
- },
1246
- isConcurrencySafe() {
1247
- return true;
1248
- },
1249
- needsPermissions() {
1250
- return false;
1251
- },
1252
- async validateInput({ server }, context) {
1253
- if (!server) return { result: true };
1254
- const clients = context?.options?.mcpClients ?? await getClients();
1255
- const found = clients.some((c) => c.name === server);
1256
- if (!found) {
1257
- return {
1258
- result: false,
1259
- message: `Server "${server}" not found. Available servers: ${clients.map((c) => c.name).join(", ")}`,
1260
- errorCode: 1
1261
- };
1262
- }
1263
- return { result: true };
1264
- },
1265
- renderToolUseMessage({ server }) {
1266
- return server ? `List MCP resources from server "${server}"` : "List all MCP resources";
1267
- },
1268
- renderToolUseRejectedMessage() {
1269
- return /* @__PURE__ */ React4.createElement(FallbackToolUseRejectedMessage, null);
1270
- },
1271
- renderToolResultMessage(output) {
1272
- return /* @__PURE__ */ React4.createElement(Box4, { justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React4.createElement(Box4, { flexDirection: "row" }, /* @__PURE__ */ React4.createElement(Text4, null, "\xA0\xA0\u23BF \xA0"), /* @__PURE__ */ React4.createElement(Text4, { bold: true }, output.length), /* @__PURE__ */ React4.createElement(Text4, null, " resources")), /* @__PURE__ */ React4.createElement(Cost, { costUSD: 0, durationMs: 0, debug: false }));
1273
- },
1274
- renderResultForAssistant(output) {
1275
- return JSON.stringify(output);
1276
- },
1277
- async *call({ server }, context) {
1278
- const clients = context.options?.mcpClients ?? await getClients();
1279
- const selected = server ? clients.filter((c) => c.name === server) : clients;
1280
- if (server && selected.length === 0) {
1281
- throw new Error(
1282
- `Server "${server}" not found. Available servers: ${clients.map((c) => c.name).join(", ")}`
1283
- );
1284
- }
1285
- const resources = [];
1286
- for (const wrapped of selected) {
1287
- if (wrapped.type !== "connected") continue;
1288
- try {
1289
- let capabilities = wrapped.capabilities ?? null;
1290
- if (!capabilities) {
1291
- try {
1292
- capabilities = wrapped.client.getServerCapabilities();
1293
- } catch {
1294
- capabilities = null;
1295
- }
1296
- }
1297
- if (!capabilities?.resources) continue;
1298
- const result = await wrapped.client.request(
1299
- { method: "resources/list" },
1300
- ListResourcesResultSchema
1301
- );
1302
- if (!result.resources) continue;
1303
- resources.push(
1304
- ...result.resources.map((r) => ({
1305
- ...r,
1306
- server: wrapped.name
1307
- }))
1308
- );
1309
- } catch {
1310
- }
1311
- }
1312
- yield {
1313
- type: "result",
1314
- data: resources,
1315
- resultForAssistant: this.renderResultForAssistant(resources)
1316
- };
1317
- }
1318
- };
1319
-
1320
- // src/tools/search/LspTool/LspTool.tsx
1321
- import { existsSync as existsSync3, readFileSync as readFileSync2, statSync as statSync2 } from "fs";
1322
- import { Box as Box5, Text as Text5 } from "ink";
1323
- import { relative as relative2, resolve as resolve2 } from "path";
1324
- import React5 from "react";
1325
- import { z as z5 } from "zod";
1326
-
1327
- // src/tools/search/LspTool/prompt.ts
1328
- var TOOL_NAME_FOR_PROMPT2 = "LSP";
1329
- var PROMPT4 = `Interact with Language Server Protocol (LSP) servers to get code intelligence features.Supports all languages.
1330
-
1331
- ## Capabilities & Scenarios
1332
- This tool acts as your "Code Analyst". Use it to understand the codebase semantically, rather than just matching text.
1333
-
1334
- ### 1. Tracing Logic (Where does this go?)
1335
- - **Scenario**: You see a function call \`calculateTax(amount)\` and need to know its formula.
1336
- - **Action**: Use \`goToDefinition\` on the function name.
1337
- - **Why**: It jumps directly to the implementation, even if it's imported from another file.
1338
-
1339
- ### 2. Impact Analysis (What uses this?)
1340
- - **Scenario**: You are planning to rename or modify the \`User\` class.
1341
- - **Action**: Use \`findReferences\` at the symbol position (line + character) in the file where \`User\` is declared or used.
1342
- - **Why**: It lists every file and line where \`User\` is referenced, ensuring you don't break dependents.
1343
-
1344
- ### 3. Exploring New Files (What's in here?)
1345
- - **Scenario**: You just opened a large file \`utils.ts\` and want a quick overview.
1346
- - **Action**: Use \`documentSymbol\`.
1347
- - **Why**: It returns a structured outline (Classes, Functions, Variables) using LspFacade policy: LSP-first for accuracy, Tree-Sitter fallback for speed when needed.
1348
-
1349
- ### 4. Interface Implementation (Who implements this?)
1350
- - **Scenario**: You see an interface \`IStorage\` and want to find the concrete class (e.g., \`S3Storage\`).
1351
- - **Action**: Use \`goToImplementation\`.
1352
- - **Why**: Grep might just find the import; LSP finds the actual code that implements the interface.
1353
-
1354
- ### 5. Scope Analysis (What is visible here?)
1355
- - **Scenario**: You are debugging a closure or nested function and want to know which variables are captured or locally defined.
1356
- - **Action**: Use \`getScope\`.
1357
- - **Why**: It returns the local variables and closure captures at the current position. Essential for debugging Python closures or JavaScript lexical scopes.
1358
-
1359
- ### 6. Real-time Diagnostics (Is this correct?)
1360
- - **Scenario**: You just edited a file and want to verify if you introduced any syntax errors.
1361
- - **Action**: Use \`diagnostics\` with \`waitForDiagnostics: true\`.
1362
- - **Why**: It waits for the language server to re-analyze the file and returns fresh errors/warnings. This is your "Self-Correction" mechanism.
1363
-
1364
- ### 7. Workspace Symbol Search (Where is this symbol?)
1365
- - **Scenario**: You need to find a class or function across the entire project.
1366
- - **Action**: Use \`workspaceSymbol\` with a query string and a scoped filePath (module or directory).
1367
- - **Why**: It searches across all files in the scope. **Important**: Must provide a scoped path to avoid unbounded full-repo queries.
1368
-
1369
- ## Supported Operations
1370
-
1371
- ### Location-Based Operations (require line + character)
1372
- These 8 operations require precise symbol position:
1373
- - \`goToDefinition\`: Find where a symbol is defined
1374
- - \`findReferences\`: Find all references to a symbol
1375
- - \`hover\`: Get hover information (documentation, type info) for a symbol
1376
- - \`goToImplementation\`: Find implementations of an interface or abstract method
1377
- - \`prepareCallHierarchy\`: Get call hierarchy item at a position (functions/methods)
1378
- - \`incomingCalls\`: Find all functions/methods that call the function at a position
1379
- - \`outgoingCalls\`: Find all functions/methods called by the function at a position
1380
- - \`getScope\`: Get local variables and closure captures at a position
1381
-
1382
- ### Document-Level Operations (no position required)
1383
- - \`documentSymbol\`: Get all symbols (functions, classes, variables) in a document
1384
- - \`diagnostics\`: Get validation errors and warnings for a file (supports waiting for fresh results)
1385
-
1386
- ### Workspace-Level Operations
1387
- - \`workspaceSymbol\`: Search for symbols across the entire workspace (requires query + scoped filePath)
1388
-
1389
- ## Parameters
1390
-
1391
- All operations require:
1392
- - \`filePath\`: The file or directory to operate on
1393
-
1394
- Location-based operations require:
1395
- - \`line\`: The line number (1-based, as shown in editors)
1396
- - \`character\`: The character offset (1-based, as shown in editors)
1397
-
1398
- \`workspaceSymbol\` requires:
1399
- - \`query\`: The symbol name or keyword to search for
1400
-
1401
- Optional:
1402
- - \`waitForDiagnostics\`: If true, wait for fresh diagnostics (used with "diagnostics" operation)
1403
- - \`timeout\`: Timeout in milliseconds for waiting operations (default: 5000)
1404
-
1405
- ## Intelligent Position Handling
1406
-
1407
- **Do not pass a symbol name.** LSPTool works on positions, not names. Use Read to locate the symbol and derive line + character first.
1408
-
1409
- ### Auto-Adjustment Mechanism
1410
- If the specified position is not on a valid symbol, the tool automatically searches for the nearest valid token:
1411
- - **Search scope**: Current line, then previous line, then next line
1412
- - **Priority**: Current line tokens are preferred (lower score)
1413
- - **Skipped tokens**: Keywords (function, class, if, for, return, etc.) and operators (=, =>, +, -, &&, ||, etc.) are automatically skipped
1414
- - **If no valid token found**: Returns an error message
1415
-
1416
- ### Best Practice for Position
1417
- 1. Use Read to open the file and locate the symbol
1418
- 2. Provide accurate 1-based line and character offset
1419
- 3. Position should be on the symbol name itself, not on keywords/operators
1420
- 4. If unsure, position at the start of the symbol name
1421
-
1422
- ## Workflow & Scope Guidelines
1423
- - **Priority order**: documentSymbol \u2192 hotspot detection \u2192 Read \u2192 LSP (ensure line + character)
1424
- - **workspaceSymbol**: Must provide scoped filePath (module or directory), avoid unbounded full-repo queries
1425
- - **documentSymbol**: Uses LspFacade policy (LSP-first for accuracy, Tree-Sitter fallback for speed)
1426
-
1427
- Note: LSP servers are automatically managed and installed for most languages. For system-level languages (like C++, Java), ensure the corresponding tools (clangd, jdtls) are in your PATH.`;
1428
- var DESCRIPTION3 = PROMPT4;
1429
-
1430
- // src/tools/search/LspTool/LspTool.tsx
1431
- var inputSchema5 = z5.strictObject({
1432
- operation: z5.enum([
1433
- "goToDefinition",
1434
- "findReferences",
1435
- "hover",
1436
- "documentSymbol",
1437
- "workspaceSymbol",
1438
- "goToImplementation",
1439
- "prepareCallHierarchy",
1440
- "incomingCalls",
1441
- "outgoingCalls",
1442
- "getScope",
1443
- "diagnostics"
1444
- ]).describe("The LSP operation to perform"),
1445
- filePath: z5.string().describe("The absolute or relative path to the file"),
1446
- line: z5.number().int().positive().optional().describe("The line number (1-based, as shown in editors). Required for location-based operations."),
1447
- character: z5.number().int().positive().optional().describe("The character offset (1-based, as shown in editors). Required for location-based operations."),
1448
- query: z5.string().optional().describe('The query string for workspaceSymbol. Required when operation is "workspaceSymbol".'),
1449
- waitForDiagnostics: z5.boolean().optional().describe('If true, wait for fresh diagnostics from the server (used with "diagnostics" operation)'),
1450
- timeout: z5.number().optional().describe("Timeout in milliseconds for waiting operations (default: 5000)")
1451
- });
1452
- var outputSchema = z5.object({
1453
- operation: z5.enum([
1454
- "goToDefinition",
1455
- "findReferences",
1456
- "hover",
1457
- "documentSymbol",
1458
- "workspaceSymbol",
1459
- "goToImplementation",
1460
- "prepareCallHierarchy",
1461
- "incomingCalls",
1462
- "outgoingCalls",
1463
- "getScope",
1464
- "diagnostics"
1465
- ]).describe("The LSP operation that was performed"),
1466
- result: z5.string().describe("The formatted result of the LSP operation"),
1467
- filePath: z5.string().describe("The file path the operation was performed on"),
1468
- resultCount: z5.number().int().nonnegative().optional().describe("Number of results (definitions, references, symbols)"),
1469
- fileCount: z5.number().int().nonnegative().optional().describe("Number of files containing results")
1470
- });
1471
- var OPERATION_LABELS = {
1472
- goToDefinition: { singular: "definition", plural: "definitions" },
1473
- findReferences: { singular: "reference", plural: "references" },
1474
- documentSymbol: { singular: "symbol", plural: "symbols" },
1475
- workspaceSymbol: { singular: "symbol", plural: "symbols" },
1476
- hover: { singular: "hover info", plural: "hover info", special: "available" },
1477
- goToImplementation: { singular: "implementation", plural: "implementations" },
1478
- prepareCallHierarchy: { singular: "call item", plural: "call items" },
1479
- incomingCalls: { singular: "caller", plural: "callers" },
1480
- outgoingCalls: { singular: "callee", plural: "callees" },
1481
- getScope: { singular: "scope", plural: "scopes" },
1482
- diagnostics: { singular: "diagnostic", plural: "diagnostics" }
1483
- };
1484
- function extractSymbolAtPosition(lines, zeroBasedLine, zeroBasedCharacter) {
1485
- try {
1486
- if (zeroBasedLine < 0 || zeroBasedLine >= lines.length) return null;
1487
- const line = lines[zeroBasedLine];
1488
- if (zeroBasedCharacter < 0 || zeroBasedCharacter >= line.length) return null;
1489
- const tokenRe = /[\w$'!]+|[+\-*/%&|^~<>=]+/g;
1490
- let match;
1491
- while ((match = tokenRe.exec(line)) !== null) {
1492
- const start = match.index;
1493
- const end = start + match[0].length;
1494
- if (zeroBasedCharacter >= start && zeroBasedCharacter < end) {
1495
- const token = match[0];
1496
- return token.length > 30 ? `${token.slice(0, 27)}...` : token;
1497
- }
1498
- }
1499
- return null;
1500
- } catch {
1501
- return null;
1502
- }
1503
- }
1504
- function getTokensForLine(line) {
1505
- const tokenRe = /[\w$'!]+|[+\-*/%&|^~<>=]+/g;
1506
- const tokens = [];
1507
- let match;
1508
- while ((match = tokenRe.exec(line)) !== null) {
1509
- tokens.push({
1510
- token: match[0],
1511
- start: match.index,
1512
- end: match.index + match[0].length
1513
- });
1514
- }
1515
- return tokens;
1516
- }
1517
- function isValidToken(token) {
1518
- const lowered = token.toLowerCase();
1519
- if (KEYWORD_TOKENS.has(lowered)) return false;
1520
- if (OPERATOR_TOKENS.has(token)) return false;
1521
- return true;
1522
- }
1523
- function findNearestValidTokenPosition(lines, zeroBasedLine, zeroBasedCharacter) {
1524
- const offsets = [0, -1, 1];
1525
- let best = null;
1526
- for (const offset of offsets) {
1527
- const lineIndex = zeroBasedLine + offset;
1528
- if (lineIndex < 0 || lineIndex >= lines.length) continue;
1529
- const lineText = lines[lineIndex];
1530
- if (!lineText || lineText.length === 0) continue;
1531
- const clampedChar = Math.min(
1532
- Math.max(zeroBasedCharacter, 0),
1533
- Math.max(lineText.length - 1, 0)
1534
- );
1535
- const tokens = getTokensForLine(lineText).filter(
1536
- (token) => isValidToken(token.token)
1537
- );
1538
- for (const token of tokens) {
1539
- const distance = clampedChar >= token.start && clampedChar < token.end ? 0 : Math.min(
1540
- Math.abs(clampedChar - token.start),
1541
- Math.abs(clampedChar - (token.end - 1))
1542
- );
1543
- const score = distance + Math.abs(offset) * 1e3;
1544
- if (!best || score < best.score) {
1545
- best = {
1546
- line: lineIndex,
1547
- character: token.start,
1548
- token: token.token,
1549
- score
1550
- };
1551
- }
1552
- }
1553
- }
1554
- if (!best) return null;
1555
- return { line: best.line, character: best.character, token: best.token };
1556
- }
1557
- var KEYWORD_TOKENS = /* @__PURE__ */ new Set([
1558
- "export",
1559
- "import",
1560
- "from",
1561
- "as",
1562
- "return",
1563
- "if",
1564
- "else",
1565
- "for",
1566
- "while",
1567
- "switch",
1568
- "case",
1569
- "break",
1570
- "continue",
1571
- "function",
1572
- "class",
1573
- "interface",
1574
- "type",
1575
- "enum",
1576
- "const",
1577
- "let",
1578
- "var",
1579
- "new",
1580
- "extends",
1581
- "implements",
1582
- "try",
1583
- "catch",
1584
- "finally",
1585
- "throw",
1586
- "await",
1587
- "async",
1588
- "yield",
1589
- "default",
1590
- "in",
1591
- "of",
1592
- "do",
1593
- "this",
1594
- "super",
1595
- "private",
1596
- "public",
1597
- "protected",
1598
- "static",
1599
- "get",
1600
- "set",
1601
- "readonly",
1602
- "abstract",
1603
- "declare",
1604
- "namespace",
1605
- "module",
1606
- "package",
1607
- "true",
1608
- "false",
1609
- "null",
1610
- "undefined"
1611
- ]);
1612
- var OPERATOR_TOKENS = /* @__PURE__ */ new Set([
1613
- "=",
1614
- "==",
1615
- "===",
1616
- "!=",
1617
- "!==",
1618
- "=>",
1619
- "+",
1620
- "-",
1621
- "*",
1622
- "/",
1623
- "%",
1624
- "&&",
1625
- "||",
1626
- "!",
1627
- "<",
1628
- ">",
1629
- "<=",
1630
- ">=",
1631
- "??",
1632
- "?.",
1633
- "::",
1634
- ":",
1635
- ";",
1636
- ",",
1637
- ".",
1638
- "..."
1639
- ]);
1640
- function getPositionGuardMessage(token, line, character) {
1641
- if (!token) {
1642
- if (line !== void 0 && character !== void 0) {
1643
- return `Invalid position: no symbol at ${line}:${character}.`;
1644
- }
1645
- return "Invalid position: no symbol detected.";
1646
- }
1647
- const lowered = token.toLowerCase();
1648
- if (KEYWORD_TOKENS.has(lowered)) {
1649
- return `Invalid position: keyword "${token}".`;
1650
- }
1651
- if (OPERATOR_TOKENS.has(token)) {
1652
- return `Invalid position: operator "${token}".`;
1653
- }
1654
- return null;
1655
- }
1656
- function toProjectRelativeIfPossible(filePath) {
1657
- const cwd = getCwd();
1658
- try {
1659
- const rel = relative2(cwd, filePath);
1660
- if (!rel || rel === "") return filePath;
1661
- if (rel.startsWith("..")) return filePath;
1662
- return rel;
1663
- } catch {
1664
- return filePath;
1665
- }
1666
- }
1667
- function summarizeToolResult(operation, resultCount, fileCount) {
1668
- const label = OPERATION_LABELS[operation] ?? {
1669
- singular: "result",
1670
- plural: "results"
1671
- };
1672
- const noun = resultCount === 1 ? label.singular : label.plural;
1673
- if (operation === "hover" && resultCount > 0 && label.special) {
1674
- return /* @__PURE__ */ React5.createElement(Text5, null, "Hover info ", label.special);
1675
- }
1676
- return /* @__PURE__ */ React5.createElement(Text5, null, "Found ", /* @__PURE__ */ React5.createElement(Text5, { bold: true }, resultCount), " ", noun, fileCount > 1 ? /* @__PURE__ */ React5.createElement(React5.Fragment, null, " ", "across ", /* @__PURE__ */ React5.createElement(Text5, { bold: true }, fileCount), " files") : null);
1677
- }
1678
- var LspTool = {
1679
- name: TOOL_NAME_FOR_PROMPT2,
1680
- async description() {
1681
- return DESCRIPTION3;
1682
- },
1683
- async prompt() {
1684
- return PROMPT4;
1685
- },
1686
- inputSchema: inputSchema5,
1687
- userFacingName() {
1688
- return "LSP";
1689
- },
1690
- async isEnabled() {
1691
- return true;
1692
- },
1693
- isReadOnly() {
1694
- return true;
1695
- },
1696
- isConcurrencySafe() {
1697
- return true;
1698
- },
1699
- needsPermissions({ filePath }) {
1700
- const abs = getAbsolutePath(filePath) ?? filePath;
1701
- return !hasReadPermission(abs || getCwd());
1702
- },
1703
- async validateInput(input) {
1704
- const parsed = inputSchema5.safeParse(input);
1705
- if (!parsed.success) {
1706
- return {
1707
- result: false,
1708
- message: `Invalid input: ${parsed.error.message}`,
1709
- errorCode: 3
1710
- };
1711
- }
1712
- const needsLocation = input.operation === "goToDefinition" || input.operation === "findReferences" || input.operation === "hover" || input.operation === "goToImplementation" || input.operation === "prepareCallHierarchy" || input.operation === "incomingCalls" || input.operation === "outgoingCalls" || input.operation === "getScope";
1713
- if (needsLocation && (input.line === void 0 || input.character === void 0)) {
1714
- return {
1715
- result: false,
1716
- message: `Operation ${input.operation} requires line and character`,
1717
- errorCode: 5
1718
- };
1719
- }
1720
- const absPath = getAbsolutePath(input.filePath) ?? input.filePath;
1721
- if (input.operation === "workspaceSymbol") {
1722
- const scopedPath = resolve2(absPath);
1723
- const cwd = getCwd();
1724
- if (scopedPath === cwd) {
1725
- return {
1726
- result: false,
1727
- message: "workspaceSymbol requires a scoped path (module or directory)",
1728
- errorCode: 6
1729
- };
1730
- }
1731
- }
1732
- if (!existsSync3(absPath)) {
1733
- return {
1734
- result: false,
1735
- message: `File does not exist: ${input.filePath}`,
1736
- errorCode: 1
1737
- };
1738
- }
1739
- try {
1740
- if (input.operation !== "workspaceSymbol" && !statSync2(absPath).isFile()) {
1741
- return {
1742
- result: false,
1743
- message: `Path is not a file: ${input.filePath}`,
1744
- errorCode: 2
1745
- };
1746
- }
1747
- } catch (err) {
1748
- const e = err instanceof Error ? err : new Error(String(err));
1749
- return {
1750
- result: false,
1751
- message: `Cannot access file: ${input.filePath}. ${e.message}`,
1752
- errorCode: 4
1753
- };
1754
- }
1755
- return { result: true };
1756
- },
1757
- renderToolUseMessage(input, { verbose }) {
1758
- const abs = getAbsolutePath(input.filePath) ?? input.filePath;
1759
- const filePathForDisplay = verbose ? abs : toProjectRelativeIfPossible(abs);
1760
- const parts = [];
1761
- if ((input.operation === "goToDefinition" || input.operation === "findReferences" || input.operation === "hover" || input.operation === "goToImplementation") && input.filePath && input.line !== void 0 && input.character !== void 0) {
1762
- try {
1763
- const content = readFileSync2(abs, "utf8");
1764
- const symbol = extractSymbolAtPosition(
1765
- content.split("\n"),
1766
- input.line - 1,
1767
- input.character - 1
1768
- );
1769
- if (symbol) {
1770
- parts.push(`operation: "${input.operation}"`);
1771
- parts.push(`symbol: "${symbol}"`);
1772
- parts.push(`in: "${filePathForDisplay}"`);
1773
- return parts.join(", ");
1774
- }
1775
- } catch {
1776
- }
1777
- parts.push(`operation: "${input.operation}"`);
1778
- parts.push(`file: "${filePathForDisplay}"`);
1779
- parts.push(`position: ${input.line}:${input.character}`);
1780
- return parts.join(", ");
1781
- }
1782
- parts.push(`operation: "${input.operation}"`);
1783
- if (input.filePath) parts.push(`file: "${filePathForDisplay}"`);
1784
- return parts.join(", ");
1785
- },
1786
- renderToolUseRejectedMessage() {
1787
- return /* @__PURE__ */ React5.createElement(FallbackToolUseRejectedMessage, null);
1788
- },
1789
- renderToolResultMessage(output, { verbose }) {
1790
- if (output.resultCount !== void 0 && output.fileCount !== void 0) {
1791
- const display = verbose ? maybeTruncateVerboseToolOutput(output.result, {
1792
- maxLines: 120,
1793
- maxChars: 2e4
1794
- }) : null;
1795
- return /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "column" }, /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "row" }, /* @__PURE__ */ React5.createElement(Text5, null, "\xA0\xA0\u23BF \xA0"), summarizeToolResult(
1796
- output.operation,
1797
- output.resultCount,
1798
- output.fileCount
1799
- )), display ? /* @__PURE__ */ React5.createElement(Box5, { marginLeft: 5 }, /* @__PURE__ */ React5.createElement(Text5, null, display.text)) : null);
1800
- }
1801
- return /* @__PURE__ */ React5.createElement(Box5, { justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React5.createElement(Box5, { flexDirection: "row" }, /* @__PURE__ */ React5.createElement(Text5, null, "\xA0\xA0\u23BF \xA0"), /* @__PURE__ */ React5.createElement(Text5, null, output.result)));
1802
- },
1803
- renderResultForAssistant(output) {
1804
- return output.result;
1805
- },
1806
- async *call(input, _context) {
1807
- const absPath = getAbsolutePath(input.filePath) ?? input.filePath;
1808
- const needsLocation = input.operation === "goToDefinition" || input.operation === "findReferences" || input.operation === "hover" || input.operation === "goToImplementation" || input.operation === "prepareCallHierarchy" || input.operation === "incomingCalls" || input.operation === "outgoingCalls" || input.operation === "getScope";
1809
- let adjustedLine = input.line;
1810
- let adjustedCharacter = input.character;
1811
- if (needsLocation && (input.line === void 0 || input.character === void 0)) {
1812
- const out2 = {
1813
- operation: input.operation,
1814
- result: "Missing line/character. Use Read to locate the symbol and provide 1-based line + character.",
1815
- filePath: input.filePath
1816
- };
1817
- yield { type: "result", data: out2, resultForAssistant: out2.result };
1818
- return;
1819
- }
1820
- if (needsLocation && input.line !== void 0 && input.character !== void 0) {
1821
- try {
1822
- const content = readFileSync2(absPath, "utf8");
1823
- const lines = content.split("\n");
1824
- const token = extractSymbolAtPosition(
1825
- lines,
1826
- input.line - 1,
1827
- input.character - 1
1828
- );
1829
- const guardMessage = getPositionGuardMessage(
1830
- token,
1831
- input.line,
1832
- input.character
1833
- );
1834
- if (guardMessage) {
1835
- const fallback = findNearestValidTokenPosition(
1836
- lines,
1837
- input.line - 1,
1838
- input.character - 1
1839
- );
1840
- if (!fallback) {
1841
- const out2 = {
1842
- operation: input.operation,
1843
- result: guardMessage,
1844
- filePath: input.filePath
1845
- };
1846
- yield { type: "result", data: out2, resultForAssistant: out2.result };
1847
- return;
1848
- }
1849
- adjustedLine = fallback.line + 1;
1850
- adjustedCharacter = fallback.character + 1;
1851
- }
1852
- } catch {
1853
- }
1854
- }
1855
- if (input.operation === "workspaceSymbol") {
1856
- if (!input.query || input.query.trim() === "") {
1857
- const out2 = {
1858
- operation: input.operation,
1859
- result: "Missing query. Provide a workspaceSymbol query string.",
1860
- filePath: input.filePath
1861
- };
1862
- yield { type: "result", data: out2, resultForAssistant: out2.result };
1863
- return;
1864
- }
1865
- }
1866
- const workspaceRoot = input.operation === "workspaceSymbol" ? statSync2(absPath).isDirectory() ? absPath : getSessionRoot() : void 0;
1867
- const targetPath = input.operation === "workspaceSymbol" ? absPath : absPath;
1868
- const formattedResult = await LspFacade.run({
1869
- operation: input.operation,
1870
- filePath: targetPath,
1871
- line: adjustedLine,
1872
- character: adjustedCharacter,
1873
- waitForDiagnostics: input.waitForDiagnostics,
1874
- timeoutMs: input.timeout,
1875
- query: input.operation === "workspaceSymbol" ? input.query : void 0,
1876
- rootPath: workspaceRoot
1877
- });
1878
- const out = {
1879
- operation: input.operation,
1880
- result: formattedResult.formatted,
1881
- filePath: input.filePath,
1882
- resultCount: formattedResult.resultCount,
1883
- fileCount: formattedResult.fileCount
1884
- };
1885
- yield { type: "result", data: out, resultForAssistant: out.result };
1886
- }
1887
- };
1888
-
1889
- // src/tools/mcp/ReadMcpResourceTool/ReadMcpResourceTool.tsx
1890
- import { Box as Box6, Text as Text6 } from "ink";
1891
- import React6 from "react";
1892
- import { z as z6 } from "zod";
1893
- import { ReadResourceResultSchema } from "@modelcontextprotocol/sdk/types.js";
1894
-
1895
- // src/tools/mcp/ReadMcpResourceTool/prompt.ts
1896
- var TOOL_NAME2 = "ReadMcpResourceTool";
1897
- var DESCRIPTION4 = `Reads a specific resource from an MCP server.
1898
- - server: The name of the MCP server to read from
1899
- - uri: The URI of the resource to read
1900
-
1901
- Usage examples:
1902
- - Read a resource from a server: \`readMcpResource({ server: "myserver", uri: "my-resource-uri" })\``;
1903
- var PROMPT5 = `Reads a specific resource from an MCP server, identified by server name and resource URI.
1904
-
1905
- Parameters:
1906
- - server (required): The name of the MCP server from which to read the resource
1907
- - uri (required): The URI of the resource to read`;
1908
-
1909
- // src/tools/mcp/ReadMcpResourceTool/ReadMcpResourceTool.tsx
1910
- var inputSchema6 = z6.strictObject({
1911
- server: z6.string().describe("The MCP server name"),
1912
- uri: z6.string().describe("The resource URI to read")
1913
- });
1914
- var ReadMcpResourceTool = {
1915
- name: TOOL_NAME2,
1916
- async description() {
1917
- return DESCRIPTION4;
1918
- },
1919
- async prompt() {
1920
- return PROMPT5;
1921
- },
1922
- inputSchema: inputSchema6,
1923
- userFacingName() {
1924
- return "readMcpResource";
1925
- },
1926
- async isEnabled() {
1927
- return true;
1928
- },
1929
- isReadOnly() {
1930
- return true;
1931
- },
1932
- isConcurrencySafe() {
1933
- return true;
1934
- },
1935
- needsPermissions() {
1936
- return false;
1937
- },
1938
- async validateInput({ server }, context) {
1939
- const clients = context?.options?.mcpClients ?? await getClients();
1940
- const match = clients.find((c) => c.name === server);
1941
- if (!match) {
1942
- return {
1943
- result: false,
1944
- message: `Server "${server}" not found. Available servers: ${clients.map((c) => c.name).join(", ")}`,
1945
- errorCode: 1
1946
- };
1947
- }
1948
- if (match.type !== "connected") {
1949
- return {
1950
- result: false,
1951
- message: `Server "${server}" is not connected`,
1952
- errorCode: 2
1953
- };
1954
- }
1955
- let capabilities = match.capabilities ?? null;
1956
- if (!capabilities) {
1957
- try {
1958
- capabilities = match.client.getServerCapabilities();
1959
- } catch {
1960
- capabilities = null;
1961
- }
1962
- }
1963
- if (!capabilities?.resources) {
1964
- return {
1965
- result: false,
1966
- message: `Server "${server}" does not support resources`,
1967
- errorCode: 3
1968
- };
1969
- }
1970
- return { result: true };
1971
- },
1972
- renderToolUseMessage({ server, uri }) {
1973
- if (!server || !uri) return null;
1974
- return `Read resource "${uri}" from server "${server}"`;
1975
- },
1976
- renderToolUseRejectedMessage() {
1977
- return /* @__PURE__ */ React6.createElement(FallbackToolUseRejectedMessage, null);
1978
- },
1979
- renderToolResultMessage(output) {
1980
- const count = output.contents?.length ?? 0;
1981
- return /* @__PURE__ */ React6.createElement(Box6, { justifyContent: "space-between", width: "100%" }, /* @__PURE__ */ React6.createElement(Box6, { flexDirection: "row" }, /* @__PURE__ */ React6.createElement(Text6, null, "\xA0\xA0\u23BF \xA0"), /* @__PURE__ */ React6.createElement(Text6, { bold: true }, "Read MCP resource"), /* @__PURE__ */ React6.createElement(Text6, null, count ? ` (${count} part${count === 1 ? "" : "s"})` : "")), /* @__PURE__ */ React6.createElement(Cost, { costUSD: 0, durationMs: 0, debug: false }));
1982
- },
1983
- renderResultForAssistant(output) {
1984
- return JSON.stringify(output);
1985
- },
1986
- async *call({ server, uri }, context) {
1987
- const clients = context.options?.mcpClients ?? await getClients();
1988
- const match = clients.find((c) => c.name === server);
1989
- if (!match) {
1990
- throw new Error(
1991
- `Server "${server}" not found. Available servers: ${clients.map((c) => c.name).join(", ")}`
1992
- );
1993
- }
1994
- if (match.type !== "connected") {
1995
- throw new Error(`Server "${server}" is not connected`);
1996
- }
1997
- let capabilities = match.capabilities ?? null;
1998
- if (!capabilities) {
1999
- try {
2000
- capabilities = match.client.getServerCapabilities();
2001
- } catch {
2002
- capabilities = null;
2003
- }
2004
- }
2005
- if (!capabilities?.resources) {
2006
- throw new Error(`Server "${server}" does not support resources`);
2007
- }
2008
- const result = await match.client.request(
2009
- { method: "resources/read", params: { uri } },
2010
- ReadResourceResultSchema
2011
- );
2012
- yield {
2013
- type: "result",
2014
- data: result,
2015
- resultForAssistant: this.renderResultForAssistant(result)
2016
- };
2017
- }
2018
- };
2019
-
2020
- // src/tools/agent/TaskTool/TaskTool.tsx
2021
- import { last, memoize } from "lodash-es";
2022
- import React7 from "react";
2023
- import { Box as Box7, Text as Text7 } from "ink";
2024
- import { z as z7 } from "zod";
2025
- import { randomUUID as randomUUID3 } from "crypto";
2026
- import { existsSync as existsSync4, readFileSync as readFileSync3 } from "fs";
2027
-
2028
- // src/utils/agent/storage.ts
2029
- import { randomUUID as randomUUID2 } from "crypto";
2030
- function getDefaultAgentId() {
2031
- return "default";
2032
- }
2033
- function resolveAgentId(agentId) {
2034
- return agentId || getDefaultAgentId();
2035
- }
2036
- function generateAgentId() {
2037
- return randomUUID2();
2038
- }
2039
-
2040
- // src/utils/agent/transcripts.ts
2041
- var transcripts = /* @__PURE__ */ new Map();
2042
- function saveAgentTranscript(agentId, messages) {
2043
- transcripts.set(agentId, messages);
2044
- }
2045
- function getAgentTranscript(agentId) {
2046
- return transcripts.get(agentId);
2047
- }
2048
-
2049
- // src/tools/agent/TaskTool/prompt.ts
2050
- var SUBAGENT_DISALLOWED_TOOL_NAMES = /* @__PURE__ */ new Set([
2051
- "Task",
2052
- "TaskOutput",
2053
- "KillShell",
2054
- "AskUserQuestion",
2055
- "MemoryRead",
2056
- "MemoryWrite"
2057
- ]);
2058
- async function getTaskTools(safeMode) {
2059
- return (await (!safeMode ? getTools() : getReadOnlyTools())).filter(
2060
- (tool) => !SUBAGENT_DISALLOWED_TOOL_NAMES.has(tool.name)
2061
- );
2062
- }
2063
- async function getPrompt(safeMode) {
2064
- const agents = await getActiveAgents();
2065
- const agentDescriptions = agents.map((agent) => {
2066
- const toolsStr = Array.isArray(agent.tools) ? agent.tools.join(", ") : "*";
2067
- return `- ${agent.agentType}: ${agent.whenToUse} (Tools: ${toolsStr})`;
2068
- }).join("\n");
2069
- return `Launch a new agent to handle complex, multi-step tasks autonomously.
2070
-
2071
- Available agent types and the tools they have access to:
2072
- ${agentDescriptions}
2073
-
2074
- When using the Task tool, you must specify a subagent_type parameter to select which agent type to use.
2075
-
2076
- When to use the Agent tool:
2077
- - When you are instructed to execute custom slash commands. Use the Agent tool with the slash command invocation as the entire prompt. The slash command can take arguments. For example: Task(description="Check the file", prompt="/check-file path/to/file.py")
2078
-
2079
- When NOT to use the Agent tool:
2080
- - If you want to read a specific file path, use the ${FileReadTool.name} or ${GlobTool.name} tool instead of the Agent tool, to find the match more quickly
2081
- - If you are searching for a specific class definition like "class Foo", use the ${GlobTool.name} tool instead, to find the match more quickly
2082
- - If you are searching for code within a specific file or set of 2-3 files, use the ${FileReadTool.name} tool instead of the Agent tool, to find the match more quickly
2083
- - Other tasks that are not related to the agent descriptions above
2084
-
2085
- Usage notes:
2086
- 1. Launch multiple agents concurrently whenever possible, to maximize performance; to do that, use a single message with multiple tool uses
2087
- 2. When the agent is done, it will return a single message back to you. The result returned by the agent is not visible to the user. To show the user the result, you should send a text message back to the user with a concise summary of the result.
2088
- 3. Each agent invocation is stateless. You will not be able to send additional messages to the agent, nor will the agent be able to communicate with you outside of its final report. Therefore, your prompt should contain a highly detailed task description for the agent to perform autonomously and you should specify exactly what information the agent should return back to you in its final and only message to you.
2089
- 4. The agent's outputs should generally be trusted
2090
- 5. Clearly tell the agent whether you expect it to write code or just to do research (search, file reads, web fetches, etc.), since it is not aware of the user's intent
2091
- 6. If the agent description mentions that it should be used proactively, then you should try your best to use it without the user having to ask for it first. Use your judgement.
2092
-
2093
- Example usage:
2094
-
2095
- <example_agent_descriptions>
2096
- "code-reviewer": use this agent after you are done writing a signficant piece of code
2097
- "greeting-responder": use this agent when to respond to user greetings with a friendly joke
2098
- </example_agent_description>
2099
-
2100
- <example>
2101
- user: "Please write a function that checks if a number is prime"
2102
- assistant: Sure let me write a function that checks if a number is prime
2103
- assistant: First let me use the ${FileWriteTool.name} tool to write a function that checks if a number is prime
2104
- assistant: I'm going to use the ${FileWriteTool.name} tool to write the following code:
2105
- <code>
2106
- function isPrime(n) {
2107
- if (n <= 1) return false
2108
- for (let i = 2; i * i <= n; i++) {
2109
- if (n % i === 0) return false
2110
- }
2111
- return true
2112
- }
2113
- </code>
2114
- <commentary>
2115
- Since a signficant piece of code was written and the task was completed, now use the code-reviewer agent to review the code
2116
- </commentary>
2117
- assistant: Now let me use the code-reviewer agent to review the code
2118
- assistant: Uses the Task tool to launch the with the code-reviewer agent
2119
- </example>
2120
-
2121
- <example>
2122
- user: "Hello"
2123
- <commentary>
2124
- Since the user is greeting, use the greeting-responder agent to respond with a friendly joke
2125
- </commentary>
2126
- assistant: "I'm going to use the Task tool to launch the with the greeting-responder agent"
2127
- </example>`;
2128
- }
2129
-
2130
- // src/tools/agent/TaskTool/constants.ts
2131
- var TOOL_NAME3 = "Task";
2132
-
2133
- // src/tools/agent/TaskTool/TaskTool.tsx
2134
- var inputSchema7 = z7.object({
2135
- description: z7.string().describe("A short (3-5 word) description of the task"),
2136
- prompt: z7.string().describe("The task for the agent to perform"),
2137
- subagent_type: z7.string().describe("The type of specialized agent to use for this task"),
2138
- model: z7.enum(["sonnet", "opus", "haiku"]).optional().describe(
2139
- "Optional model to use for this agent. If not specified, inherits from parent. Prefer haiku for quick, straightforward tasks to minimize cost and latency."
2140
- ),
2141
- resume: z7.string().optional().describe(
2142
- "Optional agent ID to resume from. If provided, the agent will continue from the previous execution transcript."
2143
- ),
2144
- run_in_background: z7.boolean().optional().describe(
2145
- "Set to true to run this agent in the background. Use TaskOutput to read the output later."
2146
- )
2147
- });
2148
- function modelEnumToPointer(model) {
2149
- if (!model) return void 0;
2150
- switch (model) {
2151
- case "haiku":
2152
- return "quick";
2153
- case "sonnet":
2154
- return "task";
2155
- case "opus":
2156
- return "main";
2157
- }
2158
- }
2159
- function normalizeAgentModelName(model) {
2160
- if (!model) return void 0;
2161
- if (model === "inherit") return "inherit";
2162
- if (model === "haiku" || model === "sonnet" || model === "opus") {
2163
- return modelEnumToPointer(model);
2164
- }
2165
- return model;
2166
- }
2167
- function getToolNameFromSpec(spec) {
2168
- const trimmed = spec.trim();
2169
- if (!trimmed) return trimmed;
2170
- const match = trimmed.match(/^([^(]+)\(([^)]+)\)$/);
2171
- if (!match) return trimmed;
2172
- const toolName = match[1]?.trim();
2173
- const ruleContent = match[2]?.trim();
2174
- if (!toolName || !ruleContent) return trimmed;
2175
- return toolName;
2176
- }
2177
- function asyncLaunchMessage(agentId) {
2178
- return buildTaskAsyncPolicy(agentId);
2179
- }
2180
- var FORK_CONTEXT_TOOL_RESULT_TEXT = TASK_FORK_CONTEXT_POLICY;
2181
- var TOOL_PROGRESS_OPEN_TAG2 = ["<tool", "-progress>"].join("");
2182
- var TOOL_PROGRESS_CLOSE_TAG2 = ["</tool", "-progress>"].join("");
2183
- function resolveParentSessionId(toolUseContext) {
2184
- const direct = typeof toolUseContext?.sessionId === "string" ? toolUseContext.sessionId.trim() : "";
2185
- if (direct) return direct;
2186
- const active = getCurrentSessionId();
2187
- if (typeof active === "string" && active.trim()) {
2188
- return active.trim();
2189
- }
2190
- return void 0;
2191
- }
2192
- function normalizeAgentPermissionMode(mode) {
2193
- if (typeof mode !== "string") return void 0;
2194
- const trimmed = mode.trim();
2195
- if (!trimmed) return void 0;
2196
- if (trimmed === "delegate") return "default";
2197
- if (trimmed === "default" || trimmed === "acceptEdits" || trimmed === "plan" || trimmed === "bypassPermissions" || trimmed === "dontAsk") {
2198
- return trimmed;
2199
- }
2200
- return void 0;
2201
- }
2202
- function applyAgentPermissionMode(base, options) {
2203
- if (!base) return base;
2204
- if (!options.agentPermissionMode) return base;
2205
- if (options.agentPermissionMode === "bypassPermissions" && (options.safeMode || base.isBypassPermissionsModeAvailable !== true)) {
2206
- return { ...base, mode: "default" };
2207
- }
2208
- return { ...base, mode: options.agentPermissionMode };
2209
- }
2210
- function readJsonArrayFile(path) {
2211
- if (!existsSync4(path)) return null;
2212
- try {
2213
- const raw = readFileSync3(path, "utf8");
2214
- const parsed = JSON.parse(raw);
2215
- return Array.isArray(parsed) ? parsed : null;
2216
- } catch {
2217
- return null;
2218
- }
2219
- }
2220
- function buildForkContextForAgent(options) {
2221
- const userPromptMessage = createUserMessageFromText(options.prompt);
2222
- if (!options.enabled || !options.toolUseId) {
2223
- return {
2224
- forkContextMessages: [],
2225
- promptMessages: [userPromptMessage]
2226
- };
2227
- }
2228
- const mainPath = getMessagesPath(options.messageLogName, options.forkNumber, 0);
2229
- const mainMessages = readJsonArrayFile(mainPath);
2230
- if (!mainMessages || mainMessages.length === 0) {
2231
- return {
2232
- forkContextMessages: [],
2233
- promptMessages: [userPromptMessage]
2234
- };
2235
- }
2236
- let toolUseMessageIndex = -1;
2237
- let toolUseMessage = null;
2238
- let taskToolUseBlock = null;
2239
- for (let i = 0; i < mainMessages.length; i++) {
2240
- const msg = mainMessages[i];
2241
- if (msg?.type !== "assistant") continue;
2242
- const blocks = Array.isArray(msg?.message?.content) ? msg.message.content : [];
2243
- const match = blocks.find(
2244
- (b) => b && b.type === "tool_use" && b.id === options.toolUseId
2245
- );
2246
- if (!match) continue;
2247
- toolUseMessageIndex = i;
2248
- toolUseMessage = msg;
2249
- taskToolUseBlock = match;
2250
- break;
2251
- }
2252
- if (toolUseMessageIndex === -1 || !toolUseMessage || !taskToolUseBlock) {
2253
- return {
2254
- forkContextMessages: [],
2255
- promptMessages: [userPromptMessage]
2256
- };
2257
- }
2258
- const forkContextMessages = mainMessages.slice(
2259
- 0,
2260
- toolUseMessageIndex
2261
- ) ?? [];
2262
- const toolUseOnlyAssistant = {
2263
- ...toolUseMessage,
2264
- uuid: randomUUID3(),
2265
- message: {
2266
- ...toolUseMessage.message,
2267
- content: [taskToolUseBlock]
2268
- }
2269
- };
2270
- const forkContextToolResult = createUserMessageFromBlocks(
2271
- [
2272
- {
2273
- type: "tool_result",
2274
- tool_use_id: taskToolUseBlock.id,
2275
- content: FORK_CONTEXT_TOOL_RESULT_TEXT
2276
- }
2277
- ],
2278
- {
2279
- data: {
2280
- status: "sub_agent_entered",
2281
- description: "Entered sub-agent context",
2282
- message: FORK_CONTEXT_TOOL_RESULT_TEXT
2283
- },
2284
- resultForAssistant: FORK_CONTEXT_TOOL_RESULT_TEXT
2285
- }
2286
- );
2287
- return {
2288
- forkContextMessages,
2289
- promptMessages: [toolUseOnlyAssistant, forkContextToolResult, userPromptMessage]
2290
- };
2291
- }
2292
- var TaskTool = {
2293
- name: TOOL_NAME3,
2294
- inputSchema: inputSchema7,
2295
- async description() {
2296
- return "Delegate complex, multi-step work to specialized agents";
2297
- },
2298
- async prompt({ safeMode }) {
2299
- return await getPrompt(safeMode);
2300
- },
2301
- userFacingName(input) {
2302
- if (input?.subagent_type && input.subagent_type !== "general-purpose") {
2303
- return input.subagent_type;
2304
- }
2305
- return "Task";
2306
- },
2307
- async isEnabled() {
2308
- return true;
2309
- },
2310
- isReadOnly() {
2311
- return true;
2312
- },
2313
- isConcurrencySafe() {
2314
- return true;
2315
- },
2316
- needsPermissions() {
2317
- return false;
2318
- },
2319
- async validateInput(input) {
2320
- if (!input.description || typeof input.description !== "string") {
2321
- return {
2322
- result: false,
2323
- message: "Description is required and must be a string"
2324
- };
2325
- }
2326
- if (!input.prompt || typeof input.prompt !== "string") {
2327
- return {
2328
- result: false,
2329
- message: "Prompt is required and must be a string"
2330
- };
2331
- }
2332
- const availableTypes = await getAvailableAgentTypes();
2333
- if (!availableTypes.includes(input.subagent_type)) {
2334
- return {
2335
- result: false,
2336
- message: `Agent type '${input.subagent_type}' not found. Available agents: ${availableTypes.join(", ")}`,
2337
- meta: { subagent_type: input.subagent_type, availableTypes }
2338
- };
2339
- }
2340
- if (input.resume) {
2341
- const transcript = getAgentTranscript(input.resume);
2342
- if (!transcript) {
2343
- return {
2344
- result: false,
2345
- message: `No transcript found for agent ID: ${input.resume}`,
2346
- meta: { resume: input.resume }
2347
- };
2348
- }
2349
- }
2350
- return { result: true };
2351
- },
2352
- renderToolUseMessage({ description, prompt }) {
2353
- if (!description || !prompt) return "";
2354
- return description;
2355
- },
2356
- renderToolUseRejectedMessage() {
2357
- return /* @__PURE__ */ React7.createElement(FallbackToolUseRejectedMessage, null);
2358
- },
2359
- renderToolResultMessage(output, { verbose }) {
2360
- const theme = getTheme();
2361
- if (output.status === "async_launched") {
2362
- const hint = output.prompt ? " (down arrow \u2193 to manage \xB7 ctrl+o to expand)" : " (down arrow \u2193 to manage)";
2363
- return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "row" }, /* @__PURE__ */ React7.createElement(Text7, null, "\xA0\xA0\u23BF \xA0"), /* @__PURE__ */ React7.createElement(Text7, null, "Backgrounded agent", !verbose && /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, hint))), verbose && output.prompt && /* @__PURE__ */ React7.createElement(
2364
- Box7,
2365
- {
2366
- paddingLeft: 2,
2367
- borderLeftStyle: "single",
2368
- borderLeftColor: theme.secondaryBorder
2369
- },
2370
- /* @__PURE__ */ React7.createElement(Text7, { color: theme.secondaryText, wrap: "wrap" }, output.prompt)
2371
- ));
2372
- }
2373
- const summary = [
2374
- output.totalToolUseCount === 1 ? "1 tool use" : `${output.totalToolUseCount} tool uses`,
2375
- `${formatNumber(output.totalTokens)} tokens`,
2376
- formatDuration(output.totalDurationMs)
2377
- ];
2378
- return /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "column" }, /* @__PURE__ */ React7.createElement(Box7, { flexDirection: "row" }, /* @__PURE__ */ React7.createElement(Text7, null, "\xA0\xA0\u23BF \xA0"), /* @__PURE__ */ React7.createElement(Text7, { dimColor: true }, "Done (", summary.join(" \xB7 "), ")")));
2379
- },
2380
- renderResultForAssistant(output) {
2381
- if (output.status === "async_launched")
2382
- return asyncLaunchMessage(output.agentId);
2383
- return output.content.map((b) => b.text).join("\n");
2384
- },
2385
- async *call(input, toolUseContext) {
2386
- const startTime = Date.now();
2387
- const {
2388
- abortController,
2389
- toolUseId,
2390
- options: {
2391
- safeMode = false,
2392
- forkNumber,
2393
- messageLogName,
2394
- verbose,
2395
- model: parentModel,
2396
- mcpClients
2397
- },
2398
- readFileTimestamps
2399
- } = toolUseContext;
2400
- const queryFn = typeof toolUseContext?.__testQuery === "function" ? toolUseContext.__testQuery : query;
2401
- const agentConfig = await getAgentByType(input.subagent_type);
2402
- if (!agentConfig) {
2403
- const available = await getAvailableAgentTypes();
2404
- throw Error(
2405
- `Agent type '${input.subagent_type}' not found. Available agents: ${available.join(", ")}`
2406
- );
2407
- }
2408
- const effectivePrompt = input.prompt;
2409
- const normalizedAgentModel = normalizeAgentModelName(agentConfig.model);
2410
- const defaultSubagentModel = "task";
2411
- const envSubagentModel = process.env.PYB_SUBAGENT_MODEL ?? process.env.CLAUDE_CODE_SUBAGENT_MODEL;
2412
- const modelToUse = (typeof envSubagentModel === "string" && envSubagentModel.trim() ? envSubagentModel.trim() : void 0) || modelEnumToPointer(input.model) || (normalizedAgentModel === "inherit" ? parentModel || defaultSubagentModel : normalizedAgentModel) || defaultSubagentModel;
2413
- const toolFilter = agentConfig.tools;
2414
- let tools = await getTaskTools(safeMode);
2415
- if (toolFilter) {
2416
- const isAllArray = Array.isArray(toolFilter) && toolFilter.length === 1 && toolFilter[0] === "*";
2417
- if (toolFilter === "*" || isAllArray) {
2418
- } else if (Array.isArray(toolFilter)) {
2419
- const allowedToolNames = new Set(
2420
- toolFilter.map(getToolNameFromSpec).filter(Boolean)
2421
- );
2422
- tools = tools.filter((t) => allowedToolNames.has(t.name));
2423
- }
2424
- }
2425
- const disallowedTools = Array.isArray(agentConfig.disallowedTools) ? agentConfig.disallowedTools : [];
2426
- if (disallowedTools.length > 0) {
2427
- const disallowedToolNames = new Set(
2428
- disallowedTools.map(getToolNameFromSpec).filter(Boolean)
2429
- );
2430
- tools = tools.filter((t) => !disallowedToolNames.has(t.name));
2431
- }
2432
- const agentId = input.resume || generateAgentId();
2433
- const parentSessionId = resolveParentSessionId(toolUseContext);
2434
- const childSessionId = agentId;
2435
- const taskListId = generateTaskListId();
2436
- const baseTranscript = input.resume ? getAgentTranscript(input.resume)?.filter((m) => m.type !== "progress") ?? null : [];
2437
- if (input.resume && baseTranscript === null) {
2438
- throw Error(`No transcript found for agent ID: ${input.resume}`);
2439
- }
2440
- const { forkContextMessages, promptMessages } = buildForkContextForAgent({
2441
- enabled: agentConfig.forkContext === true,
2442
- prompt: effectivePrompt,
2443
- toolUseId,
2444
- messageLogName,
2445
- forkNumber
2446
- });
2447
- const transcriptMessages = [
2448
- ...baseTranscript || [],
2449
- ...promptMessages
2450
- ];
2451
- const messagesForQuery = [
2452
- ...forkContextMessages,
2453
- ...transcriptMessages
2454
- ];
2455
- const [baseSystemPrompt, context, maxThinkingTokens] = await Promise.all([
2456
- getAgentPrompt(),
2457
- getContext(),
2458
- getMaxThinkingTokens(messagesForQuery)
2459
- ]);
2460
- const systemPrompt = agentConfig.systemPrompt && agentConfig.systemPrompt.length > 0 ? [...baseSystemPrompt, agentConfig.systemPrompt] : baseSystemPrompt;
2461
- const agentPermissionMode = normalizeAgentPermissionMode(
2462
- agentConfig.permissionMode
2463
- );
2464
- const toolPermissionContext = applyAgentPermissionMode(
2465
- toolUseContext.options?.toolPermissionContext,
2466
- { agentPermissionMode, safeMode }
2467
- );
2468
- const queryOptions = {
2469
- safeMode,
2470
- forkNumber,
2471
- messageLogName,
2472
- tools,
2473
- commands: [],
2474
- verbose,
2475
- permissionMode: "dontAsk",
2476
- toolPermissionContext,
2477
- maxThinkingTokens,
2478
- model: modelToUse,
2479
- mcpClients
2480
- };
2481
- if (input.run_in_background) {
2482
- const bgAbortController = new AbortController();
2483
- const taskRecord = {
2484
- type: "async_agent",
2485
- agentId,
2486
- description: input.description,
2487
- prompt: effectivePrompt,
2488
- status: "running",
2489
- startedAt: Date.now(),
2490
- messages: [...transcriptMessages],
2491
- abortController: bgAbortController,
2492
- done: Promise.resolve()
2493
- };
2494
- taskRecord.done = (async () => {
2495
- return runWithTaskListId(taskListId, async () => {
2496
- return runWithTaskListEnv(
2497
- { ...process.env, PYB_TASK_LIST_ID: taskListId },
2498
- async () => {
2499
- try {
2500
- const bgMessages = [...messagesForQuery];
2501
- const bgTranscriptMessages = [...transcriptMessages];
2502
- for await (const msg of queryFn(
2503
- bgMessages,
2504
- systemPrompt,
2505
- context,
2506
- hasPermissionsToUseTool,
2507
- {
2508
- abortController: bgAbortController,
2509
- options: queryOptions,
2510
- messageId: getLastAssistantMessageId(bgMessages),
2511
- sessionId: childSessionId,
2512
- parentSessionId,
2513
- childSessionId,
2514
- agentId,
2515
- agentType: input.subagent_type,
2516
- readFileTimestamps,
2517
- setToolJSX: () => {
2518
- }
2519
- }
2520
- )) {
2521
- bgMessages.push(msg);
2522
- bgTranscriptMessages.push(msg);
2523
- taskRecord.messages = [...bgTranscriptMessages];
2524
- upsertBackgroundAgentTask(taskRecord);
2525
- }
2526
- const lastAssistant2 = last(
2527
- bgTranscriptMessages.filter((m) => m.type === "assistant")
2528
- );
2529
- const content2 = lastAssistant2?.message?.content?.filter(
2530
- (b) => b.type === "text"
2531
- );
2532
- taskRecord.status = "completed";
2533
- taskRecord.completedAt = Date.now();
2534
- taskRecord.resultText = (content2 || []).map((b) => b.text).join("\n");
2535
- taskRecord.messages = [...bgTranscriptMessages];
2536
- upsertBackgroundAgentTask(taskRecord);
2537
- saveAgentTranscript(agentId, bgTranscriptMessages);
2538
- } catch (e) {
2539
- taskRecord.status = "failed";
2540
- taskRecord.completedAt = Date.now();
2541
- taskRecord.error = e instanceof Error ? e.message : String(e);
2542
- upsertBackgroundAgentTask(taskRecord);
2543
- }
2544
- }
2545
- );
2546
- });
2547
- })();
2548
- upsertBackgroundAgentTask(taskRecord);
2549
- const output2 = {
2550
- status: "async_launched",
2551
- agentId,
2552
- description: input.description,
2553
- prompt: effectivePrompt
2554
- };
2555
- yield {
2556
- type: "result",
2557
- data: output2,
2558
- resultForAssistant: asyncLaunchMessage(agentId)
2559
- };
2560
- return;
2561
- }
2562
- const getSidechainNumber = memoize(
2563
- () => getNextAvailableLogSidechainNumber(messageLogName, forkNumber)
2564
- );
2565
- const PROGRESS_THROTTLE_MS = 200;
2566
- let lastProgressEmitAt = 0;
2567
- let lastEmittedToolUseCount = 0;
2568
- const renderProgressText = (toolUseCount2) => {
2569
- return `${input.description || "Task"}\u2026 (${toolUseCount2} tool${toolUseCount2 === 1 ? "" : "s"})`;
2570
- };
2571
- yield {
2572
- type: "progress",
2573
- content: createAssistantMessage(
2574
- `${TOOL_PROGRESS_OPEN_TAG2}${renderProgressText(0)}${TOOL_PROGRESS_CLOSE_TAG2}`
2575
- )
2576
- };
2577
- lastProgressEmitAt = Date.now();
2578
- let toolUseCount = 0;
2579
- const queryStream = runWithTaskListId(
2580
- taskListId,
2581
- () => runWithTaskListEnv(
2582
- { ...process.env, PYB_TASK_LIST_ID: taskListId },
2583
- () => queryFn(
2584
- messagesForQuery,
2585
- systemPrompt,
2586
- context,
2587
- hasPermissionsToUseTool,
2588
- {
2589
- abortController,
2590
- options: queryOptions,
2591
- messageId: getLastAssistantMessageId(messagesForQuery),
2592
- sessionId: childSessionId,
2593
- parentSessionId,
2594
- childSessionId,
2595
- agentId,
2596
- agentType: input.subagent_type,
2597
- readFileTimestamps,
2598
- setToolJSX: () => {
2599
- }
2600
- }
2601
- )
2602
- )
2603
- );
2604
- for await (const message of queryStream) {
2605
- messagesForQuery.push(message);
2606
- transcriptMessages.push(message);
2607
- overwriteLog(
2608
- getMessagesPath(messageLogName, forkNumber, getSidechainNumber()),
2609
- transcriptMessages.filter((_) => _.type !== "progress"),
2610
- { conversationKey: `${messageLogName}:${forkNumber}` }
2611
- );
2612
- if (message.type === "assistant") {
2613
- for (const block of message.message.content) {
2614
- if (block.type === "tool_use" || block.type === "server_tool_use" || block.type === "mcp_tool_use") {
2615
- toolUseCount += 1;
2616
- }
2617
- }
2618
- }
2619
- const now = Date.now();
2620
- const hasNewToolUses = toolUseCount > lastEmittedToolUseCount;
2621
- const shouldEmit = hasNewToolUses && (lastEmittedToolUseCount === 0 || now - lastProgressEmitAt >= PROGRESS_THROTTLE_MS);
2622
- if (shouldEmit) {
2623
- yield {
2624
- type: "progress",
2625
- content: createAssistantMessage(
2626
- `${TOOL_PROGRESS_OPEN_TAG2}${renderProgressText(toolUseCount)}${TOOL_PROGRESS_CLOSE_TAG2}`
2627
- )
2628
- };
2629
- lastEmittedToolUseCount = toolUseCount;
2630
- lastProgressEmitAt = now;
2631
- }
2632
- }
2633
- const lastAssistant = last(
2634
- transcriptMessages.filter((m) => m.type === "assistant")
2635
- );
2636
- if (!lastAssistant || lastAssistant.type !== "assistant") {
2637
- throw Error("No assistant messages found");
2638
- }
2639
- const content = lastAssistant.message.content.filter(
2640
- (b) => b.type === "text"
2641
- );
2642
- saveAgentTranscript(agentId, transcriptMessages);
2643
- const totalDurationMs = Date.now() - startTime;
2644
- const totalTokens = countTokens(transcriptMessages);
2645
- const usage = lastAssistant.message.usage;
2646
- const output = {
2647
- status: "completed",
2648
- agentId,
2649
- prompt: effectivePrompt,
2650
- content,
2651
- totalToolUseCount: toolUseCount,
2652
- totalDurationMs,
2653
- totalTokens,
2654
- usage
2655
- };
2656
- const agentIdBlock = {
2657
- type: "text",
2658
- text: `agentId: ${agentId} (for resuming to continue this agent's work if needed)`,
2659
- citations: []
2660
- };
2661
- yield {
2662
- type: "result",
2663
- data: output,
2664
- resultForAssistant: [...content, agentIdBlock]
2665
- };
2666
- }
2667
- };
2668
-
2669
- // src/tools/interaction/TaskCreateTool/TaskCreateTool.tsx
2670
- import { z as z9 } from "zod";
2671
-
2672
- // src/utils/session/taskToolUtils.ts
2673
- import { z as z8 } from "zod";
2674
- var jsonValueSchema = z8.lazy(
2675
- () => z8.union([
2676
- z8.string(),
2677
- z8.number(),
2678
- z8.boolean(),
2679
- z8.null(),
2680
- z8.array(jsonValueSchema),
2681
- z8.record(jsonValueSchema)
2682
- ])
2683
- );
2684
- var metadataSchema = z8.record(jsonValueSchema);
2685
- function mapStatusToOutput(status) {
2686
- if (status === "done") return "completed";
2687
- if (status === "deleted") return "deleted";
2688
- return status;
2689
- }
2690
- function toTaskListItem(task) {
2691
- const projection = getTaskCollaborationProjection(task);
2692
- const item = {
2693
- id: task.id,
2694
- subject: task.subject,
2695
- status: mapStatusToOutput(task.status),
2696
- blockedBy: task.blockedBy ?? []
2697
- };
2698
- if (projection.owner) item.owner = projection.owner;
2699
- if (projection.worktree) item.worktree = projection.worktree;
2700
- if (projection.lane) item.lane = projection.lane;
2701
- if (projection.agent) item.agent = projection.agent;
2702
- if (projection.created_at !== void 0) item.created_at = projection.created_at;
2703
- if (projection.updated_at !== void 0) item.updated_at = projection.updated_at;
2704
- return item;
2705
- }
2706
- function toTaskDetail(task) {
2707
- return {
2708
- id: task.id,
2709
- subject: task.subject,
2710
- description: task.description,
2711
- activeForm: task.activeForm,
2712
- status: mapStatusToOutput(task.status),
2713
- metadata: task.metadata,
2714
- blocks: task.blocks ?? [],
2715
- blockedBy: task.blockedBy ?? []
2716
- };
2717
- }
2718
- function toTaskGetDetail(task) {
2719
- const projection = getTaskCollaborationProjection(task);
2720
- const detail = {
2721
- subject: task.subject,
2722
- description: task.description,
2723
- activeForm: task.activeForm,
2724
- status: mapStatusToOutput(task.status),
2725
- metadata: task.metadata,
2726
- blocks: task.blocks ?? [],
2727
- blockedBy: task.blockedBy ?? []
2728
- };
2729
- if (projection.owner) detail.owner = projection.owner;
2730
- if (projection.worktree) detail.worktree = projection.worktree;
2731
- if (projection.lane) detail.lane = projection.lane;
2732
- if (projection.agent) detail.agent = projection.agent;
2733
- if (projection.created_at !== void 0) detail.created_at = projection.created_at;
2734
- if (projection.updated_at !== void 0) detail.updated_at = projection.updated_at;
2735
- return detail;
2736
- }
2737
- function getTaskCollaborationProjection(task) {
2738
- const metadata = task.metadata ?? {};
2739
- const owner = typeof metadata.owner === "string" ? metadata.owner : void 0;
2740
- const worktree = typeof metadata.worktree === "string" ? metadata.worktree : void 0;
2741
- const lane = typeof metadata.lane === "string" ? metadata.lane : void 0;
2742
- const agent = typeof metadata.agent === "string" ? metadata.agent : void 0;
2743
- const created_at = typeof metadata.created_at === "number" || typeof metadata.created_at === "string" ? metadata.created_at : void 0;
2744
- const updated_at = typeof metadata.updated_at === "number" || typeof metadata.updated_at === "string" ? metadata.updated_at : void 0;
2745
- return { owner, worktree, lane, agent, created_at, updated_at };
2746
- }
2747
- function createTaskToolError(params) {
2748
- return {
2749
- error: {
2750
- type: params.type,
2751
- message: params.message,
2752
- code: params.code,
2753
- details: params.details
2754
- }
2755
- };
2756
- }
2757
-
2758
- // src/tools/interaction/TaskCreateTool/prompt.ts
2759
- var DESCRIPTION5 = "Plan and create tasks for complex multi-step work";
2760
- var PROMPT6 = `Use this tool to create a new task in the current task list. It is the entry point for planning multi-step work.
2761
-
2762
- ## When to Use This Tool
2763
-
2764
- Use this tool proactively in these scenarios:
2765
-
2766
- 1. **Complex multi-step tasks** - When a task requires 2 or more distinct steps or actions
2767
- 2. **Non-trivial and complex tasks** - Tasks that require careful planning or multiple operations
2768
- 3. **User explicitly requests task list** - When the user directly asks you to use the task list
2769
- 4. **User provides multiple tasks** - When users provide a list of things to be done (numbered or comma-separated)
2770
- 5. **After receiving new instructions** - Immediately capture user requirements as structured a task list or plan with structured fields
2771
-
2772
- ## When NOT to Use This Tool
2773
-
2774
- Skip using this tool only when:
2775
-
2776
- 1. The request is truly trivial and one-step
2777
- 2. It can be completed safely in less than 2 trivial steps with no follow-up work
2778
- 3. The interaction is purely conversational/informational
2779
- 4. You are only updating status or details of an existing task (use TaskUpdate)
2780
-
2781
- ## Trigger Timing
2782
- - Use TaskCreate when starting implementation work.
2783
- - Use TaskCreate when the user adds new requirements.
2784
- - Use TaskCreate when scope changes during execution.
2785
- - Use TaskCreate when a milestone is completed and the next concrete work unit begins.
2786
-
2787
- ## Core Workflow
2788
-
2789
- **The Two-Phase Pattern (IMPORTANT):**
2790
- - **Phase 1 - Create**: Create all tasks one by one WITHOUT dependencies (blockedBy=[]).
2791
- - **Phase 2 - Link**: Use TaskUpdate to add blockedBy AFTER all related tasks exist.
2792
-
2793
- This pattern ensures clean dependency graph construction and prevents reference errors.
2794
-
2795
- ## Input Notes
2796
- - subject is required.
2797
- - listId is optional; omit to use the default list.
2798
- - TaskCreate only supports single-task creation; create multiple tasks one by one.
2799
- - For multi-step plans, create the structured task list without dependencies and add blockedBy later via TaskUpdate.
2800
- - activeForm is required.
2801
- - metadata is optional and should only include stable coordination fields you want to persist.
2802
- - blockedBy is always stored as an array (empty when not used).
2803
- - description and blockedBy are kept separate in JSON; any joining is only for terminal rendering.
2804
- - Task ordering relies on task id;
2805
- - Use metadata.planFinalized="true" in TaskUpdate when dependencies are ready to be validated globally.
2806
- - Do not mention tool names or tool usage in user-facing messages.
2807
- - When uncertain whether to track, prefer tracking.
2808
-
2809
- ## Field Guidance
2810
- - subject: concise task name that reads like a goal.
2811
- - activeForm: short verb phrase that describes the in-progress action, not a full sentence.
2812
- - description: prefer a one-line overview that introduces the bullets with "Including:" plus bullet points for multi-step tasks; for very simple tasks a short sentence is fine.
2813
-
2814
- Preferred single-task template:
2815
- {
2816
- "subject": "Requirements clarification and scope definition",
2817
- "description": "Clarify the business goals, functional scope, and technical boundaries. Including:
2818
- - Confirm supported languages
2819
- - Define summary output format and quality bar
2820
- - Specify performance targets (latency, throughput)
2821
- - Identify stakeholders and acceptance criteria",
2822
- "activeForm": "Clarifying requirements and defining scope"
2823
- }
2824
-
2825
- ## Status Notes
2826
- - Use pending when dependencies prevent work and fill blockedBy.
2827
-
2828
- ## Task Lifecycle Overview
2829
-
2830
- Tasks flow through these states:
2831
-
2832
- - pending --(start work)--> in_progress --(complete)--> done
2833
- - pending --(cancel)--> deleted
2834
- - in_progress --(cancel)--> deleted
2835
-
2836
- **State Definitions:**
2837
- - **pending**: Not started, may be blocked by dependencies
2838
- - **in_progress**: Currently being worked on (only ONE task at a time)
2839
- - **done**: Successfully completed (triggers unlock propagation)
2840
- - **deleted**: Cancelled or no longer needed
2841
-
2842
- **Ready vs Blocked:**
2843
- - A task is **ready** when: status=pending AND blockedBy=[] (all dependencies satisfied)
2844
- - A task is **blocked** when: blockedBy contains task IDs that are not yet done/deleted
2845
-
2846
- ## Dependency Relationships
2847
-
2848
- Tasks can have dependencies on other tasks:
2849
-
2850
- - **blockedBy**: List of task IDs that must complete before this task can start
2851
- - **blocks**: List of task IDs that are waiting for this task (auto-synced, do not set manually)
2852
-
2853
- **How it works:**
2854
-
2855
- Task A (blockedBy: [])
2856
- Task B (blockedBy: ["A"]) \u2190 B waits for A
2857
- Task C (blockedBy: ["A"]) \u2190 C waits for A
2858
-
2859
- When Task A completes:
2860
- - blockedBy is cleared from B and C
2861
- - Both B and C become ready (can run in parallel)
2862
-
2863
- **Important:** When you set blockedBy via TaskUpdate, the reverse blocks relationship is automatically synchronized. Never manually set blocks.
2864
-
2865
- ## Handling Natural Language Requests
2866
-
2867
- When users provide vague or high-level requests (e.g., "build a user auth module"), you must:
2868
- 1. **Decompose** the request into logical subtasks based on software engineering best practices
2869
- 2. **Identify dependencies** between subtasks (what must complete before what can start)
2870
- 3. **Create tasks first** with blockedBy=[] for all tasks
2871
- 4. **Add dependencies second** using TaskUpdate.addBlockedBy
2872
-
2873
- Common decomposition patterns:
2874
- - **Database/API pattern**: Schema design \u2192 Table implementation \u2192 API endpoints \u2192 Tests
2875
- - **Frontend pattern**: Design \u2192 Components \u2192 Integration \u2192 Testing
2876
- - **Full-stack pattern**: Backend \u2192 API \u2192 Frontend \u2192 Integration tests
2877
-
2878
- ## Examples of When to Use TaskCreate
2879
-
2880
- ### Category A: No Explicit Dependencies or Order (Most Common)
2881
-
2882
- User provides a high-level request without specifying how to break it down or what should be done first.
2883
-
2884
- <example>
2885
- User: Build a user authentication module
2886
-
2887
- Assistant: This is a complex feature that requires multiple components. Let me plan the implementation steps.
2888
-
2889
- *Creates tasks (Phase 1):*
2890
- TaskCreate(subject="Design auth database schema", activeForm="Designing auth database schema")
2891
- TaskCreate(subject="Implement users table", activeForm="Implementing users table")
2892
- TaskCreate(subject="Implement sessions table", activeForm="Implementing sessions table")
2893
- TaskCreate(subject="Build auth API endpoints", activeForm="Building auth API endpoints")
2894
- TaskCreate(subject="Write unit tests", activeForm="Writing unit tests")
2895
- TaskCreate(subject="Write integration tests", activeForm="Writing integration tests")
2896
-
2897
- *Adds dependencies (Phase 2):*
2898
- TaskUpdate(taskId="2", addBlockedBy=["1"])
2899
- TaskUpdate(taskId="3", addBlockedBy=["1"])
2900
- TaskUpdate(taskId="4", addBlockedBy=["2", "3"])
2901
- TaskUpdate(taskId="5", addBlockedBy=["4"])
2902
- TaskUpdate(taskId="6", addBlockedBy=["4"])
2903
-
2904
- Auth module planned: 6 tasks with dependency graph established. Starting with database design.
2905
-
2906
- <reasoning>
2907
- The assistant used TaskCreate because:
2908
- 1. User provided a high-level feature request without task breakdown
2909
- 2. Authentication module requires multiple components (database, tables, API, tests)
2910
- 3. The assistant inferred dependencies based on software engineering practices: schema before tables, tables before API, API before tests
2911
- 4. Two-phase pattern: create all tasks first, then link dependencies
2912
- </reasoning>
2913
- </example>
2914
-
2915
- <example>
2916
- User: I want to add a dark mode toggle to the application settings. Make sure you run the tests when you're done!
2917
-
2918
- Assistant: I'll help add a dark mode toggle. Let me plan the implementation steps.
2919
-
2920
- *Creates tasks (Phase 1):*
2921
- TaskCreate(subject="Create dark mode toggle component", activeForm="Creating dark mode toggle component")
2922
- TaskCreate(subject="Add dark mode state management", activeForm="Adding dark mode state management")
2923
- TaskCreate(subject="Implement dark theme styles", activeForm="Implementing dark theme styles")
2924
- TaskCreate(subject="Update components for theme switching", activeForm="Updating components for theme switching")
2925
- TaskCreate(subject="Run tests and fix failures", activeForm="Running tests and fixing failures")
2926
-
2927
- *Adds dependencies (Phase 2):*
2928
- TaskUpdate(taskId="3", addBlockedBy=["2"])
2929
- TaskUpdate(taskId="4", addBlockedBy=["1", "3"])
2930
- TaskUpdate(taskId="5", addBlockedBy=["4"])
2931
-
2932
- Dark mode toggle planned: 5 tasks. Starting with the toggle component.
2933
-
2934
- <reasoning>
2935
- The assistant used TaskCreate because:
2936
- 1. Adding dark mode is a multi-step feature requiring UI, state management, and styling changes
2937
- 2. User explicitly requested tests be run afterward - this becomes the final task
2938
- 3. Dependencies inferred: styles depend on state management, components depend on toggle and styles, tests run last
2939
- </reasoning>
2940
- </example>
2941
-
2942
- <example>
2943
- User: Help optimize my React app performance, rendering is slow
2944
-
2945
- Assistant: Let me first examine the codebase to identify performance bottlenecks.
2946
-
2947
- *Examines component structure, render patterns, state management*
2948
-
2949
- After analyzing the codebase, I've identified several performance issues. Let me plan the optimizations.
2950
-
2951
- *Creates tasks (Phase 1):*
2952
- TaskCreate(subject="Add memoization to ProductList calculations", activeForm="Adding memoization to ProductList")
2953
- TaskCreate(subject="Implement virtualization for Dashboard lists", activeForm="Implementing list virtualization")
2954
- TaskCreate(subject="Optimize image loading in Gallery", activeForm="Optimizing image loading")
2955
- TaskCreate(subject="Fix state update loops in ShoppingCart", activeForm="Fixing state update loops")
2956
-
2957
- *Dependencies: These can be done at the same time, no specific order needed*
2958
-
2959
- React performance optimization planned: 4 tasks. Starting with ProductList memoization.
2960
-
2961
- <reasoning>
2962
- The assistant used TaskCreate because:
2963
- 1. First examined the codebase to identify specific performance issues
2964
- 2. Multiple independent optimizations were identified - each becomes a task
2965
- 3. These tasks have NO dependencies - they can run in parallel
2966
- 4. Todo list helps track which optimizations have been addressed
2967
- </reasoning>
2968
- </example>
2969
-
2970
- ### Category B: Explicit Order Specified by User
2971
-
2972
- User clearly indicates what should be done first, second, etc. using words like "first... then... finally..." or numbered lists.
2973
-
2974
- <example>
2975
- User: First design the database, then create the users table and sessions table, then write the API, and finally test
2976
-
2977
- Assistant: User has explicitly specified the order. Let me plan tasks according to this sequence.
2978
-
2979
- *Creates tasks (Phase 1):*
2980
- TaskCreate(subject="Design database schema", activeForm="Designing database schema")
2981
- TaskCreate(subject="Implement users table", activeForm="Implementing users table")
2982
- TaskCreate(subject="Implement sessions table", activeForm="Implementing sessions table")
2983
- TaskCreate(subject="Build API endpoints", activeForm="Building API endpoints")
2984
- TaskCreate(subject="Write tests", activeForm="Writing tests")
2985
-
2986
- *Adds dependencies based on user's explicit order (Phase 2):*
2987
- TaskUpdate(taskId="2", addBlockedBy=["1"]) // users table waits for database
2988
- TaskUpdate(taskId="3", addBlockedBy=["1"]) // sessions table waits for database
2989
- TaskUpdate(taskId="4", addBlockedBy=["2", "3"]) // API waits for both tables
2990
- TaskUpdate(taskId="5", addBlockedBy=["4"]) // tests wait for API
2991
-
2992
- Tasks planned in your specified order: database \u2192 users/sessions tables \u2192 API \u2192 tests
2993
-
2994
- <reasoning>
2995
- The assistant used TaskCreate because:
2996
- 1. User explicitly specified the order: database first, then tables, then API, finally tests
2997
- 2. This order translates directly into dependency relationships
2998
- 3. Users table and sessions table can run in parallel after database (both depend only on task 1)
2999
- 4. Two-phase pattern: create all tasks, then add dependencies matching user's specified order
3000
- </reasoning>
3001
- </example>
3002
-
3003
- <example>
3004
- User: I need to add a character limit to the bio field. First, add the validation logic to the backend, then update the frontend form.
3005
-
3006
- Assistant: I'll plan this with the order you specified.
3007
-
3008
- *Creates tasks (Phase 1):*
3009
- TaskCreate(subject="Add backend validation for bio character limit", activeForm="Adding backend validation")
3010
- TaskCreate(subject="Update frontend form to enforce character limit", activeForm="Updating frontend form")
3011
-
3012
- *Adds dependencies (Phase 2):*
3013
- TaskUpdate(taskId="2", addBlockedBy=["1"]) // frontend waits for backend
3014
-
3015
- Bio character limit planned: backend validation first, then frontend update.
3016
-
3017
- <reasoning>
3018
- The assistant used TaskCreate because:
3019
- 1. User used "First... then..." which explicitly defines the order
3020
- 2. Backend must be done before frontend - this is a dependency relationship
3021
- 3. Two tasks with one dependency
3022
- </reasoning>
3023
- </example>
3024
-
3025
- <example>
3026
- User: I need to implement user registration, product catalog, shopping cart, and checkout flow
3027
-
3028
- Assistant: These are core e-commerce features. Let me plan tasks for each module.
3029
-
3030
- *Creates tasks for each feature (Phase 1):*
3031
- TaskCreate(subject="Implement user registration", activeForm="Implementing user registration")
3032
- TaskCreate(subject="Implement product catalog", activeForm="Implementing product catalog")
3033
- TaskCreate(subject="Implement shopping cart", activeForm="Implementing shopping cart")
3034
- TaskCreate(subject="Implement checkout flow", activeForm="Implementing checkout flow")
3035
-
3036
- *Adds dependencies (Phase 2):*
3037
- TaskUpdate(taskId="3", addBlockedBy=["2"]) // cart depends on product catalog
3038
- TaskUpdate(taskId="4", addBlockedBy=["1", "3"]) // checkout depends on registration and cart
3039
-
3040
- E-commerce features planned: user registration and product catalog can start together, cart waits for catalog, checkout waits for registration and cart.
3041
-
3042
- <reasoning>
3043
- The assistant used TaskCreate because:
3044
- 1. User provided multiple complex features in a comma-separated list
3045
- 2. These features have natural dependencies: cart needs products, checkout needs user and cart
3046
- 3. Registration and catalog can run in parallel (no dependency between them)
3047
- 4. Todo list helps organize these large features into manageable tasks
3048
- </reasoning>
3049
- </example>
3050
-
3051
- ### Category C: Simple or Single Task
3052
-
3053
- User's request is straightforward and doesn't require complex planning.
3054
-
3055
- <example>
3056
- User: Just fix the typo in the documentation
3057
-
3058
- Assistant: This is a straightforward fix. I'll create a single task to track it.
3059
-
3060
- *Creates one task:*
3061
- TaskCreate(subject="Fix typo in documentation", activeForm="Fixing documentation typo")
3062
-
3063
- <reasoning>
3064
- The assistant used TaskCreate because:
3065
- 1. This is a single task that can be tracked
3066
- 2. No complex breakdown needed
3067
- 3. Still useful to track for completeness and progress visibility
3068
- </reasoning>
3069
- </example>
3070
-
3071
- <example>
3072
- User: Add a comment to the calculateTotal function explaining what it does
3073
-
3074
- Assistant: This is a simple single-step task.
3075
-
3076
- *Creates one task:*
3077
- TaskCreate(subject="Add comment to calculateTotal function", activeForm="Adding function comment")
3078
-
3079
- <reasoning>
3080
- The assistant used TaskCreate because:
3081
- 1. Single straightforward task in one location
3082
- 2. Creating a task helps track completion
3083
- </reasoning>
3084
- </example>
3085
-
3086
- ### Category D: Adding Forgotten Tasks
3087
-
3088
- User realizes something was missed after the initial plan.
3089
-
3090
- <example>
3091
- User: I forgot, we also need logging for the auth module
3092
-
3093
- Assistant: Let me add the logging task and link it appropriately.
3094
-
3095
- TaskCreate(subject="Add auth logging", activeForm="Adding auth logging")
3096
-
3097
- *The logging task depends on API endpoints being ready:*
3098
- TaskUpdate(taskId="7", addBlockedBy=["4"]) // logging depends on API
3099
-
3100
- Auth logging added. It will be done after the API endpoints are complete.
3101
-
3102
- <reasoning>
3103
- The assistant used TaskCreate because:
3104
- 1. User added a new requirement after initial planning
3105
- 2. New task was created first with TaskCreate
3106
- 3. Then linked to existing tasks via TaskUpdate.addBlockedBy
3107
- 4. Logging logically depends on API endpoints existence
3108
- </reasoning>
3109
- </example>
3110
- `.trim();
3111
-
3112
- // src/tools/interaction/TaskCreateTool/TaskCreateTool.tsx
3113
- var inputSchema8 = z9.strictObject({
3114
- listId: z9.string().min(1).optional(),
3115
- subject: z9.string().min(1),
3116
- description: z9.string().min(1),
3117
- activeForm: z9.string().min(1),
3118
- metadata: metadataSchema.optional()
3119
- });
3120
- var TaskCreateTool = {
3121
- name: "TaskCreate",
3122
- cachedDescription: DESCRIPTION5,
3123
- async description() {
3124
- return DESCRIPTION5;
3125
- },
3126
- async prompt() {
3127
- return PROMPT6;
3128
- },
3129
- inputSchema: inputSchema8,
3130
- userFacingName() {
3131
- return "";
3132
- },
3133
- async isEnabled() {
3134
- return true;
3135
- },
3136
- isReadOnly() {
3137
- return false;
3138
- },
3139
- isConcurrencySafe() {
3140
- return false;
3141
- },
3142
- needsPermissions() {
3143
- return false;
3144
- },
3145
- renderResultForAssistant(output) {
3146
- return JSON.stringify(output, null, 2);
3147
- },
3148
- renderToolResultMessage(output) {
3149
- return null;
3150
- },
3151
- renderToolUseMessage() {
3152
- return null;
3153
- },
3154
- async *call(input) {
3155
- try {
3156
- const subject = input.subject;
3157
- const task = createTask({
3158
- subject,
3159
- description: input.description,
3160
- activeForm: input.activeForm,
3161
- metadata: input.metadata
3162
- }, { listId: input.listId });
3163
- const { listId } = getTaskListPaths(input.listId);
3164
- const timestamp = Date.now();
3165
- const payload = {
3166
- listId,
3167
- taskId: task.id,
3168
- task,
3169
- timestamp
3170
- };
3171
- emitReminderEvent("task.created", payload);
3172
- emitReminderEvent("task.created.v1", payload);
3173
- yield {
3174
- type: "result",
3175
- data: {
3176
- listId,
3177
- item: toTaskDetail(task)
3178
- }
3179
- };
3180
- } catch (error) {
3181
- const message = error instanceof Error ? error.message : "Task create failed";
3182
- yield {
3183
- type: "result",
3184
- data: createTaskToolError({
3185
- type: "unknown",
3186
- message
3187
- })
3188
- };
3189
- }
3190
- }
3191
- };
3192
-
3193
- // src/tools/interaction/TaskGetTool/TaskGetTool.tsx
3194
- import { z as z10 } from "zod";
3195
-
3196
- // src/tools/interaction/TaskGetTool/prompt.ts
3197
- var DESCRIPTION6 = "Review planned task details";
3198
- var PROMPT7 = `Use this tool to fetch a single task by id.
3199
-
3200
- ## When to Use This Tool
3201
- Use this tool proactively in these scenarios:
3202
-
3203
- 1. You need details for one task to confirm its fields, status, or dependencies.
3204
- 2. You need the latest task details before updating a task.
3205
-
3206
- ## When NOT to Use This Tool
3207
-
3208
- Skip using this tool when:
3209
- 1. You need multiple tasks or readiness info (use TaskList).
3210
- 2. You need to modify a task (use TaskUpdate) or create one (use TaskCreate).
3211
-
3212
- NOTE that TaskGet returns a single task only; use TaskList for summaries or readiness.
3213
-
3214
- ## Input Notes
3215
- - taskId is required.
3216
- - listId is optional; omit to use the default list.
3217
- - Do not mention tool names or tool usage in user-facing messages.
3218
-
3219
- ## Examples of When to Use TaskGet
3220
-
3221
- <example>
3222
- User: Can you check the status of task t-17 before updating it?
3223
- Assistant: I'll fetch the task details first.
3224
- *Fetches task t-17*
3225
- </example>
3226
- `.trim();
3227
-
3228
- // src/tools/interaction/TaskGetTool/TaskGetTool.tsx
3229
- var inputSchema9 = z10.strictObject({
3230
- listId: z10.string().min(1).optional(),
3231
- taskId: z10.string().min(1)
3232
- });
3233
- var TaskGetTool = {
3234
- name: "TaskGet",
3235
- cachedDescription: DESCRIPTION6,
3236
- async description() {
3237
- return DESCRIPTION6;
3238
- },
3239
- async prompt() {
3240
- return PROMPT7;
3241
- },
3242
- inputSchema: inputSchema9,
3243
- userFacingName() {
3244
- return "";
3245
- },
3246
- async isEnabled() {
3247
- return true;
3248
- },
3249
- isReadOnly() {
3250
- return true;
3251
- },
3252
- isConcurrencySafe() {
3253
- return true;
3254
- },
3255
- needsPermissions() {
3256
- return false;
3257
- },
3258
- renderResultForAssistant(output) {
3259
- return JSON.stringify(output, null, 2);
3260
- },
3261
- renderToolResultMessage() {
3262
- return null;
3263
- },
3264
- renderToolUseMessage() {
3265
- return null;
3266
- },
3267
- async *call(input) {
3268
- const { listId } = getTaskListPaths(input.listId);
3269
- const task = getTask(input.taskId, { listId: input.listId });
3270
- if (!task) {
3271
- yield {
3272
- type: "result",
3273
- data: createTaskToolError({
3274
- type: "not_found",
3275
- code: "TASK_NOT_FOUND",
3276
- message: `Task ${input.taskId} not found`,
3277
- details: { taskId: input.taskId, listId }
3278
- })
3279
- };
3280
- return;
3281
- }
3282
- yield {
3283
- type: "result",
3284
- data: {
3285
- listId,
3286
- item: toTaskGetDetail(task)
3287
- }
3288
- };
3289
- }
3290
- };
3291
-
3292
- // src/tools/interaction/TaskListTool/TaskListTool.tsx
3293
- import { z as z11 } from "zod";
3294
-
3295
- // src/tools/interaction/TaskListTool/prompt.ts
3296
- var DESCRIPTION7 = "Review planned tasks, readiness, and blockers";
3297
- var PROMPT8 = `Use this tool to list tasks and understand readiness. It returns ready and blocked groupings to help prioritize work. TaskList only returns id/subject/status/blockedBy for summaries; use TaskGet for details.
3298
-
3299
- ## When to Use This Tool
3300
- Use this tool proactively in these scenarios:
3301
-
3302
- 1. You need a list of tasks for a listId (or default list).
3303
- 2. You need ready/blocked calculations to prioritize work.
3304
- 3. You need to filter by status.
3305
- 4. You want a quick summary of what can be started now.
3306
-
3307
- ## When NOT to Use This Tool
3308
-
3309
- Skip using this tool when:
3310
- 1. You need a single task by id (use TaskGet).
3311
- 2. You need to create or update tasks (use TaskCreate or TaskUpdate).
3312
-
3313
- NOTE that TaskList is for summaries and readiness; use TaskGet for details.
3314
-
3315
- ## Input Notes
3316
- - listId is optional; omit to use the default list.
3317
- - status filters the returned items; omit to exclude deleted tasks by default.
3318
- - blockedBy may be empty early in planning and will appear as dependencies are added.
3319
- - Do not mention tool names or tool usage in user-facing messages.
3320
-
3321
- ## Examples of When to Use TaskList
3322
-
3323
- <example>
3324
- User: What should we work on next?
3325
- Assistant: I'll list tasks and check readiness.
3326
- *Lists tasks with ready/blocked breakdown*
3327
- </example>
3328
-
3329
- <example>
3330
- User: Show only in-progress items.
3331
- Assistant: I'll list tasks filtered by status.
3332
- *Lists tasks with status filter*
3333
- </example>
3334
-
3335
- <example>
3336
- User: We just added dependencies, can we see what's blocked now?
3337
- Assistant: I'll list tasks to show the updated ready/blocked breakdown.
3338
- *Lists tasks with ready/blocked breakdown*
3339
- </example>
3340
- `.trim();
3341
-
3342
- // src/tools/interaction/TaskListTool/TaskListTool.tsx
3343
- var inputSchema10 = z11.strictObject({
3344
- listId: z11.string().min(1).optional(),
3345
- status: z11.enum(["pending", "in_progress", "completed", "deleted"]).optional()
3346
- });
3347
- var filterByStatus = (tasks, statuses) => {
3348
- if (!statuses || statuses.length === 0) {
3349
- return tasks.filter((item) => item.status !== "deleted");
3350
- }
3351
- const allowed = new Set(statuses);
3352
- return tasks.filter((item) => allowed.has(item.status));
3353
- };
3354
- var mapStatusFilterInput = (input) => {
3355
- if (!input) return void 0;
3356
- if (input === "completed") return ["done"];
3357
- if (input === "deleted") return ["deleted"];
3358
- if (input === "pending") return ["pending"];
3359
- return ["in_progress"];
3360
- };
3361
- var getActiveBlockers = (task, tasksById) => {
3362
- const blockers = task.blockedBy ?? [];
3363
- const active = [];
3364
- for (const blockerId of blockers) {
3365
- const blocker = tasksById.get(blockerId);
3366
- if (!blocker) {
3367
- active.push(blockerId);
3368
- continue;
3369
- }
3370
- if (blocker.status !== "done" && blocker.status !== "deleted") {
3371
- active.push(blockerId);
3372
- }
3373
- }
3374
- return active;
3375
- };
3376
- var TaskListTool = {
3377
- name: "TaskList",
3378
- cachedDescription: DESCRIPTION7,
3379
- async description() {
3380
- return DESCRIPTION7;
3381
- },
3382
- async prompt() {
3383
- return PROMPT8;
3384
- },
3385
- inputSchema: inputSchema10,
3386
- userFacingName() {
3387
- return "";
3388
- },
3389
- async isEnabled() {
3390
- return true;
3391
- },
3392
- isReadOnly() {
3393
- return true;
3394
- },
3395
- isConcurrencySafe() {
3396
- return true;
3397
- },
3398
- needsPermissions() {
3399
- return false;
3400
- },
3401
- renderResultForAssistant(output) {
3402
- return JSON.stringify(output, null, 2);
3403
- },
3404
- renderToolResultMessage() {
3405
- return null;
3406
- },
3407
- renderToolUseMessage() {
3408
- return null;
3409
- },
3410
- async *call(input) {
3411
- try {
3412
- const { listId } = getTaskListPaths(input.listId);
3413
- const tasks = listTasks({ listId: input.listId });
3414
- const filtered = filterByStatus(tasks, mapStatusFilterInput(input.status));
3415
- const tasksById = new Map(tasks.map((task) => [task.id, task]));
3416
- const items = filtered.map((task) => {
3417
- const base = toTaskListItem(task);
3418
- const activeBlockers = getActiveBlockers(task, tasksById);
3419
- return {
3420
- ...base,
3421
- ready: task.status === "pending" && activeBlockers.length === 0,
3422
- activeBlockers
3423
- };
3424
- });
3425
- yield {
3426
- type: "result",
3427
- data: {
3428
- listId,
3429
- items
3430
- }
3431
- };
3432
- } catch (error) {
3433
- const message = error instanceof Error ? error.message : "Task list failed";
3434
- yield {
3435
- type: "result",
3436
- data: createTaskToolError({
3437
- type: "unknown",
3438
- message
3439
- })
3440
- };
3441
- }
3442
- }
3443
- };
3444
-
3445
- // src/tools/interaction/TaskUpdateTool/TaskUpdateTool.tsx
3446
- import { z as z12 } from "zod";
3447
-
3448
- // src/tools/interaction/TaskUpdateTool/prompt.ts
3449
- var DESCRIPTION8 = "Manage task progress, status, and dependencies";
3450
- var PROMPT9 = `Use this tool to update an existing task. It is the primary way to track progress and adjust task details.
3451
-
3452
- ## When to Use This Tool
3453
- Use this tool proactively in these scenarios:
3454
-
3455
- 1. You need to change task fields (subject/description/status).
3456
- 2. You need to adjust dependencies (blockedBy).
3457
- 3. You need to move a task across states (pending \u2192 in_progress \u2192 completed).
3458
- 4. You are moving a task across states (pending \u2192 in_progress \u2192 completed).
3459
-
3460
- ## When NOT to Use This Tool
3461
-
3462
- Skip using this tool when:
3463
- 1. You need to create a new task (use TaskCreate).
3464
- 2. You only need to read tasks (use TaskGet or TaskList).
3465
-
3466
- NOTE that TaskUpdate should be used when you need to persist task changes.
3467
-
3468
- ## Input Notes
3469
- - taskId is required.
3470
- - listId is optional; omit to use the default list.
3471
- - Dependencies are validated for cycles; invalid cycles will be rejected.
3472
- - Use TaskUpdate to add blockedBy after tasks are created.
3473
- - Use addBlocks when the current task should block other tasks; the reverse blockedBy relation is synchronized.
3474
- - You can set metadata.planFinalized="true" to trigger a full dependency validation pass.
3475
- - activeForm is optional in updates; when omitted, existing value is preserved.
3476
- - blockedBy is always stored as an array (empty when not used).
3477
- - description and blockedBy are kept separate in JSON; any joining is only for terminal rendering.
3478
- - Task ordering relies on task id; do not use priority fields.
3479
- - Do not mention tool names or tool usage in user-facing messages.
3480
-
3481
- ## The Two Usage Patterns
3482
-
3483
- ### Pattern 1: Dependency Linking (After TaskCreate)
3484
- Use TaskUpdate to establish dependencies AFTER all tasks are created:
3485
- - Create tasks with TaskCreate (blockedBy=[])
3486
- - Link dependencies with TaskUpdate.addBlockedBy or TaskUpdate.addBlocks
3487
- - This ensures reference validity and clean graph construction
3488
-
3489
- ### Pattern 2: Status Progression
3490
- Use TaskUpdate to move tasks through the execution lifecycle:
3491
- - pending \u2192 in_progress: When starting work on a task
3492
- - in_progress \u2192 completed: When task is done (triggers unlock propagation)
3493
- - Any \u2192 deleted: When task is obsolete or cancelled
3494
-
3495
- ## Update Patterns
3496
- - Simple status-only update: update one task status directly (pending \u2192 in_progress \u2192 completed) when no dependency change is needed.
3497
- - Complex dependency and unlock update: update blockedBy/addBlocks and status together, then consume unlockResult.readyNow immediately.
3498
- - Cancel or retire path: use deleted when a task is intentionally retired and should no longer appear in default lists.
3499
-
3500
- ## Field Guidance
3501
- - activeForm: short verb phrase describing the in-progress action.
3502
- - description: prefer a one-line overview plus bullet points for multi-step tasks; for very simple tasks a short sentence is fine.
3503
- - collaboration metadata keys:
3504
- - metadata.owner for task owner identity
3505
- - metadata.worktree for bound worktree name
3506
- - metadata.lane for collaboration lane
3507
- - metadata.agent for agent role or identifier
3508
- - metadata.created_at for creation timestamp
3509
- - metadata.updated_at for update timestamp
3510
-
3511
- ## Status Notes
3512
- - Use in_progress for the task you are actively working on.
3513
- - Use pending when dependencies prevent progress and set blockedBy.
3514
- - Use completed only when all dependencies are satisfied.
3515
- - When status becomes completed, consume unlockResult.readyNow immediately.
3516
- - If readyNow is returned, continue with those tasks without calling TaskList.
3517
-
3518
- ## Task Lifecycle Overview
3519
-
3520
- Tasks flow through these states:
3521
-
3522
- - pending --(start work)--> in_progress --(complete)--> done
3523
- - pending --(cancel)--> deleted
3524
- - in_progress --(cancel)--> deleted
3525
-
3526
- **State Definitions:**
3527
- - **pending**: Not started, may be blocked by dependencies
3528
- - **in_progress**: Currently being worked on (only ONE task at a time)
3529
- - **done**: Successfully completed (triggers unlock propagation)
3530
- - **deleted**: Cancelled or no longer needed
3531
-
3532
- **Ready vs Blocked:**
3533
- - A task is **ready** when: status=pending AND blockedBy=[] (all dependencies satisfied)
3534
- - A task is **blocked** when: blockedBy contains task IDs that are not yet done/deleted
3535
-
3536
- ## Dependency Relationships
3537
-
3538
- Tasks can have dependencies on other tasks:
3539
-
3540
- - **blockedBy**: List of task IDs that must complete before this task can start
3541
- - **blocks**: List of task IDs that are waiting for this task (auto-synced, do not set manually)
3542
-
3543
- **How it works:**
3544
-
3545
- Task A (blockedBy: [])
3546
- Task B (blockedBy: ["A"]) \u2190 B waits for A
3547
- Task C (blockedBy: ["A"]) \u2190 C waits for A
3548
-
3549
- When Task A completes:
3550
- - blockedBy is cleared from B and C
3551
- - Both B and C become ready (can run in parallel)
3552
-
3553
- **Important:** When you set blockedBy via TaskUpdate, the reverse blocks relationship is automatically synchronized. Never manually set blocks.
3554
-
3555
- ## Understanding Unlock Propagation
3556
-
3557
- When a task is marked as completed:
3558
- 1. It is removed from the blockedBy list of all tasks it was blocking
3559
- 2. Any tasks that become unblocked (blockedBy becomes empty) appear in unlockResult.readyNow
3560
- 3. You should immediately continue with those readyNow tasks without calling TaskList
3561
-
3562
- This enables efficient "chain execution" without redundant status checks.
3563
-
3564
- ## Examples of When to Use TaskUpdate
3565
-
3566
- ### Category A: Status Progression (Moving Tasks Forward)
3567
-
3568
- <example>
3569
- User: The database design is done, let's start on the users table
3570
-
3571
- Assistant: Database design is complete. Let me update the task status and continue to the next step.
3572
-
3573
- TaskUpdate(taskId="1", status="completed")
3574
- *Result shows readyNow: ["2", "3"]*
3575
-
3576
- Both the users table and sessions table are now ready. I'll start with the users table.
3577
-
3578
- TaskUpdate(taskId="2", status="in_progress")
3579
-
3580
- <reasoning>
3581
- The assistant used TaskUpdate because:
3582
- 1. User indicated the database design is done
3583
- 2. Completing task 1 triggers unlock - tasks 2 and 3 become ready
3584
- 3. User said to start the users table
3585
- 4. The unlockResult.readyNow tells us what tasks are now unblocked
3586
- </reasoning>
3587
- </example>
3588
-
3589
- <example>
3590
- User: I've finished the API endpoints. What should I work on next?
3591
-
3592
- Assistant: Let me mark the API task complete and see what's unblocked.
3593
-
3594
- TaskUpdate(taskId="4", status="completed")
3595
- *Result shows readyNow: ["5", "6"]*
3596
-
3597
- The unit tests and integration tests are now ready. Would you like to start with unit tests?
3598
-
3599
- <reasoning>
3600
- The assistant used TaskUpdate because:
3601
- 1. User said they finished the API endpoints
3602
- 2. Completing this task triggers unlock propagation
3603
- 3. The readyNow field shows which tasks can now start
3604
- 4. This enables efficient "chain execution" without calling TaskList
3605
- </reasoning>
3606
- </example>
3607
-
3608
- <example>
3609
- User: Start writing the tests
3610
-
3611
- Assistant: I'll mark the test task as in progress.
3612
-
3613
- TaskUpdate(taskId="5", status="in_progress")
3614
-
3615
- <reasoning>
3616
- The assistant used TaskUpdate because:
3617
- 1. User wants to start working on tests
3618
- 2. Simple status progression: pending \u2192 in_progress
3619
- </reasoning>
3620
- </example>
3621
-
3622
- ### Category B: Task Completion and Chain Execution
3623
-
3624
- <example>
3625
- User: The database design is complete
3626
-
3627
- Assistant: Database design task is complete. Let me check what tasks are now ready.
3628
-
3629
- TaskUpdate(taskId="1", status="completed")
3630
- *Result shows unlockResult.readyNow: ["2", "3"]*
3631
-
3632
- The users table and sessions table can now be done together. I'll continue with the users table.
3633
-
3634
- TaskUpdate(taskId="2", status="in_progress")
3635
- *Continuing with unblocked task without calling TaskList*
3636
-
3637
- <reasoning>
3638
- The assistant used TaskUpdate because:
3639
- 1. User reported completion of database design
3640
- 2. unlockResult.readyNow shows tasks 2 and 3 are now unblocked and can run in parallel
3641
- 3. Assistant immediately continues with task 2 - efficient chain execution
3642
- 4. No need to call TaskList to check status
3643
- </reasoning>
3644
- </example>
3645
-
3646
- ### Category C: Task Obsolescence or Cancellation
3647
-
3648
- <example>
3649
- User: This feature is no longer needed, let's skip it for now
3650
-
3651
- Assistant: I'll mark this task as deleted.
3652
-
3653
- TaskUpdate(taskId="3", status="deleted")
3654
-
3655
- <reasoning>
3656
- The assistant used TaskUpdate because:
3657
- 1. User said the feature is no longer needed
3658
- 2. "deleted" status removes it from active task flow
3659
- 3. This may unblock other tasks that were waiting on this one
3660
- </reasoning>
3661
- </example>
3662
-
3663
- <example>
3664
- User: Actually, we don't need the session table anymore. We're using JWT instead.
3665
-
3666
- Assistant: I'll mark the session table task as deleted since we're switching to JWT.
3667
-
3668
- TaskUpdate(taskId="3", status="deleted")
3669
-
3670
- *If task 3 was blocking other tasks, they may now be unblocked*
3671
-
3672
- <reasoning>
3673
- The assistant used TaskUpdate because:
3674
- 1. User indicated a change in approach (JWT instead of sessions)
3675
- 2. The session table task is now obsolete
3676
- 3. Marking as deleted removes it from active work
3677
- </reasoning>
3678
- </example>
3679
-
3680
- ### Category D: Adding New Requirements During Execution
3681
-
3682
- <example>
3683
- User: The auth module also needs a password reset feature
3684
-
3685
- Assistant: Adding password reset functionality. Let me create a new task and add it to the existing plan.
3686
-
3687
- TaskCreate(subject="Implement password reset", activeForm="Implementing password reset")
3688
-
3689
- *This new task may need to be linked to existing tasks:*
3690
- TaskUpdate(taskId="7", addBlockedBy=["4"]) // password reset depends on API endpoints
3691
-
3692
- Password reset feature added to task list, depends on API endpoints completion.
3693
-
3694
- <reasoning>
3695
- The assistant used TaskUpdate because:
3696
- 1. User added a new requirement during execution
3697
- 2. New task was created first
3698
- 3. Then dependency was added using TaskUpdate
3699
- 4. Password reset logically depends on the API endpoints being ready
3700
- </reasoning>
3701
- </example>
3702
-
3703
- <example>
3704
- User: We need to add rate limiting to the auth endpoints too
3705
-
3706
- Assistant: I'll add a rate limiting task and link it appropriately.
3707
-
3708
- TaskCreate(subject="Add rate limiting to auth endpoints", activeForm="Adding rate limiting")
3709
- TaskUpdate(taskId="8", addBlockedBy=["4"]) // rate limiting depends on API
3710
-
3711
- Rate limiting task added. It will start after the API endpoints are complete.
3712
-
3713
- <reasoning>
3714
- The assistant used TaskUpdate because:
3715
- 1. User added a new requirement
3716
- 2. Create task first, then add dependency
3717
- 3. Rate limiting applies to API endpoints, so it depends on task 4
3718
- </reasoning>
3719
- </example>
3720
-
3721
- ### Category E: Dependency Adjustment
3722
-
3723
- <example>
3724
- User: The users table doesn't need to wait for database design, we can do it at the same time
3725
-
3726
- Assistant: I'll remove this dependency.
3727
-
3728
- TaskUpdate(taskId="2", blockedBy=[])
3729
-
3730
- The users table can now start immediately, no need to wait for database design.
3731
-
3732
- <reasoning>
3733
- The assistant used TaskUpdate because:
3734
- 1. User said the users table no longer needs to wait for database design
3735
- 2. Setting blockedBy=[] removes all blockers
3736
- 3. Task 2 is now ready to start immediately
3737
- </reasoning>
3738
- </example>
3739
-
3740
- <example>
3741
- User: Tests should wait until all development tasks are complete
3742
-
3743
- Assistant: I'll set the test task to wait for all development tasks.
3744
-
3745
- TaskUpdate(taskId="5", addBlockedBy=["1", "2", "3", "4"])
3746
-
3747
- The test task will now wait for tasks 1-4 to all complete before starting.
3748
-
3749
- <reasoning>
3750
- The assistant used TaskUpdate because:
3751
- 1. User specified that tests should wait for all development tasks
3752
- 2. addBlockedBy adds new dependencies without removing existing ones
3753
- 3. Task 5 will only be ready after all four development tasks complete
3754
- </reasoning>
3755
- </example>
3756
-
3757
- <example>
3758
- User: The API doesn't need to wait for the sessions table anymore, just the users table
3759
-
3760
- Assistant: I'll adjust the API's dependencies.
3761
-
3762
- TaskUpdate(taskId="4", blockedBy=["2"]) // only keep users table dependency
3763
-
3764
- The API now only needs to wait for the users table to complete.
3765
-
3766
- <reasoning>
3767
- The assistant used TaskUpdate because:
3768
- 1. User specified a change in dependencies
3769
- 2. Sessions table is no longer a blocker for API
3770
- 3. Setting the full blockedBy list replaces the previous one
3771
- </reasoning>
3772
- </example>
3773
-
3774
- ### Category F: Changing Execution Order
3775
-
3776
- User wants to change the planned execution order.
3777
-
3778
- <example>
3779
- User: Wait, let's do the users table first, then the database
3780
-
3781
- Assistant: I understand you want to change the execution order. Let me adjust the dependencies.
3782
-
3783
- *Current state: users table (task 2) depends on database (task 1)*
3784
-
3785
- TaskUpdate(taskId="2", blockedBy=[]) // users table no longer waits for database
3786
- TaskUpdate(taskId="1", addBlockedBy=["2"]) // database now waits for users table
3787
-
3788
- Order adjusted: users table first, then database. The users table can start immediately.
3789
-
3790
- <reasoning>
3791
- The assistant used TaskUpdate because:
3792
- 1. User requested a reversal of execution order
3793
- 2. First, remove the existing dependency from task 2
3794
- 3. Then, create a reverse dependency where database waits for users table
3795
- 4. Task 2 is now unblocked and ready to start immediately
3796
- </reasoning>
3797
- </example>
3798
-
3799
- <example>
3800
- User: Actually, let's do the users table and sessions table first, before the database design
3801
-
3802
- Assistant: I'll reverse the dependency chain so both tables come before database design.
3803
-
3804
- TaskUpdate(taskId="2", blockedBy=[]) // users table no longer blocked
3805
- TaskUpdate(taskId="3", blockedBy=[]) // sessions table no longer blocked
3806
- TaskUpdate(taskId="1", addBlockedBy=["2", "3"]) // database waits for both tables
3807
-
3808
- Both tables can now start immediately. Database design will wait for both to complete.
3809
-
3810
- <reasoning>
3811
- The assistant used TaskUpdate because:
3812
- 1. User wanted to completely reverse the execution order
3813
- 2. Multiple dependencies needed to be cleared first
3814
- 3. Then reverse dependencies were added via addBlockedBy
3815
- 4. Tasks 2 and 3 are now unblocked and can run in parallel
3816
- </reasoning>
3817
- </example>
3818
- `.trim();
3819
-
3820
- // src/tools/interaction/TaskUpdateTool/TaskUpdateTool.tsx
3821
- var normalizeDeps = (items) => [...items ?? []].filter(Boolean).sort();
3822
- var mergeDeps = (current, additions) => {
3823
- const merged = [];
3824
- const seen = /* @__PURE__ */ new Set();
3825
- for (const id of current ?? []) {
3826
- if (!id || seen.has(id)) continue;
3827
- seen.add(id);
3828
- merged.push(id);
3829
- }
3830
- for (const id of additions ?? []) {
3831
- if (!id || seen.has(id)) continue;
3832
- seen.add(id);
3833
- merged.push(id);
3834
- }
3835
- return merged;
3836
- };
3837
- var mergeMetadata = (existing, updates) => {
3838
- if (!updates) return existing;
3839
- const next = { ...existing ?? {} };
3840
- for (const [key, value] of Object.entries(updates)) {
3841
- if (value === null) {
3842
- delete next[key];
3843
- } else {
3844
- next[key] = value;
3845
- }
3846
- }
3847
- return Object.keys(next).length > 0 ? next : void 0;
3848
- };
3849
- var mapStatusInput = (status) => {
3850
- if (!status) return void 0;
3851
- if (status === "completed") return "done";
3852
- if (status === "deleted") return "deleted";
3853
- return status;
3854
- };
3855
- var depsChanged = (prev, next) => {
3856
- const prevBlockedBy = normalizeDeps(prev.blockedBy);
3857
- const nextBlockedBy = normalizeDeps(next.blockedBy);
3858
- if (prevBlockedBy.length !== nextBlockedBy.length) return true;
3859
- for (let i = 0; i < prevBlockedBy.length; i += 1) {
3860
- if (prevBlockedBy[i] !== nextBlockedBy[i]) return true;
3861
- }
3862
- const prevBlocks = normalizeDeps(prev.blocks);
3863
- const nextBlocks = normalizeDeps(next.blocks);
3864
- if (prevBlocks.length !== nextBlocks.length) return true;
3865
- for (let i = 0; i < prevBlocks.length; i += 1) {
3866
- if (prevBlocks[i] !== nextBlocks[i]) return true;
3867
- }
3868
- return false;
3869
- };
3870
- var getDepsSnapshot = (task) => ({
3871
- blocks: task.blocks ?? [],
3872
- blockedBy: task.blockedBy ?? []
3873
- });
3874
- var getMissingBlockedBy = (blockedBy, ids) => (blockedBy ?? []).filter((id) => !ids.has(id));
3875
- var isPlanFinalized = (metadata) => {
3876
- const value = metadata?.planFinalized;
3877
- return value === true || value === "true";
3878
- };
3879
- var compareTaskId = (left, right) => {
3880
- const leftValue = Number(left.id);
3881
- const rightValue = Number(right.id);
3882
- const leftIsNumber = !Number.isNaN(leftValue);
3883
- const rightIsNumber = !Number.isNaN(rightValue);
3884
- if (leftIsNumber && rightIsNumber) return leftValue - rightValue;
3885
- if (leftIsNumber) return -1;
3886
- if (rightIsNumber) return 1;
3887
- return left.id.localeCompare(right.id);
3888
- };
3889
- var inputSchema11 = z12.strictObject({
3890
- listId: z12.string().min(1).optional(),
3891
- taskId: z12.string().min(1),
3892
- subject: z12.string().optional(),
3893
- description: z12.string().optional(),
3894
- activeForm: z12.string().optional(),
3895
- status: z12.enum(["pending", "in_progress", "completed", "deleted"]).optional(),
3896
- metadata: metadataSchema.optional(),
3897
- addBlockedBy: z12.array(z12.string()).optional(),
3898
- addBlocks: z12.array(z12.string()).optional()
3899
- });
3900
- var TaskUpdateTool = {
3901
- name: "TaskUpdate",
3902
- cachedDescription: DESCRIPTION8,
3903
- async description() {
3904
- return DESCRIPTION8;
3905
- },
3906
- async prompt() {
3907
- return PROMPT9;
3908
- },
3909
- inputSchema: inputSchema11,
3910
- userFacingName() {
3911
- return "";
3912
- },
3913
- async isEnabled() {
3914
- return true;
3915
- },
3916
- isReadOnly() {
3917
- return false;
3918
- },
3919
- isConcurrencySafe() {
3920
- return false;
3921
- },
3922
- needsPermissions() {
3923
- return false;
3924
- },
3925
- renderResultForAssistant(output) {
3926
- return JSON.stringify(output, null, 2);
3927
- },
3928
- renderToolResultMessage(output) {
3929
- return null;
3930
- },
3931
- renderToolUseMessage() {
3932
- return null;
3933
- },
3934
- async *call(input) {
3935
- const { listId } = getTaskListPaths(input.listId);
3936
- const existing = getTask(input.taskId, { listId: input.listId });
3937
- if (!existing) {
3938
- yield {
3939
- type: "result",
3940
- data: createTaskToolError({
3941
- type: "not_found",
3942
- code: "TASK_NOT_FOUND",
3943
- message: `Task ${input.taskId} not found`,
3944
- details: { taskId: input.taskId, listId }
3945
- })
3946
- };
3947
- return;
3948
- }
3949
- const tasks = listTasks({ listId: input.listId });
3950
- const subject = input.subject ?? existing.subject;
3951
- const mergedBlockedBy = mergeDeps(existing.blockedBy, input.addBlockedBy);
3952
- const mappedStatus = mapStatusInput(input.status);
3953
- const mergedMetadata = mergeMetadata(existing.metadata, input.metadata);
3954
- const metadataUpdate = input.metadata;
3955
- const addBlocksTargets = new Set(
3956
- (input.addBlocks ?? []).filter((id) => id && id !== existing.id)
3957
- );
3958
- const updatedTask = {
3959
- ...existing,
3960
- subject,
3961
- description: input.description ?? existing.description,
3962
- activeForm: input.activeForm ?? existing.activeForm,
3963
- status: mappedStatus ?? existing.status,
3964
- metadata: mergedMetadata ?? existing.metadata,
3965
- blockedBy: mergedBlockedBy
3966
- };
3967
- const candidateTasks = tasks.map((task) => {
3968
- if (task.id === existing.id) {
3969
- return updatedTask;
3970
- }
3971
- if (addBlocksTargets.has(task.id)) {
3972
- return {
3973
- ...task,
3974
- blockedBy: mergeDeps(task.blockedBy, [existing.id])
3975
- };
3976
- }
3977
- return task;
3978
- });
3979
- const candidateIds = new Set(candidateTasks.map((task) => task.id));
3980
- const missingBlockedBy = getMissingBlockedBy(
3981
- updatedTask.blockedBy,
3982
- candidateIds
3983
- );
3984
- if (missingBlockedBy.length > 0) {
3985
- yield {
3986
- type: "result",
3987
- data: createTaskToolError({
3988
- type: "validation",
3989
- code: "TASK_BLOCKEDBY_NOT_FOUND",
3990
- message: "BlockedBy tasks must exist in the same list",
3991
- details: {
3992
- taskId: updatedTask.id,
3993
- listId,
3994
- missingBlockedBy
3995
- }
3996
- })
3997
- };
3998
- return;
3999
- }
4000
- const missingBlocks = (input.addBlocks ?? []).filter(
4001
- (id) => id && !candidateIds.has(id)
4002
- );
4003
- if (missingBlocks.length > 0) {
4004
- yield {
4005
- type: "result",
4006
- data: createTaskToolError({
4007
- type: "validation",
4008
- code: "TASK_BLOCKEDBY_NOT_FOUND",
4009
- message: "Blocked tasks must exist in the same list",
4010
- details: {
4011
- taskId: updatedTask.id,
4012
- listId,
4013
- missingBlocks
4014
- }
4015
- })
4016
- };
4017
- return;
4018
- }
4019
- const cycles = findTaskCycles(buildTaskGraph(candidateTasks));
4020
- if (cycles.length > 0) {
4021
- yield {
4022
- type: "result",
4023
- data: createTaskToolError({
4024
- type: "validation",
4025
- code: "TASK_CYCLE_DETECTED",
4026
- message: "Task dependency cycle detected",
4027
- details: { cycles }
4028
- })
4029
- };
4030
- return;
4031
- }
4032
- const nextMetadata = mergedMetadata ?? existing.metadata;
4033
- if (isPlanFinalized(nextMetadata) && !isPlanFinalized(existing.metadata)) {
4034
- const activeTasks = candidateTasks.filter((task) => task.status !== "deleted");
4035
- const ordered = [...activeTasks].sort(compareTaskId);
4036
- const firstTaskId = ordered[0]?.id;
4037
- for (const task of activeTasks) {
4038
- const missing = getMissingBlockedBy(task.blockedBy, candidateIds);
4039
- if (missing.length > 0) {
4040
- yield {
4041
- type: "result",
4042
- data: createTaskToolError({
4043
- type: "validation",
4044
- code: "TASK_BLOCKEDBY_NOT_FOUND",
4045
- message: "BlockedBy tasks must exist in the same list",
4046
- details: { taskId: task.id, listId, missingBlockedBy: missing }
4047
- })
4048
- };
4049
- return;
4050
- }
4051
- if (task.id !== firstTaskId && (task.blockedBy?.length ?? 0) === 0) {
4052
- yield {
4053
- type: "result",
4054
- data: createTaskToolError({
4055
- type: "validation",
4056
- code: "TASK_DEPENDENCY_FINALIZE_REQUIRED",
4057
- message: "Task dependency chain required before planFinalized",
4058
- details: { taskId: task.id, listId }
4059
- })
4060
- };
4061
- return;
4062
- }
4063
- }
4064
- }
4065
- try {
4066
- for (const task of tasks) {
4067
- if (!addBlocksTargets.has(task.id)) continue;
4068
- const merged = mergeDeps(task.blockedBy, [existing.id]);
4069
- const current = normalizeDeps(task.blockedBy);
4070
- const next = normalizeDeps(merged);
4071
- let same = current.length === next.length;
4072
- if (same) {
4073
- for (let i = 0; i < current.length; i += 1) {
4074
- if (current[i] !== next[i]) {
4075
- same = false;
4076
- break;
4077
- }
4078
- }
4079
- }
4080
- if (same) continue;
4081
- updateTask(task.id, { blockedBy: merged }, { listId: input.listId });
4082
- }
4083
- const result = updateTask(
4084
- input.taskId,
4085
- {
4086
- subject: input.subject,
4087
- description: input.description,
4088
- activeForm: input.activeForm,
4089
- status: mappedStatus,
4090
- metadata: metadataUpdate,
4091
- blockedBy: mergedBlockedBy
4092
- },
4093
- { listId: input.listId }
4094
- );
4095
- const didDepsChange = depsChanged(existing, result.task);
4096
- const updatedTimestamp = Date.now();
4097
- const updatedPayload = {
4098
- listId,
4099
- taskId: result.task.id,
4100
- task: result.task,
4101
- timestamp: updatedTimestamp
4102
- };
4103
- emitReminderEvent("task.updated", updatedPayload);
4104
- emitReminderEvent("task.updated.v1", updatedPayload);
4105
- if (existing.status !== result.task.status) {
4106
- const statusTimestamp = Date.now();
4107
- const statusPayload = {
4108
- listId,
4109
- taskId: result.task.id,
4110
- previousStatus: existing.status,
4111
- status: result.task.status,
4112
- timestamp: statusTimestamp
4113
- };
4114
- emitReminderEvent("task.status_changed", statusPayload);
4115
- emitReminderEvent("task.status_changed.v1", statusPayload);
4116
- }
4117
- if (didDepsChange) {
4118
- emitReminderEvent("task.deps_changed", {
4119
- listId,
4120
- taskId: result.task.id,
4121
- previousDeps: getDepsSnapshot(existing),
4122
- deps: getDepsSnapshot(result.task),
4123
- timestamp: Date.now()
4124
- });
4125
- }
4126
- if (result.unlockResult) {
4127
- emitReminderEvent("task.unblocked.v1", {
4128
- listId,
4129
- taskId: result.task.id,
4130
- unblocked: result.unlockResult.unblocked.map((task) => task.id),
4131
- readyNow: result.unlockResult.readyNow.map((task) => task.id),
4132
- touchedTaskIds: result.unlockResult.unlockSummary.touchedTaskIds,
4133
- timestamp: Date.now()
4134
- });
4135
- }
4136
- yield {
4137
- type: "result",
4138
- data: {
4139
- listId,
4140
- item: toTaskDetail(result.task),
4141
- depsChanged: didDepsChange,
4142
- unlockResult: result.unlockResult ? {
4143
- unblocked: result.unlockResult.unblocked.map(toTaskDetail),
4144
- partiallyUnblocked: result.unlockResult.partiallyUnblocked.map(
4145
- (item) => ({
4146
- task: toTaskDetail(item.task),
4147
- remainingBlockers: item.remainingBlockers
4148
- })
4149
- ),
4150
- readyNow: result.unlockResult.readyNow.map(toTaskDetail),
4151
- unlockSummary: result.unlockResult.unlockSummary
4152
- } : void 0
4153
- }
4154
- };
4155
- } catch (error) {
4156
- const message = error instanceof Error ? error.message : "Task update failed";
4157
- yield {
4158
- type: "result",
4159
- data: createTaskToolError({
4160
- type: "unknown",
4161
- message
4162
- })
4163
- };
4164
- }
4165
- }
4166
- };
4167
-
4168
- // src/tools/memory/MemoryReadTool/MemoryReadTool.tsx
4169
- import { existsSync as existsSync5, lstatSync, mkdirSync as mkdirSync2, readdirSync, readFileSync as readFileSync4 } from "fs";
4170
- import { Box as Box8, Text as Text8 } from "ink";
4171
- import { join as join2 } from "path";
4172
- import * as React8 from "react";
4173
- import { z as z13 } from "zod";
4174
-
4175
- // src/tools/memory/MemoryReadTool/prompt.ts
4176
- var DESCRIPTION9 = "Read agent memory files from the local filesystem.";
4177
- var PROMPT10 = `Reads agent memory files from the local filesystem.
4178
-
4179
- Usage:
4180
- - The file_path parameter must be a path relative to the agent's memory directory
4181
- - By default, it reads a specific memory file if file_path is provided
4182
- - If no file_path is specified, it returns an index of all memory files for the current agent
4183
- - Memory files are stored under ~/.pyb/memory/projects/{projectId}/memory
4184
- - Use this tool to access previously stored agent memories and task execution details
4185
-
4186
- Examples:
4187
- - To read a specific memory file: {"file_path": "task-123-summary.md"}
4188
- - To get memory index: {} (empty input)
4189
-
4190
- Security:
4191
- - File paths are validated to ensure they are within the agent's memory directory
4192
- - Only files within the agent's designated memory space can be accessed
4193
- `;
4194
-
4195
- // src/tools/memory/MemoryReadTool/MemoryReadTool.tsx
4196
- var inputSchema12 = z13.strictObject({
4197
- file_path: z13.string().optional().describe("Optional path to a specific memory file to read")
4198
- });
4199
- var MemoryReadTool = {
4200
- name: "MemoryRead",
4201
- async description() {
4202
- return DESCRIPTION9;
4203
- },
4204
- async prompt() {
4205
- return PROMPT10;
4206
- },
4207
- inputSchema: inputSchema12,
4208
- userFacingName() {
4209
- return "Read Memory";
4210
- },
4211
- async isEnabled() {
4212
- return resolveAutoMemoryFlags().enabled;
4213
- },
4214
- isReadOnly() {
4215
- return true;
4216
- },
4217
- isConcurrencySafe() {
4218
- return true;
4219
- },
4220
- needsPermissions() {
4221
- return false;
4222
- },
4223
- renderResultForAssistant({ content }) {
4224
- return content;
4225
- },
4226
- renderToolUseMessage(input) {
4227
- return Object.entries(input).map(([key, value]) => `${key}: ${JSON.stringify(value)}`).join(", ");
4228
- },
4229
- renderToolUseRejectedMessage() {
4230
- return /* @__PURE__ */ React8.createElement(FallbackToolUseRejectedMessage, null);
4231
- },
4232
- renderToolResultMessage(output) {
4233
- return /* @__PURE__ */ React8.createElement(Box8, { justifyContent: "space-between", overflowX: "hidden", width: "100%" }, /* @__PURE__ */ React8.createElement(Box8, { flexDirection: "row" }, /* @__PURE__ */ React8.createElement(Text8, null, "\xA0\xA0\uFFFD?\xA0"), /* @__PURE__ */ React8.createElement(Text8, null, output.content)));
4234
- },
4235
- async validateInput({ file_path }, context) {
4236
- const cwd = getCwd();
4237
- const projectId = await resolveProjectId(cwd);
4238
- const projectMemoryDir = getProjectMemoryDir(MEMORY_DIR, projectId);
4239
- const agentId = resolveAgentId(context?.agentId);
4240
- const agentMemoryDir = getAgentMemoryDir(projectMemoryDir, {
4241
- agentId,
4242
- agentType: context?.agentType
4243
- });
4244
- if (file_path) {
4245
- const resolved = resolveMemoryPath(agentMemoryDir, file_path);
4246
- if (!resolved.ok) {
4247
- return { result: false, message: "Invalid memory file path" };
4248
- }
4249
- if (!existsSync5(resolved.fullPath)) {
4250
- return { result: false, message: "Memory file does not exist" };
4251
- }
4252
- }
4253
- return { result: true };
4254
- },
4255
- async *call({ file_path }, context) {
4256
- const cwd = getCwd();
4257
- const projectId = await resolveProjectId(cwd);
4258
- const projectMemoryDir = getProjectMemoryDir(MEMORY_DIR, projectId);
4259
- const agentId = resolveAgentId(context?.agentId);
4260
- const agentMemoryDir = getAgentMemoryDir(projectMemoryDir, {
4261
- agentId,
4262
- agentType: context?.agentType
4263
- });
4264
- mkdirSync2(agentMemoryDir, { recursive: true });
4265
- if (file_path) {
4266
- const resolved = resolveMemoryPath(agentMemoryDir, file_path);
4267
- if (!resolved.ok) {
4268
- throw new Error("Invalid memory file path");
4269
- }
4270
- const fullPath = resolved.fullPath;
4271
- if (!existsSync5(fullPath)) {
4272
- throw new Error("Memory file does not exist");
4273
- }
4274
- const content2 = readFileSync4(fullPath, "utf-8");
4275
- yield {
4276
- type: "result",
4277
- data: {
4278
- content: content2
4279
- },
4280
- resultForAssistant: this.renderResultForAssistant({ content: content2 })
4281
- };
4282
- return;
4283
- }
4284
- const files = readdirSync(agentMemoryDir, { recursive: true }).map((f) => join2(agentMemoryDir, f.toString())).filter((f) => !lstatSync(f).isDirectory()).map((f) => `- ${f}`).join("\n");
4285
- const indexPath = join2(agentMemoryDir, "index.md");
4286
- const index = existsSync5(indexPath) ? readFileSync4(indexPath, "utf-8") : "";
4287
- const quotes = "'''";
4288
- const content = `Here are the contents of the agent memory file, \`${indexPath}\`:
4289
- ${quotes}
4290
- ${index}
4291
- ${quotes}
4292
-
4293
- Files in the agent memory directory:
4294
- ${files}`;
4295
- yield {
4296
- type: "result",
4297
- data: { content },
4298
- resultForAssistant: this.renderResultForAssistant({ content })
4299
- };
4300
- }
4301
- };
4302
-
4303
- // src/tools/memory/MemoryWriteTool/MemoryWriteTool.tsx
4304
- import { Box as Box9, Text as Text9 } from "ink";
4305
- import * as React9 from "react";
4306
- import { z as z14 } from "zod";
4307
-
4308
- // src/services/memory/write.ts
4309
- import {
4310
- existsSync as existsSync6,
4311
- mkdirSync as mkdirSync3,
4312
- readFileSync as readFileSync5,
4313
- renameSync,
4314
- rmSync as rmSync2,
4315
- writeFileSync as writeFileSync2
4316
- } from "fs";
4317
- import { dirname as dirname2 } from "path";
4318
- var defaultFsDeps = {
4319
- mkdirSync: mkdirSync3,
4320
- existsSync: existsSync6,
4321
- readFileSync: readFileSync5,
4322
- writeFileSync: writeFileSync2,
4323
- renameSync,
4324
- rmSync: rmSync2
4325
- };
4326
- function mergeContent(existing, incoming, mode) {
4327
- if (mode === "replace") return incoming;
4328
- if (!existing) return incoming;
4329
- if (!incoming) return existing;
4330
- return `${existing}
4331
- ${incoming}`;
4332
- }
4333
- function writeMemoryFile(input, deps) {
4334
- const fs = { ...defaultFsDeps, ...deps || {} };
4335
- const mode = input.mode || "replace";
4336
- const filePath = input.filePath;
4337
- const tempPath = `${filePath}.tmp`;
4338
- fs.mkdirSync(dirname2(filePath), { recursive: true });
4339
- const existing = fs.existsSync(filePath) ? fs.readFileSync(filePath, "utf8") : "";
4340
- const nextContent = mergeContent(existing, input.content, mode);
4341
- fs.writeFileSync(tempPath, nextContent, "utf8");
4342
- try {
4343
- fs.renameSync(tempPath, filePath);
4344
- } catch {
4345
- fs.writeFileSync(filePath, nextContent, "utf8");
4346
- if (fs.existsSync(tempPath)) {
4347
- fs.rmSync(tempPath, { force: true });
4348
- }
4349
- }
4350
- }
4351
-
4352
- // src/tools/memory/MemoryWriteTool/prompt.ts
4353
- var DESCRIPTION10 = "Write agent memory files to the local filesystem.";
4354
- var PROMPT11 = `Write agent memory files to the local filesystem.
4355
-
4356
- Usage:
4357
- - The file_path parameter is required and specifies the memory file to write
4358
- - The content parameter is required and contains the content to write
4359
- - The optional mode parameter supports append or replace (default replace)
4360
- - Memory files are stored under ~/.pyb/memory/projects/{projectId}/memory
4361
- - Use this tool to store agent memories and task execution details
4362
- - In replace mode existing file content is overwritten
4363
- - In append mode content is appended to the existing file
4364
-
4365
- Security:
4366
- - File paths are validated to ensure they are within the agent's memory directory
4367
- - Only files within the agent's designated memory space can be written
4368
- - Directory structure is automatically created if it doesn't exist
4369
-
4370
- Example:
4371
- {
4372
- "file_path": "task-123-summary.md",
4373
- "content": "# Task Summary
4374
-
4375
- This is the summary of the task execution...",
4376
- "mode": "append"
4377
- }
4378
- `;
4379
-
4380
- // src/tools/memory/MemoryWriteTool/MemoryWriteTool.tsx
4381
- var inputSchema13 = z14.strictObject({
4382
- file_path: z14.string().describe("Path to the memory file to write"),
4383
- content: z14.string().describe("Content to write to the file"),
4384
- mode: z14.enum(["append", "replace"]).optional().describe("Write mode: append adds to existing content, replace overwrites")
4385
- });
4386
- var MemoryWriteTool = {
4387
- name: "MemoryWrite",
4388
- async description() {
4389
- return DESCRIPTION10;
4390
- },
4391
- async prompt() {
4392
- return PROMPT11;
4393
- },
4394
- inputSchema: inputSchema13,
4395
- userFacingName() {
4396
- return "Write Memory";
4397
- },
4398
- async isEnabled() {
4399
- return resolveAutoMemoryFlags().enabled;
4400
- },
4401
- isReadOnly() {
4402
- return false;
4403
- },
4404
- isConcurrencySafe() {
4405
- return false;
4406
- },
4407
- needsPermissions() {
4408
- return false;
4409
- },
4410
- renderResultForAssistant(content) {
4411
- return content;
4412
- },
4413
- renderToolUseMessage(input) {
4414
- return Object.entries(input).map(([key, value]) => `${key}: ${JSON.stringify(value)}`).join(", ");
4415
- },
4416
- renderToolUseRejectedMessage() {
4417
- return /* @__PURE__ */ React9.createElement(FallbackToolUseRejectedMessage, null);
4418
- },
4419
- renderToolResultMessage() {
4420
- return /* @__PURE__ */ React9.createElement(Box9, { justifyContent: "space-between", overflowX: "hidden", width: "100%" }, /* @__PURE__ */ React9.createElement(Box9, { flexDirection: "row" }, /* @__PURE__ */ React9.createElement(Text9, null, " ", "\uFFFD?Updated memory")));
4421
- },
4422
- async validateInput({ file_path }, context) {
4423
- const cwd = getCwd();
4424
- const projectId = await resolveProjectId(cwd);
4425
- const projectMemoryDir = getProjectMemoryDir(MEMORY_DIR, projectId);
4426
- const agentId = resolveAgentId(context?.agentId);
4427
- const agentMemoryDir = getAgentMemoryDir(projectMemoryDir, {
4428
- agentId,
4429
- agentType: context?.agentType
4430
- });
4431
- const resolved = resolveMemoryPath(agentMemoryDir, file_path);
4432
- if (!resolved.ok) {
4433
- return { result: false, message: "Invalid memory file path" };
4434
- }
4435
- return { result: true };
4436
- },
4437
- async *call({ file_path, content, mode }, context) {
4438
- const cwd = getCwd();
4439
- const projectId = await resolveProjectId(cwd);
4440
- const projectMemoryDir = getProjectMemoryDir(MEMORY_DIR, projectId);
4441
- const agentId = resolveAgentId(context?.agentId);
4442
- const agentMemoryDir = getAgentMemoryDir(projectMemoryDir, {
4443
- agentId,
4444
- agentType: context?.agentType
4445
- });
4446
- const resolved = resolveMemoryPath(agentMemoryDir, file_path);
4447
- if (!resolved.ok) {
4448
- throw new Error("Invalid memory file path");
4449
- }
4450
- const fullPath = resolved.fullPath;
4451
- writeMemoryFile({ filePath: fullPath, content, mode });
4452
- recordFileEdit(fullPath, content);
4453
- yield {
4454
- type: "result",
4455
- data: "Saved",
4456
- resultForAssistant: "Saved"
4457
- };
4458
- }
4459
- };
4460
-
4461
- // src/tools/index.ts
4462
- var getAllTools = () => [
4463
- TaskTool,
4464
- AskExpertModelTool,
4465
- BashTool,
4466
- TaskOutputTool,
4467
- KillShellTool,
4468
- GlobTool,
4469
- GrepTool,
4470
- LspTool,
4471
- FileReadTool,
4472
- FileEditTool,
4473
- FileWriteTool,
4474
- DeleteTool,
4475
- NotebookEditTool,
4476
- TaskCreateTool,
4477
- TaskGetTool,
4478
- TaskUpdateTool,
4479
- TaskListTool,
4480
- WebFetchTool,
4481
- WebSearchTool,
4482
- AskUserQuestionTool,
4483
- SlashCommandTool,
4484
- SkillTool,
4485
- MemoryReadTool,
4486
- MemoryWriteTool,
4487
- ListMcpResourcesTool,
4488
- ReadMcpResourceTool,
4489
- MCPTool
4490
- ];
4491
- var getTools = memoize2(
4492
- async (_includeOptional) => {
4493
- const tools = [...getAllTools(), ...await getMCPTools()];
4494
- const isEnabled = await Promise.all(tools.map((tool) => tool.isEnabled()));
4495
- return tools.filter((_, i) => isEnabled[i]);
4496
- }
4497
- );
4498
- var getReadOnlyTools = memoize2(async () => {
4499
- const tools = getAllTools().filter((tool) => tool.isReadOnly());
4500
- const isEnabled = await Promise.all(tools.map((tool) => tool.isEnabled()));
4501
- return tools.filter((_, index) => isEnabled[index]);
4502
- });
4503
-
4504
- export {
4505
- toTaskListItem,
4506
- toTaskDetail,
4507
- getAllTools,
4508
- getTools,
4509
- getReadOnlyTools
4510
- };