@enrichlayer/el-linear 1.22.0 → 1.23.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.
@@ -1,6 +1,6 @@
1
1
  import { loadConfig } from "../config/config.js";
2
2
  import { resolveTeam } from "../config/resolver.js";
3
- import { ARCHIVE_PROJECT_MUTATION, CREATE_PROJECT_MUTATION, DELETE_PROJECT_MUTATION, GET_PROJECT_QUERY, GET_PROJECT_TEAM_ISSUES_QUERY, PROJECT_BY_ID_QUERY, SEARCH_PROJECTS_BY_NAME_QUERY, UPDATE_PROJECT_MUTATION, } from "../queries/projects.js";
3
+ import { ARCHIVE_PROJECT_MUTATION, CREATE_PROJECT_MUTATION, DELETE_PROJECT_MUTATION, GET_PROJECT_QUERY, GET_PROJECT_TEAM_ISSUES_QUERY, PROJECT_BY_ID_QUERY, PROJECT_READ_QUERY, SEARCH_PROJECTS_BY_NAME_QUERY, UPDATE_PROJECT_MUTATION, } from "../queries/projects.js";
4
4
  import { cached, resolveCacheTTL } from "../utils/disk-cache.js";
5
5
  import { createGraphQLService } from "../utils/graphql-service.js";
6
6
  import { createLinearService } from "../utils/linear-service.js";
@@ -413,6 +413,30 @@ async function handleDeleteProject(projectNameOrId, _options, command) {
413
413
  lastSyncId: payload.lastSyncId,
414
414
  });
415
415
  }
