@fink-andreas/pi-linear-tools 0.4.3 → 0.5.1
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 +32 -10
- package/extensions/pi-linear-tools.js +105 -142
- package/package.json +1 -1
- package/src/handlers.js +63 -3
- package/src/linear.js +985 -184
package/CHANGELOG.md
CHANGED
|
@@ -1,20 +1,42 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
-
## v0.
|
|
3
|
+
## v0.5.1 (2026-04-17)
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
### Bug Fixes
|
|
6
|
+
- **Prevent pi crashes from Linear tool errors**: Added `executeToolSafely` wrapper that catches errors and returns them as safe tool results instead of throwing, preventing pi from crashing on Linear API errors
|
|
7
|
+
|
|
8
|
+
## v0.5.0 (2026-04-17)
|
|
9
|
+
|
|
10
|
+
Linear document sync, project lifecycle management, and estimate/story points support.
|
|
11
|
+
|
|
12
|
+
### New Features
|
|
13
|
+
- **Linear document sync**: Keep Linear documents in sync with local Markdown files via `linear_doc_sync` command
|
|
14
|
+
- **Project lifecycle**: Full project lifecycle support including archive/unarchive, updates, and milestones
|
|
15
|
+
- **Estimate (story points)**: Support for estimate/story points field on issue create/update
|
|
16
|
+
- **Issue activity history**: View complete issue activity with `linear_issue_activity` tool
|
|
17
|
+
- **Batch sync**: `sync-doc --all` for batch syncing all configured document targets
|
|
6
18
|
|
|
7
19
|
### Bug Fixes
|
|
8
|
-
- Fixed
|
|
9
|
-
-
|
|
10
|
-
-
|
|
20
|
+
- Fixed project name lookup for UUID-like project names
|
|
21
|
+
- Fixed milestone name lookup for view/update/delete operations
|
|
22
|
+
- Replaced 'order' with 'sortOrder' for ProjectMilestone queries
|
|
23
|
+
- Fixed sync-doc first-run check/run parity
|
|
24
|
+
- Fixed sync-doc drift detection and target matching
|
|
25
|
+
- Fixed project archive and update validation
|
|
26
|
+
- Fixed archived project resolution for project updates
|
|
27
|
+
- Fixed sync-doc marker position preservation when position is 'top'
|
|
28
|
+
- Fixed default limit for executeIssueActivity (now 25)
|
|
11
29
|
|
|
12
30
|
### Improvements
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
-
|
|
31
|
+
- Reduced expensive Linear API issue reads
|
|
32
|
+
- Better CLI help for issue and project workflows
|
|
33
|
+
- Improved sync-doc examples and guidance
|
|
34
|
+
- Added promptSnippet to all Linear tools for consistent LLM context
|
|
35
|
+
- Removed redundant action lists from tool descriptions
|
|
36
|
+
|
|
37
|
+
### Cleanup
|
|
38
|
+
- Removed planning artifacts and documentation files
|
|
39
|
+
- Stopped tracking plan files in git
|
|
18
40
|
|
|
19
41
|
## v0.4.2 (2026-03-26)
|
|
20
42
|
|
|
@@ -452,6 +452,85 @@ async function shouldExposeMilestoneTool() {
|
|
|
452
452
|
/**
|
|
453
453
|
* Render tool result as markdown
|
|
454
454
|
*/
|
|
455
|
+
function toToolTextResult(text, details = {}) {
|
|
456
|
+
return {
|
|
457
|
+
content: [{ type: 'text', text }],
|
|
458
|
+
details,
|
|
459
|
+
};
|
|
460
|
+
}
|
|
461
|
+
|
|
462
|
+
function buildRateLimitToolResult(error, options = {}) {
|
|
463
|
+
const { cached = false } = options;
|
|
464
|
+
const resetTimestamp = error?.requestsResetAt || Date.now() + 3600000;
|
|
465
|
+
const resetTime = new Date(resetTimestamp).toLocaleTimeString();
|
|
466
|
+
|
|
467
|
+
markRateLimited(resetTimestamp);
|
|
468
|
+
|
|
469
|
+
return toToolTextResult(
|
|
470
|
+
cached
|
|
471
|
+
? `Linear API rate limit exceeded (cached).\n\nThe rate limit resets at: ${resetTime}\n\nPlease wait before making more requests.`
|
|
472
|
+
: `Linear API rate limit exceeded.\n\nThe rate limit resets at: ${resetTime}\n\nPlease wait before making more requests, or reduce the frequency of API calls.`,
|
|
473
|
+
{
|
|
474
|
+
error: true,
|
|
475
|
+
errorType: 'Ratelimited',
|
|
476
|
+
rateLimited: true,
|
|
477
|
+
cached,
|
|
478
|
+
requestsResetAt: resetTimestamp,
|
|
479
|
+
resetTime,
|
|
480
|
+
}
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
function handleToolExecutionError(error, operationLabel, options = {}) {
|
|
485
|
+
const transformedError = options.transformError ? options.transformError(error) : error;
|
|
486
|
+
const errorType = error?.type || transformedError?.type || '';
|
|
487
|
+
const errorMessage = String(transformedError?.message || transformedError || 'Unknown error');
|
|
488
|
+
|
|
489
|
+
if (errorType === 'Ratelimited' || errorMessage.toLowerCase().includes('rate limit')) {
|
|
490
|
+
return buildRateLimitToolResult(error);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
if (errorMessage.includes('Linear API error:')) {
|
|
494
|
+
throw transformedError;
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
throw new Error(`${operationLabel}: ${errorMessage}`);
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
function buildGenericToolErrorResult(error, operationLabel) {
|
|
501
|
+
const errorType = error?.type || error?.name || 'Error';
|
|
502
|
+
const errorMessage = String(error?.message || error || 'Unknown error');
|
|
503
|
+
|
|
504
|
+
return toToolTextResult(`${operationLabel}: ${errorMessage}`, {
|
|
505
|
+
error: true,
|
|
506
|
+
errorType,
|
|
507
|
+
rateLimited: false,
|
|
508
|
+
});
|
|
509
|
+
}
|
|
510
|
+
|
|
511
|
+
async function executeToolSafely(operationLabel, operation, options = {}) {
|
|
512
|
+
try {
|
|
513
|
+
return await operation();
|
|
514
|
+
} catch (error) {
|
|
515
|
+
debug('[pi-linear-tools] tool execution failed', {
|
|
516
|
+
operationLabel,
|
|
517
|
+
errorType: error?.type || error?.name || null,
|
|
518
|
+
error: String(error?.message || error || 'unknown'),
|
|
519
|
+
});
|
|
520
|
+
|
|
521
|
+
try {
|
|
522
|
+
return handleToolExecutionError(error, operationLabel, options);
|
|
523
|
+
} catch (handledError) {
|
|
524
|
+
debug('[pi-linear-tools] returning generic safe tool error result', {
|
|
525
|
+
operationLabel,
|
|
526
|
+
errorType: handledError?.type || handledError?.name || null,
|
|
527
|
+
error: String(handledError?.message || handledError || 'unknown'),
|
|
528
|
+
});
|
|
529
|
+
return buildGenericToolErrorResult(handledError, operationLabel);
|
|
530
|
+
}
|
|
531
|
+
}
|
|
532
|
+
}
|
|
533
|
+
|
|
455
534
|
function renderMarkdownResult(result, _options, _theme) {
|
|
456
535
|
const text = result.content?.[0]?.text || '';
|
|
457
536
|
|
|
@@ -491,7 +570,7 @@ async function registerLinearTools(pi) {
|
|
|
491
570
|
pi.registerTool({
|
|
492
571
|
name: 'linear_issue',
|
|
493
572
|
label: 'Linear Issue',
|
|
494
|
-
description: 'Interact with Linear issues.
|
|
573
|
+
description: 'Interact with Linear issues.',
|
|
495
574
|
promptSnippet: 'Interact with Linear issues (list, view, activity, create, update, comment, start, delete)',
|
|
496
575
|
parameters: {
|
|
497
576
|
type: 'object',
|
|
@@ -586,6 +665,10 @@ async function registerLinearTools(pi) {
|
|
|
586
665
|
type: 'string',
|
|
587
666
|
description: 'For update: mark this issue as duplicate of the given issue key/ID.',
|
|
588
667
|
},
|
|
668
|
+
estimate: {
|
|
669
|
+
type: 'integer',
|
|
670
|
+
description: 'Estimate/story points for the issue (non-negative integer, for create/update)',
|
|
671
|
+
},
|
|
589
672
|
team: {
|
|
590
673
|
type: 'string',
|
|
591
674
|
description: 'Team key (e.g. ENG) or name (optional if default team configured)',
|
|
@@ -617,17 +700,13 @@ async function registerLinearTools(pi) {
|
|
|
617
700
|
},
|
|
618
701
|
renderResult: renderMarkdownResult,
|
|
619
702
|
async execute(_toolCallId, params) {
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
`Please wait before making more requests.`
|
|
627
|
-
);
|
|
628
|
-
}
|
|
703
|
+
return executeToolSafely('Linear issue operation failed', async () => {
|
|
704
|
+
// Pre-check: skip API calls if we know we're rate limited
|
|
705
|
+
const { isRateLimited, resetAt } = checkAndClearRateLimit();
|
|
706
|
+
if (isRateLimited) {
|
|
707
|
+
return buildRateLimitToolResult({ requestsResetAt: resetAt.getTime(), type: 'Ratelimited' }, { cached: true });
|
|
708
|
+
}
|
|
629
709
|
|
|
630
|
-
try {
|
|
631
710
|
const client = await createAuthenticatedClient();
|
|
632
711
|
|
|
633
712
|
return await withRequestUsageLogging(client, 'linear_issue', params.action, async () => {
|
|
@@ -656,63 +735,14 @@ async function registerLinearTools(pi) {
|
|
|
656
735
|
throw new Error(`Unknown action: ${params.action}`);
|
|
657
736
|
}
|
|
658
737
|
});
|
|
659
|
-
}
|
|
660
|
-
// Comprehensive error handling - catch ALL errors including SDK's RatelimitedLinearError
|
|
661
|
-
const errorType = error?.type || '';
|
|
662
|
-
const errorMessage = String(error?.message || error || 'Unknown error');
|
|
663
|
-
|
|
664
|
-
// Rate limit error - provide clear reset time and mark globally
|
|
665
|
-
if (errorType === 'Ratelimited' || errorMessage.toLowerCase().includes('rate limit')) {
|
|
666
|
-
const resetTimestamp = error?.requestsResetAt || (Date.now() + 3600000);
|
|
667
|
-
const resetTime = new Date(resetTimestamp).toLocaleTimeString();
|
|
668
|
-
markRateLimited(resetTimestamp);
|
|
669
|
-
throw new Error(
|
|
670
|
-
`Linear API rate limit exceeded.\n\n` +
|
|
671
|
-
`The rate limit resets at: ${resetTime}\n\n` +
|
|
672
|
-
`Please wait before making more requests, or reduce the frequency of API calls.`
|
|
673
|
-
);
|
|
674
|
-
}
|
|
675
|
-
|
|
676
|
-
// Authentication/Forbidden errors (handles SDK's ForbiddenLinearError)
|
|
677
|
-
if (errorType === 'Forbidden' || errorType === 'AuthenticationError' ||
|
|
678
|
-
errorMessage.toLowerCase().includes('forbidden') || errorMessage.toLowerCase().includes('unauthorized')) {
|
|
679
|
-
throw new Error(
|
|
680
|
-
`Linear API authentication failed: ${errorMessage}\n\n` +
|
|
681
|
-
`Please check your API key or OAuth token permissions.`
|
|
682
|
-
);
|
|
683
|
-
}
|
|
684
|
-
|
|
685
|
-
// Network errors (handles SDK's NetworkError)
|
|
686
|
-
if (errorType === 'NetworkError' || errorMessage.toLowerCase().includes('network')) {
|
|
687
|
-
throw new Error(
|
|
688
|
-
`Network error communicating with Linear API.\n\n` +
|
|
689
|
-
`Please check your internet connection and try again.`
|
|
690
|
-
);
|
|
691
|
-
}
|
|
692
|
-
|
|
693
|
-
// Internal server errors (handles SDK's InternalError)
|
|
694
|
-
if (errorType === 'InternalError' || (error?.status >= 500 && error?.status < 600)) {
|
|
695
|
-
throw new Error(
|
|
696
|
-
`Linear API server error (${error?.status || 'unknown'}).\n\n` +
|
|
697
|
-
`Linear may be experiencing issues. Please try again later.`
|
|
698
|
-
);
|
|
699
|
-
}
|
|
700
|
-
|
|
701
|
-
// Re-throw if already formatted with "Linear API error:"
|
|
702
|
-
if (errorMessage.includes('Linear API error:')) {
|
|
703
|
-
throw error;
|
|
704
|
-
}
|
|
705
|
-
|
|
706
|
-
// Wrap unexpected errors with context - NEVER let raw errors propagate
|
|
707
|
-
throw new Error(`Linear issue operation failed: ${errorMessage}`);
|
|
708
|
-
}
|
|
738
|
+
});
|
|
709
739
|
},
|
|
710
740
|
});
|
|
711
741
|
|
|
712
742
|
pi.registerTool({
|
|
713
743
|
name: 'linear_project',
|
|
714
744
|
label: 'Linear Project',
|
|
715
|
-
description: 'Interact with Linear projects.
|
|
745
|
+
description: 'Interact with Linear projects.',
|
|
716
746
|
promptSnippet: 'Interact with Linear projects (list, view, create, update, delete, archive, unarchive)',
|
|
717
747
|
parameters: {
|
|
718
748
|
type: 'object',
|
|
@@ -771,7 +801,7 @@ async function registerLinearTools(pi) {
|
|
|
771
801
|
},
|
|
772
802
|
renderResult: renderMarkdownResult,
|
|
773
803
|
async execute(_toolCallId, params) {
|
|
774
|
-
|
|
804
|
+
return executeToolSafely('Linear project operation failed', async () => {
|
|
775
805
|
const client = await createAuthenticatedClient();
|
|
776
806
|
|
|
777
807
|
return await withRequestUsageLogging(client, 'linear_project', params.action, async () => {
|
|
@@ -794,31 +824,14 @@ async function registerLinearTools(pi) {
|
|
|
794
824
|
throw new Error(`Unknown action: ${params.action}`);
|
|
795
825
|
}
|
|
796
826
|
});
|
|
797
|
-
}
|
|
798
|
-
// Comprehensive error handling - catch ALL errors
|
|
799
|
-
const errorType = error?.type || '';
|
|
800
|
-
const errorMessage = String(error?.message || error || 'Unknown error');
|
|
801
|
-
|
|
802
|
-
if (errorType === 'Ratelimited' || errorMessage.toLowerCase().includes('rate limit')) {
|
|
803
|
-
const resetAt = error?.requestsResetAt
|
|
804
|
-
? new Date(error.requestsResetAt).toLocaleTimeString()
|
|
805
|
-
: 'approximately 1 hour from now';
|
|
806
|
-
throw new Error(`Linear API rate limit exceeded. Resets at: ${resetAt}. Please wait before retrying.`);
|
|
807
|
-
}
|
|
808
|
-
|
|
809
|
-
if (errorMessage.includes('Linear API error:')) {
|
|
810
|
-
throw error;
|
|
811
|
-
}
|
|
812
|
-
|
|
813
|
-
throw new Error(`Linear project operation failed: ${errorMessage}`);
|
|
814
|
-
}
|
|
827
|
+
});
|
|
815
828
|
},
|
|
816
829
|
});
|
|
817
830
|
|
|
818
831
|
pi.registerTool({
|
|
819
832
|
name: 'linear_project_update',
|
|
820
833
|
label: 'Linear Project Update',
|
|
821
|
-
description: 'Interact with Linear project updates.
|
|
834
|
+
description: 'Interact with Linear project updates.',
|
|
822
835
|
promptSnippet: 'Interact with Linear project updates (list, view, create, update, archive, unarchive)',
|
|
823
836
|
parameters: {
|
|
824
837
|
type: 'object',
|
|
@@ -865,7 +878,7 @@ async function registerLinearTools(pi) {
|
|
|
865
878
|
},
|
|
866
879
|
renderResult: renderMarkdownResult,
|
|
867
880
|
async execute(_toolCallId, params) {
|
|
868
|
-
|
|
881
|
+
return executeToolSafely('Linear project update operation failed', async () => {
|
|
869
882
|
const client = await createAuthenticatedClient();
|
|
870
883
|
|
|
871
884
|
return await withRequestUsageLogging(client, 'linear_project_update', params.action, async () => {
|
|
@@ -886,30 +899,14 @@ async function registerLinearTools(pi) {
|
|
|
886
899
|
throw new Error(`Unknown action: ${params.action}`);
|
|
887
900
|
}
|
|
888
901
|
});
|
|
889
|
-
}
|
|
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
|
-
}
|
|
902
|
+
});
|
|
906
903
|
},
|
|
907
904
|
});
|
|
908
905
|
|
|
909
906
|
pi.registerTool({
|
|
910
907
|
name: 'linear_team',
|
|
911
908
|
label: 'Linear Team',
|
|
912
|
-
description: 'Interact with Linear teams.
|
|
909
|
+
description: 'Interact with Linear teams.',
|
|
913
910
|
promptSnippet: 'Interact with Linear teams (list)',
|
|
914
911
|
parameters: {
|
|
915
912
|
type: 'object',
|
|
@@ -925,7 +922,7 @@ async function registerLinearTools(pi) {
|
|
|
925
922
|
},
|
|
926
923
|
renderResult: renderMarkdownResult,
|
|
927
924
|
async execute(_toolCallId, params) {
|
|
928
|
-
|
|
925
|
+
return executeToolSafely('Linear team operation failed', async () => {
|
|
929
926
|
const client = await createAuthenticatedClient();
|
|
930
927
|
|
|
931
928
|
return await withRequestUsageLogging(client, 'linear_team', params.action, async () => {
|
|
@@ -936,24 +933,7 @@ async function registerLinearTools(pi) {
|
|
|
936
933
|
throw new Error(`Unknown action: ${params.action}`);
|
|
937
934
|
}
|
|
938
935
|
});
|
|
939
|
-
}
|
|
940
|
-
// Comprehensive error handling - catch ALL errors
|
|
941
|
-
const errorType = error?.type || '';
|
|
942
|
-
const errorMessage = String(error?.message || error || 'Unknown error');
|
|
943
|
-
|
|
944
|
-
if (errorType === 'Ratelimited' || errorMessage.toLowerCase().includes('rate limit')) {
|
|
945
|
-
const resetAt = error?.requestsResetAt
|
|
946
|
-
? new Date(error.requestsResetAt).toLocaleTimeString()
|
|
947
|
-
: 'approximately 1 hour from now';
|
|
948
|
-
throw new Error(`Linear API rate limit exceeded. Resets at: ${resetAt}. Please wait before retrying.`);
|
|
949
|
-
}
|
|
950
|
-
|
|
951
|
-
if (errorMessage.includes('Linear API error:')) {
|
|
952
|
-
throw error;
|
|
953
|
-
}
|
|
954
|
-
|
|
955
|
-
throw new Error(`Linear team operation failed: ${errorMessage}`);
|
|
956
|
-
}
|
|
936
|
+
});
|
|
957
937
|
},
|
|
958
938
|
});
|
|
959
939
|
|
|
@@ -961,7 +941,7 @@ async function registerLinearTools(pi) {
|
|
|
961
941
|
pi.registerTool({
|
|
962
942
|
name: 'linear_milestone',
|
|
963
943
|
label: 'Linear Milestone',
|
|
964
|
-
description: 'Interact with Linear project milestones.
|
|
944
|
+
description: 'Interact with Linear project milestones.',
|
|
965
945
|
promptSnippet: 'Interact with Linear milestones (list, view, create, update, delete)',
|
|
966
946
|
parameters: {
|
|
967
947
|
type: 'object',
|
|
@@ -997,7 +977,7 @@ async function registerLinearTools(pi) {
|
|
|
997
977
|
},
|
|
998
978
|
renderResult: renderMarkdownResult,
|
|
999
979
|
async execute(_toolCallId, params) {
|
|
1000
|
-
|
|
980
|
+
return executeToolSafely('Linear milestone operation failed', async () => {
|
|
1001
981
|
const client = await createAuthenticatedClient();
|
|
1002
982
|
|
|
1003
983
|
return await withRequestUsageLogging(client, 'linear_milestone', params.action, async () => {
|
|
@@ -1016,26 +996,9 @@ async function registerLinearTools(pi) {
|
|
|
1016
996
|
throw new Error(`Unknown action: ${params.action}`);
|
|
1017
997
|
}
|
|
1018
998
|
});
|
|
1019
|
-
}
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
// Comprehensive error handling - catch ALL errors
|
|
1023
|
-
const errorType = error?.type || '';
|
|
1024
|
-
const errorMessage = String(hintError?.message || hintError || 'Unknown error');
|
|
1025
|
-
|
|
1026
|
-
if (errorType === 'Ratelimited' || errorMessage.toLowerCase().includes('rate limit')) {
|
|
1027
|
-
const resetAt = error?.requestsResetAt
|
|
1028
|
-
? new Date(error.requestsResetAt).toLocaleTimeString()
|
|
1029
|
-
: 'approximately 1 hour from now';
|
|
1030
|
-
throw new Error(`Linear API rate limit exceeded. Resets at: ${resetAt}. Please wait before retrying.`);
|
|
1031
|
-
}
|
|
1032
|
-
|
|
1033
|
-
if (errorMessage.includes('Linear API error:')) {
|
|
1034
|
-
throw hintError;
|
|
1035
|
-
}
|
|
1036
|
-
|
|
1037
|
-
throw new Error(`Linear milestone operation failed: ${errorMessage}`);
|
|
1038
|
-
}
|
|
999
|
+
}, {
|
|
1000
|
+
transformError: withMilestoneScopeHint,
|
|
1001
|
+
});
|
|
1039
1002
|
},
|
|
1040
1003
|
});
|
|
1041
1004
|
}
|
package/package.json
CHANGED
package/src/handlers.js
CHANGED
|
@@ -17,6 +17,7 @@ import {
|
|
|
17
17
|
fetchTeams,
|
|
18
18
|
resolveProjectRef,
|
|
19
19
|
resolveTeamRef,
|
|
20
|
+
resolveMilestoneRef,
|
|
20
21
|
getTeamWorkflowStates,
|
|
21
22
|
fetchIssueDetails,
|
|
22
23
|
fetchIssueActivity,
|
|
@@ -319,6 +320,10 @@ export async function executeIssueCreate(client, params, options = {}) {
|
|
|
319
320
|
createInput.priority = params.priority;
|
|
320
321
|
}
|
|
321
322
|
|
|
323
|
+
if (params.estimate !== undefined && params.estimate !== null) {
|
|
324
|
+
createInput.estimate = params.estimate;
|
|
325
|
+
}
|
|
326
|
+
|
|
322
327
|
if (params.parentId) {
|
|
323
328
|
createInput.parentId = params.parentId;
|
|
324
329
|
}
|
|
@@ -401,6 +406,7 @@ export async function executeIssueUpdate(client, params) {
|
|
|
401
406
|
title: params.title,
|
|
402
407
|
description: params.description,
|
|
403
408
|
priority: params.priority,
|
|
409
|
+
estimate: params.estimate,
|
|
404
410
|
state: params.state,
|
|
405
411
|
milestone: params.milestone,
|
|
406
412
|
projectMilestoneId: params.projectMilestoneId,
|
|
@@ -1041,7 +1047,24 @@ export async function executeMilestoneList(client, params) {
|
|
|
1041
1047
|
* View milestone details
|
|
1042
1048
|
*/
|
|
1043
1049
|
export async function executeMilestoneView(client, params) {
|
|
1044
|
-
|
|
1050
|
+
let projectId = null;
|
|
1051
|
+
let milestoneId = params.milestone;
|
|
1052
|
+
|
|
1053
|
+
// If milestone is not a Linear ID, resolve it with project context
|
|
1054
|
+
const ref = String(milestoneId || '').trim();
|
|
1055
|
+
const isId = typeof ref === 'string' && /^[0-9a-fA-F-]{16,}$/.test(ref);
|
|
1056
|
+
|
|
1057
|
+
if (!isId) {
|
|
1058
|
+
// Need project context to resolve milestone name
|
|
1059
|
+
let projectRef = params.project;
|
|
1060
|
+
if (!projectRef) {
|
|
1061
|
+
projectRef = path.basename(process.cwd());
|
|
1062
|
+
}
|
|
1063
|
+
const resolvedProject = await resolveProjectRef(client, projectRef);
|
|
1064
|
+
projectId = resolvedProject.id;
|
|
1065
|
+
const resolvedMilestone = await resolveMilestoneRef(client, ref, projectId);
|
|
1066
|
+
milestoneId = resolvedMilestone.id;
|
|
1067
|
+
}
|
|
1045
1068
|
|
|
1046
1069
|
const milestoneData = await fetchMilestoneDetails(client, milestoneId);
|
|
1047
1070
|
|
|
@@ -1163,7 +1186,25 @@ export async function executeMilestoneCreate(client, params) {
|
|
|
1163
1186
|
* Update a milestone
|
|
1164
1187
|
*/
|
|
1165
1188
|
export async function executeMilestoneUpdate(client, params) {
|
|
1166
|
-
|
|
1189
|
+
let projectId = null;
|
|
1190
|
+
let milestoneId = params.milestone;
|
|
1191
|
+
|
|
1192
|
+
// If milestone is not a Linear ID, resolve it with project context
|
|
1193
|
+
const ref = String(milestoneId || '').trim();
|
|
1194
|
+
const isId = typeof ref === 'string' && /^[0-9a-fA-F-]{16,}$/.test(ref);
|
|
1195
|
+
|
|
1196
|
+
|
|
1197
|
+
if (!isId) {
|
|
1198
|
+
// Need project context to resolve milestone name
|
|
1199
|
+
let projectRef = params.project;
|
|
1200
|
+
if (!projectRef) {
|
|
1201
|
+
projectRef = path.basename(process.cwd());
|
|
1202
|
+
}
|
|
1203
|
+
const resolvedProject = await resolveProjectRef(client, projectRef);
|
|
1204
|
+
projectId = resolvedProject.id;
|
|
1205
|
+
const resolvedMilestone = await resolveMilestoneRef(client, ref, projectId);
|
|
1206
|
+
milestoneId = resolvedMilestone.id;
|
|
1207
|
+
}
|
|
1167
1208
|
|
|
1168
1209
|
// Note: status is not included as it's a computed/read-only field in Linear's API
|
|
1169
1210
|
const result = await updateProjectMilestone(client, milestoneId, {
|
|
@@ -1202,7 +1243,26 @@ export async function executeMilestoneUpdate(client, params) {
|
|
|
1202
1243
|
* Delete a milestone
|
|
1203
1244
|
*/
|
|
1204
1245
|
export async function executeMilestoneDelete(client, params) {
|
|
1205
|
-
|
|
1246
|
+
let projectId = null;
|
|
1247
|
+
let milestoneId = params.milestone;
|
|
1248
|
+
|
|
1249
|
+
// If milestone is not a Linear ID, resolve it with project context
|
|
1250
|
+
const ref = String(milestoneId || '').trim();
|
|
1251
|
+
const isId = typeof ref === 'string' && /^[0-9a-fA-F-]{16,}$/.test(ref);
|
|
1252
|
+
|
|
1253
|
+
|
|
1254
|
+
if (!isId) {
|
|
1255
|
+
// Need project context to resolve milestone name
|
|
1256
|
+
let projectRef = params.project;
|
|
1257
|
+
if (!projectRef) {
|
|
1258
|
+
projectRef = path.basename(process.cwd());
|
|
1259
|
+
}
|
|
1260
|
+
const resolvedProject = await resolveProjectRef(client, projectRef);
|
|
1261
|
+
projectId = resolvedProject.id;
|
|
1262
|
+
const resolvedMilestone = await resolveMilestoneRef(client, ref, projectId);
|
|
1263
|
+
milestoneId = resolvedMilestone.id;
|
|
1264
|
+
}
|
|
1265
|
+
|
|
1206
1266
|
const result = await deleteProjectMilestone(client, milestoneId);
|
|
1207
1267
|
|
|
1208
1268
|
const label = result.name
|