@supatest/cli 0.0.2 → 0.0.3
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 +58 -315
- package/dist/agent-runner.js +224 -52
- package/dist/commands/login.js +392 -0
- package/dist/commands/setup.js +234 -0
- package/dist/config.js +29 -0
- package/dist/core/agent.js +270 -0
- package/dist/index.js +118 -31
- package/dist/modes/headless.js +117 -0
- package/dist/modes/interactive.js +430 -0
- package/dist/presenters/composite.js +32 -0
- package/dist/presenters/console.js +163 -0
- package/dist/presenters/react.js +220 -0
- package/dist/presenters/types.js +1 -0
- package/dist/presenters/web.js +78 -0
- package/dist/prompts/builder.js +181 -0
- package/dist/prompts/fixer.js +148 -0
- package/dist/prompts/headless.md +97 -0
- package/dist/prompts/index.js +3 -0
- package/dist/prompts/interactive.md +43 -0
- package/dist/prompts/plan.md +41 -0
- package/dist/prompts/planner.js +70 -0
- package/dist/prompts/prompts/builder.md +97 -0
- package/dist/prompts/prompts/fixer.md +100 -0
- package/dist/prompts/prompts/plan.md +41 -0
- package/dist/prompts/prompts/planner.md +41 -0
- package/dist/services/api-client.js +244 -0
- package/dist/services/event-streamer.js +130 -0
- package/dist/ui/App.js +322 -0
- package/dist/ui/components/AuthBanner.js +20 -0
- package/dist/ui/components/AuthDialog.js +32 -0
- package/dist/ui/components/Banner.js +12 -0
- package/dist/ui/components/ExpandableSection.js +17 -0
- package/dist/ui/components/Header.js +49 -0
- package/dist/ui/components/HelpMenu.js +89 -0
- package/dist/ui/components/InputPrompt.js +292 -0
- package/dist/ui/components/MessageList.js +42 -0
- package/dist/ui/components/QueuedMessageDisplay.js +31 -0
- package/dist/ui/components/Scrollable.js +103 -0
- package/dist/ui/components/SessionSelector.js +196 -0
- package/dist/ui/components/StatusBar.js +45 -0
- package/dist/ui/components/messages/AssistantMessage.js +20 -0
- package/dist/ui/components/messages/ErrorMessage.js +26 -0
- package/dist/ui/components/messages/LoadingMessage.js +28 -0
- package/dist/ui/components/messages/ThinkingMessage.js +17 -0
- package/dist/ui/components/messages/TodoMessage.js +44 -0
- package/dist/ui/components/messages/ToolMessage.js +218 -0
- package/dist/ui/components/messages/UserMessage.js +14 -0
- package/dist/ui/contexts/KeypressContext.js +527 -0
- package/dist/ui/contexts/MouseContext.js +98 -0
- package/dist/ui/contexts/SessionContext.js +131 -0
- package/dist/ui/hooks/useAnimatedScrollbar.js +83 -0
- package/dist/ui/hooks/useBatchedScroll.js +22 -0
- package/dist/ui/hooks/useBracketedPaste.js +31 -0
- package/dist/ui/hooks/useFocus.js +50 -0
- package/dist/ui/hooks/useKeypress.js +26 -0
- package/dist/ui/hooks/useModeToggle.js +25 -0
- package/dist/ui/types/auth.js +13 -0
- package/dist/ui/utils/file-completion.js +56 -0
- package/dist/ui/utils/input.js +50 -0
- package/dist/ui/utils/markdown.js +376 -0
- package/dist/ui/utils/mouse.js +189 -0
- package/dist/ui/utils/theme.js +59 -0
- package/dist/utils/banner.js +7 -14
- package/dist/utils/encryption.js +71 -0
- package/dist/utils/events.js +36 -0
- package/dist/utils/keychain-storage.js +120 -0
- package/dist/utils/logger.js +103 -1
- package/dist/utils/node-version.js +1 -3
- package/dist/utils/plan-file.js +75 -0
- package/dist/utils/project-instructions.js +23 -0
- package/dist/utils/rich-logger.js +1 -1
- package/dist/utils/stdio.js +80 -0
- package/dist/utils/summary.js +1 -5
- package/dist/utils/token-storage.js +242 -0
- package/package.json +35 -15
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Tool message component
|
|
3
|
+
* Displays tool calls (Read, Write, Edit, Bash, etc.)
|
|
4
|
+
*/
|
|
5
|
+
import { structuredPatch } from "diff";
|
|
6
|
+
import { Box, Text } from "ink";
|
|
7
|
+
import React from "react";
|
|
8
|
+
import { getToolDisplayName } from "shared";
|
|
9
|
+
import { theme } from "../../utils/theme.js";
|
|
10
|
+
export const ToolMessage = ({ toolName, description, input, result, isExpanded = true, }) => {
|
|
11
|
+
const displayName = getToolDisplayName(toolName);
|
|
12
|
+
const { icon, color } = getToolStyle(toolName);
|
|
13
|
+
const resultSummary = result ? getResultSummary(toolName, result) : null;
|
|
14
|
+
const hasExpandableContent = !!result;
|
|
15
|
+
const isRunning = !result;
|
|
16
|
+
// Get the actual command/content to display
|
|
17
|
+
const commandDisplay = getCommandDisplay(toolName, input);
|
|
18
|
+
// Check if this is an Edit operation - show diff by default
|
|
19
|
+
const isEditOperation = toolName === "Edit";
|
|
20
|
+
const editDiff = isEditOperation ? getEditDiff(input) : null;
|
|
21
|
+
// Tools that support expand/collapse (Bash has expandable output)
|
|
22
|
+
const isExpandableTool = toolName === "Bash" || toolName === "BashOutput" || toolName === "Command Output";
|
|
23
|
+
return (React.createElement(Box, { flexDirection: "column", marginTop: 1 },
|
|
24
|
+
React.createElement(Box, { flexDirection: "row" },
|
|
25
|
+
hasExpandableContent && isExpandableTool ? (React.createElement(Text, { color: theme.text.dim }, isExpanded ? "▼ " : "▶ ")) : (React.createElement(Text, { color: isRunning ? "yellow" : color }, "\u25CF ")),
|
|
26
|
+
React.createElement(Text, { bold: true, color: color }, displayName),
|
|
27
|
+
React.createElement(Text, { color: theme.text.dim },
|
|
28
|
+
"(",
|
|
29
|
+
description,
|
|
30
|
+
")"),
|
|
31
|
+
editDiff && (React.createElement(Text, { color: theme.text.dim },
|
|
32
|
+
" with ",
|
|
33
|
+
editDiff.additions,
|
|
34
|
+
" addition",
|
|
35
|
+
editDiff.additions !== 1 ? "s" : "",
|
|
36
|
+
" and ",
|
|
37
|
+
editDiff.removals,
|
|
38
|
+
" removal",
|
|
39
|
+
editDiff.removals !== 1 ? "s" : "")),
|
|
40
|
+
hasExpandableContent && !isExpanded && isExpandableTool && (React.createElement(Text, { color: theme.text.dim }, " (ctrl+o to expand)"))),
|
|
41
|
+
!isEditOperation && resultSummary && (React.createElement(Box, { marginLeft: 2 },
|
|
42
|
+
React.createElement(Text, { color: theme.text.dim },
|
|
43
|
+
"\u2514 ",
|
|
44
|
+
resultSummary))),
|
|
45
|
+
editDiff && (React.createElement(Box, { flexDirection: "column", marginLeft: 2 }, editDiff.lines.map((line, i) => {
|
|
46
|
+
if (line.type === "separator") {
|
|
47
|
+
return (React.createElement(Box, { key: i, flexDirection: "row" },
|
|
48
|
+
React.createElement(Text, { color: theme.text.dim }, "...")));
|
|
49
|
+
}
|
|
50
|
+
const isRemove = line.type === "remove";
|
|
51
|
+
const isAdd = line.type === "add";
|
|
52
|
+
const bgColor = isRemove ? "#5c1b1b" : isAdd ? "#1b3d1b" : undefined;
|
|
53
|
+
const prefix = isRemove ? "- " : isAdd ? "+ " : " ";
|
|
54
|
+
const prefixColor = isRemove ? "red" : isAdd ? "green" : theme.text.dim;
|
|
55
|
+
return (React.createElement(Box, { key: i, flexDirection: "row" },
|
|
56
|
+
React.createElement(Text, { color: theme.text.dim }, line.lineNum.toString().padStart(4, " ")),
|
|
57
|
+
React.createElement(Text, { color: prefixColor },
|
|
58
|
+
" ",
|
|
59
|
+
prefix),
|
|
60
|
+
React.createElement(Text, { backgroundColor: bgColor }, line.content)));
|
|
61
|
+
}))),
|
|
62
|
+
isExpanded && result && isExpandableTool && (React.createElement(Box, { flexDirection: "column", marginLeft: 4, marginTop: 1 },
|
|
63
|
+
React.createElement(Text, { color: theme.text.secondary }, truncate(result, 2000))))));
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Generate diff display for Edit operations using jsdiff
|
|
67
|
+
* Shows context lines before/after changes like Claude Code
|
|
68
|
+
*/
|
|
69
|
+
function getEditDiff(input) {
|
|
70
|
+
if (!input?.old_string || !input?.new_string)
|
|
71
|
+
return null;
|
|
72
|
+
// Use jsdiff to compute structured patch with 3 lines of context
|
|
73
|
+
const patch = structuredPatch("file", "file", input.old_string, input.new_string, "", "", { context: 3 });
|
|
74
|
+
const lines = [];
|
|
75
|
+
let additions = 0;
|
|
76
|
+
let removals = 0;
|
|
77
|
+
// Process each hunk
|
|
78
|
+
for (let hunkIndex = 0; hunkIndex < patch.hunks.length; hunkIndex++) {
|
|
79
|
+
const hunk = patch.hunks[hunkIndex];
|
|
80
|
+
// Add separator between hunks (except for first)
|
|
81
|
+
if (hunkIndex > 0) {
|
|
82
|
+
lines.push({
|
|
83
|
+
lineNum: "...",
|
|
84
|
+
type: "separator",
|
|
85
|
+
content: "",
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
let oldLineNum = hunk.oldStart;
|
|
89
|
+
let newLineNum = hunk.newStart;
|
|
90
|
+
for (const line of hunk.lines) {
|
|
91
|
+
const content = line.substring(1); // Remove the +/- prefix
|
|
92
|
+
if (line.startsWith("+")) {
|
|
93
|
+
lines.push({
|
|
94
|
+
lineNum: newLineNum,
|
|
95
|
+
type: "add",
|
|
96
|
+
content,
|
|
97
|
+
});
|
|
98
|
+
newLineNum++;
|
|
99
|
+
additions++;
|
|
100
|
+
}
|
|
101
|
+
else if (line.startsWith("-")) {
|
|
102
|
+
lines.push({
|
|
103
|
+
lineNum: oldLineNum,
|
|
104
|
+
type: "remove",
|
|
105
|
+
content,
|
|
106
|
+
});
|
|
107
|
+
oldLineNum++;
|
|
108
|
+
removals++;
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
// Context line (starts with space)
|
|
112
|
+
lines.push({
|
|
113
|
+
lineNum: newLineNum,
|
|
114
|
+
type: "context",
|
|
115
|
+
content,
|
|
116
|
+
});
|
|
117
|
+
oldLineNum++;
|
|
118
|
+
newLineNum++;
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
return { lines, additions, removals };
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* Get command/content to display based on tool type
|
|
126
|
+
*/
|
|
127
|
+
function getCommandDisplay(toolName, input) {
|
|
128
|
+
if (!input)
|
|
129
|
+
return null;
|
|
130
|
+
switch (toolName) {
|
|
131
|
+
case "Bash":
|
|
132
|
+
case "BashOutput":
|
|
133
|
+
case "Command Output": {
|
|
134
|
+
const command = input.command || input.bash_id;
|
|
135
|
+
if (!command)
|
|
136
|
+
return null;
|
|
137
|
+
// Split long commands into multiple lines
|
|
138
|
+
return [command];
|
|
139
|
+
}
|
|
140
|
+
case "Read":
|
|
141
|
+
case "Write":
|
|
142
|
+
case "Edit":
|
|
143
|
+
return input.file_path ? [input.file_path] : null;
|
|
144
|
+
case "Grep":
|
|
145
|
+
return input.pattern ? [`"${input.pattern}"${input.path ? ` in ${input.path}` : ""}`] : null;
|
|
146
|
+
case "Glob":
|
|
147
|
+
return input.pattern ? [input.pattern] : null;
|
|
148
|
+
default:
|
|
149
|
+
return null;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Get icon and color for tool based on name
|
|
154
|
+
*/
|
|
155
|
+
function getToolStyle(toolName) {
|
|
156
|
+
const styles = {
|
|
157
|
+
Read: { icon: "📖", color: theme.tool.read },
|
|
158
|
+
Write: { icon: "✏️", color: theme.tool.write },
|
|
159
|
+
Edit: { icon: "✏️", color: theme.tool.edit },
|
|
160
|
+
Bash: { icon: "🔨", color: theme.tool.bash },
|
|
161
|
+
BashOutput: { icon: "📄", color: theme.tool.bash },
|
|
162
|
+
"Command Output": { icon: "📄", color: theme.tool.bash },
|
|
163
|
+
Glob: { icon: "🔍", color: theme.tool.search },
|
|
164
|
+
Grep: { icon: "🔍", color: theme.tool.search },
|
|
165
|
+
Task: { icon: "🤖", color: theme.tool.agent },
|
|
166
|
+
TodoWrite: { icon: "📝", color: theme.text.info },
|
|
167
|
+
};
|
|
168
|
+
return styles[toolName] || { icon: "🔧", color: theme.text.secondary };
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Truncate string to max length
|
|
172
|
+
*/
|
|
173
|
+
function truncate(str, maxLength) {
|
|
174
|
+
if (str.length <= maxLength) {
|
|
175
|
+
return str;
|
|
176
|
+
}
|
|
177
|
+
return str.slice(0, maxLength - 3) + "...";
|
|
178
|
+
}
|
|
179
|
+
/**
|
|
180
|
+
* Get result summary for tool output
|
|
181
|
+
*/
|
|
182
|
+
function getResultSummary(toolName, result) {
|
|
183
|
+
switch (toolName) {
|
|
184
|
+
case "Glob": {
|
|
185
|
+
// Count lines that look like file paths
|
|
186
|
+
const lines = result.split("\n").filter((line) => line.trim());
|
|
187
|
+
const count = lines.filter((line) => !line.startsWith("Found")).length;
|
|
188
|
+
return count > 0 ? `Found ${count} files` : "No files found";
|
|
189
|
+
}
|
|
190
|
+
case "Grep": {
|
|
191
|
+
// Count lines of output
|
|
192
|
+
const lines = result.split("\n").filter((line) => line.trim());
|
|
193
|
+
return lines.length > 0
|
|
194
|
+
? `${lines.length} lines of output`
|
|
195
|
+
: "No matches found";
|
|
196
|
+
}
|
|
197
|
+
case "Read": {
|
|
198
|
+
// Count lines
|
|
199
|
+
const lines = result.split("\n").length;
|
|
200
|
+
return `Read ${lines} lines`;
|
|
201
|
+
}
|
|
202
|
+
case "Bash": {
|
|
203
|
+
// Show if there was output
|
|
204
|
+
const hasOutput = result.trim().length > 0;
|
|
205
|
+
return hasOutput ? `${result.split("\n").length} lines of output` : null;
|
|
206
|
+
}
|
|
207
|
+
case "BashOutput":
|
|
208
|
+
case "Command Output": {
|
|
209
|
+
// Show lines of shell output
|
|
210
|
+
const lines = result.split("\n").filter((line) => line.trim());
|
|
211
|
+
return lines.length > 0
|
|
212
|
+
? `${lines.length} lines of output`
|
|
213
|
+
: "No output";
|
|
214
|
+
}
|
|
215
|
+
default:
|
|
216
|
+
return null;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* User message component
|
|
3
|
+
* Displays user input/prompts
|
|
4
|
+
*/
|
|
5
|
+
import { Box, Text } from "ink";
|
|
6
|
+
import React from "react";
|
|
7
|
+
import { theme } from "../../utils/theme.js";
|
|
8
|
+
export const UserMessage = ({ text }) => {
|
|
9
|
+
return (React.createElement(Box, { flexDirection: "row", marginTop: 1 },
|
|
10
|
+
React.createElement(Box, { width: 2 },
|
|
11
|
+
React.createElement(Text, { color: theme.text.info }, "\u276F ")),
|
|
12
|
+
React.createElement(Box, { flexGrow: 1 },
|
|
13
|
+
React.createElement(Text, { color: theme.text.primary }, text))));
|
|
14
|
+
};
|