devto-mcp 0.4.4 → 0.4.5

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/tools.d.ts CHANGED
@@ -20,7 +20,7 @@ export declare function createEpic(title: string, description: string): Promise<
20
20
  export declare function createTask(title: string, description: string, epicKey?: string, issueType?: string): Promise<string>;
21
21
  export declare function createSubtask(title: string, description: string, parentKey: string): Promise<string>;
22
22
  export declare function getTasks(epicKey?: string): Promise<string>;
23
- export declare function updateTask(issueKey: string, status?: string, comment?: string, assignee?: string, priority?: string, labels?: string[], epicKey?: string): Promise<string>;
23
+ export declare function updateTask(issueKey: string, status?: string, comment?: string, assignee?: string, priority?: string, labels?: string[], epicKey?: string, title?: string, description?: string): Promise<string>;
24
24
  export declare function getComments(issueKey: string): Promise<string>;
25
25
  export declare function getProjectSummary(): Promise<string>;
26
26
  export declare function listEpics(): Promise<string>;
@@ -34,4 +34,33 @@ export declare function createSprintTool(name: string, startDate?: string, endDa
34
34
  export declare function moveToSprintTool(sprintId: string, issueKeys: string[]): Promise<string>;
35
35
  export declare function manageSprintTool(sprintId: string, action: string): Promise<string>;
36
36
  export declare function getStatus(): Promise<string>;
37
+ export declare function searchIssues(jql: string, maxResults?: number): Promise<string>;
38
+ export declare function getCustomFields(): Promise<string>;
39
+ export declare function linkIssues(issueKey1: string, issueKey2: string, linkType: string): Promise<string>;
40
+ export declare function getIssueLinks(issueKey: string): Promise<string>;
41
+ export declare function getLinkTypes(): Promise<string>;
42
+ export declare function logWork(issueKey: string, timeSpent: string, comment?: string): Promise<string>;
43
+ export declare function getWorklog(issueKey: string): Promise<string>;
44
+ export declare function setEstimate(issueKey: string, estimate: string): Promise<string>;
45
+ export declare function listVersions(): Promise<string>;
46
+ export declare function createVersion(name: string, description?: string, releaseDate?: string): Promise<string>;
47
+ export declare function releaseVersion(versionName: string): Promise<string>;
48
+ export declare function getBacklog(): Promise<string>;
49
+ export declare function moveToBacklog(issueKeys: string[]): Promise<string>;
50
+ export declare function bulkCreateTasks(tasks: Array<{
51
+ title: string;
52
+ description: string;
53
+ type?: string;
54
+ epic_key?: string;
55
+ }>): Promise<string>;
56
+ export declare function bulkUpdateTasks(updates: Array<{
57
+ issue_key: string;
58
+ status?: string;
59
+ comment?: string;
60
+ assignee?: string;
61
+ priority?: string;
62
+ }>): Promise<string>;
63
+ export declare function attachFile(issueKey: string, filePath: string): Promise<string>;
64
+ export declare function listAttachments(issueKey: string): Promise<string>;
65
+ export declare function manageWatchers(issueKey: string, action: string, userName?: string): Promise<string>;
37
66
  //# sourceMappingURL=tools.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"AAqNA;;;;;;;;GAQG;AACH,wBAAsB,eAAe,CACnC,KAAK,EAAE,MAAM,GACZ,OAAO,CAAC;IAAE,QAAQ,EAAE,IAAI,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,QAAQ,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CAmEhF;AAID,wBAAsB,UAAU,CAAC,kBAAkB,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAyG5E;AAED,wBAAsB,WAAW,CAAC,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAgBjE;AAED,wBAAsB,UAAU,CAAC,KAAK,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAGpF;AAED,wBAAsB,UAAU,CAC9B,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,OAAO,CAAC,EAAE,MAAM,EAChB,SAAS,CAAC,EAAE,MAAM,GACjB,OAAO,CAAC,MAAM,CAAC,CAiBjB;AAED,wBAAsB,aAAa,CACjC,KAAK,EAAE,MAAM,EACb,WAAW,EAAE,MAAM,EACnB,SAAS,EAAE,MAAM,GAChB,OAAO,CAAC,MAAM,CAAC,CAajB;AAED,wBAAsB,QAAQ,CAAC,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAmChE;AAED,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,MAAM,EACf,OAAO,CAAC,EAAE,MAAM,EAChB,QAAQ,CAAC,EAAE,MAAM,EACjB,QAAQ,CAAC,EAAE,MAAM,EACjB,MAAM,CAAC,EAAE,MAAM,EAAE,EACjB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,CAAC,CAqCjB;AAED,wBAAsB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAyBnE;AAED,wBAAsB,iBAAiB,IAAI,OAAO,CAAC,MAAM,CAAC,CAiCzD;AAID,wBAAsB,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,CAuBjD;AAED,wBAAsB,UAAU,CAC9B,QAAQ,EAAE,MAAM,EAChB,MAAM,CAAC,EAAE,MAAM,EACf,KAAK,CAAC,EAAE,MAAM,EACd,WAAW,CAAC,EAAE,MAAM,EACpB,OAAO,CAAC,EAAE,MAAM,GACf,OAAO,CAAC,MAAM,CAAC,CAsBjB;AAED,wBAAsB,OAAO,CAAC,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAiD9D;AAED,wBAAsB,aAAa,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQxF;AAID,wBAAsB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAUvF;AAED,wBAAsB,eAAe,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAQvE;AAED,wBAAsB,WAAW,IAAI,OAAO,CAAC,MAAM,CAAC,CAkBnD;AAED,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAO1G;AAED,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,SAAS,EAAE,MAAM,EAAE,GAAG,OAAO,CAAC,MAAM,CAAC,CA8B7F;AAED,wBAAsB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAqBxF;AAED,wBAAsB,SAAS,IAAI,OAAO,CAAC,MAAM,CAAC,CAuBjD"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../src/tools.ts"],"names":[],"mappings":"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"}
package/dist/tools.js CHANGED
@@ -1,4 +1,37 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
2
35
  var __importDefault = (this && this.__importDefault) || function (mod) {
3
36
  return (mod && mod.__esModule) ? mod : { "default": mod };
4
37
  };
@@ -24,9 +57,50 @@ exports.createSprintTool = createSprintTool;
24
57
  exports.moveToSprintTool = moveToSprintTool;
25
58
  exports.manageSprintTool = manageSprintTool;
26
59
  exports.getStatus = getStatus;
60
+ exports.searchIssues = searchIssues;
61
+ exports.getCustomFields = getCustomFields;
62
+ exports.linkIssues = linkIssues;
63
+ exports.getIssueLinks = getIssueLinks;
64
+ exports.getLinkTypes = getLinkTypes;
65
+ exports.logWork = logWork;
66
+ exports.getWorklog = getWorklog;
67
+ exports.setEstimate = setEstimate;
68
+ exports.listVersions = listVersions;
69
+ exports.createVersion = createVersion;
70
+ exports.releaseVersion = releaseVersion;
71
+ exports.getBacklog = getBacklog;
72
+ exports.moveToBacklog = moveToBacklog;
73
+ exports.bulkCreateTasks = bulkCreateTasks;
74
+ exports.bulkUpdateTasks = bulkUpdateTasks;
75
+ exports.attachFile = attachFile;
76
+ exports.listAttachments = listAttachments;
77
+ exports.manageWatchers = manageWatchers;
27
78
  const sdk_1 = __importDefault(require("@anthropic-ai/sdk"));
79
+ const fs = __importStar(require("fs"));
80
+ const path = __importStar(require("path"));
28
81
  const client_1 = require("./client");
29
82
  const config_1 = require("./config");
83
+ // ─── Content sanitization (prompt injection defense) ─────────────────────────
84
+ /**
85
+ * Wrap user-authored content from Jira in boundary markers so the LLM
86
+ * knows it's untrusted external content, not system instructions.
87
+ */
88
+ function sanitizeUserContent(text) {
89
+ if (!text)
90
+ return text;
91
+ // Strip common prompt injection patterns
92
+ const cleaned = text
93
+ .replace(/ignore (?:all )?previous instructions/gi, "[filtered]")
94
+ .replace(/you are now/gi, "[filtered]")
95
+ .replace(/system:\s/gi, "[filtered] ");
96
+ return cleaned;
97
+ }
98
+ /**
99
+ * Sanitize a task title from Jira (lightweight — just strips injection patterns).
100
+ */
101
+ function sanitizeTitle(title) {
102
+ return sanitizeUserContent(title);
103
+ }
30
104
  // ─── Anthropic system prompt ────────────────────────────────────────────────
31
105
  const PLAN_SYSTEM_PROMPT = `You are DevTo, an AI work manager for software developers. Your job is to take a feature description and break it into a structured development plan. Always return a JSON object with this exact structure and nothing else — no preamble, no markdown, just the JSON:
32
106
  {
@@ -311,16 +385,16 @@ async function getTasks(epicKey) {
311
385
  ``,
312
386
  ];
313
387
  res.tasks.forEach((task) => {
314
- lines.push(`**${task.key}** — ${task.title}`);
388
+ lines.push(`**${task.key}** — ${sanitizeTitle(task.title)}`);
315
389
  lines.push(` Status: ${task.status} | Type: ${task.type}${task.assignee ? ` | Assignee: ${task.assignee}` : ""}`);
316
390
  if (task.parent)
317
- lines.push(` Parent: ${task.parent.key} — ${task.parent.title}`);
391
+ lines.push(` Parent: ${task.parent.key} — ${sanitizeTitle(task.parent.title)}`);
318
392
  lines.push(` ${task.url}`);
319
393
  lines.push(``);
320
394
  });
321
395
  return lines.join("\n");
322
396
  }
323
- async function updateTask(issueKey, status, comment, assignee, priority, labels, epicKey) {
397
+ async function updateTask(issueKey, status, comment, assignee, priority, labels, epicKey, title, description) {
324
398
  // Resolve ambiguous references
325
399
  const resolved = await resolveIssueKey(issueKey);
326
400
  if (!resolved.resolved) {
@@ -348,8 +422,16 @@ async function updateTask(issueKey, status, comment, assignee, priority, labels,
348
422
  body.labels = labels;
349
423
  if (resolvedEpicKey)
350
424
  body.epic_key = resolvedEpicKey;
425
+ if (title)
426
+ body.title = title;
427
+ if (description)
428
+ body.description = description;
351
429
  const res = await (0, client_1.callApi)("PUT", `/api/v1/task/${resolved.key}`, body);
352
430
  const parts = [];
431
+ if (title)
432
+ parts.push(`Renamed **${resolved.key}** to "${title}".`);
433
+ if (description)
434
+ parts.push(`Updated description for **${resolved.key}**.`);
353
435
  if (status)
354
436
  parts.push(`Moved **${resolved.key}** to \`${status}\`.`);
