@enrichlayer/el-linear 1.7.0 → 1.10.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 +82 -1
- package/claude-skills/linear-operations/SKILL.md +55 -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/attachments.js +2 -1
- package/dist/commands/comments.js +17 -28
- package/dist/commands/cycles.js +2 -1
- package/dist/commands/documents.js +2 -1
- package/dist/commands/graphql.js +4 -6
- package/dist/commands/init/index.js +2 -0
- package/dist/commands/init/oauth.d.ts +6 -0
- package/dist/commands/init/oauth.js +8 -2
- package/dist/commands/init/token.d.ts +0 -8
- package/dist/commands/init/token.js +13 -1
- package/dist/commands/issue-id.js +1 -3
- 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 +183 -0
- package/dist/commands/issues/link-references.d.ts +21 -0
- package/dist/commands/issues/link-references.js +171 -0
- package/dist/commands/issues/relations.d.ts +55 -0
- package/dist/commands/issues/relations.js +132 -0
- package/dist/commands/issues.js +97 -497
- package/dist/commands/labels.js +20 -28
- package/dist/commands/profile/migrate-legacy.js +10 -33
- package/dist/commands/profile.js +1 -10
- package/dist/commands/project-milestones.js +13 -20
- package/dist/commands/projects.d.ts +5 -1
- package/dist/commands/projects.js +202 -102
- package/dist/commands/read-shortcut.d.ts +6 -0
- package/dist/commands/read-shortcut.js +6 -1
- package/dist/commands/refs.js +10 -1
- package/dist/commands/releases.js +26 -30
- package/dist/commands/search.js +21 -30
- package/dist/commands/teams.js +2 -1
- package/dist/commands/templates.js +128 -7
- package/dist/commands/users.js +3 -1
- package/dist/config/config.js +31 -9
- package/dist/config/issue-validation.js +1 -1
- package/dist/config/paths.d.ts +11 -0
- package/dist/config/paths.js +44 -6
- package/dist/config/resolver.js +2 -3
- package/dist/config/term-enforcer.js +1 -1
- package/dist/main.js +37 -2
- package/dist/queries/attachments-types.d.ts +30 -0
- package/dist/queries/attachments-types.js +5 -0
- package/dist/queries/comments-types.d.ts +49 -0
- package/dist/queries/comments-types.js +5 -0
- package/dist/queries/documents-types.d.ts +61 -0
- package/dist/queries/documents-types.js +9 -0
- package/dist/queries/introspect-types.d.ts +57 -0
- package/dist/queries/introspect-types.js +10 -0
- package/dist/queries/issues-types.d.ts +416 -0
- package/dist/queries/issues-types.js +23 -0
- package/dist/queries/issues.d.ts +2 -0
- package/dist/queries/issues.js +22 -0
- package/dist/queries/labels-types.d.ts +64 -0
- package/dist/queries/labels-types.js +5 -0
- package/dist/queries/project-milestones-types.d.ts +91 -0
- package/dist/queries/project-milestones-types.js +10 -0
- package/dist/queries/projects-types.d.ts +75 -0
- package/dist/queries/projects-types.js +5 -0
- package/dist/queries/projects.d.ts +2 -0
- package/dist/queries/projects.js +22 -0
- package/dist/queries/releases-types.d.ts +84 -0
- package/dist/queries/releases-types.js +5 -0
- package/dist/queries/search-types.d.ts +86 -0
- package/dist/queries/search-types.js +6 -0
- package/dist/queries/templates-types.d.ts +61 -0
- package/dist/queries/templates-types.js +9 -0
- package/dist/queries/templates.d.ts +3 -0
- package/dist/queries/templates.js +43 -0
- package/dist/types/linear.d.ts +8 -2
- package/dist/utils/auth.js +7 -9
- package/dist/utils/auto-link-references.js +44 -25
- package/dist/utils/disk-cache.js +2 -1
- package/dist/utils/formatters/summary.d.ts +62 -0
- package/dist/utils/formatters/summary.js +755 -0
- package/dist/utils/graphql-attachments-service.js +6 -9
- package/dist/utils/graphql-documents-service.js +19 -25
- package/dist/utils/graphql-issues-service.d.ts +118 -5
- package/dist/utils/graphql-issues-service.js +202 -209
- 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 +7 -1
- package/dist/utils/output.js +38 -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/dist/utils/workspace-url.d.ts +5 -1
- package/dist/utils/workspace-url.js +35 -5
- package/package.json +1 -1
package/dist/commands/labels.js
CHANGED
|
@@ -6,7 +6,7 @@ import { createGraphQLService } from "../utils/graphql-service.js";
|
|
|
6
6
|
import { createLinearService } from "../utils/linear-service.js";
|
|
7
7
|
import { handleAsyncCommand, outputSuccess } from "../utils/output.js";
|
|
8
8
|
import { getRootOpts } from "../utils/root-opts.js";
|
|
9
|
-
import { validateHexColor } from "../utils/validators.js";
|
|
9
|
+
import { parsePositiveInt, validateHexColor } from "../utils/validators.js";
|
|
10
10
|
async function handleCreateLabel(name, options, command) {
|
|
11
11
|
const rootOpts = getRootOpts(command);
|
|
12
12
|
const teamId = resolveTeam(options.team);
|
|
@@ -23,45 +23,35 @@ async function handleCreateLabel(name, options, command) {
|
|
|
23
23
|
name: options.parent,
|
|
24
24
|
teamId,
|
|
25
25
|
});
|
|
26
|
-
const
|
|
27
|
-
const nodes = issueLabels?.nodes;
|
|
28
|
-
const parentLabel = nodes?.[0];
|
|
26
|
+
const parentLabel = parentResult.issueLabels.nodes[0];
|
|
29
27
|
if (parentLabel) {
|
|
30
28
|
input.parentId = parentLabel.id;
|
|
31
29
|
}
|
|
32
30
|
}
|
|
33
|
-
const result = await graphQLService.rawRequest(CREATE_LABEL_MUTATION, {
|
|
34
|
-
|
|
35
|
-
});
|
|
36
|
-
const createResult = result.issueLabelCreate;
|
|
37
|
-
if (!createResult?.success) {
|
|
31
|
+
const result = await graphQLService.rawRequest(CREATE_LABEL_MUTATION, { input });
|
|
32
|
+
if (!result.issueLabelCreate.success || !result.issueLabelCreate.issueLabel) {
|
|
38
33
|
throw new Error(`Failed to create label "${name}"`);
|
|
39
34
|
}
|
|
40
|
-
outputSuccess(
|
|
35
|
+
outputSuccess(result.issueLabelCreate.issueLabel);
|
|
41
36
|
}
|
|
42
37
|
async function handleRetireLabel(labelId, _options, command) {
|
|
43
38
|
const rootOpts = getRootOpts(command);
|
|
44
39
|
const graphQLService = await createGraphQLService(rootOpts);
|
|
45
|
-
const result = await graphQLService.rawRequest(RETIRE_LABEL_MUTATION, {
|
|
46
|
-
|
|
47
|
-
});
|
|
48
|
-
const retireResult = result.issueLabelRetire;
|
|
49
|
-
if (!retireResult?.success) {
|
|
40
|
+
const result = await graphQLService.rawRequest(RETIRE_LABEL_MUTATION, { id: labelId });
|
|
41
|
+
if (!result.issueLabelRetire.success || !result.issueLabelRetire.issueLabel) {
|
|
50
42
|
throw new Error(`Failed to retire label "${labelId}"`);
|
|
51
43
|
}
|
|
52
|
-
outputSuccess(
|
|
44
|
+
outputSuccess(result.issueLabelRetire.issueLabel);
|
|
53
45
|
}
|
|
54
46
|
async function handleRestoreLabel(labelId, _options, command) {
|
|
55
47
|
const rootOpts = getRootOpts(command);
|
|
56
48
|
const graphQLService = await createGraphQLService(rootOpts);
|
|
57
|
-
const result = await graphQLService.rawRequest(RESTORE_LABEL_MUTATION, {
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
const restoreResult = result.issueLabelRestore;
|
|
61
|
-
if (!restoreResult?.success) {
|
|
49
|
+
const result = await graphQLService.rawRequest(RESTORE_LABEL_MUTATION, { id: labelId });
|
|
50
|
+
if (!result.issueLabelRestore.success ||
|
|
51
|
+
!result.issueLabelRestore.issueLabel) {
|
|
62
52
|
throw new Error(`Failed to restore label "${labelId}"`);
|
|
63
53
|
}
|
|
64
|
-
outputSuccess(
|
|
54
|
+
outputSuccess(result.issueLabelRestore.issueLabel);
|
|
65
55
|
}
|
|
66
56
|
export function setupLabelsCommands(program) {
|
|
67
57
|
const labels = program
|
|
@@ -73,22 +63,24 @@ export function setupLabelsCommands(program) {
|
|
|
73
63
|
.command("list")
|
|
74
64
|
.description("List all available labels")
|
|
75
65
|
.option("--team <team>", "filter by team key, name, or ID")
|
|
66
|
+
.option("--name <substring>", "filter by case-insensitive substring on label name")
|
|
76
67
|
.option("-l, --limit <number>", "limit results", "100")
|
|
77
68
|
.action(handleAsyncCommand(async (options, command) => {
|
|
78
69
|
const rootOpts = getRootOpts(command);
|
|
79
70
|
const teamFilter = options.team ? resolveTeam(options.team) : undefined;
|
|
80
|
-
const
|
|
71
|
+
const nameFilter = options.name;
|
|
72
|
+
const limit = parsePositiveInt(options.limit, "--limit");
|
|
81
73
|
const ttl = resolveCacheTTL({
|
|
82
74
|
configTTL: loadConfig().cacheTTLSeconds,
|
|
83
75
|
noCacheFlag: rootOpts.cache === false,
|
|
84
76
|
});
|
|
85
|
-
// Cache key includes the team filter
|
|
86
|
-
//
|
|
87
|
-
// a smaller list isn't a valid cached answer for a larger ask.
|
|
88
|
-
const cacheKey = `labels-list-team:${teamFilter ?? "_all"}-limit:${limit}`;
|
|
77
|
+
// Cache key includes the team filter and name filter so different
|
|
78
|
+
// filter combinations don't collide. Same `limit` participates
|
|
79
|
+
// because a smaller list isn't a valid cached answer for a larger ask.
|
|
80
|
+
const cacheKey = `labels-list-team:${teamFilter ?? "_all"}-name:${nameFilter ?? "_all"}-limit:${limit}`;
|
|
89
81
|
const result = await cached(cacheKey, ttl, async () => {
|
|
90
82
|
const service = await createLinearService(rootOpts);
|
|
91
|
-
return service.getLabels(teamFilter, limit);
|
|
83
|
+
return service.getLabels(teamFilter, limit, nameFilter);
|
|
92
84
|
});
|
|
93
85
|
outputSuccess({
|
|
94
86
|
data: result.labels,
|
|
@@ -38,7 +38,8 @@
|
|
|
38
38
|
import { promises as fsp } from "node:fs";
|
|
39
39
|
import path from "node:path";
|
|
40
40
|
import { confirm, input, password } from "@inquirer/prompts";
|
|
41
|
-
import {
|
|
41
|
+
import { atomicWrite } from "../../auth/oauth-fs.js";
|
|
42
|
+
import { ACTIVE_PROFILE_FILE, CONFIG_DIR, CONFIG_PATH, isSafeProfileName as isSafeName, PROFILES_DIR, profilePaths, TOKEN_PATH, } from "../../config/paths.js";
|
|
42
43
|
import { sanitizeForLog, validateToken } from "../init/token.js";
|
|
43
44
|
const DEFAULT_PROMPTS = { input, password, confirm };
|
|
44
45
|
/** Register `el-linear profile migrate-legacy` under the parent `profile` command. */
|
|
@@ -165,10 +166,7 @@ async function resolveAndValidateToken(options, prompts, validate) {
|
|
|
165
166
|
async function copyConfigIntoProfile(destConfigPath, options, prompts) {
|
|
166
167
|
const sourceContent = await fsp.readFile(CONFIG_PATH, "utf8");
|
|
167
168
|
if (!(await pathExists(destConfigPath))) {
|
|
168
|
-
await
|
|
169
|
-
mode: 0o644,
|
|
170
|
-
encoding: "utf8",
|
|
171
|
-
});
|
|
169
|
+
await atomicWrite(destConfigPath, sourceContent, 0o644);
|
|
172
170
|
return;
|
|
173
171
|
}
|
|
174
172
|
const destContent = await fsp.readFile(destConfigPath, "utf8");
|
|
@@ -192,21 +190,15 @@ async function copyConfigIntoProfile(destConfigPath, options, prompts) {
|
|
|
192
190
|
throw new Error("Aborted by user.");
|
|
193
191
|
}
|
|
194
192
|
}
|
|
195
|
-
await
|
|
196
|
-
mode: 0o644,
|
|
197
|
-
encoding: "utf8",
|
|
198
|
-
});
|
|
193
|
+
await atomicWrite(destConfigPath, sourceContent, 0o644);
|
|
199
194
|
}
|
|
200
195
|
async function writeProfileToken(destTokenPath, token, options, prompts) {
|
|
201
196
|
const newline = `${token}\n`;
|
|
202
197
|
if (!(await pathExists(destTokenPath))) {
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
// fs.writeFile mode is only honored on file creation; chmod defensively
|
|
208
|
-
// in case a parent process pre-created the file with permissive perms.
|
|
209
|
-
await fsp.chmod(destTokenPath, 0o600);
|
|
198
|
+
// atomicWrite creates the tmp file with the requested mode and
|
|
199
|
+
// renames into place, so there is no window where the token is
|
|
200
|
+
// readable at a looser mode than 0o600.
|
|
201
|
+
await atomicWrite(destTokenPath, newline, 0o600);
|
|
210
202
|
return;
|
|
211
203
|
}
|
|
212
204
|
const existing = (await fsp.readFile(destTokenPath, "utf8")).trim();
|
|
@@ -230,11 +222,7 @@ async function writeProfileToken(destTokenPath, token, options, prompts) {
|
|
|
230
222
|
throw new Error("Aborted by user.");
|
|
231
223
|
}
|
|
232
224
|
}
|
|
233
|
-
await
|
|
234
|
-
mode: 0o600,
|
|
235
|
-
encoding: "utf8",
|
|
236
|
-
});
|
|
237
|
-
await fsp.chmod(destTokenPath, 0o600);
|
|
225
|
+
await atomicWrite(destTokenPath, newline, 0o600);
|
|
238
226
|
}
|
|
239
227
|
async function ensureActiveProfile(name) {
|
|
240
228
|
await fsp.mkdir(CONFIG_DIR, { recursive: true, mode: 0o700 });
|
|
@@ -244,10 +232,7 @@ async function ensureActiveProfile(name) {
|
|
|
244
232
|
}
|
|
245
233
|
if (current === name)
|
|
246
234
|
return;
|
|
247
|
-
await
|
|
248
|
-
mode: 0o644,
|
|
249
|
-
encoding: "utf8",
|
|
250
|
-
});
|
|
235
|
+
await atomicWrite(ACTIVE_PROFILE_FILE, `${name}\n`, 0o644);
|
|
251
236
|
}
|
|
252
237
|
async function pathExists(p) {
|
|
253
238
|
try {
|
|
@@ -258,14 +243,6 @@ async function pathExists(p) {
|
|
|
258
243
|
return false;
|
|
259
244
|
}
|
|
260
245
|
}
|
|
261
|
-
/**
|
|
262
|
-
* Profile names land in filesystem paths and the active-profile marker.
|
|
263
|
-
* Same conservative charset as `profile add` to avoid `..` traversal,
|
|
264
|
-
* shell metacharacters, and Unicode lookalikes.
|
|
265
|
-
*/
|
|
266
|
-
function isSafeName(name) {
|
|
267
|
-
return /^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$/.test(name);
|
|
268
|
-
}
|
|
269
246
|
// Re-export internals so the integration test (and follow-up commands)
|
|
270
247
|
// can compose them without re-implementing the idempotency rules.
|
|
271
248
|
export { CONFIG_PATH, PROFILES_DIR, TOKEN_PATH };
|
package/dist/commands/profile.js
CHANGED
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
import { promises as fsp } from "node:fs";
|
|
24
24
|
import path from "node:path";
|
|
25
25
|
import { confirm } from "@inquirer/prompts";
|
|
26
|
-
import { ACTIVE_PROFILE_FILE, CONFIG_DIR, CONFIG_PATH, PROFILES_DIR, profilePaths, resolveActiveProfile, setActiveProfileForSession, TOKEN_PATH, } from "../config/paths.js";
|
|
26
|
+
import { ACTIVE_PROFILE_FILE, CONFIG_DIR, CONFIG_PATH, isSafeProfileName as isSafeName, PROFILES_DIR, profilePaths, resolveActiveProfile, setActiveProfileForSession, TOKEN_PATH, } from "../config/paths.js";
|
|
27
27
|
import { outputSuccess, outputWarning } from "../utils/output.js";
|
|
28
28
|
import { runFullWizard } from "./init/index.js";
|
|
29
29
|
import { registerMigrateLegacy } from "./profile/migrate-legacy.js";
|
|
@@ -180,12 +180,3 @@ async function pathExists(p) {
|
|
|
180
180
|
return false;
|
|
181
181
|
}
|
|
182
182
|
}
|
|
183
|
-
/**
|
|
184
|
-
* Profile names land in filesystem paths AND get written to a
|
|
185
|
-
* single-line marker. Restrict to a conservative charset so we can't
|
|
186
|
-
* accidentally pick up `..` traversal, command-line metacharacters, or
|
|
187
|
-
* Unicode lookalikes that would confuse `profile use`.
|
|
188
|
-
*/
|
|
189
|
-
function isSafeName(name) {
|
|
190
|
-
return /^[A-Za-z0-9][A-Za-z0-9_.-]{0,63}$/.test(name);
|
|
191
|
-
}
|
|
@@ -5,7 +5,7 @@ import { createLinearService, } from "../utils/linear-service.js";
|
|
|
5
5
|
import { handleAsyncCommand, outputSuccess } from "../utils/output.js";
|
|
6
6
|
import { getRootOpts } from "../utils/root-opts.js";
|
|
7
7
|
import { isUuid } from "../utils/uuid.js";
|
|
8
|
-
import { validateIsoDate } from "../utils/validators.js";
|
|
8
|
+
import { parsePositiveInt, validateIsoDate } from "../utils/validators.js";
|
|
9
9
|
async function resolveMilestoneId(milestoneNameOrId, graphQLService, linearService, projectNameOrId) {
|
|
10
10
|
if (isUuid(milestoneNameOrId)) {
|
|
11
11
|
return milestoneNameOrId;
|
|
@@ -17,22 +17,17 @@ async function resolveMilestoneId(milestoneNameOrId, graphQLService, linearServi
|
|
|
17
17
|
name: milestoneNameOrId,
|
|
18
18
|
projectId,
|
|
19
19
|
});
|
|
20
|
-
|
|
21
|
-
const milestonesData = projectData?.projectMilestones;
|
|
22
|
-
nodes = milestonesData?.nodes || [];
|
|
20
|
+
nodes = findRes.project?.projectMilestones.nodes ?? [];
|
|
23
21
|
}
|
|
24
22
|
if (nodes.length === 0) {
|
|
25
|
-
const globalRes = await graphQLService.rawRequest(FIND_PROJECT_MILESTONE_BY_NAME_GLOBAL, {
|
|
26
|
-
|
|
27
|
-
});
|
|
28
|
-
const globalMilestones = globalRes.projectMilestones;
|
|
29
|
-
nodes = globalMilestones?.nodes || [];
|
|
23
|
+
const globalRes = await graphQLService.rawRequest(FIND_PROJECT_MILESTONE_BY_NAME_GLOBAL, { name: milestoneNameOrId });
|
|
24
|
+
nodes = globalRes.projectMilestones.nodes;
|
|
30
25
|
}
|
|
31
26
|
if (nodes.length === 0) {
|
|
32
27
|
throw notFoundError("Milestone", milestoneNameOrId);
|
|
33
28
|
}
|
|
34
29
|
if (nodes.length > 1) {
|
|
35
|
-
const matches = nodes.map((m) => `"${m.name}" in project "${m.project
|
|
30
|
+
const matches = nodes.map((m) => `"${m.name}" in project "${m.project.name}"`);
|
|
36
31
|
throw multipleMatchesError("milestone", milestoneNameOrId, matches, "specify --project or use the milestone ID");
|
|
37
32
|
}
|
|
38
33
|
return nodes[0].id;
|
|
@@ -44,11 +39,9 @@ async function handleListMilestones(options, command) {
|
|
|
44
39
|
const projectId = await linearService.resolveProjectId(options.project);
|
|
45
40
|
const result = await graphQLService.rawRequest(LIST_PROJECT_MILESTONES_QUERY, {
|
|
46
41
|
projectId,
|
|
47
|
-
first:
|
|
42
|
+
first: parsePositiveInt(options.limit || "50", "--limit"),
|
|
48
43
|
});
|
|
49
|
-
const
|
|
50
|
-
const milestonesData = projectData?.projectMilestones;
|
|
51
|
-
const nodes = milestonesData?.nodes || [];
|
|
44
|
+
const nodes = result.project?.projectMilestones.nodes ?? [];
|
|
52
45
|
outputSuccess({ data: nodes, meta: { count: nodes.length } });
|
|
53
46
|
}
|
|
54
47
|
async function handleReadMilestone(milestoneIdOrName, options, command) {
|
|
@@ -76,11 +69,11 @@ async function handleCreateMilestone(name, options, command) {
|
|
|
76
69
|
description: options.description,
|
|
77
70
|
targetDate: options.targetDate,
|
|
78
71
|
});
|
|
79
|
-
|
|
80
|
-
|
|
72
|
+
if (!result.projectMilestoneCreate.success ||
|
|
73
|
+
!result.projectMilestoneCreate.projectMilestone) {
|
|
81
74
|
throw new Error("Failed to create project milestone");
|
|
82
75
|
}
|
|
83
|
-
outputSuccess(
|
|
76
|
+
outputSuccess(result.projectMilestoneCreate.projectMilestone);
|
|
84
77
|
}
|
|
85
78
|
async function handleUpdateMilestone(milestoneIdOrName, options, command) {
|
|
86
79
|
const rootOpts = getRootOpts(command);
|
|
@@ -102,11 +95,11 @@ async function handleUpdateMilestone(milestoneIdOrName, options, command) {
|
|
|
102
95
|
updateVars.sortOrder = Number.parseFloat(options.sortOrder);
|
|
103
96
|
}
|
|
104
97
|
const result = await graphQLService.rawRequest(UPDATE_PROJECT_MILESTONE_MUTATION, updateVars);
|
|
105
|
-
|
|
106
|
-
|
|
98
|
+
if (!result.projectMilestoneUpdate.success ||
|
|
99
|
+
!result.projectMilestoneUpdate.projectMilestone) {
|
|
107
100
|
throw new Error("Failed to update project milestone");
|
|
108
101
|
}
|
|
109
|
-
outputSuccess(
|
|
102
|
+
outputSuccess(result.projectMilestoneUpdate.projectMilestone);
|
|
110
103
|
}
|
|
111
104
|
export function setupProjectMilestonesCommands(program) {
|
|
112
105
|
const projectMilestones = program
|
|
@@ -1,2 +1,6 @@
|
|
|
1
|
-
import type { Command } from "commander";
|
|
1
|
+
import type { Command, OptionValues } from "commander";
|
|
2
|
+
export declare function resolveProjectStateFilter(options: OptionValues): {
|
|
3
|
+
states?: string[];
|
|
4
|
+
excludeStates?: string[];
|
|
5
|
+
};
|
|
2
6
|
export declare function setupProjectsCommands(program: Command): void;
|