@fink-andreas/pi-linear-tools 0.7.2 → 0.7.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 +14 -0
- package/package.json +2 -2
- package/src/auth/constants.js +7 -2
- package/src/error-hints.js +27 -0
- package/src/handlers.js +7 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## v0.7.3 (2026-07-28)
|
|
4
|
+
|
|
5
|
+
Patch release that fixes Linear issue-relation updates for OAuth users.
|
|
6
|
+
|
|
7
|
+
### Bug Fixes
|
|
8
|
+
- **Enable issue relations with default OAuth**: New OAuth grants now request Linear's `write` scope, allowing `linear_issue update` to set `blockedBy`, `blocking`, `relatedTo`, and `duplicateOf` relations.
|
|
9
|
+
- **Explain legacy-token remediation**: Relation updates rejected because `write` is missing now direct OAuth users to run `pi-linear-tools auth login`; API-key users are told to use a key with write access.
|
|
10
|
+
|
|
11
|
+
### Tests
|
|
12
|
+
- Added regression coverage for the default OAuth authorization URL scope and issue-relation scope guidance.
|
|
13
|
+
|
|
14
|
+
### Upgrade note
|
|
15
|
+
Existing OAuth tokens cannot gain the newly requested scope automatically. Re-authenticate with `pi-linear-tools auth login` before creating issue relations.
|
|
16
|
+
|
|
3
17
|
## v0.7.2 (2026-07-21)
|
|
4
18
|
|
|
5
19
|
Patch release for Pi package rename compatibility and terminal-safe fallback rendering.
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@fink-andreas/pi-linear-tools",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.3",
|
|
4
4
|
"description": "Pi extension with Linear SDK tools and configuration commands",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"engines": {
|
|
@@ -27,7 +27,7 @@
|
|
|
27
27
|
},
|
|
28
28
|
"scripts": {
|
|
29
29
|
"start": "node index.js",
|
|
30
|
-
"test": "node tests/test-package-manifest.js && node tests/test-extension-registration.js && node tests/test-render-fallback.js && node tests/test-issue-comment-result.js && node tests/test-issue-priority.js && node tests/test-settings.js && node tests/test-issue-download.js && node tests/test-api-usage-caching.js && node tests/test-issue-create-milestone.js && node tests/test-assignee-update.js && node tests/test-rate-limit-fallback-update.js && node tests/test-full-assignee-flow.js && node tests/test-branch-param.js && node tests/test-team-filter.js && node tests/test-project-crud.js && node tests/test-project-lifecycle.js && node tests/test-sync-doc.js && node tests/test-issue-activity.js",
|
|
30
|
+
"test": "node tests/test-package-manifest.js && node tests/test-oauth.js && node tests/test-extension-registration.js && node tests/test-render-fallback.js && node tests/test-issue-comment-result.js && node tests/test-issue-priority.js && node tests/test-settings.js && node tests/test-issue-download.js && node tests/test-api-usage-caching.js && node tests/test-issue-create-milestone.js && node tests/test-assignee-update.js && node tests/test-rate-limit-fallback-update.js && node tests/test-full-assignee-flow.js && node tests/test-branch-param.js && node tests/test-team-filter.js && node tests/test-project-crud.js && node tests/test-project-lifecycle.js && node tests/test-sync-doc.js && node tests/test-issue-activity.js",
|
|
31
31
|
"dev:sync-local-extension": "node scripts/dev-sync-local-extension.mjs",
|
|
32
32
|
"release:check": "npm test && npm pack --dry-run"
|
|
33
33
|
},
|
package/src/auth/constants.js
CHANGED
|
@@ -5,5 +5,10 @@
|
|
|
5
5
|
// Linear OAuth application client ID
|
|
6
6
|
export const OAUTH_CLIENT_ID = 'a3e177176c6697611367f1a2405d4a34';
|
|
7
7
|
|
|
8
|
-
// OAuth scopes - minimal required scopes
|
|
9
|
-
|
|
8
|
+
// OAuth scopes - minimal required scopes.
|
|
9
|
+
// `write` is included so issue relations (blockedBy / blocking / relatedTo / duplicateOf) work:
|
|
10
|
+
// those go through the `issueRelationCreate` mutation, which Linear gates behind the `write`
|
|
11
|
+
// scope, whereas `issueUpdate` (title/description/state/assignee/parentId) is permitted under
|
|
12
|
+
// `issues:create`. Without `write`, the `linear_issue` tool accepts the relation params but
|
|
13
|
+
// Linear rejects them with "Invalid scope: `write` required". See upstream issue #27.
|
|
14
|
+
export const OAUTH_SCOPES = ['read', 'issues:create', 'comments:create', 'write'];
|
package/src/error-hints.js
CHANGED
|
@@ -22,3 +22,30 @@ export function withMilestoneScopeHint(error) {
|
|
|
22
22
|
|
|
23
23
|
return error;
|
|
24
24
|
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* Adds remediation guidance when an issue-relation mutation is rejected because
|
|
28
|
+
* an existing credential was issued before the default OAuth scopes included `write`.
|
|
29
|
+
*
|
|
30
|
+
* @param {Error} error - The error to wrap
|
|
31
|
+
* @param {object} patch - Requested issue update fields
|
|
32
|
+
* @returns {Error} The original error or a new error with additional hint
|
|
33
|
+
*/
|
|
34
|
+
export function withIssueRelationScopeHint(error, patch = {}) {
|
|
35
|
+
const message = String(error?.message || error || 'Unknown error');
|
|
36
|
+
const relationFields = ['blockedBy', 'blocking', 'relatedTo', 'duplicateOf'];
|
|
37
|
+
const requestsRelation = relationFields.some((field) => {
|
|
38
|
+
const value = patch[field];
|
|
39
|
+
return Array.isArray(value) ? value.length > 0 : String(value || '').trim() !== '';
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
if (requestsRelation && /invalid scope/i.test(message) && /write/i.test(message)) {
|
|
43
|
+
return new Error(
|
|
44
|
+
`${message}\nHint: Issue relations require Linear's write scope. ` +
|
|
45
|
+
`If you authenticated with OAuth before this scope was added, run ` +
|
|
46
|
+
'`pi-linear-tools auth login` to re-authenticate. API-key users need a key with write access.'
|
|
47
|
+
);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return error;
|
|
51
|
+
}
|
package/src/handlers.js
CHANGED
|
@@ -46,6 +46,7 @@ import {
|
|
|
46
46
|
withHandlerErrorHandling,
|
|
47
47
|
getViewer,
|
|
48
48
|
} from './linear.js';
|
|
49
|
+
import { withIssueRelationScopeHint } from './error-hints.js';
|
|
49
50
|
import { debug } from './logger.js';
|
|
50
51
|
|
|
51
52
|
function toTextResult(text, details = {}) {
|
|
@@ -804,7 +805,12 @@ export async function executeIssueUpdate(client, params) {
|
|
|
804
805
|
projectMilestoneId: updatePatch.projectMilestoneId,
|
|
805
806
|
});
|
|
806
807
|
|
|
807
|
-
|
|
808
|
+
let result;
|
|
809
|
+
try {
|
|
810
|
+
result = await updateIssue(client, issue, updatePatch);
|
|
811
|
+
} catch (error) {
|
|
812
|
+
throw withIssueRelationScopeHint(error, updatePatch);
|
|
813
|
+
}
|
|
808
814
|
|
|
809
815
|
const friendlyChanges = result.changed.map((field) => {
|
|
810
816
|
if (field === 'stateId') return 'state';
|