355
437
  if (res.comment)
@@ -382,7 +464,7 @@ async function getComments(issueKey) {
382
464
  res.comments.forEach((c) => {
383
465
  const date = new Date(c.created).toLocaleDateString();
384
466
  lines.push(`**${c.author}** (${date}):`);
385
- lines.push(c.body);
467
+ lines.push(sanitizeUserContent(c.body));
386
468
  lines.push(``);
387
469
  });
388
470
  return lines.join("\n");
@@ -408,11 +490,11 @@ async function getProjectSummary() {
408
490
  }
409
491
  lines.push(`### Active Tasks`);
410
492
  res.issues.forEach((issue) => {
411
- const parts = [`**${issue.key}** — ${issue.title} (${issue.status})`];
493
+ const parts = [`**${issue.key}** — ${sanitizeTitle(issue.title)} (${issue.status})`];
412
494
  if (issue.assignee)
413
495
  parts.push(` Assignee: ${issue.assignee}`);
414
496
  if (issue.parent_key)
415
- parts.push(` Parent: ${issue.parent_key}${issue.epic_name ? ` — ${issue.epic_name}` : ""}`);
497
+ parts.push(` Parent: ${issue.parent_key}${issue.epic_name ? ` — ${sanitizeTitle(issue.epic_name)}` : ""}`);
416
498
  lines.push(parts.join("\n"));
417
499
  lines.push(``);
418
500
  });
@@ -628,4 +710,382 @@ async function getStatus() {
628
710
  }
629
711
  return lines.join("\n");
630
712
  }
