mulby-cli 1.1.5
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/PLUGIN_DEVELOP_PROMPT.md +1164 -0
- package/README.md +852 -0
- package/assets/default-icon.png +0 -0
- package/dist/commands/ai-session.js +44 -0
- package/dist/commands/build.js +111 -0
- package/dist/commands/config-ai.js +291 -0
- package/dist/commands/config.js +53 -0
- package/dist/commands/create/ai-create.js +183 -0
- package/dist/commands/create/assets.js +53 -0
- package/dist/commands/create/basic.js +72 -0
- package/dist/commands/create/index.js +73 -0
- package/dist/commands/create/react.js +136 -0
- package/dist/commands/create/templates/basic.js +383 -0
- package/dist/commands/create/templates/react/backend.js +72 -0
- package/dist/commands/create/templates/react/config.js +166 -0
- package/dist/commands/create/templates/react/docs.js +78 -0
- package/dist/commands/create/templates/react/hooks.js +469 -0
- package/dist/commands/create/templates/react/index.js +41 -0
- package/dist/commands/create/templates/react/types.js +1228 -0
- package/dist/commands/create/templates/react/ui.js +528 -0
- package/dist/commands/create/templates/react.js +1888 -0
- package/dist/commands/dev.js +141 -0
- package/dist/commands/pack.js +160 -0
- package/dist/commands/resume.js +97 -0
- package/dist/commands/test-ui.js +50 -0
- package/dist/index.js +71 -0
- package/dist/services/ai/PLUGIN_API.md +1102 -0
- package/dist/services/ai/PLUGIN_DEVELOP_PROMPT.md +1164 -0
- package/dist/services/ai/context-manager.js +639 -0
- package/dist/services/ai/index.js +88 -0
- package/dist/services/ai/knowledge.js +52 -0
- package/dist/services/ai/prompts.js +114 -0
- package/dist/services/ai/providers/base.js +38 -0
- package/dist/services/ai/providers/claude.js +284 -0
- package/dist/services/ai/providers/deepseek.js +28 -0
- package/dist/services/ai/providers/gemini.js +191 -0
- package/dist/services/ai/providers/glm.js +31 -0
- package/dist/services/ai/providers/minimax.js +27 -0
- package/dist/services/ai/providers/openai.js +177 -0
- package/dist/services/ai/tools.js +204 -0
- package/dist/services/ai-generator.js +968 -0
- package/dist/services/config-manager.js +117 -0
- package/dist/services/dependency-manager.js +236 -0
- package/dist/services/file-writer.js +66 -0
- package/dist/services/plan-adapter.js +244 -0
- package/dist/services/plan-command-handler.js +172 -0
- package/dist/services/plan-manager.js +502 -0
- package/dist/services/session-manager.js +113 -0
- package/dist/services/task-analyzer.js +136 -0
- package/dist/services/tui/index.js +57 -0
- package/dist/services/tui/store.js +123 -0
- package/dist/types/ai.js +172 -0
- package/dist/types/plan.js +2 -0
- package/dist/ui/Terminal.js +56 -0
- package/dist/ui/components/InputArea.js +176 -0
- package/dist/ui/components/LogArea.js +19 -0
- package/dist/ui/components/PlanPanel.js +69 -0
- package/dist/ui/components/SelectArea.js +13 -0
- package/package.json +45 -0
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.InputArea = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
// @ts-nocheck
|
|
6
|
+
const react_1 = require("react");
|
|
7
|
+
const ink_1 = require("ink");
|
|
8
|
+
const SLASH_COMMANDS = [
|
|
9
|
+
{ cmd: '/help', desc: 'show help' },
|
|
10
|
+
{ cmd: '/exit', desc: 'exit session' },
|
|
11
|
+
{ cmd: '/clear', desc: 'clear context' },
|
|
12
|
+
{ cmd: '/tokens', desc: 'show token usage' },
|
|
13
|
+
{ cmd: '/compress', desc: 'compress history' },
|
|
14
|
+
{ cmd: '/use', desc: 'switch provider' },
|
|
15
|
+
{ cmd: '/model', desc: 'switch model' },
|
|
16
|
+
{ cmd: '/plan', desc: 'show plan or force plan mode' },
|
|
17
|
+
];
|
|
18
|
+
const InputArea = ({ isPrompting, statusMessage, onSubmit }) => {
|
|
19
|
+
const [query, setQuery] = (0, react_1.useState)('');
|
|
20
|
+
const [cursorPos, setCursorPos] = (0, react_1.useState)(0);
|
|
21
|
+
const [matchingCmds, setMatchingCmds] = (0, react_1.useState)([]);
|
|
22
|
+
const [selectedCmdIndex, setSelectedCmdIndex] = (0, react_1.useState)(-1);
|
|
23
|
+
(0, ink_1.useInput)((input, key) => {
|
|
24
|
+
if (!isPrompting)
|
|
25
|
+
return;
|
|
26
|
+
// Normalize input newlines (Paste handling)
|
|
27
|
+
// Some pastes use \r, some \r\n. Convert all to \n.
|
|
28
|
+
const normalizedInput = input.replace(/\r\n/g, '\n').replace(/\r/g, '\n');
|
|
29
|
+
// 1. Handle Shift+Enter for newline (cross-platform support)
|
|
30
|
+
// Different terminals handle Shift+Enter differently:
|
|
31
|
+
// - Standard: key.return && key.shift
|
|
32
|
+
// - Mac/Linux terminals: Ctrl+J (ASCII 106, character 'j')
|
|
33
|
+
// - Some terminals: Direct '\n' without key.return flag
|
|
34
|
+
if ((key.return && key.shift) || // Standard Shift+Enter
|
|
35
|
+
(key.ctrl && input === 'j') || // Mac/Linux: Ctrl+J
|
|
36
|
+
(normalizedInput === '\n' && !key.return) // Direct newline character
|
|
37
|
+
) {
|
|
38
|
+
setQuery(prev => {
|
|
39
|
+
const next = prev.slice(0, cursorPos) + '\n' + prev.slice(cursorPos);
|
|
40
|
+
setCursorPos(cursorPos + 1);
|
|
41
|
+
return next;
|
|
42
|
+
});
|
|
43
|
+
return;
|
|
44
|
+
}
|
|
45
|
+
// 2. Handle explicit Enter key (Submit or autocomplete)
|
|
46
|
+
if (key.return) {
|
|
47
|
+
// If there are matching commands and one is selected, autocomplete it
|
|
48
|
+
if (matchingCmds.length > 0 && selectedCmdIndex >= 0) {
|
|
49
|
+
const selectedCmd = matchingCmds[selectedCmdIndex].cmd;
|
|
50
|
+
setQuery(selectedCmd);
|
|
51
|
+
setCursorPos(selectedCmd.length);
|
|
52
|
+
setMatchingCmds([]);
|
|
53
|
+
setSelectedCmdIndex(-1);
|
|
54
|
+
return;
|
|
55
|
+
}
|
|
56
|
+
// Check for continuation char '\'
|
|
57
|
+
if (query.trimEnd().endsWith('\\')) {
|
|
58
|
+
const lastSlash = query.lastIndexOf('\\');
|
|
59
|
+
if (lastSlash !== -1) {
|
|
60
|
+
const next = query.substring(0, lastSlash) + '\n';
|
|
61
|
+
setQuery(next);
|
|
62
|
+
setCursorPos(next.length);
|
|
63
|
+
}
|
|
64
|
+
return;
|
|
65
|
+
}
|
|
66
|
+
// Normal Submit
|
|
67
|
+
if (!query.trim())
|
|
68
|
+
return;
|
|
69
|
+
// Submit
|
|
70
|
+
const payload = query;
|
|
71
|
+
setQuery('');
|
|
72
|
+
setCursorPos(0);
|
|
73
|
+
setMatchingCmds([]);
|
|
74
|
+
setSelectedCmdIndex(-1);
|
|
75
|
+
onSubmit(payload);
|
|
76
|
+
return;
|
|
77
|
+
}
|
|
78
|
+
// 3. Handle Arrow Keys for cursor movement or command selection
|
|
79
|
+
if (key.leftArrow) {
|
|
80
|
+
setCursorPos(prev => Math.max(0, prev - 1));
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
if (key.rightArrow) {
|
|
84
|
+
setCursorPos(prev => Math.min(query.length, prev + 1));
|
|
85
|
+
return;
|
|
86
|
+
}
|
|
87
|
+
if (key.upArrow) {
|
|
88
|
+
// If there are matching commands, navigate the command list
|
|
89
|
+
if (matchingCmds.length > 0) {
|
|
90
|
+
setSelectedCmdIndex(prev => {
|
|
91
|
+
if (prev <= 0)
|
|
92
|
+
return matchingCmds.length - 1;
|
|
93
|
+
return prev - 1;
|
|
94
|
+
});
|
|
95
|
+
return;
|
|
96
|
+
}
|
|
97
|
+
// Otherwise, move cursor up one line
|
|
98
|
+
const lines = query.slice(0, cursorPos).split('\n');
|
|
99
|
+
if (lines.length > 1) {
|
|
100
|
+
const currentLinePos = lines[lines.length - 1].length;
|
|
101
|
+
const prevLineLength = lines[lines.length - 2].length;
|
|
102
|
+
const newPos = cursorPos - currentLinePos - 1 - (prevLineLength - Math.min(currentLinePos, prevLineLength));
|
|
103
|
+
setCursorPos(Math.max(0, newPos));
|
|
104
|
+
}
|
|
105
|
+
return;
|
|
106
|
+
}
|
|
107
|
+
if (key.downArrow) {
|
|
108
|
+
// If there are matching commands, navigate the command list
|
|
109
|
+
if (matchingCmds.length > 0) {
|
|
110
|
+
setSelectedCmdIndex(prev => {
|
|
111
|
+
if (prev >= matchingCmds.length - 1)
|
|
112
|
+
return 0;
|
|
113
|
+
return prev + 1;
|
|
114
|
+
});
|
|
115
|
+
return;
|
|
116
|
+
}
|
|
117
|
+
// Otherwise, move cursor down one line
|
|
118
|
+
const beforeCursor = query.slice(0, cursorPos);
|
|
119
|
+
const afterCursor = query.slice(cursorPos);
|
|
120
|
+
const currentLinePos = beforeCursor.split('\n').pop()?.length || 0;
|
|
121
|
+
const nextLineEnd = afterCursor.indexOf('\n');
|
|
122
|
+
if (nextLineEnd !== -1) {
|
|
123
|
+
const nextLineLength = afterCursor.slice(0, nextLineEnd).length;
|
|
124
|
+
const newPos = cursorPos + nextLineLength + 1 - currentLinePos + Math.min(currentLinePos, nextLineLength);
|
|
125
|
+
setCursorPos(Math.min(query.length, newPos));
|
|
126
|
+
}
|
|
127
|
+
else if (afterCursor.length > 0) {
|
|
128
|
+
// Move to end if on last line
|
|
129
|
+
setCursorPos(query.length);
|
|
130
|
+
}
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
// 4. Handle Backspace / Delete
|
|
134
|
+
if (key.backspace || key.delete) {
|
|
135
|
+
if (cursorPos > 0) {
|
|
136
|
+
setQuery(prev => prev.slice(0, cursorPos - 1) + prev.slice(cursorPos));
|
|
137
|
+
setCursorPos(prev => prev - 1);
|
|
138
|
+
}
|
|
139
|
+
return;
|
|
140
|
+
}
|
|
141
|
+
// 5. Handle Regular Input & Paste
|
|
142
|
+
setQuery(prev => {
|
|
143
|
+
const next = prev.slice(0, cursorPos) + normalizedInput + prev.slice(cursorPos);
|
|
144
|
+
setCursorPos(cursorPos + normalizedInput.length);
|
|
145
|
+
// Check slash commands (only single line)
|
|
146
|
+
if (next.startsWith('/') && !next.includes('\n')) {
|
|
147
|
+
const matches = SLASH_COMMANDS.filter(c => c.cmd.startsWith(next));
|
|
148
|
+
setMatchingCmds(matches);
|
|
149
|
+
// Auto-select first command when list appears
|
|
150
|
+
setSelectedCmdIndex(matches.length > 0 ? 0 : -1);
|
|
151
|
+
}
|
|
152
|
+
else {
|
|
153
|
+
setMatchingCmds([]);
|
|
154
|
+
setSelectedCmdIndex(-1);
|
|
155
|
+
}
|
|
156
|
+
return next;
|
|
157
|
+
});
|
|
158
|
+
});
|
|
159
|
+
if (!isPrompting) {
|
|
160
|
+
return ((0, jsx_runtime_1.jsx)(ink_1.Box, { borderStyle: "round", borderColor: "gray", width: "100%", children: (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "yellow", children: statusMessage || 'Waiting... (System processing)' }) }));
|
|
161
|
+
}
|
|
162
|
+
// Advanced Stats Calculation
|
|
163
|
+
const getStats = (str) => {
|
|
164
|
+
// Ensure we count lines correctly even if split result differs
|
|
165
|
+
const lines = str.split('\n');
|
|
166
|
+
return { lines: lines.length, chars: str.length };
|
|
167
|
+
};
|
|
168
|
+
const { lines, chars } = getStats(query);
|
|
169
|
+
// Only collapse if content is significantly large, allowing manual multi-line entry to be visible.
|
|
170
|
+
// Threshold: > 6 lines or > 1000 chars.
|
|
171
|
+
const shouldCollapse = lines > 6 || chars > 1000;
|
|
172
|
+
return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", width: "100%", children: [matchingCmds.length > 0 && ((0, jsx_runtime_1.jsx)(ink_1.Box, { flexDirection: "column", paddingLeft: 2, borderStyle: "single", borderColor: "blue", width: "100%", children: matchingCmds.map((c, index) => ((0, jsx_runtime_1.jsxs)(ink_1.Text, { color: index === selectedCmdIndex ? "cyan" : "blue", inverse: index === selectedCmdIndex, children: [c.cmd, " ", (0, jsx_runtime_1.jsxs)(ink_1.Text, { color: index === selectedCmdIndex ? "white" : "gray", children: ["- ", c.desc] })] }, c.cmd))) })), (0, jsx_runtime_1.jsxs)(ink_1.Box, { borderStyle: "round", borderColor: shouldCollapse ? "magenta" : "cyan", flexDirection: "column", width: "100%", children: [(0, jsx_runtime_1.jsxs)(ink_1.Box, { children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: shouldCollapse ? "magenta" : "green", children: "\u279C " }), shouldCollapse ? ((0, jsx_runtime_1.jsxs)(ink_1.Text, { color: "magenta", italic: true, children: ["[Multi-line Input: ", lines, " lines, ", chars, " chars]"] })) : ((0, jsx_runtime_1.jsxs)(ink_1.Text, { children: [cursorPos > 0 && query.slice(0, cursorPos), (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "cyan", inverse: true, children: query[cursorPos] || ' ' }), cursorPos < query.length && query.slice(cursorPos + 1)] }))] }), (0, jsx_runtime_1.jsx)(ink_1.Box, { marginTop: 0, children: (0, jsx_runtime_1.jsx)(ink_1.Text, { color: "gray", dimColor: true, children: shouldCollapse
|
|
173
|
+
? '[Enter] submit • [Backsp] delete'
|
|
174
|
+
: '[Enter] send • [Shift+Enter] newline • [\\ + Enter] newline • [/] cmd • [←→↑↓] move' }) })] })] }));
|
|
175
|
+
};
|
|
176
|
+
exports.InputArea = InputArea;
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.LogArea = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const ink_1 = require("ink");
|
|
6
|
+
const LogArea = ({ logs }) => {
|
|
7
|
+
// We only show the last 15 lines to avoid clutter,
|
|
8
|
+
// though Ink handles scrolling naturally if we don't clear screen constantly.
|
|
9
|
+
// However, for a fixed bottom input, we want a "viewport" for logs.
|
|
10
|
+
// Simple implementation: Just render the logs.
|
|
11
|
+
// In a real TUI we might want a constrained Box with overflow control,
|
|
12
|
+
// but Ink's default behavior + process.stdout usually works fine for scrolling
|
|
13
|
+
// if we are rendering a full screen app or just appending.
|
|
14
|
+
// Since we are using Ink in a way that "takes over" the last N lines,
|
|
15
|
+
// we should render logs in a Box that grows.
|
|
16
|
+
const visibleLogs = logs.slice(-20); // Keep last 20 logs visible in the "window"
|
|
17
|
+
return ((0, jsx_runtime_1.jsx)(ink_1.Box, { flexDirection: "column", paddingBottom: 1, children: visibleLogs.map((log, index) => ((0, jsx_runtime_1.jsx)(ink_1.Text, { color: "white", children: log }, index))) }));
|
|
18
|
+
};
|
|
19
|
+
exports.LogArea = LogArea;
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.DetailedPlanView = exports.PlanPanel = void 0;
|
|
4
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
+
const ink_1 = require("ink");
|
|
6
|
+
/**
|
|
7
|
+
* Get status icon for task
|
|
8
|
+
*/
|
|
9
|
+
function getStatusIcon(status) {
|
|
10
|
+
switch (status) {
|
|
11
|
+
case 'completed':
|
|
12
|
+
return '✅';
|
|
13
|
+
case 'in_progress':
|
|
14
|
+
return '🔄';
|
|
15
|
+
case 'pending':
|
|
16
|
+
return '⏸️ ';
|
|
17
|
+
case 'failed':
|
|
18
|
+
return '❌';
|
|
19
|
+
case 'skipped':
|
|
20
|
+
return '⏭️ ';
|
|
21
|
+
default:
|
|
22
|
+
return ' ';
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Get priority color
|
|
27
|
+
*/
|
|
28
|
+
function getPriorityColor(priority) {
|
|
29
|
+
switch (priority) {
|
|
30
|
+
case 'high':
|
|
31
|
+
return 'red';
|
|
32
|
+
case 'medium':
|
|
33
|
+
return 'yellow';
|
|
34
|
+
case 'low':
|
|
35
|
+
return 'gray';
|
|
36
|
+
default:
|
|
37
|
+
return 'white';
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Plan Panel Component - displays task plan with collapsible view
|
|
42
|
+
*/
|
|
43
|
+
const PlanPanel = ({ plan, collapsed = false }) => {
|
|
44
|
+
if (!plan) {
|
|
45
|
+
return null;
|
|
46
|
+
}
|
|
47
|
+
const completedCount = plan.tasks.filter(t => t.status === 'completed').length;
|
|
48
|
+
const totalCount = plan.tasks.length;
|
|
49
|
+
const progress = totalCount > 0 ? Math.round((completedCount / totalCount) * 100) : 0;
|
|
50
|
+
// Collapsed view - just show summary
|
|
51
|
+
if (collapsed) {
|
|
52
|
+
const currentTask = plan.tasks.find(t => t.status === 'in_progress');
|
|
53
|
+
const currentTaskTitle = currentTask ? currentTask.title : 'Waiting...';
|
|
54
|
+
return ((0, jsx_runtime_1.jsx)(ink_1.Box, { borderStyle: "round", borderColor: "cyan", paddingX: 1, marginBottom: 1, children: (0, jsx_runtime_1.jsxs)(ink_1.Text, { color: "cyan", children: ["\uD83D\uDCCB Tasks: ", completedCount, "/", totalCount, " (", progress, "%) | \uD83D\uDD04 ", currentTaskTitle] }) }));
|
|
55
|
+
}
|
|
56
|
+
// Expanded view - show all tasks
|
|
57
|
+
return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", paddingX: 1, marginBottom: 1, children: [(0, jsx_runtime_1.jsx)(ink_1.Box, { marginBottom: 1, children: (0, jsx_runtime_1.jsxs)(ink_1.Text, { bold: true, color: "cyan", children: ["\uD83D\uDCCB Task Plan: ", plan.goal] }) }), (0, jsx_runtime_1.jsx)(ink_1.Box, { marginBottom: 1, children: (0, jsx_runtime_1.jsxs)(ink_1.Text, { color: "gray", children: ["Progress: ", completedCount, "/", totalCount, " (", progress, "%)"] }) }), (0, jsx_runtime_1.jsx)(ink_1.Box, { flexDirection: "column", children: plan.tasks.map((task, index) => {
|
|
58
|
+
const isCurrentTask = task.status === 'in_progress';
|
|
59
|
+
const statusIcon = getStatusIcon(task.status);
|
|
60
|
+
return ((0, jsx_runtime_1.jsx)(ink_1.Box, { marginBottom: 0, children: (0, jsx_runtime_1.jsxs)(ink_1.Text, { color: isCurrentTask ? 'cyan' : 'white', bold: isCurrentTask, children: [statusIcon, " ", index + 1, ". ", task.title, task.status === 'in_progress' && ' (current)'] }) }, task.id));
|
|
61
|
+
}) }), (0, jsx_runtime_1.jsx)(ink_1.Box, { marginTop: 1, children: (0, jsx_runtime_1.jsxs)(ink_1.Text, { color: "gray", dimColor: true, children: ["Status: ", plan.status, " | Updated: ", plan.updatedAt.toLocaleTimeString()] }) })] }));
|
|
62
|
+
};
|
|
63
|
+
exports.PlanPanel = PlanPanel;
|
|
64
|
+
const DetailedPlanView = ({ plan }) => {
|
|
65
|
+
const completedCount = plan.tasks.filter(t => t.status === 'completed').length;
|
|
66
|
+
const totalCount = plan.tasks.length;
|
|
67
|
+
return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", paddingX: 2, children: [(0, jsx_runtime_1.jsx)(ink_1.Box, { marginBottom: 1, children: (0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, color: "cyan", children: "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501" }) }), (0, jsx_runtime_1.jsx)(ink_1.Box, { marginBottom: 1, children: (0, jsx_runtime_1.jsxs)(ink_1.Text, { bold: true, color: "cyan", children: ["\uD83D\uDCCB Task Plan: ", plan.goal] }) }), (0, jsx_runtime_1.jsx)(ink_1.Box, { marginBottom: 1, children: (0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, color: "cyan", children: "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501" }) }), plan.tasks.map((task, index) => ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", marginBottom: 2, children: [(0, jsx_runtime_1.jsx)(ink_1.Box, { children: (0, jsx_runtime_1.jsxs)(ink_1.Text, { color: getPriorityColor(task.priority), children: [getStatusIcon(task.status), " ", index + 1, ". ", task.title] }) }), (0, jsx_runtime_1.jsx)(ink_1.Box, { paddingLeft: 4, children: (0, jsx_runtime_1.jsxs)(ink_1.Text, { color: "gray", children: ["Description: ", task.description] }) }), task.acceptanceCriteria.length > 0 && ((0, jsx_runtime_1.jsxs)(ink_1.Box, { paddingLeft: 4, flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: "gray", children: "Acceptance Criteria:" }), task.acceptanceCriteria.map((criterion, i) => ((0, jsx_runtime_1.jsx)(ink_1.Box, { paddingLeft: 2, children: (0, jsx_runtime_1.jsxs)(ink_1.Text, { color: "gray", children: ["\u2022 ", criterion] }) }, i)))] })), task.files.length > 0 && ((0, jsx_runtime_1.jsxs)(ink_1.Box, { paddingLeft: 4, flexDirection: "column", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: "gray", children: "Files:" }), task.files.map((file, i) => ((0, jsx_runtime_1.jsx)(ink_1.Box, { paddingLeft: 2, children: (0, jsx_runtime_1.jsxs)(ink_1.Text, { color: "gray", children: ["\u2022 ", file] }) }, i)))] })), task.dependencies.length > 0 && ((0, jsx_runtime_1.jsx)(ink_1.Box, { paddingLeft: 4, children: (0, jsx_runtime_1.jsxs)(ink_1.Text, { color: "gray", children: ["Dependencies: ", task.dependencies.join(', ')] }) })), (0, jsx_runtime_1.jsx)(ink_1.Box, { paddingLeft: 4, children: (0, jsx_runtime_1.jsxs)(ink_1.Text, { color: getPriorityColor(task.priority), children: ["Priority: ", task.priority] }) }), task.error && ((0, jsx_runtime_1.jsx)(ink_1.Box, { paddingLeft: 4, children: (0, jsx_runtime_1.jsxs)(ink_1.Text, { color: "red", children: ["Error: ", task.error] }) }))] }, task.id))), (0, jsx_runtime_1.jsx)(ink_1.Box, { marginTop: 1, children: (0, jsx_runtime_1.jsx)(ink_1.Text, { bold: true, color: "cyan", children: "\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501" }) }), (0, jsx_runtime_1.jsx)(ink_1.Box, { marginBottom: 1, children: (0, jsx_runtime_1.jsxs)(ink_1.Text, { color: "gray", children: ["Progress: ", completedCount, "/", totalCount, " | Status: ", plan.status] }) }), plan.totalEstimatedTokens && ((0, jsx_runtime_1.jsx)(ink_1.Box, { marginBottom: 1, children: (0, jsx_runtime_1.jsxs)(ink_1.Text, { color: "gray", children: ["Estimated Total Tokens: ~", plan.totalEstimatedTokens] }) }))] }));
|
|
68
|
+
};
|
|
69
|
+
exports.DetailedPlanView = DetailedPlanView;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.SelectArea = void 0;
|
|
7
|
+
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
8
|
+
const ink_1 = require("ink");
|
|
9
|
+
const ink_select_input_1 = __importDefault(require("ink-select-input"));
|
|
10
|
+
const SelectArea = ({ items, onSelect }) => {
|
|
11
|
+
return ((0, jsx_runtime_1.jsxs)(ink_1.Box, { flexDirection: "column", borderStyle: "round", borderColor: "cyan", children: [(0, jsx_runtime_1.jsx)(ink_1.Text, { color: "green", children: "Select one:" }), (0, jsx_runtime_1.jsx)(ink_select_input_1.default, { items: items, onSelect: (item) => onSelect(item.value) })] }));
|
|
12
|
+
};
|
|
13
|
+
exports.SelectArea = SelectArea;
|
package/package.json
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "mulby-cli",
|
|
3
|
+
"version": "1.1.5",
|
|
4
|
+
"description": "Mulby 插件开发 CLI 工具",
|
|
5
|
+
"main": "dist/index.js",
|
|
6
|
+
"files": [
|
|
7
|
+
"dist",
|
|
8
|
+
"assets",
|
|
9
|
+
"PLUGIN_DEVELOP_PROMPT.md"
|
|
10
|
+
],
|
|
11
|
+
"bin": {
|
|
12
|
+
"mulby": "dist/index.js"
|
|
13
|
+
},
|
|
14
|
+
"scripts": {
|
|
15
|
+
"build": "tsc && cp src/services/ai/*.md dist/services/ai/",
|
|
16
|
+
"dev": "tsc -w"
|
|
17
|
+
},
|
|
18
|
+
"dependencies": {
|
|
19
|
+
"@anthropic-ai/sdk": "^0.71.2",
|
|
20
|
+
"archiver": "^7.0.0",
|
|
21
|
+
"chalk": "^4.1.2",
|
|
22
|
+
"chokidar": "^3.6.0",
|
|
23
|
+
"commander": "^12.0.0",
|
|
24
|
+
"esbuild": "^0.20.0",
|
|
25
|
+
"fs-extra": "^11.2.0",
|
|
26
|
+
"ink": "^3.2.0",
|
|
27
|
+
"ink-select-input": "^4.2.2",
|
|
28
|
+
"ink-text-input": "^4.0.3",
|
|
29
|
+
"inquirer": "^13.2.0",
|
|
30
|
+
"js-tiktoken": "^1.0.21",
|
|
31
|
+
"openai": "^6.16.0",
|
|
32
|
+
"react": "^17.0.2",
|
|
33
|
+
"uuid": "^13.0.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/archiver": "^6.0.0",
|
|
37
|
+
"@types/fs-extra": "^11.0.0",
|
|
38
|
+
"@types/ink-text-input": "^2.0.5",
|
|
39
|
+
"@types/inquirer": "^9.0.9",
|
|
40
|
+
"@types/node": "^20.0.0",
|
|
41
|
+
"@types/react": "^17.0.2",
|
|
42
|
+
"@types/uuid": "^10.0.0",
|
|
43
|
+
"typescript": "^5.0.0"
|
|
44
|
+
}
|
|
45
|
+
}
|