mu-coding 0.13.0 → 0.16.1
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 +9 -123
- package/bin/coding-agent.ts +95 -0
- package/package.json +10 -21
- package/src/config.ts +122 -0
- package/src/harness.test.ts +159 -0
- package/src/main.ts +53 -3
- package/src/plugins.ts +49 -0
- package/src/systemPrompt.ts +22 -0
- package/src/ui/ChatApp.ts +959 -0
- package/src/ui/commands.ts +35 -0
- package/src/ui/editor.ts +166 -0
- package/src/ui/markdown.ts +363 -0
- package/src/ui/picker.ts +126 -0
- package/src/ui/status.ts +61 -0
- package/src/ui/theme.ts +241 -0
- package/src/ui/transcript.test.ts +121 -0
- package/src/ui/transcript.ts +399 -0
- package/tsconfig.json +8 -0
- package/bin/mu.js +0 -2
- package/prompts/SYSTEM.md +0 -16
- package/src/app/shutdown.ts +0 -94
- package/src/app/startApp.ts +0 -49
- package/src/cli/args.ts +0 -133
- package/src/cli/install.ts +0 -107
- package/src/cli/subcommands.ts +0 -29
- package/src/cli/update.ts +0 -205
- package/src/config/index.test.ts +0 -77
- package/src/config/index.ts +0 -199
- package/src/plugin.ts +0 -124
- package/src/runtime/codingTools/bash.ts +0 -114
- package/src/runtime/codingTools/edit-file.ts +0 -60
- package/src/runtime/codingTools/index.ts +0 -39
- package/src/runtime/codingTools/read-file.ts +0 -83
- package/src/runtime/codingTools/utils.ts +0 -21
- package/src/runtime/codingTools/write-file.ts +0 -42
- package/src/runtime/createRegistry.test.ts +0 -147
- package/src/runtime/createRegistry.ts +0 -195
- package/src/runtime/fileMentionProvider.ts +0 -117
- package/src/runtime/messageBus.test.ts +0 -62
- package/src/runtime/messageBus.ts +0 -78
- package/src/runtime/pluginLoader.ts +0 -153
- package/src/runtime/startupUpdateCheck.ts +0 -163
- package/src/runtime/updateCheck.ts +0 -136
- package/src/sessions/index.test.ts +0 -66
- package/src/sessions/index.ts +0 -183
- package/src/sessions/peek.test.ts +0 -88
- package/src/sessions/project.ts +0 -51
- package/src/tui/channel/tuiChannel.test.ts +0 -107
- package/src/tui/channel/tuiChannel.ts +0 -62
- package/src/tui/chat/ChatContext.ts +0 -10
- package/src/tui/chat/MessageRendererContext.ts +0 -44
- package/src/tui/chat/ToolDisplayContext.ts +0 -33
- package/src/tui/chat/useAbort.ts +0 -85
- package/src/tui/chat/useAttachment.ts +0 -74
- package/src/tui/chat/useChat.ts +0 -113
- package/src/tui/chat/useChatPanel.ts +0 -119
- package/src/tui/chat/useChatSession.ts +0 -382
- package/src/tui/chat/useModels.ts +0 -83
- package/src/tui/chat/usePluginStatus.ts +0 -44
- package/src/tui/chat/useSessionPersistence.ts +0 -84
- package/src/tui/chat/useStatusSegments.ts +0 -82
- package/src/tui/chat/useSubagentBrowser.ts +0 -133
- package/src/tui/components/chat/ChatPanel.tsx +0 -54
- package/src/tui/components/chat/ChatPanelBody.tsx +0 -86
- package/src/tui/components/chat/Pickers.tsx +0 -44
- package/src/tui/components/chat/SubagentBrowserPanel.tsx +0 -145
- package/src/tui/components/messageView.tsx +0 -72
- package/src/tui/components/messages/EditOutput.tsx +0 -112
- package/src/tui/components/messages/ReadOutput.tsx +0 -48
- package/src/tui/components/messages/ToolHeader.tsx +0 -30
- package/src/tui/components/messages/WebFetchOutput.tsx +0 -30
- package/src/tui/components/messages/WriteOutput.tsx +0 -64
- package/src/tui/components/messages/assistantMessage.tsx +0 -72
- package/src/tui/components/messages/markdown.tsx +0 -407
- package/src/tui/components/messages/messageItem.tsx +0 -43
- package/src/tui/components/messages/reasoningBlock.tsx +0 -18
- package/src/tui/components/messages/streamingOutput.tsx +0 -18
- package/src/tui/components/messages/toolCallBlock.tsx +0 -125
- package/src/tui/components/messages/userMessage.tsx +0 -44
- package/src/tui/components/primitives/dropdown.tsx +0 -125
- package/src/tui/components/primitives/modal.tsx +0 -47
- package/src/tui/components/primitives/pickerModal.tsx +0 -47
- package/src/tui/components/primitives/scrollbar.tsx +0 -27
- package/src/tui/components/primitives/toast.tsx +0 -100
- package/src/tui/components/statusBar.tsx +0 -41
- package/src/tui/components/ui/dialogLayer.tsx +0 -175
- package/src/tui/context/ThemeContext.tsx +0 -18
- package/src/tui/hooks/useChordKeyboard.ts +0 -87
- package/src/tui/hooks/useInputInfoSegments.ts +0 -22
- package/src/tui/hooks/useScroll.ts +0 -64
- package/src/tui/hooks/useTerminal.ts +0 -40
- package/src/tui/hooks/useUI.ts +0 -15
- package/src/tui/input/InputBox.tsx +0 -6
- package/src/tui/input/InputBoxView.tsx +0 -293
- package/src/tui/input/commands.test.ts +0 -71
- package/src/tui/input/commands.ts +0 -55
- package/src/tui/input/cursor.test.ts +0 -136
- package/src/tui/input/cursor.ts +0 -214
- package/src/tui/input/dumpContext.ts +0 -107
- package/src/tui/input/sanitize.ts +0 -33
- package/src/tui/input/useCommandExecutor.ts +0 -32
- package/src/tui/input/useInputBox.ts +0 -265
- package/src/tui/input/useInputHandler.ts +0 -455
- package/src/tui/input/useMentionPicker.ts +0 -133
- package/src/tui/input/usePluginShortcuts.ts +0 -29
- package/src/tui/plugins/InkApprovalChannel.test.ts +0 -51
- package/src/tui/plugins/InkApprovalChannel.ts +0 -30
- package/src/tui/plugins/InkUIService.ts +0 -188
- package/src/tui/renderApp.tsx +0 -64
- package/src/tui/theme/index.ts +0 -1
- package/src/tui/theme/merge.test.ts +0 -49
- package/src/tui/theme/merge.ts +0 -43
- package/src/tui/theme/presets.ts +0 -90
- package/src/tui/theme/types.ts +0 -138
- package/src/tui/update/runUpdateInTui.ts +0 -127
- package/src/utils/clipboard.ts +0 -97
- package/src/utils/diff.test.ts +0 -56
- package/src/utils/diff.ts +0 -81
package/src/utils/clipboard.ts
DELETED
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
import { execFileSync, execSync } from 'node:child_process';
|
|
2
|
-
import { existsSync, readFileSync, statSync, unlinkSync } from 'node:fs';
|
|
3
|
-
import { tmpdir } from 'node:os';
|
|
4
|
-
import { join } from 'node:path';
|
|
5
|
-
import type { ImageAttachment } from 'mu-core';
|
|
6
|
-
|
|
7
|
-
const CLIPBOARD_TIMEOUT = 3000;
|
|
8
|
-
|
|
9
|
-
function tryExecFile(file: string, args: string[]): boolean {
|
|
10
|
-
try {
|
|
11
|
-
execFileSync(file, args, { stdio: 'pipe', timeout: CLIPBOARD_TIMEOUT });
|
|
12
|
-
return true;
|
|
13
|
-
} catch {
|
|
14
|
-
return false;
|
|
15
|
-
}
|
|
16
|
-
}
|
|
17
|
-
|
|
18
|
-
function tryShell(command: string): boolean {
|
|
19
|
-
try {
|
|
20
|
-
execSync(command, { stdio: 'pipe', timeout: CLIPBOARD_TIMEOUT });
|
|
21
|
-
return true;
|
|
22
|
-
} catch {
|
|
23
|
-
return false;
|
|
24
|
-
}
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
function commandExists(name: string): boolean {
|
|
28
|
-
return tryExecFile('which', [name]);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
function extractMacImage(tmpFile: string): boolean {
|
|
32
|
-
if (commandExists('pngpaste')) {
|
|
33
|
-
return tryExecFile('pngpaste', [tmpFile]);
|
|
34
|
-
}
|
|
35
|
-
// Each `-e` is a discrete script line — passing as separate args avoids shell quoting traps.
|
|
36
|
-
return tryExecFile('osascript', [
|
|
37
|
-
'-e',
|
|
38
|
-
'tell application "System Events"',
|
|
39
|
-
'-e',
|
|
40
|
-
'set imgData to the clipboard as «class PNGf»',
|
|
41
|
-
'-e',
|
|
42
|
-
`set fp to open for access POSIX file "${tmpFile}" with write permission`,
|
|
43
|
-
'-e',
|
|
44
|
-
'write imgData to fp',
|
|
45
|
-
'-e',
|
|
46
|
-
'close access fp',
|
|
47
|
-
'-e',
|
|
48
|
-
'end tell',
|
|
49
|
-
]);
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
function extractLinuxImage(tmpFile: string): boolean {
|
|
53
|
-
// xclip / wl-paste write to stdout; redirection still requires a shell.
|
|
54
|
-
// tmpFile is constructed from tmpdir() + a numeric timestamp, so quoting is safe.
|
|
55
|
-
if (tryShell(`xclip -selection clipboard -t image/png -o > "${tmpFile}"`)) {
|
|
56
|
-
return true;
|
|
57
|
-
}
|
|
58
|
-
return tryShell(`wl-paste --type image/png > "${tmpFile}"`);
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function extractPlatformImage(tmpFile: string): boolean {
|
|
62
|
-
if (process.platform === 'darwin') {
|
|
63
|
-
return extractMacImage(tmpFile);
|
|
64
|
-
}
|
|
65
|
-
if (process.platform === 'linux') {
|
|
66
|
-
return extractLinuxImage(tmpFile);
|
|
67
|
-
}
|
|
68
|
-
return false;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
function readTmpAsAttachment(tmpFile: string): ImageAttachment | null {
|
|
72
|
-
if (!existsSync(tmpFile)) {
|
|
73
|
-
return null;
|
|
74
|
-
}
|
|
75
|
-
if (statSync(tmpFile).size === 0) {
|
|
76
|
-
unlinkSync(tmpFile);
|
|
77
|
-
return null;
|
|
78
|
-
}
|
|
79
|
-
const buffer = readFileSync(tmpFile);
|
|
80
|
-
unlinkSync(tmpFile);
|
|
81
|
-
return { data: buffer.toString('base64'), mimeType: 'image/png', name: 'clipboard.png' };
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
export function readClipboardImage(): ImageAttachment | null {
|
|
85
|
-
const tmpFile = join(tmpdir(), `mu-clip-${Date.now()}.png`);
|
|
86
|
-
try {
|
|
87
|
-
if (!extractPlatformImage(tmpFile)) {
|
|
88
|
-
return null;
|
|
89
|
-
}
|
|
90
|
-
return readTmpAsAttachment(tmpFile);
|
|
91
|
-
} catch {
|
|
92
|
-
if (existsSync(tmpFile)) {
|
|
93
|
-
unlinkSync(tmpFile);
|
|
94
|
-
}
|
|
95
|
-
return null;
|
|
96
|
-
}
|
|
97
|
-
}
|
package/src/utils/diff.test.ts
DELETED
|
@@ -1,56 +0,0 @@
|
|
|
1
|
-
import { describe, expect, it } from 'bun:test';
|
|
2
|
-
import { computeDiff, renderDiff } from './diff';
|
|
3
|
-
|
|
4
|
-
describe('computeDiff', () => {
|
|
5
|
-
it('emits only context lines when texts are identical', () => {
|
|
6
|
-
const diff = computeDiff('a\nb\nc', 'a\nb\nc');
|
|
7
|
-
expect(diff.lines.every((l) => l.type === 'context')).toBe(true);
|
|
8
|
-
expect(diff.lines.find((l) => l.type === 'old')).toBeUndefined();
|
|
9
|
-
expect(diff.lines.find((l) => l.type === 'new')).toBeUndefined();
|
|
10
|
-
expect(diff.totalOldLines).toBe(3);
|
|
11
|
-
expect(diff.totalNewLines).toBe(3);
|
|
12
|
-
});
|
|
13
|
-
|
|
14
|
-
it('detects an added line in the middle', () => {
|
|
15
|
-
const diff = computeDiff('a\nb\nc', 'a\nx\nb\nc');
|
|
16
|
-
const types = diff.lines.map((l) => l.type);
|
|
17
|
-
expect(types).toContain('new');
|
|
18
|
-
expect(diff.lines.find((l) => l.type === 'new')?.value).toBe('x');
|
|
19
|
-
});
|
|
20
|
-
|
|
21
|
-
it('detects a removed line', () => {
|
|
22
|
-
const diff = computeDiff('a\nb\nc', 'a\nc');
|
|
23
|
-
expect(diff.lines.find((l) => l.type === 'old')?.value).toBe('b');
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
it('returns no lines when either side exceeds the size limit', () => {
|
|
27
|
-
const huge = Array.from({ length: 600 }, (_, i) => `line${i}`).join('\n');
|
|
28
|
-
const diff = computeDiff(huge, huge.replace('line0', 'lineX'));
|
|
29
|
-
expect(diff.lines).toEqual([]);
|
|
30
|
-
expect(diff.totalOldLines).toBe(600);
|
|
31
|
-
});
|
|
32
|
-
|
|
33
|
-
it('emits up to CONTEXT_LINES of leading/trailing context', () => {
|
|
34
|
-
const diff = computeDiff('a\nb\nc\nd\ne', 'a\nb\nc\nX\ne');
|
|
35
|
-
const contextValues = diff.lines.filter((l) => l.type === 'context').map((l) => l.value);
|
|
36
|
-
expect(contextValues).toContain('c');
|
|
37
|
-
expect(contextValues).toContain('e');
|
|
38
|
-
});
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
describe('renderDiff', () => {
|
|
42
|
-
it('prefixes added/removed/context lines and reports truncation', () => {
|
|
43
|
-
const diff = computeDiff('a\nb', 'a\nc');
|
|
44
|
-
const { lines, truncated } = renderDiff(diff, 100);
|
|
45
|
-
expect(lines.some((l) => l.startsWith('-'))).toBe(true);
|
|
46
|
-
expect(lines.some((l) => l.startsWith('+'))).toBe(true);
|
|
47
|
-
expect(truncated).toBe(false);
|
|
48
|
-
});
|
|
49
|
-
|
|
50
|
-
it('reports truncated when diff exceeds maxLines', () => {
|
|
51
|
-
const diff = computeDiff('a', Array.from({ length: 50 }, (_, i) => `n${i}`).join('\n'));
|
|
52
|
-
const { lines, truncated } = renderDiff(diff, 5);
|
|
53
|
-
expect(lines).toHaveLength(5);
|
|
54
|
-
expect(truncated).toBe(true);
|
|
55
|
-
});
|
|
56
|
-
});
|
package/src/utils/diff.ts
DELETED
|
@@ -1,81 +0,0 @@
|
|
|
1
|
-
// Lightweight diff for edit_file tool output.
|
|
2
|
-
// Uses prefix/suffix matching — sufficient for small, localized edits.
|
|
3
|
-
|
|
4
|
-
export interface DiffLine {
|
|
5
|
-
type: 'context' | 'old' | 'new';
|
|
6
|
-
value: string;
|
|
7
|
-
}
|
|
8
|
-
|
|
9
|
-
export interface DiffResult {
|
|
10
|
-
lines: DiffLine[];
|
|
11
|
-
totalOldLines: number;
|
|
12
|
-
totalNewLines: number;
|
|
13
|
-
}
|
|
14
|
-
|
|
15
|
-
const CONTEXT_LINES = 3;
|
|
16
|
-
const MAX_LINES = 500;
|
|
17
|
-
|
|
18
|
-
export function computeDiff(oldText: string, newText: string): DiffResult {
|
|
19
|
-
const oldLines = oldText.split('\n');
|
|
20
|
-
const newLines = newText.split('\n');
|
|
21
|
-
|
|
22
|
-
if (oldLines.length > MAX_LINES || newLines.length > MAX_LINES) {
|
|
23
|
-
return { lines: [], totalOldLines: oldLines.length, totalNewLines: newLines.length };
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
// Find common prefix
|
|
27
|
-
let prefixLen = 0;
|
|
28
|
-
const minLen = Math.min(oldLines.length, newLines.length);
|
|
29
|
-
while (prefixLen < minLen && oldLines[prefixLen] === newLines[prefixLen]) {
|
|
30
|
-
prefixLen++;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// Find common suffix (don't overlap with prefix)
|
|
34
|
-
let suffixLen = 0;
|
|
35
|
-
const maxSuffix = Math.min(oldLines.length - prefixLen, newLines.length - prefixLen);
|
|
36
|
-
while (
|
|
37
|
-
suffixLen < maxSuffix &&
|
|
38
|
-
oldLines[oldLines.length - 1 - suffixLen] === newLines[newLines.length - 1 - suffixLen]
|
|
39
|
-
) {
|
|
40
|
-
suffixLen++;
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
const result: DiffLine[] = [];
|
|
44
|
-
|
|
45
|
-
// Context from prefix (last N lines)
|
|
46
|
-
const ctxStart = Math.max(0, prefixLen - CONTEXT_LINES);
|
|
47
|
-
for (let i = ctxStart; i < prefixLen; i++) {
|
|
48
|
-
result.push({ type: 'context', value: oldLines[i] });
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Removed lines
|
|
52
|
-
for (let i = prefixLen; i < oldLines.length - suffixLen; i++) {
|
|
53
|
-
result.push({ type: 'old', value: oldLines[i] });
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// Added lines
|
|
57
|
-
for (let i = prefixLen; i < newLines.length - suffixLen; i++) {
|
|
58
|
-
result.push({ type: 'new', value: newLines[i] });
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
// Context from suffix (first N lines)
|
|
62
|
-
const ctxEnd = Math.min(suffixLen, CONTEXT_LINES);
|
|
63
|
-
for (let i = 0; i < ctxEnd; i++) {
|
|
64
|
-
const idx = oldLines.length - suffixLen + i;
|
|
65
|
-
result.push({ type: 'context', value: oldLines[idx] });
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
return { lines: result, totalOldLines: oldLines.length, totalNewLines: newLines.length };
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
export function renderDiff(diff: DiffResult, maxLines: number): { lines: string[]; truncated: boolean } {
|
|
72
|
-
const result: string[] = [];
|
|
73
|
-
const capped = diff.lines.slice(0, maxLines);
|
|
74
|
-
|
|
75
|
-
for (const line of capped) {
|
|
76
|
-
const prefix = line.type === 'old' ? '-' : line.type === 'new' ? '+' : ' ';
|
|
77
|
-
result.push(`${prefix} ${line.value}`);
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
return { lines: result, truncated: diff.lines.length > maxLines };
|
|
81
|
-
}
|