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 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 and how many lines
14
- make a result start collapsed in the timeline. Optional command guidance can be added to newly
15
- created agents for either a local-daemon or client-server setup. Both supplied instruction templates
16
- are editable per host.
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 longer than 20 lines starts collapsed. Change the threshold under
21
- **Settings → Plugins → Terminal integration**, or set it to 0 to keep output expanded.
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="Output display changes immediately. Command instructions apply only to newly created agents."
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 || collapseLinesError !== null}
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: transformRunReport,
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.2",
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",
@@ -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: 3,
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"),
@@ -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
- }