713
+ async function searchIssues(jql, maxResults) {
714
+ const res = await (0, client_1.callApi)("POST", "/api/v1/search", {
715
+ jql,
716
+ max_results: maxResults ?? 50,
717
+ });
718
+ if (res.total === 0) {
719
+ return `No issues found matching JQL: \`${jql}\``;
720
+ }
721
+ const lines = [
722
+ `## Search Results (${res.total} total)`,
723
+ ``,
724
+ `| Key | Title | Status | Type | Assignee |`,
725
+ `|-----|-------|--------|------|----------|`,
726
+ ];
727
+ res.issues.forEach((issue) => {
728
+ lines.push(`| ${issue.key} | ${issue.title} | ${issue.status} | ${issue.type} | ${issue.assignee ?? "—"} |`);
729
+ });
730
+ if (res.total > res.issues.length) {
731
+ lines.push(``, `_Showing ${res.issues.length} of ${res.total}. Refine your JQL for more specific results._`);
732
+ }
733
+ return lines.join("\n");
734
+ }
735
+ async function getCustomFields() {
736
+ const res = await (0, client_1.callApi)("GET", "/api/v1/fields");
737
+ if (res.total === 0) {
738
+ return "No custom fields found in the project.";
739
+ }
740
+ const lines = [
741
+ `## Custom Fields (${res.total})`,
742
+ ``,
743
+ `| ID | Name | Type | Required |`,
744
+ `|----|------|------|----------|`,
745
+ ];
746
+ res.fields.forEach((field) => {
747
+ lines.push(`| ${field.id} | ${field.name} | ${field.type} | ${field.required ? "Yes" : "No"} |`);
748
+ });
749
+ return lines.join("\n");
750
+ }
751
+ // ─── Issue Linking ────────────────────────────────────────────────────────────
752
+ async function linkIssues(issueKey1, issueKey2, linkType) {
753
+ const resolved1 = await resolveIssueKey(issueKey1);
754
+ if (!resolved1.resolved) {
755
+ return resolved1.prompt;
756
+ }
757
+ const resolved2 = await resolveIssueKey(issueKey2);
758
+ if (!resolved2.resolved) {
759
+ return resolved2.prompt;
760
+ }
761
+ await (0, client_1.callApi)("POST", "/api/v1/issues/link", {
762
+ inward_key: resolved1.key,
763
+ outward_key: resolved2.key,
764
+ link_type: linkType,
765
+ });
766
+ return `Linked **${resolved1.key}** → **${resolved2.key}** (${linkType}).`;
767
+ }
768
+ async function getIssueLinks(issueKey) {
769
+ const resolved = await resolveIssueKey(issueKey);
770
+ if (!resolved.resolved) {
771
+ return resolved.prompt;
772
+ }
773
+ const res = await (0, client_1.callApi)("GET", `/api/v1/issues/${resolved.key}/links`);
774
+ if (res.total === 0) {
775
+ return `No links found on **${resolved.key}**.`;
776
+ }
777
+ const lines = [
778
+ `## Links on ${resolved.key} (${res.total})`,
779
+ ``,
780
+ ];
781
+ res.links.forEach((link) => {
782
+ if (link.inwardIssue) {
783
+ lines.push(`- **${link.type}** ← ${link.inwardIssue.key} — ${link.inwardIssue.title} (${link.inwardIssue.status})`);
784
+ }
785
+ if (link.outwardIssue) {
786
+ lines.push(`- **${link.type}** → ${link.outwardIssue.key} — ${link.outwardIssue.title} (${link.outwardIssue.status})`);
787
+ }
788
+ });
789
+ return lines.join("\n");
790
+ }
791
+ async function getLinkTypes() {
792
+ const res = await (0, client_1.callApi)("GET", "/api/v1/issues/link-types");
793
+ if (res.total === 0) {
794
+ return "No link types available.";
795
+ }
796
+ const lines = [
797
+ `## Link Types (${res.total})`,
798
+ ``,
799
+ `| Name | Inward | Outward |`,
800
+ `|------|--------|---------|`,
801
+ ];
802
+ res.link_types.forEach((lt) => {
803
+ lines.push(`| ${lt.name} | ${lt.inward} | ${lt.outward} |`);
804
+ });
805
+ return lines.join("\n");
806
+ }
807
+ async function logWork(issueKey, timeSpent, comment) {
808
+ const resolved = await resolveIssueKey(issueKey);
809
+ if (!resolved.resolved) {
810
+ return resolved.prompt;
811
+ }
812
+ const body = { time_spent: timeSpent };
813
+ if (comment)
814
+ body.comment = comment;
815
+ await (0, client_1.callApi)("POST", `/api/v1/issues/${resolved.key}/worklog`, body);
816
+ return `Logged **${timeSpent}** on **${resolved.key}**.${comment ? ` Comment: "${comment}"` : ""}`;
817
+ }
818
+ async function getWorklog(issueKey) {
819
+ const resolved = await resolveIssueKey(issueKey);
820
+ if (!resolved.resolved) {
821
+ return resolved.prompt;
822
+ }
823
+ const res = await (0, client_1.callApi)("GET", `/api/v1/issues/${resolved.key}/worklog`);
824
+ if (res.total === 0) {
825
+ return `No work logged on **${resolved.key}**.`;
826
+ }
827
+ const lines = [
828
+ `## Work Log — ${resolved.key} (${res.total} entries)`,
829
+ ``,
830
+ ];
831
+ res.worklogs.forEach((entry) => {
832
+ const date = new Date(entry.started).toLocaleDateString();
833
+ lines.push(`**${entry.author}** — ${entry.time_spent} (${date})`);
834
+ if (entry.comment)
835
+ lines.push(` ${entry.comment}`);
836
+ lines.push(``);
837
+ });
838
+ return lines.join("\n");
839
+ }
840
+ async function setEstimate(issueKey, estimate) {
841
+ const resolved = await resolveIssueKey(issueKey);
842
+ if (!resolved.resolved) {
843
+ return resolved.prompt;
844
+ }
845
+ await (0, client_1.callApi)("PUT", `/api/v1/issues/${resolved.key}/time`, {
846
+ original_estimate: estimate,
847
+ });
848
+ return `Set estimate for **${resolved.key}** to **${estimate}**.`;
849
+ }
850
+ async function listVersions() {
851
+ const res = await (0, client_1.callApi)("GET", "/api/v1/versions");
852
+ if (res.total === 0) {
853
+ return "No versions found in the project. Use `create_version` to create one.";
854
+ }
855
+ const lines = [
856
+ `## Versions (${res.total})`,
857
+ ``,
858
+ ];
859
+ res.versions.forEach((v) => {
860
+ const status = v.released ? "Released" : "Unreleased";
861
+ const date = v.release_date ? ` (${v.release_date})` : "";
862
+ lines.push(`**${v.name}** — ${status}${date}`);
863
+ if (v.description)
864
+ lines.push(` ${v.description}`);
865
+ lines.push(``);
866
+ });
867
+ return lines.join("\n");
868
+ }
869
+ async function createVersion(name, description, releaseDate) {
870
+ const body = { name };
871
+ if (description)
872
+ body.description = description;
873
+ if (releaseDate)
874
+ body.release_date = releaseDate;
875
+ const res = await (0, client_1.callApi)("POST", "/api/v1/versions", body);
876
+ return `Version **${res.name}** created (ID: ${res.id}).`;
877
+ }
878
+ async function releaseVersion(versionName) {
879
+ // Find version by name
880
+ const versions = await (0, client_1.callApi)("GET", "/api/v1/versions");
881
+ const match = versions.versions.find((v) => v.name.toLowerCase() === versionName.toLowerCase() || v.id === versionName);
882
+ if (!match) {
883
+ return `No version found matching "${versionName}". Use \`list_versions\` to see available versions.`;
884
+ }
885
+ if (match.released) {
886
+ return `Version **${match.name}** is already released.`;
887
+ }
888
+ await (0, client_1.callApi)("PUT", `/api/v1/versions/${match.id}`, {
889
+ released: true,
890
+ release_date: new Date().toISOString().split("T")[0],
891
+ });
892
+ return `Version **${match.name}** marked as released.`;
893
+ }
894
+ async function getBacklog() {
895
+ const res = await (0, client_1.callApi)("GET", "/api/v1/backlog");
896
+ if (res.total === 0) {
897
+ return "Backlog is empty — all issues are assigned to sprints.";
898
+ }
899
+ const lines = [
900
+ `## Backlog (${res.total} issues)`,
901
+ ``,
902
+ ];
903
+ res.issues.forEach((issue) => {
904
+ lines.push(`**${issue.key}** — ${issue.title}`);
905
+ lines.push(` Status: ${issue.status} | Type: ${issue.type}${issue.assignee ? ` | Assignee: ${issue.assignee}` : ""}`);
906
+ if (issue.parent)
907
+ lines.push(` Parent: ${issue.parent.key} — ${issue.parent.title}`);
908
+ lines.push(``);
909
+ });
910
+ return lines.join("\n");
911
+ }
912
+ async function moveToBacklog(issueKeys) {
913
+ const resolvedKeys = [];
914
+ for (const key of issueKeys) {
915
+ const resolved = await resolveIssueKey(key);
916
+ if (!resolved.resolved) {
917
+ return resolved.prompt;
918
+ }
919
+ resolvedKeys.push(resolved.key);
920
+ }
921
+ await (0, client_1.callApi)("POST", "/api/v1/backlog", {
922
+ issue_keys: resolvedKeys,
923
+ });
924
+ return `Moved **${resolvedKeys.join(", ")}** to backlog.`;
925
+ }
926
+ // ─── Bulk Operations ──────────────────────────────────────────────────────────
927
+ async function bulkCreateTasks(tasks) {
928
+ const results = [];
929
+ for (const task of tasks) {
930
+ try {
931
+ const body = { title: task.title, description: task.description };
932
+ if (task.type)
933
+ body.type = task.type;
934
+ if (task.epic_key)
935
+ body.epic_key = task.epic_key;
936
+ const res = await (0, client_1.callApi)("POST", "/api/v1/task", body);
937
+ results.push({ key: res.key, title: task.title });
938
+ }
939
+ catch (err) {
940
+ results.push({
941
+ key: "FAILED",
942
+ title: task.title,
943
+ error: err instanceof Error ? err.message : String(err),
944
+ });
945
+ }
946
+ }
947
+ const succeeded = results.filter((r) => r.key !== "FAILED");
948
+ const failed = results.filter((r) => r.key === "FAILED");
949
+ const lines = [
950
+ `## Bulk Create — ${succeeded.length}/${tasks.length} succeeded`,
951
+ ``,
952
+ ];
953
+ succeeded.forEach((r) => {
954
+ lines.push(`- **${r.key}** — ${r.title}`);
955
+ });
956
+ if (failed.length > 0) {
957
+ lines.push(``, `### Failed (${failed.length})`);
958
+ failed.forEach((r) => {
959
+ lines.push(`- ${r.title}: ${r.error}`);
960
+ });
961
+ }
962
+ return lines.join("\n");
963
+ }
964
+ async function bulkUpdateTasks(updates) {
965
+ const results = [];
966
+ for (const update of updates) {
967
+ const resolved = await resolveIssueKey(update.issue_key);
968
+ if (!resolved.resolved) {
969
+ results.push({ key: update.issue_key, success: false, error: "Could not resolve issue key" });
970
+ continue;
971
+ }
972
+ try {
973
+ const body = {};
974
+ if (update.status)
975
+ body.status = update.status;
976
+ if (update.comment)
977
+ body.comment = update.comment;
978
+ if (update.assignee)
979
+ body.assignee = update.assignee;
980
+ if (update.priority)
981
+ body.priority = update.priority;
982
+ await (0, client_1.callApi)("PUT", `/api/v1/task/${resolved.key}`, body);
983
+ results.push({ key: resolved.key, success: true });
984
+ }
985
+ catch (err) {
986
+ results.push({
987
+ key: resolved.key,
988
+ success: false,
989
+ error: err instanceof Error ? err.message : String(err),
990
+ });
991
+ }
992
+ }
993
+ const succeeded = results.filter((r) => r.success);
994
+ const failed = results.filter((r) => !r.success);
995
+ const lines = [
996
+ `## Bulk Update — ${succeeded.length}/${updates.length} succeeded`,
997
+ ``,
998
+ ];
999
+ succeeded.forEach((r) => {
1000
+ lines.push(`- **${r.key}** updated`);
1001
+ });
1002
+ if (failed.length > 0) {
1003
+ lines.push(``, `### Failed (${failed.length})`);
1004
+ failed.forEach((r) => {
1005
+ lines.push(`- **${r.key}**: ${r.error}`);
1006
+ });
1007
+ }
1008
+ return lines.join("\n");
1009
+ }
1010
+ async function attachFile(issueKey, filePath) {
1011
+ const resolved = await resolveIssueKey(issueKey);
1012
+ if (!resolved.resolved) {
1013
+ return resolved.prompt;
1014
+ }
1015
+ // Read the file from the local filesystem
1016
+ let fileBuffer;
1017
+ try {
1018
+ fileBuffer = fs.readFileSync(filePath);
1019
+ }
1020
+ catch (err) {
1021
+ return `Could not read file at \`${filePath}\`: ${err instanceof Error ? err.message : String(err)}`;
1022
+ }
1023
+ const filename = path.basename(filePath);
1024
+ const contentBase64 = fileBuffer.toString("base64");
1025
+ await (0, client_1.callApi)("POST", `/api/v1/issues/${resolved.key}/attachments`, {
1026
+ filename,
1027
+ content_base64: contentBase64,
1028
+ });
1029
+ return `Attached **${filename}** to **${resolved.key}**.`;
1030
+ }
1031
+ async function listAttachments(issueKey) {
1032
+ const resolved = await resolveIssueKey(issueKey);
1033
+ if (!resolved.resolved) {
1034
+ return resolved.prompt;
1035
+ }
1036
+ const res = await (0, client_1.callApi)("GET", `/api/v1/issues/${resolved.key}/attachments`);
1037
+ if (res.total === 0) {
1038
+ return `No attachments on **${resolved.key}**.`;
1039
+ }
1040
+ const lines = [
1041
+ `## Attachments on ${resolved.key} (${res.total})`,
1042
+ ``,
1043
+ ];
1044
+ res.attachments.forEach((a) => {
1045
+ const size = a.size < 1024 ? `${a.size}B` : a.size < 1048576 ? `${Math.round(a.size / 1024)}KB` : `${(a.size / 1048576).toFixed(1)}MB`;
1046
+ const date = new Date(a.created).toLocaleDateString();
1047
+ lines.push(`- **${a.filename}** (${size}) — ${a.author}, ${date}`);
1048
+ });
1049
+ return lines.join("\n");
1050
+ }
1051
+ async function manageWatchers(issueKey, action, userName) {
1052
+ const resolved = await resolveIssueKey(issueKey);
1053
+ if (!resolved.resolved) {
1054
+ return resolved.prompt;
1055
+ }
1056
+ switch (action) {
1057
+ case "list": {
1058
+ const res = await (0, client_1.callApi)("GET", `/api/v1/issues/${resolved.key}/watchers`);
1059
+ if (res.total === 0) {
1060
+ return `No watchers on **${resolved.key}**.`;
1061
+ }
1062
+ const lines = [
1063
+ `## Watchers on ${resolved.key} (${res.total})`,
1064
+ ``,
1065
+ ];
1066
+ res.watchers.forEach((w) => {
1067
+ lines.push(`- ${w.display_name}`);
1068
+ });
1069
+ return lines.join("\n");
1070
+ }
1071
+ case "add": {
1072
+ if (!userName) {
1073
+ return `User name is required for the "add" action. Provide the display name of the person to add as a watcher.`;
1074
+ }
1075
+ await (0, client_1.callApi)("POST", `/api/v1/issues/${resolved.key}/watchers`, {
1076
+ user_name: userName,
1077
+ });
1078
+ return `Added **${userName}** as watcher on **${resolved.key}**.`;
1079
+ }
1080
+ case "remove": {
1081
+ if (!userName) {
1082
+ return `User name is required for the "remove" action. Provide the display name of the person to remove as a watcher.`;
1083
+ }
1084
+ await (0, client_1.callApi)("DELETE", `/api/v1/issues/${resolved.key}/watchers?user_name=${encodeURIComponent(userName)}`);
1085
+ return `Removed **${userName}** as watcher from **${resolved.key}**.`;
1086
+ }
1087
+ default:
1088
+ return `Invalid action "${action}". Use "list", "add", or "remove".`;
1089
+ }
1090
+ }
631
1091
  //# sourceMappingURL=tools.js.map