paseo-terminal-integration 0.5.2 → 0.5.4
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/OVERVIEW.md +5 -4
- package/README.md +2 -2
- package/client/hide-run-report.ts +11 -0
- package/client/settings-screen.tsx +2 -26
- package/index.client.tsx +2 -8
- package/package.json +1 -4
- package/shared/settings.ts +1 -10
- package/client/run-report.tsx +0 -232
package/OVERVIEW.md
CHANGED
|
@@ -10,10 +10,11 @@ accept passwords and confirmations. Background commands have no terminal or inpu
|
|
|
10
10
|
parallel, and retain more output. Commands only run after the user presses a run button or submits
|
|
11
11
|
the `/run` command.
|
|
12
12
|
|
|
13
|
-
The settings control whether completed output is sent to the agent by default
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
13
|
+
The settings control whether completed output is sent to the agent by default. Sent reports remain
|
|
14
|
+
available to the agent but are hidden from the visible timeline to avoid duplicating the inline run
|
|
15
|
+
status, recent output, and terminal. Optional command guidance can be added to newly created agents
|
|
16
|
+
for either a local-daemon or client-server setup. Both supplied instruction templates are editable
|
|
17
|
+
per host.
|
|
17
18
|
|
|
18
19
|
The plugin requires Paseo 0.10.1 or later and a macOS or Linux daemon host with `bash`. Python and
|
|
19
20
|
Node.js code blocks also require their corresponding interpreter. Plugins are trusted code; this
|
package/README.md
CHANGED
|
@@ -17,8 +17,8 @@ the reply finishes. On other Paseo versions a **Run in terminal** card appears u
|
|
|
17
17
|
- With **Send output to agent** checked (the default), the command, exit code, and output go to
|
|
18
18
|
the agent as a new message when the block finishes. If the agent is busy, delivery waits until
|
|
19
19
|
its turn ends instead of interrupting it. Unchecked, **Send output** sends it later.
|
|
20
|
-
- Sent output
|
|
21
|
-
|
|
20
|
+
- Sent output remains available to the agent but is hidden from the visible timeline, since the
|
|
21
|
+
inline run controls and terminal already show the command status and recent output.
|
|
22
22
|
- **Stop** sends Ctrl-C; **Send output so far** reports a still-running command.
|
|
23
23
|
|
|
24
24
|
**Terminal beside the chat.** On Paseo builds with the terminal open-location setting, choose
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { PluginTimelineTransformerContribution } from "@getpaseo/plugin/client";
|
|
2
|
+
import { RUN_REPORT_MARKER } from "../shared/run-report";
|
|
3
|
+
|
|
4
|
+
type UserMessageTransformer = PluginTimelineTransformerContribution<"user_message">["transform"];
|
|
5
|
+
|
|
6
|
+
/** Keep the full report available to the agent without repeating it in the visible timeline. */
|
|
7
|
+
export const hideRunReport: UserMessageTransformer = ({ item }) => {
|
|
8
|
+
if (item.text === RUN_REPORT_MARKER || item.text.startsWith(`${RUN_REPORT_MARKER}\n`)) {
|
|
9
|
+
return { items: [] };
|
|
10
|
+
}
|
|
11
|
+
};
|
|
@@ -3,7 +3,6 @@ import {
|
|
|
3
3
|
ExternalLink,
|
|
4
4
|
SettingsAction,
|
|
5
5
|
SettingsCard,
|
|
6
|
-
SettingsInput,
|
|
7
6
|
SettingsRow,
|
|
8
7
|
SettingsSection,
|
|
9
8
|
SettingsSelect,
|
|
@@ -40,14 +39,6 @@ function ReadySettingsScreen({
|
|
|
40
39
|
}) {
|
|
41
40
|
const { values, revision } = settings;
|
|
42
41
|
const [draft, setDraft] = useState(values);
|
|
43
|
-
const [collapseLinesText, setCollapseLinesText] = useState(
|
|
44
|
-
String(values.collapseOutputAfterLines),
|
|
45
|
-
);
|
|
46
|
-
const collapseLines = /^\d+$/.test(collapseLinesText) ? Number(collapseLinesText) : NaN;
|
|
47
|
-
const collapseLinesError =
|
|
48
|
-
Number.isInteger(collapseLines) && collapseLines >= 0 && collapseLines <= 5_000
|
|
49
|
-
? null
|
|
50
|
-
: "Enter a whole number from 0 to 5000.";
|
|
51
42
|
const dirty = useMemo(() => JSON.stringify(draft) !== JSON.stringify(values), [draft, values]);
|
|
52
43
|
const editorStyle = useMemo(
|
|
53
44
|
() => ({
|
|
@@ -83,21 +74,6 @@ function ReadySettingsScreen({
|
|
|
83
74
|
error={settings.saveError}
|
|
84
75
|
onValueChange={(sendToAgent) => setDraft((current) => ({ ...current, sendToAgent }))}
|
|
85
76
|
/>
|
|
86
|
-
<SettingsInput
|
|
87
|
-
label="Collapse output after this many lines"
|
|
88
|
-
hint="Long output sent back to the agent starts collapsed. Set 0 to keep all output expanded."
|
|
89
|
-
initialValue={collapseLinesText}
|
|
90
|
-
placeholder="20"
|
|
91
|
-
disabled={settings.saving}
|
|
92
|
-
error={collapseLinesError}
|
|
93
|
-
onChangeText={(text) => {
|
|
94
|
-
setCollapseLinesText(text);
|
|
95
|
-
const value = /^\d+$/.test(text) ? Number(text) : NaN;
|
|
96
|
-
if (Number.isInteger(value) && value >= 0 && value <= 5_000) {
|
|
97
|
-
setDraft((current) => ({ ...current, collapseOutputAfterLines: value }));
|
|
98
|
-
}
|
|
99
|
-
}}
|
|
100
|
-
/>
|
|
101
77
|
<SettingsRow
|
|
102
78
|
label="Where the terminal appears"
|
|
103
79
|
hint={`Commands run in the "${TERMINAL_NAME}" terminal tab. Choose Main panel or On the side in Settings → Layout → Open location → Terminals. Manually moved tabs keep their location.`}
|
|
@@ -212,10 +188,10 @@ function ReadySettingsScreen({
|
|
|
212
188
|
<SettingsCard>
|
|
213
189
|
<SettingsAction
|
|
214
190
|
label="Apply changes"
|
|
215
|
-
hint="
|
|
191
|
+
hint="Command instructions apply only to newly created agents."
|
|
216
192
|
error={settings.saveError}
|
|
217
193
|
actionLabel={settings.saving ? "Saving…" : "Save changes"}
|
|
218
|
-
disabled={settings.saving || !dirty
|
|
194
|
+
disabled={settings.saving || !dirty}
|
|
219
195
|
onPress={() => void settings.save(draft, revision)}
|
|
220
196
|
/>
|
|
221
197
|
</SettingsCard>
|
package/index.client.tsx
CHANGED
|
@@ -3,8 +3,8 @@ import { contributeLegacy } from "./client/legacy";
|
|
|
3
3
|
import type { PluginCleanup } from "@getpaseo/plugin";
|
|
4
4
|
import type { InlineActionsClient } from "./client/sdk-compat";
|
|
5
5
|
import { CodeBlockActions } from "./client/code-block-actions";
|
|
6
|
+
import { hideRunReport } from "./client/hide-run-report";
|
|
6
7
|
import { SettingsScreen } from "./client/settings-screen";
|
|
7
|
-
import { RunReport, runReportSchema, transformRunReport } from "./client/run-report";
|
|
8
8
|
import { RUNNABLE_LANGUAGES } from "./shared/code-blocks";
|
|
9
9
|
import { terminalOutputSource } from "./shared/contracts";
|
|
10
10
|
|
|
@@ -12,13 +12,7 @@ export default function contribute(client: InlineActionsClient): PluginCleanup {
|
|
|
12
12
|
client.addTimelineTransformer({
|
|
13
13
|
id: "terminal-run-report",
|
|
14
14
|
query: { itemType: "user_message" },
|
|
15
|
-
transform:
|
|
16
|
-
});
|
|
17
|
-
client.addTimelineRenderer({
|
|
18
|
-
kind: "terminal-run-report",
|
|
19
|
-
version: 1,
|
|
20
|
-
schema: runReportSchema,
|
|
21
|
-
Component: RunReport,
|
|
15
|
+
transform: hideRunReport,
|
|
22
16
|
});
|
|
23
17
|
if (typeof client.addCodeBlockActions === "function") {
|
|
24
18
|
client.addCodeBlockActions({
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "paseo-terminal-integration",
|
|
3
|
-
"version": "0.5.
|
|
3
|
+
"version": "0.5.4",
|
|
4
4
|
"description": "Run agent code blocks in a Paseo terminal and send the output back to the agent; attach terminal output to prompts",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"agents",
|
|
@@ -35,9 +35,6 @@
|
|
|
35
35
|
"lint": "oxlint",
|
|
36
36
|
"format": "oxfmt ."
|
|
37
37
|
},
|
|
38
|
-
"dependencies": {
|
|
39
|
-
"@getpaseo/highlight": "0.11.2"
|
|
40
|
-
},
|
|
41
38
|
"devDependencies": {
|
|
42
39
|
"@getpaseo/plugin": "0.10.2",
|
|
43
40
|
"@tanstack/react-query": "^5.90.11",
|
package/shared/settings.ts
CHANGED
|
@@ -4,22 +4,13 @@ import { CLIENT_SERVER_INSTRUCTIONS, LOCAL_DAEMON_INSTRUCTIONS } from "./instruc
|
|
|
4
4
|
|
|
5
5
|
export const instructionTopologies = ["local-daemon", "client-server"] as const;
|
|
6
6
|
export type InstructionTopology = (typeof instructionTopologies)[number];
|
|
7
|
-
export const DEFAULT_COLLAPSE_OUTPUT_AFTER_LINES = 20;
|
|
8
|
-
|
|
9
7
|
export const preferences = defineSettings({
|
|
10
8
|
id: "preferences",
|
|
11
9
|
scope: "host",
|
|
12
|
-
version:
|
|
10
|
+
version: 4,
|
|
13
11
|
schema: z.object({
|
|
14
12
|
/** Initial state of a code block's "Send output to agent" checkbox. */
|
|
15
13
|
sendToAgent: z.boolean().default(true),
|
|
16
|
-
/** Automatically collapse a sent run report when its output exceeds this many lines; 0 disables. */
|
|
17
|
-
collapseOutputAfterLines: z
|
|
18
|
-
.number()
|
|
19
|
-
.int()
|
|
20
|
-
.min(0)
|
|
21
|
-
.max(5_000)
|
|
22
|
-
.default(DEFAULT_COLLAPSE_OUTPUT_AFTER_LINES),
|
|
23
14
|
/** Add command-formatting and execution-topology guidance to newly created agents. */
|
|
24
15
|
injectCommandInstructions: z.boolean().default(false),
|
|
25
16
|
instructionTopology: z.enum(instructionTopologies).default("client-server"),
|
package/client/run-report.tsx
DELETED
|
@@ -1,232 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
useSettings,
|
|
3
|
-
type PluginTimelineItemProps,
|
|
4
|
-
type PluginTimelineTransformerContribution,
|
|
5
|
-
} from "@getpaseo/plugin/client";
|
|
6
|
-
import { Icon } from "@getpaseo/plugin/client/react-native";
|
|
7
|
-
import {
|
|
8
|
-
darkHighlightColors,
|
|
9
|
-
highlightCode,
|
|
10
|
-
lightHighlightColors,
|
|
11
|
-
type HighlightStyle,
|
|
12
|
-
type HighlightToken,
|
|
13
|
-
} from "@getpaseo/highlight";
|
|
14
|
-
import { useMemo, useState } from "react";
|
|
15
|
-
import { Platform, Pressable, ScrollView, Text, View } from "react-native";
|
|
16
|
-
import {
|
|
17
|
-
parseRunReport,
|
|
18
|
-
runReportSchema,
|
|
19
|
-
wrapRunReportOutcome,
|
|
20
|
-
type RunReportData,
|
|
21
|
-
} from "../shared/run-report";
|
|
22
|
-
import { DEFAULT_COLLAPSE_OUTPUT_AFTER_LINES, preferences } from "../shared/settings";
|
|
23
|
-
|
|
24
|
-
type UserMessageTransformer = PluginTimelineTransformerContribution<"user_message">["transform"];
|
|
25
|
-
type Theme = PluginTimelineItemProps["theme"];
|
|
26
|
-
|
|
27
|
-
export const transformRunReport: UserMessageTransformer = ({ item }) => {
|
|
28
|
-
const data = parseRunReport(item.text);
|
|
29
|
-
if (!data) return;
|
|
30
|
-
return {
|
|
31
|
-
items: [{ type: "plugin", kind: "terminal-run-report", version: 1, data }],
|
|
32
|
-
};
|
|
33
|
-
};
|
|
34
|
-
|
|
35
|
-
export { runReportSchema };
|
|
36
|
-
|
|
37
|
-
export function RunReport({ item, theme, layout }: PluginTimelineItemProps<RunReportData>) {
|
|
38
|
-
const settings = useSettings(preferences);
|
|
39
|
-
const threshold =
|
|
40
|
-
settings.status === "ready"
|
|
41
|
-
? settings.values.collapseOutputAfterLines
|
|
42
|
-
: DEFAULT_COLLAPSE_OUTPUT_AFTER_LINES;
|
|
43
|
-
const autoCollapsed = threshold > 0 && item.data.outputLineCount > threshold;
|
|
44
|
-
const [expandedOverride, setExpandedOverride] = useState<boolean | null>(null);
|
|
45
|
-
const expanded = expandedOverride ?? !autoCollapsed;
|
|
46
|
-
const styles = useMemo(() => createStyles(theme, layout.compact), [theme, layout.compact]);
|
|
47
|
-
|
|
48
|
-
if (!item.data.output) {
|
|
49
|
-
return (
|
|
50
|
-
<View style={styles.card}>
|
|
51
|
-
<Text style={styles.intro}>{item.data.intro}</Text>
|
|
52
|
-
<CodeBlock
|
|
53
|
-
text={item.data.code}
|
|
54
|
-
language={item.data.language}
|
|
55
|
-
styles={styles}
|
|
56
|
-
theme={theme}
|
|
57
|
-
/>
|
|
58
|
-
<Text style={styles.outputLabel}>{item.data.outputLabel}</Text>
|
|
59
|
-
</View>
|
|
60
|
-
);
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
return (
|
|
64
|
-
<View style={[styles.card, !expanded && styles.collapsedCard]}>
|
|
65
|
-
<Pressable
|
|
66
|
-
accessibilityRole="button"
|
|
67
|
-
accessibilityLabel={`${expanded ? "Collapse" : "Expand"} terminal output`}
|
|
68
|
-
accessibilityState={{ expanded }}
|
|
69
|
-
onPress={() => setExpandedOverride(!expanded)}
|
|
70
|
-
style={({ pressed }) => [styles.reportHeader, pressed && styles.pressed]}
|
|
71
|
-
>
|
|
72
|
-
<Icon
|
|
73
|
-
name={expanded ? "ChevronDown" : "ChevronRight"}
|
|
74
|
-
size={16}
|
|
75
|
-
color={theme.colors.foregroundMuted}
|
|
76
|
-
/>
|
|
77
|
-
<Text style={styles.reportTitle} numberOfLines={expanded ? undefined : 1}>
|
|
78
|
-
{expanded ? wrapRunReportOutcome(item.data.intro) : item.data.intro}
|
|
79
|
-
</Text>
|
|
80
|
-
<Text style={styles.lineCount}>
|
|
81
|
-
{item.data.outputLineCount} {item.data.outputLineCount === 1 ? "line" : "lines"}
|
|
82
|
-
</Text>
|
|
83
|
-
</Pressable>
|
|
84
|
-
{expanded ? (
|
|
85
|
-
<>
|
|
86
|
-
<CodeBlock
|
|
87
|
-
text={item.data.code}
|
|
88
|
-
language={item.data.language}
|
|
89
|
-
styles={styles}
|
|
90
|
-
theme={theme}
|
|
91
|
-
/>
|
|
92
|
-
<Text style={styles.outputLabel}>{item.data.outputLabel}</Text>
|
|
93
|
-
<CodeBlock text={item.data.output} styles={styles} />
|
|
94
|
-
</>
|
|
95
|
-
) : null}
|
|
96
|
-
</View>
|
|
97
|
-
);
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
type Styles = ReturnType<typeof createStyles>;
|
|
101
|
-
|
|
102
|
-
const MAX_HIGHLIGHT_CHARS = 100_000;
|
|
103
|
-
|
|
104
|
-
const LANGUAGE_EXTENSIONS: Readonly<Record<string, string>> = {
|
|
105
|
-
bash: "sh",
|
|
106
|
-
shell: "sh",
|
|
107
|
-
console: "sh",
|
|
108
|
-
zsh: "sh",
|
|
109
|
-
fish: "sh",
|
|
110
|
-
python: "py",
|
|
111
|
-
py: "py",
|
|
112
|
-
javascript: "js",
|
|
113
|
-
node: "js",
|
|
114
|
-
typescript: "ts",
|
|
115
|
-
};
|
|
116
|
-
|
|
117
|
-
function CodeBlock({
|
|
118
|
-
text,
|
|
119
|
-
language,
|
|
120
|
-
styles,
|
|
121
|
-
theme,
|
|
122
|
-
}: {
|
|
123
|
-
text: string;
|
|
124
|
-
language?: string;
|
|
125
|
-
styles: Styles;
|
|
126
|
-
theme?: Theme;
|
|
127
|
-
}) {
|
|
128
|
-
const highlighted = useMemo(() => highlight(text, language), [language, text]);
|
|
129
|
-
const colors =
|
|
130
|
-
theme && isDark(theme.colors.surface0) ? darkHighlightColors : lightHighlightColors;
|
|
131
|
-
|
|
132
|
-
return (
|
|
133
|
-
<ScrollView horizontal style={styles.codeBlock} contentContainerStyle={styles.codeContent}>
|
|
134
|
-
<Text selectable style={styles.code}>
|
|
135
|
-
{highlighted
|
|
136
|
-
? highlighted.map((line, lineIndex) => (
|
|
137
|
-
<Text key={lineIndex}>
|
|
138
|
-
{line.map((token, tokenIndex) => (
|
|
139
|
-
<Text
|
|
140
|
-
key={`${lineIndex}-${tokenIndex}`}
|
|
141
|
-
style={{ color: tokenColor(token, colors, theme?.colors.foreground) }}
|
|
142
|
-
>
|
|
143
|
-
{token.text}
|
|
144
|
-
</Text>
|
|
145
|
-
))}
|
|
146
|
-
{lineIndex < highlighted.length - 1 ? "\n" : null}
|
|
147
|
-
</Text>
|
|
148
|
-
))
|
|
149
|
-
: text}
|
|
150
|
-
</Text>
|
|
151
|
-
</ScrollView>
|
|
152
|
-
);
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
function highlight(text: string, language?: string): HighlightToken[][] | null {
|
|
156
|
-
if (!language || text.length > MAX_HIGHLIGHT_CHARS) return null;
|
|
157
|
-
const normalized = language.toLowerCase();
|
|
158
|
-
const extension = LANGUAGE_EXTENSIONS[normalized] ?? normalized;
|
|
159
|
-
try {
|
|
160
|
-
return highlightCode(text, `command.${extension}`);
|
|
161
|
-
} catch {
|
|
162
|
-
return null;
|
|
163
|
-
}
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
function tokenColor(
|
|
167
|
-
token: HighlightToken,
|
|
168
|
-
colors: Readonly<Record<HighlightStyle, string>>,
|
|
169
|
-
fallback = "inherit",
|
|
170
|
-
): string {
|
|
171
|
-
return token.style ? colors[token.style] : fallback;
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
function isDark(color: string): boolean {
|
|
175
|
-
const match = /^#([0-9a-f]{6})$/i.exec(color);
|
|
176
|
-
if (!match) return true;
|
|
177
|
-
const value = Number.parseInt(match[1], 16);
|
|
178
|
-
const red = value >> 16;
|
|
179
|
-
const green = (value >> 8) & 0xff;
|
|
180
|
-
const blue = value & 0xff;
|
|
181
|
-
return red * 0.299 + green * 0.587 + blue * 0.114 < 128;
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
function createStyles(theme: Theme, compact: boolean) {
|
|
185
|
-
return {
|
|
186
|
-
card: {
|
|
187
|
-
gap: 10,
|
|
188
|
-
padding: compact ? 12 : 14,
|
|
189
|
-
borderWidth: 1,
|
|
190
|
-
borderColor: theme.colors.border,
|
|
191
|
-
borderRadius: 12,
|
|
192
|
-
backgroundColor: theme.colors.surface1,
|
|
193
|
-
},
|
|
194
|
-
collapsedCard: { gap: 0, padding: 4 },
|
|
195
|
-
intro: { color: theme.colors.foreground, fontSize: 14, lineHeight: 20 },
|
|
196
|
-
reportHeader: {
|
|
197
|
-
minHeight: 36,
|
|
198
|
-
flexDirection: "row" as const,
|
|
199
|
-
alignItems: "center" as const,
|
|
200
|
-
gap: 6,
|
|
201
|
-
paddingHorizontal: 8,
|
|
202
|
-
borderRadius: 6,
|
|
203
|
-
},
|
|
204
|
-
pressed: { opacity: 0.7 },
|
|
205
|
-
reportTitle: {
|
|
206
|
-
flex: 1,
|
|
207
|
-
color: theme.colors.foreground,
|
|
208
|
-
fontSize: 13,
|
|
209
|
-
lineHeight: 18,
|
|
210
|
-
},
|
|
211
|
-
outputLabel: { flexShrink: 1, color: theme.colors.foreground, fontSize: 13 },
|
|
212
|
-
lineCount: { marginLeft: "auto" as const, color: theme.colors.foregroundMuted, fontSize: 12 },
|
|
213
|
-
codeBlock: {
|
|
214
|
-
maxWidth: "100%" as const,
|
|
215
|
-
borderWidth: 1,
|
|
216
|
-
borderColor: theme.colors.border,
|
|
217
|
-
borderRadius: 8,
|
|
218
|
-
backgroundColor: theme.colors.surface0,
|
|
219
|
-
},
|
|
220
|
-
codeContent: { paddingHorizontal: 10, paddingVertical: 8 },
|
|
221
|
-
code: {
|
|
222
|
-
color: theme.colors.foreground,
|
|
223
|
-
fontSize: 12,
|
|
224
|
-
lineHeight: 18,
|
|
225
|
-
fontFamily: Platform.select({
|
|
226
|
-
ios: "Menlo",
|
|
227
|
-
android: "monospace",
|
|
228
|
-
default: "ui-monospace, SFMono-Regular, Menlo, Consolas, monospace",
|
|
229
|
-
}),
|
|
230
|
-
},
|
|
231
|
-
};
|
|
232
|
-
}
|