@enrichlayer/el-linear 1.32.0 → 1.32.2

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.
@@ -10,6 +10,28 @@
10
10
  "Selects labels from the known label taxonomy (e.g., bug)",
11
11
  "Description includes a 'Why we need this' section",
12
12
  "Uses el-linear CLI to create the issue, not raw curl"
13
+ ],
14
+ "assertions": [
15
+ {
16
+ "type": "skill_used",
17
+ "name": "linear-operations",
18
+ "description": "Issue creation must route through the Linear operations skill."
19
+ },
20
+ {
21
+ "type": "command_matches",
22
+ "pattern": "el-linear\\s+issues?\\s+search",
23
+ "description": "Duplicate checking runs before creating the issue."
24
+ },
25
+ {
26
+ "type": "command_matches",
27
+ "pattern": "el-linear\\s+issues?\\s+create",
28
+ "description": "The issue is created through el-linear."
29
+ },
30
+ {
31
+ "type": "output_contains",
32
+ "value": "Why we need this",
33
+ "description": "The issue body includes the required intent section."
34
+ }
13
35
  ]
14
36
  },
15
37
  {
@@ -20,6 +42,23 @@
20
42
  "Uses el-linear search command with appropriate query",
21
43
  "Does not attempt to use curl against api.linear.app",
22
44
  "Returns formatted results with issue IDs and titles"
45
+ ],
46
+ "assertions": [
47
+ {
48
+ "type": "skill_used",
49
+ "name": "linear-operations",
50
+ "description": "Linear search should use the Linear operations skill."
51
+ },
52
+ {
53
+ "type": "command_matches",
54
+ "pattern": "el-linear\\s+issues?\\s+search\\s+.*auth",
55
+ "description": "Searches Linear with the requested auth query."
56
+ },
57
+ {
58
+ "type": "output_not_contains",
59
+ "value": "api.linear.app",
60
+ "description": "Does not bypass el-linear with raw Linear API calls."
61
+ }
23
62
  ]
24
63
  },
25
64
  {
@@ -30,6 +69,23 @@
30
69
  "Uses el-linear update command with the issue identifier DEV-123",
31
70
  "Sets the status to In Progress",
32
71
  "Does not create a new issue"
72
+ ],
73
+ "assertions": [
74
+ {
75
+ "type": "skill_used",
76
+ "name": "linear-operations",
77
+ "description": "Issue mutation should use the Linear operations skill."
78
+ },
79
+ {
80
+ "type": "command_matches",
81
+ "pattern": "el-linear\\s+issues?\\s+update\\s+DEV-123\\b.*--status\\s+['\\\"]?In Progress",
82
+ "description": "Updates DEV-123 to In Progress through el-linear."
83
+ },
84
+ {
85
+ "type": "command_matches",
86
+ "pattern": "el-linear\\s+issues?\\s+read\\s+DEV-123",
87
+ "description": "Reads the existing issue before mutation."
88
+ }
33
89
  ]
34
90
  },
35
91
  {
@@ -40,6 +96,33 @@
40
96
  "Does NOT trigger linear-operations skill",
41
97
  "Routes to git-branch-from-linear instead",
42
98
  "No el-linear create or update commands are issued"
99
+ ],
100
+ "assertions": [
101
+ {
102
+ "type": "skill_not_used",
103
+ "name": "linear-operations",
104
+ "description": "Branch creation is outside linear-operations scope."
105
+ },
106
+ {
107
+ "type": "skill_used",
108
+ "name": "git-branch-from-linear",
109
+ "description": "Branch creation should route to the branch skill."
110
+ },
111
+ {
112
+ "type": "command_matches",
113
+ "pattern": "el-linear\\s+issues?\\s+read\\s+DEV-456",
114
+ "description": "Reading issue metadata is allowed for branch creation."
115
+ },
116
+ {
117
+ "type": "output_not_contains",
118
+ "value": "el-linear issues create",
119
+ "description": "Does not create a new issue for branch creation."
120
+ },
121
+ {
122
+ "type": "output_not_contains",
123
+ "value": "el-linear issues update",
124
+ "description": "Does not update issue fields for branch creation."
125
+ }
43
126
  ]
44
127
  }
45
128
  ]
