brn-toolkit 1.0.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.
Files changed (48) hide show
  1. package/GEMINI.md +92 -0
  2. package/README.md +145 -0
  3. package/cli/brn.ts +301 -0
  4. package/dist/cli/brn.js +274 -0
  5. package/dist/lib/utils.js +167 -0
  6. package/dist/skills/github/scripts/create_pr.js +24 -0
  7. package/dist/skills/github/scripts/get_repo_info.js +23 -0
  8. package/dist/skills/github/scripts/list_prs.js +27 -0
  9. package/dist/skills/github/scripts/list_repos.js +39 -0
  10. package/dist/skills/jira/scripts/add_comment.js +36 -0
  11. package/dist/skills/jira/scripts/get_ticket.js +45 -0
  12. package/dist/skills/jira/scripts/list_tickets.js +42 -0
  13. package/dist/skills/jira/scripts/update_ticket.js +30 -0
  14. package/dist/skills/workflow/scripts/start.js +75 -0
  15. package/dist/skills/workspace-manager/scripts/configure_workspace.js +59 -0
  16. package/dist/skills/workspace-manager/scripts/create_workspace.js +60 -0
  17. package/lib/utils.ts +236 -0
  18. package/package.json +46 -0
  19. package/skills/git-worktree/SKILL.md +49 -0
  20. package/skills/git-worktree/scripts/clone_repo.sh +53 -0
  21. package/skills/git-worktree/scripts/create_worktree.sh +66 -0
  22. package/skills/git-worktree/scripts/list_worktrees.sh +39 -0
  23. package/skills/git-worktree/scripts/remove_worktree.sh +47 -0
  24. package/skills/github/SKILL.md +51 -0
  25. package/skills/github/references/api_patterns.md +36 -0
  26. package/skills/github/scripts/create_pr.ts +38 -0
  27. package/skills/github/scripts/get_repo_info.ts +39 -0
  28. package/skills/github/scripts/list_prs.ts +45 -0
  29. package/skills/github/scripts/list_repos.ts +54 -0
  30. package/skills/jira/SKILL.md +50 -0
  31. package/skills/jira/references/api_patterns.md +59 -0
  32. package/skills/jira/scripts/add_comment.ts +41 -0
  33. package/skills/jira/scripts/get_ticket.ts +71 -0
  34. package/skills/jira/scripts/list_tickets.ts +64 -0
  35. package/skills/jira/scripts/update_ticket.ts +50 -0
  36. package/skills/workflow/SKILL.md +94 -0
  37. package/skills/workflow/references/coding_workflow.md +88 -0
  38. package/skills/workflow/references/planning_workflow.md +96 -0
  39. package/skills/workflow/references/review_workflow.md +104 -0
  40. package/skills/workflow/scripts/start.ts +93 -0
  41. package/skills/workspace-manager/SKILL.md +49 -0
  42. package/skills/workspace-manager/scripts/configure_workspace.sh +87 -0
  43. package/skills/workspace-manager/scripts/configure_workspace.ts +70 -0
  44. package/skills/workspace-manager/scripts/create_workspace.sh +66 -0
  45. package/skills/workspace-manager/scripts/create_workspace.ts +74 -0
  46. package/skills/workspace-manager/scripts/get_active_workspace.sh +24 -0
  47. package/skills/workspace-manager/scripts/list_workspaces.sh +31 -0
  48. package/skills/workspace-manager/scripts/switch_workspace.sh +38 -0
