linmux 0.1.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/LICENSE +21 -0
- package/README.md +240 -0
- package/bin/run.js +4 -0
- package/dist/commands/comment/create.js +94 -0
- package/dist/commands/comment/delete.js +74 -0
- package/dist/commands/comment/list.js +84 -0
- package/dist/commands/comment/update.js +80 -0
- package/dist/commands/cycle/current.js +78 -0
- package/dist/commands/cycle/list.js +84 -0
- package/dist/commands/cycle/move.js +91 -0
- package/dist/commands/describe.js +65 -0
- package/dist/commands/graphql/index.js +92 -0
- package/dist/commands/install-skill.js +54 -0
- package/dist/commands/issue/archive.js +75 -0
- package/dist/commands/issue/create.js +115 -0
- package/dist/commands/issue/get.js +84 -0
- package/dist/commands/issue/list.js +93 -0
- package/dist/commands/issue/purge.js +81 -0
- package/dist/commands/issue/search.js +109 -0
- package/dist/commands/issue/transition.js +91 -0
- package/dist/commands/issue/trash.js +75 -0
- package/dist/commands/issue/update.js +126 -0
- package/dist/commands/label/create.js +91 -0
- package/dist/commands/label/list.js +76 -0
- package/dist/commands/list-tools.js +47 -0
- package/dist/commands/me.js +71 -0
- package/dist/commands/project/create.js +101 -0
- package/dist/commands/project/get.js +83 -0
- package/dist/commands/project/list.js +75 -0
- package/dist/commands/project/update-status.js +99 -0
- package/dist/commands/project/update.js +99 -0
- package/dist/commands/raw/batch.js +85 -0
- package/dist/commands/raw/index.js +72 -0
- package/dist/commands/schema.js +69 -0
- package/dist/commands/state/list.js +77 -0
- package/dist/commands/team/get.js +73 -0
- package/dist/commands/team/list.js +73 -0
- package/dist/commands/whoami.js +71 -0
- package/dist/commands/workspace/add.js +97 -0
- package/dist/commands/workspace/list.js +47 -0
- package/dist/commands/workspace/remove.js +63 -0
- package/dist/commands/workspace/replace-token.js +89 -0
- package/dist/commands/workspace/use.js +54 -0
- package/dist/core/client/factory.js +28 -0
- package/dist/core/client/index.js +2 -0
- package/dist/core/config/index.js +4 -0
- package/dist/core/config/paths.js +30 -0
- package/dist/core/config/schema.js +36 -0
- package/dist/core/config/store.js +149 -0
- package/dist/core/errors/error.js +142 -0
- package/dist/core/errors/exit-codes.js +70 -0
- package/dist/core/output/envelope.js +53 -0
- package/dist/core/output/format.js +42 -0
- package/dist/core/output/index.js +3 -0
- package/dist/core/pagination/flags.js +29 -0
- package/dist/core/pagination/index.js +2 -0
- package/dist/core/projection/presets.js +116 -0
- package/dist/core/projection/project.js +282 -0
- package/dist/core/redact/redact.js +45 -0
- package/dist/core/resolvers/cycle.js +60 -0
- package/dist/core/resolvers/index.js +7 -0
- package/dist/core/resolvers/label.js +54 -0
- package/dist/core/resolvers/project-status.js +42 -0
- package/dist/core/resolvers/project.js +43 -0
- package/dist/core/resolvers/state.js +46 -0
- package/dist/core/resolvers/team.js +50 -0
- package/dist/core/transport/fetch-interceptor.js +109 -0
- package/dist/core/transport/index.js +3 -0
- package/dist/core/transport/rate-limit.js +167 -0
- package/dist/core/workspace/resolver.js +70 -0
- package/dist/core/workspace/write-guard.js +43 -0
- package/dist/generated/graphql.js +89428 -0
- package/dist/generated/operations.js +3013 -0
- package/dist/lib/comment-create-runtime.js +96 -0
- package/dist/lib/comment-delete-runtime.js +46 -0
- package/dist/lib/comment-list-runtime.js +182 -0
- package/dist/lib/comment-update-runtime.js +93 -0
- package/dist/lib/cycle-current-runtime.js +90 -0
- package/dist/lib/cycle-list-runtime.js +151 -0
- package/dist/lib/cycle-move-runtime.js +142 -0
- package/dist/lib/describe-runtime.js +180 -0
- package/dist/lib/filter-heuristics.js +59 -0
- package/dist/lib/graphql-runtime.js +202 -0
- package/dist/lib/include-fragments.js +73 -0
- package/dist/lib/install-skill-runtime.js +228 -0
- package/dist/lib/introspection-registry.js +488 -0
- package/dist/lib/issue-archive-runtime.js +89 -0
- package/dist/lib/issue-create-runtime.js +175 -0
- package/dist/lib/issue-get-runtime.js +153 -0
- package/dist/lib/issue-list-runtime.js +164 -0
- package/dist/lib/issue-purge-runtime.js +89 -0
- package/dist/lib/issue-search-runtime.js +114 -0
- package/dist/lib/issue-transition-runtime.js +131 -0
- package/dist/lib/issue-trash-runtime.js +84 -0
- package/dist/lib/issue-update-runtime.js +164 -0
- package/dist/lib/label-create-runtime.js +113 -0
- package/dist/lib/label-list-runtime.js +97 -0
- package/dist/lib/levenshtein.js +42 -0
- package/dist/lib/list-tools-runtime.js +38 -0
- package/dist/lib/me-runtime.js +55 -0
- package/dist/lib/project-create-runtime.js +103 -0
- package/dist/lib/project-get-runtime.js +134 -0
- package/dist/lib/project-list-runtime.js +84 -0
- package/dist/lib/project-update-runtime.js +110 -0
- package/dist/lib/project-update-status-runtime.js +91 -0
- package/dist/lib/raw-batch-runtime.js +229 -0
- package/dist/lib/raw-runtime.js +171 -0
- package/dist/lib/schema-loader.js +41 -0
- package/dist/lib/schema-runtime.js +65 -0
- package/dist/lib/state-list-runtime.js +93 -0
- package/dist/lib/team-get-runtime.js +55 -0
- package/dist/lib/team-list-runtime.js +52 -0
- package/dist/lib/workspace-runtime.js +112 -0
- package/dist/operations/_registry.zod.js +5337 -0
- package/oclif.manifest.json +3631 -0
- package/package.json +99 -0
- package/schema.graphql +30772 -0
- package/skills/linmux/SKILL.md +186 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { getLastComplexity, withFetchInterception } from "../core/transport/fetch-interceptor.js";
|
|
2
|
+
import { withRateLimitRetry } from "../core/transport/rate-limit.js";
|
|
3
|
+
import "../core/transport/index.js";
|
|
4
|
+
import { createLinearClient } from "../core/client/factory.js";
|
|
5
|
+
import "../core/client/index.js";
|
|
6
|
+
import { loadConfig } from "../core/config/store.js";
|
|
7
|
+
import "../core/config/index.js";
|
|
8
|
+
import { parseFields, project } from "../core/projection/project.js";
|
|
9
|
+
import { resolveWorkspace } from "../core/workspace/resolver.js";
|
|
10
|
+
import { parsePagination } from "../core/pagination/flags.js";
|
|
11
|
+
import "../core/pagination/index.js";
|
|
12
|
+
import { TEAM_KEY_RE, UUID_RE } from "./filter-heuristics.js";
|
|
13
|
+
//#region src/lib/label-list-runtime.ts
|
|
14
|
+
async function labelListRuntime(input) {
|
|
15
|
+
const config = (input.loadConfigOverride ?? loadConfig)();
|
|
16
|
+
const envForResolver = {};
|
|
17
|
+
if (input.env.LINEAR_WORKSPACE !== void 0) envForResolver.LINEAR_WORKSPACE = input.env.LINEAR_WORKSPACE;
|
|
18
|
+
if (input.env.LINEAR_API_KEY !== void 0) envForResolver.LINEAR_API_KEY = input.env.LINEAR_API_KEY;
|
|
19
|
+
const resolved = resolveWorkspace({
|
|
20
|
+
flags: input.flags.workspace ? { workspace: input.flags.workspace } : {},
|
|
21
|
+
env: envForResolver,
|
|
22
|
+
config
|
|
23
|
+
});
|
|
24
|
+
const fields = parseFields(input.flags.fields ?? "defaults", "label");
|
|
25
|
+
const { first, after } = parsePagination({
|
|
26
|
+
...input.flags.limit !== void 0 ? { limit: input.flags.limit } : {},
|
|
27
|
+
...input.flags.cursor !== void 0 ? { cursor: input.flags.cursor } : {}
|
|
28
|
+
});
|
|
29
|
+
let filter;
|
|
30
|
+
if (input.flags.team !== void 0 && input.flags.team !== "") filter = buildTeamFilter(input.flags.team);
|
|
31
|
+
const client = (input.clientFactoryOverride ?? createLinearClient)(resolved);
|
|
32
|
+
return withFetchInterception(async () => {
|
|
33
|
+
const args = { first };
|
|
34
|
+
if (after !== void 0) args.after = after;
|
|
35
|
+
if (filter !== void 0) args.filter = filter;
|
|
36
|
+
const connection = await withRateLimitRetry(() => client.issueLabels(args), input.retryOptsOverride);
|
|
37
|
+
const projected = await Promise.all(connection.nodes.map(async (node) => {
|
|
38
|
+
return project(await hydrateForProjection(node, fields), fields);
|
|
39
|
+
}));
|
|
40
|
+
const complexity = getLastComplexity();
|
|
41
|
+
return {
|
|
42
|
+
data: projected,
|
|
43
|
+
meta: {
|
|
44
|
+
workspace: resolved.name,
|
|
45
|
+
workspaceSource: resolved.source,
|
|
46
|
+
pageInfo: {
|
|
47
|
+
hasNextPage: Boolean(connection.pageInfo?.hasNextPage),
|
|
48
|
+
endCursor: connection.pageInfo?.endCursor ?? null,
|
|
49
|
+
hasPreviousPage: Boolean(connection.pageInfo?.hasPreviousPage),
|
|
50
|
+
startCursor: connection.pageInfo?.startCursor ?? null
|
|
51
|
+
},
|
|
52
|
+
...complexity !== void 0 ? { complexity } : {}
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
});
|
|
56
|
+
}
|
|
57
|
+
function buildTeamFilter(t) {
|
|
58
|
+
if (UUID_RE.test(t)) return { team: { id: { eq: t } } };
|
|
59
|
+
if (TEAM_KEY_RE.test(t)) return { team: { key: { eq: t.toUpperCase() } } };
|
|
60
|
+
return { team: { name: { eq: t } } };
|
|
61
|
+
}
|
|
62
|
+
const RELATION_KEYS = new Set([
|
|
63
|
+
"team",
|
|
64
|
+
"parent",
|
|
65
|
+
"creator"
|
|
66
|
+
]);
|
|
67
|
+
async function hydrateForProjection(label, spec) {
|
|
68
|
+
const needs = neededRelations(spec);
|
|
69
|
+
if (needs.size === 0) {
|
|
70
|
+
const out = {};
|
|
71
|
+
for (const k of Object.keys(label)) if (!RELATION_KEYS.has(k)) out[k] = label[k];
|
|
72
|
+
return out;
|
|
73
|
+
}
|
|
74
|
+
const hydrated = {};
|
|
75
|
+
for (const k of Object.keys(label)) if (RELATION_KEYS.has(k)) {
|
|
76
|
+
if (needs.has(k)) {
|
|
77
|
+
const value = label[k];
|
|
78
|
+
hydrated[k] = await resolveLazy(value);
|
|
79
|
+
}
|
|
80
|
+
} else hydrated[k] = label[k];
|
|
81
|
+
return hydrated;
|
|
82
|
+
}
|
|
83
|
+
function neededRelations(spec) {
|
|
84
|
+
if (spec === "*") return new Set(RELATION_KEYS);
|
|
85
|
+
const out = /* @__PURE__ */ new Set();
|
|
86
|
+
for (const path of spec) {
|
|
87
|
+
const head = path.split(".")[0];
|
|
88
|
+
if (head && RELATION_KEYS.has(head)) out.add(head);
|
|
89
|
+
}
|
|
90
|
+
return out;
|
|
91
|
+
}
|
|
92
|
+
async function resolveLazy(value) {
|
|
93
|
+
if (value && typeof value.then === "function") return await value;
|
|
94
|
+
return value;
|
|
95
|
+
}
|
|
96
|
+
//#endregion
|
|
97
|
+
export { labelListRuntime };
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
//#region src/lib/levenshtein.ts
|
|
2
|
+
/**
|
|
3
|
+
* Levenshtein distance and closest-name suggester.
|
|
4
|
+
*
|
|
5
|
+
* Extracted from `raw-runtime.ts` (Phase 3) for shared use by:
|
|
6
|
+
* - `raw-runtime.ts` — "did you mean X?" suggestions on RAW_OPERATION_NOT_FOUND
|
|
7
|
+
* - `describe-runtime.ts` (Phase 4) — DESCRIBE_COMMAND_NOT_FOUND suggestions
|
|
8
|
+
*
|
|
9
|
+
* The Int32Array flat-row-major implementation avoids biome's `noNonNullAssertion`
|
|
10
|
+
* warnings that a 2D array approach would trigger.
|
|
11
|
+
*/
|
|
12
|
+
/**
|
|
13
|
+
* Return the top `limit` closest names to `missing` by Levenshtein distance.
|
|
14
|
+
* Comparison is case-insensitive so "issues" matches "Issues" with distance 0.
|
|
15
|
+
*/
|
|
16
|
+
function suggestClosest(missing, names, limit = 3) {
|
|
17
|
+
return names.map((n) => ({
|
|
18
|
+
name: n,
|
|
19
|
+
d: levenshtein(missing.toLowerCase(), n.toLowerCase())
|
|
20
|
+
})).sort((a, b) => a.d - b.d).slice(0, limit).map((x) => x.name);
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Simple iterative Levenshtein distance.
|
|
24
|
+
* Uses flat typed array to avoid biome noNonNullAssertion warnings on 2D indexing.
|
|
25
|
+
*/
|
|
26
|
+
function levenshtein(a, b) {
|
|
27
|
+
const m = a.length;
|
|
28
|
+
const n = b.length;
|
|
29
|
+
const dp = new Int32Array((m + 1) * (n + 1));
|
|
30
|
+
for (let i = 0; i <= m; i++) dp[i * (n + 1)] = i;
|
|
31
|
+
for (let j = 0; j <= n; j++) dp[j] = j;
|
|
32
|
+
for (let i = 1; i <= m; i++) for (let j = 1; j <= n; j++) {
|
|
33
|
+
const sub = a[i - 1] === b[j - 1] ? 0 : 1;
|
|
34
|
+
const del = (dp[(i - 1) * (n + 1) + j] ?? i) + 1;
|
|
35
|
+
const ins = (dp[i * (n + 1) + (j - 1)] ?? j) + 1;
|
|
36
|
+
const rep = (dp[(i - 1) * (n + 1) + (j - 1)] ?? i + j) + sub;
|
|
37
|
+
dp[i * (n + 1) + j] = Math.min(del, ins, rep);
|
|
38
|
+
}
|
|
39
|
+
return dp[m * (n + 1) + n] ?? 0;
|
|
40
|
+
}
|
|
41
|
+
//#endregion
|
|
42
|
+
export { levenshtein, suggestClosest };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { CURATED_REGISTRY, getRawRegistryView } from "./introspection-registry.js";
|
|
2
|
+
//#region src/lib/list-tools-runtime.ts
|
|
3
|
+
/**
|
|
4
|
+
* list-tools runtime — Phase 4 PLAN 04-02, INT-01.
|
|
5
|
+
*
|
|
6
|
+
* Pure data assembly from CURATED_REGISTRY + OPERATION_REGISTRY. Zero network
|
|
7
|
+
* calls, zero workspace resolution. All data originates from committed static
|
|
8
|
+
* constants (threat T-04-02-S, T-04-02-T).
|
|
9
|
+
*
|
|
10
|
+
* Two-export pattern (S1): named `listToolsRuntime` export + default oclif class
|
|
11
|
+
* lives in src/commands/list-tools.ts.
|
|
12
|
+
*/
|
|
13
|
+
async function listToolsRuntime(_args) {
|
|
14
|
+
const curated = CURATED_REGISTRY.map((entry) => ({
|
|
15
|
+
id: entry.id,
|
|
16
|
+
summary: entry.summary,
|
|
17
|
+
flags: entry.flags,
|
|
18
|
+
...entry.raw_equivalent !== void 0 ? { raw_equivalent: entry.raw_equivalent } : {}
|
|
19
|
+
}));
|
|
20
|
+
const raw = getRawRegistryView().map((entry) => ({
|
|
21
|
+
name: entry.name,
|
|
22
|
+
kind: entry.kind
|
|
23
|
+
})).sort((a, b) => a.name.localeCompare(b.name));
|
|
24
|
+
return {
|
|
25
|
+
ok: true,
|
|
26
|
+
data: {
|
|
27
|
+
curated,
|
|
28
|
+
raw,
|
|
29
|
+
counts: {
|
|
30
|
+
curated: curated.length,
|
|
31
|
+
raw: raw.length
|
|
32
|
+
}
|
|
33
|
+
},
|
|
34
|
+
meta: { command: "list-tools" }
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
//#endregion
|
|
38
|
+
export { listToolsRuntime };
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { getLastComplexity, withFetchInterception } from "../core/transport/fetch-interceptor.js";
|
|
2
|
+
import { withRateLimitRetry } from "../core/transport/rate-limit.js";
|
|
3
|
+
import "../core/transport/index.js";
|
|
4
|
+
import { createLinearClient } from "../core/client/factory.js";
|
|
5
|
+
import "../core/client/index.js";
|
|
6
|
+
import { loadConfig } from "../core/config/store.js";
|
|
7
|
+
import "../core/config/index.js";
|
|
8
|
+
import { parseFields, project } from "../core/projection/project.js";
|
|
9
|
+
import { resolveWorkspace } from "../core/workspace/resolver.js";
|
|
10
|
+
//#region src/lib/me-runtime.ts
|
|
11
|
+
async function meRuntime(input) {
|
|
12
|
+
const config = (input.loadConfigOverride ?? loadConfig)();
|
|
13
|
+
const envForResolver = {};
|
|
14
|
+
if (input.env.LINEAR_WORKSPACE !== void 0) envForResolver.LINEAR_WORKSPACE = input.env.LINEAR_WORKSPACE;
|
|
15
|
+
if (input.env.LINEAR_API_KEY !== void 0) envForResolver.LINEAR_API_KEY = input.env.LINEAR_API_KEY;
|
|
16
|
+
const resolved = resolveWorkspace({
|
|
17
|
+
flags: input.flags.workspace ? { workspace: input.flags.workspace } : {},
|
|
18
|
+
env: envForResolver,
|
|
19
|
+
config
|
|
20
|
+
});
|
|
21
|
+
const fields = parseFields(input.flags.fields ?? "defaults", "user");
|
|
22
|
+
const client = (input.clientFactoryOverride ?? createLinearClient)(resolved);
|
|
23
|
+
return withFetchInterception(async () => {
|
|
24
|
+
const user = await withRateLimitRetry(() => Promise.resolve(client.viewer), input.retryOptsOverride);
|
|
25
|
+
const org = await withRateLimitRetry(() => Promise.resolve(user.organization), input.retryOptsOverride);
|
|
26
|
+
const data = {
|
|
27
|
+
user: project({
|
|
28
|
+
id: user.id,
|
|
29
|
+
name: user.name,
|
|
30
|
+
email: user.email,
|
|
31
|
+
displayName: user.displayName,
|
|
32
|
+
admin: user.admin,
|
|
33
|
+
isMe: user.isMe,
|
|
34
|
+
active: user.active,
|
|
35
|
+
avatarUrl: user.avatarUrl
|
|
36
|
+
}, fields),
|
|
37
|
+
organization: {
|
|
38
|
+
id: org.id,
|
|
39
|
+
name: org.name,
|
|
40
|
+
urlKey: org.urlKey
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
const complexity = getLastComplexity();
|
|
44
|
+
return {
|
|
45
|
+
data,
|
|
46
|
+
meta: {
|
|
47
|
+
workspace: resolved.name,
|
|
48
|
+
workspaceSource: resolved.source,
|
|
49
|
+
...complexity !== void 0 ? { complexity } : {}
|
|
50
|
+
}
|
|
51
|
+
};
|
|
52
|
+
});
|
|
53
|
+
}
|
|
54
|
+
//#endregion
|
|
55
|
+
export { meRuntime };
|
|
@@ -0,0 +1,103 @@
|
|
|
1
|
+
import { LinearAgentError } from "../core/errors/error.js";
|
|
2
|
+
import { getLastComplexity, withFetchInterception } from "../core/transport/fetch-interceptor.js";
|
|
3
|
+
import { withRateLimitRetry } from "../core/transport/rate-limit.js";
|
|
4
|
+
import "../core/transport/index.js";
|
|
5
|
+
import { createLinearClient } from "../core/client/factory.js";
|
|
6
|
+
import "../core/client/index.js";
|
|
7
|
+
import { loadConfig } from "../core/config/store.js";
|
|
8
|
+
import "../core/config/index.js";
|
|
9
|
+
import { parseFields, project } from "../core/projection/project.js";
|
|
10
|
+
import { resolveWorkspace } from "../core/workspace/resolver.js";
|
|
11
|
+
import { requireExplicitWorkspaceForWrite } from "../core/workspace/write-guard.js";
|
|
12
|
+
import { resolveTeamId } from "../core/resolvers/team.js";
|
|
13
|
+
import "../core/resolvers/index.js";
|
|
14
|
+
import { resolveAssignee } from "./issue-create-runtime.js";
|
|
15
|
+
//#region src/lib/project-create-runtime.ts
|
|
16
|
+
async function projectCreateRuntime(input) {
|
|
17
|
+
const config = (input.loadConfigOverride ?? loadConfig)();
|
|
18
|
+
const envForResolver = {};
|
|
19
|
+
if (input.env.LINEAR_WORKSPACE !== void 0) envForResolver.LINEAR_WORKSPACE = input.env.LINEAR_WORKSPACE;
|
|
20
|
+
if (input.env.LINEAR_API_KEY !== void 0) envForResolver.LINEAR_API_KEY = input.env.LINEAR_API_KEY;
|
|
21
|
+
const resolved = resolveWorkspace({
|
|
22
|
+
flags: input.flags.workspace ? { workspace: input.flags.workspace } : {},
|
|
23
|
+
env: envForResolver,
|
|
24
|
+
config
|
|
25
|
+
});
|
|
26
|
+
requireExplicitWorkspaceForWrite(resolved, input.flags.allowActiveWorkspaceWrite ?? false);
|
|
27
|
+
if (input.flags.name === void 0 || input.flags.name === "") throw LinearAgentError.usage("--name is required");
|
|
28
|
+
if (input.flags.teams === void 0 || input.flags.teams === "") throw LinearAgentError.usage("--teams is required (comma-separated team keys/UUIDs/names)");
|
|
29
|
+
const teamRefs = input.flags.teams.split(",").map((s) => s.trim()).filter(Boolean);
|
|
30
|
+
if (teamRefs.length === 0) throw LinearAgentError.usage("--teams is required (comma-separated team keys/UUIDs/names; got no valid entries)");
|
|
31
|
+
const name = input.flags.name;
|
|
32
|
+
const fields = parseFields(input.flags.fields ?? "defaults", "project");
|
|
33
|
+
const client = (input.clientFactoryOverride ?? createLinearClient)(resolved);
|
|
34
|
+
return withFetchInterception(async () => {
|
|
35
|
+
const workspaceKey = resolved.name ?? "_api-key-env_";
|
|
36
|
+
const flags = input.flags;
|
|
37
|
+
const [teamIds, leadId] = await Promise.all([Promise.all(teamRefs.map((t) => resolveTeamId(client, workspaceKey, t, input.retryOptsOverride))), flags.lead !== void 0 ? resolveAssignee(client, flags.lead, input.retryOptsOverride) : Promise.resolve(void 0)]);
|
|
38
|
+
const createInput = {
|
|
39
|
+
name,
|
|
40
|
+
teamIds
|
|
41
|
+
};
|
|
42
|
+
if (flags.description !== void 0) createInput.description = flags.description;
|
|
43
|
+
if (flags.state !== void 0) createInput.state = flags.state;
|
|
44
|
+
if (leadId !== void 0) createInput.leadId = leadId;
|
|
45
|
+
if (flags.startDate !== void 0) createInput.startDate = flags.startDate;
|
|
46
|
+
if (flags.targetDate !== void 0) createInput.targetDate = flags.targetDate;
|
|
47
|
+
const payload = await withRateLimitRetry(() => client.createProject(createInput), input.retryOptsOverride);
|
|
48
|
+
if (!payload.success) throw LinearAgentError.linear.apiError({
|
|
49
|
+
message: "createProject returned success=false",
|
|
50
|
+
details: { lastSyncId: payload.lastSyncId }
|
|
51
|
+
});
|
|
52
|
+
let created;
|
|
53
|
+
if (payload.project !== void 0) {
|
|
54
|
+
const p = await Promise.resolve(payload.project);
|
|
55
|
+
if (p !== void 0 && p !== null) created = p;
|
|
56
|
+
}
|
|
57
|
+
let data;
|
|
58
|
+
if (created) data = project(await hydrateForProjection(created, fields), fields);
|
|
59
|
+
else data = {};
|
|
60
|
+
const complexity = getLastComplexity();
|
|
61
|
+
const meta = {
|
|
62
|
+
workspace: resolved.name,
|
|
63
|
+
workspaceSource: resolved.source,
|
|
64
|
+
...complexity !== void 0 ? { complexity } : {}
|
|
65
|
+
};
|
|
66
|
+
return {
|
|
67
|
+
data,
|
|
68
|
+
meta
|
|
69
|
+
};
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
const RELATION_KEYS = new Set(["lead", "creator"]);
|
|
73
|
+
async function hydrateForProjection(proj, spec) {
|
|
74
|
+
const needs = neededRelations(spec);
|
|
75
|
+
if (needs.size === 0) {
|
|
76
|
+
const out = {};
|
|
77
|
+
for (const k of Object.keys(proj)) if (!RELATION_KEYS.has(k)) out[k] = proj[k];
|
|
78
|
+
return out;
|
|
79
|
+
}
|
|
80
|
+
const hydrated = {};
|
|
81
|
+
for (const k of Object.keys(proj)) if (RELATION_KEYS.has(k)) {
|
|
82
|
+
if (needs.has(k)) {
|
|
83
|
+
const value = proj[k];
|
|
84
|
+
hydrated[k] = await resolveLazy(value);
|
|
85
|
+
}
|
|
86
|
+
} else hydrated[k] = proj[k];
|
|
87
|
+
return hydrated;
|
|
88
|
+
}
|
|
89
|
+
function neededRelations(spec) {
|
|
90
|
+
if (spec === "*") return new Set(RELATION_KEYS);
|
|
91
|
+
const out = /* @__PURE__ */ new Set();
|
|
92
|
+
for (const path of spec) {
|
|
93
|
+
const head = path.split(".")[0];
|
|
94
|
+
if (head && RELATION_KEYS.has(head)) out.add(head);
|
|
95
|
+
}
|
|
96
|
+
return out;
|
|
97
|
+
}
|
|
98
|
+
async function resolveLazy(value) {
|
|
99
|
+
if (value && typeof value.then === "function") return await value;
|
|
100
|
+
return value;
|
|
101
|
+
}
|
|
102
|
+
//#endregion
|
|
103
|
+
export { projectCreateRuntime };
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { LinearAgentError } from "../core/errors/error.js";
|
|
2
|
+
import { redact } from "../core/redact/redact.js";
|
|
3
|
+
import { getLastComplexity, withFetchInterception } from "../core/transport/fetch-interceptor.js";
|
|
4
|
+
import { withRateLimitRetry } from "../core/transport/rate-limit.js";
|
|
5
|
+
import "../core/transport/index.js";
|
|
6
|
+
import { createLinearClient } from "../core/client/factory.js";
|
|
7
|
+
import "../core/client/index.js";
|
|
8
|
+
import { loadConfig } from "../core/config/store.js";
|
|
9
|
+
import "../core/config/index.js";
|
|
10
|
+
import { parseFields, project } from "../core/projection/project.js";
|
|
11
|
+
import { resolveWorkspace } from "../core/workspace/resolver.js";
|
|
12
|
+
import { UUID_RE } from "./filter-heuristics.js";
|
|
13
|
+
import { validateAndMergeIncludes } from "./include-fragments.js";
|
|
14
|
+
import { resolveProjectId } from "../core/resolvers/project.js";
|
|
15
|
+
import "../core/resolvers/index.js";
|
|
16
|
+
//#region src/lib/project-get-runtime.ts
|
|
17
|
+
async function projectGetRuntime(input) {
|
|
18
|
+
const config = (input.loadConfigOverride ?? loadConfig)();
|
|
19
|
+
const envForResolver = {};
|
|
20
|
+
if (input.env.LINEAR_WORKSPACE !== void 0) envForResolver.LINEAR_WORKSPACE = input.env.LINEAR_WORKSPACE;
|
|
21
|
+
if (input.env.LINEAR_API_KEY !== void 0) envForResolver.LINEAR_API_KEY = input.env.LINEAR_API_KEY;
|
|
22
|
+
const resolved = resolveWorkspace({
|
|
23
|
+
flags: input.flags.workspace ? { workspace: input.flags.workspace } : {},
|
|
24
|
+
env: envForResolver,
|
|
25
|
+
config
|
|
26
|
+
});
|
|
27
|
+
const fields = parseFields(input.flags.fields ?? "defaults", "project");
|
|
28
|
+
const client = (input.clientFactoryOverride ?? createLinearClient)(resolved);
|
|
29
|
+
const includes = input.flags.include ?? [];
|
|
30
|
+
if (includes.length > 0) {
|
|
31
|
+
const query = composeProjectGetWithIncludes(validateAndMergeIncludes("project get", includes));
|
|
32
|
+
return withFetchInterception(async () => {
|
|
33
|
+
const ref = input.args.ref;
|
|
34
|
+
const workspaceKey = resolved.name ?? "_api-key-env_";
|
|
35
|
+
const projectId = UUID_RE.test(ref) ? ref : await resolveProjectId(client, workspaceKey, ref, input.retryOptsOverride);
|
|
36
|
+
const response = await withRateLimitRetry(() => client.client.rawRequest(query, { id: projectId }), input.retryOptsOverride);
|
|
37
|
+
if (response.error ?? !response.data) {
|
|
38
|
+
const safeMessage = redact(response.error ?? "no data returned from Linear API");
|
|
39
|
+
const safeCause = response.error !== void 0 ? redact(response.error) : void 0;
|
|
40
|
+
throw LinearAgentError.linear.apiError({
|
|
41
|
+
message: safeMessage,
|
|
42
|
+
details: {
|
|
43
|
+
command: "project get",
|
|
44
|
+
cause: safeCause
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
const projectData = response.data.project;
|
|
49
|
+
if (!projectData) throw new LinearAgentError({
|
|
50
|
+
code: "PROJECT_NOT_FOUND",
|
|
51
|
+
message: `project not found: ${ref}`,
|
|
52
|
+
details: { ref }
|
|
53
|
+
});
|
|
54
|
+
const data = project(projectData, fields);
|
|
55
|
+
const complexity = getLastComplexity();
|
|
56
|
+
return {
|
|
57
|
+
data,
|
|
58
|
+
meta: {
|
|
59
|
+
workspace: resolved.name,
|
|
60
|
+
workspaceSource: resolved.source,
|
|
61
|
+
...complexity !== void 0 ? { complexity } : {}
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
return withFetchInterception(async () => {
|
|
67
|
+
const ref = input.args.ref;
|
|
68
|
+
const workspaceKey = resolved.name ?? "_api-key-env_";
|
|
69
|
+
const projectId = UUID_RE.test(ref) ? ref : await resolveProjectId(client, workspaceKey, ref, input.retryOptsOverride);
|
|
70
|
+
const proj = await withRateLimitRetry(() => client.project(projectId), input.retryOptsOverride);
|
|
71
|
+
if (!proj) throw new LinearAgentError({
|
|
72
|
+
code: "PROJECT_NOT_FOUND",
|
|
73
|
+
message: `project not found: ${ref}`,
|
|
74
|
+
details: { ref }
|
|
75
|
+
});
|
|
76
|
+
const data = project(await hydrateForProjection(proj, fields), fields);
|
|
77
|
+
const complexity = getLastComplexity();
|
|
78
|
+
return {
|
|
79
|
+
data,
|
|
80
|
+
meta: {
|
|
81
|
+
workspace: resolved.name,
|
|
82
|
+
workspaceSource: resolved.source,
|
|
83
|
+
...complexity !== void 0 ? { complexity } : {}
|
|
84
|
+
}
|
|
85
|
+
};
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
const RELATION_KEYS = new Set(["lead", "creator"]);
|
|
89
|
+
async function hydrateForProjection(proj, spec) {
|
|
90
|
+
const needs = neededRelations(spec);
|
|
91
|
+
if (needs.size === 0) {
|
|
92
|
+
const out = {};
|
|
93
|
+
for (const k of Object.keys(proj)) if (!RELATION_KEYS.has(k)) out[k] = proj[k];
|
|
94
|
+
return out;
|
|
95
|
+
}
|
|
96
|
+
const hydrated = {};
|
|
97
|
+
for (const k of Object.keys(proj)) if (RELATION_KEYS.has(k)) {
|
|
98
|
+
if (needs.has(k)) {
|
|
99
|
+
const value = proj[k];
|
|
100
|
+
hydrated[k] = await resolveLazy(value);
|
|
101
|
+
}
|
|
102
|
+
} else hydrated[k] = proj[k];
|
|
103
|
+
return hydrated;
|
|
104
|
+
}
|
|
105
|
+
function neededRelations(spec) {
|
|
106
|
+
if (spec === "*") return new Set(RELATION_KEYS);
|
|
107
|
+
const out = /* @__PURE__ */ new Set();
|
|
108
|
+
for (const path of spec) {
|
|
109
|
+
const head = path.split(".")[0];
|
|
110
|
+
if (head && RELATION_KEYS.has(head)) out.add(head);
|
|
111
|
+
}
|
|
112
|
+
return out;
|
|
113
|
+
}
|
|
114
|
+
async function resolveLazy(value) {
|
|
115
|
+
if (value && typeof value.then === "function") return await value;
|
|
116
|
+
return value;
|
|
117
|
+
}
|
|
118
|
+
function composeProjectGetWithIncludes(fragmentText) {
|
|
119
|
+
return `
|
|
120
|
+
query ProjectWithIncludes($id: String!) {
|
|
121
|
+
project(id: $id) {
|
|
122
|
+
id name description state progress sortOrder
|
|
123
|
+
startDate targetDate startedAt completedAt canceledAt archivedAt
|
|
124
|
+
createdAt updatedAt
|
|
125
|
+
color icon slugId url
|
|
126
|
+
lead { id email name }
|
|
127
|
+
creator { id email name }
|
|
128
|
+
${fragmentText}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
`.trim();
|
|
132
|
+
}
|
|
133
|
+
//#endregion
|
|
134
|
+
export { projectGetRuntime };
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
import { getLastComplexity, withFetchInterception } from "../core/transport/fetch-interceptor.js";
|
|
2
|
+
import { withRateLimitRetry } from "../core/transport/rate-limit.js";
|
|
3
|
+
import "../core/transport/index.js";
|
|
4
|
+
import { createLinearClient } from "../core/client/factory.js";
|
|
5
|
+
import "../core/client/index.js";
|
|
6
|
+
import { loadConfig } from "../core/config/store.js";
|
|
7
|
+
import "../core/config/index.js";
|
|
8
|
+
import { parseFields, project } from "../core/projection/project.js";
|
|
9
|
+
import { resolveWorkspace } from "../core/workspace/resolver.js";
|
|
10
|
+
import { parsePagination } from "../core/pagination/flags.js";
|
|
11
|
+
import "../core/pagination/index.js";
|
|
12
|
+
//#region src/lib/project-list-runtime.ts
|
|
13
|
+
async function projectListRuntime(input) {
|
|
14
|
+
const config = (input.loadConfigOverride ?? loadConfig)();
|
|
15
|
+
const envForResolver = {};
|
|
16
|
+
if (input.env.LINEAR_WORKSPACE !== void 0) envForResolver.LINEAR_WORKSPACE = input.env.LINEAR_WORKSPACE;
|
|
17
|
+
if (input.env.LINEAR_API_KEY !== void 0) envForResolver.LINEAR_API_KEY = input.env.LINEAR_API_KEY;
|
|
18
|
+
const resolved = resolveWorkspace({
|
|
19
|
+
flags: input.flags.workspace ? { workspace: input.flags.workspace } : {},
|
|
20
|
+
env: envForResolver,
|
|
21
|
+
config
|
|
22
|
+
});
|
|
23
|
+
const fields = parseFields(input.flags.fields ?? "defaults", "project");
|
|
24
|
+
const { first, after } = parsePagination({
|
|
25
|
+
...input.flags.limit !== void 0 ? { limit: input.flags.limit } : {},
|
|
26
|
+
...input.flags.cursor !== void 0 ? { cursor: input.flags.cursor } : {}
|
|
27
|
+
});
|
|
28
|
+
const client = (input.clientFactoryOverride ?? createLinearClient)(resolved);
|
|
29
|
+
return withFetchInterception(async () => {
|
|
30
|
+
const projectsArgs = { first };
|
|
31
|
+
if (after !== void 0) projectsArgs.after = after;
|
|
32
|
+
const connection = await withRateLimitRetry(() => client.projects(projectsArgs), input.retryOptsOverride);
|
|
33
|
+
const projected = await Promise.all(connection.nodes.map(async (node) => {
|
|
34
|
+
return project(await hydrateForProjection(node, fields), fields);
|
|
35
|
+
}));
|
|
36
|
+
const complexity = getLastComplexity();
|
|
37
|
+
return {
|
|
38
|
+
data: projected,
|
|
39
|
+
meta: {
|
|
40
|
+
workspace: resolved.name,
|
|
41
|
+
workspaceSource: resolved.source,
|
|
42
|
+
pageInfo: {
|
|
43
|
+
hasNextPage: Boolean(connection.pageInfo?.hasNextPage),
|
|
44
|
+
endCursor: connection.pageInfo?.endCursor ?? null,
|
|
45
|
+
hasPreviousPage: Boolean(connection.pageInfo?.hasPreviousPage),
|
|
46
|
+
startCursor: connection.pageInfo?.startCursor ?? null
|
|
47
|
+
},
|
|
48
|
+
...complexity !== void 0 ? { complexity } : {}
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
});
|
|
52
|
+
}
|
|
53
|
+
const RELATION_KEYS = new Set(["lead", "creator"]);
|
|
54
|
+
async function hydrateForProjection(proj, spec) {
|
|
55
|
+
const needs = neededRelations(spec);
|
|
56
|
+
if (needs.size === 0) {
|
|
57
|
+
const out = {};
|
|
58
|
+
for (const k of Object.keys(proj)) if (!RELATION_KEYS.has(k)) out[k] = proj[k];
|
|
59
|
+
return out;
|
|
60
|
+
}
|
|
61
|
+
const hydrated = {};
|
|
62
|
+
for (const k of Object.keys(proj)) if (RELATION_KEYS.has(k)) {
|
|
63
|
+
if (needs.has(k)) {
|
|
64
|
+
const value = proj[k];
|
|
65
|
+
hydrated[k] = await resolveLazy(value);
|
|
66
|
+
}
|
|
67
|
+
} else hydrated[k] = proj[k];
|
|
68
|
+
return hydrated;
|
|
69
|
+
}
|
|
70
|
+
function neededRelations(spec) {
|
|
71
|
+
if (spec === "*") return new Set(RELATION_KEYS);
|
|
72
|
+
const out = /* @__PURE__ */ new Set();
|
|
73
|
+
for (const path of spec) {
|
|
74
|
+
const head = path.split(".")[0];
|
|
75
|
+
if (head && RELATION_KEYS.has(head)) out.add(head);
|
|
76
|
+
}
|
|
77
|
+
return out;
|
|
78
|
+
}
|
|
79
|
+
async function resolveLazy(value) {
|
|
80
|
+
if (value && typeof value.then === "function") return await value;
|
|
81
|
+
return value;
|
|
82
|
+
}
|
|
83
|
+
//#endregion
|
|
84
|
+
export { projectListRuntime };
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { LinearAgentError } from "../core/errors/error.js";
|
|
2
|
+
import { getLastComplexity, withFetchInterception } from "../core/transport/fetch-interceptor.js";
|
|
3
|
+
import { withRateLimitRetry } from "../core/transport/rate-limit.js";
|
|
4
|
+
import "../core/transport/index.js";
|
|
5
|
+
import { createLinearClient } from "../core/client/factory.js";
|
|
6
|
+
import "../core/client/index.js";
|
|
7
|
+
import { loadConfig } from "../core/config/store.js";
|
|
8
|
+
import "../core/config/index.js";
|
|
9
|
+
import { parseFields, project } from "../core/projection/project.js";
|
|
10
|
+
import { resolveWorkspace } from "../core/workspace/resolver.js";
|
|
11
|
+
import { requireExplicitWorkspaceForWrite } from "../core/workspace/write-guard.js";
|
|
12
|
+
import { UUID_RE } from "./filter-heuristics.js";
|
|
13
|
+
import { resolveProjectId } from "../core/resolvers/project.js";
|
|
14
|
+
import "../core/resolvers/index.js";
|
|
15
|
+
import { resolveAssignee } from "./issue-create-runtime.js";
|
|
16
|
+
//#region src/lib/project-update-runtime.ts
|
|
17
|
+
const NO_FIELDS_MESSAGE = "no fields to update -- pass at least one of --name, --description, --state, --lead, --start-date, --target-date";
|
|
18
|
+
function hasAnyFieldFlag(flags) {
|
|
19
|
+
return flags.name !== void 0 || flags.description !== void 0 || flags.state !== void 0 || flags.lead !== void 0 || flags.startDate !== void 0 || flags.targetDate !== void 0;
|
|
20
|
+
}
|
|
21
|
+
async function projectUpdateRuntime(input) {
|
|
22
|
+
const config = (input.loadConfigOverride ?? loadConfig)();
|
|
23
|
+
const envForResolver = {};
|
|
24
|
+
if (input.env.LINEAR_WORKSPACE !== void 0) envForResolver.LINEAR_WORKSPACE = input.env.LINEAR_WORKSPACE;
|
|
25
|
+
if (input.env.LINEAR_API_KEY !== void 0) envForResolver.LINEAR_API_KEY = input.env.LINEAR_API_KEY;
|
|
26
|
+
const resolved = resolveWorkspace({
|
|
27
|
+
flags: input.flags.workspace ? { workspace: input.flags.workspace } : {},
|
|
28
|
+
env: envForResolver,
|
|
29
|
+
config
|
|
30
|
+
});
|
|
31
|
+
requireExplicitWorkspaceForWrite(resolved, input.flags.allowActiveWorkspaceWrite ?? false);
|
|
32
|
+
if (!hasAnyFieldFlag(input.flags)) throw new LinearAgentError({
|
|
33
|
+
code: "VALIDATION_NO_FIELDS",
|
|
34
|
+
message: NO_FIELDS_MESSAGE
|
|
35
|
+
});
|
|
36
|
+
const fields = parseFields(input.flags.fields ?? "defaults", "project");
|
|
37
|
+
const client = (input.clientFactoryOverride ?? createLinearClient)(resolved);
|
|
38
|
+
return withFetchInterception(async () => {
|
|
39
|
+
const workspaceKey = resolved.name ?? "_api-key-env_";
|
|
40
|
+
const ref = input.args.ref;
|
|
41
|
+
const flags = input.flags;
|
|
42
|
+
const projectId = UUID_RE.test(ref) ? ref : await resolveProjectId(client, workspaceKey, ref, input.retryOptsOverride);
|
|
43
|
+
const updateInput = {};
|
|
44
|
+
if (flags.name !== void 0) updateInput.name = flags.name;
|
|
45
|
+
if (flags.description !== void 0) updateInput.description = flags.description;
|
|
46
|
+
if (flags.state !== void 0) updateInput.state = flags.state;
|
|
47
|
+
if (flags.startDate !== void 0) updateInput.startDate = flags.startDate;
|
|
48
|
+
if (flags.targetDate !== void 0) updateInput.targetDate = flags.targetDate;
|
|
49
|
+
if (flags.lead !== void 0) updateInput.leadId = await resolveAssignee(client, flags.lead, input.retryOptsOverride);
|
|
50
|
+
if (Object.keys(updateInput).length === 0) throw new LinearAgentError({
|
|
51
|
+
code: "VALIDATION_NO_FIELDS",
|
|
52
|
+
message: NO_FIELDS_MESSAGE
|
|
53
|
+
});
|
|
54
|
+
const payload = await withRateLimitRetry(() => client.updateProject(projectId, updateInput), input.retryOptsOverride);
|
|
55
|
+
if (!payload.success) throw LinearAgentError.linear.apiError({
|
|
56
|
+
message: "updateProject returned success=false",
|
|
57
|
+
details: { lastSyncId: payload.lastSyncId }
|
|
58
|
+
});
|
|
59
|
+
let updated;
|
|
60
|
+
if (payload.project !== void 0) {
|
|
61
|
+
const u = await Promise.resolve(payload.project);
|
|
62
|
+
if (u !== void 0 && u !== null) updated = u;
|
|
63
|
+
}
|
|
64
|
+
let data;
|
|
65
|
+
if (updated) data = project(await hydrateForProjection(updated, fields), fields);
|
|
66
|
+
else data = { id: projectId };
|
|
67
|
+
const complexity = getLastComplexity();
|
|
68
|
+
const meta = {
|
|
69
|
+
workspace: resolved.name,
|
|
70
|
+
workspaceSource: resolved.source,
|
|
71
|
+
...complexity !== void 0 ? { complexity } : {}
|
|
72
|
+
};
|
|
73
|
+
return {
|
|
74
|
+
data,
|
|
75
|
+
meta
|
|
76
|
+
};
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
const RELATION_KEYS = new Set(["lead", "creator"]);
|
|
80
|
+
async function hydrateForProjection(proj, spec) {
|
|
81
|
+
const needs = neededRelations(spec);
|
|
82
|
+
if (needs.size === 0) {
|
|
83
|
+
const out = {};
|
|
84
|
+
for (const k of Object.keys(proj)) if (!RELATION_KEYS.has(k)) out[k] = proj[k];
|
|
85
|
+
return out;
|
|
86
|
+
}
|
|
87
|
+
const hydrated = {};
|
|
88
|
+
for (const k of Object.keys(proj)) if (RELATION_KEYS.has(k)) {
|
|
89
|
+
if (needs.has(k)) {
|
|
90
|
+
const value = proj[k];
|
|
91
|
+
hydrated[k] = await resolveLazy(value);
|
|
92
|
+
}
|
|
93
|
+
} else hydrated[k] = proj[k];
|
|
94
|
+
return hydrated;
|
|
95
|
+
}
|
|
96
|
+
function neededRelations(spec) {
|
|
97
|
+
if (spec === "*") return new Set(RELATION_KEYS);
|
|
98
|
+
const out = /* @__PURE__ */ new Set();
|
|
99
|
+
for (const path of spec) {
|
|
100
|
+
const head = path.split(".")[0];
|
|
101
|
+
if (head && RELATION_KEYS.has(head)) out.add(head);
|
|
102
|
+
}
|
|
103
|
+
return out;
|
|
104
|
+
}
|
|
105
|
+
async function resolveLazy(value) {
|
|
106
|
+
if (value && typeof value.then === "function") return await value;
|
|
107
|
+
return value;
|
|
108
|
+
}
|
|
109
|
+
//#endregion
|
|
110
|
+
export { projectUpdateRuntime };
|