@@ -188,8 +188,10 @@ export declare class GraphQLIssuesService {
188
188
  /**
189
189
  * Batch-fetches N issues in a single GraphQL round-trip (DEV-4477).
190
190
  *
191
- * Replaces `Promise.all(refs.map(getIssueById))` — that pattern issues N
192
- * concurrent queries; this one issues exactly one. Useful for
191
+ * Replaces `Promise.all(refs.map(getIssueById))` as the primary path. The
192
+ * batch response is still order-restored locally; if Linear omits an input
193
+ * ref from that response, the missing ref falls back to the single-issue
194
+ * lookup path so valid issues are not misreported as missing. Useful for
193
195
  * `el-linear issues read <id...>`, the only caller today.
194
196
  *
195
197
  * Identifier shapes accepted (mirrors `getIssueById`):
@@ -66,8 +66,10 @@ export class GraphQLIssuesService {
66
66
  /**
67
67
  * Batch-fetches N issues in a single GraphQL round-trip (DEV-4477).
68
68
  *
69
- * Replaces `Promise.all(refs.map(getIssueById))` — that pattern issues N
70
- * concurrent queries; this one issues exactly one. Useful for
69
+ * Replaces `Promise.all(refs.map(getIssueById))` as the primary path. The
70
+ * batch response is still order-restored locally; if Linear omits an input
71
+ * ref from that response, the missing ref falls back to the single-issue
72
+ * lookup path so valid issues are not misreported as missing. Useful for
71
73
  * `el-linear issues read <id...>`, the only caller today.
72
74
  *
73
75
  * Identifier shapes accepted (mirrors `getIssueById`):
@@ -146,11 +148,12 @@ export class GraphQLIssuesService {
146
148
  for (const ref of refs) {
147
149
  const node = isUuid(ref) ? byUuid.get(ref) : byIdentifier.get(ref);
148
150
  if (!node) {
149
- throw notFoundError("Issue", ref);
151
+ ordered.push(await this.getIssueById(ref));
152
+ continue;
150
153
  }
151
- ordered.push(node);
154
+ ordered.push(this.transformIssueData(node));
152
155
  }
153
- return ordered.map((issue) => this.transformIssueData(issue));
156
+ return ordered;
154
157
  }
155
158
  async getFirstStartedStatus(team) {
156
159
  const statuses = await this.graphQLService.rawRequest(TEAM_STARTED_STATUSES_QUERY, { teamId: team.id });
@@ -5,7 +5,7 @@ import { isUuid } from "./uuid.js";
5
5
  // Unicode-aware mention regex: ASCII-only `\w` would silently fail to
6
6
  // match Cyrillic / accented Latin / CJK names (`@Юрий`, `@Niño`).
7
7
  // `\p{L}` covers all Unicode letters; `\p{N}` covers all numerics.
8
- const MENTION_NAME_CHARS = "\\p{L}\\p{N}_";
8
+ const MENTION_NAME_CHARS = "\\p{L}\\p{N}_\\-";
9
9
  const EXPLICIT_MENTION_REGEX = new RegExp(`(?<![${MENTION_NAME_CHARS}])@([${MENTION_NAME_CHARS}]+)(?![${MENTION_NAME_CHARS}/])`, "gu");
10
10
  const WHITESPACE_SPLIT_REGEX = /\s+/;
11
11
  const FENCED_CODE_REGEX = /```[\s\S]*?```/g;
@@ -32,7 +32,10 @@ export async function resolveMentions(body, linearService, options = {}) {
32
32
  // 1. Resolve explicit @name mentions (existing behavior).
33
33
  const explicit = new Map();
34
34
  const unresolvedExplicit = [];
35
- const explicitNames = [...body.matchAll(EXPLICIT_MENTION_REGEX)].map((m) => m[1]);
35
+ const explicitScanBody = stripCodeAndLinks(body);
36
+ const explicitNames = [
37
+ ...explicitScanBody.matchAll(EXPLICIT_MENTION_REGEX),
38
+ ].map((m) => m[1]);
36
39
  for (const name of new Set(explicitNames)) {
37
40
  const userId = await resolveUserByName(name, linearService);
38
41
  if (userId) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enrichlayer/el-linear",
3
- "version": "1.32.0",
3
+ "version": "1.32.2",
4
4
  "description": "A pragmatic CLI for Linear.app — deterministic team/label/member resolution, structured issue validation, configurable term enforcement, and a GraphQL escape hatch.",
5
5
  "main": "dist/main.js",
6
6
  "types": "dist/main.d.ts",