@@ -0,0 +1,39 @@
1
+ #!/bin/bash
2
+ # List all worktrees for a repository
3
+ # Usage: list_worktrees.sh <repo_name>
4
+
5
+ set -e
6
+
7
+ REPO_NAME="$1"
8
+
9
+ if [ -z "$REPO_NAME" ]; then
10
+ echo "Usage: list_worktrees.sh <repo_name>"
11
+ exit 1
12
+ fi
13
+
14
+ # Get the script directory to find workspace-manager
15
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
16
+ WORKSPACE_SCRIPT="$SCRIPT_DIR/../../workspace-manager/scripts/get_active_workspace.sh"
17
+
18
+ if [ ! -f "$WORKSPACE_SCRIPT" ]; then
19
+ echo "Error: workspace-manager skill not found"
20
+ exit 1
21
+ fi
22
+
23
+ # Get workspace config
24
+ WORKSPACE_JSON=$("$WORKSPACE_SCRIPT" --json)
25
+ WORK_DIR=$(echo "$WORKSPACE_JSON" | yq -r '.path')
26
+ WORK_DIR="${WORK_DIR/#\~/$HOME}"
27
+
28
+ REPO_PATH="$WORK_DIR/$REPO_NAME"
29
+
30
+ if [ ! -d "$REPO_PATH" ]; then
31
+ echo "Error: Repository not found at $REPO_PATH"
32
+ exit 1
33
+ fi
34
+
35
+ cd "$REPO_PATH"
36
+
37
+ echo "Worktrees for $REPO_NAME:"
38
+ echo "========================="
39
+ git worktree list
@@ -0,0 +1,47 @@
1
+ #!/bin/bash
2
+ # Remove a worktree
3
+ # Usage: remove_worktree.sh <repo_name> <branch_name>
4
+
5
+ set -e
6
+
7
+ REPO_NAME="$1"
8
+ BRANCH_NAME="$2"
9
+
10
+ if [ -z "$REPO_NAME" ] || [ -z "$BRANCH_NAME" ]; then
11
+ echo "Usage: remove_worktree.sh <repo_name> <branch_name>"
12
+ exit 1
13
+ fi
14
+
15
+ # Get the script directory to find workspace-manager
16
+ SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)"
17
+ WORKSPACE_SCRIPT="$SCRIPT_DIR/../../workspace-manager/scripts/get_active_workspace.sh"
18
+
19
+ if [ ! -f "$WORKSPACE_SCRIPT" ]; then
20
+ echo "Error: workspace-manager skill not found"
21
+ exit 1
22
+ fi
23
+
24
+ # Get workspace config
25
+ WORKSPACE_JSON=$("$WORKSPACE_SCRIPT" --json)
26
+ WORK_DIR=$(echo "$WORKSPACE_JSON" | yq -r '.path')
27
+ WORK_DIR="${WORK_DIR/#\~/$HOME}"
28
+
29
+ REPO_PATH="$WORK_DIR/$REPO_NAME"
30
+ WORKTREE_PATH="$WORK_DIR/${REPO_NAME}-worktrees/$BRANCH_NAME"
31
+
32
+ if [ ! -d "$REPO_PATH" ]; then
33
+ echo "Error: Repository not found at $REPO_PATH"
34
+ exit 1
35
+ fi
36
+
37
+ if [ ! -d "$WORKTREE_PATH" ]; then
38
+ echo "Error: Worktree not found at $WORKTREE_PATH"
39
+ exit 1
40
+ fi
41
+
42
+ cd "$REPO_PATH"
43
+
44
+ echo "Removing worktree for '$BRANCH_NAME'..."
45
+ git worktree remove "$WORKTREE_PATH"
46
+
47
+ echo "✓ Removed worktree at $WORKTREE_PATH"
@@ -0,0 +1,51 @@
1
+ ---
2
+ name: brn:github
3
+ description: |
4
+ Interact with GitHub API for repository and pull request management.
5
+ Use when: (1) Listing repositories, (2) Getting repository info, (3) Creating PRs.
6
+ ---
7
+
8
+ # GitHub Integration
9
+
10
+ ## Description
11
+ The GitHub skill provides tools to interact with the GitHub API directly from the CLI. It supports authentication via personal access tokens configured in the workspace.
12
+
13
+ ## Available Scripts
14
+
15
+ ### `list_repos`
16
+ Lists repositories for a specified organization or user. Useful for discovery.
17
+
18
+ * **Usage**: `brn github list_repos [org_or_user]`
19
+ * **Arguments**:
20
+ * `org_or_user` (optional): The name of the organization or user to list repositories for. If omitted, lists repositories for the authenticated user.
21
+ * **Example**: `brn github list_repos my-org`
22
+
23
+ ### `get_repo_info`
24
+ Retrieves detailed information about a specific repository.
25
+
26
+ * **Usage**: `brn github get_repo_info <owner/repo>`
27
+ * **Arguments**:
28
+ * `owner/repo`: The full repository name (e.g., `facebook/react`).
29
+ * **Example**: `brn github get_repo_info my-org/my-project`
30
+
31
+ ### `create_pr`
32
+ Creates a Pull Request on GitHub.
33
+
34
+ * **Usage**: `brn github create_pr <owner/repo> <head> <base> <title>`
35
+ * **Arguments**:
36
+ * `owner/repo`: The full repository name.
37
+ * `head`: The name of the branch where your changes are implemented.
38
+ * `base`: The name of the branch you want to merge into (e.g., `main`).
39
+ * `title`: The title of the Pull Request.
40
+ * **Example**: `brn github create_pr my-org/my-project feature/login main "feat: Add login page"`
41
+
42
+ ### `list_prs`
43
+ Lists open Pull Requests for a repository.
44
+
45
+ * **Usage**: `brn github list_prs <owner/repo>`
46
+ * **Arguments**:
47
+ * `owner/repo`: The full repository name.
48
+ * **Example**: `brn github list_prs my-org/my-project`
49
+
50
+ ## API Reference
51
+ See [references/api_patterns.md](references/api_patterns.md) for error handling and patterns.
@@ -0,0 +1,36 @@
1
+ # GitHub API Patterns
2
+
3
+ Common patterns for GitHub API integration.
4
+
5
+ ## Authentication
6
+
7
+ All requests use Bearer token authentication:
8
+ ```
9
+ Authorization: Bearer <token>
10
+ ```
11
+
12
+ ## Common Endpoints
13
+
14
+ | Endpoint | Method | Description |
15
+ |----------|--------|-------------|
16
+ | `/user/repos` | GET | List authenticated user's repos |
17
+ | `/orgs/{org}/repos` | GET | List org repos |
18
+ | `/repos/{owner}/{repo}` | GET | Get repo info |
19
+ | `/repos/{owner}/{repo}/pulls` | GET | List PRs |
20
+ | `/repos/{owner}/{repo}/pulls` | POST | Create PR |
21
+ | `/repos/{owner}/{repo}/issues` | GET | List issues |
22
+
23
+ ## Error Handling
24
+
25
+ | Code | Meaning | Action |
26
+ |------|---------|--------|
27
+ | 401 | Bad credentials | Check token |
28
+ | 403 | Forbidden | Check token permissions |
29
+ | 404 | Not found | Check repo name/permissions |
30
+ | 422 | Validation failed | Check request body |
31
+
32
+ ## Rate Limiting
33
+
34
+ - Authenticated: 5000 requests/hour
35
+ - Check `X-RateLimit-Remaining` header
36
+ - If rate limited, wait until `X-RateLimit-Reset` timestamp
@@ -0,0 +1,38 @@
1
+ #!/usr/bin/env npx tsx
2
+ /**
3
+ * Create a pull request
4
+ * Usage: npx tsx create_pr.ts <owner/repo> <head> <base> <title> [body]
5
+ */
6
+ import { githubRequest } from "../../../lib/utils.js";
7
+
8
+ interface PR {
9
+ number: number;
10
+ title: string;
11
+ html_url: string;
12
+ }
13
+
14
+ const [repoPath, head, base, title, body] = process.argv.slice(2);
15
+
16
+ if (!repoPath || !head || !base || !title) {
17
+ console.log(
18
+ "Usage: npx tsx create_pr.ts <owner/repo> <head_branch> <base_branch> <title> [body]"
19
+ );
20
+ console.log(
21
+ "Example: npx tsx create_pr.ts myorg/app feature-123 main 'feat: Add feature'"
22
+ );
23
+ process.exit(1);
24
+ }
25
+
26
+ const pr = await githubRequest<PR>(`/repos/${repoPath}/pulls`, {
27
+ method: "POST",
28
+ body: JSON.stringify({
29
+ title,
30
+ head,
31
+ base,
32
+ body: body ?? "",
33
+ }),
34
+ });
35
+
36
+ console.log(`✓ Created PR #${pr.number}: ${pr.title}`);
37
+ console.log(` URL: ${pr.html_url}`);
38
+ console.log(` ${head} → ${base}`);
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env npx tsx
2
+ /**
3
+ * Get repository information
4
+ * Usage: npx tsx get_repo_info.ts <owner/repo>
5
+ */
6
+ import { githubRequest } from "../../../lib/utils.js";
7
+
8
+ interface Repo {
9
+ full_name: string;
10
+ description: string | null;
11
+ private: boolean;
12
+ default_branch: string;
13
+ language: string | null;
14
+ stargazers_count: number;
15
+ forks_count: number;
16
+ clone_url: string;
17
+ ssh_url: string;
18
+ }
19
+
20
+ const repoPath = process.argv[2];
21
+
22
+ if (!repoPath) {
23
+ console.log("Usage: npx tsx get_repo_info.ts <owner/repo>");
24
+ console.log("Example: npx tsx get_repo_info.ts octocat/Hello-World");
25
+ process.exit(1);
26
+ }
27
+
28
+ const repo = await githubRequest<Repo>(`/repos/${repoPath}`);
29
+
30
+ console.log(`Repository: ${repo.full_name}`);
31
+ console.log("=".repeat(50));
32
+ console.log(`Description: ${repo.description ?? "N/A"}`);
33
+ console.log(`Visibility: ${repo.private ? "Private" : "Public"}`);
34
+ console.log(`Default: ${repo.default_branch}`);
35
+ console.log(`Language: ${repo.language ?? "N/A"}`);
36
+ console.log(`Stars: ${repo.stargazers_count}`);
37
+ console.log(`Forks: ${repo.forks_count}`);
38
+ console.log(`Clone URL: ${repo.clone_url}`);
39
+ console.log(`SSH URL: ${repo.ssh_url}`);
@@ -0,0 +1,45 @@
1
+ #!/usr/bin/env npx tsx
2
+ /**
3
+ * List pull requests for a repository
4
+ * Usage: npx tsx list_prs.ts <owner/repo> [state]
5
+ */
6
+ import { githubRequest } from "../../../lib/utils.js";
7
+
8
+ interface PR {
9
+ number: number;
10
+ title: string;
11
+ draft: boolean;
12
+ html_url: string;
13
+ head: { ref: string };
14
+ base: { ref: string };
15
+ user: { login: string };
16
+ }
17
+
18
+ const repoPath = process.argv[2];
19
+ const state = process.argv[3] ?? "open";
20
+
21
+ if (!repoPath) {
22
+ console.log("Usage: npx tsx list_prs.ts <owner/repo> [state]");
23
+ console.log("States: open, closed, all (default: open)");
24
+ process.exit(1);
25
+ }
26
+
27
+ const prs = await githubRequest<PR[]>(
28
+ `/repos/${repoPath}/pulls?state=${state}&per_page=30`
29
+ );
30
+
31
+ console.log(`Pull Requests for ${repoPath} (${state}):`);
32
+ console.log("=".repeat(50));
33
+
34
+ if (prs.length === 0) {
35
+ console.log("No pull requests found.");
36
+ process.exit(0);
37
+ }
38
+
39
+ for (const pr of prs) {
40
+ const draft = pr.draft ? "📝" : "";
41
+ console.log(`#${pr.number} ${draft} ${pr.title}`);
42
+ console.log(` ${pr.head.ref} → ${pr.base.ref} by @${pr.user.login}`);
43
+ console.log(` ${pr.html_url}`);
44
+ console.log();
45
+ }
@@ -0,0 +1,54 @@
1
+ #!/usr/bin/env npx tsx
2
+ /**
3
+ * List repositories for an organization or user
4
+ * Usage: npx tsx list_repos.ts [org_or_user]
5
+ */
6
+ import { githubRequest, getActiveWorkspace } from "../../../lib/utils.js";
7
+
8
+ interface Repo {
9
+ full_name: string;
10
+ private: boolean;
11
+ description: string | null;
12
+ }
13
+
14
+ let orgOrUser = process.argv[2];
15
+
16
+ if (!orgOrUser) {
17
+ const ws = getActiveWorkspace();
18
+ if (ws.github_org) {
19
+ orgOrUser = ws.github_org;
20
+ }
21
+ }
22
+
23
+ let endpoint = "/user/repos?per_page=100";
24
+ if (orgOrUser) {
25
+ endpoint = `/orgs/${orgOrUser}/repos?per_page=100`;
26
+ }
27
+
28
+ console.log(`Fetching repositories from: ${endpoint}`);
29
+
30
+ try {
31
+ let repos = await githubRequest<Repo[]>(endpoint);
32
+
33
+ // If org fails, try as user
34
+ if (repos.length === 0 && orgOrUser) {
35
+ repos = await githubRequest<Repo[]>(
36
+ `/users/${orgOrUser}/repos?per_page=100`
37
+ );
38
+ }
39
+
40
+ console.log(`Repositories${orgOrUser ? ` for ${orgOrUser}` : ""}:`);
41
+ console.log("=".repeat(50));
42
+
43
+ for (const repo of repos) {
44
+ const icon = repo.private ? "🔒" : "🌐";
45
+ console.log(`${icon} ${repo.full_name}`);
46
+ if (repo.description) {
47
+ console.log(` ${repo.description.slice(0, 60)}...`);
48
+ }
49
+ console.log();
50
+ }
51
+ } catch (error) {
52
+ console.error("Failed to list repositories:", error);
53
+ process.exit(1);
54
+ }
@@ -0,0 +1,50 @@
1
+ ---
2
+ name: brn:jira
3
+ description: |
4
+ Interact with JIRA API for ticket management and tracking.
5
+ Use when: (1) Listing assigned tickets, (2) Getting details, (3) Updating status.
6
+ ---
7
+
8
+ # JIRA Integration
9
+
10
+ ## Description
11
+ The JIRA skill allows you to manage your tasks without leaving the terminal. It connects to your JIRA instance using the credentials configured in your workspace.
12
+
13
+ ## Available Scripts
14
+
15
+ ### `list_tickets`
16
+ Lists JIRA tickets assigned to the current user. Can be filtered by status.
17
+
18
+ * **Usage**: `brn jira list_tickets [status]`
19
+ * **Arguments**:
20
+ * `status` (optional): Filter tickets by status (e.g., "In Progress", "To Do").
21
+ * **Example**: `brn jira list_tickets "In Progress"`
22
+
23
+ ### `get_ticket`
24
+ Retrieves detailed information about a specific JIRA ticket.
25
+
26
+ * **Usage**: `brn jira get_ticket <ticket_key>`
27
+ * **Arguments**:
28
+ * `ticket_key`: The JIRA issue key (e.g., `PROJ-123`).
29
+ * **Example**: `brn jira get_ticket PROJ-123`
30
+
31
+ ### `update_ticket`
32
+ Updates the status (transition) of a JIRA ticket.
33
+
34
+ * **Usage**: `brn jira update_ticket <ticket_key> <transition>`
35
+ * **Arguments**:
36
+ * `ticket_key`: The JIRA issue key.
37
+ * `transition`: The name of the transition/status to move to (e.g., "In Progress", "Done").
38
+ * **Example**: `brn jira update_ticket PROJ-123 "In Progress"`
39
+
40
+ ### `add_comment`
41
+ Adds a comment to a JIRA ticket.
42
+
43
+ * **Usage**: `brn jira add_comment <ticket_key> <comment>`
44
+ * **Arguments**:
45
+ * `ticket_key`: The JIRA issue key.
46
+ * `comment`: The text content of the comment.
47
+ * **Example**: `brn jira add_comment PROJ-123 "Released in version 1.2.0"`
48
+
49
+ ## API Reference
50
+ See [references/api_patterns.md](references/api_patterns.md) for patterns.
@@ -0,0 +1,59 @@
1
+ # JIRA API Patterns
2
+
3
+ Common patterns for JIRA Cloud API integration.
4
+
5
+ ## Authentication
6
+
7
+ JIRA Cloud uses Basic Auth with email and API token:
8
+ ```
9
+ Authorization: Basic base64(email:api_token)
10
+ ```
11
+
12
+ Create API token at: https://id.atlassian.com/manage-profile/security/api-tokens
13
+
14
+ ## Common Endpoints
15
+
16
+ | Endpoint | Method | Description |
17
+ |----------|--------|-------------|
18
+ | `/rest/api/3/search` | GET | Search issues with JQL |
19
+ | `/rest/api/3/issue/{key}` | GET | Get issue details |
20
+ | `/rest/api/3/issue/{key}/transitions` | GET | Get available transitions |
21
+ | `/rest/api/3/issue/{key}/transitions` | POST | Transition issue |
22
+ | `/rest/api/3/issue/{key}/comment` | POST | Add comment |
23
+
24
+ ## JQL Examples
25
+
26
+ ```
27
+ # My open tickets
28
+ assignee = currentUser() AND status != Done
29
+
30
+ # In progress in a project
31
+ project = PROJ AND status = "In Progress"
32
+
33
+ # Recently updated
34
+ assignee = currentUser() ORDER BY updated DESC
35
+ ```
36
+
37
+ ## Error Handling
38
+
39
+ | Code | Meaning | Action |
40
+ |------|---------|--------|
41
+ | 401 | Unauthorized | Check email/token |
42
+ | 403 | Forbidden | Check permissions |
43
+ | 404 | Not found | Check issue key |
44
+
45
+ ## ADF Format
46
+
47
+ JIRA API v3 uses Atlassian Document Format for rich text:
48
+ ```json
49
+ {
50
+ "type": "doc",
51
+ "version": 1,
52
+ "content": [
53
+ {
54
+ "type": "paragraph",
55
+ "content": [{"type": "text", "text": "Hello"}]
56
+ }
57
+ ]
58
+ }
59
+ ```
@@ -0,0 +1,41 @@
1
+ #!/usr/bin/env npx tsx
2
+ /**
3
+ * Add a comment to a JIRA ticket
4
+ * Usage: npx tsx add_comment.ts <ticket_key> <comment>
5
+ */
6
+ import { jiraRequest } from "../../../lib/utils.js";
7
+
8
+ const ticketKey = process.argv[2];
9
+ const commentText = process.argv.slice(3).join(" ");
10
+
11
+ if (!ticketKey || !commentText) {
12
+ console.log("Usage: npx tsx add_comment.ts <ticket_key> <comment>");
13
+ console.log("Example: npx tsx add_comment.ts PROJ-123 'PR created: https://...'");
14
+ process.exit(1);
15
+ }
16
+
17
+ // JIRA API v3 uses ADF format for comments
18
+ const body = {
19
+ body: {
20
+ type: "doc",
21
+ version: 1,
22
+ content: [
23
+ {
24
+ type: "paragraph",
25
+ content: [
26
+ {
27
+ type: "text",
28
+ text: commentText,
29
+ },
30
+ ],
31
+ },
32
+ ],
33
+ },
34
+ };
35
+
36
+ await jiraRequest(`/rest/api/3/issue/${ticketKey}/comment`, {
37
+ method: "POST",
38
+ body: JSON.stringify(body),
39
+ });
40
+
41
+ console.log(`✓ Added comment to ${ticketKey}`);
@@ -0,0 +1,71 @@
1
+ #!/usr/bin/env npx tsx
2
+ /**
3
+ * Get JIRA ticket details
4
+ * Usage: npx tsx get_ticket.ts <ticket_key>
5
+ */
6
+ import { jiraRequest, getJiraConfig } from "../../../lib/utils.js";
7
+
8
+ interface Issue {
9
+ key: string;
10
+ fields: {
11
+ summary: string;
12
+ status: { name: string };
13
+ issuetype: { name: string };
14
+ priority: { name: string } | null;
15
+ assignee: { displayName: string } | null;
16
+ reporter: { displayName: string } | null;
17
+ created: string;
18
+ updated: string;
19
+ description: unknown;
20
+ };
21
+ }
22
+
23
+ function extractText(node: unknown): string {
24
+ if (typeof node !== "object" || node === null) return "";
25
+
26
+ const obj = node as Record<string, unknown>;
27
+
28
+ if (obj.type === "text" && typeof obj.text === "string") {
29
+ return obj.text;
30
+ }
31
+
32
+ if (Array.isArray(obj.content)) {
33
+ return obj.content.map(extractText).join(" ");
34
+ }
35
+
36
+ return "";
37
+ }
38
+
39
+ const ticketKey = process.argv[2];
40
+
41
+ if (!ticketKey) {
42
+ console.log("Usage: npx tsx get_ticket.ts <ticket_key>");
43
+ console.log("Example: npx tsx get_ticket.ts PROJ-123");
44
+ process.exit(1);
45
+ }
46
+
47
+ const { url } = getJiraConfig();
48
+ const issue = await jiraRequest<Issue>(`/rest/api/3/issue/${ticketKey}`);
49
+
50
+ const { fields } = issue;
51
+
52
+ console.log(`Ticket: ${issue.key}`);
53
+ console.log("=".repeat(60));
54
+ console.log(`Summary: ${fields.summary}`);
55
+ console.log(`Status: ${fields.status.name}`);
56
+ console.log(`Type: ${fields.issuetype.name}`);
57
+ console.log(`Priority: ${fields.priority?.name ?? "None"}`);
58
+ console.log(`Assignee: ${fields.assignee?.displayName ?? "Unassigned"}`);
59
+ console.log(`Reporter: ${fields.reporter?.displayName ?? "Unknown"}`);
60
+ console.log(`Created: ${fields.created.slice(0, 10)}`);
61
+ console.log(`Updated: ${fields.updated.slice(0, 10)}`);
62
+ console.log();
63
+
64
+ if (fields.description) {
65
+ console.log("Description:");
66
+ console.log("-".repeat(40));
67
+ console.log(extractText(fields.description));
68
+ }
69
+
70
+ console.log();
71
+ console.log(`URL: ${url}/browse/${issue.key}`);
@@ -0,0 +1,64 @@
1
+ #!/usr/bin/env npx tsx
2
+ /**
3
+ * List JIRA tickets assigned to current user
4
+ * Usage: npx tsx list_tickets.ts [status]
5
+ */
6
+ import { jiraRequest } from "../../../lib/utils.js";
7
+
8
+ interface Issue {
9
+ key: string;
10
+ fields: {
11
+ summary: string;
12
+ status: { name: string };
13
+ priority: { name: string } | null;
14
+ };
15
+ }
16
+
17
+ interface SearchResult {
18
+ issues: Issue[];
19
+ }
20
+
21
+ const statusFilter = process.argv[2];
22
+
23
+ let jql = "assignee = currentUser() ORDER BY updated DESC";
24
+ if (statusFilter) {
25
+ jql = `assignee = currentUser() AND status = "${statusFilter}" ORDER BY updated DESC`;
26
+ }
27
+
28
+ const data = await jiraRequest<SearchResult>("/rest/api/3/search/jql", {
29
+ method: "POST",
30
+ body: JSON.stringify({
31
+ jql,
32
+ maxResults: 20,
33
+ fields: ["summary", "status", "priority"],
34
+ }),
35
+ });
36
+
37
+ const issues = data.issues;
38
+
39
+ console.log(`Your Tickets (${issues.length} found):`);
40
+ console.log("=".repeat(60));
41
+
42
+ if (issues.length === 0) {
43
+ console.log("No tickets found.");
44
+ process.exit(0);
45
+ }
46
+
47
+ const statusEmoji: Record<string, string> = {
48
+ "To Do": "📋",
49
+ "In Progress": "🔧",
50
+ "Code Review": "👀",
51
+ Done: "✅",
52
+ };
53
+
54
+ for (const issue of issues) {
55
+ const { key, fields } = issue;
56
+ const summary = fields.summary.slice(0, 50);
57
+ const status = fields.status.name;
58
+ const priority = fields.priority?.name ?? "None";
59
+ const emoji = statusEmoji[status] ?? "📌";
60
+
61
+ console.log(`${emoji} ${key}: ${summary}`);
62
+ console.log(` Status: ${status} | Priority: ${priority}`);
63
+ console.log();
64
+ }
@@ -0,0 +1,50 @@
1
+ #!/usr/bin/env npx tsx
2
+ /**
3
+ * Update JIRA ticket status via transition
4
+ * Usage: npx tsx update_ticket.ts <ticket_key> <target_status>
5
+ */
6
+ import { jiraRequest } from "../../../lib/utils.js";
7
+
8
+ interface Transition {
9
+ id: string;
10
+ name: string;
11
+ }
12
+
13
+ interface TransitionsResponse {
14
+ transitions: Transition[];
15
+ }
16
+
17
+ const [ticketKey, targetStatus] = process.argv.slice(2);
18
+
19
+ if (!ticketKey || !targetStatus) {
20
+ console.log("Usage: npx tsx update_ticket.ts <ticket_key> <target_status>");
21
+ console.log("Example: npx tsx update_ticket.ts PROJ-123 'In Progress'");
22
+ process.exit(1);
23
+ }
24
+
25
+ // Get available transitions
26
+ const { transitions } = await jiraRequest<TransitionsResponse>(
27
+ `/rest/api/3/issue/${ticketKey}/transitions`
28
+ );
29
+
30
+ // Find matching transition (case-insensitive)
31
+ const transition = transitions.find(
32
+ (t) => t.name.toLowerCase() === targetStatus.toLowerCase()
33
+ );
34
+
35
+ if (!transition) {
36
+ console.error(`Error: Transition '${targetStatus}' not available`);
37
+ console.error("Available transitions:");
38
+ for (const t of transitions) {
39
+ console.error(` - ${t.name}`);
40
+ }
41
+ process.exit(1);
42
+ }
43
+
44
+ // Perform transition
45
+ await jiraRequest(`/rest/api/3/issue/${ticketKey}/transitions`, {
46
+ method: "POST",
47
+ body: JSON.stringify({ transition: { id: transition.id } }),
48
+ });
49
+
50
+ console.log(`✓ Transitioned ${ticketKey} to '${targetStatus}'`);