@fink-andreas/pi-linear-tools 0.4.2 → 0.4.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/CHANGELOG.md +16 -0
- package/README.md +131 -3
- package/extensions/pi-linear-tools.js +180 -10
- package/index.js +1 -1019
- package/package.json +2 -2
- package/src/cli.js +700 -6
- package/src/handlers.js +403 -0
- package/src/linear.js +1218 -9
- package/src/sync-doc.js +1208 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v0.4.3 (2026-04-02)
|
|
4
|
+
|
|
5
|
+
Sync-doc parity and tool prompt metadata polish release.
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
- Fixed first-run `sync-doc` batch parity so `check` and `run` produce aligned project index planning
|
|
9
|
+
- Preserved existing managed marker location when `position` is `top`
|
|
10
|
+
- Resolved archived projects more reliably during project update flows
|
|
11
|
+
|
|
12
|
+
### Improvements
|
|
13
|
+
- Added `promptSnippet` metadata consistently across all Linear tools for better in-app tool discoverability/context
|
|
14
|
+
- Added default activity limit handling for issue activity execution paths
|
|
15
|
+
|
|
16
|
+
### Tests
|
|
17
|
+
- Added/updated sync-doc regression coverage for first-run check/run parity and marker placement stability
|
|
18
|
+
|
|
3
19
|
## v0.4.2 (2026-03-26)
|
|
4
20
|
|
|
5
21
|
Rate-limit resilience and API usage diagnostics release.
|
package/README.md
CHANGED
|
@@ -2,6 +2,20 @@
|
|
|
2
2
|
|
|
3
3
|
`pi-linear-tools` is a Pi extension for the [Pi coding agent](https://github.com/badlogic/pi-mono) that lets you manage [Linear](https://linear.app/about) issues, projects, and milestones via LLM tools and CLI commands.
|
|
4
4
|
|
|
5
|
+
Useful mental model:
|
|
6
|
+
- `issue update` changes issue fields; `issue comment` adds discussion; `issue activity` reads the Activity timeline
|
|
7
|
+
- `project update` changes project fields; `project-update` manages Updates tab entries
|
|
8
|
+
- `project-update` maps to Linear project updates in the Updates tab
|
|
9
|
+
- `sync-doc init` scaffolds `.linear-tools/config.json` in the folder you point at
|
|
10
|
+
- `sync-doc run` and `sync-doc check` default to all configured targets in `.linear-tools/config.json`
|
|
11
|
+
- `sync-doc --target X` narrows the operation to one configured target
|
|
12
|
+
|
|
13
|
+
Reference conventions:
|
|
14
|
+
- issues use issue key or issue ID
|
|
15
|
+
- projects use project name or project ID
|
|
16
|
+
- project updates use project update ID
|
|
17
|
+
- milestones use milestone ID
|
|
18
|
+
|
|
5
19
|
## Install
|
|
6
20
|
|
|
7
21
|
### As a pi package (recommended)
|
|
@@ -43,10 +57,13 @@ Optional non-interactive commands:
|
|
|
43
57
|
## LLM-callable tools
|
|
44
58
|
|
|
45
59
|
### `linear_issue`
|
|
46
|
-
Actions: `list`, `view`, `create`, `update`, `comment`, `start`, `delete`
|
|
60
|
+
Actions: `list`, `view`, `activity`, `create`, `update`, `comment`, `start`, `delete`
|
|
47
61
|
|
|
48
62
|
### `linear_project`
|
|
49
|
-
Actions: `list`
|
|
63
|
+
Actions: `list`, `view`, `create`, `update`, `delete`, `archive`, `unarchive`
|
|
64
|
+
|
|
65
|
+
### `linear_project_update`
|
|
66
|
+
Actions: `list`, `view`, `create`, `update`, `archive`, `unarchive`
|
|
50
67
|
|
|
51
68
|
### `linear_milestone`
|
|
52
69
|
Actions: `list`, `view`, `create`, `update`, `delete`
|
|
@@ -57,6 +74,12 @@ If installed globally via ```npm install -g @fink-andreas/pi-linear-tools```, CL
|
|
|
57
74
|
|
|
58
75
|
```bash
|
|
59
76
|
pi-linear-tools --help
|
|
77
|
+
pi-linear-tools issue --help
|
|
78
|
+
pi-linear-tools project --help
|
|
79
|
+
pi-linear-tools project-update --help
|
|
80
|
+
pi-linear-tools sync-doc --help
|
|
81
|
+
pi-linear-tools sync-doc init --cwd /path/to/subproject --project "Project name or ID"
|
|
82
|
+
pi-linear-tools sync-doc explain
|
|
60
83
|
pi-linear-tools config
|
|
61
84
|
pi-linear-tools config --api-key lin_xxx
|
|
62
85
|
pi-linear-tools config --default-team ENG
|
|
@@ -65,6 +88,8 @@ pi-linear-tools config --team ENG --project "My Project"
|
|
|
65
88
|
|
|
66
89
|
### Issue commands
|
|
67
90
|
|
|
91
|
+
Use `update` to change the issue itself. Use `comment` to add discussion. Use `activity` to read the Activity timeline shown in Linear.
|
|
92
|
+
|
|
68
93
|
```bash
|
|
69
94
|
# List issues
|
|
70
95
|
pi-linear-tools issue list --project "My Project"
|
|
@@ -75,6 +100,10 @@ pi-linear-tools issue list --project "My Project" --assignee me
|
|
|
75
100
|
pi-linear-tools issue view ENG-123
|
|
76
101
|
pi-linear-tools issue view ENG-123 --no-comments
|
|
77
102
|
|
|
103
|
+
# Read Activity timeline
|
|
104
|
+
pi-linear-tools issue activity ENG-123 --limit 20
|
|
105
|
+
pi-linear-tools issue activity https://linear.app/workspace/issue/ENG-123/example --limit 20
|
|
106
|
+
|
|
78
107
|
# Create issue
|
|
79
108
|
pi-linear-tools issue create --title "Fix login bug" --team ENG
|
|
80
109
|
pi-linear-tools issue create --title "New feature" --team ENG --project "My Project" --priority 2 --assignee me
|
|
@@ -83,13 +112,15 @@ pi-linear-tools issue create --title "New feature" --team ENG --project "My Proj
|
|
|
83
112
|
pi-linear-tools issue update ENG-123 --state "In Progress"
|
|
84
113
|
pi-linear-tools issue update ENG-123 --title "Updated title" --assignee me
|
|
85
114
|
pi-linear-tools issue update ENG-123 --milestone "Sprint 1"
|
|
115
|
+
pi-linear-tools issue update ENG-123 --sub-issue-of ENG-100
|
|
86
116
|
|
|
87
117
|
# Comment on issue
|
|
88
118
|
pi-linear-tools issue comment ENG-123 --body "This is fixed in PR #456"
|
|
119
|
+
pi-linear-tools issue comment ENG-123 --body "Blocked on API review"
|
|
89
120
|
|
|
90
121
|
# Start working on issue (creates branch, sets state to In Progress)
|
|
91
122
|
pi-linear-tools issue start ENG-123
|
|
92
|
-
pi-linear-tools issue start ENG-123 --
|
|
123
|
+
pi-linear-tools issue start ENG-123 --from-ref main --on-branch-exists suffix
|
|
93
124
|
|
|
94
125
|
# Delete issue
|
|
95
126
|
pi-linear-tools issue delete ENG-123
|
|
@@ -97,10 +128,107 @@ pi-linear-tools issue delete ENG-123
|
|
|
97
128
|
|
|
98
129
|
### Project commands
|
|
99
130
|
|
|
131
|
+
Use `project update` to change the project record itself: name, teams, dates, lead, description, priority, color, or icon.
|
|
132
|
+
|
|
100
133
|
```bash
|
|
101
134
|
pi-linear-tools project list
|
|
135
|
+
pi-linear-tools project view "My Project"
|
|
136
|
+
pi-linear-tools project create --name "Roadmap Refresh" --teams ENG,OPS --lead me
|
|
137
|
+
pi-linear-tools project update "Roadmap Refresh" --description "Updated scope" --target-date 2026-06-30
|
|
138
|
+
pi-linear-tools project update "Roadmap Refresh" --lead me --teams ENG,OPS
|
|
139
|
+
pi-linear-tools project delete "Roadmap Refresh"
|
|
140
|
+
pi-linear-tools project archive "Roadmap Refresh"
|
|
141
|
+
pi-linear-tools project unarchive 11111111-1111-4111-8111-111111111111
|
|
102
142
|
```
|
|
103
143
|
|
|
144
|
+
### Project update commands
|
|
145
|
+
|
|
146
|
+
This maps to the Updates tab inside a Linear project. Create an update by project name or ID, then use the returned project update ID for later view, update, archive, or unarchive actions.
|
|
147
|
+
|
|
148
|
+
```bash
|
|
149
|
+
pi-linear-tools project-update list --project "My Project"
|
|
150
|
+
pi-linear-tools project-update view 22222222-2222-4222-8222-222222222222
|
|
151
|
+
pi-linear-tools project-update create --project "My Project" --body "Weekly progress update" --health onTrack
|
|
152
|
+
pi-linear-tools project-update update 22222222-2222-4222-8222-222222222222 --body "Revised update" --health atRisk
|
|
153
|
+
pi-linear-tools project-update archive 22222222-2222-4222-8222-222222222222
|
|
154
|
+
pi-linear-tools project-update unarchive 22222222-2222-4222-8222-222222222222
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
### Sync doc commands
|
|
158
|
+
|
|
159
|
+
```bash
|
|
160
|
+
pi-linear-tools sync-doc init --cwd /path/to/subproject --project "Project name or ID"
|
|
161
|
+
pi-linear-tools sync-doc explain
|
|
162
|
+
pi-linear-tools sync-doc list
|
|
163
|
+
pi-linear-tools sync-doc run
|
|
164
|
+
pi-linear-tools sync-doc check
|
|
165
|
+
pi-linear-tools sync-doc run --target project-overview
|
|
166
|
+
pi-linear-tools sync-doc check --target project-overview
|
|
167
|
+
pi-linear-tools sync-doc run --file README.md --project "Project name or ID" --field content
|
|
168
|
+
pi-linear-tools sync-doc run --file docs/provider.md --project "Project name or ID" --target-type document --document-title "Provider Doc"
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
Use one project overview target for the project body, then sync deeper docs as separate Linear documents.
|
|
172
|
+
|
|
173
|
+
Use `pi-linear-tools sync-doc init --cwd /path/to/subproject --project "Project name or ID"` to scaffold a starter config.
|
|
174
|
+
|
|
175
|
+
Placement rule:
|
|
176
|
+
- put `.linear-tools/config.json` in the smallest folder that owns the docs you are syncing
|
|
177
|
+
- the CLI resolves the nearest `.linear-tools/config.json` upward from the current directory
|
|
178
|
+
- use repo root only for targets intentionally shared across multiple subprojects
|
|
179
|
+
- `~/.linear-tools/config.json` is supported for personal defaults, but it should be treated as an override layer, not the main source of truth for repo-owned sync targets
|
|
180
|
+
|
|
181
|
+
Recommended pattern for multiple docs:
|
|
182
|
+
- one `projectField` target syncs the project overview into `project.content` or `project.description`
|
|
183
|
+
- deeper docs use `targetType: "document"` so each file gets its own Linear document
|
|
184
|
+
- the project overview target can set `documentIndexMarker` to maintain a managed list of links to those documents
|
|
185
|
+
|
|
186
|
+
For repos with multiple sync targets, the normal workflow is:
|
|
187
|
+
- define them in the repo-root `.linear-tools/config.json`
|
|
188
|
+
- run `pi-linear-tools sync-doc run` to push everything
|
|
189
|
+
- run `pi-linear-tools sync-doc check` in CI or before updates if you want drift visibility
|
|
190
|
+
|
|
191
|
+
Example:
|
|
192
|
+
|
|
193
|
+
```json
|
|
194
|
+
{
|
|
195
|
+
"syncDocs": {
|
|
196
|
+
"targets": [
|
|
197
|
+
{
|
|
198
|
+
"name": "project-overview",
|
|
199
|
+
"file": "README.md",
|
|
200
|
+
"project": "Project name or ID",
|
|
201
|
+
"field": "content",
|
|
202
|
+
"marker": "project-overview",
|
|
203
|
+
"documentIndexMarker": "project-doc-links"
|
|
204
|
+
},
|
|
205
|
+
{
|
|
206
|
+
"name": "provider-foo",
|
|
207
|
+
"targetType": "document",
|
|
208
|
+
"file": "docs/provider.md",
|
|
209
|
+
"project": "Project name or ID",
|
|
210
|
+
"title": "Provider Doc",
|
|
211
|
+
"marker": "provider-foo",
|
|
212
|
+
"documentId": "optional-existing-document-id"
|
|
213
|
+
}
|
|
214
|
+
]
|
|
215
|
+
}
|
|
216
|
+
}
|
|
217
|
+
```
|
|
218
|
+
|
|
219
|
+
Store that config at `.linear-tools/config.json`. Sync state is written to `.linear-tools/sync-state.json`.
|
|
220
|
+
If `documentId` is omitted on a document target, the first sync creates a new Linear document and stores the created ID in sync state.
|
|
221
|
+
|
|
222
|
+
Managed content is wrapped in marker comments inside the target Linear field so manual content above or below the managed block is preserved:
|
|
223
|
+
|
|
224
|
+
```md
|
|
225
|
+
<!-- linear-tools:sync-start README -->
|
|
226
|
+
...synced markdown...
|
|
227
|
+
<!-- linear-tools:sync-end README -->
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
When `documentIndexMarker` is configured on the overview target, the project field also gets a second managed block containing links to the synced Linear documents.
|
|
231
|
+
|
|
104
232
|
### Team commands
|
|
105
233
|
|
|
106
234
|
```bash
|
|
@@ -48,12 +48,25 @@ try {
|
|
|
48
48
|
import {
|
|
49
49
|
executeIssueList,
|
|
50
50
|
executeIssueView,
|
|
51
|
+
executeIssueActivity,
|
|
51
52
|
executeIssueCreate,
|
|
52
53
|
executeIssueUpdate,
|
|
53
54
|
executeIssueComment,
|
|
54
55
|
executeIssueStart,
|
|
55
56
|
executeIssueDelete,
|
|
56
57
|
executeProjectList,
|
|
58
|
+
executeProjectView,
|
|
59
|
+
executeProjectCreate,
|
|
60
|
+
executeProjectUpdate,
|
|
61
|
+
executeProjectDelete,
|
|
62
|
+
executeProjectArchive,
|
|
63
|
+
executeProjectUnarchive,
|
|
64
|
+
executeProjectUpdateList,
|
|
65
|
+
executeProjectUpdateView,
|
|
66
|
+
executeProjectUpdateCreate,
|
|
67
|
+
executeProjectUpdateUpdate,
|
|
68
|
+
executeProjectUpdateArchive,
|
|
69
|
+
executeProjectUpdateUnarchive,
|
|
57
70
|
executeTeamList,
|
|
58
71
|
executeMilestoneList,
|
|
59
72
|
executeMilestoneView,
|
|
@@ -478,18 +491,19 @@ async function registerLinearTools(pi) {
|
|
|
478
491
|
pi.registerTool({
|
|
479
492
|
name: 'linear_issue',
|
|
480
493
|
label: 'Linear Issue',
|
|
481
|
-
description: 'Interact with Linear issues. Actions: list, view, create, update, comment, start, delete',
|
|
494
|
+
description: 'Interact with Linear issues. Actions: list, view, activity, create, update, comment, start, delete',
|
|
495
|
+
promptSnippet: 'Interact with Linear issues (list, view, activity, create, update, comment, start, delete)',
|
|
482
496
|
parameters: {
|
|
483
497
|
type: 'object',
|
|
484
498
|
properties: {
|
|
485
499
|
action: {
|
|
486
500
|
type: 'string',
|
|
487
|
-
enum: ['list', 'view', 'create', 'update', 'comment', 'start', 'delete'],
|
|
501
|
+
enum: ['list', 'view', 'activity', 'create', 'update', 'comment', 'start', 'delete'],
|
|
488
502
|
description: 'Action to perform on issue(s)',
|
|
489
503
|
},
|
|
490
504
|
issue: {
|
|
491
505
|
type: 'string',
|
|
492
|
-
description: 'Issue key (ABC-123) or Linear issue ID (for view, update, comment, start, delete)',
|
|
506
|
+
description: 'Issue key (ABC-123) or Linear issue ID (for view, activity, update, comment, start, delete)',
|
|
493
507
|
},
|
|
494
508
|
project: {
|
|
495
509
|
type: 'string',
|
|
@@ -509,8 +523,8 @@ async function registerLinearTools(pi) {
|
|
|
509
523
|
description: 'Optional explicit assignee ID alias for update/create debugging/compatibility.',
|
|
510
524
|
},
|
|
511
525
|
limit: {
|
|
512
|
-
type: '
|
|
513
|
-
description: 'Maximum number of issues to list
|
|
526
|
+
type: 'integer',
|
|
527
|
+
description: 'Maximum number of issues or activity entries to list',
|
|
514
528
|
},
|
|
515
529
|
includeComments: {
|
|
516
530
|
type: 'boolean',
|
|
@@ -525,9 +539,13 @@ async function registerLinearTools(pi) {
|
|
|
525
539
|
description: 'Issue description in markdown (for create, update)',
|
|
526
540
|
},
|
|
527
541
|
priority: {
|
|
528
|
-
type: '
|
|
542
|
+
type: 'integer',
|
|
529
543
|
description: 'Priority 0..4 (for create, update)',
|
|
530
544
|
},
|
|
545
|
+
includeArchived: {
|
|
546
|
+
type: 'boolean',
|
|
547
|
+
description: 'Include archived resources when listing activity or project updates',
|
|
548
|
+
},
|
|
531
549
|
state: {
|
|
532
550
|
type: 'string',
|
|
533
551
|
description: 'Target state name or ID (for create, update)',
|
|
@@ -618,6 +636,8 @@ async function registerLinearTools(pi) {
|
|
|
618
636
|
return await executeIssueList(client, params);
|
|
619
637
|
case 'view':
|
|
620
638
|
return await executeIssueView(client, params);
|
|
639
|
+
case 'activity':
|
|
640
|
+
return await executeIssueActivity(client, params);
|
|
621
641
|
case 'create':
|
|
622
642
|
return await executeIssueCreate(client, params, { resolveDefaultTeam });
|
|
623
643
|
case 'update':
|
|
@@ -692,15 +712,59 @@ async function registerLinearTools(pi) {
|
|
|
692
712
|
pi.registerTool({
|
|
693
713
|
name: 'linear_project',
|
|
694
714
|
label: 'Linear Project',
|
|
695
|
-
description: 'Interact with Linear projects. Actions: list',
|
|
715
|
+
description: 'Interact with Linear projects. Actions: list, view, create, update, delete, archive, unarchive',
|
|
716
|
+
promptSnippet: 'Interact with Linear projects (list, view, create, update, delete, archive, unarchive)',
|
|
696
717
|
parameters: {
|
|
697
718
|
type: 'object',
|
|
698
719
|
properties: {
|
|
699
720
|
action: {
|
|
700
721
|
type: 'string',
|
|
701
|
-
enum: ['list'],
|
|
722
|
+
enum: ['list', 'view', 'create', 'update', 'delete', 'archive', 'unarchive'],
|
|
702
723
|
description: 'Action to perform on project(s)',
|
|
703
724
|
},
|
|
725
|
+
project: {
|
|
726
|
+
type: 'string',
|
|
727
|
+
description: 'Project name or ID (for view, update, delete)',
|
|
728
|
+
},
|
|
729
|
+
name: {
|
|
730
|
+
type: 'string',
|
|
731
|
+
description: 'Project name (required for create, optional for update)',
|
|
732
|
+
},
|
|
733
|
+
teams: {
|
|
734
|
+
type: 'string',
|
|
735
|
+
description: 'Comma-separated team keys or IDs (required for create, optional for update)',
|
|
736
|
+
},
|
|
737
|
+
description: {
|
|
738
|
+
type: 'string',
|
|
739
|
+
description: 'Project description in markdown',
|
|
740
|
+
},
|
|
741
|
+
lead: {
|
|
742
|
+
type: 'string',
|
|
743
|
+
description: 'Project lead user ID, "me", or "none" when updating',
|
|
744
|
+
},
|
|
745
|
+
priority: {
|
|
746
|
+
type: 'integer',
|
|
747
|
+
description: 'Priority 0-4',
|
|
748
|
+
minimum: 0,
|
|
749
|
+
maximum: 4,
|
|
750
|
+
multipleOf: 1,
|
|
751
|
+
},
|
|
752
|
+
color: {
|
|
753
|
+
type: 'string',
|
|
754
|
+
description: 'Project color (hex)',
|
|
755
|
+
},
|
|
756
|
+
icon: {
|
|
757
|
+
type: 'string',
|
|
758
|
+
description: 'Project icon',
|
|
759
|
+
},
|
|
760
|
+
startDate: {
|
|
761
|
+
type: 'string',
|
|
762
|
+
description: 'Planned start date (YYYY-MM-DD)',
|
|
763
|
+
},
|
|
764
|
+
targetDate: {
|
|
765
|
+
type: 'string',
|
|
766
|
+
description: 'Planned target date (YYYY-MM-DD)',
|
|
767
|
+
},
|
|
704
768
|
},
|
|
705
769
|
required: ['action'],
|
|
706
770
|
additionalProperties: false,
|
|
@@ -714,6 +778,18 @@ async function registerLinearTools(pi) {
|
|
|
714
778
|
switch (params.action) {
|
|
715
779
|
case 'list':
|
|
716
780
|
return await executeProjectList(client);
|
|
781
|
+
case 'view':
|
|
782
|
+
return await executeProjectView(client, params);
|
|
783
|
+
case 'create':
|
|
784
|
+
return await executeProjectCreate(client, params);
|
|
785
|
+
case 'update':
|
|
786
|
+
return await executeProjectUpdate(client, params);
|
|
787
|
+
case 'delete':
|
|
788
|
+
return await executeProjectDelete(client, params);
|
|
789
|
+
case 'archive':
|
|
790
|
+
return await executeProjectArchive(client, params);
|
|
791
|
+
case 'unarchive':
|
|
792
|
+
return await executeProjectUnarchive(client, params);
|
|
717
793
|
default:
|
|
718
794
|
throw new Error(`Unknown action: ${params.action}`);
|
|
719
795
|
}
|
|
@@ -739,10 +815,102 @@ async function registerLinearTools(pi) {
|
|
|
739
815
|
},
|
|
740
816
|
});
|
|
741
817
|
|
|
818
|
+
pi.registerTool({
|
|
819
|
+
name: 'linear_project_update',
|
|
820
|
+
label: 'Linear Project Update',
|
|
821
|
+
description: 'Interact with Linear project updates. Actions: list, view, create, update, archive, unarchive',
|
|
822
|
+
promptSnippet: 'Interact with Linear project updates (list, view, create, update, archive, unarchive)',
|
|
823
|
+
parameters: {
|
|
824
|
+
type: 'object',
|
|
825
|
+
properties: {
|
|
826
|
+
action: {
|
|
827
|
+
type: 'string',
|
|
828
|
+
enum: ['list', 'view', 'create', 'update', 'archive', 'unarchive'],
|
|
829
|
+
description: 'Action to perform on project update(s)',
|
|
830
|
+
},
|
|
831
|
+
project: {
|
|
832
|
+
type: 'string',
|
|
833
|
+
description: 'Project name or ID (for list, create)',
|
|
834
|
+
},
|
|
835
|
+
projectUpdate: {
|
|
836
|
+
type: 'string',
|
|
837
|
+
description: 'Project update ID (for view, update, archive, unarchive)',
|
|
838
|
+
},
|
|
839
|
+
body: {
|
|
840
|
+
type: 'string',
|
|
841
|
+
description: 'Project update body in markdown',
|
|
842
|
+
},
|
|
843
|
+
health: {
|
|
844
|
+
type: 'string',
|
|
845
|
+
description: 'Project update health: onTrack, atRisk, or offTrack',
|
|
846
|
+
enum: ['onTrack', 'atRisk', 'offTrack'],
|
|
847
|
+
},
|
|
848
|
+
isDiffHidden: {
|
|
849
|
+
type: 'boolean',
|
|
850
|
+
description: 'Whether to hide the diff on the update',
|
|
851
|
+
},
|
|
852
|
+
limit: {
|
|
853
|
+
type: 'integer',
|
|
854
|
+
description: 'Max updates to list',
|
|
855
|
+
minimum: 1,
|
|
856
|
+
multipleOf: 1,
|
|
857
|
+
},
|
|
858
|
+
includeArchived: {
|
|
859
|
+
type: 'boolean',
|
|
860
|
+
description: 'Whether archived updates should be included when listing',
|
|
861
|
+
},
|
|
862
|
+
},
|
|
863
|
+
required: ['action'],
|
|
864
|
+
additionalProperties: false,
|
|
865
|
+
},
|
|
866
|
+
renderResult: renderMarkdownResult,
|
|
867
|
+
async execute(_toolCallId, params) {
|
|
868
|
+
try {
|
|
869
|
+
const client = await createAuthenticatedClient();
|
|
870
|
+
|
|
871
|
+
return await withRequestUsageLogging(client, 'linear_project_update', params.action, async () => {
|
|
872
|
+
switch (params.action) {
|
|
873
|
+
case 'list':
|
|
874
|
+
return await executeProjectUpdateList(client, params);
|
|
875
|
+
case 'view':
|
|
876
|
+
return await executeProjectUpdateView(client, params);
|
|
877
|
+
case 'create':
|
|
878
|
+
return await executeProjectUpdateCreate(client, params);
|
|
879
|
+
case 'update':
|
|
880
|
+
return await executeProjectUpdateUpdate(client, params);
|
|
881
|
+
case 'archive':
|
|
882
|
+
return await executeProjectUpdateArchive(client, params);
|
|
883
|
+
case 'unarchive':
|
|
884
|
+
return await executeProjectUpdateUnarchive(client, params);
|
|
885
|
+
default:
|
|
886
|
+
throw new Error(`Unknown action: ${params.action}`);
|
|
887
|
+
}
|
|
888
|
+
});
|
|
889
|
+
} catch (error) {
|
|
890
|
+
const errorType = error?.type || '';
|
|
891
|
+
const errorMessage = String(error?.message || error || 'Unknown error');
|
|
892
|
+
|
|
893
|
+
if (errorType === 'Ratelimited' || errorMessage.toLowerCase().includes('rate limit')) {
|
|
894
|
+
const resetAt = error?.requestsResetAt
|
|
895
|
+
? new Date(error.requestsResetAt).toLocaleTimeString()
|
|
896
|
+
: 'approximately 1 hour from now';
|
|
897
|
+
throw new Error(`Linear API rate limit exceeded. Resets at: ${resetAt}. Please wait before retrying.`);
|
|
898
|
+
}
|
|
899
|
+
|
|
900
|
+
if (errorMessage.includes('Linear API error:')) {
|
|
901
|
+
throw error;
|
|
902
|
+
}
|
|
903
|
+
|
|
904
|
+
throw new Error(`Linear project update operation failed: ${errorMessage}`);
|
|
905
|
+
}
|
|
906
|
+
},
|
|
907
|
+
});
|
|
908
|
+
|
|
742
909
|
pi.registerTool({
|
|
743
910
|
name: 'linear_team',
|
|
744
911
|
label: 'Linear Team',
|
|
745
912
|
description: 'Interact with Linear teams. Actions: list',
|
|
913
|
+
promptSnippet: 'Interact with Linear teams (list)',
|
|
746
914
|
parameters: {
|
|
747
915
|
type: 'object',
|
|
748
916
|
properties: {
|
|
@@ -794,6 +962,7 @@ async function registerLinearTools(pi) {
|
|
|
794
962
|
name: 'linear_milestone',
|
|
795
963
|
label: 'Linear Milestone',
|
|
796
964
|
description: 'Interact with Linear project milestones. Actions: list, view, create, update, delete',
|
|
965
|
+
promptSnippet: 'Interact with Linear milestones (list, view, create, update, delete)',
|
|
797
966
|
parameters: {
|
|
798
967
|
type: 'object',
|
|
799
968
|
properties: {
|
|
@@ -983,8 +1152,9 @@ export default async function piLinearToolsExtension(pi) {
|
|
|
983
1152
|
const showMilestoneTool = await shouldExposeMilestoneTool();
|
|
984
1153
|
const toolLines = [
|
|
985
1154
|
'LLM-callable tools:',
|
|
986
|
-
' linear_issue (list/view/create/update/comment/start/delete)',
|
|
987
|
-
' linear_project (list)',
|
|
1155
|
+
' linear_issue (list/view/activity/create/update/comment/start/delete)',
|
|
1156
|
+
' linear_project (list/view/create/update/delete/archive/unarchive)',
|
|
1157
|
+
' linear_project_update (list/view/create/update/archive/unarchive)',
|
|
988
1158
|
' linear_team (list)',
|
|
989
1159
|
];
|
|
990
1160
|
|