@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 CHANGED
@@ -79,7 +79,7 @@
79
79
  "lodash": ">=4.18.1"
80
80
  },
81
81
  "name": "@codyswann/lisa",
82
- "version": "2.11.1",
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": {
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa",
3
- "version": "2.11.1",
3
+ "version": "2.12.0",
4
4
  "description": "Universal governance — agents, skills, commands, hooks, and rules for all projects",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -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 (names + URLs, do not download unless needed)
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"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-cdk",
3
- "version": "2.11.1",
3
+ "version": "2.12.0",
4
4
  "description": "AWS CDK-specific plugin",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-expo",
3
- "version": "2.11.1",
3
+ "version": "2.12.0",
4
4
  "description": "Expo/React Native-specific skills, agents, rules, and MCP servers",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-nestjs",
3
- "version": "2.11.1",
3
+ "version": "2.12.0",
4
4
  "description": "NestJS-specific skills (GraphQL, TypeORM) and hooks (migration write-protection)",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-rails",
3
- "version": "2.11.1",
3
+ "version": "2.12.0",
4
4
  "description": "Ruby on Rails-specific hooks — RuboCop linting/formatting and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "lisa-typescript",
3
- "version": "2.11.1",
3
+ "version": "2.12.0",
4
4
  "description": "TypeScript-specific hooks — Prettier formatting, ESLint linting, and ast-grep scanning on edit",
5
5
  "author": {
6
6
  "name": "Cody Swann"
@@ -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 (names + URLs, do not download unless needed)
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"