@codyswann/lisa 2.11.1 → 2.12.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/package.json +1 -1
- package/plugins/lisa/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa/skills/jira-read-ticket/SKILL.md +18 -2
- package/plugins/lisa/skills/jira-read-ticket/scripts/download-attachment.sh +110 -0
- package/plugins/lisa-cdk/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-expo/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-nestjs/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-rails/.claude-plugin/plugin.json +1 -1
- package/plugins/lisa-typescript/.claude-plugin/plugin.json +1 -1
- package/plugins/src/base/skills/jira-read-ticket/SKILL.md +18 -2
- package/plugins/src/base/skills/jira-read-ticket/scripts/download-attachment.sh +110 -0
package/package.json
CHANGED
|
@@ -79,7 +79,7 @@
|
|
|
79
79
|
"lodash": ">=4.18.1"
|
|
80
80
|
},
|
|
81
81
|
"name": "@codyswann/lisa",
|
|
82
|
-
"version": "2.
|
|
82
|
+
"version": "2.12.0",
|
|
83
83
|
"description": "Claude Code governance framework that applies guardrails, guidance, and automated enforcement to projects",
|
|
84
84
|
"main": "dist/index.js",
|
|
85
85
|
"exports": {
|
|
@@ -34,7 +34,23 @@ Call `mcp__atlassian__getJiraIssue` for the target ticket. Extract and preserve:
|
|
|
34
34
|
- Full description (preserve formatting)
|
|
35
35
|
- Acceptance criteria section if separately structured
|
|
36
36
|
- **Validation Journey** section if present (pass verbatim to downstream)
|
|
37
|
-
- Attachments list
|
|
37
|
+
- Attachments list — capture `id`, `filename`, `mimeType`, `size`, and `content` URL for each. Do not download unless a downstream task needs the bytes (see "Downloading attachments" below).
|
|
38
|
+
|
|
39
|
+
#### Downloading attachments (opt-in)
|
|
40
|
+
|
|
41
|
+
The Atlassian MCP exposes attachment metadata but no binary-fetch tool ([JRACLOUD-97830](https://jira.atlassian.com/browse/JRACLOUD-97830), [ECO-1265](https://jira.atlassian.com/browse/ECO-1265)). Fetch attachment bytes only when a downstream task explicitly needs them — e.g., a design-fidelity check on an image, log-file analysis, PDF text extraction. For everything else, keep the URL reference and move on.
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
bash .claude/skills/jira-read-ticket/scripts/download-attachment.sh <id-or-content-url> <output-path>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Requires `JIRA_SERVER`, `JIRA_LOGIN`, and `JIRA_API_TOKEN` in the environment (same contract as `jira-evidence`). If those are not set the helper exits with code 2 and a clear remediation message — record the URL only and continue.
|
|
48
|
+
|
|
49
|
+
After download, branch on `mimeType`:
|
|
50
|
+
- `image/*` — pass the local path to image-aware downstream tools
|
|
51
|
+
- `text/*`, `application/json`, `application/xml`, `application/x-yaml` — read inline as text
|
|
52
|
+
- `application/pdf` — extract text via downstream tooling if needed
|
|
53
|
+
- everything else — record path only; do not attempt to inline binary content
|
|
38
54
|
|
|
39
55
|
### Comments
|
|
40
56
|
|
|
@@ -121,7 +137,7 @@ Produce a single structured output that the caller can pass verbatim to downstre
|
|
|
121
137
|
<chronological comments, flagged items called out>
|
|
122
138
|
|
|
123
139
|
### Attachments
|
|
124
|
-
<list>
|
|
140
|
+
<list with id, filename, mimeType, size, content URL — note any that were downloaded and their local paths>
|
|
125
141
|
|
|
126
142
|
## Remote Links
|
|
127
143
|
### Pull Requests (<count>)
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# download-attachment.sh — Download a JIRA attachment to a local file.
|
|
3
|
+
#
|
|
4
|
+
# Usage:
|
|
5
|
+
# bash download-attachment.sh <ATTACHMENT_ID_OR_URL> <OUTPUT_PATH>
|
|
6
|
+
#
|
|
7
|
+
# Why this helper exists:
|
|
8
|
+
# The Atlassian MCP server (mcp__atlassian__*) returns attachment metadata
|
|
9
|
+
# (id, filename, mimeType, size, content URL) on getJiraIssue but provides
|
|
10
|
+
# no tool to fetch the binary content. This script closes the gap by
|
|
11
|
+
# hitting the Jira REST API directly with Basic auth, mirroring the
|
|
12
|
+
# env-var contract already used by jira-evidence/scripts/post-evidence.sh.
|
|
13
|
+
#
|
|
14
|
+
# See https://jira.atlassian.com/browse/JRACLOUD-97830 for the upstream
|
|
15
|
+
# gap; remove this helper once Atlassian ships a download tool in the MCP.
|
|
16
|
+
#
|
|
17
|
+
# Required env vars:
|
|
18
|
+
# JIRA_SERVER - https://<your-tenant>.atlassian.net
|
|
19
|
+
# JIRA_LOGIN - login email
|
|
20
|
+
# JIRA_API_TOKEN - API token (https://id.atlassian.com/manage-profile/security/api-tokens)
|
|
21
|
+
#
|
|
22
|
+
# Exit codes:
|
|
23
|
+
# 0 success
|
|
24
|
+
# 1 download failed (HTTP error)
|
|
25
|
+
# 2 missing required env var
|
|
26
|
+
# 3 invalid arguments
|
|
27
|
+
|
|
28
|
+
set -euo pipefail
|
|
29
|
+
|
|
30
|
+
if [[ $# -lt 2 ]]; then
|
|
31
|
+
echo "Usage: download-attachment.sh <ATTACHMENT_ID_OR_URL> <OUTPUT_PATH>" >&2
|
|
32
|
+
exit 3
|
|
33
|
+
fi
|
|
34
|
+
ID_OR_URL="$1"
|
|
35
|
+
OUTPUT_PATH="$2"
|
|
36
|
+
|
|
37
|
+
# Resolve credentials: prefer env, fall back to jira-cli config for server/login.
|
|
38
|
+
JIRA_CONFIG="${HOME}/.config/.jira/.config.yml"
|
|
39
|
+
if [[ -z "${JIRA_SERVER:-}" && -f "$JIRA_CONFIG" ]]; then
|
|
40
|
+
JIRA_SERVER=$(grep '^server:' "$JIRA_CONFIG" | awk '{print $2}')
|
|
41
|
+
fi
|
|
42
|
+
if [[ -z "${JIRA_LOGIN:-}" && -f "$JIRA_CONFIG" ]]; then
|
|
43
|
+
JIRA_LOGIN=$(grep '^login:' "$JIRA_CONFIG" | awk '{print $2}')
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
for VAR in JIRA_SERVER JIRA_LOGIN JIRA_API_TOKEN; do
|
|
47
|
+
if [[ -z "${!VAR:-}" ]]; then
|
|
48
|
+
echo "ERROR: $VAR is not set." >&2
|
|
49
|
+
echo "Required env vars: JIRA_SERVER, JIRA_LOGIN, JIRA_API_TOKEN." >&2
|
|
50
|
+
echo "Generate an API token: https://id.atlassian.com/manage-profile/security/api-tokens" >&2
|
|
51
|
+
exit 2
|
|
52
|
+
fi
|
|
53
|
+
done
|
|
54
|
+
|
|
55
|
+
OUTPUT_DIR=$(dirname "$OUTPUT_PATH")
|
|
56
|
+
if [[ ! -d "$OUTPUT_DIR" ]]; then
|
|
57
|
+
echo "ERROR: Output directory does not exist: $OUTPUT_DIR" >&2
|
|
58
|
+
exit 3
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
if [[ "$ID_OR_URL" == http*://* ]]; then
|
|
62
|
+
ATTACHMENT_URL="$ID_OR_URL"
|
|
63
|
+
else
|
|
64
|
+
ATTACHMENT_URL="${JIRA_SERVER%/}/rest/api/3/attachment/content/$ID_OR_URL"
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
JIRA_AUTH=$(printf '%s' "$JIRA_LOGIN:$JIRA_API_TOKEN" | base64 | tr -d '\n')
|
|
68
|
+
|
|
69
|
+
# Atlassian responds 302 to a signed URL on media.atlassian.com that has its
|
|
70
|
+
# own auth and rejects Basic. Two-step: capture Location, then GET unauthed.
|
|
71
|
+
HEADERS_FILE=$(mktemp)
|
|
72
|
+
trap 'rm -f "$HEADERS_FILE"' EXIT
|
|
73
|
+
|
|
74
|
+
HTTP_CODE=$(curl -sS -o /dev/null -w '%{http_code}' \
|
|
75
|
+
--max-redirs 0 \
|
|
76
|
+
-D "$HEADERS_FILE" \
|
|
77
|
+
-H "Authorization: Basic $JIRA_AUTH" \
|
|
78
|
+
-H "Accept: */*" \
|
|
79
|
+
"$ATTACHMENT_URL" || true)
|
|
80
|
+
|
|
81
|
+
case "$HTTP_CODE" in
|
|
82
|
+
302|303|307)
|
|
83
|
+
SIGNED_URL=$(awk '/^[Ll]ocation:/{sub(/^[Ll]ocation:[ \t]*/,""); sub(/\r$/,""); print; exit}' "$HEADERS_FILE")
|
|
84
|
+
if [[ -z "$SIGNED_URL" ]]; then
|
|
85
|
+
echo "ERROR: Got HTTP $HTTP_CODE but no Location header in response." >&2
|
|
86
|
+
exit 1
|
|
87
|
+
fi
|
|
88
|
+
curl -sSf -o "$OUTPUT_PATH" "$SIGNED_URL" || { echo "ERROR: Download from signed URL failed." >&2; exit 1; }
|
|
89
|
+
;;
|
|
90
|
+
200)
|
|
91
|
+
curl -sSf -o "$OUTPUT_PATH" \
|
|
92
|
+
-H "Authorization: Basic $JIRA_AUTH" \
|
|
93
|
+
-H "Accept: */*" \
|
|
94
|
+
"$ATTACHMENT_URL" || { echo "ERROR: Direct download from $ATTACHMENT_URL failed." >&2; exit 1; }
|
|
95
|
+
;;
|
|
96
|
+
401|403)
|
|
97
|
+
echo "ERROR: Authentication failed (HTTP $HTTP_CODE). Verify JIRA_LOGIN and JIRA_API_TOKEN." >&2
|
|
98
|
+
exit 1
|
|
99
|
+
;;
|
|
100
|
+
404)
|
|
101
|
+
echo "ERROR: Attachment not found at $ATTACHMENT_URL (HTTP 404). Verify the ID and your access." >&2
|
|
102
|
+
exit 1
|
|
103
|
+
;;
|
|
104
|
+
*)
|
|
105
|
+
echo "ERROR: Unexpected HTTP $HTTP_CODE from $ATTACHMENT_URL" >&2
|
|
106
|
+
exit 1
|
|
107
|
+
;;
|
|
108
|
+
esac
|
|
109
|
+
|
|
110
|
+
echo " ✓ Downloaded -> $OUTPUT_PATH"
|
|
@@ -34,7 +34,23 @@ Call `mcp__atlassian__getJiraIssue` for the target ticket. Extract and preserve:
|
|
|
34
34
|
- Full description (preserve formatting)
|
|
35
35
|
- Acceptance criteria section if separately structured
|
|
36
36
|
- **Validation Journey** section if present (pass verbatim to downstream)
|
|
37
|
-
- Attachments list
|
|
37
|
+
- Attachments list — capture `id`, `filename`, `mimeType`, `size`, and `content` URL for each. Do not download unless a downstream task needs the bytes (see "Downloading attachments" below).
|
|
38
|
+
|
|
39
|
+
#### Downloading attachments (opt-in)
|
|
40
|
+
|
|
41
|
+
The Atlassian MCP exposes attachment metadata but no binary-fetch tool ([JRACLOUD-97830](https://jira.atlassian.com/browse/JRACLOUD-97830), [ECO-1265](https://jira.atlassian.com/browse/ECO-1265)). Fetch attachment bytes only when a downstream task explicitly needs them — e.g., a design-fidelity check on an image, log-file analysis, PDF text extraction. For everything else, keep the URL reference and move on.
|
|
42
|
+
|
|
43
|
+
```bash
|
|
44
|
+
bash .claude/skills/jira-read-ticket/scripts/download-attachment.sh <id-or-content-url> <output-path>
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
Requires `JIRA_SERVER`, `JIRA_LOGIN`, and `JIRA_API_TOKEN` in the environment (same contract as `jira-evidence`). If those are not set the helper exits with code 2 and a clear remediation message — record the URL only and continue.
|
|
48
|
+
|
|
49
|
+
After download, branch on `mimeType`:
|
|
50
|
+
- `image/*` — pass the local path to image-aware downstream tools
|
|
51
|
+
- `text/*`, `application/json`, `application/xml`, `application/x-yaml` — read inline as text
|
|
52
|
+
- `application/pdf` — extract text via downstream tooling if needed
|
|
53
|
+
- everything else — record path only; do not attempt to inline binary content
|
|
38
54
|
|
|
39
55
|
### Comments
|
|
40
56
|
|
|
@@ -121,7 +137,7 @@ Produce a single structured output that the caller can pass verbatim to downstre
|
|
|
121
137
|
<chronological comments, flagged items called out>
|
|
122
138
|
|
|
123
139
|
### Attachments
|
|
124
|
-
<list>
|
|
140
|
+
<list with id, filename, mimeType, size, content URL — note any that were downloaded and their local paths>
|
|
125
141
|
|
|
126
142
|
## Remote Links
|
|
127
143
|
### Pull Requests (<count>)
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# download-attachment.sh — Download a JIRA attachment to a local file.
|
|
3
|
+
#
|
|
4
|
+
# Usage:
|
|
5
|
+
# bash download-attachment.sh <ATTACHMENT_ID_OR_URL> <OUTPUT_PATH>
|
|
6
|
+
#
|
|
7
|
+
# Why this helper exists:
|
|
8
|
+
# The Atlassian MCP server (mcp__atlassian__*) returns attachment metadata
|
|
9
|
+
# (id, filename, mimeType, size, content URL) on getJiraIssue but provides
|
|
10
|
+
# no tool to fetch the binary content. This script closes the gap by
|
|
11
|
+
# hitting the Jira REST API directly with Basic auth, mirroring the
|
|
12
|
+
# env-var contract already used by jira-evidence/scripts/post-evidence.sh.
|
|
13
|
+
#
|
|
14
|
+
# See https://jira.atlassian.com/browse/JRACLOUD-97830 for the upstream
|
|
15
|
+
# gap; remove this helper once Atlassian ships a download tool in the MCP.
|
|
16
|
+
#
|
|
17
|
+
# Required env vars:
|
|
18
|
+
# JIRA_SERVER - https://<your-tenant>.atlassian.net
|
|
19
|
+
# JIRA_LOGIN - login email
|
|
20
|
+
# JIRA_API_TOKEN - API token (https://id.atlassian.com/manage-profile/security/api-tokens)
|
|
21
|
+
#
|
|
22
|
+
# Exit codes:
|
|
23
|
+
# 0 success
|
|
24
|
+
# 1 download failed (HTTP error)
|
|
25
|
+
# 2 missing required env var
|
|
26
|
+
# 3 invalid arguments
|
|
27
|
+
|
|
28
|
+
set -euo pipefail
|
|
29
|
+
|
|
30
|
+
if [[ $# -lt 2 ]]; then
|
|
31
|
+
echo "Usage: download-attachment.sh <ATTACHMENT_ID_OR_URL> <OUTPUT_PATH>" >&2
|
|
32
|
+
exit 3
|
|
33
|
+
fi
|
|
34
|
+
ID_OR_URL="$1"
|
|
35
|
+
OUTPUT_PATH="$2"
|
|
36
|
+
|
|
37
|
+
# Resolve credentials: prefer env, fall back to jira-cli config for server/login.
|
|
38
|
+
JIRA_CONFIG="${HOME}/.config/.jira/.config.yml"
|
|
39
|
+
if [[ -z "${JIRA_SERVER:-}" && -f "$JIRA_CONFIG" ]]; then
|
|
40
|
+
JIRA_SERVER=$(grep '^server:' "$JIRA_CONFIG" | awk '{print $2}')
|
|
41
|
+
fi
|
|
42
|
+
if [[ -z "${JIRA_LOGIN:-}" && -f "$JIRA_CONFIG" ]]; then
|
|
43
|
+
JIRA_LOGIN=$(grep '^login:' "$JIRA_CONFIG" | awk '{print $2}')
|
|
44
|
+
fi
|
|
45
|
+
|
|
46
|
+
for VAR in JIRA_SERVER JIRA_LOGIN JIRA_API_TOKEN; do
|
|
47
|
+
if [[ -z "${!VAR:-}" ]]; then
|
|
48
|
+
echo "ERROR: $VAR is not set." >&2
|
|
49
|
+
echo "Required env vars: JIRA_SERVER, JIRA_LOGIN, JIRA_API_TOKEN." >&2
|
|
50
|
+
echo "Generate an API token: https://id.atlassian.com/manage-profile/security/api-tokens" >&2
|
|
51
|
+
exit 2
|
|
52
|
+
fi
|
|
53
|
+
done
|
|
54
|
+
|
|
55
|
+
OUTPUT_DIR=$(dirname "$OUTPUT_PATH")
|
|
56
|
+
if [[ ! -d "$OUTPUT_DIR" ]]; then
|
|
57
|
+
echo "ERROR: Output directory does not exist: $OUTPUT_DIR" >&2
|
|
58
|
+
exit 3
|
|
59
|
+
fi
|
|
60
|
+
|
|
61
|
+
if [[ "$ID_OR_URL" == http*://* ]]; then
|
|
62
|
+
ATTACHMENT_URL="$ID_OR_URL"
|
|
63
|
+
else
|
|
64
|
+
ATTACHMENT_URL="${JIRA_SERVER%/}/rest/api/3/attachment/content/$ID_OR_URL"
|
|
65
|
+
fi
|
|
66
|
+
|
|
67
|
+
JIRA_AUTH=$(printf '%s' "$JIRA_LOGIN:$JIRA_API_TOKEN" | base64 | tr -d '\n')
|
|
68
|
+
|
|
69
|
+
# Atlassian responds 302 to a signed URL on media.atlassian.com that has its
|
|
70
|
+
# own auth and rejects Basic. Two-step: capture Location, then GET unauthed.
|
|
71
|
+
HEADERS_FILE=$(mktemp)
|
|
72
|
+
trap 'rm -f "$HEADERS_FILE"' EXIT
|
|
73
|
+
|
|
74
|
+
HTTP_CODE=$(curl -sS -o /dev/null -w '%{http_code}' \
|
|
75
|
+
--max-redirs 0 \
|
|
76
|
+
-D "$HEADERS_FILE" \
|
|
77
|
+
-H "Authorization: Basic $JIRA_AUTH" \
|
|
78
|
+
-H "Accept: */*" \
|
|
79
|
+
"$ATTACHMENT_URL" || true)
|
|
80
|
+
|
|
81
|
+
case "$HTTP_CODE" in
|
|
82
|
+
302|303|307)
|
|
83
|
+
SIGNED_URL=$(awk '/^[Ll]ocation:/{sub(/^[Ll]ocation:[ \t]*/,""); sub(/\r$/,""); print; exit}' "$HEADERS_FILE")
|
|
84
|
+
if [[ -z "$SIGNED_URL" ]]; then
|
|
85
|
+
echo "ERROR: Got HTTP $HTTP_CODE but no Location header in response." >&2
|
|
86
|
+
exit 1
|
|
87
|
+
fi
|
|
88
|
+
curl -sSf -o "$OUTPUT_PATH" "$SIGNED_URL" || { echo "ERROR: Download from signed URL failed." >&2; exit 1; }
|
|
89
|
+
;;
|
|
90
|
+
200)
|
|
91
|
+
curl -sSf -o "$OUTPUT_PATH" \
|
|
92
|
+
-H "Authorization: Basic $JIRA_AUTH" \
|
|
93
|
+
-H "Accept: */*" \
|
|
94
|
+
"$ATTACHMENT_URL" || { echo "ERROR: Direct download from $ATTACHMENT_URL failed." >&2; exit 1; }
|
|
95
|
+
;;
|
|
96
|
+
401|403)
|
|
97
|
+
echo "ERROR: Authentication failed (HTTP $HTTP_CODE). Verify JIRA_LOGIN and JIRA_API_TOKEN." >&2
|
|
98
|
+
exit 1
|
|
99
|
+
;;
|
|
100
|
+
404)
|
|
101
|
+
echo "ERROR: Attachment not found at $ATTACHMENT_URL (HTTP 404). Verify the ID and your access." >&2
|
|
102
|
+
exit 1
|
|
103
|
+
;;
|
|
104
|
+
*)
|
|
105
|
+
echo "ERROR: Unexpected HTTP $HTTP_CODE from $ATTACHMENT_URL" >&2
|
|
106
|
+
exit 1
|
|
107
|
+
;;
|
|
108
|
+
esac
|
|
109
|
+
|
|
110
|
+
echo " ✓ Downloaded -> $OUTPUT_PATH"
|