@alasano/pi-linear 0.1.0 → 0.2.0
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 +14 -2
- package/assets/linear_list_issues.png +0 -0
- package/assets/screenshot.png +0 -0
- package/extensions/index.ts +1 -1
- package/extensions/renderers/comments.ts +323 -0
- package/extensions/renderers/common.ts +305 -0
- package/extensions/renderers/documents.ts +326 -0
- package/extensions/renderers/initiatives.ts +344 -0
- package/extensions/renderers/issue-labels.ts +294 -0
- package/extensions/renderers/issue-relations.ts +318 -0
- package/extensions/renderers/issue-statuses.ts +199 -0
- package/extensions/renderers/issues.ts +373 -0
- package/extensions/renderers/milestones.ts +294 -0
- package/extensions/renderers/project-labels.ts +279 -0
- package/extensions/renderers/project-relations.ts +344 -0
- package/extensions/renderers/projects.ts +430 -0
- package/extensions/renderers/state.ts +35 -0
- package/extensions/renderers/teams.ts +246 -0
- package/extensions/renderers/users.ts +242 -0
- package/extensions/renderers/workspaces.ts +44 -0
- package/extensions/settings.ts +53 -23
- package/extensions/tools/comments.ts +17 -0
- package/extensions/tools/documents.ts +23 -0
- package/extensions/tools/initiatives.ts +24 -0
- package/extensions/tools/issue-labels.ts +17 -0
- package/extensions/tools/issue-relations.ts +29 -5
- package/extensions/tools/issue-statuses.ts +6 -0
- package/extensions/tools/issues.ts +29 -0
- package/extensions/tools/milestones.ts +18 -0
- package/extensions/tools/project-labels.ts +17 -0
- package/extensions/tools/project-relations.ts +17 -0
- package/extensions/tools/projects.ts +25 -1
- package/extensions/tools/teams.ts +11 -3
- package/extensions/tools/users.ts +10 -0
- package/extensions/tools/workspaces.ts +6 -0
- package/package.json +1 -1
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type AgentToolResult,
|
|
3
|
+
type Theme,
|
|
4
|
+
type ToolRenderResultOptions,
|
|
5
|
+
} from '@mariozechner/pi-coding-agent';
|
|
6
|
+
import { Text } from '@mariozechner/pi-tui';
|
|
7
|
+
import {
|
|
8
|
+
asString,
|
|
9
|
+
cleanOneLine,
|
|
10
|
+
dimStyle,
|
|
11
|
+
expandedJson,
|
|
12
|
+
shouldShowJson,
|
|
13
|
+
jsonHint,
|
|
14
|
+
LinearListResultComponent,
|
|
15
|
+
mutedStyle,
|
|
16
|
+
renderLinearToolCall,
|
|
17
|
+
renderResponsiveTable,
|
|
18
|
+
toolOutputStyle,
|
|
19
|
+
truncate,
|
|
20
|
+
truncateLine,
|
|
21
|
+
type LinearToolRenderContext,
|
|
22
|
+
type TableColumn,
|
|
23
|
+
type ToolArgs,
|
|
24
|
+
} from './common';
|
|
25
|
+
|
|
26
|
+
type UserLike = {
|
|
27
|
+
id?: string | null;
|
|
28
|
+
name?: string | null;
|
|
29
|
+
displayName?: string | null;
|
|
30
|
+
email?: string | null;
|
|
31
|
+
active?: boolean | null;
|
|
32
|
+
admin?: boolean | null;
|
|
33
|
+
guest?: boolean | null;
|
|
34
|
+
isAssignable?: boolean | null;
|
|
35
|
+
createdAt?: string | null;
|
|
36
|
+
updatedAt?: string | null;
|
|
37
|
+
url?: string | null;
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
type UserResultDetails = {
|
|
41
|
+
user?: UserLike | null;
|
|
42
|
+
users?: UserLike[];
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
const USER_LIST_PREVIEW_LIMIT = 20;
|
|
46
|
+
const NAME_LIMIT = 90;
|
|
47
|
+
const TABLE_NAME_MIN_WIDTH = 24;
|
|
48
|
+
|
|
49
|
+
function userDetails(result: AgentToolResult<any>): UserResultDetails {
|
|
50
|
+
return (result.details ?? {}) as UserResultDetails;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function userName(user: UserLike): string {
|
|
54
|
+
return truncate(
|
|
55
|
+
cleanOneLine(asString(user.name) ?? asString(user.displayName) ?? '(unnamed user)'),
|
|
56
|
+
NAME_LIMIT,
|
|
57
|
+
);
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function displayNameText(user: UserLike): string | undefined {
|
|
61
|
+
const displayName = asString(user.displayName);
|
|
62
|
+
const name = asString(user.name);
|
|
63
|
+
if (!displayName || !name || displayName === name) return undefined;
|
|
64
|
+
return displayName;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
function activeText(user: UserLike): string {
|
|
68
|
+
if (user.active === true) return 'active';
|
|
69
|
+
if (user.active === false) return 'disabled';
|
|
70
|
+
return '—';
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
function roleText(user: UserLike): string {
|
|
74
|
+
if (user.admin === true) return 'admin';
|
|
75
|
+
if (user.guest === true) return 'guest';
|
|
76
|
+
return 'member';
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
function assignableText(user: UserLike): string {
|
|
80
|
+
if (user.isAssignable === true) return 'yes';
|
|
81
|
+
if (user.isAssignable === false) return 'no';
|
|
82
|
+
return '—';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
function metadataParts(user: UserLike): string[] {
|
|
86
|
+
const displayName = displayNameText(user);
|
|
87
|
+
const email = asString(user.email);
|
|
88
|
+
|
|
89
|
+
return [
|
|
90
|
+
displayName ? `display: ${displayName}` : undefined,
|
|
91
|
+
email,
|
|
92
|
+
activeText(user),
|
|
93
|
+
roleText(user),
|
|
94
|
+
`assignable: ${assignableText(user)}`,
|
|
95
|
+
].filter((part): part is string => !!part);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
function formatUserListLine(user: UserLike, theme: Theme, width: number): string {
|
|
99
|
+
const metadata = metadataParts(user);
|
|
100
|
+
const suffix = metadata.length ? theme.fg('dim', ` · ${metadata.join(' · ')}`) : '';
|
|
101
|
+
|
|
102
|
+
return truncateLine(` ${theme.fg('toolOutput', userName(user))}${suffix}`, width);
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
function activeStyle(theme: Theme, value: string): (text: string) => string {
|
|
106
|
+
if (value === 'active') return (text) => theme.fg('success', text);
|
|
107
|
+
if (value === 'disabled') return dimStyle(theme);
|
|
108
|
+
return mutedStyle(theme);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function roleStyle(theme: Theme, value: string): (text: string) => string {
|
|
112
|
+
if (value === 'admin') return (text) => theme.fg('warning', text);
|
|
113
|
+
if (value === 'guest') return mutedStyle(theme);
|
|
114
|
+
return dimStyle(theme);
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function assignableStyle(theme: Theme, value: string): (text: string) => string {
|
|
118
|
+
if (value === 'yes') return (text) => theme.fg('success', text);
|
|
119
|
+
if (value === 'no') return dimStyle(theme);
|
|
120
|
+
return mutedStyle(theme);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
const USER_TABLE_COLUMNS: TableColumn<UserLike>[] = [
|
|
124
|
+
{
|
|
125
|
+
id: 'email',
|
|
126
|
+
label: 'Email',
|
|
127
|
+
width: 30,
|
|
128
|
+
value: (user) => asString(user.email) ?? '—',
|
|
129
|
+
style: (theme) => mutedStyle(theme),
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
id: 'active',
|
|
133
|
+
label: 'Active',
|
|
134
|
+
width: 8,
|
|
135
|
+
value: activeText,
|
|
136
|
+
style: activeStyle,
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
id: 'role',
|
|
140
|
+
label: 'Role',
|
|
141
|
+
width: 7,
|
|
142
|
+
value: roleText,
|
|
143
|
+
style: roleStyle,
|
|
144
|
+
},
|
|
145
|
+
{
|
|
146
|
+
id: 'assignable',
|
|
147
|
+
label: 'Assignable',
|
|
148
|
+
width: 10,
|
|
149
|
+
value: assignableText,
|
|
150
|
+
style: assignableStyle,
|
|
151
|
+
},
|
|
152
|
+
];
|
|
153
|
+
|
|
154
|
+
function renderUserTable(users: UserLike[], theme: Theme, width: number): string[] {
|
|
155
|
+
return renderResponsiveTable(users, theme, width, {
|
|
156
|
+
columns: USER_TABLE_COLUMNS,
|
|
157
|
+
primary: {
|
|
158
|
+
label: 'Name',
|
|
159
|
+
minWidth: TABLE_NAME_MIN_WIDTH,
|
|
160
|
+
value: userName,
|
|
161
|
+
style: (theme) => toolOutputStyle(theme),
|
|
162
|
+
},
|
|
163
|
+
dropOrder: ['assignable', 'role', 'active', 'email'],
|
|
164
|
+
fallback: formatUserListLine,
|
|
165
|
+
});
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function formatUserTitle(user: UserLike, theme: Theme): string {
|
|
169
|
+
const displayName = displayNameText(user);
|
|
170
|
+
const name = userName(user);
|
|
171
|
+
const title = displayName ? `${name} (${truncate(cleanOneLine(displayName), NAME_LIMIT)})` : name;
|
|
172
|
+
return theme.fg('toolOutput', title);
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
function renderUserCard(user: UserLike | null | undefined, theme: Theme): Text {
|
|
176
|
+
if (!user) {
|
|
177
|
+
return new Text(`\n${theme.fg('dim', 'User not found')}\n\n${jsonHint()}`, 0, 0);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const email = asString(user.email);
|
|
181
|
+
const url = asString(user.url);
|
|
182
|
+
const metadata = [
|
|
183
|
+
email,
|
|
184
|
+
activeText(user),
|
|
185
|
+
roleText(user),
|
|
186
|
+
`assignable: ${assignableText(user)}`,
|
|
187
|
+
].filter((part): part is string => !!part);
|
|
188
|
+
|
|
189
|
+
let text = `\n${theme.fg('success', '✓ User')} ${formatUserTitle(user, theme)}`;
|
|
190
|
+
if (metadata.length) text += `\n ${theme.fg('dim', metadata.join(' · '))}`;
|
|
191
|
+
if (url) text += `\n ${theme.fg('dim', url)}`;
|
|
192
|
+
text += `\n\n${jsonHint()}`;
|
|
193
|
+
|
|
194
|
+
return new Text(text, 0, 0);
|
|
195
|
+
}
|
|
196
|
+
|
|
197
|
+
export function renderLinearUserListCall(args: ToolArgs | undefined, theme: Theme): Text {
|
|
198
|
+
return renderLinearToolCall('linear_list_users', args, theme, [
|
|
199
|
+
['first', 'first'],
|
|
200
|
+
['orderBy', 'order'],
|
|
201
|
+
['filter', 'filter'],
|
|
202
|
+
['includeArchived', 'includeArchived'],
|
|
203
|
+
['includeDisabled', 'includeDisabled'],
|
|
204
|
+
]);
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function renderLinearGetUserCall(args: ToolArgs | undefined, theme: Theme): Text {
|
|
208
|
+
return renderLinearToolCall('linear_get_user', args, theme, [['userId', 'userId']]);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
export function renderLinearUserListResult(
|
|
212
|
+
result: AgentToolResult<any>,
|
|
213
|
+
options: ToolRenderResultOptions,
|
|
214
|
+
theme: Theme,
|
|
215
|
+
context: LinearToolRenderContext,
|
|
216
|
+
): Text | LinearListResultComponent<UserLike> {
|
|
217
|
+
if (options.isPartial) return new Text(theme.fg('warning', 'Loading users…'), 0, 0);
|
|
218
|
+
if (shouldShowJson(options, context)) return expandedJson(result, theme);
|
|
219
|
+
|
|
220
|
+
const users = Array.isArray(userDetails(result).users)
|
|
221
|
+
? (userDetails(result).users as UserLike[])
|
|
222
|
+
: [];
|
|
223
|
+
|
|
224
|
+
return new LinearListResultComponent(users, theme, {
|
|
225
|
+
noun: 'user',
|
|
226
|
+
emptyLabel: 'No users found',
|
|
227
|
+
previewLimit: USER_LIST_PREVIEW_LIMIT,
|
|
228
|
+
renderItems: renderUserTable,
|
|
229
|
+
});
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
export function renderLinearUserResult(
|
|
233
|
+
result: AgentToolResult<any>,
|
|
234
|
+
options: ToolRenderResultOptions,
|
|
235
|
+
theme: Theme,
|
|
236
|
+
context: LinearToolRenderContext,
|
|
237
|
+
): Text {
|
|
238
|
+
if (options.isPartial) return new Text(theme.fg('warning', 'Loading user…'), 0, 0);
|
|
239
|
+
if (shouldShowJson(options, context)) return expandedJson(result, theme);
|
|
240
|
+
|
|
241
|
+
return renderUserCard(userDetails(result).user, theme);
|
|
242
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type AgentToolResult,
|
|
3
|
+
type Theme,
|
|
4
|
+
type ToolRenderResultOptions,
|
|
5
|
+
} from '@mariozechner/pi-coding-agent';
|
|
6
|
+
import { Text } from '@mariozechner/pi-tui';
|
|
7
|
+
import {
|
|
8
|
+
asString,
|
|
9
|
+
expandedJson,
|
|
10
|
+
shouldShowJson,
|
|
11
|
+
jsonHint,
|
|
12
|
+
renderLinearToolCall,
|
|
13
|
+
type LinearToolRenderContext,
|
|
14
|
+
type ToolArgs,
|
|
15
|
+
} from './common';
|
|
16
|
+
|
|
17
|
+
type WorkspaceSwitchResultDetails = {
|
|
18
|
+
active?: string | null;
|
|
19
|
+
};
|
|
20
|
+
|
|
21
|
+
function workspaceSwitchDetails(result: AgentToolResult<any>): WorkspaceSwitchResultDetails {
|
|
22
|
+
return (result.details ?? {}) as WorkspaceSwitchResultDetails;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function renderLinearSwitchWorkspaceCall(args: ToolArgs | undefined, theme: Theme): Text {
|
|
26
|
+
return renderLinearToolCall('linear_switch_workspace', args, theme, [['name', 'name']]);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
export function renderLinearSwitchWorkspaceResult(
|
|
30
|
+
result: AgentToolResult<any>,
|
|
31
|
+
options: ToolRenderResultOptions,
|
|
32
|
+
theme: Theme,
|
|
33
|
+
context: LinearToolRenderContext,
|
|
34
|
+
): Text {
|
|
35
|
+
if (options.isPartial) return new Text(theme.fg('warning', 'Switching workspace…'), 0, 0);
|
|
36
|
+
if (shouldShowJson(options, context)) return expandedJson(result, theme);
|
|
37
|
+
|
|
38
|
+
const active = asString(workspaceSwitchDetails(result).active) ?? 'unknown';
|
|
39
|
+
return new Text(
|
|
40
|
+
`\n${theme.fg('success', `✓ Active Linear workspace: ${active}`)}\n\n${jsonHint()}`,
|
|
41
|
+
0,
|
|
42
|
+
0,
|
|
43
|
+
);
|
|
44
|
+
}
|
package/extensions/settings.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { promises as fs } from 'node:fs';
|
|
2
2
|
import { dirname, join } from 'node:path';
|
|
3
3
|
import type { ExtensionAPI, ExtensionContext } from '@mariozechner/pi-coding-agent';
|
|
4
4
|
import { getAgentDir, getSettingsListTheme } from '@mariozechner/pi-coding-agent';
|
|
5
5
|
import { type SettingItem, SettingsList } from '@mariozechner/pi-tui';
|
|
6
|
+
import { invalidateLinearResultRenderers, setDefaultJsonView } from './renderers/state';
|
|
6
7
|
|
|
7
8
|
const SETTINGS_PATH = join(getAgentDir(), 'state', 'extensions', 'linear', 'tool-settings.json');
|
|
8
9
|
const OVERLAY_MAX_INNER = 60;
|
|
@@ -166,33 +167,39 @@ const ALL_LINEAR_TOOLS = TOOL_CATEGORIES.flatMap((c) => c.tools);
|
|
|
166
167
|
|
|
167
168
|
type ToolSettings = {
|
|
168
169
|
disabledTools: string[];
|
|
170
|
+
defaultJsonView: boolean;
|
|
169
171
|
};
|
|
170
172
|
|
|
171
173
|
function createDefaultSettings(): ToolSettings {
|
|
172
|
-
return { disabledTools: [] };
|
|
174
|
+
return { disabledTools: [], defaultJsonView: false };
|
|
173
175
|
}
|
|
174
176
|
|
|
175
|
-
function loadSettings(): ToolSettings {
|
|
176
|
-
if (!existsSync(SETTINGS_PATH)) {
|
|
177
|
-
return createDefaultSettings();
|
|
178
|
-
}
|
|
177
|
+
async function loadSettings(): Promise<ToolSettings> {
|
|
179
178
|
try {
|
|
180
|
-
const raw = JSON.parse(
|
|
179
|
+
const raw = JSON.parse(await fs.readFile(SETTINGS_PATH, 'utf8'));
|
|
181
180
|
if (!raw || typeof raw !== 'object' || !Array.isArray(raw.disabledTools)) {
|
|
182
|
-
|
|
181
|
+
const settings = createDefaultSettings();
|
|
182
|
+
setDefaultJsonView(settings.defaultJsonView);
|
|
183
|
+
return settings;
|
|
183
184
|
}
|
|
184
|
-
|
|
185
|
+
const settings = {
|
|
185
186
|
disabledTools: raw.disabledTools.filter((t: unknown) => typeof t === 'string'),
|
|
187
|
+
defaultJsonView: typeof raw.defaultJsonView === 'boolean' ? raw.defaultJsonView : false,
|
|
186
188
|
};
|
|
189
|
+
setDefaultJsonView(settings.defaultJsonView);
|
|
190
|
+
return settings;
|
|
187
191
|
} catch {
|
|
188
|
-
|
|
192
|
+
const settings = createDefaultSettings();
|
|
193
|
+
setDefaultJsonView(settings.defaultJsonView);
|
|
194
|
+
return settings;
|
|
189
195
|
}
|
|
190
196
|
}
|
|
191
197
|
|
|
192
|
-
function saveSettings(settings: ToolSettings): boolean {
|
|
198
|
+
async function saveSettings(settings: ToolSettings): Promise<boolean> {
|
|
193
199
|
try {
|
|
194
|
-
|
|
195
|
-
|
|
200
|
+
setDefaultJsonView(settings.defaultJsonView);
|
|
201
|
+
await fs.mkdir(dirname(SETTINGS_PATH), { recursive: true });
|
|
202
|
+
await fs.writeFile(SETTINGS_PATH, `${JSON.stringify(settings, null, 2)}\n`, 'utf8');
|
|
196
203
|
return true;
|
|
197
204
|
} catch {
|
|
198
205
|
return false;
|
|
@@ -259,8 +266,21 @@ function frameBody(title: string, bodyLines: string[], inner: number): string[]
|
|
|
259
266
|
return [top, ...framedBody, bottom];
|
|
260
267
|
}
|
|
261
268
|
|
|
269
|
+
function defaultOutputViewValue(settings: ToolSettings): string {
|
|
270
|
+
return settings.defaultJsonView ? 'Full JSON' : 'Human readable';
|
|
271
|
+
}
|
|
272
|
+
|
|
262
273
|
function buildItems(settings: ToolSettings): SettingItem[] {
|
|
263
|
-
const items: SettingItem[] = [
|
|
274
|
+
const items: SettingItem[] = [
|
|
275
|
+
{
|
|
276
|
+
id: 'defaultJsonView',
|
|
277
|
+
label: 'Default output view',
|
|
278
|
+
description:
|
|
279
|
+
'Controls Linear tool result display. Ctrl+O toggles the other view per tool call.',
|
|
280
|
+
currentValue: defaultOutputViewValue(settings),
|
|
281
|
+
values: ['Human readable', 'Full JSON'],
|
|
282
|
+
},
|
|
283
|
+
];
|
|
264
284
|
for (const category of TOOL_CATEGORIES) {
|
|
265
285
|
items.push({
|
|
266
286
|
id: `category:${category.id}`,
|
|
@@ -311,7 +331,7 @@ async function showToolSettingsOverlay(
|
|
|
311
331
|
() => {},
|
|
312
332
|
);
|
|
313
333
|
const probeLines = probeList.render(Math.max(8, OVERLAY_MAX_INNER - 2));
|
|
314
|
-
const overlayBodyLines = ['
|
|
334
|
+
const overlayBodyLines = ['Configure Linear output and enabled tools', '', ...probeLines];
|
|
315
335
|
const overlayWidth = computeOverlayInner(overlayBodyLines, OVERLAY_MAX_INNER + 2) + 2;
|
|
316
336
|
|
|
317
337
|
await ctx.ui.custom(
|
|
@@ -320,7 +340,17 @@ async function showToolSettingsOverlay(
|
|
|
320
340
|
items,
|
|
321
341
|
maxVisibleItems,
|
|
322
342
|
settingsTheme,
|
|
323
|
-
(id, newValue) => {
|
|
343
|
+
async (id, newValue) => {
|
|
344
|
+
if (id === 'defaultJsonView') {
|
|
345
|
+
const previousDefaultJsonView = settings.defaultJsonView;
|
|
346
|
+
settings.defaultJsonView = newValue === 'Full JSON';
|
|
347
|
+
await saveSettings(settings);
|
|
348
|
+
if (settings.defaultJsonView !== previousDefaultJsonView) {
|
|
349
|
+
invalidateLinearResultRenderers();
|
|
350
|
+
}
|
|
351
|
+
return;
|
|
352
|
+
}
|
|
353
|
+
|
|
324
354
|
const nextEnabled = newValue === '[x]';
|
|
325
355
|
|
|
326
356
|
if (id.startsWith('category:')) {
|
|
@@ -354,7 +384,7 @@ async function showToolSettingsOverlay(
|
|
|
354
384
|
}
|
|
355
385
|
}
|
|
356
386
|
|
|
357
|
-
saveSettings(settings);
|
|
387
|
+
await saveSettings(settings);
|
|
358
388
|
applySettings(pi, settings);
|
|
359
389
|
},
|
|
360
390
|
() => done(undefined),
|
|
@@ -367,7 +397,7 @@ async function showToolSettingsOverlay(
|
|
|
367
397
|
const provisionalInner = Math.max(24, Math.min(safeWidth - 2, OVERLAY_MAX_INNER));
|
|
368
398
|
const listLines = settingsList.render(Math.max(8, provisionalInner - 2));
|
|
369
399
|
const bodyLines = [
|
|
370
|
-
theme.fg('muted', '
|
|
400
|
+
theme.fg('muted', 'Configure Linear output and enabled tools'),
|
|
371
401
|
'',
|
|
372
402
|
...listLines,
|
|
373
403
|
];
|
|
@@ -392,24 +422,24 @@ async function showToolSettingsOverlay(
|
|
|
392
422
|
);
|
|
393
423
|
}
|
|
394
424
|
|
|
395
|
-
export function registerLinearSettings(pi: ExtensionAPI): void {
|
|
396
|
-
let settings = loadSettings();
|
|
425
|
+
export async function registerLinearSettings(pi: ExtensionAPI): Promise<void> {
|
|
426
|
+
let settings = await loadSettings();
|
|
397
427
|
|
|
398
428
|
pi.registerCommand('linear-settings', {
|
|
399
429
|
description: 'Open Linear tool settings',
|
|
400
430
|
handler: async (_args, ctx) => {
|
|
401
|
-
settings = loadSettings();
|
|
431
|
+
settings = await loadSettings();
|
|
402
432
|
await showToolSettingsOverlay(pi, ctx, settings);
|
|
403
433
|
},
|
|
404
434
|
});
|
|
405
435
|
|
|
406
436
|
pi.on('session_start', async (_event, _ctx) => {
|
|
407
|
-
settings = loadSettings();
|
|
437
|
+
settings = await loadSettings();
|
|
408
438
|
applySettings(pi, settings);
|
|
409
439
|
});
|
|
410
440
|
|
|
411
441
|
pi.on('session_before_switch', async (_event, _ctx) => {
|
|
412
|
-
settings = loadSettings();
|
|
442
|
+
settings = await loadSettings();
|
|
413
443
|
applySettings(pi, settings);
|
|
414
444
|
});
|
|
415
445
|
}
|
|
@@ -5,6 +5,15 @@ import { PaginationParams, FilterParam, RawInputParam } from '../params';
|
|
|
5
5
|
import { COMMENT_SELECTION } from '../selections';
|
|
6
6
|
import type { JsonObject } from '../types';
|
|
7
7
|
import { compactObject, asObject, asString, GenericObjectSchema } from '../util';
|
|
8
|
+
import {
|
|
9
|
+
renderLinearCommentListCall,
|
|
10
|
+
renderLinearCommentListResult,
|
|
11
|
+
renderLinearCommentResult,
|
|
12
|
+
renderLinearCreateCommentCall,
|
|
13
|
+
renderLinearDeleteCommentCall,
|
|
14
|
+
renderLinearDeleteCommentResult,
|
|
15
|
+
renderLinearUpdateCommentCall,
|
|
16
|
+
} from '../renderers/comments';
|
|
8
17
|
|
|
9
18
|
export function commentTools() {
|
|
10
19
|
return [
|
|
@@ -16,6 +25,7 @@ export function commentTools() {
|
|
|
16
25
|
...PaginationParams,
|
|
17
26
|
...FilterParam,
|
|
18
27
|
}),
|
|
28
|
+
renderCall: renderLinearCommentListCall,
|
|
19
29
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
20
30
|
return withLinearAuth(ctx, signal, async (apiKey) => {
|
|
21
31
|
const variables = compactObject({
|
|
@@ -66,6 +76,7 @@ export function commentTools() {
|
|
|
66
76
|
};
|
|
67
77
|
});
|
|
68
78
|
},
|
|
79
|
+
renderResult: renderLinearCommentListResult,
|
|
69
80
|
}),
|
|
70
81
|
defineTool({
|
|
71
82
|
name: 'linear_create_comment',
|
|
@@ -90,6 +101,7 @@ export function commentTools() {
|
|
|
90
101
|
subscriberIds: Type.Optional(Type.Array(Type.String())),
|
|
91
102
|
...RawInputParam,
|
|
92
103
|
}),
|
|
104
|
+
renderCall: renderLinearCreateCommentCall,
|
|
93
105
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
94
106
|
return withLinearAuth(ctx, signal, async (apiKey) => {
|
|
95
107
|
const rawInput = asObject(params.input) || {};
|
|
@@ -146,6 +158,7 @@ export function commentTools() {
|
|
|
146
158
|
};
|
|
147
159
|
});
|
|
148
160
|
},
|
|
161
|
+
renderResult: renderLinearCommentResult('Created comment'),
|
|
149
162
|
}),
|
|
150
163
|
defineTool({
|
|
151
164
|
name: 'linear_update_comment',
|
|
@@ -157,6 +170,7 @@ export function commentTools() {
|
|
|
157
170
|
quotedText: Type.Optional(Type.String()),
|
|
158
171
|
...RawInputParam,
|
|
159
172
|
}),
|
|
173
|
+
renderCall: renderLinearUpdateCommentCall,
|
|
160
174
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
161
175
|
return withLinearAuth(ctx, signal, async (apiKey) => {
|
|
162
176
|
const rawInput = asObject(params.input) || {};
|
|
@@ -199,6 +213,7 @@ export function commentTools() {
|
|
|
199
213
|
};
|
|
200
214
|
});
|
|
201
215
|
},
|
|
216
|
+
renderResult: renderLinearCommentResult('Updated comment'),
|
|
202
217
|
}),
|
|
203
218
|
defineTool({
|
|
204
219
|
name: 'linear_delete_comment',
|
|
@@ -207,6 +222,7 @@ export function commentTools() {
|
|
|
207
222
|
parameters: Type.Object({
|
|
208
223
|
id: Type.String(),
|
|
209
224
|
}),
|
|
225
|
+
renderCall: renderLinearDeleteCommentCall,
|
|
210
226
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
211
227
|
return withLinearAuth(ctx, signal, async (apiKey) => {
|
|
212
228
|
const data = await linearGraphQL<{
|
|
@@ -232,6 +248,7 @@ export function commentTools() {
|
|
|
232
248
|
};
|
|
233
249
|
});
|
|
234
250
|
},
|
|
251
|
+
renderResult: renderLinearDeleteCommentResult,
|
|
235
252
|
}),
|
|
236
253
|
];
|
|
237
254
|
}
|
|
@@ -5,6 +5,17 @@ import { PaginationParams, FilterParam, RawInputParam, TeamConvenienceParams } f
|
|
|
5
5
|
import { DOCUMENT_SELECTION } from '../selections';
|
|
6
6
|
import type { JsonObject } from '../types';
|
|
7
7
|
import { compactObject, asObject, asString } from '../util';
|
|
8
|
+
import {
|
|
9
|
+
renderLinearCreateDocumentCall,
|
|
10
|
+
renderLinearDeleteDocumentCall,
|
|
11
|
+
renderLinearDocumentListCall,
|
|
12
|
+
renderLinearDocumentListResult,
|
|
13
|
+
renderLinearDocumentResult,
|
|
14
|
+
renderLinearDocumentSuccessResult,
|
|
15
|
+
renderLinearGetDocumentCall,
|
|
16
|
+
renderLinearUnarchiveDocumentCall,
|
|
17
|
+
renderLinearUpdateDocumentCall,
|
|
18
|
+
} from '../renderers/documents';
|
|
8
19
|
|
|
9
20
|
export function documentTools() {
|
|
10
21
|
return [
|
|
@@ -16,6 +27,7 @@ export function documentTools() {
|
|
|
16
27
|
...PaginationParams,
|
|
17
28
|
...FilterParam,
|
|
18
29
|
}),
|
|
30
|
+
renderCall: renderLinearDocumentListCall,
|
|
19
31
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
20
32
|
return withLinearAuth(ctx, signal, async (apiKey) => {
|
|
21
33
|
const variables = compactObject({
|
|
@@ -66,6 +78,7 @@ export function documentTools() {
|
|
|
66
78
|
};
|
|
67
79
|
});
|
|
68
80
|
},
|
|
81
|
+
renderResult: renderLinearDocumentListResult,
|
|
69
82
|
}),
|
|
70
83
|
defineTool({
|
|
71
84
|
name: 'linear_get_document',
|
|
@@ -74,6 +87,7 @@ export function documentTools() {
|
|
|
74
87
|
parameters: Type.Object({
|
|
75
88
|
documentId: Type.String(),
|
|
76
89
|
}),
|
|
90
|
+
renderCall: renderLinearGetDocumentCall,
|
|
77
91
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
78
92
|
return withLinearAuth(ctx, signal, async (apiKey) => {
|
|
79
93
|
const data = await linearGraphQL<{ document: JsonObject | null }>(
|
|
@@ -96,6 +110,7 @@ export function documentTools() {
|
|
|
96
110
|
};
|
|
97
111
|
});
|
|
98
112
|
},
|
|
113
|
+
renderResult: renderLinearDocumentResult('Document'),
|
|
99
114
|
}),
|
|
100
115
|
defineTool({
|
|
101
116
|
name: 'linear_create_document',
|
|
@@ -120,6 +135,7 @@ export function documentTools() {
|
|
|
120
135
|
title: Type.Optional(Type.String()),
|
|
121
136
|
...RawInputParam,
|
|
122
137
|
}),
|
|
138
|
+
renderCall: renderLinearCreateDocumentCall,
|
|
123
139
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
124
140
|
return withLinearAuth(ctx, signal, async (apiKey) => {
|
|
125
141
|
const rawInput = asObject(params.input) || {};
|
|
@@ -188,6 +204,7 @@ export function documentTools() {
|
|
|
188
204
|
};
|
|
189
205
|
});
|
|
190
206
|
},
|
|
207
|
+
renderResult: renderLinearDocumentResult('Created document'),
|
|
191
208
|
}),
|
|
192
209
|
defineTool({
|
|
193
210
|
name: 'linear_update_document',
|
|
@@ -214,6 +231,7 @@ export function documentTools() {
|
|
|
214
231
|
trashed: Type.Optional(Type.Boolean()),
|
|
215
232
|
...RawInputParam,
|
|
216
233
|
}),
|
|
234
|
+
renderCall: renderLinearUpdateDocumentCall,
|
|
217
235
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
218
236
|
return withLinearAuth(ctx, signal, async (apiKey) => {
|
|
219
237
|
const rawInput = asObject(params.input) || {};
|
|
@@ -286,6 +304,7 @@ export function documentTools() {
|
|
|
286
304
|
};
|
|
287
305
|
});
|
|
288
306
|
},
|
|
307
|
+
renderResult: renderLinearDocumentResult('Updated document'),
|
|
289
308
|
}),
|
|
290
309
|
defineTool({
|
|
291
310
|
name: 'linear_delete_document',
|
|
@@ -294,6 +313,7 @@ export function documentTools() {
|
|
|
294
313
|
parameters: Type.Object({
|
|
295
314
|
documentId: Type.String(),
|
|
296
315
|
}),
|
|
316
|
+
renderCall: renderLinearDeleteDocumentCall,
|
|
297
317
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
298
318
|
return withLinearAuth(ctx, signal, async (apiKey) => {
|
|
299
319
|
const data = await linearGraphQL<{
|
|
@@ -319,6 +339,7 @@ export function documentTools() {
|
|
|
319
339
|
};
|
|
320
340
|
});
|
|
321
341
|
},
|
|
342
|
+
renderResult: renderLinearDocumentSuccessResult('Deleted'),
|
|
322
343
|
}),
|
|
323
344
|
defineTool({
|
|
324
345
|
name: 'linear_unarchive_document',
|
|
@@ -327,6 +348,7 @@ export function documentTools() {
|
|
|
327
348
|
parameters: Type.Object({
|
|
328
349
|
documentId: Type.String(),
|
|
329
350
|
}),
|
|
351
|
+
renderCall: renderLinearUnarchiveDocumentCall,
|
|
330
352
|
async execute(_toolCallId, params, signal, _onUpdate, ctx) {
|
|
331
353
|
return withLinearAuth(ctx, signal, async (apiKey) => {
|
|
332
354
|
const data = await linearGraphQL<{
|
|
@@ -352,6 +374,7 @@ export function documentTools() {
|
|
|
352
374
|
};
|
|
353
375
|
});
|
|
354
376
|
},
|
|
377
|
+
renderResult: renderLinearDocumentSuccessResult('Unarchived'),
|
|
355
378
|
}),
|
|
356
379
|
];
|
|
357
380
|
}
|