@skillrecordings/cli 0.12.0 → 0.13.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/dist/index.js CHANGED
@@ -58568,9 +58568,9 @@ var require_addComment = __commonJS({
58568
58568
  Object.defineProperty(exports, "__esModule", {
58569
58569
  value: true
58570
58570
  });
58571
- exports.default = addComment;
58571
+ exports.default = addComment2;
58572
58572
  var _addComments = require_addComments();
58573
- function addComment(node, type, content, line) {
58573
+ function addComment2(node, type, content, line) {
58574
58574
  return (0, _addComments.default)(node, type, [{
58575
58575
  type: line ? "CommentLine" : "CommentBlock",
58576
58576
  value: content
@@ -73654,7 +73654,7 @@ var require_comments = __commonJS({
73654
73654
  Object.defineProperty(exports, "__esModule", {
73655
73655
  value: true
73656
73656
  });
73657
- exports.addComment = addComment;
73657
+ exports.addComment = addComment2;
73658
73658
  exports.addComments = addComments;
73659
73659
  exports.shareCommentsWithSiblings = shareCommentsWithSiblings;
73660
73660
  var _t = require_lib6();
@@ -73693,7 +73693,7 @@ var require_comments = __commonJS({
73693
73693
  return !set.has(el);
73694
73694
  });
73695
73695
  }
73696
- function addComment(type, content, line) {
73696
+ function addComment2(type, content, line) {
73697
73697
  _addComment(this.node, type, content, line);
73698
73698
  }
73699
73699
  function addComments(type, comments) {
@@ -74833,7 +74833,7 @@ var SECRET_REFS = {
74833
74833
  QDRANT_COLLECTION: "op://Support/skill-cli/QDRANT_COLLECTION",
74834
74834
  OLLAMA_BASE_URL: "op://Support/skill-cli/OLLAMA_BASE_URL",
74835
74835
  EMBEDDING_MODEL: "op://Support/skill-cli/EMBEDDING_MODEL",
74836
- AGE_SECRET_KEY: "op://Support/skill-cli-age-key/password"
74836
+ AGE_SECRET_KEY: "op://Support/skill-cli-age-key/private_key"
74837
74837
  };
74838
74838
 
74839
74839
  // src/core/secrets.ts
@@ -113466,6 +113466,2440 @@ function registerKbCommands(program3) {
113466
113466
  });
113467
113467
  }
113468
113468
 
113469
+ // src/commands/linear/index.ts
113470
+ init_esm_shims();
113471
+
113472
+ // src/commands/linear/assign.ts
113473
+ init_esm_shims();
113474
+
113475
+ // src/commands/linear/client.ts
113476
+ init_esm_shims();
113477
+ import { LinearClient } from "@linear/sdk";
113478
+ function getLinearClient() {
113479
+ const apiKey = process.env.LINEAR_API_KEY;
113480
+ if (!apiKey) {
113481
+ throw new CLIError({
113482
+ userMessage: "LINEAR_API_KEY environment variable is not set.",
113483
+ suggestion: "Set LINEAR_API_KEY to your Linear API token. Get it from https://linear.app/settings/api",
113484
+ exitCode: 1
113485
+ });
113486
+ }
113487
+ return new LinearClient({ apiKey });
113488
+ }
113489
+
113490
+ // src/commands/linear/hateoas.ts
113491
+ init_esm_shims();
113492
+ function hateoasWrap2(opts) {
113493
+ return {
113494
+ _type: opts.type,
113495
+ _command: opts.command,
113496
+ data: opts.data,
113497
+ _links: opts.links ?? [],
113498
+ _actions: opts.actions ?? []
113499
+ };
113500
+ }
113501
+ function issueLinks(identifier, teamKey) {
113502
+ const links = [
113503
+ {
113504
+ rel: "self",
113505
+ command: `skill linear issue ${identifier} --json`,
113506
+ description: "This issue"
113507
+ },
113508
+ {
113509
+ rel: "comments",
113510
+ command: `skill linear comments ${identifier} --json`,
113511
+ description: "Comments on this issue"
113512
+ }
113513
+ ];
113514
+ if (teamKey) {
113515
+ links.push({
113516
+ rel: "team-issues",
113517
+ command: `skill linear issues --team ${teamKey} --json`,
113518
+ description: "All issues in this team"
113519
+ });
113520
+ }
113521
+ return links;
113522
+ }
113523
+ function issueActions(identifier) {
113524
+ return [
113525
+ {
113526
+ action: "comment",
113527
+ command: `skill linear comment ${identifier} --body "<text>"`,
113528
+ description: "Add a comment"
113529
+ },
113530
+ {
113531
+ action: "assign",
113532
+ command: `skill linear assign ${identifier} --to <user-email>`,
113533
+ description: "Assign this issue"
113534
+ },
113535
+ {
113536
+ action: "unassign",
113537
+ command: `skill linear assign ${identifier} --unassign`,
113538
+ description: "Unassign this issue"
113539
+ },
113540
+ {
113541
+ action: "update-state",
113542
+ command: `skill linear state ${identifier} --state "<state-name>"`,
113543
+ description: "Change workflow state"
113544
+ },
113545
+ {
113546
+ action: "update-priority",
113547
+ command: `skill linear update ${identifier} --priority <0-4>`,
113548
+ description: "Change priority (0=urgent, 4=none)"
113549
+ },
113550
+ {
113551
+ action: "add-label",
113552
+ command: `skill linear label ${identifier} --add "<label-name>"`,
113553
+ description: "Add a label"
113554
+ },
113555
+ {
113556
+ action: "close",
113557
+ command: `skill linear close ${identifier}`,
113558
+ description: "Close this issue",
113559
+ destructive: true
113560
+ },
113561
+ {
113562
+ action: "link",
113563
+ command: `skill linear link ${identifier} --blocks <other-id>`,
113564
+ description: "Link to another issue"
113565
+ }
113566
+ ];
113567
+ }
113568
+ function issueListLinks(issues, teamKey) {
113569
+ const links = issues.slice(0, 10).map((issue) => ({
113570
+ rel: "issue",
113571
+ command: `skill linear issue ${issue.identifier} --json`,
113572
+ description: issue.title
113573
+ }));
113574
+ if (teamKey) {
113575
+ links.push({
113576
+ rel: "team",
113577
+ command: `skill linear team ${teamKey} --json`,
113578
+ description: "Parent team"
113579
+ });
113580
+ }
113581
+ return links;
113582
+ }
113583
+ function issueListActions(teamKey) {
113584
+ const actions = [
113585
+ {
113586
+ action: "create",
113587
+ command: `skill linear create "<title>"${teamKey ? ` --team ${teamKey}` : ""}`,
113588
+ description: "Create a new issue"
113589
+ },
113590
+ {
113591
+ action: "search",
113592
+ command: `skill linear search "<query>"`,
113593
+ description: "Search issues"
113594
+ },
113595
+ {
113596
+ action: "my-issues",
113597
+ command: `skill linear my --json`,
113598
+ description: "View my assigned issues"
113599
+ }
113600
+ ];
113601
+ if (teamKey) {
113602
+ actions.push({
113603
+ action: "team-states",
113604
+ command: `skill linear states ${teamKey} --json`,
113605
+ description: "View workflow states"
113606
+ });
113607
+ actions.push({
113608
+ action: "team-labels",
113609
+ command: `skill linear labels ${teamKey} --json`,
113610
+ description: "View available labels"
113611
+ });
113612
+ }
113613
+ return actions;
113614
+ }
113615
+ function teamLinks(teamKey) {
113616
+ return [
113617
+ {
113618
+ rel: "self",
113619
+ command: `skill linear team ${teamKey} --json`,
113620
+ description: "This team"
113621
+ },
113622
+ {
113623
+ rel: "issues",
113624
+ command: `skill linear issues --team ${teamKey} --json`,
113625
+ description: "Issues in this team"
113626
+ },
113627
+ {
113628
+ rel: "states",
113629
+ command: `skill linear states ${teamKey} --json`,
113630
+ description: "Workflow states"
113631
+ },
113632
+ {
113633
+ rel: "labels",
113634
+ command: `skill linear labels ${teamKey} --json`,
113635
+ description: "Labels"
113636
+ }
113637
+ ];
113638
+ }
113639
+ function commentLinks(commentId, issueIdentifier) {
113640
+ return [
113641
+ {
113642
+ rel: "issue",
113643
+ command: `skill linear issue ${issueIdentifier} --json`,
113644
+ description: "Parent issue"
113645
+ },
113646
+ {
113647
+ rel: "all-comments",
113648
+ command: `skill linear comments ${issueIdentifier} --json`,
113649
+ description: "All comments"
113650
+ }
113651
+ ];
113652
+ }
113653
+ function userListLinks(users) {
113654
+ return users.map((u) => ({
113655
+ rel: "user-issues",
113656
+ command: `skill linear issues --assignee ${u.email} --json`,
113657
+ description: `${u.name} (${u.email})`
113658
+ }));
113659
+ }
113660
+
113661
+ // src/commands/linear/assign.ts
113662
+ async function assignIssue(ctx, issueId, options) {
113663
+ if (options.to && options.unassign) {
113664
+ throw new CLIError({
113665
+ userMessage: "Cannot use both --to and --unassign.",
113666
+ suggestion: "Choose one: --to <email> OR --unassign",
113667
+ exitCode: 1
113668
+ });
113669
+ }
113670
+ if (!options.to && !options.unassign) {
113671
+ throw new CLIError({
113672
+ userMessage: "Missing assignment option.",
113673
+ suggestion: "Use --to <email> to assign or --unassign to remove assignee.",
113674
+ exitCode: 1
113675
+ });
113676
+ }
113677
+ try {
113678
+ const client = getLinearClient();
113679
+ const issue = await client.issue(issueId);
113680
+ if (!issue) {
113681
+ throw new CLIError({
113682
+ userMessage: `Issue not found: ${issueId}`,
113683
+ suggestion: "Use `skill linear issues --json` to list available issues.",
113684
+ exitCode: 1
113685
+ });
113686
+ }
113687
+ let assigneeId = null;
113688
+ let assigneeName = null;
113689
+ if (options.to) {
113690
+ if (options.to.toLowerCase() === "me") {
113691
+ const viewer = await client.viewer;
113692
+ assigneeId = viewer.id;
113693
+ assigneeName = viewer.name;
113694
+ } else {
113695
+ const users = await client.users();
113696
+ const user = users.nodes.find(
113697
+ (u) => u.email?.toLowerCase() === options.to.toLowerCase() || u.name?.toLowerCase() === options.to.toLowerCase()
113698
+ );
113699
+ if (!user) {
113700
+ throw new CLIError({
113701
+ userMessage: `User not found: ${options.to}`,
113702
+ suggestion: "Use `skill linear users --json` to list available users.",
113703
+ exitCode: 1
113704
+ });
113705
+ }
113706
+ assigneeId = user.id;
113707
+ assigneeName = user.name;
113708
+ }
113709
+ }
113710
+ await client.updateIssue(issue.id, {
113711
+ assigneeId: assigneeId || void 0
113712
+ });
113713
+ const team = await issue.team;
113714
+ const resultData = {
113715
+ issueId: issue.id,
113716
+ issueIdentifier: issue.identifier,
113717
+ action: options.unassign ? "unassigned" : "assigned",
113718
+ assignee: assigneeName ? { id: assigneeId, name: assigneeName } : null,
113719
+ success: true
113720
+ };
113721
+ if (ctx.format === "json") {
113722
+ ctx.output.data(
113723
+ JSON.stringify(
113724
+ hateoasWrap2({
113725
+ type: "assign-result",
113726
+ command: `skill linear issue ${issue.identifier} --json`,
113727
+ data: resultData,
113728
+ links: issueLinks(issue.identifier, team?.key),
113729
+ actions: issueActions(issue.identifier)
113730
+ }),
113731
+ null,
113732
+ 2
113733
+ )
113734
+ );
113735
+ return;
113736
+ }
113737
+ ctx.output.data("");
113738
+ if (options.unassign) {
113739
+ ctx.output.data(`\u2705 Unassigned ${issue.identifier}`);
113740
+ } else {
113741
+ ctx.output.data(`\u2705 Assigned ${issue.identifier} to ${assigneeName}`);
113742
+ }
113743
+ ctx.output.data("");
113744
+ ctx.output.data(` View: skill linear issue ${issue.identifier}`);
113745
+ ctx.output.data("");
113746
+ } catch (error) {
113747
+ const cliError = error instanceof CLIError ? error : new CLIError({
113748
+ userMessage: "Failed to assign issue.",
113749
+ suggestion: "Verify LINEAR_API_KEY is set correctly.",
113750
+ cause: error
113751
+ });
113752
+ ctx.output.error(formatError(cliError));
113753
+ process.exitCode = cliError.exitCode;
113754
+ }
113755
+ }
113756
+
113757
+ // src/commands/linear/close.ts
113758
+ init_esm_shims();
113759
+ async function closeIssue(ctx, issueId, options = {}) {
113760
+ try {
113761
+ const client = getLinearClient();
113762
+ const issue = await client.issue(issueId);
113763
+ if (!issue) {
113764
+ throw new CLIError({
113765
+ userMessage: `Issue not found: ${issueId}`,
113766
+ suggestion: "Use `skill linear issues --json` to list available issues.",
113767
+ exitCode: 1
113768
+ });
113769
+ }
113770
+ const team = await issue.team;
113771
+ if (!team) {
113772
+ throw new CLIError({
113773
+ userMessage: "Could not determine team for issue.",
113774
+ exitCode: 1
113775
+ });
113776
+ }
113777
+ const states = await team.states();
113778
+ const targetType = options.canceled ? "canceled" : "completed";
113779
+ const closeState = states.nodes.find((s) => s.type === targetType);
113780
+ if (!closeState) {
113781
+ throw new CLIError({
113782
+ userMessage: `No ${targetType} state found for team ${team.key}.`,
113783
+ suggestion: `Use \`skill linear states ${team.key}\` to see available states.`,
113784
+ exitCode: 1
113785
+ });
113786
+ }
113787
+ const currentState = await issue.state;
113788
+ await client.updateIssue(issue.id, {
113789
+ stateId: closeState.id
113790
+ });
113791
+ const resultData = {
113792
+ issueId: issue.id,
113793
+ issueIdentifier: issue.identifier,
113794
+ previousState: currentState?.name || null,
113795
+ newState: closeState.name,
113796
+ closeType: targetType,
113797
+ success: true
113798
+ };
113799
+ if (ctx.format === "json") {
113800
+ ctx.output.data(
113801
+ JSON.stringify(
113802
+ hateoasWrap2({
113803
+ type: "close-result",
113804
+ command: `skill linear issue ${issue.identifier} --json`,
113805
+ data: resultData,
113806
+ links: [
113807
+ ...issueLinks(issue.identifier, team.key),
113808
+ ...teamLinks(team.key)
113809
+ ]
113810
+ }),
113811
+ null,
113812
+ 2
113813
+ )
113814
+ );
113815
+ return;
113816
+ }
113817
+ const emoji = options.canceled ? "\u274C" : "\u2705";
113818
+ const verb = options.canceled ? "Canceled" : "Closed";
113819
+ ctx.output.data("");
113820
+ ctx.output.data(`${emoji} ${verb} ${issue.identifier}`);
113821
+ ctx.output.data("\u2500".repeat(50));
113822
+ ctx.output.data(` Title: ${issue.title}`);
113823
+ ctx.output.data(` From: ${currentState?.name || "Unknown"}`);
113824
+ ctx.output.data(` To: ${closeState.name}`);
113825
+ ctx.output.data("");
113826
+ } catch (error) {
113827
+ const cliError = error instanceof CLIError ? error : new CLIError({
113828
+ userMessage: "Failed to close issue.",
113829
+ suggestion: "Verify LINEAR_API_KEY is set correctly.",
113830
+ cause: error
113831
+ });
113832
+ ctx.output.error(formatError(cliError));
113833
+ process.exitCode = cliError.exitCode;
113834
+ }
113835
+ }
113836
+
113837
+ // src/commands/linear/comment.ts
113838
+ init_esm_shims();
113839
+ async function addComment(ctx, issueId, options) {
113840
+ if (!options.body || options.body.trim().length === 0) {
113841
+ throw new CLIError({
113842
+ userMessage: "Comment body is required.",
113843
+ suggestion: 'Usage: skill linear comment ENG-123 --body "Your comment"',
113844
+ exitCode: 1
113845
+ });
113846
+ }
113847
+ try {
113848
+ const client = getLinearClient();
113849
+ const issue = await client.issue(issueId);
113850
+ if (!issue) {
113851
+ throw new CLIError({
113852
+ userMessage: `Issue not found: ${issueId}`,
113853
+ suggestion: "Use `skill linear issues --json` to list available issues.",
113854
+ exitCode: 1
113855
+ });
113856
+ }
113857
+ const payload = await client.createComment({
113858
+ issueId: issue.id,
113859
+ body: options.body.trim()
113860
+ });
113861
+ const comment = await payload.comment;
113862
+ if (!comment) {
113863
+ throw new CLIError({
113864
+ userMessage: "Failed to create comment - no comment returned.",
113865
+ exitCode: 1
113866
+ });
113867
+ }
113868
+ const user = await comment.user;
113869
+ const commentData = {
113870
+ id: comment.id,
113871
+ body: comment.body,
113872
+ issueId: issue.id,
113873
+ issueIdentifier: issue.identifier,
113874
+ author: user ? { id: user.id, name: user.name, email: user.email } : null,
113875
+ createdAt: comment.createdAt,
113876
+ url: comment.url
113877
+ };
113878
+ if (ctx.format === "json") {
113879
+ ctx.output.data(
113880
+ JSON.stringify(
113881
+ hateoasWrap2({
113882
+ type: "comment-created",
113883
+ command: `skill linear comments ${issue.identifier} --json`,
113884
+ data: commentData,
113885
+ links: [
113886
+ ...commentLinks(comment.id, issue.identifier),
113887
+ ...issueLinks(issue.identifier)
113888
+ ],
113889
+ actions: issueActions(issue.identifier)
113890
+ }),
113891
+ null,
113892
+ 2
113893
+ )
113894
+ );
113895
+ return;
113896
+ }
113897
+ ctx.output.data("");
113898
+ ctx.output.data(`\u2705 Comment added to ${issue.identifier}`);
113899
+ ctx.output.data("\u2500".repeat(50));
113900
+ ctx.output.data(` Author: ${user?.name || "Unknown"}`);
113901
+ ctx.output.data(` URL: ${comment.url}`);
113902
+ ctx.output.data("");
113903
+ ctx.output.data(" Comment:");
113904
+ ctx.output.data(" " + "-".repeat(40));
113905
+ const bodyLines = options.body.split("\n");
113906
+ for (const line of bodyLines.slice(0, 10)) {
113907
+ ctx.output.data(` ${line}`);
113908
+ }
113909
+ if (bodyLines.length > 10) {
113910
+ ctx.output.data(` ... (${bodyLines.length - 10} more lines)`);
113911
+ }
113912
+ ctx.output.data("");
113913
+ } catch (error) {
113914
+ const cliError = error instanceof CLIError ? error : new CLIError({
113915
+ userMessage: "Failed to add comment.",
113916
+ suggestion: "Verify LINEAR_API_KEY is set correctly.",
113917
+ cause: error
113918
+ });
113919
+ ctx.output.error(formatError(cliError));
113920
+ process.exitCode = cliError.exitCode;
113921
+ }
113922
+ }
113923
+
113924
+ // src/commands/linear/comments.ts
113925
+ init_esm_shims();
113926
+ async function listComments(ctx, issueId, options = {}) {
113927
+ const limit2 = options.limit || 50;
113928
+ try {
113929
+ const client = getLinearClient();
113930
+ const issue = await client.issue(issueId);
113931
+ if (!issue) {
113932
+ throw new CLIError({
113933
+ userMessage: `Issue not found: ${issueId}`,
113934
+ suggestion: "Use `skill linear issues --json` to list available issues.",
113935
+ exitCode: 1
113936
+ });
113937
+ }
113938
+ const commentsConnection = await issue.comments({ first: limit2 });
113939
+ const comments = commentsConnection.nodes || [];
113940
+ const commentsWithUsers = await Promise.all(
113941
+ comments.map(async (comment) => ({
113942
+ comment,
113943
+ user: await comment.user
113944
+ }))
113945
+ );
113946
+ if (ctx.format === "json") {
113947
+ const commentData = commentsWithUsers.map(({ comment, user }) => ({
113948
+ id: comment.id,
113949
+ body: comment.body,
113950
+ author: user ? { id: user.id, name: user.name, email: user.email } : null,
113951
+ createdAt: comment.createdAt,
113952
+ updatedAt: comment.updatedAt,
113953
+ url: comment.url
113954
+ }));
113955
+ ctx.output.data(
113956
+ JSON.stringify(
113957
+ hateoasWrap2({
113958
+ type: "comment-list",
113959
+ command: `skill linear comments ${issue.identifier} --json`,
113960
+ data: {
113961
+ issueId: issue.id,
113962
+ issueIdentifier: issue.identifier,
113963
+ issueTitle: issue.title,
113964
+ count: comments.length,
113965
+ comments: commentData
113966
+ },
113967
+ links: issueLinks(issue.identifier),
113968
+ actions: [
113969
+ {
113970
+ action: "add-comment",
113971
+ command: `skill linear comment ${issue.identifier} --body "<text>"`,
113972
+ description: "Add a new comment"
113973
+ },
113974
+ ...issueActions(issue.identifier)
113975
+ ]
113976
+ }),
113977
+ null,
113978
+ 2
113979
+ )
113980
+ );
113981
+ return;
113982
+ }
113983
+ ctx.output.data("");
113984
+ ctx.output.data(`\u{1F4AC} Comments on ${issue.identifier}: ${issue.title}`);
113985
+ ctx.output.data("\u2500".repeat(80));
113986
+ if (comments.length === 0) {
113987
+ ctx.output.data("");
113988
+ ctx.output.data(" No comments yet.");
113989
+ ctx.output.data("");
113990
+ ctx.output.data(
113991
+ ` Add one: skill linear comment ${issue.identifier} --body "Your comment"`
113992
+ );
113993
+ ctx.output.data("");
113994
+ return;
113995
+ }
113996
+ for (const { comment, user } of commentsWithUsers) {
113997
+ const date = new Date(comment.createdAt).toLocaleDateString();
113998
+ const authorName = user?.name || "Unknown";
113999
+ ctx.output.data("");
114000
+ ctx.output.data(` \u{1F4DD} ${authorName} \u2022 ${date}`);
114001
+ ctx.output.data(" " + "-".repeat(40));
114002
+ const bodyLines = comment.body.split("\n");
114003
+ for (const line of bodyLines.slice(0, 5)) {
114004
+ ctx.output.data(` ${line}`);
114005
+ }
114006
+ if (bodyLines.length > 5) {
114007
+ ctx.output.data(` ... (${bodyLines.length - 5} more lines)`);
114008
+ }
114009
+ }
114010
+ ctx.output.data("");
114011
+ ctx.output.data(
114012
+ ` Add comment: skill linear comment ${issue.identifier} --body "text"`
114013
+ );
114014
+ ctx.output.data("");
114015
+ } catch (error) {
114016
+ const cliError = error instanceof CLIError ? error : new CLIError({
114017
+ userMessage: "Failed to list comments.",
114018
+ suggestion: "Verify LINEAR_API_KEY is set correctly.",
114019
+ cause: error
114020
+ });
114021
+ ctx.output.error(formatError(cliError));
114022
+ process.exitCode = cliError.exitCode;
114023
+ }
114024
+ }
114025
+
114026
+ // src/commands/linear/create.ts
114027
+ init_esm_shims();
114028
+ async function createIssue(ctx, title, options = {}) {
114029
+ if (!title || title.trim().length === 0) {
114030
+ throw new CLIError({
114031
+ userMessage: "Issue title is required.",
114032
+ suggestion: 'Usage: skill linear create "Issue title"',
114033
+ exitCode: 1
114034
+ });
114035
+ }
114036
+ try {
114037
+ const client = getLinearClient();
114038
+ let teamId;
114039
+ let teamKey;
114040
+ const teams = await client.teams();
114041
+ if (options.team) {
114042
+ const team = teams.nodes.find(
114043
+ (t2) => t2.key.toLowerCase() === options.team.toLowerCase() || t2.name.toLowerCase() === options.team.toLowerCase()
114044
+ );
114045
+ if (!team) {
114046
+ throw new CLIError({
114047
+ userMessage: `Team not found: ${options.team}`,
114048
+ suggestion: "Use `skill linear teams --json` to list available teams."
114049
+ });
114050
+ }
114051
+ teamId = team.id;
114052
+ teamKey = team.key;
114053
+ } else {
114054
+ const team = teams.nodes[0];
114055
+ if (!team) {
114056
+ throw new CLIError({
114057
+ userMessage: "No teams found in Linear workspace.",
114058
+ exitCode: 1
114059
+ });
114060
+ }
114061
+ teamId = team.id;
114062
+ teamKey = team.key;
114063
+ }
114064
+ let assigneeId;
114065
+ if (options.assignee) {
114066
+ if (options.assignee.toLowerCase() === "me") {
114067
+ const viewer = await client.viewer;
114068
+ assigneeId = viewer.id;
114069
+ } else {
114070
+ const users = await client.users();
114071
+ const user = users.nodes.find(
114072
+ (u) => u.email?.toLowerCase() === options.assignee.toLowerCase() || u.name?.toLowerCase() === options.assignee.toLowerCase()
114073
+ );
114074
+ if (!user) {
114075
+ throw new CLIError({
114076
+ userMessage: `User not found: ${options.assignee}`,
114077
+ suggestion: "Use `skill linear users --json` to list available users."
114078
+ });
114079
+ }
114080
+ assigneeId = user.id;
114081
+ }
114082
+ }
114083
+ let labelIds;
114084
+ if (options.label && options.label.length > 0) {
114085
+ const teamLabels = await client.issueLabels({
114086
+ filter: { team: { id: { eq: teamId } } }
114087
+ });
114088
+ const workspaceLabels = await client.issueLabels({
114089
+ filter: { team: { null: true } }
114090
+ });
114091
+ const allLabels = [...teamLabels.nodes, ...workspaceLabels.nodes];
114092
+ labelIds = [];
114093
+ for (const labelName of options.label) {
114094
+ const label = allLabels.find(
114095
+ (l) => l.name.toLowerCase() === labelName.toLowerCase()
114096
+ );
114097
+ if (!label) {
114098
+ throw new CLIError({
114099
+ userMessage: `Label not found: ${labelName}`,
114100
+ suggestion: `Use \`skill linear labels ${teamKey} --json\` to list available labels.`
114101
+ });
114102
+ }
114103
+ labelIds.push(label.id);
114104
+ }
114105
+ }
114106
+ let projectId;
114107
+ if (options.project) {
114108
+ const projects = await client.projects();
114109
+ const project = projects.nodes.find(
114110
+ (p) => p.id === options.project || p.name.toLowerCase() === options.project.toLowerCase()
114111
+ );
114112
+ if (!project) {
114113
+ throw new CLIError({
114114
+ userMessage: `Project not found: ${options.project}`,
114115
+ suggestion: "Use `skill linear projects --json` to list available projects."
114116
+ });
114117
+ }
114118
+ projectId = project.id;
114119
+ }
114120
+ const payload = await client.createIssue({
114121
+ teamId,
114122
+ title: title.trim(),
114123
+ description: options.description,
114124
+ priority: options.priority ?? 2,
114125
+ assigneeId,
114126
+ labelIds,
114127
+ projectId,
114128
+ estimate: options.estimate,
114129
+ dueDate: options.dueDate
114130
+ });
114131
+ const issue = await payload.issue;
114132
+ if (!issue) {
114133
+ throw new CLIError({
114134
+ userMessage: "Failed to create issue - no issue returned.",
114135
+ exitCode: 1
114136
+ });
114137
+ }
114138
+ const state = await issue.state;
114139
+ const assignee = await issue.assignee;
114140
+ const issueData = {
114141
+ id: issue.id,
114142
+ identifier: issue.identifier,
114143
+ title: issue.title,
114144
+ description: issue.description || null,
114145
+ state: state?.name || null,
114146
+ priority: issue.priority,
114147
+ assignee: assignee ? { id: assignee.id, name: assignee.name, email: assignee.email } : null,
114148
+ team: { key: teamKey },
114149
+ url: issue.url,
114150
+ createdAt: issue.createdAt
114151
+ };
114152
+ if (ctx.format === "json") {
114153
+ ctx.output.data(
114154
+ JSON.stringify(
114155
+ hateoasWrap2({
114156
+ type: "issue-created",
114157
+ command: `skill linear issue ${issue.identifier} --json`,
114158
+ data: issueData,
114159
+ links: issueLinks(issue.identifier, teamKey),
114160
+ actions: issueActions(issue.identifier)
114161
+ }),
114162
+ null,
114163
+ 2
114164
+ )
114165
+ );
114166
+ return;
114167
+ }
114168
+ ctx.output.data("");
114169
+ ctx.output.data(`\u2705 Issue created: ${issue.identifier}`);
114170
+ ctx.output.data("\u2500".repeat(50));
114171
+ ctx.output.data(` Title: ${issue.title}`);
114172
+ ctx.output.data(` Team: ${teamKey}`);
114173
+ ctx.output.data(` State: ${state?.name || "Backlog"}`);
114174
+ if (assignee) {
114175
+ ctx.output.data(` Assignee: ${assignee.name}`);
114176
+ }
114177
+ ctx.output.data(` URL: ${issue.url}`);
114178
+ ctx.output.data("");
114179
+ ctx.output.data(" Next steps:");
114180
+ ctx.output.data(` \u2022 View: skill linear issue ${issue.identifier}`);
114181
+ ctx.output.data(
114182
+ ` \u2022 Assign: skill linear assign ${issue.identifier} --to <email>`
114183
+ );
114184
+ ctx.output.data(
114185
+ ` \u2022 Comment: skill linear comment ${issue.identifier} --body "text"`
114186
+ );
114187
+ ctx.output.data("");
114188
+ } catch (error) {
114189
+ const cliError = error instanceof CLIError ? error : new CLIError({
114190
+ userMessage: "Failed to create Linear issue.",
114191
+ suggestion: "Verify LINEAR_API_KEY is set correctly.",
114192
+ cause: error
114193
+ });
114194
+ ctx.output.error(formatError(cliError));
114195
+ process.exitCode = cliError.exitCode;
114196
+ }
114197
+ }
114198
+
114199
+ // src/commands/linear/get.ts
114200
+ init_esm_shims();
114201
+ var PRIORITY_LABELS = {
114202
+ 0: "Urgent",
114203
+ 1: "High",
114204
+ 2: "Medium",
114205
+ 3: "Low",
114206
+ 4: "None"
114207
+ };
114208
+ var PRIORITY_EMOJI = {
114209
+ 0: "\u{1F534}",
114210
+ 1: "\u{1F7E0}",
114211
+ 2: "\u{1F7E1}",
114212
+ 3: "\u{1F7E2}",
114213
+ 4: "\u26AA"
114214
+ };
114215
+ async function getIssue(ctx, id) {
114216
+ try {
114217
+ const client = getLinearClient();
114218
+ const issue = await client.issue(id);
114219
+ if (!issue) {
114220
+ throw new CLIError({
114221
+ userMessage: `Issue ${id} not found.`,
114222
+ suggestion: "Use `skill linear issues --json` to list available issues.",
114223
+ exitCode: 1
114224
+ });
114225
+ }
114226
+ const [state, assignee, team, labels, project, parent2, cycle] = await Promise.all([
114227
+ issue.state,
114228
+ issue.assignee,
114229
+ issue.team,
114230
+ issue.labels(),
114231
+ issue.project,
114232
+ issue.parent,
114233
+ issue.cycle
114234
+ ]);
114235
+ const teamKey = team?.key;
114236
+ const issueData = {
114237
+ id: issue.id,
114238
+ identifier: issue.identifier,
114239
+ title: issue.title,
114240
+ description: issue.description || null,
114241
+ state: state?.name || null,
114242
+ stateType: state?.type || null,
114243
+ priority: issue.priority,
114244
+ priorityLabel: PRIORITY_LABELS[issue.priority] || "Unknown",
114245
+ estimate: issue.estimate || null,
114246
+ assignee: assignee ? { id: assignee.id, name: assignee.name, email: assignee.email } : null,
114247
+ team: team ? { id: team.id, key: team.key, name: team.name } : null,
114248
+ project: project ? { id: project.id, name: project.name, url: project.url } : null,
114249
+ parent: parent2 ? { id: parent2.id, identifier: parent2.identifier, title: parent2.title } : null,
114250
+ cycle: cycle ? { id: cycle.id, name: cycle.name, number: cycle.number } : null,
114251
+ labels: labels.nodes.map((l) => ({ id: l.id, name: l.name })),
114252
+ url: issue.url,
114253
+ createdAt: issue.createdAt,
114254
+ updatedAt: issue.updatedAt,
114255
+ completedAt: issue.completedAt || null,
114256
+ dueDate: issue.dueDate || null
114257
+ };
114258
+ if (ctx.format === "json") {
114259
+ ctx.output.data(
114260
+ JSON.stringify(
114261
+ hateoasWrap2({
114262
+ type: "issue",
114263
+ command: `skill linear issue ${issue.identifier} --json`,
114264
+ data: issueData,
114265
+ links: issueLinks(issue.identifier, teamKey),
114266
+ actions: issueActions(issue.identifier)
114267
+ }),
114268
+ null,
114269
+ 2
114270
+ )
114271
+ );
114272
+ return;
114273
+ }
114274
+ ctx.output.data("");
114275
+ ctx.output.data(
114276
+ `${PRIORITY_EMOJI[issue.priority] || "\u26AA"} [${issue.identifier}] ${issue.title}`
114277
+ );
114278
+ ctx.output.data("\u2500".repeat(80));
114279
+ ctx.output.data("");
114280
+ ctx.output.data(` Status: ${state?.name || "Unknown"}`);
114281
+ ctx.output.data(
114282
+ ` Priority: ${PRIORITY_LABELS[issue.priority] || "Unknown"} (${issue.priority})`
114283
+ );
114284
+ if (assignee) {
114285
+ ctx.output.data(` Assignee: ${assignee.name} <${assignee.email}>`);
114286
+ } else {
114287
+ ctx.output.data(` Assignee: Unassigned`);
114288
+ }
114289
+ if (team) {
114290
+ ctx.output.data(` Team: ${team.name} (${team.key})`);
114291
+ }
114292
+ if (project) {
114293
+ ctx.output.data(` Project: ${project.name}`);
114294
+ }
114295
+ if (cycle) {
114296
+ ctx.output.data(` Cycle: ${cycle.name}`);
114297
+ }
114298
+ if (parent2) {
114299
+ ctx.output.data(` Parent: ${parent2.identifier} - ${parent2.title}`);
114300
+ }
114301
+ if (labels.nodes.length > 0) {
114302
+ ctx.output.data(
114303
+ ` Labels: ${labels.nodes.map((l) => l.name).join(", ")}`
114304
+ );
114305
+ }
114306
+ if (issue.estimate) {
114307
+ ctx.output.data(` Estimate: ${issue.estimate} points`);
114308
+ }
114309
+ if (issue.dueDate) {
114310
+ ctx.output.data(` Due: ${issue.dueDate}`);
114311
+ }
114312
+ ctx.output.data("");
114313
+ ctx.output.data(` URL: ${issue.url}`);
114314
+ ctx.output.data(
114315
+ ` Created: ${new Date(issue.createdAt).toLocaleDateString()}`
114316
+ );
114317
+ ctx.output.data(
114318
+ ` Updated: ${new Date(issue.updatedAt).toLocaleDateString()}`
114319
+ );
114320
+ if (issue.description) {
114321
+ ctx.output.data("");
114322
+ ctx.output.data(" Description:");
114323
+ ctx.output.data(" " + "-".repeat(40));
114324
+ const descLines = issue.description.split("\n");
114325
+ for (const line of descLines.slice(0, 20)) {
114326
+ ctx.output.data(` ${line}`);
114327
+ }
114328
+ if (descLines.length > 20) {
114329
+ ctx.output.data(` ... (${descLines.length - 20} more lines)`);
114330
+ }
114331
+ }
114332
+ ctx.output.data("");
114333
+ ctx.output.data(" Actions:");
114334
+ ctx.output.data(
114335
+ ` \u2022 Comment: skill linear comment ${issue.identifier} --body "text"`
114336
+ );
114337
+ ctx.output.data(
114338
+ ` \u2022 Assign: skill linear assign ${issue.identifier} --to <email>`
114339
+ );
114340
+ ctx.output.data(
114341
+ ` \u2022 State: skill linear state ${issue.identifier} --state "Done"`
114342
+ );
114343
+ ctx.output.data(` \u2022 Close: skill linear close ${issue.identifier}`);
114344
+ ctx.output.data("");
114345
+ } catch (error) {
114346
+ const cliError = error instanceof CLIError ? error : new CLIError({
114347
+ userMessage: "Failed to fetch Linear issue.",
114348
+ suggestion: "Verify LINEAR_API_KEY is set correctly.",
114349
+ cause: error
114350
+ });
114351
+ ctx.output.error(formatError(cliError));
114352
+ process.exitCode = cliError.exitCode;
114353
+ }
114354
+ }
114355
+
114356
+ // src/commands/linear/label.ts
114357
+ init_esm_shims();
114358
+ async function modifyLabels(ctx, issueId, options) {
114359
+ const hasAdd = options.add && options.add.length > 0;
114360
+ const hasRemove = options.remove && options.remove.length > 0;
114361
+ if (!hasAdd && !hasRemove) {
114362
+ throw new CLIError({
114363
+ userMessage: "No label changes specified.",
114364
+ suggestion: 'Use --add "Label" or --remove "Label".',
114365
+ exitCode: 1
114366
+ });
114367
+ }
114368
+ try {
114369
+ const client = getLinearClient();
114370
+ const issue = await client.issue(issueId);
114371
+ if (!issue) {
114372
+ throw new CLIError({
114373
+ userMessage: `Issue not found: ${issueId}`,
114374
+ suggestion: "Use `skill linear issues --json` to list available issues.",
114375
+ exitCode: 1
114376
+ });
114377
+ }
114378
+ const team = await issue.team;
114379
+ if (!team) {
114380
+ throw new CLIError({
114381
+ userMessage: "Could not determine team for issue.",
114382
+ exitCode: 1
114383
+ });
114384
+ }
114385
+ const [teamLabels, workspaceLabels] = await Promise.all([
114386
+ client.issueLabels({ filter: { team: { id: { eq: team.id } } } }),
114387
+ client.issueLabels({ filter: { team: { null: true } } })
114388
+ ]);
114389
+ const allLabels = [...teamLabels.nodes, ...workspaceLabels.nodes];
114390
+ const currentLabelsConnection = await issue.labels();
114391
+ const currentLabelIds = new Set(
114392
+ currentLabelsConnection.nodes.map((l) => l.id)
114393
+ );
114394
+ const labelsToAdd = [];
114395
+ if (options.add) {
114396
+ for (const labelName of options.add) {
114397
+ const label = allLabels.find(
114398
+ (l) => l.name.toLowerCase() === labelName.toLowerCase()
114399
+ );
114400
+ if (!label) {
114401
+ throw new CLIError({
114402
+ userMessage: `Label not found: ${labelName}`,
114403
+ suggestion: `Use \`skill linear labels ${team.key} --json\` to list available labels.`,
114404
+ exitCode: 1
114405
+ });
114406
+ }
114407
+ if (!currentLabelIds.has(label.id)) {
114408
+ labelsToAdd.push(label.id);
114409
+ currentLabelIds.add(label.id);
114410
+ }
114411
+ }
114412
+ }
114413
+ const labelsToRemove = [];
114414
+ if (options.remove) {
114415
+ for (const labelName of options.remove) {
114416
+ const label = currentLabelsConnection.nodes.find(
114417
+ (l) => l.name.toLowerCase() === labelName.toLowerCase()
114418
+ );
114419
+ if (!label) {
114420
+ continue;
114421
+ }
114422
+ labelsToRemove.push(label.id);
114423
+ currentLabelIds.delete(label.id);
114424
+ }
114425
+ }
114426
+ if (labelsToAdd.length > 0 || labelsToRemove.length > 0) {
114427
+ await client.updateIssue(issue.id, {
114428
+ labelIds: Array.from(currentLabelIds)
114429
+ });
114430
+ }
114431
+ const updatedIssue = await client.issue(issueId);
114432
+ const updatedLabelsConnection = await updatedIssue?.labels();
114433
+ const updatedLabels = updatedLabelsConnection?.nodes || [];
114434
+ const resultData = {
114435
+ issueId: issue.id,
114436
+ issueIdentifier: issue.identifier,
114437
+ added: options.add || [],
114438
+ removed: options.remove || [],
114439
+ currentLabels: updatedLabels.map((l) => ({ id: l.id, name: l.name })),
114440
+ success: true
114441
+ };
114442
+ if (ctx.format === "json") {
114443
+ ctx.output.data(
114444
+ JSON.stringify(
114445
+ hateoasWrap2({
114446
+ type: "label-result",
114447
+ command: `skill linear issue ${issue.identifier} --json`,
114448
+ data: resultData,
114449
+ links: issueLinks(issue.identifier, team.key),
114450
+ actions: issueActions(issue.identifier)
114451
+ }),
114452
+ null,
114453
+ 2
114454
+ )
114455
+ );
114456
+ return;
114457
+ }
114458
+ ctx.output.data("");
114459
+ ctx.output.data(`\u2705 Labels updated on ${issue.identifier}`);
114460
+ ctx.output.data("\u2500".repeat(50));
114461
+ if (options.add && options.add.length > 0) {
114462
+ ctx.output.data(` Added: ${options.add.join(", ")}`);
114463
+ }
114464
+ if (options.remove && options.remove.length > 0) {
114465
+ ctx.output.data(` Removed: ${options.remove.join(", ")}`);
114466
+ }
114467
+ ctx.output.data("");
114468
+ ctx.output.data(
114469
+ ` Current labels: ${updatedLabels.length > 0 ? updatedLabels.map((l) => l.name).join(", ") : "(none)"}`
114470
+ );
114471
+ ctx.output.data("");
114472
+ } catch (error) {
114473
+ const cliError = error instanceof CLIError ? error : new CLIError({
114474
+ userMessage: "Failed to modify labels.",
114475
+ suggestion: "Verify LINEAR_API_KEY is set correctly.",
114476
+ cause: error
114477
+ });
114478
+ ctx.output.error(formatError(cliError));
114479
+ process.exitCode = cliError.exitCode;
114480
+ }
114481
+ }
114482
+
114483
+ // src/commands/linear/labels.ts
114484
+ init_esm_shims();
114485
+ async function listLabels(ctx, teamKey) {
114486
+ if (!teamKey || teamKey.trim().length === 0) {
114487
+ throw new CLIError({
114488
+ userMessage: "Team key is required.",
114489
+ suggestion: "Usage: skill linear labels ENG\nUse `skill linear teams` to list teams.",
114490
+ exitCode: 1
114491
+ });
114492
+ }
114493
+ try {
114494
+ const client = getLinearClient();
114495
+ const teams = await client.teams();
114496
+ const team = teams.nodes.find(
114497
+ (t2) => t2.key.toLowerCase() === teamKey.toLowerCase() || t2.name.toLowerCase() === teamKey.toLowerCase()
114498
+ );
114499
+ if (!team) {
114500
+ throw new CLIError({
114501
+ userMessage: `Team not found: ${teamKey}`,
114502
+ suggestion: "Use `skill linear teams --json` to list available teams.",
114503
+ exitCode: 1
114504
+ });
114505
+ }
114506
+ const [teamLabels, workspaceLabels] = await Promise.all([
114507
+ client.issueLabels({ filter: { team: { id: { eq: team.id } } } }),
114508
+ client.issueLabels({ filter: { team: { null: true } } })
114509
+ ]);
114510
+ const teamLabelNodes = teamLabels.nodes || [];
114511
+ const workspaceLabelNodes = workspaceLabels.nodes || [];
114512
+ if (ctx.format === "json") {
114513
+ const labelData = {
114514
+ team: { id: team.id, key: team.key, name: team.name },
114515
+ teamLabels: teamLabelNodes.map((l) => ({
114516
+ id: l.id,
114517
+ name: l.name,
114518
+ color: l.color,
114519
+ description: l.description || null
114520
+ })),
114521
+ workspaceLabels: workspaceLabelNodes.map((l) => ({
114522
+ id: l.id,
114523
+ name: l.name,
114524
+ color: l.color,
114525
+ description: l.description || null
114526
+ })),
114527
+ totalCount: teamLabelNodes.length + workspaceLabelNodes.length
114528
+ };
114529
+ ctx.output.data(
114530
+ JSON.stringify(
114531
+ hateoasWrap2({
114532
+ type: "label-list",
114533
+ command: `skill linear labels ${team.key} --json`,
114534
+ data: labelData,
114535
+ links: teamLinks(team.key),
114536
+ actions: [
114537
+ {
114538
+ action: "add-label",
114539
+ command: `skill linear label <issue-id> --add "<label-name>"`,
114540
+ description: "Add a label to an issue"
114541
+ },
114542
+ {
114543
+ action: "remove-label",
114544
+ command: `skill linear label <issue-id> --remove "<label-name>"`,
114545
+ description: "Remove a label from an issue"
114546
+ }
114547
+ ]
114548
+ }),
114549
+ null,
114550
+ 2
114551
+ )
114552
+ );
114553
+ return;
114554
+ }
114555
+ ctx.output.data("");
114556
+ ctx.output.data(`\u{1F3F7}\uFE0F Labels for ${team.name} (${team.key})`);
114557
+ ctx.output.data("\u2500".repeat(60));
114558
+ if (teamLabelNodes.length > 0) {
114559
+ ctx.output.data("");
114560
+ ctx.output.data(" Team Labels:");
114561
+ for (const label of teamLabelNodes) {
114562
+ ctx.output.data(` \u2022 ${label.name}`);
114563
+ }
114564
+ }
114565
+ if (workspaceLabelNodes.length > 0) {
114566
+ ctx.output.data("");
114567
+ ctx.output.data(" Workspace Labels:");
114568
+ for (const label of workspaceLabelNodes) {
114569
+ ctx.output.data(` \u2022 ${label.name}`);
114570
+ }
114571
+ }
114572
+ if (teamLabelNodes.length === 0 && workspaceLabelNodes.length === 0) {
114573
+ ctx.output.data("");
114574
+ ctx.output.data(" No labels defined.");
114575
+ }
114576
+ ctx.output.data("");
114577
+ ctx.output.data(' Usage: skill linear label ENG-123 --add "Bug"');
114578
+ ctx.output.data("");
114579
+ } catch (error) {
114580
+ const cliError = error instanceof CLIError ? error : new CLIError({
114581
+ userMessage: "Failed to list labels.",
114582
+ suggestion: "Verify LINEAR_API_KEY is set correctly.",
114583
+ cause: error
114584
+ });
114585
+ ctx.output.error(formatError(cliError));
114586
+ process.exitCode = cliError.exitCode;
114587
+ }
114588
+ }
114589
+
114590
+ // src/commands/linear/link.ts
114591
+ init_esm_shims();
114592
+ async function linkIssues(ctx, issueId, options) {
114593
+ const relationships = [
114594
+ { type: "blocks", value: options.blocks },
114595
+ { type: "blocked_by", value: options.blockedBy },
114596
+ { type: "related", value: options.related },
114597
+ { type: "duplicate", value: options.duplicate }
114598
+ ].filter((r) => r.value);
114599
+ if (relationships.length === 0) {
114600
+ throw new CLIError({
114601
+ userMessage: "No relationship specified.",
114602
+ suggestion: "Use --blocks, --blocked-by, --related, or --duplicate with a target issue ID.",
114603
+ exitCode: 1
114604
+ });
114605
+ }
114606
+ if (relationships.length > 1) {
114607
+ throw new CLIError({
114608
+ userMessage: "Only one relationship type can be specified at a time.",
114609
+ suggestion: "Choose one: --blocks, --blocked-by, --related, or --duplicate.",
114610
+ exitCode: 1
114611
+ });
114612
+ }
114613
+ const relationship = relationships[0];
114614
+ const targetValue = relationship.value;
114615
+ try {
114616
+ const client = getLinearClient();
114617
+ const [issue, targetIssue] = await Promise.all([
114618
+ client.issue(issueId),
114619
+ client.issue(targetValue)
114620
+ ]);
114621
+ if (!issue) {
114622
+ throw new CLIError({
114623
+ userMessage: `Issue not found: ${issueId}`,
114624
+ suggestion: "Use `skill linear issues --json` to list available issues.",
114625
+ exitCode: 1
114626
+ });
114627
+ }
114628
+ if (!targetIssue) {
114629
+ throw new CLIError({
114630
+ userMessage: `Target issue not found: ${targetValue}`,
114631
+ suggestion: "Use `skill linear issues --json` to list available issues.",
114632
+ exitCode: 1
114633
+ });
114634
+ }
114635
+ await client.createIssueRelation({
114636
+ issueId: issue.id,
114637
+ relatedIssueId: targetIssue.id,
114638
+ type: relationship.type
114639
+ });
114640
+ const team = await issue.team;
114641
+ const resultData = {
114642
+ issueId: issue.id,
114643
+ issueIdentifier: issue.identifier,
114644
+ targetIssueId: targetIssue.id,
114645
+ targetIdentifier: targetIssue.identifier,
114646
+ relationshipType: relationship.type,
114647
+ success: true
114648
+ };
114649
+ if (ctx.format === "json") {
114650
+ ctx.output.data(
114651
+ JSON.stringify(
114652
+ hateoasWrap2({
114653
+ type: "link-result",
114654
+ command: `skill linear issue ${issue.identifier} --json`,
114655
+ data: resultData,
114656
+ links: [
114657
+ ...issueLinks(issue.identifier, team?.key),
114658
+ {
114659
+ rel: "linked-issue",
114660
+ command: `skill linear issue ${targetIssue.identifier} --json`,
114661
+ description: targetIssue.title
114662
+ }
114663
+ ],
114664
+ actions: issueActions(issue.identifier)
114665
+ }),
114666
+ null,
114667
+ 2
114668
+ )
114669
+ );
114670
+ return;
114671
+ }
114672
+ const relationDesc = {
114673
+ blocks: "blocks",
114674
+ blocked_by: "is blocked by",
114675
+ related: "is related to",
114676
+ duplicate: "is a duplicate of"
114677
+ }[relationship.type];
114678
+ ctx.output.data("");
114679
+ ctx.output.data(`\u{1F517} Link created`);
114680
+ ctx.output.data("\u2500".repeat(50));
114681
+ ctx.output.data(
114682
+ ` ${issue.identifier} ${relationDesc} ${targetIssue.identifier}`
114683
+ );
114684
+ ctx.output.data("");
114685
+ ctx.output.data(
114686
+ ` View ${issue.identifier}: skill linear issue ${issue.identifier}`
114687
+ );
114688
+ ctx.output.data(
114689
+ ` View ${targetIssue.identifier}: skill linear issue ${targetIssue.identifier}`
114690
+ );
114691
+ ctx.output.data("");
114692
+ } catch (error) {
114693
+ const cliError = error instanceof CLIError ? error : new CLIError({
114694
+ userMessage: "Failed to link issues.",
114695
+ suggestion: "Verify LINEAR_API_KEY is set correctly.",
114696
+ cause: error
114697
+ });
114698
+ ctx.output.error(formatError(cliError));
114699
+ process.exitCode = cliError.exitCode;
114700
+ }
114701
+ }
114702
+
114703
+ // src/commands/linear/list.ts
114704
+ init_esm_shims();
114705
+ var PRIORITY_EMOJI2 = {
114706
+ 0: "\u{1F534}",
114707
+ 1: "\u{1F7E0}",
114708
+ 2: "\u{1F7E1}",
114709
+ 3: "\u{1F7E2}",
114710
+ 4: "\u26AA"
114711
+ };
114712
+ async function listIssues(ctx, options = {}) {
114713
+ const limit2 = options.limit || 20;
114714
+ try {
114715
+ const client = getLinearClient();
114716
+ const filter4 = {
114717
+ state: {
114718
+ type: {
114719
+ neq: "canceled"
114720
+ }
114721
+ }
114722
+ };
114723
+ let teamKey;
114724
+ if (options.team) {
114725
+ const teams = await client.teams();
114726
+ const team = teams.nodes.find(
114727
+ (t2) => t2.key.toLowerCase() === options.team.toLowerCase() || t2.name.toLowerCase() === options.team.toLowerCase()
114728
+ );
114729
+ if (!team) {
114730
+ throw new CLIError({
114731
+ userMessage: `Team not found: ${options.team}`,
114732
+ suggestion: "Use `skill linear teams --json` to list available teams."
114733
+ });
114734
+ }
114735
+ filter4.team = { id: { eq: team.id } };
114736
+ teamKey = team.key;
114737
+ }
114738
+ if (options.state) {
114739
+ filter4.state = {
114740
+ ...filter4.state || {},
114741
+ name: { eqIgnoreCase: options.state }
114742
+ };
114743
+ }
114744
+ if (options.assignee) {
114745
+ if (options.assignee.toLowerCase() === "me") {
114746
+ const viewer = await client.viewer;
114747
+ filter4.assignee = { id: { eq: viewer.id } };
114748
+ } else {
114749
+ const users = await client.users();
114750
+ const user = users.nodes.find(
114751
+ (u) => u.email?.toLowerCase() === options.assignee.toLowerCase() || u.name?.toLowerCase() === options.assignee.toLowerCase()
114752
+ );
114753
+ if (!user) {
114754
+ throw new CLIError({
114755
+ userMessage: `User not found: ${options.assignee}`,
114756
+ suggestion: "Use `skill linear users --json` to list available users."
114757
+ });
114758
+ }
114759
+ filter4.assignee = { id: { eq: user.id } };
114760
+ }
114761
+ }
114762
+ if (options.project) {
114763
+ const projects = await client.projects();
114764
+ const project = projects.nodes.find(
114765
+ (p) => p.id === options.project || p.name.toLowerCase() === options.project.toLowerCase()
114766
+ );
114767
+ if (!project) {
114768
+ throw new CLIError({
114769
+ userMessage: `Project not found: ${options.project}`,
114770
+ suggestion: "Use `skill linear projects --json` to list available projects."
114771
+ });
114772
+ }
114773
+ filter4.project = { id: { eq: project.id } };
114774
+ }
114775
+ if (options.priority !== void 0) {
114776
+ filter4.priority = { eq: options.priority };
114777
+ }
114778
+ const response = await client.issues({
114779
+ first: limit2,
114780
+ filter: filter4
114781
+ });
114782
+ const issues = response.nodes || [];
114783
+ const issuesWithDetails = await Promise.all(
114784
+ issues.map(async (issue) => ({
114785
+ issue,
114786
+ state: await issue.state,
114787
+ assignee: await issue.assignee,
114788
+ team: await issue.team
114789
+ }))
114790
+ );
114791
+ if (ctx.format === "json") {
114792
+ const issueData = issuesWithDetails.map(
114793
+ ({ issue, state, assignee, team }) => ({
114794
+ id: issue.id,
114795
+ identifier: issue.identifier,
114796
+ title: issue.title,
114797
+ state: state?.name || null,
114798
+ stateType: state?.type || null,
114799
+ priority: issue.priority,
114800
+ assignee: assignee ? { id: assignee.id, name: assignee.name, email: assignee.email } : null,
114801
+ team: team ? { key: team.key, name: team.name } : null,
114802
+ url: issue.url,
114803
+ createdAt: issue.createdAt,
114804
+ updatedAt: issue.updatedAt
114805
+ })
114806
+ );
114807
+ ctx.output.data(
114808
+ JSON.stringify(
114809
+ hateoasWrap2({
114810
+ type: "issue-list",
114811
+ command: `skill linear issues${teamKey ? ` --team ${teamKey}` : ""} --json`,
114812
+ data: {
114813
+ count: issues.length,
114814
+ issues: issueData
114815
+ },
114816
+ links: issueListLinks(
114817
+ issuesWithDetails.map(({ issue }) => ({
114818
+ identifier: issue.identifier,
114819
+ title: issue.title
114820
+ })),
114821
+ teamKey
114822
+ ),
114823
+ actions: issueListActions(teamKey)
114824
+ }),
114825
+ null,
114826
+ 2
114827
+ )
114828
+ );
114829
+ return;
114830
+ }
114831
+ const filterDesc = [];
114832
+ if (options.team) filterDesc.push(`team:${options.team}`);
114833
+ if (options.state) filterDesc.push(`state:"${options.state}"`);
114834
+ if (options.assignee) filterDesc.push(`assignee:${options.assignee}`);
114835
+ if (options.project) filterDesc.push(`project:"${options.project}"`);
114836
+ if (options.priority !== void 0)
114837
+ filterDesc.push(`priority:${options.priority}`);
114838
+ ctx.output.data("");
114839
+ ctx.output.data(
114840
+ `\u{1F4CB} Linear Issues (${issues.length})${filterDesc.length > 0 ? ` [${filterDesc.join(", ")}]` : ""}`
114841
+ );
114842
+ ctx.output.data("\u2500".repeat(80));
114843
+ if (issues.length === 0) {
114844
+ ctx.output.data(" No issues found matching filters.");
114845
+ ctx.output.data("");
114846
+ ctx.output.data(" Suggestions:");
114847
+ ctx.output.data(" \u2022 List all issues: skill linear issues");
114848
+ ctx.output.data(' \u2022 Create an issue: skill linear create "Title"');
114849
+ ctx.output.data("");
114850
+ return;
114851
+ }
114852
+ for (const { issue, state, assignee, team } of issuesWithDetails) {
114853
+ const emoji = PRIORITY_EMOJI2[issue.priority] || "\u26AA";
114854
+ const assigneeName = assignee ? `@${assignee.name}` : "";
114855
+ const teamBadge = team ? `[${team.key}]` : "";
114856
+ ctx.output.data("");
114857
+ ctx.output.data(
114858
+ ` ${emoji} ${teamBadge} ${issue.identifier}: ${issue.title}`
114859
+ );
114860
+ ctx.output.data(
114861
+ ` Status: ${state?.name || "unknown"}${assigneeName ? ` | Assignee: ${assigneeName}` : ""}`
114862
+ );
114863
+ }
114864
+ ctx.output.data("");
114865
+ ctx.output.data(" Use `skill linear issue <ID> --json` for full details.");
114866
+ ctx.output.data("");
114867
+ } catch (error) {
114868
+ const cliError = error instanceof CLIError ? error : new CLIError({
114869
+ userMessage: "Failed to list Linear issues.",
114870
+ suggestion: "Verify LINEAR_API_KEY is set correctly.",
114871
+ cause: error
114872
+ });
114873
+ ctx.output.error(formatError(cliError));
114874
+ process.exitCode = cliError.exitCode;
114875
+ }
114876
+ }
114877
+
114878
+ // src/commands/linear/my.ts
114879
+ init_esm_shims();
114880
+ var PRIORITY_EMOJI3 = {
114881
+ 0: "\u{1F534}",
114882
+ 1: "\u{1F7E0}",
114883
+ 2: "\u{1F7E1}",
114884
+ 3: "\u{1F7E2}",
114885
+ 4: "\u26AA"
114886
+ };
114887
+ async function listMyIssues(ctx, options = {}) {
114888
+ const limit2 = options.limit || 20;
114889
+ try {
114890
+ const client = getLinearClient();
114891
+ const viewer = await client.viewer;
114892
+ const filter4 = {
114893
+ assignee: { id: { eq: viewer.id } },
114894
+ state: {
114895
+ type: {
114896
+ nin: ["canceled", "completed"]
114897
+ }
114898
+ }
114899
+ };
114900
+ if (options.state) {
114901
+ filter4.state = {
114902
+ ...filter4.state || {},
114903
+ name: { eqIgnoreCase: options.state }
114904
+ };
114905
+ }
114906
+ const response = await client.issues({
114907
+ first: limit2,
114908
+ filter: filter4
114909
+ });
114910
+ const issues = response.nodes || [];
114911
+ const issuesWithDetails = await Promise.all(
114912
+ issues.map(async (issue) => ({
114913
+ issue,
114914
+ state: await issue.state,
114915
+ team: await issue.team
114916
+ }))
114917
+ );
114918
+ if (ctx.format === "json") {
114919
+ const issueData = issuesWithDetails.map(({ issue, state, team }) => ({
114920
+ id: issue.id,
114921
+ identifier: issue.identifier,
114922
+ title: issue.title,
114923
+ state: state?.name || null,
114924
+ stateType: state?.type || null,
114925
+ priority: issue.priority,
114926
+ team: team ? { key: team.key, name: team.name } : null,
114927
+ url: issue.url,
114928
+ createdAt: issue.createdAt,
114929
+ updatedAt: issue.updatedAt,
114930
+ dueDate: issue.dueDate || null
114931
+ }));
114932
+ ctx.output.data(
114933
+ JSON.stringify(
114934
+ hateoasWrap2({
114935
+ type: "my-issues",
114936
+ command: `skill linear my --json`,
114937
+ data: {
114938
+ user: {
114939
+ id: viewer.id,
114940
+ name: viewer.name,
114941
+ email: viewer.email
114942
+ },
114943
+ count: issues.length,
114944
+ issues: issueData
114945
+ },
114946
+ links: issueListLinks(
114947
+ issuesWithDetails.map(({ issue }) => ({
114948
+ identifier: issue.identifier,
114949
+ title: issue.title
114950
+ }))
114951
+ ),
114952
+ actions: issueListActions()
114953
+ }),
114954
+ null,
114955
+ 2
114956
+ )
114957
+ );
114958
+ return;
114959
+ }
114960
+ ctx.output.data("");
114961
+ ctx.output.data(`\u{1F464} My Issues (${issues.length}) - ${viewer.name}`);
114962
+ ctx.output.data("\u2500".repeat(80));
114963
+ if (issues.length === 0) {
114964
+ ctx.output.data("");
114965
+ ctx.output.data(" No issues assigned to you.");
114966
+ ctx.output.data("");
114967
+ ctx.output.data(
114968
+ ' Create one: skill linear create "Title" --assignee me'
114969
+ );
114970
+ ctx.output.data("");
114971
+ return;
114972
+ }
114973
+ const byStateType = /* @__PURE__ */ new Map();
114974
+ for (const item of issuesWithDetails) {
114975
+ const stateType = item.state?.type || "unknown";
114976
+ const existing = byStateType.get(stateType) || [];
114977
+ existing.push(item);
114978
+ byStateType.set(stateType, existing);
114979
+ }
114980
+ const stateOrder = ["started", "unstarted", "backlog", "triage"];
114981
+ for (const stateType of stateOrder) {
114982
+ const items = byStateType.get(stateType);
114983
+ if (!items || items.length === 0) continue;
114984
+ ctx.output.data("");
114985
+ ctx.output.data(` ${stateType.toUpperCase()}:`);
114986
+ for (const { issue, state, team } of items) {
114987
+ const emoji = PRIORITY_EMOJI3[issue.priority] || "\u26AA";
114988
+ const teamBadge = team ? `[${team.key}]` : "";
114989
+ const dueInfo = issue.dueDate ? ` | Due: ${issue.dueDate}` : "";
114990
+ ctx.output.data(
114991
+ ` ${emoji} ${teamBadge} ${issue.identifier}: ${issue.title}`
114992
+ );
114993
+ ctx.output.data(` ${state?.name || "unknown"}${dueInfo}`);
114994
+ }
114995
+ }
114996
+ ctx.output.data("");
114997
+ ctx.output.data(" Use `skill linear issue <ID>` for full details.");
114998
+ ctx.output.data("");
114999
+ } catch (error) {
115000
+ const cliError = error instanceof CLIError ? error : new CLIError({
115001
+ userMessage: "Failed to list your issues.",
115002
+ suggestion: "Verify LINEAR_API_KEY is set correctly.",
115003
+ cause: error
115004
+ });
115005
+ ctx.output.error(formatError(cliError));
115006
+ process.exitCode = cliError.exitCode;
115007
+ }
115008
+ }
115009
+
115010
+ // src/commands/linear/projects.ts
115011
+ init_esm_shims();
115012
+ async function listProjects(ctx, options = {}) {
115013
+ const limit2 = options.limit || 50;
115014
+ try {
115015
+ const client = getLinearClient();
115016
+ const response = await client.projects({
115017
+ first: limit2
115018
+ });
115019
+ const projects = response.nodes || [];
115020
+ if (ctx.format === "json") {
115021
+ ctx.output.data(
115022
+ JSON.stringify(
115023
+ {
115024
+ success: true,
115025
+ count: projects.length,
115026
+ projects: projects.map((project) => ({
115027
+ id: project.id,
115028
+ name: project.name,
115029
+ description: project.description,
115030
+ state: project.state,
115031
+ url: project.url
115032
+ }))
115033
+ },
115034
+ null,
115035
+ 2
115036
+ )
115037
+ );
115038
+ return;
115039
+ }
115040
+ ctx.output.data(`
115041
+ \u{1F4C1} Linear Projects (${projects.length}):`);
115042
+ ctx.output.data("-".repeat(80));
115043
+ if (projects.length === 0) {
115044
+ ctx.output.data(" No projects found.");
115045
+ } else {
115046
+ for (const project of projects) {
115047
+ const stateIcon = project.state === "completed" ? "\u2713" : "\u25CF";
115048
+ ctx.output.data(` ${stateIcon} ${project.name}`);
115049
+ ctx.output.data(` ID: ${project.id}`);
115050
+ ctx.output.data(` State: ${project.state}`);
115051
+ if (project.description) {
115052
+ const truncatedDesc = project.description.length > 60 ? project.description.slice(0, 60) + "..." : project.description;
115053
+ ctx.output.data(` Desc: ${truncatedDesc}`);
115054
+ }
115055
+ ctx.output.data("");
115056
+ }
115057
+ }
115058
+ } catch (error) {
115059
+ const cliError = error instanceof CLIError ? error : new CLIError({
115060
+ userMessage: "Failed to list Linear projects.",
115061
+ suggestion: "Verify LINEAR_API_KEY is set correctly.",
115062
+ cause: error
115063
+ });
115064
+ ctx.output.error(formatError(cliError));
115065
+ process.exitCode = cliError.exitCode;
115066
+ }
115067
+ }
115068
+
115069
+ // src/commands/linear/search.ts
115070
+ init_esm_shims();
115071
+ var PRIORITY_EMOJI4 = {
115072
+ 0: "\u{1F534}",
115073
+ 1: "\u{1F7E0}",
115074
+ 2: "\u{1F7E1}",
115075
+ 3: "\u{1F7E2}",
115076
+ 4: "\u26AA"
115077
+ };
115078
+ async function searchIssues(ctx, query, options = {}) {
115079
+ if (!query || query.trim().length === 0) {
115080
+ throw new CLIError({
115081
+ userMessage: "Search query is required.",
115082
+ suggestion: 'Usage: skill linear search "your query"',
115083
+ exitCode: 1
115084
+ });
115085
+ }
115086
+ const limit2 = options.limit || 20;
115087
+ try {
115088
+ const client = getLinearClient();
115089
+ const response = await client.searchIssues(query.trim(), {
115090
+ first: limit2
115091
+ });
115092
+ const issues = response.nodes || [];
115093
+ const issuesWithDetails = await Promise.all(
115094
+ issues.map(async (issue) => ({
115095
+ issue,
115096
+ state: await issue.state,
115097
+ assignee: await issue.assignee,
115098
+ team: await issue.team
115099
+ }))
115100
+ );
115101
+ if (ctx.format === "json") {
115102
+ const issueData = issuesWithDetails.map(
115103
+ ({ issue, state, assignee, team }) => ({
115104
+ id: issue.id,
115105
+ identifier: issue.identifier,
115106
+ title: issue.title,
115107
+ state: state?.name || null,
115108
+ priority: issue.priority,
115109
+ assignee: assignee ? { id: assignee.id, name: assignee.name, email: assignee.email } : null,
115110
+ team: team ? { key: team.key, name: team.name } : null,
115111
+ url: issue.url
115112
+ })
115113
+ );
115114
+ ctx.output.data(
115115
+ JSON.stringify(
115116
+ hateoasWrap2({
115117
+ type: "search-results",
115118
+ command: `skill linear search "${query}" --json`,
115119
+ data: {
115120
+ query,
115121
+ count: issues.length,
115122
+ issues: issueData
115123
+ },
115124
+ links: issueListLinks(
115125
+ issuesWithDetails.map(({ issue }) => ({
115126
+ identifier: issue.identifier,
115127
+ title: issue.title
115128
+ }))
115129
+ ),
115130
+ actions: issueListActions()
115131
+ }),
115132
+ null,
115133
+ 2
115134
+ )
115135
+ );
115136
+ return;
115137
+ }
115138
+ ctx.output.data("");
115139
+ ctx.output.data(`\u{1F50D} Search: "${query}" (${issues.length} results)`);
115140
+ ctx.output.data("\u2500".repeat(80));
115141
+ if (issues.length === 0) {
115142
+ ctx.output.data("");
115143
+ ctx.output.data(" No issues found matching your search.");
115144
+ ctx.output.data("");
115145
+ ctx.output.data(" Try:");
115146
+ ctx.output.data(" \u2022 Different keywords");
115147
+ ctx.output.data(" \u2022 skill linear issues (list all)");
115148
+ ctx.output.data(" \u2022 skill linear my (your assigned issues)");
115149
+ ctx.output.data("");
115150
+ return;
115151
+ }
115152
+ for (const { issue, state, assignee, team } of issuesWithDetails) {
115153
+ const emoji = PRIORITY_EMOJI4[issue.priority] || "\u26AA";
115154
+ const teamBadge = team ? `[${team.key}]` : "";
115155
+ ctx.output.data("");
115156
+ ctx.output.data(
115157
+ ` ${emoji} ${teamBadge} ${issue.identifier}: ${issue.title}`
115158
+ );
115159
+ ctx.output.data(
115160
+ ` Status: ${state?.name || "unknown"}${assignee ? ` | @${assignee.name}` : ""}`
115161
+ );
115162
+ }
115163
+ ctx.output.data("");
115164
+ ctx.output.data(" Use `skill linear issue <ID>` for full details.");
115165
+ ctx.output.data("");
115166
+ } catch (error) {
115167
+ const cliError = error instanceof CLIError ? error : new CLIError({
115168
+ userMessage: "Failed to search issues.",
115169
+ suggestion: "Verify LINEAR_API_KEY is set correctly.",
115170
+ cause: error
115171
+ });
115172
+ ctx.output.error(formatError(cliError));
115173
+ process.exitCode = cliError.exitCode;
115174
+ }
115175
+ }
115176
+
115177
+ // src/commands/linear/state.ts
115178
+ init_esm_shims();
115179
+ async function changeState(ctx, issueId, options) {
115180
+ if (!options.state || options.state.trim().length === 0) {
115181
+ throw new CLIError({
115182
+ userMessage: "State name is required.",
115183
+ suggestion: 'Usage: skill linear state ENG-123 --state "In Progress"',
115184
+ exitCode: 1
115185
+ });
115186
+ }
115187
+ try {
115188
+ const client = getLinearClient();
115189
+ const issue = await client.issue(issueId);
115190
+ if (!issue) {
115191
+ throw new CLIError({
115192
+ userMessage: `Issue not found: ${issueId}`,
115193
+ suggestion: "Use `skill linear issues --json` to list available issues.",
115194
+ exitCode: 1
115195
+ });
115196
+ }
115197
+ const team = await issue.team;
115198
+ if (!team) {
115199
+ throw new CLIError({
115200
+ userMessage: "Could not determine team for issue.",
115201
+ exitCode: 1
115202
+ });
115203
+ }
115204
+ const states = await team.states();
115205
+ const targetState = states.nodes.find(
115206
+ (s) => s.name.toLowerCase() === options.state.toLowerCase()
115207
+ );
115208
+ if (!targetState) {
115209
+ const availableStates = states.nodes.map((s) => s.name).join(", ");
115210
+ throw new CLIError({
115211
+ userMessage: `State not found: ${options.state}`,
115212
+ suggestion: `Available states for ${team.key}: ${availableStates}`,
115213
+ exitCode: 1
115214
+ });
115215
+ }
115216
+ const currentState = await issue.state;
115217
+ await client.updateIssue(issue.id, {
115218
+ stateId: targetState.id
115219
+ });
115220
+ const resultData = {
115221
+ issueId: issue.id,
115222
+ issueIdentifier: issue.identifier,
115223
+ previousState: currentState?.name || null,
115224
+ newState: targetState.name,
115225
+ stateType: targetState.type,
115226
+ success: true
115227
+ };
115228
+ if (ctx.format === "json") {
115229
+ ctx.output.data(
115230
+ JSON.stringify(
115231
+ hateoasWrap2({
115232
+ type: "state-change-result",
115233
+ command: `skill linear issue ${issue.identifier} --json`,
115234
+ data: resultData,
115235
+ links: issueLinks(issue.identifier, team.key),
115236
+ actions: issueActions(issue.identifier)
115237
+ }),
115238
+ null,
115239
+ 2
115240
+ )
115241
+ );
115242
+ return;
115243
+ }
115244
+ ctx.output.data("");
115245
+ ctx.output.data(`\u2705 ${issue.identifier} state changed`);
115246
+ ctx.output.data("\u2500".repeat(50));
115247
+ ctx.output.data(` From: ${currentState?.name || "Unknown"}`);
115248
+ ctx.output.data(` To: ${targetState.name}`);
115249
+ ctx.output.data("");
115250
+ ctx.output.data(` View: skill linear issue ${issue.identifier}`);
115251
+ ctx.output.data("");
115252
+ } catch (error) {
115253
+ const cliError = error instanceof CLIError ? error : new CLIError({
115254
+ userMessage: "Failed to change state.",
115255
+ suggestion: "Verify LINEAR_API_KEY is set correctly.",
115256
+ cause: error
115257
+ });
115258
+ ctx.output.error(formatError(cliError));
115259
+ process.exitCode = cliError.exitCode;
115260
+ }
115261
+ }
115262
+
115263
+ // src/commands/linear/states.ts
115264
+ init_esm_shims();
115265
+ var STATE_TYPE_EMOJI = {
115266
+ backlog: "\u{1F4CB}",
115267
+ unstarted: "\u26AA",
115268
+ started: "\u{1F535}",
115269
+ completed: "\u2705",
115270
+ canceled: "\u274C",
115271
+ triage: "\u{1F4E5}"
115272
+ };
115273
+ async function listStates(ctx, teamKey) {
115274
+ if (!teamKey || teamKey.trim().length === 0) {
115275
+ throw new CLIError({
115276
+ userMessage: "Team key is required.",
115277
+ suggestion: "Usage: skill linear states ENG\nUse `skill linear teams` to list teams.",
115278
+ exitCode: 1
115279
+ });
115280
+ }
115281
+ try {
115282
+ const client = getLinearClient();
115283
+ const teams = await client.teams();
115284
+ const team = teams.nodes.find(
115285
+ (t2) => t2.key.toLowerCase() === teamKey.toLowerCase() || t2.name.toLowerCase() === teamKey.toLowerCase()
115286
+ );
115287
+ if (!team) {
115288
+ throw new CLIError({
115289
+ userMessage: `Team not found: ${teamKey}`,
115290
+ suggestion: "Use `skill linear teams --json` to list available teams.",
115291
+ exitCode: 1
115292
+ });
115293
+ }
115294
+ const statesConnection = await team.states();
115295
+ const states = statesConnection.nodes || [];
115296
+ const sortedStates = [...states].sort((a, b) => a.position - b.position);
115297
+ if (ctx.format === "json") {
115298
+ const stateData = sortedStates.map((state) => ({
115299
+ id: state.id,
115300
+ name: state.name,
115301
+ type: state.type,
115302
+ color: state.color,
115303
+ position: state.position,
115304
+ description: state.description || null
115305
+ }));
115306
+ ctx.output.data(
115307
+ JSON.stringify(
115308
+ hateoasWrap2({
115309
+ type: "state-list",
115310
+ command: `skill linear states ${team.key} --json`,
115311
+ data: {
115312
+ team: { id: team.id, key: team.key, name: team.name },
115313
+ count: states.length,
115314
+ states: stateData
115315
+ },
115316
+ links: teamLinks(team.key),
115317
+ actions: [
115318
+ {
115319
+ action: "change-issue-state",
115320
+ command: `skill linear state <issue-id> --state "<state-name>"`,
115321
+ description: "Change an issue to this state"
115322
+ }
115323
+ ]
115324
+ }),
115325
+ null,
115326
+ 2
115327
+ )
115328
+ );
115329
+ return;
115330
+ }
115331
+ ctx.output.data("");
115332
+ ctx.output.data(`\u{1F4CA} Workflow States for ${team.name} (${team.key})`);
115333
+ ctx.output.data("\u2500".repeat(60));
115334
+ const typeOrder = [
115335
+ "triage",
115336
+ "backlog",
115337
+ "unstarted",
115338
+ "started",
115339
+ "completed",
115340
+ "canceled"
115341
+ ];
115342
+ const statesByType = /* @__PURE__ */ new Map();
115343
+ for (const state of sortedStates) {
115344
+ const existing = statesByType.get(state.type) || [];
115345
+ existing.push(state);
115346
+ statesByType.set(state.type, existing);
115347
+ }
115348
+ for (const type of typeOrder) {
115349
+ const typeStates = statesByType.get(type);
115350
+ if (!typeStates || typeStates.length === 0) continue;
115351
+ const emoji = STATE_TYPE_EMOJI[type] || "\u{1F4CC}";
115352
+ ctx.output.data("");
115353
+ ctx.output.data(` ${emoji} ${type.toUpperCase()}`);
115354
+ for (const state of typeStates) {
115355
+ ctx.output.data(` \u2022 ${state.name}`);
115356
+ }
115357
+ }
115358
+ ctx.output.data("");
115359
+ ctx.output.data(
115360
+ ' Usage: skill linear state ENG-123 --state "In Progress"'
115361
+ );
115362
+ ctx.output.data("");
115363
+ } catch (error) {
115364
+ const cliError = error instanceof CLIError ? error : new CLIError({
115365
+ userMessage: "Failed to list states.",
115366
+ suggestion: "Verify LINEAR_API_KEY is set correctly.",
115367
+ cause: error
115368
+ });
115369
+ ctx.output.error(formatError(cliError));
115370
+ process.exitCode = cliError.exitCode;
115371
+ }
115372
+ }
115373
+
115374
+ // src/commands/linear/teams.ts
115375
+ init_esm_shims();
115376
+ async function listTeams(ctx, options = {}) {
115377
+ try {
115378
+ const client = getLinearClient();
115379
+ const response = await client.teams();
115380
+ const teams = response.nodes || [];
115381
+ if (ctx.format === "json") {
115382
+ ctx.output.data(
115383
+ JSON.stringify(
115384
+ {
115385
+ success: true,
115386
+ count: teams.length,
115387
+ teams: teams.map((team) => ({
115388
+ id: team.id,
115389
+ key: team.key,
115390
+ name: team.name,
115391
+ description: team.description
115392
+ }))
115393
+ },
115394
+ null,
115395
+ 2
115396
+ )
115397
+ );
115398
+ return;
115399
+ }
115400
+ ctx.output.data(`
115401
+ \u{1F465} Linear Teams (${teams.length}):`);
115402
+ ctx.output.data("-".repeat(80));
115403
+ if (teams.length === 0) {
115404
+ ctx.output.data(" No teams found.");
115405
+ } else {
115406
+ for (const team of teams) {
115407
+ ctx.output.data(` ${team.key} - ${team.name}`);
115408
+ ctx.output.data(` ID: ${team.id}`);
115409
+ if (team.description) {
115410
+ ctx.output.data(` Desc: ${team.description}`);
115411
+ }
115412
+ ctx.output.data("");
115413
+ }
115414
+ }
115415
+ } catch (error) {
115416
+ const cliError = error instanceof CLIError ? error : new CLIError({
115417
+ userMessage: "Failed to list Linear teams.",
115418
+ suggestion: "Verify LINEAR_API_KEY is set correctly.",
115419
+ cause: error
115420
+ });
115421
+ ctx.output.error(formatError(cliError));
115422
+ process.exitCode = cliError.exitCode;
115423
+ }
115424
+ }
115425
+
115426
+ // src/commands/linear/update.ts
115427
+ init_esm_shims();
115428
+ async function updateIssue(ctx, issueId, options) {
115429
+ const hasUpdate = Object.values(options).some((v) => v !== void 0);
115430
+ if (!hasUpdate) {
115431
+ throw new CLIError({
115432
+ userMessage: "No updates specified.",
115433
+ suggestion: "Use --title, --description, --priority, --estimate, --due-date, or --project.",
115434
+ exitCode: 1
115435
+ });
115436
+ }
115437
+ try {
115438
+ const client = getLinearClient();
115439
+ const issue = await client.issue(issueId);
115440
+ if (!issue) {
115441
+ throw new CLIError({
115442
+ userMessage: `Issue not found: ${issueId}`,
115443
+ suggestion: "Use `skill linear issues --json` to list available issues.",
115444
+ exitCode: 1
115445
+ });
115446
+ }
115447
+ const updatePayload = {};
115448
+ if (options.title) {
115449
+ updatePayload.title = options.title;
115450
+ }
115451
+ if (options.description !== void 0) {
115452
+ updatePayload.description = options.description;
115453
+ }
115454
+ if (options.priority !== void 0) {
115455
+ if (options.priority < 0 || options.priority > 4) {
115456
+ throw new CLIError({
115457
+ userMessage: "Invalid priority value.",
115458
+ suggestion: "Priority must be 0-4: 0=Urgent, 1=High, 2=Medium, 3=Low, 4=None",
115459
+ exitCode: 1
115460
+ });
115461
+ }
115462
+ updatePayload.priority = options.priority;
115463
+ }
115464
+ if (options.estimate !== void 0) {
115465
+ updatePayload.estimate = options.estimate;
115466
+ }
115467
+ if (options.dueDate !== void 0) {
115468
+ updatePayload.dueDate = options.dueDate;
115469
+ }
115470
+ if (options.project) {
115471
+ const projects = await client.projects();
115472
+ const project = projects.nodes.find(
115473
+ (p) => p.id === options.project || p.name.toLowerCase() === options.project.toLowerCase()
115474
+ );
115475
+ if (!project) {
115476
+ throw new CLIError({
115477
+ userMessage: `Project not found: ${options.project}`,
115478
+ suggestion: "Use `skill linear projects --json` to list available projects.",
115479
+ exitCode: 1
115480
+ });
115481
+ }
115482
+ updatePayload.projectId = project.id;
115483
+ }
115484
+ await client.updateIssue(issue.id, updatePayload);
115485
+ const updatedIssue = await client.issue(issueId);
115486
+ const [state, assignee, team] = await Promise.all([
115487
+ updatedIssue?.state,
115488
+ updatedIssue?.assignee,
115489
+ updatedIssue?.team
115490
+ ]);
115491
+ const changes = Object.keys(options).filter(
115492
+ (k) => options[k] !== void 0
115493
+ );
115494
+ const resultData = {
115495
+ issueId: issue.id,
115496
+ issueIdentifier: issue.identifier,
115497
+ changes,
115498
+ updated: {
115499
+ title: updatedIssue?.title,
115500
+ description: updatedIssue?.description || null,
115501
+ priority: updatedIssue?.priority,
115502
+ estimate: updatedIssue?.estimate || null,
115503
+ dueDate: updatedIssue?.dueDate || null,
115504
+ state: state?.name || null,
115505
+ assignee: assignee ? { id: assignee.id, name: assignee.name } : null
115506
+ },
115507
+ success: true
115508
+ };
115509
+ if (ctx.format === "json") {
115510
+ ctx.output.data(
115511
+ JSON.stringify(
115512
+ hateoasWrap2({
115513
+ type: "update-result",
115514
+ command: `skill linear issue ${issue.identifier} --json`,
115515
+ data: resultData,
115516
+ links: issueLinks(issue.identifier, team?.key),
115517
+ actions: issueActions(issue.identifier)
115518
+ }),
115519
+ null,
115520
+ 2
115521
+ )
115522
+ );
115523
+ return;
115524
+ }
115525
+ ctx.output.data("");
115526
+ ctx.output.data(`\u2705 Updated ${issue.identifier}`);
115527
+ ctx.output.data("\u2500".repeat(50));
115528
+ for (const change of changes) {
115529
+ const value = options[change];
115530
+ ctx.output.data(` ${change}: ${value}`);
115531
+ }
115532
+ ctx.output.data("");
115533
+ ctx.output.data(` View: skill linear issue ${issue.identifier}`);
115534
+ ctx.output.data("");
115535
+ } catch (error) {
115536
+ const cliError = error instanceof CLIError ? error : new CLIError({
115537
+ userMessage: "Failed to update issue.",
115538
+ suggestion: "Verify LINEAR_API_KEY is set correctly.",
115539
+ cause: error
115540
+ });
115541
+ ctx.output.error(formatError(cliError));
115542
+ process.exitCode = cliError.exitCode;
115543
+ }
115544
+ }
115545
+
115546
+ // src/commands/linear/users.ts
115547
+ init_esm_shims();
115548
+ async function listUsers(ctx) {
115549
+ try {
115550
+ const client = getLinearClient();
115551
+ const usersConnection = await client.users();
115552
+ const users = usersConnection.nodes || [];
115553
+ const activeUsers = users.filter((u) => u.active);
115554
+ if (ctx.format === "json") {
115555
+ const userData = activeUsers.map((user) => ({
115556
+ id: user.id,
115557
+ name: user.name,
115558
+ displayName: user.displayName,
115559
+ email: user.email,
115560
+ admin: user.admin,
115561
+ active: user.active,
115562
+ avatarUrl: user.avatarUrl || null
115563
+ }));
115564
+ ctx.output.data(
115565
+ JSON.stringify(
115566
+ hateoasWrap2({
115567
+ type: "user-list",
115568
+ command: `skill linear users --json`,
115569
+ data: {
115570
+ count: activeUsers.length,
115571
+ users: userData
115572
+ },
115573
+ links: userListLinks(
115574
+ activeUsers.map((u) => ({
115575
+ id: u.id,
115576
+ email: u.email || "",
115577
+ name: u.name
115578
+ }))
115579
+ ),
115580
+ actions: [
115581
+ {
115582
+ action: "assign-to-user",
115583
+ command: `skill linear assign <issue-id> --to "<email>"`,
115584
+ description: "Assign an issue to this user"
115585
+ },
115586
+ {
115587
+ action: "filter-by-user",
115588
+ command: `skill linear issues --assignee "<email>" --json`,
115589
+ description: "View user's assigned issues"
115590
+ }
115591
+ ]
115592
+ }),
115593
+ null,
115594
+ 2
115595
+ )
115596
+ );
115597
+ return;
115598
+ }
115599
+ ctx.output.data("");
115600
+ ctx.output.data(`\u{1F465} Workspace Users (${activeUsers.length})`);
115601
+ ctx.output.data("\u2500".repeat(60));
115602
+ for (const user of activeUsers) {
115603
+ const adminBadge = user.admin ? " [Admin]" : "";
115604
+ ctx.output.data("");
115605
+ ctx.output.data(` ${user.name}${adminBadge}`);
115606
+ ctx.output.data(` Email: ${user.email || "(no email)"}`);
115607
+ }
115608
+ ctx.output.data("");
115609
+ ctx.output.data(
115610
+ ' Assign: skill linear assign ENG-123 --to "email@example.com"'
115611
+ );
115612
+ ctx.output.data("");
115613
+ } catch (error) {
115614
+ const cliError = error instanceof CLIError ? error : new CLIError({
115615
+ userMessage: "Failed to list users.",
115616
+ suggestion: "Verify LINEAR_API_KEY is set correctly.",
115617
+ cause: error
115618
+ });
115619
+ ctx.output.error(formatError(cliError));
115620
+ process.exitCode = cliError.exitCode;
115621
+ }
115622
+ }
115623
+
115624
+ // src/commands/linear/index.ts
115625
+ async function contextFromCommand2(command, options) {
115626
+ const opts = typeof command.optsWithGlobals === "function" ? command.optsWithGlobals() : {
115627
+ ...command.parent?.opts(),
115628
+ ...command.opts()
115629
+ };
115630
+ return createContext({
115631
+ format: options.json ? "json" : opts.format,
115632
+ verbose: opts.verbose,
115633
+ quiet: opts.quiet
115634
+ });
115635
+ }
115636
+ function registerLinearCommands(program3) {
115637
+ const linear = program3.command("linear").description(
115638
+ `Linear issue tracking commands
115639
+
115640
+ Quick start:
115641
+ skill linear my Your assigned issues
115642
+ skill linear issues --team ENG Team's issues
115643
+ skill linear create "Title" Create issue
115644
+ skill linear search "query" Search issues
115645
+
115646
+ All commands support --json for machine-readable output.`
115647
+ );
115648
+ linear.command("issues").description(
115649
+ `List issues with optional filters
115650
+
115651
+ Examples:
115652
+ skill linear issues All recent issues
115653
+ skill linear issues --team ENG Filter by team
115654
+ skill linear issues --state "In Progress" Filter by state
115655
+ skill linear issues --assignee me Your issues
115656
+ skill linear issues --priority 0 Urgent only`
115657
+ ).option("--limit <number>", "Maximum results (default: 20)", "20").option("--team <key>", "Filter by team key (e.g., ENG)").option("--state <name>", "Filter by state name").option("--assignee <email>", 'Filter by assignee (or "me")').option("--project <name>", "Filter by project name").option("--priority <0-4>", "Filter by priority (0=urgent)").option("--json", "Output as JSON with HATEOAS links").action(async (options, command) => {
115658
+ const ctx = await contextFromCommand2(command, options);
115659
+ await listIssues(ctx, {
115660
+ limit: parseInt(options.limit || "20", 10),
115661
+ team: options.team,
115662
+ state: options.state,
115663
+ assignee: options.assignee,
115664
+ project: options.project,
115665
+ priority: options.priority !== void 0 ? parseInt(options.priority, 10) : void 0
115666
+ });
115667
+ });
115668
+ linear.command("my").description(
115669
+ `List your assigned issues (excludes completed/canceled)
115670
+
115671
+ Examples:
115672
+ skill linear my All your open issues
115673
+ skill linear my --state "In Progress" Only in-progress
115674
+ skill linear my --limit 5 Just top 5`
115675
+ ).option("--limit <number>", "Maximum results (default: 20)", "20").option("--state <name>", "Filter by state name").option("--json", "Output as JSON with HATEOAS links").action(async (options, command) => {
115676
+ const ctx = await contextFromCommand2(command, options);
115677
+ await listMyIssues(ctx, {
115678
+ limit: parseInt(options.limit || "20", 10),
115679
+ state: options.state
115680
+ });
115681
+ });
115682
+ linear.command("search").description(
115683
+ `Search issues by text
115684
+
115685
+ Examples:
115686
+ skill linear search "authentication bug"
115687
+ skill linear search "login" --limit 10
115688
+ skill linear search "error" --json`
115689
+ ).argument("<query>", "Search query").option("--limit <number>", "Maximum results (default: 20)", "20").option("--json", "Output as JSON with HATEOAS links").action(async (query, options, command) => {
115690
+ const ctx = await contextFromCommand2(command, options);
115691
+ await searchIssues(ctx, query, {
115692
+ limit: parseInt(options.limit || "20", 10)
115693
+ });
115694
+ });
115695
+ linear.command("issue").description(
115696
+ `Get detailed info about an issue
115697
+
115698
+ Examples:
115699
+ skill linear issue ENG-123
115700
+ skill linear issue ENG-123 --json`
115701
+ ).argument("<id>", "Issue identifier (e.g., ENG-123)").option("--json", "Output as JSON with HATEOAS links").action(async (id, options, command) => {
115702
+ const ctx = await contextFromCommand2(command, options);
115703
+ await getIssue(ctx, id);
115704
+ });
115705
+ linear.command("create").description(
115706
+ `Create a new issue
115707
+
115708
+ Examples:
115709
+ skill linear create "Fix login bug"
115710
+ skill linear create "Title" --team ENG --priority 1
115711
+ skill linear create "Task" --assignee me --label "Frontend"
115712
+
115713
+ Priority: 0=Urgent, 1=High, 2=Medium, 3=Low, 4=None`
115714
+ ).argument("<title>", "Issue title").option("--description <text>", "Issue description (markdown)").option("--team <key>", "Team key (defaults to first team)").option("--priority <0-4>", "Priority level", "2").option("--assignee <email>", 'Assignee email (or "me")').option(
115715
+ "--label <name>",
115716
+ "Add label (repeatable)",
115717
+ (v, p) => [...p, v],
115718
+ []
115719
+ ).option("--project <name>", "Project name").option("--estimate <points>", "Estimate in points").option("--due-date <YYYY-MM-DD>", "Due date").option("--json", "Output as JSON with HATEOAS links").action(async (title, options, command) => {
115720
+ const ctx = await contextFromCommand2(command, options);
115721
+ await createIssue(ctx, title, {
115722
+ description: options.description,
115723
+ priority: parseInt(options.priority || "2", 10),
115724
+ team: options.team,
115725
+ label: options.label,
115726
+ assignee: options.assignee,
115727
+ project: options.project,
115728
+ estimate: options.estimate ? parseInt(options.estimate, 10) : void 0,
115729
+ dueDate: options.dueDate
115730
+ });
115731
+ });
115732
+ linear.command("update").description(
115733
+ `Update issue properties
115734
+
115735
+ Examples:
115736
+ skill linear update ENG-123 --title "New title"
115737
+ skill linear update ENG-123 --priority 1 --estimate 3
115738
+ skill linear update ENG-123 --due-date 2024-03-15`
115739
+ ).argument("<id>", "Issue identifier").option("--title <text>", "New title").option("--description <text>", "New description").option("--priority <0-4>", "New priority").option("--estimate <points>", "New estimate").option("--due-date <YYYY-MM-DD>", "New due date").option("--project <name>", "Move to project").option("--json", "Output as JSON with HATEOAS links").action(async (id, options, command) => {
115740
+ const ctx = await contextFromCommand2(command, options);
115741
+ await updateIssue(ctx, id, {
115742
+ title: options.title,
115743
+ description: options.description,
115744
+ priority: options.priority !== void 0 ? parseInt(options.priority, 10) : void 0,
115745
+ estimate: options.estimate !== void 0 ? parseInt(options.estimate, 10) : void 0,
115746
+ dueDate: options.dueDate,
115747
+ project: options.project
115748
+ });
115749
+ });
115750
+ linear.command("assign").description(
115751
+ `Assign or unassign an issue
115752
+
115753
+ Examples:
115754
+ skill linear assign ENG-123 --to user@example.com
115755
+ skill linear assign ENG-123 --to me
115756
+ skill linear assign ENG-123 --unassign`
115757
+ ).argument("<id>", "Issue identifier").option("--to <email>", 'Assign to user email (or "me")').option("--unassign", "Remove assignee").option("--json", "Output as JSON with HATEOAS links").action(async (id, options, command) => {
115758
+ const ctx = await contextFromCommand2(command, options);
115759
+ await assignIssue(ctx, id, {
115760
+ to: options.to,
115761
+ unassign: options.unassign
115762
+ });
115763
+ });
115764
+ linear.command("state").description(
115765
+ `Change issue workflow state
115766
+
115767
+ Examples:
115768
+ skill linear state ENG-123 --state "In Progress"
115769
+ skill linear state ENG-123 --state "Done"
115770
+
115771
+ Use 'skill linear states <team>' to see available states.`
115772
+ ).argument("<id>", "Issue identifier").requiredOption("--state <name>", "Target state name").option("--json", "Output as JSON with HATEOAS links").action(async (id, options, command) => {
115773
+ const ctx = await contextFromCommand2(command, options);
115774
+ await changeState(ctx, id, { state: options.state });
115775
+ });
115776
+ linear.command("close").description(
115777
+ `Close an issue (mark as done or canceled)
115778
+
115779
+ Examples:
115780
+ skill linear close ENG-123 Close as done
115781
+ skill linear close ENG-123 --canceled Cancel the issue`
115782
+ ).argument("<id>", "Issue identifier").option("--canceled", "Close as canceled instead of done").option("--json", "Output as JSON with HATEOAS links").action(async (id, options, command) => {
115783
+ const ctx = await contextFromCommand2(command, options);
115784
+ await closeIssue(ctx, id, { canceled: options.canceled });
115785
+ });
115786
+ linear.command("label").description(
115787
+ `Add or remove labels from an issue
115788
+
115789
+ Examples:
115790
+ skill linear label ENG-123 --add "Bug"
115791
+ skill linear label ENG-123 --add "Bug" --add "Frontend"
115792
+ skill linear label ENG-123 --remove "WIP"
115793
+
115794
+ Use 'skill linear labels <team>' to see available labels.`
115795
+ ).argument("<id>", "Issue identifier").option(
115796
+ "--add <name>",
115797
+ "Add label (repeatable)",
115798
+ (v, p) => [...p, v],
115799
+ []
115800
+ ).option(
115801
+ "--remove <name>",
115802
+ "Remove label (repeatable)",
115803
+ (v, p) => [...p, v],
115804
+ []
115805
+ ).option("--json", "Output as JSON with HATEOAS links").action(async (id, options, command) => {
115806
+ const ctx = await contextFromCommand2(command, options);
115807
+ await modifyLabels(ctx, id, {
115808
+ add: options.add,
115809
+ remove: options.remove
115810
+ });
115811
+ });
115812
+ linear.command("link").description(
115813
+ `Link issues together (dependencies, relations)
115814
+
115815
+ Examples:
115816
+ skill linear link ENG-123 --blocks ENG-456
115817
+ skill linear link ENG-123 --blocked-by ENG-456
115818
+ skill linear link ENG-123 --related ENG-456
115819
+ skill linear link ENG-123 --duplicate ENG-456`
115820
+ ).argument("<id>", "Source issue identifier").option("--blocks <id>", "This issue blocks <id>").option("--blocked-by <id>", "This issue is blocked by <id>").option("--related <id>", "Related to <id>").option("--duplicate <id>", "Duplicate of <id>").option("--json", "Output as JSON with HATEOAS links").action(async (id, options, command) => {
115821
+ const ctx = await contextFromCommand2(command, options);
115822
+ await linkIssues(ctx, id, {
115823
+ blocks: options.blocks,
115824
+ blockedBy: options.blockedBy,
115825
+ related: options.related,
115826
+ duplicate: options.duplicate
115827
+ });
115828
+ });
115829
+ linear.command("comment").description(
115830
+ `Add a comment to an issue
115831
+
115832
+ Examples:
115833
+ skill linear comment ENG-123 --body "Great work!"
115834
+ skill linear comment ENG-123 --body "## Update\\n- Item 1\\n- Item 2"`
115835
+ ).argument("<id>", "Issue identifier").requiredOption("--body <text>", "Comment text (supports markdown)").option("--json", "Output as JSON with HATEOAS links").action(async (id, options, command) => {
115836
+ const ctx = await contextFromCommand2(command, options);
115837
+ await addComment(ctx, id, { body: options.body });
115838
+ });
115839
+ linear.command("comments").description(
115840
+ `List comments on an issue
115841
+
115842
+ Examples:
115843
+ skill linear comments ENG-123
115844
+ skill linear comments ENG-123 --limit 10 --json`
115845
+ ).argument("<id>", "Issue identifier").option("--limit <number>", "Maximum results (default: 50)", "50").option("--json", "Output as JSON with HATEOAS links").action(async (id, options, command) => {
115846
+ const ctx = await contextFromCommand2(command, options);
115847
+ await listComments(ctx, id, {
115848
+ limit: parseInt(options.limit || "50", 10)
115849
+ });
115850
+ });
115851
+ linear.command("teams").description(
115852
+ `List all teams in your workspace
115853
+
115854
+ Examples:
115855
+ skill linear teams
115856
+ skill linear teams --json`
115857
+ ).option("--json", "Output as JSON with HATEOAS links").action(async (options, command) => {
115858
+ const ctx = await contextFromCommand2(command, options);
115859
+ await listTeams(ctx);
115860
+ });
115861
+ linear.command("states").description(
115862
+ `List workflow states for a team
115863
+
115864
+ Examples:
115865
+ skill linear states ENG
115866
+ skill linear states "Product" --json`
115867
+ ).argument("<team>", "Team key or name").option("--json", "Output as JSON with HATEOAS links").action(async (team, options, command) => {
115868
+ const ctx = await contextFromCommand2(command, options);
115869
+ await listStates(ctx, team);
115870
+ });
115871
+ linear.command("labels").description(
115872
+ `List labels for a team
115873
+
115874
+ Examples:
115875
+ skill linear labels ENG
115876
+ skill linear labels ENG --json`
115877
+ ).argument("<team>", "Team key or name").option("--json", "Output as JSON with HATEOAS links").action(async (team, options, command) => {
115878
+ const ctx = await contextFromCommand2(command, options);
115879
+ await listLabels(ctx, team);
115880
+ });
115881
+ linear.command("users").description(
115882
+ `List workspace users
115883
+
115884
+ Examples:
115885
+ skill linear users
115886
+ skill linear users --json`
115887
+ ).option("--json", "Output as JSON with HATEOAS links").action(async (options, command) => {
115888
+ const ctx = await contextFromCommand2(command, options);
115889
+ await listUsers(ctx);
115890
+ });
115891
+ linear.command("projects").description(
115892
+ `List all projects
115893
+
115894
+ Examples:
115895
+ skill linear projects
115896
+ skill linear projects --limit 100 --json`
115897
+ ).option("--limit <number>", "Maximum results (default: 50)", "50").option("--json", "Output as JSON with HATEOAS links").action(async (options, command) => {
115898
+ const ctx = await contextFromCommand2(command, options);
115899
+ await listProjects(ctx, { limit: parseInt(options.limit || "50", 10) });
115900
+ });
115901
+ }
115902
+
113469
115903
  // src/commands/memory/index.ts
113470
115904
  init_esm_shims();
113471
115905
 
@@ -117139,6 +119573,7 @@ registerDatasetCommands(program2);
117139
119573
  registerResponseCommands(program2);
117140
119574
  registerToolsCommands(program2);
117141
119575
  registerMemoryCommands(program2);
119576
+ registerLinearCommands(program2);
117142
119577
  registerFaqCommands(program2);
117143
119578
  registerDeployCommands(program2);
117144
119579
  registerKbCommands(program2);