416
+ /**
417
+ * Read a single project (DEV-4610). Resolves the identifier through the same
418
+ * `resolveProjectId` path the `--project` flags use (UUID / slug / URL / name),
419
+ * fetches the full project, and emits it. `teams.nodes` is flattened to a plain
420
+ * array so the `--format summary` project renderer (and the JSON shape) match
421
+ * `projects list` — the formatter expects a flat team list, not a `{ nodes }`
422
+ * connection. Replaces the raw-`graphql` workaround for grabbing a project's
423
+ * url / content / lead.
424
+ */
425
+ async function handleReadProject(projectNameOrId, _options, command) {
426
+ const rootOpts = getRootOpts(command);
427
+ const graphQLService = await createGraphQLService(rootOpts);
428
+ const linearService = await createLinearService(rootOpts);
429
+ const projectId = await linearService.resolveProjectId(projectNameOrId);
430
+ const result = await graphQLService.rawRequest(PROJECT_READ_QUERY, { id: projectId });
431
+ if (!result.project) {
432
+ throw new Error(`Project "${projectNameOrId}" not found`);
433
+ }
434
+ const { teams, ...rest } = result.project;
435
+ outputSuccess({
436
+ ...rest,
437
+ teams: teams.nodes.map((t) => ({ id: t.id, key: t.key, name: t.name })),
438
+ });
439
+ }
416
440
  export function setupProjectsCommands(program) {
417
441
  const projects = program
418
442
  .command("projects")
@@ -435,6 +459,10 @@ export function setupProjectsCommands(program) {
435
459
  .command("delete <project>")
436
460
  .description("Delete (trash) a project (resolves names)")
437
461
  .action(handleAsyncCommand(handleDeleteProject));
462
+ projects
463
+ .command("read <project>")
464
+ .description("Read one project's full details (resolves name/slug/URL/ID). `--format summary` shows state, lead, teams, target, progress, url; JSON includes description/content.")
465
+ .action(handleAsyncCommand(handleReadProject));
438
466
  projects
439
467
  .command("list")
440
468
  .description("List projects")
@@ -34,6 +34,24 @@ interface ProjectWithIssuesNode extends ProjectBaseNode {
34
34
  export interface ProjectByIdResponse {
35
35
  project: ProjectBaseNode | null;
36
36
  }
37
+ /** Full project read (`PROJECT_READ_QUERY`) — base fields + summary/JSON fields. */
38
+ interface ProjectReadNode extends ProjectBaseNode {
39
+ state: string;
40
+ progress: number;
41
+ url: string;
42
+ startDate: string | null;
43
+ targetDate: string | null;
44
+ description: string | null;
45
+ content: string | null;
46
+ lead: {
47
+ id: string;
48
+ name: string;
49
+ displayName: string;
50
+ } | null;
51
+ }
52
+ export interface ProjectReadResponse {
53
+ project: ProjectReadNode | null;
54
+ }
37
55
  export interface GetProjectResponse {
38
56
  projects: {
39
57
  nodes: ProjectBaseNode[];
@@ -1,5 +1,14 @@
1
1
  export declare const TEAM_LOOKUP_QUERY = "\n query TeamLookup($key: String) {\n teams(filter: { or: [{ key: { eq: $key } }, { name: { eq: $key } }] }, first: 1) {\n nodes { id key name }\n }\n }\n";
2
2
  export declare const PROJECT_BY_ID_QUERY = "\n query ProjectById($id: String!) {\n project(id: $id) {\n id\n name\n teams {\n nodes { id key name }\n }\n }\n }\n";
3
+ /**
4
+ * Full single-project read for `el-linear projects read` (DEV-4610). Carries
5
+ * everything the `--format summary` project renderer shows (name, state, lead,
6
+ * teams, target, progress, url) plus the long-form `description`/`content` the
7
+ * JSON envelope exposes — so callers no longer fall back to a raw `graphql`
8
+ * query for a project URL or its content. The `progress` + `teams` fields also
9
+ * make the payload self-describing to the summary kind-inference.
10
+ */
11
+ export declare const PROJECT_READ_QUERY = "\n query ProjectRead($id: String!) {\n project(id: $id) {\n id\n name\n state\n progress\n url\n startDate\n targetDate\n description\n content\n lead { id name displayName }\n teams {\n nodes { id key name }\n }\n }\n }\n";
3
12
  export declare const GET_PROJECT_QUERY = "\n query GetProject($name: String!) {\n projects(filter: { name: { eqIgnoreCase: $name } }, first: 1) {\n nodes {\n id\n name\n teams {\n nodes {\n id\n key\n name\n }\n }\n }\n }\n }\n";
4
13
  export declare const GET_PROJECT_TEAM_ISSUES_QUERY = "\n query GetProjectTeamIssues($projectId: String!, $teamId: String!) {\n project(id: $projectId) {\n id\n name\n teams {\n nodes {\n id\n key\n name\n }\n }\n issues(filter: { team: { id: { eq: $teamId } } }, first: 50) {\n nodes {\n id\n identifier\n title\n }\n }\n }\n }\n";
5
14
  export declare const SEARCH_PROJECTS_BY_NAME_QUERY = "\n query SearchProjectsByName($name: String!) {\n projects(filter: { name: { containsIgnoreCase: $name } }, first: 10) {\n nodes {\n id\n name\n state\n teams {\n nodes { id key name }\n }\n }\n }\n }\n";
@@ -16,6 +16,33 @@ export const PROJECT_BY_ID_QUERY = `
16
16
  }
17
17
  }
18
18
  `;
19
+ /**
20
+ * Full single-project read for `el-linear projects read` (DEV-4610). Carries
21
+ * everything the `--format summary` project renderer shows (name, state, lead,
22
+ * teams, target, progress, url) plus the long-form `description`/`content` the
23
+ * JSON envelope exposes — so callers no longer fall back to a raw `graphql`
24
+ * query for a project URL or its content. The `progress` + `teams` fields also
25
+ * make the payload self-describing to the summary kind-inference.
26
+ */
27
+ export const PROJECT_READ_QUERY = `
28
+ query ProjectRead($id: String!) {
29
+ project(id: $id) {
30
+ id
31
+ name
32
+ state
33
+ progress
34
+ url
35
+ startDate
36
+ targetDate
37
+ description
38
+ content
39
+ lead { id name displayName }
40
+ teams {
41
+ nodes { id key name }
42
+ }
43
+ }
44
+ }
45
+ `;
19
46
  export const GET_PROJECT_QUERY = `
20
47
  query GetProject($name: String!) {
21
48
  projects(filter: { name: { eqIgnoreCase: $name } }, first: 1) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@enrichlayer/el-linear",
3
- "version": "1.22.0",
3
+ "version": "1.23.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",