mu-coding 0.2.0 → 0.5.0
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 +0 -2
- package/bin/mu.js +1 -1
- package/package.json +12 -4
- package/src/app/shutdown.ts +94 -0
- package/src/app/startApp.ts +40 -0
- package/src/cli/args.ts +128 -0
- package/src/{install.ts → cli/install.ts} +19 -15
- package/src/config/index.test.ts +51 -0
- package/src/config/index.ts +181 -0
- package/src/main.ts +4 -0
- package/src/runtime/createRegistry.ts +58 -0
- package/src/runtime/pluginLoader.ts +109 -0
- package/src/sessions/index.test.ts +66 -0
- package/src/sessions/index.ts +190 -0
- package/src/sessions/peek.test.ts +88 -0
- package/src/sessions/project.ts +51 -0
- package/src/tui/{context/chat.ts → chat/ChatContext.ts} +1 -1
- package/src/tui/chat/ToolDisplayContext.ts +33 -0
- package/src/tui/{useAbort.ts → chat/useAbort.ts} +16 -7
- package/src/tui/chat/useAttachment.ts +74 -0
- package/src/tui/{useChat.ts → chat/useChat.ts} +32 -6
- package/src/tui/chat/useChatPanel.ts +96 -0
- package/src/tui/chat/useChatSession.ts +115 -0
- package/src/tui/{useModelList.ts → chat/useModels.ts} +10 -1
- package/src/tui/chat/usePluginStatus.ts +44 -0
- package/src/tui/chat/useSessionPersistence.ts +57 -0
- package/src/tui/chat/useStatusSegments.ts +49 -0
- package/src/tui/chat/useStreamConsumer.ts +118 -0
- package/src/tui/components/chat/ChatPanel.tsx +12 -38
- package/src/tui/components/chat/ChatPanelBody.tsx +30 -52
- package/src/tui/components/chat/Pickers.tsx +2 -2
- package/src/tui/components/messageView.tsx +70 -0
- package/src/tui/components/messages/EditOutput.tsx +42 -27
- package/src/tui/components/messages/ReadOutput.tsx +27 -22
- package/src/tui/components/messages/ToolHeader.tsx +26 -0
- package/src/tui/components/messages/WriteOutput.tsx +12 -24
- package/src/tui/components/messages/messageItem.tsx +4 -15
- package/src/tui/components/messages/toolCallBlock.tsx +56 -34
- package/src/tui/components/{ui → primitives}/dropdown.tsx +32 -7
- package/src/tui/components/primitives/pickerModal.tsx +45 -0
- package/src/tui/components/primitives/scrollbar.tsx +27 -0
- package/src/tui/components/statusBar.tsx +25 -0
- package/src/tui/components/ui/dialogLayer.tsx +21 -7
- package/src/tui/hooks/useScroll.ts +11 -3
- package/src/tui/input/InputBox.tsx +6 -0
- package/src/tui/{components/inputBox.tsx → input/InputBoxView.tsx} +24 -49
- package/src/tui/input/commands.test.ts +49 -0
- package/src/tui/input/commands.ts +39 -0
- package/src/tui/input/sanitize.ts +33 -0
- package/src/tui/input/useCommandExecutor.ts +32 -0
- package/src/tui/input/useInputBox.ts +88 -0
- package/src/tui/{hooks → input}/useInputHandler.ts +21 -26
- package/src/tui/{services/uiService.ts → plugins/InkUIService.ts} +68 -35
- package/src/tui/renderApp.tsx +30 -0
- package/src/utils/clipboard.ts +97 -0
- package/src/utils/diff.test.ts +56 -0
- package/src/cli.ts +0 -92
- package/src/clipboard.ts +0 -62
- package/src/config.ts +0 -116
- package/src/main.tsx +0 -161
- package/src/project.ts +0 -32
- package/src/session.ts +0 -95
- package/src/singleShot.ts +0 -42
- package/src/tui/commands.ts +0 -33
- package/src/tui/components/chatLayout.tsx +0 -192
- package/src/tui/useChatSession.ts +0 -155
- package/src/tui/useChatUI.ts +0 -51
- package/tsconfig.json +0 -10
- /package/src/{subcommands.ts → cli/subcommands.ts} +0 -0
- /package/src/tui/components/{ui → primitives}/modal.tsx +0 -0
- /package/src/tui/components/{ui → primitives}/toast.tsx +0 -0
- /package/src/{diff.ts → utils/diff.ts} +0 -0
|
@@ -1,155 +0,0 @@
|
|
|
1
|
-
import { type AgentEvent, type PluginRegistry, runAgent } from 'mu-agents';
|
|
2
|
-
import type { ChatMessage, ProviderConfig } from 'mu-provider';
|
|
3
|
-
import { useCallback, useRef, useState } from 'react';
|
|
4
|
-
import { generateSessionPath, loadSession, saveSession } from '../session';
|
|
5
|
-
import type { AttachmentState } from './useChatUI';
|
|
6
|
-
|
|
7
|
-
export interface StreamState {
|
|
8
|
-
text: string;
|
|
9
|
-
reasoning: string;
|
|
10
|
-
totalTokens: number;
|
|
11
|
-
tps: number;
|
|
12
|
-
}
|
|
13
|
-
|
|
14
|
-
const EMPTY_STREAM: StreamState = { text: '', reasoning: '', totalTokens: 0, tps: 0 };
|
|
15
|
-
|
|
16
|
-
export interface ChatSessionState {
|
|
17
|
-
messages: ChatMessage[];
|
|
18
|
-
streaming: boolean;
|
|
19
|
-
error: string | null;
|
|
20
|
-
stream: StreamState;
|
|
21
|
-
inputHistory: string[];
|
|
22
|
-
onSend: (text: string) => Promise<void>;
|
|
23
|
-
onNew: () => void;
|
|
24
|
-
onLoadSession: (path: string) => void;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
interface SessionDeps {
|
|
28
|
-
config: ProviderConfig;
|
|
29
|
-
currentModel: string;
|
|
30
|
-
attachment: AttachmentState;
|
|
31
|
-
controllerRef: React.RefObject<AbortController | null>;
|
|
32
|
-
initialMessages?: ChatMessage[];
|
|
33
|
-
registry: PluginRegistry;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function applyEvent(prev: StreamState, event: AgentEvent, tps: number): StreamState {
|
|
37
|
-
switch (event.type) {
|
|
38
|
-
case 'content':
|
|
39
|
-
return { ...prev, text: event.text, tps };
|
|
40
|
-
case 'reasoning':
|
|
41
|
-
return { ...prev, reasoning: event.text, tps };
|
|
42
|
-
case 'usage':
|
|
43
|
-
return { ...prev, totalTokens: prev.totalTokens + event.totalTokens };
|
|
44
|
-
case 'turn_end':
|
|
45
|
-
return { ...prev, text: '', reasoning: '' };
|
|
46
|
-
default:
|
|
47
|
-
return prev;
|
|
48
|
-
}
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
async function consumeAgent(
|
|
52
|
-
events: AsyncGenerator<AgentEvent>,
|
|
53
|
-
onStream: (updater: (prev: StreamState) => StreamState) => void,
|
|
54
|
-
onMessages: (messages: ChatMessage[]) => void,
|
|
55
|
-
): Promise<ChatMessage[] | null> {
|
|
56
|
-
let final: ChatMessage[] | null = null;
|
|
57
|
-
const start = Date.now();
|
|
58
|
-
let tokenCount = 0;
|
|
59
|
-
|
|
60
|
-
for await (const event of events) {
|
|
61
|
-
if (event.type === 'content' || event.type === 'reasoning') {
|
|
62
|
-
tokenCount++;
|
|
63
|
-
const elapsed = (Date.now() - start) / 1000;
|
|
64
|
-
const tps = elapsed > 0.5 ? Math.round(tokenCount / elapsed) : 0;
|
|
65
|
-
onStream((prev) => applyEvent(prev, event, tps));
|
|
66
|
-
} else if (event.type === 'messages') {
|
|
67
|
-
final = event.messages;
|
|
68
|
-
onMessages(event.messages);
|
|
69
|
-
} else {
|
|
70
|
-
onStream((prev) => applyEvent(prev, event, 0));
|
|
71
|
-
}
|
|
72
|
-
}
|
|
73
|
-
return final;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export function useChatSession(deps: SessionDeps): ChatSessionState {
|
|
77
|
-
const { config, currentModel, attachment, controllerRef, initialMessages, registry } = deps;
|
|
78
|
-
const [messages, setMessages] = useState<ChatMessage[]>(initialMessages ?? []);
|
|
79
|
-
const [streaming, setStreaming] = useState(false);
|
|
80
|
-
const [error, setError] = useState<string | null>(null);
|
|
81
|
-
const [stream, setStream] = useState<StreamState>(EMPTY_STREAM);
|
|
82
|
-
const [inputHistory, setInputHistory] = useState<string[]>(
|
|
83
|
-
initialMessages?.filter((m) => m.role === 'user').map((m) => m.content) ?? [],
|
|
84
|
-
);
|
|
85
|
-
const sessionPathRef = useRef(generateSessionPath());
|
|
86
|
-
|
|
87
|
-
const reset = useCallback(() => {
|
|
88
|
-
setStream(EMPTY_STREAM);
|
|
89
|
-
setError(null);
|
|
90
|
-
}, []);
|
|
91
|
-
|
|
92
|
-
const onSend = useCallback(
|
|
93
|
-
async (text: string) => {
|
|
94
|
-
if (streaming) {
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
const userMsg: ChatMessage = {
|
|
98
|
-
role: 'user',
|
|
99
|
-
content: text,
|
|
100
|
-
...(attachment.attachment ? { images: [attachment.attachment] } : {}),
|
|
101
|
-
};
|
|
102
|
-
setMessages((prev) => [...prev, userMsg]);
|
|
103
|
-
setInputHistory((prev) => [...prev, text]);
|
|
104
|
-
reset();
|
|
105
|
-
setStreaming(true);
|
|
106
|
-
attachment.clear();
|
|
107
|
-
|
|
108
|
-
const controller = new AbortController();
|
|
109
|
-
controllerRef.current = controller;
|
|
110
|
-
|
|
111
|
-
try {
|
|
112
|
-
const final = await consumeAgent(
|
|
113
|
-
runAgent([...messages, userMsg], config, currentModel, controller.signal, registry),
|
|
114
|
-
setStream,
|
|
115
|
-
setMessages,
|
|
116
|
-
);
|
|
117
|
-
if (final) {
|
|
118
|
-
saveSession(sessionPathRef.current, final);
|
|
119
|
-
}
|
|
120
|
-
} catch (err) {
|
|
121
|
-
if (!(err instanceof Error && err.name === 'AbortError')) {
|
|
122
|
-
setError(err instanceof Error ? err.message : 'Unknown error');
|
|
123
|
-
}
|
|
124
|
-
} finally {
|
|
125
|
-
setStreaming(false);
|
|
126
|
-
controllerRef.current = null;
|
|
127
|
-
if (!controller.signal.aborted) {
|
|
128
|
-
setStream((s) => ({ ...s, text: '', reasoning: '' }));
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
},
|
|
132
|
-
[streaming, messages, config, currentModel, attachment, controllerRef, reset, registry],
|
|
133
|
-
);
|
|
134
|
-
|
|
135
|
-
const onNew = useCallback(() => {
|
|
136
|
-
setMessages([]);
|
|
137
|
-
reset();
|
|
138
|
-
sessionPathRef.current = generateSessionPath();
|
|
139
|
-
attachment.clear();
|
|
140
|
-
}, [attachment, reset]);
|
|
141
|
-
|
|
142
|
-
const onLoadSession = useCallback(
|
|
143
|
-
(path: string) => {
|
|
144
|
-
const msgs = loadSession(path);
|
|
145
|
-
if (msgs.length > 0) {
|
|
146
|
-
setMessages(msgs);
|
|
147
|
-
sessionPathRef.current = path;
|
|
148
|
-
reset();
|
|
149
|
-
}
|
|
150
|
-
},
|
|
151
|
-
[reset],
|
|
152
|
-
);
|
|
153
|
-
|
|
154
|
-
return { messages, streaming, error, stream, inputHistory, onSend, onNew, onLoadSession };
|
|
155
|
-
}
|
package/src/tui/useChatUI.ts
DELETED
|
@@ -1,51 +0,0 @@
|
|
|
1
|
-
import type { ImageAttachment } from 'mu-provider';
|
|
2
|
-
import { useCallback, useState } from 'react';
|
|
3
|
-
import { readClipboardImage } from '../clipboard';
|
|
4
|
-
|
|
5
|
-
export interface AttachmentState {
|
|
6
|
-
attachment: ImageAttachment | null;
|
|
7
|
-
attachmentError: string | null;
|
|
8
|
-
onPaste: () => void;
|
|
9
|
-
clear: () => void;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
export function useAttachment(): AttachmentState {
|
|
13
|
-
const [attachment, setAttachment] = useState<ImageAttachment | null>(null);
|
|
14
|
-
const [attachmentError, setAttachmentError] = useState<string | null>(null);
|
|
15
|
-
|
|
16
|
-
const onPaste = useCallback(() => {
|
|
17
|
-
const img = readClipboardImage();
|
|
18
|
-
if (img) {
|
|
19
|
-
setAttachment(img);
|
|
20
|
-
setAttachmentError(null);
|
|
21
|
-
return;
|
|
22
|
-
}
|
|
23
|
-
setAttachmentError('No image on clipboard');
|
|
24
|
-
setTimeout(() => setAttachmentError(null), 3000);
|
|
25
|
-
}, []);
|
|
26
|
-
|
|
27
|
-
const clear = useCallback(() => {
|
|
28
|
-
setAttachment(null);
|
|
29
|
-
setAttachmentError(null);
|
|
30
|
-
}, []);
|
|
31
|
-
|
|
32
|
-
return { attachment, attachmentError, onPaste, clear };
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export interface TogglesState {
|
|
36
|
-
showModelPicker: boolean;
|
|
37
|
-
showSessionPicker: boolean;
|
|
38
|
-
onTogglePicker: () => void;
|
|
39
|
-
onToggleSessionPicker: () => void;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export function useToggles(): TogglesState {
|
|
43
|
-
const [showModelPicker, setShowModelPicker] = useState(false);
|
|
44
|
-
const [showSessionPicker, setShowSessionPicker] = useState(false);
|
|
45
|
-
return {
|
|
46
|
-
showModelPicker,
|
|
47
|
-
showSessionPicker,
|
|
48
|
-
onTogglePicker: useCallback(() => setShowModelPicker((p) => !p), []),
|
|
49
|
-
onToggleSessionPicker: useCallback(() => setShowSessionPicker((p) => !p), []),
|
|
50
|
-
};
|
|
51
|
-
}
|
package/tsconfig.json
DELETED
|
@@ -1,10 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"extends": "../../tsconfig.json",
|
|
3
|
-
"compilerOptions": {
|
|
4
|
-
"outDir": "dist",
|
|
5
|
-
"rootDir": "src"
|
|
6
|
-
},
|
|
7
|
-
"include": ["src/**/*"],
|
|
8
|
-
"exclude": ["node_modules", "dist"],
|
|
9
|
-
"references": [{ "path": "../mu-provider" }, { "path": "../mu-agents" }, { "path": "../mu-repomap" }]
|
|
10
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|