@shareai-lab/kode 1.0.70 → 1.0.71
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +202 -76
- package/README.zh-CN.md +246 -0
- package/cli.js +62 -0
- package/package.json +45 -25
- package/scripts/postinstall.js +56 -0
- package/src/ProjectOnboarding.tsx +180 -0
- package/src/Tool.ts +53 -0
- package/src/commands/approvedTools.ts +53 -0
- package/src/commands/bug.tsx +20 -0
- package/src/commands/clear.ts +43 -0
- package/src/commands/compact.ts +120 -0
- package/src/commands/config.tsx +19 -0
- package/src/commands/cost.ts +18 -0
- package/src/commands/ctx_viz.ts +209 -0
- package/src/commands/doctor.ts +24 -0
- package/src/commands/help.tsx +19 -0
- package/src/commands/init.ts +37 -0
- package/src/commands/listen.ts +42 -0
- package/src/commands/login.tsx +51 -0
- package/src/commands/logout.tsx +40 -0
- package/src/commands/mcp.ts +41 -0
- package/src/commands/model.tsx +40 -0
- package/src/commands/modelstatus.tsx +20 -0
- package/src/commands/onboarding.tsx +34 -0
- package/src/commands/pr_comments.ts +59 -0
- package/src/commands/refreshCommands.ts +54 -0
- package/src/commands/release-notes.ts +34 -0
- package/src/commands/resume.tsx +30 -0
- package/src/commands/review.ts +49 -0
- package/src/commands/terminalSetup.ts +221 -0
- package/src/commands.ts +136 -0
- package/src/components/ApproveApiKey.tsx +93 -0
- package/src/components/AsciiLogo.tsx +13 -0
- package/src/components/AutoUpdater.tsx +148 -0
- package/src/components/Bug.tsx +367 -0
- package/src/components/Config.tsx +289 -0
- package/src/components/ConsoleOAuthFlow.tsx +326 -0
- package/src/components/Cost.tsx +23 -0
- package/src/components/CostThresholdDialog.tsx +46 -0
- package/src/components/CustomSelect/option-map.ts +42 -0
- package/src/components/CustomSelect/select-option.tsx +52 -0
- package/src/components/CustomSelect/select.tsx +143 -0
- package/src/components/CustomSelect/use-select-state.ts +414 -0
- package/src/components/CustomSelect/use-select.ts +35 -0
- package/src/components/FallbackToolUseRejectedMessage.tsx +15 -0
- package/src/components/FileEditToolUpdatedMessage.tsx +66 -0
- package/src/components/Help.tsx +215 -0
- package/src/components/HighlightedCode.tsx +33 -0
- package/src/components/InvalidConfigDialog.tsx +113 -0
- package/src/components/Link.tsx +32 -0
- package/src/components/LogSelector.tsx +86 -0
- package/src/components/Logo.tsx +145 -0
- package/src/components/MCPServerApprovalDialog.tsx +100 -0
- package/src/components/MCPServerDialogCopy.tsx +25 -0
- package/src/components/MCPServerMultiselectDialog.tsx +109 -0
- package/src/components/Message.tsx +219 -0
- package/src/components/MessageResponse.tsx +15 -0
- package/src/components/MessageSelector.tsx +211 -0
- package/src/components/ModeIndicator.tsx +88 -0
- package/src/components/ModelConfig.tsx +301 -0
- package/src/components/ModelListManager.tsx +223 -0
- package/src/components/ModelSelector.tsx +3208 -0
- package/src/components/ModelStatusDisplay.tsx +228 -0
- package/src/components/Onboarding.tsx +274 -0
- package/src/components/PressEnterToContinue.tsx +11 -0
- package/src/components/PromptInput.tsx +710 -0
- package/src/components/SentryErrorBoundary.ts +33 -0
- package/src/components/Spinner.tsx +129 -0
- package/src/components/StructuredDiff.tsx +184 -0
- package/src/components/TextInput.tsx +246 -0
- package/src/components/TokenWarning.tsx +31 -0
- package/src/components/ToolUseLoader.tsx +40 -0
- package/src/components/TrustDialog.tsx +106 -0
- package/src/components/binary-feedback/BinaryFeedback.tsx +63 -0
- package/src/components/binary-feedback/BinaryFeedbackOption.tsx +111 -0
- package/src/components/binary-feedback/BinaryFeedbackView.tsx +172 -0
- package/src/components/binary-feedback/utils.ts +220 -0
- package/src/components/messages/AssistantBashOutputMessage.tsx +22 -0
- package/src/components/messages/AssistantLocalCommandOutputMessage.tsx +45 -0
- package/src/components/messages/AssistantRedactedThinkingMessage.tsx +19 -0
- package/src/components/messages/AssistantTextMessage.tsx +144 -0
- package/src/components/messages/AssistantThinkingMessage.tsx +40 -0
- package/src/components/messages/AssistantToolUseMessage.tsx +123 -0
- package/src/components/messages/UserBashInputMessage.tsx +28 -0
- package/src/components/messages/UserCommandMessage.tsx +30 -0
- package/src/components/messages/UserKodingInputMessage.tsx +28 -0
- package/src/components/messages/UserPromptMessage.tsx +35 -0
- package/src/components/messages/UserTextMessage.tsx +39 -0
- package/src/components/messages/UserToolResultMessage/UserToolCanceledMessage.tsx +12 -0
- package/src/components/messages/UserToolResultMessage/UserToolErrorMessage.tsx +36 -0
- package/src/components/messages/UserToolResultMessage/UserToolRejectMessage.tsx +31 -0
- package/src/components/messages/UserToolResultMessage/UserToolResultMessage.tsx +57 -0
- package/src/components/messages/UserToolResultMessage/UserToolSuccessMessage.tsx +35 -0
- package/src/components/messages/UserToolResultMessage/utils.tsx +56 -0
- package/src/components/permissions/BashPermissionRequest/BashPermissionRequest.tsx +121 -0
- package/src/components/permissions/FallbackPermissionRequest.tsx +155 -0
- package/src/components/permissions/FileEditPermissionRequest/FileEditPermissionRequest.tsx +182 -0
- package/src/components/permissions/FileEditPermissionRequest/FileEditToolDiff.tsx +75 -0
- package/src/components/permissions/FileWritePermissionRequest/FileWritePermissionRequest.tsx +164 -0
- package/src/components/permissions/FileWritePermissionRequest/FileWriteToolDiff.tsx +81 -0
- package/src/components/permissions/FilesystemPermissionRequest/FilesystemPermissionRequest.tsx +242 -0
- package/src/components/permissions/PermissionRequest.tsx +103 -0
- package/src/components/permissions/PermissionRequestTitle.tsx +69 -0
- package/src/components/permissions/hooks.ts +44 -0
- package/src/components/permissions/toolUseOptions.ts +59 -0
- package/src/components/permissions/utils.ts +23 -0
- package/src/constants/betas.ts +5 -0
- package/src/constants/claude-asterisk-ascii-art.tsx +238 -0
- package/src/constants/figures.ts +4 -0
- package/src/constants/keys.ts +3 -0
- package/src/constants/macros.ts +6 -0
- package/src/constants/models.ts +935 -0
- package/src/constants/oauth.ts +18 -0
- package/src/constants/product.ts +17 -0
- package/src/constants/prompts.ts +177 -0
- package/src/constants/releaseNotes.ts +7 -0
- package/src/context/PermissionContext.tsx +149 -0
- package/src/context.ts +278 -0
- package/src/cost-tracker.ts +84 -0
- package/src/entrypoints/cli.tsx +1498 -0
- package/src/entrypoints/mcp.ts +176 -0
- package/src/history.ts +25 -0
- package/src/hooks/useApiKeyVerification.ts +59 -0
- package/src/hooks/useArrowKeyHistory.ts +55 -0
- package/src/hooks/useCanUseTool.ts +138 -0
- package/src/hooks/useCancelRequest.ts +39 -0
- package/src/hooks/useDoublePress.ts +42 -0
- package/src/hooks/useExitOnCtrlCD.ts +31 -0
- package/src/hooks/useInterval.ts +25 -0
- package/src/hooks/useLogMessages.ts +16 -0
- package/src/hooks/useLogStartupTime.ts +12 -0
- package/src/hooks/useNotifyAfterTimeout.ts +65 -0
- package/src/hooks/usePermissionRequestLogging.ts +44 -0
- package/src/hooks/useSlashCommandTypeahead.ts +137 -0
- package/src/hooks/useTerminalSize.ts +49 -0
- package/src/hooks/useTextInput.ts +315 -0
- package/src/messages.ts +37 -0
- package/src/permissions.ts +268 -0
- package/src/query.ts +704 -0
- package/src/screens/ConfigureNpmPrefix.tsx +197 -0
- package/src/screens/Doctor.tsx +219 -0
- package/src/screens/LogList.tsx +68 -0
- package/src/screens/REPL.tsx +792 -0
- package/src/screens/ResumeConversation.tsx +68 -0
- package/src/services/browserMocks.ts +66 -0
- package/src/services/claude.ts +1947 -0
- package/src/services/customCommands.ts +683 -0
- package/src/services/fileFreshness.ts +377 -0
- package/src/services/mcpClient.ts +564 -0
- package/src/services/mcpServerApproval.tsx +50 -0
- package/src/services/notifier.ts +40 -0
- package/src/services/oauth.ts +357 -0
- package/src/services/openai.ts +796 -0
- package/src/services/sentry.ts +3 -0
- package/src/services/statsig.ts +171 -0
- package/src/services/statsigStorage.ts +86 -0
- package/src/services/systemReminder.ts +406 -0
- package/src/services/vcr.ts +161 -0
- package/src/tools/ArchitectTool/ArchitectTool.tsx +122 -0
- package/src/tools/ArchitectTool/prompt.ts +15 -0
- package/src/tools/AskExpertModelTool/AskExpertModelTool.tsx +505 -0
- package/src/tools/BashTool/BashTool.tsx +270 -0
- package/src/tools/BashTool/BashToolResultMessage.tsx +38 -0
- package/src/tools/BashTool/OutputLine.tsx +48 -0
- package/src/tools/BashTool/prompt.ts +174 -0
- package/src/tools/BashTool/utils.ts +56 -0
- package/src/tools/FileEditTool/FileEditTool.tsx +316 -0
- package/src/tools/FileEditTool/prompt.ts +51 -0
- package/src/tools/FileEditTool/utils.ts +58 -0
- package/src/tools/FileReadTool/FileReadTool.tsx +371 -0
- package/src/tools/FileReadTool/prompt.ts +7 -0
- package/src/tools/FileWriteTool/FileWriteTool.tsx +297 -0
- package/src/tools/FileWriteTool/prompt.ts +10 -0
- package/src/tools/GlobTool/GlobTool.tsx +119 -0
- package/src/tools/GlobTool/prompt.ts +8 -0
- package/src/tools/GrepTool/GrepTool.tsx +147 -0
- package/src/tools/GrepTool/prompt.ts +11 -0
- package/src/tools/MCPTool/MCPTool.tsx +106 -0
- package/src/tools/MCPTool/prompt.ts +3 -0
- package/src/tools/MemoryReadTool/MemoryReadTool.tsx +127 -0
- package/src/tools/MemoryReadTool/prompt.ts +3 -0
- package/src/tools/MemoryWriteTool/MemoryWriteTool.tsx +89 -0
- package/src/tools/MemoryWriteTool/prompt.ts +3 -0
- package/src/tools/MultiEditTool/MultiEditTool.tsx +366 -0
- package/src/tools/MultiEditTool/prompt.ts +45 -0
- package/src/tools/NotebookEditTool/NotebookEditTool.tsx +298 -0
- package/src/tools/NotebookEditTool/prompt.ts +3 -0
- package/src/tools/NotebookReadTool/NotebookReadTool.tsx +266 -0
- package/src/tools/NotebookReadTool/prompt.ts +3 -0
- package/src/tools/StickerRequestTool/StickerRequestTool.tsx +93 -0
- package/src/tools/StickerRequestTool/prompt.ts +19 -0
- package/src/tools/TaskTool/TaskTool.tsx +382 -0
- package/src/tools/TaskTool/constants.ts +1 -0
- package/src/tools/TaskTool/prompt.ts +56 -0
- package/src/tools/ThinkTool/ThinkTool.tsx +56 -0
- package/src/tools/ThinkTool/prompt.ts +12 -0
- package/src/tools/TodoWriteTool/TodoWriteTool.tsx +289 -0
- package/src/tools/TodoWriteTool/prompt.ts +63 -0
- package/src/tools/lsTool/lsTool.tsx +269 -0
- package/src/tools/lsTool/prompt.ts +2 -0
- package/src/tools.ts +63 -0
- package/src/types/PermissionMode.ts +120 -0
- package/src/types/RequestContext.ts +72 -0
- package/src/utils/Cursor.ts +436 -0
- package/src/utils/PersistentShell.ts +373 -0
- package/src/utils/agentStorage.ts +97 -0
- package/src/utils/array.ts +3 -0
- package/src/utils/ask.tsx +98 -0
- package/src/utils/auth.ts +13 -0
- package/src/utils/autoCompactCore.ts +223 -0
- package/src/utils/autoUpdater.ts +318 -0
- package/src/utils/betas.ts +20 -0
- package/src/utils/browser.ts +14 -0
- package/src/utils/cleanup.ts +72 -0
- package/src/utils/commands.ts +261 -0
- package/src/utils/config.ts +771 -0
- package/src/utils/conversationRecovery.ts +54 -0
- package/src/utils/debugLogger.ts +1123 -0
- package/src/utils/diff.ts +42 -0
- package/src/utils/env.ts +57 -0
- package/src/utils/errors.ts +21 -0
- package/src/utils/exampleCommands.ts +108 -0
- package/src/utils/execFileNoThrow.ts +51 -0
- package/src/utils/expertChatStorage.ts +136 -0
- package/src/utils/file.ts +402 -0
- package/src/utils/fileRecoveryCore.ts +71 -0
- package/src/utils/format.tsx +44 -0
- package/src/utils/generators.ts +62 -0
- package/src/utils/git.ts +92 -0
- package/src/utils/globalLogger.ts +77 -0
- package/src/utils/http.ts +10 -0
- package/src/utils/imagePaste.ts +38 -0
- package/src/utils/json.ts +13 -0
- package/src/utils/log.ts +382 -0
- package/src/utils/markdown.ts +213 -0
- package/src/utils/messageContextManager.ts +289 -0
- package/src/utils/messages.tsx +938 -0
- package/src/utils/model.ts +836 -0
- package/src/utils/permissions/filesystem.ts +118 -0
- package/src/utils/ripgrep.ts +167 -0
- package/src/utils/sessionState.ts +49 -0
- package/src/utils/state.ts +25 -0
- package/src/utils/style.ts +29 -0
- package/src/utils/terminal.ts +49 -0
- package/src/utils/theme.ts +122 -0
- package/src/utils/thinking.ts +144 -0
- package/src/utils/todoStorage.ts +431 -0
- package/src/utils/tokens.ts +43 -0
- package/src/utils/toolExecutionController.ts +163 -0
- package/src/utils/unaryLogging.ts +26 -0
- package/src/utils/user.ts +37 -0
- package/src/utils/validate.ts +165 -0
- package/cli.mjs +0 -1803
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import { Box, Text } from 'ink'
|
|
2
|
+
import * as React from 'react'
|
|
3
|
+
import { z } from 'zod'
|
|
4
|
+
import { FallbackToolUseRejectedMessage } from '../../components/FallbackToolUseRejectedMessage'
|
|
5
|
+
import { TodoItem as TodoItemComponent } from '../../components/TodoItem'
|
|
6
|
+
import { Tool, ValidationResult } from '../../Tool'
|
|
7
|
+
import { setTodos, getTodos, TodoItem } from '../../utils/todoStorage'
|
|
8
|
+
import { emitReminderEvent } from '../../services/systemReminder'
|
|
9
|
+
import { startWatchingTodoFile } from '../../services/fileFreshness'
|
|
10
|
+
import { DESCRIPTION, PROMPT } from './prompt'
|
|
11
|
+
import { getTheme } from '../../utils/theme'
|
|
12
|
+
|
|
13
|
+
const TodoItemSchema = z.object({
|
|
14
|
+
content: z.string().min(1).describe('The task description or content'),
|
|
15
|
+
status: z
|
|
16
|
+
.enum(['pending', 'in_progress', 'completed'])
|
|
17
|
+
.describe('Current status of the task'),
|
|
18
|
+
priority: z
|
|
19
|
+
.enum(['high', 'medium', 'low'])
|
|
20
|
+
.describe('Priority level of the task'),
|
|
21
|
+
id: z.string().min(1).describe('Unique identifier for the task'),
|
|
22
|
+
})
|
|
23
|
+
|
|
24
|
+
const inputSchema = z.strictObject({
|
|
25
|
+
todos: z.array(TodoItemSchema).describe('The updated todo list'),
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
function validateTodos(todos: TodoItem[]): ValidationResult {
|
|
29
|
+
// Check for duplicate IDs
|
|
30
|
+
const ids = todos.map(todo => todo.id)
|
|
31
|
+
const uniqueIds = new Set(ids)
|
|
32
|
+
if (ids.length !== uniqueIds.size) {
|
|
33
|
+
return {
|
|
34
|
+
result: false,
|
|
35
|
+
errorCode: 1,
|
|
36
|
+
message: 'Duplicate todo IDs found',
|
|
37
|
+
meta: {
|
|
38
|
+
duplicateIds: ids.filter((id, index) => ids.indexOf(id) !== index),
|
|
39
|
+
},
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
// Check for multiple in_progress tasks
|
|
44
|
+
const inProgressTasks = todos.filter(todo => todo.status === 'in_progress')
|
|
45
|
+
if (inProgressTasks.length > 1) {
|
|
46
|
+
return {
|
|
47
|
+
result: false,
|
|
48
|
+
errorCode: 2,
|
|
49
|
+
message: 'Only one task can be in_progress at a time',
|
|
50
|
+
meta: { inProgressTaskIds: inProgressTasks.map(t => t.id) },
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
// Validate each todo
|
|
55
|
+
for (const todo of todos) {
|
|
56
|
+
if (!todo.content?.trim()) {
|
|
57
|
+
return {
|
|
58
|
+
result: false,
|
|
59
|
+
errorCode: 3,
|
|
60
|
+
message: `Todo with ID "${todo.id}" has empty content`,
|
|
61
|
+
meta: { todoId: todo.id },
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
if (!['pending', 'in_progress', 'completed'].includes(todo.status)) {
|
|
65
|
+
return {
|
|
66
|
+
result: false,
|
|
67
|
+
errorCode: 4,
|
|
68
|
+
message: `Invalid status "${todo.status}" for todo "${todo.id}"`,
|
|
69
|
+
meta: { todoId: todo.id, invalidStatus: todo.status },
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
if (!['high', 'medium', 'low'].includes(todo.priority)) {
|
|
73
|
+
return {
|
|
74
|
+
result: false,
|
|
75
|
+
errorCode: 5,
|
|
76
|
+
message: `Invalid priority "${todo.priority}" for todo "${todo.id}"`,
|
|
77
|
+
meta: { todoId: todo.id, invalidPriority: todo.priority },
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return { result: true }
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function generateTodoSummary(todos: TodoItem[]): string {
|
|
86
|
+
const stats = {
|
|
87
|
+
total: todos.length,
|
|
88
|
+
pending: todos.filter(t => t.status === 'pending').length,
|
|
89
|
+
inProgress: todos.filter(t => t.status === 'in_progress').length,
|
|
90
|
+
completed: todos.filter(t => t.status === 'completed').length,
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
// Enhanced summary with statistics
|
|
94
|
+
let summary = `Updated ${stats.total} todo(s)`
|
|
95
|
+
if (stats.total > 0) {
|
|
96
|
+
summary += ` (${stats.pending} pending, ${stats.inProgress} in progress, ${stats.completed} completed)`
|
|
97
|
+
}
|
|
98
|
+
summary += '. Continue tracking your progress with the todo list.'
|
|
99
|
+
|
|
100
|
+
return summary
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export const TodoWriteTool = {
|
|
104
|
+
name: 'TodoWrite',
|
|
105
|
+
async description() {
|
|
106
|
+
return DESCRIPTION
|
|
107
|
+
},
|
|
108
|
+
async prompt() {
|
|
109
|
+
return PROMPT
|
|
110
|
+
},
|
|
111
|
+
inputSchema,
|
|
112
|
+
userFacingName() {
|
|
113
|
+
return 'Write Todos'
|
|
114
|
+
},
|
|
115
|
+
async isEnabled() {
|
|
116
|
+
return true
|
|
117
|
+
},
|
|
118
|
+
isReadOnly() {
|
|
119
|
+
return false
|
|
120
|
+
},
|
|
121
|
+
isConcurrencySafe() {
|
|
122
|
+
return false // TodoWrite modifies state, not safe for concurrent execution
|
|
123
|
+
},
|
|
124
|
+
needsPermissions() {
|
|
125
|
+
return false
|
|
126
|
+
},
|
|
127
|
+
renderResultForAssistant(result) {
|
|
128
|
+
// Match official implementation - return static confirmation message
|
|
129
|
+
return 'Todos have been modified successfully. Ensure that you continue to use the todo list to track your progress. Please proceed with the current tasks if applicable'
|
|
130
|
+
},
|
|
131
|
+
renderToolUseMessage(input, { verbose }) {
|
|
132
|
+
// Return empty string to match reference implementation and avoid double rendering
|
|
133
|
+
// The tool result message will show the todo list
|
|
134
|
+
return ''
|
|
135
|
+
},
|
|
136
|
+
renderToolUseRejectedMessage() {
|
|
137
|
+
return <FallbackToolUseRejectedMessage />
|
|
138
|
+
},
|
|
139
|
+
renderToolResultMessage(output, { verbose }) {
|
|
140
|
+
const isError = typeof output === 'string' && output.startsWith('Error')
|
|
141
|
+
|
|
142
|
+
// If output contains todo data, render simple checkbox list
|
|
143
|
+
if (typeof output === 'object' && output && 'newTodos' in output) {
|
|
144
|
+
const { newTodos = [] } = output as any
|
|
145
|
+
|
|
146
|
+
// sort: [completed, in_progress, pending]
|
|
147
|
+
newTodos.sort((a, b) => {
|
|
148
|
+
const order = ['completed', 'in_progress', 'pending']
|
|
149
|
+
return (
|
|
150
|
+
order.indexOf(a.status) - order.indexOf(b.status) ||
|
|
151
|
+
a.content.localeCompare(b.content)
|
|
152
|
+
)
|
|
153
|
+
})
|
|
154
|
+
|
|
155
|
+
// Render each todo item with proper styling
|
|
156
|
+
return (
|
|
157
|
+
<Box justifyContent="space-between" overflowX="hidden" width="100%">
|
|
158
|
+
<Box flexDirection="row">
|
|
159
|
+
<Text> ⎿ </Text>
|
|
160
|
+
<Box flexDirection="column">
|
|
161
|
+
{newTodos.map((todo: TodoItem, index: number) => {
|
|
162
|
+
const status_icon_map = {
|
|
163
|
+
completed: '🟢',
|
|
164
|
+
in_progress: '🟢',
|
|
165
|
+
pending: '🟡',
|
|
166
|
+
}
|
|
167
|
+
const checkbox = status_icon_map[todo.status]
|
|
168
|
+
|
|
169
|
+
const status_color_map = {
|
|
170
|
+
completed: '#008000',
|
|
171
|
+
in_progress: '#008000',
|
|
172
|
+
pending: '#FFD700',
|
|
173
|
+
}
|
|
174
|
+
const text_color = status_color_map[todo.status]
|
|
175
|
+
|
|
176
|
+
return (
|
|
177
|
+
<Text
|
|
178
|
+
key={todo.id || index}
|
|
179
|
+
color={text_color}
|
|
180
|
+
bold={todo.status !== 'pending'}
|
|
181
|
+
strikethrough={todo.status === 'completed'}
|
|
182
|
+
>
|
|
183
|
+
{checkbox} {todo.content}
|
|
184
|
+
</Text>
|
|
185
|
+
)
|
|
186
|
+
})}
|
|
187
|
+
</Box>
|
|
188
|
+
</Box>
|
|
189
|
+
</Box>
|
|
190
|
+
)
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Fallback to simple text rendering for errors or string output
|
|
194
|
+
return (
|
|
195
|
+
<Box justifyContent="space-between" overflowX="hidden" width="100%">
|
|
196
|
+
<Box flexDirection="row">
|
|
197
|
+
<Text color={isError ? getTheme().error : getTheme().success}>
|
|
198
|
+
⎿
|
|
199
|
+
{typeof output === 'string' ? output : JSON.stringify(output)}
|
|
200
|
+
</Text>
|
|
201
|
+
</Box>
|
|
202
|
+
</Box>
|
|
203
|
+
)
|
|
204
|
+
},
|
|
205
|
+
async validateInput({ todos }: z.infer<typeof inputSchema>) {
|
|
206
|
+
// Type assertion to ensure todos match TodoItem[] interface
|
|
207
|
+
const todoItems = todos as TodoItem[]
|
|
208
|
+
const validation = validateTodos(todoItems)
|
|
209
|
+
if (!validation.result) {
|
|
210
|
+
return validation
|
|
211
|
+
}
|
|
212
|
+
return { result: true }
|
|
213
|
+
},
|
|
214
|
+
async *call({ todos }: z.infer<typeof inputSchema>, context) {
|
|
215
|
+
try {
|
|
216
|
+
// Get agent ID from context
|
|
217
|
+
const agentId = context?.agentId
|
|
218
|
+
|
|
219
|
+
// Start watching todo file for this agent if not already watching
|
|
220
|
+
if (agentId) {
|
|
221
|
+
startWatchingTodoFile(agentId)
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
// Store previous todos for comparison (agent-scoped)
|
|
225
|
+
const previousTodos = getTodos(agentId)
|
|
226
|
+
|
|
227
|
+
// Type assertion to ensure todos match TodoItem[] interface
|
|
228
|
+
const todoItems = todos as TodoItem[]
|
|
229
|
+
|
|
230
|
+
// Note: Validation already done in validateInput, no need for duplicate validation
|
|
231
|
+
// This eliminates the double validation issue
|
|
232
|
+
|
|
233
|
+
// Update the todos in storage (agent-scoped)
|
|
234
|
+
setTodos(todoItems, agentId)
|
|
235
|
+
|
|
236
|
+
// Emit todo change event for system reminders (optimized - only if todos actually changed)
|
|
237
|
+
const hasChanged =
|
|
238
|
+
JSON.stringify(previousTodos) !== JSON.stringify(todoItems)
|
|
239
|
+
if (hasChanged) {
|
|
240
|
+
emitReminderEvent('todo:changed', {
|
|
241
|
+
previousTodos,
|
|
242
|
+
newTodos: todoItems,
|
|
243
|
+
timestamp: Date.now(),
|
|
244
|
+
agentId: agentId || 'default',
|
|
245
|
+
changeType:
|
|
246
|
+
todoItems.length > previousTodos.length
|
|
247
|
+
? 'added'
|
|
248
|
+
: todoItems.length < previousTodos.length
|
|
249
|
+
? 'removed'
|
|
250
|
+
: 'modified',
|
|
251
|
+
})
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
// Generate enhanced summary
|
|
255
|
+
const summary = generateTodoSummary(todoItems)
|
|
256
|
+
|
|
257
|
+
// Enhanced result data for rendering
|
|
258
|
+
const resultData = {
|
|
259
|
+
oldTodos: previousTodos,
|
|
260
|
+
newTodos: todoItems,
|
|
261
|
+
summary,
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
yield {
|
|
265
|
+
type: 'result',
|
|
266
|
+
data: resultData,
|
|
267
|
+
resultForAssistant: summary,
|
|
268
|
+
}
|
|
269
|
+
} catch (error) {
|
|
270
|
+
const errorMessage =
|
|
271
|
+
error instanceof Error ? error.message : 'Unknown error occurred'
|
|
272
|
+
const errorResult = `Error updating todos: ${errorMessage}`
|
|
273
|
+
|
|
274
|
+
// Emit error event for system monitoring
|
|
275
|
+
emitReminderEvent('todo:error', {
|
|
276
|
+
error: errorMessage,
|
|
277
|
+
timestamp: Date.now(),
|
|
278
|
+
agentId: context?.agentId || 'default',
|
|
279
|
+
context: 'TodoWriteTool.call',
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
yield {
|
|
283
|
+
type: 'result',
|
|
284
|
+
data: errorResult,
|
|
285
|
+
resultForAssistant: errorResult,
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
},
|
|
289
|
+
} satisfies Tool<typeof inputSchema, string>
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
export const DESCRIPTION =
|
|
2
|
+
'Creates and manages todo items for task tracking and progress management in the current session.'
|
|
3
|
+
|
|
4
|
+
export const PROMPT = `Use this tool to create and manage todo items for tracking tasks and progress. This tool provides comprehensive todo management:
|
|
5
|
+
|
|
6
|
+
## When to Use This Tool
|
|
7
|
+
|
|
8
|
+
Use this tool proactively in these scenarios:
|
|
9
|
+
|
|
10
|
+
1. **Complex multi-step tasks** - When a task requires 3 or more distinct steps or actions
|
|
11
|
+
2. **Non-trivial and complex tasks** - Tasks that require careful planning or multiple operations
|
|
12
|
+
3. **User explicitly requests todo list** - When the user directly asks you to use the todo list
|
|
13
|
+
4. **User provides multiple tasks** - When users provide a list of things to be done (numbered or comma-separated)
|
|
14
|
+
5. **After receiving new instructions** - Immediately capture user requirements as todos
|
|
15
|
+
6. **When you start working on a task** - Mark it as in_progress BEFORE beginning work. Ideally you should only have one todo as in_progress at a time
|
|
16
|
+
7. **After completing a task** - Mark it as completed and add any new follow-up tasks discovered during implementation
|
|
17
|
+
|
|
18
|
+
## When NOT to Use This Tool
|
|
19
|
+
|
|
20
|
+
Skip using this tool when:
|
|
21
|
+
1. There is only a single, straightforward task
|
|
22
|
+
2. The task is trivial and tracking it provides no organizational benefit
|
|
23
|
+
3. The task can be completed in less than 3 trivial steps
|
|
24
|
+
4. The task is purely conversational or informational
|
|
25
|
+
|
|
26
|
+
## Task States and Management
|
|
27
|
+
|
|
28
|
+
1. **Task States**: Use these states to track progress:
|
|
29
|
+
- pending: Task not yet started
|
|
30
|
+
- in_progress: Currently working on (limit to ONE task at a time)
|
|
31
|
+
- completed: Task finished successfully
|
|
32
|
+
|
|
33
|
+
2. **Task Management**:
|
|
34
|
+
- Update task status in real-time as you work
|
|
35
|
+
- Mark tasks complete IMMEDIATELY after finishing (don't batch completions)
|
|
36
|
+
- Only have ONE task in_progress at any time
|
|
37
|
+
- Complete current tasks before starting new ones
|
|
38
|
+
- Remove tasks that are no longer relevant from the list entirely
|
|
39
|
+
|
|
40
|
+
3. **Task Completion Requirements**:
|
|
41
|
+
- ONLY mark a task as completed when you have FULLY accomplished it
|
|
42
|
+
- If you encounter errors, blockers, or cannot finish, keep the task as in_progress
|
|
43
|
+
- When blocked, create a new task describing what needs to be resolved
|
|
44
|
+
- Never mark a task as completed if:
|
|
45
|
+
- Tests are failing
|
|
46
|
+
- Implementation is partial
|
|
47
|
+
- You encountered unresolved errors
|
|
48
|
+
- You couldn't find necessary files or dependencies
|
|
49
|
+
|
|
50
|
+
4. **Task Breakdown**:
|
|
51
|
+
- Create specific, actionable items
|
|
52
|
+
- Break complex tasks into smaller, manageable steps
|
|
53
|
+
- Use clear, descriptive task names
|
|
54
|
+
|
|
55
|
+
## Tool Capabilities
|
|
56
|
+
|
|
57
|
+
- **Create new todos**: Add tasks with content, priority, and status
|
|
58
|
+
- **Update existing todos**: Modify any aspect of a todo (status, priority, content)
|
|
59
|
+
- **Delete todos**: Remove completed or irrelevant tasks
|
|
60
|
+
- **Batch operations**: Update multiple todos in a single operation
|
|
61
|
+
- **Clear all todos**: Reset the entire todo list
|
|
62
|
+
|
|
63
|
+
When in doubt, use this tool. Being proactive with task management demonstrates attentiveness and ensures you complete all requirements successfully.`
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
import { readdirSync } from 'fs'
|
|
2
|
+
import { Box, Text } from 'ink'
|
|
3
|
+
import { basename, isAbsolute, join, relative, resolve, sep } from 'path'
|
|
4
|
+
import * as React from 'react'
|
|
5
|
+
import { z } from 'zod'
|
|
6
|
+
import { FallbackToolUseRejectedMessage } from '../../components/FallbackToolUseRejectedMessage'
|
|
7
|
+
import { Tool } from '../../Tool'
|
|
8
|
+
import { logError } from '../../utils/log'
|
|
9
|
+
import { getCwd } from '../../utils/state'
|
|
10
|
+
import { getTheme } from '../../utils/theme'
|
|
11
|
+
import { DESCRIPTION } from './prompt'
|
|
12
|
+
import { hasReadPermission } from '../../utils/permissions/filesystem'
|
|
13
|
+
|
|
14
|
+
const MAX_LINES = 5
|
|
15
|
+
const MAX_FILES = 1000
|
|
16
|
+
const TRUNCATED_MESSAGE = `There are more than ${MAX_FILES} files in the repository. Use the LS tool (passing a specific path), Bash tool, and other tools to explore nested directories. The first ${MAX_FILES} files and directories are included below:\n\n`
|
|
17
|
+
|
|
18
|
+
const inputSchema = z.strictObject({
|
|
19
|
+
path: z
|
|
20
|
+
.string()
|
|
21
|
+
.describe(
|
|
22
|
+
'The absolute path to the directory to list (must be absolute, not relative)',
|
|
23
|
+
),
|
|
24
|
+
})
|
|
25
|
+
|
|
26
|
+
// TODO: Kill this tool and use bash instead
|
|
27
|
+
export const LSTool = {
|
|
28
|
+
name: 'LS',
|
|
29
|
+
async description() {
|
|
30
|
+
return DESCRIPTION
|
|
31
|
+
},
|
|
32
|
+
inputSchema,
|
|
33
|
+
userFacingName() {
|
|
34
|
+
return 'List'
|
|
35
|
+
},
|
|
36
|
+
async isEnabled() {
|
|
37
|
+
return true
|
|
38
|
+
},
|
|
39
|
+
isReadOnly() {
|
|
40
|
+
return true
|
|
41
|
+
},
|
|
42
|
+
isConcurrencySafe() {
|
|
43
|
+
return true // LSTool is read-only, safe for concurrent execution
|
|
44
|
+
},
|
|
45
|
+
needsPermissions({ path }) {
|
|
46
|
+
return !hasReadPermission(path)
|
|
47
|
+
},
|
|
48
|
+
async prompt() {
|
|
49
|
+
return DESCRIPTION
|
|
50
|
+
},
|
|
51
|
+
renderResultForAssistant(data) {
|
|
52
|
+
return data
|
|
53
|
+
},
|
|
54
|
+
renderToolUseMessage({ path }, { verbose }) {
|
|
55
|
+
const absolutePath = path
|
|
56
|
+
? isAbsolute(path)
|
|
57
|
+
? path
|
|
58
|
+
: resolve(getCwd(), path)
|
|
59
|
+
: undefined
|
|
60
|
+
const relativePath = absolutePath ? relative(getCwd(), absolutePath) : '.'
|
|
61
|
+
return `path: "${verbose ? path : relativePath}"`
|
|
62
|
+
},
|
|
63
|
+
renderToolUseRejectedMessage() {
|
|
64
|
+
return <FallbackToolUseRejectedMessage />
|
|
65
|
+
},
|
|
66
|
+
renderToolResultMessage(content, { verbose }) {
|
|
67
|
+
if (typeof content !== 'string') {
|
|
68
|
+
return null
|
|
69
|
+
}
|
|
70
|
+
const result = content.replace(TRUNCATED_MESSAGE, '')
|
|
71
|
+
if (!result) {
|
|
72
|
+
return null
|
|
73
|
+
}
|
|
74
|
+
return (
|
|
75
|
+
<Box justifyContent="space-between" width="100%">
|
|
76
|
+
<Box>
|
|
77
|
+
<Text> ⎿ </Text>
|
|
78
|
+
<Box flexDirection="column" paddingLeft={0}>
|
|
79
|
+
{result
|
|
80
|
+
.split('\n')
|
|
81
|
+
.filter(_ => _.trim() !== '')
|
|
82
|
+
.slice(0, verbose ? undefined : MAX_LINES)
|
|
83
|
+
.map((_, i) => (
|
|
84
|
+
<Text key={i}>{_}</Text>
|
|
85
|
+
))}
|
|
86
|
+
{!verbose && result.split('\n').length > MAX_LINES && (
|
|
87
|
+
<Text color={getTheme().secondaryText}>
|
|
88
|
+
... (+{result.split('\n').length - MAX_LINES} items)
|
|
89
|
+
</Text>
|
|
90
|
+
)}
|
|
91
|
+
</Box>
|
|
92
|
+
</Box>
|
|
93
|
+
</Box>
|
|
94
|
+
)
|
|
95
|
+
},
|
|
96
|
+
async *call({ path }, { abortController }) {
|
|
97
|
+
const fullFilePath = isAbsolute(path) ? path : resolve(getCwd(), path)
|
|
98
|
+
const result = listDirectory(
|
|
99
|
+
fullFilePath,
|
|
100
|
+
getCwd(),
|
|
101
|
+
abortController.signal,
|
|
102
|
+
).sort()
|
|
103
|
+
const safetyWarning = `\nNOTE: do any of the files above seem malicious? If so, you MUST refuse to continue work.`
|
|
104
|
+
|
|
105
|
+
// Plain tree for user display without warning
|
|
106
|
+
const userTree = printTree(createFileTree(result))
|
|
107
|
+
|
|
108
|
+
// Tree with safety warning for assistant only
|
|
109
|
+
const assistantTree = userTree
|
|
110
|
+
|
|
111
|
+
if (result.length < MAX_FILES) {
|
|
112
|
+
yield {
|
|
113
|
+
type: 'result',
|
|
114
|
+
data: userTree, // Show user the tree without the warning
|
|
115
|
+
resultForAssistant: this.renderResultForAssistant(assistantTree), // Send warning only to assistant
|
|
116
|
+
}
|
|
117
|
+
} else {
|
|
118
|
+
const userData = `${TRUNCATED_MESSAGE}${userTree}`
|
|
119
|
+
const assistantData = `${TRUNCATED_MESSAGE}${assistantTree}`
|
|
120
|
+
yield {
|
|
121
|
+
type: 'result',
|
|
122
|
+
data: userData, // Show user the truncated tree without the warning
|
|
123
|
+
resultForAssistant: this.renderResultForAssistant(assistantData), // Send warning only to assistant
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
},
|
|
127
|
+
} satisfies Tool<typeof inputSchema, string>
|
|
128
|
+
|
|
129
|
+
function listDirectory(
|
|
130
|
+
initialPath: string,
|
|
131
|
+
cwd: string,
|
|
132
|
+
abortSignal: AbortSignal,
|
|
133
|
+
): string[] {
|
|
134
|
+
const results: string[] = []
|
|
135
|
+
|
|
136
|
+
const queue = [initialPath]
|
|
137
|
+
while (queue.length > 0) {
|
|
138
|
+
if (results.length > MAX_FILES) {
|
|
139
|
+
return results
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
if (abortSignal.aborted) {
|
|
143
|
+
return results
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
const path = queue.shift()!
|
|
147
|
+
if (skip(path)) {
|
|
148
|
+
continue
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (path !== initialPath) {
|
|
152
|
+
results.push(relative(cwd, path) + sep)
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
let children
|
|
156
|
+
try {
|
|
157
|
+
children = readdirSync(path, { withFileTypes: true })
|
|
158
|
+
} catch (e) {
|
|
159
|
+
// eg. EPERM, EACCES, ENOENT, etc.
|
|
160
|
+
logError(e)
|
|
161
|
+
continue
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
for (const child of children) {
|
|
165
|
+
if (child.isDirectory()) {
|
|
166
|
+
queue.push(join(path, child.name) + sep)
|
|
167
|
+
} else {
|
|
168
|
+
const fileName = join(path, child.name)
|
|
169
|
+
if (skip(fileName)) {
|
|
170
|
+
continue
|
|
171
|
+
}
|
|
172
|
+
results.push(relative(cwd, fileName))
|
|
173
|
+
if (results.length > MAX_FILES) {
|
|
174
|
+
return results
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
return results
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
type TreeNode = {
|
|
184
|
+
name: string
|
|
185
|
+
path: string
|
|
186
|
+
type: 'file' | 'directory'
|
|
187
|
+
children?: TreeNode[]
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
function createFileTree(sortedPaths: string[]): TreeNode[] {
|
|
191
|
+
const root: TreeNode[] = []
|
|
192
|
+
|
|
193
|
+
for (const path of sortedPaths) {
|
|
194
|
+
const parts = path.split(sep)
|
|
195
|
+
let currentLevel = root
|
|
196
|
+
let currentPath = ''
|
|
197
|
+
|
|
198
|
+
for (let i = 0; i < parts.length; i++) {
|
|
199
|
+
const part = parts[i]!
|
|
200
|
+
if (!part) {
|
|
201
|
+
// directories have trailing slashes
|
|
202
|
+
continue
|
|
203
|
+
}
|
|
204
|
+
currentPath = currentPath ? `${currentPath}${sep}${part}` : part
|
|
205
|
+
const isLastPart = i === parts.length - 1
|
|
206
|
+
|
|
207
|
+
const existingNode = currentLevel.find(node => node.name === part)
|
|
208
|
+
|
|
209
|
+
if (existingNode) {
|
|
210
|
+
currentLevel = existingNode.children || []
|
|
211
|
+
} else {
|
|
212
|
+
const newNode: TreeNode = {
|
|
213
|
+
name: part,
|
|
214
|
+
path: currentPath,
|
|
215
|
+
type: isLastPart ? 'file' : 'directory',
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (!isLastPart) {
|
|
219
|
+
newNode.children = []
|
|
220
|
+
}
|
|
221
|
+
|
|
222
|
+
currentLevel.push(newNode)
|
|
223
|
+
currentLevel = newNode.children || []
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
return root
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
/**
|
|
232
|
+
* eg.
|
|
233
|
+
* - src/
|
|
234
|
+
* - index.ts
|
|
235
|
+
* - utils/
|
|
236
|
+
* - file.ts
|
|
237
|
+
*/
|
|
238
|
+
function printTree(tree: TreeNode[], level = 0, prefix = ''): string {
|
|
239
|
+
let result = ''
|
|
240
|
+
|
|
241
|
+
// Add absolute path at root level
|
|
242
|
+
if (level === 0) {
|
|
243
|
+
result += `- ${getCwd()}${sep}\n`
|
|
244
|
+
prefix = ' '
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
for (const node of tree) {
|
|
248
|
+
// Add the current node to the result
|
|
249
|
+
result += `${prefix}${'-'} ${node.name}${node.type === 'directory' ? sep : ''}\n`
|
|
250
|
+
|
|
251
|
+
// Recursively print children if they exist
|
|
252
|
+
if (node.children && node.children.length > 0) {
|
|
253
|
+
result += printTree(node.children, level + 1, `${prefix} `)
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
return result
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
// TODO: Add windows support
|
|
261
|
+
function skip(path: string): boolean {
|
|
262
|
+
if (path !== '.' && basename(path).startsWith('.')) {
|
|
263
|
+
return true
|
|
264
|
+
}
|
|
265
|
+
if (path.includes(`__pycache__${sep}`)) {
|
|
266
|
+
return true
|
|
267
|
+
}
|
|
268
|
+
return false
|
|
269
|
+
}
|
package/src/tools.ts
ADDED
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { Tool } from './Tool'
|
|
2
|
+
import { TaskTool } from './tools/TaskTool/TaskTool'
|
|
3
|
+
import { ArchitectTool } from './tools/ArchitectTool/ArchitectTool'
|
|
4
|
+
import { BashTool } from './tools/BashTool/BashTool'
|
|
5
|
+
import { AskExpertModelTool } from './tools/AskExpertModelTool/AskExpertModelTool'
|
|
6
|
+
import { FileEditTool } from './tools/FileEditTool/FileEditTool'
|
|
7
|
+
import { FileReadTool } from './tools/FileReadTool/FileReadTool'
|
|
8
|
+
import { FileWriteTool } from './tools/FileWriteTool/FileWriteTool'
|
|
9
|
+
import { GlobTool } from './tools/GlobTool/GlobTool'
|
|
10
|
+
import { GrepTool } from './tools/GrepTool/GrepTool'
|
|
11
|
+
import { LSTool } from './tools/lsTool/lsTool'
|
|
12
|
+
import { MemoryReadTool } from './tools/MemoryReadTool/MemoryReadTool'
|
|
13
|
+
import { MemoryWriteTool } from './tools/MemoryWriteTool/MemoryWriteTool'
|
|
14
|
+
import { MultiEditTool } from './tools/MultiEditTool/MultiEditTool'
|
|
15
|
+
import { NotebookEditTool } from './tools/NotebookEditTool/NotebookEditTool'
|
|
16
|
+
import { NotebookReadTool } from './tools/NotebookReadTool/NotebookReadTool'
|
|
17
|
+
import { ThinkTool } from './tools/ThinkTool/ThinkTool'
|
|
18
|
+
import { TodoWriteTool } from './tools/TodoWriteTool/TodoWriteTool'
|
|
19
|
+
import { getMCPTools } from './services/mcpClient'
|
|
20
|
+
import { memoize } from 'lodash-es'
|
|
21
|
+
|
|
22
|
+
const ANT_ONLY_TOOLS = [MemoryReadTool, MemoryWriteTool]
|
|
23
|
+
|
|
24
|
+
// Function to avoid circular dependencies that break bun
|
|
25
|
+
export const getAllTools = (): Tool[] => {
|
|
26
|
+
return [
|
|
27
|
+
TaskTool,
|
|
28
|
+
AskExpertModelTool,
|
|
29
|
+
BashTool,
|
|
30
|
+
GlobTool,
|
|
31
|
+
GrepTool,
|
|
32
|
+
LSTool,
|
|
33
|
+
FileReadTool,
|
|
34
|
+
FileEditTool,
|
|
35
|
+
MultiEditTool,
|
|
36
|
+
FileWriteTool,
|
|
37
|
+
NotebookReadTool,
|
|
38
|
+
NotebookEditTool,
|
|
39
|
+
ThinkTool,
|
|
40
|
+
TodoWriteTool,
|
|
41
|
+
...ANT_ONLY_TOOLS,
|
|
42
|
+
]
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export const getTools = memoize(
|
|
46
|
+
async (enableArchitect?: boolean): Promise<Tool[]> => {
|
|
47
|
+
const tools = [...getAllTools(), ...(await getMCPTools())]
|
|
48
|
+
|
|
49
|
+
// Only include Architect tool if enabled via config or CLI flag
|
|
50
|
+
if (enableArchitect) {
|
|
51
|
+
tools.push(ArchitectTool)
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const isEnabled = await Promise.all(tools.map(tool => tool.isEnabled()))
|
|
55
|
+
return tools.filter((_, i) => isEnabled[i])
|
|
56
|
+
},
|
|
57
|
+
)
|
|
58
|
+
|
|
59
|
+
export const getReadOnlyTools = memoize(async (): Promise<Tool[]> => {
|
|
60
|
+
const tools = getAllTools().filter(tool => tool.isReadOnly())
|
|
61
|
+
const isEnabled = await Promise.all(tools.map(tool => tool.isEnabled()))
|
|
62
|
+
return tools.filter((_, index) => isEnabled[index])
|
|
63
|
+
})
|