@shareai-lab/kode 1.0.71 → 1.0.75
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +160 -1
- package/README.zh-CN.md +65 -1
- package/cli.js +5 -10
- package/package.json +6 -2
- package/src/ProjectOnboarding.tsx +47 -29
- package/src/Tool.ts +33 -4
- package/src/commands/agents.tsx +3401 -0
- package/src/commands/help.tsx +2 -2
- package/src/commands/resume.tsx +2 -1
- package/src/commands/terminalSetup.ts +4 -4
- package/src/commands.ts +3 -0
- package/src/components/ApproveApiKey.tsx +1 -1
- package/src/components/Config.tsx +10 -6
- package/src/components/ConsoleOAuthFlow.tsx +5 -4
- package/src/components/CustomSelect/select-option.tsx +28 -2
- package/src/components/CustomSelect/select.tsx +14 -5
- package/src/components/CustomSelect/theme.ts +45 -0
- package/src/components/Help.tsx +4 -4
- package/src/components/InvalidConfigDialog.tsx +1 -1
- package/src/components/LogSelector.tsx +1 -1
- package/src/components/MCPServerApprovalDialog.tsx +1 -1
- package/src/components/Message.tsx +2 -0
- package/src/components/ModelListManager.tsx +10 -6
- package/src/components/ModelSelector.tsx +201 -23
- package/src/components/ModelStatusDisplay.tsx +7 -5
- package/src/components/PromptInput.tsx +146 -96
- package/src/components/SentryErrorBoundary.ts +9 -3
- package/src/components/StickerRequestForm.tsx +16 -0
- package/src/components/StructuredDiff.tsx +36 -29
- package/src/components/TextInput.tsx +13 -0
- package/src/components/TodoItem.tsx +47 -0
- package/src/components/TrustDialog.tsx +1 -1
- package/src/components/messages/AssistantLocalCommandOutputMessage.tsx +5 -1
- package/src/components/messages/AssistantToolUseMessage.tsx +14 -4
- package/src/components/messages/TaskProgressMessage.tsx +32 -0
- package/src/components/messages/TaskToolMessage.tsx +58 -0
- package/src/components/permissions/FallbackPermissionRequest.tsx +2 -4
- package/src/components/permissions/FileEditPermissionRequest/FileEditPermissionRequest.tsx +1 -1
- package/src/components/permissions/FileEditPermissionRequest/FileEditToolDiff.tsx +5 -3
- package/src/components/permissions/FileWritePermissionRequest/FileWritePermissionRequest.tsx +1 -1
- package/src/components/permissions/FileWritePermissionRequest/FileWriteToolDiff.tsx +5 -3
- package/src/components/permissions/FilesystemPermissionRequest/FilesystemPermissionRequest.tsx +2 -4
- package/src/components/permissions/PermissionRequest.tsx +3 -5
- package/src/constants/macros.ts +2 -0
- package/src/constants/modelCapabilities.ts +179 -0
- package/src/constants/models.ts +90 -0
- package/src/constants/product.ts +1 -1
- package/src/context.ts +7 -7
- package/src/entrypoints/cli.tsx +23 -3
- package/src/entrypoints/mcp.ts +10 -10
- package/src/hooks/useCanUseTool.ts +1 -1
- package/src/hooks/useTextInput.ts +5 -2
- package/src/hooks/useUnifiedCompletion.ts +1405 -0
- package/src/messages.ts +1 -0
- package/src/query.ts +3 -0
- package/src/screens/ConfigureNpmPrefix.tsx +1 -1
- package/src/screens/Doctor.tsx +1 -1
- package/src/screens/REPL.tsx +11 -12
- package/src/services/adapters/base.ts +38 -0
- package/src/services/adapters/chatCompletions.ts +90 -0
- package/src/services/adapters/responsesAPI.ts +170 -0
- package/src/services/claude.ts +198 -62
- package/src/services/customCommands.ts +43 -22
- package/src/services/gpt5ConnectionTest.ts +340 -0
- package/src/services/mcpClient.ts +1 -1
- package/src/services/mentionProcessor.ts +273 -0
- package/src/services/modelAdapterFactory.ts +69 -0
- package/src/services/openai.ts +534 -14
- package/src/services/responseStateManager.ts +90 -0
- package/src/services/systemReminder.ts +113 -12
- package/src/test/testAdapters.ts +96 -0
- package/src/tools/AskExpertModelTool/AskExpertModelTool.tsx +120 -56
- package/src/tools/BashTool/BashTool.tsx +4 -31
- package/src/tools/BashTool/BashToolResultMessage.tsx +1 -1
- package/src/tools/BashTool/OutputLine.tsx +1 -0
- package/src/tools/FileEditTool/FileEditTool.tsx +4 -5
- package/src/tools/FileReadTool/FileReadTool.tsx +43 -10
- package/src/tools/MCPTool/MCPTool.tsx +2 -1
- package/src/tools/MultiEditTool/MultiEditTool.tsx +2 -2
- package/src/tools/NotebookReadTool/NotebookReadTool.tsx +15 -23
- package/src/tools/StickerRequestTool/StickerRequestTool.tsx +1 -1
- package/src/tools/TaskTool/TaskTool.tsx +170 -86
- package/src/tools/TaskTool/prompt.ts +61 -25
- package/src/tools/ThinkTool/ThinkTool.tsx +1 -3
- package/src/tools/TodoWriteTool/TodoWriteTool.tsx +65 -41
- package/src/tools/lsTool/lsTool.tsx +5 -2
- package/src/tools.ts +16 -16
- package/src/types/conversation.ts +51 -0
- package/src/types/logs.ts +58 -0
- package/src/types/modelCapabilities.ts +64 -0
- package/src/types/notebook.ts +87 -0
- package/src/utils/advancedFuzzyMatcher.ts +290 -0
- package/src/utils/agentLoader.ts +284 -0
- package/src/utils/ask.tsx +1 -0
- package/src/utils/commands.ts +1 -1
- package/src/utils/commonUnixCommands.ts +161 -0
- package/src/utils/config.ts +173 -2
- package/src/utils/conversationRecovery.ts +1 -0
- package/src/utils/debugLogger.ts +13 -13
- package/src/utils/exampleCommands.ts +1 -0
- package/src/utils/fuzzyMatcher.ts +328 -0
- package/src/utils/messages.tsx +6 -5
- package/src/utils/model.ts +120 -42
- package/src/utils/responseState.ts +23 -0
- package/src/utils/secureFile.ts +559 -0
- package/src/utils/terminal.ts +1 -0
- package/src/utils/theme.ts +11 -0
- package/src/hooks/useSlashCommandTypeahead.ts +0 -137
|
@@ -20,7 +20,11 @@ export function AssistantLocalCommandOutputMessage({
|
|
|
20
20
|
].filter(Boolean)
|
|
21
21
|
|
|
22
22
|
if (insides.length === 0) {
|
|
23
|
-
insides = [
|
|
23
|
+
insides = [
|
|
24
|
+
<React.Fragment key="0">
|
|
25
|
+
<Text>(No output)</Text>
|
|
26
|
+
</React.Fragment>
|
|
27
|
+
]
|
|
24
28
|
}
|
|
25
29
|
|
|
26
30
|
return [
|
|
@@ -9,6 +9,7 @@ import { getTheme } from '../../utils/theme'
|
|
|
9
9
|
import { BLACK_CIRCLE } from '../../constants/figures'
|
|
10
10
|
import { ThinkTool } from '../../tools/ThinkTool/ThinkTool'
|
|
11
11
|
import { AssistantThinkingMessage } from './AssistantThinkingMessage'
|
|
12
|
+
import { TaskToolMessage } from './TaskToolMessage'
|
|
12
13
|
|
|
13
14
|
type Props = {
|
|
14
15
|
param: ToolUseBlockParam
|
|
@@ -61,7 +62,7 @@ export function AssistantToolUseMessage({
|
|
|
61
62
|
)
|
|
62
63
|
}
|
|
63
64
|
|
|
64
|
-
const userFacingToolName = tool.userFacingName(param.input
|
|
65
|
+
const userFacingToolName = tool.userFacingName ? tool.userFacingName(param.input) : tool.name
|
|
65
66
|
return (
|
|
66
67
|
<Box
|
|
67
68
|
flexDirection="row"
|
|
@@ -86,9 +87,18 @@ export function AssistantToolUseMessage({
|
|
|
86
87
|
isError={erroredToolUseIDs.has(param.id)}
|
|
87
88
|
/>
|
|
88
89
|
))}
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
90
|
+
{tool.name === 'Task' && param.input ? (
|
|
91
|
+
<TaskToolMessage
|
|
92
|
+
agentType={(param.input as any).subagent_type || 'general-purpose'}
|
|
93
|
+
bold={!isQueued}
|
|
94
|
+
>
|
|
95
|
+
{userFacingToolName}
|
|
96
|
+
</TaskToolMessage>
|
|
97
|
+
) : (
|
|
98
|
+
<Text color={color} bold={!isQueued}>
|
|
99
|
+
{userFacingToolName}
|
|
100
|
+
</Text>
|
|
101
|
+
)}
|
|
92
102
|
</Box>
|
|
93
103
|
<Box flexWrap="nowrap">
|
|
94
104
|
{Object.keys(param.input as { [key: string]: unknown }).length > 0 &&
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
import { Box, Text } from 'ink'
|
|
3
|
+
import { getTheme } from '../../utils/theme'
|
|
4
|
+
|
|
5
|
+
interface Props {
|
|
6
|
+
agentType: string
|
|
7
|
+
status: string
|
|
8
|
+
toolCount?: number
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export function TaskProgressMessage({ agentType, status, toolCount }: Props) {
|
|
12
|
+
const theme = getTheme()
|
|
13
|
+
|
|
14
|
+
return (
|
|
15
|
+
<Box flexDirection="column" marginTop={1}>
|
|
16
|
+
<Box flexDirection="row">
|
|
17
|
+
<Text color={theme.claude}>⎯ </Text>
|
|
18
|
+
<Text color={theme.text} bold>
|
|
19
|
+
[{agentType}]
|
|
20
|
+
</Text>
|
|
21
|
+
<Text color={theme.secondaryText}> {status}</Text>
|
|
22
|
+
</Box>
|
|
23
|
+
{toolCount && toolCount > 0 && (
|
|
24
|
+
<Box marginLeft={3}>
|
|
25
|
+
<Text color={theme.secondaryText}>
|
|
26
|
+
Tools used: {toolCount}
|
|
27
|
+
</Text>
|
|
28
|
+
</Box>
|
|
29
|
+
)}
|
|
30
|
+
</Box>
|
|
31
|
+
)
|
|
32
|
+
}
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import React, { useEffect, useState, useMemo } from 'react'
|
|
2
|
+
import { Text } from 'ink'
|
|
3
|
+
import { getAgentByType } from '../../utils/agentLoader'
|
|
4
|
+
import { getTheme } from '../../utils/theme'
|
|
5
|
+
|
|
6
|
+
interface Props {
|
|
7
|
+
agentType: string
|
|
8
|
+
children: React.ReactNode
|
|
9
|
+
bold?: boolean
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
// Simple cache to prevent re-fetching agent configs
|
|
13
|
+
const agentConfigCache = new Map<string, any>()
|
|
14
|
+
|
|
15
|
+
export function TaskToolMessage({ agentType, children, bold = true }: Props) {
|
|
16
|
+
const theme = getTheme()
|
|
17
|
+
const [agentConfig, setAgentConfig] = useState<any>(() => {
|
|
18
|
+
// Return cached config immediately if available
|
|
19
|
+
return agentConfigCache.get(agentType) || null
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
useEffect(() => {
|
|
23
|
+
// Skip if already cached
|
|
24
|
+
if (agentConfigCache.has(agentType)) {
|
|
25
|
+
setAgentConfig(agentConfigCache.get(agentType))
|
|
26
|
+
return
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
// Load and cache agent configuration
|
|
30
|
+
let mounted = true
|
|
31
|
+
getAgentByType(agentType).then(config => {
|
|
32
|
+
if (mounted) {
|
|
33
|
+
agentConfigCache.set(agentType, config)
|
|
34
|
+
setAgentConfig(config)
|
|
35
|
+
}
|
|
36
|
+
}).catch(() => {
|
|
37
|
+
// Silently handle errors to prevent console noise
|
|
38
|
+
if (mounted) {
|
|
39
|
+
agentConfigCache.set(agentType, null)
|
|
40
|
+
}
|
|
41
|
+
})
|
|
42
|
+
|
|
43
|
+
return () => {
|
|
44
|
+
mounted = false
|
|
45
|
+
}
|
|
46
|
+
}, [agentType])
|
|
47
|
+
|
|
48
|
+
// Memoize color calculation to prevent unnecessary re-renders
|
|
49
|
+
const color = useMemo(() => {
|
|
50
|
+
return agentConfig?.color || theme.text
|
|
51
|
+
}, [agentConfig?.color, theme.text])
|
|
52
|
+
|
|
53
|
+
return (
|
|
54
|
+
<Text color={color} bold={bold}>
|
|
55
|
+
{children}
|
|
56
|
+
</Text>
|
|
57
|
+
)
|
|
58
|
+
}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Box, Text } from 'ink'
|
|
2
2
|
import React, { useMemo } from 'react'
|
|
3
|
-
import { Select } from '
|
|
3
|
+
import { Select } from '../CustomSelect/select'
|
|
4
4
|
import { getTheme } from '../../utils/theme'
|
|
5
5
|
import {
|
|
6
6
|
PermissionRequestTitle,
|
|
@@ -34,9 +34,7 @@ export function FallbackPermissionRequest({
|
|
|
34
34
|
const theme = getTheme()
|
|
35
35
|
|
|
36
36
|
// TODO: Avoid these special cases
|
|
37
|
-
const originalUserFacingName = toolUseConfirm.tool.userFacingName(
|
|
38
|
-
toolUseConfirm.input as never,
|
|
39
|
-
)
|
|
37
|
+
const originalUserFacingName = toolUseConfirm.tool.userFacingName()
|
|
40
38
|
const userFacingName = originalUserFacingName.endsWith(' (MCP)')
|
|
41
39
|
? originalUserFacingName.slice(0, -6)
|
|
42
40
|
: originalUserFacingName
|
|
@@ -64,9 +64,11 @@ export function FileEditToolDiff({
|
|
|
64
64
|
/>
|
|
65
65
|
)),
|
|
66
66
|
i => (
|
|
67
|
-
<
|
|
68
|
-
|
|
69
|
-
|
|
67
|
+
<React.Fragment key={`ellipsis-${i}`}>
|
|
68
|
+
<Text color={getTheme().secondaryText}>
|
|
69
|
+
...
|
|
70
|
+
</Text>
|
|
71
|
+
</React.Fragment>
|
|
70
72
|
),
|
|
71
73
|
)}
|
|
72
74
|
</Box>
|
|
@@ -65,9 +65,11 @@ export function FileWriteToolDiff({
|
|
|
65
65
|
/>
|
|
66
66
|
)),
|
|
67
67
|
i => (
|
|
68
|
-
<
|
|
69
|
-
|
|
70
|
-
|
|
68
|
+
<React.Fragment key={`ellipsis-${i}`}>
|
|
69
|
+
<Text color={getTheme().secondaryText}>
|
|
70
|
+
...
|
|
71
|
+
</Text>
|
|
72
|
+
</React.Fragment>
|
|
71
73
|
),
|
|
72
74
|
)
|
|
73
75
|
) : (
|
package/src/components/permissions/FilesystemPermissionRequest/FilesystemPermissionRequest.tsx
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { Box, Text } from 'ink'
|
|
2
2
|
import React, { useMemo } from 'react'
|
|
3
|
-
import { Select } from '
|
|
3
|
+
import { Select } from '../../CustomSelect/select'
|
|
4
4
|
import { getTheme } from '../../../utils/theme'
|
|
5
5
|
import {
|
|
6
6
|
PermissionRequestTitle,
|
|
@@ -133,9 +133,7 @@ function FilesystemPermissionRequestImpl({
|
|
|
133
133
|
onDone,
|
|
134
134
|
verbose,
|
|
135
135
|
}: Props): React.ReactNode {
|
|
136
|
-
const userFacingName = toolUseConfirm.tool.userFacingName(
|
|
137
|
-
toolUseConfirm.input as never,
|
|
138
|
-
)
|
|
136
|
+
const userFacingName = toolUseConfirm.tool.userFacingName()
|
|
139
137
|
|
|
140
138
|
const userFacingReadOrWrite = toolUseConfirm.tool.isReadOnly()
|
|
141
139
|
? 'Read'
|
|
@@ -51,8 +51,8 @@ export function toolUseConfirmGetPrefix(
|
|
|
51
51
|
): string | null {
|
|
52
52
|
return (
|
|
53
53
|
(toolUseConfirm.commandPrefix &&
|
|
54
|
-
!toolUseConfirm.commandPrefix.commandInjectionDetected &&
|
|
55
|
-
toolUseConfirm.commandPrefix.commandPrefix) ||
|
|
54
|
+
!(toolUseConfirm.commandPrefix as any).commandInjectionDetected &&
|
|
55
|
+
(toolUseConfirm.commandPrefix as any).commandPrefix) ||
|
|
56
56
|
null
|
|
57
57
|
)
|
|
58
58
|
}
|
|
@@ -84,9 +84,7 @@ export function PermissionRequest({
|
|
|
84
84
|
}
|
|
85
85
|
})
|
|
86
86
|
|
|
87
|
-
const toolName = toolUseConfirm.tool.userFacingName(
|
|
88
|
-
toolUseConfirm.input as never,
|
|
89
|
-
)
|
|
87
|
+
const toolName = toolUseConfirm.tool.userFacingName?.() || 'Tool'
|
|
90
88
|
useNotifyAfterTimeout(
|
|
91
89
|
`${PRODUCT_NAME} needs your permission to use ${toolName}`,
|
|
92
90
|
)
|
package/src/constants/macros.ts
CHANGED
|
@@ -3,4 +3,6 @@ import { version } from '../../package.json'
|
|
|
3
3
|
export const MACRO = {
|
|
4
4
|
VERSION: version,
|
|
5
5
|
README_URL: 'https://docs.anthropic.com/s/claude-code',
|
|
6
|
+
PACKAGE_URL: '@shareai-lab/kode',
|
|
7
|
+
ISSUES_EXPLAINER: 'report the issue at https://github.com/shareAI-lab/kode/issues',
|
|
6
8
|
}
|
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { ModelCapabilities } from '../types/modelCapabilities'
|
|
2
|
+
|
|
3
|
+
// GPT-5 standard capability definition
|
|
4
|
+
const GPT5_CAPABILITIES: ModelCapabilities = {
|
|
5
|
+
apiArchitecture: {
|
|
6
|
+
primary: 'responses_api',
|
|
7
|
+
fallback: 'chat_completions'
|
|
8
|
+
},
|
|
9
|
+
parameters: {
|
|
10
|
+
maxTokensField: 'max_completion_tokens',
|
|
11
|
+
supportsReasoningEffort: true,
|
|
12
|
+
supportsVerbosity: true,
|
|
13
|
+
temperatureMode: 'fixed_one'
|
|
14
|
+
},
|
|
15
|
+
toolCalling: {
|
|
16
|
+
mode: 'custom_tools',
|
|
17
|
+
supportsFreeform: true,
|
|
18
|
+
supportsAllowedTools: true,
|
|
19
|
+
supportsParallelCalls: true
|
|
20
|
+
},
|
|
21
|
+
stateManagement: {
|
|
22
|
+
supportsResponseId: true,
|
|
23
|
+
supportsConversationChaining: true,
|
|
24
|
+
supportsPreviousResponseId: true
|
|
25
|
+
},
|
|
26
|
+
streaming: {
|
|
27
|
+
supported: false, // Responses API doesn't support streaming yet
|
|
28
|
+
includesUsage: true
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
// Chat Completions standard capability definition
|
|
33
|
+
const CHAT_COMPLETIONS_CAPABILITIES: ModelCapabilities = {
|
|
34
|
+
apiArchitecture: {
|
|
35
|
+
primary: 'chat_completions'
|
|
36
|
+
},
|
|
37
|
+
parameters: {
|
|
38
|
+
maxTokensField: 'max_tokens',
|
|
39
|
+
supportsReasoningEffort: false,
|
|
40
|
+
supportsVerbosity: false,
|
|
41
|
+
temperatureMode: 'flexible'
|
|
42
|
+
},
|
|
43
|
+
toolCalling: {
|
|
44
|
+
mode: 'function_calling',
|
|
45
|
+
supportsFreeform: false,
|
|
46
|
+
supportsAllowedTools: false,
|
|
47
|
+
supportsParallelCalls: true
|
|
48
|
+
},
|
|
49
|
+
stateManagement: {
|
|
50
|
+
supportsResponseId: false,
|
|
51
|
+
supportsConversationChaining: false,
|
|
52
|
+
supportsPreviousResponseId: false
|
|
53
|
+
},
|
|
54
|
+
streaming: {
|
|
55
|
+
supported: true,
|
|
56
|
+
includesUsage: true
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Complete model capability mapping table
|
|
61
|
+
export const MODEL_CAPABILITIES_REGISTRY: Record<string, ModelCapabilities> = {
|
|
62
|
+
// GPT-5 series
|
|
63
|
+
'gpt-5': GPT5_CAPABILITIES,
|
|
64
|
+
'gpt-5-mini': GPT5_CAPABILITIES,
|
|
65
|
+
'gpt-5-nano': GPT5_CAPABILITIES,
|
|
66
|
+
'gpt-5-chat-latest': GPT5_CAPABILITIES,
|
|
67
|
+
|
|
68
|
+
// GPT-4 series
|
|
69
|
+
'gpt-4o': CHAT_COMPLETIONS_CAPABILITIES,
|
|
70
|
+
'gpt-4o-mini': CHAT_COMPLETIONS_CAPABILITIES,
|
|
71
|
+
'gpt-4-turbo': CHAT_COMPLETIONS_CAPABILITIES,
|
|
72
|
+
'gpt-4': CHAT_COMPLETIONS_CAPABILITIES,
|
|
73
|
+
|
|
74
|
+
// Claude series (supported through conversion layer)
|
|
75
|
+
'claude-3-5-sonnet-20241022': CHAT_COMPLETIONS_CAPABILITIES,
|
|
76
|
+
'claude-3-5-haiku-20241022': CHAT_COMPLETIONS_CAPABILITIES,
|
|
77
|
+
'claude-3-opus-20240229': CHAT_COMPLETIONS_CAPABILITIES,
|
|
78
|
+
|
|
79
|
+
// O1 series (special reasoning models)
|
|
80
|
+
'o1': {
|
|
81
|
+
...CHAT_COMPLETIONS_CAPABILITIES,
|
|
82
|
+
parameters: {
|
|
83
|
+
...CHAT_COMPLETIONS_CAPABILITIES.parameters,
|
|
84
|
+
maxTokensField: 'max_completion_tokens',
|
|
85
|
+
temperatureMode: 'fixed_one'
|
|
86
|
+
}
|
|
87
|
+
},
|
|
88
|
+
'o1-mini': {
|
|
89
|
+
...CHAT_COMPLETIONS_CAPABILITIES,
|
|
90
|
+
parameters: {
|
|
91
|
+
...CHAT_COMPLETIONS_CAPABILITIES.parameters,
|
|
92
|
+
maxTokensField: 'max_completion_tokens',
|
|
93
|
+
temperatureMode: 'fixed_one'
|
|
94
|
+
}
|
|
95
|
+
},
|
|
96
|
+
'o1-preview': {
|
|
97
|
+
...CHAT_COMPLETIONS_CAPABILITIES,
|
|
98
|
+
parameters: {
|
|
99
|
+
...CHAT_COMPLETIONS_CAPABILITIES.parameters,
|
|
100
|
+
maxTokensField: 'max_completion_tokens',
|
|
101
|
+
temperatureMode: 'fixed_one'
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
// Intelligently infer capabilities for unregistered models
|
|
107
|
+
export function inferModelCapabilities(modelName: string): ModelCapabilities | null {
|
|
108
|
+
if (!modelName) return null
|
|
109
|
+
|
|
110
|
+
const lowerName = modelName.toLowerCase()
|
|
111
|
+
|
|
112
|
+
// GPT-5 series
|
|
113
|
+
if (lowerName.includes('gpt-5') || lowerName.includes('gpt5')) {
|
|
114
|
+
return GPT5_CAPABILITIES
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// GPT-6 series (reserved for future)
|
|
118
|
+
if (lowerName.includes('gpt-6') || lowerName.includes('gpt6')) {
|
|
119
|
+
return {
|
|
120
|
+
...GPT5_CAPABILITIES,
|
|
121
|
+
streaming: { supported: true, includesUsage: true }
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
// GLM series
|
|
126
|
+
if (lowerName.includes('glm-5') || lowerName.includes('glm5')) {
|
|
127
|
+
return {
|
|
128
|
+
...GPT5_CAPABILITIES,
|
|
129
|
+
toolCalling: {
|
|
130
|
+
...GPT5_CAPABILITIES.toolCalling,
|
|
131
|
+
supportsAllowedTools: false // GLM might not support this
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// O1 series
|
|
137
|
+
if (lowerName.startsWith('o1') || lowerName.includes('o1-')) {
|
|
138
|
+
return {
|
|
139
|
+
...CHAT_COMPLETIONS_CAPABILITIES,
|
|
140
|
+
parameters: {
|
|
141
|
+
...CHAT_COMPLETIONS_CAPABILITIES.parameters,
|
|
142
|
+
maxTokensField: 'max_completion_tokens',
|
|
143
|
+
temperatureMode: 'fixed_one'
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
// Default to null, let system use default behavior
|
|
149
|
+
return null
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Get model capabilities (with caching)
|
|
153
|
+
const capabilityCache = new Map<string, ModelCapabilities>()
|
|
154
|
+
|
|
155
|
+
export function getModelCapabilities(modelName: string): ModelCapabilities {
|
|
156
|
+
// Check cache
|
|
157
|
+
if (capabilityCache.has(modelName)) {
|
|
158
|
+
return capabilityCache.get(modelName)!
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// Look up in registry
|
|
162
|
+
if (MODEL_CAPABILITIES_REGISTRY[modelName]) {
|
|
163
|
+
const capabilities = MODEL_CAPABILITIES_REGISTRY[modelName]
|
|
164
|
+
capabilityCache.set(modelName, capabilities)
|
|
165
|
+
return capabilities
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
// Try to infer
|
|
169
|
+
const inferred = inferModelCapabilities(modelName)
|
|
170
|
+
if (inferred) {
|
|
171
|
+
capabilityCache.set(modelName, inferred)
|
|
172
|
+
return inferred
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
// Default to Chat Completions
|
|
176
|
+
const defaultCapabilities = CHAT_COMPLETIONS_CAPABILITIES
|
|
177
|
+
capabilityCache.set(modelName, defaultCapabilities)
|
|
178
|
+
return defaultCapabilities
|
|
179
|
+
}
|
package/src/constants/models.ts
CHANGED
|
@@ -278,6 +278,96 @@ export default {
|
|
|
278
278
|
supports_system_messages: true,
|
|
279
279
|
supports_tool_choice: true,
|
|
280
280
|
},
|
|
281
|
+
// GPT-5 Models
|
|
282
|
+
{
|
|
283
|
+
model: 'gpt-5',
|
|
284
|
+
max_tokens: 32768,
|
|
285
|
+
max_input_tokens: 200000,
|
|
286
|
+
max_output_tokens: 32768,
|
|
287
|
+
input_cost_per_token: 0.00001,
|
|
288
|
+
output_cost_per_token: 0.00005,
|
|
289
|
+
cache_read_input_token_cost: 0.000005,
|
|
290
|
+
provider: 'openai',
|
|
291
|
+
mode: 'chat',
|
|
292
|
+
supports_function_calling: true,
|
|
293
|
+
supports_parallel_function_calling: true,
|
|
294
|
+
supports_vision: true,
|
|
295
|
+
supports_prompt_caching: true,
|
|
296
|
+
supports_system_messages: true,
|
|
297
|
+
supports_tool_choice: true,
|
|
298
|
+
supports_reasoning_effort: true,
|
|
299
|
+
supports_responses_api: true,
|
|
300
|
+
supports_custom_tools: true,
|
|
301
|
+
supports_allowed_tools: true,
|
|
302
|
+
supports_verbosity_control: true,
|
|
303
|
+
},
|
|
304
|
+
{
|
|
305
|
+
model: 'gpt-5-mini',
|
|
306
|
+
max_tokens: 16384,
|
|
307
|
+
max_input_tokens: 128000,
|
|
308
|
+
max_output_tokens: 16384,
|
|
309
|
+
input_cost_per_token: 0.000001,
|
|
310
|
+
output_cost_per_token: 0.000005,
|
|
311
|
+
cache_read_input_token_cost: 0.0000005,
|
|
312
|
+
provider: 'openai',
|
|
313
|
+
mode: 'chat',
|
|
314
|
+
supports_function_calling: true,
|
|
315
|
+
supports_parallel_function_calling: true,
|
|
316
|
+
supports_vision: true,
|
|
317
|
+
supports_prompt_caching: true,
|
|
318
|
+
supports_system_messages: true,
|
|
319
|
+
supports_tool_choice: true,
|
|
320
|
+
supports_reasoning_effort: true,
|
|
321
|
+
supports_responses_api: true,
|
|
322
|
+
supports_custom_tools: true,
|
|
323
|
+
supports_allowed_tools: true,
|
|
324
|
+
supports_verbosity_control: true,
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
model: 'gpt-5-nano',
|
|
328
|
+
max_tokens: 8192,
|
|
329
|
+
max_input_tokens: 64000,
|
|
330
|
+
max_output_tokens: 8192,
|
|
331
|
+
input_cost_per_token: 0.0000005,
|
|
332
|
+
output_cost_per_token: 0.000002,
|
|
333
|
+
cache_read_input_token_cost: 0.00000025,
|
|
334
|
+
provider: 'openai',
|
|
335
|
+
mode: 'chat',
|
|
336
|
+
supports_function_calling: true,
|
|
337
|
+
supports_parallel_function_calling: true,
|
|
338
|
+
supports_vision: false,
|
|
339
|
+
supports_prompt_caching: true,
|
|
340
|
+
supports_system_messages: true,
|
|
341
|
+
supports_tool_choice: true,
|
|
342
|
+
supports_reasoning_effort: true,
|
|
343
|
+
supports_responses_api: true,
|
|
344
|
+
supports_custom_tools: true,
|
|
345
|
+
supports_allowed_tools: true,
|
|
346
|
+
supports_verbosity_control: true,
|
|
347
|
+
},
|
|
348
|
+
{
|
|
349
|
+
model: 'gpt-5-chat-latest',
|
|
350
|
+
max_tokens: 32768,
|
|
351
|
+
max_input_tokens: 200000,
|
|
352
|
+
max_output_tokens: 32768,
|
|
353
|
+
input_cost_per_token: 0.00001,
|
|
354
|
+
output_cost_per_token: 0.00005,
|
|
355
|
+
cache_read_input_token_cost: 0.000005,
|
|
356
|
+
provider: 'openai',
|
|
357
|
+
mode: 'chat',
|
|
358
|
+
supports_function_calling: true,
|
|
359
|
+
supports_parallel_function_calling: true,
|
|
360
|
+
supports_vision: true,
|
|
361
|
+
supports_prompt_caching: true,
|
|
362
|
+
supports_system_messages: true,
|
|
363
|
+
supports_tool_choice: true,
|
|
364
|
+
supports_reasoning_effort: true,
|
|
365
|
+
supports_responses_api: false,
|
|
366
|
+
supports_custom_tools: false,
|
|
367
|
+
supports_allowed_tools: false,
|
|
368
|
+
supports_verbosity_control: true,
|
|
369
|
+
requires_chat_completions: true,
|
|
370
|
+
},
|
|
281
371
|
],
|
|
282
372
|
mistral: [
|
|
283
373
|
{
|
package/src/constants/product.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
export const PRODUCT_NAME = 'Kode'
|
|
2
2
|
export const PRODUCT_URL = 'https://github.com/shareAI-lab/Anykode'
|
|
3
|
-
export const PROJECT_FILE = '
|
|
3
|
+
export const PROJECT_FILE = 'AGENTS.md'
|
|
4
4
|
export const PRODUCT_COMMAND = 'kode'
|
|
5
5
|
export const CONFIG_BASE_DIR = '.kode'
|
|
6
6
|
export const CONFIG_FILE = '.kode.json'
|
package/src/context.ts
CHANGED
|
@@ -19,13 +19,13 @@ import { lastX } from './utils/generators'
|
|
|
19
19
|
import { getGitEmail } from './utils/user'
|
|
20
20
|
import { PROJECT_FILE } from './constants/product'
|
|
21
21
|
/**
|
|
22
|
-
* Find all
|
|
22
|
+
* Find all AGENTS.md and CLAUDE.md files in the current working directory
|
|
23
23
|
*/
|
|
24
24
|
export async function getClaudeFiles(): Promise<string | null> {
|
|
25
25
|
const abortController = new AbortController()
|
|
26
26
|
const timeout = setTimeout(() => abortController.abort(), 3000)
|
|
27
27
|
try {
|
|
28
|
-
// Search for both
|
|
28
|
+
// Search for both AGENTS.md and CLAUDE.md files
|
|
29
29
|
const [codeContextFiles, claudeFiles] = await Promise.all([
|
|
30
30
|
ripGrep(
|
|
31
31
|
['--files', '--glob', join('**', '*', PROJECT_FILE)],
|
|
@@ -46,7 +46,7 @@ export async function getClaudeFiles(): Promise<string | null> {
|
|
|
46
46
|
|
|
47
47
|
// Add instructions for additional project files
|
|
48
48
|
const fileTypes = []
|
|
49
|
-
if (codeContextFiles.length > 0) fileTypes.push('
|
|
49
|
+
if (codeContextFiles.length > 0) fileTypes.push('AGENTS.md')
|
|
50
50
|
if (claudeFiles.length > 0) fileTypes.push('CLAUDE.md')
|
|
51
51
|
|
|
52
52
|
return `NOTE: Additional project documentation files (${fileTypes.join(', ')}) were found. When working in these directories, make sure to read and follow the instructions in the corresponding files:\n${allFiles
|
|
@@ -97,21 +97,21 @@ export const getReadme = memoize(async (): Promise<string | null> => {
|
|
|
97
97
|
})
|
|
98
98
|
|
|
99
99
|
/**
|
|
100
|
-
* Get project documentation content (
|
|
100
|
+
* Get project documentation content (AGENTS.md and CLAUDE.md)
|
|
101
101
|
*/
|
|
102
102
|
export const getProjectDocs = memoize(async (): Promise<string | null> => {
|
|
103
103
|
try {
|
|
104
104
|
const cwd = getCwd()
|
|
105
|
-
const codeContextPath = join(cwd, '
|
|
105
|
+
const codeContextPath = join(cwd, 'AGENTS.md')
|
|
106
106
|
const claudePath = join(cwd, 'CLAUDE.md')
|
|
107
107
|
|
|
108
108
|
const docs = []
|
|
109
109
|
|
|
110
|
-
// Try to read
|
|
110
|
+
// Try to read AGENTS.md
|
|
111
111
|
if (existsSync(codeContextPath)) {
|
|
112
112
|
try {
|
|
113
113
|
const content = await readFile(codeContextPath, 'utf-8')
|
|
114
|
-
docs.push(`#
|
|
114
|
+
docs.push(`# AGENTS.md\n\n${content}`)
|
|
115
115
|
} catch (e) {
|
|
116
116
|
logError(e)
|
|
117
117
|
}
|
package/src/entrypoints/cli.tsx
CHANGED
|
@@ -32,6 +32,7 @@ import {
|
|
|
32
32
|
getConfigForCLI,
|
|
33
33
|
listConfigForCLI,
|
|
34
34
|
enableConfigs,
|
|
35
|
+
validateAndRepairAllGPT5Profiles,
|
|
35
36
|
} from '../utils/config'
|
|
36
37
|
import { cwd } from 'process'
|
|
37
38
|
import { dateToFilename, logError, parseLogFilename } from '../utils/log'
|
|
@@ -40,7 +41,7 @@ import { Onboarding } from '../components/Onboarding'
|
|
|
40
41
|
import { Doctor } from '../screens/Doctor'
|
|
41
42
|
import { ApproveApiKey } from '../components/ApproveApiKey'
|
|
42
43
|
import { TrustDialog } from '../components/TrustDialog'
|
|
43
|
-
import { checkHasTrustDialogAccepted } from '../utils/config'
|
|
44
|
+
import { checkHasTrustDialogAccepted, McpServerConfig } from '../utils/config'
|
|
44
45
|
import { isDefaultSlowAndCapableModel } from '../utils/model'
|
|
45
46
|
import { LogList } from '../screens/LogList'
|
|
46
47
|
import { ResumeConversation } from '../screens/ResumeConversation'
|
|
@@ -184,6 +185,13 @@ async function setup(cwd: string, safeMode?: boolean): Promise<void> {
|
|
|
184
185
|
|
|
185
186
|
// Always grant read permissions for original working dir
|
|
186
187
|
grantReadPermissionForOriginalDir()
|
|
188
|
+
|
|
189
|
+
// Start watching agent configuration files for changes
|
|
190
|
+
const { startAgentWatcher, clearAgentCache } = await import('../utils/agentLoader')
|
|
191
|
+
await startAgentWatcher(() => {
|
|
192
|
+
// Cache is already cleared in the watcher, just log
|
|
193
|
+
console.log('✅ Agent configurations hot-reloaded')
|
|
194
|
+
})
|
|
187
195
|
|
|
188
196
|
// If --safe mode is enabled, prevent root/sudo usage for security
|
|
189
197
|
if (safeMode) {
|
|
@@ -263,6 +271,17 @@ async function main() {
|
|
|
263
271
|
// Validate configs are valid and enable configuration system
|
|
264
272
|
try {
|
|
265
273
|
enableConfigs()
|
|
274
|
+
|
|
275
|
+
// 🔧 Validate and auto-repair GPT-5 model profiles
|
|
276
|
+
try {
|
|
277
|
+
const repairResult = validateAndRepairAllGPT5Profiles()
|
|
278
|
+
if (repairResult.repaired > 0) {
|
|
279
|
+
console.log(`🔧 Auto-repaired ${repairResult.repaired} GPT-5 model configurations`)
|
|
280
|
+
}
|
|
281
|
+
} catch (repairError) {
|
|
282
|
+
// Don't block startup if GPT-5 validation fails
|
|
283
|
+
console.warn('⚠️ GPT-5 configuration validation failed:', repairError)
|
|
284
|
+
}
|
|
266
285
|
} catch (error: unknown) {
|
|
267
286
|
if (error instanceof ConfigParseError) {
|
|
268
287
|
// Show the invalid config dialog with the error object
|
|
@@ -274,10 +293,11 @@ async function main() {
|
|
|
274
293
|
let inputPrompt = ''
|
|
275
294
|
let renderContext: RenderOptions | undefined = {
|
|
276
295
|
exitOnCtrlC: false,
|
|
296
|
+
// @ts-expect-error - onFlicker not in RenderOptions interface
|
|
277
297
|
onFlicker() {
|
|
278
298
|
logEvent('tengu_flicker', {})
|
|
279
299
|
},
|
|
280
|
-
}
|
|
300
|
+
} as any
|
|
281
301
|
|
|
282
302
|
if (
|
|
283
303
|
!process.stdin.isTTY &&
|
|
@@ -484,7 +504,7 @@ ${commandList}`,
|
|
|
484
504
|
.action(async ({ cwd, global }) => {
|
|
485
505
|
await setup(cwd, false)
|
|
486
506
|
console.log(
|
|
487
|
-
JSON.stringify(listConfigForCLI(
|
|
507
|
+
JSON.stringify(listConfigForCLI(global ? (true as const) : (false as const)), null, 2),
|
|
488
508
|
)
|
|
489
509
|
process.exit(0)
|
|
490
510
|
})
|