@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.
Files changed (75) hide show
  1. package/README.md +58 -315
  2. package/dist/agent-runner.js +224 -52
  3. package/dist/commands/login.js +392 -0
  4. package/dist/commands/setup.js +234 -0
  5. package/dist/config.js +29 -0
  6. package/dist/core/agent.js +270 -0
  7. package/dist/index.js +118 -31
  8. package/dist/modes/headless.js +117 -0
  9. package/dist/modes/interactive.js +430 -0
  10. package/dist/presenters/composite.js +32 -0
  11. package/dist/presenters/console.js +163 -0
  12. package/dist/presenters/react.js +220 -0
  13. package/dist/presenters/types.js +1 -0
  14. package/dist/presenters/web.js +78 -0
  15. package/dist/prompts/builder.js +181 -0
  16. package/dist/prompts/fixer.js +148 -0
  17. package/dist/prompts/headless.md +97 -0
  18. package/dist/prompts/index.js +3 -0
  19. package/dist/prompts/interactive.md +43 -0
  20. package/dist/prompts/plan.md +41 -0
  21. package/dist/prompts/planner.js +70 -0
  22. package/dist/prompts/prompts/builder.md +97 -0
  23. package/dist/prompts/prompts/fixer.md +100 -0
  24. package/dist/prompts/prompts/plan.md +41 -0
  25. package/dist/prompts/prompts/planner.md +41 -0
  26. package/dist/services/api-client.js +244 -0
  27. package/dist/services/event-streamer.js +130 -0
  28. package/dist/ui/App.js +322 -0
  29. package/dist/ui/components/AuthBanner.js +20 -0
  30. package/dist/ui/components/AuthDialog.js +32 -0
  31. package/dist/ui/components/Banner.js +12 -0
  32. package/dist/ui/components/ExpandableSection.js +17 -0
  33. package/dist/ui/components/Header.js +49 -0
  34. package/dist/ui/components/HelpMenu.js +89 -0
  35. package/dist/ui/components/InputPrompt.js +292 -0
  36. package/dist/ui/components/MessageList.js +42 -0
  37. package/dist/ui/components/QueuedMessageDisplay.js +31 -0
  38. package/dist/ui/components/Scrollable.js +103 -0
  39. package/dist/ui/components/SessionSelector.js +196 -0
  40. package/dist/ui/components/StatusBar.js +45 -0
  41. package/dist/ui/components/messages/AssistantMessage.js +20 -0
  42. package/dist/ui/components/messages/ErrorMessage.js +26 -0
  43. package/dist/ui/components/messages/LoadingMessage.js +28 -0
  44. package/dist/ui/components/messages/ThinkingMessage.js +17 -0
  45. package/dist/ui/components/messages/TodoMessage.js +44 -0
  46. package/dist/ui/components/messages/ToolMessage.js +218 -0
  47. package/dist/ui/components/messages/UserMessage.js +14 -0
  48. package/dist/ui/contexts/KeypressContext.js +527 -0
  49. package/dist/ui/contexts/MouseContext.js +98 -0
  50. package/dist/ui/contexts/SessionContext.js +131 -0
  51. package/dist/ui/hooks/useAnimatedScrollbar.js +83 -0
  52. package/dist/ui/hooks/useBatchedScroll.js +22 -0
  53. package/dist/ui/hooks/useBracketedPaste.js +31 -0
  54. package/dist/ui/hooks/useFocus.js +50 -0
  55. package/dist/ui/hooks/useKeypress.js +26 -0
  56. package/dist/ui/hooks/useModeToggle.js +25 -0
  57. package/dist/ui/types/auth.js +13 -0
  58. package/dist/ui/utils/file-completion.js +56 -0
  59. package/dist/ui/utils/input.js +50 -0
  60. package/dist/ui/utils/markdown.js +376 -0
  61. package/dist/ui/utils/mouse.js +189 -0
  62. package/dist/ui/utils/theme.js +59 -0
  63. package/dist/utils/banner.js +7 -14
  64. package/dist/utils/encryption.js +71 -0
  65. package/dist/utils/events.js +36 -0
  66. package/dist/utils/keychain-storage.js +120 -0
  67. package/dist/utils/logger.js +103 -1
  68. package/dist/utils/node-version.js +1 -3
  69. package/dist/utils/plan-file.js +75 -0
  70. package/dist/utils/project-instructions.js +23 -0
  71. package/dist/utils/rich-logger.js +1 -1
  72. package/dist/utils/stdio.js +80 -0
  73. package/dist/utils/summary.js +1 -5
  74. package/dist/utils/token-storage.js +242 -0
  75. 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
+ };