@enrichlayer/el-linear 1.6.0 → 1.9.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 +101 -5
- package/claude-skills/linear-operations/SKILL.md +55 -0
- package/dist/auth/oauth-app-config.d.ts +21 -0
- package/dist/auth/oauth-app-config.js +82 -0
- package/dist/auth/oauth-callback.js +14 -7
- package/dist/auth/oauth-fs.d.ts +22 -0
- package/dist/auth/oauth-fs.js +77 -6
- package/dist/auth/oauth-headless.d.ts +13 -8
- package/dist/auth/oauth-headless.js +18 -11
- package/dist/auth/oauth-storage.d.ts +11 -3
- package/dist/auth/oauth-storage.js +15 -8
- package/dist/auth/token-resolver.d.ts +9 -0
- package/dist/auth/token-resolver.js +57 -37
- package/dist/commands/init/defaults.d.ts +18 -1
- package/dist/commands/init/defaults.js +83 -2
- package/dist/commands/init/index.js +9 -1
- package/dist/commands/init/oauth.d.ts +10 -4
- package/dist/commands/init/oauth.js +32 -8
- package/dist/commands/init/token.d.ts +0 -8
- package/dist/commands/init/token.js +13 -1
- package/dist/commands/issues/branch.d.ts +17 -0
- package/dist/commands/issues/branch.js +40 -0
- package/dist/commands/issues/description.d.ts +89 -0
- package/dist/commands/issues/description.js +187 -0
- package/dist/commands/issues.js +76 -222
- package/dist/commands/labels.js +17 -2
- package/dist/commands/profile/migrate-legacy.js +10 -33
- package/dist/commands/profile.js +1 -10
- package/dist/commands/projects.d.ts +5 -1
- package/dist/commands/projects.js +144 -64
- package/dist/commands/read-shortcut.d.ts +6 -0
- package/dist/commands/read-shortcut.js +6 -1
- package/dist/commands/refs.js +2 -1
- package/dist/commands/teams.js +12 -2
- package/dist/commands/templates.js +127 -1
- package/dist/commands/users.js +2 -1
- package/dist/config/config.d.ts +19 -0
- package/dist/config/config.js +31 -9
- package/dist/config/issue-validation.js +1 -1
- package/dist/config/paths.d.ts +12 -0
- package/dist/config/paths.js +45 -6
- package/dist/config/term-enforcer.js +1 -1
- package/dist/main.js +39 -3
- package/dist/queries/templates.d.ts +3 -0
- package/dist/queries/templates.js +43 -0
- package/dist/utils/auth.js +7 -9
- package/dist/utils/auto-link-references.js +15 -1
- package/dist/utils/disk-cache.d.ts +51 -0
- package/dist/utils/disk-cache.js +179 -0
- package/dist/utils/formatters/summary.d.ts +62 -0
- package/dist/utils/formatters/summary.js +755 -0
- package/dist/utils/graphql-issues-service.d.ts +106 -3
- package/dist/utils/graphql-issues-service.js +51 -37
- package/dist/utils/issue-reference-extractor.d.ts +9 -1
- package/dist/utils/issue-reference-extractor.js +16 -9
- package/dist/utils/issue-reference-wrapper.js +1 -54
- package/dist/utils/linear-service.d.ts +7 -3
- package/dist/utils/linear-service.js +27 -5
- package/dist/utils/markdown-prosemirror.js +17 -1
- package/dist/utils/mention-resolver.js +17 -5
- package/dist/utils/output.d.ts +5 -1
- package/dist/utils/output.js +28 -1
- package/dist/utils/protected-ranges.d.ts +33 -0
- package/dist/utils/protected-ranges.js +73 -0
- package/dist/utils/table-formatter.d.ts +36 -0
- package/dist/utils/table-formatter.js +46 -24
- package/dist/utils/validators.d.ts +9 -1
- package/dist/utils/validators.js +10 -0
- package/package.json +1 -1
package/dist/utils/output.js
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { execFileSync } from "node:child_process";
|
|
2
|
+
import { dispatch as dispatchSummary, inferKindFromPayload, } from "./formatters/summary.js";
|
|
2
3
|
import { logger } from "./logger.js";
|
|
3
4
|
const warningBuffer = [];
|
|
4
5
|
let rawMode = false;
|
|
5
6
|
let jqFilter = null;
|
|
6
7
|
let fieldsFilter = null;
|
|
8
|
+
let outputFormat = "json";
|
|
7
9
|
export function setRawMode(enabled) {
|
|
8
10
|
rawMode = enabled;
|
|
9
11
|
}
|
|
@@ -13,6 +15,12 @@ export function setJqFilter(filter) {
|
|
|
13
15
|
export function setFieldsFilter(fields) {
|
|
14
16
|
fieldsFilter = fields;
|
|
15
17
|
}
|
|
18
|
+
export function setOutputFormat(format) {
|
|
19
|
+
outputFormat = format;
|
|
20
|
+
}
|
|
21
|
+
export function getOutputFormat() {
|
|
22
|
+
return outputFormat;
|
|
23
|
+
}
|
|
16
24
|
function filterFields(obj, fields) {
|
|
17
25
|
if (Array.isArray(obj)) {
|
|
18
26
|
return obj.map((item) => filterFields(item, fields));
|
|
@@ -29,6 +37,15 @@ function filterFields(obj, fields) {
|
|
|
29
37
|
}
|
|
30
38
|
return obj;
|
|
31
39
|
}
|
|
40
|
+
/**
|
|
41
|
+
* Emit a summary-format render to stdout for the given payload. The
|
|
42
|
+
* caller (`outputSuccess`) has already applied `--raw` and `--fields`,
|
|
43
|
+
* so the value here is post-filter — no need to unwrap again.
|
|
44
|
+
*/
|
|
45
|
+
function emitSummary(payload) {
|
|
46
|
+
const kind = inferKindFromPayload(payload);
|
|
47
|
+
logger.info(dispatchSummary(kind, payload));
|
|
48
|
+
}
|
|
32
49
|
export function outputSuccess(data) {
|
|
33
50
|
const warnings = drainWarnings();
|
|
34
51
|
let output;
|
|
@@ -66,6 +83,13 @@ export function outputSuccess(data) {
|
|
|
66
83
|
}
|
|
67
84
|
}
|
|
68
85
|
}
|
|
86
|
+
// summary format takes the post-raw / post-fields value and renders it
|
|
87
|
+
// as a human-readable block. We bypass the jq path because jq is a
|
|
88
|
+
// JSON-shape filter — it doesn't compose with text output.
|
|
89
|
+
if (outputFormat === "summary") {
|
|
90
|
+
emitSummary(output);
|
|
91
|
+
return;
|
|
92
|
+
}
|
|
69
93
|
if (jqFilter) {
|
|
70
94
|
const json = JSON.stringify(output);
|
|
71
95
|
// Normalize common shell-escape artifacts (zsh history expansion)
|
|
@@ -87,7 +111,7 @@ export function outputSuccess(data) {
|
|
|
87
111
|
logger.info(JSON.stringify(output, null, 2));
|
|
88
112
|
}
|
|
89
113
|
}
|
|
90
|
-
export function outputWarning(message
|
|
114
|
+
export function outputWarning(message) {
|
|
91
115
|
const messages = Array.isArray(message) ? message : [message];
|
|
92
116
|
for (const msg of messages) {
|
|
93
117
|
warningBuffer.push(msg);
|
|
@@ -103,6 +127,9 @@ function drainWarnings() {
|
|
|
103
127
|
export function resetWarnings() {
|
|
104
128
|
warningBuffer.length = 0;
|
|
105
129
|
}
|
|
130
|
+
export function resetOutputFormat() {
|
|
131
|
+
outputFormat = "json";
|
|
132
|
+
}
|
|
106
133
|
function outputError(error) {
|
|
107
134
|
const payload = JSON.stringify({ error: error.message }, null, 2);
|
|
108
135
|
// Write to stdout (same channel as success) so machine callers always
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared "protected ranges" scanner — used by both
|
|
3
|
+
* `issue-reference-wrapper` and `issue-reference-extractor` so they
|
|
4
|
+
* agree on which spans of input text should be excluded from
|
|
5
|
+
* identifier processing.
|
|
6
|
+
*
|
|
7
|
+
* Why this exists: a transform/consumer pair that disagree about what
|
|
8
|
+
* counts as "protected" silently produces phantom relations or
|
|
9
|
+
* double-wrapped links. Concretely, the wrapper protects fenced code,
|
|
10
|
+
* inline code, markdown links, Slack links, angle-bracket autolinks,
|
|
11
|
+
* AND bare URLs; the extractor used to only strip fenced code, so
|
|
12
|
+
* `[click](https://x/DEV-100)` would wrap correctly but the auto-link
|
|
13
|
+
* extractor would still detect DEV-100 inside the URL and create a
|
|
14
|
+
* phantom relation. Same for `https://github.com/org/repo/DEV-100.md`.
|
|
15
|
+
*
|
|
16
|
+
* Centralising the scanner makes both consumers agree by construction.
|
|
17
|
+
*/
|
|
18
|
+
/** Linear identifier shape: ABC-123, EMW-1, DEV-3592. */
|
|
19
|
+
export declare const IDENTIFIER_REGEX: RegExp;
|
|
20
|
+
export interface ProtectedRange {
|
|
21
|
+
end: number;
|
|
22
|
+
start: number;
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Find ranges of `text` that should NOT have identifiers processed.
|
|
26
|
+
* Covered: fenced code, inline backticks, existing markdown links,
|
|
27
|
+
* Slack links, angle-bracket autolinks, bare URLs.
|
|
28
|
+
*
|
|
29
|
+
* The returned ranges may overlap; callers only test "is this position
|
|
30
|
+
* inside any protected range" so overlap is harmless.
|
|
31
|
+
*/
|
|
32
|
+
export declare function findProtectedRanges(text: string): ProtectedRange[];
|
|
33
|
+
export declare function isProtected(pos: number, ranges: ProtectedRange[]): boolean;
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shared "protected ranges" scanner — used by both
|
|
3
|
+
* `issue-reference-wrapper` and `issue-reference-extractor` so they
|
|
4
|
+
* agree on which spans of input text should be excluded from
|
|
5
|
+
* identifier processing.
|
|
6
|
+
*
|
|
7
|
+
* Why this exists: a transform/consumer pair that disagree about what
|
|
8
|
+
* counts as "protected" silently produces phantom relations or
|
|
9
|
+
* double-wrapped links. Concretely, the wrapper protects fenced code,
|
|
10
|
+
* inline code, markdown links, Slack links, angle-bracket autolinks,
|
|
11
|
+
* AND bare URLs; the extractor used to only strip fenced code, so
|
|
12
|
+
* `[click](https://x/DEV-100)` would wrap correctly but the auto-link
|
|
13
|
+
* extractor would still detect DEV-100 inside the URL and create a
|
|
14
|
+
* phantom relation. Same for `https://github.com/org/repo/DEV-100.md`.
|
|
15
|
+
*
|
|
16
|
+
* Centralising the scanner makes both consumers agree by construction.
|
|
17
|
+
*/
|
|
18
|
+
/** Linear identifier shape: ABC-123, EMW-1, DEV-3592. */
|
|
19
|
+
export const IDENTIFIER_REGEX = /\b([A-Z][A-Z0-9]*-\d+)\b/g;
|
|
20
|
+
const FENCED_CODE_BLOCK_REGEX = /(?:^|\n)([ \t]*)(?:```|~~~)[^\n]*\n[\s\S]*?\n\1?(?:```|~~~)(?=\n|$)/g;
|
|
21
|
+
// Inline backtick spans — `EMW-258`. Markdown allows multiple backticks
|
|
22
|
+
// for spans containing backticks; we keep it simple and match
|
|
23
|
+
// single-backtick spans, which covers the common case.
|
|
24
|
+
const INLINE_CODE_REGEX = /`[^`\n]+?`/g;
|
|
25
|
+
// Existing markdown links: [text](url). Both halves protected so
|
|
26
|
+
// identifiers inside the URL or the text aren't reprocessed.
|
|
27
|
+
const MARKDOWN_LINK_REGEX = /\[([^\]]*)\]\(([^)]*)\)/g;
|
|
28
|
+
// Existing Slack mrkdwn links: <url|text>. Both halves protected for
|
|
29
|
+
// the same reason as markdown links.
|
|
30
|
+
const SLACK_LINK_REGEX = /<https?:\/\/[^|>\s]+\|[^>]*>/g;
|
|
31
|
+
// Angle-bracket autolinks: <https://...>
|
|
32
|
+
const ANGLE_AUTOLINK_REGEX = /<[^>\s]+>/g;
|
|
33
|
+
// Bare URLs in prose. We protect these so identifiers inside path
|
|
34
|
+
// components (e.g. "https://github.com/foo/DEV-100") don't get
|
|
35
|
+
// processed.
|
|
36
|
+
const BARE_URL_REGEX = /https?:\/\/\S+/g;
|
|
37
|
+
/**
|
|
38
|
+
* Find ranges of `text` that should NOT have identifiers processed.
|
|
39
|
+
* Covered: fenced code, inline backticks, existing markdown links,
|
|
40
|
+
* Slack links, angle-bracket autolinks, bare URLs.
|
|
41
|
+
*
|
|
42
|
+
* The returned ranges may overlap; callers only test "is this position
|
|
43
|
+
* inside any protected range" so overlap is harmless.
|
|
44
|
+
*/
|
|
45
|
+
export function findProtectedRanges(text) {
|
|
46
|
+
const ranges = [];
|
|
47
|
+
for (const re of [
|
|
48
|
+
FENCED_CODE_BLOCK_REGEX,
|
|
49
|
+
INLINE_CODE_REGEX,
|
|
50
|
+
MARKDOWN_LINK_REGEX,
|
|
51
|
+
// Slack links must be considered before generic angle-bracket
|
|
52
|
+
// autolinks — the autolink regex doesn't know about the `|label`
|
|
53
|
+
// boundary, but matchAll resets per regex so order is just for
|
|
54
|
+
// clarity here.
|
|
55
|
+
SLACK_LINK_REGEX,
|
|
56
|
+
ANGLE_AUTOLINK_REGEX,
|
|
57
|
+
BARE_URL_REGEX,
|
|
58
|
+
]) {
|
|
59
|
+
for (const m of text.matchAll(re)) {
|
|
60
|
+
const start = m.index ?? 0;
|
|
61
|
+
ranges.push({ start, end: start + m[0].length });
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
return ranges;
|
|
65
|
+
}
|
|
66
|
+
export function isProtected(pos, ranges) {
|
|
67
|
+
for (const r of ranges) {
|
|
68
|
+
if (pos >= r.start && pos < r.end) {
|
|
69
|
+
return true;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return false;
|
|
73
|
+
}
|
|
@@ -1,4 +1,40 @@
|
|
|
1
1
|
import type { LinearIssue } from "../types/linear.js";
|
|
2
|
+
/**
|
|
3
|
+
* Column definition for the fixed-width text and CSV renderers.
|
|
4
|
+
* Generic over the row type `T` so the same renderers serve issues,
|
|
5
|
+
* projects, and any future resource that wants table output. The
|
|
6
|
+
* `colorize` hook receives the row so it can branch on entity-
|
|
7
|
+
* specific fields (e.g. the issue's priority or status); pass
|
|
8
|
+
* undefined to skip color.
|
|
9
|
+
*/
|
|
10
|
+
export interface ColumnDef<T> {
|
|
11
|
+
colorize?: (value: string, row: T) => string;
|
|
12
|
+
extract: (row: T) => string;
|
|
13
|
+
header: string;
|
|
14
|
+
key: string;
|
|
15
|
+
width: number;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Render `rows` as a fixed-width text table using `columns`. Generic
|
|
19
|
+
* over the row type `T` so issues, projects, and other resources
|
|
20
|
+
* share the same width-padding + colorize machinery.
|
|
21
|
+
*/
|
|
22
|
+
export declare function renderFixedWidthTable<T>(rows: T[], columns: ColumnDef<T>[]): string;
|
|
23
|
+
/**
|
|
24
|
+
* Render `rows` as CSV using `columns`. Quotes any value containing
|
|
25
|
+
* `,` or `"` (RFC 4180-style escaping). Generic over the row type.
|
|
26
|
+
*/
|
|
27
|
+
export declare function renderCsv<T>(rows: T[], columns: ColumnDef<T>[]): string;
|
|
2
28
|
export declare function formatTable(issues: LinearIssue[], fieldNames?: string[]): string;
|
|
3
29
|
export declare function formatCsv(issues: LinearIssue[], fieldNames?: string[]): string;
|
|
30
|
+
export interface MarkdownColumnDef<T> {
|
|
31
|
+
align?: "left" | "right";
|
|
32
|
+
extract: (row: T) => string;
|
|
33
|
+
header: string;
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Render `rows` as a markdown table using `columns`. Generic over
|
|
37
|
+
* the row type. Escapes `|` and newlines in cell values.
|
|
38
|
+
*/
|
|
39
|
+
export declare function renderMarkdownTable<T>(rows: T[], columns: MarkdownColumnDef<T>[]): string;
|
|
4
40
|
export declare function formatMarkdown(issues: LinearIssue[], fieldNames?: string[]): string;
|
|
@@ -129,39 +129,54 @@ function pad(str, width) {
|
|
|
129
129
|
const truncated = truncate(str, width);
|
|
130
130
|
return truncated + " ".repeat(Math.max(0, width - truncated.length));
|
|
131
131
|
}
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
132
|
+
/**
|
|
133
|
+
* Render `rows` as a fixed-width text table using `columns`. Generic
|
|
134
|
+
* over the row type `T` so issues, projects, and other resources
|
|
135
|
+
* share the same width-padding + colorize machinery.
|
|
136
|
+
*/
|
|
137
|
+
export function renderFixedWidthTable(rows, columns) {
|
|
137
138
|
if (columns.length === 0) {
|
|
138
139
|
return "No valid columns specified.";
|
|
139
140
|
}
|
|
140
141
|
const header = columns.map((col) => pad(col.header, col.width)).join(" ");
|
|
141
142
|
const separator = columns.map((col) => "─".repeat(col.width)).join("──");
|
|
142
|
-
const
|
|
143
|
+
const body = rows.map((row) => columns
|
|
143
144
|
.map((col) => {
|
|
144
|
-
const padded = pad(col.extract(
|
|
145
|
-
return col.colorize ? col.colorize(padded,
|
|
145
|
+
const padded = pad(col.extract(row), col.width);
|
|
146
|
+
return col.colorize ? col.colorize(padded, row) : padded;
|
|
146
147
|
})
|
|
147
148
|
.join(" "));
|
|
148
|
-
return [header, separator, ...
|
|
149
|
+
return [header, separator, ...body].join("\n");
|
|
149
150
|
}
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
151
|
+
/**
|
|
152
|
+
* Render `rows` as CSV using `columns`. Quotes any value containing
|
|
153
|
+
* `,` or `"` (RFC 4180-style escaping). Generic over the row type.
|
|
154
|
+
*/
|
|
155
|
+
export function renderCsv(rows, columns) {
|
|
155
156
|
const header = columns.map((col) => col.header).join(",");
|
|
156
|
-
const
|
|
157
|
+
const body = rows.map((row) => columns
|
|
157
158
|
.map((col) => {
|
|
158
|
-
const value = col.extract(
|
|
159
|
+
const value = col.extract(row);
|
|
159
160
|
return value.includes(",") || value.includes('"')
|
|
160
161
|
? `"${value.replace(/"/g, '""')}"`
|
|
161
162
|
: value;
|
|
162
163
|
})
|
|
163
164
|
.join(","));
|
|
164
|
-
return [header, ...
|
|
165
|
+
return [header, ...body].join("\n");
|
|
166
|
+
}
|
|
167
|
+
export function formatTable(issues, fieldNames) {
|
|
168
|
+
const columnKeys = fieldNames ?? DEFAULT_COLUMNS;
|
|
169
|
+
const columns = columnKeys
|
|
170
|
+
.map((key) => ALL_COLUMNS[key])
|
|
171
|
+
.filter((col) => col !== undefined);
|
|
172
|
+
return renderFixedWidthTable(issues, columns);
|
|
173
|
+
}
|
|
174
|
+
export function formatCsv(issues, fieldNames) {
|
|
175
|
+
const columnKeys = fieldNames ?? DEFAULT_COLUMNS;
|
|
176
|
+
const columns = columnKeys
|
|
177
|
+
.map((key) => ALL_COLUMNS[key])
|
|
178
|
+
.filter((col) => col !== undefined);
|
|
179
|
+
return renderCsv(issues, columns);
|
|
165
180
|
}
|
|
166
181
|
// ── Markdown formatting ────────────────────────────────────────
|
|
167
182
|
const STATUS_MD = {
|
|
@@ -234,16 +249,23 @@ const MD_COLUMNS = {
|
|
|
234
249
|
function escapeMarkdownCell(str) {
|
|
235
250
|
return str.replace(/\|/g, "\\|").replace(/\n/g, " ");
|
|
236
251
|
}
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
252
|
+
/**
|
|
253
|
+
* Render `rows` as a markdown table using `columns`. Generic over
|
|
254
|
+
* the row type. Escapes `|` and newlines in cell values.
|
|
255
|
+
*/
|
|
256
|
+
export function renderMarkdownTable(rows, columns) {
|
|
242
257
|
if (columns.length === 0) {
|
|
243
258
|
return "No valid columns specified.";
|
|
244
259
|
}
|
|
245
260
|
const header = `| ${columns.map((col) => col.header).join(" | ")} |`;
|
|
246
261
|
const divider = `| ${columns.map((col) => (col.align === "right" ? "---:" : "---")).join(" | ")} |`;
|
|
247
|
-
const
|
|
248
|
-
return [header, divider, ...
|
|
262
|
+
const body = rows.map((row) => `| ${columns.map((col) => escapeMarkdownCell(col.extract(row))).join(" | ")} |`);
|
|
263
|
+
return [header, divider, ...body].join("\n");
|
|
264
|
+
}
|
|
265
|
+
export function formatMarkdown(issues, fieldNames) {
|
|
266
|
+
const columnKeys = fieldNames ?? DEFAULT_COLUMNS;
|
|
267
|
+
const columns = columnKeys
|
|
268
|
+
.map((key) => MD_COLUMNS[key])
|
|
269
|
+
.filter((col) => col !== undefined);
|
|
270
|
+
return renderMarkdownTable(issues, columns);
|
|
249
271
|
}
|
|
@@ -9,6 +9,14 @@ export declare function parsePositiveInt(value: string, flagName: string): numbe
|
|
|
9
9
|
export declare function validatePriority(value: string): number;
|
|
10
10
|
export declare function validateHexColor(value: string): string;
|
|
11
11
|
export declare function validateIsoDate(value: string): string;
|
|
12
|
-
|
|
12
|
+
/**
|
|
13
|
+
* Split a comma-separated list into trimmed, non-empty items.
|
|
14
|
+
*
|
|
15
|
+
* Accepts `undefined` so call sites can pipe through commander's
|
|
16
|
+
* optional flag values without an explicit truthy guard. `false`
|
|
17
|
+
* (commander's representation of `--no-foo`) and empty string both
|
|
18
|
+
* resolve to `[]`.
|
|
19
|
+
*/
|
|
20
|
+
export declare function splitList(value: string | undefined | null | false): string[];
|
|
13
21
|
export declare function parsePriorityFilter(value: string): number[];
|
|
14
22
|
export declare const PRIORITY_LABELS: Record<number, string>;
|
package/dist/utils/validators.js
CHANGED
|
@@ -42,7 +42,17 @@ export function validateIsoDate(value) {
|
|
|
42
42
|
}
|
|
43
43
|
return value;
|
|
44
44
|
}
|
|
45
|
+
/**
|
|
46
|
+
* Split a comma-separated list into trimmed, non-empty items.
|
|
47
|
+
*
|
|
48
|
+
* Accepts `undefined` so call sites can pipe through commander's
|
|
49
|
+
* optional flag values without an explicit truthy guard. `false`
|
|
50
|
+
* (commander's representation of `--no-foo`) and empty string both
|
|
51
|
+
* resolve to `[]`.
|
|
52
|
+
*/
|
|
45
53
|
export function splitList(value) {
|
|
54
|
+
if (!value)
|
|
55
|
+
return [];
|
|
46
56
|
return value
|
|
47
57
|
.split(",")
|
|
48
58
|
.map((s) => s.trim())
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@enrichlayer/el-linear",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.9.0",
|
|
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",
|